Статьи

Доступ к информации из GeoTIFF с использованием Java

Я всегда очень заинтересован в объединении информации из различных открытых источников данных. Последние пару недель я работал с информацией, связанной с ГИС, которая свободно доступна в Нидерландах. Чтобы быть более точным, я экспериментировал с данными из:

  1. СУМКА : Голландский для Basis Administratie Gebouwen, и содержит информацию обо всем здании в Нидерландах.
  2. OSM : Openstreetmap предоставляет пользовательские карты и дополнительную информацию

Я загрузил эти данные в Postgis, чтобы я мог легко поиграть и поэкспериментировать с ними. Используя эти данные, сравнительно легко создавать такие карты:

сумка-nha.png

Моей первоначальной целью было использовать эту информацию и создавать из них 3D-модели с помощью Three.js. Но, к сожалению, высота зданий не была предоставлена ​​этими источниками данных. К счастью, был дополнительный источник информации, который можно было использовать. Через  AHN  (Actueel Hoogtebestand Nederland) вы можете загружать карты высот из разных частей Нидерландов. Одним из  источников, которые  они предоставляют, являются необработанные нефильтрованные данные в виде растрового файла. Это означает, что здание, мосты и т. Д. Все еще там и не отфильтрованы. Например, такой файл выглядит так:

tiffExample.png

Этот файл содержит информацию о координатах, и для каждой координаты высота представляется в виде значения шкалы серого. Информация в этом файле хранится в формате  geoTIFF . Поэтому моя идея заключалась в том, чтобы получить координаты из зданий в базе данных. Хранить как обычные ГИС-объекты в postGIS и соотнести их с информацией о высоте из файла geoTIFF, чтобы определить высоту. Вероятно, не будет иметь наилучшей точности, но должен дать достаточно хорошее значение.

Сначала я посмотрел на прямую загрузку этой информации в postGIS и использование некоторых запросов для получения правильных значений, но, очевидно,  это  работает только с postGIS 2.0 и выше, и мне не хотелось обновлять и перезагружать все данные ГИС. Поэтому после некоторого поиска я наткнулся на библиотеку org.geotools, в которой есть большое количество javatools, связанных с ГИС. И один из них обрабатывает загрузку файлов geoTIFF для дальнейшей обработки. Поэтому я создал простую Java-программу для загрузки данных из postGIS, обогащения их информацией из geoTIFF и обновления базы данных.

Чтобы все заработало, я использовал следующий файл maven pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
 
    <groupId>geotiff</groupId>
    <artifactId>geotiff</artifactId>
    <version>1.0-SNAPSHOT</version>
 
    <dependencies>
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-geotiff</artifactId>
            <version>12.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-epsg-hsql</artifactId>
            <version>12.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>9.3-1102-jdbc41</version>
        </dependency>
        <dependency>
            <groupId>commons-dbutils</groupId>
            <artifactId>commons-dbutils</artifactId>
            <version>1.6</version>
        </dependency>
    </dependencies>
 
    <repositories>
        <repository>
            <id>maven2-repository.dev.java.net</id>
            <name>Java.net repository</name>
            <url>http://download.java.net/maven/2</url>
        </repository>
        <repository>
            <id>osgeo</id>
            <name>Open Source Geospatial Foundation Repository</name>
            <url>http://download.osgeo.org/webdav/geotools/</url>
        </repository>
        <repository>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
            <id>boundless</id>
            <name>Boundless Maven Repository</name>
            <url>http://repo.boundlessgeo.com/main</url>
        </repository>
    </repositories>
 
</project>

С установленными библиотеками реальный код не так сложен. Требуется некоторое время, чтобы экспериментировать со всеми различными объектами и методами, так как я не смог найти хорошую документацию. Ниже приведен полный код Java, который загружает geoTIFF и получает доступ к информации о высоте на основе местоположения из postGIS. Обратите внимание, что в этом случае оба прогноза одинаковы (SRID 28992), поэтому мне не нужно было конвертировать прогнозы:

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import org.geotools.coverage.grid.GridCoordinates2D;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.gce.geotiff.GeoTiffReader;
import org.geotools.geometry.DirectPosition2D;
 
import java.awt.image.Raster;
import java.io.File;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
 
/**
 * Created with IntelliJ IDEA.
 * User: jos
 * Date: 26/10/14
 * Time: 19:26
 * To change this template use File | Settings | File Templates.
 */
public class GeoTIFFTest {
 
    private final static String url = "jdbc:postgresql://localhost/bag";
    private static GridCoverage2D grid;
    private static Raster gridData;
 
    public static void main(String[] args) throws Exception {
        initTif();
        loadData();
    }
 
    private static void initTif() throws Exception {
        File tiffFile = new File("/Volumes/Iomega_HDD/mac/data/r44hn1.tif");
        GeoTiffReader reader = new GeoTiffReader(tiffFile);
 
        grid =reader.read(null);
        gridData = grid.getRenderedImage().getData();
    }
 
    private static double getValue(double x, double y) throws Exception {
 
        GridGeometry2D gg = grid.getGridGeometry();
 
        DirectPosition2D posWorld = new DirectPosition2D(x,y);
        GridCoordinates2D posGrid = gg.worldToGrid(posWorld);
 
        // envelope is the size in the target projection
        double[] pixel=new double[1];
        double[] data = gridData.getPixel(posGrid.x, posGrid.y, pixel);
        return data[0];
    }
 
    private static void loadData() throws Exception {
        Connection conn = DriverManager.getConnection(url);
        QueryRunner runner = new QueryRunner();
        final Map<Long, Double> map = new HashMap<Long, Double>();
        ResultSetHandler handler = new ResultSetHandler() {
 
            @Override
            public Object handle(ResultSet resultSet) throws SQLException {
                while (resultSet.next()) {
                    String point = resultSet.getString("point");
                    double x = Double.parseDouble(point.substring(
                            point.indexOf('(') + 1,
                            point.indexOf(' ')
                    ));
 
                    double y = Double.parseDouble(point.substring(
                            point.indexOf(' ') + 1,
                            point.indexOf(')')
                    ));
 
                    try {
                        double hoogte = getValue(x, y);
                        map.put(resultSet.getLong("gid"),hoogte);
                    } catch (Exception e) {
                        e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
                    }
                }
                return null;  //To change body of implemented methods use File | Settings | File Templates.
            }
        };
 
        runner.query(conn, "SELECT gid, ST_AsText(ST_Centroid(geovlak)) as point \n" +
                "FROM bag8mrt2014.pand\n" +
                "WHERE geovlak && ST_MakeEnvelope(130153, 408769,132896, 410774, 28992) ORDER by gid ;", handler);
 
        int count = 0;
        for (Long key : map.keySet()) {
 
            System.out.println("Inserting for key = " + key + " value: " + map.get(key));
            int col = runner.update(conn, "UPDATE bag8mrt2014.pand SET hoogte= ? where gid = ?",
                    map.get(key), key);
 
            count++;
 
            if (count%100 == 0) {
                System.out.println("count = " + count);
            }
        }
    }
}

Так что с обновленной информацией у меня теперь также есть информация о высоте в базе данных, так что я могу начать смотреть на рендеринг этой информации в 3D. Первые эксперименты обнадеживают 🙂

3d-bag.png