source: Dev/branches/jQueryUI/client/d3/d3.geo.js @ 249

Last change on this file since 249 was 249, checked in by hendrikvanantwerpen, 13 years ago

This one's for Subversion, because it's so close...

First widget (stripped down sequencer).
Seperated client and server code in two direcotry trees.

File size: 14.7 KB
Line 
1(function(){d3.geo = {};
2// TODO clip input coordinates on opposite hemisphere
3d3.geo.azimuthal = function() {
4  var mode = "orthographic", // or stereographic
5      origin,
6      scale = 200,
7      translate = [480, 250],
8      x0,
9      y0,
10      cy0,
11      sy0;
12
13  function azimuthal(coordinates) {
14    var x1 = coordinates[0] * d3_radians - x0,
15        y1 = coordinates[1] * d3_radians,
16        cx1 = Math.cos(x1),
17        sx1 = Math.sin(x1),
18        cy1 = Math.cos(y1),
19        sy1 = Math.sin(y1),
20        k = mode == "stereographic" ? 1 / (1 + sy0 * sy1 + cy0 * cy1 * cx1) : 1,
21        x = k * cy1 * sx1,
22        y = k * (sy0 * cy1 * cx1 - cy0 * sy1);
23    return [
24      scale * x + translate[0],
25      scale * y + translate[1]
26    ];
27  }
28
29  azimuthal.mode = function(x) {
30    if (!arguments.length) return mode;
31    mode = x;
32    return azimuthal;
33  };
34
35  azimuthal.origin = function(x) {
36    if (!arguments.length) return origin;
37    origin = x;
38    x0 = origin[0] * d3_radians;
39    y0 = origin[1] * d3_radians;
40    cy0 = Math.cos(y0);
41    sy0 = Math.sin(y0);
42    return azimuthal;
43  };
44
45  azimuthal.scale = function(x) {
46    if (!arguments.length) return scale;
47    scale = +x;
48    return azimuthal;
49  };
50
51  azimuthal.translate = function(x) {
52    if (!arguments.length) return translate;
53    translate = [+x[0], +x[1]];
54    return azimuthal;
55  };
56
57  return azimuthal.origin([0, 0]);
58};
59// Derived from Tom Carden's Albers implementation for Protovis.
60// http://gist.github.com/476238
61// http://mathworld.wolfram.com/AlbersEqual-AreaConicProjection.html
62
63d3.geo.albers = function() {
64  var origin = [-98, 38],
65      parallels = [29.5, 45.5],
66      scale = 1000,
67      translate = [480, 250],
68      lng0, // d3_radians * origin[0]
69      n,
70      C,
71      p0;
72
73  function albers(coordinates) {
74    var t = n * (d3_radians * coordinates[0] - lng0),
75        p = Math.sqrt(C - 2 * n * Math.sin(d3_radians * coordinates[1])) / n;
76    return [
77      scale * p * Math.sin(t) + translate[0],
78      scale * (p * Math.cos(t) - p0) + translate[1]
79    ];
80  }
81
82  function reload() {
83    var phi1 = d3_radians * parallels[0],
84        phi2 = d3_radians * parallels[1],
85        lat0 = d3_radians * origin[1],
86        s = Math.sin(phi1),
87        c = Math.cos(phi1);
88    lng0 = d3_radians * origin[0];
89    n = .5 * (s + Math.sin(phi2));
90    C = c * c + 2 * n * s;
91    p0 = Math.sqrt(C - 2 * n * Math.sin(lat0)) / n;
92    return albers;
93  }
94
95  albers.origin = function(x) {
96    if (!arguments.length) return origin;
97    origin = [+x[0], +x[1]];
98    return reload();
99  };
100
101  albers.parallels = function(x) {
102    if (!arguments.length) return parallels;
103    parallels = [+x[0], +x[1]];
104    return reload();
105  };
106
107  albers.scale = function(x) {
108    if (!arguments.length) return scale;
109    scale = +x;
110    return albers;
111  };
112
113  albers.translate = function(x) {
114    if (!arguments.length) return translate;
115    translate = [+x[0], +x[1]];
116    return albers;
117  };
118
119  return reload();
120};
121
122// A composite projection for the United States, 960x500. The set of standard
123// parallels for each region comes from USGS, which is published here:
124// http://egsc.usgs.gov/isb/pubs/MapProjections/projections.html#albers
125// TODO allow the composite projection to be rescaled?
126d3.geo.albersUsa = function() {
127  var lower48 = d3.geo.albers();
128
129  var alaska = d3.geo.albers()
130      .origin([-160, 60])
131      .parallels([55, 65]);
132
133  var hawaii = d3.geo.albers()
134      .origin([-160, 20])
135      .parallels([8, 18]);
136
137  var puertoRico = d3.geo.albers()
138      .origin([-60, 10])
139      .parallels([8, 18]);
140
141  function albersUsa(coordinates) {
142    var lon = coordinates[0],
143        lat = coordinates[1];
144    return (lat < 25
145        ? (lon < -100 ? hawaii : puertoRico)
146        : (lat > 50 ? alaska : lower48))(coordinates);
147  }
148
149  albersUsa.scale = function(x) {
150    if (!arguments.length) return lower48.scale();
151    lower48.scale(x);
152    alaska.scale(x * .6);
153    hawaii.scale(x);
154    puertoRico.scale(x * 1.5);
155    return albersUsa.translate(lower48.translate());
156  };
157
158  albersUsa.translate = function(x) {
159    if (!arguments.length) return lower48.translate();
160    var dz = lower48.scale() / 1000,
161        dx = x[0],
162        dy = x[1];
163    lower48.translate(x);
164    alaska.translate([dx - 400 * dz, dy + 170 * dz]);
165    hawaii.translate([dx - 190 * dz, dy + 200 * dz]);
166    puertoRico.translate([dx + 580 * dz, dy + 430 * dz]);
167    return albersUsa;
168  };
169
170  return albersUsa.scale(lower48.scale());
171};
172
173var d3_radians = Math.PI / 180;
174d3.geo.mercator = function() {
175  var scale = 500,
176      translate = [480, 250];
177
178  function mercator(coordinates) {
179    var x = (coordinates[0]) / 360,
180        y = (-180 / Math.PI * Math.log(Math.tan(Math.PI / 4 + coordinates[1] * Math.PI / 360))) / 360;
181    return [
182      scale * x + translate[0],
183      scale * Math.max(-.5, Math.min(.5, y)) + translate[1]
184    ];
185  }
186
187  mercator.scale = function(x) {
188    if (!arguments.length) return scale;
189    scale = +x;
190    return mercator;
191  };
192
193  mercator.translate = function(x) {
194    if (!arguments.length) return translate;
195    translate = [+x[0], +x[1]];
196    return mercator;
197  };
198
199  return mercator;
200};
201/**
202 * Returns a function that, given a GeoJSON object (e.g., a feature), returns
203 * the corresponding SVG path. The function can be customized by overriding the
204 * projection. Point features are mapped to circles with a default radius of
205 * 4.5px; the radius can be specified either as a constant or a function that
206 * is evaluated per object.
207 */
208d3.geo.path = function() {
209  var pointRadius = 4.5,
210      pointCircle = d3_path_circle(pointRadius),
211      projection = d3.geo.albersUsa();
212
213  function path(d, i) {
214    if (typeof pointRadius === "function") {
215      pointCircle = d3_path_circle(pointRadius.apply(this, arguments));
216    }
217    return d3_geo_pathType(pathTypes, d);
218  }
219
220  function project(coordinates) {
221    return projection(coordinates).join(",");
222  }
223
224  var pathTypes = {
225
226    FeatureCollection: function(f) {
227      var path = [],
228          features = f.features,
229          i = -1, // features.index
230          n = features.length;
231      while (++i < n) path.push(d3_geo_pathType(pathTypes, features[i].geometry));
232      return path.join("");
233    },
234
235    Feature: function(f) {
236      return d3_geo_pathType(pathTypes, f.geometry);
237    },
238
239    Point: function(o) {
240      return "M" + project(o.coordinates) + pointCircle;
241    },
242
243    MultiPoint: function(o) {
244      var path = [],
245          coordinates = o.coordinates,
246          i = -1, // coordinates.index
247          n = coordinates.length;
248      while (++i < n) path.push("M", project(coordinates[i]), pointCircle);
249      return path.join("");
250    },
251
252    LineString: function(o) {
253      var path = ["M"],
254          coordinates = o.coordinates,
255          i = -1, // coordinates.index
256          n = coordinates.length;
257      while (++i < n) path.push(project(coordinates[i]), "L");
258      path.pop();
259      return path.join("");
260    },
261
262    MultiLineString: function(o) {
263      var path = [],
264          coordinates = o.coordinates,
265          i = -1, // coordinates.index
266          n = coordinates.length,
267          subcoordinates, // coordinates[i]
268          j, // subcoordinates.index
269          m; // subcoordinates.length
270      while (++i < n) {
271        subcoordinates = coordinates[i];
272        j = -1;
273        m = subcoordinates.length;
274        path.push("M");
275        while (++j < m) path.push(project(subcoordinates[j]), "L");
276        path.pop();
277      }
278      return path.join("");
279    },
280
281    Polygon: function(o) {
282      var path = [],
283          coordinates = o.coordinates,
284          i = -1, // coordinates.index
285          n = coordinates.length,
286          subcoordinates, // coordinates[i]
287          j, // subcoordinates.index
288          m; // subcoordinates.length
289      while (++i < n) {
290        subcoordinates = coordinates[i];
291        j = -1;
292        m = subcoordinates.length;
293        path.push("M");
294        while (++j < m) path.push(project(subcoordinates[j]), "L");
295        path[path.length - 1] = "Z";
296      }
297      return path.join("");
298    },
299
300    MultiPolygon: function(o) {
301      var path = [],
302          coordinates = o.coordinates,
303          i = -1, // coordinates index
304          n = coordinates.length,
305          subcoordinates, // coordinates[i]
306          j, // subcoordinates index
307          m, // subcoordinates.length
308          subsubcoordinates, // subcoordinates[j]
309          k, // subsubcoordinates index
310          p; // subsubcoordinates.length
311      while (++i < n) {
312        subcoordinates = coordinates[i];
313        j = -1;
314        m = subcoordinates.length;
315        while (++j < m) {
316          subsubcoordinates = subcoordinates[j];
317          k = -1;
318          p = subsubcoordinates.length - 1;
319          path.push("M");
320          while (++k < p) path.push(project(subsubcoordinates[k]), "L");
321          path[path.length - 1] = "Z";
322        }
323      }
324      return path.join("");
325    },
326
327    GeometryCollection: function(o) {
328      var path = [],
329          geometries = o.geometries,
330          i = -1, // geometries index
331          n = geometries.length;
332      while (++i < n) path.push(d3_geo_pathType(pathTypes, geometries[i]));
333      return path.join("");
334    }
335
336  };
337
338  var areaTypes = {
339
340    FeatureCollection: function(f) {
341      var area = 0,
342          features = f.features,
343          i = -1, // features.index
344          n = features.length;
345      while (++i < n) area += d3_geo_pathType(areaTypes, features[i]);
346      return area;
347    },
348
349    Feature: function(f) {
350      return d3_geo_pathType(areaTypes, f.geometry);
351    },
352
353    Point: d3_geo_pathZero,
354    MultiPoint: d3_geo_pathZero,
355    LineString: d3_geo_pathZero,
356    MultiLineString: d3_geo_pathZero,
357
358    Polygon: function(o) {
359      return polygonArea(o.coordinates);
360    },
361
362    MultiPolygon: function(o) {
363      var sum = 0,
364          coordinates = o.coordinates,
365          i = -1, // coordinates index
366          n = coordinates.length;
367      while (++i < n) sum += polygonArea(coordinates[i]);
368      return sum;
369    },
370
371    GeometryCollection: function(o) {
372      var sum = 0,
373          geometries = o.geometries,
374          i = -1, // geometries index
375          n = geometries.length;
376      while (++i < n) sum += d3_geo_pathType(areaTypes, geometries[i]);
377      return sum;
378    }
379
380  };
381
382  function polygonArea(coordinates) {
383    var sum = area(coordinates[0]), // exterior ring
384        i = 0, // coordinates.index
385        n = coordinates.length;
386    while (++i < n) sum -= area(coordinates[i]); // holes
387    return sum;
388  }
389
390  function polygonCentroid(coordinates) {
391    var polygon = d3.geom.polygon(coordinates[0].map(projection)), // exterior ring
392        centroid = polygon.centroid(1),
393        x = centroid[0],
394        y = centroid[1],
395        z = Math.abs(polygon.area()),
396        i = 0, // coordinates index
397        n = coordinates.length;
398    while (++i < n) {
399      polygon = d3.geom.polygon(coordinates[i].map(projection)); // holes
400      centroid = polygon.centroid(1);
401      x -= centroid[0];
402      y -= centroid[1];
403      z -= Math.abs(polygon.area());
404    }
405    return [x, y, 6 * z]; // weighted centroid
406  }
407
408  var centroidTypes = {
409
410    // TODO FeatureCollection
411    // TODO Point
412    // TODO MultiPoint
413    // TODO LineString
414    // TODO MultiLineString
415    // TODO GeometryCollection
416
417    Feature: function(f) {
418      return d3_geo_pathType(centroidTypes, f.geometry);
419    },
420
421    Polygon: function(o) {
422      var centroid = polygonCentroid(o.coordinates);
423      return [centroid[0] / centroid[2], centroid[1] / centroid[2]];
424    },
425
426    MultiPolygon: function(o) {
427      var area = 0,
428          coordinates = o.coordinates,
429          centroid,
430          x = 0,
431          y = 0,
432          z = 0,
433          i = -1, // coordinates index
434          n = coordinates.length;
435      while (++i < n) {
436        centroid = polygonCentroid(coordinates[i]);
437        x += centroid[0];
438        y += centroid[1];
439        z += centroid[2];
440      }
441      return [x / z, y / z];
442    }
443
444  };
445
446
447  function area(coordinates) {
448    return Math.abs(d3.geom.polygon(coordinates.map(projection)).area());
449  }
450
451  path.projection = function(x) {
452    projection = x;
453    return path;
454  };
455
456  path.area = function(d) {
457    return d3_geo_pathType(areaTypes, d);
458  };
459
460  path.centroid = function(d) {
461    return d3_geo_pathType(centroidTypes, d);
462  };
463
464  path.pointRadius = function(x) {
465    if (typeof x === "function") pointRadius = x;
466    else {
467      pointRadius = +x;
468      pointCircle = d3_path_circle(pointRadius);
469    }
470    return path;
471  };
472
473  return path;
474};
475
476function d3_path_circle(radius) {
477  return "m0," + radius
478      + "a" + radius + "," + radius + " 0 1,1 0," + (-2 * radius)
479      + "a" + radius + "," + radius + " 0 1,1 0," + (+2 * radius)
480      + "z";
481}
482
483function d3_geo_pathZero() {
484  return 0;
485}
486
487function d3_geo_pathType(types, o) {
488  return o && o.type in types ? types[o.type](o) : "";
489}
490/**
491 * Given a GeoJSON object, returns the corresponding bounding box. The bounding
492 * box is represented by a two-dimensional array: [[left, bottom], [right,
493 * top]], where left is the minimum longitude, bottom is the minimum latitude,
494 * right is maximum longitude, and top is the maximum latitude.
495 */
496d3.geo.bounds = function(feature) {
497  var left = Infinity,
498      bottom = Infinity,
499      right = -Infinity,
500      top = -Infinity;
501  d3_geo_bounds(feature, function(x, y) {
502    if (x < left) left = x;
503    if (x > right) right = x;
504    if (y < bottom) bottom = y;
505    if (y > top) top = y;
506  });
507  return [[left, bottom], [right, top]];
508};
509
510function d3_geo_bounds(o, f) {
511  if (o.type in d3_geo_boundsTypes) d3_geo_boundsTypes[o.type](o, f);
512}
513
514var d3_geo_boundsTypes = {
515  Feature: d3_geo_boundsFeature,
516  FeatureCollection: d3_geo_boundsFeatureCollection,
517  LineString: d3_geo_boundsLineString,
518  MultiLineString: d3_geo_boundsMultiLineString,
519  MultiPoint: d3_geo_boundsLineString,
520  MultiPolygon: d3_geo_boundsMultiPolygon,
521  Point: d3_geo_boundsPoint,
522  Polygon: d3_geo_boundsPolygon
523};
524
525function d3_geo_boundsFeature(o, f) {
526  d3_geo_bounds(o.geometry, f);
527}
528
529function d3_geo_boundsFeatureCollection(o, f) {
530  for (var a = o.features, i = 0, n = a.length; i < n; i++) {
531    d3_geo_bounds(a[i].geometry, f);
532  }
533}
534
535function d3_geo_boundsLineString(o, f) {
536  for (var a = o.coordinates, i = 0, n = a.length; i < n; i++) {
537    f.apply(null, a[i]);
538  }
539}
540
541function d3_geo_boundsMultiLineString(o, f) {
542  for (var a = o.coordinates, i = 0, n = a.length; i < n; i++) {
543    for (var b = a[i], j = 0, m = b.length; j < m; j++) {
544      f.apply(null, b[j]);
545    }
546  }
547}
548
549function d3_geo_boundsMultiPolygon(o, f) {
550  for (var a = o.coordinates, i = 0, n = a.length; i < n; i++) {
551    for (var b = a[i][0], j = 0, m = b.length; j < m; j++) {
552      f.apply(null, b[j]);
553    }
554  }
555}
556
557function d3_geo_boundsPoint(o, f) {
558  f.apply(null, o.coordinates);
559}
560
561function d3_geo_boundsPolygon(o, f) {
562  for (var a = o.coordinates[0], i = 0, n = a.length; i < n; i++) {
563    f.apply(null, a[i]);
564  }
565}
566})();
Note: See TracBrowser for help on using the repository browser.