当前位置:网站首页>Openlayers 聚合图、权重聚合图以及聚合图点击事件
Openlayers 聚合图、权重聚合图以及聚合图点击事件
2022-08-11 08:05:00 【Southejor】
Openlayers 聚合图、权重聚合图以及聚合图点击事件
OpenLayers 教程
在实际工作中,Openlayers 渲染数据的方式有很多种(WMS、瓦片、矢量数据等),一次性渲染较大数据量的情况下,需要做成静态切片,比如WMTS、TMS;或者矢量切片,比如 Geojson、mvt 等。
对于数据量不是很大的数据,常常使用 热力图、聚合图 的方式在前端渲染,能够更好的体现数据特征。
本示例基于实际项目中的应用,介绍: 加载聚合图、权重聚合图、聚合图参数、聚合图点击事件 等功能的用法。
PS:如果数据量很大的话,建议数据入库,使用数据库的聚合函数来实现。
Openlayers 聚合图、权重聚合图、聚合图事件
<html lang="en">
<head>
<meta charSet="utf-8">
<!--注意:openlayers 原版的比较慢,这里引起自己服务器版-->
<link rel="stylesheet" href="http://openlayers.vip/examples/css/ol.css" type="text/css">
<style> /* 注意:这里必须给高度,否则地图初始化之后不显示;一般是计算得到高度,然后才初始化地图 */ .map {
height: 700px; width: 100%; float: left; } </style>
<!--注意:openlayers 原版的比较慢,这里引起自己服务器版-->
<script src="http://openlayers.vip/examples/resources/ol.js"></script>
<script src="http://openlayers.vip/examples/resources/jquery-3.5.1.min.js"></script>
<script src="./tiandituLayers.js"></script>
<title>OpenLayers example</title>
</head>
<body>
<h2>OpenLayers Cluster</h2>
<!--地图容器,需要指定 id -->
<div id="map" class="map"></div>
<script type="text/javascript"> var map = new ol.Map({
// 地图容器 target: 'map', // 地图图层,比如底图、矢量图等 layers: [ getIMG_CLayer(), getIBO_CLayer(), getCIA_CLayer(), ], // 地图视野 view: new ol.View({
projection: "EPSG:4326", // 定位 center: [115.67724700667199, 37.73879478106912], // 缩放 zoom: 6, maxZoom: 18, minZoom: 1, }) }); /** * @todo wkt格式数据转化成图形对象 * @param {string} wkt "POINT(112.7197265625,39.18164062499999)" 格式数据 * @param {string|Projection} sourceCode 源投影坐标系 * @param {string|Projection} targetCode 目标投影坐标系 * @returns {Feature} */ function getFeatureByWKT(wkt, sourceCode, targetCode) {
try {
let view = map.getView(); if (!wkt) {
return null; } let format = new ol.format.WKT(); let feature; feature = format.readFeature(wkt, {
featureProjection: targetCode || view.getProjection(), dataProjection: sourceCode || view.getProjection(), }); return feature; } catch (e) {
console.log(e); return null; } } /** * @todo 颜色十六进制转为 rgba * @param sColor 格式数据 * @param opacity * @returns rgba颜色字符串 */ function colorToRgb(sColor, opacity) {
//用于十六进制颜色和rgb转换的正则 var REG = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/; sColor = sColor.toLowerCase(); if (/^[A-Za-z]+$/.test(sColor)) return sColor; if (sColor && REG.test(sColor)) {
if (sColor.length === 4) {
let sColorNew = "#"; for (let i = 1; i < 4; i += 1) {
sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1)); } sColor = sColorNew; } //处理六位的颜色值 let sColorChange = []; for (let i = 1; i < 7; i += 2) {
sColorChange.push(parseInt("0x" + sColor.slice(i, i + 2))); } if (opacity) sColorChange.push(opacity); return "rgba(" + sColorChange.join(",") + ")"; // return sColorChange; } else {
return sColor; } }; // 地图事件 function clickFunction(evt) {
let feature = map.forEachFeatureAtPixel(evt.pixel, function (feature, layerVetor) {
return feature.getProperties().features; }); // 图形要素的点击事件 // 这里可以使用气泡框展示信息 if (feature && feature.length == 1) {
console.log(feature[0]); alert('点击了:' + feature[0].get('name')); } else {
feature && console.log(feature); feature && alert('点击了包含:' + feature.length + ' 个图形要素的聚合图!'); } } // 初始化点击事件标记 var initClickFlag = false; // 点击事件 function activateClickFunc() {
initClickFlag && alert('已开启点击事件!'); map.on('click', clickFunction); initClickFlag = true; } // 关闭点击事件 function shutDownClick() {
alert('已关闭点击事件!'); map.un('click', clickFunction); } // 点线面数组 var features = undefined; // 聚合图图层对象 var clusterLayer = undefined; // 资源对象 var vectorSource = undefined; // 聚合图对象 var clusterSource = undefined; // 初始化聚合图 function initCluster() {
features = []; // 模拟数据 for (var i = 1; i <= 15; i++) {
var feature = getFeatureByWKT( "POINT(" + (113 + (i / 10)) + " " + (35 + (i / 10)) + ")" ); var feature2 = getFeatureByWKT( "POINT(" + (113 + ((i + 5) / 10)) + " " + (35 + (i / 10)) + ")" ); var feature3 = getFeatureByWKT( "POINT(" + (113 + ((i + 9) / 10)) + " " + (35 + (i / 10)) + ")" ); var point = new ol.style.Style({
// 点样式 image: new ol.style.Icon({
// 允许跨域,如果不设置,打印地图不会打印 crossOrigin: 'anonymous', // 标注图片和文字之间的距离 anchor: [0.5, 0], // 图片的偏移 offset: [0.2, 0], // 图片的锚点,一般来说,都是右下角 anchorOrigin: 'bottom-right', //图标的url src: "http://api.tianditu.gov.cn/v4.0/image/marker-icon.png", scale: 1, }) }); feature.setStyle(point); feature.set('name', 'feature1-' + i); feature.set('capability', i / 15); feature2.setStyle(point); feature2.set('name', 'feature2-' + i); feature2.set('capability', i / 15); feature3.setStyle(point); feature3.set('name', 'feature3-' + i); feature3.set('capability', i / 15); features.push(feature) features.push(feature2) features.push(feature3) } /** * 资源 */ vectorSource = new ol.source.Vector({
}); // 聚合图 clusterSource = new ol.source.Cluster({
wrapX: false, source: vectorSource, }); // 图层 clusterLayer = new ol.layer.Vector({
source: clusterSource, zIndex: 1, }); map.addLayer(clusterLayer); } /** * todo 增加聚合图 * @param dynamicData (参数是features) */ function addData(dynamicData, distance, original) {
// 最大图形要素数量 var maxFeatureCount; // 当前分辨率 var currentResolution; // 普通样式,普通小圆圈 var originalStyle = function (feature) {
var features = feature.get('features'); var size = features.length; return new ol.style.Style({
image: new ol.style.Circle({
radius: 30, stroke: new ol.style.Stroke({
color: '#fff' }), fill: new ol.style.Fill({
color: '#969696' }) }), text: new ol.style.Text({
text: size.toString(), fill: new ol.style.Fill({
color: '#fff' }) }) }); } // 动态样式样式,根据权重和数量计算 var varyStyle = function (feature) {
var originalFeatures = feature.get('features'); var size = feature.get('features').length; var capability_avg = 0; var textName = ""; var j = (void 0), jj = (void 0); for (j = 0, jj = originalFeatures.length; j < jj; ++j) {
capability_avg += (originalFeatures[j].get("capability") || 1) textName = originalFeatures[j].get("name") || ""; } capability_avg = (capability_avg / size).toFixed(2); var round = getPointArray(capability_avg); var opacity = Math.min(0.8, 0.4 + (size / maxFeatureCount)); var style = new ol.style.Style({
image: new ol.style.Circle({
radius: feature.get('radius'), fill: new ol.style.Fill({
color: colorToRgb(round.split(",")[1], opacity) }) }), text: new ol.style.Text({
// text: textName ? (textName + ":" + capability_avg) : feature.get('radius'), text: "权重平均值:" + capability_avg, font: 'normal bold 14px Arial,sans-serif', fill: new ol.style.Fill({
color: '#fff' }), stroke: new ol.style.Stroke({
color: 'rgba(0, 0, 0, 0.6)', width: 3 }) }) }); return style; } // 单体 feature 样式 var featureStyle = function (feature) {
var originalFeatures = feature.get('features'); if (originalFeatures.length != 1) {
return; } var originalFeature = originalFeatures[0]; var style = originalFeature.getStyle(); style && style.setText( new ol.style.Text({
text: originalFeature.get("name"), // 偏移 offsetX: 0, offsetY: -54, // 居中 textAlign: 'center', // 比例 scale: 1, textBaseline: 'middle', // 边距 padding: [2, 2, 2, 2], // 覆盖显示:即文字超过多边形也会显示 overflow: true, // 字体颜色 fill: new ol.style.Fill({
color: 'rgba(51,51,51, 1)' }), // 字体边框,可以配合 fill 是文字高亮 stroke: new ol.style.Stroke({
color: 'rgba(0, 255, 255, 0.8)', width: 2, }), // 背景色 backgroundFill: new ol.style.Fill({
color: 'rgba(252,254,255, 1)' }), }) ) return style; } // 样式方法 function styleFunction(feature, resolution) {
// 如果是拖动地图,则不重新渲染样式 if (resolution != currentResolution) {
calculateClusterInfo(resolution); currentResolution = resolution; } var style; var size = feature.get('features').length || 0; // size大于1,则表示是聚合状态 if (size > 1) {
if (original == true) {
style = originalStyle(feature); } else {
style = varyStyle(feature); } // size 等于1,表示是单体 feature } else if (size == 1) {
style = featureStyle(feature); } return style; } // 计算聚合图样式信息 var calculateClusterInfo = function () {
if (!clusterLayer) {
return; } maxFeatureCount = 0; var features = clusterLayer.getSource().getFeatures(); var feature, radius; for (var i = features.length - 1; i >= 0; --i) {
feature = features[i]; var originalFeatures = feature.get('features'); // 计算权重 var capability = 0; var j = (void 0), jj = (void 0); for (j = 0, jj = originalFeatures.length; j < jj; ++j) {
// 这是使用 capability 自定义属性的值计算权重 capability += (originalFeatures[j].get("capability") || 1) } // 根据实际的数据量,调整聚合显示半径的大小 // PS:这个需要更新项目实际调整 if (originalFeatures.length < 10) {
radius = capability + originalFeatures.length; while (radius > 100) {
radius = radius / 10; } radius = radius + 10; } else if (originalFeatures.length >= 10 && originalFeatures.length <= 50) {
radius = capability + originalFeatures.length; while (radius > 100) {
radius = radius / 10; } radius = radius + 20; } else if (originalFeatures.length > 100 && originalFeatures.length <= 5000) {
radius = capability + originalFeatures.length; while (radius > 100) {
radius = radius / 10; } } else if (originalFeatures.length > 5000 && originalFeatures.length <= 10000) {
radius = capability + originalFeatures.length; while (radius > 100) {
radius = radius / 10; } } else if (originalFeatures.length > 10000) {
radius = capability + originalFeatures.length; while (radius > 100) {
radius = radius / 10; } radius = radius; } // 取二者最大值 maxFeatureCount = Math.max(maxFeatureCount, jj); feature.set('radius', radius); } }; // 获取聚合图颜色 // 自定义颜色(图例和颜色以逗号拼接) var getPointArray = function (v) {
if (v >= 0.8 && v <= 1) return '0.8-1.0,#FF0000' else if (v >= 0.6 && v < 0.8) return '0.6-0.8,#FFFF00' else if (v >= 0.4 && v < 0.6) return '0.4-0.6,#DAA520' else if (v >= 0.2 && v < 0.4) return '0.2-0.4,#0000FF' else if (v >= 0 && v < 0.2) return '0-0.2,#228B22' } // 设置聚合距离,也就是半径范围内聚合 clusterSource.setDistance(distance); // 添加数据 vectorSource.addFeatures(features); // 设置样式 clusterLayer && clusterLayer.setStyle(styleFunction) } // 添加聚合图 // flag, true 为加载原始样式,其他为加载权重样式 function addCluster(flag) {
closeCluster(); initCluster(); flag ? addData(features, 60, flag) : addData(features, 40); } // 关闭聚合图 function closeCluster() {
clusterLayer && map.removeLayer(clusterLayer); clusterLayer = undefined; } // 默认加载原始聚合图 addCluster(true); // 默认开始点击事件 activateClickFunc(); </script>
<button id="addCluster" onClick="addCluster(true)">添加聚合图</button>
<button id="addWeightCluster" onClick="addCluster()">添加权重聚合图</button>
<button id="closeCluster" onClick="closeCluster()">关闭聚合图</button>
<button id="activateClickFunc" onClick="activateClickFunc()">开启点击事件</button>
<button id="shutDownClick" onClick="shutDownClick()">关闭点击事件</button>
</body>
</html>
PS:点击弹出气泡框可参考 Openlayers 自定义气泡框以及定位到气泡框
在线示例
Openlayers 聚合图:Openlayers-cluster
边栏推荐
- Redis source code: how to view the Redis source code, the order of viewing the Redis source code, the sequence of the source code from the external data structure of Redis to the internal data structu
- Project 1 - PM2.5 Forecast
- Active users of mobile banking grew rapidly in June, hitting a half-year high
- 基于微信小程序的租房小程序
- 关于#sql#的问题:怎么将下面的数据按逗号分隔成多行,以列的形式展示出来
- leetcode: 69. Square root of x
- 兼容并蓄广纳百川,Go lang1.18入门精炼教程,由白丁入鸿儒,go lang复合容器类型的声明和使用EP04
- 2022-08-10 mysql/stonedb-slow SQL-Q16-time-consuming tracking
- 进阶-指针
- 项目2-年收入判断
猜你喜欢
【TA-霜狼_may-《百人计划》】图形3.7.2 command buffer简
1081 Check Password (15 points)
基于微信小程序的租房小程序
笔试题大疆08.07
IQUNIX A80 exploring TTC金粉 初体验
Four operations in TF
Project 1 - PM2.5 Forecast
JRS303-Data Verification
2022-08-10:为了给刷题的同学一些奖励,力扣团队引入了一个弹簧游戏机, 游戏机由 N 个特殊弹簧排成一排,编号为 0 到 N-1, 初始有一个小球在编号 0 的弹簧处。若小球在编号为 i 的弹
2022 China Soft Drink Market Insights
随机推荐
为什么会没有内存了呢
[Recommender System]: Overview of Collaborative Filtering and Content-Based Filtering
快速幂,逆元的求解
1081 Check Password (15 points)
《剑指offer》题解——week3(持续更新)
我的创作纪念日丨感恩这365天来有你相伴,不忘初心,各自精彩
经典论文-MobileNet V1论文及实践
matrix multiplication in tf
oracle数据库中列转行,列会有变化
leetcode: 69. Square root of x
【TA-霜狼_may-《百人计划》】图形3.7.2 command buffer简
数据库无法启动,报无法分配内存,怎么处理
分门别类输入输出,Go lang1.18入门精炼教程,由白丁入鸿儒,go lang基本数据类型和输入输出EP03
go-grpc TSL authentication solution transport: authentication handshake failed: x509 certificate relies on ... ...
3.2-分类-Logistic回归
Machine Learning Summary (2)
研发了 5 年的时序数据库,到底要解决什么问题?
抽象类和接口
Kotlin算法入门求回文数数算法优化二数字生成规则
租房小程序