From d7b5b2b377a9e642208169287b9c601602a5e774 Mon Sep 17 00:00:00 2001 From: Matthias Frei Date: Fri, 9 Jan 2026 14:25:50 +0100 Subject: [PATCH] Fix hasIconPotentially check hasIconPotentially logic tries to filter out symbols that never need to be drawn. For this, we tried to evaluate icon-image expression with zoom/state independent context and if that is successful and an empty string we know it will never have an icon. This was flawed. First, there was a logic error. Should have been hasIconPotentially = (iconImage == nullptr || !iconImage->empty()); instead of hasIconPotentially = (iconImage != nullptr && !iconImage->empty()); Also, the zoom-indpendent evaluation does not work as was assumed. The assumption was that accessing state or zoom in an expression would always result in std::monostate value to indicate evaluation failure. However that's not consistently the case. For example, "step" expressions will return the first value in such a case. Finally, it did not correctly handle result values other than strings, breaking it e.g. for "concat" expressions. Fixed by relying on (more conservative) usedKeys. Fixes "Filter symbols with neither text nor icon early" (e0e2e54b5b86affd3f71cad7303b6b233ec33907) --- .../symbol/Tiled2dMapVectorSymbolGroup.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/shared/src/map/layers/tiled/vector/symbol/Tiled2dMapVectorSymbolGroup.cpp b/shared/src/map/layers/tiled/vector/symbol/Tiled2dMapVectorSymbolGroup.cpp index deb82a0e4..c93562aa0 100644 --- a/shared/src/map/layers/tiled/vector/symbol/Tiled2dMapVectorSymbolGroup.cpp +++ b/shared/src/map/layers/tiled/vector/symbol/Tiled2dMapVectorSymbolGroup.cpp @@ -152,13 +152,15 @@ void Tiled2dMapVectorSymbolGroup::initialize(std::weak_ptrstyle.iconImageEvaluator.getValue()) { - // Attempt to evaluate without zoom and state - const auto zoomAndStateIndependentEvalCtx = EvaluationContext(dpFactor, context.get(), nullptr); - const auto value = layerDescription->style.iconImageEvaluator.getValue(); - const auto res = value->evaluate(zoomAndStateIndependentEvalCtx); - const std::string *iconImage = std::get_if(&res); - // if this succeeded returns empty, there is no icon image for any zoom level. - hasIconPotentially = (iconImage != nullptr && !iconImage->empty()); + const auto iconImageUsedKeys = layerDescription->style.iconImageEvaluator.getValue()->getUsedKeys(); + if (iconImageUsedKeys.isStateDependant() || + iconImageUsedKeys.containsUsedKey(ValueKeys::ZOOM)) { + hasIconPotentially = true; + } else { + // Expression independent of zoom/state, so we can evaluate to see if there is an icon for this feature. + const auto iconImage = layerDescription->style.getIconImage(evalContext); + hasIconPotentially = !iconImage.value.empty(); + } } else { hasIconPotentially = false; }