类名 common/base/feature/FeatureSet.js
import Zondy from '../Zondy'
import { getGUID, defaultValue } from '../../util'
import Feature from './Feature'
import {
  Geometry,
  LineString,
  MultiLineString,
  MultiPoint,
  MultiPolygon,
  Point,
  Polygon,
  SpatialReference
} from '../geometry'
import { GeometryType } from '../enum'
import Projection from '../Projection'

function transformToWGS84(spatialReference, geo) {
  if (!geo) return geo
  if (spatialReference && spatialReference.wkid !== '4326') {
    geo.spatialReference = spatialReference
    return Projection.project(geo, new SpatialReference('EPSG:4326'))
  }
  return geo
}

function coordsToLatLng(coords) {
  if (coords.length > 2) {
    return [coords[1], coords[0], coords[2]]
  }
  return [coords[1], coords[0]]
}

function coordsToLatLngs(coords, levelsDeep, _coordsToLatLng) {
  const latlngs = []

  for (let i = 0, len = coords.length, latlng; i < len; i++) {
    latlng = levelsDeep
      ? coordsToLatLngs(coords[i], levelsDeep - 1, _coordsToLatLng)
      : (_coordsToLatLng || coordsToLatLng)(coords[i])

    latlngs.push(latlng)
  }

  return latlngs
}

/**
 * 要素集合类,示例如下:<a id='add-FeatureSet'>[初始化要素集合对象]</a>
 * <br><br>[ES5引入方式]:<br/>
 * Zondy.FeatureSet() <br/>
 * [ES6引入方式]:<br/>
 * import { FeatureSet } from "@mapgis/webclient-common" <br/>
 * <br/>
 * @class FeatureSet
 * @moduleEX FeatureModule
 * @param {Object} options 构造参数
 * @param {String} [options.id] 要素集合的id,不给则给一个随机的默认id
 * @param {Array} [options.features = []] 要素数组
 * @param {SpatialReference} [options.spatialReference = new Zondy.SpatialReference('EPSG:4326')] 要素集合的参考系,
 * 默认为4326,当要素的几何对象上制定了参考系时,用要素上的;当要素的集合对象上没有指定参考系时,则使用该参考系
 *
 * @summary <h5>支持如下方法:</h5>
 * <a href='#findFeatureById'>[1、通过ID寻找要素]</a><br/>
 * <a href='#fromGeoJSON'>[3、从geoJSON对象中导入数据]</a><br/>
 * <a href='#toGeoJSON'>[4、导出为GeoJSON数据]</a><br/>
 * <a href='#fromArray'>[5、将一个要素数组对象转为要素集合]</a><br/>
 * <a href='#toJSON'>[6、导出一个JSON对像]</a><br/>
 * <a href='#clone'>[7、克隆并返回新的要素集合]</a><br/>
 * <a href='#static fromJSON'>[8、通过传入的json构造并返回一个新的几何对象]</a><br/>
 *
 * @example <caption><h7 id='add-FeatureSet'>初始化要素集合对象</h7></caption>
 * // ES5引入方式
 * const { FeatureSet } = Zondy
 * // ES6引入方式
 * import { FeatureSet } from "@mapgis/webclient-common"
 * //创建要素集合
 * let featureSet = new FeatureSet({
 *   //不填则创建一个随机的guid
 *   id: "你的id",
 *   //要素集合
 *   features: []
 * })
 */
class FeatureSet {
  constructor(options) {
    options = defaultValue(options, {})
    /**
     * 要素集合的id
     * default 随机id
     * @member {String} FeatureSet.prototype.id
     */
    this.id = defaultValue(options.id, getGUID())
    /**
     * 要素数组
     * default []
     * @member {Array} FeatureSet.prototype.features
     */
    this.features = defaultValue(options.features, [])
    /**
     * 要素集合的参考系
     * default new Zondy.SpatialReference('EPSG:4326')
     * @member {SpatialReference} FeatureSet.prototype.spatialReference
     */
    this.spatialReference = defaultValue(
      options.spatialReference,
      new SpatialReference('EPSG:4326')
    )
    /**
     * 该类的类型
     * default FeatureCollection
     * @readonly
     * @member {String} FeatureSet.prototype.type
     */
    this.type = 'FeatureCollection'
  }

  /**
   * 通过ID寻找要素,若没有id,则在要素的attributes中查找<a id='findFeatureById'></a>
   * @param id 要素ID
   * @return Feature 要素对象
   * @example <caption><h7 id='findFeatureById'>通过ID寻找要素</h7></caption>
   * let feature = featureSet.findFeatureById("要素id");
   */
  findFeatureById(id) {
    let feature = undefined
    for (let i = 0; i < this.features.length; i++) {
      if (this.features[i].id === id || this.features[i].attributes.id === id) {
        feature = this.features[i]
        break
      }
    }

    return feature
  }

  /**
   * 导出一个JSON对像<a id='toJSON'></a>
   * @return {Object} JSON对像
   */
  toJSON() {
    const json = {}
    json.id = this.id
    json.features = this.features.map((feature) => feature.toJSON())
    json.spatialReference = this.spatialReference.toJSON()
    json.type = this.type
    return json
  }

  /**
   * 克隆并返回新的要素集合<a id='clone'></a>
   * @return {FeatureSet} 克隆后的新要素集合
   * @example <caption><h7>克隆并返回新的要素集合</h7></caption>
   * let newFeatureSet = featureSet.clone();
   */
  clone() {
    const newFeatureSet = new FeatureSet()
    for (let i = 0; i < this.features.length; i++) {
      newFeatureSet.features.push(this.features[i].clone())
    }
    return newFeatureSet
  }

  /**
   * 通过传入的json构造并返回一个新的几何对象<a id='fromJSON'></a>
   * @param {Object} json JSON对象
   * @example <caption><h7 id='fromJSON'>通过传入的json构造并返回一个新的几何对象</h7></caption>
   * // ES5引入方式
   * const { FeatureSet } = Zondy
   * // ES6引入方式
   * import { FeatureSet } from "@mapgis/webclient-common"
   * const json = {
   *   //不填则创建一个随机的guid
   *   id: "你的id",
   *   //要素集合
   *   features: []
   * }
   * const featureSet = new FeatureSet.fromJSON(json)
   */
  static fromJSON(json) {
    json = defaultValue(json, {})
    return new FeatureSet(json)
  }

  /**
   * 从geoJSON对象中导入数据,若要素id相同,则会覆盖当前要素的值<a id='fromGeoJSON'></a>
   * @param geoJSON geoJSON数据
   * @example <caption><h7>从geoJSON对象中导入数据</h7></caption>
   * //数据格式参考https://geojson.org/
   * FeatureSet.fromGeoJSON({
   *   //类型必须为FeatureCollection
   *   type: "FeatureCollection",
   *   //要素集合
   *   features: []
   * })
   */
  static fromGeoJSON(geoJSON) {
    geoJSON = defaultValue(geoJSON, {})
    const { type } = geoJSON
    const features = []
    let spatialReference = new SpatialReference('EPSG:4326')
    // 获取要素集空间参考系
    if (geoJSON.crs && geoJSON.crs.properties && geoJSON.crs.properties.name) {
      spatialReference = new SpatialReference({
        wkt: geoJSON.crs.properties.name
      })
    }
    // 是一个要素collection
    if (type === 'FeatureCollection') {
      // 从features导入数据
      const geoFeatures = defaultValue(geoJSON.features, [])
      for (let i = 0; i < geoFeatures.length; i++) {
        // 确保有属性
        geoFeatures[i].properties = defaultValue(geoFeatures[i].properties, {})
        // 获取要素id
        const id = geoFeatures[i].id || geoFeatures[i].properties.id
        // 在当前要素集合中查找要素
        const feature = new Feature({
          id,
          attributes: JSON.parse(JSON.stringify(geoFeatures[i].properties)),
          geometry: Geometry.fromGeoJSON(geoFeatures[i].geometry)
        })
        feature.geometry.spatialReference = spatialReference.clone()
        features.push(feature)
      }
    }
    // 还需要考虑GeometryCollection情况,时间不够,先留着
    return new FeatureSet({
      spatialReference,
      features
    })
  }

  /**
   * 导出为GeoJSON数据<a id='toGeoJSON'></a>
   * @return {Object} geoJSON数据
   * @example <caption><h7>导出为GeoJSON数据</h7></caption>
   * let geojson = featureSet.toGeoJSON();
   */
  toGeoJSON() {
    const featureSet = {
      type: 'FeatureCollection',
      features: []
    }
    for (let i = 0; i < this.features.length; i++) {
      const feature = this.features[i].clone()
      if (feature.geometry) {
        const geometryJSON = transformToWGS84(
          feature.geometry.spatialReference,
          feature.geometry
        )
        feature.geometry = Geometry.fromGeoJSON(geometryJSON)
      }
      featureSet.features.push(feature.toGeoJSON())
    }
    return featureSet
  }

  /**
   * 将一个要素数组对象转为要素集合<a id='fromArray'></a>
   * @param {Array<Object>|FeatureSet} features
   * @param {FeatureSet} featureSet
   * @return FeatureSet 新的featureSet对象
   * @example <caption><h7>将一个要素数组对象转为要素集合</h7></caption>
   * // ES5引入方式
   * const { FeatureSet } = Zondy
   * // ES6引入方式
   * import { FeatureSet } from "@mapgis/webclient-common"
   * featureSet = FeatureSet.fromArray(
   *   [
   *   {
   *     "attributes": {
   *       "FID": 650,
   *       "CODE": "420000",
   *       "CAPNAME": "武汉市",
   *       "mpPerimeter": "38.93733309514808",
   *       "mpArea": "17.556751278282672",
   *       "mpLayer": "18",
   *       "NAME": "湖北省"
   *     },
   *     "geometry": {
   *       "type": "Polygon",
   *       "coordinates": [[]]
   *     },
   *     "symbol": null
   *   }
   * ],
   *   new FeatureSet()
   * )
   */
  static fromArray(features, featureSet, options) {
    options = defaultValue(options, {})
    const reverse = options.reverse
    let featureArr = defaultValue(features, [])
    if (features instanceof FeatureSet) {
      featureArr = defaultValue(features.features, [])
    }
    featureSet = defaultValue(featureSet, new FeatureSet())
    for (let i = 0; i < featureArr.length; i++) {
      // 确保是对象
      if (featureArr[i] && featureArr[i] instanceof Object) {
        let geometry
        // 确保有这些属性
        if (
          featureArr[i].geometry &&
          featureArr[i].geometry instanceof Object &&
          featureArr[i].geometry.type &&
          featureArr[i].geometry.coordinates
        ) {
          switch (featureArr[i].geometry.type) {
            case GeometryType.point: {
              let coordinates = featureArr[i].geometry.coordinates
              if (reverse) {
                coordinates = coordsToLatLng(coordinates)
              }
              geometry = new Point({
                coordinates
              })
              break
            }
            case GeometryType.multiPoint: {
              let coordinates = featureArr[i].geometry.coordinates
              if (reverse) {
                coordinates = coordsToLatLngs(coordinates, 0, coordsToLatLng)
              }
              geometry = new MultiPoint({
                coordinates
              })
              break
            }
            case GeometryType.lineString: {
              let coordinates = featureArr[i].geometry.coordinates
              if (reverse) {
                coordinates = coordsToLatLngs(coordinates, 0, coordsToLatLng)
              }
              geometry = new LineString({
                coordinates
              })
              break
            }
            case GeometryType.multiLineString: {
              let coordinates = featureArr[i].geometry.coordinates
              if (reverse) {
                coordinates = coordsToLatLngs(coordinates, 1, coordsToLatLng)
              }
              geometry = new MultiLineString({
                coordinates
              })
              break
            }
            case GeometryType.polygon: {
              let coordinates = featureArr[i].geometry.coordinates
              if (reverse) {
                coordinates = coordsToLatLngs(coordinates, 1, coordsToLatLng)
              }
              geometry = new Polygon({
                coordinates
              })
              break
            }
            case GeometryType.multiPolygon: {
              let coordinates = featureArr[i].geometry.coordinates
              if (reverse) {
                coordinates = coordsToLatLngs(coordinates, 2, coordsToLatLng)
              }
              geometry = new MultiPolygon({
                coordinates
              })
              break
            }
            default:
              geometry = {}
          }
        }
        // 创建要素
        const feature = new Feature({
          attributes: features[i].attributes,
          geometry,
          symbol: features[i].symbol,
          additional: features[i].additional
        })
        feature.geometry.spatialReference = featureSet.spatialReference.clone()

        featureSet.features.push(feature)
      }
    }

    return featureSet
  }
}

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