vue3加载shp文件 并地图上展示
展示:
代码:
<template><div class="yizhi-container"><div class="breadcrumb-bar" style="color:#666;padding:0;padding-bottom: 5px;font-size: 15px;"><el-breadcrumb class="app-breadcrumb"><el-breadcrumb-item style="cursor:pointer" @click="goUrl">首页 ></el-breadcrumb-item><el-breadcrumb-item style="cursor:pointer" @click="goUrl">数字底座 ></el-breadcrumb-item><el-breadcrumb-item>遗址影响评估</el-breadcrumb-item></el-breadcrumb></div><div class="main-container"><!-- 左侧地图 --><div class="map-section"><div id="map" class="map-box"></div></div><!-- 右侧面板 --><div class="side-panel"><div class="card"><h3>遗址数据</h3><div class="form-group"><label>选择遗址图层</label><select v-model="selectedLayer" class="input" @change="loadSelectedLayer"><option value="">请选择</option><option value="遗产价值">遗产价值</option><option value="改变程度">改变程度</option><option value="shanghai_gaode">shanghai_gaode</option></select></div><div class="form-group"><label>地图样式</label><select v-model="mapStyle" class="input" @change="updateMapStyle"><option value="normal">标准</option><option value="satellite">卫星</option><option value="dark">深色</option></select></div><div class="form-group"><label>缩放级别</label><input v-model="zoomLevel" type="range" min="10" max="18" class="slider" @input="updateZoom"><span>{{ zoomLevel }}</span></div></div><div class="card result-card"><h3>图层信息</h3><div v-if="currentLayerInfo"><p><strong>图层名称:</strong>{{ currentLayerInfo.name }}</p><p><strong>要素数量:</strong>{{ currentLayerInfo.featureCount }}</p><p><strong>加载状态:</strong>{{ currentLayerInfo.status }}</p></div><div v-else><p>请选择要显示的图层</p></div></div></div></div></div>
</template><script>
import { ref, onMounted } from "vue";
import * as shp from "shpjs";export default {setup() {// 数据const selectedLayer = ref("");const mapStyle = ref("normal");const zoomLevel = ref(13);const currentLayerInfo = ref(null);const map = ref(null);const currentGraphics = ref([]);// 方法const initMap = () => {map.value = new window.AMap.Map("map", {center: [121.499609, 31.239859],zoom: zoomLevel.value,mapStyle: `amap://styles/${mapStyle.value}`});};const goUrl = () => {window.parent.postMessage({type: "go",url: "SY",},"*");};const loadSelectedLayer = async () => {if (!selectedLayer.value) {clearMap();currentLayerInfo.value = null;return;}try {currentLayerInfo.value = {name: selectedLayer.value,featureCount: 0,status: '加载中...'};// 加载SHP文件const shpUrl = `/file/yizhi/${selectedLayer.value}.shp`;const response = await fetch(shpUrl);if (!response.ok) {throw new Error(`无法加载SHP文件: ${response.status}`);}const shpBuffer = await response.arrayBuffer();// 使用shpjs解析SHP文件const geometry = await shp.parseShp(shpBuffer);// 将几何数据包装成完整的GeoJSON格式const geojson = {type: "FeatureCollection",features: []};// 处理单个几何对象或几何数组if (Array.isArray(geometry)) {// 如果是几何数组,为每个几何创建featuregeometry.forEach((geom, index) => {geojson.features.push({type: "Feature",id: index,geometry: geom,properties: {}});});} else if (geometry && geometry.type) {// 如果是单个几何对象geojson.features.push({type: "Feature",id: 0,geometry: geometry,properties: {}});}console.log("===geojson==")console.log(geojson)// 显示要素displayFeatures(geojson);currentLayerInfo.value = {name: selectedLayer.value,featureCount: geojson.features.length,status: '加载完成'};} catch (error) {console.error('加载SHP文件失败:', error);currentLayerInfo.value = {name: selectedLayer.value,featureCount: 0,status: '加载失败'};alert('加载SHP文件失败,请检查文件路径和格式');}};const displayFeatures = (geojson) => {clearMap();if (!geojson.features || geojson.features.length === 0) {console.warn('没有找到要素');return;}geojson.features.forEach((feature, index) => {if (feature.geometry && feature.geometry.coordinates) {// 根据几何类型创建不同的图形if (feature.geometry.type === 'Point') {createPointMarker(feature.geometry, index);} else if (feature.geometry.type === 'Polygon') {createPolygon(feature.geometry, index);} else if (feature.geometry.type === 'LineString') {createPolyline(feature.geometry, index);} else if (feature.geometry.type === 'MultiPoint') {createMultiPoint(feature.geometry, index);} else if (feature.geometry.type === 'MultiLineString') {createMultiLineString(feature.geometry, index);} else if (feature.geometry.type === 'MultiPolygon') {createMultiPolygon(feature.geometry, index);}}});};const createPointMarker = (geometry, index) => {const coordinates = geometry.coordinates;const marker = new window.AMap.Marker({position: [coordinates[0], coordinates[1]],title: `要素 ${index + 1}`,content: `<div style="background: #1890ff; width: 10px; height: 10px; border-radius: 50%;"></div>`});marker.setMap(map.value);currentGraphics.value.push(marker);};const createPolygon = (geometry) => {const coordinates = geometry.coordinates[0]; // 取外环const path = coordinates.map(coord => [coord[0], coord[1]]);const polygon = new window.AMap.Polygon({path: path,strokeColor: "#1890ff",strokeWeight: 2,fillColor: "#1890ff",fillOpacity: 0.4,strokeStyle: "solid"});polygon.setMap(map.value);currentGraphics.value.push(polygon);};const createPolyline = (geometry) => {const coordinates = geometry.coordinates;const path = coordinates.map(coord => [coord[0], coord[1]]);const polyline = new window.AMap.Polyline({path: path,strokeColor: "#1890ff",strokeWeight: 3,strokeStyle: "solid"});polyline.setMap(map.value);currentGraphics.value.push(polyline);};const createMultiPoint = (geometry, index) => {geometry.coordinates.forEach((pointCoords, pointIndex) => {createPointMarker({ type: 'Point', coordinates: pointCoords }, `${index}_${pointIndex}`);});};const createMultiLineString = (geometry, index) => {geometry.coordinates.forEach((lineCoords, lineIndex) => {createPolyline({ type: 'LineString', coordinates: lineCoords }, `${index}_${lineIndex}`);});};const createMultiPolygon = (geometry, index) => {geometry.coordinates.forEach((polygonCoords, polygonIndex) => {createPolygon({ type: 'Polygon', coordinates: polygonCoords }, `${index}_${polygonIndex}`);});};const clearMap = () => {if (currentGraphics.value.length > 0) {currentGraphics.value.forEach(graphic => {graphic.setMap(null);});currentGraphics.value = [];}};const updateMapStyle = () => {if (map.value) {map.value.setMapStyle(`amap://styles/${mapStyle.value}`);}};const updateZoom = () => {if (map.value) {map.value.setZoom(zoomLevel.value);}};// 生命周期onMounted(() => {initMap();});return {selectedLayer,mapStyle,zoomLevel,currentLayerInfo,goUrl,loadSelectedLayer,updateMapStyle,updateZoom};},
};
</script><style scoped>
.yizhi-container {font-family: "Segoe UI", sans-serif;background-color: #f2f4f8;height: calc(100vh - 20px);display: flex;flex-direction: column;
}.breadcrumb-bar {padding: 10px 20px;background: #ffffff;box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
}.main-container {flex: 1;display: flex;overflow: hidden;
}.map-section {flex: 3;border-right: 1px solid #eee;
}.map-box {width: 100%;height: 100%;
}.side-panel {width: 350px;flex: 2;padding: 20px;background-color: #f9fafc;display: flex;flex-direction: column;gap: 20px;
}.card {background: #fff;border-radius: 10px;padding: 20px;box-shadow: 0 1px 6px rgba(0, 0, 0, 0.05);
}.card h3 {font-size: 18px;margin-bottom: 15px;color: #333;
}.form-group {margin-bottom: 15px;display: flex;flex-direction: column;
}.form-group label {font-size: 14px;margin-bottom: 6px;color: #555;
}.input {padding: 8px 12px;border: 1px solid #ccc;border-radius: 6px;font-size: 14px;
}.slider {width: 100%;margin: 10px 0;
}.result-card p {margin: 8px 0;font-size: 14px;
}.result-card strong {color: #333;
}
</style>
引入高德:
shp文件: