4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

LiveViewとGoogleMapAPIで作る GPS Logger 3

Last updated at Posted at 2021-06-30

本記事では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)で関連先を取得します

lib/live_map/loggers.ex
defmodule LiveMap.Loggers do
...
  def get_map_with_points!(id) do
    Map
    |> preload(:points)
    |> Repo.get!(id)
  end
...
end

jsで扱いやすいようにEnum.mapで変換してからpush_eventに渡していきます

lib/live_map_web/live/map_live/show.ex
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でマーカーを描画していきます
通常のマーカーはでかいので青いドットに変更しています

assets/js/hooks.js
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側の関数を実行させます

lib/live_map/loggers.ex
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の関数を実行します

lib/live_map_web/live/map_live/show.ex
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イベントを追加するだけで完了です

assets/js/hooks.js
Hooks.Map = {
  mounted() {
...
    this.handleEvent("created_point", ({ point }) => {
      this.addMarker(point)
    });
  },
...
}

最後に

こちらが最終的な成果物になります
GPSと3Gのモジュールは流石に持ってなかったので、
apple watchのアプリをswift uiで作成しました
watchOSのシミュレーターにはGPSのテストで一定間隔でApple本社あたりをぐるぐる回る物があるので今回はそちらを使用しています

Image from Gyazo

記事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

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?