建物や地形のポリゴンの「中に」マーカーを立てたいケースはいろいろあります。
turf.js で用意されているポリゴン内の位置を返す様々な機能を見ていきましょう。
- 黒: center
- 青: centroid
- 緑: centerOfMass
- 赤: pointOnFeature
黒:center
関数名の通り、図形の「中心」を返します。
穴あきや「コの字」だった場合は、図形外になる可能性があります。
青:centroid
こちらは図形の「重心」を返します。
これも、穴あきや「コの字」だった場合は、図形外になる可能性があります。
緑:centerOfMass
ちょっとよく分かりません。API Doc には、
Takes any Feature or a FeatureCollection and returns its center of mass using this formula: Centroid of Polygon.
とあるので、centroid と同じと思いきや、若干異なる地点を示しています。
赤:pointOnFeature
関数名の通り「地物内の位置」を返します。
これが唯一、穴あきや「コの字」でもポリゴン内の位置を示しています。
しかし見た目上「いい感じ」とは言えないようです。
まとめ
ポリゴンにマーカーを立てたい時、中心や重心にマーカーが表示されるのが自然ですが、ポリゴンの形状によっては中心や重心はポリゴン外になる可能性があります。
pointOnFeature はポリゴン内の点を保証しますが、自然な位置とは言い難いです。
中心や重心がポリゴン内かをチェックしポリゴン内ならそれを採用、ポリゴン外ならば pointOnFeature を使う、という二段構えが良さそうです。
コードなど
const polygon1 = {
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[ 137.796, 34.940 ],
[ 137.748, 34.882 ],
[ 137.887, 34.888 ],
[ 137.796, 34.940 ]
],
[
[ 137.812, 34.926 ],
[ 137.779, 34.892 ],
[ 137.855, 34.897 ],
[ 137.812, 34.926 ]
]
]
}
};
const polygon2 = {
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[137.8877,34.9171],
[137.8867,34.8922],
[137.9134,34.8915],
[137.9135,34.8956],
[137.8918,34.8963],
[137.8923,34.9138],
[137.9057,34.9134],
[137.9058,34.9172],
[137.8877,34.9171]
]
]
}
};
const dataSource = {
'type': 'geojson',
'data': {
"type": "FeatureCollection",
"features": [
polygon1,
polygon2,
]
}
};
const lineFeature = {
type: 'FeatureCollection',
features: [
{
"type": "Feature",
"properties": {},
"geometry": {
"type":"LineString",
"coordinates": []
}
}
]
};
let marker;
const bounds = turf.bbox(dataSource.data);
const map = new mapboxgl.Map({
container: 'map',
bounds: bounds,
style: {
version: 8,
sources: {
OSM: {
type: "raster",
tiles: [
"https://a.tile.openstreetmap.org/{z}/{x}/{y}.png",
],
tileSize: 256,
attribution:
"OpenStreetMap",
},
},
layers: [{
id: "BASEMAP",
type: "raster",
source: "OSM",
minzoom: 0,
maxzoom: 18,
}],
},
});
map.once('load', () => {
map.fitBounds(bounds, { padding: { bottom: 50, top: 50, right: 50, left: 50 }});
map.addSource('route', dataSource);
map.addLayer({
'id': 'route',
'type': 'fill',
'source': 'route',
'paint': {
'fill-color': '#088',
'fill-opacity': 0.8
}
});
const addMarker = (lnglat, colorHex) => {
const el = document.createElement('img');
el.src = `https://img.icons8.com/material/32/${colorHex}/marker--v1.png`;
new mapboxgl.Marker(el, { anchor: 'bottom' })
.setLngLat(lnglat)
.addTo(map);
};
const center1 = turf.center(polygon1);
addMarker(center1.geometry.coordinates, '555555');
const centroid1 = turf.centroid(polygon1);
addMarker(centroid1.geometry.coordinates, '0000FF');
const centerOfMass1 = turf.centerOfMass(polygon1);
addMarker(centerOfMass1.geometry.coordinates, '00FF00');
const pointOnFeature1 = turf.pointOnFeature(polygon1);
addMarker(pointOnFeature1.geometry.coordinates, 'FF0000');
const center2 = turf.center(polygon2);
addMarker(center2.geometry.coordinates, '555555');
const centroid2 = turf.centroid(polygon2);
addMarker(centroid2.geometry.coordinates, '0000FF');
const centerOfMass2 = turf.centerOfMass(polygon2);
addMarker(centerOfMass2.geometry.coordinates, '00FF00');
const pointOnFeature2 = turf.pointOnFeature(polygon2);
addMarker(pointOnFeature2.geometry.coordinates, 'FF0000');
});