openlayers 绘图功能,编辑多边形,select,snap组件的使用(六)

本篇介绍一下vue3-openlayers的select,snap的使用

1 需求

  • 点击开始绘制按钮开始绘制多边形,可以连续绘制多个多边形
  • 点击撤销上步按钮,撤销上一步绘制点
  • 绘制多个多边形(或编辑多边形时),鼠标靠近之前的已绘制完的多边形顶点时,自动吸附
  • 点击结束绘制按钮,绘制完成,点击高亮选中多边形,开启编辑模式,显示顶点(调节点)进行编辑

2 分析

主要是vue3-openlayers 中 draw,select,snap,modify 功能的使用

3 实现

3.1 简单实现

<template>
  <ol-map
    :loadTilesWhileAnimating="true"
    :loadTilesWhileInteracting="true"
    style="width: 100%; height: 100%"
    ref="mapRef"
  >
    <ol-view
      ref="view"
      :center="center"
      :rotation="rotation"
      :zoom="zoom"
      :projection="projection"
    />
    <ol-vector-layer>
      <ol-source-vector :projection="projection" :wrapX="false">
        <ol-interaction-draw
          ref="drawRef"
          :type="'Polygon'"
          :source="source"
          @drawend="drawend"
          @drawstart="drawstart"
        >
          <ol-style :overrideStyleFunction="handleStyleFunction"> </ol-style>
        </ol-interaction-draw>
        <ol-interaction-modify
          ref="modifyRef"
					v-if="modifyFlag"
          :features="selectedFeatures"
          :pixelTolerance="10"
          :insertVertexCondition="handleInsertVertexCondition"
          @modifyend="handleModifyEnd"
        >
          <ol-style :overrideStyleFunction="handleModifyStyleFunction"> </ol-style>
        </ol-interaction-modify>
        <ol-interaction-snap :edge="false" />
      </ol-source-vector>
      <ol-style :overrideStyleFunction="styleFunction"> </ol-style>
    </ol-vector-layer>
    <ol-interaction-select ref="selectRef"  :features="selectedFeatures" @select="handleSelect" :condition="selectCondition">
      <ol-style :overrideStyleFunction="selectStyleFunc"> </ol-style>
    </ol-interaction-select>
  </ol-map>
  <div class="toolbar">
    <el-button type="primary" @click="handleClick">{{ drawFlag ? '结束' : '开始' }}绘制</el-button>
    <el-button type="warning" :disabled="!drawFlag" @click="handleCancelClick">撤销上步</el-button>
  </div>
</template>

<script setup lang="ts">
import { Collection } from 'ol';
import { FeatureLike } from 'ol/Feature';
import { LineString, Point } from 'ol/geom';
import { DrawEvent } from 'ol/interaction/Draw';
import { Circle, Fill, Stroke, Style } from 'ol/style';
const center = ref([121, 31]);
const projection = ref('EPSG:4326');
const zoom = ref(5);
const rotation = ref(0);
const features = ref(new Collection()); //保存绘制的features
const selectedFeatures = ref(new Collection()); //保存绘制的features
const source = ref([]);
const mapRef = ref();
const drawRef = ref();
const selectRef = ref();
const modifyRef = ref();
const drawFlag = ref(false);
const modifyFlag = ref(true);
const selectConditions = inject('ol-selectconditions');

const selectCondition = selectConditions.click;

onMounted(() => {
  drawRef.value.draw.setActive(false);
  modifyRef.value.modify.setActive(false);
});

const drawstart = (event: Event) => {
  console.log(event);
};

const drawend = (event: DrawEvent) => {
  console.log(event.feature.getGeometry());
};

const handleClick = () => {
  drawFlag.value = !drawFlag.value;
  drawRef.value.draw.setActive(drawFlag.value);
	// modifyFlag.value=!drawFlag.value;
  modifyRef.value.modify.setActive(!drawFlag.value);
  selectRef.value.select.setActive(!drawFlag.value);
  selectRef.value.select.getFeatures().clear();
};
const handleCancelClick = () => {
  drawRef.value.draw.removeLastPoint();
};

const handleSelect = e => {
	modifyRef.value.modify.setActive(false);
	// modifyFlag.value=false;
  if (!e.selected.length) {
    selectedFeatures.value.clear();
    selectRef.value.select.getFeatures().clear();
  } else {
		nextTick(()=>{
			// modifyFlag.value=true;
			modifyRef.value?.modify.setActive(true);
			selectedFeatures.value=e.target.getFeatures();
		})
  }
};


const handleStyleFunction = (feature: FeatureLike, currentStyle: Style) => {
  const geometry = feature.getGeometry();
  const coord = geometry.getCoordinates();
  const type = geometry.getType();
  const styles: Array<Style> = [];
  if (type === 'LineString') {
    for (let i = 0; i < coord.length - 1; i++) {
      styles.push(
        new Style({
          geometry: new LineString([coord[i], coord[i + 1]]),
          stroke: new Stroke({
            color: 'orange',
            lineDash: coord.length > 2 && i < coord.length - 2 ? [] : [10],
            width: 2
          })
        })
      );
    }
  }
  return styles;
};

const handleInsertVertexCondition = e => {
  return false;
};

const handleModifyStyleFunction = (feature: FeatureLike, currentStyle: Style) => {
  const coord = feature.getGeometry().getCoordinates();
  const layer = mapRef.value.map.getLayers().item(0);
  const features = layer.getSource().getFeatures();
  const coords = features.map(feature => feature.getGeometry().getCoordinates()).flat(2);
  let style = undefined;
  // 只有鼠标在顶点时才能触发编辑功能
  if (coords.find(c => c.toString() === coord.toString())) {
    style = new Style({
      geometry: new Point(coord),
      image: new Circle({
        radius: 6,
        fill: new Fill({
          color: '#ffff'
        }),
        stroke: new Stroke({
          color: 'red',
          width: 2
        })
      })
    });
  }

  return style;
};

const handleModifyEnd = e => {
  features.value.push(e.feature); //这里可以把编辑后的feature添加到layer绑定的features中
  console.log('modifyend', e.features);
};

const styleFunction = (feature: FeatureLike, currentStyle: Style) => {
  const styles = [];
  styles.push(
    new Style({
      fill: new Fill({
        color: [128, 128, 255, 0.5]
      }),
      stroke: new Stroke({
        color: 'blue',
        width: 2
      })
    })
  );
  return styles;
};

const selectStyleFunc = feature => {
  const styles = [];
  const coord = feature.getGeometry().getCoordinates().flat(1);
  for (let i = 0; i < coord.length - 1; i++) {
    styles.push(
      new Style({
        geometry: new Point(coord[i]),
        image: new Circle({
          radius: 4,
          fill: new Fill({
            color: '#ffff'
          }),
          stroke: new Stroke({
            color: 'orange',
            width: 2
          })
        })
      })
    );
  }
  styles.push(
    new Style({
      stroke: new Stroke({
        color: 'orange',
        width: 2
      }),
      fill: new Fill({
        color: '#ffff'
      })
    })
  );
  return styles;
};
</script>
<style scoped lang="scss">
.toolbar {
  position: absolute;
  top: 20px;
  left: 100px;
}
</style>


存在问题(vue3-openlayers官网modify示例也存在这两个个问题)

  • 当绘制多个多边形时,只要一个多边形被编辑过,当编辑其他多边形时,尽管高亮选中的是当前多边形,但是之前编辑过的多边形也可以被编辑
  • 当两个多边形顶点重叠时,无法再分开,会导致同时编辑两个多边形

分析
modify组件没有仅仅使用当前选中的多边形,而是把select过的都进入编辑状态(其实原生写法也有这个问题,试着把modify传入的features参数绑定值从select.value.getFeatures()改为一个ref(new Collection()),在select组件的select事件中把选中的feature压入这个ref,既可以复现上面两个问题)

解决
modify添加v-if,强制重新渲染(会导致snap不会吸附,个人觉得可以接受)

3.2 重新实现

<template>
  <ol-map
    :loadTilesWhileAnimating="true"
    :loadTilesWhileInteracting="true"
    style="width: 100%; height: 100%"
    ref="mapRef"
  >
    <ol-view
      ref="view"
      :center="center"
      :rotation="rotation"
      :zoom="zoom"
      :projection="projection"
    />
    <ol-vector-layer>
      <ol-source-vector :projection="projection" :wrapX="false">
        <ol-interaction-draw
          ref="drawRef"
          :type="'Polygon'"
          :source="source"
          @drawend="drawend"
          @drawstart="drawstart"
        >
          <ol-style :overrideStyleFunction="handleStyleFunction"> </ol-style>
        </ol-interaction-draw>
        <ol-interaction-modify
          ref="modifyRef"
					v-if="modifyFlag"
          :features="selectedFeatures"
          :pixelTolerance="10"
          :insertVertexCondition="handleInsertVertexCondition"
          @modifyend="handleModifyEnd"
        >
          <ol-style :overrideStyleFunction="handleModifyStyleFunction"> </ol-style>
        </ol-interaction-modify>
        <ol-interaction-snap :edge="false" />
      </ol-source-vector>
      <ol-style :overrideStyleFunction="styleFunction"> </ol-style>
    </ol-vector-layer>
    <ol-interaction-select ref="selectRef"  :features="selectedFeatures" @select="handleSelect" :condition="selectCondition">
      <ol-style :overrideStyleFunction="selectStyleFunc"> </ol-style>
    </ol-interaction-select>
  </ol-map>
  <div class="toolbar">
    <el-button type="primary" @click="handleClick">{{ drawFlag ? '结束' : '开始' }}绘制</el-button>
    <el-button type="warning" :disabled="!drawFlag" @click="handleCancelClick">撤销上步</el-button>
  </div>
</template>

<script setup lang="ts">
import { Collection } from 'ol';
import { FeatureLike } from 'ol/Feature';
import { LineString, Point } from 'ol/geom';
import { DrawEvent } from 'ol/interaction/Draw';
import { Circle, Fill, Stroke, Style } from 'ol/style';
const center = ref([121, 31]);
const projection = ref('EPSG:4326');
const zoom = ref(5);
const rotation = ref(0);
const features = ref(new Collection()); //保存绘制的features
const selectedFeatures = ref(new Collection()); //保存绘制的features
const source = ref([]);
const mapRef = ref();
const drawRef = ref();
const selectRef = ref();
const modifyRef = ref();
const drawFlag = ref(false);
const modifyFlag = ref(false);
const selectConditions = inject('ol-selectconditions');

const selectCondition = selectConditions.click;

onMounted(() => {
  drawRef.value.draw.setActive(false);
  // modifyRef.value.modify.setActive(false);
});

const drawstart = (event: Event) => {
  console.log(event);
};

const drawend = (event: DrawEvent) => {
  console.log(event.feature.getGeometry());
};

const handleClick = () => {
  drawFlag.value = !drawFlag.value;
  drawRef.value.draw.setActive(drawFlag.value);
	modifyFlag.value=!drawFlag.value;
  // modifyRef.value.modify.setActive(!drawFlag.value);
  selectRef.value.select.setActive(!drawFlag.value);
  selectRef.value.select.getFeatures().clear();
};
const handleCancelClick = () => {
  drawRef.value.draw.removeLastPoint();
};

const handleSelect = e => {
	// modifyRef.value.modify.setActive(false);
	modifyFlag.value=false;
  if (!e.selected.length) {
    selectedFeatures.value.clear();
    selectRef.value.select.getFeatures().clear();
  } else {
		nextTick(()=>{
			modifyFlag.value=true;
			// modifyRef.value?.modify.setActive(true);
			selectedFeatures.value=e.target.getFeatures();
		})
  }
};


const handleStyleFunction = (feature: FeatureLike, currentStyle: Style) => {
  const geometry = feature.getGeometry();
  const coord = geometry.getCoordinates();
  const type = geometry.getType();
  const styles: Array<Style> = [];
  if (type === 'LineString') {
    for (let i = 0; i < coord.length - 1; i++) {
      styles.push(
        new Style({
          geometry: new LineString([coord[i], coord[i + 1]]),
          stroke: new Stroke({
            color: 'orange',
            lineDash: coord.length > 2 && i < coord.length - 2 ? [] : [10],
            width: 2
          })
        })
      );
    }
  }
  return styles;
};

const handleInsertVertexCondition = e => {
  return false;
};

const handleModifyStyleFunction = (feature: FeatureLike, currentStyle: Style) => {
  const coord = feature.getGeometry().getCoordinates();
  const layer = mapRef.value.map.getLayers().item(0);
  const features = layer.getSource().getFeatures();
  const coords = features.map(feature => feature.getGeometry().getCoordinates()).flat(2);
  let style = undefined;
  // 只有鼠标在顶点时才能触发编辑功能
  if (coords.find(c => c.toString() === coord.toString())) {
    style = new Style({
      geometry: new Point(coord),
      image: new Circle({
        radius: 6,
        fill: new Fill({
          color: '#ffff'
        }),
        stroke: new Stroke({
          color: 'red',
          width: 2
        })
      })
    });
  }

  return style;
};

const handleModifyEnd = e => {
  features.value.push(e.feature); //这里可以把编辑后的feature添加到layer绑定的features中
  console.log('modifyend', e.features);
};

const styleFunction = (feature: FeatureLike, currentStyle: Style) => {
  const styles = [];
  styles.push(
    new Style({
      fill: new Fill({
        color: [128, 128, 255, 0.5]
      }),
      stroke: new Stroke({
        color: 'blue',
        width: 2
      })
    })
  );
  return styles;
};

const selectStyleFunc = feature => {
  const styles = [];
  const coord = feature.getGeometry().getCoordinates().flat(1);
  for (let i = 0; i < coord.length - 1; i++) {
    styles.push(
      new Style({
        geometry: new Point(coord[i]),
        image: new Circle({
          radius: 4,
          fill: new Fill({
            color: '#ffff'
          }),
          stroke: new Stroke({
            color: 'orange',
            width: 2
          })
        })
      })
    );
  }
  styles.push(
    new Style({
      stroke: new Stroke({
        color: 'orange',
        width: 2
      }),
      fill: new Fill({
        color: '#ffff'
      })
    })
  );
  return styles;
};
</script>
<style scoped lang="scss">
.toolbar {
  position: absolute;
  top: 20px;
  left: 100px;
}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/705617.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

代码解读 | Hybrid Transformers for Music Source Separation[05]

一、背景 0、Hybrid Transformer 论文解读 1、代码复现|Demucs Music Source Separation_demucs架构原理-CSDN博客 2、Hybrid Transformer 各个模块对应的代码具体在工程的哪个地方 3、Hybrid Transformer 各个模块的底层到底是个啥&#xff08;初步感受&#xff09;&#xff1…

Tomcat部署以及虚拟主机

概论 Tomcat 是 Java 语言开发的&#xff0c;Tomcat 服务器是一个免费的开放源代码的 Web 应用服务器&#xff0c;是 Apache 软件基金会的 Jakarta 项目中的一个核心项目&#xff0c;由 Apache、Sun 和其他一些公司及个人共同开发而成。 Tomcat的组成 Tomcat 由一系列的组件构…

黑苹果睡眠总是自动唤醒(RTC)

黑苹果睡眠总是自动唤醒【RTC】 1. 问题2. 解决方案2.1. 查看重启日志2.2. 配置Disable RTC wake scheduling补丁 3. 后续4. 参考 1. 问题 黑苹果EFI 更换后&#xff0c;总是在手动 睡眠后&#xff0c;间歇性重启&#xff0c;然后再次睡眠&#xff0c;然后再重启。原因归结为&…

界面控件DevExpress WinForms垂直属性网格组件 - 拥有更灵活的UI选择(一)

DevExpress WinForms垂直&属性网格组件旨在提供UI灵活性&#xff0c;它允许用户显示数据集中的单个行或在其90度倒置网格容器中显示多行数据集。另外&#xff0c;用户可以把它用作一个属性网格&#xff0c;就像在Visual Studio IDE中那样。 P.S&#xff1a;DevExpress Win…

【软件测试】遇到bug怎么分析,这篇文章值得一看

为什么定位问题如此重要&#xff1f; 可以明确一个问题是不是真的“bug” 很多时候&#xff0c;我们找到了问题的原因&#xff0c;结果发现这根本不是bug。原因明确&#xff0c;误报就会降低 多个系统交互&#xff0c;可以明确指出是哪个系统的缺陷&#xff0c;防止“踢皮球…

OneNet创建产品和设备

onenet平台网址 https://open.iot.10086.cn/console/device/manage/devs?pidn5Yw89el5t 产品创建二号设备创建在下文中具有详细讲解 选择设备管理后&#xff0c;点击蓝色的添加设备按钮来添加设备 点击添加设备后&#xff0c;进入如下界面。设备所属产品和设备名称如下图设置…

RK3568技术笔记 Ubuntu 安装VMware Tools

安装 VMware Tools 后可以直接使用复制粘贴功能拷贝 Ubuntu 系统和 windows 主机内的文件&#xff0c;非常方便。 开启虚拟机&#xff0c;必须要进入ubuntu系统后才能进行下面的步骤。 单击 VMware 软件中的标签“虚拟机”&#xff0c;在下拉的菜单中单击“安装VMware Tools &…

技术革新,智绘未来丨悦数图数据库 v5.0 重磅亮相 WAIC 2024

本次 WAIC&#xff08;世界人工智能大会&#xff09;2024 将于7 月 4 日- 7 日在上海世博展览馆**举行&#xff0c;本次 WAIC 2024 围绕“以共商促共享 以善治促善智”为主题&#xff0c;杭州悦数科技有限公司将携最新的悦数图数据库 v5.0 亮相 E805 展位。作为国内领先的图数据…

使用GPT/文心实现诗词作画

在教育领域中&#xff0c;古诗词一直是培养学生文化素养和审美能力的重要载体。选择合适的古诗词进行学习和欣赏&#xff0c;不仅能够增强他们的语言表达能力&#xff0c;还能促进他们对中国传统文化的理解和热爱。本文将结合AI技术&#xff0c;将古诗词转换为图画。 1、选择适…

WWDC 2024 回顾:Apple Intelligence 的发布与解析

一年一度的苹果全球开发者大会&#xff08;WWDC&#xff09;如期而至&#xff0c;2024 年的 WWDC 再次成为科技界的焦点。本次发布会中&#xff0c;苹果正式推出了他们在 AI 领域的全新战略——Apple Intelligence。这一全新概念旨在为用户打造“强大、易用、全面、个性化、注重…

setOptMode -holdTargetSlack与-holdSlackFixingThreshod

我正在「拾陆楼」和朋友们讨论有趣的话题&#xff0c;你⼀起来吧&#xff1f; 拾陆楼知识星球入口 -holdTargetSlack与-holdSlackFixingThreshod这两个option都是针对hold slack的&#xff0c;前者限制slack的目标&#xff0c;默认是0&#xff0c;也就是说工具尽可能会收敛时序…

查分易怎么生成二维码

现在&#xff0c;家长和学生对于成绩查询的需求不断增长。教给各位新手教师一个简单又高效的查询工具——查分易小程序。可以为繁杂的工作做减法&#xff0c;也让学生和家长随时查看自己的学习情况。 查分易因为安全、便捷、高效&#xff0c;成为了众多学校和老师的首选。能够快…

【云服务器介绍】选择指南 腾讯云 阿里云全配置对比 搭建web 个人开发 app 游戏服务器

​省流目录&#xff1a;适用于博客建站&#xff08;2-4G&#xff09;、个人开发/小型游戏[传奇/我的世界/饥荒]&#xff08;4-8G&#xff09;、数据分析/大型游戏[幻兽帕鲁/雾锁王国]服务器&#xff08;16-64G&#xff09; 1.京东云-618专属活动 官方采购季专属活动地址&#x…

Python写UI自动化--playwright(元素定位)

本篇详细分享playwright如何进行打断点、元素定位、填写输入框、点击等操作 目录 一、PyCharm打断点进行调试 二、浏览器开发者模式检查元素 三、通过CSS或XPath进行定位 四、输入框输入文本操作 五、点击操作 总结 一、PyCharm打断点进行调试 如图所示&#xff0c;我们…

深度学习之激活函数

激活函数&#xff08;Activation Function&#xff09;是一种添加到人工神经网络中的函数&#xff0c;旨在帮助网络学习数据中的复杂模式。在神经元中&#xff0c;输入的input经过一系列加权求和后作用于另一个函数&#xff0c;这个函数就是这里的激活函数。 1. 为什么需要激活…

雷神电脑怎么找文件所在位置?四个方法让你轻松上手

在数字化时代&#xff0c;电脑文件的管理与存储显得尤为重要。对于使用雷神电脑的用户而言&#xff0c;了解如何快速定位文件所在位置&#xff0c;以及在文件丢失时采取有效的应对措施&#xff0c;是提升工作效率、保障数据安全的关键。本文将围绕这两个核心问题展开&#xff0…

怎么更快捷的修改图片大小?压缩图片jpg、png、gif的快捷方法

jpg作为最常用的一种图片格式&#xff0c;在遇到图片太大问题时&#xff0c;该如何操作能够快速在压缩图片jpg的大小呢&#xff1f;图片太大无法上传时目前常见的一个使用问题&#xff0c;只有将图片处理到合适的大小才可以正常在平台上传使用&#xff0c;一般情况下想要快速解…

Android帧绘制流程深度解析 (二)

书接上回&#xff1a;Android帧绘制流程深度解析 &#xff08;一&#xff09; 5、 dispatchVsync&#xff1a; 在请求Vsync以后&#xff0c;choreographer会等待Vsync的到来&#xff0c;在Vsync信号到来后&#xff0c;会触发dispatchVsync函数&#xff0c;从而调用onVsync方法…

自监督分类网络:创新的端到端学习方法

现代人工智能的快速发展中&#xff0c;分类任务的高效解决方案一直备受关注。今天&#xff0c;我们向大家介绍一种名为Self-Classifier的全新自监督端到端分类学习方法。由Elad Amrani、Leonid Karlinsky和Alex Bronstein团队开发&#xff0c;Self-Classifier通过优化同一样本的…