summaryrefslogtreecommitdiff
path: root/include/mbgl/style/conversion/filter.hpp
blob: 3ab91e5bbc53723a5561cf9cee50ddd25c241b9a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#pragma once

#include <mbgl/style/filter.hpp>
#include <mbgl/style/conversion.hpp>
#include <mbgl/util/geometry.hpp>

namespace mbgl {
namespace style {
namespace conversion {

template <>
struct Converter<Filter> {
public:
    template <class V>
    Result<Filter> operator()(const V& value) const {
        if (!isArray(value)) {
            return Error { "filter expression must be an array" };
        }

        if (arrayLength(value) < 1) {
            return Error { "filter expression must have at least 1 element" };
        }

        optional<std::string> op = toString(arrayMember(value, 0));
        if (!op) {
            return Error { "filter operator must be a string" };
        }

        if (*op == "==") {
            return convertBinaryFilter<EqualsFilter>(value);
        } else if (*op == "!=") {
            return convertBinaryFilter<NotEqualsFilter>(value);
        } else if (*op == ">") {
            return convertBinaryFilter<GreaterThanFilter>(value);
        } else if (*op == ">=") {
            return convertBinaryFilter<GreaterThanEqualsFilter>(value);
        } else if (*op == "<") {
            return convertBinaryFilter<LessThanFilter>(value);
        } else if (*op == "<=") {
            return convertBinaryFilter<LessThanEqualsFilter>(value);
        } else if (*op == "in") {
            return convertSetFilter<InFilter>(value);
        } else if (*op == "!in") {
            return convertSetFilter<NotInFilter>(value);
        } else if (*op == "all") {
            return convertCompoundFilter<AllFilter>(value);
        } else if (*op == "any") {
            return convertCompoundFilter<AnyFilter>(value);
        } else if (*op == "none") {
            return convertCompoundFilter<NoneFilter>(value);
        } else if (*op == "has") {
            return convertUnaryFilter<HasFilter>(value);
        } else if (*op == "!has") {
           return convertUnaryFilter<NotHasFilter>(value);
        }

        return Error { "filter operator must be one of \"==\", \"!=\", \">\", \">=\", \"<\", \"<=\", \"in\", \"!in\", \"all\", \"any\", \"none\", \"has\", or \"!has\"" };
    }

private:
    Result<Value> normalizeValue(const std::string& key, const optional<Value>& value) const {
        if (!value) {
            return Error { "filter expression value must be a boolean, number, or string" };
        } else if (key != "$type") {
            return *value;
        } else if (*value == std::string("Point")) {
            return Value(uint64_t(FeatureType::Point));
        } else if (*value == std::string("LineString")) {
            return Value(uint64_t(FeatureType::LineString));
        } else if (*value == std::string("Polygon")) {
            return Value(uint64_t(FeatureType::Polygon));
        } else {
            return Error { "value for $type filter must be Point, LineString, or Polygon" };
        }
    }

    template <class FilterType, class V>
    Result<Filter> convertUnaryFilter(const V& value) const {
        if (arrayLength(value) < 2) {
            return Error { "filter expression must have 2 elements" };
        }

        optional<std::string> key = toString(arrayMember(value, 1));
        if (!key) {
            return Error { "filter expression key must be a string" };
        }

        return FilterType { *key };
    }

    template <class FilterType, class V>
    Result<Filter> convertBinaryFilter(const V& value) const {
        if (arrayLength(value) < 3) {
            return Error { "filter expression must have 3 elements" };
        }

        optional<std::string> key = toString(arrayMember(value, 1));
        if (!key) {
            return Error { "filter expression key must be a string" };
        }

        Result<Value> filterValue = normalizeValue(*key, toValue(arrayMember(value, 2)));
        if (!filterValue) {
            return filterValue.error();
        }

        return FilterType { *key, *filterValue };
    }

    template <class FilterType, class V>
    Result<Filter> convertSetFilter(const V& value) const {
        if (arrayLength(value) < 2) {
            return Error { "filter expression must at least 2 elements" };
        }

        optional<std::string> key = toString(arrayMember(value, 1));
        if (!key) {
            return Error { "filter expression key must be a string" };
        }

        std::vector<Value> values;
        for (std::size_t i = 2; i < arrayLength(value); ++i) {
            Result<Value> filterValue = normalizeValue(*key, toValue(arrayMember(value, i)));
            if (!filterValue) {
                return filterValue.error();
            }
            values.push_back(*filterValue);
        }

        return FilterType { *key, std::move(values) };
    }

    template <class FilterType, class V>
    Result<Filter> convertCompoundFilter(const V& value) const {
        std::vector<Filter> filters;
        for (std::size_t i = 1; i < arrayLength(value); ++i) {
            Result<Filter> element = operator()(arrayMember(value, i));
            if (!element) {
                return element.error();
            }
            filters.push_back(*element);
        }

        return FilterType { std::move(filters) };
    }
};

} // namespace conversion
} // namespace style
} // namespace mbgl