使用地形图层的MapLibre GL JS:如何将水平面固定在特定高度?

7
我基于maplibre.org上的“Add a 3D model”示例编写了一些代码,以在使用setTerrain添加地形图层的地图上仅绘制水平面。
我的意图是在海拔高度给定的位置绘制几个半透明的图层,并使它们与山脉相交,有点类似等高线。
在我的第一个测试中,我只创建了一个海拔高度为0的1km宽的正方形。
我对其行为感到有些困惑,因为海拔高度0实际上是可见地图区域中心的地形高度。当我拖动地图并释放鼠标时,海拔高度0会以某种方式重新设置为中心处新的海拔高度0,导致平面改变其相对海拔高度。
以下动画GIF说明了问题:

issue plane relocating

这个GIF图向右展示了一座山脉,海拔被夸大了很多,以更好地说明问题。

我需要怎样才能指定飞机相对于海平面的高度(以米为单位),并在拖动地图时保持固定高度?

我认为我需要获取中心处地形的高度,然后将其加/减到飞机的z位置上,以便它相对于给定的地标保持在特定高度,但我不知道如何在自定义图层的渲染函数中实现这一点。

1个回答

0
就像你说的那样,一个天真的解决方案是获取屏幕中心处的地形高度和你的模型的高度,然后相减。可以通过以下方式实现:
// edited version of https://maplibre.org/maplibre-gl-js/docs/examples/add-3d-model/

  render(gl, matrix) {
    
    const modelOrigin = new LngLat(11.53, 47.67);  // just an example
    const modelElevation = map.terrain ? map.terrain.getElevationForLngLatZoom(modelOrigin, map.getZoom()) : 0;
    const modelOriginMercator = mlg.MercatorCoordinate.fromLngLat(modelOrigin, 0);
  
    // calculating the difference between center-elevation and model-elevation
    const terrainCenterElevation = map.transform.elevation;
    const deltaMetersZ = modelElevation - terrainCenterElevation;
    const mercatorPerMeter = modelOriginMercator.meterInMercatorCoordinateUnits();
    const deltaMercatorZ = deltaMetersZ * mercatorPerMeter;

    const modelTransform = {
      translateX: modelOriginMercator.x,
      translateY: modelOriginMercator.y,
      translateZ: modelOriginMercator.z + deltaMercatorZ,
      rotateX: Math.PI / 2,
      rotateY: 0,
      rotateZ: 0,
      scale: modelOriginMercator.meterInMercatorCoordinateUnits()
    };

    const rotationX = new Matrix4().makeRotationAxis(new Vector3(1, 0, 0), modelTransform.rotateX);
    const rotationY = new Matrix4().makeRotationAxis(new Vector3(1, 0, 0), modelTransform.rotateY);
    const rotationZ = new Matrix4().makeRotationAxis(new Vector3(0, 0, 1), modelTransform.rotateZ);

    const m = new Matrix4().fromArray(matrix);
    const l = new Matrix4()
      .makeTranslation(modelTransform.translateX, modelTransform.translateY, modelTransform.translateZ)
      .scale(new Vector3(modelTransform.scale, modelTransform.scale, modelTransform.scale))
      .multiply(rotationX).multiply(rotationY).multiply(rotationZ);
    const cameraProjectionMatrix = m.multiply(l);

    // @ts-ignore
    this.camera.projectionMatrix = cameraProjectionMatrix;
    // @ts-ignore
    this.renderer.resetState();
    // @ts-ignore
    this.renderer.render(this.scene, this.camera);
    map.triggerRepaint();


}
  • maplibre似乎总是将threejs场景准确地定位在屏幕中心的高度...所以当使用mlg.MercatorCoordinate.fromLngLat(modelOrigin, 0)获取模型高度时,我们不需要提供实际的模型高度,只需传递0
  • deltaMercatorZ被添加到模型的z轴平移中

关于maplibre在transform.ts中的命名约定,以下是一些注意事项:

  • elevation [米]:中心点center下方地形的高度
  • center [经度,纬度]:屏幕中心在世界坐标系中的位置
  • centerPoint [像素]:屏幕中心在屏幕上的位置
  • altitude [米]:相机相对于海平面的高度

我认为可能有更优雅的解决方案,但现在这个可能能够胜任!


更新:maplibre团队非常友好地合并了一个PR,该PR将添加关于这种特定技术的文档 - 我的理解是这个示例将在下一个maplibre发布版本中发布。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接