zcloud-gbs-bi-react/src/pages/Container/Map/js/useMapMethods.js

519 lines
18 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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 peoplePositionStartTime = useRef(null); // 当前人员定位会话共用的采样起始时间
// 设置中心点
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 () => {
const viewer = getViewer();
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: "港口" } },
}),
);
viewer.dataSources.add(entityCollection);
});
await addMergedEntityCollection(viewer, "portEntityCollection");
};
// 移除港口点
const removePortPoint = () => {
removeEntityCollection(getViewer(), "portEntityCollectionMerged");
};
// 添加分公司点
const addBranchOfficePoint = async (portArea = "", pointInfo = null) => {
const viewer = getViewer();
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: "分公司" },
},
}),
);
viewer.dataSources.add(entityCollection);
});
await addMergedEntityCollection(viewer, "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;
const viewer = getViewer();
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(viewer),
maximumHeights: Array.from({ length: pointCount }).fill(40),
minimumHeights: Array.from({ length: pointCount }).fill(0),
},
}),
);
});
viewer.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扎点图标");
const viewer = getViewer();
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 },
},
}),
);
viewer.dataSources.add(entityCollection);
});
const dataSource = await addMergedEntityCollection(
viewer,
"markEntityCollection",
options.markType,
);
enabledClustering(dataSource);
mitt.emit(changeCoverMaskVisibleMittKey, false);
};
// 删除点位
const removeMarkPoint = (markType) => {
removeEntityCollection(getViewer(), `markEntityCollectionMerged${markType ? `_${markType}` : ""}`);
};
// 人员定位使用 Viewer 的全局时钟;每次开启定位仅初始化一次,避免首批点位互相重置时钟。
const initPeoplePositionClock = () => {
const start = Cesium.JulianDate.now();
const viewer = getViewer();
peoplePositionStartTime.current = start;
viewer.clock.startTime = start.clone();
viewer.clock.currentTime = start.clone();
viewer.clock.clockRange = Cesium.ClockRange.CLAMPED;
viewer.clock.shouldAnimate = false;
return start;
};
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();
// 下一条实时定位到达前保持最后有效位置,避免全局时钟前进后点位短暂消失。
point.property.forwardExtrapolationType = Cesium.ExtrapolationType.HOLD;
point.property.forwardExtrapolationDuration = 0;
// 首批人员必须共用同一采样起点,后续实体创建不会影响已存在人员的播放时间。
const start = peoplePositionStartTime.current || initPeoplePositionClock();
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 },
},
});
};
const updatePeoplePoint = async (point) => {
const viewer = getViewer();
if (viewer.clock.shouldAnimate === false) {
viewer.clock.shouldAnimate = true;
}
if (point.icon_type !== point.lastIconType) {
const entity = viewer.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 = [];
peoplePositionStartTime.current = null;
};
return {
flyTo,
returnCurrentCenterPoint,
returnPreviousCenterPoint,
changeSceneMode,
addPortPoint,
removePortPoint,
addBranchOfficePoint,
removeBranchOfficePoint,
addFourColorDiagram,
removeFourColorDiagram,
addWall,
removeWall,
addMarkPoint,
removeMarkPoint,
initPeoplePositionClock,
addPeoplePoint,
updatePeoplePoint,
removePeoplePoint,
removePeoplePointAll,
};
}