はじめに
世の中には、たとえば Pixel Map Generator ( https://pixelmap.amcharts.com/ 下図 ) のように、正六角形のポリゴンを使って、世界地図を表現しているものがあります。
(Pixel Map Generator)
一方で、背景に使うことのできる正六角形のポリゴンデータが手元になかったので、作れるか少し確認しました。
作業環境
- Windows PC
- Docker (ベクトルタイルを作るときにunvt/nanbanでtippecanoeを使う)
- nodejs
作業手順
作業1. web mercatorの座標範囲の整理
https://epsg.io/3857 によれば、座標範囲はXmin, Ymin, Xmax, Ymaxの順に -20037508.34, -20048966.1, 20037508.34, 20048966.1 となっています。
X方向(東西方向)に約 40,075 km、Y方向に約 40,098 km です。この範囲に六角形を並べていきます。並べる角度をどうしようかなと考えましたが(例えば下の2つのパターン)、簡単のために下の左のパターンにしようと思います。
ウェブメルカトルの投影法では東西と南北で正方形になりますが、実際距離は東西約40,000kmに対し、南北約20,000km(本当は85度以北はないので、これより短い。19,000kmくらい?)で、さらに投影による歪みを考えると、中心点の配置感覚が東西方向に短いほうがいいかなぁ、、、と考えました。
作業2. スクリプトを考える。
六角形の中心座標を(cx, cy) 、一片の長さをr とすると、6つの点は以下のように与えられると思います。これを反時計回りに並べればポリゴンができます。
let p1 = [cx, cy + r]
let p2 = [cx - Math.sqrt(3) * r /2 , cy + r/2]
let p3 = [cx - Math.sqrt(3) * r /2 , cy - r/2]
let p4 = [cx, cy - r]
let p5 = [cx + Math.sqrt(3) * r /2 , cy - r/2]
let p6 = [cx + Math.sqrt(3) * r /2 , cy + r/2]
f.geometry.coordinates = [[p1,p2,p3,p4,p5,p6]]
中心点が並ぶyは以下のように与えます。赤道から北にまずy座標をリストしていき、そのあと赤道の下から南に向かってリストしていきます。
let ylist = []
let row = 0
let y = 0
while (y < ymax - r){
ylist.push(y)
row ++
y = y + 3*r/2
}
y = -3*r/2
while (y > ymin + r){
ylist.push(y)
row ++
y = y - 3*r/2
}
中心のy座標(cy)によって、中心のx座標(cx)の配置がかわるので、xについては以下のようにリストします。
for (let cy of ylist){
if ( cy % r == 0 ){
let xlist = []
let x = 0
console.log(`${cy} is A`)
while (x < xmax - Math.sqrt(3)*r/2){
xlist.push(x)
x = x + Math.sqrt(3)*r
}
x= - Math.sqrt(3)*r
while (x > xmin + Math.sqrt(3)*r/2){
xlist.push(x)
x = x - Math.sqrt(3)*r
}
for ( let cx of xlist){
id ++
f.properties._id = id
f.geometry.type = 'Polygon'
let p1 = [cx, cy + r]
let p2 = [cx - Math.sqrt(3) * r /2 , cy + r/2]
let p3 = [cx - Math.sqrt(3) * r /2 , cy - r/2]
let p4 = [cx, cy - r]
let p5 = [cx + Math.sqrt(3) * r /2 , cy - r/2]
let p6 = [cx + Math.sqrt(3) * r /2 , cy + r/2]
f.geometry.coordinates = [[p1,p2,p3,p4,p5,p6]]
f.tippecanoe.layer = layer
f.tippecanoe.minzoom = minz
f.tippecanoe.maxzoom = maxz
stream.write(JSON.stringify(f))
stream.write(', \n')
}
} else {
let xlist = []
let x = Math.sqrt(3)*r/2
console.log(`${cy} is B`)
while (x < xmax - Math.sqrt(3)*r/2){
xlist.push(x)
x = x + Math.sqrt(3)*r
}
x= - Math.sqrt(3)*r/2
while (x > xmin + Math.sqrt(3)*r/2){
xlist.push(x)
x = x - Math.sqrt(3)*r
}
for ( let cx of xlist){
id ++
f.properties._id = id
f.geometry.type = 'Polygon'
let p1 = [cx, cy + r]
let p2 = [cx - Math.sqrt(3) * r /2 , cy + r/2]
let p3 = [cx - Math.sqrt(3) * r /2 , cy - r/2]
let p4 = [cx, cy - r]
let p5 = [cx + Math.sqrt(3) * r /2 , cy - r/2]
let p6 = [cx + Math.sqrt(3) * r /2 , cy + r/2]
f.geometry.coordinates = [[p1,p2,p3,p4,p5,p6]]
f.tippecanoe.layer = layer
f.tippecanoe.minzoom = minz
f.tippecanoe.maxzoom = maxz
stream.write(JSON.stringify(f))
stream.write(', \n')
}
}
}
作業3. GeoJSON sequenceに出力する
上記の方針で、nodejsのスクリプトを書きました。
- index.js: https://github.com/ubukawa/hex/blob/main/index.js
- config file: https://github.com/ubukawa/hex/blob/main/config/default.hjson
config/default.hjson のなかで、一片の長さ、ベクトルタイル用の情報(レイヤ名、最大・最小ズーム )、もともとの投影法(ウェブメルカトル)のX方向の長さ、y方向の長さを入れておきます。
将来のために正六角形の隙間をつくるgapも作っていますが、実際には使っていません。(中心点から点を配置するとき、r=(1-gap)*r)で少し縮めるイメージでいました)
そして、index.jsを実行すると、GeoJSON Sequenceで出力できます。
node index.js
作業4. tippecanoeでベクトルタイルに変換する
できたGeoJSON sequence ファイルをfelt/tippecanoeでベクトルタイルに変換します。ビュワーでですぐに確認したいので、pmtilesにすることにします。
tippecanoe -o docs/hex70.pmtiles --no-tile-compression --no-feature-limit --no-tile-size-limit --projection=EPSG:3857 --force test-400.=EPSG:3857 --force test-400.geojsons
結果
作ったタイル(pmtiles)をPMTilesViewerで見てみました。今回、六角形の一辺が400km、200km、100kmなどのパターンで作ってみました。用途によって大きさを使い分ければよさそうですね。
まとめ
今回は六角形ポリゴンを作るところまでで終わりにしましたが、GeoJSON seqence から ベクトルタイルに変換するとこまでここに書きました。
どこかに使える六角形データはあると思いますが、自分で、一片の長さを決められる、ベクトルタイルの最大ズームと最小ズームを調整できる等の理由から自分でも作成してみました。
今後、発展させるとすると、、、
- GeoJSON seqをGeoJSON 又はシェープファイルにして、空間解析を行い結果をベクトルタイルとして公開する。
- 六角形ポリゴンにコード(例:国や地域など)をつけて、六角形のピクセルマップとして利用する。(最初に同僚が考えていた利用法)
などが考えられると思います。
参考ページ
- 今回の作業レポジトリ: https://github.com/ubukawa/hex
- Pixel Map Generator: https://pixelmap.amcharts.com/
- epsg.io (ウェブメルカトル): https://epsg.io/3857