Skip to content

Instantly share code, notes, and snippets.

@ivanmalagon
Created January 12, 2018 16:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ivanmalagon/57f28eaced3aabbcb2b11c075a2046e5 to your computer and use it in GitHub Desktop.
Save ivanmalagon/57f28eaced3aabbcb2b11c075a2046e5 to your computer and use it in GitHub Desktop.
MapboxGL with CARTO MVT
<!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>
/* 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