OpenLayersでは難なく実装できましたが、Leafletではちょっと苦戦したので書いてみました
手順を踏みながら実装していきます
GeoJSONとは
- JSONをもとにした、GISデータを記述するためのフォーマット
- 多くの GIS 技術やサービスの間で非常に人気のあるデータ形式
GeoJSONオブジェクトを使ってアイコンを表示する
まずは、GeoJSONオブジェクトを定義して押上駅にアイコンを表示してみましょう
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>アイコン表示</title>
<link href="style.css" rel="stylesheet">
<!-- Leaflet -->
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.8.0/dist/leaflet.css" integrity="sha512-hoalWLoI8r4UszCkZ5kL8vayOGVae1oxXe/2A4AO6J9+580uKHDO3JdHb7NzwwzK5xr/Fs0W40kiNHxM9vyTtQ==" crossorigin=""/>
<script src="https://unpkg.com/leaflet@1.8.0/dist/leaflet.js" integrity="sha512-BB3hKbKWOc9Ez/TAwyWxNXeoV9c1v6FIeYiBieIWkpLjauysF18NzgR1MBNBXf8/KABdlkX68nAhlwcDFLGPCQ==" crossorigin=""></script>
</head>
<body>
<div id="map"></div>
<script src="main.js"></script>
</body>
</html>
html, body{
height: 100%;
}
#map{
height: 100%;
}
ここまでは前回の記事と同じです
let lat = 35.7100069; // 緯度
let lng= 139.8108103; // 経度
let zoom = 16; // ズームレベル
let map = L.map("map"); // 地図の生成
map.setView([lat, lng], zoom); // 緯度経度、ズームレベルを設定する
// タイルレイヤを生成し、地図に追加する
// 今回はOpenStreetMapを表示する
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
{
// 著作権の表示
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}
).addTo(map);
// アイコンを追加する
// GeoJSONオブジェクトを定義する
var geojsonFeature = {
"type": "Feature",
"properties": {
"name": "押上駅",
"amenity": "Station"
},
"geometry": {
"type": "Point", // ジオメトリのタイプを指定する。今回は1点なのでポイント
"coordinates": [139.8136911, 35.709058] // 位置情報をlng, latの順で指定する
}
};
// GeoJSONオブジェクトを地図に追加する
L.geoJSON(geojsonFeature).addTo(map);
- GeoJSONオブジェクトを定義する
- coordinatesで位置情報を経度, 緯度の順番で定義する
- GeoJSONオブジェクトを地図に追加する
- ポイントデータの場合、デフォルトのマーカーが生成される
押上駅にぴょこんとアイコンが表示されました~
ここまでは公式のサンプルコード通りに実装すればすんなりいけます
GeoJSONファイルを使ってアイコンを表示する
基本的にはGeoJSONオブジェクトをソースコードにべた書きせず、外部のGeoJSONファイルを読み込んで使うことがおおいです
ということでGeoJSONファイルをどっかからダウンロードして読み込んでみましょう
データを取得する
- 今回は国土交通省国土数値情報ダウンロードサイトからデータをダウンロードする
- 残念ながら駅のポイントデータはなかった・・(ラインだった)
- 観光資源のうち東京都を選択
- ダウンロードボタンを押したらGoogleフォームのアンケートが出てきた
- ダウンロードしたZipファイルを解凍する
中身はこんな感じでした~
GeoJSONファイルを用意する
- QGISを使ってシェープファイルをGeoJSONファイルに変換してエクスポートする
- ポイントデータはP12a.shpとかいてあるので、P12a-14_13.shpを使用する
- 変換方法はQGIS 操作マニュアルP.37を参照
- 出力したGeoJSONファイルはこんな感じ
- 長いため途中省略
{
"type": "FeatureCollection",
"name": "tourism_resources",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::6668" } },
"features": [
{ "type": "Feature", "properties": { "P12_001": 10034, "P12_002": "南島", "P12_003": "13", "P12_004": "13421", "P12_005": "海岸・岬", "P12_006": "小笠原村南島", "P12_007": -1 }, "geometry": { "type": "Point", "coordinates": [ 142.175460418424194, 27.03785670484675 ] } },
{ "type": "Feature", "properties": { "P12_001": 10032, "P12_002": "三原山溶岩群", "P12_003": "13", "P12_004": "13361", "P12_005": "岩石・洞窟", "P12_006": "大島町", "P12_007": -1 }, "geometry": { "type": "Point", "coordinates": [ 139.381705306298642, 34.737855699876953 ] } },
・・略・・
]
}
- GeoJSONファイルを配置する
- 今回はdataフォルダ内に配置した
GeoJSONファイルを読み込む
let lat = 35.7100069; // 緯度
let lng= 139.8108103; // 経度
let zoom = 16; // ズームレベル
let map = L.map("map"); // 地図の生成
map.setView([lat, lng], zoom); // 緯度経度、ズームレベルを設定する
// タイルレイヤを生成し、地図に追加する
// 今回はOpenStreetMapを表示する
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
{
// 著作権の表示
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}
).addTo(map);
// アイコンを追加する
// 外部のGeoJSONファイルを取得する
fetch("./data/tourism_resources.geojson")
.then(response => response.json())
// GeoJSONを地図に追加する
.then(data => {
L.geoJSON(data).addTo(map);
});
- GeoJSONファイルを取得する
- ネットで調べているとjQueryを使っているものが多かったが、
宗教上の理由であまりjQueryを使いたくなかったためfetchを採用した
- ネットで調べているとjQueryを使っているものが多かったが、
- 取得したGeoJSONファイルを地図に追加する
東京都の観光資源がわらわらと表示されました~
index.htmlをサーバ使わずそのまま表示するとCORSエラーでGeoJSONファイルが読み込めないので、Live Server等でサーバを立てて表示しましょう
カスタムアイコンを表示する
ポイントデータは、デフォルトのマーカーが生成されます
カスタムアイコンを表示したい場合は、pointToLayerオプションを使って、マーカー生成時にアイコン設定を渡せばよいです
let lat = 35.7100069; // 緯度
let lng= 139.8108103; // 経度
let zoom = 16; // ズームレベル
let map = L.map("map"); // 地図の生成
map.setView([lat, lng], zoom); // 緯度経度、ズームレベルを設定する
// タイルレイヤを生成し、地図に追加する
// 今回はOpenStreetMapを表示する
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
{
// 著作権の表示
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}
).addTo(map);
// アイコンを追加する
// カスタムアイコンの設定
var customIcon = L.icon({
iconUrl: './img/icon.png', // アイコン画像のURL
iconSize: [40, 40], // アイコンのサイズをピクセルで指定
iconAnchor: [25, 50], // アイコンの先端の座標(右上からの相対座標)
popupAnchor: [0, -50] // ポップアップが開く点の座標(iconAnchorが基準)
});
// 外部のGeoJSONファイルを取得する
fetch("./data/tourism_resources.geojson")
.then(response => response.json())
// GeoJSONを地図に追加する
.then(data => {
L.geoJSON(data, {
// カスタムアイコンを設定する
pointToLayer: function(feature, latlng) {
return L.marker(latlng, {icon: customIcon});
}
}).addTo(map);
});
- フリーアイコンをダウンロードしてimgフォルダに配置した
- アイコンの設定はMarkers With Custom Iconsを参考
- pointToLayer関数は、GeoJSONのポイントデータのレイヤを生成する方法を定義する関数で、データが追加されたときにポイントの特徴とその 位置情報を渡す
GeoJSONファイルのポイントデータにカスタムアイコンを設定できました~
ついでに、ポップアップも表示してみましょう
アイコンをクリックしたときにポップアップを表示する
地物ごとにポップアップを表示したい場合は、onEachFeatureオプションを使って地物にポップアップを追加します
地物の名前はGeoJSONから取り出してポップアップに表示させます
let lat = 35.7100069; // 緯度
let lng= 139.8108103; // 経度
let zoom = 16; // ズームレベル
let map = L.map("map"); // 地図の生成
map.setView([lat, lng], zoom); // 緯度経度、ズームレベルを設定する
// タイルレイヤを生成し、地図に追加する
// 今回はOpenStreetMapを表示する
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
{
// 著作権の表示
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}
).addTo(map);
// アイコンを追加する
// カスタムアイコンの設定
var customIcon = L.icon({
iconUrl: './img/icon.png', // アイコン画像のURL
iconSize: [40, 40], // アイコンのサイズをピクセルで指定
iconAnchor: [25, 50], // アイコンの先端の座標(右上からの相対座標)
popupAnchor: [0, -50] // ポップアップが開く点の座標(iconAnchorが基準)
});
// 外部のGeoJSONファイルを取得する
fetch("./data/tourism_resources.geojson")
.then(response => response.json())
// GeoJSONを地図に追加する
.then(data => {
L.geoJSON(data, {
// カスタムアイコンを設定する
pointToLayer: function(feature, latlng) {
return L.marker(latlng, {icon: customIcon});
},
// ポップアップを表示する
onEachFeature: function(feature, layer){
// 地物の名前を取り出す
let name = feature.properties.P12_002;
// ポップアップに名前を表示する
layer.bindPopup(name);
}
}).addTo(map);
});
- 地物の名前はGeoJSONファイルのpropertiesの"P12_002"に格納されていた
- 名前を取り出してポップアップに表示させる
- onEachFeature関数は、作成した地物毎に呼び出される関数で、イベントやポップアップを地物に追加できる
アイコンをクリックするとポップアップが表示されるようになりました~
GISっぽくなりましたね。
参考文献
- GeoJSONについて
- 利用したデータ
- シェープファイルからGeoJSONファイルへの変換
- Leafletについて