This was just a quick copy/paste of snippets from a project.
I dod not get to test this version yet.
| // Add this to your app.js phoenix application | |
| let Hooks = {} | |
| Hooks.Map = { | |
| mounted(){ | |
| const markers = {} | |
| const map = L.map('mapid').setView([51.505, -0.09], 14) | |
| const paths = {} | |
| let geojsonLayer = L.geoJSON().addTo(map).setStyle({color: "#6435c9"}) | |
| L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', { | |
| attribution: 'Map data © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>', | |
| maxZoom: 18, | |
| id: 'mapbox/streets-v11', | |
| tileSize: 512, | |
| zoomOffset: -1, | |
| accessToken: YOUR_MAPBOX_ACCESS_TOKEN | |
| }).addTo(map) | |
| this.handleEvent("update_marker_position", ({reference, lat, lon, center_view}) => { | |
| markers[reference].setLatLng(L.latLng(lat, lon)) | |
| if (center_view) { | |
| map.flyTo(L.latLng(lat, lon)) | |
| } | |
| }) | |
| this.handleEvent("draw_path", ({reference, coordinates, color}) => { | |
| data = { | |
| "type": "LineString", | |
| "coordinates": coordinates | |
| } | |
| geojsonLayer.addData(data) | |
| }) | |
| this.handleEvent("view_init", ({reference, lat, lon, zoom_level = 20}) => { | |
| geojsonLayer.remove() | |
| geojsonLayer = L.geoJSON().addTo(map).setStyle({color: "#6435c9"}) | |
| map.setView(L.latLng(lat, lon), zoom_level) | |
| }) | |
| this.handleEvent("set_zoom_level", ({zoom_level}) => { | |
| map.setZoom(zoom_level) | |
| }) | |
| this.handleEvent("add_marker", ({reference, lat, lon}) => { | |
| // lets not add duplicates for the same marker! | |
| if (markers[reference] == null) { | |
| const marker = L.marker(L.latLng(lat, lon)) | |
| marker.addTo(map) | |
| markers[reference] = marker | |
| } | |
| }) | |
| this.handleEvent("add_marker_with_popup", ({reference, lat, lon, link}) => { | |
| // lets not add duplicates for the same marker! | |
| if (markers[reference] == null) { | |
| const marker = L.marker(L.latLng(lat, lon)) | |
| marker.bindPopup(`<a href=\"${link}\">${reference}</a>`) | |
| marker.addTo(map) | |
| markers[reference] = marker | |
| } | |
| }) | |
| this.handleEvent("clear", () => { | |
| geojsonLayer.remove() | |
| geojsonLayer = L.geoJSON().addTo(map) | |
| for (const [reference, value] of Object.entries(markers)) { | |
| marker = markers[reference] | |
| marker.remove() | |
| markers.delete(reference) | |
| } | |
| }) | |
| this.handleEvent("remove_marker", ({reference}) => { | |
| if (markers[reference] != null) { | |
| marker = markers[reference] | |
| marker.remove() | |
| markers.delete(reference) | |
| } | |
| geojsonLayer.remove() | |
| }) | |
| } | |
| } |
| defmodule Components.LeafletMap do | |
| use Phoenix.Component | |
| attr :class, :string, default: nil | |
| def map(assigns) do | |
| ~H""" | |
| <div style="overflow: hidden" phx-update="ignore" id="mapcontainer"> | |
| <div class={@class} phx-hook="Map" id="mapid"></div> | |
| </div> | |
| """ | |
| end | |
| # might be off if the maps shape is not close to a square. | |
| def calculate_initial_map_zoom_level(n, e, s, w) do | |
| lat_to_radiant = fn lat -> | |
| sin = :math.sin(lat * :math.pi() / 180) | |
| radX2 = :math.log((1 + sin) / (1 - sin)) / 2 | |
| max(min(radX2, :math.pi()), -:math.pi()) / 2 | |
| end | |
| lat_difference = abs(lat_to_radiant.(n) - lat_to_radiant.(s)) | |
| lon_difference = abs(e - w) | |
| lat_fraction = lat_difference / :math.pi() | |
| lon_fraction = lon_difference / 360 | |
| # Ensure we never get 0 in division. 1.0e-5 was chosen arbitrarily after trying different values. | |
| lat_zoom = :math.log(1 / max(lat_fraction, 1.0e-5)) / :math.log(2) | |
| lon_zoom = :math.log(1 / max(lon_fraction, 1.0e-5)) / :math.log(2) | |
| # Slight zoom out for vertical dimension, because our map view is very wide and not square. | |
| min(lat_zoom - 0.5, lon_zoom) | |
| |> min(20) # Lets not zoom in to infinity | |
| end | |
| def liveview_setup_map(socket, opts \\ []) do | |
| socket | |
| |> assign(selected_address: address) | |
| |> Phoenix.LiveView.push_event("view_init", %{ | |
| reference: opts[:reference], | |
| lat: opts[:latitude], | |
| lon: opts[:longitude], | |
| zoom_level: opts[:zoom_level] || 15 | |
| }) | |
| |> Phoenix.LiveView.push_event("add_marker", %{ | |
| reference: opts[:reference], | |
| lat: opts[:latitude], | |
| lon: opts[:longitude] | |
| }) | |
| |> Phoenix.LiveView.push_event("update_marker_position", %{ | |
| reference: opts[:reference], | |
| lat: address[:latitude], | |
| lon: address[:longitude], | |
| center_view: true | |
| }) | |
| end | |
| end |
| defmodule MyAppWeb.MapLive do | |
| use MyAppWeb, :live_view | |
| @impl true | |
| def mount(%{"id" => id}, _session, socket) do | |
| opts = [latitude: 51.123456, longitude: 7.123456, reference: "main"] | |
| LeafletMap.liveview_set_map_to_address(socket, opts) | |
| end | |
| @impl true | |
| def render(assigns) do | |
| ~H""" | |
| <div class="h-screen bg-gray-100"> | |
| <div class="flex h-screen justify-center items-center rounded-md shadow-lg"> | |
| <LeafletMap.map class="h-80" /> | |
| </div> | |
| </div> | |
| """ | |
| end | |
| end |
@nomtrosk right, sorry for rushing it out like this, but glad you got it to work in the end!
If someone else is struggling with this, I’ve outlined a straightforward approach that works without extra customization. However, since the hook can be highly dependent on individual project needs, I don’t think creating a Hex package is justified.
Simbolismo-Digital/prever@aae7c2d
Tip: remember running mix assets.setup
Great approach to get leaflet working in Elixir!
I spent some time getting this to work: The biggest time consumer was to discover that the
flexclass in line 15 of map_live would render the map gray. Anyways, here is what else i changed to make it runliveview_setup_map(looked like some left overs)liveview_setup_mapinstead ofliveview_set_map_to_addressinmap_live.ex<head>inroot.html.heexfrom hosted source https://leafletjs.com/download.htmlalias Components.LeafletMap, as: LeafletMapto map_live.exapp.jsby altering this line: