I have different hiking routes (in geoJSON format) displayed on a map. They contain linestrings, multilinestrings and markers. Currently, it looks like this: http://www.hikethisway.today/Sweden.html.

I would like to cluster the routes in circles where the size of the circle is determined by the length of the route (have a distance attribute in the geoJSON's). Something like this: https://atendesigngroup.com/blog/openlayers-plus So when zooming out to a certain level, the circle clusters appear and when clicking on a circle cluster the route (line + markers) appear.

I'm currently using Leaflet and the desired output in the second link is using Openlayers to create the visualization. Is it possible to achieve the same result using Leaflet or will I have to move to Openlayers?

  • Any suggestions?
    – Saud
    Commented Nov 29, 2017 at 6:38

1 Answer 1


Yes, it is definitely possible.

There would be nothing too complex about it, but it depends exactly on how is structured your GeoJSON data for each hiking route (sounds like your route includes a LineString and some Points).

The idea would be to gather your hiking route (i.e. LineString and Points) into a Leaflet Layer Group, and to associate it with its Circle Marker representation. You can adjust the circle radius according to some property of your LineString. The position of the circle could be computed as the center of the route bounds for example, but you could use any other computation you wish.

Then, using the map "zoomend" event, you determine whether you want to show the Circle Marker or the Layer Group.

var groups = [];

// Process each single routes (only one here)
// and add them into the groups global array.
// This assumes each "single route" contains only 1 LineString.

map.on('zoomend', onZoomEnd);

function onZoomEnd() {
  var newZoom = map.getZoom();

  if (newZoom >= 12) {
    groups.forEach(function (group) {
  } else {
    groups.forEach(function (group) {

function processSingleRoute(singleRouteGeoJSON) {
  var singleRouteContainer = {};

  singleRouteContainer.route = L.geoJSON(singleRouteGeoJSON, {
    onEachFeature: function (feature, layer) {
      if (feature.geometry.type === 'LineString') {
        // Compute a "center" of the route.
        var center = layer.getBounds().getCenter();

        // Assuming only 1 LineString in singleRouteGeoJSON.
        singleRouteContainer.circle = L.circleMarker(center, {
          radius: feature.properties.distance // in pixels
        }).bindPopup('Distance: ' + feature.properties.distance);
      layer.bindPopup('Name: ' + feature.properties.name + '<br />Distance: ' + feature.properties.distance);

  return singleRouteContainer;

Live demo: https://plnkr.co/edit/3E4i8FjgDPrB8bc1uV9z?p=preview

  • Thank you for your answer. In your script you define the GeoJSON file as singleRoute but in the function you use singleRouteGeoJSON. Where does singleRouteGeoJSON come from? Also, you mention/assume in your code that each route only contains one linestring. What if there are two or more in one route?
    – Saud
    Commented Dec 11, 2017 at 20:01
  • singleRouteGeoJSON is a function argument. As said above, the exact code then depends on how is structured your GeoJSON data for each hiking route. Here I assumed you have an array of FeatureCollection's, each collection being a single route with 1 LineString and possibly several Points, as suggested in your question. Either you refactor your data to fit this assumption, or you adjust the code to match your data structure. Should you need further help in doing so, please feel free to open a new question, with relevant details and data sample.
    – ghybs
    Commented Dec 12, 2017 at 2:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.