nodes2ts 2.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +3 -0
- package/.eslintrc.cjs +11 -0
- package/.github/workflows/lint.js.yml +22 -0
- package/.github/workflows/node.js.yml +22 -0
- package/.mocharc.js +7 -0
- package/README.md +21 -5
- package/dist/Interval.js +9 -8
- package/dist/Interval.js.map +1 -1
- package/dist/MutableInteger.js +5 -4
- package/dist/MutableInteger.js.map +1 -1
- package/dist/Platform.d.ts +15 -0
- package/dist/Platform.js +53 -0
- package/dist/Platform.js.map +1 -0
- package/dist/R1Interval.js +39 -48
- package/dist/R1Interval.js.map +1 -1
- package/dist/R2Vector.d.ts +3 -3
- package/dist/R2Vector.js +50 -57
- package/dist/R2Vector.js.map +1 -1
- package/dist/S1Angle.d.ts +34 -0
- package/dist/S1Angle.js +81 -26
- package/dist/S1Angle.js.map +1 -1
- package/dist/S1ChordAngle.d.ts +166 -0
- package/dist/S1ChordAngle.js +318 -0
- package/dist/S1ChordAngle.js.map +1 -0
- package/dist/S1Interval.js +78 -83
- package/dist/S1Interval.js.map +1 -1
- package/dist/S2.d.ts +5 -0
- package/dist/S2.js +80 -85
- package/dist/S2.js.map +1 -1
- package/dist/S2Cap.d.ts +31 -22
- package/dist/S2Cap.js +194 -187
- package/dist/S2Cap.js.map +1 -1
- package/dist/S2Cell.d.ts +14 -11
- package/dist/S2Cell.js +194 -197
- package/dist/S2Cell.js.map +1 -1
- package/dist/S2CellId.d.ts +37 -14
- package/dist/S2CellId.js +406 -356
- package/dist/S2CellId.js.map +1 -1
- package/dist/S2CellUnion.d.ts +1 -2
- package/dist/S2CellUnion.js +131 -126
- package/dist/S2CellUnion.js.map +1 -1
- package/dist/S2EdgeUtil.js +12 -14
- package/dist/S2EdgeUtil.js.map +1 -1
- package/dist/S2LatLng.d.ts +7 -2
- package/dist/S2LatLng.js +85 -81
- package/dist/S2LatLng.js.map +1 -1
- package/dist/S2LatLngRect.d.ts +7 -2
- package/dist/S2LatLngRect.js +203 -184
- package/dist/S2LatLngRect.js.map +1 -1
- package/dist/S2Metric.d.ts +1 -1
- package/dist/S2Metric.js +28 -24
- package/dist/S2Metric.js.map +1 -1
- package/dist/S2Point.d.ts +35 -0
- package/dist/S2Point.js +116 -66
- package/dist/S2Point.js.map +1 -1
- package/dist/S2Projections.d.ts +40 -5
- package/dist/S2Projections.js +226 -47
- package/dist/S2Projections.js.map +1 -1
- package/dist/S2Region.js +2 -1
- package/dist/S2Region.js.map +1 -1
- package/dist/S2RegionCoverer.d.ts +21 -6
- package/dist/S2RegionCoverer.js +144 -108
- package/dist/S2RegionCoverer.js.map +1 -1
- package/dist/export.js +42 -32
- package/dist/export.js.map +1 -1
- package/dist/utils/preconditions.d.ts +2 -0
- package/dist/utils/preconditions.js +16 -0
- package/dist/utils/preconditions.js.map +1 -0
- package/package.json +27 -21
- package/yarn.lock +0 -4381
package/dist/S2LatLngRect.js
CHANGED
|
@@ -1,46 +1,49 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.S2LatLngRect = void 0;
|
|
4
|
+
const S1Interval_1 = require("./S1Interval");
|
|
5
|
+
const R1Interval_1 = require("./R1Interval");
|
|
6
|
+
const S2LatLng_1 = require("./S2LatLng");
|
|
7
|
+
const S2_1 = require("./S2");
|
|
8
|
+
const S2Point_1 = require("./S2Point");
|
|
9
|
+
const S1Angle_1 = require("./S1Angle");
|
|
10
|
+
const S2EdgeUtil_1 = require("./S2EdgeUtil");
|
|
11
|
+
const S2Cap_1 = require("./S2Cap");
|
|
12
|
+
const Platform_1 = require("./Platform");
|
|
13
|
+
class S2LatLngRect {
|
|
14
|
+
constructor(lat, lng) {
|
|
12
15
|
this.lat = lat;
|
|
13
16
|
this.lng = lng;
|
|
14
17
|
}
|
|
15
|
-
|
|
18
|
+
static fromLatLng(lo, hi) {
|
|
16
19
|
return new S2LatLngRect(new R1Interval_1.R1Interval(lo.latRadians, hi.latRadians), new S1Interval_1.S1Interval(lo.lngRadians, hi.lngRadians));
|
|
17
|
-
}
|
|
20
|
+
}
|
|
18
21
|
/** The canonical empty rectangle */
|
|
19
|
-
|
|
22
|
+
static empty() {
|
|
20
23
|
return new S2LatLngRect(R1Interval_1.R1Interval.empty(), S1Interval_1.S1Interval.empty());
|
|
21
|
-
}
|
|
24
|
+
}
|
|
22
25
|
/** The canonical full rectangle. */
|
|
23
|
-
|
|
26
|
+
static full() {
|
|
24
27
|
return new S2LatLngRect(S2LatLngRect.fullLat(), S1Interval_1.S1Interval.full());
|
|
25
|
-
}
|
|
28
|
+
}
|
|
26
29
|
/** The full allowable range of latitudes. */
|
|
27
|
-
|
|
30
|
+
static fullLat() {
|
|
28
31
|
return new R1Interval_1.R1Interval(-S2_1.S2.M_PI_2, S2_1.S2.M_PI_2);
|
|
29
|
-
}
|
|
32
|
+
}
|
|
30
33
|
/**
|
|
31
34
|
* Construct a rectangle from a center point (in lat-lng space) and size in
|
|
32
35
|
* each dimension. If size.lng is greater than 360 degrees it is clamped,
|
|
33
36
|
* and latitudes greater than +/- 90 degrees are also clamped. So for example,
|
|
34
37
|
* FromCenterSize((80,170),(20,20)) -> (lo=(60,150),hi=(90,-170)).
|
|
35
38
|
*/
|
|
36
|
-
|
|
39
|
+
static fromCenterSize(center, size) {
|
|
37
40
|
return S2LatLngRect.fromPoint(center).expanded(size.mul(0.5));
|
|
38
|
-
}
|
|
41
|
+
}
|
|
39
42
|
/** Convenience method to construct a rectangle containing a single point. */
|
|
40
|
-
|
|
43
|
+
static fromPoint(p) {
|
|
41
44
|
// assert (p.isValid());
|
|
42
45
|
return S2LatLngRect.fromLatLng(p, p);
|
|
43
|
-
}
|
|
46
|
+
}
|
|
44
47
|
/**
|
|
45
48
|
* Convenience method to construct the minimal bounding rectangle containing
|
|
46
49
|
* the two given points. This is equivalent to starting with an empty
|
|
@@ -48,80 +51,92 @@ var S2LatLngRect = (function () {
|
|
|
48
51
|
* S2LatLngRect(lo, hi) constructor, where the first point is always used as
|
|
49
52
|
* the lower-left corner of the resulting rectangle.
|
|
50
53
|
*/
|
|
51
|
-
|
|
54
|
+
static fromPointPair(p1, p2) {
|
|
52
55
|
// assert (p1.isValid() && p2.isValid());
|
|
53
56
|
return new S2LatLngRect(R1Interval_1.R1Interval.fromPointPair(p1.latRadians, p2
|
|
54
57
|
.latRadians), S1Interval_1.S1Interval.fromPointPair(p1.lngRadians, p2.lngRadians));
|
|
55
|
-
}
|
|
58
|
+
}
|
|
56
59
|
/**
|
|
57
60
|
* Return a latitude-longitude rectangle that contains the edge from "a" to
|
|
58
61
|
* "b". Both points must be unit-length. Note that the bounding rectangle of
|
|
59
62
|
* an edge can be larger than the bounding rectangle of its endpoints.
|
|
60
63
|
*/
|
|
61
|
-
|
|
64
|
+
static fromEdge(a, b) {
|
|
62
65
|
// assert (S2.isUnitLength(a) && S2.isUnitLength(b));
|
|
63
|
-
|
|
66
|
+
const r = S2LatLngRect.fromPointPair(S2LatLng_1.S2LatLng.fromPoint(a), S2LatLng_1.S2LatLng.fromPoint(b));
|
|
64
67
|
// Check whether the min/max latitude occurs in the edge interior.
|
|
65
68
|
// We find the normal to the plane containing AB, and then a vector "dir" in
|
|
66
69
|
// this plane that also passes through the equator. We use RobustCrossProd
|
|
67
70
|
// to ensure that the edge normal is accurate even when the two points are
|
|
68
71
|
// very close together.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
72
|
+
const ab = S2_1.S2.robustCrossProd(a, b);
|
|
73
|
+
const dir = S2Point_1.S2Point.crossProd(ab, new S2Point_1.S2Point(0, 0, 1));
|
|
74
|
+
const da = dir.dotProd(a);
|
|
75
|
+
const db = dir.dotProd(b);
|
|
73
76
|
if (da * db >= 0) {
|
|
74
77
|
// Minimum and maximum latitude are attained at the vertices.
|
|
75
78
|
return r;
|
|
76
79
|
}
|
|
77
80
|
// Minimum/maximum latitude occurs in the edge interior. This affects the
|
|
78
81
|
// latitude bounds but not the longitude bounds.
|
|
79
|
-
|
|
82
|
+
const absLat = Math.acos(ab.z / Math.abs(ab.norm()));
|
|
80
83
|
if (da < 0) {
|
|
81
84
|
return new S2LatLngRect(new R1Interval_1.R1Interval(r.lat.lo, absLat), r.lng);
|
|
82
85
|
}
|
|
83
86
|
else {
|
|
84
87
|
return new S2LatLngRect(new R1Interval_1.R1Interval(-absLat, r.lat.hi), r.lng);
|
|
85
88
|
}
|
|
86
|
-
}
|
|
89
|
+
}
|
|
87
90
|
/**
|
|
88
91
|
* Return true if the rectangle is valid, which essentially just means that
|
|
89
92
|
* the latitude bounds do not exceed Pi/2 in absolute value and the longitude
|
|
90
93
|
* bounds do not exceed Pi in absolute value.
|
|
91
94
|
*
|
|
92
95
|
*/
|
|
93
|
-
|
|
96
|
+
isValid() {
|
|
94
97
|
// The lat/lng ranges must either be both empty or both non-empty.
|
|
95
98
|
return (Math.abs(this.lat.lo) <= S2_1.S2.M_PI_2 && Math.abs(this.lat.hi) <= (S2_1.S2.M_PI_2)
|
|
96
99
|
&& this.lng.isValid() && this.lat.isEmpty() == this.lng.isEmpty());
|
|
97
|
-
}
|
|
98
|
-
|
|
100
|
+
}
|
|
101
|
+
lo() {
|
|
99
102
|
return new S2LatLng_1.S2LatLng(this.lat.lo, this.lng.lo);
|
|
100
|
-
}
|
|
101
|
-
|
|
103
|
+
}
|
|
104
|
+
hi() {
|
|
102
105
|
return new S2LatLng_1.S2LatLng(this.lat.hi, this.lng.hi);
|
|
103
|
-
}
|
|
106
|
+
}
|
|
107
|
+
latLo() {
|
|
108
|
+
return S1Angle_1.S1Angle.radians(this.lat.lo);
|
|
109
|
+
}
|
|
110
|
+
latHi() {
|
|
111
|
+
return S1Angle_1.S1Angle.radians(this.lat.hi);
|
|
112
|
+
}
|
|
113
|
+
lngLo() {
|
|
114
|
+
return S1Angle_1.S1Angle.radians(this.lng.lo);
|
|
115
|
+
}
|
|
116
|
+
lngHi() {
|
|
117
|
+
return S1Angle_1.S1Angle.radians(this.lng.hi);
|
|
118
|
+
}
|
|
104
119
|
/**
|
|
105
120
|
* Return true if the rectangle is empty, i.e. it contains no points at all.
|
|
106
121
|
*/
|
|
107
|
-
|
|
122
|
+
isEmpty() {
|
|
108
123
|
return this.lat.isEmpty();
|
|
109
|
-
}
|
|
124
|
+
}
|
|
110
125
|
// Return true if the rectangle is full, i.e. it contains all points.
|
|
111
|
-
|
|
126
|
+
isFull() {
|
|
112
127
|
// console.log(this.lat.toString());
|
|
113
128
|
// console.log(S2LatLngRect.fullLat().toString());
|
|
114
129
|
return this.lat.equals(S2LatLngRect.fullLat()) && this.lng.isFull();
|
|
115
|
-
}
|
|
130
|
+
}
|
|
116
131
|
/**
|
|
117
132
|
* Return true if lng_.lo() > lng_.hi(), i.e. the rectangle crosses the 180
|
|
118
133
|
* degree latitude line.
|
|
119
134
|
*/
|
|
120
|
-
|
|
135
|
+
isInverted() {
|
|
121
136
|
return this.lng.isInverted();
|
|
122
|
-
}
|
|
137
|
+
}
|
|
123
138
|
/** Return the k-th vertex of the rectangle (k = 0,1,2,3) in CCW order. */
|
|
124
|
-
|
|
139
|
+
getVertex(k) {
|
|
125
140
|
// Return the points in CCW order (SW, SE, NE, NW).
|
|
126
141
|
switch (k) {
|
|
127
142
|
case 0:
|
|
@@ -135,87 +150,85 @@ var S2LatLngRect = (function () {
|
|
|
135
150
|
default:
|
|
136
151
|
throw new Error("Invalid vertex index.");
|
|
137
152
|
}
|
|
138
|
-
}
|
|
153
|
+
}
|
|
139
154
|
/**
|
|
140
155
|
* Return the center of the rectangle in latitude-longitude space (in general
|
|
141
156
|
* this is not the center of the region on the sphere).
|
|
142
157
|
*/
|
|
143
|
-
|
|
158
|
+
getCenter() {
|
|
144
159
|
return new S2LatLng_1.S2LatLng(this.lat.getCenter(), this.lng.getCenter());
|
|
145
|
-
}
|
|
160
|
+
}
|
|
146
161
|
/**
|
|
147
162
|
* Return the minimum distance (measured along the surface of the sphere)
|
|
148
163
|
* from a given point to the rectangle (both its boundary and its interior).
|
|
149
164
|
* The latLng must be valid.
|
|
150
165
|
*/
|
|
151
|
-
|
|
166
|
+
getDistanceLL(p) {
|
|
152
167
|
// The algorithm here is the same as in getDistance(S2LagLngRect), only
|
|
153
168
|
// with simplified calculations.
|
|
154
|
-
|
|
155
|
-
if (a.isEmpty()) {
|
|
169
|
+
if (this.isEmpty()) {
|
|
156
170
|
throw new Error();
|
|
157
171
|
}
|
|
158
172
|
if (!p.isValid()) {
|
|
159
173
|
throw new Error('point is not valid');
|
|
160
174
|
}
|
|
161
|
-
if (
|
|
162
|
-
return new S1Angle_1.S1Angle(Math.max(0.0, Math.max(p.latRadians -
|
|
175
|
+
if (this.lng.contains(p.lngRadians)) {
|
|
176
|
+
return new S1Angle_1.S1Angle(Math.max(0.0, Math.max(p.latRadians - this.lat.hi, this.lat.lo - p.latRadians)));
|
|
163
177
|
}
|
|
164
|
-
|
|
165
|
-
|
|
178
|
+
const interval = new S1Interval_1.S1Interval(this.lng.hi, this.lng.complement().getCenter());
|
|
179
|
+
let aLng = this.lng.lo;
|
|
166
180
|
if (interval.contains(p.lngRadians)) {
|
|
167
|
-
aLng =
|
|
181
|
+
aLng = this.lng.hi;
|
|
168
182
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
183
|
+
const lo = new S2LatLng_1.S2LatLng(this.lat.lo, aLng).toPoint();
|
|
184
|
+
const hi = new S2LatLng_1.S2LatLng(this.lat.hi, aLng).toPoint();
|
|
185
|
+
const loCrossHi = new S2LatLng_1.S2LatLng(0, aLng - S2_1.S2.M_PI_2).normalized().toPoint();
|
|
172
186
|
return S2EdgeUtil_1.S2EdgeUtil.getDistance(p.toPoint(), lo, hi, loCrossHi);
|
|
173
|
-
}
|
|
187
|
+
}
|
|
174
188
|
/**
|
|
175
189
|
* Return the minimum distance (measured along the surface of the sphere) to
|
|
176
190
|
* the given S2LatLngRect. Both S2LatLngRects must be non-empty.
|
|
177
191
|
*/
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
if (a.isEmpty()) {
|
|
192
|
+
getDistanceLLR(other) {
|
|
193
|
+
const b = other;
|
|
194
|
+
if (this.isEmpty()) {
|
|
182
195
|
throw new Error();
|
|
183
196
|
}
|
|
184
197
|
if (b.isEmpty()) {
|
|
185
198
|
throw new Error();
|
|
186
199
|
}
|
|
187
200
|
// First, handle the trivial cases where the longitude intervals overlap.
|
|
188
|
-
if (
|
|
189
|
-
if (
|
|
201
|
+
if (this.lng.intersects(b.lng)) {
|
|
202
|
+
if (this.lat.intersects(b.lat)) {
|
|
190
203
|
return new S1Angle_1.S1Angle(0); // Intersection between a and b.
|
|
191
204
|
}
|
|
192
205
|
// We found an overlap in the longitude interval, but not in the latitude
|
|
193
206
|
// interval. This means the shortest path travels along some line of
|
|
194
207
|
// longitude connecting the high-latitude of the lower rect with the
|
|
195
208
|
// low-latitude of the higher rect.
|
|
196
|
-
|
|
197
|
-
if (
|
|
209
|
+
let lo, hi;
|
|
210
|
+
if (this.lat.lo > b.lat.hi) {
|
|
198
211
|
lo = b.lat.hi;
|
|
199
|
-
hi =
|
|
212
|
+
hi = this.lat.lo;
|
|
200
213
|
}
|
|
201
214
|
else {
|
|
202
|
-
lo =
|
|
215
|
+
lo = this.lat.hi;
|
|
203
216
|
hi = b.lat.lo;
|
|
204
217
|
}
|
|
205
|
-
return
|
|
218
|
+
return S1Angle_1.S1Angle.radians(hi.radians() - lo.radians());
|
|
206
219
|
}
|
|
207
220
|
// The longitude intervals don't overlap. In this case, the closest points
|
|
208
221
|
// occur somewhere on the pair of longitudinal edges which are nearest in
|
|
209
222
|
// longitude-space.
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
223
|
+
let aLng, bLng;
|
|
224
|
+
const loHi = S1Interval_1.S1Interval.fromPointPair(this.lng.lo, b.lng.hi);
|
|
225
|
+
const hiLo = S1Interval_1.S1Interval.fromPointPair(this.lng.hi, b.lng.lo);
|
|
213
226
|
if (loHi.getLength() < (hiLo.getLength())) {
|
|
214
|
-
aLng =
|
|
227
|
+
aLng = this.lng.lo;
|
|
215
228
|
bLng = b.lng.hi;
|
|
216
229
|
}
|
|
217
230
|
else {
|
|
218
|
-
aLng =
|
|
231
|
+
aLng = this.lng.hi;
|
|
219
232
|
bLng = b.lng.lo;
|
|
220
233
|
}
|
|
221
234
|
// The shortest distance between the two longitudinal segments will include
|
|
@@ -223,78 +236,78 @@ var S2LatLngRect = (function () {
|
|
|
223
236
|
// to a single point-edge distance by comparing the relative latitudes of the
|
|
224
237
|
// endpoints, but for the sake of clarity, we'll do all four point-edge
|
|
225
238
|
// distance tests.
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
239
|
+
const aLo = new S2LatLng_1.S2LatLng(this.lat.lo, aLng).toPoint();
|
|
240
|
+
const aHi = new S2LatLng_1.S2LatLng(this.lat.hi, aLng).toPoint();
|
|
241
|
+
const aLoCrossHi = new S2LatLng_1.S2LatLng(0, aLng.radians().minus(S2_1.S2.M_PI_2)).normalized().toPoint();
|
|
242
|
+
const bLo = new S2LatLng_1.S2LatLng(b.lat.lo, bLng).toPoint();
|
|
243
|
+
const bHi = new S2LatLng_1.S2LatLng(b.lat.hi, bLng).toPoint();
|
|
244
|
+
const bLoCrossHi = new S2LatLng_1.S2LatLng(0, bLng.radians().minus(S2_1.S2.M_PI_2)).normalized().toPoint();
|
|
232
245
|
return S1Angle_1.S1Angle.min(S2EdgeUtil_1.S2EdgeUtil.getDistance(aLo, bLo, bHi, bLoCrossHi), S1Angle_1.S1Angle.min(S2EdgeUtil_1.S2EdgeUtil.getDistance(aHi, bLo, bHi, bLoCrossHi), S1Angle_1.S1Angle.min(S2EdgeUtil_1.S2EdgeUtil.getDistance(bLo, aLo, aHi, aLoCrossHi), S2EdgeUtil_1.S2EdgeUtil.getDistance(bHi, aLo, aHi, aLoCrossHi))));
|
|
233
|
-
}
|
|
246
|
+
}
|
|
234
247
|
/**
|
|
235
248
|
* Return the width and height of this rectangle in latitude-longitude space.
|
|
236
249
|
* Empty rectangles have a negative width and height.
|
|
237
250
|
*/
|
|
238
|
-
|
|
251
|
+
getSize() {
|
|
239
252
|
return new S2LatLng_1.S2LatLng(this.lat.getLength(), this.lng.getLength());
|
|
240
|
-
}
|
|
253
|
+
}
|
|
241
254
|
/**
|
|
242
255
|
* More efficient version of Contains() that accepts a S2LatLng rather than an
|
|
243
256
|
* S2Point.
|
|
244
257
|
*/
|
|
245
|
-
|
|
258
|
+
containsLL(ll) {
|
|
246
259
|
// assert (ll.isValid());
|
|
247
260
|
return (this.lat.contains(ll.latRadians) && this.lng.contains(ll.lngRadians));
|
|
248
|
-
}
|
|
261
|
+
}
|
|
249
262
|
/**
|
|
250
263
|
* Return true if and only if the given point is contained in the interior of
|
|
251
264
|
* the region (i.e. the region excluding its boundary). The point 'p' does not
|
|
252
265
|
* need to be normalized.
|
|
253
266
|
*/
|
|
254
|
-
|
|
267
|
+
interiorContainsP(p) {
|
|
255
268
|
return this.interiorContainsLL(S2LatLng_1.S2LatLng.fromPoint(p));
|
|
256
|
-
}
|
|
269
|
+
}
|
|
257
270
|
/**
|
|
258
271
|
* More efficient version of InteriorContains() that accepts a S2LatLng rather
|
|
259
272
|
* than an S2Point.
|
|
260
273
|
*/
|
|
261
|
-
|
|
274
|
+
interiorContainsLL(ll) {
|
|
262
275
|
// assert (ll.isValid());
|
|
263
276
|
return (this.lat.interiorContains(ll.latRadians) && this.lng
|
|
264
277
|
.interiorContains(ll.lngRadians));
|
|
265
|
-
}
|
|
278
|
+
}
|
|
266
279
|
/**
|
|
267
280
|
* Return true if and only if the rectangle contains the given other
|
|
268
281
|
* rectangle.
|
|
269
282
|
*/
|
|
270
|
-
|
|
283
|
+
containsLLR(other) {
|
|
271
284
|
return this.lat.containsI(other.lat) && this.lng.containsI(other.lng);
|
|
272
|
-
}
|
|
285
|
+
}
|
|
273
286
|
/**
|
|
274
287
|
* Return true if and only if the interior of this rectangle contains all
|
|
275
288
|
* points of the given other rectangle (including its boundary).
|
|
276
289
|
*/
|
|
277
|
-
|
|
290
|
+
interiorContainsLLR(other) {
|
|
278
291
|
return (this.lat.interiorContainsI(other.lat) && this.lng
|
|
279
292
|
.interiorContainsI(other.lng));
|
|
280
|
-
}
|
|
293
|
+
}
|
|
281
294
|
/** Return true if this rectangle and the given other rectangle have any
|
|
282
295
|
points in common. */
|
|
283
|
-
|
|
296
|
+
intersectsLLR(other) {
|
|
284
297
|
return this.lat.intersects(other.lat) && this.lng.intersects(other.lng);
|
|
285
|
-
}
|
|
298
|
+
}
|
|
286
299
|
/**
|
|
287
300
|
* Returns true if this rectangle intersects the given cell. (This is an exact
|
|
288
301
|
* test and may be fairly expensive, see also MayIntersect below.)
|
|
289
302
|
*/
|
|
290
|
-
|
|
303
|
+
intersects(cell) {
|
|
291
304
|
// First we eliminate the cases where one region completely contains the
|
|
292
305
|
// other. Once these are disposed of, then the regions will intersect
|
|
293
306
|
// if and only if their boundaries intersect.
|
|
294
307
|
if (this.isEmpty()) {
|
|
295
308
|
return false;
|
|
296
309
|
}
|
|
297
|
-
if (this.containsP(cell.
|
|
310
|
+
if (this.containsP(cell.getCenterRaw())) {
|
|
298
311
|
return true;
|
|
299
312
|
}
|
|
300
313
|
if (cell.contains(this.getCenter().toPoint())) {
|
|
@@ -308,22 +321,22 @@ var S2LatLngRect = (function () {
|
|
|
308
321
|
// latitude-longitude rectangle does not have straight edges -- two edges
|
|
309
322
|
// are curved, and at least one of them is concave.
|
|
310
323
|
// Precompute the cell vertices as points and latitude-longitudes.
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
for (
|
|
324
|
+
const cellV = new Array(4);
|
|
325
|
+
const cellLl = new Array(4);
|
|
326
|
+
for (let i = 0; i < 4; ++i) {
|
|
314
327
|
cellV[i] = cell.getVertex(i); // Must be normalized.
|
|
315
328
|
cellLl[i] = S2LatLng_1.S2LatLng.fromPoint(cellV[i]);
|
|
316
329
|
if (this.containsLL(cellLl[i])) {
|
|
317
330
|
return true; // Quick acceptance test.
|
|
318
331
|
}
|
|
319
332
|
}
|
|
320
|
-
for (
|
|
321
|
-
|
|
333
|
+
for (let i = 0; i < 4; ++i) {
|
|
334
|
+
const edgeLng = S1Interval_1.S1Interval.fromPointPair(cellLl[i].lngRadians, cellLl[(i + 1) & 3].lngRadians);
|
|
322
335
|
if (!this.lng.intersects(edgeLng)) {
|
|
323
336
|
continue;
|
|
324
337
|
}
|
|
325
|
-
|
|
326
|
-
|
|
338
|
+
const a = cellV[i];
|
|
339
|
+
const b = cellV[(i + 1) & 3];
|
|
327
340
|
if (edgeLng.contains(this.lng.lo)) {
|
|
328
341
|
if (S2LatLngRect.intersectsLngEdge(a, b, this.lat, this.lng.lo)) {
|
|
329
342
|
return true;
|
|
@@ -342,25 +355,25 @@ var S2LatLngRect = (function () {
|
|
|
342
355
|
}
|
|
343
356
|
}
|
|
344
357
|
return false;
|
|
345
|
-
}
|
|
358
|
+
}
|
|
346
359
|
/**
|
|
347
360
|
* Return true if and only if the interior of this rectangle intersects any
|
|
348
361
|
* point (including the boundary) of the given other rectangle.
|
|
349
362
|
*/
|
|
350
|
-
|
|
363
|
+
interiorIntersects(other) {
|
|
351
364
|
return (this.lat.interiorIntersects(other.lat) && this.lng
|
|
352
365
|
.interiorIntersects(other.lng));
|
|
353
|
-
}
|
|
354
|
-
|
|
366
|
+
}
|
|
367
|
+
addPoint(p) {
|
|
355
368
|
return this.addPointLL(S2LatLng_1.S2LatLng.fromPoint(p));
|
|
356
|
-
}
|
|
369
|
+
}
|
|
357
370
|
// Increase the size of the bounding rectangle to include the given point.
|
|
358
371
|
// The rectangle is expanded by the minimum amount possible.
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
372
|
+
addPointLL(ll) {
|
|
373
|
+
const newLat = this.lat.addPoint(ll.latRadians);
|
|
374
|
+
const newLng = this.lng.addPoint(ll.lngRadians);
|
|
362
375
|
return new S2LatLngRect(newLat, newLng);
|
|
363
|
-
}
|
|
376
|
+
}
|
|
364
377
|
/**
|
|
365
378
|
* Return a rectangle that contains all points whose latitude distance from
|
|
366
379
|
* this rectangle is at most margin.lat, and whose longitude distance from
|
|
@@ -372,7 +385,7 @@ var S2LatLngRect = (function () {
|
|
|
372
385
|
* NOTE: If you are trying to grow a rectangle by a certain *distance* on the
|
|
373
386
|
* sphere (e.g. 5km), use the ConvolveWithCap() method instead.
|
|
374
387
|
*/
|
|
375
|
-
|
|
388
|
+
expanded(margin) {
|
|
376
389
|
// assert (margin.latRadians >= 0 && margin.lngRadians >= 0);
|
|
377
390
|
if (this.isEmpty()) {
|
|
378
391
|
return this;
|
|
@@ -380,29 +393,37 @@ var S2LatLngRect = (function () {
|
|
|
380
393
|
return new S2LatLngRect(this.lat
|
|
381
394
|
.expanded(margin.latRadians)
|
|
382
395
|
.intersection(S2LatLngRect.fullLat()), this.lng.expanded(margin.lngRadians));
|
|
383
|
-
}
|
|
396
|
+
}
|
|
397
|
+
polarClosure() {
|
|
398
|
+
if (this.lat.lo == -S2_1.S2.M_PI_2 || this.lat.hi == S2_1.S2.M_PI_2) {
|
|
399
|
+
return new S2LatLngRect(this.lat, S1Interval_1.S1Interval.full());
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
return this;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
384
405
|
/**
|
|
385
406
|
* Return the smallest rectangle containing the union of this rectangle and
|
|
386
407
|
* the given rectangle.
|
|
387
408
|
*/
|
|
388
|
-
|
|
409
|
+
union(other) {
|
|
389
410
|
return new S2LatLngRect(this.lat.union(other.lat), this.lng.union(other.lng));
|
|
390
|
-
}
|
|
411
|
+
}
|
|
391
412
|
/**
|
|
392
413
|
* Return the smallest rectangle containing the intersection of this rectangle
|
|
393
414
|
* and the given rectangle. Note that the region of intersection may consist
|
|
394
415
|
* of two disjoint rectangles, in which case a single rectangle spanning both
|
|
395
416
|
* of them is returned.
|
|
396
417
|
*/
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
418
|
+
intersection(other) {
|
|
419
|
+
const intersectLat = this.lat.intersection(other.lat);
|
|
420
|
+
const intersectLng = this.lng.intersection(other.lng);
|
|
400
421
|
if (intersectLat.isEmpty() || intersectLng.isEmpty()) {
|
|
401
422
|
// The lat/lng ranges must either be both empty or both non-empty.
|
|
402
423
|
return S2LatLngRect.empty();
|
|
403
424
|
}
|
|
404
425
|
return new S2LatLngRect(intersectLat, intersectLng);
|
|
405
|
-
}
|
|
426
|
+
}
|
|
406
427
|
//
|
|
407
428
|
// /**
|
|
408
429
|
// * Return a rectangle that contains the convolution of this rectangle with a
|
|
@@ -428,7 +449,7 @@ var S2LatLngRect = (function () {
|
|
|
428
449
|
// return r;
|
|
429
450
|
// }
|
|
430
451
|
/** Return the surface area of this rectangle on the unit sphere. */
|
|
431
|
-
|
|
452
|
+
area() {
|
|
432
453
|
if (this.isEmpty()) {
|
|
433
454
|
return 0;
|
|
434
455
|
}
|
|
@@ -436,72 +457,71 @@ var S2LatLngRect = (function () {
|
|
|
436
457
|
// the longitude ratio.
|
|
437
458
|
//TODO: check if this.lat.hi & this.lat.lo is radians.
|
|
438
459
|
return this.lng.getLength() * (Math.sin(this.lat.hi) - Math.abs(Math.sin(this.lat.lo)));
|
|
439
|
-
}
|
|
460
|
+
}
|
|
440
461
|
/** Return true if two rectangles contains the same set of points. */
|
|
441
|
-
|
|
462
|
+
equals(that) {
|
|
442
463
|
if (!(that instanceof S2LatLngRect)) {
|
|
443
464
|
return false;
|
|
444
465
|
}
|
|
445
466
|
return this.lat.equals(that.lat) && this.lng.equals(that.lng);
|
|
446
|
-
}
|
|
467
|
+
}
|
|
447
468
|
/**
|
|
448
469
|
* Return true if the latitude and longitude intervals of the two rectangles
|
|
449
470
|
* are the same up to the given tolerance (see r1interval.h and s1interval.h
|
|
450
471
|
* for details).
|
|
451
472
|
*/
|
|
452
|
-
|
|
453
|
-
if (maxError === void 0) { maxError = 1e-15; }
|
|
473
|
+
approxEquals(other, maxError = 1e-15) {
|
|
454
474
|
return (this.lat.approxEquals(other.lat, maxError) && this.lng.approxEquals(other.lng, maxError));
|
|
455
|
-
}
|
|
475
|
+
}
|
|
456
476
|
// //////////////////////////////////////////////////////////////////////
|
|
457
477
|
// S2Region interface (see {@code S2Region} for details):
|
|
458
|
-
|
|
478
|
+
clone() {
|
|
459
479
|
return new S2LatLngRect(this.lat, this.lng);
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
// We consider two possible bounding caps, one whose axis passes
|
|
463
|
-
//
|
|
464
|
-
//
|
|
480
|
+
}
|
|
481
|
+
getCapBound() {
|
|
482
|
+
// We consider two possible bounding caps, one whose axis passes through the center of the
|
|
483
|
+
// lat-lng rectangle and one whose axis is the north or south pole. We return the smaller of the
|
|
484
|
+
// two caps.
|
|
465
485
|
if (this.isEmpty()) {
|
|
466
486
|
return S2Cap_1.S2Cap.empty();
|
|
467
487
|
}
|
|
468
|
-
|
|
469
|
-
|
|
488
|
+
let poleZ = 0;
|
|
489
|
+
let poleAngle = 0;
|
|
490
|
+
if (this.lat.lo + this.lat.hi < 0) {
|
|
470
491
|
// South pole axis yields smaller cap.
|
|
471
492
|
poleZ = -1;
|
|
472
|
-
poleAngle =
|
|
493
|
+
poleAngle = S2_1.S2.M_PI_2 + this.lat.hi;
|
|
473
494
|
}
|
|
474
495
|
else {
|
|
475
496
|
poleZ = 1;
|
|
476
|
-
poleAngle =
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
// For bounding rectangles that span 180 degrees or less in longitude, the
|
|
480
|
-
//
|
|
481
|
-
//
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
for (var k = 0; k < 4; ++k) {
|
|
497
|
+
poleAngle = S2_1.S2.M_PI_2 - this.lat.lo;
|
|
498
|
+
}
|
|
499
|
+
const poleCap = S2Cap_1.S2Cap.fromAxisAngle(new S2Point_1.S2Point(0, 0, poleZ), S1Angle_1.S1Angle.radians(poleAngle));
|
|
500
|
+
// For bounding rectangles that span 180 degrees or less in longitude, the maximum cap size is
|
|
501
|
+
// achieved at one of the rectangle vertices. For rectangles that are larger than 180 degrees,
|
|
502
|
+
// we punt and always return a bounding cap centered at one of the two poles.
|
|
503
|
+
const lngSpan = this.lng.hi - this.lng.lo;
|
|
504
|
+
if (Platform_1.Platform.IEEEremainder(lngSpan, 2 * S2_1.S2.M_PI) >= 0) {
|
|
505
|
+
if (lngSpan < 2 * S2_1.S2.M_PI) {
|
|
506
|
+
let midCap = S2Cap_1.S2Cap.fromAxisAngle(this.getCenter().toPoint(), S1Angle_1.S1Angle.radians(0));
|
|
507
|
+
for (let k = 0; k < 4; ++k) {
|
|
488
508
|
midCap = midCap.addPoint(this.getVertex(k).toPoint());
|
|
489
509
|
}
|
|
490
|
-
if (midCap.height <
|
|
510
|
+
if (midCap.height() < poleCap.height()) {
|
|
491
511
|
return midCap;
|
|
492
512
|
}
|
|
493
513
|
}
|
|
494
514
|
}
|
|
495
515
|
return poleCap;
|
|
496
|
-
}
|
|
497
|
-
|
|
516
|
+
}
|
|
517
|
+
getRectBound() {
|
|
498
518
|
return this;
|
|
499
|
-
}
|
|
500
|
-
|
|
519
|
+
}
|
|
520
|
+
containsC(cell) {
|
|
501
521
|
// A latitude-longitude rectangle contains a cell if and only if it contains
|
|
502
522
|
// the cell's bounding rectangle. (This is an exact test.)
|
|
503
523
|
return this.containsLLR(cell.getRectBound());
|
|
504
|
-
}
|
|
524
|
+
}
|
|
505
525
|
/**
|
|
506
526
|
* This test is cheap but is NOT exact. Use Intersects() if you want a more
|
|
507
527
|
* accurate and more expensive test. Note that when this method is used by an
|
|
@@ -509,97 +529,96 @@ var S2LatLngRect = (function () {
|
|
|
509
529
|
* intersect the region then it is subdivided, and the accuracy of this method
|
|
510
530
|
* goes up as the cells get smaller.
|
|
511
531
|
*/
|
|
512
|
-
|
|
532
|
+
mayIntersectC(cell) {
|
|
513
533
|
// This test is cheap but is NOT exact (see s2latlngrect.h).
|
|
514
534
|
return this.intersectsLLR(cell.getRectBound());
|
|
515
|
-
}
|
|
535
|
+
}
|
|
516
536
|
/** The point 'p' does not need to be normalized. */
|
|
517
|
-
|
|
537
|
+
containsP(p) {
|
|
518
538
|
return this.containsLL(S2LatLng_1.S2LatLng.fromPoint(p));
|
|
519
|
-
}
|
|
539
|
+
}
|
|
520
540
|
/**
|
|
521
541
|
* Return true if the edge AB intersects the given edge of constant longitude.
|
|
522
542
|
*/
|
|
523
|
-
|
|
543
|
+
static /*boolean*/ intersectsLngEdge(a, b, lat, lng) {
|
|
524
544
|
// Return true if the segment AB intersects the given edge of constant
|
|
525
545
|
// longitude. The nice thing about edges of constant longitude is that
|
|
526
546
|
// they are straight lines on the sphere (geodesics).
|
|
527
547
|
return S2_1.S2.simpleCrossing(a, b, new S2LatLng_1.S2LatLng(lat.lo, lng)
|
|
528
548
|
.toPoint(), new S2LatLng_1.S2LatLng(lat.hi, lng).toPoint());
|
|
529
|
-
}
|
|
549
|
+
}
|
|
530
550
|
/**
|
|
531
551
|
* Return true if the edge AB intersects the given edge of constant latitude.
|
|
532
552
|
*/
|
|
533
|
-
|
|
553
|
+
static /*boolean*/ intersectsLatEdge(a, b, lat, lng) {
|
|
534
554
|
// Return true if the segment AB intersects the given edge of constant
|
|
535
555
|
// latitude. Unfortunately, lines of constant latitude are curves on
|
|
536
556
|
// the sphere. They can intersect a straight edge in 0, 1, or 2 points.
|
|
537
557
|
// assert (S2.isUnitLength(a) && S2.isUnitLength(b));
|
|
538
558
|
// First, compute the normal to the plane AB that points vaguely north.
|
|
539
|
-
|
|
559
|
+
let z = S2Point_1.S2Point.normalize(S2_1.S2.robustCrossProd(a, b));
|
|
540
560
|
if (z.z < (0)) {
|
|
541
561
|
z = S2Point_1.S2Point.neg(z);
|
|
542
562
|
}
|
|
543
563
|
// Extend this to an orthonormal frame (x,y,z) where x is the direction
|
|
544
564
|
// where the great circle through AB achieves its maximium latitude.
|
|
545
|
-
|
|
546
|
-
|
|
565
|
+
const y = S2Point_1.S2Point.normalize(S2_1.S2.robustCrossProd(z, new S2Point_1.S2Point(0, 0, 1)));
|
|
566
|
+
const x = S2Point_1.S2Point.crossProd(y, z);
|
|
547
567
|
// assert (S2.isUnitLength(x) && x.z >= 0);
|
|
548
568
|
// Compute the angle "theta" from the x-axis (in the x-y plane defined
|
|
549
569
|
// above) where the great circle intersects the given line of latitude.
|
|
550
|
-
|
|
570
|
+
const sinLat = Math.sin(lat);
|
|
551
571
|
if (Math.abs(sinLat) >= (x.z)) {
|
|
552
572
|
return false; // The great circle does not reach the given latitude.
|
|
553
573
|
}
|
|
554
574
|
// assert (x.z > 0);
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
575
|
+
const cosTheta = sinLat / x.z;
|
|
576
|
+
const sinTheta = Math.sqrt(1 - cosTheta * cosTheta);
|
|
577
|
+
const theta = Math.atan2(sinTheta, cosTheta);
|
|
558
578
|
// Math.atan2(sinTheta, cosTheta);
|
|
559
579
|
// The candidate intersection points are located +/- theta in the x-y
|
|
560
580
|
// plane. For an intersection to be valid, we need to check that the
|
|
561
581
|
// intersection point is contained in the interior of the edge AB and
|
|
562
582
|
// also that it is contained within the given longitude interval "lng".
|
|
563
583
|
// Compute the range of theta values spanned by the edge AB.
|
|
564
|
-
|
|
584
|
+
const abTheta = S1Interval_1.S1Interval.fromPointPair(Math.atan2(a.dotProd(y), a.dotProd(x)), Math.atan2(b.dotProd(y), b.dotProd(x)));
|
|
565
585
|
if (abTheta.contains(theta)) {
|
|
566
586
|
// Check if the intersection point is also in the given "lng" interval.
|
|
567
|
-
|
|
587
|
+
const isect = S2Point_1.S2Point.add(S2Point_1.S2Point.mul(x, cosTheta), S2Point_1.S2Point.mul(y, sinTheta));
|
|
568
588
|
if (lng.contains(Math.atan2(isect.y, isect.x))) {
|
|
569
589
|
return true;
|
|
570
590
|
}
|
|
571
591
|
}
|
|
572
592
|
if (abTheta.contains(theta * -1)) {
|
|
573
593
|
// Check if the intersection point is also in the given "lng" interval.
|
|
574
|
-
|
|
594
|
+
const intersection = S2Point_1.S2Point.sub(S2Point_1.S2Point.mul(x, cosTheta), S2Point_1.S2Point.mul(y, sinTheta));
|
|
575
595
|
if (lng.contains(Math.atan2(intersection.y, intersection.x))) {
|
|
576
596
|
return true;
|
|
577
597
|
}
|
|
578
598
|
}
|
|
579
599
|
return false;
|
|
580
|
-
}
|
|
581
|
-
|
|
600
|
+
}
|
|
601
|
+
allVertex() {
|
|
582
602
|
return [
|
|
583
603
|
this.getVertex(0),
|
|
584
604
|
this.getVertex(1),
|
|
585
605
|
this.getVertex(2),
|
|
586
606
|
this.getVertex(3)
|
|
587
607
|
];
|
|
588
|
-
}
|
|
589
|
-
|
|
608
|
+
}
|
|
609
|
+
toGEOJSON() {
|
|
590
610
|
return {
|
|
591
611
|
type: 'Feature',
|
|
592
612
|
geometry: {
|
|
593
613
|
type: 'Polygon',
|
|
594
|
-
coordinates: [this.allVertex().concat(this.getVertex(0)).map(
|
|
614
|
+
coordinates: [this.allVertex().concat(this.getVertex(0)).map(v => [parseFloat(v.lngDegrees.toFixed(5)), parseFloat(v.latDegrees.toFixed(5))])],
|
|
595
615
|
},
|
|
596
616
|
properties: {}
|
|
597
617
|
};
|
|
598
|
-
}
|
|
599
|
-
|
|
618
|
+
}
|
|
619
|
+
toString() {
|
|
600
620
|
return "[Lo=" + this.lo().toString() + ", Hi=" + this.hi().toString() + "]";
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
}());
|
|
621
|
+
}
|
|
622
|
+
}
|
|
604
623
|
exports.S2LatLngRect = S2LatLngRect;
|
|
605
624
|
//# sourceMappingURL=S2LatLngRect.js.map
|