🔰

NimでGoogleMapっぽいマップ表示をしてみた

2023/02/23に公開

Nim言語を覚えて、一か月ほどの初心者です。
今年になってpythonにfoliumと言うGoogleMapみたいな地図表示が作成出来るライブラリがあるのを知りました。
それをNimを使って表示させたいと思い、Nimのライブラリを調べたのですが、同様の物がありませんでした。
そこで、nimpyを使えば、Pythonのパッケージライブラリであるfoliumを動かせるんじゃないかと思い、やってみました。

環境

私のPC環境はWindowsであるため、Nightly build版で動作させています。

  • OS: Windows11
  • Nim 1.9.1 (v2.0 RC版 2023/02/18)
  • Python 3.9.13
  • nimpy 0.2.0
  • nimja 0.8.6

foliumを使った地図マップの動作確認

1. nimpyライブラリのインストール

nimble install nimpy

nimpyライブラリは、pythonのライブラリや関数を呼び出せるだけじゃなく、逆にpython側からnimライブラリを呼び出す事も出来ます。

2. nimpyを使ってpythonのfoliumライブラリを呼び出すソース

sample01.nim
#[
    pythonライブラリのfoliumを呼び出したい

    何故か仮想環境化での実行が出来ないみたい
    再度pip install foliumで、pythonのLib/site-packagesに入れて確認
]#
import nimpy

let folium = pyImport("folium")
let folium_map = folium.Map(location=[35.690921, 139.700258], zoom_start=15)
discard folium_map.save("shinjuku_station.html")

どこかのサイトでfoliumを解説してたので、座標等も同じ値を入れてみました。(座標がわからないので、マルパクリでごめんなさい。)

下記のようにターミナル上から実行

nim r --hints:off .\sample01.nim

出力結果

  • ソースと同一ディレクトリにshinjuku_station.htmlファイルが作成され、ブラウザで表示した結果です。
  • ソースのコメントにも記載していますが、私はpythonライブラリを仮想環境に入れて、何時も実行しているため、再度pipfoliumを入れて確認しました。

テンプレートエンジンを使った地図マップの動作確認

Nim側に、templateエンジンのライブラリを入れて、直接leaflet.jsで表示させてみました。
利用したtemplateエンジンはnimjaライブラリです。

1. nimjaライブラリのインストール

nimble install nimja

まずは、nimbleコマンドでnimjaライブラリをインストールします。
余談ですが、nimjaは、python用テンプレートライブラリのjinja(神社)を、nimに移行したライブラリです。nim+jinjaからnimja(忍者)と言う俗語になったみたい。

2. テンプレートファイルの作成

ソースのフォルダ内に、templatesフォルダを作成し、その中のテンプレートファイルを作成します。テンプレートファイルは、下記のpakuri_folium.nimjaとします。

templates/pakuri_folium.nimja
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>foliumをパクッてみた</title>
  <link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.0/dist/leaflet.css" />
  <script src="https://unpkg.com/leaflet@1.3.0/dist/leaflet.js"></script>
  <script>
    function init() {
      var map = L.map('mapcontainer');

      //地図の中心とズームレベルを指定
      map.setView([{{map.latitude}}, {{map.longtude}}], {{map.zoom}});
      //表示するタイルレイヤのURLとAttributionコントロールの記述を設定して、地図に追加する
      L.tileLayer('https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png', {
          attribution: "<a href='https://maps.gsi.go.jp/development/ichiran.html' target='_blank'>地理院タイル</a>"
      }).addTo(map);

      // マーカーの設定
      {% for (idx, mark) in markers.pairs %}
        L.marker([{{mark.latitude}}, {{mark.longtude}}], { title:"{{mark.name}}" }).addTo(map);
      {% endfor %}
    }
  </script>
</head>
<body onload="init()">
  <div id="mapcontainer" style="position:absolute;top:0;left:0;right:0;bottom:0;"></div>
</body>
</html>

注意する点は、javascript内でコメントを入れている所です。
nimjaのテンプレートエンジンでは、{{オブジェクト名.変数名}}にすれば、自動で変換してくれます。
また、{% for (idx, mark) in markers.pairs %}から{% endfor %}を指定すれば、nim側のオブジェクトが存在した時だけ、その内部のタブが表示されます。
今回は、MapMarkerオブジェクトをnim側で作成し、nimjaテンプレートエンジン側で、変換させる所を行います。

3. nim側のソース

nim側のソースには、nimja/parserをインポートして、Mapには表示させる基準座標、Markにはマーカーを指定してテンプレートエンジンに渡すようにします。

sample02.nim
import nimja/parser
import std/os

type
  # マップの基準位置
  Map = ref object
    latitude: float
    longtude: float
    zoom: int
  # マーカー設定の位置
  Mark = ref object
    name: string
    latitude: float
    longtude: float

# templateマクロを呼び出し、templateファイル内を引数の名称と置き換え
proc renderHtml(map: Map, markers: seq[Mark]): string =
  compileTemplateFile(getScriptDir() / "templates/pakuri_folium.nimja")

# HTML文字列をファイル出力
proc writeHtml(filename, html: string): void =
  var f : File = open(filename, FileMode.fmWrite)
  defer :
    close(f)
  f.write(html)

# 地図の基準位置を設定
let map = Map(latitude: 35.8627, longtude: 139.6072, zoom: 15)
# マーカーを設定
let markers: seq[Mark] = @[
    Mark(name: "埼玉大学", latitude: 35.8627, longtude: 139.6072),
    Mark(name: "桜区役所", latitude: 35.8561, longtude: 139.6098)
  ]

var html = map.renderHtml(markers)    # templateファイルのレンダリング処理
writeHtml("pakuri.html", html)        # HTMLファイルを出力

テンプレートエンジンとnimの値が変換している場所は、compileTemplateFileマクロの部分です。renderHtml関数内の引数名mapmarkersnimjaテンプレートエンジンが自動で読み取り変換し、テンプレートファイルtemplates/pakuri_folium.nimjaの指定された項目に変換して、compileTemplateFileマクロが文字列として出力します。
その変換文字列をファイルとして出力しているだけのプログラムとなります。

出力結果
出力されたpakuri.htmlファイルをブラウザで表示した結果です。

指定された座標にマーカーが設定されて表示されているのがわかると思います。

おわりに

nimpyを使ってfoliumを呼び出せる所で記事は終わりにしようと思いましたが、あまりに芸が無いので、nimjaテンプレートエンジンを使って、地図表示をさせる部分も説明しました。
余談ではありますが、leaflet.jsの作者はウクライナ人です。戦火の中、頑張っていますの応援しましょう。それとnimの作者のドイツ人、お前はもっと頑張れよ!
言っている私は、ただ使って見てるだけですけどね…

Discussion