かっこいい地図

  • URLをコピーしました!

こんにちはジョージです。
かつて伊能忠敬は 17 年かけて日本地図を完成させたと言われています。そんな日本地図を WebViewer でカスタム App に表示しましょう!

2023031601#mov1.gif (1.7 MB)
目次

Map chart

「amCharts」にはあらかじめ、地理的な地図を表示するために必要なモジュールが組み込まれています。そして日本地図を含め、amCharts 5 で使用できるマップは数百あるそうです。
配置した地図はズームやスクロールに対応し、さらに線や点・グリッドなどを組み合わせて描画して地図のカスタマイズも可能です。

あわせて読みたい

プロジェクトのソース修正

プロジェクトの設定は 前回 とまったく同じです。* amCharts をインストールしてない場合は事前にパッケージをインストールしておいてください。

main.js

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'

document.addEventListener("DOMContentLoaded", () => {
    createApp(App).mount('#app')
});

vite.config.js

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  build: {
    rollupOptions: {
      output: {
        entryFileNames: `assets/[name].js`,
        chunkFileNames: `assets/[name].js`,
        assetFileNames: `assets/[name].[ext]`,
      },
    },
  },
})

App.vue の修正

App.vue を修正します。完成したソースは以下の通りです。

<template>
  <div class="hello" ref="chartdiv">
  </div>
</template>

<script>
import * as am5 from "@amcharts/amcharts5";
import * as am5map from "@amcharts/amcharts5/map";
import am5themes_Animated from '@amcharts/amcharts5/themes/Animated';
import am5geodata_japanHigh from "@amcharts/amcharts5-geodata/japanHigh";

export default {
  name: 'HelloWorld',
  data() {
    return {
      "tableData": [
      ],
      "prefecture": {
          "JP-01":'北海道', 
          "JP-02":'青森県', 
          "JP-03":'岩手県', 
          "JP-04":'宮城県', 
          "JP-05":'秋田県', 
          "JP-06":'山形県', 
          "JP-07":'福島県', 
          "JP-08":'茨城県', 
          "JP-09":'栃木県', 
          "JP-10":'群馬県', 
          "JP-11":'埼玉県', 
          "JP-12":'千葉県', 
          "JP-13":'東京都', 
          "JP-14":'神奈川県',
          "JP-15":'新潟県', 
          "JP-16":'富山県', 
          "JP-17":'石川県', 
          "JP-18":'福井県', 
          "JP-19":'山梨県', 
          "JP-20":'長野県', 
          "JP-21":'岐阜県', 
          "JP-22":'静岡県', 
          "JP-23":'愛知県', 
          "JP-24":'三重県', 
          "JP-25":'滋賀県', 
          "JP-26":'京都府', 
          "JP-27":'大阪府', 
          "JP-28":'兵庫県', 
          "JP-29":'奈良県', 
          "JP-30":'和歌山県',
          "JP-31":'鳥取県', 
          "JP-32":'島根県', 
          "JP-33":'岡山県', 
          "JP-34":'広島県', 
          "JP-35":'山口県', 
          "JP-36":'徳島県', 
          "JP-37":'香川県', 
          "JP-38":'愛媛県', 
          "JP-39":'高知県', 
          "JP-40":'福岡県', 
          "JP-41":'佐賀県', 
          "JP-42":'長崎県', 
          "JP-43":'熊本県', 
          "JP-44":'大分県', 
          "JP-45":'宮崎県', 
          "JP-46":'鹿児島県',
          "JP-47":'沖縄県', 
      },
    }
  },
  methods :{
    renderMap(val) {
        let tableData = JSON.parse(val);
        this.jaSeries.data.setAll(tableData);
    },
  },
  created() {
    window.renderMap = this.renderMap;
  },
  mounted() {

    // Create root and chart
    let root = am5.Root.new(this.$refs.chartdiv);

    root.setThemes([
      am5themes_Animated.new(root)
    ]);

    let chart = root.container.children.push(
      am5map.MapChart.new(root, {
      })
    );
    
    // Create series
    let jaSeries = chart.series.push(
      am5map.MapPolygonSeries.new(root, {
        geoJSON: am5geodata_japanHigh,
        geodataNames: this.prefecture,
        valueField: "value",
        calculateAggregates: true,
      })
    );

    jaSeries.mapPolygons.template.setAll({
      tooltipText: `[bold]{name}: {value}[/]`,
      interactive: true
    });

    jaSeries.mapPolygons.template.adapters.add("tooltipText", function(text, ev) {
      if (!ev.dataItem.dataContext.value) {
        return "[bold]{name}: 値なし[/]";
      }
        return text;
    })

    jaSeries.set("heatRules", [{
      target: jaSeries.mapPolygons.template,
      dataField: "value",
      min: am5.color(0x8ab7ff),
      max: am5.color(0x25529a),
      key: "fill",
    }]);

    jaSeries.events.on("click", function(ev) {
      console.log(chart.invert(ev.point))
    });

    jaSeries.data.setAll(this.tableData);
    
    this.jaSeries = jaSeries;
    this.root = root;

  },

  beforeDestroy() {
    if (this.root) {
      this.root.dispose();
    }
  }
}

</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.hello {
  width: 100%;
  height: 500px;
}
</style>

補足説明(14 行目)

サンプルで取り扱う data プロパティを準備します。tableData はカスタム App から受け取る JSON を代入します。prefecture は地域コードと日本語県名の JSON オブジェクトです。amCharts の地図データは英語なので名前変換機能を使って日本語に置き換えます。

  data() {
    return {
      "tableData": [
      ],
      "prefecture": {
          "JP-01":'北海道', 
          ...
      },
    }
  },

補足説明(69 行目)

メソッドを定義します。カスタム App から受け取った JSON を amCharts に設定します。

  methods :{
    renderMap(val) {
        let tableData = JSON.parse(val);
        this.jaSeries.data.setAll(tableData);
    },
  },

補足説明(75 行目)

カスタム App から Vue.js のメソッドにアクセスできるように Window オブジェクトに割り当てます。

  created() {
    window.renderMap = this.renderMap;
  },

補足説明(92 行目)

geoJSON プロパティに日本地図 am5geodata_japanHigh を指定します。県名を日本語に変換する為に geodataNames プロパティに this.prefecture を指定します。
最終的には「県名+値」をマウスオーバーした際のツールチップに表示したいので、値を格納するカスタムフィールドを指定します。
後から県の背景色を「値」によって色分けしたいので calculateAggregates を有効にします。背景色の塗り分けまでできるのには驚きです。

    // Create series
    let jaSeries = chart.series.push(
      am5map.MapPolygonSeries.new(root, {
        geoJSON: am5geodata_japanHigh,
        geodataNames: this.prefecture,
        valueField: "value",
        calculateAggregates: true,
      })
    );

補足説明(102 行目)

ツールチップに表示したいテキストの雛形を指定してマウス操作をキャプチャする為に interactive を有効にします。
ツールチップのテキストはカスタム App から値をもらっていない初期状態(つまり Value が空の場合)は「値なし」と表示するように調整します。

    jaSeries.mapPolygons.template.setAll({
      tooltipText: `[bold]{name}: {value}[/]`,
      interactive: true
    });
    
    jaSeries.mapPolygons.template.adapters.add("tooltipText", function(text, ev) {
      if (!ev.dataItem.dataContext.value) {
        return "[bold]{name}: 値なし[/]";
      }
        return text;
    })

補足説明(102 行目)

背景色の塗り分けを指定します。key に fill (塗り)を指定して min、max で背景色の指定をします。流されたデータによって amCharts が背景色を自動で塗り分けてくれます。いや便利。

    jaSeries.set("heatRules", [{
      target: jaSeries.mapPolygons.template,
      dataField: "value",
      min: am5.color(0x8ab7ff),
      max: am5.color(0x25529a),
      key: "fill",
    }]);

補足説明(122 行目)

県がクリックされたアクションを記述します。今回はログをコンソールに出力しているだけですが、FileMaker.PerformScriptWithOption を使ってカスタム App に処理を戻すことも可能でしょう。

    jaSeries.events.on("click", function(ev) {
      console.log(chart.invert(ev.point))
    });

補足説明(122 行目)

準備ができたら tableData を読み込みます。WebViewer に表示したばかりの状態では tableData の値が空なので、県の塗り分けもなく「県名:値なし」とツールチップが表示されます。

    jaSeries.data.setAll(this.tableData);
    
    this.jaSeries = jaSeries;
    this.root = root;

プロジェクトのビルド

ここまでできたら一度ビルドしてみましょう。

npm run build

前回 と同様に dist フォルダに html と JavaScript、CSS ファイルが出力されます。

カスタム App の準備

新規カスタム App ファイルを準備します。前回 と同様のテーブル、レイアウトを使い回します。

スクリーンショット 2023-03-16 22.06.12.png (235.3 kB)

html、js、css フィールドにはそれぞれ、ビルド後に出力されたファイルの内容を挿入します。うまくいくと日本地図が WebViewer に表示されます。

render スクリプト

amCharts に渡す JSON を作ります。本来は県ごとに集計したいレポート(セミナーやイベントの来場者の住所別集計など)と連携するとおもしろいのですが、手っ取り早くダミーデータ JSON をループで作成しちゃいましょう。

JP-01 は北海道、JP-02 は青森県 … と id を指定し、ランダムな値 value を使って ダミーデータJSON を作成します。Web ビューアで JavaScript を実行スクリプトステップを使って renderMap メソッド経由で JSON を amCharts に読み込ませます。

# #
# JSON サンプル
[
        { "id": "JP-01", value: 1 },
        { "id": "JP-02", value: 10 },
        { "id": "JP-03", value: 100 },
        ...
      ]
# 
変数を設定 [ $json ; 値: "[]" ] 
変数を設定 [ $i ; 値: 0 ] 
Loop
変数を設定 [ $json ; 値: Let ([ 	$code = "JP-" & Right( "00" & $i + 1; 2 ) ]; JSONSetElement ( $json ; $i ; JSONSetElement ( "{}"; 	[ "id"; $code; JSONString ]; 	[ "value"; Int ( Random * 10000 ); JSONNumber ] ) ; JSONObject ) ) ] 
変数を設定 [ $i ; 値: $i + 1 ] 
Exit Loop If [ $i > 47 ] 
End Loop
Web ビューアで JavaScript を実行 [ オブジェクト名: "wv" ; 関数名: "renderMap" ; 引数: $json ] 

動作確認

それでは動作確認をしてみましょう!render スクリプトを実行すると日本地図が値の大きさで塗り分けられ、マウスを重ねるとカスタム App が生成した値を表示します。

2023031601#mov2.gif (2.0 MB)

日本語県名 JSON を含めてもたったの 150 行程度のソースコードでここまでできるのは、正直すごいライブラリだと思いました。
そして今回紹介した機能は amCharts でできる事のほんの一部です。もっと詳しく知りたいかたは amCharts デモのページをみることをお薦めします。興味ある方は是非チャレンジしてみてください!

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!
目次