本記事ではPhoenixのLiveViewでGoogleMapAPIを使用してGPS Loggerを作成していきます
その3ではマーカーの描画、リアルタイムアップデートを実装していきます
LiveViewとGoogleMapAPIで作る GPS Logger 1
LiveViewとGoogleMapAPIで作る GPS Logger 2
LiveViewとGoogleMapAPIで作る GPS Logger 3
LiveViewとGoogleMapAPIで作る GPS Logger short ver
LiveViewとGoogleMapAPIで作る GPS Logger Client watchOS App + SwiftUI
Marker描画
描画するためにの座標(point)を
preload(:points)で関連先を取得します
defmodule LiveMap.Loggers do
...
def get_map_with_points!(id) do
Map
|> preload(:points)
|> Repo.get!(id)
end
...
end
jsで扱いやすいようにEnum.mapで変換してからpush_eventに渡していきます
defmodule LiveMapWeb.MapLive.Show do
...
@impl true
def handle_params(%{"id" => id}, _, socket) do
map = Loggers.get_map_with_points!(id)
points = Enum.map(route.points, fn p -> %{lat: p.lat, lng: p.lng} end)
{
:noreply,
socket
|> assign(:page_title, page_title(socket.assigns.live_action))
|> assign(:map, map)
|> assign(:points, points)
|> push_event("init_map", %{points: points})
}
end
...
end
init_map時にpointsがあった場合1つめのpointの座標をcenterにセットします
次にpointsをforEachでマーカーを描画していきます
通常のマーカーはでかいので青いドットに変更しています
import { Loader } from "@googlemaps/js-api-loader";
let Hooks = {};
Hooks.Map = {
mounted() {
this.handleEvent("init_map", ({ points }) => {
const loader = new Loader({
apiKey: "your API key",
version: "weekly",
});
loader.load().then(() => {
// change below
const center = points.length == 0 ?
{ lat: 33.30639, lng: 130.41806 }
:
{ lat: parseFloat(points[0].lat), lng: parseFloat(points[0].lng) }
const map = new google.maps.Map(document.getElementById("map"), {
center: center,
zoom: 9,
});
window.map = map;
// add below
points.forEach((point) => {
this.addMarker(point)
});
});
});
},
// add below
addMarker(point) {
const marker = new google.maps.Marker({
position: { lat: parseFloat(point.lat), lng: parseFloat(point.lng)},
animation: google.maps.Animation.DROP,
icon: {
path: google.maps.SymbolPath.CIRCLE,
fillColor: '#00F',
fillOpacity: 0.6,
strokeColor: '#00A',
strokeOpacity: 0.9,
strokeWeight: 1,
scale: 2
}
})
marker.setMap(window.map)
}
};
export default Hooks;
real-time update
最後にリアルタイムアップデートの実装になります
PhoenixではデフォルトでPubSubが起動してるので、subscribeとbroadcastを追加するだけです
apiからcreate_pointが実行された後にbroadcastでLiveView側の関数を実行させます
defmodule LiveMap.Loggers do
...
def create_point(attrs \\ %{}) do
%Point{}
|> Point.changeset(attrs)
|> Repo.insert()
|> broadcast(:created_point) # add this
end
# add below
def subscribe({:map, map_id}) do
Phoenix.PubSub.subscribe(LiveMap.PubSub, "map:#{map_id}")
end
def broadcast({:ok, point}, :created_point = event) do
Phoenix.PubSub.broadcast(LiveMap.PubSub, "map:#{point.map_id}", {event, point})
{:ok, point}
end
...
end
LiveView側ではmount時にsubscribeを実行し
handle_infoでcreate_pointのbroadcastで呼ばれたcreated_pointイベントを実行します
created_pointではassignsのpointsの更新とpush_eventで新しいマーカーを描画するJSの関数を実行します
defmodule LiveMapWeb.MapLive.Show do
...
@impl true
def mount(%{"id" => id}, _session, socket) do
Loggers.subscribe({:map, id}) # add this
{:ok, socket}
end
# add below
@impl true
def handle_info({:created_point, point}, socket) do
{
:noreply,
socket
|> update(:points, fn points -> [point | points] |> Enum.reverse end)
|> push_event("created_point", %{ point: %{ lat: point.lat, lng: point.lng} })
}
end
...
end
マーカーの描画は先程実装したので、handleEventで created_pointイベントを追加するだけで完了です
Hooks.Map = {
mounted() {
...
this.handleEvent("created_point", ({ point }) => {
this.addMarker(point)
});
},
...
}
最後に
こちらが最終的な成果物になります
GPSと3Gのモジュールは流石に持ってなかったので、
apple watchのアプリをswift uiで作成しました
watchOSのシミュレーターにはGPSのテストで一定間隔でApple本社あたりをぐるぐる回る物があるので今回はそちらを使用しています
記事3つでだいぶ長くなってしまいましたがLiveViewとGoogleMapAPIでGPS Loggerを作成できました
これを発展させれば、複数のドローンにGPSと3Gモジュールを載せてリアルタイムに状況を確認できますし、
スマートウォッチやスマホアプリでお互いの位置を確認しながらドライブなどいろいろできそうです
次は認証周りを色々詰め込んで長くなったのでLiveViewとGoogleMapAPIのみのショートバージョンになります
コード
https://github.com/thehaigo/live_map/tree/qiita
https://github.com/thehaigo/live_map/tree/render_marker