使用地理空間錨點在 Android SDK (Kotlin/Java) 上定位實際內容

地理空間錨定是錨定的一種,用來在現實世界中放置 3D 內容。

地理空間錨點類型

地理空間錨點有三種,每種類型處理海拔高度的方式皆不同:

  1. WGS84 錨點
    您可使用 WGS84 錨定標記在任何經緯度放置 3D 內容 經度和海拔高度

  2. 地形錨點
    地形錨點可以僅使用經緯度 經度與該位置的地形相對的高度。 海拔高度是相對於地面或樓層的已知高度 上傳者:VPS

  3. 屋頂錨點
    透過屋頂錨定標記,您可以只使用緯度和 相對於建築物屋頂的高度。 海拔高度是相對於建築物頂端的位置 (稱為 製作者:Streetscape Geometry。 未放置在建築物上時,系統預設會使用地形海拔高度。

WGS84 地形 屋頂
水平位置 緯度, 經度 緯度, 經度 緯度, 經度
垂直位置 相對於 WGS84 高度 相對於 Google 地圖所決定地形等級的比較 相對於 Google 地圖判定的屋頂樓層
需要由伺服器解決嗎?

必要條件

請務必先啟用 Geospatial API,再繼續操作。

地點地理空間錨點

每個錨定類型都有專屬 API 來建立這些錨定標記;詳情請參閱「地理空間錨點類型」。

根據命中測試建立錨定廣告

您也可以透過命中測試結果建立地理空間錨點。 使用命中測試中的 Pose 轉換為 GeospatialPose。可用來放置上述 3 種錨定類型。

透過 AR 姿勢取得地理空間姿勢

Earth.getGeospatialPose() 可將 AR Pose 轉換為地理空間表,提供另一種判斷經緯度的方式。

從地理空間姿勢取得 AR 姿勢

Earth.getPose() 會根據 GL 世界座標,將地球指定的水平位置、海拔高度和四元旋轉,對應到 GL 世界座標的 AR Pose。

選擇符合您用途的方法

每個建立錨點的方法都需留意相關優缺點:

  • 使用「Streetscape 幾何圖形」時 請使用點擊測試將內容附加到建築物上
  • 優先使用 Terrain 或 Rooftop 錨點,而不是 WGS84 錨點,因為它們使用 Google 地圖判定的海拔高度值。

判斷某個位置的經緯度

計算地點經緯度的方式有三種:

  • 善用地理空間創作者,透過 3D 內容飽覽並擴增世界,完全不必親臨當地。這可讓您在 Unity 編輯器中使用 Google 地圖,以視覺化方式放置 3D 沉浸式內容。並自動計算內容的緯度、經度、旋轉和高度。
  • 使用 Google 地圖
  • 使用 Google 地球。請注意,使用「Google 地球」取得這些座標時,您得到的誤差範圍只有幾公尺,而非「Google 地圖」。
  • 前往實際位置

使用 Google 地圖

如何透過 Google 地圖取得地點的經緯度:

  1. 透過電腦前往 Google 地圖

  2. 前往 [圖層] >更多

  3. 將 [地圖類型] 變更為 [衛星],並取消勾選畫面左下角的 [地球視圖] 核取方塊。

    這會強制採用 2D 視角,並消除 3D 檢視中可能產生的錯誤。

  4. 在地圖上,用滑鼠右鍵按一下該地點,然後選取經緯度,即可複製到剪貼簿。

使用 Google 地球

只要在使用者介面中按一下某個位置,即可讀取地標詳細資料的資料,在 Google 地球中計算位置的經緯度。

如要透過 Google 地球取得特定地點的經緯度,請按照下列步驟操作:

  1. 在電腦上前往 Google 地球

  2. 前往漢堡選單 ,然後選取「地圖樣式」

  3. 將「3D 建築物」切換按鈕設為關閉。

  4. 關閉「3D 建築物」開關後,按一下圖釘圖示 ,即可在所選位置新增地標。

  5. 指定要包含地標的專案,然後按一下「儲存」

  6. 在地標的「標題」欄位中,輸入地標名稱。

  7. 按一下專案窗格中的返回箭頭 ,然後選取 「More Actions」選單。

  8. 從選單中選擇「匯出成 KML 檔案」

KLM 檔案會回報 <coordinates> 標記中地標的經緯度和海拔高度 (以半形逗號分隔),如下所示:

<coordinates>-122.0755182435043,37.41347299422944,7.420342565583832</coordinates>

請「不要」使用 <LookAt> 標記中的經緯度;這些標記指定了攝影機位置,而非位置。

前往實際位置

如要計算某地點的海拔高度,請實際前往現場進行當地觀察。

取得旋轉四元數

GeospatialPose.getEastUpSouthQuaternion() 從地理空間 Pose 擷取方向,並輸出代表旋轉矩陣,將向量從目標轉換為東南 (EUS) 座標系統。X+ 點往東、Y+ 點向上,以及 Z+ 點朝南。值會依 {x, y, z, w} 順序寫入。

WGS84 錨點

WGS84 Anchor 是一種錨點,可讓您在任何指定的經緯度及高度位置放置 3D 內容。這需要先靠姿勢和方向,才能擺放在現實世界中。位置包含經緯度和海拔高度,(於 WGS84 座標系統中指定)。方向包含四元數旋轉。

海拔高度值於參考 WGS84 橢圓球體上方以公尺為單位,因此地面等級「不是」0。您的應用程式負責為每個已建立的錨點提供這些座標。

在現實世界中放置 WGS84 錨點

判斷位置的海拔高度

您可以透過下列幾種方式判斷位置的海拔高度:

  • 如果錨點的位置與使用者裝置的海拔高度相近,您可以使用與使用者裝置的高度相似的海拔高度,
  • 取得經緯度後,您可以使用 Elevation API,根據 EGM96 規格取得海拔高度。如要與 GeospatialPose 高度進行比較,您必須將 Maps API EGM96 高度轉換成 WGS84。請參閱含有指令列和 HTML 介面的 GeoidEval。Maps API 會根據內建的 WGS84 規格,回報經緯度。
  • 你可以從 Google 地球取得特定地點的經緯度。所提供的誤差範圍最多可達數公尺。請使用 KML 檔案中的 <coordinates> 標記 (「不是」<LookAt> 標記),使用經緯度和海拔高度。
  • 如果現有錨點在靠近附近不是陡坡,即使不使用其他來源 (例如 Maps API),也可以使用相機 GeospatialPose 中的海拔高度。

建立錨定標記

取得緯度、經度、高度和旋轉四元後,即可使用 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 作業已取消。

查看未來結果的地形錨點狀態

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。然後,您可以查看 Future 結果,以存取 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 會有相關聯的 FutureState,請參閱上文表格

檢查未來結果的屋頂錨點狀態

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.
  }
}

後續步驟