• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ngageoint / geopackage-js / 4078143969

pending completion
4078143969

push

github

Christopher Caldwell
bump version

3593 of 8015 branches covered (44.83%)

Branch coverage included in aggregate %.

15102 of 20471 relevant lines covered (73.77%)

1564.55 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

53.17
/lib/boundingBox.ts
1
import { Projection, ProjectionConstants, Projections, ProjectionTransform } from '@ngageoint/projections-js';
1✔
2
import { Geometry, GeometryEnvelope, GeometryUtils, Point } from '@ngageoint/simple-features-js';
1✔
3
import { GeometryTransform } from '@ngageoint/simple-features-proj-js';
1✔
4
import { Feature } from 'geojson';
5

6
/**
7
 * Bounding Box with longitude and latitude ranges in degrees
8
 */
9
export class BoundingBox {
1✔
10
  /**
11
   * Min longitude in degrees
12
   */
13
  private minLongitude: number;
14

15
  /**
16
   * Max longitude in degrees
17
   */
18
  private maxLongitude: number;
19

20
  /**
21
   * Min latitude in degrees
22
   */
23
  private minLatitude: number;
24

25
  /**
26
   * Max latitude in degrees
27
   */
28
  private maxLatitude: number;
29

30
  /**
31
   * Create a new WGS84 bounding box with world bounds (degrees)
32
   *
33
   * @return new bounding box
34
   */
35
  public static worldWGS84(): BoundingBox {
1✔
36
    return new BoundingBox();
1✔
37
  }
38

39
  /**
40
   * Create a new Web Mercator bounding box with world bounds (meters)
41
   *
42
   * @return new bounding box
43
   */
44
  public static worldWebMercator(): BoundingBox {
1✔
45
    return new BoundingBox(
1✔
46
      -ProjectionConstants.WEB_MERCATOR_HALF_WORLD_WIDTH,
47
      -ProjectionConstants.WEB_MERCATOR_HALF_WORLD_WIDTH,
48
      ProjectionConstants.WEB_MERCATOR_HALF_WORLD_WIDTH,
49
      ProjectionConstants.WEB_MERCATOR_HALF_WORLD_WIDTH,
50
    );
51
  }
52

53
  /**
54
   * Constructor
55
   */
56
  public constructor();
57

58
  /**
59
   * Constructor
60
   * @param boundingBox
61
   */
62
  public constructor(boundingBox: BoundingBox);
63

64
  /**
65
   * Constructor
66
   * @param envelope
67
   */
68
  public constructor(envelope: GeometryEnvelope);
69

70
  /**
71
   * Constructor
72
   * @param geometry
73
   */
74
  public constructor(geometry: Geometry);
75

76
  /**
77
   * Constructor
78
   * @param minLongitude
79
   * @param minLatitude
80
   * @param maxLongitude
81
   * @param maxLatitude
82
   */
83
  public constructor(minLongitude: number, minLatitude: number, maxLongitude: number, maxLatitude: number);
84

85
  /**
86
   * Constructor
87
   * @param args
88
   */
89
  public constructor(...args) {
61,529✔
90
    if (args.length === 0) {
13,913✔
91
      this.minLongitude = -ProjectionConstants.WGS84_HALF_WORLD_LON_WIDTH;
1✔
92
      this.minLatitude = -ProjectionConstants.WGS84_HALF_WORLD_LAT_HEIGHT;
1✔
93
      this.maxLongitude = ProjectionConstants.WGS84_HALF_WORLD_LON_WIDTH;
1✔
94
      this.maxLatitude = ProjectionConstants.WGS84_HALF_WORLD_LAT_HEIGHT;
1✔
95
    } else if (args.length === 1) {
13,912✔
96
      if (args[0] instanceof BoundingBox) {
7,315✔
97
        this.minLongitude = args[0].getMinLongitude();
3,916✔
98
        this.minLatitude = args[0].getMinLatitude();
3,916✔
99
        this.maxLongitude = args[0].getMaxLongitude();
3,916✔
100
        this.maxLatitude = args[0].getMaxLatitude();
3,916✔
101
      } else if (args[0] instanceof GeometryEnvelope) {
3,399!
102
        this.minLongitude = args[0].minX;
3,399✔
103
        this.minLatitude = args[0].minY;
3,399✔
104
        this.maxLongitude = args[0].maxX;
3,399✔
105
        this.maxLatitude = args[0].maxY;
3,399✔
106
      } else if (args[0] instanceof Geometry) {
×
107
        const envelope = args[0].getEnvelope();
×
108
        this.minLongitude = envelope.minX;
×
109
        this.minLatitude = envelope.minY;
×
110
        this.maxLongitude = envelope.maxX;
×
111
        this.maxLatitude = envelope.maxY;
×
112
      }
113
    } else if (args.length === 4) {
6,597!
114
      this.minLongitude = args[0];
6,597✔
115
      this.minLatitude = args[1];
6,597✔
116
      this.maxLongitude = args[2];
6,597✔
117
      this.maxLatitude = args[3];
6,597✔
118
    }
119
  }
120

121
  /**
122
   * Get the min longitude
123
   *
124
   * @return min longitude
125
   */
126
  public getMinLongitude(): number {
1✔
127
    return this.minLongitude;
1,211,931✔
128
  }
129

130
  /**
131
   * Set the min longitude
132
   *
133
   * @param minLongitude
134
   *            min longitude
135
   */
136
  public setMinLongitude(minLongitude: number): void {
1✔
137
    this.minLongitude = minLongitude;
63✔
138
  }
139

140
  /**
141
   * Get the max longitude
142
   *
143
   * @return max longitude
144
   */
145
  public getMaxLongitude(): number {
1✔
146
    return this.maxLongitude;
24,457✔
147
  }
148

149
  /**
150
   * Set the max longitude
151
   *
152
   * @param maxLongitude
153
   *            max longitude
154
   */
155
  public setMaxLongitude(maxLongitude: number): void {
1✔
156
    this.maxLongitude = maxLongitude;
63✔
157
  }
158

159
  /**
160
   * Get the min latitude
161
   *
162
   * @return min latitude
163
   */
164
  public getMinLatitude(): number {
1✔
165
    return this.minLatitude;
24,775✔
166
  }
167

168
  /**
169
   * Set the min latitude
170
   *
171
   * @param minLatitude
172
   *            min latitude
173
   */
174
  public setMinLatitude(minLatitude: number): void {
1✔
175
    this.minLatitude = minLatitude;
77✔
176
  }
177

178
  /**
179
   * Get the max latitude
180
   *
181
   * @return max latitude
182
   */
183
  public getMaxLatitude(): number {
1✔
184
    return this.maxLatitude;
1,212,249✔
185
  }
186

187
  /**
188
   * Set the max latitude
189
   *
190
   * @param maxLatitude
191
   *            max latitude
192
   */
193
  public setMaxLatitude(maxLatitude: number): void {
1✔
194
    this.maxLatitude = maxLatitude;
75✔
195
  }
196

197
  /**
198
   * Get the longitude range
199
   *
200
   * @return longitude range
201
   */
202
  public getLongitudeRange(): number {
1✔
203
    return this.getMaxLongitude() - this.getMinLongitude();
147✔
204
  }
205

206
  /**
207
   * Get the latitude range
208
   *
209
   * @return latitude range
210
   */
211
  public getLatitudeRange(): number {
1✔
212
    return this.getMaxLatitude() - this.getMinLatitude();
139✔
213
  }
214

215
  /**
216
   * Get the bounding box centroid point
217
   *
218
   * @return centroid point
219
   */
220
  public getCentroid(): Point {
1✔
221
    return BoundingBox.getCentroidForBoundingBox(this);
1✔
222
  }
223

224
  /**
225
   * Get the bounding box centroid point
226
   *
227
   * @param boundingBox
228
   *            bounding box
229
   *
230
   * @return centroid point
231
   */
232
  public static getCentroidForBoundingBox(boundingBox: BoundingBox): Point {
1✔
233
    const x = (boundingBox.getMinLongitude() + boundingBox.getMaxLongitude()) / 2.0;
2✔
234
    const y = (boundingBox.getMinLatitude() + boundingBox.getMaxLatitude()) / 2.0;
2✔
235
    return new Point(x, y);
2✔
236
  }
237

238
  /**
239
   * Get the centroid for the bounding box and projection
240
   *
241
   * @param projection
242
   *            projection of the bounding box
243
   * @return centroid point
244
   */
245
  public getCentroidInProjection(projection: Projection): Point {
1✔
246
    return BoundingBox.getCentroidForBoundingBoxInProjection(this, projection);
2✔
247
  }
248

249
  /**
250
   * Get the centroid for the bounding box and projection
251
   *
252
   * @param boundingBox
253
   *            bounding box
254
   * @param projection
255
   *            projection of the bounding box
256
   * @return centroid point
257
   */
258
  public static getCentroidForBoundingBoxInProjection(boundingBox: BoundingBox, projection: Projection): Point {
1✔
259
    let centroid;
260
    if (Projections.getUnits(projection.toString()) === 'degrees') {
2✔
261
      centroid = BoundingBox.getDegreesCentroidForBoundingBox(boundingBox);
1✔
262
    } else {
263
      centroid = BoundingBox.getCentroidForBoundingBox(boundingBox);
1✔
264
    }
265
    return centroid;
2✔
266
  }
267

268
  /**
269
   * Get the centroid for the bounding box in degrees
270
   *
271
   * @return centroid point
272
   */
273
  public getDegreesCentroid(): Point {
1✔
274
    return BoundingBox.getDegreesCentroidForBoundingBox(this);
1✔
275
  }
276

277
  /**
278
   * Get the centroid for a bounding box in degrees
279
   *
280
   * @param boundingBox
281
   *            bounding box in degrees
282
   * @return centroid point
283
   */
284
  public static getDegreesCentroidForBoundingBox(boundingBox: BoundingBox): Point {
1✔
285
    return GeometryUtils.getDegreesCentroid(BoundingBox.buildGeometryFromBoundingBox(boundingBox));
2✔
286
  }
287

288
  /**
289
   * Build a Geometry Envelope from the bounding box
290
   *
291
   * @return geometry envelope
292
   */
293
  public buildEnvelope(): GeometryEnvelope {
1✔
294
    return BoundingBox.buildEnvelopeFromBoundingBox(this);
3,492✔
295
  }
296

297
  /**
298
   * Build a Geometry Envelope from the bounding box
299
   * @param boundingBox bounding box
300
   * @return geometry envelope
301
   */
302
  public static buildEnvelopeFromBoundingBox(boundingBox: BoundingBox): GeometryEnvelope {
1✔
303
    return new GeometryEnvelope(
3,496✔
304
      boundingBox.minLongitude,
305
      boundingBox.minLatitude,
306
      boundingBox.maxLongitude,
307
      boundingBox.maxLatitude,
308
    );
309
  }
310

311
  /**
312
   * Build a geometry representation of the bounding box
313
   * @return geometry, polygon or point
314
   */
315
  public buildGeometry(): Geometry {
1✔
316
    return BoundingBox.buildGeometryFromBoundingBox(this);
2✔
317
  }
318

319
  /**
320
   * Build a geometry representation of the bounding box
321
   *
322
   * @param boundingBox
323
   *            bounding box
324
   *
325
   * @return geometry, polygon or point
326
   */
327
  public static buildGeometryFromBoundingBox(boundingBox: BoundingBox): Geometry {
1✔
328
    return BoundingBox.buildEnvelopeFromBoundingBox(boundingBox).buildGeometry();
4✔
329
  }
330

331
  /**
332
   * If the bounding box spans the Anti-Meridian, attempt to get a
333
   * complementary bounding box using the max longitude of the unit projection
334
   *
335
   * @param maxProjectionLongitude
336
   *            max longitude of the world for the current bounding box units
337
   *
338
   * @return complementary bounding box or nil if none
339
   */
340
  public complementary(maxProjectionLongitude: number): BoundingBox {
1✔
341
    let complementary = null;
×
342

343
    let adjust = null;
×
344

345
    if (this.maxLongitude > maxProjectionLongitude) {
×
346
      if (this.minLongitude >= -maxProjectionLongitude) {
×
347
        adjust = -2 * maxProjectionLongitude;
×
348
      }
349
    } else if (this.minLongitude < -maxProjectionLongitude) {
×
350
      if (this.maxLongitude <= maxProjectionLongitude) {
×
351
        adjust = 2 * maxProjectionLongitude;
×
352
      }
353
    }
354

355
    if (adjust != null) {
×
356
      complementary = this.copy();
×
357
      complementary.setMinLongitude(complementary.getMinLongitude() + adjust);
×
358
      complementary.setMaxLongitude(complementary.getMaxLongitude() + adjust);
×
359
    }
360

361
    return complementary;
×
362
  }
363

364
  /**
365
   * If the bounding box spans the Anti-Meridian, attempt to get a
366
   * complementary WGS84 bounding box
367
   *
368
   * @return complementary bounding box or nil if none
369
   */
370
  public complementaryWgs84(): BoundingBox {
1✔
371
    return this.complementary(ProjectionConstants.WGS84_HALF_WORLD_LON_WIDTH);
×
372
  }
373

374
  /**
375
   * If the bounding box spans the Anti-Meridian, attempt to get a
376
   * complementary Web Mercator bounding box
377
   *
378
   * @return complementary bounding box or nil if none
379
   */
380
  public complementaryWebMercator(): BoundingBox {
1✔
381
    return this.complementary(ProjectionConstants.WEB_MERCATOR_HALF_WORLD_WIDTH);
×
382
  }
383

384
  /**
385
   * Bound the bounding box longitudes within the min and max possible
386
   * projection values. This may result in a max longitude numerically lower
387
   * than the min longitude.
388
   *
389
   * @param maxProjectionLongitude
390
   *            max longitude of the world for the current bounding box units
391
   * @return bounded bounding box
392
   */
393
  public boundCoordinates(maxProjectionLongitude: number): BoundingBox {
1✔
394
    const bounded = this.copy();
×
395

396
    const minLongitude =
397
      ((this.getMinLongitude() + maxProjectionLongitude) % (2 * maxProjectionLongitude)) - maxProjectionLongitude;
×
398
    const maxLongitude =
399
      ((this.getMaxLongitude() + maxProjectionLongitude) % (2 * maxProjectionLongitude)) - maxProjectionLongitude;
×
400

401
    bounded.setMinLongitude(minLongitude);
×
402
    bounded.setMaxLongitude(maxLongitude);
×
403

404
    return bounded;
×
405
  }
406

407
  /**
408
   * Bound the bounding box coordinates within WGS84 range values
409
   *
410
   * @return bounded bounding box
411
   */
412
  public boundWgs84Coordinates(): BoundingBox {
1✔
413
    return this.boundCoordinates(ProjectionConstants.WGS84_HALF_WORLD_LON_WIDTH);
×
414
  }
415

416
  /**
417
   * Bound the bounding box coordinates within Web Mercator range values
418
   *
419
   * @return bounded bounding box
420
   */
421
  public boundWebMercatorCoordinates(): BoundingBox {
1✔
422
    return this.boundCoordinates(ProjectionConstants.WEB_MERCATOR_HALF_WORLD_WIDTH);
×
423
  }
424

425
  /**
426
   * Expand the bounding box max longitude above the max possible projection
427
   * value if needed to create a bounding box where the max longitude is
428
   * numerically larger than the min longitude.
429
   *
430
   * @param maxProjectionLongitude
431
   *            max longitude of the world for the current bounding box units
432
   * @return expanded bounding box
433
   */
434
  public expandCoordinates(maxProjectionLongitude: number): BoundingBox {
1✔
435
    const expanded = this.copy();
×
436

437
    const minLongitude = this.getMinLongitude();
×
438
    let maxLongitude = this.getMaxLongitude();
×
439

440
    if (minLongitude > maxLongitude) {
×
441
      const worldWraps = 1 + Math.round((minLongitude - maxLongitude) / (2 * maxProjectionLongitude));
×
442
      maxLongitude += worldWraps * 2 * maxProjectionLongitude;
×
443
      expanded.setMaxLongitude(maxLongitude);
×
444
    }
445

446
    return expanded;
×
447
  }
448

449
  /**
450
   * Expand the bounding box max longitude above the max WGS84 projection
451
   * value if needed to create a bounding box where the max longitude is
452
   * numerically larger than the min longitude.
453
   *
454
   * @return expanded bounding box
455
   */
456
  public expandWgs84Coordinates(): BoundingBox {
1✔
457
    return this.expandCoordinates(ProjectionConstants.WGS84_HALF_WORLD_LON_WIDTH);
×
458
  }
459

460
  /**
461
   * Expand the bounding box max longitude above the max Web Mercator
462
   * projection value if needed to create a bounding box where the max
463
   * longitude is numerically larger than the min longitude.
464
   *
465
   * @return expanded bounding box
466
   */
467
  public expandWebMercatorCoordinates(): BoundingBox {
1✔
468
    return this.expandCoordinates(ProjectionConstants.WEB_MERCATOR_HALF_WORLD_WIDTH);
×
469
  }
470

471
  /**
472
   * Transform the bounding box using the provided projection transform
473
   *
474
   * @param transform
475
   *            geometry transform
476
   * @return transformed bounding box
477
   */
478
  public transform(transform: GeometryTransform | ProjectionTransform): BoundingBox {
1✔
479
    const geometryTransform = new GeometryTransform(transform.getFromProjection(), transform.getToProjection());
3,623✔
480
    let transformed: BoundingBox = this.copy();
3,623✔
481
    if (!transform.getFromProjection().equalsProjection(transform.getToProjection())) {
3,623✔
482
      if (
1,288✔
483
        Projections.getUnits(geometryTransform.getFromProjection().toString()) === 'degrees' &&
1,449✔
484
        geometryTransform
485
          .getToProjection()
486
          .equals(ProjectionConstants.AUTHORITY_EPSG, ProjectionConstants.EPSG_WEB_MERCATOR.toString())
487
      ) {
488
        transformed = BoundingBox.boundDegreesBoundingBoxWithWebMercatorLimits(transformed);
161✔
489
      }
490
      const envelope = BoundingBox.buildEnvelope(transformed);
1,288✔
491
      const transformedEnvelope = geometryTransform.transformEnvelope(envelope);
1,288✔
492
      transformed = new BoundingBox(
1,288✔
493
        transformedEnvelope.minX,
494
        transformedEnvelope.minY,
495
        transformedEnvelope.maxX,
496
        transformedEnvelope.maxY,
497
      );
498
    }
499
    return transformed;
3,623✔
500
  }
501

502
  /**
503
   * Build an envelope from a bounding box
504
   * @param boundingBox
505
   */
506
  public static buildEnvelope(boundingBox: BoundingBox): GeometryEnvelope {
1✔
507
    const envelope = new GeometryEnvelope();
1,288✔
508
    envelope.minX = boundingBox.getMinLongitude();
1,288✔
509
    envelope.maxX = boundingBox.getMaxLongitude();
1,288✔
510
    envelope.minY = boundingBox.getMinLatitude();
1,288✔
511
    envelope.maxY = boundingBox.getMaxLatitude();
1,288✔
512
    return envelope;
1,288✔
513
  }
514

515
  /**
516
   * Determine if intersects with the provided bounding box
517
   *
518
   * @param boundingBox
519
   *            bounding box
520
   * @param allowEmpty
521
   *            allow empty ranges when determining intersection
522
   *
523
   * @return true if intersects
524
   */
525
  public intersects(boundingBox: BoundingBox, allowEmpty = false): boolean {
3,639✔
526
    return this.overlap(boundingBox, allowEmpty) != null;
3,519✔
527
  }
528

529
  /**
530
   * Get the overlapping bounding box with the provided bounding box
531
   * @param boundingBox bounding box
532
   * @param allowEmpty allow empty ranges when determining overlap
533
   *
534
   * @return bounding box
535
   */
536
  public overlap(boundingBox: BoundingBox, allowEmpty = false): BoundingBox {
4,183✔
537
    const minLongitude = Math.max(this.getMinLongitude(), boundingBox.getMinLongitude());
3,851✔
538
    const maxLongitude = Math.min(this.getMaxLongitude(), boundingBox.getMaxLongitude());
3,851✔
539
    const minLatitude = Math.max(this.getMinLatitude(), boundingBox.getMinLatitude());
3,851✔
540
    const maxLatitude = Math.min(this.getMaxLatitude(), boundingBox.getMaxLatitude());
3,851✔
541

542
    let overlap = null;
3,851✔
543

544
    if (
3,851✔
545
      (minLongitude < maxLongitude && minLatitude < maxLatitude) ||
9,060✔
546
      (allowEmpty && minLongitude <= maxLongitude && minLatitude <= maxLatitude)
547
    ) {
548
      overlap = new BoundingBox(minLongitude, minLatitude, maxLongitude, maxLatitude);
2,549✔
549
    }
550

551
    return overlap;
3,851✔
552
  }
553

554
  /**
555
   * Get the union bounding box with the provided bounding box
556
   * @param boundingBox bounding box
557
   * @return bounding box
558
   */
559
  public union(boundingBox: BoundingBox): BoundingBox {
1✔
560
    const minLongitude = Math.min(this.getMinLongitude(), boundingBox.getMinLongitude());
×
561
    const maxLongitude = Math.max(this.getMaxLongitude(), boundingBox.getMaxLongitude());
×
562
    const minLatitude = Math.min(this.getMinLatitude(), boundingBox.getMinLatitude());
×
563
    const maxLatitude = Math.max(this.getMaxLatitude(), boundingBox.getMaxLatitude());
×
564

565
    let union = null;
×
566

567
    if (minLongitude < maxLongitude && minLatitude < maxLatitude) {
×
568
      union = new BoundingBox(minLongitude, minLatitude, maxLongitude, maxLatitude);
×
569
    }
570

571
    return union;
×
572
  }
573

574
  /**
575
   * Determine if inclusively contains the provided bounding box
576
   *
577
   * @param boundingBox
578
   *            bounding box
579
   * @return true if contains
580
   */
581
  public contains(boundingBox: BoundingBox): boolean {
1✔
582
    return (
×
583
      this.getMinLongitude() <= boundingBox.getMinLongitude() &&
×
584
      this.getMaxLongitude() >= boundingBox.getMaxLongitude() &&
585
      this.getMinLatitude() <= boundingBox.getMinLatitude() &&
586
      this.getMaxLatitude() >= boundingBox.getMaxLatitude()
587
    );
588
  }
589

590
  /**
591
   * Function taken from https://gist.github.com/Yaffle/4654250
592
   * @param x
593
   * @private
594
   */
595
  private static nextUp(x: number): number {
1✔
596
    const EPSILON = Math.pow(2, -52);
×
597
    const MIN_VALUE = Math.pow(2, -1022);
×
598
    if (x !== x) return x;
×
599
    if (x === -1 / 0) return -Number.MAX_VALUE;
×
600
    if (x === 1 / 0) return +1 / 0;
×
601
    if (x === Number.MAX_VALUE) return +1 / 0;
×
602
    let y = x * (x < 0 ? 1 - EPSILON / 2 : 1 + EPSILON);
×
603
    if (y === x) y = MIN_VALUE * EPSILON > 0 ? x + MIN_VALUE * EPSILON : x + MIN_VALUE;
×
604
    if (y === +1 / 0) y = +Number.MAX_VALUE;
×
605
    const b = x + (y - x) / 2;
×
606
    if (x < b && b < y) y = b;
×
607
    const c = (y + x) / 2;
×
608
    if (x < c && c < y) y = c;
×
609
    return y === 0 ? -0 : y;
×
610
  }
611

612
  /**
613
   * Function taken from https://gist.github.com/Yaffle/4654250
614
   * @param x
615
   * @private
616
   */
617
  private static ulp(x): number {
1✔
618
    return x < 0 ? BoundingBox.nextUp(x) - x : x - -BoundingBox.nextUp(-x);
×
619
  }
620

621
  /**
622
   * Expand the bounding box to an equally sized width and height bounding box
623
   * with optional empty edge buffer
624
   *
625
   * @param bufferPercentage
626
   *            bounding box edge buffer percentage. A value of 0.1 adds a 10%
627
   *            buffer on each side of the squared bounding box.
628
   * @return new square expanded bounding box
629
   */
630
  public squareExpand(bufferPercentage = 0.0): BoundingBox {
1!
631
    const boundingBox: BoundingBox = this.copy();
×
632
    if (boundingBox.isPoint() && bufferPercentage > 0.0) {
×
633
      const longitudeExpand = BoundingBox.ulp(boundingBox.getMinLongitude());
×
634
      boundingBox.setMinLongitude(boundingBox.getMinLongitude() - longitudeExpand);
×
635
      boundingBox.setMaxLongitude(boundingBox.getMaxLongitude() + longitudeExpand);
×
636

637
      const latitudeExpand = BoundingBox.ulp(boundingBox.getMinLatitude());
×
638
      boundingBox.setMinLatitude(boundingBox.getMinLatitude() - latitudeExpand);
×
639
      boundingBox.setMaxLatitude(boundingBox.getMaxLatitude() + latitudeExpand);
×
640
    }
641

642
    const lonRange = boundingBox.getLongitudeRange();
×
643
    const latRange = boundingBox.getLatitudeRange();
×
644

645
    if (lonRange < latRange) {
×
646
      const halfDiff = (latRange - lonRange) / 2.0;
×
647
      boundingBox.setMinLongitude(boundingBox.getMinLongitude() - halfDiff);
×
648
      boundingBox.setMaxLongitude(boundingBox.getMaxLongitude() + halfDiff);
×
649
    } else if (latRange < lonRange) {
×
650
      const halfDiff = (lonRange - latRange) / 2.0;
×
651
      boundingBox.setMinLatitude(boundingBox.getMinLatitude() - halfDiff);
×
652
      boundingBox.setMaxLatitude(boundingBox.getMaxLatitude() + halfDiff);
×
653
    }
654

655
    const range = Math.max(Math.max(lonRange, latRange), Number.MIN_VALUE);
×
656
    const buffer = (range / (1.0 - 2.0 * bufferPercentage) - range) / 2.0;
×
657

658
    boundingBox.setMinLongitude(boundingBox.getMinLongitude() - buffer);
×
659
    boundingBox.setMinLatitude(boundingBox.getMinLatitude() - buffer);
×
660
    boundingBox.setMaxLongitude(boundingBox.getMaxLongitude() + buffer);
×
661
    boundingBox.setMaxLatitude(boundingBox.getMaxLatitude() + buffer);
×
662

663
    return boundingBox;
×
664
  }
665

666
  /**
667
   * Determine if the bounding box is of a single point
668
   *
669
   * @return true if a single point bounds
670
   */
671
  public isPoint(): boolean {
1✔
672
    return this.minLongitude === this.maxLongitude && this.minLatitude === this.maxLatitude;
×
673
  }
674

675
  /**
676
   * Copy the bounding box
677
   *
678
   * @return bounding box copy
679
   */
680
  public copy(): BoundingBox {
1✔
681
    return new BoundingBox(this);
3,915✔
682
  }
683

684
  /**
685
   * Checks if bounding boxes are equal
686
   */
687
  public equals(obj: BoundingBox): boolean {
1✔
688
    return (
3✔
689
      obj != null &&
11✔
690
      this.minLongitude === obj.minLongitude &&
691
      this.minLatitude === obj.minLatitude &&
692
      this.maxLongitude === obj.maxLongitude &&
693
      this.maxLatitude === obj.maxLatitude
694
    );
695
  }
696

697
  /**
698
   * Bound the upper and lower bounds of the degrees bounding box with web
699
   * mercator limits
700
   *
701
   * @param boundingBox degrees bounding box
702
   * @return bounding box
703
   */
704
  public static boundDegreesBoundingBoxWithWebMercatorLimits(boundingBox: BoundingBox): BoundingBox {
1✔
705
    const bounded = boundingBox.copy();
161✔
706
    if (bounded.getMinLatitude() < ProjectionConstants.WEB_MERCATOR_MIN_LAT_RANGE) {
161✔
707
      bounded.setMinLatitude(ProjectionConstants.WEB_MERCATOR_MIN_LAT_RANGE);
13✔
708
    }
709
    if (bounded.getMaxLatitude() < ProjectionConstants.WEB_MERCATOR_MIN_LAT_RANGE) {
161!
710
      bounded.setMaxLatitude(ProjectionConstants.WEB_MERCATOR_MIN_LAT_RANGE);
×
711
    }
712
    if (bounded.getMaxLatitude() > ProjectionConstants.WEB_MERCATOR_MAX_LAT_RANGE) {
161✔
713
      bounded.setMaxLatitude(ProjectionConstants.WEB_MERCATOR_MAX_LAT_RANGE);
12✔
714
    }
715
    if (bounded.getMinLatitude() > ProjectionConstants.WEB_MERCATOR_MAX_LAT_RANGE) {
161✔
716
      bounded.setMinLatitude(ProjectionConstants.WEB_MERCATOR_MAX_LAT_RANGE);
1✔
717
    }
718
    return bounded;
161✔
719
  }
720

721
  /**
722
   * Project bounding box
723
   * @param from
724
   * @param to
725
   */
726
  public projectBoundingBox(from: Projection, to: Projection): BoundingBox {
1✔
727
    return this.transform(new ProjectionTransform(from, to));
13✔
728
  }
729

730
  /**
731
   * Returns a GeoJSON representation of this bounding box
732
   */
733
  public toGeoJSON(): Feature {
1✔
734
    return {
1✔
735
      type: 'Feature',
736
      properties: {},
737
      geometry: {
738
        type: 'Polygon',
739
        coordinates: [
740
          [
741
            [this.minLongitude, this.minLatitude],
742
            [this.maxLongitude, this.minLatitude],
743
            [this.maxLongitude, this.maxLatitude],
744
            [this.minLongitude, this.maxLatitude],
745
            [this.minLongitude, this.minLatitude],
746
          ],
747
        ],
748
      },
749
    };
750
  }
751
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc