From 648f5f4d4907ad1ca1aa6a9c97bb5c252c310754 Mon Sep 17 00:00:00 2001 From: tangshaojian <63377964@qq.com> Date: Thu, 24 Apr 2025 22:52:12 +0800 Subject: [PATCH] =?UTF-8?q?tsj:=20CooglMap=20js=E6=9B=B4=E6=96=B0=E5=88=B0?= =?UTF-8?q?1.4.0=E7=89=88=E6=9C=AC=EF=BC=9B=E5=AE=9E=E7=8E=B0=E8=A1=8C?= =?UTF-8?q?=E6=94=BF=E5=8C=BA=E5=88=92=E6=8C=89=E9=92=AE=E7=BB=84=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E4=BB=8E=20regionTreeResult.json=E9=80=9A=E8=BF=87?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E6=9D=A5=E8=8E=B7=E5=8F=96=E8=80=8C=E4=B8=8D?= =?UTF-8?q?=E6=98=AF=E9=9D=99=E6=80=81=EF=BC=9B=E5=AE=9E=E7=8E=B0=E8=A1=8C?= =?UTF-8?q?=E6=94=BF=E5=8C=BA=E5=88=92=E6=8C=89=E9=92=AE=E7=82=B9=E5=87=BB?= =?UTF-8?q?=E4=B8=8E=E5=9C=B0=E5=9B=BE=E7=9A=84=E8=81=94=E5=8A=A8=EF=BC=9B?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E5=9C=B0=E5=9B=BE=E7=82=B9=E5=87=BB=E4=BA=8B?= =?UTF-8?q?=E4=BB=B6=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MapContainer/AreaSelector/index.tsx | 221 +++++--- .../DistrictMapCustomized/index.tsx | 67 ++- .../DistrictMapCustomized/loadArea-1.4.0.js | 527 ++++++++++++++++++ .../components/MapContainer/index.tsx | 24 +- 4 files changed, 748 insertions(+), 91 deletions(-) create mode 100644 src/pages/Home_v_2504/components/MapContainer/DistrictMapCustomized/loadArea-1.4.0.js diff --git a/src/pages/Home_v_2504/components/MapContainer/AreaSelector/index.tsx b/src/pages/Home_v_2504/components/MapContainer/AreaSelector/index.tsx index 6500fc36..de56f441 100644 --- a/src/pages/Home_v_2504/components/MapContainer/AreaSelector/index.tsx +++ b/src/pages/Home_v_2504/components/MapContainer/AreaSelector/index.tsx @@ -1,91 +1,147 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import classNames from 'classnames'; import styles from './index.less'; import { useGlobalModalServices } from '@/pages/GlobalModalServices/provider'; +// 导入区域树数据 +import regionTreeResult from '../DistrictMapCustomized/regionTreeResult.json'; + +// 按钮数据类型定义 +interface ButtonItem { + key: string; + label: string; + hasChildren?: boolean; + regionCode?: string; + dataCode?: string; +} // 定义按钮组数据类型 interface ButtonGroupData { - level1: Array<{ - key: string; - label: string; - hasChildren?: boolean; - }>; - level2: Record>; - level3: Record>; + level1: ButtonItem[]; + level2: Record; + level3: Record; } -const AreaSelector: React.FC = () => { +interface AreaSelectorProps { + mapInstance?: any; // CooglLoadArea instance reference +} + +const AreaSelector: React.FC = ({ mapInstance }) => { // 获取全局弹窗服务 const { dispatch } = useGlobalModalServices(); - // 按钮组数据 - const buttonGroupData: ButtonGroupData = { + // 初始化按钮组数据的状态 + const [buttonGroupData, setButtonGroupData] = useState({ level1: [ - { key: 'chengdu', label: '成都市', hasChildren: true }, - { key: 'oilSmoke', label: '油烟事件' }, - { key: 'oilSmokeAlert', label: '油烟告警' }, - { key: 'restaurant', label: '餐饮企业' }, + { key: 'chengdu', label: '成都市', hasChildren: true, regionCode: '5101' }, + { key: 'oilSmoke', label: '油烟事件', dataCode: '20101' }, + { key: 'oilSmokeAlert', label: '油烟告警', dataCode: '20102' }, + { key: 'restaurant', label: '餐饮企业', dataCode: '20103' }, ], - level2: { - chengdu: [ - { key: 'gaoxin', label: '高新区' }, - { key: 'jinjiang', label: '锦江区', hasChildren: true }, - { key: 'chenghua', label: '成华区' }, - { key: 'qingyang', label: '青羊区', hasChildren: true }, - { key: 'jinniu', label: '金牛区' }, - { key: 'xindou', label: '新都区' }, - { key: 'wuhou', label: '武侯区', hasChildren: true }, - { key: 'longquanyi', label: '龙泉驿区' }, - { key: 'wenjiang', label: '温江区' }, - { key: 'pengzhou', label: '彭州市' }, - { key: 'xinjin', label: '新津区' }, - { key: 'dayi', label: '大邑县' }, - { key: 'jianyang', label: '简阳市' }, - ] - }, - level3: { - jinjiang: [ - { key: 'qingshuitang', label: '清水塘街道' }, - { key: 'sanlitun', label: '三里口街道' }, - { key: 'dafeng', label: '大丰街道' }, - { key: 'xindu', label: '新都街道' }, - { key: 'jinxing', label: '锦兴街道' }, - { key: 'zhujiang', label: '珠江街道' }, - ], - qingyang: [ - { key: 'sanhe', label: '三河街道' }, - { key: 'shibanqiao', label: '石板桥街道' }, - { key: 'zhupanzhen', label: '竹盘珍街道' }, - { key: 'xindu2', label: '新都街道' }, - ], - wuhou: [ - { key: 'wuhou1', label: '桂湖街道' }, - { key: 'wuhou2', label: '大丰街道' }, - { key: 'wuhou3', label: '三河街道' }, - { key: 'wuhou4', label: '石板桥街道' }, - { key: 'wuhou5', label: '竹盘珍街道' }, - { key: 'wuhou6', label: '新都街道' }, - ] - } - }; + level2: {}, + level3: {}, + }); // 状态管理 const [activeLevel1, setActiveLevel1] = useState(null); const [activeLevel2, setActiveLevel2] = useState(null); const [activeLevel3, setActiveLevel3] = useState(null); + // 控制第二层、第三层按钮组的显示 const showLevel2 = activeLevel1 && buttonGroupData.level2[activeLevel1]; const showLevel3 = activeLevel2 && buttonGroupData.level3[activeLevel2]; + // 初始化时从 JSON 加载区域树数据 + useEffect(() => { + if (!regionTreeResult?.data) return; + + try { + // 解析和处理区域树数据 + const processedData = processRegionTree(regionTreeResult.data); + + // 使用解析后的数据更新按钮组 + setButtonGroupData(prevData => ({ + ...prevData, + level2: { + chengdu: processedData.level2 + }, + level3: processedData.level3 + })); + } catch (error) { + console.error('处理区域树数据出错:', error); + } + }, []); + + // 处理区域树数据的函数 + const processRegionTree = (treeData: any[]) => { + const level2Data: ButtonItem[] = []; + const level3Data: Record = {}; + + // 查找成都市节点 + const chengduNode = treeData.find(node => node.id === 5101); + + if (chengduNode && chengduNode.children) { + // 处理区级 (level2) + chengduNode.children.forEach((district: any) => { + // 判断是否有子节点(街道) + const hasChildren = district.children && district.children.length > 0; + + level2Data.push({ + key: `district_${district.id}`, + label: district.gridName, + hasChildren, + regionCode: district.id.toString(), + }); + + // 如果有子节点,处理街道 (level3) + if (hasChildren) { + const streetData: ButtonItem[] = []; + + district.children.forEach((street: any) => { + streetData.push({ + key: `street_${street.id}`, + label: street.gridName, + regionCode: street.id.toString() + }); + }); + + level3Data[`district_${district.id}`] = streetData; + } + }); + } + + return { + level2: level2Data, + level3: level3Data + }; + }; + + // 更新地图区域和数据 + const updateMapArea = (regionCode: string, dataCode?: string) => { + if (!mapInstance) return; + + // 更新地图实例的参数 + if (regionCode) { + mapInstance.params.regionCode = regionCode; + } + + if (dataCode) { + mapInstance.params.dataCode = dataCode; + } + + // 判断是否为社区级别,设置不同的加载方法 + const isCommunityLevel = regionCode?.length === 9; + + if (isCommunityLevel) { + // 社区级别,加载点位数据 + mapInstance.setPoints(true); + } else { + // 区/街道级别,加载区域数据 + mapInstance.addArea(true); + } + }; + // 处理第一层按钮点击 - const handleLevel1Click = (key: string, hasChildren?: boolean) => { + const handleLevel1Click = (key: string, hasChildren?: boolean, regionCode?: string, dataCode?: string) => { if (key === 'oilSmokeAlert') { // 点击油烟告警按钮,显示弹窗 dispatch.push('RestaurantOilAlarm'); @@ -107,10 +163,15 @@ const AreaSelector: React.FC = () => { setActiveLevel1(key); setActiveLevel2(null); } + + // 更新地图区域展示 + if (regionCode) { + updateMapArea(regionCode, dataCode); + } }; // 处理第二层按钮点击 - const handleLevel2Click = (key: string, hasChildren?: boolean) => { + const handleLevel2Click = (key: string, hasChildren?: boolean, regionCode?: string) => { if (activeLevel2 === key) { // 如果点击当前已激活的按钮,则取消选中 setActiveLevel2(null); @@ -118,12 +179,31 @@ const AreaSelector: React.FC = () => { // 否则激活该按钮 setActiveLevel2(key); } + + // 更新地图区域展示 + if (regionCode) { + updateMapArea(regionCode); + } }; - const handleLevel3Click = (key: string) => { + // 处理第三层按钮点击 + const handleLevel3Click = (key: string, regionCode?: string) => { setActiveLevel3(key); + + // 更新地图区域展示 + if (regionCode) { + updateMapArea(regionCode); + } }; + // 默认展示成都市全貌 + useEffect(() => { + if (mapInstance) { + // 设置初始化参数为成都市 + updateMapArea('5101'); + } + }, [mapInstance]); + return (
{/* 第一层按钮组 */} @@ -134,7 +214,7 @@ const AreaSelector: React.FC = () => { className={classNames(styles.button, { [styles.buttonActive]: activeLevel1 === button.key })} - onClick={() => handleLevel1Click(button.key, button.hasChildren)} + onClick={() => handleLevel1Click(button.key, button.hasChildren, button.regionCode, button.dataCode)} > {button.label} {activeLevel1 === button.key && button.hasChildren && ( @@ -157,7 +237,7 @@ const AreaSelector: React.FC = () => { className={classNames(styles.button, { [styles.buttonActive]: activeLevel2 === button.key })} - onClick={() => handleLevel2Click(button.key, button.hasChildren)} + onClick={() => handleLevel2Click(button.key, button.hasChildren, button.regionCode)} > {button.label}
@@ -171,11 +251,10 @@ const AreaSelector: React.FC = () => { {buttonGroupData.level3[activeLevel2!].map((button) => (
handleLevel3Click(button.key)} + onClick={() => handleLevel3Click(button.key, button.regionCode)} > {button.label}
diff --git a/src/pages/Home_v_2504/components/MapContainer/DistrictMapCustomized/index.tsx b/src/pages/Home_v_2504/components/MapContainer/DistrictMapCustomized/index.tsx index 8e27d77e..163feec8 100644 --- a/src/pages/Home_v_2504/components/MapContainer/DistrictMapCustomized/index.tsx +++ b/src/pages/Home_v_2504/components/MapContainer/DistrictMapCustomized/index.tsx @@ -28,7 +28,9 @@ import styles from './index.less'; import PopoverCard from './PopoverCard'; import { useDashboard } from '../../context'; // import { CooglLoadArea } from './loadAreaNew'; -import { CooglLoadArea } from './loadArea-1.3.1'; +// import { CooglLoadArea } from './loadArea-1.3.1'; +import { CooglLoadArea } from './loadArea-1.4.0'; +import AreaSelector from '../AreaSelector'; export const colorConfig: Record< string, @@ -73,8 +75,14 @@ export const colorConfig: Record< tag: require('@/assets/images/SecurityServiceOverview/dtyjtag4.png'), }, }; + +// 添加组件属性接口 +interface DistrictMapCustomizedProps { + onMapInstanceChange?: (instance: any) => void; +} + /** 地图 */ -const DistrictMapCustomized: React.FC = () => { +const DistrictMapCustomized: React.FC = ({ onMapInstanceChange }) => { const { initialState } = useModel('@@initialState'); const { areaId } = initialState; const { dispatch } = useGlobalModalServices(); @@ -128,6 +136,16 @@ const DistrictMapCustomized: React.FC = () => { currentZoomRef.current = currentZoom; }, [currentZoom]); + // Add state to track the CooglLoadArea instance + const [areaMapInstance, setAreaMapInstance] = useState(null); + + // 在地图实例状态变化时,调用父组件传递的回调 + useEffect(() => { + if (areaMapInstance && onMapInstanceChange) { + onMapInstanceChange(areaMapInstance); + } + }, [areaMapInstance, onMapInstanceChange]); + const fetchVisiblePolygons = (bounds: any) => { // console.log('fetchVisiblePolygons bounds', bounds); // Only fetch if we don't have data yet @@ -398,25 +416,40 @@ const DistrictMapCustomized: React.FC = () => { console.log('[CooglMapDemo] 开始初始化CooglLoadArea', MapRef.current); let instance = new CooglLoadArea(viewer); - // 方便调试 - // window.mapInstance = mapRef.current; - // window.gridInstance = instanceRef.current; - - // console.log('[CooglMapDemo] 开始执行mapInit'); - instance.params ={ - regionCode: '5101', - gridType:'01', - startTime:'', - endTime:'', - dataCode:'20101', + // 设置实例参数 + instance.params = { + regionCode: '5101', // 默认显示成都市 + gridType: '01', // 城市部件 + startTime: '', + endTime: '', + dataCode: '20101', // 默认数据代码 hasChildren: 1, + }; + + // 保存实例到state,以便传递给父组件 + setAreaMapInstance(instance); + + // 使用类型断言解决 TypeScript 错误 + // @ts-ignore + instance.imageGraphicOfCommunityEvent = function(detail: any) { + // 处理点位点击事件 + console.log('点位详情:', detail); + + // 可以触发弹窗显示详情 + if (detail) { + dispatch.push('EventDetail', { + title: isHomePage ? '事件详情' : '餐饮油烟事件详情', + w: 'fit-content', + props: { + eventId: detail.eventId || detail.id, + }, + }); } - instance.mapInit(); + }; - // setStatus('网格基座初始化完成'); + instance.mapInit(); } catch (error) { console.error('[CooglMapDemo] 初始化网格基座失败:', error); - // setStatus(`初始化失败: ${error}`); } } @@ -503,6 +536,7 @@ const DistrictMapCustomized: React.FC = () => { {/* */} + {/* 状态栏 - 添加区域选择器并传递地图实例 */} {/*
{ setLeftTabKey={setLeftTabKey} setCheckedKeys={setCheckedKeys} /> +
*/}
diff --git a/src/pages/Home_v_2504/components/MapContainer/DistrictMapCustomized/loadArea-1.4.0.js b/src/pages/Home_v_2504/components/MapContainer/DistrictMapCustomized/loadArea-1.4.0.js new file mode 100644 index 00000000..7cebba68 --- /dev/null +++ b/src/pages/Home_v_2504/components/MapContainer/DistrictMapCustomized/loadArea-1.4.0.js @@ -0,0 +1,527 @@ +//这个是基于ceisum的二开,所以有些api不大相同 +// 区域划分的颜色配置 +import regionResult from './regionTreeResult.json' + +const CooGL = window.CooGL; + +// 调试开关 +const DEBUG = false; + +const IS_PROD = process.env.NODE_ENV === 'production' ? true : false; + +// API基础URL配置 +const API_BASE_URLS = { + // 直接地址 + DIRECT: { + REGION_API: 'https://10.1.174.34:13600/kelan/api', + GRID_RANGE_API: 'https://10.1.174.34:13600/grid-range' + }, + // 相对地址(代理) + PROXY: { + REGION_API: IS_PROD?'/cd-comprehensive/region/coogl/api':'/region/coogl/api', + GRID_RANGE_API: IS_PROD?'/cd-comprehensive/grid-range':'/grid-range' + } +}; + +// 是否使用直接地址(true: 使用直接地址, false: 使用代理地址) +const USE_DIRECT_URL = false; + +// 获取当前使用的API基础URL +const getApiBaseUrl = (type) => { + return USE_DIRECT_URL ? API_BASE_URLS.DIRECT[type] : API_BASE_URLS.PROXY[type]; +}; + +// 调试日志函数 +const logInfo = (...args) => { + if (DEBUG) { + console.log(...args); + } +}; +const logError = (...args) => { + if (DEBUG) { + console.error(...args); + } +}; + +export class CooglLoadArea { + params = { + regionCode: '', + gridType: '', + startTime: '', + endTime: '', + dataCode: '', + hasChildren: 1, + } + degreesArray = [] + levelOfMeter = { + 16: 20, + 14: 40, + 13: 75, + 12: 150, + // 11: 400, + } + level + opLevel + graphic + rightPickHandler + compressCancelFn + tinGraphic = [] + imageGraphicListOfCommunity = [] + imageGraphicList = [] + polygonGraphicList = [] + pickHandler = null + + //部件、事件、资源等拾取事件,返回拾取的详情 + imageGraphicOfCommunityEvent = null + + //设置在哪一级别加载当前区域的部件、事件、资源等数据 值为 6(区级) 9(街道) 12(社区、村),默认为12 + loadPointsLevel = 12 + constructor(viewer, token) { + this.viewer = viewer + this.regionResult = regionResult.data + this.token = token ?? CooGL.CooServer.defaultAccessToken + } + + async mapInit() { + if (this.pickHandler) { + this.pickHandler() + this.pickHandler = null + } + + //清除画布 + this.clear() + this.limitCamHeight() + //默认加载一次市级的标注上图 + this.pickHandler = this.viewer.event.pick(e => { + const { isCommunityImage, code: regionCode } = + e?.primitive?.description || {} + if (regionCode) { + this.params.regionCode = regionCode + //在这里判断 如果是社区、村的级别执行 setPoints + //示例 判断条件根据实际情况而定 + //4 6 9 12 + const isSameLevel = regionCode?.length === this.loadPointsLevel + if (isSameLevel) { + this.setPoints(true) + } else { + this.addArea(true) + } + } else if (isCommunityImage && this.imageGraphicOfCommunityEvent) { + this.imageGraphicOfCommunityEvent(e?.primitive?.description.detail) + } + }) + //增加右键功能,后退 + this.rightPickHandler = this.viewer.event.rightclick(e => { + if (this.params.regionCode == 5101) return + // 根据code查找父节点 + const parentCode = this.findParentCode(this.params.regionCode) + this.params.regionCode = parentCode + this.addArea(true) + }) + await this.addArea(true) + this.compressFn() + } + findParentCode(code) { + if (!code) return null + + // 递归查找函数 + const findParent = (nodes, targetCode, parent = null) => { + if (!nodes || !Array.isArray(nodes)) return null + + for (const node of nodes) { + // 找到目标节点,返回父节点id + if (node.id == targetCode) { + return parent?.id || null + } + + // 递归查找子节点 + if (node.children) { + const found = findParent(node.children, targetCode, node) + if (found !== null) return found + } + } + return null + } + + return findParent(this.regionResult, code) + } + /** + * 添加区域 + * @param {string} regionCode - 区域编码 + * @param {string} gridtype - 网格类型 + * 01:城市部件 + * 02:事件 + * 03:资源 + * 04:单元网格 + * 05:责任网格 + * 06:任意网格 + * 07:资源网格 + * 08:感知设备 + * @param {string} startTime - 开始时间 格式:yyyy-MM-DD HH:mm:ss 可选 + * @param {string} endTime - 结束时间 格式:yyyy-MM-DD HH:mm:ss 可选 + */ + + async addArea(clearData = false) { + const params = { ...this.params } + if (clearData) { + this.clear() + } else { + this.clearCurrentAreaData() + } + // const list = this.findRegionByCode(params.regionCode, true) + const resList = await Promise.all([ + this.getRigionDetailByCode(params.regionCode), + this.getAllRigionDetailByCode(params.regionCode), + ]) + this.moveViewToPolygon(resList[0]) + const sons = resList[1].features + for (const res of sons) { + this.addPolygon(res) + } + + const apiUrl = `${getApiBaseUrl('REGION_API')}/es/v1.0/bigdata_search_analysis`; + fetch(apiUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + token: this.token, + }, + body: JSON.stringify(params), + }) + .then(res => res.json()) + .then(res => { + const obj = res.data + delete obj[params.regionCode] + Object.entries(obj).map(([key, value]) => { + const detailItem = sons.find(item => item.id === key) + if (!detailItem) return + const { _center, _name } = detailItem.properties + const center = _center.split(',') + const position = { + x: +center[0], + y: +center[1], + z: 0, + } + const item = { + num: value[0].num, + code: key, + position, + name: _name, + } + this.addImage(item) + }) + }) + } + + //获取指定regionCode的区域的经纬度以及中心点和下级区域的所有经纬度 + //FIXME: 这个接口需要自己实现 + async getRigionDetailByCode(regionCode) { + if (!regionCode) return Promise.reject('regionCode is required') + //参考已有代码 + // 获取geojson格式的区域边界数据 + // 获取txt格式的区域边界数据 + const apiUrl = `${getApiBaseUrl('GRID_RANGE_API')}/unit/${regionCode}`; + const response = await fetch(apiUrl) + return response.json() + } + async getAllRigionDetailByCode(regionCode) { + if (!regionCode) return Promise.reject('regionCode is required') + //参考已有代码 + // 获取geojson格式的区域边界数据 + // 获取txt格式的区域边界数据 + const apiUrl = `${getApiBaseUrl('GRID_RANGE_API')}/full/unit/${regionCode}`; + const response = await fetch(apiUrl) + return response.json() + } + + addImage({ num, code, position, name }) { + this.viewer + .addLayer( + CooGL.Layer.fromImageTextLabel({ + id: code, + description: { + code, + name, + }, + image: + 'data:image/png;base64,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', + position, + scale: 0.5, + textContent: num, + font: '16px simsum', + pixelOffset: { x: 10, y: -100 }, + fillColor: '#fff', + style: CooGL.LabelStyle.FILL_AND_OUTLINE, + outlineWidth: 4, + outlineColor: '#000', + distanceDisplayCondition: new CooGL.DistanceDisplayCondition( + 0, + 100000, + ), + }), + ) + .then(res => { + this.imageGraphicList.push(res) + }) + } + addCommunityImage(obj, position) { + this.viewer + .addLayer( + CooGL.Layer.fromImageTextLabel({ + id: 'community' + '_' + obj.id, + description: { + isCommunityImage: true, + detail: obj, + }, + image: + 'data:image/png;base64,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', + position, + textContent: obj.name, + font: '22px simsum', + pixelOffset: { x: 0, y: -150 }, + fillColor: '#fff', + style: CooGL.LabelStyle.FILL_AND_OUTLINE, + outlineWidth: 4, + distanceDisplayCondition: new CooGL.DistanceDisplayCondition( + 0, + 10000, + ), + outlineColor: '#000', + }), + ) + .then(res => { + this.imageGraphicListOfCommunity.push(res) + }) + } + addPolygon(polygonData) { + const id = 'polygon' + '_' + polygonData.id + this.viewer + .addLayer( + CooGL.Layer.fromGeoJsonGraphicResource({ + id, + resource: { + type: 'FeatureCollection', + features: [polygonData], + }, + customPolygonLabelOptions: function (positions, properties) { + const center = properties._center.split(',') + const textPosition = { x: center[0], y: center[1], z: 0 } + return { + positions, + material: '#0B304B', + textPosition, + outline: true, + outlineColor: '#36A6F5', + outlineWidth: 2, + fillColor: '#fff', + font: '12px simsum', + textContent: properties._name, + } + }, + }), + ) + .then(res => { + this.polygonGraphicList.push(res) + }) + } + + //获取村、社区下的特定网格类型点位并上图 + async setPoints(clearData = false) { + this.degreesArray = [] + if (clearData) { + this.clear() + } + const res = await this.getRigionDetailByCode(this.params.regionCode) + this.moveViewToPolygon(res) + const coordinates = res.features[0].geometry.coordinates[0] + .map(([x, y]) => `${x},${y}`) + .join(';') + // 构造请求参数 + const params = { + regionCode: this.params.regionCode, + dataCode: this.params.dataCode, + gridtype: this.params.gridType, + startTime: this.params.startTime, + endTime: this.params.endTime, + coordinates, + } + // 发送POST请求获取点位数据 + const apiUrl = `${getApiBaseUrl('REGION_API')}/es/v1.0/search`; + const response = await fetch(apiUrl, { + // const response = await fetch('/region/coogl/api/es/v1.0/search', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + token: this.token, + }, + body: JSON.stringify(params), + }) + // 解析响应数据 + const res1 = await response.json() + const { component, event, resource } = res1.data + if (component.length > 100) { + } + for (const v of component) { + const position = { x: v.longitude, y: v.latitude, z: 0 } + this.degreesArray.push( + new CooGL.Vector3(position.x, position.y, position.z), + ) + this.addCommunityImage(v, position) + } + } + //计算多边形的包围盒,并将视角移动到包围盒的中心 + moveViewToPolygon(res) { + const positions = res.features[0].geometry.coordinates.flat(Infinity) + // 将扁平化的坐标数组重新组装成对象数组 + const positionObjects = [] + for (let i = 0; i < positions.length; i += 2) { + positionObjects.push({ + x: positions[i], + y: positions[i + 1], + z: 0, + }) + } + // 添加多边形图层 + const bs = CooGL.BoundingSphere.fromPoints( + CooGL.Vector3.degreesArray2cartesian(positionObjects), + ) + // 定位到包围盒中心 + this.viewer.camera.flyToBoundingSphere( + CooGL.Vector3.cartesian2degrees(bs.center), // 包围盒中心 + bs.radius, // 包围盒半径 + { + duration: 1.5, + offset: new CooGL.HeadingPitchRange(0, -Math.PI / 2, 0), + }, + ) + } + + /** + * 根据区域编码查找对应的区域信息及其子区域 + * @param {string} code - 区域编码(4/6/9/12位) + * @returns {object|null} 返回找到的区域信息对象,未找到返回null + */ + findRegionByCode(code, findSon = false) { + if (!code) return null + // 递归查找函数 + const findInTree = nodes => { + if (!nodes || !Array.isArray(nodes)) return null + + for (const node of nodes) { + // 匹配当前节点 + if (node.id == code) { + if (findSon) + return node.children.map(item => ({ ...item, children: null })) + return node + } + + // 递归查找子节点 + if (node.children) { + const found = findInTree(node.children) + if (found) return found + } + } + return null + } + return findInTree(this.regionResult) + } + + flyToArea([x, y], code) { + const z = this.levelHeight[code.length] + this.viewer.camera.flyTo({ + duration: 1.5, + destination: { + x, + y, + z, + }, + orientation: { + heading: 6.283185302776594, + pitch: -1.5707963267948966, + roll: 0, + }, + }) + } + compressFn() { + this.compressCancelFn = this.viewer.renderEvent.addEventListener(() => { + if (!this.degreesArray.length) return + const curlevel = + this.viewer.scene.globe._surface._debug.maxDepthVisited - 2 + if (this.level !== curlevel) { + this.level = curlevel + + if (curlevel > 16) { + if (this.opLevel < 17) { + this.opLevel = curlevel + for (const graphic of this.imageGraphicListOfCommunity) { + graphic.show = true + } + } + return + } + + const diff = this.levelOfMeter[this.level] + if (!diff) { + return + } + this.opLevel = this.level + const result = CooGL.PointsCompress.compute(this.degreesArray, diff) + const values = Object.values(result) + for (const graphic of this.imageGraphicListOfCommunity) { + const position = graphic._graphic.imageTextLabel.position + graphic.show = values.some( + ({ x, y }) => position.x === x && position.y === y, + ) + } + } + }) + } + //清除标注 + clearCommunity() { + for (const item of this.imageGraphicList) { + this.viewer.removeLayer(item) + } + this.imageGraphicList = [] + } + //清除画布 + clear() { + for (const item of this.imageGraphicList) { + this.viewer.removeLayer(item) + } + for (const item of this.polygonGraphicList) { + this.viewer.removeLayer(item) + } + for (const item of this.imageGraphicListOfCommunity) { + this.viewer.removeLayer(item) + } + this.imageGraphicListOfCommunity = [] + this.imageGraphicList = [] + this.polygonGraphicList = [] + this.degreesArray = [] + } + + clearCurrentAreaData() { + const id = 'layer-' + this.params.regionCode + const layer = this.viewer.getById(id) + const index = this.imageGraphicList.indexOf(layer) + if (layer) this.viewer.removeLayer(layer) + if (index > -1) this.imageGraphicList.splice(index, 1) + } + + limitCamHeight() { + this.viewer.scene.screenSpaceCameraController.maximumZoomDistance = 300000 + } + unlimitCamHeight() { + this.viewer.scene.screenSpaceCameraController.maximumZoomDistance = Infinity + } + destory() { + this.compressCancelFn?.() + this.rightPickHandler?.() + + this.clear() + this.clearCommunity() + this.pickHandler() + this.pickHandler = null + this.unlimitCamHeight() + } +} diff --git a/src/pages/Home_v_2504/components/MapContainer/index.tsx b/src/pages/Home_v_2504/components/MapContainer/index.tsx index ab59df3c..03c21a7a 100644 --- a/src/pages/Home_v_2504/components/MapContainer/index.tsx +++ b/src/pages/Home_v_2504/components/MapContainer/index.tsx @@ -10,7 +10,13 @@ import styles from './index.less'; import DistrictMapCustomized from './DistrictMapCustomized'; // import CooglMapDemo from './CooglMapDemo'; -const Map: Record = { +// 修改地图组件的属性类型 +interface MapComponentProps { + onMapInstanceChange?: (instance: any) => void; +} + +// 扩展地图组件类型,添加属性 +const Map: Record> = { // '1': CooglMapDemo, '1': DistrictMapCustomized, // '1': DistrictMap, @@ -37,10 +43,20 @@ const MapContainer = () => { const [topKey, setTopKey] = useState('1'); const { activePage } = useDashboard(); const isHomePage = activePage === 'home'; + + // 添加状态保存地图实例 + const [mapInstance, setMapInstance] = useState(null); + + // 处理地图实例变化 + const handleMapInstanceChange = (instance: any) => { + console.log('Map instance updated:', instance); + setMapInstance(instance); + }; const renderMap = useMemo(() => { const MapComponent = Map[topKey]; - return ; + // 传递 onMapInstanceChange 回调给地图组件 + return ; }, [topKey]); return ( @@ -61,8 +77,8 @@ const MapContainer = () => { {/* 地图上的最新事件和区域选择 */} - {/* 只在下钻页面显示区域选择按钮组 */} - {!isHomePage && } + {/* 只在下钻页面显示区域选择按钮组,传递地图实例 */} + {!isHomePage && } {/* 只在下钻页面显示导航栏 */} {!isHomePage && } -- GitLab