Created
January 12, 2018 16:40
-
-
Save ivanmalagon/57f28eaced3aabbcb2b11c075a2046e5 to your computer and use it in GitHub Desktop.
MapboxGL with CARTO MVT
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>Carto - Windshaft Aggregation API - Mapbox GL example</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.43.0/mapbox-gl.js'></script> | |
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.43.0/mapbox-gl.css' rel='stylesheet' /> | |
<link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet"> | |
<style> | |
body { | |
margin: 0; | |
padding: 0; | |
} | |
#map { | |
position: absolute; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
} | |
.slider-container { | |
position: absolute; | |
bottom: 32px; | |
right: 0; | |
width: 273px; | |
z-index: 10; | |
color: white; | |
} | |
</style> | |
<script src="./slider.js"></script> | |
</head> | |
<body> | |
<div class="slider-container"> | |
<div id="slider" class="slider"></div> | |
</div> | |
<div id='map'></div> | |
<script> | |
function debounce (func, delay) { | |
var inDebounce; | |
return function() { | |
const context = this; | |
const args = arguments; | |
clearTimeout(inDebounce); | |
inDebounce = setTimeout(() => func.apply(context, args), delay); | |
}; | |
} | |
var init = false; | |
var map = null; | |
var currentValue = 1; | |
function onValueChanged(value) { | |
if (init) { | |
reloadCartoLayer(value); | |
} | |
} | |
var onValueChangedDebounced = debounce(onValueChanged, 300); | |
async function reloadCartoLayer(value) { | |
const mapConfig = { | |
buffersize: { mvt: 0 }, | |
layers: [ | |
{ | |
id: 'aggregated', | |
type: 'mapnik', | |
options: { | |
sql: 'select * from gecat_geodata_copy', | |
aggregation: { | |
threshold: 1, // Force aggregation | |
resolution: value, | |
placement: 'centroid', | |
columns: { | |
speed_avg: { | |
aggregate_function: 'avg', | |
aggregated_column: 'speed' | |
} | |
} | |
} | |
} | |
} | |
] | |
} | |
const response = await fetch('https://cdbsol-admin.carto.com/api/v1/map', { | |
method: 'POST', | |
headers: { | |
'Accept': 'application/json', | |
'Content-Type': 'application/json' | |
}, | |
body: JSON.stringify(mapConfig) | |
}); | |
const layergroup = await response.json(); | |
var tilejson = layergroup.metadata.tilejson.vector; | |
if (init) { | |
map.removeLayer("aggregated-points"); | |
map.removeSource("carto"); | |
} | |
map.addSource("carto", { | |
type: "vector", | |
tiles: tilejson.tiles | |
}); | |
map.addLayer({ | |
"id": "aggregated-points", | |
"type": "circle", | |
"source": "carto", | |
"source-layer": "aggregated", | |
"paint": { | |
"circle-radius": 3, | |
"circle-color": ["interpolate", | |
["linear"], | |
["get", "speed_avg"], | |
0, "#f6d2a9", | |
25, "#b13f64" | |
], | |
"circle-opacity": ["case", | |
[">", ["/", ["get", "_cdb_feature_count"], 50], 1], 1, | |
["/", ["get", "_cdb_feature_count"], 50] | |
] | |
} | |
}); | |
init = true; | |
} | |
function removeLabels() { | |
map.style.stylesheet.layers.forEach(function (layer) { | |
if (layer.type === 'symbol') { | |
map.removeLayer(layer.id); | |
} | |
}); | |
} | |
mapboxgl.accessToken = 'pk.eyJ1Ijoicm9jaG9hIiwiYSI6IlUtOVdzLW8ifQ.aF6UbX1O-otpss1ZIwSQWw'; | |
map = new mapboxgl.Map({ | |
container: 'map', | |
style: 'mapbox://styles/mapbox/dark-v9', | |
center: [2.17, 41.38], | |
zoom: 11, | |
minZoom: 3, | |
maxZoom: 12 | |
}); | |
map.on('load', function () { | |
removeLabels(); | |
reloadCartoLayer(currentValue); | |
}); | |
var sliderEl = document.getElementById('slider'); | |
var slider = new Slider({ | |
target: sliderEl | |
}); | |
slider.observe('value', function (value) { | |
if (value !== currentValue) { | |
currentValue = value; | |
onValueChangedDebounced(currentValue); | |
} | |
}); | |
</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
/* Slider.html generated by Svelte v1.51.1 */ | |
var Slider = (function() { "use strict"; | |
function data() { | |
return { | |
dragging: false, | |
barX: 0, | |
barY: 0, | |
barWidth: 0, | |
percentage: 8, | |
value: 1 | |
} | |
}; | |
var methods = { | |
onHandleMouseDown (event) { | |
event.preventDefault(); | |
this.set({ dragging: true }); | |
}, | |
onHandleMouseUp (event) { | |
if (this.get('dragging')) { | |
this.set({ dragging: false }); | |
} | |
}, | |
onHandleMouseMove (event) { | |
if (this.get('dragging')) { | |
event.preventDefault(); | |
var clientX = event.touches && event.touches.length ? event.touches[0].clientX : event.clientX; | |
var barX = this.get('barX'); | |
var difference = (clientX || 0) - barX; | |
difference = Math.max(0, difference); | |
difference = Math.min(this.get('barWidth'), difference); | |
var percentage = difference / this.get('barWidth'); | |
this.set({percentage: Math.round(percentage * 100)}); | |
var value = percentage * 16; | |
if (value <= 0.5) { | |
value = 0.5 | |
} else { | |
value = Math.round(value); | |
} | |
this.set({value: value}); | |
} | |
} | |
}; | |
function oncreate() { | |
var bar = this.refs.bar; | |
var barRects = bar.getClientRects()[0]; | |
this.set({ | |
barX: barRects.x, | |
barY: barRects.y, | |
barWidth: barRects.width | |
}); | |
}; | |
function encapsulateStyles(node) { | |
setAttribute(node, "svelte-1069312446", ""); | |
} | |
function add_css() { | |
var style = createElement("style"); | |
style.id = 'svelte-1069312446-style'; | |
style.textContent = "[svelte-1069312446].slider,[svelte-1069312446] .slider{font-family:'Open Sans', sans-serif;color:black;background-color:white;padding:28px 32px}h2[svelte-1069312446],[svelte-1069312446] h2{font-size:16px;margin:0 0 8px 0;padding:0;line-height:22px}p[svelte-1069312446],[svelte-1069312446] p{font-size:10px;color:#979EA1;text-transform:uppercase;margin:0;padding:0;border:0}span[svelte-1069312446],[svelte-1069312446] span{display:inline}[svelte-1069312446].ui-slider,[svelte-1069312446] .ui-slider{margin-top:16px;display:flex;align-items:center}[svelte-1069312446].ui-slider-range,[svelte-1069312446] .ui-slider-range{position:relative;width:100%;height:2px;border-radius:0;background:#DDD;margin-right:16px}[svelte-1069312446].ui-slider-range::before,[svelte-1069312446] .ui-slider-range::before{content:'';position:absolute;z-index:1;top:0;left:0;width:100%;height:1px;border:none;border-radius:0;cursor:pointer}[svelte-1069312446].ui-slider-bar,[svelte-1069312446] .ui-slider-bar{height:2px;border-radius:0;background:#1785FB}[svelte-1069312446].ui-slider-handle,[svelte-1069312446] .ui-slider-handle{border:1px solid #1785FB;cursor:pointer;position:absolute;z-index:3;top:0;left:1px;width:16px;height:16px;margin-top:-8px;margin-left:-8px;transition:transform 200ms;border-radius:50%;outline:none;background:#FFF}[svelte-1069312446].ui-slider-value,[svelte-1069312446] .ui-slider-value{color:#979EA1;border:1px solid #DDD;padding:2px 12px;border-radius:4px;text-align:center;font-size:14px}"; | |
appendNode(style, document.head); | |
} | |
function create_main_fragment(state, component) { | |
var div, h2, text_1, p, text_3, div_1, div_2, div_3, text_4, span, text_6, div_4, text_7; | |
function onwindowmouseup(event) { | |
component.onHandleMouseUp(event); | |
} | |
window.addEventListener("mouseup", onwindowmouseup); | |
function onwindowtouchend(event) { | |
component.onHandleMouseUp(event); | |
} | |
window.addEventListener("touchend", onwindowtouchend); | |
function onwindowmousemove(event) { | |
component.onHandleMouseMove(event); | |
} | |
window.addEventListener("mousemove", onwindowmousemove); | |
function onwindowtouchmove(event) { | |
component.onHandleMouseMove(event); | |
} | |
window.addEventListener("touchmove", onwindowtouchmove); | |
function mousedown_handler(event) { | |
component.onHandleMouseDown(event); | |
} | |
function touchstart_handler(event) { | |
component.onHandleMouseDown(event); | |
} | |
function mouseup_handler(event) { | |
component.onHandleMouseUp(event); | |
} | |
function touchend_handler(event) { | |
component.onHandleMouseUp(event); | |
} | |
return { | |
c: function create() { | |
div = createElement("div"); | |
h2 = createElement("h2"); | |
h2.textContent = "Maps API aggregation"; | |
text_1 = createText("\n "); | |
p = createElement("p"); | |
p.textContent = "Change resolution to modify clustering"; | |
text_3 = createText("\n\n "); | |
div_1 = createElement("div"); | |
div_2 = createElement("div"); | |
div_3 = createElement("div"); | |
text_4 = createText("\n "); | |
span = createElement("span"); | |
text_6 = createText("\n\n "); | |
div_4 = createElement("div"); | |
text_7 = createText(state.value); | |
this.h(); | |
}, | |
h: function hydrate() { | |
encapsulateStyles(div); | |
div_3.className = "ui-slider-bar"; | |
setStyle(div_3, "width", "" + state.percentage + "%"); | |
span.className = "ui-slider-handle"; | |
setStyle(span, "left", "" + state.percentage + "%"); | |
addListener(span, "mousedown", mousedown_handler); | |
addListener(span, "touchstart", touchstart_handler); | |
addListener(span, "mouseup", mouseup_handler); | |
addListener(span, "touchend", touchend_handler); | |
div_2.className = "ui-slider-range js-slider"; | |
div_4.className = "ui-slider-value"; | |
div_1.className = "ui-slider"; | |
div.className = "slider"; | |
}, | |
m: function mount(target, anchor) { | |
insertNode(div, target, anchor); | |
appendNode(h2, div); | |
appendNode(text_1, div); | |
appendNode(p, div); | |
appendNode(text_3, div); | |
appendNode(div_1, div); | |
appendNode(div_2, div_1); | |
appendNode(div_3, div_2); | |
appendNode(text_4, div_2); | |
appendNode(span, div_2); | |
component.refs.bar = div_2; | |
appendNode(text_6, div_1); | |
appendNode(div_4, div_1); | |
appendNode(text_7, div_4); | |
}, | |
p: function update(changed, state) { | |
if (changed.percentage) { | |
setStyle(div_3, "width", "" + state.percentage + "%"); | |
setStyle(span, "left", "" + state.percentage + "%"); | |
} | |
if (changed.value) { | |
text_7.data = state.value; | |
} | |
}, | |
u: function unmount() { | |
detachNode(div); | |
}, | |
d: function destroy() { | |
window.removeEventListener("mouseup", onwindowmouseup); | |
window.removeEventListener("touchend", onwindowtouchend); | |
window.removeEventListener("mousemove", onwindowmousemove); | |
window.removeEventListener("touchmove", onwindowtouchmove); | |
removeListener(span, "mousedown", mousedown_handler); | |
removeListener(span, "touchstart", touchstart_handler); | |
removeListener(span, "mouseup", mouseup_handler); | |
removeListener(span, "touchend", touchend_handler); | |
if (component.refs.bar === div_2) component.refs.bar = null; | |
} | |
}; | |
} | |
function Slider(options) { | |
init(this, options); | |
this.refs = {}; | |
this._state = assign(data(), options.data); | |
if (!document.getElementById("svelte-1069312446-style")) add_css(); | |
var _oncreate = oncreate.bind(this); | |
if (!options.root) { | |
this._oncreate = [_oncreate]; | |
} else { | |
this.root._oncreate.push(_oncreate); | |
} | |
this._fragment = create_main_fragment(this._state, this); | |
if (options.target) { | |
this._fragment.c(); | |
this._fragment.m(options.target, options.anchor || null); | |
callAll(this._oncreate); | |
} | |
} | |
assign(Slider.prototype, methods, { | |
destroy: destroy, | |
get: get, | |
fire: fire, | |
observe: observe, | |
on: on, | |
set: set, | |
teardown: destroy, | |
_set: _set, | |
_mount: _mount, | |
_unmount: _unmount | |
}); | |
Slider.prototype._recompute = noop; | |
function setAttribute(node, attribute, value) { | |
node.setAttribute(attribute, value); | |
} | |
function createElement(name) { | |
return document.createElement(name); | |
} | |
function appendNode(node, target) { | |
target.appendChild(node); | |
} | |
function createText(data) { | |
return document.createTextNode(data); | |
} | |
function setStyle(node, key, value) { | |
node.style.setProperty(key, value); | |
} | |
function addListener(node, event, handler) { | |
node.addEventListener(event, handler, false); | |
} | |
function insertNode(node, target, anchor) { | |
target.insertBefore(node, anchor); | |
} | |
function detachNode(node) { | |
node.parentNode.removeChild(node); | |
} | |
function removeListener(node, event, handler) { | |
node.removeEventListener(event, handler, false); | |
} | |
function init(component, options) { | |
component._observers = { pre: blankObject(), post: blankObject() }; | |
component._handlers = blankObject(); | |
component._bind = options._bind; | |
component.options = options; | |
component.root = options.root || component; | |
component.store = component.root.store || options.store; | |
} | |
function assign(target) { | |
var k, | |
source, | |
i = 1, | |
len = arguments.length; | |
for (; i < len; i++) { | |
source = arguments[i]; | |
for (k in source) target[k] = source[k]; | |
} | |
return target; | |
} | |
function callAll(fns) { | |
while (fns && fns.length) fns.pop()(); | |
} | |
function destroy(detach) { | |
this.destroy = noop; | |
this.fire('destroy'); | |
this.set = this.get = noop; | |
if (detach !== false) this._fragment.u(); | |
this._fragment.d(); | |
this._fragment = this._state = null; | |
} | |
function get(key) { | |
return key ? this._state[key] : this._state; | |
} | |
function fire(eventName, data) { | |
var handlers = | |
eventName in this._handlers && this._handlers[eventName].slice(); | |
if (!handlers) return; | |
for (var i = 0; i < handlers.length; i += 1) { | |
handlers[i].call(this, data); | |
} | |
} | |
function observe(key, callback, options) { | |
var group = options && options.defer | |
? this._observers.post | |
: this._observers.pre; | |
(group[key] || (group[key] = [])).push(callback); | |
if (!options || options.init !== false) { | |
callback.__calling = true; | |
callback.call(this, this._state[key]); | |
callback.__calling = false; | |
} | |
return { | |
cancel: function() { | |
var index = group[key].indexOf(callback); | |
if (~index) group[key].splice(index, 1); | |
} | |
}; | |
} | |
function on(eventName, handler) { | |
if (eventName === 'teardown') return this.on('destroy', handler); | |
var handlers = this._handlers[eventName] || (this._handlers[eventName] = []); | |
handlers.push(handler); | |
return { | |
cancel: function() { | |
var index = handlers.indexOf(handler); | |
if (~index) handlers.splice(index, 1); | |
} | |
}; | |
} | |
function set(newState) { | |
this._set(assign({}, newState)); | |
if (this.root._lock) return; | |
this.root._lock = true; | |
callAll(this.root._beforecreate); | |
callAll(this.root._oncreate); | |
callAll(this.root._aftercreate); | |
this.root._lock = false; | |
} | |
function _set(newState) { | |
var oldState = this._state, | |
changed = {}, | |
dirty = false; | |
for (var key in newState) { | |
if (differs(newState[key], oldState[key])) changed[key] = dirty = true; | |
} | |
if (!dirty) return; | |
this._state = assign({}, oldState, newState); | |
this._recompute(changed, this._state); | |
if (this._bind) this._bind(changed, this._state); | |
if (this._fragment) { | |
dispatchObservers(this, this._observers.pre, changed, this._state, oldState); | |
this._fragment.p(changed, this._state); | |
dispatchObservers(this, this._observers.post, changed, this._state, oldState); | |
} | |
} | |
function _mount(target, anchor) { | |
this._fragment.m(target, anchor); | |
} | |
function _unmount() { | |
if (this._fragment) this._fragment.u(); | |
} | |
function noop() {} | |
function blankObject() { | |
return Object.create(null); | |
} | |
function differs(a, b) { | |
return a !== b || ((a && typeof a === 'object') || typeof a === 'function'); | |
} | |
function dispatchObservers(component, group, changed, newState, oldState) { | |
for (var key in group) { | |
if (!changed[key]) continue; | |
var newValue = newState[key]; | |
var oldValue = oldState[key]; | |
var callbacks = group[key]; | |
if (!callbacks) continue; | |
for (var i = 0; i < callbacks.length; i += 1) { | |
var callback = callbacks[i]; | |
if (callback.__calling) continue; | |
callback.__calling = true; | |
callback.call(component, newValue, oldValue); | |
callback.__calling = false; | |
} | |
} | |
} | |
return Slider; | |
}()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment