diff options
author | zmiao <zmiao.jamie@gmail.com> | 2019-07-23 16:42:53 +0300 |
---|---|---|
committer | zmiao <zmiao.jamie@gmail.com> | 2019-07-23 16:42:53 +0300 |
commit | f63b91973ab81ee9d5bf61313b414c1e17c3233c (patch) | |
tree | 3d9969ee292108f2f7523a134c47ccb6d74b8695 | |
parent | d7dd35220932726e23bb3d353b32b65bf50bc198 (diff) | |
download | qtlocation-mapboxgl-f63b91973ab81ee9d5bf61313b414c1e17c3233c.tar.gz |
enable cluster properties aggregation
-rw-r--r-- | include/mbgl/annotation/annotation.hpp | 1 | ||||
-rw-r--r-- | include/mbgl/style/expression/expression.hpp | 8 | ||||
-rw-r--r-- | include/mbgl/style/property_expression.hpp | 25 | ||||
-rw-r--r-- | include/mbgl/style/sources/geojson_source.hpp | 7 | ||||
-rw-r--r-- | src/mbgl/style/expression/compound_expression.cpp | 13 | ||||
-rw-r--r-- | src/mbgl/style/expression/expression.cpp | 19 | ||||
-rw-r--r-- | src/mbgl/style/expression/parsing_context.cpp | 3 | ||||
-rw-r--r-- | src/mbgl/style/sources/geojson_source_impl.cpp | 87 | ||||
m--------- | vendor/supercluster.hpp | 0 |
9 files changed, 126 insertions, 37 deletions
diff --git a/include/mbgl/annotation/annotation.hpp b/include/mbgl/annotation/annotation.hpp index fb9ea5eba2..17728741bb 100644 --- a/include/mbgl/annotation/annotation.hpp +++ b/include/mbgl/annotation/annotation.hpp @@ -4,7 +4,6 @@ #include <mbgl/util/variant.hpp> #include <mbgl/util/color.hpp> #include <mbgl/style/property_value.hpp> -#include <mbgl/style/property_value.hpp> #include <cstdint> #include <vector> diff --git a/include/mbgl/style/expression/expression.hpp b/include/mbgl/style/expression/expression.hpp index 5f66fc6dc7..9ba5c72bce 100644 --- a/include/mbgl/style/expression/expression.hpp +++ b/include/mbgl/style/expression/expression.hpp @@ -23,6 +23,7 @@ public: std::string message; }; + class EvaluationContext { public: EvaluationContext() = default; @@ -31,6 +32,9 @@ public: EvaluationContext(float zoom_, GeometryTileFeature const * feature_) : zoom(zoom_), feature(feature_) {} + EvaluationContext(optional<double> accumulated_, GeometryTileFeature const * feature_) : + accumulated(std::move(accumulated_)), feature(feature_) + {} EvaluationContext(optional<float> zoom_, GeometryTileFeature const * feature_, optional<double> colorRampParameter_) : zoom(std::move(zoom_)), feature(feature_), colorRampParameter(std::move(colorRampParameter_)) {} @@ -41,8 +45,10 @@ public: }; optional<float> zoom; + optional<double> accumulated; GeometryTileFeature const * feature = nullptr; optional<double> colorRampParameter; + // Contains formatted section object, std::unordered_map<std::string, Value>. const Value* formattedSection = nullptr; }; @@ -162,7 +168,7 @@ public: type::Type getType() const { return type; }; EvaluationResult evaluate(optional<float> zoom, const Feature& feature, optional<double> colorRampParameter) const; - + EvaluationResult evaluate(optional<double> accumulated, const Feature& feature) const; /** * Statically analyze the expression, attempting to enumerate possible outputs. Returns * an array of values plus the sentinel null optional value, used to indicate that the diff --git a/include/mbgl/style/property_expression.hpp b/include/mbgl/style/property_expression.hpp index 32983e2380..1cf6e9683d 100644 --- a/include/mbgl/style/property_expression.hpp +++ b/include/mbgl/style/property_expression.hpp @@ -1,10 +1,10 @@ #pragma once #include <mbgl/style/expression/expression.hpp> -#include <mbgl/style/expression/is_constant.hpp> +#include <mbgl/style/expression/find_zoom_curve.hpp> #include <mbgl/style/expression/interpolate.hpp> +#include <mbgl/style/expression/is_constant.hpp> #include <mbgl/style/expression/step.hpp> -#include <mbgl/style/expression/find_zoom_curve.hpp> #include <mbgl/util/range.hpp> namespace mbgl { @@ -34,11 +34,10 @@ template <class T> class PropertyExpression final : public PropertyExpressionBase { public: // Second parameter to be used only for conversions from legacy functions. - PropertyExpression(std::unique_ptr<expression::Expression> expression_, optional<T> defaultValue_ = nullopt) - : PropertyExpressionBase(std::move(expression_)), - defaultValue(std::move(defaultValue_)) { + PropertyExpression(std::unique_ptr<expression::Expression> expression_, + optional<T> defaultValue_ = nullopt) + : PropertyExpressionBase(std::move(expression_)), defaultValue(std::move(defaultValue_)) { } - T evaluate(const expression::EvaluationContext& context, T finalDefaultValue = T()) const { assert(canEvaluateWith(context)); const expression::EvaluationResult result = expression->evaluate(context); @@ -53,6 +52,17 @@ public: return evaluate(expression::EvaluationContext(zoom)); } + T evaluate(optional<double> accumulated, + mapbox::feature::feature<double>& f, + T finalDefaultValue = T()) const { + const expression::EvaluationResult result = expression->evaluate(accumulated, f); + if (result) { + const optional<T> typed = expression::fromExpressionValue<T>(*result); + return typed ? *typed : defaultValue ? *defaultValue : finalDefaultValue; + } + return defaultValue ? *defaultValue : finalDefaultValue; + } + T evaluate(const GeometryTileFeature& feature, T finalDefaultValue) const { return evaluate(expression::EvaluationContext(&feature), finalDefaultValue); } @@ -65,8 +75,7 @@ public: return expression::fromExpressionValues<T>(expression->possibleOutputs()); } - friend bool operator==(const PropertyExpression& lhs, - const PropertyExpression& rhs) { + friend bool operator==(const PropertyExpression& lhs, const PropertyExpression& rhs) { return *lhs.expression == *rhs.expression; } diff --git a/include/mbgl/style/sources/geojson_source.hpp b/include/mbgl/style/sources/geojson_source.hpp index a03b910279..cf84704ee8 100644 --- a/include/mbgl/style/sources/geojson_source.hpp +++ b/include/mbgl/style/sources/geojson_source.hpp @@ -1,9 +1,13 @@ #pragma once #include <mbgl/style/source.hpp> +#include <mbgl/util/constants.hpp> #include <mbgl/util/geojson.hpp> #include <mbgl/util/optional.hpp> -#include <mbgl/util/constants.hpp> + +#include <string> +#include <unordered_map> +#include <utility> namespace mbgl { @@ -24,6 +28,7 @@ struct GeoJSONOptions { bool cluster = false; uint16_t clusterRadius = 50; uint8_t clusterMaxZoom = 17; + std::unordered_map<std::string, std::pair<std::string, std::string>> clusterProperties; }; class GeoJSONSource : public Source { diff --git a/src/mbgl/style/expression/compound_expression.cpp b/src/mbgl/style/expression/compound_expression.cpp index cc1d58025b..6ca6eec850 100644 --- a/src/mbgl/style/expression/compound_expression.cpp +++ b/src/mbgl/style/expression/compound_expression.cpp @@ -368,6 +368,18 @@ const auto& lineProgressCompoundExpression() { return signature; } +const auto& accumulatedCompoundExpression() { + static auto signature = detail::makeSignature("accumulated", [](const EvaluationContext& params) -> Result<double> { + if (!params.accumulated) { + return EvaluationError { + "The 'accumulated' expression is unavailable in the current evaluation context." + }; + } + return *(params.accumulated); + }); + return signature; +} + const auto& hasContextCompoundExpression() { static auto signature = detail::makeSignature("has", [](const EvaluationContext& params, const std::string& key) -> Result<bool> { if (!params.feature) { @@ -870,6 +882,7 @@ MAPBOX_ETERNAL_CONSTEXPR const auto compoundExpressionRegistry = mapbox::eternal { "zoom", zoomCompoundExpression }, { "heatmap-density", heatmapDensityCompoundExpression }, { "line-progress", lineProgressCompoundExpression }, + { "accumulated", accumulatedCompoundExpression}, { "has", hasContextCompoundExpression }, { "has", hasObjectCompoundExpression }, { "get", getContextCompoundExpression }, diff --git a/src/mbgl/style/expression/expression.cpp b/src/mbgl/style/expression/expression.cpp index 1e5b1581d2..4a7c10fa74 100644 --- a/src/mbgl/style/expression/expression.cpp +++ b/src/mbgl/style/expression/expression.cpp @@ -5,13 +5,13 @@ namespace mbgl { namespace style { namespace expression { - + class GeoJSONFeature : public GeometryTileFeature { public: const Feature& feature; - - GeoJSONFeature(const Feature& feature_) : feature(feature_) {} - + + GeoJSONFeature( const Feature& feature_) : feature(feature_) {} + FeatureType getType() const override { return apply_visitor(ToFeatureType(), feature.geometry); } @@ -26,11 +26,16 @@ public: return optional<mbgl::Value>(); } }; - - + + EvaluationResult Expression::evaluate(optional<float> zoom, const Feature& feature, optional<double> colorRampParameter) const { GeoJSONFeature f(feature); - return this->evaluate(EvaluationContext(zoom, &f, colorRampParameter)); + return this->evaluate(EvaluationContext(zoom, &f, colorRampParameter)); +} + +EvaluationResult Expression::evaluate(optional<double> accumulated, const Feature& feature) const{ + GeoJSONFeature f(feature); + return this->evaluate(EvaluationContext(accumulated,&f)); } } // namespace expression diff --git a/src/mbgl/style/expression/parsing_context.cpp b/src/mbgl/style/expression/parsing_context.cpp index a7c04f563d..6ce3a9bfaa 100644 --- a/src/mbgl/style/expression/parsing_context.cpp +++ b/src/mbgl/style/expression/parsing_context.cpp @@ -73,7 +73,8 @@ bool isConstant(const Expression& expression) { return isFeatureConstant(expression) && isGlobalPropertyConstant(expression, std::array<std::string, 2>{{"zoom", "heatmap-density"}}) && - isGlobalPropertyConstant(expression, std::array<std::string, 2>{{"zoom", "line-progress"}}); + isGlobalPropertyConstant(expression, std::array<std::string, 2>{{"zoom", "line-progress"}}) && + isGlobalPropertyConstant(expression, std::array<std::string, 2>{{"zoom", "accumulated"}}); } using namespace mbgl::style::conversion; diff --git a/src/mbgl/style/sources/geojson_source_impl.cpp b/src/mbgl/style/sources/geojson_source_impl.cpp index 24ac1d7976..c11728c085 100644 --- a/src/mbgl/style/sources/geojson_source_impl.cpp +++ b/src/mbgl/style/sources/geojson_source_impl.cpp @@ -1,9 +1,12 @@ +#include <mbgl/style/property_expression.hpp> #include <mbgl/style/sources/geojson_source_impl.hpp> -#include <mbgl/util/constants.hpp> #include <mbgl/tile/tile_id.hpp> +#include <mbgl/util/constants.hpp> #include <mbgl/util/string.hpp> #include <mapbox/geojsonvt.hpp> +#include <mbgl/style/conversion/json.hpp> +#include <rapidjson/document.h> #include <supercluster.hpp> #include <cmath> @@ -11,11 +14,26 @@ namespace mbgl { namespace style { +template <class T> +PropertyExpression<T> createPropertyExpression(const char* expr) { + using JSValue = rapidjson::GenericValue<rapidjson::UTF8<>, rapidjson::CrtAllocator>; + rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> document; + document.Parse<0>(expr); + assert(!document.HasParseError()); + + // optional<expression::TypeAnnotationOption> typeAnnotationOption; + const JSValue* expression = &document; + expression::ParsingContext ctx; + expression::ParseResult parsed = + ctx.parseExpression(mbgl::style::conversion::Convertible(expression)); + return PropertyExpression<T>(std::move(*parsed)); +} + class GeoJSONVTData : public GeoJSONData { public: - GeoJSONVTData(const GeoJSON& geoJSON, - const mapbox::geojsonvt::Options& options) - : impl(geoJSON, options) {} + GeoJSONVTData(const GeoJSON& geoJSON, const mapbox::geojsonvt::Options& options) + : impl(geoJSON, options) { + } mapbox::feature::feature_collection<int16_t> getTile(const CanonicalTileID& tileID) final { return impl.getTile(tileID.z, tileID.x, tileID.y).features; @@ -25,9 +43,8 @@ public: return {}; } - mapbox::feature::feature_collection<double> getLeaves(const std::uint32_t, - const std::uint32_t, - const std::uint32_t) final { + mapbox::feature::feature_collection<double> + getLeaves(const std::uint32_t, const std::uint32_t, const std::uint32_t) final { return {}; } @@ -43,7 +60,8 @@ class SuperclusterData : public GeoJSONData { public: SuperclusterData(const mapbox::feature::feature_collection<double>& features, const mapbox::supercluster::Options& options) - : impl(features, options) {} + : impl(features, options) { + } mapbox::feature::feature_collection<int16_t> getTile(const CanonicalTileID& tileID) final { return impl.getTile(tileID.z, tileID.x, tileID.y); @@ -54,8 +72,8 @@ public: } mapbox::feature::feature_collection<double> getLeaves(const std::uint32_t cluster_id, - const std::uint32_t limit, - const std::uint32_t offset) final { + const std::uint32_t limit, + const std::uint32_t offset) final { return impl.getLeaves(cluster_id, limit, offset); } @@ -68,22 +86,55 @@ private: }; GeoJSONSource::Impl::Impl(std::string id_, GeoJSONOptions options_) - : Source::Impl(SourceType::GeoJSON, std::move(id_)), - options(std::move(options_)) { + : Source::Impl(SourceType::GeoJSON, std::move(id_)), options(std::move(options_)) { } GeoJSONSource::Impl::Impl(const Impl& other, const GeoJSON& geoJSON) - : Source::Impl(other), - options(other.options) { + : Source::Impl(other), options(other.options) { constexpr double scale = util::EXTENT / util::tileSize; - - if (options.cluster - && geoJSON.is<mapbox::feature::feature_collection<double>>() - && !geoJSON.get<mapbox::feature::feature_collection<double>>().empty()) { + if (options.cluster && geoJSON.is<mapbox::feature::feature_collection<double>>() && + !geoJSON.get<mapbox::feature::feature_collection<double>>().empty()) { mapbox::supercluster::Options clusterOptions; clusterOptions.maxZoom = options.clusterMaxZoom; clusterOptions.extent = util::EXTENT; clusterOptions.radius = ::round(scale * options.clusterRadius); + std::unordered_map<std::string, PropertyExpression<double>> mapExpressions; + std::unordered_map<std::string, PropertyExpression<double>> reduceExpressions; + for (const auto& p : options.clusterProperties) { + mapExpressions.emplace(p.first, + createPropertyExpression<double>(p.second.second.c_str())); + std::stringstream ss; + // [operator, ['accumulated'], ['get', key]] + ss << std::string(R"([")") << p.second.first + << std::string(R"(", ["accumulated"], ["get", ")") << p.first + << std::string(R"("]])"); + reduceExpressions.emplace(p.first, createPropertyExpression<double>(ss.str().c_str())); + } + clusterOptions.map = + [&](const mapbox::feature::property_map& properties) -> mapbox::feature::property_map { + mapbox::feature::property_map ret{}; + for (const auto& p : options.clusterProperties) { + auto feature = mapbox::feature::feature<double>(); + feature.properties = properties; + auto iter = mapExpressions.find(p.first); + if (iter != mapExpressions.end()) { + ret[p.first] = iter->second.evaluate(nullopt, feature); + } + } + return ret; + }; + clusterOptions.reduce = [&](mapbox::feature::property_map& toReturn, + const mapbox::feature::property_map& toFill) { + for (const auto& p : options.clusterProperties) { + auto feature = mapbox::feature::feature<double>(); + feature.properties = toFill; + optional<double> accumulated(toReturn[p.first].get<double>()); + auto iter = reduceExpressions.find(p.first); + if (iter != reduceExpressions.end()) { + toReturn[p.first] = iter->second.evaluate(accumulated, feature); + } + } + }; data = std::make_shared<SuperclusterData>( geoJSON.get<mapbox::feature::feature_collection<double>>(), clusterOptions); } else { diff --git a/vendor/supercluster.hpp b/vendor/supercluster.hpp -Subproject 274ec138306c7b110bf4dde47706aeb43dc8147 +Subproject fd7fafd07143ac1da6b03bff353dc7a4ebe0186 |