feat: 新增pointLayer 对 style 参数 opacity 对数据映射支持

This commit is contained in:
2912401452 2021-06-15 18:19:51 +08:00
parent b431ad72c3
commit bbe559f990
12 changed files with 278 additions and 58 deletions

View File

@ -28,6 +28,11 @@ import {
StyleAttributeOption,
Triangulation,
} from './IStyleAttributeService';
import {
IStyleAttributeUpdateOptions,
StyleAttributeField,
} from '@antv/l7-core';
export enum BlendType {
normal = 'normal',
additive = 'additive',
@ -126,10 +131,17 @@ export interface ILayer {
options: ILayerModelInitializationOptions &
Partial<IModelInitializationOptions>,
): IModel;
updateStyleAttribute(
type: string,
field: StyleAttributeField,
values?: StyleAttributeOption,
updateOptions?: Partial<IStyleAttributeUpdateOptions>,
): void;
init(): ILayer;
scale(field: string | number | IScaleOptions, cfg?: IScale): ILayer;
size(field: StyleAttrField, value?: StyleAttributeOption): ILayer;
color(field: StyleAttrField, value?: StyleAttributeOption): ILayer;
// opacity(field: StyleAttrField, value?: StyleAttributeOption): ILayer;
texture(field: StyleAttrField, value?: StyleAttributeOption): ILayer;
shape(field: StyleAttrField, value?: StyleAttributeOption): ILayer;
label(field: StyleAttrField, value?: StyleAttributeOption): ILayer;

View File

@ -940,6 +940,40 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
return this;
}
public updateStyleAttribute(
type: string,
field: StyleAttributeField,
values?: StyleAttributeOption,
updateOptions?: Partial<IStyleAttributeUpdateOptions>,
) {
if (!this.inited) {
this.pendingStyleAttributes.push({
attributeName: type,
attributeField: field,
attributeValues: values,
updateOptions,
});
} else {
this.styleAttributeService.updateStyleAttribute(
type,
{
// @ts-ignore
scale: {
field,
...this.splitValuesAndCallbackInAttribute(
// @ts-ignore
values,
// @ts-ignore
this.getLayerConfig()[field],
),
},
},
// @ts-ignore
updateOptions,
);
}
}
protected getConfigSchema() {
throw new Error('Method not implemented.');
}
@ -978,38 +1012,4 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
callback: isFunction(valuesOrCallback) ? valuesOrCallback : undefined,
};
}
private updateStyleAttribute(
type: string,
field: StyleAttributeField,
values?: StyleAttributeOption,
updateOptions?: Partial<IStyleAttributeUpdateOptions>,
) {
if (!this.inited) {
this.pendingStyleAttributes.push({
attributeName: type,
attributeField: field,
attributeValues: values,
updateOptions,
});
} else {
this.styleAttributeService.updateStyleAttribute(
type,
{
// @ts-ignore
scale: {
field,
...this.splitValuesAndCallbackInAttribute(
// @ts-ignore
values,
// @ts-ignore
this.getLayerConfig()[field],
),
},
},
// @ts-ignore
updateOptions,
);
}
}
}

View File

@ -109,7 +109,7 @@ export default class DataMappingPlugin implements ILayerPlugin {
data: IParseDataItem[],
predata?: IEncodeFeature[],
): IEncodeFeature[] {
// console.log('data', data[0])
// console.log('data', data)
const mappedData = data.map((record: IParseDataItem, i) => {
const preRecord = predata ? predata[i] : {};
const encodeRecord: IEncodeFeature = {
@ -117,12 +117,14 @@ export default class DataMappingPlugin implements ILayerPlugin {
coordinates: record.coordinates,
...preRecord,
};
// console.log('encodeRecord', encodeRecord)
// console.log('attributes', attributes)
attributes
.filter((attribute) => attribute.scale !== undefined)
.forEach((attribute: IStyleAttribute) => {
// console.log('attribute', attribute)
// console.log('record', record)
let values = this.applyAttributeMapping(attribute, record);
// console.log('values', values)
attribute.needRemapping = false;
// TODO: 支持每个属性配置 postprocess
@ -145,7 +147,7 @@ export default class DataMappingPlugin implements ILayerPlugin {
});
return encodeRecord;
}) as IEncodeFeature[];
// console.log('mappedData', mappedData[0])
// console.log('mappedData', mappedData)
// 根据地图的类型判断是否需要对点位数据进行处理, 若是高德2.0则需要对坐标进行相对偏移
if (mappedData.length > 0 && this.mapService.version === 'GAODE2.x') {
@ -194,6 +196,9 @@ export default class DataMappingPlugin implements ILayerPlugin {
params.push(record[field]);
}
});
// console.log('params', params)
// console.log('attribute', attribute)
// console.log('mapping',attribute.mapping ? attribute.mapping(params) : [])
return attribute.mapping ? attribute.mapping(params) : [];
}
}

View File

@ -9,6 +9,7 @@ import {
TYPES,
} from '@antv/l7-core';
import { inject, injectable } from 'inversify';
import { isNumber } from 'lodash';
/**
* Layer indices attribute
@ -90,5 +91,25 @@ export default class RegisterStyleAttributePlugin implements ILayerPlugin {
},
},
});
styleAttributeService.registerStyleAttribute({
name: 'opacity',
type: AttributeType.Attribute,
descriptor: {
name: 'a_Opacity',
buffer: {
// give the WebGL driver a hint that this buffer may change
usage: gl.DYNAMIC_DRAW,
data: [],
type: gl.FLOAT,
},
size: 1,
update: (feature: IEncodeFeature, featureIdx: number) => {
const { opacity } = feature;
// console.log('feature', feature)
// console.log('opacity', opacity)
return !isNumber(opacity) ? [1.0] : [opacity];
},
},
});
}
}

View File

@ -14,24 +14,37 @@ import BaseModel from '../../core/BaseModel';
import { PointFillTriangulation } from '../../core/triangulation';
import pointFillFrag from '../shaders/fill_frag.glsl';
import pointFillVert from '../shaders/fill_vert.glsl';
import { isNumber } from 'lodash';
import { handleStyleOpacity } from '../../utils/dataMappingStyle';
interface IPointLayerStyleOptions {
opacity: number;
opacity: any;
strokeWidth: number;
stroke: string;
strokeOpacity: number;
offsets: [number, number];
}
// 用于判断 opacity 的值是否发生该改变
let curretnOpacity: any = '';
export default class FillModel extends BaseModel {
public getUninforms(): IModelUniform {
const {
opacity = 1,
opacity = 1.0,
stroke = 'rgb(0,0,0,0)',
strokeWidth = 1,
strokeOpacity = 1,
offsets = [0, 0],
} = this.layer.getLayerConfig() as IPointLayerStyleOptions;
// style 中 opacity 属性的处理(数据映射)
if (curretnOpacity !== JSON.stringify(opacity)) {
handleStyleOpacity(this.layer, opacity);
curretnOpacity = JSON.stringify(opacity);
}
return {
u_opacity: opacity,
u_opacity: isNumber(opacity) ? opacity : 1.0,
u_stroke_width: strokeWidth,
u_stroke_color: rgb2arr(stroke),
u_stroke_opacity: strokeOpacity,
@ -58,6 +71,8 @@ export default class FillModel extends BaseModel {
);
}
public initModels(): IModel[] {
this.getUninforms();
return this.buildModels();
}
public buildModels(): IModel[] {
@ -76,6 +91,7 @@ export default class FillModel extends BaseModel {
return [option.enable ? 0 : 1.0, option.speed || 1, option.rings || 3, 0];
}
protected registerBuiltinAttributes() {
const { opacity } = this.layer.getLayerConfig() as IPointLayerStyleOptions;
this.styleAttributeService.registerStyleAttribute({
name: 'extrude',
type: AttributeType.Attribute,

View File

@ -8,6 +8,7 @@ uniform float u_stroke_opacity : 1;
varying vec4 v_data;
varying vec4 v_color;
varying float v_opacity;
varying float v_radius;
uniform float u_time;
uniform vec4 u_aimate: [ 0, 2., 1.0, 0.2 ];
@ -67,7 +68,14 @@ void main() {
// gl_FragColor = v_color * color_t;
// gl_FragColor = mix(vec4(v_color.rgb, v_color.a * u_opacity), strokeColor * u_stroke_opacity, color_t);
gl_FragColor = mix(vec4(v_color.rgb, v_color.a * u_opacity), strokeColor * u_stroke_opacity, color_t);
// gl_FragColor = mix(vec4(v_color.rgb, v_color.a * v_opacity), strokeColor * u_stroke_opacity, color_t);
if(v_opacity < 0.0) { // style 中的 opacity 为 number
gl_FragColor = mix(vec4(v_color.rgb, v_color.a * u_opacity), strokeColor * u_stroke_opacity, color_t);
} else { // style 中的 opacity 为 string function
gl_FragColor = mix(vec4(v_color.rgb, v_color.a * v_opacity), strokeColor * u_stroke_opacity, color_t);
}
gl_FragColor.a = gl_FragColor.a * opacity_t;
if(u_aimate.x == Animate) {
float d = length(v_data.xy);

View File

@ -2,6 +2,7 @@ attribute vec4 a_Color;
attribute vec3 a_Position;
attribute vec2 a_Extrude;
attribute float a_Size;
attribute float a_Opacity;
attribute float a_Shape;
uniform mat4 u_ModelMatrix;
uniform mat4 u_Mvp;
@ -12,6 +13,7 @@ uniform vec2 u_offsets;
varying vec4 v_data;
varying vec4 v_color;
varying float v_radius;
varying float v_opacity;
#pragma include "projection"
#pragma include "picking"
@ -23,6 +25,7 @@ void main() {
// unpack color(vec2)
v_color = a_Color;
v_opacity = a_Opacity;
// radius(16-bit)
v_radius = newSize;

View File

@ -0,0 +1,59 @@
import {
ILayer,
IStyleAttributeUpdateOptions,
StyleAttributeField,
StyleAttributeOption,
} from '@antv/l7-core';
import { isArray, isFunction, isNumber, isString } from 'lodash';
/**
* style
*/
/**
* style 使 opacity
* @param field
* @param values
* @param updateOptions
*/
function registerOpacityAttribute(
layer: ILayer,
field: StyleAttributeField,
values?: StyleAttributeOption,
updateOptions?: Partial<IStyleAttributeUpdateOptions>,
) {
layer.updateStyleAttribute('opacity', field, values, updateOptions);
}
/**
* opacity
*/
function handleStyleOpacity(layer: ILayer, opacity: any) {
if (isString(opacity)) {
// opacity = 'string'
registerOpacityAttribute(layer, opacity, (value: any) => {
return value;
});
} else if (isNumber(opacity)) {
// opacity = 0.4 -> opacity 传入数字、u_Opacity 生效、v_Opacity 不生效
registerOpacityAttribute(layer, [-1], undefined);
} else if (isArray(opacity) && opacity.length === 2) {
if (isString(opacity[0]) && isFunction(opacity[1])) {
// opacity = ['string', callback]
registerOpacityAttribute(layer, opacity[0], opacity[1]);
} else if (
isString(opacity[0]) &&
isArray(opacity[1]) &&
isNumber(opacity[1][0]) &&
isNumber(opacity[1][1])
) {
// opacity = ['string', [start: number, end: nuber]]
registerOpacityAttribute(layer, opacity[0], opacity[1]);
} else {
registerOpacityAttribute(layer, [1.0], undefined);
}
} else {
registerOpacityAttribute(layer, [1.0], undefined);
}
}
export { handleStyleOpacity };

View File

@ -1,6 +1,6 @@
//@ts-ignore
import { PointLayer, Scene } from '@antv/l7';
import { GaodeMap } from '@antv/l7-maps';
import { GaodeMapV2 } from '@antv/l7-maps';
import * as React from 'react';
export default class Amap2demo extends React.Component {
// @ts-ignore
@ -13,7 +13,7 @@ export default class Amap2demo extends React.Component {
public async componentDidMount() {
const scene = new Scene({
id: 'map',
map: new GaodeMap({
map: new GaodeMapV2({
center: [121.107846, 30.267069],
pitch: 0,
style: 'normal',
@ -25,28 +25,27 @@ export default class Amap2demo extends React.Component {
{
lng: 121.107846,
lat: 30.267069,
opacity2: 0.2,
},
{
lng: 121.107,
lat: 30.267069,
opacity2: 0.4,
},
{
lng: 120.107846,
lat: 30.267069,
},
{
lng: 38.54,
lat: 77.02,
lng: 121.107846,
lat: 30.26718,
opacity2: 0.6,
},
// {
// lng: 38.54,
// lat: 77.02,
// opacity: 0.5
// },
];
this.scene = scene;
scene.on('loaded', () => {
// console.log('event test');
// @ts-ignore
// console.log(scene.map.getProjection().project);
// @ts-ignore
// console.log(scene.map.customCoords.lngLatToCoord);
const layer = new PointLayer()
.source(originData, {
parser: {
@ -55,15 +54,21 @@ export default class Amap2demo extends React.Component {
y: 'lat',
},
})
// .shape('circle')
.shape('circle')
// .shape('normal')
.shape('fill')
// .shape('fill')
.color('rgba(255, 0, 0, 0.9)')
.size(10)
.style({
stroke: '#fff',
storkeWidth: 2,
offsets: [100, 100],
// offsets: [100, 100],
// opacity: 'opacity2'
// opacity: 0.2
// opacity: ['opacity2', (d: any) => {
// return d
// }]
opacity: ['opacity2', [0.2, 0.6]],
})
.active(true);
scene.addLayer(layer);

View File

@ -58,7 +58,7 @@ export default class GaodeMapComponent extends React.Component {
.style({
stroke: '#fff',
storkeWidth: 2,
offsets: [100, 100],
// offsets: [100, 100],
});
scene.addLayer(layer);
scene.render();

View File

@ -0,0 +1,78 @@
// @ts-ignore
import { LineLayer, Scene } from '@antv/l7';
import { GaodeMap } from '@antv/l7-maps';
import * as React from 'react';
export default class Line extends React.Component {
// @ts-ignore
private scene: Scene;
public componentWillUnmount() {
this.scene.destroy();
}
public async componentDidMount() {
const scene = new Scene({
id: 'map',
map: new GaodeMap({
center: [120.19382669582967, 30.258134],
pitch: 0,
zoom: 8,
viewMode: '3D',
}),
});
this.scene = scene;
scene.on('loaded', () => {
// @ts-ignore
const layer = new LineLayer({})
.source({
type: 'FeatureCollection',
name: 'dl2',
crs: {
type: 'name',
properties: {
name: 'urn:ogc:def:crs:OGC:1.3:CRS84',
},
},
features: [
{
type: 'Feature',
properties: {},
geometry: {
type: 'MultiLineString',
coordinates: [
[
[120, 32],
[121, 32],
[121, 30],
],
],
},
},
],
})
.size(5)
.shape('line')
.color('#25d8b7');
scene.addLayer(layer);
});
}
public render() {
return (
<>
<div
id="map"
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
}}
/>
</>
);
}
}

View File

@ -0,0 +1,13 @@
import { storiesOf } from '@storybook/react';
import * as React from 'react';
import Line from './components/line';
// @ts-ignore
storiesOf('functions', module)
.add('line', () => <Line />)