Commit 28de0359 authored by Tania Gómez's avatar Tania Gómez

Avances en del proyeto de Omar

parent c7e76d28
......@@ -6,15 +6,20 @@ const cors = require('cors');
const app = express();
// parse 'numeric' type from postgres as float in JS
var types = require("pg").types;
types.setTypeParser(1700, val => {
return parseFloat(val);
});
//app.use(logger('dev'));
app.use(logger('combined', {
skip: function (req, res) { return res.statusCode < 400 }
skip: function(req, res) { return res.statusCode < 400 }
}));
app.use(cors());
app.use('/', express.static(__dirname + '/public'));
const {Pool} = require('pg');
const { Pool } = require('pg');
const dbconfig = require('./config/dbconfig.json');
const readOnlyPool = new Pool(dbconfig);
readOnlyPool.connect();
......@@ -23,6 +28,7 @@ const DirCache = require('./utils/dircache.js')
const cache = new DirCache(`./cache/${dbconfig.database?dbconfig.database:process.env.PGDATABASE?process.env.PGDATABASE:''}`);
const mvt = require('./mvt.js')(app, readOnlyPool, cache);
const geojson = require('./geojson.js')(app, readOnlyPool);
const geobuf = require('./geobuf.js')(app, readOnlyPool);
const listLayers = require('./list_layers.js')(app, readOnlyPool);
......@@ -33,4 +39,4 @@ server.setTimeout(600000);
console.log(`pgserver listening on port ${pgserverconfig.port}`);
module.exports = app;
module.exports = app;
\ No newline at end of file
......@@ -26,20 +26,16 @@
<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">Contenido de agua en la cuenca del r&iacute;o Grijalva</h1>
<p class="lead">Explora la apariencia de la cuenca del r&iacute;o Grijalva a trav&eacute;s del tiempo
por medio de la visualizaci&oacute;n de cuerpos de agua y
algunos indicadores obtenidos del an&aacute;lisis de im&aacute;genes de radar de Sentinel-1.</p>
<p class="lead">Explora la apariencia de la cuenca del r&iacute;o Grijalva a trav&eacute;s del tiempo por medio de la visualizaci&oacute;n de cuerpos de agua y algunos indicadores obtenidos del an&aacute;lisis de im&aacute;genes de radar de Sentinel-1.</p>
<hr class="my-4">
<p>Escoge las fechas para las que quieres explorar el contenido de agua y sus 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" />
<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" />
<input type="text" name="date-final" id="date-final" readonly="readonly" size="12" placeholder="Fecha final" data-calendar="true" />
</div>
</div>
</div>
......@@ -67,7 +63,7 @@
<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 id="basemap-fileLoad"></span>
</span>
</div>
</div>
......@@ -76,22 +72,24 @@
<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 id="areacpo-graph"></div>
<div class="col-6 border-right" id="radial_chart">
<div id="radial_chart2"></div>
</div>
<div class="col-6">
<div id="perimcpo-graph"></div>
<div class="col-6" id="linesChart">
<div id="linesChart2"></div>
</div>
</div>
<div class="row h-50">
<div class="col-6 border-right">
<div id="dlccpo-graph"></div>
<div class="col-6 border-right" id="stackedAreaChart">
<div id="dlccpo-graph2"></div>
</div>
<div class="col-6">
<div id="dimfrcpo-graph"></div>
<div class="col-6" id="dimfrcpo-graph">
<div id="dimfrcpo-graph2"></div>
</div>
</div>
</div>
</div>
</div>
......@@ -116,8 +114,7 @@
</div>
<!-- Modal -->
<div class="modal fade" id="explainIndicatorModal" tabindex="-1" role="dialog"
aria-labelledby="explainIndicatorModal" aria-hidden="true">
<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">
......@@ -150,9 +147,16 @@
<script src="../js/Leaflet.Sync.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.js"></script>
<script src="../js/jszip.min.js"></script>
<!-- amcharts -->
<script src="https://www.amcharts.com/lib/4/core.js"></script>
<script src="https://www.amcharts.com/lib/4/charts.js"></script>
<script src="https://www.amcharts.com/lib/4/themes/animated.js"></script>
<script src="../js/cuerpos_functions.js"></script>
<script src="../js/cuerpos_basemap.js"></script>
<script src="../js/cuerpos_charts.js"></script>
</body>
</html>
\ No newline at end of file
......@@ -8,6 +8,7 @@
/* globals map, userFiles, userDates, indicators, indicatorVars, indicatorsNames */
/* exported makeIndicatorGraph, getData, updateData, getDataInSelection */
let mainTextColor = getComputedStyle(document.body).getPropertyValue('--main-text-color');
let minDate, maxDate, xLine, yLine;
const sortByDateAscending = (a, b) => {
......@@ -16,26 +17,26 @@ const sortByDateAscending = (a, b) => {
}
//TODO: scale data to appropriate units? m2-> ha? m -> km?
const getData = async () => {
const getData = async() => {
let queryColumns = ["sum(areacpo) as area", "sum(perimcpo) as perimeter", "avg(dlccpo) as costa", "avg(dimfrcpo) as df"],
timeParse = d3.timeParse("cuerpos_%B_%Y"),
data = {};
indicators.map( indicator => {
indicators.map(indicator => {
data[indicator] = [];
data[indicator].push({name: `${indicator}0`, values: []});
data[indicator].push({ name: `${indicator}0`, values: [] });
});
const baseUrl = new URL(`/data`, window.location.href).href;
const userFilePromises = userFiles.map( async monthYear => {
const userFilePromises = userFiles.map(async monthYear => {
let queryDB = `${baseUrl}/query/${monthYear}?columns=${queryColumns.join(",")}`;
const dbData = await d3.json(queryDB);
return {date: timeParse(monthYear), value: dbData[0]};
return { date: timeParse(monthYear), value: dbData[0] };
});
const chartData = await Promise.all(userFilePromises);
chartData.map( monthData => {
indicators.map( indicator => {
chartData.map(monthData => {
indicators.map(indicator => {
data[indicator][0].values.push({
"date": monthData.date,
"value": +monthData.value[indicator]
......@@ -47,24 +48,24 @@ const getData = async () => {
return data;
}
const getDataInSelection = async () => {
const getDataInSelection = async() => {
let geojson = drawnItems.toGeoJSON(),
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 => {
indicators.map(indicator => {
data[indicator] = [];
});
const baseUrl = new URL(`/data`, window.location.href).href;
// Convert drawn items to GeoJSON and check what's inside each one of them
const drawnItemPromises = 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: []});
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: []}]...}
......@@ -72,13 +73,13 @@ const getDataInSelection = async () => {
const userFilePromises = userFiles.map(async monthYear => {
let queryData = `${baseUrl}/query/${monthYear}?columns=${queryColumns.join(",")}&filter=${filter}&group=1%3D1`;
const selectionQuery = await d3.json(queryData);
return {date: timeParse(monthYear), value: selectionQuery[0]};
return { date: timeParse(monthYear), value: selectionQuery[0] };
});
const selectionData = await Promise.all(userFilePromises);
selectionData.map( itemData => {
indicators.map( indicator => {
selectionData.map(itemData => {
indicators.map(indicator => {
let itemDataValue = itemData.value != undefined ? itemData.value[indicator] : 0;
data[indicator][i].values.push({date: itemData.date, value: itemDataValue });
data[indicator][i].values.push({ date: itemData.date, value: itemDataValue });
});
});
});
......@@ -90,18 +91,18 @@ const summarizeData = (allData, indicator) => {
let dataFull = allData;
let data = dataFull[indicator];
let data2 = [];
data.map( 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);
// 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)
.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) })
.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 => {
.map(group => {
return {
date: new Date(group.key),
value: group.value
......@@ -119,7 +120,7 @@ const makeIndicatorGraph = () => {
width = 450,
height = 400,
padding = 30,
margin = {top: 5, right: 10, bottom: 100, left: 35},
margin = { top: 5, right: 10, bottom: 100, left: 35 },
id, // variable in data to use as identifier
lineVariables = null, // list of variables to display as lines
displayName, // variable in data to use as x axis labels
......@@ -153,7 +154,7 @@ const makeIndicatorGraph = () => {
});
maxDate = d3.timeDay.offset(maxDate, 15) // get next month to get correct x-axis alignment
selection.each( () => {
selection.each(() => {
// add graph svg to selected container on webpage
let areaSVG = selection
......@@ -637,3 +638,530 @@ const makeIndicatorGraph = () => {
return chart;
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
function grijalva_theme(target) {
if (target instanceof am4core.ColorSet) {
target.list = [
am4core.color("#00c5ff"),
am4core.color("#ff5500"),
am4core.color("#98e600"),
//am4core.color("#ff5500"), //,
//am4core.color("#343d56")
//am4core.color("#232555"),
//am4core.color("#64297B"),
];
}
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ RADAR- LINES
am4core.ready(function() {
// Themes begin
am4core.useTheme(am4themes_animated);
let mainTextColor = getComputedStyle(document.body).getPropertyValue('--main-text-color');
// Themes end
/* Create chart instance */
var chart = am4core.create("radial_chart", am4charts.RadarChart);
var data = [];
/* var value1 = 500;
var value2 = 600;
var value3 = 300;
for (var i = 0; i < 12; i++) {
let date = new Date();
date.setMonth(i, 1);
value1 -= Math.round((Math.random() < 0.5 ? 1 : -1) * Math.random() * 70);
value2 -= Math.round((Math.random() < 0.5 ? 1 : -1) * Math.random() * 50);
value3 -= Math.round((Math.random() < 0.5 ? 1 : -1) * Math.random() * 20);
data.push({ date: date, value1: value1, value2: value2, value3: value3 })
}*/
chart.data = data;
//console.log(data);
/* Create axes */
var categoryAxis = chart.xAxes.push(new am4charts.DateAxis());
categoryAxis.fontSize = 9;
categoryAxis.renderer.labels.template.fill = am4core.color(mainTextColor);
var valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
valueAxis.extraMin = 0.2;
valueAxis.extraMax = 0.2;
valueAxis.tooltip.disabled = true;
valueAxis.fontSize = 9;
valueAxis.renderer.labels.template.fill = am4core.color(mainTextColor);
// valueAxis.renderer.labels.template.fontSize = 15;
/* Create and configure series */
var series1 = chart.series.push(new am4charts.RadarSeries());
series1.dataFields.valueY = "value1";
series1.dataFields.dateX = "date";
series1.strokeWidth = 1.5;
series1.tooltipText = "{valueY}";
series1.name = "Series 1";
//series1.bullets.create(am4charts.CircleBullet);
series1.dataItems.template.locations.dateX = 0.5;
var series2 = chart.series.push(new am4charts.RadarSeries());
series2.dataFields.valueY = "value2";
series2.dataFields.dateX = "date";
series2.strokeWidth = 1.5;
series2.tooltipText = "{valueY}";
series2.name = "Series 2";
//series2.bullets.create(am4charts.CircleBullet);
series2.dataItems.template.locations.dateX = 0.5;
var series3 = chart.series.push(new am4charts.RadarSeries());
series3.dataFields.valueY = "value3";
series3.dataFields.dateX = "date";
series3.strokeWidth = 1.5;
series3.tooltipText = "{valueY}";
series3.name = "Series 3";
//series2.bullets.create(am4charts.CircleBullet);
series3.dataItems.template.locations.dateX = 0.5;
chart.scrollbarX = new am4core.Scrollbar();
chart.scrollbarX.minHeight = 1;
chart.scrollbarY = new am4core.Scrollbar();
chart.scrollbarY.minWidth = 1;
chart.cursor = new am4charts.RadarCursor();
chart.legend = new am4charts.Legend();
// chart.legend.fontSize = 10;
chart.legend.fontSize = 9;
chart.legend.labels.template.fill = am4core.color(mainTextColor);
var range_estiaje = categoryAxis.axisRanges.create();
range_estiaje.date = new Date(2020, 2, 1);
range_estiaje.endDate = new Date(2020, 4, 1);
range_estiaje.axisFill.fill = am4core.color("#C39473");
range_estiaje.axisFill.fillOpacity = 0.3;
range_estiaje.grid.strokeOpacity = 0;
var range_lluvTropical = categoryAxis.axisRanges.create();
range_lluvTropical.date = new Date(2020, 4, 1);
range_lluvTropical.endDate = new Date(2020, 8, 1);
range_lluvTropical.axisFill.fill = am4core.color("#D5B49F");
range_lluvTropical.axisFill.fillOpacity = 0.3;
range_lluvTropical.grid.strokeOpacity = 0;
var range_lluvTropicalInv = categoryAxis.axisRanges.create();
range_lluvTropicalInv.date = new Date(2020, 8, 1);
range_lluvTropicalInv.endDate = new Date(2020, 11, 1);
range_lluvTropicalInv.axisFill.fill = am4core.color("#B5B5B5");
range_lluvTropicalInv.axisFill.fillOpacity = 0.3;
range_lluvTropicalInv.grid.strokeOpacity = 0;
var range_lluvInv = categoryAxis.axisRanges.create();
range_lluvInv.date = new Date(2020, 11, 1);
range_lluvInv.endDate = new Date(2020, 12, 1);
range_lluvInv.axisFill.fill = am4core.color("#E3E4DE");
range_lluvInv.axisFill.fillOpacity = 0.3;
range_lluvInv.grid.strokeOpacity = 0;
var range_lluvInv1 = categoryAxis.axisRanges.create();
range_lluvInv1.date = new Date(2020, 0, 1);
range_lluvInv1.endDate = new Date(2020, 2, 1);
range_lluvInv1.axisFill.fill = am4core.color("#E3E4DE");
range_lluvInv1.axisFill.fillOpacity = 0.3;
range_lluvInv1.grid.strokeOpacity = 0;
chart.legend = new am4charts.Legend();
chart.legend.fontSize = 9;
// let legend2 = []
var legenddata2 = [{ name: "Estiaje", fill: am4core.color("#C39473") },
{ name: "Lluvia Tropical", fill: am4core.color("#D5B49F") },
{ name: "Lluvia Tropical-Invernal", fill: am4core.color("#B5B5B5") },
{ name: "Lluvia Invernal", fill: am4core.color("#E3E4DE") }
];
//chart.legend.data = legenddata;
chart.legend.data = legenddata2;
chart.legend.fontSize = 10;
chart.legend.labels.template.fill = am4core.color(mainTextColor);
}); // end am4core.ready()
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ LINES CHARTS
am4core.ready(function() {
// Themes begin
am4core.useTheme(grijalva_theme);
// Themes end
// Create chart instance
let chart = am4core.create("linesChart", am4charts.XYChart);
let title = chart.titles.create();
title.text = "Superficie por tipo de cuerpo";
title.fontSize = 15;
title.marginBottom = 30;
title.fill = am4core.color(mainTextColor);
// Increase contrast by taking evey second color
//chart.colors.step = 2;
// Add data
chart.data = generateChartData();
// Create axes
var dateAxis = chart.xAxes.push(new am4charts.DateAxis());
dateAxis.renderer.minGridDistance = 50;
dateAxis.fontSize = 8;
dateAxis.renderer.labels.template.fill = am4core.color(mainTextColor);
// Create series
function createAxisAndSeries(field, name, opposite, bullet) {
var valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
valueAxis.fontSize = 8;
if (chart.yAxes.indexOf(valueAxis) != 0) {
valueAxis.syncWithAxis = chart.yAxes.getIndex(0);
}
var series = chart.series.push(new am4charts.LineSeries());
series.dataFields.valueY = field;
series.dataFields.dateX = "date";
series.strokeWidth = 2;
series.yAxis = valueAxis;
series.name = name;
series.tooltipText = "{name}: [bold]{valueY.formatNumber('#,###.0')} Ha[/]";
series.tensionX = 0.8;
series.showOnInit = true;
var interfaceColors = new am4core.InterfaceColorSet();
/* switch (bullet) {
case "triangle":
var bullet = series.bullets.push(new am4charts.Bullet());
bullet.width = 12;
bullet.height = 12;
bullet.horizontalCenter = "middle";
bullet.verticalCenter = "middle";
var triangle = bullet.createChild(am4core.Triangle);
triangle.stroke = interfaceColors.getFor("background");
triangle.strokeWidth = 2;
triangle.direction = "top";
triangle.width = 12;
triangle.height = 12;
break;
case "rectangle":
var bullet = series.bullets.push(new am4charts.Bullet());
bullet.width = 10;
bullet.height = 10;
bullet.horizontalCenter = "middle";
bullet.verticalCenter = "middle";
var rectangle = bullet.createChild(am4core.Rectangle);
rectangle.stroke = interfaceColors.getFor("background");
rectangle.strokeWidth = 2;
rectangle.width = 10;
rectangle.height = 10;
break;
default:
var bullet = series.bullets.push(new am4charts.CircleBullet());
bullet.circle.stroke = interfaceColors.getFor("background");
bullet.circle.strokeWidth = 2;
break;
}*/
valueAxis.renderer.line.strokeOpacity = 1;
valueAxis.renderer.line.strokeWidth = 2;
valueAxis.renderer.line.stroke = series.stroke;
valueAxis.renderer.labels.template.fill = series.stroke;
valueAxis.renderer.opposite = opposite;
}
createAxisAndSeries("permanente", "Agua permanente", false, "circle");
createAxisAndSeries("temporal", "Áreas temporalmente inundadas", true, "triangle");
createAxisAndSeries("vegetacion", "Suelos húmedos-vegetación acuática", true, "rectangle");
// Add legend
chart.legend = new am4charts.Legend();
//chart.legend.labels.template.fill = am4core.color("#ffff");
chart.legend.fontSize = 8;
chart.legend.labels.template.fill = am4core.color(mainTextColor);
chart.legend.maxHeight = 100;
// Add cursor
chart.cursor = new am4charts.XYCursor();
// generate some random data, quite different range
function generateChartData() {
var chartData = [];
var firstDate = new Date();
firstDate.setDate(firstDate.getDate() - 100);
firstDate.setHours(0, 0, 0, 0);
var permanente = 1600;
var temporal = 2900;
var vegetacion = 8700;
for (var i = 0; i < 15; i++) {
// we create date objects here. In your data, you can have date strings
// and then set format of your dates using chart.dataDateFormat property,
// however when possible, use date objects, as this will speed up chart rendering.
var newDate = new Date(firstDate);
newDate.setDate(newDate.getDate() + i);
permanente += Math.round((Math.random() < 0.5 ? 1 : -1) * Math.random() * 10);
temporal += Math.round((Math.random() < 0.5 ? 1 : -1) * Math.random() * 10);
vegetacion += Math.round((Math.random() < 0.5 ? 1 : -1) * Math.random() * 10);
chartData.push({
date: newDate,
permanente: permanente,
temporal: temporal,
vegetacion: vegetacion
});
}
return chartData;
}
}); // end am4core.ready()
//------------------------------------------------------------------------------------------------------------------------------------ STACKED AREAS
am4core.ready(function() {
// Themes begin
am4core.useTheme(am4themes_animated);
// Themes end
let chart = am4core.create("stackedAreaChart", am4charts.XYChart);
let title = chart.titles.create();
title.text = "Superficie acumulada";
title.fontSize = 15;
title.marginBottom = 30;
title.fill = am4core.color(mainTextColor);
// chart.dateFormatter.inputDateFormat = "dd-mm-yyyy";
var dateAxis = chart.xAxes.push(new am4charts.DateAxis());
dateAxis.renderer.minGridDistance = 60;
dateAxis.fontSize = 8;
dateAxis.renderer.labels.template.fill = am4core.color(mainTextColor);
dateAxis.startLocation = 0.5;
dateAxis.endLocation = 0.5;
dateAxis.baseInterval = {
timeUnit: "month",
count: 1
}
var valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
valueAxis.tooltip.disabled = true;
valueAxis.fontSize = 10;
valueAxis.renderer.labels.template.fill = am4core.color(mainTextColor);
var series = chart.series.push(new am4charts.LineSeries());
series.dataFields.dateX = "date";
series.name = "Agua permanente";
series.dataFields.valueY = "permanente";
//series.tooltipHTML = "<img src='https://www.amcharts.com/lib/3/images/car.png' style='vertical-align:bottom; margin-right: 10px; width:28px; height:21px;'><span style='font-size:14px; color:#000000;'><b>{valueY.value}</b></span>";
//series.tooltipText = "[#000]{valueY.value}[/]";
series.tooltipText = "[#000]{valueY.formatNumber('#,###.#')} Ha";
series.tooltip.background.fill = am4core.color("#FFF");
series.tooltip.getStrokeFromObject = true;
series.tooltip.background.strokeWidth = 3;
series.tooltip.getFillFromObject = false;
series.fillOpacity = 0.6;
series.strokeWidth = 2;
series.stacked = true;
var series2 = chart.series.push(new am4charts.LineSeries());
series2.name = "Áreas temporalmente inundadas";
series2.dataFields.dateX = "date";
series2.dataFields.valueY = "temporal";
//series2.tooltipHTML = "<img src='https://www.amcharts.com/lib/3/images/motorcycle.png' style='vertical-align:bottom; margin-right: 10px; width:28px; height:21px;'><span style='font-size:14px; color:#000000;'><b>{valueY.value}</b></span>";
//series2.tooltipText = "[#000]{valueY.value}[/]";
series2.tooltipText = "[#000]{valueY.formatNumber('#,###.#')} Ha";
series2.tooltip.background.fill = am4core.color("#FFF");
series2.tooltip.getFillFromObject = false;
series2.tooltip.getStrokeFromObject = true;
series2.tooltip.background.strokeWidth = 3;
series2.sequencedInterpolation = true;
series2.fillOpacity = 0.6;
series2.stacked = true;
series2.strokeWidth = 2;
var series3 = chart.series.push(new am4charts.LineSeries());
series3.name = "Suelos húmedos-vegetación acuática";
series3.dataFields.dateX = "date";
series3.dataFields.valueY = "vegetacion";
//series3.tooltipHTML = "<img src='https://www.amcharts.com/lib/3/images/bicycle.png' style='vertical-align:bottom; margin-right: 10px; width:28px; height:21px;'><span style='font-size:14px; color:#000000;'><b>{valueY.value}</b></span>";
// series3.tooltipText = "[#000]{valueY.value}[/]";
series3.tooltipText = "[#000]{valueY.formatNumber('#,###.#')} Ha";
series3.tooltip.background.fill = am4core.color("#FFF");
series3.tooltip.getFillFromObject = false;
series3.tooltip.getStrokeFromObject = true;
series3.tooltip.background.strokeWidth = 3;
series3.sequencedInterpolation = true;
series3.fillOpacity = 0.6;
series3.defaultState.transitionDuration = 1000;
series3.stacked = true;
series3.strokeWidth = 2;
chart.cursor = new am4charts.XYCursor();
chart.cursor.xAxis = dateAxis;
chart.scrollbarX = new am4core.Scrollbar();
chart.scrollbarX.minHeight = 1;
//chart.scrollbarX.position = "bottom";
// Add a legend
chart.legend = new am4charts.Legend();
// chart.legend.position = "top";
chart.legend.fontSize = 10;
chart.legend.labels.template.fill = am4core.color(mainTextColor);
// axis ranges
var range = dateAxis.axisRanges.create();
range.date = new Date(2001, 0, 1);
range.endDate = new Date(2003, 0, 1);
range.axisFill.fill = chart.colors.getIndex(7);
range.axisFill.fillOpacity = 0.2;
range.label.text = "Fines for speeding increased";
range.label.inside = true;
range.label.rotation = 90;
range.label.horizontalCenter = "right";
range.label.verticalCenter = "bottom";
var range2 = dateAxis.axisRanges.create();
range2.date = new Date(2007, 0, 1);
range2.grid.stroke = chart.colors.getIndex(7);
range2.grid.strokeOpacity = 0.6;
range2.grid.strokeDasharray = "5,2";
range2.label.text = "Motorcycle fee introduced";
range2.label.inside = true;
range2.label.rotation = 90;
range2.label.horizontalCenter = "right";
range2.label.verticalCenter = "bottom";
}); // end am4core.ready()
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ POPULATION BARS
am4core.ready(function() {
// Themes begin
am4core.useTheme(am4themes_animated);
// Themes end
// Create chart instance
var chart = am4core.create("dimfrcpo-graph", am4charts.XYChart);
let title = chart.titles.create();
title.text = "Conteo de cuerpos por tipo";
title.fontSize = 15;
title.marginBottom = 30;
title.fill = am4core.color(mainTextColor);
/*// Add data
chart.data = [{
"year": 2005,
"income": 23.5,
"expenses": 18.1
}, {
"year": 2006,
"income": 26.2,
"expenses": 22.8
}, {
"year": 2007,
"income": 30.1,
"expenses": 23.9
}, {
"year": 2008,
"income": 29.5,
"expenses": 25.1
}, {
"year": 2009,
"income": 24.6,
"expenses": 25
}];
// Create axes
var categoryAxis = chart.yAxes.push(new am4charts.CategoryAxis());
categoryAxis.dataFields.category = "year";
categoryAxis.numberFormatter.numberFormat = "#";
categoryAxis.renderer.inversed = true;
categoryAxis.renderer.grid.template.location = 0;
categoryAxis.renderer.cellStartLocation = 0.1;
categoryAxis.renderer.cellEndLocation = 0.9;*/
var dateAxis = chart.yAxes.push(new am4charts.DateAxis());
dateAxis.dataFields.date = "date"; //"year";
//dateAxis.numberFormatter.numberFormat = "#";
dateAxis.renderer.inversed = true;
dateAxis.renderer.grid.template.location = 0;
dateAxis.renderer.cellStartLocation = 0.1;
dateAxis.renderer.cellEndLocation = 0.9;
dateAxis.fontSize = 10;
dateAxis.renderer.labels.template.fill = am4core.color(mainTextColor);
var valueAxis = chart.xAxes.push(new am4charts.ValueAxis());
valueAxis.renderer.opposite = true;
valueAxis.fontSize = 10;
valueAxis.renderer.labels.template.fill = am4core.color(mainTextColor);
// Create series
function createSeries(field, name) {
var series = chart.series.push(new am4charts.ColumnSeries());
series.dataFields.valueX = field;
//series.dataFields.categoryY = "year";
series.dataFields.dateY = "date"; //"year";
series.name = name;
series.columns.template.tooltipText = "[bold]{dateX}[/]{name}: [bold]{valueX}[/]";
series.columns.template.height = am4core.percent(100);
series.sequencedInterpolation = true;
var valueLabel = series.bullets.push(new am4charts.LabelBullet());
valueLabel.label.text = "{valueX}";
valueLabel.label.horizontalCenter = "left";
valueLabel.label.dx = 10;
valueLabel.label.hideOversized = false;
valueLabel.label.truncate = false;
valueLabel.fontSize = 10;
valueLabel.label.fill = am4core.color(mainTextColor);
var categoryLabel = series.bullets.push(new am4charts.LabelBullet());
categoryLabel.label.text = "{name}";
categoryLabel.label.horizontalCenter = "right";
categoryLabel.label.dx = -10;
//categoryLabel.label.fill = am4core.color("#fff");
categoryLabel.label.hideOversized = false;
categoryLabel.label.truncate = false;
categoryLabel.fontSize = 12;
}
createSeries("permanente", "Agua permanente");
createSeries("temporal", "Áreas temporalmente inundadas");
createSeries("vegetacion", "Suelos húmedos-vegetación acuática");
}); // end am4core.ready()
\ No newline at end of file
......@@ -54,7 +54,7 @@ let colors = {
// Add options to combo box, and set their chart variables
// chart containers must already exist in index.php
indicators.forEach( (indicator, index) => {
indicators.forEach((indicator, index) => {
// colnames for queries
cols.push(`min(${indicator}) as min${indicator}`);
cols.push(`max(${indicator}) as max${indicator}`);
......@@ -113,7 +113,7 @@ const sortInitialDateAscending = (a, b) => {
// query available dates on DB
const setupDates = () => {
return new Promise( resolve => {
return new Promise(resolve => {
const baseUrl = new URL(`/data`, window.location.href).href;
let layersQuery = `${baseUrl}/list_layers`;
d3.json(layersQuery).then(layers => {
......@@ -129,14 +129,14 @@ const setupDates = () => {
dateMax = d3.max(dateArray);
//userFiles = dateArray.map( month => timeFormat(month)); // order table names by date
const dates = {min:dateMin, max:dateMax, dates:dateArray};
const dates = { min: dateMin, max: dateMax, dates: dateArray };
resolve(dates);
});
});
}
const populateDates = (dates) => { // fill out date pickers with available dates
return new Promise( resolve => {
return new Promise(resolve => {
$.datepicker.regional["es"] = {
closeText: "Cerrar",
prevText: "&#x3c;Ant",
......@@ -200,12 +200,12 @@ const populateDates = (dates) => { // fill out date pickers with available dates
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));
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
userFiles = userDates.map(month => timeFormat(month)); // order table names by date
// change date selectors from main screen to map
$("#date-initial").detach().appendTo("#datePickers");
......@@ -220,10 +220,10 @@ const populateDates = (dates) => { // fill out date pickers with available dates
// When closing final-date, either setup or update map
if (!map) {
resolve({min: startUserDate, max: endUserDate});
resolve({ min: startUserDate, max: endUserDate });
} else {
// FIXME: no need to pass data here?
updateMap({map: map, min: startUserDate, max: endUserDate});
updateMap({ map: map, min: startUserDate, max: endUserDate });
}
},
beforeShow: (el, inst) => {
......@@ -235,7 +235,7 @@ const populateDates = (dates) => { // fill out date pickers with available dates
}
const setupMap = (dates) => {
return new Promise( resolve => {
return new Promise(resolve => {
// make body tag to have style height: 100%
$("body").css("height", "100%");
......@@ -253,7 +253,7 @@ const setupMap = (dates) => {
});
//let bounds = cuencaBufferMask.getBounds();
let southWest = L.latLng(15.08, -94.42),
let southWest = L.latLng(15.08, -94.42),
northEast = L.latLng(18.82, -91.52),
bounds = L.latLngBounds(southWest, northEast);
......@@ -298,7 +298,7 @@ const setupMap = (dates) => {
}).setView([16.96, -92.97], 8);
cartoDarkLayerOverlay.addTo(overlay);
L.control.attribution({position: "bottomright"}).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 => {
......@@ -323,18 +323,18 @@ const setupMap = (dates) => {
return refMap.unproject(pt, zoom);
}
map.sync(overlay, {offsetFn: offsetGlobal});
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) => {
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});
resolve({ "map": map, "minIndicators": minIndicators, "maxIndicators": maxIndicators });
});
});
}
......@@ -350,19 +350,192 @@ const queryFiles = () => {
}
const getMinMax = table => {
return new Promise( resolve => {
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 => {
d3.json(minmaxQuery).then(minmax => {
resolve(minmax[0]);
});
});
}
const populateMap = async (mapData) => {
const populateMap = async(mapData) => {
let baseUrl = new URL(`/data`, window.location.href).href;
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ RADIAL CHART
let data = []; // empty array to hold chart data
// get am chart
let radialChart = am4core.registry.baseSprites.find(c => c.htmlContainer.id === "radial_chart");
// async queries for each date that has been loaded
const queries = userFiles.map(async(mes, i) => {
let query = `${baseUrl}/query/${mes}?columns=sum(areacpo)/10000 value1,sum(perimcpo) value2, sum(dimfrcpo) value3`;
const queryData = await d3.json(query);
let date = new Date(dateArray[i]);
date.setFullYear(2020)
queryData[0].date = date;
data.push(queryData[0]);
});
// wait for all queries to complete
const dataQueries = await Promise.all(queries);
// and then set chart data
radialChart.data = data;
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ LINE CHART
let data_ls = [];
let linesChart = am4core.registry.baseSprites.find(c => c.htmlContainer.id === "linesChart");
// async queries for each date that has been loaded
const queries_ls = userFiles.map(async(mes, i) => {
let query = `${baseUrl}/query/${mes}?columns=sum(areacpo)/10000 area, descrip&group=descrip`;
const queryData = await d3.json(query);
let date = new Date(dateArray[i]);
date.setFullYear(2016)
for (const wbody in queryData) {
queryData[wbody].date = date;
}
data_ls.push(queryData);
});
// wait for all queries to complete
const dataQueries_ls = await Promise.all(queries_ls);
// and then set chart data
//lineStackedChart.data = data_ls;
var merged = [].concat.apply([], data_ls);
//console.log(merged);
let aguaPermanente = merged.filter(function(d) { return d.descrip == "Agua permanente" });
let aguaTemporal = merged.filter(function(d) { return d.descrip == "Áreas temporalmente inundadas" });
let aguaVegetacion = merged.filter(function(d) { return d.descrip == "Suelos húmedos-vegetación acuática" });
let i;
for (i = 0; i < aguaPermanente.length; i++) {
// aguaPermanente[i].temporal = aguaTemporal[i].temporal;
// aguaPermanente[i].vegetacion = aguaVegetacion[i].vegetacion;
aguaPermanente[i].permanente = aguaPermanente[i].area;
aguaPermanente[i].temporal = aguaTemporal[i].area;
aguaPermanente[i].vegetacion = aguaVegetacion[i].area;
delete aguaPermanente[i].descrip;
delete aguaPermanente[i].area;
}
let grijalva_bodies = aguaPermanente;
grijalva_bodies.sort((a, b) => (a.date > b.date) ? 1 : -1)
//let data_ls = aguaPermanente;
//console.log(grijalva_bodies);
linesChart.data = grijalva_bodies;
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ STACKED AREAS CHART
let data_sa = [];
let stackedAreaChart = am4core.registry.baseSprites.find(c => c.htmlContainer.id === "stackedAreaChart");
// async queries for each date that has been loaded
const queries_sa = userFiles.map(async(mes, i) => {
let query_sa = `${baseUrl}/query/${mes}?columns=sum(areacpo)/10000 area, descrip&group=descrip`;
const queryData = await d3.json(query_sa);
let date = new Date(dateArray[i]);
date.setFullYear(2016)
for (const wbody in queryData) {
queryData[wbody].date = date;
}
data_sa.push(queryData);
});
// wait for all queries to complete
const dataQueries_sa = await Promise.all(queries_sa);
// and then set chart data
//lineStackedChart.data = data_ls;
var merged_1 = [].concat.apply([], data_sa);
console.log(data_sa);
let aguaPermanente_1 = merged_1.filter(function(d) { return d.descrip == "Agua permanente" });
let aguaTemporal_1 = merged_1.filter(function(d) { return d.descrip == "Áreas temporalmente inundadas" });
let aguaVegetacion_1 = merged_1.filter(function(d) { return d.descrip == "Suelos húmedos-vegetación acuática" });
let j;
for (j = 0; j < aguaPermanente_1.length; j++) {
aguaPermanente_1[j].permanente = aguaPermanente_1[j].area;
aguaPermanente_1[j].temporal = aguaTemporal_1[j].area;
aguaPermanente_1[j].vegetacion = aguaVegetacion_1[j].area;
delete aguaPermanente_1[j].descrip;
delete aguaPermanente_1[j].area;
}
let grijalva_bodies_1 = aguaPermanente_1;
grijalva_bodies_1.sort((a, b) => (a.date > b.date) ? 1 : -1)
//let data_ls = aguaPermanente;
console.log("grijalva_bodies_1", grijalva_bodies_1);
stackedAreaChart.data = grijalva_bodies_1;
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ POPULATION BARS
let data_pb = [];
let popBarsChart = am4core.registry.baseSprites.find(c => c.htmlContainer.id === "dimfrcpo-graph");
// async queries for each date that has been loaded
const queries_pb = userFiles.map(async(mes, i) => {
//let query_pb = `${baseUrl}/query/${mes}?columns=areacpo/10000 area, descrip`;
let query_pb = `${baseUrl}/query/${mes}?columns=count(descrip)::int cuenta, descrip&group=descrip`;
const queryData = await d3.json(query_pb);
let date = new Date(dateArray[i]);
date.setFullYear(2016)
for (const wbody in queryData) {
queryData[wbody].date = date;
}
data_pb.push(queryData);
});
// wait for all queries to complete
const dataQueries_pb = await Promise.all(queries_pb);
// and then set chart data
//lineStackedChart.data = data_ls;
console.log("data_pb", data_pb);
var merged_2 = [].concat.apply([], data_pb);
//console.log("merged_2", merged_2);
/* let k;
let filtered_count = data_pb;
//const chartData = await getData();
// call amchart here
for (k = 0; k < data_pb.length; k++) {
filtered_count[k].perm = (data_pb[k].filter(function(d) { return d.descrip == "Agua permanente" }).length);
filtered_count[k].temp = (data_pb[k].filter(function(d) { return d.descrip == "Áreas temporalmente inundadas" }).length);
filtered_count[k].vege = (data_pb[k].filter(function(d) { return d.descrip == "Suelos húmedos-vegetación acuática" }).length);
filtered_count[k].date = (data_pb[k][0].date);
};
console.log(filtered_count);
popBarsChart.data = filtered_count;*/
// console.log((data_pb[0].filter(function(d) { return d.descrip == "Agua permanente" })).length);
//console.log((data_pb[0].filter(function(d) { return d.descrip == "Áreas temporalmente inundadas" })).length);
//console.log((data_pb[0].filter(function(d) { return d.descrip == "Suelos húmedos-vegetación acuática" })).length);
let aguaPermanente_2 = merged_2.filter(function(d) { return d.descrip == "Agua permanente" });
let aguaTemporal_2 = merged_2.filter(function(d) { return d.descrip == "Áreas temporalmente inundadas" });
let aguaVegetacion_2 = merged_2.filter(function(d) { return d.descrip == "Suelos húmedos-vegetación acuática" });
let k;
let cuerpos_conteo = aguaPermanente_2;
for (k = 0; k < cuerpos_conteo.length; k++) {
cuerpos_conteo[k].permanente = aguaPermanente_2[k].cuenta;
cuerpos_conteo[k].temporal = aguaTemporal_2[k].cuenta;
cuerpos_conteo[k].vegetacion = aguaVegetacion_2[k].cuenta;
delete cuerpos_conteo[k].descrip;
delete cuerpos_conteo[k].cuenta;
}
//console.log(cuerpos_conteo);
// let grijalva_bodyCount = cuerpos_conteo;
cuerpos_conteo.sort((a, b) => (a.date > b.date) ? 1 : -1)
//let data_ls = aguaPermanente;
console.log(cuerpos_conteo);
popBarsChart.data = cuerpos_conteo;
//------------------------------------------------------------------------------------------------------------------------------------
let map = mapData.map,
minIndicators = mapData.minIndicators,
......@@ -382,7 +555,7 @@ const populateMap = async (mapData) => {
cuencaBufferMask.addTo(map);
// create mvt layers
userFiles.forEach( f => {
userFiles.forEach(f => {
f = mapboxLayer(f);
});
......@@ -424,12 +597,12 @@ const populateMap = async (mapData) => {
"type": "line",
"paint": {
"line-opacity": 0.75,
"line-width": ["interpolate", ["linear"], ["zoom"],
"line-width": ["interpolate", ["linear"],
["zoom"],
8, 0,
18, 4
],
"line-color": ["match",
["get", "descrip"],
"line-color": ["match", ["get", "descrip"],
"Áreas temporalmente inundadas", "#ff5500",
"Suelos húmedos-vegetación acuática", "#98e600",
"#00c5ff" // Agua permanente default
......@@ -469,7 +642,7 @@ const populateMap = async (mapData) => {
// style currentTiles
let option = $("#indicatorSelect").val(); // option selected from dropdrown
//styleTiles(option, minIndicators, maxIndicators)
//.then(legend.addTo(map)); // add legend control -> it updates
//.then(legend.addTo(map)); // add legend control -> it updates
legend.addTo(map);
let baseLayers = {
......@@ -489,7 +662,7 @@ const populateMap = async (mapData) => {
// setting their z-index
let glmapChildren = map.getPanes().tilePane.children,
children = Array.from(glmapChildren);
children.forEach( c => c.style.zIndex = "inherit" );
children.forEach(c => c.style.zIndex = "inherit");
});
}
......@@ -499,7 +672,7 @@ const updateMap = (mapData) => {
// clear tiles
currentTiles = {};
//retrieve or create tiles for current dates
userFiles.forEach( monthYear => {
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
......@@ -519,49 +692,49 @@ const updateMap = (mapData) => {
// clear minmax indicators objects
maxIndicators = {},
minIndicators = {};
indicators.forEach( (indicator) => {
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) => {
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});
resolve({ "map": map, "minIndicators": minIndicators, "maxIndicators": maxIndicators });
})
}).then( values => { // once we have new minmax values, style all tiles
}).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
//.then(legend.addTo(map)); // add legend control -> it updates
});
// Update charts
//updateCharts();
}
const updateCharts = async () => {
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) );
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]);
indicators.map(async indicator => {
indicatorVars[indicator].chart.data(allData[indicator]);
});
}
$(".loader").hide("fade", 750);
......@@ -582,8 +755,7 @@ const mapboxLayer = (monthYear) => {
type: "fill",
"paint": {
"fill-opacity": 0,
"fill-color": ["match",
["get", "descrip"],
"fill-color": ["match", ["get", "descrip"],
"Áreas temporalmente inundadas", "#ff5500",
"Suelos húmedos-vegetación acuática", "#98e600",
"#00c5ff" // Agua permanente default
......@@ -623,7 +795,7 @@ const setupTimeDimensionControl = () => {
}
L.TimeDimension.Layer.Tile = L.TimeDimension.Layer.extend({
_setAvailableTimes: function () {
_setAvailableTimes: function() {
if (this.options.times) {
return L.TimeDimension.Util.parseTimesExpression(this.options.times);
} else if (this.options.timeInterval) {
......@@ -637,7 +809,7 @@ L.TimeDimension.Layer.Tile = L.TimeDimension.Layer.extend({
}
},
onAdd: function (map) {
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;
......@@ -651,7 +823,7 @@ L.TimeDimension.Layer.Tile = L.TimeDimension.Layer.extend({
this._update();
},
onRemove: function (map) {
onRemove: function(map) {
this._timeDimension.unregisterSyncedLayer(this);
this._timeDimension.off("timeloading", this._onNewTimeLoading, this);
this._timeDimension.off("timeload", this._update, this);
......@@ -663,12 +835,12 @@ L.TimeDimension.Layer.Tile = L.TimeDimension.Layer.extend({
//this._map = null;
},
isReady: function (time) {
isReady: function(time) {
// to be implemented for each type of layer
return true;
},
_update: function () {
_update: function() {
if (!this._baseLayer || !this._map) {
return;
}
......@@ -716,10 +888,10 @@ $("#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
//.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 => {
indicators.forEach(indicator => {
d3.select(indicatorVars[indicator].container).select("svg text.title").classed("active", indicator === option ? true : false);
});
});
......@@ -737,11 +909,8 @@ const styleTiles = (option, minIndicators, maxIndicators) => {
minIndicators[option], scale(minIndicators[option]).hex(),
maxIndicators[option], scale(maxIndicators[option]).hex()
];*/
let color = ["case",
["==", ["get", "descrip"], "Agua permanente"], "#00c5ff",
["==", ["get", "descrip"], "Áreas temporalmente inundadas"], "#ff5500",
["==", ["get", "descrip"], "Suelos húmedos-vegetación acuática"], "#98e600",
'#ff0000'
let color = ["case", ["==", ["get", "descrip"], "Agua permanente"], "#00c5ff", ["==", ["get", "descrip"], "Áreas temporalmente inundadas"], "#ff5500", ["==", ["get", "descrip"], "Suelos húmedos-vegetación acuática"], "#98e600",
'#ff0000'
];
glmap.getMapboxMap().setPaintProperty(layer, "fill-color", color);
});
......@@ -761,7 +930,7 @@ legend.onAdd = () => {
var html = `<h6>${legendText}</h6><ul>`;
let classes = scale.classes();*/
var html = "<h6>Cuerpos de agua</h6><ul>";
Object.keys(colors).forEach( (c, idx, array) => {
Object.keys(colors).forEach((c, idx, array) => {
html += `<li><i style="background: ${colors[c].fill}"></i>${c}</li>`;
});
html += "</ul>";
......
......@@ -9,6 +9,8 @@
// amchart
am4core.ready(function() {
// am4core.useTheme(urban_theme);
let mainTextColor = getComputedStyle(document.body).getPropertyValue('--main-text-color');
......@@ -26,7 +28,10 @@ am4core.ready(function() {
const baseUrl = new URL(`/data`, window.location.href).href;
let table = "urbanization_year";
let query = `${baseUrl}/query/${table}?columns=yeartrimes,sum(area)/10000 as area,date&group=yeartrimes,date&sort=yeartrimes`;
d3.json(query).then(d => chart.data = d);
d3.json(query).then(d => {
chart.data = d;
ParetoData()
});
// Create axes
let xAxis = chart.xAxes.push(new am4charts.DateAxis());
......@@ -51,35 +56,112 @@ am4core.ready(function() {
valueAxis1.fontSize = 15;
valueAxis1.renderer.labels.template.fontSize = 15;
valueAxis1.numberFormatter = new am4core.NumberFormatter();
valueAxis1.numberFormatter.numberFormat = "#,###";
// Modify chart's colors
/* chart.colors.list = [
am4core.color("#313695"),
am4core.color("#313695"),
am4core.color("#313695"),
am4core.color("#5e6bbf"),
am4core.color("#5e6bbf"),
am4core.color("#5e6bbf"),
am4core.color("#5e6bbf"),
am4core.color("#e0f3f8"),
am4core.color("#e0f3f8"),
am4core.color("#e0f3f8"),
am4core.color("#e0f3f8"),
am4core.color("#ffffbf"),
am4core.color("#ffffbf"),
am4core.color("#ffffbf"),
am4core.color("#ffffbf"),
am4core.color("#b82a76"),
am4core.color("#b82a76"),
am4core.color("#b82a76"),
am4core.color("#9e0142"),
am4core.color("#9e0142"),
am4core.color("#9e0142"),
am4core.color("#9e0142"),
];*/
chart.colors.list = [
am4core.color("#BB677C"),
am4core.color("#7B5572"),
am4core.color("#3E4157"),
am4core.color("#F18275"),
am4core.color("#BB677C"),
am4core.color("#7B5572"),
am4core.color("#3E4157"),
am4core.color("#F18275"),
am4core.color("#BB677C"),
am4core.color("#7B5572"),
am4core.color("#3E4157"),
am4core.color("#F18275"),
am4core.color("#BB677C"),
am4core.color("#7B5572"),
am4core.color("#3E4157"),
//am4core.color("#F18275"),
am4core.color("#BB677C"),
am4core.color("#7B5572"),
am4core.color("#3E4157"),
am4core.color("#F18275"),
am4core.color("#BB677C"),
am4core.color("#7B5572"),
am4core.color("#3E4157"),
];
function ParetoData() {
console.log(chart.data)
var total = 0;
for (var i = 0; i < chart.data.length; i++) {
var value = chart.data[i].area;
total += value;
}
var sum = 0;
for (var i = 0; i < chart.data.length; i++) {
var value = chart.data[i].area;
sum += value;
chart.data[i].pareto = sum / total * 100;
}
}
/* let valueAxis2 = chart.yAxes.push(new am4charts.ValueAxis());
valueAxis2.title.text = "Cambio (Ha)"; //"Market Days";
valueAxis2.renderer.opposite = true;
//valueAxis2.axisRanges.value = 50
//valueAxis2.syncWithAxis = valueAxis1;
//valueAxis2.renderer.grid.template.disabled = true;
valueAxis2.numberFormatter.numberFormat = "#,###.0";
valueAxis2.title.fill = am4core.color(mainTextColor);
valueAxis2.renderer.labels.template.fill = am4core.color(mainTextColor);*/
let valueAxis3 = chart.yAxes.push(new am4charts.ValueAxis());
valueAxis3.title.text = "Cambio Porcentual (%)"; //"Market Days";
valueAxis3.title.text = "Porcentaje acumulado (%)"; //"Market Days";
// valueAxis3.calculateTotals = true;
valueAxis3.renderer.opposite = true;
valueAxis3.numberFormatter.numberFormat = "#.#";
valueAxis3.min = 0;
valueAxis3.max = 100;
valueAxis3.strictMinMax = true;
//valueAxis3.render.grid.template.disabled = true;
valueAxis3.numberFormatter = new am4core.NumberFormatter();
valueAxis3.numberFormatter.numberFormat = "#'%'"
valueAxis3.cursorTooltipEnabled = false;
//valueAxis3.numberFormatter.numberFormat = "#.#";
valueAxis3.renderer.labels.template.fill = am4core.color("#b9ce37");
valueAxis3.title.fill = am4core.color("#b9ce37");
//valueAxis3.min = -300;
valueAxis3.syncWithAxis = valueAxis1;
//valueAxis3.syncWithAxis = valueAxis1;
valueAxis3.fontSize = 15;
valueAxis3.renderer.labels.template.fontSize = 15;
valueAxis3.min = valueAxis3.minZoomed;
valueAxis3.max = valueAxis3.maxZoomed;
valueAxis3.calculateTotals = true;
//valueAxis3.min = valueAxis3.minZoomed;
//valueAxis3.max = valueAxis3.maxZoomed;
valueAxis3.numberFormatter = new am4core.NumberFormatter();
valueAxis3.numberFormatter.numberFormat = "#,###";
// valueAxis3.numberFormatter = new am4core.NumberFormatter();
// valueAxis3.numberFormatter.numberFormat = "#,###";
//valueAxis3.syncWithAxis = valueAxis1;
......@@ -94,30 +176,34 @@ am4core.ready(function() {
series1.fill = chart.colors.getIndex(0);
series1.strokeWidth = 0;
series1.clustered = false;
series1.tooltipText = "[bold]Q{dateX.formatDate('q yyyy')}:[/]\nÁrea urbanizada: {valueY.formatNumber('#.#')} Ha";
//series1.tooltipText = "[{categoryX}: bold]{valueY.formatNumber('#.0')} Ha[/]";
series1.columns.template.adapter.add("fill", function(fill, target) {
return chart.colors.getIndex(target.dataItem.index);
})
/*let series2 = chart.series.push(new am4charts.ColumnSeries());
series2.dataFields.dateX = "date";
series2.dataFields.valueY = "area";
series2.dataFields.valueYShow = "change";
series2.name = "Cambio";
series2.yAxis = valueAxis2;
series2.tooltipText = "[bold font-size: 20]{name}\n[bold font-size: 20]{valueY.change.formatNumber('#,###.#')} Ha[/]"; //M[/]";
series2.columns.template.width = am4core.percent(40);
series2.clustered = false;*/
let series3 = chart.series.push(new am4charts.LineSeries());
series3.dataFields.valueY = "area";
series3.dataFields.valueYShow = "previousChangePercent";
//series3.dataFields.valueX = "date";
series3.dataFields.valueY = "pareto"; //"area";
//series3.dataFields.valueYShow = "pareto"; //previousChangePercent";
series3.dataFields.dateX = "date";
series3.name = "Cambio Porcentual";
series3.strokeWidth = 2;
series3.name = "Porcentaje acumulado";
series3.yAxis = valueAxis3;
series3.tooltipText = "pareto: {valueY.formatNumber('#.0')}%[/]";
//series3.bullets.push(new am4charts.CircleBullet());
//series3.strokeWidth = 2;
//series3.stroke = new am4core.InterfaceColorSet().getFor("alternativeBackground");
series3.strokeOpacity = 0.5;
//series3.strokeWidth = 2;
series3.tensionX = 0.7;
series3.stroke = am4core.color("#b9ce37");
series3.strokeDasharray = "3,3";
series3.yAxis = valueAxis3;
//series3.strokeDasharray = "3,3";
//series3.yAxis = valueAxis3;
series3.tooltip.label.fontSize = 15;
series3.tooltipText = "[bold]Q{dateX.formatDate('q yyyy')}:[/]\nÁrea urbanizada: {valueY.formatNumber('#,###.##')} Ha\nCambio: {valueY.previousChange.formatNumber('#,###.##')} Ha\n{name}: {valueY.previousChangePercent.formatNumber('#,###.##')}\%";
series3.tooltipText = "[bold]Q{dateX.formatDate('q yyyy')}:[/]\nIncremento acumulado de urbanización: {valueY.formatNumber('#.0')}% [/]\nIncremento urbanización: {valueY.previousChange.formatNumber('#.0')}%";
series3.tooltip.getFillFromObject = false;
series3.tooltip.background.fill = am4core.color(mainTextColor);
......@@ -130,6 +216,18 @@ am4core.ready(function() {
chart.legend.position = "top";
chart.legend.fontSize = 15;
chart.legend.labels.template.fill = am4core.color(mainTextColor);
//chart.legend.markers.template.disabled = true;
//chart.legend.labels.template.text = "Series: [bold {color}]{name}[/]";
var legenddata = [{ name: "Q1", fill: am4core.color("#F18275") },
{ name: "Q2", fill: am4core.color("#BB677C") },
{ name: "Q3", fill: am4core.color("#7B5572") },
{ name: "Q4", fill: am4core.color("#3E4157") },
{ name: "Porcentaje acumulado", fill: am4core.color("#b9ce37"), marker: "line" }
];
chart.legend.data = legenddata;
//Add scrollbar
// chart.legend.labels.template.fill = am4core.color(mainTextColor);
......@@ -188,8 +286,10 @@ am4core.ready(function() {
// radar chart
// Themes begin
//am4core.useTheme(am4themes_QQTheme);
am4core.useTheme(am4themes_animated);
// Themes end
var rchart = am4core.create("bottomRightChart", am4charts.RadarChart);
......@@ -238,22 +338,42 @@ am4core.ready(function() {
valueAxis.renderer.grid.template.stroke = am4core.color(mainTextColor);
// valueAxis.calculateTotals = true;
//Add series
var columnSeries = rchart.series.push(new am4charts.RadarColumnSeries());
// columnSeries.dataFields.dateX = "date";
columnSeries.dataFields.categoryX = "qq"; //date
columnSeries.dataFields.valueY = "area";
// columnSeries.dataFields.valueYShow = "sum";
columnSeries.columns.template.strokeOpacity = 0;
columnSeries.columns.template.width = am4core.percent(95);
columnSeries.fill = am4core.color("#80acba");
columnSeries.fillOpacity = 0.6;
//
//columnSeries.fill = am4core.color("#80acba");
columnSeries.fillOpacity = 0.5;
columnSeries.tooltip.fontSize = 15;
columnSeries.tooltip.pointerOrientation = "down";
columnSeries.tooltip.background.fillOpacity = 0.5;
columnSeries.columns.template.tooltipText = "[bold]{qq}[/]\nTotal {valueY.formatNumber('#,###.##')} Ha";
columnSeries.cursorTooltipEnabled = false;
//columnSeries.yAxis = dateAxis;
columnSeries.columns.template.adapter.add("fill", function(fill, target) {
if (target.dataItem && (target.dataItem.categoryX == "Q1")) {
return am4core.color("#F18275");
} else if (target.dataItem && (target.dataItem.categoryX == "Q2")) {
return am4core.color("#BB677C");
} else if (target.dataItem && (target.dataItem.categoryX == "Q3")) {
return am4core.color("#7B5572");
} else if (target.dataItem && (target.dataItem.categoryX == "Q4")) {
return am4core.color("#3E4157");
} else {
return fill;
}
});
/* //ANILLO EXTERIOR
var range = dateAxis.axisRanges.create();
......@@ -306,12 +426,47 @@ am4core.ready(function() {
var bubbleBullet = bubbleSeries.bullets.push(new am4charts.CircleBullet())
bubbleBullet.locationX = 0.5;
bubbleBullet.stroke = am4core.color("#b9ce37");
bubbleBullet.fill = am4core.color("#b9ce37");
//bubbleBullet.stroke = am4core.color(mainTextColor);
//bubbleBullet.fill = am4core.color("#b9ce37");
bubbleBullet.tooltipText = "[bold]Q{date.formatDate('q yyyy')}:[/] \n{value.formatNumber('#,###.0')} Ha";
bubbleBullet.adapter.add("tooltipY", function(tooltipY, target) {
return -target.circle.radius;
})
bubbleBullet.adapter.add("fill", function(fill, target) {
if (target.dataItem && (target.dataItem.categoryY == "2014")) {
return am4core.color("#313695");
} else if (target.dataItem && (target.dataItem.categoryY == "2015")) {
return am4core.color("#5e6bbf");
} else if (target.dataItem && (target.dataItem.categoryY == "2016")) {
return am4core.color("#e0f3f8");
} else if (target.dataItem && (target.dataItem.categoryY == "2017")) {
return am4core.color("#ffffbf");
} else if (target.dataItem && (target.dataItem.categoryY == "2018")) {
return am4core.color("#b82a76");
} else if (target.dataItem && (target.dataItem.categoryY == "2019")) {
return am4core.color("#9e0142");
} else {
return fill;
}
});
bubbleBullet.adapter.add("stroke", function(fill, target) {
if (target.dataItem && (target.dataItem.categoryY == "2014")) {
return am4core.color("#313695");
} else if (target.dataItem && (target.dataItem.categoryY == "2015")) {
return am4core.color("#5e6bbf");
} else if (target.dataItem && (target.dataItem.categoryY == "2016")) {
return am4core.color("#e0f3f8");
} else if (target.dataItem && (target.dataItem.categoryY == "2017")) {
return am4core.color("#ffffbf");
} else if (target.dataItem && (target.dataItem.categoryY == "2018")) {
return am4core.color("#b82a76");
} else if (target.dataItem && (target.dataItem.categoryY == "2019")) {
return am4core.color("#9e0142");
} else {
return fill;
}
});
bubbleSeries.heatRules.push({ target: bubbleBullet.circle, min: 2, max: 12, dataField: "value", property: "radius" });
bubbleSeries.dataItems.template.locations.categoryY = 0.5;
......
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