Last active
February 10, 2020 18:06
-
-
Save fxi/9bfb0b44a88ca075258d53ad8be96154 to your computer and use it in GitHub Desktop.
Update highcharts with data from mapbox gl
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></title> | |
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' /> | |
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.38.0/mapbox-gl.js'></script> | |
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.38.0/mapbox-gl.css' rel='stylesheet' /> | |
<link href='style.css' rel='stylesheet' /> | |
<script src="https://code.highcharts.com/highcharts.src.js"></script> | |
</head> | |
<body> | |
<div id='map'></div> | |
<div id='stat'> | |
<div id='container'></div> | |
</div> | |
<script src="script.js" type="text/javascript"></script> | |
</body> | |
</html> |
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
var chart; | |
mapboxgl.accessToken = 'pk.eyJ1IjoidW5lcGdyaWQiLCJhIjoiY2lrd293Z3RhMDAzNHd4bTR4YjE4MHM0byJ9.9c-Yt3p0aKFSO2tX6CR26Q'; | |
var map = new mapboxgl.Map({ | |
container: 'map', // container id | |
style: 'mapbox://styles/mapbox/dark-v9', //hosted style id | |
center: [-77.38, 39], // starting position | |
zoom: 3 // starting zoom | |
}); | |
map.on('load', function() { | |
map.addLayer({ | |
"id": "points", | |
"type": "circle", | |
"source": { | |
"type": "geojson", | |
"data": rPoints(1000) | |
}, | |
'paint': { | |
// make circles larger as the user zooms from z12 to z22 | |
'circle-radius': { | |
'base': 10, | |
'stops': [ | |
[12, 3], | |
[22, 20] | |
] | |
}, | |
'circle-opacity': 0.8, | |
// color circles by ethnicity, using data-driven styles | |
'circle-color': { | |
property: 'nHgKg', | |
type: 'exponential', | |
stops: [ | |
[0, '#03c5f7'], | |
[1000, '#b503f7'] | |
] | |
} | |
} | |
}); | |
chartInit() | |
map.on("render", chartSetData); | |
}); | |
function getData() { | |
var vals = []; | |
var layer = "points" | |
if (!map.getLayer(layer)) return vals; | |
var test = map.queryRenderedFeatures({ | |
layers: [layer] | |
}); | |
test.forEach(function(f) { | |
vals.push([f.properties.date, f.properties.nHgKg]); | |
}) | |
vals = vals.sort(function(a, b) { | |
return a[0] - b[0]; | |
}) | |
return (vals); | |
} | |
function chartSetData() { | |
data = getData(); | |
if (!data || data.length == 0) return; | |
chart.series[0].update({ | |
data: data | |
}) | |
} | |
function chartInit(data) { | |
data = data || getData() || []; | |
Highcharts.createElement('link', { | |
href: 'https://fonts.googleapis.com/css?family=Unica+One', | |
rel: 'stylesheet', | |
type: 'text/css' | |
}, null, document.getElementsByTagName('head')[0]); | |
Highcharts.theme = { | |
colors: ['#2b908f', '#90ee7e', '#f45b5b', '#7798BF', '#aaeeee', '#ff0066', '#eeaaee', | |
'#55BF3B', '#DF5353', '#7798BF', '#aaeeee' | |
], | |
chart: { | |
backgroundColor: { | |
linearGradient: { | |
x1: 0, | |
y1: 0, | |
x2: 1, | |
y2: 1 | |
}, | |
stops: [ | |
[0, '#2a2a2b'], | |
[1, '#3e3e40'] | |
] | |
}, | |
style: { | |
fontFamily: '\'Unica One\', sans-serif' | |
}, | |
plotBorderColor: '#606063' | |
}, | |
title: { | |
style: { | |
color: '#E0E0E3', | |
textTransform: 'uppercase', | |
fontSize: '20px' | |
} | |
}, | |
subtitle: { | |
style: { | |
color: '#E0E0E3', | |
textTransform: 'uppercase' | |
} | |
}, | |
xAxis: { | |
gridLineColor: '#707073', | |
labels: { | |
style: { | |
color: '#E0E0E3' | |
} | |
}, | |
lineColor: '#707073', | |
minorGridLineColor: '#505053', | |
tickColor: '#707073', | |
title: { | |
style: { | |
color: '#A0A0A3' | |
} | |
} | |
}, | |
yAxis: { | |
gridLineColor: '#707073', | |
labels: { | |
style: { | |
color: '#E0E0E3' | |
} | |
}, | |
lineColor: '#707073', | |
minorGridLineColor: '#505053', | |
tickColor: '#707073', | |
tickWidth: 1, | |
title: { | |
style: { | |
color: '#A0A0A3' | |
} | |
} | |
}, | |
tooltip: { | |
backgroundColor: 'rgba(0, 0, 0, 0.85)', | |
style: { | |
color: '#F0F0F0' | |
} | |
}, | |
plotOptions: { | |
series: { | |
dataLabels: { | |
color: '#B0B0B3' | |
}, | |
marker: { | |
lineColor: '#333' | |
} | |
}, | |
boxplot: { | |
fillColor: '#505053' | |
}, | |
candlestick: { | |
lineColor: 'white' | |
}, | |
errorbar: { | |
color: 'white' | |
} | |
}, | |
legend: { | |
itemStyle: { | |
color: '#E0E0E3' | |
}, | |
itemHoverStyle: { | |
color: '#FFF' | |
}, | |
itemHiddenStyle: { | |
color: '#606063' | |
} | |
}, | |
credits: { | |
style: { | |
color: '#666' | |
} | |
}, | |
labels: { | |
style: { | |
color: '#707073' | |
} | |
}, | |
drilldown: { | |
activeAxisLabelStyle: { | |
color: '#F0F0F3' | |
}, | |
activeDataLabelStyle: { | |
color: '#F0F0F3' | |
} | |
}, | |
navigation: { | |
buttonOptions: { | |
symbolStroke: '#DDDDDD', | |
theme: { | |
fill: '#505053' | |
} | |
} | |
}, | |
// scroll charts | |
rangeSelector: { | |
buttonTheme: { | |
fill: '#505053', | |
stroke: '#000000', | |
style: { | |
color: '#CCC' | |
}, | |
states: { | |
hover: { | |
fill: '#707073', | |
stroke: '#000000', | |
style: { | |
color: 'white' | |
} | |
}, | |
select: { | |
fill: '#000003', | |
stroke: '#000000', | |
style: { | |
color: 'white' | |
} | |
} | |
} | |
}, | |
inputBoxBorderColor: '#505053', | |
inputStyle: { | |
backgroundColor: '#333', | |
color: 'silver' | |
}, | |
labelStyle: { | |
color: 'silver' | |
} | |
}, | |
navigator: { | |
handles: { | |
backgroundColor: '#666', | |
borderColor: '#AAA' | |
}, | |
outlineColor: '#CCC', | |
maskFill: 'rgba(255,255,255,0.1)', | |
series: { | |
color: '#7798BF', | |
lineColor: '#A6C7ED' | |
}, | |
xAxis: { | |
gridLineColor: '#505053' | |
} | |
}, | |
scrollbar: { | |
barBackgroundColor: '#808083', | |
barBorderColor: '#808083', | |
buttonArrowColor: '#CCC', | |
buttonBackgroundColor: '#606063', | |
buttonBorderColor: '#606063', | |
rifleColor: '#FFF', | |
trackBackgroundColor: '#404043', | |
trackBorderColor: '#404043' | |
}, | |
// special colors for some of the | |
legendBackgroundColor: 'rgba(0, 0, 0, 0.5)', | |
background2: '#505053', | |
dataLabelsColor: '#B0B0B3', | |
textColor: '#C0C0C0', | |
contrastTextColor: '#F0F0F3', | |
maskColor: 'rgba(255,255,255,0.3)' | |
}; | |
// Apply the theme | |
Highcharts.setOptions(Highcharts.theme); | |
chart = Highcharts.chart('container', { | |
chart: { | |
zoomType: 'x', | |
events: { | |
selection: function(event) { | |
var layer = "points"; | |
if(event.resetSelection){ | |
map.setFilter(layer,[ | |
'all' | |
]) | |
}else{ | |
var ext = event.xAxis[0]; | |
map.setFilter(layer,[ | |
'all', | |
['<','date',ext.max], | |
['>','date',ext.min] | |
]) | |
} | |
} | |
} | |
}, | |
title: { | |
text: 'Mercury consumption Hg (kg)' | |
}, | |
subtitle: { | |
text: 'Click and drag to filter date' | |
}, | |
xAxis: { | |
type: 'datetime' | |
}, | |
yAxis: { | |
title: { | |
text: 'nHgKg' | |
} | |
}, | |
legend: { | |
enabled: false | |
}, | |
plotOptions: { | |
area: { | |
fillColor: { | |
linearGradient: { | |
x1: 0, | |
y1: 0, | |
x2: 0, | |
y2: 1 | |
}, | |
stops: [ | |
[0, Highcharts.getOptions().colors[0]], | |
[1, Highcharts.Color(Highcharts.getOptions().colors[0]).setOpacity(0).get('rgba')] | |
] | |
}, | |
marker: { | |
radius: 2 | |
}, | |
lineWidth: 1, | |
states: { | |
hover: { | |
lineWidth: 1 | |
} | |
}, | |
threshold: null | |
} | |
}, | |
series: [{ | |
type: 'area', | |
name: 'Mercury KG', | |
data: data | |
}] | |
}); | |
} | |
function rCoord() { | |
var coord = [ | |
(Math.random() * 360) - 180, (Math.random() * 180) - 90 | |
] | |
return coord | |
} | |
function rDate(base) { | |
base = base || 0; | |
mx = new Date(); | |
mn = new Date("2004/12/02"); | |
dd = Math.round(mx - (Math.random() * (mx - mn - base))); | |
return (dd) | |
} | |
function rProp(base) { | |
base = base || [0]; | |
var a = (base[0] + 180) / 360 || Math.random(); | |
var prop = { | |
date: rDate(a * 1000), | |
nHgKg: Math.round(a * 1000) | |
} | |
return prop; | |
} | |
function rPoint() { | |
var coord = rCoord() | |
var point = { | |
type: "Feature", | |
geometry: { | |
type: "Point", | |
coordinates: coord | |
}, | |
properties: rProp(coord) | |
} | |
return point; | |
} | |
function rPoints(n) { | |
var gJson = { | |
type: "FeatureCollection", | |
features: [] | |
}; | |
n = n || 100; | |
for (var i = 0; i < n; i++) { | |
gJson.features.push(rPoint()); | |
} | |
return gJson; | |
} | |
function cloneArray(arr) { | |
var i = arr.length; | |
var clone = []; | |
while (i--) { | |
clone[i] = arr[i]; | |
} | |
return (clone); | |
}; | |
function getArrayStat(o) { | |
if ( | |
o.arr === undefined || | |
o.arr.constructor != Array || | |
o.arr.length === 0 | |
) return []; | |
if ( | |
o.stat == "quantile" && | |
o.percentile && | |
o.percentile.constructor == Array | |
) o.stat = "quantiles"; | |
var arr = cloneArray(o.arr); | |
var stat = o.stat ? o.stat : "max"; | |
var len_o = arr.length; | |
var len = len_o; | |
function sortNumber(a, b) { | |
return a - b; | |
} | |
opt = { | |
"max": function() { | |
var max = -Infinity; | |
var v = 0; | |
while (len--) { | |
v = arr.pop(); | |
if (v > max) { | |
max = v; | |
} | |
} | |
return max; | |
}, | |
"min": function() { | |
var min = Infinity; | |
while (len--) { | |
v = arr.pop(); | |
if (v < min) { | |
min = v; | |
} | |
} | |
return min; | |
}, | |
"sum": function() { | |
var sum = 0; | |
while (len--) { | |
sum += arr.pop(); | |
} | |
return sum; | |
}, | |
"mean": function() { | |
var sum = getArrayStat({ | |
stat: "sum", | |
arr: arr | |
}); | |
return sum / len_o; | |
}, | |
"median": function() { | |
var median = getArrayStat({ | |
stat: "quantile", | |
arr: arr, | |
percentile: 50 | |
}); | |
return median; | |
}, | |
"quantile": function() { | |
arr.sort(sortNumber); | |
o.percentile = o.percentile ? o.percentile : 50; | |
index = o.percentile / 100 * (arr.length - 1); | |
if (Math.floor(index) == index) { | |
result = arr[index]; | |
} else { | |
i = Math.floor(index); | |
fraction = index - i; | |
result = arr[i] + (arr[i + 1] - arr[i]) * fraction; | |
} | |
return result; | |
}, | |
"quantiles": function() { | |
var quantiles = {}; | |
o.percentile.forEach(function(x) { | |
var res = getArrayStat({ | |
stat: "quantile", | |
arr: arr, | |
percentile: x | |
}); | |
quantiles[x] = res; | |
}); | |
return quantiles; | |
}, | |
"distinct": function() { | |
var n = {}, | |
r = []; | |
while (len--) { | |
if (!n[arr[len]]) { | |
n[arr[len]] = true; | |
r.push(arr[len]); | |
} | |
} | |
return r; | |
} | |
}; | |
return (opt[stat](o)); | |
}; | |
function printLayerStat() { | |
var s = getLayerStat(); | |
var elN = document.getElementById("sN"); | |
var elMin = document.getElementById("sMin"); | |
var elMax = document.getElementById("sMax"); | |
var elPtil = document.getElementById("sPtil"); | |
elN.innerHTML = s.n; | |
elMin.innerHTML = s.min; | |
elMax.innerHTML = s.max; | |
elPtil.innerHTML = JSON.stringify(s.ptil); | |
} | |
function getLayerStat() { | |
var test = map.queryRenderedFeatures({ | |
layers: ['points'] | |
}); | |
var vals = []; | |
test.forEach(function(f) { | |
vals.push([ | |
f.properties.date, | |
f.properties.nHgKg | |
]); | |
}) | |
var out = { | |
n: test.length, | |
max: getArrayStat({ | |
arr: vals, | |
stat: 'max' | |
}), | |
min: getArrayStat({ | |
arr: vals, | |
stat: 'min' | |
}), | |
ptil: getArrayStat({ | |
arr: vals, | |
stat: 'quantiles', | |
percentile: [25, 50, 75] | |
}) | |
} | |
return (out) | |
} |
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
body { | |
margin: 0; | |
padding: 0; | |
} | |
#map { | |
position: absolute; | |
top: 0; | |
bottom: 0; | |
width: 100%; | |
} | |
#stat { | |
position: absolute; | |
top: 50%; | |
bottom: 0; | |
width: 100%; | |
height:50%; | |
background:#FFF; | |
z-index:1000; | |
} | |
#container { | |
height:100%; | |
width:100%; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment