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

一、定义基本场景类
  • 定义场景
  • 定义相机
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 效果展示

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

superTiger_y

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值