gem MapkickJS for beautiful JavaScript maps with one line of Ruby
MapkickJS is a javascript adapter to display coordinates on Mapbox maps. It requires a Mapbox API key.
mapkick-rb is a Ruby on Rails adapter for MapkickJS. It allows you to easily feed a JSON with coordinates and display a map within your Rails app.
To display a marker on a map, you need to know latitude and longitude GPS coordinates. gem Geocoder allows you to get coordinates based on an address (house, street, city, state, country).
Basic usage #
After installing the gem, initialize your Mapkick API key.
# echo > config/initializers/mapbox.rb
# config/initializers/mapbox.rb
ENV["MAPBOX_ACCESS_TOKEN"] = "pk.eyJ1..."
# ENV["MAPBOX_ACCESS_TOKEN"] = Rails.application.credentials.dig(:mapkick_api_key)
Basic map with multiple options:
<%= js_map [{latitude: 37.7829,
longitude: -122.4190,
label: 'My home',
tooltip: 'Hello!'
}],
id: "cities-map",
width: "800px",
height: "500px",
markers: {color: "#00FF00"},
tooltips: { hover: false, html: true},
style: "mapbox://styles/mapbox/outdoors-v12",
zoom: 15,
controls: true,
refresh: 60 %>
Result - display a marker on draggable map:
HTML tooltips #
Create a helper with a link to the location
page:
# app/helpers/locations_helper.rb
module LocationsHelper
def html_link_to_location(location)
link_to location.name,
location_url(location),
target: '_blank',
style: 'font-weight: bold; color: green'
end
end
To be able to click on the tooltip, use the option { hover: false, html: true}
.
Render the helper method in the tooltip param:
<%= js_map [{latitude: location.latitude,
longitude: location.longitude,
label: location.name,
tooltip: html_link_to_location(location)}],
tooltips: { hover: false, html: true} %>
Result - map with clickable links to locations:
Display multiple locations on the map, JSON #
For this, the best way will be to render /locations.json
:
<%= js_map locations_path(format: :json) %>
Customize the JSON:
// app/views/locations/_location.json.jbuilder
json.extract! location, :latitude, :longitude
json.label location.name
json.tooltip html_link_to_location(location)
// json.tooltip "#{html_link_to_location(location)} <br> #{location.address}"
Result - @locations
is rendered from app/views/locations/index.json.jbuilder
:
JSON with search params #
In this final example, we will factor in having a search form for place
and distance
:
# app/controllers/locations_controller.rb
class LocationsController < ApplicationController
before_action :set_location, only: %i[ show edit update destroy ]
# GET /locations or /locations.json
def index
if params[:place].present?
@locations = Location.near(params[:place], params[:distance] || 10, order: :distance)
# distance 10 km => zoom 13x; distance 100 km => zoom 10x;
# @zoom = params[:distance].eql?('10') ? 13 : 10
else
@locations = Location.all
end
respond_to do |format|
format.html
format.json
end
end
Be sure to add the query params to the path in the view:
<%= js_map locations_path(format: :json, place: params[:place], distance: params[:distance]), zoom: @zoom %>
Result - show only location within set distance
from geocoded coordinates of place
:
Bonus: Search for locations that offer a specific product #
Business problem #1: Find hotels that have a SPA
Business problem #2: Find hotels that have a Massage
Here we are solving the problem: “find all parents with children that have a particular attribute”.
In the below example location has_many :products
&& Product.name = String
.
Add product_name
search field:
<%= form_with url: locations_path, method: :get do |form| %>
<%= form.text_field :product_name, value: params[:product_name] %>
<%= form.text_field :place, value: params[:place] %>
<%= form.select :distance, [10, 100], selected: params[:distance] %>
<%= form.submit %>
<% end %>
Find locations that have product you are searching for:
# app/controllers/locations_controller.rb
def index
locations = Location.joins(:products).includes(:products) # initially select only locations that have products
if params[:product_name].present?
products = Product.where('name ILIKE ?', "%#{params[:product_name]}%")
location_ids = products.select(:location_id).distinct
locations = locations.where(id: location_ids)
end
if params[:place].present?
locations = locations.near(params[:place], params[:distance] || 10, order: :distance)
end
@locations = locations
end
Don’t forget to add product_name: params[:product_name]
to the JSON map path:
<%= js_map locations_path(format: :json, place: params[:place], product_name: params[:product_name], distance: params[:distance]) %>
Result: find locations that offer a specific product/service:
That’s it! Now you can build your own AIRNBN search frontend!
Did you like this article? Did it save you some time?