今回は国土地理院の国土数値情報 行政区域データに含まれているシェープファイルをmapshperでTopoJSONに変換した時に生じる座標値の微妙なずれにつきましてご説明したく存じます。
なおTopoJSONに変換する際に「quantization」(日本語に訳すと「量子化」になります)というパラメータを使用するのですが、これはTopoJSON形式の特徴である「デルタエンコード」(日本語に訳すと「差分符合化もしくは差分圧縮」)をする際の精度に関わる重要な要素です。
ちなみにmapshaperで量子化をしない「no-quantization」というパラメータを使用すればずれは生じなくなるのですが、この場合は差分圧縮が働かないため、ファイルサイズがGeiJSONとほぼほぼ一緒で僅かに小さくなる程度になります。
まずは本題に入る前に行政区域データの中にに含まれている「シェープファイル」と「GeoJSONファイル」との座標値の桁数の違いについて見て行きます。なお行政区域データにはもう1つ「XMLファイル」も含まれておりますが、座標値が小数点以下8桁で先の2つよりも精度が押されえれているため除いています。
行政区域データのシェープファイルに設定されている座標値の桁数
QGISで秋田県の行政区域データのシェープファイルを開き、更に属性テーブルを開き最初のレコードをコピーしてExcelシートに貼り付けると下図のようになります。
Excelデータが示しているのは地図上にある一つの地域を表す多角形(ポリゴンと呼ばれる)の頂点の座標値になります。情報として経度と緯度がスペース区切りで並んでいます。
QGISでは数値は経度・緯度ともに小数点以下17桁で表示されていますが、シェープファイルの規格では座標値は倍精度になっています。
下記Wikipediaの「シェープファイル」をご参照ください。
となるとQGISでは17桁まで表示されているのですが、倍精度の有効桁数は概ね15桁になりますので、経度であれば小数点以下12桁、緯度であれば13桁までが有効な情報となります。
行政区域データのGeoJSONファイルに設定されている座標値の桁数
GeoJSONフォーマットは2016年8月にRFC7946として発表されていて、その中の11章2項に座標精度に関する記載があります。
下記に日本語訳を引用いたします。
バイト単位にGeoJSONテキストのサイズは、主要な相互運用性を考慮し、座標値の精度は、テキストの大きさに大きな影響を与えます。多くの詳細なポリゴンを含むにGeoJSONテキストは6〜15小数点以下の精度を座標増加させることによって2倍にほとんど膨張させることができます。
日本語訳だと解り難いところがありますが、最大で小数点以下15桁まで扱えるようですからシェープファイルの12桁・13桁よりも高精度になります。
行政区域データのGeoJSONファイルとシェープファイルをGeoJSON変換したデータを比べて見る
シェープファイルのGeoJSON変換はQGISでも実行することができますが、今回はmapshaperで変換したものを使用しています。
その理由ですが、最終的にコンテンツをTopoJSON形式でご提供するにあたりシェープファイルをmapshaperでTopoJSON形式に変換を致します。
そのためmapshaperでのGeoJSON変換の精度を調べておきたかったからです。
確認する地図情報は、先ほどシェープファイルの座標値を調べるために使った秋田県男鹿市のものと同じポリゴンです。
まず秋田県行政区域データにある「N03-19_05_190101.geojson」ファイルから該当箇所のポリゴンデータをExcelにコピーして加工します。
つぎに秋田県行政区域データにある「N03-19_05_190101.shp」ファイルをmapshaperでGeoJSONに変換し同じように該当箇所のポリゴンデータをExcelの別シートにコピーして、両者での違いを緯度・経度ごとに「表」にして確認しています。
比較して差が無い場合は「-」、差が生じている場合は「行政区域GeoJSON」と「シェープファイルからGeoJSONに変換」したそれぞれの値の小数点以下12桁から先を「-」区切りで表示しています。
経度 | ||
行政区域GeoJSON | シェープファイル変換 | 小数点以下12桁以降での差 |
139.69761411180184 | 139.69761411180184 | – |
139.69760966285571 | 139.6976096628557 | 571-57 |
139.69760260677492 | 139.69760260677492 | – |
139.6975940344372 | 139.6975940344372 | – |
139.69758438381234 | 139.69758438381234 | – |
139.69758243767944 | 139.69758243767944 | – |
139.69758167325563 | 139.69758167325563 | – |
139.69758160760512 | 139.69758160760512 | – |
139.69758306090955 | 139.69758306090955 | – |
139.69759330688566 | 139.69759330688566 | – |
139.6976143321358 | 139.6976143321358 | – |
139.69762194579619 | 139.6976219457962 | 619-62 |
139.69762824914449 | 139.6976282491445 | 449-45 |
139.69763167286351 | 139.6976316728635 | 351-35 |
139.69763344992384 | 139.69763344992384 | – |
139.69763891060734 | 139.69763891060734 | – |
139.69763946818694 | 139.69763946818694 | – |
139.6976366146381 | 139.6976366146381 | – |
139.6976297537102 | 139.6976297537102 | – |
139.69762408618271 | 139.6976240861827 | 271-27 |
139.6976180544298 | 139.69761805442977 | 98-977 |
139.69761411180184 | 139.69761411180184 | – |
緯度 | ||
行政区域GeoJSON | シェープファイル変換 | 小数点以下13桁以降での差 |
40.002152225344958 | 40.00215222534496 | 958-96 |
40.002151747804987 | 40.00215174780499 | 987-99 |
40.002152225344958 | 40.00215222534496 | 958-96 |
40.002155197604338 | 40.00215519760434 | 338-34 |
40.00216346776989 | 40.00216346776989 | – |
40.002167864555361 | 40.00216786455536 | 361-36 |
40.002172532036752 | 40.00217253203675 | 752-75 |
40.00217641441003 | 40.00217641441003 | – |
40.002179748196852 | 40.00217974819685 | 852-85 |
40.002189054381404 | 40.002189054381404 | – |
40.002192774876733 | 40.00219277487673 | 733-73 |
40.002193640024529 | 40.00219364002453 | 529-53 |
40.002193441274365 | 40.00219344127436 | 365-36 |
40.002193197558086 | 40.002193197558086 | – |
40.002192225390957 | 40.00219222539096 | 957-96 |
40.00218741401801 | 40.00218741401801 | – |
40.002182838267402 | 40.0021828382674 | 402-4 |
40.002171360220132 | 40.00217136022013 | 132-13 |
40.002162197927134 | 40.002162197927134 | – |
40.00215710776439 | 40.00215710776439 | – |
40.002153801856537 | 40.00215380185654 | 537-54 |
40.002152225344958 | 40.00215222534496 | 958-96 |
経度よりも緯度での差が多く出ているのは違いが起きている桁の違い(経度は小数点以下13桁目に対して緯度は14桁目が大半です)によるものと推察致します。
こうして比べて見ると黄色のアンダーラインを引いた1ヶ所以外は、もともとGeoJSONファイルとして提供されている方の精度が高く、そういった意味ではGeoJSONデータの方がシェープファイルよりも高精度の情報を提供していただいていると言えそうです。
ただ今回TopoJSONデータを作成するにあたり下記の2点の理由からGeoJSONデータではなくシェープファイルの方のデータを使用する事に致します。
その理由としては次の2点になります。
- 小数点以下13桁目・14桁目が正確であったとしても地図上では差が確認できないほど影響が小さい。
- 一般的に「シェープファイル」と言った方が認知度が高い。
TopoJSON変換時のquantizationパラメータの設定値
前置きが長くなってしまいましたが、本題に戻ります。
mapshaperでシェープファイルをTopoJSONファイルに変換する際に、パラメータは指定せずデフォルト値のままで変換する方が楽です。
一般的にTopoJSONファイルを使用する目的は、高精度の地図情報を求めるというよりも圧縮率が高くファイルサイズが小さくなるためであると認識しています。
そもそもTopoJSON変換で高精度のためにno-quantizationパラメータを指定してファイルサイズがGeoJSONとさほど変わらなくなる程大きくなってしまうのであれば、TopoJSONではなくGeoJSONをそのまま使用した方が精度を心配する必要が無くなります。
mapshaperのコマンドリファレンスでquantizationパラメータを調べると下記のように記載されています。
いずれかの次元に沿った微分可能な点の最大数として量子化を指定します。Mike Bostockのtopojsonプログラムのオプションに相当します。既定では、mapshaperは平均セグメント長の0.02に相当する量子化を適用します。
日本語訳で表示しています。
残念ながら書かれている内容がとても難解でポイントと思われるのは「デフォルトでは平均セグメント長の0.02に相当する値を適用します」という記載です。
「平均セグメント長」というのは文字通りに解釈すると変換するデータによって異なる計算値になると思います。
ただし具体的に「平均セグメント長」が何を表しているのか?につきましては「計算の対象にする値」を具体的に特定することはできませんでした。
quantizationパラメータを変更した際のTopoJSONファイルのファイルサイズ
ファイルサイズを確認するにあたり対象にする地域を決める必要があるのですが、今回は下記の秋田県北部(薄緑色に塗られた地域)を使用します。
なぜ秋田県北部を選んだのか?なのですが、内陸県ではない都道府県のなかで複雑な海岸線を一部だけ持っているというのが理由です。丁度男鹿半島の部分が複雑な地形になっています。
なお画像では解らないのですが、島・岩・港湾設備などの情報は残したままにしています。
そのためQGISプロパティに表示されている地物数は2,459になります。
このシェープファイルをmapshaperでTopoJSONファイルに変換した時のquantizationの値は下記になります。
パラメータ | 値 | TopoJSONファイルサイズ |
quantization | なし(デフォルト) | 1,496KB |
1,000,000(1e6) | 1,487KB | |
2,000,000(2e6) | 1,566KB | |
3,000,000(3e6) | 1,612KB | |
no-quantization | 5,766KB |
上記の表から推察できることとしては、「quantizationの値が大きくなるに従ってTopoJSONファイルサイズが大きくなる」事が挙げられます。
なおシェープファイルをGeoJSONファイルに変換した場合のサイズは6,717KBです。
推察が正しいとすると、デフォルトのquantizationパラメータは1,000,000と2,000,000の間で、値としては1,110,000に近い数字になりそうです。
仮に1,110,000が平均セグメント長に0.02を乗じた値だとすると平均セグメント長は55,500,000になるのですが、「長」の単位を例えばミリメートルとすると55.5Kmになります。
ただし先ほども書きましたがこれが何を表しているのか?を特定することはできませんでした。
quantizationパラメータにより座標精度がどのように変化するか?確認する
ようやく本題に入ります。
先ほどの表でquantizationパラメータがデフォルト値、2e6、3e6の3パターンで作成したTopoJSONデータを前回と同じく「Leaflet」使って重ね合わせて見る事にします。
ただし3パターンのTopoJSONデータだけでは「どれが一番精度が高いか?」を判断することはできません。
そこで、ファイルサイズは6MB強と大きくなってしまいますが、シェープファイルをGeoJSONファイルに変換したデータも合わせて表示させることにします。
ちなみにTopoJSONデータの中では「3e6」が一番ファイルサイズが大きくなっているので一番精度が向上している事が期待されます。
今回もまずは画面キャプチャーでご説明致します。その後で実際に動かしてご確認いただければ幸いです。
なお今回は4本の線を重ね合わせて表示させているために、そのままでは地図データが見難くなってしまいます。
そこでサイトを呼び出した時に左図のような入力ダイアログ画面を表示させています。
カーソルがセットされている画面の入力欄に1から3の数字を入力してOKボタンをクリックしていただくと、下記の表に合わせ組み合わせで線を2本に絞って画面に表示します。
入力値 | 線色 | quantization | GeoJSON |
1 | 緑 | デフォルト | 黒線 |
2 | 青 | 2,000,000(2e6) | 黒線 |
3 | 赤 | 3,000,000(3e6) | 黒線 |
シェープファイルをGeoJSON変換したデータは黒線で1から3どれを選んでも表示します。
なお「無入力でOKボタン」または「キャンセルボタン」または「1から3以外を入力してOKボタン」の場合はすべての線を表示します。
画面右上に表示されているコントロールボックスの中にある「マーカー表示(紫)」のチェックボックスをクリックすると画面上3ヶ所に紫色マーカーが表示されます。
それ以外の色のマーカーにつきましては次の章でご説明致します。
マーカー表示しているポイントは線のずれが確認し易い場所になりますが、画面で目視できるようになるのはズームレベル21以上になります。それを1レベルづつ画面左上の「+」ボタンやマウスのホイールやダブルクリックでズームアップして又元に戻るのは大変です。
そこで左図のようにマーカーの上にカーソルを合わせてクリックをします。
各行政区画にカーソルを当てると当該区画をハイライト表示にするため、ポインターの形は地図上では常に指差しマークになっています。ただしマーカーにカーソルが載るとハイライト表示が消えます。
紫色のマーカーに表示される「▽ズームイン」をクリックするとズームレベル24(最大レベルは25)まで一機に拡大します。
上から順に、初期画面で一番左のマーカー、真ん中のマーカー、一番右のマーカーの順に並べてあります。
精度に関しては如何でしょうか?
緑色の線が黒線に対して一番ずれが多いのは解ると思います。
青線と赤線の違いは微妙です。赤線が黒線に近い部分もあれば、青線の方が近い部分があるところもあります。
ただ総じて見ると若干赤線の方がずれが少なく感じます。
なおズームインする前のズームレベルに戻るには、マーカーをクッリクして開かれるポップアップウインドウの中の「△ズーム戻り」のリンクをクリックしてください。
初期画面の状態に戻るには「▲初期状態」をクリックします。
ひとつお伝えした置きたいのは、TopoJSON形式ではGeoJSON形式と比較して「座標値にずれが生じている」ことは事実です。
ただしズームレベル24とすると縮尺は画面に表示されているスケールバーを見ていただければ解るのですが、1メートルが画面上で約43ミリメートルでつまり縮尺は1/23.26となり、画面で1ミリずれていた場合実際の距離では23.26ミリのずれになります。
ではなぜこのような微妙な誤差の話をして来たか?なのですが、次の章でご説明するTopoJSONデータにした際に発生するintersections(線の交差)の話につなげるためになります。
下記リンクをクッリクしていただくと、上記でご説明したウェッブサイトが動かせる形で開きます。
Leafret.jpを使用したHTMLファイルになります。是非一度ご確認をいただければ幸いです。
なお恐れ入りますが動作確認をしたブラウザ環境は下記になります。
- Google Chrome バージョン: 81.0.4044.122
- Internet Explorer11 更新バージョン11.0.185(KB4550905)
- Microsoft Edge 44.18.18362.449.0
- FireFox 75.0
TopoJSONファイルに変換することで発生するintersections(線の交差)について
ここまでTopoJSON形式に変換する際の「座標値のトポロジー処理」でquantization(量子化)値を変更する事での効果につきまして見て来ました。
変換で発生する誤差につきましてはとても微細なものなので「利用に際しては気にしなくても良い範囲である」と認識していますが、ただしこの誤差に起因して「毛抜き合わせ」で表されている箇所がintersections(線の交差)になる場合があります。
mapshaperにはintersectionsを見つけてくれる機能「detect line intersections」があるだけではなく、左図でチェックボックスが付いていないオプション「snap vertices」というintersectionsを補正してくれる機能があります。
先の章で使用したquantizationパラメータがデフォルト値、2e6、3e6の3パターンで作成したTopoJSONデータそれぞれをmapshaperで調べると上から順に表示しています。
- デフォルト値では2ヶ所
- 2e6では1ヶ所
- 3e6では3ヶ所
結果的にすべてのパターンでintersectionsは発生し、しかもquantizationの値の増加には依存していない事が解ります。
座標精度を上げているにも関わらずintersectionsが逆に増えてしまうの理由はTopoJSONのトポロジー理論が解っていないため大変恐縮ではありますがご説明することができません。
前の章でご説明したコントロールボックスの中にある「マーカー表示(紫)」以外のマーカー表示をクリックしていただくとintersectionsが発生している場所をズームインして表示することができます。
quantizationがデフォルト値の時のintersectionsは2ヶ所でコントロールボックスのマーカー表示(緑)をクリックすると表示されます。
intersectionsになっている場所が少し離れているためズームレベル24以上に拡大すると両者が表示されません。
そのためマーカー表示(緑)の時はズームレベル23まで拡大させています。
なおズームアップすればズームレベル25まで拡大する事はできます。
quantizationが2e6の時のintersectionsは1ヶ所でコントロールボックスのマーカー表示(青)をクリックすると表示されます。
ズームレベルは25まで一機に上げています。
線の状況を確認するには一度コントロールボックスのチェックボックスに付けたチェックを外してみた方が解り易くなります。
ズーム戻りをする際には再度チェックを付けていただければと思います。
quantizationが3e6の時のintersectionsは3ヶ所でコントロールボックスのマーカー表示(赤)をクリックすると表示されます。
ズームインでズームレベルは25まで一機に上げています。
画面左にある2か所は近い場所にあるのでズームインしても2か所は同じ画面の中に納まります。
最後に
quantizationパラメータを変更することでの座標値の正確性の向上と、それにはリンクしないintersectionsの発生に対してどのように収拾を付けるか?は、今回のケースのようにquantizationパラメータのデフォルトを少し変更した時にintersectionsの発生個所が少なくなることもあり、その場その場で対応を決める必要があります。
つまり残念ながら決まった方法論がなく、試行錯誤をするしかありません。
強いて挙げるならば、デフォルト値でまずやってintersectionsの発生が多いのであればquantizationパラメータを変更して見るというやり方でしょうか?
「intersectionsがあるとどのような不都合があるか?」なのですが、WebシステムでTopoJSONを使用する分には、ズームレベルを上げないのであれば影響は無い認識です。
ただしPowerBIで使用する際はintersectionsが残っていると地図が表示されないケースがありますので注意が必要です。
「最終的に簡素化するのであれば対応しなくても問題は無いのでは?」という考え方もありますが問題の解決にはなりません。
このTopoJSONに発生するintersectionsに対応するためには、元となるシェープファイルを微妙に調整する必要があります。
もちろん元となるシェープファイルにはintersectionsは無い状態ですが、TopoJSONファイルに変換した時に線の交差が起きないように今回のケースであればライン間隔をいじる事になります。
正確であるファイルを変更するということに抵抗感はありますが、結果としてTopoJSONの座標値の精度が改善するはずですのでやむを得ない修正です。
「TopoJSON形式の将来はどうなるのか?」ですが、インターネット環境が今後も改善され続けて行くのであれば、GeoJSON形式のままの通信で問題が無くなるという世の中になる可能性はあります。
そうはいいながら一番期待したいのはTopoJSON変換のためのトポロジー理論が座標値の揺らぎが無いものに進化することですが…
以上最後までご一読いただき誠にありがとうございました。