mirror of https://gitee.com/antv-l7/antv-l7
commit
a2d8894922
|
@ -1,8 +1,8 @@
|
|||
import path from 'path';
|
||||
import alias from '@rollup/plugin-alias';
|
||||
import json from '@rollup/plugin-json';
|
||||
import resolve from 'rollup-plugin-node-resolve';
|
||||
import commonjs from 'rollup-plugin-commonjs';
|
||||
import resolve from 'sharp';
|
||||
import commonjs from '@rollup/plugin-commons';
|
||||
import { terser } from 'rollup-plugin-terser';
|
||||
import analyze from 'rollup-plugin-analyzer';
|
||||
import babel from 'rollup-plugin-babel';
|
||||
|
|
|
@ -6,33 +6,33 @@
|
|||
"demos": [
|
||||
{
|
||||
"filename": "column_dark.js",
|
||||
"title": "",
|
||||
"title": "3D柱图深色",
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/antv_site/afts/img/A*7COqR4wCc6QAAAAAAAAAAABkARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "arcCircle.js",
|
||||
"title": "",
|
||||
"title": "弧线地图",
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/antv_site/afts/img/A*6Qm_QY69sBMAAAAAAAAAAABkARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "bus_dark.js",
|
||||
"title": "",
|
||||
"title": "公交线路深色",
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/antv_site/afts/img/A*j-P8RaJMEvAAAAAAAAAAAABkARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "light.js",
|
||||
"title": "",
|
||||
"title": "蜂窝图3D",
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/antv_site/afts/img/A*SLcGSbvZoEwAAAAAAAAAAABkARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "point.js",
|
||||
"title": "",
|
||||
"title": "海量点",
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/antv_site/afts/img/A*ypZCT6pqv84AAAAAAAAAAABkARQnAQ"
|
||||
}
|
||||
,
|
||||
{
|
||||
"filename": "normal.js",
|
||||
"title": "",
|
||||
"title": "亮度图",
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/antv_site/afts/img/A*xr8BQouXGvoAAAAAAAAAAABkARQnAQ"
|
||||
}
|
||||
]
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
---
|
||||
title: Gallery
|
||||
title: Featured
|
||||
order: 0
|
||||
redirect_from:
|
||||
- /en/examples
|
||||
---
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
---
|
||||
title: Gallery
|
||||
title: 官方精品库
|
||||
order: 0
|
||||
redirect_from:
|
||||
- /zh/examples
|
||||
---
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
title: Gallery
|
||||
order: -1
|
||||
icon: other
|
||||
redirect_from:
|
||||
- /en/examples
|
||||
---
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
title: 所有图表
|
||||
order: -1
|
||||
icon: other
|
||||
redirect_from:
|
||||
- /zh/examples
|
||||
---
|
|
@ -1,42 +0,0 @@
|
|||
import { Scene, LineLayer } from '@antv/l7';
|
||||
import { GaodeMap } from '@antv/l7-maps';
|
||||
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
map: new GaodeMap({
|
||||
pitch: 0,
|
||||
style: 'light',
|
||||
center: [ 104.117492, 36.492696 ],
|
||||
zoom: 3.89
|
||||
})
|
||||
});
|
||||
|
||||
fetch(
|
||||
'https://gw.alipayobjects.com/os/basement_prod/9f6afbcd-3aec-4a26-bd4a-2276d3439e0d.json'
|
||||
)
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
const layer = new LineLayer({})
|
||||
.source(data)
|
||||
.scale('value', {
|
||||
type: 'quantile'
|
||||
})
|
||||
.size('value', [ 0.5, 1, 1.5, 2 ])
|
||||
.shape('line')
|
||||
.color(
|
||||
'value',
|
||||
[
|
||||
'#0A3663',
|
||||
'#1558AC',
|
||||
'#3771D9',
|
||||
'#4D89E5',
|
||||
'#64A5D3',
|
||||
'#72BED6',
|
||||
'#83CED6',
|
||||
'#A6E1E0',
|
||||
'#B8EFE2',
|
||||
'#D7F9F0'
|
||||
].reverse()
|
||||
);
|
||||
scene.addLayer(layer);
|
||||
});
|
|
@ -22,8 +22,8 @@
|
|||
|
||||
}, {
|
||||
"filename": "scatter.js",
|
||||
"title": "定点图",
|
||||
"screenshot":"https://gw.alipayobjects.com/mdn/antv_site/afts/img/A*LnlmQ7sFWigAAAAAAAAAAABkARQnAQ"
|
||||
"title": "气泡图动画",
|
||||
"screenshot":"https://gw.alipayobjects.com/mdn/antv_site/afts/img/A*L-wETZt7WHwAAAAAAAAAAABkARQnAQ"
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -4,15 +4,15 @@ import { GaodeMap } from '@antv/l7-maps';
|
|||
const scene = new Scene({
|
||||
id: 'map',
|
||||
map: new GaodeMap({
|
||||
style: 'light',
|
||||
center: [ -121.24357, 37.58264 ],
|
||||
pitch: 0,
|
||||
zoom: 6.45
|
||||
style: 'dark',
|
||||
center: [ 112, 23.69 ],
|
||||
zoom: 2.5
|
||||
})
|
||||
});
|
||||
|
||||
fetch(
|
||||
'https://gw.alipayobjects.com/os/basement_prod/6c4bb5f2-850b-419d-afc4-e46032fc9f94.csv'
|
||||
'https://gw.alipayobjects.com/os/basement_prod/9078fd36-ce8d-4ee2-91bc-605db8315fdf.csv'
|
||||
)
|
||||
.then(res => res.text())
|
||||
.then(data => {
|
||||
|
@ -25,22 +25,12 @@ fetch(
|
|||
}
|
||||
})
|
||||
.shape('circle')
|
||||
.size(4)
|
||||
.color('Magnitude', [
|
||||
'#0A3663',
|
||||
'#1558AC',
|
||||
'#3771D9',
|
||||
'#4D89E5',
|
||||
'#64A5D3',
|
||||
'#72BED6',
|
||||
'#83CED6',
|
||||
'#A6E1E0',
|
||||
'#B8EFE2',
|
||||
'#D7F9F0'
|
||||
])
|
||||
.active(true)
|
||||
.animate(true)
|
||||
.size(40)
|
||||
.color('#ffa842')
|
||||
.style({
|
||||
opacity: 0.5,
|
||||
strokeWidth: 0
|
||||
opacity: 1
|
||||
});
|
||||
|
||||
scene.addLayer(pointLayer);
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
},
|
||||
{
|
||||
"filename": "locate.js",
|
||||
"title": "顶点符号",
|
||||
"title": "精确符号",
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/antv_site/afts/img/A*DgoWS7-XKdUAAAAAAAAAAABkARQnAQ"
|
||||
},
|
||||
{
|
||||
|
|
|
@ -25,6 +25,10 @@ function pointOnCircle(angle) {
|
|||
}]
|
||||
};
|
||||
}
|
||||
|
||||
scene.on('loaded', () => {
|
||||
// animateMarker(0);
|
||||
});
|
||||
const layer = new PointLayer()
|
||||
.source(pointOnCircle(0))
|
||||
.shape('circle')
|
||||
|
@ -36,11 +40,7 @@ const layer = new PointLayer()
|
|||
opacity: 1
|
||||
});
|
||||
scene.addLayer(layer);
|
||||
function animateMarker(timestamp) {
|
||||
layer.setData(pointOnCircle(timestamp / 1000));
|
||||
requestAnimationFrame(animateMarker);
|
||||
}
|
||||
layer.on('inited', () => {
|
||||
animateMarker(0);
|
||||
});
|
||||
|
||||
// function animateMarker(timestamp) {
|
||||
// layer.setData(pointOnCircle(timestamp / 1000));
|
||||
// requestAnimationFrame(animateMarker);
|
||||
// }
|
||||
|
|
|
@ -1,14 +1,56 @@
|
|||
import { Scene } from '@antv/l7';
|
||||
import { Scene, PointLayer } from '@antv/l7';
|
||||
import { GaodeMap } from '@antv/l7-maps';
|
||||
|
||||
const map = new AMap.Map('container', {
|
||||
resizeEnable: true, // 是否监控地图容器尺寸变化
|
||||
zoom: 11, // 初始化地图层级
|
||||
center: [ 116.397428, 39.90923 ] // 初始化地图中心点
|
||||
});
|
||||
new Scene({
|
||||
id: 'map',
|
||||
map: new GaodeMap({
|
||||
mapInstance: map
|
||||
})
|
||||
});
|
||||
window.onLoad = function() {
|
||||
const map = new AMap.Map('map', {
|
||||
viewMode: '3D',
|
||||
pitch: 0,
|
||||
mapStyle: 'amap://styles/darkblue',
|
||||
center: [ 121.435159, 31.256971 ],
|
||||
zoom: 14.89,
|
||||
minZoom: 10
|
||||
});
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
map: new GaodeMap({
|
||||
mapInstance: map
|
||||
})
|
||||
});
|
||||
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('name', [
|
||||
'circle',
|
||||
'triangle',
|
||||
'square',
|
||||
'pentagon',
|
||||
'hexagon',
|
||||
'octogon',
|
||||
'hexagram',
|
||||
'rhombus',
|
||||
'vesica'
|
||||
])
|
||||
.size('unit_price', [ 10, 25 ])
|
||||
.color('name', [ '#5B8FF9', '#5CCEA1', '#5D7092', '#F6BD16', '#E86452' ])
|
||||
.style({
|
||||
opacity: 0.3,
|
||||
strokeWidth: 2
|
||||
});
|
||||
scene.addLayer(pointLayer);
|
||||
});
|
||||
};
|
||||
const url = 'https://webapi.amap.com/maps?v=1.4.15&key=15cd8a57710d40c9b7c0e3cc120f1200&callback=onLoad';
|
||||
const jsapi = document.createElement('script');
|
||||
jsapi.charset = 'utf-8';
|
||||
jsapi.src = url;
|
||||
document.head.appendChild(jsapi);
|
||||
|
|
|
@ -13,6 +13,11 @@
|
|||
"filename": "mapbox.js",
|
||||
"title": "MapBox底图",
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/antv_site/afts/img/A*_SIYR50bbcoAAAAAAAAAAABkARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "amapInstance.js",
|
||||
"title": "通过高德地图实例化",
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/antv_site/afts/img/A*_SIYR50bbcoAAAAAAAAAAABkARQnAQ"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -33,10 +33,8 @@ module.exports = {
|
|||
title: {
|
||||
zh: '图表演示',
|
||||
en: 'Examples'
|
||||
},
|
||||
redirect: 'gallery/basic'
|
||||
}
|
||||
}
|
||||
// target: '_blank',
|
||||
],
|
||||
docs: [
|
||||
{
|
||||
|
@ -141,8 +139,8 @@ module.exports = {
|
|||
slug: 'gallery',
|
||||
icon: 'gallery',
|
||||
title: {
|
||||
zh: 'Gallery',
|
||||
en: 'Gallery'
|
||||
zh: '官方精品库',
|
||||
en: 'Featured'
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
11
package.json
11
package.json
|
@ -23,12 +23,6 @@
|
|||
"@commitlint/config-conventional": "^8.1.0",
|
||||
"@rollup/plugin-alias": "^2.2.0",
|
||||
"@rollup/plugin-json": "^4.0.0",
|
||||
"@storybook/addon-actions": "^5.1.9",
|
||||
"@storybook/addon-console": "^1.2.1",
|
||||
"@storybook/addon-info": "^5.1.9",
|
||||
"@storybook/addon-knobs": "^5.1.9",
|
||||
"@storybook/addon-notes": "^5.1.9",
|
||||
"@storybook/addon-storysource": "^5.1.11",
|
||||
"@storybook/react": "^5.1.9",
|
||||
"@types/dat.gui": "^0.7.1",
|
||||
"@types/enzyme": "^3.1.14",
|
||||
|
@ -94,8 +88,6 @@
|
|||
"rollup": "^1.27.14",
|
||||
"rollup-plugin-analyzer": "^3.2.2",
|
||||
"rollup-plugin-babel": "^4.3.3",
|
||||
"rollup-plugin-commonjs": "^10.1.0",
|
||||
"rollup-plugin-node-resolve": "^5.2.0",
|
||||
"rollup-plugin-postcss": "^2.0.3",
|
||||
"rollup-plugin-terser": "^5.1.2",
|
||||
"rollup-pluginutils": "^2.8.2",
|
||||
|
@ -173,6 +165,9 @@
|
|||
"path": "cz-conventional-changelog"
|
||||
}
|
||||
},
|
||||
"resolutions": {
|
||||
"../core-js": "3"
|
||||
},
|
||||
"tnpm": {
|
||||
"mode": "yarn"
|
||||
},
|
||||
|
|
|
@ -26,15 +26,10 @@
|
|||
"dependencies": {
|
||||
"@antv/l7-core": "^2.0.0-beta.25",
|
||||
"@antv/l7-utils": "^2.0.0-beta.25",
|
||||
"@babel/runtime": "^7.7.7",
|
||||
"@turf/distance": "^6.0.1",
|
||||
"core-js": "3",
|
||||
"eventemitter3": "^4.0.0",
|
||||
"inversify": "^5.0.1",
|
||||
"inversify-inject-decorators": "^3.1.0",
|
||||
"inversify-logging": "^0.2.1",
|
||||
"load-styles": "^2.0.0",
|
||||
"regenerator-runtime": "^0.13.3"
|
||||
"load-styles": "^2.0.0"
|
||||
},
|
||||
"gitHead": "00d23ef70d9ec76eec26833fc50ac18fe584cf26",
|
||||
"publishConfig": {
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@antv/l7-utils": "^2.0.0-beta.25",
|
||||
"@babel/runtime": "^7.7.7",
|
||||
"@mapbox/tiny-sdf": "^1.1.1",
|
||||
"ajv": "^6.10.2",
|
||||
"core-js": "3",
|
||||
|
|
|
@ -12,7 +12,7 @@ import {
|
|||
} from './IFontService';
|
||||
export const DEFAULT_CHAR_SET = getDefaultCharacterSet();
|
||||
export const DEFAULT_FONT_FAMILY = 'sans-serif';
|
||||
export const DEFAULT_FONT_WEIGHT = 'normal';
|
||||
export const DEFAULT_FONT_WEIGHT = '800';
|
||||
export const DEFAULT_FONT_SIZE = 24;
|
||||
export const DEFAULT_BUFFER = 3;
|
||||
export const DEFAULT_CUTOFF = 0.25;
|
||||
|
|
|
@ -47,7 +47,7 @@ const defaultLayerConfig: Partial<ILayerConfig> = {
|
|||
],
|
||||
shape3d: ['cylinder', 'triangleColumn', 'hexagonColumn', 'squareColumn'],
|
||||
minZoom: 0,
|
||||
maxZoom: 20,
|
||||
maxZoom: 24,
|
||||
visible: true,
|
||||
autoFit: false,
|
||||
zIndex: 0,
|
||||
|
|
|
@ -55,6 +55,7 @@ export interface ILayerModel {
|
|||
getDefaultStyle(): unknown;
|
||||
getAnimateUniforms(): IModelUniform;
|
||||
buildModels(): IModel[];
|
||||
needUpdate(): boolean;
|
||||
}
|
||||
export interface IModelUniform {
|
||||
[key: string]: IUniform;
|
||||
|
|
|
@ -161,7 +161,9 @@ export default class Scene extends EventEmitter implements ISceneService {
|
|||
this.$container as HTMLDivElement,
|
||||
this.handleWindowResized,
|
||||
);
|
||||
// window.addEventListener('resize', this.handleWindowResized, false);
|
||||
window
|
||||
.matchMedia('screen and (-webkit-min-device-pixel-ratio: 1.5)')
|
||||
.addListener(this.handleWindowResized);
|
||||
} else {
|
||||
this.logger.error('容器 id 不存在');
|
||||
}
|
||||
|
@ -227,7 +229,9 @@ export default class Scene extends EventEmitter implements ISceneService {
|
|||
this.rendererService.destroy();
|
||||
this.map.destroy();
|
||||
unbind(this.$container as HTMLDivElement, this.handleWindowResized);
|
||||
// window.removeEventListener('resize', this.handleWindowResized, false);
|
||||
window
|
||||
.matchMedia('screen and (min-resolution: 2dppx)')
|
||||
.removeListener(this.handleWindowResized);
|
||||
}
|
||||
|
||||
private handleWindowResized = () => {
|
||||
|
|
|
@ -28,9 +28,7 @@
|
|||
"@antv/l7-core": "^2.0.0-beta.25",
|
||||
"@antv/l7-layers": "^2.0.0-beta.25",
|
||||
"@antv/l7-maps": "^2.0.0-beta.25",
|
||||
"@antv/l7-scene": "^2.0.0-beta.25",
|
||||
"core-js": "3",
|
||||
"regenerator-runtime": "^0.13.3"
|
||||
"@antv/l7-scene": "^2.0.0-beta.25"
|
||||
},
|
||||
"gitHead": "00d23ef70d9ec76eec26833fc50ac18fe584cf26",
|
||||
"publishConfig": {
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
"@antv/l7-core": "^2.0.0-beta.25",
|
||||
"@antv/l7-source": "^2.0.0-beta.25",
|
||||
"@antv/l7-utils": "^2.0.0-beta.25",
|
||||
"@babel/runtime": "^7.7.7",
|
||||
"@mapbox/martini": "^0.1.0",
|
||||
"@turf/meta": "^6.0.2",
|
||||
"@types/d3-color": "^1.2.2",
|
||||
|
@ -40,7 +39,6 @@
|
|||
"inversify": "^5.0.1",
|
||||
"lodash": "^4.17.15",
|
||||
"merge-json-schemas": "1.0.0",
|
||||
"polyline-miter-util": "^1.0.1",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"regenerator-runtime": "^0.13.3",
|
||||
"tapable": "^2.0.0-beta.8"
|
||||
|
|
|
@ -16,16 +16,6 @@ export default class CityBuildingLayer extends BaseLayer {
|
|||
},
|
||||
};
|
||||
}
|
||||
|
||||
protected renderModels() {
|
||||
this.models.forEach((model) =>
|
||||
model.draw({
|
||||
uniforms: this.layerModel.getUninforms(),
|
||||
}),
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
protected buildModels() {
|
||||
this.layerModel = new CityBuildModel(this);
|
||||
this.models = this.layerModel.buildModels();
|
||||
|
|
|
@ -165,7 +165,9 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
|
|||
|
||||
private scaleOptions: IScaleOptions = {};
|
||||
|
||||
private AnimateStartTime: number;
|
||||
private animateStartTime: number;
|
||||
|
||||
private aniamateStatus: boolean = false;
|
||||
|
||||
constructor(config: Partial<ILayerConfig & ChildLayerStyleOptions> = {}) {
|
||||
super();
|
||||
|
@ -309,6 +311,7 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
|
|||
const { animateOption } = this.getLayerConfig();
|
||||
if (animateOption?.enable) {
|
||||
this.layerService.startAnimate();
|
||||
this.aniamateStatus = true;
|
||||
}
|
||||
this.buildModels();
|
||||
// 触发初始化完成事件;
|
||||
|
@ -730,10 +733,21 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
|
|||
return this.layerService.clock.getDelta();
|
||||
}
|
||||
public setAnimateStartTime() {
|
||||
this.AnimateStartTime = this.layerService.clock.getElapsedTime();
|
||||
this.animateStartTime = this.layerService.clock.getElapsedTime();
|
||||
}
|
||||
public stopAnimate() {
|
||||
if (this.aniamateStatus) {
|
||||
this.layerService.stopAnimate();
|
||||
this.aniamateStatus = false;
|
||||
this.updateLayerConfig({
|
||||
animateOption: {
|
||||
enable: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
public getLayerAnimateTime(): number {
|
||||
return this.layerService.clock.getElapsedTime() - this.AnimateStartTime;
|
||||
return this.layerService.clock.getElapsedTime() - this.animateStartTime;
|
||||
}
|
||||
|
||||
protected getConfigSchema() {
|
||||
|
@ -745,7 +759,16 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
|
|||
}
|
||||
|
||||
protected renderModels() {
|
||||
throw new Error('Method not implemented.');
|
||||
if (this.layerModelNeedUpdate) {
|
||||
this.models = this.layerModel.buildModels();
|
||||
this.layerModelNeedUpdate = false;
|
||||
}
|
||||
this.models.forEach((model) => {
|
||||
model.draw({
|
||||
uniforms: this.layerModel.getUninforms(),
|
||||
});
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
protected getModelType(): unknown {
|
||||
|
|
|
@ -77,6 +77,9 @@ export default class BaseModel<ChildLayerStyleOptions = {}>
|
|||
return {};
|
||||
}
|
||||
|
||||
public needUpdate(): boolean {
|
||||
return false;
|
||||
}
|
||||
public buildModels(): IModel[] {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import { IEncodeFeature } from '@antv/l7-core';
|
|||
import { aProjectFlat, lngLatToMeters } from '@antv/l7-utils';
|
||||
import earcut from 'earcut';
|
||||
import { vec3 } from 'gl-matrix';
|
||||
import { calculteCentroid } from '../utils/geo';
|
||||
import getNormals from '../utils/polylineNormal';
|
||||
import extrudePolygon, {
|
||||
extrude_PolygonNormal,
|
||||
|
@ -24,7 +25,7 @@ const GeometryCache: IGeometryCache = {};
|
|||
* @param feature 映射feature
|
||||
*/
|
||||
export function PointFillTriangulation(feature: IEncodeFeature) {
|
||||
const coordinates = feature.coordinates as number[];
|
||||
const coordinates = calculteCentroid(feature.coordinates);
|
||||
return {
|
||||
vertices: [...coordinates, ...coordinates, ...coordinates, ...coordinates],
|
||||
indices: [0, 1, 2, 2, 3, 0],
|
||||
|
@ -46,7 +47,6 @@ export function PointExtrudeTriangulation(feature: IEncodeFeature) {
|
|||
vertices: positions,
|
||||
indices: index,
|
||||
normals,
|
||||
// normals: Array.from(computeVertexNormals(positions, index, 3, false)),
|
||||
size: 5,
|
||||
};
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ export function PointExtrudeTriangulation(feature: IEncodeFeature) {
|
|||
* @param feature 映射feature
|
||||
*/
|
||||
export function PointImageTriangulation(feature: IEncodeFeature) {
|
||||
const coordinates = feature.coordinates as number[];
|
||||
const coordinates = calculteCentroid(feature.coordinates);
|
||||
return {
|
||||
vertices: [...coordinates],
|
||||
indices: [0],
|
||||
|
|
|
@ -21,8 +21,12 @@ export default class HeatMapLayer extends BaseLayer<IHeatMapLayerStyleOptions> {
|
|||
protected renderModels() {
|
||||
const shape = this.getModelType();
|
||||
if (shape === 'heatmap') {
|
||||
this.layerModel.render();
|
||||
return;
|
||||
this.layerModel.render(); // 独立的渲染流程
|
||||
return this;
|
||||
}
|
||||
if (this.layerModelNeedUpdate) {
|
||||
this.models = this.layerModel.buildModels();
|
||||
this.layerModelNeedUpdate = false;
|
||||
}
|
||||
this.models.forEach((model) =>
|
||||
model.draw({
|
||||
|
|
|
@ -20,8 +20,8 @@ import MultiPassRendererPlugin from './plugins/MultiPassRendererPlugin';
|
|||
import PixelPickingPlugin from './plugins/PixelPickingPlugin';
|
||||
import RegisterStyleAttributePlugin from './plugins/RegisterStyleAttributePlugin';
|
||||
import ShaderUniformPlugin from './plugins/ShaderUniformPlugin';
|
||||
import UpdateModelPlugin from './plugins/UpdateModelPlugin';
|
||||
import UpdateStyleAttributePlugin from './plugins/UpdateStyleAttributePlugin';
|
||||
|
||||
/**
|
||||
* 校验传入参数配置项的正确性
|
||||
* @see /dev-docs/ConfigSchemaValidation.md
|
||||
|
@ -74,6 +74,15 @@ container
|
|||
.bind<ILayerPlugin>(TYPES.ILayerPlugin)
|
||||
.to(UpdateStyleAttributePlugin)
|
||||
.inRequestScope();
|
||||
|
||||
/**
|
||||
* 负责Model更新
|
||||
*/
|
||||
container
|
||||
.bind<ILayerPlugin>(TYPES.ILayerPlugin)
|
||||
.to(UpdateModelPlugin)
|
||||
.inRequestScope();
|
||||
|
||||
/**
|
||||
* Multi Pass 自定义渲染管线
|
||||
*/
|
||||
|
|
|
@ -5,8 +5,6 @@ import LineModels, { LineModelType } from './models';
|
|||
export default class LineLayer extends BaseLayer<ILineLayerStyleOptions> {
|
||||
public type: string = 'LineLayer';
|
||||
|
||||
private animateStartTime: number = 0;
|
||||
|
||||
protected getConfigSchema() {
|
||||
return {
|
||||
properties: {
|
||||
|
@ -28,14 +26,6 @@ export default class LineLayer extends BaseLayer<ILineLayerStyleOptions> {
|
|||
};
|
||||
return defaultConfig[type];
|
||||
}
|
||||
protected renderModels() {
|
||||
this.models.forEach((model) =>
|
||||
model.draw({
|
||||
uniforms: this.layerModel.getUninforms(),
|
||||
}),
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
protected buildModels() {
|
||||
const shape = this.getModelType();
|
||||
|
@ -49,12 +39,4 @@ export default class LineLayer extends BaseLayer<ILineLayerStyleOptions> {
|
|||
const shape = shapeAttribute?.scale?.field as LineModelType;
|
||||
return shape || 'line';
|
||||
}
|
||||
// 拆分的动画插件
|
||||
private initAnimate() {
|
||||
const { enable } = this.animateOptions;
|
||||
if (enable) {
|
||||
this.layerService.startAnimate();
|
||||
this.animateStartTime = this.layerService.clock.getElapsedTime();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,9 @@ export default class LayerAnimateStylePlugin implements ILayerPlugin {
|
|||
private readonly rendererService: IRendererService;
|
||||
|
||||
public apply(layer: ILayer) {
|
||||
// layer.hooks.beforeRender.tap('LayerAnimateStylePlugin', () => {
|
||||
|
||||
// })
|
||||
layer.hooks.beforeRender.tap('LayerAnimateStylePlugin', () => {
|
||||
// 重新计算坐标系参数
|
||||
layer.models.forEach((model: IModel) => {
|
||||
|
|
|
@ -2,6 +2,9 @@ import { ILayer, ILayerPlugin, IMapService, TYPES } from '@antv/l7-core';
|
|||
import Source from '@antv/l7-source';
|
||||
import { encodePickingColor, rgb2arr } from '@antv/l7-utils';
|
||||
import { injectable } from 'inversify';
|
||||
/**
|
||||
* 更新图层样式,初始图层相关配置
|
||||
*/
|
||||
@injectable()
|
||||
export default class LayerStylePlugin implements ILayerPlugin {
|
||||
public apply(layer: ILayer) {
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
import { ILayer, ILayerPlugin, IMapService, TYPES } from '@antv/l7-core';
|
||||
import { injectable } from 'inversify';
|
||||
/**
|
||||
* Model 更新
|
||||
*/
|
||||
@injectable()
|
||||
export default class UpdateModelPlugin implements ILayerPlugin {
|
||||
public apply(layer: ILayer) {
|
||||
layer.hooks.beforeRender.tap('UpdateModelPlugin', () => {
|
||||
// 处理文本更新
|
||||
if (layer.layerModel) {
|
||||
layer.layerModel.needUpdate();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -28,23 +28,12 @@ export default class PointLayer extends BaseLayer<IPointLayerStyleOptions> {
|
|||
fill: {},
|
||||
extrude: {},
|
||||
image: {},
|
||||
text: {},
|
||||
text: {
|
||||
blend: 'normal',
|
||||
},
|
||||
};
|
||||
return defaultConfig[type];
|
||||
}
|
||||
protected renderModels() {
|
||||
if (this.layerModelNeedUpdate) {
|
||||
this.models = this.layerModel.buildModels();
|
||||
this.layerModelNeedUpdate = false;
|
||||
}
|
||||
this.models.forEach((model) => {
|
||||
model.draw({
|
||||
uniforms: this.layerModel.getUninforms(),
|
||||
});
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
protected buildModels() {
|
||||
const modelType = this.getModelType();
|
||||
this.layerModel = new PointModels[modelType](this);
|
||||
|
@ -79,9 +68,4 @@ export default class PointLayer extends BaseLayer<IPointLayerStyleOptions> {
|
|||
return 'text';
|
||||
}
|
||||
}
|
||||
|
||||
private updateData() {
|
||||
// const bounds = this.mapService.getBounds();
|
||||
// console.log(bounds);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { AttributeType, gl, IEncodeFeature, IModel } from '@antv/l7-core';
|
||||
import BaseModel from '../../core/BaseModel';
|
||||
import { PointExtrudeTriangulation } from '../../core/triangulation';
|
||||
import { calculteCentroid } from '../../utils/geo';
|
||||
import pointExtrudeFrag from '../shaders/extrude_frag.glsl';
|
||||
import pointExtrudeVert from '../shaders/extrude_vert.glsl';
|
||||
interface IPointLayerStyleOptions {
|
||||
|
@ -21,15 +22,7 @@ export default class ExtrudeModel extends BaseModel {
|
|||
vertexShader: pointExtrudeVert,
|
||||
fragmentShader: pointExtrudeFrag,
|
||||
triangulation: PointExtrudeTriangulation,
|
||||
blend: {
|
||||
enable: true,
|
||||
func: {
|
||||
srcRGB: gl.SRC_ALPHA,
|
||||
srcAlpha: 1,
|
||||
dstRGB: gl.ONE_MINUS_SRC_ALPHA,
|
||||
dstAlpha: 1,
|
||||
},
|
||||
},
|
||||
blend: this.getBlend(),
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
@ -108,7 +101,7 @@ export default class ExtrudeModel extends BaseModel {
|
|||
},
|
||||
size: 3,
|
||||
update: (feature: IEncodeFeature, featureIdx: number) => {
|
||||
const coordinates = feature.coordinates as number[];
|
||||
const coordinates = calculteCentroid(feature.coordinates);
|
||||
return [coordinates[0], coordinates[1], 0];
|
||||
},
|
||||
},
|
||||
|
|
|
@ -42,15 +42,7 @@ export default class ImageModel extends BaseModel {
|
|||
triangulation: PointImageTriangulation,
|
||||
primitive: gl.POINTS,
|
||||
depth: { enable: false },
|
||||
blend: {
|
||||
enable: true,
|
||||
func: {
|
||||
srcRGB: gl.SRC_ALPHA,
|
||||
srcAlpha: 1,
|
||||
dstRGB: gl.ONE_MINUS_SRC_ALPHA,
|
||||
dstAlpha: 1,
|
||||
},
|
||||
},
|
||||
blend: this.getBlend(),
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import {
|
|||
BlendType,
|
||||
gl,
|
||||
IEncodeFeature,
|
||||
ILayer,
|
||||
ILayerConfig,
|
||||
IModel,
|
||||
IModelUniform,
|
||||
|
@ -10,7 +11,8 @@ import {
|
|||
} from '@antv/l7-core';
|
||||
import { rgb2arr } from '@antv/l7-utils';
|
||||
import BaseModel from '../../core/BaseModel';
|
||||
import { PointFillTriangulation } from '../../core/triangulation';
|
||||
import CollisionIndex from '../../utils/collision-index';
|
||||
import { calculteCentroid } from '../../utils/geo';
|
||||
import {
|
||||
getGlyphQuads,
|
||||
IGlyphQuad,
|
||||
|
@ -32,14 +34,12 @@ interface IPointTextLayerStyleOptions {
|
|||
textAllowOverlap: boolean;
|
||||
}
|
||||
export function TextTriangulation(feature: IEncodeFeature) {
|
||||
const coordinates = feature.coordinates as number[];
|
||||
const centroid = feature.centroid as number[]; // 计算中心点
|
||||
const { glyphQuads } = feature;
|
||||
const vertices: number[] = [];
|
||||
const indices: number[] = [];
|
||||
const coord =
|
||||
coordinates.length === 2
|
||||
? [coordinates[0], coordinates[1], 0]
|
||||
: coordinates;
|
||||
centroid.length === 2 ? [centroid[0], centroid[1], 0] : centroid;
|
||||
glyphQuads.forEach((quad: IGlyphQuad, index: number) => {
|
||||
vertices.push(
|
||||
...coord,
|
||||
|
@ -81,14 +81,18 @@ export function TextTriangulation(feature: IEncodeFeature) {
|
|||
|
||||
export default class TextModel extends BaseModel {
|
||||
private texture: ITexture2D;
|
||||
private glyphInfo: IEncodeFeature[];
|
||||
private currentZoom: number = -1;
|
||||
private extent: [[number, number], [number, number]];
|
||||
|
||||
public getUninforms(): IModelUniform {
|
||||
const {
|
||||
fontWeight = 'normal',
|
||||
fontWeight = 800,
|
||||
fontFamily,
|
||||
stroke,
|
||||
strokeWidth,
|
||||
stroke = '#fff',
|
||||
strokeWidth = 0,
|
||||
} = this.layer.getLayerConfig() as IPointTextLayerStyleOptions;
|
||||
const { canvas, fontAtlas, mapping } = this.fontService;
|
||||
const { canvas } = this.fontService;
|
||||
return {
|
||||
u_opacity: 1.0,
|
||||
u_sdf_map: this.texture,
|
||||
|
@ -100,10 +104,9 @@ export default class TextModel extends BaseModel {
|
|||
}
|
||||
|
||||
public buildModels(): IModel[] {
|
||||
this.initTextFont();
|
||||
this.generateGlyphLayout();
|
||||
this.registerBuiltinAttributes();
|
||||
this.initGlyph();
|
||||
this.updateTexture();
|
||||
this.filterGlyphs();
|
||||
return [
|
||||
this.layer.buildLayerModel({
|
||||
moduleName: 'pointText',
|
||||
|
@ -115,9 +118,36 @@ export default class TextModel extends BaseModel {
|
|||
}),
|
||||
];
|
||||
}
|
||||
public needUpdate() {
|
||||
const {
|
||||
textAllowOverlap = false,
|
||||
} = this.layer.getLayerConfig() as IPointTextLayerStyleOptions;
|
||||
const zoom = this.mapService.getZoom();
|
||||
const extent = this.mapService.getBounds();
|
||||
const flag =
|
||||
extent[0][0] < this.extent[0][0] ||
|
||||
extent[0][1] < this.extent[0][1] ||
|
||||
extent[1][0] > this.extent[1][0] ||
|
||||
extent[1][1] < this.extent[1][1];
|
||||
|
||||
if (!textAllowOverlap && (Math.abs(this.currentZoom - zoom) > 1 || flag)) {
|
||||
this.filterGlyphs();
|
||||
this.layer.models = [
|
||||
this.layer.buildLayerModel({
|
||||
moduleName: 'pointText',
|
||||
vertexShader: textVert,
|
||||
fragmentShader: textFrag,
|
||||
triangulation: TextTriangulation,
|
||||
depth: { enable: false },
|
||||
blend: this.getBlend(),
|
||||
}),
|
||||
];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected registerBuiltinAttributes() {
|
||||
const viewProjection = this.cameraService.getViewProjectionMatrix();
|
||||
this.styleAttributeService.registerStyleAttribute({
|
||||
name: 'textOffsets',
|
||||
type: AttributeType.Attribute,
|
||||
|
@ -190,9 +220,21 @@ export default class TextModel extends BaseModel {
|
|||
},
|
||||
});
|
||||
}
|
||||
private textExtent(): [[number, number], [number, number]] {
|
||||
const bounds = this.mapService.getBounds();
|
||||
const step =
|
||||
Math.min(bounds[1][0] - bounds[0][0], bounds[1][1] - bounds[1][0]) / 2;
|
||||
return [
|
||||
[bounds[0][0] - step, bounds[0][1] - step],
|
||||
[bounds[1][0] + step, bounds[1][1] + step],
|
||||
];
|
||||
}
|
||||
/**
|
||||
* 生成文字纹理
|
||||
*/
|
||||
private initTextFont() {
|
||||
const {
|
||||
fontWeight = 'normal',
|
||||
fontWeight = '800',
|
||||
fontFamily,
|
||||
} = this.layer.getLayerConfig() as IPointTextLayerStyleOptions;
|
||||
const data = this.layer.getEncodedData();
|
||||
|
@ -213,20 +255,19 @@ export default class TextModel extends BaseModel {
|
|||
fontFamily,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 生成文字布局
|
||||
*/
|
||||
private generateGlyphLayout() {
|
||||
const { canvas, fontAtlas, mapping } = this.fontService;
|
||||
const { mapping } = this.fontService;
|
||||
const {
|
||||
spacing = 2,
|
||||
textAnchor = 'center',
|
||||
textOffset,
|
||||
padding = [4, 4],
|
||||
textAllowOverlap,
|
||||
} = this.layer.getLayerConfig() as IPointTextLayerStyleOptions;
|
||||
const data = this.layer.getEncodedData();
|
||||
data.forEach((feature: IEncodeFeature) => {
|
||||
const { coordinates, shape = '' } = feature;
|
||||
const size = feature.size as number;
|
||||
const fontScale = size / 24;
|
||||
this.glyphInfo = data.map((feature: IEncodeFeature) => {
|
||||
const { shape = '', coordinates } = feature;
|
||||
const shaping = shapeText(
|
||||
shape.toString(),
|
||||
mapping,
|
||||
|
@ -239,19 +280,61 @@ export default class TextModel extends BaseModel {
|
|||
const glyphQuads = getGlyphQuads(shaping, textOffset, false);
|
||||
feature.shaping = shaping;
|
||||
feature.glyphQuads = glyphQuads;
|
||||
feature.centroid = calculteCentroid(coordinates);
|
||||
return feature;
|
||||
});
|
||||
}
|
||||
|
||||
private drawGlyph() {
|
||||
/**
|
||||
* 文字避让
|
||||
*/
|
||||
private filterGlyphs() {
|
||||
const {
|
||||
spacing = 2,
|
||||
textAnchor = 'center',
|
||||
textOffset = [0, 0],
|
||||
padding = [4, 4],
|
||||
textAllowOverlap,
|
||||
textAllowOverlap = false,
|
||||
} = this.layer.getLayerConfig() as IPointTextLayerStyleOptions;
|
||||
const viewProjection = this.cameraService.getViewProjectionMatrix();
|
||||
if (textAllowOverlap) {
|
||||
return;
|
||||
}
|
||||
this.currentZoom = this.mapService.getZoom();
|
||||
this.extent = this.textExtent();
|
||||
const { width, height } = this.rendererService.getViewportSize();
|
||||
const collisionIndex = new CollisionIndex(width, height);
|
||||
const filterData = this.glyphInfo.filter((feature: IEncodeFeature) => {
|
||||
const { shaping, id = 0 } = feature;
|
||||
const centroid = feature.centroid as [number, number];
|
||||
const size = feature.size as number;
|
||||
const fontScale: number = size / 24;
|
||||
const pixels = this.mapService.lngLatToContainer(centroid);
|
||||
const { box } = collisionIndex.placeCollisionBox({
|
||||
x1: shaping.left * fontScale - padding[0],
|
||||
x2: shaping.right * fontScale + padding[0],
|
||||
y1: shaping.top * fontScale - padding[1],
|
||||
y2: shaping.bottom * fontScale + padding[1],
|
||||
anchorPointX: pixels.x,
|
||||
anchorPointY: pixels.y,
|
||||
});
|
||||
if (box && box.length) {
|
||||
// TODO:featureIndex
|
||||
collisionIndex.insertCollisionBox(box, id);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
this.layer.setEncodedData(filterData);
|
||||
}
|
||||
/**
|
||||
* 初始化文字布局
|
||||
*/
|
||||
private initGlyph() {
|
||||
// 1.生成文字纹理
|
||||
this.initTextFont();
|
||||
// 2.生成文字布局
|
||||
this.generateGlyphLayout();
|
||||
}
|
||||
/**
|
||||
* 更新文字纹理
|
||||
*/
|
||||
private updateTexture() {
|
||||
const { createTexture2D } = this.rendererService;
|
||||
const { canvas } = this.fontService;
|
||||
|
|
|
@ -27,7 +27,7 @@ void main() {
|
|||
vec4 projected_position = project_common_position_to_clipspace(vec4(project_pos.xyz, 1.0));
|
||||
|
||||
gl_Position = vec4(projected_position.xy / projected_position.w
|
||||
+ a_textOffsets * fontScale / u_ViewportSize * 2., 0.0, 1.0);
|
||||
+ a_textOffsets * fontScale / u_ViewportSize * 2. * u_DevicePixelRatio, 0.0, 1.0);
|
||||
v_gamma_scale = gl_Position.w;
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { IEncodeFeature } from '@antv/l7-core';
|
||||
import BaseLayer from '../core/BaseLayer';
|
||||
import { PointType } from '../point/models/';
|
||||
import PolygonModels, { PolygonModelType } from './models/';
|
||||
|
||||
interface IPolygonLayerStyleOptions {
|
||||
|
@ -20,16 +21,6 @@ export default class PolygonLayer extends BaseLayer<IPolygonLayerStyleOptions> {
|
|||
},
|
||||
};
|
||||
}
|
||||
|
||||
protected renderModels() {
|
||||
this.models.forEach((model) =>
|
||||
model.draw({
|
||||
uniforms: this.layerModel.getUninforms(),
|
||||
}),
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
protected buildModels() {
|
||||
const shape = this.getModelType();
|
||||
this.layerModel = new PolygonModels[shape](this);
|
||||
|
@ -41,6 +32,42 @@ export default class PolygonLayer extends BaseLayer<IPolygonLayerStyleOptions> {
|
|||
'shape',
|
||||
);
|
||||
const shape = shapeAttribute?.scale?.field as PolygonModelType;
|
||||
return shape || 'fill';
|
||||
if (shape === 'fill') {
|
||||
return 'fill';
|
||||
} else if (shape === 'extrude') {
|
||||
return 'extrude';
|
||||
} else if (shape === 'line') {
|
||||
return 'line';
|
||||
} else {
|
||||
return this.getPointModelType();
|
||||
}
|
||||
}
|
||||
protected getPointModelType(): PolygonModelType {
|
||||
// pointlayer
|
||||
// 2D、 3d、 shape、image、text、normal、
|
||||
const layerData = this.getEncodedData();
|
||||
const { shape2d, shape3d } = this.getLayerConfig();
|
||||
const iconMap = this.iconService.getIconMap();
|
||||
const item = layerData.find((fe: IEncodeFeature) => {
|
||||
return fe.hasOwnProperty('shape');
|
||||
});
|
||||
if (!item) {
|
||||
return 'fill';
|
||||
} else {
|
||||
const shape = item.shape;
|
||||
if (shape === 'dot') {
|
||||
return 'point_normal';
|
||||
}
|
||||
if (shape2d?.indexOf(shape as string) !== -1) {
|
||||
return 'point_fill';
|
||||
}
|
||||
if (shape3d?.indexOf(shape as string) !== -1) {
|
||||
return 'point_extrude';
|
||||
}
|
||||
if (iconMap.hasOwnProperty(shape as string)) {
|
||||
return 'point_image';
|
||||
}
|
||||
return 'text';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,32 @@
|
|||
import LineModel from '../../line/models/line';
|
||||
import PointExtrudeModel from '../../point/models/extrude';
|
||||
import PointFillModel from '../../point/models/fill';
|
||||
import IMageModel from '../../point/models/image';
|
||||
import NormalModel from '../../point/models/normal';
|
||||
import TextModel from '../../point/models/text';
|
||||
import ExtrudeModel from './extrude';
|
||||
import FillModel from './fill';
|
||||
|
||||
export type PolygonModelType = 'fill' | 'extrude' | 'line';
|
||||
export type PolygonModelType =
|
||||
| 'fill'
|
||||
| 'extrude'
|
||||
| 'line'
|
||||
| 'point_fill'
|
||||
| 'point_image'
|
||||
| 'point_normal'
|
||||
| 'point_extrude'
|
||||
| 'text';
|
||||
|
||||
const PolygonModels: { [key in PolygonModelType]: any } = {
|
||||
fill: FillModel,
|
||||
line: LineModel,
|
||||
extrude: ExtrudeModel,
|
||||
};
|
||||
text: TextModel,
|
||||
point_fill: PointFillModel,
|
||||
point_image: IMageModel,
|
||||
point_normal: NormalModel,
|
||||
point_extrude: PointExtrudeModel,
|
||||
|
||||
// point_fill: PointModels.fill,
|
||||
};
|
||||
export default PolygonModels;
|
||||
|
|
|
@ -9,10 +9,6 @@ export interface ICollisionBox {
|
|||
// @mapbox/grid-index 并没有类似 hitTest 的单纯获取碰撞检测结果的方法,query 将导致计算大量多余的包围盒结果,因此使用改良版
|
||||
import { mat4, vec4 } from 'gl-matrix';
|
||||
import GridIndex from './grid-index';
|
||||
|
||||
// 为 viewport 加上 buffer,避免边缘处的文本无法显示
|
||||
const viewportPadding = 100;
|
||||
|
||||
/**
|
||||
* 基于网格实现文本避让,大幅提升包围盒碰撞检测效率
|
||||
* @see https://zhuanlan.zhihu.com/p/74373214
|
||||
|
@ -21,6 +17,7 @@ export default class CollisionIndex {
|
|||
private width: number;
|
||||
private height: number;
|
||||
private grid: GridIndex;
|
||||
private viewportPadding: number = 100;
|
||||
private screenRightBoundary: number;
|
||||
private screenBottomBoundary: number;
|
||||
private gridRightBoundary: number;
|
||||
|
@ -28,30 +25,35 @@ export default class CollisionIndex {
|
|||
constructor(width: number, height: number) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.viewportPadding = Math.max(width, height);
|
||||
// 创建网格索引
|
||||
this.grid = new GridIndex(
|
||||
width + 2 * viewportPadding,
|
||||
height + 2 * viewportPadding,
|
||||
width + this.viewportPadding,
|
||||
height + this.viewportPadding,
|
||||
25,
|
||||
);
|
||||
|
||||
this.screenRightBoundary = width + viewportPadding;
|
||||
this.screenBottomBoundary = height + viewportPadding;
|
||||
this.gridRightBoundary = width + 2 * viewportPadding;
|
||||
this.gridBottomBoundary = height + 2 * viewportPadding;
|
||||
this.screenRightBoundary = width + this.viewportPadding;
|
||||
this.screenBottomBoundary = height + this.viewportPadding;
|
||||
this.gridRightBoundary = width + 2 * this.viewportPadding;
|
||||
this.gridBottomBoundary = height + 2 * this.viewportPadding;
|
||||
}
|
||||
|
||||
public placeCollisionBox(collisionBox: ICollisionBox, mvpMatrix: mat4) {
|
||||
const projectedPoint = this.project(
|
||||
mvpMatrix,
|
||||
collisionBox.anchorPointX,
|
||||
collisionBox.anchorPointY,
|
||||
);
|
||||
public placeCollisionBox(collisionBox: ICollisionBox) {
|
||||
// const projectedPoint = this.project(
|
||||
// mvpMatrix,
|
||||
// collisionBox.anchorPointX,
|
||||
// collisionBox.anchorPointY,
|
||||
// );
|
||||
|
||||
const tlX = collisionBox.x1 + projectedPoint.x;
|
||||
const tlY = collisionBox.y1 + projectedPoint.y;
|
||||
const brX = collisionBox.x2 + projectedPoint.x;
|
||||
const brY = collisionBox.y2 + projectedPoint.y;
|
||||
const tlX =
|
||||
collisionBox.x1 + collisionBox.anchorPointX + this.viewportPadding;
|
||||
const tlY =
|
||||
collisionBox.y1 + collisionBox.anchorPointY + this.viewportPadding;
|
||||
const brX =
|
||||
collisionBox.x2 + collisionBox.anchorPointX + this.viewportPadding;
|
||||
const brY =
|
||||
collisionBox.y2 + collisionBox.anchorPointY + this.viewportPadding;
|
||||
|
||||
if (
|
||||
!this.isInsideGrid(tlX, tlY, brX, brY) ||
|
||||
|
@ -79,14 +81,16 @@ export default class CollisionIndex {
|
|||
* @param {number} y P20 平面坐标Y
|
||||
* @return {Point} projectedPoint
|
||||
*/
|
||||
public project(mvpMatrix: mat4, x: number, y: number) {
|
||||
public project(mvpMatrix: number[], x: number, y: number) {
|
||||
const point = vec4.fromValues(x, y, 0, 1);
|
||||
const out = vec4.create();
|
||||
vec4.transformMat4(out, point, mvpMatrix);
|
||||
// @ts-ignore
|
||||
const mat = mat4.fromValues(...mvpMatrix);
|
||||
vec4.transformMat4(out, point, mat);
|
||||
// GL 坐标系[-1, 1] -> viewport 坐标系[width, height]
|
||||
return {
|
||||
x: ((out[0] / out[3] + 1) / 2) * this.width + viewportPadding,
|
||||
y: ((-out[1] / out[3] + 1) / 2) * this.height + viewportPadding,
|
||||
x: ((out[0] / out[3] + 1) / 2) * this.width + this.viewportPadding,
|
||||
y: ((-out[1] / out[3] + 1) / 2) * this.height + this.viewportPadding,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
type Position = number[];
|
||||
import { isNumber } from 'lodash';
|
||||
export function calculteCentroid(
|
||||
coord: Position | Position[] | Position[][],
|
||||
): Position {
|
||||
// let pos = coord as Position;
|
||||
if (isNumber(coord[0])) {
|
||||
return coord as Position;
|
||||
} else if (isNumber(coord[0][0])) {
|
||||
throw new Error('当前数据不支持标注');
|
||||
} else if (isNumber(coord[0][0][0])) {
|
||||
const coords = coord as Position[][];
|
||||
let xSum = 0;
|
||||
let ySum = 0;
|
||||
let len = 0;
|
||||
coords.forEach((coor: Position[]) => {
|
||||
coor.forEach((pos) => {
|
||||
xSum += pos[0];
|
||||
ySum += pos[1];
|
||||
len++;
|
||||
});
|
||||
});
|
||||
return [xSum / len, ySum / len, 0];
|
||||
} else {
|
||||
throw new Error('当前数据不支持标注');
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ type CallBack = (...args: any[]) => any;
|
|||
* @see https://zhuanlan.zhihu.com/p/74373214
|
||||
*/
|
||||
class GridIndex {
|
||||
private boxCells: number[][];
|
||||
private boxCells: number[][] = [];
|
||||
private xCellCount: number;
|
||||
private yCellCount: number;
|
||||
private boxKeys: string[];
|
||||
|
|
|
@ -237,7 +237,7 @@ export function getGlyphQuads(
|
|||
textOffset: [number, number] = [0, 0],
|
||||
alongLine: boolean,
|
||||
): IGlyphQuad[] {
|
||||
const { positionedGlyphs } = shaping;
|
||||
const { positionedGlyphs = [] } = shaping;
|
||||
const quads: IGlyphQuad[] = [];
|
||||
|
||||
for (const positionedGlyph of positionedGlyphs) {
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
"dependencies": {
|
||||
"@antv/l7-core": "^2.0.0-beta.25",
|
||||
"@antv/l7-utils": "^2.0.0-beta.25",
|
||||
"@babel/runtime": "^7.7.7",
|
||||
"core-js": "3",
|
||||
"gl-matrix": "^3.1.0",
|
||||
"inversify": "^5.0.1",
|
||||
|
|
|
@ -26,10 +26,8 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@antv/l7-core": "^2.0.0-beta.25",
|
||||
"@babel/runtime": "^7.7.7",
|
||||
"core-js": "3",
|
||||
"inversify": "^5.0.1",
|
||||
"inversify-logging": "^0.2.1",
|
||||
"lodash": "^4.17.15",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"regenerator-runtime": "^0.13.3",
|
||||
|
|
|
@ -27,13 +27,10 @@
|
|||
"@antv/l7-maps": "^2.0.0-beta.25",
|
||||
"@antv/l7-renderer": "^2.0.0-beta.25",
|
||||
"@antv/l7-utils": "^2.0.0-beta.25",
|
||||
"@babel/runtime": "^7.7.7",
|
||||
"core-js": "3",
|
||||
"inversify": "^5.0.1",
|
||||
"inversify-inject-decorators": "^3.1.0",
|
||||
"mapbox-gl": "^1.2.1",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"regenerator-runtime": "^0.13.3"
|
||||
"reflect-metadata": "^0.1.13"
|
||||
},
|
||||
"gitHead": "00d23ef70d9ec76eec26833fc50ac18fe584cf26",
|
||||
"publishConfig": {
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
"dependencies": {
|
||||
"@antv/l7-core": "^2.0.0-beta.25",
|
||||
"@antv/l7-utils": "^2.0.0-beta.25",
|
||||
"@babel/runtime": "^7.7.7",
|
||||
"@mapbox/geojson-rewind": "^0.4.0",
|
||||
"@turf/helpers": "^6.1.4",
|
||||
"@turf/invariant": "^6.1.2",
|
||||
|
@ -35,22 +34,15 @@
|
|||
"d3-dsv": "^1.1.1",
|
||||
"d3-hexbin": "^0.2.2",
|
||||
"eventemitter3": "^4.0.0",
|
||||
"gl-matrix": "^3.1.0",
|
||||
"inversify": "^5.0.1",
|
||||
"inversify-inject-decorators": "^3.1.0",
|
||||
"inversify-logging": "^0.2.1",
|
||||
"lodash": "^4.17.15",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"regenerator-runtime": "^0.13.3",
|
||||
"supercluster": "^6.0.2",
|
||||
"tapable": "^2.0.0-beta.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/d3-dsv": "^1.0.36",
|
||||
"@types/d3-hexbin": "^0.2.3",
|
||||
"@types/gl-matrix": "^2.4.5",
|
||||
"@types/lodash": "^4.14.138",
|
||||
"@types/viewport-mercator-project": "^6.1.0"
|
||||
"@types/lodash": "^4.14.138"
|
||||
},
|
||||
"gitHead": "00d23ef70d9ec76eec26833fc50ac18fe584cf26",
|
||||
"publishConfig": {
|
||||
|
|
|
@ -22,22 +22,8 @@
|
|||
"author": "lzxue",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.7.7",
|
||||
"@turf/helpers": "^6.1.4",
|
||||
"core-js": "3",
|
||||
"eventemitter3": "^4.0.0",
|
||||
"gl-matrix": "^3.1.0",
|
||||
"inversify": "^5.0.1",
|
||||
"inversify-inject-decorators": "^3.1.0",
|
||||
"inversify-logging": "^0.2.1",
|
||||
"lodash": "^4.17.15",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"regenerator-runtime": "^0.13.3",
|
||||
"tapable": "^2.0.0-beta.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/gl-matrix": "^2.4.5",
|
||||
"@types/lodash": "^4.14.138"
|
||||
"core-js": "3"
|
||||
},
|
||||
"gitHead": "00d23ef70d9ec76eec26833fc50ac18fe584cf26",
|
||||
"publishConfig": {
|
||||
|
|
|
@ -19,7 +19,7 @@ export default class ZoomComponent extends React.Component {
|
|||
const scene = new Scene({
|
||||
id: 'map',
|
||||
map: new Mapbox({
|
||||
style: 'mapbox://styles/mapbox/streets-v9',
|
||||
style: 'dark',
|
||||
center: [110.19382669582967, 30.258134],
|
||||
pitch: 0,
|
||||
zoom: 3,
|
||||
|
@ -39,9 +39,10 @@ export default class ZoomComponent extends React.Component {
|
|||
'#FF7A45',
|
||||
'#CF1D49',
|
||||
])
|
||||
.shape('fill')
|
||||
.shape('name', 'text')
|
||||
.size(10)
|
||||
.style({
|
||||
opacity: 0.3,
|
||||
opacity: 1.0,
|
||||
});
|
||||
scene.addLayer(layer);
|
||||
const zoomControl = new Zoom({
|
||||
|
|
|
@ -44,7 +44,7 @@ export default class TextLayerDemo extends React.Component {
|
|||
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
map: new GaodeMap({
|
||||
map: new Mapbox({
|
||||
center: [120.19382669582967, 30.258134],
|
||||
pitch: 0,
|
||||
style: 'dark',
|
||||
|
@ -61,17 +61,17 @@ export default class TextLayerDemo extends React.Component {
|
|||
},
|
||||
})
|
||||
.shape('m', 'text')
|
||||
.size(24)
|
||||
.size(12)
|
||||
.color('#fff')
|
||||
.style({
|
||||
fontWeight: 800,
|
||||
textAnchor: 'center', // 文本相对锚点的位置 center|left|right|top|bottom|top-left
|
||||
textOffset: [0, 0], // 文本相对锚点的偏移量 [水平, 垂直]
|
||||
spacing: 2, // 字符间距
|
||||
padding: [4, 4], // 文本包围盒 padding [水平,垂直],影响碰撞检测结果,避免相邻文本靠的太近
|
||||
strokeColor: 'white', // 描边颜色
|
||||
strokeWidth: 4, // 描边宽度
|
||||
strokeOpacity: 1.0,
|
||||
// fontWeight: 200,
|
||||
// textAnchor: 'center', // 文本相对锚点的位置 center|left|right|top|bottom|top-left
|
||||
// textOffset: [0, 0], // 文本相对锚点的偏移量 [水平, 垂直]
|
||||
// spacing: 2, // 字符间距
|
||||
// padding: [1, 1], // 文本包围盒 padding [水平,垂直],影响碰撞检测结果,避免相邻文本靠的太近
|
||||
// stroke: 'red', // 描边颜色
|
||||
// strokeWidth: 2, // 描边宽度
|
||||
// strokeOpacity: 1.0,
|
||||
});
|
||||
scene.addLayer(pointLayer);
|
||||
|
||||
|
|
Loading…
Reference in New Issue