Start地点、Goal地点近傍のトラックポイントを消去する。
変更履歴:
・経路(トラック)に沿って、消去するようにした(今までは、経路に関係なく、近ければ消去していた)。(2015-11-08)
・オブジェクト指向化した(コンストラクタを作成した)。(2015-11-08)
・メインのコードをhtmlファイルに記載するようにした。(2015-11-08)
応用例(GpxClipper:gpxファイルを加工するウェブアプリ)
◆html
70,71行目:ボタンのクリックで gpx_clip() を実行する。
19行目以降:xmlファイル(sample.xml)の読み込み。
29行目:コンストラクタGpxClipを用いてインスタンスgpxclipを作成する。
36行目:gpxclipを初期化する。
37行目:描画する。
xml ファイルの読み込みには、jQuery の $.ajax を使う。
29-37行目:ファイルを読み込んだ後に実行するスクリプト
19行目以降:xmlファイル(sample.xml)の読み込み。
29行目:コンストラクタGpxClipを用いてインスタンスgpxclipを作成する。
36行目:gpxclipを初期化する。
37行目:描画する。
xml ファイルの読み込みには、jQuery の $.ajax を使う。
29-37行目:ファイルを読み込んだ後に実行するスクリプト
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel=icon href=../_img/BlueSky_favicon.png sizes="16x16" type="image/png">
<title>gpxClipper</title>
<script src="http://maps.googleapis.com/maps/api/js?libraries=geometry&sensor=false"></script>
<script src="jquery.js"></script>
<script src="gpxClipperTest.js"></script>
<script src="gmOverlayHtml.js"></script>
<!--
http://www.atmarkit.co.jp/ait/articles/1112/16/news135.html
-->
<script>
$(function() {
url_gpx = "sample.xml";
// XMLファイル読み込み開始
$.ajax({
url: url_gpx,
cache:false,
dataType:"xml",
error: function(XMLHttpRequest, textStatus, errorThrown){
alert('読み込みエラー!');
},
success:function(data){
var gpxclip = new GpxClip({
xml: data,
// typeId: google.maps.MapTypeId.HYBRID,
// pathInit: { weight: 1 },
startButtonId: "start",
goalButtonId: "goal"
});
gpxclip.init();
gpxclip.draw();
}
});
});
</script></head>
<style type="text/css">
* {
margin: 0;
padding: 0;
}
html, body {
height: 100%;
}
body {
overflow: hidden;
background-color: ivory;
}
p {
background-color: #99cc00
}
#map_canvas {
height: 100%;
}
</style>
<body>
<input id="start" type="button" value="Startの周辺削除" onclick="gpx_clip(LatLngStart)" style="width:100px" >
<input id="goal" type="button" value="Goalの周辺削除" onclick="gpx_clip(LatLngGoal)" style="width:100px" >
<span>半径</span>
<input type="text" id="clip" value="100" size="3">
<span>(m) 以内の点を削除</span>
<div id="map_canvas"></div>
</body>
</html>
◆コンストラクタ(GpxClip)
optionsとdefaultsにより、プロパティを設定する。
関数(メソッド)はprototypeで設定する。
関数(メソッド)はprototypeで設定する。
var GpxClip = function(options) { // ver.2.1
var defaults = {
typeId: google.maps.MapTypeId.ROADMAP, // マップタイプ
timeFontSize: "16px", // 時間表示のフォントサイズ
period: 30, // 30分ごとに時間を表示
pathInit: { // 読み込んだxmlのトラックポイント
color: "Blue", // 線の色
opacity: 0.5, // 線の透過度
weight: 5 // 線の幅
},
path: { // クリップしたトラックポイント
image: new google.maps.MarkerImage( // Markerのイメージ
'images/point.png', // marker image
new google.maps.Size(16,16), // marker size
new google.maps.Point(0,0), // marker origin
new google.maps.Point(8,8) // marker anchor
)
},
start: { // スタートポイント
mark: {
offsetX: -64,
offsetY: -48,
content: '<img src="images/start.png">'
}
},
goal: { // ゴールポイント
mark: {
offsetX: 0,
offsetY: -48,
content: '<img src="images/goal.png">'
}
}
};
$.extend(true, this, defaults, options); // デフォルトオプションと渡されたオプションのマージ
this.period = this.period*60*1000; // 分→msに変換
};初期処理(GpxClip.prototype.init)
5-11行:出力用のxmlを作成する。13-50行:xmlファイルから緯度(Lat)、経度(Lng)、日時を取り込んで、初期のポイント(self.pathInit.point)、クリップ後のポイント(self.path.point)を初期化する。
28行:marker の一括消去用の配列を定義する。
30-35:Google Map を描画。
52-56行:Start地点、Goal地点を初期化する。
58-64行:トラックを描画。
66-73行:ボタンの設定。
GpxClip.prototype.init = function(){
var self = this;
// $(self.xml).find("name").each(function(){
// $(this).text("gpxClipper");
// });
// <name>タグのテキストが日本語の場合エラーがでるので、すべてgpxClipperに変更するようにした。 2014-12-14
// 問題なくなっていたので、コメントアウトした 2016-05-13
// 日本語があるとUTF-8、ないとShift-JISで保存されるようである。
var serializer = new XMLSerializer();
var xmlString = serializer.serializeToString(self.xml);
setBlobUrl("download", xmlString);
self.path.point = [];
self.pathInit.point = [];
$(self.xml).find("trkpt").each(function(i){
var Lat = this.getAttribute("lat");
var Lng = this.getAttribute("lon");
var LatLng = new google.maps.LatLng(Lat,Lng);
var date = new Date($(this).find("time").text());
self.pathInit.point.push(LatLng);
self.path.point[i] = {
latlng: LatLng,
date: date,
}
});
self.markerList = new google.maps.MVCArray();
var layout = new GmLayout('map_canvas', self.path.point);
self.map = new google.maps.Map(document.getElementById('map_canvas'),{
zoom: layout.zoom,
center: layout.center,
mapTypeId: self.typeId
});
$(self.xml).find("trkpt").each(function(i){
self.path.point[i].marker = new google.maps.Marker({
position: self.path.point[i].latlng,
map: self.map,
icon: self.path.image
});
self.path.point[i].infobox = self.setInfobox (self.path.point[i]);
});
self.path.point[0].distance = 0;
for ( i=1; i<self.path.point.length; i++) {
self.path.point[i].distance = self.path.point[i-1].distance
+ google.maps.geometry.spherical.computeDistanceBetween(self.path.point[i].latlng, self.path.point[i-1].latlng);
}
self.start.distance = self.path.point[0].distance;
self.goal.distance = self.path.point[self.path.point.length-1].distance;
self.start.index = 0;
self.goal.index = self.path.point.length-1;
var flightPath = new google.maps.Polyline({
path: self.pathInit.point,
strokeColor: self.pathInit.color,
strokeOpacity: self.pathInit.opacity,
strokeWeight: self.pathInit.weight
});
flightPath.setMap(self.map);
$('#'+self.startButtonId).on( "click", function(){
self.distanceClip = $("#clip").val();
self.clipStart();
});
$('#'+self.goalButtonId).on( "click", function(){
self.distanceClip = $("#clip").val();
self.clipGoal();
});
}描画(GpxClip.prototype.draw)
gpxファイルをGoogle Map 上に描画する。GpxClip.prototype.draw = function (){
var self = this;
// トラックポイントのマークを表示
var trkptLength = $(self.xml).find("trkpt").length;
for ( i=self.start.index; i<self.goal.index+1; i++) {
self.path.point[i].marker.setMap(self.map);
self.markerList.push(self.path.point[i].marker);
self.setMarkerInfo(self.path.point[i].marker, self.path.point[i].infobox) ;
}
// Startのマークを表示
var i = self.start.index;
var infobox = self.setInfobox (self.path.point[i]);
infobox.setMap(self.map);
self.markerList.push(infobox);
var mark = new GMOverlayHtml({
map: self.map,
position: self.path.point[i].latlng,
offsetX: self.start.mark.offsetX,
offsetY: self.start.mark.offsetY,
content: self.start.mark.content
});
mark.setMap(self.map);
self.markerList.push(mark);
var min = self.path.point[i].date.getTime() % self.period;
var nextTime = (self.path.point[i].date.getTime() - min) + self.period;
for ( i = 0; i < self.path.point.length; i++ ) {
if(self.path.point[i].date.getTime() > nextTime){
var infobox = self.setInfobox (self.path.point[i]);
infobox.setMap(self.map);
var min = self.path.point[i].date.getTime() % self.period;
var nextTime = (self.path.point[i].date.getTime() - min) + self.period;
}
}
// Goalのマークを表示
var i = self.goal.index;
var infobox = self.setInfobox (self.path.point[i]);
infobox.setMap(self.map);
self.markerList.push(infobox);
var mark = new GMOverlayHtml({
map: self.map,
position: self.path.point[i].latlng,
offsetX: self.goal.mark.offsetX,
offsetY: self.goal.mark.offsetY,
content: self.goal.mark.content
});
mark.setMap(self.map);
self.markerList.push(mark);
}その他のプロトタイプ
GpxClip.prototype.setInfobox = function (point) {
var self = this;
var date = point.date;
var $content = $('<p></p>').css({
fontSize: self.timeFontSize
}).text(
date.getHours() + ':' + date.getMinutesString()
);
var infobox = new GMOverlayHtml({
map: self.map,
position: point.latlng,
offsetX: 0,
offsetY: -16,
content: $content[0].outerHTML
});
return infobox;
}
// gpxの各点に時刻を表示(mouseover時)
GpxClip.prototype.setMarkerInfo = function (marker, infobox) {
var self = this;
google.maps.event.addListener(marker, 'mouseover', function() {
infobox.setMap(self.map);
self.markerList.push(infobox);
});
google.maps.event.addListener(marker, 'mouseout', function() {
infobox.setMap(null);
});
}
GpxClip.prototype.clipStart = function(){
var self = this;
var cnt=0;
$(self.xml).find("trkpt").each(function(i){
var distance = self.path.point[i+self.start.index].distance-self.start.distance;
if ( distance < self.distanceClip ) {
$(this).remove();
cnt++;
}
});
self.start.index = self.start.index + cnt;
self.start.distance = self.path.point[self.start.index].distance;
var serializer = new XMLSerializer();
var xmlString = serializer.serializeToString(self.xml);
setBlobUrl("download", xmlString);
self.markerList.forEach(function(marker, idx) {
marker.setMap(null);
});
self.draw();
}
GpxClip.prototype.clipGoal = function(){
var self = this;
var cnt=0;
$(self.xml).find("trkpt").each(function(i){
var distance = self.goal.distance-self.path.point[i+self.start.index].distance;
if ( distance < self.distanceClip ) {
$(this).remove();
cnt++;
}
});
self.goal.index = self.goal.index - cnt;
self.goal.distance = self.path.point[self.goal.index].distance;
var serializer = new XMLSerializer();
var xmlString = serializer.serializeToString(self.xml);
setBlobUrl("download", xmlString);
self.markerList.forEach(function(marker, idx) {
marker.setMap(null);
});
self.draw();
}Google Maps API のzoom, center を適当な値に設定する関数〔リンク〕
10-13行目:Math.max.apply(null,配列), Math.min.apply(null,配列) は配列の中から最大値、最小値を求める関数。
20-21行目:360°が 256×2zoom に対応していることを利用している〔リンク〕。
function gmLayout(arrLatLng){
var arrLat = new Array() ;
var arrLng = new Array() ;
for ( i = 0; i < arrLatLng.length; i++ ) {
arrLat[i] = arrLatLng[i].lat();
arrLng[i] = arrLatLng[i].lng();
}
var maxLat = Math.max.apply(null,arrLat);
var maxLng = Math.max.apply(null,arrLng);
var minLat = Math.min.apply(null,arrLat);
var minLng = Math.min.apply(null,arrLng);
var ctrLat = (maxLat+minLat)/2;
var ctrLng = (maxLng+minLng)/2;
var rangeLat = maxLat-minLat;
var rangeLng = maxLng-minLng;
var zoomLat = Math.floor( Math.log(360/rangeLat*$(document).height()/256) / Math.log(2) );
var zoomLng = Math.floor( Math.log(360/rangeLng*$(document).width()/256) / Math.log(2) );
this.zoom = Math.min ( zoomLat, zoomLng );
this.center = new google.maps.LatLng(ctrLat,ctrLng);
}
分を2桁の文字列に変換するプロトタイプ〔リンク〕
Date.prototype.getMinutesString = function(){
var minutes = this.getMinutes();
if ( minutes < 10 ) {
minutes = "0" + minutes;
}
return minutes;
}
以上