地理空间锚点是一种锚点,可让您将 3D 内容放置在现实世界中。
地理空间锚点类型
地理空间锚点有三种类型,每种类型处理海拔的方式各不相同:
WGS84 锚点:
借助 WGS84 锚点,您可以将 3D 内容放置在任何给定的纬度、经度和海拔高度。地形锚点:
借助地形锚点,您只需使用纬度和经度以及相对于该位置地形的高度即可放置内容。海拔是相对于 VPS 所知的地面或地板确定的。屋顶锚点:
屋顶锚点可让您仅使用纬度和经度放置内容,且高度相对于建筑物屋顶在该位置。海拔高度是以 Streetscape Geometry 已知的建筑物顶部为参照物的。如果未放置在建筑物上,则此值将默认为地形海拔。
WGS84 | 地形 | 屋顶 | |
---|---|---|---|
水平位置 | 纬度,经度 | 纬度,经度 | 纬度,经度 |
垂直位置 | 相对于 WGS84 海拔 | 相对于 Google 地图确定的地形高度 | 相对于由 Google 地图确定的屋顶楼层 |
需要由服务器解决? | 否 | 是 | 是 |
前提条件
请务必先启用 Geospatial API,然后再继续操作。
放置地理空间锚点
每种锚点类型都有用于创建它们的专用 API;如需了解详情,请参阅地理空间锚点的类型。
根据点击测试创建锚点
您还可以根据点击测试结果创建地理空间锚点。使用来自碰撞测试的姿势,并将其转换为 GeospatialPose
。您可以使用它放置上述 3 种锚点类型中的任意一种。
从 AR 姿势获取地理空间姿势
Earth.getGeospatialPose()
提供了另一种确定纬度和经度的方法,即将 AR 姿势转换为地理空间姿势。
从地理空间姿态获取 AR 姿态
Earth.getPose()
会将相对于东上南坐标系的地球指定水平位置、海拔和四元数旋转转换为相对于 GL 世界坐标的 AR 姿势。
选择适合您的使用场景的方法
请注意,创建锚点的每种方法都有相关的权衡:
- 使用 Streetscape Geometry 时,请使用点击测试将内容附加到建筑物。
- 首选地形锚点或屋顶锚点,而不是 WGS84 锚点,因为它们使用由 Google 地图确定的海拔高度值。
确定地理位置的纬度和经度
您可以通过以下三种方式计算地点的纬度和经度:
- 使用 Geospatial Creator 即可查看世界并通过 3D 内容对其进行增强,而无需亲自前往相应地点。这样,您就可以在 Unity 编辑器中使用 Google 地图直观地放置 3D 沉浸式内容。系统会自动为您计算内容的纬度、经度、旋转度和海拔。
- 使用Google地图
- 使用 Google 地球。请注意,使用 Google 地球(而非 Google 地图)获取这些坐标时,误差范围最多可达几米。
- 前往实际营业地点
使用Google地图
如需使用 Google 地图获取某个位置的纬度和经度,请执行以下操作:
在桌面设备上访问 Google 地图。
依次转至图层 > 更多。
将地图类型更改为卫星,然后取消选中屏幕左下角的地球仪视图复选框。
这将强制使用 2D 透视图,并消除可能因角度 3D 视图而产生的错误。
在地图上,右键点击该地点,然后选择经度/纬度即可将其复制到剪贴板。
使用 Google 地球
您可以通过以下方式在 Google 地球中计算某个地点的纬度和经度:在界面中点击相应地点,然后读取地图注点详细信息中的数据。
如需使用 Google 地球获取某个地点的经纬度,请执行以下操作:
在桌面计算机上打开 Google 地球。
找到汉堡形菜单 ,然后选择地图样式。
关闭 3D 建筑开关。
关闭 3D 建筑开关后,点击图钉图标 即可在所选位置添加地图注点。
指定要包含地标的项目,然后点击保存。
在相应地标的标题字段中,输入地标的名称。
点击项目窗格中的返回箭头 ,然后选择 更多操作菜单。
从菜单中选择导出为 KML 文件。
KLM 文件会在 <coordinates>
标记中报告地标的纬度、经度和海拔高度(以英文逗号分隔),如下所示:
<coordinates>-122.0755182435043,37.41347299422944,7.420342565583832</coordinates>
请勿使用 <LookAt>
标记中的纬度和经度,因为它们指定的是相机位置,而不是地点。
前往实际位置
您可以亲自前往某个地点并进行当地观测,以计算该地点的海拔。
获取旋转四元数
GeospatialPose.getEastUpSouthQuaternion()
从地理空间姿势中提取方向,并输出一个四元数,该四元数表示将向量从目标向东上南 (EUS) 坐标系转换的旋转矩阵。X+ 指向东,Y+ 指向上,Z+ 指向南。值的写入顺序为 {x, y, z, w}
。
WGS84 锚点
WGS84 锚点是一种锚点,允许您将 3D 内容放置在任何给定的纬度、经度和高度。它依赖于姿态和方向,才能放置在现实世界中。该位置由 WGS84 坐标系中指定的纬度、经度和海拔组成。方向由四元数旋转组成。
海拔高度是以高于参考 WGS84 椭球体的米为单位报告的,因此,地平面不为零。您的应用负责为每个创建的锚点提供这些坐标。
在现实世界中放置 WGS84 锚点
确定地点的海拔高度
可通过以下几种方法确定位置以放置锚点时的海拔高度:
- 如果锚点的位置在用户附近,您可以使用与用户设备的海拔高度相似的海拔高度。
- 获取纬度和经度后,使用 Elevation API 根据 EGM96 规范获取海拔。您必须将 Maps API EGM96 海拔转换为 WGS84,以便与
GeospatialPose
海拔进行比较。请参阅同时具有命令行和 HTML 界面的 GeoidEval。Maps API 开箱即用,可根据 WGS84 规范报告纬度和经度。 - 您可以通过 Google 地球获取某个地点的纬度、经度和海拔。这会导致误差范围最多达到几米。使用 KML 文件中
<coordinates>
标记(而非<LookAt>
标记)中的纬度、经度和海拔。 - 如果附近有现有锚点,并且您不在陡坡上,则可以使用相机的
GeospatialPose
中的海拔高度,而无需使用其他来源(例如 Google 地图 API)。
创建锚点
获得纬度、经度、海拔和旋转四元数后,使用 Earth.createAnchor()
将内容锚定到您指定的地理坐标。
Java
if (earth != null && earth.getTrackingState() == TrackingState.TRACKING) { Anchor anchor = earth.createAnchor( /* Location values */ latitude, longitude, altitude, /* Rotational pose values */ qx, qy, qz, qw); // Attach content to the anchor specified by geodetic location and pose. }
Kotlin
if (earth.trackingState == TrackingState.TRACKING) { val anchor = earth.createAnchor( /* Location values */ latitude, longitude, altitude, /* Rotational pose values */ qx, qy, qz, qw ) // Attach content to the anchor specified by geodetic location and pose. }
地形锚点
地形锚点是一种锚点,可让您仅使用纬度和经度来放置 AR 对象,并利用 VPS 中的信息来查找相对于地面的确切海拔。
您需要提供相对于地形的高度,而不是输入所需的高度。如果此值为零,则锚点将与地形齐平。
设置飞机查找模式
平面检测是可选操作,无需进行此操作即可使用锚点。请注意,仅使用水平平面。水平平面有助于地形锚点在地面上动态对齐。
使用 Config.PlaneFindingMode
选择应用检测飞机的方式。
使用新的 Async API 创建地形锚点
如需创建和放置地形锚点,请调用 Earth.resolveAnchorOnTerrainAsync()
。
锚点不会立即就绪,需要解析。问题解决后,您将可以在 ResolveAnchorOnTerrainFuture
中看到该应用。
Java
final ResolveAnchorOnTerrainFuture future = earth.resolveAnchorOnTerrainAsync( latitude, longitude, /* altitudeAboveTerrain= */ 0.0f, qx, qy, qz, qw, (anchor, state) -> { if (state == TerrainAnchorState.SUCCESS) { // do something with the anchor here } else { // the anchor failed to resolve } });
Kotlin
var future = earth.resolveAnchorOnTerrainAsync( latitude, longitude, altitudeAboveTerrain, qx, qy, qz, qw, { anchor, state -> if (state == TerrainAnchorState.SUCCESS) { // do something with the anchor here } else { // the anchor failed to resolve } } )
了解未来状况
Future 将有一个关联的 FutureState
。
州 | 说明 |
---|---|
FutureState.PENDING |
该操作仍处于待处理状态。 |
FutureState.DONE |
操作已完成,且结果可供获取。 |
FutureState.CANCELLED |
此操作已取消。 |
检查 Future 结果的地形锚点状态
Anchor.TerrainAnchorState
属于异步操作,是最终 Future 结果的一部分。
Java
switch (terrainAnchorState) { case SUCCESS: // A resolving task for this Terrain anchor has finished successfully. break; case ERROR_UNSUPPORTED_LOCATION: // The requested anchor is in a location that isn't supported by the Geospatial API. break; case ERROR_NOT_AUTHORIZED: // An error occurred while authorizing your app with the ARCore API. See // https://developers.google.com/ar/reference/java/com/google/ar/core/Anchor.TerrainAnchorState#error_not_authorized // for troubleshooting steps. break; case ERROR_INTERNAL: // The Terrain anchor could not be resolved due to an internal error. break; default: // not reachable break; }
Kotlin
when (state) { TerrainAnchorState.SUCCESS -> { // A resolving task for this Terrain anchor has finished successfully. } TerrainAnchorState.ERROR_UNSUPPORTED_LOCATION -> { // The requested anchor is in a location that isn't supported by the Geospatial API. } TerrainAnchorState.ERROR_NOT_AUTHORIZED -> { // An error occurred while authorizing your app with the ARCore API. See // https://developers.google.com/ar/reference/java/com/google/ar/core/Anchor.TerrainAnchorState#error_not_authorized // for troubleshooting steps. } TerrainAnchorState.ERROR_INTERNAL -> { // The Terrain anchor could not be resolved due to an internal error. } else -> { // Default. } }
屋顶锚点
屋顶锚点是一种锚点,与上文中的地形锚点非常相似。不同之处在于,您需要提供屋顶以上的海拔高度,而不是地形上方的高度。
使用新的 Async API 创建屋顶锚点
锚点不会立即就绪,需要解析。
如需创建并放置 Rooftop 锚点,请调用 Earth.resolveAnchorOnRooftopAsync()
。与地形锚点类似,您也将访问 Future 的 FutureState
。然后,您可以查看“未来”结果以访问 Anchor.RooftopAnchorState
。
Java
final ResolveAnchorOnRooftopFuture future = earth.resolveAnchorOnRooftopAsync( latitude, longitude, /* altitudeAboveRooftop= */ 0.0f, qx, qy, qz, qw, (anchor, state) -> { if (state == RooftopAnchorState.SUCCESS) { // do something with the anchor here } else { // the anchor failed to resolve } });
Kotlin
var future = earth.resolveAnchorOnRooftopAsync( latitude, longitude, altitudeAboveRooftop, qx, qy, qz, qw, { anchor, state -> if (state == RooftopAnchorState.SUCCESS) { // do something with the anchor here } else { // the anchor failed to resolve } } )
查看 Future 的状态
Future 将有一个关联的 FutureState
,请参阅上文中的表格。
检查 Future 结果的 Rooftop 锚点状态
Anchor.RooftopAnchorState
属于异步操作,是最终 Future 结果的一部分。
Java
switch (rooftopAnchorState) { case SUCCESS: // A resolving task for this Rooftop anchor has finished successfully. break; case ERROR_UNSUPPORTED_LOCATION: // The requested anchor is in a location that isn't supported by the Geospatial API. break; case ERROR_NOT_AUTHORIZED: // An error occurred while authorizing your app with the ARCore API. // https://developers.google.com/ar/reference/java/com/google/ar/core/Anchor.RooftopAnchorState#error_not_authorized // for troubleshooting steps. break; case ERROR_INTERNAL: // The Rooftop anchor could not be resolved due to an internal error. break; default: // not reachable break; }
Kotlin
when (state) { RooftopAnchorState.SUCCESS -> { // A resolving task for this Rooftop anchor has finished successfully. } RooftopAnchorState.ERROR_UNSUPPORTED_LOCATION -> { // The requested anchor is in a location that isn't supported by the Geospatial API. } RooftopAnchorState.ERROR_NOT_AUTHORIZED -> { // An error occurred while authorizing your app with the ARCore API. See // https://developers.google.com/ar/reference/java/com/google/ar/core/Anchor.RooftopAnchorState#error_not_authorized // for troubleshooting steps. } RooftopAnchorState.ERROR_INTERNAL -> { // The Rooftop anchor could not be resolved due to an internal error. } else -> { // Default. } }
后续步骤
- 请务必了解 Geospatial API 使用配额。