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

这篇具有很好参考价值的文章主要介绍了结合ace编辑器实现MapboxGL热力图样式在线配置。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

概述

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

效果

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

实现

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转换方法如下:文章来源地址https://www.toymoban.com/news/detail-514196.html

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
      });
    }
  }
},

到了这里,关于结合ace编辑器实现MapboxGL热力图样式在线配置的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Linux使用Docker部署StackEdit结合内网穿透实现公网访问本地编辑器

    前言 实现ubuntu 系统桌面级别的远程连接,需要在ubuntu 系统中安装vnc,既然是桌面,前提是需要ubuntu 带有图形化界面,如果没有,可以执行以下命令安装图形化界面 1. ubuntu安装VNC 在ubuntu中安装vnc 安装LightDM【LightDM从设计上就是支持本地图形界面以获得最好的兼容性】 安装过程中会

    2024年04月08日
    浏览(52)
  • Unity3d C#利用Editor编辑器拓展实现配置UI背景样式一键设置UI背景样式功能(含源码)

    在开发UI滚动列表的时候,经常会有每项的背景图不统一的情况,会间隔重复的情况居多。这种情况下,手动去设置间隔一行的背景图或者颜色是比较麻烦的。在此背景下,笔者尝试写个小工具,在搭建UI时配置一下循环背景的样式,可以通过一键点击后设置UI背景的样式,省

    2024年02月03日
    浏览(49)
  • DevExpress WinForms数据编辑器组件,提供丰富的数据输入样式!(二)

    DevExpress WinForms超过80个高影响力的WinForms编辑器和多用途控件,从屏蔽数据输入和内置数据验证到HTML格式化,DevExpress数据编辑库提供了无与伦比的数据编辑选项,包括用于独立数据编辑或用于容器控件(如Grid, TreeList和Ribbon)的单元格。 在上文中(点击这里回顾),我们为大家

    2024年02月11日
    浏览(41)
  • DevExpress WinForms数据编辑器组件,提供丰富的数据输入样式!(一)

    DevExpress WinForms超过80个高影响力的WinForms编辑器和多用途控件,从屏蔽数据输入和内置数据验证到HTML格式化,DevExpress数据编辑库提供了无与伦比的数据编辑选项,包括用于独立数据编辑或用于容器控件(如Grid, TreeList和Ribbon)的单元格。 PS:DevExpress WinForm拥有180+组件和UI库,能

    2024年02月12日
    浏览(43)
  • uniapp小程序开发-富文本编辑器mp-html,juice外部样式转内联样式

    在开发一款公司的小程序过程中,有一个需求:web端后端可以上传word文档,后端转为html字符串;小程序接收显示,并且可以在小程序进行编辑修改。 其实在日常小程序中很少见到富文本编辑器的使用,所以这次使用的过程中也尝试了多种,但是最好用的还是mp-html 这款插件

    2024年02月09日
    浏览(44)
  • vue代码编辑器vue-codemirror的简单使用更改样式和切换主题等

    可以查看官网演示:vue-codemirror | Homepage 支持的语言mode:CodeMirror: Language Modes  支持的主题样式:CodeMirror: Theme Demo  开始安装和使用:  基础使用 注册全局组件 注册局部组件 使用组件 实现的效果: 编辑器默认高度是300px,如果想更改高度和字体大小:添加样式  更改主题的

    2024年02月13日
    浏览(39)
  • wangEditor富文本编辑器的调用开发实录2(V5版本自定义粘贴,去除复制word或网页html冗余样式代码的解决方案)

    在使用wangEditor富文本编辑器时,当从word文档或者其他网页复制文本内容粘贴到编辑器中,如果不过滤掉复制文本中自带的样式,会导致复制的内容比较错乱,甚至无法添加到数据库中。为了解决这个问题,我们需要对从word中粘贴的内容进行处理,把多余的代码剔除,让粘贴

    2024年02月16日
    浏览(38)
  • 编辑器的缩略图实现原理

    部分 Web 版的 IDE 编辑器未曾实现缩略图功能,探寻一下缩略图的实现逻辑。以 VSCode 为例。 VSCode 的编辑器是 monaco 实现的,编辑器的编辑区都是采用的 虚拟渲染 ,即仅渲染可视区的代码,可视区之外的动态去除 DOM 节点。 打开 VScode Help Toggle Developer Tools,观察 DOM 节点的状态

    2024年02月09日
    浏览(36)
  • 实现一个python代码编辑器

    代码编辑器采用了monacoEditor,一个现成的编辑器。网上有很多文档介绍和开源项目,但是怎么说呢,跟着做,可以实现一个网页编辑器,可以高亮python的语法,但是没有python的提示,找不到可以参考的,js我也不会,看的着实云里雾里。 【提示】在VsCode上运行比较方便 -----

    2024年02月21日
    浏览(55)
  • qt+opencv实现图片编辑器

    借助QLabel容器,进行显示图片作为背景,然后重写QLabel类实现矩形,直线和圆形的实现。opencv板块直接实现相关图片操作。 打开图片 裁切 改变亮度和对比度 顺时针旋转和逆时针旋转 重写的QLabel

    2024年02月16日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包