mirror of https://gitee.com/antv-l7/antv-l7
feat: polygon 支持text
This commit is contained in:
parent
0a9558d8eb
commit
3114ab55a5
|
@ -12,7 +12,7 @@ import {
|
||||||
} from './IFontService';
|
} from './IFontService';
|
||||||
export const DEFAULT_CHAR_SET = getDefaultCharacterSet();
|
export const DEFAULT_CHAR_SET = getDefaultCharacterSet();
|
||||||
export const DEFAULT_FONT_FAMILY = 'sans-serif';
|
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_FONT_SIZE = 24;
|
||||||
export const DEFAULT_BUFFER = 3;
|
export const DEFAULT_BUFFER = 3;
|
||||||
export const DEFAULT_CUTOFF = 0.25;
|
export const DEFAULT_CUTOFF = 0.25;
|
||||||
|
|
|
@ -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() {
|
protected buildModels() {
|
||||||
this.layerModel = new CityBuildModel(this);
|
this.layerModel = new CityBuildModel(this);
|
||||||
this.models = this.layerModel.buildModels();
|
this.models = this.layerModel.buildModels();
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { IEncodeFeature } from '@antv/l7-core';
|
||||||
import { aProjectFlat, lngLatToMeters } from '@antv/l7-utils';
|
import { aProjectFlat, lngLatToMeters } from '@antv/l7-utils';
|
||||||
import earcut from 'earcut';
|
import earcut from 'earcut';
|
||||||
import { vec3 } from 'gl-matrix';
|
import { vec3 } from 'gl-matrix';
|
||||||
|
import { calculteCentroid } from '../utils/geo';
|
||||||
import getNormals from '../utils/polylineNormal';
|
import getNormals from '../utils/polylineNormal';
|
||||||
import extrudePolygon, {
|
import extrudePolygon, {
|
||||||
extrude_PolygonNormal,
|
extrude_PolygonNormal,
|
||||||
|
@ -24,7 +25,7 @@ const GeometryCache: IGeometryCache = {};
|
||||||
* @param feature 映射feature
|
* @param feature 映射feature
|
||||||
*/
|
*/
|
||||||
export function PointFillTriangulation(feature: IEncodeFeature) {
|
export function PointFillTriangulation(feature: IEncodeFeature) {
|
||||||
const coordinates = feature.coordinates as number[];
|
const coordinates = calculteCentroid(feature.coordinates);
|
||||||
return {
|
return {
|
||||||
vertices: [...coordinates, ...coordinates, ...coordinates, ...coordinates],
|
vertices: [...coordinates, ...coordinates, ...coordinates, ...coordinates],
|
||||||
indices: [0, 1, 2, 2, 3, 0],
|
indices: [0, 1, 2, 2, 3, 0],
|
||||||
|
@ -46,7 +47,6 @@ export function PointExtrudeTriangulation(feature: IEncodeFeature) {
|
||||||
vertices: positions,
|
vertices: positions,
|
||||||
indices: index,
|
indices: index,
|
||||||
normals,
|
normals,
|
||||||
// normals: Array.from(computeVertexNormals(positions, index, 3, false)),
|
|
||||||
size: 5,
|
size: 5,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ export function PointExtrudeTriangulation(feature: IEncodeFeature) {
|
||||||
* @param feature 映射feature
|
* @param feature 映射feature
|
||||||
*/
|
*/
|
||||||
export function PointImageTriangulation(feature: IEncodeFeature) {
|
export function PointImageTriangulation(feature: IEncodeFeature) {
|
||||||
const coordinates = feature.coordinates as number[];
|
const coordinates = calculteCentroid(feature.coordinates);
|
||||||
return {
|
return {
|
||||||
vertices: [...coordinates],
|
vertices: [...coordinates],
|
||||||
indices: [0],
|
indices: [0],
|
||||||
|
|
|
@ -8,10 +8,9 @@ export default class UpdateModelPlugin implements ILayerPlugin {
|
||||||
public apply(layer: ILayer) {
|
public apply(layer: ILayer) {
|
||||||
layer.hooks.beforeRender.tap('UpdateModelPlugin', () => {
|
layer.hooks.beforeRender.tap('UpdateModelPlugin', () => {
|
||||||
// 处理文本更新
|
// 处理文本更新
|
||||||
layer.layerModel.needUpdate();
|
if (layer.layerModel) {
|
||||||
// if (layer.layerModel.needUpdate()) {
|
layer.layerModel.needUpdate();
|
||||||
// layer.layerModelNeedUpdate = true;
|
}
|
||||||
// }
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { AttributeType, gl, IEncodeFeature, IModel } from '@antv/l7-core';
|
import { AttributeType, gl, IEncodeFeature, IModel } from '@antv/l7-core';
|
||||||
import BaseModel from '../../core/BaseModel';
|
import BaseModel from '../../core/BaseModel';
|
||||||
import { PointExtrudeTriangulation } from '../../core/triangulation';
|
import { PointExtrudeTriangulation } from '../../core/triangulation';
|
||||||
|
import { calculteCentroid } from '../../utils/geo';
|
||||||
import pointExtrudeFrag from '../shaders/extrude_frag.glsl';
|
import pointExtrudeFrag from '../shaders/extrude_frag.glsl';
|
||||||
import pointExtrudeVert from '../shaders/extrude_vert.glsl';
|
import pointExtrudeVert from '../shaders/extrude_vert.glsl';
|
||||||
interface IPointLayerStyleOptions {
|
interface IPointLayerStyleOptions {
|
||||||
|
@ -100,7 +101,7 @@ export default class ExtrudeModel extends BaseModel {
|
||||||
},
|
},
|
||||||
size: 3,
|
size: 3,
|
||||||
update: (feature: IEncodeFeature, featureIdx: number) => {
|
update: (feature: IEncodeFeature, featureIdx: number) => {
|
||||||
const coordinates = feature.coordinates as number[];
|
const coordinates = calculteCentroid(feature.coordinates);
|
||||||
return [coordinates[0], coordinates[1], 0];
|
return [coordinates[0], coordinates[1], 0];
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -12,6 +12,7 @@ import {
|
||||||
import { rgb2arr } from '@antv/l7-utils';
|
import { rgb2arr } from '@antv/l7-utils';
|
||||||
import BaseModel from '../../core/BaseModel';
|
import BaseModel from '../../core/BaseModel';
|
||||||
import CollisionIndex from '../../utils/collision-index';
|
import CollisionIndex from '../../utils/collision-index';
|
||||||
|
import { calculteCentroid } from '../../utils/geo';
|
||||||
import {
|
import {
|
||||||
getGlyphQuads,
|
getGlyphQuads,
|
||||||
IGlyphQuad,
|
IGlyphQuad,
|
||||||
|
@ -33,14 +34,12 @@ interface IPointTextLayerStyleOptions {
|
||||||
textAllowOverlap: boolean;
|
textAllowOverlap: boolean;
|
||||||
}
|
}
|
||||||
export function TextTriangulation(feature: IEncodeFeature) {
|
export function TextTriangulation(feature: IEncodeFeature) {
|
||||||
const coordinates = feature.coordinates as number[];
|
const centroid = feature.centroid as number[]; // 计算中心点
|
||||||
const { glyphQuads } = feature;
|
const { glyphQuads } = feature;
|
||||||
const vertices: number[] = [];
|
const vertices: number[] = [];
|
||||||
const indices: number[] = [];
|
const indices: number[] = [];
|
||||||
const coord =
|
const coord =
|
||||||
coordinates.length === 2
|
centroid.length === 2 ? [centroid[0], centroid[1], 0] : centroid;
|
||||||
? [coordinates[0], coordinates[1], 0]
|
|
||||||
: coordinates;
|
|
||||||
glyphQuads.forEach((quad: IGlyphQuad, index: number) => {
|
glyphQuads.forEach((quad: IGlyphQuad, index: number) => {
|
||||||
vertices.push(
|
vertices.push(
|
||||||
...coord,
|
...coord,
|
||||||
|
@ -90,8 +89,8 @@ export default class TextModel extends BaseModel {
|
||||||
const {
|
const {
|
||||||
fontWeight = 800,
|
fontWeight = 800,
|
||||||
fontFamily,
|
fontFamily,
|
||||||
stroke,
|
stroke = '#fff',
|
||||||
strokeWidth,
|
strokeWidth = 0,
|
||||||
} = this.layer.getLayerConfig() as IPointTextLayerStyleOptions;
|
} = this.layer.getLayerConfig() as IPointTextLayerStyleOptions;
|
||||||
const { canvas } = this.fontService;
|
const { canvas } = this.fontService;
|
||||||
return {
|
return {
|
||||||
|
@ -235,7 +234,7 @@ export default class TextModel extends BaseModel {
|
||||||
*/
|
*/
|
||||||
private initTextFont() {
|
private initTextFont() {
|
||||||
const {
|
const {
|
||||||
fontWeight = 'normal',
|
fontWeight = '800',
|
||||||
fontFamily,
|
fontFamily,
|
||||||
} = this.layer.getLayerConfig() as IPointTextLayerStyleOptions;
|
} = this.layer.getLayerConfig() as IPointTextLayerStyleOptions;
|
||||||
const data = this.layer.getEncodedData();
|
const data = this.layer.getEncodedData();
|
||||||
|
@ -268,7 +267,7 @@ export default class TextModel extends BaseModel {
|
||||||
} = this.layer.getLayerConfig() as IPointTextLayerStyleOptions;
|
} = this.layer.getLayerConfig() as IPointTextLayerStyleOptions;
|
||||||
const data = this.layer.getEncodedData();
|
const data = this.layer.getEncodedData();
|
||||||
this.glyphInfo = data.map((feature: IEncodeFeature) => {
|
this.glyphInfo = data.map((feature: IEncodeFeature) => {
|
||||||
const { shape = '' } = feature;
|
const { shape = '', coordinates } = feature;
|
||||||
const shaping = shapeText(
|
const shaping = shapeText(
|
||||||
shape.toString(),
|
shape.toString(),
|
||||||
mapping,
|
mapping,
|
||||||
|
@ -281,6 +280,7 @@ export default class TextModel extends BaseModel {
|
||||||
const glyphQuads = getGlyphQuads(shaping, textOffset, false);
|
const glyphQuads = getGlyphQuads(shaping, textOffset, false);
|
||||||
feature.shaping = shaping;
|
feature.shaping = shaping;
|
||||||
feature.glyphQuads = glyphQuads;
|
feature.glyphQuads = glyphQuads;
|
||||||
|
feature.centroid = calculteCentroid(coordinates);
|
||||||
return feature;
|
return feature;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -301,10 +301,10 @@ export default class TextModel extends BaseModel {
|
||||||
const collisionIndex = new CollisionIndex(width, height);
|
const collisionIndex = new CollisionIndex(width, height);
|
||||||
const filterData = this.glyphInfo.filter((feature: IEncodeFeature) => {
|
const filterData = this.glyphInfo.filter((feature: IEncodeFeature) => {
|
||||||
const { shaping, id = 0 } = feature;
|
const { shaping, id = 0 } = feature;
|
||||||
const coordinates = feature.coordinates as [number, number];
|
const centroid = feature.centroid as [number, number];
|
||||||
const size = feature.size as number;
|
const size = feature.size as number;
|
||||||
const fontScale: number = size / 24;
|
const fontScale: number = size / 24;
|
||||||
const pixels = this.mapService.lngLatToContainer(coordinates);
|
const pixels = this.mapService.lngLatToContainer(centroid);
|
||||||
const { box } = collisionIndex.placeCollisionBox({
|
const { box } = collisionIndex.placeCollisionBox({
|
||||||
x1: shaping.left * fontScale - padding[0],
|
x1: shaping.left * fontScale - padding[0],
|
||||||
x2: shaping.right * fontScale + padding[0],
|
x2: shaping.right * fontScale + padding[0],
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { IEncodeFeature } from '@antv/l7-core';
|
import { IEncodeFeature } from '@antv/l7-core';
|
||||||
import BaseLayer from '../core/BaseLayer';
|
import BaseLayer from '../core/BaseLayer';
|
||||||
|
import { PointType } from '../point/models/';
|
||||||
import PolygonModels, { PolygonModelType } from './models/';
|
import PolygonModels, { PolygonModelType } from './models/';
|
||||||
|
|
||||||
interface IPolygonLayerStyleOptions {
|
interface IPolygonLayerStyleOptions {
|
||||||
|
@ -31,6 +32,42 @@ export default class PolygonLayer extends BaseLayer<IPolygonLayerStyleOptions> {
|
||||||
'shape',
|
'shape',
|
||||||
);
|
);
|
||||||
const shape = shapeAttribute?.scale?.field as PolygonModelType;
|
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 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 ExtrudeModel from './extrude';
|
||||||
import FillModel from './fill';
|
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 } = {
|
const PolygonModels: { [key in PolygonModelType]: any } = {
|
||||||
fill: FillModel,
|
fill: FillModel,
|
||||||
line: LineModel,
|
line: LineModel,
|
||||||
extrude: ExtrudeModel,
|
extrude: ExtrudeModel,
|
||||||
};
|
text: TextModel,
|
||||||
|
point_fill: PointFillModel,
|
||||||
|
point_image: IMageModel,
|
||||||
|
point_normal: NormalModel,
|
||||||
|
point_extrude: PointExtrudeModel,
|
||||||
|
|
||||||
|
// point_fill: PointModels.fill,
|
||||||
|
};
|
||||||
export default PolygonModels;
|
export default PolygonModels;
|
||||||
|
|
|
@ -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('当前数据不支持标注');
|
||||||
|
}
|
||||||
|
}
|
|
@ -237,7 +237,7 @@ export function getGlyphQuads(
|
||||||
textOffset: [number, number] = [0, 0],
|
textOffset: [number, number] = [0, 0],
|
||||||
alongLine: boolean,
|
alongLine: boolean,
|
||||||
): IGlyphQuad[] {
|
): IGlyphQuad[] {
|
||||||
const { positionedGlyphs } = shaping;
|
const { positionedGlyphs = [] } = shaping;
|
||||||
const quads: IGlyphQuad[] = [];
|
const quads: IGlyphQuad[] = [];
|
||||||
|
|
||||||
for (const positionedGlyph of positionedGlyphs) {
|
for (const positionedGlyph of positionedGlyphs) {
|
||||||
|
|
|
@ -19,7 +19,7 @@ export default class ZoomComponent extends React.Component {
|
||||||
const scene = new Scene({
|
const scene = new Scene({
|
||||||
id: 'map',
|
id: 'map',
|
||||||
map: new Mapbox({
|
map: new Mapbox({
|
||||||
style: 'mapbox://styles/mapbox/streets-v9',
|
style: 'dark',
|
||||||
center: [110.19382669582967, 30.258134],
|
center: [110.19382669582967, 30.258134],
|
||||||
pitch: 0,
|
pitch: 0,
|
||||||
zoom: 3,
|
zoom: 3,
|
||||||
|
@ -39,9 +39,10 @@ export default class ZoomComponent extends React.Component {
|
||||||
'#FF7A45',
|
'#FF7A45',
|
||||||
'#CF1D49',
|
'#CF1D49',
|
||||||
])
|
])
|
||||||
.shape('fill')
|
.shape('name', 'text')
|
||||||
|
.size(10)
|
||||||
.style({
|
.style({
|
||||||
opacity: 0.3,
|
opacity: 1.0,
|
||||||
});
|
});
|
||||||
scene.addLayer(layer);
|
scene.addLayer(layer);
|
||||||
const zoomControl = new Zoom({
|
const zoomControl = new Zoom({
|
||||||
|
|
|
@ -64,14 +64,14 @@ export default class TextLayerDemo extends React.Component {
|
||||||
.size(12)
|
.size(12)
|
||||||
.color('#fff')
|
.color('#fff')
|
||||||
.style({
|
.style({
|
||||||
fontWeight: 200,
|
// fontWeight: 200,
|
||||||
textAnchor: 'center', // 文本相对锚点的位置 center|left|right|top|bottom|top-left
|
// textAnchor: 'center', // 文本相对锚点的位置 center|left|right|top|bottom|top-left
|
||||||
textOffset: [0, 0], // 文本相对锚点的偏移量 [水平, 垂直]
|
// textOffset: [0, 0], // 文本相对锚点的偏移量 [水平, 垂直]
|
||||||
spacing: 2, // 字符间距
|
// spacing: 2, // 字符间距
|
||||||
padding: [1, 1], // 文本包围盒 padding [水平,垂直],影响碰撞检测结果,避免相邻文本靠的太近
|
// padding: [1, 1], // 文本包围盒 padding [水平,垂直],影响碰撞检测结果,避免相邻文本靠的太近
|
||||||
stroke: 'red', // 描边颜色
|
// stroke: 'red', // 描边颜色
|
||||||
strokeWidth: 2, // 描边宽度
|
// strokeWidth: 2, // 描边宽度
|
||||||
strokeOpacity: 1.0,
|
// strokeOpacity: 1.0,
|
||||||
});
|
});
|
||||||
scene.addLayer(pointLayer);
|
scene.addLayer(pointLayer);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue