Skip to content

Instantly share code, notes, and snippets.

@ryanbaumann
Last active February 3, 2023 08:05
Show Gist options
  • Save ryanbaumann/04c442906638e27db9da243f29195592 to your computer and use it in GitHub Desktop.
Save ryanbaumann/04c442906638e27db9da243f29195592 to your computer and use it in GitHub Desktop.
Example of loading large geojson into Mapbox GL JS

Example - Large geojson in Mapbox GL JS

* Load svg icon appears while data is loading
* Uncompressed geojson file is 29 MB on dropbox
* Total time to load depends on client internet speed & client GPU
* Geojson source is split into two parts to optimize load and render time
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title></title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v2.6.0/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v2.6.0/mapbox-gl.css' rel='stylesheet' />
<link href='https://www.mapbox.com/base/latest/base.css' rel='stylesheet' />
<style>
body {
margin: 0;
padding: 0;
}
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
.map-overlay {
position: absolute;
width: 180px;
top: 0;
left: 10px;
padding: 10px;
margin-left: 5px;
margin-top: 2px;
margin-bottom: 2px;
margin-right: 5px;
z-index: 1;
}
.map-overlay .map-overlay-inner {
background: rgba(0, 0, 0, .8);
color: #fff;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.10);
border-radius: 3px;
padding: 10px;
margin-bottom: 10px;
z-index: 1;
}
.map-overlay-inner fieldset {
border: none;
padding: 0;
margin: 0 0 10px;
z-index: 1;
}
/* Dark attribution */
.mapboxgl-ctrl.mapboxgl-ctrl-attrib {
background: rgba(0, 0, 0, .8);
}
.mapboxgl-ctrl.mapboxgl-ctrl-attrib a {
color: #fff;
}
/* Dark popup */
.mapboxgl-popup-content {
background-color: #202020;
color: #fff;
margin-left: 5px;
margin-top: 2px;
margin-bottom: 2px;
margin-right: 5px;
z-index: 1000;
}
.mapboxgl-popup-anchor-bottom-left .mapboxgl-popup-tip,
.mapboxgl-popup-anchor-bottom-right .mapboxgl-popup-tip,
.mapboxgl-popup-anchor-bottom .mapboxgl-popup-tip {
border-top-color: #202020;
}
.mapboxgl-popup-anchor-top-left .mapboxgl-popup-tip,
.mapboxgl-popup-anchor-top-right .mapboxgl-popup-tip,
.mapboxgl-popup-anchor-top .mapboxgl-popup-tip {
border-bottom-color: #202020;
}
.mapboxgl-popup-anchor-right .mapboxgl-popup-tip {
border-left-color: #202020;
}
.mapboxgl-popup-anchor-left .mapboxgl-popup-tip {
border-right-color: #202020;
}
#popup-menu ul,
#menu li {
margin: 0;
padding: 0;
z-index: 100;
}
.mapboxgl-ctrl-group {
-webkit-filter: invert(100%);
}
.loader {
margin: -10px 0 0 -250px;
height: 100px;
width: 20%;
position: fixed;
text-align: center;
padding: 1em;
top: 50%;
left: 50%;
margin: 0 auto 1em;
z-index: 9999;
}
/*
Set the color of the icon
*/
svg path,
svg rect {
fill: #FF6700;
}
</style>
</head>
<body>
<div id='map'></div>
<div class='map-overlay top'>
<div class='map-overlay-inner'>
<fieldset>
<label><b>Select property</b></label>
<select id='prop' name='prop'>
<option value='CYC_INJ'>Cyclist Injuries</option>
<option value='CYC_KIL'>Cyclist Fatalities</option>
<option value='PED_INJ'>Pedestrian Injuries</option>
<option value='PED_KIL'>Pedestrian Fatalities</option>
</select>
</fieldset>
<b><a href="https://data.cityofnewyork.us/Public-Safety/NYPD-Motor-Vehicle-Collisions/h9gi-nx95"
target="_blank">NYC Open Data</a></b>
</div>
</div>
<div class="loader loader--style1" title="0" id="loader">
<svg version="1.1" id="loader-1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px" y="0px" width="40px" height="40px" viewBox="0 0 40 40" enable-background="new 0 0 40 40"
xml:space="preserve">
<path opacity="0.2" fill="#000"
d="M20.201,5.169c-8.254,0-14.946,6.692-14.946,14.946c0,8.255,6.692,14.946,14.946,14.946
s14.946-6.691,14.946-14.946C35.146,11.861,28.455,5.169,20.201,5.169z M20.201,31.749c-6.425,0-11.634-5.208-11.634-11.634
c0-6.425,5.209-11.634,11.634-11.634c6.425,0,11.633,5.209,11.633,11.634C31.834,26.541,26.626,31.749,20.201,31.749z" />
<path fill="#000" d="M26.013,10.047l1.654-2.866c-2.198-1.272-4.743-2.012-7.466-2.012h0v3.312h0
C22.32,8.481,24.301,9.057,26.013,10.047z">
<animateTransform attributeType="xml" attributeName="transform" type="rotate" from="0 20 20"
to="360 20 20" dur="0.5s" repeatCount="indefinite" />
</path>
</svg>
</div>
<script>
mapboxgl.accessToken = 'pk.eyJ1IjoicnNiYXVtYW5uIiwiYSI6ImNqNmhkZnhkZDA4M3Yyd3AwZDR4cmdhcDIifQ.TGKKAC6pPP0L-uMDJ5xFAA';
var bounds = [
[-75.04728500751165, 39.68392799015035],
[-72.91058699000139, 41.87764500765852]
];
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/dark-v10',
center: [-74.0059, 40.7128],
zoom: 10,
minZoom: 9,
maxZoom: 18,
pitch: 40,
maxBounds: bounds
});
function init() {
map.addSource('veh-incidents-1', {
type: 'geojson',
data: 'https://data.cityofnewyork.us/resource/h9gi-nx95.geojson',
buffer: 0,
maxzoom: 16
});
if (window.location.search.indexOf('embed') !== -1) map.scrollZoom.disable();
map.addLayer({
'id': 'veh-incd-1',
'type': 'circle',
'source': 'veh-incidents-1',
'paint': {
'circle-color': {
property: 'CYC_INJ',
type: 'interval',
stops: [
[1, 'orange'],
[2, 'red']
]
},
'circle-radius': {
property: 'CYC_INJ',
base: 3,
type: 'interval',
stops: [
[1, 3],
[2, 8],
[3, 12]
]
},
'circle-opacity': 0.8,
'circle-blur': 0.5
},
'filter': ['>=', 'CYC_INJ', 1]
}, 'waterway-label');
map.addLayer({
'id': 'veh-incd-base-1',
'type': 'circle',
'source': 'veh-incidents-1',
'paint': {
'circle-color': 'yellow',
'circle-radius': 3,
'circle-opacity': 0.3,
'circle-blur': 1
},
'filter': ['<', 'CYC_INJ', 1]
}, 'waterway-label');
map.once('style.load', function (e) {
init();
map.addControl(new mapboxgl.NavigationControl());
map.on('click', function (e) {
var features = map.queryRenderedFeatures(e.point, {
layers: ['veh-incd-1', 'veh-incd-2']
});
if (!features.length) {
return;
}
var feature = features[0];
var popup = new mapboxgl.Popup()
.setLngLat(map.unproject(e.point))
.setHTML('<h3>Collision Detail</h3>' +
'<ul>' +
'<li>Year: <b>' + feature.properties.YEAR + '</b></li>' +
'<li>Pedestrian Injuries: <b>' + feature.properties.PED_INJ + '</b></li>' +
'<li>Pedestrian Fatalities: <b>' + feature.properties.PED_KIL + '</b></li>' +
'<li>Cyclist Injuries: <b>' + feature.properties.CYC_INJ + '</b></li>' +
'<li>Cyclist Fatalities: <b>' + feature.properties.CYC_KIL + '</b></li>' +
'</ul>')
.addTo(map);
});
//Hide loading bar once tiles from geojson are loaded
map.on('data', function (e) {
if (e.dataType === 'source' && e.sourceId === 'veh-incidents-1') {
document.getElementById("loader").style.visibility = "hidden";
}
})
// Use the same approach as above to indicate that the symbols are clickable
// by changing the cursor style to 'pointer'.
map.on('mousemove', function (e) {
var features = map.queryRenderedFeatures(e.point, {
layers: ['veh-incd-1', 'veh-incd-2']
});
map.getCanvas().style.cursor = (features.length) ? 'pointer' : '';
});
var prop = document.getElementById('prop');
prop.addEventListener('change', function () {
map.setPaintProperty('veh-incd-1', 'circle-color', {
property: prop.value,
type: 'interval',
stops: [
[1, 'orange'],
[2, 'red']
]
});
map.setPaintProperty('veh-incd-1', 'circle-radius', {
property: prop.value,
base: 3,
type: 'interval',
stops: [
[1, 3],
[2, 6],
[3, 9]
]
});
map.setFilter('veh-incd-1', ['>=', prop.value, 1]);
});
});
}
</script>
</body>
</html>
@owenabrams
Copy link

Hello Ryan, been trying to get this code to work but am stuck. Honestly, am new to Mapbox and your project here "Mapbox & The IBM Data Science Experience" is what really inspired me and compelled me to learn mapping code. Can you please help me figure this out.
Thanks -
Abe ([email protected])

@iisstizzy
Copy link

Hello Ryan, been trying to get this code to work but am stuck. Honestly, am new to Mapbox and your project here "Mapbox & The IBM Data Science Experience" is what really inspired me and compelled me to learn mapping code. Can you please help me figure this out.
Thanks -
Abe ([email protected])

Thanks for the example @ryanbaumann!

@owenabrams, I had the same problem since this relied on a custom minified version of the nyc.gov data, but I couldn't ever download the linked dropbox file either. I uploaded two minified versions to a new repo:
https://github.com/iisstizzy/Example-Large-geojson-in-Mapbox-GL-JS

(@ryanbaumann - Sorry, I forked this gist, but my ocd wants the relevant geojson data in a repo with the demo itself. Let me know if you'd rather me take it down, . Thanks again for the example! :)

@ryanbaumann
Copy link
Author

Not at all! Happy to update this example as well since people find it useful. Thank you both.

@gauravkhurana17
Copy link

Hi @ryanbaumann,

Question regarding Geojson source is split into two parts to optimize load and render time

Thank you for the example. We have some similar requirement to load large geojson to mapboxgl. Having said that, We are considering splitting our geo json into smaller chunks.
That is the reason i stumbled across your example. However, i am trying to understand where exactly we have splitted geo json in your example and how are we fetching the other pieces of it and appending to map source?

I see we addSource just once. 'https://data.cityofnewyork.us/resource/h9gi-nx95.geojson'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment