D3.JS- Format tick values with an ordinal scale axis - arrays

I'm using an ordinal scale in my axis:
var xBarScale = d3.scale.ordinal()
.rangeRoundBands([0, width], .1)
.domain(d3.range(data.length));
var xBarAxis = d3.svg.axis()
.scale(xBarScale)
.orient("bottom");
My tick labels are in my data array. What is the best way to format my tick values (using a D3 method) than simply listing each one in the array as I've done here:
xBarAxis.tickValues([data[0].q,data[1].q,data[2].q,data[3].q,data[4].q]);

I'm using D3 v4 and this is how I would do it:
var xBarScale = d3.scale.ordinal()
.rangeRoundBands([0, width], .1)
.domain(d3.range(data.length));
var xBarAxis = d3.axisBottom( xBarScale )
.tickFormat((d)=> d + ' position' ) // just an example of formatting each Tick's text
.tickValues( data.map(d => d.q) ) // creates an Array of `q` values
As to my example, you can set the tickFormat to format your text labels however you wish

Related

Google Earth Engine - error 'Cannot export array bands.' How to export map layer of cross-covariance as Geotiff?

I have computed and displayed as a map layer the cross-covariance of Landsat-derived NDVI and CHIRPS precipitation data.
I now want to export this as an image, clipped to my region of interest, but am getting the following error:
Error 'Cannot export array bands'
I have not managed to find a solution. Is there a way to export this map layer as geotiff? I think perhaps I need to flatten the array but am unsure how to do this.
Here is the code below:
l8toa = ee.ImageCollection("LANDSAT/LC08/C01/T1_TOA")
//Define a region of interest - Baringo county, kenya
var Baringo2 = /* color: #98ff00 */ee.Geometry.Polygon(
[[[35.69382363692023, 1.4034169899773616],
[35.69382363692023, 1.2606333558875118],
[35.61691934004523, 1.0079975313237526],
[35.58945351973273, 0.6509798625215468],
[35.71030312910773, 0.35436075019447294],
[35.72128945723273, 0.18956774160826206],
[35.61691934004523, 0.18407460674896256],
[35.58945351973273, 0.13463632293582842],
[35.71030312910773, 0.04125265421470341],
[35.68283730879523, -0.0466379620709295],
[35.74875527754523, -0.18945988757796725],
[35.96848184004523, 0.05223897866641199],
[36.09482461348273, 0.002800509340276178],
[36.27060586348273, 0.2719645271288622],
[36.23215371504523, 0.45872822561768967],
[36.32004434004523, 0.6509798625215468],
[36.47934609785773, 0.8651943843139164],
[36.32004434004523, 0.9915205478901427],
[36.18271523848273, 1.1672705367627716],
[36.08933144942023, 1.1892385469740003],
[35.79270059004523, 1.6944479915417494]]]);
//print (Baringo2);
//Add Baringo
Map.addLayer(ee.Image().paint(Baringo2, 0, 2), {}, 'Baringo_county');
Map.centerObject(Baringo2);
//B) Filtering, masking and preparing bands of interest
//preprocess the Landsat 8 imagery by filtering it to the location of interest, masking clouds,
//and adding the variables in the model:
// This field contains UNIX time in milliseconds.
var timeField = 'system:time_start';
// Use this function to mask clouds in all Landsat imagery.
var maskClouds = function(image) {
var quality = image.select('BQA');
var cloud01 = quality.eq(61440);
var cloud02 = quality.eq(53248);
var cloud03 = quality.eq(28672);
var mask = cloud01.or(cloud02).or(cloud03).not();
return image.updateMask(mask);
};
// Use this function to add variables for NDVI, time and a constant
// to Landsat 8 imagery.
var addVariablesl8 = function(image) {
// Compute time in fractional years since the epoch.
var date = ee.Date(image.get(timeField));
var years = date.difference(ee.Date('1970-01-01'), 'year');
// Return the image with the added bands.
return image
// Add an NDVI band.
.addBands(image.normalizedDifference(['B5', 'B4']).rename('NDVI'))
.float()
// Add a time band.
.addBands(ee.Image(years).rename('t').float())
// Add a constant band.
.addBands(ee.Image.constant(1));
};
// Remove clouds, add variables and filter to the area of interest - landsat 8.
var filteredLandsatl8 = l8toa
.filterDate('2013-02-07', '2018-08-25')
.filterBounds(Baringo2)
.map(maskClouds)
.map(addVariablesl8);
// Cross-covariance is measuring the correspondence between a variable and a covariate at a lag.
//Create a lagged ImageCollection
var lag = function(leftCollection, rightCollection, lagDays) {
var filter = ee.Filter.and(
ee.Filter.maxDifference({
difference: 1000 * 60 * 60 * 24 * lagDays,
leftField: timeField,
rightField: timeField
}),
ee.Filter.greaterThan({
leftField: timeField,
rightField: timeField
}));
return ee.Join.saveAll({
matchesKey: 'images',
measureKey: 'delta_t',
ordering: timeField,
ascending: false, // Sort reverse chronologically
}).apply({
primary: leftCollection,
secondary: rightCollection,
condition: filter
});
};
//This function joins a collection to itself, using a filter that gets all the images before but within a specified time difference (in days) of each image.
//That list of previous images within the lag time is stored in a property of the image called images, sorted reverse chronologically.
//Compute cross covariance
//i) The covariance reducer expects a set of one-dimensional arrays as input.
//So pixel values corresponding to time t need to be stacked with pixel values at time t ? l as multiple bands in the same image.
var merge = function(image) {
// Function to be passed to iterate.
var merger = function(current, previous) {
return ee.Image(previous).addBands(current);
};
return ee.ImageCollection.fromImages(
image.get('images')).iterate(merger, image);
};
//...use that function to merge the bands from the lagged collection:
//Use a function to convert the merged bands to arrays with bands pt and ph, then reduce with the covariance reducer:
var covariance = function(mergedCollection, band, lagBand) {
return mergedCollection.select([band, lagBand]).map(function(image) {
return image.toArray();
}).reduce(ee.Reducer.covariance(), 8);
};
//is NDVI related in some way to the precipitation before the NDVI was observed?
//To estimate the strength of this relationship (in every pixel),
//load precipitation, join, merge, and reduce as previously:
// Load Precipitation data (covariate)
var chirps = ee.ImageCollection('UCSB-CHG/CHIRPS/PENTAD');
// Join the t-l (l=1 pentad) precipitation images to the Landsat.
var lag1PrecipNDVI = lag(filteredLandsatl8, chirps, 5);
// rainfall 5 days previous - aimed at annual grasses that respond quickly
// Add the precipitation images as bands.
var merged1PrecipNDVI = ee.ImageCollection(lag1PrecipNDVI.map(merge));
// Compute, visualise and display cross-covariance.
var cov1PrecipNDVI = covariance(merged1PrecipNDVI, 'NDVI', 'precipitation');
// create vizualization parameters
var viz = {min:-0.5, max:0.5, palette:['0000FF', '008000', 'FF0000']};
Map.addLayer(cov1PrecipNDVI.arrayGet([0, 1]).clip(Baringo2), viz, 'NDVI - PRECIP cov (lag = 5), Baringo');
//red is high cross covariance and blue is low covariance between NDVI and precipitation 5 days previously
// Export the cov1PrecipNDVI image, specifying scale and region.
Export.image.toDrive({
folder: 'Baringo_Remote_Sensing',
image: cov1PrecipNDVI,
description: 'NDVI - PRECIP cov (lag = 5)',
scale: 30,
region: Baringo2,
maxPixels: 1e10
});
Can anyone help me please?
Thank you.

Why openlayers 4 haversineDistance distances are calculated differently

Openlayers 4 "haversineDistance" calculate differently (compared to ol.Sphere.getLength() ) distance between 2 points.
Why?
Standard example with my custom code
var formatLength = function(line) {
var length = ol.Sphere.getLength(line);
console.log('');
console.log('');
console.log('--1: ', length);
var wgs84Sphere = new ol.Sphere(6378137);
var arr = line.getCoordinates();
var lenghtC = wgs84Sphere.haversineDistance(arr[0], arr[1]) / 100000;
console.log('--2: ', lenghtC);
var output;
if (length > 100) {
output = (Math.round(length / 1000 * 100) / 100) +
' ' + 'km';
} else {
output = (Math.round(length * 100) / 100) +
' ' + 'm';
}
return output;
};
Reading the OpenLayers 4 source code is quite helpful in this case.
The getLength() and haversineDistance() will eventually use the same algorithm to compute a distance:
ol.Sphere.getDistance_ = function(c1, c2, radius) {
var lat1 = ol.math.toRadians(c1[1]);
var lat2 = ol.math.toRadians(c2[1]);
var deltaLatBy2 = (lat2 - lat1) / 2;
var deltaLonBy2 = ol.math.toRadians(c2[0] - c1[0]) / 2;
var a = Math.sin(deltaLatBy2) * Math.sin(deltaLatBy2) + Math.sin(deltaLonBy2) * Math.sin(deltaLonBy2) * Math.cos(lat1) * Math.cos(lat2);
return 2 * radius * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
};
The main difference is that haversineDistance() method only applies to two coordinates, whereas getLength() is more powerful and will sum the distance for any number of coordinates, managing different kind of geometries.
Therefore, for a simple line distance between two points, you should not see any difference.
If there's one, it may be because you are using a different geometry type, or because you are not working in the same projection. The getLength() method works by default with EPSG:3857 / EPSG:4326.
ol.Sphere.getLength = function(geometry, opt_options) {
var options = opt_options || {};
var radius = options.radius || ol.Sphere.DEFAULT_RADIUS;
var projection = options.projection || 'EPSG:3857';
geometry = geometry.clone().transform(projection, 'EPSG:4326');
var type = geometry.getType();
[...]
Note that these calls are likely to change in OpenLayers 5 as explained here
Updated sample code
The problem is in the projection.
var wgs84Sphere = new ol.Sphere(6371008.8);
var length = wgs84Sphere.haversineDistance(cor1, cor2); // Must be 'EPSG:4326'
Openlayer "works" (mostly) with 'EPSG:3857' (Mercator).
But "ol.Sphere.haversineDistance" need 'EPSG:4326' (Lat/Long).
It has not been written in the documentation.
ol.Sphere.getLength() geometry by default is 'EPSG:3857'. The following private functions are converted to 'EPSG:4326'.

Data and Series label not showing on WinForm Chart control

I am having a problem getting the default WinForms Chart control to work. I have a single chart with a single area. In this area, I want to display three (3) series whose labels are on a single legend.
Each of the value arrays shown in the code below contains six (6) values.
When I run the application the chart is only showing the background with the title and the name of the FIRST series I defined, the others seem to be ignored. Also, no grid and no data points or lines are displayed. The chart is basically blank.
this.chart.SuspendLayout();
this.chart.ChartAreas.Clear();
this.chart.Series.Clear();
ChartType chartType = ChartType.Column;
// prepare the area
const string AREA_NAME = "ChartAreaBP";
ChartArea bpChartArea = new ChartArea(AREA_NAME);
bpChartArea.AxisX.LabelStyle.Format = "dd/MMM\nyyyy";
bpChartArea.AxisX.MajorGrid.LineColor = System.Drawing.Color.LightGray;
bpChartArea.AxisY.MajorGrid.LineColor = System.Drawing.Color.LightGray;
bpChartArea.BackColor = System.Drawing.Color.LimeGreen;
bpChartArea.BackGradientStyle = GradientStyle.DiagonalRight;
bpChartArea.Position.Auto = true;
bpChartArea.InnerPlotPosition.Auto = true;
this.chart.ChartAreas.Add(bpChartArea);
// prepare the values. X is Date/time all other 3 are BYTE/INT
var xvals = from x in items select x.TimeStamp;
var yvalsSys = from y in items select y.Systolic;
var yvalsDia = from y in items select y.Diastolic;
var yvalsRhy = from y in items select y.Rhythm;
// The first series, other 2 omitted from HERE for simplicity
const string SYS_SERIES = "Systolic";
Series sysBPSeries = new Series(SYS_SERIES, 4);
sysBPSeries.ChartType = chartType;
sysBPSeries.ChartArea = AREA_NAME;
sysBPSeries.XValueType = ChartValueType.Auto;
sysBPSeries.YValueType = ChartValueType.Date;
sysBPSeries.XAxisType = AxisType.Primary;
sysBPSeries.YAxisType = AxisType.Primary;
sysBPSeries.Enabled = true;
this.chart.Series.Add(sysBPSeries);
this.chart.Series[SYS_SERIES].Points.DataBindXY(xvals, yvalsSys);
// here the other two series are defined.
But when I run the application only the legend of the FIRST series is shown even though the other two are defined in the code (I omitted them from this listing) just the same way as the first series.
And as I stated above, no grid nor values are shown. However, the chart shown in design mode does show all three labels on the first and only legend and all three lines.

d3.js adding circle focus points to multi-line series

After 6 long hours, I managed to add just a couple of more lines to my example, following my previous post (D3 tooltip show values from nested dataset) concerning the use of tooltips.
Now I am stuck at a different point - I can't make circle points that snap to the line points. Other users have already pointed me to a few directions (thanks #Mark), but still haven't been able to combine everything and make it work as I want it.
I have created one circle for each line with its corresponding line color. When hovering over with the mouse, a tooltip with all the lines' values appears and the circles must be positioned on the lines on the x and y axis.
My problem lies in the following piece of code, located inside the mousemove function, in line #106 of this fiddle edit: updated fiddle (https://jsfiddle.net/2en21Lqh/13/):
d3.selectAll(parent + ' .d3-focuspoint')
.classed("hidden", false)
.attr("cx", xz(lastDate))
.attr("cy", function(c) {
return d3.map(c.values, function(d,i) {
if (lastDate == d.date) {
console.log(d.value);
return d.value;
}
})
});
The circles are already bound to the existing data (two days ago I wouldn't have figured this on my own! - at least I am sligthly improving! :/ ), so I am trying to apply the correct cy value but can't figure out the way to do it. The function inside cy returns an object rather than the d.value I am looking for. What am I doing wrong? I've been trying for hours to find any similar examples but can't find any :/
edit: even pointers to the right direction would help :/
Try this:
var mousemoveFunc = function(d, i) {
var x0 = xz.invert(d3.mouse(this)[0]);
var lastDate,
cys = [], //<-- create array to hold y values for circles
ds = []; //<-- create array to hold tooltip text
dataGroup.forEach(function(e) { //<-- loop the data (I removed the map since we now need to return two things)
var i = bisectDate(e.values, x0, 1),
d0 = e.values[i - 1],
d1 = e.values[i];
var d = x0 - d0.date > d1.date - x0 ? d1 : d0;
lastDate = d.date; //<-- hold onto the date (same for all xs)
cys.push(d.value); //<-- hold onto the y value for all circles
ds.push(e.key + " " + d.value); //<-- make the tooltip line
});
var mouse = d3.mouse(svg.node()).map(function(d) {
return parseInt(d);
});
var left = Math.min(containerwidth, mouse[0]+margin.left+margin.right),
top = Math.min(containerheight, mouse[1]+margin.top+margin.right);
d3.selectAll(parent + ' .d3-focuspoint')
.classed("hidden", false)
.attr("cx", xz(lastDate)) //<-- set x position
.attr("cy", function(d,i) {
return yz(cys[i]); //<-- loop the 3 circles and set y position
});
tooltip
.html(lastDate.toString() + "<br/>" + ds.join("<br/>"))
.classed('hidden', false)
.style('left', left + 'px')
.style('top', top + 'px');
};
Updated fiddle.

Amcharts SerialChart multiple line graphs different category value member paths (Silverlight)

EDIT I rewrote my question to make it more understandable after the conversation below with Tony (thanks!).
GOAL Render multiple line graphs (let's say 2) in the same chart. The charts have different x/y value pairs. For one x-value, I do not know both y-values.
I am using Silverlight. The classes available for this are SerialChart and LineGraph. The data source for both graphs is the same and is set at the SerialChart level. The name of the property for the x-axis is also defined there for both graphs (CategoryValueMemberPath).
As suggested by the amCharts documentation, we need to create objects that have a property for the category axis (x-axis) and then one property per graph. Let's call them "Graph1" and "Graph2". So the data source looks something like this:
List<MyClass> data = new List<MyClass>()
{
new MyClass() { Category = 0.1, Graph1 = 0.14, Graph2 = ??? }
,new MyClass() { Category = 0.15, Graph1 = ???, Graph2 = 0.05 }
,new MyClass() { Category = 0.2, Graph1 = 0.35, Graph2 = ??? }
,new MyClass() { Category = 0.18, Graph1 = ???, Graph2 = 0.12 }
... and so on ...
}
PROBLEM What am I supposed to do about the "???" values? I do not have the actual value for that graph for that category value.
If I do not set a value, the default value of 0.0 is assumed and it draws a spike to the x-axis. If I set the previously known Graph1/Graph2 value, then it creates a horizontal connection where there is not supposed to be one. I am basically altering the graph which leads to a wrong result.
So how do I solve this? I am getting the feeling that amCharts do not support this scenario.
You need to add two 'value' axes, one in the X direction and one in the Y direction (imagine, like a bubble chart).
// AXES
// X
var xAxis = new AmCharts.ValueAxis();
xAxis.position = "bottom";
xAxis.gridAlpha = 0.1;
xAxis.autoGridCount = true;
chart.addValueAxis(xAxis);
// Y
var yAxis = new AmCharts.ValueAxis();
yAxis.position = "left";
yAxis.gridAlpha = 0.1;
yAxis.autoGridCount = true;
chart.addValueAxis(yAxis);
Merge all your data points into one array with a common X axis field name ('x' in my example) and for points on line 1, add a property of 'line1' with its value, and for points on line 2, add a property of 'line2'.
For example, your data would look like this:
var chartData = [
{x:0.1,line1:0.25},
{x:0.246,line1:0.342},
{x:0.12,line2:0.16},
{x:0.3,line2:0.485}
];
Then add a 'graph' for each line to your chart specifying where to get the value from in the object array.
// GRAPHS
var graph = new AmCharts.AmGraph();
graph.xField = "x";
graph.yField = "line1";
graph.lineAlpha = 1;
graph.lineColor = '#FF9E01';
chart.addGraph(graph);
var graph2 = new AmCharts.AmGraph();
graph2.xField = "x";
graph2.yField = "line2";
graph.lineAlpha = 1;
graph2.lineColor = '#9EFF01';
chart.addGraph(graph2);
I've put all this into a Fiddle for you - http://jsfiddle.net/64EWx/

Resources