mirror of https://gitee.com/antv-l7/antv-l7
feat(layer): add imagelayer
This commit is contained in:
parent
6f33e5f72b
commit
a995815284
|
@ -1,4 +1,4 @@
|
|||
import { ISourceCFG } from '@l7/source';
|
||||
import { ISourceCFG } from '@l7/core';
|
||||
import { AsyncParallelHook, SyncHook } from 'tapable';
|
||||
import { IModel } from '../renderer/IModel';
|
||||
import { IMultiPassRenderer } from '../renderer/IMultiPassRenderer';
|
||||
|
|
|
@ -33,6 +33,7 @@ export interface ITexture2DInitializationOptions {
|
|||
*/
|
||||
data?:
|
||||
| undefined
|
||||
| HTMLImageElement
|
||||
| number[]
|
||||
| number[][]
|
||||
| Uint8Array
|
||||
|
|
|
@ -12,7 +12,7 @@ type CallBack = (...args: any[]) => any;
|
|||
export interface ITransform {
|
||||
type: string;
|
||||
[key: string]: any;
|
||||
callback: CallBack;
|
||||
callback?: CallBack;
|
||||
}
|
||||
|
||||
export interface ISourceCFG {
|
||||
|
|
|
@ -29,7 +29,9 @@
|
|||
"earcut": "^2.2.1",
|
||||
"eventemitter3": "^3.1.0",
|
||||
"gl-matrix": "^3.1.0",
|
||||
"gl-vec2": "^1.3.0",
|
||||
"lodash": "^4.17.15",
|
||||
"polyline-miter-util": "^1.0.1",
|
||||
"tapable": "^2.0.0-beta.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { ILayerStyleOptions } from '@l7/core';
|
||||
import { IICONMap, ILayerStyleOptions } from '@l7/core';
|
||||
import { lngLatToMeters } from '@l7/utils';
|
||||
import { vec3 } from 'gl-matrix';
|
||||
import { IExtrudeGeomety } from '../point/shape/extrude';
|
||||
interface IBufferCfg {
|
||||
data: unknown[];
|
||||
imagePos?: unknown;
|
||||
iconMap?: IICONMap;
|
||||
style?: ILayerStyleOptions;
|
||||
}
|
||||
export type Position = number[];
|
||||
|
@ -26,6 +27,7 @@ export interface IEncodeFeature {
|
|||
coordinates: unknown;
|
||||
bufferInfo: unknown;
|
||||
}
|
||||
|
||||
export default class Buffer {
|
||||
public attributes: {
|
||||
[key: string]: Float32Array;
|
||||
|
@ -33,14 +35,14 @@ export default class Buffer {
|
|||
public verticesCount: number = 0;
|
||||
public indexArray: Uint32Array = new Uint32Array(0);
|
||||
public indexCount: number = 0;
|
||||
|
||||
public instanceGeometry: IExtrudeGeomety;
|
||||
protected data: unknown[];
|
||||
protected imagePos: unknown;
|
||||
protected iconMap: IICONMap;
|
||||
protected style: any;
|
||||
|
||||
constructor({ data, imagePos, style }: IBufferCfg) {
|
||||
constructor({ data, iconMap, style }: IBufferCfg) {
|
||||
this.data = data;
|
||||
this.imagePos = imagePos;
|
||||
this.iconMap = iconMap as IICONMap;
|
||||
this.style = style;
|
||||
this.init();
|
||||
}
|
||||
|
@ -126,7 +128,7 @@ export default class Buffer {
|
|||
const { color, id, pattern, size } = feature;
|
||||
const bufferInfo = feature.bufferInfo as IBufferInfo;
|
||||
const { verticesOffset } = bufferInfo;
|
||||
const imagePos = this.imagePos;
|
||||
const imagePos = this.iconMap;
|
||||
const start1 = verticesOffset;
|
||||
for (let i = 0; i < num; i++) {
|
||||
if (color) {
|
||||
|
|
|
@ -10,12 +10,13 @@ import {
|
|||
IMultiPassRenderer,
|
||||
IRendererService,
|
||||
ISource,
|
||||
ISourceCFG,
|
||||
lazyInject,
|
||||
StyleAttributeField,
|
||||
StyleAttributeOption,
|
||||
TYPES,
|
||||
} from '@l7/core';
|
||||
import Source, { ISourceCFG } from '@l7/source';
|
||||
import Source from '@l7/source';
|
||||
import { isFunction } from 'lodash';
|
||||
import { SyncHook } from 'tapable';
|
||||
import DataEncodePlugin from '../plugins/DataEncodePlugin';
|
||||
|
|
|
@ -46,7 +46,9 @@ export default class StyleAttribute implements ILayerStyleAttribute {
|
|||
if (scales.some((scale) => scale.type === StyleScaleType.VARIABLE)) {
|
||||
this.type = StyleScaleType.VARIABLE;
|
||||
scales.forEach((scale) => {
|
||||
scale.scale.range(this.values);
|
||||
if (this.values.length > 0) {
|
||||
scale.scale.range(this.values);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 设置attribute 常量值
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
import BufferBase, { IEncodeFeature, Position } from '../../core/BaseBuffer';
|
||||
import extrudePolygon, {
|
||||
fillPolygon,
|
||||
IExtrudeGeomety,
|
||||
} from '../../point/shape/extrude';
|
||||
import {
|
||||
geometryShape,
|
||||
ShapeType2D,
|
||||
ShapeType3D,
|
||||
} from '../../point/shape/Path';
|
||||
export default class GridHeatMapBuffer extends BufferBase {
|
||||
private verticesOffset: number = 0;
|
||||
protected buildFeatures() {
|
||||
this.verticesOffset = 0;
|
||||
const layerData = this.data as IEncodeFeature[];
|
||||
layerData.forEach((feature: IEncodeFeature) => {
|
||||
this.calculateFill(feature);
|
||||
});
|
||||
}
|
||||
protected calculateFeatures() {
|
||||
const layerData = this.data as IEncodeFeature[];
|
||||
const shape = layerData[0].shape as ShapeType3D | ShapeType2D;
|
||||
this.verticesCount = layerData.length;
|
||||
this.indexCount = 0;
|
||||
this.instanceGeometry = this.getGeometry(shape as
|
||||
| ShapeType2D
|
||||
| ShapeType3D);
|
||||
}
|
||||
protected calculateFill(feature: IEncodeFeature) {
|
||||
feature.bufferInfo = { verticesOffset: this.verticesOffset };
|
||||
const coordinates = feature.coordinates as Position;
|
||||
this.encodeArray(feature, 1);
|
||||
this.attributes.positions.set([...coordinates, 1], this.verticesOffset * 3);
|
||||
this.verticesOffset++;
|
||||
}
|
||||
private getGeometry(shape: ShapeType2D | ShapeType3D): IExtrudeGeomety {
|
||||
const path = geometryShape[shape]
|
||||
? geometryShape[shape]()
|
||||
: geometryShape.circle();
|
||||
// const geometry = ShapeType2D[str as ShapeType2D]
|
||||
// ? fillPolygon([path])
|
||||
// : extrudePolygon([path]);
|
||||
const geometry = fillPolygon([path]);
|
||||
return geometry;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
import {
|
||||
gl,
|
||||
IRendererService,
|
||||
IShaderModuleService,
|
||||
lazyInject,
|
||||
TYPES,
|
||||
} from '@l7/core';
|
||||
import BaseLayer from '../core/BaseLayer';
|
||||
import GridHeatMapBuffer from './buffers/GridBuffer';
|
||||
import hexagon_frag from './shaders/hexagon_frag.glsl';
|
||||
import hexagon_vert from './shaders/hexagon_vert.glsl';
|
||||
|
||||
export default class HeatMapLayer extends BaseLayer {
|
||||
public name: string = 'HeatMapLayer';
|
||||
|
||||
@lazyInject(TYPES.IShaderModuleService)
|
||||
private readonly shaderModule: IShaderModuleService;
|
||||
|
||||
@lazyInject(TYPES.IRendererService)
|
||||
private readonly renderer: IRendererService;
|
||||
|
||||
protected renderModels() {
|
||||
this.models.forEach((model) =>
|
||||
model.draw({
|
||||
uniforms: {
|
||||
u_ModelMatrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
|
||||
},
|
||||
}),
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
protected buildModels(): void {
|
||||
this.shaderModule.registerModule('grid', {
|
||||
vs: hexagon_vert,
|
||||
fs: hexagon_frag,
|
||||
});
|
||||
this.models = [];
|
||||
const { vs, fs, uniforms } = this.shaderModule.getModule('grid');
|
||||
const buffer = new GridHeatMapBuffer({
|
||||
data: this.getEncodedData(),
|
||||
});
|
||||
console.log(this.getSource());
|
||||
console.log(buffer);
|
||||
const {
|
||||
createAttribute,
|
||||
createBuffer,
|
||||
createElements,
|
||||
createModel,
|
||||
} = this.renderer;
|
||||
|
||||
this.models.push(
|
||||
createModel({
|
||||
attributes: {
|
||||
a_miter: createAttribute({
|
||||
buffer: createBuffer({
|
||||
data: buffer.instanceGeometry.positions,
|
||||
type: gl.FLOAT,
|
||||
}),
|
||||
size: 3,
|
||||
divisor: 0,
|
||||
}),
|
||||
// a_normal: createAttribute({
|
||||
// buffer: createBuffer({
|
||||
// data: buffer.attributes.normals,
|
||||
// type: gl.FLOAT,
|
||||
// }),
|
||||
// size: 3,
|
||||
// }),
|
||||
a_color: createAttribute({
|
||||
buffer: createBuffer({
|
||||
data: buffer.attributes.colors,
|
||||
type: gl.FLOAT,
|
||||
}),
|
||||
size: 4,
|
||||
divisor: 1,
|
||||
}),
|
||||
// a_size: createAttribute({
|
||||
// buffer: createBuffer({
|
||||
// data: buffer.attributes.sizes,
|
||||
// type: gl.FLOAT,
|
||||
// }),
|
||||
// size: 1,
|
||||
// divisor: 1,
|
||||
// }),
|
||||
a_Position: createAttribute({
|
||||
buffer: createBuffer({
|
||||
data: buffer.attributes.positions,
|
||||
type: gl.FLOAT,
|
||||
}),
|
||||
size: 3,
|
||||
divisor: 1,
|
||||
}),
|
||||
},
|
||||
uniforms: {
|
||||
...uniforms,
|
||||
u_opacity: (this.styleOption.opacity as number) || 1.0,
|
||||
u_radius: [
|
||||
this.getSource().data.xOffset,
|
||||
this.getSource().data.yOffset,
|
||||
],
|
||||
},
|
||||
fs,
|
||||
vs,
|
||||
count: buffer.instanceGeometry.index.length,
|
||||
instances: buffer.verticesCount,
|
||||
elements: createElements({
|
||||
data: buffer.instanceGeometry.index,
|
||||
type: gl.UNSIGNED_INT,
|
||||
}),
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
precision highp float;
|
||||
varying vec4 v_color;
|
||||
uniform float u_opacity: 0.1;
|
||||
void main() {
|
||||
gl_FragColor = v_color;
|
||||
gl_FragColor.a *= u_opacity;
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
precision highp float;
|
||||
attribute vec3 a_Position;
|
||||
attribute vec3 a_miter;
|
||||
attribute float a_size;
|
||||
attribute vec4 a_color;
|
||||
uniform vec2 u_radius;
|
||||
uniform float u_coverage: 1.;
|
||||
uniform float u_angle: 0;
|
||||
uniform mat4 u_ModelMatrix;
|
||||
varying vec4 v_color;
|
||||
#pragma include "projection"
|
||||
void main() {
|
||||
v_color = a_color;
|
||||
mat2 rotationMatrix = mat2(cos(u_angle), sin(u_angle), -sin(u_angle), cos(u_angle));
|
||||
vec2 offset =(vec2(a_miter.xy * u_radius * u_coverage * rotationMatrix));
|
||||
vec4 project_pos = project_position(vec4(a_Position.xy + offset, 0, 1.0));
|
||||
gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy, 0., 1.0));
|
||||
}
|
|
@ -1,5 +1,16 @@
|
|||
import BaseLayer from './core/BaseLayer';
|
||||
import HeatMapLayer from './heatmap';
|
||||
import Line from './line';
|
||||
import PointLayer from './point';
|
||||
import Point from './point/point';
|
||||
import PolygonLayer from './polygon';
|
||||
export { BaseLayer, PointLayer, PolygonLayer, Point };
|
||||
import ImageLayer from './raster';
|
||||
export {
|
||||
BaseLayer,
|
||||
PointLayer,
|
||||
PolygonLayer,
|
||||
Point,
|
||||
Line,
|
||||
ImageLayer,
|
||||
HeatMapLayer,
|
||||
};
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import { lngLatToMeters, Point } from '@l7/utils';
|
||||
import BufferBase, { IEncodeFeature, Position } from '../../core/BaseBuffer';
|
||||
import getNormals from '../../utils/polylineNormal';
|
||||
interface IBufferInfo {
|
||||
normals: number[];
|
||||
arrayIndex: number[];
|
||||
|
@ -8,7 +10,7 @@ interface IBufferInfo {
|
|||
verticesOffset: number;
|
||||
indexOffset: number;
|
||||
}
|
||||
export default class FillBuffer extends BufferBase {
|
||||
export default class LineBuffer extends BufferBase {
|
||||
private hasPattern: boolean;
|
||||
protected buildFeatures() {
|
||||
const layerData = this.data as IEncodeFeature[];
|
||||
|
@ -32,13 +34,19 @@ export default class FillBuffer extends BufferBase {
|
|||
protected calculateFeatures() {
|
||||
const layerData = this.data as IEncodeFeature[];
|
||||
// 计算长
|
||||
layerData.forEach((feature: IEncodeFeature) => {
|
||||
let { coordinates } = feature;
|
||||
layerData.forEach((feature: IEncodeFeature, index: number) => {
|
||||
let coordinates = feature.coordinates as Position[] | Position[][];
|
||||
if (Array.isArray(coordinates[0][0])) {
|
||||
coordinates = coordinates[0];
|
||||
coordinates = coordinates[0] as Position[];
|
||||
}
|
||||
// @ts-ignore
|
||||
const projectCoord: number[][] = coordinates.map((item: Position[]) => {
|
||||
// @ts-ignore
|
||||
const p: Point = [...item];
|
||||
return lngLatToMeters(p);
|
||||
});
|
||||
const { normals, attrIndex, attrPos, attrDistance, miters } = getNormals(
|
||||
coordinates,
|
||||
coordinates as number[][],
|
||||
false,
|
||||
this.verticesCount,
|
||||
);
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
uniform float u_blur : 0.9;
|
||||
uniform float u_blur : 0.99;
|
||||
varying vec4 v_color;
|
||||
varying vec3 v_normal;
|
||||
void main() {
|
||||
gl_FragColor = v_color;
|
||||
// anti-alias
|
||||
// float blur = 1. - smoothstep(u_blur, 1., length(v_normal));
|
||||
// gl_FragColor.a *= blur;
|
||||
float blur = smoothstep(u_blur, 1., length(v_normal.xy));
|
||||
gl_FragColor.a *= blur;
|
||||
}
|
|
@ -60,7 +60,11 @@ export default class DataEncodePlugin implements ILayerPlugin {
|
|||
let scale = this.scaleCache[field as string];
|
||||
if (!scale) {
|
||||
scale = this.scaleController.createScale(field as string, data);
|
||||
if (scale.type === StyleScaleType.VARIABLE) {
|
||||
if (
|
||||
scale.type === StyleScaleType.VARIABLE &&
|
||||
attribute.values &&
|
||||
attribute.values.length > 0
|
||||
) {
|
||||
scale.scale.range(attribute.values);
|
||||
}
|
||||
this.scaleCache[field as string] = scale;
|
||||
|
|
|
@ -9,7 +9,8 @@ import {
|
|||
StyleScaleType,
|
||||
TYPES,
|
||||
} from '@l7/core';
|
||||
import Source, { ISourceCFG } from '@l7/source';
|
||||
import { ISourceCFG } from '@l7/core';
|
||||
import Source from '@l7/source';
|
||||
export default class DataSourcePlugin implements ILayerPlugin {
|
||||
public apply(layer: ILayer) {
|
||||
layer.hooks.init.tap('DataSourcePlugin', () => {
|
||||
|
|
|
@ -4,7 +4,7 @@ import BaseBuffer, {
|
|||
Position,
|
||||
} from '../../core/BaseBuffer';
|
||||
import extrudePolygon, { IExtrudeGeomety } from '../shape/extrude';
|
||||
import { geometryShape, ShapeType } from '../shape/Path';
|
||||
import { geometryShape, ShapeType2D, ShapeType3D } from '../shape/Path';
|
||||
interface IGeometryCache {
|
||||
[key: string]: IExtrudeGeomety;
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ export default class ExtrudeBuffer extends BaseBuffer {
|
|||
this.indexOffset = 0;
|
||||
layerData.forEach((feature: IEncodeFeature) => {
|
||||
const { shape } = feature;
|
||||
const { positions, index } = this.getGeometry(shape as ShapeType);
|
||||
const { positions, index } = this.getGeometry(shape as ShapeType3D);
|
||||
this.verticesCount += positions.length / 3;
|
||||
this.indexCount += index.length;
|
||||
});
|
||||
|
@ -39,7 +39,7 @@ export default class ExtrudeBuffer extends BaseBuffer {
|
|||
}
|
||||
private calculateFill(feature: IEncodeFeature) {
|
||||
const { coordinates, shape } = feature;
|
||||
const instanceGeometry = this.getGeometry(shape as ShapeType);
|
||||
const instanceGeometry = this.getGeometry(shape as ShapeType3D);
|
||||
const numPoint = instanceGeometry.positions.length / 3;
|
||||
feature.bufferInfo = {
|
||||
verticesOffset: this.verticesOffset,
|
||||
|
@ -65,7 +65,7 @@ export default class ExtrudeBuffer extends BaseBuffer {
|
|||
this.indexOffset += indexArray.length;
|
||||
}
|
||||
|
||||
private getGeometry(shape: ShapeType): IExtrudeGeomety {
|
||||
private getGeometry(shape: ShapeType3D): IExtrudeGeomety {
|
||||
if (this.geometryCache && this.geometryCache[shape]) {
|
||||
return this.geometryCache[shape];
|
||||
}
|
||||
|
|
|
@ -9,11 +9,11 @@ export default class ImageBuffer extends BaseBuffer {
|
|||
const layerData = this.data as IEncodeFeature[];
|
||||
layerData.forEach((item: IEncodeFeature, index: number) => {
|
||||
const { color = [0, 0, 0, 0], size, id, shape, coordinates } = item;
|
||||
const { x, y } = this.imagePos[shape];
|
||||
const { x, y } = this.iconMap[shape as string];
|
||||
const coor = coordinates as Position;
|
||||
this.attributes.vertices.set([coor[0], coor[1], coor[2] || 0], index * 3);
|
||||
this.attributes.colors.set(color, index * 4);
|
||||
this.attributes.pickingIds.set([id], index);
|
||||
this.attributes.pickingIds.set([id as number], index);
|
||||
this.attributes.sizes.set([size as number], index); //
|
||||
this.attributes.uv.set([x, y], index * 2);
|
||||
});
|
||||
|
|
|
@ -42,7 +42,6 @@ export default class PointLayer extends BaseLayer {
|
|||
data: this.getEncodedData(),
|
||||
});
|
||||
buffer.computeVertexNormals('miters', false);
|
||||
console.log(buffer); // TODO: normal
|
||||
const {
|
||||
createAttribute,
|
||||
createBuffer,
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
type IPosition = [number, number, number];
|
||||
export type IPath = IPosition[];
|
||||
export enum ShapeType {
|
||||
CIRCLE = 'cylinder',
|
||||
SQUARE = 'squareColumn',
|
||||
TRIANGLE = 'triangleColumn',
|
||||
HEXAGON = 'hexagonColumn',
|
||||
PENTAGON = 'pentagonColumn',
|
||||
export enum ShapeType3D {
|
||||
CYLINDER = 'cylinder',
|
||||
SQUARECOLUMN = 'squareColumn',
|
||||
TRIANGLECOLUMN = 'triangleColumn',
|
||||
HEXAGONCOLUMN = 'hexagonColumn',
|
||||
PENTAGONCOLUMN = 'pentagonColumn',
|
||||
}
|
||||
export enum ShapeType2D {
|
||||
CIRCLE = 'circle',
|
||||
SQUARE = 'square',
|
||||
TRIANGLE = 'triangle',
|
||||
HEXAGON = 'hexagon',
|
||||
PENTAGON = 'pentagon',
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -24,7 +31,7 @@ export function polygonPath(pointCount: number, start: number = 0): IPath {
|
|||
const y = Math.cos(t + Math.PI / 4);
|
||||
return [x, y, 0];
|
||||
});
|
||||
path.push(path[0]);
|
||||
// path.push(path[0]);
|
||||
return path;
|
||||
}
|
||||
|
||||
|
@ -45,9 +52,14 @@ export function pentagon(): IPath {
|
|||
}
|
||||
|
||||
export const geometryShape = {
|
||||
[ShapeType.CIRCLE]: circle,
|
||||
[ShapeType.HEXAGON]: hexagon,
|
||||
[ShapeType.TRIANGLE]: triangle,
|
||||
[ShapeType.SQUARE]: square,
|
||||
[ShapeType.PENTAGON]: pentagon,
|
||||
[ShapeType2D.CIRCLE]: circle,
|
||||
[ShapeType2D.HEXAGON]: hexagon,
|
||||
[ShapeType2D.TRIANGLE]: triangle,
|
||||
[ShapeType2D.SQUARE]: square,
|
||||
[ShapeType2D.PENTAGON]: pentagon,
|
||||
[ShapeType3D.CYLINDER]: circle,
|
||||
[ShapeType3D.HEXAGONCOLUMN]: hexagon,
|
||||
[ShapeType3D.TRIANGLECOLUMN]: triangle,
|
||||
[ShapeType3D.SQUARECOLUMN]: square,
|
||||
[ShapeType3D.PENTAGONCOLUMN]: pentagon,
|
||||
};
|
||||
|
|
|
@ -60,3 +60,15 @@ export default function extrudePolygon(path: IPath[]): IExtrudeGeomety {
|
|||
index: indexArray,
|
||||
};
|
||||
}
|
||||
export function fillPolygon(points: IPath[]) {
|
||||
const flattengeo = earcut.flatten(points);
|
||||
const triangles = earcut(
|
||||
flattengeo.vertices,
|
||||
flattengeo.holes,
|
||||
flattengeo.dimensions,
|
||||
);
|
||||
return {
|
||||
positions: flattengeo.vertices,
|
||||
index: triangles,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,14 +4,13 @@ interface IImageFeature extends IEncodeFeature {
|
|||
}
|
||||
export default class ImageBuffer extends BaseBuffer {
|
||||
protected calculateFeatures() {
|
||||
const layerData = this.data as IImageFeature[];
|
||||
this.verticesCount = 4;
|
||||
this.verticesCount = 6;
|
||||
this.indexCount = 6;
|
||||
}
|
||||
protected buildFeatures() {
|
||||
this.attributes.uv = new Float32Array(this.verticesCount * 2);
|
||||
const layerData = this.data as IImageFeature[];
|
||||
const coordinates = layerData[0].coordinates as Position[];
|
||||
const images = layerData[0].images;
|
||||
const positions: number[] = [
|
||||
...coordinates[0],
|
||||
0,
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
import {
|
||||
gl,
|
||||
IRendererService,
|
||||
IShaderModuleService,
|
||||
ITexture2D,
|
||||
lazyInject,
|
||||
TYPES,
|
||||
} from '@l7/core';
|
||||
import BaseLayer from '../core/BaseLayer';
|
||||
import ImageBuffer from './buffers/ImageBuffer';
|
||||
import image_frag from './shaders/image_frag.glsl';
|
||||
import image_vert from './shaders/image_vert.glsl';
|
||||
export default class ImageLayer extends BaseLayer {
|
||||
public name: string = 'imageLayer';
|
||||
@lazyInject(TYPES.IShaderModuleService)
|
||||
private readonly shaderModule: IShaderModuleService;
|
||||
|
||||
@lazyInject(TYPES.IRendererService)
|
||||
private readonly renderer: IRendererService;
|
||||
|
||||
protected renderModels() {
|
||||
this.models.forEach((model) =>
|
||||
model.draw({
|
||||
uniforms: {
|
||||
u_ModelMatrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
|
||||
},
|
||||
}),
|
||||
);
|
||||
return this;
|
||||
}
|
||||
protected buildModels() {
|
||||
const {
|
||||
createAttribute,
|
||||
createBuffer,
|
||||
createElements,
|
||||
createTexture2D,
|
||||
createModel,
|
||||
} = this.renderer;
|
||||
this.shaderModule.registerModule('image', {
|
||||
vs: image_vert,
|
||||
fs: image_frag,
|
||||
});
|
||||
|
||||
this.models = [];
|
||||
const { vs, fs, uniforms } = this.shaderModule.getModule('image');
|
||||
const source = this.getSource();
|
||||
// const imageData = await source.data.images;
|
||||
const buffer = new ImageBuffer({
|
||||
data: this.getEncodedData(),
|
||||
});
|
||||
source.data.images.then((imageData: HTMLImageElement[]) => {
|
||||
const texture: ITexture2D = createTexture2D({
|
||||
data: imageData[0],
|
||||
width: imageData[0].width,
|
||||
height: imageData[0].height,
|
||||
});
|
||||
this.models.push(
|
||||
createModel({
|
||||
attributes: {
|
||||
a_Position: createAttribute({
|
||||
buffer: createBuffer({
|
||||
data: buffer.attributes.positions,
|
||||
type: gl.FLOAT,
|
||||
}),
|
||||
size: 3,
|
||||
}),
|
||||
a_uv: createAttribute({
|
||||
buffer: createBuffer({
|
||||
data: buffer.attributes.uv,
|
||||
type: gl.FLOAT,
|
||||
}),
|
||||
size: 2,
|
||||
}),
|
||||
},
|
||||
uniforms: {
|
||||
...uniforms,
|
||||
u_texture: texture,
|
||||
u_opacity: 1.0,
|
||||
},
|
||||
fs,
|
||||
vs,
|
||||
count: buffer.verticesCount,
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
precision mediump float;
|
||||
uniform float u_opacity: 1.0;
|
||||
uniform sampler2D u_texture;
|
||||
uniform float u_opacity;
|
||||
varying vec2 v_texCoord;
|
||||
void main() {
|
||||
vec4 color = texture2D(u_texture,vec2(v_texCoord.x,1.0-v_texCoord.y));
|
||||
gl_FragColor = color * u_opacity;
|
||||
vec4 color = texture2D(u_texture,vec2(v_texCoord.x,v_texCoord.y));
|
||||
gl_FragColor = color;
|
||||
}
|
|
@ -1,9 +1,12 @@
|
|||
precision highp float;
|
||||
varying vec2 v_texCoord;
|
||||
uniform mat4 u_ModelMatrix;
|
||||
attribute vec3 a_Position;
|
||||
attribute vec2 a_uv;
|
||||
varying vec2 v_texCoord;
|
||||
|
||||
#pragma include "projection"
|
||||
void main() {
|
||||
v_texCoord = uv;
|
||||
v_texCoord = a_uv;
|
||||
vec4 project_pos = project_position(vec4(a_Position, 1.0));
|
||||
gl_Position = project_common_position_to_clipspace(vec4(project_pos.xyz, 1.0));
|
||||
gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy,0., 1.0));
|
||||
}
|
|
@ -1,195 +0,0 @@
|
|||
/**
|
||||
* 对于 polyline-normal 的改进
|
||||
* 超过阈值,miter 转成 bevel 接头,
|
||||
* 要注意 Three.js 中默认 THREE.FrontFaceDirectionCCW
|
||||
* @see https://zhuanlan.zhihu.com/p/59541559
|
||||
*/
|
||||
|
||||
// @ts-ignore
|
||||
import { copy, create, dot } from 'gl-vec2';
|
||||
// @ts-ignore
|
||||
import { computeMiter, direction, normal } from 'polyline-miter-util';
|
||||
|
||||
// @ts-ignore
|
||||
function extrusions(positions, out, miters, point, normal1, scale) {
|
||||
addNext(out, miters, normal1, -scale);
|
||||
addNext(out, miters, normal1, scale);
|
||||
positions.push(...point, 0);
|
||||
positions.push(...point, 0);
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
// tslint:disable-next-line:no-shadowed-variable
|
||||
function addNext(out, miters, normal, length) {
|
||||
out.push(normal[0], normal[1], 0);
|
||||
miters.push(length);
|
||||
}
|
||||
// @ts-ignore
|
||||
function lineSegmentDistance(end, start) {
|
||||
const dx = start[0] - end[0];
|
||||
const dy = start[1] - end[1];
|
||||
const dz = start[2] - end[2];
|
||||
return Math.sqrt(dx * dx + dy * dy + dz * dz);
|
||||
}
|
||||
// @ts-ignore
|
||||
function isPointEqual(a, b) {
|
||||
return a[0] === b[0] && a[1] === b[1];
|
||||
}
|
||||
// @ts-ignore
|
||||
export default function(points, closed, indexOffset) {
|
||||
const lineA = [0, 0];
|
||||
const lineB = [0, 0];
|
||||
const tangent = [0, 0];
|
||||
const miter = [0, 0];
|
||||
// tslint:disable-next-line:variable-name
|
||||
let _started = false;
|
||||
// tslint:disable-next-line:variable-name
|
||||
let _normal = null;
|
||||
const tmp = create();
|
||||
let count = indexOffset || 0;
|
||||
const miterLimit = 3;
|
||||
// @ts-ignore
|
||||
const out = [];
|
||||
const attrPos = [];
|
||||
const attrIndex = [];
|
||||
// @ts-ignore
|
||||
const miters = [];
|
||||
const attrDistance = [0, 0];
|
||||
if (closed) {
|
||||
points = points.slice();
|
||||
points.push(points[0]);
|
||||
}
|
||||
|
||||
const total = points.length;
|
||||
|
||||
for (let i = 1; i < total; i++) {
|
||||
const index = count;
|
||||
const last = points[i - 1];
|
||||
const cur = points[i];
|
||||
let next = i < points.length - 1 ? points[i + 1] : null;
|
||||
// 如果当前点和前一点相同,跳过
|
||||
if (isPointEqual(last, cur)) {
|
||||
continue;
|
||||
}
|
||||
if (next) {
|
||||
let nextIndex = i + 1;
|
||||
// 找到不相同的下一点
|
||||
while (next && isPointEqual(cur, next)) {
|
||||
next = nextIndex < points.length - 1 ? points[++nextIndex] : null;
|
||||
}
|
||||
}
|
||||
const lineDistance = lineSegmentDistance(cur, last);
|
||||
const d = lineDistance + attrDistance[attrDistance.length - 1];
|
||||
|
||||
direction(lineA, cur, last);
|
||||
|
||||
if (!_normal) {
|
||||
_normal = [0, 0];
|
||||
normal(_normal, lineA);
|
||||
}
|
||||
|
||||
if (!_started) {
|
||||
_started = true;
|
||||
// @ts-ignore
|
||||
extrusions(attrPos, out, miters, last, _normal, 1);
|
||||
}
|
||||
|
||||
attrIndex.push(index + 0, index + 2, index + 1);
|
||||
|
||||
// no miter, simple segment
|
||||
if (!next) {
|
||||
// reset normal
|
||||
normal(_normal, lineA);
|
||||
// @ts-ignore
|
||||
extrusions(attrPos, out, miters, cur, _normal, 1);
|
||||
attrDistance.push(d, d);
|
||||
attrIndex.push(index + 1, index + 2, index + 3);
|
||||
count += 2;
|
||||
} else {
|
||||
// get unit dir of next line
|
||||
direction(lineB, next, cur);
|
||||
|
||||
// stores tangent & miter
|
||||
let miterLen = computeMiter(tangent, miter, lineA, lineB, 1);
|
||||
|
||||
// get orientation
|
||||
const flip = dot(tangent, _normal) < 0 ? -1 : 1;
|
||||
const bevel = Math.abs(miterLen) > miterLimit;
|
||||
|
||||
// 处理前后两条线段重合的情况,这种情况不需要使用任何接头(miter/bevel)。
|
||||
// 理论上这种情况下 miterLen = Infinity,本应通过 isFinite(miterLen) 判断,
|
||||
// 但是 AMap 投影变换后丢失精度,只能通过一个阈值(1000)判断。
|
||||
if (Math.abs(miterLen) > 1000) {
|
||||
// @ts-ignore
|
||||
extrusions(attrPos, out, miters, cur, _normal, 1);
|
||||
attrIndex.push(index + 1, index + 2, index + 3);
|
||||
attrIndex.push(index + 2, index + 4, index + 3);
|
||||
normal(tmp, lineB);
|
||||
copy(_normal, tmp); // store normal for next round
|
||||
// @ts-ignore
|
||||
extrusions(attrPos, out, miters, cur, _normal, 1);
|
||||
attrDistance.push(d, d, d, d);
|
||||
|
||||
// the miter is now the normal for our next join
|
||||
count += 4;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bevel) {
|
||||
miterLen = miterLimit;
|
||||
|
||||
// next two points in our first segment
|
||||
// @ts-ignore
|
||||
extrusions(attrPos, out, miters, cur, _normal, 1);
|
||||
|
||||
attrIndex.push(index + 1, index + 2, index + 3);
|
||||
|
||||
// now add the bevel triangle
|
||||
attrIndex.push(
|
||||
...(flip === 1
|
||||
? [index + 2, index + 4, index + 5]
|
||||
: [index + 4, index + 5, index + 3]),
|
||||
);
|
||||
|
||||
normal(tmp, lineB);
|
||||
copy(_normal, tmp); // store normal for next round
|
||||
// @ts-ignore
|
||||
extrusions(attrPos, out, miters, cur, _normal, 1);
|
||||
attrDistance.push(d, d, d, d);
|
||||
|
||||
// the miter is now the normal for our next join
|
||||
count += 4;
|
||||
} else {
|
||||
// next two points in our first segment
|
||||
// @ts-ignore
|
||||
extrusions(attrPos, out, miters, cur, _normal, 1);
|
||||
attrIndex.push(index + 1, index + 2, index + 3);
|
||||
|
||||
// now add the miter triangles
|
||||
// @ts-ignore
|
||||
addNext(out, miters, miter, miterLen * -flip);
|
||||
attrPos.push(...cur, 0);
|
||||
attrIndex.push(index + 2, index + 4, index + 3);
|
||||
attrIndex.push(index + 4, index + 5, index + 6);
|
||||
normal(tmp, lineB);
|
||||
copy(_normal, tmp); // store normal for next round
|
||||
// @ts-ignore
|
||||
extrusions(attrPos, out, miters, cur, _normal, 1);
|
||||
attrDistance.push(d, d, d, d, d);
|
||||
|
||||
// the miter is now the normal for our next join
|
||||
count += 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
// @ts-ignore
|
||||
return {
|
||||
// @ts-ignore
|
||||
normals: out,
|
||||
attrIndex,
|
||||
attrPos,
|
||||
attrDistance,
|
||||
// @ts-ignore
|
||||
miters,
|
||||
};
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import { aProjectFlat, lngLatToMeters, Point } from '@l7/utils';
|
||||
import { vec2 } from 'gl-matrix';
|
||||
|
||||
export function computeMiter(
|
||||
tangent: vec2,
|
||||
miter: vec2,
|
||||
|
@ -17,55 +17,66 @@ export function computeNormal(out: vec2, dir: vec2) {
|
|||
return vec2.set(out, -dir[1], dir[0]);
|
||||
}
|
||||
export function direction(out: vec2, a: vec2, b: vec2) {
|
||||
vec2.sub(out, a, b);
|
||||
const a1 = aProjectFlat([a[0], a[1]]);
|
||||
const b1 = aProjectFlat([b[0], b[1]]);
|
||||
vec2.sub(out, a1, b1);
|
||||
vec2.normalize(out, out);
|
||||
return out;
|
||||
}
|
||||
function extrusions(
|
||||
positions: number[],
|
||||
out: vec2,
|
||||
miters: vec2,
|
||||
out: number[],
|
||||
miters: number[],
|
||||
point: vec2,
|
||||
normal: vec2,
|
||||
scale,
|
||||
scale: number,
|
||||
) {
|
||||
addNext(out, miters, normal, -scale);
|
||||
addNext(out, miters, normal, scale);
|
||||
positions.push(...point);
|
||||
positions.push(...point);
|
||||
positions.push(point[0], point[1], 0);
|
||||
positions.push(point[0], point[1], 0);
|
||||
}
|
||||
|
||||
function addNext(out, miters, normal, length) {
|
||||
function addNext(
|
||||
out: number[],
|
||||
miters: number[],
|
||||
normal: vec2,
|
||||
length: number,
|
||||
) {
|
||||
out.push(normal[0], normal[1], 0);
|
||||
miters.push(length);
|
||||
}
|
||||
|
||||
function lineSegmentDistance(end, start) {
|
||||
function lineSegmentDistance(end: vec2, start: vec2) {
|
||||
const dx = start[0] - end[0];
|
||||
const dy = start[1] - end[1];
|
||||
const dz = start[2] - end[2];
|
||||
return Math.sqrt(dx * dx + dy * dy + dz * dz);
|
||||
// const dz = start[2] - end[2];
|
||||
return Math.sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
function isPointEqual(a, b) {
|
||||
function isPointEqual(a: vec2, b: vec2) {
|
||||
return a[0] === b[0] && a[1] === b[1];
|
||||
}
|
||||
|
||||
export default function(points, closed, indexOffset) {
|
||||
export default function(
|
||||
points: number[][],
|
||||
closed: boolean,
|
||||
indexOffset: number,
|
||||
) {
|
||||
const lineA = vec2.fromValues(0, 0);
|
||||
const lineB = vec2.fromValues(0, 0);
|
||||
const tangent = vec2.fromValues(0, 0);
|
||||
const miter = vec2.fromValues(0, 0);
|
||||
let _started = false;
|
||||
let _normal = null;
|
||||
const miter: vec2 = vec2.create();
|
||||
let started = false;
|
||||
let lineNormal = null;
|
||||
const tmp = vec2.create();
|
||||
let count = indexOffset || 0;
|
||||
const miterLimit = 3;
|
||||
|
||||
const out = [];
|
||||
const attrPos = [];
|
||||
const attrIndex = [];
|
||||
const miters = [];
|
||||
const out: number[] = [];
|
||||
const attrPos: number[] = [];
|
||||
const attrIndex: number[] = [];
|
||||
const miters: number[] = [];
|
||||
const attrDistance = [0, 0];
|
||||
if (closed) {
|
||||
points = points.slice();
|
||||
|
@ -76,9 +87,12 @@ export default function(points, closed, indexOffset) {
|
|||
|
||||
for (let i = 1; i < total; i++) {
|
||||
const index = count;
|
||||
const last = points[i - 1];
|
||||
const cur = points[i];
|
||||
let next = i < points.length - 1 ? points[i + 1] : null;
|
||||
const last = vec2.fromValues(points[i - 1][0], points[i - 1][1]);
|
||||
const cur = vec2.fromValues(points[i][0], points[i][1]);
|
||||
let next =
|
||||
i < points.length - 1
|
||||
? vec2.fromValues(points[i + 1][0], points[i + 1][1])
|
||||
: null;
|
||||
// 如果当前点和前一点相同,跳过
|
||||
if (isPointEqual(last, cur)) {
|
||||
continue;
|
||||
|
@ -87,22 +101,23 @@ export default function(points, closed, indexOffset) {
|
|||
let nextIndex = i + 1;
|
||||
// 找到不相同的下一点
|
||||
while (next && isPointEqual(cur, next)) {
|
||||
next = nextIndex < points.length - 1 ? points[++nextIndex] : null;
|
||||
next =
|
||||
nextIndex < points.length - 1
|
||||
? vec2.fromValues(points[++nextIndex][0], points[nextIndex][1])
|
||||
: null;
|
||||
}
|
||||
}
|
||||
const lineDistance = lineSegmentDistance(cur, last);
|
||||
const lineDistance = lineSegmentDistance(cur, last); // TODO: 根据平面坐标计算距离
|
||||
const d = lineDistance + attrDistance[attrDistance.length - 1];
|
||||
|
||||
direction(lineA, cur, last);
|
||||
|
||||
if (!_normal) {
|
||||
_normal = [0, 0];
|
||||
computeNormal(_normal, lineA);
|
||||
if (!lineNormal) {
|
||||
lineNormal = vec2.create();
|
||||
computeNormal(lineNormal, lineA);
|
||||
}
|
||||
|
||||
if (!_started) {
|
||||
_started = true;
|
||||
extrusions(attrPos, out, miters, last, _normal, 1);
|
||||
if (!started) {
|
||||
started = true;
|
||||
extrusions(attrPos, out, miters, last, lineNormal, 1);
|
||||
}
|
||||
|
||||
attrIndex.push(index + 0, index + 2, index + 1);
|
||||
|
@ -110,8 +125,8 @@ export default function(points, closed, indexOffset) {
|
|||
// no miter, simple segment
|
||||
if (!next) {
|
||||
// reset normal
|
||||
computeNormal(_normal, lineA);
|
||||
extrusions(attrPos, out, miters, cur, _normal, 1);
|
||||
computeNormal(lineNormal, lineA);
|
||||
extrusions(attrPos, out, miters, cur, lineNormal, 1);
|
||||
attrDistance.push(d, d);
|
||||
attrIndex.push(index + 1, index + 2, index + 3);
|
||||
count += 2;
|
||||
|
@ -120,23 +135,30 @@ export default function(points, closed, indexOffset) {
|
|||
direction(lineB, next, cur);
|
||||
|
||||
// stores tangent & miter
|
||||
let miterLen = computeMiter(tangent, miter, lineA, lineB, 1);
|
||||
let miterLen = computeMiter(
|
||||
tangent,
|
||||
vec2.fromValues(miter[0], miter[1]),
|
||||
lineA,
|
||||
lineB,
|
||||
1,
|
||||
);
|
||||
|
||||
// get orientation
|
||||
const flip = vec2.dot(tangent, _normal) < 0 ? -1 : 1;
|
||||
const flip = vec2.dot(tangent, lineNormal) < 0 ? -1 : 1;
|
||||
const bevel = Math.abs(miterLen) > miterLimit;
|
||||
|
||||
// 处理前后两条线段重合的情况,这种情况不需要使用任何接头(miter/bevel)。
|
||||
// 理论上这种情况下 miterLen = Infinity,本应通过 isFinite(miterLen) 判断,
|
||||
// 但是 AMap 投影变换后丢失精度,只能通过一个阈值(1000)判断。
|
||||
|
||||
if (Math.abs(miterLen) > 1000) {
|
||||
extrusions(attrPos, out, miters, cur, _normal, 1);
|
||||
extrusions(attrPos, out, miters, cur, lineNormal, 1);
|
||||
attrIndex.push(index + 1, index + 2, index + 3);
|
||||
attrIndex.push(index + 2, index + 4, index + 3);
|
||||
computeNormal(tmp, lineB);
|
||||
vec2.copy(_normal, tmp); // store normal for next round
|
||||
vec2.copy(lineNormal, tmp); // store normal for next round
|
||||
|
||||
extrusions(attrPos, out, miters, cur, _normal, 1);
|
||||
extrusions(attrPos, out, miters, cur, lineNormal, 1);
|
||||
attrDistance.push(d, d, d, d);
|
||||
|
||||
// the miter is now the normal for our next join
|
||||
|
@ -148,7 +170,7 @@ export default function(points, closed, indexOffset) {
|
|||
miterLen = miterLimit;
|
||||
|
||||
// next two points in our first segment
|
||||
extrusions(attrPos, out, miters, cur, _normal, 1);
|
||||
extrusions(attrPos, out, miters, cur, lineNormal, 1);
|
||||
|
||||
attrIndex.push(index + 1, index + 2, index + 3);
|
||||
|
||||
|
@ -160,27 +182,27 @@ export default function(points, closed, indexOffset) {
|
|||
);
|
||||
|
||||
computeNormal(tmp, lineB);
|
||||
vec2.copy(_normal, tmp); // store normal for next round
|
||||
vec2.copy(lineNormal, tmp); // store normal for next round
|
||||
|
||||
extrusions(attrPos, out, miters, cur, _normal, 1);
|
||||
extrusions(attrPos, out, miters, cur, lineNormal, 1);
|
||||
attrDistance.push(d, d, d, d);
|
||||
|
||||
// the miter is now the normal for our next join
|
||||
count += 4;
|
||||
} else {
|
||||
// next two points in our first segment
|
||||
extrusions(attrPos, out, miters, cur, _normal, 1);
|
||||
extrusions(attrPos, out, miters, cur, lineNormal, 1);
|
||||
attrIndex.push(index + 1, index + 2, index + 3);
|
||||
|
||||
// now add the miter triangles
|
||||
addNext(out, miters, miter, miterLen * -flip);
|
||||
attrPos.push(...cur);
|
||||
addNext(out, miters, lineNormal, miterLen * -flip);
|
||||
attrPos.push(cur[0], cur[1], 0);
|
||||
attrIndex.push(index + 2, index + 4, index + 3);
|
||||
attrIndex.push(index + 4, index + 5, index + 6);
|
||||
computeNormal(tmp, lineB);
|
||||
vec2.copy(_normal, tmp); // store normal for next round
|
||||
vec2.copy(lineNormal, tmp); // store normal for next round
|
||||
|
||||
extrusions(attrPos, out, miters, cur, _normal, 1);
|
||||
extrusions(attrPos, out, miters, cur, lineNormal, 1);
|
||||
attrDistance.push(d, d, d, d, d);
|
||||
|
||||
// the miter is now the normal for our next join
|
||||
|
|
|
@ -44,8 +44,8 @@ export default class ReglModel implements IModel {
|
|||
blend,
|
||||
stencil,
|
||||
cull,
|
||||
instances,
|
||||
} = options;
|
||||
|
||||
const reglUniforms: { [key: string]: IUniform } = {};
|
||||
if (uniforms) {
|
||||
this.uniforms = uniforms;
|
||||
|
@ -60,7 +60,6 @@ export default class ReglModel implements IModel {
|
|||
Object.keys(attributes).forEach((name: string) => {
|
||||
reglAttributes[name] = (attributes[name] as ReglAttribute).get();
|
||||
});
|
||||
|
||||
const drawParams: regl.DrawConfig = {
|
||||
attributes: reglAttributes,
|
||||
frag: fs,
|
||||
|
@ -70,6 +69,9 @@ export default class ReglModel implements IModel {
|
|||
primitiveMap[primitive === undefined ? gl.TRIANGLES : primitive],
|
||||
count,
|
||||
};
|
||||
if (instances) {
|
||||
drawParams.instances = instances;
|
||||
}
|
||||
|
||||
if (elements) {
|
||||
drawParams.elements = (elements as ReglElements).get();
|
||||
|
|
|
@ -56,6 +56,7 @@ export default class ReglRendererService implements IRendererService {
|
|||
'EXT_SRGB', // baseColor emmisive
|
||||
'OES_texture_float', // shadow map
|
||||
'WEBGL_depth_texture',
|
||||
'angle_instanced_arrays',
|
||||
'EXT_texture_filter_anisotropic', // VSM shadow map
|
||||
],
|
||||
optionalExtensions: ['oes_texture_float_linear'],
|
||||
|
|
|
@ -1,24 +1,4 @@
|
|||
export type DataType = string | object[] | object;
|
||||
export interface IParserCfg {
|
||||
type: string;
|
||||
x?: string;
|
||||
y?: string;
|
||||
x1?: string;
|
||||
y1?: string;
|
||||
coordinates?: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
type CallBack = (...args: any[]) => any;
|
||||
export interface ITransform {
|
||||
type: string;
|
||||
[key: string]: any;
|
||||
callback: CallBack;
|
||||
}
|
||||
|
||||
export interface ISourceCFG {
|
||||
parser?: IParserCfg;
|
||||
transforms?: ITransform[];
|
||||
}
|
||||
export interface IDictionary<TValue> {
|
||||
[key: string]: TValue;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { csvParse } from 'd3-dsv';
|
||||
import { IJsonData, IParserCfg, IParserData } from '../interface';
|
||||
import { IJsonData, IParserCfg, IParserData } from '@l7/core';
|
||||
import json from './json';
|
||||
export default function csv(data: string, cfg: IParserCfg): IParserData {
|
||||
const csvData: IJsonData = csvParse(data);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { getImage } from '@l7/utils';
|
||||
import { IParserData } from '../interface';
|
||||
|
||||
interface IImageCfg {
|
||||
extent: [number, number, number, number];
|
||||
}
|
||||
|
@ -8,9 +8,13 @@ export default function image(
|
|||
cfg: IImageCfg,
|
||||
): IParserData {
|
||||
const { extent } = cfg;
|
||||
|
||||
const images = new Promise((resolve) => {
|
||||
loadData(data, (res: any) => {
|
||||
resolve(res);
|
||||
});
|
||||
});
|
||||
const resultData: IParserData = {
|
||||
images: loadData(data),
|
||||
images,
|
||||
_id: 1,
|
||||
dataArray: [
|
||||
{
|
||||
|
@ -21,16 +25,26 @@ export default function image(
|
|||
};
|
||||
return resultData;
|
||||
}
|
||||
function loadData(data: string | string[]): Promise<Response | Response[]> {
|
||||
function loadData(data: string | string[], done: any) {
|
||||
const url = data;
|
||||
const imageDatas: HTMLImageElement[] = [];
|
||||
if (typeof url === 'string') {
|
||||
const imageRequest = new Request(url);
|
||||
return fetch(imageRequest);
|
||||
} else {
|
||||
const fetchs = url.map((item: string) => {
|
||||
const imageRequest = new Request(item);
|
||||
return fetch(imageRequest);
|
||||
getImage({ url }, (err: string, img: HTMLImageElement) => {
|
||||
imageDatas.push(img);
|
||||
done(imageDatas);
|
||||
});
|
||||
} else {
|
||||
const imageCount = url.length;
|
||||
let imageindex = 0;
|
||||
url.forEach((item) => {
|
||||
getImage({ url: item }, (err: any, img: HTMLImageElement) => {
|
||||
imageindex++;
|
||||
imageDatas.push(img);
|
||||
if (imageindex === imageCount) {
|
||||
done(imageDatas);
|
||||
}
|
||||
});
|
||||
});
|
||||
return Promise.all(fetchs);
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import {
|
|||
IParseDataItem,
|
||||
IParserCfg,
|
||||
IParserData,
|
||||
} from '../interface';
|
||||
} from '@l7/core';
|
||||
export default function json(data: IJsonData, cfg: IParserCfg): IParserData {
|
||||
const { x, y, x1, y1, coordinates } = cfg;
|
||||
const resultData: IParseDataItem[] = [];
|
||||
|
|
|
@ -36,6 +36,9 @@ export default class Source extends EventEmitter {
|
|||
this.hooks.init.tap('parser', () => {
|
||||
this.excuteParser();
|
||||
});
|
||||
this.hooks.init.tap('transform', () => {
|
||||
this.executeTrans();
|
||||
});
|
||||
this.init();
|
||||
}
|
||||
|
||||
|
@ -60,6 +63,5 @@ export default class Source extends EventEmitter {
|
|||
}
|
||||
private init() {
|
||||
this.hooks.init.call(this);
|
||||
// this.excuteParser(); // 数据解析
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,9 +24,9 @@ export function aggregatorToGrid(data: IParserData, option: ITransform) {
|
|||
const { gridHash, gridOffset } = _pointsGridHash(dataArray, size);
|
||||
const layerData = _getGridLayerDataFromGridHash(gridHash, gridOffset, option);
|
||||
return {
|
||||
yOffset: ((gridOffset.xOffset / 360) * (256 << 20)) / 2,
|
||||
xOffset: ((gridOffset.xOffset / 360) * (256 << 20)) / 2,
|
||||
radius: ((gridOffset.xOffset / 360) * (256 << 20)) / 2,
|
||||
yOffset: gridOffset.yOffset / 1.8,
|
||||
xOffset: gridOffset.xOffset / 1.8,
|
||||
radius: gridOffset.xOffset,
|
||||
dataArray: layerData,
|
||||
};
|
||||
}
|
||||
|
@ -42,8 +42,8 @@ function _pointsGridHash(dataArray: any[], size: number) {
|
|||
latMax = pLat > latMax ? pLat : latMax;
|
||||
}
|
||||
}
|
||||
// const centerLat = (latMin + latMax) / 2;
|
||||
const centerLat = 34.54083;
|
||||
const centerLat = (latMin + latMax) / 2;
|
||||
// const centerLat = 34.54083;
|
||||
const gridOffset = _calculateGridLatLonOffset(size, centerLat);
|
||||
if (gridOffset.xOffset <= 0 || gridOffset.yOffset <= 0) {
|
||||
return { gridHash: {}, gridOffset };
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
class AJAXError extends Error {
|
||||
private status: number;
|
||||
private url: string;
|
||||
|
||||
constructor(message: string, status: number, url: string) {
|
||||
super(message);
|
||||
this.status = status;
|
||||
this.url = url;
|
||||
|
||||
// work around for https://github.com/Rich-Harris/buble/issues/40
|
||||
this.name = this.constructor.name;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public toString() {
|
||||
return `${this.name}: ${this.message} (${this.status}): ${this.url}`;
|
||||
}
|
||||
}
|
||||
|
||||
function makeRequest(requestParameters: any) {
|
||||
const xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.open('GET', requestParameters.url, true);
|
||||
for (const k in requestParameters.headers) {
|
||||
if (requestParameters.headers.hasOwnProperty(k)) {
|
||||
xhr.setRequestHeader(k, requestParameters.headers[k]);
|
||||
}
|
||||
}
|
||||
xhr.withCredentials = requestParameters.credentials === 'include';
|
||||
return xhr;
|
||||
}
|
||||
|
||||
export const getJSON = (requestParameters: any, callback: any) => {
|
||||
const xhr = makeRequest(requestParameters);
|
||||
xhr.setRequestHeader('Accept', 'application/json');
|
||||
xhr.onerror = () => {
|
||||
callback(new Error(xhr.statusText));
|
||||
};
|
||||
xhr.onload = () => {
|
||||
if (xhr.status >= 200 && xhr.status < 300 && xhr.response) {
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(xhr.response);
|
||||
} catch (err) {
|
||||
return callback(err);
|
||||
}
|
||||
callback(null, data);
|
||||
} else {
|
||||
if (xhr.status === 401) {
|
||||
callback(
|
||||
new AJAXError(`${xhr.statusText}`, xhr.status, requestParameters.url),
|
||||
);
|
||||
} else {
|
||||
callback(
|
||||
new AJAXError(xhr.statusText, xhr.status, requestParameters.url),
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.send();
|
||||
return xhr;
|
||||
};
|
||||
|
||||
export const getArrayBuffer = (requestParameters: any, callback: any) => {
|
||||
const xhr = makeRequest(requestParameters);
|
||||
xhr.responseType = 'arraybuffer';
|
||||
xhr.onerror = () => {
|
||||
callback(new Error(xhr.statusText));
|
||||
};
|
||||
xhr.onload = () => {
|
||||
const response = xhr.response;
|
||||
if (response.byteLength === 0 && xhr.status === 200) {
|
||||
return callback(new Error('http status 200 returned without content.'));
|
||||
}
|
||||
if (xhr.status >= 200 && xhr.status < 300 && xhr.response) {
|
||||
callback(null, {
|
||||
data: response,
|
||||
cacheControl: xhr.getResponseHeader('Cache-Control'),
|
||||
expires: xhr.getResponseHeader('Expires'),
|
||||
});
|
||||
} else {
|
||||
callback(
|
||||
new AJAXError(xhr.statusText, xhr.status, requestParameters.url),
|
||||
);
|
||||
}
|
||||
};
|
||||
xhr.send();
|
||||
return xhr;
|
||||
};
|
||||
|
||||
function sameOrigin(url: string) {
|
||||
const a = window.document.createElement('a');
|
||||
a.href = url;
|
||||
return (
|
||||
a.protocol === window.document.location.protocol &&
|
||||
a.host === window.document.location.host
|
||||
);
|
||||
}
|
||||
|
||||
const transparentPngUrl =
|
||||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQYV2NgAAIAAAUAAarVyFEAAAAASUVORK5CYII=';
|
||||
|
||||
export const getImage = (requestParameters: any, callback: any) => {
|
||||
// request the image with XHR to work around caching issues
|
||||
// see https://github.com/mapbox/mapbox-gl-js/issues/1470
|
||||
return getArrayBuffer(requestParameters, (err: string, imgData: any) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
} else if (imgData) {
|
||||
const img = new window.Image();
|
||||
const URL = window.URL || window.webkitURL;
|
||||
img.onload = () => {
|
||||
callback(null, img);
|
||||
URL.revokeObjectURL(img.src);
|
||||
};
|
||||
const blob = new window.Blob([new Uint8Array(imgData.data)], {
|
||||
type: 'image/png',
|
||||
});
|
||||
img.src = imgData.data.byteLength
|
||||
? URL.createObjectURL(blob)
|
||||
: transparentPngUrl;
|
||||
}
|
||||
});
|
||||
};
|
|
@ -1,6 +1,6 @@
|
|||
import { BBox } from '@turf/helpers';
|
||||
const originShift = (2 * Math.PI * 6378137) / 2.0;
|
||||
type Point = [number, number] | [number, number, number];
|
||||
export type Point = [number, number] | [number, number, number];
|
||||
/**
|
||||
* 计算地理数据范围
|
||||
* @param {dataArray} data 地理坐标数据
|
||||
|
@ -137,3 +137,20 @@ export function validateLngLat(lnglat: Point, validate: boolean): Point {
|
|||
}
|
||||
return lnglat.length === 3 ? [lng, lat, lnglat[2]] : [lng, lat];
|
||||
}
|
||||
export function aProjectFlat(lnglat: number[]) {
|
||||
const maxs = 85.0511287798;
|
||||
const lat = Math.max(Math.min(maxs, lnglat[1]), -maxs);
|
||||
const scale = 256 << 20;
|
||||
let d = Math.PI / 180;
|
||||
let x = lnglat[0] * d;
|
||||
let y = lat * d;
|
||||
y = Math.log(Math.tan(Math.PI / 4 + y / 2));
|
||||
|
||||
const a = 0.5 / Math.PI;
|
||||
const b = 0.5;
|
||||
const c = -0.5 / Math.PI;
|
||||
d = 0.5;
|
||||
x = scale * (a * x + b) - 215440491;
|
||||
y = scale * (c * y + d) - 106744817;
|
||||
return [parseInt(x.toString(), 10), parseInt(y.toString(), 10)];
|
||||
}
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
export { djb2hash, BKDRHash } from './hash';
|
||||
export * from './fetchData';
|
||||
export * from './geo';
|
||||
|
|
|
@ -4,6 +4,9 @@ import AMap from './components/AMap';
|
|||
import Mapbox from './components/Mapbox';
|
||||
import Polygon from './components/Polygon';
|
||||
import Point3D from './components/Point3D';
|
||||
import Line from './components/Line';
|
||||
import ImageLayer from './components/Image';
|
||||
import GridHeatMap from './components/GridHeatmap';
|
||||
// @ts-ignore
|
||||
import notes from './Map.md';
|
||||
|
||||
|
@ -15,4 +18,7 @@ storiesOf('地图底图测试', module)
|
|||
notes: { markdown: notes },
|
||||
})
|
||||
.add('Polygon', () => <Polygon />)
|
||||
.add('Point3D', () => <Point3D />);
|
||||
.add('Point3D', () => <Point3D />)
|
||||
.add('Line', () => <Line />)
|
||||
.add('GridHeatMap', () => <GridHeatMap />)
|
||||
.add('Image', () => <ImageLayer />);
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
import { HeatMapLayer } from '@l7/layers';
|
||||
import { Scene } from '@l7/scene';
|
||||
import * as React from 'react';
|
||||
|
||||
export default class GridHeatMap extends React.Component {
|
||||
private scene: Scene;
|
||||
|
||||
public componentWillUnmount() {
|
||||
this.scene.destroy();
|
||||
}
|
||||
|
||||
public async componentDidMount() {
|
||||
const response = await fetch(
|
||||
'https://gw.alipayobjects.com/os/basement_prod/c3f8bda2-081b-449d-aa9f-9413b779205b.json',
|
||||
);
|
||||
const scene = new Scene({
|
||||
center: [116.49434030056, 39.868073421167621],
|
||||
id: 'map',
|
||||
pitch: 0,
|
||||
type: 'amap',
|
||||
style: 'mapbox://styles/mapbox/streets-v9',
|
||||
zoom: 16,
|
||||
});
|
||||
const layer = new HeatMapLayer({});
|
||||
layer
|
||||
.source(await response.json(), {
|
||||
parser: {
|
||||
type: 'json',
|
||||
x: 'lng',
|
||||
y: 'lat',
|
||||
},
|
||||
transforms: [
|
||||
{
|
||||
type: 'grid',
|
||||
size: 50,
|
||||
field: 'count',
|
||||
method: 'sum',
|
||||
},
|
||||
],
|
||||
})
|
||||
.size('sum', (value: number) => {
|
||||
return value;
|
||||
})
|
||||
.shape('circle')
|
||||
.style({
|
||||
coverage: 1.2,
|
||||
angle: 0,
|
||||
})
|
||||
.color('count', [
|
||||
'#002466',
|
||||
'#105CB3',
|
||||
'#2894E0',
|
||||
'#CFF6FF',
|
||||
'#FFF5B8',
|
||||
'#FFAB5C',
|
||||
'#F27049',
|
||||
'#730D1C',
|
||||
]);
|
||||
scene.addLayer(layer);
|
||||
scene.render();
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<div
|
||||
id="map"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
import { ImageLayer } from '@l7/layers';
|
||||
import { Scene } from '@l7/scene';
|
||||
import * as React from 'react';
|
||||
|
||||
export default class ImageLayerDemo extends React.Component {
|
||||
private scene: Scene;
|
||||
|
||||
public componentWillUnmount() {
|
||||
this.scene.destroy();
|
||||
}
|
||||
|
||||
public componentDidMount() {
|
||||
const scene = new Scene({
|
||||
center: [121.2680, 30.3628],
|
||||
id: 'map',
|
||||
pitch: 0,
|
||||
type: 'mapbox',
|
||||
style: 'mapbox://styles/mapbox/streets-v9',
|
||||
zoom: 10,
|
||||
});
|
||||
const layer = new ImageLayer({});
|
||||
layer.source(
|
||||
'https://gw.alipayobjects.com/zos/rmsportal/FnHFeFklTzKDdUESRNDv.jpg',
|
||||
{
|
||||
parser: {
|
||||
type: 'image',
|
||||
extent: [121.168, 30.2828, 121.384, 30.4219],
|
||||
},
|
||||
},
|
||||
);
|
||||
scene.addLayer(layer);
|
||||
scene.render();
|
||||
this.scene = scene;
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<div
|
||||
id="map"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -36,15 +36,29 @@ export default class Point3D extends React.Component {
|
|||
pitch: 0,
|
||||
type: 'mapbox',
|
||||
style: 'mapbox://styles/mapbox/dark-v9',
|
||||
zoom: 2,
|
||||
zoom: 13,
|
||||
});
|
||||
const LineLayer = new Line({});
|
||||
|
||||
LineLayer.source(testdata)
|
||||
.size(5)
|
||||
.color('red')
|
||||
LineLayer.source(await response.json())
|
||||
.size(1)
|
||||
.shape('line')
|
||||
.size(10);
|
||||
.color(
|
||||
'ELEV',
|
||||
[
|
||||
'#E8FCFF',
|
||||
'#CFF6FF',
|
||||
'#A1E9ff',
|
||||
'#65CEF7',
|
||||
'#3CB1F0',
|
||||
'#2894E0',
|
||||
'#1772c2',
|
||||
'#105CB3',
|
||||
'#0D408C',
|
||||
'#002466',
|
||||
].reverse(),
|
||||
)
|
||||
.render();
|
||||
scene.addLayer(LineLayer);
|
||||
// function run() {
|
||||
// scene.render();
|
||||
|
|
|
@ -46,10 +46,6 @@ export default class Point3D extends React.Component {
|
|||
// requestAnimationFrame(run);
|
||||
scene.render();
|
||||
this.scene = scene;
|
||||
console.log(pointLayer);
|
||||
|
||||
// @ts-ignore
|
||||
window.layer = pointLayer;
|
||||
}
|
||||
|
||||
public render() {
|
||||
|
|
12
yarn.lock
12
yarn.lock
|
@ -7295,6 +7295,11 @@ gl-matrix@^3.0.0, gl-matrix@^3.1.0:
|
|||
resolved "https://registry.yarnpkg.com/gl-matrix/-/gl-matrix-3.1.0.tgz#f5b2de17d8fed95a79e5025b10cded0ab9ccbed0"
|
||||
integrity sha512-526NA+3EA+ztAQi0IZpSWiM0fyQXIp7IbRvfJ4wS/TjjQD0uv0fVybXwwqqSOlq33UckivI0yMDlVtboWm3k7A==
|
||||
|
||||
gl-vec2@^1.0.0, gl-vec2@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/gl-vec2/-/gl-vec2-1.3.0.tgz#83d472ed46034de8e09cbc857123fb6c81c51199"
|
||||
integrity sha512-YiqaAuNsheWmUV0Sa8k94kBB0D6RWjwZztyO+trEYS8KzJ6OQB/4686gdrf59wld4hHFIvaxynO3nRxpk1Ij/A==
|
||||
|
||||
glob-parent@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae"
|
||||
|
@ -11387,6 +11392,13 @@ polished@^3.3.1:
|
|||
dependencies:
|
||||
"@babel/runtime" "^7.4.5"
|
||||
|
||||
polyline-miter-util@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/polyline-miter-util/-/polyline-miter-util-1.0.1.tgz#b693f2389ea0ded36a6bcf5ecd2ece4b6917d957"
|
||||
integrity sha1-tpPyOJ6g3tNqa89ezS7OS2kX2Vc=
|
||||
dependencies:
|
||||
gl-vec2 "^1.0.0"
|
||||
|
||||
popper.js@^1.14.4, popper.js@^1.14.7:
|
||||
version "1.15.0"
|
||||
resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.15.0.tgz#5560b99bbad7647e9faa475c6b8056621f5a4ff2"
|
||||
|
|
Loading…
Reference in New Issue