From 8a5bff8ee630673c6ebc496322eab94a41ae9353 Mon Sep 17 00:00:00 2001 From: Ivo van Dongen Date: Tue, 31 Jan 2017 15:44:18 +0200 Subject: [core] default value support in categorical function conversion --- cmake/test-files.cmake | 2 + include/mbgl/style/conversion/function.hpp | 33 +++++++- include/mbgl/style/function/camera_function.hpp | 2 +- include/mbgl/style/function/categorical_stops.hpp | 10 +-- include/mbgl/style/function/composite_function.hpp | 23 ++++-- include/mbgl/style/function/exponential_stops.hpp | 2 +- include/mbgl/style/function/identity_stops.hpp | 2 +- include/mbgl/style/function/interval_stops.hpp | 6 +- include/mbgl/style/function/source_function.hpp | 17 ++-- src/mbgl/layout/symbol_layout.cpp | 4 +- src/mbgl/style/conversion/stringify.hpp | 8 ++ src/mbgl/style/function/categorical_stops.cpp | 6 +- src/mbgl/style/function/identity_stops.cpp | 22 +++-- src/mbgl/style/layout_property.hpp | 11 +++ src/mbgl/style/paint_property_binder.hpp | 18 +++-- .../style/possibly_evaluated_property_value.hpp | 7 -- test/src/mbgl/test/stub_geometry_tile_feature.hpp | 34 ++++++++ test/style/conversion/stringify.test.cpp | 6 +- test/style/function/source_function.test.cpp | 94 ++++++++++++++++++++++ 19 files changed, 243 insertions(+), 64 deletions(-) create mode 100644 test/src/mbgl/test/stub_geometry_tile_feature.hpp create mode 100644 test/style/function/source_function.test.cpp diff --git a/cmake/test-files.cmake b/cmake/test-files.cmake index 2e2c297492..9ab9918162 100644 --- a/cmake/test-files.cmake +++ b/cmake/test-files.cmake @@ -50,6 +50,7 @@ set(MBGL_TEST_FILES test/src/mbgl/test/fixture_log_observer.hpp test/src/mbgl/test/stub_file_source.cpp test/src/mbgl/test/stub_file_source.hpp + test/src/mbgl/test/stub_geometry_tile_feature.hpp test/src/mbgl/test/stub_layer_observer.hpp test/src/mbgl/test/stub_style_observer.hpp test/src/mbgl/test/stub_tile_observer.hpp @@ -80,6 +81,7 @@ set(MBGL_TEST_FILES # style/function test/style/function/camera_function.test.cpp + test/style/function/source_function.test.cpp # style test/style/group_by_layout.test.cpp diff --git a/include/mbgl/style/conversion/function.hpp b/include/mbgl/style/conversion/function.hpp index 90b4b95063..7d69fa55c5 100644 --- a/include/mbgl/style/conversion/function.hpp +++ b/include/mbgl/style/conversion/function.hpp @@ -198,6 +198,21 @@ struct Converter> { } }; +template +Result> convertDefaultValue(const V& value) { + auto defaultValueValue = objectMember(value, "defaultValue"); + if (!defaultValueValue) { + return {}; + } + + auto defaultValue = convert(*defaultValueValue); + if (!defaultValue) { + return Error { "wrong type for \"default\": " + defaultValue.error().message }; + } + + return *defaultValue; +} + template struct Converter> { template @@ -221,7 +236,12 @@ struct Converter> { return stops.error(); } - return SourceFunction(*propertyString, *stops); + auto defaultValue = convertDefaultValue(value); + if (!defaultValue) { + return defaultValue.error(); + } + + return SourceFunction(*propertyString, *stops, *defaultValue); } }; @@ -280,6 +300,11 @@ struct Converter> { return Error { "function property must be a string" }; } + auto defaultValue = convertDefaultValue(value); + if (!defaultValue) { + return defaultValue.error(); + } + std::string type = "exponential"; auto typeValue = objectMember(value, "type"); if (typeValue && toString(*typeValue)) { @@ -305,7 +330,7 @@ struct Converter> { inner.stops.emplace(stop.first.second, stop.second); } - return CompositeFunction(*propertyString, convertedStops); + return CompositeFunction(*propertyString, convertedStops, *defaultValue); } else if (type == "interval") { auto stops = convertStops, T>(value); if (!stops) { @@ -318,7 +343,7 @@ struct Converter> { inner.stops.emplace(stop.first.second, stop.second); } - return CompositeFunction(*propertyString, convertedStops); + return CompositeFunction(*propertyString, convertedStops, *defaultValue); } else if (type == "categorical") { auto stops = convertStops, T>(value); if (!stops) { @@ -331,7 +356,7 @@ struct Converter> { inner.stops.emplace(stop.first.second, stop.second); } - return CompositeFunction(*propertyString, convertedStops); + return CompositeFunction(*propertyString, convertedStops, *defaultValue); } else { return Error { "unsupported function type" }; } diff --git a/include/mbgl/style/function/camera_function.hpp b/include/mbgl/style/function/camera_function.hpp index a96978939d..5636b1663c 100644 --- a/include/mbgl/style/function/camera_function.hpp +++ b/include/mbgl/style/function/camera_function.hpp @@ -25,7 +25,7 @@ public: T evaluate(float zoom) const { return stops.match([&] (const auto& s) { - return s.evaluate(Value(double(zoom))); + return s.evaluate(Value(double(zoom))).value_or(T()); }); } diff --git a/include/mbgl/style/function/categorical_stops.hpp b/include/mbgl/style/function/categorical_stops.hpp index 11ec2a0e5a..c8505115ab 100644 --- a/include/mbgl/style/function/categorical_stops.hpp +++ b/include/mbgl/style/function/categorical_stops.hpp @@ -21,20 +21,18 @@ public: using Stops = std::map; Stops stops; - T defaultValue; CategoricalStops() = default; - CategoricalStops(Stops stops_, T defaultValue_ = T()) - : stops(std::move(stops_)), - defaultValue(std::move(defaultValue_)) { + CategoricalStops(Stops stops_) + : stops(std::move(stops_)) { assert(stops.size() > 0); } - T evaluate(const Value&) const; + optional evaluate(const Value&) const; friend bool operator==(const CategoricalStops& lhs, const CategoricalStops& rhs) { - return lhs.stops == rhs.stops && lhs.defaultValue == rhs.defaultValue; + return lhs.stops == rhs.stops; } }; diff --git a/include/mbgl/style/function/composite_function.hpp b/include/mbgl/style/function/composite_function.hpp index 169a455435..f42a5f06f4 100644 --- a/include/mbgl/style/function/composite_function.hpp +++ b/include/mbgl/style/function/composite_function.hpp @@ -43,9 +43,10 @@ public: std::map>, std::map>>>; - CompositeFunction(std::string property_, Stops stops_) + CompositeFunction(std::string property_, Stops stops_, optional defaultValue_ = {}) : property(std::move(property_)), - stops(std::move(stops_)) { + stops(std::move(stops_)), + defaultValue(std::move(defaultValue_)) { } std::tuple, Range> @@ -73,13 +74,17 @@ public: } Range evaluate(Range coveringStops, - const GeometryTileFeature& feature) const { + const GeometryTileFeature& feature, + T finalDefaultValue) const { optional v = feature.getValue(property); if (!v) { - return { T(), T() }; + return { + defaultValue.value_or(finalDefaultValue), + defaultValue.value_or(finalDefaultValue) + }; } auto eval = [&] (const auto& s) { - return s.evaluate(*v); + return s.evaluate(*v).value_or(defaultValue.value_or(finalDefaultValue)); }; return Range { coveringStops.min.match(eval), @@ -87,9 +92,9 @@ public: }; } - T evaluate(float zoom, const GeometryTileFeature& feature) const { + T evaluate(float zoom, const GeometryTileFeature& feature, T finalDefaultValue) const { std::tuple, Range> ranges = coveringRanges(zoom); - Range resultRange = evaluate(std::get<1>(ranges), feature); + Range resultRange = evaluate(std::get<1>(ranges), feature, finalDefaultValue); return util::interpolate( resultRange.min, resultRange.max, @@ -98,11 +103,13 @@ public: friend bool operator==(const CompositeFunction& lhs, const CompositeFunction& rhs) { - return lhs.property == rhs.property && lhs.stops == rhs.stops; + return std::tie(lhs.property, lhs.stops, lhs.defaultValue) + == std::tie(rhs.property, rhs.stops, rhs.defaultValue); } std::string property; Stops stops; + optional defaultValue; }; } // namespace style diff --git a/include/mbgl/style/function/exponential_stops.hpp b/include/mbgl/style/function/exponential_stops.hpp index 7bd8783614..051f5aa9aa 100644 --- a/include/mbgl/style/function/exponential_stops.hpp +++ b/include/mbgl/style/function/exponential_stops.hpp @@ -22,7 +22,7 @@ public: base(base_) { } - T evaluate(const Value& value) const { + optional evaluate(const Value& value) const { if (stops.empty()) { assert(false); return T(); diff --git a/include/mbgl/style/function/identity_stops.hpp b/include/mbgl/style/function/identity_stops.hpp index 4e199f2e15..741ebbbe0c 100644 --- a/include/mbgl/style/function/identity_stops.hpp +++ b/include/mbgl/style/function/identity_stops.hpp @@ -8,7 +8,7 @@ namespace style { template class IdentityStops { public: - T evaluate(const Value&) const; + optional evaluate(const Value&) const; friend bool operator==(const IdentityStops&, const IdentityStops&) { diff --git a/include/mbgl/style/function/interval_stops.hpp b/include/mbgl/style/function/interval_stops.hpp index cf879d730b..50f2b48453 100644 --- a/include/mbgl/style/function/interval_stops.hpp +++ b/include/mbgl/style/function/interval_stops.hpp @@ -18,15 +18,15 @@ public: : stops(std::move(stops_)) { } - T evaluate(const Value& value) const { + optional evaluate(const Value& value) const { if (stops.empty()) { assert(false); - return T(); + return {}; } optional z = numericValue(value); if (!z) { - return T(); + return {}; } auto it = stops.upper_bound(*z); diff --git a/include/mbgl/style/function/source_function.hpp b/include/mbgl/style/function/source_function.hpp index e998be082a..29b1067a19 100644 --- a/include/mbgl/style/function/source_function.hpp +++ b/include/mbgl/style/function/source_function.hpp @@ -28,28 +28,31 @@ public: CategoricalStops, IdentityStops>>; - SourceFunction(std::string property_, Stops stops_) + SourceFunction(std::string property_, Stops stops_, optional defaultValue_ = {}) : property(std::move(property_)), - stops(std::move(stops_)) { + stops(std::move(stops_)), + defaultValue(std::move(defaultValue_)) { } - T evaluate(const GeometryTileFeature& feature) const { + T evaluate(const GeometryTileFeature& feature, T finalDefaultValue) const { optional v = feature.getValue(property); if (!v) { - return T(); + return defaultValue.value_or(finalDefaultValue); } - return stops.match([&] (const auto& s) { - return s.evaluate(*v); + return stops.match([&] (const auto& s) -> T { + return s.evaluate(*v).value_or(defaultValue.value_or(finalDefaultValue)); }); } friend bool operator==(const SourceFunction& lhs, const SourceFunction& rhs) { - return lhs.property == rhs.property && lhs.stops == rhs.stops; + return std::tie(lhs.property, lhs.stops, lhs.defaultValue) + == std::tie(rhs.property, rhs.stops, rhs.defaultValue); } std::string property; Stops stops; + optional defaultValue; }; } // namespace style diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index 5dd36c41bd..665806cb0e 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -129,8 +129,8 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, if (hasIcon) { ft.icon = util::replaceTokens(layout.get(), getValue); - ft.iconOffset = layout.get().evaluate(zoom, *feature); - ft.iconRotation = layout.get().evaluate(zoom, *feature) * util::DEG2RAD; + ft.iconOffset = layout.evaluate(zoom, *feature); + ft.iconRotation = layout.evaluate(zoom, *feature) * util::DEG2RAD; } if (ft.text || ft.icon) { diff --git a/src/mbgl/style/conversion/stringify.hpp b/src/mbgl/style/conversion/stringify.hpp index 71755bd9d6..5aab1076fd 100644 --- a/src/mbgl/style/conversion/stringify.hpp +++ b/src/mbgl/style/conversion/stringify.hpp @@ -330,6 +330,10 @@ void stringify(Writer& writer, const SourceFunction& f) { writer.Key("property"); writer.String(f.property); SourceFunction::Stops::visit(f.stops, StringifyStops { writer }); + if (f.defaultValue) { + writer.Key("default"); + stringify(writer, *f.defaultValue); + } writer.EndObject(); } @@ -339,6 +343,10 @@ void stringify(Writer& writer, const CompositeFunction& f) { writer.Key("property"); writer.String(f.property); CompositeFunction::Stops::visit(f.stops, StringifyStops { writer }); + if (f.defaultValue) { + writer.Key("default"); + stringify(writer, *f.defaultValue); + } writer.EndObject(); } diff --git a/src/mbgl/style/function/categorical_stops.cpp b/src/mbgl/style/function/categorical_stops.cpp index bcdf170f17..21df4fa9f6 100644 --- a/src/mbgl/style/function/categorical_stops.cpp +++ b/src/mbgl/style/function/categorical_stops.cpp @@ -18,13 +18,13 @@ optional categoricalValue(const Value& value) { } template -T CategoricalStops::evaluate(const Value& value) const { +optional CategoricalStops::evaluate(const Value& value) const { auto v = categoricalValue(value); if (!v) { - return defaultValue; + return {}; } auto it = stops.find(*v); - return it == stops.end() ? defaultValue : it->second; + return it == stops.end() ? optional() : it->second; } template class CategoricalStops; diff --git a/src/mbgl/style/function/identity_stops.cpp b/src/mbgl/style/function/identity_stops.cpp index e210b4d773..9bddc3feac 100644 --- a/src/mbgl/style/function/identity_stops.cpp +++ b/src/mbgl/style/function/identity_stops.cpp @@ -7,36 +7,34 @@ namespace mbgl { namespace style { template <> -float IdentityStops::evaluate(const Value& value) const { - return numericValue(value) - .value_or(0.0f); +optional IdentityStops::evaluate(const Value& value) const { + return numericValue(value); } template <> -Color IdentityStops::evaluate(const Value& value) const { +optional IdentityStops::evaluate(const Value& value) const { if (!value.is()) { - return Color::black(); + return {}; } - return Color::parse(value.get()) - .value_or(Color::black()); + return Color::parse(value.get()); } template <> -std::array IdentityStops>::evaluate(const Value& value) const { +optional> IdentityStops>::evaluate(const Value& value) const { if (!value.is>()) { - return {{ 0, 0 }}; + return {}; } const std::vector& vector = value.get>(); if (vector.size() != 2 || !numericValue(vector[0]) || !numericValue(vector[1])) { - return {{ 0, 0 }}; + return {}; } - return {{ + return {{{ *numericValue(vector[0]), *numericValue(vector[1]) - }}; + }}}; } } // namespace style diff --git a/src/mbgl/style/layout_property.hpp b/src/mbgl/style/layout_property.hpp index 7ce2ecc7b8..25cff4b92d 100644 --- a/src/mbgl/style/layout_property.hpp +++ b/src/mbgl/style/layout_property.hpp @@ -17,6 +17,7 @@ public: using UnevaluatedType = PropertyValue; using EvaluatorType = PropertyEvaluator; using EvaluatedType = T; + using Type = T; }; template @@ -25,6 +26,7 @@ public: using UnevaluatedType = DataDrivenPropertyValue; using EvaluatorType = DataDrivenPropertyEvaluator; using EvaluatedType = PossiblyEvaluatedPropertyValue; + using Type = T; }; template @@ -40,6 +42,15 @@ public: class Evaluated : public Tuple { public: using Tuple::Tuple; + + template + typename P::Type evaluate(float z, const GeometryTileFeature& feature) const { + using T = typename P::Type; + return this->template get

().match( + [&] (const T& t) { return t; }, + [&] (const SourceFunction& t) { return t.evaluate(feature, P::defaultValue()); }, + [&] (const CompositeFunction& t) { return t.evaluate(z, feature, P::defaultValue()); }); + } }; class Unevaluated : public Tuple { diff --git a/src/mbgl/style/paint_property_binder.hpp b/src/mbgl/style/paint_property_binder.hpp index 964f33d2ec..79c7692b2f 100644 --- a/src/mbgl/style/paint_property_binder.hpp +++ b/src/mbgl/style/paint_property_binder.hpp @@ -50,12 +50,13 @@ public: using Attributes = gl::Attributes; using Vertex = typename Attributes::Vertex; - SourceFunctionPaintPropertyBinder(SourceFunction function_) - : function(std::move(function_)) { + SourceFunctionPaintPropertyBinder(SourceFunction function_, T defaultValue_) + : function(std::move(function_)), + defaultValue(std::move(defaultValue_)) { } void populateVertexVector(const GeometryTileFeature& feature, std::size_t length) { - AttributeValue value = Attribute::value(function.evaluate(feature)); + AttributeValue value = Attribute::value(function.evaluate(feature, defaultValue)); for (std::size_t i = vertexVector.vertexSize(); i < length; ++i) { vertexVector.emplace_back(Vertex { value }); } @@ -86,6 +87,7 @@ public: private: SourceFunction function; + T defaultValue; gl::VertexVector vertexVector; optional> vertexBuffer; }; @@ -103,13 +105,14 @@ public: using Attributes = gl::Attributes; using Vertex = typename Attributes::Vertex; - CompositeFunctionPaintPropertyBinder(CompositeFunction function_, float zoom) + CompositeFunctionPaintPropertyBinder(CompositeFunction function_, float zoom, T defaultValue_) : function(std::move(function_)), + defaultValue(std::move(defaultValue_)), coveringRanges(function.coveringRanges(zoom)) { } void populateVertexVector(const GeometryTileFeature& feature, std::size_t length) { - Range range = function.evaluate(std::get<1>(coveringRanges), feature); + Range range = function.evaluate(std::get<1>(coveringRanges), feature, defaultValue); AttributeValue min = Attribute::value(range.min); AttributeValue max = Attribute::value(range.max); for (std::size_t i = vertexVector.vertexSize(); i < length; ++i) { @@ -148,6 +151,7 @@ public: private: using InnerStops = typename CompositeFunction::InnerStops; CompositeFunction function; + T defaultValue; std::tuple, Range> coveringRanges; gl::VertexVector vertexVector; optional> vertexBuffer; @@ -171,10 +175,10 @@ public: return ConstantPaintPropertyBinder(constant); }, [&] (const SourceFunction& function) { - return SourceFunctionPaintPropertyBinder(function); + return SourceFunctionPaintPropertyBinder(function, PaintProperty::defaultValue()); }, [&] (const CompositeFunction& function) { - return CompositeFunctionPaintPropertyBinder(function, zoom); + return CompositeFunctionPaintPropertyBinder(function, zoom, PaintProperty::defaultValue()); } )) { } diff --git a/src/mbgl/style/possibly_evaluated_property_value.hpp b/src/mbgl/style/possibly_evaluated_property_value.hpp index bb917442f6..8c3f1780a6 100644 --- a/src/mbgl/style/possibly_evaluated_property_value.hpp +++ b/src/mbgl/style/possibly_evaluated_property_value.hpp @@ -43,13 +43,6 @@ public: auto match(Ts&&... ts) const { return value.match(std::forward(ts)...); } - - T evaluate(float z, const GeometryTileFeature& feature) const { - return value.match( - [&] (const T& t) { return t; }, - [&] (const SourceFunction& t) { return t.evaluate(feature); }, - [&] (const CompositeFunction& t) { return t.evaluate(z, feature); }); - } }; } // namespace style diff --git a/test/src/mbgl/test/stub_geometry_tile_feature.hpp b/test/src/mbgl/test/stub_geometry_tile_feature.hpp new file mode 100644 index 0000000000..21d198a96b --- /dev/null +++ b/test/src/mbgl/test/stub_geometry_tile_feature.hpp @@ -0,0 +1,34 @@ +#include +#include + +namespace mbgl { + +class StubGeometryTileFeature : public GeometryTileFeature { +public: + StubGeometryTileFeature(PropertyMap properties_) + : properties(std::move(properties_)) { + } + + PropertyMap properties; + optional id = {}; + FeatureType type = FeatureType::Point; + GeometryCollection geometry = {}; + + FeatureType getType() const override { + return type; + } + + optional getID() const override { + return id; + } + + optional getValue(const std::string& key) const override { + return properties.count(key) ? properties.at(key) : optional(); + } + + GeometryCollection getGeometries() const override { + return geometry; + } +}; + +} // namespace mbgl diff --git a/test/style/conversion/stringify.test.cpp b/test/style/conversion/stringify.test.cpp index 6814563ceb..dd04789d4b 100644 --- a/test/style/conversion/stringify.test.cpp +++ b/test/style/conversion/stringify.test.cpp @@ -95,6 +95,8 @@ TEST(Stringify, SourceFunction) { "{\"property\":\"property\",\"type\":\"categorical\",\"stops\":[[true,1.0]]}"); ASSERT_EQ(stringify(SourceFunction("property", IdentityStops {})), "{\"property\":\"property\",\"type\":\"identity\"}"); + ASSERT_EQ(stringify(SourceFunction("property", IdentityStops {}, 0.0f)), + "{\"property\":\"property\",\"type\":\"identity\",\"default\":0.0}"); } TEST(Stringify, CompositeFunction) { @@ -102,11 +104,11 @@ TEST(Stringify, CompositeFunction) { std::map> { { 0, ExponentialStops { {{0, 1}}, 2 } }, { 1, ExponentialStops { {{0, 1}}, 2 } } - })), + }, 0.0f)), "{\"property\":\"property\",\"type\":\"exponential\",\"base\":2.0," "\"stops\":[" "[{\"zoom\":0.0,\"value\":0.0},1.0]," - "[{\"zoom\":1.0,\"value\":0.0},1.0]]}"); + "[{\"zoom\":1.0,\"value\":0.0},1.0]],\"default\":0.0}"); } TEST(Stringify, PropertyValue) { diff --git a/test/style/function/source_function.test.cpp b/test/style/function/source_function.test.cpp new file mode 100644 index 0000000000..260620c8d0 --- /dev/null +++ b/test/style/function/source_function.test.cpp @@ -0,0 +1,94 @@ +#include +#include + +#include + +using namespace mbgl; +using namespace mbgl::style; + +using namespace std::string_literals; + +static StubGeometryTileFeature oneInteger { + PropertyMap {{ "property", uint64_t(1) }} +}; + +static StubGeometryTileFeature oneDouble { + PropertyMap {{ "property", 1.0 }} +}; + +static StubGeometryTileFeature oneString { + PropertyMap {{ "property", "1"s }} +}; + +static StubGeometryTileFeature red { + PropertyMap {{ "property", "red"s }} +}; + +static StubGeometryTileFeature oneTwoInteger { + PropertyMap {{ "property", std::vector({uint64_t(1), uint64_t(2)}) }} +}; + +static StubGeometryTileFeature oneTwoDouble { + PropertyMap {{ "property", std::vector({1.0, 2.0}) }} +}; + +static StubGeometryTileFeature oneTwoString { + PropertyMap {{ "property", std::vector({"1"s, "2"s}) }} +}; + +static StubGeometryTileFeature trueFeature { + PropertyMap {{ "property", true }} +}; + +static StubGeometryTileFeature falseFeature { + PropertyMap {{ "property", false }} +}; + +TEST(SourceFunction, Identity) { + EXPECT_EQ(1.0f, SourceFunction("property", IdentityStops(), 0.0f) + .evaluate(oneInteger, 2.0f)); + EXPECT_EQ(1.0f, SourceFunction("property", IdentityStops(), 0.0f) + .evaluate(oneDouble, 2.0f)); + EXPECT_EQ(0.0f, SourceFunction("property", IdentityStops(), 0.0f) + .evaluate(oneString, 2.0f)); + EXPECT_EQ(2.0f, SourceFunction("property", IdentityStops()) + .evaluate(oneString, 2.0f)); + + EXPECT_EQ(Color::red(), SourceFunction("property", IdentityStops(), Color::black()) + .evaluate(red, Color::black())); + EXPECT_EQ(Color::black(), SourceFunction("property", IdentityStops(), Color::black()) + .evaluate(oneInteger, Color::black())); + + std::array zeroArray {{ 0, 0 }}; + EXPECT_EQ((std::array {{ 1, 2 }}), (SourceFunction>("property", IdentityStops>(), zeroArray) + .evaluate(oneTwoInteger, zeroArray))); + EXPECT_EQ((std::array {{ 1, 2 }}), (SourceFunction>("property", IdentityStops>(), zeroArray) + .evaluate(oneTwoDouble, zeroArray))); + EXPECT_EQ((std::array {{ 0, 0 }}), (SourceFunction>("property", IdentityStops>(), zeroArray) + .evaluate(oneTwoString, zeroArray))); +} + +TEST(SourceFunction, Categorical) { + EXPECT_EQ(1.0f, SourceFunction("property", CategoricalStops({{ int64_t(1), 1.0f }})) + .evaluate(oneInteger, 0.0f)); + EXPECT_EQ(1.0f, SourceFunction("property", CategoricalStops({{ int64_t(1), 1.0f }})) + .evaluate(oneDouble, 0.0f)); + EXPECT_EQ(0.0f, SourceFunction("property", CategoricalStops({{ int64_t(1), 1.0f }})) + .evaluate(oneString, 0.0f)); + + EXPECT_EQ(0.0f, SourceFunction("property", CategoricalStops({{ "1"s, 1.0f }})) + .evaluate(oneInteger, 0.0f)); + EXPECT_EQ(0.0f, SourceFunction("property", CategoricalStops({{ "1"s, 1.0f }})) + .evaluate(oneDouble, 0.0f)); + EXPECT_EQ(1.0f, SourceFunction("property", CategoricalStops({{ "1"s, 1.0f }})) + .evaluate(oneString, 0.0f)); + + EXPECT_EQ(1.0f, SourceFunction("property", CategoricalStops({{ true, 1.0f }})) + .evaluate(trueFeature, 0.0f)); + EXPECT_EQ(0.0f, SourceFunction("property", CategoricalStops({{ true, 1.0f }})) + .evaluate(falseFeature, 0.0f)); + EXPECT_EQ(0.0f, SourceFunction("property", CategoricalStops({{ false, 1.0f }})) + .evaluate(trueFeature, 0.0f)); + EXPECT_EQ(1.0f, SourceFunction("property", CategoricalStops({{ false, 1.0f }})) + .evaluate(falseFeature, 0.0f)); +} -- cgit v1.2.1