GeoJSON Polygon Holes with TurfJS

As mentioned in our basic GeoJSON example, selecting GeoJSON as the return type results in overlapping polygons. Although removing overlapping areas will not look as nice as using SVG, it is possible. In this example we are using TurfJS to cut out the overlapping areas in the polygons.

Buffering and simplifying GeoJSON outputs is not available for all plan levels. Check our plans for details.
Displaying GeoJSON with holes using TurfJS

Client-side geoprocessing

GET YOUR FREE API KEY to use this example
hide code
<!DOCTYPE html>
  <!--  Include jquery - required for XHR requests -->
  <script src=""></script>
  <!--  Include leaflet javascript and css -->
  <link rel="stylesheet" href="" crossorigin="">
  <script src="" crossorigin=""></script>
  <!-- turfjs for polygons holes -->
  <script src=""></script>
  <!--  Include r360.js -->
  <script src=""></script>

    html, body { width: 100%; height: 100%; margin: 0; }
    #map { width: 100%; height: 100%; }

  <!--  where the map will live  -->
  <div id="map"></div>
    // define a pair of coordinates, where the map should be centered
    // and should serve a the source for polygonization
    var latlon = [52.51, 13.37];

    // add the map and set the initial center to berlin
    var map ='map').setView(latlon, 14);
    map.attributionControl.addAttribution("ÖPNV Daten © <a href='' target='_blank'>VBB</a>");

    // initialise the base map
    r360.basemap({ style: 'basic', apikey: '__APIPLACEHOLDER__' }).addTo(map);

    // create the marker and add it to the map
    var marker = L.marker((latlon)).addTo(map);

    // you need to define some options for the polygon service
    // for more travel options check out the other tutorials
    var travelOptions = r360.travelOptions();
    // please contact us and request your own key if you don't already have one
    // set the service url for your area
    // we only have one source which is the marker we just added
    // we want to have 2 polygons: 10 & 20 minutes
    // go by foot
    // request geojson
    // wgs84
    // ~150m in longitudinal degrees at 52° latitude
    var bufferLengthsMeters = r360.Util.metersInDegrees(150, latlon[0]);
    travelOptions.setBuffer(Math.round(bufferLengthsMeters.lng * 1000) / 1000); // round to thousands

    var styler = function(time){
      return {
        color: "#999",
        weight: 1,
        opacity: 1,
        fillOpacity: 0.4,
        fillColor: time === 1200 ? "#C1272D" : "#006837"

    var geoJsonLayer = L.geoJson(null, {
      style: function(feature){
        return styler(;

    // call the r360°- service
    r360.PolygonService.getTravelTimePolygons(travelOptions, function(geojsonPolygons){

      var poly1200 = geojsonPolygons.features.filter(function(poly){
        return === 1200;
      var poly600 = geojsonPolygons.features.filter(function(poly){
        return === 600;

      var poly1200_holy = turf.difference(poly1200, poly600);
        "type": "FeatureCollection",
        "features": [poly1200_holy, poly600]