Commit 19653fe8 authored by Tania Gómez's avatar Tania Gómez

common layers in centropais project

parent 98a94535
......@@ -150,7 +150,7 @@
<script type="text/javascript" src="../js/Leaflet.Sync.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.js"></script>
<script type="text/javascript" src="../js/jszip.min.js"></script>
<script src="../js/grijalva_functions.js"></script>
<script src="../js/centropais_functions.js"></script>
<script src="../js/centropais_basemap.js"></script>
<script src="../js/grijalva_charts.js"></script>
......
......@@ -130,7 +130,7 @@ const getCuencasColor = s => {
"rgba(168,56,0,0.3)";
}
let munis_contexto, zms, pistasaereas, autobuses, supermercados, bancos, agebsbypop, agebsbydens, agricolaoi, agricolapv, escuelas, hospitales, hoteles, subcuencas, tuxtlaVH;
let munis_contexto, lim_zms, zms, conurbaciones, pistasaereas, autobuses, supermercados, bancos, agebsbypop, agebsbydens, agricolaoi, agricolapv, escuelas, hospitales, hoteles, subcuencas, tuxtlaVH;
// *** WMS INEGI ***
// // AEROPUERTOS
......@@ -176,15 +176,15 @@ let layer_RedNacionalCaminos = new L.tileLayer.wms("http://gaia.inegi.org.mx/NLB
attribution: "INEGI 2019"
});
//MANZANAS
let layer_manzanaswms = new L.tileLayer.wms("http://gaia.inegi.org.mx/NLB/tunnel/wms/wms61?", {
layers: "c104",
format: "image/svg+xml",
transparent: true,
pane: "pane_manzanas",
attribution: "INEGI 2019",
minZoom: 13,
maxZoom: 18
});
// let layer_manzanaswms = new L.tileLayer.wms("http://gaia.inegi.org.mx/NLB/tunnel/wms/wms61?", {
// layers: "c104",
// format: "image/svg+xml",
// transparent: true,
// pane: "pane_manzanas",
// attribution: "INEGI 2019",
// minZoom: 13,
// maxZoom: 18
// });
//LOCALIDAD URBANA
let layer_locUrban = new L.tileLayer.wms("http://gaia.inegi.org.mx/NLB/tunnel/wms/wms61?", {
layers: "c102",
......@@ -225,7 +225,7 @@ let layer_pistasaereas = new L.geoJson(null, {
borderWidth: 0,
borderColor: "transparent",
backgroundColor: "transparent" //,
// textColor: "#C0C0C0"
// textColor: "#C0C0C0"
});
return new L.Marker(latlng, {
icon: avionMarker
......@@ -258,7 +258,7 @@ let layer_autobuses = new L.geoJson(null, {
borderWidth: 0,
borderColor: "transparent",
backgroundColor: "transparent" //,
// textColor: "#C0C0C0"
// textColor: "#C0C0C0"
});
return new L.Marker(latlng, {
icon: busMarker
......@@ -429,7 +429,7 @@ const style_limZMs = () => {
return {
pane: "pane_limZMs",
opacity: 1,
color: "rgba(101,43,0,0.6)",
color: "rgba(168,0,0,1)",
dashArray: "",
lineCap: "butt",
lineJoin: "miter",
......@@ -447,45 +447,16 @@ let layer_limZMs = new L.geoJson(null, {
const style_ZMs = () => {
return {
pane: "pane_limZMs",
pane: "pane_ZMs",
opacity: 1,
color: "rgba(110,110,100,1)",
dashArray: "",
lineCap: "butt",
lineJoin: "miter",
weight: 1.0,
fillOpacity: "rgba(255,170,0,1)"
fillOpacity: 1,
fillColor: "rgba(255,170,0,1)"
}
// switch (String(feature.properties["ZM_NOMBRE"])) {
// case "Tuxtla Gutiérrez":
// return {
// pane: "pane_ZMs",
// opacity: 1,
// color: "rgba(139,47,0,0.6)",
// dashArray: "",
// lineCap: "butt",
// lineJoin: "miter",
// weight: 1.0,
// fill: true,
// fillOpacity: 1,
// fillColor: "rgba(139,60,0,0.6)"
// }
// //break;
// case "Villahermosa":
// return {
// pane: "pane_ZMs",
// opacity: 1,
// color: "rgba(139,47,0,0.6)",
// dashArray: "",
// lineCap: "butt",
// lineJoin: "miter",
// weight: 1.0,
// fill: true,
// fillOpacity: 1,
// fillColor: "rgba(207,152,62,0.6)"
// }
// //break;
// }
}
let layer_ZMs = new L.geoJson(null, {
......@@ -495,6 +466,27 @@ let layer_ZMs = new L.geoJson(null, {
style: style_ZMs
});
const style_conurbaciones_rurbana = () => {
return {
pane: "pane_conurbaciones",
opacity: 1,
color: "rgba(35,35,35,0.1)",
dashArray: "",
lineCap: "butt",
lineJoin: "miter",
weight: 1.0,
fillOpacity: 1,
fillColor: "rgba(255,0,0,0.1)"
}
}
let layer_conurbaciones_rurbana = new L.geoJson(null, {
attribution: "",
pane: "pane_conurbaciones",
//onEachFeature: pop_ZMs,
style: style_conurbaciones_rurbana
});
const style_agebsbypop = feature => {
return {
pane: "pane_agebsbypop",
......@@ -621,9 +613,10 @@ const toggleButtons = () => {
const makeBaseMap = () => {
createPane("pane_munis", 402);
createPane("pane_munis", 402);
createPane("pane_munis_contexto", 402);
createPane("pane_ZMs", 403);
createPane("pane_limZMs", 404);
createPane("pane_conurbaciones", 404);
createPane("pane_locs", 405);
createPane("pane_cuencas", 406);
createPane("pane_buffer", 407);
......@@ -654,8 +647,8 @@ const makeBaseMap = () => {
layerControl.addOverlay(layer_RedNacionalCaminos, "Red Nacional de Caminos <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src=\"http://gaia.inegi.org.mx/NLB/tunnel/wms/mdm6wms?map=/opt/map/mdm60/mdm61leyendaprueba.map&Request=GetLegendGraphic&format=image/png&Version=1.1.1&Service=WMS&LAYER=c200\" alt=\"Red Nacional de Caminos\"/>");
zip2Lyr("../centropais/data/munis_contexto.zip", munis_contexto, layer_munis_contexto);
layerControl.addOverlay(layer_munis_contexto, "&nbsp;<svg height=\"15\" width=\"15\"><path d=\"M0 0 L15 0 L15 10 L0 10 Z \" x=\"5\" y=\"5\" fill=\"rgba(255,255,212,0.4)\" stroke=\"rgba(110,100,100,0.4)\" stroke-dasharray=\" \"></rect></svg>&nbsp; Municipios para dar contexto a la región");
// zip2Lyr("../riesgos/data/Central de autobuses.zip", autobuses, layer_autobuses);
// layerControl.addOverlay(layer_autobuses, "<i class=\"fa fa-bus1\" style=\"color:#fda50f;margin-top:3px; margin-left:0px;font-size: 13px;\"></i> Central de Autobuses");
zip2Lyr("../centropais/data/munis_conurbaciones_rurbana.zip", conurbaciones, layer_conurbaciones_rurbana);
layerControl.addOverlay(layer_conurbaciones_rurbana, "&nbsp;<svg height=\"15\" width=\"15\"><path d=\"M0 0 L15 0 L15 10 L0 10 Z \" x=\"5\" y=\"5\" fill=\"rgba(255,0,0,0.1)\" stroke=\"rgba(35,35,35,0.4)\" stroke-dasharray=\" \"></rect></svg>&nbsp; Municipios que albergan las conurbaciones de la región");
// zip2Lyr("../grijalva/data/escuelas.zip", escuelas, layer_escuelas);
// layerControl.addOverlay(layer_escuelas, "Escuelas con 50 personas ocupadas o más<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<i class=\"fa fa-escuela\" style=\"color:#409400;margin-top:3px; margin-left:0px;font-size: 13px;\"></i> Básico<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<i class=\"fa fa-escuela\" style=\"color:#df6400;margin-top:3px; margin-left:0px;font-size: 13px;\"></i> Medio superior<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<i class=\"fa fa-escuela\" style=\"color:#c0d1e5;margin-top:3px; margin-left:0px;font-size: 13px;\"></i> Otras escuelas");
// zip2Lyr("../grijalva/data/hospitales.zip", hospitales, layer_hospitales);
......@@ -677,8 +670,8 @@ const makeBaseMap = () => {
// layerControl.addOverlay(layer_agebsbypop, "Población total por AGEB <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<svg height=\"15\" width=\"15\"><path d=\"M0 0 L15 0 L15 10 L0 10 Z \" x=\"5\" y=\"5\" fill=\"#ffffff\" stroke=\"rgba(0,0,0,0.6)\" stroke-dasharray=\" \"></rect></svg> Sin Población<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<svg height=\"15\" width=\"15\"><path d=\"M0 0 L15 0 L15 10 L0 10 Z \" x=\"5\" y=\"5\" fill=\"#fcdd5d\" stroke = \"rgba(0,0,0,0.6)\" stroke-dasharray=\" \"></rect></svg> 0 - 1000 <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<svg height = \"15\" width=\"15\"><path d=\"M0 0 L15 0 L15 10 L0 10 Z \" x=\"5\" y=\"5\" fill=\"#f7ba3e\" stroke = \"rgba(0,0,0,0.6)\" stroke-dasharray=\" \"></rect></svg> 1001 - 2500 <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<svg height =\"15\" width=\"15\"><path d=\"M0 0 L15 0 L15 10 L0 10 Z \" x=\"5\" y=\"5\" fill=\"#d68522\" stroke=\"rgba(0,0,0,0.6)\" stroke-dasharray=\" \"></rect></svg> 2501 - 4000 <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<svg height =\"15\" width=\"15\"><path d=\"M0 0 L15 0 L15 10 L0 10 Z \" x=\"5\" y=\"5\" fill=\"#9e4410\" stroke=\"rgba(0,0,0,0.6)\" stroke-dasharray=\" \"></rect></svg> 4001 - 6000 <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<svg height =\"15\" width=\"15\"><path d=\"M0 0 L15 0 L15 10 L0 10 Z \" x=\"5\" y=\"5\" fill=\"#6b0601\" stroke=\"rgba(0,0,0,0.6)\" stroke-dasharray=\" \"></rect></svg> 6001 - 18600");
// layerControl.addOverlay(layer_manzanaswms, "<img src=\"http://gaia.inegi.org.mx/NLB/tunnel/wms/mdm6wms?map=/opt/map/mdm60/mdm61leyendaprueba.map&Request=GetLegendGraphic&format=image/png&Version=1.1.1&Service=WMS&LAYER=c102m\" alt=\"Manzanas\"/>");
layerControl.addOverlay(layer_locUrban, "<img src=\"http://gaia.inegi.org.mx/NLB/tunnel/wms/mdm6wms?map=/opt/map/mdm60/mdm61leyendaprueba.map&Request=GetLegendGraphic&format=image/png&Version=1.1.1&Service=WMS&LAYER=c102\" alt=\"Localidades urbanas\"/>");
// zip2Lyr("../grijalva/data/limites_TG_V.zip", tuxtlaVH, layer_limZMs);
// layerControl.addOverlay(layer_limZMs, "&nbsp; <svg height=\"15\" width=\"15\"><path d=\"M0 0 L15 0 L15 10 L0 10 Z \" x=\"5\" y=\"5\" fill=\"none\" stroke=\"rgba(101,43,0,0.6)\" stroke-dasharray=\" \"></rect></svg> Límites de zonas metropolitanas");
zip2Lyr("../centropais/data/limite_ZMs.zip", lim_zms, layer_limZMs);
layerControl.addOverlay(layer_limZMs, "&nbsp; <svg height=\"15\" width=\"15\"><path d=\"M0 0 L15 0 L15 10 L0 10 Z \" x=\"5\" y=\"5\" fill=\"none\" stroke=\"rgba(168,0,0,1)\" stroke-dasharray=\" \"></rect></svg> Límites de zonas metropolitanas");
zip2Lyr("../centropais/data/ZMs.zip", zms, layer_ZMs);
layerControl.addOverlay(layer_ZMs, "&nbsp;<svg height=\"15\" width=\"15\"><path d=\"M0 0 L15 0 L15 10 L0 10 Z \" x=\"5\" y=\"5\" fill=\"rgba(255,170,0,1)\" stroke=\"rgba(110,100,100,1)\" stroke-dasharray=\" \"></rect></svg>&nbsp;Zonas metropolitanas");
// zip2Lyr("../grijalva/data/cuencas.zip", subcuencas, layer_cuencas);
......@@ -689,113 +682,113 @@ const makeBaseMap = () => {
// Localize Leaflet.Draw texts
L.drawLocal = {
draw: {
toolbar: {
actions: {
title: "Cancelar dibujo",
text: "Cancelar"
},
finish: {
title: "Terminar dibujo",
text: "Terminar"
},
undo: {
title: "Eliminar último punto dibujado",
text: "Eliminar último punto"
},
buttons: {
polyline: "Dibujar una polilínea",
polygon: "Dibujar un polígono",
rectangle: "Dibujar un rectángulo",
circle: "Dibujar un círculo",
marker: "Dibujar un marcador",
circlemarker: "Dibujar un marcador circular"
}
},
handlers: {
circle: {
tooltip: {
start: "Haz click y arrastra para dibujar un círculo"
draw: {
toolbar: {
actions: {
title: "Cancelar dibujo",
text: "Cancelar"
},
radius: "Radio"
},
circlemarker: {
tooltip: {
start: "Haz click en el mapa para ubicar el marcador circular"
}
},
marker: {
tooltip: {
start: "Haz click en el mapa para ubicar el marcador"
}
},
polygon: {
error: "<strong>Error:</strong>",
tooltip: {
start: "Haz click para empezar a dibujar la figura",
cont: "Haz click para continuar dibujando la figura",
end: "Haz click en el primer punto para cerrar la figura"
}
},
polyline: {
error: "<strong>Error:</strong> las líneas no deben cruzarse",
tooltip: {
start: "Haz click para empezar a dibujar la línea",
cont: "Haz click para continuar dibujando la línea",
end: "Haz click en el último punto para terminar la línea"
}
},
rectangle: {
tooltip: {
start: "Haz click y arrastra para dibujar un rectángulo"
finish: {
title: "Terminar dibujo",
text: "Terminar"
},
undo: {
title: "Eliminar último punto dibujado",
text: "Eliminar último punto"
},
buttons: {
polyline: "Dibujar una polilínea",
polygon: "Dibujar un polígono",
rectangle: "Dibujar un rectángulo",
circle: "Dibujar un círculo",
marker: "Dibujar un marcador",
circlemarker: "Dibujar un marcador circular"
}
},
simpleshape: {
tooltip: {
end: "Suelta el ratón para terminar de dibujar"
}
}
}
},
edit: {
toolbar: {
actions: {
save: {
title: "Guardar los cambios",
text: "Guardar"
handlers: {
circle: {
tooltip: {
start: "Haz click y arrastra para dibujar un círculo"
},
radius: "Radio"
},
cancel: {
title: "Cancelar la edición, descarta todos los cambios",
text: "Cancelar"
circlemarker: {
tooltip: {
start: "Haz click en el mapa para ubicar el marcador circular"
}
},
marker: {
tooltip: {
start: "Haz click en el mapa para ubicar el marcador"
}
},
polygon: {
error: "<strong>Error:</strong>",
tooltip: {
start: "Haz click para empezar a dibujar la figura",
cont: "Haz click para continuar dibujando la figura",
end: "Haz click en el primer punto para cerrar la figura"
}
},
polyline: {
error: "<strong>Error:</strong> las líneas no deben cruzarse",
tooltip: {
start: "Haz click para empezar a dibujar la línea",
cont: "Haz click para continuar dibujando la línea",
end: "Haz click en el último punto para terminar la línea"
}
},
clearAll: {
title: "Limpiar todas las capas",
text: "Limpiar todo"
rectangle: {
tooltip: {
start: "Haz click y arrastra para dibujar un rectángulo"
}
},
simpleshape: {
tooltip: {
end: "Suelta el ratón para terminar de dibujar"
}
}
},
buttons: {
edit: "Editar capas",
editDisabled: "No hay capas que editar",
remove: "Eliminar capas",
removeDisabled: "No hay capas que eliminar"
}
},
handlers: {
edit: {
tooltip: {
text: "Arrastra el marcador para editar la figura",
subtext: "Haz click en cancelar para deshacer los cambios"
edit: {
toolbar: {
actions: {
save: {
title: "Guardar los cambios",
text: "Guardar"
},
cancel: {
title: "Cancelar la edición, descarta todos los cambios",
text: "Cancelar"
},
clearAll: {
title: "Limpiar todas las capas",
text: "Limpiar todo"
}
},
buttons: {
edit: "Editar capas",
editDisabled: "No hay capas que editar",
remove: "Eliminar capas",
removeDisabled: "No hay capas que eliminar"
}
},
remove: {
tooltip: {
text: "Haz click en una figura para eliminarla"
handlers: {
edit: {
tooltip: {
text: "Arrastra el marcador para editar la figura",
subtext: "Haz click en cancelar para deshacer los cambios"
}
},
remove: {
tooltip: {
text: "Haz click en una figura para eliminarla"
}
}
}
}
}
}
// leaflet draw control
// leaflet draw control
drawnItems = L.featureGroup().addTo(map);
let drawControl = new L.Control.Draw({
......
/*
* Copyright 2019 - All rights reserved.
* Rodrigo Tapia-McClung
*
* August-September 2019
*/
/* globals omnivore, Promise, chroma, makeBaseMap, makeIndicatorGraph, getData, getDataInSelection */
/* exported indicators. userFiles, userDates, timeParse, layerControl, updateCharts */
let timeParse,
timeFormat,
timeDimensionControl,
userFiles = [],
monthArray = ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"],
dateArray = [],
dateMin,
dateMax,
minUserDate,
maxUserDate,
userDates,
map,
overlay,
glmap,
osmLayer, cartoLightLayer, cartoDarkLayer,
timeLayer,
layerControl,
scale;
// define empty objects and indicators
let maxIndicators = {},
minIndicators = {},
indicators = ["area", "perimeter", "costa", "df"],
indicatorsNames = ["Área", "Perímetro", "Desarrollo de la línea de costa", "Dimensión fractal"],
indicatorsUnits = ["m\u00B2", "m", "", ""],
indicatorsxAxisFormat = [".2s", ".2s", ".2f", ".2f"],
indicatorVars = {},
cols = [];
// Add options to combo box, and set their chart variables
// chart containers must already exist in index.php
indicators.forEach((indicator, index) => {
// colnames for queries
cols.push(`min(${indicator}) as min${indicator}`);
cols.push(`max(${indicator}) as max${indicator}`);
// initialize min and max objects to hold values for each indicator and add select options
maxIndicators[indicator] = 0;
minIndicators[indicator] = 1e30;
$("#indicatorSelect").append(`<option value="${indicator}"> ${indicatorsNames[index]}</option>`);
// chart variables
indicatorVars[indicator] = {
"chart": `${indicator}Chart`,
"chartaData": `${indicator}Data`,
"container": `#${indicator}-graph`
};
});
indicatorVars.area.explanation = "Muestra la suma del &aacute;rea contenida en los pol&iacute;gonos dentro de la selecci&oacute;n o en toda la regi&oacute;n de estudio.";
indicatorVars.perimeter.explanation = "Muestra la suma del per&iacute;metro de los pol&iacute;gonos dentro de la selecci&oacute;n o en toda la regi&oacute;n de estudio.";
indicatorVars.costa.explanation = "Relaciona el per&iacute;metro de un cuerpo de agua o longitud de l&iacute;nea de costa con el per&iacute;metro de un c&iacute;rculo de igual \
&aacute;rea (A) que el cuerpo. El valor m&iacute;nimo es 1, el cual corresponde a un c&iacute;rculo perfecto, mientras que el valor m&aacute;ximo no tiene l&iacute;mite. \
En varios estudios se han obtenido valores mayores de 20, como en algunos lagos en Finlandia. Los valores cercanos a 1 son indicativos de formas circulares, mientras que \
los valores mayores de 3 son referencia de cuerpos de agua con contornos de formas alargadas. \
<br/><br/> \
Se calcula como \
<p class=\"equation\">DI = <span class=\"frac\"><sup>P</sup><span>&frasl;</span><sub>2&radic;<span style=\"text-decoration: overline;\">&pi; A</span></sub></span>,</p> \
donde P es el per&iacute;metro y A es el &aacute;rea.";
indicatorVars.df.explanation = "La geometr&iacute;a fractal se usa para hacer referencia a objetos demasiado irregulares y que tienen la propiedad de ser invariantes ante \
cambios de escala. En el caso de la hidrolog&iacute;a, este &iacute;ndice nos refiere la sinuosidad del contorno de un cuerpo de agua. La dimensi&oacute;n presenta valores \
reales no negativos entre 1 y 2. Los valores de este &iacute;ndice aumentan conforme el contorno del cuerpo de agua es más sinuoso. El an&aacute;lisis fractal se ha utilizado \
con &eacute;xito para medir y caracterizar rasgos lineales irregulares como las costas. \
<br/></br> \
Se calcula como \
<p class=\"equation\">DF = <span class=\"frac\"><sup>2 ln(P/4)</sup><span>&frasl;</span><sub>ln(A)</sub></span>,</p> \
donde P es el per&iacute;metro y A es el &aacute;rea.";
let currentTiles = {},
allTiles = {};
d3.json("https://unpkg.com/d3-time-format@2.1.1/locale/es-MX.json").then(locale => {
d3.timeFormatDefaultLocale(locale);
timeParse = d3.timeParse("%B_%Y");
timeFormat = d3.timeFormat("%B_%Y");
setupTimeDimensionControl();
setupDates()
.then(dates => populateDates(dates))
.then(userData => setupMap(userData))
.then(map => populateMap(map));
});
const sortInitialDateAscending = (a, b) => {
// Dates will be cast to numbers automagically:
return a - b;
}
// query available dates on DB
const setupDates = () => {
return new Promise(resolve => {
const baseUrl = new URL(`/data`, window.location.href).href;
let layersQuery = `${baseUrl}/list_layers`;
d3.json(layersQuery).then(layers => {
layers.forEach(layer => {
dateArray.push(timeParse(layer.f_table_name)); // convert filenames to dates
})
dateArray = dateArray.sort(sortInitialDateAscending); // order dates
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 populateDates = (dates) => { // fill out date pickers with available dates
return new Promise(resolve => {
$.datepicker.regional["es"] = {
closeText: "Cerrar",
prevText: "&#x3c;Ant",
nextText: "Sig&#x3e;",
currentText: "Hoy",
monthNames: ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio",
"Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"
],
monthNamesShort: ["Ene", "Feb", "Mar", "Abr", "May", "Jun",
"Jul", "Ago", "Sep", "Oct", "Nov", "Dic"
],
dayNames: ["Domingo", "Lunes", "Martes", "Mi&eacute;rcoles", "Jueves", "Viernes", "S&aacute;bado"],
dayNamesShort: ["Dom", "Lun", "Mar", "Mi&eacute;", "Juv", "Vie", "S&aacute;b"],
dayNamesMin: ["Do", "Lu", "Ma", "Mi", "Ju", "Vi", "S&aacute;"],
weekHeader: "Sm",
dateFormat: "yy/mm/dd",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: ""
}
$.datepicker.setDefaults($.datepicker.regional["es"]);
// month pickers
$("#date-initial").datepicker({
minDate: dates.min,
maxDate: dates.max,
defaultDate: dates.min,
changeMonth: true,
changeYear: true,
showButtonPanel: true,
dateFormat: "M yy",
onClose: function() {
let month = $("#ui-datepicker-div .ui-datepicker-month :selected").val();
let year = $("#ui-datepicker-div .ui-datepicker-year :selected").val();
minUserDate = new Date(year, month, 1); // initial date
$(this).datepicker("setDate", minUserDate);
$("#date-final").datepicker("option", {
"minDate": minUserDate,
disabled: false
});
// hack to avoid needing to change date twice in second datepicker
setTimeout(() => { $("#date-final").datepicker("show") }, 10);
},
beforeShow: el => {
$("#ui-datepicker-div").toggleClass("hide-calendar", $(el).is("[data-calendar=\"false\"]"));
}
});
$("#date-final").datepicker({
maxDate: dates.max,
defaultDate: dates.max,
changeMonth: true,
changeYear: true,
showButtonPanel: true,
disabled: true,
dateFormat: "M yy",
onClose: function() {
let month = $("#ui-datepicker-div .ui-datepicker-month :selected").val();
let year = $("#ui-datepicker-div .ui-datepicker-year :selected").val();
maxUserDate = new Date(year, month, 1); // final date
$(this).datepicker("setDate", maxUserDate);
// use .setUTCHours(6,0,0) to adjust DST offset fror some months
let startUserDate = new Date(minUserDate.setUTCHours(6, 0, 0));
let endUserDate = new Date(maxUserDate.setUTCHours(6, 0, 0));
// pass new timeinterval to timeDimension player
userDates = L.TimeDimension.Util.explodeTimeRange(startUserDate, endUserDate, "P1M");
userFiles = userDates.map(month => timeFormat(month)); // order table names by date
// change date selectors from main screen to map
$("#date-initial").detach().appendTo("#datePickers");
$("#date-final").detach().appendTo("#datePickers");
$("#datePickers").css("display", "block");
// hide initial screen and show map
$("#startHeader").remove();
$("#initial-backdrop").remove();
$("#mainContainer")[0].style.setProperty("display", "flex", "important")
$("#mexmap").show();
// When closing final-date, either setup or update map
if (!map) {
resolve({ min: startUserDate, max: endUserDate });
} else {
// FIXME: no need to pass data here?
updateMap({ map: map, min: startUserDate, max: endUserDate });
}
},
beforeShow: (el, inst) => {
inst.input.datepicker("refresh");
$("#ui-datepicker-div").toggleClass("hide-calendar", $(el).is("[data-calendar=\"false\"]"));
}
});
})
}
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.88, -103.61),
northEast = L.latLng(22.63, -98.00),
bounds = L.latLngBounds(southWest, northEast);
map = L.map("mexmap", {
//center: [17.22, -92.28],
minZoom: 7, //8
zoom: 7, //6
attributionControl: false,
timeDimension: true,
timeDimensionOptions: {
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 });
//console.log(userFiles);
// query db to get min/max values per month and indicator and store them in an object
queryFiles().then(minmax => {
minmax.map(minmaxMonth => {
indicators.forEach((indicator) => {
minIndicators[indicator] = Math.min(minIndicators[indicator], minmaxMonth[`min${indicator}`]);
maxIndicators[indicator] = Math.max(maxIndicators[indicator], minmaxMonth[`max${indicator}`]);
});
});
resolve({ "map": map, "minIndicators": minIndicators, "maxIndicators": maxIndicators });
});
});
}
const flatten = arrays => {
return [].concat(arrays);
}
const queryFiles = () => {
return Promise.all(userFiles.map(getMinMax))
// the result is an array of arrays, so we'll flatten them here
.then(flatten);
}
const getMinMax = table => {
return new Promise(resolve => {
const baseUrl = new URL(`/data`, window.location.href).href;
let minmaxQuery = `${baseUrl}/query/${table}?columns=${cols.join(", ")}`;
d3.json(minmaxQuery).then(minmax => {
resolve(minmax[0]);
});
});
}
const populateMap = async(mapData) => {
const chartData = await getData();
// Define charts with reusable components
indicators.map(async(indicator, index) => {
// indicatorVars[indicator].chart gives areaChart, perimeterChart, etc.
// First, make all charts with same general options
indicatorVars[indicator].chart = makeIndicatorGraph()
.width(Math.floor($(indicatorVars[indicator].container)[0].offsetParent.offsetWidth * 0.95))
.height(Math.floor($(indicatorVars[indicator].container)[0].offsetParent.offsetHeight * 0.95))
.lineVariables([`${indicator}0`]) // d.area is used to draw stuff on y axis
.displayName("date") // d.date is used to draw stuff on x axis
.lineAxisLabel("(m)")
.id(indicator)
.xAxisFormat(d3.timeFormat("%b '%y")) // label x axis as mm 'yy
.yAxisFormat(d3.format(indicatorsxAxisFormat[index])) // yAxis label in SI or other
// Then, specify some parameters for each chart
.tooltipUnits(indicatorsUnits[index])
.tooltipFormat(d3.format(indicatorsxAxisFormat[index])) // tooltip format in SI or other
.title(indicatorsUnits[index] == "" ? indicatorsNames[index] :
`${indicatorsNames[index]} (${indicatorsUnits[index]})`);
// Finally, set chart data with async function calling stuff from DB
indicatorVars[indicator].chart.data(chartData[indicator]);
// create chart with passed options
d3.select(indicatorVars[indicator].container)
.call(indicatorVars[indicator].chart);
// Reload chart data and force chart update
indicatorVars[indicator].chartData = indicatorVars[indicator].chart.data(); // get chart data
indicatorVars[indicator].chart.data(indicatorVars[indicator].chartData); // set chart data
// Highlight plot title according to selected option
let option = $("#indicatorSelect").val(); // option selected from dropdrown
d3.select(indicatorVars[option].container).select("svg text.title").classed("active", true);
});
let map = mapData.map,
minIndicators = mapData.minIndicators,
maxIndicators = mapData.maxIndicators;
let cuencaLayer = L.geoJson(null, {
style: {
stroke: true,
weight: 5,
color: "grey",
opacity: .35,
fillOpacity: 0.05
},
interactive: false
});
let cuencaBufferMask = omnivore.topojson("data/buffer_cuenca.topojson", null, cuencaLayer);
cuencaBufferMask.addTo(map);
//map.createPane("wb-Tiles");
//map.getPane("wb-Tiles").style.zIndex = 450;
// create mvt layers
userFiles.forEach(f => {
f = mapboxLayer(f);
});
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", () => {
userFiles.forEach(monthYear => {
glmap.getMapboxMap().addLayer(currentTiles[monthYear]);
});
Object.keys(allTiles).forEach(layer => {
if (layer == userFiles[0]) {
glmap.getMapboxMap().setPaintProperty(layer, "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);
// 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);
makeBaseMap(); // basemap.js
});
}
const updateMap = (mapData) => {
//console.log(userFiles);
// clear tiles
currentTiles = {};
//retrieve or create tiles for current dates
userFiles.forEach(monthYear => {
if (Object.keys(allTiles).includes(monthYear)) {
currentTiles[monthYear] = allTiles[monthYear]; // recover tile if it has already been created
//currentJSONs[monthYear] = allJSONs[monthYear]; // recover json if it has already been created
return; // if file has already been processed, exit
} else { // if file cannot be found in allTiles, then add 1 to the number of files to process
let newTile = mapboxLayer(monthYear);
glmap.getMapboxMap().addLayer(newTile);
if (monthYear == userFiles[0]) {
glmap.getMapboxMap().setPaintProperty(monthYear, "fill-opacity", 0.7)
}
}
});
// update timeDimension times
timeLayer._timeDimension.setAvailableTimes(userDates, "replace");
timeLayer._timeDimension.setCurrentTime(mapData.min);
// clear minmax indicators objects
maxIndicators = {},
minIndicators = {};
indicators.forEach((indicator) => {
maxIndicators[indicator] = 0;
minIndicators[indicator] = 1e30;
});
// query db for new minmax values then style tiles
new Promise(resolve => {
queryFiles().then(minmax => {
minmax.map(minmaxMonth => {
indicators.forEach((indicator) => {
minIndicators[indicator] = Math.min(minIndicators[indicator], minmaxMonth[`min${indicator}`]);
maxIndicators[indicator] = Math.max(maxIndicators[indicator], minmaxMonth[`max${indicator}`]);
});
});
resolve({ "map": map, "minIndicators": minIndicators, "maxIndicators": maxIndicators });
})
}).then(values => { // once we have new minmax values, style all tiles
let option = $("#indicatorSelect").val(), // option selected from dropdrown
min = values.minIndicators,
max = values.maxIndicators;
styleTiles(option, min, max)
.then(legend.addTo(map)); // add legend control -> it updates
});
// Update charts
updateCharts();
}
const updateCharts = async() => {
$(".loader").css("display", "block");
// TODO: add mask while fetching async data
// if user has drawn polygons, update chart with data inside selection
if (drawnItems.toGeoJSON().features.length != 0) {
let allData = await getDataInSelection();
indicators.map(async indicator => {
indicatorVars[indicator].chart.data(summarizeData(allData, indicator));
});
} else {
// otherwise use all data
let allData = await getData();
indicators.map(async indicator => {
indicatorVars[indicator].chart.data(allData[indicator]);
});
}
$(".loader").hide("fade", 750);
}
// define MVT layer for given month table and all indicators
const mapboxLayer = (monthYear) => {
const baseUrl = new URL(`/data`, window.location.href).href;
let pbfLayer = {
id: monthYear,
source: {
type: "vector",
tiles: [`${baseUrl}/${monthYear}/mvt/{z}/{x}/{y}?geom_column=geom&columns=${indicators.join()}`],
maxzoom: 19,
minzoom: 6
},
"source-layer": monthYear,
type: "fill",
"paint": {
"fill-opacity": 0,
/*"fill-color": [
"interpolate",
["linear"],
["get", "df"],
1, "rgba(255, 0, 0, 0.5)", // red
1.3, "rgba(0, 255, 0, 0.5)", // green
]/*,
"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}`;
}
});
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();
// get data for time
let d = new Date(time),
year = d.getFullYear().toString(),
m = d.getUTCMonth(),
month = monthArray[m].toLowerCase(),
monthYear = `${month}_${year}`;
// Update title
let title = $("#title");
title.html(`<h2>Cobertura de agua en la cuenca del r&iacute;o Grijalva en ${month} de ${year}</h2>`);
// 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);
};
// When selecting indicator from dropdown, style tiles.
$("#indicatorSelect").on("change", function() {
// style currentTiles
let option = this.value; // option selected from dropdrown
styleTiles(option, minIndicators, maxIndicators)
.then(legend.addTo(map)); // add legend control -> it updates
// FIXME: re-adding control updates its contents... why?
// Highlight plot title according to selected option
indicators.forEach(indicator => {
d3.select(indicatorVars[indicator].container).select("svg text.title").classed("active", indicator === option ? true : false);
});
});
const styleTiles = (option, minIndicators, maxIndicators) => {
// define color scale domain based on min-max values for selected indicator
let domain = [minIndicators[option], maxIndicators[option]];
//console.log(domain)
scale = chroma.scale("PuBu").padding([0.5, 0]).domain(domain).classes(5);
Object.keys(currentTiles).forEach(layer => {
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);
});
return Promise.resolve(scale);
}
let legend = L.control({
position: "bottomright"
});
legend.onAdd = () => {
let div = L.DomUtil.create("div", "info legend leaflet-bar");
let option = $("#indicatorSelect").val();
let optionIndex = indicators.indexOf(option);
let legendText = indicatorsUnits[optionIndex] == "" ? indicatorsNames[optionIndex] :
`${indicatorsNames[optionIndex]} (${indicatorsUnits[optionIndex]})`;
var html = `<h6>${legendText}</h6><ul>`;
let classes = scale.classes();
classes.forEach((c, idx, array) => {
if (idx != array.length - 1) {
html += `<li><i style="background: ${scale(c).hex()}"></i>${d3.format(",.4~s")(classes[idx])} - ${d3.format(",.4~s")(classes[idx+1])}</li>`;
}
});
html += "</ul>";
div.innerHTML = html;
return div;
};
\ 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