LoginSignup
5
2

More than 1 year has passed since last update.

LiveViewとGoogleMapAPIで作る GPS Logger short ver

Last updated at Posted at 2021-06-30

本記事ではPhoenixのLiveViewでGoogleMapAPIを使用してGPS Loggerを作成していきます

今回は認証周りをすべて省いてLiveViewによるGoogleMapの更新のみに焦点を当てています

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

プロジェクト作成、DB,GoogleMapセットアップ

mix phx.new google_map --live
cd google_map
cd assets
npm install @googlemaps/js-api-loader
cd ..
mix phx.gen.schema Loggers.Point points lat:decimal lng:decimal
mix ecto.create
mix ecto.migrate
assets/js/app.js
import "phoenix_html"
import {Socket} from "phoenix"
import topbar from "topbar"
import {LiveSocket} from "phoenix_live_view"
import Hooks from './hooks.js' // add this

let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
// modify below
let liveSocket = new LiveSocket("/live", Socket, {
  hooks: Hooks,
  params: {_csrf_token: csrfToken}
})

...
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(() => {
        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;

        points.forEach((point) => {
          this.addMarker(point)
        });
      });
    });
    this.handleEvent("created_point", ({ point }) => {
      this.addMarker(point)
    });
  },
  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;

List, Create, Subscribe, Broadcast

lib/google_map/loggers.ex
defmodule GoogleMap.Loggers do
  import Ecto.Query, warn: false
  alias GoogleMap.Repo

  alias GoogleMap.Loggers.Point

  def list_points do
      Repo.all(Point)
  end

  def create_point(attrs \\ %{}) do
    %Point{}
    |> Point.changeset(attrs)
    |> Repo.insert()
    |> broadcast(:created_point)
  end

  def subscribe(:map) do
    Phoenix.PubSub.subscribe(GoogleMap.PubSub, "map")
  end

  def broadcast({:ok, point}, :created_point = event) do
    Phoenix.PubSub.broadcast(GoogleMap.PubSub, "map", {event, point})
    {:ok, point}
  end
end

mount時、座標登録時の処理

lib/google_map_web/live/page_live.ex
defmodule GoogleMapWeb.PageLive do
  use GoogleMapWeb, :live_view
  alias GoogleMap.Loggers

  @impl true
  def mount(_params, _session, socket) do
    Loggers.subscribe(:map)
    points = Loggers.list_points |> Enum.map(fn p -> %{lat: p.lat, lng: p.lng} end)
    {
      :ok,
      socket
      |> push_event("init_map", %{points: points})
    }
  end

  @impl true
  def handle_info({:created_point, point}, socket) do
    {
      :noreply,
      socket
      |> push_event("created_point", %{ point: %{ lat: point.lat, lng: point.lng} })
    }
  end
end
[lib/google_map_web/live/page_live.html.leex].html
<h1>Show Map</h1>
<section id="googleMap" phx-update="ignore" phx-hook="Map">
  <div id="map" style="height: 400px; border-radius:2em;"></div>
</section>

座標登録API

lib/google_map_web/controllers/point_controller.ex
defmodule GoogleMapWeb.PointController do
  use GoogleMapWeb, :controller

  alias GoogleMap.Loggers
  alias GoogleMap.Loggers.Point

  def create(conn, point_params) do
    with {:ok, %Point{}} <- Loggers.create_point(point_params) do
      send_resp(conn, 200, "ok")
    end
  end
end
lib/google_map_web/router.ex
defmodule GoogleMapWeb.Router do
...
  scope "/api", GoogleMapWeb do
    pipe_through :api

    resources "/points", PointController, only: [:create]
  end
...
end

最後に

いかがでしたでしょうか?
認証周りを除外すると少ないコードでGoogleMapのリアルタイム更新が実現できているのがわかります
GoogleMapAPIにはマーカーの描画だけではなく、scrollやハイライトなどいろいろあるので是非試してみてください
本記事は以上になります

次はGPSの送信側の記事をapple watch app, ReactNative辺りで書けたらと思います

コード

5
2
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
5
2