I have angular scope variable which is being used in ng-repeat as well. I want to create a chart starting from a date and ending at a certain date with the current day marker. I am using javascript loader.js for drawing the charts but since I need to draw multiple charts inside ng-repeat.
My code looks like this:
$scope.items = [{"title1":"start_day: 01-01-2017", "end_day:15-02-2018"},
{"title2":"start_day: 05-10-2017", "end_day:10-01-2019"}];
and javascript code from google charts:
anychart.onDocumentReady(function () {
// create data tree on our data
var treeData = anychart.data.tree(getData());
// create resource gantt chart
var chart = anychart.ganttResource();
// set container id for the chart
chart.container('container');
// set data for the chart
chart.data(treeData);
// set start splitter position settings
chart.splitterPosition(150);
var now = (new Date()).getTime();
var sec = 1000;
var min = 60*sec;
var hour = 60*min;
var day = 24*hour;
// create linemarkers
var tl = chart.getTimeline();
tl.lineMarker(0).value("current");
// get chart data grid link to set column settings
var dataGrid = chart.dataGrid();
// initiate chart drawing
chart.draw();
// zoom chart to specified date
chart.fitAll();
});
function getData() {
var now = (new Date()).getTime();
var sec = 1000;
var min = 60*sec;
var hour = 60*min;
var day = 24*hour;
return [
{
"periods": [
{"id": "1_1", "start": now - 365*day, "end": now + 100*day }]
},
];
}
I want to pass the dates from angularjs scope variable to javascript code here which will replace the existing data of start and end, also I need to convert the dates difference.
Thanks in advance! :)
I would recommend to use another JS object that stores common data for both angular and js.
var DATES = [{"title1":"start_day: 01-01-2017", "end_day:15-02-2018"},
{"title2":"start_day: 05-10-2017", "end_day:10-01-2019"}];
...
//in agular
$scope.items = DATES;
...
//in loader
function getData() {
...
return DATES;
}
You can also store an angular $scope into a variable and then use it as a usual variable into JS code, var ctrl = $scope but I would recommend to go with the first option.
--
Regarding to dates difference. Try to use standard Date class.
For example:
var deltaMS = new Date("02-15-2018") - new Date("01-01-2017");
var deltaDays = delta / 1000 / 60/ 60 / 24;
//410 days
Related
This question refers to post: Calculating NDVI per region, month & year with Google Earth Engine?
I have modified the code posted by #Kel Markert https://code.earthengine.google.com/349615d7802d59f677181bef0badad9f
to attempt to get a maximum monthly NDVI value from a small polygon from Landsat 8 in Google Earth Engine and export to CSV.
But I keep getting an error of
"Dictionary.get, argument 'key': Invalid type. Expected: String. Actual: List".
Any advice on how to fix this?
https://code.earthengine.google.com/bf6ea84442f33694b7f12247d1eabd3a
Table link
https://code.earthengine.google.com/?asset=users/mangrovewatch/MCM1?
var region = table,
L8 = ee.ImageCollection("LANDSAT/LC08/C01/T1_TOA");
var cloudlessNDVI = L8.map(function(image) {
// Get a cloud score in [0, 100].
var cloud = ee.Algorithms.Landsat.simpleCloudScore(image).select('cloud');
// Create a mask of cloudy pixels from an arbitrary threshold.
var mask = cloud.lte(20);
// Compute NDVI.
var ndvi = image.normalizedDifference(['B5', 'B4']).rename('NDVI');
// Return the masked image with an NDVI band.
return image.addBands(ndvi).updateMask(mask);
});
var startDate = ee.Date('2013-05-01'); // set analysis start time
var endDate = ee.Date('2019-12-31'); // set analysis end time
// calculate the number of months to process
var nMonths = ee.Number(endDate.difference(startDate,'month')).round();
// get a list of time strings to pass into a dictionary later on
var monList = ee.List.sequence(0, nMonths).map(function (n) {
return startDate.advance(n, 'month').format('YYYMMdd');
})
print(monList)
var result = region.map(function(feature){
// map over each month
var timeSeries = ee.List.sequence(0,nMonths).map(function (n){
// calculate the offset from startDate
var ini = startDate.advance(n,'month');
// advance just one month
var end = ini.advance(1,'month');
// filter and reduce
var data = cloudlessNDVI.filterDate(ini,end).max().reduceRegion({
reducer: ee.Reducer.max(),
geometry: feature.geometry(),
scale: 30
});
// get the value and check that it has data
var val = ee.Number(data.get(['ndvi']));
val = ee.Number(ee.Algorithms.If(val,val,-999));
// return max
return val;
});
// create new dictionary with date strings and values
var timeDict = ee.Dictionary.fromLists(monList,timeSeries);
// return feature with a timeseries property and results
return feature.set(timeDict);
});
// print to see if it is doing what we expect...
print(result);
// Export the data to a table for further analysis
Export.table.toDrive({
collection:result,
description:"MCM1_NDVI",
fileFormat:"CSV",
//selectors:["HRpcode","timeseries"]
})
I am using NEX-GDDP for obtaining daily climatology (Precipitation, Temp min and Temp max) data of the 21 GCM models in the period 2018-01-01 to 2099-12-31, for certain points. I made this script, for one model in one scenario
//Dataset
var dataset = ee.ImageCollection('NASA/NEX-GDDP')
.filter(ee.Filter.date('2018-01-01', '2099-12-31'))
.filterMetadata('scenario','equals','rcp45')
.filterMetadata('model','equals','MPI-ESM-LR')
//Points of interest
var Acomayo = ee.Geometry.Point([-71.689166667, -13.921388889]),
var Machupicchu = ee.Geometry.Point([-72.545555556, -13.166666667]),
var Urubamba = ee.Geometry.Point([-72.129116546, -13.323123791]),
var Pisac = ee.Geometry.Point([-71.849444444, -13.415833333]),
var Ccatcca = ee.Geometry.Point([-71.56, -13.609722222]),
var GranjaKcayra = ee.Geometry.Point([-71.875, -13.556666667]),
var Pomacanchi = ee.Geometry.Point([-71.5357971, -14.027777778]),
var Sicuani = ee.Geometry.Point([-71.236944444, -14.253333333]);
var pts = ee.FeatureCollection(ee.List([ee.Feature(Acomayo),ee.Feature(Machupicchu),ee.Feature(Urubamba),ee.Feature(Pisac)
,ee.Feature(Ccatcca),ee.Feature(GranjaKcayra),ee.Feature(Pomacanchi),ee.Feature(Sicuani)]));
//Export to table .CSV
// Empty Collection to fill
var ft = ee.FeatureCollection(ee.List([]));
//Without removal of null values ----------------------------------
//Function to extract values from image collection based on point file and export as a table
var fill = function(img, ini) {
// type cast
var inift = ee.FeatureCollection(ini);
// gets the values for the points in the current img
var ft2 = img.reduceRegions(pts, ee.Reducer.first(),30);
// gets the date of the img
var date = img.date().format("yyyy/MM/dd");
var scenario = img.get("scenario");
var model = img.get("model");
// writes the date in each feature
var ft3 = ft2.map(function(f){return f.set("date", date, "scenario", scenario, "model", model)});
// merges the FeatureCollections
return inift.merge(ft3);
};
// Iterates over the ImageCollection
var newft = ee.FeatureCollection(dataset.iterate(fill, ft));
//print(newft);
// Export
Export.table.toDrive({
collection: newft,
description: 'GCM_diario',
folder: 'Downscalling_Diario',
fileFormat: 'csv'
});
The scripts work fine for two days and two points, but for the current points and period of time I need, it still working after 5 hrs. To reduce the amount of data I think these ideas:
Average the daily data of the 21 GCMs models in the product, and make it one ImgaeCollection, so
there is only a need to separate by scenario.
Export the ImageCollection of every variable (Pp, Tmin, Tmax) to a NetCDF only the area that contains points (don't know if it is
possible).
geometry = ee.Geometry.Polygon(
[[[-72.77555636882136, -12.867571480133547],
[-72.77555636882136, -14.670820732958893],
[-70.69914035319636, -14.670820732958893],
[-70.69914035319636, -12.867571480133547]]], null, false);
If there is another way to download this data I open to do it.
Using .iterate() can be very memory intensive and is prone to memory errors. A more straight forward approach to this would be to select a specific point you want to focus on, loop over all of the days of interest, and use .reduceRegion() to get the information desired. You can then export the time series as a CSV and convert it to whichever format you want.
Here is an example that gets all variables for all models and scenarios:
// specify start and end date
// Change as needed
var startDate = ee.Date('2018-01-01');
var endDate = ee.Date('2019-01-01');
// get the dataset between date range and extract band on interest
var dataset = ee.ImageCollection('NASA/NEX-GDDP')
.filter(ee.Filter.date(startDate,endDate));
// get projection and band information
var firstImage = dataset.first();
var bandNames = firstImage.bandNames();
var proj = firstImage.projection();
var point = ee.Geometry.Point([-71.689166667, -13.921388889])
// calculate number of days to map and extract data for
var n = endDate.difference(startDate,'day').subtract(1);
// map over each date and extract all climate model values
var timeseries = ee.FeatureCollection(
ee.List.sequence(0,n).map(function(i){
var t1 = startDate.advance(i,'day');
var t2 = t1.advance(1,'day');
var dailyColl = dataset.filterDate(t1, t2);
var dailyImg = dailyColl.toBands();
// rename bands to handle different names by date
var bands = dailyImg.bandNames();
var renamed = bands.map(function(b){
var split = ee.String(b).split('_');
return split.slice(0,2).cat(split.slice(-1)).join('_');
});
// extract the data for the day and add time information
var dict = dailyImg.rename(renamed).reduceRegion({
reducer: ee.Reducer.mean(),
geometry: point,
scale: proj.nominalScale()
}).combine(
ee.Dictionary({'system:time_start':t1.millis(),'isodate':t1.format('YYYY-MM-dd')})
);
return ee.Feature(point,dict);
})
);
print(timeseries);
// get properties to chart (all climate models)
var props = timeseries.first().propertyNames().removeAll(['system:time_start','system:index','isodate']);
// Make a chart of the results.
var chart = ui.Chart.feature.byFeature(timeseries, 'system:time_start', props.getInfo());
print(chart);
Map.addLayer(point);
Map.centerObject(point,6);
// export feature collection to CSV
Export.table.toDrive({
collection: timeseries,
description: 'NEX-GDDP-timeseries',
fileFormat: 'CSV',
});
You may run into memory errors in the code editor when working with long date ranges (2018-2099) but the export should work. Also, keep in mind that the Earth Engine exports are a little simplistic so exporting point by point would be the best approach, 1) to avoid memory errors and 2) keep the resulting CSV to one point. You can merge all the points together and export the time series per point in one file but that may be difficult to work with once exported...
Here is a working link: https://code.earthengine.google.com/139432f76ae3f6a81b1459762325ef7f
What I have done
I have added google charts to my page. The data is populated based on the date and duration the user selects. (The duration can be any amount of hours).
What I need to do
I need to ensure that the chart can accommodate the duration selected. By spacing the gant data and making the chart movable.
The problem
Is that I cannot get the chart to accomodate large sets of data without cramping the data. I need the data spaced out and thus make the chart movable (responsive) based on the amount of data searched for.
The Code
function drawChart() {
var data = [];
var header = ['Activity', 'Start Time', 'End Time'];
data.push(header);
for(var i =0; i < $scope.timelineArr.length;i++){
var year = $scope.timelineArr[i].thumbnail_date.substring(0,4);
var month = $scope.timelineArr[i].thumbnail_date.substring(5,7) -1;
var day = $scope.timelineArr[i].thumbnail_date.substring(8,10);
var hour = $scope.timelineArr[i].thumbnail_time.substring(0,2);
var min = $scope.timelineArr[i].thumbnail_time.substring(3, 5);
console.log(year+ " "+month+ " "+day);
var tmp = [];
tmp.push(""+$scope.timelineArr[i].cam_name, new Date(year, month, day, hour, min), new Date(year, month, day, hour, min));
data.push(tmp);
}
$scope.data = google.visualization.arrayToDataTable(data);
var options = {
'legend':'left',
'title':'Camera Events',
'is3D':true,
tooltip: {
isHtml: true
},
'width':1000,
'height':400
}
$scope.chart = new google.visualization.Timeline(document.getElementById('chart_div'));
$scope.chart.draw($scope.data, options);
//////////
google.charts.load('current', {'packages':['timeline']});
google.charts.setOnLoadCallback(drawChart);
I also included what it looks like now.As you can see the data is cramped, imagine if I choose more hours. Thank you.
Is there a way to make the timeline ZOOMABLE?
I'm trying to have my Highstock chart update every minute, by replacing its existing data set with one that is extracted from a url request (which would have different data at different times).
So far I'm not even able to get the data to refresh based on manual changes.
Work so far: https://jsfiddle.net/Lz8gLw8j/
I'm trying to test changing data and then updating it with:
varData = [[0,0],[1,2]]
$('#chart2').highcharts().redraw();
Which does nothing.
You can set the new data to highchart in following way:
var newData = [
[1251763200000, 23.61],
[1251849600000, 23.60],
[1251936000000, 23.79]
]
var chart = $('#chart2').highcharts();
chart.series[0].setData(newData);
$('#chart2').highcharts().redraw();
Updated fiddler:
https://jsfiddle.net/Lz8gLw8j/5/
It looks like there is a load event that you can use
chart : {
events : {
load : function () {
// set up the updating of the chart each second
var series = this.series[0];
setInterval(function () {
var x = (new Date()).getTime(), // current time
y = Math.round(Math.random() * 100);
series.addPoint([x, y], true, true);
}, 1000);
}
}
}
Here's a fiddle: https://jsfiddle.net/jjwilly16/Lz8gLw8j/6/
FYI: I pulled this from an example off of the Highcharts website: http://jsfiddle.net/gh/get/jquery/1.9.1/highslide-software/highcharts.com/tree/master/samples/stock/demo/dynamic-update/
You can add data to the existing series using the addPoint method and replace the dataset using the setData method. The second option argument for both of the methods allows you to redraw the chart. The data should be an array of arrays. The nested array contains the actual data that is plotted. The first elements of the inner, nested array is the epoch (x) while the second is the actual value (y). The epoch is the number of seconds since January 1, 1970 Midnight GMT/UTC. I have updated the fiddle to allow you to add data points as well as reset the data.
The main change are these lines:
$('#updateButton').click(function() {
var date = $('#newDate').val();
var epoch = Math.round(new Date(date).getTime());
var value = parseInt($('#newValue').val());
var chart = $('#chart2').highcharts();
chart.series[0].addPoint([epoch, value], true);
});
$('#resetChart').click(function() {
var chart = $('#chart2').highcharts();
chart.series[0].setData(null, true);
});
https://jsfiddle.net/Lz8gLw8j/7/
I'm working with chart.js and to render a doughnut chart. I want to set the initial chart total value to zero so it can render a full " empty" chart. When I instatiate the chart with zeros it does not render. I cannot find how it handle zeros in the developer documentation.
var kPoints = 000;
var mPoints = 000;
var tPoints = 000;
var cPoints = 000;
var doughnutData = [ {
value : kPoints,
color : "#FF8000"
}, {
value : mPoints,
color : "#99CC00"
}, {
value : tPoints,
color : "#0099CC"
}, {
value : cPoints,
color : "#333333"
}, ];
var ctx = $("#profileChart").get(0).getContext("2d");
var myDoughnut = new Chart(ctx).Doughnut(doughnutData);
From reading the source code for Chart.js I've found that the it tries to sum each of the value fields in its datasource before rendering the chart (see the use of segmentTotal here).
To workaround this, use null for all the values and set one (or more) of the data points to a very small, near zero value. I've used a float notation here for one of the values:
var kPoints = null;
var mPoints = null;
var tPoints = null;
var cPoints = 1e-10;
After that, the example below re-renders the chart (after a 3 second delay) with different data values to show a case of the values updating from the default "dark" chart to a filled out version:
setTimeout(function () {
// Generate a new, random value for each of the data points
doughnutData.forEach(function (item) {
item.value = Math.floor((Math.random() * 10) + 1);
});
var ctx = $("#profileChart").get(0).getContext("2d");
var myDoughnut = new Chart(ctx).Doughnut(doughnutData);
}, 3000);
JSFiddle Example: http://jsfiddle.net/MasterXen/6S9DB/3/
Keep a running total of the values when building the doughnut data.
If there are zero data points, or the total value of all data points is zero, then simply inject an extra dummy point with a label like "No Data" along with an either imperceptible (near-zero) value or a dummy value like 1. In either case, you'll end up with a valid chart with a single category like "No Data".