torutkのブログ

ソフトウェア・エンジニアのブログ

Javaで地図を表示〜GeoToolsを使って

はじめに

Java Advent Calendar 2012の17日目です。
エントリ時は、昨年度まで担当していたJavaの仕事でenumのいろいろな活用を知ったので、enumについて整理を兼ねて書こうと思っていました。しかし、Javaenumをググってみるとすでに数多くのenumの紹介記事があり、また、名著Effective Java 第2版 (The Java Series)で触れられていた「シングルトンをenumで」もちらほら紹介しているブログがあり、いまさら感がありました。
そこで、方針を変更して今回のエントリは、地図を表示するJavaプログラミングを取り上げることにしました。


一般的なソフトウェア技術系で目にする地図表示の多くは、グーグルマップ、Yahooマップ、マピオンなどのWebサービスを利用した「マッシュアップ」的なもので、GIS(地理情報システム)の知識*1がなくても手軽に地図表示を使った機能を実現できます。

しかし、インターネットへの常時接続*2が前提となるため、インターネット接続ができない機器での地図機能の使用、Webサービス側の提供する地図機能/品質*3が足りない場合、などに際しては、地図機能を作り込むことも必要になります。

今回は、無償で入手可能な地図データと、Java用のオープンソース地図ライブラリ(GeoTools)を使い、スタンドアロンで地図データを読み込み表示するプログラムをJavaで作る例を紹介します。

地図データ

地図データは、地理的な形状をベクターまたはラスター形式で表現し、データ個々には属性情報が付きます。例えば河川なら、形状に加えて河川の種別(1級河川、2級河川)や名前などが属性になります。

地理的な形状は、通常緯度経度座標で表現します*4

緯度経度は測地系の違いに注意

この緯度経度が曲者で、測地系が異なると同じ緯度経度の値でも違う位置となってしまいます。測地系は簡単にいえば(つまり不正確な説明というやつです)、緯度経度を決める際に地球の形をどう定義したかです。緯度経度は、地球中心を原点とした極座標系で地球表面の任意の点を表すので、地球の形の定義*5が違うと同じ場所が異なる緯度経度の値となってしまいます。

日本の地図データは、一昔前は「日本測地系」で緯度経度値化され、今は「世界測地系(JGD2000)で緯度経度値化されています。外国の地図データは、「世界測地系(WGS1984)」で緯度経度値化されているものが多いようです。
日本測地系は、地球の形をベッセル楕円体と定義し、世界測地系(JGD2000およびWGS1984)は地球の形をGSR80楕円体と定義しています。日本測地系世界測地系の差は日本付近では数百メートルとなります。ちなみに、JGD2000とWGS1984は通常の地図の使用であればほぼ同じとみなせます。

地図データの入手とフォーマット

現在、日本の電子地図データが国土交通省で整備され無償公開されているので、日本の地図を表示する場合、これを利用するのが一般的です。

国土交通省の地図データの概要や入手先については、先日書いた次のブログを参照ください。
国土数値情報(シェープファイル形式)をArcGISへ取り込み - torutkの日記

地図データフォーマットは、GISツールでほぼ標準となっているESRIシェープファイル形式を使います。GeoToolsも標準でシェープファイル形式を扱えます。

国土交通省数値地図情報は、今年からシェープファイル形式でも提供するようになったので、プログラムから簡単に利用できるようになりました。

地図データの加工

数値地図情報は、県別に地図データを提供しているものが多いので、日本全国を一度に表示するにはちょっと不便です。そこで、今回はあらかじめ専用のGISツール*6を使って1つのシェープファイルにまとめています。

地図ライブラリGeoTools

GeoToolsは、LGPLで提供されるオープンソースGISライブラリで、地図データの読み書き、変換、表示ほか各種GIS機能を持ちます。

入手

GeoToolsは、JAR形式ライブラリファイルの提供とmavenリポジトリの提供があります。ライブラリ、APIドキュメントの入手は、次のサイトから行います。本日時点で最新バージョンは8.4です。
GeoTools The Open Source Java GIS Toolkit — GeoTools

次のURLに、EclipseNetBeansMavenの設定方法があります。

本ブログは、Windows環境のNetBeans上でAntライブラリとしてGeoToolsライブラリを設定し利用する場合の記述となります。

GeoToolsライブラリのダウンロード

GeoToolsのページ上で[Downloads]リンクを辿り、[GeoTools 8 Releases]リンクを辿り(直上にGeoTools 8.0 Releaseのリンクがありますが、これは8.0であって、8.xではないので間違えないように要注意)、[8.4](最新版)リンクを辿り、次の2つをダウンロードします。

これを任意の場所に解凍・展開します。本ブログでは、C:\java以下に展開します。
まず、geotools-8.4-bin.zipをC:\java直下に展開します。C:\java\geotools-8.4\ディレクトリが生成され、中にかなり多数のJARファイルが配置されています。
次に、geotools-8.4-doc.zipをC:\java\geotools-8.4直下に展開します。C:\java\geotools-8.4\apidocs\ディレクトリが生成され、中にJavadocが展開されます。

C:\java
   +-- geotools-8.4
          +--- xxxxx.jar (多数のJARファイル)
          +--- apidocs
NetBeansのAntライブラリ設定作成

NetBeansを起動し、GeoToolsのライブラリ設定を作成します。
まず、[ツール]メニュー>[Antライブラリ]を選択し、「Antライブラリ・マネージャ」ダイアログで[新規ライブラリ]ボタンを押します。「新規ライブラリ」ダイアログでライブラリ名に"GeoTools"と入力し[OK]ボタンを押し、「Antライブラリ・マネージャ」ダイアログでライブラリ"GeoTools"のクラスパスとJavadocを指定します。
[クラスパス]タブを選択、[JAR/フォルダの追加]ボタンを押し、C:\java\geotools-8.4\ディレクトリ直下にあるJARファイルから、以下を除くすべてを選択します。

GeoToolsのQuickStartにある記載はバージョン2.6のもので少々古いため、上のリストではいくつか除外JARファイルを追加していますが、おそらくもう少し増えるのではないかと思います。とりあえずこれで単純なサンプルは動作しています。

「Antライブラリ・マネージャ」ダイアログで[Javadoc]タブを選択、[ZIP/フォルダの追加]ボタンを押し、c:\java\geotools-8.4\apidocsディレクトリを指定します。

サンプルプログラムの作成

最初の出発点として、シェープファイル形式のデータ1つだけを表示する簡単なサンプルプログラムを作成します。QuickStartのドキュメントに載っているものと同等です。

NetBeansで、[ファイル]メニューから[新規プロジェクト]を選び、プロジェクト種類にJavaアプリケーションを選択します。

作成したら、プロジェクトのプロパティでライブラリを選択し、[ライブラリの追加]ボタンを押して、ライブラリに先ほど作成した"GeoTools"を追加します。

今回はmainメソッドに直接処理を書いてしまいます。(あくまで説明用に簡素化)

    public static void main(String[] args) throws IOException {
        File file = JFileDataStoreChooser.showOpenFile("shp", null);
        if (file == null) {
            return;
        }

まず地図データのファイル選択ダイアログを表示しファイル選択をします。次のような画面になります*7

続いて、同じmainメソッドに記述していきます。

        FileDataStore store = FileDataStoreFinder.getDataStore(file);
        SimpleFeatureSource featureSource = store.getFeatureSource();

指定したシェープファイルに対応するフィーチャー集合(地図データ要素の集まり)の入力源を生成します。まだこの時点ではデータ自体の読み込みは行われません。データの読み込みは、実際に表示する範囲が決まってから必要なフィーチャ(地図データ要素)だけ行います。なお、IOExceptionがthrowされる可能性があるため、mainメソッドにthrows宣言を追加します。

mainメソッドの記述を続行します。

        MapContent map = new MapContent();
        map.setTitle("簡素なシェープファイルビューア");

表示対象の地図データをレイヤー単位で管理するほか、表示範囲・投影法などを管理するMapViewportを保持するMapContentを生成します。ウィンドウタイトルはここでsetTitleメソッドで指定します。

mainメソッドの記述を続行します。

        Style style = SLD.createSimpleStyle(featureSource.getSchema());
        Layer layer = new FeatureLayer(featureSource, style);
        map.addLayer(layer);

表示するスタイル(線の色等)を作成し、地図データのレイヤを作成します。レイヤは先ほどのMapContentに追加します。

mainメソッドの残りの記述をします。

        JMapFrame.showMap(map);

サンプルプログラムの実行

サンプルプログラムを実行し、国土数値情報の海岸線(日本全国マージ版)を指定します。

画面上部に並ぶツールバーには、拡大、縮小、移動、指定した地図データの詳細表示、地図有効範囲全体表示と並んでいます。

画面下部に並ぶツールバーには、設定、カーソル位置の経度・緯度、地図表示範囲、投影法が表示されています。

投影法に[GCS_JGD_2000]とあるのは、投影法未定義のため緯度経度をそのまま画面に直交座標として表示している状態(緯度経度投影)を示しており、日本列島の表示がやや横長になっています。

投影法の設定

画面右下の[GCS_JGD_2000]をクリックすると、ポップアップメニューが表示されます。[Set the reference system...]を選択すると、投影法選択のダイアログが表示されます。

投影法の表記の左端の数字はEPSGコード(European Petroleum Survey Group)です。測地系、投影法のパラメータ定義を特定するIDとなります。

日本付近を主眼とした投影法はこの選択肢には少ないので、ここでは「3099:JGD2000/UTM zone 53N」を指定してみます。UTMはユニバーサル横メルカトール図法で、ゾーン53Nは西日本付近を指します。UTMは本来大縮尺に使う投影なので日本全体の表示には使わないのですが・・・。

先ほどに比べ、日本列島が縦に細長くなったのが分かります。

サンプルコードとデータ

Githubに置いています。
https://github.com/torutk/geotoolapp

なお、地図データは7zipで圧縮したものをGithubに上げています。

最後に

本当は河川、鉄道、県・市区境界などの複数の地図データをレイヤ表示するといったことまでやりたかったのですが、今回は海岸線を表示するだけで力尽きてしまいました。地図データの加工に時間がかかったことと、投影法の指定をプログラム上で制御しようと調べて悩んでいたら時間切れになってしまったのです。

今後、もう少しGeoToolsの機能を使っていきたいと思っています。

*1:GISの知識は敷居が高く、GIS専門家ではないプログラマーとしては、GISを使う開発に携わって2年ちょっとになりますが、まだまだ知識/経験不足を覚える状態です。

*2:キャッシュを保持することによってインターネット接続が断続的になる場合でも機能を提供し続ける等の対応は進んできてはいます。

*3:iPhone5の地図品質は世間を騒がせています。

*4:局所的な地図データなら、特定の場所を原点とした直交座標(XY座標)で表現することもあります。

*5:地球を回転楕円体とする測地系であれば、中心位置、楕円の長軸、短軸の長さ

*6:商用製品で値の張るArcGISを使いましたが、オープンソースGISツールもいくつかあります。Quantum GISやuDigなどです。

*7:SwingのルックアンドフィールにはNimbusLookAndFeelを指定しています。