MonacaとNCMBで経県値風アプリを作る（その2: 匿名認証と地図の表示）


経県値というアプリは知っていますか？各都道府県毎に、その地域で経験した内容を記録していくことで累計ポイントを獲得するアプリです。

[経県値](https://uub.jp/kkn/#app)

今回はNCMBとMonacaを使って経県値風のアプリ（地図タップアプリ）を作ります。SVGを使って日本地図を表示し、経験したことをチェックしていきます。得点は出していませんが、ちょっとカスタマイズすればできるでしょう。

[前回の記事では画面の説明とSDKの導入までを進めました](https://qiita.com/goofmint/items/8e462b165fc3b62eac5a)ので、今回は匿名認証と地図表示を実装していきます。

## コードについて

今回のコードは[memorymap-monaca](https://github.com/NCMBMania/memorymap-monaca)にアップロードしてあります。実装時の参考にしてください。


## 匿名認証を有効にする

匿名認証とは、デバイスで生成したUUIDを使って認証を行う機能になります。自動生成した文字列なので、ユーザーがパスワードを入力したりする必要がなく、手軽に使えます。

管理画面のアプリ設定にして、匿名認証を有効化します。デフォルトでは無効なので注意してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/667535ea-99ec-0632-c738-79064bc7e6b2.png)


## 匿名認証を利用する

匿名認証はアプリの起動時に利用するので `js/app.js` にて実行します。NCMBを初期化した後、まずログイン判定を行います。これはログインユーザー情報を取得し、 `null` 判定を行うことで可能です。 `null` ならばログインしていない状態です。

```jsx
// ログインユーザーを取得
const user = ncmb.User.getCurrentUser();
// ログイン判定
if (!user) {
  // ログインしていない場合は匿名ログイン
}
```

そしてログインしていない場合は以下のコードで匿名認証を実行します。たった1行のコードで認証処理が完了します。

```jsx
// ログインしていない場合は匿名ログイン
await ncmb.User.loginAsAnonymous();
```

## 地図を表示する

ここからはNCMBは関係なく、D3.jsなどの話になります。まず必要なライブラリを `www/index.html` にて読み込みます。位置情報を扱うためのD3 Geoなども読み込みます。

```jsx
<script src="https://cdn.jsdelivr.net/npm/d3-array@3"></script>
<script src="https://cdn.jsdelivr.net/npm/d3-geo@3"></script>
<script src="https://cdn.jsdelivr.net/npm/d3@7"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/3.0.2/topojson.min.js" integrity="sha512-4UKI/XKm3xrvJ6pZS5oTRvIQGIzZFoXR71rRBb1y2N+PbwAsKa5tPl2J6WvbEvwN3TxQCm8hMzsl/pO+82iRlg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
```

### ファイルの作成

日本地図は[D3.jsで日本地図を描き、都道府県別に色を塗る - Qiita](https://qiita.com/ran/items/d88c5126362576be3291)に沿って作成します。topojsonの最新版ではコマンドラインオプションが異なるため、1系をインストールしています。

できあがったファイル `japan.json` は `www/assets` 以下に保存します。

### ストアの作成

`japan.json` で取得できる都道府県一覧は、一覧画面でも利用します。そこでストアを作成しておきます。これは `www/js/stores.js` の内容です。

```jsx
const createStore = Framework7.createStore;
const store = createStore({
  state: {
    prefectures: [],
  },
  getters: {
    prefectures({ state }) {
      return state.prefectures;
    }
  },
  actions: {
    setPrefectures({ state }, prefectures) {
      state.prefectures = prefectures;
    },
  },
})
```

### www/pages/map.htmlの処理

では画面を作成します。地図を表示するのは `#map` になります。

```html
<div class="page-content">
	<svg id="map"></svg>
</div>
```

ここからはJavaScriptのコードです。画面がマウントされた際のイベント `$onMounted` に実装します。

```jsx
$onMounted(async () => {
			await showMap();
｝）；
```

`showMap` 関数では、まずD3.jsでの描画部分を作成します。地図データなので、緯度経度を使って中心を指定できます。

```jsx
const showMap = async () => {
	const map = document.querySelector('#map');
	const width = map.clientWidth;
	const height = map.clientHeight;
	const svg = d3.select("#map");
	const projection = d3.geoMercator()
		.center([136, 35.5])
		.scale(1000)
		.translate([width / 2, height / 2]);

```

そして `japan.json` から都道府県一覧を取得し、それをストアにも保存します。

```jsx
const japan = await d3.json("../assets/japan.json");
pref = japan.objects.pref.geometries.map(d => d.properties.name_local);
$store.dispatch('setPrefectures', pref);
```

その `pref` を使ってTopoJSONを作り、D3.jsで描画します。

```jsx
const topo = topojson.feature(japan, japan.objects.pref).features;
const view = svg.selectAll(".pref")
	.data(topo)
	.enter()
	.append("path")
	.attr("class", function(d) {
			return "pref pref" + pref.indexOf(d.properties.name_local);
	})
	.attr("d", path);
```

最後にズームイベント追加します。ただし、この実装では任意の場所をズームできず、使い勝手が悪いです。皆さんのアプリでは修正してください。

```jsx
function zoomed() {
	const transform = d3.zoomTransform(this);
	this.setAttribute("transform", transform);
}
const zoom = d3.zoom()
	.scaleExtent([1, 5])
	.translateExtent([
		[-10, -10],
		[width + 90, height + 100]
	])
	.on("zoom", zoomed);
svg.call(zoom);
```

これで画面上に日本地図が表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/1fdea388-a0f1-921b-ea96-48ac175e9b8a.png)


## まとめ

今回は匿名認証の実装と、アプリ画面に日本地図を表示するまでを実装しました。次回は[地図をタップした際の処理、データ保存と取得](https://qiita.com/goofmint/items/75ff29b3770e4539febb)を行います。