Commit 09a94557 authored by Rodrigo Tapia-McClung's avatar Rodrigo Tapia-McClung

Urbanization tiles with timedimension

parent 4c9f8000
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Crecimiento urbano en la regi&oacute;n metropolitana centro pa&iacute;s</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<link rel="stylesheet" href="http://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css" />
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.5.1/dist/leaflet.css" />
<link rel="stylesheet" href="https://cdn.rawgit.com/socib/Leaflet.TimeDimension/master/dist/leaflet.timedimension.control.min.css" />
<link rel="stylesheet" href="https://api.tiles.mapbox.com/mapbox-gl-js/v1.1.0/mapbox-gl.css" />
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<link rel="stylesheet" href="../css/style.css">
<link rel="icon" href="../img/grijalva.png" sizes="16x16">
</head>
<body>
<div id="initial-backdrop">
<img class="img_bg" src="../img/centropais2.jpg" alt="Afectaci&oacute;n por inundaci&oacute;n">
<span class="photo-credit">Foto: Felinto C&oacute;rdoda </span>
</div>
<div class="container" id="startHeader">
<div class="jumbotron mt-5" style="background-color: rgba(233,236,239,0.75); opacity: 0.99;">
<h1 class="display-4">Monitoreo de crecimiento urbano en la regi&oacute;n metropolitana centro pa&iacute;s</h1>
<p class="lead">Consulta el crecimiento de los aglomerados urbanos del Corredor Metropolitano Centro Pa&iacute;s resultado del an&aacute;lisis de im&aacute;genes &oacute;pticas de Sentinel-2 y conoce los indicadores que caracterizan su evoluci&oacute;n econ&oacute;mica
en las &uacute;ltimas d&eacute;cadas.</p>
<hr class="my-4">
<!--<p>Escoge las fechas para las que quieres explorar el crecimiento urbano y algunos indicadores.</p>-->
<div class="container">
<div class="row justify-content-center">
<!--<div class="col-4 text-center">
<input type="text" name="date-initial" id="date-initial" readonly="readonly" size="12" placeholder="Fecha inicial" data-calendar="false" />
</div>
<div class="col-4 text-center">
<input type="text" name="date-final" id="date-final" readonly="readonly" size="12" placeholder="Fecha final" data-calendar="true" />
</div>-->
<button type="button" class="btn btn-primary btn-lg" id="showMap">Entrar</button>
</div>
</div>
</div>
</div>
<div class="d-flex flex-column" id="mainContainer">
<header class="page-header py-2">
<div class="container text-center">
<div class="row align-items-center text-center" id="title">
<h2>Crecimiento urbano en la regi&oacute;n metropolitana centro pa&iacute;s</h2>
</div>
</div>
</header>
<!-- middle container -->
<div class="container-fluid d-flex flex-fill">
<div class="row d-flex flex-fill">
<div class="col-md-6" id="mexmap">
<!-- <div class="col-md-10" id="mexmap"> -->
<!--<div id="datePickers"></div>
<div class="picker">
<select id="indicatorSelect"></select>
</div>-->
<div class="text-center loader">
<i class="fa fa-spinner2 fa-spin fa-5x loadSpinner"></i><br>
<span class="loadText">Cargando datos...<br>
<span id="wb-fileLoad"></span>
<span id="basemap-fileLoad"></span>
</span>
</div>
</div>
<div class="col-md-6" id="overlaydiv"></div>
<div class="col-md-6" id="story">
<div class="loader"></div>
<div class="row h-50 border-bottom">
<!--<div class="col-6 border-right">-->
<div class="col">
<div id="amchartdiv"></div>
</div>
<!--<div class="col-6">
<div id="df-graph"></div>
</div>-->
</div>
<div class="row h-50">
<div class="col-6 border-right">
<div id="tablediv">
<table class="table table-dark table-striped table-fixed" id="tblViajes">
<thead class="thead-dark">
<tr>
<th scope="col">Origen</th>
<th scope="col">Destino</th>
<th scope="col">Viajes</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
<div class="col-6">
<div id="perimeter-graph"></div>
</div>
</div>
</div>
</div>
</div>
<footer class="page-footer py-2">
<div class="container-fluid text-center">
<div class="row align-items-center">
<div class="col">
<span id="logo-conacyt"><a href="http://www.concayt.gob.mx" target="_blank"><img
src="../img/conacyt3.png" height="70" class="grayscale"></a></span>
</div>
<div class="col">
<span id="logo-cg"><a href="http://www.centrogeo.org.mx" target="_blank"><img
src="../img/centrogeo.png" height="70" class="grayscale"></a></span>
</div>
<div class="col">
<span id="logo-geoint"><a href="http://geoint.mx/" target="_blank"><img
src="../img/geoint.png" height="70" class="grayscale"></a></span>
</div>
</div>
</div>
</footer>
</div>
<!-- Modal -->
<div class="modal fade" id="explainIndicatorModal" tabindex="-1" role="dialog" aria-labelledby="explainIndicatorModal" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="explainIndicatorModalTitle">Modal title</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body"></div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-dismiss="modal">Cerrar</button>
</div>
</div>
</div>
</div>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
<script src="https://unpkg.com/leaflet@1.5.1/dist/leaflet.js"></script>
<script src="https://cdn.rawgit.com/nezasa/iso8601-js-period/master/iso8601.min.js"></script>
<script src="https://cdn.rawgit.com/socib/Leaflet.TimeDimension/master/dist/leaflet.timedimension.min.js"></script>
<script src="https://api.tiles.mapbox.com/mapbox-gl-js/v1.1.0/mapbox-gl.js"></script>
<script src="https://unpkg.com/mapbox-gl-leaflet/leaflet-mapbox-gl.js"></script>
<script src="../js/Leaflet.Sync.js"></script>
<script src="../js/evolucion_urbana.js"></script>
</body>
</html>
\ No newline at end of file
/*
* Copyright 2020 - All rights reserved.
* Rodrigo Tapia-McClung
*
* August 2020
*/
/* globals omnivore, Promise, chroma, makeBaseMap, makeIndicatorGraph, getData, getDataInSelection */
/* exported indicators. userFiles, userDates, timeParse, layerControl, updateCharts, intervals */
let timeDimensionControl,
map,
overlay,
glmap,
osmLayer, cartoLightLayer, cartoDarkLayer,
timeLayer,
layerControl,
tiles,
scale;
$("#showMap").on("click", () => {
// hide initial screen and show map
$("#startHeader").remove();
$("#initial-backdrop").remove();
$("#mainContainer")[0].style.setProperty("display", "flex", "important")
$("#mexmap").show();
setupTimeDimensionControl();
getMinMax("urbanization_year") // get minmax year.Q values to popoulate timeDimension
.then(dates => setupMap(dates))
.then(map => populateMap(map));
});
const getMinMax = table => {
return new Promise(resolve => {
let table = "urbanization_year";
const baseUrl = new URL(`/data`, window.location.href).href;
let minmaxQuery = `${baseUrl}/query/${table}?columns=${`min(yeartrimes), max(yeartrimes)`}`;
d3.json(minmaxQuery).then(minmax => {
resolve(minmax[0]);
});
});
}
const setupMap = (dates) => {
return new Promise(resolve => {
// make body tag to have style height: 100%
$("body").css("height", "100%");
osmLayer = L.tileLayer("http://{s}.tile.osm.org/{z}/{x}/{y}.png", {
maxZoom: 19,
attribution: "&copy; <a href=\"http://osm.org/copyright\" target=\"_blank\">OpenStreetMap</a> contributors"
});
cartoLightLayer = L.tileLayer("https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png", {
maxZoom: 19,
attribution: "Map tiles by Carto, under CC BY 3.0. Data by OpenStreetMap, under ODbL."
});
cartoDarkLayer = L.tileLayer("https://cartodb-basemaps-{s}.global.ssl.fastly.net/dark_all/{z}/{x}/{y}.png", {
maxZoom: 19,
attribution: "Map tiles by Carto, under CC BY 3.0. Data by OpenStreetMap, under ODbL."
});
//let bounds = cuencaBufferMask.getBounds();
let southWest = L.latLng(19.87, -102.87),
northEast = L.latLng(23.02, -99.31),
bounds = L.latLngBounds(southWest, northEast);
let datesArray = [dates.min, dates.max]; // [2014.2, 2019.4]
// convert year.Q dates array to initil and end Date object to explode in timeDimension
let times = datesArray.map( date => {
let q = (date - parseInt(date))*10;
return new Date(parseInt(date), parseInt(q*3-3), 1);
});
map = L.map("mexmap", {
//center: [17.22, -92.28],
minZoom: 8,
zoom: 6,
attributionControl: false,
timeDimension: true,
//timeDimensionControl: true,
timeDimensionOptions: {
times: L.TimeDimension.Util.explodeTimeRange(times[0], times[1], "P3M")
//times: userDates,
//currentTime: dates.min
},
maxBounds: bounds
}).setView([21.15, -100.94], 8);
cartoDarkLayer.addTo(map);
// set sync map on right
let osmLayerOverlay = L.tileLayer("http://{s}.tile.osm.org/{z}/{x}/{y}.png", {
maxZoom: 19,
attribution: "&copy; <a href=\"http://osm.org/copyright\" target=\"_blank\">OpenStreetMap</a> contributors"
}),
cartoLightLayerOverlay = L.tileLayer("https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png", {
maxZoom: 19,
attribution: "Map tiles by Carto, under CC BY 3.0. Data by OpenStreetMap, under ODbL."
}),
cartoDarkLayerOverlay = L.tileLayer("https://cartodb-basemaps-{s}.global.ssl.fastly.net/dark_all/{z}/{x}/{y}.png", {
maxZoom: 19,
attribution: "Map tiles by Carto, under CC BY 3.0. Data by OpenStreetMap, under ODbL."
});
overlay = L.map("overlaydiv", {
zoomControl: false,
inertia: false,
keyboard: false,
//dragging: false,
scrollWheelZoom: true,
attributionControl: false,
zoomAnimation: true
}).setView([16.96, -92.97], 8);
cartoDarkLayerOverlay.addTo(overlay);
L.control.attribution({ position: "bottomright" }).addTo(overlay);
// on base layer on main map, change base layer on overlay map
map.on("baselayerchange", event => {
overlay.eachLayer(l => overlay.removeLayer(l));
if (event.name == "Carto Dark") {
cartoDarkLayerOverlay.addTo(overlay);
} else if (event.name == "Carto Light") {
cartoLightLayerOverlay.addTo(overlay);
} else if (event.name == "OpenStreetMap") {
osmLayerOverlay.addTo(overlay);
}
});
const offsetGlobal = (center, zoom, refMap, tgtMap) => {
var refC = refMap.getContainer();
var tgtC = tgtMap.getContainer();
var pt = refMap.project(center, zoom)
.subtract([refC.offsetLeft, refC.offsetTop])
.subtract(refMap.getSize().divideBy(2))
.add([tgtC.offsetLeft, tgtC.offsetTop])
.add(tgtMap.getSize().divideBy(2));
return refMap.unproject(pt, zoom);
}
map.sync(overlay, { offsetFn: offsetGlobal });
resolve({ "map": map });
});
}
const populateMap = async(mapData) => {
tiles = mapboxLayer("urbanization_year");
let map = mapData.map;
glmap = L.mapboxGL({
accessToken: "no-token",
style: {
"version": 8,
"sources": {},
"layers": []
}
}).addTo(map);
// after mapboxGL map is ready with styles do this:
glmap.getMapboxMap().on("style.load", () => {
glmap.getMapboxMap().addLayer(tiles);
//glmap.getMapboxMap().setPaintProperty(tiles, "fill-opacity", 0.7)
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: "P1M"
});
timeLayer.addTo(map);
// FIX: comment out to avoid DB calls
// style currentTiles
/*let option = $("#indicatorSelect").val(); // option selected from dropdrown
styleTiles(option, minIndicators, maxIndicators)
.then(legend.addTo(map)); // add legend control -> it updates
*/
let baseLayers = {
"Carto Dark": cartoDarkLayer,
"Carto Light": cartoLightLayer,
"OpenStreetMap": osmLayer
};
var overlays = {
// "<span id=\"cuencaOverlay\">Agua en la cuenca del Grijalva</span>": timeLayer
};
layerControl = L.control.layers(baseLayers, overlays).addTo(map);
//legend.addTo(map);
// 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" );
});
}
// define MVT layer for given table and all trimester column
const mapboxLayer = (table) => {
const baseUrl = new URL(`/data`, window.location.href).href;
let pbfLayer = {
id: table,
source: {
type: "vector",
tiles: [`${baseUrl}/${table}/mvt/{z}/{x}/{y}?geom_column=geom&columns=yeartrimes`],
maxzoom: 19,
minzoom: 6
},
"source-layer": table,
type: "fill",
"paint": {
"fill-opacity": 0.7,
"fill-color": [
"interpolate",
["linear"],
["get", "yeartrimes"],
2014, "rgba(0, 255, 0, 0.5)", // green
2019, "rgba(255, 0, 0, 0.5)", // red
],/*
"fill-outline-color": [
"interpolate",
["linear"],
["get", "df"],
1, "rgba(255, 0, 0, 0.6)", // red
1.3, "rgba(0, 255, 0, 0.6)", // green
]*/
}
}
/*currentTiles[monthYear] = pbfLayer;
allTiles[monthYear] = 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 `Q${month/3+1} ${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 ${yearQ}</h2>`);
// styleTiles based on yeartrimester value
let filter = ["<=", ["get", "yeartrimes"], parseFloat(yeartrimester)];
glmap.getMapboxMap().setFilter("urbanization_year", filter);
/*let color = [
"interpolate", ["linear"],
["get", option],
minIndicators[option], scale(minIndicators[option]).hex(),
maxIndicators[option], scale(maxIndicators[option]).hex()
];*/
//glmap.getMapboxMap().setPaintProperty(layer, "fill-color", color);
// Update graphs only on timeload event
/*indicators.forEach(indicator => {
indicatorVars[indicator].chartData = indicatorVars[indicator].chart.data(); // get chart data
indicatorVars[indicator].chart.data(indicatorVars[indicator].chartData); // set chart data
});*/
//console.time("process");
//console.log("data for", monthYear);
//console.log(currentTiles)
/*Object.keys(allTiles).forEach(layer => {
if (layer !== monthYear) { // hide all other months
glmap.getMapboxMap().setPaintProperty(layer, "fill-opacity", 0);
} else { // except current one
glmap.getMapboxMap().setPaintProperty(layer, "fill-opacity", 0.7);
}
});*/
//console.timeEnd("process");
}
});
L.timeDimension.layer.Tile = (layer, options) => {
return new L.TimeDimension.Layer.Tile(layer, options);
};
\ 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