从 ESRI shapefile 数据创建一个 KML 服务
简介: 近来,基于地图的 mashup 大量出现。Mashup 要求服务必须能被聚合。基于位置的 mashup 需要能够提供边界信息的服务。借助基于 Web 的地图提供商,您只需很少的投资甚至无需投资,就能创建基于地图的 mashup。在本文中,了解如何从 ESRI shapefile 创建一个 KML 边界服务以便用于 mashup。
<!-- <p class="ibm-no-print"> <div id="dw-tag-this" class="ibm-no-print"></div> <div id="interestShow" class="ibm-no-print"></div> </p> -->发布日期: 2010 年 3 月 01 日
级别: 中级
其他语言版本: 英文 <!-- <br /><b>建议:</b> <span id="nCmts"><img alt="" src="//www.ibm.com/developerworks/i/circle-preloader.gif" height="12" width="50" /><img alt="" src="//www.ibm.com/i/gif" height="14" width="1" /></span> --><!-- Rating_Area_Begin --><!-- Ensure that div id is based on input id and ends with -widget -->
时下,基于地图的 mashup 无处不在,很大程度上是因为可被聚合的服务现在广泛可用。对于一个基于地图的 mashup,一般需要能够提供地理边界的服务。美国人口局发布的州、县和 Zip 编码分界采用的是 Environmental Systems Research Institute (ESRI) shapefile 格式。在本文中,了解如何创建一个简单的服务来通过开源软件从 ESRI shapefile 交付 KML 文件。
现在已经有很多商业的和开源的工具和产品可供您用来处理空间坐标数据和创建 mashup。本文中的示例使用了开源软件:作为数据库的 PostGIS、GeoTools Java™ 库、OpenLayers 地图库以及作为应用服务器的 Apache Tomcat。
这个示例解决方案对空间坐标转换使用 GeoTools,为将 BLOB(binary large object)数据从数据库数据转变为几何(geometry)数据使用了 JTS。
下载 本文中使用的示例代码。要获得本例中使用的软件,参见 参考资料。
本节讨论了创建本文中的这个示例所需做的准备。
图 1 给出了这个示例解决方案的逻辑架构。
美国人口局使用了 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 并不提供实用工具来直接将 ESRI shapefile 格式的数据导入到数据库(像 IBM® DB2® 那样)。将数据导入 PostGIS,必须执行如下两步骤:
INSERT
语句。要使用此工具从 shapefile 创建一个具有 SQL INSERT
语句的文件,可以使用命令 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 码,导入到数据库内的数据可以具有多个行。一个州有可能由多个非连续的地块组成。例如阿拉斯加州就包括多个岛屿。为了简化我们的应用程序,我们将只选择面积最大的那个地块。
地理数据可以用两种格式表示:Well Known Binary (WKB) 及 Well Known Text (WKT)。PostGIS 提供了 AsBinary()
和 AsText()
函数来将 geometry 列分别转变为数据的 WKB 和 WKT 表示。查询数据的 SQL 语句如清单 1 所示。
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 给出了一个例子。
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 所示。
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 给出了一个例子。
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 文档所具有的布局应该如下所示。
<?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 所示。
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 文档的服务已经准备好了。下一步是创建一个 Web 页面来使用此服务通过 OpenLayers 显示地图上的边界。
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。
借助 mashup,企业就可以开启所有数据类型的潜能。您现在能够提供给用户创建位置应用程序的能力,而成本只是传统应用程序开发范型的很小一部分。
位置智能 mashup 以及基于地图的 mashup 是非常流行的 mashup 类别。随着基于 Web 的地图提供商的出现,现在用较少的资金投入(甚至无需资金投入)就可以很容易地创建基于地图的 mashup。在本文中,您了解了如何使用开源软件来从 ESRI shapefile 创建一个可用在 mashup 内的 KML 边界服务。
<!-- CMA ID: 470202 --><!-- Site ID: 10 --><!-- XSLT stylesheet used to transform this file: dw-article-6.0-beta.xsl -->示例代码 | os-kmlservice-SourceFiles.zip | 11KB | HTTP |