I am creating a leaflet map with popup d3 barplots that are tied to specific geographic points. I am using this code to create the leaflet popups (but with my own data): http://bl.ocks.org/Andrew-Reid/11602fac1ea66c2a6d7f78067b2deddb
I want to be able to add a fixed horizontal line (which will represent a threshold in that data), something like this. I added a fixed line to the code (after this other post):
var lineEnd = 90;
var line = svg.append("line")
.attr("x1", 0)
.attr("x2", width)
.attr("y1", function(){ return y(lineEnd)})
.attr("y2", function(){ return y(lineEnd)})
.attr("stroke-width", 2)
.attr("stroke", "black");
But in the output on my leaflet map, the line seems to appear at arbitrary points on the graphs. Does anyone have any ideas of what I might need to change in the original code to be able to add this horizontal line?
One thing I noticed is that you add your line to the svg directly
But the bars are added to a group element that is translated
var g = svg.append("g")
.attr("transform","translate("+[margin.left,margin.top]+")");
What if you add the line to that group element instead, does it appear in the right place?
Related
My implementation for Brush & Zoom functionality in my d3 line chart is not working as expected,
I followed this link - https://bl.ocks.org/EfratVil/92f894ac0ba265192411e73f633a3e2f,
Problems what I am facing is -
chart is not showing all the values, I have 4 data but it only shows 3 data
onClick of dot I am showing the rect which is not moving with the brush functionality
minor thing but chart always goes out of the box
My code sandbox - https://codesandbox.io/s/proud-firefly-xy1py
Can someone point out what I am doing wrong? thanks.
Please suggest me what I am doing wrong, thanks.
Your first point is going behind your clip area. For example, if you right click on the first visible circle and inspect element you will see all 4 circle elements are present in the dom. The first circle element is behind the axis.
This means you have to move your plot to the right. Unfortunately, the way you have coded the chart you have not appended a g element for the main chart and then appended the circles and path to that g element. As a result this has to be done in multiple places.
First we adjust your clip path as:
svg
.append("defs")
.append("SVG:clipPath")
.attr("id", "clip")
.append("SVG:rect")
.attr("width", containerWidth)
.attr("height", height)
.attr("x", 40)
.attr("y", 0);
next we adjust your circles
scatter
.selectAll(".foo")
.data(data)
.enter()
.append("circle")
.attr("class", "foo")
.attr("transform", "translate(40,0)")
and then your line
scatter
.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line)
.attr("transform", "translate(40,0)");
You will have to account for this 40 px translate for your other elements as well. Although I am having a hard time destructuring your svg. I think this should give you the idea though. Check the axis matches the time points as well.
Check the code sand box
Update
To make the rectangles move with the brush, you will have to add code to your brushed const function to recalculate the x, y, width and height using the updated scales.
Update2
After going through the codesandbox presented in the comments I was able to add the code to update the rectangles to the brushed const as below to make the rects also move with the brushing:
// update rectangles
scatter
.selectAll(".rect-elements")
.attr("x", d => {
console.log(d);
return xScale(d.startTime) - 12.5;
})
.attr("y", 0)
.attr("width", 24)
.attr("height", height + 5);
Full working Code Sandbox.
I'm coding a dimple based bubble chart, which for some z-values removes a bubble and draws a big red X instead, which is an svg path created by line interpolation like this:
var points = [{"x":x-edgeSize,"y":y+edgeSize},
{"x":x,"y":y},
{"x":x+edgeSize,"y":y-edgeSize},
{"x":x,"y":y},
{"x":x-edgeSize,"y":y-edgeSize},
{"x":x,"y":y},
{"x":x+edgeSize,"y":y+edgeSize},
{"x":x,"y":y}];
var lineFunction = d3.svg.line()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.interpolate("linear");
var path = graphSelection.append("path").attr("d",lineFunction(points))
.attr("stroke",color)
.attr("stroke-width",lineWidth);
Now, I want it to be responsive and I followed dimple's example for responsive charts:
Dimple - Responsive sizing and now all the sizes and bounds are by % and I'm calling draw on resize:
this.chart.draw(this.delay,true);
The problem is that the red Xs don't move by themselves, obviously.
So I tried to move it independently on resize, but I don't know the right coordinates until the transition ends - which makes it a 2 step transition.
Will adding the X-path to series.shapes help? will it move along with the other bubbles?
Is there a standard way of doing this?
Thanks
I created two different SVGs. One contains a graph with data points, the other one contains three lines. The lines color are supposed to be dependent on the selected data point and I have not managed to get this done yet (more details below). The jsfiddle can be found here: jsfiddle.
What I would like to do is to change the color of the three lines when I mouseover the data points. I managed to change the color of all lines to the same color but would actually like to use the color that is associated to the respective data point but I don't know how I can pass the color data which are stored in myColors to the function where I set the lines' color.
The relevant code is shown below. I add a graph with datapoints to mySvg and when I mouseover the data points, I change their color to black and the color of the lines in the other SVG to green. However, instead of changing all lines' color to green, I would actually like to change their colors to the colors defined in myColors (see the above linked jsfiddle to find the data). How could I do this?
var circles = mySvg.selectAll("circle")
.data(lineData)
.enter()
.append("circle");
var circleAttributes = circles
.attr("cx", function (d) { return xScale(d.x); })
.attr("cy", function (d) { return yScale(d.y); })
.attr("r", 6)
.style("fill", 'red')
.on('mouseover', function(d){
d3.select(this).style("fill", 'black');
d3.select('#myLines').selectAll("line").attr("class","sweepline").style("stroke", 'green');
})
.on('mouseout', function(d){
d3.select(this).style("fill", 'red');
});
As with many d3 problems this one is easily solved using data binding. Your custom colors could be bound to the lines you append to the second SVG. Since your array myColors, consisting of the arrays of custom colors per line, has the same structure as your other arrays like names, x1Val, y1Val and so forth, it can be easily integrated in the data array coords used for binding information to your lines:
var coords = d3.zip(names, x1Val, y1Val, x2Val, y2Val, myColors);
This data per line can later on be used in the mouseover event handler for your circles setting the stroke style on the lines.
.on('mouseover', function(d,i) {
// ...
d3.select('#myLines')
.selectAll("line")
.style("stroke", function(d) {
return d[5][i].color;
});
})
The callback determines the color by
accessing the array of custom colors, which is at position 5 of the data array bound to the lines, hence d[5],
getting the ith object of this array of colors. The i is the index of this circle, which is passed as parameter to the event handler and made available to the stroke callback by a closure,
getting property .color from this object
Check the updated JSFiddle for a working example.
Furthermore, I have updated the mouseout handler to delete the previously set stroke style causing the lines to be reset to their default color set by class sweepline. This behaviour, at least to my eyes, seemed to be missing.
d3.select('#myLines')
.selectAll("line")
.style("stroke", null);
I started from a sample Map app at: http://bl.ocks.org/d3noob/raw/5193723/
I want to place a custom pie chart, as shown in the fig below. I created one by adding the code snippet just after the creation of circles is done.
Pie-chart Snippet:
var r=10;
var p = Math.PI*2;
var arc = d3.svg.arc()
.innerRadius(r-3)
.outerRadius(r)
.startAngle(0)
.endAngle(p* d.value1);
var arc2 = d3.svg.arc()
.innerRadius(r-7)
.outerRadius(r-4)
.startAngle(0)
.endAngle(p* d.value2);
g.append("path")
.attr("d", arc)
.attr("fill", "red")
.attr("transform", "translate(400,500)");
g.append("path")
.attr("d", arc2)
.attr("fill", "orange")
.attr("transform", "translate(400,500)");
It comes out nicely as shown in the pic below near Thailand:
Problem
When I zoom or move the map, the pie-chart disappears but the circles remain intact. Can someone help me understand it?
One can notice a very crude way the arcs are plotted. The pie-chart is expected to be plotted for each of the city. I am looking for a cleaner way just like the way circles are drawn.
The code that runs when zoom takes place
g.selectAll("path")
.attr("d", path.projection(projection));
is selecting all paths and modifying their "d" attribute. Since it's "generically" just looking for pathss, then it's also grabbing the donut paths you created and modifying them (probably setting them to empty strings or NaNs).
You can fix this either by taking the donuts out of the same g of the geo paths, so that they don't get selected. OR, you can make your "path" selector more specific, by adding some class (e.g. "geo") to all the geo paths and using that class whenever you select them (e.g. g.selectAll("path.geo")).
I can successfully display some points on a openlayers basemap using d3.js however I want to actually display icons (at the moment maki png icons) instead of an svg point.
is it possible to load a png/jpg/svg image to a map using d3.js?
So far I can load the icon directly onto the svg but all the markers locate on the same spot so I think I'm having a problem converting the coordinates properly.
var feature = svg.selectAll("path")
.data(amenities.features)
.enter()
.append("svg:image")
.attr("xlink:href", "maki/renders/post-18#2x.png")
.attr("x", function(d, i) {return amenities.features[i].geometry.coordinates[0]})
.attr("y", function(d, i) {return amenities.features[i].geometry.coordinates[1]})
.attr("width", "20")
.attr("height", "20")
.attr("class", "amenity");
Previously I have been able to create an svg with image background inside it using a 'pattern' to show the image so that is also a possibility but I couldn't translate the code to use it with the geographic aspect of d3.
I know I'm writing the icons at the moment to the 'svg' div, so they don't transform correctly when I zoom in and out. I'm aiming to write the images to the 'g' div, as in have code like:
var feature = g.selectAll("path")
but when I use this line, the elements appear on the document but the icons don't actually render on the map.
Thanks in advance.
There a few issues here. First, I'm not sure you fully grasp how d3 selections works, as indicated by the fact that you are binding amenities.features to your selection and then accessing it for the x and y attributes via an index. See How Selections Work for more details on this. In addition, you need to translate the geographic coordinates of the features to screen coordinates by passing them through your geographic projection function. This should get you close:
// more projections: https://github.com/d3/d3-geo-projection/
var projection = d3.geoAlbers();
var amenities = svg.selectAll('.amenities')
.data(amenities.features);
amenities.enter().append('image');
amenities
.attr("class", "amenities")
.attr("xlink:href", "maki/renders/post-18#2x.png")
// The data is already bound so use it instead of the index. Als,
// you need to translate geo coordinates to screen coordinates by
// passing them through your projection function.
.attr("x", function(d,i) {return projection(d.geometry.coordinates)[0];})
.attr("y", function(d,i) {return projection(d.geometry.coordinates)[1];})
.attr("width", "20")
.attr("height", "20")
I don't think I appropriately used groups but I think the key is having the transform then translate thing in there.
So my example is http://bl.ocks.org/mpmckenna8/b87df1c44243aa1575cb.
But because I didn't use groups properly I don't know that the icons would handle zooming like you seem to want. In my example I just append the images to the circles I've created.
.attr("transform", function(d) { return "translate(" + projection(d.geometry.coordinates) + ")"; })
.attr('opacity',.3)
.attr('fill', '#fad959')