Babylonjs学习笔记(十一)——加载geoJson文件

news2024/9/21 16:38:36
一、定义基本场景类
  • 定义场景
  • 定义相机
import { ArcRotateCamera, Color4, CubeTexture, Engine, GlowLayer, KeyboardEventTypes, Scene, Vector3 } from '@babylonjs/core';

import { AdvancedDynamicTexture } from '@babylonjs/gui';

class SceneManager {
  public engine: Engine;
  public scene: Scene;
  public camera: ArcRotateCamera;
  public advanceTexture: AdvancedDynamicTexture;
  public glowLayer: GlowLayer;

  constructor(canvas: HTMLCanvasElement) {
    this.engine = new Engine(canvas);
    const { scene, camera } = this.BuildScene();
    this.scene = scene;
    this.camera = camera;
    this.advanceTexture = AdvancedDynamicTexture.CreateFullscreenUI('ui');
    this.glowLayer = this.SetGlowLayer();

    this.setEnv();
    this.Resize();
    this.RenderLoop();
  }

  BuildScene(): { scene: Scene; camera: ArcRotateCamera } {
    const scene = new Scene(this.engine);
    scene.clearColor = new Color4(0, 0, 0, 0);

    const camera = new ArcRotateCamera('camera', 1.57, 1.57, 5.5, Vector3.Zero());

    camera.attachControl(this.engine.getRenderingCanvas(), true);
    camera.minZ = 0;
    camera.wheelPrecision = 20;
    camera.fov = 0.7;

    camera.lowerAlphaLimit = null;
    camera.upperAlphaLimit = null;
    camera.lowerBetaLimit = null;
    camera.upperBetaLimit = null;

    camera.inputs.removeByType('ArcRotateCameraMouseWheelInput');

    return {
      camera,
      scene
    };
  }

  SetGlowLayer(): GlowLayer {
    const glowLayer = new GlowLayer('gl');
    glowLayer.isEnabled = true;
    glowLayer.intensity = 0.3;
    return glowLayer;
  }

  RenderLoop() {
    this.engine.runRenderLoop(() => {
      this.scene.render();
    });
  }

  Resize() {
    if (window) {
      window.addEventListener('resize', () => {
        this.engine.resize();
      });
    }
  }
  // 切换调试面板
  public async RegisterInspectorOnInput(scene: Scene) {
    const isEnv = import.meta.env.MODE === 'development';
    if (isEnv) {
      await Promise.all([import('@babylonjs/core/Debug/debugLayer'), import('@babylonjs/inspector')]);
      scene.debugLayer.show({ embedMode: true });
    }

    const toggleInspector = () => {
      if (scene.debugLayer.isVisible()) scene.debugLayer.hide();
      else scene.debugLayer.show();
    };

    scene.onKeyboardObservable.add((kbInfo) => {
      if (kbInfo.type === KeyboardEventTypes.KEYDOWN && kbInfo.event.key === 'i') {
        toggleInspector();
      }
    });
  }

  setEnv() {
    const skybox = CubeTexture.CreateFromPrefilteredData('env/Earth_Skybox.env', this.scene);
    this.scene.createDefaultSkybox(skybox, false, 30, 0.98);

    const hdr = CubeTexture.CreateFromPrefilteredData('env/Earth_Env.env', this.scene);
    this.scene.environmentTexture = hdr;

    this.scene.autoClear = false; // Color buffer
    this.scene.autoClearDepthAndStencil = false; // Depth and stencil, obviously
  }

  Dispose() {
    this.scene.dispose();
    this.engine.dispose();
  }
}

export { SceneManager };
二、定义子类继承父类
  • 继承父类
  • 加载json文件
  • 利用墨卡托投影算法创建地图轮廓

import * as d3 from 'd3';

import earcut from 'earcut';

1.加载json文件

  LoadJson() {
    return new Promise<JSONType>((resolve) => {
      Tools.LoadFile('json/china.json', (response) => {
        try {
          const json = JSON.parse(response as string) as JSONType;
          resolve(json);
        } catch (error) {
          console.error(`地图json加载失败:${error}`);
        }
      });
    });
  }

 

2.生成边线和轮廓

2.1定义地图中心和投影算法

const center: [number, number] = [108.55, 34.32];
export function projection(args: [number, number]) {
  const result = d3.geoMercator().center(center).scale(5).translate([0, 0])(args) as [number, number];
  return result;
}

 2.2 封装创建多边形算法

// 创建多边形
export function CreatePolygon(path: Vector3[], scene: Scene) {
 
  return MeshBuilder.ExtrudePolygon(
    'polygon',
    {
      shape: path,
      sideOrientation: Mesh.FRONTSIDE,
      depth: 0.5,
      // 这里设置会覆盖材质的颜色
      faceColors: [new Color4(1, 0, 0, 1), new Color4(0, 1, 0, 0.1), new Color4(1, 1, 1, 1)],
      faceUV: [new Vector4(0, 0, 1, 1), new Vector4(0, 0, 1, 1), new Vector4(0, 0, 1, 1)]
    },
    scene,
    earcut
  );
}

// 创建边界线
export function CreateBoundaryLine(path: Vector3[]) {
  const line = MeshBuilder.CreateLines('line', { points: path.concat(path[0]), updatable: true });
  line.color = Color3.Random();
  return line;
}

2.3 通过json数据创建多边形


  CreateMapFromGeoJSON(geoJsonData: JSONType) {
    return new Promise<Boolean>((resolve) => {
      const { features } = geoJsonData;

      const _createPolygonsFromCoordinates = (coordinate: number[][][] | number[][], meshList: Mesh[], lineList: Mesh[]) => {
        coordinate.forEach(() => {
          //获取轮廓
          const path = coordinate[0].map((coord: any) => {
            const [x, y] = projection([coord[0], coord[1]]) as [number, number];
            // 投影在xz平面
            return new Vector3(x, 0, -y);
          });
          const polygon = CreatePolygon(path, this.scene);
          meshList.push(polygon);

          const line = CreateBoundaryLine(path);
          lineList.push(line);
        });
      };

      features.forEach((feature, groundIndex) => {
        const { center, name, centroid } = feature.properties;
        const { coordinates, type } = feature.geometry;
        const meshList: Mesh[] = [];
        const lineList: Mesh[] = [];

        if (type === 'Polygon') _createPolygonsFromCoordinates(coordinates, meshList, lineList);
        if (type === 'MultiPolygon') coordinates.forEach((item) => _createPolygonsFromCoordinates(item, meshList, lineList));

        if (meshList.length > 0) {
          const mergedMesh = Mesh.MergeMeshes(meshList, true, true, undefined, false, true);
          if (mergedMesh) {
            mergedMesh.name = name + groundIndex;
            this.regionList.push({
              name,
              center,
              centroid,
              mesh: mergedMesh
            });
          }
        }
      });
      resolve(true);
    });
  }

2.4 创建省份标签

//创建区域标签
export function CreateRegionLabel(regionList: TypeRegionList[], advanceTexture: AdvancedDynamicTexture) {
  const _drawText = (name: string, mesh: Mesh) => {
    const text = new TextBlock();
    text.text = name;
    text.fontSize = 12;
    text.color = 'white';
    advanceTexture.addControl(text);

    text.linkWithMesh(mesh);
  };
  const _drawImage = (mesh: Mesh) => {
    const image = new Image('point', 'textures/point.png');
    image.width = '70px';
    image.height = '70px';

    advanceTexture.addControl(image);
    image.linkWithMesh(mesh);
    // image.linkOffsetX = -15;
    // image.linkOffsetY = 0;
  };
  regionList.forEach((item) => {
    const { mesh, name } = item;
    _drawText(name, mesh);
    _drawImage(mesh);
  });
}

2.5 创建飞线


export function CreateFlyline(regionList: TypeRegionList[], scene: Scene) {
  const flylineCenter = [116.41995, 40.18994] as [number, number];
  const [x, y] = projection(flylineCenter);
  const origin = new Vector3(x, 0, -y);

  // 创建texture
  const createLineTexture = (): Texture => {
    const textureColors = new Uint8Array([255, 255, 255, 0, 0, 255]);
    const texture = new RawTexture(
      textureColors,
      textureColors.length / 3,
      1,
      Engine.TEXTUREFORMAT_RGB,
      scene,
      false,
      true,
      Engine.TEXTURE_NEAREST_NEAREST
    );
    texture.wrapU = RawTexture.WRAP_ADDRESSMODE;
    texture.name = 'blue-white-texture';
    texture.uScale = 5;
    return texture;
  };

  const texture = createLineTexture();

  const createLinesInstance = (points: Vector3[]): GreasedLineBaseMesh => {
    const line = CreateGreasedLine(
      'line',
      {
        points,
        updatable: true
      },
      {
        width: 0.008,
        colorMode: GreasedLineMeshColorMode.COLOR_MODE_MULTIPLY
      },
      scene
    );
    return line;
  };

  regionList.forEach((city) => {
    const { centroid } = city;
    if (centroid) {
      const [x, y] = projection(centroid);
      const targetVec = new Vector3(x, 0, -y);
      CreateWave(new Vector3(targetVec.x, targetVec.y + 0.01, targetVec.z), scene);
      const middle = origin.add(targetVec).scale(0.5);
      let control = new Vector3(middle.x, 1, middle.z);
      // 创建贝塞尔曲线
      const curve = Curve3.CreateQuadraticBezier(origin, control, targetVec, 64);
      // 将贝塞尔曲线的点赋予line
      const line = createLinesInstance(curve.getPoints());
      (line.material as StandardMaterial).emissiveTexture = texture;
      texture.uScale = 5;

      scene.onBeforeRenderObservable.add(() => {
        texture.uOffset += -0.0005 * scene.getAnimationRatio();
      });
    }
  });
}

2.6 创建缩放动画

export function CreateWave(position: Vector3, scene: Scene) {
  const plane = MeshBuilder.CreatePlane('wave', { size: 0.15 });
  const mat = new StandardMaterial('mat');
  const texture = new Texture('textures/wave.png');
  mat.emissiveColor = Color3.White();
  mat.diffuseTexture = texture;
  mat.useAlphaFromDiffuseTexture = true;
  texture.hasAlpha = true;
  plane.material = mat;
  plane.position = position;
  plane.rotation.x = Math.PI / 2;

  let radio = 1.0; // 初始化缩放比例
  let size = 1; // 初始大小
  scene.onBeforeRenderObservable.add(() => {
    radio += 0.01; // 控制缩放速度
    const scling = size * radio;
    const initVector = new Vector3(scling, scling, scling);
    plane.scaling = initVector;

    if (radio <= 1.5) {
      plane.material!.alpha = (radio - 1) * 2; // 透明度从0到1
    } else if (radio > 1.5 && radio <= 2) {
      plane.material!.alpha = 1 - (radio - 1.5) * 2; // 透明度从1到0
    } else {
      radio = 1.0; // 重置缩放比例
    }
  });
}

2.7 创建action

export function AddEvent(regionList: TypeRegionList[]) {
  regionList.forEach((item) => {
    const { mesh, name } = item;
    mesh.actionManager = new ActionManager();

    // 改变样式action
    mesh.actionManager.registerAction(new SetValueAction(ActionManager.OnPointerOverTrigger, mesh.material, 'diffuseColor', Color3.Blue()));
    mesh.actionManager.registerAction(
      new SetValueAction(ActionManager.OnPointerOutTrigger, mesh.material, 'diffuseColor', (mesh.material as StandardMaterial)!.diffuseColor)
    );

    mesh.actionManager.registerAction(new InterpolateValueAction(ActionManager.OnPointerOverTrigger, mesh, 'scaling', new Vector3(1, -1.2, 1), 300));
    mesh.actionManager.registerAction(new InterpolateValueAction(ActionManager.OnPointerOutTrigger, mesh, 'scaling', new Vector3(1, 1, 1), 300));
    // 点击事件action
    mesh.actionManager.registerAction(
      new ExecuteCodeAction(
        {
          trigger: ActionManager.OnLeftPickTrigger,
          // 参数传递
          parameter: function (actionEvent: any) {
            return actionEvent.sourceEvent.key === 'R';
          }
        },
        (evt) => {
          console.log(evt, name);
          // 派发事件
          emitter.emit(EVENT_NAME.SET_PROVINCE_DATA, name as TProvinceKeys);
        }
      )
    );
  });
}

3. 构造函数中调用

constructor(params: { canvas: HTMLCanvasElement; topList: String[] }) {
    const { canvas, topList } = params;
    super(canvas);
    // new HemisphericLight('ligt', Vector3.Up());
    this.LoadJson().then((geoJson: JSONType) => {
      this.CreateMapFromGeoJSON(geoJson).then(() => {
        // 创建区域标签
        CreateRegionLabel(this.regionList, this.advanceTexture);
        // 创建飞线
        CreateFlyline(this.regionList, this.scene);
        // 添加交互事件
        AddEvent(this.regionList);
        // 轮播
        this.SetupLoopInteraction();
      });
    });
  }

4 效果展示

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

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

相关文章

springboot系列教程(一):简介与入门案例(含源码)

一、SpringBoot简介 SpringBoot继承了Spring优秀的基因&#xff0c;上手难度小简化配置&#xff0c;提供各种默认配置来简化项目配置内嵌式容器简化Web项目&#xff0c;简化编码 Spring Boot 则会帮助开发着快速启动一个 web 容器&#xff0c;在 Spring Boot 中&#xff0c;只…

【Linux】从零开始认识多线程 --- 线程控制

在这个浮躁的时代 只有自律的人才能脱颖而出 -- 《觉醒年代》 从零开始认识多线程 --- 线程控制 1 知识回顾2 线程控制2.1 线程创建2.2 线程等待2.3 线程终止 3 测试运行3.1 小试牛刀 --- 创建线程3.2 探幽析微 --- 理解线程参数3.3 小有心得 --- 探索线程返回3.4 求索无厌 …

金九银十,软件测试面试题大全(自动化篇+含答案)

“ 今天我给大家介绍一些python自动化测试中常见的面试题&#xff0c;涵盖了Python基础、测试框架、测试工具、测试方法等方面的内容&#xff0c;希望能够帮助你提升自己的水平和信心。” 项目相关 1.什么项目适合做自动化测试&#xff1f; 答&#xff1a;一般来说&#xff…

鸿蒙模拟器(HarmonyOS Emulator)Beta申请审核流程

文 | Promise Sun 一.背景&#xff1a; 鸿蒙项目开发需要使用模拟器进行开发测试&#xff0c;但目前想在DevEco Studio开发工具中使用模拟器就必须到华为官网进行报名申请&#xff0c;参加“鸿蒙模拟器&#xff08;HarmonyOS Emulator&#xff09;Beta活动申请”。 申请审核通…

在互联网供应链系统可能是永远不会过时的系统

一、前言 在互联网在到人工智能&#xff0c;从基本的门户网站&#xff0c;社交网站&#xff0c;到移动互联网&#xff0c;视频网站&#xff0c;再到现在比较火爆短视频直播和人工智能AI&#xff0c;大模型。互联网的迭代&#xff0c;出现了无数的系统。但是有些系统一直久经不…

知识图谱和 LLM:利用Neo4j驾驭大型语言模型(探索真实用例)

这是关于 Neo4j 的 NaLLM 项目的一篇博客文章。这个项目是为了探索、开发和展示这些 LLM 与 Neo4j 结合的实际用途。 2023 年,ChatGPT 等大型语言模型 (LLM) 因其理解和生成类似人类的文本的能力而风靡全球。它们能够适应不同的对话环境、回答各种主题的问题,甚至模拟创意写…

Java中的流类型详解

Java中的流类型详解 1、按照流的方向分类1.1 输入流&#xff08;InputStream&#xff09;1.2 输出流&#xff08;OutputStream&#xff09; 2、按照实现功能分类2.1 节点流&#xff08;Node Stream 或 Basic Stream&#xff09;2.2 处理流&#xff08;Wrapper Stream 或 Proces…

Java语言程序设计——篇四(2)

类和对象 方法设计定义和使用方法访问方法和修改方法方法的调用方法参数的传递✨方法重载✨构造方法(构造器)&#x1f6a9;this关键字this关键字主要用于以下两种情况&#xff1a; 编程练习静态变量和静态方法静态变量静态方法&#x1f308;解释&#xff1a;main方法的访问权限…

玩客云刷入海纳思系统

玩客云(晶晨S805)刷机 | 海纳思系统 (ecoo.top) https://www.ecoo.top/update/soft_init/amlproject/USB_Burning_Tool_v2.1.3.exe https://node4.histb.com:9088/update/system/s805/hinas_s805_eMMC.burn.img.zip

【排序算法】—— 归并排序

归并排序时间复杂度O(NlongN)&#xff0c;空间复杂度O(N)&#xff0c;是一种稳定的排序&#xff0c;其次可以用来做外排序算法&#xff0c;即对磁盘(文件)上的数据进行排序。 目录 一、有序数组排序 二、排序思路 三、递归实现 四、非递归实现 一、有序数组排序 要理解归…

[算法] 优选算法(五):二分查找(上)

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏: &#x1f9ca; Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 &#x1f355; Collection与…

Linux虚拟机扩展磁盘空间

文章目录 在VM上进行扩展新的磁盘空间进入虚拟机将扩展的磁盘空间分配给对应的分区 VM 下的Linux虚拟机提示磁盘空间不足&#xff0c;需要对其进行磁盘扩容&#xff0c;主要有以下两步&#xff1a; 在VM上进行扩展新的磁盘空间 先关闭虚拟机在VM的虚拟机设置处进行硬盘扩展 …

STM32自己从零开始实操:PCB全过程

一、PCB总体分布 以下只能让大家看到各个模块大致分布在板子的哪一块&#xff0c;只能说每个人画都有自己的理由&#xff1a; 电源&#xff1a;从外部接入电源&#xff0c;5V接到中间&#xff0c;向上变成4V供给无线&#xff0c;向下变成3V供给下面的接口&#xff08;也刻意放…

Java---SpringBoot详解二

勤奋勤劳铸梦成&#xff0c; 晨曦微露起长征。 汗水浇灌花似锦&#xff0c; 寒窗苦读岁月明。 千锤百炼心如铁&#xff0c; 万里征途志不倾。 持之以恒终有日&#xff0c; 功成名就笑谈中。 目录 一&#xff0c;统一响应结果 二&#xff0c;三层架构 三&#xff0c;分层解耦 四…

力扣第九题

回文数 提示&#xff1a; 给你一个整数 x &#xff0c;如果 x 是一个回文整数&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 回文数是指正序&#xff08;从左向右&#xff09;和倒序&#xff08;从右向左&#xff09;读都是一样的整数。 代码展示&#…

请你谈谈:AnnotatedBeanDefinitionReader 显式地注册一个Bean到Spring容器,以及注册并解析配置类

为了深入探讨Spring框架中的beanDefinition对象&#xff0c;我们不可避免地要提及BeanFactoryPostProcessor这一核心类&#xff0c;它作为Spring的bean工厂后置处理器发挥着关键作用。接下来&#xff0c;我们将详细讨论BeanFactoryPostProcessor的执行时机&#xff0c;这是一个…

顶顶通呼叫中心中间件-添加自定义变量到CDR方法(mod_cti基于FreeSWITCH)

顶顶通呼叫中心中间件-添加自定义变量到CDR方法(mod_cti基于FreeSWITCH) 1、自定义变量添加到cti.json 例&#xff1a;需要添加的变量为“bridge_uepoch" 2、添加进数据库 在数据库中找到表"cdr"在cdr表中也添加数据&#xff0c;数据名为新变量名&#xff1a…

基于Java的科大讯飞大模型API调用实现

写在前面&#xff1a;因为现在自己实习的公司新拓展的一个业务是结合AI的低代码平台&#xff0c;我负责后端的开发&#xff0c;之前一直都是直接使用gpt或者文心一言等ui界面来直接使用大模型&#xff0c;从来没有自己调接口过&#xff0c;所以本文记录一下自己第一次使用大模型…

P2p网络性能测度及监测系统模型

P2p网络性能测度及监测系统模型 网络IP性能参数 IP包传输时延时延变化误差率丢失率虚假率吞吐量可用性连接性测度单向延迟测度单向分组丢失测度往返延迟测度 OSI中的位置-> 网络层 用途 面相业务的网络分布式计算网络游戏IP软件电话流媒体分发多媒体通信 业务质量 通过…

从零开始做题:什么奇奇怪怪的东西

题目 解题 mrf拓展名&#xff0c;macro recorder打开&#xff0c;鼠标键盘的记录 然后解压flag.zip即可&#xff0c;发现有一个挂载的文件&#xff0c;直接打开后 显示所有的隐藏文件 一个一个打开 然后进行拼接运行吧估计。 首先打开txt文件直接久就给出了代码&#xff1…