Mapbox Clusters with Ruby on Rails

Rui Freitas
Light the Fuse and Run
3 min readApr 24, 2019

Aggregate points on a map in clusters with Ruby on Rails and Mapbox

When we have a geocoded model in Rails, we may want to display the records in our database as markers on a map. However, once you begin to have too many records close to each other, it ruins the map experience for your users.

One solution to this problem is to aggregate markers that are close to each other in clusters which break apart as soon as the zooms in. Mapbox’s mapbox-gl API has this functionality and we’ll implement it within the context of Rails.

We’re going to be using Ruby on Rails 5 with Webpacker 4 and Turbolinks, but the setup should be similar with other versions. In our example, we have a Car model that is geocoded — it has latitude and longitude coordinates.

Let’s set up the backend first.

We want to display a map with Carinstances on the index action of CarsController:

class CarsController < ApplicationController
# ...
def index
@cars = Car.where.not(latitude: nil, longitude: nil)
@geojson = build_geojson
end
private def build_geojson
{
type: "FeatureCollection",
features: @cars.map(&:to_feature)
}
end
end

We only want to display Car instances that have been properly geocoded, so we exclude those without latitude and longitude. Then we build the geojson object required by Mapbox to build our clusters. We’re delegating the responsibility of each instance to render itself as a feature:

class Car < ApplicationRecord
# ...
def coordinates
[longitude, latitude]
end
def to_feature
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": coordinates
},
"properties": {
"car_id": id,
"name": name,
"info_window": ApplicationController.new.render_to_string(
partial: "cars/infowindow",
locals: { car: self }
)
}
}
end

Later we’ll want to open a popup when we click on an individual point on the map, so we’re rendering it as HTML and adding it to our feature hash.

Finally, we pass the geojson to the view so it can be picked up by JavaScript:

<div id="map"
data-cars="<%= @geojson.to_json %>"
data-mapbox-api-key="<%= ENV['MAPBOX_API_KEY'] %>">
</div>

First, we add Mapbox GL JS API to our Rails app with yarn add mapbox-gl.

There are several parts in displaying the cluster. Once the map loads, we need to add a geojson data source, which we parse from the data attributes we added to the map element:

map.on('load', function() {
const cars = JSON.parse(mapElement.dataset.cars);
map.addSource('cars', {
type: 'geojson',
data: cars,
cluster: true,
clusterMaxZoom: 14,
clusterRadius: 50
});
}

Then we need to add three layers to the maps. One to display the cluster circles which aggregate our geojson features. Another one to display the count of aggregated features inside each cluster circle. Finally, a layer to display the circle for unaggregated or individual features.

Next, we want to set up some events. For the aggregated clusters, we want the cursor to become a pointer once we hover on a cluster and we want to zoom in on and center the map on that cluster on click.

For unaggregated features, we want to center the map on click and also display a pointer cursor when hovering on them.

Here’s the full code:

You can check a live example app here: https://mapbox-clusters.herokuapp.com/ and the source code here: https://github.com/rodloboz/mapbox-clusters

--

--

Rui Freitas
Light the Fuse and Run

Lead Teacher @ Le Wagon | Web Developer @ Light the Fuse and Run: http://lightthefuse.run/ | Photographer @ Rod Loboz: https://blog.rodloboz.com/