From c31e18dad24a2fcf9e9624347906c9c020154f12 Mon Sep 17 00:00:00 2001 From: Andy Spencer Date: Wed, 27 Jan 2010 20:48:45 +0000 Subject: [PATCH] Adding map plugin using OpenStreetMap data --- src/gis-wms.c | 21 +++- src/plugins/Makefile.am | 3 +- src/plugins/map.c | 217 ++++++++++++++++++++++++++++++++++++++++ src/plugins/map.h | 53 ++++++++++ src/tile_test.c | 41 ++++++-- 5 files changed, 326 insertions(+), 9 deletions(-) create mode 100644 src/plugins/map.c create mode 100644 src/plugins/map.h diff --git a/src/gis-wms.c b/src/gis-wms.c index d2e0766..e075ba6 100644 --- a/src/gis-wms.c +++ b/src/gis-wms.c @@ -15,6 +15,23 @@ * along with this program. If not, see . */ +/** + * Metacarte + * --------- + * http://labs.metacarta.com/wms/vmap0? + * LAYERS=basic& + * SERVICE=WMS& + * VERSION=1.1.1& + * REQUEST=GetMap& + * STYLES=& + * EXCEPTIONS=application/vnd.ogc.se_inimage& + * FORMAT=image/jpeg& + * SRS=EPSG:4326& + * BBOX=0,-90,180,90& + * WIDTH=256& + * HEIGHT=256 + */ + /** * http://www.nasa.network.com/elev? * SERVICE=WMS& @@ -72,7 +89,7 @@ static gchar *_make_uri(GisWms *wms, GisTile *tile) tile->edge.n); } -void gis_wms_soup_chunk_cb(SoupMessage *message, SoupBuffer *chunk, gpointer _file) +static void _soup_chunk_cb(SoupMessage *message, SoupBuffer *chunk, gpointer _file) { FILE *file = _file; if (!SOUP_STATUS_IS_SUCCESSFUL(message->status_code)) { @@ -108,7 +125,7 @@ char *gis_wms_make_local(GisWms *self, GisTile *tile) gchar *uri = _make_uri(self, tile); g_debug("GisWms: make_local - fetching %s", uri); SoupMessage *message = soup_message_new("GET", uri); - g_signal_connect(message, "got-chunk", G_CALLBACK(gis_wms_soup_chunk_cb), file); + g_signal_connect(message, "got-chunk", G_CALLBACK(_soup_chunk_cb), file); soup_message_headers_set_range(message->request_headers, ftell(file), -1); int status = soup_session_send_message(self->soup, message); if (!SOUP_STATUS_IS_SUCCESSFUL(message->status_code)) diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am index d0fd720..6f288f4 100644 --- a/src/plugins/Makefile.am +++ b/src/plugins/Makefile.am @@ -7,9 +7,10 @@ LIBS = $(top_srcdir)/src/libgis.la pluginsdir = "$(libdir)/gis" -plugins_LTLIBRARIES = bmng.la srtm.la env.la test.la +plugins_LTLIBRARIES = bmng.la srtm.la map.la env.la test.la bmng_la_SOURCES = bmng.c bmng.h srtm_la_SOURCES = srtm.c srtm.h +map_la_SOURCES = map.c map.h env_la_SOURCES = env.c env.h test_la_SOURCES = test.c test.h diff --git a/src/plugins/map.c b/src/plugins/map.c new file mode 100644 index 0000000..d8ff6c2 --- /dev/null +++ b/src/plugins/map.c @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2009 Andy Spencer + * + * This program 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 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +#include +#include +#include + +#include + +#include "map.h" + +#define MAX_RESOLUTION 500 +#define TILE_WIDTH 1024 +#define TILE_HEIGHT 512 + +struct _LoadTileData { + GisPluginMap *self; + GisTile *tile; + GdkPixbuf *pixbuf; +}; +static gboolean _load_tile_cb(gpointer _data) +{ + struct _LoadTileData *data = _data; + GisPluginMap *self = data->self; + GisTile *tile = data->tile; + GdkPixbuf *pixbuf = data->pixbuf; + g_free(data); + + /* Create Texture */ + g_debug("GisPluginMap: _load_tile_cb start"); + guchar *pixels = gdk_pixbuf_get_pixels(pixbuf); + gboolean alpha = gdk_pixbuf_get_has_alpha(pixbuf); + gint width = gdk_pixbuf_get_width(pixbuf); + gint height = gdk_pixbuf_get_height(pixbuf); + + guint *tex = g_new0(guint, 1); + gis_viewer_begin(self->viewer); + glGenTextures(1, tex); + glBindTexture(GL_TEXTURE_2D, *tex); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glTexImage2D(GL_TEXTURE_2D, 0, 4, width, height, 0, + (alpha ? GL_RGBA : GL_RGB), GL_UNSIGNED_BYTE, pixels); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + glFlush(); + gis_viewer_end(self->viewer); + + tile->data = tex; + gtk_widget_queue_draw(GTK_WIDGET(self->viewer)); + g_object_unref(pixbuf); + return FALSE; +} + +static void _load_tile(GisTile *tile, gpointer _self) +{ + GisPluginMap *self = _self; + g_debug("GisPluginMap: _load_tile start %p", g_thread_self()); + char *path = gis_wms_make_local(self->wms, tile); + struct _LoadTileData *data = g_new0(struct _LoadTileData, 1); + data->self = self; + data->tile = tile; + data->pixbuf = gdk_pixbuf_new_from_file(path, NULL); + if (data->pixbuf) { + g_idle_add_full(G_PRIORITY_LOW, _load_tile_cb, data, NULL); + } else { + g_warning("GisPluginMap: _load_tile - Error loading pixbuf %s", path); + g_remove(path); + } + g_free(path); + g_debug("GisPluginMap: _load_tile end %p", g_thread_self()); +} + +static gboolean _free_tile_cb(gpointer data) +{ + glDeleteTextures(1, data); + g_free(data); + return FALSE; +} +static void _free_tile(GisTile *tile, gpointer _self) +{ + GisPluginMap *self = _self; + g_debug("GisPluginMap: _free_tile: %p=%d", tile->data, *(guint*)tile->data); + g_idle_add_full(G_PRIORITY_LOW, _free_tile_cb, tile->data, NULL); +} + +static gpointer _update_tiles(gpointer _self) +{ + g_debug("GisPluginMap: _update_tiles"); + GisPluginMap *self = _self; + g_mutex_lock(self->mutex); + gdouble lat, lon, elev; + gis_viewer_get_location(self->viewer, &lat, &lon, &elev); + gis_tile_update(self->tiles, + MAX_RESOLUTION, TILE_WIDTH, TILE_WIDTH, + lat, lon, elev, + _load_tile, self); + gis_tile_gc(self->tiles, time(NULL)-10, + _free_tile, self); + g_mutex_unlock(self->mutex); + return NULL; +} + +/************* + * Callbacks * + *************/ +static void _on_location_changed(GisViewer *viewer, + gdouble lat, gdouble lon, gdouble elev, GisPluginMap *self) +{ + g_thread_create(_update_tiles, self, FALSE, NULL); +} + +static gpointer _expose(GisCallback *callback, gpointer _self) +{ + GisPluginMap *self = GIS_PLUGIN_MAP(_self); + g_debug("GisPluginMap: expose viewer=%p tiles=%p,%p", + self->viewer, self->tiles, self->tiles->data); + gis_viewer_render_tiles(self->viewer, self->tiles); + return NULL; +} + +/*********** + * Methods * + ***********/ +GisPluginMap *gis_plugin_map_new(GisViewer *viewer) +{ + g_debug("GisPluginMap: new"); + GisPluginMap *self = g_object_new(GIS_TYPE_PLUGIN_MAP, NULL); + self->viewer = g_object_ref(viewer); + + /* Load initial tiles */ + _load_tile(self->tiles, self); + g_thread_create(_update_tiles, self, FALSE, NULL); + + /* Connect signals */ + self->sigid = g_signal_connect(self->viewer, "location-changed", + G_CALLBACK(_on_location_changed), self); + + /* Add renderers */ + GisCallback *callback = gis_callback_new(_expose, self); + gis_viewer_add(viewer, GIS_OBJECT(callback), GIS_LEVEL_WORLD, 0); + + return self; +} + + +/**************** + * GObject code * + ****************/ +/* Plugin init */ +static void gis_plugin_map_plugin_init(GisPluginInterface *iface); +G_DEFINE_TYPE_WITH_CODE(GisPluginMap, gis_plugin_map, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE(GIS_TYPE_PLUGIN, + gis_plugin_map_plugin_init)); +static void gis_plugin_map_plugin_init(GisPluginInterface *iface) +{ + g_debug("GisPluginMap: plugin_init"); + /* Add methods to the interface */ +} +/* Class/Object init */ +static void gis_plugin_map_init(GisPluginMap *self) +{ + g_debug("GisPluginMap: init"); + /* Set defaults */ + self->mutex = g_mutex_new(); + self->tiles = gis_tile_new(NULL, NORTH, SOUTH, EAST, WEST); + self->wms = gis_wms_new( + "http://labs.metacarta.com/wms/vmap0", "basic", "image/png", + "osm/", "png", TILE_WIDTH, TILE_HEIGHT); +} +static void gis_plugin_map_dispose(GObject *gobject) +{ + g_debug("GisPluginMap: dispose"); + GisPluginMap *self = GIS_PLUGIN_MAP(gobject); + /* Drop references */ + if (self->viewer) { + g_signal_handler_disconnect(self->viewer, self->sigid); + g_object_unref(self->viewer); + self->viewer = NULL; + } + G_OBJECT_CLASS(gis_plugin_map_parent_class)->dispose(gobject); +} +static void gis_plugin_map_finalize(GObject *gobject) +{ + g_debug("GisPluginMap: finalize"); + GisPluginMap *self = GIS_PLUGIN_MAP(gobject); + /* Free data */ + gis_tile_free(self->tiles, _free_tile, self); + gis_wms_free(self->wms); + g_mutex_free(self->mutex); + G_OBJECT_CLASS(gis_plugin_map_parent_class)->finalize(gobject); + +} +static void gis_plugin_map_class_init(GisPluginMapClass *klass) +{ + g_debug("GisPluginMap: class_init"); + GObjectClass *gobject_class = (GObjectClass*)klass; + gobject_class->dispose = gis_plugin_map_dispose; + gobject_class->finalize = gis_plugin_map_finalize; +} diff --git a/src/plugins/map.h b/src/plugins/map.h new file mode 100644 index 0000000..3cc7d56 --- /dev/null +++ b/src/plugins/map.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2009 Andy Spencer + * + * This program 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 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +#ifndef __MAP_H__ +#define __MAP_H__ + +#include + +#define GIS_TYPE_PLUGIN_MAP (gis_plugin_map_get_type ()) +#define GIS_PLUGIN_MAP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GIS_TYPE_PLUGIN_MAP, GisPluginMap)) +#define GIS_IS_PLUGIN_MAP(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GIS_TYPE_PLUGIN_MAP)) +#define GIS_PLUGIN_MAP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIS_TYPE_PLUGIN_MAP, GisPluginMapClass)) +#define GIS_IS_PLUGIN_MAP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIS_TYPE_PLUGIN_MAP)) +#define GIS_PLUGIN_MAP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIS_TYPE_PLUGIN_MAP, GisPluginMapClass)) + +typedef struct _GisPluginMap GisPluginMap; +typedef struct _GisPluginMapClass GisPluginMapClass; + +struct _GisPluginMap { + GObject parent_instance; + + /* instance members */ + GisViewer *viewer; + GisTile *tiles; + GisWms *wms; + GMutex *mutex; + gulong sigid; +}; + +struct _GisPluginMapClass { + GObjectClass parent_class; +}; + +GType gis_plugin_map_get_type(); + +/* Methods */ +GisPluginMap *gis_plugin_map_new(GisViewer *viewer); + +#endif diff --git a/src/tile_test.c b/src/tile_test.c index dd27cc7..d67e14f 100644 --- a/src/tile_test.c +++ b/src/tile_test.c @@ -29,32 +29,58 @@ void chunk_callback(gsize cur, gsize total, gpointer _state) gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(state->progress), (gdouble)cur/total); } -gpointer do_cache(gpointer _image) +gpointer do_bmng_cache(gpointer _image) { GtkImage *image = _image; - g_message("Creating tile"); + g_message("Creating bmng tile"); GisTile *tile = gis_tile_new(NULL, NORTH, SOUTH, EAST, WEST); tile->children[0][1] = gis_tile_new(tile, NORTH, 0, 0, WEST); tile = tile->children[0][1]; - g_message("Fetching image"); + g_message("Fetching bmng image"); GisWms *bmng_wms = gis_wms_new( "http://www.nasa.network.com/wms", "bmng200406", "image/jpeg", "bmng_test/", "jpg", 512, 256); const char *path = gis_wms_make_local(bmng_wms, tile); - g_message("Loading image: [%s]", path); + g_message("Loading bmng image: [%s]", path); GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, NULL); gdk_threads_enter(); gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf); gdk_threads_leave(); - g_message("Cleaning up"); + g_message("Cleaning bmng up"); gis_wms_free(bmng_wms); gis_tile_free(tile, NULL, NULL); return NULL; } +gpointer do_osm_cache(gpointer _image) +{ + GtkImage *image = _image; + g_message("Creating osm tile"); + GisTile *tile = gis_tile_new(NULL, NORTH, SOUTH, EAST, WEST); + tile->children[0][1] = gis_tile_new(tile, NORTH, 0, 0, WEST); + tile = tile->children[0][1]; + + g_message("Fetching osm image"); + GisWms *osm_wms = gis_wms_new( + "http://labs.metacarta.com/wms/vmap0", "basic", "image/png", + "osm_test/", "png", 512, 256); + const char *path = gis_wms_make_local(osm_wms, tile); + + g_message("Loading osm image: [%s]", path); + GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, NULL); + gdk_threads_enter(); + gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf); + gdk_threads_leave(); + + g_message("Cleaning osm up"); + gis_wms_free(osm_wms); + gis_tile_free(tile, NULL, NULL); + return NULL; +} + gboolean key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer user_data) { if (event->keyval == GDK_q) @@ -75,18 +101,21 @@ int main(int argc, char **argv) GtkWidget *scroll = gtk_scrolled_window_new(NULL, NULL); GtkWidget *bmng_image = gtk_image_new(); GtkWidget *srtm_image = gtk_image_new(); + GtkWidget *osm_image = gtk_image_new(); gtk_container_add(GTK_CONTAINER(win), vbox1); gtk_box_pack_start(GTK_BOX(vbox1), scroll, TRUE, TRUE, 0); gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox2); gtk_box_pack_start(GTK_BOX(vbox2), bmng_image, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox2), srtm_image, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(vbox2), osm_image, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox1), status, FALSE, FALSE, 0); g_signal_connect(win, "key-press-event", G_CALLBACK(key_press_cb), NULL); g_signal_connect(win, "destroy", gtk_main_quit, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - g_thread_create(do_cache, bmng_image, FALSE, NULL); + g_thread_create(do_bmng_cache, bmng_image, FALSE, NULL); + g_thread_create(do_osm_cache, osm_image, FALSE, NULL); gtk_widget_show_all(win); gtk_main(); -- 2.43.2