import type { JSONSupport, JSONSupportMixinProperties } from "../core/JSONSupport.js";
import type { APIUnitString } from "../core/units.js";

export interface SpatialReferenceProperties extends JSONSupportMixinProperties, Partial<Pick<SpatialReference, "falseM" | "falseX" | "falseY" | "falseZ" | "imageCoordinateSystem" | "latestVcsWkid" | "latestWkid" | "mTolerance" | "mUnits" | "vcsWkid" | "wkid" | "wkt" | "wkt2" | "xyTolerance" | "xyUnits" | "zTolerance" | "zUnits">> {}

/**
 * Defines the spatial reference of a view, layer, or method parameters.
 * This indicates the projected or geographic coordinate system used to locate geographic features in the map.
 * Each projected and geographic coordinate system is defined by either a
 * well-known ID [(WKID)](https://developers.arcgis.com/javascript/latest/api-reference/esri-geometry-SpatialReference.html#wkid) or a definition string [(WKT)](https://developers.arcgis.com/javascript/latest/api-reference/esri-geometry-SpatialReference.html#wkt). Note that for versions prior to ArcGIS 10, only [wkid](https://developers.arcgis.com/javascript/latest/api-reference/esri-geometry-SpatialReference.html#wkid) was supported.
 * For a full list of supported spatial reference IDs and their corresponding definition strings, see
 * [Using spatial references](https://developers.arcgis.com/rest/services-reference/enterprise/using-spatial-references.htm).
 *
 * @since 4.0
 * @example
 * // set the spatial reference of a view to WebMercator using wkid
 * const view = new SceneView({
 *   container: "viewDiv",
 *   map: map,
 *   spatialReference: {
 *     wkid: 3857
 *   }
 * });
 * @example
 * // set the spatial reference of a geometry
 * // to WGS84 using the WGS84 property
 * const point = new Point({
 *   x: 10.1,
 *   y: 47.4,
 *   spatialReference: SpatialReference.WGS84
 * });
 */
export default class SpatialReference extends JSONSupport {
  /**
   * A convenience spatial reference instance for Web Mercator.
   *
   * @example
   * // returns true if the webMercatorUtils can
   * // project geometries from WGS84 to Web Mercator
   * let canProjectWGS84toWebMercator = webMercatorUtils.canProject(SpatialReference.WGS84, SpatialReference.WebMercator);
   */
  static readonly WebMercator: SpatialReference;
  /**
   * A convenience spatial reference instance for WGS84.
   *
   * @example
   * // returns true if the webMercatorUtils can
   * // project geometries from WGS84 to Web Mercator
   * let canProjectWGS84toWebMercator = webMercatorUtils.canProject(SpatialReference.WGS84, SpatialReference.WebMercator);
   */
  static readonly WGS84: SpatialReference;
  constructor(properties?: SpatialReferenceProperties);
  /** The false origin of coordinate M values. The default is determined by the spatial reference. */
  accessor falseM: number | null | undefined;
  /** The false origin of coordinate X values. The default is determined by the spatial reference. */
  accessor falseX: number | null | undefined;
  /** The false origin of coordinate Y values. The default is determined by the spatial reference. */
  accessor falseY: number | null | undefined;
  /** The false origin of coordinate Z values. The default is determined by the spatial reference. */
  accessor falseZ: number | null | undefined;
  /**
   * An [image coordinate system](https://developers.arcgis.com/rest/services-reference/raster-ics.htm) defines the
   * spatial reference used to display the image in its original coordinates
   * without distortion, map transformations or ortho-rectification. Typically, [ImageryLayer](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-ImageryLayer.html)
   * is displayed in the [MapView#spatialReference](https://developers.arcgis.com/javascript/latest/api-reference/esri-views-MapView.html#spatialReference) of the view.
   * In some cases, converting images into map coordinates can cause your images to look skewed or distorted because
   * of the various transformations and terrain corrections that are used.
   * Since there is no distortion with images in the image coordinate system, it is ideal for using with oblique imagery
   * and mensuration.
   *
   * The image can be displayed in its original coordinates only in 2D [MapView](https://developers.arcgis.com/javascript/latest/api-reference/esri-views-MapView.html) with a
   * `top-up`rotation which is always oriented in the look of direction of the dataset. This works similarly to an in-car navigation
   * system where the choices are often either north is at the top of the screen (therefore, not using a top up option) or
   * the screen rotates so the travel direction is always displayed at the top.
   *
   * @since 4.13
   * @see [Sample - ImageryLayer image coordinate system](https://developers.arcgis.com/javascript/latest/sample-code/layers-imagery-coordinatesystem/)
   * @see [Image coordinate system](https://developers.arcgis.com/documentation/common-data-types/image-coordinate-system.htm)
   * @see [Image space analysis](https://pro.arcgis.com/en/pro-app/latest/help/analysis/image-analyst/what-is-image-space-analysis-.htm)
   * @see [ImageryLayer#getCatalogItemICSInfo()](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-ImageryLayer.html#getCatalogItemICSInfo)
   * @example
   * // get image coordinate system of the specified catalog item
   * // for example Raster.OBJECTID = 1600
   * layer.getCatalogItemICSInfo(imageId).then(function(info) {
   * // create a spatialReference object and set its
   * // imageCoordinateSystem property
   *  let sr = { // autocasts to esri/geometry/SpatialReference
   *    imageCoordinateSystem: { id: imageId }
   *  };
   *
   *  // Calculate an extent for the mapview based on the image's extent
   *  // in its original coordinate system
   *  const width = document.getElementById("viewDiv").getBoundingClientRect().width;
   *  const height = document.getElementById("viewDiv").getBoundingClientRect().height;
   *  const newExt = info.icsExtent.clone();
   *  const scaleFactor = 5;
   *  newExt.xmin = (newExt.xmin + newExt.xmax - width * scaleFactor) / 2;
   *  newExt.xmax = newExt.xmin + width * scaleFactor;
   *  newExt.ymin = (newExt.ymin + newExt.ymax - height * scaleFactor) / 2;
   *  newExt.ymax = newExt.ymin + height * scaleFactor;
   *  newExt.spatialReference = sr;
   *
   *  // set the MapView's spatialReference to the image's coordinate system
   *  // and the extent to the extent calculated above
   *  view = new MapView({
   *    container: "viewDiv",
   *    map: map,
   *    spatialReference: sr,
   *    extent: newExt
   *  });
   * });
   */
  accessor imageCoordinateSystem: any | null | undefined;
  /** Indicates if the spatial reference refers to a geographic coordinate system. */
  get isGeographic(): boolean;
  /**
   * Indicates if the [wkid](https://developers.arcgis.com/javascript/latest/api-reference/esri-geometry-SpatialReference.html#wkid) of the spatial reference object is one of the following values:
   * `102113`, `102100`, `3857`.
   */
  get isWebMercator(): boolean;
  /**
   * Indicates if the [wkid](https://developers.arcgis.com/javascript/latest/api-reference/esri-geometry-SpatialReference.html#wkid) of the spatial reference object is
   * `4326`.
   */
  get isWGS84(): boolean;
  /**
   * Indicates if the spatial reference of the map supports wrapping around the International
   * Date Line. Value is `true` if the spatial reference is Web Mercator or WGS84.
   */
  get isWrappable(): boolean;
  /** The latest vertical coordinate system well-known ID of the spatial reference. */
  accessor latestVcsWkid: number | null | undefined;
  /** The latest well-known ID of the spatial reference. */
  accessor latestWkid: number | null | undefined;
  /**
   * The factor to convert one unit value in the spatial reference's [unit](https://developers.arcgis.com/javascript/latest/api-reference/esri-geometry-SpatialReference.html#unit) to meters.
   *
   * @since 4.26
   * @example
   * // Convert 1 pixel in a MapView to meters
   * const pixelInMeter = view.resolution * view.spatialReference.metersPerUnit;
   * @example
   * // Measure the geodesic distance between two points in the view's unit
   * const distanceInViewUnit = geodesicUtils.geodesicDistance(pointA, pointB, "meters")  / view.spatialReference.metersPerUnit;
   * @example
   * const DPI = 96
   * const inchesPerMeter = 39.37;
   *
   * // Get the scale for an extent
   * function getScale(extent, viewWidth) {
   *   const unitValue = extent.spatialReference.metersPerUnit;
   *   return (extent.width / viewWidth) * unitValue * inchesPerMeter * DPI;
   * }
   */
  get metersPerUnit(): number;
  /**
   * The tolerance value reflects the accuracy of coordinates.
   * If one coordinate is within the tolerance value of another, they are interpreted as being at the same location.
   * This value is used in relational and topological operations when determining whether two points are close enough to be given the same coordinate value or they are far enough apart to each have their own coordinate value.
   * The default tolerance is set to 0.001 meters or its equivalent in map units.
   */
  accessor mTolerance: number | null | undefined;
  /**
   * The number of distinct values that can be represented per one unit of floating point coordinates.
   * The resolution for M is 1/mUnits. The default resolution is set to 0.0001 meters or its equivalent in map units.
   */
  accessor mUnits: number | null | undefined;
  /**
   * The unit of the spatial reference. Returns `null` if no unit could be determined.
   *
   * Some values of the possible values listed here cannot be used as parameters for certain functions.
   * For example, [geodesicDistance()](https://developers.arcgis.com/javascript/latest/api-reference/esri-geometry-support-geodesicUtils.html#geodesicDistance) only accepts `meters`, `feet`, `us-feet`.
   * For other values, you can use the `meters` unit and use [metersPerUnit](https://developers.arcgis.com/javascript/latest/api-reference/esri-geometry-SpatialReference.html#metersPerUnit) convert to the spatial reference's `unit`.
   *
   * @since 4.26
   * @see [metersPerUnit](https://developers.arcgis.com/javascript/latest/api-reference/esri-geometry-SpatialReference.html#metersPerUnit)
   */
  get unit(): APIUnitString | "degrees" | null | undefined;
  /** The vertical coordinate system well-known ID of the spatial reference. */
  accessor vcsWkid: number | null | undefined;
  /**
   * The well-known ID of a spatial reference.
   * See [Using spatial references](https://developers.arcgis.com/rest/services-reference/enterprise/using-spatial-references.htm)
   * for a list of supported spatial references.
   */
  accessor wkid: number | null | undefined;
  /** The well-known text that defines a spatial reference. */
  accessor wkt: string | null | undefined;
  /**
   * The well-known text of the coordinate system as defined by OGC standard for well-known text strings.
   *
   * @since 4.28
   */
  accessor wkt2: string | null | undefined;
  /**
   * The tolerance value reflects the accuracy of coordinates.
   * If one coordinate is within the tolerance value of another, they are interpreted as being at the same location.
   * This value is used in relational and topological operations when determining whether two points are close enough to be given the same coordinate value or they are far enough apart to each have their own coordinate value.
   * The default tolerance is set to 0.001 meters or its equivalent in map units.
   */
  accessor xyTolerance: number | null | undefined;
  /**
   * The number of distinct values that can be represented per one unit of floating point coordinates.
   * The resolution for X and Y is 1/xyUnits. The default resolution is set to 0.0001 meters or its equivalent in map units.
   */
  accessor xyUnits: number | null | undefined;
  /**
   * The tolerance value reflects the accuracy of coordinates.
   * If one coordinate is within the tolerance value of another, they are interpreted as being at the same location.
   * This value is used in relational and topological operations when determining whether two points are close enough to be given the same coordinate value or they are far enough apart to each have their own coordinate value.
   * The default tolerance is set to 0.001 meters or its equivalent in map units.
   */
  accessor zTolerance: number | null | undefined;
  /**
   * The number of distinct values that can be represented per one unit of floating point coordinates.
   * The resolution for Z is 1/zUnits. The default resolution is set to 0.0001 meters or its equivalent in map units.
   */
  accessor zUnits: number | null | undefined;
  /**
   * Returns a deep clone of the spatial reference object.
   *
   * @returns Returns a deep clone of the spatial reference object.
   */
  clone(): SpatialReference;
  /**
   * Checks if the specified spatial reference object has the same [wkid](https://developers.arcgis.com/javascript/latest/api-reference/esri-geometry-SpatialReference.html#wkid) or
   * [wkt](https://developers.arcgis.com/javascript/latest/api-reference/esri-geometry-SpatialReference.html#wkt) as this spatial reference object.
   *
   * @param spatialReference - The spatial reference to compare to.
   * @returns Returns `true` if the input spatial reference object has the
   *                   same wkid or wkt as this spatial reference object.
   * @example
   * const SpatialReference = await $arcgis.import("@arcgis/core/geometry/SpatialReference.js");
   * const sr1 = new SpatialReference({ wkid: 4326 });
   * const sr2 = new SpatialReference({ wkid: 4326 });
   * console.log(sr1.equals(sr2)); // true
   */
  equals(spatialReference: SpatialReference | null | undefined): boolean;
}