地理空間錨點是一種錨點,可讓您在現實世界中放置 3D 內容。
地理空間錨點的類型
地理空間錨點有三種類型,每種類型處理高度的方式皆不同:
WGS84 錨點:
WGS84 錨點可用於在任何指定的經緯度及海拔高度上放置 3D 內容。地形錨點:
地形錨點可讓您僅使用經緯度 (且相對於特定位置的地形的高度) 放置內容。 海拔高度是以 VPS 所稱的地面或樓層為依據。屋頂錨點:
屋頂錨點可讓您僅使用經緯度 (相對於該位置建築物屋頂的高度) 放置內容。海拔高度是以街景幾何圖形所稱的建築物頂端為準。如此一來,如果不放置在建築物上,就會預設為地形高度。
WGS84 | 地形 | 屋頂 | |
---|---|---|---|
水平位置 | 緯度, 經度 | 緯度, 經度 | 緯度, 經度 |
垂直位置 | 相對於 WGS84 高度 | 相對於 Google 地圖判定的地形等級 | 相對於 Google 地圖判定的頂樓樓層 |
需要由伺服器解析嗎? | 否 | 是 | 是 |
必要條件
請務必先啟用 Geospatial API 再繼續操作。
地點地理空間錨點
每種錨點類型都有用來建立的專屬 API;詳情請參閱「地理空間錨點類型」一文。
根據命中測試建立錨定廣告
您也可以透過命中測試結果建立地理空間錨點。使用命中測試的 Pose,將其轉換為 GeospatialPose
。用它來放入上述 3 種錨定類型中任一種。
透過 AR 姿勢取得地理空間姿勢
Earth.getGeospatialPose()
提供另一種判斷經緯度的方式,方法是將 AR 姿勢轉換為地理空間姿勢。
從地理空間姿勢取得 AR 姿勢
Earth.getPose()
會將相對於東南方座標框架的地球指定的水平位置、高度和四元數旋轉角度,轉換成與 GL 世界座標相關的 AR 姿勢。
依照您的用途選擇適合的方法
每種建立錨點的方法各不相同,請特別留意:
- 使用街景幾何圖形時,請使用命中測試將內容附加至建築物。
- 優先使用地形或屋頂錨點在 WGS84 錨點上,因為它們會使用 Google 地圖決定的高度值。
判斷地點的經緯度
有三種方式可以計算地點的經緯度:
- 使用地理空間建立者工具,不必實際前往任何地點,也能透過 3D 內容觀看世界並擴張世界。您可以在 Unity 編輯器中使用 Google 地圖,以視覺化方式呈現 3D 沉浸式內容。系統會自動計算內容的經緯度、旋轉和高度。
- 使用 Google 地圖
- 使用 Google 地球。請注意,使用 Google 地球 (而不是 Google 地圖) 取得這些座標時,會產生高達幾公尺的誤差範圍。
- 前往實體位置
使用 Google 地圖
如何使用 Google 地圖取得指定地點的經緯度:
透過電腦前往 Google 地圖。
依序前往「圖層」 >「更多」。
將「地圖類型」變更為「衛星」,並取消勾選畫面左下角的「地球檢視」核取方塊。
這會強制執行 2D 透視,並排除角度 3D 檢視畫面可能出現的錯誤。
在地圖上按一下滑鼠右鍵,然後選取經緯度,即可複製到剪貼簿。
使用 Google 地球
您可以透過 Google 地球計算某地點的經緯度,方法是按一下使用者介面中的某個位置,然後讀取地標詳細資料中的資料。
如何使用 Google 地球取得特定地點的經緯度:
在電腦上前往 Google 地球。
前往漢堡選單 ,然後選取「地圖樣式」。
關閉「3D 建築物」開關。
關閉「3D 建築物」開關後,按一下圖釘圖示 ,即可在所選位置新增地標。
指定要包含地標的專案,然後按一下「儲存」。
在地標的「標題」欄位中,輸入地標的名稱。
按一下專案窗格中的返回箭頭 ,然後選取 「More Actions」選單。
在選單中選擇「匯出為 KML 檔案」。
KLM 檔案會回報 <coordinates>
標記中以半形逗號隔開地標的經緯度和海拔高度,如下所示:
<coordinates>-122.0755182435043,37.41347299422944,7.420342565583832</coordinates>
請勿使用 <LookAt>
標記中的經緯度,因為該標記可指定攝影機位置,而不是位置。
前往實體位置
您可以親自前往當地進行當地觀察,藉此計算特定地點的海拔高度。
取得旋轉四元數
GeospatialPose.getEastUpSouthQuaternion()
會從地理空間位置擷取方向,並輸出四元數,這個四元數表示將向量從目標轉換到東南方 (EUS) 座標系統的旋轉矩陣。東方 X 點以上、Y 點以上,南邊是 Z+ 點。值會依順序 {x, y, z, w}
寫入。
WGS84 錨點
WGS84 Anchor 是一種錨定類型,可讓您以任何指定經緯度和海拔高度放置 3D 內容。要放置在真實世界中,關鍵在於姿勢和方向。位置由 WGS84 座標系統中指定的經緯度和海拔高度組成。方向包含四元數旋轉。
海拔高度是以公尺為單位記錄在參考 WGS84 橢球體上方,因此地面高度「不」為零。您的應用程式負責為每個建立的錨點提供這些座標。
在現實世界中放置 WGS84 錨點
判斷地點的海拔高度
您可以透過下列幾種方式判斷地點放置錨點的海拔高度:
- 如果錨點的位置在使用者接近使用者的位置,您可以採用與使用者裝置高度類似的海拔高度。
- 取得經緯度後,請使用 Elevation API 取得根據 EGM96 規格的海拔高度。您必須將 Maps API EGM96 高度轉換為 WGS84,才能與
GeospatialPose
高度進行比較。請參閱同時擁有指令列和 HTML 介面的 GeoidEval。Maps API 會根據 WGS84 規格回報經緯度。 - 您可以透過 Google 地球取得特定地點的經緯度和海拔高度。這樣就能收到高達數公尺的誤差範圍。在 KML 檔案中使用
<coordinates>
標記的經緯度和海拔高度,而「不是」<LookAt>
標記。 - 如果現有錨點相近 且您不在陡峭的斜坡上,或許可以使用相機
GeospatialPose
的海拔高度,而無需使用其他來源,例如 Maps 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 會產生相關聯的 FutureState
,請參閱上方的表格。
查看 Future 結果的屋頂錨點狀態
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. } }
後續步驟
- 請務必瞭解地理空間的 API 使用配額。