import { deepMerge, getLevelMiddleHeight, wgs84ToGcj02Format, convertPosition, } from "../util"; import Mars3dEntity from "../base/mars3d_entity"; import GaodeEntity from "../base/gaode_entity"; import SijiEntity from "../base/siji_entity"; import CesiumEntity from "../base/cesium_entity"; export default (hnMap: any) => { const defaultOption = { id: "", type: "label", position: [], text: "", fontSize: 16, fontWeight: "normal", fontFamily: "楷体", color: "#000000", background: "", opacity: 1, outline: false, outlineColor: "#ffffff", outlineWidth: 2, outlineOpacity: 1, offset: [0, 0], angle: 0, scaleByDistance: true, distanceDisplayCondition: false, distanceDisplayCondition_far: 1, distanceDisplayCondition_near: 18, data: null, }; class mars3d_class extends Mars3dEntity { type: any = "label"; id: any = null; option: any = JSON.parse(JSON.stringify(defaultOption)); config: any = null; graphic: any = null; constructor(option: any) { super(hnMap); this.id = option.id; deepMerge(this.option, option); this.config = this.formatConfig(this.option); if (this.option.type === "label") { this.graphic = new mars3d.graphic.LabelEntity(this.config); } else if (this.option.type === "mapLabel") { this.graphic = new mars3d.graphic.RectangleEntity(this.config); } } formatConfig(option: any) { if (option.type === "label") { const position = new mars3d.LngLatPoint( option.position[0], option.position[1], option.position[2] || 0 ); const distanceDisplayCondition_far = getLevelMiddleHeight( option.distanceDisplayCondition_far ); const distanceDisplayCondition_near = getLevelMiddleHeight( option.distanceDisplayCondition_near ); return { id: option.id, position: position, style: { text: option.text, color: option.color, opacity: option.opacity, font_size: option.fontSize, font_family: option.fontFamily, outline: option.outline, outlineColor: option.outlineColor, outlineWidth: option.outlineWidth, outlineOpacity: option.outlineOpacity, pixelOffset: convertPosition(option.offset), scaleByDistance: option.scaleByDistance, clampToGround: !option.position[2], distanceDisplayCondition: option.distanceDisplayCondition, distanceDisplayCondition_far: distanceDisplayCondition_far, distanceDisplayCondition_near: distanceDisplayCondition_near, background: !!option.background, backgroundColor: option.background, }, attr: option.data, }; } else if (option.type === "mapLabel") { const p1 = new mars3d.LngLatPoint( option.position[0][0], option.position[0][1] ); const p2 = new mars3d.LngLatPoint( option.position[1][0], option.position[1][1] ); return { id: option.id, positions: [p1, p2], style: { clampToGround: !(option.position[0] && option.position[0][2]), materialOptions: { text: option.text, font_family: "楷体", color: option.color, font_weight: option.fontWeight, font_size: option.fontSize, background: !!option.background, stroke: option.outline, strokeColor: option.outlineColor, strokeWidth: option.outlineWidth, opacity: option.opacity, }, materialType: mars3d.MaterialType.Text, rotationDegree: option.angle, distanceDisplayCondition: option.distanceDisplayCondition, distanceDisplayCondition_far: option.distanceDisplayCondition_far, distanceDisplayCondition_near: option.distanceDisplayCondition_near, }, attr: option.data, }; } } set(option: any) { deepMerge(this.option, option); this.config = this.formatConfig(this.option); this.graphic.setOptions(this.config); } } class gaode_class extends GaodeEntity { id: any = null; option: any = JSON.parse(JSON.stringify(defaultOption)); config: any = null; graphic: any = null; constructor(option: any) { super(hnMap); this.id = option.id; deepMerge(this.option, option); this.config = this.formatConfig(this.option); this.graphic = new AMap.Text(this.config); } formatConfig(option: any) { let amapPosition = wgs84ToGcj02Format(option.position); return { extData: { id: option.id, data: option.data, }, text: option.text, anchor: "center", // 设置文本标记锚点 draggable: true, cursor: "pointer", style: { border: 0, "text-align": "center", "font-size": option.fontSize, color: option.color, "background-color": option.background, }, position: [amapPosition[0], amapPosition[1]], offset: option.offset, }; } set(option: any) { deepMerge(this.option, option); this.config = this.formatConfig(this.option); this.graphic.setText(this.config.text); this.graphic.setStyle(this.config.style); this.graphic.setPosition(this.config.position); } } class siji_class extends SijiEntity { type: any = "label"; id: any = null; option: any = JSON.parse(JSON.stringify(defaultOption)); config: any = null; constructor(option: any) { super(hnMap); this.id = option.id; deepMerge(this.option, option); this.config = this.formatConfig(this.option); } formatConfig(option: any) { let config: any = {}; config = { id: option.id, type: "symbol", source: { type: "geojson", data: { type: "FeatureCollection", features: [], }, }, layout: { "text-field": "{name}", "text-size": Number(option.fontSize), "text-anchor": "top", // 顶部对齐 "text-offset": this.pixelOffsetToTextOffset( option.offset, option.fontSize ), "text-max-width": 8, "text-font": ["Microsoft YaHei Regular"], }, // 文本样式 paint: { "text-color": option.color, "text-halo-color": option.outline ? option.outlineColor : "transparent", // 文字外边线颜色 "text-halo-width": option.outline ? Number(option.outlineWidth) : 0, // 文字外边线宽度 "text-opacity": Number(option.opacity), // "text-halo-opacity": option.outline ? option.outlineOpacity : 1, }, // 填充样式 }; if (option.type === "label") { config.source.data.features[0] = { type: "Feature", geometry: { type: "Point", coordinates: convertPosition(option.position), }, properties: { name: option.text, }, }; } else if (option.type === "mapLabel") { option.position.forEach((item: any) => { config.source.data.features.push({ type: "Feature", properties: { name: option.text }, geometry: { type: "Point", coordinates: convertPosition(option.position), }, }); }); } return config; } /** * 将 pixelOffset 转换为 text-offset (ems) * @param {Array} pixelOffset - [x, y] 像素偏移 * @param {number} textSizePx - 字体大小(px) * @returns {Array} text-offset in ems */ pixelOffsetToTextOffset(pixelOffset: any, textSizePx: any): Array { return [pixelOffset[0] / textSizePx, pixelOffset[1] / textSizePx]; } set(option: any) { deepMerge(this.option, option); this.config = this.formatConfig(this.option); let mySource = hnMap.map.map.getSource(this.config.id); if (!mySource) { return; } if (this.option.type == "label") { mySource.setData({ type: "FeatureCollection", features: [ { type: "Feature", properties: { name: this.option.text }, geometry: { type: "Point", coordinates: convertPosition(option.position), }, }, ], }); } else if (this.option.type == "mapLabel") { // 构建新的数据源对象 const sourceObj = { type: "FeatureCollection", features: this.option.position.map((item: any) => ({ type: "Feature", properties: { name: this.option.text }, geometry: { type: "Point", coordinates: item, }, })), }; // 更新数据源 mySource.setData(sourceObj); } for (let key in this.config) { if (this.config.hasOwnProperty(key)) { if (key == "paint") { for (let key2 in this.config[key]) { if (this.config[key].hasOwnProperty(key2)) { // 遍历 paint 属性 hnMap.map.map.setPaintProperty( this.config.id, key2, key2 == "text-halo-width" || key2 == "text-opacity" ? Number(this.config[key][key2]) : this.config[key][key2] ); } } } if (key == "layout") { for (let key2 in this.config[key]) { if (this.config[key].hasOwnProperty(key2)) { // 遍历 layout 属性 hnMap.map.map.setLayoutProperty( this.config.id, key2, key2 === "text-size" ? Number(this.config[key][key2]) : this.config[key][key2] ); } } } } } } } class cesium_class extends CesiumEntity { type: any = "label"; id: any = null; option: any = JSON.parse(JSON.stringify(defaultOption)); config: any = null; graphic: any = null; constructor(option: any) { super(hnMap); this.id = option.id; deepMerge(this.option, option); this.config = this.formatConfig(this.option); // 创建 Cesium 实体 this.graphic = new Cesium.Entity(this.config); // 添加自定义属性 this.graphic.userData = { type: "label", originalData: this.option.data || {}, }; } formatConfig(option: any) { // 转换位置坐标为 Cesium 格式 let pos = convertPosition(option.position); const position = Cesium.Cartesian3.fromDegrees( pos[0], pos[1], pos[2] || 0 ); const config: any = { id: option.id, name: option.id, position: position, label: this.createLabelStyle(option), properties: option.data || {}, // 添加自定义数据 }; // 添加距离显示条件 if (option.distanceDisplayCondition) { const near = getLevelMiddleHeight(option.distanceDisplayCondition_near); const far = getLevelMiddleHeight(option.distanceDisplayCondition_far); config.label.distanceDisplayCondition = new Cesium.DistanceDisplayCondition(near, far); } return config; } // 创建标签材质 createLabelStyle(option: any) { // 构建完整的字体字符串 const fontWeight = Number(option.fontWeight) || "normal"; const fontSize = Number(option.fontSize) || 16; const fontFamily = option.fontFamily || "sans-serif"; const font = `${fontWeight} ${fontSize}px ${fontFamily}`; const style: any = { text: option.text, font: font, fillColor: Cesium.Color.fromCssColorString(option.color).withAlpha( Number(option.opacity) || 1 ), outline: option.outline, outlineColor: Cesium.Color.fromCssColorString( option.outlineColor || "#ffffff" ).withAlpha(Number(option.outlineOpacity) || 1), outlineWidth: option.outline ? option.outlineWidth : 0, style: Cesium.LabelStyle.FILL_AND_OUTLINE, pixelOffset: new Cesium.Cartesian2( Number(option.offset[0]) || 0, Number(option.offset[1]) || 0 ), clampToGround: !option.position[2], showBackground: !!option.background, backgroundColor: option.background ? Cesium.Color.fromCssColorString(option.background).withAlpha( Number(option.opacity) || 1 ) : Cesium.Color.BLACK.withAlpha(0.5), scaleByDistance: option.scaleByDistance ? new Cesium.NearFarScalar(1.0e2, 1.0, 1.0e7, 0.1) : undefined, }; return style; } // 设置方法 set(option: any) { deepMerge(this.option, option); // 更新实体 if (this.graphic && this.graphic.label) { const label = this.graphic.label; // 批量更新标签样式属性 const updatedStyle = this.createLabelStyle(this.option); Object.assign(label, updatedStyle); // 更新偏移 if (option.offset !== undefined) { label.pixelOffset = new Cesium.Cartesian2( Number(option.offset[0]), Number(option.offset[1]) ); } if (option.scaleByDistance) { label.scaleByDistance = new Cesium.NearFarScalar( 1.0e2, 1.0, 1.0e7, 0.1 ); } else { label.scaleByDistance = undefined; } // 更新位置 if (option.position !== undefined) { let pos = convertPosition(option.position); const position = Cesium.Cartesian3.fromDegrees( pos[0], pos[1], pos[2] || 0 ); // this.graphic.position = Cesium.Cartesian3.fromDegrees( // option.position[0], // option.position[1], // option.position[2] || 0 // ); } // 更新距离显示条件 if (option.distanceDisplayCondition) { const near = getLevelMiddleHeight( option.distanceDisplayCondition_near ); const far = getLevelMiddleHeight(option.distanceDisplayCondition_far); label.distanceDisplayCondition = new Cesium.DistanceDisplayCondition( near, far ); } else { label.distanceDisplayCondition = undefined; } } } // 销毁方法 destroy() { if (this.graphic && hnMap.map.map && hnMap.map.map.entities) { hnMap.map.map.entities.remove(this.graphic); } } // 打开弹窗(如果需要) openPopup(htmlContent: string) { if (this.graphic && hnMap.map) { // 设置实体的描述属性 this.graphic.description = htmlContent; // 选中实体以显示弹窗 hnMap.map.selectedEntity = this.graphic; } } } const fn: any = { mars3d: mars3d_class, gaode: gaode_class, siji: siji_class, cesium: cesium_class, }; return fn[hnMap.mapType]; };