152.Vue3中使用OpenLayers+Turf实现遮罩布挖洞效果

项目演示了如何使用 Turf.js 的 turf.mask() 方法与 OpenLayers 实现地图遮罩布挖洞效果,支持一键生成洞口区域、遮罩布、以及最终合成的遮罩布挖洞图形。


📸效果预览


(上图为遮罩布在地图上挖出洞后的样式,仅供参考)


🧩技术栈

  • Vue3 + Composition API

  • OpenLayers(地图库)

  • Turf.js(地理计算库)

  • Element Plus(UI 控件)

  • Autonavi 高德地图瓦片源


✨功能介绍

按钮名称功能描述
绘制小洞使用 turf.polygon 创建一个小洞区域(Polygon)
绘制遮罩布使用 turf.polygon 创建整个大遮罩布区域
合力遮罩打洞使用 turf.mask(洞, 遮罩布) 将遮罩布打上洞
清除图层清除当前地图上的所有遮罩图形


🧠关键逻辑详解

1️⃣ turf.mask 实现遮罩减洞

const maskResult = () => {
  turfSource.clear()
  const hole = turf.polygon(holeData)
  const mask = turf.polygon(maskData)
  const masked = turf.mask(hole, JSON.parse(JSON.stringify(mask)))
  show(masked)
}
  • turf.polygon():构建多边形

  • turf.mask():将 mask 多边形“剪掉” hole 区域,返回一个挖好洞的多边形


2️⃣ GeoJSON 与 OpenLayers 特征同步

const show = (geojsonData) => {
  const features = new GeoJSON().readFeatures(geojsonData, {
    dataProjection: 'EPSG:4326',
    featureProjection: 'EPSG:3857',
  })
  turfSource.addFeatures(features)
}
  • 使用 GeoJSON 读取并转换为 OpenLayers 能识别的 Feature 实体,投影从 WGS84 转成 EPSG:3857


3️⃣ 样式定义:遮罩区域 + 多边形点位标注

const vectorLayer = new VectorLayer({
  source: turfSource,
  style: [
    new Style({
      fill: new Fill({ color: 'rgba(255,0,0,0.2)' }),
      stroke: new Stroke({ width: 2, color: 'blue' })
    }),
    new Style({
      image: new RegularShape({
        radius: 6,
        points: 4,
        rotation: Math.PI / 4,
        fill: new Fill({ color: 'white' }),
        stroke: new Stroke({ color: 'red', width: 2 })
      }),
      geometry: function (feature) {
        const coordinates = feature.getGeometry().getCoordinates()[0]
        return new MultiPoint(coordinates)
      }
    })
  ]
})
  • 外层填充透明红色,描边蓝色

  • 顶点采用红白相间的矩形菱形样式标注(辅助观察)


🧪完整示例运行步骤

✅ 1. 安装依赖

npm install ol @turf/turf

✅ 2. 页面引入组件

将源码内容放置于 Vue 页面中(推荐 *.vue 文件中)


📦完整代码

<!--
 * @Author: 彭麒
 * @Date: 2025/7/14
 * @Email: 1062470959@qq.com
 * @Description: 此源码版权归吉檀迦俐所有,可供学习和借鉴或商用。
 -->
<template>
  <div class="container">
    <div class="w-full flex justify-center flex-wrap">
      <div class="font-bold text-[24px]">
        Vue3中使用OpenLayers+Turf 实现遮罩布挖洞效果
      </div>
    </div>
    <div class="flex gap-2 mb-4 w-full justify-center">
      <el-button type="primary" size="small" @click="drawHole">绘制小洞</el-button>
      <el-button type="primary" size="small" @click="drawMask">绘制遮罩布</el-button>
      <el-button type="primary" size="small" @click="maskResult">合力遮罩打洞</el-button>
      <el-button type="danger" size="small" @click="clearSource">清除图层</el-button>
    </div>
    <div id="vue-openlayers" ref="mapContainer"></div>
  </div>
</template>

<script setup>
import 'ol/ol.css'
import { ref, onMounted } from 'vue'
import Map from 'ol/Map'
import View from 'ol/View'
import TileLayer from 'ol/layer/Tile'
import XYZ from 'ol/source/XYZ'
import VectorSource from 'ol/source/Vector'
import VectorLayer from 'ol/layer/Vector'
import GeoJSON from 'ol/format/GeoJSON'
import { Fill, Stroke, Style, RegularShape } from 'ol/style'
import { fromLonLat } from 'ol/proj'
import { MultiPoint } from 'ol/geom'
import * as turf from '@turf/turf'

const map = ref(null)
const mapContainer = ref(null)

const turfSource = new VectorSource({ wrapX: false })

const holeData = [
  [
    [112, -21],
    [116, -36],
    [146, -39],
    [153, -24],
    [133, -10],
    [112, -21]
  ]
]

const maskData = [
  [
    [90, -55],
    [170, -55],
    [170, 10],
    [90, 10],
    [90, -55]
  ]
]

const show = (geojsonData) => {
  const features = new GeoJSON().readFeatures(geojsonData, {
    dataProjection: 'EPSG:4326',
    featureProjection: 'EPSG:3857',
  })
  turfSource.addFeatures(features)
}

const clearSource = () => {
  turfSource.clear()
}

const drawHole = () => {
  const hole = turf.polygon(holeData)
  show(hole)
}

const drawMask = () => {
  const mask = turf.polygon(maskData)
  show(mask)
}

const maskResult = () => {
  turfSource.clear()
  const hole = turf.polygon(holeData)
  const mask = turf.polygon(maskData)
  const masked = turf.mask(hole, JSON.parse(JSON.stringify(mask)))
  show(masked)
}

const initMap = () => {
  const baseLayer = new TileLayer({
    source: new XYZ({
      url: 'http://wprd0{1-4}.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=en&size=1&scl=1&style=7'
    })
  })

  const vectorLayer = new VectorLayer({
    source: turfSource,
    style: [
      new Style({
        fill: new Fill({ color: 'rgba(255,0,0,0.2)' }),
        stroke: new Stroke({ width: 2, color: 'blue' })
      }),
      new Style({
        image: new RegularShape({
          radius: 6,
          points: 4,
          rotation: Math.PI / 4,
          fill: new Fill({ color: 'white' }),
          stroke: new Stroke({ color: 'red', width: 2 })
        }),
        geometry: function (feature) {
          const coordinates = feature.getGeometry().getCoordinates()[0]
          return new MultiPoint(coordinates)
        }
      })
    ]
  })

  map.value = new Map({
    target: mapContainer.value,
    layers: [baseLayer, vectorLayer],
    view: new View({
      projection: 'EPSG:3857',
      center: fromLonLat([146, -39]),
      zoom: 2
    })
  })
}

onMounted(() => {
  initMap()
})
</script>

<style scoped>
.container {
  width: 840px;
  height: 570px;
  margin: 50px auto;
  border: 1px solid #42B983;
}

#vue-openlayers {
  width: 800px;
  height: 400px;
  margin: 0 auto;
  border: 1px solid #42B983;
  position: relative;
}
</style>

🧭适用场景

  • 土地规划中的“保留区挖空”

  • 可视区域剔除遮罩高亮

  • 城市管理中对敏感区域进行裁剪遮罩

  • GIS 应用中实现地理图层 Mask 操作


✍️结语

本篇展示了如何通过 OpenLayers 与 Turf.js 组合实现地理遮罩布打洞功能。遮罩效果不仅直观,也非常适合拓展复杂图层控制、互动分析等 GIS 需求。

如果你觉得文章对你有帮助,欢迎点赞、评论、收藏!
你也可以关注我,查看更多 Vue3 + OpenLayers + Turf + Cesium 的三维地理可视化实战内容!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吉檀迦俐

你的鼓励奖是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值