Top Banner
世世世世世世世世世 JavaFX ででででででJJUG CCC Fall 2016 #ccc_e2 でで で
82

JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

Apr 12, 2017

Download

Software

torutk
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

世界は四角ではない~ JavaFX で地図を描く~

JJUG CCC Fall 2016 #ccc_e2高橋 徹

Page 2: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

高橋 徹の自己紹介– コミュニティ活動

http://www.javareading.com/bof/毎月 1 回読書会開催中 次回 12/17

– ブログ等• http://www.torutk.com• http://d.hatena.ne.jp/torutk/ • Twitter @boochnich

2016-12 ~

2016-09 ~11

Page 3: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

アジェンダ

1. 地図の歪み2. 地図投影の原理3.JavaFX で投影地図を描く

Page 4: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

アジェンダ

1. 地図の歪み2. 地図投影の原理3.JavaFX で投影地図を描く

Page 5: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

この素晴らしい世界に祝福を

• インターネットで簡単に地図を利用

1. 手に取って

2. タップして

3. しゃべって

の 3 ステップで OK

地図データ : Google

Page 6: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

だがしかし

• 世界全体を表示してみると‥‥

1. 四角い

2. 北と南が巨大化

3. 南北端が切り捨て

メルカトル図法で投影した地図

地図データ : Google

Page 7: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

だがしかし

• 正方形の画像タイルでズーム対応

国土地理院 地理院タイル仕様の説明から引用 http://maps.gsi.go.jp/development/siyou.html

Web アーキテクチャに適している

Page 8: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

地図に面積を求めるのは間違っているのだろうか

3) インド

2) カザフスタン

1) アラスカ

問題)面積の大きい順に並べると?

A: 1) > 2) > 3)B: 3) > 2) > 1)

Page 9: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

地図に面積を求めるのは間違っているのだろうか

3) インド

2) カザフスタン

1) アラスカ

問題)面積の大きい順に並べると?

A: 1) > 2) > 3)B: 3) > 2) > 1)

Page 10: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

地図に面積を求めるのは間違っているのだろうか

3) インド2) カザフスタン1) アラスカ

グード図法で投影した世界地図、正積図法

Page 11: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

地図に面積を求めるのは間違っているのだろうか

国・州名 メルカトル図法 グード図法 統計データ

1) アラスカ州 781 万 km2 142 万 km2 172 万 km2

2) カザフスタン 639 万 km2 284 万 km2 282 万 km2

3) インド 377 万 km2 317 万 km2 329 万 km2

投影法により投影された地図上の面積

問題)面積の大きい順に並べると?

A: 1) > 2) > 3)B: 3) > 2) > 1)

Page 12: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

四角は地図の嘘

• 日本列島はどのような形状

メルカトル図法 アルベルス正積円錐図法

Page 13: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

四角は地図の嘘

アルベルス正積円錐

メルカトル

2 つの投影法で同一縮尺の表示を重ねて比較

Page 14: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

アジェンダ

1. 地図の歪み2. 地図投影の原理3.JavaFX で投影地図を描く

Page 15: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

Re: ゼロから始める地図投影

• 地球上の位置の表現(緯度経度)

グリニッジ子午線

赤道経度 λ

緯度 φ

「準拠回転楕円体」GRS80 WGS84

長半径 6,378,137m 6,378,137m短半径 6,356,752.31414m 6,356,752.314245

m

Page 16: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

Re: ゼロから始める地図投影

• 緯度経度の点を平面座標の点に変換

x = f(λ, φ)y = g(λ, φ)

点( λ, φ )点(x , y)

変換には何らかの歪みが生じる。• 面積比を維持:正積図法• 角度を維持: 正角図法

• 距離をある点で維持† 1 :正距図法• 方位をある点で維持† 1 :方位図法

Page 17: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

Re: ゼロから始める地図投影

• JavaFX での地図表示

y

x y

x

JavaFX の変換( Transform または Affine )を使用

スクロール、回転、拡大縮小が容易に実現

地図平面座標系 画面座標系

Page 18: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

Re: ゼロから始める地図投影

• 地図データの入手(無償)–全世界の海岸線データ(ベクターデータ)

GSHHG GSHHS Natural Earth Coastline

提供元 NOAA(アメリカ海洋大気庁)

ボランティアベース( NACIS†1 支援)

ライセンス LGPL パブリックドメイン

解像度(縮尺) 1/100万179,819レコード

1/1 千万4,132 レコード

データ形式 独自バイナリ形式、シェープファイル形式

シェープファイル形式

URL http://www.soest.hawaii.edu/pwessel/gshhg/index.html

http://www.naturalearthdata.com/downloads/

Page 19: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

Re: ゼロから始める地図投影

• 地図データの入手(無償)– データ解像度を対馬南部( 1/75万縮尺)で比較

GSHHG  GSHHS

Natural Earth

coastline

Page 20: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

アジェンダ

1. 地図の歪み2. 地図投影の原理3.JavaFX で投影地図を描く

Page 21: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

JavaFX駆け足紹介

• JavaFX の提供機能–ボタン、ラベル、グラフ等の UI部品– 2D グラフィックス(ベクトル、画像)– 3D グラフィックス(ポリゴン)–エフェクト(特殊効果)–アニメーション–サウンド–動画– Web 表示

「 Java SE Development Kit 8u112 Demos and Samples 」に含まれるEnsemble を動かしてみると一通り確認できます。

Page 22: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

JavaFX駆け足紹介

アプローチ1)すべて Java コードで記述

2)画面レイアウトと見栄えを FXML で記述、制御を Java コードで記述

3)画面レイアウトを FXML で記述、見栄えを CSS で記述、制御をJava コードで記述

1) 2) 3)

Javaソース

✔ ✔ ✔

FXML ✔ ✔CSS ✔

Page 23: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

JavaFX 開発環境(例)

今回使用した環境統合開発環境( IDE ) NetBeans IDE 8.2

( nightly build 20161122 )

画面レイアウトツール Scene Builder 8.2.0Java 開発キット Java SE Development Kit

8u112入手先• NetBeans https://netbeans.org/downloads/• Scene Builder http://gluonhq.com/labs/scene-builder/#

download• Java SE

http://www.oracle.com/technetwork/java/javase/downloads/

Page 24: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

プログラムの構成(1)

地図データ

投影変換

平面座標系ベクターデータ

描画

画面

• データフロー

緯度・経度座標系 地図平面直交座標系 画面直交座標系

Page 25: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

プログラムの構成(2)

• プログラム構造

地図ビュー地図ビューコントローラ地図モデル

アプリケーション制御

Shapefileリーダー Proj4j

Page 26: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ・バイ・ステップ目次

ステップ0

ステップ1

ステップ2

ステップ3

ステップ4

ステップ5

NetBeans JavaFX FXMLアプリケーションの雛形生成

画面レイアウトとバインディング

モデル作成、テストパターン表示

拡大・縮小、スクロール操作

シェープファイル読み込み

投影法の適用

Page 27: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ0

ステップ0 NetBeans JavaFX FXMLアプリケーションの雛形生成

Page 28: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ0

• JavaFX FXML アプリケーション雛形

地図ビューTinyMapView

地図ビューコントローラTinyMapViewController

地図モデル

アプリケーション制御TinyMapApp

JavaFX Application

FXMLJavaFX

Controller

Shapefileリーダー Proj4j

Page 29: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ0

• JavaFX FXML アプリケーション雛形– NetBeans IDE で新規プロジェクト作成

Page 30: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ0JavaFX FXML アプリケーション雛形生成時の命名は超重要

JAR ファイル名、インストーラ名、アプリケーション名

FXML ファイル名、コントローラクラス名の基底

パッケージ名、アプリケーション制御クラス名

Page 31: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ0

Page 32: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ1

ステップ0

ステップ1

ステップ2

ステップ3

ステップ4

ステップ5

NetBeans JavaFX FXMLアプリケーションの雛形生成

画面レイアウトとバインディング

モデル作成、テストパターン表示

拡大・縮小、スクロール操作

シェープファイル読み込み

投影法の適用

Page 33: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ1

ステップ1 画面レイアウトとバインディング

Page 34: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ1

ぽとり・ぺた でレイアウト

ウィンドウ上下左右との紐づけ

Scene Builderツールでお手軽画面作成

FXML ファイルに保存

Page 35: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ1

• バインディング(インジェクション)

@FXMLprivate Label scaleLabel;

@FXMLprivate Canvas mapCanvas;Scene Builder

( TinyMapView.fxml)Controller

( TinyMapViewController.java)

FXML ファイルを JavaFX がロードした際に、コントローラのインスタンスが生成され、 @FXML アノテーションが付いたフィールドには、UI コントロールのインスタンスがインジェクションされる。

Page 36: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

Scene Builder( TinyMapView.fxml)

ステップ1

@FXMLprivate void loadShapefile(ActionEvent event) { // …} Controller

( TinyMapViewController.java)

• バインディング

Page 37: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ1

• Canvas の大きさをウィンドウの大きさに追従させる@FXML private Canvas mapCanvas;@FXML private Pane rootPane; :@Overridepublic void initialize(URL url, ResourceBundle rb) { mapCanvas.widthProperty().bind( rootPane.widthProperty().subtract(120) ); mapCanvas.heightProperty().bind( rootPane.heightProperty() );}

Page 38: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ1

Page 39: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ2

ステップ0

ステップ1

ステップ2

ステップ3

ステップ4

ステップ5

NetBeans JavaFX FXMLアプリケーションの雛形生成

画面レイアウトとバインディング

モデル作成、テストパターン表示

拡大・縮小、スクロール操作

シェープファイル読み込み

投影法の適用

Page 40: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ2

ステップ2 モデル作成、テストパターン表示

Page 41: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ2

• プログラム構造

地図ビュー地図ビューコントローラ地図モデル

アプリケーション制御

Shapefileリーダー Proj4j

Page 42: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ2

• 地図データ(海岸線)– ポリライン(折れ線)データ

– Canvasにポリライン(折れ線)を描く– ポリラインを表現するクラスは?(1) javafx.scene.shape.Polyline(2) 独自クラスを定義

(x1, y1)

(x2, y2)

(x3, y3) (x4, y4)

Page 43: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ2

• Canvasへのポリラインの描画– javafx.scene.canvas.GraphicsContext

public void strokePolyline( double[] xPoints, double[] yPoints, int nPoints);

なお、 javafx.scene.shape.Polyline は、 1 つの配列で x,y を取るので API にインピーダンスミスマッチがあるPolyline polyline = new Polilyne(

0, 0, // (x1, y1) 10, 100, // (x2, y2) 20, 400, // (x3, y3));

Page 44: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ2

• 独自クラス TinyMapPolyline を作成

public class TinyMapPolyline { private final double[] xArray; private final double[] yArray;

public double[] getXArray() { return xArray; }

public double[] getYArray() { return yArray; } :

X 座標の値の配列Y 座標の値の配列で表現

Page 45: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ2

• モデルクラス TinyMapModel を作成–シェープファイルの指定–ポリライン集合の取得–投影法の指定

public class TinyMapModel {

public TinyMapModel(File file) {…}

public void loadLines() {…}

   public Stream<TinyMapPolyline> stream() {…}

Page 46: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ2

• テストパターンの表示– TinyMapViewController

private void drawMapCanvas() { GraphicsContext gc = mapCanvas.getGraphicsContext2D(); gc.setStroke(Color.LIGHTGREEN);

mapModel.stream().forEach(polyline -> gc.strokePolyline(polyline.getXArray(), polyline.getYArray(), polyline.size()); );}

Page 47: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ2

Page 48: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ3

ステップ0

ステップ1

ステップ2

ステップ3

ステップ4

ステップ5

NetBeans JavaFX FXMLアプリケーションの雛形生成

画面レイアウトとバインディング

モデル作成、テストパターン表示

拡大・縮小、スクロール操作

シェープファイル読み込み

投影法の適用

Page 49: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ3

ステップ3 拡大・縮小、スクロール操作

Page 50: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ2

• プログラム構造

地図ビュー地図ビューコントローラ地図モデル

アプリケーション制御

Shapefileリーダー Proj4j

Page 51: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ3

• マウスドラッグで平行移動// ドラッグで平行移動するための開始場所保持mapCanvas.setOnMousePressed(ev -> { dragStartPoint = new Point2D(ev.getSceneX(), ev.getSceneY()); mapTranslateAtDragStart = mapTranslate;});// ドラッグで平行移動mapCanvas.setOnMouseDragged(ev -> { Point2D drag = new Point2D(ev.getSceneX(), ev.getSceneY()); mapTranslate = mapTranslateAtDragStart.add( drag.subtract(dragStartPoint)); mapTransform.setToTransform( scaleProperty.get(), 0f, mapTranslate.getX(), 0f, -scaleProperty.get(), mapTranslate.getY()); drawMapCanvas();});

Page 52: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ3

• マウスホイールで拡大・縮小

mapCanvas.setOnScroll(ev -> { scaleProperty.set(ev.getDeltaY() >= 0 ? scaleProperty.get() * SCALE_RATE : scaleProperty.get() / SCALE_RATE); mapTransform.setToTransform( scaleProperty.get(), 0, mapTranslate.getX(), 0, -scaleProperty.get(), mapTranslate.getY()); drawMapCanvas();});

Page 53: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ3

• JavaFX Beans プロパティ–値の変更を伝搬(通知)する

private DoubleProperty scaleProperty = new SimpleDoubleProperty(1);

scaleProperty.addListener((target, oldValue, newValue) -> { scaleLabel.setText(String.format("1/%,d", (int) (1 / (newValue.doubleValue() * dotPitchInMeter))));});

プロパティの値が変更されたら処理を実行する

プロパティの定義

scaleProperty.set(1 / mapToScale(15_000_000));プロパティに値を設定

scaleProperty.get() * SCALE_RATEプロパティから値を取り出し

Page 54: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ3

Page 55: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ4

ステップ0

ステップ1

ステップ2

ステップ3

ステップ4

ステップ5

NetBeans JavaFX FXMLアプリケーションの雛形生成

画面レイアウトとバインディング

モデル作成、テストパターン表示

拡大・縮小、スクロール操作

シェープファイル読み込み

投影法の適用

Page 56: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ4

ステップ4 シェープファイル読み込み

Page 57: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ4

• プログラム構造

地図ビュー地図ビューコントローラ地図モデル

アプリケーション制御

Shapefileリーダー Proj4j

Page 58: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ4

• シェープファイル読み込みライブラリJava ESRI Shape File Reader

https://sourceforge.net/projects/javashapefilere/APL2.0 ライセンスJAR ファイル提供( 1 ファイル・ 37KB )

Page 59: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ4

• シェープファイルの選択画面

javafx.stage.FileChooser

Page 60: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ4

• シェープファイルの選択画面FileChooser chooser = new FileChooser();chooser.setTitle("シェープファイルを選択してね ");chooser.setInitialDirectory( Paths.get(System.getProperty("user.dir")).toFile());chooser.getExtensionFilters().add( new FileChooser.ExtensionFilter("Shapefile", "*.shp"));File selected = chooser.showOpenDialog(mapCanvas.getScene().getWindow());mapModel = new TinyMapModel(selected);try { mapModel.loadLines();} catch (TinyMapException ex) { showError("シェープファイルの読み込みでエラーが発生しました。 ", ex);}

Page 61: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ4

• シェープファイルの読み込み

ValidationPreferences pref = new   ValidationPreferences();pref.setAllowUnlimitedNumberOfPointsPerShape(true);

• 1 万頂点以上のポリラインを読み込み許可

try (InputStream inStream = new BufferedInputStream( new FileInputStream(mapFile))) {

} catch (IOException | InvalidShapeFileException ex) { throw new TinyMapException("シェープファイル読み込み時に例外発生 ", ex);}

海岸線(ポリライン)読み込み処理

• ファイル読み込み処理の骨格

Page 62: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ4

• ポリライン読み込み処理ShapeFileReader reader = new ShapeFileReader(inStream,pref);AbstractShape shape = reader.next();while (shape != null) { if (shape.getShapeType() != ShapeType.POLYLINE) { continue; } PolylineShape polyline = (PolylineShape) shape; for (int i = 0; i < polyline.getNumberOfParts(); i++) { TinyMapPolyline mapPolyline = createMapPolyline(polyline, i); polylines.add(mapPolyline); } shape = reader.next();} マルチパート対応処理

  1 つのレコードに連続しない複数のポリラインが格納されている場合の対応

ポリライン以外の形状は読み飛ばし

Page 63: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ4

• ポリライン読み込み処理TinyMapPolyline createMapPolyline(PolylineShape shape, int part) { PointData[] gcsPoints = shape.getPointsOfPart(part); double[] xArray = new double[gcsPoints.length]; double[] yArray = new double[gcsPoints.length]; for (int i = 0; i < gcsPoints.length; i++) { Point2D pcsPoint = projection.apply(gcsPoints[i]); xArray[i] = pcsPoint.getX(); yArray[i] = pcsPoint.getY(); } return new TinyMapPolyline(xArray, yArray); }x, y 座標値の配列へ

の詰め込み

// 緯度経度を m に単純変換する投影projection = p -> new Point2D( p.getX() * 100_000, p.getY() * 100_000);

Page 64: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ4

Page 65: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ5

ステップ0

ステップ1

ステップ2

ステップ3

ステップ4

ステップ5

NetBeans JavaFX FXMLアプリケーションの雛形生成

画面レイアウトとバインディング

モデル作成、テストパターン表示

拡大・縮小、スクロール操作

シェープファイル読み込み

投影法の適用

Page 66: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ5

ステップ5 投影法の適用

Page 67: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ5 .0• サンソン図法の変換を自前で実装public static final double R = 6_371_007.181; // 地球真球半径[m] private Function<PointData, Point2D> sansonProjection = p -> { double radX = Math.toRadians(p.getX()); double radY = Math.toRadians(p.getY()); double sx = R * radX * Math.cos(radY); double sy = R * radY; return new Point2D(sx, sy);};

Page 68: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ5

• プログラム構造

地図ビュー地図ビューコントローラ地図モデル

アプリケーション制御

Shapefileリーダー Proj4j

Page 69: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ5

• 地図投影変換ライブラリ  proj4J

http://trac.osgeo.org/proj4j/APL2.0 ライセンスJAR ファイル提供( 1 ファイル・ 2MB )maven リポジトリから入手可能

Page 70: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ5

• 複数の投影法の選択 UIpublic enum MapProjection { MOLLWEIDE("ESRI:54009"), MERCATOR("ESRI:54004"), ECKERT6("ESRI:54010"), CASSINI("ESRI:54028"); :

public void initialize(URL url, ResourceBundle rb) { ObservableList<MapProjection> list = FXCollections.observableArrayList( MapProjection.values() ); projectionComboBox.getItems().addAll(list);

Page 71: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ5

• 投影法を enum に実装

public static enum MapProjection { MOLLWEIDE("ESRI:54009"), MERCATOR("ESRI:54004"), ECKERT6("ESRI:54010"), CASSINI("ESRI:54028");

private MapProjection(String name) { .. }

public Function<PointData, Point2D> projection() { ..}}

空間座標系コードで投影法を指定

空間座標系コードを引数に取り初期化

投影変換の関数インタフェース実装を返す

Page 72: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ5

• 投影法を enum に実装(フィールド)

public static enum MapProjection { : private CRSFactory crsFactory = new CRSFactory(); private CoordinateTransformFactory ctFactory = new CoordinateTransformFactory(); private CoordinateReferenceSystem crsWgs84; private CoordinateReferenceSystem crsProjected; private CoordinateTransform transform; :}

変換前の緯度経度座標系

変換後の地図座標系

投影変換

Page 73: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ5

• 投影法を enum に実装(コンストラクタ)

public static enum MapProjection { : private MapProjection(String name) { crsWgs84 = crsFactory.createFromName("EPSG:4326"); crsProjected = crsFactory.createFromName(name); transform = ctFactory.createTransform( crsWgs84, crsProjected); } :}

変換前の緯度経度座標系

変換後の地図座標系

投影変換

Page 74: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ5

• 投影法を enum に実装(投影)

public static enum MapProjection { : public Function<PointData, Point2D> projection() { return point -> { ProjCoordinate gcsPoint = new ProjCoordinate(point.getX(), point.getY()); ProjCoordinate pcsPoint = new ProjCoordinate(); pcsPoint = transform.transform(gcsPoint, pcsPoint); return new Point2D(pcsPoint.x, pcsPoint.y); }; }}

変換前の緯度経度座標

変換後の地図座標

投影変換

Page 75: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

ステップ5

Page 76: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

地図ライブラリについて

• Java で本格的に地図を使用するなら、本格的なライブラリが有用(必要)

GeoTools(オープンソース LGPL )http://www.geotools.org

• 地図データ処理等充実• GUI はおまけ程度

ArcGIS Runtime SDK for Java (商用製品)https://developers.arcgis.com/java/latest/

• JavaFX GUI 対応

Page 77: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

• 緯度経度、測地系–書籍「地図投影法」政春尋志 著、朝倉書店、 2011年

–国土地理院 基本測量 日本の測地系http://www.gsi.go.jp/sokuchikijun/datum-main.html

– 「地図投影法学習のための地図画像素材集」から図を引用しました

http://user.numazu-ct.ac.jp/~tsato/tsato/graphics/map_projection/

参考資料

Page 78: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

参考資料

• 今回のサンプルプログラムhttps://github.com/torutk/jjugccc2016fall-javafx

Page 79: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

Q&A• Q) 地図データは間引かなくも使えますか?

– A) Natural Earth Coastline は比較的粗いデータなので全世界表示でもなんとか表示できましたが、 GSHHS のフルデータは重くて表示困難でした。 GSHHS は 5段階の間引きデータを提供しているので、表示する縮尺に応じて間引きレベルを選択するのがよいです。

Page 80: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

デモでの失敗• プロジェクタを接続し、画面設定を「複製」にしていたところ、デモプログラムの縮尺表示が「 1/0」となり、データが何も描画されない問題が発生した。– 画面設定を拡張にしてデモプログラムを起動、それをプロジェクタ画面へ移動

したところ縮尺表示が「 1/200,000,000」のように正常となり地図が表示された。

– 実行時に次のように画面解像度(ドットピッチ)を取得し、そこから地図縮尺を計算していた。プロジェクタ使用時(複製モード)にこの値がどうなっているかを追って調査する。

// 実行環境でのドットピッチを計算dotPitchInMeter = 1 / Screen.getPrimary().getDpi() * METER_PER_INCH;

Page 81: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

JJUG CCC をお楽しみください

Page 82: JJUG CCC 2016 Fall: World is not a square, rendering world coastline map with Java, JavaFX, shapefile reader and proj4J.

メモ

• シェープファイル( Shapefile )形式–ベクトルデータと属性情報など複数ファイル

で構成される–地図データでよく用いられる形式–仕様書が公開(以下 URL は日本語訳版)

https://www.esrij.com/cgi-bin/wp/wp-content/uploads/documents/shapefile_j.pdf