mirror of https://gitee.com/antv-l7/antv-l7
feat: 栅格rampcolor 支持cat、quantize、custom 着色方式 (#1554)
* fix: circle meter size && remove unuse code file * docs: add demo * feat: 新增attribute diff 校验 * fix: lint error * feat: 增强 rampcolor 类型 * fix: rampcolor domain * docs: rampcolor 优化 * fix: 纬度范围大于85 的情况 * fix: rampcolor quantize * docs: 更新demo
This commit is contained in:
parent
5f489e55c8
commit
ba7c9a0c1d
|
@ -0,0 +1,98 @@
|
|||
import type { ChoroplethLayerProps,IconImageLayerProps } from '@antv/larkmap';
|
||||
import { ChoroplethLayer, LarkMap,LegendRamp,CustomControl,IconImageLayer} from '@antv/larkmap';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
;
|
||||
const layerOptions: Omit<ChoroplethLayerProps, 'source'> = {
|
||||
autoFit: true,
|
||||
fillColor: {
|
||||
field: '达峰进度条',
|
||||
value: [
|
||||
'#fee5d9',
|
||||
'#fc9272',
|
||||
'#fb6a4a',
|
||||
'#de2d26',
|
||||
'#a50f15',
|
||||
],
|
||||
scale: {
|
||||
type: 'quantize',
|
||||
domain: [0, 100],
|
||||
unknown: '#f7f4f9',
|
||||
}
|
||||
},
|
||||
opacity: 1,
|
||||
strokeColor: '#ddd',
|
||||
lineWidth: 1,
|
||||
state: {
|
||||
active: { strokeColor: 'green', lineWidth: 1.5, lineOpacity: 0.8 },
|
||||
select: { strokeColor: 'red', lineWidth: 1.5, lineOpacity: 0.8 },
|
||||
},
|
||||
|
||||
label: {
|
||||
field: 'name',
|
||||
visible: false,
|
||||
style: {
|
||||
textAllowOverlap: true,
|
||||
fill: '#333', fontSize: 10, stroke: '#aaa', strokeWidth: 1 },
|
||||
},
|
||||
};
|
||||
|
||||
export default () => {
|
||||
const [update,setUpdate] = useState<number>()
|
||||
const [options, setOptions] = useState(layerOptions);
|
||||
const [source, setSource] = useState({
|
||||
data: { type: 'FeatureCollection', features: [] },
|
||||
parser: {
|
||||
type: 'json',
|
||||
geometry: 'geometry',
|
||||
}
|
||||
});
|
||||
|
||||
const [labelsource, setLabelsource] = useState({
|
||||
data: { type: 'FeatureCollection', features: [] },
|
||||
parser: {
|
||||
type: 'json',
|
||||
geometry: 'centroid',
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
fetch('https://mdn.alipayobjects.com/afts/file/A*7HqFT7he7KoAAAAAAAAAAAAADrd2AQ/12.20%20%E5%90%84%E7%9C%81%E4%BB%BD%E9%A6%96%E8%BD%AE%E6%84%9F%E6%9F%93%E9%AB%98%E5%B3%B0%E6%9C%9F%E9%A2%84%E6%B5%8B.json')
|
||||
.then((response) => response.json())
|
||||
.then((data: any) => {
|
||||
setSource((prevState) => ({ ...prevState, data }));
|
||||
setLabelsource((prevState) => ({ ...prevState, data }))
|
||||
});
|
||||
setInterval(()=>{
|
||||
setUpdate(Math.random())
|
||||
},2000)
|
||||
}, []);
|
||||
return (
|
||||
<LarkMap mapType="Gaode" style={{ height: '70vh' }}>
|
||||
<ChoroplethLayer {...options} source={source}
|
||||
onDataUpdate={()=>{
|
||||
console.log('onDataUpdate')
|
||||
}} />
|
||||
<CustomControl
|
||||
position="bottomright"
|
||||
className="custom-control-class"
|
||||
style={{ background: '#fff', borderRadius: 4, overflow: 'hidden', padding: 16 }}
|
||||
>
|
||||
<h3>达峰进度</h3>
|
||||
<LegendRamp
|
||||
lableUnit="%"
|
||||
labels={[ 0,20, 40, 60, 80, 100]}
|
||||
colors={[
|
||||
'#fee5d9',
|
||||
'#fc9272',
|
||||
'#fb6a4a',
|
||||
'#de2d26',
|
||||
'#a50f15',
|
||||
]}
|
||||
barWidth={300}
|
||||
/>
|
||||
</CustomControl>
|
||||
|
||||
</LarkMap>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,2 @@
|
|||
### 区域图
|
||||
<code src="./demos/polygon.tsx"></code>
|
File diff suppressed because it is too large
Load Diff
|
@ -1,2 +1,2 @@
|
|||
### polygon
|
||||
### select
|
||||
<code src="./demos/select.tsx"></code>
|
|
@ -54,13 +54,9 @@ export default () => {
|
|||
rampColors: {
|
||||
colors: [
|
||||
'#FF4818',
|
||||
'#F7B74A',
|
||||
'#FFF598',
|
||||
'#91EABC',
|
||||
'#2EA9A1',
|
||||
'#206C7C',
|
||||
],
|
||||
weights: [0.1, 0.1, 0.1, 0.1, 0.1, 0.5],
|
||||
positions: [0., 1.0],
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
### 疫情达峰
|
||||
<code src="./map.tsx"></code>
|
|
@ -0,0 +1,112 @@
|
|||
import { PolygonLayer, LineLayer, PointLayer, Scene, Source } from '@antv/l7';
|
||||
import { GaodeMap } from '@antv/l7-maps';
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
export default () => {
|
||||
useEffect(() => {
|
||||
fetch(
|
||||
'https://mdn.alipayobjects.com/afts/file/A*7HqFT7he7KoAAAAAAAAAAAAADrd2AQ/12.20%20%E5%90%84%E7%9C%81%E4%BB%BD%E9%A6%96%E8%BD%AE%E6%84%9F%E6%9F%93%E9%AB%98%E5%B3%B0%E6%9C%9F%E9%A2%84%E6%B5%8B.json',
|
||||
)
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
map: new GaodeMap({
|
||||
pitch: 0,
|
||||
style: 'dark',
|
||||
center: [112, 37.8],
|
||||
zoom: 3,
|
||||
}),
|
||||
});
|
||||
const chinaSource = new Source(data, {
|
||||
parser: {
|
||||
type: 'json',
|
||||
geometry: 'geometry',
|
||||
},
|
||||
});
|
||||
const layer = new PolygonLayer({
|
||||
autoFit: true,
|
||||
})
|
||||
.source(chinaSource)
|
||||
.scale('达峰进度条', {
|
||||
type: 'quantize',
|
||||
domain: [0, 100],
|
||||
unknown: '#f7f4f9',
|
||||
})
|
||||
.shape('fill')
|
||||
.color('达峰进度条', [
|
||||
'#fee5d9',
|
||||
'#fc9272',
|
||||
'#fb6a4a',
|
||||
'#de2d26',
|
||||
'#a50f15',
|
||||
])
|
||||
.style({
|
||||
opacity: 1,
|
||||
});
|
||||
const linelayer = new LineLayer({})
|
||||
.source(chinaSource)
|
||||
.shape('line')
|
||||
.color('#ddd')
|
||||
.style({
|
||||
opacity: 1,
|
||||
});
|
||||
|
||||
layer.on('inited', () => {
|
||||
console.log(layer.getLegend('color'));
|
||||
});
|
||||
const pointSource = new Source(data, {
|
||||
parser: {
|
||||
type: 'json',
|
||||
geometry: 'center',
|
||||
},
|
||||
});
|
||||
const nameLayer = new PointLayer()
|
||||
.source(pointSource)
|
||||
.size(12)
|
||||
.shape('name', 'text')
|
||||
.color('#525252')
|
||||
.style({
|
||||
textAnchor: 'top', // 文本相对锚点的位置 center|left|right|top|bottom|top-left
|
||||
textOffset: [0, 0], // 文本相对锚点的偏移量 [水平, 垂直]
|
||||
// spacing: 2, // 字符间距
|
||||
// padding: [ 1, 1 ], // 文本包围盒 padding [水平,垂直],影响碰撞检测结果,避免相邻文本靠的太近
|
||||
stroke: '#fff', // 描边颜色
|
||||
strokeWidth: 1, // 描边宽度
|
||||
strokeOpacity: 1.0,
|
||||
});
|
||||
|
||||
const textLayer = new PointLayer()
|
||||
.source(pointSource)
|
||||
.size(14)
|
||||
.shape('达峰进度条', 'text')
|
||||
.color('#e7298a')
|
||||
.style({
|
||||
textAnchor: 'bottom', // 文本相对锚点的位置 center|left|right|top|bottom|top-left
|
||||
textOffset: [0, -20], // 文本相对锚点的偏移量 [水平, 垂直]
|
||||
// spacing: 2, // 字符间距
|
||||
// padding: [ 1, 1 ], // 文本包围盒 padding [水平,垂直],影响碰撞检测结果,避免相邻文本靠的太近
|
||||
stroke: '#fff', // 描边颜色
|
||||
strokeWidth: 2, // 描边宽度
|
||||
strokeOpacity: 1.0,
|
||||
fontWeight: 800,
|
||||
textAllowOverlap: true,
|
||||
});
|
||||
|
||||
scene.addLayer(layer);
|
||||
scene.addLayer(linelayer);
|
||||
scene.addLayer(nameLayer);
|
||||
scene.addLayer(textLayer);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
id="map"
|
||||
style={{
|
||||
height: '100vh',
|
||||
position: 'relative',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -42,7 +42,7 @@ export default () => {
|
|||
},
|
||||
})
|
||||
.style({
|
||||
opacity: 0.8,
|
||||
opacity: 1.0,
|
||||
clampLow: false,
|
||||
clampHigh: false,
|
||||
domain: [100, 8000],
|
||||
|
|
|
@ -59,10 +59,12 @@ export default () => {
|
|||
.style({
|
||||
clampLow: false,
|
||||
clampHigh: false,
|
||||
domain: [ 0, 90 ],
|
||||
domain: [ 1, 90 ],
|
||||
nodataValue: 0,
|
||||
rampColors: {
|
||||
colors: [ 'rgba(92,58,16,0)', 'rgba(92,58,16,0)', '#fabd08', '#f1e93f', '#f1ff8f', '#fcfff7' ],
|
||||
type:'quantize',
|
||||
colors:['#1b9e77','#d95f02','#7570b3','#e7298a','#66a61e','#e6ab02'],
|
||||
// colors: [ 'rgba(92,58,16,0)', 'rgba(92,58,16,0)', '#fabd08', '#f1e93f', '#f1ff8f', '#fcfff7' ],
|
||||
positions: [ 0, 0.05, 0.1, 0.25, 0.5, 1.0 ]
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// @ts-ignore
|
||||
import { RasterLayer, Scene } from '@antv/l7';
|
||||
// @ts-ignore
|
||||
import { GaodeMap } from '@antv/l7-maps';
|
||||
import { GaodeMap,Map } from '@antv/l7-maps';
|
||||
import React, { useEffect } from 'react';
|
||||
import * as GeoTIFF from 'geotiff';
|
||||
|
||||
|
@ -17,7 +17,7 @@ export default () => {
|
|||
useEffect(() => {
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
map: new GaodeMap({
|
||||
map: new Map({
|
||||
center: [121.268, 30.3628],
|
||||
zoom: 3,
|
||||
}),
|
||||
|
@ -42,20 +42,14 @@ export default () => {
|
|||
},
|
||||
})
|
||||
.style({
|
||||
opacity: 0.8,
|
||||
opacity: 1,
|
||||
clampLow: false,
|
||||
clampHigh: false,
|
||||
domain: [100, 8000],
|
||||
domain: [0, 10000],
|
||||
rampColors: {
|
||||
colors: [
|
||||
'#FF4818',
|
||||
'#F7B74A',
|
||||
'#FFF598',
|
||||
'#91EABC',
|
||||
'#2EA9A1',
|
||||
'#206C7C',
|
||||
].reverse(),
|
||||
positions: [0, 0.2, 0.4, 0.6, 0.8, 1.0],
|
||||
type:'custom',
|
||||
colors: ['#b2182b','#d6604d','#f4a582','#fddbc7','#f7f7f7','#d1e5f0','#92c5de','#4393c3','#2166ac'],
|
||||
positions: [0, 50, 200, 500, 2000, 3000, 4000, 5000, 8000,10000],
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -2,59 +2,36 @@ import { RasterLayer, Scene, Source } from '@antv/l7';
|
|||
import { Map } from '@antv/l7-maps';
|
||||
import React, { useEffect } from 'react';
|
||||
import * as GeoTIFF from 'geotiff';
|
||||
|
||||
// https://gee-community-catalog.org/projects/esrilc2020/
|
||||
const colorList = [
|
||||
'#419bdf', // Water
|
||||
'#419bdf',
|
||||
|
||||
'#397d49', // Tree
|
||||
'#397d49',
|
||||
'#358221', // Tree
|
||||
|
||||
'#88b053', // Grass
|
||||
'#88b053',
|
||||
|
||||
|
||||
'#7a87c6', // vegetation
|
||||
'#7a87c6',
|
||||
|
||||
|
||||
'#e49635', // Crops
|
||||
'#e49635',
|
||||
|
||||
|
||||
'#dfc35a', // shrub
|
||||
'#dfc35a',
|
||||
|
||||
'#c4281b', // Built Area
|
||||
'#c4281b',
|
||||
|
||||
'#a59b8f', // Bare ground
|
||||
'#a59b8f',
|
||||
'#ED022A', // Built Area
|
||||
|
||||
'#a8ebff', // Snow
|
||||
'#a8ebff',
|
||||
|
||||
'#616161', // Clouds
|
||||
'#616161',
|
||||
'#EDE9E4', // Bare ground
|
||||
|
||||
|
||||
'#F2FAFF', // Snow
|
||||
|
||||
'#C8C8C8', // Clouds
|
||||
];
|
||||
const positions = [
|
||||
0.0,
|
||||
0.1,
|
||||
0.1,
|
||||
0.2,
|
||||
0.2,
|
||||
0.3,
|
||||
0.3,
|
||||
0.4,
|
||||
0.4,
|
||||
0.5,
|
||||
0.5,
|
||||
0.6,
|
||||
0.6,
|
||||
0.7,
|
||||
0.7,
|
||||
0.8,
|
||||
0.8,
|
||||
0.9,
|
||||
0.9,
|
||||
1.0,
|
||||
1,2,3,4,5,6,7,8,9,10,11,
|
||||
];
|
||||
export default () => {
|
||||
useEffect(() => {
|
||||
|
@ -93,6 +70,7 @@ export default () => {
|
|||
const width = image.getWidth();
|
||||
const height = image.getHeight();
|
||||
const values = await image.readRasters();
|
||||
console.log(values)
|
||||
return { rasterData: values[0], width, height };
|
||||
},
|
||||
},
|
||||
|
@ -100,13 +78,13 @@ export default () => {
|
|||
);
|
||||
|
||||
layer.source(tileSource).style({
|
||||
domain: [0.001, 11.001],
|
||||
// domain: [0, 255],
|
||||
clampLow: false,
|
||||
rampColors: {
|
||||
type:"cat",
|
||||
colors: colorList,
|
||||
positions,
|
||||
// colors: ['#f00', '#f00'],
|
||||
// positions: [0, 1]
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { IColorRamp } from '@antv/l7-utils';
|
||||
import { ITexture2D } from '../renderer/ITexture2D';
|
||||
export interface ITextureService {
|
||||
setColorTexture(texture: ITexture2D,colorRamp: IColorRamp):void;
|
||||
getColorTexture(colorRamp: IColorRamp): ITexture2D
|
||||
setColorTexture(texture: ITexture2D,colorRamp: IColorRamp,domain?:[number,number]):void;
|
||||
getColorTexture(colorRamp: IColorRamp, domain?:[number,number]): ITexture2D
|
||||
destroy():void;
|
||||
|
||||
}
|
|
@ -406,7 +406,7 @@ export interface ILayer {
|
|||
field: StyleAttributeField,
|
||||
values?: StyleAttributeOption,
|
||||
updateOptions?: Partial<IStyleAttributeUpdateOptions>,
|
||||
): void;
|
||||
): boolean;
|
||||
setLayerPickService(layerPickService:ILayerPickService):void;
|
||||
init(): Promise<void>;
|
||||
scale(field: string | number | IScaleOptions, cfg?: IScale): ILayer;
|
||||
|
|
|
@ -94,6 +94,7 @@ export interface ITexture2DInitializationOptions {
|
|||
|
||||
export interface ITexture2D {
|
||||
get(): unknown;
|
||||
getSize():[number,number];
|
||||
update(options: any): void;
|
||||
bind(): void;
|
||||
resize(options: { width: number; height: number }): void;
|
||||
|
|
|
@ -57,7 +57,7 @@ import Source from '@antv/l7-source';
|
|||
import { encodePickingColor, WorkerSourceMap } from '@antv/l7-utils';
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
import { Container } from 'inversify';
|
||||
import { isFunction, isObject, isUndefined } from 'lodash';
|
||||
import { isEqual, isFunction, isObject, isUndefined } from 'lodash';
|
||||
import { BlendTypes } from '../utils/blend';
|
||||
import { styleDataMapping } from '../utils/dataMappingStyle';
|
||||
import { calculateData } from '../utils/layerData';
|
||||
|
@ -140,7 +140,7 @@ export default class BaseLayer<ChildLayerStyleOptions = {}>
|
|||
// 每个 Layer 都有一个
|
||||
public multiPassRenderer: IMultiPassRenderer;
|
||||
|
||||
// 注入插件集
|
||||
// 注入插件
|
||||
public plugins: ILayerPlugin[];
|
||||
|
||||
public startInit: boolean = false;
|
||||
|
@ -538,8 +538,13 @@ export default class BaseLayer<ChildLayerStyleOptions = {}>
|
|||
values?: StyleAttributeOption,
|
||||
updateOptions?: Partial<IStyleAttributeUpdateOptions>,
|
||||
) {
|
||||
this.updateStyleAttribute('filter', field, values, updateOptions);
|
||||
this.dataState.dataSourceNeedUpdate = true;
|
||||
const flag = this.updateStyleAttribute(
|
||||
'filter',
|
||||
field,
|
||||
values,
|
||||
updateOptions,
|
||||
);
|
||||
this.dataState.dataSourceNeedUpdate = flag;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -552,8 +557,13 @@ export default class BaseLayer<ChildLayerStyleOptions = {}>
|
|||
field,
|
||||
values,
|
||||
};
|
||||
this.updateStyleAttribute('shape', field, values, updateOptions);
|
||||
this.dataState.dataSourceNeedUpdate = true; // 通过数据更新驱动shape 更新
|
||||
const flag = this.updateStyleAttribute(
|
||||
'shape',
|
||||
field,
|
||||
values,
|
||||
updateOptions,
|
||||
);
|
||||
this.dataState.dataSourceNeedUpdate = flag;
|
||||
return this;
|
||||
}
|
||||
public label(
|
||||
|
@ -1338,7 +1348,14 @@ export default class BaseLayer<ChildLayerStyleOptions = {}>
|
|||
field: StyleAttributeField,
|
||||
values?: StyleAttributeOption,
|
||||
updateOptions?: Partial<IStyleAttributeUpdateOptions>,
|
||||
) {
|
||||
): boolean {
|
||||
// encode diff
|
||||
const preAttribute = this.configService.getAttributeConfig(this.id) || {};
|
||||
// @ts-ignore
|
||||
if (isEqual(preAttribute[type], { field, values })) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 存储 Attribute
|
||||
if (
|
||||
[
|
||||
|
@ -1358,6 +1375,7 @@ export default class BaseLayer<ChildLayerStyleOptions = {}>
|
|||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (!this.startInit) {
|
||||
// 开始初始化执行
|
||||
this.pendingStyleAttributes.push({
|
||||
|
@ -1385,6 +1403,7 @@ export default class BaseLayer<ChildLayerStyleOptions = {}>
|
|||
updateOptions,
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public getLayerAttributeConfig(): Partial<ILayerAttributesOption> {
|
||||
|
|
|
@ -6,7 +6,14 @@ import {
|
|||
TYPES,
|
||||
} from '@antv/l7-core';
|
||||
|
||||
import { generateColorRamp, IColorRamp } from '@antv/l7-utils';
|
||||
import {
|
||||
generateCatRamp,
|
||||
generateColorRamp,
|
||||
generateCustomRamp,
|
||||
generateLinearRamp,
|
||||
generateQuantizeRamp,
|
||||
IColorRamp,
|
||||
} from '@antv/l7-utils';
|
||||
|
||||
export default class TextureService implements ITextureService {
|
||||
private layer: ILayer;
|
||||
|
@ -20,21 +27,21 @@ export default class TextureService implements ITextureService {
|
|||
TYPES.IRendererService,
|
||||
);
|
||||
}
|
||||
public getColorTexture(colorRamp: IColorRamp) {
|
||||
public getColorTexture(colorRamp: IColorRamp, domain?: [number, number]) {
|
||||
// TODO 支持传入图片
|
||||
const currentkey = this.getTextureKey(colorRamp);
|
||||
const currentkey = this.getTextureKey(colorRamp, domain);
|
||||
if (this.key === currentkey) {
|
||||
return this.colorTexture;
|
||||
} else {
|
||||
this.createColorTexture(colorRamp);
|
||||
this.createColorTexture(colorRamp, domain);
|
||||
}
|
||||
this.key = currentkey;
|
||||
return this.colorTexture;
|
||||
}
|
||||
|
||||
public createColorTexture(colorRamp: IColorRamp) {
|
||||
public createColorTexture(colorRamp: IColorRamp, domain?: [number, number]) {
|
||||
const { createTexture2D } = this.rendererService;
|
||||
const imageData = generateColorRamp(colorRamp) as ImageData;
|
||||
const imageData = this.getColorRampBar(colorRamp, domain) as ImageData;
|
||||
const texture = createTexture2D({
|
||||
data: imageData.data,
|
||||
width: imageData.width,
|
||||
|
@ -45,8 +52,12 @@ export default class TextureService implements ITextureService {
|
|||
return texture;
|
||||
}
|
||||
|
||||
public setColorTexture(texture: ITexture2D, colorRamp: IColorRamp) {
|
||||
this.key = this.getTextureKey(colorRamp);
|
||||
public setColorTexture(
|
||||
texture: ITexture2D,
|
||||
colorRamp: IColorRamp,
|
||||
domain: [number, number],
|
||||
) {
|
||||
this.key = this.getTextureKey(colorRamp, domain);
|
||||
this.colorTexture = texture;
|
||||
}
|
||||
|
||||
|
@ -54,7 +65,27 @@ export default class TextureService implements ITextureService {
|
|||
this.colorTexture?.destroy();
|
||||
}
|
||||
|
||||
private getTextureKey(colorRamp: IColorRamp): string {
|
||||
return `${colorRamp.colors.join('_')}_${colorRamp.positions.join('_')}`;
|
||||
private getColorRampBar(colorRamp: IColorRamp, domain?: [number, number]) {
|
||||
switch (colorRamp.type) {
|
||||
case 'cat':
|
||||
return generateCatRamp(colorRamp);
|
||||
case 'quantize':
|
||||
return generateQuantizeRamp(colorRamp);
|
||||
case 'custom':
|
||||
return generateCustomRamp(colorRamp, domain as [number, number]);
|
||||
case 'linear':
|
||||
return generateLinearRamp(colorRamp, domain as [number, number]);
|
||||
default:
|
||||
return generateColorRamp(colorRamp) as ImageData;
|
||||
}
|
||||
}
|
||||
|
||||
private getTextureKey(
|
||||
colorRamp: IColorRamp,
|
||||
domain?: [number, number],
|
||||
): string {
|
||||
return `${colorRamp.colors.join('_')}_${colorRamp?.positions?.join('_')}_${
|
||||
colorRamp.type
|
||||
}_${domain?.join('_')}`;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,9 +43,7 @@ export default class DataMappingPlugin implements ILayerPlugin {
|
|||
return flag;
|
||||
}
|
||||
layer.dataState.dataMappingNeedUpdate = false;
|
||||
this.generateMaping(layer, { styleAttributeService });
|
||||
|
||||
return true;
|
||||
return this.generateMaping(layer, { styleAttributeService });
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -72,7 +70,6 @@ export default class DataMappingPlugin implements ILayerPlugin {
|
|||
return this.applyAttributeMapping(filter, record)[0];
|
||||
});
|
||||
}
|
||||
|
||||
if (attributesToRemapping.length) {
|
||||
// 过滤数据
|
||||
const encodeData = this.mapping(
|
||||
|
@ -83,6 +80,7 @@ export default class DataMappingPlugin implements ILayerPlugin {
|
|||
);
|
||||
layer.setEncodedData(encodeData);
|
||||
}
|
||||
|
||||
// 处理文本更新,更新文字形状
|
||||
// layer.emit('remapping', null);
|
||||
});
|
||||
|
@ -96,7 +94,6 @@ export default class DataMappingPlugin implements ILayerPlugin {
|
|||
const attributes = styleAttributeService.getLayerStyleAttributes() || [];
|
||||
const filter = styleAttributeService.getLayerStyleAttribute('filter');
|
||||
const { dataArray } = layer.getSource().data;
|
||||
|
||||
let filterData = dataArray;
|
||||
// 数据过滤完 再执行数据映射
|
||||
if (filter?.scale) {
|
||||
|
@ -111,8 +108,13 @@ export default class DataMappingPlugin implements ILayerPlugin {
|
|||
filterData = layer.processData(filterData);
|
||||
const encodeData = this.mapping(layer, attributes, filterData, undefined);
|
||||
layer.setEncodedData(encodeData);
|
||||
|
||||
if (dataArray.length === 0 && layer.encodeDataLength === 0) {
|
||||
return false;
|
||||
}
|
||||
// 对外暴露事件
|
||||
layer.emit('dataUpdate', null);
|
||||
return true;
|
||||
}
|
||||
|
||||
private mapping(
|
||||
|
|
|
@ -5,7 +5,7 @@ import {
|
|||
IModel,
|
||||
ITexture2D,
|
||||
} from '@antv/l7-core';
|
||||
import { generateColorRamp, getMask, IColorRamp } from '@antv/l7-utils';
|
||||
import { getMask,getDefaultDomain } from '@antv/l7-utils';
|
||||
import BaseModel from '../../core/BaseModel';
|
||||
import { IRasterLayerStyleOptions } from '../../core/interface';
|
||||
import { RasterImageTriangulation } from '../../core/triangulation';
|
||||
|
@ -14,22 +14,21 @@ import rasterVert from '../shaders/raster_2d_vert.glsl';
|
|||
export default class RasterModel extends BaseModel {
|
||||
protected texture: ITexture2D;
|
||||
protected colorTexture: ITexture2D;
|
||||
private rampColors: any;
|
||||
public getUninforms() {
|
||||
const {
|
||||
opacity = 1,
|
||||
clampLow = true,
|
||||
clampHigh = true,
|
||||
noDataValue = -9999999,
|
||||
domain = [0, 1],
|
||||
domain,
|
||||
rampColors,
|
||||
} = this.layer.getLayerConfig() as IRasterLayerStyleOptions;
|
||||
this.colorTexture = this.layer.textureService.getColorTexture(rampColors);
|
||||
|
||||
const newdomain = domain ||getDefaultDomain(rampColors)
|
||||
this.colorTexture = this.layer.textureService.getColorTexture(rampColors,newdomain);
|
||||
return {
|
||||
u_opacity: opacity || 1,
|
||||
u_texture: this.texture,
|
||||
u_domain: domain,
|
||||
u_domain: newdomain,
|
||||
u_clampLow: clampLow,
|
||||
u_clampHigh: typeof clampHigh !== 'undefined' ? clampHigh : clampLow,
|
||||
u_noDataValue: noDataValue,
|
||||
|
@ -56,7 +55,7 @@ export default class RasterModel extends BaseModel {
|
|||
}
|
||||
}
|
||||
|
||||
public async initModels(): Promise<IModel[]> {
|
||||
public async initModels(): Promise<IModel[]> {
|
||||
const {
|
||||
mask = false,
|
||||
maskInside = true,
|
||||
|
@ -122,18 +121,5 @@ export default class RasterModel extends BaseModel {
|
|||
},
|
||||
});
|
||||
}
|
||||
|
||||
private updateColorTexture() {
|
||||
const { createTexture2D } = this.rendererService;
|
||||
const {
|
||||
rampColors,
|
||||
} = this.layer.getLayerConfig() as IRasterLayerStyleOptions;
|
||||
const imageData = generateColorRamp(rampColors as IColorRamp);
|
||||
this.colorTexture = createTexture2D({
|
||||
data: imageData.data,
|
||||
width: imageData.width,
|
||||
height: imageData.height,
|
||||
flipY: false,
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import {
|
|||
IModelUniform,
|
||||
ITexture2D,
|
||||
} from '@antv/l7-core';
|
||||
import { getMask } from '@antv/l7-utils';
|
||||
import { getMask,getDefaultDomain } from '@antv/l7-utils';
|
||||
import BaseModel from '../../core/BaseModel';
|
||||
import { IRasterLayerStyleOptions } from '../../core/interface';
|
||||
import { RasterImageTriangulation } from '../../core/triangulation';
|
||||
|
@ -21,20 +21,21 @@ import {
|
|||
clampLow = true,
|
||||
clampHigh = true,
|
||||
noDataValue = -9999999,
|
||||
domain = [0, 1],
|
||||
domain,
|
||||
rampColors,
|
||||
colorTexture
|
||||
} = this.layer.getLayerConfig() as IRasterLayerStyleOptions;
|
||||
const newdomain = domain ||getDefaultDomain(rampColors)
|
||||
let texture:ITexture2D | undefined = colorTexture;
|
||||
if(!colorTexture) {
|
||||
texture = this.layer.textureService.getColorTexture(rampColors) as ITexture2D;
|
||||
texture = this.layer.textureService.getColorTexture(rampColors,newdomain) as ITexture2D;
|
||||
} else {
|
||||
this.layer.textureService.setColorTexture(colorTexture,rampColors)
|
||||
this.layer.textureService.setColorTexture(colorTexture,rampColors,newdomain)
|
||||
}
|
||||
return {
|
||||
u_opacity: opacity || 1,
|
||||
u_texture: this.texture,
|
||||
u_domain: domain,
|
||||
u_domain: newdomain,
|
||||
u_clampLow: clampLow,
|
||||
u_clampHigh: typeof clampHigh !== 'undefined' ? clampHigh : clampLow,
|
||||
u_noDataValue: noDataValue,
|
||||
|
|
|
@ -1,101 +0,0 @@
|
|||
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_2d_frag.glsl';
|
||||
import rasterVert from '../shaders/raster_2d_vert.glsl';
|
||||
export default class RasterModel extends BaseModel {
|
||||
protected texture: ITexture2D;
|
||||
public getUninforms() {
|
||||
const { createTexture2D } = this.rendererService;
|
||||
const {
|
||||
colorTexture = createTexture2D({
|
||||
data: [],
|
||||
width: 0,
|
||||
height: 0,
|
||||
flipY: false,
|
||||
}),
|
||||
opacity = 1,
|
||||
clampLow = true,
|
||||
clampHigh = true,
|
||||
noDataValue = -9999999,
|
||||
domain = [0, 1],
|
||||
} = this.layer.getLayerConfig() as IRasterLayerStyleOptions;
|
||||
|
||||
return {
|
||||
u_opacity: opacity || 1,
|
||||
u_texture: this.texture,
|
||||
u_domain: domain,
|
||||
u_clampLow: clampLow,
|
||||
u_clampHigh: typeof clampHigh !== 'undefined' ? clampHigh : clampLow,
|
||||
u_noDataValue: noDataValue,
|
||||
u_colorTexture: colorTexture,
|
||||
};
|
||||
}
|
||||
|
||||
public async initModels(): Promise<IModel[]> {
|
||||
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];
|
||||
this.texture = createTexture2D({
|
||||
data: parserDataItem.data,
|
||||
width: parserDataItem.width,
|
||||
height: parserDataItem.height,
|
||||
format: gl.LUMINANCE,
|
||||
type: gl.FLOAT,
|
||||
});
|
||||
|
||||
const model = await this.layer
|
||||
.buildLayerModel({
|
||||
moduleName: 'rasterTileImageData',
|
||||
vertexShader: rasterVert,
|
||||
fragmentShader: rasterFrag,
|
||||
triangulation: RasterImageTriangulation,
|
||||
depth: { enable: false },
|
||||
stencil: getMask(mask, maskInside),
|
||||
})
|
||||
return [model]
|
||||
}
|
||||
|
||||
public async buildModels():Promise<IModel[]> {
|
||||
return await this.initModels();
|
||||
}
|
||||
|
||||
public clearModels(): void {
|
||||
this.texture?.destroy();
|
||||
}
|
||||
|
||||
protected registerBuiltinAttributes() {
|
||||
this.styleAttributeService.registerStyleAttribute({
|
||||
name: 'uv',
|
||||
type: AttributeType.Attribute,
|
||||
descriptor: {
|
||||
name: 'a_Uv',
|
||||
buffer: {
|
||||
usage: gl.DYNAMIC_DRAW,
|
||||
data: [],
|
||||
type: gl.FLOAT,
|
||||
},
|
||||
size: 2,
|
||||
update: (
|
||||
feature: IEncodeFeature,
|
||||
featureIdx: number,
|
||||
vertex: number[],
|
||||
) => {
|
||||
return [vertex[3], vertex[4]];
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ void main() {
|
|||
else if ((!u_clampLow && value < u_domain[0]) || (!u_clampHigh && value > u_domain[1]))
|
||||
gl_FragColor = vec4(0, 0, 0, 0);
|
||||
else {
|
||||
|
||||
float normalisedValue =(value - u_domain[0]) / (u_domain[1] -u_domain[0]);
|
||||
vec4 color = texture2D(u_colorTexture,vec2(normalisedValue, 0));
|
||||
gl_FragColor = color;
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { ILayerAttributesOption } from '@antv/l7-core';
|
||||
// import RasterLayer from './layers/RasterDataLayer';
|
||||
import RasterLayer from '../../raster'
|
||||
import Tile from './Tile';
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import { ILayerAttributesOption, ITexture2D } from '@antv/l7-core';
|
|||
import RasterLayer from '../../raster'
|
||||
import { IRasterLayerStyleOptions } from '../../core/interface';
|
||||
import Tile from './Tile';
|
||||
import { getDefaultDomain } from '@antv/l7-utils';
|
||||
|
||||
const DEFAULT_COLOR_TEXTURE_OPTION = {
|
||||
positions: [0, 1],
|
||||
|
@ -12,9 +13,10 @@ export default class RasterTile extends Tile {
|
|||
private colorTexture: ITexture2D;
|
||||
public async initTileLayer(): Promise<void> {
|
||||
const attributes = this.parent.getLayerAttributeConfig();
|
||||
const layerOptions = this.getLayerOptions();
|
||||
const layerOptions = this.getLayerOptions() ;
|
||||
const sourceOptions = this.getSourceOption();
|
||||
this.colorTexture = this.parent.textureService.getColorTexture((layerOptions as unknown as IRasterLayerStyleOptions).rampColors)
|
||||
const {rampColors,domain} = this.getLayerOptions() as unknown as IRasterLayerStyleOptions;
|
||||
this.colorTexture = this.parent.textureService.getColorTexture(rampColors,domain);
|
||||
const layer = new RasterLayer({
|
||||
...layerOptions,
|
||||
colorTexture: this.colorTexture,
|
||||
|
@ -57,8 +59,8 @@ export default class RasterTile extends Tile {
|
|||
*/
|
||||
public styleUpdate(...arg: any): void {
|
||||
|
||||
const { rampColors = DEFAULT_COLOR_TEXTURE_OPTION } = arg;
|
||||
this.colorTexture = this.parent.textureService.getColorTexture(rampColors)
|
||||
const { rampColors = DEFAULT_COLOR_TEXTURE_OPTION, domain} = arg as IRasterLayerStyleOptions;
|
||||
this.colorTexture = this.parent.textureService.getColorTexture(rampColors,domain || getDefaultDomain(rampColors))
|
||||
this.layers.forEach(layer => layer.style({ colorTexture: this.colorTexture }));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
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>
|
||||
> {
|
||||
public type: string = 'RasterLayer';
|
||||
public async buildModels() {
|
||||
const model = this.getModel();
|
||||
this.layerModel = new model(this);
|
||||
await this.initLayerModels();
|
||||
}
|
||||
|
||||
public getModel() {
|
||||
const type = this.getModelType();
|
||||
return type === 'rasterRgb' ? RasterRgbModel :RasterModel;
|
||||
}
|
||||
public getModelType():string {
|
||||
return this.layerSource.parser.type === 'rasterRgb' ? 'rasterRgb' : 'raster'
|
||||
|
||||
}
|
||||
|
||||
protected getDefaultConfig() {
|
||||
return {};
|
||||
}
|
||||
}
|
|
@ -98,6 +98,10 @@ export default class ReglTexture2D implements ITexture2D {
|
|||
this.height = height;
|
||||
}
|
||||
|
||||
public getSize(): [number, number] {
|
||||
return [this.width, this.height];
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
if (!this.isDestroy) {
|
||||
this.texture?.destroy();
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
### rampColors 颜色色带
|
||||
- type 类型 支持 `linear','quantize','custom','cat'
|
||||
- colors 颜色数组
|
||||
- positions 数据分段区间,可选,quantize 不需要设置 position,position 为原始数据值
|
||||
|
||||
⚠️ 2.13 新增特性
|
||||
|
||||
#### cat 枚举类型色带
|
||||
|
||||
枚举类型色带只支持 0 -255 的整数类型,positions 用来设置枚举
|
||||
```tsx
|
||||
{
|
||||
type:'cat',
|
||||
colors:['#e41a1c','#377eb8','#4daf4a','#984ea3','#ff7f00'],
|
||||
positions:[1,20,101,102,200],
|
||||
}
|
||||
```
|
||||
|
||||
#### quantize 等间距分类色带
|
||||
|
||||
等间距只根据数据的区间 domain 进行均匀分段,如 domain [0,10000],如果分 5 段,每段间距 2000。
|
||||
等间距不需要设置 positions,只需要设置colors,根据colors 的长度设置分段数
|
||||
|
||||
```tsx
|
||||
rampColors: {
|
||||
type:'quantize',
|
||||
colors: ['#f0f9e8','#bae4bc','#7bccc4','#43a2ca','#0868ac']
|
||||
}
|
||||
```
|
||||
#### linear 线性连续色带
|
||||
|
||||
linear 为现有连续类型的加强版,positions 支持设置源数据,不需要转换成 0-1
|
||||
|
||||
```tsx
|
||||
rampColors: {
|
||||
type:'linear',
|
||||
colors: ['#f0f9e8','#bae4bc','#7bccc4','#43a2ca','#0868ac'],
|
||||
positions [0,200,1000,4000,8000]
|
||||
}
|
||||
|
||||
⚠️ 兼容 2.13.0 之前版本,未设置type 时,position 值域为 0-1。
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
#### custom 自定义分段色带
|
||||
|
||||
自定义分段色带区别等间距色带,用户自定义分段间隔。
|
||||
自定义 positions 的长度需要比 colors 的长度多1个,同时poisitions
|
||||
|
||||
```tsx
|
||||
rampColors: {
|
||||
type:'custom',
|
||||
colors: ['#f0f9e8','#bae4bc','#7bccc4','#43a2ca','#0868ac'],
|
||||
positions [0,200,1000,4000,8000,10000]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
配置值域映射颜色的色带,值域的范围为 `[0 - 1]`, 对应的我们需要为每一个 `position` 位置设置一个颜色值。
|
||||
|
||||
⚠️ colors, positions 的长度要相同
|
||||
|
||||
```javascript
|
||||
layer.style({
|
||||
rampColors: {
|
||||
colors: ['#FF4818', '#F7B74A', '#FFF598', '#91EABC', '#2EA9A1', '#206C7C'],
|
||||
positions: [0, 0.2, 0.4, 0.6, 0.8, 1.0],
|
||||
},
|
||||
});
|
|
@ -19,20 +19,5 @@ layer.style({
|
|||
| noDataValue | `number` | 不会显示的值 | `-9999999` |
|
||||
| rampColors | `IRampColors` | 值域映射颜色的色带 | `/` |
|
||||
|
||||
#### rampColors
|
||||
|
||||
- colors 颜色数组
|
||||
- positions 数据区间
|
||||
|
||||
配置值域映射颜色的色带,值域的范围为 `[0 - 1]`, 对应的我们需要为每一个 `position` 位置设置一个颜色值。
|
||||
|
||||
⚠️ colors, positions 的长度要相同
|
||||
|
||||
```javascript
|
||||
layer.style({
|
||||
rampColors: {
|
||||
colors: ['#FF4818', '#F7B74A', '#FFF598', '#91EABC', '#2EA9A1', '#206C7C'],
|
||||
positions: [0, 0.2, 0.4, 0.6, 0.8, 1.0],
|
||||
},
|
||||
});
|
||||
```
|
||||
<embed src="@/docs/api/raster_layer/common/rampcolors.md"></embed>
|
|
@ -17,20 +17,4 @@ layer.style({
|
|||
| noDataValue | `number` | 不会显示的值 | `-9999999` |
|
||||
| rampColors | `IRampColors` | 值域映射颜色的色带 | `/` |
|
||||
|
||||
#### rampColors
|
||||
|
||||
- colors 颜色数组
|
||||
- positions 数据区间
|
||||
|
||||
配置值域映射颜色的色带,值域的范围为 `[0 - 1]`, 对应的我们需要为每一个 `position` 位置设置一个颜色值。
|
||||
|
||||
⚠️ colors, positions 的长度要相同
|
||||
|
||||
```javascript
|
||||
layer.style({
|
||||
rampColors: {
|
||||
colors: ['#FF4818', '#F7B74A', '#FFF598', '#91EABC', '#2EA9A1', '#206C7C'],
|
||||
positions: [0, 0.2, 0.4, 0.6, 0.8, 1.0],
|
||||
},
|
||||
});
|
||||
```
|
||||
<embed src="@/docs/api/raster_layer/common/rampcolors.md"></embed>
|
||||
|
|
|
@ -12,7 +12,7 @@ order: 0
|
|||
- L7 本身内部没有提供栅格数据格式, 需要将外部的栅格数据文件解析后或者提供解析方法做传入、如 `tiff`、`lerc`。
|
||||
- 栅格图层除了支持简单渲染之外还支持栅格数据的多波段计算,可以用于绘制遥感彩色影像。
|
||||
|
||||
### 直接绘制
|
||||
### 数据绘制
|
||||
|
||||
我们可以直接在外部计算出栅格的波段数据后传给栅格图层使用。
|
||||
|
||||
|
|
|
@ -67,13 +67,13 @@ layer.on('legend:color', (ev) => console.log(ev));
|
|||
|
||||
### legend:size
|
||||
|
||||
数据映射更新,图例发生变化,color 大小改变
|
||||
数据映射更新,图例发生变化,size 大小改变
|
||||
参数 option
|
||||
- type 映射通道、图例类型
|
||||
- attr 映射实例
|
||||
|
||||
```js
|
||||
layer.on('legend:color', (ev) => console.log(ev));
|
||||
layer.on('legend:size', (ev) => console.log(ev));
|
||||
|
||||
```
|
||||
|
||||
|
|
|
@ -10,11 +10,6 @@
|
|||
"title": "雷达图",
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_816329/afts/img/A*JDO-R5XU7xwAAAAAAAAAAAAAARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "light.js",
|
||||
"title": "夜光图",
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_816329/afts/img/A*xznhSJFEAXYAAAAAAAAAAAAAARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "image.js",
|
||||
"title": "图片",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
---
|
||||
title: 栅格图层
|
||||
title: 图片栅格
|
||||
order: 0
|
||||
---
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
---
|
||||
title: 数据栅格
|
||||
title: 多波段
|
||||
order: 0
|
||||
---
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
// https://gw.alipayobjects.com/zos/antvdemo/assets/2019_clip/ndvi_201905.tif
|
||||
import { RasterLayer, Scene } from '@antv/l7';
|
||||
import { GaodeMap } from '@antv/l7-maps';
|
||||
import * as GeoTIFF from 'geotiff';
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
map: new GaodeMap({
|
||||
center: [121.268, 30.3628],
|
||||
zoom: 3,
|
||||
}),
|
||||
});
|
||||
async function getTiffData() {
|
||||
const response = await fetch(
|
||||
'https://gw.alipayobjects.com/os/rmsportal/XKgkjjGaAzRyKupCBiYW.dat',
|
||||
);
|
||||
const arrayBuffer = await response.arrayBuffer();
|
||||
return arrayBuffer;
|
||||
}
|
||||
scene.on('loaded', async () => {
|
||||
const tiffdata = await getTiffData();
|
||||
const tiff = await GeoTIFF.fromArrayBuffer(tiffdata);
|
||||
const image = await tiff.getImage();
|
||||
const width = image.getWidth();
|
||||
const height = image.getHeight();
|
||||
const values = await image.readRasters();
|
||||
|
||||
const layer = new RasterLayer();
|
||||
layer
|
||||
.source(values[0], {
|
||||
parser: {
|
||||
type: 'raster',
|
||||
width,
|
||||
height,
|
||||
extent: [73.482190241, 3.82501784112, 135.106618732, 57.6300459963],
|
||||
},
|
||||
})
|
||||
.style({
|
||||
opacity: 1,
|
||||
clampLow: false,
|
||||
clampHigh: false,
|
||||
domain: [0, 10000],
|
||||
rampColors: {
|
||||
type:'custom',
|
||||
colors: ['#b2182b','#d6604d','#f4a582','#fddbc7','#f7f7f7','#d1e5f0','#92c5de','#4393c3','#2166ac'],
|
||||
positions: [0, 50, 200, 500, 2000, 3000, 4000, 5000, 8000,10000],
|
||||
},
|
||||
});
|
||||
|
||||
scene.addLayer(layer)
|
||||
});
|
|
@ -0,0 +1,50 @@
|
|||
// https://gw.alipayobjects.com/zos/antvdemo/assets/2019_clip/ndvi_201905.tif
|
||||
import { RasterLayer, Scene } from '@antv/l7';
|
||||
import { GaodeMap } from '@antv/l7-maps';
|
||||
import * as GeoTIFF from 'geotiff';
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
map: new GaodeMap({
|
||||
center: [121.268, 30.3628],
|
||||
zoom: 3,
|
||||
}),
|
||||
});
|
||||
async function getTiffData() {
|
||||
const response = await fetch(
|
||||
'https://gw.alipayobjects.com/os/rmsportal/XKgkjjGaAzRyKupCBiYW.dat',
|
||||
);
|
||||
const arrayBuffer = await response.arrayBuffer();
|
||||
return arrayBuffer;
|
||||
}
|
||||
scene.on('loaded', async () => {
|
||||
const tiffdata = await getTiffData();
|
||||
const tiff = await GeoTIFF.fromArrayBuffer(tiffdata);
|
||||
const image = await tiff.getImage();
|
||||
const width = image.getWidth();
|
||||
const height = image.getHeight();
|
||||
const values = await image.readRasters();
|
||||
|
||||
const layer = new RasterLayer();
|
||||
layer
|
||||
.source(values[0], {
|
||||
parser: {
|
||||
type: 'raster',
|
||||
width,
|
||||
height,
|
||||
extent: [73.482190241, 3.82501784112, 135.106618732, 57.6300459963],
|
||||
},
|
||||
})
|
||||
.style({
|
||||
opacity: 1,
|
||||
clampLow: false,
|
||||
clampHigh: false,
|
||||
domain: [0, 10000],
|
||||
rampColors: {
|
||||
type:'quantize', // 等间距 不需要设置 position
|
||||
colors: ['#b2182b','#d6604d','#f4a582','#fddbc7','#f7f7f7','#d1e5f0','#92c5de','#4393c3','#2166ac'],
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
scene.addLayer(layer)
|
||||
});
|
|
@ -49,8 +49,9 @@ async function addLayer() {
|
|||
domain: [ 0, 90 ],
|
||||
nodataValue: 0,
|
||||
rampColors: {
|
||||
colors: [ 'rgba(92,58,16,0)', 'rgba(92,58,16,0)', '#fabd08', '#f1e93f', '#f1ff8f', '#fcfff7' ],
|
||||
positions: [ 0, 0.05, 0.1, 0.25, 0.5, 1.0 ]
|
||||
type:'linear', // 2.13.0 及以后版本支持
|
||||
colors: ['rgba(92,58,16,0)','rgba(92,58,16,0)', '#fabd08', '#f1e93f', '#f1ff8f', '#fcfff7' ],
|
||||
positions: [0,3, 9, 22.5, 45, 90 ]
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
// https://gw.alipayobjects.com/zos/antvdemo/assets/2019_clip/ndvi_201905.tif
|
||||
import { RasterLayer, Scene } from '@antv/l7';
|
||||
import { GaodeMap } from '@antv/l7-maps';
|
||||
import * as GeoTIFF from 'geotiff';
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
map: new GaodeMap({
|
||||
style: 'dark',
|
||||
center: [ 105, 37.5 ],
|
||||
zoom: 2.5
|
||||
})
|
||||
});
|
||||
scene.on('loaded', () => {
|
||||
addLayer();
|
||||
});
|
||||
async function getTiffData() {
|
||||
const response = await fetch(
|
||||
'https://gw.alipayobjects.com/zos/antvdemo/assets/light_clip/lightF182013.tiff'
|
||||
);
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
async function addLayer() {
|
||||
const tiffdata = await getTiffData();
|
||||
|
||||
const layer = new RasterLayer({});
|
||||
layer
|
||||
.source(tiffdata.data, {
|
||||
parser: {
|
||||
type: 'raster',
|
||||
width: tiffdata.width,
|
||||
height: tiffdata.height,
|
||||
extent: [ 73.4821902409999979, 3.8150178409999995, 135.1066187319999869, 57.6300459959999998 ]
|
||||
}
|
||||
})
|
||||
.style({
|
||||
clampLow: false,
|
||||
clampHigh: false,
|
||||
domain: [ 0, 90 ],
|
||||
nodataValue: 0,
|
||||
rampColors: {
|
||||
positions: [ 0, 0.05, 0.1, 0.25, 0.5, 1.0 ],// 数据需要换成 0-1
|
||||
colors: ['rgba(92,58,16,0)','rgba(92,58,16,0)', '#fabd08', '#f1e93f', '#f1ff8f', '#fcfff7' ],
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
scene.addLayer(layer);
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"title": {
|
||||
"zh": "单波段数据栅格",
|
||||
"en": "Gallery"
|
||||
},
|
||||
"demos": [
|
||||
|
||||
{
|
||||
"filename": "light_default.js",
|
||||
"title": "夜光图-默认线性",
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_816329/afts/img/A*xznhSJFEAXYAAAAAAAAAAAAAARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "light.js",
|
||||
"title": "夜光图-线性色带",
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_816329/afts/img/A*xznhSJFEAXYAAAAAAAAAAAAAARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "dem.js",
|
||||
"title": "高程图-自定义色带",
|
||||
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*_RtOTrYOOrUAAAAAAAAAAAAADmJ7AQ/original"
|
||||
},
|
||||
{
|
||||
"filename": "dem_quantize.js",
|
||||
"title": "高程图-等间距色带",
|
||||
"screenshot": "https://mdn.alipayobjects.com//huamei_qa8qxu/afts/img/A*DT1HQbl4JKMAAAAAAAAAAAAADmJ7AQ/original"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: Raster Data Map
|
||||
order: 0
|
||||
---
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: 单波段
|
||||
order: 0
|
||||
---
|
|
@ -4,56 +4,33 @@ import * as GeoTIFF from 'geotiff';
|
|||
|
||||
const colorList = [
|
||||
'#419bdf', // Water
|
||||
'#419bdf',
|
||||
|
||||
'#397d49', // Tree
|
||||
'#397d49',
|
||||
'#358221', // Tree
|
||||
|
||||
'#88b053', // Grass
|
||||
'#88b053',
|
||||
|
||||
|
||||
'#7a87c6', // vegetation
|
||||
'#7a87c6',
|
||||
|
||||
|
||||
'#e49635', // Crops
|
||||
'#e49635',
|
||||
|
||||
|
||||
'#dfc35a', // shrub
|
||||
'#dfc35a',
|
||||
|
||||
'#c4281b', // Built Area
|
||||
'#c4281b',
|
||||
|
||||
'#a59b8f', // Bare ground
|
||||
'#a59b8f',
|
||||
'#ED022A', // Built Area
|
||||
|
||||
'#a8ebff', // Snow
|
||||
'#a8ebff',
|
||||
|
||||
'#616161', // Clouds
|
||||
'#616161'
|
||||
'#EDE9E4', // Bare ground
|
||||
|
||||
|
||||
'#F2FAFF', // Snow
|
||||
|
||||
'#C8C8C8', // Clouds
|
||||
];
|
||||
const positions = [
|
||||
0.0,
|
||||
0.1,
|
||||
0.1,
|
||||
0.2,
|
||||
0.2,
|
||||
0.3,
|
||||
0.3,
|
||||
0.4,
|
||||
0.4,
|
||||
0.5,
|
||||
0.5,
|
||||
0.6,
|
||||
0.6,
|
||||
0.7,
|
||||
0.7,
|
||||
0.8,
|
||||
0.8,
|
||||
0.9,
|
||||
0.9,
|
||||
1.0
|
||||
1,2,3,4,5,6,7,8,9,10,11,
|
||||
];
|
||||
|
||||
const scene = new Scene({
|
||||
|
@ -97,9 +74,10 @@ scene.on('loaded', () => {
|
|||
|
||||
layer.source(tileSource)
|
||||
.style({
|
||||
domain: [ 0.001, 11.001 ],
|
||||
domain: [ 0, 255],// 枚举类型domain 必须为0-255
|
||||
clampLow: false,
|
||||
rampColors: {
|
||||
type:'cat',
|
||||
colors: colorList,
|
||||
positions
|
||||
}
|
||||
|
@ -115,7 +93,7 @@ const wrap = document.getElementById('map');
|
|||
const legend = document.createElement('div');
|
||||
|
||||
const data = [];
|
||||
for (let i = 0; i < colorList.length; i += 2) {
|
||||
for (let i = 0; i < colorList.length; i += 1) {
|
||||
data.push({
|
||||
color: colorList[i],
|
||||
text: [
|
||||
|
@ -129,7 +107,7 @@ for (let i = 0; i < colorList.length; i += 2) {
|
|||
'Bare ground',
|
||||
'Snow',
|
||||
'Clouds'
|
||||
][i / 2]
|
||||
][i]
|
||||
});
|
||||
}
|
||||
const strArr = [];
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import * as d3 from 'd3-color';
|
||||
import { Context } from 'vm';
|
||||
import { $window, isMini } from './mini-adapter';
|
||||
export interface IColorRamp {
|
||||
type?: 'cat' | 'linear' | 'quantize' | 'custom'
|
||||
positions: number[];
|
||||
colors: string[];
|
||||
weights?: number[];
|
||||
}
|
||||
|
||||
export function isColor(str: any) {
|
||||
|
@ -51,6 +52,7 @@ export interface IImagedata {
|
|||
height: number;
|
||||
}
|
||||
|
||||
// 连续型 老版本兼容
|
||||
export function generateColorRamp(
|
||||
colorRamp: IColorRamp,
|
||||
): ImageData | IImagedata {
|
||||
|
@ -60,29 +62,18 @@ export function generateColorRamp(
|
|||
canvas.height = 1;
|
||||
let data = null;
|
||||
|
||||
if (colorRamp.weights) {
|
||||
// draw enum color
|
||||
let count = 0;
|
||||
colorRamp.weights.map((w, index) => {
|
||||
const color = colorRamp.colors[index] || 'rgba(0, 0, 0, 0)';
|
||||
const stop = count + w;
|
||||
ctx.fillStyle = color;
|
||||
ctx.fillRect(count * 256, 0, stop * 256, 1);
|
||||
count = stop;
|
||||
});
|
||||
} else {
|
||||
// draw linear color
|
||||
const gradient = ctx.createLinearGradient(0, 0, 256, 1);
|
||||
// draw linear color
|
||||
const gradient = ctx.createLinearGradient(0, 0, 256, 1);
|
||||
|
||||
const min = colorRamp.positions[0];
|
||||
const max = colorRamp.positions[colorRamp.positions.length - 1];
|
||||
for (let i = 0; i < colorRamp.colors.length; ++i) {
|
||||
const value = (colorRamp.positions[i] - min) / (max - min);
|
||||
gradient.addColorStop(value, colorRamp.colors[i]);
|
||||
}
|
||||
ctx.fillStyle = gradient;
|
||||
ctx.fillRect(0, 0, 256, 1);
|
||||
const min = colorRamp.positions[0];
|
||||
const max = colorRamp.positions[colorRamp.positions.length - 1];
|
||||
for (let i = 0; i < colorRamp.colors.length; ++i) {
|
||||
const value = (colorRamp.positions[i] - min) / (max - min);
|
||||
gradient.addColorStop(value, colorRamp.colors[i]);
|
||||
}
|
||||
ctx.fillStyle = gradient;
|
||||
ctx.fillRect(0, 0, 256, 1);
|
||||
|
||||
|
||||
if (!isMini) {
|
||||
data = ctx.getImageData(0, 0, 256, 1).data;
|
||||
|
@ -108,3 +99,149 @@ export function generateColorRamp(
|
|||
return { data, width: 256, height: 1 };
|
||||
}
|
||||
}
|
||||
|
||||
// 连续型 Position 支持设置原始数据
|
||||
export function generateLinearRamp(
|
||||
colorRamp: IColorRamp,
|
||||
domain: [number, number],
|
||||
): ImageData | IImagedata {
|
||||
let canvas = $window.document.createElement('canvas');
|
||||
let ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
canvas.width = 256;
|
||||
canvas.height = 1;
|
||||
// draw linear color
|
||||
const gradient = ctx.createLinearGradient(0, 0, 256, 1);
|
||||
const step = domain[1] - domain[0];
|
||||
|
||||
for (let i = 0; i < colorRamp.colors.length; ++i) {
|
||||
const value = Math.max((colorRamp.positions[i] - domain[0]) / step,0);
|
||||
console.log(value)
|
||||
gradient.addColorStop(value, colorRamp.colors[i]);
|
||||
}
|
||||
ctx.fillStyle = gradient;
|
||||
ctx.fillRect(0, 0, 256, 1);
|
||||
const data = ctx.getImageData(0, 0, 256, 1).data;
|
||||
const imageData = toIEIMageData(ctx, data);
|
||||
|
||||
// @ts-ignore
|
||||
canvas = null;
|
||||
// @ts-ignore
|
||||
ctx = null;
|
||||
return imageData
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 枚举类型
|
||||
export function generateCatRamp(
|
||||
colorRamp: IColorRamp,
|
||||
): ImageData | IImagedata {
|
||||
|
||||
let canvas = $window.document.createElement('canvas');
|
||||
let ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
canvas.width = 256;
|
||||
canvas.height = 1;
|
||||
const imageData = ctx.createImageData(256, 1);
|
||||
imageData.data.fill(0);
|
||||
colorRamp.positions.forEach((p: number, index: number) => {
|
||||
const colorArray = rgb2arr(colorRamp.colors[index])
|
||||
imageData.data[p * 4 + 0] = colorArray[0] * 255;
|
||||
imageData.data[p * 4 + 1] = colorArray[1] * 255;
|
||||
imageData.data[p * 4 + 2] = colorArray[2] * 255;
|
||||
imageData.data[p * 4 + 3] = colorArray[3] * 255;
|
||||
})
|
||||
// @ts-ignore
|
||||
canvas = null;
|
||||
// @ts-ignore
|
||||
ctx = null;
|
||||
return imageData;
|
||||
}
|
||||
|
||||
// 等间距
|
||||
export function generateQuantizeRamp(
|
||||
colorRamp: IColorRamp,
|
||||
): ImageData | IImagedata {
|
||||
let canvas = $window.document.createElement('canvas');
|
||||
let ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
ctx.globalAlpha = 1.0
|
||||
canvas.width = 256;
|
||||
canvas.height = 1;
|
||||
const step = 256 / colorRamp.colors.length;// TODO 精度问题
|
||||
// draw linear color
|
||||
for (let i = 0; i < colorRamp.colors.length; i++) {
|
||||
ctx.beginPath();
|
||||
ctx.lineWidth = 2;
|
||||
|
||||
ctx.strokeStyle = colorRamp.colors[i];
|
||||
ctx.moveTo(i * step, 0); // positioned at 50,25
|
||||
ctx.lineTo((i + 1) * step, 0);
|
||||
ctx.stroke();
|
||||
|
||||
}
|
||||
|
||||
const data = ctx.getImageData(0, 0, 256, 1).data;
|
||||
// 使用 createImageData 替代 new ImageData、兼容 IE11
|
||||
const imageData = toIEIMageData(ctx, data);
|
||||
|
||||
|
||||
// @ts-ignore
|
||||
canvas = null;
|
||||
// @ts-ignore
|
||||
ctx = null;
|
||||
return imageData;
|
||||
}
|
||||
|
||||
// 自定义间距
|
||||
|
||||
export function generateCustomRamp(
|
||||
colorRamp: IColorRamp,
|
||||
domain: [number, number],
|
||||
): ImageData | IImagedata {
|
||||
|
||||
let canvas = $window.document.createElement('canvas');
|
||||
let ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
ctx.globalAlpha = 1.0
|
||||
canvas.width = 256;
|
||||
canvas.height = 1;
|
||||
const step = domain[1] - domain[0];
|
||||
if(colorRamp.positions.length - colorRamp.colors.length !==1) {
|
||||
console.warn('positions 的数字个数应当比 colors 的样式多一个,poisitions 的首尾值一般为数据的最大最新值')
|
||||
}
|
||||
|
||||
for (let i = 0; i < colorRamp.colors.length; i++) {
|
||||
ctx.beginPath();
|
||||
ctx.lineWidth = 2;
|
||||
ctx.strokeStyle = colorRamp.colors[i];
|
||||
ctx.moveTo((colorRamp.positions[i] - domain[0]) / step * 255, 0); // positioned at 50,25
|
||||
ctx.lineTo((colorRamp.positions[i + 1]- domain[0]) / step * 255, 0);
|
||||
ctx.stroke();
|
||||
|
||||
}
|
||||
const data = ctx.getImageData(0, 0, 256, 1).data;
|
||||
const imageData = toIEIMageData(ctx, data);
|
||||
// @ts-ignore
|
||||
canvas = null;
|
||||
// @ts-ignore
|
||||
ctx = null;
|
||||
return imageData;
|
||||
}
|
||||
function toIEIMageData(ctx: Context, data: Uint8ClampedArray) {
|
||||
const imageData = ctx.createImageData(256, 1);
|
||||
for (let i = 0; i < imageData.data.length; i += 4) {
|
||||
imageData.data[i + 0] = data[i + 0];
|
||||
imageData.data[i + 1] = data[i + 1];
|
||||
imageData.data[i + 2] = data[i + 2];
|
||||
imageData.data[i + 3] = data[i + 3];
|
||||
}
|
||||
return imageData
|
||||
}
|
||||
|
||||
export function getDefaultDomain(rampColors:IColorRamp) {
|
||||
switch (rampColors.type) {
|
||||
case 'cat' :
|
||||
return [0,255]
|
||||
default:
|
||||
[0,1]
|
||||
}
|
||||
|
||||
}
|
|
@ -2,7 +2,7 @@ import earcut from 'earcut';
|
|||
import { calculateCentroid } from '../geo';
|
||||
import ExtrudePolyline from './extrude_polyline';
|
||||
import { IEncodeFeature } from './interface';
|
||||
|
||||
const LATMAX = 85.0511287798;
|
||||
export function LineTriangulation(feature: IEncodeFeature) {
|
||||
const { coordinates, originCoordinates, version } = feature;
|
||||
// let path = coordinates as number[][][] | number[][];
|
||||
|
@ -91,9 +91,17 @@ export function polygonFillTriangulation(feature: IEncodeFeature) {
|
|||
}
|
||||
|
||||
function project_mercator(x: number, y: number) {
|
||||
let y1 = y;
|
||||
if (y > LATMAX) {
|
||||
y1 = LATMAX;
|
||||
}
|
||||
if (y < -LATMAX) {
|
||||
y1 = -LATMAX;
|
||||
}
|
||||
|
||||
return [
|
||||
(Math.PI * x) / 180 + Math.PI,
|
||||
Math.PI - Math.log(Math.tan(Math.PI * 0.25 + ((Math.PI * y) / 180) * 0.5)),
|
||||
Math.PI - Math.log(Math.tan(Math.PI * 0.25 + ((Math.PI * y1) / 180) * 0.5)),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue