| [e4c61dd] | 1 | // https://d3js.org/d3-geo/ v3.1.1 Copyright 2010-2024 Mike Bostock, 2008-2012 Charles Karney
|
|---|
| 2 | (function (global, factory) {
|
|---|
| 3 | typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('d3-array')) :
|
|---|
| 4 | typeof define === 'function' && define.amd ? define(['exports', 'd3-array'], factory) :
|
|---|
| 5 | (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.d3 = global.d3 || {}, global.d3));
|
|---|
| 6 | })(this, (function (exports, d3Array) { 'use strict';
|
|---|
| 7 |
|
|---|
| 8 | var epsilon = 1e-6;
|
|---|
| 9 | var epsilon2 = 1e-12;
|
|---|
| 10 | var pi = Math.PI;
|
|---|
| 11 | var halfPi = pi / 2;
|
|---|
| 12 | var quarterPi = pi / 4;
|
|---|
| 13 | var tau = pi * 2;
|
|---|
| 14 |
|
|---|
| 15 | var degrees = 180 / pi;
|
|---|
| 16 | var radians = pi / 180;
|
|---|
| 17 |
|
|---|
| 18 | var abs = Math.abs;
|
|---|
| 19 | var atan = Math.atan;
|
|---|
| 20 | var atan2 = Math.atan2;
|
|---|
| 21 | var cos = Math.cos;
|
|---|
| 22 | var ceil = Math.ceil;
|
|---|
| 23 | var exp = Math.exp;
|
|---|
| 24 | var hypot = Math.hypot;
|
|---|
| 25 | var log = Math.log;
|
|---|
| 26 | var pow = Math.pow;
|
|---|
| 27 | var sin = Math.sin;
|
|---|
| 28 | var sign = Math.sign || function(x) { return x > 0 ? 1 : x < 0 ? -1 : 0; };
|
|---|
| 29 | var sqrt = Math.sqrt;
|
|---|
| 30 | var tan = Math.tan;
|
|---|
| 31 |
|
|---|
| 32 | function acos(x) {
|
|---|
| 33 | return x > 1 ? 0 : x < -1 ? pi : Math.acos(x);
|
|---|
| 34 | }
|
|---|
| 35 |
|
|---|
| 36 | function asin(x) {
|
|---|
| 37 | return x > 1 ? halfPi : x < -1 ? -halfPi : Math.asin(x);
|
|---|
| 38 | }
|
|---|
| 39 |
|
|---|
| 40 | function haversin(x) {
|
|---|
| 41 | return (x = sin(x / 2)) * x;
|
|---|
| 42 | }
|
|---|
| 43 |
|
|---|
| 44 | function noop() {}
|
|---|
| 45 |
|
|---|
| 46 | function streamGeometry(geometry, stream) {
|
|---|
| 47 | if (geometry && streamGeometryType.hasOwnProperty(geometry.type)) {
|
|---|
| 48 | streamGeometryType[geometry.type](geometry, stream);
|
|---|
| 49 | }
|
|---|
| 50 | }
|
|---|
| 51 |
|
|---|
| 52 | var streamObjectType = {
|
|---|
| 53 | Feature: function(object, stream) {
|
|---|
| 54 | streamGeometry(object.geometry, stream);
|
|---|
| 55 | },
|
|---|
| 56 | FeatureCollection: function(object, stream) {
|
|---|
| 57 | var features = object.features, i = -1, n = features.length;
|
|---|
| 58 | while (++i < n) streamGeometry(features[i].geometry, stream);
|
|---|
| 59 | }
|
|---|
| 60 | };
|
|---|
| 61 |
|
|---|
| 62 | var streamGeometryType = {
|
|---|
| 63 | Sphere: function(object, stream) {
|
|---|
| 64 | stream.sphere();
|
|---|
| 65 | },
|
|---|
| 66 | Point: function(object, stream) {
|
|---|
| 67 | object = object.coordinates;
|
|---|
| 68 | stream.point(object[0], object[1], object[2]);
|
|---|
| 69 | },
|
|---|
| 70 | MultiPoint: function(object, stream) {
|
|---|
| 71 | var coordinates = object.coordinates, i = -1, n = coordinates.length;
|
|---|
| 72 | while (++i < n) object = coordinates[i], stream.point(object[0], object[1], object[2]);
|
|---|
| 73 | },
|
|---|
| 74 | LineString: function(object, stream) {
|
|---|
| 75 | streamLine(object.coordinates, stream, 0);
|
|---|
| 76 | },
|
|---|
| 77 | MultiLineString: function(object, stream) {
|
|---|
| 78 | var coordinates = object.coordinates, i = -1, n = coordinates.length;
|
|---|
| 79 | while (++i < n) streamLine(coordinates[i], stream, 0);
|
|---|
| 80 | },
|
|---|
| 81 | Polygon: function(object, stream) {
|
|---|
| 82 | streamPolygon(object.coordinates, stream);
|
|---|
| 83 | },
|
|---|
| 84 | MultiPolygon: function(object, stream) {
|
|---|
| 85 | var coordinates = object.coordinates, i = -1, n = coordinates.length;
|
|---|
| 86 | while (++i < n) streamPolygon(coordinates[i], stream);
|
|---|
| 87 | },
|
|---|
| 88 | GeometryCollection: function(object, stream) {
|
|---|
| 89 | var geometries = object.geometries, i = -1, n = geometries.length;
|
|---|
| 90 | while (++i < n) streamGeometry(geometries[i], stream);
|
|---|
| 91 | }
|
|---|
| 92 | };
|
|---|
| 93 |
|
|---|
| 94 | function streamLine(coordinates, stream, closed) {
|
|---|
| 95 | var i = -1, n = coordinates.length - closed, coordinate;
|
|---|
| 96 | stream.lineStart();
|
|---|
| 97 | while (++i < n) coordinate = coordinates[i], stream.point(coordinate[0], coordinate[1], coordinate[2]);
|
|---|
| 98 | stream.lineEnd();
|
|---|
| 99 | }
|
|---|
| 100 |
|
|---|
| 101 | function streamPolygon(coordinates, stream) {
|
|---|
| 102 | var i = -1, n = coordinates.length;
|
|---|
| 103 | stream.polygonStart();
|
|---|
| 104 | while (++i < n) streamLine(coordinates[i], stream, 1);
|
|---|
| 105 | stream.polygonEnd();
|
|---|
| 106 | }
|
|---|
| 107 |
|
|---|
| 108 | function geoStream(object, stream) {
|
|---|
| 109 | if (object && streamObjectType.hasOwnProperty(object.type)) {
|
|---|
| 110 | streamObjectType[object.type](object, stream);
|
|---|
| 111 | } else {
|
|---|
| 112 | streamGeometry(object, stream);
|
|---|
| 113 | }
|
|---|
| 114 | }
|
|---|
| 115 |
|
|---|
| 116 | var areaRingSum$1 = new d3Array.Adder();
|
|---|
| 117 |
|
|---|
| 118 | // hello?
|
|---|
| 119 |
|
|---|
| 120 | var areaSum$1 = new d3Array.Adder(),
|
|---|
| 121 | lambda00$2,
|
|---|
| 122 | phi00$2,
|
|---|
| 123 | lambda0$2,
|
|---|
| 124 | cosPhi0$1,
|
|---|
| 125 | sinPhi0$1;
|
|---|
| 126 |
|
|---|
| 127 | var areaStream$1 = {
|
|---|
| 128 | point: noop,
|
|---|
| 129 | lineStart: noop,
|
|---|
| 130 | lineEnd: noop,
|
|---|
| 131 | polygonStart: function() {
|
|---|
| 132 | areaRingSum$1 = new d3Array.Adder();
|
|---|
| 133 | areaStream$1.lineStart = areaRingStart$1;
|
|---|
| 134 | areaStream$1.lineEnd = areaRingEnd$1;
|
|---|
| 135 | },
|
|---|
| 136 | polygonEnd: function() {
|
|---|
| 137 | var areaRing = +areaRingSum$1;
|
|---|
| 138 | areaSum$1.add(areaRing < 0 ? tau + areaRing : areaRing);
|
|---|
| 139 | this.lineStart = this.lineEnd = this.point = noop;
|
|---|
| 140 | },
|
|---|
| 141 | sphere: function() {
|
|---|
| 142 | areaSum$1.add(tau);
|
|---|
| 143 | }
|
|---|
| 144 | };
|
|---|
| 145 |
|
|---|
| 146 | function areaRingStart$1() {
|
|---|
| 147 | areaStream$1.point = areaPointFirst$1;
|
|---|
| 148 | }
|
|---|
| 149 |
|
|---|
| 150 | function areaRingEnd$1() {
|
|---|
| 151 | areaPoint$1(lambda00$2, phi00$2);
|
|---|
| 152 | }
|
|---|
| 153 |
|
|---|
| 154 | function areaPointFirst$1(lambda, phi) {
|
|---|
| 155 | areaStream$1.point = areaPoint$1;
|
|---|
| 156 | lambda00$2 = lambda, phi00$2 = phi;
|
|---|
| 157 | lambda *= radians, phi *= radians;
|
|---|
| 158 | lambda0$2 = lambda, cosPhi0$1 = cos(phi = phi / 2 + quarterPi), sinPhi0$1 = sin(phi);
|
|---|
| 159 | }
|
|---|
| 160 |
|
|---|
| 161 | function areaPoint$1(lambda, phi) {
|
|---|
| 162 | lambda *= radians, phi *= radians;
|
|---|
| 163 | phi = phi / 2 + quarterPi; // half the angular distance from south pole
|
|---|
| 164 |
|
|---|
| 165 | // Spherical excess E for a spherical triangle with vertices: south pole,
|
|---|
| 166 | // previous point, current point. Uses a formula derived from Cagnoli’s
|
|---|
| 167 | // theorem. See Todhunter, Spherical Trig. (1871), Sec. 103, Eq. (2).
|
|---|
| 168 | var dLambda = lambda - lambda0$2,
|
|---|
| 169 | sdLambda = dLambda >= 0 ? 1 : -1,
|
|---|
| 170 | adLambda = sdLambda * dLambda,
|
|---|
| 171 | cosPhi = cos(phi),
|
|---|
| 172 | sinPhi = sin(phi),
|
|---|
| 173 | k = sinPhi0$1 * sinPhi,
|
|---|
| 174 | u = cosPhi0$1 * cosPhi + k * cos(adLambda),
|
|---|
| 175 | v = k * sdLambda * sin(adLambda);
|
|---|
| 176 | areaRingSum$1.add(atan2(v, u));
|
|---|
| 177 |
|
|---|
| 178 | // Advance the previous points.
|
|---|
| 179 | lambda0$2 = lambda, cosPhi0$1 = cosPhi, sinPhi0$1 = sinPhi;
|
|---|
| 180 | }
|
|---|
| 181 |
|
|---|
| 182 | function area(object) {
|
|---|
| 183 | areaSum$1 = new d3Array.Adder();
|
|---|
| 184 | geoStream(object, areaStream$1);
|
|---|
| 185 | return areaSum$1 * 2;
|
|---|
| 186 | }
|
|---|
| 187 |
|
|---|
| 188 | function spherical(cartesian) {
|
|---|
| 189 | return [atan2(cartesian[1], cartesian[0]), asin(cartesian[2])];
|
|---|
| 190 | }
|
|---|
| 191 |
|
|---|
| 192 | function cartesian(spherical) {
|
|---|
| 193 | var lambda = spherical[0], phi = spherical[1], cosPhi = cos(phi);
|
|---|
| 194 | return [cosPhi * cos(lambda), cosPhi * sin(lambda), sin(phi)];
|
|---|
| 195 | }
|
|---|
| 196 |
|
|---|
| 197 | function cartesianDot(a, b) {
|
|---|
| 198 | return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|---|
| 199 | }
|
|---|
| 200 |
|
|---|
| 201 | function cartesianCross(a, b) {
|
|---|
| 202 | return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]];
|
|---|
| 203 | }
|
|---|
| 204 |
|
|---|
| 205 | // TODO return a
|
|---|
| 206 | function cartesianAddInPlace(a, b) {
|
|---|
| 207 | a[0] += b[0], a[1] += b[1], a[2] += b[2];
|
|---|
| 208 | }
|
|---|
| 209 |
|
|---|
| 210 | function cartesianScale(vector, k) {
|
|---|
| 211 | return [vector[0] * k, vector[1] * k, vector[2] * k];
|
|---|
| 212 | }
|
|---|
| 213 |
|
|---|
| 214 | // TODO return d
|
|---|
| 215 | function cartesianNormalizeInPlace(d) {
|
|---|
| 216 | var l = sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
|
|---|
| 217 | d[0] /= l, d[1] /= l, d[2] /= l;
|
|---|
| 218 | }
|
|---|
| 219 |
|
|---|
| 220 | var lambda0$1, phi0, lambda1, phi1, // bounds
|
|---|
| 221 | lambda2, // previous lambda-coordinate
|
|---|
| 222 | lambda00$1, phi00$1, // first point
|
|---|
| 223 | p0, // previous 3D point
|
|---|
| 224 | deltaSum,
|
|---|
| 225 | ranges,
|
|---|
| 226 | range;
|
|---|
| 227 |
|
|---|
| 228 | var boundsStream$1 = {
|
|---|
| 229 | point: boundsPoint$1,
|
|---|
| 230 | lineStart: boundsLineStart,
|
|---|
| 231 | lineEnd: boundsLineEnd,
|
|---|
| 232 | polygonStart: function() {
|
|---|
| 233 | boundsStream$1.point = boundsRingPoint;
|
|---|
| 234 | boundsStream$1.lineStart = boundsRingStart;
|
|---|
| 235 | boundsStream$1.lineEnd = boundsRingEnd;
|
|---|
| 236 | deltaSum = new d3Array.Adder();
|
|---|
| 237 | areaStream$1.polygonStart();
|
|---|
| 238 | },
|
|---|
| 239 | polygonEnd: function() {
|
|---|
| 240 | areaStream$1.polygonEnd();
|
|---|
| 241 | boundsStream$1.point = boundsPoint$1;
|
|---|
| 242 | boundsStream$1.lineStart = boundsLineStart;
|
|---|
| 243 | boundsStream$1.lineEnd = boundsLineEnd;
|
|---|
| 244 | if (areaRingSum$1 < 0) lambda0$1 = -(lambda1 = 180), phi0 = -(phi1 = 90);
|
|---|
| 245 | else if (deltaSum > epsilon) phi1 = 90;
|
|---|
| 246 | else if (deltaSum < -epsilon) phi0 = -90;
|
|---|
| 247 | range[0] = lambda0$1, range[1] = lambda1;
|
|---|
| 248 | },
|
|---|
| 249 | sphere: function() {
|
|---|
| 250 | lambda0$1 = -(lambda1 = 180), phi0 = -(phi1 = 90);
|
|---|
| 251 | }
|
|---|
| 252 | };
|
|---|
| 253 |
|
|---|
| 254 | function boundsPoint$1(lambda, phi) {
|
|---|
| 255 | ranges.push(range = [lambda0$1 = lambda, lambda1 = lambda]);
|
|---|
| 256 | if (phi < phi0) phi0 = phi;
|
|---|
| 257 | if (phi > phi1) phi1 = phi;
|
|---|
| 258 | }
|
|---|
| 259 |
|
|---|
| 260 | function linePoint(lambda, phi) {
|
|---|
| 261 | var p = cartesian([lambda * radians, phi * radians]);
|
|---|
| 262 | if (p0) {
|
|---|
| 263 | var normal = cartesianCross(p0, p),
|
|---|
| 264 | equatorial = [normal[1], -normal[0], 0],
|
|---|
| 265 | inflection = cartesianCross(equatorial, normal);
|
|---|
| 266 | cartesianNormalizeInPlace(inflection);
|
|---|
| 267 | inflection = spherical(inflection);
|
|---|
| 268 | var delta = lambda - lambda2,
|
|---|
| 269 | sign = delta > 0 ? 1 : -1,
|
|---|
| 270 | lambdai = inflection[0] * degrees * sign,
|
|---|
| 271 | phii,
|
|---|
| 272 | antimeridian = abs(delta) > 180;
|
|---|
| 273 | if (antimeridian ^ (sign * lambda2 < lambdai && lambdai < sign * lambda)) {
|
|---|
| 274 | phii = inflection[1] * degrees;
|
|---|
| 275 | if (phii > phi1) phi1 = phii;
|
|---|
| 276 | } else if (lambdai = (lambdai + 360) % 360 - 180, antimeridian ^ (sign * lambda2 < lambdai && lambdai < sign * lambda)) {
|
|---|
| 277 | phii = -inflection[1] * degrees;
|
|---|
| 278 | if (phii < phi0) phi0 = phii;
|
|---|
| 279 | } else {
|
|---|
| 280 | if (phi < phi0) phi0 = phi;
|
|---|
| 281 | if (phi > phi1) phi1 = phi;
|
|---|
| 282 | }
|
|---|
| 283 | if (antimeridian) {
|
|---|
| 284 | if (lambda < lambda2) {
|
|---|
| 285 | if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) lambda1 = lambda;
|
|---|
| 286 | } else {
|
|---|
| 287 | if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) lambda0$1 = lambda;
|
|---|
| 288 | }
|
|---|
| 289 | } else {
|
|---|
| 290 | if (lambda1 >= lambda0$1) {
|
|---|
| 291 | if (lambda < lambda0$1) lambda0$1 = lambda;
|
|---|
| 292 | if (lambda > lambda1) lambda1 = lambda;
|
|---|
| 293 | } else {
|
|---|
| 294 | if (lambda > lambda2) {
|
|---|
| 295 | if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) lambda1 = lambda;
|
|---|
| 296 | } else {
|
|---|
| 297 | if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) lambda0$1 = lambda;
|
|---|
| 298 | }
|
|---|
| 299 | }
|
|---|
| 300 | }
|
|---|
| 301 | } else {
|
|---|
| 302 | ranges.push(range = [lambda0$1 = lambda, lambda1 = lambda]);
|
|---|
| 303 | }
|
|---|
| 304 | if (phi < phi0) phi0 = phi;
|
|---|
| 305 | if (phi > phi1) phi1 = phi;
|
|---|
| 306 | p0 = p, lambda2 = lambda;
|
|---|
| 307 | }
|
|---|
| 308 |
|
|---|
| 309 | function boundsLineStart() {
|
|---|
| 310 | boundsStream$1.point = linePoint;
|
|---|
| 311 | }
|
|---|
| 312 |
|
|---|
| 313 | function boundsLineEnd() {
|
|---|
| 314 | range[0] = lambda0$1, range[1] = lambda1;
|
|---|
| 315 | boundsStream$1.point = boundsPoint$1;
|
|---|
| 316 | p0 = null;
|
|---|
| 317 | }
|
|---|
| 318 |
|
|---|
| 319 | function boundsRingPoint(lambda, phi) {
|
|---|
| 320 | if (p0) {
|
|---|
| 321 | var delta = lambda - lambda2;
|
|---|
| 322 | deltaSum.add(abs(delta) > 180 ? delta + (delta > 0 ? 360 : -360) : delta);
|
|---|
| 323 | } else {
|
|---|
| 324 | lambda00$1 = lambda, phi00$1 = phi;
|
|---|
| 325 | }
|
|---|
| 326 | areaStream$1.point(lambda, phi);
|
|---|
| 327 | linePoint(lambda, phi);
|
|---|
| 328 | }
|
|---|
| 329 |
|
|---|
| 330 | function boundsRingStart() {
|
|---|
| 331 | areaStream$1.lineStart();
|
|---|
| 332 | }
|
|---|
| 333 |
|
|---|
| 334 | function boundsRingEnd() {
|
|---|
| 335 | boundsRingPoint(lambda00$1, phi00$1);
|
|---|
| 336 | areaStream$1.lineEnd();
|
|---|
| 337 | if (abs(deltaSum) > epsilon) lambda0$1 = -(lambda1 = 180);
|
|---|
| 338 | range[0] = lambda0$1, range[1] = lambda1;
|
|---|
| 339 | p0 = null;
|
|---|
| 340 | }
|
|---|
| 341 |
|
|---|
| 342 | // Finds the left-right distance between two longitudes.
|
|---|
| 343 | // This is almost the same as (lambda1 - lambda0 + 360°) % 360°, except that we want
|
|---|
| 344 | // the distance between ±180° to be 360°.
|
|---|
| 345 | function angle(lambda0, lambda1) {
|
|---|
| 346 | return (lambda1 -= lambda0) < 0 ? lambda1 + 360 : lambda1;
|
|---|
| 347 | }
|
|---|
| 348 |
|
|---|
| 349 | function rangeCompare(a, b) {
|
|---|
| 350 | return a[0] - b[0];
|
|---|
| 351 | }
|
|---|
| 352 |
|
|---|
| 353 | function rangeContains(range, x) {
|
|---|
| 354 | return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x;
|
|---|
| 355 | }
|
|---|
| 356 |
|
|---|
| 357 | function bounds(feature) {
|
|---|
| 358 | var i, n, a, b, merged, deltaMax, delta;
|
|---|
| 359 |
|
|---|
| 360 | phi1 = lambda1 = -(lambda0$1 = phi0 = Infinity);
|
|---|
| 361 | ranges = [];
|
|---|
| 362 | geoStream(feature, boundsStream$1);
|
|---|
| 363 |
|
|---|
| 364 | // First, sort ranges by their minimum longitudes.
|
|---|
| 365 | if (n = ranges.length) {
|
|---|
| 366 | ranges.sort(rangeCompare);
|
|---|
| 367 |
|
|---|
| 368 | // Then, merge any ranges that overlap.
|
|---|
| 369 | for (i = 1, a = ranges[0], merged = [a]; i < n; ++i) {
|
|---|
| 370 | b = ranges[i];
|
|---|
| 371 | if (rangeContains(a, b[0]) || rangeContains(a, b[1])) {
|
|---|
| 372 | if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1];
|
|---|
| 373 | if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0];
|
|---|
| 374 | } else {
|
|---|
| 375 | merged.push(a = b);
|
|---|
| 376 | }
|
|---|
| 377 | }
|
|---|
| 378 |
|
|---|
| 379 | // Finally, find the largest gap between the merged ranges.
|
|---|
| 380 | // The final bounding box will be the inverse of this gap.
|
|---|
| 381 | for (deltaMax = -Infinity, n = merged.length - 1, i = 0, a = merged[n]; i <= n; a = b, ++i) {
|
|---|
| 382 | b = merged[i];
|
|---|
| 383 | if ((delta = angle(a[1], b[0])) > deltaMax) deltaMax = delta, lambda0$1 = b[0], lambda1 = a[1];
|
|---|
| 384 | }
|
|---|
| 385 | }
|
|---|
| 386 |
|
|---|
| 387 | ranges = range = null;
|
|---|
| 388 |
|
|---|
| 389 | return lambda0$1 === Infinity || phi0 === Infinity
|
|---|
| 390 | ? [[NaN, NaN], [NaN, NaN]]
|
|---|
| 391 | : [[lambda0$1, phi0], [lambda1, phi1]];
|
|---|
| 392 | }
|
|---|
| 393 |
|
|---|
| 394 | var W0, W1,
|
|---|
| 395 | X0$1, Y0$1, Z0$1,
|
|---|
| 396 | X1$1, Y1$1, Z1$1,
|
|---|
| 397 | X2$1, Y2$1, Z2$1,
|
|---|
| 398 | lambda00, phi00, // first point
|
|---|
| 399 | x0$4, y0$4, z0; // previous point
|
|---|
| 400 |
|
|---|
| 401 | var centroidStream$1 = {
|
|---|
| 402 | sphere: noop,
|
|---|
| 403 | point: centroidPoint$1,
|
|---|
| 404 | lineStart: centroidLineStart$1,
|
|---|
| 405 | lineEnd: centroidLineEnd$1,
|
|---|
| 406 | polygonStart: function() {
|
|---|
| 407 | centroidStream$1.lineStart = centroidRingStart$1;
|
|---|
| 408 | centroidStream$1.lineEnd = centroidRingEnd$1;
|
|---|
| 409 | },
|
|---|
| 410 | polygonEnd: function() {
|
|---|
| 411 | centroidStream$1.lineStart = centroidLineStart$1;
|
|---|
| 412 | centroidStream$1.lineEnd = centroidLineEnd$1;
|
|---|
| 413 | }
|
|---|
| 414 | };
|
|---|
| 415 |
|
|---|
| 416 | // Arithmetic mean of Cartesian vectors.
|
|---|
| 417 | function centroidPoint$1(lambda, phi) {
|
|---|
| 418 | lambda *= radians, phi *= radians;
|
|---|
| 419 | var cosPhi = cos(phi);
|
|---|
| 420 | centroidPointCartesian(cosPhi * cos(lambda), cosPhi * sin(lambda), sin(phi));
|
|---|
| 421 | }
|
|---|
| 422 |
|
|---|
| 423 | function centroidPointCartesian(x, y, z) {
|
|---|
| 424 | ++W0;
|
|---|
| 425 | X0$1 += (x - X0$1) / W0;
|
|---|
| 426 | Y0$1 += (y - Y0$1) / W0;
|
|---|
| 427 | Z0$1 += (z - Z0$1) / W0;
|
|---|
| 428 | }
|
|---|
| 429 |
|
|---|
| 430 | function centroidLineStart$1() {
|
|---|
| 431 | centroidStream$1.point = centroidLinePointFirst;
|
|---|
| 432 | }
|
|---|
| 433 |
|
|---|
| 434 | function centroidLinePointFirst(lambda, phi) {
|
|---|
| 435 | lambda *= radians, phi *= radians;
|
|---|
| 436 | var cosPhi = cos(phi);
|
|---|
| 437 | x0$4 = cosPhi * cos(lambda);
|
|---|
| 438 | y0$4 = cosPhi * sin(lambda);
|
|---|
| 439 | z0 = sin(phi);
|
|---|
| 440 | centroidStream$1.point = centroidLinePoint;
|
|---|
| 441 | centroidPointCartesian(x0$4, y0$4, z0);
|
|---|
| 442 | }
|
|---|
| 443 |
|
|---|
| 444 | function centroidLinePoint(lambda, phi) {
|
|---|
| 445 | lambda *= radians, phi *= radians;
|
|---|
| 446 | var cosPhi = cos(phi),
|
|---|
| 447 | x = cosPhi * cos(lambda),
|
|---|
| 448 | y = cosPhi * sin(lambda),
|
|---|
| 449 | z = sin(phi),
|
|---|
| 450 | w = atan2(sqrt((w = y0$4 * z - z0 * y) * w + (w = z0 * x - x0$4 * z) * w + (w = x0$4 * y - y0$4 * x) * w), x0$4 * x + y0$4 * y + z0 * z);
|
|---|
| 451 | W1 += w;
|
|---|
| 452 | X1$1 += w * (x0$4 + (x0$4 = x));
|
|---|
| 453 | Y1$1 += w * (y0$4 + (y0$4 = y));
|
|---|
| 454 | Z1$1 += w * (z0 + (z0 = z));
|
|---|
| 455 | centroidPointCartesian(x0$4, y0$4, z0);
|
|---|
| 456 | }
|
|---|
| 457 |
|
|---|
| 458 | function centroidLineEnd$1() {
|
|---|
| 459 | centroidStream$1.point = centroidPoint$1;
|
|---|
| 460 | }
|
|---|
| 461 |
|
|---|
| 462 | // See J. E. Brock, The Inertia Tensor for a Spherical Triangle,
|
|---|
| 463 | // J. Applied Mechanics 42, 239 (1975).
|
|---|
| 464 | function centroidRingStart$1() {
|
|---|
| 465 | centroidStream$1.point = centroidRingPointFirst;
|
|---|
| 466 | }
|
|---|
| 467 |
|
|---|
| 468 | function centroidRingEnd$1() {
|
|---|
| 469 | centroidRingPoint(lambda00, phi00);
|
|---|
| 470 | centroidStream$1.point = centroidPoint$1;
|
|---|
| 471 | }
|
|---|
| 472 |
|
|---|
| 473 | function centroidRingPointFirst(lambda, phi) {
|
|---|
| 474 | lambda00 = lambda, phi00 = phi;
|
|---|
| 475 | lambda *= radians, phi *= radians;
|
|---|
| 476 | centroidStream$1.point = centroidRingPoint;
|
|---|
| 477 | var cosPhi = cos(phi);
|
|---|
| 478 | x0$4 = cosPhi * cos(lambda);
|
|---|
| 479 | y0$4 = cosPhi * sin(lambda);
|
|---|
| 480 | z0 = sin(phi);
|
|---|
| 481 | centroidPointCartesian(x0$4, y0$4, z0);
|
|---|
| 482 | }
|
|---|
| 483 |
|
|---|
| 484 | function centroidRingPoint(lambda, phi) {
|
|---|
| 485 | lambda *= radians, phi *= radians;
|
|---|
| 486 | var cosPhi = cos(phi),
|
|---|
| 487 | x = cosPhi * cos(lambda),
|
|---|
| 488 | y = cosPhi * sin(lambda),
|
|---|
| 489 | z = sin(phi),
|
|---|
| 490 | cx = y0$4 * z - z0 * y,
|
|---|
| 491 | cy = z0 * x - x0$4 * z,
|
|---|
| 492 | cz = x0$4 * y - y0$4 * x,
|
|---|
| 493 | m = hypot(cx, cy, cz),
|
|---|
| 494 | w = asin(m), // line weight = angle
|
|---|
| 495 | v = m && -w / m; // area weight multiplier
|
|---|
| 496 | X2$1.add(v * cx);
|
|---|
| 497 | Y2$1.add(v * cy);
|
|---|
| 498 | Z2$1.add(v * cz);
|
|---|
| 499 | W1 += w;
|
|---|
| 500 | X1$1 += w * (x0$4 + (x0$4 = x));
|
|---|
| 501 | Y1$1 += w * (y0$4 + (y0$4 = y));
|
|---|
| 502 | Z1$1 += w * (z0 + (z0 = z));
|
|---|
| 503 | centroidPointCartesian(x0$4, y0$4, z0);
|
|---|
| 504 | }
|
|---|
| 505 |
|
|---|
| 506 | function centroid(object) {
|
|---|
| 507 | W0 = W1 =
|
|---|
| 508 | X0$1 = Y0$1 = Z0$1 =
|
|---|
| 509 | X1$1 = Y1$1 = Z1$1 = 0;
|
|---|
| 510 | X2$1 = new d3Array.Adder();
|
|---|
| 511 | Y2$1 = new d3Array.Adder();
|
|---|
| 512 | Z2$1 = new d3Array.Adder();
|
|---|
| 513 | geoStream(object, centroidStream$1);
|
|---|
| 514 |
|
|---|
| 515 | var x = +X2$1,
|
|---|
| 516 | y = +Y2$1,
|
|---|
| 517 | z = +Z2$1,
|
|---|
| 518 | m = hypot(x, y, z);
|
|---|
| 519 |
|
|---|
| 520 | // If the area-weighted ccentroid is undefined, fall back to length-weighted ccentroid.
|
|---|
| 521 | if (m < epsilon2) {
|
|---|
| 522 | x = X1$1, y = Y1$1, z = Z1$1;
|
|---|
| 523 | // If the feature has zero length, fall back to arithmetic mean of point vectors.
|
|---|
| 524 | if (W1 < epsilon) x = X0$1, y = Y0$1, z = Z0$1;
|
|---|
| 525 | m = hypot(x, y, z);
|
|---|
| 526 | // If the feature still has an undefined ccentroid, then return.
|
|---|
| 527 | if (m < epsilon2) return [NaN, NaN];
|
|---|
| 528 | }
|
|---|
| 529 |
|
|---|
| 530 | return [atan2(y, x) * degrees, asin(z / m) * degrees];
|
|---|
| 531 | }
|
|---|
| 532 |
|
|---|
| 533 | function constant(x) {
|
|---|
| 534 | return function() {
|
|---|
| 535 | return x;
|
|---|
| 536 | };
|
|---|
| 537 | }
|
|---|
| 538 |
|
|---|
| 539 | function compose(a, b) {
|
|---|
| 540 |
|
|---|
| 541 | function compose(x, y) {
|
|---|
| 542 | return x = a(x, y), b(x[0], x[1]);
|
|---|
| 543 | }
|
|---|
| 544 |
|
|---|
| 545 | if (a.invert && b.invert) compose.invert = function(x, y) {
|
|---|
| 546 | return x = b.invert(x, y), x && a.invert(x[0], x[1]);
|
|---|
| 547 | };
|
|---|
| 548 |
|
|---|
| 549 | return compose;
|
|---|
| 550 | }
|
|---|
| 551 |
|
|---|
| 552 | function rotationIdentity(lambda, phi) {
|
|---|
| 553 | if (abs(lambda) > pi) lambda -= Math.round(lambda / tau) * tau;
|
|---|
| 554 | return [lambda, phi];
|
|---|
| 555 | }
|
|---|
| 556 |
|
|---|
| 557 | rotationIdentity.invert = rotationIdentity;
|
|---|
| 558 |
|
|---|
| 559 | function rotateRadians(deltaLambda, deltaPhi, deltaGamma) {
|
|---|
| 560 | return (deltaLambda %= tau) ? (deltaPhi || deltaGamma ? compose(rotationLambda(deltaLambda), rotationPhiGamma(deltaPhi, deltaGamma))
|
|---|
| 561 | : rotationLambda(deltaLambda))
|
|---|
| 562 | : (deltaPhi || deltaGamma ? rotationPhiGamma(deltaPhi, deltaGamma)
|
|---|
| 563 | : rotationIdentity);
|
|---|
| 564 | }
|
|---|
| 565 |
|
|---|
| 566 | function forwardRotationLambda(deltaLambda) {
|
|---|
| 567 | return function(lambda, phi) {
|
|---|
| 568 | lambda += deltaLambda;
|
|---|
| 569 | if (abs(lambda) > pi) lambda -= Math.round(lambda / tau) * tau;
|
|---|
| 570 | return [lambda, phi];
|
|---|
| 571 | };
|
|---|
| 572 | }
|
|---|
| 573 |
|
|---|
| 574 | function rotationLambda(deltaLambda) {
|
|---|
| 575 | var rotation = forwardRotationLambda(deltaLambda);
|
|---|
| 576 | rotation.invert = forwardRotationLambda(-deltaLambda);
|
|---|
| 577 | return rotation;
|
|---|
| 578 | }
|
|---|
| 579 |
|
|---|
| 580 | function rotationPhiGamma(deltaPhi, deltaGamma) {
|
|---|
| 581 | var cosDeltaPhi = cos(deltaPhi),
|
|---|
| 582 | sinDeltaPhi = sin(deltaPhi),
|
|---|
| 583 | cosDeltaGamma = cos(deltaGamma),
|
|---|
| 584 | sinDeltaGamma = sin(deltaGamma);
|
|---|
| 585 |
|
|---|
| 586 | function rotation(lambda, phi) {
|
|---|
| 587 | var cosPhi = cos(phi),
|
|---|
| 588 | x = cos(lambda) * cosPhi,
|
|---|
| 589 | y = sin(lambda) * cosPhi,
|
|---|
| 590 | z = sin(phi),
|
|---|
| 591 | k = z * cosDeltaPhi + x * sinDeltaPhi;
|
|---|
| 592 | return [
|
|---|
| 593 | atan2(y * cosDeltaGamma - k * sinDeltaGamma, x * cosDeltaPhi - z * sinDeltaPhi),
|
|---|
| 594 | asin(k * cosDeltaGamma + y * sinDeltaGamma)
|
|---|
| 595 | ];
|
|---|
| 596 | }
|
|---|
| 597 |
|
|---|
| 598 | rotation.invert = function(lambda, phi) {
|
|---|
| 599 | var cosPhi = cos(phi),
|
|---|
| 600 | x = cos(lambda) * cosPhi,
|
|---|
| 601 | y = sin(lambda) * cosPhi,
|
|---|
| 602 | z = sin(phi),
|
|---|
| 603 | k = z * cosDeltaGamma - y * sinDeltaGamma;
|
|---|
| 604 | return [
|
|---|
| 605 | atan2(y * cosDeltaGamma + z * sinDeltaGamma, x * cosDeltaPhi + k * sinDeltaPhi),
|
|---|
| 606 | asin(k * cosDeltaPhi - x * sinDeltaPhi)
|
|---|
| 607 | ];
|
|---|
| 608 | };
|
|---|
| 609 |
|
|---|
| 610 | return rotation;
|
|---|
| 611 | }
|
|---|
| 612 |
|
|---|
| 613 | function rotation(rotate) {
|
|---|
| 614 | rotate = rotateRadians(rotate[0] * radians, rotate[1] * radians, rotate.length > 2 ? rotate[2] * radians : 0);
|
|---|
| 615 |
|
|---|
| 616 | function forward(coordinates) {
|
|---|
| 617 | coordinates = rotate(coordinates[0] * radians, coordinates[1] * radians);
|
|---|
| 618 | return coordinates[0] *= degrees, coordinates[1] *= degrees, coordinates;
|
|---|
| 619 | }
|
|---|
| 620 |
|
|---|
| 621 | forward.invert = function(coordinates) {
|
|---|
| 622 | coordinates = rotate.invert(coordinates[0] * radians, coordinates[1] * radians);
|
|---|
| 623 | return coordinates[0] *= degrees, coordinates[1] *= degrees, coordinates;
|
|---|
| 624 | };
|
|---|
| 625 |
|
|---|
| 626 | return forward;
|
|---|
| 627 | }
|
|---|
| 628 |
|
|---|
| 629 | // Generates a circle centered at [0°, 0°], with a given radius and precision.
|
|---|
| 630 | function circleStream(stream, radius, delta, direction, t0, t1) {
|
|---|
| 631 | if (!delta) return;
|
|---|
| 632 | var cosRadius = cos(radius),
|
|---|
| 633 | sinRadius = sin(radius),
|
|---|
| 634 | step = direction * delta;
|
|---|
| 635 | if (t0 == null) {
|
|---|
| 636 | t0 = radius + direction * tau;
|
|---|
| 637 | t1 = radius - step / 2;
|
|---|
| 638 | } else {
|
|---|
| 639 | t0 = circleRadius(cosRadius, t0);
|
|---|
| 640 | t1 = circleRadius(cosRadius, t1);
|
|---|
| 641 | if (direction > 0 ? t0 < t1 : t0 > t1) t0 += direction * tau;
|
|---|
| 642 | }
|
|---|
| 643 | for (var point, t = t0; direction > 0 ? t > t1 : t < t1; t -= step) {
|
|---|
| 644 | point = spherical([cosRadius, -sinRadius * cos(t), -sinRadius * sin(t)]);
|
|---|
| 645 | stream.point(point[0], point[1]);
|
|---|
| 646 | }
|
|---|
| 647 | }
|
|---|
| 648 |
|
|---|
| 649 | // Returns the signed angle of a cartesian point relative to [cosRadius, 0, 0].
|
|---|
| 650 | function circleRadius(cosRadius, point) {
|
|---|
| 651 | point = cartesian(point), point[0] -= cosRadius;
|
|---|
| 652 | cartesianNormalizeInPlace(point);
|
|---|
| 653 | var radius = acos(-point[1]);
|
|---|
| 654 | return ((-point[2] < 0 ? -radius : radius) + tau - epsilon) % tau;
|
|---|
| 655 | }
|
|---|
| 656 |
|
|---|
| 657 | function circle() {
|
|---|
| 658 | var center = constant([0, 0]),
|
|---|
| 659 | radius = constant(90),
|
|---|
| 660 | precision = constant(2),
|
|---|
| 661 | ring,
|
|---|
| 662 | rotate,
|
|---|
| 663 | stream = {point: point};
|
|---|
| 664 |
|
|---|
| 665 | function point(x, y) {
|
|---|
| 666 | ring.push(x = rotate(x, y));
|
|---|
| 667 | x[0] *= degrees, x[1] *= degrees;
|
|---|
| 668 | }
|
|---|
| 669 |
|
|---|
| 670 | function circle() {
|
|---|
| 671 | var c = center.apply(this, arguments),
|
|---|
| 672 | r = radius.apply(this, arguments) * radians,
|
|---|
| 673 | p = precision.apply(this, arguments) * radians;
|
|---|
| 674 | ring = [];
|
|---|
| 675 | rotate = rotateRadians(-c[0] * radians, -c[1] * radians, 0).invert;
|
|---|
| 676 | circleStream(stream, r, p, 1);
|
|---|
| 677 | c = {type: "Polygon", coordinates: [ring]};
|
|---|
| 678 | ring = rotate = null;
|
|---|
| 679 | return c;
|
|---|
| 680 | }
|
|---|
| 681 |
|
|---|
| 682 | circle.center = function(_) {
|
|---|
| 683 | return arguments.length ? (center = typeof _ === "function" ? _ : constant([+_[0], +_[1]]), circle) : center;
|
|---|
| 684 | };
|
|---|
| 685 |
|
|---|
| 686 | circle.radius = function(_) {
|
|---|
| 687 | return arguments.length ? (radius = typeof _ === "function" ? _ : constant(+_), circle) : radius;
|
|---|
| 688 | };
|
|---|
| 689 |
|
|---|
| 690 | circle.precision = function(_) {
|
|---|
| 691 | return arguments.length ? (precision = typeof _ === "function" ? _ : constant(+_), circle) : precision;
|
|---|
| 692 | };
|
|---|
| 693 |
|
|---|
| 694 | return circle;
|
|---|
| 695 | }
|
|---|
| 696 |
|
|---|
| 697 | function clipBuffer() {
|
|---|
| 698 | var lines = [],
|
|---|
| 699 | line;
|
|---|
| 700 | return {
|
|---|
| 701 | point: function(x, y, m) {
|
|---|
| 702 | line.push([x, y, m]);
|
|---|
| 703 | },
|
|---|
| 704 | lineStart: function() {
|
|---|
| 705 | lines.push(line = []);
|
|---|
| 706 | },
|
|---|
| 707 | lineEnd: noop,
|
|---|
| 708 | rejoin: function() {
|
|---|
| 709 | if (lines.length > 1) lines.push(lines.pop().concat(lines.shift()));
|
|---|
| 710 | },
|
|---|
| 711 | result: function() {
|
|---|
| 712 | var result = lines;
|
|---|
| 713 | lines = [];
|
|---|
| 714 | line = null;
|
|---|
| 715 | return result;
|
|---|
| 716 | }
|
|---|
| 717 | };
|
|---|
| 718 | }
|
|---|
| 719 |
|
|---|
| 720 | function pointEqual(a, b) {
|
|---|
| 721 | return abs(a[0] - b[0]) < epsilon && abs(a[1] - b[1]) < epsilon;
|
|---|
| 722 | }
|
|---|
| 723 |
|
|---|
| 724 | function Intersection(point, points, other, entry) {
|
|---|
| 725 | this.x = point;
|
|---|
| 726 | this.z = points;
|
|---|
| 727 | this.o = other; // another intersection
|
|---|
| 728 | this.e = entry; // is an entry?
|
|---|
| 729 | this.v = false; // visited
|
|---|
| 730 | this.n = this.p = null; // next & previous
|
|---|
| 731 | }
|
|---|
| 732 |
|
|---|
| 733 | // A generalized polygon clipping algorithm: given a polygon that has been cut
|
|---|
| 734 | // into its visible line segments, and rejoins the segments by interpolating
|
|---|
| 735 | // along the clip edge.
|
|---|
| 736 | function clipRejoin(segments, compareIntersection, startInside, interpolate, stream) {
|
|---|
| 737 | var subject = [],
|
|---|
| 738 | clip = [],
|
|---|
| 739 | i,
|
|---|
| 740 | n;
|
|---|
| 741 |
|
|---|
| 742 | segments.forEach(function(segment) {
|
|---|
| 743 | if ((n = segment.length - 1) <= 0) return;
|
|---|
| 744 | var n, p0 = segment[0], p1 = segment[n], x;
|
|---|
| 745 |
|
|---|
| 746 | if (pointEqual(p0, p1)) {
|
|---|
| 747 | if (!p0[2] && !p1[2]) {
|
|---|
| 748 | stream.lineStart();
|
|---|
| 749 | for (i = 0; i < n; ++i) stream.point((p0 = segment[i])[0], p0[1]);
|
|---|
| 750 | stream.lineEnd();
|
|---|
| 751 | return;
|
|---|
| 752 | }
|
|---|
| 753 | // handle degenerate cases by moving the point
|
|---|
| 754 | p1[0] += 2 * epsilon;
|
|---|
| 755 | }
|
|---|
| 756 |
|
|---|
| 757 | subject.push(x = new Intersection(p0, segment, null, true));
|
|---|
| 758 | clip.push(x.o = new Intersection(p0, null, x, false));
|
|---|
| 759 | subject.push(x = new Intersection(p1, segment, null, false));
|
|---|
| 760 | clip.push(x.o = new Intersection(p1, null, x, true));
|
|---|
| 761 | });
|
|---|
| 762 |
|
|---|
| 763 | if (!subject.length) return;
|
|---|
| 764 |
|
|---|
| 765 | clip.sort(compareIntersection);
|
|---|
| 766 | link(subject);
|
|---|
| 767 | link(clip);
|
|---|
| 768 |
|
|---|
| 769 | for (i = 0, n = clip.length; i < n; ++i) {
|
|---|
| 770 | clip[i].e = startInside = !startInside;
|
|---|
| 771 | }
|
|---|
| 772 |
|
|---|
| 773 | var start = subject[0],
|
|---|
| 774 | points,
|
|---|
| 775 | point;
|
|---|
| 776 |
|
|---|
| 777 | while (1) {
|
|---|
| 778 | // Find first unvisited intersection.
|
|---|
| 779 | var current = start,
|
|---|
| 780 | isSubject = true;
|
|---|
| 781 | while (current.v) if ((current = current.n) === start) return;
|
|---|
| 782 | points = current.z;
|
|---|
| 783 | stream.lineStart();
|
|---|
| 784 | do {
|
|---|
| 785 | current.v = current.o.v = true;
|
|---|
| 786 | if (current.e) {
|
|---|
| 787 | if (isSubject) {
|
|---|
| 788 | for (i = 0, n = points.length; i < n; ++i) stream.point((point = points[i])[0], point[1]);
|
|---|
| 789 | } else {
|
|---|
| 790 | interpolate(current.x, current.n.x, 1, stream);
|
|---|
| 791 | }
|
|---|
| 792 | current = current.n;
|
|---|
| 793 | } else {
|
|---|
| 794 | if (isSubject) {
|
|---|
| 795 | points = current.p.z;
|
|---|
| 796 | for (i = points.length - 1; i >= 0; --i) stream.point((point = points[i])[0], point[1]);
|
|---|
| 797 | } else {
|
|---|
| 798 | interpolate(current.x, current.p.x, -1, stream);
|
|---|
| 799 | }
|
|---|
| 800 | current = current.p;
|
|---|
| 801 | }
|
|---|
| 802 | current = current.o;
|
|---|
| 803 | points = current.z;
|
|---|
| 804 | isSubject = !isSubject;
|
|---|
| 805 | } while (!current.v);
|
|---|
| 806 | stream.lineEnd();
|
|---|
| 807 | }
|
|---|
| 808 | }
|
|---|
| 809 |
|
|---|
| 810 | function link(array) {
|
|---|
| 811 | if (!(n = array.length)) return;
|
|---|
| 812 | var n,
|
|---|
| 813 | i = 0,
|
|---|
| 814 | a = array[0],
|
|---|
| 815 | b;
|
|---|
| 816 | while (++i < n) {
|
|---|
| 817 | a.n = b = array[i];
|
|---|
| 818 | b.p = a;
|
|---|
| 819 | a = b;
|
|---|
| 820 | }
|
|---|
| 821 | a.n = b = array[0];
|
|---|
| 822 | b.p = a;
|
|---|
| 823 | }
|
|---|
| 824 |
|
|---|
| 825 | function longitude(point) {
|
|---|
| 826 | return abs(point[0]) <= pi ? point[0] : sign(point[0]) * ((abs(point[0]) + pi) % tau - pi);
|
|---|
| 827 | }
|
|---|
| 828 |
|
|---|
| 829 | function polygonContains(polygon, point) {
|
|---|
| 830 | var lambda = longitude(point),
|
|---|
| 831 | phi = point[1],
|
|---|
| 832 | sinPhi = sin(phi),
|
|---|
| 833 | normal = [sin(lambda), -cos(lambda), 0],
|
|---|
| 834 | angle = 0,
|
|---|
| 835 | winding = 0;
|
|---|
| 836 |
|
|---|
| 837 | var sum = new d3Array.Adder();
|
|---|
| 838 |
|
|---|
| 839 | if (sinPhi === 1) phi = halfPi + epsilon;
|
|---|
| 840 | else if (sinPhi === -1) phi = -halfPi - epsilon;
|
|---|
| 841 |
|
|---|
| 842 | for (var i = 0, n = polygon.length; i < n; ++i) {
|
|---|
| 843 | if (!(m = (ring = polygon[i]).length)) continue;
|
|---|
| 844 | var ring,
|
|---|
| 845 | m,
|
|---|
| 846 | point0 = ring[m - 1],
|
|---|
| 847 | lambda0 = longitude(point0),
|
|---|
| 848 | phi0 = point0[1] / 2 + quarterPi,
|
|---|
| 849 | sinPhi0 = sin(phi0),
|
|---|
| 850 | cosPhi0 = cos(phi0);
|
|---|
| 851 |
|
|---|
| 852 | for (var j = 0; j < m; ++j, lambda0 = lambda1, sinPhi0 = sinPhi1, cosPhi0 = cosPhi1, point0 = point1) {
|
|---|
| 853 | var point1 = ring[j],
|
|---|
| 854 | lambda1 = longitude(point1),
|
|---|
| 855 | phi1 = point1[1] / 2 + quarterPi,
|
|---|
| 856 | sinPhi1 = sin(phi1),
|
|---|
| 857 | cosPhi1 = cos(phi1),
|
|---|
| 858 | delta = lambda1 - lambda0,
|
|---|
| 859 | sign = delta >= 0 ? 1 : -1,
|
|---|
| 860 | absDelta = sign * delta,
|
|---|
| 861 | antimeridian = absDelta > pi,
|
|---|
| 862 | k = sinPhi0 * sinPhi1;
|
|---|
| 863 |
|
|---|
| 864 | sum.add(atan2(k * sign * sin(absDelta), cosPhi0 * cosPhi1 + k * cos(absDelta)));
|
|---|
| 865 | angle += antimeridian ? delta + sign * tau : delta;
|
|---|
| 866 |
|
|---|
| 867 | // Are the longitudes either side of the point’s meridian (lambda),
|
|---|
| 868 | // and are the latitudes smaller than the parallel (phi)?
|
|---|
| 869 | if (antimeridian ^ lambda0 >= lambda ^ lambda1 >= lambda) {
|
|---|
| 870 | var arc = cartesianCross(cartesian(point0), cartesian(point1));
|
|---|
| 871 | cartesianNormalizeInPlace(arc);
|
|---|
| 872 | var intersection = cartesianCross(normal, arc);
|
|---|
| 873 | cartesianNormalizeInPlace(intersection);
|
|---|
| 874 | var phiArc = (antimeridian ^ delta >= 0 ? -1 : 1) * asin(intersection[2]);
|
|---|
| 875 | if (phi > phiArc || phi === phiArc && (arc[0] || arc[1])) {
|
|---|
| 876 | winding += antimeridian ^ delta >= 0 ? 1 : -1;
|
|---|
| 877 | }
|
|---|
| 878 | }
|
|---|
| 879 | }
|
|---|
| 880 | }
|
|---|
| 881 |
|
|---|
| 882 | // First, determine whether the South pole is inside or outside:
|
|---|
| 883 | //
|
|---|
| 884 | // It is inside if:
|
|---|
| 885 | // * the polygon winds around it in a clockwise direction.
|
|---|
| 886 | // * the polygon does not (cumulatively) wind around it, but has a negative
|
|---|
| 887 | // (counter-clockwise) area.
|
|---|
| 888 | //
|
|---|
| 889 | // Second, count the (signed) number of times a segment crosses a lambda
|
|---|
| 890 | // from the point to the South pole. If it is zero, then the point is the
|
|---|
| 891 | // same side as the South pole.
|
|---|
| 892 |
|
|---|
| 893 | return (angle < -epsilon || angle < epsilon && sum < -epsilon2) ^ (winding & 1);
|
|---|
| 894 | }
|
|---|
| 895 |
|
|---|
| 896 | function clip(pointVisible, clipLine, interpolate, start) {
|
|---|
| 897 | return function(sink) {
|
|---|
| 898 | var line = clipLine(sink),
|
|---|
| 899 | ringBuffer = clipBuffer(),
|
|---|
| 900 | ringSink = clipLine(ringBuffer),
|
|---|
| 901 | polygonStarted = false,
|
|---|
| 902 | polygon,
|
|---|
| 903 | segments,
|
|---|
| 904 | ring;
|
|---|
| 905 |
|
|---|
| 906 | var clip = {
|
|---|
| 907 | point: point,
|
|---|
| 908 | lineStart: lineStart,
|
|---|
| 909 | lineEnd: lineEnd,
|
|---|
| 910 | polygonStart: function() {
|
|---|
| 911 | clip.point = pointRing;
|
|---|
| 912 | clip.lineStart = ringStart;
|
|---|
| 913 | clip.lineEnd = ringEnd;
|
|---|
| 914 | segments = [];
|
|---|
| 915 | polygon = [];
|
|---|
| 916 | },
|
|---|
| 917 | polygonEnd: function() {
|
|---|
| 918 | clip.point = point;
|
|---|
| 919 | clip.lineStart = lineStart;
|
|---|
| 920 | clip.lineEnd = lineEnd;
|
|---|
| 921 | segments = d3Array.merge(segments);
|
|---|
| 922 | var startInside = polygonContains(polygon, start);
|
|---|
| 923 | if (segments.length) {
|
|---|
| 924 | if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
|
|---|
| 925 | clipRejoin(segments, compareIntersection, startInside, interpolate, sink);
|
|---|
| 926 | } else if (startInside) {
|
|---|
| 927 | if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
|
|---|
| 928 | sink.lineStart();
|
|---|
| 929 | interpolate(null, null, 1, sink);
|
|---|
| 930 | sink.lineEnd();
|
|---|
| 931 | }
|
|---|
| 932 | if (polygonStarted) sink.polygonEnd(), polygonStarted = false;
|
|---|
| 933 | segments = polygon = null;
|
|---|
| 934 | },
|
|---|
| 935 | sphere: function() {
|
|---|
| 936 | sink.polygonStart();
|
|---|
| 937 | sink.lineStart();
|
|---|
| 938 | interpolate(null, null, 1, sink);
|
|---|
| 939 | sink.lineEnd();
|
|---|
| 940 | sink.polygonEnd();
|
|---|
| 941 | }
|
|---|
| 942 | };
|
|---|
| 943 |
|
|---|
| 944 | function point(lambda, phi) {
|
|---|
| 945 | if (pointVisible(lambda, phi)) sink.point(lambda, phi);
|
|---|
| 946 | }
|
|---|
| 947 |
|
|---|
| 948 | function pointLine(lambda, phi) {
|
|---|
| 949 | line.point(lambda, phi);
|
|---|
| 950 | }
|
|---|
| 951 |
|
|---|
| 952 | function lineStart() {
|
|---|
| 953 | clip.point = pointLine;
|
|---|
| 954 | line.lineStart();
|
|---|
| 955 | }
|
|---|
| 956 |
|
|---|
| 957 | function lineEnd() {
|
|---|
| 958 | clip.point = point;
|
|---|
| 959 | line.lineEnd();
|
|---|
| 960 | }
|
|---|
| 961 |
|
|---|
| 962 | function pointRing(lambda, phi) {
|
|---|
| 963 | ring.push([lambda, phi]);
|
|---|
| 964 | ringSink.point(lambda, phi);
|
|---|
| 965 | }
|
|---|
| 966 |
|
|---|
| 967 | function ringStart() {
|
|---|
| 968 | ringSink.lineStart();
|
|---|
| 969 | ring = [];
|
|---|
| 970 | }
|
|---|
| 971 |
|
|---|
| 972 | function ringEnd() {
|
|---|
| 973 | pointRing(ring[0][0], ring[0][1]);
|
|---|
| 974 | ringSink.lineEnd();
|
|---|
| 975 |
|
|---|
| 976 | var clean = ringSink.clean(),
|
|---|
| 977 | ringSegments = ringBuffer.result(),
|
|---|
| 978 | i, n = ringSegments.length, m,
|
|---|
| 979 | segment,
|
|---|
| 980 | point;
|
|---|
| 981 |
|
|---|
| 982 | ring.pop();
|
|---|
| 983 | polygon.push(ring);
|
|---|
| 984 | ring = null;
|
|---|
| 985 |
|
|---|
| 986 | if (!n) return;
|
|---|
| 987 |
|
|---|
| 988 | // No intersections.
|
|---|
| 989 | if (clean & 1) {
|
|---|
| 990 | segment = ringSegments[0];
|
|---|
| 991 | if ((m = segment.length - 1) > 0) {
|
|---|
| 992 | if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
|
|---|
| 993 | sink.lineStart();
|
|---|
| 994 | for (i = 0; i < m; ++i) sink.point((point = segment[i])[0], point[1]);
|
|---|
| 995 | sink.lineEnd();
|
|---|
| 996 | }
|
|---|
| 997 | return;
|
|---|
| 998 | }
|
|---|
| 999 |
|
|---|
| 1000 | // Rejoin connected segments.
|
|---|
| 1001 | // TODO reuse ringBuffer.rejoin()?
|
|---|
| 1002 | if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
|
|---|
| 1003 |
|
|---|
| 1004 | segments.push(ringSegments.filter(validSegment));
|
|---|
| 1005 | }
|
|---|
| 1006 |
|
|---|
| 1007 | return clip;
|
|---|
| 1008 | };
|
|---|
| 1009 | }
|
|---|
| 1010 |
|
|---|
| 1011 | function validSegment(segment) {
|
|---|
| 1012 | return segment.length > 1;
|
|---|
| 1013 | }
|
|---|
| 1014 |
|
|---|
| 1015 | // Intersections are sorted along the clip edge. For both antimeridian cutting
|
|---|
| 1016 | // and circle clipping, the same comparison is used.
|
|---|
| 1017 | function compareIntersection(a, b) {
|
|---|
| 1018 | return ((a = a.x)[0] < 0 ? a[1] - halfPi - epsilon : halfPi - a[1])
|
|---|
| 1019 | - ((b = b.x)[0] < 0 ? b[1] - halfPi - epsilon : halfPi - b[1]);
|
|---|
| 1020 | }
|
|---|
| 1021 |
|
|---|
| 1022 | var clipAntimeridian = clip(
|
|---|
| 1023 | function() { return true; },
|
|---|
| 1024 | clipAntimeridianLine,
|
|---|
| 1025 | clipAntimeridianInterpolate,
|
|---|
| 1026 | [-pi, -halfPi]
|
|---|
| 1027 | );
|
|---|
| 1028 |
|
|---|
| 1029 | // Takes a line and cuts into visible segments. Return values: 0 - there were
|
|---|
| 1030 | // intersections or the line was empty; 1 - no intersections; 2 - there were
|
|---|
| 1031 | // intersections, and the first and last segments should be rejoined.
|
|---|
| 1032 | function clipAntimeridianLine(stream) {
|
|---|
| 1033 | var lambda0 = NaN,
|
|---|
| 1034 | phi0 = NaN,
|
|---|
| 1035 | sign0 = NaN,
|
|---|
| 1036 | clean; // no intersections
|
|---|
| 1037 |
|
|---|
| 1038 | return {
|
|---|
| 1039 | lineStart: function() {
|
|---|
| 1040 | stream.lineStart();
|
|---|
| 1041 | clean = 1;
|
|---|
| 1042 | },
|
|---|
| 1043 | point: function(lambda1, phi1) {
|
|---|
| 1044 | var sign1 = lambda1 > 0 ? pi : -pi,
|
|---|
| 1045 | delta = abs(lambda1 - lambda0);
|
|---|
| 1046 | if (abs(delta - pi) < epsilon) { // line crosses a pole
|
|---|
| 1047 | stream.point(lambda0, phi0 = (phi0 + phi1) / 2 > 0 ? halfPi : -halfPi);
|
|---|
| 1048 | stream.point(sign0, phi0);
|
|---|
| 1049 | stream.lineEnd();
|
|---|
| 1050 | stream.lineStart();
|
|---|
| 1051 | stream.point(sign1, phi0);
|
|---|
| 1052 | stream.point(lambda1, phi0);
|
|---|
| 1053 | clean = 0;
|
|---|
| 1054 | } else if (sign0 !== sign1 && delta >= pi) { // line crosses antimeridian
|
|---|
| 1055 | if (abs(lambda0 - sign0) < epsilon) lambda0 -= sign0 * epsilon; // handle degeneracies
|
|---|
| 1056 | if (abs(lambda1 - sign1) < epsilon) lambda1 -= sign1 * epsilon;
|
|---|
| 1057 | phi0 = clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1);
|
|---|
| 1058 | stream.point(sign0, phi0);
|
|---|
| 1059 | stream.lineEnd();
|
|---|
| 1060 | stream.lineStart();
|
|---|
| 1061 | stream.point(sign1, phi0);
|
|---|
| 1062 | clean = 0;
|
|---|
| 1063 | }
|
|---|
| 1064 | stream.point(lambda0 = lambda1, phi0 = phi1);
|
|---|
| 1065 | sign0 = sign1;
|
|---|
| 1066 | },
|
|---|
| 1067 | lineEnd: function() {
|
|---|
| 1068 | stream.lineEnd();
|
|---|
| 1069 | lambda0 = phi0 = NaN;
|
|---|
| 1070 | },
|
|---|
| 1071 | clean: function() {
|
|---|
| 1072 | return 2 - clean; // if intersections, rejoin first and last segments
|
|---|
| 1073 | }
|
|---|
| 1074 | };
|
|---|
| 1075 | }
|
|---|
| 1076 |
|
|---|
| 1077 | function clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1) {
|
|---|
| 1078 | var cosPhi0,
|
|---|
| 1079 | cosPhi1,
|
|---|
| 1080 | sinLambda0Lambda1 = sin(lambda0 - lambda1);
|
|---|
| 1081 | return abs(sinLambda0Lambda1) > epsilon
|
|---|
| 1082 | ? atan((sin(phi0) * (cosPhi1 = cos(phi1)) * sin(lambda1)
|
|---|
| 1083 | - sin(phi1) * (cosPhi0 = cos(phi0)) * sin(lambda0))
|
|---|
| 1084 | / (cosPhi0 * cosPhi1 * sinLambda0Lambda1))
|
|---|
| 1085 | : (phi0 + phi1) / 2;
|
|---|
| 1086 | }
|
|---|
| 1087 |
|
|---|
| 1088 | function clipAntimeridianInterpolate(from, to, direction, stream) {
|
|---|
| 1089 | var phi;
|
|---|
| 1090 | if (from == null) {
|
|---|
| 1091 | phi = direction * halfPi;
|
|---|
| 1092 | stream.point(-pi, phi);
|
|---|
| 1093 | stream.point(0, phi);
|
|---|
| 1094 | stream.point(pi, phi);
|
|---|
| 1095 | stream.point(pi, 0);
|
|---|
| 1096 | stream.point(pi, -phi);
|
|---|
| 1097 | stream.point(0, -phi);
|
|---|
| 1098 | stream.point(-pi, -phi);
|
|---|
| 1099 | stream.point(-pi, 0);
|
|---|
| 1100 | stream.point(-pi, phi);
|
|---|
| 1101 | } else if (abs(from[0] - to[0]) > epsilon) {
|
|---|
| 1102 | var lambda = from[0] < to[0] ? pi : -pi;
|
|---|
| 1103 | phi = direction * lambda / 2;
|
|---|
| 1104 | stream.point(-lambda, phi);
|
|---|
| 1105 | stream.point(0, phi);
|
|---|
| 1106 | stream.point(lambda, phi);
|
|---|
| 1107 | } else {
|
|---|
| 1108 | stream.point(to[0], to[1]);
|
|---|
| 1109 | }
|
|---|
| 1110 | }
|
|---|
| 1111 |
|
|---|
| 1112 | function clipCircle(radius) {
|
|---|
| 1113 | var cr = cos(radius),
|
|---|
| 1114 | delta = 2 * radians,
|
|---|
| 1115 | smallRadius = cr > 0,
|
|---|
| 1116 | notHemisphere = abs(cr) > epsilon; // TODO optimise for this common case
|
|---|
| 1117 |
|
|---|
| 1118 | function interpolate(from, to, direction, stream) {
|
|---|
| 1119 | circleStream(stream, radius, delta, direction, from, to);
|
|---|
| 1120 | }
|
|---|
| 1121 |
|
|---|
| 1122 | function visible(lambda, phi) {
|
|---|
| 1123 | return cos(lambda) * cos(phi) > cr;
|
|---|
| 1124 | }
|
|---|
| 1125 |
|
|---|
| 1126 | // Takes a line and cuts into visible segments. Return values used for polygon
|
|---|
| 1127 | // clipping: 0 - there were intersections or the line was empty; 1 - no
|
|---|
| 1128 | // intersections 2 - there were intersections, and the first and last segments
|
|---|
| 1129 | // should be rejoined.
|
|---|
| 1130 | function clipLine(stream) {
|
|---|
| 1131 | var point0, // previous point
|
|---|
| 1132 | c0, // code for previous point
|
|---|
| 1133 | v0, // visibility of previous point
|
|---|
| 1134 | v00, // visibility of first point
|
|---|
| 1135 | clean; // no intersections
|
|---|
| 1136 | return {
|
|---|
| 1137 | lineStart: function() {
|
|---|
| 1138 | v00 = v0 = false;
|
|---|
| 1139 | clean = 1;
|
|---|
| 1140 | },
|
|---|
| 1141 | point: function(lambda, phi) {
|
|---|
| 1142 | var point1 = [lambda, phi],
|
|---|
| 1143 | point2,
|
|---|
| 1144 | v = visible(lambda, phi),
|
|---|
| 1145 | c = smallRadius
|
|---|
| 1146 | ? v ? 0 : code(lambda, phi)
|
|---|
| 1147 | : v ? code(lambda + (lambda < 0 ? pi : -pi), phi) : 0;
|
|---|
| 1148 | if (!point0 && (v00 = v0 = v)) stream.lineStart();
|
|---|
| 1149 | if (v !== v0) {
|
|---|
| 1150 | point2 = intersect(point0, point1);
|
|---|
| 1151 | if (!point2 || pointEqual(point0, point2) || pointEqual(point1, point2))
|
|---|
| 1152 | point1[2] = 1;
|
|---|
| 1153 | }
|
|---|
| 1154 | if (v !== v0) {
|
|---|
| 1155 | clean = 0;
|
|---|
| 1156 | if (v) {
|
|---|
| 1157 | // outside going in
|
|---|
| 1158 | stream.lineStart();
|
|---|
| 1159 | point2 = intersect(point1, point0);
|
|---|
| 1160 | stream.point(point2[0], point2[1]);
|
|---|
| 1161 | } else {
|
|---|
| 1162 | // inside going out
|
|---|
| 1163 | point2 = intersect(point0, point1);
|
|---|
| 1164 | stream.point(point2[0], point2[1], 2);
|
|---|
| 1165 | stream.lineEnd();
|
|---|
| 1166 | }
|
|---|
| 1167 | point0 = point2;
|
|---|
| 1168 | } else if (notHemisphere && point0 && smallRadius ^ v) {
|
|---|
| 1169 | var t;
|
|---|
| 1170 | // If the codes for two points are different, or are both zero,
|
|---|
| 1171 | // and there this segment intersects with the small circle.
|
|---|
| 1172 | if (!(c & c0) && (t = intersect(point1, point0, true))) {
|
|---|
| 1173 | clean = 0;
|
|---|
| 1174 | if (smallRadius) {
|
|---|
| 1175 | stream.lineStart();
|
|---|
| 1176 | stream.point(t[0][0], t[0][1]);
|
|---|
| 1177 | stream.point(t[1][0], t[1][1]);
|
|---|
| 1178 | stream.lineEnd();
|
|---|
| 1179 | } else {
|
|---|
| 1180 | stream.point(t[1][0], t[1][1]);
|
|---|
| 1181 | stream.lineEnd();
|
|---|
| 1182 | stream.lineStart();
|
|---|
| 1183 | stream.point(t[0][0], t[0][1], 3);
|
|---|
| 1184 | }
|
|---|
| 1185 | }
|
|---|
| 1186 | }
|
|---|
| 1187 | if (v && (!point0 || !pointEqual(point0, point1))) {
|
|---|
| 1188 | stream.point(point1[0], point1[1]);
|
|---|
| 1189 | }
|
|---|
| 1190 | point0 = point1, v0 = v, c0 = c;
|
|---|
| 1191 | },
|
|---|
| 1192 | lineEnd: function() {
|
|---|
| 1193 | if (v0) stream.lineEnd();
|
|---|
| 1194 | point0 = null;
|
|---|
| 1195 | },
|
|---|
| 1196 | // Rejoin first and last segments if there were intersections and the first
|
|---|
| 1197 | // and last points were visible.
|
|---|
| 1198 | clean: function() {
|
|---|
| 1199 | return clean | ((v00 && v0) << 1);
|
|---|
| 1200 | }
|
|---|
| 1201 | };
|
|---|
| 1202 | }
|
|---|
| 1203 |
|
|---|
| 1204 | // Intersects the great circle between a and b with the clip circle.
|
|---|
| 1205 | function intersect(a, b, two) {
|
|---|
| 1206 | var pa = cartesian(a),
|
|---|
| 1207 | pb = cartesian(b);
|
|---|
| 1208 |
|
|---|
| 1209 | // We have two planes, n1.p = d1 and n2.p = d2.
|
|---|
| 1210 | // Find intersection line p(t) = c1 n1 + c2 n2 + t (n1 ⨯ n2).
|
|---|
| 1211 | var n1 = [1, 0, 0], // normal
|
|---|
| 1212 | n2 = cartesianCross(pa, pb),
|
|---|
| 1213 | n2n2 = cartesianDot(n2, n2),
|
|---|
| 1214 | n1n2 = n2[0], // cartesianDot(n1, n2),
|
|---|
| 1215 | determinant = n2n2 - n1n2 * n1n2;
|
|---|
| 1216 |
|
|---|
| 1217 | // Two polar points.
|
|---|
| 1218 | if (!determinant) return !two && a;
|
|---|
| 1219 |
|
|---|
| 1220 | var c1 = cr * n2n2 / determinant,
|
|---|
| 1221 | c2 = -cr * n1n2 / determinant,
|
|---|
| 1222 | n1xn2 = cartesianCross(n1, n2),
|
|---|
| 1223 | A = cartesianScale(n1, c1),
|
|---|
| 1224 | B = cartesianScale(n2, c2);
|
|---|
| 1225 | cartesianAddInPlace(A, B);
|
|---|
| 1226 |
|
|---|
| 1227 | // Solve |p(t)|^2 = 1.
|
|---|
| 1228 | var u = n1xn2,
|
|---|
| 1229 | w = cartesianDot(A, u),
|
|---|
| 1230 | uu = cartesianDot(u, u),
|
|---|
| 1231 | t2 = w * w - uu * (cartesianDot(A, A) - 1);
|
|---|
| 1232 |
|
|---|
| 1233 | if (t2 < 0) return;
|
|---|
| 1234 |
|
|---|
| 1235 | var t = sqrt(t2),
|
|---|
| 1236 | q = cartesianScale(u, (-w - t) / uu);
|
|---|
| 1237 | cartesianAddInPlace(q, A);
|
|---|
| 1238 | q = spherical(q);
|
|---|
| 1239 |
|
|---|
| 1240 | if (!two) return q;
|
|---|
| 1241 |
|
|---|
| 1242 | // Two intersection points.
|
|---|
| 1243 | var lambda0 = a[0],
|
|---|
| 1244 | lambda1 = b[0],
|
|---|
| 1245 | phi0 = a[1],
|
|---|
| 1246 | phi1 = b[1],
|
|---|
| 1247 | z;
|
|---|
| 1248 |
|
|---|
| 1249 | if (lambda1 < lambda0) z = lambda0, lambda0 = lambda1, lambda1 = z;
|
|---|
| 1250 |
|
|---|
| 1251 | var delta = lambda1 - lambda0,
|
|---|
| 1252 | polar = abs(delta - pi) < epsilon,
|
|---|
| 1253 | meridian = polar || delta < epsilon;
|
|---|
| 1254 |
|
|---|
| 1255 | if (!polar && phi1 < phi0) z = phi0, phi0 = phi1, phi1 = z;
|
|---|
| 1256 |
|
|---|
| 1257 | // Check that the first point is between a and b.
|
|---|
| 1258 | if (meridian
|
|---|
| 1259 | ? polar
|
|---|
| 1260 | ? phi0 + phi1 > 0 ^ q[1] < (abs(q[0] - lambda0) < epsilon ? phi0 : phi1)
|
|---|
| 1261 | : phi0 <= q[1] && q[1] <= phi1
|
|---|
| 1262 | : delta > pi ^ (lambda0 <= q[0] && q[0] <= lambda1)) {
|
|---|
| 1263 | var q1 = cartesianScale(u, (-w + t) / uu);
|
|---|
| 1264 | cartesianAddInPlace(q1, A);
|
|---|
| 1265 | return [q, spherical(q1)];
|
|---|
| 1266 | }
|
|---|
| 1267 | }
|
|---|
| 1268 |
|
|---|
| 1269 | // Generates a 4-bit vector representing the location of a point relative to
|
|---|
| 1270 | // the small circle's bounding box.
|
|---|
| 1271 | function code(lambda, phi) {
|
|---|
| 1272 | var r = smallRadius ? radius : pi - radius,
|
|---|
| 1273 | code = 0;
|
|---|
| 1274 | if (lambda < -r) code |= 1; // left
|
|---|
| 1275 | else if (lambda > r) code |= 2; // right
|
|---|
| 1276 | if (phi < -r) code |= 4; // below
|
|---|
| 1277 | else if (phi > r) code |= 8; // above
|
|---|
| 1278 | return code;
|
|---|
| 1279 | }
|
|---|
| 1280 |
|
|---|
| 1281 | return clip(visible, clipLine, interpolate, smallRadius ? [0, -radius] : [-pi, radius - pi]);
|
|---|
| 1282 | }
|
|---|
| 1283 |
|
|---|
| 1284 | function clipLine(a, b, x0, y0, x1, y1) {
|
|---|
| 1285 | var ax = a[0],
|
|---|
| 1286 | ay = a[1],
|
|---|
| 1287 | bx = b[0],
|
|---|
| 1288 | by = b[1],
|
|---|
| 1289 | t0 = 0,
|
|---|
| 1290 | t1 = 1,
|
|---|
| 1291 | dx = bx - ax,
|
|---|
| 1292 | dy = by - ay,
|
|---|
| 1293 | r;
|
|---|
| 1294 |
|
|---|
| 1295 | r = x0 - ax;
|
|---|
| 1296 | if (!dx && r > 0) return;
|
|---|
| 1297 | r /= dx;
|
|---|
| 1298 | if (dx < 0) {
|
|---|
| 1299 | if (r < t0) return;
|
|---|
| 1300 | if (r < t1) t1 = r;
|
|---|
| 1301 | } else if (dx > 0) {
|
|---|
| 1302 | if (r > t1) return;
|
|---|
| 1303 | if (r > t0) t0 = r;
|
|---|
| 1304 | }
|
|---|
| 1305 |
|
|---|
| 1306 | r = x1 - ax;
|
|---|
| 1307 | if (!dx && r < 0) return;
|
|---|
| 1308 | r /= dx;
|
|---|
| 1309 | if (dx < 0) {
|
|---|
| 1310 | if (r > t1) return;
|
|---|
| 1311 | if (r > t0) t0 = r;
|
|---|
| 1312 | } else if (dx > 0) {
|
|---|
| 1313 | if (r < t0) return;
|
|---|
| 1314 | if (r < t1) t1 = r;
|
|---|
| 1315 | }
|
|---|
| 1316 |
|
|---|
| 1317 | r = y0 - ay;
|
|---|
| 1318 | if (!dy && r > 0) return;
|
|---|
| 1319 | r /= dy;
|
|---|
| 1320 | if (dy < 0) {
|
|---|
| 1321 | if (r < t0) return;
|
|---|
| 1322 | if (r < t1) t1 = r;
|
|---|
| 1323 | } else if (dy > 0) {
|
|---|
| 1324 | if (r > t1) return;
|
|---|
| 1325 | if (r > t0) t0 = r;
|
|---|
| 1326 | }
|
|---|
| 1327 |
|
|---|
| 1328 | r = y1 - ay;
|
|---|
| 1329 | if (!dy && r < 0) return;
|
|---|
| 1330 | r /= dy;
|
|---|
| 1331 | if (dy < 0) {
|
|---|
| 1332 | if (r > t1) return;
|
|---|
| 1333 | if (r > t0) t0 = r;
|
|---|
| 1334 | } else if (dy > 0) {
|
|---|
| 1335 | if (r < t0) return;
|
|---|
| 1336 | if (r < t1) t1 = r;
|
|---|
| 1337 | }
|
|---|
| 1338 |
|
|---|
| 1339 | if (t0 > 0) a[0] = ax + t0 * dx, a[1] = ay + t0 * dy;
|
|---|
| 1340 | if (t1 < 1) b[0] = ax + t1 * dx, b[1] = ay + t1 * dy;
|
|---|
| 1341 | return true;
|
|---|
| 1342 | }
|
|---|
| 1343 |
|
|---|
| 1344 | var clipMax = 1e9, clipMin = -clipMax;
|
|---|
| 1345 |
|
|---|
| 1346 | // TODO Use d3-polygon’s polygonContains here for the ring check?
|
|---|
| 1347 | // TODO Eliminate duplicate buffering in clipBuffer and polygon.push?
|
|---|
| 1348 |
|
|---|
| 1349 | function clipRectangle(x0, y0, x1, y1) {
|
|---|
| 1350 |
|
|---|
| 1351 | function visible(x, y) {
|
|---|
| 1352 | return x0 <= x && x <= x1 && y0 <= y && y <= y1;
|
|---|
| 1353 | }
|
|---|
| 1354 |
|
|---|
| 1355 | function interpolate(from, to, direction, stream) {
|
|---|
| 1356 | var a = 0, a1 = 0;
|
|---|
| 1357 | if (from == null
|
|---|
| 1358 | || (a = corner(from, direction)) !== (a1 = corner(to, direction))
|
|---|
| 1359 | || comparePoint(from, to) < 0 ^ direction > 0) {
|
|---|
| 1360 | do stream.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0);
|
|---|
| 1361 | while ((a = (a + direction + 4) % 4) !== a1);
|
|---|
| 1362 | } else {
|
|---|
| 1363 | stream.point(to[0], to[1]);
|
|---|
| 1364 | }
|
|---|
| 1365 | }
|
|---|
| 1366 |
|
|---|
| 1367 | function corner(p, direction) {
|
|---|
| 1368 | return abs(p[0] - x0) < epsilon ? direction > 0 ? 0 : 3
|
|---|
| 1369 | : abs(p[0] - x1) < epsilon ? direction > 0 ? 2 : 1
|
|---|
| 1370 | : abs(p[1] - y0) < epsilon ? direction > 0 ? 1 : 0
|
|---|
| 1371 | : direction > 0 ? 3 : 2; // abs(p[1] - y1) < epsilon
|
|---|
| 1372 | }
|
|---|
| 1373 |
|
|---|
| 1374 | function compareIntersection(a, b) {
|
|---|
| 1375 | return comparePoint(a.x, b.x);
|
|---|
| 1376 | }
|
|---|
| 1377 |
|
|---|
| 1378 | function comparePoint(a, b) {
|
|---|
| 1379 | var ca = corner(a, 1),
|
|---|
| 1380 | cb = corner(b, 1);
|
|---|
| 1381 | return ca !== cb ? ca - cb
|
|---|
| 1382 | : ca === 0 ? b[1] - a[1]
|
|---|
| 1383 | : ca === 1 ? a[0] - b[0]
|
|---|
| 1384 | : ca === 2 ? a[1] - b[1]
|
|---|
| 1385 | : b[0] - a[0];
|
|---|
| 1386 | }
|
|---|
| 1387 |
|
|---|
| 1388 | return function(stream) {
|
|---|
| 1389 | var activeStream = stream,
|
|---|
| 1390 | bufferStream = clipBuffer(),
|
|---|
| 1391 | segments,
|
|---|
| 1392 | polygon,
|
|---|
| 1393 | ring,
|
|---|
| 1394 | x__, y__, v__, // first point
|
|---|
| 1395 | x_, y_, v_, // previous point
|
|---|
| 1396 | first,
|
|---|
| 1397 | clean;
|
|---|
| 1398 |
|
|---|
| 1399 | var clipStream = {
|
|---|
| 1400 | point: point,
|
|---|
| 1401 | lineStart: lineStart,
|
|---|
| 1402 | lineEnd: lineEnd,
|
|---|
| 1403 | polygonStart: polygonStart,
|
|---|
| 1404 | polygonEnd: polygonEnd
|
|---|
| 1405 | };
|
|---|
| 1406 |
|
|---|
| 1407 | function point(x, y) {
|
|---|
| 1408 | if (visible(x, y)) activeStream.point(x, y);
|
|---|
| 1409 | }
|
|---|
| 1410 |
|
|---|
| 1411 | function polygonInside() {
|
|---|
| 1412 | var winding = 0;
|
|---|
| 1413 |
|
|---|
| 1414 | for (var i = 0, n = polygon.length; i < n; ++i) {
|
|---|
| 1415 | for (var ring = polygon[i], j = 1, m = ring.length, point = ring[0], a0, a1, b0 = point[0], b1 = point[1]; j < m; ++j) {
|
|---|
| 1416 | a0 = b0, a1 = b1, point = ring[j], b0 = point[0], b1 = point[1];
|
|---|
| 1417 | if (a1 <= y1) { if (b1 > y1 && (b0 - a0) * (y1 - a1) > (b1 - a1) * (x0 - a0)) ++winding; }
|
|---|
| 1418 | else { if (b1 <= y1 && (b0 - a0) * (y1 - a1) < (b1 - a1) * (x0 - a0)) --winding; }
|
|---|
| 1419 | }
|
|---|
| 1420 | }
|
|---|
| 1421 |
|
|---|
| 1422 | return winding;
|
|---|
| 1423 | }
|
|---|
| 1424 |
|
|---|
| 1425 | // Buffer geometry within a polygon and then clip it en masse.
|
|---|
| 1426 | function polygonStart() {
|
|---|
| 1427 | activeStream = bufferStream, segments = [], polygon = [], clean = true;
|
|---|
| 1428 | }
|
|---|
| 1429 |
|
|---|
| 1430 | function polygonEnd() {
|
|---|
| 1431 | var startInside = polygonInside(),
|
|---|
| 1432 | cleanInside = clean && startInside,
|
|---|
| 1433 | visible = (segments = d3Array.merge(segments)).length;
|
|---|
| 1434 | if (cleanInside || visible) {
|
|---|
| 1435 | stream.polygonStart();
|
|---|
| 1436 | if (cleanInside) {
|
|---|
| 1437 | stream.lineStart();
|
|---|
| 1438 | interpolate(null, null, 1, stream);
|
|---|
| 1439 | stream.lineEnd();
|
|---|
| 1440 | }
|
|---|
| 1441 | if (visible) {
|
|---|
| 1442 | clipRejoin(segments, compareIntersection, startInside, interpolate, stream);
|
|---|
| 1443 | }
|
|---|
| 1444 | stream.polygonEnd();
|
|---|
| 1445 | }
|
|---|
| 1446 | activeStream = stream, segments = polygon = ring = null;
|
|---|
| 1447 | }
|
|---|
| 1448 |
|
|---|
| 1449 | function lineStart() {
|
|---|
| 1450 | clipStream.point = linePoint;
|
|---|
| 1451 | if (polygon) polygon.push(ring = []);
|
|---|
| 1452 | first = true;
|
|---|
| 1453 | v_ = false;
|
|---|
| 1454 | x_ = y_ = NaN;
|
|---|
| 1455 | }
|
|---|
| 1456 |
|
|---|
| 1457 | // TODO rather than special-case polygons, simply handle them separately.
|
|---|
| 1458 | // Ideally, coincident intersection points should be jittered to avoid
|
|---|
| 1459 | // clipping issues.
|
|---|
| 1460 | function lineEnd() {
|
|---|
| 1461 | if (segments) {
|
|---|
| 1462 | linePoint(x__, y__);
|
|---|
| 1463 | if (v__ && v_) bufferStream.rejoin();
|
|---|
| 1464 | segments.push(bufferStream.result());
|
|---|
| 1465 | }
|
|---|
| 1466 | clipStream.point = point;
|
|---|
| 1467 | if (v_) activeStream.lineEnd();
|
|---|
| 1468 | }
|
|---|
| 1469 |
|
|---|
| 1470 | function linePoint(x, y) {
|
|---|
| 1471 | var v = visible(x, y);
|
|---|
| 1472 | if (polygon) ring.push([x, y]);
|
|---|
| 1473 | if (first) {
|
|---|
| 1474 | x__ = x, y__ = y, v__ = v;
|
|---|
| 1475 | first = false;
|
|---|
| 1476 | if (v) {
|
|---|
| 1477 | activeStream.lineStart();
|
|---|
| 1478 | activeStream.point(x, y);
|
|---|
| 1479 | }
|
|---|
| 1480 | } else {
|
|---|
| 1481 | if (v && v_) activeStream.point(x, y);
|
|---|
| 1482 | else {
|
|---|
| 1483 | var a = [x_ = Math.max(clipMin, Math.min(clipMax, x_)), y_ = Math.max(clipMin, Math.min(clipMax, y_))],
|
|---|
| 1484 | b = [x = Math.max(clipMin, Math.min(clipMax, x)), y = Math.max(clipMin, Math.min(clipMax, y))];
|
|---|
| 1485 | if (clipLine(a, b, x0, y0, x1, y1)) {
|
|---|
| 1486 | if (!v_) {
|
|---|
| 1487 | activeStream.lineStart();
|
|---|
| 1488 | activeStream.point(a[0], a[1]);
|
|---|
| 1489 | }
|
|---|
| 1490 | activeStream.point(b[0], b[1]);
|
|---|
| 1491 | if (!v) activeStream.lineEnd();
|
|---|
| 1492 | clean = false;
|
|---|
| 1493 | } else if (v) {
|
|---|
| 1494 | activeStream.lineStart();
|
|---|
| 1495 | activeStream.point(x, y);
|
|---|
| 1496 | clean = false;
|
|---|
| 1497 | }
|
|---|
| 1498 | }
|
|---|
| 1499 | }
|
|---|
| 1500 | x_ = x, y_ = y, v_ = v;
|
|---|
| 1501 | }
|
|---|
| 1502 |
|
|---|
| 1503 | return clipStream;
|
|---|
| 1504 | };
|
|---|
| 1505 | }
|
|---|
| 1506 |
|
|---|
| 1507 | function extent() {
|
|---|
| 1508 | var x0 = 0,
|
|---|
| 1509 | y0 = 0,
|
|---|
| 1510 | x1 = 960,
|
|---|
| 1511 | y1 = 500,
|
|---|
| 1512 | cache,
|
|---|
| 1513 | cacheStream,
|
|---|
| 1514 | clip;
|
|---|
| 1515 |
|
|---|
| 1516 | return clip = {
|
|---|
| 1517 | stream: function(stream) {
|
|---|
| 1518 | return cache && cacheStream === stream ? cache : cache = clipRectangle(x0, y0, x1, y1)(cacheStream = stream);
|
|---|
| 1519 | },
|
|---|
| 1520 | extent: function(_) {
|
|---|
| 1521 | return arguments.length ? (x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1], cache = cacheStream = null, clip) : [[x0, y0], [x1, y1]];
|
|---|
| 1522 | }
|
|---|
| 1523 | };
|
|---|
| 1524 | }
|
|---|
| 1525 |
|
|---|
| 1526 | var lengthSum$1,
|
|---|
| 1527 | lambda0,
|
|---|
| 1528 | sinPhi0,
|
|---|
| 1529 | cosPhi0;
|
|---|
| 1530 |
|
|---|
| 1531 | var lengthStream$1 = {
|
|---|
| 1532 | sphere: noop,
|
|---|
| 1533 | point: noop,
|
|---|
| 1534 | lineStart: lengthLineStart,
|
|---|
| 1535 | lineEnd: noop,
|
|---|
| 1536 | polygonStart: noop,
|
|---|
| 1537 | polygonEnd: noop
|
|---|
| 1538 | };
|
|---|
| 1539 |
|
|---|
| 1540 | function lengthLineStart() {
|
|---|
| 1541 | lengthStream$1.point = lengthPointFirst$1;
|
|---|
| 1542 | lengthStream$1.lineEnd = lengthLineEnd;
|
|---|
| 1543 | }
|
|---|
| 1544 |
|
|---|
| 1545 | function lengthLineEnd() {
|
|---|
| 1546 | lengthStream$1.point = lengthStream$1.lineEnd = noop;
|
|---|
| 1547 | }
|
|---|
| 1548 |
|
|---|
| 1549 | function lengthPointFirst$1(lambda, phi) {
|
|---|
| 1550 | lambda *= radians, phi *= radians;
|
|---|
| 1551 | lambda0 = lambda, sinPhi0 = sin(phi), cosPhi0 = cos(phi);
|
|---|
| 1552 | lengthStream$1.point = lengthPoint$1;
|
|---|
| 1553 | }
|
|---|
| 1554 |
|
|---|
| 1555 | function lengthPoint$1(lambda, phi) {
|
|---|
| 1556 | lambda *= radians, phi *= radians;
|
|---|
| 1557 | var sinPhi = sin(phi),
|
|---|
| 1558 | cosPhi = cos(phi),
|
|---|
| 1559 | delta = abs(lambda - lambda0),
|
|---|
| 1560 | cosDelta = cos(delta),
|
|---|
| 1561 | sinDelta = sin(delta),
|
|---|
| 1562 | x = cosPhi * sinDelta,
|
|---|
| 1563 | y = cosPhi0 * sinPhi - sinPhi0 * cosPhi * cosDelta,
|
|---|
| 1564 | z = sinPhi0 * sinPhi + cosPhi0 * cosPhi * cosDelta;
|
|---|
| 1565 | lengthSum$1.add(atan2(sqrt(x * x + y * y), z));
|
|---|
| 1566 | lambda0 = lambda, sinPhi0 = sinPhi, cosPhi0 = cosPhi;
|
|---|
| 1567 | }
|
|---|
| 1568 |
|
|---|
| 1569 | function length(object) {
|
|---|
| 1570 | lengthSum$1 = new d3Array.Adder();
|
|---|
| 1571 | geoStream(object, lengthStream$1);
|
|---|
| 1572 | return +lengthSum$1;
|
|---|
| 1573 | }
|
|---|
| 1574 |
|
|---|
| 1575 | var coordinates = [null, null],
|
|---|
| 1576 | object = {type: "LineString", coordinates: coordinates};
|
|---|
| 1577 |
|
|---|
| 1578 | function distance(a, b) {
|
|---|
| 1579 | coordinates[0] = a;
|
|---|
| 1580 | coordinates[1] = b;
|
|---|
| 1581 | return length(object);
|
|---|
| 1582 | }
|
|---|
| 1583 |
|
|---|
| 1584 | var containsObjectType = {
|
|---|
| 1585 | Feature: function(object, point) {
|
|---|
| 1586 | return containsGeometry(object.geometry, point);
|
|---|
| 1587 | },
|
|---|
| 1588 | FeatureCollection: function(object, point) {
|
|---|
| 1589 | var features = object.features, i = -1, n = features.length;
|
|---|
| 1590 | while (++i < n) if (containsGeometry(features[i].geometry, point)) return true;
|
|---|
| 1591 | return false;
|
|---|
| 1592 | }
|
|---|
| 1593 | };
|
|---|
| 1594 |
|
|---|
| 1595 | var containsGeometryType = {
|
|---|
| 1596 | Sphere: function() {
|
|---|
| 1597 | return true;
|
|---|
| 1598 | },
|
|---|
| 1599 | Point: function(object, point) {
|
|---|
| 1600 | return containsPoint(object.coordinates, point);
|
|---|
| 1601 | },
|
|---|
| 1602 | MultiPoint: function(object, point) {
|
|---|
| 1603 | var coordinates = object.coordinates, i = -1, n = coordinates.length;
|
|---|
| 1604 | while (++i < n) if (containsPoint(coordinates[i], point)) return true;
|
|---|
| 1605 | return false;
|
|---|
| 1606 | },
|
|---|
| 1607 | LineString: function(object, point) {
|
|---|
| 1608 | return containsLine(object.coordinates, point);
|
|---|
| 1609 | },
|
|---|
| 1610 | MultiLineString: function(object, point) {
|
|---|
| 1611 | var coordinates = object.coordinates, i = -1, n = coordinates.length;
|
|---|
| 1612 | while (++i < n) if (containsLine(coordinates[i], point)) return true;
|
|---|
| 1613 | return false;
|
|---|
| 1614 | },
|
|---|
| 1615 | Polygon: function(object, point) {
|
|---|
| 1616 | return containsPolygon(object.coordinates, point);
|
|---|
| 1617 | },
|
|---|
| 1618 | MultiPolygon: function(object, point) {
|
|---|
| 1619 | var coordinates = object.coordinates, i = -1, n = coordinates.length;
|
|---|
| 1620 | while (++i < n) if (containsPolygon(coordinates[i], point)) return true;
|
|---|
| 1621 | return false;
|
|---|
| 1622 | },
|
|---|
| 1623 | GeometryCollection: function(object, point) {
|
|---|
| 1624 | var geometries = object.geometries, i = -1, n = geometries.length;
|
|---|
| 1625 | while (++i < n) if (containsGeometry(geometries[i], point)) return true;
|
|---|
| 1626 | return false;
|
|---|
| 1627 | }
|
|---|
| 1628 | };
|
|---|
| 1629 |
|
|---|
| 1630 | function containsGeometry(geometry, point) {
|
|---|
| 1631 | return geometry && containsGeometryType.hasOwnProperty(geometry.type)
|
|---|
| 1632 | ? containsGeometryType[geometry.type](geometry, point)
|
|---|
| 1633 | : false;
|
|---|
| 1634 | }
|
|---|
| 1635 |
|
|---|
| 1636 | function containsPoint(coordinates, point) {
|
|---|
| 1637 | return distance(coordinates, point) === 0;
|
|---|
| 1638 | }
|
|---|
| 1639 |
|
|---|
| 1640 | function containsLine(coordinates, point) {
|
|---|
| 1641 | var ao, bo, ab;
|
|---|
| 1642 | for (var i = 0, n = coordinates.length; i < n; i++) {
|
|---|
| 1643 | bo = distance(coordinates[i], point);
|
|---|
| 1644 | if (bo === 0) return true;
|
|---|
| 1645 | if (i > 0) {
|
|---|
| 1646 | ab = distance(coordinates[i], coordinates[i - 1]);
|
|---|
| 1647 | if (
|
|---|
| 1648 | ab > 0 &&
|
|---|
| 1649 | ao <= ab &&
|
|---|
| 1650 | bo <= ab &&
|
|---|
| 1651 | (ao + bo - ab) * (1 - Math.pow((ao - bo) / ab, 2)) < epsilon2 * ab
|
|---|
| 1652 | )
|
|---|
| 1653 | return true;
|
|---|
| 1654 | }
|
|---|
| 1655 | ao = bo;
|
|---|
| 1656 | }
|
|---|
| 1657 | return false;
|
|---|
| 1658 | }
|
|---|
| 1659 |
|
|---|
| 1660 | function containsPolygon(coordinates, point) {
|
|---|
| 1661 | return !!polygonContains(coordinates.map(ringRadians), pointRadians(point));
|
|---|
| 1662 | }
|
|---|
| 1663 |
|
|---|
| 1664 | function ringRadians(ring) {
|
|---|
| 1665 | return ring = ring.map(pointRadians), ring.pop(), ring;
|
|---|
| 1666 | }
|
|---|
| 1667 |
|
|---|
| 1668 | function pointRadians(point) {
|
|---|
| 1669 | return [point[0] * radians, point[1] * radians];
|
|---|
| 1670 | }
|
|---|
| 1671 |
|
|---|
| 1672 | function contains(object, point) {
|
|---|
| 1673 | return (object && containsObjectType.hasOwnProperty(object.type)
|
|---|
| 1674 | ? containsObjectType[object.type]
|
|---|
| 1675 | : containsGeometry)(object, point);
|
|---|
| 1676 | }
|
|---|
| 1677 |
|
|---|
| 1678 | function graticuleX(y0, y1, dy) {
|
|---|
| 1679 | var y = d3Array.range(y0, y1 - epsilon, dy).concat(y1);
|
|---|
| 1680 | return function(x) { return y.map(function(y) { return [x, y]; }); };
|
|---|
| 1681 | }
|
|---|
| 1682 |
|
|---|
| 1683 | function graticuleY(x0, x1, dx) {
|
|---|
| 1684 | var x = d3Array.range(x0, x1 - epsilon, dx).concat(x1);
|
|---|
| 1685 | return function(y) { return x.map(function(x) { return [x, y]; }); };
|
|---|
| 1686 | }
|
|---|
| 1687 |
|
|---|
| 1688 | function graticule() {
|
|---|
| 1689 | var x1, x0, X1, X0,
|
|---|
| 1690 | y1, y0, Y1, Y0,
|
|---|
| 1691 | dx = 10, dy = dx, DX = 90, DY = 360,
|
|---|
| 1692 | x, y, X, Y,
|
|---|
| 1693 | precision = 2.5;
|
|---|
| 1694 |
|
|---|
| 1695 | function graticule() {
|
|---|
| 1696 | return {type: "MultiLineString", coordinates: lines()};
|
|---|
| 1697 | }
|
|---|
| 1698 |
|
|---|
| 1699 | function lines() {
|
|---|
| 1700 | return d3Array.range(ceil(X0 / DX) * DX, X1, DX).map(X)
|
|---|
| 1701 | .concat(d3Array.range(ceil(Y0 / DY) * DY, Y1, DY).map(Y))
|
|---|
| 1702 | .concat(d3Array.range(ceil(x0 / dx) * dx, x1, dx).filter(function(x) { return abs(x % DX) > epsilon; }).map(x))
|
|---|
| 1703 | .concat(d3Array.range(ceil(y0 / dy) * dy, y1, dy).filter(function(y) { return abs(y % DY) > epsilon; }).map(y));
|
|---|
| 1704 | }
|
|---|
| 1705 |
|
|---|
| 1706 | graticule.lines = function() {
|
|---|
| 1707 | return lines().map(function(coordinates) { return {type: "LineString", coordinates: coordinates}; });
|
|---|
| 1708 | };
|
|---|
| 1709 |
|
|---|
| 1710 | graticule.outline = function() {
|
|---|
| 1711 | return {
|
|---|
| 1712 | type: "Polygon",
|
|---|
| 1713 | coordinates: [
|
|---|
| 1714 | X(X0).concat(
|
|---|
| 1715 | Y(Y1).slice(1),
|
|---|
| 1716 | X(X1).reverse().slice(1),
|
|---|
| 1717 | Y(Y0).reverse().slice(1))
|
|---|
| 1718 | ]
|
|---|
| 1719 | };
|
|---|
| 1720 | };
|
|---|
| 1721 |
|
|---|
| 1722 | graticule.extent = function(_) {
|
|---|
| 1723 | if (!arguments.length) return graticule.extentMinor();
|
|---|
| 1724 | return graticule.extentMajor(_).extentMinor(_);
|
|---|
| 1725 | };
|
|---|
| 1726 |
|
|---|
| 1727 | graticule.extentMajor = function(_) {
|
|---|
| 1728 | if (!arguments.length) return [[X0, Y0], [X1, Y1]];
|
|---|
| 1729 | X0 = +_[0][0], X1 = +_[1][0];
|
|---|
| 1730 | Y0 = +_[0][1], Y1 = +_[1][1];
|
|---|
| 1731 | if (X0 > X1) _ = X0, X0 = X1, X1 = _;
|
|---|
| 1732 | if (Y0 > Y1) _ = Y0, Y0 = Y1, Y1 = _;
|
|---|
| 1733 | return graticule.precision(precision);
|
|---|
| 1734 | };
|
|---|
| 1735 |
|
|---|
| 1736 | graticule.extentMinor = function(_) {
|
|---|
| 1737 | if (!arguments.length) return [[x0, y0], [x1, y1]];
|
|---|
| 1738 | x0 = +_[0][0], x1 = +_[1][0];
|
|---|
| 1739 | y0 = +_[0][1], y1 = +_[1][1];
|
|---|
| 1740 | if (x0 > x1) _ = x0, x0 = x1, x1 = _;
|
|---|
| 1741 | if (y0 > y1) _ = y0, y0 = y1, y1 = _;
|
|---|
| 1742 | return graticule.precision(precision);
|
|---|
| 1743 | };
|
|---|
| 1744 |
|
|---|
| 1745 | graticule.step = function(_) {
|
|---|
| 1746 | if (!arguments.length) return graticule.stepMinor();
|
|---|
| 1747 | return graticule.stepMajor(_).stepMinor(_);
|
|---|
| 1748 | };
|
|---|
| 1749 |
|
|---|
| 1750 | graticule.stepMajor = function(_) {
|
|---|
| 1751 | if (!arguments.length) return [DX, DY];
|
|---|
| 1752 | DX = +_[0], DY = +_[1];
|
|---|
| 1753 | return graticule;
|
|---|
| 1754 | };
|
|---|
| 1755 |
|
|---|
| 1756 | graticule.stepMinor = function(_) {
|
|---|
| 1757 | if (!arguments.length) return [dx, dy];
|
|---|
| 1758 | dx = +_[0], dy = +_[1];
|
|---|
| 1759 | return graticule;
|
|---|
| 1760 | };
|
|---|
| 1761 |
|
|---|
| 1762 | graticule.precision = function(_) {
|
|---|
| 1763 | if (!arguments.length) return precision;
|
|---|
| 1764 | precision = +_;
|
|---|
| 1765 | x = graticuleX(y0, y1, 90);
|
|---|
| 1766 | y = graticuleY(x0, x1, precision);
|
|---|
| 1767 | X = graticuleX(Y0, Y1, 90);
|
|---|
| 1768 | Y = graticuleY(X0, X1, precision);
|
|---|
| 1769 | return graticule;
|
|---|
| 1770 | };
|
|---|
| 1771 |
|
|---|
| 1772 | return graticule
|
|---|
| 1773 | .extentMajor([[-180, -90 + epsilon], [180, 90 - epsilon]])
|
|---|
| 1774 | .extentMinor([[-180, -80 - epsilon], [180, 80 + epsilon]]);
|
|---|
| 1775 | }
|
|---|
| 1776 |
|
|---|
| 1777 | function graticule10() {
|
|---|
| 1778 | return graticule()();
|
|---|
| 1779 | }
|
|---|
| 1780 |
|
|---|
| 1781 | function interpolate(a, b) {
|
|---|
| 1782 | var x0 = a[0] * radians,
|
|---|
| 1783 | y0 = a[1] * radians,
|
|---|
| 1784 | x1 = b[0] * radians,
|
|---|
| 1785 | y1 = b[1] * radians,
|
|---|
| 1786 | cy0 = cos(y0),
|
|---|
| 1787 | sy0 = sin(y0),
|
|---|
| 1788 | cy1 = cos(y1),
|
|---|
| 1789 | sy1 = sin(y1),
|
|---|
| 1790 | kx0 = cy0 * cos(x0),
|
|---|
| 1791 | ky0 = cy0 * sin(x0),
|
|---|
| 1792 | kx1 = cy1 * cos(x1),
|
|---|
| 1793 | ky1 = cy1 * sin(x1),
|
|---|
| 1794 | d = 2 * asin(sqrt(haversin(y1 - y0) + cy0 * cy1 * haversin(x1 - x0))),
|
|---|
| 1795 | k = sin(d);
|
|---|
| 1796 |
|
|---|
| 1797 | var interpolate = d ? function(t) {
|
|---|
| 1798 | var B = sin(t *= d) / k,
|
|---|
| 1799 | A = sin(d - t) / k,
|
|---|
| 1800 | x = A * kx0 + B * kx1,
|
|---|
| 1801 | y = A * ky0 + B * ky1,
|
|---|
| 1802 | z = A * sy0 + B * sy1;
|
|---|
| 1803 | return [
|
|---|
| 1804 | atan2(y, x) * degrees,
|
|---|
| 1805 | atan2(z, sqrt(x * x + y * y)) * degrees
|
|---|
| 1806 | ];
|
|---|
| 1807 | } : function() {
|
|---|
| 1808 | return [x0 * degrees, y0 * degrees];
|
|---|
| 1809 | };
|
|---|
| 1810 |
|
|---|
| 1811 | interpolate.distance = d;
|
|---|
| 1812 |
|
|---|
| 1813 | return interpolate;
|
|---|
| 1814 | }
|
|---|
| 1815 |
|
|---|
| 1816 | var identity$1 = x => x;
|
|---|
| 1817 |
|
|---|
| 1818 | var areaSum = new d3Array.Adder(),
|
|---|
| 1819 | areaRingSum = new d3Array.Adder(),
|
|---|
| 1820 | x00$2,
|
|---|
| 1821 | y00$2,
|
|---|
| 1822 | x0$3,
|
|---|
| 1823 | y0$3;
|
|---|
| 1824 |
|
|---|
| 1825 | var areaStream = {
|
|---|
| 1826 | point: noop,
|
|---|
| 1827 | lineStart: noop,
|
|---|
| 1828 | lineEnd: noop,
|
|---|
| 1829 | polygonStart: function() {
|
|---|
| 1830 | areaStream.lineStart = areaRingStart;
|
|---|
| 1831 | areaStream.lineEnd = areaRingEnd;
|
|---|
| 1832 | },
|
|---|
| 1833 | polygonEnd: function() {
|
|---|
| 1834 | areaStream.lineStart = areaStream.lineEnd = areaStream.point = noop;
|
|---|
| 1835 | areaSum.add(abs(areaRingSum));
|
|---|
| 1836 | areaRingSum = new d3Array.Adder();
|
|---|
| 1837 | },
|
|---|
| 1838 | result: function() {
|
|---|
| 1839 | var area = areaSum / 2;
|
|---|
| 1840 | areaSum = new d3Array.Adder();
|
|---|
| 1841 | return area;
|
|---|
| 1842 | }
|
|---|
| 1843 | };
|
|---|
| 1844 |
|
|---|
| 1845 | function areaRingStart() {
|
|---|
| 1846 | areaStream.point = areaPointFirst;
|
|---|
| 1847 | }
|
|---|
| 1848 |
|
|---|
| 1849 | function areaPointFirst(x, y) {
|
|---|
| 1850 | areaStream.point = areaPoint;
|
|---|
| 1851 | x00$2 = x0$3 = x, y00$2 = y0$3 = y;
|
|---|
| 1852 | }
|
|---|
| 1853 |
|
|---|
| 1854 | function areaPoint(x, y) {
|
|---|
| 1855 | areaRingSum.add(y0$3 * x - x0$3 * y);
|
|---|
| 1856 | x0$3 = x, y0$3 = y;
|
|---|
| 1857 | }
|
|---|
| 1858 |
|
|---|
| 1859 | function areaRingEnd() {
|
|---|
| 1860 | areaPoint(x00$2, y00$2);
|
|---|
| 1861 | }
|
|---|
| 1862 |
|
|---|
| 1863 | var x0$2 = Infinity,
|
|---|
| 1864 | y0$2 = x0$2,
|
|---|
| 1865 | x1 = -x0$2,
|
|---|
| 1866 | y1 = x1;
|
|---|
| 1867 |
|
|---|
| 1868 | var boundsStream = {
|
|---|
| 1869 | point: boundsPoint,
|
|---|
| 1870 | lineStart: noop,
|
|---|
| 1871 | lineEnd: noop,
|
|---|
| 1872 | polygonStart: noop,
|
|---|
| 1873 | polygonEnd: noop,
|
|---|
| 1874 | result: function() {
|
|---|
| 1875 | var bounds = [[x0$2, y0$2], [x1, y1]];
|
|---|
| 1876 | x1 = y1 = -(y0$2 = x0$2 = Infinity);
|
|---|
| 1877 | return bounds;
|
|---|
| 1878 | }
|
|---|
| 1879 | };
|
|---|
| 1880 |
|
|---|
| 1881 | function boundsPoint(x, y) {
|
|---|
| 1882 | if (x < x0$2) x0$2 = x;
|
|---|
| 1883 | if (x > x1) x1 = x;
|
|---|
| 1884 | if (y < y0$2) y0$2 = y;
|
|---|
| 1885 | if (y > y1) y1 = y;
|
|---|
| 1886 | }
|
|---|
| 1887 |
|
|---|
| 1888 | // TODO Enforce positive area for exterior, negative area for interior?
|
|---|
| 1889 |
|
|---|
| 1890 | var X0 = 0,
|
|---|
| 1891 | Y0 = 0,
|
|---|
| 1892 | Z0 = 0,
|
|---|
| 1893 | X1 = 0,
|
|---|
| 1894 | Y1 = 0,
|
|---|
| 1895 | Z1 = 0,
|
|---|
| 1896 | X2 = 0,
|
|---|
| 1897 | Y2 = 0,
|
|---|
| 1898 | Z2 = 0,
|
|---|
| 1899 | x00$1,
|
|---|
| 1900 | y00$1,
|
|---|
| 1901 | x0$1,
|
|---|
| 1902 | y0$1;
|
|---|
| 1903 |
|
|---|
| 1904 | var centroidStream = {
|
|---|
| 1905 | point: centroidPoint,
|
|---|
| 1906 | lineStart: centroidLineStart,
|
|---|
| 1907 | lineEnd: centroidLineEnd,
|
|---|
| 1908 | polygonStart: function() {
|
|---|
| 1909 | centroidStream.lineStart = centroidRingStart;
|
|---|
| 1910 | centroidStream.lineEnd = centroidRingEnd;
|
|---|
| 1911 | },
|
|---|
| 1912 | polygonEnd: function() {
|
|---|
| 1913 | centroidStream.point = centroidPoint;
|
|---|
| 1914 | centroidStream.lineStart = centroidLineStart;
|
|---|
| 1915 | centroidStream.lineEnd = centroidLineEnd;
|
|---|
| 1916 | },
|
|---|
| 1917 | result: function() {
|
|---|
| 1918 | var centroid = Z2 ? [X2 / Z2, Y2 / Z2]
|
|---|
| 1919 | : Z1 ? [X1 / Z1, Y1 / Z1]
|
|---|
| 1920 | : Z0 ? [X0 / Z0, Y0 / Z0]
|
|---|
| 1921 | : [NaN, NaN];
|
|---|
| 1922 | X0 = Y0 = Z0 =
|
|---|
| 1923 | X1 = Y1 = Z1 =
|
|---|
| 1924 | X2 = Y2 = Z2 = 0;
|
|---|
| 1925 | return centroid;
|
|---|
| 1926 | }
|
|---|
| 1927 | };
|
|---|
| 1928 |
|
|---|
| 1929 | function centroidPoint(x, y) {
|
|---|
| 1930 | X0 += x;
|
|---|
| 1931 | Y0 += y;
|
|---|
| 1932 | ++Z0;
|
|---|
| 1933 | }
|
|---|
| 1934 |
|
|---|
| 1935 | function centroidLineStart() {
|
|---|
| 1936 | centroidStream.point = centroidPointFirstLine;
|
|---|
| 1937 | }
|
|---|
| 1938 |
|
|---|
| 1939 | function centroidPointFirstLine(x, y) {
|
|---|
| 1940 | centroidStream.point = centroidPointLine;
|
|---|
| 1941 | centroidPoint(x0$1 = x, y0$1 = y);
|
|---|
| 1942 | }
|
|---|
| 1943 |
|
|---|
| 1944 | function centroidPointLine(x, y) {
|
|---|
| 1945 | var dx = x - x0$1, dy = y - y0$1, z = sqrt(dx * dx + dy * dy);
|
|---|
| 1946 | X1 += z * (x0$1 + x) / 2;
|
|---|
| 1947 | Y1 += z * (y0$1 + y) / 2;
|
|---|
| 1948 | Z1 += z;
|
|---|
| 1949 | centroidPoint(x0$1 = x, y0$1 = y);
|
|---|
| 1950 | }
|
|---|
| 1951 |
|
|---|
| 1952 | function centroidLineEnd() {
|
|---|
| 1953 | centroidStream.point = centroidPoint;
|
|---|
| 1954 | }
|
|---|
| 1955 |
|
|---|
| 1956 | function centroidRingStart() {
|
|---|
| 1957 | centroidStream.point = centroidPointFirstRing;
|
|---|
| 1958 | }
|
|---|
| 1959 |
|
|---|
| 1960 | function centroidRingEnd() {
|
|---|
| 1961 | centroidPointRing(x00$1, y00$1);
|
|---|
| 1962 | }
|
|---|
| 1963 |
|
|---|
| 1964 | function centroidPointFirstRing(x, y) {
|
|---|
| 1965 | centroidStream.point = centroidPointRing;
|
|---|
| 1966 | centroidPoint(x00$1 = x0$1 = x, y00$1 = y0$1 = y);
|
|---|
| 1967 | }
|
|---|
| 1968 |
|
|---|
| 1969 | function centroidPointRing(x, y) {
|
|---|
| 1970 | var dx = x - x0$1,
|
|---|
| 1971 | dy = y - y0$1,
|
|---|
| 1972 | z = sqrt(dx * dx + dy * dy);
|
|---|
| 1973 |
|
|---|
| 1974 | X1 += z * (x0$1 + x) / 2;
|
|---|
| 1975 | Y1 += z * (y0$1 + y) / 2;
|
|---|
| 1976 | Z1 += z;
|
|---|
| 1977 |
|
|---|
| 1978 | z = y0$1 * x - x0$1 * y;
|
|---|
| 1979 | X2 += z * (x0$1 + x);
|
|---|
| 1980 | Y2 += z * (y0$1 + y);
|
|---|
| 1981 | Z2 += z * 3;
|
|---|
| 1982 | centroidPoint(x0$1 = x, y0$1 = y);
|
|---|
| 1983 | }
|
|---|
| 1984 |
|
|---|
| 1985 | function PathContext(context) {
|
|---|
| 1986 | this._context = context;
|
|---|
| 1987 | }
|
|---|
| 1988 |
|
|---|
| 1989 | PathContext.prototype = {
|
|---|
| 1990 | _radius: 4.5,
|
|---|
| 1991 | pointRadius: function(_) {
|
|---|
| 1992 | return this._radius = _, this;
|
|---|
| 1993 | },
|
|---|
| 1994 | polygonStart: function() {
|
|---|
| 1995 | this._line = 0;
|
|---|
| 1996 | },
|
|---|
| 1997 | polygonEnd: function() {
|
|---|
| 1998 | this._line = NaN;
|
|---|
| 1999 | },
|
|---|
| 2000 | lineStart: function() {
|
|---|
| 2001 | this._point = 0;
|
|---|
| 2002 | },
|
|---|
| 2003 | lineEnd: function() {
|
|---|
| 2004 | if (this._line === 0) this._context.closePath();
|
|---|
| 2005 | this._point = NaN;
|
|---|
| 2006 | },
|
|---|
| 2007 | point: function(x, y) {
|
|---|
| 2008 | switch (this._point) {
|
|---|
| 2009 | case 0: {
|
|---|
| 2010 | this._context.moveTo(x, y);
|
|---|
| 2011 | this._point = 1;
|
|---|
| 2012 | break;
|
|---|
| 2013 | }
|
|---|
| 2014 | case 1: {
|
|---|
| 2015 | this._context.lineTo(x, y);
|
|---|
| 2016 | break;
|
|---|
| 2017 | }
|
|---|
| 2018 | default: {
|
|---|
| 2019 | this._context.moveTo(x + this._radius, y);
|
|---|
| 2020 | this._context.arc(x, y, this._radius, 0, tau);
|
|---|
| 2021 | break;
|
|---|
| 2022 | }
|
|---|
| 2023 | }
|
|---|
| 2024 | },
|
|---|
| 2025 | result: noop
|
|---|
| 2026 | };
|
|---|
| 2027 |
|
|---|
| 2028 | var lengthSum = new d3Array.Adder(),
|
|---|
| 2029 | lengthRing,
|
|---|
| 2030 | x00,
|
|---|
| 2031 | y00,
|
|---|
| 2032 | x0,
|
|---|
| 2033 | y0;
|
|---|
| 2034 |
|
|---|
| 2035 | var lengthStream = {
|
|---|
| 2036 | point: noop,
|
|---|
| 2037 | lineStart: function() {
|
|---|
| 2038 | lengthStream.point = lengthPointFirst;
|
|---|
| 2039 | },
|
|---|
| 2040 | lineEnd: function() {
|
|---|
| 2041 | if (lengthRing) lengthPoint(x00, y00);
|
|---|
| 2042 | lengthStream.point = noop;
|
|---|
| 2043 | },
|
|---|
| 2044 | polygonStart: function() {
|
|---|
| 2045 | lengthRing = true;
|
|---|
| 2046 | },
|
|---|
| 2047 | polygonEnd: function() {
|
|---|
| 2048 | lengthRing = null;
|
|---|
| 2049 | },
|
|---|
| 2050 | result: function() {
|
|---|
| 2051 | var length = +lengthSum;
|
|---|
| 2052 | lengthSum = new d3Array.Adder();
|
|---|
| 2053 | return length;
|
|---|
| 2054 | }
|
|---|
| 2055 | };
|
|---|
| 2056 |
|
|---|
| 2057 | function lengthPointFirst(x, y) {
|
|---|
| 2058 | lengthStream.point = lengthPoint;
|
|---|
| 2059 | x00 = x0 = x, y00 = y0 = y;
|
|---|
| 2060 | }
|
|---|
| 2061 |
|
|---|
| 2062 | function lengthPoint(x, y) {
|
|---|
| 2063 | x0 -= x, y0 -= y;
|
|---|
| 2064 | lengthSum.add(sqrt(x0 * x0 + y0 * y0));
|
|---|
| 2065 | x0 = x, y0 = y;
|
|---|
| 2066 | }
|
|---|
| 2067 |
|
|---|
| 2068 | // Simple caching for constant-radius points.
|
|---|
| 2069 | let cacheDigits, cacheAppend, cacheRadius, cacheCircle;
|
|---|
| 2070 |
|
|---|
| 2071 | class PathString {
|
|---|
| 2072 | constructor(digits) {
|
|---|
| 2073 | this._append = digits == null ? append : appendRound(digits);
|
|---|
| 2074 | this._radius = 4.5;
|
|---|
| 2075 | this._ = "";
|
|---|
| 2076 | }
|
|---|
| 2077 | pointRadius(_) {
|
|---|
| 2078 | this._radius = +_;
|
|---|
| 2079 | return this;
|
|---|
| 2080 | }
|
|---|
| 2081 | polygonStart() {
|
|---|
| 2082 | this._line = 0;
|
|---|
| 2083 | }
|
|---|
| 2084 | polygonEnd() {
|
|---|
| 2085 | this._line = NaN;
|
|---|
| 2086 | }
|
|---|
| 2087 | lineStart() {
|
|---|
| 2088 | this._point = 0;
|
|---|
| 2089 | }
|
|---|
| 2090 | lineEnd() {
|
|---|
| 2091 | if (this._line === 0) this._ += "Z";
|
|---|
| 2092 | this._point = NaN;
|
|---|
| 2093 | }
|
|---|
| 2094 | point(x, y) {
|
|---|
| 2095 | switch (this._point) {
|
|---|
| 2096 | case 0: {
|
|---|
| 2097 | this._append`M${x},${y}`;
|
|---|
| 2098 | this._point = 1;
|
|---|
| 2099 | break;
|
|---|
| 2100 | }
|
|---|
| 2101 | case 1: {
|
|---|
| 2102 | this._append`L${x},${y}`;
|
|---|
| 2103 | break;
|
|---|
| 2104 | }
|
|---|
| 2105 | default: {
|
|---|
| 2106 | this._append`M${x},${y}`;
|
|---|
| 2107 | if (this._radius !== cacheRadius || this._append !== cacheAppend) {
|
|---|
| 2108 | const r = this._radius;
|
|---|
| 2109 | const s = this._;
|
|---|
| 2110 | this._ = ""; // stash the old string so we can cache the circle path fragment
|
|---|
| 2111 | this._append`m0,${r}a${r},${r} 0 1,1 0,${-2 * r}a${r},${r} 0 1,1 0,${2 * r}z`;
|
|---|
| 2112 | cacheRadius = r;
|
|---|
| 2113 | cacheAppend = this._append;
|
|---|
| 2114 | cacheCircle = this._;
|
|---|
| 2115 | this._ = s;
|
|---|
| 2116 | }
|
|---|
| 2117 | this._ += cacheCircle;
|
|---|
| 2118 | break;
|
|---|
| 2119 | }
|
|---|
| 2120 | }
|
|---|
| 2121 | }
|
|---|
| 2122 | result() {
|
|---|
| 2123 | const result = this._;
|
|---|
| 2124 | this._ = "";
|
|---|
| 2125 | return result.length ? result : null;
|
|---|
| 2126 | }
|
|---|
| 2127 | }
|
|---|
| 2128 |
|
|---|
| 2129 | function append(strings) {
|
|---|
| 2130 | let i = 1;
|
|---|
| 2131 | this._ += strings[0];
|
|---|
| 2132 | for (const j = strings.length; i < j; ++i) {
|
|---|
| 2133 | this._ += arguments[i] + strings[i];
|
|---|
| 2134 | }
|
|---|
| 2135 | }
|
|---|
| 2136 |
|
|---|
| 2137 | function appendRound(digits) {
|
|---|
| 2138 | const d = Math.floor(digits);
|
|---|
| 2139 | if (!(d >= 0)) throw new RangeError(`invalid digits: ${digits}`);
|
|---|
| 2140 | if (d > 15) return append;
|
|---|
| 2141 | if (d !== cacheDigits) {
|
|---|
| 2142 | const k = 10 ** d;
|
|---|
| 2143 | cacheDigits = d;
|
|---|
| 2144 | cacheAppend = function append(strings) {
|
|---|
| 2145 | let i = 1;
|
|---|
| 2146 | this._ += strings[0];
|
|---|
| 2147 | for (const j = strings.length; i < j; ++i) {
|
|---|
| 2148 | this._ += Math.round(arguments[i] * k) / k + strings[i];
|
|---|
| 2149 | }
|
|---|
| 2150 | };
|
|---|
| 2151 | }
|
|---|
| 2152 | return cacheAppend;
|
|---|
| 2153 | }
|
|---|
| 2154 |
|
|---|
| 2155 | function index(projection, context) {
|
|---|
| 2156 | let digits = 3,
|
|---|
| 2157 | pointRadius = 4.5,
|
|---|
| 2158 | projectionStream,
|
|---|
| 2159 | contextStream;
|
|---|
| 2160 |
|
|---|
| 2161 | function path(object) {
|
|---|
| 2162 | if (object) {
|
|---|
| 2163 | if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments));
|
|---|
| 2164 | geoStream(object, projectionStream(contextStream));
|
|---|
| 2165 | }
|
|---|
| 2166 | return contextStream.result();
|
|---|
| 2167 | }
|
|---|
| 2168 |
|
|---|
| 2169 | path.area = function(object) {
|
|---|
| 2170 | geoStream(object, projectionStream(areaStream));
|
|---|
| 2171 | return areaStream.result();
|
|---|
| 2172 | };
|
|---|
| 2173 |
|
|---|
| 2174 | path.measure = function(object) {
|
|---|
| 2175 | geoStream(object, projectionStream(lengthStream));
|
|---|
| 2176 | return lengthStream.result();
|
|---|
| 2177 | };
|
|---|
| 2178 |
|
|---|
| 2179 | path.bounds = function(object) {
|
|---|
| 2180 | geoStream(object, projectionStream(boundsStream));
|
|---|
| 2181 | return boundsStream.result();
|
|---|
| 2182 | };
|
|---|
| 2183 |
|
|---|
| 2184 | path.centroid = function(object) {
|
|---|
| 2185 | geoStream(object, projectionStream(centroidStream));
|
|---|
| 2186 | return centroidStream.result();
|
|---|
| 2187 | };
|
|---|
| 2188 |
|
|---|
| 2189 | path.projection = function(_) {
|
|---|
| 2190 | if (!arguments.length) return projection;
|
|---|
| 2191 | projectionStream = _ == null ? (projection = null, identity$1) : (projection = _).stream;
|
|---|
| 2192 | return path;
|
|---|
| 2193 | };
|
|---|
| 2194 |
|
|---|
| 2195 | path.context = function(_) {
|
|---|
| 2196 | if (!arguments.length) return context;
|
|---|
| 2197 | contextStream = _ == null ? (context = null, new PathString(digits)) : new PathContext(context = _);
|
|---|
| 2198 | if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius);
|
|---|
| 2199 | return path;
|
|---|
| 2200 | };
|
|---|
| 2201 |
|
|---|
| 2202 | path.pointRadius = function(_) {
|
|---|
| 2203 | if (!arguments.length) return pointRadius;
|
|---|
| 2204 | pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_);
|
|---|
| 2205 | return path;
|
|---|
| 2206 | };
|
|---|
| 2207 |
|
|---|
| 2208 | path.digits = function(_) {
|
|---|
| 2209 | if (!arguments.length) return digits;
|
|---|
| 2210 | if (_ == null) digits = null;
|
|---|
| 2211 | else {
|
|---|
| 2212 | const d = Math.floor(_);
|
|---|
| 2213 | if (!(d >= 0)) throw new RangeError(`invalid digits: ${_}`);
|
|---|
| 2214 | digits = d;
|
|---|
| 2215 | }
|
|---|
| 2216 | if (context === null) contextStream = new PathString(digits);
|
|---|
| 2217 | return path;
|
|---|
| 2218 | };
|
|---|
| 2219 |
|
|---|
| 2220 | return path.projection(projection).digits(digits).context(context);
|
|---|
| 2221 | }
|
|---|
| 2222 |
|
|---|
| 2223 | function transform(methods) {
|
|---|
| 2224 | return {
|
|---|
| 2225 | stream: transformer(methods)
|
|---|
| 2226 | };
|
|---|
| 2227 | }
|
|---|
| 2228 |
|
|---|
| 2229 | function transformer(methods) {
|
|---|
| 2230 | return function(stream) {
|
|---|
| 2231 | var s = new TransformStream;
|
|---|
| 2232 | for (var key in methods) s[key] = methods[key];
|
|---|
| 2233 | s.stream = stream;
|
|---|
| 2234 | return s;
|
|---|
| 2235 | };
|
|---|
| 2236 | }
|
|---|
| 2237 |
|
|---|
| 2238 | function TransformStream() {}
|
|---|
| 2239 |
|
|---|
| 2240 | TransformStream.prototype = {
|
|---|
| 2241 | constructor: TransformStream,
|
|---|
| 2242 | point: function(x, y) { this.stream.point(x, y); },
|
|---|
| 2243 | sphere: function() { this.stream.sphere(); },
|
|---|
| 2244 | lineStart: function() { this.stream.lineStart(); },
|
|---|
| 2245 | lineEnd: function() { this.stream.lineEnd(); },
|
|---|
| 2246 | polygonStart: function() { this.stream.polygonStart(); },
|
|---|
| 2247 | polygonEnd: function() { this.stream.polygonEnd(); }
|
|---|
| 2248 | };
|
|---|
| 2249 |
|
|---|
| 2250 | function fit(projection, fitBounds, object) {
|
|---|
| 2251 | var clip = projection.clipExtent && projection.clipExtent();
|
|---|
| 2252 | projection.scale(150).translate([0, 0]);
|
|---|
| 2253 | if (clip != null) projection.clipExtent(null);
|
|---|
| 2254 | geoStream(object, projection.stream(boundsStream));
|
|---|
| 2255 | fitBounds(boundsStream.result());
|
|---|
| 2256 | if (clip != null) projection.clipExtent(clip);
|
|---|
| 2257 | return projection;
|
|---|
| 2258 | }
|
|---|
| 2259 |
|
|---|
| 2260 | function fitExtent(projection, extent, object) {
|
|---|
| 2261 | return fit(projection, function(b) {
|
|---|
| 2262 | var w = extent[1][0] - extent[0][0],
|
|---|
| 2263 | h = extent[1][1] - extent[0][1],
|
|---|
| 2264 | k = Math.min(w / (b[1][0] - b[0][0]), h / (b[1][1] - b[0][1])),
|
|---|
| 2265 | x = +extent[0][0] + (w - k * (b[1][0] + b[0][0])) / 2,
|
|---|
| 2266 | y = +extent[0][1] + (h - k * (b[1][1] + b[0][1])) / 2;
|
|---|
| 2267 | projection.scale(150 * k).translate([x, y]);
|
|---|
| 2268 | }, object);
|
|---|
| 2269 | }
|
|---|
| 2270 |
|
|---|
| 2271 | function fitSize(projection, size, object) {
|
|---|
| 2272 | return fitExtent(projection, [[0, 0], size], object);
|
|---|
| 2273 | }
|
|---|
| 2274 |
|
|---|
| 2275 | function fitWidth(projection, width, object) {
|
|---|
| 2276 | return fit(projection, function(b) {
|
|---|
| 2277 | var w = +width,
|
|---|
| 2278 | k = w / (b[1][0] - b[0][0]),
|
|---|
| 2279 | x = (w - k * (b[1][0] + b[0][0])) / 2,
|
|---|
| 2280 | y = -k * b[0][1];
|
|---|
| 2281 | projection.scale(150 * k).translate([x, y]);
|
|---|
| 2282 | }, object);
|
|---|
| 2283 | }
|
|---|
| 2284 |
|
|---|
| 2285 | function fitHeight(projection, height, object) {
|
|---|
| 2286 | return fit(projection, function(b) {
|
|---|
| 2287 | var h = +height,
|
|---|
| 2288 | k = h / (b[1][1] - b[0][1]),
|
|---|
| 2289 | x = -k * b[0][0],
|
|---|
| 2290 | y = (h - k * (b[1][1] + b[0][1])) / 2;
|
|---|
| 2291 | projection.scale(150 * k).translate([x, y]);
|
|---|
| 2292 | }, object);
|
|---|
| 2293 | }
|
|---|
| 2294 |
|
|---|
| 2295 | var maxDepth = 16, // maximum depth of subdivision
|
|---|
| 2296 | cosMinDistance = cos(30 * radians); // cos(minimum angular distance)
|
|---|
| 2297 |
|
|---|
| 2298 | function resample(project, delta2) {
|
|---|
| 2299 | return +delta2 ? resample$1(project, delta2) : resampleNone(project);
|
|---|
| 2300 | }
|
|---|
| 2301 |
|
|---|
| 2302 | function resampleNone(project) {
|
|---|
| 2303 | return transformer({
|
|---|
| 2304 | point: function(x, y) {
|
|---|
| 2305 | x = project(x, y);
|
|---|
| 2306 | this.stream.point(x[0], x[1]);
|
|---|
| 2307 | }
|
|---|
| 2308 | });
|
|---|
| 2309 | }
|
|---|
| 2310 |
|
|---|
| 2311 | function resample$1(project, delta2) {
|
|---|
| 2312 |
|
|---|
| 2313 | function resampleLineTo(x0, y0, lambda0, a0, b0, c0, x1, y1, lambda1, a1, b1, c1, depth, stream) {
|
|---|
| 2314 | var dx = x1 - x0,
|
|---|
| 2315 | dy = y1 - y0,
|
|---|
| 2316 | d2 = dx * dx + dy * dy;
|
|---|
| 2317 | if (d2 > 4 * delta2 && depth--) {
|
|---|
| 2318 | var a = a0 + a1,
|
|---|
| 2319 | b = b0 + b1,
|
|---|
| 2320 | c = c0 + c1,
|
|---|
| 2321 | m = sqrt(a * a + b * b + c * c),
|
|---|
| 2322 | phi2 = asin(c /= m),
|
|---|
| 2323 | lambda2 = abs(abs(c) - 1) < epsilon || abs(lambda0 - lambda1) < epsilon ? (lambda0 + lambda1) / 2 : atan2(b, a),
|
|---|
| 2324 | p = project(lambda2, phi2),
|
|---|
| 2325 | x2 = p[0],
|
|---|
| 2326 | y2 = p[1],
|
|---|
| 2327 | dx2 = x2 - x0,
|
|---|
| 2328 | dy2 = y2 - y0,
|
|---|
| 2329 | dz = dy * dx2 - dx * dy2;
|
|---|
| 2330 | if (dz * dz / d2 > delta2 // perpendicular projected distance
|
|---|
| 2331 | || abs((dx * dx2 + dy * dy2) / d2 - 0.5) > 0.3 // midpoint close to an end
|
|---|
| 2332 | || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) { // angular distance
|
|---|
| 2333 | resampleLineTo(x0, y0, lambda0, a0, b0, c0, x2, y2, lambda2, a /= m, b /= m, c, depth, stream);
|
|---|
| 2334 | stream.point(x2, y2);
|
|---|
| 2335 | resampleLineTo(x2, y2, lambda2, a, b, c, x1, y1, lambda1, a1, b1, c1, depth, stream);
|
|---|
| 2336 | }
|
|---|
| 2337 | }
|
|---|
| 2338 | }
|
|---|
| 2339 | return function(stream) {
|
|---|
| 2340 | var lambda00, x00, y00, a00, b00, c00, // first point
|
|---|
| 2341 | lambda0, x0, y0, a0, b0, c0; // previous point
|
|---|
| 2342 |
|
|---|
| 2343 | var resampleStream = {
|
|---|
| 2344 | point: point,
|
|---|
| 2345 | lineStart: lineStart,
|
|---|
| 2346 | lineEnd: lineEnd,
|
|---|
| 2347 | polygonStart: function() { stream.polygonStart(); resampleStream.lineStart = ringStart; },
|
|---|
| 2348 | polygonEnd: function() { stream.polygonEnd(); resampleStream.lineStart = lineStart; }
|
|---|
| 2349 | };
|
|---|
| 2350 |
|
|---|
| 2351 | function point(x, y) {
|
|---|
| 2352 | x = project(x, y);
|
|---|
| 2353 | stream.point(x[0], x[1]);
|
|---|
| 2354 | }
|
|---|
| 2355 |
|
|---|
| 2356 | function lineStart() {
|
|---|
| 2357 | x0 = NaN;
|
|---|
| 2358 | resampleStream.point = linePoint;
|
|---|
| 2359 | stream.lineStart();
|
|---|
| 2360 | }
|
|---|
| 2361 |
|
|---|
| 2362 | function linePoint(lambda, phi) {
|
|---|
| 2363 | var c = cartesian([lambda, phi]), p = project(lambda, phi);
|
|---|
| 2364 | resampleLineTo(x0, y0, lambda0, a0, b0, c0, x0 = p[0], y0 = p[1], lambda0 = lambda, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream);
|
|---|
| 2365 | stream.point(x0, y0);
|
|---|
| 2366 | }
|
|---|
| 2367 |
|
|---|
| 2368 | function lineEnd() {
|
|---|
| 2369 | resampleStream.point = point;
|
|---|
| 2370 | stream.lineEnd();
|
|---|
| 2371 | }
|
|---|
| 2372 |
|
|---|
| 2373 | function ringStart() {
|
|---|
| 2374 | lineStart();
|
|---|
| 2375 | resampleStream.point = ringPoint;
|
|---|
| 2376 | resampleStream.lineEnd = ringEnd;
|
|---|
| 2377 | }
|
|---|
| 2378 |
|
|---|
| 2379 | function ringPoint(lambda, phi) {
|
|---|
| 2380 | linePoint(lambda00 = lambda, phi), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0;
|
|---|
| 2381 | resampleStream.point = linePoint;
|
|---|
| 2382 | }
|
|---|
| 2383 |
|
|---|
| 2384 | function ringEnd() {
|
|---|
| 2385 | resampleLineTo(x0, y0, lambda0, a0, b0, c0, x00, y00, lambda00, a00, b00, c00, maxDepth, stream);
|
|---|
| 2386 | resampleStream.lineEnd = lineEnd;
|
|---|
| 2387 | lineEnd();
|
|---|
| 2388 | }
|
|---|
| 2389 |
|
|---|
| 2390 | return resampleStream;
|
|---|
| 2391 | };
|
|---|
| 2392 | }
|
|---|
| 2393 |
|
|---|
| 2394 | var transformRadians = transformer({
|
|---|
| 2395 | point: function(x, y) {
|
|---|
| 2396 | this.stream.point(x * radians, y * radians);
|
|---|
| 2397 | }
|
|---|
| 2398 | });
|
|---|
| 2399 |
|
|---|
| 2400 | function transformRotate(rotate) {
|
|---|
| 2401 | return transformer({
|
|---|
| 2402 | point: function(x, y) {
|
|---|
| 2403 | var r = rotate(x, y);
|
|---|
| 2404 | return this.stream.point(r[0], r[1]);
|
|---|
| 2405 | }
|
|---|
| 2406 | });
|
|---|
| 2407 | }
|
|---|
| 2408 |
|
|---|
| 2409 | function scaleTranslate(k, dx, dy, sx, sy) {
|
|---|
| 2410 | function transform(x, y) {
|
|---|
| 2411 | x *= sx; y *= sy;
|
|---|
| 2412 | return [dx + k * x, dy - k * y];
|
|---|
| 2413 | }
|
|---|
| 2414 | transform.invert = function(x, y) {
|
|---|
| 2415 | return [(x - dx) / k * sx, (dy - y) / k * sy];
|
|---|
| 2416 | };
|
|---|
| 2417 | return transform;
|
|---|
| 2418 | }
|
|---|
| 2419 |
|
|---|
| 2420 | function scaleTranslateRotate(k, dx, dy, sx, sy, alpha) {
|
|---|
| 2421 | if (!alpha) return scaleTranslate(k, dx, dy, sx, sy);
|
|---|
| 2422 | var cosAlpha = cos(alpha),
|
|---|
| 2423 | sinAlpha = sin(alpha),
|
|---|
| 2424 | a = cosAlpha * k,
|
|---|
| 2425 | b = sinAlpha * k,
|
|---|
| 2426 | ai = cosAlpha / k,
|
|---|
| 2427 | bi = sinAlpha / k,
|
|---|
| 2428 | ci = (sinAlpha * dy - cosAlpha * dx) / k,
|
|---|
| 2429 | fi = (sinAlpha * dx + cosAlpha * dy) / k;
|
|---|
| 2430 | function transform(x, y) {
|
|---|
| 2431 | x *= sx; y *= sy;
|
|---|
| 2432 | return [a * x - b * y + dx, dy - b * x - a * y];
|
|---|
| 2433 | }
|
|---|
| 2434 | transform.invert = function(x, y) {
|
|---|
| 2435 | return [sx * (ai * x - bi * y + ci), sy * (fi - bi * x - ai * y)];
|
|---|
| 2436 | };
|
|---|
| 2437 | return transform;
|
|---|
| 2438 | }
|
|---|
| 2439 |
|
|---|
| 2440 | function projection(project) {
|
|---|
| 2441 | return projectionMutator(function() { return project; })();
|
|---|
| 2442 | }
|
|---|
| 2443 |
|
|---|
| 2444 | function projectionMutator(projectAt) {
|
|---|
| 2445 | var project,
|
|---|
| 2446 | k = 150, // scale
|
|---|
| 2447 | x = 480, y = 250, // translate
|
|---|
| 2448 | lambda = 0, phi = 0, // center
|
|---|
| 2449 | deltaLambda = 0, deltaPhi = 0, deltaGamma = 0, rotate, // pre-rotate
|
|---|
| 2450 | alpha = 0, // post-rotate angle
|
|---|
| 2451 | sx = 1, // reflectX
|
|---|
| 2452 | sy = 1, // reflectX
|
|---|
| 2453 | theta = null, preclip = clipAntimeridian, // pre-clip angle
|
|---|
| 2454 | x0 = null, y0, x1, y1, postclip = identity$1, // post-clip extent
|
|---|
| 2455 | delta2 = 0.5, // precision
|
|---|
| 2456 | projectResample,
|
|---|
| 2457 | projectTransform,
|
|---|
| 2458 | projectRotateTransform,
|
|---|
| 2459 | cache,
|
|---|
| 2460 | cacheStream;
|
|---|
| 2461 |
|
|---|
| 2462 | function projection(point) {
|
|---|
| 2463 | return projectRotateTransform(point[0] * radians, point[1] * radians);
|
|---|
| 2464 | }
|
|---|
| 2465 |
|
|---|
| 2466 | function invert(point) {
|
|---|
| 2467 | point = projectRotateTransform.invert(point[0], point[1]);
|
|---|
| 2468 | return point && [point[0] * degrees, point[1] * degrees];
|
|---|
| 2469 | }
|
|---|
| 2470 |
|
|---|
| 2471 | projection.stream = function(stream) {
|
|---|
| 2472 | return cache && cacheStream === stream ? cache : cache = transformRadians(transformRotate(rotate)(preclip(projectResample(postclip(cacheStream = stream)))));
|
|---|
| 2473 | };
|
|---|
| 2474 |
|
|---|
| 2475 | projection.preclip = function(_) {
|
|---|
| 2476 | return arguments.length ? (preclip = _, theta = undefined, reset()) : preclip;
|
|---|
| 2477 | };
|
|---|
| 2478 |
|
|---|
| 2479 | projection.postclip = function(_) {
|
|---|
| 2480 | return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip;
|
|---|
| 2481 | };
|
|---|
| 2482 |
|
|---|
| 2483 | projection.clipAngle = function(_) {
|
|---|
| 2484 | return arguments.length ? (preclip = +_ ? clipCircle(theta = _ * radians) : (theta = null, clipAntimeridian), reset()) : theta * degrees;
|
|---|
| 2485 | };
|
|---|
| 2486 |
|
|---|
| 2487 | projection.clipExtent = function(_) {
|
|---|
| 2488 | return arguments.length ? (postclip = _ == null ? (x0 = y0 = x1 = y1 = null, identity$1) : clipRectangle(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reset()) : x0 == null ? null : [[x0, y0], [x1, y1]];
|
|---|
| 2489 | };
|
|---|
| 2490 |
|
|---|
| 2491 | projection.scale = function(_) {
|
|---|
| 2492 | return arguments.length ? (k = +_, recenter()) : k;
|
|---|
| 2493 | };
|
|---|
| 2494 |
|
|---|
| 2495 | projection.translate = function(_) {
|
|---|
| 2496 | return arguments.length ? (x = +_[0], y = +_[1], recenter()) : [x, y];
|
|---|
| 2497 | };
|
|---|
| 2498 |
|
|---|
| 2499 | projection.center = function(_) {
|
|---|
| 2500 | return arguments.length ? (lambda = _[0] % 360 * radians, phi = _[1] % 360 * radians, recenter()) : [lambda * degrees, phi * degrees];
|
|---|
| 2501 | };
|
|---|
| 2502 |
|
|---|
| 2503 | projection.rotate = function(_) {
|
|---|
| 2504 | return arguments.length ? (deltaLambda = _[0] % 360 * radians, deltaPhi = _[1] % 360 * radians, deltaGamma = _.length > 2 ? _[2] % 360 * radians : 0, recenter()) : [deltaLambda * degrees, deltaPhi * degrees, deltaGamma * degrees];
|
|---|
| 2505 | };
|
|---|
| 2506 |
|
|---|
| 2507 | projection.angle = function(_) {
|
|---|
| 2508 | return arguments.length ? (alpha = _ % 360 * radians, recenter()) : alpha * degrees;
|
|---|
| 2509 | };
|
|---|
| 2510 |
|
|---|
| 2511 | projection.reflectX = function(_) {
|
|---|
| 2512 | return arguments.length ? (sx = _ ? -1 : 1, recenter()) : sx < 0;
|
|---|
| 2513 | };
|
|---|
| 2514 |
|
|---|
| 2515 | projection.reflectY = function(_) {
|
|---|
| 2516 | return arguments.length ? (sy = _ ? -1 : 1, recenter()) : sy < 0;
|
|---|
| 2517 | };
|
|---|
| 2518 |
|
|---|
| 2519 | projection.precision = function(_) {
|
|---|
| 2520 | return arguments.length ? (projectResample = resample(projectTransform, delta2 = _ * _), reset()) : sqrt(delta2);
|
|---|
| 2521 | };
|
|---|
| 2522 |
|
|---|
| 2523 | projection.fitExtent = function(extent, object) {
|
|---|
| 2524 | return fitExtent(projection, extent, object);
|
|---|
| 2525 | };
|
|---|
| 2526 |
|
|---|
| 2527 | projection.fitSize = function(size, object) {
|
|---|
| 2528 | return fitSize(projection, size, object);
|
|---|
| 2529 | };
|
|---|
| 2530 |
|
|---|
| 2531 | projection.fitWidth = function(width, object) {
|
|---|
| 2532 | return fitWidth(projection, width, object);
|
|---|
| 2533 | };
|
|---|
| 2534 |
|
|---|
| 2535 | projection.fitHeight = function(height, object) {
|
|---|
| 2536 | return fitHeight(projection, height, object);
|
|---|
| 2537 | };
|
|---|
| 2538 |
|
|---|
| 2539 | function recenter() {
|
|---|
| 2540 | var center = scaleTranslateRotate(k, 0, 0, sx, sy, alpha).apply(null, project(lambda, phi)),
|
|---|
| 2541 | transform = scaleTranslateRotate(k, x - center[0], y - center[1], sx, sy, alpha);
|
|---|
| 2542 | rotate = rotateRadians(deltaLambda, deltaPhi, deltaGamma);
|
|---|
| 2543 | projectTransform = compose(project, transform);
|
|---|
| 2544 | projectRotateTransform = compose(rotate, projectTransform);
|
|---|
| 2545 | projectResample = resample(projectTransform, delta2);
|
|---|
| 2546 | return reset();
|
|---|
| 2547 | }
|
|---|
| 2548 |
|
|---|
| 2549 | function reset() {
|
|---|
| 2550 | cache = cacheStream = null;
|
|---|
| 2551 | return projection;
|
|---|
| 2552 | }
|
|---|
| 2553 |
|
|---|
| 2554 | return function() {
|
|---|
| 2555 | project = projectAt.apply(this, arguments);
|
|---|
| 2556 | projection.invert = project.invert && invert;
|
|---|
| 2557 | return recenter();
|
|---|
| 2558 | };
|
|---|
| 2559 | }
|
|---|
| 2560 |
|
|---|
| 2561 | function conicProjection(projectAt) {
|
|---|
| 2562 | var phi0 = 0,
|
|---|
| 2563 | phi1 = pi / 3,
|
|---|
| 2564 | m = projectionMutator(projectAt),
|
|---|
| 2565 | p = m(phi0, phi1);
|
|---|
| 2566 |
|
|---|
| 2567 | p.parallels = function(_) {
|
|---|
| 2568 | return arguments.length ? m(phi0 = _[0] * radians, phi1 = _[1] * radians) : [phi0 * degrees, phi1 * degrees];
|
|---|
| 2569 | };
|
|---|
| 2570 |
|
|---|
| 2571 | return p;
|
|---|
| 2572 | }
|
|---|
| 2573 |
|
|---|
| 2574 | function cylindricalEqualAreaRaw(phi0) {
|
|---|
| 2575 | var cosPhi0 = cos(phi0);
|
|---|
| 2576 |
|
|---|
| 2577 | function forward(lambda, phi) {
|
|---|
| 2578 | return [lambda * cosPhi0, sin(phi) / cosPhi0];
|
|---|
| 2579 | }
|
|---|
| 2580 |
|
|---|
| 2581 | forward.invert = function(x, y) {
|
|---|
| 2582 | return [x / cosPhi0, asin(y * cosPhi0)];
|
|---|
| 2583 | };
|
|---|
| 2584 |
|
|---|
| 2585 | return forward;
|
|---|
| 2586 | }
|
|---|
| 2587 |
|
|---|
| 2588 | function conicEqualAreaRaw(y0, y1) {
|
|---|
| 2589 | var sy0 = sin(y0), n = (sy0 + sin(y1)) / 2;
|
|---|
| 2590 |
|
|---|
| 2591 | // Are the parallels symmetrical around the Equator?
|
|---|
| 2592 | if (abs(n) < epsilon) return cylindricalEqualAreaRaw(y0);
|
|---|
| 2593 |
|
|---|
| 2594 | var c = 1 + sy0 * (2 * n - sy0), r0 = sqrt(c) / n;
|
|---|
| 2595 |
|
|---|
| 2596 | function project(x, y) {
|
|---|
| 2597 | var r = sqrt(c - 2 * n * sin(y)) / n;
|
|---|
| 2598 | return [r * sin(x *= n), r0 - r * cos(x)];
|
|---|
| 2599 | }
|
|---|
| 2600 |
|
|---|
| 2601 | project.invert = function(x, y) {
|
|---|
| 2602 | var r0y = r0 - y,
|
|---|
| 2603 | l = atan2(x, abs(r0y)) * sign(r0y);
|
|---|
| 2604 | if (r0y * n < 0)
|
|---|
| 2605 | l -= pi * sign(x) * sign(r0y);
|
|---|
| 2606 | return [l / n, asin((c - (x * x + r0y * r0y) * n * n) / (2 * n))];
|
|---|
| 2607 | };
|
|---|
| 2608 |
|
|---|
| 2609 | return project;
|
|---|
| 2610 | }
|
|---|
| 2611 |
|
|---|
| 2612 | function conicEqualArea() {
|
|---|
| 2613 | return conicProjection(conicEqualAreaRaw)
|
|---|
| 2614 | .scale(155.424)
|
|---|
| 2615 | .center([0, 33.6442]);
|
|---|
| 2616 | }
|
|---|
| 2617 |
|
|---|
| 2618 | function albers() {
|
|---|
| 2619 | return conicEqualArea()
|
|---|
| 2620 | .parallels([29.5, 45.5])
|
|---|
| 2621 | .scale(1070)
|
|---|
| 2622 | .translate([480, 250])
|
|---|
| 2623 | .rotate([96, 0])
|
|---|
| 2624 | .center([-0.6, 38.7]);
|
|---|
| 2625 | }
|
|---|
| 2626 |
|
|---|
| 2627 | // The projections must have mutually exclusive clip regions on the sphere,
|
|---|
| 2628 | // as this will avoid emitting interleaving lines and polygons.
|
|---|
| 2629 | function multiplex(streams) {
|
|---|
| 2630 | var n = streams.length;
|
|---|
| 2631 | return {
|
|---|
| 2632 | point: function(x, y) { var i = -1; while (++i < n) streams[i].point(x, y); },
|
|---|
| 2633 | sphere: function() { var i = -1; while (++i < n) streams[i].sphere(); },
|
|---|
| 2634 | lineStart: function() { var i = -1; while (++i < n) streams[i].lineStart(); },
|
|---|
| 2635 | lineEnd: function() { var i = -1; while (++i < n) streams[i].lineEnd(); },
|
|---|
| 2636 | polygonStart: function() { var i = -1; while (++i < n) streams[i].polygonStart(); },
|
|---|
| 2637 | polygonEnd: function() { var i = -1; while (++i < n) streams[i].polygonEnd(); }
|
|---|
| 2638 | };
|
|---|
| 2639 | }
|
|---|
| 2640 |
|
|---|
| 2641 | // A composite projection for the United States, configured by default for
|
|---|
| 2642 | // 960×500. The projection also works quite well at 960×600 if you change the
|
|---|
| 2643 | // scale to 1285 and adjust the translate accordingly. The set of standard
|
|---|
| 2644 | // parallels for each region comes from USGS, which is published here:
|
|---|
| 2645 | // http://egsc.usgs.gov/isb/pubs/MapProjections/projections.html#albers
|
|---|
| 2646 | function albersUsa() {
|
|---|
| 2647 | var cache,
|
|---|
| 2648 | cacheStream,
|
|---|
| 2649 | lower48 = albers(), lower48Point,
|
|---|
| 2650 | alaska = conicEqualArea().rotate([154, 0]).center([-2, 58.5]).parallels([55, 65]), alaskaPoint, // EPSG:3338
|
|---|
| 2651 | hawaii = conicEqualArea().rotate([157, 0]).center([-3, 19.9]).parallels([8, 18]), hawaiiPoint, // ESRI:102007
|
|---|
| 2652 | point, pointStream = {point: function(x, y) { point = [x, y]; }};
|
|---|
| 2653 |
|
|---|
| 2654 | function albersUsa(coordinates) {
|
|---|
| 2655 | var x = coordinates[0], y = coordinates[1];
|
|---|
| 2656 | return point = null,
|
|---|
| 2657 | (lower48Point.point(x, y), point)
|
|---|
| 2658 | || (alaskaPoint.point(x, y), point)
|
|---|
| 2659 | || (hawaiiPoint.point(x, y), point);
|
|---|
| 2660 | }
|
|---|
| 2661 |
|
|---|
| 2662 | albersUsa.invert = function(coordinates) {
|
|---|
| 2663 | var k = lower48.scale(),
|
|---|
| 2664 | t = lower48.translate(),
|
|---|
| 2665 | x = (coordinates[0] - t[0]) / k,
|
|---|
| 2666 | y = (coordinates[1] - t[1]) / k;
|
|---|
| 2667 | return (y >= 0.120 && y < 0.234 && x >= -0.425 && x < -0.214 ? alaska
|
|---|
| 2668 | : y >= 0.166 && y < 0.234 && x >= -0.214 && x < -0.115 ? hawaii
|
|---|
| 2669 | : lower48).invert(coordinates);
|
|---|
| 2670 | };
|
|---|
| 2671 |
|
|---|
| 2672 | albersUsa.stream = function(stream) {
|
|---|
| 2673 | return cache && cacheStream === stream ? cache : cache = multiplex([lower48.stream(cacheStream = stream), alaska.stream(stream), hawaii.stream(stream)]);
|
|---|
| 2674 | };
|
|---|
| 2675 |
|
|---|
| 2676 | albersUsa.precision = function(_) {
|
|---|
| 2677 | if (!arguments.length) return lower48.precision();
|
|---|
| 2678 | lower48.precision(_), alaska.precision(_), hawaii.precision(_);
|
|---|
| 2679 | return reset();
|
|---|
| 2680 | };
|
|---|
| 2681 |
|
|---|
| 2682 | albersUsa.scale = function(_) {
|
|---|
| 2683 | if (!arguments.length) return lower48.scale();
|
|---|
| 2684 | lower48.scale(_), alaska.scale(_ * 0.35), hawaii.scale(_);
|
|---|
| 2685 | return albersUsa.translate(lower48.translate());
|
|---|
| 2686 | };
|
|---|
| 2687 |
|
|---|
| 2688 | albersUsa.translate = function(_) {
|
|---|
| 2689 | if (!arguments.length) return lower48.translate();
|
|---|
| 2690 | var k = lower48.scale(), x = +_[0], y = +_[1];
|
|---|
| 2691 |
|
|---|
| 2692 | lower48Point = lower48
|
|---|
| 2693 | .translate(_)
|
|---|
| 2694 | .clipExtent([[x - 0.455 * k, y - 0.238 * k], [x + 0.455 * k, y + 0.238 * k]])
|
|---|
| 2695 | .stream(pointStream);
|
|---|
| 2696 |
|
|---|
| 2697 | alaskaPoint = alaska
|
|---|
| 2698 | .translate([x - 0.307 * k, y + 0.201 * k])
|
|---|
| 2699 | .clipExtent([[x - 0.425 * k + epsilon, y + 0.120 * k + epsilon], [x - 0.214 * k - epsilon, y + 0.234 * k - epsilon]])
|
|---|
| 2700 | .stream(pointStream);
|
|---|
| 2701 |
|
|---|
| 2702 | hawaiiPoint = hawaii
|
|---|
| 2703 | .translate([x - 0.205 * k, y + 0.212 * k])
|
|---|
| 2704 | .clipExtent([[x - 0.214 * k + epsilon, y + 0.166 * k + epsilon], [x - 0.115 * k - epsilon, y + 0.234 * k - epsilon]])
|
|---|
| 2705 | .stream(pointStream);
|
|---|
| 2706 |
|
|---|
| 2707 | return reset();
|
|---|
| 2708 | };
|
|---|
| 2709 |
|
|---|
| 2710 | albersUsa.fitExtent = function(extent, object) {
|
|---|
| 2711 | return fitExtent(albersUsa, extent, object);
|
|---|
| 2712 | };
|
|---|
| 2713 |
|
|---|
| 2714 | albersUsa.fitSize = function(size, object) {
|
|---|
| 2715 | return fitSize(albersUsa, size, object);
|
|---|
| 2716 | };
|
|---|
| 2717 |
|
|---|
| 2718 | albersUsa.fitWidth = function(width, object) {
|
|---|
| 2719 | return fitWidth(albersUsa, width, object);
|
|---|
| 2720 | };
|
|---|
| 2721 |
|
|---|
| 2722 | albersUsa.fitHeight = function(height, object) {
|
|---|
| 2723 | return fitHeight(albersUsa, height, object);
|
|---|
| 2724 | };
|
|---|
| 2725 |
|
|---|
| 2726 | function reset() {
|
|---|
| 2727 | cache = cacheStream = null;
|
|---|
| 2728 | return albersUsa;
|
|---|
| 2729 | }
|
|---|
| 2730 |
|
|---|
| 2731 | return albersUsa.scale(1070);
|
|---|
| 2732 | }
|
|---|
| 2733 |
|
|---|
| 2734 | function azimuthalRaw(scale) {
|
|---|
| 2735 | return function(x, y) {
|
|---|
| 2736 | var cx = cos(x),
|
|---|
| 2737 | cy = cos(y),
|
|---|
| 2738 | k = scale(cx * cy);
|
|---|
| 2739 | if (k === Infinity) return [2, 0];
|
|---|
| 2740 | return [
|
|---|
| 2741 | k * cy * sin(x),
|
|---|
| 2742 | k * sin(y)
|
|---|
| 2743 | ];
|
|---|
| 2744 | }
|
|---|
| 2745 | }
|
|---|
| 2746 |
|
|---|
| 2747 | function azimuthalInvert(angle) {
|
|---|
| 2748 | return function(x, y) {
|
|---|
| 2749 | var z = sqrt(x * x + y * y),
|
|---|
| 2750 | c = angle(z),
|
|---|
| 2751 | sc = sin(c),
|
|---|
| 2752 | cc = cos(c);
|
|---|
| 2753 | return [
|
|---|
| 2754 | atan2(x * sc, z * cc),
|
|---|
| 2755 | asin(z && y * sc / z)
|
|---|
| 2756 | ];
|
|---|
| 2757 | }
|
|---|
| 2758 | }
|
|---|
| 2759 |
|
|---|
| 2760 | var azimuthalEqualAreaRaw = azimuthalRaw(function(cxcy) {
|
|---|
| 2761 | return sqrt(2 / (1 + cxcy));
|
|---|
| 2762 | });
|
|---|
| 2763 |
|
|---|
| 2764 | azimuthalEqualAreaRaw.invert = azimuthalInvert(function(z) {
|
|---|
| 2765 | return 2 * asin(z / 2);
|
|---|
| 2766 | });
|
|---|
| 2767 |
|
|---|
| 2768 | function azimuthalEqualArea() {
|
|---|
| 2769 | return projection(azimuthalEqualAreaRaw)
|
|---|
| 2770 | .scale(124.75)
|
|---|
| 2771 | .clipAngle(180 - 1e-3);
|
|---|
| 2772 | }
|
|---|
| 2773 |
|
|---|
| 2774 | var azimuthalEquidistantRaw = azimuthalRaw(function(c) {
|
|---|
| 2775 | return (c = acos(c)) && c / sin(c);
|
|---|
| 2776 | });
|
|---|
| 2777 |
|
|---|
| 2778 | azimuthalEquidistantRaw.invert = azimuthalInvert(function(z) {
|
|---|
| 2779 | return z;
|
|---|
| 2780 | });
|
|---|
| 2781 |
|
|---|
| 2782 | function azimuthalEquidistant() {
|
|---|
| 2783 | return projection(azimuthalEquidistantRaw)
|
|---|
| 2784 | .scale(79.4188)
|
|---|
| 2785 | .clipAngle(180 - 1e-3);
|
|---|
| 2786 | }
|
|---|
| 2787 |
|
|---|
| 2788 | function mercatorRaw(lambda, phi) {
|
|---|
| 2789 | return [lambda, log(tan((halfPi + phi) / 2))];
|
|---|
| 2790 | }
|
|---|
| 2791 |
|
|---|
| 2792 | mercatorRaw.invert = function(x, y) {
|
|---|
| 2793 | return [x, 2 * atan(exp(y)) - halfPi];
|
|---|
| 2794 | };
|
|---|
| 2795 |
|
|---|
| 2796 | function mercator() {
|
|---|
| 2797 | return mercatorProjection(mercatorRaw)
|
|---|
| 2798 | .scale(961 / tau);
|
|---|
| 2799 | }
|
|---|
| 2800 |
|
|---|
| 2801 | function mercatorProjection(project) {
|
|---|
| 2802 | var m = projection(project),
|
|---|
| 2803 | center = m.center,
|
|---|
| 2804 | scale = m.scale,
|
|---|
| 2805 | translate = m.translate,
|
|---|
| 2806 | clipExtent = m.clipExtent,
|
|---|
| 2807 | x0 = null, y0, x1, y1; // clip extent
|
|---|
| 2808 |
|
|---|
| 2809 | m.scale = function(_) {
|
|---|
| 2810 | return arguments.length ? (scale(_), reclip()) : scale();
|
|---|
| 2811 | };
|
|---|
| 2812 |
|
|---|
| 2813 | m.translate = function(_) {
|
|---|
| 2814 | return arguments.length ? (translate(_), reclip()) : translate();
|
|---|
| 2815 | };
|
|---|
| 2816 |
|
|---|
| 2817 | m.center = function(_) {
|
|---|
| 2818 | return arguments.length ? (center(_), reclip()) : center();
|
|---|
| 2819 | };
|
|---|
| 2820 |
|
|---|
| 2821 | m.clipExtent = function(_) {
|
|---|
| 2822 | return arguments.length ? ((_ == null ? x0 = y0 = x1 = y1 = null : (x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1])), reclip()) : x0 == null ? null : [[x0, y0], [x1, y1]];
|
|---|
| 2823 | };
|
|---|
| 2824 |
|
|---|
| 2825 | function reclip() {
|
|---|
| 2826 | var k = pi * scale(),
|
|---|
| 2827 | t = m(rotation(m.rotate()).invert([0, 0]));
|
|---|
| 2828 | return clipExtent(x0 == null
|
|---|
| 2829 | ? [[t[0] - k, t[1] - k], [t[0] + k, t[1] + k]] : project === mercatorRaw
|
|---|
| 2830 | ? [[Math.max(t[0] - k, x0), y0], [Math.min(t[0] + k, x1), y1]]
|
|---|
| 2831 | : [[x0, Math.max(t[1] - k, y0)], [x1, Math.min(t[1] + k, y1)]]);
|
|---|
| 2832 | }
|
|---|
| 2833 |
|
|---|
| 2834 | return reclip();
|
|---|
| 2835 | }
|
|---|
| 2836 |
|
|---|
| 2837 | function tany(y) {
|
|---|
| 2838 | return tan((halfPi + y) / 2);
|
|---|
| 2839 | }
|
|---|
| 2840 |
|
|---|
| 2841 | function conicConformalRaw(y0, y1) {
|
|---|
| 2842 | var cy0 = cos(y0),
|
|---|
| 2843 | n = y0 === y1 ? sin(y0) : log(cy0 / cos(y1)) / log(tany(y1) / tany(y0)),
|
|---|
| 2844 | f = cy0 * pow(tany(y0), n) / n;
|
|---|
| 2845 |
|
|---|
| 2846 | if (!n) return mercatorRaw;
|
|---|
| 2847 |
|
|---|
| 2848 | function project(x, y) {
|
|---|
| 2849 | if (f > 0) { if (y < -halfPi + epsilon) y = -halfPi + epsilon; }
|
|---|
| 2850 | else { if (y > halfPi - epsilon) y = halfPi - epsilon; }
|
|---|
| 2851 | var r = f / pow(tany(y), n);
|
|---|
| 2852 | return [r * sin(n * x), f - r * cos(n * x)];
|
|---|
| 2853 | }
|
|---|
| 2854 |
|
|---|
| 2855 | project.invert = function(x, y) {
|
|---|
| 2856 | var fy = f - y, r = sign(n) * sqrt(x * x + fy * fy),
|
|---|
| 2857 | l = atan2(x, abs(fy)) * sign(fy);
|
|---|
| 2858 | if (fy * n < 0)
|
|---|
| 2859 | l -= pi * sign(x) * sign(fy);
|
|---|
| 2860 | return [l / n, 2 * atan(pow(f / r, 1 / n)) - halfPi];
|
|---|
| 2861 | };
|
|---|
| 2862 |
|
|---|
| 2863 | return project;
|
|---|
| 2864 | }
|
|---|
| 2865 |
|
|---|
| 2866 | function conicConformal() {
|
|---|
| 2867 | return conicProjection(conicConformalRaw)
|
|---|
| 2868 | .scale(109.5)
|
|---|
| 2869 | .parallels([30, 30]);
|
|---|
| 2870 | }
|
|---|
| 2871 |
|
|---|
| 2872 | function equirectangularRaw(lambda, phi) {
|
|---|
| 2873 | return [lambda, phi];
|
|---|
| 2874 | }
|
|---|
| 2875 |
|
|---|
| 2876 | equirectangularRaw.invert = equirectangularRaw;
|
|---|
| 2877 |
|
|---|
| 2878 | function equirectangular() {
|
|---|
| 2879 | return projection(equirectangularRaw)
|
|---|
| 2880 | .scale(152.63);
|
|---|
| 2881 | }
|
|---|
| 2882 |
|
|---|
| 2883 | function conicEquidistantRaw(y0, y1) {
|
|---|
| 2884 | var cy0 = cos(y0),
|
|---|
| 2885 | n = y0 === y1 ? sin(y0) : (cy0 - cos(y1)) / (y1 - y0),
|
|---|
| 2886 | g = cy0 / n + y0;
|
|---|
| 2887 |
|
|---|
| 2888 | if (abs(n) < epsilon) return equirectangularRaw;
|
|---|
| 2889 |
|
|---|
| 2890 | function project(x, y) {
|
|---|
| 2891 | var gy = g - y, nx = n * x;
|
|---|
| 2892 | return [gy * sin(nx), g - gy * cos(nx)];
|
|---|
| 2893 | }
|
|---|
| 2894 |
|
|---|
| 2895 | project.invert = function(x, y) {
|
|---|
| 2896 | var gy = g - y,
|
|---|
| 2897 | l = atan2(x, abs(gy)) * sign(gy);
|
|---|
| 2898 | if (gy * n < 0)
|
|---|
| 2899 | l -= pi * sign(x) * sign(gy);
|
|---|
| 2900 | return [l / n, g - sign(n) * sqrt(x * x + gy * gy)];
|
|---|
| 2901 | };
|
|---|
| 2902 |
|
|---|
| 2903 | return project;
|
|---|
| 2904 | }
|
|---|
| 2905 |
|
|---|
| 2906 | function conicEquidistant() {
|
|---|
| 2907 | return conicProjection(conicEquidistantRaw)
|
|---|
| 2908 | .scale(131.154)
|
|---|
| 2909 | .center([0, 13.9389]);
|
|---|
| 2910 | }
|
|---|
| 2911 |
|
|---|
| 2912 | var A1 = 1.340264,
|
|---|
| 2913 | A2 = -0.081106,
|
|---|
| 2914 | A3 = 0.000893,
|
|---|
| 2915 | A4 = 0.003796,
|
|---|
| 2916 | M = sqrt(3) / 2,
|
|---|
| 2917 | iterations = 12;
|
|---|
| 2918 |
|
|---|
| 2919 | function equalEarthRaw(lambda, phi) {
|
|---|
| 2920 | var l = asin(M * sin(phi)), l2 = l * l, l6 = l2 * l2 * l2;
|
|---|
| 2921 | return [
|
|---|
| 2922 | lambda * cos(l) / (M * (A1 + 3 * A2 * l2 + l6 * (7 * A3 + 9 * A4 * l2))),
|
|---|
| 2923 | l * (A1 + A2 * l2 + l6 * (A3 + A4 * l2))
|
|---|
| 2924 | ];
|
|---|
| 2925 | }
|
|---|
| 2926 |
|
|---|
| 2927 | equalEarthRaw.invert = function(x, y) {
|
|---|
| 2928 | var l = y, l2 = l * l, l6 = l2 * l2 * l2;
|
|---|
| 2929 | for (var i = 0, delta, fy, fpy; i < iterations; ++i) {
|
|---|
| 2930 | fy = l * (A1 + A2 * l2 + l6 * (A3 + A4 * l2)) - y;
|
|---|
| 2931 | fpy = A1 + 3 * A2 * l2 + l6 * (7 * A3 + 9 * A4 * l2);
|
|---|
| 2932 | l -= delta = fy / fpy, l2 = l * l, l6 = l2 * l2 * l2;
|
|---|
| 2933 | if (abs(delta) < epsilon2) break;
|
|---|
| 2934 | }
|
|---|
| 2935 | return [
|
|---|
| 2936 | M * x * (A1 + 3 * A2 * l2 + l6 * (7 * A3 + 9 * A4 * l2)) / cos(l),
|
|---|
| 2937 | asin(sin(l) / M)
|
|---|
| 2938 | ];
|
|---|
| 2939 | };
|
|---|
| 2940 |
|
|---|
| 2941 | function equalEarth() {
|
|---|
| 2942 | return projection(equalEarthRaw)
|
|---|
| 2943 | .scale(177.158);
|
|---|
| 2944 | }
|
|---|
| 2945 |
|
|---|
| 2946 | function gnomonicRaw(x, y) {
|
|---|
| 2947 | var cy = cos(y), k = cos(x) * cy;
|
|---|
| 2948 | return [cy * sin(x) / k, sin(y) / k];
|
|---|
| 2949 | }
|
|---|
| 2950 |
|
|---|
| 2951 | gnomonicRaw.invert = azimuthalInvert(atan);
|
|---|
| 2952 |
|
|---|
| 2953 | function gnomonic() {
|
|---|
| 2954 | return projection(gnomonicRaw)
|
|---|
| 2955 | .scale(144.049)
|
|---|
| 2956 | .clipAngle(60);
|
|---|
| 2957 | }
|
|---|
| 2958 |
|
|---|
| 2959 | function identity() {
|
|---|
| 2960 | var k = 1, tx = 0, ty = 0, sx = 1, sy = 1, // scale, translate and reflect
|
|---|
| 2961 | alpha = 0, ca, sa, // angle
|
|---|
| 2962 | x0 = null, y0, x1, y1, // clip extent
|
|---|
| 2963 | kx = 1, ky = 1,
|
|---|
| 2964 | transform = transformer({
|
|---|
| 2965 | point: function(x, y) {
|
|---|
| 2966 | var p = projection([x, y]);
|
|---|
| 2967 | this.stream.point(p[0], p[1]);
|
|---|
| 2968 | }
|
|---|
| 2969 | }),
|
|---|
| 2970 | postclip = identity$1,
|
|---|
| 2971 | cache,
|
|---|
| 2972 | cacheStream;
|
|---|
| 2973 |
|
|---|
| 2974 | function reset() {
|
|---|
| 2975 | kx = k * sx;
|
|---|
| 2976 | ky = k * sy;
|
|---|
| 2977 | cache = cacheStream = null;
|
|---|
| 2978 | return projection;
|
|---|
| 2979 | }
|
|---|
| 2980 |
|
|---|
| 2981 | function projection (p) {
|
|---|
| 2982 | var x = p[0] * kx, y = p[1] * ky;
|
|---|
| 2983 | if (alpha) {
|
|---|
| 2984 | var t = y * ca - x * sa;
|
|---|
| 2985 | x = x * ca + y * sa;
|
|---|
| 2986 | y = t;
|
|---|
| 2987 | }
|
|---|
| 2988 | return [x + tx, y + ty];
|
|---|
| 2989 | }
|
|---|
| 2990 | projection.invert = function(p) {
|
|---|
| 2991 | var x = p[0] - tx, y = p[1] - ty;
|
|---|
| 2992 | if (alpha) {
|
|---|
| 2993 | var t = y * ca + x * sa;
|
|---|
| 2994 | x = x * ca - y * sa;
|
|---|
| 2995 | y = t;
|
|---|
| 2996 | }
|
|---|
| 2997 | return [x / kx, y / ky];
|
|---|
| 2998 | };
|
|---|
| 2999 | projection.stream = function(stream) {
|
|---|
| 3000 | return cache && cacheStream === stream ? cache : cache = transform(postclip(cacheStream = stream));
|
|---|
| 3001 | };
|
|---|
| 3002 | projection.postclip = function(_) {
|
|---|
| 3003 | return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip;
|
|---|
| 3004 | };
|
|---|
| 3005 | projection.clipExtent = function(_) {
|
|---|
| 3006 | return arguments.length ? (postclip = _ == null ? (x0 = y0 = x1 = y1 = null, identity$1) : clipRectangle(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reset()) : x0 == null ? null : [[x0, y0], [x1, y1]];
|
|---|
| 3007 | };
|
|---|
| 3008 | projection.scale = function(_) {
|
|---|
| 3009 | return arguments.length ? (k = +_, reset()) : k;
|
|---|
| 3010 | };
|
|---|
| 3011 | projection.translate = function(_) {
|
|---|
| 3012 | return arguments.length ? (tx = +_[0], ty = +_[1], reset()) : [tx, ty];
|
|---|
| 3013 | };
|
|---|
| 3014 | projection.angle = function(_) {
|
|---|
| 3015 | return arguments.length ? (alpha = _ % 360 * radians, sa = sin(alpha), ca = cos(alpha), reset()) : alpha * degrees;
|
|---|
| 3016 | };
|
|---|
| 3017 | projection.reflectX = function(_) {
|
|---|
| 3018 | return arguments.length ? (sx = _ ? -1 : 1, reset()) : sx < 0;
|
|---|
| 3019 | };
|
|---|
| 3020 | projection.reflectY = function(_) {
|
|---|
| 3021 | return arguments.length ? (sy = _ ? -1 : 1, reset()) : sy < 0;
|
|---|
| 3022 | };
|
|---|
| 3023 | projection.fitExtent = function(extent, object) {
|
|---|
| 3024 | return fitExtent(projection, extent, object);
|
|---|
| 3025 | };
|
|---|
| 3026 | projection.fitSize = function(size, object) {
|
|---|
| 3027 | return fitSize(projection, size, object);
|
|---|
| 3028 | };
|
|---|
| 3029 | projection.fitWidth = function(width, object) {
|
|---|
| 3030 | return fitWidth(projection, width, object);
|
|---|
| 3031 | };
|
|---|
| 3032 | projection.fitHeight = function(height, object) {
|
|---|
| 3033 | return fitHeight(projection, height, object);
|
|---|
| 3034 | };
|
|---|
| 3035 |
|
|---|
| 3036 | return projection;
|
|---|
| 3037 | }
|
|---|
| 3038 |
|
|---|
| 3039 | function naturalEarth1Raw(lambda, phi) {
|
|---|
| 3040 | var phi2 = phi * phi, phi4 = phi2 * phi2;
|
|---|
| 3041 | return [
|
|---|
| 3042 | lambda * (0.8707 - 0.131979 * phi2 + phi4 * (-0.013791 + phi4 * (0.003971 * phi2 - 0.001529 * phi4))),
|
|---|
| 3043 | phi * (1.007226 + phi2 * (0.015085 + phi4 * (-0.044475 + 0.028874 * phi2 - 0.005916 * phi4)))
|
|---|
| 3044 | ];
|
|---|
| 3045 | }
|
|---|
| 3046 |
|
|---|
| 3047 | naturalEarth1Raw.invert = function(x, y) {
|
|---|
| 3048 | var phi = y, i = 25, delta;
|
|---|
| 3049 | do {
|
|---|
| 3050 | var phi2 = phi * phi, phi4 = phi2 * phi2;
|
|---|
| 3051 | phi -= delta = (phi * (1.007226 + phi2 * (0.015085 + phi4 * (-0.044475 + 0.028874 * phi2 - 0.005916 * phi4))) - y) /
|
|---|
| 3052 | (1.007226 + phi2 * (0.015085 * 3 + phi4 * (-0.044475 * 7 + 0.028874 * 9 * phi2 - 0.005916 * 11 * phi4)));
|
|---|
| 3053 | } while (abs(delta) > epsilon && --i > 0);
|
|---|
| 3054 | return [
|
|---|
| 3055 | x / (0.8707 + (phi2 = phi * phi) * (-0.131979 + phi2 * (-0.013791 + phi2 * phi2 * phi2 * (0.003971 - 0.001529 * phi2)))),
|
|---|
| 3056 | phi
|
|---|
| 3057 | ];
|
|---|
| 3058 | };
|
|---|
| 3059 |
|
|---|
| 3060 | function naturalEarth1() {
|
|---|
| 3061 | return projection(naturalEarth1Raw)
|
|---|
| 3062 | .scale(175.295);
|
|---|
| 3063 | }
|
|---|
| 3064 |
|
|---|
| 3065 | function orthographicRaw(x, y) {
|
|---|
| 3066 | return [cos(y) * sin(x), sin(y)];
|
|---|
| 3067 | }
|
|---|
| 3068 |
|
|---|
| 3069 | orthographicRaw.invert = azimuthalInvert(asin);
|
|---|
| 3070 |
|
|---|
| 3071 | function orthographic() {
|
|---|
| 3072 | return projection(orthographicRaw)
|
|---|
| 3073 | .scale(249.5)
|
|---|
| 3074 | .clipAngle(90 + epsilon);
|
|---|
| 3075 | }
|
|---|
| 3076 |
|
|---|
| 3077 | function stereographicRaw(x, y) {
|
|---|
| 3078 | var cy = cos(y), k = 1 + cos(x) * cy;
|
|---|
| 3079 | return [cy * sin(x) / k, sin(y) / k];
|
|---|
| 3080 | }
|
|---|
| 3081 |
|
|---|
| 3082 | stereographicRaw.invert = azimuthalInvert(function(z) {
|
|---|
| 3083 | return 2 * atan(z);
|
|---|
| 3084 | });
|
|---|
| 3085 |
|
|---|
| 3086 | function stereographic() {
|
|---|
| 3087 | return projection(stereographicRaw)
|
|---|
| 3088 | .scale(250)
|
|---|
| 3089 | .clipAngle(142);
|
|---|
| 3090 | }
|
|---|
| 3091 |
|
|---|
| 3092 | function transverseMercatorRaw(lambda, phi) {
|
|---|
| 3093 | return [log(tan((halfPi + phi) / 2)), -lambda];
|
|---|
| 3094 | }
|
|---|
| 3095 |
|
|---|
| 3096 | transverseMercatorRaw.invert = function(x, y) {
|
|---|
| 3097 | return [-y, 2 * atan(exp(x)) - halfPi];
|
|---|
| 3098 | };
|
|---|
| 3099 |
|
|---|
| 3100 | function transverseMercator() {
|
|---|
| 3101 | var m = mercatorProjection(transverseMercatorRaw),
|
|---|
| 3102 | center = m.center,
|
|---|
| 3103 | rotate = m.rotate;
|
|---|
| 3104 |
|
|---|
| 3105 | m.center = function(_) {
|
|---|
| 3106 | return arguments.length ? center([-_[1], _[0]]) : (_ = center(), [_[1], -_[0]]);
|
|---|
| 3107 | };
|
|---|
| 3108 |
|
|---|
| 3109 | m.rotate = function(_) {
|
|---|
| 3110 | return arguments.length ? rotate([_[0], _[1], _.length > 2 ? _[2] + 90 : 90]) : (_ = rotate(), [_[0], _[1], _[2] - 90]);
|
|---|
| 3111 | };
|
|---|
| 3112 |
|
|---|
| 3113 | return rotate([0, 0, 90])
|
|---|
| 3114 | .scale(159.155);
|
|---|
| 3115 | }
|
|---|
| 3116 |
|
|---|
| 3117 | exports.geoAlbers = albers;
|
|---|
| 3118 | exports.geoAlbersUsa = albersUsa;
|
|---|
| 3119 | exports.geoArea = area;
|
|---|
| 3120 | exports.geoAzimuthalEqualArea = azimuthalEqualArea;
|
|---|
| 3121 | exports.geoAzimuthalEqualAreaRaw = azimuthalEqualAreaRaw;
|
|---|
| 3122 | exports.geoAzimuthalEquidistant = azimuthalEquidistant;
|
|---|
| 3123 | exports.geoAzimuthalEquidistantRaw = azimuthalEquidistantRaw;
|
|---|
| 3124 | exports.geoBounds = bounds;
|
|---|
| 3125 | exports.geoCentroid = centroid;
|
|---|
| 3126 | exports.geoCircle = circle;
|
|---|
| 3127 | exports.geoClipAntimeridian = clipAntimeridian;
|
|---|
| 3128 | exports.geoClipCircle = clipCircle;
|
|---|
| 3129 | exports.geoClipExtent = extent;
|
|---|
| 3130 | exports.geoClipRectangle = clipRectangle;
|
|---|
| 3131 | exports.geoConicConformal = conicConformal;
|
|---|
| 3132 | exports.geoConicConformalRaw = conicConformalRaw;
|
|---|
| 3133 | exports.geoConicEqualArea = conicEqualArea;
|
|---|
| 3134 | exports.geoConicEqualAreaRaw = conicEqualAreaRaw;
|
|---|
| 3135 | exports.geoConicEquidistant = conicEquidistant;
|
|---|
| 3136 | exports.geoConicEquidistantRaw = conicEquidistantRaw;
|
|---|
| 3137 | exports.geoContains = contains;
|
|---|
| 3138 | exports.geoDistance = distance;
|
|---|
| 3139 | exports.geoEqualEarth = equalEarth;
|
|---|
| 3140 | exports.geoEqualEarthRaw = equalEarthRaw;
|
|---|
| 3141 | exports.geoEquirectangular = equirectangular;
|
|---|
| 3142 | exports.geoEquirectangularRaw = equirectangularRaw;
|
|---|
| 3143 | exports.geoGnomonic = gnomonic;
|
|---|
| 3144 | exports.geoGnomonicRaw = gnomonicRaw;
|
|---|
| 3145 | exports.geoGraticule = graticule;
|
|---|
| 3146 | exports.geoGraticule10 = graticule10;
|
|---|
| 3147 | exports.geoIdentity = identity;
|
|---|
| 3148 | exports.geoInterpolate = interpolate;
|
|---|
| 3149 | exports.geoLength = length;
|
|---|
| 3150 | exports.geoMercator = mercator;
|
|---|
| 3151 | exports.geoMercatorRaw = mercatorRaw;
|
|---|
| 3152 | exports.geoNaturalEarth1 = naturalEarth1;
|
|---|
| 3153 | exports.geoNaturalEarth1Raw = naturalEarth1Raw;
|
|---|
| 3154 | exports.geoOrthographic = orthographic;
|
|---|
| 3155 | exports.geoOrthographicRaw = orthographicRaw;
|
|---|
| 3156 | exports.geoPath = index;
|
|---|
| 3157 | exports.geoProjection = projection;
|
|---|
| 3158 | exports.geoProjectionMutator = projectionMutator;
|
|---|
| 3159 | exports.geoRotation = rotation;
|
|---|
| 3160 | exports.geoStereographic = stereographic;
|
|---|
| 3161 | exports.geoStereographicRaw = stereographicRaw;
|
|---|
| 3162 | exports.geoStream = geoStream;
|
|---|
| 3163 | exports.geoTransform = transform;
|
|---|
| 3164 | exports.geoTransverseMercator = transverseMercator;
|
|---|
| 3165 | exports.geoTransverseMercatorRaw = transverseMercatorRaw;
|
|---|
| 3166 |
|
|---|
| 3167 | }));
|
|---|