mirror of https://gitee.com/antv-l7/antv-l7
feat: add Hill shade demo (#1349)
* fix: 修复 featureScale 错误 * style: lint style * fix: remove featureScalePlugin async * feat: add hillShade demo - openlayers Co-authored-by: shihui <yiqianyao.yqy@alibaba-inc.com>
This commit is contained in:
parent
a2e525e12b
commit
c00b2a38d5
|
@ -0,0 +1,3 @@
|
||||||
|
### Raster - HillShade
|
||||||
|
山体阴影
|
||||||
|
<code src="./rasterData/hillshade.tsx"></code>
|
|
@ -0,0 +1,205 @@
|
||||||
|
//@ts-ignore
|
||||||
|
import { Scene, RasterLayer } from '@antv/l7';
|
||||||
|
//@ts-ignore
|
||||||
|
import { Mapbox } from '@antv/l7-maps';
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
useEffect(() => {
|
||||||
|
const scene = new Scene({
|
||||||
|
id: 'map',
|
||||||
|
map: new Mapbox({
|
||||||
|
center: [-119.5591, 37.715],
|
||||||
|
zoom: 9,
|
||||||
|
style: 'mapbox://styles/mapbox/cjaudgl840gn32rnrepcb9b9g',
|
||||||
|
token: 'pk.eyJ1IjoiMTg5Njk5NDg2MTkiLCJhIjoiY2w3dHk3dnN4MDYzaDNycDkyMDl2bzh6NiJ9.YIrG9kwUpayLj01f6W23Gw'
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = 256;
|
||||||
|
canvas.height = 256;
|
||||||
|
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||||
|
|
||||||
|
scene.on('loaded', () => {
|
||||||
|
const layer = new RasterLayer();
|
||||||
|
layer
|
||||||
|
.source(
|
||||||
|
// 'https://api.mapbox.com/v4/mapbox.terrain-rgb/{z}/{x}/{y}.pngraw?access_token=pk.eyJ1IjoiMTg5Njk5NDg2MTkiLCJhIjoiY2w3dHk3dnN4MDYzaDNycDkyMDl2bzh6NiJ9.YIrG9kwUpayLj01f6W23Gw',
|
||||||
|
'https://api.mapbox.com/raster/v1/mapbox.mapbox-terrain-dem-v1/{z}/{x}/{y}.webp?sku=101xrmKJip7uQ&access_token=pk.eyJ1IjoiMTg5Njk5NDg2MTkiLCJhIjoiY2w3dHk3dnN4MDYzaDNycDkyMDl2bzh6NiJ9.YIrG9kwUpayLj01f6W23Gw',
|
||||||
|
// 'https://b.tiles.mapbox.com/v3/aj.sf-dem/12/657/1589.png',
|
||||||
|
// https://b.tiles.mapbox.com/v3/aj.sf-dem/12/659/1589.png
|
||||||
|
{
|
||||||
|
parser: {
|
||||||
|
type: 'rasterTile',
|
||||||
|
dataType: 'arraybuffer',
|
||||||
|
tileSize: 256,
|
||||||
|
// extent: [-180, -85.051129, 179, 85.051129],
|
||||||
|
format: async (data: any) => {
|
||||||
|
const blob: Blob = new Blob([new Uint8Array(data)], {
|
||||||
|
type: 'image/png',
|
||||||
|
});
|
||||||
|
const img = await createImageBitmap(blob);
|
||||||
|
ctx.clearRect(0, 0, 256, 256);
|
||||||
|
ctx.drawImage(img, 0, 0, 256, 256);
|
||||||
|
|
||||||
|
const shadeOptions = {
|
||||||
|
resolution: 256,
|
||||||
|
sunEl: 0,
|
||||||
|
sunAz: 131,
|
||||||
|
vert: 2
|
||||||
|
}
|
||||||
|
const elevationImage = ctx.getImageData(0, 0, 256, 256);
|
||||||
|
const width = elevationImage.width;
|
||||||
|
const height = elevationImage.height;
|
||||||
|
const elevationData = elevationImage.data;
|
||||||
|
const shadeData = new Uint8ClampedArray(elevationData.length);
|
||||||
|
const dp = shadeOptions.resolution * 2;
|
||||||
|
const maxX = width - 1;
|
||||||
|
const maxY = height - 1;
|
||||||
|
const pixel = [0, 0, 0, 0];
|
||||||
|
const twoPi = 2 * Math.PI;
|
||||||
|
const halfPi = Math.PI / 2;
|
||||||
|
|
||||||
|
const sunEl = (Math.PI * shadeOptions.sunEl) / 180;
|
||||||
|
const sunAz = (Math.PI * shadeOptions.sunAz) / 180;
|
||||||
|
|
||||||
|
const cosSunEl = Math.cos(sunEl);
|
||||||
|
const sinSunEl = Math.sin(sunEl);
|
||||||
|
let pixelX,
|
||||||
|
pixelY,
|
||||||
|
x0,
|
||||||
|
x1,
|
||||||
|
y0,
|
||||||
|
y1,
|
||||||
|
offset,
|
||||||
|
z0,
|
||||||
|
z1,
|
||||||
|
dzdx,
|
||||||
|
dzdy,
|
||||||
|
slope,
|
||||||
|
aspect,
|
||||||
|
cosIncidence,
|
||||||
|
scaled;
|
||||||
|
function calculateElevation(pixel) {
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
// return pixel[0] + pixel[1] * 2 + pixel[2] * 3;
|
||||||
|
|
||||||
|
return -10000 + (pixel[0] * 256 * 256 + pixel[1] * 2 * 256 + pixel[2]) * 0.1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const arr2: number[] = [];
|
||||||
|
|
||||||
|
for (pixelY = 0; pixelY <= maxY; ++pixelY) {
|
||||||
|
y0 = pixelY === 0 ? 0 : pixelY - 1;
|
||||||
|
y1 = pixelY === maxY ? maxY : pixelY + 1;
|
||||||
|
for (pixelX = 0; pixelX <= maxX; ++pixelX) {
|
||||||
|
x0 = pixelX === 0 ? 0 : pixelX - 1;
|
||||||
|
x1 = pixelX === maxX ? maxX : pixelX + 1;
|
||||||
|
|
||||||
|
// determine elevation for (x0, pixelY)
|
||||||
|
offset = (pixelY * width + x0) * 4;
|
||||||
|
pixel[0] = elevationData[offset];
|
||||||
|
pixel[1] = elevationData[offset + 1];
|
||||||
|
pixel[2] = elevationData[offset + 2];
|
||||||
|
pixel[3] = elevationData[offset + 3];
|
||||||
|
z0 = shadeOptions.vert * calculateElevation(pixel);
|
||||||
|
|
||||||
|
// determine elevation for (x1, pixelY)
|
||||||
|
offset = (pixelY * width + x1) * 4;
|
||||||
|
pixel[0] = elevationData[offset];
|
||||||
|
pixel[1] = elevationData[offset + 1];
|
||||||
|
pixel[2] = elevationData[offset + 2];
|
||||||
|
pixel[3] = elevationData[offset + 3];
|
||||||
|
z1 = shadeOptions.vert * calculateElevation(pixel);
|
||||||
|
|
||||||
|
dzdx = (z1 - z0) / dp;
|
||||||
|
|
||||||
|
// determine elevation for (pixelX, y0)
|
||||||
|
offset = (y0 * width + pixelX) * 4;
|
||||||
|
pixel[0] = elevationData[offset];
|
||||||
|
pixel[1] = elevationData[offset + 1];
|
||||||
|
pixel[2] = elevationData[offset + 2];
|
||||||
|
pixel[3] = elevationData[offset + 3];
|
||||||
|
z0 = shadeOptions.vert * calculateElevation(pixel);
|
||||||
|
|
||||||
|
// determine elevation for (pixelX, y1)
|
||||||
|
offset = (y1 * width + pixelX) * 4;
|
||||||
|
pixel[0] = elevationData[offset];
|
||||||
|
pixel[1] = elevationData[offset + 1];
|
||||||
|
pixel[2] = elevationData[offset + 2];
|
||||||
|
pixel[3] = elevationData[offset + 3];
|
||||||
|
z1 = shadeOptions.vert * calculateElevation(pixel);
|
||||||
|
|
||||||
|
dzdy = (z1 - z0) / dp;
|
||||||
|
|
||||||
|
slope = Math.atan(Math.sqrt(dzdx * dzdx + dzdy * dzdy));
|
||||||
|
|
||||||
|
aspect = Math.atan2(dzdy, -dzdx);
|
||||||
|
if (aspect < 0) {
|
||||||
|
aspect = halfPi - aspect;
|
||||||
|
} else if (aspect > halfPi) {
|
||||||
|
aspect = twoPi - aspect + halfPi;
|
||||||
|
} else {
|
||||||
|
aspect = halfPi - aspect;
|
||||||
|
}
|
||||||
|
|
||||||
|
cosIncidence =
|
||||||
|
sinSunEl * Math.cos(slope) +
|
||||||
|
cosSunEl * Math.sin(slope) * Math.cos(sunAz - aspect);
|
||||||
|
|
||||||
|
offset = (pixelY * width + pixelX) * 4;
|
||||||
|
scaled = 255 * cosIncidence;
|
||||||
|
shadeData[offset + 1] = scaled;
|
||||||
|
shadeData[offset + 2] = scaled;
|
||||||
|
shadeData[offset + 3] = elevationData[offset + 3];
|
||||||
|
|
||||||
|
const r = shadeData[offset]
|
||||||
|
const g = shadeData[offset + 1]
|
||||||
|
const b = shadeData[offset + 2]
|
||||||
|
// (0.30*R)+(0.59*G)+(0.11*B)
|
||||||
|
arr2.push( 0.30 * r + 0.59 * g + 0.11 * b );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {rasterData: arr2, width: width, height: height};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.style({
|
||||||
|
opacity: 0.5,
|
||||||
|
domain: [0, 255],
|
||||||
|
rampColors: {
|
||||||
|
colors: [
|
||||||
|
'#fff',
|
||||||
|
'#000',
|
||||||
|
],
|
||||||
|
positions: [0, 1.0],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
scene.addLayer(layer)
|
||||||
|
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
id="map"
|
||||||
|
style={{
|
||||||
|
height: '500px',
|
||||||
|
position: 'relative',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
Loading…
Reference in New Issue