iOS地图

MapKit框架详细解析(六) —— 添加自定义图块(一)

2019-04-25  本文已影响6人  刀客传奇

版本记录

版本号 时间
V1.0 2019.04.25 星期四

前言

MapKit框架直接从您的应用界面显示地图或卫星图像,调出兴趣点,并确定地图坐标的地标信息。接下来几篇我们就一起看一下这个框架。感兴趣的看下面几篇文章。
1. MapKit框架详细解析(一) —— 基本概览(一)
2. MapKit框架详细解析(二) —— 基本使用简单示例(一)
3. MapKit框架详细解析(三) —— 基本使用简单示例(二)
4. MapKit框架详细解析(四) —— 一个叠加视图相关的简单示例(一)
5. MapKit框架详细解析(五) —— 一个叠加视图相关的简单示例(二)

开始

首先看下写作环境

Swift 4, iOS 11, Xcode 9

在这个MapKit教程中,您将学习如何创建和使用自定义地图图块(map tiles),使您的应用中的地图独一无二,从而在竞争中脱颖而出。 您还将了解如何设置自定义位置注释并动态呈现叠加层。

地图在现代应用程序中无处不在。 地图提供附近景点的位置,帮助用户在城镇或公园中导航,查找附近的朋友,跟踪旅程中的进度,或提供增强现实游戏的背景。

不幸的是,这意味着从应用到应用,大多数地图看起来都一样。

本教程介绍如何包含手绘地图(Hand-drawing),而不是编程生成的地图,如Pokemon Go中的地图。

手绘地图是一项重大的工作。 鉴于平面的大小,它只适用于定义明确,地理位置较小的区域。 如果您的地图有一个明确定义的区域,那么自定义地图可以为您的应用添彩。

MapQuest是一款有趣的冒险游戏的开始。这位英雄在现实生活中在纽约中央公园附近奔跑,但他们开始冒险,与怪物搏斗,并在另类现实中收集宝藏。它有一个可爱,幼稚的设计,让玩家感觉舒适,并表明游戏并不严肃。

游戏中有几个兴趣点Points of Interest (POI),用于定义玩家可以与游戏互动的位置。这些可以是任务,怪物,商店或其他游戏元素。在POI周围进入一个10米的区域会开始相遇。为了本教程的目的,实际的游戏玩法和地图渲染相比没那么重要。

项目中有两个繁重的文件:

游戏的主要视图是MKMapViewMapKit使用各种缩放级别的图块填充其视图,并提供有关地理要素,道路等的信息。

地图视图可以显示传统的道路地图或卫星图像。这对于在城市中航行非常有用,但对于想象你在中世纪的世界中进行冒险却毫无用处。但是,MapKit可让您提供自己的地图艺术,以自定义美学和呈现信息。

地图视图由许多在您平移视图时动态加载的图块组成。这些图块是256像素乘256像素,并且排列在与墨卡托地图投影相对应的网格中。

要查看正在运行的地图,请构建并运行该应用。

哇! 多么美丽的小镇。 游戏的主要界面是一个地区,这意味着如果不进入访问中央公园就没有任何东西可以看和做。


Testing Out Location

与其他教程相反,MapQuest是一个功能强大的应用程序! 但是,除非你住在纽约市,否则你有点不走运。 幸运的是,XCode至少有两种模拟位置的方法。

1. Simulating a Location

应用程序仍在iPhone模拟器中运行,设置用户的位置。

转到Debug \ Location \ Custom Location ...

将纬度设置为40.767769,将经度设置为-73.971870。 这将激活蓝色用户位置点并将地图聚焦在中央公园动物园上。 这是一个野生妖精生活的地方;你将被迫与它战斗,然后收集它的宝藏。

殴打无助的妖精后,你将被放置在动物园里(注意蓝点)。

2. Simulating an Adventure

静态位置对于测试许多基于位置的应用程序非常有用。 但是,这个游戏需要访问多个位置作为冒险的一部分。 模拟器可以模拟跑步,骑车和驾驶的不断变化的位置。 这些预先包含的旅行是为了Cupertino,但MapQuest只在纽约遇到过。

诸如此类的场合要求使用GPX文件(GPS交换格式)进行模拟定位。 此文件指定了许多航点,模拟器将在它们之间插入路线。

创建此文件超出了本教程的范围,但示例项目包含一个供您使用的测试GPX文件。

通过选择Product \ Scheme \ Edit Scheme ...XCode中打开scheme编辑器。

选择左窗格中的Run,然后选择右侧的Options选项卡。 在Core Location部分中,单击Allow Location Simulation的复选标记。 在Default Location下拉列表中,选择Game Test

这意味着该应用程序将模拟在Game Test.gpx中指定的航点之间移动。

建立并运行。

模拟器现在将从第五大道地铁步行到中央公园动物园,在那里你将不得不再次与妖精战斗。在那之后,你可以去你最喜欢的水果公司的旗舰店买一把升级的剑。一旦循环完成,冒险将重新开始。


Replace the Tiles with OpenStreetMap

OpenStreetMap是一个社区支持的地图数据开放数据库。该数据可用于生成Apple Maps使用的相同类型的地图图块。Open Street Map社区提供的不仅仅是基本的道路地图,例如地形,自行车和艺术渲染的专用地图。

注意:Open Street Map tile policy对数据使用,归因和API访问有严格要求。这适用于教程,但在使用生产应用程序中的图块之前检查合规性。

1. Creating a New Overlay

替换地图图块需要使用MKTileOverlay在默认Apple Maps上显示新图块。

打开MapViewController.swift,并将setupTileRenderer()替换为以下内容:

func setupTileRenderer() {
  // 1
  let template = "https://tile.openstreetmap.org/{z}/{x}/{y}.png"

  // 2
  let overlay = MKTileOverlay(urlTemplate: template)

  // 3
  overlay.canReplaceMapContent = true

  // 4
  mapView.add(overlay, level: .aboveLabels)

  //5
  tileRenderer = MKTileOverlayRenderer(tileOverlay: overlay)
}

默认情况下,MKTileOverlay支持通过模板化的URL加载切片以获取切片路径。

在显示图块之前,必须使用MKMapView设置图块渲染器,以便绘制图块。

viewDidLoad()的底部添加以下行:

mapView.delegate = self

这将MapViewController设置为其mapView的代理。

接下来,在MapView Delegate扩展中,添加以下方法:

func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
  return tileRenderer
}

叠加渲染器告诉地图视图如何绘制叠加层。 图块渲染器是用于加载和绘制地图图块的特殊子类。

就这些! 构建并运行以查看标准Apple地图替换为Open Street Map

此时,您可以真正看到open source地图和Apple Maps之间的区别!


Dividing up the Earth

图块叠加层的神奇之处在于能够将图块路径转换为特定图像资源。 图块的路径由坐标表示:x,y和z。 x和y对应于地图表面上的索引,0,0是左上方的图块。 z坐标用于缩放级别,并确定构成整个地图的图块数量。

在缩放级别0,整个世界由1×1网格表示,需要一个图块:

在缩放级别1,整个世界被划分为2×2网格。 这需要四个图块:

在级别2,行数和列数再次翻倍,需要16个图块:

此模式继续,每个缩放级别的细节级别和平铺数量翻两番。每个缩放级别需要2^2 * z个图块,一直到缩放级别19需要274,877,906,944个图块!


Creating Custom Tiles

由于地图视图设置为遵循用户的位置,因此默认缩放级别设置为16,这显示了良好的细节级别,以便为用户提供他们所在位置的上下文。缩放等级16将需要4,294,967,296个整个平面的图块!手绘这些图块需要一辈子的时间。

拥有像城镇或公园这样较小的有界区域可以创建自定义艺术作品。对于更大范围的位置,可以从源数据以程序方式生成图块。

由于此游戏的图块已预先渲染并包含在资源包中,因此您只需加载它们即可。遗憾的是,通用URL模板是不够的,因为如果渲染器请求应用程序中未包含的数十亿个切片之一,则最好优雅地失败。

为此,您需要一个自定义的MKTileOverlay子类。打开AdventureMapOverlay.swift并添加以下代码:

class AdventureMapOverlay: MKTileOverlay {

  override func url(forTilePath path: MKTileOverlayPath) -> URL {
    let tileUrl = "https://tile.openstreetmap.org/\(path.z)/\(path.x)/\(path.y).png"
    return URL(string: tileUrl)!
  }
}

这将设置子类,并使用带有专用URL生成器的模板URL替换基本类。

暂时保留Open Street Map图块以测试自定义叠加层。

打开MapViewController.swift,并将setupTileRenderer()替换为以下内容:

func setupTileRenderer() {
  let overlay = AdventureMapOverlay()

  overlay.canReplaceMapContent = true
  mapView.add(overlay, level: .aboveLabels)
  tileRenderer = MKTileOverlayRenderer(tileOverlay: overlay)
}

这在自定义子类中交换。

建立并再次运行。 如果一切顺利,游戏应该与以前完全一样。 好极了!

1. Loading the Prerendered Tiles

有趣的来了。 打开AdventureMapOverlay.swift,并用以下内容替换url(forTilePath :)

override func url(forTilePath path: MKTileOverlayPath) -> URL {

  // 1
  let tilePath = Bundle.main.url(
    forResource: "\(path.y)",
    withExtension: "png",
    subdirectory: "tiles/\(path.z)/\(path.x)",
    localization: nil)

  guard let tile = tilePath else {

    // 2
    return Bundle.main.url(
      forResource: "parchment",
      withExtension: "png",
      subdirectory: "tiles",
      localization: nil)!
  }
  return tile
}

此代码加载游戏的自定义图块。

建立并再次运行。 现在显示自定义地图。

尝试放大和缩小以查看不同级别的细节。

2. Bounding the Zoom Level

不要放大或缩小太多,否则你将完全丢失地图。

幸运的是,这是一个简单的修复。 打开MapViewController.swift,将以下行添加到setupTileRenderer()的底部:

overlay.minimumZ = 13
overlay.maximumZ = 16

这会通知mapView只在这些缩放级别之间提供图块。 将缩放更改为缩放应用程序中提供的平铺图像。 没有提供额外的细节,但至少现在显示的图像与比例相匹配。

后记

本篇主要介绍了添加自定义图块,感兴趣的给个赞或者关注~~~

上一篇下一篇

猜你喜欢

热点阅读