类名 common/document/layer/arcgis/ArcGISVectorTileLayer.js
import { VectorTileLayer } from '../baseLayer'
import { LayerEventType, LayerType, LoadStatus } from '../../../base/enum'
import { Zondy } from '../../../base'
import { defaultValue, isNull, isNumber, Log, toJSON } from '../../../util'
import { VectorTileServer } from '../../../service'
import { Extent, Point, SpatialReference } from '../../../base/geometry'
import ArcGISVectorTileSubLayer from './ArcGISVectorTileSubLayer'
import { LayerViewUpdateEvent } from '../../../base/event'
import TileInfoUtil from '../support/TileInfoUtil'
import TileInfo from '../support/TileInfo'
import { jsonClone } from '../../../util/Utils'
import FetchRequest from '../../../service/FetchRequest'

/**
 * ArcGIS矢量瓦片图层
 * 支持通过加载矢量瓦片服务、加载矢量瓦片样式文件和设置矢量瓦片样式的方式创建矢量瓦片图层
 * <br><br>[ES5引入方式]:<br/>
 * Zondy.Layer.ArcGISVectorTileLayer() <br/>
 * [ES6引入方式]:<br/>
 * import { ArcGISVectorTileLayer } from "@mapgis/webclient-common" <br/>
 * <br/>
 * 针对图层的操作请在图层加载完毕事件中进行<br/>
 * Layer.on('layerview-created', function (result) {<br/>
 *   console.log("加载完毕:", result.layer)<br/>
 * });<br/>
 * 如果不想在该事件中放入业务代码,则请确认图层资源以加载完毕后再进行操作<br/>
 * if(layer.loadStatus === 'loaded') {<br/>
 *   // 你的业务逻辑<br/>
 * }
 * <br/>
 * @class ArcGISVectorTileLayer
 * @moduleEX LayerModule
 * @classdesc ArcGIS矢量瓦片图层
 * @extends VectorTileLayer
 * @fires Layer#图层加载完毕事件
 * @fires Layer#图层销毁完毕事件
 * @fires Layer#图层更新完毕事件
 * @fires Layer#图层显隐更新完毕事件
 * @fires Layer#图层透明度更新完毕事件
 * @param {Object} options 构造参数
 * @param {String} [options.url] 服务基地址,<br>
 * 支持矢量瓦片服务,例如:<br>
 * 'https://basemaps.arcgis.com/arcgis/rest/services/World_Basemap_v2/VectorTileServer',<br>
 * 参考示例:<a href='#vector-tile-server'>[加载矢量瓦片服务]</a><br/>
 * 支持矢量瓦片样式文件,例如:<br>
 * 'https://jsapi.maps.arcgis.com/sharing/rest/content/items/75f4dfdff19e445395653121a95a85db/resources/styles/root.json',<br>
 * 参考示例:<a href='#vector-tile-json'>[加载矢量瓦片样式文件]</a><br/>
 * 删除图层方法:<a href='#remove-layer'>[删除图层]</a>
 * @param {Object} [options.style] 矢量瓦片样式,也可以支持通过传入矢量瓦片样式的方式构造矢量瓦片,注意矢量瓦片样式要符合arcgis矢量瓦片的规范,<br>
 * 参考示例:<a href='#vector-tile-style'>[加载矢量瓦片样式]</a>
 * @param {Number} [options.minScale = null] 最小比例尺,只有当地图视图的比例尺大于最小比例尺时显示矢量瓦片图层
 * @param {Number} [options.maxScale = null] 最大比例尺,只有当地图视图的比例尺小于最大比例尺时显示矢量瓦片图层
 * @param {Number} [options.opacity = 1] 图层透明度,0到1之间的值,0为完全透明,1为不透明,参考示例:<a href='#opacity'>[设置图层透明度]</a>
 * @param {String} [options.tokenKey = 'token'] token名
 * @param {String} [options.tokenValue] token值,只有当tokenValue存在时,才会绑定token
 * @param {Boolean} [options.visible = true] 图层显示或隐藏,true则显示,false则隐藏,参考示例:<a href='#visible'>[设置图层显隐]</a>
 * @param {Array<ArcGISVectorTileSubLayer>} [options.sublayers=[]] 子图层信息。指定的和服务器端获取图层的交集, 默认不设置,加载全部图层。可设置子图层的可见性,以及专题图参数。<br/>
 * @param {String} [option.labelsRenderMode = 'off-screen'] 指定矢量瓦片注记的渲染模式,仅在三维上有效。on-screen表示使用三维接口实时渲染注记;off-screen表示通过先将注记渲染到图片上,再通过三维接口渲染到屏幕。
 * @param {Polygon|Extent|Circle|null} [options.clippingArea = null] 图层空间裁剪范围,仅支持多边形裁剪、矩形裁剪、圆形裁剪
 * 目前子图层支持的专题图如下:<br/>
 * [1、单值专题图]{@link UniqueValueRenderer}<br/>
 * [2、分段专题图]{@link ClassBreakRenderer}<br/>
 * [3、统一专题图]{@link SimpleRenderer}<br/>
 * 参考示例:<br/>
 * <a href='#showAllSubLayers'>[1、显示所有子图层]</a><br/>
 * <a href='#showSubLayersById'>[2、根据id显示子图层]</a><br/>
 * <a href='#showSubLayers-property'>[3、通过修改子图参数,设置子图层显隐]</a><br/>
 * <a href='#setLayerRenderer-unique'>[4、设置子图层专题图-单值专题图]</a><br/>
 * <a href='#setLayerRenderer-break'>[5、设置子图层专题图-分段专题图]</a><br/>
 * <a href='#setLayerRenderer-simple'>[6、设置子图层专题图-统一专题图]</a><br/>
 * <a href='#setLayerRenderer-property'>[7、通过修改子图参数,设置子图层专题图]</a><br/>
 *
 *@summary <h5>支持如下方法:</h5>
 * <a href='#findSublayerById'>[1、根据子图层id查询图层]</a><br/>
 * <a href='#loadStyle'>[2、获取矢量瓦片样式]</a><br/>
 * <a href='#fromJSON'>[3、通过传入的json构造并返回一个新的几何对象]</a><br/>
 * [4、导出为json对象]{@link OGCLayer#toJSON}<br/>
 * [5、克隆几何对象]{@link OGCLayer#clone}<br>
 * <a href='#getLayoutProperties'>[6、获取图层布局属性]</a><br/>
 * <a href='#setLayoutProperties'>[7、设置图层布局属性]</a><br/>
 * <a href='#getPaintProperties'>[8、获取图层绘制属性]</a><br/>
 * <a href='#setPaintProperties'>[9、设置图层绘制属性]</a><br/>
 * <a href='#getStyleLayer'>[10、获取图层]</a><br/>
 * <a href='#setStyleLayer'>[11、设置图层属性对象]</a><br/>
 * <a href='#deleteStyleLayer'>[12、删除图层]</a><br/>
 * <a href='#setStyleLayerVisible'>[13、设置图层可见性]</a><br/>
 * <a href='#getStyleLayerVisible'>[14、获取图层可见性]</a><br/>
 * <a href='#getStyleLayerFilter'>[15、获取属性过滤条件]</a><br/>
 * <a href='#setStyleLayerFilter'>[16、属性过滤]</a><br/>
 * <a href='#getStyleLayerZoomRange'>[17、获取图层最小、最大层级]</a><br/>
 * <a href='#setStyleLayerZoomRange'>[18、设置图层最小、最大层级]</a><br/>
 *
 * @example <caption><h7 id='vector-tile-server'>加载矢量瓦片服务</h7></caption>
 * //初始化地图管理容器
 * // ES5引入方式
 * const { ArcGISVectorTileLayer } = Zondy.Layer
 * const { Map,MapView } = Zondy
 * // ES6引入方式
 * import { ArcGISVectorTileLayer,Map,MapView } from "@mapgis/webclient-common"
 * const map = new Map();
 * //初始化地图引擎
 * const mapView = new MapView({
 *   //指定视图id
 *   viewId: "viewer-id",
 *   //绑定地图管理容器
 *   map: map
 * });
 * //添加矢量瓦片图层
 * const arcgisVectorTileLayer = new ArcGISVectorTileLayer({
 *   url: 'https://basemaps.arcgis.com/arcgis/rest/services/World_Basemap_v2/VectorTileServer'
 * });
 * map.add(arcgisVectorTileLayer);
 *
 * @example <caption><h7 id='vector-tile-json'>加载矢量瓦片样式文件</h7></caption>
 *  // ES5引入方式
 * const { ArcGISVectorTileLayer } = Zondy.Layer
 * const { Map,MapView } = Zondy
 * // ES6引入方式
 * import { ArcGISVectorTileLayer } from "@mapgis/webclient-common"
 * //初始化地图管理容器
 * const map = new Map();
 * //初始化地图引擎
 * const mapView = new MapView({
 *   //指定视图id
 *   viewId: "viewer-id",
 *   //绑定地图管理容器
 *   map: map
 * });
 * //添加矢量瓦片图层
 * const arcgisVectorTileLayer = new ArcGISVectorTileLayer({
 *   url: 'https://jsapi.maps.arcgis.com/sharing/rest/content/items/75f4dfdff19e445395653121a95a85db/resources/styles/root.json'
 * });
 * map.add(arcgisVectorTileLayer);
 *
 * @example <caption><h7 id='vector-tile-style'>加载矢量瓦片样式</h7></caption>
 *  // ES5引入方式
 * const { ArcGISVectorTileLayer } = Zondy.Layer
 * const { Map,MapView } = Zondy
 * // ES6引入方式
 * import { ArcGISVectorTileLayer } from "@mapgis/webclient-common"
 * //初始化地图管理容器
 * const map = new Map();
 * //初始化地图引擎
 * const mapView = new MapView({
 *   //指定视图id
 *   viewId: "viewer-id",
 *   //绑定地图管理容器
 *   map: map
 * });
 * //添加矢量瓦片图层
 * const arcgisVectorTileLayer = new ArcGISVectorTileLayer({
 *   // 设置你的样式
 *   style: {}
 * });
 * map.add(arcgisVectorTileLayer);
 *
 * @example <caption><h7 id='opacity'>设置图层透明度</h7></caption>
 * igsVectorTileLayer.opacity = 0.5
 *
 * @example <caption><h7 id='visible'>显示或隐藏图层</h7></caption>
 * igsVectorTileLayer.visible = !igsVectorTileLayer.visible
 *
 * @example <caption><h7 id='remove-layer'>删除图层</h7></caption>
 * map.remove(igsVectorTileLayer)
 *
 * @example <caption><h7 id='showAllSubLayers'>显示所有子图层</h7></caption>
 *  // ES5引入方式
 * const { ArcGISVectorTileLayer } = Zondy.Layer
 * // ES6引入方式
 * import { ArcGISVectorTileLayer } from "@mapgis/webclient-common"
 * // 添加图层
 * const igsVectorTileLayer = new ArcGISVectorTileLayer({
 *   // 服务基地址
 *   url: 'http://{ip}:{port}/igs/rest/services/VectorTile/{serviceName}/VectorTileServer',
 *   //默认不设置sublayers,加载全部图层
 * });
 * map.add(igsVectorTileLayer);
 *
 * @example <caption><h7 id='showSubLayersById'>根据id显示子图层</h7></caption>
 * // ES5引入方式
 * const { ArcGISVectorTileLayer } = Zondy.Layer
 * // ES6引入方式
 * import { ArcGISVectorTileLayer } from "@mapgis/webclient-common"
 * // 添加图层
 * const igsVectorTileLayer = new ArcGISVectorTileLayer({
 *   // 服务基地址
 *   url: 'http://{ip}:{port}/igs/rest/services/VectorTile/{serviceName}/VectorTileServer',
 *   // 根据id显示子图层
 *   sublayers: [
 *      {
 *        // 子图层id
 *        id: '子图层id',
 *        // 显示子图层
 *        visible: true
 *      }
 *    ]
 * });
 * map.add(igsVectorTileLayer);
 *
 * @example <caption><h7 id='showSubLayers-property'>通过修改子图参数,设置子图层显隐</h7></caption>
 * // ES5引入方式
 * const { ArcGISVectorTileLayer } = Zondy.Layer
 * // ES6引入方式
 * import { ArcGISVectorTileLayer } from "@mapgis/webclient-common"
 * // 添加图层
 * const igsVectorTileLayer = new ArcGISVectorTileLayer({
 *   // 服务基地址
 *   url: 'http://{ip}:{port}/igs/rest/services/VectorTile/{serviceName}/VectorTileServer'
 * });
 * map.add(igsVectorTileLayer);
 * igsVectorTileLayer.on('layerview-created', function (result) {
 *   console.log("加载完毕:", result.layer)
 *   // 根据id获取子图层
 *   const subLayer = igsVectorTileLayer.findSublayerById('图层id')
 *   // 设置子图层显隐
 *   subLayer.visible = false
 *   // 视点跳转
 *   sceneView.flyTo({
 *     extent: result.layer.extent
 *   })
 * })
 *
 * @example <caption><h7 id='setLayerRenderer-unique'>设置子图层专题图-单值专题图</h7></caption>
 * // ES5引入方式
 * const { ArcGISVectorTileLayer } = Zondy.Layer
 * const { UniqueValueRenderer } = Zondy.Renderer
 * const { SimpleFillSymbol,SimpleLineSymbol } = Zondy.Symbol
 * const { Color } = Zondy
 * // ES6引入方式
 * import { ArcGISVectorTileLayer,UniqueValueRenderer ,SimpleFillSymbol,SimpleLineSymbol,Color} from "@mapgis/webclient-common"
 * const igsVectorTileLayer = new ArcGISVectorTileLayer({
 *   // 服务基地址,当不指定图层名称时,默认查询第一个子图层
 *   url: 'http://{ip}:{port}/igs/rest/services/VectorTile/{serviceName}/VectorTileServer',
 *   // 设置子图层专题图
 *   sublayers: [
 *      {
 *        // 子图层id
 *        id: '子图层id',
 *        // 设置渲染样式-单值专题图
 *        renderer: new UniqueValueRenderer({
 *          //专题图过滤字段名
 *          field: '你的字段名',
 *          // 默认样式,当没有匹配到指定值时,会使用默认样式
 *          //因为该数据的几何类型为区,因此设置区样式
 *          defaultSymbol: new SimpleFillSymbol({
 *            // 填充颜色
 *            color: 'rgba(1,1,252,0)',
 *            // 外边线样式
 *            outline: new SimpleLineSymbol({
 *              //线颜色
 *              color: new Color(255, 1, 0, 1),
 *              //线宽
 *              width: 1
 *            })
 *          }),
 *          //单值专题图过滤调条件数组
 *          uniqueValueInfos: [{
 *            //指定字段值
 *            value: "指定的值",
 *            //匹配到该值后的样式
 *            //因为该数据的几何类型为区,因此设置区样式
 *            symbol: new SimpleFillSymbol({
 *              // 填充颜色
 *              color: 'rgba(1,1,252,1)',
 *              // 外边线样式
 *              outline: new SimpleLineSymbol({
 *                //线符号颜色
 *                color: new Color(255, 1, 0, 11),
 *                //线宽
 *                width: 1
 *                })
 *              })
 *            },{
 *              //指定字段值
 *              value: "指定的值",
 *              //匹配到该值后的样式
 *              //因为该数据的几何类型为区,因此设置区样式
 *              symbol: new SimpleFillSymbol({
 *                // 填充颜色
 *                color: new Color(211, 111, 11, 1)
 *              })
 *            }]
 *        })
 *      }
 *   ]
 * });
 * map.add(igsVectorTileLayer);
 *
 * @example <caption><h7 id='setLayerRenderer-break'>设置子图层专题图-分段专题图</h7></caption>
 * // ES5引入方式
 * const { ArcGISVectorTileLayer } = Zondy.Layer
 * const { ClassBreakRenderer } = Zondy.Renderer
 * const { SimpleFillSymbol,SimpleLineSymbol } = Zondy.Symbol
 * const { Color } = Zondy
 * // ES6引入方式
 * import { ArcGISVectorTileLayer,ClassBreakRenderer ,SimpleFillSymbol,SimpleLineSymbol,Color} from "@mapgis/webclient-commo
 * const igsVectorTileLayer = new ArcGISVectorTileLayer({
 *   // 服务基地址,当不指定图层名称时,默认查询第一个子图层
 *   url: 'http://{ip}:{port}/igs/rest/services/VectorTile/{serviceName}/VectorTileServer',
 *   // 设置子图层专题图
 *   sublayers: [
 *     {
 *      // 子图层id
 *      id: '子图层id',
 *      // 设置渲染样式-分段专题图
 *      renderer: new ClassBreakRenderer({
 *        //专题图过滤字段名
 *        field: '你的字段名',
 *        // 默认样式,当没有匹配到指定值时,会使用默认样式
 *        // 因为该数据的几何类型为区,因此设置区样式
 *        defaultSymbol:  new SimpleFillSymbol({
 *          // 填充颜色
 *          color: 'rgba(222,1,252,1)',
 *          // 线符号样式
 *          outline: new SimpleLineSymbol({
 *            //线符号颜色
 *            color: new Color(255, 1, 0, 1),
 *            //线宽
 *            width: 1
 *          })
 *        }),
 *        //分段专题图过滤条件数组
 *        classBreakInfos: [{
 *          // 最大过滤范围,field对应的值小于maxValue
 *          maxValue: "最大范围",
 *          // 最小过滤范围,field对应的值大于等于minValue
 *          minValue: "最小范围",
 *          // 匹配到该值后的样式
 *          // 因为该数据的几何类型为区,因此设置区样式
 *          symbol:  new SimpleFillSymbol({
 *            // 填充颜色
 *            color: 'rgba(1,1,252,1)',
 *            // 线符号样式
 *            outline: new SimpleLineSymbol({
 *              //线符号颜色
 *              color: new Color(255, 1, 0, 1),
 *              //线宽
 *              width: 1
 *            })
 *          })
 *        }]
 *      })
 *    }
 *  ]
 * });
 * map.add(igsVectorTileLayer);
 *
 * @example <caption><h7 id='setLayerRenderer-property'>通过修改子图参数,设置子图层专题图</h7></caption>
 * // ES5引入方式
 * const { ArcGISVectorTileLayer } = Zondy.Layer
 * const { ClassBreakRenderer } = Zondy.Renderer
 * const { SimpleFillSymbol,SimpleLineSymbol } = Zondy.Symbol
 * const { Color } = Zondy
 * // ES6引入方式
 * import { ArcGISVectorTileLayer,ClassBreakRenderer ,SimpleFillSymbol,SimpleLineSymbol,Color} from "@mapgis/webclient-commo
 * const igsVectorTileLayer = new ArcGISVectorTileLayer({
 *   // 服务基地址
 *   url: 'http://{ip}:{port}/igs/rest/services/VectorTile/{serviceName}/VectorTileServer'
 * });
 * map.add(igsVectorTileLayer);
 * // 根据id获取子图层
 * const subLayer = igsVectorTileLayer.findSublayerById('子图层id')
 * // 设置渲染样式-分段专题图
 * subLayer.renderer = new ClassBreakRenderer({
 *     // 专题图过滤字段名
 *     field: '你的字段名',
 *     // 默认样式,当没有匹配到指定值时,会使用默认样式
 *     // 因为该数据的几何类型为区,因此设置区样式
 *     defaultSymbol:  new SimpleFillSymbol({
 *       // 填充颜色
 *       color: 'rgba(222,1,252,1)',
 *       // 线符号样式
 *       outline: new SimpleLineSymbol({
 *         //线符号颜色
 *         color: new Color(255, 1, 0, 1),
 *         //线宽
 *         width: 1
 *       })
 *     }),
 *     //分段专题图过滤条件数组
 *     classBreakInfos: [{
 *       // 最大过滤范围,field对应的值小于maxValue
 *       maxValue: "最大范围",
 *       // 最小过滤范围,field对应的值大于等于minValue
 *       minValue: "最小范围",
 *       // 匹配到该值后的样式
 *       // 因为该数据的几何类型为区,因此设置区样式
 *       symbol:  new SimpleFillSymbol({
 *         // 填充颜色
 *         color: 'rgba(1,1,252,1)',
 *         // 线符号样式
 *         outline: new SimpleLineSymbol({
 *           //线符号颜色
 *           color: new Color(255, 1, 0, 1),
 *           //线宽
 *           width: 1
 *         })
 *       })
 *     }]
 *   })
 * @example <caption><h7 id='setLayerRenderer-simple'>统一专题图</h7></caption>
 * // ES5引入方式
 * const { ArcGISVectorTileLayer } = Zondy.Layer
 * const { SimpleRenderer } = Zondy.Renderer
 * const { SimpleFillSymbol,SimpleLineSymbol } = Zondy.Symbol
 * const { Color } = Zondy
 * // ES6引入方式
 * import { ArcGISVectorTileLayer,SimpleRenderer ,SimpleFillSymbol,SimpleLineSymbol,Color} from "@mapgis/webclient-commo
 * const igsVectorTileLayer = new ArcGISVectorTileLayer({
 *   // 服务基地址,当不指定图层名称时,默认查询第一个子图层
 *   url: 'http://{ip}:{port}/igs/rest/services/VectorTile/{serviceName}/VectorTileServer',
 *   // 设置渲染样式-统一专题图
 *   renderer: new SimpleRenderer({
 *     // 因为该数据的几何类型为区,因此设置区样式
 *     symbol: new SimpleFillSymbol({
 *       // 填充颜色
 *       color: new Zondy.Color(255, 0, 0),
 *       // 外边线样式
 *       outline: new SimpleLineSymbol({
 *         // 线颜色
 *         color: new Color(0, 0, 0),
 *         // 线宽度
 *         width: 1
 *       })
 *     })
 *   })
 * })
 */

class ArcGISVectorTileLayer extends VectorTileLayer {
  constructor(options) {
    super(options)
    options = defaultValue(options, {})
    this.type = LayerType.arcgisVectorTile
    this.description = 'ArcGIS矢量瓦片图层'
    this.vectorTileType = 'arcgis'
    /**
     * 矢量瓦片服务地址
     * @member {String} ArcGISVectorTileLayer.prototype.url
     */
    this.url = defaultValue(options.url, '')
    /**
     * 矢量瓦片子图层
     * @member {Array<ArcGISVectorTileSubLayer>} ArcGISVectorTileLayer.prototype.sublayers
     */
    this.sublayers = defaultValue(options.sublayers, [])
    /**
     * 服务支持的能力
     * @member {Array<String>} ArcGISVectorTileLayer.prototype.capabilities
     */
    this.capabilities = []
    // 空间裁剪范围
    this._clippingArea = defaultValue(options.clippingArea, null)
    /**
     * 图层加载状态
     * @readonly
     * @default not-loaded
     * @member {LoadStatus} ArcGISVectorTileLayer.prototype.loadStatus
     */
    this.loadStatus = LoadStatus.notLoaded
    /**
     * 指定矢量瓦片注记的渲染模式(目前仅支持三维上矢量瓦片注记的渲染)
     * @member {String} ArcGISVectorTileLayer.prototype.labelsRenderMode
     */
    this.labelsRenderMode = defaultValue(options.labelsRenderMode, 'off-screen')
    // 当前mvt矢量瓦片样式
    this._style = defaultValue(options.style, undefined)
    // 测试接口
    this.labelingInfo = defaultValue(options.labelingInfo, [])
    // 矢量瓦片服务
    this._vectorTileServer = new VectorTileServer({
      url: this.url
    })
    // 所有图层
    this._allSublayers = []
  }

  load() {
    // 发起请求
    const self = this
    if (self.url.indexOf('root.json') > -1) {
      return FetchRequest.commit('get', self.url, {}).then((result) => {
        return result.json().then((resultJSON) => {
          Log.info('矢量瓦片样式查询成功:', resultJSON)
          self._vectorTileServer = new VectorTileServer({
            url: resultJSON.sources['esri'].url
          })
          return self._vectorTileServer.queryServerInfo().then((tileInfo) => {
            Log.info('矢量瓦片信息查询成功:', tileInfo)
            // 初始化图层信息
            self._initTileInfo(tileInfo)
            // 获取矢量瓦片样式信息
            self._initStyle(resultJSON)
            // 获取所有子图层
            self._initSubLayers()
            return new Promise((resolve) => {
              self.loadStatus = LoadStatus.loaded
              self.loaded = true
              resolve(self)
            })
          })
        })
      })
    } else if (self.url.indexOf('/VectorTileServer') > -1) {
      return self._vectorTileServer.queryServerInfo().then((tileInfo) => {
        Log.info('矢量瓦片信息查询成功:', tileInfo)
        // 初始化图层信息
        self._initTileInfo(tileInfo)
        return FetchRequest.commit(
          'get',
          `${self.url}/resources/styles/root.json`,
          {}
        ).then((resultJSON) => {
          return resultJSON.json().then((styleJSON) => {
            // 获取矢量瓦片样式信息
            self._initStyleByServer(styleJSON)
            // 获取所有子图层
            self._initSubLayers()
            return new Promise((resolve) => {
              self.loadStatus = LoadStatus.loaded
              self.loaded = true
              resolve(self)
            })
          })
        })
      })
    } else if (this._style && this._style instanceof Object) {
      self._vectorTileServer = new VectorTileServer({
        url: this._style.sources['esri'].url
      })
      return self._vectorTileServer.queryServerInfo().then((tileInfo) => {
        Log.info('矢量瓦片信息查询成功:', tileInfo)
        // 初始化图层信息
        self._initTileInfo(tileInfo)
        // 获取矢量瓦片样式信息
        self._initStyle(self._style)
        // 获取所有子图层
        self._initSubLayers()
        return new Promise((resolve) => {
          self.loadStatus = LoadStatus.loaded
          self.loaded = true
          resolve(self)
        })
      })
    }
  }

  /**
   * 初始化瓦片基本参数
   * @private
   * @param {Object} tileInfo 瓦片信息
   * */
  _initTileInfo(tileInfo) {
    const self = this
    const data = tileInfo.data
    // 初始化参考系
    const initSpatialReference = self._spatialReference
      ? self._spatialReference.clone()
      : null
    // 外部未设置空间参考系信息时,优先读取空间参考系相关信息
    if (data && data.tileInfo && data.tileInfo.spatialReference) {
      const spatialReference =
        data.spatialReference || data.tileInfo.spatialReference
      // 设置数据空间参考系
      if (spatialReference) {
        if (spatialReference.latestWkid) {
          self._spatialReference = new SpatialReference({
            wkid: parseInt(spatialReference.latestWkid)
          })
        } else if (spatialReference.wkt) {
          self._spatialReference = new SpatialReference({
            wkt: spatialReference.wkt
          })
        } else {
          Log.error(
            '矢量瓦片服务坐标系信息不存在,请检查igs返回的矢量瓦片服务元信息!'
          )
        }
      }
      // 获取瓦片信息
      self.tileInfo = TileInfoUtil.parseTileInfo2(data.tileInfo)
      // 重置extent
      if (isNull(self.extent) && data.fullExtent) {
        self.extent = new Extent({
          xmin: data.fullExtent.xmin,
          ymin: data.fullExtent.ymin,
          xmax: data.fullExtent.xmax,
          ymax: data.fullExtent.ymax,
          spatialReference: self._spatialReference
        })
      }
    }

    // 初始化坐标系 目前仅支持标准3857或4326
    if (initSpatialReference) {
      // 外部设置有参考系
      // 判断是否为地理坐标系
      self._spatialReference = initSpatialReference
      const geoCoodsArr = [4326, 4490, 4610, 4214]
      if (geoCoodsArr.indexOf(initSpatialReference.wkid) > -1) {
        self.tileInfo = new TileInfo({
          origin: new Point({
            coordinates: [-180, 90]
          }),
          lods: TileInfoUtil.getLods(1.40625, 'degree', 21),
          size: [256, 256],
          spatialReference: new SpatialReference('EPSG:4326')
        })
        self._spatialReference.extent = new Extent({
          xmin: -180,
          ymin: -90,
          xmax: 180,
          ymax: 90,
          spatialReference: new SpatialReference('EPSG:4326')
        })
        self._extent = self._spatialReference.extent.clone()
      }
    }
    // 没有设置坐标系时,默认为3857
    if (!self._spatialReference) {
      self._spatialReference = new SpatialReference('EPSG:3857')
      // web墨卡托
      self.tileInfo = new TileInfo({
        origin: new Point({
          coordinates: [-20037508.342787, 20037508.342787]
        }),
        lods: TileInfoUtil.getLods(156543.033925625, 'm', 21),
        size: [256, 256],
        spatialReference: new SpatialReference('EPSG:3857')
      })
      self._spatialReference.extent = new Extent({
        xmin: -20037508.342787,
        ymin: -20037508.342787,
        xmax: 20037508.342787,
        ymax: 20037508.342787,
        spatialReference: new SpatialReference('EPSG:3857')
      })
      self._extent = self._spatialReference.extent.clone()
    }

    // 支持mapbox加载矢量瓦片
    if (self._spatialReference && self.tileInfo) {
      const tileInfo = self.tileInfo
      let tileSize = 256
      if (Array.isArray(tileInfo.size)) {
        tileSize = Math.max(tileInfo.size[0], tileInfo.size[1])
      }
      const cols = self.tileInfo.cols || 256
      const rols = self.tileInfo.rols || 256
      self._spatialReference._mapboxTileSize = Math.max(tileSize, cols, rols)
    }

    // 解析服务器提供的能力
    if (data.capabilities) {
      self.capabilities = Array.isArray(data.capabilities)
        ? data.capabilities
        : [data.capabilities]
    }
  }

  /**
   * 初始化矢量瓦片样式
   * @private
   * @param {Object} style 矢量瓦片样式对象
   * */
  _initStyle(style) {
    this._style = style
    this._style.sources.esri.tiles = [
      `${this._style.sources.esri.url}/tile/{z}/{y}/{x}.pbf`
    ]
    delete this._style.sources.esri.url
  }

  /**
   * 初始化矢量瓦片样式
   * @private
   * @param {Object} style 服务端获取的矢量瓦片样式对象
   * */
  _initStyleByServer(style) {
    this._style = style
    this._style.sprite = `${this.url}/resources/sprites/sprite`
    this._style.glyphs = `${this.url}/resources/fonts/{fontstack}/{range}.pbf`
    this._style.sources.esri.tiles = [`${this.url}/tile/{z}/{y}/{x}.pbf`]
    delete this._style.sources.esri.url
  }

  /**
   * 初始化矢量瓦片子图层
   * @private
   * */
  _initSubLayers() {
    const layers = this._style.layers
    const sublayers = []
    // 初始化图层
    if (Array.isArray(this.sublayers) && this.sublayers.length > 0) {
      this.sublayers.forEach((sublayer) => {
        for (let i = 0; i < layers.length; i++) {
          const layerInfo = layers[i]
          if (layerInfo.id === sublayer.id) {
            const subLayer = new ArcGISVectorTileSubLayer(
              // eslint-disable-next-line prefer-object-spread
              Object.assign(
                {
                  type: layerInfo.type,
                  id: layerInfo.id,
                  title: layerInfo.description,
                  style: layerInfo,
                  visible: undefined,
                  minZoom: layerInfo.minZoom,
                  maxZoom: layerInfo.maxZoom,
                  sublayers: [],
                  layer: this
                },
                sublayer
              )
            )
            sublayers.push(subLayer)
          }
        }
      })
    } else {
      for (let i = 0; i < layers.length; i++) {
        const layerInfo = layers[i]
        const subLayer = new ArcGISVectorTileSubLayer({
          type: layerInfo.type,
          id: layerInfo.id,
          title: layerInfo.description,
          style: layerInfo,
          visible: undefined,
          minZoom: layerInfo.minZoom,
          maxZoom: layerInfo.maxZoom,
          sublayers: [],
          layer: this
        })
        sublayers.push(subLayer)
      }
    }

    this.sublayers = sublayers
  }

  get allSublayers() {
    if (this._allSublayers.length === 0) {
      this.sublayers.forEach((element) => {
        this._getSubLayers(element, this._allSublayers)
      })
    }
    return this._allSublayers
  }

  get style() {
    const style = JSON.parse(JSON.stringify(this._style))
    // 设置图层样式
    style.layers = this.sublayers.map((v) =>
      JSON.parse(JSON.stringify(v.style))
    )
    return style
  }

  /**
   * 获取矢量瓦片子图层
   * @private
   * @param {ArcGISVectorTileSubLayer} subLayer 子图层
   * @param {Array<ArcGISVectorTileSubLayer>} 矢量瓦片子图层数组
   */
  _getSubLayers(subLayer, sublayers) {
    sublayers.push(subLayer)
    if (subLayer.sublayers && subLayer.sublayers.length > 0) {
      subLayer.sublayers.forEach((element) => {
        this._getSubLayers(element, sublayers)
      })
    }
  }

  /**
   * @description 根据子图层id查询图层
   * @param {String} sublayerID 图层ID
   * @return {ArcGISVectorTileSubLayer} 子图层
   * @example <caption><h7 id='findSublayerById'>根据子图层id查询图层</h7></caption>
   * // ES5引入方式
   * const { ArcGISVectorTileLayer } = Zondy.Layer
   * // ES6引入方式
   * import { ArcGISVectorTileLayer} from "@mapgis/webclient-common"
   * const igsVectorTileLayer = new ArcGISVectorTileLayer({
   *   // 服务基地址
   *   url: 'http://{ip}:{port}/igs/rest/services/VectorTile/{serviceName}/VectorTileServer'
   * });
   * map.add(igsVectorTileLayer);
   * // 根据id获取子图层
   * const subLayer = igsVectorTileLayer.findSublayerById('子图层id')
   */
  findSublayerById(sublayerID) {
    for (let i = 0; i < this.allSublayers.length; i++) {
      if (this.allSublayers[i].id === sublayerID) {
        return this.allSublayers[i]
      }
    }
    return null
  }

  /**
   * 发送图层更新事件
   * @private
   * @param {Array<LayerViewUpdateContent>} 更新内容
   * @fires ArcGISVectorTileLayer#图层样式更新事件
   */
  _fireLayerUpdateEvent(updateContent) {
    if (!this._map) return
    this._map.fire(
      LayerEventType.layerUpdate,
      new LayerViewUpdateEvent({
        type: LayerEventType.layerUpdate,
        target: this._map,
        layer: this,
        message: '',
        updateContent
      })
    )
  }

  /**
   * <a id='getLayoutProperties'/>
   * 获取图层布局属性
   * @param {String} layerId 图层id
   * @return {Object|null} 返回图层布局属性
   */
  getLayoutProperties(layerId) {
    const sublayer = this.findSublayerById(layerId)
    if (sublayer) {
      const style = sublayer.style
      return style.layout || {}
    }
    return null
  }

  /**
   * <a id='setLayoutProperties'/>
   * 设置图层布局属性
   * @param {String} layerId 图层id
   * @param {Object} layout 布局属性
   */
  setLayoutProperties(layerId, layout) {
    const sublayer = this.findSublayerById(layerId)
    if (sublayer && sublayer.style) {
      sublayer.style.layout = Object.assign(
        sublayer.style.layout || {},
        layout || {}
      )
    }
    this._fireLayerUpdateEvent([
      {
        params: arguments,
        dataChanged: true,
        name: 'setLayoutProperties',
        operationType: 'method'
      }
    ])
  }

  /**
   * <a id='getPaintProperties'/>
   * 获取图层绘制属性
   * @param {String} layerId 图层id
   * @return {Object|null} 返回图层绘制属性
   */
  getPaintProperties(layerId) {
    const sublayer = this.findSublayerById(layerId)
    if (sublayer) {
      const style = sublayer.style
      return style.paint || {}
    }
    return null
  }

  /**
   * <a id='setPaintProperties'/>
   * 设置图层绘制属性
   * @param {String} layerId 图层id
   * @param {Object} paint 绘制属性
   */
  setPaintProperties(layerId, paint) {
    const sublayer = this.findSublayerById(layerId)
    if (sublayer && sublayer.style) {
      sublayer.style.paint = Object.assign(
        sublayer.style.paint || {},
        paint || {}
      )
    }
    this._fireLayerUpdateEvent([
      {
        params: arguments,
        dataChanged: true,
        name: 'setPaintProperties',
        operationType: 'method'
      }
    ])
  }

  /**
   * <a id='getStyleLayer'/>
   * 获取图层
   * @param {String} layerId 图层id
   * @return {Object|null} 图层对象。可以查看矢量瓦片json中layers属性对图层的定义或者查看mvt style文件标准。
   */
  getStyleLayer(layerId) {
    const sublayer = this.findSublayerById(layerId)
    if (sublayer) {
      return sublayer.style
    }
    return null
  }

  /**
   * <a id='setStyleLayer'/>
   * 设置图层属性对象,可以对图层进行修改、重新排序等。布局属性和绘制属性推荐用setLayoutProperties和setPaintProperties实现。
   * @param {Object} layer 图层对象
   * @param {String} index 图层顺序
   */
  setStyleLayer(layer, index) {
    const layerObject = layer || {}
    const sublayer = this.findSublayerById(layerObject.id)
    if (sublayer) {
      const sublayers = this.sublayers
      const sortIndex = sublayers.findIndex((v) => v.id === sublayer.id)
      // 先移除
      sublayers.splice(sortIndex, 1)
      // 插入点
      const insertIndex = parseInt(defaultValue(index, sublayers.length))
      // 计算插入点index
      const i = Math.min(Math.max(0, insertIndex), sublayers.length)
      // 插入sublayers
      sublayers.splice(i, 0, sublayer)
      this._fireLayerUpdateEvent([
        {
          params: arguments,
          dataChanged: true,
          name: 'setStyleLayer',
          operationType: 'method'
        }
      ])
    }
  }

  /**
   * <a id='deleteStyleLayer'/>
   * 删除图层
   * @param {String} layerId 图层id
   */
  deleteStyleLayer(layerId) {
    const sublayer = this.findSublayerById(layerId)
    if (sublayer) {
      // 移除sublayer
      const sublayers = this.sublayers
      const sortIndex = sublayers.findIndex((v) => v === sublayer)
      sublayers.splice(sortIndex, 1)
      // 发送事件
      this._fireLayerUpdateEvent([
        {
          params: arguments,
          dataChanged: true,
          name: 'deleteStyleLayer',
          operationType: 'method'
        }
      ])
    }
  }

  /**
   * <a id='setStyleLayerVisible'/>
   * 设置图层可见性,visible为true,显示图层,为false,隐藏图层
   * @param {String} layerId 图层id
   * @param {Boolean} visible 可见性
   */
  setStyleLayerVisible(layerId, visible) {
    const visibleDesc = visible ? 'visible' : 'none'
    this.setLayoutProperties(layerId, {
      visibility: visibleDesc
    })
  }

  /**
   * 获取图层可见性
   * <a id='getStyleLayerVisible'/>
   * @param {String} layerId 图层id
   * @return {Boolean} 图层可见性状态
   */
  getStyleLayerVisible(layerId) {
    const layout = this.getLayoutProperties(layerId)
    if (layout) {
      return layout.visibility === 'visible'
    }
    return false
  }

  /**
   * <a id='getStyleLayerFilter'/>
   * 获取属性过滤条件
   * @param {String} layerId 图层ID
   * @return {Array<String>} filter 属性过滤条件数组
   */
  getStyleLayerFilter(layerId) {
    const sublayer = this.findSublayerById(layerId)
    if (sublayer) {
      return sublayer.style.filter || undefined
    }
    return undefined
  }

  /**
   * <a id='setStyleLayerFilter'/>
   * 属性过滤
   * @param {String} layerId 图层ID
   * @param {Array<String>} filter 过滤条件数组
   */
  setStyleLayerFilter(layerId, filter) {
    const sublayer = this.findSublayerById(layerId)
    if (sublayer) {
      const style = sublayer.style
      style.filter = filter
      this._fireLayerUpdateEvent([
        {
          params: arguments,
          dataChanged: true,
          name: 'setStyleLayerFilter',
          operationType: 'method'
        }
      ])
    }
  }

  /**
   * <a id='getStyleLayerZoomRange'/>
   * 获取图层最小、最大层级
   * @param {String} layerId 图层id
   * @return {Array<Number>}
   */
  getStyleLayerZoomRange(layerId) {
    const sublayer = this.findSublayerById(layerId)
    if (sublayer) {
      const style = sublayer.style
      const { minzoom, maxzoom } = style
      return [minzoom, maxzoom]
    }
  }

  /**
   * <a id='setStyleLayerZoomRange'/>
   * 设置图层最小、最大层级
   * @param {String} layerId 图层id
   * @param {Number} minZoom 最小层级
   * @param {Number} maxZoom 最大层级
   */
  setStyleLayerZoomRange(layerId, minZoom, maxZoom) {
    if (!isNumber(minZoom) || !isNumber(maxZoom) || minZoom > maxZoom) {
      Log.error('setStyleLayerZoomRange zoom参数输入有误')
      return
    }
    const sublayer = this.findSublayerById(layerId)
    if (sublayer) {
      const style = sublayer.style
      style.minzoom = minZoom
      style.maxzoom = maxZoom
      this._fireLayerUpdateEvent([
        {
          params: arguments,
          dataChanged: true,
          name: 'setStyleLayerZoomRange',
          operationType: 'method'
        }
      ])
    }
  }

  destroy() {}

  /**
   * <a id='loadStyle'/>
   * 获取矢量瓦片样式
   * @return {Promise<Object>} 返回mvtStyle
   * @example <caption><h7 id='loadStyle'>获取矢量瓦片样式</h7></caption>
   * // ES5引入方式
   * const { ArcGISVectorTileLayer } = Zondy.Layer
   * // ES6引入方式
   * import { ArcGISVectorTileLayer} from "@mapgis/webclient-common"
   * const igsVectorTileLayer = new ArcGISVectorTileLayer.({
   *   // 服务基地址
   *   url: 'http://{ip}:{port}/igs/rest/services/VectorTile/{serviceName}/VectorTileServer'
   * });
   * const style=igsVectorTileLayer.loadStyle()
   */
  loadStyle() {
    return this._vectorTileServer.getStyle().then((res) => res.data)
  }

  /**
   * 通过传入的json构造并返回一个新的几何对象<a id='fromJSON'></a>
   * @param {Object} [json] JSON对象
   * @example <caption><h7>通过传入的json构造并返回一个新的几何对象</h7></caption>
   * // ES5引入方式
   * const { ArcGISVectorTileLayer } = Zondy.Layer
   * // ES6引入方式
   * import { ArcGISVectorTileLayer } from "@mapgis/webclient-common"
   * const json = {
   *   // 服务基地址
   *   url: 'http://{ip}:{port}/igs/rest/services/Tile/{serviceName}/TileServer'
   * }
   * const igsVectorTileLayer = new ArcGISVectorTileLayer.fromJSON(json)
   */
  static fromJSON(json) {
    json = jsonClone(json)
    const _layer = new ArcGISVectorTileLayer(json)
    _layer._isFromJSON = true

    return _layer
  }

  /**
   * @description 转换为json对象
   * @return {Object} json对象
   */
  toJSON() {
    const json = super.toJSON()
    json.type = this.type
    json.description = this.description
    json.capabilities = this.capabilities
    json.url = this.url
    json.sublayers = this.sublayers.map((sublayer) =>
      toJSON(sublayer, ArcGISVectorTileSubLayer)
    )
    json.labelsRenderMode = this.labelsRenderMode
    return json
  }
}

Object.defineProperties(ArcGISVectorTileLayer.prototype, {
  /**
   * 空间裁剪范围
   * @member ArcGISVectorTileLayer.prototype.clippingArea
   * @type {Polygon|Extent|Circle|null}
   */
  clippingArea: {
    get() {
      return this._clippingArea
    },
    set(value) {
      this._clippingArea = value
      this._fireUpdateEvent('空间裁剪区域', [
        {
          name: 'clippingArea',
          params: [this._clippingArea],
          dataChanged: true,
          operationType: 'property'
        }
      ])
    }
  }
})

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