类名 common/base/geometry/MultiPoint.js
import { defaultValue, isNumber } from '../../util'
import Zondy from '../Zondy'
import Geometry from './Geometry'
import { GeometryType, IGSGeometryType } from '../enum'
import Point from './Point'
import Log from '../../util/Log'
import { calcExtent } from './Utiles'

/**
 * 多点几何对象
 * <br><br>[ES5引入方式]:<br/>
 * Zondy.Geometry.MultiPoint() <br/>
 * [ES6引入方式]:<br/>
 * import { MultiPoint } from "@mapgis/webclient-common" <br/>
 * <br/>
 * @class MultiPoint
 * @moduleEX GeometryModule
 * @extends Geometry
 * @param {Object} options 构造参数
 * @param {Number[]} [options.coordinates = []] 几何点的坐标,支持任意单位,参考示例:<a href='#MultiPoint'>[多点几何对象]</a>
 * @param {SpatialReference} [options.spatialReference = new Zondy.SpatialReference('EPSG:4326')] 几何点的空间参考系,默认4326,当不是4326时请指定坐标系,方便进行投影转换,参考示例:<a href='#SpatialReference'>[指定坐标系]</a>
 * @summary <h5>支持如下方法:</h5>
 * <a href='#addPoint'>[1、添加点坐标]</a><br/>
 * <a href='#getPoint'>[2、返回指定索引处的点]</a><br/>
 * <a href='#removePoint'>[3、从多点中移除一个点]</a><br/>
 * <a href='#setPoint'>[4、更新指定索引处的点]</a><br/>
 * <a href='#getIGSType'>[5、返回IGS所对应的GeometryModule型]</a><br/>
 * <a href='#toOldIGSGeometry'>[6、返回igs1.0的几何对象]</a><br/>
 * <a href='#toString'>[7、返回字符串]</a><br/>
 * <a href='#toDots'>[8、返回Dots对象,仅包括多边形的外圈]</a>
 * <a href='#fromJSON'>[9、通过传入的json构造并返回一个新的几何对象]</a><br/>
 * <a href='#toJSON'>[10、导出为json对象]</a><br/>
 * [11、克隆几何对象]{@link Geometry#clone}
 *
 * @example <caption><h5 id='MultiPoint'>创建几何对象</h5></caption>
 * // ES5引入方式
 * const { MultiPoint } = Zondy.Geometry
 * // ES6引入方式
 * import { MultiPoint } from "@mapgis/webclient-common"
 * new MultiPoint({
 *   coordinates:[
 *     // 第一个点
 *     [100, 0.0],
 *     // 第二个点
 *     [101, 1.0]
 *   ]
 * })
 *
 * @example <caption><h5 id='SpatialReference'>指定坐标系</h5></caption>
 * // ES5引入方式
 * const { Point } = Zondy.Geometry
 * const { SpatialReference } = Zondy
 * // ES6引入方式
 * import { Point, SpatialReference } from "@mapgis/webclient-common"
 * new Point({
 *   // 现在为3857坐标系
 *   coordinates:[
 *     // 第一个点
 *     [12929863.44711455, 3934286.575385226],
 *     // 第二个点
 *     [12060733.232006868, 3377247.5680546067]
 *   ],
 *   // 当不是4326时请指定坐标系,方便进行投影转换
 *   spatialReference: new SpatialReference('EPSG:3857')
 * })
 */

class MultiPoint extends Geometry {
  constructor(options) {
    super(options)
    // 构造几何对象
    options = defaultValue(options, {})
    /**
     * 几何点的坐标
     * @member {Array} MultiPoint.prototype.coordinates
     */
    this.coordinates = defaultValue(options.coordinates, [])
    if (!Array.isArray(this.coordinates)) {
      throw new Error('坐标必须是数组')
    }
    for (let i = 0; i < this.coordinates.length; i++) {
      if (this.coordinates[i].length < 2 || this.coordinates[i].length > 3) {
        throw new Error('多点的数组长度必须在合理范围内(2或者3)')
      }
      // 确保所有点都有第三个值才是有z值
      if (this.coordinates[i].length === 3) {
        this.hasZ = true
      } else {
        this.hasZ = false
      }
    }
    this.type = GeometryType.multiPoint
  }

  /**
   * 添加点坐标,可以为Point对象、[x, y, z(可选)]数组、Point对象数组、[x, y, z(可选)]对象数组
   * @param {Point|Number[]} point 可以是point,也可以是表示XY坐标的数字数组
   * @param {String} [mode = 'add'] 添加模式,add:正常添加,deduplication:去除重复点
   * @return {MultiPoint} 多点对象
   * @example <caption><h7 id='addPoint'>添加点坐标</h7></caption>
   * // ES5引入方式
   * const { MultiPoint } = Zondy.Geometry
   * // ES6引入方式
   * import { MultiPoint } from "@mapgis/webclient-common"
   * const multiPoint = new MultiPoint({
   *   coordinates:[
   *     // 第一个点
   *     [100, 0.0],
   *     // 第二个点
   *     [101, 1.0]
   *   ]
   * })
   * multiPoint.addPoint([102,2.0])
   */
  addPoint(point, mode) {
    mode = defaultValue(mode, 'add')
    // 是点几何对象
    if (point instanceof Point) {
      this._addPoint(point, mode)
    }
    // 添加数组
    else if (point instanceof Array) {
      // 判断是否是点坐标数组
      const isNumber = this._isNumberArray(point)

      // 是点坐标数组
      if (isNumber) {
        // 添加点
        this._addPoint(
          new Point({
            coordinates: point
          }),
          mode
        )
      }
      // 不是点坐标数组
      else {
        for (let i = 0; i < point.length; i++) {
          // 是点几何对象
          if (point[i] instanceof Point) {
            this._addPoint(point[i], mode)
          }
          // 是数组
          else if (point[i] instanceof Array) {
            const pointIsNumber = this._isNumberArray(point[i])
            // 是点坐标对象
            if (pointIsNumber) {
              // 添加点
              this._addPoint(
                new Point({
                  coordinates: point[i]
                }),
                mode
              )
            }
            // 点几何对象
            else if (point instanceof Point) {
              this._addPoint(point[i], mode)
            }
            // 不是点坐标对象或点几何
            else {
              Log.error('point的子元素类型不是点几何对象或者点坐标数组!')
            }
          } else {
            Log.error('point的类型不是点几何对象或者点坐标数组!')
          }
        }
      }
    } else {
      Log.error('point的类型不是点几何对象或者点坐标数组!')
    }

    // 更新外包盒
    if (this.coordinates.length >= 2) {
      this.extent = this._calcExtent(this.coordinates, this.hasZ)
    }
    this.coordinates = JSON.parse(JSON.stringify(this.coordinates))
    return this
  }

  /**
   * 添加点坐标
   * @private
   * @param {Point} point 点几何对象
   * @param {String} [mode = 'add'] 添加模式,add:正常添加,deduplication:去除重复点
   * */
  _addPoint(point, mode) {
    // 添加模式
    if (mode === 'add') {
      this.coordinates.push(point.coordinates)
    }
    // 去重添加模式
    else if (!this.contains(point)) {
      this.coordinates.push(point.coordinates)
    }
  }

  /**
   * 判断是否是点坐标数组
   * @private
   * @param {Array} point 要判断的对象
   * @return {Boolean} 是否是点坐标数组
   * */
  _isNumberArray(point) {
    // 先判断类型
    if (!(point instanceof Array)) {
      return false
    }

    // 判断是否是点坐标数组
    let isNumber = true
    if (point.length === 2 || point.length === 3) {
      for (let i = 0; i < point.length; i++) {
        if (!(typeof point[i] === 'number')) {
          isNumber = false
          break
        }
      }
    } else {
      isNumber = false
    }

    return isNumber
  }

  /**
   * 判断多点对象是否包含某一个点
   * @param {Point | Array<Number>} point 点对象
   * @return {Boolean} 是否包含
   * */
  contains(point) {
    // 点对象
    if (point instanceof Point) {
      for (let i = 0; i < this.coordinates.length; i++) {
        if (point.equals(this.coordinates[i])) {
          return true
        }
      }
    }
    // 坐标数组
    else if (
      point instanceof Array &&
      (point.length === 2 || point.length === 3)
    ) {
      for (let i = 0; i < this.coordinates.length; i++) {
        const _point = new Point({
          coordinates: point
        })
        if (_point.equals(this.coordinates[i])) {
          return true
        }
      }
    } else {
      Log.error('必须为点对象或坐标数组才能比较是否相等!')
    }

    return false
  }

  /**
   * 返回指定索引处的点
   * @param {Number} index 点对象
   * @return {Point} 在指定下标处的点
   * @example <caption><h7 id='getPoint'>返回指定索引处的点</h7></caption>
   * // ES5引入方式
   * const { MultiPoint } = Zondy.Geometry
   * // ES6引入方式
   * import { MultiPoint } from "@mapgis/webclient-common"
   * const multiPoint = new MultiPoint({
   *   coordinates:[
   *     // 第一个点
   *     [100, 0.0],
   *     // 第二个点
   *     [101, 1.0]
   *   ]
   * })
   * const point = multiPoint.getPoint(0)
   */
  getPoint(index) {
    index = Number(index)
    if (!this._isValidate(index)) {
      throw new Error('未找到指定索引,请输入正确的索引')
    }
    const points = this._cloneCoordinates()
    return new Point({
      coordinates: points[index]
    })
  }

  /**
   * 从多点中移除一个点。索引指定要删除的点
   * @param {Number} index 点对象
   * @return {Point | null} 返回被移除的点
   * @example <caption><h7 id='removePoint'>从多点中移除一个点</h7></caption>
   * // ES5引入方式
   * const { MultiPoint } = Zondy.Geometry
   * // ES6引入方式
   * import { MultiPoint } from "@mapgis/webclient-common"
   * const multiPoint = new MultiPoint({
   *   coordinates:[
   *     // 第一个点
   *     [100, 0.0],
   *     // 第二个点
   *     [101, 1.0]
   *   ]
   * })
   * const point = multiPoint.removePoint(0)
   */
  removePoint(index) {
    index = Number(index)
    if (!this._isValidate(index)) return null
    const points = this._cloneCoordinates()
    this.coordinates.splice(index, 1)
    this.coordinates = JSON.parse(JSON.stringify(this.coordinates))
    return new Point({
      coordinates: points[index]
    })
  }

  /**
   * 更新指定索引处的点
   * @param {Number} index 点在points属性中的索引
   * @param {Point|Number[]} point 在新位置指定的几何点
   * @return {MultiPoint} 返回更新后的多点
   * @example <caption><h7 id='setPoint'>更新指定索引处的点</h7></caption>
   * // ES5引入方式
   * const { MultiPoint } = Zondy.Geometry
   * // ES6引入方式
   * import { MultiPoint } from "@mapgis/webclient-common"
   * const multiPoint = new MultiPoint({
   *   coordinates:[
   *     // 第一个点
   *     [100, 0.0],
   *     // 第二个点
   *     [101, 1.0]
   *   ]
   * })
   * multiPoint.setPoint(1, [103, 3.0])
   */
  setPoint(index, point) {
    if (!this._isValidate(index)) {
      throw new Error('未找到指定索引,请输入正确的索引')
    }
    const points = this._cloneCoordinates()
    if (Array.isArray(points)) {
      points.splice(index, 1, Point.toCoordinates(point))
      this.coordinates = points
    }
    this.coordinates = JSON.parse(JSON.stringify(this.coordinates))
    return this
  }

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

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

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

    return json
  }

  /**
   * 返回igs1.0的几何对象<a id='toOldIGSGeometry'></a>
   * @returns Object igs1.0的几何对象
   */
  toOldIGSGeometry() {
    const PntGeom = []
    for (let i = 0; i < this.coordinates.length; i++) {
      let Dot = {}
      Dot = {
        x: this.coordinates[i][0],
        y: this.coordinates[i][1]
      }
      PntGeom.push({
        Dot,
        GID: i
      })
    }
    return {
      PntGeom
    }
  }

  /**
   * 返回如下格式的字符串:"x0,y0,x1,y1,x2,y2"<a id='toString'></a>
   * @returns string
   */
  toString() {
    let _str = ''
    // 开始循环线内的点
    for (let i = 0; i < this.coordinates.length; i++) {
      // 线内的点
      const _point = this.coordinates[i]
      // 有可能是xyz,因此for循环
      for (let k = 0; k < _point.length; k++) {
        // 加,号
        _str += `${_point[k]},`
      }
    }
    // 删除最后一个,
    _str = _str.substring(0, _str.length - 1)
    return _str
  }

  /**
   * 返回Dots对象,仅包括多边形的外圈<a id='toDots'></a>
   * @returns Array Dots对象
   */
  toDots() {
    const Dots = []
    const coordinates = this.coordinates

    for (let i = 0; i < coordinates.length; i++) {
      Dots.push({
        x: coordinates[i][0],
        y: coordinates[i][1]
      })
    }

    return Dots
  }

  /**
   * @function MultiPoint.prototype._isValidate
   * @private
   * @description 判断输入是否合法
   * @param {Number} pointIndex 数组下标
   * @returns {Boolean} 输入是否合法
   */
  _isValidate(pointIndex) {
    const len = this.coordinates.length
    if (!isNumber(pointIndex) || pointIndex < 0 || pointIndex >= len) {
      return false
    }
    return true
  }

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

  /**
   * @function LineString.prototype._calcExtent
   * @private
   * @description 计算外包盒
   * @param {Array} coordinates 坐标点
   * @param {Boolean} hasZ 是否是三维
   * @returns {Extent}
   */
  _calcExtent(coordinates, hasZ) {
    if (coordinates.length > 1) {
      return calcExtent(coordinates, hasZ, GeometryType.lineString)
    }
  }

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

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