GeoJSON Polygons in MapboxGL

MapboxGL does not support SVG, so our beautiful polygons won't work out of the box. GeoJSON works great though, and with some tricky extrusion, we can manage the overlaps. Thanks to parallel for the inspiration for the polygon approach!

Buffering and simplifying GeoJSON outputs is not available for all plan levels. Check our plans for details.
Displaying GeoJSON in MapboxGL

GeoJSON for the win!

GET YOUR FREE API KEY to use this example
hide code
<!DOCTYPE html>
<html>

<head>
    <!--  Include jquery - required for XHR requests -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
    <!--  Include mapboxgl javascript and css -->
    <script src="https://api.tiles.mapbox.com/mapbox-gl-js/v0.31.0/mapbox-gl.js"></script>
    <script src="https://unpkg.com/@turf/turf@3.5.2/turf.min.js"></script>
    <link href="https://api.tiles.mapbox.com/mapbox-gl-js/v0.31.0/mapbox-gl.css" rel="stylesheet">
    <!--  Include r360.js -->
    <script src="https://releases.route360.net/r360-js/latest.js"></script>
    <style>
        html, body { width: 100%; height: 100%; margin: 0; }
        #map { width: 100%; height: 100%; }
        .marker-inner {
            background-color: #fff; border: #fff 2px solid;
            border-radius: 50% 50% 50% 0; position: relative;
            cursor: pointer; box-shadow: 1px 1px 1px;
            height: 30px; width: 30px; margin: -15px 0 0 -15px;
            background-size: cover; transform: rotate(-45deg);
        }
        .marker-inner .marker-image {
            background-image: url('../../assets/images/tgm_logo_color_500.png'); background-size: cover; 
            transform: rotate(45deg) translate(-2px,1px); height: 36px;
        }
    </style>
</head>
<body>
    <!--  where the map will live  -->
    <div id="map"></div>
    
    <script>
        // define a pair of coordinates, where the map should be centered
        // and should serve a the source for polygonization
        var lnglat = [13.37, 52.51];
        var travelTimes = [300,600,900,1200,1500,1800];

        // add the map and set the initial center to berlin
        var map = new mapboxgl.Map({
            container: 'map',
            style: r360.getGLStyle('dark', '__APIPLACEHOLDER__'),
            zoom: 12,
            pitch: 65,
            center: lnglat,
            attributionControl: false
        })
        .addControl(new mapboxgl.NavigationControl())
        .addControl(new mapboxgl.AttributionControl({ compact: true }));

        function markerDiv() {
            var el = document.createElement('div');
            var inner = document.createElement('div');
            var img = document.createElement('div');
            el.className = 'marker';
            inner.className = 'marker-inner';
            img.className = 'marker-image';
            inner.appendChild(img);
            el.appendChild(inner);
            return el;
        }

        // 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
        travelOptions.setServiceKey('__APIPLACEHOLDER__');
        // set the service url for your area
        travelOptions.setServiceUrl('https://api.targomo.com/westcentraleurope/');
        // we only have one source which is the marker we just added
        travelOptions.addSource({ lat: lnglat[1], lon: lnglat[0], id: 1 });

        // we want to have a single polygon for 30 minutes
        travelOptions.setTravelTimes(travelTimes);
        // go by foot
        travelOptions.setTravelType('bike');
        // request geojson
        travelOptions.setPolygonSerializer('geojson');
        // simplify polygons a bit to increase perf
        travelOptions.setSimplifyMeter(200);
        // wgs84
        travelOptions.setSrid(4326);

        // ~150m in longitudinal degrees at 52° latitude
        var bufferLengthsMeters = r360.Util.metersInDegrees(150, lnglat[1]);
        travelOptions.setBuffer(Math.round(bufferLengthsMeters.lng * 1000) / 1000); // round to thousands

        // height stops function
        function getHeightStops(travelTimes, heightFactor) {
            return [
                [travelTimes[0], travelTimes.length * (10 * heightFactor)],
                [travelTimes[travelTimes.length - 1], travelTimes.length * heightFactor]
            ]
        }

        // color stop function
        function getColorStops(times) {
            var colors = r360.config.defaultTravelTimeControlOptions.travelTimes.map(function(time, idx) {
                return [times[idx], time.color];
            });
            return colors;
        }

        map.on('load', function() {
            var marker = new mapboxgl.Marker(markerDiv(), {
                offset: [0, -25]
            });
            marker.setLngLat(lnglat).addTo(map);
            // call the r360°- service
            r360.PolygonService.getTravelTimePolygons(travelOptions, function(geojsonPolygons) {
                // get bounds for fiiting view
                var bbox = turf.bbox(geojsonPolygons);

                map.addLayer({
                    'id': 'polygons',
                    'type': 'fill-extrusion',
                    'source': {
                        'type': 'geojson',
                        'data': geojsonPolygons
                    },
                    'layout': {},
                    'paint': {
                        'fill-extrusion-base': 0,
                        'fill-extrusion-height': {
                            'property': 'time',
                            'stops': getHeightStops(travelTimes, 2)
                        },
                        'fill-extrusion-color': {
                            'property': 'time',
                            'stops': getColorStops(travelTimes)
                        },
                        'fill-extrusion-opacity': .5
                    }
                });

                map.fitBounds(bbox, {padding: 20});
            });
        })
    </script>
</body>
</html>