住所を入力するとCesiumでピンを立てるアプリ

# はじめに
ブラウザで住所を入力するとCesiumの地球儀にピンを立てるアプリを作った。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">住所を入力するとCesiumの地球儀にピンを立てるアプリを作りました。 <a href="https://t.co/mMAGHcChpW">pic.twitter.com/mMAGHcChpW</a></p>&mdash; ダンブル扉 (@danburutobira) <a href="https://twitter.com/danburutobira/status/1594280552751714304?ref_src=twsrc%5Etfw">November 20, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

>Cesium とは？
そもそもCesiumとは何かという問いに誤解を恐れずに答えるなら、Webブラウザ上で動かせる精巧なバーチャル地球儀です。
正しい位置関係と時間の概念をもっており、様々な3D地理空間情報データを可視化したり重ね合わせて分析したりするプラットフォームとして広く利用されております。

https://www.hcc.co.jp/hcclab/20200623/

公式様

https://cesium.com/



# 使用したもの
* Javascript
* [cesium-webpack-example](https://github.com/CesiumGS/cesium-webpack-example)：CesiumをWebpackで使えるようにしたもの
* 国土地理院API：住所から経度緯度を取得できるAPI

国土地理院APIの参考

https://zenn.dev/yhorikawa/scraps/1da995b10aa07b

# 頑張ったこと
* CesiumJSのサンプルを使用せずcesium-webpackを使った。
* 住所をブラウザ入力で取得できるようにした。
* 住所入力後、`viewer.camera.flyTo` というCesiumの関数で視点移動するようにした。

# 頑張らなかったこと
* フロントエンドとバックエンドを分けず、フロントエンドから直接APIを叩いている。
* 住所入力が失敗した時のエラー処理がない。
* デプロイ未実施。

# コード
https://github.com/ikenohotori/CesiumPinViewer

cesium-webpack-example に対して下記の3ファイルを修正および追加している。
## index.html
```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
</head>
<body>
  <!-- 住所入力 -->
  <input type="text" placeholder="住所を入力" id="AdressText">
  <input type="button" value="ピンを立てる" id="ResisterButton">
  <!-- Cesium表示 -->
  <div id="cesiumContainer"></div>
</body>
</html>
```
## index.js
```javascript
import { Ion, Viewer,Color,camera,VerticalOrigin, PinBuilder, Cartesian3 } from "cesium";
import "cesium/Build/Cesium/Widgets/widgets.css";
import "../src/css/main.css"
const {
  getLongitude
} = require("./address");

Ion.defaultAccessToken = '<ACCESS_TOKEN>';

let ResisterButton = document.getElementById('ResisterButton');
ResisterButton.addEventListener('click', butotnClick);

// 住所が入力されたときの挙動
function butotnClick(){
  const AdressText = document.getElementById('AdressText').value;
  getLongitude(AdressText)
  .then(data =>{
      pinSet(data.longitude, data.latitude);
      
  })
};

// Cesiumで地球儀表示
const viewer = new Viewer("cesiumContainer", {
  timeline: false
});

// ピンを立てるためのオブジェクト生成
const pinBuilder = new PinBuilder();

// ピンを立てる
function pinSet(longitude, latitude)
{
  const bluePin = viewer.entities.add({
    name: "blue pin",
    position: Cartesian3.fromDegrees(longitude, latitude),
    billboard: {
      image: pinBuilder.fromColor(Color.ROYALBLUE, 10).toDataURL(),
      verticalOrigin: VerticalOrigin.BOTTOM,
    },
  });

  // 視点移動
  viewer.camera.flyTo({   
    destination : Cartesian3.fromDegrees(longitude, latitude, 450000)
  });
}
```

* CesiumJSを使用するためにアクセストークンが必要。[公式のクイックスタート](https://cesium.com/learn/cesiumjs-learn/cesiumjs-quickstart/)を参考にアクセストークンを取得し、`<ACCESS_TOKEN>`にコピーする。
* ピンを立てる処理は[公式のデモ](https://sandcastle.cesium.com/index.html?src=Map%2520Pins.html)が参考になる。
ただし、デモコードはCesiumクラスから記載しているのに対して、cesium-webpackで使用する場合はモジュールをインポートした上で、モジュール名だけ書かないとエラーになるので注意。
例）
✕ `const pinBuilder = new Cesium.PinBuilder();`
〇 `const pinBuilder = PinBuilder();`


## address.js
```javascript
// 住所から経度・緯度を取得
function getLongitude(AdressText) {
    return new Promise((resolve,reject) =>{
        try
        {
            const uri = 'https://msearch.gsi.go.jp/address-search/AddressSearch?q=' + AdressText;
            // URLエンコード
            const encodedurl = encodeURI(uri);
            fetch(encodedurl)
            .then(response => response.json())
            .then((data) => {  
                // 経度, 緯度
                LongitudeInfo = {
                    "longitude" :data[0].geometry.coordinates[0],
                    "latitude" :data[0].geometry.coordinates[1]
                }
                return resolve(LongitudeInfo);
            })
            .catch(err =>{
                return reject(err);
            });
        }
        catch(err)
        {
            console.log(err)
        }
    })
}

module.exports = { 
    getLongitude
}
```
* 住所から経度緯度を取得するために国土地理院APIを使用している。これは非同期で実行されるため、結果を取得するために`Promise`を使用している。
* 日本語や半角文字をURLに変換するために`encodeURI`を使用。

おしまい。