スクリーンショット_2016-08-29_20.58.05

TopoJsonをCanvas上に3D表示する

TopoJsonという、地形情報をJSON形式で表した物がある。GeoJsonの親戚だ。これをJSで扱うにはD3.jsというSVG向けライブラリを使うのが一般的だ。しかし、場合によってはTHREE.jsという3Dライブラリを用いてCanvas上に表現したいこともある。いまやってる案件がそれだ。

一応、公式ではないが、TopoJsonデータをD3.jsを介して、THREE.jsへ持って行き、Canvas上に3D表現としてのせる方法というのは存在する。数は多くないが、故にあまりぶれない情報を得られる。

ここで実際に、その情報通りにコードを組んでみる。しかし、どこが原因なのかは分からないが、一部変な線が入ってしまう(画像参照)。みたところ「1国で地続きではない土地を持っている」場合、その複数の土地の輪郭を一筆書きのようにつなげようとしているようだ。フランスやアメリカなんかはひどい状態である。

確かにデータ上は、国単位で1つのオブジェクトとして管理されており、その中で、土地の輪郭を作るための頂点座標が記録されている。ただ、土地ごとに別配列になっており、本来ならこのような問題は起きないはずだ。おそらく、D3.js用データからTHREE.js用データに変換する際に、マージされてしまっているのだろう。

既存のライブラリを弄るには、さすがに手間がかかるので、データの方を弄ることにした。1つの国に複数の土地があったら、それらを別の国として扱い、1国1土地になるようにした。今回は国情報そのものに意味を持たないので、この分割が可能だった。

しかしやってみると次の問題が見つかる。一部の土地で、横方向に線が表示されてしまうのだ。一見、先ほどの同じ問題で、分割がうまく行っていないようにも見えるがそうではない。今回は、同じ土地ながら180度経線の位置で表示が分割されてしまい、しかしデータ上は1つながりなので、無理やり一筆書きをしようとしてるようにみえる。

いろいろ試した結果、これはD3.jsのprojectionの仕様で、子午線を中心に東西180度にTopoJsonを展開しようとするためにおこるようだ。データ上、頂点のx座標を+360して、数値上は近い位置になるように移動させても、表示はなおらなかった。

こうなったら、もう土地を分割するしかない。各土地の頂点座標配列を順次確認し、x座標が180以上飛ぶようなら、それはおそらく回りこんだんだろうと判断し、別の配列に突っ込むようにした。世界は球ではないのだ。平面なのだ。右端が左端につながっているはずがないのである。

これですべてがうまくいくと思われた。データ上はたしかに土地が分かれている。海はここを堺に、滝になるだろう。しかし世の中そんなに甘くなかった。変な土地が現れたのである。

ぱっと見、下記の4つの土地情報が生まれているように見る。

[[-180, 90], [180, 90], [180, 0]], //右上の三角形
[[180, 0], 180, -90], [0, -90]], // 右下の三角形
[[-180, 0], [0, -90], [-180, -90]], // 左下の三角形
[[-180, 90], [0, 0], [-180, 0]] // 左上の三角形

しかしこのデータが見つからない。THREE.js上では、各土地はそれぞれメッシュとして存在しているので、このメッシュに、配列の番号分の奥行きをつけて問題の土地を探したが、わかったのは「何番目の土地か」であり、何が問題なのかはわからなかった。座標を見ても、±30前後の値を持つ12個の頂点があるだけなのだ。

しかたがないので、この意味不明な土地を非表示にすることにした。これでやっと目的の「ありえない地続き表示」がなくなったのである。

こういう複雑なデータを処理するには、さすがに1から組むのは得策ではない。出来る限り既存のライブラリを使うのがベターなのだが、一方で、中でどのような処理がされているのかわからないし、わかったところで、直した時の「別の場所への影響」を考えると、あまり手を加えたくない。そうなると、出力を見ながら入力(データ)を変更して、いい感じにするしかないのだ。しかしこれはこれで苦行。なんかもっといい方法はないのだろうか…

※追記

QGISというツールで国情報確認してみたら、どうも「南アフリカ」のデータのようだ。必要な土地ならもう少し頑張って残そうかと思ったが、見た限りではこの12点が指し示す土地が見当たらない。というか、南アフリカに内包してる感じがする。表示が崩れる原因は結局分からないが、不要な土地として処理してしまうことにする。

この記事が気に入ったらサポートをしてみませんか?