第02章 - 环境搭建与快速开始

第02章 - 环境搭建与快速开始

2.1 开发环境准备

2.1.1 Java 环境配置

GeoTools 31.x 需要 Java 17 或更高版本。推荐使用 Eclipse Temurin(原 AdoptOpenJDK)或 Oracle JDK。

Windows 安装步骤

  1. 下载 JDK 17:Eclipse Temurin
  2. 运行安装程序
  3. 配置环境变量:
:: 设置 JAVA_HOME
setx JAVA_HOME "C:\Program Files\Eclipse Adoptium\jdk-17.0.9.9-hotspot"

:: 添加到 PATH
setx PATH "%PATH%;%JAVA_HOME%\bin"

验证安装

java -version
# 输出:openjdk version "17.0.9" 2023-10-17
# OpenJDK Runtime Environment Temurin-17.0.9+9 (build 17.0.9+9)

javac -version
# 输出:javac 17.0.9

Linux/macOS 安装

# Ubuntu/Debian
sudo apt update
sudo apt install openjdk-17-jdk

# macOS (Homebrew)
brew install openjdk@17

# 验证安装
java -version

2.1.2 Maven 安装配置

GeoTools 使用 Maven 进行依赖管理,推荐 Maven 3.8.6 或更高版本。

下载安装

  1. 访问 Maven 下载页面
  2. 下载 Binary zip archive
  3. 解压到目标目录

配置环境变量

# Windows
setx M2_HOME "C:\apache-maven-3.9.6"
setx PATH "%PATH%;%M2_HOME%\bin"

# Linux/macOS
export M2_HOME=/opt/apache-maven-3.9.6
export PATH=$M2_HOME/bin:$PATH

验证安装

mvn -version
# Apache Maven 3.9.6 (bc0240f3c744dd6b6ec2920b3cd08dcc295161ae)
# Maven home: /opt/apache-maven-3.9.6
# Java version: 17.0.9, vendor: Eclipse Adoptium

2.1.3 配置 Maven 仓库

GeoTools 的依赖包托管在 OSGeo Maven 仓库,需要在 settings.xmlpom.xml 中配置。

全局配置 (~/.m2/settings.xml)

<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
          https://maven.apache.org/xsd/settings-1.0.0.xsd">
    
    <profiles>
        <profile>
            <id>geotools</id>
            <repositories>
                <repository>
                    <id>osgeo</id>
                    <name>OSGeo Release Repository</name>
                    <url>https://repo.osgeo.org/repository/release/</url>
                    <snapshots><enabled>false</enabled></snapshots>
                    <releases><enabled>true</enabled></releases>
                </repository>
                <repository>
                    <id>osgeo-snapshot</id>
                    <name>OSGeo Snapshot Repository</name>
                    <url>https://repo.osgeo.org/repository/snapshot/</url>
                    <snapshots><enabled>true</enabled></snapshots>
                    <releases><enabled>false</enabled></releases>
                </repository>
            </repositories>
        </profile>
    </profiles>
    
    <activeProfiles>
        <activeProfile>geotools</activeProfile>
    </activeProfiles>
    
    <!-- 可选:配置镜像加速 -->
    <mirrors>
        <mirror>
            <id>aliyun</id>
            <mirrorOf>central</mirrorOf>
            <name>Aliyun Maven Mirror</name>
            <url>https://maven.aliyun.com/repository/central</url>
        </mirror>
    </mirrors>
</settings>

2.1.4 IDE 配置

IntelliJ IDEA 配置

  1. 安装插件(可选):

    • Maven Helper
    • SonarLint
  2. 配置 Maven

    • File → Settings → Build, Execution, Deployment → Build Tools → Maven
    • 设置 Maven home path
    • 设置 User settings file
  3. 配置 JDK

    • File → Project Structure → Project
    • 选择 JDK 17
  4. 内存设置

    • Help → Edit Custom VM Options
    -Xms512m
    -Xmx2048m
    

Eclipse 配置

  1. 安装 m2e 插件

    • Help → Eclipse Marketplace
    • 搜索 "Maven Integration for Eclipse"
  2. 配置 Maven

    • Window → Preferences → Maven → Installations
    • 添加 Maven 安装目录
  3. 配置 JRE

    • Window → Preferences → Java → Installed JREs
    • 添加 JDK 17

2.2 创建 GeoTools 项目

2.2.1 使用 Maven 创建项目

命令行创建

# 创建项目目录
mkdir geotools-tutorial
cd geotools-tutorial

# 使用 Maven 原型创建项目
mvn archetype:generate \
    -DgroupId=com.example.geotools \
    -DartifactId=geotools-quickstart \
    -DarchetypeArtifactId=maven-archetype-quickstart \
    -DarchetypeVersion=1.4 \
    -DinteractiveMode=false

2.2.2 完整的 pom.xml 配置

<?xml version="1.0" encoding="UTF-8"?>
<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>com.example.geotools</groupId>
    <artifactId>geotools-quickstart</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>GeoTools Quickstart</name>
    <description>GeoTools 入门示例项目</description>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <geotools.version>31.0</geotools.version>
    </properties>

    <!-- GeoTools 仓库配置 -->
    <repositories>
        <repository>
            <id>osgeo</id>
            <name>OSGeo Release Repository</name>
            <url>https://repo.osgeo.org/repository/release/</url>
            <snapshots><enabled>false</enabled></snapshots>
            <releases><enabled>true</enabled></releases>
        </repository>
    </repositories>

    <!-- 使用 BOM 统一管理版本 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.geotools</groupId>
                <artifactId>geotools</artifactId>
                <version>${geotools.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <!-- ==================== 核心模块 ==================== -->
        
        <!-- GeoTools 核心 API -->
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-main</artifactId>
        </dependency>
        
        <!-- API 定义模块 -->
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-api</artifactId>
        </dependency>
        
        <!-- ==================== 数据格式 ==================== -->
        
        <!-- Shapefile 支持 -->
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-shapefile</artifactId>
        </dependency>
        
        <!-- GeoJSON 支持 -->
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-geojson</artifactId>
        </dependency>
        
        <!-- GeoPackage 支持 -->
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-geopkg</artifactId>
        </dependency>
        
        <!-- ==================== 坐标系统 ==================== -->
        
        <!-- 坐标参考系统核心 -->
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-referencing</artifactId>
        </dependency>
        
        <!-- EPSG 坐标系数据库(HSQL 实现) -->
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-epsg-hsql</artifactId>
        </dependency>
        
        <!-- ==================== 渲染模块 ==================== -->
        
        <!-- 地图渲染引擎 -->
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-render</artifactId>
        </dependency>
        
        <!-- Swing GUI 支持 -->
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-swing</artifactId>
        </dependency>
        
        <!-- ==================== 数据库支持 ==================== -->
        
        <!-- JDBC 核心 -->
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-jdbc</artifactId>
        </dependency>
        
        <!-- PostGIS 支持 -->
        <dependency>
            <groupId>org.geotools.jdbc</groupId>
            <artifactId>gt-jdbc-postgis</artifactId>
        </dependency>
        
        <!-- ==================== 测试依赖 ==================== -->
        
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- 编译插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.11.0</version>
                <configuration>
                    <source>17</source>
                    <target>17</target>
                </configuration>
            </plugin>
            
            <!-- 可执行 JAR 插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.5.1</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>com.example.geotools.App</mainClass>
                                </transformer>
                                <!-- 合并 SPI 服务文件 -->
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

2.2.3 项目结构

geotools-quickstart/
├── pom.xml
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── com/
│   │   │       └── example/
│   │   │           └── geotools/
│   │   │               ├── App.java
│   │   │               ├── DataStoreExample.java
│   │   │               ├── GeometryExample.java
│   │   │               └── MapRenderExample.java
│   │   └── resources/
│   │       └── log4j2.xml
│   └── test/
│       └── java/
│           └── com/
│               └── example/
│                   └── geotools/
│                       └── AppTest.java
└── data/
    └── sample/
        └── countries.shp

2.3 模块选择指南

2.3.1 按功能选择模块

根据项目需求选择必要的模块:

┌─────────────────────────────────────────────────────────────┐
│                    模块选择决策树                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   需要什么功能?                                             │
│   │                                                         │
│   ├─→ 基础几何处理                                          │
│   │   └─→ gt-main                                          │
│   │                                                         │
│   ├─→ 读写 Shapefile                                        │
│   │   └─→ gt-main + gt-shapefile                           │
│   │                                                         │
│   ├─→ 读写 GeoJSON                                          │
│   │   └─→ gt-main + gt-geojson                             │
│   │                                                         │
│   ├─→ 坐标转换                                              │
│   │   └─→ gt-referencing + gt-epsg-hsql                    │
│   │                                                         │
│   ├─→ 地图渲染                                              │
│   │   └─→ gt-render + gt-referencing                       │
│   │                                                         │
│   ├─→ 连接 PostGIS                                          │
│   │   └─→ gt-jdbc + gt-jdbc-postgis                        │
│   │                                                         │
│   ├─→ WMS/WFS 客户端                                        │
│   │   └─→ gt-wms / gt-wfs-ng                               │
│   │                                                         │
│   └─→ 空间分析处理                                          │
│       └─→ gt-process + gt-process-feature                  │
│                                                             │
└─────────────────────────────────────────────────────────────┘

2.3.2 常用模块组合

基础配置(最小依赖)

<dependencies>
    <dependency>
        <groupId>org.geotools</groupId>
        <artifactId>gt-main</artifactId>
    </dependency>
</dependencies>

Shapefile 处理

<dependencies>
    <dependency>
        <groupId>org.geotools</groupId>
        <artifactId>gt-shapefile</artifactId>
    </dependency>
    <dependency>
        <groupId>org.geotools</groupId>
        <artifactId>gt-epsg-hsql</artifactId>
    </dependency>
</dependencies>

地图渲染

<dependencies>
    <dependency>
        <groupId>org.geotools</groupId>
        <artifactId>gt-render</artifactId>
    </dependency>
    <dependency>
        <groupId>org.geotools</groupId>
        <artifactId>gt-swing</artifactId>
    </dependency>
    <dependency>
        <groupId>org.geotools</groupId>
        <artifactId>gt-epsg-hsql</artifactId>
    </dependency>
</dependencies>

数据库访问

<dependencies>
    <dependency>
        <groupId>org.geotools.jdbc</groupId>
        <artifactId>gt-jdbc-postgis</artifactId>
    </dependency>
    <!-- PostgreSQL 驱动 -->
    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <version>42.6.0</version>
    </dependency>
</dependencies>

2.3.3 EPSG 数据库选择

GeoTools 提供多种 EPSG 坐标系数据库实现:

模块 大小 特点 适用场景
gt-epsg-hsql ~6MB 完整数据库,启动快 通用开发
gt-epsg-wkt ~2MB 仅 WKT 定义 嵌入式应用
gt-epsg-extension ~6MB 支持自定义扩展 需要自定义 CRS

推荐:一般情况下使用 gt-epsg-hsql

2.4 快速开始示例

2.4.1 几何操作示例

package com.example.geotools;

import org.locationtech.jts.geom.*;
import org.geotools.geometry.jts.JTSFactoryFinder;

/**
 * JTS 几何操作示例
 */
public class GeometryExample {
    
    private static final GeometryFactory gf = JTSFactoryFinder.getGeometryFactory();
    
    public static void main(String[] args) {
        System.out.println("=== JTS 几何操作示例 ===\n");
        
        // 1. 创建各种几何类型
        createGeometries();
        
        // 2. 几何操作
        geometryOperations();
        
        // 3. 空间关系判断
        spatialRelations();
        
        // 4. 几何度量
        geometryMeasurements();
    }
    
    private static void createGeometries() {
        System.out.println("【创建几何对象】");
        
        // 创建点
        Point point = gf.createPoint(new Coordinate(116.4, 39.9));
        System.out.println("Point: " + point);
        
        // 创建线
        Coordinate[] lineCoords = {
            new Coordinate(0, 0),
            new Coordinate(10, 10),
            new Coordinate(20, 5)
        };
        LineString line = gf.createLineString(lineCoords);
        System.out.println("LineString: " + line);
        
        // 创建多边形
        Coordinate[] polyCoords = {
            new Coordinate(0, 0),
            new Coordinate(10, 0),
            new Coordinate(10, 10),
            new Coordinate(0, 10),
            new Coordinate(0, 0)  // 闭合点
        };
        Polygon polygon = gf.createPolygon(polyCoords);
        System.out.println("Polygon: " + polygon);
        
        // 从 WKT 创建
        try {
            WKTReader reader = new WKTReader(gf);
            Geometry geom = reader.read("POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))");
            System.out.println("从 WKT 创建: " + geom.getGeometryType());
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        System.out.println();
    }
    
    private static void geometryOperations() {
        System.out.println("【几何操作】");
        
        // 创建两个多边形
        Polygon poly1 = gf.createPolygon(new Coordinate[]{
            new Coordinate(0, 0),
            new Coordinate(10, 0),
            new Coordinate(10, 10),
            new Coordinate(0, 10),
            new Coordinate(0, 0)
        });
        
        Polygon poly2 = gf.createPolygon(new Coordinate[]{
            new Coordinate(5, 5),
            new Coordinate(15, 5),
            new Coordinate(15, 15),
            new Coordinate(5, 15),
            new Coordinate(5, 5)
        });
        
        // 缓冲区
        Geometry buffer = poly1.buffer(2);
        System.out.println("缓冲区面积: " + buffer.getArea());
        
        // 合并
        Geometry union = poly1.union(poly2);
        System.out.println("合并面积: " + union.getArea());
        
        // 相交
        Geometry intersection = poly1.intersection(poly2);
        System.out.println("相交面积: " + intersection.getArea());
        
        // 差集
        Geometry difference = poly1.difference(poly2);
        System.out.println("差集面积: " + difference.getArea());
        
        // 凸包
        Geometry convexHull = poly1.convexHull();
        System.out.println("凸包: " + convexHull);
        
        // 质心
        Point centroid = poly1.getCentroid();
        System.out.println("质心: " + centroid);
        
        System.out.println();
    }
    
    private static void spatialRelations() {
        System.out.println("【空间关系判断】");
        
        Polygon outer = gf.createPolygon(new Coordinate[]{
            new Coordinate(0, 0),
            new Coordinate(20, 0),
            new Coordinate(20, 20),
            new Coordinate(0, 20),
            new Coordinate(0, 0)
        });
        
        Point inside = gf.createPoint(new Coordinate(10, 10));
        Point outside = gf.createPoint(new Coordinate(30, 30));
        
        Polygon overlapping = gf.createPolygon(new Coordinate[]{
            new Coordinate(15, 15),
            new Coordinate(25, 15),
            new Coordinate(25, 25),
            new Coordinate(15, 25),
            new Coordinate(15, 15)
        });
        
        // 空间关系判断
        System.out.println("outer.contains(inside): " + outer.contains(inside));
        System.out.println("outer.contains(outside): " + outer.contains(outside));
        System.out.println("inside.within(outer): " + inside.within(outer));
        System.out.println("outer.intersects(overlapping): " + outer.intersects(overlapping));
        System.out.println("outer.disjoint(outside): " + outer.disjoint(outside));
        System.out.println("outer.overlaps(overlapping): " + outer.overlaps(overlapping));
        
        System.out.println();
    }
    
    private static void geometryMeasurements() {
        System.out.println("【几何度量】");
        
        Polygon polygon = gf.createPolygon(new Coordinate[]{
            new Coordinate(0, 0),
            new Coordinate(10, 0),
            new Coordinate(10, 10),
            new Coordinate(0, 10),
            new Coordinate(0, 0)
        });
        
        LineString line = gf.createLineString(new Coordinate[]{
            new Coordinate(0, 0),
            new Coordinate(3, 4)
        });
        
        Point p1 = gf.createPoint(new Coordinate(0, 0));
        Point p2 = gf.createPoint(new Coordinate(3, 4));
        
        System.out.println("多边形面积: " + polygon.getArea());
        System.out.println("多边形周长: " + polygon.getLength());
        System.out.println("线长度: " + line.getLength());
        System.out.println("两点距离: " + p1.distance(p2));
        System.out.println("包围盒: " + polygon.getEnvelopeInternal());
    }
}

2.4.2 读取 Shapefile 示例

package com.example.geotools;

import org.geotools.api.data.FileDataStore;
import org.geotools.api.data.FileDataStoreFinder;
import org.geotools.api.data.Query;
import org.geotools.api.data.SimpleFeatureSource;
import org.geotools.api.feature.simple.SimpleFeature;
import org.geotools.api.feature.simple.SimpleFeatureType;
import org.geotools.api.filter.Filter;
import org.geotools.api.filter.FilterFactory;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.factory.CommonFactoryFinder;
import org.locationtech.jts.geom.Geometry;

import java.io.File;
import java.io.IOException;

/**
 * Shapefile 读取示例
 */
public class ShapefileReadExample {
    
    public static void main(String[] args) {
        File file = new File("data/sample/countries.shp");
        
        if (!file.exists()) {
            System.out.println("请准备示例 Shapefile 文件: " + file.getAbsolutePath());
            return;
        }
        
        try {
            // 基本读取
            basicRead(file);
            
            // 带过滤条件读取
            filteredRead(file);
            
            // 属性投影读取
            projectedRead(file);
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 基本读取
     */
    private static void basicRead(File file) throws IOException {
        System.out.println("=== 基本读取 ===\n");
        
        // 打开数据源
        FileDataStore store = FileDataStoreFinder.getDataStore(file);
        SimpleFeatureSource source = store.getFeatureSource();
        
        // 获取要素类型信息
        SimpleFeatureType schema = source.getSchema();
        System.out.println("要素类型名称: " + schema.getTypeName());
        System.out.println("坐标参考系: " + schema.getCoordinateReferenceSystem().getName());
        System.out.println("属性数量: " + schema.getAttributeCount());
        System.out.println("几何属性: " + schema.getGeometryDescriptor().getLocalName());
        
        // 打印所有属性
        System.out.println("\n属性列表:");
        schema.getAttributeDescriptors().forEach(attr -> {
            System.out.printf("  - %s: %s%n", 
                attr.getLocalName(), 
                attr.getType().getBinding().getSimpleName());
        });
        
        // 读取要素
        SimpleFeatureCollection collection = source.getFeatures();
        System.out.println("\n要素总数: " + collection.size());
        
        // 遍历前 5 个要素
        System.out.println("\n前 5 个要素:");
        int count = 0;
        try (SimpleFeatureIterator features = collection.features()) {
            while (features.hasNext() && count < 5) {
                SimpleFeature feature = features.next();
                Geometry geom = (Geometry) feature.getDefaultGeometry();
                System.out.printf("  %s - 几何类型: %s, 顶点数: %d%n",
                    feature.getID(),
                    geom.getGeometryType(),
                    geom.getNumPoints());
                count++;
            }
        }
        
        // 关闭数据源
        store.dispose();
        System.out.println();
    }
    
    /**
     * 带过滤条件读取
     */
    private static void filteredRead(File file) throws IOException {
        System.out.println("=== 过滤读取 ===\n");
        
        FileDataStore store = FileDataStoreFinder.getDataStore(file);
        SimpleFeatureSource source = store.getFeatureSource();
        
        // 创建过滤器工厂
        FilterFactory ff = CommonFactoryFinder.getFilterFactory();
        
        // 创建属性过滤器(假设有 NAME 属性)
        Filter filter = ff.equals(ff.property("NAME"), ff.literal("China"));
        
        // 应用过滤器
        SimpleFeatureCollection filtered = source.getFeatures(filter);
        System.out.println("过滤后要素数: " + filtered.size());
        
        try (SimpleFeatureIterator features = filtered.features()) {
            while (features.hasNext()) {
                SimpleFeature feature = features.next();
                System.out.println("找到: " + feature.getAttribute("NAME"));
            }
        }
        
        store.dispose();
        System.out.println();
    }
    
    /**
     * 属性投影读取(只读取部分属性)
     */
    private static void projectedRead(File file) throws IOException {
        System.out.println("=== 属性投影读取 ===\n");
        
        FileDataStore store = FileDataStoreFinder.getDataStore(file);
        SimpleFeatureSource source = store.getFeatureSource();
        
        // 创建查询,只读取指定属性
        Query query = new Query(source.getSchema().getTypeName());
        query.setPropertyNames("the_geom", "NAME");  // 只读取几何和 NAME 属性
        query.setMaxFeatures(10);  // 限制返回数量
        
        SimpleFeatureCollection collection = source.getFeatures(query);
        
        System.out.println("查询返回要素数: " + collection.size());
        
        try (SimpleFeatureIterator features = collection.features()) {
            while (features.hasNext()) {
                SimpleFeature feature = features.next();
                System.out.printf("  %s: %s%n", 
                    feature.getID(),
                    feature.getAttribute("NAME"));
            }
        }
        
        store.dispose();
    }
}

2.4.3 坐标转换示例

package com.example.geotools;

import org.geotools.api.referencing.FactoryException;
import org.geotools.api.referencing.crs.CoordinateReferenceSystem;
import org.geotools.api.referencing.operation.MathTransform;
import org.geotools.api.referencing.operation.TransformException;
import org.geotools.geometry.jts.JTS;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.geotools.referencing.CRS;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;

/**
 * 坐标转换示例
 */
public class CRSTransformExample {
    
    public static void main(String[] args) {
        try {
            // 1. 基本坐标转换
            basicTransform();
            
            // 2. 几何对象转换
            geometryTransform();
            
            // 3. 常用坐标系信息
            crsInfo();
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 基本坐标转换
     */
    private static void basicTransform() throws FactoryException, TransformException {
        System.out.println("=== 基本坐标转换 ===\n");
        
        // 定义源和目标坐标系
        CoordinateReferenceSystem sourceCRS = CRS.decode("EPSG:4326");  // WGS84
        CoordinateReferenceSystem targetCRS = CRS.decode("EPSG:3857");  // Web Mercator
        
        System.out.println("源坐标系: " + sourceCRS.getName());
        System.out.println("目标坐标系: " + targetCRS.getName());
        
        // 创建转换器
        MathTransform transform = CRS.findMathTransform(sourceCRS, targetCRS, true);
        
        // 转换坐标(北京)
        double[] srcCoord = {116.4074, 39.9042};  // 经度, 纬度
        double[] dstCoord = new double[2];
        
        transform.transform(srcCoord, 0, dstCoord, 0, 1);
        
        System.out.println("\n北京坐标转换:");
        System.out.printf("  WGS84: (%.4f, %.4f)%n", srcCoord[0], srcCoord[1]);
        System.out.printf("  Web Mercator: (%.2f, %.2f)%n", dstCoord[0], dstCoord[1]);
        
        // 反向转换
        MathTransform inverseTransform = transform.inverse();
        double[] backCoord = new double[2];
        inverseTransform.transform(dstCoord, 0, backCoord, 0, 1);
        
        System.out.printf("  反向转换: (%.4f, %.4f)%n", backCoord[0], backCoord[1]);
        System.out.println();
    }
    
    /**
     * 几何对象转换
     */
    private static void geometryTransform() throws FactoryException, TransformException {
        System.out.println("=== 几何对象转换 ===\n");
        
        GeometryFactory gf = JTSFactoryFinder.getGeometryFactory();
        
        // 创建 WGS84 坐标的点
        Point wgs84Point = gf.createPoint(new Coordinate(116.4074, 39.9042));
        
        // 获取转换器
        CoordinateReferenceSystem sourceCRS = CRS.decode("EPSG:4326");
        CoordinateReferenceSystem targetCRS = CRS.decode("EPSG:32650");  // UTM Zone 50N
        MathTransform transform = CRS.findMathTransform(sourceCRS, targetCRS, true);
        
        // 转换几何
        Geometry utmPoint = JTS.transform(wgs84Point, transform);
        
        System.out.println("WGS84 点: " + wgs84Point);
        System.out.println("UTM Zone 50N 点: " + utmPoint);
        
        // 转换坐标
        Coordinate srcCoord = new Coordinate(116.4074, 39.9042);
        Coordinate dstCoord = JTS.transform(srcCoord, null, transform);
        
        System.out.printf("\n坐标转换: (%.4f, %.4f) -> (%.2f, %.2f)%n",
            srcCoord.x, srcCoord.y, dstCoord.x, dstCoord.y);
        System.out.println();
    }
    
    /**
     * 常用坐标系信息
     */
    private static void crsInfo() throws FactoryException {
        System.out.println("=== 常用坐标系信息 ===\n");
        
        String[] epsgCodes = {"EPSG:4326", "EPSG:3857", "EPSG:4490", "EPSG:32650"};
        
        for (String code : epsgCodes) {
            CoordinateReferenceSystem crs = CRS.decode(code);
            System.out.println(code + ":");
            System.out.println("  名称: " + crs.getName());
            System.out.println("  WKT 前100字符: " + crs.toWKT().substring(0, 
                Math.min(100, crs.toWKT().length())) + "...");
            System.out.println();
        }
    }
}

2.4.4 地图显示示例

package com.example.geotools;

import org.geotools.api.data.FileDataStore;
import org.geotools.api.data.FileDataStoreFinder;
import org.geotools.api.data.SimpleFeatureSource;
import org.geotools.api.style.Style;
import org.geotools.map.FeatureLayer;
import org.geotools.map.Layer;
import org.geotools.map.MapContent;
import org.geotools.styling.SLD;
import org.geotools.swing.JMapFrame;

import java.awt.Color;
import java.io.File;

/**
 * 地图显示示例
 */
public class MapDisplayExample {
    
    public static void main(String[] args) throws Exception {
        // 打开 Shapefile
        File file = new File("data/sample/countries.shp");
        if (!file.exists()) {
            System.out.println("请准备示例数据文件: " + file.getAbsolutePath());
            System.out.println("可以从 Natural Earth (naturalearthdata.com) 下载国家边界数据");
            return;
        }
        
        FileDataStore store = FileDataStoreFinder.getDataStore(file);
        SimpleFeatureSource featureSource = store.getFeatureSource();
        
        // 创建地图内容
        MapContent map = new MapContent();
        map.setTitle("GeoTools 地图显示示例");
        
        // 创建样式
        Style style = SLD.createPolygonStyle(
            Color.BLUE,      // 边框颜色
            Color.CYAN,      // 填充颜色
            0.5f             // 透明度
        );
        
        // 创建图层并添加到地图
        Layer layer = new FeatureLayer(featureSource, style);
        map.addLayer(layer);
        
        // 显示地图窗口
        JMapFrame.showMap(map);
    }
}

2.5 常见问题排查

2.5.1 依赖下载问题

问题:无法下载 GeoTools 依赖

Could not resolve dependencies for project: Could not find artifact 
org.geotools:gt-main:jar:31.0 in central

解决方案

  1. 确认 pom.xml 中添加了 OSGeo 仓库
  2. 检查网络连接
  3. 清理本地仓库缓存:
mvn dependency:purge-local-repository
mvn clean install

2.5.2 EPSG 数据库问题

问题:找不到 EPSG 代码

org.geotools.referencing.factory.FactoryNotFoundException: 
No factory found for EPSG:4326

解决方案

添加 EPSG 数据库依赖:

<dependency>
    <groupId>org.geotools</groupId>
    <artifactId>gt-epsg-hsql</artifactId>
</dependency>

2.5.3 SPI 服务加载问题

问题:打成 JAR 后无法运行

java.lang.IllegalArgumentException: No DataStoreFactory found for parameters

解决方案

使用 maven-shade-plugin 并配置 ServicesResourceTransformer:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <configuration>
        <transformers>
            <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
        </transformers>
    </configuration>
</plugin>

2.5.4 内存问题

问题:OutOfMemoryError

解决方案

增加 JVM 内存:

# 运行时
java -Xms512m -Xmx2g -jar your-app.jar

# Maven 编译时
export MAVEN_OPTS="-Xms512m -Xmx2g"
mvn clean install

2.5.5 日志配置

添加 log4j2.xml 配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </Console>
    </Appenders>
    <Loggers>
        <Logger name="org.geotools" level="INFO"/>
        <Logger name="org.geotools.data" level="DEBUG"/>
        <Root level="INFO">
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>

2.6 本章小结

本章介绍了 GeoTools 开发环境的搭建:

  1. 环境准备

    • JDK 17+ 安装配置
    • Maven 3.8.6+ 安装配置
    • OSGeo 仓库配置
  2. 项目创建

    • Maven 项目结构
    • pom.xml 完整配置
    • 模块选择指南
  3. 快速示例

    • 几何操作
    • Shapefile 读取
    • 坐标转换
    • 地图显示
  4. 问题排查

    • 依赖问题
    • EPSG 问题
    • 内存配置

关键要点

  • 使用 Maven BOM 统一管理 GeoTools 版本
  • 必须配置 OSGeo Maven 仓库
  • 根据需求选择必要的模块
  • 使用 gt-epsg-hsql 提供坐标系支持
  • 注意 SPI 服务文件的合并

下一步

在下一章中,我们将深入了解 GeoTools 的核心架构和模块设计。


← 上一章:GeoTools 概述与入门 | 返回目录 | 下一章:核心架构与模块设计 →

posted @ 2025-12-29 10:47  我才是银古  阅读(3)  评论(0)    收藏  举报