From 0eab78d63a573b5fa2e5046e44cbd0109df8cbc5 Mon Sep 17 00:00:00 2001 From: "Thiago Marcos P. Santos" Date: Thu, 8 Mar 2018 23:56:42 +0200 Subject: Bump Mapbox GL Native Bump version. mapbox-gl-native @ 27b21363e62c105db0b040b4c5a5ef31170ebd30 --- include/mbgl/renderer/renderer_backend.hpp | 6 +- .../conversion/data_driven_property_value.hpp | 33 +- include/mbgl/style/conversion/expression.hpp | 39 --- .../conversion/heatmap_color_property_value.hpp | 7 +- include/mbgl/style/conversion/property_value.hpp | 9 +- include/mbgl/style/data_driven_property_value.hpp | 9 + include/mbgl/style/expression/length.hpp | 32 ++ include/mbgl/style/expression/let.hpp | 3 + include/mbgl/style/expression/literal.hpp | 4 + include/mbgl/style/expression/parsing_context.hpp | 34 +- include/mbgl/style/filter.hpp | 22 +- include/mbgl/style/filter_evaluator.hpp | 259 ++------------- include/mbgl/style/function/camera_function.hpp | 11 +- include/mbgl/style/function/composite_function.hpp | 13 +- include/mbgl/style/function/source_function.hpp | 15 +- include/mbgl/style/layers/custom_layer.hpp | 89 +++-- include/mbgl/util/color.hpp | 2 + include/mbgl/util/projection.hpp | 16 +- mapbox-gl-native.pro | 7 +- platform/default/mbgl/storage/offline.cpp | 2 +- platform/default/mbgl/util/default_styles.hpp | 3 - platform/default/sqlite3.cpp | 11 +- platform/qt/include/qmapbox.hpp | 22 +- platform/qt/include/qmapboxgl.hpp | 5 +- platform/qt/src/qmapbox.cpp | 22 +- platform/qt/src/qmapboxgl.cpp | 80 +++-- src/mbgl/annotation/render_annotation_source.cpp | 4 +- src/mbgl/annotation/render_annotation_source.hpp | 2 +- src/mbgl/geometry/feature_index.cpp | 109 +++--- src/mbgl/geometry/feature_index.hpp | 59 ++-- src/mbgl/gl/program.hpp | 5 + src/mbgl/layout/symbol_instance.cpp | 4 +- src/mbgl/layout/symbol_layout.cpp | 22 +- src/mbgl/layout/symbol_layout.hpp | 9 +- src/mbgl/layout/symbol_projection.cpp | 6 +- src/mbgl/map/map.cpp | 6 +- src/mbgl/map/transform_state.cpp | 13 + src/mbgl/map/transform_state.hpp | 1 + src/mbgl/programs/symbol_program.hpp | 4 +- src/mbgl/renderer/buckets/symbol_bucket.cpp | 6 + src/mbgl/renderer/buckets/symbol_bucket.hpp | 5 + .../renderer/layers/render_background_layer.cpp | 1 + src/mbgl/renderer/layers/render_circle_layer.cpp | 66 +++- src/mbgl/renderer/layers/render_circle_layer.hpp | 3 +- src/mbgl/renderer/layers/render_custom_layer.cpp | 33 +- src/mbgl/renderer/layers/render_custom_layer.hpp | 3 +- .../layers/render_fill_extrusion_layer.cpp | 7 +- .../layers/render_fill_extrusion_layer.hpp | 3 +- src/mbgl/renderer/layers/render_fill_layer.cpp | 7 +- src/mbgl/renderer/layers/render_fill_layer.hpp | 3 +- src/mbgl/renderer/layers/render_heatmap_layer.cpp | 6 +- src/mbgl/renderer/layers/render_heatmap_layer.hpp | 3 +- src/mbgl/renderer/layers/render_line_layer.cpp | 7 +- src/mbgl/renderer/layers/render_line_layer.hpp | 3 +- src/mbgl/renderer/render_layer.hpp | 5 +- src/mbgl/renderer/render_source.hpp | 2 +- src/mbgl/renderer/renderer_impl.cpp | 58 ++-- src/mbgl/renderer/renderer_impl.hpp | 8 +- .../sources/render_custom_geometry_source.cpp | 4 +- .../sources/render_custom_geometry_source.hpp | 2 +- .../renderer/sources/render_geojson_source.cpp | 4 +- .../renderer/sources/render_geojson_source.hpp | 2 +- src/mbgl/renderer/sources/render_image_source.cpp | 2 +- src/mbgl/renderer/sources/render_image_source.hpp | 2 +- .../renderer/sources/render_raster_dem_source.cpp | 2 +- .../renderer/sources/render_raster_dem_source.hpp | 2 +- src/mbgl/renderer/sources/render_raster_source.cpp | 2 +- src/mbgl/renderer/sources/render_raster_source.hpp | 2 +- src/mbgl/renderer/sources/render_vector_source.cpp | 4 +- src/mbgl/renderer/sources/render_vector_source.hpp | 2 +- src/mbgl/renderer/tile_pyramid.cpp | 13 +- src/mbgl/renderer/tile_pyramid.hpp | 2 +- src/mbgl/shaders/collision_box.cpp | 5 +- src/mbgl/shaders/line.cpp | 3 + src/mbgl/shaders/line_pattern.cpp | 10 +- src/mbgl/shaders/symbol_icon.cpp | 7 +- src/mbgl/shaders/symbol_sdf.cpp | 7 +- src/mbgl/style/conversion/filter.cpp | 68 +++- src/mbgl/style/conversion/stringify.hpp | 135 +------- src/mbgl/style/conversion/tileset.cpp | 2 + src/mbgl/style/expression/at.cpp | 14 +- src/mbgl/style/expression/compound_expression.cpp | 25 +- src/mbgl/style/expression/interpolate.cpp | 6 +- src/mbgl/style/expression/length.cpp | 66 ++++ src/mbgl/style/expression/parsing_context.cpp | 72 +++- src/mbgl/style/expression/value.cpp | 9 +- src/mbgl/style/filter.cpp | 13 + src/mbgl/style/filter_evaluator.cpp | 225 +++++++++++++ src/mbgl/style/layers/background_layer.cpp | 2 + src/mbgl/style/layers/circle_layer.cpp | 2 + src/mbgl/style/layers/custom_layer.cpp | 16 +- src/mbgl/style/layers/custom_layer_impl.cpp | 12 +- src/mbgl/style/layers/custom_layer_impl.hpp | 14 +- src/mbgl/style/layers/fill_extrusion_layer.cpp | 2 + src/mbgl/style/layers/fill_layer.cpp | 2 + src/mbgl/style/layers/heatmap_layer.cpp | 2 + src/mbgl/style/layers/hillshade_layer.cpp | 2 + src/mbgl/style/layers/line_layer.cpp | 2 + src/mbgl/style/layers/raster_layer.cpp | 2 + src/mbgl/style/layers/symbol_layer.cpp | 2 + src/mbgl/text/collision_index.cpp | 61 +--- src/mbgl/text/collision_index.hpp | 5 +- src/mbgl/text/cross_tile_symbol_index.cpp | 6 +- src/mbgl/text/placement.cpp | 45 ++- src/mbgl/text/placement.hpp | 20 +- src/mbgl/tile/custom_geometry_tile.cpp | 2 +- src/mbgl/tile/geojson_tile.cpp | 2 +- src/mbgl/tile/geometry_tile.cpp | 91 +++-- src/mbgl/tile/geometry_tile.hpp | 47 +-- src/mbgl/tile/geometry_tile_worker.cpp | 180 +++++----- src/mbgl/tile/geometry_tile_worker.hpp | 14 +- src/mbgl/tile/tile.cpp | 6 +- src/mbgl/tile/tile.hpp | 9 +- src/mbgl/util/color.cpp | 22 +- src/mbgl/util/i18n.cpp | 37 ++- src/mbgl/util/i18n.hpp | 2 + src/mbgl/util/intersection_tests.cpp | 9 +- src/mbgl/util/intersection_tests.hpp | 1 + src/mbgl/util/throttler.cpp | 36 -- src/mbgl/util/throttler.hpp | 22 -- src/mbgl/util/tile_cover.cpp | 104 ++++-- src/mbgl/util/tile_cover.hpp | 24 +- src/mbgl/util/tile_cover_impl.cpp | 365 +++++++++++++++++++++ src/mbgl/util/tile_cover_impl.hpp | 90 +++++ 124 files changed, 2042 insertions(+), 1158 deletions(-) delete mode 100644 include/mbgl/style/conversion/expression.hpp create mode 100644 include/mbgl/style/expression/length.hpp create mode 100644 src/mbgl/style/expression/length.cpp create mode 100644 src/mbgl/style/filter.cpp create mode 100644 src/mbgl/style/filter_evaluator.cpp delete mode 100644 src/mbgl/util/throttler.cpp delete mode 100644 src/mbgl/util/throttler.hpp create mode 100644 src/mbgl/util/tile_cover_impl.cpp create mode 100644 src/mbgl/util/tile_cover_impl.hpp diff --git a/include/mbgl/renderer/renderer_backend.hpp b/include/mbgl/renderer/renderer_backend.hpp index b83c128169..1d5f4e8f4e 100644 --- a/include/mbgl/renderer/renderer_backend.hpp +++ b/include/mbgl/renderer/renderer_backend.hpp @@ -47,10 +47,10 @@ protected: // as a matched pair, exclusively through BackendScope, in two situations: // // 1. When releasing GL resources during Renderer destruction - // (Including calling CustomLayerDeinitializeFunction during RenderCustomLayer destruction) + // (Including calling CustomLayerHost::deinitialize during RenderCustomLayer destruction) // 2. When renderering through Renderer::render() - // (Including calling CustomLayerDeinitializeFunction for newly added custom layers and - // CustomLayerDeinitializeFunction on layer removal) + // (Including calling CustomLayerHost::initialize for newly added custom layers and + // CustomLayerHost::deinitialize on layer removal) virtual void activate() = 0; virtual void deactivate() = 0; diff --git a/include/mbgl/style/conversion/data_driven_property_value.hpp b/include/mbgl/style/conversion/data_driven_property_value.hpp index 8880d28fb1..07ed201c99 100644 --- a/include/mbgl/style/conversion/data_driven_property_value.hpp +++ b/include/mbgl/style/conversion/data_driven_property_value.hpp @@ -4,10 +4,12 @@ #include #include #include -#include #include #include #include +#include +#include +#include #include @@ -20,24 +22,35 @@ template struct Converter> { optional> operator()(const Convertible& value, Error& error) const { + using namespace mbgl::style::expression; + if (isUndefined(value)) { return DataDrivenPropertyValue(); - } else if (expression::isExpression(value)) { - optional> expression = convert>( - value, - error, - valueTypeToExpressionType()); - + } else if (isExpression(value)) { + ParsingContext ctx(valueTypeToExpressionType()); + ParseResult expression = ctx.parseLayerPropertyExpression(value); if (!expression) { + error = { ctx.getCombinedErrors() }; return {}; } - if (isFeatureConstant(**expression)) { + bool featureConstant = isFeatureConstant(**expression); + bool zoomConstant = isZoomConstant(**expression); + + if (featureConstant && !zoomConstant) { return DataDrivenPropertyValue(CameraFunction(std::move(*expression))); - } else if (isZoomConstant(**expression)) { + } else if (!featureConstant && zoomConstant) { return DataDrivenPropertyValue(SourceFunction(std::move(*expression))); - } else { + } else if (!featureConstant && !zoomConstant) { return DataDrivenPropertyValue(CompositeFunction(std::move(*expression))); + } else { + auto literal = dynamic_cast(expression->get()); + assert(literal); + optional constant = fromExpressionValue(literal->getValue()); + if (!constant) { + return {}; + } + return DataDrivenPropertyValue(*constant); } } else if (!isObject(value)) { optional constant = convert(value, error); diff --git a/include/mbgl/style/conversion/expression.hpp b/include/mbgl/style/conversion/expression.hpp deleted file mode 100644 index c5fcf906a7..0000000000 --- a/include/mbgl/style/conversion/expression.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - -namespace mbgl { -namespace style { -namespace conversion { - -using namespace mbgl::style::expression; - -template<> struct Converter> { - optional> operator()(const Convertible& value, Error& error, type::Type expected) const { - ParsingContext ctx(optional {expected}); - ParseResult parsed = ctx.parse(value); - if (parsed) { - return std::move(*parsed); - } - std::string combinedError; - for (const ParsingError& parsingError : ctx.getErrors()) { - if (combinedError.size() > 0) { - combinedError += "\n"; - } - if (parsingError.key.size() > 0) { - combinedError += parsingError.key + ": "; - } - combinedError += parsingError.message; - } - error = { combinedError }; - return {}; - }; -}; - -} // namespace conversion -} // namespace style -} // namespace mbgl diff --git a/include/mbgl/style/conversion/heatmap_color_property_value.hpp b/include/mbgl/style/conversion/heatmap_color_property_value.hpp index e3689c524c..a4710792d2 100644 --- a/include/mbgl/style/conversion/heatmap_color_property_value.hpp +++ b/include/mbgl/style/conversion/heatmap_color_property_value.hpp @@ -4,11 +4,11 @@ #include #include #include -#include #include #include #include #include +#include namespace mbgl { namespace style { @@ -17,11 +17,14 @@ namespace conversion { template <> struct Converter { optional operator()(const Convertible& value, Error& error) const { + using namespace mbgl::style::expression; if (isUndefined(value)) { return HeatmapColorPropertyValue(); } else if (isExpression(value)) { - optional> expression = convert>(value, error, expression::type::Color); + ParsingContext ctx(type::Color); + ParseResult expression = ctx.parseLayerPropertyExpression(value); if (!expression) { + error = { ctx.getCombinedErrors() }; return {}; } assert(*expression); diff --git a/include/mbgl/style/conversion/property_value.hpp b/include/mbgl/style/conversion/property_value.hpp index 97117de2ec..3130661f61 100644 --- a/include/mbgl/style/conversion/property_value.hpp +++ b/include/mbgl/style/conversion/property_value.hpp @@ -4,11 +4,11 @@ #include #include #include -#include #include #include #include #include +#include namespace mbgl { namespace style { @@ -17,13 +17,18 @@ namespace conversion { template struct Converter> { optional> operator()(const Convertible& value, Error& error) const { + using namespace mbgl::style::expression; + if (isUndefined(value)) { return PropertyValue(); } else if (isExpression(value)) { - optional> expression = convert>(value, error, valueTypeToExpressionType()); + ParsingContext ctx(valueTypeToExpressionType()); + ParseResult expression = ctx.parseLayerPropertyExpression(value); if (!expression) { + error = { ctx.getCombinedErrors() }; return {}; } + if (isFeatureConstant(**expression)) { return { CameraFunction(std::move(*expression)) }; } else { diff --git a/include/mbgl/style/data_driven_property_value.hpp b/include/mbgl/style/data_driven_property_value.hpp index 5d7c596363..0a1fce29c7 100644 --- a/include/mbgl/style/data_driven_property_value.hpp +++ b/include/mbgl/style/data_driven_property_value.hpp @@ -50,6 +50,15 @@ public: return !value.template is>() && !value.template is>(); } + bool isExpression() const { + return value.match( + [] (const Undefined&) { return false; }, + [] (const T&) { return false; }, + [] (const CameraFunction& fn) { return fn.isExpression; }, + [] (const SourceFunction& fn) { return fn.isExpression; }, + [] (const CompositeFunction& fn) { return fn.isExpression; }); + } + template auto match(Ts&&... ts) const { return value.match(std::forward(ts)...); diff --git a/include/mbgl/style/expression/length.hpp b/include/mbgl/style/expression/length.hpp new file mode 100644 index 0000000000..1d754f1932 --- /dev/null +++ b/include/mbgl/style/expression/length.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include + +#include +#include + +namespace mbgl { +namespace style { +namespace expression { + +class Length : public Expression { +public: + Length(std::unique_ptr input); + + static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx); + + EvaluationResult evaluate(const EvaluationContext& params) const override; + void eachChild(const std::function& visit) const override; + bool operator==(const Expression& e) const override; + std::vector> possibleOutputs() const override; + std::string getOperator() const override { return "length"; } + +private: + std::unique_ptr input; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/expression/let.hpp b/include/mbgl/style/expression/let.hpp index 75d2adda62..d0210d8bba 100644 --- a/include/mbgl/style/expression/let.hpp +++ b/include/mbgl/style/expression/let.hpp @@ -70,6 +70,9 @@ public: mbgl::Value serialize() const override; std::string getOperator() const override { return "var"; } + + const std::shared_ptr& getBoundExpression() const { return value; } + private: std::string name; std::shared_ptr value; diff --git a/include/mbgl/style/expression/literal.hpp b/include/mbgl/style/expression/literal.hpp index d854b419f4..a00c468efc 100644 --- a/include/mbgl/style/expression/literal.hpp +++ b/include/mbgl/style/expression/literal.hpp @@ -40,6 +40,10 @@ public: std::vector> possibleOutputs() const override { return {{ value }}; } + + Value getValue() const { + return value; + } mbgl::Value serialize() const override; std::string getOperator() const override { return "literal"; } diff --git a/include/mbgl/style/expression/parsing_context.hpp b/include/mbgl/style/expression/parsing_context.hpp index f92a4c95ea..c19974a4f7 100644 --- a/include/mbgl/style/expression/parsing_context.hpp +++ b/include/mbgl/style/expression/parsing_context.hpp @@ -55,7 +55,7 @@ class ParsingContext { public: ParsingContext() : errors(std::make_shared>()) {} ParsingContext(std::string key_) : key(std::move(key_)), errors(std::make_shared>()) {} - explicit ParsingContext(optional expected_) + explicit ParsingContext(type::Type expected_) : expected(std::move(expected_)), errors(std::make_shared>()) {} @@ -67,6 +67,7 @@ public: std::string getKey() const { return key; } optional getExpected() const { return expected; } const std::vector& getErrors() const { return *errors; } + const std::string getCombinedErrors() const; enum TypeAnnotationOption { includeTypeAnnotations, @@ -74,16 +75,21 @@ public: }; /* - Parse the given style-spec JSON value into an Expression object. - Specifically, this function is responsible for determining the expression - type (either Literal, or the one named in value[0]) and dispatching to the - appropriate ParseXxxx::parse(const V&, ParsingContext) method. + Parse the given style-spec JSON value as an expression. */ - ParseResult parse(const mbgl::style::conversion::Convertible& value, - TypeAnnotationOption typeAnnotationOption = includeTypeAnnotations); + ParseResult parseExpression(const mbgl::style::conversion::Convertible& value, + TypeAnnotationOption typeAnnotationOption = includeTypeAnnotations); /* - Parse a child expression. + Parse the given style-spec JSON value as an expression intended to be used + in a layout or paint property. This entails checking additional constraints + that exist in that context but not, e.g., for filters. + */ + ParseResult parseLayerPropertyExpression(const mbgl::style::conversion::Convertible& value, + TypeAnnotationOption typeAnnotationOption = includeTypeAnnotations); + + /* + Parse a child expression. For use by individual Expression::parse() methods. */ ParseResult parse(const mbgl::style::conversion::Convertible&, std::size_t, @@ -91,7 +97,7 @@ public: TypeAnnotationOption typeAnnotationOption = includeTypeAnnotations); /* - Parse a child expression. + Parse a child expression. For use by individual Expression::parse() methods. */ ParseResult parse(const mbgl::style::conversion::Convertible&, std::size_t index, @@ -141,6 +147,16 @@ private: errors(std::move(errors_)) {} + + /* + Parse the given style-spec JSON value into an Expression object. + Specifically, this function is responsible for determining the expression + type (either Literal, or the one named in value[0]) and dispatching to the + appropriate ParseXxxx::parse(const V&, ParsingContext) method. + */ + ParseResult parse(const mbgl::style::conversion::Convertible& value, + TypeAnnotationOption typeAnnotationOption = includeTypeAnnotations); + std::string key; optional expected; std::shared_ptr scope; diff --git a/include/mbgl/style/filter.hpp b/include/mbgl/style/filter.hpp index a204a2b17a..ccf8dce188 100644 --- a/include/mbgl/style/filter.hpp +++ b/include/mbgl/style/filter.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -232,6 +233,15 @@ public: return true; } }; + +class ExpressionFilter { +public: + std::shared_ptr expression; + + friend bool operator==(const ExpressionFilter& lhs, const ExpressionFilter& rhs) { + return *(lhs.expression) == *(rhs.expression); + } +}; using FilterBase = variant< @@ -258,19 +268,13 @@ using FilterBase = variant< class IdentifierInFilter, class IdentifierNotInFilter, class HasIdentifierFilter, - class NotHasIdentifierFilter>; + class NotHasIdentifierFilter, + class ExpressionFilter>; class Filter : public FilterBase { public: using FilterBase::FilterBase; - - bool operator()(const Feature&) const; - - template - bool operator()(const GeometryTileFeature&) const; - - template - bool operator()(FeatureType type, optional id, PropertyAccessor accessor) const; + bool operator()(const expression::EvaluationContext& context) const; }; } // namespace style diff --git a/include/mbgl/style/filter_evaluator.hpp b/include/mbgl/style/filter_evaluator.hpp index 66223d7282..a4a4098b3f 100644 --- a/include/mbgl/style/filter_evaluator.hpp +++ b/include/mbgl/style/filter_evaluator.hpp @@ -19,242 +19,37 @@ namespace style { // does not match } */ -template class FilterEvaluator { public: - const FeatureType featureType; - const optional featureIdentifier; - const PropertyAccessor propertyAccessor; + const expression::EvaluationContext context; + + bool operator()(const NullFilter&) const; + bool operator()(const EqualsFilter& filter) const; + bool operator()(const NotEqualsFilter& filter) const; + bool operator()(const LessThanFilter& filter) const; + bool operator()(const LessThanEqualsFilter& filter) const; + bool operator()(const GreaterThanFilter& filter) const; + bool operator()(const GreaterThanEqualsFilter& filter) const; + bool operator()(const InFilter& filter) const; + bool operator()(const NotInFilter& filter) const; + bool operator()(const AnyFilter& filter) const; + bool operator()(const AllFilter& filter) const; + bool operator()(const NoneFilter& filter) const; + bool operator()(const HasFilter& filter) const; + bool operator()(const NotHasFilter& filter) const; + bool operator()(const TypeEqualsFilter& filter) const; + bool operator()(const TypeNotEqualsFilter& filter) const; + bool operator()(const TypeInFilter& filter) const; + bool operator()(const TypeNotInFilter& filter) const; + bool operator()(const IdentifierEqualsFilter& filter) const; + bool operator()(const IdentifierNotEqualsFilter& filter) const; + bool operator()(const IdentifierInFilter& filter) const; + bool operator()(const IdentifierNotInFilter& filter) const; + bool operator()(const HasIdentifierFilter&) const; + bool operator()(const NotHasIdentifierFilter&) const; + bool operator()(const ExpressionFilter&) const; - bool operator()(const NullFilter&) const { - return true; - } - - bool operator()(const EqualsFilter& filter) const { - optional actual = propertyAccessor(filter.key); - return actual && equal(*actual, filter.value); - } - - bool operator()(const NotEqualsFilter& filter) const { - optional actual = propertyAccessor(filter.key); - return !actual || !equal(*actual, filter.value); - } - - bool operator()(const LessThanFilter& filter) const { - optional actual = propertyAccessor(filter.key); - return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ < rhs_; }); - } - - bool operator()(const LessThanEqualsFilter& filter) const { - optional actual = propertyAccessor(filter.key); - return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ <= rhs_; }); - } - - bool operator()(const GreaterThanFilter& filter) const { - optional actual = propertyAccessor(filter.key); - return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ > rhs_; }); - } - - bool operator()(const GreaterThanEqualsFilter& filter) const { - optional actual = propertyAccessor(filter.key); - return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ >= rhs_; }); - } - - bool operator()(const InFilter& filter) const { - optional actual = propertyAccessor(filter.key); - if (!actual) - return false; - for (const auto& v: filter.values) { - if (equal(*actual, v)) { - return true; - } - } - return false; - } - - bool operator()(const NotInFilter& filter) const { - optional actual = propertyAccessor(filter.key); - if (!actual) - return true; - for (const auto& v: filter.values) { - if (equal(*actual, v)) { - return false; - } - } - return true; - } - - bool operator()(const AnyFilter& filter) const { - for (const auto& f: filter.filters) { - if (Filter::visit(f, *this)) { - return true; - } - } - return false; - } - - bool operator()(const AllFilter& filter) const { - for (const auto& f: filter.filters) { - if (!Filter::visit(f, *this)) { - return false; - } - } - return true; - } - - bool operator()(const NoneFilter& filter) const { - for (const auto& f: filter.filters) { - if (Filter::visit(f, *this)) { - return false; - } - } - return true; - } - - bool operator()(const HasFilter& filter) const { - return bool(propertyAccessor(filter.key)); - } - - bool operator()(const NotHasFilter& filter) const { - return !propertyAccessor(filter.key); - } - - - bool operator()(const TypeEqualsFilter& filter) const { - return featureType == filter.value; - } - - bool operator()(const TypeNotEqualsFilter& filter) const { - return featureType != filter.value; - } - - bool operator()(const TypeInFilter& filter) const { - for (const auto& v: filter.values) { - if (featureType == v) { - return true; - } - } - return false; - } - - bool operator()(const TypeNotInFilter& filter) const { - for (const auto& v: filter.values) { - if (featureType == v) { - return false; - } - } - return true; - } - - - bool operator()(const IdentifierEqualsFilter& filter) const { - return featureIdentifier == filter.value; - } - - bool operator()(const IdentifierNotEqualsFilter& filter) const { - return featureIdentifier != filter.value; - } - - bool operator()(const IdentifierInFilter& filter) const { - for (const auto& v: filter.values) { - if (featureIdentifier == v) { - return true; - } - } - return false; - } - - bool operator()(const IdentifierNotInFilter& filter) const { - for (const auto& v: filter.values) { - if (featureIdentifier == v) { - return false; - } - } - return true; - } - - bool operator()(const HasIdentifierFilter&) const { - return bool(featureIdentifier); - } - - bool operator()(const NotHasIdentifierFilter&) const { - return !featureIdentifier; - } - -private: - template - struct Comparator { - const Op& op; - - template - bool operator()(const T& lhs, const T& rhs) const { - return op(lhs, rhs); - } - - template - auto operator()(const T0& lhs, const T1& rhs) const - -> typename std::enable_if_t::value && !std::is_same::value && - std::is_arithmetic::value && !std::is_same::value, bool> { - return op(double(lhs), double(rhs)); - } - - template - auto operator()(const T0&, const T1&) const - -> typename std::enable_if_t::value || std::is_same::value || - !std::is_arithmetic::value || std::is_same::value, bool> { - return false; - } - - bool operator()(const NullValue&, - const NullValue&) const { - // Should be unreachable; null is not currently allowed by the style specification. - assert(false); - return false; - } - - bool operator()(const std::vector&, - const std::vector&) const { - // Should be unreachable; nested values are not currently allowed by the style specification. - assert(false); - return false; - } - - bool operator()(const PropertyMap&, - const PropertyMap&) const { - // Should be unreachable; nested values are not currently allowed by the style specification. - assert(false); - return false; - } - }; - - template - bool compare(const Value& lhs, const Value& rhs, const Op& op) const { - return Value::binary_visit(lhs, rhs, Comparator { op }); - } - - bool equal(const Value& lhs, const Value& rhs) const { - return compare(lhs, rhs, [] (const auto& lhs_, const auto& rhs_) { return lhs_ == rhs_; }); - } }; -inline bool Filter::operator()(const Feature& feature) const { - return operator()(apply_visitor(ToFeatureType(), feature.geometry), feature.id, [&] (const std::string& key) -> optional { - auto it = feature.properties.find(key); - if (it == feature.properties.end()) - return {}; - return it->second; - }); -} - -template -bool Filter::operator()(const GeometryTileFeature& feature) const { - return operator()(feature.getType(), feature.getID(), [&] (const auto& key) { return feature.getValue(key); }); -} - -template -bool Filter::operator()(FeatureType type, optional id, PropertyAccessor accessor) const { - return FilterBase::visit(*this, FilterEvaluator { type, id, accessor }); -} - } // namespace style } // namespace mbgl diff --git a/include/mbgl/style/function/camera_function.hpp b/include/mbgl/style/function/camera_function.hpp index 1da5d2c601..97ba633e44 100644 --- a/include/mbgl/style/function/camera_function.hpp +++ b/include/mbgl/style/function/camera_function.hpp @@ -27,15 +27,16 @@ public: IntervalStops>>; CameraFunction(std::unique_ptr expression_) - : expression(std::move(expression_)), + : isExpression(true), + expression(std::move(expression_)), zoomCurve(expression::findZoomCurveChecked(expression.get())) { assert(!expression::isZoomConstant(*expression)); assert(expression::isFeatureConstant(*expression)); } - CameraFunction(Stops stops_) - : stops(std::move(stops_)), + CameraFunction(const Stops& stops) + : isExpression(false), expression(stops.match([&] (const auto& s) { return expression::Convert::toExpression(s); })), @@ -76,12 +77,10 @@ public: } bool useIntegerZoom = false; + bool isExpression; const expression::Expression& getExpression() const { return *expression; } - // retained for compatibility with pre-expression function API - Stops stops; - private: std::shared_ptr expression; const variant zoomCurve; diff --git a/include/mbgl/style/function/composite_function.hpp b/include/mbgl/style/function/composite_function.hpp index f391b101ae..614c345c25 100644 --- a/include/mbgl/style/function/composite_function.hpp +++ b/include/mbgl/style/function/composite_function.hpp @@ -51,16 +51,16 @@ public: CompositeCategoricalStops>>; CompositeFunction(std::unique_ptr expression_) - : expression(std::move(expression_)), + : isExpression(true), + expression(std::move(expression_)), zoomCurve(expression::findZoomCurveChecked(expression.get())) { assert(!expression::isZoomConstant(*expression)); assert(!expression::isFeatureConstant(*expression)); } - CompositeFunction(std::string property_, Stops stops_, optional defaultValue_ = {}) - : property(std::move(property_)), - stops(std::move(stops_)), + CompositeFunction(const std::string& property, const Stops& stops, optional defaultValue_ = {}) + : isExpression(false), defaultValue(std::move(defaultValue_)), expression(stops.match([&] (const auto& s) { return expression::Convert::toExpression(property, s); @@ -113,12 +113,11 @@ public: const expression::Expression& getExpression() const { return *expression; } - std::string property; - Stops stops; - optional defaultValue; bool useIntegerZoom = false; + bool isExpression; private: + optional defaultValue; std::shared_ptr expression; const variant zoomCurve; }; diff --git a/include/mbgl/style/function/source_function.hpp b/include/mbgl/style/function/source_function.hpp index d3caa90ee5..5b51d0bf81 100644 --- a/include/mbgl/style/function/source_function.hpp +++ b/include/mbgl/style/function/source_function.hpp @@ -30,15 +30,15 @@ public: IdentityStops>>; SourceFunction(std::unique_ptr expression_) - : expression(std::move(expression_)) + : isExpression(true), + expression(std::move(expression_)) { assert(expression::isZoomConstant(*expression)); assert(!expression::isFeatureConstant(*expression)); } - SourceFunction(std::string property_, Stops stops_, optional defaultValue_ = {}) - : property(std::move(property_)), - stops(std::move(stops_)), + SourceFunction(const std::string& property, const Stops& stops, optional defaultValue_ = {}) + : isExpression(false), defaultValue(std::move(defaultValue_)), expression(stops.match([&] (const IdentityStops&) { return expression::Convert::fromIdentityFunction(expression::valueTypeToExpressionType(), property); @@ -67,15 +67,12 @@ public: } bool useIntegerZoom = false; + bool isExpression; const expression::Expression& getExpression() const { return *expression; } - // retained for compatibility with pre-expression function API - std::string property; - Stops stops; - optional defaultValue; - private: + optional defaultValue; std::shared_ptr expression; }; diff --git a/include/mbgl/style/layers/custom_layer.hpp b/include/mbgl/style/layers/custom_layer.hpp index bf3387f95b..fbe3a4a6c2 100644 --- a/include/mbgl/style/layers/custom_layer.hpp +++ b/include/mbgl/style/layers/custom_layer.hpp @@ -2,20 +2,13 @@ #include +#include + namespace mbgl { namespace style { /** - * Initialize any GL state needed by the custom layer. This method is called once, from the - * main thread, at a point when the GL context is active but before rendering for the first - * time. - * - * Resources that are acquired in this method must be released in the UninitializeFunction. - */ -using CustomLayerInitializeFunction = void (*)(void* context); - -/** - * Parameters that define the current camera position for a CustomLayerRenderFunction. + * Parameters that define the current camera position for a `CustomLayerHost::render()` function. */ struct CustomLayerRenderParameters { double width; @@ -26,48 +19,52 @@ struct CustomLayerRenderParameters { double bearing; double pitch; double fieldOfView; + std::array projectionMatrix; }; -/** - * Render the layer. This method is called once per frame. The implementation should not make - * any assumptions about the GL state (other than that the correct context is active). It may - * make changes to the state, and is not required to reset values such as the depth mask, stencil - * mask, and corresponding test flags to their original values. - * Make sure that you are drawing your fragments with a z value of 1 to take advantage of the - * opaque fragment culling in case there are opaque layers above your custom layer. - */ -using CustomLayerRenderFunction = void (*)(void* context, const CustomLayerRenderParameters&); - -/** - * Called when the system has destroyed the underlying GL context. The - * `CustomLayerDeinitializeFunction` will not be called in this case, however - * `CustomLayerInitializeFunction` will be called instead to prepare for a new render. - * - */ -using CustomLayerContextLostFunction = void (*)(void* context); - -/** - * Destroy any GL state needed by the custom layer, and deallocate context, if necessary. This - * method is called once, from the main thread, at a point when the GL context is active. - * - * Note that it may be called even when the InitializeFunction has not been called. - */ -using CustomLayerDeinitializeFunction = void (*)(void* context); +class CustomLayerHost { +public: + virtual ~CustomLayerHost() = default; + /** + * Initialize any GL state needed by the custom layer. This method is called once, from the + * main thread, at a point when the GL context is active but before rendering for the first + * time. + * + * Resources that are acquired in this method must be released in the `deinitialize` function. + */ + virtual void initialize() = 0; + + /** + * Render the layer. This method is called once per frame. The implementation should not make + * any assumptions about the GL state (other than that the correct context is active). It may + * make changes to the state, and is not required to reset values such as the depth mask, stencil + * mask, and corresponding test flags to their original values. + * Make sure that you are drawing your fragments with a z value of 1 to take advantage of the + * opaque fragment culling in case there are opaque layers above your custom layer. + */ + virtual void render(const CustomLayerRenderParameters&) = 0; + + /** + * Called when the system has destroyed the underlying GL context. The + * `deinitialize` function will not be called in this case, however + * `initialize` will be called instead to prepare for a new render. + * + */ + virtual void contextLost() = 0; + + /** + * Destroy any GL state needed by the custom layer, and deallocate context, if necessary. This + * method is called once, from the main thread, at a point when the GL context is active. + * + * Note that it may be called even when the `initialize` function has not been called. + */ + virtual void deinitialize() = 0; +}; class CustomLayer : public Layer { public: CustomLayer(const std::string& id, - CustomLayerInitializeFunction, - CustomLayerRenderFunction, - CustomLayerContextLostFunction, - CustomLayerDeinitializeFunction, - void* context); - - CustomLayer(const std::string& id, - CustomLayerInitializeFunction, - CustomLayerRenderFunction, - CustomLayerDeinitializeFunction, - void* context); + std::unique_ptr host); ~CustomLayer() final; diff --git a/include/mbgl/util/color.hpp b/include/mbgl/util/color.hpp index 300d7fae82..01a4c8f292 100644 --- a/include/mbgl/util/color.hpp +++ b/include/mbgl/util/color.hpp @@ -4,6 +4,7 @@ #include #include +#include namespace mbgl { @@ -37,6 +38,7 @@ public: static optional parse(const std::string&); std::string stringify() const; + std::array toArray() const; }; inline bool operator==(const Color& colorA, const Color& colorB) { diff --git a/include/mbgl/util/projection.hpp b/include/mbgl/util/projection.hpp index b4a34521a4..65a84d8dc2 100644 --- a/include/mbgl/util/projection.hpp +++ b/include/mbgl/util/projection.hpp @@ -78,8 +78,9 @@ public: return project_(latLng, worldSize(scale)); } - static Point project(const LatLng& latLng, uint8_t zoom) { - return project_(latLng, std::pow(2.0, zoom)); + //Returns point on tile + static Point project(const LatLng& latLng, int32_t zoom) { + return project_(latLng, 1 << zoom); } static LatLng unproject(const Point& p, double scale, LatLng::WrapMode wrapMode = LatLng::Unwrapped) { @@ -90,16 +91,7 @@ public: wrapMode }; } - - // Project lat, lon to point in a zoom-dependent world size - static Point project(const LatLng& point, uint8_t zoom, uint16_t tileSize) { - const double t2z = tileSize * std::pow(2, zoom); - Point pt = project_(point, t2z); - // Flip y coordinate - auto x = ::round(std::min(pt.x, t2z)); - auto y = ::round(std::min(t2z - pt.y, t2z)); - return { x, y }; - } + private: static Point project_(const LatLng& latLng, double worldSize) { return Point { diff --git a/mapbox-gl-native.pro b/mapbox-gl-native.pro index 2cede951ab..7ae7f13ecf 100644 --- a/mapbox-gl-native.pro +++ b/mapbox-gl-native.pro @@ -232,6 +232,7 @@ SOURCES += \ src/mbgl/style/expression/interpolate.cpp \ src/mbgl/style/expression/is_constant.cpp \ src/mbgl/style/expression/is_expression.cpp \ + src/mbgl/style/expression/length.cpp \ src/mbgl/style/expression/let.cpp \ src/mbgl/style/expression/literal.cpp \ src/mbgl/style/expression/match.cpp \ @@ -239,6 +240,8 @@ SOURCES += \ src/mbgl/style/expression/step.cpp \ src/mbgl/style/expression/util.cpp \ src/mbgl/style/expression/value.cpp \ + src/mbgl/style/filter.cpp \ + src/mbgl/style/filter_evaluator.cpp \ src/mbgl/style/function/categorical_stops.cpp \ src/mbgl/style/function/expression.cpp \ src/mbgl/style/function/identity_stops.cpp \ @@ -348,8 +351,8 @@ SOURCES += \ src/mbgl/util/premultiply.cpp \ src/mbgl/util/stopwatch.cpp \ src/mbgl/util/string.cpp \ - src/mbgl/util/throttler.cpp \ src/mbgl/util/tile_cover.cpp \ + src/mbgl/util/tile_cover_impl.cpp \ src/mbgl/util/tiny_sdf.cpp \ src/mbgl/util/url.cpp \ src/mbgl/util/version.cpp \ @@ -429,4 +432,4 @@ INCLUDEPATH += \ src QMAKE_CXXFLAGS += \ - -DMBGL_VERSION_REV=\\\"qt-v1.3.0\\\" + -DMBGL_VERSION_REV=\\\"qt-v1.4.0\\\" diff --git a/platform/default/mbgl/storage/offline.cpp b/platform/default/mbgl/storage/offline.cpp index 7670790be9..598a0b182b 100644 --- a/platform/default/mbgl/storage/offline.cpp +++ b/platform/default/mbgl/storage/offline.cpp @@ -43,7 +43,7 @@ uint64_t OfflineTilePyramidRegionDefinition::tileCount(style::SourceType type, u const Range clampedZoomRange = coveringZoomRange(type, tileSize, zoomRange); unsigned long result = 0;; for (uint8_t z = clampedZoomRange.min; z <= clampedZoomRange.max; z++) { - result += util::tileCount(bounds, z, tileSize); + result += util::tileCount(bounds, z); } return result; diff --git a/platform/default/mbgl/util/default_styles.hpp b/platform/default/mbgl/util/default_styles.hpp index 43dafb8083..13f08252a7 100644 --- a/platform/default/mbgl/util/default_styles.hpp +++ b/platform/default/mbgl/util/default_styles.hpp @@ -19,12 +19,9 @@ constexpr const DefaultStyle light = { "mapbox://styles/mapbox/light- constexpr const DefaultStyle dark = { "mapbox://styles/mapbox/dark-v9", "Dark", 9 }; constexpr const DefaultStyle satellite = { "mapbox://styles/mapbox/satellite-v9", "Satellite", 9 }; constexpr const DefaultStyle satelliteStreets = { "mapbox://styles/mapbox/satellite-streets-v10", "Satellite Streets", 10 }; -constexpr const DefaultStyle trafficDay = { "mapbox://styles/mapbox/traffic-day-v2", "Traffic Day", 2 }; -constexpr const DefaultStyle trafficNight = { "mapbox://styles/mapbox/traffic-night-v2", "Traffic Night", 2 }; const DefaultStyle orderedStyles[] = { streets, outdoors, light, dark, satellite, satelliteStreets, - trafficDay, trafficNight, }; const size_t numOrderedStyles = sizeof(orderedStyles) / sizeof(DefaultStyle); diff --git a/platform/default/sqlite3.cpp b/platform/default/sqlite3.cpp index 8a567d602e..8f9c34191f 100644 --- a/platform/default/sqlite3.cpp +++ b/platform/default/sqlite3.cpp @@ -291,11 +291,12 @@ template <> void Query::bind(int offset, const char *value) { } // We currently cannot use sqlite3_bind_blob64 / sqlite3_bind_text64 because they -// was introduced in SQLite 3.8.7, and we need to support earlier versions: -// iOS 8.0: 3.7.13 -// iOS 8.2: 3.8.5 -// According to http://stackoverflow.com/questions/14288128/what-version-of-sqlite-does-ios-provide, -// the first iOS version with 3.8.7+ was 9.0, with 3.8.8. +// were introduced in SQLite 3.8.7, and we need to support earlier versions: +// Android 11: 3.7 +// Android 21: 3.8 +// Android 24: 3.9 +// Per https://developer.android.com/reference/android/database/sqlite/package-summary. +// The first iOS version with 3.8.7+ was 9.0, with 3.8.8. void Query::bind(int offset, const char * value, std::size_t length, bool retain) { assert(stmt.impl); diff --git a/platform/qt/include/qmapbox.hpp b/platform/qt/include/qmapbox.hpp index 471616daee..369890343f 100644 --- a/platform/qt/include/qmapbox.hpp +++ b/platform/qt/include/qmapbox.hpp @@ -9,14 +9,10 @@ // This header follows the Qt coding style: https://wiki.qt.io/Qt_Coding_Style -#if !defined(QT_MAPBOXGL_STATIC) -# if defined(QT_BUILD_MAPBOXGL_LIB) -# define Q_MAPBOXGL_EXPORT Q_DECL_EXPORT -# else -# define Q_MAPBOXGL_EXPORT Q_DECL_IMPORT -# endif +#if defined(QT_BUILD_MAPBOXGL_LIB) + #define Q_MAPBOXGL_EXPORT Q_DECL_EXPORT #else -# define Q_MAPBOXGL_EXPORT + #define Q_MAPBOXGL_EXPORT Q_DECL_IMPORT #endif namespace QMapbox { @@ -116,12 +112,16 @@ struct Q_MAPBOXGL_EXPORT CustomLayerRenderParameters { double zoom; double bearing; double pitch; - double altitude; + double fieldOfView; }; -typedef void (*CustomLayerInitializeFunction)(void* context) ; -typedef void (*CustomLayerRenderFunction)(void* context, const CustomLayerRenderParameters&); -typedef void (*CustomLayerDeinitializeFunction)(void* context); +class Q_MAPBOXGL_EXPORT CustomLayerHostInterface { +public: + virtual ~CustomLayerHostInterface() = default; + virtual void initialize() = 0; + virtual void render(const CustomLayerRenderParameters&) = 0; + virtual void deinitialize() = 0; +}; } // namespace QMapbox diff --git a/platform/qt/include/qmapboxgl.hpp b/platform/qt/include/qmapboxgl.hpp index b5676fbedd..79eb672d1f 100644 --- a/platform/qt/include/qmapboxgl.hpp +++ b/platform/qt/include/qmapboxgl.hpp @@ -230,10 +230,7 @@ public: void removeImage(const QString &name); void addCustomLayer(const QString &id, - QMapbox::CustomLayerInitializeFunction, - QMapbox::CustomLayerRenderFunction, - QMapbox::CustomLayerDeinitializeFunction, - void* context, + QScopedPointer& host, const QString& before = QString()); void addLayer(const QVariantMap ¶ms, const QString& before = QString()); bool layerExists(const QString &id); diff --git a/platform/qt/src/qmapbox.cpp b/platform/qt/src/qmapbox.cpp index 1386a4b7aa..2180f22d07 100644 --- a/platform/qt/src/qmapbox.cpp +++ b/platform/qt/src/qmapbox.cpp @@ -158,27 +158,9 @@ namespace QMapbox { */ /*! - \typedef QMapbox::CustomLayerDeinitializeFunction + \class QMapbox::CustomLayerHostInterface - Represents a callback to be called when destroying a custom layer. - - \warning This is used for delegating the rendering of a layer to the user of - this API and is not officially supported. Use at your own risk. -*/ - -/*! - \typedef QMapbox::CustomLayerInitializeFunction - - Represents a callback to be called when initializing a custom layer. - - \warning This is used for delegating the rendering of a layer to the user of - this API and is not officially supported. Use at your own risk. -*/ - -/*! - \typedef QMapbox::CustomLayerRenderFunction - - Represents a callback to be called on each render pass for a custom layer. + Represents a host interface to be implemented for rendering custom layers. \warning This is used for delegating the rendering of a layer to the user of this API and is not officially supported. Use at your own risk. diff --git a/platform/qt/src/qmapboxgl.cpp b/platform/qt/src/qmapboxgl.cpp index 34fd5509fa..8c3355dc09 100644 --- a/platform/qt/src/qmapboxgl.cpp +++ b/platform/qt/src/qmapboxgl.cpp @@ -89,15 +89,33 @@ QThreadStorage> loop; std::shared_ptr sharedDefaultFileSource( const std::string& cachePath, const std::string& assetRoot, uint64_t maximumCacheSize) { - static std::weak_ptr weak; - auto fs = weak.lock(); + static std::mutex mutex; + static std::unordered_map> fileSources; - if (!fs) { - weak = fs = std::make_shared( - cachePath, assetRoot, maximumCacheSize); + std::lock_guard lock(mutex); + + // Purge entries no longer in use. + for (auto it = fileSources.begin(); it != fileSources.end();) { + if (!it->second.lock()) { + it = fileSources.erase(it); + } else { + ++it; + } + } + + // Return an existing FileSource if available. + auto sharedFileSource = fileSources.find(cachePath); + if (sharedFileSource != fileSources.end()) { + return sharedFileSource->second.lock(); } - return fs; + // New path, create a new FileSource. + auto newFileSource = std::make_shared( + cachePath, assetRoot, maximumCacheSize); + + fileSources[cachePath] = newFileSource; + + return newFileSource; } // Conversion helper functions. @@ -141,10 +159,9 @@ std::unique_ptr toStyleImage(const QString &id, const QImage QMapboxGLSettings is used to configure QMapboxGL at the moment of its creation. Once created, the QMapboxGLSettings of a QMapboxGL can no longer be changed. - Cache-related settings are shared between all QMapboxGL instances because different - maps will share the same cache database file. The first map to configure cache properties - such as size and path will force the configuration to all newly instantiated QMapboxGL - objects. + Cache-related settings are shared between all QMapboxGL instances using the same cache path. + The first map to configure cache properties such as size will force the configuration + to all newly instantiated QMapboxGL objects using the same cache in the same process. \since 4.7 */ @@ -1356,20 +1373,43 @@ void QMapboxGL::removeSource(const QString& id) this API and is not officially supported. Use at your own risk. */ void QMapboxGL::addCustomLayer(const QString &id, - QMapbox::CustomLayerInitializeFunction initFn, - QMapbox::CustomLayerRenderFunction renderFn, - QMapbox::CustomLayerDeinitializeFunction deinitFn, - void *context, + QScopedPointer& host, const QString& before) { + class HostWrapper : public mbgl::style::CustomLayerHost { + public: + QScopedPointer ptr; + HostWrapper(QScopedPointer& p) + : ptr(p.take()) { + } + + void initialize() { + ptr->initialize(); + } + + void render(const mbgl::style::CustomLayerRenderParameters& params) { + QMapbox::CustomLayerRenderParameters renderParams; + renderParams.width = params.width; + renderParams.height = params.height; + renderParams.latitude = params.latitude; + renderParams.longitude = params.longitude; + renderParams.zoom = params.zoom; + renderParams.bearing = params.bearing; + renderParams.pitch = params.pitch; + renderParams.fieldOfView = params.fieldOfView; + ptr->render(renderParams); + } + + void contextLost() { } + + void deinitialize() { + ptr->deinitialize(); + } + }; + d_ptr->mapObj->getStyle().addLayer(std::make_unique( id.toStdString(), - reinterpret_cast(initFn), - // This cast is safe as long as both mbgl:: and QMapbox:: - // CustomLayerRenderParameters members remains the same. - (mbgl::style::CustomLayerRenderFunction)renderFn, - reinterpret_cast(deinitFn), - context), + std::make_unique(host)), before.isEmpty() ? mbgl::optional() : mbgl::optional(before.toStdString())); } diff --git a/src/mbgl/annotation/render_annotation_source.cpp b/src/mbgl/annotation/render_annotation_source.cpp index a237100d13..7d776f21c4 100644 --- a/src/mbgl/annotation/render_annotation_source.cpp +++ b/src/mbgl/annotation/render_annotation_source.cpp @@ -65,8 +65,8 @@ RenderAnnotationSource::queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const std::vector& layers, const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const { - return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, collisionIndex); + const mat4& projMatrix) const { + return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, projMatrix); } std::vector RenderAnnotationSource::querySourceFeatures(const SourceQueryOptions&) const { diff --git a/src/mbgl/annotation/render_annotation_source.hpp b/src/mbgl/annotation/render_annotation_source.hpp index e812ec2883..da87d13814 100644 --- a/src/mbgl/annotation/render_annotation_source.hpp +++ b/src/mbgl/annotation/render_annotation_source.hpp @@ -28,7 +28,7 @@ public: const TransformState& transformState, const std::vector& layers, const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const final; + const mat4& projMatrix) const final; std::vector querySourceFeatures(const SourceQueryOptions&) const final; diff --git a/src/mbgl/geometry/feature_index.cpp b/src/mbgl/geometry/feature_index.cpp index 3b5e12b54a..fdd9558d0b 100644 --- a/src/mbgl/geometry/feature_index.cpp +++ b/src/mbgl/geometry/feature_index.cpp @@ -17,54 +17,56 @@ namespace mbgl { -FeatureIndex::FeatureIndex() - : grid(util::EXTENT, util::EXTENT, util::EXTENT / 16) { // 16x16 grid -> 32px cell +FeatureIndex::FeatureIndex(std::unique_ptr tileData_) + : grid(util::EXTENT, util::EXTENT, util::EXTENT / 16) // 16x16 grid -> 32px cell + , tileData(std::move(tileData_)) { } void FeatureIndex::insert(const GeometryCollection& geometries, std::size_t index, const std::string& sourceLayerName, - const std::string& bucketName) { + const std::string& bucketLeaderID) { for (const auto& ring : geometries) { auto envelope = mapbox::geometry::envelope(ring); - grid.insert(IndexedSubfeature(index, sourceLayerName, bucketName, sortIndex++), - {convertPoint(envelope.min), convertPoint(envelope.max)}); + if (envelope.min.x < util::EXTENT && + envelope.min.y < util::EXTENT && + envelope.max.x >= 0 && + envelope.max.y >= 0) { + grid.insert(IndexedSubfeature(index, sourceLayerName, bucketLeaderID, sortIndex++), + {convertPoint(envelope.min), convertPoint(envelope.max)}); + } } } -static bool topDown(const IndexedSubfeature& a, const IndexedSubfeature& b) { - return a.sortIndex > b.sortIndex; -} - -static bool topDownSymbols(const IndexedSubfeature& a, const IndexedSubfeature& b) { - return a.sortIndex < b.sortIndex; -} - void FeatureIndex::query( std::unordered_map>& result, const GeometryCoordinates& queryGeometry, - const float bearing, + const TransformState& transformState, + const mat4& posMatrix, const double tileSize, const double scale, const RenderedQueryOptions& queryOptions, - const GeometryTileData& geometryTileData, const UnwrappedTileID& tileID, - const std::string& sourceID, const std::vector& layers, - const CollisionIndex& collisionIndex, - const float additionalQueryRadius) const { + const float additionalQueryPadding) const { + + if (!tileData) { + return; + } // Determine query radius const float pixelsToTileUnits = util::EXTENT / tileSize / scale; - const int16_t additionalRadius = std::min(util::EXTENT, additionalQueryRadius * pixelsToTileUnits); + const int16_t additionalPadding = std::min(util::EXTENT, additionalQueryPadding * pixelsToTileUnits); // Query the grid index mapbox::geometry::box box = mapbox::geometry::envelope(queryGeometry); - std::vector features = grid.query({ convertPoint(box.min - additionalRadius), - convertPoint(box.max + additionalRadius) }); + std::vector features = grid.query({ convertPoint(box.min - additionalPadding), + convertPoint(box.max + additionalPadding) }); - std::sort(features.begin(), features.end(), topDown); + std::sort(features.begin(), features.end(), [](const IndexedSubfeature& a, const IndexedSubfeature& b) { + return a.sortIndex > b.sortIndex; + }); size_t previousSortIndex = std::numeric_limits::max(); for (const auto& indexedFeature : features) { @@ -72,26 +74,59 @@ void FeatureIndex::query( if (indexedFeature.sortIndex == previousSortIndex) continue; previousSortIndex = indexedFeature.sortIndex; - addFeature(result, indexedFeature, queryGeometry, queryOptions, geometryTileData, tileID.canonical, layers, bearing, pixelsToTileUnits); + addFeature(result, indexedFeature, queryOptions, tileID.canonical, layers, queryGeometry, transformState, pixelsToTileUnits, posMatrix); + } +} + +std::unordered_map> FeatureIndex::lookupSymbolFeatures(const std::vector& symbolFeatures, + const RenderedQueryOptions& queryOptions, + const std::vector& layers, + const OverscaledTileID& tileID, + const std::shared_ptr>& featureSortOrder) const { + std::unordered_map> result; + if (!tileData) { + return result; } + std::vector sortedFeatures(symbolFeatures.begin(), symbolFeatures.end()); + + std::sort(sortedFeatures.begin(), sortedFeatures.end(), [featureSortOrder](const IndexedSubfeature& a, const IndexedSubfeature& b) { + // Same idea as the non-symbol sort order, but symbol features may have changed their sort order + // since their corresponding IndexedSubfeature was added to the CollisionIndex + // The 'featureSortOrder' is relatively inefficient for querying but cheap to build on every bucket sort + if (featureSortOrder) { + // queryRenderedSymbols documentation says we'll return features in + // "top-to-bottom" rendering order (aka last-to-first). + // Actually there can be multiple symbol instances per feature, so + // we sort each feature based on the first matching symbol instance. + auto sortedA = std::find(featureSortOrder->begin(), featureSortOrder->end(), a.index); + auto sortedB = std::find(featureSortOrder->begin(), featureSortOrder->end(), b.index); + assert(sortedA != featureSortOrder->end()); + assert(sortedB != featureSortOrder->end()); + return sortedA > sortedB; + } else { + // Bucket hasn't been re-sorted based on angle, so use same "reverse of appearance in source data" + // logic as non-symboles + return a.sortIndex > b.sortIndex; + } + }); - std::vector symbolFeatures = collisionIndex.queryRenderedSymbols(queryGeometry, tileID, sourceID); - std::sort(symbolFeatures.begin(), symbolFeatures.end(), topDownSymbols); - for (const auto& symbolFeature : symbolFeatures) { - addFeature(result, symbolFeature, queryGeometry, queryOptions, geometryTileData, tileID.canonical, layers, bearing, pixelsToTileUnits); + for (const auto& symbolFeature : sortedFeatures) { + mat4 unusedMatrix; + addFeature(result, symbolFeature, queryOptions, tileID.canonical, layers, GeometryCoordinates(), {}, 0, unusedMatrix); } + return result; } void FeatureIndex::addFeature( std::unordered_map>& result, const IndexedSubfeature& indexedFeature, - const GeometryCoordinates& queryGeometry, const RenderedQueryOptions& options, - const GeometryTileData& geometryTileData, const CanonicalTileID& tileID, const std::vector& layers, - const float bearing, - const float pixelsToTileUnits) const { + const GeometryCoordinates& queryGeometry, + const TransformState& transformState, + const float pixelsToTileUnits, + const mat4& posMatrix) const { auto getRenderLayer = [&] (const std::string& layerID) -> const RenderLayer* { for (const auto& layer : layers) { @@ -106,14 +141,14 @@ void FeatureIndex::addFeature( std::unique_ptr sourceLayer; std::unique_ptr geometryTileFeature; - for (const std::string& layerID : bucketLayerIDs.at(indexedFeature.bucketName)) { + for (const std::string& layerID : bucketLayerIDs.at(indexedFeature.bucketLeaderID)) { const RenderLayer* renderLayer = getRenderLayer(layerID); if (!renderLayer) { continue; } if (!geometryTileFeature) { - sourceLayer = geometryTileData.getLayer(indexedFeature.sourceLayerName); + sourceLayer = tileData->getLayer(indexedFeature.sourceLayerName); assert(sourceLayer); geometryTileFeature = sourceLayer->getFeature(indexedFeature.index); @@ -121,11 +156,11 @@ void FeatureIndex::addFeature( } if (!renderLayer->is() && - !renderLayer->queryIntersectsFeature(queryGeometry, *geometryTileFeature, tileID.z, bearing, pixelsToTileUnits)) { + !renderLayer->queryIntersectsFeature(queryGeometry, *geometryTileFeature, tileID.z, transformState, pixelsToTileUnits, posMatrix)) { continue; } - if (options.filter && !(*options.filter)(*geometryTileFeature)) { + if (options.filter && !(*options.filter)(style::expression::EvaluationContext { static_cast(tileID.z), geometryTileFeature.get() })) { continue; } @@ -155,8 +190,8 @@ optional FeatureIndex::translateQueryGeometry( return translated; } -void FeatureIndex::setBucketLayerIDs(const std::string& bucketName, const std::vector& layerIDs) { - bucketLayerIDs[bucketName] = layerIDs; +void FeatureIndex::setBucketLayerIDs(const std::string& bucketLeaderID, const std::vector& layerIDs) { + bucketLayerIDs[bucketLeaderID] = layerIDs; } } // namespace mbgl diff --git a/src/mbgl/geometry/feature_index.hpp b/src/mbgl/geometry/feature_index.hpp index e95bb94da6..739c1f282f 100644 --- a/src/mbgl/geometry/feature_index.hpp +++ b/src/mbgl/geometry/feature_index.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -14,6 +15,7 @@ namespace mbgl { class RenderedQueryOptions; class RenderLayer; +class TransformState; class CollisionIndex; @@ -23,50 +25,47 @@ public: IndexedSubfeature(std::size_t index_, std::string sourceLayerName_, std::string bucketName_, size_t sortIndex_) : index(index_) , sourceLayerName(std::move(sourceLayerName_)) - , bucketName(std::move(bucketName_)) + , bucketLeaderID(std::move(bucketName_)) , sortIndex(sortIndex_) - , tileID(0, 0, 0) + , bucketInstanceId(0) {} - IndexedSubfeature(std::size_t index_, std::string sourceLayerName_, std::string bucketName_, size_t sortIndex_, - std::string sourceID_, CanonicalTileID tileID_) - : index(index_) - , sourceLayerName(std::move(sourceLayerName_)) - , bucketName(std::move(bucketName_)) - , sortIndex(std::move(sortIndex_)) - , sourceID(std::move(sourceID_)) - , tileID(std::move(tileID_)) - {} + IndexedSubfeature(const IndexedSubfeature& other, uint32_t bucketInstanceId_) + : index(other.index) + , sourceLayerName(other.sourceLayerName) + , bucketLeaderID(other.bucketLeaderID) + , sortIndex(other.sortIndex) + , bucketInstanceId(bucketInstanceId_) + {} size_t index; std::string sourceLayerName; - std::string bucketName; + std::string bucketLeaderID; size_t sortIndex; // Only used for symbol features - std::string sourceID; - CanonicalTileID tileID; + uint32_t bucketInstanceId; }; class FeatureIndex { public: - FeatureIndex(); + FeatureIndex(std::unique_ptr tileData_); - void insert(const GeometryCollection&, std::size_t index, const std::string& sourceLayerName, const std::string& bucketName); + const GeometryTileData* getData() { return tileData.get(); } + + void insert(const GeometryCollection&, std::size_t index, const std::string& sourceLayerName, const std::string& bucketLeaderID); void query( std::unordered_map>& result, const GeometryCoordinates& queryGeometry, - const float bearing, + const TransformState&, + const mat4& posMatrix, const double tileSize, const double scale, const RenderedQueryOptions& options, - const GeometryTileData&, const UnwrappedTileID&, - const std::string&, const std::vector&, - const CollisionIndex&, - const float additionalQueryRadius) const; + const float additionalQueryPadding) const; static optional translateQueryGeometry( const GeometryCoordinates& queryGeometry, @@ -75,23 +74,31 @@ public: const float bearing, const float pixelsToTileUnits); - void setBucketLayerIDs(const std::string& bucketName, const std::vector& layerIDs); + void setBucketLayerIDs(const std::string& bucketLeaderID, const std::vector& layerIDs); + + std::unordered_map> lookupSymbolFeatures( + const std::vector& symbolFeatures, + const RenderedQueryOptions& options, + const std::vector& layers, + const OverscaledTileID& tileID, + const std::shared_ptr>& featureSortOrder) const; private: void addFeature( std::unordered_map>& result, const IndexedSubfeature&, - const GeometryCoordinates& queryGeometry, const RenderedQueryOptions& options, - const GeometryTileData&, const CanonicalTileID&, const std::vector&, - const float bearing, - const float pixelsToTileUnits) const; + const GeometryCoordinates& queryGeometry, + const TransformState& transformState, + const float pixelsToTileUnits, + const mat4& posMatrix) const; GridIndex grid; unsigned int sortIndex = 0; std::unordered_map> bucketLayerIDs; + std::unique_ptr tileData; }; } // namespace mbgl diff --git a/src/mbgl/gl/program.hpp b/src/mbgl/gl/program.hpp index 3b54ec194a..af02ad3d54 100644 --- a/src/mbgl/gl/program.hpp +++ b/src/mbgl/gl/program.hpp @@ -36,8 +36,13 @@ public: context.createShader(ShaderType::Fragment, fragmentSource))), uniformsState((context.linkProgram(program), Uniforms::bindLocations(program))), attributeLocations(Attributes::bindLocations(program)) { + // Re-link program after manually binding only active attributes in Attributes::bindLocations context.linkProgram(program); + + // We have to re-initialize the uniforms state from the bindings as the uniform locations + // get shifted on some implementations + uniformsState = Uniforms::bindLocations(program); } template diff --git a/src/mbgl/layout/symbol_instance.cpp b/src/mbgl/layout/symbol_instance.cpp index 6e152349ca..7dfa8edf43 100644 --- a/src/mbgl/layout/symbol_instance.cpp +++ b/src/mbgl/layout/symbol_instance.cpp @@ -27,7 +27,7 @@ SymbolInstance::SymbolInstance(Anchor& anchor_, anchor(anchor_), line(line_), index(index_), - hasText(shapedTextOrientations.first || shapedTextOrientations.second), + hasText(false), hasIcon(shapedIcon), // Create the collision features that will be used to check whether this symbol instance can be placed @@ -48,6 +48,8 @@ SymbolInstance::SymbolInstance(Anchor& anchor_, if (shapedTextOrientations.second) { verticalGlyphQuads = getGlyphQuads(shapedTextOrientations.second, layout, textPlacement, positions); } + // 'hasText' depends on finding at least one glyph in the shaping that's also in the GlyphPositionMap + hasText = horizontalGlyphQuads.size() > 0 || verticalGlyphQuads.size() > 0; if (shapedTextOrientations.first && shapedTextOrientations.second) { writingModes = WritingModeType::Horizontal | WritingModeType::Vertical; diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index a41a98fcaf..b2f6fd450f 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -42,7 +42,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, std::unique_ptr sourceLayer_, ImageDependencies& imageDependencies, GlyphDependencies& glyphDependencies) - : bucketName(layers.at(0)->getID()), + : bucketLeaderID(layers.at(0)->getID()), sourceLayer(std::move(sourceLayer_)), overscaling(parameters.tileID.overscaleFactor()), zoom(parameters.tileID.overscaledZ), @@ -100,7 +100,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, const size_t featureCount = sourceLayer->featureCount(); for (size_t i = 0; i < featureCount; ++i) { auto feature = sourceLayer->getFeature(i); - if (!leader.filter(feature->getType(), feature->getID(), [&] (const auto& key) { return feature->getValue(key); })) + if (!leader.filter(expression::EvaluationContext { this->zoom, feature.get() })) continue; SymbolFeature ft(std::move(feature)); @@ -126,7 +126,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, if (hasText) { std::string u8string = layout.evaluate(zoom, ft); - if (layout.get().isConstant()) { + if (layout.get().isConstant() && !leader.layout.get().isExpression()) { u8string = util::replaceTokens(u8string, getValue); } @@ -159,7 +159,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, if (hasIcon) { std::string icon = layout.evaluate(zoom, ft); - if (layout.get().isConstant()) { + if (layout.get().isConstant() && !leader.layout.get().isExpression()) { icon = util::replaceTokens(icon, getValue); } ft.icon = icon; @@ -181,8 +181,7 @@ bool SymbolLayout::hasSymbolInstances() const { } void SymbolLayout::prepare(const GlyphMap& glyphMap, const GlyphPositions& glyphPositions, - const ImageMap& imageMap, const ImagePositions& imagePositions, - const OverscaledTileID& tileID, const std::string& sourceID) { + const ImageMap& imageMap, const ImagePositions& imagePositions) { const bool textAlongLine = layout.get() == AlignmentType::Map && layout.get() == SymbolPlacementType::Line; @@ -253,7 +252,7 @@ void SymbolLayout::prepare(const GlyphMap& glyphMap, const GlyphPositions& glyph // if either shapedText or icon position is present, add the feature if (shapedTextOrientations.first || shapedIcon) { - addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, shapedIcon, glyphPositionMap, tileID, sourceID); + addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, shapedIcon, glyphPositionMap); } feature.geometry.clear(); @@ -266,9 +265,7 @@ void SymbolLayout::addFeature(const std::size_t index, const SymbolFeature& feature, const std::pair& shapedTextOrientations, optional shapedIcon, - const GlyphPositionMap& glyphPositionMap, - const OverscaledTileID& tileID, - const std::string& sourceID) { + const GlyphPositionMap& glyphPositionMap) { const float minScale = 0.5f; const float glyphSize = 24.0f; @@ -297,8 +294,7 @@ void SymbolLayout::addFeature(const std::size_t index, : layout.get(); const float textRepeatDistance = symbolSpacing / 2; - IndexedSubfeature indexedFeature(feature.index, sourceLayer->getName(), bucketName, symbolInstances.size(), - sourceID, tileID.canonical); + IndexedSubfeature indexedFeature(feature.index, sourceLayer->getName(), bucketLeaderID, symbolInstances.size()); auto addSymbolInstance = [&] (const GeometryCoordinates& line, Anchor& anchor) { // https://github.com/mapbox/vector-tile-spec/tree/master/2.1#41-layers @@ -424,7 +420,7 @@ std::unique_ptr SymbolLayout::place(const bool showCollisionBoxes) const bool mayOverlap = layout.get() || layout.get() || layout.get() || layout.get(); - auto bucket = std::make_unique(layout, layerPaintProperties, textSize, iconSize, zoom, sdfIcons, iconsNeedLinear, mayOverlap, std::move(symbolInstances)); + auto bucket = std::make_unique(layout, layerPaintProperties, textSize, iconSize, zoom, sdfIcons, iconsNeedLinear, mayOverlap, bucketLeaderID, std::move(symbolInstances)); for (SymbolInstance &symbolInstance : bucket->symbolInstances) { diff --git a/src/mbgl/layout/symbol_layout.hpp b/src/mbgl/layout/symbol_layout.hpp index 6951c29ada..43b577859f 100644 --- a/src/mbgl/layout/symbol_layout.hpp +++ b/src/mbgl/layout/symbol_layout.hpp @@ -34,8 +34,7 @@ public: GlyphDependencies&); void prepare(const GlyphMap&, const GlyphPositions&, - const ImageMap&, const ImagePositions&, - const OverscaledTileID&, const std::string&); + const ImageMap&, const ImagePositions&); std::unique_ptr place(const bool showCollisionBoxes); @@ -44,7 +43,7 @@ public: std::map> layerPaintProperties; - const std::string bucketName; + const std::string bucketLeaderID; std::vector symbolInstances; private: @@ -52,9 +51,7 @@ private: const SymbolFeature&, const std::pair& shapedTextOrientations, optional shapedIcon, - const GlyphPositionMap&, - const OverscaledTileID&, - const std::string&); + const GlyphPositionMap&); bool anchorIsTooClose(const std::u16string& text, const float repeatDistance, const Anchor&); std::map> compareText; diff --git a/src/mbgl/layout/symbol_projection.cpp b/src/mbgl/layout/symbol_projection.cpp index 9e077e2532..ef669c6e19 100644 --- a/src/mbgl/layout/symbol_projection.cpp +++ b/src/mbgl/layout/symbol_projection.cpp @@ -240,7 +240,11 @@ namespace mbgl { const PlacedSymbol& symbol, const mat4& labelPlaneMatrix, const bool returnTileDistance) { - + if (symbol.glyphOffsets.empty()) { + assert(false); + return optional>(); + } + const float firstGlyphOffset = symbol.glyphOffsets.front(); const float lastGlyphOffset = symbol.glyphOffsets.back();; diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index 947973415a..d81544eed5 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -446,7 +446,6 @@ CameraOptions Map::cameraForGeometry(const Geometry& geometry, const Edg latLngs.push_back({ pt.y, pt.x }); }); return cameraForLatLngs(latLngs, padding, bearing); - } LatLngBounds Map::latLngBoundsForCamera(const CameraOptions& camera) const { @@ -748,6 +747,11 @@ void Map::Impl::onInvalidate() { } void Map::Impl::onUpdate() { + // Don't load/render anything in still mode until explicitly requested. + if (mode != MapMode::Continuous && !stillImageRequest) { + return; + } + TimePoint timePoint = mode == MapMode::Continuous ? Clock::now() : Clock::time_point::max(); transform.updateTransitions(timePoint); diff --git a/src/mbgl/map/transform_state.cpp b/src/mbgl/map/transform_state.cpp index 18d2c24aee..a85b251fb4 100644 --- a/src/mbgl/map/transform_state.cpp +++ b/src/mbgl/map/transform_state.cpp @@ -421,4 +421,17 @@ float TransformState::getCameraToTileDistance(const UnwrappedTileID& tileID) con return projectedCenter[3]; } +float TransformState::maxPitchScaleFactor() const { + if (size.isEmpty()) { + return {}; + } + auto latLng = screenCoordinateToLatLng({ 0, static_cast(getSize().height) }); + mat4 mat = coordinatePointMatrix(getZoom()); + Point pt = Projection::project(latLng, scale) / double(util::tileSize); + vec4 p = {{ pt.x, pt.y, 0, 1 }}; + vec4 topPoint; + matrix::transformMat4(topPoint, p, mat); + return topPoint[3] / getCameraToCenterDistance(); +} + } // namespace mbgl diff --git a/src/mbgl/map/transform_state.hpp b/src/mbgl/map/transform_state.hpp index 451802034d..b6f8ae4424 100644 --- a/src/mbgl/map/transform_state.hpp +++ b/src/mbgl/map/transform_state.hpp @@ -87,6 +87,7 @@ public: } float getCameraToTileDistance(const UnwrappedTileID&) const; + float maxPitchScaleFactor() const; private: bool rotatedNorth() const; diff --git a/src/mbgl/programs/symbol_program.hpp b/src/mbgl/programs/symbol_program.hpp index a14afac702..9b5037ed9f 100644 --- a/src/mbgl/programs/symbol_program.hpp +++ b/src/mbgl/programs/symbol_program.hpp @@ -61,8 +61,8 @@ struct SymbolLayoutAttributes : gl::Attributes< {{ static_cast(labelAnchor.x), static_cast(labelAnchor.y), - static_cast(::round(o.x * 64)), // use 1/64 pixels for placement - static_cast(::round((o.y + glyphOffsetY) * 64)) + static_cast(::round(o.x * 32)), // use 1/32 pixels for placement + static_cast(::round((o.y + glyphOffsetY) * 32)) }}, {{ tx, diff --git a/src/mbgl/renderer/buckets/symbol_bucket.cpp b/src/mbgl/renderer/buckets/symbol_bucket.cpp index 60e8a0b504..4fe03eb453 100644 --- a/src/mbgl/renderer/buckets/symbol_bucket.cpp +++ b/src/mbgl/renderer/buckets/symbol_bucket.cpp @@ -18,11 +18,13 @@ SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated layo bool sdfIcons_, bool iconsNeedLinear_, bool sortFeaturesByY_, + const std::string bucketName_, const std::vector&& symbolInstances_) : layout(std::move(layout_)), sdfIcons(sdfIcons_), iconsNeedLinear(iconsNeedLinear_ || iconSize.isDataDriven() || !iconSize.isZoomConstant()), sortFeaturesByY(sortFeaturesByY_), + bucketLeaderID(std::move(bucketName_)), symbolInstances(std::move(symbolInstances_)), textSizeBinder(SymbolSizeBinder::create(zoom, textSize, TextSize::defaultValue())), iconSizeBinder(SymbolSizeBinder::create(zoom, iconSize, IconSize::defaultValue())) { @@ -200,8 +202,12 @@ void SymbolBucket::sortFeatures(const float angle) { text.triangles.clear(); icon.triangles.clear(); + featureSortOrder = std::make_unique>(); + featureSortOrder->reserve(symbolInstanceIndexes.size()); + for (auto i : symbolInstanceIndexes) { const SymbolInstance& symbolInstance = symbolInstances[i]; + featureSortOrder->push_back(symbolInstance.featureIndex); if (symbolInstance.placedTextIndex) { addPlacedSymbol(text.triangles, text.placedSymbols[*symbolInstance.placedTextIndex]); diff --git a/src/mbgl/renderer/buckets/symbol_bucket.hpp b/src/mbgl/renderer/buckets/symbol_bucket.hpp index ed8afb052c..e4aaf5ba30 100644 --- a/src/mbgl/renderer/buckets/symbol_bucket.hpp +++ b/src/mbgl/renderer/buckets/symbol_bucket.hpp @@ -47,6 +47,7 @@ public: bool sdfIcons, bool iconsNeedLinear, bool sortFeaturesByY, + const std::string bucketLeaderID, const std::vector&&); void upload(gl::Context&) override; @@ -64,6 +65,8 @@ public: const bool iconsNeedLinear; const bool sortFeaturesByY; + const std::string bucketLeaderID; + optional sortedAngle; bool staticUploaded = false; @@ -131,6 +134,8 @@ public: uint32_t bucketInstanceId = 0; bool justReloaded = false; + + std::shared_ptr> featureSortOrder; }; } // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_background_layer.cpp b/src/mbgl/renderer/layers/render_background_layer.cpp index aebc4cc9aa..44c3fffb6c 100644 --- a/src/mbgl/renderer/layers/render_background_layer.cpp +++ b/src/mbgl/renderer/layers/render_background_layer.cpp @@ -7,6 +7,7 @@ #include #include #include +#include namespace mbgl { diff --git a/src/mbgl/renderer/layers/render_circle_layer.cpp b/src/mbgl/renderer/layers/render_circle_layer.cpp index 6092ff5452..56fccfe071 100644 --- a/src/mbgl/renderer/layers/render_circle_layer.cpp +++ b/src/mbgl/renderer/layers/render_circle_layer.cpp @@ -93,27 +93,75 @@ void RenderCircleLayer::render(PaintParameters& parameters, RenderSource*) { } } +GeometryCoordinate projectPoint(const GeometryCoordinate& p, const mat4& posMatrix, const Size& size) { + vec4 pos = {{ static_cast(p.x), static_cast(p.y), 0, 1 }}; + matrix::transformMat4(pos, pos, posMatrix); + return { + static_cast((static_cast(pos[0] / pos[3]) + 1) * size.width * 0.5), + static_cast((static_cast(pos[1] / pos[3]) + 1) * size.height * 0.5) + }; +} + +GeometryCoordinates projectQueryGeometry(const GeometryCoordinates& queryGeometry, const mat4& posMatrix, const Size& size) { + GeometryCoordinates projectedGeometry; + for (auto& p : queryGeometry) { + projectedGeometry.push_back(projectPoint(p, posMatrix, size)); + } + return projectedGeometry; +} + bool RenderCircleLayer::queryIntersectsFeature( const GeometryCoordinates& queryGeometry, const GeometryTileFeature& feature, const float zoom, - const float bearing, - const float pixelsToTileUnits) const { + const TransformState& transformState, + const float pixelsToTileUnits, + const mat4& posMatrix) const { // Translate query geometry - auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( + const GeometryCoordinates& translatedQueryGeometry = FeatureIndex::translateQueryGeometry( queryGeometry, evaluated.get(), evaluated.get(), - bearing, - pixelsToTileUnits); + transformState.getAngle(), + pixelsToTileUnits).value_or(queryGeometry); // Evaluate functions - auto radius = evaluated.evaluate(zoom, feature) * pixelsToTileUnits; - auto stroke = evaluated.evaluate(zoom, feature) * pixelsToTileUnits; + auto radius = evaluated.evaluate(zoom, feature); + auto stroke = evaluated.evaluate(zoom, feature); + auto size = radius + stroke; + + // For pitch-alignment: map, compare feature geometry to query geometry in the plane of the tile + // Otherwise, compare geometry in the plane of the viewport + // A circle with fixed scaling relative to the viewport gets larger in tile space as it moves into the distance + // A circle with fixed scaling relative to the map gets smaller in viewport space as it moves into the distance + bool alignWithMap = evaluated.evaluate(zoom, feature) == AlignmentType::Map; + const GeometryCoordinates& transformedQueryGeometry = alignWithMap ? + translatedQueryGeometry : + projectQueryGeometry(translatedQueryGeometry, posMatrix, transformState.getSize()); + auto transformedSize = alignWithMap ? size * pixelsToTileUnits : size; + + auto geometry = feature.getGeometries(); + for (auto& ring : geometry) { + for (auto& point : ring) { + const GeometryCoordinate& transformedPoint = alignWithMap ? point : projectPoint(point, posMatrix, transformState.getSize()); + + float adjustedSize = transformedSize; + vec4 center = {{ static_cast(point.x), static_cast(point.y), 0, 1 }}; + matrix::transformMat4(center, center, posMatrix); + auto pitchScale = evaluated.evaluate(zoom, feature); + auto pitchAlignment = evaluated.evaluate(zoom, feature); + if (pitchScale == CirclePitchScaleType::Viewport && pitchAlignment == AlignmentType::Map) { + adjustedSize *= center[3] / transformState.getCameraToCenterDistance(); + } else if (pitchScale == CirclePitchScaleType::Map && pitchAlignment == AlignmentType::Viewport) { + adjustedSize *= transformState.getCameraToCenterDistance() / center[3]; + } + + if (util::polygonIntersectsBufferedPoint(transformedQueryGeometry, transformedPoint, adjustedSize)) return true; + } + } - // Test intersection - return util::polygonIntersectsBufferedMultiPoint(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries(), radius + stroke); + return false; } } // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_circle_layer.hpp b/src/mbgl/renderer/layers/render_circle_layer.hpp index f31715f98f..c9eeae4652 100644 --- a/src/mbgl/renderer/layers/render_circle_layer.hpp +++ b/src/mbgl/renderer/layers/render_circle_layer.hpp @@ -20,8 +20,9 @@ public: const GeometryCoordinates&, const GeometryTileFeature&, const float, + const TransformState&, const float, - const float) const override; + const mat4&) const override; std::unique_ptr createBucket(const BucketParameters&, const std::vector&) const override; diff --git a/src/mbgl/renderer/layers/render_custom_layer.cpp b/src/mbgl/renderer/layers/render_custom_layer.cpp index a429b8d82e..be9f64d9eb 100644 --- a/src/mbgl/renderer/layers/render_custom_layer.cpp +++ b/src/mbgl/renderer/layers/render_custom_layer.cpp @@ -6,23 +6,24 @@ #include #include #include +#include namespace mbgl { using namespace style; RenderCustomLayer::RenderCustomLayer(Immutable _impl) - : RenderLayer(LayerType::Custom, _impl) { + : RenderLayer(LayerType::Custom, _impl), host(_impl->host) { + assert(BackendScope::exists()); + host->initialize(); } RenderCustomLayer::~RenderCustomLayer() { assert(BackendScope::exists()); - if (initialized) { - if (contextDestroyed && impl().contextLostFn ) { - impl().contextLostFn(impl().context); - } else if (!contextDestroyed && impl().deinitializeFn) { - impl().deinitializeFn(impl().context); - } + if (contextDestroyed) { + host->contextLost(); + } else { + host->deinitialize(); } } @@ -44,15 +45,13 @@ std::unique_ptr RenderCustomLayer::createBucket(const BucketParameters&, } void RenderCustomLayer::render(PaintParameters& paintParameters, RenderSource*) { - if (context != impl().context || !initialized) { + if (host != impl().host) { //If the context changed, deinitialize the previous one before initializing the new one. - if (context && !contextDestroyed && impl().deinitializeFn) { - MBGL_CHECK_ERROR(impl().deinitializeFn(context)); + if (host && !contextDestroyed) { + MBGL_CHECK_ERROR(host->deinitialize()); } - context = impl().context; - assert(impl().initializeFn); - MBGL_CHECK_ERROR(impl().initializeFn(impl().context)); - initialized = true; + host = impl().host; + MBGL_CHECK_ERROR(host->initialize()); } gl::Context& glContext = paintParameters.context; @@ -74,9 +73,11 @@ void RenderCustomLayer::render(PaintParameters& paintParameters, RenderSource*) parameters.bearing = -state.getAngle() * util::RAD2DEG; parameters.pitch = state.getPitch(); parameters.fieldOfView = state.getFieldOfView(); + mat4 projMatrix; + state.getProjMatrix(projMatrix); + parameters.projectionMatrix = projMatrix; - assert(impl().renderFn); - MBGL_CHECK_ERROR(impl().renderFn(context, parameters)); + MBGL_CHECK_ERROR(host->render(parameters)); // Reset the view back to our original one, just in case the CustomLayer changed // the viewport or Framebuffer. diff --git a/src/mbgl/renderer/layers/render_custom_layer.hpp b/src/mbgl/renderer/layers/render_custom_layer.hpp index 6d1fea99d3..971d8d8f42 100644 --- a/src/mbgl/renderer/layers/render_custom_layer.hpp +++ b/src/mbgl/renderer/layers/render_custom_layer.hpp @@ -24,9 +24,8 @@ public: }; private: - bool initialized = false; bool contextDestroyed = false; - void * context = nullptr; + std::shared_ptr host; }; template <> diff --git a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp index fbd6160e8a..871464223c 100644 --- a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp +++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp @@ -151,14 +151,15 @@ bool RenderFillExtrusionLayer::queryIntersectsFeature( const GeometryCoordinates& queryGeometry, const GeometryTileFeature& feature, const float, - const float bearing, - const float pixelsToTileUnits) const { + const TransformState& transformState, + const float pixelsToTileUnits, + const mat4&) const { auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( queryGeometry, evaluated.get(), evaluated.get(), - bearing, + transformState.getAngle(), pixelsToTileUnits); return util::polygonIntersectsMultiPolygon(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries()); diff --git a/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp index 838494cf91..f7ba13c267 100644 --- a/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp +++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp @@ -22,8 +22,9 @@ public: const GeometryCoordinates&, const GeometryTileFeature&, const float, + const TransformState&, const float, - const float) const override; + const mat4&) const override; std::unique_ptr createBucket(const BucketParameters&, const std::vector&) const override; diff --git a/src/mbgl/renderer/layers/render_fill_layer.cpp b/src/mbgl/renderer/layers/render_fill_layer.cpp index 22cb9563c1..efd3f4215c 100644 --- a/src/mbgl/renderer/layers/render_fill_layer.cpp +++ b/src/mbgl/renderer/layers/render_fill_layer.cpp @@ -188,14 +188,15 @@ bool RenderFillLayer::queryIntersectsFeature( const GeometryCoordinates& queryGeometry, const GeometryTileFeature& feature, const float, - const float bearing, - const float pixelsToTileUnits) const { + const TransformState& transformState, + const float pixelsToTileUnits, + const mat4&) const { auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( queryGeometry, evaluated.get(), evaluated.get(), - bearing, + transformState.getAngle(), pixelsToTileUnits); return util::polygonIntersectsMultiPolygon(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries()); diff --git a/src/mbgl/renderer/layers/render_fill_layer.hpp b/src/mbgl/renderer/layers/render_fill_layer.hpp index a51865698f..bd195fb828 100644 --- a/src/mbgl/renderer/layers/render_fill_layer.hpp +++ b/src/mbgl/renderer/layers/render_fill_layer.hpp @@ -20,8 +20,9 @@ public: const GeometryCoordinates&, const GeometryTileFeature&, const float, + const TransformState&, const float, - const float) const override; + const mat4&) const override; std::unique_ptr createBucket(const BucketParameters&, const std::vector&) const override; diff --git a/src/mbgl/renderer/layers/render_heatmap_layer.cpp b/src/mbgl/renderer/layers/render_heatmap_layer.cpp index 4f2e899220..72c60446aa 100644 --- a/src/mbgl/renderer/layers/render_heatmap_layer.cpp +++ b/src/mbgl/renderer/layers/render_heatmap_layer.cpp @@ -165,12 +165,12 @@ bool RenderHeatmapLayer::queryIntersectsFeature( const GeometryCoordinates& queryGeometry, const GeometryTileFeature& feature, const float zoom, - const float bearing, - const float pixelsToTileUnits) const { + const TransformState&, + const float pixelsToTileUnits, + const mat4&) const { (void) queryGeometry; (void) feature; (void) zoom; - (void) bearing; (void) pixelsToTileUnits; return false; } diff --git a/src/mbgl/renderer/layers/render_heatmap_layer.hpp b/src/mbgl/renderer/layers/render_heatmap_layer.hpp index 3f0b1f91b4..29fad7d8b8 100644 --- a/src/mbgl/renderer/layers/render_heatmap_layer.hpp +++ b/src/mbgl/renderer/layers/render_heatmap_layer.hpp @@ -22,8 +22,9 @@ public: const GeometryCoordinates&, const GeometryTileFeature&, const float, + const TransformState&, const float, - const float) const override; + const mat4&) const override; void updateColorRamp(); diff --git a/src/mbgl/renderer/layers/render_line_layer.cpp b/src/mbgl/renderer/layers/render_line_layer.cpp index 1b4a1c0ff7..02f61af0fa 100644 --- a/src/mbgl/renderer/layers/render_line_layer.cpp +++ b/src/mbgl/renderer/layers/render_line_layer.cpp @@ -162,15 +162,16 @@ bool RenderLineLayer::queryIntersectsFeature( const GeometryCoordinates& queryGeometry, const GeometryTileFeature& feature, const float zoom, - const float bearing, - const float pixelsToTileUnits) const { + const TransformState& transformState, + const float pixelsToTileUnits, + const mat4&) const { // Translate query geometry auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( queryGeometry, evaluated.get(), evaluated.get(), - bearing, + transformState.getAngle(), pixelsToTileUnits); // Evaluate function diff --git a/src/mbgl/renderer/layers/render_line_layer.hpp b/src/mbgl/renderer/layers/render_line_layer.hpp index 8bf7e2329d..5d5d79c044 100644 --- a/src/mbgl/renderer/layers/render_line_layer.hpp +++ b/src/mbgl/renderer/layers/render_line_layer.hpp @@ -29,8 +29,9 @@ public: const GeometryCoordinates&, const GeometryTileFeature&, const float, + const TransformState&, const float, - const float) const override; + const mat4&) const override; std::unique_ptr createBucket(const BucketParameters&, const std::vector&) const override; diff --git a/src/mbgl/renderer/render_layer.hpp b/src/mbgl/renderer/render_layer.hpp index 55831cb72c..04a1608564 100644 --- a/src/mbgl/renderer/render_layer.hpp +++ b/src/mbgl/renderer/render_layer.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -17,6 +18,7 @@ class PropertyEvaluationParameters; class PaintParameters; class RenderSource; class RenderTile; +class TransformState; class RenderLayer { protected: @@ -69,8 +71,9 @@ public: const GeometryCoordinates&, const GeometryTileFeature&, const float, + const TransformState&, const float, - const float) const { return false; }; + const mat4&) const { return false; }; virtual std::unique_ptr createBucket(const BucketParameters&, const std::vector&) const = 0; diff --git a/src/mbgl/renderer/render_source.hpp b/src/mbgl/renderer/render_source.hpp index 53519c763e..dc80cb1dc6 100644 --- a/src/mbgl/renderer/render_source.hpp +++ b/src/mbgl/renderer/render_source.hpp @@ -65,7 +65,7 @@ public: const TransformState& transformState, const std::vector& layers, const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const = 0; + const mat4& projMatrix) const = 0; virtual std::vector querySourceFeatures(const SourceQueryOptions&) const = 0; diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp index 2ac714e122..ded07a0909 100644 --- a/src/mbgl/renderer/renderer_impl.cpp +++ b/src/mbgl/renderer/renderer_impl.cpp @@ -85,11 +85,6 @@ void Renderer::Impl::setObserver(RendererObserver* observer_) { void Renderer::Impl::render(const UpdateParameters& updateParameters) { if (updateParameters.mode != MapMode::Continuous) { - // Don't load/render anyting in still mode until explicitly requested. - if (!updateParameters.stillImageRequest) { - return; - } - // Reset zoom history state. zoomHistory.first = true; } @@ -401,10 +396,6 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) { } placementChanged = newPlacement->commit(*placement, parameters.timePoint); - // commitFeatureIndexes depends on the assumption that no new FeatureIndex has been loaded since placement - // started. If we violate this assumption, then we need to either make CollisionIndex completely independendent of - // FeatureIndex, or find a way for its entries to point to multiple FeatureIndexes. - commitFeatureIndexes(); crossTileSymbolIndex.pruneUnusedLayers(usedSymbolLayers); if (placementChanged || symbolBucketsChanged) { placement = std::move(newPlacement); @@ -673,6 +664,39 @@ std::vector Renderer::Impl::queryRenderedFeatures(const ScreenLineStrin return queryRenderedFeatures(geometry, options, layers); } + +void Renderer::Impl::queryRenderedSymbols(std::unordered_map>& resultsByLayer, + const ScreenLineString& geometry, + const std::vector& layers, + const RenderedQueryOptions& options) const { + + auto renderedSymbols = placement->getCollisionIndex().queryRenderedSymbols(geometry); + std::vector> bucketQueryData; + for (auto entry : renderedSymbols) { + bucketQueryData.push_back(placement->getQueryData(entry.first)); + } + // Although symbol query is global, symbol results are only sortable within a bucket + // For a predictable global sort order, we sort the buckets based on their corresponding tile position + std::sort(bucketQueryData.begin(), bucketQueryData.end(), [](const RetainedQueryData& a, const RetainedQueryData& b) { + return + std::tie(a.tileID.canonical.z, a.tileID.canonical.y, a.tileID.wrap, a.tileID.canonical.x) < + std::tie(b.tileID.canonical.z, b.tileID.canonical.y, b.tileID.wrap, b.tileID.canonical.x); + }); + + for (auto wrappedQueryData : bucketQueryData) { + auto& queryData = wrappedQueryData.get(); + auto bucketSymbols = queryData.featureIndex->lookupSymbolFeatures(renderedSymbols[queryData.bucketInstanceId], + options, + layers, + queryData.tileID, + queryData.featureSortOrder); + + for (auto layer : bucketSymbols) { + auto& resultFeatures = resultsByLayer[layer.first]; + std::move(layer.second.begin(), layer.second.end(), std::inserter(resultFeatures, resultFeatures.end())); + } + } +} std::vector Renderer::Impl::queryRenderedFeatures(const ScreenLineString& geometry, const RenderedQueryOptions& options, const std::vector& layers) const { std::unordered_set sourceIDs; @@ -680,13 +704,18 @@ std::vector Renderer::Impl::queryRenderedFeatures(const ScreenLineStrin sourceIDs.emplace(layer->baseImpl->source); } + mat4 projMatrix; + transformState.getProjMatrix(projMatrix); + std::unordered_map> resultsByLayer; for (const auto& sourceID : sourceIDs) { if (RenderSource* renderSource = getRenderSource(sourceID)) { - auto sourceResults = renderSource->queryRenderedFeatures(geometry, transformState, layers, options, placement->getCollisionIndex()); + auto sourceResults = renderSource->queryRenderedFeatures(geometry, transformState, layers, options, projMatrix); std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin())); } } + + queryRenderedSymbols(resultsByLayer, geometry, layers, options); std::vector result; @@ -785,15 +814,6 @@ bool Renderer::Impl::hasTransitions(TimePoint timePoint) const { return false; } -void Renderer::Impl::commitFeatureIndexes() { - for (auto& source : renderSources) { - for (auto& renderTile : source.second->getRenderTiles()) { - Tile& tile = renderTile.get().tile; - tile.commitFeatureIndex(); - } - } -} - void Renderer::Impl::updateFadingTiles() { fadingTiles = false; for (auto& source : renderSources) { diff --git a/src/mbgl/renderer/renderer_impl.hpp b/src/mbgl/renderer/renderer_impl.hpp index 4675ac79ea..4124f6f416 100644 --- a/src/mbgl/renderer/renderer_impl.hpp +++ b/src/mbgl/renderer/renderer_impl.hpp @@ -64,7 +64,12 @@ private: RenderLayer* getRenderLayer(const std::string& id); const RenderLayer* getRenderLayer(const std::string& id) const; - + + void queryRenderedSymbols(std::unordered_map>& resultsByLayer, + const ScreenLineString& geometry, + const std::vector& layers, + const RenderedQueryOptions& options) const; + std::vector queryRenderedFeatures(const ScreenLineString&, const RenderedQueryOptions&, const std::vector&) const; // GlyphManagerObserver implementation. @@ -74,7 +79,6 @@ private: void onTileChanged(RenderSource&, const OverscaledTileID&) override; void onTileError(RenderSource&, const OverscaledTileID&, std::exception_ptr) override; - void commitFeatureIndexes(); void updateFadingTiles(); friend class Renderer; diff --git a/src/mbgl/renderer/sources/render_custom_geometry_source.cpp b/src/mbgl/renderer/sources/render_custom_geometry_source.cpp index 057ad5a4a7..2d28b8dd84 100644 --- a/src/mbgl/renderer/sources/render_custom_geometry_source.cpp +++ b/src/mbgl/renderer/sources/render_custom_geometry_source.cpp @@ -68,8 +68,8 @@ RenderCustomGeometrySource::queryRenderedFeatures(const ScreenLineString& geomet const TransformState& transformState, const std::vector& layers, const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const { - return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, collisionIndex); + const mat4& projMatrix) const { + return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, projMatrix); } std::vector RenderCustomGeometrySource::querySourceFeatures(const SourceQueryOptions& options) const { diff --git a/src/mbgl/renderer/sources/render_custom_geometry_source.hpp b/src/mbgl/renderer/sources/render_custom_geometry_source.hpp index 033d731029..5533fe2b83 100644 --- a/src/mbgl/renderer/sources/render_custom_geometry_source.hpp +++ b/src/mbgl/renderer/sources/render_custom_geometry_source.hpp @@ -28,7 +28,7 @@ public: const TransformState& transformState, const std::vector& layers, const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const final; + const mat4& projMatrix) const final; std::vector querySourceFeatures(const SourceQueryOptions&) const final; diff --git a/src/mbgl/renderer/sources/render_geojson_source.cpp b/src/mbgl/renderer/sources/render_geojson_source.cpp index cbf4db70b5..0e265efff4 100644 --- a/src/mbgl/renderer/sources/render_geojson_source.cpp +++ b/src/mbgl/renderer/sources/render_geojson_source.cpp @@ -86,8 +86,8 @@ RenderGeoJSONSource::queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const std::vector& layers, const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const { - return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, collisionIndex); + const mat4& projMatrix) const { + return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, projMatrix); } std::vector RenderGeoJSONSource::querySourceFeatures(const SourceQueryOptions& options) const { diff --git a/src/mbgl/renderer/sources/render_geojson_source.hpp b/src/mbgl/renderer/sources/render_geojson_source.hpp index 72fccbd043..297fa09a29 100644 --- a/src/mbgl/renderer/sources/render_geojson_source.hpp +++ b/src/mbgl/renderer/sources/render_geojson_source.hpp @@ -32,7 +32,7 @@ public: const TransformState& transformState, const std::vector& layers, const RenderedQueryOptions& options, - const CollisionIndex&) const final; + const mat4& projMatrix) const final; std::vector querySourceFeatures(const SourceQueryOptions&) const final; diff --git a/src/mbgl/renderer/sources/render_image_source.cpp b/src/mbgl/renderer/sources/render_image_source.cpp index 31a5916a34..5c497e8144 100644 --- a/src/mbgl/renderer/sources/render_image_source.cpp +++ b/src/mbgl/renderer/sources/render_image_source.cpp @@ -85,7 +85,7 @@ RenderImageSource::queryRenderedFeatures(const ScreenLineString&, const TransformState&, const std::vector&, const RenderedQueryOptions&, - const CollisionIndex&) const { + const mat4&) const { return std::unordered_map> {}; } diff --git a/src/mbgl/renderer/sources/render_image_source.hpp b/src/mbgl/renderer/sources/render_image_source.hpp index 85ee0ace11..cf14e180fd 100644 --- a/src/mbgl/renderer/sources/render_image_source.hpp +++ b/src/mbgl/renderer/sources/render_image_source.hpp @@ -33,7 +33,7 @@ public: const TransformState& transformState, const std::vector& layers, const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const final; + const mat4& projMatrix) const final; std::vector querySourceFeatures(const SourceQueryOptions&) const final; diff --git a/src/mbgl/renderer/sources/render_raster_dem_source.cpp b/src/mbgl/renderer/sources/render_raster_dem_source.cpp index b3153622c3..fbf2c09d19 100644 --- a/src/mbgl/renderer/sources/render_raster_dem_source.cpp +++ b/src/mbgl/renderer/sources/render_raster_dem_source.cpp @@ -147,7 +147,7 @@ RenderRasterDEMSource::queryRenderedFeatures(const ScreenLineString&, const TransformState&, const std::vector&, const RenderedQueryOptions&, - const CollisionIndex& ) const { + const mat4&) const { return std::unordered_map> {}; } diff --git a/src/mbgl/renderer/sources/render_raster_dem_source.hpp b/src/mbgl/renderer/sources/render_raster_dem_source.hpp index 741214a14d..48c7803e92 100644 --- a/src/mbgl/renderer/sources/render_raster_dem_source.hpp +++ b/src/mbgl/renderer/sources/render_raster_dem_source.hpp @@ -28,7 +28,7 @@ public: const TransformState& transformState, const std::vector& layers, const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const final; + const mat4& projMatrix) const final; std::vector querySourceFeatures(const SourceQueryOptions&) const final; diff --git a/src/mbgl/renderer/sources/render_raster_source.cpp b/src/mbgl/renderer/sources/render_raster_source.cpp index 60b3fa9a3b..f97ce4e65b 100644 --- a/src/mbgl/renderer/sources/render_raster_source.cpp +++ b/src/mbgl/renderer/sources/render_raster_source.cpp @@ -77,7 +77,7 @@ RenderRasterSource::queryRenderedFeatures(const ScreenLineString&, const TransformState&, const std::vector&, const RenderedQueryOptions&, - const CollisionIndex& ) const { + const mat4&) const { return std::unordered_map> {}; } diff --git a/src/mbgl/renderer/sources/render_raster_source.hpp b/src/mbgl/renderer/sources/render_raster_source.hpp index 78eda199ac..32539a046d 100644 --- a/src/mbgl/renderer/sources/render_raster_source.hpp +++ b/src/mbgl/renderer/sources/render_raster_source.hpp @@ -28,7 +28,7 @@ public: const TransformState& transformState, const std::vector& layers, const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const final; + const mat4& projMatrix) const final; std::vector querySourceFeatures(const SourceQueryOptions&) const final; diff --git a/src/mbgl/renderer/sources/render_vector_source.cpp b/src/mbgl/renderer/sources/render_vector_source.cpp index e87bea5dcd..4de4f01e3f 100644 --- a/src/mbgl/renderer/sources/render_vector_source.cpp +++ b/src/mbgl/renderer/sources/render_vector_source.cpp @@ -80,8 +80,8 @@ RenderVectorSource::queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const std::vector& layers, const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const { - return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, collisionIndex); + const mat4& projMatrix) const { + return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, projMatrix); } std::vector RenderVectorSource::querySourceFeatures(const SourceQueryOptions& options) const { diff --git a/src/mbgl/renderer/sources/render_vector_source.hpp b/src/mbgl/renderer/sources/render_vector_source.hpp index 592160dc16..6fd2425aa3 100644 --- a/src/mbgl/renderer/sources/render_vector_source.hpp +++ b/src/mbgl/renderer/sources/render_vector_source.hpp @@ -28,7 +28,7 @@ public: const TransformState& transformState, const std::vector& layers, const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const final; + const mat4& projMatrix) const final; std::vector querySourceFeatures(const SourceQueryOptions&) const final; diff --git a/src/mbgl/renderer/tile_pyramid.cpp b/src/mbgl/renderer/tile_pyramid.cpp index 8f83a0f982..d28e95181b 100644 --- a/src/mbgl/renderer/tile_pyramid.cpp +++ b/src/mbgl/renderer/tile_pyramid.cpp @@ -242,7 +242,7 @@ std::unordered_map> TilePyramid::queryRendered const TransformState& transformState, const std::vector& layers, const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const { + const mat4& projMatrix) const { std::unordered_map> result; if (renderTiles.empty() || geometry.empty()) { return result; @@ -264,14 +264,19 @@ std::unordered_map> TilePyramid::queryRendered std::tie(b.id.canonical.z, b.id.canonical.y, b.id.wrap, b.id.canonical.x); }); + auto maxPitchScaleFactor = transformState.maxPitchScaleFactor(); + for (const RenderTile& renderTile : sortedTiles) { + const float scale = std::pow(2, transformState.getZoom() - renderTile.id.canonical.z); + auto queryPadding = maxPitchScaleFactor * renderTile.tile.getQueryPadding(layers) * util::EXTENT / util::tileSize / scale; + GeometryCoordinate tileSpaceBoundsMin = TileCoordinate::toGeometryCoordinate(renderTile.id, box.min); - if (tileSpaceBoundsMin.x >= util::EXTENT || tileSpaceBoundsMin.y >= util::EXTENT) { + if (tileSpaceBoundsMin.x - queryPadding >= util::EXTENT || tileSpaceBoundsMin.y - queryPadding >= util::EXTENT) { continue; } GeometryCoordinate tileSpaceBoundsMax = TileCoordinate::toGeometryCoordinate(renderTile.id, box.max); - if (tileSpaceBoundsMax.x < 0 || tileSpaceBoundsMax.y < 0) { + if (tileSpaceBoundsMax.x + queryPadding < 0 || tileSpaceBoundsMax.y + queryPadding < 0) { continue; } @@ -286,7 +291,7 @@ std::unordered_map> TilePyramid::queryRendered transformState, layers, options, - collisionIndex); + projMatrix); } return result; diff --git a/src/mbgl/renderer/tile_pyramid.hpp b/src/mbgl/renderer/tile_pyramid.hpp index 2638599c38..0cef9e2c40 100644 --- a/src/mbgl/renderer/tile_pyramid.hpp +++ b/src/mbgl/renderer/tile_pyramid.hpp @@ -54,7 +54,7 @@ public: const TransformState& transformState, const std::vector&, const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const; + const mat4& projMatrix) const; std::vector querySourceFeatures(const SourceQueryOptions&) const; diff --git a/src/mbgl/shaders/collision_box.cpp b/src/mbgl/shaders/collision_box.cpp index 9d11640bf4..bc5d9bc6f9 100644 --- a/src/mbgl/shaders/collision_box.cpp +++ b/src/mbgl/shaders/collision_box.cpp @@ -22,7 +22,10 @@ varying float v_notUsed; void main() { vec4 projectedPoint = u_matrix * vec4(a_anchor_pos, 0, 1); highp float camera_to_anchor_distance = projectedPoint.w; - highp float collision_perspective_ratio = 0.5 + 0.5 * (u_camera_to_center_distance / camera_to_anchor_distance); + highp float collision_perspective_ratio = clamp( + 0.5 + 0.5 * (u_camera_to_center_distance / camera_to_anchor_distance), + 0.0, // Prevents oversized near-field boxes in pitched/overzoomed tiles + 4.0); gl_Position = u_matrix * vec4(a_pos, 0.0, 1.0); gl_Position.xy += a_extrude * u_extrude_scale * gl_Position.w * collision_perspective_ratio; diff --git a/src/mbgl/shaders/line.cpp b/src/mbgl/shaders/line.cpp index c700295a15..68d2dcc468 100644 --- a/src/mbgl/shaders/line.cpp +++ b/src/mbgl/shaders/line.cpp @@ -31,6 +31,7 @@ uniform vec2 u_gl_units_to_pixels; varying vec2 v_normal; varying vec2 v_width2; varying float v_gamma_scale; +varying highp float v_linesofar; #ifndef HAS_UNIFORM_u_color @@ -131,6 +132,8 @@ void main() { vec2 a_extrude = a_data.xy - 128.0; float a_direction = mod(a_data.z, 4.0) - 1.0; + v_linesofar = (floor(a_data.z / 4.0) + a_data.w * 64.0) * 2.0; + vec2 pos = a_pos_normal.xy; // x is 1 if it's a round cap, 0 otherwise diff --git a/src/mbgl/shaders/line_pattern.cpp b/src/mbgl/shaders/line_pattern.cpp index f8d785ade9..be88255e3c 100644 --- a/src/mbgl/shaders/line_pattern.cpp +++ b/src/mbgl/shaders/line_pattern.cpp @@ -215,8 +215,14 @@ void main() { float x_a = mod(v_linesofar / u_pattern_size_a.x, 1.0); float x_b = mod(v_linesofar / u_pattern_size_b.x, 1.0); - float y_a = 0.5 + (v_normal.y * v_width2.s / u_pattern_size_a.y); - float y_b = 0.5 + (v_normal.y * v_width2.s / u_pattern_size_b.y); + + // v_normal.y is 0 at the midpoint of the line, -1 at the lower edge, 1 at the upper edge + // we clamp the line width outset to be between 0 and half the pattern height plus padding (2.0) + // to ensure we don't sample outside the designated symbol on the sprite sheet. + // 0.5 is added to shift the component to be bounded between 0 and 1 for interpolation of + // the texture coordinate + float y_a = 0.5 + (v_normal.y * clamp(v_width2.s, 0.0, (u_pattern_size_a.y + 2.0) / 2.0) / u_pattern_size_a.y); + float y_b = 0.5 + (v_normal.y * clamp(v_width2.s, 0.0, (u_pattern_size_b.y + 2.0) / 2.0) / u_pattern_size_b.y); vec2 pos_a = mix(u_pattern_tl_a / u_texsize, u_pattern_br_a / u_texsize, vec2(x_a, y_a)); vec2 pos_b = mix(u_pattern_tl_b / u_texsize, u_pattern_br_b / u_texsize, vec2(x_b, y_b)); diff --git a/src/mbgl/shaders/symbol_icon.cpp b/src/mbgl/shaders/symbol_icon.cpp index f5c2bbe22d..c037c81005 100644 --- a/src/mbgl/shaders/symbol_icon.cpp +++ b/src/mbgl/shaders/symbol_icon.cpp @@ -80,7 +80,10 @@ void main() { highp float distance_ratio = u_pitch_with_map ? camera_to_anchor_distance / u_camera_to_center_distance : u_camera_to_center_distance / camera_to_anchor_distance; - highp float perspective_ratio = 0.5 + 0.5 * distance_ratio; + highp float perspective_ratio = clamp( + 0.5 + 0.5 * distance_ratio, + 0.0, // Prevents oversized near-field symbols in pitched/overzoomed tiles + 4.0); size *= perspective_ratio; @@ -102,7 +105,7 @@ void main() { mat2 rotation_matrix = mat2(angle_cos, -1.0 * angle_sin, angle_sin, angle_cos); vec4 projected_pos = u_label_plane_matrix * vec4(a_projected_pos.xy, 0.0, 1.0); - gl_Position = u_gl_coord_matrix * vec4(projected_pos.xy / projected_pos.w + rotation_matrix * (a_offset / 64.0 * fontScale), 0.0, 1.0); + gl_Position = u_gl_coord_matrix * vec4(projected_pos.xy / projected_pos.w + rotation_matrix * (a_offset / 32.0 * fontScale), 0.0, 1.0); v_tex = a_tex / u_texsize; vec2 fade_opacity = unpack_opacity(a_fade_opacity); diff --git a/src/mbgl/shaders/symbol_sdf.cpp b/src/mbgl/shaders/symbol_sdf.cpp index 441eaf7aac..b584c00315 100644 --- a/src/mbgl/shaders/symbol_sdf.cpp +++ b/src/mbgl/shaders/symbol_sdf.cpp @@ -156,7 +156,10 @@ void main() { highp float distance_ratio = u_pitch_with_map ? camera_to_anchor_distance / u_camera_to_center_distance : u_camera_to_center_distance / camera_to_anchor_distance; - highp float perspective_ratio = 0.5 + 0.5 * distance_ratio; + highp float perspective_ratio = clamp( + 0.5 + 0.5 * distance_ratio, + 0.0, // Prevents oversized near-field symbols in pitched/overzoomed tiles + 4.0); size *= perspective_ratio; @@ -180,7 +183,7 @@ void main() { mat2 rotation_matrix = mat2(angle_cos, -1.0 * angle_sin, angle_sin, angle_cos); vec4 projected_pos = u_label_plane_matrix * vec4(a_projected_pos.xy, 0.0, 1.0); - gl_Position = u_gl_coord_matrix * vec4(projected_pos.xy / projected_pos.w + rotation_matrix * (a_offset / 64.0 * fontScale), 0.0, 1.0); + gl_Position = u_gl_coord_matrix * vec4(projected_pos.xy / projected_pos.w + rotation_matrix * (a_offset / 32.0 * fontScale), 0.0, 1.0); float gamma_scale = gl_Position.w; vec2 tex = a_tex / u_texsize; diff --git a/src/mbgl/style/conversion/filter.cpp b/src/mbgl/style/conversion/filter.cpp index bb7bb6ea98..3c941945fd 100644 --- a/src/mbgl/style/conversion/filter.cpp +++ b/src/mbgl/style/conversion/filter.cpp @@ -1,11 +1,52 @@ #include #include +#include +#include +#include namespace mbgl { namespace style { namespace conversion { + +using GeometryValue = mapbox::geometry::value; -static optional normalizeValue(const optional& value, Error& error) { +// This is a port from https://github.com/mapbox/mapbox-gl-js/blob/master/src/style-spec/feature_filter/index.js +static bool isExpressionFilter(const Convertible& filter) { + if (!isArray(filter) || arrayLength(filter) == 0) { + return false; + } + + optional op = toString(arrayMember(filter, 0)); + + if (!op) { + return false; + + } else if (*op == "has") { + if (arrayLength(filter) < 2) return false; + optional operand = toString(arrayMember(filter, 1)); + return operand && *operand != "$id" && *operand != "$type"; + + } else if (*op == "in" || *op == "!in" || *op == "!has" || *op == "none") { + return false; + + } else if (*op == "==" || *op == "!=" || *op == ">" || *op == ">=" || *op == "<" || *op == "<=") { + return arrayLength(filter) == 3 && (isArray(arrayMember(filter, 1)) || isArray(arrayMember(filter, 2))); + + } else if (*op == "any" || *op == "all") { + for (std::size_t i = 1; i < arrayLength(filter); i++) { + Convertible f = arrayMember(filter, i); + if (!isExpressionFilter(f) && !toBool(f)) { + return false; + } + } + return true; + + } else { + return true; + } +} + +static optional normalizeValue(const optional& value, Error& error) { if (!value) { error = { "filter expression value must be a boolean, number, or string" }; return {}; @@ -32,7 +73,7 @@ static optional toFeatureType(const Convertible& value, Error& erro } static optional toFeatureIdentifier(const Convertible& value, Error& error) { - optional identifier = toValue(value); + optional identifier = toValue(value); if (!identifier) { error = { "filter expression value must be a boolean, number, or string" }; return {}; @@ -99,7 +140,7 @@ optional convertEqualityFilter(const Convertible& value, Error& error) { return { IdentifierFilterType { *filterValue } }; } else { - optional filterValue = normalizeValue(toValue(arrayMember(value, 2)), error); + optional filterValue = normalizeValue(toValue(arrayMember(value, 2)), error); if (!filterValue) { return {}; } @@ -121,7 +162,7 @@ optional convertBinaryFilter(const Convertible& value, Error& error) { return {}; } - optional filterValue = normalizeValue(toValue(arrayMember(value, 2)), error); + optional filterValue = normalizeValue(toValue(arrayMember(value, 2)), error); if (!filterValue) { return {}; } @@ -167,9 +208,9 @@ optional convertSetFilter(const Convertible& value, Error& error) { return { IdentifierFilterType { std::move(values) } }; } else { - std::vector values; + std::vector values; for (std::size_t i = 2; i < arrayLength(value); ++i) { - optional filterValue = normalizeValue(toValue(arrayMember(value, i)), error); + optional filterValue = normalizeValue(toValue(arrayMember(value, i)), error); if (!filterValue) { return {}; } @@ -193,8 +234,23 @@ optional convertCompoundFilter(const Convertible& value, Error& error) { return { FilterType { std::move(filters) } }; } + +optional convertExpressionFilter(const Convertible& value, Error& error) { + expression::ParsingContext ctx(expression::type::Boolean); + expression::ParseResult expression = ctx.parseExpression(value); + if (!expression) { + error = { ctx.getCombinedErrors() }; + return {}; + } + + return { ExpressionFilter { std::move(*expression) } }; +} optional Converter::operator()(const Convertible& value, Error& error) const { + if (isExpressionFilter(value)) { + return convertExpressionFilter(value, error); + } + if (!isArray(value)) { error = { "filter expression must be an array" }; return {}; diff --git a/src/mbgl/style/conversion/stringify.hpp b/src/mbgl/style/conversion/stringify.hpp index 6ae6fede42..7b7727d7c4 100644 --- a/src/mbgl/style/conversion/stringify.hpp +++ b/src/mbgl/style/conversion/stringify.hpp @@ -225,6 +225,10 @@ public: void operator()(const NotHasIdentifierFilter&) { stringifyUnaryFilter("!has", "$id"); } + + void operator()(const ExpressionFilter& filter) { + stringify(writer, filter.expression->serialize()); + } private: template @@ -286,138 +290,19 @@ void stringify(Writer& writer, const Undefined&) { writer.Null(); } -template -void stringify(Writer& writer, const CategoricalValue& v) { - CategoricalValue::visit(v, [&] (const auto& v_) { stringify(writer, v_); }); -} - -template -class StringifyStops { -public: - Writer& writer; - - template - void operator()(const ExponentialStops& f) { - writer.Key("type"); - writer.String("exponential"); - writer.Key("base"); - writer.Double(f.base); - writer.Key("stops"); - stringifyStops(f.stops); - } - - template - void operator()(const IntervalStops& f) { - writer.Key("type"); - writer.String("interval"); - writer.Key("stops"); - stringifyStops(f.stops); - } - - template - void operator()(const CategoricalStops& f) { - writer.Key("type"); - writer.String("categorical"); - writer.Key("stops"); - stringifyStops(f.stops); - } - - template - void operator()(const IdentityStops&) { - writer.Key("type"); - writer.String("identity"); - } - - template - void operator()(const CompositeExponentialStops& f) { - writer.Key("type"); - writer.String("exponential"); - writer.Key("base"); - writer.Double(f.base); - writer.Key("stops"); - stringifyCompositeStops(f.stops); - } - - template - void operator()(const CompositeIntervalStops& f) { - writer.Key("type"); - writer.String("interval"); - writer.Key("stops"); - stringifyCompositeStops(f.stops); - } - - template - void operator()(const CompositeCategoricalStops& f) { - writer.Key("type"); - writer.String("categorical"); - writer.Key("stops"); - stringifyCompositeStops(f.stops); - } - -private: - template - void stringifyStops(const std::map& stops) { - writer.StartArray(); - for (const auto& stop : stops) { - writer.StartArray(); - stringify(writer, stop.first); - stringify(writer, stop.second); - writer.EndArray(); - } - writer.EndArray(); - } - - template - void stringifyCompositeStops(const std::map& stops) { - writer.StartArray(); - for (const auto& outer : stops) { - for (const auto& inner : outer.second) { - writer.StartArray(); - writer.StartObject(); - writer.Key("zoom"); - writer.Double(outer.first); - writer.Key("value"); - stringify(writer, inner.first); - writer.EndObject(); - stringify(writer, inner.second); - writer.EndArray(); - } - } - writer.EndArray(); - } -}; - template -void stringify(Writer& writer, const CameraFunction& f) { - writer.StartObject(); - CameraFunction::Stops::visit(f.stops, StringifyStops { writer }); - writer.EndObject(); +void stringify(Writer& writer, const CameraFunction& fn) { + stringify(writer, fn.getExpression().serialize()); } template -void stringify(Writer& writer, const SourceFunction& f) { - writer.StartObject(); - writer.Key("property"); - writer.String(f.property); - SourceFunction::Stops::visit(f.stops, StringifyStops { writer }); - if (f.defaultValue) { - writer.Key("default"); - stringify(writer, *f.defaultValue); - } - writer.EndObject(); +void stringify(Writer& writer, const SourceFunction& fn) { + stringify(writer, fn.getExpression().serialize()); } template -void stringify(Writer& writer, const CompositeFunction& f) { - writer.StartObject(); - writer.Key("property"); - writer.String(f.property); - CompositeFunction::Stops::visit(f.stops, StringifyStops { writer }); - if (f.defaultValue) { - writer.Key("default"); - stringify(writer, *f.defaultValue); - } - writer.EndObject(); +void stringify(Writer& writer, const CompositeFunction& fn) { + stringify(writer, fn.getExpression().serialize()); } template diff --git a/src/mbgl/style/conversion/tileset.cpp b/src/mbgl/style/conversion/tileset.cpp index 6d89cef944..a2c4aa80b3 100644 --- a/src/mbgl/style/conversion/tileset.cpp +++ b/src/mbgl/style/conversion/tileset.cpp @@ -103,6 +103,8 @@ optional Converter::operator()(const Convertible& value, Error error = { "bounds left longitude should be less than right longitude" }; return {}; } + *left = util::max(-180.0, *left); + *right = util::min(180.0, *right); result.bounds = LatLngBounds::hull({ *bottom, *left }, { *top, *right }); } diff --git a/src/mbgl/style/expression/at.cpp b/src/mbgl/style/expression/at.cpp index e447d33bc7..725e5ddb51 100644 --- a/src/mbgl/style/expression/at.cpp +++ b/src/mbgl/style/expression/at.cpp @@ -19,15 +19,21 @@ EvaluationResult At::evaluate(const EvaluationContext& params) const { const auto i = evaluatedIndex->get(); const auto inputArray = evaluatedInput->get>(); - if (i < 0 || i >= inputArray.size()) { + if (i < 0) { return EvaluationError { - "Array index out of bounds: " + stringify(i) + - " > " + util::toString(inputArray.size()) + "." + "Array index out of bounds: " + util::toString(i) + " < 0." + }; + } + + if (i >= inputArray.size()) { + return EvaluationError { + "Array index out of bounds: " + util::toString(i) + + " > " + util::toString(inputArray.size() - 1) + "." }; } if (i != std::floor(i)) { return EvaluationError { - "Array index must be an integer, but found " + stringify(i) + " instead." + "Array index must be an integer, but found " + util::toString(i) + " instead." }; } return inputArray[static_cast(i)]; diff --git a/src/mbgl/style/expression/compound_expression.cpp b/src/mbgl/style/expression/compound_expression.cpp index 86d968c521..c257dbf2bb 100644 --- a/src/mbgl/style/expression/compound_expression.cpp +++ b/src/mbgl/style/expression/compound_expression.cpp @@ -3,9 +3,11 @@ #include #include #include +#include #include #include #include +#include namespace mbgl { namespace style { @@ -209,12 +211,7 @@ std::unordered_map initiali ); }); define("to-rgba", [](const Color& color) -> Result> { - return std::array {{ - 255 * color.r / color.a, - 255 * color.g / color.a, - 255 * color.b / color.a, - color.a - }}; + return color.toArray(); }); define("rgba", rgba); @@ -271,13 +268,6 @@ std::unordered_map initiali return object.at(key); }); - define("length", [](const std::vector& arr) -> Result { - return arr.size(); - }); - define("length", [] (const std::string s) -> Result { - return s.size(); - }); - define("properties", [](const EvaluationContext& params) -> Result> { if (!params.feature) { return EvaluationError { @@ -374,6 +364,11 @@ std::unordered_map initiali return result; }); + define("round", [](double x) -> Result { return std::round(x); }); + define("floor", [](double x) -> Result { return std::floor(x); }); + define("ceil", [](double x) -> Result { return std::ceil(x); }); + define("abs", [](double x) -> Result { return std::abs(x); }); + define(">", [](double lhs, double rhs) -> Result { return lhs > rhs; }); define(">", [](const std::string& lhs, const std::string& rhs) -> Result { return lhs > rhs; }); define(">=", [](double lhs, double rhs) -> Result { return lhs >= rhs; }); @@ -385,6 +380,10 @@ std::unordered_map initiali define("!", [](bool e) -> Result { return !e; }); + define("is-supported-script", [](const std::string& x) -> Result { + return util::i18n::isStringInSupportedScript(x); + }); + define("upcase", [](const std::string& input) -> Result { return platform::uppercase(input); }); diff --git a/src/mbgl/style/expression/interpolate.cpp b/src/mbgl/style/expression/interpolate.cpp index 30b2cba81b..daad8523f2 100644 --- a/src/mbgl/style/expression/interpolate.cpp +++ b/src/mbgl/style/expression/interpolate.cpp @@ -223,7 +223,11 @@ mbgl::Value Interpolate::serialize() const { interpolator.match( [&](const ExponentialInterpolator& exponential) { - serialized.emplace_back(std::vector{{ std::string("exponential"), exponential.base }}); + if (exponential.base == 1) { + serialized.emplace_back(std::vector{{ std::string("linear") }}); + } else { + serialized.emplace_back(std::vector{{ std::string("exponential"), exponential.base }}); + } }, [&](const CubicBezierInterpolator& cubicBezier) { static const std::string cubicBezierTag("cubic-bezier"); diff --git a/src/mbgl/style/expression/length.cpp b/src/mbgl/style/expression/length.cpp new file mode 100644 index 0000000000..258353ae4e --- /dev/null +++ b/src/mbgl/style/expression/length.cpp @@ -0,0 +1,66 @@ +#include +#include + +namespace mbgl { +namespace style { +namespace expression { + +Length::Length(std::unique_ptr input_) + : Expression(type::Number), + input(std::move(input_)) { +} + +EvaluationResult Length::evaluate(const EvaluationContext& params) const { + const EvaluationResult value = input->evaluate(params); + if (!value) return value; + return value->match( + [] (const std::string& s) { + return EvaluationResult { double(s.size()) }; + }, + [] (const std::vector& v) { + return EvaluationResult { double(v.size()) }; + }, + [&] (const auto&) -> EvaluationResult { + return EvaluationError { "Expected value to be of type string or array, but found " + toString(typeOf(*value)) + " instead." }; + }); +} + +void Length::eachChild(const std::function& visit) const { + visit(*input); +} + +bool Length::operator==(const Expression& e) const { + if (auto eq = dynamic_cast(&e)) { + return *eq->input == *input; + } + return false; +} + +std::vector> Length::possibleOutputs() const { + return { nullopt }; +} + +using namespace mbgl::style::conversion; +ParseResult Length::parse(const Convertible& value, ParsingContext& ctx) { + std::size_t length = arrayLength(value); + + if (length != 2) { + ctx.error("Expected one argument, but found " + util::toString(length) + " instead."); + return ParseResult(); + } + + ParseResult input = ctx.parse(arrayMember(value, 1), 1); + if (!input) return ParseResult(); + + type::Type type = (*input)->getType(); + if (!type.is() && !type.is() && !type.is()) { + ctx.error("Expected argument of type string or array, but found " + toString(type) + " instead."); + return ParseResult(); + } + + return ParseResult(std::make_unique(std::move(*input))); +} + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/expression/parsing_context.cpp b/src/mbgl/style/expression/parsing_context.cpp index 0215982209..b522aeff9a 100644 --- a/src/mbgl/style/expression/parsing_context.cpp +++ b/src/mbgl/style/expression/parsing_context.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -31,23 +32,36 @@ namespace style { namespace expression { bool isConstant(const Expression& expression) { - if (dynamic_cast(&expression)) { - return false; + if (auto varExpression = dynamic_cast(&expression)) { + return isConstant(*varExpression->getBoundExpression()); } - + if (auto compound = dynamic_cast(&expression)) { if (compound->getName() == "error") { return false; } } + + bool isTypeAnnotation = dynamic_cast(&expression) || + dynamic_cast(&expression) || + dynamic_cast(&expression); - bool literalArgs = true; + bool childrenConstant = true; expression.eachChild([&](const Expression& child) { - if (!dynamic_cast(&child)) { - literalArgs = false; + // We can _almost_ assume that if `expressions` children are constant, + // they would already have been evaluated to Literal values when they + // were parsed. Type annotations are the exception, because they might + // have been inferred and added after a child was parsed. + + // So we recurse into isConstant() for the children of type annotations, + // but otherwise simply check whether they are Literals. + if (isTypeAnnotation) { + childrenConstant = childrenConstant && isConstant(child); + } else { + childrenConstant = childrenConstant && dynamic_cast(&child); } }); - if (!literalArgs) { + if (!childrenConstant) { return false; } @@ -89,6 +103,7 @@ const ExpressionRegistry& getExpressionRegistry() { {"case", Case::parse}, {"coalesce", Coalesce::parse}, {"interpolate", parseInterpolate}, + {"length", Length::parse}, {"let", Let::parse}, {"literal", Literal::parse}, {"match", parseMatch}, @@ -139,15 +154,15 @@ ParseResult ParsingContext::parse(const Convertible& value, TypeAnnotationOption return parsed; } - if (expected) { - auto array = [&](std::unique_ptr expression) { - std::vector> args; - args.push_back(std::move(expression)); - return args; - }; + auto array = [&](std::unique_ptr expression) { + std::vector> args; + args.push_back(std::move(expression)); + return args; + }; + if (expected) { const type::Type actual = (*parsed)->getType(); - if ((*expected == type::String || *expected == type::Number || *expected == type::Boolean) && actual == type::Value) { + if ((*expected == type::String || *expected == type::Number || *expected == type::Boolean || *expected == type::Object) && actual == type::Value) { if (typeAnnotationOption == includeTypeAnnotations) { parsed = { std::make_unique(*expected, array(std::move(*parsed))) }; } @@ -167,7 +182,7 @@ ParseResult ParsingContext::parse(const Convertible& value, TypeAnnotationOption } } - // If an expression's arguments are all literals, we can evaluate + // If an expression's arguments are all constant, we can evaluate // it immediately and replace it with a literal value in the // parsed result. if (!dynamic_cast(parsed->get()) && isConstant(**parsed)) { @@ -191,8 +206,16 @@ ParseResult ParsingContext::parse(const Convertible& value, TypeAnnotationOption } } - // if this is the root expression, enforce constraints on the use ["zoom"]. - if (key.size() == 0 && !isZoomConstant(**parsed)) { + return parsed; +} + +ParseResult ParsingContext::parseExpression(const Convertible& value, TypeAnnotationOption typeAnnotationOption) { + return parse(value, typeAnnotationOption); +} + +ParseResult ParsingContext::parseLayerPropertyExpression(const Convertible& value, TypeAnnotationOption typeAnnotationOption) { + ParseResult parsed = parse(value, typeAnnotationOption); + if (parsed && !isZoomConstant(**parsed)) { optional> zoomCurve = findZoomCurve(parsed->get()); if (!zoomCurve) { error(R"("zoom" expression may only be used as input to a top-level "step" or "interpolate" expression.)"); @@ -202,10 +225,23 @@ ParseResult ParsingContext::parse(const Convertible& value, TypeAnnotationOption return ParseResult(); } } - return parsed; } +const std::string ParsingContext::getCombinedErrors() const { + std::string combinedError; + for (const ParsingError& parsingError : *errors) { + if (combinedError.size() > 0) { + combinedError += "\n"; + } + if (parsingError.key.size() > 0) { + combinedError += parsingError.key + ": "; + } + combinedError += parsingError.message; + } + return combinedError; +} + optional ParsingContext::checkType(const type::Type& t) { assert(expected); optional err = type::checkSubtype(*expected, t); diff --git a/src/mbgl/style/expression/value.cpp b/src/mbgl/style/expression/value.cpp index 72779d4956..1b3257c755 100644 --- a/src/mbgl/style/expression/value.cpp +++ b/src/mbgl/style/expression/value.cpp @@ -106,12 +106,13 @@ Value ValueConverter::toExpressionValue(const mbgl::Value& value) { mbgl::Value ValueConverter::fromExpressionValue(const Value& value) { return value.match( [&](const Color& color)->mbgl::Value { + std::array array = color.toArray(); return std::vector{ std::string("rgba"), - double(255 * color.r / color.a), - double(255 * color.g / color.a), - double(255 * color.b / color.a), - double(color.a) + array[0], + array[1], + array[2], + array[3], }; }, [&](const std::vector& values)->mbgl::Value { diff --git a/src/mbgl/style/filter.cpp b/src/mbgl/style/filter.cpp new file mode 100644 index 0000000000..51aa6bcf82 --- /dev/null +++ b/src/mbgl/style/filter.cpp @@ -0,0 +1,13 @@ +#include +#include +#include + +namespace mbgl { +namespace style { + +bool Filter::operator()(const expression::EvaluationContext &context) const { + return FilterBase::visit(*this, FilterEvaluator { context }); +} + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/filter_evaluator.cpp b/src/mbgl/style/filter_evaluator.cpp new file mode 100644 index 0000000000..72022172f4 --- /dev/null +++ b/src/mbgl/style/filter_evaluator.cpp @@ -0,0 +1,225 @@ +#include +#include +#include + +namespace mbgl { +namespace style { + +template +struct Comparator { + const Op& op; + + template + bool operator()(const T& lhs, const T& rhs) const { + return op(lhs, rhs); + } + + template + auto operator()(const T0& lhs, const T1& rhs) const + -> typename std::enable_if_t::value && !std::is_same::value && + std::is_arithmetic::value && !std::is_same::value, bool> { + return op(double(lhs), double(rhs)); + } + + template + auto operator()(const T0&, const T1&) const + -> typename std::enable_if_t::value || std::is_same::value || + !std::is_arithmetic::value || std::is_same::value, bool> { + return false; + } + + bool operator()(const NullValue&, + const NullValue&) const { + // Should be unreachable; null is not currently allowed by the style specification. + assert(false); + return false; + } + + bool operator()(const std::vector&, + const std::vector&) const { + // Should be unreachable; nested values are not currently allowed by the style specification. + assert(false); + return false; + } + + bool operator()(const PropertyMap&, + const PropertyMap&) const { + // Should be unreachable; nested values are not currently allowed by the style specification. + assert(false); + return false; + } +}; + +template +bool compare(const Value& lhs, const Value& rhs, const Op& op) { + return Value::binary_visit(lhs, rhs, Comparator { op }); +} + +bool equal(const Value& lhs, const Value& rhs) { + return compare(lhs, rhs, [] (const auto& lhs_, const auto& rhs_) { return lhs_ == rhs_; }); +} + +bool FilterEvaluator::operator()(const NullFilter&) const { + return true; +} + +bool FilterEvaluator::operator()(const EqualsFilter& filter) const { + optional actual = context.feature->getValue(filter.key); + return actual && equal(*actual, filter.value); +} + +bool FilterEvaluator::operator()(const NotEqualsFilter& filter) const { + optional actual = context.feature->getValue(filter.key); + return !actual || !equal(*actual, filter.value); +} + +bool FilterEvaluator::operator()(const LessThanFilter& filter) const { + optional actual = context.feature->getValue(filter.key); + return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ < rhs_; }); +} + +bool FilterEvaluator::operator()(const LessThanEqualsFilter& filter) const { + optional actual = context.feature->getValue(filter.key); + return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ <= rhs_; }); +} + +bool FilterEvaluator::operator()(const GreaterThanFilter& filter) const { + optional actual = context.feature->getValue(filter.key); + return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ > rhs_; }); +} + +bool FilterEvaluator::operator()(const GreaterThanEqualsFilter& filter) const { + optional actual = context.feature->getValue(filter.key); + return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ >= rhs_; }); +} + +bool FilterEvaluator::operator()(const InFilter& filter) const { + optional actual = context.feature->getValue(filter.key); + if (!actual) + return false; + for (const auto& v: filter.values) { + if (equal(*actual, v)) { + return true; + } + } + return false; +} + +bool FilterEvaluator::operator()(const NotInFilter& filter) const { + optional actual = context.feature->getValue(filter.key); + if (!actual) + return true; + for (const auto& v: filter.values) { + if (equal(*actual, v)) { + return false; + } + } + return true; +} + +bool FilterEvaluator::operator()(const AnyFilter& filter) const { + for (const auto& f: filter.filters) { + if (Filter::visit(f, *this)) { + return true; + } + } + return false; +} + +bool FilterEvaluator::operator()(const AllFilter& filter) const { + for (const auto& f: filter.filters) { + if (!Filter::visit(f, *this)) { + return false; + } + } + return true; +} + +bool FilterEvaluator::operator()(const NoneFilter& filter) const { + for (const auto& f: filter.filters) { + if (Filter::visit(f, *this)) { + return false; + } + } + return true; +} + +bool FilterEvaluator::operator()(const HasFilter& filter) const { + return bool(context.feature->getValue(filter.key)); +} + +bool FilterEvaluator::operator()(const NotHasFilter& filter) const { + return !context.feature->getValue(filter.key); +} + +bool FilterEvaluator::operator()(const TypeEqualsFilter& filter) const { + return context.feature->getType() == filter.value; +} + +bool FilterEvaluator::operator()(const TypeNotEqualsFilter& filter) const { + return context.feature->getType() != filter.value; +} + +bool FilterEvaluator::operator()(const TypeInFilter& filter) const { + for (const auto& v: filter.values) { + if (context.feature->getType() == v) { + return true; + } + } + return false; +} + +bool FilterEvaluator::operator()(const TypeNotInFilter& filter) const { + for (const auto& v: filter.values) { + if (context.feature->getType() == v) { + return false; + } + } + return true; +} + +bool FilterEvaluator::operator()(const IdentifierEqualsFilter& filter) const { + return context.feature->getID() == filter.value; +} + +bool FilterEvaluator::operator()(const IdentifierNotEqualsFilter& filter) const { + return context.feature->getID() != filter.value; +} + +bool FilterEvaluator::operator()(const IdentifierInFilter& filter) const { + for (const auto& v: filter.values) { + if (context.feature->getID() == v) { + return true; + } + } + return false; +} + +bool FilterEvaluator::operator()(const IdentifierNotInFilter& filter) const { + for (const auto& v: filter.values) { + if (context.feature->getID() == v) { + return false; + } + } + return true; +} + +bool FilterEvaluator::operator()(const HasIdentifierFilter&) const { + return bool(context.feature->getID()); +} + +bool FilterEvaluator::operator()(const NotHasIdentifierFilter&) const { + return !context.feature->getID(); +} + +bool FilterEvaluator::operator()(const ExpressionFilter& filter) const { + const expression::EvaluationResult result = filter.expression->evaluate(context); + if (result) { + const optional typed = expression::fromExpressionValue(*result); + return typed ? *typed : false; + } + return false; +} + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/layers/background_layer.cpp b/src/mbgl/style/layers/background_layer.cpp index d4ead18816..66ab46c078 100644 --- a/src/mbgl/style/layers/background_layer.cpp +++ b/src/mbgl/style/layers/background_layer.cpp @@ -53,12 +53,14 @@ void BackgroundLayer::setMinZoom(float minZoom) { auto impl_ = mutableImpl(); impl_->minZoom = minZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } void BackgroundLayer::setMaxZoom(float maxZoom) { auto impl_ = mutableImpl(); impl_->maxZoom = maxZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } // Layout properties diff --git a/src/mbgl/style/layers/circle_layer.cpp b/src/mbgl/style/layers/circle_layer.cpp index 9854932699..6dd744df1f 100644 --- a/src/mbgl/style/layers/circle_layer.cpp +++ b/src/mbgl/style/layers/circle_layer.cpp @@ -81,12 +81,14 @@ void CircleLayer::setMinZoom(float minZoom) { auto impl_ = mutableImpl(); impl_->minZoom = minZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } void CircleLayer::setMaxZoom(float maxZoom) { auto impl_ = mutableImpl(); impl_->maxZoom = maxZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } // Layout properties diff --git a/src/mbgl/style/layers/custom_layer.cpp b/src/mbgl/style/layers/custom_layer.cpp index 854c771847..0e51a70e50 100644 --- a/src/mbgl/style/layers/custom_layer.cpp +++ b/src/mbgl/style/layers/custom_layer.cpp @@ -6,20 +6,8 @@ namespace mbgl { namespace style { CustomLayer::CustomLayer(const std::string& layerID, - CustomLayerInitializeFunction init, - CustomLayerRenderFunction render, - CustomLayerContextLostFunction contextLost, - CustomLayerDeinitializeFunction deinit, - void* context) - : Layer(makeMutable(layerID, init, render, contextLost, deinit, context)) { -} - -CustomLayer::CustomLayer(const std::string& layerID, - CustomLayerInitializeFunction init, - CustomLayerRenderFunction render, - CustomLayerDeinitializeFunction deinit, - void* context) - : Layer(makeMutable(layerID, init, render, nullptr, deinit, context)) { + std::unique_ptr host) + : Layer(makeMutable(layerID, std::move(host))) { } CustomLayer::~CustomLayer() = default; diff --git a/src/mbgl/style/layers/custom_layer_impl.cpp b/src/mbgl/style/layers/custom_layer_impl.cpp index 1de268d2e2..05c41623c4 100644 --- a/src/mbgl/style/layers/custom_layer_impl.cpp +++ b/src/mbgl/style/layers/custom_layer_impl.cpp @@ -4,17 +4,9 @@ namespace mbgl { namespace style { CustomLayer::Impl::Impl(const std::string& id_, - CustomLayerInitializeFunction initializeFn_, - CustomLayerRenderFunction renderFn_, - CustomLayerContextLostFunction contextLostFn_, - CustomLayerDeinitializeFunction deinitializeFn_, - void* context_) + std::unique_ptr host_) : Layer::Impl(LayerType::Custom, id_, std::string()) { - initializeFn = initializeFn_; - renderFn = renderFn_; - deinitializeFn = deinitializeFn_; - contextLostFn = contextLostFn_; - context = context_; + host = std::move(host_); } bool CustomLayer::Impl::hasLayoutDifference(const Layer::Impl&) const { diff --git a/src/mbgl/style/layers/custom_layer_impl.hpp b/src/mbgl/style/layers/custom_layer_impl.hpp index 62efbbe15b..a41962c276 100644 --- a/src/mbgl/style/layers/custom_layer_impl.hpp +++ b/src/mbgl/style/layers/custom_layer_impl.hpp @@ -3,6 +3,8 @@ #include #include +#include + namespace mbgl { class TransformState; @@ -12,20 +14,12 @@ namespace style { class CustomLayer::Impl : public Layer::Impl { public: Impl(const std::string& id, - CustomLayerInitializeFunction, - CustomLayerRenderFunction, - CustomLayerContextLostFunction, - CustomLayerDeinitializeFunction, - void* context); + std::unique_ptr host); bool hasLayoutDifference(const Layer::Impl&) const override; void stringifyLayout(rapidjson::Writer&) const override; - CustomLayerInitializeFunction initializeFn = nullptr; - CustomLayerRenderFunction renderFn = nullptr; - CustomLayerContextLostFunction contextLostFn = nullptr; - CustomLayerDeinitializeFunction deinitializeFn = nullptr; - void* context = nullptr; + std::shared_ptr host; }; } // namespace style diff --git a/src/mbgl/style/layers/fill_extrusion_layer.cpp b/src/mbgl/style/layers/fill_extrusion_layer.cpp index 62f92cef75..c5b4ef0ef3 100644 --- a/src/mbgl/style/layers/fill_extrusion_layer.cpp +++ b/src/mbgl/style/layers/fill_extrusion_layer.cpp @@ -81,12 +81,14 @@ void FillExtrusionLayer::setMinZoom(float minZoom) { auto impl_ = mutableImpl(); impl_->minZoom = minZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } void FillExtrusionLayer::setMaxZoom(float maxZoom) { auto impl_ = mutableImpl(); impl_->maxZoom = maxZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } // Layout properties diff --git a/src/mbgl/style/layers/fill_layer.cpp b/src/mbgl/style/layers/fill_layer.cpp index 65975752db..99a2a51ed0 100644 --- a/src/mbgl/style/layers/fill_layer.cpp +++ b/src/mbgl/style/layers/fill_layer.cpp @@ -81,12 +81,14 @@ void FillLayer::setMinZoom(float minZoom) { auto impl_ = mutableImpl(); impl_->minZoom = minZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } void FillLayer::setMaxZoom(float maxZoom) { auto impl_ = mutableImpl(); impl_->maxZoom = maxZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } // Layout properties diff --git a/src/mbgl/style/layers/heatmap_layer.cpp b/src/mbgl/style/layers/heatmap_layer.cpp index 4989ff15f1..3f7881ddd3 100644 --- a/src/mbgl/style/layers/heatmap_layer.cpp +++ b/src/mbgl/style/layers/heatmap_layer.cpp @@ -85,12 +85,14 @@ void HeatmapLayer::setMinZoom(float minZoom) { auto impl_ = mutableImpl(); impl_->minZoom = minZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } void HeatmapLayer::setMaxZoom(float maxZoom) { auto impl_ = mutableImpl(); impl_->maxZoom = maxZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } // Layout properties diff --git a/src/mbgl/style/layers/hillshade_layer.cpp b/src/mbgl/style/layers/hillshade_layer.cpp index ea736af1ad..e352ae090c 100644 --- a/src/mbgl/style/layers/hillshade_layer.cpp +++ b/src/mbgl/style/layers/hillshade_layer.cpp @@ -59,12 +59,14 @@ void HillshadeLayer::setMinZoom(float minZoom) { auto impl_ = mutableImpl(); impl_->minZoom = minZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } void HillshadeLayer::setMaxZoom(float maxZoom) { auto impl_ = mutableImpl(); impl_->maxZoom = maxZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } // Layout properties diff --git a/src/mbgl/style/layers/line_layer.cpp b/src/mbgl/style/layers/line_layer.cpp index 1c7f0d28ee..56eac34c00 100644 --- a/src/mbgl/style/layers/line_layer.cpp +++ b/src/mbgl/style/layers/line_layer.cpp @@ -82,12 +82,14 @@ void LineLayer::setMinZoom(float minZoom) { auto impl_ = mutableImpl(); impl_->minZoom = minZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } void LineLayer::setMaxZoom(float maxZoom) { auto impl_ = mutableImpl(); impl_->maxZoom = maxZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } // Layout properties diff --git a/src/mbgl/style/layers/raster_layer.cpp b/src/mbgl/style/layers/raster_layer.cpp index a9a8d273fa..36b2e3e027 100644 --- a/src/mbgl/style/layers/raster_layer.cpp +++ b/src/mbgl/style/layers/raster_layer.cpp @@ -59,12 +59,14 @@ void RasterLayer::setMinZoom(float minZoom) { auto impl_ = mutableImpl(); impl_->minZoom = minZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } void RasterLayer::setMaxZoom(float maxZoom) { auto impl_ = mutableImpl(); impl_->maxZoom = maxZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } // Layout properties diff --git a/src/mbgl/style/layers/symbol_layer.cpp b/src/mbgl/style/layers/symbol_layer.cpp index d1a1ba246e..c940f3b00a 100644 --- a/src/mbgl/style/layers/symbol_layer.cpp +++ b/src/mbgl/style/layers/symbol_layer.cpp @@ -82,12 +82,14 @@ void SymbolLayer::setMinZoom(float minZoom) { auto impl_ = mutableImpl(); impl_->minZoom = minZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } void SymbolLayer::setMaxZoom(float maxZoom) { auto impl_ = mutableImpl(); impl_->maxZoom = maxZoom; baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } // Layout properties diff --git a/src/mbgl/text/collision_index.cpp b/src/mbgl/text/collision_index.cpp index 833658c33e..091840a371 100644 --- a/src/mbgl/text/collision_index.cpp +++ b/src/mbgl/text/collision_index.cpp @@ -219,7 +219,7 @@ std::pair CollisionIndex::placeLineFeature(CollisionFeature& feature, } -void CollisionIndex::insertFeature(CollisionFeature& feature, bool ignorePlacement) { +void CollisionIndex::insertFeature(CollisionFeature& feature, bool ignorePlacement, uint32_t bucketInstanceId) { if (feature.alongLine) { for (auto& circle : feature.boxes) { if (!circle.used) { @@ -227,18 +227,18 @@ void CollisionIndex::insertFeature(CollisionFeature& feature, bool ignorePlaceme } if (ignorePlacement) { - ignoredGrid.insert(IndexedSubfeature(feature.indexedFeature), {{ circle.px, circle.py }, circle.radius}); + ignoredGrid.insert(IndexedSubfeature(feature.indexedFeature, bucketInstanceId), {{ circle.px, circle.py }, circle.radius}); } else { - collisionGrid.insert(IndexedSubfeature(feature.indexedFeature), {{ circle.px, circle.py }, circle.radius}); + collisionGrid.insert(IndexedSubfeature(feature.indexedFeature, bucketInstanceId), {{ circle.px, circle.py }, circle.radius}); } } } else { assert(feature.boxes.size() == 1); auto& box = feature.boxes[0]; if (ignorePlacement) { - ignoredGrid.insert(IndexedSubfeature(feature.indexedFeature), {{ box.px1, box.py1 }, { box.px2, box.py2 }}); + ignoredGrid.insert(IndexedSubfeature(feature.indexedFeature, bucketInstanceId), {{ box.px1, box.py1 }, { box.px2, box.py2 }}); } else { - collisionGrid.insert(IndexedSubfeature(feature.indexedFeature), {{ box.px1, box.py1 }, { box.px2, box.py2 }}); + collisionGrid.insert(IndexedSubfeature(feature.indexedFeature, bucketInstanceId), {{ box.px1, box.py1 }, { box.px2, box.py2 }}); } } } @@ -262,66 +262,41 @@ bool polygonIntersectsBox(const LineString& polygon, const GridIndex CollisionIndex::queryRenderedSymbols(const GeometryCoordinates& queryGeometry, const UnwrappedTileID& tileID, const std::string& sourceID) const { - std::vector result; +std::unordered_map> CollisionIndex::queryRenderedSymbols(const ScreenLineString& queryGeometry) const { + std::unordered_map> result; if (queryGeometry.empty() || (collisionGrid.empty() && ignoredGrid.empty())) { return result; } - - mat4 posMatrix; - mat4 projMatrix; - transformState.getProjMatrix(projMatrix); - transformState.matrixFor(posMatrix, tileID); - matrix::multiply(posMatrix, projMatrix, posMatrix); - - // queryGeometry is specified in integer tile units, but in projecting we switch to float pixels - LineString projectedQuery; + + LineString gridQuery; for (const auto& point : queryGeometry) { - auto projected = projectPoint(posMatrix, convertPoint(point)); - projectedQuery.push_back(projected); + gridQuery.emplace_back(point.x + viewportPadding, point.y + viewportPadding); } - auto envelope = mapbox::geometry::envelope(projectedQuery); + auto envelope = mapbox::geometry::envelope(gridQuery); using QueryResult = std::pair::BBox>; - std::vector thisTileFeatures; std::vector features = collisionGrid.queryWithBoxes(envelope); - - for (auto& queryResult : features) { - auto& feature = queryResult.first; - if (feature.sourceID == sourceID && feature.tileID == tileID.canonical) { - // We only have to filter on the canonical ID because even if the feature is showing multiple times - // we treat it as one feature. - thisTileFeatures.push_back(queryResult); - } - } - std::vector ignoredFeatures = ignoredGrid.queryWithBoxes(envelope); - for (auto& queryResult : ignoredFeatures) { - auto& feature = queryResult.first; - if (feature.sourceID == sourceID && feature.tileID == tileID.canonical) { - thisTileFeatures.push_back(queryResult); - } - } + features.insert(features.end(), ignoredFeatures.begin(), ignoredFeatures.end()); - std::unordered_map>> sourceLayerFeatures; - for (auto& queryResult : thisTileFeatures) { + std::unordered_map> seenBuckets; + for (auto& queryResult : features) { auto& feature = queryResult.first; auto& bbox = queryResult.second; // Skip already seen features. - auto& seenFeatures = sourceLayerFeatures[feature.sourceLayerName][feature.bucketName]; + auto& seenFeatures = seenBuckets[feature.bucketInstanceId]; if (seenFeatures.find(feature.index) != seenFeatures.end()) continue; - - seenFeatures.insert(feature.index); - if (!polygonIntersectsBox(projectedQuery, bbox)) { + if (!polygonIntersectsBox(gridQuery, bbox)) { continue; } - result.push_back(feature); + seenFeatures.insert(feature.index); + result[feature.bucketInstanceId].push_back(feature); } return result; diff --git a/src/mbgl/text/collision_index.hpp b/src/mbgl/text/collision_index.hpp index 8653c1d76c..b2be4c6ade 100644 --- a/src/mbgl/text/collision_index.hpp +++ b/src/mbgl/text/collision_index.hpp @@ -28,11 +28,10 @@ public: const bool pitchWithMap, const bool collisionDebug); - void insertFeature(CollisionFeature& feature, bool ignorePlacement); + void insertFeature(CollisionFeature& feature, bool ignorePlacement, uint32_t bucketInstanceId); - std::vector queryRenderedSymbols(const GeometryCoordinates&, const UnwrappedTileID& tileID, const std::string& sourceID) const; + std::unordered_map> queryRenderedSymbols(const ScreenLineString&) const; - private: bool isOffscreen(const CollisionBox&) const; bool isInsideGrid(const CollisionBox&) const; diff --git a/src/mbgl/text/cross_tile_symbol_index.cpp b/src/mbgl/text/cross_tile_symbol_index.cpp index f88bab9d6f..b0c3511ce3 100644 --- a/src/mbgl/text/cross_tile_symbol_index.cpp +++ b/src/mbgl/text/cross_tile_symbol_index.cpp @@ -62,7 +62,7 @@ CrossTileSymbolLayerIndex::CrossTileSymbolLayerIndex() { } bool CrossTileSymbolLayerIndex::addBucket(const OverscaledTileID& tileID, SymbolBucket& bucket, uint32_t& maxCrossTileID) { - auto thisZoomIndexes = indexes[tileID.overscaledZ]; + const auto& thisZoomIndexes = indexes[tileID.overscaledZ]; auto previousIndex = thisZoomIndexes.find(tileID); if (previousIndex != thisZoomIndexes.end()) { if (previousIndex->second.bucketInstanceId == bucket.bucketInstanceId) { @@ -153,6 +153,10 @@ bool CrossTileSymbolIndex::addLayer(RenderSymbolLayer& symbolLayer) { auto bucket = renderTile.tile.getBucket(*symbolLayer.baseImpl); assert(dynamic_cast(bucket)); SymbolBucket& symbolBucket = *reinterpret_cast(bucket); + if (symbolBucket.bucketLeaderID != symbolLayer.getID()) { + // Only add this layer if it's the "group leader" for the bucket + continue; + } if (!symbolBucket.bucketInstanceId) { symbolBucket.bucketInstanceId = ++maxBucketInstanceId; diff --git a/src/mbgl/text/placement.cpp b/src/mbgl/text/placement.cpp index 54b2b7539b..9883a1f456 100644 --- a/src/mbgl/text/placement.cpp +++ b/src/mbgl/text/placement.cpp @@ -49,17 +49,25 @@ void Placement::placeLayer(RenderSymbolLayer& symbolLayer, const mat4& projMatri if (!renderTile.tile.isRenderable()) { continue; } - - auto bucket = renderTile.tile.getBucket(*symbolLayer.baseImpl); + assert(dynamic_cast(&renderTile.tile)); + GeometryTile& geometryTile = static_cast(renderTile.tile); + + + auto bucket = geometryTile.getBucket(*symbolLayer.baseImpl); assert(dynamic_cast(bucket)); SymbolBucket& symbolBucket = *reinterpret_cast(bucket); + + if (symbolBucket.bucketLeaderID != symbolLayer.getID()) { + // Only place this layer if it's the "group leader" for the bucket + continue; + } auto& layout = symbolBucket.layout; const float pixelsToTileUnits = renderTile.id.pixelsToTileUnits(1, state.getZoom()); - const float scale = std::pow(2, state.getZoom() - renderTile.tile.id.overscaledZ); - const float textPixelRatio = (util::tileSize * renderTile.tile.id.overscaleFactor()) / util::EXTENT; + const float scale = std::pow(2, state.getZoom() - geometryTile.id.overscaledZ); + const float textPixelRatio = (util::tileSize * geometryTile.id.overscaleFactor()) / util::EXTENT; mat4 posMatrix; state.matrixFor(posMatrix, renderTile.id); @@ -76,7 +84,14 @@ void Placement::placeLayer(RenderSymbolLayer& symbolLayer, const mat4& projMatri layout.get() == style::AlignmentType::Map, state, pixelsToTileUnits); - + + + // As long as this placement lives, we have to hold onto this bucket's + // matching FeatureIndex/data for querying purposes + retainedQueryData.emplace(std::piecewise_construct, + std::forward_as_tuple(symbolBucket.bucketInstanceId), + std::forward_as_tuple(symbolBucket.bucketInstanceId, geometryTile.getFeatureIndex(), geometryTile.id)); + placeLayerBucket(symbolBucket, posMatrix, textLabelPlaneMatrix, iconLabelPlaneMatrix, scale, textPixelRatio, showCollisionBoxes, seenCrossTileIDs, renderTile.tile.holdForFade()); } } @@ -150,11 +165,11 @@ void Placement::placeLayerBucket( } if (placeText) { - collisionIndex.insertFeature(symbolInstance.textCollisionFeature, bucket.layout.get()); + collisionIndex.insertFeature(symbolInstance.textCollisionFeature, bucket.layout.get(), bucket.bucketInstanceId); } if (placeIcon) { - collisionIndex.insertFeature(symbolInstance.iconCollisionFeature, bucket.layout.get()); + collisionIndex.insertFeature(symbolInstance.iconCollisionFeature, bucket.layout.get(), bucket.bucketInstanceId); } assert(symbolInstance.crossTileID != 0); @@ -220,6 +235,10 @@ void Placement::updateLayerOpacities(RenderSymbolLayer& symbolLayer) { auto bucket = renderTile.tile.getBucket(*symbolLayer.baseImpl); assert(dynamic_cast(bucket)); SymbolBucket& symbolBucket = *reinterpret_cast(bucket); + if (symbolBucket.bucketLeaderID != symbolLayer.getID()) { + // Only update opacities this layer if it's the "group leader" for the bucket + continue; + } updateBucketOpacities(symbolBucket, seenCrossTileIDs); } } @@ -305,6 +324,10 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, std::set& bucket.updateOpacity(); bucket.sortFeatures(state.getAngle()); + auto retainedData = retainedQueryData.find(bucket.bucketInstanceId); + if (retainedData != retainedQueryData.end()) { + retainedData->second.featureSortOrder = bucket.featureSortOrder; + } } float Placement::symbolFadeChange(TimePoint now) const { @@ -337,5 +360,13 @@ void Placement::setStale() { const CollisionIndex& Placement::getCollisionIndex() const { return collisionIndex; } + +const RetainedQueryData& Placement::getQueryData(uint32_t bucketInstanceId) const { + auto it = retainedQueryData.find(bucketInstanceId); + if (it == retainedQueryData.end()) { + throw std::runtime_error("Placement::getQueryData with unrecognized bucketInstanceId"); + } + return it->second; +} } // namespace mbgl diff --git a/src/mbgl/text/placement.hpp b/src/mbgl/text/placement.hpp index 653ae352ed..0e1751b127 100644 --- a/src/mbgl/text/placement.hpp +++ b/src/mbgl/text/placement.hpp @@ -44,7 +44,21 @@ public: // visible right away. const bool skipFade; }; - + +struct RetainedQueryData { + uint32_t bucketInstanceId; + std::shared_ptr featureIndex; + OverscaledTileID tileID; + std::shared_ptr> featureSortOrder; + + RetainedQueryData(uint32_t bucketInstanceId_, + std::shared_ptr featureIndex_, + OverscaledTileID tileID_) + : bucketInstanceId(bucketInstanceId_) + , featureIndex(std::move(featureIndex_)) + , tileID(std::move(tileID_)) {} +}; + class Placement { public: Placement(const TransformState&, MapMode mapMode); @@ -59,6 +73,8 @@ public: bool stillRecent(TimePoint now) const; void setRecent(TimePoint now); void setStale(); + + const RetainedQueryData& getQueryData(uint32_t bucketInstanceId) const; private: void placeLayerBucket( @@ -85,6 +101,8 @@ private: TimePoint recentUntil; bool stale = false; + + std::unordered_map retainedQueryData; }; } // namespace mbgl diff --git a/src/mbgl/tile/custom_geometry_tile.cpp b/src/mbgl/tile/custom_geometry_tile.cpp index 33962ad87d..a2fefcfa9f 100644 --- a/src/mbgl/tile/custom_geometry_tile.cpp +++ b/src/mbgl/tile/custom_geometry_tile.cpp @@ -79,7 +79,7 @@ void CustomGeometryTile::querySourceFeatures( auto feature = layer->getFeature(i); // Apply filter, if any - if (queryOptions.filter && !(*queryOptions.filter)(*feature)) { + if (queryOptions.filter && !(*queryOptions.filter)(style::expression::EvaluationContext { static_cast(id.overscaledZ), feature.get() })) { continue; } diff --git a/src/mbgl/tile/geojson_tile.cpp b/src/mbgl/tile/geojson_tile.cpp index bbec899950..f211c03569 100644 --- a/src/mbgl/tile/geojson_tile.cpp +++ b/src/mbgl/tile/geojson_tile.cpp @@ -30,7 +30,7 @@ void GeoJSONTile::querySourceFeatures( auto feature = layer->getFeature(i); // Apply filter, if any - if (options.filter && !(*options.filter)(*feature)) { + if (options.filter && !(*options.filter)(style::expression::EvaluationContext { static_cast(this->id.overscaledZ), feature.get() })) { continue; } diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp index a58c744065..8efe12d54f 100644 --- a/src/mbgl/tile/geometry_tile.cpp +++ b/src/mbgl/tile/geometry_tile.cpp @@ -125,23 +125,16 @@ void GeometryTile::setShowCollisionBoxes(const bool showCollisionBoxes_) { } void GeometryTile::onLayout(LayoutResult result, const uint64_t resultCorrelationID) { - // Don't mark ourselves loaded or renderable until the first successful placement - // TODO: Ideally we'd render this tile without symbols as long as this tile wasn't - // replacing a tile at a different zoom that _did_ have symbols. - (void)resultCorrelationID; - nonSymbolBuckets = std::move(result.nonSymbolBuckets); - pendingFeatureIndex = std::move(result.featureIndex); - pendingData = std::move(result.tileData); - observer->onTileChanged(*this); -} - -void GeometryTile::onPlacement(PlacementResult result, const uint64_t resultCorrelationID) { loaded = true; renderable = true; if (resultCorrelationID == correlationID) { pending = false; } - symbolBuckets = std::move(result.symbolBuckets); + + buckets = std::move(result.buckets); + + latestFeatureIndex = std::move(result.featureIndex); + if (result.glyphAtlasImage) { glyphAtlasImage = std::move(*result.glyphAtlasImage); } @@ -183,11 +176,7 @@ void GeometryTile::upload(gl::Context& context) { } }; - for (auto& entry : nonSymbolBuckets) { - uploadFn(*entry.second); - } - - for (auto& entry : symbolBuckets) { + for (auto& entry : buckets) { uploadFn(*entry.second); } @@ -203,7 +192,6 @@ void GeometryTile::upload(gl::Context& context) { } Bucket* GeometryTile::getBucket(const Layer::Impl& layer) const { - const auto& buckets = layer.type == LayerType::Symbol ? symbolBuckets : nonSymbolBuckets; const auto it = buckets.find(layer.id); if (it == buckets.end()) { return nullptr; @@ -213,13 +201,15 @@ Bucket* GeometryTile::getBucket(const Layer::Impl& layer) const { return it->second.get(); } -void GeometryTile::commitFeatureIndex() { - if (pendingFeatureIndex) { - featureIndex = std::move(pendingFeatureIndex); - } - if (pendingData) { - data = std::move(pendingData); +float GeometryTile::getQueryPadding(const std::vector& layers) { + float queryPadding = 0; + for (const RenderLayer* layer : layers) { + auto bucket = getBucket(*layer->baseImpl); + if (bucket && bucket->hasData()) { + queryPadding = std::max(queryPadding, bucket->getQueryRadius(*layer)); + } } + return queryPadding; } void GeometryTile::queryRenderedFeatures( @@ -228,39 +218,34 @@ void GeometryTile::queryRenderedFeatures( const TransformState& transformState, const std::vector& layers, const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) { - - if (!featureIndex || !data) return; - - // Determine the additional radius needed factoring in property functions - float additionalRadius = 0; - for (const RenderLayer* layer : layers) { - auto bucket = getBucket(*layer->baseImpl); - if (bucket) { - additionalRadius = std::max(additionalRadius, bucket->getQueryRadius(*layer)); - } - } - - featureIndex->query(result, - queryGeometry, - transformState.getAngle(), - util::tileSize * id.overscaleFactor(), - std::pow(2, transformState.getZoom() - id.overscaledZ), - options, - *data, - id.toUnwrapped(), - sourceID, - layers, - collisionIndex, - additionalRadius); + const mat4& projMatrix) { + + if (!getData()) return; + + const float queryPadding = getQueryPadding(layers); + + mat4 posMatrix; + transformState.matrixFor(posMatrix, id.toUnwrapped()); + matrix::multiply(posMatrix, projMatrix, posMatrix); + + latestFeatureIndex->query(result, + queryGeometry, + transformState, + posMatrix, + util::tileSize * id.overscaleFactor(), + std::pow(2, transformState.getZoom() - id.overscaledZ), + options, + id.toUnwrapped(), + layers, + queryPadding * transformState.maxPitchScaleFactor()); } void GeometryTile::querySourceFeatures( std::vector& result, const SourceQueryOptions& options) { - // Data not yet available - if (!data) { + // Data not yet available, or tile is empty + if (!getData()) { return; } @@ -273,7 +258,7 @@ void GeometryTile::querySourceFeatures( for (auto sourceLayer : *options.sourceLayers) { // Go throught all sourceLayers, if any // to gather all the features - auto layer = data->getLayer(sourceLayer); + auto layer = getData()->getLayer(sourceLayer); if (layer) { auto featureCount = layer->featureCount(); @@ -281,7 +266,7 @@ void GeometryTile::querySourceFeatures( auto feature = layer->getFeature(i); // Apply filter, if any - if (options.filter && !(*options.filter)(*feature)) { + if (options.filter && !(*options.filter)(style::expression::EvaluationContext { static_cast(this->id.overscaledZ), feature.get() })) { continue; } diff --git a/src/mbgl/tile/geometry_tile.hpp b/src/mbgl/tile/geometry_tile.hpp index 00a4aafadf..af122474c2 100644 --- a/src/mbgl/tile/geometry_tile.hpp +++ b/src/mbgl/tile/geometry_tile.hpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include @@ -55,43 +54,33 @@ public: const TransformState&, const std::vector& layers, const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) override; + const mat4& projMatrix) override; void querySourceFeatures( std::vector& result, const SourceQueryOptions&) override; + float getQueryPadding(const std::vector&) override; + void cancel() override; class LayoutResult { public: - std::unordered_map> nonSymbolBuckets; + std::unordered_map> buckets; std::unique_ptr featureIndex; - std::unique_ptr tileData; - - LayoutResult(std::unordered_map> nonSymbolBuckets_, - std::unique_ptr featureIndex_, - std::unique_ptr tileData_) - : nonSymbolBuckets(std::move(nonSymbolBuckets_)), - featureIndex(std::move(featureIndex_)), - tileData(std::move(tileData_)) {} - }; - void onLayout(LayoutResult, uint64_t correlationID); - - class PlacementResult { - public: - std::unordered_map> symbolBuckets; optional glyphAtlasImage; optional iconAtlasImage; - PlacementResult(std::unordered_map> symbolBuckets_, - optional glyphAtlasImage_, - optional iconAtlasImage_) - : symbolBuckets(std::move(symbolBuckets_)), + LayoutResult(std::unordered_map> buckets_, + std::unique_ptr featureIndex_, + optional glyphAtlasImage_, + optional iconAtlasImage_) + : buckets(std::move(buckets_)), + featureIndex(std::move(featureIndex_)), glyphAtlasImage(std::move(glyphAtlasImage_)), iconAtlasImage(std::move(iconAtlasImage_)) {} }; - void onPlacement(PlacementResult, uint64_t correlationID); + void onLayout(LayoutResult, uint64_t correlationID); void onError(std::exception_ptr, uint64_t correlationID); @@ -100,11 +89,11 @@ public: void markRenderedPreviously() override; void performedFadePlacement() override; - void commitFeatureIndex() override; + const std::shared_ptr getFeatureIndex() const { return latestFeatureIndex; } protected: const GeometryTileData* getData() { - return data.get(); + return latestFeatureIndex ? latestFeatureIndex->getData() : nullptr; } private: @@ -123,17 +112,13 @@ private: uint64_t correlationID = 0; - std::unordered_map> nonSymbolBuckets; - std::unique_ptr featureIndex; - std::unique_ptr pendingFeatureIndex; - std::unique_ptr data; - std::unique_ptr pendingData; + std::unordered_map> buckets; + + std::shared_ptr latestFeatureIndex; optional glyphAtlasImage; optional iconAtlasImage; - std::unordered_map> symbolBuckets; - const MapMode mode; bool showCollisionBoxes; diff --git a/src/mbgl/tile/geometry_tile_worker.cpp b/src/mbgl/tile/geometry_tile_worker.cpp index 24841dd125..2e7d588d9b 100644 --- a/src/mbgl/tile/geometry_tile_worker.cpp +++ b/src/mbgl/tile/geometry_tile_worker.cpp @@ -41,48 +41,73 @@ GeometryTileWorker::GeometryTileWorker(ActorRef self_, GeometryTileWorker::~GeometryTileWorker() = default; /* - NOTE: The comments below are technically correct, but currently - conceptually misleading. The change to foreground label placement - means that: - (1) "placement" here is a misnomer: the remaining role of - "attemptPlacement" is symbol buffer generation - (2) Once a tile has completed layout, we will only run - "attemptPlacement" once - (3) Tiles won't be rendered until "attemptPlacement" has run once - - TODO: Simplify GeometryTileWorker to fit its new role - https://github.com/mapbox/mapbox-gl-native/issues/10457 - GeometryTileWorker is a state machine. This is its transition diagram. States are indicated by [state], lines are transitions triggered by messages, (parentheses) are actions taken on transition. - [idle] <----------------------------. - | | - set{Data,Layers,Placement}, symbolDependenciesChanged | - | | - (do layout/placement; self-send "coalesced") | - v | - [coalescing] --- coalesced ------------. - | | - .-----------------. .---------------. + [Idle] <-------------------------. + | | + set{Data,Layers}, symbolDependenciesChanged, | + setShowCollisionBoxes | + | | + (do parse and/or symbol layout; self-send "coalesced") | + v | + [Coalescing] --- coalesced ---------. + | | + .-----------. .---------------------. | | - .--- set{Data,Layers} setPlacement -----. - | | | | - | v v | - .-- [need layout] <-- set{Data,Layers} -- [need placement] --. + .--- set{Data,Layers} setShowCollisionBoxes, + | | symbolDependenciesChanged --. + | | | | + | v v | + .-- [NeedsParse] <-- set{Data,Layers} -- [NeedsSymbolLayout] ---. | | coalesced coalesced | | v v - (do layout or placement; self-send "coalesced"; goto [coalescing]) - - The idea is that in the [idle] state, layout or placement happens immediately - in response to a "set" message. During this processing, multiple "set" messages - might get queued in the mailbox. At the end of processing, we self-send "coalesced", - read all the queued messages until we get to "coalesced", and then redo either - layout or placement if there were one or more "set"s (with layout taking priority, - since it will trigger placement when complete), or return to the [idle] state if not. + (do parse or symbol layout; self-send "coalesced"; goto [coalescing]) + + The idea is that in the [idle] state, parsing happens immediately in response to + a "set" message, and symbol layout happens once all symbol dependencies are met. + During this processing, multiple "set" messages might get queued in the mailbox. + At the end of processing, we self-send "coalesced", read all the queued messages + until we get to "coalesced", and then re-parse if there were one or more "set"s or + return to the [idle] state if not. + + One important goal of the design is to prevent starvation. Under heavy load new + requests for tiles should not prevent in progress request from completing. + It is nevertheless possible to restart an in-progress request: + + - [Idle] setData -> parse() + sends getGlyphs, hasPendingSymbolDependencies() is true + enters [Coalescing], sends coalesced + - [Coalescing] coalesced -> [Idle] + - [Idle] setData -> new parse(), interrupts old parse() + sends getGlyphs, hasPendingSymbolDependencies() is true + enters [Coalescing], sends coalesced + - [Coalescing] onGlyphsAvailable -> [NeedsSymbolLayout] + hasPendingSymbolDependencies() may or may not be true + - [NeedsSymbolLayout] coalesced -> performSymbolLayout() + Generates result depending on whether dependencies are met + -> [Idle] + + In this situation, we are counting on the idea that even with rapid changes to + the tile's data, the set of glyphs/images it requires will not keep growing without + limit. + + Although parsing (which populates all non-symbol buckets and requests dependencies + for symbol buckets) is internally separate from symbol layout, we only return + results to the foreground when we have completed both steps. Because we _move_ + the result buckets to the foreground, it is necessary to re-generate all buckets from + scratch for `setShowCollisionBoxes`, even though it only affects symbol layers. + + The GL JS equivalent (in worker_tile.js and vector_tile_worker_source.js) + is somewhat simpler because it relies on getGlyphs/getImages calls that transfer + an entire set of glyphs/images on every tile load, while the native logic + maintains a local state that can be incrementally updated. Because each tile load + call becomes self-contained, the equivalent of the coalescing logic is handled by + 'reloadTile' queueing a single extra 'reloadTile' callback to run after the next + completed parse. */ void GeometryTileWorker::setData(std::unique_ptr data_, uint64_t correlationID_) { @@ -92,14 +117,14 @@ void GeometryTileWorker::setData(std::unique_ptr data_, switch (state) { case Idle: - redoLayout(); + parse(); coalesce(); break; case Coalescing: - case NeedLayout: - case NeedPlacement: - state = NeedLayout; + case NeedsParse: + case NeedsSymbolLayout: + state = NeedsParse; break; } } catch (...) { @@ -114,16 +139,16 @@ void GeometryTileWorker::setLayers(std::vector> layers_, switch (state) { case Idle: - redoLayout(); + parse(); coalesce(); break; case Coalescing: - case NeedPlacement: - state = NeedLayout; + case NeedsSymbolLayout: + state = NeedsParse; break; - case NeedLayout: + case NeedsParse: break; } } catch (...) { @@ -138,16 +163,20 @@ void GeometryTileWorker::setShowCollisionBoxes(bool showCollisionBoxes_, uint64_ switch (state) { case Idle: - attemptPlacement(); - coalesce(); + if (!hasPendingParseResult()) { + // Trigger parse if nothing is in flight, otherwise symbol layout will automatically + // pick up the change + parse(); + coalesce(); + } break; case Coalescing: - state = NeedPlacement; + state = NeedsSymbolLayout; break; - case NeedPlacement: - case NeedLayout: + case NeedsSymbolLayout: + case NeedsParse: break; } } catch (...) { @@ -160,19 +189,23 @@ void GeometryTileWorker::symbolDependenciesChanged() { switch (state) { case Idle: if (symbolLayoutsNeedPreparation) { - attemptPlacement(); + // symbolLayoutsNeedPreparation can only be set true by parsing + // and the parse result can only be cleared by performSymbolLayout + // which also clears symbolLayoutsNeedPreparation + assert(hasPendingParseResult()); + performSymbolLayout(); coalesce(); } break; case Coalescing: if (symbolLayoutsNeedPreparation) { - state = NeedPlacement; + state = NeedsSymbolLayout; } break; - case NeedPlacement: - case NeedLayout: + case NeedsSymbolLayout: + case NeedsParse: break; } } catch (...) { @@ -191,13 +224,16 @@ void GeometryTileWorker::coalesced() { state = Idle; break; - case NeedLayout: - redoLayout(); + case NeedsParse: + parse(); coalesce(); break; - case NeedPlacement: - attemptPlacement(); + case NeedsSymbolLayout: + // We may have entered NeedsSymbolLayout while coalescing + // after a performSymbolLayout. In that case, we need to + // start over with parsing in order to do another layout. + hasPendingParseResult() ? performSymbolLayout() : parse(); coalesce(); break; } @@ -279,7 +315,7 @@ static std::vector> toRenderLayers(const std::vecto return renderLayers; } -void GeometryTileWorker::redoLayout() { +void GeometryTileWorker::parse() { if (!data || !layers) { return; } @@ -292,8 +328,8 @@ void GeometryTileWorker::redoLayout() { } std::unordered_map> symbolLayoutMap; - std::unordered_map> buckets; - auto featureIndex = std::make_unique(); + buckets.clear(); + featureIndex = std::make_unique(*data ? (*data)->clone() : nullptr); BucketParameters parameters { id, mode, pixelRatio }; GlyphDependencies glyphDependencies; @@ -339,7 +375,7 @@ void GeometryTileWorker::redoLayout() { for (std::size_t i = 0; !obsolete && i < geometryLayer->featureCount(); i++) { std::unique_ptr feature = geometryLayer->getFeature(i); - if (!filter(feature->getType(), feature->getID(), [&] (const auto& key) { return feature->getValue(key); })) + if (!filter(expression::EvaluationContext { static_cast(this->id.overscaledZ), feature.get() })) continue; GeometryCollection geometries = feature->getGeometries(); @@ -368,13 +404,7 @@ void GeometryTileWorker::redoLayout() { requestNewGlyphs(glyphDependencies); requestNewImages(imageDependencies); - parent.invoke(&GeometryTile::onLayout, GeometryTile::LayoutResult { - std::move(buckets), - std::move(featureIndex), - *data ? (*data)->clone() : nullptr, - }, correlationID); - - attemptPlacement(); + performSymbolLayout(); } bool GeometryTileWorker::hasPendingSymbolDependencies() const { @@ -385,9 +415,13 @@ bool GeometryTileWorker::hasPendingSymbolDependencies() const { } return !pendingImageDependencies.empty(); } + +bool GeometryTileWorker::hasPendingParseResult() const { + return bool(featureIndex); +} -void GeometryTileWorker::attemptPlacement() { - if (!data || !layers || hasPendingSymbolDependencies()) { +void GeometryTileWorker::performSymbolLayout() { + if (!data || !layers || !hasPendingParseResult() || hasPendingSymbolDependencies()) { return; } @@ -407,15 +441,12 @@ void GeometryTileWorker::attemptPlacement() { } symbolLayout->prepare(glyphMap, glyphAtlas.positions, - imageMap, imageAtlas.positions, - id, sourceID); + imageMap, imageAtlas.positions); } symbolLayoutsNeedPreparation = false; } - std::unordered_map> buckets; - for (auto& symbolLayout : symbolLayouts) { if (obsolete) { return; @@ -435,11 +466,12 @@ void GeometryTileWorker::attemptPlacement() { } firstLoad = false; - - parent.invoke(&GeometryTile::onPlacement, GeometryTile::PlacementResult { + + parent.invoke(&GeometryTile::onLayout, GeometryTile::LayoutResult { std::move(buckets), + std::move(featureIndex), std::move(glyphAtlasImage), - std::move(iconAtlasImage), + std::move(iconAtlasImage) }, correlationID); } diff --git a/src/mbgl/tile/geometry_tile_worker.hpp b/src/mbgl/tile/geometry_tile_worker.hpp index 0276392679..b5417c8114 100644 --- a/src/mbgl/tile/geometry_tile_worker.hpp +++ b/src/mbgl/tile/geometry_tile_worker.hpp @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include #include @@ -43,8 +45,8 @@ public: private: void coalesced(); - void redoLayout(); - void attemptPlacement(); + void parse(); + void performSymbolLayout(); void coalesce(); @@ -53,6 +55,7 @@ private: void symbolDependenciesChanged(); bool hasPendingSymbolDependencies() const; + bool hasPendingParseResult() const; ActorRef self; ActorRef parent; @@ -62,12 +65,15 @@ private: const std::atomic& obsolete; const MapMode mode; const float pixelRatio; + + std::unique_ptr featureIndex; + std::unordered_map> buckets; enum State { Idle, Coalescing, - NeedLayout, - NeedPlacement + NeedsParse, + NeedsSymbolLayout }; State state = Idle; diff --git a/src/mbgl/tile/tile.cpp b/src/mbgl/tile/tile.cpp index 88db2ba07c..b95944f10e 100644 --- a/src/mbgl/tile/tile.cpp +++ b/src/mbgl/tile/tile.cpp @@ -38,7 +38,11 @@ void Tile::queryRenderedFeatures( const TransformState&, const std::vector&, const RenderedQueryOptions&, - const CollisionIndex&) {} + const mat4&) {} + +float Tile::getQueryPadding(const std::vector&) { + return 0; +} void Tile::querySourceFeatures( std::vector&, diff --git a/src/mbgl/tile/tile.hpp b/src/mbgl/tile/tile.hpp index 23365c6ae3..23d6864205 100644 --- a/src/mbgl/tile/tile.hpp +++ b/src/mbgl/tile/tile.hpp @@ -58,12 +58,14 @@ public: const TransformState&, const std::vector&, const RenderedQueryOptions& options, - const CollisionIndex&); + const mat4& projMatrix); virtual void querySourceFeatures( std::vector& result, const SourceQueryOptions&); + virtual float getQueryPadding(const std::vector&); + void setTriedCache(); // Returns true when the tile source has received a first response, regardless of whether a load @@ -109,11 +111,6 @@ public: // and will have time to finish by the second placement. virtual void performedFadePlacement() {} - // FeatureIndexes are loaded asynchronously, but must be used with a CollisionIndex - // generated from the same data. Calling commitFeatureIndex signals the current - // CollisionIndex is up-to-date and allows us to start using the last loaded FeatureIndex - virtual void commitFeatureIndex() {} - void dumpDebugLogs() const; const OverscaledTileID id; diff --git a/src/mbgl/util/color.cpp b/src/mbgl/util/color.cpp index c8145d36e7..55f1b65436 100644 --- a/src/mbgl/util/color.cpp +++ b/src/mbgl/util/color.cpp @@ -23,11 +23,25 @@ optional Color::parse(const std::string& s) { } std::string Color::stringify() const { + std::array array = toArray(); return "rgba(" + - util::toString(r * 255 / a) + "," + - util::toString(g * 255 / a) + "," + - util::toString(b * 255 / a) + "," + - util::toString(a) + ")"; + util::toString(array[0]) + "," + + util::toString(array[1]) + "," + + util::toString(array[2]) + "," + + util::toString(array[3]) + ")"; +} + +std::array Color::toArray() const { + if (a == 0) { + return {{ 0, 0, 0, 0 }}; + } else { + return {{ + r * 255 / a, + g * 255 / a, + b * 255 / a, + a, + }}; + } } } // namespace mbgl diff --git a/src/mbgl/util/i18n.cpp b/src/mbgl/util/i18n.cpp index 1fc13bfb7d..5530796915 100644 --- a/src/mbgl/util/i18n.cpp +++ b/src/mbgl/util/i18n.cpp @@ -1,4 +1,5 @@ -#include "i18n.hpp" +#include +#include #include #include @@ -65,7 +66,7 @@ DEFINE_IS_IN_UNICODE_BLOCK(UnifiedCanadianAboriginalSyllabics, 0x1400, 0x167F) // DEFINE_IS_IN_UNICODE_BLOCK(Hanunoo, 0x1720, 0x173F) // DEFINE_IS_IN_UNICODE_BLOCK(Buhid, 0x1740, 0x175F) // DEFINE_IS_IN_UNICODE_BLOCK(Tagbanwa, 0x1760, 0x177F) -// DEFINE_IS_IN_UNICODE_BLOCK(Khmer, 0x1780, 0x17FF) +DEFINE_IS_IN_UNICODE_BLOCK(Khmer, 0x1780, 0x17FF) // DEFINE_IS_IN_UNICODE_BLOCK(Mongolian, 0x1800, 0x18AF) DEFINE_IS_IN_UNICODE_BLOCK(UnifiedCanadianAboriginalSyllabicsExtended, 0x18B0, 0x18FF) // DEFINE_IS_IN_UNICODE_BLOCK(Limbu, 0x1900, 0x194F) @@ -581,6 +582,38 @@ std::u16string verticalizePunctuation(const std::u16string& input) { char16_t verticalizePunctuation(char16_t chr) { return verticalPunctuation.count(chr) ? verticalPunctuation.at(chr) : 0; } + +bool charInSupportedScript(char16_t chr) { + // This is a rough heuristic: whether we "can render" a script + // actually depends on the properties of the font being used + // and whether differences from the ideal rendering are considered + // semantically significant. + + // Even in Latin script, we "can't render" combinations such as the fi + // ligature, but we don't consider that semantically significant.n false; + if ((chr >= 0x0900 && chr <= 0x0DFF) || + // Main blocks for Indic scripts and Sinhala + (chr >= 0x0F00 && chr <= 0x109F) || + // Main blocks for Tibetan and Myanmar + isInKhmer(chr)) { + // These blocks cover common scripts that require + // complex text shaping, based on unicode script metadata: + // http://www.unicode.org/repos/cldr/trunk/common/properties/scriptMetadata.txt + // where "Web Rank <= 32" "Shaping Required = YES" + return false; + } + return true; +} + +bool isStringInSupportedScript(const std::string& input) { + auto u16string = util::utf8_to_utf16::convert(input); + for (char16_t chr : u16string) { + if (!charInSupportedScript(chr)) { + return false; + } + } + return true; +} } // namespace i18n } // namespace util diff --git a/src/mbgl/util/i18n.hpp b/src/mbgl/util/i18n.hpp index b3960c743c..a74215a134 100644 --- a/src/mbgl/util/i18n.hpp +++ b/src/mbgl/util/i18n.hpp @@ -72,6 +72,8 @@ std::u16string verticalizePunctuation(const std::u16string& input); @return The character’s specialized vertical form; 0 if not applicable. */ char16_t verticalizePunctuation(char16_t chr); + +bool isStringInSupportedScript(const std::string& input); } // namespace i18n } // namespace util diff --git a/src/mbgl/util/intersection_tests.cpp b/src/mbgl/util/intersection_tests.cpp index e6ce245c0e..780fce98f9 100644 --- a/src/mbgl/util/intersection_tests.cpp +++ b/src/mbgl/util/intersection_tests.cpp @@ -82,11 +82,16 @@ bool lineIntersectsBufferedLine(const GeometryCoordinates& lineA, const Geometry return false; } +bool polygonIntersectsBufferedPoint(const GeometryCoordinates& polygon, const GeometryCoordinate& point, float radius) { + if (polygonContainsPoint(polygon, point)) return true; + if (pointIntersectsBufferedLine(point, polygon, radius)) return true; + return false; +} + bool polygonIntersectsBufferedMultiPoint(const GeometryCoordinates& polygon, const GeometryCollection& rings, float radius) { for (auto& ring : rings) { for (auto& point : ring) { - if (polygonContainsPoint(polygon, point)) return true; - if (pointIntersectsBufferedLine(point, polygon, radius)) return true; + if (polygonIntersectsBufferedPoint(polygon, point, radius)) return true; } } return false; diff --git a/src/mbgl/util/intersection_tests.hpp b/src/mbgl/util/intersection_tests.hpp index 5bcb29c767..c105fe4dd0 100644 --- a/src/mbgl/util/intersection_tests.hpp +++ b/src/mbgl/util/intersection_tests.hpp @@ -9,6 +9,7 @@ bool polygonIntersectsBufferedMultiPoint(const GeometryCoordinates&, const Geome bool polygonIntersectsBufferedMultiLine(const GeometryCoordinates&, const GeometryCollection&, float radius); bool polygonIntersectsPolygon(const GeometryCoordinates&, const GeometryCoordinates&); bool polygonIntersectsMultiPolygon(const GeometryCoordinates&, const GeometryCollection&); +bool polygonIntersectsBufferedPoint(const GeometryCoordinates& polygon, const GeometryCoordinate& point, float radius); } // namespace util } // namespace mbgl diff --git a/src/mbgl/util/throttler.cpp b/src/mbgl/util/throttler.cpp deleted file mode 100644 index 910810ce2f..0000000000 --- a/src/mbgl/util/throttler.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include - -namespace mbgl { -namespace util { - -Throttler::Throttler(Duration frequency_, std::function&& function_) - : frequency(frequency_) - , function(std::move(function_)) - , pendingInvocation(false) - , lastInvocation(TimePoint::min()) -{} - -void Throttler::invoke() { - if (pendingInvocation) { - return; - } - - Duration timeToNextInvocation = lastInvocation == TimePoint::min() - ? Duration::zero() - : (lastInvocation + frequency) - Clock::now(); - - if (timeToNextInvocation <= Duration::zero()) { - lastInvocation = Clock::now(); - function(); - } else { - pendingInvocation = true; - timer.start(timeToNextInvocation, Duration::zero(), [this]{ - pendingInvocation = false; - lastInvocation = Clock::now(); - function(); - }); - } -} - -} // namespace util -} // namespace mbgl diff --git a/src/mbgl/util/throttler.hpp b/src/mbgl/util/throttler.hpp deleted file mode 100644 index 175de7ccaf..0000000000 --- a/src/mbgl/util/throttler.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#include -#include - -namespace mbgl { -namespace util { - -class Throttler { -public: - Throttler(Duration frequency, std::function&& function); - - void invoke(); -private: - Duration frequency; - std::function function; - - Timer timer; - bool pendingInvocation; - TimePoint lastInvocation; -}; - -} // namespace util -} // namespace mbgl diff --git a/src/mbgl/util/tile_cover.cpp b/src/mbgl/util/tile_cover.cpp index 39b562d811..488e6b88ce 100644 --- a/src/mbgl/util/tile_cover.cpp +++ b/src/mbgl/util/tile_cover.cpp @@ -2,13 +2,18 @@ #include #include #include +#include +#include #include +#include namespace mbgl { namespace { +using ScanLine = const std::function; + // Taken from polymaps src/Layer.js // https://github.com/simplegeo/polymaps/blob/master/src/Layer.js#L333-L383 struct edge { @@ -27,8 +32,6 @@ struct edge { } }; -using ScanLine = const std::function; - // scan-line conversion static void scanSpans(edge e0, edge e1, int32_t ymin, int32_t ymax, ScanLine scanLine) { double y0 = ::fmax(ymin, std::floor(e1.y0)); @@ -147,11 +150,11 @@ std::vector tileCover(const LatLngBounds& bounds_, int32_t z) { { std::min(bounds_.north(), util::LATITUDE_MAX), bounds_.east() }); return tileCover( - TileCoordinate::fromLatLng(z, bounds.northwest()).p, - TileCoordinate::fromLatLng(z, bounds.northeast()).p, - TileCoordinate::fromLatLng(z, bounds.southeast()).p, - TileCoordinate::fromLatLng(z, bounds.southwest()).p, - TileCoordinate::fromLatLng(z, bounds.center()).p, + Projection::project(bounds.northwest(), z), + Projection::project(bounds.northeast(), z), + Projection::project(bounds.southeast(), z), + Projection::project(bounds.southwest(), z), + Projection::project(bounds.center(), z), z); } @@ -169,25 +172,80 @@ std::vector tileCover(const TransformState& state, int32_t z) { z); } +std::vector tileCover(const Geometry& geometry, int32_t z) { + std::vector result; + TileCover tc(geometry, z, true); + while (tc.hasNext()) { + result.push_back(*tc.next()); + }; + + return result; +} + // Taken from https://github.com/mapbox/sphericalmercator#xyzbbox-zoom-tms_style-srs // Computes the projected tiles for the lower left and upper right points of the bounds // and uses that to compute the tile cover count -uint64_t tileCount(const LatLngBounds& bounds, uint8_t zoom, uint16_t tileSize_){ - - auto sw = Projection::project(bounds.southwest().wrapped(), zoom, tileSize_); - auto ne = Projection::project(bounds.northeast().wrapped(), zoom, tileSize_); - - auto x1 = floor(sw.x/ tileSize_); - auto x2 = floor((ne.x - 1) / tileSize_); - auto y1 = floor(sw.y/ tileSize_); - auto y2 = floor((ne.y - 1) / tileSize_); - - auto minX = ::fmax(std::min(x1, x2), 0); - auto maxX = std::max(x1, x2); - auto minY = (std::pow(2, zoom) - 1) - std::max(y1, y2); - auto maxY = (std::pow(2, zoom) - 1) - ::fmax(std::min(y1, y2), 0); - - return (maxX - minX + 1) * (maxY - minY + 1); +uint64_t tileCount(const LatLngBounds& bounds, uint8_t zoom){ + if (zoom == 0) { + return 1; + } + auto sw = Projection::project(bounds.southwest(), zoom); + auto ne = Projection::project(bounds.northeast(), zoom); + auto maxTile = std::pow(2.0, zoom); + auto x1 = floor(sw.x); + auto x2 = ceil(ne.x) - 1; + auto y1 = util::clamp(floor(sw.y), 0.0, maxTile - 1); + auto y2 = util::clamp(floor(ne.y), 0.0, maxTile - 1); + + auto dx = x1 > x2 ? (maxTile - x1) + x2 : x2 - x1; + auto dy = y1 - y2; + return (dx + 1) * (dy + 1); +} + +uint64_t tileCount(const Geometry& geometry, uint8_t z) { + uint64_t tileCount = 0; + + TileCover tc(geometry, z, true); + while (tc.next()) { + tileCount++; + }; + return tileCount; +} + +TileCover::TileCover(const LatLngBounds&bounds_, int32_t z) { + LatLngBounds bounds = LatLngBounds::hull( + { std::max(bounds_.south(), -util::LATITUDE_MAX), bounds_.west() }, + { std::min(bounds_.north(), util::LATITUDE_MAX), bounds_.east() }); + + if (bounds.isEmpty() || + bounds.south() > util::LATITUDE_MAX || + bounds.north() < -util::LATITUDE_MAX) { + bounds = LatLngBounds::world(); + } + + auto sw = Projection::project(bounds.southwest(), z); + auto ne = Projection::project(bounds.northeast(), z); + auto se = Projection::project(bounds.southeast(), z); + auto nw = Projection::project(bounds.northwest(), z); + + Polygon p({ {sw, nw, ne, se, sw} }); + impl = std::make_unique(z, p, false); +} + +TileCover::TileCover(const Geometry& geom, int32_t z, bool project/* = true*/) + : impl( std::make_unique(z, geom, project)) { +} + +TileCover::~TileCover() { + +} + +optional TileCover::next() { + return impl->next(); +} + +bool TileCover::hasNext() { + return impl->hasNext(); } } // namespace util diff --git a/src/mbgl/util/tile_cover.hpp b/src/mbgl/util/tile_cover.hpp index b2098b59b8..c953d764d2 100644 --- a/src/mbgl/util/tile_cover.hpp +++ b/src/mbgl/util/tile_cover.hpp @@ -2,9 +2,11 @@ #include #include -#include +#include +#include #include +#include namespace mbgl { @@ -13,13 +15,31 @@ class LatLngBounds; namespace util { +// Helper class to stream tile-cover results per row +class TileCover { +public: + TileCover(const LatLngBounds&, int32_t z); + // When project == true, projects the geometry points to tile coordinates + TileCover(const Geometry&, int32_t z, bool project = true); + ~TileCover(); + + optional next(); + bool hasNext(); + +private: + class Impl; + std::unique_ptr impl; +}; + int32_t coveringZoomLevel(double z, style::SourceType type, uint16_t tileSize); std::vector tileCover(const TransformState&, int32_t z); std::vector tileCover(const LatLngBounds&, int32_t z); +std::vector tileCover(const Geometry&, int32_t z); // Compute only the count of tiles needed for tileCover -uint64_t tileCount(const LatLngBounds&, uint8_t z, uint16_t tileSize); +uint64_t tileCount(const LatLngBounds&, uint8_t z); +uint64_t tileCount(const Geometry&, uint8_t z); } // namespace util } // namespace mbgl diff --git a/src/mbgl/util/tile_cover_impl.cpp b/src/mbgl/util/tile_cover_impl.cpp new file mode 100644 index 0000000000..b3fc07f7dd --- /dev/null +++ b/src/mbgl/util/tile_cover_impl.cpp @@ -0,0 +1,365 @@ +#include +#include + +#include +#include +#include +#include +#include + +namespace mbgl { +namespace util { + +using PointList = std::vector>; + +struct TileSpan { + int32_t xmin, xmax; + bool winding; +}; + + +// Find the first local minimum going forward in the list. +void start_list_on_local_minimum(PointList& points) { + auto prev_pt = std::prev(points.end(), 2); + auto pt = points.begin(); + auto next_pt = std::next(pt); + while (pt != points.end()) { + if ((pt->y <= prev_pt->y) && + (pt->y < next_pt->y)) { + break; + } + prev_pt = pt; + pt++; + next_pt++; + if (next_pt == points.end()) { next_pt = std::next(points.begin()); } + } + //Re-close linear rings with first_pt = last_pt + if (points.back() == points.front()) { + points.pop_back(); + } + std::rotate(points.begin(), pt, points.end()); + points.push_back(*points.begin()); +} + +//Create a bound towards a local maximum point, starting from pt. +Bound create_bound_towards_maximum(PointList& points, PointList::iterator& pt) { + if (std::distance(pt, points.end()) < 2) { return {}; } + if (std::distance(pt, points.end()) == 2) { + Bound bnd; + if (pt->y < std::next(pt)->y) { + std::copy(pt, points.end(), std::back_inserter(bnd.points)); + bnd.winding = true; + } + else { + std::reverse_copy(pt, points.end(), std::back_inserter(bnd.points)); + bnd.winding = false; + } + pt = points.end(); + return bnd; + } + const auto begin = pt; + auto prev_pt = pt == points.begin() ? std::prev(points.end(), 2) : std::prev(pt); + auto next_pt = std::next(pt) == points.end() ? std::next(points.begin()) : std::next(pt); + while (pt != points.end()) { + if ((pt->y >= prev_pt->y) && + (pt->y > next_pt->y )) { + break; + } + prev_pt = pt; + pt++; + next_pt++; + if (next_pt == points.end()) { next_pt = std::next(points.begin()); } + } + + Bound bnd; + if (std::next(pt) == points.end()) { next_pt = points.end(); pt++; }; + bnd.points.reserve(static_cast(std::distance(begin, next_pt))); + std::copy(begin, next_pt, std::back_inserter(bnd.points)); + bnd.winding = true; + return bnd; +} + +//Create a bound towards a local minimum point, starting from pt. +Bound create_bound_towards_minimum(PointList& points, PointList::iterator& pt) { + if (std::distance(pt, points.end()) < 2) { return {}; } + if (std::distance(pt, points.end()) == 2) { + Bound bnd; + if (pt->y < std::next(pt)->y) { + std::copy(pt, points.end(), std::back_inserter(bnd.points)); + bnd.winding = true; + } + else { + std::reverse_copy(pt, points.end(), std::back_inserter(bnd.points)); + bnd.winding = false; + } + pt = points.end(); + return bnd; + } + auto begin = pt; + auto prev_pt = pt == points.begin() ? std::prev(points.end(), 2) : std::prev(pt); + auto next_pt = std::next(pt) == points.end() ? std::next(points.begin()) : std::next(pt); + while (pt != points.end()) { + if ((pt->y <= prev_pt->y) && + (pt->y < next_pt->y)) { + break; + } + prev_pt = pt; + pt++; + next_pt++; + if (next_pt == points.end()) { next_pt = std::next(points.begin()); } + } + + Bound bnd; + if (std::next(pt) == points.end()) { next_pt = points.end(); pt++; }; + bnd.points.reserve(static_cast(std::distance(begin, next_pt))); + //For bounds that start at a max, reverse copy so that all bounds start at a min + std::reverse_copy(begin, next_pt, std::back_inserter(bnd.points)); + bnd.winding = false; + return bnd; +} + +//Build a map of bounds and their starting Y tile coordinate. +void build_bounds_map(PointList& points, uint32_t maxTile, BoundsMap& et, bool closed = false) { + if (points.size() < 2) return; + //While traversing closed rings, start the bounds at a local minimum + if (closed) { + start_list_on_local_minimum(points); + } + + auto pointsIter = points.begin(); + while (pointsIter != points.end()) { + Bound to_max = create_bound_towards_maximum(points, pointsIter); + Bound to_min = create_bound_towards_minimum(points, pointsIter); + + if (to_max.points.size() > 0) { + // Projections may result in values beyond the bounds, clamp to max tile coordinates + const auto y = static_cast(std::floor(clamp(to_max.points.front().y, 0.0, (double)maxTile))); + et[y].push_back(to_max); + } + if (to_min.points.size() > 0) { + const auto y = static_cast(std::floor(clamp(to_min.points.front().y, 0.0, (double)maxTile))); + et[y].push_back(to_min); + } + } + assert(pointsIter == points.end()); +} + +void update_span(TileSpan& xp, double x) { + xp.xmin = std::min(xp.xmin, static_cast(std::floor(x))); + xp.xmax = std::max(xp.xmax, static_cast(std::ceil(x))); +} + +//Build a vector of X tile-coordinates spanned by each bound. +std::vector scan_row(uint32_t y, Bounds& aet) { + std::vector tile_range; + tile_range.reserve(aet.size()); + + for(Bound& b: aet) { + TileSpan xp = { INT_MAX, 0, b.winding }; + double x; + const auto numEdges = b.points.size() - 1; + assert(numEdges >= 1); + while (b.currentPoint < numEdges) { + x = b.interpolate(y); + update_span(xp, x); + + // If this edge ends beyond the current row, find the x-intercept where + // it exits the row + auto& p1 = b.points[b.currentPoint + 1]; + if (p1.y > y+1) { + x = b.interpolate(y+1); + update_span(xp, x); + break; + } else if(b.currentPoint == numEdges - 1) { + // For last edge, consider x-intercept at the end of the edge. + x = p1.x; + update_span(xp, x); + } + b.currentPoint++; + } + tile_range.push_back(xp); + } + // Erase bounds in the active table whose current edge ends inside this row, + // or there are no more edges + auto bound = aet.begin(); + while (bound != aet.end()) { + if ( bound->currentPoint == bound->points.size() - 1 && + bound->points[bound->currentPoint].y <= y+1) { + bound = aet.erase(bound); + } else { + bound++; + } + } + // Sort the X-extents of each crossing bound by x_min, x_max + std::sort(tile_range.begin(), tile_range.end(), [] (TileSpan& a, TileSpan& b) { + return std::tie(a.xmin, a.xmax) < std::tie(b.xmin, b.xmax); + }); + + return tile_range; +} + +struct BuildBoundsMap { + int32_t zoom; + bool project = false; + BuildBoundsMap(int32_t z, bool p): zoom(z), project(p) {} + + void buildTable(const std::vector>& points, BoundsMap& et, bool closed = false) const { + PointList projectedPoints; + if (project) { + projectedPoints.reserve(points.size()); + for(const auto&p : points) { + projectedPoints.push_back( + Projection::project(LatLng{ p.y, p.x }, zoom)); + } + } else { + projectedPoints.insert(projectedPoints.end(), points.begin(), points.end()); + } + build_bounds_map(projectedPoints, 1 << zoom, et, closed); + } + + void buildPolygonTable(const Polygon& polygon, BoundsMap& et) const { + for(const auto&ring : polygon) { + buildTable(ring, et, true); + } + } + BoundsMap operator()(const Point&p) const { + Bound bnd; + auto point = p; + if(project) { + point = Projection::project(LatLng{p.y, p.x}, zoom); + } + bnd.points.insert(bnd.points.end(), 2, point); + bnd.winding = false; + BoundsMap et; + const auto y = static_cast(std::floor(clamp(point.y, 0.0, (double)(1 << zoom)))); + et[y].push_back(bnd); + return et; + } + + BoundsMap operator()(const MultiPoint& points) const { + BoundsMap et; + for (const Point& p: points) { + Bound bnd; + auto point = p; + if(project) { + point = Projection::project(LatLng{p.y, p.x}, zoom); + } + bnd.points.insert(bnd.points.end(), 2, point); + bnd.winding = false; + const auto y = static_cast(std::floor(clamp(point.y, 0.0, (double)(1 << zoom)))); + et[y].push_back(bnd); + } + return et; + } + + BoundsMap operator()(const LineString& lines) const { + BoundsMap et; + buildTable(lines, et); + return et; + } + + BoundsMap operator()(const MultiLineString& lines) const { + BoundsMap et; + for(const auto&line : lines) { + buildTable(line, et); + } + return et; + } + + BoundsMap operator()(const Polygon& polygon) const { + BoundsMap et; + buildPolygonTable(polygon, et); + return et; + } + + BoundsMap operator()(const MultiPolygon& polygons) const { + BoundsMap et; + for(const auto& polygon: polygons) { + buildPolygonTable(polygon, et); + } + return et; + } + + BoundsMap operator()(const mapbox::geometry::geometry_collection&) const { + return {}; + } +}; + +TileCover::Impl::Impl(int32_t z, const Geometry& geom, bool project) + : zoom(z) { + ToFeatureType toFeatureType; + isClosed = apply_visitor(toFeatureType, geom) == FeatureType::Polygon; + + BuildBoundsMap toBoundsMap(z, project); + boundsMap = apply_visitor(toBoundsMap, geom); + if (boundsMap.size() == 0) return; + + //Iniitalize the active edge table, and current row span + currentBounds = boundsMap.begin(); + tileY = 0; + nextRow(); + if (tileXSpans.empty()) return; + tileX = tileXSpans.front().first; +} + +void TileCover::Impl::nextRow() { + // Update AET for next row + if (currentBounds != boundsMap.end()) { + if (activeBounds.size() == 0 && currentBounds->first > tileY) { + //For multi-geoms: use the next row with an edge table starting point + tileY = currentBounds->first; + } + if (tileY == currentBounds->first) { + + std::move(currentBounds->second.begin(), currentBounds->second.end(), std::back_inserter(activeBounds)); + currentBounds++; + } + } + //Scan aet and update currenRange with x_min, x_max pairs + auto xps = util::scan_row(tileY, activeBounds); + if (xps.size() == 0) { + return; + } + + auto x_min = xps[0].xmin; + auto x_max = xps[0].xmax; + int32_t nzRule = xps[0].winding ? 1 : -1; + for (size_t i = 1; i < xps.size(); i++) { + auto xp = xps[i]; + if (!(isClosed && nzRule != 0)) { + if (xp.xmin > x_max && xp.xmax >= x_max) { + tileXSpans.emplace(x_min, x_max); + x_min = xp.xmin; + } + } + nzRule += xp.winding ? 1 : -1; + x_max = std::max(x_min, xp.xmax); + } + tileXSpans.emplace(x_min, x_max); +} + +bool TileCover::Impl::hasNext() const { + return (!tileXSpans.empty() && tileX < tileXSpans.front().second && tileY < (1u << zoom)); +} + +optional TileCover::Impl::next() { + if (!hasNext()) return {}; + + const auto x = tileX; + const auto y = tileY; + tileX++; + if (tileX >= tileXSpans.front().second) { + tileXSpans.pop(); + if(tileXSpans.empty()) { + tileY++; + nextRow(); + } + if (!tileXSpans.empty()) { + tileX = tileXSpans.front().first; + } + } + return UnwrappedTileID(zoom, x, y); +} + +} // namespace util +} // namespace mbgl diff --git a/src/mbgl/util/tile_cover_impl.hpp b/src/mbgl/util/tile_cover_impl.hpp new file mode 100644 index 0000000000..7c16718984 --- /dev/null +++ b/src/mbgl/util/tile_cover_impl.hpp @@ -0,0 +1,90 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include + +namespace mbgl { + +class TransformState; +class LatLngBounds; + +namespace util { + +struct Bound; + +using Bounds = std::vector; +using BoundsMap = std::map; + +// A chain of points from a local minimum to a local maximum. `winding` indicates +// the direction of the original geometry. +struct Bound { + std::vector> points; + size_t currentPoint = 0; + bool winding = false; + + Bound() = default; + Bound(const Bound& rhs) { + points = rhs.points; + currentPoint = rhs.currentPoint; + winding = rhs.winding; + } + Bound& operator=(Bound&& rhs) { + points = std::move(rhs.points); + currentPoint = rhs.currentPoint; + winding = rhs.winding; + return *this; + } + + // Compute the interpolated x coordinate at y for the current edge + double interpolate(uint32_t y) { + const auto& p0 = points[currentPoint]; + const auto& p1 = points[currentPoint + 1]; + + const auto dx = p1.x - p0.x; + const auto dy = p1.y - p0.y; + auto x = p0.x; + if (dx == 0) { + return x; + } else if (dy == 0){ + return y <= p0.y ? p0.x : p1.x; + } + if (y < p0.y) return x; + if (y > p1.y) return p1.x; + x = (dx / dy) * (y - p0.y) + p0.x; + return x; + } +}; + +class TileCover::Impl { +public: + Impl(int32_t z, const Geometry& geom, bool project = true); + ~Impl() = default; + + optional next(); + bool hasNext() const; + +private: + using TileSpans = std::queue>; + + void nextRow(); + + const int32_t zoom; + bool isClosed; + + BoundsMap boundsMap; + BoundsMap::iterator currentBounds; + // List of bounds that begin at or before `tileY` + Bounds activeBounds; + + TileSpans tileXSpans; + uint32_t tileY; + int32_t tileX; +}; + +} // namespace util +} // namespace mbgl -- cgit v1.2.1