如何从数据库中的地图上绘制瓷砖?

Posted

技术标签:

【中文标题】如何从数据库中的地图上绘制瓷砖?【英文标题】:How to draw tiles on Map from Database? 【发布时间】:2016-11-10 22:31:43 【问题描述】:

我正在使用 Swift 3.0 开发基于 GIS 的 ios 应用程序。我想在地图上绘制图块,图块图像存储在 SQLite 数据库中。

我的问题是如何从数据库中检索平铺图像并在地图上绘制这些图像,因为数据库包含列缩放(值将是 12、13 等)、平铺行(值将是 4122、3413 等) , tile_column (值将是 4122、3413 等)和数据,但我得到以千为单位的缩放级别值,并且我在 iOS 应用程序中得到纬度和经度值,所以我需要将这些值转换为匹配数据库中的值。我找到了一种将缩放级别转换为 1 到 18 比例的方法,但我不知道是否可以使用纬度和经度来匹配 tile_row 和 tile_column 值。

另外请验证我将缩放级别转换为 1 到 18(类似于谷歌地图缩放级别)的代码是否正确:

let zoomLevel = Int(log2(360 / MKCoordinateRegionForMapRect(mapRect).span.longitudeDelta))

谢谢。

【问题讨论】:

【参考方案1】:

在 IOS(和大多数地图系统)中,平铺图像存储在目录结构中,其中每个缩放级别都是以缩放级别编号命名的父目录。在该目录下,有一个或多个目录以下面瓦片的纵向瓦片编号命名 - 例如,在缩放级别 10 下,有 1024 个瓦片,您的目录可能是 750、751、752 和 753,如果那是他们的图像相对下降。在每个经度(x 坐标)目录下是该 y 坐标的图像(256 X 256 像素),每个都以 x 平铺坐标命名,在缩放级别为 10 时也是 1024 个。

要查找您在这些范围内的位置,请使用 MKMapPointForCoordinate(CLLocationCoordinate2D),它会在完全缩小时为您提供某个位置的纬度 (y) 和经度 (x) 地图点。要获取经度 (y) 瓷砖编号,请使用:

Int((pow(2.0, Double(z)) as Double) * point.y / 268435456.0) 

...其中大数字是缩放级别 0 时 x 轴上的总点数(2^20 块 * 256 像素/块)。这样,如果 point.x 是大数的 1/3,则图像是 1/3 到 1024 的瓦片,表示该数字落入的 256 像素间隔(即瓦片)的整数就是名称目录。

纬度 (y) 地图点和图块编号的计算方法类似,该编号是图像文件的名称。因此,在缩放级别 10 时,沿 x 轴的 1024 个图块中的 752 个和沿 y 轴的 1024 个中的 486 个图块的图像将位于文件中:

...Documents/Maps/yourDirectory/10/752/486.png

...如果您将整个地图目录命名为 Maps 以及这组图块的特定目录为 yourDirectory。当您使用覆盖时,您将使用此目录信息以及其余设置来实例化 MKTileOverlay 对象。请注意,偏移量是从左下角开始的,除非您指定它们是相反的,因为它们正在考虑 x 和 y 轴(提醒您使用 CoreGraphics 作为 UIImage 吗?)。

最后,在给定要为其捕获快照的区域的两个角点的情况下,我计算缩放级别的方法如下:

    let position1 = MKMapPointForCoordinate(bottomRight)
    let position2 = MKMapPointForCoordinate(topLeft)
    let xPosition1 = position1.x / Setting.shared.mapScale
    let xPosition2 = position2.x / Setting.shared.mapScale
    let yPosition1 = position1.y / Setting.shared.mapScale
    let yPosition2 = position2.y / Setting.shared.mapScale

    let relativeSpanX = xPosition1 - xPosition2    // X distance between points relative to size of full map
    let relativeSpanY = yPosition1 - yPosition2    // Y distance between points relative to size of full map
    let spanForZoom = max(relativeSpanX, relativeSpanY)

    startingZoom = max(10, Int(log2(1.0 / spanForZoom)) - 1)

这将为您提供适合包含两个点的区域大小的图块的缩放级别,但请注意,该大小的任何标准图块(或小于完整地图的任何大小)都可能包含这两个点,具体取决于在它们相对于网格的位置上。例如,如果它们跨越本初子午线,则在缩放级别 1 的第一步到 4 个图块将它们分开,因此您可能需要 2 - 4 个起始缩放大小的图块来获得两者。理想情况下,编写一个函数来告诉您包含 CLLocationCoordinate2D 的图块编号(x 和 y),因为这在您选择、下载和收集图块时非常方便。

虽然您可以使用几何方法来计算纵向/y 轴值,但 MKMapPointForCoordinate() 对于纬度/y 轴计算是必不可少的,因为当您向北移动时,墨卡托地图是非线性的或南,该功能会为您处理。

这应该可以帮助您入门,但这是一个挑剔的过程 - 需要关注的一件事是布局始终从左下角开始;收集和标记图块时很容易混淆。

我使用以下函数计算给定缩放级别下点所在的图块的 x 和 y 坐标:

func getTileCoordinates(location: CLLocationCoordinate2D, z: Int) -> (x: Int, y: Int)

    let point = MKMapPointForCoordinate(location)
    let locationX = Int((pow(2.0, Double(z)) as Double) * point.x / 268435456.0)
    let locationY = Int((pow(2.0, Double(z)) as Double) * point.y / 268435456.0)

    return (locationX, locationY)

...再次,其中 268435456.0 是缩放级别 20 地图中沿 x 或 y 轴的像素总数。请注意,所有这些都是针对 Apple MapKit 地图和显示它们的功能。

【讨论】:

顺便说一句,我在缩放计算中添加了最大值,因为我不想缩小到 10 级以上,因为我使用地图来支持导航,而你负担不起更多6 或 7 级缩放(7 级需要超过 5,000 个图块!)。负 1 是为了获得一点喘息的空间,并增加我将两个点都放在 1 或 2 个瓷砖上的机会。 感谢您的回答。您能否解释一下变量或如何获取您正在使用的这些变量,例如 z、point、mapScale。您添加了获取缩放级别的代码,但我不确定我的缩放级别是多少,spanForZoom 是我的缩放级别? bottomRight 和 topLeft 位于我想要地图的区域的角落。我通过在我的航点中的位置之间找到最大和最小纬度和经度并使用它们来定义两个点来获得这些:bottomRight = (min, max) 和 topLeft = (max, min)。从那里开始缩放计算应该完成其余的工作以获取缩放。这些计算中的 .x 和 .y 值是系统返回的 MKMapPoints 值的属性,对应于图形上的 x 和 y 点。 [见下一条评论] 我使用第一个方程式将这些点转换为图块编号,该方程式仅使用图表中的点总数 (268435456) 和您正在使用的缩放级别的图块数 (pow( 2.0, Double(z))) 以获取沿 x 和 y 轴的图块的编号。当我下拉并将多个缩放级别的地图图块存储到目录结构中时,我将 z 的值从 startZoom 更改为更高的值。 [见下一条评论] 有关这方面的一些背景知识,我推荐nshipster.com/mktileoverlay-mkmapsnapshotter-mkdirections 上的文章以及Apple 的MKTileOverlayPath 和MKTileOverlay 文档。我希望这一切都会有所帮助,我承认我花了一些时间来消化这些和其他资源并尝试让它们全部发挥作用。

以上是关于如何从数据库中的地图上绘制瓷砖?的主要内容,如果未能解决你的问题,请参考以下文章

如何实现mapbox-gl的tile源码

Android应用谷歌地图显示灰色瓷砖而不是地图!

如何在 react-native 中的 mapbox 地图上绘制导航线?

如何循环这个 PL/SQL 语句,以便它在我的 Google 地图上绘制多个标记?

瓷砖和 MouseState MonoGame 的更新时间很慢

用OpenGL在大范围内绘制世界地图[WGS84]