
Set up basic rails app
- we will use database PostgreSQL, esbuild to bundle the project's JavaScript, and TailwindCSS for its CSS. We'll also skip installing a test framework by passing the -T flag.
rails new leaflet_map-T-d postgresql--css=tailwind--javascript=esbuildcdleaflet_maprails db:create
- Configure Tailwind: In
application.tailwind.css
replace the@tailwind
directives with@import
@import"tailwindcss/base";@import"tailwindcss/components";@import"tailwindcss/utilities";
Create basic MVC
- create
Place
model which will have longitude, latitude and name attributes
rails g model Place name:string longitude:float latitude:floatrails db:migrate
- add simple validation for name, longitude and latitude on
Place
model
classPlace<ApplicationRecordvalidates:name,presence:truevalidates:latitude,numericality:{greater_than_or_equal_to:-90,less_than_or_equal_to:90}validates:longitude,numericality:{greater_than_or_equal_to:-180,less_than_or_equal_to:180}end
- update
seed.rb
file to create some data for places
Place.create(name:'Place-1',longitude:-70.06,latitude:39.35)Place.create(name:'Place-2',longitude:-55.30,latitude:35.20)Place.create(name:'Place-3',longitude:-80.20,latitude:25.20)Place.create(name:'Place-4',longitude:-90.20,latitude:15.20)
Run
rails db:seed
to seed dataCreate
PlacesController
rails g controller Places index
it will createPlacesController
with index action as well as view files for index
- Update
PlacesController
to show list of places
classPlacesController<ApplicationControllerdefindex@places=Place.allendend
- Also, update
route.rb
to make that index, home page
Rails.application.routes.drawdoroot'places#index'end
To test that every thing is working so far, run rails server withbin/rails
and go tolocalhost
You can see default view provided byviews/places/index.html.erb
Create basic map with leaflet
- install leaflet
yarn add leaflet
- update
views/layouts/index.html.erb
<!DOCTYPE html><html><head><title>LeafletMap</title><metaname="viewport"content="width=device-width,initial-scale=1"><%=csrf_meta_tags%><%=csp_meta_tag%><%=stylesheet_link_tag"application","data-turbo-track":"reload"%><%=javascript_include_tag"application","data-turbo-track":"reload",defer:true%></head><body><mainclass="container mx-auto mt-28 px-5"><%=yield%></main></body></html>
We just wrap main content that is<%= yield %>
with<main>
just to give it a little bit style
- Update
views/places/index.html.erb
this is place where we will display our map
<divclass="w-full"><h1class="font-bold text-4xl mb-2">Places</h1><divdata-controller="maps"><divdata-maps-target="container"class="h-[75vh] w-auto"></div></div></div>
The main things you need to focus here isdata-controller="map"
indicates stimulus controller anddata-map-target="container"
this allow us to definecontainerTarget
on our map stimulus controller
- Generate map stimulus controller
rails g stimulus map
- update
map_controller.js
import{Controller}from"@hotwired/stimulus"importLfrom"leaflet"exportdefaultclassextendsController{statictargets=["container"]connect(){this.createMap()this.map.setView([27.700769,85.300140],12);}createMap(){this.map=L.map(this.containerTarget)L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png',{maxZoom:19,attribution:'© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'}).addTo(this.map);}disconnect(){this.map.remove();}}
Here, first we import L from leaflet.when our element with attributedata-controller = "map"
enters DOMconnect
will be called in which we usecreateMap
function to create map.
With linestatic targets = ["container"]
we add container target which we defined in our HTML withdata-map-target="container"
now that element can be access withthis.containerTarget
Inside ofcreateMap()
function with linethis.map = L.map(this.containerTarget)
we initialize empty map insidecontainerTarget
element and we added tile layer to our map withL.tileLayer
After creating map and adding tile, we set map's view to Kathmandu, Nepal with linethis.map.setView([27.700769, 85.300140], 12)
- Add
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.3/dist/leaflet.css" integrity="sha256-kLaT2GOSpHechhsozzB+flnD+zUyjE2LlfWPgU04xyI=" crossorigin=""/>
inside head section ofapplication.html.erb
file for styling map
<!DOCTYPE html><html><head><title>LeafletMap</title><metaname="viewport"content="width=device-width,initial-scale=1"><%=csrf_meta_tags%><%=csp_meta_tag%><%=stylesheet_link_tag"application","data-turbo-track":"reload"%><linkrel="stylesheet"href="https://unpkg.com/leaflet@1.9.3/dist/leaflet.css"integrity="sha256-kLaT2GOSpHechhsozzB+flnD+zUyjE2LlfWPgU04xyI="crossorigin=""/><%=javascript_include_tag"application","data-turbo-track":"reload",defer:true%></head><body><mainclass="container mx-auto mt-28 px-5"><%=yield%></main></body></html>
Now, restart rails server you can see map of Kathmandu,Nepal
Showing our list of places on map
- Update
index.html.erb
<divclass="w-full"><h1class="font-bold text-4xl mb-2">Places</h1><divdata-controller="map"data-map-latlong-value="<%=@places.pluck(:latitude, :longitude)%>"><divdata-map-target="container"class="h-[75vh] w-auto"></div></div></div>
Here, we adddata-map-latlong-value="<%=@places.pluck(:latitude, :longitude)%>"
which we can access withthis.latlongValue
from ourmap_controller.js
- Update
map_controller.js
import{Controller}from"@hotwired/stimulus"importLfrom"leaflet"exportdefaultclassextendsController{statictargets=["container"]staticvalues={latlong:Array}connect(){this.createMap()this.map.fitBounds(this.latlongValue)this.latlongValue.forEach(place=>this.addMarker(place))}createMap(){this.map=L.map(this.containerTarget)L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png',{maxZoom:20,attribution:'© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'}).addTo(this.map);}addMarker(place){const[latitude,longitude]=place;L.marker([latitude,longitude]).addTo(this.map).bindPopup(`<div>latitude:${latitude}</div><div>longitude:${longitude}</div>`)}disconnect(){this.map.remove();}}
Linestatic values = { latlong: Array }
add value latlong which datatype will be array and now we can access this value which we defined inindex.html.erb
withthis.latlongValue
Instead of showing map of Kathmandu, Nepal withsetView()
function we usefitBounds()
which take list of latitude and longitude and map will be adjusted to fit all of provided latitude and longitude
And for each latitude and longitude value we call functionaddMarker
. OuraddMarker
function is responsible for adding marker to provided longitude and latitude and we also add popup showing it's latitude and longitude which will appear while clicking on marker withbindPopup
Now our map is look like this
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse