理系学生日記

おまえはいつまで学生気分なのか

GISを学びながらOpenStreetMapの地図をJavaで描画してみる

最近は地図と向き合う業務も出てきており、色々と学ばないといけません。

基礎知識

GIS

まず学ばないといけない大きな括りがGIS (Geographic Information System)で、これは地理情報を扱うシステムの総称を指します。

地理情報システムとは、地理情報および付加情報をコンピュータ上で作成・保存・利用・管理・表示・検索するシステムを言う

wikipedia:地理情報システム

地図表示機能やルート案内機能を持つGoogle MapsももちろんGISの1つと言えるでしょう。

測地系

一般に、地球上の位置は緯度と経度で表現されます。しかし、地球の形状は完全な球状ではなく、赤道が少し膨らんだ形(回転楕円体)となっています。これにより、同じ緯度・経度の値でも、その値が地球上のどの場所を示すかは、使用されている地球形状のモデルによって微妙に異なります。 したがって、地球上の位置を特定または表現する際には、どの地球形状のモデル(測地系)を使用しているのかを明記する必要があります。これにより、位置情報の精度と一貫性が保たれます。

世界で共通的に利用できる測地系は「世界測地系」と呼ばれ、WGS系、ITRF系、PZ系の3つがあります。WGS系、ITRF系はいずれも地球の形状と重力場をモデル化する測地系です。

ITRF系

ITRF系は、定期的に改訂され、最新の測地学的データを反映します。これにより、地球の動きや地殻変動をより正確に表現できます。このため、主として陸域で利用されるようです。

ITRF系は、我が国をはじめ多くの国家が陸域で採用しています。

(問1-6)世界測地系は単一のものではないのか。

JGD2011

JDG2011は日本が現在採用している測地系です。

日本が現在採用している測地系は、VLBIやGNSSなどの宇宙技術を利用して定めた「日本測地系2011」(JGD2011)といい、世界全体で共通に利用ができる世界測地系であるITRF(国際地球基準座標系)に基づいています。

日本の測地系 | 国土地理院

WGS系

WGS系は、地殻変動等を明示的にモデル化しているわけではありません。

世界的に使われている測地系がWGS84 (World Geodetic System 1984)です。 GPSや海洋航行で使用されており、Google Mapsや後述するOpenStreetMapsもWGS84を使っています。

OpenStreetMap

OpenStreetMapは、オープンデータの地理情報を使るプロジェクトです。したがって、OpenStreetMap上での地理情報の正確性はコミュニティ次第となります。 この正確性に関してはいくつか研究論文も存在しており、日本においても(場所によりその精度は大きく異なれど)全体的にはある程度の精度は保持しているように読み取れます。

ライセンスはODbLに基づきます。 サマリはこちら

OpenStreetMapのデータを使って地図をJavaで描画してみる

https://github.com/kiririmode/geotools-sandbox にソースを突っ込みました。 以下のコマンドを実行することで、OpenStreetMap (測地系はWGS84です)の地図を画面上に描画できます。

$ mvn exec:java -Dexec.mainClass=com.kiririmode.OsmMapFrame

実装ソースは以下のとおりで、とても単純ですね。利用しているのはGeoToolsという、Java用のGIS Toolkitです。

import org.geotools.map.MapContent;
import org.geotools.swing.JMapFrame;
import org.geotools.tile.TileService;
import org.geotools.tile.impl.osm.OSMService;
import org.geotools.tile.util.TileLayer;

public class OsmMapFrame {
    public static void main(String args[])
    {
        String baseURL = "https://tile.openstreetmap.org/";
        TileService service = new OSMService("OSM", baseURL);

        MapContent map = new MapContent();
        map.addLayer(new TileLayer(service));
        JMapFrame.showMap(map);
    }
}

実装ソースを見ていただいた時、まず馴染みのないのがTileServiceでしょう。これを理解するためには、Web上で地図がどのように配信されているかを理解する必要があります。

タイルサーバの利用

地図の配信は、多くの場合タイルサーバと呼ばれるサーバによって行われます。

タイルサーバは、地図データを「タイル」と呼ばれる小さな正方形の画像に分割して配信します。タイルは一般的に256ピクセルx 256ピクセルの大きさを持ち、それぞれが地図の特定の地域を表しています。Google Mapsも、先のOpenStreetMapもこのタイルサーバによって地図が配信されます。

ユーザが特定の地域をズームインまたはズームアウトすると、タイルサーバはその地域のタイルを生成または取得し、配信します。これにより、全体の地図を一度にダウンロードする必要がなくなるわけですね。

このタイルサーバを抽象化したものがTileServiceであり、TileServiceのOpenStreetMap(OSM)用実装がOSMServiceです。

GeoTools上のユーザガイドとしては、Tile clientあたりが該当です。

地図の描画

GeoToolsを利用して地図を描画しようとすると、描画用アーキテクチャを理解しなければなりません。 GeoToolsでは、描画する地図は「一連のレイヤ」として表現されます。この「一連のレイヤ」がMapContextです。Map data and display classesから引用しますが、MapContextとLayerは以下の関係になります。

したがって、上記ソースではレイヤの1つにTileLayerを指定することで、OpenStreetMapの世界地図を描画させているわけです。

地図の絞り込み

JMapFrameの機能を用いればズームイン等も可能なのですが、ここでは東京駅周辺を直に描画してみましょう。 以下のように変更してみます。

 git diff
diff --git a/src/main/java/com/kiririmode/OsmMapFrame.java b/src/main/java/com/kiririmode/OsmMapFrame.java
index 4cd1e3b..6364726 100644
--- a/src/main/java/com/kiririmode/OsmMapFrame.java
+++ b/src/main/java/com/kiririmode/OsmMapFrame.java
@@ -1,6 +1,9 @@
 package com.kiririmode;

+import org.geotools.geometry.jts.ReferencedEnvelope;
 import org.geotools.map.MapContent;
+import org.geotools.map.MapViewport;
+import org.geotools.referencing.crs.DefaultGeographicCRS;
 import org.geotools.swing.JMapFrame;
 import org.geotools.tile.TileService;
 import org.geotools.tile.impl.osm.OSMService;
@@ -14,6 +17,20 @@ public class OsmMapFrame {

         MapContent map = new MapContent();
         map.addLayer(new TileLayer(service));
+
+        // 東京駅の緯度と経度
+        double lat = 35.681236;
+        double lon = 139.767125;
+
+        // 表示範囲を設定
+        double span = 0.01;  // 緯度/経度での範囲
+        DefaultGeographicCRS crs = DefaultGeographicCRS.WGS84;
+        ReferencedEnvelope envelope = new ReferencedEnvelope(lon - span, lon + span, lat - span, lat + span, crs);
+
+        // MapContentに表示範囲を設定
+        MapViewport viewport = new MapViewport(envelope);
+        map.setViewport(viewport);
+
         JMapFrame.showMap(map);
     }
 }

MapViewportというクラスが登場していますが、これは描画範囲を指定する位置付けになります。

Represents the area of the map to drawn.

Map data and display classes — GeoTools 29-SNAPSHOT User Guide

今回はWGS84測位系での緯度・軽度の範囲を指定し、東京駅周辺を表示してみました。

まとめ

というわけでGIS初心者がGeoToolsとOpenStreetMapを使って、地図を描画する基本を試してみました。楽しい。