Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

[core] Enable text-offset with variable label placement #15542

Merged
merged 3 commits into from
Sep 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion mapbox-gl-js
Submodule mapbox-gl-js updated 356 files
1 change: 1 addition & 0 deletions platform/android/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to
- Introduce `clusterProperties` option for aggregated cluster properties. [#15425](https://github.com/mapbox/mapbox-gl-native/pull/15425)
- Expose the `CameraPosition#padding` field and associated utility camera position builders. This gives a choice to set a persisting map padding immediately during a transition instead of setting it lazily `MapboxMap#setPadding`, which required scheduling additional transition to be applied. This also deprecates `MapboxMap#setPadding` as there should be no need for a lazy padding setter. [#15444](https://github.com/mapbox/mapbox-gl-native/pull/15444)
- Add number-format expression that allows to format a number to a string, with configurations as minimal/maximal fraction and locale/currency. [#15424](https://github.com/mapbox/mapbox-gl-native/pull/15424)
- Enable using of `text-offset` option together with `text-variable-anchor` (if `text-radial-offset` option is not provided). If used with `text-variable-anchor`, input values will be taken as absolute values. Offsets along the x- and y-axis will be applied automatically based on the anchor position. [#15542](https://github.com/mapbox/mapbox-gl-native/pull/15542)

### Performance improvements
- Mark used offline region resources in batches. [#15521](https://github.com/mapbox/mapbox-gl-native/pull/15521)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2296,7 +2296,7 @@ public static PropertyValue<Expression> textJustify(Expression value) {
}

/**
* Radial offset of text, in the direction of the symbol's anchor. Useful in combination with {@link PropertyFactory#textVariableAnchor}, which doesn't support the two-dimensional {@link PropertyFactory#textOffset}.
* Radial offset of text, in the direction of the symbol's anchor. Useful in combination with {@link PropertyFactory#textVariableAnchor}, which defaults to using the two-dimensional {@link PropertyFactory#textOffset} if present.
*
* @param value a Float value
* @return property wrapper around Float
Expand All @@ -2306,7 +2306,7 @@ public static PropertyValue<Float> textRadialOffset(Float value) {
}

/**
* Radial offset of text, in the direction of the symbol's anchor. Useful in combination with {@link PropertyFactory#textVariableAnchor}, which doesn't support the two-dimensional {@link PropertyFactory#textOffset}.
* Radial offset of text, in the direction of the symbol's anchor. Useful in combination with {@link PropertyFactory#textVariableAnchor}, which defaults to using the two-dimensional {@link PropertyFactory#textOffset} if present.
*
* @param value a Float value
* @return property wrapper around Float
Expand All @@ -2316,7 +2316,7 @@ public static PropertyValue<Expression> textRadialOffset(Expression value) {
}

/**
* To increase the chance of placing high-priority labels on the map, you can provide an array of {@link Property.TEXT_ANCHOR} locations: the render will attempt to place the label at each location, in order, before moving onto the next label. Use `text-justify: auto` to choose justification based on anchor position. To apply an offset, use the {@link PropertyFactory#textRadialOffset} instead of the two-dimensional {@link PropertyFactory#textOffset}.
* To increase the chance of placing high-priority labels on the map, you can provide an array of {@link Property.TEXT_ANCHOR} locations: the render will attempt to place the label at each location, in order, before moving onto the next label. Use `text-justify: auto` to choose justification based on anchor position. To apply an offset, use the {@link PropertyFactory#textRadialOffset} or the two-dimensional {@link PropertyFactory#textOffset}.
*
* @param value a String[] value
* @return property wrapper around String[]
Expand All @@ -2326,7 +2326,7 @@ public static PropertyValue<String[]> textVariableAnchor(String[] value) {
}

/**
* To increase the chance of placing high-priority labels on the map, you can provide an array of {@link Property.TEXT_ANCHOR} locations: the render will attempt to place the label at each location, in order, before moving onto the next label. Use `text-justify: auto` to choose justification based on anchor position. To apply an offset, use the {@link PropertyFactory#textRadialOffset} instead of the two-dimensional {@link PropertyFactory#textOffset}.
* To increase the chance of placing high-priority labels on the map, you can provide an array of {@link Property.TEXT_ANCHOR} locations: the render will attempt to place the label at each location, in order, before moving onto the next label. Use `text-justify: auto` to choose justification based on anchor position. To apply an offset, use the {@link PropertyFactory#textRadialOffset} or the two-dimensional {@link PropertyFactory#textOffset}.
*
* @param value a String[] value
* @return property wrapper around String[]
Expand Down Expand Up @@ -2476,7 +2476,7 @@ public static PropertyValue<Expression> textTransform(Expression value) {
}

/**
* Offset distance of text from its anchor. Positive values indicate right and down, while negative values indicate left and up.
* Offset distance of text from its anchor. Positive values indicate right and down, while negative values indicate left and up. If used with text-variable-anchor, input values will be taken as absolute values. Offsets along the x- and y-axis will be applied automatically based on the anchor position.
*
* @param value a Float[] value
* @return property wrapper around Float[]
Expand All @@ -2486,7 +2486,7 @@ public static PropertyValue<Float[]> textOffset(Float[] value) {
}

/**
* Offset distance of text from its anchor. Positive values indicate right and down, while negative values indicate left and up.
* Offset distance of text from its anchor. Positive values indicate right and down, while negative values indicate left and up. If used with text-variable-anchor, input values will be taken as absolute values. Offsets along the x- and y-axis will be applied automatically based on the anchor position.
*
* @param value a Float[] value
* @return property wrapper around Float[]
Expand Down
13 changes: 5 additions & 8 deletions platform/darwin/src/MGLSymbolStyleLayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -1404,8 +1404,7 @@ MGL_EXPORT
ems downward. Set this property to `nil` to reset it to the default value.

This property is only applied to the style if `text` is non-`nil`, and
`textRadialOffset` is set to `nil`, and `textVariableAnchor` is set to `nil`.
Otherwise, it is ignored.
`textRadialOffset` is set to `nil`. Otherwise, it is ignored.

You can set this property to an expression containing any of the following:

Expand All @@ -1428,8 +1427,7 @@ MGL_EXPORT
ems upward. Set this property to `nil` to reset it to the default value.

This property is only applied to the style if `text` is non-`nil`, and
`textRadialOffset` is set to `nil`, and `textVariableAnchor` is set to `nil`.
Otherwise, it is ignored.
`textRadialOffset` is set to `nil`. Otherwise, it is ignored.

You can set this property to an expression containing any of the following:

Expand Down Expand Up @@ -1521,8 +1519,8 @@ MGL_EXPORT

/**
Radial offset of text, in the direction of the symbol's anchor. Useful in
combination with `textVariableAnchor`, which doesn't support the
two-dimensional `textOffset`.
combination with `textVariableAnchor`, which defaults to using the
two-dimensional `textOffset` if present.

This property is measured in ems.

Expand Down Expand Up @@ -1634,8 +1632,7 @@ MGL_EXPORT
provide an array of `textAnchor` locations: the render will attempt to place
the label at each location, in order, before moving onto the next label. Use
`textJustify: auto` to choose justification based on anchor position. To apply
an offset, use the `textRadialOffset` instead of the two-dimensional
`textOffset`.
an offset, use the `textRadialOffset` or the two-dimensional `textOffset`.

This property is only applied to the style if `text` is non-`nil`, and
`symbolPlacement` is set to an expression that evaluates to or
Expand Down
1 change: 1 addition & 0 deletions platform/ios/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT
* Fixed a bug with annotation view positions after camera transitions. ([#15122](https://github.com/mapbox/mapbox-gl-native/pull/15122/))
* Fixed a rendering issue of `collisionBox` when `text-translate` or `icon-translate` is enabled. ([#15467](https://github.com/mapbox/mapbox-gl-native/pull/15467))
* Fixed an issue where the scale bar text would become illegible if iOS 13 dark mode was enabled. ([#15524](https://github.com/mapbox/mapbox-gl-native/pull/15524))
* Enable using of `text-offset` option together with `text-variable-anchor` (if `text-radial-offset` option is not provided). If used with `text-variable-anchor`, input values will be taken as absolute values. Offsets along the x- and y-axis will be applied automatically based on the anchor position. ([#15542](https://github.com/mapbox/mapbox-gl-native/pull/15542))

### Performance improvements

Expand Down
4 changes: 2 additions & 2 deletions src/mbgl/layout/symbol_instance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ SymbolInstance::SymbolInstance(Anchor& anchor_,
const float overscaling,
const float iconRotation,
const float textRotation,
float radialTextOffset_,
const std::array<float, 2>& variableTextOffset_,
bool allowVerticalPlacement,
const SymbolContent iconType) :
sharedData(std::move(sharedData_)),
Expand All @@ -104,7 +104,7 @@ SymbolInstance::SymbolInstance(Anchor& anchor_,
iconOffset(iconOffset_),
key(std::move(key_)),
textBoxScale(textBoxScale_),
radialTextOffset(radialTextOffset_),
variableTextOffset(variableTextOffset_),
singleLine(shapedTextOrientations.singleLine) {
// 'hasText' depends on finding at least one glyph in the shaping that's also in the GlyphPositionMap
if(!sharedData->empty()) symbolContent |= SymbolContent::Text;
Expand Down
4 changes: 2 additions & 2 deletions src/mbgl/layout/symbol_instance.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class SymbolInstance {
const float overscaling,
const float iconRotation,
const float textRotation,
float radialTextOffset,
const std::array<float, 2>& variableTextOffset,
bool allowVerticalPlacement,
const SymbolContent iconType = SymbolContent::None);

Expand Down Expand Up @@ -130,7 +130,7 @@ class SymbolInstance {
optional<size_t> placedIconIndex;
optional<size_t> placedVerticalIconIndex;
float textBoxScale;
float radialTextOffset;
std::array<float, 2> variableTextOffset;
bool singleLine;
uint32_t crossTileID = 0;
};
Expand Down
101 changes: 78 additions & 23 deletions src/mbgl/layout/symbol_layout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,13 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters,
pixelRatio(parameters.pixelRatio),
tileSize(util::tileSize * overscaling),
tilePixelRatio(float(util::EXTENT) / tileSize),
textSize(toSymbolLayerProperties(layers.at(0)).layerImpl().layout.get<TextSize>()),
iconSize(toSymbolLayerProperties(layers.at(0)).layerImpl().layout.get<IconSize>()),
layout(createLayout(toSymbolLayerProperties(layers.at(0)).layerImpl().layout, zoom)) {
const SymbolLayer::Impl& leader = toSymbolLayerProperties(layers.at(0)).layerImpl();

textSize = leader.layout.get<TextSize>();
iconSize = leader.layout.get<IconSize>();
textRadialOffset = leader.layout.get<TextRadialOffset>();

const bool hasText = has<TextField>(*layout) && has<TextFont>(*layout);
const bool hasIcon = has<IconImage>(*layout);

Expand Down Expand Up @@ -235,29 +237,27 @@ Shaping& shapingForTextJustifyType(ShapedTextOrientations& shapedTextOrientation
}
}

} // namespace

// static
Point<float> SymbolLayout::evaluateRadialOffset(SymbolAnchorType anchor, float radialOffset) {
Point<float> result{};
std::array<float, 2> evaluateRadialOffset(style::SymbolAnchorType anchor, float radialOffset) {
std::array<float, 2> result{{0.0f, 0.0f}};
if (radialOffset < 0.0f) radialOffset = 0.0f; // Ignore negative offset.
// solve for r where r^2 + r^2 = radialOffset^2
const float sqrt2 = 1.41421356237f;
const float hypotenuse = radialOffset / sqrt2;

switch (anchor) {
case SymbolAnchorType::TopRight:
case SymbolAnchorType::TopLeft:
result.y = hypotenuse - baselineOffset;
result[1] = hypotenuse - baselineOffset;
break;
case SymbolAnchorType::BottomRight:
case SymbolAnchorType::BottomLeft:
result.y = -hypotenuse + baselineOffset;
result[1] = -hypotenuse + baselineOffset;
break;
case SymbolAnchorType::Bottom:
result.y = -radialOffset + baselineOffset;
result[1] = -radialOffset + baselineOffset;
break;
case SymbolAnchorType::Top:
result.y = radialOffset - baselineOffset;
result[1] = radialOffset - baselineOffset;
break;
default:
break;
Expand All @@ -266,17 +266,17 @@ Point<float> SymbolLayout::evaluateRadialOffset(SymbolAnchorType anchor, float r
switch (anchor) {
case SymbolAnchorType::TopRight:
case SymbolAnchorType::BottomRight:
result.x = -hypotenuse;
result[0] = -hypotenuse;
break;
case SymbolAnchorType::TopLeft:
case SymbolAnchorType::BottomLeft:
result.x = hypotenuse;
result[0] = hypotenuse;
break;
case SymbolAnchorType::Left:
result.x = radialOffset;
result[0] = radialOffset;
break;
case SymbolAnchorType::Right:
result.x = -radialOffset;
result[0] = -radialOffset;
break;
default:
break;
Expand All @@ -285,6 +285,54 @@ Point<float> SymbolLayout::evaluateRadialOffset(SymbolAnchorType anchor, float r
return result;
}

} // namespace

// static
std::array<float, 2> SymbolLayout::evaluateVariableOffset(style::SymbolAnchorType anchor, std::array<float, 2> offset) {
if (offset[1] == INVALID_OFFSET_VALUE) {
return evaluateRadialOffset(anchor, offset[0]);
}
std::array<float, 2> result{{0.0f, 0.0f}};
offset[0] = std::abs(offset[0]);
offset[1] = std::abs(offset[1]);

switch (anchor) {
case SymbolAnchorType::TopRight:
case SymbolAnchorType::TopLeft:
case SymbolAnchorType::Top:
result[1] = offset[1] - baselineOffset;
break;
case SymbolAnchorType::BottomRight:
case SymbolAnchorType::BottomLeft:
case SymbolAnchorType::Bottom:
result[1] = -offset[1] + baselineOffset;
break;
case SymbolAnchorType::Center:
case SymbolAnchorType::Left:
case SymbolAnchorType::Right:
break;
}

switch (anchor) {
case SymbolAnchorType::TopRight:
case SymbolAnchorType::BottomRight:
case SymbolAnchorType::Right:
result[0] = -offset[0];
break;
case SymbolAnchorType::TopLeft:
case SymbolAnchorType::BottomLeft:
case SymbolAnchorType::Left:
result[0] = offset[0];
break;
case SymbolAnchorType::Center:
case SymbolAnchorType::Top:
case SymbolAnchorType::Bottom:
break;
}

return result;
}

void SymbolLayout::prepareSymbols(const GlyphMap& glyphMap, const GlyphPositions& glyphPositions,
const ImageMap& imageMap, const ImagePositions& imagePositions) {
const bool isPointPlacement = layout->get<SymbolPlacement>() == SymbolPlacementType::Point;
Expand All @@ -296,7 +344,7 @@ void SymbolLayout::prepareSymbols(const GlyphMap& glyphMap, const GlyphPositions

ShapedTextOrientations shapedTextOrientations;
optional<PositionedIcon> shapedIcon;
Point<float> textOffset;
std::array<float, 2> textOffset{{0.0f, 0.0f}};

// if feature has text, shape the text
if (feature.formattedText) {
Expand All @@ -320,18 +368,18 @@ void SymbolLayout::prepareSymbols(const GlyphMap& glyphMap, const GlyphPositions
return result;
};
const std::vector<style::TextVariableAnchorType> variableTextAnchor = layout->evaluate<TextVariableAnchor>(zoom, feature);
const float radialOffset = layout->evaluate<TextRadialOffset>(zoom, feature);
const SymbolAnchorType textAnchor = layout->evaluate<TextAnchor>(zoom, feature);
if (variableTextAnchor.empty()) {
// Layers with variable anchors use the `text-radial-offset` property and the [x, y] offset vector
// is calculated at placement time instead of layout time
const float radialOffset = layout->evaluate<TextRadialOffset>(zoom, feature);
if (radialOffset > 0.0f) {
// The style spec says don't use `text-offset` and `text-radial-offset` together
// but doesn't actually specify what happens if you use both. We go with the radial offset.
textOffset = evaluateRadialOffset(textAnchor, radialOffset * util::ONE_EM);
} else {
textOffset = { layout->evaluate<TextOffset>(zoom, feature)[0] * util::ONE_EM,
layout->evaluate<TextOffset>(zoom, feature)[1] * util::ONE_EM};
textOffset = {{layout->evaluate<TextOffset>(zoom, feature)[0] * util::ONE_EM,
layout->evaluate<TextOffset>(zoom, feature)[1] * util::ONE_EM}};
}
}
TextJustifyType textJustify = textAlongLine ? TextJustifyType::Center : layout->evaluate<TextJustify>(zoom, feature);
Expand Down Expand Up @@ -435,14 +483,13 @@ void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex,
const ShapedTextOrientations& shapedTextOrientations,
optional<PositionedIcon> shapedIcon,
const GlyphPositions& glyphPositions,
Point<float> offset,
std::array<float, 2> textOffset,
const SymbolContent iconType) {
const float minScale = 0.5f;
const float glyphSize = 24.0f;

const float layoutTextSize = layout->evaluate<TextSize>(zoom + 1, feature);
const float layoutIconSize = layout->evaluate<IconSize>(zoom + 1, feature);
const std::array<float, 2> textOffset = {{ offset.x, offset.y }};

const std::array<float, 2> iconOffset = layout->evaluate<IconOffset>(zoom, feature);

Expand All @@ -462,7 +509,15 @@ void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex,
const float textMaxAngle = layout->get<TextMaxAngle>() * util::DEG2RAD;
const float iconRotation = layout->evaluate<IconRotate>(zoom, feature);
const float textRotation = layout->evaluate<TextRotate>(zoom, feature);
const float radialTextOffset = layout->evaluate<TextRadialOffset>(zoom, feature) * util::ONE_EM;
std::array<float, 2> variableTextOffset;
if (!textRadialOffset.isUndefined()) {
variableTextOffset = {{layout->evaluate<TextRadialOffset>(zoom, feature) * util::ONE_EM,
INVALID_OFFSET_VALUE}};
} else {
variableTextOffset = {{layout->evaluate<TextOffset>(zoom, feature)[0] * util::ONE_EM,
layout->evaluate<TextOffset>(zoom, feature)[1] * util::ONE_EM}};
}

const SymbolPlacementType textPlacement = layout->get<TextRotationAlignment>() != AlignmentType::Map
? SymbolPlacementType::Point
: layout->get<SymbolPlacement>();
Expand Down Expand Up @@ -503,7 +558,7 @@ void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex,
iconBoxScale, iconPadding, iconOffset, indexedFeature,
layoutFeatureIndex, feature.index,
feature.formattedText ? feature.formattedText->rawText() : std::u16string(),
overscaling, iconRotation, textRotation, radialTextOffset, allowVerticalPlacement, iconType);
overscaling, iconRotation, textRotation, variableTextOffset, allowVerticalPlacement, iconType);
}
};

Expand Down
Loading