源码分析之Openlayers中OverviewMap鹰眼控件

概述

本文主要介绍 Openlayers 中提供的最后一个控件,鹰眼控件OverviewMap的源码实现和核心原理.鹰眼控件简单来说就是提供一个小窗口视图,可以实时反应当前地图在整个地图的地理位置,可以理解成整体局部的关系.

源码分析

OverviewMap类继承于Control类.关于Control类,可以参考源码分析之Openlayers中的控件篇Control基类介绍
.

OverviewMap类源码

OverviewMap类实现如下:

class OverviewMap extends Control {constructor(options) {options = options ? options : {};super({element: document.createElement("div"),render: options.render,target: options.target,});this.boundHandleRotationChanged_ = this.handleRotationChanged_.bind(this);this.collapsed_ =options.collapsed !== undefined ? options.collapsed : true;this.collapsible_ =options.collapsible !== undefined ? options.collapsible : true;if (!this.collapsible_) {this.collapsed_ = false;}this.rotateWithView_ =options.rotateWithView !== undefined ? options.rotateWithView : false;this.viewExtent_ = undefined;const className =options.className !== undefined ? options.className : "ol-overviewmap";const tipLabel =options.tipLabel !== undefined ? options.tipLabel : "Overview map";const collapseLabel =options.collapseLabel !== undefined ? options.collapseLabel : "\u2039";if (typeof collapseLabel === "string") {this.collapseLabel_ = document.createElement("span");this.collapseLabel_.textContent = collapseLabel;} else {this.collapseLabel_ = collapseLabel;}const label = options.label !== undefined ? options.label : "\u203A";if (typeof label === "string") {this.label_ = document.createElement("span");this.label_.textContent = label;} else {this.label_ = label;}const activeLabel =this.collapsible_ && !this.collapsed_ ? this.collapseLabel_ : this.label_;const button = document.createElement("button");button.setAttribute("type", "button");button.title = tipLabel;button.appendChild(activeLabel);button.addEventListener(EventType.CLICK,this.handleClick_.bind(this),false);this.ovmapDiv_ = document.createElement("div");this.ovmapDiv_.className = "ol-overviewmap-map";this.view_ = options.view;const ovmap = new Map({view: options.view,controls: new Collection(),interactions: new Collection(),});this.ovmap_ = ovmap;if (options.layers) {options.layers.forEach(function (layer) {ovmap.addLayer(layer);});}const box = document.createElement("div");box.className = "ol-overviewmap-box";box.style.boxSizing = "border-box";/*** @type {import("../Overlay.js").default}* @private*/this.boxOverlay_ = new Overlay({position: [0, 0],positioning: "center-center",element: box,});this.ovmap_.addOverlay(this.boxOverlay_);const cssClasses =className +" " +CLASS_UNSELECTABLE +" " +CLASS_CONTROL +(this.collapsed_ && this.collapsible_ ? " " + CLASS_COLLAPSED : "") +(this.collapsible_ ? "" : " ol-uncollapsible");const element = this.element;element.className = cssClasses;element.appendChild(this.ovmapDiv_);element.appendChild(button);/* Interactive map */const scope = this;const overlay = this.boxOverlay_;const overlayBox = this.boxOverlay_.getElement();/* Functions definition */const computeDesiredMousePosition = function (mousePosition) {return {clientX: mousePosition.clientX,clientY: mousePosition.clientY,};};const move = function (event) {const position = /** @type {?} */ (computeDesiredMousePosition(event));const coordinates = ovmap.getEventCoordinate(/** @type {MouseEvent} */ (position));overlay.setPosition(coordinates);};const endMoving = function (event) {const coordinates = ovmap.getEventCoordinateInternal(event);scope.getMap().getView().setCenterInternal(coordinates);window.removeEventListener("pointermove", move);window.removeEventListener("pointerup", endMoving);};/* Binding */this.ovmapDiv_.addEventListener("pointerdown", function () {if (event.target === overlayBox) {window.addEventListener("pointermove", move);}window.addEventListener("pointerup", endMoving);});}setMap(map) {const oldMap = this.getMap();if (map === oldMap) {return;}if (oldMap) {const oldView = oldMap.getView();if (oldView) {this.unbindView_(oldView);}this.ovmap_.setTarget(null);}super.setMap(map);if (map) {this.ovmap_.setTarget(this.ovmapDiv_);this.listenerKeys.push(listen(map,ObjectEventType.PROPERTYCHANGE,this.handleMapPropertyChange_,this));const view = map.getView();if (view) {this.bindView_(view);}if (!this.ovmap_.isRendered()) {this.updateBoxAfterOvmapIsRendered_();}}}handleMapPropertyChange_(event) {if (event.key === MapProperty.VIEW) {const oldView = /** @type {import("../View.js").default} */ (event.oldValue);if (oldView) {this.unbindView_(oldView);}const newView = this.getMap().getView();this.bindView_(newView);} else if (!this.ovmap_.isRendered() &&(event.key === MapProperty.TARGET || event.key === MapProperty.SIZE)) {this.ovmap_.updateSize();}}bindView_(view) {if (!this.view_) {// Unless an explicit view definition was given, derive default from whatever main map uses.const newView = new View({projection: view.getProjection(),});this.ovmap_.setView(newView);}view.addChangeListener(ViewProperty.ROTATION,this.boundHandleRotationChanged_);// Sync once with the new viewthis.handleRotationChanged_();if (view.isDef()) {this.ovmap_.updateSize();this.resetExtent_();}}unbindView_(view) {view.removeChangeListener(ViewProperty.ROTATION,this.boundHandleRotationChanged_);}handleRotationChanged_() {if (this.rotateWithView_) {this.ovmap_.getView().setRotation(this.getMap().getView().getRotation());}}validateExtent_() {const map = this.getMap();const ovmap = this.ovmap_;if (!map.isRendered() || !ovmap.isRendered()) {return;}const mapSize = /** @type {import("../size.js").Size} */ (map.getSize());const view = map.getView();const extent = view.calculateExtentInternal(mapSize);if (this.viewExtent_ && equalsExtent(extent, this.viewExtent_)) {// repeats of the same extent may indicate constraint conflicts leading to an endless cyclereturn;}this.viewExtent_ = extent;const ovmapSize = /** @type {import("../size.js").Size} */ (ovmap.getSize());const ovview = ovmap.getView();const ovextent = ovview.calculateExtentInternal(ovmapSize);const topLeftPixel = ovmap.getPixelFromCoordinateInternal(getTopLeft(extent));const bottomRightPixel = ovmap.getPixelFromCoordinateInternal(getBottomRight(extent));const boxWidth = Math.abs(topLeftPixel[0] - bottomRightPixel[0]);const boxHeight = Math.abs(topLeftPixel[1] - bottomRightPixel[1]);const ovmapWidth = ovmapSize[0];const ovmapHeight = ovmapSize[1];if (boxWidth < ovmapWidth * MIN_RATIO ||boxHeight < ovmapHeight * MIN_RATIO ||boxWidth > ovmapWidth * MAX_RATIO ||boxHeight > ovmapHeight * MAX_RATIO) {this.resetExtent_();} else if (!containsExtent(ovextent, extent)) {this.recenter_();}}resetExtent_() {if (MAX_RATIO === 0 || MIN_RATIO === 0) {return;}const map = this.getMap();const ovmap = this.ovmap_;const mapSize = /** @type {import("../size.js").Size} */ (map.getSize());const view = map.getView();const extent = view.calculateExtentInternal(mapSize);const ovview = ovmap.getView();// get how many times the current map overview could hold different// box sizes using the min and max ratio, pick the step in the middle used// to calculate the extent from the main map to set it to the overview map,const steps = Math.log(MAX_RATIO / MIN_RATIO) / Math.LN2;const ratio = 1 / (Math.pow(2, steps / 2) * MIN_RATIO);scaleFromCenter(extent, ratio);ovview.fitInternal(polygonFromExtent(extent));}recenter_() {const map = this.getMap();const ovmap = this.ovmap_;const view = map.getView();const ovview = ovmap.getView();ovview.setCenterInternal(view.getCenterInternal());}updateBox_() {const map = this.getMap();const ovmap = this.ovmap_;if (!map.isRendered() || !ovmap.isRendered()) {return;}const mapSize = /** @type {import("../size.js").Size} */ (map.getSize());const view = map.getView();const ovview = ovmap.getView();const rotation = this.rotateWithView_ ? 0 : -view.getRotation();const overlay = this.boxOverlay_;const box = this.boxOverlay_.getElement();const center = view.getCenter();const resolution = view.getResolution();const ovresolution = ovview.getResolution();const width = (mapSize[0] * resolution) / ovresolution;const height = (mapSize[1] * resolution) / ovresolution;// set position using center coordinatesoverlay.setPosition(center);// set box size calculated from map extent size and overview map resolutionif (box) {box.style.width = width + "px";box.style.height = height + "px";const transform = "rotate(" + rotation + "rad)";box.style.transform = transform;}}updateBoxAfterOvmapIsRendered_() {if (this.ovmapPostrenderKey_) {return;}this.ovmapPostrenderKey_ = listenOnce(this.ovmap_,MapEventType.POSTRENDER,(event) => {delete this.ovmapPostrenderKey_;this.updateBox_();});}handleClick_(event) {event.preventDefault();this.handleToggle_();}handleToggle_() {this.element.classList.toggle(CLASS_COLLAPSED);if (this.collapsed_) {replaceNode(this.collapseLabel_, this.label_);} else {replaceNode(this.label_, this.collapseLabel_);}this.collapsed_ = !this.collapsed_;// manage overview map if it had not been rendered before and control// is expandedconst ovmap = this.ovmap_;if (!this.collapsed_) {if (ovmap.isRendered()) {this.viewExtent_ = undefined;ovmap.render();return;}ovmap.updateSize();this.resetExtent_();this.updateBoxAfterOvmapIsRendered_();}}getCollapsible() {return this.collapsible_;}setCollapsible(collapsible) {if (this.collapsible_ === collapsible) {return;}this.collapsible_ = collapsible;this.element.classList.toggle("ol-uncollapsible");if (!collapsible && this.collapsed_) {this.handleToggle_();}}setCollapsed(collapsed) {if (!this.collapsible_ || this.collapsed_ === collapsed) {return;}this.handleToggle_();}getCollapsed() {return this.collapsed_;}getRotateWithView() {return this.rotateWithView_;}setRotateWithView(rotateWithView) {if (this.rotateWithView_ === rotateWithView) {return;}this.rotateWithView_ = rotateWithView;if (this.getMap().getView().getRotation() !== 0) {if (this.rotateWithView_) {this.handleRotationChanged_();} else {this.ovmap_.getView().setRotation(0);}this.viewExtent_ = undefined;this.validateExtent_();this.updateBox_();}}getOverviewMap() {return this.ovmap_;}render(mapEvent) {this.validateExtent_();this.updateBox_();}
}

OverviewMap类构造函数

OverviewMap类构造函数接受一个参数对象options,默认为空对象{};调用super方法,创建鹰眼控件容器;绑定方法handleRotationChanged_this指向,初始化折叠属性,和Attributions属性控件类似,默认情况下,鹰眼控件是折叠状态,点击可以展开.初始化this.rotateWithView_变量,默认为false,表示鹰眼地图视图的旋转是否与地图主视图同步;初始化鹰眼控件的类名和标签,创建控件元素等;然后是监听控件按钮的点击事件handleClick_;创建鹰眼地图的容器ol-overviewmap-map,调用Map类实例化ovmap,ovmapview参数是options.view传递过来的;判断,若options.layers存在,则遍历它将图层添加到ovmap中;然后创建一个Overlaythis.boxOverlay_添加到ovmap中,this.boxOverlay_是一个矩形框,用来模拟地图主视图区域,然后监听ovmap的的容器pointerdown类型的事件,主要是pointermove方法,即在鹰眼控件视图内移动鼠标可以控制矩形框boxOverlay_的位置;还有会监听pointerup事件,当鼠标抬起时调用endMoving方法,此时会获取鼠标在ovmap中的坐标,然后调用this.getMap()即获得地图主视图,设置它的中心点为鼠标抬起的坐标位置,最后移除pointermovepointerup事件监听.

OverviewMap类主要方法

OverviewMap类主要有以下方法:

  • setMap方法

setMap方法被调用时,会先获取地图主视图,判断参数map是否就是地图主视图,若二者一致,则return;判断,若地图主视图存在,则调用this.unbindView_进行解绑,并且鹰眼控件的目标元素置空;然后调用父类的setMap方法;判断,若参数map存在,则设置鹰眼控件的目标元素,并且注册地图主视图propertychange类型的监听,事件为this.handleMapPropertyChange_;判断,若地图主视图存在,则调用this.bindView_进行绑定;最后判断,若鹰眼地图视图没有渲染,则调用this.updateBoxAfterOvmapIsRendered_方法,更新鹰眼控件中的矩形overlay

  • handleMapPropertyChange_方法

当地图的属性发生改变时,会调用handleMapPropertyChange_方法;该方法内部会先判断参数eventkey是否为view,即是否是视图发生变化,若是,则需要解绑旧视图,绑定新视图;若不是,则判断,若鹰眼地图没有渲染完成并且event.keytarget或者size,则更新鹰眼控件的地图大小。

  • bindView_方法

bindView_方法首先会判断,若this.view_不存在,则实例化一个View类,投影为参数view的投影,然后设置鹰眼地图的视图;然后注册视图的rotation类型的监听事件this.boundHandleRotationChanged_,然后调用this.boundHandleRotationChanged_执行一次;最后判断,若参数view没有中心点也没有分辨率,则重置鹰眼地图的大小以及调用resetExtent_重置鹰眼地图视图为地图主视图最大值和最小值比例的一半。

  • unbindView_方法

unbindView_方法就是移除参数view上的rotation的监听事件this.boundHandleRotationChanged_

  • handleRotationChanged_方法

handleRotationChanged_方法内部会判断,若this.rotateWithView_true,则根据地图主视图的旋转角度同步设置鹰眼地图的旋转角度,意思就是同步两个地图的旋转角度,但是this.rotateWithView_默认为false,若需要同步地图,则需要传参数。

  • validateExtent_方法

validateExtent_方法内部就是用来计算两个地图的范围,然后决定是调用this.resetExtent_还是this.recenter_方法

  • resetExtent_方法

resetExtent_方法用于重置鹰眼地图范围

  • recenter_方法

recenter_方法首先会获取地图主视图的中心点,然后将其设置为鹰眼地图的中心点

  • updateBox_方法

updateBox_用于设置鹰眼控件中的overlay,前面提过该overlay矩形表示当前地图主视图在地图最大范围中的相对大小;该方法内部会先获取地图主视图的中心点、分辨率,然后进行一系列的换算,然后更新鹰眼控件中的矩形大小和位置。

  • updateBoxAfterOvmapIsRendered_方法

updateBoxAfterOvmapIsRendered_方法就是在鹰眼地图渲染完成后,调用this.updateBox_更新overlay;这里设计得很巧妙,只监听一次;

  • handleClick_方法

handleClick_方法内部就是调用handleToggle_

  • handleToggle_方法

handleToggle_方法就是用来显示或隐藏鹰眼地图

  • getCollapsible方法

getCollapsible方法获取鹰眼控件地图是否有可以进行折叠的能力

  • setCollapsible方法

setCollapsible方法就是设置鹰眼控件的折叠属性

  • setCollapsed方法

setCollapsed方法就是设置显示或隐藏鹰眼控件的地图,内部是调用this.handleToggle_方法

  • getCollapsed方法

getCollapsed方法用于获取鹰眼控件地图的折叠状态

  • getRotateWithView方法

getRotateWithView方法获取变量this.rotateWithView_

  • setRotateWithView方法

setRotateWithView方法用于设置this.rotateWithView_变量,调用该方法后,若地图主视图的旋转角度不为0,则判断,若this.rotateWithView_(等同于参数rotateWithView)为true,则调用this.handleRotationChanged_进行旋转鹰眼地图;否则将鹰眼地图旋转角度设置为0;最后调用validateExtent_updateBox_方法。

  • getOverviewMap方法

getOverviewMap方法用于获取鹰眼地图的实例。

  • render方法
    在鹰眼控件进行setMap后会调用,内部就是执行this.validateExtent_updateBox_方法。

总结

本文主要介绍了鹰眼控件OverviewMap的实现原理和主要方法的讲解。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

visual studio 2022 c++使用教程

介绍 c开发windows一般都是visual studio&#xff0c;linux一般是vscode&#xff0c;但vscode调试c不方便&#xff0c;所以很多情况都是2套代码&#xff0c;在windows上用vs开发方便&#xff0c;在转到linux。 安装 1、官网下载vs2022企业版–选择桌面开发–安装位置–安装–…

python学opencv|读取图像(十七)认识alpha通道

【1】引言 前序学习进程中&#xff0c;我们已经掌握了RGB和HSV图像的通道拆分和合并&#xff0c;获得了很多意想不到的效果&#xff0c;相关链接包括且不限于&#xff1a; python学opencv|读取图像&#xff08;十二&#xff09;BGR图像转HSV图像-CSDN博客 python学opencv|读…

设计模式--单例模式【创建型模式】

设计模式的分类 我们都知道有 23 种设计模式&#xff0c;这 23 种设计模式可分为如下三类&#xff1a; 创建型模式&#xff08;5 种&#xff09;&#xff1a;单例模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式。结构型模式&#xff08;7 种&#xff09;&#xff1…

neo4j 图表数据导入到 TuGraph

neo4j 图表数据导入到 TuGraph 代码文件说明后文 前言:近期在引入阿里的 TuGraph 图数据库&#xff0c;需要将 原 neo4j 数据导入到新的 tugraph 数据库中。预期走csv文件导入导出&#xff0c;但因为格式和数据库设计问题&#xff0c;操作起来比较麻烦&#xff08;可能是个人没…

模具生产过程中的标签使用流程图

①NFC芯片嵌入周转筐&#xff0c;通过读卡器读取CK_Label_v3的数据&#xff0c;并将这些信息上传至服务器进行存储&#xff1b; ②服务器随后与客户的WMS&#xff08;仓库管理系统&#xff09;进行交互&#xff0c;记录和同步注塑机的原始数据&#xff1b; ③当周转筐内的模具…

Linux线程同步

1 线程同步概念 假设有有三个线程A,B,C&#xff0c;当前一个线程A对内存中的共享资源进行访问时&#xff0c;其它线程B&#xff0c;C都不可以对这块内存进行操作&#xff0c;直到线程A对这块内存访问完毕为止&#xff0c;B&#xff0c;C中的一个才能访问这块内存&#xff0c;剩…

Vue与React:前端框架的巅峰对决

文章目录 一、引言&#xff08;一&#xff09;前端框架发展现状简述 二、Vue 与 React 框架概述&#xff08;一&#xff09;Vue.js 简介&#xff08;二&#xff09;React.js 简介 三、开发效率对比&#xff08;一&#xff09;Vue 开发效率分析&#xff08;二&#xff09;React …

项目管理工具Maven(一)

Maven的概念 什么是Maven 翻译为“专家”&#xff0c;“内行”Maven是跨平台的项目管理工具。主要服务于基于Java平台的项目构建&#xff0c;依赖管理和项目信息管理。什么是理想的项目构建&#xff1f; 高度自动化&#xff0c;跨平台&#xff0c;可重用的组件&#xff0c;标准…

【Prometheus 】【实战篇(五)】深入解析 Prometheus 监控指标类型:Counter、Gauge、Histogram 和 Summary

Prometheus 提供了四种核心的指标类型&#xff0c;分别是 Counter&#xff08;计数器&#xff09;、Gauge&#xff08;仪表&#xff09;、Histogram&#xff08;直方图&#xff09;和 Summary&#xff08;摘要&#xff09;。这些指标类型在客户端库中有具体的使用说明&#xff…

outlook smtp 发送邮件

前提条件 开通 app password 开通 smtp 服务 import smtplib from email.mime.multipart import MIMEMultipart from email.mime.text import MIMETextdef send_html_email_smtp(sender_email, sender_password, recipient_email, subject, html_content):# Create the messag…

如何利用Python爬虫获得Lazada商品评论列表

在电商领域&#xff0c;用户评论是了解商品口碑和市场反馈的重要渠道。对于Lazada这样的东南亚电商平台&#xff0c;获取商品评论列表对于市场分析、产品改进和销售策略的制定至关重要。本文将详细介绍如何使用Python编写爬虫程序&#xff0c;以获取Lazada商品的评论列表。 一、…

穷举vs暴搜vs深搜vs回溯vs剪枝系列一>找出所有子集的异或总和再求和

题目&#xff1a; 解析&#xff1a; 代码&#xff1a; private int ret;//返回周结果private int path;//枚举一个元素就异或进去public int subsetXORSum(int[] nums) {dfs(nums, 0);return ret;} private void dfs(int[] nums, int pos){ret path;for(int i pos; i <…

如何设计一个秒杀系统

开局一张图 结局要说清 对于设计一个秒杀系统&#xff0c;结合图片分层结构&#xff0c;根据每一层从访问层&#xff0c;负载层&#xff0c;服务层&#xff0c;业务层&#xff0c;支撑层&#xff0c;数据层&#xff0c;详细说明每一层应该怎么设计。 应该注意那些事项。比如访…

【LeetCode】45.跳跃游戏II

题目链接&#xff1a; 45.跳跃游戏 题目描述&#xff1a; 思路一&#xff08;广度优先搜索算法BFS&#xff09; 通过广度优先搜索算法寻找最短距离 代码实现&#xff1a; class Solution { public:int jump(vector<int>& nums) {int n nums.size();if(n<1) re…

WPF ControlTemplate 控件模板

区别于 DataTemplate 数据模板&#xff0c;ControlTemplate 是控件模板&#xff0c;是为自定义控件的 Template 属性服务的&#xff0c;Template 属性类型就是 ControlTemplate。 演示&#xff0c; 自定义一个控件 MyControl&#xff0c;包含一个字符串类型的依赖属性。 pub…

clickhouse-题库

1、clickhouse介绍以及架构 clickhouse一个分布式列式存储数据库&#xff0c;主要用于在线分析查询 2、列式存储和行式存储有什么区别&#xff1f; 行式存储&#xff1a; 1&#xff09;、数据是按行存储的 2&#xff09;、没有建立索引的查询消耗很大的IO 3&#xff09;、建…

arcgisPro将面要素转成CAD多段线

1、说明&#xff1a;正常使用【导出为CAD】工具&#xff0c;则导出的是CAD三维多线段&#xff0c;无法进行编辑操作、读取面积等。这是因为要素面中包含Z值&#xff0c;导出则为三维多线段数据。需要利用【复制要素】工具禁用M值和Z值&#xff0c;再导出为CAD&#xff0c;则得到…

【Unity3D】实现可视化链式结构数据(节点数据)

关键词&#xff1a;UnityEditor、可视化节点编辑、Unity编辑器自定义窗口工具 使用Newtonsoft.Json、UnityEditor相关接口实现 主要代码&#xff1a; Handles.DrawBezier(起点&#xff0c;终点&#xff0c;起点切线向量&#xff0c;终点切线向量&#xff0c;颜色&#xff0c;n…

详细解读TISAX认证的意义

详细解读TISAX认证的意义&#xff0c;犹如揭开信息安全领域的一颗璀璨明珠&#xff0c;它不仅代表了企业在信息安全管理方面的卓越成就&#xff0c;更是通往全球汽车供应链信任桥梁的关键一环。TISAX&#xff0c;即“Trusted Information Security Assessment Exchange”&#…

Ubuntu搭建ES8集群+加密通讯+https访问

目录 写在前面 一、前期准备 1. 创建用户和用户组 2. 修改limits.conf文件 3. 关闭操作系统swap功能 4. 调整mmap上限 二、安装ES 1.下载ES 2.配置集群间安全访问证书密钥 3.配置elasticsearch.yml 4.修改jvm.options 5.启动ES服务 6.修改密码 7.启用外部ht…