-
-
Save ryanbaumann/733ba99c5ca1d9d15259081b395e4b00 to your computer and use it in GitHub Desktop.
GL JS Feature States with Data Joins
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset='utf-8' /> | |
<title>Display a map</title> | |
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' /> | |
<script src='https://dl.dropbox.com/s/hzalyoaqrmuditi/mapbox-gl.js'></script> | |
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.45.0/mapbox-gl.css' rel='stylesheet' /> | |
<script src='https://cdnjs.cloudflare.com/ajax/libs/chroma-js/1.3.7/chroma.min.js'></script> | |
<style> | |
body { | |
margin: 0; | |
padding: 0; | |
} | |
#map { | |
position: absolute; | |
top: 0; | |
bottom: 0; | |
width: 100%; | |
} | |
</style> | |
</head> | |
<body> | |
<div id='map'> | |
</div> | |
<script> | |
mapboxgl.accessToken = 'pk.eyJ1IjoicnNiYXVtYW5uIiwiYSI6IjdiOWEzZGIyMGNkOGY3NWQ4ZTBhN2Y5ZGU2Mzg2NDY2In0.jycgv7qwF8MMIWt4cT0RaQ'; | |
var map = new mapboxgl.Map({ | |
container: 'map', | |
style: 'mapbox://styles/mapbox/light-v9?optimize=true', | |
center: [-99.9, 41.5], | |
zoom: 4 | |
}); | |
var maxValue = 0; | |
var newdata = new Map(); | |
var breaks = []; | |
var scale = []; | |
var getJSON = function(url, callback) { | |
var xhr = new XMLHttpRequest(); | |
xhr.open('GET', url, true); | |
xhr.responseType = 'json'; | |
xhr.onload = function() { | |
var status = xhr.status; | |
if (status === 200) { | |
callback(null, xhr.response); | |
} else { | |
callback(status, xhr.response); | |
} | |
}; | |
xhr.send(); | |
}; | |
map.on('style.load', function() { | |
var domain = []; | |
getJSON('https://dl.dropbox.com/s/0vtzw9aw089obxi/population.json', | |
function(err, data) { | |
if (err !== null) { | |
console.log('error fetching file') | |
} else { | |
data.forEach(function(row) { | |
if (!row["Zip Code ZCTA"]) { | |
return | |
} | |
// Normalized population density | |
let area = row['area'] > 0 ? row['area'] : 1; | |
maxValue = row["2010 Census Population"] <= maxValue ? maxValue : row["2010 Census Population"]; | |
domain.push(row["2010 Census Population"]) | |
newdata[row["Zip Code ZCTA"]] = { | |
population: row["2010 Census Population"], | |
area: row['area'], | |
ratio: row["2010 Census Population"] / (area / 5280) | |
} | |
}) | |
breaks = chroma.limits(domain, 'q', 6); | |
scale = chroma.scale('OrRd').colors(7); | |
} | |
}); | |
initLayers(); | |
}); | |
function initLayers() { | |
// Add source for state polygons hosted on Mapbox, based on US Census Data: | |
// https://www.census.gov/geo/maps-data/data/cbf/cbf_state.html | |
map.addSource("postcodes", { | |
type: "vector", | |
url: "mapbox://mapbox.pbi-us-postcodes-v1", | |
}); | |
map.addLayer({ | |
"id": "postcodes", | |
"type": "fill", | |
"source": "postcodes", | |
"source-layer": "pbi-us-postcodes", | |
"paint": { | |
"fill-color": 'rgba(0,0,0,0)', | |
"fill-opacity": ["case", ['==', ["feature-state", 'hover'], 1], 1, 0.8], | |
"fill-outline-color": "rgba(255,255,255,0.1)" | |
} | |
}, 'waterway-label'); | |
map.addLayer({ | |
"id": "postcodes-line", | |
"type": "line", | |
"source": "postcodes", | |
"source-layer": "pbi-us-postcodes", | |
"layout": { | |
'line-join': 'round', | |
'line-cap': 'round' | |
}, | |
"paint": { | |
"line-color": ['case', ['==', ["feature-state", 'hover'], 1], 'black', 'rgba(0,0,0,0)'], | |
"line-width": { | |
"stops": [ | |
[0, 1], | |
[18, 4] | |
] | |
} | |
} | |
}, 'waterway-label'); | |
function createColorExpression(stops, colors, value) { | |
var expression = ['interpolate', ['linear'], ['to-number', value]] | |
for (var i = 0; i < stops.length; i++) { | |
expression.push(stops[i]) | |
expression.push(colors[i]) | |
} | |
return expression | |
} | |
function setPostcodes() { | |
map.setPaintProperty('postcodes', 'fill-color', createColorExpression(breaks, scale, ['feature-state', 'colorValue'])) | |
for (var key in newdata) { | |
map.setFeatureState({ | |
source: 'postcodes', | |
sourceLayer: 'pbi-us-postcodes', | |
id: key | |
}, { | |
'colorValue': newdata[key]['population'] != undefined ? newdata[key]['population'] : 0 | |
}) | |
} | |
} | |
let hoverId = 0; | |
function debounce(func, wait, immediate) { | |
var timeout; | |
return function() { | |
var context = this, | |
args = arguments; | |
var later = function() { | |
timeout = null; | |
if (!immediate) func.apply(context, args); | |
}; | |
var callNow = immediate && !timeout; | |
clearTimeout(timeout); | |
timeout = setTimeout(later, wait); | |
if (callNow) func.apply(context, args); | |
}; | |
}; | |
var popup = new mapboxgl.Popup({ | |
closeButton: false, | |
closeOnClick: false | |
}); | |
var onMouseMove = debounce(function(e) { | |
var features = map.queryRenderedFeatures(e.point, { | |
layers: ['postcodes'] | |
}); | |
if (!features.length) { | |
popup.remove(); | |
map.getCanvas().style.cursor = ''; | |
map.setFeatureState({ | |
source: 'postcodes', | |
sourceLayer: 'pbi-us-postcodes', | |
id: hoverId | |
}, { 'hover': 0.5 }) | |
hoverId = 0; | |
return | |
} | |
map.getCanvas().style.cursor = 'pointer'; | |
let newHoverId = features[0].id; | |
if (newHoverId != hoverId) { | |
map.setFeatureState({ | |
source: 'postcodes', | |
sourceLayer: 'pbi-us-postcodes', | |
id: hoverId | |
}, { 'hover': 0.5 }) | |
hoverId = newHoverId | |
} | |
map.setFeatureState({ | |
source: 'postcodes', | |
sourceLayer: 'pbi-us-postcodes', | |
id: hoverId | |
}, { 'hover': 1 }) | |
popup.setLngLat(map.unproject(e.point)) | |
.setHTML('<b>Population: </b>' + newdata[hoverId]['population'] + ' <br>' + | |
'<b>Area: </b>' + newdata[hoverId]['area'] + ' <br>' + | |
'<b>Population/mi^s: </b>' + Math.round(newdata[hoverId]['ratio'], 3)) | |
.addTo(map); | |
}, 14, true); | |
map.on('mousemove', onMouseMove); | |
let setAfterLoad = function(e) { | |
if (e.sourceId === 'postcodes' && e.isSourceLoaded) { | |
setPostcodes(); | |
map.off('sourcedata', setAfterLoad) | |
} | |
} | |
if (map.isSourceLoaded('postcodes')) { | |
setPostcodes(); | |
} else { | |
map.on('sourcedata', setAfterLoad); | |
} | |
}; | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment