マップリンク – ReactとSVGで、簡単なインタラクティブ地図制作ツールを作ってみた

記事カテ:コード

この記事を読むのに約7分かかる

掲載日:

MAP-Link は React ベースの地図制作ツールで、日本の地理の先生が使うことを想定して作られています。地図の一部を色で塗り潰したり、塗り潰した部分を元に戻したり、地図をズームしたり、マウスでパンしたりすることができます。 編集が終わった後は、ダウンロードボタンを使って地図の PNG ファイルを直接ダウンロードすることができます。

このプロジェクトは、日本の地理の先生方に教材を提供するためのデータベースサイトを作成している友人のために作りました。彼の Wordpress サイトに直接アプリを組み込み、サブスクリプションシステムの導入も手伝いました。


ほとんどのマップは有料でしか利用できませんが、ここでは 無料のマップの一つを試すことができます。

Map-Linkでオリジナル地図を作り、ダウンロードする流れのデモ動画
Map-Linkでオリジナル地図を作り、ダウンロードする流れ

仕組みの説明

ベース

  • アプリは、コントローラー部分(すべてのボタンが配置されている場所)と、マップ本体に分かれています。
  • 基本的に、ユーザーは SVG 要素、内部の <path> 要素(地図の州や国を形成する)、あと SVG を囲むいくつかのラッパー要素を直接操作します。
  • マップは、Wordpress のカスタム投稿タイプ+マップの SVG データを保存するために投稿に付けられたカスタムフィールドを使用して管理・保存されます。
    • そのため、別の地図に切り替えるには、ユーザーが違うページに移動する必要があり、そのたびにアプリが再初期化されます(毎回、異なる地図データが使われます)。

Converting and creating the map

  • React Hook(useMap)を作って、マップを操作するためのファンクションと、マップ要素自体をコンポーネントツリーに提供しています。
  • フックは、いくつかの refs ( useRef )、いくつかの state オブジェクト(useState)、あとそれらを使用するいくつかの関数を宣言します。
  • そして hook が生の SVG データを受け取り、React 化します。通常の HTML 要素を React 要素に変えます。また、上述の refs をサブ要素に付けます。
    • これらは全部 useEffect の中で行われ、DOM が読み込まれて、マップデータが安全に取得できるようになっています。

塗装と「元に戻す」機能

  • ユーザーが選択した現在の色は、React の useState hook で管理されます。
  • マップ変換の過程で、内側の<path>要素にクリックイベントハンドラが追加され、クリックされたときに色の state に応じて色が変わるようになっています。
    • <path> 要素をクリックすると、fill の色が変わります。
  • 「元に戻す」機能は、最後に行ったペイント操作を元に戻すための命令を格納するスタックに過ぎません。
    • また、「リセット」機能は、スタック内のすべてのものに対して「元に戻す」を実行し、それをクリアするだけです。

パンとズーム機能

  • パンニング機能では、ユーザーがマウスをドラッグしている位置、マップのサイズ、現在のマップのズームレベルに基づいて計算が行われます。
  • これらの計算結果は、マップのラッパー要素の CSS transform の値を変更するために使用されます。
  • ズームも同様で、 zoom の状態を利用して、別のラッパー要素の CSS transform 値を変換します。

PNG に変換してダウンロード機能

  • 今回のプロジェクトで最も面倒だったところは、絶対これでした。
  • Javascript を使ってブラウザから画像をダウンロードするには、まず HTML5 の<canvas>要素にコンテンツを paint する必要があります。
  • 最初は、HTML2Canvas というライブラリを使って、マップとそのラッパー要素を canvas に変換し、ダウンロードできるようにしようとしました。
    • 僕がマップのラッパー要素に対して行っていた変換を、ライブラリは再現できませんでした。
  • 結局、この変換を自分で実装することになりました。そして、地図の SVG の寸法とラッパーの変換に基づいていくつかの計算を行い、操作した地図を canvas 要素にコピーすることができました。
  • そこからは、簡単に PNG のダウンロード機能が完成しました。

Wordpress 用としてプロジェクトをエクスポートする

  • CRACOというサードパーティライブラリを使って、React プロジェクト全体を 1 つの JS ファイルとしてラップしてエクスポートしました。これにより、各マップページの下部にたった1つのファイルを簡単に埋め込むことができました。

感想

今になってみると、このプロジェクトで React を使う必要はなかったかもしれません。いくつかの解決策は、React の奇妙な点を回避するために過剰なエンジニアリングをしないといけなかったです。

まあ、完成した製品はいいものになっていると思います。もし、感想や質問があれば教えてください!