I cannot calculate the area of a polygon in D3 using the function path.area()
I have tried feeding it a list of coordinates as follows:
var d = [
[-1, 415.44],
[146.93, 304.47],
[195.45, 152.13],
[-1, 134.64]
]
path.area(d)
I have also tried to feed it (what I think is) a TopoJSON object, as follows:
path.area({
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": d},
});
The first attempt gives you value 0. The second gives you 'NaN'. Do you guys know what I am doing wrong? As an FYI, I need to calculate the area of a polygon that I reference as follows:
d3.select("#IDofpolygon");
You have to use d3.polygonArea, which:
Returns the signed area of the specified polygon. If the vertices of the polygon are in counterclockwise order (assuming a coordinate system where the origin ⟨0,0⟩ is in the top-left corner), the returned area is positive; otherwise it is negative, or zero.
Here is the demo:
var d = [
[-1, 415.44],
[146.93, 304.47],
[195.45, 152.13],
[-1, 134.64]
];
var area = d3.polygonArea(d);
console.log(area)
<script src="https://d3js.org/d3.v4.min.js"></script>
Related
Salutations all and happy holidays.
I Noticed an interesting behavioral quirk while trying to draw polygon layers with L.geoJson(). consider the following code:
var polygonCoords = [
{"type": "Feature",
"properties": {"group": "Violations"},
"geometry": {
"type" : "Polygon",
"coordinates": [[
[-107.69348, 43.22519],
[-105.48523, 42.99259],
[-107.7594, 42.26105]
]]
}
}];
and
var polygons = L.polygon([
[43.22519, -107.69348],
[42.99259, -105.48523],
[42.26105, -107.7594]
]);
Now, both work in their respective contexts. I was just wondering why the coordinate matrix within L.polygon() has to be reflected in order to show up where one expects it to be when passed into L.goeJson() like so:
var jsonPoly = L.geoJson(polygonCoords, {
style: function(feature) {
if (feature.properties.group == "Violations") {
return {color: "#ff0000"};
}
}
});
Or is this an oversight within leaflet? Also, is there a way to automate this reflection with say toGeoJson(polygons)?
Thanks so much all.
When creating a geoJson layer the coordinates are expected to match the GeoJSON standard (x,y,z or lng, lat, altitude) (GeoJSON position specs)
If you have string of GeoJSON where your coordinates are not in this format, you can create your GeoJSON layer with a custom coordsToLatLng function that will handle this conversion to the standard's format (Leaflet Doc)
If you have a polygon layer and want to add it to an existing GeoJSON feature group you can do something like:
var polygons = L.polygon([
[43.22519, -107.69348],
[42.99259, -105.48523],
[42.26105, -107.7594]
]);
var gg = polygons.toGeoJSON();
var jsonFeatureGroup = L.geoJson().addTo(map);
jsonFeatureGroup.addData(gg);
map.fitBounds(jsonFeatureGroup.getBounds());
I'm trying to hide some leaflet features outside of a defined area.
I have a leaflet map displaying rivers as features on a RiverLayer and a circleLayer used to draw an area around the current center of the map.
Each river is separated in multiple parts inside my database and I retrieve only the parts intersecting with my current circle area.
The result look like this:
The rivers are showing outside the area, because I selected the parts intersecting with it.
I could select in my database all the parts within the area but I would lose the parts that are not entirely inside the area.
Calculating the intersection point for each part concerned in order to adjust the coordinates would be a solution but a complex one.
In fact, I would prefer to simply hide these overflows on the client side but I can't find a solution.
Is there a possibility with leaflet to do something like this?
Thanks for you time
Here is an example using turfJS using the booleanWithin and lineSplit functions.
I did the example on a simple basic HTML and Vanilla JS. I added another linestring on the "river" to simulate an outside the circle river
var mymap = L.map('mapid').setView([43.63458105967136, 1.1613321304321291], 13);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 20,
attribution: 'Map data © OpenStreetMap contributors',
}).addTo(mymap);
var center = [43.63458105967136, 1.1613321304321291];
var radius = 1500;
// L.circle(center, radius, {
// color: '#ff4081', fillColor: '#ff4081', fillOpacity: 0.5
// }).addTo(mymap);
var riverGeoJSON = [
{ "type": "Feature", "geometry": { "coordinates": [[1.159444487444759, 43.633815447205706], [1.160243520516838, 43.634633600388156], [1.160731009187281, 43.6350432633719], [1.161774921971743, 43.63541373375439], [1.162079879908259, 43.63564209781788], [1.162320030539753, 43.635959368371424], [1.162373764624914, 43.636409391647234], [1.161800286153361, 43.637212422659154], [1.160910734693605, 43.63832601539633], [1.160651867030764, 43.63886255455486], [1.160332394101095, 43.639317964879666], [1.159189872203288, 43.640743176542664], [1.158053840843969, 43.641810274789506], [1.156922548158863, 43.642651534145514], [1.155851918485514, 43.64349381183714], [1.155156982509935, 43.644214650781954], [1.15326441791592, 43.64594659208024], [1.152374775964331, 43.6470151231795], [1.151428904349222, 43.64790448439313], [1.151107886218696, 43.64840394819371]], "type": "LineString" } },
{ "type": "Feature", "geometry": { "coordinates": [[1.156570800342349, 43.632121495293006], [1.158291185472127, 43.63272397754135], [1.158901458643683, 43.633090727638866], [1.159444487444759, 43.633815447205706]], "type": "LineString" } },
{ "type": "Feature", "geometry": { "coordinates": [[1.168152938761366, 43.62917262321181], [1.167467920251437, 43.62939958202886], [1.166101976396903, 43.62960874939632], [1.164673843635074, 43.629863651007135], [1.163738326615552, 43.63021236020524], [1.163236303364402, 43.630566588076604], [1.162728104605807, 43.63119071739829], [1.161282685092185, 43.632253508072225], [1.160336935333006, 43.633151033736986], [1.159444487444759, 43.633815447205706]], "type": "LineString" } },
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "LineString",
"coordinates": [
[
1.0526275634765625,
43.550289946081115
],
[
1.07940673828125,
43.63334186269
],
[
1.0764884948730469,
43.6336524704596
]
]
}
}
];
// L.geoJSON(riverGeoJSON, {}).addTo(mymap);
var centerGeoJSON = [center[1], center[0]];
var radiusGeoJSON = radius / 1000;
var options = { steps: 50, units: 'kilometers' };
var circleGeoJSON = turf.circle(centerGeoJSON, radiusGeoJSON, options);
L.geoJSON(circleGeoJSON, {}).addTo(mymap);
var riverClipped = {}
for (let index = 0; index < riverGeoJSON.length; index++) {
const feature = riverGeoJSON[index];
var within = turf.booleanWithin(feature, circleGeoJSON);
console.log({ within });
var split = turf.lineSplit(feature, circleGeoJSON);
console.log({ split });
if (within && split.features.length === 0) {
L.geoJSON(feature, {}).addTo(mymap);
} else {
L.geoJSON(split.features[0], {}).addTo(mymap);
}
}
Circle is calculated with turfJS to have a valid GeoJSON feature. This feature is then used as a splitter.
When line is completely inside the circle, the within function returns true, the split function doesn't return a split feature.
When line is completely outside the circle, the within function is false and the split function doesn't return a split feature.
When the line intersect the circle, the within function returns false, and the first feature from the split feature collection is the one inside the circle.
Complete source code on JSFiddle: https://jsfiddle.net/tsamaya/6sc58m7u/
I would like to use d3.svg.diagonal() to create a Bezier curve. Someone could explain me how it works. I mean I don't understand the link between the mathematical definition https://en.wikipedia.org/wiki/B%C3%A9zier_curve and the diagonal function with source, target and projection. Where are the control points?
I mean I don't understand the link between the mathematical definition https://en.wikipedia.org/wiki/B%C3%A9zier_curve and the diagonal function with source, target and projection. Where are the control points?
the projection converts the inputs (source and target) to coords (x,y) and output as an Array
default is a 1:1 projection, no change of the values.
//pseudocode:
var src = projection(source),
trg = projection(target);
var points = [
src,
[src[0], (src[1] + trg[1]) / 2],
[trg[0], (src[1] + trg[1]) / 2],
trg
]
I have this geometric shape file, so no map of a city.
I store it in a GIS database as GeoJson. Now, I want to visualize the geojson data. I created the GeoJson data first with QGIS and exported it as Coordinate Reference System WGS 84 EPSG:4326. This is an example data of Shapefile one:
{
"type":"FeatureCollection",
"crs":{
"type":"name",
"properties":{
"name":"urn:ogc:def:crs:OGC:1.3:CRS84"
}
},
"features":[
{
"type":"Feature",
"properties":{
"Membership":0.000000,
"Membership_1":0.000000,
"Membership_2":0.000000,
"Membership_3":0.000000,
"Membership_4":0.000000,
"Membership_5":0.000000,
"Membership_6":0.000000,
"Membership_7":0.000000,
"Membership_8":0.000000,
"Membership_9":0.997638,
"Asymmetry":0.622090,
"Elliptic_F":0.368607,
"Density":1.720265,
"Radius_of_":2.122269,
"Rectangula":0.701797,
"Radius_of__1":0.341230,
"Main_direc":63.913780,
"Mean_red":251.683422,
"Mean_green":253.246326,
"Mean_blue":251.654027,
"Shape_inde":1.663047,
"Compactnes":2.373016,
"Roundness":1.781040,
"Border_ind":1.603306
},
"geometry":{
"type":"MultiPolygon",
"coordinates":[
[
[
[
0.0,
293.0
],
[
116.0,
293.0
],
[
116.0,
288.0
],
[
117.0,
288.0
],
[
117.0,
287.0
],
GeoJson Shapefile two the geometry is at the end:
{
"type":"FeatureCollection",
"crs":{
"type":"name",
"properties":{
"name":"urn:ogc:def:crs:OGC:1.3:CRS84"
}
},
"features":[
{
"type":"Feature",
"properties":{
"Ratio_red":0.337287,
"Ratio_gree":0.324566,
"Ratio_blue":0.338147,
"Asymmetry":0.233023,
"Elliptic_F":0.835821,
"Density":2.111246,
"Radius_of_":1.191572,
"Max_diff":0.040743,
"Rectangula":0.958607,
"Ratio_DSM_":1.001866,
"Diff_DSM_w":0.604676,
"LengthWidt":1.266667,
"Radius_of__1":0.894812,
"Main_direc":0.507535,
"Standard_d":4.209384,
"Standard_d_1":13.755727,
"Standard_d_2":12.358206,
"Standard_d_3":16.194083,
"Standard_d_4":21.437695,
"Standard_d_5":0.486436,
"Mean_slope":195.593284,
"Mean_slope_1":34.988806,
"Mean_red":143.451493,
"Mean_green":138.041045,
"Mean_blue":143.817164,
"Mean_DSM":324.615672,
"Shape_inde":1.038440,
"Mean_Diff_":0.604676,
"Compactnes":1.063433,
"Brightness":141.769900,
"Roundness":0.296759,
"Area_m2":1.715200,
"Border_ind":1.000000
},
"geometry":{
"type":"MultiPolygon",
"coordinates":[
[
[
[
-1.796831198293312,
46.775409744271464
],
[
-1.796815938387422,
46.775411620389058
],
The geometry is at the end of the file. I already tried things from this post but this works only for polygons and not multipolygons:
Venue/Indoor Map using D3.js and Geojson
I tried to visualize both with the following code:
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
//Width and height
var w = 800;
var h = 800;
var colors = d3.scale.category20();
var projection = d3.geo.mercator()
.translate([w/2, h/2]);
var path = d3.geo.path()
.projection(projection);
//Define path generator
var path = d3.geo.path();
//Create SVG element
var svg = d3.select("body").append("svg").attr({width: w, height: h});
//Load in GeoJSON data
d3.json("imageOne.json", function(json) {
//Bind data and create one path per GeoJSON feature
svg.selectAll("path")
.data(json.features)
.enter()
.append("path")
.attr("d", path)
.style("fill", function(d,i){return colors(i)});
});
</script>
After running the script I get for the first data the following result:
Trying the script for the second image I get a white page.
I uploaded the two shape files
Shapefiles
I struggled with this type of issue for days. Turns out the coordinate system used to serialize the map was projected instead of geometric, meaning that the data was already stored as x and y values on a 2d plane, not coordinates on a sphere.
Mike Bostock explains it very where in this google groups post:
https://groups.google.com/forum/#!topic/d3-js/OSp_sMZjfok
The issue is that d3.geo.projection is primarily intended for converting spherical coordinates to Cartesian coordinates, so when you create a d3.geo.projection instance from a raw projection function, it assumes spherical coordinates. Meaning, it assumes your raw projection function takes radians λ and φ as input, converts the input coordinates from degrees to radians, and performs adaptive resampling on the output.
All of which makes it great for implementing new geographic projections, but you’ll probably want to take a different route for implementing a custom Cartesian projection.
One approach is to implement a custom geometry stream. This is a lower-level API that lets you control exactly how the geometry is transformed, and is suitable for a simple scale and translate:
http://bl.ocks.org/mbostock/6216797
So armed with this knowledge, of course pumping the points thru a projection that expects the data to be spherical is going to result in a big mess.
If I viewed the shapefile or geojson in QGIS application, at the bottom right it shows the Coordinate Reference System (CRS) used to encode the values. In my case it was using 5320 (which is projected/2d) instead of something like 4326 (which is a geographic coordinate system)
I need to plot 3 series of data, the first is a line and the other 2 are just dots. However the line should be a step chart (instead of the line drawing from point to point, it should draw the line horizontal and then up to the value
I am stuck as to how to get this with JQPlot.
$(document).ready(function(){
var plot1 = $.jqplot ('chart1', [[3,7,9,1,4,6,8,2,5]]);
});
The above code would produce the blue line on the below graph, instead I need the green line.
Unfortunately, I am not allowed to make comments. Therefore I have to write a new answer.
The already given answer suggests to subtract a small amount from the x value (0.001 in this example) to prevent the triangle effect. But this is not quite accurate and can only be seen as workaround.
The triangle effect is caused by the sorting performed by jqPlot. Sorting is required by most chart types, including line charts. If the data is already sorted before feeding it to jqPlot, sorting can be disabled for jqPlot by setting the sortData attribute to false, see jqPlot.sortData
This will prevent the sorting issues and therefore no triangle effect occurs!
You may also want to hide the point markers as jqPlot doesn't know the difference between the real points and our injected artificial points.
var data = [3, 7, 9, 1, 4, 6, 8, 2, 5];
var points = [[1, data[0]]];
for (var i = 1; i < data.length; i++) {
points.push([i + 1, data[i - 1]]);
points.push([i + 1, data[i]]);
}
var plot1 = $.jqplot('chart1', [points], {
sortData: false,
seriesDefaults: {
showMarker: false
}
});
Try it in a fiddle
If you want to get also the point markers right, the only option I see is changing the rendering logic, e.g. writing a step chart plugin.
For reference, see also the answers in the following post: jqPlot step chart not plotting in series order
You need to specify both the x and y value for each point on the graph. The tricky thing is, if two points have the same x value, jqplot may reverse them, which winds up looking like a triangle plot rather than a square. So, the solution is to take each point after the first, subtract a small amount from the x value (in my example, 0.001), and make that the x value for a new point that has the same y value as the point before it. Here's a hard-coded example:
var plot1 = $.jqplot ('chart1', [[
[1,3], [1.999,3],
[2,7], [2.999,7],
[3,9], [3.999,9],
//...
]]);
Try it in a fiddle.
To create such a list in code, just loop over the original data set and add the necessary extra steps:
var data = [3,7,9,1,4,6,8,2,5];
var points = [[1, data[0]]], len = data.length;
for (var i = 1; i < len; i++) {
points.push([i + .999, data[i - 1]]);
points.push([i + 1, data[i]]);
}
var plot1 = $.jqplot ('chart1', [points]);
Try it in an updated fiddle.