mirror of https://gitee.com/antv-l7/antv-l7
commit
1fb532d0e7
|
@ -5,7 +5,7 @@ order: 2
|
|||
|
||||
# 简介
|
||||
|
||||
L7 专注数据可视化层数据表达,目前 L7 还不支持独立的地图引擎,需要引入第三方引擎,目前支持 高德地图和 MapBox 两种。
|
||||
L7 专注数据可视化层数据表达,目前 L7 还不支持独立的地图引擎,需要引入第三方引擎,目前支持 高德地图和 MapBox 两种。=
|
||||
L7 在内部解决了不同底图地图直接的差异,同时 L7 层面统一管理地图的操作方法。
|
||||
|
||||
## Map
|
||||
|
@ -25,6 +25,15 @@ L7 在内部解决了不同底图地图直接的差异,同时 L7 层面统一
|
|||
|
||||
#### 高德地图实例化
|
||||
|
||||
高德地图 API 配置参数
|
||||
|
||||
- token
|
||||
注册高德 [API token](https://lbs.amap.com/api/javascript-api/guide/abc/prepare)
|
||||
|
||||
- plugin {array} `['AMap.ElasticMarker','AMap.CircleEditor']`
|
||||
|
||||
加载[高德地图插件](https://lbs.amap.com/api/javascript-api/guide/abc/plugins)
|
||||
|
||||
```javascript
|
||||
const L7AMap = new GaodeMap({
|
||||
pitch: 35.210526315789465,
|
||||
|
@ -32,6 +41,7 @@ const L7AMap = new GaodeMap({
|
|||
center: [104.288144, 31.239692],
|
||||
zoom: 4.4,
|
||||
token: 'xxxx - token',
|
||||
plugin: [], // 可以不设置
|
||||
});
|
||||
```
|
||||
|
||||
|
@ -59,6 +69,8 @@ const scene = new Scene({
|
|||
|
||||
⚠️ 传入地图实例需要自行引入相关地图的 API
|
||||
|
||||
⚠️ viewMode 设置为 3D 模式
|
||||
|
||||
#### 传入高德地图实例
|
||||
|
||||
```javascript
|
||||
|
@ -76,6 +88,9 @@ const scene = new Scene({
|
|||
});
|
||||
```
|
||||
|
||||
[示例地址](/zh/examples/tutorial/map#amapInstance)
|
||||
[代码地址](https://github.com/antvis/L7/blob/master/examples/tutorial/map/demo/amapInstance.js)
|
||||
|
||||
#### 传入 Mapbox 地图实例
|
||||
|
||||
```javascript
|
||||
|
|
|
@ -25,6 +25,15 @@ L7 在内部解决了不同底图地图直接的差异,同时 L7 层面统一
|
|||
|
||||
#### 高德地图实例化
|
||||
|
||||
高德地图 API 配置参数
|
||||
|
||||
- token
|
||||
注册高德 [API token](https://lbs.amap.com/api/javascript-api/guide/abc/prepare)
|
||||
|
||||
- plugin {array} `['AMap.ElasticMarker','AMap.CircleEditor']`
|
||||
|
||||
加载[高德地图插件](https://lbs.amap.com/api/javascript-api/guide/abc/plugins)
|
||||
|
||||
```javascript
|
||||
const L7AMap = new GaodeMap({
|
||||
pitch: 35.210526315789465,
|
||||
|
@ -32,6 +41,7 @@ const L7AMap = new GaodeMap({
|
|||
center: [104.288144, 31.239692],
|
||||
zoom: 4.4,
|
||||
token: 'xxxx - token',
|
||||
plugin: [], // 可以不设置
|
||||
});
|
||||
```
|
||||
|
||||
|
@ -59,6 +69,8 @@ const scene = new Scene({
|
|||
|
||||
⚠️ 传入地图实例需要自行引入相关地图的 API
|
||||
|
||||
⚠️ viewMode 设置为 3D 模式
|
||||
|
||||
#### 传入高德地图实例
|
||||
|
||||
```javascript
|
||||
|
|
|
@ -187,7 +187,6 @@ layer.source(
|
|||
parser:{
|
||||
type:'json',
|
||||
coordinates: "coord",
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
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({
|
||||
pitch: 0,
|
||||
style: 'dark',
|
||||
center: [ 115.5268, 34.3628 ],
|
||||
zoom: 3
|
||||
})
|
||||
});
|
||||
addLayer();
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
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.482190241, 3.82501784112, 135.106618732, 57.6300459963 ]
|
||||
}
|
||||
})
|
||||
.style({
|
||||
heightRatio: 100,
|
||||
opacity: 0.8,
|
||||
domain: [ 0, 8000 ],
|
||||
rampColors: {
|
||||
colors: [ '#FF4818', '#F7B74A', '#FFF598', '#91EABC', '#2EA9A1', '#206C7C' ].reverse(),
|
||||
positions: [ 0, 0.2, 0.4, 0.6, 0.8, 1.0 ]
|
||||
}
|
||||
});
|
||||
|
||||
scene.addLayer(layer);
|
||||
}
|
|
@ -46,7 +46,7 @@ const defaultLayerConfig: Partial<ILayerConfig> = {
|
|||
'vesica',
|
||||
],
|
||||
shape3d: ['cylinder', 'triangleColumn', 'hexagonColumn', 'squareColumn'],
|
||||
minZoom: 0,
|
||||
minZoom: -1,
|
||||
maxZoom: 24,
|
||||
visible: true,
|
||||
autoFit: false,
|
||||
|
|
|
@ -40,12 +40,12 @@ describe('ConfigService', () => {
|
|||
const { valid, errorText } = configService.validateMapConfig({
|
||||
zoom: 100,
|
||||
minZoom: 100,
|
||||
maxZoom: -1,
|
||||
maxZoom: -2,
|
||||
});
|
||||
expect(valid).toBeFalsy();
|
||||
expect(errorText).toMatch('zoom should be <= 20');
|
||||
expect(errorText).toMatch('minZoom should be <= 20');
|
||||
expect(errorText).toMatch('maxZoom should be >= 0');
|
||||
expect(errorText).toMatch('zoom should be <= 24');
|
||||
expect(errorText).toMatch('minZoom should be <= 24');
|
||||
expect(errorText).toMatch('maxZoom should be >= -1');
|
||||
|
||||
expect(
|
||||
configService.validateMapConfig({
|
||||
|
|
|
@ -6,18 +6,18 @@ export default {
|
|||
// 地图缩放等级
|
||||
zoom: {
|
||||
type: 'number',
|
||||
minimum: 0,
|
||||
maximum: 20,
|
||||
minimum: -1,
|
||||
maximum: 24,
|
||||
},
|
||||
minZoom: {
|
||||
type: 'number',
|
||||
minimum: 0,
|
||||
maximum: 20,
|
||||
minimum: -1,
|
||||
maximum: 24,
|
||||
},
|
||||
maxZoom: {
|
||||
type: 'number',
|
||||
minimum: 0,
|
||||
maximum: 20,
|
||||
minimum: -1,
|
||||
maximum: 24,
|
||||
},
|
||||
// 地图中心点
|
||||
center: {
|
||||
|
|
|
@ -5,7 +5,7 @@ export interface IWarnInfo {
|
|||
}
|
||||
const WarnInfo: IWarnInfo = {
|
||||
MapToken:
|
||||
'您正在使用 Demo测试地图token,如果生成环境中使用去对应地图请注册Token',
|
||||
'您正在使用 Demo测试地图token,如果生产环境中使用去对应地图请注册Token',
|
||||
SDK: '请确认引入了mapbox-gl api且在L7之前引入',
|
||||
};
|
||||
|
||||
|
|
|
@ -71,6 +71,10 @@ export interface IMapConfig<RawMap = {}> {
|
|||
* 地图实例
|
||||
*/
|
||||
mapInstance?: RawMap;
|
||||
/**
|
||||
* 高德地图API插件
|
||||
*/
|
||||
plugin?: string[];
|
||||
/**
|
||||
* 容器 DOM id
|
||||
*/
|
||||
|
|
|
@ -424,7 +424,9 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
|
|||
this.buildModels();
|
||||
return this;
|
||||
}
|
||||
public style(options: object & Partial<ILayerConfig>): ILayer {
|
||||
public style(
|
||||
options: Partial<ChildLayerStyleOptions> & Partial<ILayerConfig>,
|
||||
): ILayer {
|
||||
const { passes, ...rest } = options;
|
||||
|
||||
// passes 特殊处理
|
||||
|
@ -582,7 +584,6 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
|
|||
}
|
||||
public isVisible(): boolean {
|
||||
const zoom = this.mapService.getZoom();
|
||||
|
||||
const {
|
||||
visible,
|
||||
minZoom = -Infinity,
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
ILayer,
|
||||
ILayerConfig,
|
||||
ILayerModel,
|
||||
ILayerService,
|
||||
IMapService,
|
||||
IModel,
|
||||
IModelUniform,
|
||||
|
@ -44,6 +45,7 @@ export default class BaseModel<ChildLayerStyleOptions = {}>
|
|||
protected styleAttributeService: IStyleAttributeService;
|
||||
protected mapService: IMapService;
|
||||
protected cameraService: ICameraService;
|
||||
protected layerService: ILayerService;
|
||||
|
||||
constructor(layer: ILayer) {
|
||||
this.layer = layer;
|
||||
|
@ -57,6 +59,10 @@ export default class BaseModel<ChildLayerStyleOptions = {}>
|
|||
this.cameraService = layer
|
||||
.getContainer()
|
||||
.get<ICameraService>(TYPES.ICameraService);
|
||||
this.layerService = layer
|
||||
.getContainer()
|
||||
.get<ILayerService>(TYPES.ILayerService);
|
||||
|
||||
// 注册 Attribute
|
||||
this.registerBuiltinAttributes();
|
||||
// 开启动画
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
import BaseLayer from '../core/BaseLayer';
|
||||
import ImageModels, { ImageModelType } from './models/index';
|
||||
interface IImageLayerStyleOptions {
|
||||
opacity: number;
|
||||
}
|
||||
export default class ImageLayer extends BaseLayer<IImageLayerStyleOptions> {
|
||||
public type: string = 'ImageLayer';
|
||||
protected getConfigSchema() {
|
||||
return {
|
||||
properties: {
|
||||
opacity: {
|
||||
type: 'number',
|
||||
minimum: 0,
|
||||
maximum: 1,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
protected getDefaultConfig() {
|
||||
const type = this.getModelType();
|
||||
const defaultConfig = {
|
||||
image: {},
|
||||
};
|
||||
return defaultConfig[type];
|
||||
}
|
||||
protected buildModels() {
|
||||
const modelType = this.getModelType();
|
||||
this.layerModel = new ImageModels[modelType](this);
|
||||
this.models = this.layerModel.buildModels();
|
||||
}
|
||||
|
||||
protected getModelType(): ImageModelType {
|
||||
return 'image';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
import {
|
||||
AttributeType,
|
||||
gl,
|
||||
IEncodeFeature,
|
||||
ILayer,
|
||||
ILayerPlugin,
|
||||
ILogService,
|
||||
IModel,
|
||||
IModelUniform,
|
||||
IRasterParserDataItem,
|
||||
IStyleAttributeService,
|
||||
ITexture2D,
|
||||
lazyInject,
|
||||
TYPES,
|
||||
} from '@antv/l7-core';
|
||||
import { generateColorRamp, IColorRamp } from '@antv/l7-utils';
|
||||
import BaseModel from '../../core/BaseModel';
|
||||
import { RasterImageTriangulation } from '../../core/triangulation';
|
||||
import ImageFrag from '../shaders/image_frag.glsl';
|
||||
import ImageVert from '../shaders/image_vert.glsl';
|
||||
|
||||
interface IImageLayerStyleOptions {
|
||||
opacity: number;
|
||||
}
|
||||
export default class ImageModel extends BaseModel {
|
||||
protected texture: ITexture2D;
|
||||
public getUninforms(): IModelUniform {
|
||||
const { opacity } = this.layer.getLayerConfig() as IImageLayerStyleOptions;
|
||||
return {
|
||||
u_opacity: opacity || 1,
|
||||
u_texture: this.texture,
|
||||
};
|
||||
}
|
||||
public buildModels() {
|
||||
const source = this.layer.getSource();
|
||||
const { createTexture2D } = this.rendererService;
|
||||
this.texture = createTexture2D({
|
||||
height: 0,
|
||||
width: 0,
|
||||
});
|
||||
source.data.images.then((imageData: HTMLImageElement[]) => {
|
||||
this.texture = createTexture2D({
|
||||
data: imageData[0],
|
||||
width: imageData[0].width,
|
||||
height: imageData[0].height,
|
||||
});
|
||||
this.layerService.renderLayers();
|
||||
});
|
||||
return [
|
||||
this.layer.buildLayerModel({
|
||||
moduleName: 'RasterImage',
|
||||
vertexShader: ImageVert,
|
||||
fragmentShader: ImageFrag,
|
||||
triangulation: RasterImageTriangulation,
|
||||
primitive: gl.TRIANGLES,
|
||||
depth: { enable: false },
|
||||
blend: this.getBlend(),
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
protected getConfigSchema() {
|
||||
return {
|
||||
properties: {
|
||||
opacity: {
|
||||
type: 'number',
|
||||
minimum: 0,
|
||||
maximum: 1,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
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[],
|
||||
attributeIdx: number,
|
||||
) => {
|
||||
return [vertex[3], vertex[4]];
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
import ImageModel from './image';
|
||||
export type ImageModelType = 'image';
|
||||
|
||||
const ImageModels: { [key in ImageModelType]: any } = {
|
||||
image: ImageModel,
|
||||
};
|
||||
|
||||
export default ImageModels;
|
|
@ -3,11 +3,11 @@ import CityBuildingLayer from './citybuliding/building';
|
|||
import BaseLayer from './core/BaseLayer';
|
||||
import './glsl.d';
|
||||
import HeatmapLayer from './heatmap';
|
||||
import ImageLayer from './image';
|
||||
import LineLayer from './line/index';
|
||||
import PointLayer from './point';
|
||||
import PolygonLayer from './polygon';
|
||||
import ImageLayer from './raster/image';
|
||||
import RasterLayer from './raster/raster';
|
||||
import RasterLayer from './raster';
|
||||
|
||||
import ConfigSchemaValidationPlugin from './plugins/ConfigSchemaValidationPlugin';
|
||||
import DataMappingPlugin from './plugins/DataMappingPlugin';
|
||||
|
|
|
@ -1,106 +1,106 @@
|
|||
import {
|
||||
AttributeType,
|
||||
gl,
|
||||
IEncodeFeature,
|
||||
ILayer,
|
||||
ITexture2D,
|
||||
} from '@antv/l7-core';
|
||||
import BaseLayer from '../core/BaseLayer';
|
||||
import { RasterImageTriangulation } from '../core/triangulation';
|
||||
import rasterImageFrag from './shaders/image_frag.glsl';
|
||||
import rasterImageVert from './shaders/image_vert.glsl';
|
||||
interface IRaterLayerStyleOptions {
|
||||
opacity: number;
|
||||
}
|
||||
// import {
|
||||
// AttributeType,
|
||||
// gl,
|
||||
// IEncodeFeature,
|
||||
// ILayer,
|
||||
// ITexture2D,
|
||||
// } from '@antv/l7-core';
|
||||
// import BaseLayer from '../core/BaseLayer';
|
||||
// import { RasterImageTriangulation } from '../core/triangulation';
|
||||
// import rasterImageFrag from './shaders/image_frag.glsl';
|
||||
// import rasterImageVert from './shaders/image_vert.glsl';
|
||||
// interface IRaterLayerStyleOptions {
|
||||
// opacity: number;
|
||||
// }
|
||||
|
||||
export default class ImageLayer extends BaseLayer<IRaterLayerStyleOptions> {
|
||||
public type: string = 'ImageLayer';
|
||||
protected texture: ITexture2D;
|
||||
// export default class ImageLayer extends BaseLayer<IRaterLayerStyleOptions> {
|
||||
// public type: string = 'ImageLayer';
|
||||
// protected texture: ITexture2D;
|
||||
|
||||
protected getConfigSchema() {
|
||||
return {
|
||||
properties: {
|
||||
opacity: {
|
||||
type: 'number',
|
||||
minimum: 0,
|
||||
maximum: 1,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
// protected getConfigSchema() {
|
||||
// return {
|
||||
// properties: {
|
||||
// opacity: {
|
||||
// type: 'number',
|
||||
// minimum: 0,
|
||||
// maximum: 1,
|
||||
// },
|
||||
// },
|
||||
// };
|
||||
// }
|
||||
|
||||
protected renderModels() {
|
||||
const { opacity } = this.getLayerConfig();
|
||||
if (this.texture) {
|
||||
this.models.forEach((model) =>
|
||||
model.draw({
|
||||
uniforms: {
|
||||
u_opacity: opacity || 1,
|
||||
u_texture: this.texture,
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
// protected renderModels() {
|
||||
// const { opacity } = this.getLayerConfig();
|
||||
// if (this.texture) {
|
||||
// this.models.forEach((model) =>
|
||||
// model.draw({
|
||||
// uniforms: {
|
||||
// u_opacity: opacity || 1,
|
||||
// u_texture: this.texture,
|
||||
// },
|
||||
// }),
|
||||
// );
|
||||
// }
|
||||
|
||||
return this;
|
||||
}
|
||||
// return this;
|
||||
// }
|
||||
|
||||
protected buildModels() {
|
||||
this.registerBuiltinAttributes();
|
||||
const source = this.getSource();
|
||||
const { createTexture2D } = this.rendererService;
|
||||
source.data.images.then((imageData: HTMLImageElement[]) => {
|
||||
this.texture = createTexture2D({
|
||||
data: imageData[0],
|
||||
width: imageData[0].width,
|
||||
height: imageData[0].height,
|
||||
});
|
||||
this.renderModels();
|
||||
});
|
||||
this.models = [
|
||||
this.buildLayerModel({
|
||||
moduleName: 'RasterImage',
|
||||
vertexShader: rasterImageVert,
|
||||
fragmentShader: rasterImageFrag,
|
||||
triangulation: RasterImageTriangulation,
|
||||
primitive: gl.TRIANGLES,
|
||||
depth: { enable: false },
|
||||
blend: {
|
||||
enable: true,
|
||||
func: {
|
||||
srcRGB: gl.SRC_ALPHA,
|
||||
srcAlpha: 1,
|
||||
dstRGB: gl.ONE_MINUS_SRC_ALPHA,
|
||||
dstAlpha: 1,
|
||||
},
|
||||
},
|
||||
}),
|
||||
];
|
||||
}
|
||||
// protected buildModels() {
|
||||
// this.registerBuiltinAttributes();
|
||||
// const source = this.getSource();
|
||||
// const { createTexture2D } = this.rendererService;
|
||||
// source.data.images.then((imageData: HTMLImageElement[]) => {
|
||||
// this.texture = createTexture2D({
|
||||
// data: imageData[0],
|
||||
// width: imageData[0].width,
|
||||
// height: imageData[0].height,
|
||||
// });
|
||||
// this.renderModels();
|
||||
// });
|
||||
// this.models = [
|
||||
// this.buildLayerModel({
|
||||
// moduleName: 'RasterImage',
|
||||
// vertexShader: rasterImageVert,
|
||||
// fragmentShader: rasterImageFrag,
|
||||
// triangulation: RasterImageTriangulation,
|
||||
// primitive: gl.TRIANGLES,
|
||||
// depth: { enable: false },
|
||||
// blend: {
|
||||
// enable: true,
|
||||
// func: {
|
||||
// srcRGB: gl.SRC_ALPHA,
|
||||
// srcAlpha: 1,
|
||||
// dstRGB: gl.ONE_MINUS_SRC_ALPHA,
|
||||
// dstAlpha: 1,
|
||||
// },
|
||||
// },
|
||||
// }),
|
||||
// ];
|
||||
// }
|
||||
|
||||
private 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[],
|
||||
attributeIdx: number,
|
||||
) => {
|
||||
return [vertex[3], vertex[4]];
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
// private 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[],
|
||||
// attributeIdx: number,
|
||||
// ) => {
|
||||
// return [vertex[3], vertex[4]];
|
||||
// },
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
import { IColorRamp } from '@antv/l7-utils';
|
||||
import BaseLayer from '../core/BaseLayer';
|
||||
import RasterModels, { RasterModelType } from './models/index';
|
||||
interface IRasterLayerStyleOptions {
|
||||
opacity: number;
|
||||
domain: [number, number];
|
||||
noDataValue: number;
|
||||
clampLow: boolean;
|
||||
clampHigh: boolean;
|
||||
rampColors: IColorRamp;
|
||||
}
|
||||
export default class RaterLayer extends BaseLayer<IRasterLayerStyleOptions> {
|
||||
public type: string = 'RasterLayer';
|
||||
protected getConfigSchema() {
|
||||
return {
|
||||
properties: {
|
||||
opacity: {
|
||||
type: 'number',
|
||||
minimum: 0,
|
||||
maximum: 1,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
protected getDefaultConfig() {
|
||||
const type = this.getModelType();
|
||||
const defaultConfig = {
|
||||
raster: {},
|
||||
raster3d: {},
|
||||
};
|
||||
return defaultConfig[type];
|
||||
}
|
||||
protected buildModels() {
|
||||
const modelType = this.getModelType();
|
||||
this.layerModel = new RasterModels[modelType](this);
|
||||
this.models = this.layerModel.buildModels();
|
||||
}
|
||||
|
||||
protected getModelType(): RasterModelType {
|
||||
return 'raster';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import RasterModel from './raster';
|
||||
export type RasterModelType = 'raster' | 'raster3d';
|
||||
|
||||
const RasterModels: { [key in RasterModelType]: any } = {
|
||||
raster: RasterModel,
|
||||
raster3d: RasterModel,
|
||||
};
|
||||
|
||||
export default RasterModels;
|
|
@ -0,0 +1,113 @@
|
|||
import {
|
||||
AttributeType,
|
||||
gl,
|
||||
IEncodeFeature,
|
||||
ILayer,
|
||||
ILayerPlugin,
|
||||
ILogService,
|
||||
IModel,
|
||||
IModelUniform,
|
||||
IRasterParserDataItem,
|
||||
IStyleAttributeService,
|
||||
ITexture2D,
|
||||
lazyInject,
|
||||
TYPES,
|
||||
} from '@antv/l7-core';
|
||||
import { generateColorRamp, IColorRamp } from '@antv/l7-utils';
|
||||
import BaseModel from '../../core/BaseModel';
|
||||
import { RasterImageTriangulation } from '../../core/triangulation';
|
||||
import rasterFrag from '../shaders/raster_2d_frag.glsl';
|
||||
import rasterVert from '../shaders/raster_2d_vert.glsl';
|
||||
|
||||
interface IRasterLayerStyleOptions {
|
||||
opacity: number;
|
||||
domain: [number, number];
|
||||
noDataValue: number;
|
||||
clampLow: boolean;
|
||||
clampHigh: boolean;
|
||||
rampColors: IColorRamp;
|
||||
}
|
||||
export default class RasterModel extends BaseModel {
|
||||
protected texture: ITexture2D;
|
||||
protected colorTexture: ITexture2D;
|
||||
public getUninforms() {
|
||||
const {
|
||||
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: this.colorTexture,
|
||||
};
|
||||
}
|
||||
|
||||
public buildModels() {
|
||||
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,
|
||||
// aniso: 4,
|
||||
});
|
||||
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: true,
|
||||
});
|
||||
return [
|
||||
this.layer.buildLayerModel({
|
||||
moduleName: 'RasterImageData',
|
||||
vertexShader: rasterVert,
|
||||
fragmentShader: rasterFrag,
|
||||
triangulation: RasterImageTriangulation,
|
||||
primitive: gl.TRIANGLES,
|
||||
depth: { enable: false },
|
||||
blend: this.getBlend(),
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
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[],
|
||||
attributeIdx: number,
|
||||
) => {
|
||||
return [vertex[3], vertex[4]];
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
|
@ -5,12 +5,14 @@ import {
|
|||
ILayer,
|
||||
ILayerPlugin,
|
||||
ILogService,
|
||||
IModelUniform,
|
||||
IRasterParserDataItem,
|
||||
IStyleAttributeService,
|
||||
ITexture2D,
|
||||
lazyInject,
|
||||
TYPES,
|
||||
} from '@antv/l7-core';
|
||||
|
||||
import { generateColorRamp, IColorRamp } from '@antv/l7-utils';
|
||||
import BaseLayer from '../core/BaseLayer';
|
||||
import { RasterTriangulation } from './buffers/triangulation';
|
||||
|
@ -30,6 +32,9 @@ export default class RasterLayer extends BaseLayer<IRasterLayerStyleOptions> {
|
|||
protected texture: ITexture2D;
|
||||
protected colorTexture: ITexture2D;
|
||||
|
||||
public getAnimateUniforms(): IModelUniform {
|
||||
return {};
|
||||
}
|
||||
protected getConfigSchema() {
|
||||
return {
|
||||
properties: {
|
||||
|
|
|
@ -4,15 +4,26 @@ uniform sampler2D u_texture;
|
|||
uniform sampler2D u_colorTexture;
|
||||
uniform float u_min;
|
||||
uniform float u_max;
|
||||
uniform vec2 u_domain;
|
||||
uniform float u_noDataValue;
|
||||
uniform bool u_clampLow: true;
|
||||
uniform bool u_clampHigh: true;
|
||||
varying vec2 v_texCoord;
|
||||
|
||||
void main() {
|
||||
|
||||
float value = texture2D(u_texture,vec2(v_texCoord.x,v_texCoord.y)).a;
|
||||
value = clamp(value,u_min,u_max);
|
||||
float value1 = (value - u_min) / (u_max -u_min);
|
||||
float value = texture2D(u_texture,vec2(v_texCoord.x,v_texCoord.y)).r;
|
||||
if (value == u_noDataValue)
|
||||
gl_FragColor = vec4(0.0, 0, 0, 0.0);
|
||||
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]);
|
||||
vec2 ramp_pos = vec2(
|
||||
fract(16.0 * (1.0 - value1)),
|
||||
floor(16.0 * (1.0 - value1)) / 16.0);
|
||||
gl_FragColor = texture2D(u_colorTexture,ramp_pos);;
|
||||
fract(16.0 * (1.0 - normalisedValue)),
|
||||
floor(16.0 * (1.0 - normalisedValue)) / 16.0);
|
||||
gl_FragColor = texture2D(u_colorTexture, ramp_pos);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -229,6 +229,7 @@ export default class AMapService
|
|||
maxZoom = 18,
|
||||
token = AMAP_API_KEY,
|
||||
mapInstance,
|
||||
plugin = [],
|
||||
...rest
|
||||
} = this.config;
|
||||
// 高德地图创建独立的container;
|
||||
|
@ -273,7 +274,9 @@ export default class AMapService
|
|||
if (token === AMAP_API_KEY) {
|
||||
this.logger.warn(this.configService.getSceneWarninfo('MapToken'));
|
||||
}
|
||||
const url: string = `https://webapi.amap.com/maps?v=${AMAP_VERSION}&key=${token}&plugin=Map3D&callback=initAMap`;
|
||||
const url: string = `https://webapi.amap.com/maps?v=${AMAP_VERSION}&key=${token}&plugin=Map3D${plugin.join(
|
||||
',',
|
||||
)}&callback=initAMap`;
|
||||
const $jsapi = document.createElement('script');
|
||||
$jsapi.id = AMAP_SCRIPT_ID;
|
||||
$jsapi.charset = 'utf-8';
|
||||
|
|
|
@ -52,7 +52,6 @@ export default class ReglRendererService implements IRendererService {
|
|||
// TODO: use extensions
|
||||
extensions: [
|
||||
'OES_element_index_uint',
|
||||
// 'EXT_shader_texture_lod', // IBL 兼容性问题
|
||||
'EXT_blend_minmax',
|
||||
'OES_standard_derivatives', // wireframe
|
||||
// 'OES_texture_float', // shadow map 兼容性问题
|
||||
|
@ -60,7 +59,7 @@ export default class ReglRendererService implements IRendererService {
|
|||
'angle_instanced_arrays',
|
||||
'EXT_texture_filter_anisotropic', // VSM shadow map
|
||||
],
|
||||
// optionalExtensions: ['oes_texture_float_linear'],
|
||||
optionalExtensions: ['oes_texture_float_linear', 'OES_texture_float'],
|
||||
profile: true,
|
||||
onDone: (err: Error | null, r?: regl.Regl | undefined): void => {
|
||||
if (err || !r) {
|
||||
|
|
|
@ -15,7 +15,7 @@ export default class ImageLayerDemo extends React.Component {
|
|||
map: new Mapbox({
|
||||
center: [121.268, 30.3628],
|
||||
pitch: 0,
|
||||
style: 'mapbox://styles/mapbox/streets-v9',
|
||||
style: 'dark',
|
||||
zoom: 10,
|
||||
}),
|
||||
});
|
||||
|
@ -30,7 +30,6 @@ export default class ImageLayerDemo extends React.Component {
|
|||
},
|
||||
);
|
||||
scene.addLayer(layer);
|
||||
scene.render();
|
||||
this.scene = scene;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,19 @@
|
|||
import { RasterLayer, Scene } from '@antv/l7';
|
||||
import { Mapbox } from '@antv/l7-maps';
|
||||
import * as dat from 'dat.gui';
|
||||
// @ts-ignore
|
||||
import * as GeoTIFF from 'geotiff/dist/geotiff.bundle.js';
|
||||
import * as React from 'react';
|
||||
|
||||
export default class ImageLayerDemo extends React.Component {
|
||||
private scene: Scene;
|
||||
private gui: dat.GUI;
|
||||
|
||||
public componentWillUnmount() {
|
||||
this.scene.destroy();
|
||||
if (this.gui) {
|
||||
this.gui.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
public async componentDidMount() {
|
||||
|
@ -17,7 +22,7 @@ export default class ImageLayerDemo extends React.Component {
|
|||
map: new Mapbox({
|
||||
center: [121.268, 30.3628],
|
||||
pitch: 0,
|
||||
style: 'mapbox://styles/mapbox/streets-v9',
|
||||
style: 'dark',
|
||||
zoom: 2,
|
||||
}),
|
||||
});
|
||||
|
@ -36,6 +41,8 @@ export default class ImageLayerDemo extends React.Component {
|
|||
})
|
||||
.style({
|
||||
opacity: 0.8,
|
||||
domain: [100, 4000],
|
||||
clampLow: true,
|
||||
rampColors: {
|
||||
colors: [
|
||||
'#002466',
|
||||
|
@ -53,8 +60,45 @@ export default class ImageLayerDemo extends React.Component {
|
|||
},
|
||||
});
|
||||
scene.addLayer(layer);
|
||||
scene.render();
|
||||
|
||||
this.scene = scene;
|
||||
/*** 运行时修改样式属性 ***/
|
||||
const gui = new dat.GUI();
|
||||
this.gui = gui;
|
||||
const styleOptions = {
|
||||
clampLow: true,
|
||||
clampHigh: true,
|
||||
noDataValue: -9999999,
|
||||
min: 100,
|
||||
max: 4000,
|
||||
};
|
||||
const rasterFolder = gui.addFolder('栅格可视化');
|
||||
rasterFolder.add(styleOptions, 'clampLow').onChange((clampLow: boolean) => {
|
||||
layer.style({
|
||||
clampLow,
|
||||
});
|
||||
scene.render();
|
||||
});
|
||||
rasterFolder
|
||||
.add(styleOptions, 'clampHigh')
|
||||
.onChange((clampHigh: boolean) => {
|
||||
layer.style({
|
||||
clampHigh,
|
||||
});
|
||||
scene.render();
|
||||
});
|
||||
rasterFolder.add(styleOptions, 'min', 0, 9000).onChange((min: number) => {
|
||||
layer.style({
|
||||
domain: [min, styleOptions.max],
|
||||
});
|
||||
scene.render();
|
||||
});
|
||||
rasterFolder.add(styleOptions, 'max', 0, 9000).onChange((max: number) => {
|
||||
layer.style({
|
||||
domain: [styleOptions.min, max],
|
||||
});
|
||||
scene.render();
|
||||
});
|
||||
}
|
||||
|
||||
public render() {
|
||||
|
|
Loading…
Reference in New Issue