feat(point image): add point image

This commit is contained in:
thinkinggis 2019-10-16 19:31:09 +08:00
parent 557115b12f
commit 9ffc15ddc3
17 changed files with 177 additions and 50 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -59,6 +59,8 @@ export default class BaseLayer implements ILayer {
public styleAttributes: { public styleAttributes: {
[key: string]: Required<ILayerStyleAttribute>; [key: string]: Required<ILayerStyleAttribute>;
} = {}; } = {};
@lazyInject(TYPES.IIconService)
protected readonly iconService: IIconService;
protected layerSource: Source; protected layerSource: Source;
@ -71,9 +73,6 @@ export default class BaseLayer implements ILayer {
@lazyInject(TYPES.IRendererService) @lazyInject(TYPES.IRendererService)
private readonly rendererService: IRendererService; private readonly rendererService: IRendererService;
@lazyInject(TYPES.IIconService)
private readonly iconService: IIconService;
constructor(initializationOptions: Partial<ILayerInitializationOptions>) { constructor(initializationOptions: Partial<ILayerInitializationOptions>) {
this.initializationOptions = initializationOptions; this.initializationOptions = initializationOptions;
} }

View File

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

View File

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

View File

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

View File

@ -1,15 +1,18 @@
precision highp float; precision highp float;
attribute vec3 a_Position; attribute vec3 a_Position;
attribute vec4 a_color; attribute vec4 a_color;
attribute vec2 a_uv;
attribute float a_size; attribute float a_size;
attribute float a_shape; attribute float a_shape;
varying vec4 v_color; varying vec4 v_color;
varying vec2 v_uv; varying vec2 v_uv;
uniform mat4 u_ModelMatrix;
#pragma include "projection" #pragma include "projection"
void main() { void main() {
v_color = a_color; v_color = a_color;
v_uv = a_uv;
vec4 project_pos = project_position(vec4(a_Position, 1.0)); 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; gl_PointSize = a_size;
v_uv = uv;
} }

View File

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

View File

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

View File

@ -7,6 +7,7 @@ import Point3D from './components/Point3D';
import Line from './components/Line'; import Line from './components/Line';
import ImageLayer from './components/Image'; import ImageLayer from './components/Image';
import GridHeatMap from './components/GridHeatmap'; import GridHeatMap from './components/GridHeatmap';
import PointImage from './components/pointImage';
// @ts-ignore // @ts-ignore
import notes from './Map.md'; import notes from './Map.md';
@ -21,4 +22,5 @@ storiesOf('地图底图测试', module)
.add('Point3D', () => <Point3D />) .add('Point3D', () => <Point3D />)
.add('Line', () => <Line />) .add('Line', () => <Line />)
.add('GridHeatMap', () => <GridHeatMap />) .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(); scene.render();
this.scene = scene; console.log(scene);
} }
public render() { public render() {

View File

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