Line chart/graph with an irregular threshold field
Asked Answered
E

2

1

Looking to create a bar chart with an irregular, colored threshold field in the background, so that each data point has its own individual set of min/max thresholds, which ultimately would look something like this: http://dcalvitti.webs.com/plant/SAMPLE.png

Looked at D3 examples like this one: http://bl.ocks.org/mbostock/4062844

Can the latter example be manipulated to look more like the image I created?

Thanks in advance..

Elli answered 26/2, 2014 at 0:51 Comment(1)
Your sample image doesn't load.Guenevere
U
3

The graph shown in your sample image is actually much easier than the linked example; for that, you don't need to create a clipping path and you don't need to draw the line twice with two different colours.

For drawing the coloured background, use an area-path generator, created with d3.svg.area(). Set the y0 accessor function to be extract your minimum value for each point in your data array, and the y1 accessor function to extract the maximum value.

Then draw the line overtop as a normal line graph with a d3.svg.line() path generator.

Working example, adapted from the fiddles in the comments: http://jsfiddle.net/h45CD/12/
(Note: I commented out half the dataset, since the "year" values were repeated, not sure what that was supposed to represent.)

Key code:

// Define the value line path generator
var line = d3.svg.line()                        
    .x( function(d) { return x(d.year); } ) 
    .y( function(d) { return y(d.temp); } );

// Define the area path generator
var area = d3.svg.area()
    .x(  function(d) {  return x(d.year); } )
    .y0( function(d) { return y(d.min); } )
    .y1( function(d) { return y(d.max); } );

/* ... */

// Add the background area showing the historic range
svg.append("path")
   .datum(data)
   .attr("class", "historicRange")
   .attr("d", area);

// Add the value line 
svg.append("path") 
    .datum(data)
    .attr("class", "dataline")
    .attr("d", line);

Edit based on comments

If you do want a line that changes colour depending on historic values, as opposed to a line drawn overtop of a background range, the most straight-forward solution is probably to create a <pattern> element consisting of the different coloured regions, and use this to stroke the value line.

You'll want to familiarize yourself with the different options for the pattern element. This MDN tutorial has a good intro, or you could dive into the full W3 specs.

For this situation, we want the pattern to be sized and positioned relative to the coordinate system used for drawing the line, regardless of the size or shape of the line itself. That means we will be setting both the patternUnits and the patternContentUnits to be userSpaceOnUse. The height and width of the pattern will be the height and width of the plotting area.

Within the pattern we will draw the area that represents the max-min range, but we also need to draw separate areas, with different colours, for values above the max and values below the min. We can use the same area generator for each, but need to change the y0/y1 accessor functions each time.

Key code:

// Add the pattern showing the historic range
var pattern =  defs.append("pattern")
    .datum(data) //add the data to the <pattern> element
                //so it will be inherited by each <path> we append
    .attr({
        "patternUnits":"userSpaceOnUse",
        "patternContentUnits":"userSpaceOnUse",
        "width": width,
        "height": height
    })
    .attr("id", "strokePattern");

pattern.append("path")
   .attr("class", "historicRange between")
   .attr("d", area);

pattern.append("path")
   .attr("class", "historicRange above")
   .attr("d", area.y1( 0 )
                  .y0( function(d){return y(d.max);} )
        );

pattern.append("path")
   .attr("class", "historicRange below")
   .attr("d", area.y1( function(d){return y(d.min);}  )
                  .y0( height )
        );

// Add the value line 
plot.append("path")             
    .datum(data)            
    .attr("class", "dataline")  
    .attr("d", line)
    .style("stroke", "url(#strokePattern)");        

Working example: http://jsfiddle.net/h45CD/14/

Upkeep answered 26/2, 2014 at 3:54 Comment(9)
jsfiddle.net/h45CD/3 Can I provide this link for you to try and illustrate?Elli
Not sure how that relates. And without the data about where you want the coloured range to be, I can't show you.Upkeep
I came up with an actual working chart. Readings within the semi-curved range are to be orange or whatever, and those that fall outside that range, say red if above the MAX, and blue for below the MIN, would change to those colors automatically. jsfiddle.net/mTfq8Elli
To clarify: do you want a line that changes colour depending on value, or a line overtop of a coloured background, like in your sample image?Upkeep
Lines will change color when they exceed or fail to meet a certain value. The 2 horizontal, slightly arced lines represent specific thresholds. The sample image illustrated an area in the background where the colors are to change. Thanks again for responding and being patient.Elli
Ah, well, in that case my comment about "much easier" isn't accurate, but I still think there is a better solution than drawing the lines multiple times with clipping paths -- namely, create the background with areas, but instead of drawing it on screen, use it as a pattern definition that gets applied for stroking the line. I will update the answer with details and examples.Upkeep
I think what you mean is overlay an area on the graph with a certain opacity that where it overlaps the bar values the bars will change color as a result. Actually, my priority has changed. I'm looking to have an input on the same page for the individual columns. Take a look at this: jsfiddle.net/amcharts/6Eb53 that was created by the same guy from whom I culled the chart from, though it was a different post, case. Is there an efficient way of integrating the two? Have manipulated that example to post the necessary bar values, but cannot integrate them into original with the thresholdsElli
Sorry @user3241848, but if you keep on changing what you want my answers are never going to solve it. And grabbing all sorts of complicated code from other people doesn't help you figure out he basics of what you want and how to create it. You can use the pattern approach for a bar graph, too -- you just use the pattern as the fill property of the bars, instead of the stroke property. Drawing a graph based on user input values is a completely different question.Upkeep
That's fair. Though I am learning a lot and spending tons of hours experimenting, mashing different techniques from online examples and exp'd people like you. Thanks again!Elli
E
-1

I'm including a web page link with charts authored by myself based on AMCharts and with the help of that web site's founder. Contains several examples of the above question and more..

http://dcalvitti.webs.com/SAMPLE/NEWWEBINDEX.html

The charts provided are still being worked on. For example, AMcharts does have a function that clips the color of a line above/below a certain value which I didn't know about, so there is still work to be done. I spent many weeks on the charts and thought I'd share. I'm sure someone will find something new here down the road...

Elli answered 1/4, 2014 at 2:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.