summaryrefslogtreecommitdiff
path: root/src/mbgl
diff options
context:
space:
mode:
Diffstat (limited to 'src/mbgl')
-rw-r--r--src/mbgl/style/conversion/geojson_options.cpp69
-rw-r--r--src/mbgl/style/expression/compound_expression.cpp13
-rw-r--r--src/mbgl/style/expression/dsl.cpp23
-rw-r--r--src/mbgl/style/expression/expression.cpp8
-rw-r--r--src/mbgl/style/expression/parsing_context.cpp3
-rw-r--r--src/mbgl/style/sources/geojson_source.cpp2
-rw-r--r--src/mbgl/style/sources/geojson_source_impl.cpp69
-rw-r--r--src/mbgl/style/sources/geojson_source_impl.hpp2
8 files changed, 162 insertions, 27 deletions
diff --git a/src/mbgl/style/conversion/geojson_options.cpp b/src/mbgl/style/conversion/geojson_options.cpp
index 11bd7cc507..08553e34bb 100644
--- a/src/mbgl/style/conversion/geojson_options.cpp
+++ b/src/mbgl/style/conversion/geojson_options.cpp
@@ -1,11 +1,15 @@
#include <mbgl/style/conversion/geojson_options.hpp>
#include <mbgl/style/conversion_impl.hpp>
+#include <mbgl/style/expression/dsl.hpp>
+
+#include <sstream>
namespace mbgl {
namespace style {
namespace conversion {
-optional<GeoJSONOptions> Converter<GeoJSONOptions>::operator()(const Convertible& value, Error& error) const {
+optional<GeoJSONOptions> Converter<GeoJSONOptions>::operator()(const Convertible& value,
+ Error& error) const {
GeoJSONOptions options;
const auto minzoomValue = objectMember(value, "minzoom");
@@ -83,12 +87,71 @@ optional<GeoJSONOptions> Converter<GeoJSONOptions>::operator()(const Convertible
if (toBool(*lineMetricsValue)) {
options.lineMetrics = *toBool(*lineMetricsValue);
} else {
- error = { "GeoJSON source lineMetrics value must be a boolean" };
+ error.message = "GeoJSON source lineMetrics value must be a boolean";
+ return nullopt;
+ }
+ }
+
+ const auto clusterProperties = objectMember(value, "clusterProperties");
+ if (clusterProperties) {
+ if (!isObject(*clusterProperties)) {
+ error.message = "GeoJSON source clusterProperties value must be an object";
+ return nullopt;
+ }
+ GeoJSONOptions::ClusterProperties result;
+ assert(error.message.empty());
+ eachMember(
+ *clusterProperties,
+ [&](const std::string& k,
+ const mbgl::style::conversion::Convertible& v) -> optional<conversion::Error> {
+ // Each property shall be formed as ["key" : [operator, [mapExpression]]]
+ // or ["key" : [[operator, ['accumulated'], ['get', key]], [mapExpression]]]
+ if (!isArray(v) || arrayLength(v) != 2) {
+ error.message =
+ "GeoJSON source clusterProperties member must be an array with length of 2";
+ return nullopt;
+ }
+ auto map = expression::dsl::createExpression(arrayMember(v, 1));
+ if (!map) {
+ error.message =
+ "Failed to convert GeoJSON source clusterProperties map expression";
+ return nullopt;
+ }
+ std::unique_ptr<expression::Expression> reduce;
+ if (isArray(arrayMember(v, 0))) {
+ reduce = expression::dsl::createExpression(arrayMember(v, 0));
+ } else {
+ auto reduceOp = toString(arrayMember(v, 0));
+ if (!reduceOp) {
+ error.message =
+ "GeoJSON source clusterProperties member must contain a valid operator";
+ return nullopt;
+ }
+ std::stringstream ss;
+ // Reformulate reduce expression to [operator, ['accumulated'], ['get', key]]
+ // The reason to create expression via parsing string instead of invoking function
+ // createCompoundExpression is due to expression type disunity can’t be resolved
+ // with current logic of createCompoundExpression
+ ss << std::string(R"([")") << *reduceOp
+ << std::string(R"(", ["accumulated"], ["get", ")") << k
+ << std::string(R"("]])");
+ reduce = expression::dsl::createExpression(ss.str().c_str());
+ }
+ if (!reduce) {
+ error.message =
+ "Failed to convert GeoJSON source clusterProperties reduce expression";
+ return nullopt;
+ }
+ result.emplace(k, std::make_pair(std::move(map), std::move(reduce)));
+ return nullopt;
+ });
+ if (!error.message.empty()) {
return nullopt;
}
+ options.clusterProperties = std::move(result);
}
- return { options };
+ return { std::move(options) };
}
} // namespace conversion
diff --git a/src/mbgl/style/expression/compound_expression.cpp b/src/mbgl/style/expression/compound_expression.cpp
index 34f0f5dea3..c637856ad9 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() {
+ const static auto signature = detail::makeSignature("accumulated", [](const EvaluationContext& params) -> Result<Value> {
+ if (!params.accumulated) {
+ return EvaluationError {
+ "The 'accumulated' expression is unavailable in the current evaluation context."
+ };
+ }
+ return Value(toExpressionValue(*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) {
@@ -871,6 +883,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/dsl.cpp b/src/mbgl/style/expression/dsl.cpp
index 70442de968..fac7dfbbd9 100644
--- a/src/mbgl/style/expression/dsl.cpp
+++ b/src/mbgl/style/expression/dsl.cpp
@@ -10,6 +10,10 @@
#include <mbgl/style/expression/compound_expression.hpp>
#include <mbgl/style/expression/format_expression.hpp>
+#include <mapbox/geojsonvt.hpp>
+#include <mbgl/style/conversion/json.hpp>
+#include <rapidjson/document.h>
+
namespace mbgl {
namespace style {
namespace expression {
@@ -21,6 +25,25 @@ std::unique_ptr<Expression> compound(const char* op, std::vector<std::unique_ptr
assert(result);
return std::move(*result);
}
+
+std::unique_ptr<Expression> createExpression(const char* expr) {
+ using JSValue = rapidjson::GenericValue<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
+ rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> document;
+ document.Parse<0>(expr);
+ if (document.HasParseError()) return nullptr;
+
+ const JSValue* expression = &document;
+ expression::ParsingContext ctx;
+ expression::ParseResult parsed =
+ ctx.parseExpression(mbgl::style::conversion::Convertible(expression));
+ return parsed ? std::move(*parsed) : nullptr;
+}
+
+std::unique_ptr<Expression> createExpression(const mbgl::style::conversion::Convertible& expr) {
+ expression::ParsingContext ctx;
+ expression::ParseResult parsed = ctx.parseExpression(expr);
+ return parsed ? std::move(*parsed) : nullptr;
+}
std::unique_ptr<Expression> error(std::string message) {
return std::make_unique<Error>(std::move(message));
diff --git a/src/mbgl/style/expression/expression.cpp b/src/mbgl/style/expression/expression.cpp
index d61f435eec..6bfda99064 100644
--- a/src/mbgl/style/expression/expression.cpp
+++ b/src/mbgl/style/expression/expression.cpp
@@ -25,13 +25,17 @@ 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));
}
+EvaluationResult Expression::evaluate(optional<mbgl::Value> accumulated, const Feature& feature) const {
+ GeoJSONFeature f(feature);
+ return this->evaluate(EvaluationContext(accumulated, &f));
+}
+
} // 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 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.cpp b/src/mbgl/style/sources/geojson_source.cpp
index 4e3478322d..72a51e212f 100644
--- a/src/mbgl/style/sources/geojson_source.cpp
+++ b/src/mbgl/style/sources/geojson_source.cpp
@@ -9,7 +9,7 @@
namespace mbgl {
namespace style {
-GeoJSONSource::GeoJSONSource(const std::string& id, const GeoJSONOptions& options)
+GeoJSONSource::GeoJSONSource(const std::string& id, optional<GeoJSONOptions> options)
: Source(makeMutable<Impl>(std::move(id), options)) {
}
diff --git a/src/mbgl/style/sources/geojson_source_impl.cpp b/src/mbgl/style/sources/geojson_source_impl.cpp
index 24ac1d7976..c3cb942709 100644
--- a/src/mbgl/style/sources/geojson_source_impl.cpp
+++ b/src/mbgl/style/sources/geojson_source_impl.cpp
@@ -1,6 +1,7 @@
#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/feature.hpp>
#include <mbgl/util/string.hpp>
#include <mapbox/geojsonvt.hpp>
@@ -13,9 +14,9 @@ namespace style {
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 +26,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 +43,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 +55,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);
}
@@ -67,23 +68,53 @@ private:
mapbox::supercluster::Supercluster impl;
};
-GeoJSONSource::Impl::Impl(std::string id_, GeoJSONOptions options_)
- : Source::Impl(SourceType::GeoJSON, std::move(id_)),
- options(std::move(options_)) {
+template <class T>
+T evaluateFeature(const mapbox::feature::feature<double>& f,
+ const std::shared_ptr<expression::Expression>& expression,
+ optional<T> accumulated = nullopt) {
+ const expression::EvaluationResult result = expression->evaluate(accumulated, f);
+ if (result) {
+ optional<T> typed = expression::fromExpressionValue<T>(*result);
+ if (typed) {
+ return std::move(*typed);
+ }
+ }
+ return T();
+}
+
+GeoJSONSource::Impl::Impl(std::string id_, optional<GeoJSONOptions> options_)
+ : Source::Impl(SourceType::GeoJSON, std::move(id_)) {
+ options = options_ ? std::move(*options_) : GeoJSONOptions{};
}
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);
+ Feature feature;
+ clusterOptions.map = [&](const PropertyMap& properties) -> PropertyMap {
+ PropertyMap ret{};
+ for (const auto& p : options.clusterProperties) {
+ feature.properties = properties;
+ ret[p.first] = evaluateFeature<Value>(feature, p.second.first);
+ }
+ return ret;
+ };
+ clusterOptions.reduce = [&](PropertyMap& toReturn, const PropertyMap& toFill) {
+ for (const auto& p : options.clusterProperties) {
+ if (toFill.count(p.first) == 0) {
+ continue;
+ }
+ feature.properties = toFill;
+ optional<Value> accumulated(toReturn[p.first]);
+ toReturn[p.first] = evaluateFeature<Value>(feature, p.second.second, accumulated);
+ }
+ };
data = std::make_shared<SuperclusterData>(
geoJSON.get<mapbox::feature::feature_collection<double>>(), clusterOptions);
} else {
diff --git a/src/mbgl/style/sources/geojson_source_impl.hpp b/src/mbgl/style/sources/geojson_source_impl.hpp
index b88ab35ee0..26b9d95a39 100644
--- a/src/mbgl/style/sources/geojson_source_impl.hpp
+++ b/src/mbgl/style/sources/geojson_source_impl.hpp
@@ -26,7 +26,7 @@ public:
class GeoJSONSource::Impl : public Source::Impl {
public:
- Impl(std::string id, GeoJSONOptions);
+ Impl(std::string id, optional<GeoJSONOptions>);
Impl(const GeoJSONSource::Impl&, const GeoJSON&);
~Impl() final;