in d3.geo MultiPoint how can I provide different shapes for different poins?

天大地大妈咪最大 提交于 2021-02-19 04:47:04

问题


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:

  1. 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 use d3.svg.symbol() functions to draw the paths, or make your own, or use images, it's up to you.)

  2. 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.)

  3. 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 and marker-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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!