Commit 7e24cf65 authored by Rodrigo Tapia-McClung's avatar Rodrigo Tapia-McClung

Create and update charts with less DB requests

parent 850046ff
......@@ -2,11 +2,11 @@
* Copyright 2019 - All rights reserved.
* Rodrigo Tapia-McClung
*
* August 2019
* August-September 2019
*/
/* globals map, userFiles, userDates, indicators, indicatorVars, indicatorsNames */
/* exported makeIndicatorGraph, getData, getDataInSelection */
/* exported makeIndicatorGraph, getData, updateData, getDataInSelection */
let minDate, maxDate, xLine, yLine;
......@@ -16,145 +16,80 @@ const sortByDateAscending = (a, b) => {
}
//TODO: scale data to appropriate units? m2-> ha? m -> km?
const getData = async (indicator) => {
//let data = [{name: `${indicator}0`, values: []}];
let timeParse = d3.timeParse("%B_%Y");
//timeFormat = d3.timeFormat("%B %Y");
const promises = userFiles.map( async monthYear => {
// get sum of area/perimeter or average of coast/fractal dimension
let queryDB = indicator == "costa" || indicator == "df" ?
`http://localhost:8090/data/query/${monthYear}?columns=avg(${indicator})%20as%20${indicator}` :
`http://localhost:8090/data/query/${monthYear}?columns=sum(${indicator})%20as%20${indicator}`;
const getData = async () => {
let queryColumns = ["sum(area) as area", "sum(perimeter) as perimeter", "avg(costa) as costa", "avg(df) as df"],
timeParse = d3.timeParse("%B_%Y"),
data = {};
indicators.map( indicator => {
data[indicator] = [];
data[indicator].push({name: `${indicator}0`, values: []});
});
const userFilePromises = userFiles.map( async monthYear => {
let queryDB = `http://localhost:8090/data/query/${monthYear}?columns=${queryColumns.join(",")}`;
const dbData = await d3.json(queryDB);
return {date: timeParse(monthYear), value: dbData[0]};
});
const chartData = await Promise.all(promises);
let data = [{name: `${indicator}0`, values: []}];
chartData.map( monthYear => {
data[0].values.push({
"date": monthYear.date,
"value": +monthYear.value[indicator]
const chartData = await Promise.all(userFilePromises);
chartData.map( monthData => {
indicators.map( indicator => {
data[indicator][0].values.push({
"date": monthData.date,
"value": +monthData.value[indicator]
});
// order data[i].values.date
data[indicator].forEach(d => d.values.sort(sortByDateAscending));
});
});
data.forEach(d => d.values.sort(sortByDateAscending));
return data;
}
// FIXME: Improve this function. Too many requests: indicators x months x drawn items...
// Find way to make one request and get all indicators per month and drawn item.
// Something like the commented function below.
const getDataInSelection = async (indicator) => {
let timeParse = d3.timeParse("%B_%Y");
const getDataInSelection = async () => {
let geojson = drawnItems.toGeoJSON(),
data = [];
drawnItemsPromises = geojson.features.map( async (item, i) => {
// set SRID for drawn features
item.geometry.crs = {"type":"name","properties":{"name":"EPSG:4326"}};
data.push({name: indicator + (i + 1), values: []});
let columns = indicator == "costa" || indicator == "df" ?
`avg(${indicator})%20as%20${indicator}` :`sum(${indicator})%20as%20${indicator}`;
let filter = `ST_Intersects(geom, (SELECT ST_Multi(ST_GeomFromGeoJSON('${JSON.stringify(item.geometry)}') ) ) )`
const filePpromises = userFiles.map(async monthYear => {
let queryData = `http://localhost:8090/data/query/${monthYear}?columns=${columns}&filter=${filter}&group=1%3D1`;
const selectionQuery = await d3.json(queryData);
return {date: timeParse(monthYear), value: selectionQuery[0]};
});
const selectionData = await Promise.all(filePpromises);
selectionData.map( itemData => {
let itemDataValue = itemData.value != undefined ? itemData.value[indicator] : 0;
data[i].values.push({date: itemData.date, value: itemDataValue });
});
return data;
});
const drawnItemsData = await Promise.all(drawnItemsPromises);
let data2 = [];
data.map( a => data2.push(a.values)); // flatten array of data[i].values
data2 = [].concat.apply([], data2);
// insert sum of each date to first element of data array with name "column0"
data.unshift({
"name": `${indicator}0`,
"values": d3.nest()
.key( d => d.date)
// return d3.sum or d3.average depending on indicator
.rollup( v => { return indicator == "costa" || indicator == "df" ? d3.mean(v, v => v.value) : d3.sum(v, v => v.value) })
.entries(data2)
// map "key" and "value" from d3.nest().rollup() to date and value
.map( group => {
return {
date: new Date(group.key),
value: group.value
}
})
});
// order data[i].values.date
data.forEach(d => d.values.sort(sortByDateAscending));
return data;
}
/*const getDataInSelection = async () => {
let geojson = drawnItems.toGeoJSON();
let queryColumns = ["sum(area) as area", "sum(perimeter) as perimeter", "avg(costa) as costa", "avg(df) as df"];
let timeParse = d3.timeParse("%B_%Y"),
queryColumns = ["sum(area) as area", "sum(perimeter) as perimeter", "avg(costa) as costa", "avg(df) as df"],
timeParse = d3.timeParse("%B_%Y"),
data = {};
indicators.map( indicator => {
data[indicator] = [];
});
//let promises;
// End up with data = {area: Array(0), perimeter: Array(0), length: Array(0), DI: Array(0)}
// Convert drawn items to GeoJSON and check what's inside each one of them
drawnItems.toGeoJSON().features.map( async (item, i) => {
const drawnItemPromises = drawnItems.toGeoJSON().features.map( async (item, i) => {
// set SRID fro drawn features
geojson.features[i].geometry.crs = {"type":"name","properties":{"name":"EPSG:4326"}};
indicators.map( async indicator => {
data[indicator].push({name: indicator + (i + 1), values: []});
});
// End up with data = {area: [{name: "area1", values: []}], perimeter: [{name: "perimeter1", values: []}]...}
//console.log(data)
let filter = `ST_Intersects(geom, (SELECT ST_Multi(ST_GeomFromGeoJSON('${JSON.stringify(geojson.features[i].geometry)}') ) ) )`
const promises = userFiles.map(async monthYear => {
const userFilePromises = userFiles.map(async monthYear => {
let queryData = `http://localhost:8090/data/query/${monthYear}?columns=${queryColumns.join(",")}&filter=${filter}&group=1%3D1`;
const selectionQuery = await d3.json(queryData);
return {date: timeParse(monthYear), value: selectionQuery[0]};
});
const selectionData = await Promise.all(promises);
// let data = [{name: `${indicator}0`, values: []}];
const selectionData = await Promise.all(userFilePromises);
selectionData.map( itemData => {
//console.log(itemData, i)
indicators.map( indicator => {
data[indicator][i].values.push({date: itemData.date, value: itemData.value[indicator] });
let itemDataValue = itemData.value != undefined ? itemData.value[indicator] : 0;
data[indicator][i].values.push({date: itemData.date, value: itemDataValue });
});
});
});
const drawnItemsData = await Promise.all(drawnItemPromises);
return data;
}*/
}
const summarizeData = async (allData, indicator) => {
/*let dataFull = await allData;
const summarizeData = (allData, indicator) => {
let dataFull = allData;
let data = dataFull[indicator];
let data2 = [];
console.log(allData, data[0]);
data.map( a => { console.log(a); data2.push(a.values)}); // flatten array of data[i].values
data.map( a => data2.push(a.values)); // flatten array of data[i].values
data2 = [].concat.apply([], data2);
console.log(data2);*/
allData.then( val => {
let data = val['area'];
let data2 = [];
console.log(val, data);
data.forEach( a => { data2.push(a.values)}); // flatten array of data[i].values
data2 = [].concat.apply([], data2);
console.log(data2);
})
/*// insert sum of each date to first element of data array with name "column0"
// insert sum of each date to first element of data array with name "column0"
data.unshift({
"name": `${indicator}0`,
"values": d3.nest()
......@@ -171,7 +106,7 @@ const summarizeData = async (allData, indicator) => {
});
// order data[i].values.date
data.forEach(d => d.values.sort(sortByDateAscending));
//console.log(data)*/
console.log(data)
return data;
}
......
......@@ -2,7 +2,7 @@
* Copyright 2019 - All rights reserved.
* Rodrigo Tapia-McClung
*
* August 2019
* August-September 2019
*/
/* globals omnivore, Promise, chroma, makeBaseMap, makeIndicatorGraph, getData, getDataInSelection */
......@@ -338,7 +338,44 @@ const getMinMax = table => {
});
}
const populateMap = (mapData) => {
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;
......@@ -411,39 +448,6 @@ const populateMap = (mapData) => {
layerControl = L.control.layers(baseLayers, overlays).addTo(map);
makeBaseMap(); // basemap.js
});
// 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( await getData(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);
});
}
const updateMap = (mapData) => {
......@@ -500,19 +504,20 @@ const updateMap = (mapData) => {
updateCharts();
}
const updateCharts = () => {
const updateCharts = async () => {
// 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( await summarizeData(await getDataInSelection(), indicator) );
indicatorVars[indicator].chart.data( await getDataInSelection(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( await getData(indicator));
indicatorVars[indicator].chart.data( allData[indicator]);
});
}
}
......
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