三十四、openlayers官网示例Dynamic clusters解析——动态的聚合图层

官网demo地址:

https://openlayers.org/en/latest/examples/clusters-dynamic.html

这篇绘制了多个聚合图层。

先初始化地图 ,设置了地图视角的边界extent,限制了地图缩放的范围

 initMap() {const raster = new TileLayer({source: new XYZ({attributions:'Base map: <a target="_blank" href="https://basemap.at/">basemap.at</a>',url: "https://maps{1-4}.wien.gv.at/basemap/bmapgrau/normal/google3857/{z}/{y}/{x}.png",}),});this.map = new Map({layers: [raster],target: "map",view: new View({center: [0, 0],zoom: 2,maxZoom: 19,extent: [...fromLonLat([16.1793, 48.1124]),...fromLonLat([16.5559, 48.313]),],showFullExtent: true,}),});this.map.on("pointermove", this.moveEvent);this.map.on("click", this.clickEvent);},

创建一个聚合数据源,数据是geoJson格式的。

    const vectorSource = new VectorSource({format: new GeoJSON(),url: "https://openlayers.org/en/latest/examples/data/geojson/photovoltaic.json",});const clusterSource = new Cluster({attributions:'Data: <a href="https://www.data.gv.at/auftritte/?organisation=stadt-wien">Stadt Wien</a>',distance: 35,source: vectorSource,});

 然后创建一个聚合图层

//聚合图层this.clustersLayer = new VectorLayer({source: clusterSource,style: this.clusterStyle,});this.map.addLayer(this.clustersLayer);

因为每个feature的样式不一样,所以样式这里都绑定了函数。

这里的outerCircle定义为全局变量而并非局部变量,主要是因为clusterStyle函数是个高频触发函数,将outerCircle写成全局的可以不用new那么多次。使用样式组的方法绘制出来了外发光效果,其实就是画了两次圆形。

//黄色圆圈 内外两层 发光效果clusterStyle(feature) {const size = feature.get("features").length;//还有下级if (size > 1) {return [new Style({image: this.outerCircle,}),new Style({image: this.innerCircle,text: new Text({text: size.toString(),fill: this.textFill,stroke: this.textStroke,}),}),];}//没有下级const originalFeature = feature.get("features")[0];return this.clusterMemberStyle(originalFeature);},
 this.textFill = new Fill({color: "#fff",});this.textStroke = new Stroke({color: "rgba(0, 0, 0, 0.6)",width: 3,});this.innerCircle = new CircleStyle({radius: 14,fill: new Fill({color: "rgba(255, 165, 0, 0.7)",}),});this.outerCircle = new CircleStyle({radius: 20,fill: new Fill({color: "rgba(255, 153, 102, 0.3)",}),});

有下级的时候图形是橙色发光的样式,没有下级的时候。根据feature的LEISTUNG字段显示为不同的icon图形。

clusterMemberStyle(clusterMember) {return new Style({geometry: clusterMember.getGeometry(),image:clusterMember.get("LEISTUNG") > 5 ? this.darkIcon : this.lightIcon,});},
 this.darkIcon = new Icon({src: "data/icons/emoticon-cool.svg",});this.lightIcon = new Icon({src: "data/icons/emoticon-cool-outline.svg",});

 创建一个凸包图层。

凸包是包含给定点集的最小凸多边形,在许多计算几何应用中非常重要,如图形学、地理信息系统(GIS)和形状分析。计算凸包的算法通常基于点的排序和几何性质,可以有效地处理大规模的数据。 

下载引入monotone-chain-convex-hull

npm i monotone-chain-convex-hull
import monotoneChainConvexHull from "monotone-chain-convex-hull";
      //凸包图层样式this.convexHullFill = new Fill({color: "rgba(255, 153, 0, 0.4)",});this.convexHullStroke = new Stroke({color: "rgba(204, 85, 0, 1)",width: 1.5,});
      //凸包图层this.clusterHulls = new VectorLayer({source: clusterSource,style: this.clusterHullStyle,});
 clusterHullStyle(cluster) {if (cluster !== this.hoverFeature) {return null;}const originalFeatures = cluster.get("features");const points = originalFeatures.map((feature) =>feature.getGeometry().getCoordinates());return new Style({geometry: new Polygon([monotoneChainConvexHull(points)]),fill: this.convexHullFill,stroke: this.convexHullStroke,});},

当鼠标移动到点图层时,显示凸包效果。 

  moveEvent(event) {this.clustersLayer.getFeatures(event.pixel).then((features) => {if (features[0] !== this.hoverFeature) {this.hoverFeature = features[0];this.clusterHulls.setStyle(this.clusterHullStyle);this.map.getTargetElement().style.cursor =this.hoverFeature && this.hoverFeature.get("features").length > 1? "pointer": "";}});},

然后是点线图层

this.clusterCircles = new VectorLayer({source: clusterSource,style: this.clusterCircleStyle,});

当前视图的缩放级别达到了最大缩放级别或者范围的宽度和高度都小于当前视图的分辨率      

往点线图层的style数组中添加两个样式。。

clusterCircleStyle(cluster, resolution) {if (cluster !== this.clickFeature || resolution !== this.clickResolution) {return null;}const clusterMembers = cluster.get("features");const centerCoordinates = cluster.getGeometry().getCoordinates();return this.generatePointsCircle( clusterMembers.length,cluster.getGeometry().getCoordinates(),resolution).reduce((styles, coordinates, i) => {const point = new Point(coordinates);const line = new LineString([centerCoordinates, coordinates]);styles.unshift(new Style({geometry: line,stroke: this.convexHullStroke,}));styles.push(this.clusterMemberStyle(new Feature({...clusterMembers[i].getProperties(),geometry: point,})));return styles;}, []);},

generatePointsCircle 方法根据聚类成员的数量、中心坐标和当前分辨率,生成一个圆周上的点坐标数组。 

generatePointsCircle(count, clusterCenter, resolution) {//计算圆周长度和每个点的角度步长const circumference =this.circleDistanceMultiplier * this.circleFootSeparation * (2 + count);let legLength = circumference / (Math.PI * 2); const angleStep = (Math.PI * 2) / count;const res = [];let angle;//调整线段长度 确保线段长度至少为 35,并根据分辨率进行调整。legLength = Math.max(legLength, 35) * resolution; //生成圆周上的点坐标for (let i = 0; i < count; ++i) {angle = this.circleStartAngle + i * angleStep;res.push([clusterCenter[0] + legLength * Math.cos(angle),clusterCenter[1] + legLength * Math.sin(angle),]);}return res;},

 点击事件时,获取当前点击的feature的边界值,定位到指定位置。

clickEvent(event) {this.clustersLayer.getFeatures(event.pixel).then((features) => {if (features.length > 0) {const clusterMembers = features[0].get("features");if (clusterMembers.length > 1) {const extent = createEmpty();clusterMembers.forEach((feature) =>extend(extent, feature.getGeometry().getExtent()));const view = this.map.getView();const resolution = this.map.getView().getResolution();//如果当前视图的缩放级别达到了最大缩放级别 如果范围的宽度和高度都小于当前视图的分辨率if (view.getZoom() === view.getMaxZoom() ||(getWidth(extent) < resolution && getHeight(extent) < resolution)) {this.clickFeature = features[0];this.clickResolution = resolution;this.clusterCircles.setStyle(this.clusterCircleStyle);} else {view.fit(extent, { duration: 500, padding: [50, 50, 50, 50] });}}}});},

完整代码:

<template><div class="box"><h1>Dynamic clusters</h1><div id="map"></div></div>
</template><script>
import Feature from "ol/Feature.js";
import GeoJSON from "ol/format/GeoJSON.js";
import Map from "ol/Map.js";
import View from "ol/View.js";
import {Circle as CircleStyle,Fill,Icon,Stroke,Style,Text,
} from "ol/style.js";
import { Cluster, Vector as VectorSource, XYZ } from "ol/source.js";
import { LineString, Point, Polygon } from "ol/geom.js";
import { Tile as TileLayer, Vector as VectorLayer } from "ol/layer.js";
import { createEmpty, extend, getHeight, getWidth } from "ol/extent.js";
import { fromLonLat } from "ol/proj.js";
// import convexHull from "convex-hull";
import monotoneChainConvexHull from "monotone-chain-convex-hull";
export default {name: "",components: {},data() {return {map: null,hoverFeature: null,convexHullFill: null,convexHullStroke: null,textFill: null,textStroke: null,innerCircle: null,outerCircle: null,darkIcon: null,lightIcon: null,circleDistanceMultiplier: 1,circleFootSeparation: 28,circleStartAngle: Math.PI / 2,clickFeature: null,clustersLayer: null,clusterHulls: null,clusterCircles: null,clickResolution:null,};},computed: {},created() {},mounted() {this.initMap();this.initStyle();this.addClusterLayers();},methods: {initMap() {const raster = new TileLayer({source: new XYZ({attributions:'Base map: <a target="_blank" href="https://basemap.at/">basemap.at</a>',url: "https://maps{1-4}.wien.gv.at/basemap/bmapgrau/normal/google3857/{z}/{y}/{x}.png",}),});this.map = new Map({layers: [raster],target: "map",view: new View({center: [0, 0],zoom: 2,maxZoom: 19,extent: [...fromLonLat([16.1793, 48.1124]),...fromLonLat([16.5559, 48.313]),],showFullExtent: true,}),});this.map.on("pointermove", this.moveEvent);this.map.on("click", this.clickEvent);},addClusterLayers() {const vectorSource = new VectorSource({format: new GeoJSON(),url: "https://openlayers.org/en/latest/examples/data/geojson/photovoltaic.json",});const clusterSource = new Cluster({attributions:'Data: <a href="https://www.data.gv.at/auftritte/?organisation=stadt-wien">Stadt Wien</a>',distance: 35,source: vectorSource,});//凸包图层this.clusterHulls = new VectorLayer({source: clusterSource,style: this.clusterHullStyle,});//聚合图层this.clustersLayer = new VectorLayer({source: clusterSource,style: this.clusterStyle,});//特定情况下的图层this.clusterCircles = new VectorLayer({source: clusterSource,style: this.clusterCircleStyle,});this.map.addLayer(this.clusterHulls);this.map.addLayer(this.clustersLayer);this.map.addLayer(this.clusterCircles);},initStyle() {//凸包图层样式this.convexHullFill = new Fill({color: "rgba(255, 153, 0, 0.4)",});this.convexHullStroke = new Stroke({color: "rgba(204, 85, 0, 1)",width: 1.5,});this.textFill = new Fill({color: "#fff",});this.textStroke = new Stroke({color: "rgba(0, 0, 0, 0.6)",width: 3,});this.innerCircle = new CircleStyle({radius: 14,fill: new Fill({color: "rgba(255, 165, 0, 0.7)",}),});this.outerCircle = new CircleStyle({radius: 20,fill: new Fill({color: "rgba(255, 153, 102, 0.3)",}),});this.darkIcon = new Icon({src: "data/icons/emoticon-cool.svg",});this.lightIcon = new Icon({src: "data/icons/emoticon-cool-outline.svg",});},clusterMemberStyle(clusterMember) {return new Style({geometry: clusterMember.getGeometry(),image:clusterMember.get("LEISTUNG") > 5 ? this.darkIcon : this.lightIcon,});},clusterCircleStyle(cluster, resolution) {if (cluster !== this.clickFeature || resolution !== this.clickResolution) {return null;}const clusterMembers = cluster.get("features");const centerCoordinates = cluster.getGeometry().getCoordinates();return this.generatePointsCircle( clusterMembers.length,cluster.getGeometry().getCoordinates(),resolution).reduce((styles, coordinates, i) => {const point = new Point(coordinates);const line = new LineString([centerCoordinates, coordinates]);styles.unshift(new Style({geometry: line,stroke: this.convexHullStroke,}));styles.push(this.clusterMemberStyle(new Feature({...clusterMembers[i].getProperties(),geometry: point,})));return styles;}, []);},generatePointsCircle(count, clusterCenter, resolution) {//计算圆周长度和每个点的角度步长const circumference =this.circleDistanceMultiplier * this.circleFootSeparation * (2 + count);let legLength = circumference / (Math.PI * 2); const angleStep = (Math.PI * 2) / count;const res = [];let angle;//调整线段长度 确保线段长度至少为 35,并根据分辨率进行调整。legLength = Math.max(legLength, 35) * resolution; //生成圆周上的点坐标for (let i = 0; i < count; ++i) {angle = this.circleStartAngle + i * angleStep;res.push([clusterCenter[0] + legLength * Math.cos(angle),clusterCenter[1] + legLength * Math.sin(angle),]);}return res;},clusterHullStyle(cluster) {if (cluster !== this.hoverFeature) {return null;}const originalFeatures = cluster.get("features");const points = originalFeatures.map((feature) =>feature.getGeometry().getCoordinates());return new Style({geometry: new Polygon([monotoneChainConvexHull(points)]),fill: this.convexHullFill,stroke: this.convexHullStroke,});},//黄色圆圈 内外两层 发光效果clusterStyle(feature) {const size = feature.get("features").length;//还有下级if (size > 1) {return [new Style({image: this.outerCircle,}),new Style({image: this.innerCircle,text: new Text({text: size.toString(),fill: this.textFill,stroke: this.textStroke,}),}),];}//没有下级const originalFeature = feature.get("features")[0];return this.clusterMemberStyle(originalFeature);},moveEvent(event) {this.clustersLayer.getFeatures(event.pixel).then((features) => {if (features[0] !== this.hoverFeature) {this.hoverFeature = features[0];this.clusterHulls.setStyle(this.clusterHullStyle);this.map.getTargetElement().style.cursor =this.hoverFeature && this.hoverFeature.get("features").length > 1? "pointer": "";}});},clickEvent(event) {this.clustersLayer.getFeatures(event.pixel).then((features) => {if (features.length > 0) {const clusterMembers = features[0].get("features");if (clusterMembers.length > 1) {const extent = createEmpty();clusterMembers.forEach((feature) =>extend(extent, feature.getGeometry().getExtent()));const view = this.map.getView();const resolution = this.map.getView().getResolution();//如果当前视图的缩放级别达到了最大缩放级别 如果范围的宽度和高度都小于当前视图的分辨率if (view.getZoom() === view.getMaxZoom() ||(getWidth(extent) < resolution && getHeight(extent) < resolution)) {this.clickFeature = features[0];this.clickResolution = resolution;this.clusterCircles.setStyle(this.clusterCircleStyle);} else {view.fit(extent, { duration: 500, padding: [50, 50, 50, 50] });}}}});},},
};
</script><style lang="scss" scoped>
#map {width: 100%;height: 500px;
}
.box {height: 100%;
}#info {width: 100%;height: 24rem;overflow: scroll;display: flex;align-items: baseline;border: 1px solid black;justify-content: flex-start;
}
</style>

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

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

相关文章

JAVA流程控制break,continue,goto

1.break在任何循环语句的主体成分&#xff0c;均可用break控制循环的流程。break用于强行退出循环&#xff0c;不执行循环中剩余的语句。&#xff08;break语句也在switch语句中使用&#xff09; 如图&#xff1a;break语句强行退出循环&#xff0c;结果输出1~30便结束&#xf…

两数之和-第13届蓝桥杯选拔赛Python真题精选

[导读]&#xff1a;超平老师的Scratch蓝桥杯真题解读系列在推出之后&#xff0c;受到了广大老师和家长的好评&#xff0c;非常感谢各位的认可和厚爱。作为回馈&#xff0c;超平老师计划推出《Python蓝桥杯真题解析100讲》&#xff0c;这是解读系列的第76讲。 两数之和&#xf…

Java面试八股之死锁和活锁的区别

死锁和活锁的区别 基本定义&#xff1a; 死锁&#xff08;Deadlock&#xff09;&#xff1a;指两个或多个线程互相等待对方释放资源&#xff0c;从而导致所有线程都无法继续执行的状态。每个线程至少持有一个资源&#xff0c;并等待另一个由其他线程持有的资源&#xff0c;形…

初始操作系统

概念&#xff1a; 1.系统资源的管理者&#xff1a;实质控制和管理整个计算机系统的硬件和软件资源&#xff0c;并合理地组织调度计算机地工作和资源的分配 2.向上层提供方便易用的服务&#xff1a;以提供给用户和其他软件方便接口和环境 封装思想&#xff1a;操作系统把一些丑…

阿里云服务器接入百度云防护后显示502原因

最近&#xff0c;发现很多使用了阿里云服务器的网站出现502的情况 经百度云防护技术排查发现阿里云机房对百度云防护的IP进行了拦截&#xff0c;原因近期可能是百度云防护的IP请求过于频繁&#xff0c;导致阿里云机房策略把百度云的IP当成了攻击IP。 解决办法是提交工单让阿里…

第三方模块的下载与安装

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 在进行Python程序开发时&#xff0c;除了可以使用Python内置的标准模块外&#xff0c;还有很多第三方模块可以被我们所使用。对于这些第三方模块&…

5.31——进军MYSQL

目录 简略版&#xff1a; 详解版&#xff1a; 一. myaql概述&#xff1a; 数据库&#xff1a; 数据库管理系统&#xff1a; SQL&#xff1a; 二. masql的安装&#xff1a; 启动与停止&#xff1a; 1. MYSQL提供的命令行 2. windows提供的命令行工具 三.数据模型 …

Claude 3可使用第三方API,实现业务流程自动化

5月31日&#xff0c;著名大模型平台Anthropic宣布&#xff0c;Claude3模型可以使用第三方API和工具。 这也就是说&#xff0c;用户通过文本提问的方式就能让Claude自动执行多种任务&#xff0c;例如&#xff0c;从发票中自动提取姓名、日期、金额等&#xff0c;该功能对于开发…

DVWA靶场搭建:Apache、MySQL、PHP、DVWA

最近为了能够较为真实地学习Web渗透的各种技术&#xff0c;就想着自己搭建一个专门用于学习的Web演练平台--DVWA“靶场”。 DVWA可以进行暴力&#xff08;破解&#xff09;、命令行注入、跨站请求伪造、文件包含、文件上传、不安全的验证码、SQL注入、SQL盲注、弱会话ID、XSS漏…

批量归一化(BN)和层归一化(LN)的区别

批量归一化&#xff08;Batch Normalization, BN&#xff09;和层归一化&#xff08;Layer Normalization, LN&#xff09;是深度学习中常用的两种归一化技术&#xff0c;它们主要用于解决训练过程中的内部协变量偏移问题&#xff0c;加速模型收敛和提高稳定性。 1. 为什么需要…

基于Java的工程项目管理系统的功能与技术优势 工程管理系统源码

在当今的工程领域&#xff0c;项目管理的高效协同和信息共享是提升工作效率、降低成本的关键。本文将向您介绍一款基于Java技术构建的工程项目管理系统&#xff0c;该系统采用前后端分离的先进技术框架&#xff0c;功能全面&#xff0c;能够满足不同角色的需求&#xff0c;从项…

如何制作一本温馨的电子相册呢?

随着科技的不断发展&#xff0c;电子相册已经成为了一种流行的方式来记录和分享我们的生活。一张张照片&#xff0c;一段段视频&#xff0c;都能让我们回忆起那些温馨的时光。那么&#xff0c;如何制作一本温馨的电子相册呢&#xff1f; 首先&#xff0c;选择一款合适的电子相册…

Vuforia AR篇(五)— 地平面检测

目录 前言一、什么是地平面识别&#xff1f;二、使用步骤三、示例代码四、效果五、总结 前言 在增强现实&#xff08;AR&#xff09;应用程序的开发中&#xff0c;地平面识别是一项关键技术&#xff0c;它允许虚拟对象与现实世界的地面进行互动。Vuforia 是一个功能强大的 AR …

【Linux】深入理解进程的优先级(Linux 2.6版本O(1)调度算法)

进程的优先级 【前置知识】一、进程的优先级(一&#xff09;为什么要有优先级&#xff1f;&#xff08;二&#xff09;进程的优先级的范围 二、操作系统是如何实现进程的优先级&#xff1f;&#xff08;Linux内核2.6版本O(1)调度算法&#xff09; 【前置知识】 首先我们要了解…

FFmpeg 中 Filters 使用文档介绍

描述 这份文档描述了由libavfilter库提供的过滤器Filters、源sources和接收器sinks。 滤镜介绍 FFmpeg通过libavfilter库启用过滤功能。在libavfilter中,一个过滤器可以有多个输入和多个输出。为了说明可能的类型,我们考虑以下过滤器图: 这个过滤器图将输入流分成两个流,然…

补上缺失的一环----一种数据库系统主动对外推送表的增删改实时变动数据的实践

在实践中&#xff0c;一些应用程序或模块需要实时获取某些数据库表的增删改变动数据。 对此需求&#xff0c;常见的方案有: 1、应用程序通过轮循查询数据库方式获取数据库表的增删改变动数据. 2、应用程序在把数据写入数据库表之前&#xff0c;通过事件方式向外通知数据库表的增…

OZON的选品工具,OZON选品工具推荐

在电商领域&#xff0c;选品一直是决定卖家成功与否的关键因素之一。随着OZON平台的崛起&#xff0c;越来越多的卖家开始关注并寻求有效的选品工具&#xff0c;以帮助他们在这个竞争激烈的市场中脱颖而出。本文将详细介绍OZON的选品工具&#xff0c;并推荐几款实用的辅助工具&a…

redis之发布与订阅

华子目录 什么是发布与订阅&#xff1f;常用命令psubscribe pattern1 [pattern2...]subscribe channel1 [channel2...]publish channel messagepunsubscribe pattern1 [pattern2...]unsubscribe [channel1 [channel2...]]pubsub subcommand argument1 [argument2...] 示例1示例…

ESP使用巴法云远程OTA(VScode + Platform io)

ESP使用巴法云远程OTA&#xff08;Platform&#xff09; 什么是OTA&#xff1a; OTA&#xff08;Over-the-AirTechnology&#xff09;即空中下载技术&#xff0c;是通过移动通信的空中接口实现对移动终端设备及SIM卡数据进行远程管理的技术。OTA升级是物联网&#xff08;IOT&am…

如何使用前端表格控件实现多数据源整合?

前言 作为表格产品的典型应用场景之一&#xff0c;几乎所有的行业都会存在类 Excel 报表开发这样的应用场景&#xff0c;而在这些应用场景中&#xff0c;经常会遇见下面的这些痛点&#xff1a; 报表数据往往来自多个不同的数据源&#xff0c;需要报表系统能够同时连接多个数据源…