I am trying to create a bubble chart using DC.js
The sample data I have is:
State_Name Area Cities Villages Population
A 200 60 1050 10000
B 300 80 1100 15678
C 500 20 2220 97767
D 600 90 3400 50000
So, I want to show total three Bubbles on a bubbleChart. The first bubble should represent total Area and rest two should represent total Cities and total Villages respectively. I have made a barChart for States_Names vs Population where xAxis represent States_name and yAxis represent Population. Now I want whenever I click on one or more bars or you can say States_name I should get the sum of Area, Cities and Villages for all those selected States_name. These sum values for Area, Cities and Villages will represent three bubbles on bubbleChart respectively.
For example: if I click on two bars from barChart which represent State_Name = A and B respectively, I
should get three bubbles on bubbleChart. On hovering these three
bubbles I should get values total Area = 200+300= 500, Total
Cities = 60+80 = 140, total Villages = 1050+1100 = 2150 respectively.
For Positioning of bubbles:
I can fix their yAxis position. And, their xAxis position and radius should depend on the value that bubble has. For above example radius of bubble representing total Villages should be larger than the radius of other two bubbles. Similarly, for xAxis position bubble representing total Cities will be on left side of other two bubbles.
I tried code from here: http://jsfiddle.net/gordonwoodhull/37uET/6/
but getting the error: "group.top is not a function". It seems
that code works only for rowChart.
var data = [
{type: 'foo', a: 10, b: 9, c: 11, d: 2},
{type: 'bar', a: 1, b: 4, c: 2, d: 3},
{type: 'foo', a: 0, b: 2, c: 1, d: 22},
{type: 'bar', a: 11, b: 5, c: 6, d: 5}];
var ndx1 = crossfilter(data);
function regroup(dim, cols) {
var _groupAll = dim.groupAll().reduce(
function(p, v) { // add
cols.forEach(function(c) {
p[c] += v[c];
});
return p;
},
function(p, v) { // remove
cols.forEach(function(c) {
p[c] -= v[c];
});
return p;
},
function() { // init
var p = {};
cols.forEach(function(c) {
p[c] = 0;
});
return p;
});
return {
all: function() {
// or _.pairs, anything to turn the object into an array
return d3.map(_groupAll.value()).entries();
}
};
}
var dim = ndx1.dimension(function(r) { return r.a; });
var sidewaysGroup = regroup(dim, ['a', 'b', 'c', 'd']);
var typeDim = ndx.dimension(function(r) { return r.type; });
var sidewaysRow = dc.bubbleChart('#state-donations')
.width(350).height(300)
.dimension(dim)
.group(sidewaysGroup)
.elasticX(true)
.maxBubbleRelativeSize(0.3)
.x(d3.scale.linear().domain([0, 10]))
.y(d3.scale.linear().domain([0, 10]))
.r(d3.scale.linear().domain([0, 400]))
.elasticY(true)
.yAxisPadding(100)
.elasticX(true)
.xAxisPadding(500);
please help!
Related
I'm trying to show a line and % changes in a single highchart plot, but the changes are very little and It can't be seen in the plot. I made a simplified code to show my problem:
a <- c(300,200, 400, 10, 40, 80)
b <- c(0.8, 2, -2, -1.5, -1.1, 2)
d<-cbind(a,b)
dt <- seq(as.Date("2018-01-01"), as.Date("2018-01-06"), by = "days")
ts <- xts(d, dt )
highchart(type="stock") %>%
hc_add_series(ts$a,
type = "line",
color="black") %>%
hc_add_series(ts$b,
type = "lollipop",
color="red")
I need to increase the size of "ts$b" in the plot, how can I do it? I also tried with two axis, but It seems doesn't solve the problem.
I see two solutions to achieve that.
The first you mentioned - using two yAxis and manipulating their height and top distance.
Example JS code:
yAxis: [{
height: '90%',
opposite: false
},
{
visible: false,
top: '83%',
height: '15%',
}
]
Demo:
https://jsfiddle.net/BlackLabel/0826r7sh/
Another way is using a modified logarithmic axis. Negative values can't be plotted on a log axis, because by nature, the axis will only show positive values. In that case you need to use a custom extension according to the following thread:
Highcharts negative logarithmic scale solution stopped working
(function(H) {
H.addEvent(H.Axis, 'afterInit', function() {
const logarithmic = this.logarithmic;
if (logarithmic && this.options.custom.allowNegativeLog) {
// Avoid errors on negative numbers on a log axis
this.positiveValuesOnly = false;
// Override the converter functions
logarithmic.log2lin = num => {
const isNegative = num < 0;
let adjustedNum = Math.abs(num);
if (adjustedNum < 10) {
adjustedNum += (10 - adjustedNum) / 10;
}
const result = Math.log(adjustedNum) / Math.LN10;
return isNegative ? -result : result;
};
logarithmic.lin2log = num => {
const isNegative = num < 0;
let result = Math.pow(10, Math.abs(num));
if (result < 10) {
result = (10 * (result - 1)) / (10 - 1);
}
return isNegative ? -result : result;
};
}
});
}(Highcharts));
.
yAxis: {
type: 'logarithmic',
custom: {
allowNegativeLog: true
}
},
Demo
https://jsfiddle.net/BlackLabel/nw6osucm/
I came Across heatmapGridSeries in lightning chart , I just want to know if this is possible.
Please Check above image , I have lineseries with one axis.. and i want this color bands above the line series with opacity.
For example
if I add value to the heatmapGridSeries from 0 to 100 , it should automatically show orange.. from 100 to 200 it should show green and so on.
Can be done, but shouldn't be a very practical usage case for heatmaps.
Paletted/gradient series background or bands should give nicer results.
Here's a snippet that looks like your picture (without the line series), and also adds a single Band so you can see how they behave a bit differently.
Transparency can be added after R,G,B.
const {
lightningChart,
LUT,
PalettedFill,
ColorRGBA,
emptyLine,
SolidFill,
AxisScrollStrategies,
} = lcjs
const chart = lightningChart().ChartXY()
const line = chart.addLineSeries()
const heatmap = chart.addHeatmapScrollingGridSeries({
scrollDimension: 'columns',
resolution: 1,
step: {
x: 1,
y: 1,
},
})
const transparency = 100
heatmap
.setFillStyle(new PalettedFill({
lut: new LUT({
steps: [
{value: 0, color: ColorRGBA(255,127,39, transparency)},
{value: 1, color: ColorRGBA(181,230,29, transparency)},
{value: 2, color: ColorRGBA(112,146,190, transparency)},
{value: 3, color: ColorRGBA(255,242,0, transparency)},
{value: 4, color: ColorRGBA(237,28,36, transparency)}
]
})
}))
.setPixelInterpolationMode('disabled')
.setWireframeStyle(emptyLine)
chart.getDefaultAxisX().setInterval(-10, 0).setScrollStrategy(AxisScrollStrategies.progressive)
line.add({x:0,y:0})
let x = 1
setInterval(() => {
const y = Math.random()
const p = {x, y}
line.add(p)
const iColor = x % 5
heatmap.addIntensityValues([[iColor]])
x += 1
}, 1000)
<script src="http://unpkg.com/#arction/lcjs#3.1.0/dist/lcjs.iife.js"></script>
I want to draw line from one x-axis to another x-axis , is it possible ?
Right now I can see only constant line is there , but its drawing horizontal line in full page.
const axis = chart.getDefaultAxisX();
const line = axis.addConstantLine(false).setMouseInteractions(false);
Is there any other way to do it like with rectangle ? or do you guys have any plan to do that via constant line in future ?
I have attached example image below
If I have understood your question correctly you want to draw a line from one X axis value to another X axis value on the same axis.
This can be done by using ChartXY.addSegmentSeries(). This series type allows you to draw individual line segments from Point A to Point B. Each segment can be configured individually. See the API Documentation for configuration specifics: https://www.arction.com/lightningchart-js-api-documentation/v3.0.0/classes/segmentfigure.html
To achieve a line from X = 30 to X = 70 you need to first create a new SegmentSeries if you don't already have one.
You can specify the X (or/and Y) axis for the SegmentSeries when creating the series. chart.addSegmentSeries({xAxis: axisX1})
const axisX1 = chart.addAxisX()
// create series
const lineSegment = chart.addSegmentSeries({
xAxis: axisX1
})
// optionally disable cursor and mouse interactions
.setCursorEnabled(false)
.setMouseInteractions(false)
Then you can create a new line on the series
lineSegment.add({
startX: 30,
endX: 70,
startY: 0.2,
endY: 0.2
})
.setStrokeStyle(stroke => stroke.setFillStyle(fill => fill.setColor(ColorHEX('#0f0a'))))
This would create semi-transparent green line from {x: 30, y: 0.2} to {x: 70, y: 0.2}.
You can create as many lines as you need just by calling lineSegment.add() again with new startX/Y and endX/Y values.
Alternatively if you would like to have a rectangle on the chart instead of line you can add a new RectangleSeries. And add a new rectangle on that series.
const axisX2 = chart.addAxisX()
const rectSeries = chart.addRectangleSeries({
xAxis: axisX2
})
.setMouseInteractions(false)
.setCursorEnabled(false)
rectSeries.add({
x1: 30,
x2: 70,
y1: 0.3,
y2: 0.7,
})
.setFillStyle(f => f.setColor(ColorHEX('#f00a')))
See below for a working example with both line and rectangle.
// Extract required parts from LightningChartJS.
const {
lightningChart,
Themes,
UIElementBuilders,
UIBackgrounds,
ColorHEX,
SolidFill
} = lcjs
// Import data-generator from 'xydata'-library.
const {
createProgressiveRandomGenerator
} = xydata
const chart = lightningChart().ChartXY()
const axisX1 = chart.getDefaultAxisX()
const axisX2 = chart.addAxisX()
const axisX3 = chart.addAxisX({
opposite: true
})
const series = chart.addLineSeries()
chart.getDefaultAxisX().setInterval(0, 100, false, true)
chart.getDefaultAxisY().setInterval(0, 1, false, true)
createProgressiveRandomGenerator()
.setNumberOfPoints(100)
.generate()
.toPromise()
.then(data => {
series.add(data)
})
const rectSeries = chart.addRectangleSeries({
xAxis: axisX2
})
.setMouseInteractions(false)
.setCursorEnabled(false)
rectSeries.add({
x1: 30,
x2: 70,
y1: 0.3,
y2: 0.7,
})
.setFillStyle(f => f.setColor(ColorHEX('#f00a')))
const lineSegment = chart.addSegmentSeries({
xAxis: axisX3
})
.setCursorEnabled(false)
.setMouseInteractions(false)
lineSegment.add({
startX: 30,
endX: 70,
startY: 0.2,
endY: 0.2
})
.setStrokeStyle(stroke => stroke.setFillStyle(fill => fill.setColor(ColorHEX('#0f0a'))))
lineSegment.add({
startX: 40,
endX: 90,
startY: 0.2,
endY: 0.1
})
.setStrokeStyle(stroke => stroke.setFillStyle(fill => fill.setColor(ColorHEX('#fffa'))))
<script src="https://unpkg.com/#arction/lcjs#3.0.0/dist/lcjs.iife.js"></script>
<script src="https://unpkg.com/#arction/xydata#1.4.0/dist/xydata.iife.js"></script>
You can use the Axis Band to draw a rectangle which will work similar to the constant line.
const axis = chart.getDefaultAxisX()
const band = axis.addBand()
You can set both the constant line and band to be either on top of all the Series in the chart, or below all Series in the chart, by supplying the onTop: boolean parameter when creating one.
You can set the start and end values of the Band with the Band.setValueStart() and Band.setValueEnd() methods respectively.
For example:
band.setValueStart(100)
band.setValueEnd(200)
This would set the Band cover the range from 100 to 200.
If you have mouse interactions enabled for the band (which is on by default), users can also click and drag the band from its edges to resize it.
The full API documentation for band can be found here.
I have seen scripts that claim to enter coordinates and it'll tell you if they intersect, but I have an array of X,Y values for a couple of "lines" but how do I cycle through the points to find out if they intersect?
I've included a photo of my graph and as you see, eventually my plots cross over, I just want to know if my values ever cross over (intersect).
How do I run through this to find out if any intersection ever occurs?
var Test = {
x: [8043, 10695, 13292, 17163, 20716, 25270],
y: [1000, 274, 100, 27.4, 10, 2.74],
fill: 'tozeroy',
type: 'scatter',
name: 'Test'
};
var Test2 = {
x: [8043, 10063, 12491, 16081, 19408, 23763],
y: [1000, 274, 100, 27.4, 10, 2.74],
fill: 'tozeroy',
type: 'scatter',
name: 'Test2'
};
var Test3 = {
x: [4700, 5943, 7143, 8841, 10366, 13452],
y: [1000, 274, 100, 27.4, 10, 2.74],
fill: 'tozeroy',
type: 'scatter',
name: 'Test3'
};
var data = [Test, Test2, Test3];
var layout = {
width: 700,
height: 700,
xaxis: {
type: 'log',
range: [3,5]
},
yaxis: {
type: 'log',
range: [-2,3]
}
};
Plotly.newPlot('myDiv', data,layout);
Path intercepts
This answer is a follow on from my answer to your most resent question.
The code snippet below will find the intercepts of the paths in the tables as structured in this questions example data using a modified intercept function from the answer link in may comment from aforementioned answer.
Note I am assuming that each table eg Test in your example data represents a curve (Path as a set of line segments) and that intercepts are not expected within a table but rather between tables.
Basic solution
It does this by checking each line segment in one table against each line segment in the other and storing all intercepts in an array.
Note that if a intercept is found at the start or end point of a line it may appear in the array of intercepts twice as the intercept test includes these points.
Note lines that are parallel, even if they have matching start and or end points will not count as intercepts.
The example is run against the example data and has a verbose console output to guide, if needed, you working through what ever data sets you are wrangling. The console logs can be removed without ill effect.
var Test = {
x: [8043, 10695, 13292, 17163, 20716, 25270],
y: [1000, 274, 100, 27.4, 10, 2.74],
fill: 'tozeroy',
type: 'scatter',
name: 'Test'
};
var Test2 = {
x: [8043, 10063, 12491, 16081, 19408, 23763],
y: [1000, 274, 100, 27.4, 10, 2.74],
fill: 'tozeroy',
type: 'scatter',
name: 'Test2'
};
var Test3 = {
x: [4700, 5943, 7143, 8841, 10366, 13452],
y: [1000, 274, 100, 27.4, 10, 2.74],
fill: 'tozeroy',
type: 'scatter',
name: 'Test3'
};
// Copy from here to end comment and place into you page (code base)
// lines outputting to the console eg console.log are just there to help you out
// and can be removed
const lineIntercepts = (() => {
const Point = (x, y) => ({x, y});
const Line = (p1, p2) => ({p1, p2});
const Vector = line => Point(line.p2.x - line.p1.x, line.p2.y - line.p1.y);
function interceptSegs(line1, line2) {
const a = Vector(line1), b = Vector(line2);
const c = a.x * b.y - a.y * b.x;
if (c) {
const e = Point(line1.p1.x - line2.p1.x, line1.p1.y - line2.p1.y);
const u = (a.x * e.y - a.y * e.x) / c;
if (u >= 0 && u <= 1) {
const u = (b.x * e.y - b.y * e.x) / c;
if (u >= 0 && u <= 1) {
return Point(line1.p1.x + a.x * u, line1.p1.y + a.y * u);
}
}
}
}
const PointFromTable = (t, idx) => Point(t.x[idx], t.y[idx]);
const LineFromTable = (t, idx) => Line(PointFromTable(t, idx++), PointFromTable(t, idx));
return function (table1, table2) {
const results = [];
var i = 0, j;
while (i < table1.x.length - 1) {
const line1 = LineFromTable(table1, i);
j = 0;
while (j < table2.x.length - 1) {
const line2 = LineFromTable(table2, j);
const point = interceptSegs(line1, line2);
if (point) {
results.push({
description: `'${table1.name}' line seg index ${i}-${i+1} intercepts '${table2.name}' line seg index ${j} - ${j+1}`,
// The description (line above) can be replaced
// with relevant data as follows
/* remove this line to include additional info per intercept
tableName1: table1.name,
tableName2: table2.name,
table_1_PointStartIdx: i,
table_1_PointEndIdx: i + 1,
table_2_PointStartIdx: j,
table_2_PointEndIdx: j + 1,
and remove this line */
x: point.x,
y: point.y,
});
}
j ++;
}
i++;
}
if (results.length) {
console.log("Found " + results.length + " intercepts for '" + table1.name + "' and '" + table2.name + "'");
console.log(results);
return results;
}
console.log("No intercepts found for '" + table1.name + "' and '" + table2.name + "'");
}
})();
// end of code
// Test and example code only from here down.
var res1 = lineIntercepts(Test, Test2);
var res2 = lineIntercepts(Test, Test3);
var res3 = lineIntercepts(Test2, Test3);
Using the above function
This bit of code illustrates how you extract intercepts from the function results
// find all the intercepts for the paths in tabels Test and Test2
const results = lineIntercepts(Test, Test2); // pass two tables
// If results not undefined then intercepts have been found
if (results) { // results is an array of found intercepts
// to get the point/s as there could be several
for (const intercept of results) { // loop over every intercept
// a single intercept coordinate
const x = intercept.x; // get x
const y = intercept.y; // get y
}
}
Better solutions
The paths look very much like they are a plot of some function thus there are even simpler solutions.
Rather than list out lines of code, I will direct you towards graphing calculators in case you are unaware of such useful time savers. They would have solved your problem in the time it takes to enter the data (by copy&paste thats not very long)
Online graphing calculators example apps Geogebra and Desmos and many more.
I am new to google earth engine and not so familiar with javascript. I want to display the cleared images (B4,B3,B2 bands) of Sentinel 2 by each dates in layers (each layer represent each date). The code is shown as below, but always get error 'no Band 4, constant band'. Can anyone help me to solve this problem? Thanks!
var lakes=table.geometry();
Map.centerObject(lakes, 15);
function maskS2clouds(image) {
var qa = image.select('QA60');
// Bits 10 and 11 are clouds and cirrus, respectively.
var cloudBitMask = 1 << 10;
var cirrusBitMask = 1 << 11;
// Both flags should be set to zero, indicating clear conditions.
var mask = qa.bitwiseAnd(cloudBitMask).eq(0)
.and(qa.bitwiseAnd(cirrusBitMask).eq(0));
return image.updateMask(mask).divide(10000);
}
var start = ee.Date('2015-06-20');
var finish = ee.Date('2018-06-01');
var collection = ee.ImageCollection('COPERNICUS/S2')
.filterDate(start, finish)
.filterBounds(lakes)
.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 10))
.map(maskS2clouds);
var rgbVis = {
min: 0.0,
max: 0.3,
bands: ['B4', 'B3', 'B2'],
};
function addImage(imageL) { // display each image in collection
var id = imageL.id;
var image = ee.Image(imageL.id);
Map.addLayer(image.select(['B4','B3','B2']).clip(lakes),rgbVis,id)
}
collection.evaluate(function(collection) { // use map on client-side
print(collection.features);
collection.features.map(addImage);
})