GIS 的过去、现在和未来:PostGIS 2.0 发布!

通过 PostGIS 2.0 扩展 PostgreSQL 的功能,探索空间数据库的所有魔力。

即使您不熟悉 GIS,我也很确定您知道什么是 Web 地图。GIS 代表地理信息系统,它起源于 1970 年代初期,是一套面向科学家(制图师、土地规划师和生物学家)的工具和技术。从那时起,就像许多其他计算机相关领域一样,该领域经历了惊人的发展。最具革命性的事情之一是,现在地图,尤其是 Web 地图,已成为数百万人日常生活中的常见体验。不仅在过去的几年里,我们看到人们越来越多地使用地图应用程序,而且个人 Web 地图也出现了爆炸式增长。今天,许多博客和个人网站都有地图。

什么是 PostGIS?

那么,空间数据的特殊之处是什么呢? 实际上并没有太多特别之处——很多数据都有位置参考(将您的地址簿视为一个简单的示例),但空间组件实际上并未组织起来。 当您想要组织您的空间数据时,您需要使用适当的工具进行操作。

空间数据与所有其他数据类型一样,需要存储在某个地方。RDBMS 是存储、处理和分析海量数据的绝佳工具,但如果您要走这条路,则需要带有空间扩展的 RDBMS。 您知道一个很棒的开源 RDBMS 吗? 我敢打赌你知道。 我们中的许多人通常在 Web 应用程序中使用 MySQL,但当涉及到空间数据时,它不是首选。 当涉及到空间数据时,您的朋友是 PostGIS,PostgreSQL 的绝佳伴侣。

我相信您听说过 PostgreSQL。 它可能是最著名的开源 RDBMS,并且 LJ 过去经常报道它。 如果您不熟悉它,请查看 Reuven M. Lerner 在 2011 年 4 月的 LJ 杂志上发表的“PostgreSQL 9.0”(https://linuxjournal.cn/article/10986)。

PostGIS 不是一个新项目。 它始于 2001 年,并在 2006 年的 1.0 版本中达到成熟。 2012 年 4 月 3 日,发布了 2.0 版本。 2.0 版本是一个重大转变,它确实打破了向后兼容性。 PostGIS 开发人员不得不进行此项突破,因为采用了新的序列化(请参阅“资源”)。 2012 年 6 月 22 日,发布了 2.0.1 版本,这是一个错误修复版本,这是撰写本文时最新的版本。

安装 PostGIS

无论您的 Linux 机器上是否安装了 PostgreSQL,启动并运行 PostGIS 都非常简单。 您可以下载源代码并自行编译,这并不难,但对于初次了解 PostGIS 来说,这不是真正必要的。 如果您喜欢编译,请查看参考资料——官方文档非常详细和完整。 社区中也有许多关于自定义安装的博客文章。

当您没有特定要求时,简单的方法通常是最好的。 您可以使用您的 Linux 发行版提供的软件包(例如,对于 Debian 发行版,键入 sudo apt-get install postgresql-9.1-postgis)。 但是,与其他快速发展的软件一样,您不会找到最新版本。

如果您想要最前沿的版本,EnterpriseDB 准备的二进制文件可能会派上用场。 安装非常简单,它还包括 Stack Builder,这是一个用于添加工具和使用未来版本升级安装的实用程序。

扩展 PostgreSQL

作为 PostgreSQL 的扩展,您可能想知道 PostGIS 为 PostgreSQL 附带的许多功能添加了什么。 简而言之,它扩展了空间对象的存储、检索和分析能力。 让我们看一个例子,以更好地解释它的工作原理。 您知道 RDBMS 可以回答诸如“每个部门目前有多少雇员在休假?”之类的问题。 使用 PostgreSQL 提出这个问题的标准方法是使用 SQL


SELECT COUNT(E.SERIAL) AS #, D.NAME FROM EMPLOYERS E 
 ↪JOIN DEPARTMENT D ON (DEP_ID)  WHERE E.ON_HOLYDAY = 1 
 ↪GROUP BY D.NAME ORDER BY D.NAME

如果你的问题有一个空间组成部分怎么办? 假设您想知道您所在县的新高速公路路径 3 公里范围内有多少房屋。 标准 SQL 没有表达此功能的功能,但 PostGIS 可以帮助执行分析


SELECT COUNT(id) FROM houses WHERE ST_DWithin(geom,(SELECT 
 ↪highway.geom FROM county, highway WHERE ST_Intersects 
 ↪(county.geom, highway.geom) AND county.name = 'Orange' 
 ↪AND highway.name = 'Interstate 5'),3000);

看起来强大吗? 的确如此! 上面的代码片段应该给您一些关于 PostGIS 提供的提示——大量的特殊函数,前缀为 ST_ 用于查询和处理,加上两种新的数据类型,称为 geometry 和 geography。

当然,geometry 和 geography 是空间要素的数据类型。 它们非常相似。 两者都允许您在表中存储简单的几何对象。 最大的区别在于 geography 接受大地坐标(即,在球面参考系中以度表示),而 geometry 接受在平面参考系上定义的坐标。 Geography 是在 PostGIS 1.5.0 版本中引入的,由于底层的复杂数学,只有少数函数支持它。

我所说的简单要素是指点、线和面。 有了它们,您可以对真实世界进行建模。 实际上,这是一种标准方法——简单要素的属性和行为由开放地理空间联盟(OGC,一个致力于定义 GIS 和数据互操作性开放标准的组织)建模,并且 PostGIS 从早期版本开始,就内置了对该标准的强大支持。

向表中添加 geometry 支持非常简单。 假设您要构建一个世界首都表,您将从基本属性开始


CREATE TABLE capitals (
id SERIAL,
state_name TEXT,
capital_name TEXT,
population numeric(8,0),
PRIMARY KEY(id)
);

如果您要存储可以在地图上表示的要素,则需要添加空间参考。 点 geometry 可能是一个好方法; AddGeometryColumn 是您需要的函数


SELECT AddGeometryColumn('gisuser', 
 ↪'capitals','geom',4326,'POINT',2);

在这里,您传递了模式、表名、geometry 列名、空间参考系统和 geometry 类型的值。 最后一个值表示您想要一个二维 geometry(即,在表面上定义的点)。 如果您要存储海拔,可以将维度值设置为三。 还有更多。 PostGIS 还支持四维 geometry。 嗯,第四维不是用于旅行,但它有助于将度量与 geometry 相关联,而第四维实际上称为 M。 例如,河流网络可以建模为多线串值,其中 M 坐标值测量到河流入海口的距离。 方法 ST_LocateBetween 可用于查找河流中位于距入海口例如 10 到 12 公里之间的所有部分。

在使用您的表之前,最好在 geometry 列上创建索引。 语法等同于任何其他索引创建; 索引类型是 GiST(广义搜索树),有点类似于 R 树索引


CREATE INDEX capitals_geom_gist ON capitals USING gist (geom);

现在让我们向表中添加真实数据。 如何在 geometry 列中插入值? ST_GeomFromText 函数为您转换数值。 那么,让我们插入您在伦敦观看奥运会时拾取的坐标


INSERT INTO capitals (state_name, capital_name, population, geom) 
 ↪values('UK','London', 6500000, 
 ↪ST_GeomFromText('POINT(-0.01639, 51.53861)', 4326));

您传递给函数的文本称为空间对象的 Well-Known Text (WKT) 表示。 点非常容易定义,但是如何表达线或面呢? 您可以模仿首都表定义来创建河流表,并为泰晤士河添加一条记录


ST_GeomFromText('LINESTRING(0.31221 51.47033, 0.33477 51.45171, 
 ↪0.44437 51.45851, 0.45877 51.48934, 0.61523 51.49512)',4326)

另一个表可以包含用面表示的著名建筑物。 您可以在这里找到威斯敏斯特教堂


ST_GeomFromText('POLYGON((-0.12850 51.49963, -0.12856 51.49929, 
 ↪-0.12814 51.49927, -0.12822 51.49896, -0.12722 51.49890, 
 ↪-0.12714 51.49919, -0.12627 51.49933, -0.12711 51.49957, 
 ↪-0.12707 51.49971, -0.12751 51.49974, -0.12758 51.49956, 
 ↪-0.12850 51.49963),(-0.12810 51.49902, -0.12805 51.49924,
 ↪-0.12757 51.49921, -0.12761 51.49897, -0.12810 51.49902))',4326)

面的 WKT 包含两个用圆括号括起来的坐标列表,而线始终由单个列表定义。 实际上,面可能包含孔。 第一个列表定义面的外环,而后续列表(您可以根据需要添加任意多个)定义包围孔的内环。

数据分析

知道您的要素安全地存储在数据库中很好,但您可能希望将它们用于检索以外的其他目的。 PostGIS 函数允许您与空间对象交互并探索它们的关系。

称为构造函数的函数从多种格式的定义构建 geometry。 它们有点像翻译器。 您之前在 WKT 中使用过它,ST_GeomFromKMLST_GeomFromGeoJSON 启用了从其他流行格式的转换。 输出函数启用反向转换,如 ST_AsTextST_AsGeoJSONST_AsKML

ST_IsValidST_GeometryType 检查 geometry 的基本属性。 您可以使用 ST_NumPoint 与 geometry 交互以检索顶点总数,并使用 ST_PointN 获取第 n 个顶点; ST_RemovePoint 删除您传递给函数的 position 处的顶点。 函数名称通常是不言自明的,例如 ST_ScaleST_Rotate

ST_Distance 测量两个 geometry 对象之间的最小距离。 与其他函数一样,此函数已重载,确切的定义是


float ST_Distance(geometry g1, geometry g2);
float ST_Distance(geography gg1, geography gg2);
float ST_Distance(geography gg1, geography gg2, boolean use_spheroid);

返回的距离是沿 Cartesian 平面测量的 geometry,以及沿 spheroid/sphere 测量的 geography 类型。 如果您正在查询相对较近的对象,那么如何使用它们的问题似乎是徒劳的,但请考虑测量从旧金山到丹佛的距离


SELECT to_char(round(ST_Distance(
ST_GeomFromText('POINT(-122.440 37.802)',4326)::geography,
St_GeomFromText('POINT(-104.987 39.757)',4326)::geography
)),'999,999,999');
1,529,519

大约 1,530 公里是很长的路程,从旧金山直接到丹佛可能是一个真正的挑战,因此还有额外的里程空间。 但是,如果您尝试在印刷地图上测量相同的距离,您可能会发现一个相当不同的结果。 正如您在小学学到的那样,地球的形状几乎是一个球体。 当地图在平面表面上表示地球的很大一部分时(是的,曲面显示器即将问世),它必须扭曲真实的形状和距离。 通过将两个 geography 对象传递给 ST_Distance,您要求它在球体表面上执行距离计算。 让我们使用 geometry,它将使用 Cartesian 平面进行计算


SELECT to_char(round(ST_Distance(
ST_Transform(ST_GeomFromText('POINT(-122.440 37.802)',4326),3857),
ST_Transform(ST_GeomFromText('POINT(-104.987 39.757)',4326),3857)
)),'999,999,999');

为了获得以米为单位的结果,与之前的米相当,您需要添加 ST_Transform 函数以动态将 SRS 更改为大多数 Web 地图系统使用的 Web Mercator


1,962,818

超过 1,900 公里! 嘿,Mercator 先生,你要带我去哪里?

加载数据

您已经了解了如何在 PostGIS 中以多种方式处理空间数据,但是如何将数据加载到数据库中呢? 如果您熟悉 PostgreSQL,您就会知道它附带了 psql,一个命令行工具,或者如果您喜欢使用 GUI 进行交互,您可能一直在使用 pgAdmin III。 两者都不擅长处理空间数据,但您可以执行 SQL 代码来执行数据加载。

如果您在 Internet 上搜索,您很快就会意识到许多数据都以 shapefile 格式提供,这是一种二进制专有格式,是空间数据交换的事实标准。 您是否想知道如何将二进制格式转换为 SQL 脚本? 不用担心; 自早期版本以来,PostGIS 就包含了一些工具,可以读取 shapefile 并将其加载到数据库中。

shp2pgsql 和 pgsql2shp 是命令行工具,可让您的数据进出。 毫不奇怪,shp2pgsql 加载数据。 实际上,shapefile 并非真正由 shp2pgsql 加载,而是转换为 psql 可以保存并为您加载的形式。 因此,您只需将输出管道传输到 psql


$ shp2pgsql -s 4269 -g geom -I ~/data/counties.shp 
 ↪public.counties | psql -h localhost -p 5432 -d 
 ↪postgisDB -U gisuser

所需的基本参数集是 -s 用于设置空间参考系统,-g 用于命名几何列(在附加数据时很有用),以及 -I 用于创建空间索引。 还有很多其他参数使其成为一个灵活的工具。 像往常一样,如果您需要执行不太重要的数据加载,-? 是您的朋友。 除了创建新表(默认选项)之外,您还可以将数据附加到现有表、删除并重新创建它,或者只是创建一个空表,根据 shapefile 数据对其结构进行建模。 pgsql2shp 可让您将数据放入 shapefile


$ pgsql2shp -f ~/data/rivers -h localhost -p 5432 -u 
 ↪postgres postgisDB0 public.rivers

数据源可以是表或视图,但您也可以在提取时过滤数据,以仅导出表的一部分


$ pgsql2shp -f ~/data/california_counties -h localhost -p 
 ↪5432 -u postgres postgisDB "SELECT * FROM 
 ↪public.counties WHERE statefp = '06'"

顾名思义,shp2pgsql-gui 是 shp2pgsql 的图形版本。 2.0 版本引入了一些有趣的功能。 尽管名称如此,但您现在可以将其用于加载 shapefile 和导出 shapefile,并且尽管早期版本一次处理一个 shapefile,但现在您可以添加任意多个需要加载的文件,然后一次运行它。

图 1. Shapefile 加载器 GUI

栅格数据

在 PostGIS 中存储和处理栅格数据类似于矢量数据。 航空影像和卫星场景(如 Google 地图中可见的那些)是常见的示例,但其他类型在 PostGIS 内部可能更有用。 实际上,在 PostGIS 内部拥有栅格数据的真正价值在于执行分析的可能性。 您还可以在分析中混合栅格和矢量数据。 数字高程模型(栅格,其中每个像素都关联一个高程值)通常被地质学家用于执行地形分析。 已添加栅格数据类型以支持此类数据。 您可以像为矢量创建表一样为栅格存储创建表


CREATE TABLE myraster(rid integer, rast raster);

栅格被平铺成规则的瓦片,每个块都作为表中的一条记录加载。 例如,如果您的 imagery.tif 文件大小为 4096x3072 像素,并且您选择 256x256 像素的瓦片大小,则加载后,您将得到一个包含 192 条记录的表。

从 SQL 提示符加载栅格数据并不容易。 与矢量一样,存在一个命令行实用程序 raster2pgsql


$ raster2pgsql -s 4326 -t 256x256 -I -C 
 ↪/home/postgis/data/imagery.tif imagery | 
 ↪psql -d postgisDB -h localhost -p 5432 -U gisuser

参数非常相似,不同之处在于您使用 -t 设置瓦片大小,而 -C 设置栅格上的标准约束集。

总结

本文仅是对 PostGIS 功能的简要探索。 请考虑一下,大约有 700 个专门用于处理空间数据的函数。 我希望您觉得它有趣并想尝试一下。 在专家中,PostGIS 一直被认为是难以驾驭的骏马。 我认为这需要一点谦逊和阅读手册的意愿。 但是,一旦您开始使用它,您很快就会发现自己问为什么人们要花大价钱购买商业空间数据库。

资源

EnterpriseDB 下载:http://www.enterprisedb.com/downloads/postgres-postgresql-downloads

Shapefile 格式:http://en.wikipedia.org/wiki/Shapefile

ESRI 关于 Shapefile 的官方白皮书:http://www.esri.com/library/whitepapers/pdfs/shapefile.pdf

EPSG 代码的主要参考:http://epsg-registry.org

PostGIS 2.0 演示文稿(您可以在第 5-13 页找到有关新序列化的详细信息):http://s3.cleverelephant.ca/foss4gna2012-postgis2.pdf

PostGIS 用户 Wiki:http://trac.osgeo.org/postgis/wiki/UsersWikiMain

PostGIS 官方文档:http://www.postgis.org/documentation

加载 Disqus 评论