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