source: node_modules/d3-geo/src/bounds.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: 5.4 KB
Line 
1import {Adder} from "d3-array";
2import {areaStream, areaRingSum} from "./area.js";
3import {cartesian, cartesianCross, cartesianNormalizeInPlace, spherical} from "./cartesian.js";
4import {abs, degrees, epsilon, radians} from "./math.js";
5import stream from "./stream.js";
6
7var lambda0, phi0, lambda1, phi1, // bounds
8 lambda2, // previous lambda-coordinate
9 lambda00, phi00, // first point
10 p0, // previous 3D point
11 deltaSum,
12 ranges,
13 range;
14
15var boundsStream = {
16 point: boundsPoint,
17 lineStart: boundsLineStart,
18 lineEnd: boundsLineEnd,
19 polygonStart: function() {
20 boundsStream.point = boundsRingPoint;
21 boundsStream.lineStart = boundsRingStart;
22 boundsStream.lineEnd = boundsRingEnd;
23 deltaSum = new Adder();
24 areaStream.polygonStart();
25 },
26 polygonEnd: function() {
27 areaStream.polygonEnd();
28 boundsStream.point = boundsPoint;
29 boundsStream.lineStart = boundsLineStart;
30 boundsStream.lineEnd = boundsLineEnd;
31 if (areaRingSum < 0) lambda0 = -(lambda1 = 180), phi0 = -(phi1 = 90);
32 else if (deltaSum > epsilon) phi1 = 90;
33 else if (deltaSum < -epsilon) phi0 = -90;
34 range[0] = lambda0, range[1] = lambda1;
35 },
36 sphere: function() {
37 lambda0 = -(lambda1 = 180), phi0 = -(phi1 = 90);
38 }
39};
40
41function boundsPoint(lambda, phi) {
42 ranges.push(range = [lambda0 = lambda, lambda1 = lambda]);
43 if (phi < phi0) phi0 = phi;
44 if (phi > phi1) phi1 = phi;
45}
46
47function linePoint(lambda, phi) {
48 var p = cartesian([lambda * radians, phi * radians]);
49 if (p0) {
50 var normal = cartesianCross(p0, p),
51 equatorial = [normal[1], -normal[0], 0],
52 inflection = cartesianCross(equatorial, normal);
53 cartesianNormalizeInPlace(inflection);
54 inflection = spherical(inflection);
55 var delta = lambda - lambda2,
56 sign = delta > 0 ? 1 : -1,
57 lambdai = inflection[0] * degrees * sign,
58 phii,
59 antimeridian = abs(delta) > 180;
60 if (antimeridian ^ (sign * lambda2 < lambdai && lambdai < sign * lambda)) {
61 phii = inflection[1] * degrees;
62 if (phii > phi1) phi1 = phii;
63 } else if (lambdai = (lambdai + 360) % 360 - 180, antimeridian ^ (sign * lambda2 < lambdai && lambdai < sign * lambda)) {
64 phii = -inflection[1] * degrees;
65 if (phii < phi0) phi0 = phii;
66 } else {
67 if (phi < phi0) phi0 = phi;
68 if (phi > phi1) phi1 = phi;
69 }
70 if (antimeridian) {
71 if (lambda < lambda2) {
72 if (angle(lambda0, lambda) > angle(lambda0, lambda1)) lambda1 = lambda;
73 } else {
74 if (angle(lambda, lambda1) > angle(lambda0, lambda1)) lambda0 = lambda;
75 }
76 } else {
77 if (lambda1 >= lambda0) {
78 if (lambda < lambda0) lambda0 = lambda;
79 if (lambda > lambda1) lambda1 = lambda;
80 } else {
81 if (lambda > lambda2) {
82 if (angle(lambda0, lambda) > angle(lambda0, lambda1)) lambda1 = lambda;
83 } else {
84 if (angle(lambda, lambda1) > angle(lambda0, lambda1)) lambda0 = lambda;
85 }
86 }
87 }
88 } else {
89 ranges.push(range = [lambda0 = lambda, lambda1 = lambda]);
90 }
91 if (phi < phi0) phi0 = phi;
92 if (phi > phi1) phi1 = phi;
93 p0 = p, lambda2 = lambda;
94}
95
96function boundsLineStart() {
97 boundsStream.point = linePoint;
98}
99
100function boundsLineEnd() {
101 range[0] = lambda0, range[1] = lambda1;
102 boundsStream.point = boundsPoint;
103 p0 = null;
104}
105
106function boundsRingPoint(lambda, phi) {
107 if (p0) {
108 var delta = lambda - lambda2;
109 deltaSum.add(abs(delta) > 180 ? delta + (delta > 0 ? 360 : -360) : delta);
110 } else {
111 lambda00 = lambda, phi00 = phi;
112 }
113 areaStream.point(lambda, phi);
114 linePoint(lambda, phi);
115}
116
117function boundsRingStart() {
118 areaStream.lineStart();
119}
120
121function boundsRingEnd() {
122 boundsRingPoint(lambda00, phi00);
123 areaStream.lineEnd();
124 if (abs(deltaSum) > epsilon) lambda0 = -(lambda1 = 180);
125 range[0] = lambda0, range[1] = lambda1;
126 p0 = null;
127}
128
129// Finds the left-right distance between two longitudes.
130// This is almost the same as (lambda1 - lambda0 + 360°) % 360°, except that we want
131// the distance between ±180° to be 360°.
132function angle(lambda0, lambda1) {
133 return (lambda1 -= lambda0) < 0 ? lambda1 + 360 : lambda1;
134}
135
136function rangeCompare(a, b) {
137 return a[0] - b[0];
138}
139
140function rangeContains(range, x) {
141 return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x;
142}
143
144export default function(feature) {
145 var i, n, a, b, merged, deltaMax, delta;
146
147 phi1 = lambda1 = -(lambda0 = phi0 = Infinity);
148 ranges = [];
149 stream(feature, boundsStream);
150
151 // First, sort ranges by their minimum longitudes.
152 if (n = ranges.length) {
153 ranges.sort(rangeCompare);
154
155 // Then, merge any ranges that overlap.
156 for (i = 1, a = ranges[0], merged = [a]; i < n; ++i) {
157 b = ranges[i];
158 if (rangeContains(a, b[0]) || rangeContains(a, b[1])) {
159 if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1];
160 if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0];
161 } else {
162 merged.push(a = b);
163 }
164 }
165
166 // Finally, find the largest gap between the merged ranges.
167 // The final bounding box will be the inverse of this gap.
168 for (deltaMax = -Infinity, n = merged.length - 1, i = 0, a = merged[n]; i <= n; a = b, ++i) {
169 b = merged[i];
170 if ((delta = angle(a[1], b[0])) > deltaMax) deltaMax = delta, lambda0 = b[0], lambda1 = a[1];
171 }
172 }
173
174 ranges = range = null;
175
176 return lambda0 === Infinity || phi0 === Infinity
177 ? [[NaN, NaN], [NaN, NaN]]
178 : [[lambda0, phi0], [lambda1, phi1]];
179}
Note: See TracBrowser for help on using the repository browser.