fix(layers): heatmap 3d effect

This commit is contained in:
thinkinggis 2019-11-11 13:52:00 +08:00
parent 0e000f5715
commit 4c5c1a9d49
17 changed files with 166 additions and 92 deletions

View File

@ -1,55 +0,0 @@
import { Scene } from '@l7/scene';
import { HeatMapGridLayer } from '@l7/layers';
const scene = new Scene({
id: 'map',
style: 'light',
pitch: 0,
center: [116.49434030056, 39.868073421167621],
type: 'amap',
zoom: 16,
});
fetch('https://gw.alipayobjects.com/os/basement_prod/c3f8bda2-081b-449d-aa9f-9413b779205b.json')
.then((res) => res.json())
.then((data) => {
const layer =
new HeatMapGridLayer({
})
.source(data, {
parser: {
type: 'json',
x: 'lng',
y: 'lat',
},
transforms: [
{
type: 'grid',
size: 50,
field: 'count',
method: 'sum',
},
],
})
.size('sum', (value) => {
return value;
})
.shape('square')
.style({
coverage: 0.8,
angle: 0,
opacity: 0.6,
})
.color('count', [
'#002466',
'#105CB3',
'#2894E0',
'#CFF6FF',
'#FFF5B8',
'#FFAB5C',
'#F27049',
'#730D1C',
]);
scene.addLayer(layer);
});

View File

@ -5,7 +5,7 @@
},
"demos": [
{
"filename": "grid.js",
"filename": "world3d.js",
"title": "网格热力图"
},
{

View File

@ -16,7 +16,9 @@ fetch('https://gw.alipayobjects.com/os/basement_prod/337ddbb7-aa3f-4679-ab60-d64
new HeatMapLayer({
})
.source(data)
.size('capacity', [0, 1]) // weight映射通道
.size('capacity', [0, 1])
.shape('heatmap')
// weight映射通道
.style({
intensity: 10,
radius: 5,

View File

@ -0,0 +1,41 @@
import { Scene } from '@l7/scene';
import { HeatMapLayer } from '@l7/layers';
const scene = new Scene({
id: 'map',
style: 'dark',
pitch: 0,
center: [116.49434030056, 39.868073421167621],
type: 'mapbox',
zoom: 3,
});
fetch('https://gw.alipayobjects.com/os/basement_prod/337ddbb7-aa3f-4679-ab60-d64359241955.json')
.then((res) => res.json())
.then((data) => {
const layer =
new HeatMapLayer({
})
.source(data)
.size('capacity', [0, 1])
.shape('heatmap3D')
// weight映射通道
.style({
intensity: 10,
radius: 5,
opacity: 1.0,
rampColors: {
colors: [
'#2E8AE6',
'#69D1AB',
'#DAF291',
'#FFD591',
'#FF7A45',
'#CF1D49',
],
positions: [0,0.2, 0.4, 0.6, 0.8, 1.0],
},
});
scene.addLayer(layer);
});

View File

@ -13,6 +13,11 @@
"filename": "world.js",
"title": "气泡图 - 电厂装机量"
},
{
"filename": "text.js",
"title": "文本"
}
]
}

View File

@ -0,0 +1,41 @@
import { Scene } from '@l7/scene';
import { PointLayer, PointImageLayer } from '@l7/layers'
const scene = new Scene({
id: 'map',
pitch: 0,
type: 'amap',
style: 'light',
center: [121.40, 31.258134],
zoom: 15,
minZoom: 10
});
fetch('https://gw.alipayobjects.com/os/basement_prod/893d1d5f-11d9-45f3-8322-ee9140d288ae.json')
.then((res) => res.json())
.then((data) => {
const pointLayer =
new PointLayer({
})
.source(data, {
parser: {
type: 'json',
x: 'longitude',
y: 'latitude'
}
}).shape('circle')
.size('unit_price', [5, 25])
.color('#5B8FF9')
.label('name')
.style({
opacity: 0.3,
strokeWidth: 1,
strokeColor: "#5B8FF9",
})
scene.addLayer(pointLayer);
console.log(pointLayer);
});

View File

@ -34,7 +34,7 @@ fetch('https://gw.alipayobjects.com/os/basement_prod/893d1d5f-11d9-45f3-8322-ee9
}
})
.shape('name', ['00', '01','02'])
.size('unit_price', [30, 100])
.size('unit_price', [5, 15])
scene.addLayer(imageLayer);
});

View File

@ -61,6 +61,7 @@ export interface ILayer {
size(field: StyleAttrField, value?: StyleAttributeOption): ILayer;
color(field: StyleAttrField, value?: StyleAttributeOption): ILayer;
shape(field: StyleAttrField, value?: StyleAttributeOption): ILayer;
label(field: StyleAttrField, value?: StyleAttributeOption): ILayer;
// pattern(field: string, value: StyleAttributeOption): ILayer;
// filter(field: string, value: StyleAttributeOption): ILayer;
// active(option: ActiveOption): ILayer;

View File

@ -130,6 +130,13 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
@lazyInject(TYPES.ILayerService)
protected readonly layerService: ILayerService;
protected enodeOptions: {
[type: string]: {
field: StyleAttributeField;
values?: StyleAttributeOption;
};
} = {};
private encodedData: IEncodeFeature[];
private configSchema: object;
@ -142,12 +149,6 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
>;
private scaleOptions: IScaleOptions = {};
private enodeOptions: {
[type: string]: {
field: string;
};
};
@lazyInject(TYPES.IInteractionService)
private readonly interactionService: IInteractionService;
@ -236,6 +237,10 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
values?: StyleAttributeOption,
updateOptions?: Partial<IStyleAttributeUpdateOptions>,
) {
this.enodeOptions.shape = {
field,
values,
};
this.styleAttributeService.updateStyleAttribute(
'shape',
{
@ -254,6 +259,29 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
);
return this;
}
public label(
field: StyleAttributeField,
values?: StyleAttributeOption,
updateOptions?: Partial<IStyleAttributeUpdateOptions>,
) {
this.styleAttributeService.updateStyleAttribute(
'label',
{
// @ts-ignore
scale: {
field,
...this.splitValuesAndCallbackInAttribute(
// @ts-ignore
values,
null,
),
},
},
// @ts-ignore
updateOptions,
);
return this;
}
public source(data: any, options?: ISourceCFG): ILayer {
this.sourceOption = {

View File

@ -56,7 +56,8 @@ export default class HeatMapLayer extends BaseLayer<IHeatMapLayerStyleOptions> {
protected renderModels() {
const { clear, useFramebuffer } = this.rendererService;
const shapeAttr = this.styleAttributeService.getLayerStyleAttribute('shape');
const shapeType = shapeAttr?.scale?.field || 'heatmap';
useFramebuffer(this.heatmapFramerBuffer, () => {
clear({
color: [0, 0, 0, 0],
@ -66,17 +67,19 @@ export default class HeatMapLayer extends BaseLayer<IHeatMapLayerStyleOptions> {
});
this.drawIntensityMode();
});
this.draw3DHeatMap();
// this.draw3DHeatMap();
shapeType === 'heatmap' ? this.drawColorMode(): this.draw3DHeatMap();
// this.drawIntensityMode();
return this;
}
protected buildModels() {
const shapeAttr = this.styleAttributeService.getLayerStyleAttribute('shape');
const shapeType = shapeAttr?.scale?.field || 'heatmap';
this.registerBuiltinAttributes(this);
this.intensityModel = this.buildHeatMapIntensity();
this.models = [this.intensityModel];
// this.colorModel = this.buildHeatmapColor();
this.colorModel = this.build3dHeatMap();
this.colorModel = shapeType === 'heatmap' ? this.buildHeatmapColor(): this.build3dHeatMap();
this.models.push(this.colorModel);
const { rampColors } = this.getStyleOptions();
const imageData = generateColorRamp(rampColors as IColorRamp);
@ -95,8 +98,8 @@ export default class HeatMapLayer extends BaseLayer<IHeatMapLayerStyleOptions> {
height,
wrapS: gl.CLAMP_TO_EDGE,
wrapT: gl.CLAMP_TO_EDGE,
min: gl.NEAREST,
mag: gl.NEAREST,
min: gl.LINEAR,
mag: gl.LINEAR,
}),
});
@ -175,9 +178,9 @@ export default class HeatMapLayer extends BaseLayer<IHeatMapLayerStyleOptions> {
enable: true,
func: {
srcRGB: gl.ONE,
srcAlpha: 1,
srcAlpha: gl.ONE_MINUS_SRC_ALPHA,
dstRGB: gl.ONE,
dstAlpha: 1,
dstAlpha: gl.ONE_MINUS_SRC_ALPHA,
},
},
});
@ -280,7 +283,7 @@ export default class HeatMapLayer extends BaseLayer<IHeatMapLayerStyleOptions> {
private build3dHeatMap() {
const { getViewportSize } = this.rendererService;
const { width, height } = getViewportSize();
const triangulation = heatMap3DTriangulation(width / 4.0, height / 4.0);
const triangulation = heatMap3DTriangulation(width / 2.0, height / 2.0);
this.shaderModuleService.registerModule('heatmap3dColor', {
vs: heatmap3DVert,
fs: heatmap3DFrag,

View File

@ -5,14 +5,13 @@ varying vec2 v_texCoord;
varying float v_intensity;
void main(){
float intensity = texture2D(u_texture, v_texCoord).r;
float intensity = texture2D(u_texture, v_texCoord).r;
vec2 ramp_pos = vec2(
fract(16.0 * (1.0 - v_intensity)),
floor(16.0 * (1.0 - v_intensity)) / 16.0);
// vec4 color = texture2D(u_colorTexture,vec2(0.5,1.0-intensity));
vec4 color = texture2D(u_colorTexture,ramp_pos);
gl_FragColor = color;
// gl_FragColor.a = color.a * smoothstep(0.0, 0.01, v_intensity) * u_opacity;
// gl_FragColor.a = 0.2;
gl_FragColor.a = color.a * smoothstep(0.1,0.2,intensity)* u_opacity;
}

View File

@ -40,6 +40,6 @@ void main() {
v_intensity = texture2D(u_texture, v_texCoord).r;
fh = toBezier(v_intensity, b).y;
gl_Position = project_common_position_to_clipspace(vec4(position.xy, v_intensity * 50., 1.0));
gl_Position = project_common_position_to_clipspace(vec4(position.xy, fh * 50., 1.0));
}

View File

@ -2,15 +2,16 @@ uniform sampler2D u_texture;
uniform sampler2D u_colorTexture;
uniform float u_opacity;
varying vec2 v_texCoord;
varying float v_intensity;
void main(){
float intensity = texture2D(u_texture, v_texCoord).r;
float intensity = texture2D(u_texture, v_texCoord).r;
vec2 ramp_pos = vec2(
fract(16.0 * (1.0 - intensity)),
floor(16.0 * (1.0 - intensity)) / 16.0);
// vec4 color = texture2D(u_colorTexture,vec2(0.5,1.0-intensity));
vec4 color = texture2D(u_colorTexture,ramp_pos);
gl_FragColor = color;
gl_FragColor.a = color.a * smoothstep(0.,0.01,intensity) * u_opacity;
gl_FragColor.a = color.a * smoothstep(0.,0.05,intensity) * u_opacity;
}

View File

@ -3,8 +3,9 @@ attribute vec3 a_Position;
attribute vec2 a_Uv;
uniform sampler2D u_texture;
varying vec2 v_texCoord;
varying float v_intensity;
void main() {
v_texCoord = a_Uv;
float intensity = texture2D(u_texture, v_texCoord).r;
gl_Position = vec4(a_Position.xy,intensity -0.5, 1.);
v_intensity = texture2D(u_texture, v_texCoord).r;
gl_Position = vec4(a_Position.xy, 0, 1.);
}

View File

@ -93,14 +93,21 @@ export default class FeatureScalePlugin implements ILayerPlugin {
if (scales.some((scale) => scale.type === StyleScaleType.VARIABLE)) {
attributeScale.type = StyleScaleType.VARIABLE;
scales.forEach((scale) => {
// 如果设置了回调干啥这不需要设置让range
if (!attributeScale.callback && attributeScale.values.length > 0) {
scale.scale.range(attributeScale.values);
// 如果设置了回调, 这不需要设置让range
if (!attributeScale.callback) {
if(attributeScale.values) {
scale.scale.range(attributeScale.values);
} else if(scale.option?.type==='cat') {
// 如果没有设置初值且 类型为catrange ==domain;
scale.scale.range(scale.option.domain);
}
}
});
} else {
// 设置attribute 常量值 常量直接在value取值
attributeScale.type = StyleScaleType.CONSTANT;
attributeScale.values = scales.map((scale, index) => {
return scale.scale(attributeScale.names[index]);
});
@ -166,7 +173,7 @@ export default class FeatureScalePlugin implements ILayerPlugin {
type: StyleScaleType.VARIABLE,
option: scaleOption,
};
if (!data || !data.length) {
if (!data || !data.length) {
if (scaleOption && scaleOption.type) {
styleScale.scale = this.createDefaultScale(scaleOption);

View File

@ -164,14 +164,14 @@ export default class TextLayer extends BaseLayer<IPointTextLayerStyleOptions> {
});
}
private iniTextFont() {
private initTextFont() {
const { fontWeight = 'normal', fontFamily } = this.getStyleOptions();
const data = this.getEncodedData();
const characterSet: string[] = [];
data.forEach((item: IEncodeFeature) => {
let { text = '' } = item;
text = text.toString();
for (const char of text) {
let { shape = '' } = item;
shape = shape.toString();
for (const char of shape) {
// 去重
if (characterSet.indexOf(char) === -1) {
characterSet.push(char);

View File

@ -46,7 +46,7 @@ export default class ReglRendererService implements IRendererService {
alpha: true,
// use TAA instead of MSAA
// @see https://www.khronos.org/registry/webgl/specs/1.0/#5.2.1
antialias: false,
antialias: true,
premultipliedAlpha: true,
},
// TODO: use extensions