MapKit框架详细解析(十七) —— 基于MapKit使用Indoor Maps来绘制建筑物内部的地图的简单示例(一)

版本记录

版本号 时间
V1.0 2020.10.12 星期一

前言

MapKit框架直接从您的应用界面显示地图或卫星图像,调出兴趣点,并确定地图坐标的地标信息。接下来几篇我们就一起看一下这个框架。感兴趣的看下面几篇文章。
1. MapKit框架详细解析(一) —— 基本概览(一)
2. MapKit框架详细解析(二) —— 基本使用简单示例(一)
3. MapKit框架详细解析(三) —— 基本使用简单示例(二)
4. MapKit框架详细解析(四) —— 一个叠加视图相关的简单示例(一)
5. MapKit框架详细解析(五) —— 一个叠加视图相关的简单示例(二)
6. MapKit框架详细解析(六) —— 添加自定义图块(一)
7. MapKit框架详细解析(七) —— 添加自定义图块(二)
8. MapKit框架详细解析(八) —— 添加自定义图块(三)
9. MapKit框架详细解析(九) —— 地图特定区域放大和创建自定义地图annotations(一)
10. MapKit框架详细解析(十) —— 地图特定区域放大和创建自定义地图annotations(二)
11. MapKit框架详细解析(十一) —— 自定义MapKit Tiles(一)
12. MapKit框架详细解析(十二) —— 自定义MapKit Tiles(二)
13. MapKit框架详细解析(十三) —— MapKit Overlay Views(一)
14. MapKit框架详细解析(十四) —— MapKit Overlay Views(二)
15. MapKit框架详细解析(十五) —— 基于MapKit和Core Location的Routing(一)
16. MapKit框架详细解析(十六) —— 基于MapKit和Core Location的Routing(二)

开始

首先看下主要内容:

在本MapKit教程中,您将学习如何使用Indoor Maps来绘制建筑物内部的地图,在不同stories之间切换以及查找建筑物内部的位置。内容来自翻译。

接着看下写作环境:

Swift 5, iOS 14, Xcode 12

苹果公司在iOS 13中引入Indoor Maps to MapKit。该程序提供了对物理结构内部进行地图绘制的工具,从而使用户可以从内部导航建筑物。

在本教程中,您将学习如何使用Indoor MapsRazeWare办公室的位置添加到地图中。 在此过程中,您将学习:

  • Indoor Mapping Data Format (IMDF)数据解析为模型。
  • 在地图上从GeoJSON绘制几何。
  • 样式图几何(Style map geometry)
  • 在结构的不同级别之间切换。
  • 获取室内位置。

注意:本高级教程假定您可以轻松地使用SwiftXcode中构建iOS应用。 本教程使用MapKit。 如果您不熟悉MapKit,请先阅读MapKit Tutorial: Getting Started。

打开启动项目。 进行构建并运行以查看您的工作方式。

对于你们中观察较少的人,它是一张地图。

MapKit框架详细解析(十七) —— 基于MapKit使用Indoor Maps来绘制建筑物内部的地图的简单示例(一)_第1张图片

入门项目已经包含您要使用的用户界面,一些模型文件和IMDF存档。现在,它不是一个鼓舞人心的应用程序;它肯定不会在应用程序审查中超越Apple Genius。但是,您将改变这一点!


Understanding Indoor Maps

在开始编写代码之前,重要的是要知道您正在使用哪种数据。在本教程中,您将使用两个标准:GeoJSONIMDF

1. What Is GeoJSON?

GeoJSON是一种用于表示地理数据结构的格式。顾名思义,GeoJSON基于JSON格式。互联网工程任务组(Internet Engineering Task Force (IETF))于2015年发布了该文档,以标准化程序员对地理数据进行建模的方式。

GeoJSON支持使用纬度和经度对定义的一系列不同的几何类型。您可以组合经纬度对以形成更复杂的结构。 GeoJSON对象可以表示:

  • Geometry: A region of space
  • Feature: A spatially-bound entity
  • FeatureCollection: A list of Features
MapKit框架详细解析(十七) —— 基于MapKit使用Indoor Maps来绘制建筑物内部的地图的简单示例(一)_第2张图片

看一下上面地图中概述的建筑结构。 结构几何图形中的每个点都有一个纬度和经度坐标,就像您在笛卡尔坐标系Cartesian coordinate system中的x-y位置一样。 每个点的标记为16,代表绘制顺序。 左侧的列显示每个点的实际经纬度坐标。

此功能的GeoJSON表示形式如下:

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {
        "stroke": "#101889",
        "stroke-width": 2,
        "stroke-opacity": 1,
        "fill": "#98a1e6",
        "fill-opacity": 0.5
      },
      "geometry": {
        "type": "Polygon",
        "coordinates": [
          [
            [
              -121.80389642715454,
              37.33966009140741
            ],
            [
              -121.80312395095825,
              37.338790026148
            ],
            [
              -121.80198669433592,
              37.33937007077421
            ],
            [
              -121.80222272872923,
              37.33960891137705
            ],
            [
              -121.80301666259766,
              37.33966009140741
            ],
            [
              -121.80331707000731,
              37.33998423078978
            ],
            [
              -121.80389642715454,
              37.33966009140741
            ]
          ]
        ]
      }
    }
  ]
}

您可以自己重新创建此确切的结构。 转到geojson.io并将上面的GeoJSON粘贴到右侧的编辑器中。 由于数组中的每个元素都是一个lat-long对,因此结构将出现在与前面显示的图像相同的位置。

2. What Is IMDF?

苹果在iOS 13中引入了Indoor Mapping Data Format (IMDF),作为对结构内部建模的新标准。 IMDF存档包含一组要素类型,这些要素类型组合在一起可以描述室内空间,包括墙壁,门口,楼梯,水平等。 通常,您可以使用第三方软件来创建IMDF存档,以将详细的平面图转换为IMDF

注意:创建IMDF存档超出了本教程的范围。 如果您想了解更多有关此的内容,这里有一个很棒的教程,Creating and Validating IMDF Datasets。

IMDF Feature Types

您已经知道IMDF档案本质上是GeoJSON文件的集合,当组合在一起时,它们描述了一个室内空间。 但是,这到底是如何工作的呢? 接下来,您将找到答案。

Xcode中,打开Project navigator,然后展开IMDF/Data。 每个GeoJSON文件的名称都与IMDF文档中描述的要素类型相对应。

例如,occupant.geojsonOccupant types的集合,building.geojsonBuilding types的集合,依此类推。 就这么简单。

您将在本教程中看到的主要类型是:

  • Venue
  • Unit
  • Occupant
  • Amenity
  • Level

现在您已经了解了将要使用的工具,是时候将它们投入使用了!

3. Using the GeoJSON Format

在项目导航器中打开occupant.geojson。 您会发现GeoJSONIMDF确实没有听起来那么可怕。

看一下列表中的其中一位occupants

{
    "feature_type": "occupant",
    "geometry": null,
    "id": "5ac4bf40-2dbf-4bdb-9c39-495c442b7e39",
    "properties": {
        "address_id": null,
        "alt_name": null,
        "anchor_id": "76336b53-81c9-4c93-8f81-a4c008d54ba1",
        "category": "office",
        "display_point": {
            "coordinates": [
                -121.889609,
                37.329678
              ],
            "type": "Point"
        },
        "hours": "Su-Sa 09:00-17:00",
        "name": {
            "en": "Ray's Office"
        },
        "phone": "+14087924512",
        "restriction": null,
        "website": null,
        "correlation_id": null
    },
    "type": "Feature"
}

这位occupant描述了Ray的办公室。一个有效的occupant必须定义一个id,feature_type必须具有一个乘员值,并且其几何值必须为null。

occupant不需要自己的几何图形,因为它使用来自anchor_idproperties内引用的锚点的几何图形。

仍在occupant.geojson中,复制第一个occupantanchor_id,然后打开anchor.geojson并搜索id。您会在其中找到关联的锚点功能。

properties中的所有其他键都是描述occupant的元数据。在本文中,您唯一要使用的密钥是name,但在实际情况下,如果该信息与您的应用有关,则可以扩展它以显示营业时间,电话号码和网站。

另外,那不是Ray的真实电话号码,所以不要打扰。

注意:您已经介绍了有关GeoJSONIMDF的一些顶级理论,但还有很多要了解的知识。 Apple的文档提供了IMDF和不同功能类型的详细概述。 GeoJSON官方网站GeoJSON website上有一些很好的信息,但这是非常技术性的。

学习的好方法是使用geojson.io上的工具进一步探索GeoJSON。您可以直接在地图上绘制几何图形,并使其输出关联的JSON

接下来,研究如何将GeoJSON文件转换为可以在代码中使用的实际Swift模型。


Modeling GeoJSON

Project navigator中打开IMDF/Models。 您会注意到,您已经有很多需要入门的IMDF功能类型,但是其中一种是Venue类型。 您现在将添加它。

Models中创建一个新文件,将其命名为Venue.swift并添加以下内容:

class Venue: Feature {
  struct Properties: Codable {
    let category: String
  }

  var levelsByOrdinal: [Int: [Level]] = [:]
}

这就是定义Venue所需的全部代码。 看起来有点少,不是吗? Venue与项目中的大多数其他模型类型一起从Feature继承,Feature是一泛类,该类具有通用类型Properties,类型受限于Decodable

Feature定义了每个IMDF feature共有的idgeometryGeoJSON数据内的Properties对象可以是任何有效的JSON对象。 每个功能都定义了自己的Properties,如您在Venue模型中所见。

Venue.Properties作为通用类型参数传递。 它符合Codable,因此满足PropertiesDecodable的要求。

构建并运行。 您还看不到任何变化,但是距离室内地图仅一步之遥。

MapKit框架详细解析(十七) —— 基于MapKit使用Indoor Maps来绘制建筑物内部的地图的简单示例(一)_第3张图片

Decoding GeoJSON With MKGeoJSONDecoder

如果没有提供读取数据的方式来创建这种新的数据格式,Apple会感觉不太好。 在本部分中,您将使用MKGeoJSONDecoder解码GeoJSON数据。 MKGeoJSONDecoder提供了一种使用decode(_ :)GeoJSON解码为MapKit类型的方法。

打开IMDF / IMDFDecoder.swift。 您已经具有对某些类型进行解码的代码,但是您需要添加一个decode(_ :)方法以将所有这些绑定到一个Venue对象中。

IMDFDecoder.swift的顶部,添加以下方法:

func decode(_ imdfDirectory: URL) throws -> Venue {
  // 1
  let archive = Archive(directory: imdfDirectory)

  // 2
  let venues = try decodeFeatures(Venue.self, from: .venue, in: archive)
  let levels = try decodeFeatures(Level.self, from: .level, in: archive)
  let units = try decodeFeatures(Unit.self, from: .unit, in: archive)
  let openings = try decodeFeatures(Opening.self, from: .opening, in: archive)
  let amenities = try decodeFeatures(Amenity.self, from: .amenity, in: archive)

  // 3
  if venues.isEmpty {
    throw IMDFError.invalidData
  }
  let venue = venues[0]
  venue.levelsByOrdinal = Dictionary(grouping: levels) { level in
    level.properties.ordinal
  }

  // 4
  let unitsByLevel = Dictionary(grouping: units) { unit in
    unit.properties.levelId
  }

  let openingsByLevel = Dictionary(grouping: openings) { opening in
    opening.properties.levelId
  }

  // 5
  for level in levels {
    if let unitsInLevel = unitsByLevel[level.id] {
      level.units = unitsInLevel
    }
    if let openingsInLevel = openingsByLevel[level.id] {
      level.openings = openingsInLevel
    }
  }

  // 6
  let unitsById = units.reduce(into: [UUID: Unit]()) { result, unit in
    result[unit.id] = unit
  }

  // 7
  for amenity in amenities {
    guard let pointGeometry = amenity.geometry[0] as? MKPointAnnotation 
      else { throw IMDFError.invalidData }

    if let name = amenity.properties.name?.bestLocalizedValue {
      amenity.title = name
      amenity.subtitle = amenity.properties.category.capitalized
    } else {
      amenity.title = amenity.properties.category.capitalized
    }

    for unitID in amenity.properties.unitIds {
      let unit = unitsById[unitID]
      unit?.amenities.append(amenity)
    }

    amenity.coordinate = pointGeometry.coordinate
  }

  // 8
  try decodeOccupants(units: units, in: archive)

  return venue
}

相当多的代码,但是您会发现它并不太复杂。简而言之,您将IMDF文件解码到一个venue中,其中包含有关其内部的高度,单位,开口和便利设施的信息。

查看代码,正在发生的事情:

  • 1) archive是用于访问完整IMDF存档的容器。它需要项目中IMDF存档的URL,并且可以单独访问每个GeoJSON文件。在这里,您创建一个Archive实例。
  • 2) 使用decodeFeatures(_:from:in :),可以将feature type解码为模型。
  • 3) 如果venues是空的,则抛出错误。否则,请按Level中的ordinal属性对级别进行分组,然后将结果的Dictionary分配给venue中的levelsByOrdinal
  • 4) 通过将unitsopenings按其levelId分组来创建两个新的Dictionary对象。
  • 5) 对于每个级别,使用在第四步中创建的两个Dictionary对象添加单位和空缺。
  • 6) 按IDunits进行分组。
  • 7) 为每个Amenity设置title, subtitlecoordinate。对于任何关联的Unit,将Amenity添加到amenities数组。
  • 8) 解码完其他所有内容后,请调用encodeOccupants(units:in :)occupants进行解码。

1. Decoding the Archive

现在,您的解码方法已启动并正在运行,您需要从主视图控制器中调用它。

打开MapViewController.swift。在var mapView的声明下,添加以下代码:

let decoder = IMDFDecoder()
var venue: Venue?

在这里,您将准备一个IMDFDecoder以及一个用于存储解码后的venue的属性。

接下来,在setupMapView()下,添加以下新方法:

func loadRazeHQIndoorMapData() {
  guard let resourceURL = Bundle.main.resourceURL else { return }

  let imdfDirectory = resourceURL.appendingPathComponent("Data")
  do {
    venue = try decoder.decode(imdfDirectory)
  } catch let error {
    print(error)
  }
}

此方法从应用程序的资源包中加载Data目录,然后使用您在上一步中创建的decode(_:)将其解码到venue中。

现在,您需要一种方法,通过缩放到地图上的位置来突出高亮您的venue。 在loadRazeHQIndoorMapData()之后,添加以下代码以创建一个名为showDefaultMapRect()的新方法:

func showDefaultMapRect() {
  guard
    let venue = venue,
    let venueOverlay = venue.geometry[0] as? MKOverlay
    else { return }

  mapView.setVisibleMapRect(
    venueOverlay.boundingMapRect,
    edgePadding: UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20),
    animated: false
  )
}

此方法使用venue几何体中的boundingMapRectmapView上调用setVisibleMapRect(_:edgePadding:animated :)

boundingMapRect包含几何图形中最小的可见地图矩形。 setVisibleMapRect(_:edgePadding:animated :)edgePadding参数是地图矩形在屏幕点周围的额外空间。

最后,您需要调用这两个新方法。 在viewDidLoad()的底部添加。

loadRazeHQIndoorMapData()
showDefaultMapRect()

构建并运行查看结果

欢迎来到RazeWare总部!至少,它的位置。仍然没有可见的结构,但是随着地图的放大,您必须具有有效的Venue


Rendering Geometry on a Map

现在您可以使用本机模型类型了,现在该进行制图了!

MapKitMKOverlay对象与MKOverlayRenderer结合使用以在地图上绘制叠加层。

MKOverlay是一种协议,用于描述叠加层的几何形状。 MapKit包含采用此协议定义各种形状的各种具体类,例如矩形,圆形和多边形。

MKOverlayRenderer是一个绘制MKOverlay视觉表示的对象。

1. Adding Overlays

打开MapViewController.swift并向下滚动到extension MapViewController:MKMapViewDelegate。您会注意到这里已经定义了一些方法。

每当需要在地图视图上绘制叠加层时,MapKit都会调用mapView(_:rendererFor :)-> MKOverlayRenderer。此方法负责将适当的MKOverlayRenderer传递回MapKit

目前,它做的并不多,但是接下来您将对其进行更改。

用以下代码替换整个方法:

func mapView(
  _ mapView: MKMapView, 
  rendererFor overlay: MKOverlay
) -> MKOverlayRenderer {
  guard
    let shape = overlay as? (MKShape & MKGeoJSONObject),
    let feature = currentLevelFeatures
      .first( where: { $0.geometry.contains( where: { $0 == shape }) })
    else { return MKOverlayRenderer(overlay: overlay) }

  let renderer: MKOverlayPathRenderer
  switch overlay {
  case is MKMultiPolygon:
    renderer = MKMultiPolygonRenderer(overlay: overlay)
  case is MKPolygon:
    renderer = MKPolygonRenderer(overlay: overlay)
  case is MKMultiPolyline:
    renderer = MKMultiPolylineRenderer(overlay: overlay)
  case is MKPolyline:
    renderer = MKPolylineRenderer(overlay: overlay)
  default:
    return MKOverlayRenderer(overlay: overlay)
  }

  feature.configure(overlayRenderer: renderer)

  return renderer
}

在委派MKMapView上调用addOverlays(_ :)时,将调用此方法。提供给addOverlays(_ :)的叠加层。

如您先前所学,MKOverlay是具体类用来创建基本形状的协议。因此,在上面的代码中,您将检查其类型,并根据结果创建适当的MKOverlayRenderer子类。

最后,在返回创建的渲染器之前,请在feature上调用configure(overlayRenderer :)。这是StylableFeature中的方法。

您将在本教程的后面部分中确切地看到StylableFeature是什么。

但是首先,您必须学习如何在叠加层旁边向地图添加注释(annotations)

2. Adding Annotations

您添加注释的方式与覆盖图类似。 MKMapViewDelegate有一个委托方法mapView(_:viewFor :)-> MKAnnotationView ?,每次在委派MKMapView上调用addAnnotations(_ :)时都会调用该方法。

就像叠加层一样,只有在向地图添加注释后,该方法才会被调用。目前,这还没有发生-但现在是您改变这种情况的时候了。

showDefaultMapRect()下面添加以下方法:

private func showFeatures(for ordinal: Int) {
  guard venue != nil else {
    return
  }

  // 1
  currentLevelFeatures.removeAll()
  mapView.removeOverlays(currentLevelOverlays)
  mapView.removeAnnotations(currentLevelAnnotations)
  currentLevelAnnotations.removeAll()
  currentLevelOverlays.removeAll()

  // 2
  if let levels = venue?.levelsByOrdinal[ordinal] {
    for level in levels {
      currentLevelFeatures.append(level)
      currentLevelFeatures += level.units
      currentLevelFeatures += level.openings

      let occupants = level.units.flatMap { unit in
        unit.occupants
      }

      let amenities = level.units.flatMap { unit in
        unit.amenities
      }

      currentLevelAnnotations += occupants
      currentLevelAnnotations += amenities
    }
  }

  // 3
  let currentLevelGeometry = currentLevelFeatures.flatMap { 
    feature in
    feature.geometry
  }

  currentLevelOverlays = currentLevelGeometry.compactMap { 
    mkOverlay in
    mkOverlay as? MKOverlay
  }

  // 4
  mapView.addOverlays(currentLevelOverlays)
  mapView.addAnnotations(currentLevelAnnotations)
}

该方法的主要职责是拆除现有的叠加层和注释,并在其位置绘制新的叠加层和注释。 它采用一个单一的参数,即ordinal,该参数指定获取场地几何图形的场所中的哪个级别。

细分:

  • 1) 从地图上清除所有现有的注释和叠加层,并从本地缓存中删除关联的对象。
  • 2) 对于选定的级别,获取关联的features并将其存储在您刚清空的本地数组内。
  • 3) 创建两个数组。 第一个包含所选级别的unitsoccupants的几何形状。 然后,使用compactMap(_ :)创建一个MKOverlay对象数组。
  • 4) 在mapView上调用addOverlays(_ :)addAnnotations(_ :)将注释和叠加层添加到地图上。

要查看运行中的代码,请在viewDidLoad()的底部添加:

showFeatures(for: 1)

构建并运行。 尝试单击地图上的注释以查看当前的features

您现在是真正的制图师!

现在,您的地图可以正常运行,并按您的意愿显示RazeWare办公室。 但是,它看起来并不理想。 在下一步中,您将自己的样式添加到地图中。


Styling Map Geometry

您已经为几何图形处理了很多样式,但是仍然看到两个默认的地图钉。 您需要将其替换为更时尚的东西。

在本部分中,您将学习StyleableFeature的功能以及如何使用它来设置Occupant模型类型。

StylableFeature是示例项目(不是MapKit)提供的协议,该协议定义了两种方法,一致的类型应采用这些方法来提供功能的自定义样式。

打开IMDF / StylableFeatures.swift以查看内容。

protocol StylableFeature {
  var geometry: [MKShape & MKGeoJSONObject] { get }
  func configure(overlayRenderer: MKOverlayPathRenderer)
  func configure(annotationView: MKAnnotationView)
}

extension StylableFeature {
  func configure(overlayRenderer: MKOverlayPathRenderer) {}
  func configure(annotationView: MKAnnotationView) {}
}

您会注意到,这两种configure方法均默认为空实现,因为它们不是必需的。 一会儿您会明白为什么。 唯一的其他必须实现的是,合格的对象必须提供一个几何对象数组,这些对象从MKShape继承并符合MKGeoJSONObject

打开IMDF / Models / Occupant.swift并将以下扩展名添加到文件的底部,在大括号后:

extension Occupant {
  private enum StylableCategory: String {
    case play
    case office
  }
}

StylableCategory定义了occupant.geojson中使用的两种类型。

打开occupant.geojson并查看两个对象的category键。 您会注意到其中包含playoffice。 这些类型是完全任意的,可以是GeoJSON的作者选择的任何类型。 您可以根据需要创建任意多个这些类别。

继续在Occupant.swift中,将下一个扩展名添加到文件的底部,在大括号后:

extension Occupant: StylableFeature {
  func configure(annotationView: MKAnnotationView) {
    if let category = StylableCategory(rawValue: properties.category) {
      switch category {
      case .play:
        annotationView.backgroundColor = UIColor(named: "PlayFill")
      case .office:
        annotationView.backgroundColor = UIColor(named: "OfficeFill")
      }
    }

    annotationView.displayPriority = .defaultHigh
  }
}

现在,Occupant符合StylableFeature并实现了configure(annotationView:MKAnnotationView)。 此方法检查category是否为有效的StylableCategory,并根据该值返回颜色。 如果一个叠加层与另一个叠加层发生碰撞,则displayPriority最低的叠加层将被隐藏。

构建并运行。 您会看到这些可怕的默认地图图钉已消失。 取而代之的是两个具有自定义颜色的地图点。


Selecting Levels

您已经遇到过几次Level。 它描述建筑物的一个级别或story,并且可以具有自己的一组功能。 以一个购物中心为例:他们通常有多个story,每个楼层都有不同的商店。 使用Level,您可以描述每个楼层上的哪些商店。

接口的分段控件已经到位,并且已经连接到segmentedControlValueChanged(_ sender :)。 您还可以基于带有showFeatures(for :)Level重绘几何。 您一切顺利!

打开MapViewController.swift并将以下代码添加到segmentedControlValueChanged(_ sender :)的主体中:

showFeatures(for: sender.selectedSegmentIndex)

构建并运行。 当您在分段控件上更改级别时,地图叠加层将重新绘制提供的级别。

在最后一节中,您将学习如何使用室内位置来查看办公室内的位置。


Using Location With Indoor Maps

您可能会想,“ GPS如何在室内工作!” 简单的答案是:否。 至少不好。

室内定位实际上根本没有使用GPS。 苹果使用iOS设备内部的传感器根据方向,行进速度和射频“指纹”来确定您的室内位置。

固定的Wi-Fi接入点在建筑物内部发出可追踪的频率模式。 这些独特的信号是使用Apple的Indoor Survey应用收集的。 打开Indoor Survey应用程序后,您便可以在场所四处走动,同时收集频率和信号。 这从内部映射您的建筑物。

不用担心,您不必飞到加利福尼亚州并在本文中划定办公室的位置-也许下次。

将下面的变量添加到MapViewController.swiftlet decoder = IMDFDecoder()顶部:

let locationManager = CLLocationManager()

现在,在segmentedControlValueChanged(_ sender :)上方添加以下方法:

func startListeningForLocation() {
  locationManager.requestWhenInUseAuthorization()
}

最后,将以下代码添加到viewDidLoad()的底部:

startListeningForLocation()

使用上面的代码,您将在加载视图控制器时开始侦听位置更改。

为此,您需要在模拟器中启用位置服务并添加自定义位置。 将模拟器置于前台,导航至Features ▸ Location ▸ Custom Location。 在出现的对话框中,输入以下内容:

  • 37.329422 for Latitude
  • -121.887861 for Longitude

构建并运行。 模拟器将要求位置许可-确保您授予它!

MapKit框架详细解析(十七) —— 基于MapKit使用Indoor Maps来绘制建筑物内部的地图的简单示例(一)_第4张图片

现在,您已经了解了IMDF的基础知识,但还有很多东西要学习。 尝试在本教程中使用GeoJSON来构建自己的地图几何。 尝试添加楼层,更改名称和自定义颜色。

geojson.io是开始创建自己的GeoJSON的好地方。 尝试在地图上绘制形状,然后在此项目的IMDF存档中使用它们。

如果您想将MapKit的知识提高到一个新的水平,请尝试我们的专业课程:MapKit and CoreLocation。

确保您还查看了WWDC19上有关室内地图的两个讲座: What’s New in MapKit and MapKitJS以及Adding Indoor Maps to your App and Website。

后记

本篇主要讲述了基于MapKit使用Indoor Maps来绘制建筑物内部的地图的简单示例,感兴趣的给个赞或者关注~~~

MapKit框架详细解析(十七) —— 基于MapKit使用Indoor Maps来绘制建筑物内部的地图的简单示例(一)_第5张图片

你可能感兴趣的:(MapKit框架详细解析(十七) —— 基于MapKit使用Indoor Maps来绘制建筑物内部的地图的简单示例(一))