Commit 9f584d23 authored by Rodrigo Tapia-McClung's avatar Rodrigo Tapia-McClung

First attempt to show periurban tiles

parent ed8b1084
...@@ -2,13 +2,18 @@ ...@@ -2,13 +2,18 @@
* Copyright 2021 - All rights reserved. * Copyright 2021 - All rights reserved.
* Rodrigo Tapia-McClung * Rodrigo Tapia-McClung
* *
* January 2021 * January-March 2021
*/ */
/* globals Promise */ /* globals Promise, omnivore */
/* exported layerControl */ /* exported layerControl */
let timeDimensionControl, let monthArray = ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"],
dateArray = [],
userFiles = [],
dateMin,
dateMax,
timeDimensionControl,
map, map,
overlay, overlay,
glmap, glmap,
...@@ -20,7 +25,12 @@ let timeDimensionControl, ...@@ -20,7 +25,12 @@ let timeDimensionControl,
const baseUrl = new URL(window.location.href).origin; // returns "http://localhost:8090" const baseUrl = new URL(window.location.href).origin; // returns "http://localhost:8090"
// use `${baseUrl}/something` // use `${baseUrl}/something`
const getMinMax = table => { let timeParse = d3.timeParse("%Y");
let currentTiles = {},
allTiles = {};
/*const getMinMax = table => {
return new Promise(resolve => { return new Promise(resolve => {
//let table = "urbanization_year"; //let table = "urbanization_year";
let minmaxQuery = `${baseUrl}/query/${table}?columns=${`min(yeartrimes), max(yeartrimes)`}`; let minmaxQuery = `${baseUrl}/query/${table}?columns=${`min(yeartrimes), max(yeartrimes)`}`;
...@@ -28,9 +38,37 @@ const getMinMax = table => { ...@@ -28,9 +38,37 @@ const getMinMax = table => {
resolve(minmax[0]); resolve(minmax[0]);
}); });
}); });
}*/
const sortInitialDateAscending = (a, b) => a - b;
// Dates will be cast to numbers automagically:
// query available dates on DB
const setupDates = () => {
return new Promise(resolve => {
let layersQuery = `${baseUrl}/list_layers`;
d3.json(layersQuery).then(layers => {
layers.forEach(layer => {
if (layer.f_table_name.startsWith("periurbano")) {
let table = layer.f_table_name.split("periurbano_")[1];
dateArray.push(timeParse(table)); // convert table names to dates
userFiles.push(layer.f_table_name);
}
});
dateArray = dateArray.sort(sortInitialDateAscending); // order dates
userFiles = userFiles.sort();
dateMin = d3.min(dateArray);
dateMax = d3.max(dateArray);
//userFiles = dateArray.map( month => timeFormat(month)); // order table names by date
const dates = { min: dateMin, max: dateMax, dates: dateArray };
resolve(dates);
});
});
} }
const setupMap = () => { const setupMap = (dates) => {
return new Promise(resolve => { return new Promise(resolve => {
// make body tag to have style height: 100% // make body tag to have style height: 100%
$("body").css("height", "100%"); $("body").css("height", "100%");
...@@ -57,6 +95,11 @@ const setupMap = () => { ...@@ -57,6 +95,11 @@ const setupMap = () => {
minZoom: 8, minZoom: 8,
zoom: 6, zoom: 6,
attributionControl: false, attributionControl: false,
timeDimension: true,
timeDimensionOptions: {
times: L.TimeDimension.Util.explodeTimeRange(dates.min, dates.max, "P1Y"),
currentTime: dates.min
},
maxBounds: bounds maxBounds: bounds
}).setView([21.15, -100.94], 8); }).setView([21.15, -100.94], 8);
...@@ -118,9 +161,12 @@ const setupMap = () => { ...@@ -118,9 +161,12 @@ const setupMap = () => {
}); });
} }
const populateMap = async(mapData) => { const populateMap = async (mapData) => {
//tiles = mapboxLayer("urbanization_year");
let map = mapData.map; let map = mapData.map;
let munisLayer = L.geoJson(null, { let munisLayer = L.geoJson(null, {
style: { style: {
stroke: true, stroke: true,
...@@ -131,8 +177,8 @@ const populateMap = async(mapData) => { ...@@ -131,8 +177,8 @@ const populateMap = async(mapData) => {
}, },
interactive: false interactive: false
}); });
let munis = omnivore.geojson("data/municipios.geojson", null, munisLayer); //let munis = omnivore.geojson("data/municipios.geojson", null, munisLayer);
munis.addTo(map); //munis.addTo(map);
let corredorLayer = L.geoJson(null, { let corredorLayer = L.geoJson(null, {
style: { style: {
...@@ -140,15 +186,14 @@ const populateMap = async(mapData) => { ...@@ -140,15 +186,14 @@ const populateMap = async(mapData) => {
weight: 1, weight: 1,
color: "#00ff00", color: "#00ff00",
opacity: 1, opacity: 1,
weight: 1.0,
fill: true, fill: true,
fillOpacity: 1, fillOpacity: 1,
fillColor:"rgba(0,240,2,0.05)" fillColor:"rgba(0,240,2,0.05)"
}, },
interactive: false interactive: false
}); });
let corredor = omnivore.geojson("data/corredor_bajio.geojson", null, corredorLayer); //let corredor = omnivore.geojson("data/corredor_bajio.geojson", null, corredorLayer);
corredor.addTo(map); //corredor.addTo(map);
let bufferConLayer = L.geoJson(null, { let bufferConLayer = L.geoJson(null, {
style: { style: {
...@@ -156,15 +201,14 @@ const populateMap = async(mapData) => { ...@@ -156,15 +201,14 @@ const populateMap = async(mapData) => {
weight: 1, weight: 1,
color: "#d5bef5", color: "#d5bef5",
opacity: 1, opacity: 1,
weight: 1.0,
fill: true, fill: true,
fillOpacity: 1, fillOpacity: 1,
fillColor: "rgba(213,190,245,0.25)" fillColor: "rgba(213,190,245,0.25)"
}, },
interactive: false interactive: false
}); });
let bufferCon = omnivore.geojson("data/buffer_corredor_con_locs.geojson", null, bufferConLayer); //let bufferCon = omnivore.geojson("data/buffer_corredor_con_locs.geojson", null, bufferConLayer);
bufferCon.addTo(map); //bufferCon.addTo(map);
let bufferSinLayer = L.geoJson(null, { let bufferSinLayer = L.geoJson(null, {
style: { style: {
...@@ -172,16 +216,73 @@ const populateMap = async(mapData) => { ...@@ -172,16 +216,73 @@ const populateMap = async(mapData) => {
weight: 1, weight: 1,
color: "#c43c39", color: "#c43c39",
opacity: 1, opacity: 1,
weight: 1.0,
fill: true, fill: true,
fillOpacity: 1, fillOpacity: 1,
fillColor: "rgba(196,60,57,0.25)" fillColor: "rgba(196,60,57,0.25)"
}, },
interactive: false interactive: false
}); });
let bufferSin = omnivore.geojson("data/buffer_corredor_sin_locs.geojson", null, bufferSinLayer); //let bufferSin = omnivore.geojson("data/buffer_corredor_sin_locs.geojson", null, bufferSinLayer);
bufferSin.addTo(map); //bufferSin.addTo(map);
// create mvt layers
userFiles.forEach(f => {
mapboxLayer(f);
});
glmap = L.mapboxGL({
accesToken: "no-token",
style: {
"version": 8,
"sources": {},
"layers": []
}
}).addTo(map);
// after mapboxGL map is ready with styles do this:
glmap.getMapboxMap().on("style.load", () => {
userFiles.forEach(year => {
glmap.getMapboxMap().addLayer(currentTiles[year]);
});
Object.keys(allTiles).forEach(layer => {
if (layer == userFiles[0]) {
glmap.getMapboxMap().setPaintProperty(layer, "fill-opacity", 0.7);
}
})
//glmap.getMapboxMap().addLayer(tiles);
/*glmap.getMapboxMap().addSource("munisvt", {
type: "vector",
tiles: [`${baseUrl}/periurbano_2015/mvt/{z}/{x}/{y}?geom_column=geom&columns=gridcode`],
//data: "http://localhost:8090/centropais/data/municipios.geojson",
maxzoom: 19,
minzoom: 6
});
glmap.getMapboxMap().addLayer({
id: "munisvt",
source: "munisvt",
"source-layer": "periurbano_2015", //table name
//"layout": {},
type: "fill",
paint: {
"fill-opacity": 0.5,
"fill-color": "#088"
}
});*/
timeDimensionControl.addTo(map);
// Pass dummy geojson layer to timeDimension in order to register and sync
timeLayer = L.timeDimension.layer.Tile(L.geoJSON(), {
updateTimeDimension: true,
updateTimeDimensionMode: "replace",
waitForReady: true,
duration: "P1Y"
});
timeLayer.addTo(map);
let baseLayers = { let baseLayers = {
"Carto Dark": cartoDarkLayer, "Carto Dark": cartoDarkLayer,
...@@ -189,15 +290,162 @@ const populateMap = async(mapData) => { ...@@ -189,15 +290,162 @@ const populateMap = async(mapData) => {
"OpenStreetMap": osmLayer "OpenStreetMap": osmLayer
}; };
var overlays = { var overlays = {
"<span id=\"bufferSin\"><svg height=\"15\" width=\"15\"><path d=\"M0 0 L15 0 L15 10 L0 10 Z \" x=\"5\" y=\"5\" fill=\"rgba(196,60,57,0.25)\" stroke=\"#c43c39\"></rect></svg>&nbsp; Interfaz periurbana del Corredor del Baj&iacute;o de<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2km <b>sin</b> localidades de m&aacute;s 10mil habitantes</span>": bufferSin, //"<span id=\"bufferSin\"><svg height=\"15\" width=\"15\"><path d=\"M0 0 L15 0 L15 10 L0 10 Z \" x=\"5\" y=\"5\" fill=\"rgba(196,60,57,0.25)\" stroke=\"#c43c39\"></rect></svg>&nbsp; Interfaz periurbana del Corredor del Baj&iacute;o de<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2km <b>sin</b> localidades de m&aacute;s 10mil habitantes</span>": bufferSin,
"<span id=\"bufferCon\"><svg height=\"15\" width=\"15\"><path d=\"M0 0 L15 0 L15 10 L0 10 Z \" x=\"5\" y=\"5\" fill=\"rgba(213,190,245,0.25)\" stroke=\"#d5bef5\"></rect></svg>&nbsp; Interfaz periurbana del Corredor del Baj&iacute;o de<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2km <b>coin</b> localidades de m&aacute;s 10mil habitantes</span>": bufferCon, //"<span id=\"bufferCon\"><svg height=\"15\" width=\"15\"><path d=\"M0 0 L15 0 L15 10 L0 10 Z \" x=\"5\" y=\"5\" fill=\"rgba(213,190,245,0.25)\" stroke=\"#d5bef5\"></rect></svg>&nbsp; Interfaz periurbana del Corredor del Baj&iacute;o de<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2km <b>con</b> localidades de m&aacute;s 10mil habitantes</span>": bufferCon,
"<span id=\"corredor\"><svg height=\"15\" width=\"15\"><path d=\"M0 0 L15 0 L15 10 L0 10 Z \" x=\"5\" y=\"5\" fill=\"rgba(0,240,2,0.05)\" stroke=\"#00ff00\"></rect></svg>&nbsp; Corredor del Baj&iacute;o</span>": corredor, //"<span id=\"corredor\"><svg height=\"15\" width=\"15\"><path d=\"M0 0 L15 0 L15 10 L0 10 Z \" x=\"5\" y=\"5\" fill=\"rgba(0,240,2,0.05)\" stroke=\"#00ff00\"></rect></svg>&nbsp; Corredor del Baj&iacute;o</span>": corredor,
"<span id=\"munis\"><svg height=\"15\" width=\"15\"><path d=\"M0 0 L15 0 L15 10 L0 10 Z \" x=\"5\" y=\"5\" fill=\"rgba(128,128,128, 0.05)\" stroke=\"#808080\"></rect></svg>&nbsp; Contexto municipal</span>": munis, //"<span id=\"munis\"><svg height=\"15\" width=\"15\"><path d=\"M0 0 L15 0 L15 10 L0 10 Z \" x=\"5\" y=\"5\" fill=\"rgba(128,128,128, 0.05)\" stroke=\"#808080\"></rect></svg>&nbsp; Contexto municipal</span>": munis,
}; };
// fix for leaflet-mapbox-gl v. 0.0.11 that adds map's to tile pane:
// get children of map tile pane, create array from it and iterate
// setting their z-index
let glmapChildren = map.getPanes().tilePane.children,
children = Array.from(glmapChildren);
children.forEach(c => c.style.zIndex = "inherit");
layerControl = L.control.layers(baseLayers, overlays).addTo(map); layerControl = L.control.layers(baseLayers, overlays).addTo(map);
});
}
// define MVT layer for given table and all trimester column
const mapboxLayer = (table) => {
let pbfLayer = {
id: table,
source: {
type: "vector",
tiles: [`${baseUrl}/${table}/mvt/{z}/{x}/{y}?geom_column=geom&columns=grd_urb`],
maxzoom: 19,
minzoom: 6
},
"source-layer": table,
type: "fill",
"paint": {
"fill-opacity": 0,
"fill-color": [
"match",
["get", "grd_urb"],
"Bajo", "rgb(49,54,149)",
"Medio", "rgb(255,255,191)",
"Alto", "rgb(158,1,66)",
/* other values */ "#ccc"
]
}
}
currentTiles[table] = pbfLayer;
allTiles[table] = pbfLayer;
return pbfLayer;
} }
const setupTimeDimensionControl = () => {
L.Control.TimeDimensionCustom = L.Control.TimeDimension.extend({
_getDisplayDateFormat: date => {
let d = new Date(date);
let year = d.getFullYear().toString();
//let month = d.getUTCMonth();
//return `${monthArray[month]} ${year}`;
return `${year}`;
}
});
timeDimensionControl = new L.Control.TimeDimensionCustom({
loopButton: true,
/*minSpeed: 1,
maxSpeed: 5,*/
timeSteps: 1,
playReverseButton: true,
//limitSliders: true,
playerOptions: {
//buffer: 5,
//minBufferReady: 5,
transitionTime: 125,
loop: true
},
timeZones: ["Local"]
});
}
L.TimeDimension.Layer.Tile = L.TimeDimension.Layer.extend({
_setAvailableTimes: function() {
if (this.options.times) {
return L.TimeDimension.Util.parseTimesExpression(this.options.times);
} else if (this.options.timeInterval) {
let tiArray = L.TimeDimension.Util.parseTimeInterval(this.options.timeInterval);
let period = this.options.period || "P1D";
let validTimeRange = this.options.validTimeRange || undefined;
//alert("times");
return L.TimeDimension.Util.explodeTimeRange(tiArray[0], tiArray[1], period, validTimeRange);
} else {
return [];
}
},
onAdd: function(map) {
// Don't call prototype so this_update() does not get called twice
//L.TimeDimension.Layer.prototype.onAdd.call(this, map);
this._map = map;
if (!this._timeDimension && map.timeDimension) {
this._timeDimension = map.timeDimension;
}
this._timeDimension.on("timeloading", this._onNewTimeLoading, this);
this._timeDimension.on("timeload", this._update, this);
this._timeDimension.registerSyncedLayer(this);
map.addLayer(this._baseLayer);
this._update();
},
onRemove: function(map) {
this._timeDimension.unregisterSyncedLayer(this);
this._timeDimension.off("timeloading", this._onNewTimeLoading, this);
this._timeDimension.off("timeload", this._update, this);
//this._baseLayer.getContainer().style.display = "none";
/*Object.keys(allTiles).forEach(layer => { // hide all tiles
glmap.getMapboxMap().setPaintProperty(layer, "fill-opacity", 0);
});*/
//this.eachLayer(map.removeLayer, map);
//this._map = null;
},
isReady: function(time) {
// to be implemented for each type of layer
return true;
},
_update: function() {
if (!this._baseLayer || !this._map) {
return;
}
var time = this._timeDimension.getCurrentTime();
//TODO: get current time, parse quarter and compare tile value. Incremental add over time
// get data for time
let d = new Date(time),
year = d.getFullYear().toString(),
m = d.getUTCMonth(),
yeartrimester = year + "." + (m / 3 + 1),
yearQ = `Q${m/3+1} ${year}`;
//month = monthArray[m].toLowerCase(),
//monthYear = `${month}_${year}`;
// Update title
let title = $("#title");
title.html(`<h2>Crecimiento urbano en la regi&oacute;n metropolitana centro pa&iacute;s en ${year}</h2>`);
// styleTiles based on yeartrimester value
Object.keys(allTiles).forEach(layer => {
if (layer.split("periurbano_")[1] !== year) { // hide all other years
glmap.getMapboxMap().setPaintProperty(layer, "fill-opacity", 0);
} else {
glmap.getMapboxMap().setPaintProperty(layer, "fill-opacity", 0.7);
}
})
}
});
L.timeDimension.layer.Tile = (layer, options) => {
return new L.TimeDimension.Layer.Tile(layer, options);
};
// hide initial screen and show map // hide initial screen and show map
$("#startHeader").remove(); $("#startHeader").remove();
$(".picker").remove(); $(".picker").remove();
...@@ -210,5 +458,19 @@ $("#mexmap").show(); ...@@ -210,5 +458,19 @@ $("#mexmap").show();
$("#odTableRow").removeClass("align-self-center"); $("#odTableRow").removeClass("align-self-center");
$("#odCard").hide(); $("#odCard").hide();
setupMap() /*setupMap()
.then(map => populateMap(map)); .then(map => populateMap(map));*/
\ No newline at end of file
d3.json("https://unpkg.com/d3-time-format@2.1.1/locale/es-MX.json").then(async locale => {
d3.timeFormatDefaultLocale(locale);
//let timeParse = d3.timeParse("%B_%Y");
//let timeFormat = d3.timeFormat("cuerpos_%B_%Y");
setupTimeDimensionControl();
let years = await setupDates(), // get available years
mapData = await setupMap(years);
populateMap(mapData);
});
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment