summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThiago Marcos P. Santos <tmpsantos@gmail.com>2018-03-08 23:56:42 +0200
committerThiago Marcos P. Santos <tmpsantos@gmail.com>2018-05-04 17:46:17 +0300
commit0eab78d63a573b5fa2e5046e44cbd0109df8cbc5 (patch)
tree790d3aa3095a5274cc6042ab2fbe5def3e7d43c7
parent4ad1413d94c13d52ee679de1fe5fe720cf32e4fe (diff)
downloadqtlocation-mapboxgl-0eab78d63a573b5fa2e5046e44cbd0109df8cbc5.tar.gz
Bump Mapbox GL Native
Bump version. mapbox-gl-native @ 27b21363e62c105db0b040b4c5a5ef31170ebd30
-rw-r--r--include/mbgl/renderer/renderer_backend.hpp6
-rw-r--r--include/mbgl/style/conversion/data_driven_property_value.hpp33
-rw-r--r--include/mbgl/style/conversion/expression.hpp39
-rw-r--r--include/mbgl/style/conversion/heatmap_color_property_value.hpp7
-rw-r--r--include/mbgl/style/conversion/property_value.hpp9
-rw-r--r--include/mbgl/style/data_driven_property_value.hpp9
-rw-r--r--include/mbgl/style/expression/length.hpp32
-rw-r--r--include/mbgl/style/expression/let.hpp3
-rw-r--r--include/mbgl/style/expression/literal.hpp4
-rw-r--r--include/mbgl/style/expression/parsing_context.hpp34
-rw-r--r--include/mbgl/style/filter.hpp22
-rw-r--r--include/mbgl/style/filter_evaluator.hpp259
-rw-r--r--include/mbgl/style/function/camera_function.hpp11
-rw-r--r--include/mbgl/style/function/composite_function.hpp13
-rw-r--r--include/mbgl/style/function/source_function.hpp15
-rw-r--r--include/mbgl/style/layers/custom_layer.hpp89
-rw-r--r--include/mbgl/util/color.hpp2
-rw-r--r--include/mbgl/util/projection.hpp16
-rw-r--r--mapbox-gl-native.pro7
-rw-r--r--platform/default/mbgl/storage/offline.cpp2
-rw-r--r--platform/default/mbgl/util/default_styles.hpp3
-rw-r--r--platform/default/sqlite3.cpp11
-rw-r--r--platform/qt/include/qmapbox.hpp22
-rw-r--r--platform/qt/include/qmapboxgl.hpp5
-rw-r--r--platform/qt/src/qmapbox.cpp22
-rw-r--r--platform/qt/src/qmapboxgl.cpp80
-rw-r--r--src/mbgl/annotation/render_annotation_source.cpp4
-rw-r--r--src/mbgl/annotation/render_annotation_source.hpp2
-rw-r--r--src/mbgl/geometry/feature_index.cpp109
-rw-r--r--src/mbgl/geometry/feature_index.hpp59
-rw-r--r--src/mbgl/gl/program.hpp5
-rw-r--r--src/mbgl/layout/symbol_instance.cpp4
-rw-r--r--src/mbgl/layout/symbol_layout.cpp22
-rw-r--r--src/mbgl/layout/symbol_layout.hpp9
-rw-r--r--src/mbgl/layout/symbol_projection.cpp6
-rw-r--r--src/mbgl/map/map.cpp6
-rw-r--r--src/mbgl/map/transform_state.cpp13
-rw-r--r--src/mbgl/map/transform_state.hpp1
-rw-r--r--src/mbgl/programs/symbol_program.hpp4
-rw-r--r--src/mbgl/renderer/buckets/symbol_bucket.cpp6
-rw-r--r--src/mbgl/renderer/buckets/symbol_bucket.hpp5
-rw-r--r--src/mbgl/renderer/layers/render_background_layer.cpp1
-rw-r--r--src/mbgl/renderer/layers/render_circle_layer.cpp66
-rw-r--r--src/mbgl/renderer/layers/render_circle_layer.hpp3
-rw-r--r--src/mbgl/renderer/layers/render_custom_layer.cpp33
-rw-r--r--src/mbgl/renderer/layers/render_custom_layer.hpp3
-rw-r--r--src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp7
-rw-r--r--src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp3
-rw-r--r--src/mbgl/renderer/layers/render_fill_layer.cpp7
-rw-r--r--src/mbgl/renderer/layers/render_fill_layer.hpp3
-rw-r--r--src/mbgl/renderer/layers/render_heatmap_layer.cpp6
-rw-r--r--src/mbgl/renderer/layers/render_heatmap_layer.hpp3
-rw-r--r--src/mbgl/renderer/layers/render_line_layer.cpp7
-rw-r--r--src/mbgl/renderer/layers/render_line_layer.hpp3
-rw-r--r--src/mbgl/renderer/render_layer.hpp5
-rw-r--r--src/mbgl/renderer/render_source.hpp2
-rw-r--r--src/mbgl/renderer/renderer_impl.cpp58
-rw-r--r--src/mbgl/renderer/renderer_impl.hpp8
-rw-r--r--src/mbgl/renderer/sources/render_custom_geometry_source.cpp4
-rw-r--r--src/mbgl/renderer/sources/render_custom_geometry_source.hpp2
-rw-r--r--src/mbgl/renderer/sources/render_geojson_source.cpp4
-rw-r--r--src/mbgl/renderer/sources/render_geojson_source.hpp2
-rw-r--r--src/mbgl/renderer/sources/render_image_source.cpp2
-rw-r--r--src/mbgl/renderer/sources/render_image_source.hpp2
-rw-r--r--src/mbgl/renderer/sources/render_raster_dem_source.cpp2
-rw-r--r--src/mbgl/renderer/sources/render_raster_dem_source.hpp2
-rw-r--r--src/mbgl/renderer/sources/render_raster_source.cpp2
-rw-r--r--src/mbgl/renderer/sources/render_raster_source.hpp2
-rw-r--r--src/mbgl/renderer/sources/render_vector_source.cpp4
-rw-r--r--src/mbgl/renderer/sources/render_vector_source.hpp2
-rw-r--r--src/mbgl/renderer/tile_pyramid.cpp13
-rw-r--r--src/mbgl/renderer/tile_pyramid.hpp2
-rw-r--r--src/mbgl/shaders/collision_box.cpp5
-rw-r--r--src/mbgl/shaders/line.cpp3
-rw-r--r--src/mbgl/shaders/line_pattern.cpp10
-rw-r--r--src/mbgl/shaders/symbol_icon.cpp7
-rw-r--r--src/mbgl/shaders/symbol_sdf.cpp7
-rw-r--r--src/mbgl/style/conversion/filter.cpp68
-rw-r--r--src/mbgl/style/conversion/stringify.hpp135
-rw-r--r--src/mbgl/style/conversion/tileset.cpp2
-rw-r--r--src/mbgl/style/expression/at.cpp14
-rw-r--r--src/mbgl/style/expression/compound_expression.cpp25
-rw-r--r--src/mbgl/style/expression/interpolate.cpp6
-rw-r--r--src/mbgl/style/expression/length.cpp66
-rw-r--r--src/mbgl/style/expression/parsing_context.cpp72
-rw-r--r--src/mbgl/style/expression/value.cpp9
-rw-r--r--src/mbgl/style/filter.cpp13
-rw-r--r--src/mbgl/style/filter_evaluator.cpp225
-rw-r--r--src/mbgl/style/layers/background_layer.cpp2
-rw-r--r--src/mbgl/style/layers/circle_layer.cpp2
-rw-r--r--src/mbgl/style/layers/custom_layer.cpp16
-rw-r--r--src/mbgl/style/layers/custom_layer_impl.cpp12
-rw-r--r--src/mbgl/style/layers/custom_layer_impl.hpp14
-rw-r--r--src/mbgl/style/layers/fill_extrusion_layer.cpp2
-rw-r--r--src/mbgl/style/layers/fill_layer.cpp2
-rw-r--r--src/mbgl/style/layers/heatmap_layer.cpp2
-rw-r--r--src/mbgl/style/layers/hillshade_layer.cpp2
-rw-r--r--src/mbgl/style/layers/line_layer.cpp2
-rw-r--r--src/mbgl/style/layers/raster_layer.cpp2
-rw-r--r--src/mbgl/style/layers/symbol_layer.cpp2
-rw-r--r--src/mbgl/text/collision_index.cpp61
-rw-r--r--src/mbgl/text/collision_index.hpp5
-rw-r--r--src/mbgl/text/cross_tile_symbol_index.cpp6
-rw-r--r--src/mbgl/text/placement.cpp45
-rw-r--r--src/mbgl/text/placement.hpp20
-rw-r--r--src/mbgl/tile/custom_geometry_tile.cpp2
-rw-r--r--src/mbgl/tile/geojson_tile.cpp2
-rw-r--r--src/mbgl/tile/geometry_tile.cpp91
-rw-r--r--src/mbgl/tile/geometry_tile.hpp47
-rw-r--r--src/mbgl/tile/geometry_tile_worker.cpp180
-rw-r--r--src/mbgl/tile/geometry_tile_worker.hpp14
-rw-r--r--src/mbgl/tile/tile.cpp6
-rw-r--r--src/mbgl/tile/tile.hpp9
-rw-r--r--src/mbgl/util/color.cpp22
-rw-r--r--src/mbgl/util/i18n.cpp37
-rw-r--r--src/mbgl/util/i18n.hpp2
-rw-r--r--src/mbgl/util/intersection_tests.cpp9
-rw-r--r--src/mbgl/util/intersection_tests.hpp1
-rw-r--r--src/mbgl/util/throttler.cpp36
-rw-r--r--src/mbgl/util/throttler.hpp22
-rw-r--r--src/mbgl/util/tile_cover.cpp104
-rw-r--r--src/mbgl/util/tile_cover.hpp24
-rw-r--r--src/mbgl/util/tile_cover_impl.cpp365
-rw-r--r--src/mbgl/util/tile_cover_impl.hpp90
124 files changed, 2042 insertions, 1158 deletions
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 <mbgl/style/conversion.hpp>
#include <mbgl/style/conversion/constant.hpp>
#include <mbgl/style/conversion/function.hpp>
-#include <mbgl/style/conversion/expression.hpp>
#include <mbgl/style/expression/is_expression.hpp>
#include <mbgl/style/expression/is_constant.hpp>
#include <mbgl/style/expression/find_zoom_curve.hpp>
+#include <mbgl/style/expression/literal.hpp>
+#include <mbgl/style/expression/value.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
#include <unordered_set>
@@ -20,24 +22,35 @@ template <class T>
struct Converter<DataDrivenPropertyValue<T>> {
optional<DataDrivenPropertyValue<T>> operator()(const Convertible& value, Error& error) const {
+ using namespace mbgl::style::expression;
+
if (isUndefined(value)) {
return DataDrivenPropertyValue<T>();
- } else if (expression::isExpression(value)) {
- optional<std::unique_ptr<Expression>> expression = convert<std::unique_ptr<Expression>>(
- value,
- error,
- valueTypeToExpressionType<T>());
-
+ } else if (isExpression(value)) {
+ ParsingContext ctx(valueTypeToExpressionType<T>());
+ 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<T>(CameraFunction<T>(std::move(*expression)));
- } else if (isZoomConstant(**expression)) {
+ } else if (!featureConstant && zoomConstant) {
return DataDrivenPropertyValue<T>(SourceFunction<T>(std::move(*expression)));
- } else {
+ } else if (!featureConstant && !zoomConstant) {
return DataDrivenPropertyValue<T>(CompositeFunction<T>(std::move(*expression)));
+ } else {
+ auto literal = dynamic_cast<Literal*>(expression->get());
+ assert(literal);
+ optional<T> constant = fromExpressionValue<T>(literal->getValue());
+ if (!constant) {
+ return {};
+ }
+ return DataDrivenPropertyValue<T>(*constant);
}
} else if (!isObject(value)) {
optional<T> constant = convert<T>(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 <mbgl/style/expression/parsing_context.hpp>
-#include <mbgl/style/expression/type.hpp>
-#include <mbgl/style/conversion.hpp>
-
-#include <memory>
-
-namespace mbgl {
-namespace style {
-namespace conversion {
-
-using namespace mbgl::style::expression;
-
-template<> struct Converter<std::unique_ptr<Expression>> {
- optional<std::unique_ptr<Expression>> operator()(const Convertible& value, Error& error, type::Type expected) const {
- ParsingContext ctx(optional<type::Type> {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 <mbgl/style/conversion.hpp>
#include <mbgl/style/conversion/constant.hpp>
#include <mbgl/style/conversion/function.hpp>
-#include <mbgl/style/conversion/expression.hpp>
#include <mbgl/style/expression/value.hpp>
#include <mbgl/style/expression/is_constant.hpp>
#include <mbgl/style/expression/is_expression.hpp>
#include <mbgl/style/expression/find_zoom_curve.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
namespace mbgl {
namespace style {
@@ -17,11 +17,14 @@ namespace conversion {
template <>
struct Converter<HeatmapColorPropertyValue> {
optional<HeatmapColorPropertyValue> operator()(const Convertible& value, Error& error) const {
+ using namespace mbgl::style::expression;
if (isUndefined(value)) {
return HeatmapColorPropertyValue();
} else if (isExpression(value)) {
- optional<std::unique_ptr<Expression>> expression = convert<std::unique_ptr<Expression>>(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 <mbgl/style/conversion.hpp>
#include <mbgl/style/conversion/constant.hpp>
#include <mbgl/style/conversion/function.hpp>
-#include <mbgl/style/conversion/expression.hpp>
#include <mbgl/style/expression/value.hpp>
#include <mbgl/style/expression/is_constant.hpp>
#include <mbgl/style/expression/is_expression.hpp>
#include <mbgl/style/expression/find_zoom_curve.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
namespace mbgl {
namespace style {
@@ -17,13 +17,18 @@ namespace conversion {
template <class T>
struct Converter<PropertyValue<T>> {
optional<PropertyValue<T>> operator()(const Convertible& value, Error& error) const {
+ using namespace mbgl::style::expression;
+
if (isUndefined(value)) {
return PropertyValue<T>();
} else if (isExpression(value)) {
- optional<std::unique_ptr<Expression>> expression = convert<std::unique_ptr<Expression>>(value, error, valueTypeToExpressionType<T>());
+ ParsingContext ctx(valueTypeToExpressionType<T>());
+ ParseResult expression = ctx.parseLayerPropertyExpression(value);
if (!expression) {
+ error = { ctx.getCombinedErrors() };
return {};
}
+
if (isFeatureConstant(**expression)) {
return { CameraFunction<T>(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<CameraFunction<T>>() && !value.template is<CompositeFunction<T>>();
}
+ bool isExpression() const {
+ return value.match(
+ [] (const Undefined&) { return false; },
+ [] (const T&) { return false; },
+ [] (const CameraFunction<T>& fn) { return fn.isExpression; },
+ [] (const SourceFunction<T>& fn) { return fn.isExpression; },
+ [] (const CompositeFunction<T>& fn) { return fn.isExpression; });
+ }
+
template <class... Ts>
auto match(Ts&&... ts) const {
return value.match(std::forward<Ts>(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 <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+
+#include <memory>
+#include <vector>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+class Length : public Expression {
+public:
+ Length(std::unique_ptr<Expression> input);
+
+ static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);
+
+ EvaluationResult evaluate(const EvaluationContext& params) const override;
+ void eachChild(const std::function<void(const Expression&)>& visit) const override;
+ bool operator==(const Expression& e) const override;
+ std::vector<optional<Value>> possibleOutputs() const override;
+ std::string getOperator() const override { return "length"; }
+
+private:
+ std::unique_ptr<Expression> 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<Expression>& getBoundExpression() const { return value; }
+
private:
std::string name;
std::shared_ptr<Expression> 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<optional<Value>> 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<std::vector<ParsingError>>()) {}
ParsingContext(std::string key_) : key(std::move(key_)), errors(std::make_shared<std::vector<ParsingError>>()) {}
- explicit ParsingContext(optional<type::Type> expected_)
+ explicit ParsingContext(type::Type expected_)
: expected(std::move(expected_)),
errors(std::make_shared<std::vector<ParsingError>>())
{}
@@ -67,6 +67,7 @@ public:
std::string getKey() const { return key; }
optional<type::Type> getExpected() const { return expected; }
const std::vector<ParsingError>& 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<type::Type> expected;
std::shared_ptr<detail::Scope> 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 <mbgl/util/variant.hpp>
#include <mbgl/util/feature.hpp>
#include <mbgl/util/geometry.hpp>
+#include <mbgl/style/expression/expression.hpp>
#include <string>
#include <vector>
@@ -232,6 +233,15 @@ public:
return true;
}
};
+
+class ExpressionFilter {
+public:
+ std::shared_ptr<const expression::Expression> 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 <class GeometryTileFeature>
- bool operator()(const GeometryTileFeature&) const;
-
- template <class PropertyAccessor>
- bool operator()(FeatureType type, optional<FeatureIdentifier> 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 PropertyAccessor>
class FilterEvaluator {
public:
- const FeatureType featureType;
- const optional<FeatureIdentifier> 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<Value> actual = propertyAccessor(filter.key);
- return actual && equal(*actual, filter.value);
- }
-
- bool operator()(const NotEqualsFilter& filter) const {
- optional<Value> actual = propertyAccessor(filter.key);
- return !actual || !equal(*actual, filter.value);
- }
-
- bool operator()(const LessThanFilter& filter) const {
- optional<Value> 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<Value> 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<Value> 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<Value> 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<Value> 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<Value> 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 <class Op>
- struct Comparator {
- const Op& op;
-
- template <class T>
- bool operator()(const T& lhs, const T& rhs) const {
- return op(lhs, rhs);
- }
-
- template <class T0, class T1>
- auto operator()(const T0& lhs, const T1& rhs) const
- -> typename std::enable_if_t<std::is_arithmetic<T0>::value && !std::is_same<T0, bool>::value &&
- std::is_arithmetic<T1>::value && !std::is_same<T1, bool>::value, bool> {
- return op(double(lhs), double(rhs));
- }
-
- template <class T0, class T1>
- auto operator()(const T0&, const T1&) const
- -> typename std::enable_if_t<!std::is_arithmetic<T0>::value || std::is_same<T0, bool>::value ||
- !std::is_arithmetic<T1>::value || std::is_same<T1, bool>::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<Value>&,
- const std::vector<Value>&) 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 <class Op>
- bool compare(const Value& lhs, const Value& rhs, const Op& op) const {
- return Value::binary_visit(lhs, rhs, Comparator<Op> { 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<Value> {
- auto it = feature.properties.find(key);
- if (it == feature.properties.end())
- return {};
- return it->second;
- });
-}
-
-template <class GeometryTileFeature>
-bool Filter::operator()(const GeometryTileFeature& feature) const {
- return operator()(feature.getType(), feature.getID(), [&] (const auto& key) { return feature.getValue(key); });
-}
-
-template <class PropertyAccessor>
-bool Filter::operator()(FeatureType type, optional<FeatureIdentifier> id, PropertyAccessor accessor) const {
- return FilterBase::visit(*this, FilterEvaluator<PropertyAccessor> { 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<T>>>;
CameraFunction(std::unique_ptr<expression::Expression> 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::Expression> expression;
const variant<const expression::InterpolateBase*, const expression::Step*> 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<T>>>;
CompositeFunction(std::unique_ptr<expression::Expression> 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<T> defaultValue_ = {})
- : property(std::move(property_)),
- stops(std::move(stops_)),
+ CompositeFunction(const std::string& property, const Stops& stops, optional<T> 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<T> defaultValue;
bool useIntegerZoom = false;
+ bool isExpression;
private:
+ optional<T> defaultValue;
std::shared_ptr<expression::Expression> expression;
const variant<const expression::InterpolateBase*, const expression::Step*> 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<T>>>;
SourceFunction(std::unique_ptr<expression::Expression> 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<T> defaultValue_ = {})
- : property(std::move(property_)),
- stops(std::move(stops_)),
+ SourceFunction(const std::string& property, const Stops& stops, optional<T> defaultValue_ = {})
+ : isExpression(false),
defaultValue(std::move(defaultValue_)),
expression(stops.match([&] (const IdentityStops<T>&) {
return expression::Convert::fromIdentityFunction(expression::valueTypeToExpressionType<T>(), 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<T> defaultValue;
-
private:
+ optional<T> defaultValue;
std::shared_ptr<expression::Expression> 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 <mbgl/style/layer.hpp>
+#include <array>
+
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<double, 16> 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<CustomLayerHost> 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 <cassert>
#include <string>
+#include <array>
namespace mbgl {
@@ -37,6 +38,7 @@ public:
static optional<Color> parse(const std::string&);
std::string stringify() const;
+ std::array<double, 4> 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<double> project(const LatLng& latLng, uint8_t zoom) {
- return project_(latLng, std::pow(2.0, zoom));
+ //Returns point on tile
+ static Point<double> project(const LatLng& latLng, int32_t zoom) {
+ return project_(latLng, 1 << zoom);
}
static LatLng unproject(const Point<double>& 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<double> project(const LatLng& point, uint8_t zoom, uint16_t tileSize) {
- const double t2z = tileSize * std::pow(2, zoom);
- Point<double> 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<double> project_(const LatLng& latLng, double worldSize) {
return Point<double> {
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<uint8_t> 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<QMapbox::CustomLayerHostInterface>& host,
const QString& before = QString());
void addLayer(const QVariantMap &params, 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<std::shared_ptr<mbgl::util::RunLoop>> loop;
std::shared_ptr<mbgl::DefaultFileSource> sharedDefaultFileSource(
const std::string& cachePath, const std::string& assetRoot, uint64_t maximumCacheSize) {
- static std::weak_ptr<mbgl::DefaultFileSource> weak;
- auto fs = weak.lock();
+ static std::mutex mutex;
+ static std::unordered_map<std::string, std::weak_ptr<mbgl::DefaultFileSource>> fileSources;
- if (!fs) {
- weak = fs = std::make_shared<mbgl::DefaultFileSource>(
- cachePath, assetRoot, maximumCacheSize);
+ std::lock_guard<std::mutex> 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<mbgl::DefaultFileSource>(
+ cachePath, assetRoot, maximumCacheSize);
+
+ fileSources[cachePath] = newFileSource;
+
+ return newFileSource;
}
// Conversion helper functions.
@@ -141,10 +159,9 @@ std::unique_ptr<mbgl::style::Image> 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<QMapbox::CustomLayerHostInterface>& host,
const QString& before)
{
+ class HostWrapper : public mbgl::style::CustomLayerHost {
+ public:
+ QScopedPointer<QMapbox::CustomLayerHostInterface> ptr;
+ HostWrapper(QScopedPointer<QMapbox::CustomLayerHostInterface>& 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<mbgl::style::CustomLayer>(
id.toStdString(),
- reinterpret_cast<mbgl::style::CustomLayerInitializeFunction>(initFn),
- // This cast is safe as long as both mbgl:: and QMapbox::
- // CustomLayerRenderParameters members remains the same.
- (mbgl::style::CustomLayerRenderFunction)renderFn,
- reinterpret_cast<mbgl::style::CustomLayerDeinitializeFunction>(deinitFn),
- context),
+ std::make_unique<HostWrapper>(host)),
before.isEmpty() ? mbgl::optional<std::string>() : mbgl::optional<std::string>(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<const RenderLayer*>& 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<Feature> 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<const RenderLayer*>& layers,
const RenderedQueryOptions& options,
- const CollisionIndex& collisionIndex) const final;
+ const mat4& projMatrix) const final;
std::vector<Feature>
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<const GeometryTileData> 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<float>(envelope.min), convertPoint<float>(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<float>(envelope.min), convertPoint<float>(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<std::string, std::vector<Feature>>& 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<const RenderLayer*>& 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<int16_t>(util::EXTENT, additionalQueryRadius * pixelsToTileUnits);
+ const int16_t additionalPadding = std::min<int16_t>(util::EXTENT, additionalQueryPadding * pixelsToTileUnits);
// Query the grid index
mapbox::geometry::box<int16_t> box = mapbox::geometry::envelope(queryGeometry);
- std::vector<IndexedSubfeature> features = grid.query({ convertPoint<float>(box.min - additionalRadius),
- convertPoint<float>(box.max + additionalRadius) });
+ std::vector<IndexedSubfeature> features = grid.query({ convertPoint<float>(box.min - additionalPadding),
+ convertPoint<float>(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<size_t>::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<std::string, std::vector<Feature>> FeatureIndex::lookupSymbolFeatures(const std::vector<IndexedSubfeature>& symbolFeatures,
+ const RenderedQueryOptions& queryOptions,
+ const std::vector<const RenderLayer*>& layers,
+ const OverscaledTileID& tileID,
+ const std::shared_ptr<std::vector<size_t>>& featureSortOrder) const {
+ std::unordered_map<std::string, std::vector<Feature>> result;
+ if (!tileData) {
+ return result;
}
+ std::vector<IndexedSubfeature> 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<IndexedSubfeature> 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<std::string, std::vector<Feature>>& result,
const IndexedSubfeature& indexedFeature,
- const GeometryCoordinates& queryGeometry,
const RenderedQueryOptions& options,
- const GeometryTileData& geometryTileData,
const CanonicalTileID& tileID,
const std::vector<const RenderLayer*>& 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<GeometryTileLayer> sourceLayer;
std::unique_ptr<GeometryTileFeature> 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<RenderSymbolLayer>() &&
- !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<float>(tileID.z), geometryTileFeature.get() })) {
continue;
}
@@ -155,8 +190,8 @@ optional<GeometryCoordinates> FeatureIndex::translateQueryGeometry(
return translated;
}
-void FeatureIndex::setBucketLayerIDs(const std::string& bucketName, const std::vector<std::string>& layerIDs) {
- bucketLayerIDs[bucketName] = layerIDs;
+void FeatureIndex::setBucketLayerIDs(const std::string& bucketLeaderID, const std::vector<std::string>& 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 <mbgl/tile/tile_id.hpp>
#include <mbgl/util/grid_index.hpp>
#include <mbgl/util/feature.hpp>
+#include <mbgl/util/mat4.hpp>
#include <vector>
#include <string>
@@ -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<const GeometryTileData> 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<std::string, std::vector<Feature>>& 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 RenderLayer*>&,
- const CollisionIndex&,
- const float additionalQueryRadius) const;
+ const float additionalQueryPadding) const;
static optional<GeometryCoordinates> translateQueryGeometry(
const GeometryCoordinates& queryGeometry,
@@ -75,23 +74,31 @@ public:
const float bearing,
const float pixelsToTileUnits);
- void setBucketLayerIDs(const std::string& bucketName, const std::vector<std::string>& layerIDs);
+ void setBucketLayerIDs(const std::string& bucketLeaderID, const std::vector<std::string>& layerIDs);
+
+ std::unordered_map<std::string, std::vector<Feature>> lookupSymbolFeatures(
+ const std::vector<IndexedSubfeature>& symbolFeatures,
+ const RenderedQueryOptions& options,
+ const std::vector<const RenderLayer*>& layers,
+ const OverscaledTileID& tileID,
+ const std::shared_ptr<std::vector<size_t>>& featureSortOrder) const;
private:
void addFeature(
std::unordered_map<std::string, std::vector<Feature>>& result,
const IndexedSubfeature&,
- const GeometryCoordinates& queryGeometry,
const RenderedQueryOptions& options,
- const GeometryTileData&,
const CanonicalTileID&,
const std::vector<const RenderLayer*>&,
- const float bearing,
- const float pixelsToTileUnits) const;
+ const GeometryCoordinates& queryGeometry,
+ const TransformState& transformState,
+ const float pixelsToTileUnits,
+ const mat4& posMatrix) const;
GridIndex<IndexedSubfeature> grid;
unsigned int sortIndex = 0;
std::unordered_map<std::string, std::vector<std::string>> bucketLayerIDs;
+ std::unique_ptr<const GeometryTileData> 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 <class BinaryProgram>
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<GeometryTileLayer> 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<TextField>(zoom, ft);
- if (layout.get<TextField>().isConstant()) {
+ if (layout.get<TextField>().isConstant() && !leader.layout.get<TextField>().isExpression()) {
u8string = util::replaceTokens(u8string, getValue);
}
@@ -159,7 +159,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters,
if (hasIcon) {
std::string icon = layout.evaluate<IconImage>(zoom, ft);
- if (layout.get<IconImage>().isConstant()) {
+ if (layout.get<IconImage>().isConstant() && !leader.layout.get<IconImage>().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<TextRotationAlignment>() == AlignmentType::Map &&
layout.get<SymbolPlacement>() == 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<Shaping, Shaping>& shapedTextOrientations,
optional<PositionedIcon> 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<SymbolPlacement>();
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<SymbolBucket> SymbolLayout::place(const bool showCollisionBoxes)
const bool mayOverlap = layout.get<TextAllowOverlap>() || layout.get<IconAllowOverlap>() ||
layout.get<TextIgnorePlacement>() || layout.get<IconIgnorePlacement>();
- auto bucket = std::make_unique<SymbolBucket>(layout, layerPaintProperties, textSize, iconSize, zoom, sdfIcons, iconsNeedLinear, mayOverlap, std::move(symbolInstances));
+ auto bucket = std::make_unique<SymbolBucket>(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<SymbolBucket> place(const bool showCollisionBoxes);
@@ -44,7 +43,7 @@ public:
std::map<std::string,
std::pair<style::IconPaintProperties::PossiblyEvaluated, style::TextPaintProperties::PossiblyEvaluated>> layerPaintProperties;
- const std::string bucketName;
+ const std::string bucketLeaderID;
std::vector<SymbolInstance> symbolInstances;
private:
@@ -52,9 +51,7 @@ private:
const SymbolFeature&,
const std::pair<Shaping, Shaping>& shapedTextOrientations,
optional<PositionedIcon> shapedIcon,
- const GlyphPositionMap&,
- const OverscaledTileID&,
- const std::string&);
+ const GlyphPositionMap&);
bool anchorIsTooClose(const std::u16string& text, const float repeatDistance, const Anchor&);
std::map<std::u16string, std::vector<Anchor>> 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<std::pair<PlacedGlyph, PlacedGlyph>>();
+ }
+
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<double>& 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<float>(getSize().height) });
+ mat4 mat = coordinatePointMatrix(getZoom());
+ Point<double> 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<int16_t>(labelAnchor.x),
static_cast<int16_t>(labelAnchor.y),
- static_cast<int16_t>(::round(o.x * 64)), // use 1/64 pixels for placement
- static_cast<int16_t>(::round((o.y + glyphOffsetY) * 64))
+ static_cast<int16_t>(::round(o.x * 32)), // use 1/32 pixels for placement
+ static_cast<int16_t>(::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<SymbolInstance>&& 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<std::vector<size_t>>();
+ 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<SymbolInstance>&&);
void upload(gl::Context&) override;
@@ -64,6 +65,8 @@ public:
const bool iconsNeedLinear;
const bool sortFeaturesByY;
+ const std::string bucketLeaderID;
+
optional<float> sortedAngle;
bool staticUploaded = false;
@@ -131,6 +134,8 @@ public:
uint32_t bucketInstanceId = 0;
bool justReloaded = false;
+
+ std::shared_ptr<std::vector<size_t>> 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 <mbgl/programs/programs.hpp>
#include <mbgl/programs/background_program.hpp>
#include <mbgl/util/tile_cover.hpp>
+#include <mbgl/map/transform_state.hpp>
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<double>(p.x), static_cast<double>(p.y), 0, 1 }};
+ matrix::transformMat4(pos, pos, posMatrix);
+ return {
+ static_cast<int16_t>((static_cast<float>(pos[0] / pos[3]) + 1) * size.width * 0.5),
+ static_cast<int16_t>((static_cast<float>(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<style::CircleTranslate>(),
evaluated.get<style::CircleTranslateAnchor>(),
- bearing,
- pixelsToTileUnits);
+ transformState.getAngle(),
+ pixelsToTileUnits).value_or(queryGeometry);
// Evaluate functions
- auto radius = evaluated.evaluate<style::CircleRadius>(zoom, feature) * pixelsToTileUnits;
- auto stroke = evaluated.evaluate<style::CircleStrokeWidth>(zoom, feature) * pixelsToTileUnits;
+ auto radius = evaluated.evaluate<style::CircleRadius>(zoom, feature);
+ auto stroke = evaluated.evaluate<style::CircleStrokeWidth>(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<style::CirclePitchAlignment>(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<double>(point.x), static_cast<double>(point.y), 0, 1 }};
+ matrix::transformMat4(center, center, posMatrix);
+ auto pitchScale = evaluated.evaluate<style::CirclePitchScale>(zoom, feature);
+ auto pitchAlignment = evaluated.evaluate<style::CirclePitchAlignment>(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<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) 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 <mbgl/style/layers/custom_layer_impl.hpp>
#include <mbgl/map/transform_state.hpp>
#include <mbgl/gl/gl.hpp>
+#include <mbgl/util/mat4.hpp>
namespace mbgl {
using namespace style;
RenderCustomLayer::RenderCustomLayer(Immutable<style::CustomLayer::Impl> _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<Bucket> 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<style::CustomLayerHost> 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<style::FillExtrusionTranslate>(),
evaluated.get<style::FillExtrusionTranslateAnchor>(),
- 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<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) 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<style::FillTranslate>(),
evaluated.get<style::FillTranslateAnchor>(),
- 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<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) 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<style::LineTranslate>(),
evaluated.get<style::LineTranslateAnchor>(),
- 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<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) 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 <mbgl/style/layer_impl.hpp>
#include <mbgl/style/layer_type.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
+#include <mbgl/util/mat4.hpp>
#include <memory>
#include <string>
@@ -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<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) 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<const RenderLayer*>& layers,
const RenderedQueryOptions& options,
- const CollisionIndex& collisionIndex) const = 0;
+ const mat4& projMatrix) const = 0;
virtual std::vector<Feature>
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<Feature> Renderer::Impl::queryRenderedFeatures(const ScreenLineStrin
return queryRenderedFeatures(geometry, options, layers);
}
+
+void Renderer::Impl::queryRenderedSymbols(std::unordered_map<std::string, std::vector<Feature>>& resultsByLayer,
+ const ScreenLineString& geometry,
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options) const {
+
+ auto renderedSymbols = placement->getCollisionIndex().queryRenderedSymbols(geometry);
+ std::vector<std::reference_wrapper<const RetainedQueryData>> 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<Feature> Renderer::Impl::queryRenderedFeatures(const ScreenLineString& geometry, const RenderedQueryOptions& options, const std::vector<const RenderLayer*>& layers) const {
std::unordered_set<std::string> sourceIDs;
@@ -680,13 +704,18 @@ std::vector<Feature> Renderer::Impl::queryRenderedFeatures(const ScreenLineStrin
sourceIDs.emplace(layer->baseImpl->source);
}
+ mat4 projMatrix;
+ transformState.getProjMatrix(projMatrix);
+
std::unordered_map<std::string, std::vector<Feature>> 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<Feature> 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<std::string, std::vector<Feature>>& resultsByLayer,
+ const ScreenLineString& geometry,
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options) const;
+
std::vector<Feature> queryRenderedFeatures(const ScreenLineString&, const RenderedQueryOptions&, const std::vector<const RenderLayer*>&) 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<const RenderLayer*>& 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<Feature> 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<const RenderLayer*>& layers,
const RenderedQueryOptions& options,
- const CollisionIndex& collisionIndex) const final;
+ const mat4& projMatrix) const final;
std::vector<Feature>
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<const RenderLayer*>& 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<Feature> 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<const RenderLayer*>& layers,
const RenderedQueryOptions& options,
- const CollisionIndex&) const final;
+ const mat4& projMatrix) const final;
std::vector<Feature>
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 RenderLayer*>&,
const RenderedQueryOptions&,
- const CollisionIndex&) const {
+ const mat4&) const {
return std::unordered_map<std::string, std::vector<Feature>> {};
}
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<const RenderLayer*>& layers,
const RenderedQueryOptions& options,
- const CollisionIndex& collisionIndex) const final;
+ const mat4& projMatrix) const final;
std::vector<Feature> 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 RenderLayer*>&,
const RenderedQueryOptions&,
- const CollisionIndex& ) const {
+ const mat4&) const {
return std::unordered_map<std::string, std::vector<Feature>> {};
}
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<const RenderLayer*>& layers,
const RenderedQueryOptions& options,
- const CollisionIndex& collisionIndex) const final;
+ const mat4& projMatrix) const final;
std::vector<Feature>
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 RenderLayer*>&,
const RenderedQueryOptions&,
- const CollisionIndex& ) const {
+ const mat4&) const {
return std::unordered_map<std::string, std::vector<Feature>> {};
}
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<const RenderLayer*>& layers,
const RenderedQueryOptions& options,
- const CollisionIndex& collisionIndex) const final;
+ const mat4& projMatrix) const final;
std::vector<Feature>
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<const RenderLayer*>& 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<Feature> 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<const RenderLayer*>& layers,
const RenderedQueryOptions& options,
- const CollisionIndex& collisionIndex) const final;
+ const mat4& projMatrix) const final;
std::vector<Feature>
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<std::string, std::vector<Feature>> TilePyramid::queryRendered
const TransformState& transformState,
const std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options,
- const CollisionIndex& collisionIndex) const {
+ const mat4& projMatrix) const {
std::unordered_map<std::string, std::vector<Feature>> result;
if (renderTiles.empty() || geometry.empty()) {
return result;
@@ -264,14 +264,19 @@ std::unordered_map<std::string, std::vector<Feature>> 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<std::string, std::vector<Feature>> 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 RenderLayer*>&,
const RenderedQueryOptions& options,
- const CollisionIndex& collisionIndex) const;
+ const mat4& projMatrix) const;
std::vector<Feature> 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 <mbgl/style/conversion/filter.hpp>
#include <mbgl/util/geometry.hpp>
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/expression/type.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
namespace mbgl {
namespace style {
namespace conversion {
+
+using GeometryValue = mapbox::geometry::value;
-static optional<Value> normalizeValue(const optional<Value>& 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<std::string> op = toString(arrayMember(filter, 0));
+
+ if (!op) {
+ return false;
+
+ } else if (*op == "has") {
+ if (arrayLength(filter) < 2) return false;
+ optional<std::string> 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<GeometryValue> normalizeValue(const optional<GeometryValue>& value, Error& error) {
if (!value) {
error = { "filter expression value must be a boolean, number, or string" };
return {};
@@ -32,7 +73,7 @@ static optional<FeatureType> toFeatureType(const Convertible& value, Error& erro
}
static optional<FeatureIdentifier> toFeatureIdentifier(const Convertible& value, Error& error) {
- optional<Value> identifier = toValue(value);
+ optional<GeometryValue> identifier = toValue(value);
if (!identifier) {
error = { "filter expression value must be a boolean, number, or string" };
return {};
@@ -99,7 +140,7 @@ optional<Filter> convertEqualityFilter(const Convertible& value, Error& error) {
return { IdentifierFilterType { *filterValue } };
} else {
- optional<Value> filterValue = normalizeValue(toValue(arrayMember(value, 2)), error);
+ optional<GeometryValue> filterValue = normalizeValue(toValue(arrayMember(value, 2)), error);
if (!filterValue) {
return {};
}
@@ -121,7 +162,7 @@ optional<Filter> convertBinaryFilter(const Convertible& value, Error& error) {
return {};
}
- optional<Value> filterValue = normalizeValue(toValue(arrayMember(value, 2)), error);
+ optional<GeometryValue> filterValue = normalizeValue(toValue(arrayMember(value, 2)), error);
if (!filterValue) {
return {};
}
@@ -167,9 +208,9 @@ optional<Filter> convertSetFilter(const Convertible& value, Error& error) {
return { IdentifierFilterType { std::move(values) } };
} else {
- std::vector<Value> values;
+ std::vector<GeometryValue> values;
for (std::size_t i = 2; i < arrayLength(value); ++i) {
- optional<Value> filterValue = normalizeValue(toValue(arrayMember(value, i)), error);
+ optional<GeometryValue> filterValue = normalizeValue(toValue(arrayMember(value, i)), error);
if (!filterValue) {
return {};
}
@@ -193,8 +234,23 @@ optional<Filter> convertCompoundFilter(const Convertible& value, Error& error) {
return { FilterType { std::move(filters) } };
}
+
+optional<Filter> 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<Filter> Converter<Filter>::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 <class F>
@@ -286,138 +290,19 @@ void stringify(Writer& writer, const Undefined&) {
writer.Null();
}
-template <class Writer>
-void stringify(Writer& writer, const CategoricalValue& v) {
- CategoricalValue::visit(v, [&] (const auto& v_) { stringify(writer, v_); });
-}
-
-template <class Writer>
-class StringifyStops {
-public:
- Writer& writer;
-
- template <class T>
- void operator()(const ExponentialStops<T>& f) {
- writer.Key("type");
- writer.String("exponential");
- writer.Key("base");
- writer.Double(f.base);
- writer.Key("stops");
- stringifyStops(f.stops);
- }
-
- template <class T>
- void operator()(const IntervalStops<T>& f) {
- writer.Key("type");
- writer.String("interval");
- writer.Key("stops");
- stringifyStops(f.stops);
- }
-
- template <class T>
- void operator()(const CategoricalStops<T>& f) {
- writer.Key("type");
- writer.String("categorical");
- writer.Key("stops");
- stringifyStops(f.stops);
- }
-
- template <class T>
- void operator()(const IdentityStops<T>&) {
- writer.Key("type");
- writer.String("identity");
- }
-
- template <class T>
- void operator()(const CompositeExponentialStops<T>& f) {
- writer.Key("type");
- writer.String("exponential");
- writer.Key("base");
- writer.Double(f.base);
- writer.Key("stops");
- stringifyCompositeStops(f.stops);
- }
-
- template <class T>
- void operator()(const CompositeIntervalStops<T>& f) {
- writer.Key("type");
- writer.String("interval");
- writer.Key("stops");
- stringifyCompositeStops(f.stops);
- }
-
- template <class T>
- void operator()(const CompositeCategoricalStops<T>& f) {
- writer.Key("type");
- writer.String("categorical");
- writer.Key("stops");
- stringifyCompositeStops(f.stops);
- }
-
-private:
- template <class K, class V>
- void stringifyStops(const std::map<K, V>& stops) {
- writer.StartArray();
- for (const auto& stop : stops) {
- writer.StartArray();
- stringify(writer, stop.first);
- stringify(writer, stop.second);
- writer.EndArray();
- }
- writer.EndArray();
- }
-
- template <class InnerStops>
- void stringifyCompositeStops(const std::map<float, InnerStops>& 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 <class Writer, class T>
-void stringify(Writer& writer, const CameraFunction<T>& f) {
- writer.StartObject();
- CameraFunction<T>::Stops::visit(f.stops, StringifyStops<Writer> { writer });
- writer.EndObject();
+void stringify(Writer& writer, const CameraFunction<T>& fn) {
+ stringify(writer, fn.getExpression().serialize());
}
template <class Writer, class T>
-void stringify(Writer& writer, const SourceFunction<T>& f) {
- writer.StartObject();
- writer.Key("property");
- writer.String(f.property);
- SourceFunction<T>::Stops::visit(f.stops, StringifyStops<Writer> { writer });
- if (f.defaultValue) {
- writer.Key("default");
- stringify(writer, *f.defaultValue);
- }
- writer.EndObject();
+void stringify(Writer& writer, const SourceFunction<T>& fn) {
+ stringify(writer, fn.getExpression().serialize());
}
template <class Writer, class T>
-void stringify(Writer& writer, const CompositeFunction<T>& f) {
- writer.StartObject();
- writer.Key("property");
- writer.String(f.property);
- CompositeFunction<T>::Stops::visit(f.stops, StringifyStops<Writer> { writer });
- if (f.defaultValue) {
- writer.Key("default");
- stringify(writer, *f.defaultValue);
- }
- writer.EndObject();
+void stringify(Writer& writer, const CompositeFunction<T>& fn) {
+ stringify(writer, fn.getExpression().serialize());
}
template <class Writer, class T>
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<Tileset> Converter<Tileset>::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<double>();
const auto inputArray = evaluatedInput->get<std::vector<Value>>();
- 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<std::size_t>(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 <mbgl/style/expression/util.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/math/log2.hpp>
+#include <mbgl/util/i18n.hpp>
#include <mbgl/util/ignore.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/platform.hpp>
+#include <cmath>
namespace mbgl {
namespace style {
@@ -209,12 +211,7 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali
);
});
define("to-rgba", [](const Color& color) -> Result<std::array<double, 4>> {
- return std::array<double, 4> {{
- 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<std::string, CompoundExpressionRegistry::Definition> initiali
return object.at(key);
});
- define("length", [](const std::vector<Value>& arr) -> Result<double> {
- return arr.size();
- });
- define("length", [] (const std::string s) -> Result<double> {
- return s.size();
- });
-
define("properties", [](const EvaluationContext& params) -> Result<std::unordered_map<std::string, Value>> {
if (!params.feature) {
return EvaluationError {
@@ -374,6 +364,11 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali
return result;
});
+ define("round", [](double x) -> Result<double> { return std::round(x); });
+ define("floor", [](double x) -> Result<double> { return std::floor(x); });
+ define("ceil", [](double x) -> Result<double> { return std::ceil(x); });
+ define("abs", [](double x) -> Result<double> { return std::abs(x); });
+
define(">", [](double lhs, double rhs) -> Result<bool> { return lhs > rhs; });
define(">", [](const std::string& lhs, const std::string& rhs) -> Result<bool> { return lhs > rhs; });
define(">=", [](double lhs, double rhs) -> Result<bool> { return lhs >= rhs; });
@@ -385,6 +380,10 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali
define("!", [](bool e) -> Result<bool> { return !e; });
+ define("is-supported-script", [](const std::string& x) -> Result<bool> {
+ return util::i18n::isStringInSupportedScript(x);
+ });
+
define("upcase", [](const std::string& input) -> Result<std::string> {
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<T>::serialize() const {
interpolator.match(
[&](const ExponentialInterpolator& exponential) {
- serialized.emplace_back(std::vector<mbgl::Value>{{ std::string("exponential"), exponential.base }});
+ if (exponential.base == 1) {
+ serialized.emplace_back(std::vector<mbgl::Value>{{ std::string("linear") }});
+ } else {
+ serialized.emplace_back(std::vector<mbgl::Value>{{ 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 <mbgl/style/expression/length.hpp>
+#include <mbgl/util/string.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+Length::Length(std::unique_ptr<Expression> 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<Value>& 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<void(const Expression&)>& visit) const {
+ visit(*input);
+}
+
+bool Length::operator==(const Expression& e) const {
+ if (auto eq = dynamic_cast<const Length*>(&e)) {
+ return *eq->input == *input;
+ }
+ return false;
+}
+
+std::vector<optional<Value>> 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::Array>() && !type.is<type::StringType>() && !type.is<type::ValueType>()) {
+ ctx.error("Expected argument of type string or array, but found " + toString(type) + " instead.");
+ return ParseResult();
+ }
+
+ return ParseResult(std::make_unique<Length>(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 <mbgl/style/expression/compound_expression.hpp>
#include <mbgl/style/expression/equals.hpp>
#include <mbgl/style/expression/interpolate.hpp>
+#include <mbgl/style/expression/length.hpp>
#include <mbgl/style/expression/let.hpp>
#include <mbgl/style/expression/literal.hpp>
#include <mbgl/style/expression/match.hpp>
@@ -31,23 +32,36 @@ namespace style {
namespace expression {
bool isConstant(const Expression& expression) {
- if (dynamic_cast<const Var*>(&expression)) {
- return false;
+ if (auto varExpression = dynamic_cast<const Var*>(&expression)) {
+ return isConstant(*varExpression->getBoundExpression());
}
-
+
if (auto compound = dynamic_cast<const CompoundExpressionBase*>(&expression)) {
if (compound->getName() == "error") {
return false;
}
}
+
+ bool isTypeAnnotation = dynamic_cast<const Coercion*>(&expression) ||
+ dynamic_cast<const Assertion*>(&expression) ||
+ dynamic_cast<const ArrayAssertion*>(&expression);
- bool literalArgs = true;
+ bool childrenConstant = true;
expression.eachChild([&](const Expression& child) {
- if (!dynamic_cast<const Literal*>(&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<const Literal*>(&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> expression) {
- std::vector<std::unique_ptr<Expression>> args;
- args.push_back(std::move(expression));
- return args;
- };
+ auto array = [&](std::unique_ptr<Expression> expression) {
+ std::vector<std::unique_ptr<Expression>> 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<Assertion>(*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<Literal *>(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<variant<const InterpolateBase*, const Step*, ParsingError>> 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<std::string> ParsingContext::checkType(const type::Type& t) {
assert(expected);
optional<std::string> 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<mbgl::Value>::toExpressionValue(const mbgl::Value& value) {
mbgl::Value ValueConverter<mbgl::Value>::fromExpressionValue(const Value& value) {
return value.match(
[&](const Color& color)->mbgl::Value {
+ std::array<double, 4> array = color.toArray();
return std::vector<mbgl::Value>{
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<Value>& 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 <mbgl/style/filter.hpp>
+#include <mbgl/style/filter_evaluator.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
+
+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 <mbgl/style/filter.hpp>
+#include <mbgl/style/filter_evaluator.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
+
+namespace mbgl {
+namespace style {
+
+template <class Op>
+struct Comparator {
+ const Op& op;
+
+ template <class T>
+ bool operator()(const T& lhs, const T& rhs) const {
+ return op(lhs, rhs);
+ }
+
+ template <class T0, class T1>
+ auto operator()(const T0& lhs, const T1& rhs) const
+ -> typename std::enable_if_t<std::is_arithmetic<T0>::value && !std::is_same<T0, bool>::value &&
+ std::is_arithmetic<T1>::value && !std::is_same<T1, bool>::value, bool> {
+ return op(double(lhs), double(rhs));
+ }
+
+ template <class T0, class T1>
+ auto operator()(const T0&, const T1&) const
+ -> typename std::enable_if_t<!std::is_arithmetic<T0>::value || std::is_same<T0, bool>::value ||
+ !std::is_arithmetic<T1>::value || std::is_same<T1, bool>::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<Value>&,
+ const std::vector<Value>&) 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 <class Op>
+bool compare(const Value& lhs, const Value& rhs, const Op& op) {
+ return Value::binary_visit(lhs, rhs, Comparator<Op> { 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<Value> actual = context.feature->getValue(filter.key);
+ return actual && equal(*actual, filter.value);
+}
+
+bool FilterEvaluator::operator()(const NotEqualsFilter& filter) const {
+ optional<Value> actual = context.feature->getValue(filter.key);
+ return !actual || !equal(*actual, filter.value);
+}
+
+bool FilterEvaluator::operator()(const LessThanFilter& filter) const {
+ optional<Value> 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<Value> 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<Value> 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<Value> 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<Value> 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<Value> 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<bool> typed = expression::fromExpressionValue<bool>(*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<Impl>(layerID, init, render, contextLost, deinit, context)) {
-}
-
-CustomLayer::CustomLayer(const std::string& layerID,
- CustomLayerInitializeFunction init,
- CustomLayerRenderFunction render,
- CustomLayerDeinitializeFunction deinit,
- void* context)
- : Layer(makeMutable<Impl>(layerID, init, render, nullptr, deinit, context)) {
+ std::unique_ptr<CustomLayerHost> host)
+ : Layer(makeMutable<Impl>(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<CustomLayerHost> 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 <mbgl/style/layer_impl.hpp>
#include <mbgl/style/layers/custom_layer.hpp>
+#include <memory>
+
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<CustomLayerHost> host);
bool hasLayoutDifference(const Layer::Impl&) const override;
void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override;
- CustomLayerInitializeFunction initializeFn = nullptr;
- CustomLayerRenderFunction renderFn = nullptr;
- CustomLayerContextLostFunction contextLostFn = nullptr;
- CustomLayerDeinitializeFunction deinitializeFn = nullptr;
- void* context = nullptr;
+ std::shared_ptr<CustomLayerHost> 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<bool,bool> 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<float>& polygon, const GridIndex<Inde
return util::polygonIntersectsPolygon(integerPolygon, bboxPoints);
}
-std::vector<IndexedSubfeature> CollisionIndex::queryRenderedSymbols(const GeometryCoordinates& queryGeometry, const UnwrappedTileID& tileID, const std::string& sourceID) const {
- std::vector<IndexedSubfeature> result;
+std::unordered_map<uint32_t, std::vector<IndexedSubfeature>> CollisionIndex::queryRenderedSymbols(const ScreenLineString& queryGeometry) const {
+ std::unordered_map<uint32_t, std::vector<IndexedSubfeature>> 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<float> projectedQuery;
+
+ LineString<float> gridQuery;
for (const auto& point : queryGeometry) {
- auto projected = projectPoint(posMatrix, convertPoint<float>(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<IndexedSubfeature, GridIndex<IndexedSubfeature>::BBox>;
- std::vector<QueryResult> thisTileFeatures;
std::vector<QueryResult> 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<QueryResult> 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<std::string, std::unordered_map<std::string, std::unordered_set<std::size_t>>> sourceLayerFeatures;
- for (auto& queryResult : thisTileFeatures) {
+ std::unordered_map<uint32_t, std::unordered_set<size_t>> 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<IndexedSubfeature> queryRenderedSymbols(const GeometryCoordinates&, const UnwrappedTileID& tileID, const std::string& sourceID) const;
+ std::unordered_map<uint32_t, std::vector<IndexedSubfeature>> 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<SymbolBucket*>(bucket));
SymbolBucket& symbolBucket = *reinterpret_cast<SymbolBucket*>(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<GeometryTile*>(&renderTile.tile));
+ GeometryTile& geometryTile = static_cast<GeometryTile&>(renderTile.tile);
+
+
+ auto bucket = geometryTile.getBucket(*symbolLayer.baseImpl);
assert(dynamic_cast<SymbolBucket*>(bucket));
SymbolBucket& symbolBucket = *reinterpret_cast<SymbolBucket*>(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::IconRotationAlignment>() == 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<style::TextIgnorePlacement>());
+ collisionIndex.insertFeature(symbolInstance.textCollisionFeature, bucket.layout.get<style::TextIgnorePlacement>(), bucket.bucketInstanceId);
}
if (placeIcon) {
- collisionIndex.insertFeature(symbolInstance.iconCollisionFeature, bucket.layout.get<style::IconIgnorePlacement>());
+ collisionIndex.insertFeature(symbolInstance.iconCollisionFeature, bucket.layout.get<style::IconIgnorePlacement>(), 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<SymbolBucket*>(bucket));
SymbolBucket& symbolBucket = *reinterpret_cast<SymbolBucket*>(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<uint32_t>&
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> featureIndex;
+ OverscaledTileID tileID;
+ std::shared_ptr<std::vector<size_t>> featureSortOrder;
+
+ RetainedQueryData(uint32_t bucketInstanceId_,
+ std::shared_ptr<FeatureIndex> 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<uint32_t, RetainedQueryData> 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<float>(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<float>(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<const RenderLayer*>& 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<const RenderLayer*>& 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<Feature>& 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<float>(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 <mbgl/renderer/image_manager.hpp>
#include <mbgl/text/glyph_manager.hpp>
#include <mbgl/util/feature.hpp>
-#include <mbgl/util/throttler.hpp>
#include <mbgl/actor/actor.hpp>
#include <mbgl/geometry/feature_index.hpp>
@@ -55,43 +54,33 @@ public:
const TransformState&,
const std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options,
- const CollisionIndex& collisionIndex) override;
+ const mat4& projMatrix) override;
void querySourceFeatures(
std::vector<Feature>& result,
const SourceQueryOptions&) override;
+ float getQueryPadding(const std::vector<const RenderLayer*>&) override;
+
void cancel() override;
class LayoutResult {
public:
- std::unordered_map<std::string, std::shared_ptr<Bucket>> nonSymbolBuckets;
+ std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets;
std::unique_ptr<FeatureIndex> featureIndex;
- std::unique_ptr<GeometryTileData> tileData;
-
- LayoutResult(std::unordered_map<std::string, std::shared_ptr<Bucket>> nonSymbolBuckets_,
- std::unique_ptr<FeatureIndex> featureIndex_,
- std::unique_ptr<GeometryTileData> 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<std::string, std::shared_ptr<Bucket>> symbolBuckets;
optional<AlphaImage> glyphAtlasImage;
optional<PremultipliedImage> iconAtlasImage;
- PlacementResult(std::unordered_map<std::string, std::shared_ptr<Bucket>> symbolBuckets_,
- optional<AlphaImage> glyphAtlasImage_,
- optional<PremultipliedImage> iconAtlasImage_)
- : symbolBuckets(std::move(symbolBuckets_)),
+ LayoutResult(std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets_,
+ std::unique_ptr<FeatureIndex> featureIndex_,
+ optional<AlphaImage> glyphAtlasImage_,
+ optional<PremultipliedImage> 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<FeatureIndex> 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<std::string, std::shared_ptr<Bucket>> nonSymbolBuckets;
- std::unique_ptr<FeatureIndex> featureIndex;
- std::unique_ptr<FeatureIndex> pendingFeatureIndex;
- std::unique_ptr<const GeometryTileData> data;
- std::unique_ptr<const GeometryTileData> pendingData;
+ std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets;
+
+ std::shared_ptr<FeatureIndex> latestFeatureIndex;
optional<AlphaImage> glyphAtlasImage;
optional<PremultipliedImage> iconAtlasImage;
- std::unordered_map<std::string, std::shared_ptr<Bucket>> 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<GeometryTileWorker> 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<const GeometryTileData> data_, uint64_t correlationID_) {
@@ -92,14 +117,14 @@ void GeometryTileWorker::setData(std::unique_ptr<const GeometryTileData> 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<Immutable<Layer::Impl>> 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<std::unique_ptr<RenderLayer>> 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<std::string, std::unique_ptr<SymbolLayout>> symbolLayoutMap;
- std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets;
- auto featureIndex = std::make_unique<FeatureIndex>();
+ buckets.clear();
+ featureIndex = std::make_unique<FeatureIndex>(*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<GeometryTileFeature> feature = geometryLayer->getFeature(i);
- if (!filter(feature->getType(), feature->getID(), [&] (const auto& key) { return feature->getValue(key); }))
+ if (!filter(expression::EvaluationContext { static_cast<float>(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<std::string, std::shared_ptr<Bucket>> 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 <mbgl/util/optional.hpp>
#include <mbgl/util/immutable.hpp>
#include <mbgl/style/layer_impl.hpp>
+#include <mbgl/geometry/feature_index.hpp>
+#include <mbgl/renderer/bucket.hpp>
#include <atomic>
#include <memory>
@@ -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<GeometryTileWorker> self;
ActorRef<GeometryTile> parent;
@@ -62,12 +65,15 @@ private:
const std::atomic<bool>& obsolete;
const MapMode mode;
const float pixelRatio;
+
+ std::unique_ptr<FeatureIndex> featureIndex;
+ std::unordered_map<std::string, std::shared_ptr<Bucket>> 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 RenderLayer*>&,
const RenderedQueryOptions&,
- const CollisionIndex&) {}
+ const mat4&) {}
+
+float Tile::getQueryPadding(const std::vector<const RenderLayer*>&) {
+ return 0;
+}
void Tile::querySourceFeatures(
std::vector<Feature>&,
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 RenderLayer*>&,
const RenderedQueryOptions& options,
- const CollisionIndex&);
+ const mat4& projMatrix);
virtual void querySourceFeatures(
std::vector<Feature>& result,
const SourceQueryOptions&);
+ virtual float getQueryPadding(const std::vector<const RenderLayer*>&);
+
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> Color::parse(const std::string& s) {
}
std::string Color::stringify() const {
+ std::array<double, 4> 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<double, 4> 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 <mbgl/util/i18n.hpp>
+#include <mbgl/util/utf.hpp>
#include <algorithm>
#include <map>
@@ -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 <mbgl/util/throttler.hpp>
-
-namespace mbgl {
-namespace util {
-
-Throttler::Throttler(Duration frequency_, std::function<void()>&& 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 <mbgl/util/chrono.hpp>
-#include <mbgl/util/timer.hpp>
-
-namespace mbgl {
-namespace util {
-
-class Throttler {
-public:
- Throttler(Duration frequency, std::function<void()>&& function);
-
- void invoke();
-private:
- Duration frequency;
- std::function<void()> 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 <mbgl/util/constants.hpp>
#include <mbgl/util/interpolate.hpp>
#include <mbgl/map/transform_state.hpp>
+#include <mbgl/util/tile_cover_impl.hpp>
+#include <mbgl/util/tile_coordinate.hpp>
#include <functional>
+#include <list>
namespace mbgl {
namespace {
+using ScanLine = const std::function<void(int32_t x0, int32_t x1, int32_t y)>;
+
// 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<void(int32_t x0, int32_t x1, int32_t y)>;
-
// 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<UnwrappedTileID> 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<UnwrappedTileID> tileCover(const TransformState& state, int32_t z) {
z);
}
+std::vector<UnwrappedTileID> tileCover(const Geometry<double>& geometry, int32_t z) {
+ std::vector<UnwrappedTileID> 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<double>& 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<double> p({ {sw, nw, ne, se, sw} });
+ impl = std::make_unique<TileCover::Impl>(z, p, false);
+}
+
+TileCover::TileCover(const Geometry<double>& geom, int32_t z, bool project/* = true*/)
+ : impl( std::make_unique<TileCover::Impl>(z, geom, project)) {
+}
+
+TileCover::~TileCover() {
+
+}
+
+optional<UnwrappedTileID> 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 <mbgl/tile/tile_id.hpp>
#include <mbgl/style/types.hpp>
-#include <mbgl/util/tile_coordinate.hpp>
+#include <mbgl/util/geometry.hpp>
+#include <mbgl/util/optional.hpp>
#include <vector>
+#include <memory>
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<double>&, int32_t z, bool project = true);
+ ~TileCover();
+
+ optional<UnwrappedTileID> next();
+ bool hasNext();
+
+private:
+ class Impl;
+ std::unique_ptr<Impl> impl;
+};
+
int32_t coveringZoomLevel(double z, style::SourceType type, uint16_t tileSize);
std::vector<UnwrappedTileID> tileCover(const TransformState&, int32_t z);
std::vector<UnwrappedTileID> tileCover(const LatLngBounds&, int32_t z);
+std::vector<UnwrappedTileID> tileCover(const Geometry<double>&, 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<double>&, 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 <mbgl/util/tile_cover_impl.hpp>
+#include <mbgl/util/tile_coordinate.hpp>
+
+#include <functional>
+#include <cmath>
+#include <assert.h>
+#include <limits.h>
+#include <algorithm>
+
+namespace mbgl {
+namespace util {
+
+using PointList = std::vector<Point<double>>;
+
+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::size_t>(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::size_t>(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<uint32_t>(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<uint32_t>(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<int32_t>(std::floor(x)));
+ xp.xmax = std::max(xp.xmax, static_cast<int32_t>(std::ceil(x)));
+}
+
+//Build a vector of X tile-coordinates spanned by each bound.
+std::vector<TileSpan> scan_row(uint32_t y, Bounds& aet) {
+ std::vector<TileSpan> 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<Point<double>>& 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<double>& polygon, BoundsMap& et) const {
+ for(const auto&ring : polygon) {
+ buildTable(ring, et, true);
+ }
+ }
+ BoundsMap operator()(const Point<double>&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<uint32_t>(std::floor(clamp(point.y, 0.0, (double)(1 << zoom))));
+ et[y].push_back(bnd);
+ return et;
+ }
+
+ BoundsMap operator()(const MultiPoint<double>& points) const {
+ BoundsMap et;
+ for (const Point<double>& 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<uint32_t>(std::floor(clamp(point.y, 0.0, (double)(1 << zoom))));
+ et[y].push_back(bnd);
+ }
+ return et;
+ }
+
+ BoundsMap operator()(const LineString<double>& lines) const {
+ BoundsMap et;
+ buildTable(lines, et);
+ return et;
+ }
+
+ BoundsMap operator()(const MultiLineString<double>& lines) const {
+ BoundsMap et;
+ for(const auto&line : lines) {
+ buildTable(line, et);
+ }
+ return et;
+ }
+
+ BoundsMap operator()(const Polygon<double>& polygon) const {
+ BoundsMap et;
+ buildPolygonTable(polygon, et);
+ return et;
+ }
+
+ BoundsMap operator()(const MultiPolygon<double>& polygons) const {
+ BoundsMap et;
+ for(const auto& polygon: polygons) {
+ buildPolygonTable(polygon, et);
+ }
+ return et;
+ }
+
+ BoundsMap operator()(const mapbox::geometry::geometry_collection<double>&) const {
+ return {};
+ }
+};
+
+TileCover::Impl::Impl(int32_t z, const Geometry<double>& 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<UnwrappedTileID> 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 <mbgl/util/tile_cover.hpp>
+#include <mbgl/util/geometry.hpp>
+#include <mbgl/util/optional.hpp>
+
+#include <vector>
+#include <map>
+#include <queue>
+
+namespace mbgl {
+
+class TransformState;
+class LatLngBounds;
+
+namespace util {
+
+struct Bound;
+
+using Bounds = std::vector<Bound>;
+using BoundsMap = std::map<uint32_t, Bounds>;
+
+// A chain of points from a local minimum to a local maximum. `winding` indicates
+// the direction of the original geometry.
+struct Bound {
+ std::vector<Point<double>> 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<double>& geom, bool project = true);
+ ~Impl() = default;
+
+ optional<UnwrappedTileID> next();
+ bool hasNext() const;
+
+private:
+ using TileSpans = std::queue<std::pair<int32_t, int32_t>>;
+
+ 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