前言
基于上一篇文章已经实现了地图底图的初步构建,接下来实现如何在底图上加上一个高亮的边界地图区域,也就是GeoJSON地图加载.
第一步 获取GeoJson资源
在这里用的是阿里云提供的开源的地理工具[,](DataV.GeoAtlas地理小工具系列 (aliyun.com))
在这里选择您想要的区域文件,例如我选择了广东省佛山市,点击复制之后保存在项目的foshan.json文件中.
geoJson文件资源默认下载下来的类型是FeatureCollection,然后主要的就是各种feature中的coordinates表示经纬度的数据,openlayer就是根据其经纬数据来渲染出区域地图以及后续的点位图标等内容;
第二步 实现加载geojson代码
const requireGeoJson = (url: string) => {
return new URL(`../../assets/geoJSON/${url}.json`, import.meta.url).href
}
const loadJsonMap = () => {
// 创建 GeoJSON 图层
const geoJSONLayer = new VectorLayer({
source: new VectorSource({
url: requireGeoJson('foshan'),
format: new GeoJSON()
}),
zIndex: 10,
style: new Style({
stroke: new Stroke({
color: 'rgb(131, 243, 255)',
width: 2 // 设置描边宽度为 2 像素
}),
fill: new Fill({
color: 'rgba(131, 243, 255, 0.16)'
})
})
})
map.value.addLayer(geoJSONLayer)
}
图中创建了一个新的向量图层对象VectorLayer,并且定义图层的数据源VectorSource,它从指定的 URL 加载 GeoJSON 数据.zIndex指定了图层的层级,一般来说只需要比底图高就可以了,Style用来指定图层的样式,如stroke用来表示图层的边框样式,而fill则表示整个图层的填充样式;具体效果如下:
第三步 给图层加上阴影效果
为了使图层在地图上突出显示,也为了更加美观,需要有个类似于3D立体的效果,试了很多种方法,由于openlayer不支持直接对地图文件实现偏移,也没办法直接设置阴影属性.在此,想到一个方法就是,在原图层下方加上一个黑色的阴影图层,然后对原来的json文件里面的coordinates属性做更改,例如加上0.01来实现偏移的目的;
首先来封装一个函数,传一个资源文件以及经度和纬度偏移的数值,代码如下
// 地图json的经纬度偏移
export const shiftCoordinates = (featureCollection: any, lon: any, lat: any) => {
// 遍历每个特征
featureCollection.features.forEach((feature: any) => {
// 获取几何对象
const geometry = feature.geometry;
// 根据几何类型进行不同的操作
if (geometry.type === 'Point') {
shiftPoint(geometry.coordinates, lon, lat);
} else if (geometry.type === 'LineString' || geometry.type === 'MultiLineString') {
shiftLineOrPolygon(geometry.coordinates, lon, lat);
} else if (geometry.type === 'Polygon' || geometry.type === 'MultiPolygon') {
geometry.coordinates.forEach((ring: any) => {
shiftLineOrPolygon(ring, lon, lat);
});
}
});
return featureCollection;
}
export const shiftPoint = (coordinates: any, lon: any, lat: any) => {
coordinates[0] += lon; // 经度
coordinates[1] += lat; // 纬度
}
export const shiftLineOrPolygon = (coordinates: any, lon: any, lat: any) => {
coordinates.forEach((line: any) => {
line.forEach((point: any) => {
point[0] += lon; // 经度
point[1] += lat; // 纬度
});
});
}
接着就是新增一个阴影图层,在此之前先把json资源文件进行转换,要注意的是,原先的图层加载的是url资源,所以你获取到的是地址,还需要自己手动转成资源才能进行转换,代码如下
const geojson = ref()
const loadGeoJson = async (url: string) => {
const response = await fetch(url, {
method: 'GET',
credentials: 'omit' // 避免发送Cookie
})
if (!response.ok) {
throw new Error(`Failed to fetch GeoJSON: ${response.status}`)
}
return await response.json()
}
//先获取资源地址
const geojsonUrl = requireGeoJson('foshan')
//获取地址里面的文件数据
geojson.value = await loadGeoJson(geojsonUrl)
const shiftedGeoJson = shiftCoordinates(geojson.value, 0.01, -0.03)
// 创建阴影图层
const shadowLayer = new VectorLayer({
source: new VectorSource({
features: new GeoJSON().readFeatures(shiftedGeoJson)
}), // 使用相同的源
zIndex: 9, // 确保阴影层在内容层之下
style: new Style({
stroke: new Stroke({
color: 'rgba(0, 0, 0, 0.5)', // 阴影颜色
width: 1
}),
fill: new Fill({
color: 'rgba(0, 0, 0, 0.5)' // 阴影填充颜色
})
})
})
// 添加图层到地图
map.value.addLayer(shadowLayer)
看一下具体效果:
可以看到图层的阴影已经出来了,具体的位置可以根据上方的函数来进行控制shiftCoordinates(geojson.value, 0.01, -0.03)
完整代码如下:
<script setup lang="ts">
import { Map, View } from 'ol'
import TileLayer from 'ol/layer/Tile'
import XYZ from 'ol/source/XYZ'
import { Fill, Stroke, Style } from 'ol/style'
import { Vector as VectorSource } from 'ol/source'
import VectorLayer from 'ol/layer/Vector'
import GeoJSON from 'ol/format/GeoJSON'
import { onMounted, ref } from 'vue'
const requireGeoJson = (url: string) => {
return new URL(`../../assets/geoJSON/${url}.json`, import.meta.url).href
}
// 地图json的经纬度偏移
const shiftCoordinates = (featureCollection: any, lon: any, lat: any) => {
// 遍历每个特征
featureCollection.features.forEach((feature: any) => {
// 获取几何对象
const geometry = feature.geometry
// 根据几何类型进行不同的操作
if (geometry.type === 'Point') {
shiftPoint(geometry.coordinates, lon, lat)
} else if (
geometry.type === 'LineString' ||
geometry.type === 'MultiLineString'
) {
shiftLineOrPolygon(geometry.coordinates, lon, lat)
} else if (
geometry.type === 'Polygon' ||
geometry.type === 'MultiPolygon'
) {
geometry.coordinates.forEach((ring: any) => {
shiftLineOrPolygon(ring, lon, lat)
})
}
})
return featureCollection
}
const shiftPoint = (coordinates: any, lon: any, lat: any) => {
coordinates[0] += lon // 经度
coordinates[1] += lat // 纬度
}
const shiftLineOrPolygon = (coordinates: any, lon: any, lat: any) => {
coordinates.forEach((line: any) => {
line.forEach((point: any) => {
point[0] += lon // 经度
point[1] += lat // 纬度
})
})
}
const map = ref()
const initMap = () => {
map.value = new Map({
// 让id为map的div作为地图的容器
target: 'map',
// 设置地图图层
layers: [
new TileLayer({
source: new XYZ({
url: 'http://10.151.64.97/map2/{z}/{x}/{y}.jpg'
}),
zIndex: 5
})
],
// 设置显示地图的视图
view: new View({
center: [112.9598836, 23.12635347],
zoom: 9.5,
maxZoom: 14,
minZoom: 8,
projection: 'EPSG:4326'
})
})
}
const geojson = ref()
const loadGeoJson = async (url: string) => {
const response = await fetch(url, {
method: 'GET',
credentials: 'omit' // 避免发送Cookie
})
if (!response.ok) {
throw new Error(`Failed to fetch GeoJSON: ${response.status}`)
}
return await response.json()
}
const loadJsonMap = async () => {
// 创建 GeoJSON 图层
const geoJSONLayer = new VectorLayer({
source: new VectorSource({
url: requireGeoJson('foshan'),
format: new GeoJSON()
}),
zIndex: 10,
style: new Style({
stroke: new Stroke({
color: 'rgb(131, 243, 255)',
width: 2 // 设置描边宽度为 1 像素
}),
fill: new Fill({
color: 'rgba(131, 243, 255, 0.16)'
})
})
})
const geojsonUrl = requireGeoJson('foshan')
geojson.value = await loadGeoJson(geojsonUrl)
const shiftedGeoJson = shiftCoordinates(geojson.value, 0.01, -0.03)
// 创建阴影图层
const shadowLayer = new VectorLayer({
source: new VectorSource({
features: new GeoJSON().readFeatures(shiftedGeoJson)
}), // 使用相同的源
zIndex: 9, // 确保阴影层在内容层之下
style: new Style({
stroke: new Stroke({
color: 'rgba(0, 0, 0, 0.5)', // 阴影颜色
width: 1
}),
fill: new Fill({
color: 'rgba(0, 0, 0, 0.5)' // 阴影填充颜色
})
})
})
// 添加图层到地图
map.value.addLayer(shadowLayer)
map.value.addLayer(geoJSONLayer)
}
onMounted(() => {
initMap()
loadJsonMap()
})
</script>
<template>
<div class="container">
<div id="map"></div>
</div>
</template>
<style lang="less" scoped>
.container {
width: 100%;
height: 100vh;
background: #000;
display: flex;
flex-direction: column;
#map {
flex: 1;
}
}
</style>