内容


使用 GDAL 在 Linux 上的应用程序中使用地理空间数据

地理空间数据是什么,如何使用?

Comments

如果您想要编写能使用所有类型地图的高质量软件,那就值得花时间了解本文主题。在您的软件中,地图有多种用途 — 有些很重要,比如在房地产网站上显示房屋位置,有些不太重要,但能增色不少,如在客户关系管理应用程序中显示地址地图。

地理信息科学一瞥

地理信息数据可能以任何形式保存。手写的地址,例如 1384 Elm Street,从技术上讲,就是地理数据。而专门的格式,将会使空间数据的交流和使用变得更加容易。大多数数据文件都包含一个单独的数据集,通常称为。这种命名惯例来自于一种以多层数据叠加形成地图。

矢量数据和通用格式

矢量数据 指的是通过几何结构定义的数据,如线段表示街道,而点表示位置。矢量数据包含由 xy 坐标,经常还有 z(立面)坐标所定义的几何图形。有些数据格式还提供 m(度量)坐标,这通常用于时间增量。

同样,一些格式还支持多部分图形(例如,多个线条或多个多边形)。多个单独的、不连接的图形组合在一起,形成一个图形元素。夏威夷就是个例子,它包含多个离散形状(岛屿),通常表示成一个单独的、多部分的图形。更高级的格式还允许指定圆弧、圆或其他复杂形状,还有数据格式允许混合多个图形(例如,点和线在同一文件中)。几何矢量形状通常称为特征,指的是特征数据,它与矢量数据形状数据 是一个意思。

大多数矢量数据格式还允许存储属性数据 — 通常是一个简单的数据库,每一组值对应一个图形,很像电子表格,每行对应一个图形。图 1 是矢量数据和属性表的示例。

图 1. 矢量数据和属性表
地图上覆盖的表中显示了国家的数据,包括 NAME、GMI_CNTRY 和 REGION
地图上覆盖的表中显示了国家的数据,包括 NAME、GMI_CNTRY 和 REGION

目前为止,最常用的矢量格式是 ESRI Shapefile(见 参考资料),它只存储一种图形;每个文件只包含点或多边形或线条。此格式还支持三维多部分图形,还有度量(m)维。 此格式至少包含三个独立文件:

  • example.shp— 存储实际几何形状
  • example.shx— 存储图形的空间索引,使呈现及其他操作更快
  • example.dbf— 存储属性表数据(实际是 dBASE 格式文件)
  • example.prj— 定义空间引用系统(SRS)或投影

光栅数据和通用格式

光栅数据 是由覆盖空间区域的网格值所定义的非几何数据。比如 10 米乘 10 米的网格,取每个交叉点的值(共有 100 组)就形成一个光栅数据集。光栅数据的层通常称为,而术语 仍然用于光栅和矢量数据。

高程数据就是很好的单级光栅数据的例子,它经常用颜色坡度,如 图 2 所示。电磁辐射是另一种常见的数据集(例如,近红外辐射或可见光)。标准的天线或卫星照片是三段数据集,其中包含红、绿、蓝段。

图 2. 高程数据集,根据高度从红色到深蓝色
图片显示地形图,按照标题描述进行红色编码
图片显示地形图,按照标题描述进行红色编码

光栅数据最常用的开放数据格式是 GeoTIFF— 对 TIFF 图片扩展,其中包含附加标记,如空间引用/投影信息和位置信息。见 参考资料 中 GDAL 光栅格式信息页,了解更多关于其他光栅数据格式的信息。

空间引用系统

有多种方式来定义地面坐标系统;有一些在某些用途方面比其他的更好。每个坐标系统都有劣势。这些系统的共同目标是在平面上表示椭圆形状 — 有些地方失真也是不可避免的。最著名的是 World Geodetic System [WGS84] 地理,或维度和经度(分别对应 yx 轴)。沿着赤道使用维度和经度相当简单,形成的网格线大致也是方形的。而在北方和南方地区,会出现严重失真(例如,格陵兰岛大部分地区)。使用 2-D 地图时保持精确度需要理解空间引用系统并合理使用。

地球不是圆的:椭圆体模型

地球是椭圆体,在两极稍扁,在赤道凸起。描述地球的形状的数学模型已经发展了很长时间。最常用的是 WGS1984;见参考资料中 Geodetic Models for the Earth 页,获取更多信息。

在数据中使用不匹配的椭圆体模型一般是主要问题。椭圆体模型是空间引用系统定义的第一部分。

投影系统

空间引用系统的第二部分是投影,它定义了如何将地球表面放置在 2-D 平面上。与椭圆体模型不同的是,使用正确的投影非常必要,使用不匹配的投影会造成让人笑话的错误,如图 3 中的布局错误。

图 3. 两个图形文件有不同的 Albers Equal Area 投影,无法连接起来
图形显示的是地图中美国是紫色,还有一些绿色圆圈错误地偏离地图
图形显示的是地图中美国是紫色,还有一些绿色圆圈错误地偏离地图

地理学投影(维度和经度)之后,最常用的投影是横向 Mercator 投影。想象有一个管道,将地球放于其中,沿着直线将地球的图像投影到管道中。地球接触管道的地方称为中央子午线, 周围地区的角度和距离保持得非常好,但随着从中央子午线出发,质量会急剧下降。世界范围内,为北半球和南半球定义了 60 个横向 Mercator 区域,称为 Universal Transverse Mercator (UTM)。

通用免费数据源

当地和国家政府经常发布免费数据。向本市办事处询问关于地理信息系统(GIS)的数据,通常您会得到比您所需还多的数据。可以说,最好的基于 web 的数据源是 USGS Seamless Data Server(见参考资料),它提供了大量的数据。

处理数据的有用软件

有几个开源工具可用于 GIS 数据:

  • Quantum GIS (QGIS)— QGIS 是完全可用于 Windows®、Linux® 和 Mac OS X 的 GUI。最好的创建和编辑数据的工具之一,它还包含一个插件架构可用于扩展。
  • MapWindow GIS— 另一个可完全用于 Windows 的 GUI 解决方案,它也包含一个插件架构,可使用 Microsoft® C# 和 Microsoft Visual Basic.NET® 进行扩展。
  • GDAL/OGR Simple Feature Library— 主要用于编程,它附带一些宝贵的命令行工具,如 gdal_translateogr2ogr(分别是光栅和矢量格式转换程序),以及 gdaltransform(数据投影之间转换的工具)。
  • MapServer— 此服务器从数据源生成图像,提供符号、标签和其他所有细节信息来生成一张地图。

这很复杂:为什么不能就用 Google Maps 或 Google Earth?

当然可以!这两个都是很棒的工具,但不适合所有用途。Google Maps 适合用在包含很多内容的网站上,您只是需要一个简单的地图,不需要很多数据,而且它适合没有编程背景的人。但是,它缺乏实际使用您自己的数据而不止是添加简单的位置的功能,并且它依赖于第三方。同样,Google Earth 也有自己的应用程序,是个神奇的工具, 但它不适合用于科学分析或类似于 ArcMap、QGIS、GRASS 及其他大规模应用程序。

使用地理数据编码

您所有的语言技能和工具都用得上。GDAL 包含处理多种 GIS 数据的工具,包含多种通用语言的绑定附件,包括 C/C++、PHP、Python、Perl、C#,甚至还有 Microsoft Visual Basic® V6。使用您熟悉的语言:所有的变化都可以。

程序样例:高尔夫场地垃圾报告程序

本应用程序样例的目标是创建一个公共 web 页面,用户可以用来查看高尔夫场地,并报告问题,如乱抛垃圾或浸水。问题报告应该立刻能在地图上让查阅者看到,并且存储在服务器中的图形文件应能由高尔夫场地维护人员从桌面 GIS 和手持设备上更新。图 4 显示了该项目的完成状态。

图 4. 完整的基本 web 应用程序样例
屏幕截图显示高尔夫场地照片,其中有各种图形元素,有缩放、平移和报告问题的按钮
屏幕截图显示高尔夫场地照片,其中有各种图形元素,有缩放、平移和报告问题的按钮

主要数据 — 2008 年航空图像 — 来自于 USGS Seamless Server。惟一用到的其他数据集是表示圆洞边界的图形文件。这使用 QGIS 手工数字化,只是为地图提供标签,并为每个孔提供边框(最大和最小 xy 值来定义一个矩形),用于 Zoom To 选项。

前端 GUI 与 HTML 和 jQuery

在前端用到的工具包括标准 CSS/HTML,以及 jQuery、Smarty 和 Raphaël。jQuery 是很棒的 JavaScript 扩展工具,它能让您在保证质量的同时,快速编写代码。相关的项目 — jQuery UI — 可提供一组能快速使用的小部件和通用 GUI 元素。Smarty 是 PHP 驱动的模板引擎,能轻松分离代码和逻辑:此处不需要使用很多特性,只要从一开始就正确地使用 Smarty 就可以提升扩展性。最后,Raphaël 是 JavaScript 绘图库,它能轻松地在 HTML 元素上绘制几何形状。

GUI 的主要用途是呈现地图。此外,GUI 必须要能让读者执行基本的操作,如缩放和平移,并且能使执行一些任务变得简单。MapServer 是用来生成图像的,它需要按固定格式生成一个请求,发送给 MapServer,其中包含所需的图片宽度和高度,以及所需图片的空间坐标(边框)。这些坐标可以来自于 JavaScript — 从 Zoom To 特性 — 或者由您提供。通过以下方式请求一张地图:构造一个请求,发送给服务器,再直接加载到 <div> 元素。如清单 1 所示,在一行 JavaScript 代码中就可以完成。

清单 1. 地图加载到 div 元素
// Order of bbox is important - MinX, MinY, MaxX, MaxY
$('#map').css('background-image', 'url(' + ret.mapservPath + '?MAP=' + ret.mapFile + 
	'&SERVICE=WMS&LAYERS=' + layers + 
	'&MODE=MAP&REQUEST=GetMap&VERSION=1.1&SRS=epsg:32612&BBOX=' + MinX + ',' + MinY +
	',' + MaxX + ',' + MaxY + '&FORMAT=PNG&WIDTH=700&HEIGHT=600&cacheavoidance=' + 
	Math.random() + ')');

您可以使用 Raphaël 让用户在地图上绘制矩形,并可用于放大和缩小操作。这会提供一组对应于新缩放级的坐标像素。但是,您还需要知道对应于此项选择的几何坐标,这样您才能在每个方向上使用 “每个投影单元的像素 ”,如清单 2 所示。

清单 2. 从像素转换(投影)到被投影对象
var x1 = Math.min(lastMouseX, e.pageX);
var x2 = Math.max(lastMouseX, e.pageX);
var y1 = Math.min(lastMouseY, e.pageY);
var y2 = Math.max(lastMouseY, e.pageY);

// Adjust for map's position
var position = $('#map').position();
x1 -= position.left;
y1 -= position.top;
x2 -= position.left;
y2 -= position.top;

var pixelPerProjectionX = (currentMaxX - currentMinX) / 700;
var pixelPerProjectionY = (currentMaxY - currentMinY) / 600;
var corner1ProjectedX = currentMinX + x1 * pixelPerProjectionX;
var corner1ProjectedY = currentMaxY - y1 * pixelPerProjectionY;
var corner2ProjectedX = currentMinX + x2 * pixelPerProjectionX;
var corner2ProjectedY = currentMaxY - y2 * pixelPerProjectionY;

if (corner2ProjectedY < corner1ProjectedY) {
	// We're upside down!
	var temp = corner2ProjectedY;
	corner2ProjectedY = corner1ProjectedY;
	corner1ProjectedY = temp;
}

prepareMap(corner1ProjectedX, corner1ProjectedY, corner2ProjectedX, corner2ProjectedY);

这段代码提供了发送给 MapServer 的坐标:宽度和高度可以任意(本例中是硬编码)。还有需要考虑的事情:MapServer 会给您所要求的一切,毫无怨言。如果用户绘制一个狭长的方框,MapServer 会愉快地满足要求,根据所要求的宽度和高度返回一个矩形,形成一个扭曲、压扁或扁平的图像。您可以通过保证范围(每个投影长度单元对应多少像素)在 xy 方向上相同(或接近)来防止这种情况发生。清单 3 演示了此概念,但使用了一个简便方法:对定义方框的 XY 坐标,从 Max 减去 Min,从而使范围大致相当。

清单 3. 图像倾斜校正
var scaleX = (MaxX - MinX);
var scaleY = (MaxY - MinY);
// Prevent image warping due to oddly shaped requests - 
// fiddle with X until scale is matched
// This dataset's projection is UTM zone 12, so tolerance is in meters - 
// 5 meter skew tolerance
while (!equalWithinTolerance(scaleX, scaleY, 5)) {
	if (scaleX > scaleY) {
		// Bring X in
		MinX += 0.00005;
		MaxX -= 0.00005;
	}
	else {
		// Push X out
		MinX -= 0.00005;
		MaxX += 0.00005;
	}
	scaleX = (MaxX - MinX);
}

基本上,这些就是前端所要做的,已满足标准 web 应用程序设计( UI 元素、说明、标题,等等),还有专门行为。

使用 PHP 和 C++ 进行后端处理

MapServer 要提供地图图片,必须被告知要使用的层以及如何呈现。为此,要用到地图文件— 一个纯文本文件。幸运的是,您根本不需要手工构造地图文件。QGIS 包含一个插件来从加载项目中导出地图文件,让您通过 GUI 来设计地图,并手工修改。通常,地图文件导出功能运行良好,但有时会有一些常见错误:

  • 符号设置文件已经定义,名为 Symbols.txt,它不一定存在。假如您不使用自定义符号,就删除此行。对于字体设置文件 Fontset.txt 同样如此。
  • 可能存在一些颜色透明和轻微着色差异。您可以通过试错手工消除这些差异。
  • 文件路径输出经常会不正常,特别是上传数据到服务器的时候。搜索并替换到救援系统。

web 应用程序后端处理其余部分使用 PHP。Application 类处理所有请求,它只有两个操作:getHoleBoundsreportProblem。此函数演示了使用 OGR Simple Feature Library 来打开图形文件。例如,添加一个新图形/问题只需打开文件、在文件中打开层(记住有些数据格式支持一个文件中多层数据)、创建特性并添加。清单 4 演示了添加问题报告的完整函数。

清单 4. 完整的应用程序样例:Golf Course Problem Reporter
function reportProblem() {
	// Data validation
	foreach (array('action', 'problemType', 'description', 'x', 'y')
		as $requiredItem) {
		if (!isset($_REQUEST[$requiredItem])) 
			throw new Exception("Missing required item $requiredItem.");
	}
	if (!is_numeric($_REQUEST['x']))
		throw new Exception("Non-numeric X coordinate provided.");
	if (!is_numeric($_REQUEST['y']))
		throw new Exception("Non-numeric Y coordinate provided.");

	// Fire up OGR/GDAL drivers...
	OGRRegisterAll();

	// Open shapefile for writing (1 as second arg)
	$driver = NULL;
	$ds = OGROpen(Config::problemsShapefile, true, $driver);

	// Open the layer - shapefiles only have one layer per file (0)
	$layer = OGR_DS_GetLayer($ds, 0);
	$layerDef = OGR_L_GetLayerDefn($layer);

	// Create a feature and set field values
	$newFeature = OGR_F_Create($layerDef);
	OGR_F_SetFieldString($newFeature, 0, $_REQUEST['description']); 
	OGR_F_SetFieldString($newFeature, 1, $_REQUEST['problemType']); 

	// Create geometry and insert our point
	$newGeom = OGR_G_CreateGeometry(wkbPoint);
	OGR_G_SetPoint($newGeom, 0, $_REQUEST['x'], $_REQUEST['y'], 0);

	// Tie it together
	OGR_F_SetGeometry($newFeature, $newGeom);
	OGR_L_CreateFeature($layer, $newFeature);

	// Close it
	OGR_DS_Destroy($ds);
}

由于是样例,与问题图形文件的交互通过使用 C++ 创建命令行工具添加并显示问题完成。用 C++ 与图形文件交互使用与 PHP 相同的基本方法,但用的是更面向对象的方法。提供了对该工具的封装,它用的是 zenity(一个 GTK+ UI 工具),您可以使用它,而无需命令行开发经验(例如,高尔夫球场办公人员)。

结束语

理解并在您的软件中使用 GIS 概念和数据,并且在合适的地方实现它,对您的用户非常有用。在 web、桌面、移动设备和其他平台上工作的开发人员能从使用空间地理数据中获益。地图系统及相关话题正变得热门 — 尤其是越来越多的移动设备带有 GPS 位置信息。我们生活的方方面面正在以某种方式与我们的位置联系在一起 — 我们的软件也是这样。


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Open source
ArticleID=631666
ArticleTitle=使用 GDAL 在 Linux 上的应用程序中使用地理空间数据
publish-date=03102011