X-Git-Url: http://pileus.org/git/?p=grits;a=blobdiff_plain;f=src%2Fwms.c;fp=src%2Fwms.c;h=0000000000000000000000000000000000000000;hp=2aba9dc418aa4add8549a9540dcfd4036c0ff83c;hb=733833b754f20c98976bca621d87d090621abff3;hpb=8060da4427ab86674362a745e9d4af15b8e48f95 diff --git a/src/wms.c b/src/wms.c deleted file mode 100644 index 2aba9dc..0000000 --- a/src/wms.c +++ /dev/null @@ -1,541 +0,0 @@ -/* - * 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 . - */ - -/** - * http://www.nasa.network.com/elev? - * SERVICE=WMS& - * VERSION=1.1.0& - * REQUEST=GetMap& - * LAYERS=bmng200406& - * STYLES=& - * SRS=EPSG:4326& - * BBOX=-180,-90,180,90& - * FORMAT=image/jpeg& - * WIDTH=600& - * HEIGHT=300 - * - * http://www.nasa.network.com/elev? - * SERVICE=WMS& - * VERSION=1.1.0& - * REQUEST=GetMap& - * LAYERS=srtm30& - * STYLES=& - * SRS=EPSG:4326& - * BBOX=-180,-90,180,90& - * FORMAT=application/bil32& - * WIDTH=600& - * HEIGHT=300 - */ - -#include -#include -#include -#include -#include -#include - -#include "wms.h" - -/* TODO: try to remove these */ -#include "gis-world.h" -#include - -gchar *wms_make_uri(WmsInfo *info, gdouble xmin, gdouble ymin, gdouble xmax, gdouble ymax) -{ - return g_strdup_printf( - "%s?" - "SERVICE=WMS&" - "VERSION=1.1.0&" - "REQUEST=GetMap&" - "LAYERS=%s&" - "STYLES=&" - "SRS=EPSG:4326&" - "BBOX=%f,%f,%f,%f&" - "FORMAT=%s&" - "WIDTH=%d&" - "HEIGHT=%d", - info->uri_prefix, - info->uri_layer, - xmin, ymin, xmax, ymax, - info->uri_format, - info->width, - info->height); -} - -/**************** - * WmsCacheNode * - ****************/ -WmsCacheNode *wms_cache_node_new(WmsCacheNode *parent, - gdouble xmin, gdouble ymin, gdouble xmax, gdouble ymax, gint width) -{ - WmsCacheNode *self = g_new0(WmsCacheNode, 1); - //g_debug("WmsCacheNode: new - %p %+7.3f,%+7.3f,%+7.3f,%+7.3f", - // self, xmin, ymin, xmax, ymax); - self->latlon[0] = xmin; - self->latlon[1] = ymin; - self->latlon[2] = xmax; - self->latlon[3] = ymax; - self->parent = parent; - if (ymin <= 0 && ymax >= 0) - self->res = ll2m(xmax-xmin, 0)/width; - else - self->res = ll2m(xmax-xmin, MIN(ABS(ymin),ABS(ymax)))/width; - return self; -} - -void wms_cache_node_free(WmsCacheNode *node, WmsFreeer freeer) -{ - //g_debug("WmsCacheNode: free - %p", node); - if (node->data) { - freeer(node); - node->data = NULL; - } - for (int x = 0; x < 4; x++) - for (int y = 0; y < 4; y++) - if (node->children[x][y]) - wms_cache_node_free(node->children[x][y], freeer); - g_free(node); -} - -/*********** - * WmsInfo * - ***********/ -WmsInfo *wms_info_new(WmsLoader loader, WmsFreeer freeer, - gchar *uri_prefix, gchar *uri_layer, gchar *uri_format, - gchar *cache_prefix, gchar *cache_ext, - gint resolution, gint width, gint height -) { - WmsInfo *self = g_new0(WmsInfo, 1); - self->loader = loader; - self->freeer = freeer; - self->uri_prefix = uri_prefix; - self->uri_layer = uri_layer; - self->uri_format = uri_format; - self->cache_prefix = cache_prefix; - self->cache_ext = cache_ext; - self->resolution = resolution; - self->width = width; - self->height = height; - - self->max_age = 60; - self->atime = time(NULL); - self->gc_source = g_timeout_add_seconds(1, (GSourceFunc)wms_info_gc, self); - self->cache_root = wms_cache_node_new(NULL, -180, -90, 180, 90, width); - self->soup = soup_session_async_new(); - return self; -} - -struct _CacheImageState { - WmsInfo *info; - gchar *path; - FILE *output; - WmsCacheNode *node; - WmsChunkCallback user_chunk_cb; - WmsDoneCallback user_done_cb; - gpointer user_data; -}; -void wms_info_soup_chunk_cb(SoupMessage *message, SoupBuffer *chunk, gpointer _state) -{ - struct _CacheImageState *state = _state; - if (!SOUP_STATUS_IS_SUCCESSFUL(message->status_code)) - return; - - goffset total = soup_message_headers_get_content_length(message->response_headers); - if (fwrite(chunk->data, chunk->length, 1, state->output) != 1) - g_warning("WmsInfo: soup_chunk_cb - eror writing data"); - - gdouble cur = (gdouble)ftell(state->output); - if (state->user_chunk_cb) - state->user_chunk_cb(cur, total, state->user_data); -} -void wms_info_soup_done_cb(SoupSession *session, SoupMessage *message, gpointer _state) -{ - struct _CacheImageState *state = _state; - if (!SOUP_STATUS_IS_SUCCESSFUL(message->status_code)) - return; - gchar *dest = g_strndup(state->path, strlen(state->path)-5); - g_rename(state->path, dest); - state->node->atime = time(NULL); - state->info->loader(state->node, dest, state->info->width, state->info->height); - if (state->user_done_cb) - state->user_done_cb(state->node, state->user_data); - state->node->caching = FALSE; - fclose(state->output); - g_free(state->path); - g_free(dest); - g_free(state); -} -gboolean wms_info_cache_loader_cb(gpointer _state) -{ - struct _CacheImageState *state = _state; - state->node->atime = time(NULL); - state->info->loader(state->node, state->path, state->info->width, state->info->height); - if (state->user_done_cb) - state->user_done_cb(state->node, state->user_data); - state->node->caching = FALSE; - g_free(state->path); - g_free(state); - return FALSE; -} -/** - * Cache required tiles - * 1. Load closest tile that's stored on disk - * 2. Fetch the correct tile from the remote server - */ -void wms_info_cache(WmsInfo *info, gdouble resolution, gdouble lat, gdouble lon, - WmsChunkCallback chunk_callback, WmsDoneCallback done_callback, - gpointer user_data) -{ - /* Base cache path */ - gdouble x=lon, y=lat; - gdouble xmin=-180, ymin=-90, xmax=180, ymax=90; - gdouble xdist = xmax - xmin; - gdouble ydist = ymax - ymin; - int xpos=0, ypos=0; - gdouble cur_lat = 0; - - WmsCacheNode *target_node = info->cache_root; - WmsCacheNode *approx_node = NULL; - - GString *target_path = g_string_new(g_get_user_cache_dir()); - g_string_append(target_path, G_DIR_SEPARATOR_S); - g_string_append(target_path, "wms"); - g_string_append(target_path, G_DIR_SEPARATOR_S); - g_string_append(target_path, info->cache_prefix); - gchar *approx_path = NULL; - - /* Create nodes to tiles, determine paths and lat-lon coords */ - while (TRUE) { - /* Update the best approximation if it exists on disk */ - gchar *tmp = g_strconcat(target_path->str, info->cache_ext, NULL); - if (g_file_test(tmp, G_FILE_TEST_EXISTS)) { - g_free(approx_path); - approx_node = target_node; - approx_path = tmp; - } else { - g_free(tmp); - } - - /* Break if current resolution (m/px) is good enough */ - if (ll2m(xdist, cur_lat)/info->width <= resolution || - ll2m(xdist, cur_lat)/info->width <= info->resolution) - break; - - /* Get locations for the correct sub-tile */ - xpos = (int)(((x - xmin) / xdist) * 4); - ypos = (int)(((y - ymin) / ydist) * 4); - if (xpos == 4) xpos--; - if (ypos == 4) ypos--; - xdist /= 4; - ydist /= 4; - xmin = xmin + xdist*(xpos+0); - ymin = ymin + ydist*(ypos+0); - xmax = xmin + xdist; - ymax = ymin + ydist; - cur_lat = MIN(ABS(ymin), ABS(ymax)); - - /* Update target for correct sub-tile */ - g_string_append_printf(target_path, "/%d%d", xpos, ypos); - if (target_node->children[xpos][ypos] == NULL) - target_node->children[xpos][ypos] = - wms_cache_node_new(target_node, - xmin, ymin, xmax, ymax, info->width); - target_node = target_node->children[xpos][ypos]; - } - - /* Load disk on-disk approximation, TODO: async */ - if (approx_node && !approx_node->data && !approx_node->caching) { - approx_node->caching = TRUE; - struct _CacheImageState *state = g_new0(struct _CacheImageState, 1); - state->info = info; - state->path = approx_path; - state->node = approx_node; - state->user_done_cb = done_callback; - state->user_data = user_data; - g_idle_add(wms_info_cache_loader_cb, state); - } else { - g_free(approx_path); - } - - /* If target image is not on-disk, download it now */ - if (target_node != approx_node && !target_node->caching) { - target_node->caching = TRUE; - g_string_append(target_path, info->cache_ext); - g_string_append(target_path, ".part"); - - gchar *dirname = g_path_get_dirname(target_path->str); - g_mkdir_with_parents(dirname, 0755); - g_free(dirname); - - struct _CacheImageState *state = g_new0(struct _CacheImageState, 1); - state->info = info; - state->path = target_path->str; - state->output = fopen(target_path->str, "a"); - state->node = target_node; - state->user_chunk_cb = chunk_callback; - state->user_done_cb = done_callback; - state->user_data = user_data; - - gchar *uri = wms_make_uri(info, xmin, ymin, xmax, ymax); - SoupMessage *message = soup_message_new("GET", uri); - g_signal_connect(message, "got-chunk", G_CALLBACK(wms_info_soup_chunk_cb), state); - soup_message_headers_set_range(message->request_headers, ftell(state->output), -1); - - soup_session_queue_message(info->soup, message, wms_info_soup_done_cb, state); - - g_debug("Caching file: %s -> %s", uri, state->path); - g_free(uri); - g_string_free(target_path, FALSE); - } else { - g_string_free(target_path, TRUE); - } -} -/* TODO: - * - Store WmsCacheNode in point and then use parent pointers to go up/down - * - If resolution doesn't change, tell caller to skip remaining calculations - */ -WmsCacheNode *wms_info_fetch(WmsInfo *info, WmsCacheNode *root, - gdouble resolution, gdouble lat, gdouble lon, - gboolean *correct) -{ - if (root && root->data && !root->caching && - root->latlon[0] <= lon && lon <= root->latlon[2] && - root->latlon[1] <= lat && lat <= root->latlon[3] && - root->res <= resolution && - (!root->parent || root->parent->res > resolution)) { - *correct = TRUE; - info->atime = time(NULL); - root->atime = info->atime; - return root; - } - - if (info->cache_root == NULL) { - *correct = FALSE; - return NULL; - } - WmsCacheNode *node = info->cache_root; - WmsCacheNode *best = (node && node->data ? node : NULL); - gdouble xmin=-180, ymin=-90, xmax=180, ymax=90, xdist=360, ydist=180; - gdouble cur_lat = 0; - int xpos=0, ypos=0; - gdouble cur_res = ll2m(xdist, cur_lat)/info->width; - while (cur_res > resolution && - cur_res > info->resolution) { - - xpos = ((lon - xmin) / xdist) * 4; - ypos = ((lat - ymin) / ydist) * 4; - if (xpos == 4) xpos--; - if (ypos == 4) ypos--; - xdist /= 4; - ydist /= 4; - xmin = xmin + xdist*(xpos+0); - ymin = ymin + ydist*(ypos+0); - cur_lat = MIN(ABS(ymin), ABS(ymax)); - - node = node->children[xpos][ypos]; - if (node == NULL) - break; - if (node->data) - best = node; - - cur_res = ll2m(xdist, cur_lat)/info->width; - } - if (correct) - *correct = (node && node == best); - info->atime = time(NULL); - if (best) - best->atime = info->atime; - return best; -} - -WmsCacheNode *wms_info_fetch_cache(WmsInfo *info, WmsCacheNode *root, - gdouble res, gdouble lat, gdouble lon, - WmsChunkCallback chunk_callback, WmsDoneCallback done_callback, gpointer user_data) -{ - /* Fetch a node, if it isn't cached, cache it, also keep it's parent cached */ - gboolean correct; - WmsCacheNode *node = wms_info_fetch(info, root, res, lat, lon, &correct); - if (!node || !correct) - wms_info_cache(info, res, lat, lon, chunk_callback, done_callback, user_data); - //else if (node->parent && node->parent->data == NULL) - // wms_info_cache(info, node->parent->res, lat, lon, chunk_callback, done_callback, user_data); - //else if (node->parent) - // node->parent->atime = node->atime; - return node; -} - -/* Delete unused nodes and prune empty branches */ -static WmsCacheNode *wms_info_gc_cb(WmsInfo *self, WmsCacheNode *node) -{ - gboolean empty = FALSE; - if (self->atime - node->atime > self->max_age && - node->data && node != self->cache_root && !node->caching) { - g_debug("WmsInfo: gc - expired node %p", node); - self->freeer(node); - node->data = NULL; - empty = TRUE; - } - for (int x = 0; x < 4; x++) - for (int y = 0; y < 4; y++) - if (node->children[x][y]) { - node->children[x][y] = - wms_info_gc_cb(self, node->children[x][y]); - empty = FALSE; - } - if (empty) { - g_debug("WmsInfo: gc - empty branch %p", node); - /* - * TODO: Don't prune nodes while we're caching WmsCacheNodes in the Roam triangles - * and points - g_free(node); - return NULL; - */ - return node; - } else { - return node; - } -} - -gboolean wms_info_gc(WmsInfo *self) -{ - if (!wms_info_gc_cb(self, self->cache_root)) - g_warning("WmsInfo: gc - root not should not be empty"); - return TRUE; -} - -void wms_info_free(WmsInfo *self) -{ - wms_cache_node_free(self->cache_root, self->freeer); - g_object_unref(self->soup); - g_free(self); -} - - -/************************ - * Blue Marble Next Gen * - ************************/ -void bmng_opengl_loader(WmsCacheNode *node, const gchar *path, gint width, gint height) -{ - GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, NULL); - node->data = g_new0(guint, 1); - - /* Load image */ - guchar *pixels = gdk_pixbuf_get_pixels(pixbuf); - int alpha = gdk_pixbuf_get_has_alpha(pixbuf); - int nchan = 4; // gdk_pixbuf_get_n_channels(pixbuf); - - /* Create Texture */ - glGenTextures(1, node->data); - glBindTexture(GL_TEXTURE_2D, *(guint*)node->data); - - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glPixelStorei(GL_PACK_ALIGNMENT, 1); - glTexImage2D(GL_TEXTURE_2D, 0, nchan, width, height, 0, - (alpha ? GL_RGBA : GL_RGB), GL_UNSIGNED_BYTE, pixels); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); - - g_object_unref(pixbuf); - g_debug("WmsCacheNode: bmng_opengl_loader: %s -> %p", path, node->data); -} -void bmng_opengl_freeer(WmsCacheNode *node) -{ - g_debug("WmsCacheNode: bmng_opengl_freeer: %p", node->data); - glDeleteTextures(1, node->data); - g_free(node->data); -} - -void bmng_pixbuf_loader(WmsCacheNode *node, const gchar *path, gint width, gint height) -{ - node->data = gdk_pixbuf_new_from_file(path, NULL); - g_debug("WmsCacheNode: bmng_opengl_loader: %s -> %p", path, node->data); -} -void bmng_pixbuf_freeer(WmsCacheNode *node) -{ - g_debug("WmsCacheNode: bmng_opengl_freeer: %p", node->data); - g_object_unref(node->data); -} - -WmsInfo *wms_info_new_for_bmng(WmsLoader loader, WmsFreeer freeer) -{ - loader = loader ?: bmng_opengl_loader; - freeer = freeer ?: bmng_opengl_freeer; - return wms_info_new(loader, freeer, - "http://www.nasa.network.com/wms", "bmng200406", "image/jpeg", - "bmng", ".jpg", 500, 512, 256); -} - -/******************************************** - * Shuttle Radar Topography Mission 30 Plus * - ********************************************/ -void srtm_bil_loader(WmsCacheNode *node, const gchar *path, gint width, gint height) -{ - WmsBil *bil = g_new0(WmsBil, 1); - gchar **char_data = (gchar**)&bil->data; - g_file_get_contents(path, char_data, NULL, NULL); - bil->width = width; - bil->height = height; - node->data = bil; - g_debug("WmsCacheNode: srtm_opengl_loader: %s -> %p", path, node->data); -} -void srtm_bil_freeer(WmsCacheNode *node) -{ - g_debug("WmsCacheNode: srtm_opengl_freeer: %p", node); - g_free(((WmsBil*)node->data)->data); - g_free(node->data); -} - -void srtm_pixbuf_loader(WmsCacheNode *node, const gchar *path, gint width, gint height) -{ - GdkPixbuf *pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, width, height); - guchar *pixels = gdk_pixbuf_get_pixels(pixbuf); - gint stride = gdk_pixbuf_get_rowstride(pixbuf); - - gint16 *data; - gchar **char_data = (gchar**)&data; - g_file_get_contents(path, char_data, NULL, NULL); - for (int r = 0; r < height; r++) { - for (int c = 0; c < width; c++) { - gint16 value = data[r*width + c]; - //guchar color = (float)(MAX(value,0))/8848 * 255; - guchar color = (float)value/8848 * 255; - pixels[r*stride + c*3 + 0] = color; - pixels[r*stride + c*3 + 1] = color; - pixels[r*stride + c*3 + 2] = color; - } - } - g_free(data); - - node->data = pixbuf; - g_debug("WmsCacheNode: srtm_opengl_loader: %s -> %p", path, node->data); -} -void srtm_pixbuf_freeer(WmsCacheNode *node) -{ - g_debug("WmsCacheNode: srtm_opengl_freeer: %p", node); - g_object_unref(node->data); -} - -WmsInfo *wms_info_new_for_srtm(WmsLoader loader, WmsFreeer freeer) -{ - loader = loader ?: srtm_bil_loader; - freeer = freeer ?: srtm_bil_freeer; - return wms_info_new(loader, freeer, - "http://www.nasa.network.com/elev", "srtm30", "application/bil", - "srtm", ".bil", 500, 512, 256); -}