当前位置: 首页 > news >正文

OpenLayers:封装Overlay的方法

平时在使用OpenLayers的Overlay时常感觉不便,于是最近我便封装了一些Overlay增删改查的方法,以提高可用性。这边文章中我会介绍我封装的方法,同时记录这个过程中踩的一些坑。

添加Overlay

/**
 * @abstract 添加overlay
 * @param {*} map
 * @param {object} options overlay的配置项
 * @param {string} options.id overlay的id,可以用来唯一标识overlay
 * @param {string} options.groupId overlay的组名,可以用来批量操作overlay
 * @param {boolean} options.visibility overlay的初始可见性
 * options中的其它参数参考 ol/Overlay的构造函数参数
 */
export const addOverlay = (map, options) => {
   let {
    groupId = "default",
    positioning = "center-center",
    visibility = true,
    element,
    ...other
  } = options;

  // 将DOMstring转换为DOM元素
  if (!(element instanceof HTMLElement)) {
    const container = document.createElement("div");
    container.innerHTML = element;
    element = container.firstChild;
  }

  // 创建overlay
  const overlay = new Overlay({
    element,
    positioning,
    ...other,
  });

  // 设置额外的属性
  overlay.setProperties({
    groupId,
    visibility,
  });

  // 设置初始的可见性
  overlay.on("change:map", () => {
    if (!visibility) {
      setTimeout(() => {
        overlay.getElement().parentElement.style.display = "none";
      }, 0);
    }
  });

  // 添加到地图
  map.addOverlay(overlay);
};

addOverlay方法的特别之处

我封装的添加Overlay的方法,主要的改造是三个地方。

第一,对Overlay的element属性做了一个封装。

在我第一次使用Overlay的时候我就犯了一个错误,我给element属性传递了一个DOMString(比如`<div class="test-overlay pink">粉色overlay</div>`),结果就出现了类似于下面的报错。这是因为Overlay的element属性只能接收一个DOM元素。

现在经过我的封装后,我的addOverlay方法允许传入的element属性是一个DOMString

第二,给Overlay添加一些额外的属性,这些属性会在后续帮助我去实现一些对Overlay的操作。

  // 设置额外的属性
  overlay.setProperties({
    groupId,
    visibility,
  });

第三,给Overlay设置了初始的可见性,如果传入的visibility参数为false,就会暂时先将overlay隐藏。(我还考虑后续或许可以通过这个来实现popup)

// 设置初始的可见性
overlay.on("change:map", () => {
  if (!visibility) {
    setTimeout(() => {
      overlay.getElement().parentElement.style.display = "none";
    }, 0);
  }
});

查询Overlay

/**
 * @abstract 查找overlay
 * @param {*} map
 * @param {object} condition 筛选条件
 * @param {string} condition.id overlay的id筛选需要操作的overlay
 * @param {string} condition.groupId overlay的groupId筛选需要操作的overlay
 * @param {function} condition.condition 自定义条件函数,筛选需要操作的overlay,返回true则操作,false则忽略
 * @returns  {Array} 符合条件的overlay数组
 */
export const findOverlay = (map, { id, groupId, condition }) => {
  const targetOverlays = [];
  const overlays = map.getOverlays().getArray();

  overlays.forEach(overlay => {
    const groupId_ = overlay.get("groupId");
    const id_ = overlay.getId();

    if (id) {
      if (id_ === id) {
        targetOverlays.push(overlay);
      }
    }

    if (groupId) {
      if (groupId_ === groupId) {
        targetOverlays.push(overlay);
      }
    }

    if (condition) {
      if (condition(overlay, id_, groupId_, overlays)) {
        targetOverlays.push(overlay);
      }
    }
  });

  return targetOverlays;
};

findOverlay支持的查询方式

我设计的查找Overlay的方法findOverlay支持通过三种方式进行查找:

  1. 通过id查找,这个id是创建 Overlay时的参数(我建议在创建overlay时最好都要添加这个参数)
// 查找id为'overlay-1'的overlay
findOverlay( map , { id : 'overlay-1' })
  1. 通过groupId查找,这个是我在addOverlay方法中添加的一个额外属性,我的计划是通过groupId实现对Overlay的分组管理,可以同时去操作一组的Overlay。
// 查找所有groupId 为 stationTooltip 的overlay
findOverlay( map , { groupId : 'stationTooltip' })
  1. 通过一个筛选函数condition来进行查找,这个就类似于js中Array.prototype.find()方法。我会为地图中绑定的每个overlay都调用condition方法,若condition方法返回true则将Overlay返回,若返回false则Overlay会被忽略。

  condition方法将会接收到四个参数:

    • overlay,当前的overlay对象;
    • id,当前的overlay对象的id;
    • groupId,当前overlay对象的groupId;
    • overlays,地图中的所有overlay对象组成的数组
// 查找所有groupId 为 stationTooltip 的overlay
findOverlay( map , { condition:(overlay , id , groupId , overlays)=> groupId == "stationTooltip" })

最后,注意findOverlay方法返回一个包含所有符合条件的Overlay对象的数组。

删除Overlay

/**
 * @abstract 移除overlay
 * @param {*} map
 * @param {object} condition 筛选条件
 * @param {string} condition.id overlay的id筛选需要操作的overlay
 * @param {string} condition.groupId overlay的groupId筛选需要操作的overlay
 * @param {function} condition.condition 自定义条件函数,筛选需要操作的overlay,返回true则操作,false则忽略
 */
export const removeOverlay = (map, { id, groupId, condition }) => {
  const targetOverlays = findOverlay(map, { id, groupId, condition });

  targetOverlays.forEach(overlay => {
    map.removeOverlay(overlay);
  });
};

/**
 *@abstract 清除所有overlay
 * @param {*} map
 */
export const clearOverlay = map => {
  const overlays = map.getOverlays();
  overlays.clear();
};

我封装了两个删除Overlay的方法removeOverlayclearOverlay,前者删除符合条件的Overlay,后者清空地图中的所有Overlay。

错误的删除方式

之前我在网上看到下面的这种删除Overlay的方式。也就是边遍历边删除,这种方法是错误的。会有删除不尽的问题。

  const overlays = map.getOverlays();
  overlays.forEach((overlay, index) => {
    if (overlay.getId() === id) {
      overlays.removeAt(index);
    }
  });

正确的写法是先通过遍历将想要删除Overlay收集到一个数组中,然后再循环数组进行删除.。

  const overlays = map.getOverlays();

  const targetOverlays = []
  overlays.forEach((overlay, index) => {
    if (overlay.getId() === id) {
      targetOverlays.push(overlay)
    }
  });

  targetOverlays.forEach((overlay)=>{
    map.removeOverlay(overlay) 
  })

显示/隐藏Overlay

/**
 * @abstract 设置overlay的可见性
 * @param {*} map
 * @param {object} condition 筛选条件
 * @param {string} condition.id overlay的id筛选需要操作的overlay
 * @param {string} condition.groupId overlay的groupId筛选需要操作的overlay
 * @param {function} condition.condition 自定义条件函数,筛选需要操作的overlay,返回true则操作,false则忽略
 * @param {boolean} visible 要设置的可见性
 */
export const setOverlayVisibility = (
  map,
  { id, groupId, condition, visible }
) => {
  const targetOverlays = findOverlay(map, { id, groupId, condition });

  targetOverlays.forEach(overlay => {
    overlay.setProperties({
      visibility: visible,
    });

    const element = overlay.getElement();
    element.parentElement.style.display = visible ? "flex" : "none";
  });
};

/**
 * @abstract 切换overlay的可见性
 * @param {*} map
 * @param {object} condition 筛选条件
 * @param {string} condition.id overlay的id筛选需要操作的overlay
 * @param {string} condition.groupId overlay的groupId筛选需要操作的overlay
 * @param {function} condition.condition 自定义条件函数,筛选需要操作的overlay,返回true则操作,false则忽略
 */
export const toggleOverlayVisibility = (map, { id, groupId, condition }) => {
  const targetOverlays = findOverlay(map, { id, groupId, condition });

  targetOverlays.forEach(overlay => {
    const newVisibility = !overlay.get("visibility");
    overlay.setProperties({
      visibility: newVisibility,
    });

    const element = overlay.getElement();
    element.parentElement.style.display = newVisibility ? "flex" : "none";
  });
};

显示/隐藏Overlay的方法我也封装了两个 setOverlayVisibility用于手动的设置显隐,toggleOverlayVisibility用于自动设置显隐。

隐藏Overlay的方法是去修改Overlay的element的父元素的display。我们在创建Overlay的时候传入的element实际上会被用另一个元素包装起来,这个元素(默认class为ol-overlay-container ol-selectable的元素)才是Overlay的根元素。

参考资料

  1. OpenLayers v10.5.0 API - Class: Overlay
  2. OpenLayers v10.5.0 API - Class: Collection
  3. OpenLayers之 OverLay问题汇总_openlayers overlay zindex-CSDN博客
http://www.dtcms.com/a/106875.html

相关文章:

  • WASM I/O 2025 | MoonBit获Kotlin核心开发,Golem Cloud CEO高度评价
  • 人工智能赋能管理系统,如何实现智能化决策?
  • 操作系统(中断 异常 陷阱) ─── linux第28课
  • 脑影像分析软件推荐 | JuSpace
  • 【kubernetes】pod拉取镜像的策略
  • 关于SQL子查询的使用策略
  • ​自动化网络架构搜索(Neural Architecture Search,NAS)
  • RNN模型与NLP应用——(9/9)Self-Attention(自注意力机制)
  • 1Panel 面板 宝塔面板 Ubuntu 24.04
  • 叁仟数智指路机器人是否支持远程监控和管理?
  • Rclone同步Linux数据到google云盘
  • 【SQL】MySQL进阶3:Innodb引擎结构,事务与ACID的实现
  • Apifox Helper 与 Swagger3 区别
  • 一个服务器算分布式吗,分布式需要几个服务器
  • 电子企业MES管理系统智能排产与动态调度优化
  • 化工网平台API接口开发实战:从接入到数据解析‌
  • 递归(实践版)
  • 阿里云AI Studio 2.0:拖拽搭建企业级智能客服系统
  • 信息学奥赛一本通 1611:仓库建设 | 洛谷 P2120 [ZJOI2007] 仓库建设
  • Cribl 创建路线Route
  • dubbo RPC协议
  • Unity Standard Shader 解析(二)之ForwardAdd(标准版)
  • <贪心算法>
  • 第四章、Isaacsim在GUI中构建机器人(3):添加摄像头和传感器
  • Java语言如何用AI实现文件报告的自动质检?
  • Vue3编译器深度解析:从模板编译到极致性能优化
  • 【NLP 面经 5】
  • 通过ansible+docker-compose快速安装一主两从redis+三sentinel
  • 获取最新浏览器驱动,并自动安装适配浏览器
  • 使用 Amazon Lightsail 启动并配置 WordPress 实例教程