本文将帮助读者获取GeoTools的源代码并进行编译。下载源代码并进行编译有助于读者对GeoTools建立整体性的理解,帮助读者厘清GeoTools的模块划分。同时因为GeoTools是一个开源类库,读者在实际使用中遇到的一些问题可以通过直接阅读GeoTools源代码来进行解决。
GeoTools是一个使用Java程序设计语言开发的地理信息类库,因此在介绍GeoTools之前,本节将简单介绍Java程序设计语言。使用Java程序设计语言并不困难,根据一些网站的统计,Java程序设计语言是当前使用人数最多的计算机程序设计语言之一。为了更好地学习GeoTools,本书建议读者首先具有一定的Java程序设计语言使用基础。
Java的规则和语法是以C和C++语言为基础的。用Java开发软件的一个主要优势是其具有可移植性。如果你在笔记本电脑上编写了Java程序的代码,就很容易将代码移植到移动设备上。当詹姆斯·高斯林在20世纪90年代初发明这种语言时,其主要目标是能够实现“一次编写,随地运行”(Write once,Run everywhere)。
除了单纯的程序设计语言,Java生态还包含一套软件平台。要使用Java创建一个应用程序,你需要下载Java开发工具包(Java Development Kit,JDK),它可被用于Windows、macOS和Linux。用户用Java程序设计语言编写程序,然后由编译器将程序转化为Java字节码,也就是Java虚拟机(Java Virtual Machine,JVM)的指令集。Java虚拟机是Java运行环境(Java Runtime Environment,JRE)的一部分。Java字节码在任何支持Java虚拟机的系统上运行,都无须修改,因此你的Java代码可以在“任何地方”运行。
Java语言具有以下特性。
(1)Java语言是简单的。
Java语言的语法与C语言、C++语言的很接近,这使得大多数程序员能很容易学习和使用它。另一方面,Java丢弃了C++中很少使用的、很难理解的、令人迷惑的那些特性,如操作符重载、多继承、自动的强制类型转换等。Java语言不使用指针,而是使用引用。而且Java提供了自动的垃圾回收机制,使得程序员不必为内存管理而担忧。
(2)Java语言是面向对象的。
Java语言提供类、接口和继承等原语,简单起见,Java只支持类之间的单继承,但支持接口之间的多继承,并支持类与接口之间的实现机制(关键字为implements)。Java语言全面支持动态绑定,而C++语言只支持对虚函数使用动态绑定。总之,Java语言是一种纯粹的面向对象的程序设计语言。
(3)Java语言是分布式的。
Java语言支持互联网应用的开发,在基本的Java应用程序接口中有一个网络应用程序设计包(java.net),它提供了用于网络应用程序设计的类,包括统一资源定位符(Uniform Resource Locator,URL)、URLConnection、套接字(Socket)、ServerSocket等。Java的远程方法调用(Remote Method Invocation,RMI)机制是开发分布式应用的重要手段。
(4)Java语言是健壮的。
Java的强类型机制、异常处理机制、垃圾回收机制等是Java程序健壮性的重要保证。对指针的丢弃是Java的明智选择。Java的安全检查机制使得Java更具健壮性。
(5)Java语言是安全的。
Java通常被用在网络环境中,为此,Java提供了一套安全机制以防恶意代码的攻击。除了Java语言具有的许多安全特性以外,Java对通过网络下载的类具有一套安全防范机制(类ClassLoader),如为该类分配不同的命名空间以防其替代本地的同名类、对该类字节代码进行检查,并提供安全管理机制(类SecurityManager)为Java应用设置安全哨兵。
(6)Java语言是体系结构中立的。
Java程序(.java文件)在Java平台上被编译为体系结构中立的字节码格式(.class文件),然后就可以在实现这个Java平台的任何系统中运行,这适合于异构的网络环境和软件的分发。
(7)Java语言是可移植的。
这种可移植性来源于体系结构中立性,另外,Java还严格规定了各个基本数据类型的长度。Java系统本身也具有很强的可移植性,Java编译器是用Java实现的,但是JRE是用ANSI C实现的。
(8)Java语言是解释型的。
如前所述,Java程序在Java平台上被编译为字节码格式,这种格式的文件可以在实现这个Java平台的任何系统中运行。在运行时,Java平台中的Java解释器对这些文件中的字节码进行解释执行,执行过程中需要的类在连接阶段被载入运行环境。
(9)Java是高性能的。
与那些解释型的高级脚本语言相比,Java的确是高性能的。事实上,Java的运行速度随着即时(Just-In-Time,JIT)编译器技术的发展越来越接近于C++的运行速度。
(10)Java语言是多线程的。
在Java语言中,线程是一种特殊的对象,它必须由线程或其子(孙)类来创建。通常有两种方法来创建线程:其一,将一个实现了线程接口的对象包装成线程;其二,从Thread类派生出子类并重写run方法,该子类的对象即线程。值得注意的是,Thread类已经实现了Runnable接口,因此,任何一个线程均有对应的run方法,而run方法中包含线程所要运行的代码。线程的活动由一组方法来控制。Java语言支持多个线程的同时执行,并提供多线程之间的同步机制(关键字为synchronized)。
(11)Java语言是动态的。
Java语言的设计目标之一是适应动态变化的环境。Java程序需要的类可以动态地被载入运行环境,也可以通过网络来载入所需要的类。这也有利于软件的升级。另外,Java中的类有一个运行时的表示,能进行运行时的类型检查。
在运行Java程序时,通常使用JRE组件。JDK的功能是将Java代码编译为字节码,而JRE则是运行字节码的组件。如果要把Java代码编译成Java字节码,就需要用到JDK,因为JDK包含JRE,二者关系如图2-1所示。
其中最底层的是不同的操作系统,例如我们比较熟悉的Windows、Linux、macOS等。基于Java,用户可以实现在不同的操作系统中使用同一份代码。上层的JRE中包含JVM以及一些运行时库(runtime library)。JRE是JDK的子集,JDK中还有一些其他的组成元素,例如编译器(compiler)、调试器(debugger)等,这些共同构成了Java的完整生态。
图2-1 JDK与JRE关系
软件的使用离不开构建过程,我们需要将已有的软件源代码编译成更为底层的代码,才能够保证物理机或者虚拟机能够正常地运行。本节将从以下3个方面来介绍与GeoTools的构建相关的内容。
〓● 安装构建工具。
〓● 使用Maven构建GeoTools。
〓● Java 8和Java 11。
GeoTools是用Java程序设计语言编写的,在开发GeoTools时,需将编译选项更改如下。
〓● 集成开发环境(Integrated Development Environment,IDE):生成符合Java 8的代码。
〓● Maven:source=1.8。
15.x及更高版本的GeoTools 需要使用JDK 1.8进行编译。如果你的项目使用的是旧版本的Java,请使用对应版本的GeoTools,GeoTools版本和Java版本的对应关系如表2-1所示。
表2-1 GeoTools版本与Java版本的对应关系
起始版本 | 终止版本 | 编译设置 | 兼容Java版本 | 测试验证 |
GeoTools 21.x | 最新版本 | compiler=1.8 | Java 8, Java 11 | OpenJDK |
GeoTools 15.x | GeoTools 20.x | compiler=1.8 | Java 8 | OpenJDK、Oracle JRE |
GeoTools 11.x | GeoTools 14.x | compiler=1.7 | Java 7 | OpenJDK、Oracle JRE |
GeoTools 8.x | GeoTools 10.x | compiler=1.6 | Java 6 | Oracle JRE |
GeoTools 2.5.x | GeoTools 8.x | compiler=1.5 | Java 5 | Sun JRE |
GeoTools 2.x | GeoTools 2.4.x | compiler=1.4 | Java 1.4 | Sun JRE |
使用Java 8构建的GeoTools 21.x可以在Java 11环境中使用,构建出来的每个Java归档(Java Archive,JAR)包都包含一个用于Java 11模块路径的自动模块名称。
GeoTools从21.x后提供了对Java 11的支持,但请注意,使用Java 11构建的GeoTools仅能应用于Java 11环境中,这是由Java 8和Java 11编译后的类文件不兼容造成的。
GeoTools使用Maven作为自己的构建系统,因此需要先安装Maven。
(1)从Maven的官方主页来下载Maven。
(2)解压缩到本地目录。
(3)新增Maven环境变量,并添加到PATH环境变量中,如代码清单2-1所示。
代码清单2-1 Maven环境变量配置
- M2_HOME = C:\java\apache-maven-3.8.3
- PATH = %PATH%;%M2_HOME%\bin
(4)以Windows系统为例,打开命令提示符窗口,输入并执行mvn –v后显示当前安装的Maven版本信息,如代码清单2-2所示,即表示成功安装了Maven。
代码清单2-2 Maven安装验证
- C:\xxx\>mvn -v
- Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
- Maven home: D:\xxx\apache-maven-3.6.3\bin\..
- Java version: 1.8.0_261, vendor: Oracle Corporation, runtime: C:\Program
- Files\Java\jdk1.8.0_261\jre
- Default locale: zh_CN, platform encoding: GBK
- OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"
GeoTools使用GitHub网站作为自己的仓库,用户可直接从GitHub网站上下载最新的GeoTools源代码,下载后的源代码目录说明如表2-2所示。
表2-2 GeoTools源代码目录说明
目录 | 说明 |
build/ | 用于存放有助于构建过程的Java项目 |
docs/ | 用于存放文档和HTML页面 |
modules/library/ | 用于存放GeoTools的核心库 |
modules/extensions/ | 用于存放建立在核心库基础上的扩展库 |
modules/ogc/ | 用于存放符合OGC规范的数据结构和实现 |
modules/plugins/ | 用于存放与核心库配合的插件 |
modules/unsupported/ | 用于存放社区插件 |
spike/ | 作为临时空间 |
GeoTools源代码的编码为UTF-8。GeoTools使用Git[1]来进行版本控制,Git的具体使用超出了本书的范畴,读者可以自行学习相关内容。
Maven是一个Java项目管理工具和构建工具。它可以将Ant等开源实用程序整合到一个易于使用的构建工具链中。
Maven的最核心部分是使用“项目对象模型”文件(即项目文件pom.xml)。Java项目中的所有模块信息都存于项目文件中。项目文件会告诉你模块的名称、谁维护它、谁开发它以及它依赖什么。项目文件中最重要的部分是依赖项,因为Maven使用它来确定构建模块的顺序以及在需要时下载哪些依赖包。
每个模块均可以有自己的项目文件,子模块的项目文件继承父模块的项目文件,每个模块的项目文件均位于该模块的根目录下。最上级模块,也称为根模块,它的项目文件定义了开源许可证和通用配置。并且根模块中的项目文件有一个依赖管理部分,专门列出了GeoTools所有依赖的版本号,以保证GeoTools所有子模块使用同样版本的依赖。
GeoTools模块与模块之间有互相依赖的关系,因此读者在第一次构建时需要执行完整构建,以便在Maven的本地存储库中安装GeoTools每个模块的依赖包。
当读者下载了GeoTools源代码并解压缩到本地目录后,如C:\java\geotools,进入此目录,需要确保本地计算机连接了互联网,因为Maven在构建时会从互联网上的Maven仓库拉取GeoTools的依赖包。然后在命令提示符窗口中执行mvn install命令,由于是首次构建,Maven拉取依赖包的时间可能会较长。如果构建失败,请检查Maven的日志输出,判断错误原因并修复错误后重新执行mvn clean install即可。
第一次构建时,Maven 需要下载所有依赖,可能需要20~30分钟或更久(依据读者具体网络情况而定)。之后再进行构建时Maven会检查本地依赖包是否需要更新,若不需更新则直接跳过。Maven的依赖包检查基于MD5校验码,不需要很长时间。根据硬件和网络状况,随后的构建可能需要10分钟。下载完所有依赖包后,读者可以离线构建并避免检查MD5校验码,从而使构建速度加快5~7分钟。最后,读者可以关闭测试用例(mvn clean install - DskipTests -o)并离线构建以在2分钟内完成构建。
如果读者在构建GeoTools时耗时过长,还可以通过以下方法进行加速。
(1)尽量不使用mvn clean命令。
(2)使用Maven多线程构建。
(3)修改后仅重建单个模块而不是全部重新构建。
(4)更新Maven的配置文件settings.xml,添加国内云厂商的Maven镜像仓库地址,如阿里云Maven仓库、华为云Maven仓库等地址。
(5)尽量使用离线构建(仅当所有GeoTools依赖包都下载到本地存储库时)。
Java 17已在2021年9月正式发布,该版本将作为长期支持(Long Term Support,LTS)版本。然而目前在Java开发生态中占据主流位置的仍是Java 8。GeoTools目前支持Java 8和Java 11两个版本,但是在笔者的实际使用过程中,生产环境多为Java 8,GeoTools的官方更新说明和测试用例也均为Java 8。因此,基于对稳定性的考量,建议读者在实际生产环境中继续使用Java 8的GeoTools,Java 11的GeoTools可作为读者自己学习使用,不建议在实际生产环境中使用。
GeoTools作为一个Java类库,它提供了不同抽象层级的使用方式。在添加了GeoTools的依赖包后,用户可直接使用GeoTools的工具类对空间数据进行处理,也可以通过GeoTools工厂类解析空间数据格式,更可以通过Java命名和目录接口(Java Naming and Directory Interface,JNDI)将GeoTools集成进用户已有的工程中。用户可根据应用场景,选择合适的使用方式。
由于GeoTools本身是用Java编写的,其本身可以被封装成一个JAR包,这是Java对自身代码编译并封装以后所得的文件。我们如果想要使用GeoTools的功能,那就需要将它的JAR包引入自己的项目里面来。目前主要有两种方式,一种是使用项目管理工具引用,例如使用Maven引用,另一种是直接引用JAR包。
GeoTools是使用Maven构建的,Maven非常擅长整理大量Java依赖包的层级依赖关系,因此使用Maven是引用GeoTools依赖的推荐方式。
直接引用JAR包是将所有GeoTools的依赖包存储到本地开发环境中,是一种传统的Java依赖包组织方式。需要注意的是,由于Java依赖包之间经常会发生依赖冲突,因此需将GeoTools二进制分发版中的所有内容和上级依赖包存储到本地IDE中。
作为一个开源库,读者可以自由调用所需的GeoTools中的各种类。然而,GeoTools提供了一种更为干净的方法。随着GeoTools的迭代,GeoTools从自身的内部实现中干净地分离出几组应用程序接口(Application Program Interface,API),并对外暴露。这些接口被称为GeoAPI。使用这些合理封装的接口可以保证在GeoTools发生升级期间对本地代码改动最少。
如果读者使用这些接口编写代码,GeoAPI会在GeoTools升级过程中保证接口不发生变化。如果这些接口发生变化(仅当底层标准实现发生变化时),这些接口将在下个发布周期内被标记为弃用,以此来警示用户,让用户进行平滑升级。
在当前的GeoTools版本中,这些稳定的接口主要包含在3个模块中,分别如下。
(1)gt-opengis模块,该模块用于提供各类OGC和ISO的规范接口。
(2)JTS模块,该模块用于提供各类平面几何对象的Java实现。
(3)gt-main模块,该模块用于提供GeoTools自身的能力。
这些接口提供了基础和常用的空间数据结构和空间分析能力,通过面向接口程序设计,GeoTools可在不关心具体实现的情况下使用相关能力。
除了接口,GeoTools还提供了许多工具类,这些工具类大体可分为3类。
(1)常见操作工具类,通过对一些通用方法进行封装,减少编码负担的实用工具类,比如CQL、DataUtilities和JTS类。其中每一个工具类都提供了多个公共方法来帮助读者充分利用GeoTools提供的服务。
(2)运行时工具类,即在GeoTools运行时将接口和实现黏合在一起,显著的一个例子是FactoryFinders类,该工具类允许你在类路径(CLASSPATH)上查找各种可用的、满足GeoTools插件规范的实现。
(3)GeoTools扩展工具类,即在自身之上提供额外的服务,并需要额外的公共类来实现这一点,一个常见例子是位于gt-brewer包下的ColorBrewer类。
用户可以直接使用上述工具类,其中有一部分是动态类,用户需要构造相关的对象才能调用相关的方法;另一部分是静态方法,用户只需要直接通过类名就可以使用对应的方法,更加方便。
接口只定义了数据结构应该是什么样子,但是没有提供创建对象的方法。在 Java 中,解决该问题的方法是提供一个“工厂”,工厂提供了“创建”对象的方法,用户可以使用工厂来代替新建对象操作,这种设计模式被称为工厂模式。GeoTools提供了一系列工厂类,允许用户创建和使用各种工厂对象,例如几何要素、样式、属性过滤器、空间过滤器、空间坐标系和空间数据源。GeoTools提供了一个FactoryFinder工厂类,用于定位类路径上可用的工厂实现。通过使用FactoryFinder工厂类,用户的代码可以构建为仅使用接口运行,实现完全的定义与实现相分离。
虽然用户可以直接找到并使用各种工厂中的每一个实现类,但这会在用户的代码和实际实现之间引入依赖性。这种依赖于特定实现的做法会使用户的代码难以更改,并阻止在将来利用更好的实现来替代该实现的可能。上文也许有些抽象,下面具体举例来讲解,首先我们通过以下代码直接创建一个Shapefile文件数据源,如代码清单2-3所示。
代码清单2-3 直接创建Shapefile文件数据源
- ShapefileDataStoreFactory factory = new ShapefileDataStoreFactory();
- ShapeFileDataStore = factory.createDataStore(file);
但是从2.2版本后,GeoTools提供了一种更加灵活的方式来创建Shapefile文件数据源。使用DataStoreFinder工厂类根据传入的不同参数类型,自动查找匹配的数据源类型对应的实现,并进行创建工作,如代码清单2-4所示。
代码清单2-4 使用工厂模式创建Shapefile文件数据源
- File file = new File("example.shp");
- Map map = Collections.singletonMap("url", file.toURL());
- DataStore dataStore = DataStoreFinder.getDataStore(map);
本章首先简单介绍了GeoTools所使用的Java开发语言。然后介绍了如何用源代码构建GeoTools,帮助读者建立对GeoTools源代码的整体认知。最后,介绍了使用GeoTools进行开发的一般使用方式,分别是通过引用依赖包、使用接口、使用工具类和使用工厂模式进行开发。当然,本章对开发的相关介绍仍然是总体性和概括性的,详细的开发使用说明见后续章节。
本文摘自《GeoTools 地理信息系统开发》
国内初次详解GeoTools解决地理信息系统难题的入门指南,阐述如何管理坐标参考系统、矢量数据、栅格数据和连接地理数据库,源码解析与实际案例相结合。
GeoTools 是由开源社区维护的一套地理信息系统的开发组件和解决方案。GeoTools 的实现完整遵循了OGC 的各类规范,并在二十多年的迭代中,形成了活跃的开源社区生态。
本书从GeoTools 的基本信息、社区生态开始介绍,随后以地理信息的基本概念为脉络,详细介绍GeoTools 是如何实现并管理坐标参考系统、如何管理矢量数据和栅格数据,以及如何连接地理数据库的。在本书的最后,通过实现一个空间数据管理系统,将全书的知识体系串联起来,帮助读者形成开源地理信息解决方案,来解决地理信息系统中的数据解析、坐标计算、空间关系等常见问题。
本书适用于有志于从事地理信息系统开发工作的人员,也可以帮助从事传统商业地理信息系统开发和管理工作的人员进一步了解开源地理信息系统。