[libshumate] vector: Add string format expressions



commit cc45563e8c91a909084c4e87c4bda5c52bcc12f6
Author: James Westman <james jwestman net>
Date:   Wed Dec 22 21:34:14 2021 -0600

    vector: Add string format expressions

 shumate/meson.build                                |   2 +
 .../shumate-vector-expression-format-private.h     |  29 +++++
 shumate/vector/shumate-vector-expression-format.c  | 123 +++++++++++++++++++++
 shumate/vector/shumate-vector-expression.c         |   7 +-
 shumate/vector/shumate-vector-value-private.h      |   2 +
 shumate/vector/shumate-vector-value.c              |  21 ++++
 tests/vector-expression.c                          |  31 ++++++
 7 files changed, 214 insertions(+), 1 deletion(-)
---
diff --git a/shumate/meson.build b/shumate/meson.build
index 5ee22be..7f387fd 100644
--- a/shumate/meson.build
+++ b/shumate/meson.build
@@ -34,6 +34,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-format-private.h',
   'vector/shumate-vector-expression-interpolate-private.h',
   'vector/shumate-vector-expression-literal-private.h',
   'vector/shumate-vector-fill-layer-private.h',
@@ -156,6 +157,7 @@ if get_option('vector_renderer')
     'vector/shumate-vector-expression.c',
     'vector/shumate-vector-expression-interpolate.c',
     'vector/shumate-vector-expression-filter.c',
+    'vector/shumate-vector-expression-format.c',
     'vector/shumate-vector-expression-literal.c',
     'vector/shumate-vector-fill-layer.c',
     'vector/shumate-vector-layer.c',
diff --git a/shumate/vector/shumate-vector-expression-format-private.h 
b/shumate/vector/shumate-vector-expression-format-private.h
new file mode 100644
index 0000000..18f6fe5
--- /dev/null
+++ b/shumate/vector/shumate-vector-expression-format-private.h
@@ -0,0 +1,29 @@
+/*
+ * 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_FORMAT (shumate_vector_expression_format_get_type())
+G_DECLARE_FINAL_TYPE (ShumateVectorExpressionFormat, shumate_vector_expression_format, SHUMATE, 
VECTOR_EXPRESSION_FORMAT, ShumateVectorExpression)
+
+ShumateVectorExpression *shumate_vector_expression_format_new (const char *format, GError **error);
+
+G_END_DECLS
diff --git a/shumate/vector/shumate-vector-expression-format.c 
b/shumate/vector/shumate-vector-expression-format.c
new file mode 100644
index 0000000..0826d22
--- /dev/null
+++ b/shumate/vector/shumate-vector-expression-format.c
@@ -0,0 +1,123 @@
+/*
+ * 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-renderer.h"
+#include "shumate-vector-expression-format-private.h"
+#include "shumate-vector-value-private.h"
+
+struct _ShumateVectorExpressionFormat
+{
+  ShumateVectorExpression parent_instance;
+
+  /* A list of format segments. Even-indexed segments are literals, every
+   * other segment is a variable. */
+  char **format;
+};
+
+G_DEFINE_TYPE (ShumateVectorExpressionFormat, shumate_vector_expression_format, 
SHUMATE_TYPE_VECTOR_EXPRESSION)
+
+
+ShumateVectorExpression *
+shumate_vector_expression_format_new (const char *format, GError **error)
+{
+  g_autoptr(ShumateVectorExpressionFormat) self = g_object_new (SHUMATE_TYPE_VECTOR_EXPRESSION_FORMAT, NULL);
+  int balance = 0;
+
+  /* Ensure the braces in the format string are balanced and not nested */
+  for (int i = 0, n = strlen (format); i < n; i ++)
+    {
+      if (format[i] == '{')
+        balance ++;
+      else if (format[i] == '}')
+        balance --;
+
+      if (balance != 0 && balance != 1)
+        {
+          g_set_error (error,
+                       SHUMATE_STYLE_ERROR,
+                       SHUMATE_STYLE_ERROR_INVALID_EXPRESSION,
+                       "Format string `%s` is nested or unbalanced",
+                       format);
+          return NULL;
+        }
+    }
+
+  self->format = g_strsplit_set (format, "{}", 0);
+
+  return (ShumateVectorExpression *)g_steal_pointer (&self);
+}
+
+static void
+shumate_vector_expression_format_finalize (GObject *object)
+{
+  ShumateVectorExpressionFormat *self = (ShumateVectorExpressionFormat *)object;
+
+  g_clear_pointer (&self->format, g_strfreev);
+
+  G_OBJECT_CLASS (shumate_vector_expression_format_parent_class)->finalize (object);
+}
+
+
+static gboolean
+shumate_vector_expression_format_eval (ShumateVectorExpression  *expr,
+                                       ShumateVectorRenderScope *scope,
+                                       ShumateVectorValue       *out)
+{
+  ShumateVectorExpressionFormat *self = (ShumateVectorExpressionFormat *)expr;
+  g_autoptr(GString) string = g_string_new ("");
+  ShumateVectorValue value = SHUMATE_VECTOR_VALUE_INIT;
+  char *variable_val;
+  int i = 0;
+
+  while (TRUE)
+    {
+      if (self->format[i] == NULL)
+        break;
+
+      g_string_append (string, self->format[i]);
+      i ++;
+
+      if (self->format[i] == NULL)
+        break;
+
+      shumate_vector_render_scope_get_variable (scope, self->format[i], &value);
+      variable_val = shumate_vector_value_as_string (&value);
+      g_string_append (string, variable_val);
+      g_free (variable_val);
+      shumate_vector_value_unset (&value);
+      i ++;
+    }
+
+  shumate_vector_value_set_string (out, string->str);
+  return TRUE;
+}
+
+
+static void
+shumate_vector_expression_format_class_init (ShumateVectorExpressionFormatClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  ShumateVectorExpressionClass *expr_class = SHUMATE_VECTOR_EXPRESSION_CLASS (klass);
+
+  object_class->finalize = shumate_vector_expression_format_finalize;
+  expr_class->eval = shumate_vector_expression_format_eval;
+}
+
+static void
+shumate_vector_expression_format_init (ShumateVectorExpressionFormat *self)
+{
+}
diff --git a/shumate/vector/shumate-vector-expression.c b/shumate/vector/shumate-vector-expression.c
index 42d04b1..b31ac54 100644
--- a/shumate/vector/shumate-vector-expression.c
+++ b/shumate/vector/shumate-vector-expression.c
@@ -19,6 +19,7 @@
 #include "shumate-vector-renderer.h"
 #include "shumate-vector-expression-private.h"
 #include "shumate-vector-expression-filter-private.h"
+#include "shumate-vector-expression-format-private.h"
 #include "shumate-vector-expression-interpolate-private.h"
 #include "shumate-vector-expression-literal-private.h"
 #include "shumate-vector-value-private.h"
@@ -36,6 +37,7 @@ shumate_vector_expression_from_json (JsonNode *json, GError **error)
     {
       g_auto(GValue) gvalue = G_VALUE_INIT;
       g_auto(ShumateVectorValue) value = SHUMATE_VECTOR_VALUE_INIT;
+      const char *string;
 
       json_node_get_value (json, &gvalue);
       if (!shumate_vector_value_set_from_g_value (&value, &gvalue))
@@ -47,7 +49,10 @@ shumate_vector_expression_from_json (JsonNode *json, GError **error)
           return NULL;
         }
 
-      return shumate_vector_expression_literal_new (&value);
+      if (shumate_vector_value_get_string (&value, &string))
+        return shumate_vector_expression_format_new (string, error);
+      else
+        return shumate_vector_expression_literal_new (&value);
     }
   else if (JSON_NODE_HOLDS_OBJECT (json))
     return shumate_vector_expression_interpolate_from_json_obj (json_node_get_object (json), error);
diff --git a/shumate/vector/shumate-vector-value-private.h b/shumate/vector/shumate-vector-value-private.h
index 33ad778..0676a1e 100644
--- a/shumate/vector/shumate-vector-value-private.h
+++ b/shumate/vector/shumate-vector-value-private.h
@@ -61,3 +61,5 @@ void shumate_vector_value_set_color (ShumateVectorValue *self, GdkRGBA *color);
 gboolean shumate_vector_value_get_color (ShumateVectorValue *self, GdkRGBA *color);
 
 gboolean shumate_vector_value_equal (ShumateVectorValue *a, ShumateVectorValue *b);
+
+char *shumate_vector_value_as_string (ShumateVectorValue *self);
diff --git a/shumate/vector/shumate-vector-value.c b/shumate/vector/shumate-vector-value.c
index 4d1751e..6f5f8d4 100644
--- a/shumate/vector/shumate-vector-value.c
+++ b/shumate/vector/shumate-vector-value.c
@@ -234,3 +234,24 @@ shumate_vector_value_equal (ShumateVectorValue *a, ShumateVectorValue *b)
       g_assert_not_reached ();
     }
 }
+
+
+char *
+shumate_vector_value_as_string (ShumateVectorValue *self)
+{
+  switch (self->type)
+    {
+    case TYPE_NULL:
+      return g_strdup ("");
+    case TYPE_NUMBER:
+      return g_strdup_printf ("%f", self->number);
+    case TYPE_BOOLEAN:
+      return g_strdup (self->boolean ? "true" : "false");
+    case TYPE_STRING:
+      return g_strdup (self->string);
+    case TYPE_COLOR:
+      return gdk_rgba_to_string (&self->color);
+    default:
+      g_assert_not_reached ();
+    }
+}
diff --git a/tests/vector-expression.c b/tests/vector-expression.c
index c9b33e7..1f319f5 100644
--- a/tests/vector-expression.c
+++ b/tests/vector-expression.c
@@ -224,6 +224,36 @@ test_vector_expression_filter_errors (void)
 }
 
 
+static void
+test_vector_expression_format ()
+{
+  GError *error = NULL;
+  g_autoptr(GBytes) vector_data = NULL;
+  gconstpointer data;
+  gsize len;
+  ShumateVectorRenderScope scope;
+  g_autoptr(JsonNode) node = json_from_string ("\"{name}\"", NULL);
+  g_autoptr(ShumateVectorExpression) expression;
+
+  expression = shumate_vector_expression_from_json (node, &error);
+  g_assert_no_error (error);
+
+  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_cmpstr (shumate_vector_expression_eval_string (expression, &scope, NULL), ==, "Hello, world!");
+}
+
+
 int
 main (int argc, char *argv[])
 {
@@ -236,6 +266,7 @@ main (int argc, char *argv[])
   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);
+  g_test_add_func ("/vector/expression/format", test_vector_expression_format);
 
   return g_test_run ();
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]