On Wed, Oct 21, 2009 at 01:53:25AM +0400, Gleb Smirnoff wrote:
T> I'll submit updated patch tomorrow.
I'll better send it right now. Better to share it earlier, and
outdate the previous WIp patch.
P.S. Going to bed. To hack more tomorrow.
--
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..05a4231 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,18 @@ 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);
+ }
+ if (options->header_func) {
+ curl_easy_setopt ( curl, CURLOPT_HEADERDATA, to);
+ curl_easy_setopt ( curl, CURLOPT_HEADERFUNCTION, options->header_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..5c429f0 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,15 @@ typedef struct {
* File content checker.
*/
VikFileContentCheckerFunc check_file;
+
+ /**
+ * A function to receive data, instead of default one.
+ */
+ curl_write_callback write_func;
+ /**
+ * A function to read header.
+ */
+ curl_write_callback header_func;
} DownloadOptions;
/* TODO: convert to Glib */
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 <glib/gprintf.h>
#include "background.h"
#include "curl_download.h"
#include "dialog.h"
#include "download.h"
#include "preferences.h"
#include "viklayer.h"
#include "vikviewport.h"
#include "vikwmslayer.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;
gboolean url_invalid;
GMutex *mtx;
GdkPixbufLoader *loader;
gchar *errstr;
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_body(void *, size_t, size_t, void *);
static size_t receive_header(void *, size_t, size_t, void *);
static DownloadOptions options = {
.write_func = (curl_write_callback )receive_body,
.header_func = (curl_write_callback )receive_header,
};
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;
if (vwl->errstr != NULL) {
gchar *errstr = vwl->errstr;
vwl->errstr = NULL;
g_mutex_unlock(vwl->mtx);
a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vwl), GTK_MESSAGE_ERROR, errstr, NULL);
g_free(errstr);
return;
}
/* Same coordinates, no error, but 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;
if (vwl->url_invalid) {
g_mutex_unlock(vwl->mtx);
return;
}
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);
}
}
typedef struct {
GdkPixbufLoader *loader;
} ImgDownloadInfo;
typedef struct {
GMarkupParseContext *ctx;
gint state;
gchar *errstr;
} XMLDownloadInfo;
typedef struct {
gint ctype;
union {
ImgDownloadInfo img;
XMLDownloadInfo xml;
};
} DownloadInfo;
enum { CTYPE_UNKNOWN = 0, CTYPE_OGCXML, CTYPE_IMAGE };
static size_t receive_image(void *ptr, size_t len, DownloadInfo *di);
static size_t receive_ogcxml(void *ptr, size_t len, DownloadInfo *di);
static int
download_thread(VikWMSLayer *vwl, gpointer threaddata)
{
DownloadInfo di;
gchar *locale, *url;
int reqnum, res;
GError *gx = NULL;
memset(&di, 0, sizeof(di));
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, &di, &options);
if (res != 0 || di.ctype == CTYPE_UNKNOWN)
goto done;
/* Handling error from server. */
if (di.ctype == CTYPE_OGCXML) {
gchar *errstr;
g_assert(di.xml.ctx != NULL);
g_markup_parse_context_free(di.xml.ctx);
errstr = g_strdup_printf("WMS server responded with error: %s Stopping WMS layer.\n", di.xml.errstr);
g_free(di.xml.errstr);
g_mutex_lock(vwl->mtx);
vwl->url_invalid = TRUE;
if (vwl->loader) {
g_object_unref(G_OBJECT(vwl->loader));
vwl->loader = NULL;
}
vwl->errstr = errstr;
g_mutex_unlock(vwl->mtx);
gdk_threads_enter();
vik_layer_emit_update(VIK_LAYER(vwl));
gdk_threads_leave();
goto done;
}
/* Normal image processing. */
g_assert(di.ctype == CTYPE_IMAGE);
g_assert(di.img.loader != NULL);
gdk_pixbuf_loader_close(di.img.loader, &gx);
if (gx != NULL) {
g_warning ("Couldn't read image data: %s", gx->message);
g_error_free(gx);
g_object_unref(G_OBJECT(di.img.loader));
goto done;
}
g_mutex_lock(vwl->mtx);
if (reqnum == vwl->reqnum) {
if (vwl->loader)
g_object_unref(G_OBJECT(vwl->loader));
vwl->loader = di.img.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(di.img.loader));
}
done:
g_object_unref(G_OBJECT(vwl));
return (res);
}
/*
* Parse headers in HTTP reply, called by libcurl.
*/
static size_t
receive_header(void *ptr, size_t size, size_t nmemb, void *v)
{
DownloadInfo *di = (DownloadInfo *)v;
gchar *header, *value, *s0 = (gchar *)ptr;
gint len = size * nmemb;
s0[len] = '\0';
value = g_strstr_len(s0, -1, ": ");
if (value == NULL)
return (len);
value[0] = '\0';
header = g_ascii_strdown(s0, len);
value += 2;
g_strchomp(value);
if (g_strcmp0(header, "content-type") == 0) {
if (g_str_has_prefix(value, "image"))
di->ctype = CTYPE_IMAGE;
if (g_strcmp0(value, "application/vnd.ogc.se_xml") == 0)
di->ctype = CTYPE_OGCXML;
}
g_free(header);
return (len);
}
/*
* Read HTTP body, called by libcurl.
*/
static size_t
receive_body(void *ptr, size_t size, size_t nmemb, void *v)
{
DownloadInfo *di = (DownloadInfo *)v;
gint len = size * nmemb;
switch(di->ctype) {
case CTYPE_IMAGE:
return (receive_image(ptr, len, di));
break;
case CTYPE_OGCXML:
return (receive_ogcxml(ptr, len, di));
break;
}
g_warning("%s: unknown content-type", __func__);
return (len);
}
static size_t
receive_image(void *ptr, size_t len, DownloadInfo *di)
{
GError *gx = NULL;
if (di->img.loader == NULL)
di->img.loader = gdk_pixbuf_loader_new();
gdk_pixbuf_loader_write(di->img.loader, (guchar *)ptr, len, &gx);
if (gx != NULL) {
g_warning ("Couldn't read image data: %s", gx->message);
g_error_free(gx);
}
return (len);
}
/*
* VikLayerParam
*/
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);
vwl->url_invalid = FALSE;
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);
}
/*
* Dragging WMS layer means adjusting its georeferincing data.
*/
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);
}
// To be separated to ogc_xml.c
static void ogcxml_start_element(GMarkupParseContext *, const gchar *, const gchar **, const gchar **, gpointer, GError **);
static void ogcxml_end_element(GMarkupParseContext *, const gchar *, gpointer, GError **);
static void ogcxml_text(GMarkupParseContext *, const gchar *, gsize, gpointer, GError **);
static GMarkupParser OGC_XMLParser = {
.start_element = ogcxml_start_element,
.end_element = ogcxml_end_element,
.text = ogcxml_text,
};
enum { OGC_XML_UNKNOWN = 0, OGC_XML_SERVICE_EXCEPTION, };
static void
ogcxml_start_element(GMarkupParseContext *ctx, const gchar *name, const gchar **attrs, const gchar **values, gpointer data, GError **error)
{
DownloadInfo *di = data;
if (g_strcmp0(name, "ServiceException") == 0)
di->xml.state = OGC_XML_SERVICE_EXCEPTION;
}
static void
ogcxml_end_element(GMarkupParseContext *ctx, const gchar *name, gpointer data, GError **error)
{
DownloadInfo *di = data;
/* XXX: todo push/pop states? */
di->xml.state = OGC_XML_UNKNOWN;
}
static void
ogcxml_text(GMarkupParseContext *ctx, const gchar *text, gsize len, gpointer data, GError **error)
{
DownloadInfo *di = data;
if (di->xml.state == OGC_XML_SERVICE_EXCEPTION)
di->xml.errstr = g_strstrip(g_strndup(text, len));
}
static size_t
receive_ogcxml(void *ptr, size_t len, DownloadInfo *di)
{
GError *gx = NULL;
if (di->xml.ctx == NULL)
di->xml.ctx = g_markup_parse_context_new(&OGC_XMLParser,
G_MARKUP_PREFIX_ERROR_POSITION, di, NULL);
g_markup_parse_context_parse(di->xml.ctx, (gchar *)ptr, len, &gx);
if (gx != NULL) {
g_warning ("Couldn't parse OGC XML data: %s", gx->message);
g_error_free(gx);
}
return (len);
}
#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/