大多数浏览器和
Developer App 均支持流媒体播放。
-
为您的 App 和网站添加室内地图
通过“室内地图计划”,拥有大型公共或私密空间的组织可以提供精确的室内位置信息并展示精美的室内地图,从而营造出色的用户体验。探索室内地图实现的整体流程,然后深入探讨相关的技术细节,了解 MapKit 和 MapKit JS 如何利用强大的 API 和地理标准将室内地图快速整合到您的 app 和网站中。
资源
- Apple Developer: MapKit JS
- Displaying an Indoor Map
- Displaying Indoor Maps with MapKit JS
- MapKit
- 演示幻灯片 (PDF)
相关视频
WWDC19
-
下载
欢迎各位 我叫 Stephane 是地图团队中的 一名工程师 我将和我的同事 Mithilesh 一起 和各位开发者来聊聊 如何在你们的 App 和网站里添加室内地图
在 iOS 11 中 我们引入了室内地图 作为 Apple 地图的一部分 我们为机场和商场等场所 提供了漂亮且 详细的平面图
当你处在室内空间时 室内地图可以很好的帮助你 查看和搜索你在室内的路径
使用楼层选择器 你可以在 不同的楼层间进行切换 并且在这些场所内你得到的室内位置 是与 GPS 相似的 甚至更好
今天 我们就将和 各位探讨一下 开发者如何在 自己的 App 中呈现室内地图以及 室内定位
我们一开始将探讨一下 室内地图的数据格式 之后我们将为各位 介绍一些室内地图项目中的 可用的工具 之后我们将为各位展示 如何在 iOS 和 web 的 App 当中 展示室内地图 那么 我们开始吧 IMDF 代表的是 Indoor Mapping Data Format 它是一种特定的室内空间的建模规范
但在讨论更多细节之前 让我们先退一步 仔细看看室内地图的一般含义 室内地图和一般地图一样 是由许多层组成 每一层可能都 分别包含着一些特性 比方说 我们把这张地图分解一下 室内地图的基础是建筑的覆盖区域 之后我们就会得到一个平面 在这个平面上 我们可以找到一些 细节的空间单元 比如房间和过道 这些空间单元有门 我们还可以看到 在商场里经常看到的售货亭 房间和售货亭都有标签 比如 标记出使用该房间的 企业的名称 或者是用图案标记出 像是电梯和卫生间 这样的公共设施 最后 还有可以用 虚拟边界把多个空间单元 划归到一个区域内
好了 这就是用来制作室内地图的 一些图层的样例
因此 为了能进行准确地描述 也为了能够 创建和显示出这样的地图 我们需要一种新的方式 可以用于准确的描述它的内容 这就需要 IMDF 这种数据格式发挥作用
IMDF 是进行室内空间 建模的一种规范
它是一种基于 GeoJSON 的二维数据格式 不仅易于创建和理解 而且方便应用程序调用 一个 IMDF 档案就是一组 JSON 文件
更进一步来说 它由一个 manifest.json 文件以及 多个 GeoJSON 文件组成 每个文件都是一组特征的集合 这些特征又可以赋予 IMDF 的特征以类型
也就是说每个 IMDF 特征就是一个 寻常的 GeoJSON 的特征 其中包含一个负责表明类型的 type(特征) 也许会有一个 geometry(几何对象) 并且会有一组 properties(属性)
IMDF 规范还要求提供一个 ID 这个 ID 是一个 UUID 类型的字符串 并且还需要一个额外的 称作 feature_type 即特征类型的项 这一项的值 表明正在建模的是某一种室内空间的对象 让我们来看看 IMDF 的一些主要的特征类型吧
level 类型模拟的是楼层区域的 位置信息以及物理范围
每个 level 还有一个全称 比如说这里的 Parking Level 1 再配个缩写像是 P1 下面这个属性 则用来指出它是属于哪一栋建筑的
它还有一个序数 代表了该楼层 在建筑的总楼层范围内的位置
一楼的序数对应的是 0 地下楼层使用负数来表示 当然与之对应的 地上楼层使用正数
每一个楼层都是由各个空间单元组成的 unit 类型则是模拟了 空间里的位置和范围 它可以代表房间 过道 楼梯间 或是 电梯
opening 类型则模拟了像是门一类的入口
其中可以定义一些有趣的属性 像是这个入口的可访问性 或者是访问控制系统的类型 kiosk 类型则模拟的是一些装置和设施 一般是一些用来提供服务 或是分销产品的 比如说一个咨询台或者是自动售货机 多亏有了 occupant 这种特征类型 我们就可以把商业信息添加到地图当中 occupant 这种类型可以 提供包括商户的名字 电话号码 营业时间在内的商业信息
这个类型里面不包含 geometry 对象 但是我们可以通过它关联的 anchor 对象获取它们的显示点和地址 anchor 类型代表的是一种用于引导的锚点 它们给一个单元区域 赋予了外围呈现位置
它们也可能会引用某个地址
anchor 类型在 IMDF 中起着重要的作用 其他的一些特征类型 比如之前提过的 occupant unit 或者是 kiosk 都需要通过 anchor 类型的对象才能连接到地址之上 我们可能还想增加一些便利设施 amenity 这样的特征类型就可以 用来模拟永久性的设施 或是其他便利设施 比如说照相亭 ATM 机 或者是 某个展品之类的 section 这种特征类型用于 描述在一个楼层内 具有特定主题的区域范围 比如说购物中心的美食广场 或是机场内的安检区域
section 这种类型不需要 通过物理边界来具体化
building 类型就是用来描述现实中的建筑物 它们会有一个名字并且还有一个地址 但是 这一类型中并不包含 geometry(几何对象) 来描述建筑物的范围 而把这件事交给 另一种称作 footprint 的特征类型来实现
footprint 可以分成 3 个类型 aerial 型的 footprint 能用来圈定 建筑物地面以上部分的最大范围
ground 型的 footprint 圈定 地面区域的最大范围 而 subterranean 型的 footprint 则圈定地下楼层的最大范围
好了 刚才我们简要的 概述了一下 IMDF 的主要的特征类型 多亏了 IMDF 我们才能够使用 一种正式且灵活的方式描述室内空间
但是 其实 IMDF 规范中还 涵盖了更多的内容 你可以到 register.apple.com 去查看更多的内容 Apple 与许多顶级的 平台供应商合作 提供支持 IMDF 的地理空间工具 你可以直接使用这些 来为你的场所创建一个 IMDF 档案文件
你可以立即就启用这个档案文件 并将其显示在你的 App 之中 我们将在稍后进行演示 如果你想启用室内定位 又该如何操作呢
好了 我们的室内地图项目 就是来解决这种需求的 通过参与室内地图项目 你可以使用 Apple 提供的一些工具 来对你的数据进行 可视化和验证操作 更有趣的是 你还可以为你的场所添加室内定位功能 或是标记用户所处位置的蓝点功能 此外 你还可以 允许 Apple 在 Apple 地图中 显示你的建筑场所信息 注册室内地图项目 仅需登录 register.Apple.com/indoor 注册起来非常的简单易行 你只需要提供一些 关于你自己以及 你所在的组织的基本信息 比如 名字 位置 和 地址 一旦 Apple 审核 并批准了你的 App 你就可以开始与你的 地理空间工具的供应商展开合作 来创建 IMDF 存档文件 当你有了存档文件之后 你就可以使用 IMDF 沙箱 来验证它了 IMDF 沙箱是一个工具 它可以用来将 IMDF 数据 进行可视化并验证 它报告的大多数问题 都可以通过单击几下就立即得到解决
有一些错误可能会 需要更多的工作 这时你可能希望把你的数据提取回来 交给地图制作方进行再修复 当 IMDF 沙箱没有反馈错误时 你可以把你的数据提交至 更费时的验证测试 如果没有检测到问题的话 你就可以进入到下一个阶段 使用室内测量 App 为你的场所启用室内定位
室内测量 App 是 你在 iOS 上启用室内定位的配套程序
通过收集所在地点的 射频纹理特征来测量你的场所 测量依赖于这样一个事实 那就是建筑物内固定的 WiFi 接入点所发出的 射频模式是独特的 而且射频模式取决于你所处的位置 当测量完成时 测量数据会被上传到 Apple 的服务器 并进行处理 如果没有报错的话 你的场所的室内位置就会变成实时的
你可以用一个测试 App 来测一下室内位置的准确性
测量的过程是非常简单和直观的 我们有一些指导方针 以便帮助你获得最好的结果
好了 这样一来 你就已经创建了 IMDF 档案文件 并且还对其进行了验证 你还有可能已经启用了室内定位 下一步就是 在你的 App 和网站上显示它
接下来我将让我的同事 Mithilesh 来为大家展示 如何把室内地图呈现在 iOS App 当中 谢谢 Stephane
大家好 我是 Mithilesh 是地图团队的一名工程师 我在这里向各位展示 如何使用 IMDF 数据 在 iOS App 中显示室内地图 我们将为恐龙博物馆的 游客创建一个简单的 App 我们就叫它《Dinoseum》
这款 App 会显示一个 博物馆的地图 其中包含有 不同的展品 餐厅 商店等等
博物馆的各个区域 都有描述性的标签和图标 用户可以点击这些标签和图标 来显示详细信息
在右上角 我们有一个楼层切换器 来切换至不同的楼层 这款 App 还利用室内定位 来显示用户在 博物馆内的位置
那么 构建这款 App 都需要什么呢 显示一个基本的室内地图 可以分为 3 个步骤
第 1 步 我们将向项目中 添加 IMDF 文件 并为每一种特征类型创建模型类 第 2 步 我们将解码 IMDF 文件 并且给模型类创建实例 然后我们给这些实例化后的特征类型的对象 创建一个关系图 将它们关联起来
场所由建筑物构成 建筑物包含有足迹和楼层 楼层中又包含有空间单元和出入口 以此类推 第 3 步 我们将从已经解码了的 特征对象中检索它们 在 GeoJSON 文件中的几何对象 并把它们呈现出来以此创建室内地图
现在 让我们快速浏览下 需要编写的代码吧
对于第 1 步而言 我们创建一个 名为 feature 的抽象类
每一个 IMDF 的特征对象都必须有一个 唯一的标识符 他还必须有一组 描述特征的属性 它也许还会包含有 geometry 对象 也就是一个 MKShape 数组对象
使用上面的抽象类作为基类 我们将会为像是 unit(空间单元) 这样的具体的特征类 编写它们的具体实现
对于解码 IMDF 文件 其实就是一个 GeoJSON 文件 我们将使用新的 MKGeoJSONDecoder 并调用其中的 decode 方法
稍后 我们将带大家看一下 具体的实现细节 在进行第 3 步的过程中 我们将使用 MapKit 框架中的 addOverlays 和 addAnnotations 用这两个 API 在地图上 绘制多边形以及线和点 对于每一个添加到地图上的 overlay(覆盖) 我们都会有一个 给到 mapView 的代理回调方法 叫做 renderFor overlay
对于每一个 annotation 来说 我们都会有一个给 mapView 的回调方法 叫做 viewFor annotation 在这两个代理方法中 我们便有机会 给室内地图的元素 定制它们的外观 那么在有了这些之后 我们就开始 为我们的恐龙博物馆做室内地图吧
我们将从一个非常基础的 App 开始 这是一个基于 MKMapView 的 单一视图的 App
我想做的第一件事 就是确保我已经给我的项目 添加了 IMDF 文件 它们在这儿 在你的 App 里 你可以选择将这些文件 直接打包到你的 App 内 或者是从你的服务器进行下载
我们看一下其中一个文件
这是一个 unit 的特征类型 我们的目标是解码并呈现 这个文件里面的内容
我会先写一个叫做 feature 的抽象类
这直接给出了我们之前看到的三个成员 identifier properties 和 geometry
我们的这个类缺少一个初始化方法 我们给他添加一个
初始化由一串选择语句加上 属性值的抛出异常的语句来组成 非常的简单直白 MKGeoJSON 的特征会把其属性 公开成不透明的数据 所以在使用之前我们必须要先解码
我们使用 JSONDecoder 这个类 来解码我们的数据并把它转换为 相邻的对象 现在 我可以给 feature 这个类 来编写它的具体实现 我们首先先实现 unit(空间单元)这个类型
我们把基类设成 feature
为了描述 unit 这种特征类型 我们需要定义一些属性
属性由 category 和 levelID 组成 category 用于区分 不同种类的 unit 的类型 levelID 则是该 unit 所属的 楼层的标识符 unit 中包括有 occupant(使用者) 以及 amenity(便利设施) 所以让我们在这里加上它们两个 这就是我们需要给 unit 特征类 所做的所有操作 与 unit 类相似的 我们可以为 venue(场所)以及 level(楼层)以及其他所有的我们想要 解码的特征类型创建模型类
我已经把它们都实现好了 并且添加到了项目当中 你可以下载 本场会议的示例代码 并查看它们 好了 我们可以进到第 2 个步骤了 解码 IMDF 数据 我们将在 IMDFDecoder 类中实现这件事 我们添加一个 MKGeoJSONDecoder 类型的属性
这个是 MapKit 框架中 一个新的可用类 你可以使用它解码所有的 GeoJSON 数据
我现在要写一个名叫 decodeFeatures 的方法来解码单个的 IMDF 文件 在这个方法中 我们首先从磁盘里 读取 IMDF 文件的内容 并使用实例化后的 MKGeoJSONDecoder 方法 对数据进行解码 我们可以得到一个 MKGeoJSONFeature 型的对象数组 我们可以使用它来初始化我们的模型类 我现在要写一个名为 IMDFDecoder 的方法 来解码 IMDF 档案文件 或是 IMDF 文件集
让我们创建我们希望解码的 特征对象的具体实例吧 例如 venues(场所) levels(楼层)以及 units(空间单元) 我们使用的就是刚才我写过的 decodeFeatures 方法 以此解码我们想要解码的特征类型
为了能够更容易地显示出 正确的特征子集 我们需要给不同的特征对象间 创建关联
例如 venue(场所)包含有 levels(楼层) 我们把他们关联起来 我们是在按序号对 levels(楼层) 进行分组之后再做上去的关联
levels 包含 units(空间单元)以及 openings(出入口)我们把它们也关联起来
units 和 openings 都有一个属性 叫做 levelID 我们使用 levelID 将 units 和 level 进行分组 之后再遍历这个 venue(场所)的所有的楼层 找到包含在对应楼层中的空间单元 把它们关联起来 对于openings 也是一样的操作
unit 当中由包含 amenities(便利设施)和 occupants(使用者) 我们给它们创建上关联
我们遍历所有的 amenities 找到他们对应的 unit 并建立关联
同样的 我们也可以把 occupants 和 unit 关联起来
但 occupants 是个例外 请记住 我们用 occupants 是来显示 地图上商户的名称的 它们是没有 geometry 这个对象的 取而代之的 他们依赖于 anchor(锚点)对象 来获取它们的显示位置
好了 现在我们就有了一个解码方法
并且结合上 IMDF 档案文件可以 给我们提供一个完全解码并且配置过的 venue(场所)对象并且它 还引入了所有我们希望呈现出来的 特征类型
所以现在我们可以进入到第 3 步了 就是在地图上呈现 IMDF 数据 我们将在控制主视图的 IndoorMapViewController 类中 实现这件事
让我们调用 在第 2 步中定义的解码方法
我们会得到一个 venue(场所)的对象 这样我们就可以把其他的特征以 overlay(覆盖)和 annotation(注释)的方式添加上去
为此 我将编写一个叫做 showFeaturesForOrdinal 的方法 其中序号是整型的 代表某一个 level 具体在 整个 building 的所有楼层范围内的位置 在这个方法中 我们首先 移除掉属于前一个楼层的 所有的覆盖和注释 之后再把我们希望 在这个当前楼层中呈现出来的 特征给聚合起来 检索出这些特征对象的 geometry 部分 并把它们以覆盖和注释的形式 添加到地图上 让我们从 showFeaturesForOrdinal 中调用这个方法
我们把初始楼层设为 序号为 1 的楼层 我们已经实现了 MKMapViewDelegate 方法 mapView 以及 renderFor overlay 其中我们为每种 GeoJSON 的几何类型都创建了 MKOverlayRenderer 的实例 包括新的 MKMultipolygonRenderer 以及 新的 MKMultipolylineRenderer 方法 以便更有效地呈现 多边形和线条 让我们运行一下这个 App 看看我们是不是得到了一个室内地图
好了 现在我们有了一个非常基本的博物馆地图
让我们放大一点 在这张地图内 空间单元是以多边形来显示的
像卫生间这样的便利设施则是以 注释的形式呈现的 之后使用者像是这里的 Jurassic Table Restaurant 也是以注释的形式呈现的 因为博物馆也是个多楼层的建筑 所以它需要一个楼层选择器
我们给它加一个 我已经在 Storyboard 里 加了一个楼层选择器 这是一个基于 UIStackView 的楼层选择器 我现在把它调成可见的 为了接受与楼层选择器 相关的更新信息 我们已经实现了一个 楼层选择的代理事件 其中包含一个 叫做 selectedLevelDidChange 的方法 每当用户在楼层选择器中 进行选择时 都会调用这个方法
我们在这里处理更换楼层更换的事件
当楼层发生变化时 我们使用 showFeaturesForOrdinal 方法 更新地图中所选楼层的 各个特征 我们再运行一下 确认下我们已经有了一个 楼层选择器 好了 现在我就可以在楼层选择器里 点击上面的项目 并查看博物馆的每一个楼层了
让我们在这里停一下 回顾一下我们刚才所做的
首先 我们给 IMDF 的特征类型 都创建它们的模型类
之后 我们使用 MKGeoJSONDecoder 对 IMDF 数据进行了解码 并且在特征类之间建立了关联 最后 我们使用MapKit框架的 addOverlays 和 addAnnotation 这两个 API 在地图视图上呈现 IMDF 数据
你可能已经注意到 我们现在的地图没有什么样式 上面的注释看起来 也不怎么有趣 只需要两个额外的步骤 你就可以显著地改进 室内地图体验和功能 首先是将样式应用到室内地图的元素上
对于理解和使用地图来说 给它设计一个合适的样式是十分重要的
你可以使用与 App 团队 或是公司品牌相匹配的 独一无二的颜色或图标 在最后一个步骤里 我们可以通过展示用户的实时位置 来让你的 App 变得更加实用
请记住 你可以参与 室内地图项目以及 运用室内测量 App 来测量你的场所 用这样的方式你就可以 获得准确的室内位置
幸运的是 我们有一个团队 已经测量过恐龙博物馆了 所以我们可以更进一步将实时位置 和表现位置的蓝点添加到题图当中 让我们先来看一下 一些用来辅助样式设计的代码吧 我们将定义一个叫做 StylableFeature 的协议 它有一个属性和两个方法
每一个可以进行样式设计的特征都必须包含 geometry 并且 为了将样式应用于 overlays 我们需要实现 configure(overlayRenderer:) 这个方法类似的 并且 为了将样式应用于 annotations 我们需要实现 configure(annotationView:) 这个方法 我将切换回 Xcode 为各位展示下 如何实现上述的内容
为了应用样式 我们需要利用 asset 这个目录 这里面我已经给 这个室内地图添加了特定的颜色 所以 为了应用这些颜色 我们进到 styles.swift 这个文件内
这里我已经定义好了 StylableFeature 协议了
现在 我们给 amenity 特征 来应用一下样式吧
我们可以通过扩展 amenity 类来适配 stylableFeature 这个协议 并且因为 amenity 包含有 点状的 geometry 对象 我们将要实现 configure(annotationView:) 这个方法 并把其中的 annotation 视图的背景颜色 设置成 asset 目录中的 默认颜色
在这个地方我们同时可以 把 annotation 的显示优先级 设置成 defaultLow 类似的 我们可以扩展 unit 类来适配 stylableFeature 这个协议 来给 unit 配上样式
但是对于 unit 来说 我们希望给每一种 unit 都配上独特的颜色
为了实现这件事 我将定义一个叫做 StylableCategory 的 枚举型变量 其中的值 是我们希望设计样式的 各种 unit 的名称 并且因为 unit 包含有 have polygon 多边形的 geometry 对象 我们需要实现 这个 configure(overlayRenderer:) 方法 在这个方法中 我们将处理 不同种的 unit 的值 并且给它们都赋予 不一样的颜色 我将切换到主要的 视图控制类这个位置 在我的 mapView 中的 rendererFor overlay 这个方法里 我们将调用 feature.configure(overlayRenderer:) 这个方法来为特征应用上不同的颜色 而不是让每个多边形 都使用相同的风格和颜色 类似的 对于 annotation 来说 我将调用 stylableFeature.configure(annotationView:) 方法 来给每个 annotation 应用特定的样式 我们想把 occupant 渲染成 一个简单点配上一个标签 之后 amenity 就是一个灰色的点 为了完成这件事 我已经实现了 自定义 annotation 视图的语句 并且把它们放到项目文件里了
在这个位置 我们调用了上面提到的 configure(annotationView:) 方法 并将特定的样式应用给了 annotation 视图 现在 让我们再运行一下这个 App 看看我们的地图是不是看起来不一样了 好了 它现在看起来 比之前的效果好了很多
现在 我们可以看到 不同的空间单元都有了不一样的颜色 并且我们的便利设施都配成了灰色的点 并且使用者 比如 Jurassic Table Restaurant 这里 是以点配上一个标签来显示的 但是这里还是有一些地方是不对的 地图上的这个区域 它是过道 我记得它属于一种空间单元 但是它的颜色和其他他周围的空间单元 没有太大的不同
让我们来看看能不能修复它
为此 我将进到 style.swift 文件内 我们给过道这个类别 添加一个 type 值 我们必须在 configure overlayRenderer 方法中处理这件事
在我们设置颜色的地方 walkway 的填充颜色 应该是叫做 walkway fill 的颜色 这个颜色应该在我们的 asset 目录下 让我们确认一下 好了 这里我们给它设置成了白色 我再运行一下 App 看下我们是不是修复了这个问题 好了 非常棒 这看起来好多了 只是简单地 给过道应用了一个不同的颜色 我们这个地图的外观就有了显著的改善 让我们再优化一下
给便利设施使用灰点的确不错 但我觉得用图标会更好一点
比普通的图标更好的是什么呢 恐龙图标应该是最佳选择 那么我们就用恐龙的图标吧
我们的设计师已经给了我们一些图标 我们已经把它们放到了项目里
让我们把它们放到地图里吧 我进入到 styles.swift 文件内 我们在这儿
看到 amenity 显示出来是个点 让我们在这做一点改变
为了方便起见 我们把图标的名称 和 amenity 类别设置成一样的
所以这里的代码就是到 asset 的 category 里面看一下这个特定的名称 如果找到一个名称对应的图标 我们就把 annotation 视图的 image 属性 设置成这个图标 如果没找到 就继续按照默认的方式显示灰点 我们运行一下 App 看下效果
太酷了 现在我们就能在地图上看到一些 漂亮的图标了 并且我们可以清晰地 看到洗手间以及扶梯和电梯的位置 更重要的是 我们可以看到一个叫霸王龙的展览
最后一件事 我们知道我们在这个博物馆内 是可以使用室内定位的 那就让我们利用起来这个功能吧 让我们把用户的位置添加到地图中
为了实现这件事 我到这里的 Map View 这个位置 从这儿打开 user location 在我的 ViewController 这个文件里 首先我要添加一个 CLLocationManager 类型的属性 然后用这个属性 在 App 处于前台使用状态的时候 向用户请求定位功能的权限 每当我们获得一个定位的更新 我们就会得到一个回传的参数给到 MKMapViewDelegate 方法 之后 mapView 就会更新用户的位置 让我们来处理下位置更新吧 首先我们要检查一下用户是不是在 场所的内部 如果用户在内部的话 我们就不需要更新地图了
如果用户在场所的外部的话 我们也不用更新地图 但是 如果用户在内部的话 我们就要用 MKUserLocation 上的 CLLocation 这个属性 来获取用户的坐标 CLLocation 不仅提供用户的 经度和纬度 还将提供楼层的信息 不过前提是你的场所已经被测量过 并且用户在场所的内部 所以我们将利用 从 CLLocation 对象中的楼层信息 并使用 showFeaturesForOrdinal 来 更新地图信息 以呈现用户的位置 让我们在运行一下这个 App
我在这点击允许 现在我们就有课用户的室内定位 我还必须说明一下 当用户在不同的楼层间进行了切换 地图将显示用户的当前的楼层 以上这就是这个演示的全部内容 接下来的部分就由 Stephane 来为各位演示如何在 Web App 中显示室内地图吧
好的 使用 MapKit JS 我们可以在 Web 上 实现出类似的室内地图 的外观和感觉
我将演示如何使用 MapKit JS 渲染我们的地图 并且为所有的主要浏览器 带来相同的室内地图体验 在 MapKitJS 我们要用到的关键方法是 importGeoJSON 方法 importGeoJSON 方法需要接收两个参数 第一个是需要转换的 GeoJSON 特征量 第二个是 GeoJSON 的代理对象 代理对象允许我们以 自定义的方式来创建项目
在这个基本示例中 我们有两个代理对象 geoJSONDidError 让我们能够对 错误做出反应 geoJSONDidComplete 可以给我们一个 刚创建好的项目的数组
之后们们通过 showItems 方法 来显示之前得到的项 我们其实也可以用 addItems 方法 但区别是 addItems 方法 不会把地图的中心点给聚焦在新添加的项目上
为了给 overlays 添加样式 比如说多边形和线条 我们可以运用 styleForOverlay 方法 它用一个新获得的 overlay 作为参数 并期望返回一个 style 型的对象 当然我们也可以创建一个 全新的样式 但我们也可以通过 style 属性 使用与 overlay 相关联的默认样式 之后再定制它
我们也可以使用 itemForPoint 方法 定制点的呈现效果 itemForPoint 接收 point 特征的一个 coordinate 作为参数 并且会返回一个项 这里返回的是一个 MarkerAnnotation 项
当然 我们也可以设置 标题和颜色等选项 我们还可以设置显示的优先级 当地图被缩放的很小的时候 如果地图上出现很多的注释 它们可能就会发生冲突 有多种策略可以解决这个问题 比如将注释集群在一起显示成一个 或者是根据显示优先级来显示
MapView JS 可以根据显示的优先级 来决定首先应该 隐藏哪些注释
有了 importGeoJSON 我们就有了 创建恐龙博物馆的地图所需的一切了 那么我们开始吧
我们就从一个简单的 mapView 来开始我们的项目吧 我正在运行着一个 HTTP 服务器 并且它正在为我们的 Web 资产 提供着服务 同时他还负责生成 MapKit JS 的授权令牌
为了将恐龙博物馆的地图 绘制在浏览器里 我们需要 加载 IMDF 文件 并为其中包含的 每个特性创建 mapView 项 这听起来很难 但并非如此 有了 importGeoJSON 方法 一切就变得不那么复杂 因为我们之前已经看到 IMDF 实际上就是 GeoJSON 在 iOS 的演示里 你们已经看到 我们实际给所有的特征做了个图的结构 在这个演示里我们将采取 有点不同的方法 根据需要去过滤特征
我们创建一个名为 IMDFArchive 的类 它将提供加载 组织和筛选特征所用到的 基本的方法
静态的 ImdfArchive.load 方法 将从服务器下载 我们需要的所有文件 然后将特征聚合到一个数组中 并将该数组传递给构造函数 然后创建两个对象 featureById 和 featuresByType 它们在后面就会派上用场
现在我们就可以调用这个方法 来加载我们的特征 并且我们可以用 importGeoJSON 来 给每个特征创建 MapKit 项 好了 现在我们需要做的 就是用 showItems 方法来 显示这些新创建的地图项 我们来做这一步
好了 我们试一下 好了 我们可以看到地图上 呈现了一大堆的几何图形 不过这也是意料之中的 因为我们要求 MapKit JS 渲染了 我们这个场所内的所有特征 我们需要的是只保留我们需要的 特征类型并按照楼层来 过滤并显示它们 因此 我们需要在 IMDFArchive 类中实现一些方法 他们只返回给定序号的 特定的类型的特征对象
我们先从 level 开始吧
好了 所以这里我们只保留了 有着正确序号的 level 类似的 我们也给 units 做一样的操作
好了 它们都有一样的 levelID
之后我们会为 units 里面的 amenities 也做一样的操作 还有 level 上的 openings amenities 和 anchors
让我们在这里暂停一下 事实上由于 IMDF files 根源上是 标准的 GeoJSON 所以才让 我们的工作变得如此简单 这里无需复杂的解析或者解码的逻辑语句 我们只需要知道 我们希望呈现的属性它是 属于哪一种特征类型就足够了
好了 现在我们重新写一下渲染的逻辑
我们将在这里创建一个叫做 createItemsForOrdinal 的函数 它将为给定的序号创建出对应的项
现在我们要做的就是 得到这个序号对应的特征 我们还是先从 levels 开始 好的 对于 levels 中的每一层 我们都等到了对应的 units 类似的还有 amenities 以及 openings
好了 所以这就是我们现在 想要的所有的特征类型
好了 现在我们要做的就是显示它们 现在我们用一个序号 来调用 createItemsForOrdinal 函数 我们就用序号 1 它代表 2 楼 然后我们把它适配到地图上
好了 让我们来写一下 好的 这样就好多了 现在我们可以清楚地 看到 2 楼的结构了 现在让我们添加一个楼层选择器 我已经实现了一个简单的楼层选择器 它其实就是一个基础的 HTML 表单 是我用 venue 内的 level 的名称来实现的 你可以在网站上 本场讲座的下方链接中 找到示例代码并查看细节
在需要的情况下 楼层选择器也会使用我们的 createItemsForOrdinal 来创建项 我已经导入了脚本 所以这里我需要的就是实例化它 并选择序号 1 这样一来 在加载完页面的时候 就会显示出 2 楼 我们再来试一下
好了 现在 我就可以在楼层间切换了 现在 我们需要做的 就是添加一些样式
现在 所有的空间单元看起来都是一样的 现在我们甚至无法把 代表出入口的线 和其他的几何图形区分出来 给了我们一个错误的印象 就好像所有的空间单元都没有门 这点有点让人困惑 我们可以定制 多边形和线条的样式 只需要在 GeoJSON 的代理对象中 把 styleForOverlay 方法给实现出来就可以了 那么 我们就从把 openings 画成白色 来开始吧 好了 这很简单 我们只需在这儿选到 opening 特征这个位置 之后把描边颜色设置成白色就可以了
正如我们在 iOS App 种所做的 这里让我们也根据 unit 的类别 来设置不同的填充颜色 为了实现这件事 让我们创建一个叫做 unitStyles 的对象
好了 所以这个对象内 将包含我们想要显示的每一个种类的 unit 的样式属性
但我们也需要一个默认的 因为有一些种类的 unit 可能没有指定的样式 我们先把默认的写出来 好了 现在我们就可以给 我们关心的 unit 的种类 来设定样式了
好了 所以 我们可以用这个对象 来给 unit 的 overlay 应用上一个样式 好了 所以我们在这做的 就是获取指定的 unit 类别的唯一样式 之后把 fillOpacity strokeColor 和 fillColor 设置成样式值 如果没有样式值 就设置按默认值显示
这实际上是一个很好的例子 说明了渲染 IMDF 地图是多么容易
大多数情况下将 feature_type 和其包含 categoty 联合起来运用一下 就能把样式给应用上去 当然 我们其实也可以 依赖其他属性来实现这件事 那么 让我们来看一下变化吧 好了 看起来好多了 现在我们就能看出来 哪里是过道以及用户可以从哪儿 进到各个房间 现在 你也许会注意到 地图上的这些红色大头针 它们代表的是便利设施 请记住 amenities 包含 点型 geometries 并且 importGeoJSON 默认情况会把点型的 geometries 按照 marker annotations 的方式呈现出来
这里 我们希望和 iOS App 使用相同的图标 为此 我们需要使用 MapKit JS 的 图片注释功能
就像我们之前所做的 我们可以通过 提供我们自己的 itemForPoint 方法 来替换掉 importGeoJSON 的默认行为 那么我们就来这么做吧
好了 所以我们在这儿做的 就是从属性中找到一个显示用的名称 或者是显示 category 给传回来的种类名 如果这两者都没有的话 就把它本身的名称作为 annotation 来显示
当这个 annotation 被点击时 显示用的名称将会出现在对话框里 我们还给所有的 amenities 先设置了 一个较低的显示优先级 这样我们就能给其他 更重要的 amenities 比如说展品 设置一个更高的优先级 我们很快就会讲到
并且最后 如果存在的话 如果我们的图标有一个 URL 的话 我们就创建一个 ImageAnnotation 如果没有的话 我们就返回 CreateDotAnnotation 的函数值
我已经在一个单独的文件里 实现了一个简单的点注释 而且我也已经把它导到项目里了 所以我们可以在这里直接用
好了 我们在这里要做的是 根据 category 给图标提供 正确的 URL 为此 我们在这儿创建一个对象 叫做 iconUrls 好了 这很简单
现在我们就可以用这个对象 来为图标获取 URL
你可能会注意到 这里的有一些条目非常的独特 它们并不是用来显示各种便利设施的 这些标志是用来表示 霸王龙 蜥脚类动物 以及骨骼展厅的
所以 我们需要以不同的方式来处理它们
所以我们要做的是测试名称 如果名称是正确的匹配上了 我们就把对应的图标给显示上去 当然 我们也可以用 feature ID 来实现这件事
我们同时给所有的展品 设置了一个较高的显示优先级 其中霸王龙展览的优先级最高 最后但是也是十分重要的一步 我们还将把展品和 名称的 unicode 字符做一个匹配 是的 你没听错 实际上恐龙和霸王龙 都有它们专属的 unicode 字符
好了 让我们重新加载一下看下地图的渲染效果
现在 这张地图就可以 对得起《Dinoseum》这个名字了 再看这儿 如果我缩到 非常 非常 非常 远的时候 这是我们最后呈现出来的注释 是非常匹配主题的一个恐龙展品的图标
好了 当然 我们也可以 限制用户 让他们不能把比例尺 缩放到非常小 我们可以使用 MapKit JS 中提供的 一个叫做 cameraZoomRange 的设置 来实现这一点 好了 让我们来试一下 看看我们现在还能不能缩小了 看 无法再缩小了 这太棒了 好了 所以 最后一个我们想要在这里 处理掉的就是 occpant 了 请记得 这个部分 的确有点挑战性 因为 occupants 并不包含显示的定位点 但它们和 anchors 对象相连 anchors 是有显示的定位点的 因此 我们可以使用与之关联的 anchor 来获取坐标 以便将 MapKit 注释放在地图上
因此 让我们在 ImdfArchive 类 中实现一个方法 它将所有 occupants 和他们的 anchors 按照特定的序号绑定在一起
好了 所以 这个方法返回一个 对象数组 这个数组包含of objects containing the occupant 和它关联的 那么 让我们用这个来渲染 occupants 吧
就像你在这看到的 我们做的操作和给 amenities 的操作 是非常相似的 不过有一个例外 我们并不将 occupant 它本身提供给 importGeoJSON 这个方法 而提供的是 anchor 因为 anchor 中包含 geometry 我们把 occupant 的名称 设置成 annotation 显示出来的标题 我还基于 category 来设定了一个 特定的类 叫做 category-annotation 这样 我就能给某些特定的 occupants 提供一个特定的样式
我已经在我们的 CSS 文件里 提供了一个样式 好了 现在我们再重新加载一次 好了 现在我们可以看到 Jurassic Table 这是我们一个著名的室内餐厅 现在 让我们回顾下我们都做了什么 首先我们从服务器上 下载 GeoJSON 文件 从中提取各类特征 并按照类别来组织它们
之后 我们实现了一些方法 来获得给定序数后的 对应特征上的特定类型 然后我们添加了一个楼层选择器 以便用户能够 选择它们想要查看的楼层
我们还给空间单元和出入口 提供了特定的样式
最后 我们为便利设施和使用者 创建了图标和 自定义的注释 替换掉了默认的标记注释效果 使用 MapKit JS 我们能够 创建一个和 iOS 版本 具有相同外观和感觉的地图 现在 我们有了一个漂亮的地图 可以把它嵌入到 Dinoseum 的网站里 并在所有的主流浏览器中 给用户们提供 相同的使用体验 关于这一点 我将让 Mithilesh 上台来做个总结
大家好 我希望你们能喜欢你们今天看到的一切
在我们结束今天的讲座之前 我还想介绍一些 适用于室内地图的最佳实践建议
我们先来说说样式
当你设计一个室内地图的时候 应该让它看起来像是你 App 的 自然的延申 你应该使用和你的 App 的主题 或是和你的公司相匹配的颜色及图标
不要尝试去复制 Apple 地图 或者其他 App 的外观效果
取而代之的是你要确保 地图上的覆盖颜色 图标 标签 要匹配你的 App 的视觉效果
给你地图上的不同的特征对象 选择不同的风格的样式
给诸如电梯之类的区域 使用特定的颜色分类 让人们可以一眼就发现它们
使用容易识别的图标
图标不仅在视觉上吸引人 而且给能够 有效的传达其目的
你应该根据缩放级别 调整地图的细节 过多的细节会让地图 看起来杂乱无章 当地图被缩到非常小的时候 展示大的区域 像是房间和建筑物 然后随着地图的放大 逐步添加更多的 细节功能和标签等
你应该在地图里包含一些 周围的区域来提供一些相关的参照 临街的街道 运动场 以及其他地标可以帮助人们 在使用地图时确定方位
并且 最终也是最重要的是 在 iOS 上开启 用户的室内定位 可以让用户在浏览你的场所时 优化他们的使用体验
更多信息 你可以 查看相关的讲座 《What's New in MapKit and MapKit JS》以及 《Introducing the Apple Maps Program》这两场讲座 如果你有任何意见和问题 可以在这场讲座结束后 到我们的实验室来 非常感谢各位 出席这场讲座 希望你们能享受接下来的会议 [掌声]
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。