D3:在d3中查找地理多边形的区域

时间:2013-04-15 15:58:14

标签: javascript d3.js visualization data-visualization geojson

我有一张使用geoJSON文件绘制国家/地区的地图。然后我想画一个以每个国家为中心的圆圈。但对于有几个边界地区的国家(美国有大陆,夏威夷,阿拉斯加),我希望这个圈子位于最大的边界地区。我试图通过比较不同边界区域的区域来做到这一点,但由于我无法理解的原因而无法正常工作。

以下是geoJSON的一个示例,展示了澳大利亚如何拥有多个边界区域:

{"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"},

这是我尝试比较不同边界区域的代码。在if语句中,我试图弄清楚该国家是否有多个边界区域(似乎有效),然后在for区块中选择最大的区域。

问题:我得到的“区域”值都是0,coords总是从第一个边界区域中选择,而不是最大的。

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;

}

这是路径的定义。

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

非常感谢任何指导。非常感谢。

3 个答案:

答案 0 :(得分:9)

您可以使用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

请注意,逆时针多边形将具有正面积,顺时针多边形将具有负面积。自相交多边形通常具有顺时针和逆时针区域,因此它们的区域将被抵消。

答案 1 :(得分:4)

仅仅因为没有人添加完全实现的答案,我决定回答我自己的问题。这里的代码采用原始问题中指定的geoJSON,并返回具有多个边界区域的国家/地区的最大边界区域的坐标。对于只有一个边界区域的国家,它返回一个边界区域的坐标。 (我不得不稍微修改一下代码,使其成为一个独立的方法,但我很确定它没有错误。)

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;

答案 2 :(得分:1)

path.area函数需要一个功能,并且不能仅使用一个坐标列表。使其工作的最简单方法可能是复制原始对象,删除您不感兴趣的所有坐标并将其传递给path.area。代码看起来像

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);
    ...
}