/
GeodeticDatum.ts
752 lines (676 loc) · 31.2 KB
/
GeodeticDatum.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
* @module Geometry
*/
// cspell:ignore JSONXYZ, ETRF, OSGB, DHDN, NADCON, GEOCN
import { Geometry, Vector3d, XYAndZ } from "@itwin/core-geometry";
import { GeodeticEllipsoid, GeodeticEllipsoidProps } from "./GeodeticEllipsoid";
/** Holds 3 components of a Positional Vector rotation definition in arc seconds
* @public
* @extensions
*/
export interface XyzRotationProps {
/** X rotation component in arc second */
x: number;
/** Y rotation component in arc second*/
y: number;
/** Z rotation component in arc second*/
z: number;
}
/** Hold 3 components data of a Positional Vector rotation definition in arc seconds
* @public
*/
export class XyzRotation implements XyzRotationProps {
/** X rotation component in arc second */
public readonly x!: number;
/** Y rotation component in arc second*/
public readonly y!: number;
/** Z rotation component in arc second*/
public readonly z!: number;
public constructor(data?: XyzRotationProps) {
if (data) {
this.x = data.x;
this.y = data.y;
this.z = data.z;
}
}
/** Creates a Rotations object from JSON representation.
* @public */
public static fromJSON(data: XyzRotationProps): XyzRotation {
return new XyzRotation(data);
}
/** Creates a JSON from the Rotations definition
* @public */
public toJSON(): XyzRotationProps {
return { x: this.x, y: this.y, z: this.z };
}
/** Compares two geodetic rotations. It applies a minuscule angular tolerance
* @public */
public equals(other: XyzRotation): boolean {
return (Math.abs(this.x - other.x) < Geometry.smallAngleSeconds &&
Math.abs(this.y - other.y) < Geometry.smallAngleSeconds &&
Math.abs(this.z - other.z) < Geometry.smallAngleSeconds);
}
}
/** Type indicating the geodetic transformation method
* @public
* @extensions
*/
export type GeodeticTransformMethod =
"None" |
"Geocentric" |
"PositionalVector" |
"GridFiles" |
"MultipleRegression" |
"Undefined";
/** This interface represents a geocentric (three parameters) geodetic transformation.
* @public
* @extensions
*/
export interface GeocentricTransformProps {
/** The frame translation components in meters */
delta: XYAndZ;
}
/** This class represents a geocentric (three parameters) geodetic transformation.
* @public
*/
export class GeocentricTransform implements GeocentricTransformProps {
/** The frame translation components in meters */
public readonly delta: Vector3d;
public constructor(data?: GeocentricTransformProps) {
this.delta = data ? Vector3d.fromJSON(data.delta) : new Vector3d();
}
/** Creates a Geocentric Transform from JSON representation.
* @public */
public static fromJSON(data: GeocentricTransformProps): GeocentricTransform {
return new GeocentricTransform(data);
}
/** Creates a JSON from the Geodetic GeocentricTransform definition
* @public */
public toJSON(): GeocentricTransformProps {
return { delta: { x: this.delta.x, y: this.delta.y, z: this.delta.z } };
}
/** Compares two geodetic transforms. It applies a minuscule tolerance.
* @public */
public equals(other: GeocentricTransform): boolean {
return (Math.abs(this.delta.x - other.delta.x) < Geometry.smallMetricDistance &&
Math.abs(this.delta.y - other.delta.y) < Geometry.smallMetricDistance &&
Math.abs(this.delta.z - other.delta.z) < Geometry.smallMetricDistance);
}
}
/** This interface represents a positional vector (seven parameters) geodetic transformation corresponding to
* EPSG operation 9606. Beware that the convention relative to rotation direction is different
* from the Coordinate Frame operation (epsg 9607).
* @public
* @extensions
*/
export interface PositionalVectorTransformProps {
/** The frame translation components in meters */
delta: XYAndZ;
/** The frame rotation components in arc seconds. The rotation sign convention is the one associated with
* the operation EPSG:9606 following recommendation of ISO 19111 specifications */
rotation: XyzRotationProps;
/** Scale in parts per million. The scale effectively applied will be 1 plus scale divided by 1 000 000. */
scalePPM: number;
}
/** This class represents a positional vector (seven parameters) geodetic transformation corresponding to
* EPSG operation 9606. Beware that the convention relative to rotation direction is different
* from the Coordinate Frame operation (epsg 9607).
* @public
*/
export class PositionalVectorTransform implements PositionalVectorTransformProps {
/** The frame translation components in meters */
public readonly delta!: Vector3d;
/** The frame rotation components in arc seconds. The rotation sign convention is the one associated with
* the operation EPSG:9606 following recommendation of ISO 19111 specifications */
public readonly rotation!: XyzRotation;
/** Scale in parts per million. The scale effectively applied will be 1 plus scale divided by 1 000 000. */
public readonly scalePPM!: number;
public constructor(data?: PositionalVectorTransformProps) {
if (data) {
this.delta = data.delta ? Vector3d.fromJSON(data.delta) : new Vector3d();
this.rotation = data.rotation ? XyzRotation.fromJSON(data.rotation) : new XyzRotation();
this.scalePPM = data.scalePPM;
}
}
/** Creates a Positional Vector Transform from JSON representation.
* @public */
public static fromJSON(data: PositionalVectorTransformProps): PositionalVectorTransform {
return new PositionalVectorTransform(data);
}
/** Creates a JSON from the Positional Vector Transform definition
* @public */
public toJSON(): PositionalVectorTransformProps {
return {
delta: { x: this.delta.x, y: this.delta.y, z: this.delta.z },
rotation: this.rotation.toJSON(),
scalePPM: this.scalePPM,
};
}
/** Compares two Positional Vector Transforms. It applies a minuscule tolerance to number compares.
* @public */
public equals(other: PositionalVectorTransform): boolean {
if (Math.abs(this.delta.x - other.delta.x) > Geometry.smallMetricDistance ||
Math.abs(this.delta.y - other.delta.y) > Geometry.smallMetricDistance ||
Math.abs(this.delta.z - other.delta.z) > Geometry.smallMetricDistance ||
Math.abs(this.scalePPM - other.scalePPM) > Geometry.smallFraction)
return false;
return this.rotation.equals(other.rotation);
}
}
/** Type indicating the file format of the grid files.
* @public
* @extensions
*/
export type GridFileFormat =
"NONE" |
"NTv1" |
"NTv2" |
"NADCON" |
"FRENCH" |
"JAPAN" |
"ATS77" |
"GEOCN" |
"OSTN02" |
"OSTN15";
/** type to indicate the grid file application direction.
* @public
* @extensions
*/
export type GridFileDirection = "Direct" | "Inverse";
/** Grid file definition containing name of the file, the format and the direction it should be applied
* @public
* @extensions
*/
export interface GridFileDefinitionProps {
/** Name of the grid shift file. This name is relative to the expected dictionary root document.
* Typical grid shift file name will contain first the country name it applies to then possibly some sub path.
* Example of existing grid files:
* Germany/BETA2007.gsb or Brazil/SAD69_003.GSB but sometimes longer paths USA/NADCON/conus.l*s
* Note that the file name can contain wildcards when the format requires more than one file. For example
* the NADCON format makes use of a different file for latitude and longitude shifts thus the .l*s extension in the
* file name above.
* Forward slash is always used to separate the path components.
*/
fileName: string;
/** The grid file format */
format: GridFileFormat;
/** The grid file application direction */
direction: GridFileDirection;
}
/** Grid file definition containing name of the file, the format and the direction it should be applied
* @public
*/
export class GridFileDefinition implements GridFileDefinitionProps {
/** Name of the grid shift file. This name is relative to the expected dictionary root document.
* Typical grid shift file name will contain first the country name it applies to then possibly some sub path.
* Example of existing grid files:
* Germany/BETA2007.gsb or Brazil/SAD69_003.GSB but sometimes longer paths USA/NADCON/conus.l*s
* Note that the file name can contain wildcards when the format requires more than one file. For example
* the NADCON format makes use of a different file for latitude and longitude shifts thus the .l*s extension in the
* file name above.
* Forward slash is always used to separate the path components.
*/
public readonly fileName: string;
/** The grid file format */
public readonly format: GridFileFormat;
/** The grid file application direction */
public readonly direction: GridFileDirection;
public constructor(data?: GridFileDefinitionProps) {
this.fileName = data ? data.fileName : "";
this.format = data ? data.format : "NTv2";
this.direction = data ? data.direction : "Direct";
}
/** Creates a Grid File Definition from JSON representation.
* @public */
public static fromJSON(data: GridFileDefinitionProps): GridFileDefinition {
return new GridFileDefinition(data);
}
/** Creates a JSON from the Grid File Definition
* @public */
public toJSON(): GridFileDefinitionProps {
return { fileName: this.fileName, format: this.format, direction: this.direction };
}
/** Compares two grid file definition. It is a strict compare operation not an equivalence test.
* @public */
public equals(other: GridFileDefinition): boolean {
return (this.fileName === other.fileName && this.direction === other.direction && this.format === other.format);
}
}
/** This interface represents a grid files based geodetic transformation.
* @public
* @extensions
*/
export interface GridFileTransformProps {
/** The list of grid files. The order of file is meaningful, the first encountered that covers the extent of coordinate
* transformation will be used. */
files: GridFileDefinitionProps[];
/** The positional vector fallback transformation used for extents not covered by the grid files */
fallback?: PositionalVectorTransformProps;
}
/** This class represents a grid files based geodetic transformation.
* @public
*/
export class GridFileTransform implements GridFileTransformProps {
/** The list of grid files. The order of file is meaningful, the first encountered that covers the extent of coordinate
* transformation will be used. */
public readonly files: GridFileDefinition[];
/** The positional vector fallback transformation used for extents not covered by the grid files */
public readonly fallback?: PositionalVectorTransform;
public constructor(data?: GridFileTransformProps) {
this.files = [];
if (data) {
this.fallback = data.fallback ? PositionalVectorTransform.fromJSON(data.fallback) : undefined;
if (Array.isArray(data.files)) {
this.files = [];
for (const item of data.files)
this.files.push(GridFileDefinition.fromJSON(item));
}
}
}
/** Creates a Grid File Transform from JSON representation.
* @public */
public static fromJSON(data: GridFileTransformProps): GridFileTransform {
return new GridFileTransform(data);
}
/** Creates a JSON from the Grid File Transform definition
* @public */
public toJSON(): GridFileTransformProps {
const data: GridFileTransformProps = { files: [] };
data.fallback = this.fallback ? this.fallback.toJSON() : undefined;
if (Array.isArray(this.files)) {
for (const item of this.files)
data.files.push(item.toJSON());
}
return data;
}
/** Compares two Grid File Transforms. It is a strict compare operation not an equivalence test.
* @public */
public equals(other: GridFileTransform): boolean {
if (this.files.length !== other.files.length)
return false;
for (let idx = 0; idx < this.files.length; ++idx)
if (!this.files[idx].equals(other.files[idx]))
return false;
if ((this.fallback === undefined) !== (other.fallback === undefined))
return false;
if (this.fallback && !this.fallback.equals(other.fallback!))
return false;
return true;
}
}
/** This interface represents a geodetic transformation that enables transforming longitude/latitude coordinates
* from one datum to another.
* @public
* @extensions
*/
export interface GeodeticTransformProps {
/** The method used by the geodetic transform */
method: GeodeticTransformMethod;
/** The complete definition of the source geodetic ellipsoid referred to by ellipsoidId.
* The source ellipsoid identifier enables obtaining the shape of the Earth mathematical model
* for the purpose of performing the transformation.
*/
sourceEllipsoid?: GeodeticEllipsoidProps;
/** The complete definition of the target geodetic ellipsoid referred to by ellipsoidId.
* The target ellipsoid identifier enables obtaining the shape of the Earth mathematical model
* for the purpose of performing the transformation.*/
targetEllipsoid?: GeodeticEllipsoidProps;
/** The id of the source datum. */
sourceDatumId?: string;
/** The id of the target datum. This id is useful to seach within a geodetic transform path for
* a shortcut to another included datum.
*/
targetDatumId?: string;
/** When method is Geocentric this property contains the geocentric parameters */
geocentric?: GeocentricTransformProps;
/** When method is PositionalVector this property contains the positional vector parameters */
positionalVector?: PositionalVectorTransformProps;
/** When method is GridFiles this property contains the grid files parameters */
gridFile?: GridFileTransformProps;
}
/** This class represents a geodetic transformation that enables transforming longitude/latitude coordinates
* from one datum to another.
* @public
*/
export class GeodeticTransform implements GeodeticTransformProps {
/** The method used by the geodetic transform */
public readonly method: GeodeticTransformMethod;
/** The identifier of the source geodetic datum as stored in the dictionary or the service database.
* This identifier is optional and informational only.
*/
public readonly sourceEllipsoid?: GeodeticEllipsoid;
/** The complete definition of the target geodetic ellipsoid referred to by ellipsoidId.
* The target ellipsoid identifier enables obtaining the shape of the Earth mathematical model
* for the purpose of performing the transformation.*/
public readonly targetEllipsoid?: GeodeticEllipsoid;
/** The id of the source datum. */
public readonly sourceDatumId?: string;
/** The id of the target datum. This id is useful to seach within a geodetic transform path for
* a shortcut to another included datum.
*/
public readonly targetDatumId?: string;
/** When method is Geocentric this property contains the geocentric parameters */
public readonly geocentric?: GeocentricTransform;
/** When method is PositionalVector this property contains the positional vector parameters */
public readonly positionalVector?: PositionalVectorTransform;
/** When method is GridFiles this property contains the grid files parameters */
public readonly gridFile?: GridFileTransform;
public constructor(data?: GeodeticTransformProps) {
this.method = "None";
if (data) {
this.method = data.method;
this.sourceEllipsoid = data.sourceEllipsoid ? GeodeticEllipsoid.fromJSON(data.sourceEllipsoid) : undefined;
this.targetEllipsoid = data.targetEllipsoid ? GeodeticEllipsoid.fromJSON(data.targetEllipsoid) : undefined;
this.sourceDatumId = data.sourceDatumId;
this.targetDatumId = data.targetDatumId;
this.geocentric = data.geocentric ? GeocentricTransform.fromJSON(data.geocentric) : undefined;
this.positionalVector = data.positionalVector ? PositionalVectorTransform.fromJSON(data.positionalVector) : undefined;
this.gridFile = data.gridFile ? GridFileTransform.fromJSON(data.gridFile) : undefined;
}
}
/** Creates a Geodetic Transform from JSON representation.
* @public */
public static fromJSON(data: GeodeticTransformProps): GeodeticTransform {
return new GeodeticTransform(data);
}
/** Creates a JSON from the Geodetic Transform definition
* @public */
public toJSON(): GeodeticTransformProps {
const data: GeodeticTransformProps = { method: this.method };
data.sourceEllipsoid = this.sourceEllipsoid ? this.sourceEllipsoid.toJSON() : undefined;
data.targetEllipsoid = this.targetEllipsoid ? this.targetEllipsoid.toJSON() : undefined;
data.sourceDatumId = this.sourceDatumId;
data.targetDatumId = this.targetDatumId;
data.geocentric = this.geocentric ? this.geocentric.toJSON() : undefined;
data.positionalVector = this.positionalVector ? this.positionalVector.toJSON() : undefined;
data.gridFile = this.gridFile ? this.gridFile.toJSON() : undefined;
return data;
}
/** Compares two geodetic Transforms. It is not an equivalence test since
* descriptive information is strictly compared. A minuscule tolerance is applied to number compares.
* @public */
public equals(other: GeodeticTransform): boolean {
if (this.method !== other.method)
return false;
if (this.sourceDatumId !== other.sourceDatumId || this.targetDatumId !== other.targetDatumId)
return false;
if ((this.sourceEllipsoid === undefined) !== (other.sourceEllipsoid === undefined))
return false;
if (this.sourceEllipsoid && !this.sourceEllipsoid.equals(other.sourceEllipsoid!))
return false;
if ((this.targetEllipsoid === undefined) !== (other.targetEllipsoid === undefined))
return false;
if (this.targetEllipsoid && !this.targetEllipsoid.equals(other.targetEllipsoid!))
return false;
if ((this.geocentric === undefined) !== (other.geocentric === undefined))
return false;
if (this.geocentric && !this.geocentric.equals(other.geocentric!))
return false;
if ((this.positionalVector === undefined) !== (other.positionalVector === undefined))
return false;
if (this.positionalVector && !this.positionalVector.equals(other.positionalVector!))
return false;
if ((this.gridFile === undefined) !== (other.gridFile === undefined))
return false;
if (this.gridFile && !this.gridFile.equals(other.gridFile!))
return false;
return true;
}
}
/** This interface represents a geodetic datum transform path. It contains a list of transforms linking
* a source to a target geodetic datum.
* @public
*/
export interface GeodeticTransformPathProps {
/** Source geodetic datum key name */
sourceDatumId?: string;
/** Target geodetic datum key name */
targetDatumId?: string;
/** The transformation path from source datum to target datum.
*/
transforms?: GeodeticTransformProps[];
}
/** This class represents a geodetic datum transform path. It contains a list of transforms linking
* a source to a target geodetic datum.
* @public
*/
export class GeodeticTransformPath implements GeodeticTransformPathProps {
/** Source geodetic datum key name */
public readonly sourceDatumId?: string;
/** Target geodetic datum key name */
public readonly targetDatumId?: string;
/** The transformation path from source datum to target datum.
*/
public readonly transforms?: GeodeticTransform[];
public constructor(_data?: GeodeticTransformPathProps) {
if (_data) {
this.sourceDatumId = _data.sourceDatumId;
this.targetDatumId = _data.targetDatumId;
if (Array.isArray(_data.transforms)) {
this.transforms = [];
for (const item of _data.transforms)
this.transforms.push(GeodeticTransform.fromJSON(item));
}
}
}
/** Creates a Geodetic transform path from JSON representation.
* @public */
public static fromJSON(data: GeodeticTransformPathProps): GeodeticTransformPath {
return new GeodeticTransformPath(data);
}
/** Creates a JSON from the Geodetic transform path definition
* @public */
public toJSON(): GeodeticTransformPathProps {
const data: GeodeticTransformPathProps = {};
data.sourceDatumId = this.sourceDatumId;
data.targetDatumId = this.targetDatumId;
if (Array.isArray(this.transforms)) {
data.transforms = [];
for (const item of this.transforms)
data.transforms.push(item.toJSON());
}
return data;
}
/** Compares two Geodetic Transform Paths. It is a strict compare operation not an equivalence test.
* It takes into account descriptive properties not only mathematical definition properties.
* @public */
public equals(other: GeodeticTransformPath): boolean {
if (this.sourceDatumId !== other.sourceDatumId || this.targetDatumId !== other.targetDatumId)
return false;
if ((this.transforms === undefined) !== (other.transforms === undefined))
return false;
if (this.transforms && other.transforms) {
if (this.transforms.length !== other.transforms.length)
return false;
for (let idx = 0; idx < this.transforms.length; ++idx)
if (!this.transforms[idx].equals(other.transforms[idx]))
return false;
}
return true;
}
}
/** This interface represents a geodetic datum. Geodetic datums are based on an ellipsoid.
* In addition to the ellipsoid definition they are the base for longitude/latitude coordinates.
* Geodetic datums are the basis for geodetic transformations. Most geodetic datums are defined by specifying
* the transformation to the common base WGS84 (or local equivalent). The transforms property can contain the
* definition of the transformation path to WGS84.
* Sometimes there exists transformation paths direct from one non-WGS84 datum to another non-WGS84. The current model
* does not allow specifications of these special paths at the moment.
* @public
* @extensions
*/
export interface GeodeticDatumProps {
/** GeodeticDatum key name */
id?: string;
/** Description */
description?: string;
/** If true then indicates the definition is deprecated. It should then be used for backward compatibility only.
** If false or undefined then the definition is not deprecated.
*/
deprecated?: boolean;
/** A textual description of the source of the geodetic datum definition. */
source?: string;
/** The EPSG code of the geodetic datum. If undefined then there is no EPSG code associated. */
epsg?: number;
/** The key name to the base Ellipsoid. */
ellipsoidId?: string;
/** The full definition of the geodetic ellipsoid associated to the datum. If undefined then the ellipsoidId must be used to fetch the definition from the dictionary, geographic coordinate system service or the backend */
ellipsoid?: GeodeticEllipsoidProps;
/** The transformation to WGS84. If null then there is no known transformation to WGS84. Although
* this is rare it occurs in a few cases where the country charges for obtaining and using
* the transformation and its parameters, or if the transformation is maintained secret for military reasons.
* In this case the recommendation is to considered the geodetic datum to be coincident to WGS84 keeping
* in mind imported global data such as Google Map or Bing Map data may be approximately located.
* The list of transforms contains normally a single transform but there can be a sequence of transformations
* required to transform to WGS84, such as the newer datum definitions for Slovakia or Switzerland.
*/
transforms?: GeodeticTransformProps[];
/** The optional list of transformation paths to other datum. These should only be used if the path to
* these datum is not included in the transforms property definition of the transformation to WGS84.
* It should not be used either if the transformation to the datum can be infered from the concatenation
* of their individual paths to WGS84. These should be used to express an alternate shortcut path that is
* inherent to the nature of the datum. As an example it is required to represent the transformation
* from NAD27 to NAD83/2011 since NAD83/2011 is coincident to WGS84 yet the NAD27 datum to WGS84 path
* only includes transformation to NAD83, making the path of transforms to NAD83/2011 not related
* to the available paths to WGS84.
*/
additionalTransformPaths?: GeodeticTransformPathProps[];
}
/** This class represents a geodetic datum. Geodetic datums are based on an ellipsoid.
* In addition to the ellipsoid definition they are the base for longitude/latitude coordinates.
* Geodetic datums are the basis for geodetic transformations. Most geodetic datums are defined by specifying
* the transformation to the common base WGS84 (or local equivalent). The transforms property can contain the
* definition of the transformation path to WGS84.
* Sometimes there exists transformation paths direct from one non-WGS84 datum to another non-WGS84. The current model
* does not allow specifications of these special paths at the moment.
* @public
*/
export class GeodeticDatum implements GeodeticDatumProps {
/** GeodeticDatum key name */
public readonly id?: string;
/** Description */
public readonly description?: string;
/** If true then indicates the definition is deprecated. It should then be used for backward compatibility only.
* If false then the definition is not deprecated. Default is false.
*/
public readonly deprecated: boolean;
/** A textual description of the source of the geodetic datum definition. */
public readonly source?: string;
/** The EPSG code of the geodetic datum. If undefined then there is no EPSG code associated. */
public readonly epsg?: number;
/** The key name to the base Ellipsoid. */
public readonly ellipsoidId?: string;
/** The full definition of the geodetic ellipsoid associated to the datum. If undefined then the ellipsoidId must
* be used to fetch the definition from the dictionary, geographic coordinate system service or the backend
*/
public readonly ellipsoid?: GeodeticEllipsoid;
/** The transformation to WGS84. If null then there is no known transformation to WGS84. Although
* this is rare it occurs in a few cases where the country charges for obtaining and using
* the transformation and its parameters, or if the transformation is maintained secret for military reasons.
* In this case the recommendation is to considered the geodetic datum to be coincident to WGS84 keeping
* in mind imported global data such as Google Map or Bing Map data may be approximately located.
* The list of transforms contains normally a single transform but there can be a sequence of transformations
* required to transform to WGS84, such as the newer datum definitions for Slovakia or Switzerland.
*/
public readonly transforms?: GeodeticTransform[];
/** The optional list of transformation paths to other datum. These should only be used if the path to
* these datum is not included in the transforms property definition of the transformation to WGS84.
* It should not be used either if the transformation to the datum can be infered from the concatenation
* of their individual paths to WGS84. These should be used to express an alternate shortcut path that is
* inherent to the nature of the datum. As an example it is required to represent the transformation
* from NAD27 to NAD83/2011 since NAD83/2011 is coincident to WGS84 yet the NAD27 datum to WGS84 path
* only includes transformation to NAD83, making the path of transforms to NAD83/2011 not related
* to the available paths to WGS84.
*/
public readonly additionalTransformPaths?: GeodeticTransformPath[];
public constructor(_data?: GeodeticDatumProps) {
this.deprecated = false;
if (_data) {
this.id = _data.id;
this.description = _data.description;
this.deprecated = _data.deprecated ?? false;
this.source = _data.source;
this.epsg = _data.epsg;
this.ellipsoidId = _data.ellipsoidId;
this.ellipsoid = _data.ellipsoid ? GeodeticEllipsoid.fromJSON(_data.ellipsoid) : undefined;
if (Array.isArray(_data.transforms)) {
this.transforms = [];
for (const item of _data.transforms)
this.transforms.push(GeodeticTransform.fromJSON(item));
}
if (Array.isArray(_data.additionalTransformPaths)) {
this.additionalTransformPaths = [];
for (const item of _data.additionalTransformPaths)
this.additionalTransformPaths.push(GeodeticTransformPath.fromJSON(item));
}
}
}
/** Creates a Geodetic Datum from JSON representation.
* @public */
public static fromJSON(data: GeodeticDatumProps): GeodeticDatum {
return new GeodeticDatum(data);
}
/** Creates a JSON from the Geodetic Datum definition
* @public */
public toJSON(): GeodeticDatumProps {
const data: GeodeticDatumProps = {};
data.id = this.id;
data.description = this.description;
/* We prefer to use the default undef instead of false value for deprecated value in Json */
data.deprecated = (this.deprecated === false ? undefined : true);
data.source = this.source;
data.epsg = this.epsg;
data.ellipsoidId = this.ellipsoidId;
data.ellipsoid = this.ellipsoid ? this.ellipsoid.toJSON() : undefined;
if (Array.isArray(this.transforms)) {
data.transforms = [];
for (const item of this.transforms)
data.transforms.push(item.toJSON());
}
if (Array.isArray(this.additionalTransformPaths)) {
data.additionalTransformPaths = [];
for (const item of this.additionalTransformPaths)
data.additionalTransformPaths.push(item.toJSON());
}
return data;
}
/** Compares two Geodetic Datums. It is a strict compare operation not an equivalence test.
* It takes into account descriptive properties not only mathematical definition properties.
* @public */
public equals(other: GeodeticDatum): boolean {
if (this.id !== other.id ||
this.description !== other.description ||
this.deprecated !== other.deprecated ||
this.source !== other.source ||
this.epsg !== other.epsg ||
this.ellipsoidId !== other.ellipsoidId)
return false;
if ((this.ellipsoid === undefined) !== (other.ellipsoid === undefined))
return false;
if (this.ellipsoid && !this.ellipsoid.equals(other.ellipsoid!))
return false;
if ((this.transforms === undefined) !== (other.transforms === undefined))
return false;
if (this.transforms && other.transforms) {
if (this.transforms.length !== other.transforms.length)
return false;
for (let idx = 0; idx < this.transforms.length; ++idx)
if (!this.transforms[idx].equals(other.transforms[idx]))
return false;
}
if ((this.additionalTransformPaths === undefined) !== (other.additionalTransformPaths === undefined))
return false;
if (this.additionalTransformPaths && other.additionalTransformPaths) {
if (this.additionalTransformPaths.length !== other.additionalTransformPaths.length)
return false;
for (let idx = 0; idx < this.additionalTransformPaths.length; ++idx)
if (!this.additionalTransformPaths[idx].equals(other.additionalTransformPaths[idx]))
return false;
}
return true;
}
}