从 ESRI shapefile 数据创建一个 KML 服务
近来,基于地图的 mashup 大量出现。Mashup 要求服务必须能被聚合。基于位置的 mashup 需要能够提供边界信息的服务。借助基于 Web 的地图提供商,您只需很少的投资甚至无需投资,就能创建基于地图的 mashup。在本文中,了解如何从 ESRI shapefile 创建一个 KML 边界服务以便用于 mashup。
从 ESRI shapefile 数据创建一个 KML 服务
近来,基于地图的 mashup 大量出现。Mashup 要求服务必须能被聚合。基于位置的 mashup 需要能够提供边界信息的服务。借助基于 Web 的地图提供商,您只需很少的投资甚至无需投资,就能创建基于地图的 mashup。在本文中,了解如何从 ESRI shapefile 创建一个 KML 边界服务以便用于 mashup。
简介
时下,基于地图的 mashup 无处不在,很大程度上是因为可被聚合的服务现在广泛可用。对于一个基于地图的 mashup,一般需要能够提供地理边界的服务。美国人口局发布的州、县和 Zip 编码分界采用的是 Environmental Systems Research Institute (ESRI) shapefile 格式。在本文中,了解如何创建一个简单的服务来通过开源软件从 ESRI shapefile 交付 KML 文件。
技术和软件产品
现在已经有很多商业的和开源的工具和产品可供您用来处理空间坐标数据和创建 mashup。本文中的示例使用了开源软件:作为数据库的 PostGIS、GeoTools Java™ 库、OpenLayers 地图库以及作为应用服务器的 Apache Tomcat。
-
PostGIS
- 一个对开源关系数据库 PostgresSQL 的空间扩展。PostGIS 支持由 OpenGIS Simple Features Specification for SQL 标准描述的空间数据类型和函数。 GeoTools
-
一个 Java 库,用来执行空间数据的处理。 GeoTools 包括:
- 对各种空间数据格式(比如 ESRI shapefile、Geography Markup Language (GML)、MapInfo Interchange Format (MIF))的支持以及空间坐标转换。
- 来自 Vivid Solutions 的 Java Topology Suite (JTS),定义了 Java 类来表示空间数据。
这个示例解决方案对空间坐标转换使用 GeoTools,为将 BLOB(binary large object)数据从数据库数据转变为几何(geometry)数据使用了 JTS。
OpenLayers
- 一个开源 JavaScript 地图库,让您可以向 Web 页面添加动态地图。您可以覆盖基础地图之上的标记、线条和多边形。本文中的例子使用了 OpenLayer 的一个功能,即从 KML 文件在基础地图之上添加一个层。 Apache Tomcat
- 本例中使用的一个开源 J2EE JSP 和 servlet 容器,用来部署服务。
下载 本文中使用的示例代码。要获得本例中使用的软件,参见 参考资料。
做好准备
本节讨论了创建本文中的这个示例所需做的准备。
逻辑架构
图 1 给出了这个示例解决方案的逻辑架构。
图 1. 逻辑架构
![逻辑架构](https://i-blog.csdnimg.cn/blog_migrate/d0d8dad1e57565774bdaf51ac8589393.png)
边界数据
美国人口局使用了 Topologically Integrated Geographic Encoding and Referencing (TIGER) 格式和数据库来描述和发布地面属性,比如道路、建筑、河流、湖泊和普查地段。TIGER 数据库包含的数据集包括由州、县、城市和 ZIP 编码组成的地理边界。这个数据集使用 ESRI shapefile 格式(参见 参考资料)。本例使用的是 ESRI shapefile 格式的州边界。
获得数据
美国人口局提供的州边界的数据集是 ARC/INFO Export (.e00)、ARC/INFO Ungenerate 和 ESRI shapefile (.shp) 格式的。本文只使用了其中的 ESRI shapefile 格式。每个州都有各自的州边界,所有的州边界则包括在一个 shapefile 中。本例就使用了这个包含所有州边界的 shapefile (st99_d00_shp.zip)。
将 st99_d00_shp.zip 文件的内容解压缩以得到 st99_d00.shp shapefile 及关联文件(st99_d00.shx 和 st99_d00.dbf)。
将数据导入 PostGIS
PostGIS 并不提供实用工具来直接将 ESRI shapefile 格式的数据导入到数据库(像 IBM® DB2® 那样)。将数据导入 PostGIS,必须执行如下两步骤:
- PostGIS 提供了 shp2pgsql 工具,可用来将 ESRI shapefile 转变为适合 SQL 的
INSERT
语句。要使用此工具从 shapefile 创建一个具有 SQLINSERT
语句的文件,可以使用命令shp2pgsql st99_d00.shp testdb.state_bounds > statebounds.sql
。这个命令,如果没有命令行选项,将会将 SRS ID 设置为默认的值-1
。对于本文的目的而言,这没有问题,因为您将为反向投影使用 GeoTools,坐标转换则不使用数据库特性。 - 确保数据库启动后,就可以运行
psql
命令来执行此脚本了。
默认地,由 shp2pgsql 工具创建的 SQL 文件将会包含 table create
和用以将 geometry 列添加到表中的语句。您可以使用此工具的各种命令行选项来不创建这个表。(相关的 PostGIS 文档,请参见 参考资料。)
验证导入
您可以使用类似 pgAdmin III 的管理工具或是通过在此表上执行 SQL SELECT
语句来验证这个导入。您可以在 geometry 列内尝试使用 PostGIS 函数,比如 AsText()
和 AsBinary()
,以确保导入成功。
应用程序
用来从刚刚导入的数据创建 KML 的应用程序是一个 J2EE servlet。这个 servlet 将会:
- 接收状态 FIPS 代码作为输入。
- 查询这个数据库。
- 执行坐标转换。
- 使用数据创建一个 KML。
理解数据
针对一个给定的州的 FIPS 码,导入到数据库内的数据可以具有多个行。一个州有可能由多个非连续的地块组成。例如阿拉斯加州就包括多个岛屿。为了简化我们的应用程序,我们将只选择面积最大的那个地块。
查询数据库并读取 geometry
地理数据可以用两种格式表示:Well Known Binary (WKB) 及 Well Known Text (WKT)。PostGIS 提供了 AsBinary()
和 AsText()
函数来将 geometry 列分别转变为数据的 WKB 和 WKT 表示。查询数据的 SQL 语句如清单 1 所示。
清单 1. 用来检索 WKB 格式的州边界的查询
String stateBoundsSql = "SELECT AsBinary(the_geom), name FROM state_bounds WHERE state = '" + stateCd + "' ORDER BY area DESC LIMIT 1";
GeoTools 库是 Java Topology Suite (JTS) 自带的,它具有类 WKBReader
,能将 geometry 的 WKB 表示转变为代表这个 geometry 的 Java 对象。我们使用它将州边界数据转变为 MultiPolygon
Java 对象,它是另一个 JTS 类。清单 2 给出了一个例子。
清单 2. 将 geometry 读入一个 JTS Java 对象
WKBReader wkbRead = new WKBReader();
Geometry geom = wkbRead.read(resultSet.getBytes(1));
MultiPolygon multiPolygon = (MultiPolygon) geom;
坐标转换
从美国人口局导入的数据使用基于 North American Datum 1983 (NAD83) 的空间参照系统。它将地球视为一个近似的椭圆体。基于 Web 的地图库,比如 OpenLayers、Google Maps 和 Microsoft® Bing,均基于 World Geodetic System (WGS) 数据。为了简化在基于 Web 的地图上显示数据所涉及的计算,这些库均认为地球是球体,而非椭圆体。因此,数据需要在转换后才能覆盖在基于 Web 的地图上。可以使用由 GeoTools 库提供的转换函数,如清单 3 所示。
清单 3. 创建转换对象
String SOURCE_WKT = "GEOGCS[\"GCS_North_American_1983\",DATUM[\"D_North_American_1983\", SPHEROID[\"GRS_1980\",6378137.0,298.257222101]],PRIMEM[\"Greenwich\",0.0],UNIT[ \"Degree\",0.0174532925199433]], AXIS[\"Latitude\",NORTH]]"; String TARGET_WKT = "GEOGCS[\"WGS 84\",DATUM[\"World Geodetic System 1984\",SPHEROID[ \"WGS 84\", 6378137.0, 298.257223563, AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\", \"6326\"]],PRIMEM[\"Greenwich\", 0.0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\", 0.017453292519943295],AXIS[\"Geodetic latitude\", NORTH],AXIS[\"Geodetic longitude\", EAST],AUTHORITY[\"EPSG\",\"4326\"]]"; CRSFactory crsFactory = FactoryFinder.getCRSFactory(null); CoordinateReferenceSystem sourceCRS = crsFactory .createFromWKT(SOURCE_WKT); CoordinateReferenceSystem targetCRS = crsFactory .createFromWKT(TARGET_WKT); Hints hint = new Hints(Hints.LENIENT_DATUM_SHIFT, true); CoordinateOperationFactory coFactory = FactoryFinder.getCoordinateOperationFactory(hint); CoordinateOperation co = coFactory.createOperation(sourceCRS,targetCRS); MathTransform transform = co.getMathTransform();
有了用来执行坐标转换的对象后,就可以传递坐标对并逐个对之进行转换。清单 4 给出了一个例子。
清单 4. 转换坐标
Coordinate[] coordinates = multiPolygon.getCoordinates(); for (int i = 0; i < coordinates.length; i++) { Coordinate coordinate = coordinates[i]; double x = coordinate.x; double y = coordinate.y; DirectPosition point = new GeneralDirectPosition(x, y); point = transform.transform(point, point); double lat = point.getOrdinate(0); double lng = point.getOrdinate(1); .. }
创建 KML
转换坐标后,现在只需要使用它们构造一个 KML 文档(参见 参考资料 获得 KML 文档)。一个用来表示多边形数据的 KML 文档所具有的布局应该如下所示。
清单 5. 用来表示多边形数据的示例 KML
<?xml version="1.0" encoding="UTF-8"?> <kml xmlns="http://earth.google.com/kml/2.0"> <Document> <Placemark> <MultiGeometry> <Style> <PolyStyle> <color>66FF0000</color> <colorMode>normal</colorMode> <fill>1</fill> <outline>0</outline> </PolyStyle> </Style> <Polygon> <LinearRing> <coordinates>-109.045,36.99,0 -109.0451,36.967,0 -109.0452,36.968,0 -109.0452,36.958,0 -109.045,36.99,0 </coordinates> </LinearRing> </Polygon> </MultiGeometry> </Placemark> </Document> </kml>
为了保持本文中的代码尽量简单,我们将不会使用 XML 解析器或 JAXB 来构造这个 KML 文档,而是通过追加字符串构造这个 KML,如清单 6 所示。
清单 6. 创建 KML 文档
String kmlHeader = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><kml xmlns=\"http://earth.google.com/kml/2.0\"><Document><Placemark><MultiGeometry> <Style><PolyStyle> <color>66FF0000</color><colorMode>normal</colorMode><fill>1</fill> <outline>0</outline></PolyStyle></Style><Polygon><LinearRing><coordinates>"; String kmlFooter = "</coordinates></LinearRing></Polygon></MultiGeometry></Placemark> </Document></kml>"; String kmlBody = ""; for (int i = 0; i < coordinates.length; i++) { .. double lat = point.getOrdinate(0); double lng = point.getOrdinate(1); kmlBody += lng + "," + lat + "," + 0 + "\n"; }
查看 KML
提供一个描述州边界的 KML 文档的服务已经准备好了。下一步是创建一个 Web 页面来使用此服务通过 OpenLayers 显示地图上的边界。
清单 7. 使用 KML 服务来覆盖地图形状的 JavaScript
map.addLayer(new OpenLayers.Layer.GML("KML", "http://localhost:8080/OpenKML/StateKml?stateCd=04", { format: OpenLayers.Format.KML, formatOptions: { extractStyles: true, extractAttributes: true, maxDepth: 2 } }));
如下所示的图 2 显示了亚利桑纳州的 KML 覆盖,在来自 Metacarta 的基础地图上使用了 OpenLayer。
图 2. OpenLayers 地图上的 KML 覆盖
![OpenLayers 地图上的 KML 覆盖](https://i-blog.csdnimg.cn/blog_migrate/6e50dd545b232577c2a02d27956b310a.png)
结束语
借助 mashup,企业就可以开启所有数据类型的潜能。您现在能够提供给用户创建位置应用程序的能力,而成本只是传统应用程序开发范型的很小一部分。
位置智能 mashup 以及基于地图的 mashup 是非常流行的 mashup 类别。随着基于 Web 的地图提供商的出现,现在用较少的资金投入(甚至无需资金投入)就可以很容易地创建基于地图的 mashup。在本文中,您了解了如何使用开源软件来从 ESRI shapefile 创建一个可用在 mashup 内的 KML 边界服务。
参考资料
学习
- 了解 PostGIS 的安装和配置。
- 获得 KML 的 XSD 文件。
- 借助文档、博客、wiki 和下载更多地了解 GeoTools。
- 探索面向 Web 的 OpenLayers 免费地图。
- 了解如何将 ESRI shapefile 导入 PostGIS。
- 更多地了解来自美国人口局的 TIGER 数据库。
- 要收听面向软件开发人员的有趣访谈和讨论,请查看 developerWorks 播客。
- 随时关注 developerWorks 技术活动和网络广播。
- 查阅最近将在全球举办的面向 IBM 开放源码开发人员的研讨会、交易展览、网络广播和其他活动。
- 访问 developerWorks Open source 专区获得丰富的 how-to 信息、工具和项目更新以及最受欢迎的文章和教程,帮助您用开放源码技术进行开发,并将它们与 IBM 产品结合使用。
- My developerWorks 社区涵盖了大量主题,是一个成功社区的典范。
- 查看免费的 developerWorks 演示中心,观看并了解 IBM 及开源技术和产品功能。
获得产品和技术
- 下载 PostGIS。主要的发布版均具有重大的功能改进(对象支持、空间索引、mapserver 支持、SRID 支持); 非主要的发布版则为一般的用户社区提供了 bug 修复和较小的特性增强。
- 从 Apache Software Foundation 下载 Apache Tomcat。
- 从美国人口局获得 州 ESRI shapefile。
- 使用 IBM 产品评估试用版软件 改进您的下一个开发项目,这些软件可以通过下载获得。
- 下载 IBM 产品评估试用版软件 或 IBM SOA Sandbox for Reuse,开始使用来自 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere® 的应用程序开发工具和中间件产品。
讨论
- 参与 developerWorks 博客 并加入 developerWorks 社区。