首页 > 其他分享 >openlayer, 由一个图标遮盖线段需求引发的思考

openlayer, 由一个图标遮盖线段需求引发的思考

时间:2024-07-09 14:30:58浏览次数:7  
标签:ol features openlayer 线段 feature 矩形 图标

最近碰到这么一个需求:有一些面(Polygon)和线(LineString),需要在面的边线上或者在线上均匀绘制一些矩形图标(在各种分辨率下)。在一个Polygon的边线或者LineString上绘制一个矩形图标时,图标会遮住线,如果图标是部分镂空的,就会看到线从图标中穿过,如何让图标看起来遮住了线呢,自然而然就会想到:只需要将图标和线做相交操作,取图标外的两条线段就行了。那么需要依次解决如下问题:

  1. 如何将矩形图标区域和线做相交计算,并算出来不在矩形区域内的两条线段
  2. 在openlayer中,缩放地图时如何动态去绘制出计算出来的线段和矩形图标

首先,第一个问题很好解决,我们只需要使用turf.js的API即可,使用 turf.lineIntersect(line, polygon)计算出线和线上的矩形图标的相交点,然后使用相交点和线段的起点或者终点进行连接即可。由于我的需求是点图标必须在线的中心线,先将线以中心线切分成两条线段,然后使用中心点的坐标和矩形图标的宽高计算出图标矩形的polygon,然后分别用切分的两条线段和图标polygon进行相交计算,计算出来的交点必为新线段的一个点,然后使用 turf.booleanPointInPolygon(line[0], polygon)计算线段的第一个点在不在矩形内,如果在,说明我们应该线的取第二个点来和交点组成新的线段

const getSplitLineString = function (lineString, iconPoints, resolution) {
    if (!iconPoints || iconPoints.length === 0) {
      return lineString
    }
    // 获取icon的矩形区域
    let iconPoint = iconPoints.shift()
    let p1 = this.getIconPolygon(iconPoint, resolution)

    // 计算线段与矩形的交点
    let intersect = turf.lineIntersect(turf.lineString(lineString), p1)
    let intersectCoord = turf.coordAll(intersect)[0]
    // 如果线段的第一个点在icon内,就取第二个点和交点连接成线
    let firstIn = turf.booleanPointInPolygon(turf.point(lineString[0]), p1)
    let newLine = [firstIn ? lineString[1] : lineString[0], intersectCoord]
    return getSplitLineString(newLine, iconPoints, resolution)
}

接下来就是重头戏了,在openlayer中,缩放地图时如何动态去绘制出计算出来的线段和矩形图标。

需要解决这个问题,那就需要深入了解openlayer的map的渲染的逻辑。这里我们使用的是ol.layer.Vector。那就深入研究下openlayer地图是如何渲染feature的(注:我使用的openlayer版本为4.6.5,现在新版本api基本上大同小异)

openlayer地图渲染流程分析图

上面我简单梳理了下openlayer是如何渲染的,大致是这样,在ol.Map中全局注册了一些地图的Renderer和图层的Renderer,这个类比较重要,以layer的renderer来举例,通过继承类ol.renderer.Layer或其子类,编写自己的LayerRender,编写相关的钩子方法,就可以自定义绘制图层:

  1. 在类上挂载一个静态钩子方法handles(type, layer),这样地图就能知道你的LayerRender处理的是哪种类型的Layer
  2. 挂载一个静态钩子方法create(mapRenderer, layer) 用于初始化此render
  3. 实现钩子方法prepareFrame(frameState, layerState)、composeFrame(frameState, layerState, context)来实现图层的重绘(地图发生变化时,均会重绘地图,从而执行钩子方法)

其他流程分析可以参考上面的分析图。在prepareFrame有一段非常重要的代码,是本次需要的

var vectorLayer = /** @type {ol.layer.Vector} */ (this.getLayer());
var vectorSource = vectorLayer.getSource();

// ....中间代码省略

if (vectorLayerRenderOrder) {
    /** @type {Array.<ol.Feature>} */
    var features = [];
    vectorSource.forEachFeatureInExtent(extent,
        /**
         * @param {ol.Feature} feature Feature.
         */
        function(feature) {
          features.push(feature);
        }, this);
    features.sort(vectorLayerRenderOrder);
    for (var i = 0, ii = features.length; i < ii; ++i) {
      renderFeature(features[i]);
    }
  } else {
    vectorSource.forEachFeatureInExtent(extent, renderFeature, this);
  }

可以看到,在这个钩子方法里调用的layer.getSouce()方法来获取图层source,并使用了vectorSource.forEachFeatureInExtent方法来处理每一个feature,那么,正好就有时机介入来处理我们的feature了

ol.source.Vector.prototype.forEachFeatureInExtent = function(extent, callback, opt_this) {
  if (this.featuresRtree_) {
    return this.featuresRtree_.forEachInExtent(extent, callback, opt_this);
  } else if (this.featuresCollection_) {
    return this.featuresCollection_.forEach(callback, opt_this);
  }
};

forEachFeatureInExtent 方法并不复杂,只是遍历了内部数据源的feature并执行了传入的回调。所以我们只需要编写一个新的类继承ol.source.Vector类,并重写forEachFeatureInExtent方法即可,伪代码如下

var MyVectorSource = function (opt_options) {
  ol.source.Vector.call(this, opt_options)
}
ol.inherits(MyVectorSource, ol.source.Vector);

MyVectorSource.prototype.forEachFeatureInExtent = function (extent, callback, opt_this) {
  let features = [];
  ol.source.Vector.prototype.forEachFeatureInExtent.call(
    this,
    extent,
    function (feature) {
      // 这里将处理ol.souce.Vector里原始的feature
      // 我们现在可以切分线了,并将切分完成的feature放入到数组中,具体代码省略
      features.push(handleYourFeature(feature));
    },
    opt_this
  );
  for (let i = 0, ii = features.length; i < ii; ++i) {
    callback(features[i]);
  }
};

标签:ol,features,openlayer,线段,feature,矩形,图标
From: https://blog.csdn.net/qq_41211797/article/details/140290396

相关文章

  • QT实现wifi信号强弱图标
            Qt界面中绘制WiFi信号强弱图标。模拟类似手机的WIFI强度图标的绘制,比较简单,欢迎参考。一、简述        使用Qt实现WIFI信号图标。二、效果  三、核心代码  1、头文件#ifndefWIFIICON_H#defineWIFIICON_H#include<QWidget>classW......
  • GLFLS课程:线段树进阶
    线段树合并与分裂线段树合并两个权值线段树\(T_1\)和\(T_2\)的合并是一个递归的过程。我们不妨设要合并的子树为\(x\)和\(y\),其对应区间均为\([l,\r]\)那么分类讨论如下:  \((1)\)首先若\(x=0\)或\(y=0\),则\(x\),\(y\)中有空节点,直接返回\(x+y\)即可......
  • 20240706总结(线段树应用)
    A-PhysicalEducationLessonsCF915EPhysicalEducationLessons题解:没什么好说的,动态开点模板题(好像普通线段树也可以做)B-GCDofanArrayCF1493DGCDofanArray题解:暴力分解质因数,修改的时候也把x分解,对每个质数开一个可重集合(multiset)记录一下每个质数出现的不同位......
  • UWP WinUI 制作一个路径矢量图标按钮样式入门
    本文将告诉大家如何在UWP或WinUI3或UNO里,如何制作一个路径按钮。路径按钮就是使用几何路径轮廓表示内容的按钮,常见于各种图标按钮,或svg系贴图矢量图按钮在网上有非常多矢量图库,其中免费的图库也非常多,比如https://www.iconfont.cn/等等。在咱的应用程序里面,可以使用这......
  • 电脑wifi图标消失:参考Window11 !!!
     以下是四种解决方案,希望能够帮助wifi图标消失的宝子!!!一、更新驱动1.右击选择设备管理器2.点击网络适配器如果出现以下图片这种情况。右击,更新驱动,如下图所示3.浏览/搜索电脑驱动,建议按图片操作 4.任意选择一个驱动,点击下一步5.等待更新完成,然后重启电脑观察w......
  • 274: vue+openlayers 点图标的大小随着zoom的放大缩小而变化
    作者:还是大剑师兰特,曾为美国某知名大学计算机专业研究生,现为国内GIS领域高级前端工程师,CSDN知名博主,深耕openlayers、leaflet、mapbox、cesium,canvas,echarts等技术开发,欢迎加微信(gis-dajianshi),一起交流。查看本专栏目录-本文是第274个示例文章目录一......
  • 李超线段树 学习笔记
    问题引入:在一个平面内,有\(n\)次操作,这些操作分为\(2\)种:第一种是在平面内加入一条线段;第二种是给定一个\(k\),查询直线\(x=k\)与这些线段交点的最大值。(强制在线,\(n\le10^5,1\lex\le39989,-10^9\ley\le10^9\))求这种用区间覆盖的问题,一般我们会想到线段树。但是一般......
  • 273:vue+openlayers 显示流动轨迹并计算航向
    作者:还是大剑师兰特,曾为美国某知名大学计算机专业研究生,现为国内GIS领域高级前端工程师,CSDN知名博主,深耕openlayers、leaflet、mapbox、cesium,canvas,echarts等技术开发,欢迎加微信(gis-dajianshi),一起交流。查看本专栏目录-本文是第273个示例文章目录一......
  • vue+openlayers之几何图形交互绘制基础与实践
    文章目录1.实现效果2.实现步骤3.示例页面代码3.基本几何图形绘制的关键代码1.实现效果绘制点、线、多边形、圆、正方形、长方形2.实现步骤引用openlayers开发库。加载天地图wmts瓦片地图。在页面上添加几何图形绘制的功能按钮,使用下拉列表(select)设置几何图形绘制......
  • [学习笔记] 动态开点权值线段树合并 - 数据结构
    权值线段树例题【模板】普通平衡树#include<bits/stdc++.h>usingnamespacestd;constintN=1e5+1;intn,val[N],opt[N],num[N],cnt,len,san[N],m[N],rnk[N];unordered_map<int,int>dfn;structWeightedSegmentTree{ #definels(id<<1) #define......