0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Javaを使用して日本の地理空間データから標高情報を取得する方法

Last updated at Posted at 2023-08-09

地理情報の解析や研究において、標高情報は非常に重要です。この記事では、Javaを使用して日本の地理空間データから標高情報を取得する方法を解説します。

1. Mavenによるライブラリの設定

Javaプロジェクトで外部ライブラリを簡単に管理するために、Mavenを使用します。以下の依存関係をpom.xmlに追記することで、OkHttpライブラリをプロジェクトに追加できます。

<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>4.9.1</version> <!-- 最新のバージョンに更新してください -->
</dependency>

2. 標高データの取得

ElevationFetcherクラスを使用して、指定された緯度と経度に基づいて標高データを取得します。このクラスは、OkHttpライブラリを使用して、日本の地理空間データ提供サイトから標高データを取得します。

コードの詳細:

  • getOnlyElevationメソッド: 指定された緯度と経度の標高データを取得します。
  • fetchElevationメソッド: 緯度と経度をワールド座標に変換し、複数のDEMソースから標高データを取得します。
  • getElevationメソッド: ワールド座標をピクセル座標に変換し、指定されたDEMソースから標高データを取得します。

以下は、ElevationFetcherクラスの一部です:

import java.io.IOException;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

import okhttp3.OkHttpClient;
import okhttp3.Request;

/**
 * ElevationFetcherクラスは、指定された緯度と経度の標高データを取得するためのクラスです。
 * 日本の地理空間データ提供サイトから標高データを取得します。
 */
public class ElevationFetcher {

    private static final int          CONST_NO_DATA = -1;

    private static final String       BASE_URL      = "https://cyberjapandata.gsi.go.jp/xyz/";

    private static final OkHttpClient client        = new OkHttpClient();

    /**
     * メインメソッド。東京の緯度と経度を使用して標高データを取得し、結果を出力します。
     * @param args コマンドライン引数(未使用)
     */
    public static void main(final String[] args) {

        final var lon = 139.6917; // 東京の経度
        final var lat = 35.6895; // 東京の緯度

        try {
            final var elevation = ElevationFetcher.getOnlyElevation(lat, lon);
            System.out.println(elevation);
        } catch (final IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 指定された緯度と経度の標高データのみを取得します。
     * @param lat 緯度
     * @param lon 経度
     * @return 標高データ
     * @throws IOException データ取得中の例外
     */
    public static BigDecimal getOnlyElevation(final double lat, final double lon) throws IOException {

        try {
            final var resultMap = ElevationFetcher.fetchElevation(lat, lon);
            final var elevation = new BigDecimal(resultMap.get("elevation"));
            return elevation;
        } catch (final NumberFormatException e) {
            final var elevation = new BigDecimal(CONST_NO_DATA);
            return elevation;
        }
    }


    /**
     * 指定された緯度と経度の標高データを取得します。
     * @param lat 緯度
     * @param lon 経度
     * @return 標高データを含むMap
     * @throws IOException データ取得中の例外
     */
    public static Map <String, String> fetchElevation(final double lat, final double lon) throws IOException {

        final var lng_rad = Math.toRadians(lon);
        final var lat_rad = Math.toRadians(lat);
        final var R = 128 / Math.PI;
        final var worldCoordX = R * (lng_rad + Math.PI);
        final var worldCoordY = -1 * R / 2 * Math.log((1 + Math.sin(lat_rad)) / (1 - Math.sin(lat_rad))) + 128;

        var elevation = ElevationFetcher.getElevation(worldCoordX, worldCoordY, 15, "dem5a", 1);
        var hsrc = "5m(レーザ)";

        if (elevation == ElevationFetcher.CONST_NO_DATA) {
            elevation = ElevationFetcher.getElevation(worldCoordX, worldCoordY, 15, "dem5b", 1);
            hsrc = "5m(写真測量)";
        }

        if (elevation == ElevationFetcher.CONST_NO_DATA) {
            elevation = ElevationFetcher.getElevation(worldCoordX, worldCoordY, 14, "dem", 0);
            hsrc = "10m";
        }

        final Map <String, String> resultMap = new HashMap <>();
        if (elevation == ElevationFetcher.CONST_NO_DATA) {
            resultMap.put("elevation", "-----");
        } else {
            resultMap.put("elevation", String.format("%.2f", elevation)); // 標高データをStringに変換
        }
        resultMap.put("hsrc", hsrc);

        return resultMap;
    }


    /**
     * 指定されたワールド座標、ズームレベル、DEMソースから標高データを取得します。
     * @param worldCoordX ワールド座標X
     * @param worldCoordY ワールド座標Y
     * @param zoom ズームレベル
     * @param demSource DEMソース
     * @param dataRound 丸める小数点以下の桁数
     * @return 標高データの値
     * @throws IOException データ取得中の例外
     */
    private static Double getElevation(final double worldCoordX, final double worldCoordY, final int zoom, final String demSource,
            final int dataRound) throws IOException {

        final var PixelX = worldCoordX * Math.pow(2, zoom);
        final var TileX = (int) (PixelX / 256);
        final var PixelY = worldCoordY * Math.pow(2, zoom);
        final var TileY = (int) (PixelY / 256);
        final var PixelXint = (int) PixelX;
        final var px = PixelXint % 256;
        final var PixelYint = (int) PixelY;
        final var py = PixelYint % 256;

        final var sFileName = ElevationFetcher.BASE_URL + String.format("%s/%d/%d/%d.txt", demSource, zoom, TileX, TileY);
        final var request = new Request.Builder().url(sFileName).build();

        try (var response = ElevationFetcher.client.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                return (double) ElevationFetcher.CONST_NO_DATA;
            }

            final var responseBody = response.body();
            if (responseBody == null) {
                return (double) ElevationFetcher.CONST_NO_DATA;
            }

            final var lines = responseBody.string().split("\n");
            if (lines.length <= py) {
                return (double) ElevationFetcher.CONST_NO_DATA;
            }

            final var values = lines[py].split(",");
            if (values.length <= px) {
                return (double) ElevationFetcher.CONST_NO_DATA;
            }

            final var elevation = Double.parseDouble(values[px]);
            return Math.round(elevation * Math.pow(10, dataRound)) / Math.pow(10, dataRound);

        }
    }
}

標高タイルの詳細仕様(国土地理院より

標高タイルは、地図タイルと同一のタイル座標とピクセル座標を用いてデータを整備しています。
カンマ区切りのテキスト形式とPNG形式のファイルが提供されています。
標高タイルのデータ仕様では、256ピクセル×256ピクセルの地図タイルと関連づけられています。
テキスト形式の場合、1行にカンマ区切りで256個の標高値を表す数値データが格納されており、1枚の標高タイルは256行からなります。
PNG形式の場合、24ビットカラーのPNG形式で、一つのタイルの大きさは256ピクセル×256ピクセルです。

3. 結論

JavaとElevationFetcherクラス、そしてMavenの設定を適切に使用することで、日本の地理空間データから正確な標高情報を効率的に取得することができます。この手法は、地理情報の解析や研究に非常に役立ちます。

※参考

出典: 国土地理院 または 地理院タイル

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?