[gnome-maps/wip/jonasdn/local-tiles: 2/3] Add file tile source
- From: Jonas Danielsson <jonasdn src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-maps/wip/jonasdn/local-tiles: 2/3] Add file tile source
- Date: Sat, 15 Aug 2015 20:43:21 +0000 (UTC)
commit a2168c6e1fd49685f6740a194e72de66ddd6e3b5
Author: Jonas Danielsson <jonas threetimestwo org>
Date: Wed Aug 12 15:25:42 2015 +0200
Add file tile source
lib/Makefile.am | 13 +-
lib/maps-file-tile-source.c | 636 +++++++++++++++++++++++++++++++++++++++++++
lib/maps-file-tile-source.h | 73 +++++
3 files changed, 720 insertions(+), 2 deletions(-)
---
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 35203da..c3edc11 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -4,8 +4,17 @@ BUILT_SOURCES = \
maps-enum-types.c \
maps-enum-types.h
-libgnome_maps_headers_private = maps-contact-store.h maps-contact.h maps.h
-libgnome_maps_sources = maps-contact-store.c maps-contact.c
+libgnome_maps_headers_private = \
+ maps-contact-store.h \
+ maps-contact.h \
+ maps.h \
+ maps-file-tile-source.h
+
+libgnome_maps_sources = \
+ maps-contact-store.c \
+ maps-contact.c \
+ maps-file-tile-source.c
+
libgnome_maps_la_SOURCES = \
$(libgnome_maps_sources) \
$(libgnome_maps_headers_private) \
diff --git a/lib/maps-file-tile-source.c b/lib/maps-file-tile-source.c
new file mode 100644
index 0000000..326f976
--- /dev/null
+++ b/lib/maps-file-tile-source.c
@@ -0,0 +1,636 @@
+/*
+ * Copyright (c) 2015 Jonas Danielsson
+ *
+ * GNOME Maps is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * GNOME Maps 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with GNOME Maps; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Jonas Danielsson <jonas threetimestwo org>
+ */
+
+#include <champlain/champlain.h>
+#include <gio/gio.h>
+#include <glib.h>
+#include <glib-object.h>
+#include <libsoup/soup.h>
+#include <stdlib.h>
+
+#include "maps-file-tile-source.h"
+
+#define MAPS_FILE_TILE_SOURCE_ERROR maps_file_tile_source_error_quark ()
+
+GQuark
+maps_file_tile_source_error_quark (void)
+{
+ return g_quark_from_static_string ("maps-file-tile-source-error");
+}
+
+enum {
+ PROP_0,
+
+ PROP_PATH,
+ PROP_MAX_ZOOM,
+ PROP_MIN_ZOOM,
+ PROP_WORLD
+
+};
+
+struct _MapsFileTileSourcePrivate
+{
+ gchar *path;
+ gchar *extension;
+ gint max_zoom;
+ gint min_zoom;
+ ChamplainBoundingBox *world;
+
+ long min_x;
+ long min_y;
+ long max_x;
+ long max_y;
+};
+
+typedef struct
+{
+ ChamplainMapSource *map_source;
+ ChamplainTile *tile;
+} CallbackData;
+
+G_DEFINE_TYPE_WITH_PRIVATE (MapsFileTileSource, maps_file_tile_source, CHAMPLAIN_TYPE_TILE_SOURCE)
+
+static void fill_tile (ChamplainMapSource *map_source,
+ ChamplainTile *tile);
+
+
+static void
+maps_file_tile_source_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MapsFileTileSource *tile_source = MAPS_FILE_TILE_SOURCE (object);
+
+ switch (prop_id)
+ {
+ case PROP_PATH:
+ tile_source->priv->path = g_strdup ((char *) g_value_get_string (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+maps_file_tile_source_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MapsFileTileSource *tile_source = MAPS_FILE_TILE_SOURCE (object);
+
+ switch (prop_id)
+ {
+ case PROP_PATH:
+ g_value_set_string (value, tile_source->priv->path);
+ break;
+
+ case PROP_MIN_ZOOM:
+ g_value_set_uint (value, tile_source->priv->min_zoom);
+ break;
+
+ case PROP_MAX_ZOOM:
+ g_value_set_uint (value, tile_source->priv->max_zoom);
+ break;
+
+ case PROP_WORLD:
+ g_value_set_boxed (value, tile_source->priv->world);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+maps_file_tile_source_dispose (GObject *object)
+{
+ G_OBJECT_CLASS (maps_file_tile_source_parent_class)->dispose (object);
+}
+
+static void
+maps_file_tile_source_finalize (GObject *object)
+{
+ MapsFileTileSource *tile_source = MAPS_FILE_TILE_SOURCE (object);
+
+ if (tile_source->priv->path)
+ g_free (tile_source->priv->path);
+
+ if (tile_source->priv->extension)
+ g_free (tile_source->priv->extension);
+
+ G_OBJECT_CLASS (maps_file_tile_source_parent_class)->finalize (object);
+}
+
+static guint
+get_max_zoom_level (ChamplainMapSource *source)
+{
+ MapsFileTileSource *tile_source = (MapsFileTileSource *) source;
+
+ return tile_source->priv->max_zoom;
+}
+
+static guint
+get_min_zoom_level (ChamplainMapSource *source)
+{
+ MapsFileTileSource *tile_source = (MapsFileTileSource *) source;
+
+ return tile_source->priv->min_zoom;
+}
+
+static void
+maps_file_tile_source_class_init (MapsFileTileSourceClass *klass)
+{
+ ChamplainMapSourceClass *map_source_class = CHAMPLAIN_MAP_SOURCE_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GParamSpec *pspec;
+
+ object_class->finalize = maps_file_tile_source_finalize;
+ object_class->dispose = maps_file_tile_source_dispose;
+ object_class->get_property = maps_file_tile_source_get_property;
+ object_class->set_property = maps_file_tile_source_set_property;
+ map_source_class->get_max_zoom_level = get_max_zoom_level;
+ map_source_class->get_min_zoom_level = get_min_zoom_level;
+ map_source_class->fill_tile = fill_tile;
+
+ /**
+ * MapsFileTileSource:path:
+ *
+ * The path to the tile source.
+ *
+ */
+ pspec = g_param_spec_string ("path",
+ "Path",
+ "The path to the tile source",
+ "",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+ g_object_class_install_property (object_class, PROP_PATH, pspec);
+
+ /**
+ * MapsFileTileSource:min_zoom:
+ *
+ * The minimum zoom level of the tile source.
+ *
+ */
+ pspec = g_param_spec_uint ("min-zoom",
+ "Minimum zoom",
+ "The minimum zoom level of the tile source",
+ 0,
+ 20,
+ 2,
+ G_PARAM_READABLE);
+ g_object_class_install_property (object_class, PROP_MIN_ZOOM, pspec);
+
+ /**
+ * MapsFileTileSource:max_zoom:
+ *
+ * The maximum zoom level of the tile source.
+ *
+ */
+ pspec = g_param_spec_uint ("max-zoom",
+ "Maximum zoom",
+ "The maximum zoom level of the tile source",
+ 0,
+ 20,
+ 2,
+ G_PARAM_READABLE);
+ g_object_class_install_property (object_class, PROP_MAX_ZOOM, pspec);
+
+ /**
+ * MapsFileTileSource:world:
+ *
+ * Set a bounding box to limit the world to. No tiles will be loaded
+ * outside of this bounding box. It will not be possible to scroll outside
+ * of this bounding box.
+ *
+ */
+ pspec = g_param_spec_boxed ("world",
+ "The world",
+ "The bounding box to limit the #ChamplainView to",
+ CHAMPLAIN_TYPE_BOUNDING_BOX,
+ G_PARAM_READABLE);
+ g_object_class_install_property (object_class, PROP_WORLD, pspec);
+}
+
+static void
+maps_file_tile_source_init (MapsFileTileSource *tile_source)
+{
+ tile_source->priv = maps_file_tile_source_get_instance_private (tile_source);
+ tile_source->priv->path = NULL;
+ tile_source->priv->extension = NULL;
+ tile_source->priv->max_zoom = -1;
+ tile_source->priv->min_zoom = 21;
+ tile_source->priv->world = NULL;
+ tile_source->priv->min_x = G_MAXLONG;
+ tile_source->priv->min_y = G_MAXLONG;
+ tile_source->priv->max_x = 0;
+ tile_source->priv->max_y = 0;
+}
+
+static gboolean
+get_zoom_levels (MapsFileTileSource *tile_source,
+ GError **error)
+{
+ GFile *file;
+ GFileEnumerator *enumerator;
+ gboolean ret = TRUE;
+ long orig_min = tile_source->priv->min_zoom;
+ long orig_max = tile_source->priv->max_zoom;
+
+ file = g_file_new_for_path (tile_source->priv->path);
+ enumerator = g_file_enumerate_children (file, "standard::*",
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ NULL,
+ error);
+ if (!enumerator)
+ return FALSE;
+
+ while (TRUE)
+ {
+ GFileInfo *info;
+ const char *name;
+ char *endptr;
+ long val;
+
+ if (!g_file_enumerator_iterate (enumerator, &info, NULL, NULL, error)) {
+ ret = FALSE;
+ goto out;
+ }
+
+ if (!info)
+ break;
+
+ if (g_file_info_get_file_type (info) != G_FILE_TYPE_DIRECTORY)
+ continue;
+
+ name = g_file_info_get_name (info);
+ val = strtol (name, &endptr, 0);
+ if (endptr == name || *endptr != '\0')
+ continue;
+
+ if (val > tile_source->priv->max_zoom)
+ tile_source->priv->max_zoom = val;
+
+ if (val < tile_source->priv->min_zoom)
+ tile_source->priv->min_zoom = val;
+ }
+
+ if (tile_source->priv->min_zoom == orig_min ||
+ tile_source->priv->max_zoom == orig_max) {
+ ret = FALSE;
+ if (error)
+ {
+ *error = g_error_new_literal (MAPS_FILE_TILE_SOURCE_ERROR, 0,
+ "Failed to find tile structure in directory");
+ }
+ }
+
+ out:
+ g_object_unref (file);
+ g_object_unref (enumerator);
+
+ return ret;
+}
+
+static gboolean
+get_y_bounds (MapsFileTileSource *tile_source,
+ const char *path,
+ GError **error)
+{
+ GFileEnumerator *enumerator;
+ GFile *file;
+ gboolean ret = TRUE;
+ gboolean found = FALSE;
+
+ file = g_file_new_for_path (path);
+ enumerator = g_file_enumerate_children (file, "standard::*",
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ NULL,
+ error);
+ if (!enumerator)
+ return FALSE;
+
+ while (TRUE)
+ {
+ GFileInfo *info;
+ char **names;
+ char *endptr;
+ long y;
+
+ if (!g_file_enumerator_iterate (enumerator, &info,
+ NULL, NULL, error)) {
+ ret = FALSE;
+ goto out;
+ }
+
+ if (!info)
+ break;
+
+ if (g_file_info_get_file_type (info) != G_FILE_TYPE_REGULAR)
+ continue;
+
+ names = g_strsplit (g_file_info_get_name (info), ".", 2);
+ if (!tile_source->priv->extension)
+ tile_source->priv->extension = g_strdup (names[1]);
+
+ y = strtol (names[0], &endptr, 0);
+ if (endptr == names[0] || *endptr != '\0') {
+ g_strfreev (names);
+ continue;
+ }
+
+ if (!found)
+ found = TRUE;
+
+ g_strfreev (names);
+
+ if (y > tile_source->priv->max_y)
+ tile_source->priv->max_y = y;
+
+ if (y < tile_source->priv->min_y)
+ tile_source->priv->min_y = y;
+ }
+
+ if (!found)
+ {
+ ret = FALSE;
+ if (error)
+ {
+ *error = g_error_new_literal (MAPS_FILE_TILE_SOURCE_ERROR, 0,
+ "Failed to find tile structure in directory");
+ }
+ }
+
+ out:
+ g_object_unref (file);
+ g_object_unref (enumerator);
+ return ret;
+}
+
+static gboolean
+get_bounds (MapsFileTileSource *tile_source,
+ GError **error)
+{
+ GFileEnumerator *enumerator;
+ GFile *file;
+ char *path;
+ gboolean ret = TRUE;
+ char min_zoom[3];
+ gboolean found = FALSE;
+
+ sprintf (min_zoom, "%u", tile_source->priv->min_zoom);
+ path = g_build_filename (tile_source->priv->path, min_zoom, NULL);
+ file = g_file_new_for_path (path);
+
+ enumerator = g_file_enumerate_children (file, "standard::*",
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ NULL,
+ error);
+ if (!enumerator)
+ return FALSE;
+
+ while (TRUE)
+ {
+ char *y_path;
+ GFileInfo *info;
+ const char *name;
+ char *endptr;
+ long x;
+
+ if (!g_file_enumerator_iterate (enumerator, &info, NULL, NULL, error)) {
+ ret = FALSE;
+ goto out;
+ }
+
+ if (!info)
+ break;
+
+ if (g_file_info_get_file_type (info) != G_FILE_TYPE_DIRECTORY)
+ continue;
+
+ name = g_file_info_get_name (info);
+ x = strtol (name, &endptr, 0);
+ if (endptr == name || *endptr != '\0')
+ continue;
+
+ if (!found)
+ found = TRUE;
+
+ if (x > tile_source->priv->max_x)
+ tile_source->priv->max_x = x;
+
+ if (x < tile_source->priv->min_x)
+ tile_source->priv->min_x = x;
+
+ y_path = g_build_filename (path, name, NULL);
+ if (!get_y_bounds (tile_source, y_path, error)) {
+ g_free (y_path);
+ ret = FALSE;
+ goto out;
+ }
+ g_free (y_path);
+ }
+
+ if (!found)
+ {
+ ret = FALSE;
+ if (error)
+ {
+ *error = g_error_new_literal (MAPS_FILE_TILE_SOURCE_ERROR, 0,
+ "Failed to find tile structure in directory");
+ }
+ }
+
+ out:
+ g_free (path);
+ g_object_unref (file);
+ g_object_unref (enumerator);
+ return ret;
+}
+
+gboolean
+maps_file_tile_source_prepare (MapsFileTileSource *tile_source,
+ GError **error)
+{
+ g_return_if_fail (MAPS_IS_FILE_TILE_SOURCE (tile_source));
+ g_return_if_fail (tile_source->priv->path != NULL);
+
+ ChamplainMapSource *source = (ChamplainMapSource *) tile_source;
+ gboolean ret = TRUE;
+
+ if (!get_zoom_levels (tile_source, error)) {
+ ret = FALSE;
+ goto out;
+ }
+
+ if (!get_bounds (tile_source, error)) {
+ ret = FALSE;
+ goto out;
+ }
+
+ tile_source->priv->world = champlain_bounding_box_new ();
+ tile_source->priv->world->left = champlain_map_source_get_longitude (source,
+ tile_source->priv->min_zoom,
+ tile_source->priv->min_x * 256);
+ tile_source->priv->world->right = champlain_map_source_get_longitude (source,
+ tile_source->priv->min_zoom,
+ tile_source->priv->max_x * 256);
+ tile_source->priv->world->top = champlain_map_source_get_latitude (source,
+ tile_source->priv->min_zoom,
+ tile_source->priv->min_y * 256);
+ tile_source->priv->world->bottom = champlain_map_source_get_latitude (source,
+ tile_source->priv->min_zoom,
+ tile_source->priv->max_y * 256);
+ out:
+ return ret;
+}
+
+static void
+tile_rendered_cb (ChamplainTile *tile,
+ gpointer data,
+ guint size,
+ gboolean error,
+ CallbackData *user_data)
+{
+ ChamplainMapSource *map_source = user_data->map_source;
+ ChamplainMapSource *next_source;
+
+ g_signal_handlers_disconnect_by_func (tile, tile_rendered_cb, user_data);
+ g_slice_free (CallbackData, user_data);
+
+ next_source = champlain_map_source_get_next_source (map_source);
+
+ if (!error)
+ {
+ ChamplainTileSource *tile_source = CHAMPLAIN_TILE_SOURCE (map_source);
+ ChamplainTileCache *tile_cache = champlain_tile_source_get_cache (tile_source);
+
+ if (tile_cache && data)
+ champlain_tile_cache_store_tile (tile_cache, tile, data, size);
+
+ champlain_tile_set_fade_in (tile, TRUE);
+ champlain_tile_set_state (tile, CHAMPLAIN_STATE_DONE);
+ champlain_tile_display_content (tile);
+ }
+ else if (next_source)
+ champlain_map_source_fill_tile (next_source, tile);
+
+ g_object_unref (map_source);
+ g_object_unref (tile);
+}
+
+static void
+tile_loaded_cb (GFile *file,
+ GAsyncResult *res,
+ CallbackData *user_data)
+{
+ ChamplainMapSource *map_source = user_data->map_source;
+ ChamplainTileSource *tile_source = CHAMPLAIN_TILE_SOURCE (map_source);
+ ChamplainMapSource *next_source = champlain_map_source_get_next_source (map_source);
+ ChamplainTile *tile = user_data->tile;
+ CallbackData *data;
+ ChamplainRenderer *renderer;
+ char *content;
+ gsize length;
+
+ g_slice_free (CallbackData, user_data);
+
+ if (!g_file_load_contents_finish (file, res, &content, &length, NULL, NULL))
+ {
+ goto load_next;
+ }
+
+ renderer = champlain_map_source_get_renderer (map_source);
+ g_return_if_fail (CHAMPLAIN_IS_RENDERER (renderer));
+
+ data = g_slice_new (CallbackData);
+ data->map_source = map_source;
+
+ g_signal_connect (tile, "render-complete", G_CALLBACK (tile_rendered_cb), data);
+
+ champlain_renderer_set_data (renderer, content, length);
+ champlain_renderer_render (renderer, tile);
+
+ return;
+
+load_next:
+ if (next_source)
+ champlain_map_source_fill_tile (next_source, tile);
+
+ goto cleanup;
+
+finish:
+ champlain_tile_set_fade_in (tile, TRUE);
+ champlain_tile_set_state (tile, CHAMPLAIN_STATE_DONE);
+ champlain_tile_display_content (tile);
+
+cleanup:
+ g_object_unref (tile);
+ g_object_unref (map_source);
+}
+
+static void
+fill_tile (ChamplainMapSource *map_source,
+ ChamplainTile *tile)
+{
+ g_return_if_fail (MAPS_IS_FILE_TILE_SOURCE (map_source));
+ g_return_if_fail (CHAMPLAIN_IS_TILE (tile));
+
+ MapsFileTileSource *tile_source = MAPS_FILE_TILE_SOURCE (map_source);
+ CallbackData *callback_data;
+ GFile *file;
+ gchar *path = NULL;
+
+ if (champlain_tile_get_state (tile) == CHAMPLAIN_STATE_DONE)
+ return;
+
+ path = g_strdup_printf("%s/%d/%d/%d.%s",
+ tile_source->priv->path,
+ champlain_tile_get_zoom_level (tile),
+ champlain_tile_get_x (tile),
+ champlain_tile_get_y (tile),
+ tile_source->priv->extension);
+ file = g_file_new_for_path (path);
+
+ if (g_file_query_exists(file, NULL))
+ {
+ callback_data = g_slice_new (CallbackData);
+ callback_data->tile = tile;
+ callback_data->map_source = map_source;
+
+ g_object_ref (map_source);
+ g_object_ref (tile);
+
+ g_file_load_contents_async (file, NULL,
+ (GAsyncReadyCallback) tile_loaded_cb,
+ callback_data);
+ }
+ else
+ {
+ ChamplainMapSource *next_source = champlain_map_source_get_next_source (map_source);
+
+ if (CHAMPLAIN_IS_MAP_SOURCE (next_source))
+ champlain_map_source_fill_tile (next_source, tile);
+ }
+
+ g_object_unref (file);
+ g_free (path);
+}
diff --git a/lib/maps-file-tile-source.h b/lib/maps-file-tile-source.h
new file mode 100644
index 0000000..9b432e0
--- /dev/null
+++ b/lib/maps-file-tile-source.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2015 Jonas Danielsson
+ *
+ * GNOME Maps is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * GNOME Maps 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with GNOME Maps; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Jonas Danielsson <jonas threetimestwo org>
+ */
+
+#ifndef _MAPS_FILE_TILE_SOURCE_H_
+#define _MAPS_FILE_TILE_SOURCE_H_
+
+#include <champlain/champlain.h>
+
+G_BEGIN_DECLS
+
+#define MAPS_TYPE_FILE_TILE_SOURCE maps_file_tile_source_get_type ()
+
+#define MAPS_FILE_TILE_SOURCE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAPS_TYPE_FILE_TILE_SOURCE, MapsFileTileSource))
+
+#define MAPS_FILE_TILE_SOURCE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), MAPS_TYPE_FILE_TILE_SOURCE, MapsFileTileSourceClass))
+
+#define MAPS_IS_FILE_TILE_SOURCE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAPS_TYPE_FILE_TILE_SOURCE))
+
+#define MAPS_IS_FILE_TILE_SOURCE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), MAPS_TYPE_FILE_TILE_SOURCE))
+
+#define MAPS_FILE_TILE_SOURCE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), MAPS_TYPE_FILE_TILE_SOURCE, MapsFileTileSourceClass))
+
+typedef struct _MapsFileTileSourcePrivate MapsFileTileSourcePrivate;
+
+typedef struct _MapsFileTileSource MapsFileTileSource;
+typedef struct _MapsFileTileSourceClass MapsFileTileSourceClass;
+
+/**
+ * MapsFileTileSource:
+ *
+ * The #MapsFileTileSource structure contains only private data
+ * and should be accessed using the provided API
+ *
+ */
+struct _MapsFileTileSource
+{
+ ChamplainTileSource parent_instance;
+
+ MapsFileTileSourcePrivate *priv;
+};
+
+struct _MapsFileTileSourceClass
+{
+ ChamplainTileSourceClass parent_class;
+};
+
+GType maps_file_tile_source_get_type (void);
+
+gboolean maps_file_tile_source_prepare (MapsFileTileSource *tile_source, GError **error);
+G_END_DECLS
+
+#endif /* _MAPS_FILE_TILE_SOURCE_H_ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]