类名 common/document/layer/GeoJSONLayer.js
import * as T from '@turf/turf'
import Layer from './baseLayer/Layer'
import { Zondy, ElevationInfo, FeatureSet } from '../../base'
import { GeometryType, LayerType, LoadStatus } from '../../base/enum'
import { defaultValue, Log, toJSON } from '../../util'
import { Extent, Geometry, SpatialReference } from '../../base/geometry'
import FetchRequest from '../../service/FetchRequest'
import { BaseRenderer } from '../renderer'
import { jsonClone } from '../../util/Utils'

/**
 * geojson图层,仅支持文件服务<br/>
 * 目前二维和三维上支持4326(包括4490,4214以及4610),3857以及EPSG支持的自定义坐标系,若是想要绘制源数据为非4326坐标系几何数据,需要在初始化时指定具体坐标系<br/>
 * 参考示例:<br/>
 * <a href='#add-GeoJSONLayer'>[加载GeoJSON图层]</a>
 * <br><br>[ES5引入方式]:<br/>
 * Zondy.Layer.GraphicsLayer() <br/>
 * [ES6引入方式]:<br/>
 * import { GraphicsLayer } from "@mapgis/webclient-common"
 * @class GeoJSONLayer
 * @moduleEX LayerModule
 * @extends Layer
 * @fires Layer#图层加载完毕事件
 * @fires Layer#图层销毁完毕事件
 * @fires Layer#图层更新完毕事件
 * @fires Layer#图层显隐更新完毕事件
 * @fires Layer#图层透明度更新完毕事件
 * @fires Layer#图层刷新完毕事件
 * @fires GeoJSONLayer#图层样式更新完毕事件
 * @param {Object} options 构造参数
 * @param {String} [options.url] 服务基地址,仅支持文件服务:<br/>
 * 参考示例:<br/>
 * <a href='#add-GeoJSONLayer'>[1、加载GeoJSON图层]</a><br/>
 * <a href='#add-GeoJSONLayer-custom'>[2、加载自定义坐标系的GeoJSON图层]</a><br/>
 * <a href='#removeLayer'>[3、删除GeoJSON图层]</a>
 * @param {String} [options.id] 图层id,若不填则给一个随机id
 * @param {Boolean} [options.visible = show] 图层可见性,参考示例:<a href='#visible'>[图层可见性]</a>
 * @param {Number} [options.opacity = 1] 图层透明度,0~1之间的值,0完全透明,1不透明,参考示例:<a href='#opacity'>[图层透明度]</a>
 * @param {SpatialReference} [options.spatialReference] 图层坐标系,默认为4326,若是想要绘制源数据为非4326坐标系几何数据,需要在初始化时指定具体坐标系
 * @param {BaseRenderer} [options.renderer] 渲染样式,<br>
 * 目前支持如下样式:<br/>
 * [1、单值专题图]{@link UniqueValueRenderer}<br/>
 * [2、分段专题图]{@link ClassBreakRenderer}<br/>
 * [3、统一专题图]{@link SimpleRenderer}<br/>
 * 参考示例:<br/>
 * <a href='#UniqueValueRenderer'>[1、单值专题图]</a><br/>
 * <a href='#ClassBreakRenderer'>[2、分段专题图]</a><br/>
 * <a href='#SimpleRenderer'>[3、统一专题图]</a><br/>
 * @param {Boolean} [options.labelsVisible = false] 是否开启动态注记,仅支持三维动态注记渲染
 * @param {Array<LabelClass>} [options.labelingInfo = []] 注记样式数组,可以和renderer同时启用,默认取数组的第一个样式,
 * 仅支持三维场景,参考示例:<a href='#add-labelingInfo'>[注记样式]</a><br/>
 * @param {ElevationInfo} [options.elevationInfo] 设置高程参数,指定几何是否贴地、贴模型、都贴、都不贴或者是绝对高度
 * @param {Number} [options.minScale = 0] 最小缩放级数,仅会请求级数大于等于minScale的图片
 * @param {Number} [options.maxScale = 19] 最大缩放级数,仅会请求级数小于等于maxScale的图片
 * @param {String} [options.tokenKey = 'token'] token名
 * @param {String} [options.tokenValue] token值,只有当tokenValue存在时,才会绑定token
 * @param {Object} [options.customParameters = {}] 自定义查询参数,键值对,填写该参数后,会在数据请求的来链接后面以&key=value的形式拼接字符串
 *
 * @summary <h5>支持如下方法:</h5>
 * <a href='#load'>[1、加载图层资源]</a><br/>
 * <a href='#destroy'>[2、销毁图层]</a><br/>
 * <a href='#queryFeatures'>[3、返回所有要素]</a><br/>
 * <a href='#queryFeaturesCount'>[4、查询要素数量]</a><br/>
 * <a href='#fromJSON'>[5、通过传入的json构造并返回一个新的几何对象]</a><br/>
 * [6、导出为json对象]{@link OGCLayer#toJSON}<br/>
 * [7、克隆几何对象]{@link OGCLayer#clone}
 *
 * @example <caption><h7 id='add-GeoJSONLayer'>初始化GeoJSON图层</h7></caption>
 * // 初始化图层管理容器
 * // ES5引入方式
 * const { Map,MapView } = Zondy
 * const { GeoJSONLayer } = Zondy.Layer
 * // ES6引入方式
 * import { Map,MapView,GeoJSONLayer } from "@mapgis/webclient-common"
 * const map = new Zondy.Map();
 * // 初始化地图视图对象
 * const mapView = new MapView({
 *   // 视图id
 *   viewId: "viewer-id",
 *   // 图层管理容器
 *   map: map
 * });
 * // 创建图层
 * const geojsonLayer = new GeoJSONLayer({
 *   // 服务基地址
 *   url: 'json数据地址'
 * })
 * // 添加图层
 * map.add(geojsonLayer)
 *
 * @example <caption><h7 id='add-GeoJSONLayer-custom'>加载自定义坐标系的GeoJSON图层</h7></caption>
 * // ES5引入方式
 * const { Map,MapView,SpatialReference} = Zondy
 * const { GeoJSONLayer } = Zondy.Layer
 * // ES6引入方式
 * import { Map,MapView,SpatialReference,GeoJSONLayer } from "@mapgis/webclient-common"
 * // 初始化图层管理容器
 * const map = new Map();
 * // 初始化地图视图对象
 * const mapView = new MapView({
 *   // 视图id
 *   viewId: "viewer-id",
 *   // 图层管理容器
 *   map: map
 * });
 * // 创建图层
 * const geojsonLayer = new GeoJSONLayer({
 *   // 服务基地址
 *   url: 'json数据地址',
 *   // 设置坐标系
 *   spatialReference: new SpatialReference({
 *     wkid: '坐标系的wkid'
 *   })
 * })
 * // 添加图层
 * map.add(geojsonLayer)
 *
 * @example <caption><h7 id='removeLayer'>删除GeoJSON图层</h7></caption>
 * // 删除图层
 * map.remove(geojsonLayer)
 *
 * @example <caption><h7 id='UniqueValueRenderer'>单值专题图-区数据</h7></caption>
 * // ES5引入方式
 * const { Map,MapView,Color} = Zondy
 * const { GeoJSONLayer } = Zondy.Layer
 * const { UniqueValueRenderer } = Zondy.Renderer
 * const { SimpleLineSymbol,SimpleFillSymbol } = Zondy.Symbol
 * // ES6引入方式
 * import { Map,MapView,Color,GeoJSONLayer,UniqueValueRenderer,SimpleLineSymbol,SimpleFillSymbol } from "@mapgis/webclient-common"
 * // 初始化图层管理容器
 * const map = new Map();
 * // 初始化地图视图对象
 * const mapView = new MapView({
 *   // 视图id
 *   viewId: "viewer-id",
 *   // 图层管理容器
 *   map: map
 * });
 * // 创建图层
 * const geojsonLayer = new GeoJSONLayer({
 *   // 服务基地址
 *   url: 'json数据地址',
 *   // 渲染样式 - 单值专题图 - 区数据
 *   renderer: new UniqueValueRenderer({
 *     //字段名
 *     field: '你的字段名',
 *     // 默认符号,不在专题图指定范围内的会采用该样式,可不设置
 *     defaultSymbol: new Zondy.Symbol.SimpleFillSymbol({
 *       // 填充颜色
 *       color: Zondy.Color(1 , 1, 252),
 *       // 外边线样式
 *       outline: new SimpleLineSymbol({
 *         //线颜色
 *         color: new Color(255, 1, 0, 1),
 *         //线宽
 *         width: 1
 *       })
 *     }),
 *     //单值专题图字段样式
 *     uniqueValueInfos: [{
 *       //指定字段值
 *       value: "指定的值",
 *       //匹配到该值后的样式
 *       symbol: new SimpleFillSymbol({
 *         // 填充颜色
 *         color: Color(1, 1, 252),
 *         // 外边线样式
 *         outline: new SimpleLineSymbol({
 *           //线符号颜色
 *           color: new Color(255, 1, 0),
 *           //线宽
 *           width: 1
 *           })
 *         })
 *       },{
 *         //指定字段值
 *         value: "指定的值",
 *         //匹配到该值后的样式
 *         symbol: new SimpleFillSymbol({
 *           // 填充颜色
 *           color: new Color(211, 111, 11, 1)
 *         })
 *       }]
 *   })
 * })
 * map.add(geojsonLayer)
 *
 * @example <caption><h7 id='ClassBreakRenderer'>分段专题图-区数据</h7></caption>
 * // ES5引入方式
 * const { Map,MapView,Color} = Zondy
 * const { GeoJSONLayer } = Zondy.Layer
 * const { ClassBreakRenderer } = Zondy.Renderer
 * const { SimpleLineSymbol,SimpleFillSymbol } = Zondy.Symbol
 * // ES6引入方式
 * import { Map,MapView,Color,GeoJSONLayer,ClassBreakRenderer, SimpleLineSymbol,SimpleFillSymbol} from "@mapgis/webclient-common"
 * // 初始化图层管理容器
 * const map = new Map();
 * // 初始化地图视图对象
 * const mapView = new MapView({
 *   // 视图id
 *   viewId: "viewer-id",
 *   // 图层管理容器
 *   map: map
 * });
 * // 创建图层
 * const geojsonLayer = new GeoJSONLayer({
 *   // 服务基地址
 *   url: 'json数据地址',
 *   // 渲染样式 - 分段专题图 - 区数据
 *   renderer: new ClassBreakRenderer({
 *      //字段名
 *      field: '你的字段名',
 *      // 指定默认样式,不在专题图指定范围内的会采用该样式,可不设置
 *      defaultSymbol:  new SimpleFillSymbol({
 *        // 填充颜色
 *        color: new Color(222, 1, 252),
 *        // 线符号样式
 *        outline: new SimpleLineSymbol({
 *          //线符号颜色
 *          color: new Zondy.Color(255, 1, 0),
 *          //线宽
 *          width: 1
 *        })
 *      }),
 *      //分段专题图字段样式
 *      classBreakInfos: [{
 *        // 最大范围
 *        maxValue: "最大范围",
 *        // 最小范围
 *        minValue: "最小范围",
 *        //匹配到该值后的样式
 *        symbol:  new SimpleFillSymbol({
 *          // 填充颜色
 *          color: new Color(1, 1, 252),
 *          // 线符号样式
 *          outline: new SimpleLineSymbol({
 *            //线符号颜色
 *            color: new Color(255, 1, 0, 1),
 *            //线宽
 *            width: 1
 *          })
 *        })
 *      }]
 *    })
 * })
 * map.add(geojsonLayer)
 *
 * @example <caption><h7 id='SimpleRenderer'>统一专题图-区数据</h7></caption>
 * // ES5引入方式
 * const { GeoJSONLayer } = Zondy.Layer
 * const { SimpleRenderer } = Zondy.Renderer
 * const { SimpleFillSymbol,SimpleLineSymbol } = Zondy.Symbol
 * const { Color } = Zondy
 * // ES6引入方式
 * import { Color,GeoJSONLayer, SimpleRenderer,SimpleLineSymbol,SimpleFillSymbol} from "@mapgis/webclient-common"
 * const geojsonLayer = new GeoJSONLayer({
 *   // 服务基地址
 *   url: 'json数据地址',
 *   // 渲染样式 - 统一专题图 - 区数据
 *   renderer: new SimpleRenderer({
 *     // 设置几何的样式
 *     symbol: new SimpleFillSymbol({
 *       //线符号颜色
 *       color: randomColor(),
 *       // 外边线样式
 *       outline: new SimpleLineSymbol({
 *         // 外边线颜色
 *         color: new Color(1, 1, 252),
 *         // 外边线宽度
 *         width: 1
 *       })
 *     })
 *   })
 * })
 * map.add(geojsonLayer)
 *
 * @example <caption><h7 id='index'>设置图层顺序</h7></caption>
 * // 加载完毕后,更改图层顺序
 * map.reorder(geojsonLayer, '要移动到的index');
 *
 * @example <caption><h7 id='visible'>图层可见性</h7></caption>
 * // 创建图层
 * // ES5引入方式
 * const { GeoJSONLayer } = Zondy.Layer
 * // ES6引入方式
 * import { GeoJSONLayer } from "@mapgis/webclient-common"
 * const geojsonLayer = new GeoJSONLayer({
 *   // 服务基地址
 *   url: 'json数据地址',
 *   // 设置图层可见性
 *   visible: true
 * })
 *
 * // 图层加载完成后,设置可见性
 * geojsonLayer.visible = !geojsonLayer.visible
 *
 * @example <caption><h7 id='opacity'>图层透明度</h7></caption>
 * // 创建图层
 *  // ES5引入方式
 * const { GeoJSONLayer } = Zondy.Layer
 * // ES6引入方式
 * import { GeoJSONLayer } from "@mapgis/webclient-common"
 * const geojsonLayer = new Zondy.Layer.GeoJSONLayer({
 *   // 服务基地址
 *   url: 'json数据地址',
 *   // 设置图层透明度
 *   opacity: 1
 * })
 *
 * // 图层加载完成后,设置可见性
 * geojsonLayer.opacity = 0.5
 *
 * @example <caption><h7 id='add-labelingInfo'>启用注记</h7></caption>
 *  // ES5引入方式
 * const { LabelClass,Font } = Zondy
 * const { TextSymbol } = Zondy.Symbol
 * const { GeoJSONLayer } = Zondy.Layer
 * // ES6引入方式
 * import { LabelClass,Font,TextSymbol,GeoJSONLayer } from "@mapgis/webclient-common"
 * // 初始化LabelClass
 * const labelClass = new LabelClass({
 *   // 指定文本符号样式
 *   symbol: new TextSymbol({
 *     // 文字颜色
 *     color: new Color(252, 100, 22, 1),
 *     // 文字样式
 *     font: new Font({
 *       // 字体
 *       family: "微软雅黑",
 *       // 文字大小,单位像素
 *       size: 30,
 *       // 文字是否为斜体,正常模式
 *       style: "normal",
 *       // 文字粗细
 *       weight: "normal"
 *     })
 *   })
 * })
 * // 初始化GeoJSON图层
 * const geojsonLayer = new GeoJSONLayer({
 *   // 服务基地址,当不指定图层名称时,默认查询第一个子图层
 *   url: 'http://{ip}:{port}/igs/rest/services/{ServiceName}/FeatureServer',
 *   // 可在此处设置渲染样式
 *   renderer: {},
 *   // 启用注记
 *   labelsVisible: true,
 *   // 设置注记样式
 *   labelingInfo: [labelClass]
 * })
 * // 添加到容器中
 * map.add(geojsonLayer)
 */

/**
 * 图层样式更新完毕事件,请注意该事件是图层更新事件(layerview-update)的子事件
 * @event GeoJSONLayer#图层样式更新完毕事件
 * @property {Object} event 事件对象
 * @property {String} [event.type = 'layerview-update'] 图层更新完毕事件
 * @property {String} [event.message = null] 更新描述
 * @property {Array<UpdateContent>} [event.updateContent = null] 更新详情对象
 * @property {Layer} [event.layer = null] 地图图层对象
 * @property {MapView} [event.layerView = null] 图层的视图对象
 * @property {Layer} [event.sourceTarget = null] 事件发起对象
 * @property {Map} [event.target = null] 事件接收对象
 * @example <caption><h5>图层样式更新完毕事件</h5></caption>
 * Layer.on('layerview-update', function (event) {
 *   // 获取更新事件对象
 *   console.log("更新完毕:", event)
 *   // 获取更新详情数组
 *   const updateContent = event.updateContent
 *   // 循环数组,根据事件名进行后续操作
 *   for (let i = 0; i < updateContent.length; i++) {
 *     // 图层样式更新完毕事件
 *     if(updateContent[i].name === 'renderer'){
 *       console.log("图层样式更新完毕事件:", event);
 *     }
 *   }
 * });
 */
class GeoJSONLayer extends Layer {
  constructor(options) {
    super(options)
    options = defaultValue(options, {})
    this.type = LayerType.geojson
    this._spatialReference = defaultValue(
      options.spatialReference,
      new SpatialReference('EPSG:4326')
    )
    /**
     * 最小层级
     * @member {Number} GeoJSONLayer.prototype.minScale
     */
    this.minScale = defaultValue(options.minScale, 0)
    /**
     * 最大层级
     * @member {Number} GeoJSONLayer.prototype.maxScale
     */
    this.maxScale = defaultValue(options.maxScale, 19)
    /**
     * token名
     * @member {String} GeoJSONLayer.prototype.tokenKey
     */
    this.tokenKey = defaultValue(options.tokenKey, 'token')
    /**
     * token值
     * @member {String} GeoJSONLayer.prototype.tokenValue
     */
    this.tokenValue = defaultValue(options.tokenValue, undefined)
    /**
     * 渲染器
     * @member {String} GeoJSONLayer.prototype.renderer
     */
    this.renderer = this._decorate(
      'renderer',
      options.renderer,
      BaseRenderer,
      BaseRenderer.fromJSON
    )
    /**
     * 请求地址
     * @member {String} GeoJSONLayer.prototype.url
     */
    this.url = defaultValue(options.url, '')
    /**
     * 高程信息
     * @member {ElevationInfo} GeoJSONLayer.prototype.elevationInfo
     */
    this.elevationInfo = defaultValue(
      options.elevationInfo,
      new ElevationInfo()
    )
    /**
     * 自定义查询参数customParameters
     * @member {String} GeoJSONLayer.prototype.customParameters
     */
    this.customParameters = defaultValue(options.customParameters, {})
    /**
     * 几何类型
     * @member {String} GeoJSONLayer.prototype.geometryType
     */
    this.geometryType = GeometryType.geometry
    /**
     * 是否开启动态注记,仅支持三维动态注记渲染
     * @member {Boolean} GeoJSONLayer.prototype.labelsVisible
     */
    this.labelsVisible = defaultValue(options.labelsVisible, false)
    /**
     * 注记样式数组,默认取数组的第一个样式,仅支持三维动态注记渲染
     * @member {Array<LabelClass>} GeoJSONLayer.prototype.labelingInfo
     */
    this.labelingInfo = defaultValue(options.labelingInfo, [])
    // 缓存几何数据
    this._source = null
  }

  /**
   * 加载图层资源<a id='load'></a>
   * @return {Promise<GeoJSONLayer>}
   */
  load() {
    // GeoJSONLayer使用自己的初始化逻辑
    return this._load()
  }

  /**
   * 子类加载服务端元信息的方法
   * @private
   * */
  _load() {
    const self = this
    return this._parseGeoJSON().then((json) => {
      const data = json
      if (data) {
        // 缓存数据
        self._source = data
        // 计算范围
        const layerRange = T.bbox(data)
        const xMin = parseFloat(layerRange[0])
        const yMin = parseFloat(layerRange[1])
        const xMax = parseFloat(layerRange[2])
        const yMax = parseFloat(layerRange[3])
        self.extent = new Extent({
          xmin: xMin,
          xmax: xMax,
          ymin: yMin,
          ymax: yMax,
          spatialReference: self._spatialReference
        })
        // 获取数据类型
        this.geometryType = GeometryType.geometry
        // 计算SpatialReference
        if (
          data &&
          data.crs &&
          data.crs.properties &&
          data.crs.properties.name
        ) {
          this._spatialReference = data.crs.properties.name
        }
        return new Promise((resolve) => {
          self.loadStatus = LoadStatus.loaded
          self.loaded = true
          resolve(self)
        })
      } else {
        Log.error('几何信息为空', data)
      }
    })
  }

  /**
   * 销毁图层<a id='destroy'></a>
   */
  destroy() {}

  /**
   * 返回所有要素<a id='queryFeatures'></a>
   * @return {Promise<FeatureSet>}
   */
  queryFeatures() {
    return this._parseGeoJSON().then((res) => {
      return FeatureSet.fromGeoJSON(res)
    })
  }

  /**
   * 查询要素数量<a id='queryFeaturesCount'></a>
   * @return {Promise<Number>}
   */
  queryFeaturesCount() {
    return this._parseGeoJSON().then((res) => {
      return FeatureSet.fromGeoJSON(res)
    })
  }

  /**
   * @private
   * @return {Promise<Object>}
   */
  _parseGeoJSON() {
    const self = this
    return new Promise((reslove) => {
      if (typeof self.url === 'string') {
        let url = self.url
        if (self.customParameters) {
          const keys = Object.keys(self.customParameters)
          let queryStr = '?'
          keys.forEach((key) => {
            queryStr += `${key}=${self.customParameters[key]}&`
          })
          if (queryStr !== '?') {
            queryStr = queryStr.substring(queryStr.length - 1, 0)
            url += queryStr
          }
        }
        FetchRequest.commit('get', url, {}).then((res) => {
          reslove(res.json())
        })
      } else {
        reslove(self.url)
      }
    }).catch((err) => {
      Log.error(err)
    })
  }

  /**
   * 通过传入的json构造并返回一个新的几何对象<a id='fromJSON'></a>
   * @param {Object} [json] JSON对象
   * @example <caption><h7>通过传入的json构造并返回一个新的几何对象</h7></caption>
   * const json = {
   *   // 服务基地址
   *   url: 'json数据地址'
   * }
   * const geojsonLayer = new Zondy.Layer.GeoJSONLayer.fromJSON(json)
   */
  static fromJSON(json) {
    json = jsonClone(json)
    const _layer = new GeoJSONLayer(json)
    _layer.renderer = BaseRenderer.fromJSON(json.renderer)
    _layer.elevationInfo = new ElevationInfo(json.elevationInfo)
    _layer.extent = Geometry.fromJSON(json.extent)

    return _layer
  }

  toJSON() {
    const _json = super.toJSON()

    _json.url = this.url
    _json.elevationInfo = toJSON(this.elevationInfo, ElevationInfo)
    _json.customParameters = this.customParameters
    _json.labelsVisible = this.labelsVisible
    _json.geometryType = this.geometryType
    _json.renderer = toJSON(this.renderer, BaseRenderer)
    _json.extent = toJSON(this.extent, Extent)
    _json.labelingInfo = this.labelingInfo

    return _json
  }
}

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