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

相关文章

Debian 13升级后网络转发等功能异常怎么办? 并非错误而是管理机制变更

《Debian13升级后网络转发等功能异常怎么办?并非错误而是管理机制变更》很多朋友反馈,更新到Debian13后网络转发等功能异常,这并非BUG而是Debian13Trixie调整... 日前 Debian 13 Trixie 发布后已经有众多网友升级到新版本,只不过升级后发现某些功能存在异常,例如网络转

C#文件复制异常:"未能找到文件"的解决方案与预防措施

《C#文件复制异常:未能找到文件的解决方案与预防措施》在C#开发中,文件操作是基础中的基础,但有时最基础的File.Copy()方法也会抛出令人困惑的异常,当targetFilePath设置为D:2... 目录一个看似简单的文件操作问题问题重现与错误分析错误代码示例错误信息根本原因分析全面解决方案1. 确保

Java利用@SneakyThrows注解提升异常处理效率详解

《Java利用@SneakyThrows注解提升异常处理效率详解》这篇文章将深度剖析@SneakyThrows的原理,用法,适用场景以及隐藏的陷阱,看看它如何让Java异常处理效率飙升50%,感兴趣的... 目录前言一、检查型异常的“诅咒”:为什么Java开发者讨厌它1.1 检查型异常的痛点1.2 为什么说

Java异常捕获及处理方式详解

《Java异常捕获及处理方式详解》异常处理是Java编程中非常重要的一部分,它允许我们在程序运行时捕获并处理错误或不预期的行为,而不是让程序直接崩溃,本文将介绍Java中如何捕获异常,以及常用的异常处... 目录前言什么是异常?Java异常的基本语法解释:1. 捕获异常并处理示例1:捕获并处理单个异常解释:

Python利用GeoPandas打造一个交互式中国地图选择器

《Python利用GeoPandas打造一个交互式中国地图选择器》在数据分析和可视化领域,地图是展示地理信息的强大工具,被将使用Python、wxPython和GeoPandas构建的交互式中国地图行... 目录技术栈概览代码结构分析1. __init__ 方法:初始化与状态管理2. init_ui 方法:

Python自定义异常的全面指南(入门到实践)

《Python自定义异常的全面指南(入门到实践)》想象你正在开发一个银行系统,用户转账时余额不足,如果直接抛出ValueError,调用方很难区分是金额格式错误还是余额不足,这正是Python自定义异... 目录引言:为什么需要自定义异常一、异常基础:先搞懂python的异常体系1.1 异常是什么?1.2

Java.lang.InterruptedException被中止异常的原因及解决方案

《Java.lang.InterruptedException被中止异常的原因及解决方案》Java.lang.InterruptedException是线程被中断时抛出的异常,用于协作停止执行,常见于... 目录报错问题报错原因解决方法Java.lang.InterruptedException 是 Jav

Spring Boot 中的默认异常处理机制及执行流程

《SpringBoot中的默认异常处理机制及执行流程》SpringBoot内置BasicErrorController,自动处理异常并生成HTML/JSON响应,支持自定义错误路径、配置及扩展,如... 目录Spring Boot 异常处理机制详解默认错误页面功能自动异常转换机制错误属性配置选项默认错误处理

SpringBoot 异常处理/自定义格式校验的问题实例详解

《SpringBoot异常处理/自定义格式校验的问题实例详解》文章探讨SpringBoot中自定义注解校验问题,区分参数级与类级约束触发的异常类型,建议通过@RestControllerAdvice... 目录1. 问题简要描述2. 异常触发1) 参数级别约束2) 类级别约束3. 异常处理1) 字段级别约束

Spring Boot @RestControllerAdvice全局异常处理最佳实践

《SpringBoot@RestControllerAdvice全局异常处理最佳实践》本文详解SpringBoot中通过@RestControllerAdvice实现全局异常处理,强调代码复用、统... 目录前言一、为什么要使用全局异常处理?二、核心注解解析1. @RestControllerAdvice2