mapboxGL中山体背景+边界阴影的一种实现方案

news2024/12/23 6:57:21

概述

很多地图可视化的项目中有要求实现如下的效果,本文借助QGISPSturf.js,在mapboxGL中实现山体背景+边界阴影的效果。

实现效果

image.png

实现

1. 需要数据

要实现这样的效果,我们需要如下数据:

  1. 山体背景图
  2. 地级市数据
  3. 省级边界数据,可通过地级市数据融合得到
  4. 边界阴影,通过省级边界数据计算获取

测试数据下载地址:https://gitee.com/lzugis15/blogs-demo/blob/master/gansu.zip

2. 数据处理

2.1 省级边界数据

如果没有改数据,可复制一份地级市的数据,在QGIS中开启图层编辑,全选要素,通过Merge选中要素生成。
image.png

2.2 山体背景图

1)导出影像

QGIS中添加高德影像图,并添加省边界数据,设置省边界不显示,导出地图。
[图片上传中…(image.png-6c9beb-1716705916905-0)]
根据省边界数据计算导出范围,并设置导出格式为*.tif
image.png

2)裁剪影像

将导出的*.tif添加到QGIS中,在Raster菜单下选择栅格裁剪工具,将导出的数据根据省边界数据进行裁剪。
image.png

3)导出背景图

跟操作**1)**一样,导出裁剪后的地图,导出格式选择*.png,导出后的图片如下图。
image.png

4)处理背景图

导出后的背景图是彩色的,还需要在PS中进一步处理成为蓝色调(可根据需求进行处理)。处理方式是在上面叠加一个图层,设置填充颜色,并设置模式为色相,再将两个图层合并成一个图层,处理后如下图。
image.png

2.3 边界阴影

边界阴影效果是将生边界数据进行一定的偏移,这个实现是在代码中实现的,实现代码如下:

const center = this.map.getCenter().toArray();
 // 获取地图中心点屏幕位置
const { x, y } = this.map.project(center);
const offset = [6, 6];
// 计算当前级别下横向、纵向偏移的经纬度
const centerOffset = this.map.unproject([x - offset[0], y - offset[1]]).toArray();
const xOffset = centerOffset[0] - center[0],
  yOffset = center[1] - centerOffset[1];
// 深拷贝,防止数据被篡改
const _res = JSON.parse(JSON.stringify(result));
const geometry = _res.geometry;
if (geometry.type === "polygon") {
  geometry.coordinates.forEach((c) => {
    c.forEach((p) => {
      p[0] += xOffset;
      p[1] += yOffset;
    });
  });
} else {
  geometry.coordinates.forEach((c) => {
    c.forEach((p) => {
      p.forEach((_p) => {
        _p[0] += xOffset;
        _p[1] += yOffset;
      });
    });
  });
}

3. 完整实现

完整实现代码如下。

<template>
  <div class="map">
    <my-map
      :onLoad="mapLoaded"
      :style="style"
      :center="[104.29901000000001, 37.94116735562514]"
      :zoom="zoom"
    >
    </my-map>
  </div>
</template>

<script>
import { MyMap } from "@/components/map/index.vue";
import * as turf from "@turf/turf";

class Geojson {
  constructor(features = []) {
    this.features = features;
    this.type = "FeatureCollection";
  }
}

export default {
  components: {
    MyMap,
  },
  data() {
    return {
      map: null,
      zoom: 3.51,
      style: {
        version: 8,
        name: "my-map-style",
        sprite: window.location.href + "icons/sprite",
        glyphs: window.location.href + "fonts/{fontstack}/{range}.pbf",
        sources: {
          "image-admin": {
            url: "/imgs/gansu-bg.png",
            type: "image",
            // 省边界数据的四至
            coordinates: [
              [92.3390100000000018, 42.795259999999999],
              [108.712530000000001, 42.795259999999999],
              [108.712530000000001, 32.5938900000000018],
              [92.3390100000000018, 32.5938900000000018],
            ],
          },
          "admin-boundry": {
            type: "geojson",
            data: new Geojson(),
          },
          "admin-shadow": {
            type: "geojson",
            data: new Geojson(),
          },
          "admin-children-boundry": {
            type: "geojson",
            data: new Geojson(),
          },
          "admin-children-boundry-h": {
            type: "geojson",
            data: new Geojson(),
          },
        },
        layers: [
          {
            id: "admin-shadow-fill",
            source: "admin-shadow",
            type: "fill",
            paint: {
              "fill-color": "#356caa",
              "fill-opacity": 1,
            },
          },
          {
            id: "image-admin",
            source: "image-admin",
            type: "raster",
            paint: {
              "raster-opacity": 0.55,
              "raster-fade-duration": 0,
            },
          },
          {
            id: "admin-children-boundry-fill",
            source: "admin-children-boundry",
            type: "fill",
            paint: {
              "fill-color": "#599AFF",
              "fill-opacity": 0.1,
            },
          },
          {
            id: "admin-children-boundry-fill-h",
            source: "admin-children-boundry-h",
            type: "fill",
            paint: {
              "fill-color": "#599AFF",
              "fill-opacity": 0.5,
            },
          },
          {
            id: "admin-children-boundry-line",
            source: "admin-children-boundry",
            type: "line",
            paint: {
              "line-color": "#bbe6ff",
              "line-width": 1.5,
            },
          },
          {
            id: "admin-children-boundry-line-h",
            source: "admin-children-boundry-h",
            type: "line",
            paint: {
              "line-color": "#bbe6ff",
              "line-width": 2,
            },
          },
          {
            id: "admin-boundry-line",
            source: "admin-boundry",
            type: "line",
            paint: {
              "line-color": "#bbe8ff",
              "line-width": 3,
            },
          },
          {
            id: "admin-children-boundry-label",
            source: "admin-children-boundry",
            type: "symbol",
            layout: {
              'text-allow-overlap': false,
              'text-size': 14,
              'text-rotate': 0,
              'text-field': `{name}`,
            },
            paint: {
              'text-opacity': 1,
              'text-color': '#ffffff',
              'text-halo-blur': 0.1,
              'text-halo-width': 0.1,
              'text-halo-color': '#356caa',
            },
          },
        ],
      },
      adminFeatures: [],
    };
  },
  methods: {
    setBoundry(features) {
      features = JSON.parse(JSON.stringify(features));
      let result = features.splice(0, 1)[0],
        feat2 = features.splice(0, 1)[0];
      while (features.length > 0) {
        result = turf.union(result, feat2);
        feat2 = features.splice(0, 1)[0];
      }
      this.map.getSource("admin-boundry").setData(result);
      setTimeout(() => {
        const center = this.map.getCenter().toArray();
        const { x, y } = this.map.project(center);
        const offset = [6, 6];
        const centerOffset = this.map
          .unproject([x - offset[0], y - offset[1]])
          .toArray();
        const xOffset = centerOffset[0] - center[0],
          yOffset = center[1] - centerOffset[1];
        const _res = JSON.parse(JSON.stringify(result));
        const geometry = _res.geometry;
        if (geometry.type === "polygon") {
          geometry.coordinates.forEach((c) => {
            c.forEach((p) => {
              p[0] += xOffset;
              p[1] += yOffset;
            });
          });
        } else {
          geometry.coordinates.forEach((c) => {
            c.forEach((p) => {
              p.forEach((_p) => {
                _p[0] += xOffset;
                _p[1] += yOffset;
              });
            });
          });
        }
        this.map.getSource("admin-shadow").setData(_res);
      }, 200);
    },

    registerEvent() {
      this.map.on("mousemove", "admin-children-boundry-fill", (e) => {
        const adcode = e.features[0].properties.adcode;
        const feature = this.adminFeatures.find(
          (d) => d.properties.adcode === adcode
        );
        this.map.getSource("admin-children-boundry-h").setData(feature);
        this.map.getCanvasContainer().style.cursor = "pointer";
      });
      this.map.on("mouseout", "admin-children-boundry-fill", (e) => {
        this.map.getSource("admin-children-boundry-h").setData(new Geojson());
        this.map.getCanvasContainer().style.cursor = "default";
      });
    },
    initData() {
      this.map.scrollZoom.disable();
      this.map.doubleClickZoom.disable();
      this.map.dragPan.disable();
      this.map.dragRotate.disable();

      fetch(`/gansu-b.geojson`)
        .then((res) => res.json())
        .then((geojson) => {
          this.setBoundry(geojson.features);
          this.fit2Geojson(geojson);
        });
      fetch(`/gansu-c.geojson`)
        .then((res) => res.json())
        .then((geojson) => {
          this.adminFeatures = geojson.features;
          this.map.getSource("admin-children-boundry").setData(geojson);
          this.registerEvent()
        });
    },
    mapLoaded(map) {
      this.map = map;
      this.initData();
    },
    fit2Geojson(geojson) {
      const [xmin, ymin, xmax, ymax] = turf.bbox(geojson);
      const bbox = [
        [xmin, ymin],
        [xmax, ymax],
      ];
      const padding = 100;
      const options = {
        padding: {
          top: padding,
          bottom: padding,
          left: padding,
          right: padding,
        },
        duration: 100,
      };
      this.map.fitBounds(bbox, options);
    },
  },
};
</script>

<style scoped lang="scss">
.map {
  width: 100%;
  height: 100%;
}
</style>

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

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

相关文章

[卷积神经网络]RepConv和重参数化

RepConv是Yolov7,YoloV9中一个重要的结构&#xff0c;其主要用于在保持精度不退化的情况下提升推理速度。RepConv在学习阶段和推理阶段拥有不同的结构&#xff0c;这使得其推理阶段的复杂度大大降低。这项技术的核心在于重参数化。 一、重参数化 重参数化的主要思想是将卷积(C…

2024年弘连网络FIC大会竞赛题线下决赛题

总结&#xff1a; FIC决赛的时候&#xff0c;很多小问题没发现&#xff0c;在pve平台做题确实很方便。 这套题目复盘完&#xff0c;服务器这块的知识确实收获了很多&#xff0c;对pve集群平台和网络拓扑也有了一定的认识&#xff0c;感谢各位大佬悉心指导。 接下来&#xff0…

工具使用-网络性能测试工具(iperf)-TCP 和 UDP 的吞吐量-包转发率参数的理解

时间戳&#xff1a;2024年5月26日15:18:39 iperf 和 netperf 都是最常用的网络性能测试工具&#xff0c;测试 TCP 和 UDP 的吞吐量。它们都以客户端和服务器通信的方式&#xff0c;测试一段时间内的平均吞吐量。 接下来&#xff0c;我们就以 iperf 为例&#xff0c;看一下 TC…

C语言PTA练习题:三角形类别,输入三角形三条边,求面积,四则计算器,猴子吃桃

7-1 三角形类别 输入三个整数&#xff0c;以这三个数为边长&#xff0c;判断是否构成三角形&#xff1b;若不能输出"no"&#xff0c;若构成三角形&#xff0c;进一步判断它们构的是&#xff1a;锐角三角形或直角三角形或钝角三角形.分别输出"ruijiao",&qu…

Spring MVC+mybatis项目入门:旅游网(四)用户注册——mybatis的配置与使用以及Spring MVC重定向

个人博客&#xff1a;Spring MVCmybatis项目入门:旅游网&#xff08;四&#xff09;用户注册2-持久化 | iwtss blog 先看这个&#xff01; 这是18年的文章&#xff0c;回收站里恢复的&#xff0c;现阶段看基本是没有参考意义的&#xff0c;技术老旧脱离时代&#xff08;2024年…

qt-C++笔记之QThread使用

qt-C笔记之QThread使用 ——2024-05-26 下午 code review! 参考博文&#xff1a; qt-C笔记之使用QtConcurrent异步地执行槽函数中的内容&#xff0c;使其不阻塞主界面 qt-C笔记之QThread使用 文章目录 qt-C笔记之QThread使用一:Qt中几种多线程方法1.1. 使用 QThread 和 Lambda…

【二叉树】LeetCode.144:二叉树的前序遍历(小细节把握)

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;初阶初阶结构刷题 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 目录 1.题目描述&#xff1a;​编辑 2.问题分析&#xff1a; &#x1f354;函数解读&#xff1a; …

功率电感的设计步骤

文章目录 1&#xff1a;高导磁气隙&#xff08;铁氧体&#xff09;1.1设计原理1.2 设计步骤 2 铁粉芯2.1&#xff1a;设计原理2.2&#xff1a;设计步骤 TI电感设计 学习视频原链接 截图 1 截图1 截图1 截图 2 截图2 截图2 1&#xff1a;高导磁气隙&#xff08;铁氧体&#…

solidworks画螺栓学习笔记

螺栓 单位mm 六边形 直径16mm 水平约束 拉伸 选择厚度6mm 拉伸切除 画相切圆 切除厚度6mm&#xff0c;反向切除 &#xff0c;拔模角度45 螺栓 直径9mm&#xff0c;长度30mm 倒角 直径1mm&#xff0c;角度45 异形孔向导 螺纹线 偏移打勾&#xff0c;距离为2mm&#…

【C语言】八进制、十六进制

前言 在我们日常生活中使用的数往往是十进制的&#xff0c;而当我们学习C语言后我们会接触到许多不同的进制并且时常需要去思考与使用这些不同的进制&#xff08;尤其是2的幂相关的进制&#xff0c;因为这种计数系统比十进制更接近于计算机的二进制系统&#xff09;&#xff0…

牛客网刷题 | BC100 直角三角形图案

目前主要分为三个专栏&#xff0c;后续还会添加&#xff1a; 专栏如下&#xff1a; C语言刷题解析 C语言系列文章 我的成长经历 感谢阅读&#xff01; 初来乍到&#xff0c;如有错误请指出&#xff0c;感谢&#xff01; 描述 KiKi学习了循环&am…

爽!AI手绘变插画,接单赚爆了!

我最近发现一款名叫Hyper-SD15-Scribble的AI项目&#xff0c;可以实现一键手绘变插画的功能&#xff0c;而且它搭载了字节出品的超快速生成图片的AI大模型Hyper-SD15&#xff0c;可以实现几乎实时生成图片&#xff0c;有了它&#xff0c;拿去接一些手绘商单分分钟出图&#xff…

java生产制造执行系统MES源码:系统环境:Java EE 8、Servlet 3.0、Apache Maven 3 2;

MES系统技术选型 系统环境&#xff1a;Java EE 8、Servlet 3.0、Apache Maven 3 2&#xff1b; 主框架&#xff1a;Spring Boot 2.2.x、Spring Framework 5.2.x、Spring Security 5.2.x 3 持久层&#xff1a;Apache MyBatis 3.5.x、Hibernate Validation 6.0.x、Alibaba Dru…

基于STM32实现智能气体检测报警系统

⬇帮大家整理了单片机的资料 包括stm32的项目合集【源码开发文档】 点击下方蓝字即可领取&#xff0c;感谢支持&#xff01;⬇ 点击领取更多嵌入式详细资料 问题讨论&#xff0c;stm32的资料领取可以私信&#xff01; 目录 引言环境准备智能气体检测报警系统基础代码示例&…

ZDH-智能营销-插件服务

目录 主题 项目源码 预览地址 安装包下载地址 插件服务 插件服务使用场景 插件服务日志 感谢支持 主题 本篇文章主要介绍ZDH-智能营销平台下的插件服务,包含插件的应用场景 项目源码 zdh_web: GitHub - zhaoyachao/zdh_web: 大数据采集,抽取平台 zdh_magic_mirror: …

AIGC002-LoRA让大模型微调更加轻盈方便!

AIGC002-LoRA让大模型微调更加轻盈方便&#xff01; 文章目录 0 论文工作1 论文方法2 效果 0 论文工作 这篇论文名为 LORA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS&#xff0c;作者是 Edward Hu 等人。它提出了一种名为 低秩自适应 (Low-Rank Adaptation, LoRA) 的新方…

AI 谈“浔川AI翻译机”

在天工AI&#xff0c;天工AI在全网搜索“浔川AI翻译机”。 1 创作助手谈“浔川AI翻译机”&#xff1a; “浔川AI翻译机”是一个利用人工智能技术进行语言翻译的设备或应用程序。它可以将一种语言的文字或口语翻译成另一种语言&#xff0c;以实现不同语言之间的沟通和理解。浔…

网络布线与数制转换

信号与传输介质 信号概述 什么是信号 信息 人对现实世界事物存在方式或运动状态的某种认识 数据 用于描述事物的某些属性的具体量值 信号 信息传递的媒介 例如&#xff0c;描述某一件物体&#xff0c;它的长、宽、高、质地、颜色、气味等就是用以形容该物体的数据。通…

图书管理系统——Java版

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a;JavaSE 顺序表的学习&#xff0c;点我 目录 图书管理系统菜单 基本框架&#xff1a; 书&#xff1a; 书架&#xff1a; 用户&#xff…