import { defaultValue, isNumber } from '../../util'
import Zondy from '../Zondy'
import Geometry from './Geometry'
import { GeometryType, IGSGeometryType } from '../enum'
import Point from './Point'
import Log from '../../util/Log'
import { calcExtent } from './Utiles'
/**
* 多点几何对象
* <br><br>[ES5引入方式]:<br/>
* Zondy.Geometry.MultiPoint() <br/>
* [ES6引入方式]:<br/>
* import { MultiPoint } from "@mapgis/webclient-common" <br/>
* <br/>
* @class MultiPoint
* @moduleEX GeometryModule
* @extends Geometry
* @param {Object} options 构造参数
* @param {Number[]} [options.coordinates = []] 几何点的坐标,支持任意单位,参考示例:<a href='#MultiPoint'>[多点几何对象]</a>
* @param {SpatialReference} [options.spatialReference = new Zondy.SpatialReference('EPSG:4326')] 几何点的空间参考系,默认4326,当不是4326时请指定坐标系,方便进行投影转换,参考示例:<a href='#SpatialReference'>[指定坐标系]</a>
* @summary <h5>支持如下方法:</h5>
* <a href='#addPoint'>[1、添加点坐标]</a><br/>
* <a href='#getPoint'>[2、返回指定索引处的点]</a><br/>
* <a href='#removePoint'>[3、从多点中移除一个点]</a><br/>
* <a href='#setPoint'>[4、更新指定索引处的点]</a><br/>
* <a href='#getIGSType'>[5、返回IGS所对应的GeometryModule型]</a><br/>
* <a href='#toOldIGSGeometry'>[6、返回igs1.0的几何对象]</a><br/>
* <a href='#toString'>[7、返回字符串]</a><br/>
* <a href='#toDots'>[8、返回Dots对象,仅包括多边形的外圈]</a>
* <a href='#fromJSON'>[9、通过传入的json构造并返回一个新的几何对象]</a><br/>
* <a href='#toJSON'>[10、导出为json对象]</a><br/>
* [11、克隆几何对象]{@link Geometry#clone}
*
* @example <caption><h5 id='MultiPoint'>创建几何对象</h5></caption>
* // ES5引入方式
* const { MultiPoint } = Zondy.Geometry
* // ES6引入方式
* import { MultiPoint } from "@mapgis/webclient-common"
* new MultiPoint({
* coordinates:[
* // 第一个点
* [100, 0.0],
* // 第二个点
* [101, 1.0]
* ]
* })
*
* @example <caption><h5 id='SpatialReference'>指定坐标系</h5></caption>
* // ES5引入方式
* const { Point } = Zondy.Geometry
* const { SpatialReference } = Zondy
* // ES6引入方式
* import { Point, SpatialReference } from "@mapgis/webclient-common"
* new Point({
* // 现在为3857坐标系
* coordinates:[
* // 第一个点
* [12929863.44711455, 3934286.575385226],
* // 第二个点
* [12060733.232006868, 3377247.5680546067]
* ],
* // 当不是4326时请指定坐标系,方便进行投影转换
* spatialReference: new SpatialReference('EPSG:3857')
* })
*/
class MultiPoint extends Geometry {
constructor(options) {
super(options)
// 构造几何对象
options = defaultValue(options, {})
/**
* 几何点的坐标
* @member {Array} MultiPoint.prototype.coordinates
*/
this.coordinates = defaultValue(options.coordinates, [])
if (!Array.isArray(this.coordinates)) {
throw new Error('坐标必须是数组')
}
for (let i = 0; i < this.coordinates.length; i++) {
if (this.coordinates[i].length < 2 || this.coordinates[i].length > 3) {
throw new Error('多点的数组长度必须在合理范围内(2或者3)')
}
// 确保所有点都有第三个值才是有z值
if (this.coordinates[i].length === 3) {
this.hasZ = true
} else {
this.hasZ = false
}
}
this.type = GeometryType.multiPoint
}
/**
* 添加点坐标,可以为Point对象、[x, y, z(可选)]数组、Point对象数组、[x, y, z(可选)]对象数组
* @param {Point|Number[]} point 可以是point,也可以是表示XY坐标的数字数组
* @param {String} [mode = 'add'] 添加模式,add:正常添加,deduplication:去除重复点
* @return {MultiPoint} 多点对象
* @example <caption><h7 id='addPoint'>添加点坐标</h7></caption>
* // ES5引入方式
* const { MultiPoint } = Zondy.Geometry
* // ES6引入方式
* import { MultiPoint } from "@mapgis/webclient-common"
* const multiPoint = new MultiPoint({
* coordinates:[
* // 第一个点
* [100, 0.0],
* // 第二个点
* [101, 1.0]
* ]
* })
* multiPoint.addPoint([102,2.0])
*/
addPoint(point, mode) {
mode = defaultValue(mode, 'add')
// 是点几何对象
if (point instanceof Point) {
this._addPoint(point, mode)
}
// 添加数组
else if (point instanceof Array) {
// 判断是否是点坐标数组
const isNumber = this._isNumberArray(point)
// 是点坐标数组
if (isNumber) {
// 添加点
this._addPoint(
new Point({
coordinates: point
}),
mode
)
}
// 不是点坐标数组
else {
for (let i = 0; i < point.length; i++) {
// 是点几何对象
if (point[i] instanceof Point) {
this._addPoint(point[i], mode)
}
// 是数组
else if (point[i] instanceof Array) {
const pointIsNumber = this._isNumberArray(point[i])
// 是点坐标对象
if (pointIsNumber) {
// 添加点
this._addPoint(
new Point({
coordinates: point[i]
}),
mode
)
}
// 点几何对象
else if (point instanceof Point) {
this._addPoint(point[i], mode)
}
// 不是点坐标对象或点几何
else {
Log.error('point的子元素类型不是点几何对象或者点坐标数组!')
}
} else {
Log.error('point的类型不是点几何对象或者点坐标数组!')
}
}
}
} else {
Log.error('point的类型不是点几何对象或者点坐标数组!')
}
// 更新外包盒
if (this.coordinates.length >= 2) {
this.extent = this._calcExtent(this.coordinates, this.hasZ)
}
this.coordinates = JSON.parse(JSON.stringify(this.coordinates))
return this
}
/**
* 添加点坐标
* @private
* @param {Point} point 点几何对象
* @param {String} [mode = 'add'] 添加模式,add:正常添加,deduplication:去除重复点
* */
_addPoint(point, mode) {
// 添加模式
if (mode === 'add') {
this.coordinates.push(point.coordinates)
}
// 去重添加模式
else if (!this.contains(point)) {
this.coordinates.push(point.coordinates)
}
}
/**
* 判断是否是点坐标数组
* @private
* @param {Array} point 要判断的对象
* @return {Boolean} 是否是点坐标数组
* */
_isNumberArray(point) {
// 先判断类型
if (!(point instanceof Array)) {
return false
}
// 判断是否是点坐标数组
let isNumber = true
if (point.length === 2 || point.length === 3) {
for (let i = 0; i < point.length; i++) {
if (!(typeof point[i] === 'number')) {
isNumber = false
break
}
}
} else {
isNumber = false
}
return isNumber
}
/**
* 判断多点对象是否包含某一个点
* @param {Point | Array<Number>} point 点对象
* @return {Boolean} 是否包含
* */
contains(point) {
// 点对象
if (point instanceof Point) {
for (let i = 0; i < this.coordinates.length; i++) {
if (point.equals(this.coordinates[i])) {
return true
}
}
}
// 坐标数组
else if (
point instanceof Array &&
(point.length === 2 || point.length === 3)
) {
for (let i = 0; i < this.coordinates.length; i++) {
const _point = new Point({
coordinates: point
})
if (_point.equals(this.coordinates[i])) {
return true
}
}
} else {
Log.error('必须为点对象或坐标数组才能比较是否相等!')
}
return false
}
/**
* 返回指定索引处的点
* @param {Number} index 点对象
* @return {Point} 在指定下标处的点
* @example <caption><h7 id='getPoint'>返回指定索引处的点</h7></caption>
* // ES5引入方式
* const { MultiPoint } = Zondy.Geometry
* // ES6引入方式
* import { MultiPoint } from "@mapgis/webclient-common"
* const multiPoint = new MultiPoint({
* coordinates:[
* // 第一个点
* [100, 0.0],
* // 第二个点
* [101, 1.0]
* ]
* })
* const point = multiPoint.getPoint(0)
*/
getPoint(index) {
index = Number(index)
if (!this._isValidate(index)) {
throw new Error('未找到指定索引,请输入正确的索引')
}
const points = this._cloneCoordinates()
return new Point({
coordinates: points[index]
})
}
/**
* 从多点中移除一个点。索引指定要删除的点
* @param {Number} index 点对象
* @return {Point | null} 返回被移除的点
* @example <caption><h7 id='removePoint'>从多点中移除一个点</h7></caption>
* // ES5引入方式
* const { MultiPoint } = Zondy.Geometry
* // ES6引入方式
* import { MultiPoint } from "@mapgis/webclient-common"
* const multiPoint = new MultiPoint({
* coordinates:[
* // 第一个点
* [100, 0.0],
* // 第二个点
* [101, 1.0]
* ]
* })
* const point = multiPoint.removePoint(0)
*/
removePoint(index) {
index = Number(index)
if (!this._isValidate(index)) return null
const points = this._cloneCoordinates()
this.coordinates.splice(index, 1)
this.coordinates = JSON.parse(JSON.stringify(this.coordinates))
return new Point({
coordinates: points[index]
})
}
/**
* 更新指定索引处的点
* @param {Number} index 点在points属性中的索引
* @param {Point|Number[]} point 在新位置指定的几何点
* @return {MultiPoint} 返回更新后的多点
* @example <caption><h7 id='setPoint'>更新指定索引处的点</h7></caption>
* // ES5引入方式
* const { MultiPoint } = Zondy.Geometry
* // ES6引入方式
* import { MultiPoint } from "@mapgis/webclient-common"
* const multiPoint = new MultiPoint({
* coordinates:[
* // 第一个点
* [100, 0.0],
* // 第二个点
* [101, 1.0]
* ]
* })
* multiPoint.setPoint(1, [103, 3.0])
*/
setPoint(index, point) {
if (!this._isValidate(index)) {
throw new Error('未找到指定索引,请输入正确的索引')
}
const points = this._cloneCoordinates()
if (Array.isArray(points)) {
points.splice(index, 1, Point.toCoordinates(point))
this.coordinates = points
}
this.coordinates = JSON.parse(JSON.stringify(this.coordinates))
return this
}
/**
* 返回IGS所对应的GeometryModule型<a id='getIGSType'></a>
* @returns string GeometryModule型
*/
getIGSType() {
return IGSGeometryType.multiPoint
}
/**
* 通过传入的json构造并返回一个新的几何对象
* @param {Object} [json] JSON对象
* @example <caption><h7 id='fromJSON'>通过传入的json构造并返回一个新的几何对象</h7></caption>
* // ES5引入方式
* const { MultiPoint } = Zondy.Geometry
* // ES6引入方式
* import { MultiPoint } from "@mapgis/webclient-common"
* const json = {
* coordinates:[
* // 第一个点
* [100, 0.0],
* // 第二个点
* [101, 1.0]
* ]
* }
* const multiPoint = MultiPoint.fromJSON(json)
*/
static fromJSON(json) {
json = defaultValue(json, {})
return new MultiPoint(json)
}
/**
* <a id='toJSON'></a>
* 导出为json对象
* @return {Object} json对象
*/
toJSON() {
const json = super.toJSON()
json.coordinates = JSON.parse(JSON.stringify(this.coordinates))
json.extent = this.extent ? this.extent.toJSON() : undefined
return json
}
/**
* 返回igs1.0的几何对象<a id='toOldIGSGeometry'></a>
* @returns Object igs1.0的几何对象
*/
toOldIGSGeometry() {
const PntGeom = []
for (let i = 0; i < this.coordinates.length; i++) {
let Dot = {}
Dot = {
x: this.coordinates[i][0],
y: this.coordinates[i][1]
}
PntGeom.push({
Dot,
GID: i
})
}
return {
PntGeom
}
}
/**
* 返回如下格式的字符串:"x0,y0,x1,y1,x2,y2"<a id='toString'></a>
* @returns string
*/
toString() {
let _str = ''
// 开始循环线内的点
for (let i = 0; i < this.coordinates.length; i++) {
// 线内的点
const _point = this.coordinates[i]
// 有可能是xyz,因此for循环
for (let k = 0; k < _point.length; k++) {
// 加,号
_str += `${_point[k]},`
}
}
// 删除最后一个,
_str = _str.substring(0, _str.length - 1)
return _str
}
/**
* 返回Dots对象,仅包括多边形的外圈<a id='toDots'></a>
* @returns Array Dots对象
*/
toDots() {
const Dots = []
const coordinates = this.coordinates
for (let i = 0; i < coordinates.length; i++) {
Dots.push({
x: coordinates[i][0],
y: coordinates[i][1]
})
}
return Dots
}
/**
* @function MultiPoint.prototype._isValidate
* @private
* @description 判断输入是否合法
* @param {Number} pointIndex 数组下标
* @returns {Boolean} 输入是否合法
*/
_isValidate(pointIndex) {
const len = this.coordinates.length
if (!isNumber(pointIndex) || pointIndex < 0 || pointIndex >= len) {
return false
}
return true
}
/**
* @function MultiPoint.prototype._cloneCoordinates
* @private
* @description 拷贝几何信息
* @returns {Array}
*/
_cloneCoordinates() {
return JSON.parse(JSON.stringify(this.coordinates))
}
/**
* @function LineString.prototype._calcExtent
* @private
* @description 计算外包盒
* @param {Array} coordinates 坐标点
* @param {Boolean} hasZ 是否是三维
* @returns {Extent}
*/
_calcExtent(coordinates, hasZ) {
if (coordinates.length > 1) {
return calcExtent(coordinates, hasZ, GeometryType.lineString)
}
}
/**
* 克隆几何对象
* @return {Geometry} 克隆后的几何对象
*/
clone() {
return new MultiPoint(this.toJSON())
}
}
Object.defineProperties(MultiPoint.prototype, {
/**
* 外包盒
* @member {Number} MultiPoint.prototype.extent
* */
extent: {
configurable: false,
get() {
return this._calcExtent(this.coordinates, this.hasZ)
}
}
})
Zondy.Geometry.MultiPoint = MultiPoint
export default MultiPoint