I am using D3 to render a vector map, based off of a GeoJSON dataset.
I have an existing path and I need to transform the path using a tween. To do this I need to use D3 to lookup the current projection object for this path. However, I can't seem to figure out the syntax to achieve this. Is it possible to look up the projection of an existing D3 path?
I can look up the path with:
const mapPath = d3.select('path.states');
But, I'm not sure where to go from there to get the projection object from this path. Thanks for any help or direction you might be able to provide.
Since (based on the discussion in the comments) it sounds like the code that used a projection to render the map lives in the same app as the code that needs that projection elsewhere in the app, then this is really a question of app architecture — not projection detection. There are certainly ways to architect it such that the projection is "handed down" to the subroutine that need it, but that really depends on the frameworks and your implementation so far etc.
So without getting into architecture, another way to do this, is following Mark's suggestion (and in fact indicated by the code snippet in your original question) using d3's data binding to hang the projection off of the DOM node that the projection helped produce. IMO, compared with an app architecture solution, using data binding for this purpose is the lesser option, because it could make your code harder to follow bug prone. But it will work and, in a simple app at least, it's reasonable to do it this way. So...
Presumably, somewhere you have code that created and rendered the paths, and it might look something like this:
var projection = d3.geo.mercator();
var mapG = svg.append('g').attr('class', 'map');
var mapG.selectAll('path').data(myMapData).enter()
.append('path')
.attr("d", d3.geo.path().projection(projection));
I'm sure yours looks different, but hopefully it's close to the above, because I need to point out that the are N paths, corresponding to N entries in myMapData, and that each path already has a datum associated with it (the datum of the geo feature it represents). So a good place to hang your projection is NOT on the paths (because there are multiples, and they have have data bound to them already) but rather on the parent of the paths — mapG in this case. The way you do it is using .datum() to bind the projection to the DOM node:
mapG.datum(projection);
Now, from that other place in your code, where you need to look up the projection, you'd use datum() to extract it:
var mapG = d3.select('svg g.map')
var projection = mapG.datum()
Related
I am using the natural earth 10m files to reduce, simplify, and quantize them into TopoJSON. However, for my use case, I'm attempting to store the data on the server and request a country or region I am currently interested in to display with d3. Because of this I need to keep my file size fairly small.
I was able to write some hacky code to strip out the region geometry data and arcs from the full TopoJSON file and serve that at a reasonably small size. Unfortunately, I've come to realize that doing a mesh on this does not give me any results. If, however, I do not reindex all of the arcs and instead just inject empty arrays into the arc positions I don't need, mesh works fine.
Basically, my question is, is there a way for me to strip out a specific country or region from a large TopoJSON file? I don't mean just the geometry object since that's easy enough to do, but also reduce the main arcs array to only include what is needed. I'm struggling to find a clean way to do this.
Thanks!
I need to be able to draw links between the circles in a circle pack layout, like {source: i, target: j}. I've seen some questions about how to combine this layout with the force layout that made me think It could be a way to achieve this but I haven't had any luck on that path, also what I need is something completely static. Thank you in advance for any suggestions.
I finally could draw lines between nodes by appending them to the svg and calculating each line position based on the radius of the nodes that each line were to connect, but the problem was that the pack layout that I'm using is zoomable, so when I tried to zoom by clicking the lines neither translate nor re-sized (of course, since there was no code performing such things), so I tried to implement a translation function for the lines so they would move along with the zoom, then I saw that it would take some serious geometry to achieve this, so since I'm a lazy programmer and not at all experienced with d3 I tried to come with something more simple... at last I decided to represent the links between the nodes with a different approach (without lines), I used the "mouseover" event so when the pointer is over a node it will highlight (changed circle stroke attribute color and width) the nodes connected to it. This way I achieved my goal and the view looks a lot cleaner without all the lines crossing over it (they were a lot). I know that some may think that this does not answer the original question, and I'm sure that this can be achieved by someone with more experience (and time and patience) using lines and implementing the right calculation, but in my case this solution solves my problem and maybe it could be of help to others.
I'm looking at plotting a scatterplot with a large number of points (500,000 and upwards).
Currently, we're doing this in Python with Matplotlib. It plots the points, and it provides controls to pan and zoom. I don't believe it provides any clustering or points, it just plots them all - doesn't make much sense at the zoomed out view, I suppose, but you can zoom in and they're all there.
I was looking at doing the chart in JavaScript, to make it a bit easier to distribute. I was looking at D3.js, to see if something similar is feasible there. I did find this example of a basic scatterplot:
http://bl.ocks.org/mbostock/3887118
Firstly, would you be able to plot that number of points? (500,000 and upwards) I was under the impression you couldn't due to the overhead of all the DOM objects? Are there ways around this?
Secondly, is there any kind of clustering available, either a library or even just an example of this being done in D3.js?
Thirdly, if anybody knows any good examples of pan/zoom functionality and clustering, or even just a packaged JS library that handles it, that would be awesome.
Fourth, it would be also nice to have click handlers for each point - and to display some text either in a overlay, or even just in a separate window. Any thoughts on this?
Can you draw half a million points with D3? Sure, but not with SVG. You'll have to use canvas (here's a simple example with 10,000 points that includes brush-based selection: http://bl.ocks.org/emeeks/306e64e0d687a4374bcd) and that means that you no longer have individual elements to assign click handlers to. You will not be able to render half a million points with SVG, because all those DOM elements will choke your interface, as you mentioned.
D3 does include quadtree support that can be leveraged for clustering. It's in use in the above example to speed up search but you could use it to nest elements in proximity at certain scales.
Ultimately, your choices are:
1) Some other library/custom implementation that renders in canvas and polls the mouse position to give you the data element rendered at that point.
2) A sophisticated custom D3 approach that nests elements in proximity and only renders SVG elements appropriate at the zoom level and canvas position (pan) you're at.
Yes, D3.js can be made to work with million scale data with two things:
pre-rendering on the server side. For more see here: https://mango-is.com/blog/engineering/pre-render-d3-js-charts-at-server-side/
By aggregating (or clustering) part of the data so that user can interact and expand the graph if need be. For this use collapsible nodes if you can (http://bl.ocks.org/mbostock/1062288).
Also avoid using force layout. It takes time to settle and converge to a stable positioning.
For clustering libraries, I would pick one up off the shelf. I would choose the scikits library from python, there are many in JavaScript but they are not very robust as they mostly cover k-means or hierarchical clustering. I would precalculate the coordinates using scikits by clustering and then render it using D3.
D3 handles Pan and zoom. Again click handlers and text display are available in D3. (http://bl.ocks.org/robschmuecker/7880033)
Good morning,
just starting with the awesome d3js library ...
I want to show only a portion of a graph with the force directed layout. The idea is to have one node declared as the "center" and show all nodes within a distance of two (for example) from this center node, the neighbors of the center node and the neighbors of the neighbors. If the user clicks on one of the displayed nodes it becomes the "new" center node and a different "subgraph" is displayed. I wonder if there is an example around implementing this kind of subgraph layout and if some kind of a "node-distance" algorithm is already implemented in d3js.
Thanks a lot
martin
UPDATE:
Just found the example Modifying a Force Layout which illuminates how to add and remove nodes and edges from a force directed layout.
I just uploaded a "proof of concept level" of an interactive force directed subgraph.
http://justdharma.com/d3/sub-graph/
In this example I use backbonejs under the hood. Being the first time I implement something with backbonejs, I for sure use it in a very crude manner. While this example might illuminate one way how an interactive sub-graph can be achieved it is for sure not a template how to do it - as said just a proof of concept hack ...
This isn't implemented in D3, and I'm not aware of any examples. What you would have to do is the following:
Set the fixed attribute of the new center node to true to prevent the force layout from changing its position.
Set the px and py attributes of that same node to the center position.
For each node in your force layout, compute the shortest path to the new center node.
Depending on the length of the path in each case, either remove the node or keep it.
The trickiest part here is the computation of the path from each node to the new center, but even this is a pretty standard algorithmic problem. Another thing to keep in mind is that you need to modify the data structures that contain the nodes and links of the force layout in place, i.e. you can't set new nodes and links for the force layout and expect everything to work smoothly.
I have an svg element composed of many different path objects, each of which represents one U.S state.
http://jsfiddle.net/jGjZ2/
I would like to merge the east territory (gold) into a single path object with no visible divisions.
The end result should look like this (ignore the inaccuracies):
I am using D3.
There is no GeoJSON or TopoJSON data - the map is svg directly embedded in html (see jsfiddle).
Thanks a lot!
Assuming you can ignore the stated restriction of manipulating an existing SVG image (which seems like an arbitrary restriction given the ready availability of cartographic boundaries in more easy-to-manipulate formats…), you can use topojson.mesh to merge multiple polygons. Though, note this approach has a few limitations as described in this example:
Another simple approach is to just draw the highlighted polygons twice: once with a thick black stroke and no fill, and a second time on top with orange fill and no stroke. This achieves the same effect without any need for topological manipulation:
I suppose if you really had to, you could reach into the SVG element and do the same thing by extracting the vector data, but it will be easier if you start from clean data.