@wemap/geo 7.4.0 → 8.0.0-alpha.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.
@@ -349,9 +349,30 @@ class Level {
349
349
  */
350
350
  class Coordinates {
351
351
 
352
+ /** @type {Number} */
353
+ _lat;
354
+
355
+ /** @type {Number} */
356
+ _lng;
357
+
358
+ /** @type {Number|null} */
359
+ _alt = null;
360
+
361
+ /** @type {Level|null} */
362
+ _level = null;
363
+
364
+ /** @type {[Number, Number, Number]|null} */
365
+ _ecef;
366
+
352
367
  autoWrap = true;
353
368
 
354
- constructor(lat, lng, alt, level) {
369
+ /**
370
+ * @param {Number} lat
371
+ * @param {Number} lng
372
+ * @param {?(Number|null)} alt
373
+ * @param {?(Level|null)} level
374
+ */
375
+ constructor(lat, lng, alt = null, level = null) {
355
376
  this.lat = lat;
356
377
  this.lng = lng;
357
378
  this.alt = alt;
@@ -359,23 +380,28 @@ class Coordinates {
359
380
  this._ecef = null;
360
381
  }
361
382
 
383
+ /** @type {Number} */
362
384
  get lat() {
363
385
  return this._lat;
364
386
  }
365
387
 
388
+ /** @type {Number} */
366
389
  get latitude() {
367
390
  return this._lat;
368
391
  }
369
392
 
393
+ /** @type {Number} */
370
394
  get lng() {
371
395
  return this._lng;
372
396
  }
373
397
 
398
+ /** @type {Number} */
374
399
  get longitude() {
375
400
  return this._lng;
376
401
  }
377
402
 
378
403
  /**
404
+ * @type {Number|null}
379
405
  * alt does not denote the altitude of a point but its height from
380
406
  * the "level" field (if defined) or from the ground
381
407
  */
@@ -383,10 +409,12 @@ class Coordinates {
383
409
  return this._alt;
384
410
  }
385
411
 
412
+ /** @type {Level|null} */
386
413
  get level() {
387
414
  return this._level;
388
415
  }
389
416
 
417
+ /** @type {Number} */
390
418
  set lat(lat) {
391
419
  if (typeof lat === 'number' && Math.abs(lat) <= 90) {
392
420
  this._lat = lat;
@@ -396,10 +424,12 @@ class Coordinates {
396
424
  this._ecef = null;
397
425
  }
398
426
 
399
- set latitude(lat) {
427
+ /** @type {Number} */
428
+ set latitude(_) {
400
429
  throw new Error('Please use Coordinate#lat setter instead of Coordinate#latitude');
401
430
  }
402
431
 
432
+ /** @type {Number} */
403
433
  set lng(lng) {
404
434
  if (typeof lng === 'number') {
405
435
  this._lng = lng;
@@ -412,10 +442,12 @@ class Coordinates {
412
442
  this._ecef = null;
413
443
  }
414
444
 
415
- set longitude(lng) {
445
+ /** @type {Number} */
446
+ set longitude(_) {
416
447
  throw new Error('Please use Coordinate#lng setter instead of Coordinate#longitude');
417
448
  }
418
449
 
450
+ /** @type {Number|null} */
419
451
  set alt(alt) {
420
452
  if (typeof alt === 'number') {
421
453
  this._alt = alt;
@@ -428,6 +460,7 @@ class Coordinates {
428
460
  this._ecef = null;
429
461
  }
430
462
 
463
+ /** @type {Level|null} */
431
464
  set level(level) {
432
465
  if (level instanceof Level) {
433
466
  this._level = level;
@@ -439,6 +472,10 @@ class Coordinates {
439
472
  }
440
473
  }
441
474
 
475
+ /**
476
+ * Deep clone coordinates
477
+ * @returns {!Coordinates}
478
+ */
442
479
  clone() {
443
480
  const output = new Coordinates(this.lat, this.lng, this.alt);
444
481
  if (this.level) {
@@ -479,17 +516,35 @@ class Coordinates {
479
516
  && Level.equalsTo(pos1.level, pos2.level);
480
517
  }
481
518
 
519
+ /**
520
+ * @param {Coordinates} other
521
+ * @returns {!Boolean}
522
+ */
482
523
  equalsTo(other) {
483
524
  return Coordinates.equalsTo(this, other);
484
525
  }
485
526
 
527
+ /**
528
+ * @param {!Number} distance
529
+ * @param {!Number} bearing
530
+ * @param {?Number} elevation
531
+ * @returns {!Coordinates}
532
+ * @throws {Error} if elevation is defined and point altitude is not defined
533
+ */
486
534
  destinationPoint(distance, bearing, elevation) {
487
535
  const newPoint = this.clone();
488
536
  newPoint.move(distance, bearing, elevation);
489
537
  return newPoint;
490
538
  }
491
539
 
492
- // Source: http://www.movable-type.co.uk/scripts/latlong.html#destPoint
540
+ /**
541
+ * Source: http://www.movable-type.co.uk/scripts/latlong.html#destPoint
542
+ * @param {!Number} distance
543
+ * @param {!Number} bearing
544
+ * @param {?Number} elevation
545
+ * @returns {!Coordinates}
546
+ * @throws {Error} if elevation is defined and point altitude is not defined
547
+ */
493
548
  move(distance, bearing, elevation) {
494
549
 
495
550
  const dR = distance / Constants.R_MAJOR;
@@ -518,7 +573,6 @@ class Coordinates {
518
573
  this.alt += elevation;
519
574
  }
520
575
 
521
-
522
576
  return this;
523
577
  }
524
578
 
@@ -553,10 +607,19 @@ class Coordinates {
553
607
  return Constants.R_MAJOR * cosn;
554
608
  }
555
609
 
610
+ /**
611
+ * @param {!Coordinates} point1
612
+ * @param {!Coordinates} point2
613
+ * @returns {!Number}
614
+ */
556
615
  static distanceBetween(point1, point2) {
557
616
  return point1.distanceTo(point2);
558
617
  }
559
618
 
619
+ /**
620
+ * @param {!Coordinates} location2
621
+ * @returns {!Number}
622
+ */
560
623
  bearingTo(location2) {
561
624
  const lat1 = deg2rad(this.lat);
562
625
  const lat2 = deg2rad(location2.lat);
@@ -566,6 +629,11 @@ class Coordinates {
566
629
  Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(diffLng));
567
630
  }
568
631
 
632
+ /**
633
+ * @param {!Coordinates} point1
634
+ * @param {!Coordinates} point2
635
+ * @returns {!Number}
636
+ */
569
637
  static bearingTo(point1, point2) {
570
638
  return point1.bearingTo(point2);
571
639
  }
@@ -577,20 +645,25 @@ class Coordinates {
577
645
  * as a sphere instead of an ellipse
578
646
  */
579
647
 
648
+ /** @type {![Number, Number, Number, Number]} */
580
649
  get enuToEcefRotation() {
581
650
  const rot1 = Quaternion.fromAxisAngle([0, 0, 1], Math.PI / 2 + deg2rad(this.lng));
582
651
  const rot2 = Quaternion.fromAxisAngle([1, 0, 0], Math.PI / 2 - deg2rad(this.lat));
583
652
  return Quaternion.multiply(rot1, rot2);
584
653
  }
585
654
 
655
+ /** @type {![Number, Number, Number, Number]} */
586
656
  get ecefToEnuRotation() {
587
657
  const rot1 = Quaternion.fromAxisAngle([1, 0, 0], deg2rad(this.lat) - Math.PI / 2);
588
658
  const rot2 = Quaternion.fromAxisAngle([0, 0, 1], -deg2rad(this.lng) - Math.PI / 2);
589
659
  return Quaternion.multiply(rot1, rot2);
590
660
  }
591
661
 
592
- // https://gist.github.com/klucar/1536194
593
- // Adapted for spherical formula
662
+ /**
663
+ * https://gist.github.com/klucar/1536194
664
+ * Adapted for spherical formula
665
+ * @type {![Number, Number, Number]}
666
+ */
594
667
  get ecef() {
595
668
 
596
669
  if (!this._ecef) {
@@ -608,6 +681,10 @@ class Coordinates {
608
681
  return this._ecef;
609
682
  }
610
683
 
684
+ /**
685
+ * @param {![Number, Number, Number]} ecef
686
+ * @returns {!Coordinates}
687
+ */
611
688
  static fromECEF(ecef) {
612
689
 
613
690
  const x = ecef[0];
@@ -628,8 +705,13 @@ class Coordinates {
628
705
  }
629
706
 
630
707
 
631
- // https://stackoverflow.com/questions/1299567/how-to-calculate-distance-from-a-point-to-a-line-segment-on-a-sphere
632
- // Adapted to ECEF
708
+ /**
709
+ * https://stackoverflow.com/questions/1299567/how-to-calculate-distance-from-a-point-to-a-line-segment-on-a-sphere
710
+ * Adapted to ECEF
711
+ * @param {!Coordinates} p1
712
+ * @param {!Coordinates} p2
713
+ * @returns {Coordinates|null}
714
+ */
633
715
  getSegmentProjection(p1, p2) {
634
716
 
635
717
  const a = Vector3.normalize(p1.ecef);
@@ -669,6 +751,9 @@ class Coordinates {
669
751
  * Input / Output
670
752
  */
671
753
 
754
+ /**
755
+ * @returns {!String}
756
+ */
672
757
  toString() {
673
758
  let str = '[' + this._lat.toFixed(7) + ', ' + this._lng.toFixed(7);
674
759
  if (this._alt !== null) {
@@ -681,6 +766,9 @@ class Coordinates {
681
766
  return str;
682
767
  }
683
768
 
769
+ /**
770
+ * @returns {!Object}
771
+ */
684
772
  toJson() {
685
773
  const output = {
686
774
  lat: this.lat,
@@ -695,10 +783,17 @@ class Coordinates {
695
783
  return output;
696
784
  }
697
785
 
786
+ /**
787
+ * @param {!Object} json
788
+ * @returns {!Coordinates}
789
+ */
698
790
  static fromJson(json) {
699
791
  return new Coordinates(json.lat, json.lng, json.alt, Level.fromString(json.level));
700
792
  }
701
793
 
794
+ /**
795
+ * @returns {!Object}
796
+ */
702
797
  toCompressedJson() {
703
798
  const output = [this.lat, this.lng];
704
799
  if (this.alt !== null || this.level !== null) {
@@ -710,6 +805,10 @@ class Coordinates {
710
805
  return output;
711
806
  }
712
807
 
808
+ /**
809
+ * @param {!Object} json
810
+ * @returns {!Coordinates}
811
+ */
713
812
  static fromCompressedJson(json) {
714
813
  const coords = new Coordinates(json[0], json[1]);
715
814
  if (json.length > 2) {
@@ -1433,6 +1532,67 @@ class RelativePosition {
1433
1532
  */
1434
1533
  class GeoRelativePosition extends RelativePosition { }
1435
1534
 
1535
+ class GeoRef {
1536
+
1537
+ /** @type {!Coordinates} */
1538
+ origin;
1539
+
1540
+ /** @type {number} */
1541
+ scale = 1;
1542
+
1543
+ /** @type {number} */
1544
+ heading = 0;
1545
+
1546
+
1547
+ /**
1548
+ * @param {number[]} localPosition in ENU frame
1549
+ * @returns {Coordinates}
1550
+ */
1551
+ localToWorld(localPosition) {
1552
+ const localPositionScaled = Vector3.multiplyScalar(localPosition, this.scale);
1553
+ const rotationOffset = Quaternion.fromAxisAngle([0, 0, 1], this.heading);
1554
+ const ecefToEnuRotationOrigin = Quaternion.multiply(rotationOffset, this.origin.ecefToEnuRotation);
1555
+ const ecefTranslation = Quaternion.rotate(ecefToEnuRotationOrigin, localPositionScaled);
1556
+ const ecef = Vector3.add(this.origin.ecef, ecefTranslation);
1557
+ return Coordinates.fromECEF(ecef);
1558
+ }
1559
+
1560
+ /**
1561
+ * @param {Coordinates} coords
1562
+ * @returns {number[]} localPosition in ENU frame
1563
+ */
1564
+ worldToLocal(coords) {
1565
+ const rotationOffset = Quaternion.fromAxisAngle([0, 0, 1], -this.heading);
1566
+ const enuToEcefRotationOrigin = Quaternion.multiply(this.origin.enuToEcefRotation, rotationOffset);
1567
+ const ecefTranslation = Vector3.subtract(coords.ecef, this.origin.ecef);
1568
+ const ecefTranslationScaled = Vector3.multiplyScalar(ecefTranslation, 1 / this.scale);
1569
+ return Quaternion.rotate(enuToEcefRotationOrigin, ecefTranslationScaled);
1570
+ }
1571
+
1572
+ /**
1573
+ * @returns {Object}
1574
+ */
1575
+ toJson() {
1576
+ return {
1577
+ origin: this.origin.toJson(),
1578
+ scale: this.scale,
1579
+ heading: this.heading
1580
+ };
1581
+ }
1582
+
1583
+ /**
1584
+ * @param {Object} json
1585
+ * @returns {GeoRef}
1586
+ */
1587
+ static fromJson(json) {
1588
+ const geoRef = new GeoRef();
1589
+ geoRef.origin = Coordinates.fromJson(json.origin);
1590
+ geoRef.scale = json.scale;
1591
+ geoRef.heading = json.heading;
1592
+ return geoRef;
1593
+ }
1594
+ }
1595
+
1436
1596
  class Attitude {
1437
1597
 
1438
1598
  _quaternion = [1, 0, 0, 0];
@@ -2468,11 +2628,11 @@ class MapMatching {
2468
2628
  checkEdge = false;
2469
2629
  }
2470
2630
 
2471
- if (!Level.equalsTo(location.level, edge.node1.coords.level) && !edge.node1.io) {
2631
+ if (!Level.equalsTo(location.level, edge.node1.coords.level) && !(edge.node1.io && !location.level)) {
2472
2632
  checkNode1 = false;
2473
2633
  }
2474
2634
 
2475
- if (!Level.equalsTo(location.level, edge.node2.coords.level) && !edge.node2.io) {
2635
+ if (!Level.equalsTo(location.level, edge.node2.coords.level) && !(edge.node2.io && !location.level)) {
2476
2636
  checkNode2 = false;
2477
2637
  }
2478
2638
 
@@ -2776,6 +2936,10 @@ class GraphRouter {
2776
2936
 
2777
2937
  const createdNodes = [];
2778
2938
 
2939
+ /**
2940
+ * @param {GraphNode|Coordinates} point
2941
+ * @returns {GraphNode}
2942
+ */
2779
2943
  const retrieveOrCreateNearestNode = point => {
2780
2944
  if (point instanceof GraphNode) {
2781
2945
  return point;
@@ -2788,10 +2952,13 @@ class GraphRouter {
2788
2952
 
2789
2953
  const proj = this._mapMatching.getProjection(point, true, false, false, acceptEdgeFn);
2790
2954
  if (!proj) {
2791
- throw new NoRouteFoundError(start, end,
2792
- `Point ${point.toString()} is too far from the network `
2793
- + `> ${this._mapMatching.maxDistance.toFixed(0)} meters`
2794
- );
2955
+ let message = `Point ${point.toString()} is too far from the network `
2956
+ + `> ${this._mapMatching.maxDistance.toFixed(0)} meters.`;
2957
+ if (point.level) {
2958
+ message += ' If it is a multi-level map, please verify if you have'
2959
+ + ` a network at level ${point.level.toString()}.`;
2960
+ }
2961
+ throw new NoRouteFoundError(start, end, message);
2795
2962
  }
2796
2963
  if (proj.nearestElement instanceof GraphNode) {
2797
2964
  return proj.nearestElement;
@@ -2999,5 +3166,5 @@ class GraphRouter {
2999
3166
  }
3000
3167
  }
3001
3168
 
3002
- export { AbsoluteHeading, Attitude, BoundingBox, Constants, Coordinates, GeoRelativePosition, GraphEdge, GraphItinerary, GraphNode, GraphProjection, GraphRouter, GraphRouterOptions, GraphUtils, Level, MapMatching, Network, NoRouteFoundError, RelativePosition, UserPosition, Utils };
3169
+ export { AbsoluteHeading, Attitude, BoundingBox, Constants, Coordinates, GeoRef, GeoRelativePosition, GraphEdge, GraphItinerary, GraphNode, GraphProjection, GraphRouter, GraphRouterOptions, GraphUtils, Level, MapMatching, Network, NoRouteFoundError, RelativePosition, UserPosition, Utils };
3003
3170
  //# sourceMappingURL=wemap-geo.es.js.map