ラベル Google Maps API の投稿を表示しています。 すべての投稿を表示
ラベル Google Maps API の投稿を表示しています。 すべての投稿を表示

2013-10-29

Google Maps API : 複数のMarker を一括消去する

MVCArray.forEach を使う。
markerList = new google.maps.MVCArray();

    marker1.setMap(map);
    markerList.push(marker1);

    marker2.setMap(map);
    markerList.push(marker2);

  :

    markerList.forEach(function(marker, idx) {
      marker.setMap(null);
    });

応用例(GpxClipper:gpxファイルを加工するウェブアプリ)


関連ページ
参考にしたページ

Google Maps API :zoom, center を適当な値に設定する

◆mapOptions を定義するスクリプト

var layout = new gmLayout(mapId, arrLatLng);
 var mapOptions = {
     zoom: layout.zoom,
     center: layout.center,
       mapTypeId: google.maps.MapTypeId.ROADMAP
    };
  map = new google.maps.Map(document.getElementById('map_canvas'),mapOptions);
gmLayout は最適化した zoom (lauout.zoom) と 最適化したcenter (layout.center)を求めるコンストラクタ
緯度経度の値の入った配列 arrLatLang に基づき、google.maps.Map の mapOptions の zoom と center を適当な値に決める。

◆コンストラクタ gmLayout

function gmLayout(mapId, 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(340/rangeLat*$('#'+mapId).height()/256) / Math.log(2) );
 var zoomLng = Math.floor( Math.log(340/rangeLng*$('#'+mapId).width()/256) / Math.log(2) );

 this.zoom = Math.min ( zoomLat, zoomLng );
 this.center = new google.maps.LatLng(ctrLat,ctrLng);

}
10-13行目:Math.max.apply(null,配列), Math.min.apply(null,配列) は配列の中から最大値、最小値を求める関数。
20-21行目:340°(360°ではない)が 256×2zoom に対応していることを利用している〔リンク〕


応用例(GpxClipper:gpxファイルを加工するウェブアプリ)


関連ページ
参考にしたページ

2013-11-10 改定


以上

2013-10-28

Google Maps API:gpxファイルを描画&編集する

同じドメイン(ここでは同じフォルダ)にあるgpxファイル(拡張子はxml)を読み込む。
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行目:ファイルを読み込んだ後に実行するスクリプト
<!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で設定する。
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;
}





以上





2013-10-20

Google Maps API:TIPS

◆距離を求める

3行目。"google.maps.geometry.spherical.computeDistanceBetween"を使う。
distanceの単位は(m)
var positionA = new google.maps.LatLng(latA,lngA);
var positionB = new google.maps.LatLng(latB,lngB);
var distance = google.maps.geometry.spherical.computeDistanceBetween(positionA, positionB);

尚、Google Maps APIをロードする際の引数にgeometryライブラリを追加してやる必要がある。
(URL引数にlibraries=geometryを追加する)
<script src="http://maps.googleapis.com/maps/api/js?libraries=geometry&sensor=false"></script> 

http://choni-waniwani.blogspot.jp/2011/07/google-maps-javascript-api-v32.html


◆sensor= は不要

下記の通り、sensor= は不要になった。

SensorNotRequired Warning 
The sensor parameter is no longer required for the Google Maps JavaScript API. It won't prevent the Google Maps JavaScript API from working correctly, but we recommend that you remove the sensor parameter from the script element.

<script src="http://maps.googleapis.com/maps/api/js?sensor=false"></script> 
のsensor=falseは不要。
<script src="http://maps.googleapis.com/maps/api/js"></script> 
でOK。

◆.sphericalを使う

libraries=geometryが必要。

<script src="http://maps.googleapis.com/maps/api/js?libraries=geometry"></script> 



以上

2013-10-06

Google Maps API: カスタムオーバーレイ

サンプル
応用例

変更履歴:
・慣例に則って、コンストラクタ名の最初の文字を大文字にした。(2015-11-08)
・メインのコードをhtmlファイルに記載した。(2015-11-08)
・デフォルト値の設定の仕方を変更した。(2015-11-08)
・コンストラクタ内では、.setMap()は実行しないようにした。メインのプログラムで実行するようにした。(2015-11-08)

◆html

7行目: Maps API JavaScript を読み込む。APIキーの取得は〔リンク〕を参照。
71行目:地図を表示するdiv要素を定義する。
28行目以降:マーカーをオーバーレイする(Google Maps API提供のオーバーレイ)。
40行目以降:htmlをオーバーレイする(カスタムオーバーレイ)。ここではキャプションと画像。
46行目:GMOverlayHtml を用いてマーカーと同様にオブジェクトを作成しています。
54行目:カスタムオーバーレイを表示。
58行目:カスタムオーバーレイを非表示。

尚、[1]のように.show(), .hide()を用いて表示、非表示することもできるが、この場合、.onAdd()が呼ばれるタイミングとの関係でうまく動作しない場合があったため、このような方法とした。

<!DOCTYPE html>
<html lang="ja">

<head>
<title>GoogleMapOverlay</title>
<link rel="stylesheet" href="GoogleMapOverlay.css">
<script src="http://maps.googleapis.com/maps/api/js?key=********"></script>
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script src="GMOverlayHtml-2.0.js"></script>

<script>

$(function() {

 var map;

// Google Map を描画
 var myLat = 51.507136;
 var myLng = -0.076421;
 var point = new google.maps.LatLng(myLat,myLng); //中央の座標を定義
 var myOptions = {
     zoom: 5, //ズームを定義
        center: point,
        mapTypeId: google.maps.MapTypeId.ROADMAP
    };
  map = new google.maps.Map(document.getElementById('map_canvas'), myOptions);

// Marker を overlay
 var image = new google.maps.MarkerImage('images/place.png',
     new google.maps.Size(16, 16),
       new google.maps.Point(0,0),
       new google.maps.Point(8, 8)
 );
 var marker = new google.maps.Marker({
     position: point,
        map: map,
  icon: image
    });

// html を overlay(カスタムオーバーレイ)
 var content = '<div style="background-color:darkgray; border: solid 1px;">';
  content += '<p>London</p>';
  content += '<img src="images/001.jpg"></div>';
 var offsetX = 10;
 var offsetY = 10;
 var overlayHtml = new GMOverlayHtml({
  position: marker.getPosition(), 
  content: content, 
  offsetX: offsetX, 
  offsetY: offsetY
 });

   google.maps.event.addListener(marker, 'mouseover', function() {
     overlayHtml.setMap(map);
   });

   google.maps.event.addListener(marker, 'mouseout', function() {
    overlayHtml.setMap(null);
   });

});

</script>

</head>


<body>
 <div id="header">Sample</div>
 <hr>
 <div id="map_canvas"></div> 
</body>
                   
</html>

◆コンストラクタ GMOverlayHtml (GMOverlayHtml-2.0.js)

1行目以降:コンストラクタ GMOverlayHtml を定義。新しいオブジェクトのプロパティとして渡されたパラメータを初期化します。このとき、optsに入力がなければデフォルト値が使われます。
20行目:OverlayView を明示的に GMOverlayHtml のサブクラスとして指定します。これは、新しいクラスの prototype を親クラスのインスタンスに設定することで実現します。
22行目以降:プロトタイプの中に onAdd() メソッドを実装し、オーバーレイを地図に貼り込みます。地図でオーバーレイの貼付が可能になると OverlayView.onAdd() が呼び出されます。
39行目以降:プロトタイプ中に draw() メソッドを実装し、オブジェクトの表示を制御します。オブジェクトが最初に表示されたとき、OverlayView.draw() が呼び出されます。
48行目以降:オーバーレイの中に追加した要素を消去するための onRemove() メソッドも実装する必要があります。このメソッドは、オーバーレイの map プロパティを null に設定すると、API が自動的に呼び出します。
以上は参考文献[1]に従っています。

var GMOverlayHtml = function(opts) {
 var defaults = {
  offsetX: 0,        // デフォルト値
  offsetY: 0,        // デフォルト値
  position: null,    // 入力値
  content: null,     // 入力値
 }
 for ( var key in defaults ) {
  if (defaults[key] == null) {
   this[key] = opts[key];
  } else {
   this[key] = defaults[key];
   if (opts[key]) this[key] = opts[key];
  }
 }
}

//  以下は、https://developers.google.com/maps/documentation/javascript/customoverlays に従って記載。

GMOverlayHtml.prototype = new google.maps.OverlayView();

GMOverlayHtml.prototype.onAdd = function() {
        // 何度も呼ばれる可能性があるので、divが未設定の場合のみ要素生成
        if (!this.div) {
          // 出力したい要素生成
          this.div = document.createElement( "div" );
          this.div.style.position = "absolute";
       this.div.style.border = '0px none';
       this.div.style.overflow = 'hidden';
       this.div.style.visibility = this.visibility_;
          this.div.innerHTML = this.content;
          // 要素を追加する子を取得
          var panes = this.getPanes();
          // 要素追加
          panes.overlayImage.appendChild( this.div ); // overlayImage: marker の foreground を含む pane
        }
}

GMOverlayHtml.prototype.draw = function() {
        // 緯度、軽度の情報を、Pixel(google.maps.Point)に変換
        var point = this.getProjection().fromLatLngToDivPixel(this.position);
        // 取得したPixel情報の座標に、要素の位置を設定
        // 座標の位置を左上の座標とする位置に要素が設定される
        this.div.style.left = point.x + this.offsetX + 'px';
        this.div.style.top = point.y + this.offsetY + 'px';
}

GMOverlayHtml.prototype.onRemove = function() {
  if (this.div) {
    this.div.parentNode.removeChild(this.div);
    this.div = null;
  }
}

◆css

* {
 margin: 0;
 padding: 0;
}

html, body {
  height: 100%;
}

body {
 overflow: hidden;
 background-color: ivory; 
}

#header {
 height: 5%;
 font: bold 32px arial,sans-serif;
 color: blue;
    margin: 5px;
} 

#map_canvas {
 height: 90%;
}


References





以上