返回介绍

Shaded Relief (with WebGL)

发布于 2022-11-30 23:36:05 字数 7018 浏览 0 评论 0 收藏 0

Calculate shaded relief from elevation data

For the shaded relief, a single tiled source of elevation data is used as input. The shaded relief is calculated by the layer's style with a color expression. The style variables are updated when the user drags one of the sliders. The band operator is used to sample data from neighboring pixels for calculating slope and aspect, which is done with the ['band', bandIndex, xOffset, yOffset] syntax.

main.js

import 'ol/ol.css';
import Map from 'ol/Map';
import View from 'ol/View';
import {OSM, XYZ} from 'ol/source';
import {WebGLTile as TileLayer} from 'ol/layer';

const variables = {};

// The method used to extract elevations from the DEM.
// In this case the format used is
// red + green * 2 + blue * 3
//
// Other frequently used methods include the Mapbox format
// (red * 256 * 256 + green * 256 + blue) * 0.1 - 10000
// and the Terrarium format
// (red * 256 + green + blue / 256) - 32768
function elevation(xOffset, yOffset) {
  return [
    '+',
    ['*', 256, ['band', 1, xOffset, yOffset]],
    [
      '+',
      ['*', 2 * 256, ['band', 2, xOffset, yOffset]],
      ['*', 3 * 256, ['band', 3, xOffset, yOffset]],
    ],
  ];
}

// Generates a shaded relief image given elevation data.  Uses a 3x3
// neighborhood for determining slope and aspect.
const dp = ['*', 2, ['resolution']];
const z0x = ['*', ['var', 'vert'], elevation(-1, 0)];
const z1x = ['*', ['var', 'vert'], elevation(1, 0)];
const dzdx = ['/', ['-', z1x, z0x], dp];
const z0y = ['*', ['var', 'vert'], elevation(0, -1)];
const z1y = ['*', ['var', 'vert'], elevation(0, 1)];
const dzdy = ['/', ['-', z1y, z0y], dp];
const slope = ['atan', ['^', ['+', ['^', dzdx, 2], ['^', dzdy, 2]], 0.5]];
const aspect = ['clamp', ['atan', ['-', 0, dzdx], dzdy], -Math.PI, Math.PI];
const sunEl = ['*', Math.PI / 180, ['var', 'sunEl']];
const sunAz = ['*', Math.PI / 180, ['var', 'sunAz']];

const cosIncidence = [
  '+',
  ['*', ['sin', sunEl], ['cos', slope]],
  ['*', ['*', ['cos', sunEl], ['sin', slope]], ['cos', ['-', sunAz, aspect]]],
];
const scaled = ['*', 255, cosIncidence];

const shadedRelief = new TileLayer({
  opacity: 0.3,
  source: new XYZ({
    url: 'https://{a-d}.tiles.mapbox.com/v3/aj.sf-dem/{z}/{x}/{y}.png',
    crossOrigin: 'anonymous',
  }),
  style: {
    variables: variables,
    color: ['color', scaled, scaled, scaled],
  },
});

const controlIds = ['vert', 'sunEl', 'sunAz'];
controlIds.forEach(function (id) {
  const control = document.getElementById(id);
  const output = document.getElementById(id + 'Out');
  function updateValues() {
    output.innerText = control.value;
    variables[id] = Number(control.value);
  }
  updateValues();
  const listener = function () {
    updateValues();
    shadedRelief.updateStyleVariables(variables);
  };
  control.addEventListener('input', listener);
  control.addEventListener('change', listener);
});

const map = new Map({
  target: 'map',
  layers: [
    new TileLayer({
      source: new OSM(),
    }),
    shadedRelief,
  ],
  view: new View({
    extent: [-13675026, 4439648, -13580856, 4580292],
    center: [-13615645, 4497969],
    minZoom: 10,
    maxZoom: 16,
    zoom: 13,
  }),
});

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>Shaded Relief (with WebGL)</title>
    <!-- Pointer events polyfill for old browsers, see https://caniuse.com/#feat=pointer -->
    <script src="https://unpkg.com/elm-pep"></script>
    <!-- The line below is only needed for old environments like Internet Explorer and Android 4.x -->
    <script src="https://cdn.polyfill.io/v3/polyfill.min.js?features=fetch,requestAnimationFrame,Element.prototype.classList,URL,TextDecoder,Number.isInteger"></script>
    <style>
      .map {
        width: 100%;
        height:400px;
      }
      table.controls td {
        padding: 2px 5px;
      }
      table.controls td:nth-child(3) {
        text-align: right;
        min-width: 3em;
      }
    </style>
  </head>
  <body>
    <div id="map" class="map"></div>
    <table class="controls">
      <tr>
        <td><label for="vert">vertical exaggeration:</label></td>
        <td><input id="vert" type="range" min="1" max="5" value="1"/></td>
        <td><span id="vertOut"></span> x</td>
      </tr>
      <tr>
        <td><label for="sunEl">sun elevation:</label></td>
        <td><input id="sunEl" type="range" min="0" max="90" value="45"/></td>
        <td><span id="sunElOut"></span> °</td>
      </tr>
      <tr>
        <td><label for="sunAz">sun azimuth:</label></td>
        <td><input id="sunAz" type="range" min="0" max="360" value="45"/></td>
        <td><span id="sunAzOut"></span> °</td>
      </tr>
    </table>
    <script src="main.js"></script>
  </body>
</html>

package.json

{
  "name": "webgl-shaded-relief",
  "dependencies": {
    "ol": "7.1.0"
  },
  "devDependencies": {
    "parcel": "^2.0.0-beta.1"
  },
  "scripts": {
    "start": "parcel index.html",
    "build": "parcel build --public-url . index.html"
  }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文