什么是读取、表示和渲染地图数据的最佳方式?

13
我对编写一款简单的导航应用程序很感兴趣。在搜索免费地图数据后,我选择了美国人口普查局TIGER2007年线/形状文件地图数据。该数据分为各个县的zip文件,我已经下载了我所在地区的单个县的地图数据。
最佳读取此地图数据的方法是什么?
我应该:
- 读入这些文件 - 解析它们 - 正则表达式还是某个可以解析这些Shapefile的库? - 将数据加载到我的应用程序中 - 我应该直接将点加载到内存中的某个数据结构中?使用小型数据库?一旦关闭地图数据的应用程序,我就不需要持久性。用户可以重新加载Shapefile。
读取Shapefile数据后,最佳渲染地图的方法是什么?
理想情况下,我希望能够读入县级地图数据Shapefile并将所有折线呈现到屏幕上,并允许旋转和缩放。
我应该:
  • 如何将经纬度点转换为屏幕坐标? - 据我所知,Shapefile使用经度和纬度来表示其点。因此,我必须将它们转换为屏幕坐标以显示地图要素。
  • 以一种可以轻松旋转和缩放整个地图的方式呈现地图数据(用于道路、边界等的多条折线)?
  • 将我的整个地图作为一系列“瓦片”呈现,只呈现在视野范围内的要素/线条?

TIGER数据呈现为显示地图的示例:
alt text

任何有经验和见解的人都可以告诉我最佳方法是如何读取这些文件,我应该如何在程序中表示它们(数据库、内存数据结构),以及我应该如何渲染(包括旋转/缩放)屏幕上的地图数据。

编辑:为了澄清,我不想使用任何Google或Yahoo地图API。同样,我不想使用OpenStreetMap。我正在寻找一种比利用那些API/程序更为自主创新的方法。这将是一个 桌面 应用程序。


4
好的,已经两年了。 应用程序在哪里? - Adam Davis
11个回答

25
首先,我建议您使用2008 TIGER files
其次,正如其他人指出的那样,现在已经有很多项目可以读取、解释、转换和使用这些数据。构建自己的解析器来处理这些数据几乎是微不足道的,所以除非您打算将整个项目作为一个整体使用,否则没有必要查看另一个项目的代码并尝试提取所需内容。
如果您想从较低的层次开始
解析
建立自己的TIGER解析器(相对容易-只需一个线段的数据库),并在其上构建一个简单的渲染器(线条、多边形、字母/名称),也将相对容易。在渲染阶段,您需要查看各种地图投影类型。最常用的(因此对用户最熟悉)是墨卡托投影-它相对简单且快速。您可能想尝试支持其他投影方式。
这将提供一些“乐趣”,让您了解如何投影地图,以及如何反转该投影(例如,当用户在地图上点击时,您希望看到他们点击的纬度/经度-需要反转当前的投影方程)。
渲染
当我开发我的渲染器时,我决定基于固定大小(嵌入式设备)和固定放大倍数来设置窗口。这意味着我可以将地图居中于某个纬度/经度,并且通过中心像素=中心纬度/经度在给定的放大倍数下,根据墨卡托投影计算出每个纬度/经度所代表的像素,反之亦然。
有些程序则允许窗口大小变化,而不是使用放大倍数和固定点,它们使用两个固定点(通常是定义窗口的矩形的左上角和右下角)。在这种情况下,确定像素到纬度/经度的转换变得非常简单 - 只需要进行一些插值计算。旋转和缩放会使这个转换函数稍微复杂一些,但不应该过于复杂 - 它仍然是一个带有插值的矩形窗口,但窗口的角不需要与北方保持任何特定的方向。这会增加一些特殊情况(例如,您可以将地图翻转并从地球内部查看),但这些并不繁琐,并且可以在工作过程中处理。
一旦你完成了经纬度到像素的转换,渲染线条和多边形就相对简单,除了一些常见的图形问题(如线条或多边形的边缘不恰当地重叠、抗锯齿等)。但是渲染一个基本的丑陋地图,就像许多开源渲染器所做的那样,相对直接。
你还可以玩一下距离和大圆计算——例如,一个很好的经验法则是,在赤道上每一度的纬度或经度大约是111.1公里,但是当你靠近两极时,一个会发生变化,而另一个仍然保持在111.1公里。
然而,你如何存储和引用数据,取决于你打算用它做什么。如果你想在人口统计和路径规划方面使用相同的数据库结构,会遇到很多困难问题——对于一个给定的数据库结构和索引,其中一个可能快速,而另一个可能很慢。
对于小型地图渲染项目,使用邮政编码并仅加载附近的邮政编码是可行的,但如果你需要跨国家的路线,就需要不同的结构。一些实现方式有“叠加”数据库,只包含主要道路,并将路线对齐到叠加层(或通过多个叠加层——本地、城市、县、州、国家)。这样可以实现快速但有时效率低下的路径规划。
铺砖
铺砖你的地图其实并不容易。在较低的放大倍数下,你可以渲染整个地图然后切割它。在较高的放大倍数下,你无法一次性渲染整个地图(由于内存/空间限制),所以你必须将其切割。
在瓷砖边界处切割线条,以便能够渲染单独的瓷砖,结果并不完美 - 通常的做法是将线条渲染超出瓷砖边界(或者至少保留线条末端的数据,尽管一旦发现已经超出边缘,渲染就会停止) - 这样可以减少线条在跨越瓷砖时出现的不完全匹配的错误。
当你解决这个问题时,你会明白我在说什么。
同样,找到进入给定瓷砖的数据也不是一件简单的事情 - 一条线可能两端都在给定的瓷砖之外,但是穿过了该瓷砖。你需要查阅有关图形的书籍(Michael Abrash的书是权威参考资料,现在在上述链接中可以免费获取)。虽然它主要讲解游戏,但窗口化、裁剪、多边形边缘、碰撞等概念在这里同样适用。
然而,你可能想要在更高的水平上进行游戏。
一旦你完成了上述任务(可以通过改编现有项目或自己完成),你可能想要尝试其他场景和算法。
逆地理编码相对容易。输入纬度/经度(或在地图上点击)即可获取最近的地址。这将教会你如何解释TIGER数据中的线段上的地址。
基本地理编码是一个困难的问题。编写一个地址解析器是一个有用且有趣的项目,然后使用TIGER数据将其转换为纬度/经度是非平凡的,但非常有趣。从简单和小规模开始,要求精确的名称和格式匹配,然后开始研究“相似”匹配和音标匹配。在这个领域有很多研究 - 可以参考搜索引擎项目以获得一些帮助。
在两点之间找到最短路径是一个非平凡的问题。有许多算法可以实现这一点,其中大部分都有专利。我建议如果你尝试这个,可以使用自己设计的简单算法,然后进行一些研究,将你的设计与现有技术进行比较。如果你对图论感兴趣,这将是非常有趣的。
跟着路径并提前给出指示并不像一开始看起来那么容易。给定一组带有相关的纬度/经度对的指示,使用外部输入(GPS或模拟GPS)“跟随”路线,并开发一个算法,在用户接近每个真实交叉口时给出指示。请注意,由于道路弯曲等原因,纬度/经度对的数量比指示多,您需要检测行进方向等。有很多你在实施之前看不到的特殊情况。
兴趣点搜索。这个很有趣 - 你需要找到当前位置,并找到离起点一定距离(直线距离或更难 - 驾驶距离)内的所有兴趣点(不是TIGER的一部分,可以自己制作或获取其他来源)。这个有趣之处在于你必须将POI数据库转换为易于在此情况下搜索的格式。你不能花时间遍历数百万条目,进行距离计算(sqrt(x^2 + y^2)),然后返回结果。你需要有一些方法或算法来首先减少数据量。
旅行推销员。多目的地的路线规划。只是常规路线规划的更难版本。

你可以在这里找到许多项目和信息来源的链接 here

祝你好运,请无论你做什么,无论多么初级或丑陋,都要发布出来,这样其他人才能受益!

-Adam


哇,这是一个非常出色的答案。感谢您付出的努力。 - ScottS

14

SharpMap是一个针对WinForms和ASP.NET的开源.NET 2.0地图引擎。它可能提供您需要的所有功能。它处理包括ESRI shapefiles在内的大多数常见的GIS矢量和栅格数据格式。


SharpMap很棒,但如果您使用未修改的来自shp/dbf的数据集,那么如果您有一个非常大的数据集,您可能会遇到性能问题。 - Jonke

7
解决方案如下:
  • 使用像MapServer、GeoServer、Degree(开源)这样的地理空间服务器。

它们可以读取和提供shapefile文件(以及许多其他格式)。例如,当安装了GeoServer时,可以作为演示从美国人口普查局TIGER shapefile文件中提供数据。

  • 使用像OpenLayers这样的JavaScript制图库(请参见此链接中的示例)。

网络上有很多使用此解决方案的示例。


我应该指出这将是一个桌面应用程序。那会如何影响你的回答? - mmcdole
大多数这些“服务器”可以在本地运行,实际上只需运行几个参数即可输出所需的图像,然后退出。不过,尝试让几个软件协同工作仍然需要更多的工作。 - Adam Davis

5

有趣的问题。这是我的做法。

我收集所需的任何几何图形,以任何格式提供。我一直在从美国地质调查局提取数据,因此这相当于一堆:

然后我编写了一个程序,将这些形状定义“编译”成有效渲染的形式。这意味着进行任何必要的投影和数据格式转换,以有效地显示数据。一些细节:

  • 对于2D应用程序,您可以使用任何投影方式:地图投影
  • 对于3D,您需要将这些纬度/经度转换为3D坐标。以下是如何进行此操作的一些数学方法:从球面坐标到普通矩形坐标的转换
  • 将所有基元分解成四叉树/八叉树(2D/3D)。该树中的叶节点包含引用所有与该叶节点的轴对齐边界框相交的几何体。(这意味着一个几何体可能会被引用多次。)
  • 然后将几何体分成顶点表和绘图命令表。这是OpenGL的理想格式。可以通过使用顶点缓冲区(Vertex Buffer Objects)发出命令,使用glDrawArrays
  • 使用一般的访问者模式来遍历四叉树/八叉树。遍历涉及测试访问者是否与树的给定节点相交,直到遇到叶节点。访问者包括:绘图、碰撞检测和选择。(因为树叶可以包含对几何体的重复引用,所以行走者标记节点为已访问并在此后忽略它们。这些标记必须在进行下一次遍历之前重置或更新。)
  • 使用空间分区系统(其中之一是树)和绘图效率高的表示对于实现高帧速率至关重要。我发现,在这些类型的应用程序中,您希望帧速率尽可能高,最低为20 fps。更不用说大量性能将为您提供创造更美观的地图的机会。(我的地图远非好看,但总有一天会变得好看。)
  • 空间分区通过减少发送到处理器的绘图命令数量来帮助渲染性能。然而,当用户实际上想要查看整个数据集(例如航拍视图)时,可能会出现问题。在这种情况下,需要一个级别细节控制系统。由于我的应用程序涉及街道,因此我优先考虑高速公路和较大的道路。我的绘图代码知道在我的帧速率下可以绘制多少个基元。基元也按此优先级排序。我只绘制前x个项目,其中x是我可以以所需帧速率绘制的基元数。

其余部分是相机控制和动画,用于显示任何数据。

以下是我现有实现的一些示例:

图片 http://seabusmap.com/assets/Picture%205.png 图片 http://seabusmap.com/assets/Picture%207.png


使用空间划分系统(其中一种树)和绘图高效的表示方法,能否详细说明一下?我曾尝试使用OpenGL来渲染一个非常大的数据集(欧洲所有道路),但我卡在了某个地方,因为我似乎需要将所有线条简化为更简单的。 - Jonke
@Simucal 这个问题很有趣,因为处理数据的最佳方式完全取决于您对最终应用程序的期望。它是3D显示还是2D?您是否使用高度图显示地理数据?查看者是否需要缩小以包括整个数据集?有很多问题需要考虑... - Frank Krueger
@Jonke 空间划分用于限制需要处理的数量 - 对于绘图来说,这是视图体积剪裁。现在,如果用户尝试使用/查看整个数据集,则必须施加某种LOD控制。 - Frank Krueger
@Frank Krueger,2d,没有高度图。只需考虑道路/边界数据的最基本绘制(包括缩放/缩放和旋转)。我将一次加载最多几个县,因此任何给定时间都不会有太多数据。 - mmcdole
@Simucal,那么请遵从我的建议。;-) 这似乎有些过头了,但是我认为你的地图绘制代码值得你投入每一个可以使用的优化策略。请查阅游戏编程文献以获取更多信息。 - Frank Krueger

2
本地存储虎数据,我会选择使用 Postgresqlpostgis 工具。
它们有一个令人印象深刻的工具集,对于您来说,Tiger Geocoder 提供了一种很好的导入和使用虎数据的方式。
您需要查看与 postgis 交互的工具,最可能是某种 mapserver
来自 http://postgis.refractions.net/documentation/
现在有几个开源工具可以与PostGIS一起使用。uDig项目正在开发一个完整的读/写桌面环境,可以直接与PostGIS一起使用。对于互联网地图,明尼苏达大学Mapserver可以使用PostGIS作为数据源。GeoTools Java GIS工具包支持PostGIS,GeoServer Web Feature Server也支持PostGIS。GRASS支持PostGIS作为数据源。JUMP Java桌面GIS查看器有一个简单的插件用于读取PostGIS数据,QGIS桌面有良好的PostGIS支持。可以使用OGR C ++库和命令行工具(当然还有捆绑的Shape文件转储程序)将PostGIS数据导出到多种输出GIS格式。当然,任何可以使用PostgreSQL的语言都可以使用PostGIS--列表包括Perl、PHP、Python、TCL、C、C++、Java、C#等等。

编辑:尽管Mapserver在其名称中有SERVER一词,但它在桌面环境中也可用。


2
尽管您已经决定使用TIGER数据,但您可能会对OSM(Open Street Map)感兴趣,因为OSM中已经包含了完整的TIGER数据,并且还增加了用户贡献的数据。如果您坚持使用TIGER格式,您的应用程序将对国际用户无用,而使用OSM,您可以一次性获得TIGER和其他所有内容。
OSM是一个开放项目,提供协作编辑的免费世界地图。您可以获取所有这些数据,以良好结构化的XML形式,可以查询某个区域,也可以下载整个世界的大文件。
有一些可用于各种编程语言的OSM地图渲染器,其中大多数是开源的,但仍有许多工作要做。
还有一个可用的OSM路由服务。它具有Web界面,也可以通过Web服务API进行查询。同样,它还没有全部完成。用户肯定可以在其上构建桌面或移动路由应用程序。
即使你决定不参与那个项目,你仍然可以从中获得很多灵感。只需要查看项目维基和涉及的各种软件项目的来源(你会在维基内找到它们的链接)。

1

如果您不介意支付解决方案的费用,Safe Software 生产了名为 FME 的产品。这个工具可以帮助您将数据从任何格式翻译成几乎任何其他格式,包括 Google Earth 格式 KML 或将其呈现为 JPEG(或一系列 JPEG)。转换数据后,您可以使用他们的 API 将 Google 地球嵌入应用程序中,或者只显示平铺图像。

另外,FME 是一个非常强大的平台,因此在进行翻译时,您可以添加或删除不必要的数据部分。如果有多个源,请合并它们。转换坐标(我不记得 Google 地球究竟使用的是什么坐标系)。将备份存储在数据库中。但是,如果您愿意花费一些钱,您应该考虑这个。

您还可以创建旗帜(就像您的样例地图),其中包含位置(放置位置)和有关该位置的其他数据/评论。这些旗帜有各种形状和大小。


1

相对于墨卡托或其他投影方式的简化是假设纬度和经度的转换因子是恒定的。将纬度的度数乘以69.172英里;对于经度,选择地图区域的中间纬度并乘以(180-经度)乘以cosine(中间纬度)*69.172。一旦转换为英里,您可以使用另一组转换来获得屏幕坐标。

这是我在1979年使用的方法。

我的每度英里数来源。


1

您也可以使用微软的可视化地球地图应用程序和API,或者使用谷歌的API。我一直在商业上使用ESRI产品进行编程,没有太多涉及开放API。

另外,您可能还想看看Maker! 和Finder!。它们是相对较新的程序,但我认为它们是免费的。可能会在嵌入数据方面存在一些限制。Maker 可以在此处找到。

问题是非商业规模下的空间处理还比较新。


1

当我回答这个问题时,它被标记为:

"在 .Net 中呈现 Shapefile(地图数据)中的折线的最佳方法是什么?"

现在是一个不同的问题,但我保留我的答案给原始问题。

我写了一个 .net 版本,可以使用纯 GDI+ 在 c# 中绘制矢量数据(例如来自 shp 文件的几何形状)。这很有趣。

原因是我们需要处理具有大量附加信息的不同版本的几何形状和属性,因此我们不能使用商业地图组件或开源组件。

做这个事情的主要问题是建立视口,并将 WGIS84 坐标转换/变换为缩小比例和 GDI+ x、y 坐标,如果您甚至需要重新投影,则等待投影。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接