Skip to content

Commit

Permalink
feat: 增加对于点要素的自动标注
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaoiver committed Aug 14, 2019
1 parent f50ec3c commit 8feefb6
Show file tree
Hide file tree
Showing 16 changed files with 875 additions and 212 deletions.
37 changes: 29 additions & 8 deletions demos/06_text.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta name="geometry" content="diagram">
<link rel="stylesheet" href="./assets/common.css">
<title>point_circle</title>
<title>text layer</title>
<style>
#map { position:absolute; top:0; bottom:0; width:100%; }
</style>
Expand All @@ -29,21 +29,42 @@
});
window.scene = scene;
scene.on('loaded', () => {
$.get('./data/provincePoint.json', data => {
$.get('https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_10m_geography_regions_points.geojson', data => {
scene.PointLayer({
zIndex: 2
zIndex: 3
})
.source(data)
.shape('name', 'text')
.shape('circle')
.active(true)
.size(12) // default 1
.color('name')
.size(4)
.color('#fff')
.style({
stroke: '#999',
strokeWidth: 0,
strokeWidth: 1,
opacity: 1.0
})
.render();

scene.TextLayer({
zIndex: 4
})
.source(data)
.shape('name', 'text')
.active(true)
.size('scalerank', [ 10, 20, 24 ])
.color('scalerank', [ 'red', 'blue', 'black' ])
.style({
// fontFamily: 'Monaco, monospace', // 字体
fontWeight: 400,
textAnchor: 'center', // 文本相对锚点的位置 center|left|right|top|bottom|top-left
textOffset: [ 0, 0 ], // 文本相对锚点的偏移量 [水平, 垂直]
spacing: 2, // 字符间距
padding: [ 4, 4 ], // 文本包围盒 padding [水平,垂直],影响碰撞检测结果,避免相邻文本靠的太近
strokeColor: 'white', // 描边颜色
strokeWidth: 2, // 描边宽度
opacity: 1.0
})
.render();
.render();
});
});

Expand Down
29 changes: 19 additions & 10 deletions src/core/scene.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export default class Scene extends Base {
this.fontAtlasManager = new FontAtlasManager();
this._layers = [];
this.animateCount = 0;
this.inited = false;
}

_initEngine(mapContainer) {
Expand All @@ -44,16 +45,24 @@ export default class Scene extends Base {
this.map = Map.map;
this._initEngine(Map.renderDom);
Map.asyncCamera(this._engine);
this.initLayer();
this._registEvents();
const hash = this.get('hash');
if (hash) {
const Ctor = getInteraction('hash');
const interaction = new Ctor({ layer: this });
interaction._onHashChange();
}
this.emit('loaded');
this._engine.update();

// 等待相机同步之后再进行首次渲染
Map.on('cameraloaded', () => {
if (!this.inited) {
this.initLayer();
this._registEvents();
const hash = this.get('hash');
if (hash) {
const Ctor = getInteraction('hash');
const interaction = new Ctor({ layer: this });
interaction._onHashChange();
}
this.emit('loaded');
this.inited = true;
}
this._engine.update();
this.emit('cameraloaded');
});
});
}
initLayer() {
Expand Down
259 changes: 147 additions & 112 deletions src/geom/buffer/point/text.js
Original file line number Diff line number Diff line change
@@ -1,130 +1,165 @@
export default function TextBuffer(layerData, fontAtlasManager) {
/**
* 为文本构建顶点数据,仅支持点要素自动标注。
* @see https://zhuanlan.zhihu.com/p/72222549
* @see https://zhuanlan.zhihu.com/p/74373214
*/
import { shapeText, getGlyphQuads } from '../../../util/symbol-layout';

export default function TextBuffer(
layerData,
sourceData,
options,
fontAtlasManager,
collisionIndex,
mvpMatrix
) {
const {
textField,
fontWeight,
fontFamily
} = options;
const characterSet = [];
layerData.forEach(element => {
let text = element.shape || '';
sourceData.forEach(element => {
// shape 存储了 text-field
let text = element[textField] || '';
text = text.toString();
for (let j = 0; j < text.length; j++) {
// 去重
if (characterSet.indexOf(text[j]) === -1) {
characterSet.push(text[j]);
}
}
});
fontAtlasManager.setProps({
characterSet
characterSet,
fontFamily,
fontWeight
});
const attr = drawGlyph(layerData, fontAtlasManager);
return attr;
return drawGlyph(layerData, sourceData, options, fontAtlasManager, collisionIndex, mvpMatrix);
}
function drawGlyph(layerData, fontAtlasManager) {

function drawGlyph(
layerData, sourceData,
{
textField,
spacing = 2,
textAnchor = 'center',
textOffset = [ 0, 0 ],
padding = [ 4, 4 ]
},
fontAtlasManager,
collisionIndex,
mvpMatrix
) {
const { texture, fontAtlas, mapping } = fontAtlasManager;

const attributes = {
originPoints: [],
textSizes: [],
textOffsets: [],
fontAtlas,
texture,
positions: [],
colors: [],
textureElements: [],
pickingIds: []
pickingIds: [],
textUVs: [],
textOffsets: [],
textSizes: [],
index: []
};
const { texture, fontAtlas, mapping, scale } = fontAtlasManager;
layerData.forEach(function(element) {
const size = element.size;
const pos = element.coordinates;
let text = element.shape || '';
text = text.toString();
const pen = {
x: (-text.length * size) / 2,
y: 0
};
for (let i = 0; i < text.length; i++) {
const metric = mapping[text[i]];
const { x, y, width, height } = metric;
const color = element.color;
const offsetX = pen.x;
const offsetY = pen.y;
attributes.pickingIds.push(
element.id,
element.id,
element.id,
element.id,
element.id,
element.id
);
attributes.textOffsets.push(
// 文字在词语的偏移量
offsetX,
offsetY,
offsetX,
offsetY,
offsetX,
offsetY,
offsetX,
offsetY,
offsetX,
offsetY,
offsetX,
offsetY
);
attributes.originPoints.push(
// 词语的经纬度坐标
pos[0],
pos[1],
0,
pos[0],
pos[1],
0,
pos[0],
pos[1],
0,
pos[0],
pos[1],
0,
pos[0],
pos[1],
0,
pos[0],
pos[1],
0
);
attributes.textSizes.push(
size,
size * scale,
0,
size * scale,
0,
0,
size,
size * scale,
0,
0,
size,
0
);
attributes.colors.push(
...color,
...color,
...color,
...color,
...color,
...color
);
attributes.textureElements.push(
// 文字纹理坐标
x + width,
y,
x,
y,
x,
y + height,
x + width,
y,
x,
y + height,
x + width,
y + height
);
pen.x = pen.x + size;
let indexCounter = 0;
layerData.forEach((feature, i) => {
const { size, coordinates } = feature;
// 根据字段获取文本
const text = `${sourceData[i][textField] || ''}`;
// sdf 中默认字号为 24
const fontScale = size / 24;

// 1. 计算每个字符相对锚点的位置
const shaping = shapeText(text, mapping, 24, textAnchor, 'center', spacing, textOffset);

if (shaping) {
// 2. 尝试加入空间索引,获取碰撞检测结果
// TODO:按照 feature 中指定字段排序,确定插入权重,保证优先级高的文本优先展示
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: coordinates[0],
anchorPointY: coordinates[1]
}, mvpMatrix);

// 无碰撞则加入空间索引
if (box && box.length) {
// TODO:featureIndex
collisionIndex.insertCollisionBox(box, 0);

// 3. 计算可供渲染的文本块,其中每个字符都包含纹理坐标
const glyphQuads = getGlyphQuads(shaping, textOffset, false);

// 4. 构建顶点数据,四个顶点组成一个 quad
indexCounter = addAttributeForFeature(feature, attributes, glyphQuads, indexCounter);
}
}
});
attributes.texture = texture;
attributes.fontAtlas = fontAtlas;
return attributes;
}

function addAttributeForFeature(feature, attributes, glyphQuads, indexCounter) {
const { id, size, color, coordinates } = feature;
glyphQuads.forEach(quad => {

attributes.pickingIds.push(
id,
id,
id,
id
);

attributes.colors.push(
...color,
...color,
...color,
...color
);

attributes.positions.push(
coordinates[0], coordinates[1],
coordinates[0], coordinates[1],
coordinates[0], coordinates[1],
coordinates[0], coordinates[1]
);

attributes.textUVs.push(
quad.tex.x, quad.tex.y + quad.tex.height,
quad.tex.x + quad.tex.width, quad.tex.y + quad.tex.height,
quad.tex.x + quad.tex.width, quad.tex.y,
quad.tex.x, quad.tex.y,
);

attributes.textOffsets.push(
quad.tl.x, quad.tl.y,
quad.tr.x, quad.tr.y,
quad.br.x, quad.br.y,
quad.bl.x, quad.bl.y
);

attributes.textSizes.push(
size,
size,
size,
size
);

attributes.index.push(
0 + indexCounter,
1 + indexCounter,
2 + indexCounter,
2 + indexCounter,
3 + indexCounter,
0 + indexCounter
);
indexCounter += 4;
});

return indexCounter;
}
Loading

0 comments on commit 8feefb6

Please sign in to comment.