openlayer-渲染geoJson地图文件(二)

588 阅读2分钟

前言

基于上一篇文章已经实现了地图底图的初步构建,接下来实现如何在底图上加上一个高亮的边界地图区域,也就是GeoJSON地图加载.

第一步 获取GeoJson资源

在这里用的是阿里云提供的开源的地理工具[,](DataV.GeoAtlas地理小工具系列 (aliyun.com))

image.png 在这里选择您想要的区域文件,例如我选择了广东省佛山市,点击复制之后保存在项目的foshan.json文件中.

image.png

geoJson文件资源默认下载下来的类型是FeatureCollection,然后主要的就是各种feature中的coordinates表示经纬度的数据,openlayer就是根据其经纬数据来渲染出区域地图以及后续的点位图标等内容;

image.png

第二步 实现加载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则表示整个图层的填充样式;具体效果如下:

image.png

第三步 给图层加上阴影效果

为了使图层在地图上突出显示,也为了更加美观,需要有个类似于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)

看一下具体效果:

image.png

可以看到图层的阴影已经出来了,具体的位置可以根据上方的函数来进行控制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>