Fix raster color (#1511)

* feat: 调试栅格瓦片图层颜色渲染失效问题

* feat: 添加自定义数据获取

Co-authored-by: Dreammy23 <echo.cmy@antgroup.com>
This commit is contained in:
@thinkinggis 2022-11-28 11:42:17 +08:00 committed by GitHub
parent ee3cc51d3f
commit f39b6f3653
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 291 additions and 23 deletions

View File

@ -1,6 +1,11 @@
import { Scene, RasterLayer } from '@antv/l7';
import { GaodeMap } from '@antv/l7-maps';
import React, { useEffect } from 'react';
import { encodeParams, sign } from './signGenerator';
const canvas = document.createElement('canvas');
canvas.width = 256;
canvas.height = 256;
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
export default () => {
useEffect(() => {
@ -8,42 +13,139 @@ export default () => {
id: 'map',
pickBufferScale: 1.0,
map: new GaodeMap({
center: [121.268, 30.3628],
center: [127.471855, 46.509622], // 绥化市-北林区
pitch: 0,
style: 'blank',
zoom: 10,
}),
});
const layerTile = new RasterLayer({
zIndex: 1,
});
layerTile.source(
'//t{0-4}.tianditu.gov.cn/cva_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cva&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=f1f2021a42d110057042177cd22d856f',
{
parser: {
type: 'rasterTile',
tileSize: 256,
const Base64toArrayBuffer = (base64Data) => {
const padding = '='.repeat((4 - (base64Data.length % 4)) % 4);
const base64 = (base64Data + padding).replace(/-/g, '+').replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
const getUrlQueryParams = (url = location.search): any => {
const params = {};
const keys = url.match(/([^?&]+)(?==)/g);
const values = url.match(/(?<==)([^&]*)/g);
for (const index in keys) {
// @ts-ignore
params[keys[index]] = values?.[index];
}
return params;
};
// const layerTile = new RasterLayer({
// zIndex: 1,
// });
// layerTile.source(
// '//t{0-4}.tianditu.gov.cn/cva_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cva&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=f1f2021a42d110057042177cd22d856f',
// {
// parser: {
// type: 'rasterTile',
// tileSize: 256,
},
},
);
// },
// },
// );
const layerTile2 = new RasterLayer({
zIndex: 0,
});
layerTile2.source(
'http://webst0{1-4}.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}',
}).source(
// 'http://webst0{1-4}.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}',
`https://openapi.alipay.com/gateway.do?x={x}&y={y}&z={z}&index=1001&crow_type=101`,
{
parser: {
type: 'rasterTile',
dataType:'customArrayBuffer',
tileSize: 256,
},
getCustomData: ({x,y,z},callback)=>{
const biz_content = { x, y, z, index:1001, crow_type:101 };
const newUrl = sign('anttech.ai.cv.rs.xytile.get', biz_content, {
charset: 'utf-8',
version: '1.0',
});
const signStr = Object.keys(encodeParams(newUrl))
.sort()
.map((key) => {
let data = encodeParams(newUrl)[key];
if (Array.prototype.toString.call(data) !== '[object String]') {
data = JSON.stringify(data);
}
return `${key}=${data}`;
})
.join('&');
const url = `https://openapi.alipay.com/gateway.do?${signStr}`;
fetch(url).then(res=>res.json()).then(response=>{
const bufferData = Base64toArrayBuffer(response?.anttech_ai_cv_rs_xytile_get_response?.image || '');
callback(null, bufferData);
})
},
format: async (data: any) => {
// 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: {
// type:'rgb',
// options:{
// RMinMax:[0,255],
// GMinMax:[0,255],
// BMinMax:[0,255],
// }
// },
}
},
);
layerTile2.style({
opacity: 1,
clampLow: false,
clampHigh: false,
domain: [0, 150],
rampColors: {
colors: ['rgba(0, 0, 0, 0)','rgba(0, 0, 0, 0)','#EAC300','#EAC300' ],
positions: [0,0.2,0.6,1],
},
});
scene.on('loaded', () => {
scene.addLayer(layerTile);
// scene.addLayer(layerTile);
scene.addLayer(layerTile2);
});
}, []);

View File

@ -0,0 +1,114 @@
import moment from 'moment';
import JSEncrypt from 'jsencrypt';
import CryptoJS from 'crypto-js';
// const key =
// 遥感地图网页应用 2021003154649392 私钥
// 'MIIEpQIBAAKCAQEAmGPwXSi1MjuIvEh0Gl75d62tTQF/vzMkFDuLjx5RJ/DiSntXbmVV3ZJjICNOKLaWCemHvBerJfXtDQUAJWVo8FwJJQrla3oGHQp38vUNtRLGQQkozzWeSslaXZc/cEDLBfjogzk4jojEXuFpgbzY0BJZg+KHh6k7TK/Tf6Zrn19fxVojeAx8njguIjlDbZAtnaNYlq3fYIVzPX3nd/xTQmLwHKjeuJ626i+zdo57oNKHOOStX/0Isf6y4rj8cdPrmxfWDw86vV1Pd378sKL0vejOrS6TLoYG8sMGk8Fi//ecXnY1068vYl2tQOIqHiZb8XGCFIYoNWefvHPsufqkQwIDAQABAoIBAQCAnfDTn7T2tZqdVwVu8HPbF6Gp2bcJF8++p9cNLwhODpffRySuzyZegNva1xFzeU8/BHQEQ3G2805ka7EI3wNnwjYRppWlVXLtddVxIHS2PCdJ4sInyNhVxIXZcfsf8f+ohcEwZ84dNr+OWO+kiU/wpVwZ3MqIrEOZYNi/5yT0d8jdNNMsDGeGwGzULN/eIqgiF9l3vbn4w79WoUTOll6yxC/8ZYa9TX8yFWGKjElmr6K/c2x2NuRpToQgYv5iqN6m1biUNmkoqQ3cbysGAKP2Vb7S9GEjI6Kcp3TwfyrE65s7pS0aU6lNfuqzjNuulRDnxAXP2+fHSOcZBFQSu7TpAoGBAOPO+9jbezq6eQ9seruN8UMriTiFRU02E+HNihCbM2Vvwbb4wet25dJYL4TqRT+CawQA7XQ85/8QCl0mpUOFDJa6agYSby2Eai2IDZ5KxvdDNXDuQv+R4Wh2TV3xjK4tyrUZhYGZZ8g1MrxuilvR7/gG41pl0X28aDHxzjWgV4JtAoGBAKs/sccl4QbINq2AUB/rWKFv2SNVMTpq13/PtRCeVe49Gin9SpEuYm5XD/0n9hNRDiYul8Hwcq1ff5O+bEi+UJyqyVDjJs82onhCJOY+hWkTcluQCkjKNqKyTciys0gsZorY8/oxzkZ7aBQSkhZ4+ReR8tf2Ti46QTB1ft/55xNvAoGBAKINb6JIH1UbqVqLdJNO2b1Kjwah5zaRrXsHV5uQi/MwmMCsHGZ/4eTLzqHidPhEshPbBQ+W9AHBS14QS6fIUbg3S4yeOHBMratOCUH9N6RVLwLyur5K6+n/nfGiDs0ozfYd/Za/pdAJ59mbWNkQcoAhhkGcBHZJPGWq+WG0egvRAoGBAJnLPTS17w7xNjHBf7P634E0etTBiNGG7HyLpHgFSvUHiPzWzmXuO1YO+HwJrHSSodtmksII2GSd1GxyYrs9uBXLR7tOFSeZUHYiKRNiaNwCK/zRMntGp6Sb70FkQ6B4x6rAxvQWUMN5xvHn760g/cvONkL/oBziEnObwdMSkxUfAoGAQ2zEYHRAas8lichIbcrmfvtBUSfAKIEJO+rXeNoxjEKPHRERlCVnAs/O5OmxbB+oZzyrK16+LGM+3jnDEf0kdT49cqkMK1n8CJR2W9MrbK8jMFZOR0XW7vQ0J0vnc5R6vobf3U4jFq8nVK3ehOUicVIqre6LEiHKVQUqC3y+Vl4=';
// 遥感地图网页应用 2021003154694648 私钥
// 'MIIEpQIBAAKCAQEA1N165ud87194oMhMT4oCsoF+DenzjhRMEo4WxlSbWsyGRExQh7MSZ+aZZS8RWDLSfy3VNpUhpYllDiGg/Ng2q6SE4inU2O5IBghBKWJoznld+0CiUCKFEFukRYtUszedHiN5rlfE4E/PsUF0RiV8VRCPMdsHsfQvwYJ54CAuFKKcD4FLHrBmHb7X7D569gGJYEyf4L5iBxLoFgoiMnUwoY2SZr4ge+kW94RvNuUCWHDDA8o5/Z8SMztotrwRE1utHgqbPSV1wIn1vppQs8tH2qp8m0s5lT11iEqoJ6NPNlglU6s/4p7CV4genTvXOPyGqVKxz1i29m2LebU0khSwzwIDAQABAoIBAQCyEOBp3kXV3HCFV2j2tIWTG93997JFLVeBwhjtKgOXjjXXVlubWMJR5kZ1rUz+Ee2idA3DDjfKSUge7STFvnzlUsfShZnHKnVXjnosC2WPK1nh/2yISogzaeXeQzLEhQZLGvQEUumfl0QvjIie0WbTpfmThB1I0PNOFj6rrV++482DNdOfIGPAjWb+Id32vbQxgzUtFNsSno85l4EFRZQ9hfXsljxRuDeA4VLUWo7ZpOztNVO95emhAWgWQ2S0/YNgVO04UVVfiCQSqa80HpNz4l+4WnCQEDKrOA4kY64h988g7MG1bN9uaV9zQ6u6wh/Ijb4vvIL/ShwaQHvWtE5RAoGBAPJFgPcyv7Eatg6WMM+bW5ihf6Q2x3KUvocAtnhjLb0DNGsewJGknCU0dE5BQN0TdbV3H1mncLaBOl7XFT2Rvgs0KdUzCBi60uyMtFSRnty/UarE5vdUH8pKWX+OniRcMr9nYD1k/CxWrBKfhOwi8rxgaIJkbIjzHWzwKbZoeEvXAoGBAODtZSKmBc83v5fRR4eSr0QPDzjvzEOZc/g5AOzKhJx+2d496t25bULuxWEsHonvZweLNZraM2wV/lM6wLbmEj1FYHvcuU9TvInceqCbKcXSCkZOpqyuUKJGQVHIAswno07cekp2SFB+1OABYSvEWqVeYg3rqzVjGqVrvuBae2PJAoGBAMTzJbVPhzAVk9zl0cZj+KFq4JYBhkAqlXywYqYZkkwut8VBWbDMjbddHhOjznQqZq1fqpe6m9Fx2p7Q4M6NlV4MSNmtw64+6kss00hQnUG9MknOCikUNUfBC2K78OmEHiklg8JFPw9YYkg9b9R7ULM8+JjPxL/MS6aM0owb/3c/AoGBAKXir2okY7h19xmywTxdlGFvcdyeDln0vLDe6a25lqAMdgYQSD2KWei5TFzkOwmjxKqtorU4JCCc/9rGRAcgG2eQ2R3ApfK5YR2Tu+TjSqWYyPcdXpOQY+uqQNZd2qJSwYCR3qc4IREs2Tb0DYRH5kp8F3kIzFYtZyOFGVtBoCrxAoGAdZnOWrUyXx6OJg98+TtV5HMkNIVbdGjjoJvmvxgXnABHKbyRjnP4WxARQiiBga/qh07+Ovo4EkbyWf5oqkogzY+F969A5zeSQCuJ7Gu6HzBdjcAIRzq8hDqBiFSskYBFz/Ohn8JL8Y757JXYgjur6AppOk4KElC0o/ZOXkPjg7k=';
// RSA 签名
const encrypt = new JSEncrypt();
// encrypt.setPrivateKey(key);
const AK = {
appId: '2021003154694648', // 遥感地图网页应用
key:
'MIIEpQIBAAKCAQEA1N165ud87194oMhMT4oCsoF+DenzjhRMEo4WxlSbWsyGRExQh7MSZ+aZZS8RWDLSfy3VNpUhpYllDiGg/Ng2q6SE4inU2O5IBghBKWJoznld+0CiUCKFEFukRYtUszedHiN5rlfE4E/PsUF0RiV8VRCPMdsHsfQvwYJ54CAuFKKcD4FLHrBmHb7X7D569gGJYEyf4L5iBxLoFgoiMnUwoY2SZr4ge+kW94RvNuUCWHDDA8o5/Z8SMztotrwRE1utHgqbPSV1wIn1vppQs8tH2qp8m0s5lT11iEqoJ6NPNlglU6s/4p7CV4genTvXOPyGqVKxz1i29m2LebU0khSwzwIDAQABAoIBAQCyEOBp3kXV3HCFV2j2tIWTG93997JFLVeBwhjtKgOXjjXXVlubWMJR5kZ1rUz+Ee2idA3DDjfKSUge7STFvnzlUsfShZnHKnVXjnosC2WPK1nh/2yISogzaeXeQzLEhQZLGvQEUumfl0QvjIie0WbTpfmThB1I0PNOFj6rrV++482DNdOfIGPAjWb+Id32vbQxgzUtFNsSno85l4EFRZQ9hfXsljxRuDeA4VLUWo7ZpOztNVO95emhAWgWQ2S0/YNgVO04UVVfiCQSqa80HpNz4l+4WnCQEDKrOA4kY64h988g7MG1bN9uaV9zQ6u6wh/Ijb4vvIL/ShwaQHvWtE5RAoGBAPJFgPcyv7Eatg6WMM+bW5ihf6Q2x3KUvocAtnhjLb0DNGsewJGknCU0dE5BQN0TdbV3H1mncLaBOl7XFT2Rvgs0KdUzCBi60uyMtFSRnty/UarE5vdUH8pKWX+OniRcMr9nYD1k/CxWrBKfhOwi8rxgaIJkbIjzHWzwKbZoeEvXAoGBAODtZSKmBc83v5fRR4eSr0QPDzjvzEOZc/g5AOzKhJx+2d496t25bULuxWEsHonvZweLNZraM2wV/lM6wLbmEj1FYHvcuU9TvInceqCbKcXSCkZOpqyuUKJGQVHIAswno07cekp2SFB+1OABYSvEWqVeYg3rqzVjGqVrvuBae2PJAoGBAMTzJbVPhzAVk9zl0cZj+KFq4JYBhkAqlXywYqYZkkwut8VBWbDMjbddHhOjznQqZq1fqpe6m9Fx2p7Q4M6NlV4MSNmtw64+6kss00hQnUG9MknOCikUNUfBC2K78OmEHiklg8JFPw9YYkg9b9R7ULM8+JjPxL/MS6aM0owb/3c/AoGBAKXir2okY7h19xmywTxdlGFvcdyeDln0vLDe6a25lqAMdgYQSD2KWei5TFzkOwmjxKqtorU4JCCc/9rGRAcgG2eQ2R3ApfK5YR2Tu+TjSqWYyPcdXpOQY+uqQNZd2qJSwYCR3qc4IREs2Tb0DYRH5kp8F3kIzFYtZyOFGVtBoCrxAoGAdZnOWrUyXx6OJg98+TtV5HMkNIVbdGjjoJvmvxgXnABHKbyRjnP4WxARQiiBga/qh07+Ovo4EkbyWf5oqkogzY+F969A5zeSQCuJ7Gu6HzBdjcAIRzq8hDqBiFSskYBFz/Ohn8JL8Y757JXYgjur6AppOk4KElC0o/ZOXkPjg7k=',
};
const openAPIConfig: any = {
gateway: 'http://openapi.stable.dl.alipaydev.com/gateway.do',
timeout: 5000,
charset: 'utf-8',
version: '1.0',
appId: '2021003154694648',
camelcase: false,
};
export async function callByOpenAPI(
method: string,
// modelId: string,
// version: string,
// appId: string = '',
bizContent: Object,
) {
const params = sign(method, bizContent, openAPIConfig);
const encodedParams = encodeParams(params);
// console.log('encodedParams :>> ', encodedParams);
return encodedParams;
}
/**
*
* @description https://opendocs.alipay.com/common/02kf5q
* @param {string} method alipay.ebpp.bill.add
* @param {object} bizContent
* @param {object} config sdk
*/
export function sign(method: string, bizContent: any = {}, config: any): any {
const signParams = Object.assign({
method,
appId: config.appId || AK.appId,
charset: config.charset,
version: config.version,
signType: 'RSA2',
timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
// appAuthToken: '202210BB64237396751c42aa9c225985256e2D92',
// ws_service_url: 'mysearchgw-36.gz00b.dev.alipay.net:12200', // 可选,开发环境需要传,预发环境不需要传
});
encrypt.setPrivateKey(config.key || AK.key);
if (bizContent) {
signParams.bizContent = JSON.stringify(toSnakeCases(bizContent));
}
// params key 驼峰转下划线
const deCamelizeParams = toSnakeCases(signParams);
// 排序
const signStr = Object.keys(deCamelizeParams)
.sort()
.map((key) => {
let data = deCamelizeParams[key];
if (Array.prototype.toString.call(data) !== '[object String]') {
data = JSON.stringify(data);
}
return `${key}=${data}`;
})
.join('&');
// console.log('signStr :>> ', signStr);
// 计算签名
const sign = RSAEncrypt(signStr);
return Object.assign(deCamelizeParams, { sign });
}
function RSAEncrypt(signStr: string) {
const start = Date.now();
const encrypted = encrypt.sign(signStr, CryptoJS.SHA256, 'sha256');
// console.log('encrypted :>> ', encrypted, Date.now() - start);
return encrypted;
}
function toSnakeCases(params: any) {
const out: { [key in string]: any } = {};
Object.keys(params).forEach((key) => {
const newKey = key.replace(
/([A-Z])/g,
(match) => '_' + match.toLowerCase(),
);
out[newKey] = params[key];
});
return out;
}
export function encodeParams(params: any) {
const out: { [key in string]: any } = {};
Object.keys(params).forEach((key) => {
out[key] = encodeURIComponent(params[key]);
});
return out;
}

View File

@ -3,6 +3,3 @@ raster 图片瓦片
#### 卫星图
<code src="./raster/satellite.tsx"></code>
#### 图片瓦片 - 颜色映射
<code src="./raster/imageDataMapping.tsx"></code>

View File

@ -154,7 +154,9 @@
"webpack-merge": "^4.1.4",
"wellknown": "^0.5.0",
"worker-loader": "^2.0.0",
"yorkie": "^2.0.0"
"yorkie": "^2.0.0",
"crypto-js": "^4.1.1",
"jsencrypt": "^3.2.1"
},
"scripts": {
"dev": "npm run worker && dumi dev",

View File

@ -8,6 +8,8 @@ export enum RasterTileType {
IMAGE = 'image',
ARRAYBUFFER = 'arraybuffer',
RGB = 'rgb',
CUSTOMRGB = 'customRGB',
CUSTOMARRAYBUFFER = 'customArrayBuffer',
}
export interface IGeojsonvtOptions {
@ -28,6 +30,7 @@ export interface ITileParserCFG {
minZoom?: number;
maxZoom?: number;
zoomOffset?: number;
getCustomData: (tile: { x: number, y: number, z: number },cb:(err:any,data:any)=>void)=>void,
extent?: [number, number, number, number];
requestParameters: Partial<RequestParameters>;
updateStrategy?: 'overlap' | 'replace';

View File

@ -36,8 +36,10 @@ export function getTileFactory(layer: ILayer) {
const { dataType } = layer.getSource().parser;
switch(dataType) {
case 'rgb':
case 'customRGB':
return RasterRGBTile;
case 'arraybuffer':
case 'customArrayBuffer':
return RasterTile
case "terrainRGB" :
return RasterTerrainRGBTile

View File

@ -6,6 +6,7 @@ import {
TilesetManagerOptions,
} from '@antv/l7-utils';
import { IParserData } from '../interface';
import { getCustomData } from '../utils/tile/getCustomData';
import {
defaultFormat,
getTileBuffer,
@ -61,6 +62,15 @@ export default function rasterTile(
cfg?.format || defaultFormat,
cfg?.operation,
);
case RasterTileType.CUSTOMARRAYBUFFER:
case RasterTileType.CUSTOMRGB:
return getCustomData(
tile,
// @ts-ignore
cfg?.getCustomData,
cfg?.format || defaultFormat,
cfg?.operation,
);
default:
return getTileImage(data as string | string[], tileParams, tile, cfg);
}

View File

@ -0,0 +1,38 @@
import { IRasterFormat, IBandsOperation } from '../../interface';
// import { bindCancel } from './request';
import {
SourceTile,
} from '@antv/l7-utils';
import { processRasterData } from '../bandOperation/bands';
export const getCustomData = async (
tile: SourceTile,
getCustomData: (tile: { x: number, y: number, z: number }, cb: (err: any, data: any) => void) => void,
rasterFormat: IRasterFormat,
operation?: IBandsOperation,
) => {
return new Promise((resolve, reject) => {
getCustomData({
x: tile.x,
y: tile.y,
z: tile.z
}, (err, data) => {
if(err){
reject(err)
}
if (data) {
processRasterData([{data,bands:[0]}], rasterFormat, operation, (err: any, img: any) => {
if (err) {
reject(err);
} else if (img) {
resolve(img);
}
},);
}
})
})
}

View File

@ -36,7 +36,7 @@ export const getTileBuffer = async (
getRasterFile(
tile,
requestParameters,
(err, img) => {
(err:any, img:any) => {
if (err) {
reject(err);
} else if (img) {