/*---------------------------------------------------------------------------------------------
* 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 } from "@itwin/core-geometry";
import { GeodeticEllipsoid } from "./GeodeticEllipsoid";
/** Hold 3 components data of a Positional Vector rotation definition in arc seconds
 *  @public
 */
export class XyzRotation {
    constructor(data) {
        if (data) {
            this.x = data.x;
            this.y = data.y;
            this.z = data.z;
        }
    }
    /** Creates a Rotations object from JSON representation.
     * @public */
    static fromJSON(data) {
        return new XyzRotation(data);
    }
    /** Creates a JSON from the Rotations definition
     * @public */
    toJSON() {
        return { x: this.x, y: this.y, z: this.z };
    }
    /** Compares two geodetic rotations. It applies a minuscule angular tolerance
     *  @public */
    equals(other) {
        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);
    }
}
/** This class represents a geocentric (three parameters) geodetic transformation.
 *  @public
 */
export class GeocentricTransform {
    constructor(data) {
        this.delta = data ? Vector3d.fromJSON(data.delta) : new Vector3d();
    }
    /** Creates a Geocentric Transform from JSON representation.
     * @public */
    static fromJSON(data) {
        return new GeocentricTransform(data);
    }
    /** Creates a JSON from the Geodetic GeocentricTransform definition
     * @public */
    toJSON() {
        return { delta: { x: this.delta.x, y: this.delta.y, z: this.delta.z } };
    }
    /** Compares two geodetic transforms. It applies a minuscule tolerance.
     *  @public */
    equals(other) {
        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 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 {
    constructor(data) {
        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 */
    static fromJSON(data) {
        return new PositionalVectorTransform(data);
    }
    /** Creates a JSON from the Positional Vector Transform definition
     * @public */
    toJSON() {
        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 */
    equals(other) {
        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);
    }
}
/** Grid file definition containing name of the file, the format and the direction it should be applied
 *  @public
 */
export class GridFileDefinition {
    constructor(data) {
        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 */
    static fromJSON(data) {
        return new GridFileDefinition(data);
    }
    /** Creates a JSON from the Grid File Definition
     * @public */
    toJSON() {
        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 */
    equals(other) {
        return (this.fileName === other.fileName && this.direction === other.direction && this.format === other.format);
    }
}
/** This class represents a grid files based geodetic transformation.
 *  @public
 */
export class GridFileTransform {
    constructor(data) {
        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 */
    static fromJSON(data) {
        return new GridFileTransform(data);
    }
    /** Creates a JSON from the Grid File Transform definition
     * @public */
    toJSON() {
        const data = { 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 */
    equals(other) {
        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 class represents a geodetic transformation that enables transforming longitude/latitude coordinates
 *  from one datum to another.
 *  @public
 */
export class GeodeticTransform {
    constructor(data) {
        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 */
    static fromJSON(data) {
        return new GeodeticTransform(data);
    }
    /** Creates a JSON from the Geodetic Transform definition
     * @public */
    toJSON() {
        const data = { 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 */
    equals(other) {
        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 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 {
    constructor(_data) {
        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 */
    static fromJSON(data) {
        return new GeodeticTransformPath(data);
    }
    /** Creates a JSON from the Geodetic transform path definition
     * @public */
    toJSON() {
        const data = {};
        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 */
    equals(other) {
        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 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 {
    constructor(_data) {
        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 */
    static fromJSON(data) {
        return new GeodeticDatum(data);
    }
    /** Creates a JSON from the Geodetic Datum definition
     * @public */
    toJSON() {
        const data = {};
        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 */
    equals(other) {
        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;
    }
}
//# sourceMappingURL=GeodeticDatum.js.map