LoginSignup
1
1

More than 1 year has passed since last update.

(メモ)nodejsで 正六角形のポリゴン(ベクトルタイル、GeoJSON sequence)を作った

Last updated at Posted at 2023-02-05

はじめに

世の中には、たとえば Pixel Map Generator ( https://pixelmap.amcharts.com/ 下図 ) のように、正六角形のポリゴンを使って、世界地図を表現しているものがあります。
image.png
(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くらい?)で、さらに投影による歪みを考えると、中心点の配置感覚が東西方向に短いほうがいいかなぁ、、、と考えました。

image.png image.png

作業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のスクリプトを書きました。

config/default.hjson のなかで、一片の長さ、ベクトルタイル用の情報(レイヤ名、最大・最小ズーム )、もともとの投影法(ウェブメルカトル)のX方向の長さ、y方向の長さを入れておきます。
image.png
将来のために正六角形の隙間をつくる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などのパターンで作ってみました。用途によって大きさを使い分ければよさそうですね。

  • 400km
    image.png

  • 200km
    image.png

  • 100km
    image.png

まとめ

今回は六角形ポリゴンを作るところまでで終わりにしましたが、GeoJSON seqence から ベクトルタイルに変換するとこまでここに書きました。

どこかに使える六角形データはあると思いますが、自分で、一片の長さを決められる、ベクトルタイルの最大ズームと最小ズームを調整できる等の理由から自分でも作成してみました。

今後、発展させるとすると、、、

  • GeoJSON seqをGeoJSON 又はシェープファイルにして、空間解析を行い結果をベクトルタイルとして公開する。
  • 六角形ポリゴンにコード(例:国や地域など)をつけて、六角形のピクセルマップとして利用する。(最初に同僚が考えていた利用法)

などが考えられると思います。

参考ページ

1
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
1
1