Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shield rotation #716

Merged
merged 29 commits into from
Mar 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
809a7bc
Add shield rotation debug page
wipfli Dec 14, 2021
fe93c5b
Fix lint
wipfli Dec 14, 2021
cd6136b
Add `text-rotate-to-line` property
wipfli Dec 16, 2021
270a47f
Merge branch 'main' into shield-rotation
wipfli Jan 3, 2022
d695660
Merge branch 'main' into shield-rotation
wipfli Jan 28, 2022
6e039e2
Merge main
wipfli Feb 24, 2022
a93aa62
Lint
wipfli Feb 24, 2022
956aee4
Remove debug files
wipfli Feb 24, 2022
b9bbd9d
Add render tests
wipfli Feb 24, 2022
f207d91
Merge main
wipfli Feb 24, 2022
4218a2c
Add debug/shield-rotation.html
wipfli Mar 2, 2022
e5e6376
Fix `text-rotation-alignment: viewport`
wipfli Mar 3, 2022
6c026aa
Update render tests
wipfli Mar 4, 2022
3c4ed6d
listen for change event instead of click (works better in safari and …
xabbu42 Mar 8, 2022
b0459f3
Increase example font size
wipfli Mar 9, 2022
add9a43
Rename property to `text-new-property` for unbiased discussion
wipfli Mar 9, 2022
cc11c59
Remove new bool property. Introduce new value `text-rotation-alignmen…
wipfli Mar 10, 2022
80f8ea3
Remove render tests for `text-new-property`
wipfli Mar 10, 2022
aebb89e
Add render tests
wipfli Mar 10, 2022
30faab6
Use ABC in render test
wipfli Mar 10, 2022
20cc938
Rename `viewport-letter` to `viewport-glyph`. Fix typo.
wipfli Mar 10, 2022
b2b6b8e
Update doc string
wipfli Mar 10, 2022
58f40ad
Update render test
wipfli Mar 10, 2022
5f3af95
Merge branch 'main' into shield-rotation
wipfli Mar 11, 2022
b00e38a
Fix logic
wipfli Mar 11, 2022
fd48809
Add unit test
wipfli Mar 11, 2022
cb85b91
Add `text-pitch-alignment: auto` render test
wipfli Mar 14, 2022
6f71acb
Merge branch 'main' into shield-rotation
wipfli Mar 15, 2022
da7f3d8
Update changelog and document sdk support for `viewport-glyph`
wipfli Mar 15, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

### Features and improvements

- Add option `viewport-glyph` to `text-rotation-alignment` which places glyphs along a linestring and rotates them to the x-axis of the viewport (#716).

- *...Add new stuff here...*

### 🐞 Bug fixes
Expand All @@ -10,6 +12,8 @@
- Add FeatureIdentifier type to define feature parameter in setFeatureState, removeFeatureState, and getFeatureState methods. Change FeatureIdentifier.id from `id: string | number;` to `id?: string | number | undefined;` (#1095)
- Change map.on, map.off, and map.once type parameter from "type: MapEvent" to "type: MapEvent | string" (#1094)

- *...Add new stuff here...*

## 2.1.7

### 🐞 Bug fixes
Expand Down
162 changes: 162 additions & 0 deletions debug/shield-rotation.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
<!DOCTYPE html>
<html lang="en">

<head>
<title>MapLibre GL JS debug page</title>
<meta charset='utf-8'>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel='stylesheet' href='../dist/maplibre-gl.css' />
<style>
body {
margin: 0;
padding: 0;
}

html,
body,
#map {
height: 100%;
}
</style>
</head>

<body>
<style>
.map-overlay {
font: 12px/20px 'Helvetica Neue', Arial, Helvetica, sans-serif;
position: absolute;
width: 200px;
top: 0;
left: 0;
padding: 10px;
}

.map-overlay .map-overlay-inner {
background-color: #fff;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
border-radius: 3px;
padding: 10px;
margin-bottom: 10px;
}

.map-overlay-inner fieldset {
border: none;
padding: 0;
margin: 0 0 10px;
}

.map-overlay-inner fieldset:last-child {
margin: 0;
}

.map-overlay-inner select {
width: 100%;
}

.map-overlay-inner label {
display: block;
font-weight: bold;
margin: 0 0 5px;
}
</style>

<div id="map"></div>
<div class="map-overlay top">
<div class="map-overlay-inner">
<fieldset>
<label>text-rotation-alignment</label>
<select id="rotation" name="rotation">
<option value="map">map</option>
<option value="viewport">viewport</option>
<option value="viewport-glyph">viewport-glyph</option>
</select>
</fieldset>
<fieldset>
<label>text-pitch-alignment</label>
<select id="pitch" name="pitch">
<option value="map">map</option>
<option value="viewport">viewport</option>
</select>
</fieldset>
</div>
</div>

<script src='../dist/maplibre-gl-dev.js'></script>
<script>
var map = window.map = new maplibregl.Map({
container: 'map',
zoom: 6,
center: [-69.482, 45.42],
style: 'https://demotiles.maplibre.org/style.json',
hash: true
});

map.addControl(new maplibregl.NavigationControl());
map.addControl(new maplibregl.GeolocateControl());

map.on('load', function () {
map.addSource('maine', {
'type': 'geojson',
'data': {
'type': 'Feature',
'geometry': {
'type': 'Polygon',
'coordinates': [
[
[-67.13734351262877, 45.137451890638886],
[-66.96466, 44.8097],
[-68.03252, 44.3252],
[-69.06, 43.98],
[-70.11617, 43.68405],
[-70.64573401557249, 43.090083319667144],
[-70.75102474636725, 43.08003225358635],
[-70.79761105007827, 43.21973948828747],
[-70.98176001655037, 43.36789581966826],
[-70.94416541205806, 43.46633942318431],
[-71.08482, 45.3052400000002],
[-70.6600225491012, 45.46022288673396],
[-70.30495378282376, 45.914794623389355],
[-70.00014034695016, 46.69317088478567],
[-69.23708614772835, 47.44777598732787],
[-68.90478084987546, 47.184794623394396],
[-68.23430497910454, 47.35462921812177],
[-67.79035274928509, 47.066248887716995],
[-67.79141211614706, 45.702585354182816],
[-67.13734351262877, 45.137451890638886]
]
]
}
}
});
map.addLayer({
'id': 'maine-line',
'type': 'line',
'source': 'maine'
});
map.addLayer({
'id': 'maine-text',
'type': 'symbol',
'source': 'maine',
'layout': {
'text-field': 'maine',
'symbol-placement': 'line',
'text-pitch-alignment': 'map',
'text-rotation-alignment': 'map',
'text-size': 36
}
});
var rotation = document.getElementById('rotation');
rotation.addEventListener('change', function() {
map.setLayoutProperty('maine-text', 'text-rotation-alignment', rotation.value);
});
var pitch = document.getElementById('pitch');
pitch.addEventListener('change', function() {
map.setLayoutProperty('maine-text', 'text-pitch-alignment', pitch.value);
});

});

</script>
</body>

</html>
2 changes: 1 addition & 1 deletion src/data/bucket/symbol_bucket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,7 @@ class SymbolBucket implements Bucket {

if (text) {
const fontStack = textFont.evaluate(evaluationFeature, {}, canonical).join(',');
const textAlongLine = layout.get('text-rotation-alignment') === 'map' && layout.get('symbol-placement') !== 'point';
const textAlongLine = layout.get('text-rotation-alignment') !== 'viewport' && layout.get('symbol-placement') !== 'point';
wipfli marked this conversation as resolved.
Show resolved Hide resolved
this.allowVerticalPlacement = this.writingModes && this.writingModes.indexOf(WritingMode.vertical) >= 0;
for (const section of text.sections) {
if (!section.image) {
Expand Down
64 changes: 64 additions & 0 deletions src/render/draw_symbol.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import SymbolStyleLayer from '../style/style_layer/symbol_style_layer';
import Painter from './painter';
import Program from './program';
import drawSymbol from './draw_symbol';
import * as symbolProjection from '../symbol/projection';
import type ZoomHistory from '../style/zoom_history';
import type Map from '../ui/map';
import type Transform from '../geo/transform';
Expand All @@ -18,6 +19,7 @@ jest.mock('./program');
jest.mock('../source/source_cache');
jest.mock('../source/tile');
jest.mock('../data/bucket/symbol_bucket');
jest.mock('../symbol/projection');

describe('drawSymbol', () => {
test('should not do anything', () => {
Expand Down Expand Up @@ -85,4 +87,66 @@ describe('drawSymbol', () => {

expect(programMock.draw).toHaveBeenCalledTimes(1);
});

test('should call updateLineLabels with rotateToLine === false if text-rotation-alignment is viewport-glyph', () => {

const painterMock = new Painter(null, null);
painterMock.context = {
gl: {},
activeTexture: {
set: () => {}
}
} as any;
painterMock.renderPass = 'translucent';
painterMock.transform = {pitch: 0, labelPlaneMatrix: mat4.create()} as any as Transform;
painterMock.options = {} as any;

const layerSpec = {
id: 'mock-layer',
source: 'empty-source',
type: 'symbol',
layout: {
'text-rotation-alignment': 'viewport-glyph',
'text-field': 'ABC',
'symbol-placement': 'line',
},
paint: {
'text-opacity': 1
}
} as SymbolLayerSpecification;
const layer = new SymbolStyleLayer(layerSpec);
layer.recalculate({zoom: 0, zoomHistory: {} as ZoomHistory} as EvaluationParameters, []);

const tileId = new OverscaledTileID(1, 0, 1, 0, 0);
tileId.posMatrix = mat4.create();
const programMock = new Program(null, null, null, null, null, null);
(painterMock.useProgram as jest.Mock).mockReturnValue(programMock);
const bucketMock = new SymbolBucket(null);
bucketMock.icon = {
programConfigurations: {
get: () => {}
},
segments: {
get: () => [1]
}
} as any;
bucketMock.iconSizeData = {
kind: 'constant',
layoutSize: 1
};
const tile = new Tile(tileId, 256);
tile.tileID = tileId;
tile.imageAtlasTexture = {
bind: () => {}
} as any;
(tile.getBucket as jest.Mock).mockReturnValue(bucketMock);
const sourceCacheMock = new SourceCache(null, null, null);
(sourceCacheMock.getTile as jest.Mock).mockReturnValue(tile);
sourceCacheMock.map = {showCollisionBoxes: false} as any as Map;

const spy = jest.spyOn(symbolProjection, 'updateLineLabels');
drawSymbol(painterMock, sourceCacheMock, layer, [tileId], null);

expect(spy.mock.calls[0][8]).toBeFalsy(); // rotateToLine === false
});
});
5 changes: 3 additions & 2 deletions src/render/draw_symbol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ function drawLayerSymbols(

const rotateWithMap = rotationAlignment === 'map';
const pitchWithMap = pitchAlignment === 'map';
const alongLine = rotateWithMap && layer.layout.get('symbol-placement') !== 'point';
const alongLine = rotationAlignment !== 'viewport' && layer.layout.get('symbol-placement') !== 'point';
// Line label rotation happens in `updateLineLabels`
// Pitched point labels are automatically rotated by the labelPlaneMatrix projection
// Unpitched point labels need to have their rotation applied after projection
Expand Down Expand Up @@ -331,7 +331,8 @@ function drawLayerSymbols(
bucket.hasIconData();

if (alongLine) {
symbolProjection.updateLineLabels(bucket, coord.posMatrix, painter, isText, labelPlaneMatrix, glCoordMatrix, pitchWithMap, keepUpright);
const rotateToLine = layer.layout.get('text-rotation-alignment') === 'map';
symbolProjection.updateLineLabels(bucket, coord.posMatrix, painter, isText, labelPlaneMatrix, glCoordMatrix, pitchWithMap, keepUpright, rotateToLine);
}

const matrix = painter.translatePosMatrix(coord.posMatrix, tile, translate, translateAnchor),
Expand Down
6 changes: 6 additions & 0 deletions src/style-spec/reference/v8.json
Original file line number Diff line number Diff line change
Expand Up @@ -1690,6 +1690,9 @@
"viewport": {
"doc": "Produces glyphs whose x-axes are aligned with the x-axis of the viewport, regardless of the value of `symbol-placement`."
},
"viewport-glyph": {
"doc": "When `symbol-placement` is set to `point`, aligns text to the x-axis of the viewport. When `symbol-placement` is set to `line` or `line-center`, aligns glyphs to the x-axis of the viewport and places them along the line."
},
"auto": {
"doc": "When `symbol-placement` is set to `point`, this is equivalent to `viewport`. When `symbol-placement` is set to `line` or `line-center`, this is equivalent to `map`."
}
Expand All @@ -1711,6 +1714,9 @@
"android": "4.2.0",
"ios": "3.4.0",
"macos": "0.3.0"
},
"`viewport-glyph` value": {
"js": "2.1.8"
}
},
"expression": {
Expand Down
2 changes: 1 addition & 1 deletion src/style/style_layer/symbol_style_layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class SymbolStyleLayer extends StyleLayer {

// If unspecified, `*-pitch-alignment` inherits `*-rotation-alignment`
if (this.layout.get('text-pitch-alignment') === 'auto') {
this.layout._values['text-pitch-alignment'] = this.layout.get('text-rotation-alignment');
this.layout._values['text-pitch-alignment'] = this.layout.get('text-rotation-alignment') === 'map' ? 'map' : 'viewport';
wipfli marked this conversation as resolved.
Show resolved Hide resolved
}
if (this.layout.get('icon-pitch-alignment') === 'auto') {
this.layout._values['icon-pitch-alignment'] = this.layout.get('icon-rotation-alignment');
Expand Down
3 changes: 2 additions & 1 deletion src/symbol/collision_index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@ class CollisionIndex {
symbol,
lineVertexArray,
labelPlaneMatrix,
projectionCache);
projectionCache,
false);

let collisionDetected = false;
let inGrid = false;
Expand Down
Loading