基于Java的地理空间应用架构
基于Java的地理空间应用架构
在本文中,我们将理解地理空间应用的核心架构和重要元素。我们将从理解什么是地理空间应用以及构建一个地理空间应用的典型挑战开始。
地理空间应用基本上是指利用地理空间数据来提供其核心功能的应用程序。简单来说,地理空间数据是任何代表地点、位置、地图、导航等的数据。即使没有花哨的定义,我们也经常被这些应用程序所包围。例如,我们最喜欢的拼车应用、外卖应用和电影预订应用都是地理空间应用。
地理空间数据基本上是描述具有地球表面或附近位置的对象、事件或其他特征的信息。例如,想象一个应用程序可以建议我们今天晚上最近的剧院播放我们最喜欢的莎士比亚戏剧。它可以通过结合剧院的位置信息、戏剧的属性信息以及事件的时间信息来实现这一点。
地理空间数据的许多其他有用应用为我们的日常提供了价值——例如,当我们试图在一天中的任何时候找到一个愿意带我们去目的地的出租车。或者当我们等不及那个重要的货物到达,并且幸运地可以确定它在运输过程中的确切位置。
实际上,这已经成为我们这些天经常使用的许多应用程序的基本要求。
3. 地理空间技术
在我们了解构建地理空间应用的细微差别之前,让我们先了解一下一些核心技术,这些技术为这类应用提供了支持。这些是帮助我们以有用形式生成、处理和呈现地理空间数据的基础技术。
遥感(RS)是通过测量一个区域的反射和发射辐射来检测和监测一个区域的物理特性的过程,通常这是通过使用遥感卫星完成的。它在测绘、情报和商业应用领域有着重要的用途。
全球定位系统(GPS)是指基于24颗卫星网络的卫星导航系统,这些卫星在中地球轨道(MEO)上飞行。它可以为地球上任何地方的合适接收器提供地理定位和时间信息,只要它与四个或更多的GPS卫星有无遮挡的视线。
地理信息系统(GIS)是一个创建、管理、分析和绘制所有类型数据的系统。例如,它帮助我们将位置数据与更多的描述性信息结合起来,比如那个位置有什么。它帮助提高了几个行业的沟通和决策能力。
4. 构建地理空间应用的挑战
为了理解我们在构建地理空间应用时应做的设计选择,了解所涉及的挑战非常重要。通常,地理空间应用需要对大量的地理空间数据进行实时分析。例如,为急救人员找到通往最近受自然灾害影响的地方的最快备选路线至关重要。
因此,地理空间应用的一个基本要求是存储大量的地理空间数据,并以非常低的延迟进行任意查询。现在,了解空间数据的性质以及为什么需要特殊处理也很重要。基本上,空间数据表示在几何空间中定义的对象。
让我们想象一下,我们在城市周围有多个感兴趣的地点。一个地点通常由其纬度、经度和(可能的)海拔来描述:

现在,我们真正感兴趣的是找到给定地点附近的地点。因此,我们需要计算这个地点到所有可能地点的距离。这类查询与我们熟悉的常规数据库查询大不相同。这些被称为空间查询。它们涉及几何数据类型,并考虑这些几何体之间的空间关系。
我们已经知道,没有有效的索引,任何生产数据库可能都无法生存。对于空间数据也是如此。然而,由于其性质,常规索引对于空间数据和我们想要执行的空间查询类型不是很有效。因此,我们需要称为空间索引的专门索引,可以帮助我们更有效地执行空间操作。
5. 空间数据类型和查询
现在我们已经了解了处理空间数据的挑战,重要的是要注意几种类型的空间数据。此外,我们可以对它们执行几种有趣的查询,以满足独特的需求。我们将涵盖其中一些数据类型以及我们可以对它们执行的操作。
我们通常根据空间参考系统谈论空间数据。这由坐标系统和基准面组成。有几种坐标系统,如仿射、圆柱、笛卡尔、椭球、线性、极地、球面和垂直。基准面是一组定义坐标系统原点位置、比例和方向的参数。
广义上讲,许多支持空间数据的数据库将它们分为两类,几何和地理:

几何在平面坐标系统中存储空间数据。这有助于我们用笛卡尔空间中的坐标表示点、线和区域等形状。地理基于地球表面的坐标系统存储空间数据。这在表示具有纬度和经度坐标的地球表面上的相同形状时非常有用。
我们可以对空间数据进行两种基本类型的查询。这些基本上是找到最近邻或发送不同类型的范围查询。我们已经看到过早期找到最近邻的查询示例。一般的想法是确定最接近查询点的一定数量的项目。
另一种重要的查询类型是范围查询。在这里,我们感兴趣的是知道所有落在查询范围内的项目。查询范围可以是查询点周围一定半径的矩形或圆。例如,我们可以使用这种查询来确定我们站立地点两英里半径内的所有意大利餐厅。
6. 空间数据的数据结构
现在,我们将了解一些更适合构建空间索引的数据结构。这将帮助我们理解它们与常规索引有何不同,以及为什么它们在处理空间操作时更有效。无一例外,几乎所有这些数据结构都是树数据结构的变体。
6.1. 常规数据库索引
数据库索引基本上是一种提高数据检索操作速度的数据结构。没有索引,我们将不得不浏览所有行以搜索我们感兴趣的行。但是,对于一个相当大的表,即使浏览索引也可能需要相当长的时间。
然而,重要的是减少获取键的步骤数量,并减少这样做的磁盘操作次数。B树或平衡树是一种自平衡树数据结构,每个节点存储多个排序的键值对。这有助于在从磁盘的单次读取操作中将更大的键集拉入处理器缓存。
虽然B树工作得很好,我们通常使用B+树来构建数据库索引。B+树与B树非常相似,不同之处在于它只在叶节点存储值或数据:

在这里,所有叶节点也都链接在一起,因此提供了对键值对的有序访问。这里的好处是叶节点提供了索引的第一级,而内部节点提供了多级索引。
常规数据库索引专注于在单一维度上对其键进行排序。例如,我们可以在我们的数据库表上对像邮政编码这样的属性创建一个索引。这将帮助我们查询具有特定邮政编码或在邮政编码范围内的所有位置。
6.2. 空间数据库索引
在地理空间应用中,我们通常对最近邻或范围查询感兴趣。例如,我们可能想要找到所有在特定点10英里内的位置。常规数据库索引在这里不是很有用。实际上,还有其他更适合构建空间索引的数据结构。
最常用的数据结构之一是R树。R树最初由Antonin Guttman在1984年提出,适用于存储像位置这样的空间对象。R树背后的基本思想是将附近的对象分组,并在树的更高一级用它们的最小边界矩形来表示:

对于大多数操作,R树与B树并没有太大的不同。关键区别在于使用边界矩形来决定是否在子树中搜索。为了更好的性能,我们应该确保矩形不覆盖太多的空白空间,并且它们之间不要重叠太多。最有趣的是,R树可以扩展到覆盖三个甚至更多维度!
用于构建空间索引的另一种数据结构是Kd树,它是R树的轻微变体。Kd树将数据空间分割成两部分,而不是将其划分为多个矩形。因此,Kd树中的树节点代表分隔平面而不是边界框。虽然Kd树证明更容易实现并且更快,但它不适合数据总是在变化的情况。
这些数据结构背后的基本思想基本上是将数据划分为轴对齐区域,并将它们存储在树节点中。事实上,还有相当多这样的数据结构我们可以使用,比如BSP树和R*树。
7. 原生支持的数据库
我们已经看到空间数据与常规数据有何不同,以及为什么它们需要特殊处理。因此,我们需要构建地理空间应用的是一个可以本地支持存储空间数据类型,并且可以高效执行空间查询的数据库。我们称这样的数据库管理系统为空间数据库管理系统。
几乎所有主流数据库都开始提供某种级别的空间数据支持。这包括一些流行的数据库管理系统,如MySQL、Microsoft SQL Server、PostgreSQL、Redis、MongoDB、Elasticsearch和Neo4J。然而,也有一些专门构建的空间数据库可用,比如GeoMesa、PostGIS和Oracle Spatial。
7.1. Redis
Redis是一个内存中的数据结构存储,我们可以将其用作数据库、缓存或消息代理。它可以最小化网络开销和延迟,因为它在内存中高效执行操作。Redis支持各种数据结构如哈希、集合、排序集合、列表和字符串。特别对我们感兴趣的是排序集合,它为成员添加了按分数排序的有序视图。
在Redis中,使用排序集合作为底层数据结构实现地理空间索引。Redis实际上使用geohash算法将纬度和经度编码到排序集合的分数中。Geo Set是使用排序集合实现的,以更抽象的层次支持Redis中的地理空间数据。
Redis提供了简单的命令来使用地理空间索引并执行常见操作,例如创建新集合和添加或更新集合中的成员。例如,我们可以使用GEOADD命令从命令行添加成员:
GEOADD locations 20.99 65.44 Vehicle-1 23.99 55.45 Vehicle-2
在这里,我们正在将一些车辆的位置添加到名为“locations”的Geo Set中。
Redis还提供了几种读取索引的方法,如ZRANGE、ZSCAN和GEOPOS。此外,我们可以使用GEODIST命令计算集合中成员之间的距离。但最有趣的命令是那些允许我们按位置搜索索引的命令。例如,我们可以使用GEORADIUSBYMEMBER命令搜索特定成员半径范围内的成员:
GEORADIUSBYMEMBER locations Vehicle-3 1000 m
在这里,我们感兴趣的是找到第三辆车周围一公里范围内的所有其他车辆。
Redis在提供支持存储大量地理空间数据和执行低延迟地理空间查询方面非常强大且简单。
7.2. MongoDB
MongoDB是一个面向文档的数据库管理系统,使用类似JSON的文档存储数据,并可选模式。它提供了多种搜索文档的方法,如字段查询、范围查询和正则表达式。我们甚至可以对文档进行索引以进行主键和次键索引。此外,MongoDB通过分片和复制提供高可用性和水平可扩展性。
我们可以将空间数据存储在MongoDB中,作为GeoJSON对象或作为旧版坐标对。GeoJSON对象对于存储类似地球表面的地点数据很有用,而旧版坐标对则适用于我们可以在欧几里得平面上表示的数据。
要指定GeoJSON数据,我们可以使用一个嵌入文档,其中字段名为_type_表示GeoJSON对象类型,另一个字段名为_coordinates_表示对象的坐标:
db.vehicles.insert( {
name: "Vehicle-1",
location: { type: "Point", coordinates: [ 83.97, 70.77 ] }
} )
在这里,我们正在向名为_vehicles_的集合中添加一个文档。嵌入文档是一个带有其经度和纬度坐标的类型_Point_的GeoJSON对象。
此外,MongoDB**提供了多种空间索引类型,如_2dsphere_和_2d_**以支持空间查询。_2dsphere_支持计算类似地球球体的几何体的查询:
db.vehicles.createIndex( { location : "2dsphere" } )
在这里,我们正在为我们的集合的_location_字段创建一个_2dsphere_索引。
最后,MongoDB提供了几种空间查询操作符来促进通过空间数据的搜索。一些操作符是_geoIntersects_、geoWithin、near_和_nearSphere。这些操作符可以解释平面或球体上的几何体。
例如,让我们看看如何使用_near_操作符:
db.places.find(
{
location:
{
$near: {
$geometry: { type: "Point", coordinates: [ 93.96, 30.78 ] },
$minDistance: 500,
$maxDistance: 1000
}
}
}
)
在这里,我们正在搜索距离提到的GeoJSON _Point_至少500米且最多1000米的文档。
MongoDB的灵活性、扩展效率和对地理空间数据的内在支持使其非常适合地理空间应用。
7.3. PostGIS
PostgreSQL是一个关系数据库管理系统,提供SQL合规性和ACID事务功能。它非常通用,支持各种工作负载。PostgreSQL包括内置的同步复制支持和内置的常规B树和哈希表索引。PostGIS是PostgreSQL的空间数据库扩展器。
基本上,PostGIS为PostgreSQL添加了存储地理空间数据和执行位置查询的支持,并使用SQL。它为_Point_、LineString、_Polygon_等添加了几何类型。此外,它还提供了使用R-tree-over-GiST(通用搜索树)的空间索。最后,它还添加了用于地理空间测量和集合操作的空间操作符。
我们可以像往常一样在PostgreSQL中创建数据库,并启用PostGIS扩展来开始使用它。基本上,数据存储在行和列中,但PostGIS引入了一个具有由空间参考标识符(SRID)定义的特定坐标系的几何列。PostGIS还增加了许多选项,用于加载不同的GIS数据格式。
PostGIS支持几何和地理数据类型。我们可以使用常规SQL查询来创建表并插入地理数据类型:
CREATE TABLE vehicles (name VARCHAR, geom GEOGRAPHY(Point));
INSERT INTO vehicles VALUES ('Vehicle-1', 'POINT(44.34 82.96)');
在这里,我们创建了一个新的表“vehicles”,并使用_Point_几何添加了一个特定车辆的位置。
PostGIS增加了相当多的空间函数来对数据执行空间操作。例如,我们可以使用空间函数_ST_AsText_将几何数据读取为文本:
SELECT name, ST_AsText(geom) FROM vehicles;
当然,对我们来说,更有用的查询是查找给定点附近的所有车辆:
SELECT geom FROM vehicles
WHERE ST_Distance( geom, 'SRID=4326;POINT(43.32 68.35)' ) < 1000
在这里,我们正在搜索距离提供的点一公里半径内的所有车辆。
PostGIS为PostgreSQL增加了空间能力,允许我们利用熟悉的SQL语义处理空间数据。此外,我们还可以从使用PostgreSQL的所有优势中受益。
8. 行业标准和规范
虽然我们已经看到数据库层对空间数据的支持正在增长,那么应用层呢?为了构建地理空间应用,我们需要编写能够高效处理空间数据的代码。
此外,我们需要标准和规范来表示和在不同组件之间传输空间数据。进一步地,语言绑定可以支持我们用像Java这样的语言构建地理空间应用。
在本节中,我们将介绍地理空间应用领域已经发生的一些标准化工作,它们制定的标准以及我们可以使用的库。
8.1. 标准化努力
在这方面已经进行了很多开发,通过多个组织的协作努力,已经建立了几个标准和最佳实践。让我们首先了解一下为不同行业的地理空间应用的推进和标准化做出贡献的一些组织。
环境系统研究所(ESRI)或许是最古老和最大的国际地理信息系统(GIS)软件和地理数据库管理应用供应商之一。他们开发了一系列名为ArcGIS的GIS软件,针对多个平台,如桌面、服务器和移动设备。它还建立并推广了用于矢量和栅格数据类型的数据格式——例如,Shapefile、文件地理数据库、Esri Grid和Mosaic。
开放地理空间联盟(OGC)是一个由300多家公司、政府机构和大学组成的国际行业联盟,参与共识过程,以开发公开可用的接口规范。这些规范使复杂的空间信息和服务对所有类型的应用程序都是可访问和有用的。目前,OGC标准包括30多个标准,包括空间参考系统标识符(SRID)、地理标记语言(GML)和简单特性——SQL(SFS)。
开源地理空间基金会(OSGeo)是一个非营利性、非政府组织,支持和促进开放地理空间技术和数据的协作开发。它促进了像瓦片地图服务(TMS)这样的地理空间规范。此外,它还帮助开发了几个地理空间库,如GeoTools和PostGIS。它还在其保护伞下推广像QGIS这样的应用程序,这是一个用于数据查看、编辑和分析的桌面GIS。这些只是OSGeo在其保护伞下推广的一些项目。
8.2. 地理空间标准:OGC GeoAPI
GeoAPI实现标准通过GeoAPI库定义了一个Java语言API,包括一组类型和方法,我们可以使用它们来操作地理信息。地理信息的底层结构应遵循国际标准化组织(ISO)技术委员会211和OGC采用的规范。
GeoAPI提供了Java接口,这些接口是实现无关的。在我们实际使用GeoAPI之前,我们必须从第三方实现列表中选择。我们可以使用GeoAPI执行几种地理空间操作。例如,我们可以使用EPSG代码获取坐标参考系统(CRS)。然后,我们可以执行坐标操,如在一对CRS之间的地图投影:
CoordinateReferenceSystem sourceCRS =
crsFactory.createCoordinateReferenceSystem("EPSG:4326"); // WGS 84
CoordinateReferenceSystem targetCRS =
crsFactory.createCoordinateReferenceSystem("EPSG:3395"); // WGS 84 / World Mercator
CoordinateOperation operation = opFactory.createOperation(sourceCRS, targetCRS);
double[] sourcePt = new double[] {
27 + (59 + 17.0 / 60) / 60, // 27°59'17"N
86