import { useRef } from "react"; import portEntityBillboardImage from "~/assets/images/map_bi/point/dianwei.png"; import branchOfficeEntityBillboardImage from "~/assets/images/map_bi/point/gongsidianwei.png"; import people_blue from "~/assets/images/map_bi/point/ico_people_blue.png"; import people_orange from "~/assets/images/map_bi/point/ico_people_orange.png"; import people_red from "~/assets/images/map_bi/point/ico_people_red.png"; import people_yellow from "~/assets/images/map_bi/point/ico_people_yellow.png"; import edgeCMT from "./edge/cmt.js"; import edgeCSY from "./edge/csy.js"; import edgeCZKS from "./edge/czks.js"; import edgeQHD from "./edge/qhd.js"; import { addMergedEntityCollection, createEntityCollection, createId, getBillboard, getPosition, removeEntityCollection, } from "./mapUtils"; import mitt from "./mitt"; import { changeCoverMaskVisibleMittKey } from "./mittKey"; import { chunkedLoad, filterNull, formatPolygon } from "./utils"; import "./material/TrajectoryPolylineTrailLinkMaterialProperty.js"; import "./material/WallPolylineTrailLinkMaterialProperty"; const portPoint = [ { id: "00002", name: "沧州黄骅港矿石港务有限公司", introduce: "公司现共有10个泊位(10-20万吨级),设计年通过能力6400万吨。堆场面积176万平米,堆存能力740万吨,大型装卸设备44台套。", position: { x: 117.91412, y: 38.35902 }, corpinfoId: "f8da1790b1034058ae2efefd69af3284", }, { id: "00003", name: "秦皇岛港", introduce: "秦皇岛港分为东、西两个港区,现有生产性泊位50个,年设计通过能力2.26亿吨,经营货类主要包括煤炭、金属矿石、油品及液体化工、集装箱及其他杂货等。", position: { x: 119.61254, y: 39.92572 }, }, { id: "00004", name: "曹妃甸实业港务", introduce: "公司现共有6个泊位(5-30万吨级),设计年通过能力6550万吨。堆场面积146万平米,堆存能力1350万吨,大型装卸设备23台套。", position: { x: 118.51022, y: 38.93503 }, corpinfoId: "8854edee3aa94be496cee676b6d4845a", }, { id: "00005", name: "曹妃甸煤炭港务", introduce: "公司现共有5个泊位(5-10万吨级),设计年通过能力5000万吨。堆场面积83万平米,堆存能力373.5万吨,大型装卸设备19台套。", position: { x: 118.43701, y: 38.9866 }, corpinfoId: "6aa255d41602497fa0f934a822820df4", }, ]; const peopleImg = { blue: people_blue, orange: people_orange, red: people_red, yellow: people_yellow, }; // 人员定位颜色图片 const Cesium = window.Cesium; export default function useMapMethods(viewerRef, request) { // 获取当前 Cesium 实例:初始化前为空,地图方法会在 initMap 完成后被调用。 const getViewer = () => viewerRef.current; // 默认中心点:初始化和回到首页视角时使用,保持全局港区俯视高度。 const defineCenterPoint = { longitude: 119.6486945226887, latitude: 39.93555616569192, height: 900000, }; // 当前中心点:记录最近一次 flyTo 的视角,供外部按钮回到当前位置。 const currentCenterPointRef = useRef({ longitude: 119.6486945226887, latitude: 39.93555616569192, height: 900000, }); // 上一次中心点:从分公司返回港口层级时,需要回退到进入前的视角。 const previousCenterPointRef = useRef({ longitude: 119.6486945226887, latitude: 39.93555616569192, height: 900000, }); const peopleId = useRef([]); // 人员定位 ID 列表 // 设置中心点 const flyTo = ({ longitude, latitude, height } = defineCenterPoint) => { getViewer().camera.flyTo({ destination: getPosition(longitude, latitude, height), duration: 2 }); previousCenterPointRef.current = { ...currentCenterPointRef.current }; currentCenterPointRef.current = { longitude, latitude, height }; }; // 返回当前中心点 const returnCurrentCenterPoint = () => { const { longitude, latitude, height } = currentCenterPointRef.current; getViewer().camera.flyTo({ destination: getPosition(longitude, latitude, height), duration: 2 }); }; // 返回上一次中心点 const returnPreviousCenterPoint = () => { const { longitude, latitude, height } = previousCenterPointRef.current; getViewer().camera.flyTo({ destination: getPosition(longitude, latitude, height), duration: 2 }); currentCenterPointRef.current = { longitude, latitude, height }; }; // 切换场景模式 const changeSceneMode = (type = "3d") => { getViewer().scene.mode = { "2d": Cesium.SceneMode.COLUMBUS_VIEW, "3d": Cesium.SceneMode.SCENE3D }[type]; }; // 添加港口点 const addPortPoint = async () => { await chunkedLoad(portPoint, 10, async (item) => { const entityCollection = createEntityCollection("portEntityCollection"); entityCollection.entities.add( new Cesium.Entity({ id: item.id, name: item.name, position: getPosition(item.position.x, item.position.y), billboard: await getBillboard({ image: portEntityBillboardImage, name: item.name }), monitorItems: { data: { ...item, markType: "港口" } }, }), ); getViewer().dataSources.add(entityCollection); }); await addMergedEntityCollection(getViewer(), "portEntityCollection"); }; // 移除港口点 const removePortPoint = () => { removeEntityCollection(getViewer(), "portEntityCollectionMerged"); }; // 添加分公司点 const addBranchOfficePoint = async (portArea = "", pointInfo = null) => { let requestData = []; if (!pointInfo) { const { data } = await request.getCorpInfoListAll({ eqPortArea: portArea, inType: [0, 1, 2, 6] }); requestData = data || []; } mitt.emit(changeCoverMaskVisibleMittKey, true); let branchOfficePoint = []; if (!pointInfo) { branchOfficePoint = filterNull(requestData); } else { branchOfficePoint = filterNull([pointInfo]); } await chunkedLoad(branchOfficePoint, 10, async (item) => { const entityCollection = createEntityCollection("branchOfficeEntityCollection"); entityCollection.entities.add( new Cesium.Entity({ id: item.id, name: item.corpName, position: getPosition(item.longitude, item.latitude), billboard: await getBillboard({ image: branchOfficeEntityBillboardImage, name: item.corpName }), monitorItems: { data: { ...item, id: item.id, markType: "分公司" }, }, }), ); getViewer().dataSources.add(entityCollection); }); await addMergedEntityCollection(getViewer(), "branchOfficeEntityCollection"); mitt.emit(changeCoverMaskVisibleMittKey, false); }; // 删除分公司点 const removeBranchOfficePoint = () => { removeEntityCollection(getViewer(), "branchOfficeEntityCollectionMerged"); }; // 添加四色图 const addFourColorDiagram = (id, corpionId) => { const edgeMap = { "00003": edgeQHD, "00002": edgeCZKS, "00005": edgeCMT, "00004": edgeCSY, }; if (!edgeMap[id]) return; mitt.emit(changeCoverMaskVisibleMittKey, true); const currentEdge = edgeMap[id](); const entityCollection = createEntityCollection( "fourColorDiagramEntityCollection", ); // 先收集需要渲染的数据 const itemsToRender = []; for (const currentEdgeKey in currentEdge) { const currentEdgeItem = currentEdge[currentEdgeKey]; if (currentEdgeKey === "wallList") continue; for (let i = 0; i < currentEdgeItem.length; i++) { const item = currentEdgeItem[i]; // 如果有 corpionId,则只添加匹配的;否则全部添加 if (!corpionId || item.corpinfoId === corpionId) { itemsToRender.push(item); } if (!item.corpinfoId) { itemsToRender.push(item); } } } // 统一渲染 itemsToRender.forEach((item) => { const latitudeAndLongitude = formatPolygon(item); entityCollection.entities.add( new Cesium.Entity({ id: createId(), polygon: { hierarchy: Cesium.Cartesian3.fromDegreesArray(latitudeAndLongitude), extrudedHeight: item.stretchHeight, height: item.height, material: Cesium.Color.fromCssColorString(item.color), outline: !!item.strokeColor, outlineColor: Cesium.Color.fromCssColorString(item.strokeColor), }, }), ); }); getViewer().dataSources.add(entityCollection); mitt.emit(changeCoverMaskVisibleMittKey, false); }; // 删除四色图 const removeFourColorDiagram = () => { removeEntityCollection(getViewer(), "fourColorDiagramEntityCollection"); }; // 添加墙 const addWall = (id, corpionId) => { const edgeMap = { "00003": edgeQHD, "00002": edgeCZKS, "00005": edgeCMT, "00004": edgeCSY, }; if (!edgeMap[id]) return; mitt.emit(changeCoverMaskVisibleMittKey, true); const currentEdge = edgeMap[id](); const entityCollection = createEntityCollection("wallEntityCollection"); const wallList = currentEdge.wallList; if (!Array.isArray(wallList)) return; // 筛选需要渲染的边界墙 const itemsToRender = wallList.filter(item => !corpionId || item.corpinfoId === corpionId); // 统一渲染 itemsToRender.forEach((item) => { const latitudeAndLongitude = formatPolygon(item); const positions = Cesium.Cartesian3.fromDegreesArray(latitudeAndLongitude); const pointCount = latitudeAndLongitude.length / 2; entityCollection.entities.add( new Cesium.Entity({ id: createId(), wall: { positions, material: new Cesium.WallPolylineTrailLinkMaterialProperty(getViewer()), maximumHeights: Array.from({ length: pointCount }).fill(40), minimumHeights: Array.from({ length: pointCount }).fill(0), }, }), ); }); getViewer().dataSources.add(entityCollection); mitt.emit(changeCoverMaskVisibleMittKey, false); }; // 删除墙 const removeWall = () => { removeEntityCollection(getViewer(), "wallEntityCollection"); }; // 点位聚合 const enabledClustering = (dataSource) => { dataSource.clustering.enabled = true; // 聚合开启 dataSource.clustering.pixelRange = 50; // 设置像素范围,以扩展显示边框 dataSource.clustering.minimumClusterSize = 5; // 设置最小的聚合点数目,超过此数目才能聚合 let removeListener; // 按聚合层级创建对应图标 const pinBuilder = new Cesium.PinBuilder(); const pin100 = pinBuilder .fromText("100+", Cesium.Color.BLUE, 48) .toDataURL(); const pin50 = pinBuilder.fromText("50+", Cesium.Color.BLUE, 48).toDataURL(); const pin40 = pinBuilder.fromText("40+", Cesium.Color.RED, 48).toDataURL(); const pin30 = pinBuilder.fromText("30+", Cesium.Color.RED, 48).toDataURL(); const pin20 = pinBuilder.fromText("20+", Cesium.Color.RED, 48).toDataURL(); const pin10 = pinBuilder.fromText("10+", Cesium.Color.RED, 48).toDataURL(); // 10以内聚合图标 const singleDigitPins = Array.from({ length: 8 }); for (let i = 0; i < singleDigitPins.length; ++i) { singleDigitPins[i] = pinBuilder .fromText(`${i + 2}`, Cesium.Color.VIOLET, 48) .toDataURL(); } customStyle(); function customStyle() { if (Cesium.defined(removeListener)) { removeListener(); removeListener = undefined; } else { removeListener = dataSource.clustering.clusterEvent.addEventListener( (clusteredEntities, cluster) => { cluster.label.show = false; cluster.billboard.show = true; cluster.billboard.id = cluster.label.id; cluster.billboard.verticalOrigin = Cesium.VerticalOrigin.BOTTOM; cluster.billboard.horizontalOrigin = Cesium.HorizontalOrigin.CENTER; cluster.billboard.heightReference = Cesium.HeightReference.CLAMP_TO_GROUND; cluster.billboard.disableDepthTestDistance = Number.POSITIVE_INFINITY; const thresholdMap = [ { threshold: 100, image: pin100 }, { threshold: 50, image: pin50 }, { threshold: 40, image: pin40 }, { threshold: 30, image: pin30 }, { threshold: 20, image: pin20 }, { threshold: 10, image: pin10 }, ]; const matched = thresholdMap.find( ({ threshold }) => clusteredEntities.length >= threshold, ); cluster.billboard.image = matched ? matched.image : singleDigitPins[clusteredEntities.length - 2]; }, ); } const pixelRange = dataSource.clustering.pixelRange; dataSource.clustering.pixelRange = 0; dataSource.clustering.pixelRange = pixelRange; } }; /** * desc: 添加点位 * @param {Array} pointList * @param options {Object: {markIcon, type, subLabel, titleKey}} 配置项 * @param options.markIcon {String} 扎点图标 * @param options.markType {String} 扎点类型 * @param options.subLabel {String} 二级标题(用于弹窗标题) * @param options.titleKey {String} 标题键(用于从item中获取标题) * @param options.isShowModal {Boolean} 是否显示弹窗 */ const addMarkPoint = async (pointList, options) => { if (!options.markType) throw new Error("请传入markType(扎点类型)"); if (!options.markIcon) throw new Error("请传入markIcon(扎点图标)"); mitt.emit(changeCoverMaskVisibleMittKey, true); await chunkedLoad(filterNull(pointList), 10, async (item) => { const entityCollection = createEntityCollection( `markEntityCollection_${options.markType}`, ); const name = item[options.titleKey] || ""; entityCollection.entities.add( new Cesium.Entity({ id: createId(), name, position: getPosition(item.longitude || item.lng, item.latitude || item.lat), billboard: await getBillboard({ image: options.markIcon, name }), monitorItems: { data: { ...item, markType: `标记点_${options.markType}`, title: options.subLabel, isShowModal: options.isShowModal ?? true }, }, }), ); getViewer().dataSources.add(entityCollection); }); const dataSource = await addMergedEntityCollection( getViewer(), "markEntityCollection", options.markType, ); enabledClustering(dataSource); mitt.emit(changeCoverMaskVisibleMittKey, false); }; // 删除点位 const removeMarkPoint = (markType) => { removeEntityCollection(getViewer(), `markEntityCollectionMerged${markType ? `_${markType}` : ""}`); }; const addPeoplePoint = async (point, options) => { const image = peopleImg[point.icon_type]; if (!image) return; if (!options.markType) throw new Error("请传入markType(扎点类型)"); const clonePoint = { ...point }; point.property = new Cesium.SampledPositionProperty(); const start = Cesium.JulianDate.now(); const position = Cesium.Cartesian3.fromDegrees(point.x, point.y, 0); point.property.addSample(start, position); point.lastTime = start; point.lastIconType = point.icon_type; peopleId.current.push(point.id); getViewer().entities.add({ id: point.id, name: point.name, position: point.property, billboard: await getBillboard({ image, name: point.name || point.id, }), monitorItems: { data: { ...clonePoint, markType: `标记点_${options.markType}`, isShowModal: options.isShowModal ?? true }, }, }); getViewer().clock.startTime = start.clone(); getViewer().clock.currentTime = start.clone(); getViewer().clock.clockRange = Cesium.ClockRange.CLAMPED; getViewer().clock.shouldAnimate = false; }; const updatePeoplePoint = async (point) => { if (getViewer().clock.shouldAnimate === false) { getViewer().clock.shouldAnimate = true; } if (point.icon_type !== point.lastIconType) { const entity = getViewer().entities.getById(point.id); if (!entity) return; entity.billboard.image = ( await getBillboard({ image: peopleImg[point.icon_type], name: point.name || point.id, }) ).image; point.lastIconType = point.icon_type; } const position = Cesium.Cartesian3.fromDegrees(point.x, point.y, 0); const nextTime = Cesium.JulianDate.addSeconds( point.lastTime, 10, new Cesium.JulianDate(), ); point.property.addSample(nextTime, position); point.lastTime = nextTime; }; const removePeoplePoint = (id) => { getViewer().entities.removeById(id); peopleId.current = peopleId.current.filter(item => item !== id); }; const removePeoplePointAll = () => { peopleId.current.forEach((id) => { getViewer().entities.removeById(id); }); peopleId.current = []; }; return { flyTo, returnCurrentCenterPoint, returnPreviousCenterPoint, changeSceneMode, addPortPoint, removePortPoint, addBranchOfficePoint, removeBranchOfficePoint, addFourColorDiagram, removeFourColorDiagram, addWall, removeWall, addMarkPoint, removeMarkPoint, addPeoplePoint, updatePeoplePoint, removePeoplePoint, removePeoplePointAll, }; }