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

news2024/12/23 18:37:18

概述

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-form
          label-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-upload
          drag
          ref="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 = file
  this.showData()
},
handleExceed(files) {
  this.$refs.file.clearFiles()
  this.$refs.file.handleStart(files[0])
},
showData() {
  const that = this
  if(!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 = null
    if(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 = false
  if(headers.includes('lon') && headers.includes('lat')) {
    geomType = 'Point'
  } else if(headers.includes('wkt')) {
    isWkt = true
    const 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} = d
    return 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 = true
  for (const styleKey in style) {
    let val = style[styleKey]
    if(typeof val === 'string') val = val.replace(/'/g, '"')
    if(val === '') isOk = false
    if(styleKey !== 'heatmap-color' && ! Number.isNaN(Number(val))) style[styleKey] = Number(va
    else style[styleKey] = JSON.parse(val || '{}')
    if(styleKey === 'heatmap-opacity' && style[styleKey] > 1) style[styleKey] = 1
    if(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.coloradmin.cn/o/708779.html

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

相关文章

MSF之信息收集及漏洞利用

MSF之信息收集及漏洞利用 一、Metasploit简介二、Metasploit安装三、安装postgresql数据库四、KaIi-msfdb-Postgresql报错排查处理五、Metasploit-启动六、Metasploit-目录结构六、Metasploit-模块七、Metasploit-信息收集7.1、db_nmap/nmap7.2、Metasploit auxiliary7.2.1、端…

【STM32】步进电机及其驱动(ULN2003驱动28BYJ-48丨按键控制电机旋转)

本篇文章包含的内容 一、步进电机的结构和工作原理1.1 步进控制系统的组成1.2 步进电机简介1.3 步进电机的分类1.4 步进电机的工作原理1.4.1 单极性步进电机&#xff08;5线4相&#xff09;1.4.2 双极性步进电机&#xff08;4线2相&#xff09;1.4.3 细分器驱动原理 1.5 步进电…

hcia回顾复习

一、OSI七层参考模型 OSI/RM 开放式系统互联参考模型 由ISO ---- 国际标准化组织 — 1979提出 核心思想 分层 :上层协议再下层协议提供服务的基础上再提供增值服务。 应用层 — 提供各种应用服务.可以将抽象语言转换为编码 .应用程序 APP&#xff1a;通过人机交互提供&#xff…

Win10打字输入法不显示输入框怎么办?

Win10的打字输入法是我们日常计算机使用中必不可少的工具之一&#xff0c;然而&#xff0c;有时候在使用过程中可能会遇到打字输入法不显示输入框的问题&#xff0c;这给我们的输入和操作带来了很大的困扰&#xff0c;如果您也遇到了这个问题&#xff0c;不要担心&#xff0c;以…

Linux--获取某个区间文本的指令:head和tail

Linux--获取文本前n行的指令&#xff1a;head 语法&#xff1a; head 选项 文件名 功能&#xff1a; head 用来显示档案的开头至标准输出中&#xff0c;默认head命令打印其相应文件的开头10行。 选项&#xff1a; -n <行数> 显示的行数 示例&#xff1a; ①生成默…

【UE5 Cesium】05-Cesium for Unreal 在子关卡中添加Actor

上一篇&#xff1a;【UE5 Cesium】04-Cesium for Unreal 将不同地区的倾斜摄影作为不同子关卡 步骤 首先将关卡切换到“DenverRooftop” 添加一个“立方体” 将关卡切换到“Globe” 然后再向场景中添加一个“椎体” 此时如果我们将关卡切换到“Boston”&#xff0c;只能看到“…

如何高效获取嵌入式系统知识和技能

学习嵌入式系统的方法&#xff1a; 设定明确的目标&#xff1a;在学习嵌入式系统之前&#xff0c;明确自己的学习目标和期望结果。这可以帮助你更有针对性地选择学习材料和项目&#xff0c;并保持专注和动力。 分解学习计划&#xff1a;将学习过程分解成小的可管理的任务和阶段…

SQL注入经验方法总结

SQL注入 先判断是哪种数据库。再进行后续操作。 SQL注入漏洞产生的原理 web应用程序&#xff0c;对用户输入的语句没有做严格的过滤&#xff0c;导致被输入的语句被拼接成恶意代码的SQL语句进入数据库中查询&#xff0c;修改信息等。 所以SQL注入漏洞需要的条件&#xff1a…

chatglm2 本地部署中遇到的问题

在本地GPU部署的时候&#xff0c;发现了报错&#xff0c; ModuleNotFoundError: No module named transformers_modules.chatglm2-6b 但是自己路径都是正确的&#xff0c; 确实是按照双斜杠来写的路径。 但依旧报错 最后发现是安装的 transformers 包的版本太新导致的。 …

抖音SEO账号矩阵系统源码

一、抖音SEO账号矩阵系统源码思路 1. 数据采集与分析 2. 排名算法设计 3. 用户管理模块 4. 内容推荐系统 二、抖音矩阵系统源码功能概述 &#xff08;1&#xff09;多平台多账号管理,支持抖音&#xff0c;快手&#xff0c;好看视频&#xff0c;B站&#xff0c;西瓜&#x…

jdbc获取数据库元数据信息

DatabaseMetaData 接口&#xff1a; 获取数据库&#xff0c;&#xff0c;&#xff0c;表&#xff0c;&#xff0c;列&#xff0c;&#xff0c;等元数据信息 jdbc使用&#xff1a; // 获取一个连接 Connection connection DriverManager.getConnection(url,username,password)…

葡萄酒数据可视化分析

葡萄酒数据可视化分析 必应壁纸供图 数据集&#xff1a;https://download.csdn.net/download/weixin_53742691/87982219 import pandas as pd import seaborn as sns import matplotlib.pyplot as pltwine pd.read_csv("wine_quality/wine_edited.csv") wine.hea…

chatgpt赋能python:用Python来制作动画

用Python来制作动画 Python是一种高级编程语言&#xff0c;可以用于许多任务&#xff0c;包括数据分析、网络编程&#xff0c;甚至是制作动画。在这篇文章中&#xff0c;我们将讨论如何使用Python来制作动画。 Python中的动画库 Python中有许多用于制作动画的库。其中最流行…

论文笔记--Goat: Fine-tuned LLaMA Outperforms GPT-4 on Arithmetic Tasks

论文笔记--Goat: Fine-tuned LLaMA Outperforms GPT-4 on Arithmetic Tasks 1. 文章简介2. 文章概括3 文章重点技术3.1 LLM的选择3.2 算数任务的可学习性(learnability)3.3 大模型的加减乘除 4. 数值实验结果5. 文章亮点6. 原文传送门7. References 1. 文章简介 标题&#xff…

选读SQL经典实例笔记01_检索和排序

1. 在WHERE子句中引用别名列 1.1. 当表里的某些列没有被恰当命名的时候&#xff0c;这个技巧尤其有用 1.2. sql select sal as salary, comm as commissionfrom empwhere salary &#xff1c; 5000 1.3. 内嵌视图 1.3.1. sql select *from (select sal as salary, comm …

按 DDD 设计这个新项目

一、专业术语 各种服务 IAAS&#xff1a;基础设施服务&#xff0c;Infrastructure-as-a-service PAAS&#xff1a;平台服务&#xff0c;Platform-as-a-service SAAS&#xff1a;软件服务&#xff0c;Software-as-a-service 二、架构演变 图片 从图中已经可以很容易看出架…

R 语言 ggplot2 PCA 主成分分析(虚拟数据集)

生成虚拟数据集 library(ggplot2)data.matrix <- matrix(nrow 100, ncol 10)colnames(data.matrix) <- c(paste("wt",1:5,sep ""),paste("ko",1:5,sep "") )rownames(data.matrix) <- paste("gene",1:100,sep…

vue安装|win11系统

1.安装node.js https://nodejs.org/en/download 下载对应系统对应位数的.msi文件&#xff0c; 下载完成后&#xff0c;一直点击next进行安装 自定义安装路径&#xff0c;我的安装路径为**“D:\nodejs”** # 检查node.js版本 node -V# 检查npm版本 npm -V在D:\nodejs下新建两…

DataGrip连接clickhouse

首先保证ClickHouse启动了&#xff1a; 先建一个工程&#xff1a; 建立数据库源连接&#xff1a; 用户名和密码可以不写&#xff1a; 添加ClickHouse驱动&#xff1a;最好不用自己下载的驱动&#xff0c;会出现一些错误以及连接失败&#xff0c;用在线下载的。 选择一个版…

《深入浅出SSD:固态存储核心技术、原理与实战》----学习记录(三)

第3章 SSD存储介质&#xff1a;闪存 3.1 闪存物理结构 3.1.1 闪存器件原理 1978年诞生的世界上第一块固态硬盘就是基于DRAM的。但由于保存在DRAM中的数据有掉电易失性&#xff0c;当然还有成本因素&#xff0c;所以现在的固态硬盘一般都不采用DRAM&#xff0c;而是使用闪存…