TypeScriptでSoracomの基地局の位置情報を取得できるAPIを実行する

[基地局の位置情報を取得できる API を公開しました！](https://blog.soracom.com/ja-jp/2020/10/14/cell-location-api/)
に記載されているAPIを利用して位置情報を取得してみます。

## はじめに
### Soracom APIの使い方
リファレンスが公開されているのでこちらから使い方を試してみることが可能です
https://users.soracom.io/ja-jp/tools/api/reference/

### SoracomのAPIから基地局情報を取得するために必要なAPI
少なくとも以下の3つのAPIを実行する必要がありました。
1. [APIトークンの取得(認証キーを利用してAPIを実行してトークンを取得します)](https://users.soracom.io/ja-jp/tools/api/reference/#/Auth/auth)
2. [3G の場合： MCC（Mobile Country Code、モバイル国コード）、MNC（Mobile Network Code、モバイルネットワークコード）、LAC（Location Area Code、位置エリアコード）、CID（Cell ID、セル ID）,4G の場合： MCC、MNC、TAC（Tracking Area Code、追跡エリアコード）、ECID（Enhanced CellID、拡張 セル ID)の取得](https://users.soracom.io/ja-jp/tools/api/reference/#/Subscriber/getSubscriber)
3. [基地局の位置情報を取得](https://users.soracom.io/ja-jp/tools/api/reference/#/CellLocation/getCellLocation)

以下の3つのAPIを実効する4G用のTypeScriptのサンプルは以下

ブログにもあるように3Gと4Gで必要な情報は変わります。
[基地局の位置情報を取得できる API を公開しました！](https://blog.soracom.com/ja-jp/2020/10/14/cell-location-api/)

```
実は基地局の ID（基地局を一意に特定する情報）は以下のような 4 つの要素からなっています：

3G の場合： MCC（Mobile Country Code、モバイル国コード）、MNC（Mobile Network Code、モバイルネットワークコード）、LAC（Location Area Code、位置エリアコード）、CID（Cell ID、セル ID）
4G の場合： MCC、MNC、TAC（Tracking Area Code、追跡エリアコード）、ECID（Enhanced CellID、拡張 セル ID)
```

``` soracom.ts
import * as rp from "request-promise";

console.log("*** 開始 ***");
const BASE_URl = "https://api.soracom.io/v1";
const TOKEN_URL = BASE_URl + "/auth";
const SUBSCRIBE_URL = BASE_URl + "/subscribers";
const CELL_LOCATION_URL = BASE_URl + "/cell_locations";
const SORACOM_AUTH_KEY_ID = process.env.SORACOM_AUTH_KEY_ID;
const SORACOM_AUTH_KEY = process.env.SORACOM_AUTH_KEY;
const SORACOM_TOKEN_TIMEOUT_SECONDS = 86400;
const IMSI = process.env.IMSI || "";

export interface SoracomToken {
  apiKey?: string | undefined;
  operatorId?: string | undefined;
  token?: string | undefined;
}

export interface CellLocationResponce {
  lon?: number | undefined;
  lat?: number | undefined;
}

export interface SoracomSubscriberResponce {
  mcc: number;
  mnc: number;
  tac: number;
  ecid: number;
}

async function getToken(): Promise<SoracomToken> {
  const options = {
    url: TOKEN_URL,
    method: "POST",
    headers: {
      "content-type": "application/json",
    },
    body: JSON.stringify({
      authKey: SORACOM_AUTH_KEY,
      authKeyId: SORACOM_AUTH_KEY_ID,
      tokenTimeoutSeconds: SORACOM_TOKEN_TIMEOUT_SECONDS,
    }),
  };
  try {
    const res = await rp.post(options);
    const response = JSON.parse(res);
    return {
      apiKey: response.apiKey,
      operatorId: response.operatorId,
      token: response.token,
    };
  } catch (err) {
    console.error(`ERROR: ${err.message}`);
    return {
      apiKey: "",
      operatorId: "",
      token: "",
    };
  }
}

async function getSubscribe(
  token: SoracomToken,
  imsi: string
): Promise<SoracomSubscriberResponce> {
  const options = {
    url: SUBSCRIBE_URL + "/" + imsi,
    method: "GET",
    headers: {
      "content-type": "application/json",
      "X-Soracom-Token": token.token,
      "X-Soracom-API-Key": token.apiKey,
    },
  };
  try {
    const res = await rp.get(options);
    const response = JSON.parse(res);
    console.log(response);
    return {
      mcc: response.previousSession.cell.mcc,
      mnc: response.previousSession.cell.mnc,
      tac: response.previousSession.cell.tac,
      ecid: response.previousSession.cell.eci,
    };
  } catch (err) {
    console.error(`ERROR: ${err.message}`);
    return {
      mcc: 0,
      mnc: 0,
      tac: 0,
      ecid: 0,
    };
  }
}

async function getCellLocation(
  token: SoracomToken,
  targetLocation: SoracomSubscriberResponce
): Promise<CellLocationResponce> {
  const options = {
    url: CELL_LOCATION_URL,
    method: "GET",
    qs: {
      mcc: targetLocation.mcc,
      mnc: targetLocation.mnc,
      tac: targetLocation.tac,
      ecid: targetLocation.eci,
    },
    headers: {
      "content-type": "application/json",
      "X-Soracom-Token": token.token,
      "X-Soracom-API-Key": token.apiKey,
    },
  };
  try {
    const res = await rp.get(options);
    const response = JSON.parse(res);
    console.log(response);
    return {
      lon: response.lon,
      lat: response.lat,
    };
  } catch (err) {
    console.error(`ERROR: ${err.message}`);
    return {
      lon: 0,
      lat: 0,
    };
  }
}

async function start() {
  const token = await getToken();
  console.log(`token = ${JSON.stringify(token)})`);
  const subscribe = await getSubscribe(token, IMSI);
  console.log(`subscribe = ${JSON.stringify(subscribe)}`);
  const cellLocation = await getCellLocation(token, subscribe);
  console.log(`cellLocation = ${JSON.stringify(cellLocation)}`);
}

start();

```

細かい注意事項として
getSubscriberで戻り値としてeciが返ってくるのですが、getCellLocationで指定するのはeciではなくてecidでした。

## まとめ
Soracom APIを利用した基地局の位置情報を取得する方法をまとめました
参考になれば幸いです