On Wed, Oct 14, 2009 at 06:56:50PM +0400, Gleb Smirnoff wrote:
T> Attached is a simplemost WMS support. Can't even be called a work in 
progress,
T> just a proof of concept. It has hardcoded URL, and it doesn't have separate
T> thread for downloading. However it works:
T> 
T> I'm going to add three more things to it:
T> 
T> 1) separate download thread
T> 2) choosing URL via menu
T> 3) drag'n'drop the layer, to fix poorly georeferenced data
T> 
T> And then tidy up code.

Well, here is updated patch that can be considered a working code. At least
I'm going to use it in my next trip, if I managed to get working
receiver+gpsd+viking working.

Here image is downloaded in separate thread, so nothing is blocking on
network. No menu for URL yet, but there is an entry box where URL can
be typed in. Drag'n'drop isn't visualized, but it is working!

Please review the patch and tell me your comments.

Is there any readers on list??? :)

-- 
Totus tuus, Glebius.
GLEB-RIPE
diff --git a/configure.ac b/configure.ac
index 6b86f99..a107f2c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -221,6 +221,19 @@ case $ac_cv_enable_dem24k in
 esac
 AM_CONDITIONAL([DEM24K], [test x$ac_cv_enable_dem24k = xyes])
 
+AC_ARG_ENABLE(wms, AC_HELP_STRING([--enable-wms],
+              [enable WMS support (default is enable)]),
+              [ac_cv_enable_wms=$enableval],
+              [ac_cv_enable_wms=yes])
+AC_CACHE_CHECK([whether to enable WMS support],
+               [ac_cv_enable_wms], [ac_cv_enable_wms=yes])
+case $ac_cv_enable_wms in
+  yes)
+    AC_DEFINE(VIK_CONFIG_WMS, [], [WMS SUPPORT])
+    ;;
+esac
+AM_CONDITIONAL([WMS], [test x$ac_cv_enable_wms = xyes])
+
 # Realtime GPS tracking
 AC_ARG_ENABLE(realtime-gps-tracking, AC_HELP_STRING([--enable-realtime-gps-tracking],
               [enable realtime GPS tracking (default is enable)]),
diff --git a/src/Makefile.am b/src/Makefile.am
index 796ca74..e2e9a7b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -139,6 +139,11 @@ libviking_a_SOURCES += \
 	geonamessearch.c geonamessearch.h
 endif
 
+if WMS
+libviking_a_SOURCES += \
+	vikwmslayer.c vikwmslayer.h
+endif
+
 viking_SOURCES = main.c
 
 LDADD           = libviking.a $(PACKAGE_LIBS) @EXPAT_LIBS@ @LIBCURL@ icons/libicons.a
diff --git a/src/background.c b/src/background.c
index d1d0c9b..0eb0e48 100644
--- a/src/background.c
+++ b/src/background.c
@@ -78,7 +78,8 @@ static void thread_die ( gpointer args[6] )
 {
   vik_thr_free_func userdata_free_func = args[3];
 
-  userdata_free_func ( args[2] );
+  if (userdata_free_func)
+    userdata_free_func ( args[2] );
 
   if ( GPOINTER_TO_INT(args[6]) )
   {
diff --git a/src/curl_download.c b/src/curl_download.c
index f230dca..ddeae82 100644
--- a/src/curl_download.c
+++ b/src/curl_download.c
@@ -117,8 +117,9 @@ void curl_download_init()
   curl_download_user_agent = g_strdup_printf ("%s/%s %s", PACKAGE, VERSION, curl_version());
 }
 
-int curl_download_uri ( const char *uri, FILE *f, DownloadOptions *options )
+int curl_download_uri ( const char *uri, void *to, DownloadOptions *options )
 {
+  FILE *f = (FILE *)to;
   CURL *curl;
   CURLcode res = CURLE_FAILED_INIT;
   const gchar *cookie_file;
@@ -131,8 +132,14 @@ int curl_download_uri ( const char *uri, FILE *f, DownloadOptions *options )
       if (vik_verbose)
         curl_easy_setopt ( curl, CURLOPT_VERBOSE, 1 );
       curl_easy_setopt ( curl, CURLOPT_URL, uri );
-      curl_easy_setopt ( curl, CURLOPT_WRITEDATA, f );
-      curl_easy_setopt ( curl, CURLOPT_WRITEFUNCTION, curl_write_func);
+      if (options->write_func == NULL) {
+	g_assert(f != NULL);
+	curl_easy_setopt ( curl, CURLOPT_WRITEDATA, f );
+	curl_easy_setopt ( curl, CURLOPT_WRITEFUNCTION, curl_write_func);
+      } else {
+	curl_easy_setopt ( curl, CURLOPT_WRITEDATA, to );
+	curl_easy_setopt ( curl, CURLOPT_WRITEFUNCTION, options->write_func);
+      }
       curl_easy_setopt ( curl, CURLOPT_NOPROGRESS, 0 );
       curl_easy_setopt ( curl, CURLOPT_PROGRESSDATA, NULL );
       curl_easy_setopt ( curl, CURLOPT_PROGRESSFUNCTION, curl_progress_func);
diff --git a/src/curl_download.h b/src/curl_download.h
index 0fc7518..e64918f 100644
--- a/src/curl_download.h
+++ b/src/curl_download.h
@@ -28,6 +28,6 @@
 
 void curl_download_init ();
 int curl_download_get_url ( const char *hostname, const char *uri, FILE *f, DownloadOptions *options, gboolean ftp );
-int curl_download_uri ( const char *uri, FILE *f, DownloadOptions *options );
+int curl_download_uri ( const char *uri, void *to, DownloadOptions *options );
 
 #endif
diff --git a/src/download.h b/src/download.h
index f4aa8df..20947ca 100644
--- a/src/download.h
+++ b/src/download.h
@@ -23,6 +23,7 @@
 #define _VIKING_DOWNLOAD_H
 
 #include <stdio.h>
+#include <curl/curl.h>
 
 /* File content check */
 typedef gboolean (*VikFileContentCheckerFunc) (FILE*);
@@ -47,6 +48,11 @@ typedef struct {
    * File content checker.
    */
   VikFileContentCheckerFunc check_file;
+
+  /**
+   * A function to receive data, instead of default one.
+   */
+  curl_write_callback	write_func;
 } DownloadOptions;
 
 /* TODO: convert to Glib */
diff --git a/src/uibuilder.c b/src/uibuilder.c
index d9b497c..bed64d3 100644
--- a/src/uibuilder.c
+++ b/src/uibuilder.c
@@ -120,7 +120,8 @@ GtkWidget *a_uibuilder_new_widget ( VikLayerParam *param, VikLayerParamData data
       if ( param->type == VIK_LAYER_PARAM_STRING )
       {
         rv = gtk_entry_new ();
-        gtk_entry_set_text ( GTK_ENTRY(rv), data.s );
+	if (data.s)
+		gtk_entry_set_text ( GTK_ENTRY(rv), data.s );
       }
       break;
     case VIK_LAYER_WIDGET_PASSWORD:
diff --git a/src/vikcoord.h b/src/vikcoord.h
index 8647cae..ff78874 100644
--- a/src/vikcoord.h
+++ b/src/vikcoord.h
@@ -42,8 +42,6 @@ typedef struct {
 /* notice we can cast to either UTM or LatLon */
 /* possible more modes to come? xy? we'll leave that as an option */
 
-VikCoord *vik_coord_new();
-
 void vik_coord_convert(VikCoord *coord, VikCoordMode dest_mode);
 void vik_coord_copy_convert(const VikCoord *coord, VikCoordMode dest_mode, VikCoord *dest);
 gdouble vik_coord_diff(const VikCoord *c1, const VikCoord *c2);
diff --git a/src/viklayer.c b/src/viklayer.c
index e85427e..f3e6406 100644
--- a/src/viklayer.c
+++ b/src/viklayer.c
@@ -38,6 +38,7 @@ extern VikLayerInterface vik_coord_layer_interface;
 extern VikLayerInterface vik_georef_layer_interface;
 extern VikLayerInterface vik_gps_layer_interface;
 extern VikLayerInterface vik_dem_layer_interface;
+extern VikLayerInterface vik_wms_layer_interface;
 
 enum {
   VL_UPDATE_SIGNAL,
@@ -126,6 +127,7 @@ static VikLayerInterface *vik_layer_interfaces[VIK_LAYER_NUM_TYPES] = {
   &vik_gps_layer_interface,
   &vik_maps_layer_interface,
   &vik_dem_layer_interface,
+  &vik_wms_layer_interface,
 };
 
 VikLayerInterface *vik_layer_get_interface ( gint type )
diff --git a/src/viklayer.h b/src/viklayer.h
index 0225fe5..df8beda 100644
--- a/src/viklayer.h
+++ b/src/viklayer.h
@@ -71,6 +71,7 @@ enum {
   VIK_LAYER_GPS,
   VIK_LAYER_MAPS,
   VIK_LAYER_DEM,
+  VIK_LAYER_WMS,
   VIK_LAYER_NUM_TYPES
 };
 
#include "globals.h"

#include <locale.h>
#include <glib/gstdio.h>
#include <glib/gi18n.h>

#include "download.h"
#include "curl_download.h"
#include "background.h"
#include "vikviewport.h"
#include "viklayer.h"
#include "vikwmslayer.h"
#include "preferences.h"

#include "icons/icons.h"

static VikWMSLayer *wms_layer_new(VikViewport *);
static void wms_layer_free(VikWMSLayer *);
static void wms_layer_draw(VikWMSLayer *, VikViewport *);
static VikLayerParamData wms_layer_getparam(VikWMSLayer *, guint16);
static gboolean wms_layer_setparam(VikWMSLayer *, guint16, VikLayerParamData, VikViewport *);
static gpointer wms_tool_create(VikWindow *, VikViewport *);
static gboolean wms_tool_click(VikWMSLayer *, GdkEventButton *, gpointer);
static gboolean wms_tool_release(VikWMSLayer *, GdkEventMotion *, gpointer);

static VikLayerParam wms_layer_params[] = {
	{"url", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("URL:"), VIK_LAYER_WIDGET_ENTRY },
};

enum { PARAM_URL = 0, NUM_PARAMS };

static VikToolInterface wms_layer_tools[] = {
    {
	.name = 	N_("Move WMS layer"),
	.create =	(VikToolConstructorFunc )wms_tool_create,
	.click =	(VikToolMouseFunc )wms_tool_click,
	.release =	(VikToolMouseFunc )wms_tool_release,
	.cursor_type =	GDK_CURSOR_IS_PIXMAP,
	.cursor_data =	&cursor_edwp_pixbuf,
    },
};

VikLayerInterface vik_wms_layer_interface = {
	.name =			"WMS",
	.icon =			&vikmapslayer_pixbuf,
	.tools =		wms_layer_tools,
	.tools_count =		sizeof(wms_layer_tools)/sizeof(VikToolInterface),
	.params =		wms_layer_params,
	.params_count =		NUM_PARAMS,
	.get_param =		(VikLayerFuncGetParam )wms_layer_getparam,
	.set_param =		(VikLayerFuncSetParam )wms_layer_setparam,
	.menu_items_selection =	VIK_MENU_ITEM_ALL,
	.create =		(VikLayerFuncCreate )wms_layer_new,
	.free =			(VikLayerFuncFree )wms_layer_free,
	.draw =			(VikLayerFuncDraw )wms_layer_draw,
};

struct _VikWMSLayer {
	VikLayer	 vl;
	gchar		 *url;

	GMutex		*mtx;
	GdkPixbufLoader	*loader;	/* != NULL => loading done */

	gdouble		delta_x, delta_y;

	gdouble		minx, miny, maxx, maxy;
	gint		width, height;

	gint		reqnum;		/* incrementing, to track requests */
};

static int download_thread(VikWMSLayer *, gpointer);
static size_t receive_image(void *, size_t, size_t, void *);

static DownloadOptions options = { 
	.write_func =	(curl_write_callback )receive_image,
};

static VikWMSLayer *
wms_layer_new(VikViewport *vp)
{
	VikWMSLayer *vwl = VIK_WMS_LAYER(g_object_new(VIK_WMS_LAYER_TYPE,
	    NULL));

	vik_layer_init(VIK_LAYER(vwl), VIK_LAYER_WMS);
	vwl->mtx = g_mutex_new();
	vwl->delta_x = vwl->delta_y = 0;
	vwl->reqnum = 0;

	return (vwl);
}

static void
wms_layer_free(VikWMSLayer *vwl)
{
	g_mutex_free(vwl->mtx);
	g_free(vwl->url);
}

static void
wms_layer_draw(VikWMSLayer *vwl, VikViewport *vp)
{
	gdouble minx, miny, maxx, maxy;
	gint width, height;

	vik_viewport_get_min_max_lat_lon(vp, &miny, &maxy, &minx, &maxx);
	width = vik_viewport_get_width(vp);
	height = vik_viewport_get_height(vp);

	miny += vwl->delta_y; maxy += vwl->delta_y;
	minx += vwl->delta_x; maxx += vwl->delta_x;

	/*
	 * We either are being called for the first time, due to new view
	 * of the screen or indirectly called via vik_layer_emit_update().
	 * In the first case we are usually being requested for a new image.
	 * We save new coords and image size in layer private data and launch
	 * download thread. In the second case we check that image is fully
	 * loaded, and draw it.
	 */
	g_mutex_lock(vwl->mtx);
	if (vwl->minx == minx && vwl->miny == miny && vwl->maxx == maxx &&
	    vwl->maxy == maxy && vwl->width == width && vwl->height == height) {
		GdkPixbufLoader	*loader;
		GdkPixbuf *pixbuf;

		/* Same coordinates, image not ready. */
		if (vwl->loader == NULL) {
			g_mutex_unlock(vwl->mtx);
			return;
		}

		loader = vwl->loader;
		vwl->loader = NULL;
		g_mutex_unlock(vwl->mtx);

		pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);

		vik_viewport_draw_pixbuf(vp, pixbuf, 0, 0, 0, 0, -1, -1);

		g_object_unref(G_OBJECT(loader));
	} else {
		gchar *tmp;

		vwl->minx = minx; vwl->miny = miny;
		vwl->maxx = maxx; vwl->maxy = maxy;
		vwl->width = width; vwl->height = height;
		vwl->reqnum++;

		tmp = g_strdup("Downloading WMS data");

		g_object_ref(G_OBJECT(vwl));
		a_background_thread(VIK_GTK_WINDOW_FROM_LAYER(vwl), tmp,
			(vik_thr_func )download_thread, vwl, NULL, NULL, 1);

		g_mutex_unlock(vwl->mtx);

		g_free(tmp);
	}
}

static int
download_thread(VikWMSLayer *vwl, gpointer threaddata)
{
	GdkPixbufLoader	*loader;
	gchar *locale, *url;
	int reqnum, res;

	loader = gdk_pixbuf_loader_new();

	g_mutex_lock(vwl->mtx);
	reqnum = vwl->reqnum;
	locale = setlocale(LC_NUMERIC, "C");
	url = g_strdup_printf("%s&bbox=%2.8f,%2.8f,%2.8f,%2.8f&width=%u&height=%u",
	    vwl->url, vwl->minx, vwl->miny, vwl->maxx, vwl->maxy, vwl->width,
	    vwl->height);
	setlocale(LC_NUMERIC, locale);
	g_mutex_unlock(vwl->mtx);

	res = curl_download_uri(url, loader, &options);

	gdk_pixbuf_loader_close(loader, NULL);

	if (res == 0) {
		g_mutex_lock(vwl->mtx);
		if (reqnum == vwl->reqnum) {
			if (vwl->loader)
				g_object_unref(G_OBJECT(vwl->loader));
			vwl->loader = loader;
			g_mutex_unlock(vwl->mtx);
			gdk_threads_enter();
			vik_layer_emit_update(VIK_LAYER(vwl));
			gdk_threads_leave();
		} else {
			/* New request has been issued, image not needed */
			g_mutex_unlock(vwl->mtx);
			g_object_unref(G_OBJECT(loader));
		}
	} else
		g_object_unref(G_OBJECT(loader));

	g_object_unref(G_OBJECT(vwl));

	return (res);
}

/*
 * This one is called by libcurl anytime we receive a chunk of data.
 */
static size_t
receive_image(void *ptr, size_t size, size_t nmemb, void *s)
{
	GdkPixbufLoader *loader = (GdkPixbufLoader *)s;
	GError *gx = NULL;

	gdk_pixbuf_loader_write(loader, (guchar *)ptr, size * nmemb, &gx);
	if (gx != NULL) {
		g_warning ("Couldn't read image data: %s", gx->message);
		g_error_free(gx);
	}

	return (size * nmemb);
}

static VikLayerParamData
wms_layer_getparam (VikWMSLayer *vwl, guint16 id)
{
	VikLayerParamData rv;

	switch (id) {
	case PARAM_URL:
		rv.s = vwl->url;
		break;
	}

	return (rv);
}

static gboolean
wms_layer_setparam(VikWMSLayer *vwl, guint16 id, VikLayerParamData data, VikViewport *vvp)
{
	switch (id) {
	case PARAM_URL:
		if (vwl->url)
			g_free(vwl->url);
		vwl->url = g_strdup(data.s);
		vik_layer_emit_update(VIK_LAYER(vwl));
		break;
	}

	return (TRUE);
}

/*
 * VikTool
 */

typedef struct {
	VikViewport	*vp;
	gint		oldx, oldy;
} tool_ed;

static gpointer
wms_tool_create(VikWindow *vw, VikViewport *vp)
{
	tool_ed *t = g_new(tool_ed, 1);
	t->vp = vp;

	return (t);
}

static gboolean
wms_tool_click(VikWMSLayer *vwl, GdkEventButton *event, gpointer ptr)
{
	tool_ed *t = ptr;

	g_assert(vwl->vl.type == VIK_LAYER_WMS);

	t->oldx = event->x;
	t->oldy = event->y;

	return (TRUE);
}

static gboolean
wms_tool_release(VikWMSLayer *vwl, GdkEventMotion *event, gpointer ptr)
{
	VikCoord oldcoord, coord;
	struct LatLon oldll, ll;
	tool_ed *t = ptr;

	g_assert(vwl->vl.type == VIK_LAYER_WMS);

	vik_viewport_screen_to_coord(t->vp, t->oldx, t->oldy, &oldcoord);
	vik_coord_to_latlon(&oldcoord, &oldll);
	vik_viewport_screen_to_coord(t->vp, event->x, event->y, &coord);
	vik_coord_to_latlon(&coord, &ll);

	vwl->delta_x += oldll.lon - ll.lon;
	vwl->delta_y += oldll.lat - ll.lat;

	return (TRUE);
}

/*
 * Syntax sugar.
 */

GType
vik_wms_layer_get_type()
{
	static GType vwl_type = 0;

	if (!vwl_type) {
		static const GTypeInfo vwl_info = {
			.class_size =		sizeof (VikWMSLayerClass),
			.instance_size =	sizeof (VikWMSLayer),
		};

		vwl_type = g_type_register_static(VIK_LAYER_TYPE, "VikWMSLayer",
		    &vwl_info, 0 );
	}

	return (vwl_type);
}

#if 0
/*
 * Non-layer stuff, that can later be cut to a separate file.
 */

#define	PARAMS_GROUP_KEY "wms"
#define	PARAMS_NAMESPACE "wms."
void
wms_preferences_init()
{
	a_preferences_register_group(PARAMS_GROUP_KEY, "WMS servers");
}
#endif
#ifndef	_VIKING_WMSLAYER_H
#define	_VIKING_WMSLAYER_H

#define VIK_WMS_LAYER_TYPE		(vik_wms_layer_get_type())
#define VIK_WMS_LAYER(obj)		(G_TYPE_CHECK_INSTANCE_CAST((obj), \
					    VIK_WMS_LAYER_TYPE, VikWMSLayer))
#define VIK_WMS_LAYER_CLASS(class)	(G_TYPE_CHECK_CLASS_CAST((class), \
					    VIK_WMS_LAYER_TYPE, VikWMSLayerClass))
#define IS_VIK_WMS_LAYER(obj)		(G_TYPE_CHECK_INSTANCE_TYPE((obj), \
					    VIK_WMS_LAYER_TYPE))
#define IS_VIK_WMS_LAYER_CLASS(class)	(G_TYPE_CHECK_CLASS_TYPE((class), \
					    VIK_WMS_LAYER_TYPE))

GType vik_wms_layer_get_type();

typedef struct _VikWMSLayerClass VikWMSLayerClass;
struct _VikWMSLayerClass
{
	VikLayerClass object_class;
};

typedef struct _VikWMSLayer VikWMSLayer;

#if 0
/* Non-layer stuff */
void wms_preferences_init();
#endif

#endif /* _VIKING_WMSLAYER_H */
------------------------------------------------------------------------------
Come build with us! The BlackBerry(R) Developer Conference in SF, CA
is the only developer event you need to attend this year. Jumpstart your
developing skills, take BlackBerry mobile applications to market and stay 
ahead of the curve. Join us from November 9 - 12, 2009. Register now!
http://p.sf.net/sfu/devconference
_______________________________________________
Viking-devel mailing list
Viking-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/viking-devel
Viking home page: http://viking.sf.net/

Reply via email to