0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

leaflet + leaflet-kmlで表示させるアイコンのサイズをKMLのscaleで指定できるようにする

Last updated at Posted at 2023-11-16

Leafletを使った地図アプリを作る上で試行錯誤したことをメモとして書いています。その機能あるよなど、なにかあればぜひコメントください。

概要

やりたいこと

  • leafletで表示中の地図に、KMLファイルの内容を表示し、表示されたアイコンサイズを変えたい
  • issueに1件あるのですが、私はgoogleEarthから出力できるファイルをそのまま使いたかったので、独自に書くことにしました。https://github.com/windycom/leaflet-kml/issues/38
as-is to-be
スクリーンショット 2023-11-17 4.10.41.png スクリーンショット 2023-11-17 6.27.20.png

結果

  • leaflet-kmlを拡張(力技で仕様を上書き)
    • kmlに書かれたscale情報を取得できるようにする
    • アイコンサイズをscaleにあわせて拡大縮小できるようにする

使用するもの

事前準備

以前の記事で作成していた sample.kml を今回も仕様します。

KMLファイルを作成する

  1. kmlファイルを作成する
  2. kml情報をWebページに表示する

kmlに記載してあるアイコンサイズ(scale)を変える

今回は、kmlファイルの方のアイコンに記載しているscale情報を編集しておきます。
2つあるマーカのうち、片方だけscaleを2にしておきます。

googlemapではアイコンサイズのscaleが1の場合、32pxで表示されます。
scaleはその何倍かを設定するものです。

sample.kml(一部抜粋)
<IconStyle>
  <color>ff0051e6</color>
  <!--  <scale>1</scale>  ← こうなっていたものを -->
  <scale>2</scale> <!--  ← こうする  -->
  <Icon>
    <href>https://www.gstatic.com/mapspro/images/stock/503-wht-blank_maps.png</href>
  </Icon>
  <hotSpot x="32" xunits="pixels" y="64" yunits="insetPixels"/>
</IconStyle>

その状態でWebページをリロードしてもアイコンサイズに変化はありません。
leaflet-kmlでは、アイコンサイズは32pxに固定されています。

まだサイズに変化はありません。
スクリーンショット 2023-11-17 4.10.41.png

scaleが適用できるようにする

leaflet-KMLのコードを一部コピーし、機能を拡張します。

scaleが適用できるようにする

アイコンを処理している関数(parseStyle)をコピー

Leaflet-KML Pluginのファイル、KML.jsの中でparseStyleを検索。出てきた箇所を含む関数parseStyleをごっそりコピー、自分で作ったindex.htmlLeaflet-KML Pluginを読み込んだ直後に貼り付ける。

貼り付けの際、parseStyle:functionの部分をL.KML.parseStyle=functionに書き換える。

index.htmlの一部分を抜粋
// ↓こうなっていたものを
parseStyle:function function (xml) { ...

// ↓ こうする
L.KML.parseStyle = function function (xml) { ...

kmlに記載しているscale情報を取得

  1. 標準ではscale情報を読み取っていないので、scale情報を取得するようにする
  2. scale情報からアイコンサイズを拡大させる
1. 標準ではscale情報を読み取っていないので、scale情報を取得するようにする
index.htmlの一部分を抜粋
// L.KML.parseStyle = function (xml) の中の
// function _parse (xml) { の中

if (key === 'hotSpot')
  {
    for (var j = 0; j < e.attributes.length; j++) {
      options[e.attributes[j].name] = e.attributes[j].nodeValue;
	}
  } else {
    var value = e.childNodes[0].nodeValue;
    if (key === 'color') {
       options.opacity = parseInt(value.substring(0, 2), 16) / 255.0;
       options.color = '#' + value.substring(6, 8) + value.substring(4, 6) + value.substring(2, 4);
    } else if (key === 'width') {
       options.weight = value;
    } else if (key === 'Icon') {
       ioptions = _parse(e);
       if (ioptions.href) { options.href = ioptions.href; }
    } else if (key === 'href') {
       options.href = value;
    }
    // scaleを追加 ここから
      else if (key === 'scale') {
      options.scale = value;
    }
    // scaleを追加 ここまで
  }
2. scale情報からアイコンサイズを拡大させる
  1. attributesscale: true, を追加
  2. if (ioptions.href) {...}の中に追記
index.htmlの一部分を抜粋 「if (ioptions.href) {...}付近」
// attributesに scale: true を追加
var attributes = {color: true, width: true, Icon: true, href: true, scale: true, hotSpot: true};

if (ioptions.href) {
    // アイコンサイズを決める 仕様を追加 ここから
    let iconSize = 32; // とりあえず
    if (ioptions.scale) {
      iconSize = Math.floor(iconSize * parseFloat(ioptions.scale));
      if (isNaN(iconSize)) {
        iconSize = 32;
      }
    }
    // アイコンサイズを決める 仕様を追加 ここまで

    style.icon = new L.KMLIcon({
      iconUrl: ioptions.href,
      shadowUrl: null,
      anchorRef: {x: ioptions.x, y: ioptions.y},
      anchorType: {x: ioptions.xunits, y: ioptions.yunits},
      iconSize: [iconSize, iconSize], // 追加: アイコンサイズを指定する
      iconAnchor: [iconSize / 2, iconSize / 2], // 追加: アイコンのセンターを座標に合わせる
    });
  }

アイコンサイズが変わりました。
スクリーンショット 2023-11-17 6.27.20.png

完成したindex.htmlの全容

index.html
<!DOCTYPE html>
<html>
<head>
    <title>Leaflet KML Example</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!-- Leaflet CSS -->
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />

    <!-- Leaflet JavaScript -->
    <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>

    <!-- Leaflet-KML Plugin -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet-plugins/3.4.0/layer/vector/KML.js"></script>
    <script>
        L.KML.parseStyle = function (xml) {
		var style = {}, poptions = {}, ioptions = {}, el, id;

		var attributes = {color: true, width: true, scale:true, Icon: true, href: true, hotSpot: true};

		function _parse (xml) {
			var options = {};
			for (var i = 0; i < xml.childNodes.length; i++) {
				var e = xml.childNodes[i];
				var key = e.tagName;
				if (!attributes[key]) { continue; }
				if (key === 'hotSpot')
				{
					for (var j = 0; j < e.attributes.length; j++) {
						options[e.attributes[j].name] = e.attributes[j].nodeValue;
					}
				} else {
					var value = e.childNodes[0].nodeValue;
					if (key === 'color') {
						options.opacity = parseInt(value.substring(0, 2), 16) / 255.0;
						options.color = '#' + value.substring(6, 8) + value.substring(4, 6) + value.substring(2, 4);
					} else if (key === 'width') {
						options.weight = value;
					} else if (key === 'Icon') {
						ioptions = _parse(e);
						if (ioptions.href) { options.href = ioptions.href; }
					} else if (key === 'href') {
						options.href = value;
					}
					  // scaleを追加
					  else if (key === 'scale') {
						options.scale = value;
					}
				}
			}
			return options;
		}

		el = xml.getElementsByTagName('LineStyle');
		if (el && el[0]) { style = _parse(el[0]); }
		el = xml.getElementsByTagName('PolyStyle');
		if (el && el[0]) { poptions = _parse(el[0]); }
		if (poptions.color) { style.fillColor = poptions.color; }
		if (poptions.opacity) { style.fillOpacity = poptions.opacity; }
		el = xml.getElementsByTagName('IconStyle');
		if (el && el[0]) { ioptions = _parse(el[0]); }
		if (ioptions.href) {
    // アイコンサイズを決める 仕様を追加 ここから
    let iconSize = 32; // とりあえず
    if (ioptions.scale) {
		console.log(ioptions.scale);
      iconSize = Math.floor(iconSize * parseFloat(ioptions.scale));
      if (isNaN(iconSize)) {
        iconSize = 32;
      }
    }
    // アイコンサイズを決める 仕様を追加 ここまで

    style.icon = new L.KMLIcon({
      iconUrl: ioptions.href,
      shadowUrl: null,
      anchorRef: {x: ioptions.x, y: ioptions.y},
      anchorType: {x: ioptions.xunits, y: ioptions.yunits},
      iconSize: [iconSize, iconSize], // 追加: アイコンサイズを指定する
      iconAnchor: [iconSize / 2, iconSize / 2], // 追加: アイコンのセンターを座標に合わせる
    });
  }
		
  id = xml.getAttribute('id');
  if (id && style) {
    style.id = id;
  }

  return style;
};

    </script>
</head>
<body>
    <div id="map" style="height: 400px;"></div>
    <script>
        // Create a Leaflet map
        var map = L.map('map').setView([0, 0], 2); // Set the initial map center and zoom level.

        // Add a base layer (e.g., OpenStreetMap)
        L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
            maxZoom: 19,
        }).addTo(map);

        // Load and display a KML file on the map
        var kmlURL = 'sample.kml'; // Replace with the path to your KML file
        var kmlLayer = new L.KML(kmlURL, {
            async: true,
        }).addTo(map);
    </script>
</body>
</html>
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?