vue中使用高德地图自定义掩膜背景结合threejs

news2025/2/12 13:45:19

技术架构

vue3+高德地图2.0+threejs

代码步骤

这里我们就用合肥市为主要的地区,将其他地区扣除,首先使用高德的webapi的DistrictSearch功能,使用该功能之前记得检查一下初始化的时候是否添加到plugins中,然后搜索合肥市的行政数据。

district.search(targetArea.name, (status, result) => {      if (status === "complete") {        //设置中心点        map.setCenter([result.districtList[0].center.lng, result.districtList[0].center.lat], true);                                                                //整个世界        let outer = [new AMap.LngLat(-360, 90, true), new AMap.LngLat(-360, -90, true), new AMap.LngLat(360, -90, true), new AMap.LngLat(360, 90, true)];                                //合肥市边界信息        let holes = result.districtList[0].boundaries;        let pathArray = [outer];
        // 绘制行政区划        pathArray.push.apply(pathArray, holes);
            // 在合肥市内描边        let polygon1 = new AMap.Polygon({path: [outer],          strokeColor: "#00eeff",          strokeWeight: 5,          fillOpacity: 0,        });                                                                    //添加至地图                                polygon.setPath(pathArray);              map.add(polygon);                                                     }    });  

这里就已经有了整个城市的轮廓。

图片

我们的需求是自定义背景,首先我们需要获取到除了合肥市以外的地区,这里我们使用threejs,版本是0.157.0,大家这里可以安装和我一样的版本保证不会出错,版本太高会有问题,控制台 npm i three@0.157.0,在代码中import * as THREE from "three"导入,这里我们单独将threejs业务代码分出去。​​​​​​​

------继续上面的代码----------
//先获取地图画布的大小const innerHeight = mapRef.value.clientHeight;const innerWidth = mapRef.value.clientWidth;
let options = {    pathArray,    center: [result.districtList[0].center.lng, result.districtList[0].center.lat],    size: {      innerWidth,      innerHeight,    },  };                //这里传入数据,单独写一个js文件来处理threejs业务 ,instace是AMapcreateThreeLayer(instance, map, options);

收集到我们需要的数据之后单独建一个js文件,先安装一个插件叫turf,这个插件非常善于处理地图上的数学、多边型,先 npm i @turf/turf,再导入import * as turf from "@turf/turf,下面开始写逻辑。​​​​​​​

import * as THREE from "three";import * as turf from "@turf/turf";
let glLayer;let camera, renderer, scene, raycaster;let customCoords = null;
/** * @description 创建ThreeLayer */export function createThreeLayer(instance, map, options) {  const { polygon, center, size } = options;
  customCoords = map.customCoords;  // 地图中心转换为墨卡托  const centerCoord = customCoords.lngLatToCoord(center);
  // polygon是传入的多边形坐标,是一个非遮罩区域,outer外边框是整个地图的边界  const outer = polygon[0];  const inner = polygon[1];
  // inner的bbox  const turfPoints = inner.map((item) => [item.lng, item.lat]);  const innerBbox = turf.bbox(turf.polygon([turfPoints]));  // 最小正方形  const squared = turf.square(innerBbox);  // 转为4个点  const squaredPoints = [    [squared[0], squared[1]],    [squared[0], squared[3]],    [squared[2], squared[3]],    [squared[2], squared[1]],  ];
  // 多边形坐标转换为世界中的坐标  const innerCoords = inner.map((item) => {    return {      ...item,      coord: customCoords.lngLatToCoord([item.lng, item.lat]),    };  });  const outerCoords = outer.map((item) => {    return {      ...item,      coord: customCoords.lngLatToCoord([item.lng, item.lat]),    };  });                //世界边界点  const squaredPointsCoords = squaredPoints.map((item) => customCoords.lngLatToCoord(item));                        //——--------------------——------------继续写代码的位置-------------------------           }

这里将我们所有使用的点转换好了之后就可以创建自定义图层了,接下来开始绘制,这里我们准备两张图片,一张贴图,一张法线贴图(让图片凹凸部分感光,让画面更有质感),所有步骤的解释都在注释里面。​​​​​​​

// 背景贴图  const bgTexture = new THREE.TextureLoader().load("static/three/bg.png");  // 法线背景贴图  const normalTexture = new THREE.TextureLoader().load("static/three/bg_normal.png");
if (!glLayer) {    glLayer = new instance.GLCustomLayer({      zIndex: 10,      init: (gl) => {        camera = new THREE.PerspectiveCamera(45, size.innerWidth / size.innerHeight, 100, 1 << 30);
        renderer = new THREE.WebGLRenderer({          context: gl, // 地图的 gl 上下文        });                                        renderer.autoClear = false;        scene = new THREE.Scene();        // 环境光照和平行光        let aLight = new THREE.AmbientLight(0xffffff, 2);        let dLight = new THREE.DirectionalLight(0xffffff);        dLight.intensity = 6;        dLight.position.set(centerCoord[0], -100000, 50000);        dLight.target.position.set(centerCoord[0], centerCoord[1], 0);        dLight.target.updateMatrixWorld(); 
        scene.add(dLight);        scene.add(aLight);
        // 辅助        // const helper = new THREE.DirectionalLightHelper(dLight, 1);        // scene.add(helper);
                // 辅助坐标轴        // const axesHelper = new THREE.AxesHelper(100000);        // scene.add(axesHelper);


        // 创建遮罩区域,遮罩区域 = outer - inner        const maskShape = new THREE.Shape();
        // 创建outer        maskShape.moveTo(outerCoords[0].coord[0], outerCoords[0].coord[1]);        outerCoords.forEach((item) => {          maskShape.lineTo(item.coord[0], item.coord[1]);        });
        // 创建inner        maskShape.holes.push(new THREE.Path());        maskShape.holes[0].moveTo(innerCoords[0].coord[0], innerCoords[0].coord[1]);        innerCoords.forEach((item) => {          maskShape.holes[0].lineTo(item.coord[0], item.coord[1]);        });
        // 信息添加给多边形        const maskGeometry = new THREE.ShapeGeometry(maskShape);
        // 调整纹理坐标 这里是调整贴图偏移和大小        bgTexture.wrapS = THREE.RepeatWrapping;        bgTexture.wrapT = THREE.RepeatWrapping;        bgTexture.repeat.set(120, 120);        bgTexture.offset.set(0.877, 0.275);                                                                               //这里是调整法线贴图的        normalTexture.wrapS = THREE.RepeatWrapping;        normalTexture.wrapT = THREE.RepeatWrapping;        normalTexture.repeat.set(120, 120);        normalTexture.offset.set(0.877, 0.275);
        // 调整 UV        maskGeometry.computeBoundingBox();        const bbox = maskGeometry.boundingBox;        let size1 = new THREE.Vector2();        bbox.getSize(size1);
        const uvAttribute = maskGeometry.attributes.uv;
        for (let i = 0; i < uvAttribute.count; i++) {          const uv = new THREE.Vector2().fromBufferAttribute(uvAttribute, i);          uv.x = (uv.x - bbox.min.x) / size1.x; // uv坐标映射          uv.y = (uv.y - bbox.min.y) / size1.y;          uvAttribute.setXY(i, uv.x, uv.y);        }
        uvAttribute.needsUpdate = true;
        const maskMaterial = new THREE.MeshStandardMaterial({          map: bgTexture,          side: THREE.DoubleSide,          transparent: true,          normalMap: normalTexture,          roughness: 0.5,          metalness: 0.5,        });
        const maskMesh = new THREE.Mesh(maskGeometry, maskMaterial);        scene.add(maskMesh);
        
      },
      render: () => {        let { near, far, fov, up, lookAt, position } = customCoords.getCameraParams();        camera.near = near;        camera.far = far;        camera.fov = fov;        camera.position.set(...position);        camera.up.set(...up);        camera.lookAt(...lookAt);        camera.updateProjectionMatrix();        renderer.render(scene, camera);        renderer.resetState();      },    });
    map.add(glLayer);
    const animate = () => {      map.render();      requestAnimationFrame(animate);    };    animate();
    function onWindowResize() {      camera.aspect = size.innerWidth / size.innerHeight;      camera.updateProjectionMatrix();      renderer.setSize(size.innerWidth, size.innerHeight);    }    window.addEventListener("resize", onWindowResize);  }

下面放出效果图。

图片

因为加上了法线贴图所以他会像这样。

图片

图片

加入高德地图各种特效之后是这样的效果。

图片

素材放在下面了,需要的朋友可以自取哦~

图片

图片

图片

高德开放平台第二期实战案例,三等奖作品

作者:左文韬

仅代表作者个人观点

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

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

相关文章

Python----PyQt开发(PyQt高级:图像显示,定时器,进度条)

一、图像显示 1.1、增加图标 1.直接创建setWindowIcon(QIcon(灯泡.jpg)) import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton from PyQt5.QtGui import QIconclass MainWindow(QMainWindow):def __init__(self):super(MainWindow, self).__init_…

Tomcat添加到Windows系统服务中,服务名称带空格

要将Tomcat添加到Windows系统服务中&#xff0c;可以通过Tomcat安装目录中“\bin\service.bat”来完成&#xff0c;如果目录中没有service.bat&#xff0c;则需要使用其它方法。 打到CMD命令行窗口&#xff0c;通过cd命令跳转到Tomcat安装目录的“\bin\”目录&#xff0c;然后执…

2025.2.10 每日学习记录3:技术报告只差相关工作+补实验

0.近期主任务线 1.完成小论文准备 目标是3月份完成实验点1的全部实验和论文。 2.准备教资笔试 打算留个十多天左右&#xff0c;一次性备考笔试的三个科目 1.实习申请技术准备&#xff1a;微调、Agent、RAG 据央视财经&#xff0c;数据显示&#xff0c;截至2024年12月…

微生物学术语和定义 | 微生物学词汇表

​ 微生物学作为一门研究微生物及其与环境、宿主和其他生物相互作用的科学&#xff0c;涵盖了广泛的学科领域和专业术语。然而&#xff0c;由于微生物学的快速发展和跨学科融合&#xff0c;许多术语的定义和使用在不同领域中可能存在差异甚至混淆。 随着新冠疫情的全球蔓延&am…

Java集合List详解(带脑图)

允许重复元素&#xff0c;有序。常见的实现类有 ArrayList、LinkedList、Vector。 ArrayList ArrayList 是在 Java 编程中常用的集合类之一&#xff0c;它提供了便捷的数组操作&#xff0c;并在动态性、灵活性和性能方面取得了平衡。如果需要频繁在中间插入和删除元素&#xf…

分层解耦-ioc引入

内聚: 软件中各个功能模块内部的功能联系。 耦合: 衡量软件中各个层/模块之间的依赖、关联的程度。 软件设计原则: 高内聚低耦合。

如何利用DeepSeek开源模型打造OA系统专属AI助手

利用DeepSeek开源模型打造OA系统专属AI助手&#xff0c;可以显著提升办公效率&#xff0c;增强信息检索和管理能力。 注册与登录DeepSeek平台 访问DeepSeek官网 访问DeepSeek的官方网站DeepSeek。使用电子邮件或手机号码注册账号并登录。 获取API Key 登录DeepSeek平台&am…

uni getLocation 公众号h5获取定位坐标没有返回

先看代码 //获取经纬度getLocation() {console.log("111")uni.getLocation({type: wgs84,success: function (res) {console.log(当前位置的经度&#xff1a; res.longitude);console.log(当前位置的纬度&#xff1a; res.latitude);},fail: function(err) {conso…

C语言基本概念————讨论sqrt()和pow()函数与整数的关系

本文来源&#xff1a;C语言基本概念——讨论sqrt()和pow()函数与整数的关系. C语言基本概念——sqrt和pow函数与整数的关系 1. 使用sqrt()是否可以得到完全平方数的精确的整数平方根1.1 完全平方数的计算结果是否精确&#xff1f;1.2 为什么不会出现误差&#xff08;如 1.99999…

如何本地部署DeepSeek

第一步&#xff1a;安装ollama https://ollama.com/download 打开官网&#xff0c;选择对应版本 第二步&#xff1a;选择合适的模型 https://ollama.com/ 模型名称中的 1.5B、7B、8B 等数字代表模型的参数量&#xff08;Parameters&#xff09;&#xff0c;其中 B 是英文 B…

正则表达式--元字符-限定符(4)

正则的限定元字符 表示前边一个符号代表的内容出现多少次 1.* ------ 表示0~正无穷次 2. ------ 表示 1~正无穷次 3. ? ------ 表示 0~1次 4. {n} ------ 表示 限定 n 次, 不能多也不能少 5. {n,} ------ 表示 最少 n 次 6. {n,m} ------ 表示 最少 n 次, 最多 m 次 <!DO…

【CMAEL多智能体框架】第一节 环境搭建及简单应用(构建一个鲜花选购智能体)

第一节 环境搭建 文章目录 第一节 环境搭建前言一、安装二、获取API1. 使用熟悉的API代理平台2.设置不使用明文存放API 三 、具体应用进阶任务 总结 前言 CAMEL Multi-Agent是一个开源的、灵活的框架&#xff0c;它提供了一套完整的工具和库&#xff0c;用于构建和模拟多智能体…

网络工程师 (31)VLAN

前言 VLAN&#xff08;Virtual Local Area Network&#xff09;即虚拟局域网&#xff0c;是一种将物理局域网划分成多个逻辑上独立的虚拟网络的技术。 一、定义与特点 定义&#xff1a;VLAN是对连接到的第二层交换机端口的网络用户的逻辑分段&#xff0c;不受网络用户的物理位置…

玩转适配器模式

文章目录 解决方案现实的举例适用场景实现方式适配器模式优缺点优点:缺点:适配器模式可比上一篇的工厂模式好理解多了,工厂模式要具有抽象的思维。这个适配器模式,正如字面意思,就是要去适配某一件物品。 假如你正在开发一款股票市场监测程序, 它会从不同来源下载 XML 格…

腾讯云大数据套件TBDS与阿里云大数据能力产品对比

前言 博主在接触大数据方向研究的时候是在2016年,那时候正是大数据概念非常火热的一个时间段,最著名的Google的3篇论文。Google FS、MapReduce、BigTable,奠定了大数据框架产品的基础。Google文件系统,计算框架和存储框架。往后所有的大数据产品和过程域无一不是在三个模块…

Codeforces Round 1003 (Div. 4)(A~G题题解)

A. Skibidus and Amogu 思路&#xff1a;把字符串最后的us变成i就可以了&#xff0c;水题一个 #include <iostream> #include <string> int main() { int t; std::cin >> t; std::cin.ignore(); while (t--) { std::string W; std::getline(std::c…

ubuntu使用防火墙开放和关闭指定端口

防火墙可以阻止或允许外部对特定端口的访问&#xff0c;Ubuntu 常用的防火墙管理工具是 ufw&#xff08;Uncomplicated Firewall&#xff09; &#xff0c;如果在开发网络通信相关的内容时&#xff0c;要确保所需的端口是打开的&#xff0c;这样可以排除出题出现时的一个问题—…

mysql8.0使用PXC实现高可用

1.什么是 PXC PXC 是一套 MySQL 高可用集群解决方案&#xff0c;与传统的基于主从复制模式的集群架构相比 PXC 最突出特点就是解决了诟病已久的数据复制延迟问题&#xff0c;基本上可以达到实时同步。而且节点与节点之间&#xff0c;他们相互的关系是对等的。PXC 最关注的是数据…

大数据学习之SparkStreaming、PB级百战出行网约车项目一

一.SparkStreaming 163.SparkStreaming概述 Spark Streaming is an extension of the core Spark API that enables scalable, high-throughput, fault-tolerant stream processing of live data streams. Spark Streaming 是核心 Spark API 的扩展&#xff0c;支持实时数据…

Ollama部署DeepSeek(windows or ubuntu)

Ollama(官网是https://ollama.com/)是一个专为在本地机器上便捷部署和运行大型语言模型&#xff08;LLM&#xff09;而设计的开源框架。它简化了大型语言模型的部署过程&#xff0c;提供了轻量级与可扩展的架构&#xff0c;使得研究人员、开发人员和爱好者能够更加方便地在本地…