D3: Search for a geo-polygon area in d3

I have a map that uses a geoJSON file to draw countries. Then I want to draw a circle focused on each country. But for countries with several bordering regions (the US has a mainland, Hawaii, Alaska), I want the circle to be the largest bordering region. I try to do this by comparing areas of different bounding areas, but it doesn’t work for reasons that I cannot understand.

Here is an example from geoJSON showing how Australia has several limiting areas:

{"type":"Feature","properties":{"name":"Australia"},"geometry":{"type":"MultiPolygon","coordinates":[[[[145.397978,-40.792549],[146.364121,-41.137695],[146.908584,-41.000546],[147.689259,-40.808258],[148.289068,-40.875438],[148.359865,-42.062445],[148.017301,-42.407024],[147.914052,-43.211522],[147.564564,-42.937689],[146.870343,-43.634597],[146.663327,-43.580854],[146.048378,-43.549745],[145.43193,-42.693776],[145.29509,-42.03361],[144.718071,-41.162552],[144.743755,-40.703975],[145.397978,-40.792549]]],[[[143.561811,-13.763656],[143.922099,-14.548311],[144.563714,-14.171176],[144.894908,-14.594458],[145.374724,-14.984976],[145.271991,-15.428205],[145.48526,-16.285672],[145.637033,-16.784918],[145.888904,-16.906926],[146.160309,-17.761655],[146.063674,-18.280073],[146.387478,-18.958274],[147.471082,-19.480723],[148.177602,-19.955939],[148.848414,-20.39121],[148.717465,-20.633469],[149.28942,-21.260511],[149.678337,-22.342512],[150.077382,-22.122784],[150.482939,-22.556142],[150.727265,-22.402405],[150.899554,-23.462237],[151.609175,-24.076256],[152.07354,-24.457887],[152.855197,-25.267501],[153.136162,-26.071173],[153.161949,-26.641319],[153.092909,-27.2603],[153.569469,-28.110067],[153.512108,-28.995077],[153.339095,-29.458202],[153.069241,-30.35024],[153.089602,-30.923642],[152.891578,-31.640446],[152.450002,-32.550003],[151.709117,-33.041342],[151.343972,-33.816023],[151.010555,-34.31036],[150.714139,-35.17346],[150.32822,-35.671879],[150.075212,-36.420206],[149.946124,-37.109052],[149.997284,-37.425261],[149.423882,-37.772681],[148.304622,-37.809061],[147.381733,-38.219217],[146.922123,-38.606532],[146.317922,-39.035757],[145.489652,-38.593768],[144.876976,-38.417448],[145.032212,-37.896188],[144.485682,-38.085324],[143.609974,-38.809465],[142.745427,-38.538268],[142.17833,-38.380034],[141.606582,-38.308514],[140.638579,-38.019333],[139.992158,-37.402936],[139.806588,-36.643603],[139.574148,-36.138362],[139.082808,-35.732754],[138.120748,-35.612296],[138.449462,-35.127261],[138.207564,-34.384723],[137.71917,-35.076825],[136.829406,-35.260535],[137.352371,-34.707339],[137.503886,-34.130268],[137.890116,-33.640479],[137.810328,-32.900007],[136.996837,-33.752771],[136.372069,-34.094766],[135.989043,-34.890118],[135.208213,-34.47867],[135.239218,-33.947953],[134.613417,-33.222778],[134.085904,-32.848072],[134.273903,-32.617234],[132.990777,-32.011224],[132.288081,-31.982647],[131.326331,-31.495803],[129.535794,-31.590423],[128.240938,-31.948489],[127.102867,-32.282267],[126.148714,-32.215966],[125.088623,-32.728751],[124.221648,-32.959487],[124.028947,-33.483847],[123.659667,-33.890179],[122.811036,-33.914467],[122.183064,-34.003402],[121.299191,-33.821036],[120.580268,-33.930177],[119.893695,-33.976065],[119.298899,-34.509366],[119.007341,-34.464149],[118.505718,-34.746819],[118.024972,-35.064733],[117.295507,-35.025459],[116.625109,-35.025097],[115.564347,-34.386428],[115.026809,-34.196517],[115.048616,-33.623425],[115.545123,-33.487258],[115.714674,-33.259572],[115.679379,-32.900369],[115.801645,-32.205062],[115.689611,-31.612437],[115.160909,-30.601594],[114.997043,-30.030725],[115.040038,-29.461095],[114.641974,-28.810231],[114.616498,-28.516399],[114.173579,-28.118077],[114.048884,-27.334765],[113.477498,-26.543134],[113.338953,-26.116545],[113.778358,-26.549025],[113.440962,-25.621278],[113.936901,-25.911235],[114.232852,-26.298446],[114.216161,-25.786281],[113.721255,-24.998939],[113.625344,-24.683971],[113.393523,-24.384764],[113.502044,-23.80635],[113.706993,-23.560215],[113.843418,-23.059987],[113.736552,-22.475475],[114.149756,-21.755881],[114.225307,-22.517488],[114.647762,-21.82952],[115.460167,-21.495173],[115.947373,-21.068688],[116.711615,-20.701682],[117.166316,-20.623599],[117.441545,-20.746899],[118.229559,-20.374208],[118.836085,-20.263311],[118.987807,-20.044203],[119.252494,-19.952942],[119.805225,-19.976506],[120.85622,-19.683708],[121.399856,-19.239756],[121.655138,-18.705318],[122.241665,-18.197649],[122.286624,-17.798603],[122.312772,-17.254967],[123.012574,-16.4052],[123.433789,-17.268558],[123.859345,-17.069035],[123.503242,-16.596506],[123.817073,-16.111316],[124.258287,-16.327944],[124.379726,-15.56706],[124.926153,-15.0751],[125.167275,-14.680396],[125.670087,-14.51007],[125.685796,-14.230656],[126.125149,-14.347341],[126.142823,-14.095987],[126.582589,-13.952791],[127.065867,-13.817968],[127.804633,-14.276906],[128.35969,-14.86917],[128.985543,-14.875991],[129.621473,-14.969784],[129.4096,-14.42067],[129.888641,-13.618703],[130.339466,-13.357376],[130.183506,-13.10752],[130.617795,-12.536392],[131.223495,-12.183649],[131.735091,-12.302453],[132.575298,-12.114041],[132.557212,-11.603012],[131.824698,-11.273782],[132.357224,-11.128519],[133.019561,-11.376411],[133.550846,-11.786515],[134.393068,-12.042365],[134.678632,-11.941183],[135.298491,-12.248606],[135.882693,-11.962267],[136.258381,-12.049342],[136.492475,-11.857209],[136.95162,-12.351959],[136.685125,-12.887223],[136.305407,-13.29123],[135.961758,-13.324509],[136.077617,-13.724278],[135.783836,-14.223989],[135.428664,-14.715432],[135.500184,-14.997741],[136.295175,-15.550265],[137.06536,-15.870762],[137.580471,-16.215082],[138.303217,-16.807604],[138.585164,-16.806622],[139.108543,-17.062679],[139.260575,-17.371601],[140.215245,-17.710805],[140.875463,-17.369069],[141.07111,-16.832047],[141.274095,-16.38887],[141.398222,-15.840532],[141.702183,-15.044921],[141.56338,-14.561333],[141.63552,-14.270395],[141.519869,-13.698078],[141.65092,-12.944688],[141.842691,-12.741548],[141.68699,-12.407614],[141.928629,-11.877466],[142.118488,-11.328042],[142.143706,-11.042737],[142.51526,-10.668186],[142.79731,-11.157355],[142.866763,-11.784707],[143.115947,-11.90563],[143.158632,-12.325656],[143.522124,-12.834358],[143.597158,-13.400422],[143.561811,-13.763656]]]]},"id":"AUS"}, 

And here is the code where I am trying to compare areas of different bounding areas. In this if I'm trying to find out if a country has more than one bounding region (this works), and then in the for block select the largest one.

Problem: the "area" values ​​that I get are 0 and coords always selected from the first bounding area, not the largest.

 function calculateCountryCenter(country) { var coords; //Check if the country has more than one bounding region. if (country.geometry.coordinates.length > 1) { coords = country.geometry.coordinates[0][0]; var regionArea = path.area(country.geometry.coordinates[0]); for (var i=0; i<country.geometry.coordinates.length; i++) { if (path.area(country.geometry.coordinates[i]) > regionArea) { coords = country.geometry.coordinates[i][0]; } } } else { coords = country.geometry.coordinates[0]; } var averageCoords = [0,0]; coords.forEach(function(coord) { averageCoords[0] += coord[0] averageCoords[1] += coord[1] }); averageCoords[0] = averageCoords[0] / coords.length averageCoords[1] = averageCoords[1] / coords.length return averageCoords; } 

Here is the definition of the path.

  var path = d3.geo.path().projection(projection) 

Any guidance would be greatly appreciated. Thank you very much.

+6
source share
3 answers

You can calculate the area of ​​an arbitrary polygon using the Shoelace algorithm.

 function polygonArea(points) { var sum = 0.0; var length = points.length; if (length < 3) { return sum; } points.forEach(function(d1, i1) { i2 = (i1 + 1) % length; d2 = points[i2]; sum += (d2[1] * d1[0]) - (d1[1] * d2[0]); }); return sum / 2; } polygonArea([[0,0], [4,0], [4,3]]); // 6 

Keep in mind that polygons in a counterclockwise direction will have a positive region, and polygons in a clockwise direction will have a negative region. Self-intersecting polygons usually have both clockwise and counterclockwise, so their area will be canceled.

+7
source

Just because no one has yet added a fully implemented answer, I decided to answer my own question. Here's the code that takes geoJSON, as indicated in the original question, and returns the coordinates of the largest bounding area for countries with more than one bounding area. For countries with only one restricted area, it returns the coordinates of one restricted area. (I had to modify the code a bit to make it a standalone method here, but I'm sure it is error-free.)

 function getLargestBoundingRegion(country) { var largestRegionCoords; var path = d3.geo.path() //Check if the country has more than one bounding region. if (country.geometry.coordinates.length > 1) { var regionToReturn = { "type": "Feature", "geometry": { "type": "Polygon", "coordinates": country.geometry.coordinates[0]}, }; for (var i=1; i<country.geometry.coordinates.length; i++) { var testRegion = { "type": "Feature", "geometry": { "type": "Polygon", "coordinates": country.geometry.coordinates[i]}, }; if (path.area(testRegion) > path.area(regionToReturn)) { regionToReturn = testRegion; largestRegionCoords = country.geometry.coordinates[i][0]; } } } else { largestRegionCoords = country.geometry.coordinates[0]; } return largestRegionCoords; 
+4
source

The path.area function expects a function and does not work with just a list of coordinates. The easiest way to make it work is probably to copy the source object, delete all coordinates that you are not interested in, and pass them to path.area . The code would look like

 for (var i=0; i<country.geometry.coordinates.length; i++) { var copy = JSON.parse(JSON.stringify(country)); copy.geometry.coordinates = [copy.geometry.coordinates[i]]; path.area(copy); ... } 
+1
source

All Articles