地理空間アンカーを使用して iOS で実際のコンテンツを配置する

ジオ空間アンカーは、3D コンテンツを現実世界に配置できるアンカーの一種です。

地理空間アンカーの種類

地理空間アンカーには 3 つのタイプがあり、高度の扱い方はそれぞれ異なります。

  1. WGS84 アンカー:
    WGS84 アンカーを使用すると、任意の緯度、経度、高度に 3D コンテンツを配置できます。

  2. 地形アンカー:
    地形アンカーを使用すると、緯度と経度のみを使用してコンテンツを配置し、その位置の地形に対する高さを指定できます。高度は、VPS で知られている地面または床に対して測定されます。

  3. 屋上アンカー:
    屋上アンカーを使用すると、緯度と経度のみを使用してコンテンツを配置できます。その位置の建物の屋上に対する高さも指定できます。標高は、ストリートビュー ジオメトリにより、建物の最上部を基準に決定されます。建物に配置されていない場合は、デフォルトで地形の高さに設定されます。

WGS84 地形 屋上
水平方向 緯度、経度 緯度、経度 緯度、経度
垂直方向 WGS84 高度に相対 Google マップで決定された地形レベルでの相対パス Google マップによって決定された屋上レベルからの相対値
サーバー側で解決する必要があるか いいえ はい

前提条件

続行する前に、Geospatial API を有効にしてください。

地理空間アンカーを配置する

各アンカータイプには、作成専用の API があります。詳細については、地理空間アンカーの種類をご覧ください。

ヒットテストからアンカーを作成する

ヒットテスト結果から地理空間アンカーを作成することもできます。ヒットテストの変換を使用して、GARGeospatialTransform に変換します。これを使用して、前述の 3 種類のアンカーを配置できます。

AR 変換から地理空間変換を取得する

GARSession.geospatialTransformFromTransform:error: は、AR 変換を地理空間変換に変換することで、緯度と経度を決定する追加の方法を提供します。

地理空間変換から AR 変換を取得する

GARSession.transformFromGeospatialCoordinate:altitude:eastUpSouthQTarget:error: は、東が上、南が下、北が左の座標フレームに対する地球で指定された水平位置、高度、クォータニオン回転を、GL ワールド座標に対する AR 変換に変換します。

ユースケースに適した方法を選択する

アンカーを作成する方法にはそれぞれトレードオフが伴います。

  • 街並みのジオメトリを使用する場合は、ヒットテストを使用して建物にコンテンツを配置します。
  • WGS84 アンカーよりも、地形アンカーまたは屋上アンカーを使用することをおすすめします。これらのアンカーは、Google マップによって決定された高度値を使用します。

場所の緯度と経度を特定する

場所の緯度と経度は、次の 3 つの方法で計算できます。

  • Geospatial Creator を使用すると、実際にその場所に移動しなくても、3D コンテンツで世界を表示して拡張できます。これにより、Unity エディタで Google マップを使用して、没入感のある 3D コンテンツを視覚的に配置できます。コンテンツの緯度、経度、回転、高度が自動的に計算されます。
  • Googleマップを使用
  • Google Earth を使用します。Google マップではなく Google Earth を使用して座標を取得する場合、誤差は最大で数メートルになる可能性があります。
  • 物理的な場所に移動する

Googleマップを使用

Google マップを使用して場所の緯度と経度を取得するには:

  1. パソコンで Google マップにアクセスします。

  2. [レイヤ] > [その他] に移動します。

  3. [地図の種類] を [航空写真] に変更し、画面左下にある [地球ビュー] チェックボックスをオフにします。

    これにより、2D の視点が強制され、角度のある 3D ビューから生じる可能性のあるエラーを排除できます。

  4. 地図上で場所を右クリックし、経度と緯度を選択してクリップボードにコピーします。

Google Earth を使用する

Google Earth で場所の緯度と経度を計算するには、UI で場所をクリックし、目印の詳細からデータを読みます。

Google Earth を使用して場所の緯度と経度を取得するには:

  1. パソコンで Google Earth にアクセスします。

  2. ハンバーガー メニュー に移動し、[地図のスタイル] を選択します。

  3. [建物の 3D 表示] スイッチをオフにします。

  4. [建物の 3D 表示] スイッチをオフにしたら、ピンアイコン をクリックして、選択した場所に地図マーカーを追加します。

  5. 目印を含むプロジェクトを指定して、[保存] をクリックします。

  6. 目印の [タイトル] フィールドに、目印の名前を入力します。

  7. プロジェクト ペインの戻る矢印 をクリックし、 [その他の操作] メニューを選択します。

  8. メニューから [Export as KML file] を選択します。

KLM ファイルでは、プレースマークの緯度、経度、高度が <coordinates> タグでカンマ区切りで報告されます。次に例を示します。

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

<LookAt> タグの緯度と経度は、場所ではなくカメラの位置を指定するものとして使用しないでください

物理的な場所に移動する

場所の高度は、実際にその場所に行き、現地で測定することで計算できます。

回転四元数を取得する

GARGeospatialTransform.eastUpSouthQTarget は、地理空間変換から向きを抽出し、ベクトルをターゲットから東北南(EUS)座標系に変換する回転行列を表すクォータニオンを出力します。X+ は東、Y+ は上、Z+ は南を指します。値は {x, y, z, w} の順序で書き込まれます。

WGS84 アンカー

WGS84 アンカーは、任意の緯度、経度、高度に 3D コンテンツを配置できるアンカーの一種です。現実世界に配置するには、変換と向きを使用します。位置は、WGS84 座標系で指定される緯度、経度、高度で構成されます。向きはクォータニオン回転で構成されます。

高度は、地面レベルがゼロではないように、基準となる WGS84 楕円体から上のメートルで報告されます。アプリは、作成した各アンカーのこれらの座標を提供する役割を担います。

現実世界に WGS84 アンカーを配置する

場所の標高を特定する

アンカーを配置する場所の高さを特定する方法はいくつかあります。

  • アンカーの位置がユーザーの近くにある場合は、ユーザーのデバイスの高度に近い高度を使用できます。
  • 緯度と経度を取得したら、Elevation API を使用して EGM96 仕様に基づいて高度を取得します。標高 GARGeospatialTransform と比較するには、Maps API EGM96 の高度を WGS84 に変換する必要があります。コマンドラインと HTML インターフェースの両方がある GeoidEval をご覧ください。Maps API は、WGS84 仕様に従って緯度と経度をデフォルトで報告します。
  • 場所の緯度、経度、高度は Google Earth で取得できます。これにより、最大数 m の誤差が生じます。KML ファイルの <LookAt> タグではなく、<coordinates> タグの緯度、経度、高度を使用します。
  • 既存のアンカーが近くにあり、急斜面でない場合は、Maps API などの別のソースを使用せずに、カメラの GARGeospatialTransform から高度を使用できる場合があります。

アンカーを作成する

緯度、経度、高度、回転クォータニオンを取得したら、createAnchorWithCoordinate:altitude:eastUpSouthQAnchor:error: を使用して、指定した地理座標にコンテンツを固定します。

  NSError *error = nil;
  GARAnchor *anchor = [self.garSession createAnchorWithCoordinate:coordinate
                                                         altitude:altitude
                                               eastUpSouthQAnchor:eastUpSouthQAnchor
                                                            error:&error];

地形アンカー

地形アンカーはアンカーの一種で、緯度と経度のみを使用して AR オブジェクトを配置できます。VPS の情報を利用して、地面からの正確な高度を特定できます。

目的の高度を入力するのではなく、地形からの高度を指定します。0 に設定すると、アンカーは地形と同じ高さになります。

飛行機の検出モードを設定する

飛行機の検出は任意であり、アンカーを利用するために必須ではありません。水平面のみが使用されます。水平面は、地面上の地形アンカーの動的な配置に役立ちます。

ARWorldTrackingConfiguration.PlaneDetection を使用して、アプリで飛行機を検出する方法を選択します。

新しい Async API を使用して地形アンカーを作成する

地形アンカーを作成して配置するには、GARSession.createAnchorWithCoordinate:altitudeAboveTerrain:eastUpSouthQAnchor:completionHandler:error: を呼び出します。

アンカーはすぐには使用できるわけではないため、解決する必要があります。解決すると、GARCreateAnchorOnTerrainFuture に表示されます。

GARCreateAnchorOnTerrainFuture *future = [self.garSession createAnchorWithCoordinate:coordinate
                                                                altitudeAboveTerrain:altitude
                                                                  eastUpSouthQAnchor:eastUpSouthQTarget
                                                                   completionHandler:^(GARAnchor *anchor, GARTerrainAnchorState state) {
                                                                     // handle completion
                                                                   }
                                                                               error:&error];

将来の状況をチェック

Future には GARFutureState が関連付けられます。

説明
GARFutureStatePending オペレーションはまだ保留中です。
GARFutureStateDone オペレーションが完了し、結果が利用可能になります。
GARFutureStateCancelled オペレーションがキャンセルされました。

将来の結果の地形アンカーの状態を確認する

GARTerrainAnchorState は非同期オペレーションに属し、最終的な Future 結果の一部です。

switch (future.resultTerrainAnchorState) {
  case GARTerrainAnchorStateSuccess:
    // Terrain anchor finished resolving.
    break;
  case GARTerrainAnchorStateErrorUnsupportedLocation:
    // The requested anchor is in a location that isn't supported by the Geospatial API.
    break;
  case GARTerrainAnchorStateErrorNotAuthorized:
    // An error occurred while authorizing your app with the ARCore API. See
    // https://developers.google.com/ar/reference/ios/group/GARTerrainAnchorState#garterrainanchorstateerrornotauthorized
    // for troubleshooting steps.
    break;
  case GARTerrainAnchorStateErrorInternal:
    // The Terrain anchor could not be resolved due to an internal error.
    break;
  default:
    break;
}

屋上アンカー

屋上アンカーのヒーロー画像

屋上アンカーはアンカーの一種で、上記の地形アンカーとよく似ています。違いは、地面からの標高ではなく、屋上からの標高を指定することです。

新しい Async API を使用して屋上アンカーを作成する

アンカーはすぐには使用できるわけではないため、解決する必要があります。

屋上アンカーを作成して配置するには、GARSession.createAnchorWithCoordinate:altitudeAboveRooftop:eastUpSouthQAnchor:completionHandler:error: を呼び出します。地形アンカーと同様に、Future の GARFutureState にもアクセスできます。その後、[将来の結果] を確認して GARRooftopAnchorState にアクセスできます。

GARCreateAnchorOnRooftopFuture *future = [self.garSession createAnchorWithCoordinate:coordinate
                                                                altitudeAboveRooftop:altitude
                                                                  eastUpSouthQAnchor:eastUpSouthQTarget
                                                                   completionHandler:^(GARAnchor *anchor, GARRooftopAnchorState state) {
                                                                     // handle completion
                                                                   }
                                                                               error:&error];

将来の状況をチェック

将来の値には GARFutureState が関連付けられます(上記のを参照)。

将来の結果の屋上アンカーの状態を確認する

GARRooftopAnchorState は非同期オペレーションに属し、最終的な Future 結果の一部です。

switch (future.resultRooftopAnchorState) {
  case GARRooftopAnchorStateSuccess:
    // Rooftop anchor finished resolving.
    break;
  case GARRooftopAnchorStateErrorUnsupportedLocation:
    // The requested anchor is in a location that isn't supported by the Geospatial API.
    break;
  case GARRooftopAnchorStateErrorNotAuthorized:
    // An error occurred while authorizing your app with the ARCore API. See
    // https://developers.google.com/ar/reference/ios/group/GARRooftopAnchorState#garrooftopanchorstateerrornotauthorized
    // for troubleshooting steps.
    break;
  case GARRooftopAnchorStateErrorInternal:
    // The Rooftop anchor could not be resolved due to an internal error.
    break;
  default:
    break;
}

次のステップ