The answer below on drawing a polygon works well for a single polygon. However, what if there are more than one polygon? Simply adding an additional polygon with points seems not to work even though using "select all" would seem to indicate that it would be OK to add a couple more polygons without much problem..
We have an array of polygons, each of which has an attribute Points which is an array of points.
The first array with polygon should obviously be mapped and the point arrays of each member processed as described. But how to spedify this two-level structure with d3?
Proper format for drawing polygon data in D3
The answer is simple and straightaway. Just pass the array of polygons as data to d3 selection.
In your case it seems that you are using an array of polygons which are composite objects, each having a key called 'Points'. I assume it looks something like this-
var arrayOfPolygons = [{
"name": "polygon 1",
"points":[
{"x":0.0, "y":25.0},
{"x":8.5,"y":23.4},
{"x":13.0,"y":21.0},
{"x":19.0,"y":15.5}
]
},
{
"name": "polygon 2",
"points":[
{"x":0.0, "y":50.0},
{"x":15.5,"y":23.4},
{"x":18.0,"y":30.0},
{"x":20.0,"y":16.5}
]
},
... etc.
];
You will just have to use d.Points instead of d when writing the equivalent map function, which can be written as follows-
vis.selectAll("polygon")
.data(arrayOfPolygons)
.enter().append("polygon")
.attr("points",function(d) {
return d.points.map(function(d) {
return [scaleX(d.x),scale(d.y)].join(",");
}).join(" ");
})
.attr("stroke","black")
.attr("stroke-width",2);
You can check the following working JSFiddle to verify.
EDIT- The same example as above with convex hull implementation for rendering complete polygons. http://jsfiddle.net/arunkjn/EpBCH/1/ Note the difference in polygon#4
Related
I am using data driven styling to style a Mapbox vector layer for a choropleth map. However, rather than getting the data strictly from the layer properties, I need to use statistic data from a separate object (pulled from our database). This separate object contains one statistic value for each polygon in the vector layer. The object maps the stat values to the vector layer polygons by a variable called "GEOID". In order to marry the polygon with its data, I would like to use a Mapbox expression to get the GEOID value from each polygon in the vector layer and pass this id to a separate function to get the statistic value for the polygon having this GEOID. Is this possible?
vectorLayer: {
id: "fooLayer",
type: "fill",
"source-layer": "foo-layer-dvf1ci",
paint: {
"fill-color": [
"rgba",
100,100,100,
this.getStatForDistrict(["get", "GEOID"])]
]
}
},
getStatForDistrict(districtId) {
console.log("districtId: " + districtId);
let alphaValue = fetchDataForThisDistrictFromDatabase(districtId)
return alphaValue;
},
I see that currently, I am passing ["get", "GEOID"] into the function getStatForDistrict when what I actually need is to pass the computed Mapbox expression.
More on Mapbox-GL's paint property: https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#paint-property
More on Mapbox-GL's expressions syntax: https://docs.mapbox.com/mapbox-gl-js/style-spec/expressions/
No, there is not any way to call arbitrary functions from within expressions, which look up data that doesn't exist within the feature in question.
There are several ways to make choropleths. I'd suggest you start by looking at the mapbox-choropleth library.
I'm currently trying to map a sphere with squares using d3.js and GeoJon. I've found a script which generate a grid using MultiPolygon : https://jsfiddle.net/94dhdmnw/
{
type: 'FeatureCollection',
features: [{
type: 'Feature',
geometry: {
type: 'MultiPolygon',
coordinates: [coordinates]
}
}]
}
Nonetheless, I need an id for each square of my grid. That's why I want to map my sphere using Polygons instead of MultiPolygons. Thus, I've adapted the script in order to get a Polygon mapping : https://jsfiddle.net/et3s6p33/
Yet, when I map my sphere using the file obtained through the Polygon script, I have only half of my sphere which is mapped - not even with the right squares' size.
Sphere with MultiPolygon mapping :
Sphere with Polygon mapping :
Any ideas why?
I am using this script to map my sphere : http://jsfiddle.net/6n230b3w
Edit : Or maybe there is a way to set a property for each square of my MultiPolygon?
I'm trying to only display the line intersections within graticule and not the whole lines within a D3 map.
Is there any support to do that with d3.geo.graticule?
Thanks very much for your help!
Thierry
The short answer to your question is: no, there is no way to only render the intersections with d3.geo.graticule.
But the desired result could be achieved with little effort by putting the intersections you want to be drawn into a helper array which is passed to a path generator for rendering. I forked one of my Plunks to demonstrate the solution:
Set up a helper array containing geometry objects to be passed to the path generator.
// Generate two-dimensional array containing the coordinates of all
// intersections we are interested in.
var lonLatIntersections = d3.range(-180, 180, 10).map(function(lat) {
return d3.range(0, 360, 10).map(function(lon) {
// Return geometry objects that can be handled by the path generator.
return {type: "Point", coordinates: [lon,lat]};
});
});
Use a nested selection to bind the two-dimensional (lat/lon) array and use the enter selections to append the intersection points. The actual drawing is done by supplying the geometry objects to the path generator which will take into account the projection.
// Do a nested selection to bind the two-dimensional data.
map.selectAll("g.lat")
.data(lonLatIntersections)
.enter().append("g") // Create groups per latitude.
.classed("lat", true)
.selectAll("circle")
.data(function(d) { return d; }) // Data binding for longitudes
.enter().append("path")
.attr({
"d": path, // Use the path generator to draw points
"class": "confluence"
});
I am using the pack layout for packing different no of equal sized circles. I have a group of clusters to be visualized. So I am calling pack function for each cluster of circles. In all the d3 examples the diameter is either calculated with the given size or fixed diameter. I would like to calculate it according to the no of circles to be packed. So how do I calculate the packing circle diameter?
is there any formula so that I can pack the circles without wasting the space.
If you truly don't care about relative sizing of the circles, then you could make your JSON file represent only the data you care about(say, names) and feed your packing function a dummy value that the 'value' accessor function is expecting.
For instance:
var circleChildren = [{
"value": 1
}, {
"value": 1
}, {
"value": 1
}, {
"value": 1
}];
would give you a JSON object that you can use as children for your packing function:
var circleInput = Object();
circleInput.children = circleChildren;
You can verify that in your console by running:
bubble.nodes(circleInput)
.filter(function (d) {
return !d.children; //we're flattening the 'parent-child' node structure
})
where bubble is your D3 packing bubble variable.
Here's a fiddle that demonstrates that. It may have some extra things but it implements what you're looking for. In addition, you can play around with the number of circles by adding more dummies in the JSON file, as well as changing the SVG container size in the diameter variable. Hope that helps!
EDIT: The size of your layout(in this case, a misnomer of the 'diameter' variable) directly determines the size and diameter of your circles within. At some point you have to assign the pack.size() or pack.radius() value in order for your circles to display within a layout(documentation ):
If size is specified, sets the available layout size to the specified two-element array of numbers representing x and y. If size is not specified, returns the current size, which defaults to 1×1.
Here you have several options:
If you want your circles to be 'dynamically' sized to your available element's width (that is, if you want them to cover up all the element width available) then I'd recommend you get your element's width beforehand, and then apply in your pack() function. The problem is then you have to think about resizing, etc.
If you want to keep the maximum sizing available, then you have to make your viz responsive. There's a really good question already in SO that deals with that.
I know this isn't the full solution but hopefully that points you in the right direction for what you're trying to do.
FURTHER EDIT:
All of a sudden, another idea came to mind. Kind of an implementation of my previous suggestion, but this would ensure you're using the maximum space available at the time for your circle drawing:
zone = d3.select("#myDiv");
myWidth = zone.style("width").substring(0, zone.style("width").length - 2);
Is there any way I could iterate through all circle elements that I added to a graph? For example, let's say I created four circles on a graph with specific id values. If I want to find a circle element with a specific id, how should I got about doing that?
You would do something like
svg.selectAll("circle").filter(function(d) { return d.id == myid; });
assuming that you have bound data to the circles that contains an .id attribute.