NimでGoogleMapっぽいマップ表示をしてみた
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ライブラリを呼び出すソース
#[
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ライブラリを仮想環境に入れて、何時も実行しているため、再度
pip
でfolium
を入れて確認しました。
テンプレートエンジンを使った地図マップの動作確認
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
とします。
<!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側のオブジェクトが存在した時だけ、その内部のタブが表示されます。
今回は、Map
とMarker
オブジェクトをnim側で作成し、nimja
テンプレートエンジン側で、変換させる所を行います。
3. nim側のソース
nim側のソースには、nimja/parser
をインポートして、Map
には表示させる基準座標、Mark
にはマーカーを指定してテンプレートエンジンに渡すようにします。
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
関数内の引数名map
、markers
をnimja
テンプレートエンジンが自動で読み取り変換し、テンプレートファイルtemplates/pakuri_folium.nimja
の指定された項目に変換して、compileTemplateFile
マクロが文字列として出力します。
その変換文字列をファイルとして出力しているだけのプログラムとなります。
出力結果
出力されたpakuri.html
ファイルをブラウザで表示した結果です。
指定された座標にマーカーが設定されて表示されているのがわかると思います。
おわりに
nimpy
を使ってfolium
を呼び出せる所で記事は終わりにしようと思いましたが、あまりに芸が無いので、nimja
テンプレートエンジンを使って、地図表示をさせる部分も説明しました。
余談ではありますが、leaflet.js
の作者はウクライナ人です。戦火の中、頑張っていますの応援しましょう。それとnim
の作者のドイツ人、お前はもっと頑張れよ!
言っている私は、ただ使って見てるだけですけどね…
Discussion