40 openlayers setCenter 之后 绘制了Overlay 地图定位异常

2024-03-24 06:44

本文主要是介绍40 openlayers setCenter 之后 绘制了Overlay 地图定位异常,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

这是之前在 生产环境碰到的一个问题

这个其实就是 业务上一个地图点击点位展示详情, 然后再点击另外一个点位 展示详情, 切换中心店的这个过程 

其主要的问题是 使用 openlayers 的 Map.View.setCenter() 了之后, 整个地图的中心点切换到了一个莫名其妙的地方 

然后 经过大量的事件排查, 我们发现 在我们复现问题的操作的过程中总共有两个地方在进行中心点的切换 

第一个是我们业务上的 map.getView().setCenter(), 然后另外一个是 openlayers 的 Overlay 元素的重新绘制 的过程中的一个中心点的切换

然后 正常的业务情况是 先执行 Overlay 的相关业务绘制, 然后再执行 map.getView().setCenter()

然后 在我们这里测试出了某一个情况下 业务代码这边先执行的 map.getView().setCenter() 然后再执行的 Overlay 的相关业务绘制, 然后导致 地图上面看不到数据, 产生了一个 bug, 就是 我点击了目标点位, 期望地图上面能够展示这个点, 以及这个点位的详情信息, 但是实际情况是 地图上面展示的是空的

我们这里 便是来看一下 这个问题 

测试用例

<template><div style="width: 1920px; height:1080px;" ><div class="olMapUsageClass"></div><div class="overdelay1" ref="overdelay1" >this is over delay1</div></div></template><script>import Map from 'ol/Map'import View from 'ol/View'import DragPan from 'ol/interaction/DragPan'import MouseWheelZoom from 'ol/interaction/MouseWheelZoom'import PointerInteraction from 'ol/interaction/Pointer'import GeoJSON from 'ol/format/GeoJSON'import {Tile as TileLayer} from 'ol/layer'import {Vector as VectorLayer} from 'ol/layer'import {Image as ImageLayer} from 'ol/layer'import {Vector as VectorSource} from 'ol/source'import {Feature as Feature} from 'ol'import Point from 'ol/geom/Point'import LineString from 'ol/geom/LineString'import Polygon from 'ol/geom/Polygon'import CircleGeo from 'ol/geom/Circle'import XYZ from "ol/source/XYZ"import ImageStatic from "ol/source/ImageStatic"import {Circle, Fill, Icon, RegularShape, Stroke, Style, Text} from 'ol/style'import Overlay from 'ol/Overlay'import {transformExtent, transform} from "ol/proj"export default {name: "olMapUsage",components: {},props: {},data() {return {map: null,tdtImgLayer: null,labelLayer: null,overlay: null,};},computed: {},watch: {},created() {},mounted() {this.initMap()this.test12SetCenterThenAddOverlay()},methods: {initMap() {let center = [105.065735, 30.659462]let projection = "EPSG:4326"let zoom = 10let minZoom = 5let maxZoom = 20const layer = []const view = new View({...(this.viewOptions || {}),projection,center,zoom,minZoom,maxZoom})this.map = new Map({...(this.mapOptions || {}),layers: [].concat(layer),view: view,target: this.$el,controls: [],interactions: [new DragPan(),new MouseWheelZoom(),new PointerInteraction({handleEvent: this.handleEvent})]})},test06AddCircleLayer(coord, color) {color = color || 'green'let style = new Style({image: new Circle({radius:20,fill: new Fill({color: color})})})let feature = new Feature({geometry: new Point(coord)})feature.setStyle(style)let source = new VectorSource()source.addFeature(feature)let layer = new VectorLayer({source: source})this.map.addLayer(layer);},test10SetCenter(coord, color) {this.map.getView().setCenter(coord)this.test06AddCircleLayer(coord, color)},test11AddOverlay(coord) {this.overlay && this.map.removeOverlay(this.overlay)this.overlay = new Overlay({element: this.$refs.overdelay1,position: coord,positioning: "bottom-center",offset: [0, 0],autoPan: true,autoPanMargin: 200,autoPanAnimation: {duration: 1000},map: this.map})this.map.addOverlay(this.overlay)},test12SetCenterThenAddOverlay() {// refer cyclethis.test06AddCircleLayer([10.265735, 10.659462], "#007f5a")this.test06AddCircleLayer([105.565735, 30.759462], "#0039ff")let _this = this// use this for map.addOverlay's animation updatesetTimeout(function() {_this.test11AddOverlay([10.065735, 10.459462])_this.test10SetCenter([10.065735, 10.459462], "yellow")}, 2000)// the core case, normal or exception or compensatedsetTimeout(function() {// case1. function of addOverlay_this.test11AddOverlay([105.065735, 30.259462])// case2. normal case// _this.test11AddOverlay([105.065735, 30.259462])// _this.test10SetCenter([105.065735, 30.259462], "red")// case3. exception case// _this.test10SetCenter([105.065735, 30.259462], "red")// _this.test11AddOverlay([105.065735, 30.259462])// case4. compensated case// _this.test10SetCenter([105.065735, 30.259462], "red")// setTimeout(function() {//   _this.test11AddOverlay([105.065735, 30.259462])// }, 1000)}, 5000)},}};
</script><style lang="scss">.olMapUsageClass {}.overdelay1 {position: absolute;border: 1px greenyellow solid;width: 200px;height: 50px;}
</style>

map.addOverlay 的绘制和计算 

这里使用 上面的 case1 的相关代码 来查看

整个测试用例分为了三次状态, 第一个是初始状态, 第二个是 切换到 [10.065735, 10.459462] 附近, 第三个是切换到 [105.065735, 30.259462] 附近 

我们这里 着重关注 第三次切换

我们上面配置的 Overlay 的 autopan 相关配置为 margin 为 200, duration 为 1s, 我们这里是从做下切换到右上, 因此 overlay1 是距离右上 200px, 然后整个切换的耗时为 1s

注意这里 Overlay 的 autopan 切换的中心点不是将 Overlay 放在正中间, 只需要满足 autopanMargin 的配置即可 

我们可以将 autopanMargin 配置调整下, 再看下一个大概的情况, autoPan 只需要确保给定的元素在页面上能够找到即可 

然后 autopan 目标中心点计算方式如下 

这里 newCenterPx 的计算涉及到两个变量, 其一是 center, 即 map.getView().getCenter 为当前的 [10.065735, 10.459462]

另外一个 delta 计算结果为 [68617, -14213]

delta 的计算依赖于 overlayRect, 值为 [70137, -13923, 70337, -13873]

然后最终 计算出来新的中心点为 [104.29, 29.85]

保证的是 center 切换到新的中心点的时候 可以看到 [105.065735, 30.259462] 的 Overlay

正常绘制 以及 切换中心点的情况

这里切换到 case2 来执行 

会先执行 Overlay 然后再执行 map.getView().setCenter

Overlay 的 autopan 这个阶段, 可以看到和上面是一样的, 然后设置中心点为 [104.29, 29.85], 以及动画切换 

然后接着是 setCenter 的处理, 设置中心点为 [105.065735, 30.259462] 然后渲染 

这样最终的效果如下, center 为 点位的位置, 以及 overlay所在的位置 

异常绘制 以及 切换中心点的情况

这里切换到 case3 来执行 

会先执行 map.getView().setCenter 然后再 执行 Overlay

map.getView().setCenter

map.getView().setCenter 之后, 设置了 map 的中心点的字段为 [105.065735, 30.259462]

然后接下来的 applyTargetState 才会开始真实的数据的渲染, 切换 

Overlay 的 autopan 这个阶段

然后 autopan 目标中心点计算方式如下 

这里 newCenterPx 的计算涉及到两个变量, 其一是 center, 即 map.getView().getCenter 为当前的 [105.065735, 30.259462]

另外一个 delta 计算结果为 [68617, -14213]

delta 的计算依赖于 overlayRect, 值为 [70137, -13923, 70337, -13873]

可以看到 和上面正常的 Overlay 加载的时候会存在一些问题 

第一 center 变化了, 拿到的 overlayRect 还是 [10.065735, 10.459462] 状态下的 overlayRect, 然后计算出的偏移也是 [10.065735, 10.459462] 相比于 [105.065735, 30.259462] 的偏移 

然后 导致了计算中心点存在问题, 最终 autopan 切换到目标中心点之后, 切换到了一个错误的中心点, 导致页面看不到数据 

然后 我们大致推导一下 这个新的中心点

从上面 map.addOverlay 中我们可以看到 delta 对应的经纬度偏移大致为 [104.29-10.06, 29.85-10.45], 即为 [94.23, 19.4]

然后 大致的新的中心点位为 [105.065735+94.23, 30.259462+19.4] 为 [199.29, 49.65]

可以看到 和我们的推导一样

综上 核心问题就是, map.getView().setCenter 之后先切换了 map.view 的中心点的数据 

但是 View.applyTargetState 的过程是异步的 

Overlay 的Overlay.panIntoView 中 this.getRect(element, [outerWidth(element),outerHeight(element),]); 获取到的元素状态为 中心点为 [10.065735, 10.459462] 的状态下面的元素状态

然后一个是 最新的中心点位, 一个是基于 旧的中心点位元素状态计算的偏移, 然后 最终得到了一个 错误的结果

如何先切换中心点 然后再绘制 Overlay

然后 这里的一个 曲线救国的调整方式就是, 在 setCenter 渲染结束了之后, 在添加 Overlay, 调整的方式如下 

// case4. compensated case
_this.test10SetCenter([105.065735, 30.259462], "red")
setTimeout(function() {_this.test11AddOverlay([105.065735, 30.259462])
}, 1000)

然后 我们这里看一下 我们这里的 Overlay 的添加情况, 这会是 另外的一个状态

切换了中心点之后, 然后再执行 test11AddOverlay 的时候, 会发现 Overlay 的坐标在当前上下文的区域内, 不用进行 autopan, 直接跳过了 autopan 的流程 

完 

这篇关于40 openlayers setCenter 之后 绘制了Overlay 地图定位异常的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/840761

相关文章

Java Optional避免空指针异常的实现

《JavaOptional避免空指针异常的实现》空指针异常一直是困扰开发者的常见问题之一,本文主要介绍了JavaOptional避免空指针异常的实现,帮助开发者编写更健壮、可读性更高的代码,减少因... 目录一、Optional 概述二、Optional 的创建三、Optional 的常用方法四、Optio

如何解决mmcv无法安装或安装之后报错问题

《如何解决mmcv无法安装或安装之后报错问题》:本文主要介绍如何解决mmcv无法安装或安装之后报错问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录mmcv无法安装或安装之后报错问题1.当我们运行YOwww.chinasem.cnLO时遇到2.找到下图所示这里3.

Java异常架构Exception(异常)详解

《Java异常架构Exception(异常)详解》:本文主要介绍Java异常架构Exception(异常),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. Exception 类的概述Exception的分类2. 受检异常(Checked Exception)

Java报NoClassDefFoundError异常的原因及解决

《Java报NoClassDefFoundError异常的原因及解决》在Java开发过程中,java.lang.NoClassDefFoundError是一个令人头疼的运行时错误,本文将深入探讨这一问... 目录一、问题分析二、报错原因三、解决思路四、常见场景及原因五、深入解决思路六、预http://www

一文带你深入了解Python中的GeneratorExit异常处理

《一文带你深入了解Python中的GeneratorExit异常处理》GeneratorExit是Python内置的异常,当生成器或协程被强制关闭时,Python解释器会向其发送这个异常,下面我们来看... 目录GeneratorExit:协程世界的死亡通知书什么是GeneratorExit实际中的问题案例

使用Folium在Python中进行地图可视化的操作指南

《使用Folium在Python中进行地图可视化的操作指南》在数据分析和可视化领域,地图可视化是一项非常重要的技能,它能够帮助我们更直观地理解和展示地理空间数据,Folium是一个基于Python的地... 目录引言一、Folium简介与安装1. Folium简介2. 安装Folium二、基础使用1. 创建

Java捕获ThreadPoolExecutor内部线程异常的四种方法

《Java捕获ThreadPoolExecutor内部线程异常的四种方法》这篇文章主要为大家详细介绍了Java捕获ThreadPoolExecutor内部线程异常的四种方法,文中的示例代码讲解详细,感... 目录方案 1方案 2方案 3方案 4结论方案 1使用 execute + try-catch 记录

解决java.lang.NullPointerException问题(空指针异常)

《解决java.lang.NullPointerException问题(空指针异常)》本文详细介绍了Java中的NullPointerException异常及其常见原因,包括对象引用为null、数组元... 目录Java.lang.NullPointerException(空指针异常)NullPointer

Spring Boot统一异常拦截实践指南(最新推荐)

《SpringBoot统一异常拦截实践指南(最新推荐)》本文介绍了SpringBoot中统一异常处理的重要性及实现方案,包括使用`@ControllerAdvice`和`@ExceptionHand... 目录Spring Boot统一异常拦截实践指南一、为什么需要统一异常处理二、核心实现方案1. 基础组件

使用Python绘制蛇年春节祝福艺术图

《使用Python绘制蛇年春节祝福艺术图》:本文主要介绍如何使用Python的Matplotlib库绘制一幅富有创意的“蛇年有福”艺术图,这幅图结合了数字,蛇形,花朵等装饰,需要的可以参考下... 目录1. 绘图的基本概念2. 准备工作3. 实现代码解析3.1 设置绘图画布3.2 绘制数字“2025”3.3