source: node_modules/d3-geo/dist/d3-geo.js@ e4c61dd

Last change on this file since e4c61dd was e4c61dd, checked in by istevanoska <ilinastevanoska@…>, 6 months ago

Prototype 1.1

  • Property mode set to 100644
File size: 87.7 KB
Line 
1// https://d3js.org/d3-geo/ v3.1.1 Copyright 2010-2024 Mike Bostock, 2008-2012 Charles Karney
2(function (global, factory) {
3typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('d3-array')) :
4typeof 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
8var epsilon = 1e-6;
9var epsilon2 = 1e-12;
10var pi = Math.PI;
11var halfPi = pi / 2;
12var quarterPi = pi / 4;
13var tau = pi * 2;
14
15var degrees = 180 / pi;
16var radians = pi / 180;
17
18var abs = Math.abs;
19var atan = Math.atan;
20var atan2 = Math.atan2;
21var cos = Math.cos;
22var ceil = Math.ceil;
23var exp = Math.exp;
24var hypot = Math.hypot;
25var log = Math.log;
26var pow = Math.pow;
27var sin = Math.sin;
28var sign = Math.sign || function(x) { return x > 0 ? 1 : x < 0 ? -1 : 0; };
29var sqrt = Math.sqrt;
30var tan = Math.tan;
31
32function acos(x) {
33 return x > 1 ? 0 : x < -1 ? pi : Math.acos(x);
34}
35
36function asin(x) {
37 return x > 1 ? halfPi : x < -1 ? -halfPi : Math.asin(x);
38}
39
40function haversin(x) {
41 return (x = sin(x / 2)) * x;
42}
43
44function noop() {}
45
46function streamGeometry(geometry, stream) {
47 if (geometry && streamGeometryType.hasOwnProperty(geometry.type)) {
48 streamGeometryType[geometry.type](geometry, stream);
49 }
50}
51
52var 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
62var 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
94function 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
101function 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
108function 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
116var areaRingSum$1 = new d3Array.Adder();
117
118// hello?
119
120var areaSum$1 = new d3Array.Adder(),
121 lambda00$2,
122 phi00$2,
123 lambda0$2,
124 cosPhi0$1,
125 sinPhi0$1;
126
127var 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
146function areaRingStart$1() {
147 areaStream$1.point = areaPointFirst$1;
148}
149
150function areaRingEnd$1() {
151 areaPoint$1(lambda00$2, phi00$2);
152}
153
154function 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
161function 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
182function area(object) {
183 areaSum$1 = new d3Array.Adder();
184 geoStream(object, areaStream$1);
185 return areaSum$1 * 2;
186}
187
188function spherical(cartesian) {
189 return [atan2(cartesian[1], cartesian[0]), asin(cartesian[2])];
190}
191
192function 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
197function cartesianDot(a, b) {
198 return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
199}
200
201function 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
206function cartesianAddInPlace(a, b) {
207 a[0] += b[0], a[1] += b[1], a[2] += b[2];
208}
209
210function cartesianScale(vector, k) {
211 return [vector[0] * k, vector[1] * k, vector[2] * k];
212}
213
214// TODO return d
215function 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
220var 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
228var 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
254function 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
260function 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
309function boundsLineStart() {
310 boundsStream$1.point = linePoint;
311}
312
313function boundsLineEnd() {
314 range[0] = lambda0$1, range[1] = lambda1;
315 boundsStream$1.point = boundsPoint$1;
316 p0 = null;
317}
318
319function 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
330function boundsRingStart() {
331 areaStream$1.lineStart();
332}
333
334function 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°.
345function angle(lambda0, lambda1) {
346 return (lambda1 -= lambda0) < 0 ? lambda1 + 360 : lambda1;
347}
348
349function rangeCompare(a, b) {
350 return a[0] - b[0];
351}
352
353function rangeContains(range, x) {
354 return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x;
355}
356
357function 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
394var 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
401var 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.
417function 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
423function 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
430function centroidLineStart$1() {
431 centroidStream$1.point = centroidLinePointFirst;
432}
433
434function 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
444function 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
458function 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).
464function centroidRingStart$1() {
465 centroidStream$1.point = centroidRingPointFirst;
466}
467
468function centroidRingEnd$1() {
469 centroidRingPoint(lambda00, phi00);
470 centroidStream$1.point = centroidPoint$1;
471}
472
473function 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
484function 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
506function 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
533function constant(x) {
534 return function() {
535 return x;
536 };
537}
538
539function 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
552function rotationIdentity(lambda, phi) {
553 if (abs(lambda) > pi) lambda -= Math.round(lambda / tau) * tau;
554 return [lambda, phi];
555}
556
557rotationIdentity.invert = rotationIdentity;
558
559function 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
566function 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
574function rotationLambda(deltaLambda) {
575 var rotation = forwardRotationLambda(deltaLambda);
576 rotation.invert = forwardRotationLambda(-deltaLambda);
577 return rotation;
578}
579
580function 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
613function 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.
630function 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].
650function 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
657function 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
697function 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
720function pointEqual(a, b) {
721 return abs(a[0] - b[0]) < epsilon && abs(a[1] - b[1]) < epsilon;
722}
723
724function 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.
736function 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
810function 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
825function longitude(point) {
826 return abs(point[0]) <= pi ? point[0] : sign(point[0]) * ((abs(point[0]) + pi) % tau - pi);
827}
828
829function 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
896function 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
1011function 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.
1017function 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
1022var 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.
1032function 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
1077function 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
1088function 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
1112function 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
1284function 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
1344var 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
1349function 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
1507function 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
1526var lengthSum$1,
1527 lambda0,
1528 sinPhi0,
1529 cosPhi0;
1530
1531var lengthStream$1 = {
1532 sphere: noop,
1533 point: noop,
1534 lineStart: lengthLineStart,
1535 lineEnd: noop,
1536 polygonStart: noop,
1537 polygonEnd: noop
1538};
1539
1540function lengthLineStart() {
1541 lengthStream$1.point = lengthPointFirst$1;
1542 lengthStream$1.lineEnd = lengthLineEnd;
1543}
1544
1545function lengthLineEnd() {
1546 lengthStream$1.point = lengthStream$1.lineEnd = noop;
1547}
1548
1549function 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
1555function 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
1569function length(object) {
1570 lengthSum$1 = new d3Array.Adder();
1571 geoStream(object, lengthStream$1);
1572 return +lengthSum$1;
1573}
1574
1575var coordinates = [null, null],
1576 object = {type: "LineString", coordinates: coordinates};
1577
1578function distance(a, b) {
1579 coordinates[0] = a;
1580 coordinates[1] = b;
1581 return length(object);
1582}
1583
1584var 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
1595var 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
1630function containsGeometry(geometry, point) {
1631 return geometry && containsGeometryType.hasOwnProperty(geometry.type)
1632 ? containsGeometryType[geometry.type](geometry, point)
1633 : false;
1634}
1635
1636function containsPoint(coordinates, point) {
1637 return distance(coordinates, point) === 0;
1638}
1639
1640function 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
1660function containsPolygon(coordinates, point) {
1661 return !!polygonContains(coordinates.map(ringRadians), pointRadians(point));
1662}
1663
1664function ringRadians(ring) {
1665 return ring = ring.map(pointRadians), ring.pop(), ring;
1666}
1667
1668function pointRadians(point) {
1669 return [point[0] * radians, point[1] * radians];
1670}
1671
1672function contains(object, point) {
1673 return (object && containsObjectType.hasOwnProperty(object.type)
1674 ? containsObjectType[object.type]
1675 : containsGeometry)(object, point);
1676}
1677
1678function 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
1683function 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
1688function 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
1777function graticule10() {
1778 return graticule()();
1779}
1780
1781function 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
1816var identity$1 = x => x;
1817
1818var areaSum = new d3Array.Adder(),
1819 areaRingSum = new d3Array.Adder(),
1820 x00$2,
1821 y00$2,
1822 x0$3,
1823 y0$3;
1824
1825var 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
1845function areaRingStart() {
1846 areaStream.point = areaPointFirst;
1847}
1848
1849function areaPointFirst(x, y) {
1850 areaStream.point = areaPoint;
1851 x00$2 = x0$3 = x, y00$2 = y0$3 = y;
1852}
1853
1854function areaPoint(x, y) {
1855 areaRingSum.add(y0$3 * x - x0$3 * y);
1856 x0$3 = x, y0$3 = y;
1857}
1858
1859function areaRingEnd() {
1860 areaPoint(x00$2, y00$2);
1861}
1862
1863var x0$2 = Infinity,
1864 y0$2 = x0$2,
1865 x1 = -x0$2,
1866 y1 = x1;
1867
1868var 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
1881function 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
1890var 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
1904var 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
1929function centroidPoint(x, y) {
1930 X0 += x;
1931 Y0 += y;
1932 ++Z0;
1933}
1934
1935function centroidLineStart() {
1936 centroidStream.point = centroidPointFirstLine;
1937}
1938
1939function centroidPointFirstLine(x, y) {
1940 centroidStream.point = centroidPointLine;
1941 centroidPoint(x0$1 = x, y0$1 = y);
1942}
1943
1944function 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
1952function centroidLineEnd() {
1953 centroidStream.point = centroidPoint;
1954}
1955
1956function centroidRingStart() {
1957 centroidStream.point = centroidPointFirstRing;
1958}
1959
1960function centroidRingEnd() {
1961 centroidPointRing(x00$1, y00$1);
1962}
1963
1964function centroidPointFirstRing(x, y) {
1965 centroidStream.point = centroidPointRing;
1966 centroidPoint(x00$1 = x0$1 = x, y00$1 = y0$1 = y);
1967}
1968
1969function 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
1985function PathContext(context) {
1986 this._context = context;
1987}
1988
1989PathContext.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
2028var lengthSum = new d3Array.Adder(),
2029 lengthRing,
2030 x00,
2031 y00,
2032 x0,
2033 y0;
2034
2035var 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
2057function lengthPointFirst(x, y) {
2058 lengthStream.point = lengthPoint;
2059 x00 = x0 = x, y00 = y0 = y;
2060}
2061
2062function 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.
2069let cacheDigits, cacheAppend, cacheRadius, cacheCircle;
2070
2071class 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
2129function 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
2137function 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
2155function 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
2223function transform(methods) {
2224 return {
2225 stream: transformer(methods)
2226 };
2227}
2228
2229function 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
2238function TransformStream() {}
2239
2240TransformStream.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
2250function 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
2260function 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
2271function fitSize(projection, size, object) {
2272 return fitExtent(projection, [[0, 0], size], object);
2273}
2274
2275function 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
2285function 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
2295var maxDepth = 16, // maximum depth of subdivision
2296 cosMinDistance = cos(30 * radians); // cos(minimum angular distance)
2297
2298function resample(project, delta2) {
2299 return +delta2 ? resample$1(project, delta2) : resampleNone(project);
2300}
2301
2302function 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
2311function 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
2394var transformRadians = transformer({
2395 point: function(x, y) {
2396 this.stream.point(x * radians, y * radians);
2397 }
2398});
2399
2400function 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
2409function 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
2420function 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
2440function projection(project) {
2441 return projectionMutator(function() { return project; })();
2442}
2443
2444function 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
2561function 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
2574function 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
2588function 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
2612function conicEqualArea() {
2613 return conicProjection(conicEqualAreaRaw)
2614 .scale(155.424)
2615 .center([0, 33.6442]);
2616}
2617
2618function 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.
2629function 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
2646function 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
2734function 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
2747function 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
2760var azimuthalEqualAreaRaw = azimuthalRaw(function(cxcy) {
2761 return sqrt(2 / (1 + cxcy));
2762});
2763
2764azimuthalEqualAreaRaw.invert = azimuthalInvert(function(z) {
2765 return 2 * asin(z / 2);
2766});
2767
2768function azimuthalEqualArea() {
2769 return projection(azimuthalEqualAreaRaw)
2770 .scale(124.75)
2771 .clipAngle(180 - 1e-3);
2772}
2773
2774var azimuthalEquidistantRaw = azimuthalRaw(function(c) {
2775 return (c = acos(c)) && c / sin(c);
2776});
2777
2778azimuthalEquidistantRaw.invert = azimuthalInvert(function(z) {
2779 return z;
2780});
2781
2782function azimuthalEquidistant() {
2783 return projection(azimuthalEquidistantRaw)
2784 .scale(79.4188)
2785 .clipAngle(180 - 1e-3);
2786}
2787
2788function mercatorRaw(lambda, phi) {
2789 return [lambda, log(tan((halfPi + phi) / 2))];
2790}
2791
2792mercatorRaw.invert = function(x, y) {
2793 return [x, 2 * atan(exp(y)) - halfPi];
2794};
2795
2796function mercator() {
2797 return mercatorProjection(mercatorRaw)
2798 .scale(961 / tau);
2799}
2800
2801function 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
2837function tany(y) {
2838 return tan((halfPi + y) / 2);
2839}
2840
2841function 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
2866function conicConformal() {
2867 return conicProjection(conicConformalRaw)
2868 .scale(109.5)
2869 .parallels([30, 30]);
2870}
2871
2872function equirectangularRaw(lambda, phi) {
2873 return [lambda, phi];
2874}
2875
2876equirectangularRaw.invert = equirectangularRaw;
2877
2878function equirectangular() {
2879 return projection(equirectangularRaw)
2880 .scale(152.63);
2881}
2882
2883function 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
2906function conicEquidistant() {
2907 return conicProjection(conicEquidistantRaw)
2908 .scale(131.154)
2909 .center([0, 13.9389]);
2910}
2911
2912var A1 = 1.340264,
2913 A2 = -0.081106,
2914 A3 = 0.000893,
2915 A4 = 0.003796,
2916 M = sqrt(3) / 2,
2917 iterations = 12;
2918
2919function 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
2927equalEarthRaw.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
2941function equalEarth() {
2942 return projection(equalEarthRaw)
2943 .scale(177.158);
2944}
2945
2946function gnomonicRaw(x, y) {
2947 var cy = cos(y), k = cos(x) * cy;
2948 return [cy * sin(x) / k, sin(y) / k];
2949}
2950
2951gnomonicRaw.invert = azimuthalInvert(atan);
2952
2953function gnomonic() {
2954 return projection(gnomonicRaw)
2955 .scale(144.049)
2956 .clipAngle(60);
2957}
2958
2959function 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
3039function 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
3047naturalEarth1Raw.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
3060function naturalEarth1() {
3061 return projection(naturalEarth1Raw)
3062 .scale(175.295);
3063}
3064
3065function orthographicRaw(x, y) {
3066 return [cos(y) * sin(x), sin(y)];
3067}
3068
3069orthographicRaw.invert = azimuthalInvert(asin);
3070
3071function orthographic() {
3072 return projection(orthographicRaw)
3073 .scale(249.5)
3074 .clipAngle(90 + epsilon);
3075}
3076
3077function stereographicRaw(x, y) {
3078 var cy = cos(y), k = 1 + cos(x) * cy;
3079 return [cy * sin(x) / k, sin(y) / k];
3080}
3081
3082stereographicRaw.invert = azimuthalInvert(function(z) {
3083 return 2 * atan(z);
3084});
3085
3086function stereographic() {
3087 return projection(stereographicRaw)
3088 .scale(250)
3089 .clipAngle(142);
3090}
3091
3092function transverseMercatorRaw(lambda, phi) {
3093 return [log(tan((halfPi + phi) / 2)), -lambda];
3094}
3095
3096transverseMercatorRaw.invert = function(x, y) {
3097 return [-y, 2 * atan(exp(x)) - halfPi];
3098};
3099
3100function 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
3117exports.geoAlbers = albers;
3118exports.geoAlbersUsa = albersUsa;
3119exports.geoArea = area;
3120exports.geoAzimuthalEqualArea = azimuthalEqualArea;
3121exports.geoAzimuthalEqualAreaRaw = azimuthalEqualAreaRaw;
3122exports.geoAzimuthalEquidistant = azimuthalEquidistant;
3123exports.geoAzimuthalEquidistantRaw = azimuthalEquidistantRaw;
3124exports.geoBounds = bounds;
3125exports.geoCentroid = centroid;
3126exports.geoCircle = circle;
3127exports.geoClipAntimeridian = clipAntimeridian;
3128exports.geoClipCircle = clipCircle;
3129exports.geoClipExtent = extent;
3130exports.geoClipRectangle = clipRectangle;
3131exports.geoConicConformal = conicConformal;
3132exports.geoConicConformalRaw = conicConformalRaw;
3133exports.geoConicEqualArea = conicEqualArea;
3134exports.geoConicEqualAreaRaw = conicEqualAreaRaw;
3135exports.geoConicEquidistant = conicEquidistant;
3136exports.geoConicEquidistantRaw = conicEquidistantRaw;
3137exports.geoContains = contains;
3138exports.geoDistance = distance;
3139exports.geoEqualEarth = equalEarth;
3140exports.geoEqualEarthRaw = equalEarthRaw;
3141exports.geoEquirectangular = equirectangular;
3142exports.geoEquirectangularRaw = equirectangularRaw;
3143exports.geoGnomonic = gnomonic;
3144exports.geoGnomonicRaw = gnomonicRaw;
3145exports.geoGraticule = graticule;
3146exports.geoGraticule10 = graticule10;
3147exports.geoIdentity = identity;
3148exports.geoInterpolate = interpolate;
3149exports.geoLength = length;
3150exports.geoMercator = mercator;
3151exports.geoMercatorRaw = mercatorRaw;
3152exports.geoNaturalEarth1 = naturalEarth1;
3153exports.geoNaturalEarth1Raw = naturalEarth1Raw;
3154exports.geoOrthographic = orthographic;
3155exports.geoOrthographicRaw = orthographicRaw;
3156exports.geoPath = index;
3157exports.geoProjection = projection;
3158exports.geoProjectionMutator = projectionMutator;
3159exports.geoRotation = rotation;
3160exports.geoStereographic = stereographic;
3161exports.geoStereographicRaw = stereographicRaw;
3162exports.geoStream = geoStream;
3163exports.geoTransform = transform;
3164exports.geoTransverseMercator = transverseMercator;
3165exports.geoTransverseMercatorRaw = transverseMercatorRaw;
3166
3167}));
Note: See TracBrowser for help on using the repository browser.