本書は「技術好き学生支援コミュニティ」Iwaken Lab.1の有志メンバー4名が「各々が好きな技術の記事を書く」をコンセプトに執筆を始めました。その結果、XR中心の内容に仕上がりました。
XR分野は新しい分野がゆえに、情熱がある同世代の開発仲間を作りづらく、孤独を感じながら開発している学生が多く存在します。Iwaken Lab.は技術に対する情熱と孤独感を抱えた学生が集まってできたコミュニティです。本書を通じて、執筆者の「好き」が伝わり、有志の輪が広がっていくと嬉しいです。
Iwaken Lab.主催 / イワケン (@iwaken71)
Iwaken Lab.は、イワケンこと岩﨑謙汰個人が主催する「技術好き学生支援コミュニティ」です。最初は、熱量ある学生を活躍させたいという想いから始まりました。ビジョンとして「技術好きな『個』が活躍するキーパーソンコミュニティ」を掲げ、「好きな技術で社会インパクトを与える」をミッションとして各々のメンバーが自走的に活動しています。2021年5月にスタートし、2023年10月現在、49名のメンバーと5名の社会人メンターが所属しています。
・XR(VR/AR/MR)
・フォトグラメトリ/NeRF
・AI(AIキャラクター)
・ロボット/IoT
・3DCG/Blender/Houdini
など、3D分野を切り口に幅広い分野のメンバーが集まっています。週に1回少人数(5~30名)のオンライン集会を開催し、過去100回以上開催。技術に関する交流やディスカッションを行ってきました。学生メンバーと一緒に、招待制Discordでの活動共有へのフィードバック、技術ニュースシェア、共同プロジェクトの進行などを行っています。毎週土曜日のオンライン集会は気軽に見学できますので、興味がある方はIwaken Lab.のホームページ(https://iwakenlab.jp/)を見ていただいて、イワケンのTwitter/X @iwaken71までご連絡ください。
本書に記載された内容は、情報の提供のみを目的としています。したがって、本書を用いた開発、製作、運用は、必ずご自身の責任と判断によって行ってください。これらの情報による開発、製作、運用の結果について、著者はいかなる責任も負いません。
2022年5月にGoogle I/Oで発表されたARCore Geospatial API(以下Geospatial API)は、AR界隈で大きく話題になりました。Geospatial APIを使った位置合わせでは緯度経度を使うため、マップサービスや地理空間情報との連携を試みた方は少なくないのではないでしょうか。本章では、地理空間情報のひとつであるProject PLATEAU(以下PLATEAU)の建物モデルと組み合わせる際に必要になるであろう平面直角座標系についてご紹介します。
本章の執筆をいたします、にー兄さん1と申します。UnityやWebでAR開発をするのが好きなソフトウェアエンジニアです。
本章では「平面直角座標系」の話をします。座標系といっても色々ありますが、せっかくなので最近話題のGeospatial APIに絡めた話をしようと考えました。本章を通して、Unityのような直交座標系がベースのプラットフォームで世界測地系の位置情報を扱う方法のイメージを持っていただければ幸いです。
また、本章ではGeospatial APIを使ったARアプリ開発の始め方については詳しく触れません。あくまでも、メインは座標系の話にしたいと考えております。
本章では、次に挙げるような方を読者として想定した内容になっております。必ずしも当てはまる必要はありませんが、分野などが違うと話がわかりにくい可能性がありますのでご了承ください。
・PLATEAUやGeospatial APIについて知っている方
・上記ふたつを組み合わせて使ってみたい方
・Geospatial APIを触り始めたが緯度経度高さでの指定で苦戦したことがある / 苦しんでいる方
・PLATEAUのfbxモデル原点が、なぜ遠くになるのかを知りたい方
また、本章では次のような知識・経験がある前提での内容になっております。
・Unityの基礎知識
・PLATEAUやGeospatial APIについての理解
・Geosptial APIを使ったサンプルアプリなどを動かしたり、コードリーディングしたりといった経験
・PLATEAUについて知っていて、中のデータを見たことがある
Geospatial API2ついて簡単に説明します。すでにGeospatial APIがどのようなものかをご存知の方は、読み飛ばしていただいて構いません。
このAPIは名前にあるとおり、ARCoreというARフレームワークの機能として提供されているVPSです。Geospatial APIはGoogleストリートビューやGoogle EarthなどのデータをもとにVPSマップが作成されているため、ストリートビューが使える場所・地域で位置合わせができます。そのため、従来VPSはその場所に行ってスキャンをすることでVPSマップを作成していたのに対し、Geospatial APIの場合はスキャンの必要がなく、すぐに位置合わせが可能という特徴があります。
そのような特徴から、Geospatial APIはすでに地球規模で位置合わせが可能といっても過言ではありません。そして、地球規模なのはVPSマップの利用範囲だけではなく、アンカーの座標系もです。たとえばImmersalのようなVPSだと、スキャンしたVPSマップの原点座標を中心として位置合わせの結果を返します。Azure Spatial AnchorsのようなVPSは、設置したアンカー座標がワールド座標のどこにあるのかを返却します。Azure Spatial Anchors同様、Geospatial APIも、返却されるのはアンカーのワールド座標ですが、そのアンカーは緯度・軽度・高さの情報によって設置されることになります。
加えて、Geospatial APIを使うと、ユーザーの位置情報を高精度に測位可能です。従来の位置同定で使われるGPSでは屋外のひらけた場所でも数メートルの誤差があるのに対して、Geospatial APIは利用可能な範囲だと数十センチの誤差に収まるのが一般的です。自分がよく位置合わせのテストで使う「つくば駅」では、いつも30cm~60cmほどの誤差になっていることが多いですね。このように、ユーザーの位置を地球規模で測位可能なため、設置するアンカーの位置も地球を基準とできるのです。
Project PLATEAUの説明を公式Webページ3から引用すると、「国土交通省が主導する、日本全国の3D都市モデルの整備・活用・オープンデータ化プロジェクト」という説明になるらしいです。東京都をはじめ、全国56都市(2022年8月 執筆時点)で都市の3Dモデルを作成し、そのデータをOBJ・FBX・CityGML・3DTilesなどの形式で公開しており、誰でも利用できるようになっています。これほどのプロジェクトを国が力を入れて推進しているのはとてもすごいことですし、PLATEAUが発表された当時もかなり話題になったのではないでしょうか。公開されているデータは手元にダウンロードして使うこともできますし、PLATEAU View4というサービスを使ってWebブラウザ上でも閲覧できます。
WGS84は世界測地系と呼ばれるもので、緯度・経度・高さの3つの要素で位置を表します。Geospatial APIのアンカーを設置する際には、WGS84ベースで位置や回転を指定することになります。
WGS84のような測地系は、球体である地球の特性にそってデザインされています。しかしこのままでは、既存のゲームエンジンなどとかなり相性が悪いでしょう。そこで、ある地点を原点として、その周辺をあたかも平面であるように扱う直角座標系が必要になりました。それが「平面直角座標系」です。
平面直角座標系の原点は、日本各地に19か所定義されています(図1.2参照。こちらの図は国土地理院のWebサイト5から引用しました)。
図1.2を見ると、関東周辺に一番近い原点は「9」とあります。東京の測地系を平面直角座標系に変換する際は9番目を原点とすることが多く、この座標系を「平面直角9系」と呼びます。平面直角9系の原点は、緯度経度で(139.5000, 36.0000)となります。本章では秋葉原のPLATEAUモデルを用いて解説するため、この平面直角9系を用います。
ここまでの座標系の話を踏まえて、PLAETAUのデータと向き合ってみましょう。秋葉原駅周辺のデータを用いるのに際して、GoogleマップとPLATEAUの東京都23区構築範囲図67を見比べてみると、駅周辺は次の4つの区間にまたがっていることがわかります。
・53394631
・53394632
・53394641
・53394642
これらのデータを取得するには、G空間情報センターの東京都23区のfbxモデルのページ8から533946の区域のデータをダウンロードします。ダウンロードしたファイルを解凍すると、「LOD1」というZIPファイルがあるので、こちらも解凍しましょう。
LOD1の中には各区域のFBXモデルが入っています。その中から図1.5のように、今回必要なモデルを探しましょう。ちなみに、ファイル名に書かれている「6677」という数字は、平面直角9系を表わすEPSGコードと呼ばれるものです。つまり、この数字が書いてあるPLATEAUモデルは、平面直角9系で投影されているということでもあります。
これらのFBXファイルをUnityにインポートしてみましょう。UnityでFBXモデルをインポートすると大きさの単位が1cmと解釈されてしまうのですが、PLATEAUモデルは1m単位で作られているので、図1.6のようにinspectorからConvert Unitsのチェックを外しましょう。
モデルデータをシーンに追加すると無事、秋葉原駅周辺の建物モデルができあがっているのがわかります。よく見ると図1.8のように、モデル原点がかなり遠くのほうにありますが、平面直角9系の原点と秋葉原駅との間に距離があるためですね。ちなみに、平面直角9系の原点は埼玉県にあるらしいです。
Geospatial APIでは、WGS84という測地系にしたがって緯度経度の情報を元にアンカーを作成します。シーンにPLAETAUデータを配置した状態ではモデルの原点がWGS84で(139.5000, 36.0000)の位置になっていますが、それでは秋葉原駅から距離があるため、秋葉原駅を原点に移動させましょう。正確には、秋葉原駅の位置にあるオブジェクトを親にすることで疑似的に実現します。
まずは秋葉原駅の位置情報を取得する必要がありますので、Googleマップを使いましょう。図1.9のように、Googleマップでは右クリックメニューから緯度経度をコピーできます。秋葉原駅で検索してみると、位置は緯度経度=(35.69859, 139.77304)ということがわかりました。この値はクリックする場所によって変わってきますので、ご自分で試される場合には違う値になる可能性があります。
次に、取得した位置情報が平面直角9系だとどこになるのかを計算していきます。この値がわかれば、Unityシーン上の秋葉原駅の座標がわかりますね。世界測地系から平面直角座標系への変換には国土地理院のAPIがとても便利で、このAPIを使用するためのUI9も国土地理院から提供されているので、そちらを使います。図1.10のように値を入力して計算実行をしてみると、x,y座標が(-33438.2918, -5456.3348)と出ましたね。
Unityのシーンで位置を合わせるためには、高さの情報も必要になります。GeospatialAPIでの高さの情報は「楕円体高」という値で表現されており、楕円体高は「標高」と「ジオイド高」というふたつの値を足したものです。これらふたつについて詳しく解説しませんが、国土地理院の図10を見るとイメージしやすいです。
PLATEAUのモデルはすでに標高が考慮されているため、よく見ると建物モデルの底辺が0地点よりも上に上がっています。つまり、GeospatialAPIで扱っている楕円体高に建物の高さを合わせるためにはジオイド高が必要になります。ジオイド高の計算も、国土地理院からAPIとインターフェース11が提供されています。計算すると、図1.11のように36.7672メートルと算出されました。よって、アンカーの高さを36.7672メートルとして設置すれば、現実の建物とPLATEAUモデルの高さが合うということになります。
国土地理院のAPIで計算した位置をUnityシーンで表示してみましょう。シーン上に空のオブジェクトを作成して、位置を変更します。国土地理院によると、秋葉原駅は平面直角座標系で(x,y)=(-33438.2918, -5456.3348)とわかったのですが、ゲームオブジェクトのPositionは(x,y,z)=(5456.3348, 0, 33438.2918)としてください。算出されたx座標を符号反転させてUnity座標系のz座標に、y座標を符号反転させてUnity座標系のx座標に入れています。すると、図1.12のように、オブジェクトの位置がちょうど秋葉原駅と重なりました。
最後にPLATEAUモデルを空のオブジェクトの子に設定して、それを原点に移動させてプレハブ化すれば準備完了です。PLATEAUのモデルが少し重いので、余力があれば駅周辺以外のモデルを間引くなどの作業をするとよいでしょう。
それでは本章のゴールである、Geospatial APIでの表示をやっていきましょう。Geospatial APIの環境設定については本章では触れないので、適宜公式ドキュメント12などを参考になさってください。
Geospatial APIで空間アンカーを設置するにはスクリプトを書く必要があるので、最低限のコードを用意しました。
1: using Google.XR.ARCoreExtensions;
2: using UnityEngine;
3: using UnityEngine.XR.ARFoundation;
4:
5: namespace GeospatialPlateauAkiba
6: {
7: public class GeoAnchorPlacer : MonoBehaviour
8: {
9: [SerializeField] private ARAnchorManager arAnchorManager = null;
10:
11: [SerializeField] private GameObject anchorObject = null;
12:
13: public void PlaceAnchor()
14: {
15: if (!arAnchorManager)
16: {
17: return;
18: }
19:
20: // 秋葉原駅の座標: 35.69859, 139.77304
21: // ジオイド高: 36.7671
22: var anchor = arAnchorManager.AddAnchor(
23: 35.69859,
24: 139.77304,
25: 36.7672 - 1.5,
26: Quaternion.identity
27: );
28:
29: if (!anchorObject)
30: {
31: return;
32: }
33:
34: Instantiate(anchorObject, anchor.transform);
35: }
36: }
37: }
このコンポーネントは、AR Anchor ManagerコンポーネントとプレハブをSerializeFieldとして持っているので、インスペクタから指定する必要があります。プレハブは前節までで作成した、駅を中心としたPLAETAUモデルのプレハブを想定しています。そしてこのコンポーネントは、アンカーを設置してアンカーのtransformをもとにプレハブをInstantiateするメソッドを公開していますね。このメソッドをuGUIのボタンを押したら発火するような処理を組めば、実際に動くようになるでしょう。
PlaceAnchorメソッド内のアンカー作成処理だけリスト1.2に抜き出してみました。ここでは順番に緯度・経度・高さを指定していますが、引き算している1.5メートルはスマートフォンの高さを意味しています。おそらくですが、ARFoundationではカメラ位置を高さ0として扱うため、地面に近づけるには高さ分だけ引き算する必要があるのだと推測できます。
var anchor = arAnchorManager.AddAnchor(
35.69859,
139.77304,
36.7672 - 1.5,
Quaternion.identity
);
そして実際にプログラムを秋葉原駅周辺で実行してみると、無事PLAETAUモデルが現実の建物に重なっているのがわかりました。
最終的に、無事PLATEAUの建物モデルをGeospatial APIを使って位置合わせできました。筆者は普段AR開発をしているときはUnityの座標系を基準にコンテンツを配置するので、Geospatial APIが出てきた瞬間に「なんかめんどくさそうだな......」と思ってしまいました。しかしいざ勉強してみると面白く奥が深かったのに加え、なにより地球を基準とすることは拡張現実を実現するアプローチとして、とても合理的にも感じました。ロケーションベースARやVPSは、まだ新しい技術で、社会実装事例が少ない領域です。今後どのようなアプローチがスタンダードになっていくのかまだわかりませんが、Geospatial APIはその提案のひとつとなるような興味深いVPSなので、今後の動向に注目していきたいですね。