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; }
以上