import * as Cesium from 'cesium' import { uid, degToCartesian, degsToCartesians, toCesiumColor, DEFAULT_VECTOR_LAYER_ID } from '@/map/utils/utils' // deps: { store, layerService } export function createEntityService(deps) { const { store, layerService } = deps const svc = { _ensureVectorLayer(layerId) { const id = layerId || DEFAULT_VECTOR_LAYER_ID if (!store.layers[id]) { return layerService .addLayer({ id, type: 'vector', source: null, options: { visible: true } }) .then(() => store.layers[id].obj) } return Promise.resolve(store.layers[id].obj) }, async addPoint(opts) { const o = opts || {} const ds = await this._ensureVectorLayer(o.layerId) const id = o.id || uid('point') const ent = new Cesium.Entity({ id, position: degToCartesian(o.position), point: { pixelSize: o.pixelSize || 8, color: toCesiumColor(o.color || '#1E90FF', 1), heightReference: o.clampToGround ? Cesium.HeightReference.CLAMP_TO_GROUND : Cesium.HeightReference.NONE, }, properties: o.properties || {}, }) ds.entities.add(ent) return id }, async addPolyline(opts) { const o = opts || {} const ds = await this._ensureVectorLayer(o.layerId) const id = o.id || uid('line') const ent = new Cesium.Entity({ id, polyline: { positions: degsToCartesians(o.positions || []), width: o.width || 3, material: toCesiumColor(o.color || '#FF4500', 1), clampToGround: !!o.clampToGround, }, properties: o.properties || {}, }) ds.entities.add(ent) return id }, async addPolygon(opts) { const o = opts || {} const ds = await this._ensureVectorLayer(o.layerId) const id = o.id || uid('polygon') const ent = new Cesium.Entity({ id, polygon: { hierarchy: new Cesium.PolygonHierarchy(degsToCartesians(o.positions || [])), material: toCesiumColor(o.fillColor || 'rgba(0,191,255,0.2)'), outline: true, outlineColor: toCesiumColor(o.outlineColor || '#00BFFF', 1), outlineWidth: o.outlineWidth || 1, perPositionHeight: !(o.clampToGround === undefined ? true : o.clampToGround), }, properties: o.properties || {}, }) ds.entities.add(ent) return id }, async addLabel(opts) { const o = opts || {} const ds = await this._ensureVectorLayer(o.layerId) const id = o.id || uid('label') const ent = new Cesium.Entity({ id, position: degToCartesian(o.position), label: { text: o.text || '', font: o.font || '14px sans-serif', fillColor: toCesiumColor(o.fillColor || '#ffffff', 1), outlineColor: toCesiumColor(o.outlineColor || '#000000', 1), outlineWidth: o.outlineWidth || 2, pixelOffset: o.pixelOffset || new Cesium.Cartesian2(0, -10), }, properties: o.properties || {}, }) ds.entities.add(ent) return id }, /** * 添加广告牌(Billboard)实体到地图 * @param {Object} opts - 配置选项 * @param {string} [opts.id] - 实体 ID,不提供则自动生成 * @param {string} [opts.layerId] - 图层 ID,不提供则使用默认图层 * @param {Array} opts.position - 位置 [经度, 纬度] 或 [经度, 纬度, 高度],高度默认为 0 * @param {string} opts.image - 图片 URL 或路径 * @param {number} [opts.width=32] - 图片宽度(像素) * @param {number} [opts.height=32] - 图片高度(像素) * @param {boolean} [opts.clampToGround=true] - 是否贴地 * @param {Cesium.VerticalOrigin} [opts.verticalOrigin] - 垂直对齐方式 * @param {Array|Cesium.Cartesian2} [opts.pixelOffset] - 像素偏移 [x, y] * @param {number} [opts.disableDepthTestDistance] - 禁用深度测试的距离 * @param {Object} [opts.properties] - 自定义属性 * @returns {Promise} 返回实体 ID */ async addBillboard(opts) { const o = opts || {} // 验证必需参数 if (!Array.isArray(o.position) || o.position.length < 2) { throw new Error('addBillboard 需要提供 position 参数 [经度, 纬度] 或 [经度, 纬度, 高度]') } const image = o.image || o.icon if (!image) { throw new Error('addBillboard 需要提供 image 或 icon 参数') } // 确保 position 包含高度,如果没有则默认为 0 const position = o.position.length === 2 ? [o.position[0], o.position[1], 0] : o.position const ds = await this._ensureVectorLayer(o.layerId) const id = o.id || uid('billboard') // 处理像素偏移 let pixelOffset if (o.pixelOffset instanceof Cesium.Cartesian2) { pixelOffset = o.pixelOffset } else if (Array.isArray(o.pixelOffset)) { pixelOffset = new Cesium.Cartesian2( o.pixelOffset[0] || 0, o.pixelOffset[1] || 0 ) } else if (o.pixelOffset && typeof o.pixelOffset === 'object') { pixelOffset = new Cesium.Cartesian2( o.pixelOffset.x || 0, o.pixelOffset.y || 0 ) } else { pixelOffset = new Cesium.Cartesian2(0, 0) } const ent = new Cesium.Entity({ id, position: degToCartesian(position), billboard: { image, width: o.width || 32, height: o.height || 32, verticalOrigin: o.verticalOrigin || Cesium.VerticalOrigin.BOTTOM, heightReference: o.clampToGround === false ? Cesium.HeightReference.NONE : Cesium.HeightReference.CLAMP_TO_GROUND, disableDepthTestDistance: typeof o.disableDepthTestDistance === 'number' ? o.disableDepthTestDistance : Number.POSITIVE_INFINITY, pixelOffset, }, properties: o.properties || {}, }) ds.entities.add(ent) return id }, removeEntity(entityId) { if (!entityId) return false for (const id in store.layers) { const rec = store.layers[id] if ((rec.type === 'vector' || rec.type === 'datasource') && rec.obj.entities) { const e = rec.obj.entities.getById(entityId) if (e) { rec.obj.entities.remove(e) return true } } } return false }, getEntity(entityId) { if (!entityId) return undefined for (const id in store.layers) { const rec = store.layers[id] if ((rec.type === 'vector' || rec.type === 'datasource') && rec.obj.entities) { const e = rec.obj.entities.getById(entityId) if (e) return e } } return undefined }, clearLayerEntities(layerId) { const id = layerId || DEFAULT_VECTOR_LAYER_ID const rec = store.layers[id] if (rec && rec.obj && rec.obj.entities) rec.obj.entities.removeAll() }, } return svc }