D3 V4 zoom.transform jump on drag - d3.js

I'm brand new to D3 and am reading up here on how to set the current zoom transform but I'm having troubles comprehending what I'm doing wrong.. I'm using the following line to initially center a circle on the page.
g.call(zoom.transform, d3.zoomIdentity.translate(width / 2, height / 2).scale(2));
Here is a fiddle of the issue. Just click and drag anywhere in the result window. You should see the circle instantly jump to the upper left corner.
https://jsfiddle.net/hjukmqjv/3/
Am I using the incorrect method to achieve my goal perhaps?

you simply need to set the call on the svg
svg.call(zoom.transform, d3.zoomIdentity.translate(width / 2, height / 2).scale(2));

First of all, You're invoking zoom two times with svg, and g. With the g the transformation is applied (the circle is in the center of the page).
Then apply the svg zoom (with the zoom in/out event)that visualize the circle in it's (0,0) screen position and produce the jump.
However if you want to see the circle in the centre just put the circle in that position inserting the correct attribute to circle:
var g = svg.append("g");
g.append("circle")
.attr("r", 50)
.attr("cx",width / 2)
.attr("cy",height / 2)
.style("fill", "#B8DEE6");
and use the zoom on the svg element.
Updated fiddle
Hope it helps.

Related

D3 graph with 3 sides?

I am creating a d3 contour graph with the d3-contour plugin. I am creating grid lines using the tickSize function e.g.
const yAxis = d3.axisLeft(yScale).ticks(4).tickSize(-dimensions.width);
However - I only want the grid borders to be on the top, bottom and right hand side. Not on the left at all. Is there a way to do this?
Thanks!

D3 polygon projection is wrong

I have a working world projection using D3 geoOrthographic and topoJSON. It rotates and everything.
I wanted to place a hexagon shape at a coordinate and projected according to its place on the globe, which works... except my shape is really weird. I get a 5-sided shape, like one of the points is just missing, that rotates properly with the globe.
And then also a circle around the edge of the globe that does not rotate.
I have a function that throws out hexagon coordinates, I've tried with several scales and offsets, always the exact same behavior.
let hex = svgOrbit.append("path")
.datum({"type":"GeometryCollection","geometries":[{"type":"Polygon","coordinates":[[[6.732,6],[5,7],[3.268,6],[3.268,4],[5,3],[6.732,4]]]}]})
.attr("d", myGeoOrthographicProjection);
The circle looks like that no matter how I rotate, the trying-to-hexagon orients as desired sans that missing point.
The path does show a d attr with these two separate polygons.
I just plain don't understand what's happening here. There aren't even any weird numbers, like a zero or NaN or anything in the coordinates. The entire planet projects correctly, but a hexagon throws it for a loop?
The outer circle indicates that you have an inverted polygon: you are drawing a feature of the world minus the intended feature. As d3 uses spherical math in calculating projections, winding order matters, as opposed to most geographic tools which treat spherical coordinates as Cartesian (even when projecting). The first map below in red shows this by applying a fill.
The missing point is a bit odd, normally D3 won't render invalid geojson syntax and it won't throw an error or warning in not rendering anything. The issue here is that the last point in your coordinate array should be the first coordinate. I've forgotten where in the spec this is, and haven't looked as to why D3 renders it like this at all. When attempting to take a look at your geojson at geojson.io I noticed it didn't render at all with the missing end point.
I've rewound the coordinates (lazily with .reverse()) and added the extra point in the map on the right.
let hex = {"type":"GeometryCollection","geometries":[{"type":"Polygon","coordinates":[[[6.732,6],[5,7],[3.268,6],[3.268,4],[5,3],[6.732,4]]]}]};
let hex2 = {"type":"GeometryCollection","geometries":[{"type":"Polygon","coordinates":[[[6.732,6],[5,7],[3.268,6],[3.268,4],[5,3],[6.732,4],[6.732,6]].reverse()]}]};
let projection = d3.geoOrthographic().scale(125).translate([125,125]);
let path = d3.geoPath(projection);
let svg = d3.select("body")
.append("svg")
.attr("width", 500)
.attr("height", 250);
svg
.append("path")
.datum(hex)
.attr("d", path)
.attr("fill", "crimson");
svg.append("g")
.attr("transform","translate(250,0)")
.append("path")
.datum(hex2)
.attr("d", path)
.attr("fill","steelblue");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

How to rotate points from csv using orthographic projection d3

I want to ask you for a help. I have copied this code (see: http://bl.ocks.org/d3noob/5193723). I've changed the projection to orthographic and still loading points from csv file. Is it possible to rotate the globe also with points rotating?
I have only found a possibility to rotate the globe but my points are not rotating.
I would be gratefull for any help. Thanks.
It would be useful if you post the code when asking questions, this helps the community to better understand the question. In the example that you mention, the callback of the zoom event of the zoom behavior scales and translates the container group of the features. If you want to rotate the globe, you should change the rotation of the projection instead, and update the paths of the features.
I recently made a gist showing how to create a celestial sphere with the orthographic projection, and drawing the stars as small circles. In this gist, the drag behavior is used to rotate the globe.
// Rotate the sphere with drag behavior
var dragBehavior = d3.behavior.drag()
.origin(Object)
.on('drag', function(d) {
projection.rotate([(d.x = d3.event.x) / 2, -(d.y = d3.event.y) / 2]);
svg.selectAll('path').attr('d', function(u) {
// The circles are not properly generated when the
// projection has the clipAngle option set.
return path(u) ? path(u) : 'M 10 10';
});
});
Note that there are better ways to rotate a sphere, for a better strategy, see Jason Davis' article on rotating maps. Regards,

sync d3.js map and leaflet map

In one application I have both d3 map and leaflet map, I follow Mike's code to sync them(http://bost.ocks.org/mike/leaflet/) and draw svg points on leaflet map. The problem is that some points being cutoff at the edge of SVG container, and other SVG elements added later on (an animated pulse in this case) will only partially shown. I wonder if there's anyway to expand the SVG container based on the features (points) bound.
a working demo is here: http://xqin1.github.io/usschools/usac_school_stat.html, to reproduce the issue:
1. select the 'Number of Students' slider bar to 5300-6545, the two points on Leaftlet map only half shown.
2. click the first table row, map will zoom to the point, but the animated pulse being cut off.
Any suggestions are highly appreciated.
thanks
xiaoming
I had this problem as well, and found a solution. Referencing mbostock's sample code you just need to add a little bit of padding to the projected bounding box that is calculated each time reset is called:
var bottomLeft = project(bounds[0]),
topRight = project(bounds[1]);
Insert something like this (just after the above declarations):
var padding = 25; // In pixels, choose large enough to prevent edge clipping
// of your largest element
bottomLeft = [bottomLeft[0]-padding, bottomLeft[1]+padding]
topRight = [topRight[0]+padding, topRight[1]-padding]

D3 force layout: making pan on drag (zoom) smoother

I've got a d3.js static force layout graph that can get rather big (sometimes parts of it are clipped), so I'd like to let the user pan the whole graph by dragging. I don't think I need dragging of individual nodes, I've got a feeling that's just going to be confusing, but would like to make it possible to show the parts of the graph that are clipped by the svg boundaries.
I've got a minimal example at http://bl.ocks.org/3811811 which uses
visF.append("rect")
.attr("class", "background")
.attr("width", width)
.attr("height", height)
.call(d3.behavior.zoom().on("zoom", redrawVisF));
function redrawVisF () {
visF.attr("transform","translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
}
to implement the panning, but I find it really "skittery" and not very smooth at all, to the point where I'm guessing it will stop people from trying the drag function at all. Has anyone got a clue why this happens and/or an idea for how to fix it?
The problem is that d3.behavior.zoom retrieves the current mouse position relative to the clicked item's container element, and you are moving the container element! So the relative position is constantly changing, hence the jittering effect you're seeing.
You probably want to move the background <rect> so that it's a direct child of the <svg> element. This achieves two things:
The position will now be relative to the <svg> container, which isn't moving.
Currently, you are moving the <rect> when you zoom or pan, so the zoomable area changes and some parts of the viewport are no longer zoomable. Having the background <rect> in the same place fixes this problem too.

Resources