D3 and leaflet returning different SVG in Chrome/Safari and Firefox - svg

I am follow this example to make a map with leaflet and d3, using the latest versions of d3 and leaflet. Something in my code is causing d3 to return different values for the SVG elements in Chrome and FF 28. This is causing the points to be skewed in FF, which has different d values in the PATH elements as well as different transform properties in the SVG elements.
Here is the SVG for Chrome:
<svg width="1049711" height="1802" transform="translate(127,1079)" style="margin-left: -127px; margin-top: -1079px;">
<g class="leaflet-zoom-hide" transform="translate(127,1079)">
<path class="nora f" id="1383_ST_BERNARD_AVE" lat="29.970905251" long="90.064206456" d="M287,210m0,2a2,2 0 1,1 0,-4a2,2 0 1,1 0,4z"></path>
<path class="fixed f" id="7400_ADVENTURE_AVE" lat="30.0599104550001" long="89.9260116889999" d="M1092,-389m0,2a2,2 0 1,1 0,-4a2,2 0 1,1 0,4z"></path>
Here is the SVG for Firefox
<svg width="1049711" height="1802" style="margin-left: -97px; margin-top: -1079px;" transform="translate(97,1079)">
<g class="leaflet-zoom-hide" transform="translate(97,1079)">
<path class="nora f" id="1383_ST_BERNARD_AVE" lat="29.970905251" long="90.064206456" d="M317,210m0,2a2,2 0 1,1 0,-4a2,2 0 1,1 0,4z"/>
<path class="fixed f" id="7400_ADVENTURE_AVE" lat="30.0599104550001" long="89.9260116889999" d="M1122,-389m0,2a2,2 0 1,1 0,-4a2,2 0 1,1 0,4z"/><path class="nora f" id="4170_OLD_GENTILLY_RD" lat="30.0024662600001" long="90.0401487569999" d="M457,-3m0,2a2,2 0 1,1 0,-4a2,2 0 1,1 0,4z"/>
Here is the code that loads the map and projects the points. At the very end there is a function project that returns a different x-value for the point in Chrome and FF 28. I think that this line is creating the overall problem with the code. The x-value is off by different values at different times, so it is hard to write a correction.
var map = new L.Map("map", {center: [29.95, -90.05], zoom: 13, minZoom:10, maxZoom:18})
.addLayer(new L.tileLayer('http://{s}.www.toolserver.org/tiles/bw-mapnik/{z}/{x}/{y}.png'));
var svg = d3.select(map.getPanes().overlayPane).append("svg"),
g = svg.append("g").attr("class", "leaflet-zoom-hide");
//these brackets are jinja2 template syntax. They eventually return 'static/out.json'
d3.json('out.json') }}', function(collection) {
var bounds = d3.geo.bounds(collection),
path = d3.geo.path().projection(project).pointRadius(function (d) {return 2});
console.warn(path)
var feature = g.selectAll("path")
.data(collection.features)
.enter().append("path").attr("class", function(d){
return d.properties.category + " " + d.properties.investigates;;
}).attr("id", function(d){
return d.geometry.address;
}).attr("lat", function(d){
return Math.abs(d.geometry.coordinates[1]);
}).attr("long", function(d){
return Math.abs(d.geometry.coordinates[0]);
});
$(".t").on("click", function(e) {
var adr = "/" + this.id;
showDialog(adr);
});
map.on("viewreset", reset);
reset();
// Reposition the SVG to cover the features.
function reset() {
console.warn(bounds)
var bottomLeft = project(bounds[0]),
topRight = project(bounds[1]);
svg .attr("width", topRight[0] - bottomLeft[0])
.attr("height", bottomLeft[1] - topRight[1])
.style("margin-left", bottomLeft[0] + "px")
.style("margin-top", topRight[1] + "px").attr("transform", "translate(" + -bottomLeft[0] + "," + -topRight[1] + ")");
g .attr("transform", "translate(" + -bottomLeft[0] + "," + -topRight[1] + ")");
feature.attr("d", path)
}
// Use Leaflet to implement a D3 geographic projection.
function project(x) {
var point = map.latLngToLayerPoint(new L.LatLng(x[1], x[0]));
return [point.x, point.y];
}
});
I proposed this as a bug to leaflet. If you try the fiddle in FF 28 and Chrome you will see that line 51 returns different x-values for the same lat/long in Chrome (right x value) and firefox (wrong x value)
I have tried this fiddle in FF 27 and FF 28 -- each of these versions of firefox returns a different (and incorrect) x-value for the point on line 51.
Have I hit a bug in leaflet or d3 or is there an issue with my code? Is there a workaround? What's going on here?

I ended up getting this to work by following this example: https://gist.github.com/ZJONSSON/2957559
Here is the working code:
var path = d3.geo.path().projection(function project(x) {
var point = map.latLngToLayerPoint(new L.LatLng(x[1], x[0]));
return [point.x, point.y];
}).pointRadius(function(d) {
return 2;
});
/* Load and project/redraw on zoom */
d3.json("static/out.json", function(collection) {
var feature = g.selectAll("path")
.data(collection.features)
.enter().append("path")
.attr("class", function(d) {
return d.properties.category + " " + d.properties.investigates + " " + d.properties.thumbnail;
}).attr("id", function(d) {
return d.properties.address;
}).attr("lat", function(d) {
return Math.abs(d.geometry.coordinates[1]);
}).attr("long", function(d) {
return Math.abs(d.geometry.coordinates[0]);
})
.attr("d", path);
map.on("viewreset", function reset() {
feature.attr("d", path);
});
loadThumbs();
});

The problem is how you're calling d3.json.
d3.json('{{url_for('static', filename='out.json') }}', //...
isn't valid. The d3.json function expects its first parameter to be a URL string, not an object. In both FF and webkit you're probably getting random garbage and that's what your code uses to calculate the bounds.
Are you trying to use a template (i.e. mustache) to construct the URL? If so, you're forgetting to render the template.

Related

[D3][SVG] zoom to object

could you please help me with zooming to SVG objects. no idea how to do this.
i need to zoom and center by click on object, i've made a test plunkr, so please take a look: http://plnkr.co/edit/ZQxhQ8VVoIXjMvdFIvQF
here's full code:
$(function(){
svg = d3.select("#svg");
svg_group = d3.select('#outer_group');
zoom = d3.behavior.zoom()
.translate([0, 0])
.scale(1)
.scaleExtent([.5, 20])
.on("zoom", zoomed);
svg.call(zoom);
function zoomed() {
svg_group.style("stroke-width", 1.5 / d3.event.scale + "px");
svg_group.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
$zoomService.$broadcast('zoom', {
scale: d3.event.scale
});
}
$('.sector').click(function(){
//zoom to somehow??
});
});
You have to use call zoom.event explicitly on the correct element after setting the desired translation and scaling.
var zoomed = false;
$('.sector').click(function(){
var bbox = this.getBBox();
var scale = 4;
// Manually calculating the position to which to transition to
// Will differ on a case by case basis.
svg
.call(zoom
.translate([ (- bbox.x - bbox.width / 2) * scale
, (- bbox.y - bbox.height / 2) * scale
])
.scale(scale).event
);
});
Demo: http://plnkr.co/edit/h1UP87dfQneRCFye9Xtu?p=preview
In the demo, I changed the position of the polygons and the viewBox on the svg to make it easier to calculate the exact coordinates to transition to for the zoom to stay centered. I also added some transitions and zoom-to-zero behavior not shown in the code excerpt above.
Sidenote: You don't have to use jQuery here to bind to click events; D3's selection.on can provide that function.

Snap.svg - How to change a polyline to another svg object

So SVG and expecially Snap.svg is very new to me an I am stuck on trying to get my svg object to animate along a defined path.
I have setup a fiddle showing what I have been able to accomplish so far:
http://jsfiddle.net/k1L0g5j2/1/
// ---------
// SVG C
// ---------
var snapC = Snap("#svgC");
// SVG C - "Squiggly" Path
var myPathC = snapC.path("M1462,170c0,0,42,282-236,290s-270-68-506,60s-273.7,70.4-466-160C32,94-232,170-240,358c0,420.4,498-272,598-68").attr({
fill: "none"
});
// SVG C - Draw Path
var lenC = myPathC.getTotalLength();
// SVG C - Triangle (As Polyline)
var Triangle = snapC.polyline("0,30 15,0 30,30");
Triangle.attr({
id: "plane",
fill: "#fff"
});
var triangleGroup = snapC.g(Triangle); // Group polyline
setTimeout(function () {
Snap.animate(0, lenC, function (value) {
movePoint = myPathC.getPointAtLength(value);
triangleGroup.transform('t' + parseInt(movePoint.x) + ',' + parseInt(movePoint.y - 15) + 'r' + (movePoint.alpha - 90));
}, 4500, mina.easeinout);
});
But I need to change this triangle to be another SVG object (moth.svg). I can't figure out how this is done.

d3 on click for circles not working

I have a simple, modified version of the cluster diagram from D3 that I'm trying to get to respond to mouse clicks. It works for the links between nodes but not the nodes themselves. It looks to me like I'm treating lines and nodes (svg circles) the same, and yet nodes do not work... but of course D3 itself is generating those lines...
I have a very simple demo of it on JSFiddle at: http://jsfiddle.net/gaelicmichael1965/c2XWg/8/
What's going on? I would certainly appreciate any help that could be offered.
var nodes = tree.nodes(flareData),
links = tree.links(nodes);
// Create all of the link paths (using diagonal projection)
// Uses D3 functions to create SVG elements
var link = vis.selectAll(".link")
.data(links)
.enter().append("path")
.attr("class", "link")
.attr("d", diagonal)
.on("click", function(d, index) {
console.log("Selected line");
});
// Create all of the g-elements that contain node svg-elements
var node = vis.selectAll(".node")
.data(nodes)
.enter()
.append("circle")
.attr("class", "node")
.attr("r", 4.5)
.attr("transform", function(d) { return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")"; })
// In actuality, will need to access property of d
.style( "fill", function(d, index) { return fillColors[index%4] } )
.on("click", function(d, index) {
console.log("Selected node");
});
The issue you have stems from your CSS. In particular, you are turning off pointer events for the nodes, meaning that mouse-triggered events (such as "click") are not processing:
.node {
font-size: 12px;
pointer-events: none; /*Comment out or remove this line*/
}
Comment out or remove the pointer-events:none; line in your CSS to allow the nodes to be the target of your "click" event.

How can I connect coordinate points on a map using d3js?

I'm having trouble connecting points with a line on a map using d3. I think that I should use d3.svg.line() to create the points - but when I do it, I simply get a very small blob. Please see the link below for a screenshot of what I've been able to accomplish thus far - I want to connect the black dots with a line. Any help would be much appreciated.
Screenshot
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height*3 + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var group = svg.selectAll("g")
.data(data)
.enter()
.append("g")
var projection = d3.geo.mercator().scale(5000).translate([-2000,5900])
var path = d3.geo.path().projection(projection)
var graticule = d3.geo.graticule()
var line = d3.svg.line()
.interpolate("linear")
.x(function(d) { d.geometry.coordinates[0]; })
.y(function(d) { return d.geometry.coordinates[1] ; });
// this returns a parse error
// .x(function(d) { return projection(d.geometry.coordinates[0]); })
// .y(function(d) { return projection(d.geometry.coordinates[1]) ; });
var area = group.append("path")
.attr("d", path)
// .attr("d", line(data))
.attr("class", "area")
})
You have to pass both components of your coordinate to the d3.geo.mercator object, before taking each one separately as your x and y values. Your 'parse error' should go away if you use
.x(function(d) { return projection([d.lon, d.lat])[0]; })
.y(function(d) { return projection([d.lon, d.lat])[1]; });
instead. This post has a more complete example: D3 map Styling tutorial III: Drawing animated paths.
Hopefully once you are drawing the lines in the correct projection, they'll appear as you expect.

d3 drag behavior glitch when dragging svg elements

I am trying to use d3 to make a block which contains an arbitrary number of rects and text elements. The way I envisioned it was to nest everything within an svg and have the svg be dragable while everything moves along with it.
However, whenever I attach a drag behavior to the element it blurs whenever I move it. This behavior occurs even when I nest a g within the svg and everything else withing the g element.
Here is the simplified code. I have a main svg in which I insert another svg in which I nest a rect.
var dragT = d3.select('#test').append('svg').selectAll('svg.test')
.data([{x:100,y:100}])
.enter().append('svg')
.attr('x',100).attr('y',100)
.style('width',100)
.call(rectDragBehav).append('g')
.append('rect').attr('x',100).attr('y',100)
.attr('width',100).attr('height',100);
var rectDragBehav = d3.behavior.drag()
.on('drag', rectDragDrag)
function rectDragDrag(d,i) {
d.x += d3.event.dx;
d.y += d3.event.dy; console.log(1);
d3.select(this)
.attr('x',d.x)
.attr('y',d.y);//.attr("transform", "translate(" + d.x + "," + d.y + ")");
}
Update: I don't know what this entails, but I just discovered that when you scroll down so that the entire svg is out of sight and scroll back up, the afterimages disappear.
Fill your SVG with a big white background <rect /> behind the content, e.g.
<svg …>
<rect fill="white" x="-100" y="-100" width="2000" height="2000" />
…
</svg>
You're just seeing redraw errors from the browser not properly dirtying the changed region. I was seeing the same thing today with Chrome on Windows on this test, but the problems do not appear on any browser under OS X.
Here is an example using jsFiddle. I have made the sample to help you, and I hope it will be beneficial for you.
The HTML:
<svg height="400" width="600"></svg>
The JavaScript:
function onDragDrop(dragHandler, dropHandler) {
var drag = d3.behavior.drag();
drag.on("drag", dragHandler).on("dragend", dropHandler);
return drag;
}
var g = d3.select("body").select("svg").append("g").data([{ x: 50, y: 50 }]);
g.append("rect")
.attr("width", 40)
.attr("height", 40)
.attr("stroke", "red")
.attr("fill","transparent")
.attr("x", function (d) { return d.x; })
.attr("y", function (d) { return d.y; })
.call(onDragDrop(dragmove, dropHandler));
g.append("text")
.text("Any Text")
.attr("x", function (d) { return d.x; })
.attr("y", function (d) { return d.y; })
.call(onDragDrop(dragmove, dropHandler));
function dropHandler(d) {
// alert('dropped');
}
function dragmove(d) {
d3.select(this)
.attr("x", d.x = d3.event.x)
.attr("y", d.y = d3.event.y);
}

Resources