类名 common/base/geometry/Point.js
import {defaultValue, isNumber, toJSON} from '../../util'
import Zondy from '../Zondy'
import Geometry from './Geometry'
import { GeometryType, IGSGeometryType } from '../enum'
import Extent from './Extent'

/**
 * 点几何对象
 * <br><br>[ES5引入方式]:<br/>
 * Zondy.Geometry.Point() <br/>
 * [ES6引入方式]:<br/>
 * import { Point } from "@mapgis/webclient-common" <br/>
 * <br/>
 * @class Point
 * @moduleEX GeometryModule
 * @extends Geometry
 * @param {Object} options 构造参数
 * @param {Array} [options.coordinates = []] 几何点的坐标数组,支持任意单位,参考示例:<a href='#Point'>[点几何对象]</a>
 * @param {SpatialReference} [options.spatialReference = new Zondy.SpatialReference('EPSG:4326')] 几何点的空间参考系,默认4326,当不是4326时请指定坐标系,方便进行投影转换,参考示例:<a href='#SpatialReference'>[指定坐标系]</a>
 *
 * @summary <h5>支持如下方法:</h5>
 * <a href='#toCoordinates'>[1、获取坐标数组]</a><br/>
 * <a href='#copy'>[2、将另一个点对象的所有值复制到当前点对象]</a><br/>
 * <a href='#distance'>[3、计算与另一个点的距离]</a><br/>
 * <a href='#equals'>[4、判断两个点是否相等]</a><br/>
 * <a href='#normalize'>[5、归一化计算]</a><br/>
 * <a href='#toString'>[6、返回字符串]</a><br/>
 * <a href='#getIGSType'>[7、返回IGS所对应的GeometryModule型]</a><br/>
 * <a href='#toOldIGSGeometry'>[8、返回igs1.0的几何对象]</a>
 * <a href='#fromJSON'>[9、通过传入的json构造并返回一个新的几何对象]</a><br/>
 * <a href='#toJSON'>[10、导出为json对象]</a><br/>
 * [11、克隆几何对象]{@link Geometry#clone}
 *
 * @example <caption><h7 id='Point'>创建几何对象</h7></caption>
 * // ES5引入方式
 * const { Point } = Zondy.Geometry
 * // ES6引入方式
 * import { Point } from "@mapgis/webclient-common"
 * new Point({
 *   coordinates: [100.0, 0.0]
 * })
 *
 * @example <caption><h7 id='SpatialReference'>指定坐标系</h7></caption>
 * // ES5引入方式
 * const { Point } = Zondy.Geometry
 * const { SpatialReference } = Zondy
 * // ES6引入方式
 * import { Point, SpatialReference } from "@mapgis/webclient-common"
 * new Point({
 *   // 现在为3857坐标系
 *   coordinates: [12929863.44711455, 3377247.5680546067],
 *   // 当不是4326时请指定坐标系,方便进行投影转换
 *   spatialReference: new SpatialReference('EPSG:3857')
 * })
 */

class Point extends Geometry {
  constructor(options) {
    super(options)
    // 构造几何对象
    options = defaultValue(options, {})
    /**
     * 几何点的坐标数组
     * @member {Array} Point.prototype.coordinates
     */
    this.coordinates = defaultValue(options.coordinates, [])
    /**
     * 几何类型
     * @member {GeometryType} Point.prototype.type
     */
    this.type = GeometryType.point

    // 判断坐标是否合理
    if (!Array.isArray(this.coordinates)) {
      throw new Error('坐标必须为数组')
    }
    if (this.coordinates.length < 2 || this.coordinates.length > 3) {
      throw new Error('坐标数组的长度必须在合理范围内(2或者3)')
    }
    if (!isNumber(this.coordinates[0]) || !isNumber(this.coordinates[1])) {
      throw new Error('坐标数组成员必须为数字')
    }

    // 计算hasZ的值
    if (this.coordinates.length === 3) {
      this.hasZ = true
    } else {
      this.hasZ = false
    }
  }

  /**
   * @static
   * 获取坐标数组
   * @param {Point | Number[]} point 输入坐标信息
   * @return {Number[]} 坐标数组
   * @example <caption><h7 id='toCoordinates'>获取坐标数组</h7></caption>
   * // ES5引入方式
   * const { Point } = Zondy.Geometry
   * // ES6引入方式
   * import { Point } from "@mapgis/webclient-common"
   * const point = new Point({
   *   coordinates: [100.0, 0.0]
   * })
   * const coordinates = Point.toCoordinates(point)
   */
  static toCoordinates(point) {
    if (!point) throw new Error('输入参数不合法.')
    let coordinates = JSON.parse(JSON.stringify(point))
    if (point instanceof Point) {
      coordinates = point._cloneCoordinates()
    }
    return coordinates
  }

  /**
   * 通过传入的json构造并返回一个新的几何对象
   * @param {Object} [json] JSON对象
   * @example <caption><h7 id='fromJSON'>通过传入的json构造并返回一个新的几何对象</h7></caption>
   * // ES5引入方式
   * const { Point } = Zondy.Geometry
   * // ES6引入方式
   * import { Point } from "@mapgis/webclient-common"
   * const json = {
   *   coordinates: [100.0, 0.0]
   * }
   * const point = new Point.fromJSON(json)
   */
  static fromJSON(json) {
    json = defaultValue(json, {})
    return new Point(json)
  }

  /**
   * 将另一个点对象的所有值复制到当前点对象
   * @param {Point} point 被复制的点对象
   * @return {Point} 修改后的Point
   * @example <caption><h7 id='copy'>将另一个点对象的所有值复制到当前点对象</h7></caption>
   * // ES5引入方式
   * const { Point } = Zondy.Geometry
   * // ES6引入方式
   * import { Point } from "@mapgis/webclient-common"
   * const point1 = new Point({
   *   coordinates: [100.0, 0.0]
   * })
   * const point2 = new Point({
   *   coordinates: [200.0, 10.0]
   * })
   * point1.copy(point2)
   */
  copy(point) {
    // 输入检测
    Point.fromJSON(point.toJSON())
    return this
  }

  /**
   * 计算与另一个点的距离
   * @param {Point|Number[]} point 用于计算距离的点
   * @return {Number} 距离
   * @example <caption><h7 id='distance'>计算与另一个点的距离</h7></caption>
   * // ES5引入方式
   * const { Point } = Zondy.Geometry
   * // ES6引入方式
   * import { Point } from "@mapgis/webclient-common"
   * const point1 = new Point({
   *   coordinates: [100.0, 0.0]
   * })
   * const point2 = new Point({
   *   coordinates: [200.0, 10.0]
   * })
   * const distance = point1.distance(point2)
   */
  distance(point) {
    if (!point) throw new Error('输入参数不合法.')
    // 判断传入point参数的合理性
    const coordinates = Point.toCoordinates(point)
    const width = Math.abs(this.coordinates[0] - coordinates[0])
    const height = Math.abs(this.coordinates[1] - coordinates[1])
    let zlength = 0
    if (this.hasZ && coordinates.length >= 3) {
      zlength = Math.abs(this.coordinates[2] - point.coordinates[[2]])
    }
    return Math.sqrt(
      Math.pow(width, 2) + Math.pow(height, 2) + Math.pow(zlength, 2)
    )
  }

  /**
   * 判断两个点是否相等
   * @param {Point | Array<Number>} [point=null] 点对象或坐标数组
   * @return {Boolean} 两个点是否相等
   * @example <caption><h7 id='equals'>判断两个点是否相等</h7></caption>
   * // ES5引入方式
   * const { Point } = Zondy.Geometry
   * // ES6引入方式
   * import { Point } from "@mapgis/webclient-common"
   * const point1 = new Point({
   *   coordinates: [100.0, 0.0]
   * })
   * const point2 = new Point({
   *   coordinates: [200.0, 10.0]
   * })
   * const equal = point1.equals(point2)
   */
  equals(point) {
    if (point instanceof Point) {
      // 二维点
      if (!this.hasZ) {
        if (
          !!Math.abs(this.coordinates[0] - point.coordinates[0]) <= 10e-8 &&
          !!Math.abs(this.coordinates[1] - point.coordinates[1]) <= 10e-8
        ) {
          return true
        }
        return false
      }
      // 三维点
      else {
        if (
          !!Math.abs(this.coordinates[0] - point.coordinates[0]) <= 10e-8 &&
          !!Math.abs(this.coordinates[1] - point.coordinates[1]) <= 10e-8 &&
          !!Math.abs(this.coordinates[2] - point.coordinates[2]) <= 10e-8
        ) {
          return true
        }
        return false
      }
    }
    // 点数组
    else if (point instanceof Array) {
      // 二维点
      if (point.length === 2) {
        if (
          !!Math.abs(this.coordinates[0] - point[0]) <= 10e-8 &&
          !!Math.abs(this.coordinates[1] - point[1]) <= 10e-8
        ) {
          return true
        }
        return false
      }
      // 三维点
      else if (point.length === 3) {
        if (
          !!Math.abs(this.coordinates[0] - point[0]) <= 10e-8 &&
          !!Math.abs(this.coordinates[1] - point[1]) <= 10e-8 &&
          !!Math.abs(this.coordinates[2] - point[2]) <= 10e-8
        ) {
          return true
        }
        return false
      }
      // 点数量不对
      else {
        throw new Error('坐标数组的数量有误!')
      }
    } else {
      throw new Error('必须为点对象或坐标数组才能比较是否相等!')
    }
  }

  /**
   * 归一化计算
   * @returns {Point} 归一化后的点对象
   * @example <caption><h7 id='normalize'>归一化计算</h7></caption>
   * // ES5引入方式
   * const { Point } = Zondy.Geometry
   * // ES6引入方式
   * import { Point } from "@mapgis/webclient-common"
   * const point1 = new Point({
   *   coordinates: [100.0, 0.0]
   * })
   * const normalize = point1.normalize()
   */
  normalize() {
    let point
    // 开根号计算
    if (this.hasZ) {
      point = new Point({
        coordinates: [0, 0, 0]
      })
    } else {
      point = new Point({
        coordinates: [0, 0]
      })
    }
    const dis = this.distance(point)
    const coordinates = this._cloneCoordinates()
    coordinates[0] /= dis
    coordinates[1] /= dis
    if (this.hasZ) {
      coordinates[2] /= dis
    }
    this.coordinates = coordinates
    return this
  }

  /**
   * 返回如下格式的字符串:"x,y",如果有z,则返回"x,y,z"
   * @returns {String} 字符串
   * @example <caption><h7 id='toString'>返回字符串</h7></caption>
   * // ES5引入方式
   * const { Point } = Zondy.Geometry
   * // ES6引入方式
   * import { Point } from "@mapgis/webclient-common"
   * const point1 = new Point({
   *   coordinates: [100.0, 0.0]
   * })
   * const str = point1.toString()
   */
  toString() {
    if (!this.hasZ) {
      return `${this.coordinates[0]},${this.coordinates[1]}`
    }
    return `${this.coordinates[0]},${this.coordinates[1]},${this.coordinates[2]}`
  }

  /**
   * <a id='toJSON'></a>
   * 导出为json对象
   * @return {Object} json对象
   */
  toJSON() {
    const json = super.toJSON()
    json.coordinates = JSON.parse(JSON.stringify(this.coordinates))
    json.extent = toJSON(this.extent, Extent)

    return json
  }

  /**
   * 返回IGS所对应的GeometryModule型<a id='getIGSType'></a>
   * @returns string GeometryModule型
   */
  getIGSType() {
    if (this.hasZ) {
      return IGSGeometryType.point3D
    }
    return IGSGeometryType.point
  }

  /**
   * 返回igs1.0的几何对象<a id='toOldIGSGeometry'></a>
   * @returns Object igs1.0的几何对象
   */
  toOldIGSGeometry() {
    return {
      PntGeom: [
        {
          Dot: {
            x: this.coordinates[0],
            y: this.coordinates[1]
          }
        }
      ],
      GID: parseInt(String(Math.random() * 10000000))
    }
  }

  /**
   * @function Point.prototype._cloneCoordinates
   * @private
   * @description 拷贝几何信息
   * @returns {Array}
   */
  _cloneCoordinates() {
    return JSON.parse(JSON.stringify(this.coordinates))
  }

  /**
   * @function Point.prototype._calcExtent
   * @private
   * @description 计算外包盒
   * @param {Array} coordinates 坐标点
   * @param {Boolean} hasZ 是否是三维
   * @returns {Extent}
   */
  _calcExtent(coordinates, hasZ) {
    if (!hasZ) {
      return new Extent({
        xmin: coordinates[0],
        ymin: coordinates[1],
        xmax: coordinates[0],
        ymax: coordinates[1]
      })
    }
    return new Extent({
      xmin: coordinates[0],
      ymin: coordinates[1],
      zmin: coordinates[2],
      xmax: coordinates[0],
      ymax: coordinates[1],
      zmax: coordinates[2]
    })
  }

  /**
   * 克隆几何对象
   * @return {Geometry} 克隆后的几何对象
   */
  clone() {
    return new Point(this.toJSON())
  }
}
Object.defineProperties(Point.prototype, {
  /**
   * 外包盒
   * @member {Number} Point.prototype.extent
   * */
  extent: {
    configurable: false,
    get() {
      return this._calcExtent(this.coordinates, this.hasZ)
    }
  }
})

Zondy.Geometry.Point = Point
export default Point
构造函数
成员变量
方法
事件