feat(point image): add point image

This commit is contained in:
thinkinggis 2019-10-16 19:31:09 +08:00
parent a995815284
commit 89b25133a1
20 changed files with 180 additions and 55 deletions

View File

@ -6,7 +6,7 @@ import getDecorators from 'inversify-inject-decorators';
import { TYPES } from './types';
/** Service interfaces */
import { IIconService} from './services/asset/IIconService';
import { IIconService } from './services/asset/IIconService';
import { ICameraService } from './services/camera/ICameraService';
import { IGlobalConfigService } from './services/config/IConfigService';
import { ICoordinateSystemService } from './services/coordinate/ICoordinateSystemService';

View File

@ -15,7 +15,10 @@ export interface IICONMap {
[key: string]: IIconValue;
}
export interface IIconService {
canvasHeight: number;
init(): void;
addImage(id: string, image: IImage): void;
getTexture(): ITexture2D;
getIconMap(): IICONMap;
getCanvas(): HTMLCanvasElement;
}

View File

@ -1,5 +1,8 @@
import { inject, injectable } from 'inversify';
import { TYPES } from '../../types';
import { buildIconMaping } from '../../utils/font_util';
import { gl } from '../renderer/gl';
import { IRendererService } from '../renderer/IRendererService';
import { ITexture2D } from '../renderer/ITexture2D';
import {
IIcon,
@ -13,23 +16,24 @@ const MAX_CANVAS_WIDTH = 1024;
const imageSize = 64;
@injectable()
export default class IconService implements IIconService {
public canvasHeight: number;
private textrure: ITexture2D;
private canvas: HTMLCanvasElement;
private iconData: IIcon[];
private iconMap: IICONMap;
private canvasHeigth: number;
private textrure: ITexture2D;
private ctx: CanvasRenderingContext2D;
constructor() {
public init() {
this.iconData = [];
this.iconMap = {};
this.canvas = document.createElement('canvas');
// this.texture =
this.ctx = this.canvas.getContext('2d') as CanvasRenderingContext2D;
}
public async addImage(id: string, image: IImage) {
const imagedata = (await this.loadImage(image)) as HTMLImageElement;
public addImage(id: string, image: IImage) {
let imagedata = new Image();
this.loadImage(image).then((img) => {
imagedata = img as HTMLImageElement;
});
this.iconData.push({
id,
image: imagedata,
@ -42,28 +46,35 @@ export default class IconService implements IIconService {
MAX_CANVAS_WIDTH,
);
this.iconMap = mapping;
this.canvasHeigth = canvasHeight;
this.canvasHeight = canvasHeight;
this.updateIconAtlas();
}
public getTexture(): ITexture2D {
throw new Error('Method not implemented.');
return this.textrure;
}
public getIconMap() {
return this.iconMap;
}
public getCanvas() {
return this.canvas;
}
private updateIconAtlas() {
this.canvas.width = MAX_CANVAS_WIDTH;
this.canvas.height = this.canvasHeigth;
this.canvas.height = this.canvasHeight;
Object.keys(this.iconMap).forEach((item: string) => {
const { x, y, image } = this.iconMap[item];
this.ctx.drawImage(image, x, y, imageSize, imageSize);
});
// this.texture.magFilter = THREE.LinearFilter;
// this.texture.minFilter = THREE.LinearFilter;
// this.texture.needsUpdate = true;
// const { createTexture2D } = this.rendererService;
// this.textrure = createTexture2D({
// data: this.canvas,
// width: this.canvas.width,
// height: this.canvasHeight,
// mag: gl.LINEAR,
// });
}
private loadImage(url: IImage) {
@ -73,6 +84,7 @@ export default class IconService implements IIconService {
return;
}
const image = new Image();
image.crossOrigin = 'anonymous';
image.onload = () => {
resolve(image);
};

View File

@ -33,6 +33,7 @@ export interface ITexture2DInitializationOptions {
*/
data?:
| undefined
| HTMLCanvasElement
| HTMLImageElement
| number[]
| number[][]

View File

@ -1,10 +1,13 @@
import { IImage } from '../asset/IIconService';
import { ILayer } from '../layer/ILayerService';
import { IMapConfig } from '../map/IMapService';
import { IRenderConfig } from '../renderer/IRendererService';
export interface ISceneService {
init(config: IMapConfig & IRenderConfig): void;
addLayer(layer: ILayer): void;
addImage(id: string, image: IImage): void;
render(): void;
destroy(): void;
}

View File

@ -3,6 +3,7 @@ import { inject, injectable } from 'inversify';
import { AsyncParallelHook, AsyncSeriesHook } from 'tapable';
import { TYPES } from '../../types';
import { createRendererContainer } from '../../utils/dom';
import { IIconService, IImage } from '../asset/IIconService';
import { ICameraService, IViewport } from '../camera/ICameraService';
import { IGlobalConfig, IGlobalConfigService } from '../config/IConfigService';
import { IInteractionService } from '../interaction/IInteractionService';
@ -12,7 +13,6 @@ import { IMapCamera, IMapService } from '../map/IMapService';
import { IRendererService } from '../renderer/IRendererService';
import { IShaderModuleService } from '../shader/IShaderModuleService';
import { ISceneService } from './ISceneService';
/**
* will emit `loaded` `resize` `destroy` event
*/
@ -45,6 +45,9 @@ export default class Scene extends EventEmitter implements ISceneService {
@inject(TYPES.IShaderModuleService)
private readonly shaderModule: IShaderModuleService;
@inject(TYPES.IIconService)
private readonly iconService: IIconService;
/**
*
*/
@ -76,6 +79,10 @@ export default class Scene extends EventEmitter implements ISceneService {
public init(globalConfig: IGlobalConfig) {
this.configService.setAndCheckConfig(globalConfig);
// 初始化资源管理 字体,图片
this.iconService.init();
/**
*
*/
@ -151,6 +158,9 @@ export default class Scene extends EventEmitter implements ISceneService {
this.interactionService.destroy();
window.removeEventListener('resize', this.handleWindowResized, false);
}
public addImage(id: string, img: IImage) {
this.iconService.addImage(id, img);
}
private handleWindowResized = () => {
this.emit('resize');
@ -174,7 +184,6 @@ export default class Scene extends EventEmitter implements ISceneService {
this.render();
}
};
private handleMapCameraChanged = (viewport: IViewport) => {
this.cameraService.update(viewport);
this.render();

View File

@ -1,7 +1,6 @@
// Blinn-Phong model
// apply lighting in vertex shader instead of fragment shader
// @see https://learnopengl.com/Advanced-Lighting/Advanced-Lighting
// TODO: support point light、spot light & sun light
uniform float u_ambient : 1.0;
uniform float u_diffuse : 1.0;
uniform float u_specular : 1.0;

View File

@ -59,6 +59,8 @@ export default class BaseLayer implements ILayer {
public styleAttributes: {
[key: string]: Required<ILayerStyleAttribute>;
} = {};
@lazyInject(TYPES.IIconService)
protected readonly iconService: IIconService;
protected layerSource: Source;
@ -71,9 +73,6 @@ export default class BaseLayer implements ILayer {
@lazyInject(TYPES.IRendererService)
private readonly rendererService: IRendererService;
@lazyInject(TYPES.IIconService)
private readonly iconService: IIconService;
constructor(initializationOptions: Partial<ILayerInitializationOptions>) {
this.initializationOptions = initializationOptions;
}
@ -140,7 +139,7 @@ export default class BaseLayer implements ILayer {
return this;
}
public style(options: ILayerStyleOptions): ILayer {
this.styleOption = options; // TODO: merge 默认同类型
this.styleOption = options;
return this;
}
public render(): ILayer {

View File

@ -85,10 +85,9 @@ export default class DataEncodePlugin implements ILayerPlugin {
id: record._id,
coordinates: record.coordinates,
};
// TODO: 数据过滤
Object.keys(attributes).forEach((attributeName: string) => {
const attribute = attributes[attributeName];
// const { type } = attribute; // TODO: 支持常量 或变量
// const { type } = attribute;
// if (type === StyleScaleType.CONSTANT) {
// return;
// }

View File

@ -7,11 +7,12 @@ export default class ImageBuffer extends BaseBuffer {
}
protected buildFeatures() {
const layerData = this.data as IEncodeFeature[];
this.attributes.uv = new Float32Array(this.verticesCount * 2);
layerData.forEach((item: IEncodeFeature, index: number) => {
const { color = [0, 0, 0, 0], size, id, shape, coordinates } = item;
const { x, y } = this.iconMap[shape as string];
const { x, y } = this.iconMap[shape as string] || { x: 0, y: 0 };
const coor = coordinates as Position;
this.attributes.vertices.set([coor[0], coor[1], coor[2] || 0], index * 3);
this.attributes.positions.set(coor, index * 3);
this.attributes.colors.set(color, index * 4);
this.attributes.pickingIds.set([id as number], index);
this.attributes.sizes.set([size as number], index); //

View File

@ -1,5 +1,6 @@
import {
gl,
IIconService,
IRendererService,
IShaderModuleService,
lazyInject,
@ -7,8 +8,11 @@ import {
} from '@l7/core';
import BaseLayer from '../core/BaseLayer';
import ExtrudeBuffer from './buffers/ExtrudeBuffer';
import ImageBuffer from './buffers/ImageBuffer';
import extrude_frag from './shaders/extrude_frag.glsl';
import extrude_vert from './shaders/extrude_vert.glsl';
import image_frag from './shaders/image_frag.glsl';
import image_vert from './shaders/image_vert.glsl';
export default class PointLayer extends BaseLayer {
public name: string = 'PointLayer';
@ -19,6 +23,7 @@ export default class PointLayer extends BaseLayer {
@lazyInject(TYPES.IRendererService)
private readonly renderer: IRendererService;
protected renderModels() {
this.models.forEach((model) =>
model.draw({
@ -35,20 +40,28 @@ export default class PointLayer extends BaseLayer {
vs: extrude_vert,
fs: extrude_frag,
});
this.shaderModule.registerModule('pointImage', {
vs: image_vert,
fs: image_frag,
});
this.models = [];
const { vs, fs, uniforms } = this.shaderModule.getModule('point');
const buffer = new ExtrudeBuffer({
data: this.getEncodedData(),
});
buffer.computeVertexNormals('miters', false);
const { vs, fs, uniforms } = this.shaderModule.getModule('pointImage');
// const buffer = new ExtrudeBuffer({
// data: this.getEncodedData(),
// });
// buffer.computeVertexNormals('miters', false);
const {
createAttribute,
createBuffer,
createElements,
createTexture2D,
createModel,
} = this.renderer;
const buffer = new ImageBuffer({
data: this.getEncodedData(),
iconMap: this.iconService.getIconMap(),
});
this.models.push(
createModel({
attributes: {
@ -78,27 +91,40 @@ export default class PointLayer extends BaseLayer {
data: buffer.attributes.sizes,
type: gl.FLOAT,
}),
size: 3,
size: 1,
}),
a_shape: createAttribute({
a_uv: createAttribute({
buffer: createBuffer({
data: buffer.attributes.miters,
data: buffer.attributes.uv,
type: gl.FLOAT,
}),
size: 3,
size: 2,
}),
// a_shape: createAttribute({
// buffer: createBuffer({
// data: buffer.attributes.miters,
// type: gl.FLOAT,
// }),
// size: 3,
// }),
},
uniforms: {
...uniforms,
u_opacity: this.styleOption.opacity as number,
u_texture: createTexture2D({
data: this.iconService.getCanvas(),
width: 1024,
height: this.iconService.canvasHeight,
}),
},
fs,
vs,
count: buffer.indexArray.length,
elements: createElements({
data: buffer.indexArray,
type: gl.UNSIGNED_INT,
}),
primitive: gl.POINTS,
count: buffer.verticesCount,
// elements: createElements({
// data: buffer.indexArray,
// type: gl.UNSIGNED_INT,
// }),
}),
);
}

View File

@ -1,8 +1,9 @@
uniform sampler2D u_texture;
varying vec4 v_color;
varying vec2 v_uv;
void main(){
vec2 pos=v_uv+gl_PointCoord / 512.*64.;
pos.y=1.-pos.y;
vec2 pos= v_uv + gl_PointCoord / vec2(1024.,128.)*64.;
pos.y= 1.- pos.y;
vec4 textureColor=texture2D(u_texture,pos);
if(v_color == vec4(0.)){
gl_FragColor= textureColor;

View File

@ -1,15 +1,18 @@
precision highp float;
attribute vec3 a_Position;
attribute vec4 a_color;
attribute vec2 a_uv;
attribute float a_size;
attribute float a_shape;
varying vec4 v_color;
varying vec2 v_uv;
uniform mat4 u_ModelMatrix;
#pragma include "projection"
void main() {
v_color = a_color;
v_uv = a_uv;
vec4 project_pos = project_position(vec4(a_Position, 1.0));
gl_Position = project_common_position_to_clipspace(vec4(project_pos, 1.0));
gl_Position = project_common_position_to_clipspace(vec4(project_pos.xyz, 1.0));
gl_PointSize = a_size;
v_uv = uv;
}

View File

@ -107,7 +107,7 @@ export default function(
: null;
}
}
const lineDistance = lineSegmentDistance(cur, last); // TODO: 根据平面坐标计算距离
const lineDistance = lineSegmentDistance(cur, last);
const d = lineDistance + attrDistance[attrDistance.length - 1];
direction(lineA, cur, last);
if (!lineNormal) {

View File

@ -1,5 +1,6 @@
import {
container,
IImage,
ILayer,
IMapConfig,
IMapService,
@ -73,10 +74,14 @@ class Scene {
public render(): void {
this.sceneService.render();
}
public addImage(id: string, img: IImage) {
this.sceneService.addImage(id, img);
}
public destroy() {
this.sceneService.destroy();
}
// 资源管理
}
export { Scene };

View File

@ -108,6 +108,7 @@ export const getImage = (requestParameters: any, callback: any) => {
callback(err);
} else if (imgData) {
const img = new window.Image();
img.crossOrigin = 'anonymous';
const URL = window.URL || window.webkitURL;
img.onload = () => {
callback(null, img);

View File

@ -7,6 +7,7 @@ import Point3D from './components/Point3D';
import Line from './components/Line';
import ImageLayer from './components/Image';
import GridHeatMap from './components/GridHeatmap';
import PointImage from './components/pointImage';
// @ts-ignore
import notes from './Map.md';
@ -21,4 +22,5 @@ storiesOf('地图底图测试', module)
.add('Point3D', () => <Point3D />)
.add('Line', () => <Line />)
.add('GridHeatMap', () => <GridHeatMap />)
.add('Image', () => <ImageLayer />);
.add('Image', () => <ImageLayer />)
.add('pointImage', () => <PointImage />);

View File

@ -28,9 +28,9 @@ export default class ImageLayerDemo extends React.Component {
},
},
);
scene.addLayer(layer);
// scene.addLayer(layer);
scene.render();
this.scene = scene;
console.log(scene);
}
public render() {

View File

@ -19,6 +19,10 @@ export default class Point3D extends React.Component {
style: 'mapbox://styles/mapbox/streets-v9',
zoom: 1,
});
scene.addImage(
'00',
'https://gw.alipayobjects.com/mdn/antv_site/afts/img/A*kzTMQqS2QdUAAAAAAAAAAABkARQnAQ',
);
const pointLayer = new Point({});
const p1 = {
type: 'FeatureCollection',
@ -39,13 +43,8 @@ export default class Point3D extends React.Component {
.shape('scalerank', [ 'triangleColumn', 'squareColumn', 'hexagonColumn' ,'cylinder' ])
.size([25, 10]);
scene.addLayer(pointLayer);
// function run() {
// scene.render();
// requestAnimationFrame(run);
// }
// requestAnimationFrame(run);
scene.render();
this.scene = scene;
}
public render() {

View File

@ -0,0 +1,62 @@
import { Point } from '@l7/layers';
import { Scene } from '@l7/scene';
import * as React from 'react';
import data from './data.json';
export default class PointImage extends React.Component {
private scene: Scene;
public componentWillUnmount() {
this.scene.destroy();
}
public componentDidMount() {
const scene = new Scene({
center: [120.19382669582967, 30.258134],
id: 'map',
pitch: 0,
type: 'mapbox',
style: 'mapbox://styles/mapbox/streets-v9',
zoom: 1,
});
scene.addImage(
'00',
'https://gw.alipayobjects.com/mdn/antv_site/afts/img/A*kzTMQqS2QdUAAAAAAAAAAABkARQnAQ',
);
const pointLayer = new Point({});
const p1 = {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
properties: {},
geometry: {
type: 'Point',
coordinates: [83.671875, 44.84029065139799],
},
},
],
};
pointLayer
.source(data)
// .color('blue')
.shape('00')
.size(14);
scene.addLayer(pointLayer);
scene.render();
}
public render() {
return (
<div
id="map"
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
}}
/>
);
}
}