はじめに
この記事は Elixirアドベントカレンダーのシリーズ4の23日目の記事です
今回は保存したGPSログを地図上に描画していきます
Hooksの改修
Hooksを以下のように修正しています
mounted直下
- init イベントに変更
initイベント
- 引数のlogを addSourceでgeojson形式でログデータを入れる
- addLayerでログデータをどのように描画するかを指定
- startボタンを押すまで自分の位置を取得しなかったので、自分の位置だけを更新する watchを追加
render_logイベント
- 引数のlogでsourceを更新し、再描画する
start_loggingイベント
- 自己位置だけを更新するwatchをクリアして、センターとGPSをLiveView側に送信する watchを追加
stop_loggingイベント
- センターとGPSをLiveView側に送信するwatchをクリアして、 自己位置だけを更新するwatchを追加
genGeoJson関数
- ログデータをGeoJson形式に変換
phoenix/assets/js/hooks.js
Hooks.MapLibere = {
mounted() {
- navigator.geolocation.getCurrentPosition((pos) => {
- const init = [pos.coords.longitude, pos.coords.latitude];
+ this.handleEvent("init", ({ log }) => {
+ navigator.geolocation.getCurrentPosition((pos) => {
+ const init = [pos.coords.longitude, pos.coords.latitude];
const map = new maplibregl.Map({
container: "map",
style: "https://tile2.openstreetmap.jp/styles/osm-bright/style.json",
center: init,
zoom: 12,
});
map.on("load", () => {
map.addControl(new maplibregl.NavigationControl());
+ map.addSource("map_log", {
+ type: "geojson",
+ data: {
+ type: "FeatureCollection",
+ features: genGeoJSON(log),
+ },
+ });
+ map.addLayer({
+ id: "points",
+ type: "circle",
+ source: "map_log",
+ layout: {},
+ paint: {
+ "circle-color": "#0000FF",
+ "circle-radius": 4,
+ },
+ });
});
const user = new maplibregl.Marker().setLngLat(init).addTo(map);
+ const watchID = navigator.geolocation.watchPosition((position) => {
+ const coords = position.coords;
+ window.user.setLngLat([coords.longitude, coords.latitude]);
+ });
+ window.watchID = watchID;
window.user = user;
window.map = map;
});
});
+ this.handleEvent("render_log", ({ log }) => {
+ window.map.getSource("map_log").setData({
+ type: "FeatureCollection",
+ features: genGeoJSON(log),
+ });
+ });
this.handleEvent("start_logging", () => {
+ navigator.geolocation.clearWatch(window.watchID);
const watchID = navigator.geolocation.watchPosition((position) => {
const coords = position.coords;
window.user.setLngLat([coords.longitude, coords.latitude]);
window.map.setCenter([coords.longitude, coords.latitude]);
this.pushEvent("update", {
lat: coords.latitude,
lng: coords.longitude,
});
});
window.watchID = watchID;
});
this.handleEvent("stop_logging", () => {
navigator.geolocation.clearWatch(window.watchID);
+ const watchID = navigator.geolocation.watchPosition((position) => {
+ const coords = position.coords;
+ window.user.setLngLat([coords.longitude, coords.latitude]);
+ });
+ window.watchID = watchID;
});
},
};
+ const genGeoJSON = (log) => {
+ return log.map((latlng) => {
+ return {
+ type: "Feature",
+ properties: {},
+ geometry: {
+ type: "Point",
+ coordinates: latlng,
+ },
+ };
+ });
+ };
export default Hooks
ログデータを取得する list_position/1 を改修
route_idに紐づくpositionを取得していましたが、ログの表示に使うlat,lngだけが欲しいので selectで返すデータを加工します
lib/spotter/loggers.ex
...
def list_positions(route_id) do
from(
pos in Position,
- where: pos.route_id == ^route_id
+ where: pos.route_id == ^route_id,
+ select: [pos.lng, pos.lat]
)
|> Repo.all()
end
end
LiveView側の改修
mount時の既存ログを描画する処理と、新規ログ登録時の処理を追加します
lib/spotter_web/live/route_live/show.ex
defmodule SpotterWeb.RouteLive.Show do
...
@impl true
def handle_params(%{"id" => id}, _, socket) do
+ log = Loggers.list_positions(id)
socket
|> assign(:page_title, page_title(socket.assigns.live_action))
|> assign(:route, Loggers.get_route!(id))
+ |> assign(:log, log)
|> assign(:pos, [0, 0])
|> assign(:record, false)
+ |> push_event("init", %{log: log})
|> then(&{:noreply, &1})
end
...
def handle_event("update", %{"lat" => lat, "lng" => lng}, socket) do
if Geocalc.distance_between(socket.assigns.pos, [lat, lng]) > 100 do
Loggers.create_position(%{lat: lat, lng: lng, route_id: socket.assigns.route.id})
+ new_log = [[lng, lat] | socket.assigns.log]
socket
|> assign(:pos, [lat, lng])
+ |> assign(:log, new_log)
+ |> push_event("render_log", %{log: new_log})
|> then(&{:noreply, &1})
else
{:noreply, socket}
end
end
end
動作確認
追従モードの切り替えと、ログがMap上に描画されるのを確認できました
最後に
本記事ではGPSログデータのMap上への描画と、追従ロジックの改修を行いました
GPSロガーについてはこれで一旦完了になります
次はおまけでネイティブ機能の呼び出しについて解説します
本記事は以上になりますありがとうございました