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