I'm working on a relatively simple visualization for interval data. The data points will be reported for every 15 minutes of the day, and I'm trying to chart that for the whole day. Everything works fine if I limit it to very few points, but when I try to use an entire day of data, the line appears to fragment into several lines for some reason.
I've inspected the SVG elements, and there's only one path element, so I'm not sure what's going on. I've put all the relevant code (which is pretty simple really, not much room to mess up) into a fiddle for reference: http://jsfiddle.net/TUjhB/. Any tips are appreciated.
var data = d3.csv.parse(csvData);
var line = d3.svg.line()
.x(function (d) { return x(d.Timestamp); })
.y(function (d) { return y(d.num); });
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
It works fine if you sort the data (which isn't sorted in your input) --
data.sort(function(a,b) { return a.Timestamp - b.Timestamp; });
Complete jsfiddle here.
Related
TLDR: How to find boxes that are lined up horizontally
Given I have the data from an image like this:
We can visually see that we have two lines:
Tare: 11700 kg 10:40:58 am 16-May
Gross: 21300 kg 12:49:34 pm 9-Aug
The data I have for each blue box shown in the image is:
Top
Left
Width
Height
Coordinates for each corner of the box (X, Y)
My main thought is to start from the top of my "grid" and loop through each value of y, and then group boxes where they share the largest amount of matching "y" values, but it seems very over the top for something that seems simple.
Unsure really where to go from here
Example data set
I was able to get boxes lined up using this bit of code (in JavaScript), it essentially finds the first "most top left" box, and then finds any boxes that "intersect" with a line that starts from the middle of that first box
We don't care what order we get the boxes in, so as long as we start with the most left on any line we are golden.
function getMostTopLeftBox(boxes) {
const sorted = boxes.slice()
.sort(
(a, b) => {
if (a.Left === b.Left) {
return a.Top < b.Top ? -1 : 1;
}
return a.Left < b.Left ? -1 : 1;
}
);
return sorted[0];
}
function getAlignedBoxesFromSet(boxes) {
const mostTopLeftBox = getMostTopLeftBox(boxes);
const line = mostTopLeftBox.Top + (mostTopLeftBox.Height / 2);
return boxes
.filter(({ Top, Height }) => Top < line && (Top + Height) > line)
.sort(({ Left: a }, { Left: b }) => a < b ? -1 : 1)
}
function getAlignedBoxes(boxes) {
let remaining = boxes;
const lines = [];
const next = () => {
const line = getAlignedBoxesFromSet(remaining);
lines.push(line);
remaining = remaining.filter(box => line.indexOf(box) === -1);
if (!remaining.length) {
return;
}
return next();
};
next();
return lines;
}
The above code with the data set provided above gives us this result
However, it doesn't account for slight angles on the boxes, for example this image:
Another example of different boxes, with senstive information removed:
You can see from the above that the values below should be considered to be on the same line:
Product: [type]
Num Of [type]: 0
[value]: [value]
I may make a new question for this, but part of the answer to this is to figure out the actual curve of a line, and not just assume that the median angle of all lines is the actual "curve" of the line, so if I was to start with the most left box, then progress to the second box, now I have two distinct lines that I would want to find the smoothed curve for, which I would then use to find the next box, as I find each box I would want to adjust this curve to find the complete line, I will investigate this one further, if anyone has any hints, please do mention it.
I've managed to solve this, with a variant of the code posted in the question.
Here is a code sandbox of the solution, I will do a full write up of this, but here it is for now: https://codesandbox.io/s/102xnl7on3
Here is an example of grouped boxes based on angled lines calculated from the angle of all horizontal lines, if all the boxes were to be straight, then the result would be the lines being straight as well, so it should work in all scenarios.
Here is also an example where the lines are straight:
You can see the lines from the box before intersecting with the next box, it does this each time until it can find a complete line of boxes (till no more line up), this works out better than using an average angle from the entire data set.
I would like to be able to generate a mathematical curve for the already found boxes and apply that to find the next box, but for now, using the previous box as the anchor works pretty well.
I have a rather difficult problem. I am making a topdown 2D game. And I have decided to make an omnidirectional character control like realm of the mad god (Video shows at second 16 what that means). I have it all working fine and have managed to work out almost all the kinks of this sort of camera but one thing remains.
(if you dont know what effect im looking create you can see and example hee at 16 seconds mark: https://youtu.be/N2q6aXkvIiI?t=12s)
When the Camera of the the main client gets rotated more that 180 degrees Left becomes right and right becomes left and my messages to the server about flipping the given character when he is supposed to get inverted. I somewhat fixed this by making an if statement that sends an opposite flip request when your world is completely flipped.
However up and down also become a factor as they dont send requests to flip the character.
What im trying to get to is that I have overcomplicted this script I beleive when trying to get the correct messages accross all clients.
I am wondering if anyone has a logical solution to making sure all chracters on the network are always facing the correct direction at all times.
I am using socket.io and Node.js for speaking to the server.
Any input would be appeciated.
TL;DR
How would you go about making realm of the mad gods camera rotation style when all clients need to know what way everyone is facing. (see video 0:16)
thanks.
I have somewhat fixed this problem with a bit of a rewrite of my logic and managed to make all the magic happen on the client and keep the server out of it entirely. I do not know if what I have done is super effecient as I am rather new to coding.
The logic is that I have created a circle collider2D that represents the players field of view. so things slightly outside the camera view are included. Any collision with an object tagged "otherPlayer" will add them to a list.
I then iterate through the list, if its not empty, and I determine if the object is moving away or coming towards me. (thank you to HigherScriptingAuthority for part of the code for this :: https://forum.unity3d.com/threads/left-right-test-function.31420/).
once I know that, all I have to do is grab the transform of that object and flip it accordingly. Boom it now rotates the object or player correctly no matter what rotation the main client is in.
Hope this makes sense and it helps someone:
private float lastDist = 0;
public float dirNum;
void CheckArea()
{
if (listOfPlayers.Count != 0)
{
foreach(var otherPlayer in listOfPlayers)
{
float distance = (transform.position - otherPlayer.transform.position).magnitude;
if (distance < lastDist)
{
Vector3 heading = otherPlayer.transform.position - transform.position;
dirNum = AngleDir(transform.forward, heading, transform.up);
var objectDirection = AngleDir(transform.forward, heading, transform.up);
if (objectDirection > 0)
{
var nav = otherPlayer.GetComponent<Navigator>();
nav.Left();
}
if (objectDirection < 0)
{
var nav = otherPlayer.GetComponent<Navigator>();
nav.Right();
}
}
if (distance > lastDist)
{
Vector3 heading = otherPlayer.transform.position - transform.position;
dirNum = AngleDir(transform.forward, heading, transform.up);
var objectDirection = AngleDir(transform.forward, heading, transform.up);
if (objectDirection > 0)
{
var nav = otherPlayer.GetComponent<Navigator>();
nav.Right();
}
if (objectDirection < 0)
{
var nav = otherPlayer.GetComponent<Navigator>();
nav.Left();
}
}
lastDist = distance;
}
}
}
Overall context: I have a db of cross-references among pages in a wiki space, and want an incrementally-growing visualization of links.
I have working code that shows clusters of labels as you mouseover. But when you move away, rather than hiding all the labels, I want to keep certain key labels (e.g. the centers of clusters).
I forked an existing example and got it roughly working.
info is at http://webseitz.fluxent.com/wiki/WikiGraphBrowser
near the bottom of that or any other page in that space, in the block that starts with "BackLinks:", at the end you'll find "Click here for WikiGraphBrowser" which will launch a window with the interface
equivalent static subset example visible at http://www.wikigraph.net/static/d3/cgmartin/WikiGraphBrowser/:
code for that example is at https://github.com/BillSeitz/WikiGraphBrowser/blob/master/js/wiki_graph.js
Code that works at removing all labels:
i = j = 0;
if (!bo) { //bo=False - from mouseout
//labels.select('text.label').remove();
labels.filter(function(o) {
return !(o.name in clicked_names);
})
.text(function(o) { return ""; });
j++;
}
Code attempting to leave behind some labels, which does not work:
labels.forEach(function(o) {
if (!(d.name in clicked_names)) {
d.text.label.remove();
}
I know I'm just not grokking the d3 model at all....
thx
The problem comes down to your use of in to search for a name in an array. The Javascript in keyword searches object keys not object values. For an array, the keys are the index values. So testing (d.name in clicked_names) will always return false.
Try
i = j = 0;
if (!bo) { //bo=False - from mouseout
//labels.select('text.label').remove();
labels.filter(function(o) {
return (clicked_names.indexOf(o.name) < 0);
})
.text(function(o) { return ""; });
j++;
}
The array .indexOf(object) method returns -1 if none of the elements in the array are equal (by triple-equals standards) to the parameter. Alternatively, if you are trying to support IE8 (I'm assuming not, since you're using SVG), you could use a .some(function) test.
By the way, there's a difference between removing a label and just setting it's text content to the empty string. Which one to use will depend on whether you want to show the text again later. Either way, just be sure you don't end up with a proliferation of empty labels clogging up your browser.
We're displaying time series data (utilisation of a compute resource, sampled hourly over months) on a stacked area chart using D3.js:
d3.json("/growth/instance_count_1month.json", function( data ) {
data.forEach(function(d) {
d.datapoints = d.datapoints.map(
function(da) {
// NOTE i'm not sure why this needs to be multiplied by 1000
return {date: new Date(da[1] * 1000),
count: da[0]};
});
});
x.domain(d3.extent(data[0].datapoints, function(d) { return d.date; }));
y.domain([0,
Math.ceil(d3.max(data.map(function (d) {return d3.max(d.datapoints, function (d) { return d.count; });})) / 100) * 100
]);
The result is rather spiky for my tastes:
Is there an easy way to simplify the data, either using D3 or another readily available library? I want to reduce the spikiness, but also reduce the volume of data to be graphed, as it will get out of hand.
I have a preference for doing this at the UI level, rather than touching the logging routines (even though redundant JSON data will have to be transferred.)
You have a number of options, you need to decided what is the best way forward for the type of data you have and the needs of it been used. Without knowing more about your data the best I can suggest is re-sampling. Simply report the data at longer intervals ('rolling up' the data). Alternatively you could use a rolling average or look at various line smoothing algorithms.
I have a chart with a DateTime axis as my horizontal and a Linear Axis for my vertical inside a Adobe Flex Line Chart. I want to use a Cartesian Data Canvas as a background element and draw custom set of background graphics mostly rectangles. When I have more than a single data point, the graphics work perfectly since they are supposed to span the width of the entire chart.
When I have only a single data point, however, I can't seem to get the rectangles to draw. Since I want my rectangles to span the entire width of the chart, I was thinking that I could get the x-coordinates from my axis, but this isn't working.
var canvasWidth:Number = chtCanvas.width;
var canvasHeight:Number = chtCanvas.height;
var minPt:Array;
var maxPt:Array;
var minPtDate:Date;
var maxPtDate:Date;
var minPtComplete:Point;
var maxPtComplete:Point;
// This works fine when there is more than 1 data point
minPt = chtCanvas.localToData(new Point(0, 0));
maxPt = chtCanvas.localToData(new Point(canvasWidth,canvasHeight));
//This does return a date object, but wont draw below
minPtDate = axisDate.minimum;
maxPtDate = axisDate.maximum;
//This returns NaN for the x
minPtComplete = chtCanvas.dataToLocal(minPtDate, axisSalary.minimum);
maxPtComplete = chtCanvas.dataToLocal(maxPtDate, axisSalary.maximum);
// Also tried this. Also returns NaN for the x value
//minPtComplete = chtCanvas.dataToLocal(axisDate.minimum, axisSalary.minumum);
//maxPtComplete = chtCanvas.dataToLocal(axisDate.maximum, axisSalary.maximum);
My actual drawing method is as follows:
// Tried this, works with points >2, does not draw with single data point
chtCanvas.drawRect(minPt[0], detail[i].MaxValue, maxPt[0], detail[i].MinValue);
//tried this, no effect with single point
//chtCanvas.drawRect(minPtDate, detail[i].MaxValue, maxPtDate, detail[i].MinValue);
// Tried this, no effect with single point
//chtCanvas.drawRect(minPtDate, minPt[1], maxPtDate, detail[i].MinValue);
// Tried this also
//chtCanvas.drawRect(minPtComplete.x, detail[i].MaxValue, maxPtComplete.x, detail[i].MinValue);
In this example, detail is an array collection of salary values and Im using the data value in the array to determine the vertical bounds of my rectangles.
I need to draw the rectangles the entire width of the chart (even when there is only a single data point). Thanks
Thanks to Heikki for his help. The following code works to use the axis values to draw on your Cartesian Data Canvas:
chtCanvas.drawRect(axisDate.minimum as Date, axisSalary.maximum, axisDate.maximum as Date, axisSalary.minimum);
Casting the values as Date really helped. The rest of the code used above is unecessary.
One thing to note, I was using a DateFormatter to format the date values from my data. What I didn't consider was that when using a DateTimeAxis, Flex will automatically add in extra dates to display on the axis. In my case, I was using a custom parse function to create MY points, but wasnt considering the points Flex was creating and also passing to my parse function (Therefore, they were not getting parsed correctly). Once I corrected this, the values laid out correctly in the case of multiple data points. I'm still having a bit of an issue with single data points and them not filling the chart entirely, but they are now drawing.
UPDATE:
Although there are signs of life, the minimum and maximum are still not drawing the entire width of the chart in some cases depending on the dataUnits and labelUnits combination.
UPDATE #2: SOLVED
Ok, so the axis does work as minimum/maximum values for the Cartesian Data Canvas but there is something important to remember. For a single point (and probably for multiple points as well, I just couldnt visually see the difference), when using a custom DateTimeAxis parse function such as what was in the Adobe Flex ASDoc tutorials:
private function axisDateParseFunction(item:String):Date
{
var inputDate:String = item;
inputDate = fmtDate.format(inputDate);
var newDate:Date = new Date();
if(inputDate)
{
var a:Array = inputDate.split('/');
newDate.fullYear = a[2];
newDate.month = a[0] - 1;
newDate.date = a[1];
newDate.hours = 0;
newDate.hoursUTC = 0;
newDate.minutes = 0;
newDate.minutesUTC = 0;
newDate.seconds = 0;
newDate.secondsUTC = 0;
newDate.milliseconds = 0;
newDate.millisecondsUTC = 0;
}
return newDate;
}
You MUST remember to set the UTC values as shown above also. Since the DateTimeAxis uses date AND time, when you create new Date objects, their time values also get set to the local system time. Remember to set those values to zero also or you will get points that dont exactly line up with your axis labels.