结合ace编辑器实现MapboxGL热力图样式在线配置

概述

MapboxGL热力图的配置参数并不多,但是有时候为了或得一个比较好用的热力图配置参数,我们不得不改代码再预览,显得尤为麻烦,为方便配置,实现实时预览,本文使用ace实现了一个热力图样式在线配置页面。

效果

image.png

实现

1. 技术栈

  • Vue3 + Element Plus
  • ace Editor
  • mapboxGL

2. 实现功能

  • csv、json、geojson数据上传并解析
  • mapboxGL热力图
  • 热力图样式编辑与实时预览

3. 实现

3.1 交互界面

<template><div class="tips"><b>说明:</b>实现热力图样式的配置与预览。</div><div class="container"><div class="setting-panel"><div class="title">配置参数</div><div class="content"><el-formlabel-width="0":model="styleFormData"><el-form-item label=""><div class="label">Radius<div class="tooltip"><el-icon><InfoFilled /></el-icon><div class="tooltips"><p>Radius of influence of one heatmap point in pixels. Increasing the value makes the heatmap smoother, but less detailed.</p></div></div></div><el-input class="my-input" size="small" :rows="2" type="textarea" v-model="styleFormData.radius" /></el-form-item><el-form-item label=""><div class="label">Color<div class="tooltip"><el-icon><InfoFilled /></el-icon><div class="tooltips"><p>Defines the color of each pixel based on its density value in a heatmap. Should be an expression that uses ["heatmap-density"] as input.</p></div></div></div><el-input class="my-input" size="small" :rows="4" type="textarea" v-model="styleFormData.color" /></el-form-item><el-form-item label=""><div class="label">Weight<div class="tooltip"><el-icon><InfoFilled /></el-icon><div class="tooltips"><p>A measure of how much an individual point contributes to the heatmap. A value of 10 would be equivalent to having 10 points of weight 1 in the same spot. Especially useful when combined with clustering.</p></div></div></div><el-input class="my-input" size="small" :rows="2" type="textarea" v-model="styleFormData.weight" /></el-form-item><el-form-item label=""><div class="label">Intensity<div class="tooltip"><el-icon><InfoFilled /></el-icon><div class="tooltips"><p>Similar to `heatmap-weight` but controls the intensity of the heatmap globally. Primarily used for adjusting the heatmap based on zoom level.</p></div></div></div><el-input class="my-input" size="small" :rows="2" type="textarea" v-model="styleFormData.intensity" /></el-form-item><el-form-item label=""><div class="label">Opacity<div class="tooltip"><el-icon><InfoFilled /></el-icon><div class="tooltips"><p>The global opacity at which the heatmap layer will be drawn.</p></div></div></div><el-input class="my-input" size="small" :rows="2" type="textarea" v-model="styleFormData.opacity" /></el-form-item></el-form></div><div class="title">JSON编辑器<div class="tools"><el-button size="small" @click="copyStyle">复制</el-button></div></div><div class="content code" id="codeEditor"></div></div><div class="main-panel"><div class="data-panel"><el-uploaddragref="file"action="''":multiple="false":auto-upload="false":limit="1":on-exceed="handleExceed":on-change="changeDataFile":accept="'.csv,.json,.geojson'"><div class="el-upload__text">拖动文件到此或 <em>点击上传</em></div><template #tip><div class="el-upload__tip">可上传csv、json、geojson等格式点数据,如为csv、json需包含lon,lat字段,如添加<b style="color: red">权重</b>,需<b style="color: red"></b>字段</div></template></el-upload></div><MapComponent :is-tools="false" @map-loaded="mapLoaded" style="height: 100%;"></MapComponent></div></div>
</template><style scoped lang="scss">
@import "../../assets/common/style";
.container {margin-top: 1.5rem;height: calc(100% - 3.5rem);position: relative;display: flex;flex-direction: row;.main-panel {flex-grow: 1;height: calc(100% - 1.8rem);position: relative;.data-panel {padding: 0.8rem;background-color: white;position: absolute;top: 1rem;right: 1rem;z-index: 99;width: 25rem;}}.setting-panel {width: 25rem;height: 100%;box-shadow: 0 0 5px #ccc;box-sizing: border-box;margin-right: 1.5rem;}.title {padding: 0.6rem 1.2rem;font-weight: bold;font-size: 1.1rem;border: 1px solid #efefef;}.content {padding: 1.2rem 1.2rem 0 1.2rem;&.code {height: calc(100% - 33.7rem)}}.tools {float: right;}.label, .my-input {display: inline-block;width: calc(100% - 7rem);.el-input__wrapper {width: 100%;}}.label {width: 6rem;height: 100%;line-height: 1.8;text-align: right;padding-right: 0.6rem;}.tooltip {display: inline-block;cursor: pointer;position: relative;&:hover {.tooltips {display: block;}}.tooltips {display: none;position: absolute;left: -8px;top: 22px;background-color: rgba(0,0,0,0.6);color: #fff;border-radius: 3px;z-index: 999;padding: 0.5rem;width: 17rem;white-space: normal;font-size: 12px;p {width: 100%;word-break: break-word;margin: 0;text-align: left;line-height: 1.5;}&:before {content: ' ';width: 0;height: 0;border: 5px solid transparent;border-bottom-color: rgba(0,0,0,0.6);position: absolute;left: 10px;top: -10px;}}}
}
</style>

3.2 数据上传与解析

changeDataFile(file, fileList) {uploadFile = filethis.showData()
},
handleExceed(files) {this.$refs.file.clearFiles()this.$refs.file.handleStart(files[0])
},
showData() {const that = thisif(!uploadFile) {ElMessage({message: '未上传文件!',type: 'warning',})return}const fileType = uploadFile.name.split('.')[1]const reader = new FileReader();reader.readAsText(uploadFile.raw,'GB2312');reader.onload = function () {const fileContent = reader.result;let geojson = nullif(fileType === 'csv') {let {geomType, features} = csv2geojson(fileContent)geomType = geomType.toLowerCase()if (geomType.indexOf('point') !== -1) {geojson = new Geojson(features)}} else if(fileType === 'json') {let {geomType, features} = json2Geojson(JSON.parse(fileContent))geomType = geomType.toLowerCase()if (geomType.indexOf('point') !== -1) {geojson = new Geojson(features)}} else {geojson = JSON.parse(fileContent)}if(geojson) {map.getSource(`${DATA_LAYER}-source`).setData(geojson);that.styleUpdate()const [xmin, ymin, xmax, ymax] = turf.bbox(geojson);const bbox = [[xmin, ymin], [xmax, ymax]];map.fitBounds(bbox, {padding: {top: 100, bottom:100, left: 150, right: 150},duration: 500})}}
},

csv2geojson和json2Geojson转换方法如下:

import {Feature} from './geojson'
import { wktToGeoJSON } from "@terraformer/wkt"export function csv2geojson(csvContent) {const splitChar = csvContent.indexOf('\r') ? '\r' : '\r\n'const lines = csvContent.split(splitChar).filter(v => Boolean(v))const headers = lines[0].split(',').map(header => header.toLowerCase())let geomType = '', features = [], isWkt = falseif(headers.includes('lon') && headers.includes('lat')) {geomType = 'Point'} else if(headers.includes('wkt')) {isWkt = trueconst geom = wktToGeoJSON(lines[1].split(',')[headers.indexOf('wkt')])geomType = geom.type}if(geomType) {for (let i = 1; i < lines.length; i++) {const line = lines[i].split(',')if(line.length === headers.length) {let props = {}headers.forEach((header, index) => {if(!['wkt', 'lon', 'lat'].includes(header))  props[header] = line[index]})const lonIndex = headers.indexOf('lon')const latIndex = headers.indexOf('lat')const geometry = isWkt ?  wktToGeoJSON(line[headers.indexOf('wkt')]) : [line[lonIndex], line[latIndex]].map(Number)features.push(new Feature(geomType, props, geometry))}}}return {headers,geomType,features}
}export function json2Geojson(json) {if(!Array.isArray(json)) throw new Error('数据格式错误')const geomType = 'Point'const features = json.map(d => {const {lon, lat} = dreturn new Feature(geomType, d, [lon, lat])})return {geomType,features}
}

3.3 样式编辑与实时预览

initEditor() {editor = ace.edit("codeEditor");const theme = "github";const language = "json";editor.setTheme("ace/theme/" + theme);editor.session.setMode("ace/mode/" + language);editor.setFontSize(14);editor.setReadOnly(false);editor.setOption("wrap", "free");editor.setOptions({enableBasicAutocompletion: true,enableSnippets: true,enableLiveAutocompletion: true,tabSize: 2});this.styleUpdate()
},
styleUpdate() {const style = {"heatmap-radius": this.styleFormData.radius,"heatmap-color": this.styleFormData.color,"heatmap-weight": this.styleFormData.weight,"heatmap-intensity": this.styleFormData.intensity,"heatmap-opacity": this.styleFormData.opacity,}let isOk = truefor (const styleKey in style) {let val = style[styleKey]if(typeof val === 'string') val = val.replace(/'/g, '"')if(val === '') isOk = falseif(styleKey !== 'heatmap-color' && ! Number.isNaN(Number(val))) style[styleKey] = Number(vaelse style[styleKey] = JSON.parse(val || '{}')if(styleKey === 'heatmap-opacity' && style[styleKey] > 1) style[styleKey] = 1if(styleKey === 'heatmap-opacity' && style[styleKey] < 0) style[styleKey] = 0}if(isOk) {editor.setValue(JSON.stringify(style, null, 2))if(window.map) {if(map.getLayer(`${DATA_LAYER}-layer`)) map.removeLayer(`${DATA_LAYER}-layer`)map.addLayer({id: `${DATA_LAYER}-layer`,type: "heatmap",source: `${DATA_LAYER}-source`,paint: style});}}
},

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

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

相关文章

MapReduce分布式计算(二)

MapReduce工作流程 原始数据File 1T数据被切分成块存放在HDFS上&#xff0c;每一个块有128M大小 数据块Block hdfs上数据存储的一个单元,同一个文件中块的大小都是相同的 因为数据存储到HDFS上不可变&#xff0c;所以有可能块的数量和集群的计算能力不匹配 我们需要一个动态…

Java面试题3

[TOC]目录 1. spring事务 事务简介 事务在逻辑上是一组操作&#xff0c;要么执行&#xff0c;要不都不执行。主要是针对数据库而言的&#xff0c;比如说 MySQL。 为了保证事务是正确可靠的&#xff0c;在数据库进行写入或者更新操作时&#xff0c;就必须得表现出 ACID 的 4 …

ESP8266-NodeMCU搭建Arduino IDE开发环境

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、准备工作二、搭建步骤1.打开Arduino IDE 首选项2.打开Arduino IDE的“开发板管理器”3.在Arduino IDE的开发板菜单中找到“NodeMCU开发板”4.设置Arduino IDE的…

mongodb原理

一&#xff1a;MongoDB的优点和缺点 优点 面向文档存储(类JSON数据模式简单而强大)动态查询全索引支持,扩展到内部对象和内嵌数组查询记录分析快速,就地更新高效存储二进制大对象 (比如照片和视频)复制和故障切换支持Auto- Sharding自动分片支持云级扩展性MapReduce 支持复杂…

Redis:redis基于各大实战场景下的基本使用

文章目录 前言String 命令实战1.业务缓存对应redis中的指令伪代码 2.分布式锁对应redis中的指令伪代码 3.限流对应redis中的指令伪代码 List 命令实战1.提醒功能对应Redis中的指令伪代码 2.热点列表对应Redis中的指令伪代码 Hash 命令实战1.用户资料缓存对应redis中的指令伪代码…

Qt实现拖拽功能(支持拖放文件、拖放操作)

目录 拖放Qt程序接受其他程序的拖拽部件/控件之间相互拖放总结 拖放 拖放是在一个应用程序内或者多个应用程序之间传递信息的一种直观的现代操作方式。除了为剪贴板提供支持外,通常它还提供数据移动和复制的功能。 拖放操作包括两个截然不同的动作:拖动和放下。Qt窗口部件可以…

MATLAB 之 Simulink 子系统及其封装

这里写目录标题 一、子系统及其封装1. 子系统的创建1.1 通过 Subsystem 模块建立子系统1.2 通过已有的模块建立子系统 2. 子系统的条件执行2.1 使能子系统2.2 触发子系统2.3 使能加触发子系统 3. 子系统的封装3.1 lcon & Ports 选项卡的参数设置3.2 Parameters & Dialo…

控制请求来源的HTML Meta标签 - Referrer详解

83. 控制请求来源的HTML Meta标签 - Referrer详解 在Web开发中&#xff0c;为了保护用户的隐私和安全&#xff0c;控制请求的来源信息是至关重要的。HTML中的<meta>标签提供了一种简单而有效的方式来控制请求的来源&#xff0c;其中包括Referrer&#xff08;引荐者&…

SpringMVC原理分析 | Controller配置、RestFul风格

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; Controller配置 控制器Controller 控制器复杂提供访问应用程序的行为&#xff0c;通常通过接口定义或注释定义的两种方法实现控制器负责解析用户的请求并将其转换为一…

SpringBoot整合定时任务技术Quartz

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ RequestMapping注解 &#x1f680;Quartz应用场…

在教育领域中使用ChatGPT有哪些优点?

人工智能在教育领域的应用正在迅速增加。OpenAI于2022年11月开发的聊天机器人ChatGPT在全球范围内广受欢迎。 由于其受欢迎程度以及生成类似人类问题的回答的能力&#xff0c;ChatGPT正在成为许多学习者和教育工作者值得信赖的伴侣。然而&#xff0c;与任何新兴技术一样&#x…

2019年全国硕士研究生入学统一考试管理类专业学位联考逻辑试题——纯享题目版

&#x1f3e0;个人主页&#xff1a;fo安方的博客✨ &#x1f482;个人简历&#xff1a;大家好&#xff0c;我是fo安方&#xff0c;考取过HCIE Cloud Computing、CCIE Security、CISP等证书。&#x1f433; &#x1f495;兴趣爱好&#xff1a;b站天天刷&#xff0c;题目常常看&a…