int mapcache_imageio_is_valid_format(mapcache_context *ctx, mapcache_buffer *buffer) { mapcache_image_format_type t = mapcache_imageio_header_sniff(ctx,buffer); if(t==GC_PNG || t==GC_JPEG) { return MAPCACHE_TRUE; } else { return MAPCACHE_FALSE; } }
mapcache_image* mapcache_imageio_decode(mapcache_context *ctx, mapcache_buffer *buffer) { mapcache_image_format_type type = mapcache_imageio_header_sniff(ctx,buffer); if(type == GC_PNG) { return _mapcache_imageio_png_decode(ctx,buffer); } else if(type == GC_JPEG) { return _mapcache_imageio_jpeg_decode(ctx,buffer); } else { ctx->set_error(ctx, 500, "mapcache_imageio_decode: unrecognized image format"); return NULL; } }
mapcache_http_response *mapcache_core_get_map(mapcache_context *ctx, mapcache_request_get_map *req_map) { mapcache_image_format *format = NULL; mapcache_http_response *response; mapcache_map *basemap = NULL; char *timestr; #ifdef DEBUG if(req_map->nmaps ==0) { ctx->set_error(ctx,500,"BUG: get_map called with 0 maps"); return NULL; } #endif if(req_map->getmap_strategy == MAPCACHE_GETMAP_ERROR) { ctx->set_error(ctx, 404, "full wms support disabled"); return NULL; } format = NULL; response = mapcache_http_response_create(ctx->pool); if(req_map->getmap_strategy == MAPCACHE_GETMAP_ASSEMBLE) { basemap = mapcache_assemble_maps(ctx, req_map->maps, req_map->nmaps, req_map->resample_mode); if(GC_HAS_ERROR(ctx)) return NULL; } else if(!ctx->config->non_blocking && req_map->getmap_strategy == MAPCACHE_GETMAP_FORWARD) { int i; basemap = req_map->maps[0]; for(i=0; i<req_map->nmaps; i++) { if(!req_map->maps[i]->tileset->source) { ctx->set_error(ctx,404,"cannot forward request for tileset %s: no source configured", req_map->maps[i]->tileset->name); return NULL; } } basemap->tileset->source->render_map(ctx, basemap); if(GC_HAS_ERROR(ctx)) return NULL; if(req_map->nmaps>1) { if(!basemap->raw_image) { basemap->raw_image = mapcache_imageio_decode(ctx,basemap->encoded_data); if(GC_HAS_ERROR(ctx)) return NULL; } for(i=1; i<req_map->nmaps; i++) { mapcache_map *overlaymap = req_map->maps[i]; overlaymap->tileset->source->render_map(ctx, overlaymap); if(GC_HAS_ERROR(ctx)) return NULL; if(!overlaymap->raw_image) { overlaymap->raw_image = mapcache_imageio_decode(ctx,overlaymap->encoded_data); if(GC_HAS_ERROR(ctx)) return NULL; } if(GC_HAS_ERROR(ctx)) return NULL; mapcache_image_merge(ctx,basemap->raw_image,overlaymap->raw_image); if(GC_HAS_ERROR(ctx)) return NULL; if(!basemap->expires || overlaymap->expires<basemap->expires) basemap->expires = overlaymap->expires; } } } else { ctx->set_error(ctx,400,"failed getmap, readonly mode"); return NULL; } if(basemap->raw_image) { format = req_map->getmap_format; /* always defined, defaults to JPEG */ response->data = format->write(ctx,basemap->raw_image,format); if(GC_HAS_ERROR(ctx)) { return NULL; } } else { /* this case happens when we have a forward strategy for a single tileset */ #ifdef DEBUG if(!basemap->encoded_data) { ctx->set_error(ctx,500,"###BUG### core_get_map failed with null encoded_data"); return NULL; } #endif response->data = basemap->encoded_data; } /* compute the content-type */ if(format && format->mime_type) { apr_table_set(response->headers,"Content-Type",format->mime_type); } else { mapcache_image_format_type t = mapcache_imageio_header_sniff(ctx,response->data); if(t == GC_PNG) apr_table_set(response->headers,"Content-Type","image/png"); else if(t == GC_JPEG) apr_table_set(response->headers,"Content-Type","image/jpeg"); } /* compute expiry headers */ if(basemap->expires) { apr_time_t now = apr_time_now(); apr_time_t additional = apr_time_from_sec(basemap->expires); apr_time_t texpires = now + additional; apr_table_set(response->headers, "Cache-Control", apr_psprintf(ctx->pool, "max-age=%d", basemap->expires)); timestr = apr_palloc(ctx->pool, APR_RFC822_DATE_LEN); apr_rfc822_date(timestr, texpires); apr_table_setn(response->headers, "Expires", timestr); } response->mtime = basemap->mtime; return response; }
mapcache_http_response *mapcache_core_get_tile(mapcache_context *ctx, mapcache_request_get_tile *req_tile) { int expires = 0; mapcache_http_response *response; int i,is_empty=1 /* response image is initially empty */; char *timestr; mapcache_image *base=NULL; mapcache_image_format *format = NULL; #ifdef DEBUG if(req_tile->ntiles ==0) { ctx->set_error(ctx,500,"BUG: get_tile called with 0 tiles"); return NULL; } #endif response = mapcache_http_response_create(ctx->pool); mapcache_prefetch_tiles(ctx,req_tile->tiles,req_tile->ntiles); if(GC_HAS_ERROR(ctx)) return NULL; /* loop through tiles, and eventually merge them vertically together */ for(i=0; i<req_tile->ntiles; i++) { mapcache_tile *tile = req_tile->tiles[i]; /* shortcut */ if(tile->mtime && (tile->mtime < response->mtime || response->mtime == 0)) response->mtime = tile->mtime; if(tile->expires && (tile->expires < expires || expires == 0)) { expires = tile->expires; } if(tile->nodata) { /* treat the special case where the cache explicitely stated that the tile was empty, and we don't have any vertical merging to do */ if(tile->encoded_data && req_tile->ntiles == 1) { response->data = tile->encoded_data; /* we don't touch is_empty, as we have access to the encoded empty image, but the resulting tile is empty */ } continue; } /* treat the most common case: - we have a single tile request (i.e. isempty is true) - the cache returned the encoded image */ if(is_empty && tile->encoded_data) { response->data = tile->encoded_data; /* just in case we also have the raw image data available, keep a ref to it if we need to merge another tile ontop of it*/ if(tile->raw_image) { base = tile->raw_image; } is_empty = 0; /* we now know we might need to do some vertical merging */ continue; } /* if we're here, either * - we need to merge the current tile onto the previous one(s), or * - we only have the tile's raw data available */ if(!is_empty) { /* we have an existing tile, so we know we need to merge the current one into it */ if(!base) { /* the existing tile has not been decoded yet, but we need the access to the raw pixels*/ base = mapcache_imageio_decode(ctx, response->data); if(!base) return NULL; } response->data = NULL; /* the encoded data is now obsolete, as we will be merging the current tile */ /* we need to access the current tile's pixel data */ if(!tile->raw_image) { tile->raw_image = mapcache_imageio_decode(ctx,tile->encoded_data); if(!tile->raw_image) return NULL; } mapcache_image_merge(ctx, base, tile->raw_image); } else { /* we don't need to merge onto an existing tile and don't have access to the tile's encoded data. * * we don't encode the tile's raw image data just yet because we might need to merge another one on top * of it later. */ base = tile->raw_image; is_empty = 0; } } if(!response->data) { /* we need to encode the raw image data*/ if(base) { if(req_tile->format) { format = req_tile->format; } else { format = req_tile->tiles[0]->tileset->format; if(!format) { format = ctx->config->default_image_format; /* this one is always defined */ } } response->data = format->write(ctx, base, format); if(GC_HAS_ERROR(ctx)) { return NULL; } } else { #ifdef DEBUG if(!is_empty) { ctx->set_error(ctx,500,"BUG: no image data to encode, but tile not marked as empty"); return NULL; } #endif unsigned char empty[5] = {'#',0,0,0,0}; response->data = mapcache_empty_png_decode(ctx,empty,&is_empty); /* is_empty is unchanged and left to 1 */ format = mapcache_configuration_get_image_format(ctx->config,"PNG8"); } } /* compute the content-type */ mapcache_image_format_type t = mapcache_imageio_header_sniff(ctx,response->data); if(t == GC_PNG) apr_table_set(response->headers,"Content-Type","image/png"); else if(t == GC_JPEG) apr_table_set(response->headers,"Content-Type","image/jpeg"); /* compute expiry headers */ if(expires) { apr_time_t now = apr_time_now(); apr_time_t additional = apr_time_from_sec(expires); apr_time_t texpires = now + additional; apr_table_set(response->headers, "Cache-Control",apr_psprintf(ctx->pool, "max-age=%d", expires)); timestr = apr_palloc(ctx->pool, APR_RFC822_DATE_LEN); apr_rfc822_date(timestr, texpires); apr_table_setn(response->headers, "Expires", timestr); } return response; }
mapcache_http_response *mapcache_core_get_tile(mapcache_context *ctx, mapcache_request_get_tile *req_tile) { int expires = 0; mapcache_http_response *response; int i,first = -1; int ntiles_with_data = 0; char *timestr; mapcache_image *base=NULL,*overlay; mapcache_image_format *format = NULL; #ifdef DEBUG if(req_tile->ntiles ==0) { ctx->set_error(ctx,500,"BUG: get_tile called with 0 tiles"); return NULL; } #endif expires = 0; response = mapcache_http_response_create(ctx->pool); mapcache_prefetch_tiles(ctx,req_tile->tiles,req_tile->ntiles); if(GC_HAS_ERROR(ctx)) return NULL; /* count how many tiles actually contain data */ for(i=0; i<req_tile->ntiles; i++) { /* add 1 if tile->nodata == 0 */ ntiles_with_data -= req_tile->tiles[i]->nodata - 1; } if(ntiles_with_data == 0) { ctx->set_error(ctx,404, "no tiles containing image data could be retrieved (not in cache, and read-only tileset or no source configured)"); return NULL; } /* this loop retrieves the tiles from the caches, and eventually decodes and merges them together * if multiple tiles were asked for */ for(i=0; i<req_tile->ntiles; i++) { mapcache_tile *tile = req_tile->tiles[i]; if(tile->nodata) continue; if(first == -1) { first = i; response->mtime = tile->mtime; expires = tile->expires; /* if we have multiple tiles to merge, decode the image data */ if(ntiles_with_data>1) { if(!tile->raw_image) { tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data); if(!tile->raw_image) return NULL; } base = tile->raw_image; } } else { if(response->mtime < tile->mtime) response->mtime = tile->mtime; if(tile->expires < expires) { expires = tile->expires; } if(!tile->raw_image) { tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data); if(!tile->raw_image) return NULL; } overlay = tile->raw_image; mapcache_image_merge(ctx, base, overlay); if(GC_HAS_ERROR(ctx)) { return NULL; } } } format = NULL; /* if we had more than one tile, we need to encode the raw image data into a mapcache_buffer */ if(ntiles_with_data > 1) { if(req_tile->format) { format = req_tile->format; } else { format = req_tile->tiles[first]->tileset->format; if(!format) { format = ctx->config->default_image_format; /* this one is always defined */ } } response->data = format->write(ctx, base, format); if(GC_HAS_ERROR(ctx)) { return NULL; } } else { response->data = req_tile->tiles[first]->encoded_data; format = req_tile->tiles[first]->tileset->format; } /* compute the content-type */ if(format && format->mime_type) { apr_table_set(response->headers,"Content-Type",format->mime_type); } else { mapcache_image_format_type t = mapcache_imageio_header_sniff(ctx,response->data); if(t == GC_PNG) apr_table_set(response->headers,"Content-Type","image/png"); else if(t == GC_JPEG) apr_table_set(response->headers,"Content-Type","image/jpeg"); } /* compute expiry headers */ if(expires) { apr_time_t now = apr_time_now(); apr_time_t additional = apr_time_from_sec(expires); apr_time_t texpires = now + additional; apr_table_set(response->headers, "Cache-Control",apr_psprintf(ctx->pool, "max-age=%d", expires)); timestr = apr_palloc(ctx->pool, APR_RFC822_DATE_LEN); apr_rfc822_date(timestr, texpires); apr_table_setn(response->headers, "Expires", timestr); } return response; }
/** * \brief push tile data to riak * * writes the content of mapcache_tile::data to the configured riak instance(s) * \private \memberof mapcache_cache_riak * \sa mapcache_cache::tile_set() */ static void _mapcache_cache_riak_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { char *key,*content_type; int error; int connect_error = RIACK_SUCCESS; int retries = 3; struct RIACK_OBJECT object; struct RIACK_CONTENT content; struct RIACK_PUT_PROPERTIES properties; struct RIACK_CLIENT *client; mapcache_pooled_connection *pc; mapcache_cache_riak *cache = (mapcache_cache_riak*)pcache; memset(&content, 0, sizeof(struct RIACK_CONTENT)); memset(&object, 0, sizeof(struct RIACK_OBJECT)); memset(&properties, 0, sizeof(struct RIACK_PUT_PROPERTIES)); //Use Buckets defaults instead of setting the read/write attributes /* properties.w_use = 1; properties.w = 1; properties.dw_use = 1; properties.dw = 0;*/ key = mapcache_util_get_tile_key(ctx, tile, NULL, " \r\n\t\f\e\a\b", "#"); GC_CHECK_ERROR(ctx); if (!tile->encoded_data) { tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format); GC_CHECK_ERROR(ctx); } content_type = tile->tileset->format?(tile->tileset->format->mime_type?tile->tileset->format->mime_type:NULL):NULL; if(!content_type) { /* compute the content-type */ mapcache_image_format_type t = mapcache_imageio_header_sniff(ctx,tile->encoded_data); if(t == GC_PNG) content_type = "image/png"; else if(t == GC_JPEG) content_type = "image/jpeg"; } pc = _riak_get_connection(ctx, cache, tile); GC_CHECK_ERROR(ctx); client = pc->connection; // Set up the riak object to put. Need to do this after we get the client connection object.bucket.value = cache->bucket.value; object.bucket.len = cache->bucket.len; object.key.value = key; object.key.len = strlen(key); object.vclock.len = 0; object.content_count = 1; object.content = &content; content.content_type.value = content_type; content.content_type.len = content_type?strlen(content_type):0; content.data = (uint8_t*)tile->encoded_data->buf; content.data_len = tile->encoded_data->size; // If we get an error it is advised that we call reconnect. It also appears // that every now and then we get an error and need to retry once again to // get it to work. do { error = riack_put(client, object, 0, &properties); if (error != RIACK_SUCCESS) { ctx->log(ctx, MAPCACHE_WARN, "Retry %d in riak_set for tile %s from cache %s due to eror %d", (4 - retries), key, cache->cache.name, error); for (connect_error = riack_reconnect(client); connect_error != RIACK_SUCCESS && retries > 0; connect_error = riack_reconnect(client)) { --retries; } --retries; } } while (error != RIACK_SUCCESS && retries >= 0); if (connect_error != RIACK_SUCCESS) mapcache_connection_pool_invalidate_connection(ctx,pc); else mapcache_connection_pool_release_connection(ctx,pc); if (error != RIACK_SUCCESS) { ctx->set_error(ctx, 500, "failed to store tile %s to cache %s due to error %d.", key, cache->cache.name, error); } }