Apache SIS 构建并运行示例应用指南

Apache SIS 1.6 构建并运行示例应用的实测指南:从零创建 Maven 工程并引入依赖,解决数据库驱动版本与大地数据集工厂两类真实故障,示例覆盖坐标参照系解析、WKT 输出、测地线距离计算与坐标转换,附编译运行命令、真实回显、堆内存限制验证与常见问题。

Apache SISApache SIS2026/10/12

Apache SIS 构建并运行示例应用指南

目标:从零构建并运行一个基于 Apache SIS 1.6 的最小 Java 示例应用,演示 CRS 解析、WKT 输出、两点测地线(大圆)距离计算与 NAD27 转 WGS84 坐标转换。给出完整 pom.xml、示例源码、编译运行命令与真实回显,并沉淀 Derby 版本、EPSG 工厂加载两处关键排障。所有命令与输出均在 Ubuntu 22.04.5 + JDK 17 + Maven 3.6.3 环境实测通过。

一、环境信息

  • 操作系统:Ubuntu 22.04.5 LTS
  • Java:OpenJDK 17.0.20.1(openjdk-17-jdk)
  • 构建工具:Apache Maven 3.6.3(Ubuntu 官方源)
  • Apache SIS 版本:1.6(Maven 坐标 org.apache.sis.core:sis-referencing:1.6,Maven Central 已核实存在)
  • 示例工程目录:/root/sis-demo/
  • 演示账号:root

二、前置条件

安装 JDK 17 与 Maven(SIS 1.6 要求 Java 11+,本文用 JDK 17 实测):

sudo apt-get update
sudo apt-get install -y openjdk-17-jdk maven
java -version
mvn -version | head -3

实测回显:

openjdk version "17.0.20.1" 2026-08-18
OpenJDK Runtime Environment (build 17.0.20.1+1-1-22.04-Ubuntu)
OpenJDK 64-Bit Server VM (build 17.0.20.1+1-1-22.04-Ubuntu, mixed mode, sharing)
Apache Maven 3.6.3
Maven home: /usr/share/maven
Java version: 17.0.20.1, vendor: Ubuntu, runtime: /usr/lib/jvm/java-17-openjdk-amd64

三、工程结构

标准 Maven 布局,一个 pom 与一个主类:

sis-demo/
├── pom.xml
└── src/main/java/demo/SisDemo.java
mkdir -p /root/sis-demo/src/main/java/demo

四、完整 pom.xml

依赖三件套:sis-referencing(坐标参照系核心)、sis-embedded-data(内嵌完整 EPSG 数据集,运行时依赖)、derby(内嵌数据库使用的 Derby 驱动,运行时依赖)。版本统一为 1.6,Derby 与 SIS 1.6 发行包内置版本一致为 10.15.2.0:

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

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.apache.sis.core</groupId>
            <artifactId>sis-referencing</artifactId>
            <version>1.6</version>
        </dependency>
        <dependency>
            <groupId>org.apache.sis.non-free</groupId>
            <artifactId>sis-embedded-data</artifactId>
            <version>1.6</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.derby</groupId>
            <artifactId>derby</artifactId>
            <version>10.15.2.0</version>
            <scope>runtime</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>3.1.0</version>
                <configuration>
                    <mainClass>demo.SisDemo</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

💡 说明:sis-embedded-data 属 org.apache.sis.non-free 分组(EPSG 数据许可与 Apache 2.0 不同,故单独分发,约 4.8 MB,引入即视为同意 EPSG 使用条款)。它自带以 Derby 10.15.2.0 创建的完整 EPSG 数据库,因此 Derby 驱动必须同为 10.15.2.0。

五、示例源码

创建 /root/sis-demo/src/main/java/demo/SisDemo.java,完成三步:CRS.forCode 解析 EPSG:4326 并输出 WKT → GeodeticCalculator 计算巴黎到东京测地线距离 → CRS.findOperation + MathTransform 把 NAD27 坐标转到 WGS84:

package demo;

import org.apache.sis.geometry.DirectPosition2D;
import org.apache.sis.io.wkt.FormattableObject;
import org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox;
import org.apache.sis.referencing.CRS;
import org.apache.sis.referencing.GeodeticCalculator;
import org.opengis.geometry.DirectPosition;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.CoordinateOperation;
import org.opengis.referencing.operation.MathTransform;

public class SisDemo {
    public static void main(String[] args) throws Exception {
        // 1) CRS.forCode:从 EPSG 代码解析坐标参照系
        CoordinateReferenceSystem wgs84 = CRS.forCode("EPSG:4326");
        System.out.println("=== CRS.forCode(EPSG:4326) ===");
        System.out.println("name = " + wgs84.getName().getCode());
        System.out.println(((FormattableObject) wgs84).toWKT());

        // 2) GeodeticCalculator:两点大圆(测地线)距离,巴黎 -> 东京
        GeodeticCalculator calc = GeodeticCalculator.create(wgs84);
        calc.setStartGeographicPoint(48.8566, 2.3522);   // 巴黎(纬度, 经度)
        calc.setEndGeographicPoint(35.6762, 139.6503);   // 东京(纬度, 经度)
        System.out.println("=== GeodeticCalculator Paris -> Tokyo ===");
        System.out.println("geodesic distance = " + calc.getGeodesicDistance() + " metres");

        // 3) CRS.findOperation + MathTransform:NAD27(EPSG:4267) -> WGS84(EPSG:4326)
        CoordinateReferenceSystem source = CRS.forCode("EPSG:4267");
        CoordinateReferenceSystem target = CRS.forCode("EPSG:4326");
        CoordinateOperation op = CRS.findOperation(source, target,
                new DefaultGeographicBoundingBox(-125.0, -66.0, 24.0, 50.0));
        MathTransform mt = op.getMathTransform();
        // EPSG:4267 轴序为 (纬度, 经度)
        DirectPosition p1 = new DirectPosition2D(source, 37.783, -122.417);
        DirectPosition p2 = mt.transform(p1, null);
        System.out.println("=== transform EPSG:4267 -> EPSG:4326 ===");
        System.out.println("operation = " + op.getName().getCode());
        System.out.println("input  = " + p1.getOrdinate(0) + ", " + p1.getOrdinate(1));
        System.out.println("output = " + p2.getOrdinate(0) + ", " + p2.getOrdinate(1));
    }
}

💡 SIS 1.6 API 适配要点(本例编译期两处真实踩坑后修正):

  • WKT 格式化类为 org.apache.sis.io.wkt.FormattableObject(含 toWKT() 方法);旧资料中的 Formattable 接口在 1.6 已不存在,直接引用会编译报 cannot find symbol。
  • GeodeticCalculator 构造器为包私有,对外入口是静态工厂 GeodeticCalculator.create(crs);直接 new GeodeticCalculator(crs) 会报构造器签名不匹配(要求 (CoordinateReferenceSystem, Ellipsoid) 两参)。

六、编译与运行

6.1 首次运行的两处依赖排障(实测沉淀)

排障一:Derby 版本不匹配。 最初按官方 EPSG 页示例引入 org.apache.derby:derby:10.14.2.0 并用环境变量复用命令行工具已安装的 EPSG 库时,运行报:

java.sql.SQLException: Failed to start database '/opt/sis/apache-sis-1.6/data/Databases/SpatialMetadata' ...
Caused by: ERROR XSLAN: Database at /opt/sis/apache-sis-1.6/data/Databases/SpatialMetadata has an incompatible format with the current version of the software.  The database was created by or upgraded by version 10.15.

核实 SIS 1.6 发行包内置 Derby 版本:

Bundle-Name: Apache Derby 10.15
Bundle-Version: 10.15.2000000.1873585

结论:SIS 1.6 生态统一使用 Derby 10.15.2.0(sis-embedded-data 自身 pom 亦声明 derbytools:10.15.2.0),pom 中 Derby 版本必须一致。

排障二:classpath 模式下 EPSG 工厂不连 SQL 数据库。 仅引入 sis-referencing 并设置 SIS_DATA 指向命令行工具安装的 EPSG 库时,坐标操作查询退化为兜底工厂,真实探测结果:

factory class = org.apache.sis.referencing.EPSGFactoryFallback
ops = []          (createFromCoordinateReferenceSystemCodes("4267","4326") 为空)

此时 CRS.findOperation 找不到 EPSG 操作,返回名为 SIS:Unspecified datum change 的兜底变换(无基准改正参数,NAD27 坐标直接透传,误差不可接受)。官方对 Java 应用推荐以 Maven 依赖方式获取 EPSG 数据集,即本文 pom 采用的 sis-embedded-data:引入后无需设置任何环境变量,CRS.forCode 即可解析完整 EPSG 定义。

6.2 编译与运行命令

export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
export PATH=$JAVA_HOME/bin:$PATH
cd /root/sis-demo

# 编译
mvn -q clean compile

# 运行(exec-maven-plugin 已配置 mainClass)
mvn -q exec:java

6.3 JVM 堆内存限制与验证

示例应用会启动内嵌 Derby,建议限制堆内存(256–512MB 区间)。通过 MAVEN_OPTS 注入(exec:java 与 Maven 同进程):

export MAVEN_OPTS="-Xms256m -Xmx512m"
mvn -q exec:java

运行期间另开终端验证生效值:

jps -v | grep "Xms256m"

实测回显(可见 -Xms256m -Xmx512m 已生效):

7843 Launcher -Xms256m -Xmx512m -Dclassworlds.conf=/usr/share/maven/bin/m2.conf -Dmaven.home=/usr/share/maven -Dlibrary.jansi.path=/usr/share/maven/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/root/sis-demo

七、运行结果解读

编译成功(mvn -q clean compile 无输出即无错误)。完整运行回显:

=== CRS.forCode(EPSG:4326) ===
name = WGS 84
GEOGCRS["WGS 84",
  ENSEMBLE["World Geodetic System 1984 ensemble",
    MEMBER["World Geodetic System 1984 (Transit)"],
    MEMBER["World Geodetic System 1984 (G730)"],
    MEMBER["World Geodetic System 1984 (G873)"],
    MEMBER["World Geodetic System 1984 (G1150)"],
    MEMBER["World Geodetic System 1984 (G1674)"],
    MEMBER["World Geodetic System 1984 (G1762)"],
    MEMBER["World Geodetic System 1984 (G2139)"],
    MEMBER["World Geodetic System 1984 (G2296)"],
    ELLIPSOID["WGS 84", 6378137.0, 298.257223563, LENGTHUNIT["metre", 1]],
    ENSEMBLEACCURACY[2.0]],
    PRIMEM["Greenwich", 0.0, ANGLEUNIT["degree", 0.017453292519943295]],
  CS[ellipsoidal, 2],
    AXIS["Latitude (B)", north, ORDER[1], AXISMINVALUE[-90.0], AXISMAXVALUE[90.0], RANGEMEANING[exact]],
    AXIS["Longitude (L)", east, ORDER[2], AXISMINVALUE[-180.0], AXISMAXVALUE[180.0], RANGEMEANING[wraparound]],
    ANGLEUNIT["degree", 0.017453292519943295],
  USAGE[
    SCOPE["Horizontal component of 3D system."],
    AREA["World"],
    BBOX[-90.00, -180.00, 90.00, 180.00]],
  ID["EPSG", 4326, "12.047", URI["urn:ogc:def:crs:EPSG:12.047:4326"]]]
=== GeodeticCalculator Paris -> Tokyo ===
geodesic distance = 9735308.654239194 metres
=== transform EPSG:4267 -> EPSG:4326 ===
operation = NAD27 to WGS 84 (4)
input  = 37.783, -122.417
output = 37.78290101412837, -122.41805034782497
Exception in thread "Shutdown" java.lang.NoClassDefFoundError: org/apache/sis/metadata/sql/internal/shared/LocalDataSource$1
        ... 5 more
(退出码 0)

结果解读:

  • CRS.forCode("EPSG:4326"):从内嵌完整 EPSG 库解析出 WGS 84 地理坐标系,toWKT() 输出 WKT 2 格式,ID["EPSG", 4326, "12.047", ...] 表明定义来自完整 EPSG 数据集(若只有内置子集,此处会多出 CITATION["Subset of EPSG"] 且无 ENSEMBLE 细节)。
  • 测地线距离:巴黎(48.8566N, 2.3522E)到东京(35.6762N, 139.6503E)沿 WGS84 椭球测地线约 9735.3 公里,与公开大圆距离参考值一致。
  • 坐标转换:operation = NAD27 to WGS 84 (4) 是 EPSG 官方操作(即 EPSG:1173,命令行工具 sis transform 同场景返回同一操作码);旧基准点 (37.783, -122.417) 转换为 (37.78290101412837, -122.41805034782497)。
  • 结尾 Shutdown 提示:Exception in thread "Shutdown" ... LocalDataSource$1 是 exec:java 关闭期类加载器卸载顺序产生的噪音,主流程输出完整、退出码 0,可忽略;直接 java -cp 方式运行不会出现。

八、注意事项

⚠️ 注意:

  1. 三件依赖版本必须配套:SIS 1.6 + sis-embedded-data 1.6 + Derby 10.15.2.0;Derby 用旧版(如 10.14.2.0)会报 ERROR XSLAN 数据库版本不兼容。
  2. sis-embedded-data 为 EPSG 完整数据集的非自由许可发行物,引入即视为接受其使用条款;若不需要完整 EPSG(仅 WGS84 等常量与 WKT 自定义),可去掉该依赖。
  3. 坐标转换务必提供感兴趣区域(DefaultGeographicBoundingBox):区域为 null 时 SIS 可能返回 SIS:Unspecified datum change 兜底操作(无基准改正),结果与官方 EPSG 操作有差异。
  4. 命令行工具安装的 EPSG 库(SIS_DATA 方式)主要服务于 sis 命令行;Java 应用内使用完整 EPSG 的官方推荐是 Maven 依赖 sis-embedded-data。

九、常见问题

Q1:编译报 cannot find symbol class Formattable? SIS 1.6 已无 org.apache.sis.io.wkt.Formattable,改用 org.apache.sis.io.wkt.FormattableObject(toWKT()/toString() 同名可用)。

Q2:编译报 constructor GeodeticCalculator cannot be applied to given types? GeodeticCalculator 构造器不是公开 API,用静态工厂 GeodeticCalculator.create(crs) 创建。

Q3:运行报 ERROR XSLAN: ... incompatible format ... created by or upgraded by version 10.15? Derby 驱动版本低于数据库创建版本。把 pom 中 Derby 升到 10.15.2.0(与 SIS 1.6 发行包、sis-embedded-data 内部一致)。

Q4:findOperation 返回 SIS:Unspecified datum change 而不是 EPSG 操作? EPSG 数据集未接入应用。确认 pom 已带 sis-embedded-data + derby:10.15.2.0;同时为 CRS.findOperation 传入感兴趣区域。排障时可探测 CRS.getAuthorityFactory("EPSG"):返回 EPSGFactoryFallback 说明完整 EPSG 工厂未生效。

Q5:运行结束打印 Exception in thread "Shutdown" ... NoClassDefFoundError? exec:java 关闭期类加载器竞争噪音,主流程已完成、退出码 0,可忽略;或改用打包后 java -cp 方式运行。

十、参考链接

  • SIS 官方下载与 Maven 依赖说明:https://sis.apache.org/downloads.html
  • EPSG 数据集接入方式:https://sis.apache.org/epsg.html
  • 命令行同能力对照:https://sis.apache.org/command-line.html
  • Maven Central 构件:https://repo1.maven.org/maven2/org/apache/sis/core/sis-referencing/1.6/