问题
I have some geoJson data that I am charting using d3.geo.
When I write something like
d3.select("svg")
...
.attr("d", function(d) {
return path({
type:"MultiPoint",
coordinates: get_activity_coords_(d.activities)
});
})
I always get a circle for each coordinate. The coordinates represent locations of various stopping points of a journey. What I would prefer is a different shape for the first and the last coordinate.
Is it possible to do this using MultiPoint, is there an example that I can follow? I could draw the points one by one, but I recall reading that MultiPoint is far faster. Plus, the code would be much clearer to read.
Thanks a lot.
回答1:
You can't do different shapes for MultiPoint geoJSON with d3.geo.path
. You can change the radius based on a function, but it looks like you can only set it per feature and not per point, so you'd have to break your set of points into multiple features and lose any performance benefit from using the single element.
However, there are other ways to go about doing this.
One option, as you mentioned, is to create a nested selection with a separate <path>
element for each point, and draw each path using a d3.svg.symbol()
function. You can then customize the symbol function to be based on data or index.
var trips = d3.select("svg").selectAll("g.trips")
.data(/*The data you were currently using for each path,
now gets to a group of paths */)
.attr("class", "trips");
//also set any other properties for the each trip as a whole
var pointSymbol = d3.svg.symbol().type(function(d,i){
if (i === 0)
//this is the first point within its groups
return "cross";
if ( this === this.parentNode.querySelector("path:last-of-type") )
//this is the last point within its group
return "square";
//else:
return "circle";
});
var points = trips.selectAll("path")
.data(function(d) {
return get_activity_coords_(d.activities);
//return the array of point objects
})
.attr("transform", function(d){
/* calculate the position of the point using
your projection function directly */
})
.attr("d", pointSymbol);
Another option, which allows you to set custom shapes for the first and last point (but all intermediary points would be the same) is to connect the points as the vertices of a single, invisible <path>
element and use line markers to draw the point symbols.
Your approach would be:
Create a
<defs>
element within your SVG (either hard-coded or dynamically with d3), and define the start, middle and end marker points within them. (You can used3.svg.symbol()
functions to draw the paths, or make your own, or use images, it's up to you.)Use a
d3.svg.line()
function to create the path's "d" attribute based on your array of point coordinates; the x and y accessor functions for the line should use the projection function that you're using for the map to get the x/y position from the coordinates of that point. To avoid calculating the projection twice, you can save the projected coordinates in the data object:var multipointLine = d3.svg.line() .x(function(d,i) { d.projectedCoords = projection(d); return d.projectedCoords[0]; }) .y(function(d){ return d.projectedCoords[1];});
(You can't use your
d3.geo.path()
function to draw the lines as a map feature, because it will break the line into curves to match the curves of longitude and latitude lines in your map projection; to get the line markers to work, the path needs to be just a simple straight-line connection between points.)Set the style on that path to be no stroke and no fill, so the line itself doesn't show up, but then set the
marker-start
,marker-mid
andmarker-end
properties on the line to reference the id values of the correct marker element.
To get you started, here's an example using d3 to dynamically-generate line markers:
Is it possible to use d3.svg.symbol along with svg.marker
来源:https://stackoverflow.com/questions/22529038/in-d3-geo-multipoint-how-can-i-provide-different-shapes-for-different-poins