高德地图three生成围栏

本文详细介绍了如何在Vue应用中使用AMapJSAPI和Three.js库,创建一个可自定义高度的3D地图,包括读取数据、绘制多边形、地形渲染以及动画效果。作者还展示了如何处理地理位置数据并将其转换为Three.js坐标系进行渲染。
摘要由CSDN通过智能技术生成

提示:以下是本篇文章正文内容,下面案例可供参考

一、使用步骤

1.引入库

代码如下(示例):

npm i @amap/amap-jsapi-loader
npm i three

根据需求自定义高度(结合自定义多边形)
在这里插入图片描述

2.读入数据

代码如下(示例):

<template>
  <div>
    高度<input type="text" v-model.number="height" />
    <div class="myMap" ref="echart" id="myMap"></div>
  </div>
</template>

<script>
import AMapLoader from "@amap/amap-jsapi-loader";
import * as THREE from "three";

export default {
  name: "App",
  data() {
    return {
      height: 1000,
    };
  },
  mounted() {
    this.initMap();
    window.addEventListener("resize", this.onWindowResize);
  },
  methods: {
    //加载地图api文件,修改自己高德地图key,版本不用调整,若调整请查看浏览器中会不会报错
    initMap() {
      AMapLoader.load({
        key: "xxxxxx", //高德地图key,实际应用做到配置文件中
        version: "2.0",
        AMapUI: {
          version: "1.1",
          plugins: ["overlay/SimpleMarker"],
        },
        Loca: {
          // 是否加载 Loca, 缺省不加载
          version: "2.0.0",
        },
        plugins: [
          "AMap.DistrictSearch",
          "AMap.ToolBar",
          "AMap.Polygon",
          "AMap.PolygonEditor",
          "AMap.MouseTool",
          "AMap.ControlBar",
        ],
      }).then(() => {
        this.loadMap(); //实例化地图
      });
    },
    // 初始化地图
    loadMap() {
      let _this = this;
      let map = new AMap.Map("myMap", {
        pitch: 45, //地图俯仰角度,有效范围 0 度- 83 度
        zoom: 16,
        // zooms: [9, 12], // 设置缩放范围
        center: [116.396532, 39.918138],
        viewMode: "3D", //地图模式
        // dragEnable: false, // 禁止地图拖动
        showLabel: true,
        buildingAnimation: true,
        // mapStyle: "amap://styles/darkblue",
        // 工具栏默认包含缩放功能,确保它已启用
        toolbar: {
          visible: true,
        },
        showLabel: false, // 禁止显示文字标签
        layers: [new AMap.TileLayer.Satellite()],
      });
      _this.myMap = map;
      // 地图加载完成
      _this.myMap.on("complete", () => {
        _this.$nextTick(() => {
          // 绘制多边形
          _this.initLayer();
        });
      });
    },
    seve() {
      this.initLayer();
    },
    initLayer() {
      let _this = this;

      const layer = new AMap.GLCustomLayer({
        zIndex: 9999,
        visible: true,
        init: (gl) => {
          _this.initThree(gl);
          _this.createWall();
          _this.animate();
        },
        render: () => {
          const { near, far, fov, up, lookAt, position } =
            _this.myMap.customCoords.getCameraParams();

          this.camera.near = near; // 近平面
          this.camera.far = far; // 远平面
          this.camera.fov = fov; // 视野范围
          this.camera.position.set(...position);
          this.camera.up.set(...up);
          this.camera.lookAt(...lookAt);

          // 更新相机坐标系
          this.camera.updateProjectionMatrix();

          this.renderer.render(this.scene, this.camera);

          // 这里必须执行!重新设置 three 的 gl 上下文状态
          this.renderer.resetState();
        },
      });

      this.myMap.add(layer);
    },
    initThree(gl) {
      const container = document.getElementById("myMap");
      this.camera = new THREE.PerspectiveCamera(
        60,
        container.clientWidth / container.clientHeight,
        100,
        1 << 30
      );
      this.renderer = new THREE.WebGLRenderer({
        context: gl,
        // canvas: document.querySelector('.amap-layer'), //也可以直接用canvas初始化
        antialias: true, // 抗锯齿,默认false 耗性能
      });
      // 自动清空画布这里必须设置为 false,否则地图底图将无法显示
      this.renderer.autoClear = false;
      this.renderer.outputEncoding = THREE.sRGBEncoding;

      this.scene = new THREE.Scene();
      // 增加环境光
      const aLight = new THREE.AmbientLight(0xffffff, 0.3);
      this.scene.add(aLight);
    },
    createWall() {
      const color = '#FFD500';
      let faceList = [];
      let faceVertexUvs = [];

      // 墙体路径原始数据
      const data = [
        [
          [116.391836, 39.922464],
          [116.401749, 39.922925],
          [116.401921, 39.913643],
          [116.392179, 39.913182],

        ],
        [
          [116.407196, 39.918264],
          [116.417753, 39.918528],
          [116.417581, 39.908455],
        ],
      ];


      // 起点坐标=结束坐标形成封闭图形
      data.forEach((item) => {
        item.push(item[0])
      });

      console.log(data,'data');
      // 地理坐标转为three坐标系,不管用不用arr,都需要转换一个非空数组
      // 否则customCoords没实例化api会报错
      this.paths = this.myMap.customCoords.lngLatsToCoords(data);

      // 合并多个闭合范围
      for (let i = 0; i < this.paths.length; i++) {
        const { face, uvs } = this.generateVecData(this.paths[i]);
        faceList = [...faceList, ...face];
        faceVertexUvs = [...faceVertexUvs, ...uvs];
      }
      // 背景层
      const geometry = new THREE.BufferGeometry();
      geometry.setAttribute(
        "position",
        new THREE.BufferAttribute(new Float32Array(faceList), 3)
      );
      geometry.setAttribute(
        "uv",
        new THREE.BufferAttribute(new Float32Array(faceVertexUvs), 2)
      );

      const material1 = new THREE.MeshBasicMaterial({
        color: color,
        side: THREE.DoubleSide,
        transparent: true,
        depthWrite: false,
        // wireframe: true,
        opacity: 0.5, // 添加这一行以设置透明度
        depthTest: false,
      });
      const mesh1 = new THREE.Mesh(geometry, material1);
      this.scene.add(mesh1);

      // 动画层
      const geometry2 = geometry.clone();
      this.texture = this.generateTexture(128, color);
      this.texture.wrapS = THREE.RepeatWrapping; // 水平重复平铺
      this.texture.wrapT = THREE.RepeatWrapping; // 垂直重复平铺

      const material2 = new THREE.MeshBasicMaterial({
        side: THREE.DoubleSide,
        transparent: true,
        depthWrite: false,
        map: this.texture,
        depthTest: false,
      });

      const mesh2 = new THREE.Mesh(geometry2, material2);
      this.scene.add(mesh2);
    },
    animate() {
      let _this = this;
      // 动效纹理偏移
      let texture_offset = 0;
      // 纹理偏移
      texture_offset -= 0.03; // 向上移动
      this.texture.offset.set(0, texture_offset);

      if (this.myMap) {
        this.myMap.render();
      }
      requestAnimationFrame(() => {
        _this.animate();
      });
    },
    generateVecData(arr) {
      const vec3List = []; // 顶点数组
      let faceList = []; // 三角面数组
      let faceVertexUvs = []; // 面的UV层队列,用于纹理和几何信息映射

      // t3---t2
      // |  \  |
      // t0---t1
      // UV面
      // 下三角[t0, t1, t3]
      // 上三角[t3, t1, t2]
      const t0 = [0, 0];
      const t1 = [1, 0];
      const t2 = [1, 1];
      const t3 = [0, 1];

      // 高度
      for (let i = 0; i < arr.length; i++) {
        const [x1, y1] = arr[i];
        vec3List.push([x1, y1, 0]);
        vec3List.push([x1, y1, this.height]);
      }

      // 1---3
      // | \ |
      // 0---2
      // 三角面顶点,没有顺序要求,但要跟UV面顺序一致
      // 下三角 [0,1,2]
      // 上三角 [1,2,3]
      for (let i = 0; i < vec3List.length - 2; i++) {
        if (i % 2 === 0) {
          // 下三角
          faceList = [
            ...faceList,
            ...vec3List[i],
            ...vec3List[i + 2],
            ...vec3List[i + 1],
          ];
          // UV
          faceVertexUvs = [...faceVertexUvs, ...t0, ...t1, ...t3];
        } else {
          // 上三角
          faceList = [
            ...faceList,
            ...vec3List[i],
            ...vec3List[i + 1],
            ...vec3List[i + 2],
          ];
          // UV
          faceVertexUvs = [...faceVertexUvs, ...t3, ...t1, ...t2];
        }
      }

      return {
        face: faceList,
        uvs: faceVertexUvs,
      };
    },
    generateTexture(size = 64, color = "#ff0000") {
      let canvas = document.createElement("canvas");
      canvas.width = size;
      canvas.height = size;
      let ctx = canvas.getContext("2d");
      let linearGradient = ctx.createLinearGradient(0, 0, 0, size);
      linearGradient.addColorStop(0.2, this.hexToRgba(color, 0.0));
      linearGradient.addColorStop(0.8, this.hexToRgba(color, 0.5));
      linearGradient.addColorStop(1.0, this.hexToRgba(color, 1.0));
      ctx.fillStyle = linearGradient;
      ctx.fillRect(0, 0, size, size);

      this.texture = new THREE.Texture(canvas);
      this.texture.needsUpdate = true; //必须
      return this.texture;
    },
    hexToRgba(hex, opacity = 1) {
      return (
        "rgba(" +
        parseInt("0x" + hex.slice(1, 3)) +
        "," +
        parseInt("0x" + hex.slice(3, 5)) +
        "," +
        parseInt("0x" + hex.slice(5, 7)) +
        "," +
        opacity +
        ")"
      );
    },
  },
};
</script>
<style lang="less">
#myMap {
  height: 100vh;
}
</style>

高德官方有官方示例


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值