toolkit/javascript/d3/src/geo/path.js
changeset 47 c0b4a8b5a012
equal deleted inserted replaced
46:efd9c589177a 47:c0b4a8b5a012
       
     1 /**
       
     2  * Returns a function that, given a GeoJSON object (e.g., a feature), returns
       
     3  * the corresponding SVG path. The function can be customized by overriding the
       
     4  * projection. Point features are mapped to circles with a default radius of
       
     5  * 4.5px; the radius can be specified either as a constant or a function that
       
     6  * is evaluated per object.
       
     7  */
       
     8 d3.geo.path = function() {
       
     9   var pointRadius = 4.5,
       
    10       pointCircle = d3_path_circle(pointRadius),
       
    11       projection = d3.geo.albersUsa();
       
    12 
       
    13   function path(d, i) {
       
    14     if (typeof pointRadius === "function") {
       
    15       pointCircle = d3_path_circle(pointRadius.apply(this, arguments));
       
    16     }
       
    17     return pathType(d) || null;
       
    18   }
       
    19 
       
    20   function project(coordinates) {
       
    21     return projection(coordinates).join(",");
       
    22   }
       
    23 
       
    24   var pathType = d3_geo_type({
       
    25 
       
    26     FeatureCollection: function(o) {
       
    27       var path = [],
       
    28           features = o.features,
       
    29           i = -1, // features.index
       
    30           n = features.length;
       
    31       while (++i < n) path.push(pathType(features[i].geometry));
       
    32       return path.join("");
       
    33     },
       
    34 
       
    35     Feature: function(o) {
       
    36       return pathType(o.geometry);
       
    37     },
       
    38 
       
    39     Point: function(o) {
       
    40       return "M" + project(o.coordinates) + pointCircle;
       
    41     },
       
    42 
       
    43     MultiPoint: function(o) {
       
    44       var path = [],
       
    45           coordinates = o.coordinates,
       
    46           i = -1, // coordinates.index
       
    47           n = coordinates.length;
       
    48       while (++i < n) path.push("M", project(coordinates[i]), pointCircle);
       
    49       return path.join("");
       
    50     },
       
    51 
       
    52     LineString: function(o) {
       
    53       var path = ["M"],
       
    54           coordinates = o.coordinates,
       
    55           i = -1, // coordinates.index
       
    56           n = coordinates.length;
       
    57       while (++i < n) path.push(project(coordinates[i]), "L");
       
    58       path.pop();
       
    59       return path.join("");
       
    60     },
       
    61 
       
    62     MultiLineString: function(o) {
       
    63       var path = [],
       
    64           coordinates = o.coordinates,
       
    65           i = -1, // coordinates.index
       
    66           n = coordinates.length,
       
    67           subcoordinates, // coordinates[i]
       
    68           j, // subcoordinates.index
       
    69           m; // subcoordinates.length
       
    70       while (++i < n) {
       
    71         subcoordinates = coordinates[i];
       
    72         j = -1;
       
    73         m = subcoordinates.length;
       
    74         path.push("M");
       
    75         while (++j < m) path.push(project(subcoordinates[j]), "L");
       
    76         path.pop();
       
    77       }
       
    78       return path.join("");
       
    79     },
       
    80 
       
    81     Polygon: function(o) {
       
    82       var path = [],
       
    83           coordinates = o.coordinates,
       
    84           i = -1, // coordinates.index
       
    85           n = coordinates.length,
       
    86           subcoordinates, // coordinates[i]
       
    87           j, // subcoordinates.index
       
    88           m; // subcoordinates.length
       
    89       while (++i < n) {
       
    90         subcoordinates = coordinates[i];
       
    91         j = -1;
       
    92         if ((m = subcoordinates.length - 1) > 0) {
       
    93           path.push("M");
       
    94           while (++j < m) path.push(project(subcoordinates[j]), "L");
       
    95           path[path.length - 1] = "Z";
       
    96         }
       
    97       }
       
    98       return path.join("");
       
    99     },
       
   100 
       
   101     MultiPolygon: function(o) {
       
   102       var path = [],
       
   103           coordinates = o.coordinates,
       
   104           i = -1, // coordinates index
       
   105           n = coordinates.length,
       
   106           subcoordinates, // coordinates[i]
       
   107           j, // subcoordinates index
       
   108           m, // subcoordinates.length
       
   109           subsubcoordinates, // subcoordinates[j]
       
   110           k, // subsubcoordinates index
       
   111           p; // subsubcoordinates.length
       
   112       while (++i < n) {
       
   113         subcoordinates = coordinates[i];
       
   114         j = -1;
       
   115         m = subcoordinates.length;
       
   116         while (++j < m) {
       
   117           subsubcoordinates = subcoordinates[j];
       
   118           k = -1;
       
   119           if ((p = subsubcoordinates.length - 1) > 0) {
       
   120             path.push("M");
       
   121             while (++k < p) path.push(project(subsubcoordinates[k]), "L");
       
   122             path[path.length - 1] = "Z";
       
   123           }
       
   124         }
       
   125       }
       
   126       return path.join("");
       
   127     },
       
   128 
       
   129     GeometryCollection: function(o) {
       
   130       var path = [],
       
   131           geometries = o.geometries,
       
   132           i = -1, // geometries index
       
   133           n = geometries.length;
       
   134       while (++i < n) path.push(pathType(geometries[i]));
       
   135       return path.join("");
       
   136     }
       
   137 
       
   138   });
       
   139 
       
   140   var areaType = path.area = d3_geo_type({
       
   141 
       
   142     FeatureCollection: function(o) {
       
   143       var area = 0,
       
   144           features = o.features,
       
   145           i = -1, // features.index
       
   146           n = features.length;
       
   147       while (++i < n) area += areaType(features[i]);
       
   148       return area;
       
   149     },
       
   150 
       
   151     Feature: function(o) {
       
   152       return areaType(o.geometry);
       
   153     },
       
   154 
       
   155     Polygon: function(o) {
       
   156       return polygonArea(o.coordinates);
       
   157     },
       
   158 
       
   159     MultiPolygon: function(o) {
       
   160       var sum = 0,
       
   161           coordinates = o.coordinates,
       
   162           i = -1, // coordinates index
       
   163           n = coordinates.length;
       
   164       while (++i < n) sum += polygonArea(coordinates[i]);
       
   165       return sum;
       
   166     },
       
   167 
       
   168     GeometryCollection: function(o) {
       
   169       var sum = 0,
       
   170           geometries = o.geometries,
       
   171           i = -1, // geometries index
       
   172           n = geometries.length;
       
   173       while (++i < n) sum += areaType(geometries[i]);
       
   174       return sum;
       
   175     }
       
   176 
       
   177   }, 0);
       
   178 
       
   179   function polygonArea(coordinates) {
       
   180     var sum = area(coordinates[0]), // exterior ring
       
   181         i = 0, // coordinates.index
       
   182         n = coordinates.length;
       
   183     while (++i < n) sum -= area(coordinates[i]); // holes
       
   184     return sum;
       
   185   }
       
   186 
       
   187   function polygonCentroid(coordinates) {
       
   188     var polygon = d3.geom.polygon(coordinates[0].map(projection)), // exterior ring
       
   189         area = polygon.area(),
       
   190         centroid = polygon.centroid(area < 0 ? (area *= -1, 1) : -1),
       
   191         x = centroid[0],
       
   192         y = centroid[1],
       
   193         z = area,
       
   194         i = 0, // coordinates index
       
   195         n = coordinates.length;
       
   196     while (++i < n) {
       
   197       polygon = d3.geom.polygon(coordinates[i].map(projection)); // holes
       
   198       area = polygon.area();
       
   199       centroid = polygon.centroid(area < 0 ? (area *= -1, 1) : -1);
       
   200       x -= centroid[0];
       
   201       y -= centroid[1];
       
   202       z -= area;
       
   203     }
       
   204     return [x, y, 6 * z]; // weighted centroid
       
   205   }
       
   206 
       
   207   var centroidType = path.centroid = d3_geo_type({
       
   208 
       
   209     // TODO FeatureCollection
       
   210     // TODO Point
       
   211     // TODO MultiPoint
       
   212     // TODO LineString
       
   213     // TODO MultiLineString
       
   214     // TODO GeometryCollection
       
   215 
       
   216     Feature: function(o) {
       
   217       return centroidType(o.geometry);
       
   218     },
       
   219 
       
   220     Polygon: function(o) {
       
   221       var centroid = polygonCentroid(o.coordinates);
       
   222       return [centroid[0] / centroid[2], centroid[1] / centroid[2]];
       
   223     },
       
   224 
       
   225     MultiPolygon: function(o) {
       
   226       var area = 0,
       
   227           coordinates = o.coordinates,
       
   228           centroid,
       
   229           x = 0,
       
   230           y = 0,
       
   231           z = 0,
       
   232           i = -1, // coordinates index
       
   233           n = coordinates.length;
       
   234       while (++i < n) {
       
   235         centroid = polygonCentroid(coordinates[i]);
       
   236         x += centroid[0];
       
   237         y += centroid[1];
       
   238         z += centroid[2];
       
   239       }
       
   240       return [x / z, y / z];
       
   241     }
       
   242 
       
   243   });
       
   244 
       
   245   function area(coordinates) {
       
   246     return Math.abs(d3.geom.polygon(coordinates.map(projection)).area());
       
   247   }
       
   248 
       
   249   path.projection = function(x) {
       
   250     projection = x;
       
   251     return path;
       
   252   };
       
   253 
       
   254   path.pointRadius = function(x) {
       
   255     if (typeof x === "function") pointRadius = x;
       
   256     else {
       
   257       pointRadius = +x;
       
   258       pointCircle = d3_path_circle(pointRadius);
       
   259     }
       
   260     return path;
       
   261   };
       
   262 
       
   263   return path;
       
   264 };
       
   265 
       
   266 function d3_path_circle(radius) {
       
   267   return "m0," + radius
       
   268       + "a" + radius + "," + radius + " 0 1,1 0," + (-2 * radius)
       
   269       + "a" + radius + "," + radius + " 0 1,1 0," + (+2 * radius)
       
   270       + "z";
       
   271 }