当前位置: 首页 > news >正文

GeoScene Maps 开发-核心地图-标记点管理-用户交互弹窗

什么是GeoScene Maps?

GeoScene Maps是一个功能强大的Web地图开发平台,它提供了丰富的地图服务和开发工具。与传统的地图API相比,GeoScene Maps具有以下优势:

  • 🗺️ 多样化底图:支持卫星影像、矢量地图、地形图等多种底图类型
  • 🎯 高精度定位:提供精确的地理位置服务
  • 🚀 高性能渲染:优化的地图渲染引擎,支持大量数据展示
  • 🔧 丰富的API:完整的地图操作和交互功能

项目架构设计

在开始编码之前,我们先来了解一下整个项目的架构设计。基于Vue 3的组合式API,我们将项目分为以下几个核心模块:

1. 核心地图组件 (MapComponent.vue)

这是我们的主要地图容器,负责:

  • 地图的初始化和渲染
  • 底图切换功能
  • 标记点的显示和交互
  • 弹窗的位置计算和显示

2. 标记点管理 (useMarkers.js)

使用Vue 3的组合式API设计,提供:

  • 标记点的创建、删除、更新
  • 不同类型标记点的管理(人员、车辆、设备)
  • 标记点状态的响应式管理

3. 用户交互弹窗 (UserPopup.vue)

负责展示:

  • 人员详细信息
  • 车辆状态信息
  • 设备管理界面
  • 各种操作按钮

第一步:地图初始化

让我们从最基础的地图初始化开始。首先,我们需要引入必要的GeoScene模块:

import Map from "@geoscene/core/Map.js";
import MapView from "@geoscene/core/views/MapView.js";
import WebTileLayer from "@geoscene/core/layers/WebTileLayer.js";
import Basemap from "@geoscene/core/Basemap.js";
import esriConfig from "@geoscene/core/config.js";

配置天地图服务

在中国的地图应用中,天地图是一个非常重要的底图数据源。我们需要先获取天地图的API Key:

// 天地图API Key - 需要到天地图官网申请
const TIANDITU_KEY = "你的天地图API密钥";

创建多种底图类型

为了给用户提供更好的体验,我们支持三种不同的底图类型:

const createBasemap = () => {switch (currentMapType.value) {case "satellite":// 卫星影像底图return markRaw(new Basemap({baseLayers: [// 卫星影像层markRaw(new WebTileLayer({urlTemplate: `https://t{subDomain}.tianditu.gov.cn/img_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={level}&TILEROW={row}&TILECOL={col}&tk=${TIANDITU_KEY}`,subDomains: ["0", "1", "2", "3"],copyright: "© 天地图"})),// 卫星标注层markRaw(new WebTileLayer({urlTemplate: `https://t{subDomain}.tianditu.gov.cn/cia_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cia&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={level}&TILEROW={row}&TILECOL={col}&tk=${TIANDITU_KEY}`,subDomains: ["0", "1", "2", "3"],copyright: "© 天地图"}))]}));// ... 其他底图类型}
};

初始化地图视图

const initMap = async () => {await nextTick(); // 等待DOM更新if (!mapContainer.value) {console.error("Map container not found");return;}try {const basemap = createBasemap();// 创建地图实例map.value = markRaw(new Map({basemap: basemap}));// 创建地图视图view.value = markRaw(new MapView({container: mapContainer.value,map: map.value,center: [116.6974, 39.9093], // 北京坐标zoom: 15,constraints: {rotationEnabled: false,snapToZoom: false,  // 允许连续缩放minZoom: 1,maxZoom: 20}}));// 等待视图准备完成view.value.when(async () => {console.log("地图初始化完成");// 在这里可以添加标记点等其他功能});} catch (error) {console.error("地图初始化失败:", error);}
};

第二步:标记点系统设计

标记点是地图应用中最重要的功能之一。我们设计了一个灵活的标记点管理系统。

组合式API设计

使用Vue 3的组合式API,我们可以创建一个可复用的标记点管理逻辑:

export function useMarkers(emit) {// 响应式状态const isInitialized = ref(false);const markers = ref(new Map());const selectedMarker = ref(null);// 标记点统计状态const markerState = reactive({totalCount: 0,personCount: 0,deviceCount: 0,vehicleCount: 0,lastError: null});// 初始化标记点服务const initMarkerService = async (view, map) => {try {markerService.initialize(view, map);setupEventListeners();isInitialized.value = true;return true;} catch (error) {console.error("标记点服务初始化失败:", error);return false;}};return {isInitialized,markers,selectedMarker,markerState,initMarkerService,addPersonMarker,addVehicleMarker,// ... 其他方法};
}

添加人员标记点

const addPersonMarker = (config) => {if (!isInitialized.value) {console.error("标记点服务未初始化");return null;}try {const markerInfo = markerService.createPersonMarker(config);// 更新响应式状态markers.value = new Map(markerService.getAllMarkers());updateMarkerCounts();return markerInfo;} catch (error) {console.error("添加人员标记点失败:", error);return null;}
};

批量添加标记点

为了提高性能和用户体验,我们支持批量添加标记点:

const addMultiplePeople = (peopleData, iconUrl) => {const results = [];peopleData.forEach(person => {const marker = addPersonMarker({id: person.id,name: person.name,position: person.position,iconUrl,company: person.company || '',phone: person.phone || '',title: person.title || person.name});if (marker) results.push(marker);});return results;
};

第三步:交互式弹窗系统

当用户点击地图上的标记点时,我们需要显示一个包含详细信息的弹窗。

弹窗位置计算

最关键的是如何将地理坐标转换为屏幕坐标:

const updatePopupPosition = () => {if (!popupGeometry.value || !view.value) {return;}try {// 创建地理坐标点const point = new Point({longitude: popupGeometry.value.longitude,latitude: popupGeometry.value.latitude,spatialReference: { wkid: 4326 }});// 转换为屏幕坐标const screenPosition = view.value.toScreen(point);if (screenPosition && typeof screenPosition.x === 'number') {// 更新弹窗位置popupPosition.value = {position: 'absolute',left: screenPosition.x + 'px',top: (screenPosition.y - 20) + 'px',zIndex: 1000,transform: 'translate(-50%, -100%)'};}} catch (error) {console.error("更新弹出框位置失败:", error);}
};

处理标记点点击事件

const handleMarkerClick = (data) => {// 设置弹窗信息if (data.attributes.type === 'vehicle') {popupMarker.value = {type: 'vehicle',name: data.attributes.name,plateNumber: data.attributes.plateNumber,driver: data.attributes.driver,vehicleType: data.attributes.vehicleType};} else {popupMarker.value = {type: 'person',name: data.attributes.name,company: data.attributes.company,phone: data.attributes.phone};}// 存储地理坐标popupGeometry.value = {longitude: data.graphic.geometry.longitude,latitude: data.graphic.geometry.latitude};// 显示弹窗showPopup.value = true;// 计算弹窗位置nextTick(() => {updatePopupPosition();});
};

监听地图变化

当用户缩放或平移地图时,弹窗位置需要实时更新:

if (!viewChangeListener && view.value) {viewChangeListener = view.value.watch(['center', 'zoom', 'rotation'], () => {updatePopupPosition();});
}

第四步:弹窗组件设计

我们的弹窗组件需要支持不同类型的信息展示:

人员信息弹窗

<template><div v-if="userInfo.type === 'person'" class="person-popup"><div class="user-header"><div class="user-avatar"><img src="@/assets/login/用户.svg" alt="用户头像" /></div><div class="user-info"><div class="info-row"><span class="info-label">姓名:</span><span class="us-name">{{ userInfo.name }}</span></div><div class="info-row"><span class="info-label">所属公司:</span><span class="company-name">{{ userInfo.company }}</span></div><div class="contact-info"><div class="contact-row"><span class="phone-label">联系电话:</span><span class="phone-number">{{ userInfo.phone }}</span><div class="action-buttons"><button class="call-btn" @click="handleCall(userInfo.phone)">📞</button><button class="message-btn" @click="handleMessage">💬</button></div></div></div></div><button class="popup-close" @click="closePopup">×</button></div><!-- 设备列表 --><div class="devices-section"><!-- 设备展示和管理 --></div><!-- 状态切换 --><div class="status-section"><div v-for="status in statusOptions" :key="status.value"class="status-item" :class="{ active: currentStatus === status.value }"@click="handleStatusChange(status.value)"><span>{{ status.label }}</span></div></div></div>
</template>

车辆信息弹窗

车辆弹窗需要展示不同的信息:

<div v-else-if="userInfo.type === 'vehicle'" class="vehicle-popup"><div class="vehicle-header"><div class="vehicle-icon"><img src="@/assets/map/bottom/车辆.png" alt="车辆图标" /></div><div class="vehicle-info"><div class="info-row"><span class="info-label">车辆名称:</span><span class="vehicle-name">{{ userInfo.name }}</span></div><div class="info-row"><span class="info-label">车牌号:</span><span class="plate-number">{{ userInfo.plateNumber }}</span></div><div class="info-row"><span class="info-label">驾驶员:</span><span class="driver-name">{{ userInfo.driver }}</span></div></div></div>
</div>

第五步:性能优化技巧

1. 使用markRaw避免响应式代理

GeoScene的对象不应该被Vue的响应式系统代理,这会导致性能问

// 正确的做法
map.value = markRaw(new Map({basemap: basemap
}));view.value = markRaw(new MapView({container: mapContainer.value,map: map.value,// ...
}));

2. 优化地图瓦片请求

为了避免429错误(请求过于频繁),我们需要配置合理的请求参数:

new WebTileLayer({urlTemplate: "...",subDomains: ["0", "1", "2", "3"], // 减少子域名数量requestOptions: {timeout: 10000,maxRetryCount: 3,retryDelay: 1000}
})

3. 内存管理

在组件卸载时,正确清理资源:

onUnmounted(() => {// 关闭弹窗closePopup();// 清理地图视图变化监听器if (viewChangeListener) {viewChangeListener.remove();viewChangeListener = null;}if (view.value) {view.value.destroy();view.value = null;}if (map.value) {map.value = null;}
});

第六步:实际应用示例

让我们看看如何在实际项目中使用这些功能:

添加测试数据

const addMultipleTestMarkers = () => {const center = view.value.center;// 人员数据const peopleData = [{id: 'person-001',name: '李四',position: { longitude: center.longitude + 0.001, latitude: center.latitude + 0.001 },company: '技术部',phone: '13800138001',title: '技术员工'}// ... 更多数据];// 车辆数据const vehiclesData = [{id: 'vehicle-001',name: '巡逻车1号',position: { longitude: center.longitude - 0.002, latitude: center.latitude - 0.001 },plateNumber: '京A12345',driver: '司机张三',type: '警用车辆'}// ... 更多数据];// 批量添加addMultiplePeople(peopleData, renIcon);addMultipleVehicles(vehiclesData, renIcon);
};

组件使用

在父组件中使用我们的地图组件:

<template><div class="app-container"><MapComponent ref="mapRef"@marker-click="handleMarkerClick"@map-click-empty="handleMapClickEmpty"/></div>
</template><script setup>
import MapComponent from '@/components/MapComponent.vue';const mapRef = ref(null);const handleMarkerClick = (data) => {console.log('标记点被点击:', data);
};const handleMapClickEmpty = () => {console.log('点击了地图空白区域');
};// 可以通过ref调用地图组件的方法
const addNewMarker = () => {mapRef.value?.addPersonMarker({id: 'new-person',name: '新用户',position: { longitude: 116.397, latitude: 39.909 }});
};
</script>

常见问题和解决方案

1. 地图加载缓慢

问题:天地图瓦片加载速度慢

解决方案:

  • 减少并发请求数量
  • 增加重试机制
  • 使用CDN加速

2. 弹窗位置不准确

问题:弹窗位置计算错误

解决方案:

  • 确保在DOM更新后计算位置
  • 监听地图视图变化事件
  • 使用防抖处理频繁更新

3. 内存泄漏

问题:长时间使用后浏览器卡顿

解决方案:

  • 正确清理事件监听器
  • 销毁地图实例
  • 避免响应式代理GeoScene对象

总结

通过本教程,我们学习了如何:

  1. 初始化GeoScene地图:包括底图配置和视图设置
  2. 设计标记点系统:使用组合式API管理标记点状态
  3. 实现交互弹窗:动态位置计算和信息展示
  4. 优化性能:内存管理和请求优化
  5. 处理实际应用场景:批量数据处理和用户交互

GeoScene Maps为我们提供了强大的地图开发能力,结合Vue 3的现代特性,我们可以构建出功能丰富、性能优秀的地图应用。希望这个教程能够帮助您在自己的项目中成功应用这些技术。

记住,好的地图应用不仅要功能完善,更要注重用户体验。通过合理的交互设计、流畅的动画效果和及时的反馈机制,您的地图应用一定能够给用户带来优秀的使用体验。

http://www.dtcms.com/a/355204.html

相关文章:

  • 大白话拆解力扣算法 HOT 100 - 哈希/双指针/滑动窗口
  • Mac Pro M4芯片 安装 VMware Fusion 和 windows
  • Vue Router 路由守卫详解与面试指南
  • 实体门店怎么利用小程序做好分销
  • 目标检测领域基本概念
  • 【Python】QT(PySide2、PyQt5):Qt Designer,VS Code使用designer,可能的报错
  • 发那科机器人弧焊电源气体省气装置
  • esp32c2 at 请问通过HTTPS进行OTA升级的AT命令流程有吗?
  • 专项智能练习(多媒体概述)
  • 如果已经安装了electron的一个版本,再次使用命令npm install electron不指定electron版本时,会下载安装新版本么?
  • VS2022+QT6.7+Multimedia(捕获Windows音频数据,生成实时频谱)
  • Day16_【机器学习建模流程】
  • Python备份实战专栏第2/6篇:30分钟搭建企业级API认证系统,安全性吊打90%的方案
  • R语言贝叶斯方法在生态环境领域中的高阶技术应用
  • Mac 开发环境与配置操作速查表
  • 基于Vue2+elementUi实现树形 横向 合并 table不规则表格
  • 华为S5720S重置密码
  • 前沿技术观察:从AI 时代到量子计算的下一站
  • 智能物联网(AIoT)核心技术落地路径与企业数字化转型适配方案
  • 如何通俗的理解操作系统的IO多路复用
  • H5 本地跨域设置
  • “帕萨特B5钳盘式制动器结构设计三维PROE模型7张CAD图纸PDF图“
  • UE5.5模型导入FBX强制x轴向前Force Front XAxis
  • 上线问题——Mac系统下如何获取鸿蒙APP证书公钥和MD5指纹
  • 密码管理中
  • 多线程 【详解】| Java 学习日志 | 第 14 天
  • Ansys Icepak AEDT 中的后处理脚本
  • 护网面经总结(三)
  • 三维细节呈现核心技术:法线、凹凸与置换贴图全解析与应用指南
  • 物业满意度调查数据分析——从 “数据杂乱” 到 “精准改进” 的落地经验(满意度调查问卷)