Compare commits
5 Commits
1f8f47f946
...
66a8ebf6f7
| Author | SHA1 | Date |
|---|---|---|
|
|
66a8ebf6f7 | |
|
|
936a96b184 | |
|
|
f32d44bad9 | |
|
|
273aa353ed | |
|
|
cff14a1652 |
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { declareRequest } from "@cqsjjb/jjb-dva-runtime";
|
||||||
|
|
||||||
|
// 人员轨迹按终端或人员编号、时间范围查询,接口参数由轨迹筛选面板传入。
|
||||||
|
export const getPeopleTrajectory = declareRequest(
|
||||||
|
"getPeopleTrajectoryLoading",
|
||||||
|
"Post > @/personnelPosition/bi/personTrace",
|
||||||
|
);
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 2.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
|
|
@ -9,3 +9,4 @@ export const NS_GLOBAL = defineNamespace("global");
|
||||||
export const NS_BI_STATISTICS = defineNamespace("biStatistics");
|
export const NS_BI_STATISTICS = defineNamespace("biStatistics");
|
||||||
export const NS_BI_MARK = defineNamespace("biMark");
|
export const NS_BI_MARK = defineNamespace("biMark");
|
||||||
export const NS_BI_MARK_INFO = defineNamespace("biMarkInfo");
|
export const NS_BI_MARK_INFO = defineNamespace("biMarkInfo");
|
||||||
|
export const NS_BI_PEOPLE = defineNamespace("biPeople");
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
||||||
|
import { message } from "antd";
|
||||||
import { produce } from "immer";
|
import { produce } from "immer";
|
||||||
import { AnimatePresence, motion } from "motion/react";
|
import { AnimatePresence, motion } from "motion/react";
|
||||||
import { useContext, useEffect, useState } from "react";
|
import { useContext, useEffect, useState } from "react";
|
||||||
|
|
@ -7,8 +8,10 @@ import bg8 from "~/assets/images/map_bi/bottom_utils/bg8.png";
|
||||||
import titleImg from "~/assets/images/map_bi/bottom_utils/title.png";
|
import titleImg from "~/assets/images/map_bi/bottom_utils/title.png";
|
||||||
import titleOnImg from "~/assets/images/map_bi/bottom_utils/title_on.png";
|
import titleOnImg from "~/assets/images/map_bi/bottom_utils/title_on.png";
|
||||||
import { NS_BI_MARK } from "~/enumerate/namespace";
|
import { NS_BI_MARK } from "~/enumerate/namespace";
|
||||||
import { branchOfficeUtilsList } from "~/pages/Container/Map/components/BottomUtils/branchOfficeUtilsList";
|
|
||||||
import { Context } from "~/pages/Container/Map/js/context";
|
import { Context } from "~/pages/Container/Map/js/context";
|
||||||
|
import usePeoplePosition from "~/pages/Container/Map/js/usePeoplePosition";
|
||||||
|
import PeopleTrajectorySelect from "../PeopleTrajectorySelect";
|
||||||
|
import { branchOfficeUtilsList } from "./branchOfficeUtilsList";
|
||||||
import { portUtilsList } from "./portUtilsList";
|
import { portUtilsList } from "./portUtilsList";
|
||||||
import { usePortUtilsAnimation } from "./usePortUtilsAnimation";
|
import { usePortUtilsAnimation } from "./usePortUtilsAnimation";
|
||||||
import "./index.less";
|
import "./index.less";
|
||||||
|
|
@ -65,13 +68,22 @@ function BottomUtils(props) {
|
||||||
portArea,
|
portArea,
|
||||||
} = useContext(Context);
|
} = useContext(Context);
|
||||||
|
|
||||||
const bottomUtilsMode = !pureMap && currentPort && !currentBranchOffice
|
const [peopleList, setPeopleList] = useState([]);
|
||||||
|
const { connect: peoplePositionConnect, disconnect: peoplePositionDisconnect } = usePeoplePosition(
|
||||||
|
mapMethods,
|
||||||
|
currentBranchOffice,
|
||||||
|
setPeopleList,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 菜单数据只由地图层级决定;纯净地图仅隐藏工具栏,不能重置已选菜单。
|
||||||
|
const bottomUtilsBaseMode = currentPort && !currentBranchOffice
|
||||||
? "port"
|
? "port"
|
||||||
: !pureMap && currentPort && currentBranchOffice ? "branchOffice" : null;
|
: currentPort && currentBranchOffice ? "branchOffice" : null;
|
||||||
const [list, setList] = useState(() => bottomUtilsMode === "port"
|
const bottomUtilsMode = pureMap ? null : bottomUtilsBaseMode;
|
||||||
|
const [list, setList] = useState(() => bottomUtilsBaseMode === "port"
|
||||||
? portUtilsList
|
? portUtilsList
|
||||||
: bottomUtilsMode === "branchOffice" ? branchOfficeUtilsList : []);
|
: bottomUtilsBaseMode === "branchOffice" ? branchOfficeUtilsList : []);
|
||||||
const [listMode, setListMode] = useState(bottomUtilsMode);
|
const [listMode, setListMode] = useState(bottomUtilsBaseMode);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
parentControls: portUtilsParentControls,
|
parentControls: portUtilsParentControls,
|
||||||
|
|
@ -88,6 +100,10 @@ function BottomUtils(props) {
|
||||||
);
|
);
|
||||||
|
|
||||||
const resetAllCheck = () => {
|
const resetAllCheck = () => {
|
||||||
|
// 底部工具栏被外部操作重置时,人员定位的 WebSocket 和 Cesium 实体不会随勾选状态自动销毁,需要统一清理。
|
||||||
|
peoplePositionDisconnect();
|
||||||
|
mapMethods.current.removePeoplePointAll();
|
||||||
|
setPeopleList([]);
|
||||||
setList(produce((draft) => {
|
setList(produce((draft) => {
|
||||||
draft.forEach((item) => {
|
draft.forEach((item) => {
|
||||||
item.list.forEach((item1) => {
|
item.list.forEach((item1) => {
|
||||||
|
|
@ -98,17 +114,17 @@ function BottomUtils(props) {
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (bottomUtilsMode === "port") {
|
if (bottomUtilsBaseMode === "port") {
|
||||||
setList(portUtilsList);
|
setList(portUtilsList);
|
||||||
}
|
}
|
||||||
else if (bottomUtilsMode === "branchOffice") {
|
else if (bottomUtilsBaseMode === "branchOffice") {
|
||||||
setList(branchOfficeUtilsList);
|
setList(branchOfficeUtilsList);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
setList([]);
|
setList([]);
|
||||||
}
|
}
|
||||||
setListMode(bottomUtilsMode);
|
setListMode(bottomUtilsBaseMode);
|
||||||
}, [bottomUtilsMode]);
|
}, [bottomUtilsBaseMode]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
resetAllCheck();
|
resetAllCheck();
|
||||||
|
|
@ -118,43 +134,96 @@ function BottomUtils(props) {
|
||||||
actions.setBottomUtilsIndex(bottomUtilsCurrentIndex === index ? -1 : index);
|
actions.setBottomUtilsIndex(bottomUtilsCurrentIndex === index ? -1 : index);
|
||||||
};
|
};
|
||||||
|
|
||||||
const optionsItemsClick = async (index, index1, item, item1) => {
|
// 统一维护子菜单勾选状态,避免各业务分支直接修改列表数据。
|
||||||
const check = !list[index].list[index1].check;
|
const updateOptionCheck = (index, index1, check) => {
|
||||||
if (check) {
|
|
||||||
if (item1.request) {
|
|
||||||
let pointData = [];
|
|
||||||
const requestParams = { portArea, corpinfoId: currentBranchOffice, pageIndex: 1, pageSize: 9999 };
|
|
||||||
if (dangerWorkTypes.includes(item1.type)) {
|
|
||||||
requestParams.workType = item1.type;
|
|
||||||
}
|
|
||||||
if (gateTypes.includes(item1.type)) {
|
|
||||||
requestParams.gateType = item1.type;
|
|
||||||
}
|
|
||||||
const { data } = await props[item1.request](requestParams);
|
|
||||||
pointData = [...(data || [])];
|
|
||||||
if (dangerWorkTypes.includes(item1.type)) {
|
|
||||||
for (let i = 0; i < pointData.length; i++) {
|
|
||||||
pointData[i].latitude = pointData[i].info.latitude;
|
|
||||||
pointData[i].longitude = pointData[i].info.longitude;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mapMethods.current.addMarkPoint(pointData, {
|
|
||||||
markType: item1.type,
|
|
||||||
markIcon: item1.markIcon,
|
|
||||||
subLabel: item1.label,
|
|
||||||
titleKey: item1.titleKey,
|
|
||||||
isShowModal: item1.isShowModal,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
mapMethods.current.removeMarkPoint(item1.type);
|
|
||||||
}
|
|
||||||
setList(produce((draft) => {
|
setList(produce((draft) => {
|
||||||
draft[index].list[index1].check = check;
|
draft[index].list[index1].check = check;
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 人员轨迹依赖实时定位数据;取消定位时需要同步取消同组轨迹。
|
||||||
|
const resetPeopleTrajectory = (index) => {
|
||||||
|
setList(produce((draft) => {
|
||||||
|
draft[index].list.forEach((option) => {
|
||||||
|
if (option.type === "peopleTrajectory")
|
||||||
|
option.check = false;
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
// 根据菜单类型补充接口参数转换坐标。
|
||||||
|
const getMarkPointData = async (item) => {
|
||||||
|
const requestParams = { portArea, corpinfoId: currentBranchOffice, pageIndex: 1, pageSize: 9999 };
|
||||||
|
if (dangerWorkTypes.includes(item.type))
|
||||||
|
requestParams.workType = item.type;
|
||||||
|
if (gateTypes.includes(item.type))
|
||||||
|
requestParams.gateType = item.type;
|
||||||
|
|
||||||
|
const { data } = await props[item.request](requestParams);
|
||||||
|
const pointData = data || [];
|
||||||
|
if (dangerWorkTypes.includes(item.type)) {
|
||||||
|
return pointData.map(point => ({
|
||||||
|
...point,
|
||||||
|
latitude: point.info.latitude,
|
||||||
|
longitude: point.info.longitude,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
return pointData;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 查询并渲染带请求配置的菜单点位;无请求配置的菜单无需执行地图操作。
|
||||||
|
const addMarkPoint = async (item) => {
|
||||||
|
if (!item.request)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const pointData = await getMarkPointData(item);
|
||||||
|
mapMethods.current.addMarkPoint(pointData, {
|
||||||
|
markType: item.type,
|
||||||
|
markIcon: item.markIcon,
|
||||||
|
subLabel: item.label,
|
||||||
|
titleKey: item.titleKey,
|
||||||
|
isShowModal: item.isShowModal,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const optionsItemsClick = async (index, index1, item) => {
|
||||||
|
const check = !list[index].list[index1].check;
|
||||||
|
updateOptionCheck(index, index1, check);
|
||||||
|
|
||||||
|
if (!check) {
|
||||||
|
if (item.type === "peoplePosition") {
|
||||||
|
resetPeopleTrajectory(index);
|
||||||
|
mapMethods.current.removePeopleTrajectory();
|
||||||
|
peoplePositionDisconnect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mapMethods.current.removeMarkPoint(item.type);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.type === "peoplePosition" && currentPort === "00003") {
|
||||||
|
// 连接 WebSocket 前初始化本次定位会话的统一时钟,首批人员共享同一采样基准。
|
||||||
|
mapMethods.current.initPeoplePositionClock();
|
||||||
|
peoplePositionConnect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.type === "peopleTrajectory") {
|
||||||
|
const isPeoplePositionChecked = list[index].list.some(
|
||||||
|
option => option.type === "peoplePosition" && option.check,
|
||||||
|
);
|
||||||
|
if (!isPeoplePositionChecked) {
|
||||||
|
message.warning("请先选择人员定位!!!");
|
||||||
|
updateOptionCheck(index, index1, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await addMarkPoint(item);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isPeopleTrajectoryChecked = list.some(item => item.list?.some(option => option.type === "peopleTrajectory" && option.check));
|
||||||
|
|
||||||
const renderPortUtils = () => {
|
const renderPortUtils = () => {
|
||||||
if (bottomUtilsMode === "port" && listMode === bottomUtilsMode) {
|
if (bottomUtilsMode === "port" && listMode === bottomUtilsMode) {
|
||||||
return list.map((item, index) => {
|
return list.map((item, index) => {
|
||||||
|
|
@ -194,7 +263,7 @@ function BottomUtils(props) {
|
||||||
transition={{ delay: index1 * portUtilsAnimationConfig.staggerDelay }}
|
transition={{ delay: index1 * portUtilsAnimationConfig.staggerDelay }}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
optionsItemsClick(index, index1, item, item1);
|
optionsItemsClick(index, index1, item1);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img src={item1.check ? item1.checkImg : item1.img} alt="" className="child_img" />
|
<img src={item1.check ? item1.checkImg : item1.img} alt="" className="child_img" />
|
||||||
|
|
@ -239,7 +308,7 @@ function BottomUtils(props) {
|
||||||
className={`item ${item1.check ? "active" : ""}`}
|
className={`item ${item1.check ? "active" : ""}`}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
optionsItemsClick(index, index1, item, item1);
|
optionsItemsClick(index, index1, item1);
|
||||||
}}
|
}}
|
||||||
variants={branchOfficeItemAnimation}
|
variants={branchOfficeItemAnimation}
|
||||||
>
|
>
|
||||||
|
|
@ -259,6 +328,18 @@ function BottomUtils(props) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="map_content_bottom_utils_container">
|
<div className="map_content_bottom_utils_container">
|
||||||
|
<AnimatePresence>
|
||||||
|
{isPeopleTrajectoryChecked && (
|
||||||
|
<motion.div
|
||||||
|
className="people_trajectory_select"
|
||||||
|
initial={{ y: 20, opacity: 0 }}
|
||||||
|
animate={{ y: 0, opacity: 1 }}
|
||||||
|
exit={{ y: 20, opacity: 0 }}
|
||||||
|
>
|
||||||
|
<PeopleTrajectorySelect peopleList={peopleList} />
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
<AnimatePresence mode="wait">
|
<AnimatePresence mode="wait">
|
||||||
{bottomUtilsMode && (
|
{bottomUtilsMode && (
|
||||||
<motion.div
|
<motion.div
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,117 @@
|
||||||
|
import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
||||||
|
import { Button, DatePicker, Form, message, Select, Spin } from "antd";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import { useContext, useEffect, useMemo, useState } from "react";
|
||||||
|
import { NS_BI_PEOPLE } from "~/enumerate/namespace";
|
||||||
|
import { Context } from "~/pages/Container/Map/js/context";
|
||||||
|
import "./index.less";
|
||||||
|
|
||||||
|
const MAX_TRAJECTORY_RANGE = 24 * 60 * 60 * 1000;
|
||||||
|
|
||||||
|
function PeopleTrajectorySelect(props) {
|
||||||
|
const { mapMethods } = useContext(Context);
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const [queryLoading, setQueryLoading] = useState(false);
|
||||||
|
|
||||||
|
const peopleOptions = useMemo(
|
||||||
|
() => props.peopleList.map(person => ({
|
||||||
|
value: person.terminalNo || person.staffNo,
|
||||||
|
label: person.staffName,
|
||||||
|
})),
|
||||||
|
[props.peopleList],
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => mapMethods.current.removePeopleTrajectory();
|
||||||
|
}, [mapMethods]);
|
||||||
|
|
||||||
|
const handleQuery = async () => {
|
||||||
|
const { personId, timeRange } = await form.validateFields();
|
||||||
|
const [startTime, endTime] = timeRange.map(time => dayjs(time).valueOf());
|
||||||
|
if (endTime - startTime > MAX_TRAJECTORY_RANGE) {
|
||||||
|
message.warning("时间范围不能超过24小时");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const person = props.peopleList.find(item => (item.terminalNo || item.staffNo) === personId);
|
||||||
|
const params = { startTime, endTime };
|
||||||
|
if (person?.terminalNo)
|
||||||
|
params.terminalNo = person.terminalNo;
|
||||||
|
else
|
||||||
|
params.staffNo = person?.staffNo;
|
||||||
|
|
||||||
|
setQueryLoading(true);
|
||||||
|
try {
|
||||||
|
const { data } = await props.getPeopleTrajectory(params);
|
||||||
|
const points = data?.points || [];
|
||||||
|
if (points.length < 2) {
|
||||||
|
mapMethods.current.removePeopleTrajectory();
|
||||||
|
message.warning("该时间范围内没有可绘制的轨迹");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mapMethods.current.addPeopleTrajectory(points);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
setQueryLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleReset = () => {
|
||||||
|
form.resetFields();
|
||||||
|
mapMethods.current.removePeopleTrajectory();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="people_trajectory_select__content">
|
||||||
|
<Spin spinning={queryLoading}>
|
||||||
|
<Form
|
||||||
|
form={form}
|
||||||
|
styles={{ label: { color: "#fff" } }}
|
||||||
|
className="people_trajectory_select__form"
|
||||||
|
>
|
||||||
|
<Form.Item
|
||||||
|
className="people_trajectory_select__person"
|
||||||
|
label="人员"
|
||||||
|
name="personId"
|
||||||
|
rules={[{ required: true, message: "请选择人员" }]}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
showSearch
|
||||||
|
optionFilterProp="label"
|
||||||
|
options={peopleOptions}
|
||||||
|
placeholder="请选择人员"
|
||||||
|
styles={{
|
||||||
|
root: { color: "#fff", backgroundColor: "rgba(2, 30, 81, 0.85)", borderColor: "rgba(2, 30, 81, 1)" },
|
||||||
|
placeholder: { color: "#fff" },
|
||||||
|
suffix: { color: "#fff" },
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
className="people_trajectory_select__time"
|
||||||
|
label="时间范围"
|
||||||
|
name="timeRange"
|
||||||
|
rules={[{ required: true, message: "请选择时间范围" }]}
|
||||||
|
>
|
||||||
|
<DatePicker.RangePicker
|
||||||
|
showTime
|
||||||
|
styles={{
|
||||||
|
root: {
|
||||||
|
color: "#fff",
|
||||||
|
backgroundColor: "rgba(2, 30, 81, 0.85)",
|
||||||
|
borderColor: "rgba(2, 30, 81, 1)",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<div className="people_trajectory_select__actions">
|
||||||
|
<Button type="primary" loading={queryLoading} onClick={handleQuery}>查询</Button>
|
||||||
|
<Button onClick={handleReset}>重置</Button>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
</Spin>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Connect([NS_BI_PEOPLE], true)(PeopleTrajectorySelect);
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
.people_trajectory_select {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 180px;
|
||||||
|
left: calc(50% - 500px);
|
||||||
|
z-index: 2;
|
||||||
|
width: 900px;
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
padding: 12px 16px;
|
||||||
|
color: #fff;
|
||||||
|
background: rgba(6, 24, 64, 0.94);
|
||||||
|
border: 1px solid rgba(70, 174, 255, 0.7);
|
||||||
|
box-shadow: 0 0 16px rgba(36, 136, 255, 0.35);
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{ant-prefix}-form-item {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__form {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
.@{ant-prefix}-select-selector,
|
||||||
|
.@{ant-prefix}-picker {
|
||||||
|
color: #fff;
|
||||||
|
background-color: rgba(2, 30, 81, 0.85);
|
||||||
|
border-color: rgba(2, 30, 81, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{ant-prefix}-picker-input > input::placeholder,
|
||||||
|
.@{ant-prefix}-picker-suffix,
|
||||||
|
.@{ant-prefix}-picker-separator {
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{ant-prefix}-picker {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__person {
|
||||||
|
width: 220px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__time {
|
||||||
|
width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -12,7 +12,6 @@ import CustomModal from "./components/Content/modal";
|
||||||
import Header from "./components/Header";
|
import Header from "./components/Header";
|
||||||
import RightUtils from "./components/RightUtils";
|
import RightUtils from "./components/RightUtils";
|
||||||
import { Context, initialMapState, MAP_ACTION, mapReducer } from "./js/context";
|
import { Context, initialMapState, MAP_ACTION, mapReducer } from "./js/context";
|
||||||
import useInitMap from "./js/initMap";
|
|
||||||
import mitt from "./js/mitt";
|
import mitt from "./js/mitt";
|
||||||
import {
|
import {
|
||||||
changeCoverMaskVisibleMittKey,
|
changeCoverMaskVisibleMittKey,
|
||||||
|
|
@ -21,6 +20,7 @@ import {
|
||||||
clickPortPointMittKey,
|
clickPortPointMittKey,
|
||||||
resetBottomUtilsMittKey,
|
resetBottomUtilsMittKey,
|
||||||
} from "./js/mittKey";
|
} from "./js/mittKey";
|
||||||
|
import useInitMap from "./js/useInitMap";
|
||||||
import "./index.less";
|
import "./index.less";
|
||||||
|
|
||||||
function Map(props) {
|
function Map(props) {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { useRef } from "react";
|
import { useRef } from "react";
|
||||||
import { createObliquePhotography } from "~/pages/Container/Map/js/mapUtils";
|
import { createObliquePhotography } from "~/pages/Container/Map/js/mapUtils";
|
||||||
import useMapMethods from "./mapMethods";
|
import useMapMethods from "./useMapMethods";
|
||||||
import usePointClickEvent from "./pointClickEvent";
|
import usePointClickEvent from "./usePointClickEvent";
|
||||||
|
|
||||||
const Cesium = window.Cesium;
|
const Cesium = window.Cesium;
|
||||||
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
import { useRef } from "react";
|
import { useRef } from "react";
|
||||||
import portEntityBillboardImage from "~/assets/images/map_bi/point/dianwei.png";
|
import portEntityBillboardImage from "~/assets/images/map_bi/point/dianwei.png";
|
||||||
import branchOfficeEntityBillboardImage from "~/assets/images/map_bi/point/gongsidianwei.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 edgeCMT from "./edge/cmt.js";
|
||||||
import edgeCSY from "./edge/csy.js";
|
import edgeCSY from "./edge/csy.js";
|
||||||
import edgeCZKS from "./edge/czks.js";
|
import edgeCZKS from "./edge/czks.js";
|
||||||
|
|
@ -16,8 +20,8 @@ import {
|
||||||
import mitt from "./mitt";
|
import mitt from "./mitt";
|
||||||
import { changeCoverMaskVisibleMittKey } from "./mittKey";
|
import { changeCoverMaskVisibleMittKey } from "./mittKey";
|
||||||
import { chunkedLoad, filterNull, formatPolygon } from "./utils";
|
import { chunkedLoad, filterNull, formatPolygon } from "./utils";
|
||||||
import "./TrajectoryPolylineTrailLinkMaterialProperty.js";
|
import "./material/TrajectoryPolylineTrailLinkMaterialProperty.js";
|
||||||
import "./WallPolylineTrailLinkMaterialProperty";
|
import "./material/WallPolylineTrailLinkMaterialProperty";
|
||||||
|
|
||||||
const portPoint = [
|
const portPoint = [
|
||||||
{
|
{
|
||||||
|
|
@ -53,6 +57,14 @@ const portPoint = [
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const peopleImg = {
|
||||||
|
blue: people_blue,
|
||||||
|
orange: people_orange,
|
||||||
|
red: people_red,
|
||||||
|
yellow: people_yellow,
|
||||||
|
}; // 人员定位颜色图片
|
||||||
|
const peopleTrajectoryEntityId = "peopleTrajectoryEntity";
|
||||||
|
|
||||||
const Cesium = window.Cesium;
|
const Cesium = window.Cesium;
|
||||||
|
|
||||||
export default function useMapMethods(viewerRef, request) {
|
export default function useMapMethods(viewerRef, request) {
|
||||||
|
|
@ -80,6 +92,9 @@ export default function useMapMethods(viewerRef, request) {
|
||||||
height: 900000,
|
height: 900000,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const peopleId = useRef([]); // 人员定位 ID 列表
|
||||||
|
const peoplePositionStartTime = useRef(null); // 当前人员定位会话共用的采样起始时间
|
||||||
|
|
||||||
// 设置中心点
|
// 设置中心点
|
||||||
const flyTo = ({ longitude, latitude, height } = defineCenterPoint) => {
|
const flyTo = ({ longitude, latitude, height } = defineCenterPoint) => {
|
||||||
getViewer().camera.flyTo({ destination: getPosition(longitude, latitude, height), duration: 2 });
|
getViewer().camera.flyTo({ destination: getPosition(longitude, latitude, height), duration: 2 });
|
||||||
|
|
@ -107,6 +122,7 @@ export default function useMapMethods(viewerRef, request) {
|
||||||
|
|
||||||
// 添加港口点
|
// 添加港口点
|
||||||
const addPortPoint = async () => {
|
const addPortPoint = async () => {
|
||||||
|
const viewer = getViewer();
|
||||||
await chunkedLoad(portPoint, 10, async (item) => {
|
await chunkedLoad(portPoint, 10, async (item) => {
|
||||||
const entityCollection = createEntityCollection("portEntityCollection");
|
const entityCollection = createEntityCollection("portEntityCollection");
|
||||||
entityCollection.entities.add(
|
entityCollection.entities.add(
|
||||||
|
|
@ -118,9 +134,9 @@ export default function useMapMethods(viewerRef, request) {
|
||||||
monitorItems: { data: { ...item, markType: "港口" } },
|
monitorItems: { data: { ...item, markType: "港口" } },
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
getViewer().dataSources.add(entityCollection);
|
viewer.dataSources.add(entityCollection);
|
||||||
});
|
});
|
||||||
await addMergedEntityCollection(getViewer(), "portEntityCollection");
|
await addMergedEntityCollection(viewer, "portEntityCollection");
|
||||||
};
|
};
|
||||||
|
|
||||||
// 移除港口点
|
// 移除港口点
|
||||||
|
|
@ -130,6 +146,7 @@ export default function useMapMethods(viewerRef, request) {
|
||||||
|
|
||||||
// 添加分公司点
|
// 添加分公司点
|
||||||
const addBranchOfficePoint = async (portArea = "", pointInfo = null) => {
|
const addBranchOfficePoint = async (portArea = "", pointInfo = null) => {
|
||||||
|
const viewer = getViewer();
|
||||||
let requestData = [];
|
let requestData = [];
|
||||||
if (!pointInfo) {
|
if (!pointInfo) {
|
||||||
const { data } = await request.getCorpInfoListAll({ eqPortArea: portArea, inType: [0, 1, 2, 6] });
|
const { data } = await request.getCorpInfoListAll({ eqPortArea: portArea, inType: [0, 1, 2, 6] });
|
||||||
|
|
@ -156,9 +173,9 @@ export default function useMapMethods(viewerRef, request) {
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
getViewer().dataSources.add(entityCollection);
|
viewer.dataSources.add(entityCollection);
|
||||||
});
|
});
|
||||||
await addMergedEntityCollection(getViewer(), "branchOfficeEntityCollection");
|
await addMergedEntityCollection(viewer, "branchOfficeEntityCollection");
|
||||||
mitt.emit(changeCoverMaskVisibleMittKey, false);
|
mitt.emit(changeCoverMaskVisibleMittKey, false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -240,6 +257,7 @@ export default function useMapMethods(viewerRef, request) {
|
||||||
};
|
};
|
||||||
if (!edgeMap[id])
|
if (!edgeMap[id])
|
||||||
return;
|
return;
|
||||||
|
const viewer = getViewer();
|
||||||
mitt.emit(changeCoverMaskVisibleMittKey, true);
|
mitt.emit(changeCoverMaskVisibleMittKey, true);
|
||||||
const currentEdge = edgeMap[id]();
|
const currentEdge = edgeMap[id]();
|
||||||
const entityCollection = createEntityCollection("wallEntityCollection");
|
const entityCollection = createEntityCollection("wallEntityCollection");
|
||||||
|
|
@ -262,7 +280,7 @@ export default function useMapMethods(viewerRef, request) {
|
||||||
id: createId(),
|
id: createId(),
|
||||||
wall: {
|
wall: {
|
||||||
positions,
|
positions,
|
||||||
material: new Cesium.WallPolylineTrailLinkMaterialProperty(getViewer()),
|
material: new Cesium.WallPolylineTrailLinkMaterialProperty(viewer),
|
||||||
maximumHeights: Array.from({ length: pointCount }).fill(40),
|
maximumHeights: Array.from({ length: pointCount }).fill(40),
|
||||||
minimumHeights: Array.from({ length: pointCount }).fill(0),
|
minimumHeights: Array.from({ length: pointCount }).fill(0),
|
||||||
},
|
},
|
||||||
|
|
@ -270,7 +288,7 @@ export default function useMapMethods(viewerRef, request) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
getViewer().dataSources.add(entityCollection);
|
viewer.dataSources.add(entityCollection);
|
||||||
mitt.emit(changeCoverMaskVisibleMittKey, false);
|
mitt.emit(changeCoverMaskVisibleMittKey, false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -359,6 +377,7 @@ export default function useMapMethods(viewerRef, request) {
|
||||||
throw new Error("请传入markType(扎点类型)");
|
throw new Error("请传入markType(扎点类型)");
|
||||||
if (!options.markIcon)
|
if (!options.markIcon)
|
||||||
throw new Error("请传入markIcon(扎点图标)");
|
throw new Error("请传入markIcon(扎点图标)");
|
||||||
|
const viewer = getViewer();
|
||||||
mitt.emit(changeCoverMaskVisibleMittKey, true);
|
mitt.emit(changeCoverMaskVisibleMittKey, true);
|
||||||
await chunkedLoad(filterNull(pointList), 10, async (item) => {
|
await chunkedLoad(filterNull(pointList), 10, async (item) => {
|
||||||
const entityCollection = createEntityCollection(
|
const entityCollection = createEntityCollection(
|
||||||
|
|
@ -376,10 +395,10 @@ export default function useMapMethods(viewerRef, request) {
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
getViewer().dataSources.add(entityCollection);
|
viewer.dataSources.add(entityCollection);
|
||||||
});
|
});
|
||||||
const dataSource = await addMergedEntityCollection(
|
const dataSource = await addMergedEntityCollection(
|
||||||
getViewer(),
|
viewer,
|
||||||
"markEntityCollection",
|
"markEntityCollection",
|
||||||
options.markType,
|
options.markType,
|
||||||
);
|
);
|
||||||
|
|
@ -392,6 +411,114 @@ export default function useMapMethods(viewerRef, request) {
|
||||||
removeEntityCollection(getViewer(), `markEntityCollectionMerged${markType ? `_${markType}` : ""}`);
|
removeEntityCollection(getViewer(), `markEntityCollectionMerged${markType ? `_${markType}` : ""}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 查询新轨迹前先移除旧轨迹,确保地图上始终只展示当前筛选结果。
|
||||||
|
const removePeopleTrajectory = () => {
|
||||||
|
getViewer().entities.removeById(peopleTrajectoryEntityId);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 将接口返回的经纬高点位绘制为动态轨迹线,少于两个点时不创建无意义的线段。
|
||||||
|
const addPeopleTrajectory = (points = []) => {
|
||||||
|
removePeopleTrajectory();
|
||||||
|
if (points.length < 2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const coordinates = points.flatMap(point => [point.lon, point.lat, point.alt || 0]);
|
||||||
|
getViewer().entities.add({
|
||||||
|
id: peopleTrajectoryEntityId,
|
||||||
|
polyline: {
|
||||||
|
positions: Cesium.Cartesian3.fromDegreesArrayHeights(coordinates),
|
||||||
|
width: 5,
|
||||||
|
material: new Cesium.TrajectoryPolylineTrailLinkMaterialProperty(getViewer()),
|
||||||
|
clampToGround: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 人员定位使用 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;
|
||||||
|
removePeopleTrajectory();
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
flyTo,
|
flyTo,
|
||||||
returnCurrentCenterPoint,
|
returnCurrentCenterPoint,
|
||||||
|
|
@ -407,5 +534,12 @@ export default function useMapMethods(viewerRef, request) {
|
||||||
removeWall,
|
removeWall,
|
||||||
addMarkPoint,
|
addMarkPoint,
|
||||||
removeMarkPoint,
|
removeMarkPoint,
|
||||||
|
addPeopleTrajectory,
|
||||||
|
removePeopleTrajectory,
|
||||||
|
initPeoplePositionClock,
|
||||||
|
addPeoplePoint,
|
||||||
|
updatePeoplePoint,
|
||||||
|
removePeoplePoint,
|
||||||
|
removePeoplePointAll,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
import { useWebSocket } from "ahooks";
|
||||||
|
import { useRef } from "react";
|
||||||
|
import edgeQHD from "./edge/qhd.js";
|
||||||
|
import { filterNull, isPointWithinTheArea } from "./utils";
|
||||||
|
|
||||||
|
export default function usePeoplePosition(mapMethods, currentBranchOffice, onPeopleChange) {
|
||||||
|
const markType = "peoplePosition";
|
||||||
|
const points = useRef([]);
|
||||||
|
|
||||||
|
const wsProtocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
||||||
|
const wsUrl = `${wsProtocol}//${process.env.app.API_HOST.replace(/^https?:\/\//, "")}/personnelPosition/ws/location?corpinfoId=${currentBranchOffice}`;
|
||||||
|
|
||||||
|
const onMessage = async (message) => {
|
||||||
|
const data = JSON.parse(message.data);
|
||||||
|
const peopleList = filterNull(Array.isArray(data) ? data : [data]);
|
||||||
|
for (let i = 0; i < peopleList.length; i++) {
|
||||||
|
const item = peopleList[i];
|
||||||
|
if (!item)
|
||||||
|
continue;
|
||||||
|
const index = points.current.findIndex(
|
||||||
|
item1 => item1.id.toString() === (item.terminalNo || item.staffNo).toString(),
|
||||||
|
);
|
||||||
|
const x = item.lon;
|
||||||
|
const y = item.lat;
|
||||||
|
const pointColor = isPointWithinTheArea(edgeQHD(), x, y);
|
||||||
|
if (index !== -1) {
|
||||||
|
points.current[index].x = x;
|
||||||
|
points.current[index].y = y;
|
||||||
|
points.current[index].icon_type = pointColor;
|
||||||
|
mapMethods.current && (await mapMethods.current.updatePeoplePoint(points.current[index]));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const perLoc = {
|
||||||
|
...item,
|
||||||
|
id: item.terminalNo || item.staffNo,
|
||||||
|
name: item.staffName,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
icon_type: pointColor,
|
||||||
|
};
|
||||||
|
points.current.push(perLoc);
|
||||||
|
mapMethods.current && (await mapMethods.current.addPeoplePoint(perLoc, { markType, isShowModal: false }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 将已去重的实时人员列表同步给轨迹筛选面板,避免面板直接依赖 WebSocket。
|
||||||
|
onPeopleChange?.([...points.current]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const { connect, disconnect } = useWebSocket(
|
||||||
|
wsUrl,
|
||||||
|
{
|
||||||
|
manual: true,
|
||||||
|
onClose: () => {
|
||||||
|
mapMethods.current && mapMethods.current.removePeoplePointAll();
|
||||||
|
points.current = [];
|
||||||
|
onPeopleChange?.([]);
|
||||||
|
},
|
||||||
|
onMessage: (message) => {
|
||||||
|
onMessage(message);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return { connect, disconnect };
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// 过滤掉无经纬度的数据
|
// 过滤掉无经纬度的数据
|
||||||
export const filterNull = (arr = []) => {
|
export const filterNull = (arr = []) => {
|
||||||
return arr.filter(item => (item.longitude || item.lng) && (item.latitude || item.lat));
|
return arr.filter(item => (item.longitude || item.lng || item.lon) && (item.latitude || item.lat));
|
||||||
};
|
};
|
||||||
|
|
||||||
// 格式化多边形
|
// 格式化多边形
|
||||||
|
|
@ -245,3 +245,50 @@ export function textFormatter(value, maxLength = 4) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isPointInPolygon = (polygon, lng, lat) => {
|
||||||
|
const numberOfPoints = polygon.length;
|
||||||
|
const polygonLats = [];
|
||||||
|
const polygonLngs = [];
|
||||||
|
for (let i = 0; i < numberOfPoints; i++) {
|
||||||
|
polygonLats.push(polygon[i].y);
|
||||||
|
polygonLngs.push(polygon[i].x);
|
||||||
|
}
|
||||||
|
let polygonContainsPoint = false;
|
||||||
|
for (
|
||||||
|
let node = 0, altNode = numberOfPoints - 1;
|
||||||
|
node < numberOfPoints;
|
||||||
|
altNode = node++
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
(polygonLngs[node] > lng) !== (polygonLngs[altNode] > lng)
|
||||||
|
&& lat
|
||||||
|
< ((polygonLats[altNode] - polygonLats[node])
|
||||||
|
* (lng - polygonLngs[node]))
|
||||||
|
/ (polygonLngs[altNode] - polygonLngs[node])
|
||||||
|
+ polygonLats[node]
|
||||||
|
) {
|
||||||
|
polygonContainsPoint = !polygonContainsPoint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return polygonContainsPoint;
|
||||||
|
};
|
||||||
|
const forEachIsPointInPolygon = (polygonList = [], lng, lat) => {
|
||||||
|
return polygonList.some((item) => {
|
||||||
|
return isPointInPolygon(item.position, lng, lat);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const isPointWithinTheArea = (pointBox = {}, x, y) => {
|
||||||
|
if (forEachIsPointInPolygon(pointBox.redList, x, y)) {
|
||||||
|
return "red";
|
||||||
|
}
|
||||||
|
else if (forEachIsPointInPolygon(pointBox.orangeList, x, y)) {
|
||||||
|
return "orange";
|
||||||
|
}
|
||||||
|
else if (forEachIsPointInPolygon(pointBox.yellowList, x, y)) {
|
||||||
|
return "yellow";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return "blue";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue