第02章 - 快速入门与环境配置

第02章 - 快速入门与环境配置

2.1 开发环境准备

2.1.1 系统要求

geometry-api-java 对系统环境的要求非常友好:

组件 最低要求 推荐版本
Java JDK 1.7+ JDK 8/11/17
Maven 3.x 3.6+
内存 512MB 2GB+
操作系统 跨平台 任意

特别说明

  • geometry-api-java 是纯 Java 库,无需本地库依赖
  • 与操作系统无关,Windows/Linux/macOS 均可运行
  • 不依赖 GIS 软件环境,开箱即用

2.1.2 JDK 安装与配置

如果尚未安装 JDK,请按以下步骤操作:

Windows 环境

  1. 下载 JDK(推荐 Adoptium
  2. 运行安装程序
  3. 配置环境变量:
    JAVA_HOME = C:\Program Files\Java\jdk-17
    Path = %JAVA_HOME%\bin;%Path%
    
  4. 验证安装:
    java -version
    

Linux/macOS 环境

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

# macOS (使用 Homebrew)
brew install openjdk@17

# 验证安装
java -version

2.1.3 Maven 安装与配置

Windows 环境

  1. 下载 Maven
  2. 解压到目录(如 C:\maven
  3. 配置环境变量:
    MAVEN_HOME = C:\maven
    Path = %MAVEN_HOME%\bin;%Path%
    
  4. 验证安装:
    mvn -version
    

Linux/macOS 环境

# Ubuntu/Debian
sudo apt install maven

# macOS
brew install maven

# 验证安装
mvn -version

配置国内镜像(可选,加速依赖下载):

编辑 ~/.m2/settings.xml

<settings>
    <mirrors>
        <mirror>
            <id>aliyun</id>
            <name>Aliyun Maven Mirror</name>
            <url>https://maven.aliyun.com/repository/public</url>
            <mirrorOf>central</mirrorOf>
        </mirror>
    </mirrors>
</settings>

2.2 项目创建与依赖配置

2.2.1 创建 Maven 项目

使用命令行创建

mvn archetype:generate \
    -DgroupId=com.example.gis \
    -DartifactId=geometry-demo \
    -DarchetypeArtifactId=maven-archetype-quickstart \
    -DarchetypeVersion=1.4 \
    -DinteractiveMode=false

使用 IDE 创建

IDEA:

  1. File → New → Project
  2. 选择 Maven
  3. 选择 JDK 版本
  4. 填写 GroupId 和 ArtifactId

Eclipse:

  1. File → New → Maven Project
  2. 选择 Create a simple project
  3. 填写项目信息

2.2.2 添加 Maven 依赖

编辑 pom.xml,添加 geometry-api-java 依赖:

<?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.gis</groupId>
    <artifactId>geometry-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <geometry-api.version>2.2.4</geometry-api.version>
    </properties>

    <dependencies>
        <!-- Esri Geometry API for Java -->
        <dependency>
            <groupId>com.esri.geometry</groupId>
            <artifactId>esri-geometry-api</artifactId>
            <version>${geometry-api.version}</version>
        </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>${maven.compiler.source}</source>
                    <target>${maven.compiler.target}</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2.2.3 依赖说明

geometry-api-java 的核心依赖很少:

<!-- 核心依赖(自动引入) -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.9.6</version>
</dependency>

依赖树

com.esri.geometry:esri-geometry-api:2.2.4
└── com.fasterxml.jackson.core:jackson-core:2.9.6

这种精简的依赖结构使得 geometry-api-java:

  • 体积小巧(JAR 约 1MB)
  • 集成简单,无依赖冲突
  • 启动快速,内存占用低

2.3 第一个几何程序

2.3.1 创建点并计算缓冲区

创建文件 src/main/java/com/example/gis/HelloGeometry.java

package com.example.gis;

import com.esri.core.geometry.*;

public class HelloGeometry {
    public static void main(String[] args) {
        // 1. 创建一个点(北京天安门坐标)
        Point tiananmen = new Point(116.397428, 39.90923);
        System.out.println("创建点: " + tiananmen);
        
        // 2. 创建空间参考(WGS84,EPSG:4326)
        SpatialReference sr = SpatialReference.create(4326);
        System.out.println("空间参考容差: " + sr.getTolerance());
        
        // 3. 创建 1 度缓冲区
        Polygon buffer = GeometryEngine.buffer(tiananmen, sr, 1.0);
        System.out.println("缓冲区顶点数: " + buffer.getPointCount());
        
        // 4. 查询缓冲区边界
        Envelope2D env = new Envelope2D();
        buffer.queryEnvelope2D(env);
        System.out.println("缓冲区范围: " + 
            "X[" + env.xmin + ", " + env.xmax + "], " +
            "Y[" + env.ymin + ", " + env.ymax + "]");
        
        // 5. 转换为 GeoJSON
        String geoJson = GeometryEngine.geometryToGeoJson(sr, buffer);
        System.out.println("GeoJSON 长度: " + geoJson.length() + " 字符");
        
        // 6. 计算缓冲区面积
        double area = buffer.calculateArea2D();
        System.out.println("缓冲区面积: " + area + " 平方度");
    }
}

运行程序

mvn compile exec:java -Dexec.mainClass=com.example.gis.HelloGeometry

预期输出

创建点: {"x":116.397428,"y":39.90923}
空间参考容差: 1.0E-9
缓冲区顶点数: 100
缓冲区范围: X[115.397428, 117.397428], Y[38.90923, 40.90923]
GeoJSON 长度: 4521 字符
缓冲区面积: 3.141592653589793 平方度

2.3.2 创建折线并计算长度

package com.example.gis;

import com.esri.core.geometry.*;

public class PolylineExample {
    public static void main(String[] args) {
        // 创建一条北京到上海的折线
        Polyline route = new Polyline();
        
        // 添加路径点
        route.startPath(116.397428, 39.90923);   // 北京
        route.lineTo(117.200983, 39.084158);     // 天津
        route.lineTo(118.184086, 39.635508);     // 唐山
        route.lineTo(120.585316, 31.298886);     // 苏州
        route.lineTo(121.469170, 31.224361);     // 上海
        
        // 输出折线信息
        System.out.println("路径数: " + route.getPathCount());
        System.out.println("顶点数: " + route.getPointCount());
        System.out.println("长度: " + route.calculateLength2D() + " 度");
        
        // 获取各顶点
        System.out.println("\n途经城市坐标:");
        for (int i = 0; i < route.getPointCount(); i++) {
            Point2D pt = route.getXY(i);
            System.out.printf("  点%d: (%.4f, %.4f)%n", i, pt.x, pt.y);
        }
        
        // 转换为 WKT
        String wkt = GeometryEngine.geometryToWkt(route, 0);
        System.out.println("\nWKT 表示:");
        System.out.println(wkt);
    }
}

2.3.3 创建多边形并进行空间分析

package com.example.gis;

import com.esri.core.geometry.*;

public class PolygonExample {
    public static void main(String[] args) {
        // 创建北京五环区域(简化多边形)
        Polygon beijing5Ring = new Polygon();
        beijing5Ring.startPath(116.1, 39.7);
        beijing5Ring.lineTo(116.7, 39.7);
        beijing5Ring.lineTo(116.7, 40.1);
        beijing5Ring.lineTo(116.1, 40.1);
        beijing5Ring.closePathWithLine();
        
        // 创建一个查询点(故宫位置)
        Point palace = new Point(116.397, 39.917);
        
        // 创建空间参考
        SpatialReference sr = SpatialReference.create(4326);
        
        // 判断点是否在多边形内
        boolean contains = GeometryEngine.contains(beijing5Ring, palace, sr);
        System.out.println("故宫是否在五环内: " + contains);
        
        // 计算多边形面积
        double area = beijing5Ring.calculateArea2D();
        System.out.println("五环面积: " + area + " 平方度");
        
        // 计算多边形周长
        double length = beijing5Ring.calculateLength2D();
        System.out.println("五环周长: " + length + " 度");
        
        // 获取边界框
        Envelope env = new Envelope();
        beijing5Ring.queryEnvelope(env);
        System.out.println("边界框: " + env);
        
        // 创建另一个多边形(朝阳区,简化)
        Polygon chaoyang = new Polygon();
        chaoyang.startPath(116.4, 39.85);
        chaoyang.lineTo(116.65, 39.85);
        chaoyang.lineTo(116.65, 40.0);
        chaoyang.lineTo(116.4, 40.0);
        chaoyang.closePathWithLine();
        
        // 计算交集
        Geometry intersection = GeometryEngine.intersect(beijing5Ring, chaoyang, sr);
        if (!intersection.isEmpty()) {
            double intersectArea = intersection.calculateArea2D();
            System.out.println("与朝阳区交集面积: " + intersectArea + " 平方度");
        }
        
        // 判断两个多边形是否相交
        boolean intersects = !GeometryEngine.disjoint(beijing5Ring, chaoyang, sr);
        System.out.println("两区域是否相交: " + intersects);
    }
}

2.4 使用 GeometryEngine 简化 API

2.4.1 GeometryEngine 概述

GeometryEngine 是 geometry-api-java 提供的便捷工具类,封装了常用的几何操作。它的特点是:

  • 静态方法,无需创建实例
  • 简化 API,参数更少
  • 内部调用对应的 Operator
// GeometryEngine 常用方法
public class GeometryEngine {
    // 格式转换
    public static MapGeometry jsonToGeometry(String json);
    public static String geometryToJson(SpatialReference sr, Geometry geometry);
    public static String geometryToGeoJson(SpatialReference sr, Geometry geometry);
    public static Geometry geometryFromWkt(String wkt, int importFlags, Geometry.Type type);
    public static String geometryToWkt(Geometry geometry, int exportFlags);
    
    // 空间操作
    public static Geometry[] union(Geometry[] geometries, SpatialReference sr);
    public static Geometry difference(Geometry g1, Geometry g2, SpatialReference sr);
    public static Geometry intersect(Geometry g1, Geometry g2, SpatialReference sr);
    public static Polygon buffer(Geometry geometry, SpatialReference sr, double distance);
    public static Geometry clip(Geometry geometry, Envelope envelope, SpatialReference sr);
    public static Geometry convexHull(Geometry geometry);
    public static Geometry simplify(Geometry geometry, SpatialReference sr);
    
    // 空间关系
    public static boolean equals(Geometry g1, Geometry g2, SpatialReference sr);
    public static boolean contains(Geometry g1, Geometry g2, SpatialReference sr);
    public static boolean within(Geometry g1, Geometry g2, SpatialReference sr);
    public static boolean crosses(Geometry g1, Geometry g2, SpatialReference sr);
    public static boolean touches(Geometry g1, Geometry g2, SpatialReference sr);
    public static boolean overlaps(Geometry g1, Geometry g2, SpatialReference sr);
    public static boolean disjoint(Geometry g1, Geometry g2, SpatialReference sr);
    public static boolean relate(Geometry g1, Geometry g2, SpatialReference sr, String relation);
    
    // 距离计算
    public static double distance(Geometry g1, Geometry g2, SpatialReference sr);
    public static double geodesicDistanceOnWGS84(Point from, Point to);
}

2.4.2 与 Operator 的对比

使用 Operator(更灵活、功能更多):

// 获取缓冲区算子
OperatorBuffer op = (OperatorBuffer) OperatorFactoryLocal
    .getInstance()
    .getOperator(Operator.Type.Buffer);

// 执行缓冲区操作(可以使用游标处理多个几何)
Geometry result = op.execute(geometry, sr, 10.0, null);

使用 GeometryEngine(更简洁):

// 一行代码完成
Polygon result = GeometryEngine.buffer(geometry, sr, 10.0);

选择建议

  • 简单场景:使用 GeometryEngine
  • 批量处理:使用 Operator + Cursor
  • 需要进度回调:使用 Operator + ProgressTracker
  • 需要几何加速:使用 Operator + accelerateGeometry

2.5 项目结构最佳实践

2.5.1 推荐的项目结构

geometry-demo/
├── pom.xml
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── com/example/gis/
│   │   │       ├── App.java               # 应用入口
│   │   │       ├── service/               # 业务服务层
│   │   │       │   ├── GeometryService.java
│   │   │       │   └── SpatialAnalysisService.java
│   │   │       ├── util/                  # 工具类
│   │   │       │   ├── GeometryUtils.java
│   │   │       │   └── FormatConverter.java
│   │   │       └── model/                 # 数据模型
│   │   │           └── GeoFeature.java
│   │   └── resources/
│   │       └── data/                      # 测试数据
│   │           └── sample.geojson
│   └── test/
│       └── java/
│           └── com/example/gis/
│               └── GeometryServiceTest.java
└── README.md

2.5.2 工具类封装示例

package com.example.gis.util;

import com.esri.core.geometry.*;

/**
 * 几何工具类
 */
public class GeometryUtils {
    
    // WGS84 空间参考(常用)
    public static final SpatialReference WGS84 = SpatialReference.create(4326);
    
    // CGCS2000 地理坐标系
    public static final SpatialReference CGCS2000 = SpatialReference.create(4490);
    
    /**
     * 从 GeoJSON 字符串解析几何
     */
    public static Geometry fromGeoJson(String geoJson) {
        MapGeometry mapGeom = GeometryEngine.geoJsonToGeometry(
            geoJson, 0, Geometry.Type.Unknown);
        return mapGeom.getGeometry();
    }
    
    /**
     * 将几何转换为 GeoJSON
     */
    public static String toGeoJson(Geometry geometry) {
        return GeometryEngine.geometryToGeoJson(WGS84, geometry);
    }
    
    /**
     * 从 WKT 字符串解析几何
     */
    public static Geometry fromWkt(String wkt) {
        return GeometryEngine.geometryFromWkt(wkt, 0, Geometry.Type.Unknown);
    }
    
    /**
     * 将几何转换为 WKT
     */
    public static String toWkt(Geometry geometry) {
        return GeometryEngine.geometryToWkt(geometry, 0);
    }
    
    /**
     * 计算缓冲区(米为单位,仅适用于小范围)
     * 注意:在 WGS84 坐标系下,此方法仅适用于赤道附近
     */
    public static Polygon bufferInMeters(Geometry geometry, double meters) {
        // 简化处理:1度 ≈ 111000米(赤道处)
        double degrees = meters / 111000.0;
        return GeometryEngine.buffer(geometry, WGS84, degrees);
    }
    
    /**
     * 计算两点间的测地距离(米)
     */
    public static double geodesicDistance(Point p1, Point p2) {
        return GeometryEngine.geodesicDistanceOnWGS84(p1, p2);
    }
    
    /**
     * 判断几何是否有效(非空且非空集)
     */
    public static boolean isValid(Geometry geometry) {
        return geometry != null && !geometry.isEmpty();
    }
    
    /**
     * 获取几何的中心点
     */
    public static Point getCentroid(Geometry geometry) {
        Envelope2D env = new Envelope2D();
        geometry.queryEnvelope2D(env);
        return new Point(env.getCenterX(), env.getCenterY());
    }
}

2.5.3 业务服务示例

package com.example.gis.service;

import com.esri.core.geometry.*;
import com.example.gis.util.GeometryUtils;
import java.util.List;
import java.util.ArrayList;

/**
 * 空间分析服务
 */
public class SpatialAnalysisService {
    
    private final SpatialReference sr;
    
    public SpatialAnalysisService() {
        this.sr = GeometryUtils.WGS84;
    }
    
    /**
     * 查找指定范围内的点
     */
    public List<Point> findPointsWithinPolygon(List<Point> points, Polygon area) {
        List<Point> result = new ArrayList<>();
        for (Point point : points) {
            if (GeometryEngine.contains(area, point, sr)) {
                result.add(point);
            }
        }
        return result;
    }
    
    /**
     * 合并多个多边形
     */
    public Polygon unionPolygons(List<Polygon> polygons) {
        if (polygons.isEmpty()) {
            return new Polygon();
        }
        
        Geometry[] geoms = polygons.toArray(new Geometry[0]);
        Geometry result = GeometryEngine.union(geoms, sr);
        return (Polygon) result;
    }
    
    /**
     * 计算多边形的面积覆盖统计
     */
    public double calculateAreaInSquareKm(Polygon polygon) {
        // 简化计算,仅适用于小范围
        // 1平方度 ≈ 12321 平方公里(中纬度地区近似值)
        double areaDegrees = Math.abs(polygon.calculateArea2D());
        return areaDegrees * 12321;
    }
}

2.6 编译与运行

2.6.1 编译项目

# 编译
mvn compile

# 跳过测试编译
mvn compile -DskipTests

2.6.2 运行程序

方式一:使用 Maven

mvn exec:java -Dexec.mainClass=com.example.gis.HelloGeometry

方式二:打包后运行

# 打包
mvn package

# 运行
java -cp target/geometry-demo-1.0-SNAPSHOT.jar:target/dependency/* \
    com.example.gis.HelloGeometry

方式三:创建可执行 JAR

pom.xml 中添加:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.4.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.gis.HelloGeometry</mainClass>
                    </transformer>
                </transformers>
            </configuration>
        </execution>
    </executions>
</plugin>
# 打包
mvn package

# 运行
java -jar target/geometry-demo-1.0-SNAPSHOT.jar

2.6.3 运行测试

# 运行所有测试
mvn test

# 运行特定测试类
mvn test -Dtest=GeometryServiceTest

# 生成测试报告
mvn surefire-report:report

2.7 常见问题与解决方案

2.7.1 依赖下载失败

问题:Maven 下载依赖超时或失败

解决方案

  1. 配置国内镜像(见 2.1.3 节)
  2. 清理本地仓库后重试:
    rm -rf ~/.m2/repository/com/esri
    mvn clean install
    
  3. 使用离线安装:
    # 下载 JAR 后手动安装
    mvn install:install-file \
        -Dfile=esri-geometry-api-2.2.4.jar \
        -DgroupId=com.esri.geometry \
        -DartifactId=esri-geometry-api \
        -Dversion=2.2.4 \
        -Dpackaging=jar
    

2.7.2 编译版本问题

问题:编译时提示 Java 版本不兼容

解决方案
确保 pom.xml 中的编译版本与安装的 JDK 一致:

<properties>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
</properties>

或者使用工具链指定 JDK:

mvn compile -Dmaven.compiler.source=11 -Dmaven.compiler.target=11

2.7.3 内存溢出

问题:处理大量几何时出现 OutOfMemoryError

解决方案

  1. 增加 JVM 内存:
    java -Xmx4g -jar app.jar
    
  2. 使用流式处理(Cursor)代替批量处理
  3. 分批处理大数据集

2.7.4 数值精度问题

问题:几何计算结果与预期有偏差

解决方案

  1. 使用适当的空间参考
  2. 检查几何数据是否有效
  3. 使用 simplify() 清理问题几何
// 简化几何,修复可能的问题
Geometry simplified = GeometryEngine.simplify(geometry, sr);

2.8 本章小结

本章介绍了 geometry-api-java 的开发环境搭建和基本使用方法:

  1. 环境准备:JDK 和 Maven 的安装配置
  2. 项目创建:Maven 项目结构和依赖配置
  3. 基础示例:点、线、面的创建和空间分析
  4. API 使用:GeometryEngine 的便捷方法
  5. 最佳实践:项目结构和工具类封装
  6. 编译运行:多种运行方式和常见问题

通过本章学习,你应该能够:

  • 搭建 geometry-api-java 开发环境
  • 创建和操作基本几何对象
  • 使用 GeometryEngine 进行空间分析
  • 组织合理的项目结构

下一章,我们将深入学习几何对象的详细特性。


← 上一章:框架概述与设计理念 | 下一章:几何对象详解 →

posted @ 2025-12-03 15:15  我才是银古  阅读(5)  评论(0)    收藏  举报