mirror of https://gitee.com/antv-l7/antv-l7
feat: 栅格瓦片的多波段计算 (#1367)
* fix: 修复 featureScale 错误 * style: lint style * fix: remove featureScalePlugin async * feat: 增加多波段瓦片的 operation 操作 * style: lint style * feat: 增加波段指定逻辑 * style: lint style * feat: 优化多波段的请求操作路径代码 * style: lint style * feat: multi raster layer support express operation 1.0 * style: lint style * feat: 增加栅格图层的多波段分析能力 * chore: add raster layer demo * style: lint style * style: lint style * style: lint style * style: lint style * style: lint style * feat: 补充栅格数据表达式计算 * chore: 调整栅格计算方法的位置 * chore: 优化栅格计算代码结构 * chore: 优化数据栅格代码 * style: lint style * style: lint style * style: lint style * feat: band operation handle empty data * feat: 支持彩色多通道栅格 * feat: 数据栅格瓦片支持彩色多通道波段计算 * style: lint style * style: lint style * style: lint style Co-authored-by: shihui <yiqianyao.yqy@alibaba-inc.com>
This commit is contained in:
parent
36dcd9928c
commit
5540d4c610
|
@ -24,57 +24,50 @@ async function getTiffData() {
|
|||
};
|
||||
}
|
||||
|
||||
async function addLayer() {
|
||||
const tiffdata = await getTiffData();
|
||||
|
||||
const layer = new RasterLayer({});
|
||||
layer
|
||||
.source(tiffdata.data, {
|
||||
parser: {
|
||||
type: 'raster',
|
||||
width: tiffdata.width,
|
||||
height: tiffdata.height,
|
||||
min: 0,
|
||||
max: 80,
|
||||
extent: [73.482190241, 3.82501784112, 135.106618732, 57.6300459963],
|
||||
},
|
||||
})
|
||||
.style({
|
||||
heightRatio: 100,
|
||||
opacity: 0.8,
|
||||
domain: [0, 2000],
|
||||
rampColors: {
|
||||
colors: [
|
||||
'#FF4818',
|
||||
'#F7B74A',
|
||||
'#FFF598',
|
||||
'#91EABC',
|
||||
'#2EA9A1',
|
||||
'#206C7C',
|
||||
].reverse(),
|
||||
positions: [0, 0.2, 0.4, 0.6, 0.8, 1.0],
|
||||
},
|
||||
});
|
||||
return layer;
|
||||
}
|
||||
|
||||
export default () => {
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
map: new GaodeMap({
|
||||
center: [121.268, 30.3628],
|
||||
zoom: 3,
|
||||
}),
|
||||
});
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
map: new GaodeMap({
|
||||
center: [121.268, 30.3628],
|
||||
zoom: 3,
|
||||
}),
|
||||
});
|
||||
|
||||
const layer = await addLayer();
|
||||
scene.on('loaded', async () => {
|
||||
const tiffdata = await getTiffData();
|
||||
|
||||
scene.on('loaded', () => {
|
||||
scene.addLayer(layer);
|
||||
});
|
||||
})();
|
||||
const layer = new RasterLayer({});
|
||||
layer
|
||||
.source(tiffdata.data, {
|
||||
parser: {
|
||||
type: 'raster',
|
||||
width: tiffdata.width,
|
||||
height: tiffdata.height,
|
||||
min: 0,
|
||||
max: 80,
|
||||
extent: [73.482190241, 3.82501784112, 135.106618732, 57.6300459963],
|
||||
},
|
||||
})
|
||||
.style({
|
||||
heightRatio: 100,
|
||||
opacity: 0.8,
|
||||
domain: [0, 2000],
|
||||
rampColors: {
|
||||
colors: [
|
||||
'#FF4818',
|
||||
'#F7B74A',
|
||||
'#FFF598',
|
||||
'#91EABC',
|
||||
'#2EA9A1',
|
||||
'#206C7C',
|
||||
].reverse(),
|
||||
positions: [0, 0.2, 0.4, 0.6, 0.8, 1.0],
|
||||
},
|
||||
});
|
||||
|
||||
scene.addLayer(layer);
|
||||
});
|
||||
}, []);
|
||||
return (
|
||||
<div
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
// @ts-ignore
|
||||
import { RasterLayer, Scene } from '@antv/l7';
|
||||
// @ts-ignore
|
||||
import { GaodeMap } from '@antv/l7-maps';
|
||||
import React, { useEffect } from 'react';
|
||||
import * as GeoTIFF from 'geotiff';
|
||||
|
||||
async function getTiffData() {
|
||||
const response = await fetch(
|
||||
'https://gw.alipayobjects.com/os/rmsportal/XKgkjjGaAzRyKupCBiYW.dat',
|
||||
);
|
||||
const arrayBuffer = await response.arrayBuffer();
|
||||
const tiff = await GeoTIFF.fromArrayBuffer(arrayBuffer);
|
||||
const image = await tiff.getImage();
|
||||
const width = image.getWidth();
|
||||
const height = image.getHeight();
|
||||
const values = await image.readRasters();
|
||||
return {
|
||||
data: values[0],
|
||||
width,
|
||||
height,
|
||||
min: 0,
|
||||
max: 8000,
|
||||
};
|
||||
}
|
||||
|
||||
export default () => {
|
||||
useEffect(() => {
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
map: new GaodeMap({
|
||||
center: [121.268, 30.3628],
|
||||
zoom: 3,
|
||||
}),
|
||||
});
|
||||
|
||||
scene.on('loaded', async () => {
|
||||
|
||||
const tiffdata = await getTiffData();
|
||||
|
||||
const layer = new RasterLayer({});
|
||||
layer
|
||||
.source(tiffdata.data, {
|
||||
parser: {
|
||||
type: 'raster',
|
||||
width: tiffdata.width,
|
||||
height: tiffdata.height,
|
||||
min: 0,
|
||||
max: 80,
|
||||
extent: [73.482190241, 3.82501784112, 135.106618732, 57.6300459963],
|
||||
},
|
||||
})
|
||||
.style({
|
||||
heightRatio: 100,
|
||||
opacity: 0.8,
|
||||
domain: [0, 2000],
|
||||
rampColors: {
|
||||
colors: [
|
||||
'#FF4818',
|
||||
'#F7B74A',
|
||||
'#FFF598',
|
||||
'#91EABC',
|
||||
'#2EA9A1',
|
||||
'#206C7C',
|
||||
].reverse(),
|
||||
positions: [0, 0.2, 0.4, 0.6, 0.8, 1.0],
|
||||
},
|
||||
});
|
||||
|
||||
scene.addLayer(layer);
|
||||
|
||||
});
|
||||
|
||||
}, []);
|
||||
return (
|
||||
<div
|
||||
id="map"
|
||||
style={{
|
||||
height: '500px',
|
||||
position: 'relative',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,93 @@
|
|||
// @ts-ignore
|
||||
import { RasterLayer, Scene } from '@antv/l7';
|
||||
// @ts-ignore
|
||||
import { GaodeMap } from '@antv/l7-maps';
|
||||
import React, { useEffect } from 'react';
|
||||
import * as GeoTIFF from 'geotiff';
|
||||
|
||||
async function getTiffData() {
|
||||
const response = await fetch(
|
||||
'https://gw.alipayobjects.com/os/rmsportal/XKgkjjGaAzRyKupCBiYW.dat',
|
||||
// 'http://127.0.0.1:3333/p.tif',
|
||||
);
|
||||
const arrayBuffer = await response.arrayBuffer();
|
||||
return arrayBuffer
|
||||
}
|
||||
|
||||
export default () => {
|
||||
useEffect(() => {
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
map: new GaodeMap({
|
||||
center: [121.268, 30.3628],
|
||||
zoom: 3,
|
||||
}),
|
||||
});
|
||||
|
||||
scene.on('loaded', async () => {
|
||||
|
||||
const tiffdata = await getTiffData();
|
||||
|
||||
const layer = new RasterLayer({})
|
||||
layer.source({
|
||||
data: tiffdata,
|
||||
bands: [0]
|
||||
}, {
|
||||
parser: {
|
||||
type: 'raster',
|
||||
// width: tiffdata.width,
|
||||
// height: tiffdata.height,
|
||||
format: async (data, bands) => {
|
||||
|
||||
const tiff = await GeoTIFF.fromArrayBuffer(data);
|
||||
const imageCount = await tiff.getImageCount();
|
||||
console.log('imageCount', imageCount)
|
||||
|
||||
const image = await tiff.getImage();
|
||||
const width = image.getWidth();
|
||||
const height = image.getHeight();
|
||||
const values = await image.readRasters();
|
||||
return { rasterData: values[0], width, height };
|
||||
|
||||
},
|
||||
// operation: (allBands) => {
|
||||
// return allBands[0].rasterData;
|
||||
// },
|
||||
operation: ['+', ['band', 0], 1],
|
||||
min: 0,
|
||||
max: 80,
|
||||
extent: [73.482190241, 3.82501784112, 135.106618732, 57.6300459963],
|
||||
},
|
||||
})
|
||||
.style({
|
||||
heightRatio: 100,
|
||||
opacity: 0.8,
|
||||
domain: [0, 2000],
|
||||
rampColors: {
|
||||
colors: [
|
||||
'#FF4818',
|
||||
'#F7B74A',
|
||||
'#FFF598',
|
||||
'#91EABC',
|
||||
'#2EA9A1',
|
||||
'#206C7C',
|
||||
].reverse(),
|
||||
positions: [0, 0.2, 0.4, 0.6, 0.8, 1.0],
|
||||
},
|
||||
});
|
||||
|
||||
scene.addLayer(layer);
|
||||
|
||||
});
|
||||
|
||||
}, []);
|
||||
return (
|
||||
<div
|
||||
id="map"
|
||||
style={{
|
||||
height: '500px',
|
||||
position: 'relative',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,94 @@
|
|||
// @ts-ignore
|
||||
import { RasterLayer, Scene } from '@antv/l7';
|
||||
// @ts-ignore
|
||||
import { GaodeMap } from '@antv/l7-maps';
|
||||
import React, { useEffect } from 'react';
|
||||
import * as GeoTIFF from 'geotiff';
|
||||
|
||||
async function getTiffData(url: string) {
|
||||
const response = await fetch(url);
|
||||
const arrayBuffer = await response.arrayBuffer();
|
||||
return arrayBuffer
|
||||
}
|
||||
|
||||
export default () => {
|
||||
useEffect(() => {
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
map: new GaodeMap({
|
||||
center: [110, 30.3628],
|
||||
zoom: 3,
|
||||
}),
|
||||
});
|
||||
|
||||
scene.on('loaded', async () => {
|
||||
//
|
||||
const url1 = 'https://gw.alipayobjects.com/os/rmsportal/XKgkjjGaAzRyKupCBiYW.dat';
|
||||
// 全国夜光图
|
||||
const url2 = 'https://gw.alipayobjects.com/zos/antvdemo/assets/light_clip/lightF182013.tiff'
|
||||
const tiffdata = await getTiffData(url1);
|
||||
const tiffdata2 = await getTiffData(url2);
|
||||
// const rasterData = { data: tiffdata }
|
||||
const rasterData = [
|
||||
{ data: tiffdata },
|
||||
{ data: tiffdata2 }
|
||||
];
|
||||
|
||||
const layer = new RasterLayer({})
|
||||
layer.source(rasterData, {
|
||||
parser: {
|
||||
type: 'raster',
|
||||
format: async (data, bands) => {
|
||||
// console.log('bands', bands)
|
||||
const tiff = await GeoTIFF.fromArrayBuffer(data);
|
||||
// const imageCount = await tiff.getImageCount();
|
||||
|
||||
const image = await tiff.getImage();
|
||||
const width = image.getWidth();
|
||||
const height = image.getHeight();
|
||||
const values = await image.readRasters();
|
||||
return { rasterData: values[0], width, height };
|
||||
|
||||
},
|
||||
// operation: (allBands) => {
|
||||
// console.log(allBands)
|
||||
// return allBands[0].rasterData;
|
||||
// },
|
||||
operation: ['+', ['+', ['band', 0], 90], ['*', ['band', 1], 50]],
|
||||
min: 0,
|
||||
max: 80,
|
||||
extent: [73.482190241, 3.82501784112, 135.106618732, 57.6300459963],
|
||||
},
|
||||
})
|
||||
.style({
|
||||
heightRatio: 100,
|
||||
opacity: 0.8,
|
||||
domain: [0, 4000],
|
||||
rampColors: {
|
||||
colors: [
|
||||
'#FF4818',
|
||||
'#F7B74A',
|
||||
'#FFF598',
|
||||
'#333',
|
||||
'#222',
|
||||
'#000',
|
||||
].reverse(),
|
||||
positions: [0, 0.2, 0.4, 0.6, 0.8, 1.0],
|
||||
},
|
||||
});
|
||||
|
||||
scene.addLayer(layer);
|
||||
|
||||
});
|
||||
|
||||
}, []);
|
||||
return (
|
||||
<div
|
||||
id="map"
|
||||
style={{
|
||||
height: '500px',
|
||||
position: 'relative',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,85 @@
|
|||
// @ts-ignore
|
||||
import { RasterLayer, Scene } from '@antv/l7';
|
||||
// @ts-ignore
|
||||
import { GaodeMap } from '@antv/l7-maps';
|
||||
import React, { useEffect } from 'react';
|
||||
import * as GeoTIFF from 'geotiff';
|
||||
|
||||
async function getTiffData(url: string) {
|
||||
const response = await fetch(url);
|
||||
const arrayBuffer = await response.arrayBuffer();
|
||||
return arrayBuffer
|
||||
}
|
||||
|
||||
export default () => {
|
||||
useEffect(() => {
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
map: new GaodeMap({
|
||||
center: [121.268, 30.3628],
|
||||
zoom: 3,
|
||||
}),
|
||||
});
|
||||
|
||||
scene.on('loaded', async () => {
|
||||
const url1 = 'https://gw.alipayobjects.com/os/rmsportal/XKgkjjGaAzRyKupCBiYW.dat';
|
||||
const url2 = 'https://gw.alipayobjects.com/zos/antvdemo/assets/light_clip/lightF182013.tiff'
|
||||
const tiffdata = await getTiffData(url1);
|
||||
const tiffdata2 = await getTiffData(url2);
|
||||
|
||||
const layer = new RasterLayer({})
|
||||
layer.source([
|
||||
{
|
||||
data: tiffdata,
|
||||
bands: [0],
|
||||
},
|
||||
{
|
||||
data: tiffdata2,
|
||||
bands: [0],
|
||||
}
|
||||
], {
|
||||
parser: {
|
||||
type: 'rasterRgb',
|
||||
format: async (data, bands) => {
|
||||
// console.log(bands, )
|
||||
const tiff = await GeoTIFF.fromArrayBuffer(data);
|
||||
const imageCount = await tiff.getImageCount();
|
||||
console.log('imageCount', imageCount, bands)
|
||||
|
||||
const image = await tiff.getImage();
|
||||
const width = image.getWidth();
|
||||
const height = image.getHeight();
|
||||
const values = await image.readRasters();
|
||||
return { rasterData: values[0], width, height };
|
||||
},
|
||||
// operation: (allBands) => {
|
||||
// return allBands[0].rasterData;
|
||||
// },
|
||||
// operation: ['+', ['band', 0], 1],
|
||||
operation: {
|
||||
r: ['*', ['band', 1], 1],
|
||||
g: ['*', ['band', 1], 1],
|
||||
b: ['*', ['band', 1], 1],
|
||||
},
|
||||
extent: [73.482190241, 3.82501784112, 135.106618732, 57.6300459963],
|
||||
},
|
||||
})
|
||||
.style({
|
||||
// opacity: 0.8,
|
||||
channelRMax: 100,
|
||||
channelGMax: 100,
|
||||
channelBMax: 100
|
||||
});
|
||||
scene.addLayer(layer);
|
||||
});
|
||||
}, []);
|
||||
return (
|
||||
<div
|
||||
id="map"
|
||||
style={{
|
||||
height: '500px',
|
||||
position: 'relative',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,118 @@
|
|||
// @ts-ignore
|
||||
import { RasterLayer, Scene } from '@antv/l7';
|
||||
// @ts-ignore
|
||||
import { GaodeMap } from '@antv/l7-maps';
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
async function getTiffData(url: string) {
|
||||
const response = await fetch(url);
|
||||
const arrayBuffer = await response.arrayBuffer();
|
||||
return arrayBuffer
|
||||
}
|
||||
|
||||
export default () => {
|
||||
useEffect(() => {
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
map: new GaodeMap({
|
||||
center: [110, 30.3628],
|
||||
zoom: 3,
|
||||
}),
|
||||
});
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
const canvasSize = 256;
|
||||
canvas.width = canvasSize;
|
||||
canvas.height = canvasSize;
|
||||
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
|
||||
scene.on('loaded', async () => {
|
||||
const url1 = 'https://gw.alipayobjects.com/mdn/rms_816329/afts/img/A*sV6gSYSdpl4AAAAAAAAAAAAAARQnAQ';
|
||||
const tiffdata = await getTiffData(url1);
|
||||
|
||||
// Gray = R*0.299 + G*0.587 + B*0.114
|
||||
const grayExp = ['+',
|
||||
['*', ['band', 0], 0.299],
|
||||
[
|
||||
'+',
|
||||
['*', ['band', 1], 0.587],
|
||||
['*', ['band', 2], 0.114]
|
||||
]
|
||||
]
|
||||
const layer = new RasterLayer({})
|
||||
layer.source([
|
||||
{
|
||||
data: tiffdata,
|
||||
bands: [0],
|
||||
},
|
||||
], {
|
||||
parser: {
|
||||
type: 'rasterRgb',
|
||||
format: async (data, bands) => {
|
||||
const blob: Blob = new Blob([new Uint8Array(data)], {
|
||||
type: 'image/png',
|
||||
});
|
||||
const img = await createImageBitmap(blob);
|
||||
|
||||
ctx.clearRect(0, 0, canvasSize, canvasSize);
|
||||
ctx.drawImage(img, 0, 0, canvasSize, canvasSize);
|
||||
const imgData = ctx.getImageData(0, 0, canvasSize, canvasSize).data;
|
||||
const channelR: number[] = [];
|
||||
const channelG: number[] = [];
|
||||
const channelB: number[] = [];
|
||||
for (let i = 0; i < imgData.length; i += 4) {
|
||||
const R = imgData[i];
|
||||
const G = imgData[i + 1];
|
||||
const B = imgData[i + 2];
|
||||
|
||||
channelR.push(R);
|
||||
channelG.push(G);
|
||||
channelB.push(B);
|
||||
}
|
||||
|
||||
return [
|
||||
{ rasterData: channelR, width: canvasSize, height: canvasSize },
|
||||
{ rasterData: channelG, width: canvasSize, height: canvasSize },
|
||||
{ rasterData: channelB, width: canvasSize, height: canvasSize }
|
||||
];
|
||||
},
|
||||
// operation: (allBands) => {
|
||||
// return allBands[0].rasterData;
|
||||
// },
|
||||
// operation: ['+', ['band', 0], 1],
|
||||
operation: {
|
||||
r: ['band', 0],
|
||||
g: ['band', 1],
|
||||
b: ['band', 2],
|
||||
// r: ['*', ['band', 0], 1],
|
||||
// g: ['*', ['band', 1], 1],
|
||||
// b: ['*', ['band', 2], 1],
|
||||
// r: grayExp,
|
||||
// g: grayExp,
|
||||
// b: grayExp,
|
||||
},
|
||||
extent: [73.482190241, 3.82501784112, 135.106618732, 57.6300459963],
|
||||
},
|
||||
})
|
||||
.style({
|
||||
// opacity: 0.8,
|
||||
// channelRMax: 100,
|
||||
// channelGMax: 100,
|
||||
// channelBMax: 100
|
||||
});
|
||||
scene.addLayer(layer);
|
||||
});
|
||||
return () => {
|
||||
scene.destroy();
|
||||
}
|
||||
}, []);
|
||||
return (
|
||||
<div
|
||||
id="map"
|
||||
style={{
|
||||
height: '500px',
|
||||
position: 'relative',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,3 @@
|
|||
### RasterLayer - raster
|
||||
#### 普通 rasterLayer
|
||||
<code src="./demos/rasterData.tsx"></code>
|
|
@ -0,0 +1,4 @@
|
|||
### RasterLayer - rasterFile
|
||||
#### raster file data
|
||||
加载单个栅格文件的二进制流数据,自己提供栅格波段数据的提取方法。
|
||||
<code src="./demos/rasterFile.tsx"></code>
|
|
@ -0,0 +1,4 @@
|
|||
### RasterLayer - rasterFile2
|
||||
#### raster file data2
|
||||
加载多个栅格文件的二进制流数据,自己提供栅格波段数据的提取方法。
|
||||
<code src="./demos/rasterFile2.tsx"></code>
|
|
@ -0,0 +1,3 @@
|
|||
### RasterLayer - rasterRgb
|
||||
#### 普通 rgb rasterLayer
|
||||
<code src="./demos/rasterRgb.tsx"></code>
|
|
@ -0,0 +1,3 @@
|
|||
### RasterLayer - rasterRgb2
|
||||
#### 普通 rgb2 rasterLayer
|
||||
<code src="./demos/rasterRgb2.tsx"></code>
|
|
@ -28,7 +28,8 @@ export default () => {
|
|||
'https://a.tiles.mapbox.com/v4/mapbox.mapbox-streets-v6/{z}/{x}/{y}.vector.pbf?access_token=';
|
||||
const token =
|
||||
// 'pk.eyJ1IjoiMTg5Njk5NDg2MTkiLCJhIjoiY2s5OXVzdHlzMDVneDNscDVjdzVmeXl0dyJ9.81SQ5qaJS0xExYLbDZAGpQ';
|
||||
'pk.eyJ1IjoiMTg5Njk5NDg2MTkiLCJhIjoiY2w3dHk3dnN4MDYzaDNycDkyMDl2bzh6NiJ9.YIrG9kwUpayLj01f6W23Gw';
|
||||
// 'pk.eyJ1IjoiMTg5Njk5NDg2MTkiLCJhIjoiY2w3dHk3dnN4MDYzaDNycDkyMDl2bzh6NiJ9.YIrG9kwUpayLj01f6W23Gw';
|
||||
'pk.eyJ1IjoiMTg5Njk5NDg2MTkiLCJhIjoiY2w4bXNyeHgzMGl0cjNvbXlmeHFjeDBwZCJ9.05W7JfyT6BVkpu12dYL58w';
|
||||
const source = new Source(url + token, {
|
||||
parser: {
|
||||
type: 'mvt',
|
||||
|
|
|
@ -27,7 +27,8 @@ export default () => {
|
|||
'https://a.tiles.mapbox.com/v4/mapbox.mapbox-streets-v6/{z}/{x}/{y}.vector.pbf?access_token=';
|
||||
const token =
|
||||
// 'pk.eyJ1IjoiMTg5Njk5NDg2MTkiLCJhIjoiY2s5OXVzdHlzMDVneDNscDVjdzVmeXl0dyJ9.81SQ5qaJS0xExYLbDZAGpQ';
|
||||
'pk.eyJ1IjoiMTg5Njk5NDg2MTkiLCJhIjoiY2w3dHk3dnN4MDYzaDNycDkyMDl2bzh6NiJ9.YIrG9kwUpayLj01f6W23Gw';
|
||||
// 'pk.eyJ1IjoiMTg5Njk5NDg2MTkiLCJhIjoiY2w3dHk3dnN4MDYzaDNycDkyMDl2bzh6NiJ9.YIrG9kwUpayLj01f6W23Gw';
|
||||
'pk.eyJ1IjoiMTg5Njk5NDg2MTkiLCJhIjoiY2w4bXNyeHgzMGl0cjNvbXlmeHFjeDBwZCJ9.05W7JfyT6BVkpu12dYL58w';
|
||||
const source = new Source(url + token, {
|
||||
parser: {
|
||||
type: 'mvt',
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
### Raster - HillShade
|
||||
山体阴影
|
||||
<code src="./rasterData/hillshade.tsx"></code>
|
||||
<code src="./rasterData/hillShading.tsx"></code>
|
||||
|
|
|
@ -12,7 +12,7 @@ export default () => {
|
|||
center: [-119.5591, 37.715],
|
||||
zoom: 9,
|
||||
style: 'mapbox://styles/mapbox/cjaudgl840gn32rnrepcb9b9g',
|
||||
token: 'pk.eyJ1IjoiMTg5Njk5NDg2MTkiLCJhIjoiY2w3dHk3dnN4MDYzaDNycDkyMDl2bzh6NiJ9.YIrG9kwUpayLj01f6W23Gw'
|
||||
token: 'pk.eyJ1IjoiMTg5Njk5NDg2MTkiLCJhIjoiY2w4bXNyeHgzMGl0cjNvbXlmeHFjeDBwZCJ9.05W7JfyT6BVkpu12dYL58w'
|
||||
}),
|
||||
});
|
||||
|
||||
|
@ -26,7 +26,7 @@ export default () => {
|
|||
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://api.mapbox.com/raster/v1/mapbox.mapbox-terrain-dem-v1/{z}/{x}/{y}.webp?sku=101xrmKJip7uQ&access_token=pk.eyJ1IjoiMTg5Njk5NDg2MTkiLCJhIjoiY2w4bXNyeHgzMGl0cjNvbXlmeHFjeDBwZCJ9.05W7JfyT6BVkpu12dYL58w',
|
||||
// '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
|
||||
{
|
|
@ -1,205 +0,0 @@
|
|||
//@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',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -25,8 +25,7 @@ export default () => {
|
|||
const layer = new RasterLayer();
|
||||
layer
|
||||
.source(
|
||||
'https://api.mapbox.com/v4/mapbox.terrain-rgb/{z}/{x}/{y}.pngraw?access_token=pk.eyJ1IjoiMTg5Njk5NDg2MTkiLCJhIjoiY2w3dHk3dnN4MDYzaDNycDkyMDl2bzh6NiJ9.YIrG9kwUpayLj01f6W23Gw',
|
||||
|
||||
'http://webst0{1-4}.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}',
|
||||
{
|
||||
parser: {
|
||||
type: 'rasterTile',
|
||||
|
@ -46,10 +45,7 @@ export default () => {
|
|||
const arr: number[] = [];
|
||||
for (let i = 0; i < imgData.length; i += 4) {
|
||||
const R = imgData[i];
|
||||
const G = imgData[i + 1];
|
||||
const B = imgData[i + 2];
|
||||
const d = -10000 + (R * 256 * 256 + G * 256 + B) * 0.1;
|
||||
arr.push(d);
|
||||
arr.push(R);
|
||||
}
|
||||
return {
|
||||
rasterData: arr,
|
||||
|
@ -61,7 +57,7 @@ export default () => {
|
|||
},
|
||||
)
|
||||
.style({
|
||||
domain: [0, 1014],
|
||||
domain: [0, 255],
|
||||
clampLow: true,
|
||||
rampColors: {
|
||||
colors: [
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
//@ts-ignore
|
||||
import { Scene, RasterLayer } from '@antv/l7';
|
||||
//@ts-ignore
|
||||
import { Map } from '@antv/l7-maps';
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
export default () => {
|
||||
useEffect(() => {
|
||||
const scene = new Scene({
|
||||
id: 'map2',
|
||||
map: new Map({
|
||||
center: [105.732421875, 32.24997445586331],
|
||||
pitch: 0,
|
||||
style: 'dark',
|
||||
zoom: 2,
|
||||
}),
|
||||
});
|
||||
|
||||
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(
|
||||
'http://webst0{1-4}.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}',
|
||||
{
|
||||
parser: {
|
||||
type: 'rasterTile',
|
||||
// dataType: 'arraybuffer',
|
||||
dataType: 'rgb',
|
||||
tileSize: 256,
|
||||
zoomOffset: 0,
|
||||
extent: [-180, -85.051129, 179, 85.051129],
|
||||
minZoom: 0,
|
||||
format: async (data: any, bands) => {
|
||||
// console.log(bands)
|
||||
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 imgData = ctx.getImageData(0, 0, 256, 256).data;
|
||||
const channelR: number[] = [];
|
||||
const channelG: number[] = [];
|
||||
const channelB: number[] = [];
|
||||
for (let i = 0; i < imgData.length; i += 4) {
|
||||
const R = imgData[i];
|
||||
const G = imgData[i + 1];
|
||||
const B = imgData[i + 2];
|
||||
channelR.push(R);
|
||||
channelG.push(G);
|
||||
channelB.push(B);
|
||||
}
|
||||
return [
|
||||
{ rasterData: channelR, width: 256, height: 256 },
|
||||
{ rasterData: channelG, width: 256, height: 256 },
|
||||
{ rasterData: channelB, width: 256, height: 256 }
|
||||
]
|
||||
},
|
||||
// operation: ['band', 0]
|
||||
// operation: (allBands) => {
|
||||
// // console.log(allBands)
|
||||
// return allBands[0].rasterData
|
||||
// }
|
||||
operation: {
|
||||
r: ['band', 0],
|
||||
g: ['band', 1],
|
||||
b: ['band', 2],
|
||||
}
|
||||
},
|
||||
},
|
||||
)
|
||||
.style({
|
||||
});
|
||||
|
||||
scene.addLayer(layer)
|
||||
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
id="map2"
|
||||
style={{
|
||||
height: '500px',
|
||||
position: 'relative',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -1,4 +1,6 @@
|
|||
// @ts-ignore
|
||||
import { RasterLayer, Scene, Source } from '@antv/l7';
|
||||
// @ts-ignore
|
||||
import { GaodeMap } from '@antv/l7-maps';
|
||||
import React, { useEffect } from 'react';
|
||||
import * as GeoTIFF from 'geotiff';
|
||||
|
@ -80,9 +82,19 @@ export default () => {
|
|||
maskfence: maskData
|
||||
});
|
||||
|
||||
// const urls = [
|
||||
// 'https://ganos.oss-cn-hangzhou.aliyuncs.com/m2/l7/tiff_jx/{z}/{x}/{y}.tiff',
|
||||
// 'https://ganos.oss-cn-hangzhou.aliyuncs.com/m2/l7/tiff_jx/{z}/{x}/{y}.tiff',
|
||||
// ]
|
||||
const urls = [
|
||||
'https://ganos.oss-cn-hangzhou.aliyuncs.com/m2/l7/tiff_jx/{z}/{x}/{y}.tiff',
|
||||
'https://ganos.oss-cn-hangzhou.aliyuncs.com/m2/l7/tiff_jx/{z}/{x}/{y}.tiff',
|
||||
{
|
||||
url: 'https://ganos.oss-cn-hangzhou.aliyuncs.com/m2/l7/tiff_jx/{z}/{x}/{y}.tiff',
|
||||
bands: [0]
|
||||
},
|
||||
{
|
||||
url: 'https://ganos.oss-cn-hangzhou.aliyuncs.com/m2/l7/tiff_jx/{z}/{x}/{y}.tiff'
|
||||
// default bands: [0]
|
||||
}
|
||||
]
|
||||
const tileSource = new Source(urls,
|
||||
{
|
||||
|
@ -91,29 +103,38 @@ export default () => {
|
|||
dataType: 'arraybuffer',
|
||||
tileSize: 256,
|
||||
maxZoom: 13.1,
|
||||
format: async data => {
|
||||
format: async (data, bands) => {
|
||||
// current raster file bands
|
||||
console.log('bands', bands)
|
||||
|
||||
const tiff = await GeoTIFF.fromArrayBuffer(data[0]);
|
||||
const image = await tiff.getImage();
|
||||
const tiff = await GeoTIFF.fromArrayBuffer(data);
|
||||
const image = await tiff.getImage(bands[0]);
|
||||
const width = image.getWidth();
|
||||
const height = image.getHeight();
|
||||
const values = await image.readRasters();
|
||||
const rasterData = values[0];
|
||||
|
||||
// return bandData | bandData[]
|
||||
// return [{ rasterData, width, height }];
|
||||
return { rasterData, width, height };
|
||||
},
|
||||
// bands: [0, 1]
|
||||
operation: ['*', ['band', 0], 2],
|
||||
// operation: (allBands) => {
|
||||
// const rasterData: number[] = [];
|
||||
// const { width, height } = allBands[0];
|
||||
// const length = width * height;
|
||||
// const band0 = allBands[0];
|
||||
// const band1 = allBands[1];
|
||||
|
||||
const tiff2 = await GeoTIFF.fromArrayBuffer(data[1]);
|
||||
const image2 = await tiff2.getImage();
|
||||
const values2 = await image2.readRasters();
|
||||
const rasterData2 = values2[0];
|
||||
|
||||
const r = rasterData.map((d, i) => {
|
||||
|
||||
const d2 = rasterData2[i]
|
||||
return d/2 + d2/3
|
||||
})
|
||||
|
||||
return { rasterData: r, width, height };
|
||||
}
|
||||
// for(let i = 0;i < length; i++) {
|
||||
// const v1 = band0.rasterData[i] | 0;
|
||||
// const v2 = band1.rasterData[i] | 0;
|
||||
// rasterData.push(v1 * (1/2) + v2 * (1/2))
|
||||
// }
|
||||
// return rasterData;
|
||||
// // return bands[0].rasterData;
|
||||
// }
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
### Raster - RasterData RGB
|
||||
彩色数据栅格(多通道)
|
||||
|
||||
#### 加载 image
|
||||
<code src="./rasterData/loadImageRGB.tsx"></code>
|
|
@ -579,7 +579,7 @@ export default class BaseLayer<ChildLayerStyleOptions = {}>
|
|||
}
|
||||
|
||||
public source(data: any, options?: ISourceCFG): ILayer {
|
||||
if (data?.data) {
|
||||
if (data?.type === 'source') {
|
||||
// 判断是否为source
|
||||
this.setSource(data);
|
||||
return this;
|
||||
|
|
|
@ -279,4 +279,8 @@ export interface IRasterLayerStyleOptions extends IBaseLayerStyleOptions {
|
|||
clampHigh: boolean;
|
||||
rampColors: IColorRamp;
|
||||
rampColorsData?: ImageData | IImagedata;
|
||||
|
||||
channelRMax?: number;
|
||||
channelGMax?: number;
|
||||
channelBMax?: number;
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ export default class RaterLayer extends BaseLayer<IRasterLayerStyleOptions> {
|
|||
const type = this.getModelType();
|
||||
const defaultConfig = {
|
||||
raster: {},
|
||||
rasterRgb: {},
|
||||
raster3d: {},
|
||||
rasterTile: {},
|
||||
};
|
||||
|
@ -42,6 +43,8 @@ export default class RaterLayer extends BaseLayer<IRasterLayerStyleOptions> {
|
|||
switch (parserType) {
|
||||
case 'raster':
|
||||
return 'raster';
|
||||
case 'rasterRgb':
|
||||
return 'rasterRgb';
|
||||
case 'rasterTile':
|
||||
return 'rasterTile';
|
||||
default:
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import RasterTileModel from '../../tile/models/tileModel';
|
||||
import RasterModel from './raster';
|
||||
export type RasterModelType = 'raster' | 'raster3d' | 'rasterTile';
|
||||
import RasterRgbModel from './rasterRgb';
|
||||
export type RasterModelType = 'raster' | 'raster3d' | 'rasterTile' | 'rasterRgb';
|
||||
|
||||
const RasterModels: { [key in RasterModelType]: any } = {
|
||||
raster: RasterModel,
|
||||
rasterRgb: RasterRgbModel,
|
||||
raster3d: RasterModel,
|
||||
rasterTile: RasterTileModel,
|
||||
};
|
||||
|
|
|
@ -41,7 +41,26 @@ export default class RasterModel extends BaseModel {
|
|||
};
|
||||
}
|
||||
|
||||
public initModels(callbackModel: (models: IModel[]) => void) {
|
||||
private async getRasterData(parserDataItem: any) {
|
||||
if(Array.isArray(parserDataItem.data)) {
|
||||
// 直接传入波段数据
|
||||
return {
|
||||
data: parserDataItem.data,
|
||||
width: parserDataItem.width,
|
||||
height: parserDataItem.height,
|
||||
}
|
||||
} else {
|
||||
// 多波段形式、需要进行处理
|
||||
const { rasterData, width, height } = await parserDataItem.data;
|
||||
return {
|
||||
data: Array.from(rasterData),
|
||||
width,
|
||||
height
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async initModels(callbackModel: (models: IModel[]) => void) {
|
||||
const {
|
||||
mask = false,
|
||||
maskInside = true,
|
||||
|
@ -51,10 +70,13 @@ export default class RasterModel extends BaseModel {
|
|||
const source = this.layer.getSource();
|
||||
const { createTexture2D } = this.rendererService;
|
||||
const parserDataItem = source.data.dataArray[0];
|
||||
|
||||
const {data, width, height} = await this.getRasterData(parserDataItem);
|
||||
|
||||
this.texture = createTexture2D({
|
||||
data: parserDataItem.data,
|
||||
width: parserDataItem.width,
|
||||
height: parserDataItem.height,
|
||||
data,
|
||||
width,
|
||||
height,
|
||||
format: gl.LUMINANCE,
|
||||
type: gl.FLOAT,
|
||||
// aniso: 4,
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
import {
|
||||
AttributeType,
|
||||
gl,
|
||||
IEncodeFeature,
|
||||
IModel,
|
||||
ITexture2D,
|
||||
} from '@antv/l7-core';
|
||||
import { getMask } from '@antv/l7-utils';
|
||||
import BaseModel from '../../core/BaseModel';
|
||||
import { IRasterLayerStyleOptions } from '../../core/interface';
|
||||
import { RasterImageTriangulation } from '../../core/triangulation';
|
||||
import rasterFrag from '../shaders/raster_rgb_frag.glsl';
|
||||
import rasterVert from '../shaders/raster_2d_vert.glsl';
|
||||
export default class RasterModel extends BaseModel {
|
||||
protected texture: ITexture2D;
|
||||
protected channelRMax: number = 256;
|
||||
protected channelGMax: number = 256;
|
||||
protected channelBMax: number = 256;
|
||||
|
||||
public getUninforms() {
|
||||
const {
|
||||
opacity = 1,
|
||||
channelRMax,
|
||||
channelGMax,
|
||||
channelBMax
|
||||
} = this.layer.getLayerConfig() as IRasterLayerStyleOptions;
|
||||
return {
|
||||
u_opacity: opacity || 1,
|
||||
u_texture: this.texture,
|
||||
u_channelRMax: channelRMax !== undefined ? channelRMax : this.channelRMax,
|
||||
u_channelGMax: channelGMax !== undefined ? channelGMax : this.channelGMax,
|
||||
u_channelBMax: channelBMax !== undefined ? channelBMax : this.channelBMax,
|
||||
};
|
||||
}
|
||||
|
||||
private async getRasterData(parserDataItem: any) {
|
||||
if(Array.isArray(parserDataItem.data)) {
|
||||
// 直接传入波段数据
|
||||
return {
|
||||
data: parserDataItem.data,
|
||||
width: parserDataItem.width,
|
||||
height: parserDataItem.height,
|
||||
}
|
||||
} else {
|
||||
// 多波段形式、需要进行处理
|
||||
// 支持彩色栅格(多通道)
|
||||
const { rasterData, width, height, channelR, channelG, channelB } = await parserDataItem.data;
|
||||
this.channelRMax = channelR;
|
||||
this.channelGMax = channelG;
|
||||
this.channelBMax = channelB;
|
||||
return {
|
||||
data: Array.from(rasterData),
|
||||
width,
|
||||
height
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async initModels(callbackModel: (models: IModel[]) => void) {
|
||||
const {
|
||||
mask = false,
|
||||
maskInside = true,
|
||||
} = this.layer.getLayerConfig() as IRasterLayerStyleOptions;
|
||||
const source = this.layer.getSource();
|
||||
const { createTexture2D } = this.rendererService;
|
||||
const parserDataItem = source.data.dataArray[0];
|
||||
|
||||
const {data, width, height} = await this.getRasterData(parserDataItem);
|
||||
this.texture = createTexture2D({
|
||||
// @ts-ignore
|
||||
data,
|
||||
width,
|
||||
height,
|
||||
format: gl.RGB,
|
||||
type: gl.FLOAT,
|
||||
});
|
||||
|
||||
this.layer
|
||||
.buildLayerModel({
|
||||
moduleName: 'rasterImageDataRGBA',
|
||||
vertexShader: rasterVert,
|
||||
fragmentShader: rasterFrag,
|
||||
triangulation: RasterImageTriangulation,
|
||||
primitive: gl.TRIANGLES,
|
||||
depth: { enable: false },
|
||||
stencil: getMask(mask, maskInside),
|
||||
pick: false,
|
||||
})
|
||||
.then((model) => {
|
||||
callbackModel([model]);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.warn(err);
|
||||
callbackModel([]);
|
||||
});
|
||||
}
|
||||
|
||||
public buildModels(callbackModel: (models: IModel[]) => void) {
|
||||
this.initModels(callbackModel);
|
||||
}
|
||||
|
||||
public clearModels(): void {
|
||||
this.texture?.destroy();
|
||||
}
|
||||
|
||||
protected registerBuiltinAttributes() {
|
||||
// point layer size;
|
||||
this.styleAttributeService.registerStyleAttribute({
|
||||
name: 'uv',
|
||||
type: AttributeType.Attribute,
|
||||
descriptor: {
|
||||
name: 'a_Uv',
|
||||
buffer: {
|
||||
// give the WebGL driver a hint that this buffer may change
|
||||
usage: gl.DYNAMIC_DRAW,
|
||||
data: [],
|
||||
type: gl.FLOAT,
|
||||
},
|
||||
size: 2,
|
||||
update: (
|
||||
feature: IEncodeFeature,
|
||||
featureIdx: number,
|
||||
vertex: number[],
|
||||
) => {
|
||||
return [vertex[3], vertex[4]];
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
precision mediump float;
|
||||
uniform float u_opacity: 1.0;
|
||||
uniform sampler2D u_texture;
|
||||
uniform float u_channelRMax: 256.;
|
||||
uniform float u_channelGMax: 256.;
|
||||
uniform float u_channelBMax: 256.;
|
||||
varying vec2 v_texCoord;
|
||||
|
||||
void main() {
|
||||
vec3 rgb = texture2D(u_texture,vec2(v_texCoord.x,v_texCoord.y)).rgb;
|
||||
gl_FragColor = vec4(rgb.r/u_channelRMax, rgb.g/u_channelGMax, rgb.b/u_channelBMax, u_opacity);
|
||||
}
|
|
@ -22,7 +22,6 @@ export default class RasterTiffTile extends TileFactory {
|
|||
clampLow,
|
||||
mask,
|
||||
} = initOptions;
|
||||
|
||||
const rasterData = tile.data;
|
||||
if (!rasterData.data) {
|
||||
console.warn('raster data not exist!');
|
||||
|
@ -31,13 +30,15 @@ export default class RasterTiffTile extends TileFactory {
|
|||
layerIDList: [],
|
||||
};
|
||||
}
|
||||
const dataType = this.parentLayer?.getSource()?.parser?.dataType;
|
||||
const layer = new RasterDataLayer({
|
||||
visible: tile.isVisible,
|
||||
mask,
|
||||
})
|
||||
.source(rasterData.data, {
|
||||
parser: {
|
||||
type: 'raster',
|
||||
// 数据栅格分为单通道栅格和多通道彩色栅格
|
||||
type: dataType === 'rgb' ? 'rasterRgb': 'raster',
|
||||
width: rasterData.width,
|
||||
height: rasterData.height,
|
||||
extent: tile.bboxPolygon.bbox,
|
||||
|
@ -46,6 +47,7 @@ export default class RasterTiffTile extends TileFactory {
|
|||
.style({
|
||||
colorTexture,
|
||||
opacity,
|
||||
// TODO: 目前从 domain 从父瓦片图层的 style 进行配置,后续考虑从每个时机请求的栅格文件中进行配置
|
||||
domain,
|
||||
clampHigh,
|
||||
clampLow,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import BaseLayer from '../../core/BaseLayer';
|
||||
import { IRasterLayerStyleOptions } from '../../core/interface';
|
||||
import RasterModel from '../../raster/models/rasterTile';
|
||||
import RasterRgbModel from '../../raster/models/rasterRgb';
|
||||
|
||||
export default class RasterTiffLayer extends BaseLayer<
|
||||
Partial<IRasterLayerStyleOptions>
|
||||
|
@ -21,7 +22,11 @@ export default class RasterTiffLayer extends BaseLayer<
|
|||
}
|
||||
|
||||
protected getModelType() {
|
||||
return RasterModel;
|
||||
if(this.layerSource.parser.type === 'rasterRgb') {
|
||||
return RasterRgbModel;
|
||||
} else {
|
||||
return RasterModel;
|
||||
}
|
||||
}
|
||||
protected getConfigSchema() {
|
||||
return {
|
||||
|
|
|
@ -6,6 +6,7 @@ import json from './parser/json';
|
|||
import mapboxVectorTile from './parser/mvt';
|
||||
import geojsonVTTile from './parser/geojsonvt';
|
||||
import raster from './parser/raster';
|
||||
import rasterRgb from './parser/rasterRgb';
|
||||
import rasterTile, { rasterDataTypes } from './parser/raster-tile';
|
||||
import testTile from './parser/testTile';
|
||||
import Source from './source';
|
||||
|
@ -25,6 +26,7 @@ registerParser('image', image);
|
|||
registerParser('csv', csv);
|
||||
registerParser('json', json);
|
||||
registerParser('raster', raster);
|
||||
registerParser('rasterRgb', rasterRgb);
|
||||
registerTransform('cluster', cluster);
|
||||
registerTransform('filter', filter);
|
||||
registerTransform('join', join);
|
||||
|
|
|
@ -26,6 +26,7 @@ export interface IParserData {
|
|||
export enum RasterTileType {
|
||||
IMAGE = 'image',
|
||||
ARRAYBUFFER = 'arraybuffer',
|
||||
RGB = 'rgb',
|
||||
}
|
||||
|
||||
export interface IGeojsonvtOptions {
|
||||
|
@ -58,9 +59,41 @@ export interface ITileParserCFG {
|
|||
geojsonvtOptions?: IGeojsonvtOptions;
|
||||
|
||||
format?: any;
|
||||
operation?: any;
|
||||
}
|
||||
|
||||
export interface IJsonItem {
|
||||
[key: string]: any;
|
||||
}
|
||||
export type IJsonData = IJsonItem[];
|
||||
|
||||
export interface IRasterData {
|
||||
rasterData: HTMLImageElement | Uint8Array| ImageBitmap | null | undefined;
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
export type IRasterFormat = (imageData: ArrayBuffer, bands: number[], channels?: string[]) => Promise<IRasterData|IRasterData[]>;
|
||||
export interface IRasterFileData {
|
||||
data: ArrayBuffer;
|
||||
bands: number[];
|
||||
}
|
||||
|
||||
export type IRgbOperation = {
|
||||
r?: any[];
|
||||
g?: any[]
|
||||
b?: any[]
|
||||
};
|
||||
|
||||
export type IBandsOperation = ((bands: IRasterData[]) => Uint8Array | Array<number>) | any[] | IRgbOperation;
|
||||
|
||||
export type IRasterLayerData = number[] | IRasterFileData | IRasterFileData[];
|
||||
|
||||
export interface IRasterCfg {
|
||||
format?: IRasterFormat;
|
||||
operation?: IBandsOperation;
|
||||
extent: [number, number, number, number];
|
||||
width: number;
|
||||
height: number;
|
||||
max: number;
|
||||
min: number;
|
||||
}
|
|
@ -1,6 +1,15 @@
|
|||
import { Tile, TileLoadParams, TilesetManagerOptions } from '@antv/l7-utils';
|
||||
import {
|
||||
Tile,
|
||||
TileLoadParams,
|
||||
TilesetManagerOptions,
|
||||
ITileBand,
|
||||
} from '@antv/l7-utils';
|
||||
import { IParserData, ITileParserCFG, RasterTileType } from '../interface';
|
||||
import { defaultFormat, getTileBuffer, getTileImage } from '../utils/getTile';
|
||||
import {
|
||||
defaultFormat,
|
||||
getTileBuffer,
|
||||
getTileImage,
|
||||
} from '../utils/tile/getRasterTile';
|
||||
|
||||
const DEFAULT_CONFIG: Partial<TilesetManagerOptions> = {
|
||||
tileSize: 256,
|
||||
|
@ -9,7 +18,13 @@ const DEFAULT_CONFIG: Partial<TilesetManagerOptions> = {
|
|||
zoomOffset: 0,
|
||||
};
|
||||
|
||||
export const rasterDataTypes = [RasterTileType.ARRAYBUFFER];
|
||||
export const rasterDataTypes = [RasterTileType.ARRAYBUFFER, RasterTileType.RGB];
|
||||
|
||||
function isUrlError(url: string | string[] | ITileBand[]) {
|
||||
if (Array.isArray(url) && url.length === 0) return true;
|
||||
if (!Array.isArray(url) && typeof url !== 'string') return true;
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param data
|
||||
|
@ -17,23 +32,29 @@ export const rasterDataTypes = [RasterTileType.ARRAYBUFFER];
|
|||
* @returns
|
||||
*/
|
||||
export default function rasterTile(
|
||||
data: string | string[],
|
||||
data: string | string[] | ITileBand[],
|
||||
cfg?: ITileParserCFG,
|
||||
): IParserData {
|
||||
const tileDataType: RasterTileType = cfg?.dataType || RasterTileType.IMAGE;
|
||||
if (isUrlError(data)) throw new Error('tile server url is error');
|
||||
|
||||
let tileDataType: RasterTileType = cfg?.dataType || RasterTileType.IMAGE;
|
||||
// Tip: RasterTileType.RGB 是彩色多通道的数据纹理,同样走数据纹理的请求
|
||||
if (tileDataType === RasterTileType.RGB)
|
||||
tileDataType = RasterTileType.ARRAYBUFFER;
|
||||
const getTileData = (tileParams: TileLoadParams, tile: Tile) => {
|
||||
switch (tileDataType) {
|
||||
case RasterTileType.IMAGE:
|
||||
return getTileImage(data, tileParams, tile);
|
||||
return getTileImage(data as string | string[], tileParams, tile);
|
||||
case RasterTileType.ARRAYBUFFER:
|
||||
return getTileBuffer(
|
||||
data,
|
||||
tileParams,
|
||||
tile,
|
||||
cfg?.format || defaultFormat,
|
||||
cfg?.operation,
|
||||
);
|
||||
default:
|
||||
return getTileImage(data, tileParams, tile);
|
||||
return getTileImage(data as string | string[], tileParams, tile);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,14 +1,36 @@
|
|||
import { IParserData, IRasterCfg } from '@antv/l7-core';
|
||||
export default function raster(data: number[], cfg: IRasterCfg): IParserData {
|
||||
const { extent, width, height, min, max } = cfg;
|
||||
import { IParserData } from '@antv/l7-core';
|
||||
import { IRasterLayerData, IRasterCfg, IRasterFileData } from '../interface';
|
||||
import { bandsOperation } from '../utils/bandOperation/bands';
|
||||
import { isNumberArray } from '../utils/util';
|
||||
|
||||
export default function raster(
|
||||
data: IRasterLayerData,
|
||||
cfg: IRasterCfg,
|
||||
): IParserData {
|
||||
const { extent, width, height, min, max, format, operation } = cfg;
|
||||
let bandData, rasterWidth, rasterHeight;
|
||||
if (format === undefined || isNumberArray(data)) {
|
||||
// 兼容写法 - 用户直接传入解析完的波段数据
|
||||
bandData = Array.from(data as number[]);
|
||||
rasterWidth = width;
|
||||
rasterHeight = height;
|
||||
} else {
|
||||
// 用户传入为解析的栅格数据 - arraybuffer
|
||||
// 将数据统一为 IRasterFileData[]
|
||||
const imageDataList = (Array.isArray(data)
|
||||
? data
|
||||
: [data]) as IRasterFileData[];
|
||||
bandData = bandsOperation(imageDataList, format, operation);
|
||||
}
|
||||
|
||||
const resultData = {
|
||||
_id: 1,
|
||||
dataArray: [
|
||||
{
|
||||
_id: 1,
|
||||
data: Array.from(data),
|
||||
width,
|
||||
height,
|
||||
data: bandData,
|
||||
width: rasterWidth,
|
||||
height: rasterHeight,
|
||||
min,
|
||||
max,
|
||||
coordinates: [
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
import { IParserData } from '@antv/l7-core';
|
||||
import { IRasterLayerData, IRasterCfg, IRasterFileData } from '../interface';
|
||||
import { bandsOperation } from '../utils/bandOperation/bands';
|
||||
import { isNumberArray } from '../utils/util';
|
||||
|
||||
export default function rasterRgb(
|
||||
data: IRasterLayerData,
|
||||
cfg: IRasterCfg,
|
||||
): IParserData {
|
||||
const { extent, width, height, min, max, format, operation } = cfg;
|
||||
let bandData, rasterWidth, rasterHeight;
|
||||
if (format === undefined || isNumberArray(data)) {
|
||||
// 兼容写法 - 用户直接传入解析完的波段数据
|
||||
bandData = Array.from(data as number[]);
|
||||
rasterWidth = width;
|
||||
rasterHeight = height;
|
||||
} else {
|
||||
// 用户传入为解析的栅格数据 - arraybuffer
|
||||
// 将数据统一为 IRasterFileData[]
|
||||
const imageDataList = (Array.isArray(data)
|
||||
? data
|
||||
: [data]) as IRasterFileData[];
|
||||
bandData = bandsOperation(imageDataList, format, operation);
|
||||
}
|
||||
|
||||
const resultData = {
|
||||
_id: 1,
|
||||
dataArray: [
|
||||
{
|
||||
_id: 1,
|
||||
data: bandData,
|
||||
width: rasterWidth,
|
||||
height: rasterHeight,
|
||||
min,
|
||||
max,
|
||||
coordinates: [
|
||||
[extent[0], extent[1]],
|
||||
[extent[2], extent[3]],
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
return resultData;
|
||||
}
|
|
@ -33,6 +33,7 @@ function mergeCustomizer(objValue: any, srcValue: any) {
|
|||
}
|
||||
|
||||
export default class Source extends EventEmitter implements ISource {
|
||||
public type: string = 'source';
|
||||
public inited: boolean = false;
|
||||
public data: IParserData;
|
||||
public center: [number, number];
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
import { IRasterFileData, IRasterFormat, IBandsOperation, IRgbOperation, IRasterData } from '../../interface';
|
||||
import { calculate } from './math';
|
||||
import {
|
||||
ResponseCallback,
|
||||
} from '@antv/l7-utils';
|
||||
|
||||
/**
|
||||
* 执行波段计算 format + operation
|
||||
* @param imageDataList
|
||||
* @param rasterFormat
|
||||
* @param operation
|
||||
* @returns
|
||||
*/
|
||||
export async function bandsOperation(imageDataList: IRasterFileData[], rasterFormat: IRasterFormat, operation: IBandsOperation|undefined) {
|
||||
if(imageDataList.length === 0) {
|
||||
return {
|
||||
rasterData: [0],
|
||||
width: 1,
|
||||
heigh: 1
|
||||
}
|
||||
}
|
||||
|
||||
let bandsData = (await Promise.all(
|
||||
imageDataList.map(({ data, bands = [0] }) => rasterFormat(data, bands)),
|
||||
)) as IRasterData[];
|
||||
// @ts-ignore
|
||||
bandsData = bandsData.flat();
|
||||
// Tip: rasterFormat 返回值 rasterData|rasterData[]
|
||||
|
||||
// 多个栅格数据必须是相同大小才能进行相互之间的运算
|
||||
const { width, height } = bandsData[0];
|
||||
|
||||
type IOperationResult = HTMLImageElement | Uint8Array | ImageBitmap | null | undefined
|
||||
let rasterData: IOperationResult|IOperationResult[];
|
||||
let channelR = undefined;
|
||||
let channelG = undefined;
|
||||
let channelB = undefined;
|
||||
switch (typeof operation) {
|
||||
case 'function':
|
||||
rasterData = operation(bandsData) as Uint8Array;
|
||||
break;
|
||||
case 'object':
|
||||
// 波段计算表达式 - operation
|
||||
// operation: ['+', ['band', 0], 1]
|
||||
/**
|
||||
* operation: {
|
||||
* r: ['+', ['band', 0], 1],
|
||||
* g: ['+', ['band', 0], 1],
|
||||
* b: ['+', ['band', 0], 1],
|
||||
* }
|
||||
*/
|
||||
if(!Array.isArray(operation)) {
|
||||
const rgbBands = getRgbBands(operation, bandsData);
|
||||
const {
|
||||
data,
|
||||
channelRMax,
|
||||
channelGMax,
|
||||
channelBMax
|
||||
} = combineRGBChannels(rgbBands);
|
||||
rasterData = data as unknown as Uint8Array;
|
||||
channelR = channelRMax;
|
||||
channelG = channelGMax;
|
||||
channelB = channelBMax;
|
||||
} else {
|
||||
rasterData = calculate(operation as any[], bandsData);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
rasterData = bandsData[0].rasterData;
|
||||
}
|
||||
return {
|
||||
rasterData,
|
||||
width,
|
||||
height,
|
||||
channelR,
|
||||
channelG,
|
||||
channelB
|
||||
}
|
||||
}
|
||||
|
||||
function getRgbBands(operation: IRgbOperation, bandsData: IRasterData[]) {
|
||||
if(operation.r === undefined) console.warn('Channel R lost in Operation! Use band[0] to fill!');
|
||||
if(operation.g === undefined) console.warn('Channel G lost in Operation! Use band[0] to fill!');
|
||||
if(operation.b === undefined) console.warn('Channel B lost in Operation! Use band[0] to fill!');
|
||||
const r = calculate(operation.r || ['band', 0], bandsData);
|
||||
const g = calculate(operation.g || ['band', 0], bandsData);
|
||||
const b = calculate(operation.b || ['band', 0], bandsData);
|
||||
return [r, g, b];
|
||||
}
|
||||
|
||||
/**
|
||||
* 将波段数据进行合并操作(彩色多通道)
|
||||
* @param bandsData
|
||||
*/
|
||||
function combineRGBChannels(bandsData: Uint8Array[]) {
|
||||
const channelR = bandsData[0];
|
||||
const channelG = bandsData[1];
|
||||
const channelB = bandsData[2];
|
||||
const data = [];
|
||||
let channelRMax = -Infinity;
|
||||
let channelGMax = -Infinity;
|
||||
let channelBMax = -Infinity;
|
||||
for(let i = 0;i < channelR.length;i++) {
|
||||
data.push(channelR[i]);channelRMax = Math.max(channelRMax, channelR[i]);
|
||||
data.push((channelG)[i]);channelGMax = Math.max(channelGMax, channelR[i]);
|
||||
data.push((channelB)[i]);channelBMax = Math.max(channelBMax, channelR[i]);
|
||||
}
|
||||
return {
|
||||
data,
|
||||
channelRMax,
|
||||
channelGMax,
|
||||
channelBMax
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理每个请求得到的栅格文件数据
|
||||
*/
|
||||
export async function handleRasterFiles(
|
||||
rasterFiles: IRasterFileData[],
|
||||
rasterFormat: IRasterFormat,
|
||||
operation: IBandsOperation | undefined,
|
||||
callback: ResponseCallback<any>
|
||||
) {
|
||||
const { rasterData, width, height } = await bandsOperation(rasterFiles, rasterFormat, operation)
|
||||
// 目前 max|min 没有生效
|
||||
const defaultMIN = 0;
|
||||
const defaultMAX = 8000;
|
||||
callback(null, {
|
||||
data: rasterData,
|
||||
width,
|
||||
height,
|
||||
min: defaultMIN,
|
||||
max: defaultMAX,
|
||||
});
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
|
||||
import { IRasterData } from '../../interface';
|
||||
|
||||
/** 数学运算 根据计算表达式进行数学运算
|
||||
* * * Math operators:
|
||||
* `['*', value1, value2]` multiplies `value1` by `value2`
|
||||
* `['/', value1, value2]` divides `value1` by `value2`
|
||||
* `['+', value1, value2]` adds `value1` and `value2`
|
||||
* `['-', value1, value2]` subtracts `value2` from `value1`
|
||||
* `['%', value1, value2]` returns the result of `value1 % value2` (modulo)
|
||||
* `['^', value1, value2]` returns the value of `value1` raised to the `value2` power
|
||||
* `['abs', value1]` returns the absolute value of `value1`
|
||||
* `['floor', value1]` returns the nearest integer less than or equal to `value1`
|
||||
* `['round', value1]` returns the nearest integer to `value1`
|
||||
* `['ceil', value1]` returns the nearest integer greater than or equal to `value1`
|
||||
* `['sin', value1]` returns the sine of `value1`
|
||||
* `['cos', value1]` returns the cosine of `value1`
|
||||
* `['atan', value1, value2]` returns `atan2(value1, value2)`. If `value2` is not provided, returns `atan(value1)`
|
||||
*/
|
||||
export function mathematical(symbol: string, n1: number, n2: number) {
|
||||
switch(symbol) {
|
||||
case '+': return n1 + n2;
|
||||
case '-': return n1 - n2;
|
||||
case '*': return n1 * n2;
|
||||
case '/': return n1 / n2;
|
||||
case '%': return n1 % n2;
|
||||
|
||||
case '^': return Math.pow(n1, n2);
|
||||
case 'abs': return Math.abs(n1);
|
||||
case 'floor': return Math.floor(n1);
|
||||
case 'round': return Math.round(n1);
|
||||
case 'ceil': return Math.ceil(n1);
|
||||
case 'sin': return Math.sin(n1);
|
||||
case 'cos': return Math.cos(n1);
|
||||
case 'atan': return (n2 === -1) ? Math.atan(n1): Math.atan2(n1, n2);
|
||||
|
||||
default:
|
||||
console.warn('Calculate symbol err! Return default 0');
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据表达式计算
|
||||
* @param express
|
||||
* @param bandsData
|
||||
*/
|
||||
export function calculate(express: any[], bandsData: IRasterData[]) {
|
||||
const {width, height} = bandsData[0];
|
||||
const dataArray = bandsData.map(band => band.rasterData) as Uint8Array[];
|
||||
const length = width * height;
|
||||
const rasterData = [];
|
||||
const originExp = JSON.stringify(express);
|
||||
for(let i = 0;i < length; i++) {
|
||||
const exp = JSON.parse(originExp);
|
||||
// 将表达式中的 ['band', 0]、['band', 1] 等替换为实际的栅格数据
|
||||
const expResult = spellExpress(exp, dataArray, i);
|
||||
if(typeof expResult === 'number') {
|
||||
// exp: ['band', 0] => exp: 2 ...
|
||||
// exp 直接指定了波段值,替换完后直接就是数值了,无需计算
|
||||
rasterData.push(expResult);
|
||||
} else {
|
||||
const result = calculateExpress(exp);
|
||||
rasterData.push(result);
|
||||
}
|
||||
|
||||
}
|
||||
return rasterData as unknown as Uint8Array;
|
||||
}
|
||||
|
||||
type IExpress = any[];
|
||||
|
||||
/**
|
||||
* 将表达式中的指定波段替换为对应波段的栅格数据
|
||||
* @param express
|
||||
* @param dataArray
|
||||
* @param index
|
||||
*/
|
||||
export function spellExpress(express: IExpress, dataArray: Uint8Array[], index: number) {
|
||||
/**
|
||||
* 用户直接指定波段数值,无需计算
|
||||
*/
|
||||
if(express.length === 2 && express[0] === 'band' && typeof express[1] === 'number') {
|
||||
try {
|
||||
return dataArray[express[1]][index];
|
||||
} catch(err) {
|
||||
console.warn('Raster Data err!');
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
express.map((e, i) => {
|
||||
if(Array.isArray(e) && e.length > 0) {
|
||||
switch(e[0]) {
|
||||
case 'band':
|
||||
try {
|
||||
express[i] = dataArray[e[1]][index];
|
||||
} catch(err) {
|
||||
console.warn('Raster Data err!');
|
||||
express[i] = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
spellExpress(e, dataArray, index);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function formatExpress(express: IExpress) {
|
||||
const [symbol1, symbol2 = -1, symbol3 = -1] = express;
|
||||
if(symbol1 === undefined) {
|
||||
console.warn('Express err!')
|
||||
return ['+', 0, 0];
|
||||
}
|
||||
const symbol = symbol1.replace(/\s+/g, '');
|
||||
|
||||
return [symbol, symbol2, symbol3];
|
||||
}
|
||||
|
||||
export function calculateExpress(express: IExpress) {
|
||||
const formatExp = formatExpress(express);
|
||||
const str = formatExp[0];
|
||||
let left = formatExp[1];
|
||||
let right = formatExp[2];
|
||||
|
||||
if(Array.isArray(left)) {
|
||||
left = calculateExpress(express[1]);
|
||||
}
|
||||
if(Array.isArray(right)) {
|
||||
right = calculateExpress(express[2]);
|
||||
}
|
||||
return mathematical(str, left, right);
|
||||
}
|
|
@ -1,151 +0,0 @@
|
|||
import {
|
||||
getImage,
|
||||
makeXMLHttpRequestPromise,
|
||||
ResponseCallback,
|
||||
IRasterParser,
|
||||
arrayBufferToTiffImage,
|
||||
RequestParameters,
|
||||
getArrayBuffer,
|
||||
getURLFromTemplate,
|
||||
Tile,
|
||||
TileLoadParams,
|
||||
} from '@antv/l7-utils';
|
||||
|
||||
/**
|
||||
* 用于获取 raster data 的瓦片,如 tiff、lerc、dem 等
|
||||
* 支持多文件模式
|
||||
* @param url
|
||||
* @param tileParams
|
||||
* @param tile
|
||||
* @param rasterParser
|
||||
* @returns
|
||||
*/
|
||||
export const getTileBuffer = async (
|
||||
url: string | string[],
|
||||
tileParams: TileLoadParams,
|
||||
tile: Tile,
|
||||
rasterParser: (imageData: ArrayBuffer) => Promise<IRasterParser>,
|
||||
): Promise<HTMLImageElement | ImageBitmap> => {
|
||||
const requestParameters = {
|
||||
url: getTileUrl(url, tileParams),
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
getTiffImage(
|
||||
tile,
|
||||
requestParameters,
|
||||
(err, img) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else if (img) {
|
||||
resolve(img);
|
||||
}
|
||||
},
|
||||
rasterParser,
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const getTiffImage = async (
|
||||
tile: Tile,
|
||||
requestParameters: RequestParameters,
|
||||
callback: ResponseCallback<HTMLImageElement | ImageBitmap | null>,
|
||||
rasterParser: any,
|
||||
) => {
|
||||
if (Array.isArray(requestParameters.url)) {
|
||||
const imageDataList = [];
|
||||
const xhrList: any[] = [];
|
||||
const errList = [];
|
||||
const urls = requestParameters.url;
|
||||
|
||||
for (let i = 0; i < urls.length; i++) {
|
||||
const params = {
|
||||
...requestParameters,
|
||||
url: urls[i],
|
||||
};
|
||||
const { err, data, xhr } = await makeXMLHttpRequestPromise({
|
||||
...params,
|
||||
type: 'arrayBuffer',
|
||||
});
|
||||
if (err) {
|
||||
errList.push(err);
|
||||
}
|
||||
xhrList.push(xhr);
|
||||
imageDataList.push(data);
|
||||
}
|
||||
setTileXHRCancelFunc(tile, xhrList);
|
||||
|
||||
if (errList.length > 0) {
|
||||
callback(errList as Error[], null);
|
||||
return;
|
||||
}
|
||||
|
||||
const { rasterData, width, height } = await rasterParser(imageDataList);
|
||||
const defaultMIN = 0;
|
||||
const defaultMAX = 8000;
|
||||
callback(null, {
|
||||
// @ts-ignore
|
||||
data: rasterData,
|
||||
width,
|
||||
height,
|
||||
min: defaultMIN,
|
||||
max: defaultMAX,
|
||||
});
|
||||
} else {
|
||||
const xhr = getArrayBuffer(requestParameters, (err, imgData) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
} else if (imgData) {
|
||||
arrayBufferToTiffImage(imgData, callback, rasterParser);
|
||||
}
|
||||
});
|
||||
setTileXHRCancelFunc(tile, [xhr]);
|
||||
}
|
||||
};
|
||||
|
||||
function setTileXHRCancelFunc(tile: Tile, xhrList: any[]) {
|
||||
tile.xhrCancel = () => {
|
||||
xhrList.map((xhr) => {
|
||||
xhr.abort();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function getTileUrl(url: string | string[], tileParams: TileLoadParams) {
|
||||
if (Array.isArray(url)) {
|
||||
return url.map((src) => getURLFromTemplate(src, tileParams));
|
||||
} else {
|
||||
return getURLFromTemplate(url, tileParams);
|
||||
}
|
||||
}
|
||||
|
||||
export const getTileImage = async (
|
||||
url: string | string[],
|
||||
tileParams: TileLoadParams,
|
||||
tile: Tile,
|
||||
): Promise<HTMLImageElement | ImageBitmap> => {
|
||||
// TODO: 后续考虑支持加载多服务
|
||||
const imgUrl = getURLFromTemplate(
|
||||
Array.isArray(url) ? url[0] : url,
|
||||
tileParams,
|
||||
);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const xhr = getImage({ url: imgUrl }, (err, img) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else if (img) {
|
||||
resolve(img);
|
||||
}
|
||||
});
|
||||
tile.xhrCancel = () => xhr.abort();
|
||||
});
|
||||
};
|
||||
|
||||
export const defaultFormat = () => {
|
||||
return {
|
||||
rasterData: new Uint8Array([0]),
|
||||
width: 1,
|
||||
height: 1,
|
||||
};
|
||||
};
|
|
@ -0,0 +1,82 @@
|
|||
import { IRasterFormat, IBandsOperation, IRasterFileData } from '../../interface';
|
||||
import { getTileBandParams, bindCancel } from './request';
|
||||
import {
|
||||
makeXMLHttpRequestPromise,
|
||||
ResponseCallback,
|
||||
ITileBand,
|
||||
RequestParameters,
|
||||
getArrayBuffer,
|
||||
Tile,
|
||||
} from '@antv/l7-utils';
|
||||
import { handleRasterFiles } from '../bandOperation/bands';
|
||||
|
||||
export const getRasterFile = async (
|
||||
tile: Tile,
|
||||
requestParameters: RequestParameters,
|
||||
callback: ResponseCallback<HTMLImageElement | ImageBitmap | null>,
|
||||
rasterFormat: IRasterFormat,
|
||||
operation?: IBandsOperation,
|
||||
) => {
|
||||
// Tip: 至少存在一个请求文件的 url,处理得到标准的 ITileBand[] url 路径和 bands 参数
|
||||
const tileBandParams: ITileBand[] = getTileBandParams(requestParameters.url);
|
||||
|
||||
if(tileBandParams.length > 1) {// 同时请求多文件
|
||||
const { rasterFiles, xhrList, errList } = await getMultiArrayBuffer(tileBandParams, requestParameters);
|
||||
bindCancel(tile, xhrList);
|
||||
if (errList.length > 0) {
|
||||
callback(errList as Error[], null);
|
||||
return;
|
||||
}
|
||||
|
||||
handleRasterFiles(rasterFiles, rasterFormat, operation, callback);
|
||||
} else {
|
||||
const xhr = getArrayBuffer(requestParameters, (err, imgData) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
} else if (imgData) {
|
||||
const rasterFiles = [{
|
||||
data: imgData,
|
||||
bands: tileBandParams[0].bands
|
||||
}];
|
||||
handleRasterFiles(rasterFiles, rasterFormat, operation, callback);
|
||||
}
|
||||
});
|
||||
bindCancel(tile, [xhr]);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* get multi raster files async
|
||||
* @param tileBandParams
|
||||
* @param requestParameters
|
||||
* @returns
|
||||
*/
|
||||
async function getMultiArrayBuffer(tileBandParams: ITileBand[], requestParameters: RequestParameters) {
|
||||
const rasterFiles: IRasterFileData[] = [];
|
||||
const xhrList: any[] = [];
|
||||
const errList = [];
|
||||
|
||||
for (let i = 0; i < tileBandParams.length; i++) {
|
||||
const tileBandParam = tileBandParams[i]
|
||||
const params = {
|
||||
...requestParameters,
|
||||
url: tileBandParam.url,
|
||||
};
|
||||
|
||||
const bands = tileBandParam.bands;
|
||||
const { err, data, xhr } = await makeXMLHttpRequestPromise({
|
||||
...params,
|
||||
type: 'arrayBuffer',
|
||||
});
|
||||
if (err) {
|
||||
errList.push(err);
|
||||
}
|
||||
xhrList.push(xhr);
|
||||
rasterFiles.push({
|
||||
data,
|
||||
bands,
|
||||
});
|
||||
}
|
||||
|
||||
return { rasterFiles, xhrList, errList }
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
import {
|
||||
getImage,
|
||||
ITileBand,
|
||||
getURLFromTemplate,
|
||||
Tile,
|
||||
TileLoadParams,
|
||||
} from '@antv/l7-utils';
|
||||
import { getTileUrl } from './request';
|
||||
import { IRasterFormat, IBandsOperation } from '../../interface';
|
||||
import { getRasterFile } from './getRasterData';
|
||||
|
||||
/**
|
||||
* 用于获取 raster data 的瓦片,如 tiff、lerc、dem 等
|
||||
* 支持多文件模式
|
||||
* @param url
|
||||
* @param tileParams
|
||||
* @param tile
|
||||
* @param rasterFormat
|
||||
* @returns
|
||||
*/
|
||||
export const getTileBuffer = async (
|
||||
url: string | string[] | ITileBand[],
|
||||
tileParams: TileLoadParams,
|
||||
tile: Tile,
|
||||
rasterFormat: IRasterFormat,
|
||||
operation?: IBandsOperation,
|
||||
): Promise<HTMLImageElement | ImageBitmap> => {
|
||||
const requestParameters = {
|
||||
// getTileUrl 将原始的 url 路径进行转化(多服务器)
|
||||
url: getTileUrl(url, tileParams),
|
||||
};
|
||||
return new Promise((resolve, reject) => {
|
||||
getRasterFile(
|
||||
tile,
|
||||
requestParameters,
|
||||
(err, img) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else if (img) {
|
||||
resolve(img);
|
||||
}
|
||||
},
|
||||
rasterFormat,
|
||||
operation,
|
||||
);
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 获取图片格式的文件 jpg、png 等
|
||||
* @param url
|
||||
* @param tileParams
|
||||
* @param tile
|
||||
* @returns
|
||||
*/
|
||||
export const getTileImage = async (
|
||||
url: string | string[],
|
||||
tileParams: TileLoadParams,
|
||||
tile: Tile,
|
||||
): Promise<HTMLImageElement | ImageBitmap> => {
|
||||
// TODO: 后续考虑支持加载多服务
|
||||
const imgUrl = getURLFromTemplate(
|
||||
Array.isArray(url) ? url[0] : url,
|
||||
tileParams,
|
||||
);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const xhr = getImage({ url: imgUrl }, (err, img) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else if (img) {
|
||||
resolve(img);
|
||||
}
|
||||
});
|
||||
tile.xhrCancel = () => xhr.abort();
|
||||
});
|
||||
};
|
||||
|
||||
export const defaultFormat = () => {
|
||||
return {
|
||||
rasterData: new Uint8Array([0]),
|
||||
width: 1,
|
||||
height: 1,
|
||||
};
|
||||
};
|
|
@ -0,0 +1,98 @@
|
|||
import {
|
||||
ITileBand,
|
||||
getURLFromTemplate,
|
||||
TileLoadParams,
|
||||
Tile
|
||||
} from '@antv/l7-utils';
|
||||
|
||||
/**
|
||||
* 根据不同的输入,将对应的 url 路径进行转化
|
||||
* https://{a-c}.xxx.ccc => https://a.xxx.ccc
|
||||
* https://{a-c}.xxx.ccc => https://b.xxx.ccc
|
||||
* https://{a-c}.xxx.ccc => https://c.xxx.ccc
|
||||
* @param url
|
||||
* 'https://a.xxx.ccc '
|
||||
* or
|
||||
* ['https://a.xxx.ccc', 'https://c.ddd.ccc']
|
||||
* or
|
||||
* [
|
||||
* {
|
||||
* url: 'https://a.xxx.ccc',
|
||||
* bands: [0]
|
||||
* },
|
||||
* ...
|
||||
* ]
|
||||
* @param tileParams
|
||||
* @returns
|
||||
*/
|
||||
export function getTileUrl(
|
||||
url: string | string[] | ITileBand[],
|
||||
tileParams: TileLoadParams,
|
||||
) {
|
||||
if (Array.isArray(url)) {
|
||||
if(typeof url[0] === 'string') {
|
||||
return (url as string[]).map((src) =>
|
||||
getURLFromTemplate(src, tileParams),
|
||||
);
|
||||
} else {
|
||||
return (url as ITileBand[]).map((o) => {
|
||||
return {
|
||||
url: getURLFromTemplate(o.url, tileParams),
|
||||
bands: o.bands || [0],
|
||||
};
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return getURLFromTemplate(url, tileParams);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理 url 中的波段参数,将不同的 url 格式处理成统一格式
|
||||
* @param urlBandParam
|
||||
* 'https://a.bb.xxx'
|
||||
* or
|
||||
* [
|
||||
* 'https://a.bb.xxx',
|
||||
* 'https://a.bb.xxx'
|
||||
* ]
|
||||
* or
|
||||
* [
|
||||
* {
|
||||
* url: 'https://a.bb.xxx',
|
||||
* bands: [0, 1]
|
||||
* },
|
||||
* ...
|
||||
* ]
|
||||
* @returns
|
||||
*/
|
||||
export function getTileBandParams(urlBandParam: string | string[] | ITileBand[]): ITileBand[] {
|
||||
if(typeof urlBandParam === 'string') {
|
||||
return [{
|
||||
url: urlBandParam,
|
||||
bands: [0]
|
||||
}]
|
||||
} else if(typeof urlBandParam[0] === 'string') {
|
||||
return urlBandParam.map(param => {
|
||||
return {
|
||||
url: param as string,
|
||||
bands: [0]
|
||||
};
|
||||
})
|
||||
} else {
|
||||
return urlBandParam as ITileBand[];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 tile 文件请求的取消函数
|
||||
* @param tile
|
||||
* @param xhrList
|
||||
*/
|
||||
export function bindCancel(tile: Tile, xhrList: any[]) {
|
||||
tile.xhrCancel = () => {
|
||||
xhrList.map((xhr) => {
|
||||
xhr.abort();
|
||||
});
|
||||
};
|
||||
}
|
|
@ -1,8 +1,45 @@
|
|||
import { IRasterLayerData, IRasterFileData } from '../interface';
|
||||
|
||||
interface IDataItem {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export function getColumn(data: IDataItem[], columnName: string) {
|
||||
return data.map((item: IDataItem) => {
|
||||
return item[columnName] * 1;
|
||||
});
|
||||
}
|
||||
|
||||
export function isRasterFileData(data?: IRasterLayerData) {
|
||||
if (data === undefined) return false;
|
||||
if (!Array.isArray(data) && data.data !== undefined) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function isRasterFileDataArray(data: IRasterLayerData) {
|
||||
if (Array.isArray(data)) {
|
||||
if (data.length === 0) return false;
|
||||
if (isRasterFileData(data[0] as IRasterFileData)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function isNumberArray(data: IRasterLayerData) {
|
||||
if (Array.isArray(data)) {
|
||||
if (data.length === 0) return true;
|
||||
if (typeof data[0] === 'number') {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
import { getReferrer } from './env';
|
||||
import { $window, $XMLHttpRequest } from './mini-adapter';
|
||||
|
||||
export interface ITileBand {
|
||||
url: string;
|
||||
bands: number[];
|
||||
}
|
||||
|
||||
export type RequestParameters = {
|
||||
url: string | string[];
|
||||
url: string | string[] | ITileBand[];
|
||||
headers?: any;
|
||||
method?: 'GET' | 'POST' | 'PUT';
|
||||
body?: string;
|
||||
|
@ -55,7 +60,7 @@ function makeFetchRequest(
|
|||
callback: ResponseCallback<any>,
|
||||
) {
|
||||
const url = Array.isArray(requestParameters.url) ? requestParameters.url[0] : requestParameters.url;
|
||||
const request = new Request(url, {
|
||||
const request = new Request(url as string, {
|
||||
method: requestParameters.method || 'GET',
|
||||
body: requestParameters.body,
|
||||
credentials: requestParameters.credentials,
|
||||
|
@ -96,7 +101,7 @@ function makeFetchRequest(
|
|||
new AJAXError(
|
||||
response.status,
|
||||
response.statusText,
|
||||
url,
|
||||
url.toString(),
|
||||
body,
|
||||
),
|
||||
),
|
||||
|
@ -157,7 +162,7 @@ function makeXMLHttpRequest(
|
|||
type: xhr.getResponseHeader('Content-Type'),
|
||||
});
|
||||
callback(
|
||||
new AJAXError(xhr.status, xhr.statusText, url, body),
|
||||
new AJAXError(xhr.status, xhr.statusText, url.toString(), body),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
@ -305,44 +310,3 @@ export const getImage = (
|
|||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const arrayBufferToTiffImage = async (
|
||||
data: ArrayBuffer,
|
||||
callback: (err?: Error | null, image?: any) => void,
|
||||
rasterParser: any,
|
||||
) => {
|
||||
try {
|
||||
const { rasterData, width, height } = await rasterParser(data);
|
||||
const defaultMIN = 0;
|
||||
const defaultMAX = 8000;
|
||||
callback(null, {
|
||||
data: rasterData,
|
||||
width,
|
||||
height,
|
||||
min: defaultMIN,
|
||||
max: defaultMAX,
|
||||
});
|
||||
} catch (err) {
|
||||
callback(null, new Error('' + err));
|
||||
}
|
||||
};
|
||||
|
||||
export const getTiffImage = (
|
||||
requestParameters: RequestParameters,
|
||||
callback: ResponseCallback<HTMLImageElement | ImageBitmap | null>,
|
||||
rasterParser: any,
|
||||
) => {
|
||||
return getArrayBuffer(requestParameters, (err, imgData) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
} else if (imgData) {
|
||||
arrayBufferToTiffImage(imgData, callback, rasterParser);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export interface IRasterParser {
|
||||
rasterData: HTMLImageElement | ImageBitmap | null | undefined;
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
Loading…
Reference in New Issue