Commit 4374ac3e authored by Rodrigo Tapia-McClung's avatar Rodrigo Tapia-McClung

Read original data and transpose. Use geojson instead of mbtiles

parent b4935b0b
This diff is collapsed.
Catalogo de estaciones,,,,,,
cve_estac,nom_estac,longitud,latitud,alt,obs_estac,id_station
ACO,Acolman,-98.912003,19.635501,2198,,484150020109
AJU,Ajusco,-99.162611,19.154286,2942,,484090120400
AJM,Ajusco Medio,-99.207744,19.272161,2548,,484090120609
ARA,Aragón,-99.074549,19.470218,2200,Finalizó operación en 2010,484090050301
ATI,Atizapan,-99.254133,19.576963,2341,,484150130101
AZC,Azcapotzalco,-99.198657,19.487728,2279,Finalizó operación en 2010,484090020201
BJU,Benito Juárez,-99.159596,19.370464,2249,Finalizó operación en 2005,484090140201
CAM,Camarones,-99.169794,19.468404,2233,,484090020301
CCA,Centro de Ciencias de la Atmósfera,-99.176111,19.326111,2294,,484090030501
CES,Cerro de la Estrella,-99.074678,19.334731,2219,Finalizó operación en 2010,484090070111
CFE,Museo Tecnológico de la CFE,-99.194279,19.414393,2287,Finalizó operación en 1996,484090160102
CHO,Chalco,-98.886088,19.266948,2253,,484150250109
COR,CORENA,-99.02604,19.265346,2242,,484090130204
COY,Coyoacán,-99.157101,19.350258,2260,,484090030303
CUA,Cuajimalpa,-99.291705,19.365313,2704,,484090040109
CUI,Cuitláhuac,-99.165849,19.469859,2255,Finalizó operación en 1993,484090020101
CUT,Cuautitlán,-99.198602,19.722186,2263,,484150950109
DIC,Diconsa,-99.185774,19.298819,2305,,484090120304
EAJ,Ecoguardas Ajusco,-99.203971,19.271222,2584,,484090120404
EDL,Exconv. Desierto Leones,-99.310635,19.313357,2980,,484090040204
FAC,FES Acatlán,-99.243524,19.482473,2299,,484150570109
FAN,Felipe Ángeles,-99.17492,19.299126,2279,Finalizó operación en 1996,484090120102
GAM,Gustavo A. Madero,-99.094517,19.4827,2227,,484090050809
HAN,Hangares,-99.083623,19.420518,2235,Finalizó operación en 2006,484090170209
HGM,Hospital General de México,-99.152207,19.411617,2234,,484090150409
IBM,Legaria,-99.21536,19.443319,2314,,484090160504
IMP,Inst. Mexicano del Petróleo,-99.147294,19.487561,2250,Finalizó operación en 2010,484090050209
INN,Investigaciones Nucleares,-99.38052,19.291968,3082,,484150620109
IZT,Iztacalco,-99.117641,19.384413,2238,,484090060101
LAA,Lab. de Analisis Ambiental,-99.147312,19.483781,2255,,484090050604
LAG,Lagunilla,-99.135183,19.44242,2223,Finalizó operación en 2010,484090150101
LLA,Los Laureles,-99.039644,19.578792,2230,,484150330201
LOM,Lomas,-99.242062,19.403,2434,,484090160406
LPR,La Presa,-99.11772,19.534727,2302,,484151040203
LVI,La Villa,-99.117749,19.46789,2228,Finalizó operación en 2010,484090050101
MCM,Museo de la Cd. de México,-99.131924,19.429071,2237,,484090150304
MER,Merced,-99.119594,19.42461,2245,,484090170127
MGH,Mguel Hidalgo,-99.20266,19.40405,2327,,484090160609
MIN,Metro Insurgentes,-99.162885,19.42144,2231,Finalizó operación en 2007,484090150201
MON,Montecillo,-98.902853,19.460415,2252,,484150990113
MPA,Milpa Alta,-98.990189,19.1769,2594,,484090090104
NET,Netzahualcoyotl,-99.026119,19.42115,2230,Finalizó operación en 2000,484150580201
NEZ,Nezahualcóyotl,-99.028212,19.393734,2235,,484150580115
PED,Pedregal,-99.204136,19.325146,2326,,484090100127
PER,La Perla,-98.991858,19.38286,2237,Finalizó operación en 2011,484150580303
PLA,Plateros,-99.200109,19.365869,2345,Finalizó operación en 2010,484090100209
POT,Portales,-99.145766,19.376494,2237,Finalizó operación en 1996,484090140102
SAG,San Agustín,-99.030324,19.532968,2241,,484150330327
SFE,Santa fe,-99.262865,19.357357,2599,,484090040309
SHA,Secretaría de Hacienda,-99.207868,19.446203,2272,,484090160202
SJA,San Juan Aragón,-99.086095,19.452592,2258,,484090050701
SNT,San Nicolas Totolapan,-99.256462,19.250385,2946,,484090080104
SUR,Santa Ursula,-99.149994,19.31448,2279,,484090030109
TAC,Tacuba,-99.202455,19.453907,2275,Finalizó operación en 2010,484090160309
TAH,Tlahuac,-99.010564,19.246459,2297,,484090130309
TAX,Taxqueña,-99.123204,19.335689,2242,Finalizó operación en 2010,484090030201
TEC,Cerro del Tepeyac,-99.114229,19.487227,2265,,484090050404
TLA,Tlalnepantla,-99.204597,19.529077,2311,,484151040115
TLI,Tultitlán,-99.177173,19.602542,2313,,484151090101
TPN,Tlalpan,-99.184177,19.257041,2522,,484090120209
UAX,UAM Xochimilco,-99.103629,19.304441,2246,,484090030401
UIZ,UAM Iztapalapa,-99.07388,19.360794,2221,,484090070219
UNM,Unidad Movil,-99.147137,19.482238,,,484090000099
VAL,Vallejo,-99.165702,19.522437,2248,Finalizó operación en 2010,484090050501
VIF,Villa de las Flores,-99.09659,19.658223,2242,,484150200109
XAL,Xalostoc,-99.0824,19.525995,2160,,484150330415
XCH,Xochimilco,-99.118252,19.267066,2243,Finalizó operación en 1999,484090130102
FAR,FES Aragón,-99.046176,19.473692,2230,,484800150584
SAC,Santiago Acahualtepec,-99.009381,19.34561,2293,,484800090073
This diff is collapsed.
This diff is collapsed.
...@@ -2,10 +2,10 @@ ...@@ -2,10 +2,10 @@
* Copyright 2021 - All rights reserved. * Copyright 2021 - All rights reserved.
* Rodrigo Tapia-McClung * Rodrigo Tapia-McClung
* *
* February-June 2021 * February-July 2021
*/ */
/* global mapboxgl, turf */ /* global mapboxgl, turf, Papa */
const windowUrl = new URL(window.location.href); const windowUrl = new URL(window.location.href);
const baseUrl = windowUrl.hostname == "localhost" ? const baseUrl = windowUrl.hostname == "localhost" ?
...@@ -20,14 +20,36 @@ let cdmx, estaciones, ozono, fechas, ...@@ -20,14 +20,36 @@ let cdmx, estaciones, ozono, fechas,
playing = false, playing = false,
animate; animate;
// Read CDMX boundaries
fetch(`${baseUrl}/data/cdmx.geojson`) fetch(`${baseUrl}/data/cdmx.geojson`)
.then(response => response.json()) .then(response => response.json())
.then(d => cdmx = d); .then(d => cdmx = d);
fetch(`${baseUrl}/data/estaciones.geojson`) // Read and parse csv station data
.then(response => response.json()) fetch(`${baseUrl}/data/cat_estacion.csv`)
.then(d => estaciones = d); .then(response => response.arrayBuffer())
.then(d => new TextDecoder("iso-8859-1").decode(d).split("\r\n"))
.then(d => { // arrange elements as a decent array
let array = [];
d.forEach( d => array.push(d.split(",")));
let array2 = array.slice(1, -1); // remove spurious first and last line
const [keys, ...values] = array2; // deconstruct
let estacionesCSV = values.map( array2 => // create useful object of key:value pairs for each station
array2.reduce((a, v, i) => ({ ...a, [keys[i]]: v }), {})
);
let estacionesArray = [];
estacionesCSV.forEach( e => { // create features for feature collection
let location = turf.point([+e.longitud, +e.latitud], {
cve_estac: e.cve_estac,
nom_estac: e.nom_estac,
longitud: +e.longitud,
latitud: +e.latitud,
alt: +e.alt
});
estacionesArray.push(location);
});
estaciones = turf.featureCollection(estacionesArray);
});
const papaPromise = url => { const papaPromise = url => {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
...@@ -36,19 +58,14 @@ const papaPromise = url => { ...@@ -36,19 +58,14 @@ const papaPromise = url => {
header: true, header: true,
skipEmptyLines: true, skipEmptyLines: true,
complete: resolve, complete: resolve,
error: reject,
encoding: "UTF8"
}); });
}); });
} }
let ozonoPromise = papaPromise(`${baseUrl}/data/ozono.csv`); // ozono es en ppb: 10^12 // read ozono csv data
// FIXME: check and fix date order in data let ozonoPromise = papaPromise(`${baseUrl}/data/O3.csv`); // ozono es en ppb: 10^12
/*const getOzono = fetch(`${baseUrl}/ozono.csv`)
.then(response => response.text())
.then(d => Papa.parse(d))
.catch(err => console.log(err))
getOzono.then(d => ozono = d.data);
*/
let map = new mapboxgl.Map({ let map = new mapboxgl.Map({
"container": "mexmap", "container": "mexmap",
...@@ -61,14 +78,13 @@ let map = new mapboxgl.Map({ ...@@ -61,14 +78,13 @@ let map = new mapboxgl.Map({
}); });
map.addControl(new mapboxgl.NavigationControl()); map.addControl(new mapboxgl.NavigationControl());
// map language - doesn't quite work // map language - doesn't quite work
/*map.addControl(new MapboxLanguage({ /*map.addControl(new MapboxLanguage({
defaultLanguage: 'es' defaultLanguage: 'es'
}));*/ }));*/
// TODO: display more friendly dates // TODO: display more friendly dates
const dateTimeOptions = { /*const dateTimeOptions = {
weekday: "short", weekday: "short",
day: "numeric", day: "numeric",
month:"short", month:"short",
...@@ -79,11 +95,33 @@ const dateTimeOptions = { ...@@ -79,11 +95,33 @@ const dateTimeOptions = {
//minute: '2-digit', //minute: '2-digit',
hour12: false, hour12: false,
timeZone: 'America/Mexico_City' timeZone: 'America/Mexico_City'
}; };*/
//let myDate = "1577919600000"; //let myDate = "1577919600000";
//let myDate = "1605218400000"; //let myDate = "1605218400000";
const transpose = values => {
if (!Array.isArray(values))
throw new Error("`values` must be an array");
if (values.length === 0)
return {};
const keys = Object.keys(values[0]);
const transposed = {
data: {},
count: values.length,
};
keys.forEach(key => {
transposed.data[key] = [];
values.forEach(value => {
transposed.data[key].push(value[key]);
});
});
return transposed;
}
const range = (start, stop, step = 1) => const range = (start, stop, step = 1) =>
Array(Math.ceil((stop - start) / step)).fill(start).map((x, y) => x + y * step); Array(Math.ceil((stop - start) / step)).fill(start).map((x, y) => x + y * step);
...@@ -101,7 +139,6 @@ const intersect = (fc1, fc2) => { ...@@ -101,7 +139,6 @@ const intersect = (fc1, fc2) => {
} }
const makeSurface = date => { const makeSurface = date => {
//map.getSource("estaciones").serialize().data
// filter data for given date value // filter data for given date value
let subdata = ozono.map(d => { return {time: d["time"], [date]: d[date]} }); let subdata = ozono.map(d => { return {time: d["time"], [date]: d[date]} });
let maxValue = Math.max.apply(Math, subdata.map( d=> d[date])); let maxValue = Math.max.apply(Math, subdata.map( d=> d[date]));
...@@ -109,12 +146,13 @@ const makeSurface = date => { ...@@ -109,12 +146,13 @@ const makeSurface = date => {
turf.featureEach(estaciones, point => { turf.featureEach(estaciones, point => {
let stationData = subdata.filter( i => i.time == point.properties.cve_estac); let stationData = subdata.filter( i => i.time == point.properties.cve_estac);
point.properties.calidad = stationData[0] && parseFloat(stationData[0][date]) != -99 ? parseFloat(stationData[0][date]) : 0; point.properties.calidad = stationData[0] && parseFloat(stationData[0][date]) != -99 ? parseFloat(stationData[0][date]) : 0;
// TODO: remove station instead of assigning value to 0 when value = -99?
// use value to style station dot // use value to style station dot
map.setFeatureState( map.setFeatureState(
{ {
// source tileset and source layer // source tileset and source layer: sourceLayer is needed only if using mbtiles
source: "estaciones", source: "estaciones",
sourceLayer: "estaciones", //sourceLayer: "estaciones",
// unique ID row name // unique ID row name
id: point.properties.cve_estac id: point.properties.cve_estac
}, },
...@@ -129,7 +167,7 @@ const makeSurface = date => { ...@@ -129,7 +167,7 @@ const makeSurface = date => {
let options = {gridType: "point", property: "calidad", units: "kilometers", weight: 2}; let options = {gridType: "point", property: "calidad", units: "kilometers", weight: 2};
let grid = turf.interpolate(estaciones, 0.75, options); let grid = turf.interpolate(estaciones, 0.75, options);
let breaks = range(0, maxValue != -99 ? maxValue : 0 + 1, 5); let breaks = range(0, maxValue != -99 ? maxValue : 1, 5);
//let lines = turf.isolines(grid, breaks, {zProperty: "calidad"}); //let lines = turf.isolines(grid, breaks, {zProperty: "calidad"});
let bValues = breaks.map( b => { return {value: b} }); let bValues = breaks.map( b => { return {value: b} });
let bands = turf.isobands(grid, breaks, {zProperty: "calidad", breaksProperties: bValues}); let bands = turf.isobands(grid, breaks, {zProperty: "calidad", breaksProperties: bValues});
...@@ -157,10 +195,17 @@ const run = () => { ...@@ -157,10 +195,17 @@ const run = () => {
map.on("style.load", async () => { map.on("style.load", async () => {
map.addSource("estaciones", { // add vector tile source
/*map.addSource("estaciones", {
"type": "vector", "type": "vector",
"tiles": [`${baseUrl}/estaciones/mbtiles/{z}/{x}/{y}.pbf`], "tiles": [`${baseUrl}/estaciones/mbtiles/{z}/{x}/{y}.pbf`],
promoteId: "cve_estac" promoteId: "cve_estac"
});*/
// or geojson source
map.addSource("estaciones", {
type: "geojson",
data: estaciones,
promoteId: "cve_estac"
}); });
map.addSource("interpolation", { map.addSource("interpolation", {
...@@ -190,9 +235,26 @@ map.on("style.load", async () => { ...@@ -190,9 +235,26 @@ map.on("style.load", async () => {
clearInterval(animate); clearInterval(animate);
} }
}); });
// after reading ozono csv
ozonoPromise.then( results => { ozonoPromise.then( results => {
ozono = results.data; let data = results.data;
fechas = Object.keys(ozono[0]).slice(1,-1).map(d => parseInt(d)); data.forEach( d => d.time = Date.parse(new Date(`${d.FECHA} ${d.HORA}:0:0`))); // calculate new time
let transposed = transpose(data).data; // transpose data
// TODO: make everything without transposing data?
let stations = Object.keys(transposed).filter(
s => s != "FECHA" && s != "HORA" && s != "time" // ignore unwanted keys
);
ozono = [];
stations.forEach( station => { // create appropriate object structure
let obj = {};
obj["time"] = station;
transposed.time.forEach((time, index) => {
obj[time] = transposed[station][index];
});
ozono.push(obj);
});
fechas = Object.keys(ozono[0]).slice(1, -1).map(d => parseInt(d));
slider.min = fechas[0]; slider.min = fechas[0];
slider.max = fechas[fechas.length-1]; slider.max = fechas[fechas.length-1];
slider.step = 3600000; slider.step = 3600000;
...@@ -203,20 +265,6 @@ map.on("style.load", async () => { ...@@ -203,20 +265,6 @@ map.on("style.load", async () => {
let maxValue = Math.max(...ozono.map( d => Object.values(d)).flat().filter(val => !isNaN(val))); let maxValue = Math.max(...ozono.map( d => Object.values(d)).flat().filter(val => !isNaN(val)));
/*let expression = ["match", ["get", "cve_estac"]]; /*let expression = ["match", ["get", "cve_estac"]];
results.data.forEach( row => { results.data.forEach( row => {
map.setFeatureState(
{
// source tileset and source layer
source: "estaciones",
sourceLayer: "estaciones",
// unique ID row name
id: row.time
},
// Add rows you want to style/interact with
{
calidad: parseFloat(row[fechas[0]]) != -99 ? parseFloat(row[fechas[0]]) : 0
//candidate: row.candidate,
}
);
var green = (parseFloat(row[myDate]) / maxValue) * 255; var green = (parseFloat(row[myDate]) / maxValue) * 255;
var color = "rgba(" + 0 + ", " + green + ", " + 0 + ", 1)"; var color = "rgba(" + 0 + ", " + green + ", " + 0 + ", 1)";
expression.push(row["time"], color); expression.push(row["time"], color);
...@@ -242,7 +290,7 @@ map.on("style.load", async () => { ...@@ -242,7 +290,7 @@ map.on("style.load", async () => {
"id": "estaciones-circle", "id": "estaciones-circle",
"type": "circle", "type": "circle",
"source": "estaciones", "source": "estaciones",
"source-layer": "estaciones", //"source-layer": "estaciones",
"layout": {}, "layout": {},
"paint": { "paint": {
"circle-radius": 4, "circle-radius": 4,
...@@ -359,6 +407,7 @@ map.on("style.load", async () => { ...@@ -359,6 +407,7 @@ map.on("style.load", async () => {
.setHTML("Estación: " + estacion + "<br/>Clave: " + cve_estac + "<br/>Valor: " + calidad) .setHTML("Estación: " + estacion + "<br/>Clave: " + cve_estac + "<br/>Valor: " + calidad)
.addTo(map); .addTo(map);
}); });
// TODO: update popup value when already hovered on
// Remove popup on mouse out // Remove popup on mouse out
map.on("mouseleave", "estaciones-circle", () => { map.on("mouseleave", "estaciones-circle", () => {
......
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