Leafletを使った地図アプリを作る上で試行錯誤したことをメモとして書いています。その機能あるよなど、なにかあればぜひコメントください。
概要
やりたいこと
- leafletで表示中の地図に、KMLファイルの内容を表示し、表示されたアイコンサイズを変えたい
- issueに1件あるのですが、私はgoogleEarthから出力できるファイルをそのまま使いたかったので、独自に書くことにしました。https://github.com/windycom/leaflet-kml/issues/38
as-is | to-be |
---|---|
![]() |
![]() |
結果
- leaflet-kmlを拡張(力技で仕様を上書き)
- kmlに書かれたscale情報を取得できるようにする
- アイコンサイズをscaleにあわせて拡大縮小できるようにする
使用するもの
- Leaflet
- leafletjsのKML表示プラグインleaflet-kml
- KMLの作成にはGoogle マイマップ、Google Earthを利用しています。
事前準備
以前の記事で作成していた sample.kml を今回も仕様します。
- kmlファイルを作成する
- 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に固定されています。
scaleが適用できるようにする
leaflet-KMLのコードを一部コピーし、機能を拡張します。
scaleが適用できるようにする
アイコンを処理している関数(parseStyle)をコピー
Leaflet-KML Pluginのファイル、KML.jsの中でparseStyle
を検索。出てきた箇所を含む関数parseStyle
をごっそりコピー、自分で作ったindex.html
のLeaflet-KML Plugin
を読み込んだ直後に貼り付ける。
貼り付けの際、parseStyle:functionの部分をL.KML.parseStyle=functionに書き換える。
index.htmlの一部分を抜粋
// ↓こうなっていたものを
parseStyle:function function (xml) { ...
// ↓ こうする
L.KML.parseStyle = function function (xml) { ...
kmlに記載しているscale情報を取得
- 標準ではscale情報を読み取っていないので、scale情報を取得するようにする
- 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情報からアイコンサイズを拡大させる
-
attributes
にscale: true,
を追加 -
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], // 追加: アイコンのセンターを座標に合わせる
});
}
完成した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>