[libshumate] vector: Add filter expressions
- From: Marcus Lundblad <mlundblad src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libshumate] vector: Add filter expressions
- Date: Mon, 1 Nov 2021 22:00:05 +0000 (UTC)
commit 45e461b81f3730bfe7e64f41cb57e5160f14f684
Author: James Westman <james jwestman net>
Date: Sat Aug 28 23:16:37 2021 -0500
vector: Add filter expressions
These expressions are used primarily to show only certain features in a
layer (for example, a "landuse" layer might only cover certain
landuses).
demos/map-style.json | 12 +
shumate/meson.build | 2 +
shumate/shumate-vector-style.c | 5 +-
.../shumate-vector-expression-filter-private.h | 31 ++
shumate/vector/shumate-vector-expression-filter.c | 393 +++++++++++++++++++++
shumate/vector/shumate-vector-expression.c | 11 +-
shumate/vector/shumate-vector-layer.c | 15 +-
.../vector/shumate-vector-render-scope-private.h | 2 +
shumate/vector/shumate-vector-render-scope.c | 50 +++
shumate/vector/shumate-vector-value-private.h | 3 +
shumate/vector/shumate-vector-value.c | 30 ++
tests/data/0.pbf | Bin 0 -> 435 bytes
tests/data/tests.gresource.xml | 1 +
tests/vector-expression.c | 119 +++++++
14 files changed, 665 insertions(+), 9 deletions(-)
---
diff --git a/demos/map-style.json b/demos/map-style.json
index 79d3c2c..52dc5c0 100644
--- a/demos/map-style.json
+++ b/demos/map-style.json
@@ -24,6 +24,18 @@
"id": "country_boundary",
"type": "line",
"source-layer": "admin_boundary",
+ "filter": ["==", "admin_level", 2],
+ "paint": {
+ "line-color": "#9a9996",
+ "line-opacity": 0.7,
+ "line-width": 1.2
+ }
+ },
+ {
+ "id": "state_boundary",
+ "type": "line",
+ "source-layer": "admin_boundary",
+ "filter": ["==", "admin_level", 4],
"paint": {
"line-color": "#9a9996",
"line-opacity": 0.5,
diff --git a/shumate/meson.build b/shumate/meson.build
index e49847f..08acdc7 100644
--- a/shumate/meson.build
+++ b/shumate/meson.build
@@ -28,6 +28,7 @@ libshumate_private_h = [
'vector/shumate-vector-background-layer-private.h',
'vector/shumate-vector-expression-private.h',
+ 'vector/shumate-vector-expression-filter-private.h',
'vector/shumate-vector-expression-interpolate-private.h',
'vector/shumate-vector-expression-literal-private.h',
'vector/shumate-vector-fill-layer-private.h',
@@ -65,6 +66,7 @@ libshumate_sources = [
'vector/shumate-vector-background-layer.c',
'vector/shumate-vector-expression.c',
'vector/shumate-vector-expression-interpolate.c',
+ 'vector/shumate-vector-expression-filter.c',
'vector/shumate-vector-expression-literal.c',
'vector/shumate-vector-fill-layer.c',
'vector/shumate-vector-layer.c',
diff --git a/shumate/shumate-vector-style.c b/shumate/shumate-vector-style.c
index c692e4a..6726a5c 100644
--- a/shumate/shumate-vector-style.c
+++ b/shumate/shumate-vector-style.c
@@ -173,7 +173,10 @@ shumate_vector_style_initable_init (GInitable *initable,
return FALSE;
if (!(layer = shumate_vector_layer_create_from_json (layer_obj, error)))
- return FALSE;
+ {
+ g_prefix_error (error, "layer '%s': ", json_object_get_string_member (layer_obj, "id"));
+ return FALSE;
+ }
g_ptr_array_add (self->layers, layer);
}
diff --git a/shumate/vector/shumate-vector-expression-filter-private.h
b/shumate/vector/shumate-vector-expression-filter-private.h
new file mode 100644
index 0000000..78a0072
--- /dev/null
+++ b/shumate/vector/shumate-vector-expression-filter-private.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 James Westman <james jwestman net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+#include "shumate-vector-expression-private.h"
+
+G_BEGIN_DECLS
+
+#define SHUMATE_TYPE_VECTOR_EXPRESSION_FILTER (shumate_vector_expression_filter_get_type())
+G_DECLARE_FINAL_TYPE (ShumateVectorExpressionFilter, shumate_vector_expression_filter, SHUMATE,
VECTOR_EXPRESSION_FILTER, ShumateVectorExpression)
+
+ShumateVectorExpression *shumate_vector_expression_filter_from_json_array (JsonArray *array,
+ GError **error);
+
+G_END_DECLS
diff --git a/shumate/vector/shumate-vector-expression-filter.c
b/shumate/vector/shumate-vector-expression-filter.c
new file mode 100644
index 0000000..ecca473
--- /dev/null
+++ b/shumate/vector/shumate-vector-expression-filter.c
@@ -0,0 +1,393 @@
+/*
+ * Copyright (C) 2021 James Westman <james jwestman net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+
+#include "shumate-vector-style.h"
+#include "shumate-vector-expression-filter-private.h"
+#include "shumate-vector-expression-literal-private.h"
+
+typedef enum {
+ EXPR_NOT,
+ EXPR_NONE,
+ EXPR_ANY,
+ EXPR_ALL,
+ EXPR_HAS,
+ EXPR_NOT_HAS,
+ EXPR_GET,
+ EXPR_IN,
+ EXPR_NOT_IN,
+ EXPR_EQ,
+ EXPR_NE,
+ EXPR_GT,
+ EXPR_LT,
+ EXPR_GE,
+ EXPR_LE,
+} ExpressionType;
+
+struct _ShumateVectorExpressionFilter
+{
+ ShumateVectorExpression parent_instance;
+
+ ExpressionType type;
+ GPtrArray *expressions;
+};
+
+G_DEFINE_TYPE (ShumateVectorExpressionFilter, shumate_vector_expression_filter,
SHUMATE_TYPE_VECTOR_EXPRESSION)
+
+
+ShumateVectorExpression *
+shumate_vector_expression_filter_from_json_array (JsonArray *array, GError **error)
+{
+ g_autoptr(ShumateVectorExpressionFilter) self = g_object_new (SHUMATE_TYPE_VECTOR_EXPRESSION_FILTER, NULL);
+ JsonNode *op_node;
+ const char *op;
+ int expect_exprs = -1;
+ int expect_ge_exprs = -1;
+
+ if (json_array_get_length (array) == 0)
+ {
+ g_set_error (error,
+ SHUMATE_STYLE_ERROR,
+ SHUMATE_STYLE_ERROR_INVALID_EXPRESSION,
+ "Expected first element of filter array to be a string");
+ return NULL;
+ }
+
+ op_node = json_array_get_element (array, 0);
+ if (!JSON_NODE_HOLDS_VALUE (op_node) || json_node_get_value_type (op_node) != G_TYPE_STRING)
+ {
+ g_set_error (error,
+ SHUMATE_STYLE_ERROR,
+ SHUMATE_STYLE_ERROR_INVALID_EXPRESSION,
+ "Expected first element of filter array to be a string");
+ return NULL;
+ }
+
+ op = json_node_get_string (op_node);
+
+ if (g_strcmp0 ("!", op) == 0)
+ {
+ self->type = EXPR_NOT;
+ expect_exprs = 1;
+ }
+ else if (g_strcmp0 ("none", op) == 0)
+ self->type = EXPR_NONE;
+ else if (g_strcmp0 ("any", op) == 0)
+ self->type = EXPR_ANY;
+ else if (g_strcmp0 ("all", op) == 0)
+ self->type = EXPR_ALL;
+ else if (g_strcmp0 ("has", op) == 0)
+ {
+ self->type = EXPR_HAS;
+ expect_exprs = 1;
+ }
+ else if (g_strcmp0 ("!has", op) == 0)
+ {
+ self->type = EXPR_NOT_HAS;
+ expect_exprs = 1;
+ }
+ else if (g_strcmp0 ("in", op) == 0)
+ {
+ self->type = EXPR_IN;
+ expect_ge_exprs = 1;
+ }
+ else if (g_strcmp0 ("!in", op) == 0)
+ {
+ self->type = EXPR_NOT_IN;
+ expect_ge_exprs = 1;
+ }
+ else if (g_strcmp0 ("==", op) == 0)
+ {
+ self->type = EXPR_EQ;
+ expect_exprs = 2;
+ }
+ else if (g_strcmp0 ("!=", op) == 0)
+ {
+ self->type = EXPR_NE;
+ expect_exprs = 2;
+ }
+ else if (g_strcmp0 (">", op) == 0)
+ {
+ self->type = EXPR_GT;
+ expect_exprs = 2;
+ }
+ else if (g_strcmp0 ("<", op) == 0)
+ {
+ self->type = EXPR_LT;
+ expect_exprs = 2;
+ }
+ else if (g_strcmp0 (">=", op) == 0)
+ {
+ self->type = EXPR_GE;
+ expect_exprs = 2;
+ }
+ else if (g_strcmp0 ("<=", op) == 0)
+ {
+ self->type = EXPR_LE;
+ expect_exprs = 2;
+ }
+ else
+ {
+ g_set_error (error,
+ SHUMATE_STYLE_ERROR,
+ SHUMATE_STYLE_ERROR_INVALID_EXPRESSION,
+ "Unrecognized operator %s", op);
+ return FALSE;
+ }
+
+ if (expect_exprs > 0 && json_array_get_length (array) - 1 != expect_exprs)
+ {
+ g_set_error (error,
+ SHUMATE_STYLE_ERROR,
+ SHUMATE_STYLE_ERROR_INVALID_EXPRESSION,
+ "Operator `%s` expected exactly %d arguments, got %d",
+ op, expect_exprs, json_array_get_length (array) - 1);
+ return FALSE;
+ }
+
+ if (expect_ge_exprs > 0 && json_array_get_length (array) - 1 < expect_ge_exprs)
+ {
+ g_set_error (error,
+ SHUMATE_STYLE_ERROR,
+ SHUMATE_STYLE_ERROR_INVALID_EXPRESSION,
+ "Operator `%s` expected at least %d arguments, got %d",
+ op, expect_ge_exprs, json_array_get_length (array) - 1);
+ return FALSE;
+ }
+
+ for (int i = 1; i < json_array_get_length (array); i ++)
+ {
+ JsonNode *arg = json_array_get_element (array, i);
+ g_autoptr(ShumateVectorExpression) expr = NULL;
+
+ if (i == 1
+ && expect_exprs == 2
+ && JSON_NODE_HOLDS_VALUE (arg)
+ && json_node_get_value_type (arg) == G_TYPE_STRING)
+ {
+ /* If the first argument of 2-argument function is a string,
+ * convert it to a GET expression so we can do things like
+ * ["==", "admin_level", 2] */
+ g_auto(ShumateVectorValue) value = SHUMATE_VECTOR_VALUE_INIT;
+
+ expr = g_object_new (SHUMATE_TYPE_VECTOR_EXPRESSION_FILTER, NULL);
+ ((ShumateVectorExpressionFilter *)expr)->type = EXPR_GET;
+
+ shumate_vector_value_set_string (&value, json_node_get_string (arg));
+ g_ptr_array_add (((ShumateVectorExpressionFilter *)expr)->expressions,
shumate_vector_expression_literal_new (&value));
+ }
+ else if (!(expr = shumate_vector_expression_from_json (arg, error)))
+ return NULL;
+
+ g_ptr_array_add (self->expressions, g_steal_pointer (&expr));
+ }
+
+ return (ShumateVectorExpression *)g_steal_pointer (&self);
+}
+
+
+static void
+shumate_vector_expression_filter_finalize (GObject *object)
+{
+ ShumateVectorExpressionFilter *self = (ShumateVectorExpressionFilter *)object;
+
+ g_clear_pointer (&self->expressions, g_ptr_array_unref);
+
+ G_OBJECT_CLASS (shumate_vector_expression_filter_parent_class)->finalize (object);
+}
+
+
+static gboolean
+shumate_vector_expression_filter_eval (ShumateVectorExpression *expr,
+ ShumateVectorRenderScope *scope,
+ ShumateVectorValue *out)
+{
+ ShumateVectorExpressionFilter *self = (ShumateVectorExpressionFilter *)expr;
+ g_auto(ShumateVectorValue) value = SHUMATE_VECTOR_VALUE_INIT;
+ g_auto(ShumateVectorValue) value2 = SHUMATE_VECTOR_VALUE_INIT;
+ gboolean inverted = FALSE;
+ gboolean boolean;
+ double number, number2;
+ g_autofree char *string = NULL;
+ ShumateVectorExpression **expressions = (ShumateVectorExpression **)self->expressions->pdata;
+ guint n_expressions = self->expressions->len;
+
+ switch (self->type)
+ {
+ case EXPR_NOT:
+ g_assert (n_expressions == 1);
+
+ if (!shumate_vector_expression_eval (expressions[0], scope, &value))
+ return FALSE;
+ if (!shumate_vector_value_get_boolean (&value, &boolean))
+ return FALSE;
+
+ shumate_vector_value_set_boolean (out, !boolean);
+ return TRUE;
+
+ case EXPR_NONE:
+ inverted = TRUE;
+ G_GNUC_FALLTHROUGH;
+ case EXPR_ANY:
+ for (int i = 0; i < n_expressions; i ++)
+ {
+ if (!shumate_vector_expression_eval (expressions[i], scope, &value))
+ return FALSE;
+ if (!shumate_vector_value_get_boolean (&value, &boolean))
+ return FALSE;
+
+ if (boolean)
+ {
+ shumate_vector_value_set_boolean (out, TRUE ^ inverted);
+ return TRUE;
+ }
+ }
+
+ shumate_vector_value_set_boolean (out, FALSE ^ inverted);
+ return TRUE;
+
+ case EXPR_ALL:
+ for (int i = 0; i < n_expressions; i ++)
+ {
+ if (!shumate_vector_expression_eval (expressions[i], scope, &value))
+ return FALSE;
+ if (!shumate_vector_value_get_boolean (&value, &boolean))
+ return FALSE;
+
+ if (!boolean)
+ {
+ shumate_vector_value_set_boolean (out, FALSE);
+ return TRUE;
+ }
+ }
+
+ shumate_vector_value_set_boolean (out, TRUE);
+ return TRUE;
+
+ case EXPR_NOT_HAS:
+ inverted = TRUE;
+ G_GNUC_FALLTHROUGH;
+ case EXPR_HAS:
+ g_assert (n_expressions == 1);
+
+ string = shumate_vector_expression_eval_string (expressions[0], scope, NULL);
+ shumate_vector_render_scope_get_variable (scope, string, &value);
+ shumate_vector_value_set_boolean (out, !shumate_vector_value_is_null (&value) ^ inverted);
+ return TRUE;
+
+ case EXPR_GET:
+ g_assert (n_expressions == 1);
+
+ string = shumate_vector_expression_eval_string (expressions[0], scope, NULL);
+ shumate_vector_render_scope_get_variable (scope, string, &value);
+ shumate_vector_value_copy (&value, out);
+ return TRUE;
+
+ case EXPR_NOT_IN:
+ inverted = TRUE;
+ G_GNUC_FALLTHROUGH;
+ case EXPR_IN:
+ g_assert (n_expressions >= 1);
+
+ shumate_vector_expression_eval (expressions[0], scope, &value);
+
+ for (int i = 1; i < n_expressions; i ++)
+ {
+ shumate_vector_expression_eval (expressions[i], scope, &value2);
+ if (shumate_vector_value_equal (&value, &value2))
+ {
+ shumate_vector_value_set_boolean (out, TRUE ^ inverted);
+ return TRUE;
+ }
+ }
+
+ shumate_vector_value_set_boolean (out, FALSE ^ inverted);
+ return TRUE;
+
+ case EXPR_NE:
+ inverted = TRUE;
+ G_GNUC_FALLTHROUGH;
+ case EXPR_EQ:
+ g_assert (n_expressions == 2);
+
+ if (!shumate_vector_expression_eval (expressions[0], scope, &value))
+ return FALSE;
+ if (!shumate_vector_expression_eval (expressions[1], scope, &value2))
+ return FALSE;
+
+ shumate_vector_value_set_boolean (out, shumate_vector_value_equal (&value, &value2) ^ inverted);
+ return TRUE;
+
+ case EXPR_LE:
+ inverted = TRUE;
+ G_GNUC_FALLTHROUGH;
+ case EXPR_GT:
+ g_assert (n_expressions == 2);
+
+ if (!shumate_vector_expression_eval (expressions[0], scope, &value))
+ return FALSE;
+ if (!shumate_vector_expression_eval (expressions[1], scope, &value2))
+ return FALSE;
+
+ if (!shumate_vector_value_get_number (&value, &number))
+ return FALSE;
+ if (!shumate_vector_value_get_number (&value2, &number2))
+ return FALSE;
+
+ shumate_vector_value_set_boolean (out, (number > number2) ^ inverted);
+ return TRUE;
+
+ case EXPR_GE:
+ inverted = TRUE;
+ G_GNUC_FALLTHROUGH;
+ case EXPR_LT:
+ g_assert (n_expressions == 2);
+
+ if (!shumate_vector_expression_eval (expressions[0], scope, &value))
+ return FALSE;
+ if (!shumate_vector_expression_eval (expressions[1], scope, &value2))
+ return FALSE;
+
+ if (!shumate_vector_value_get_number (&value, &number))
+ return FALSE;
+ if (!shumate_vector_value_get_number (&value2, &number2))
+ return FALSE;
+
+ shumate_vector_value_set_boolean (out, (number < number2) ^ inverted);
+ return TRUE;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+shumate_vector_expression_filter_class_init (ShumateVectorExpressionFilterClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ ShumateVectorExpressionClass *expr_class = SHUMATE_VECTOR_EXPRESSION_CLASS (klass);
+
+ object_class->finalize = shumate_vector_expression_filter_finalize;
+ expr_class->eval = shumate_vector_expression_filter_eval;
+}
+
+
+static void
+shumate_vector_expression_filter_init (ShumateVectorExpressionFilter *self)
+{
+ self->expressions = g_ptr_array_new_with_free_func (g_object_unref);
+}
diff --git a/shumate/vector/shumate-vector-expression.c b/shumate/vector/shumate-vector-expression.c
index 14862a2..045bfd1 100644
--- a/shumate/vector/shumate-vector-expression.c
+++ b/shumate/vector/shumate-vector-expression.c
@@ -18,6 +18,7 @@
#include "shumate-vector-style.h"
#include "shumate-vector-expression-private.h"
+#include "shumate-vector-expression-filter-private.h"
#include "shumate-vector-expression-interpolate-private.h"
#include "shumate-vector-expression-literal-private.h"
#include "shumate-vector-value-private.h"
@@ -50,14 +51,10 @@ shumate_vector_expression_from_json (JsonNode *json, GError **error)
}
else if (JSON_NODE_HOLDS_OBJECT (json))
return shumate_vector_expression_interpolate_from_json_obj (json_node_get_object (json), error);
+ else if (JSON_NODE_HOLDS_ARRAY (json))
+ return shumate_vector_expression_filter_from_json_array (json_node_get_array (json), error);
else
- {
- g_set_error (error,
- SHUMATE_STYLE_ERROR,
- SHUMATE_STYLE_ERROR_INVALID_EXPRESSION,
- "Unsupported expression type");
- return NULL;
- }
+ g_assert_not_reached ();
}
diff --git a/shumate/vector/shumate-vector-layer.c b/shumate/vector/shumate-vector-layer.c
index 8bacdc6..f665e79 100644
--- a/shumate/vector/shumate-vector-layer.c
+++ b/shumate/vector/shumate-vector-layer.c
@@ -17,6 +17,7 @@
#include <json-glib/json-glib.h>
#include "shumate-vector-background-layer-private.h"
+#include "shumate-vector-expression-private.h"
#include "shumate-vector-fill-layer-private.h"
#include "shumate-vector-layer-private.h"
#include "shumate-vector-line-layer-private.h"
@@ -28,6 +29,8 @@ typedef struct
double minzoom;
double maxzoom;
char *source_layer;
+ ShumateVectorExpression *filter;
+
} ShumateVectorLayerPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (ShumateVectorLayer, shumate_vector_layer, G_TYPE_OBJECT)
@@ -38,6 +41,7 @@ shumate_vector_layer_create_from_json (JsonObject *object, GError **error)
{
ShumateVectorLayer *layer;
ShumateVectorLayerPrivate *priv;
+ JsonNode *filter;
const char *type = json_object_get_string_member_with_default (object, "type", NULL);
if (type == NULL)
@@ -68,6 +72,13 @@ shumate_vector_layer_create_from_json (JsonObject *object, GError **error)
priv->maxzoom = json_object_get_double_member_with_default (object, "maxzoom", 1000000000.0);
priv->source_layer = g_strdup (json_object_get_string_member_with_default (object, "source-layer", NULL));
+ filter = json_object_get_member (object, "filter");
+ if (filter != NULL)
+ {
+ if (!(priv->filter = shumate_vector_expression_from_json (filter, error)))
+ return NULL;
+ }
+
return layer;
}
@@ -79,6 +90,7 @@ shumate_vector_layer_finalize (GObject *object)
ShumateVectorLayerPrivate *priv = shumate_vector_layer_get_instance_private (self);
g_clear_pointer (&priv->source_layer, g_free);
+ g_clear_object (&priv->filter);
G_OBJECT_CLASS (shumate_vector_layer_parent_class)->finalize (object);
}
@@ -135,7 +147,8 @@ shumate_vector_layer_render (ShumateVectorLayer *self, ShumateVectorRenderScope
for (int j = 0; j < scope->layer->n_features; j ++)
{
scope->feature = scope->layer->features[j];
- SHUMATE_VECTOR_LAYER_GET_CLASS (self)->render (self, scope);
+ if (priv->filter == NULL || shumate_vector_expression_eval_boolean (priv->filter, scope, FALSE))
+ SHUMATE_VECTOR_LAYER_GET_CLASS (self)->render (self, scope);
}
cairo_restore (scope->cr);
diff --git a/shumate/vector/shumate-vector-render-scope-private.h
b/shumate/vector/shumate-vector-render-scope-private.h
index e28c043..52242f5 100644
--- a/shumate/vector/shumate-vector-render-scope-private.h
+++ b/shumate/vector/shumate-vector-render-scope-private.h
@@ -20,6 +20,7 @@
#include <glib-object.h>
#include <cairo/cairo.h>
#include "vector_tile.pb-c.h"
+#include "shumate-vector-value-private.h"
typedef struct {
cairo_t *cr;
@@ -35,3 +36,4 @@ typedef struct {
gboolean shumate_vector_render_scope_find_layer (ShumateVectorRenderScope *self, const char *layer_name);
void shumate_vector_render_scope_exec_geometry (ShumateVectorRenderScope *self);
+void shumate_vector_render_scope_get_variable (ShumateVectorRenderScope *self, const char *variable,
ShumateVectorValue *value);
diff --git a/shumate/vector/shumate-vector-render-scope.c b/shumate/vector/shumate-vector-render-scope.c
index 58f8a1c..e544b4e 100644
--- a/shumate/vector/shumate-vector-render-scope.c
+++ b/shumate/vector/shumate-vector-render-scope.c
@@ -85,3 +85,53 @@ shumate_vector_render_scope_exec_geometry (ShumateVectorRenderScope *self)
}
}
}
+
+
+void
+shumate_vector_render_scope_get_variable (ShumateVectorRenderScope *self, const char *variable,
ShumateVectorValue *value)
+{
+ shumate_vector_value_unset (value);
+
+ if (g_strcmp0 (variable, "zoom") == 0)
+ {
+ shumate_vector_value_set_number (value, self->zoom_level);
+ return;
+ }
+
+ if (self->feature == NULL)
+ return;
+
+ if (g_strcmp0 ("$type", variable) == 0)
+ {
+ switch (self->feature->type)
+ {
+ case VECTOR_TILE__TILE__GEOM_TYPE__POINT:
+ shumate_vector_value_set_string (value, "Point");
+ return;
+ case VECTOR_TILE__TILE__GEOM_TYPE__LINESTRING:
+ shumate_vector_value_set_string (value, "LineString");
+ return;
+ case VECTOR_TILE__TILE__GEOM_TYPE__POLYGON:
+ shumate_vector_value_set_string (value, "Polygon");
+ return;
+ default:
+ shumate_vector_value_unset (value);
+ return;
+ }
+ }
+
+ for (int i = 1; i < self->feature->n_tags; i += 2)
+ {
+ int key = self->feature->tags[i - 1];
+ int val = self->feature->tags[i];
+
+ if (key >= self->layer->n_keys || val >= self->layer->n_values)
+ return;
+
+ if (g_strcmp0 (self->layer->keys[key], variable) == 0)
+ {
+ shumate_vector_value_set_from_feature_value (value, self->layer->values[val]);
+ return;
+ }
+ }
+}
diff --git a/shumate/vector/shumate-vector-value-private.h b/shumate/vector/shumate-vector-value-private.h
index a0bfb56..33ad778 100644
--- a/shumate/vector/shumate-vector-value-private.h
+++ b/shumate/vector/shumate-vector-value-private.h
@@ -19,6 +19,7 @@
#include <json-glib/json-glib.h>
#include <gtk/gtk.h>
+#include "vector_tile.pb-c.h"
#define SHUMATE_VECTOR_COLOR_BLACK ((GdkRGBA) {.red=0, .green=0, .blue=0, .alpha=1})
@@ -39,8 +40,10 @@ typedef struct {
#define SHUMATE_VECTOR_VALUE_INIT ((ShumateVectorValue) {.type = 0})
gboolean shumate_vector_value_set_from_g_value (ShumateVectorValue *self, const GValue *value);
+void shumate_vector_value_set_from_feature_value (ShumateVectorValue *self, VectorTile__Tile__Value *value);
void shumate_vector_value_unset (ShumateVectorValue *self);
+gboolean shumate_vector_value_is_null (ShumateVectorValue *self);
void shumate_vector_value_copy (ShumateVectorValue *self, ShumateVectorValue *out);
G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC (ShumateVectorValue, shumate_vector_value_unset)
diff --git a/shumate/vector/shumate-vector-value.c b/shumate/vector/shumate-vector-value.c
index 294dea8..4d1751e 100644
--- a/shumate/vector/shumate-vector-value.c
+++ b/shumate/vector/shumate-vector-value.c
@@ -62,6 +62,29 @@ shumate_vector_value_set_from_g_value (ShumateVectorValue *self, const GValue *v
return TRUE;
}
+
+void
+shumate_vector_value_set_from_feature_value (ShumateVectorValue *self, VectorTile__Tile__Value *value)
+{
+ if (value->has_int_value)
+ shumate_vector_value_set_number (self, value->int_value);
+ else if (value->has_uint_value)
+ shumate_vector_value_set_number (self, value->uint_value);
+ else if (value->has_sint_value)
+ shumate_vector_value_set_number (self, value->sint_value);
+ else if (value->has_float_value)
+ shumate_vector_value_set_number (self, value->float_value);
+ else if (value->has_double_value)
+ shumate_vector_value_set_number (self, value->double_value);
+ else if (value->has_bool_value)
+ shumate_vector_value_set_boolean (self, value->bool_value);
+ else if (value->string_value != NULL)
+ shumate_vector_value_set_string (self, value->string_value);
+ else
+ shumate_vector_value_unset (self);
+}
+
+
void
shumate_vector_value_unset (ShumateVectorValue *self)
{
@@ -71,6 +94,13 @@ shumate_vector_value_unset (ShumateVectorValue *self)
}
+gboolean
+shumate_vector_value_is_null (ShumateVectorValue *self)
+{
+ return self->type == TYPE_NULL;
+}
+
+
void
shumate_vector_value_copy (ShumateVectorValue *self, ShumateVectorValue *out)
{
diff --git a/tests/data/0.pbf b/tests/data/0.pbf
new file mode 100644
index 0000000..c93ed44
Binary files /dev/null and b/tests/data/0.pbf differ
diff --git a/tests/data/tests.gresource.xml b/tests/data/tests.gresource.xml
index 6359a7a..fb3fe80 100644
--- a/tests/data/tests.gresource.xml
+++ b/tests/data/tests.gresource.xml
@@ -2,5 +2,6 @@
<gresources>
<gresource prefix="/org/gnome/shumate/Tests">
<file>style.json</file>
+ <file>0.pbf</file>
</gresource>
</gresources>
diff --git a/tests/vector-expression.c b/tests/vector-expression.c
index 837881e..c9b33e7 100644
--- a/tests/vector-expression.c
+++ b/tests/vector-expression.c
@@ -108,6 +108,122 @@ test_vector_expression_interpolate_color (void)
}
+static gboolean
+filter_with_scope (ShumateVectorRenderScope *scope, const char *filter)
+{
+ g_autoptr(GError) error = NULL;
+ g_autoptr(JsonNode) node = json_from_string (filter, NULL);
+ g_autoptr(ShumateVectorExpression) expression = shumate_vector_expression_from_json (node, &error);
+
+ g_assert_no_error (error);
+
+ return shumate_vector_expression_eval_boolean (expression, scope, FALSE);
+}
+
+
+static gboolean
+filter (const char *filter)
+{
+ return filter_with_scope (NULL, filter);
+}
+
+
+static void
+test_vector_expression_basic_filter (void)
+{
+ g_assert_true (filter ("true"));
+ g_assert_false (filter ("false"));
+ g_assert_false (filter ("[\"!\", true]"));
+ g_assert_true (filter ("[\"!\", false]"));
+ g_assert_true (filter ("[\"any\", false, true]"));
+ g_assert_false (filter ("[\"any\", false, false]"));
+ g_assert_true (filter ("[\"none\", false, false]"));
+ g_assert_false (filter ("[\"none\", true, false]"));
+ g_assert_true (filter ("[\"all\", true, true]"));
+ g_assert_false (filter ("[\"all\", false, true]"));
+
+ g_assert_false (filter ("[\"any\"]"));
+ g_assert_true (filter ("[\"none\"]"));
+ g_assert_true (filter ("[\"all\"]"));
+
+ g_assert_true (filter ("[\"in\", 10, 20, 10, 13]"));
+ g_assert_true (filter ("[\"!in\", 10, 20, 0, 13]"));
+
+ g_assert_true (filter ("[\"==\", 10, 10]"));
+ g_assert_false (filter ("[\"==\", 10, 20]"));
+ g_assert_false (filter ("[\"==\", 10, \"10\"]"));
+ g_assert_false (filter ("[\"!=\", 10, 10]"));
+ g_assert_true (filter ("[\"!=\", 10, 20]"));
+ g_assert_true (filter ("[\"!=\", 10, \"10\"]"));
+ g_assert_true (filter ("[\">\", 20, 10]"));
+ g_assert_false (filter ("[\">\", 10, 10]"));
+ g_assert_false (filter ("[\">\", 5, 10]"));
+ g_assert_true (filter ("[\"<\", 10, 20]"));
+ g_assert_false (filter ("[\"<\", 10, 10]"));
+ g_assert_false (filter ("[\"<\", 10, 5]"));
+ g_assert_true (filter ("[\">=\", 20, 10]"));
+ g_assert_true (filter ("[\">=\", 10, 10]"));
+ g_assert_false (filter ("[\">=\", 5, 10]"));
+ g_assert_true (filter ("[\"<=\", 10, 20]"));
+ g_assert_true (filter ("[\"<=\", 10, 10]"));
+ g_assert_false (filter ("[\"<=\", 10, 5]"));
+}
+
+
+static void
+test_vector_expression_feature_filter (void)
+{
+ GError *error = NULL;
+ g_autoptr(GBytes) vector_data = NULL;
+ gconstpointer data;
+ gsize len;
+ ShumateVectorRenderScope scope;
+
+ vector_data = g_resources_lookup_data ("/org/gnome/shumate/Tests/0.pbf", G_RESOURCE_LOOKUP_FLAGS_NONE,
NULL);
+ g_assert_no_error (error);
+
+ data = g_bytes_get_data (vector_data, &len);
+ scope.tile = vector_tile__tile__unpack (NULL, len, data);
+ g_assert_nonnull (scope.tile);
+
+ scope.zoom_level = 10;
+
+ g_assert_true (shumate_vector_render_scope_find_layer (&scope, "helloworld"));
+ scope.feature = scope.layer->features[0];
+
+ g_assert_true (filter_with_scope (&scope, "[\"==\", \"name\", \"Hello, world!\"]"));
+ g_assert_false (filter_with_scope (&scope, "[\"==\", \"name\", \"Goodbye, world!\"]"));
+ g_assert_true (filter_with_scope (&scope, "[\"has\", \"name\"]"));
+ g_assert_false (filter_with_scope (&scope, "[\"!has\", \"name\"]"));
+ g_assert_false (filter_with_scope (&scope, "[\"has\", \"name:en\"]"));
+ g_assert_true (filter_with_scope (&scope, "[\"!has\", \"name:en\"]"));
+ g_assert_true (filter_with_scope (&scope, "[\"==\", \"$type\", \"Point\"]"));
+ g_assert_true (filter_with_scope (&scope, "[\"==\", \"zoom\", 10]"));
+}
+
+
+static void
+filter_expect_error (const char *filter)
+{
+ g_autoptr(GError) error = NULL;
+ g_autoptr(JsonNode) node = json_from_string (filter, NULL);
+ g_autoptr(ShumateVectorExpression) expression = shumate_vector_expression_from_json (node, &error);
+
+ g_assert_error (error, SHUMATE_STYLE_ERROR, SHUMATE_STYLE_ERROR_INVALID_EXPRESSION);
+ g_assert_null (expression);
+}
+
+static void
+test_vector_expression_filter_errors (void)
+{
+ filter_expect_error ("[\"not an operator\"]");
+ filter_expect_error ("[\"in\"]");
+ filter_expect_error ("[\"==\", 0, 1, 2]");
+ filter_expect_error ("[]");
+ filter_expect_error ("[[]]");
+}
+
+
int
main (int argc, char *argv[])
{
@@ -117,6 +233,9 @@ main (int argc, char *argv[])
g_test_add_func ("/vector/expression/literal", test_vector_expression_literal);
g_test_add_func ("/vector/expression/interpolate", test_vector_expression_interpolate);
g_test_add_func ("/vector/expression/interpolate-color", test_vector_expression_interpolate_color);
+ g_test_add_func ("/vector/expression/basic-filter", test_vector_expression_basic_filter);
+ g_test_add_func ("/vector/expression/feature-filter", test_vector_expression_feature_filter);
+ g_test_add_func ("/vector/expression/filter-errors", test_vector_expression_filter_errors);
return g_test_run ();
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]