Skip to content

Web Gis开发 Openlayers

npm install ol

https://www.openlayers.vip/

https://openlayers.org/en/latest/apidoc/module-ol_Map-Map.html

Openlayers 初始化高德地图

一张图片

HTML

<template>
    <!-- 地图控制按钮区域 -->
    <div class="disGIS">
        <el-button @click="btn1">矢量路网</el-button>
        <el-button @click="btn2">影像图</el-button>
        <el-button @click="btn3">点聚合</el-button>
    </div>
    <div class="pos" v-show="showOpenLayers">
        <div style="text-align:center">{{ clickedFeatureValue.name }}</div>
        <div>地区经纬度:{{ [clickedFeatureValue.lon, clickedFeatureValue.lat] }}</div>
        <div>区域:{{ clickedFeatureValue.city }}</div>
        <div>value:{{ clickedFeatureValue.value }}</div>
        <div>介绍:{{ clickedFeatureValue.title }}</div>
        <div>评论:{{ clickedFeatureValue.zxcv }}</div>


        
    </div>
    <!-- 地图容器 -->
    <div id="map" style="width: 100%; height: 100vh;"></div>
</template>
  
<script setup>
import GetMap from '../components/GetMap.js'
import "ol/ol.css";
import Map from "ol/Map";
import OSM from "ol/source/OSM";
import TileLayer from "ol/layer/Tile";
import View from "ol/View";
import XYZ from 'ol/source/XYZ';
import { fromLonLat } from 'ol/proj';
import VectorLayer from 'ol/layer/Vector'
import VectorSource from 'ol/source/Vector'
import Feature from 'ol/Feature'
import Overlay from 'ol/Overlay'

import Point from 'ol/geom/Point'
import { Style, Fill, Stroke, Circle } from 'ol/style'
import GeoJSON from 'ol/format/GeoJSON'
import { Text } from 'ol/style'
import Cluster from 'ol/source/Cluster';
import { ref, computed, onMounted } from 'vue'
const showOpenLayers = ref(false)
const cityNanJing = ref([{}])
const URL = ref('http://wprd0{1-4}.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scl=1&style=7')
let gaodeMapLayer = null;
const ShowGis = ref('图层');
const baseLayer = ref()
const vectorLayer = ref()
const APICity = ref([])
const showPopup = false
const clickedFeatureValue = ref('')
const clickedFeature = ref(null)
const clickedCoordinate = ref('')
// 点数据
const points = ref([
    { lon: 118.796624, lat: 32.059344, value: 1111110, name: '南京', city:'玄武区',title: '1232123123', zxcv: '顶顶顶顶', show: '1' },
    { lon: 118.792199, lat: 32.050678, value: 5, show: '1' },
    { lon: 118.786088, lat: 32.033818, value: 8, show: '1' },
    { lon: 118.732688, lat: 32.004538, value: 7, show: '1' },
    { lon: 118.769739, lat: 32.066966, value: 6, show: '1' },
    { lon: 118.625307, lat: 32.05839, value: 9, show: '1' },
    { lon: 118.808702, lat: 32.102147, value: 4, show: '1' },
    { lon: 118.77207, lat: 31.995946, value: 3, show: '1' },
    { lon: 118.850621, lat: 31.953418, value: 12, show: '1' },
    { lon: 118.85065, lat: 32.340655, value: 2, show: '1' }
]);
const btn1 = () => {
    ShowGis.value = '图层'
    Gis_init()
}
const btn2 = () => {
    ShowGis.value = '影像图'
    Gis_init()
}
const btn3 = () => {
    // 移除可能已存在的聚类图层
    const layers = gaodeMapLayer.getLayers().getArray();
    for (let i = layers.length - 1; i >= 0; i--) {
        if (layers[i].get('name') === 'clusterLayer') {
            gaodeMapLayer.removeLayer(layers[i]);
        }
    }
    // 创建点要素
    const features = points.value.map(point => {
        const feature = new Feature({
            geometry: new Point(fromLonLat([point.lon, point.lat])),
            value: point.value,
            feature1: point,//   这里的获取值的关键
        });
        return feature;
    });
    // 创建矢量源并添加点要素
    const source = new VectorSource({
        features: features
    });

    // 创建聚类源
    const clusterSource = new Cluster({
        distance: 40, // 聚合距离(像素)
        source: source
    });
    // 创建聚类图层
    const clusterLayer = new VectorLayer({
        source: clusterSource,
        style: function (feature) {
            const size = feature.get('features').length;
            const features = feature.get('features');
            const avgValue = features.reduce((sum, f) => sum + f.get('value'), 0) / size;

            // 根据平均值计算半径
            const radius = Math.min(10 + avgValue * 2, 30);
            let displayText = '';
            if (size > 1) {
                displayText = `${size}\navg:${avgValue.toFixed(1)}`; // 聚合点显示数量和平均值
            } else {
                displayText = features[0].get('value').toString(); // 单点显示原始值
            }
            return new Style({
                image: new Circle({
                    radius: radius,
                    fill: new Fill({
                        color: 'orange'
                    }),
                    stroke: new Stroke({
                        color: 'white',
                        width: 2
                    })
                }),
                text: new Text({
                    text: displayText,
                    font: 'bold 12px Arial',
                    fill: new Fill({
                        color: '#fff'
                    }),
                    stroke: new Stroke({
                        color: 'rgba(0,0,0,0.7)',
                        width: 3
                    }),
                    textAlign: 'center',
                    textBaseline: 'middle',
                    offsetY: 0
                })
            });
        },
        name: 'clusterLayer' // 为图层添加标识
    });

    // 添加到地图
    gaodeMapLayer.addLayer(clusterLayer)

    // GIS点击事件获取你当前的值    feature1: point,//   这里的获取值的关键
    gaodeMapLayer.on('singleclick', (e) => {
        // console.log(feature,'走了!')
        gaodeMapLayer.forEachFeatureAtPixel(e.pixel, (feature) => {
            console.log(feature, 'feature')
            if (!feature?.values_.features) {
                showOpenLayers.value = false
                return false // 确保只处理第一个匹配要素

            } else {
                if (feature) {
                    clickedFeatureValue.value = feature?.values_?.features[0]?.values_?.feature1
                    // const coord = feature.getGeometry().getCoordinates()
                    // clickedCoordinate.value = `经度: ${coord[0].toFixed(6)}, 纬度: ${coord[1].toFixed(6)}`
                    // clickedFeatureValue.value = feature.get('feature') // 确保属性名一致
                    // clickedFeatureValue.value = feature.get('feature1') // 确保属性名一致
                    showOpenLayers.value = true
                    return true // 确保只处理第一个匹配要素
                }
            }
        })
    })


}
const Gis_init = () => {
    GetMap.then(res => {
        res.data.features.forEach((item, index) => {
            APICity.value.push(item.properties.center)
        });
        if (gaodeMapLayer) {
            gaodeMapLayer.dispose();
        }

        const source = new XYZ({
            url: ShowGis.value === '图层' ? 'http://wprd0{1-4}.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scl=1&style=7'
                : 'http://wprd0{1-4}.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scl=2&style=6',
            wrapX: false,
        })

        const layer = new TileLayer({
            source: source
        })
        // 严格使用points.value创建要素
        const features = points.value.map(point => {
            const feature = new Feature({
                geometry: new Point(fromLonLat([point.lon, point.lat])),
                originalValue: point.value // 使用统一属性名
            })
            return feature
        })
        console.log(features, 'featuresfeaturesfeatures')
        vectorLayer.value = new VectorLayer({
            source: new VectorSource({
                url: 'https://geo.datav.aliyun.com/areas_v3/bound/320100_full.json',
                format: new GeoJSON(),
                // features:features,
            }),
            style: function (feature) {
                const name = feature.get('name');
                // 定义要高亮的区域列表
                const highlightAreas = ['秦淮区', '建邺区']; // 替换为你需要高亮的区域
                const highlightAreas1 = ['江宁区',]; // 替换为你需要高亮的区域

                if (highlightAreas.includes(name)) {
                    // 高亮样式
                    return new Style({
                        fill: new Fill({ color: 'rgba(255, 0, 0, 0.347)' }), // 红色高亮
                        stroke: new Stroke({ color: 'rgba(255, 0, 0, 0.8)', width: 2 }),
                        text: new Text({
                            text: name,
                            font: '14px Calibri,sans-serif',
                            fill: new Fill({ color: 'white' }),
                            overflow: true
                        })
                    });
                } else if (highlightAreas1.includes(name)) {
                    // 高亮样式
                    return new Style({
                        fill: new Fill({ color: '#c59d528a' }), // 红色高亮
                        // stroke: new Stroke({ color: 'white', width:0 }),
                        text: new Text({
                            text: name,
                            font: '14px Calibri,sans-serif',
                            fill: new Fill({ color: 'white' }),
                            overflow: true
                        })
                    });
                } else {
                    // 默认样式
                    return new Style({
                        fill: new Fill({ color: 'rgba(0, 153, 148, 0.3)' }),
                        stroke: new Stroke({ color: 'rgba(73, 242, 242, 0.8)', width: 0.5 }),
                        text: new Text({
                            text: name,
                            font: '14px Calibri,sans-serif',
                            fill: new Fill({ color: 'white' }),
                            overflow: true
                        })
                    });
                }
                // return new Style({
                //     fill: new Fill({ color: 'rgba(0, 153, 148, 0.3)' }),
                //     stroke: new Stroke({ color: 'rgba(73, 242, 242, 0.8)', width: 1 }),
                //     text: new Text({
                //         text: feature.get('name'),
                //         font: '14px Calibri,sans-serif',
                //         fill: new Fill({ color: 'white' }),
                //         overflow: true
                //     })
                // });
            }
        })
        gaodeMapLayer = new Map({
            target: 'map',
            layers: ShowGis.value == '影像图' ? [layer, vectorLayer.value] : [layer],
            view: new View({
                center: fromLonLat([118.796624, 32.059344]),
                zoom: 10,
                projection: 'EPSG:3857',
            }),
        });
    })
}

onMounted(() => {
    Gis_init()
})
</script>
  
<style scoped lang="scss">
.disGIS {
    position: relative;
    position: fixed;
    top: 20px;
    left: 0;
    width: 300px;
    border: 1px solid #ccc;
    background: white;
    border-radius: 3%;
    z-index: 999;
    background: white;
}

.el-button {}

::v-deep .el-button+.el-button {
    margin: 3px 0;
}

.pos {
    position: absolute;
    top: 200px;
    z-index: 999;
    width: 300px;
    height: 300px;
    border: 1px solid #ccc;
    background: white;
    display: flex;
    flex-direction: column;

    div {
        padding: 10px;
    }
}
</style>

<template>
    <!-- 地图控制按钮区域 -->
    <div class="disGIS">
        <el-button @click="btn1">矢量路网</el-button>
        <el-button @click="btn2">影像图</el-button>
        <el-button @click="btn3">点聚合</el-button>
    </div>
    <div class="pos" v-show="showOpenLayers">
        <div style="text-align:center">{{ clickedFeatureValue.name }}</div>
        <div>地区经纬度:{{ [clickedFeatureValue.lon, clickedFeatureValue.lat] }}</div>
        <div>区域:{{ clickedFeatureValue.city }}</div>
        <div>value:{{ clickedFeatureValue.value }}</div>
        <div>介绍:{{ clickedFeatureValue.title }}</div>
        <div>评论:{{ clickedFeatureValue.zxcv }}</div>


        
    </div>
    <!-- 地图容器 -->
    <div id="map" style="width: 100%; height: 100vh;"></div>
</template>
  
<script setup>
import GetMap from '../components/GetMap.js'
import "ol/ol.css";
import Map from "ol/Map";
import OSM from "ol/source/OSM";
import TileLayer from "ol/layer/Tile";
import View from "ol/View";
import XYZ from 'ol/source/XYZ';
import { fromLonLat } from 'ol/proj';
import VectorLayer from 'ol/layer/Vector'
import VectorSource from 'ol/source/Vector'
import Feature from 'ol/Feature'
import Overlay from 'ol/Overlay'

import Point from 'ol/geom/Point'
import { Style, Fill, Stroke, Circle } from 'ol/style'
import GeoJSON from 'ol/format/GeoJSON'
import { Text } from 'ol/style'
import Cluster from 'ol/source/Cluster';
import { ref, computed, onMounted } from 'vue'
const showOpenLayers = ref(false)
const cityNanJing = ref([{}])
const URL = ref('http://wprd0{1-4}.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scl=1&style=7')
let gaodeMapLayer = null;
const ShowGis = ref('图层');
const baseLayer = ref()
const vectorLayer = ref()
const APICity = ref([])
const showPopup = false
const clickedFeatureValue = ref('')
const clickedFeature = ref(null)
const clickedCoordinate = ref('')
// 点数据
const points = ref([
    { lon: 118.796624, lat: 32.059344, value: 1111110, name: '南京', city:'玄武区',title: '1232123123', zxcv: '顶顶顶顶', show: '1' },
    { lon: 118.792199, lat: 32.050678, value: 5, show: '1' },
    { lon: 118.786088, lat: 32.033818, value: 8, show: '1' },
    { lon: 118.732688, lat: 32.004538, value: 7, show: '1' },
    { lon: 118.769739, lat: 32.066966, value: 6, show: '1' },
    { lon: 118.625307, lat: 32.05839, value: 9, show: '1' },
    { lon: 118.808702, lat: 32.102147, value: 4, show: '1' },
    { lon: 118.77207, lat: 31.995946, value: 3, show: '1' },
    { lon: 118.850621, lat: 31.953418, value: 12, show: '1' },
    { lon: 118.85065, lat: 32.340655, value: 2, show: '1' }
]);
const btn1 = () => {
    ShowGis.value = '图层'
    Gis_init()
}
const btn2 = () => {
    ShowGis.value = '影像图'
    Gis_init()
}
const btn3 = () => {
    // 移除可能已存在的聚类图层
    const layers = gaodeMapLayer.getLayers().getArray();
    for (let i = layers.length - 1; i >= 0; i--) {
        if (layers[i].get('name') === 'clusterLayer') {
            gaodeMapLayer.removeLayer(layers[i]);
        }
    }
    // 创建点要素
    const features = points.value.map(point => {
        const feature = new Feature({
            geometry: new Point(fromLonLat([point.lon, point.lat])),
            value: point.value,
            feature1: point,//   这里的获取值的关键
        });
        return feature;
    });
    // 创建矢量源并添加点要素
    const source = new VectorSource({
        features: features
    });

    // 创建聚类源
    const clusterSource = new Cluster({
        distance: 40, // 聚合距离(像素)
        source: source
    });
    // 创建聚类图层
    const clusterLayer = new VectorLayer({
        source: clusterSource,
        style: function (feature) {
            const size = feature.get('features').length;
            const features = feature.get('features');
            const avgValue = features.reduce((sum, f) => sum + f.get('value'), 0) / size;

            // 根据平均值计算半径
            const radius = Math.min(10 + avgValue * 2, 30);
            let displayText = '';
            if (size > 1) {
                displayText = `${size}\navg:${avgValue.toFixed(1)}`; // 聚合点显示数量和平均值
            } else {
                displayText = features[0].get('value').toString(); // 单点显示原始值
            }
            return new Style({
                image: new Circle({
                    radius: radius,
                    fill: new Fill({
                        color: 'orange'
                    }),
                    stroke: new Stroke({
                        color: 'white',
                        width: 2
                    })
                }),
                text: new Text({
                    text: displayText,
                    font: 'bold 12px Arial',
                    fill: new Fill({
                        color: '#fff'
                    }),
                    stroke: new Stroke({
                        color: 'rgba(0,0,0,0.7)',
                        width: 3
                    }),
                    textAlign: 'center',
                    textBaseline: 'middle',
                    offsetY: 0
                })
            });
        },
        name: 'clusterLayer' // 为图层添加标识
    });

    // 添加到地图
    gaodeMapLayer.addLayer(clusterLayer)

    // GIS点击事件获取你当前的值    feature1: point,//   这里的获取值的关键
    gaodeMapLayer.on('singleclick', (e) => {
        // console.log(feature,'走了!')
        gaodeMapLayer.forEachFeatureAtPixel(e.pixel, (feature) => {
            console.log(feature, 'feature')
            if (!feature?.values_.features) {
                showOpenLayers.value = false
                return false // 确保只处理第一个匹配要素

            } else {
                if (feature) {
                    clickedFeatureValue.value = feature?.values_?.features[0]?.values_?.feature1
                    // const coord = feature.getGeometry().getCoordinates()
                    // clickedCoordinate.value = `经度: ${coord[0].toFixed(6)}, 纬度: ${coord[1].toFixed(6)}`
                    // clickedFeatureValue.value = feature.get('feature') // 确保属性名一致
                    // clickedFeatureValue.value = feature.get('feature1') // 确保属性名一致
                    showOpenLayers.value = true
                    return true // 确保只处理第一个匹配要素
                }
            }
        })
    })


}
const Gis_init = () => {
    GetMap.then(res => {
        res.data.features.forEach((item, index) => {
            APICity.value.push(item.properties.center)
        });
        if (gaodeMapLayer) {
            gaodeMapLayer.dispose();
        }

        const source = new XYZ({
            url: ShowGis.value === '图层' ? 'http://wprd0{1-4}.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scl=1&style=7'
                : 'http://wprd0{1-4}.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scl=2&style=6',
            wrapX: false,
        })

        const layer = new TileLayer({
            source: source
        })
        // 严格使用points.value创建要素
        const features = points.value.map(point => {
            const feature = new Feature({
                geometry: new Point(fromLonLat([point.lon, point.lat])),
                originalValue: point.value // 使用统一属性名
            })
            return feature
        })
        console.log(features, 'featuresfeaturesfeatures')
        vectorLayer.value = new VectorLayer({
            source: new VectorSource({
                url: 'https://geo.datav.aliyun.com/areas_v3/bound/320100_full.json',
                format: new GeoJSON(),
                // features:features,
            }),
            style: function (feature) {
                const name = feature.get('name');
                // 定义要高亮的区域列表
                const highlightAreas = ['秦淮区', '建邺区']; // 替换为你需要高亮的区域
                const highlightAreas1 = ['江宁区',]; // 替换为你需要高亮的区域

                if (highlightAreas.includes(name)) {
                    // 高亮样式
                    return new Style({
                        fill: new Fill({ color: 'rgba(255, 0, 0, 0.347)' }), // 红色高亮
                        stroke: new Stroke({ color: 'rgba(255, 0, 0, 0.8)', width: 2 }),
                        text: new Text({
                            text: name,
                            font: '14px Calibri,sans-serif',
                            fill: new Fill({ color: 'white' }),
                            overflow: true
                        })
                    });
                } else if (highlightAreas1.includes(name)) {
                    // 高亮样式
                    return new Style({
                        fill: new Fill({ color: '#c59d528a' }), // 红色高亮
                        // stroke: new Stroke({ color: 'white', width:0 }),
                        text: new Text({
                            text: name,
                            font: '14px Calibri,sans-serif',
                            fill: new Fill({ color: 'white' }),
                            overflow: true
                        })
                    });
                } else {
                    // 默认样式
                    return new Style({
                        fill: new Fill({ color: 'rgba(0, 153, 148, 0.3)' }),
                        stroke: new Stroke({ color: 'rgba(73, 242, 242, 0.8)', width: 0.5 }),
                        text: new Text({
                            text: name,
                            font: '14px Calibri,sans-serif',
                            fill: new Fill({ color: 'white' }),
                            overflow: true
                        })
                    });
                }
                // return new Style({
                //     fill: new Fill({ color: 'rgba(0, 153, 148, 0.3)' }),
                //     stroke: new Stroke({ color: 'rgba(73, 242, 242, 0.8)', width: 1 }),
                //     text: new Text({
                //         text: feature.get('name'),
                //         font: '14px Calibri,sans-serif',
                //         fill: new Fill({ color: 'white' }),
                //         overflow: true
                //     })
                // });
            }
        })
        gaodeMapLayer = new Map({
            target: 'map',
            layers: ShowGis.value == '影像图' ? [layer, vectorLayer.value] : [layer],
            view: new View({
                center: fromLonLat([118.796624, 32.059344]),
                zoom: 10,
                projection: 'EPSG:3857',
            }),
        });
    })
}

onMounted(() => {
    Gis_init()
})
</script>
  
<style scoped lang="scss">
.disGIS {
    position: relative;
    position: fixed;
    top: 20px;
    left: 0;
    width: 300px;
    border: 1px solid #ccc;
    background: white;
    border-radius: 3%;
    z-index: 999;
    background: white;
}

.el-button {}

::v-deep .el-button+.el-button {
    margin: 3px 0;
}

.pos {
    position: absolute;
    top: 200px;
    z-index: 999;
    width: 300px;
    height: 300px;
    border: 1px solid #ccc;
    background: white;
    display: flex;
    flex-direction: column;

    div {
        padding: 10px;
    }
}
</style>

替我上班,工资分你一半