static void _mapcache_cache_mbtiles_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*)pcache; struct sqlite_conn *conn; mapcache_pooled_connection *pc = mapcache_sqlite_get_conn(ctx,cache,tile,0); if (GC_HAS_ERROR(ctx)) { mapcache_sqlite_release_conn(ctx, pc); return; } conn = SQLITE_CONN(pc); if(!tile->raw_image) { tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data); if(GC_HAS_ERROR(ctx)) { mapcache_sqlite_release_conn(ctx, pc); return; } } sqlite3_exec(conn->handle, "BEGIN TRANSACTION", 0, 0, 0); _single_mbtile_set(ctx, cache, tile,conn); if (GC_HAS_ERROR(ctx)) { sqlite3_exec(conn->handle, "ROLLBACK TRANSACTION", 0, 0, 0); } else { sqlite3_exec(conn->handle, "END TRANSACTION", 0, 0, 0); } mapcache_sqlite_release_conn(ctx, pc); }
static void _mapcache_cache_mbtiles_multi_set(mapcache_context *ctx, mapcache_tile *tiles, int ntiles) { struct sqlite_conn *conn = NULL; int i; /* decode/encode image data before going into the sqlite write lock */ for (i = 0; i < ntiles; i++) { mapcache_tile *tile = &tiles[i]; if(!tile->raw_image) { tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data); GC_CHECK_ERROR(ctx); } /* only encode to image format if tile is not blank */ if (mapcache_image_blank_color(tile->raw_image) != MAPCACHE_TRUE && !tile->encoded_data) { tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format); GC_CHECK_ERROR(ctx); } } conn = _sqlite_get_conn(ctx, &tiles[0], 0); GC_CHECK_ERROR(ctx); sqlite3_exec(conn->handle, "BEGIN TRANSACTION", 0, 0, 0); for (i = 0; i < ntiles; i++) { mapcache_tile *tile = &tiles[i]; _single_mbtile_set(ctx,tile,conn); if(GC_HAS_ERROR(ctx)) break; } if (GC_HAS_ERROR(ctx)) { sqlite3_exec(conn->handle, "ROLLBACK TRANSACTION", 0, 0, 0); } else { sqlite3_exec(conn->handle, "END TRANSACTION", 0, 0, 0); } _sqlite_release_conn(ctx, &tiles[0], conn); }
/** * \brief push tile data to memcached * * writes the content of mapcache_tile::data to the configured memcached instance(s) * \returns MAPCACHE_FAILURE if there is no data to write, or if the tile isn't locked * \returns MAPCACHE_SUCCESS if the tile has been successfully written * \private \memberof mapcache_cache_memcache * \sa mapcache_cache::tile_set() */ static void _mapcache_cache_memcache_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { char *key; int rv; /* set no expiration if not configured */ int expires =0; mapcache_buffer *encoded_data = NULL; mapcache_cache_memcache *cache = (mapcache_cache_memcache*)pcache; mapcache_pooled_connection *pc; struct mapcache_memcache_pooled_connection *mpc; pc = _mapcache_memcache_get_conn(ctx,cache,tile); GC_CHECK_ERROR(ctx); mpc = pc->connection; key = mapcache_util_get_tile_key(ctx, tile,NULL," \r\n\t\f\e\a\b","#"); if(GC_HAS_ERROR(ctx)) goto cleanup; if(tile->tileset->auto_expire) expires = tile->tileset->auto_expire; if(cache->detect_blank) { if(!tile->raw_image) { tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data); GC_CHECK_ERROR(ctx); } if(mapcache_image_blank_color(tile->raw_image) != MAPCACHE_FALSE) { encoded_data = mapcache_buffer_create(5,ctx->pool); ((char*)encoded_data->buf)[0] = '#'; memcpy(((char*)encoded_data->buf)+1,tile->raw_image->data,4); encoded_data->size = 5; } } if(!encoded_data) { if(!tile->encoded_data) { tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format); if(GC_HAS_ERROR(ctx)) goto cleanup; } encoded_data = tile->encoded_data; } /* concatenate the current time to the end of the memcache data so we can extract it out * when we re-get the tile */ char *data = calloc(1,encoded_data->size+sizeof(apr_time_t)); apr_time_t now = apr_time_now(); apr_pool_cleanup_register(ctx->pool, data, (void*)free, apr_pool_cleanup_null); memcpy(data,encoded_data->buf,encoded_data->size); memcpy(&(data[encoded_data->size]),&now,sizeof(apr_time_t)); rv = apr_memcache_set(mpc->memcache,key,data,encoded_data->size+sizeof(apr_time_t),expires,0); if(rv != APR_SUCCESS) { ctx->set_error(ctx,500,"failed to store tile %d %d %d to memcache cache %s", tile->x,tile->y,tile->z,cache->cache.name); goto cleanup; } cleanup: _mapcache_memcache_release_conn(ctx,pc); }
//------------------------------------------------------------------------------ void _mapcache_source_tms_render_map_elevation(mapcache_context *ctx, mapcache_map *map) { mapcache_source_tms *tms; int elevationblock; int zoom, x, y; char* url; double dx, dy; tms = (mapcache_source_tms*)map->tileset->source; elevationblock = map->grid_link->grid->elevationblock; _GetTileCoords(map, &zoom, &x, &y, tms->flipy); url = apr_psprintf(ctx->pool,"%s/1.0.0/%s/%i/%i/%i.%s", tms->url,tms->layer,zoom,x,y,tms->format); tms->http->url = apr_pstrdup(ctx->pool,url); map->encoded_data = mapcache_buffer_create(30000,ctx->pool); mapcache_http_do_request(ctx, tms->http, map->encoded_data, NULL, NULL); GC_CHECK_ERROR(ctx); if(!mapcache_imageio_is_valid_format(ctx,map->encoded_data)) { char *returned_data = apr_pstrndup(ctx->pool,(char*)map->encoded_data->buf,map->encoded_data->size); ctx->set_error(ctx, 502, "tms request for tileset %s returned an unsupported format:\n%s", map->tileset->name, returned_data); return; } map->raw_image = mapcache_imageio_decode(ctx, map->encoded_data); map->raw_image->is_elevation = MC_ELEVATION_YES; GC_CHECK_ERROR(ctx); //map->raw_image->stride = 4 * elevationblock; dx = fabs(map->grid_link->grid->extent.maxx-map->grid_link->grid->extent.minx); dy = fabs(map->grid_link->grid->extent.maxx-map->grid_link->grid->extent.minx); map->raw_image->x0 = map->extent.minx / dx * 2.0; map->raw_image->y0 = map->extent.miny / dy * 2.0; map->raw_image->x1 = map->extent.maxx / dx * 2.0; map->raw_image->y1 = map->extent.maxy / dy * 2.0; if (map->raw_image->w != elevationblock || map->raw_image->h != elevationblock) { ctx->set_error(ctx,500,"Error: size of heightmap from source is not configured propery!"); } }
static void _mapcache_cache_mbtiles_set(mapcache_context *ctx, mapcache_tile *tile) { struct sqlite_conn *conn = _sqlite_get_conn(ctx, tile, 0); GC_CHECK_ERROR(ctx); if(!tile->raw_image) { tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data); if(GC_HAS_ERROR(ctx)) { _sqlite_release_conn(ctx, tile, conn); return; } } sqlite3_exec(conn->handle, "BEGIN TRANSACTION", 0, 0, 0); _single_mbtile_set(ctx,tile,conn); if (GC_HAS_ERROR(ctx)) { sqlite3_exec(conn->handle, "ROLLBACK TRANSACTION", 0, 0, 0); } else { sqlite3_exec(conn->handle, "END TRANSACTION", 0, 0, 0); } _sqlite_release_conn(ctx, tile, conn); }
static void _single_mbtile_set(mapcache_context *ctx, mapcache_tile *tile, struct sqlite_conn *conn) { sqlite3_stmt *stmt1,*stmt2; mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*)tile->tileset->cache; int ret; if(!tile->raw_image) { tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data); GC_CHECK_ERROR(ctx); } if(mapcache_image_blank_color(tile->raw_image) != MAPCACHE_FALSE) { stmt1 = conn->prepared_statements[MBTILES_SET_EMPTY_TILE_STMT1_IDX]; stmt2 = conn->prepared_statements[MBTILES_SET_EMPTY_TILE_STMT2_IDX]; if(!stmt1) { sqlite3_prepare(conn->handle, "insert or ignore into images(tile_id,tile_data) values (:color,:data);", -1, &conn->prepared_statements[MBTILES_SET_EMPTY_TILE_STMT1_IDX], NULL); sqlite3_prepare(conn->handle, "insert or replace into map(tile_column,tile_row,zoom_level,tile_id) values (:x,:y,:z,:color);", -1, &conn->prepared_statements[MBTILES_SET_EMPTY_TILE_STMT2_IDX], NULL); stmt1 = conn->prepared_statements[MBTILES_SET_EMPTY_TILE_STMT1_IDX]; stmt2 = conn->prepared_statements[MBTILES_SET_EMPTY_TILE_STMT2_IDX]; } cache->bind_stmt(ctx, stmt1, tile); cache->bind_stmt(ctx, stmt2, tile); } else { stmt1 = conn->prepared_statements[MBTILES_SET_TILE_STMT1_IDX]; stmt2 = conn->prepared_statements[MBTILES_SET_TILE_STMT2_IDX]; if(!stmt1) { sqlite3_prepare(conn->handle, "insert or replace into images(tile_id,tile_data) values (:key,:data);", -1, &conn->prepared_statements[MBTILES_SET_TILE_STMT1_IDX], NULL); sqlite3_prepare(conn->handle, "insert or replace into map(tile_column,tile_row,zoom_level,tile_id) values (:x,:y,:z,:key);", -1, &conn->prepared_statements[MBTILES_SET_TILE_STMT2_IDX], NULL); stmt1 = conn->prepared_statements[MBTILES_SET_TILE_STMT1_IDX]; stmt2 = conn->prepared_statements[MBTILES_SET_TILE_STMT2_IDX]; } cache->bind_stmt(ctx, stmt1, tile); cache->bind_stmt(ctx, stmt2, tile); } do { ret = sqlite3_step(stmt1); if (ret != SQLITE_DONE && ret != SQLITE_ROW && ret != SQLITE_BUSY && ret != SQLITE_LOCKED) { ctx->set_error(ctx, 500, "mbtiles backend failed on image set: %s (%d)", sqlite3_errmsg(conn->handle), ret); break; } if (ret == SQLITE_BUSY) { sqlite3_reset(stmt1); } } while (ret == SQLITE_BUSY || ret == SQLITE_LOCKED); if(ret == SQLITE_DONE) { do { ret = sqlite3_step(stmt2); if (ret != SQLITE_DONE && ret != SQLITE_ROW && ret != SQLITE_BUSY && ret != SQLITE_LOCKED) { ctx->set_error(ctx, 500, "mbtiles backend failed on map set: %s (%d)", sqlite3_errmsg(conn->handle), ret); break; } if (ret == SQLITE_BUSY) { sqlite3_reset(stmt2); } } while (ret == SQLITE_BUSY || ret == SQLITE_LOCKED); } sqlite3_reset(stmt1); sqlite3_reset(stmt2); }
/** * \brief apply appropriate tile properties to the sqlite statement */ static void _bind_sqlite_params(mapcache_context *ctx, void *vstmt, mapcache_tile *tile) { sqlite3_stmt *stmt = vstmt; int paramidx; /* tile->x */ paramidx = sqlite3_bind_parameter_index(stmt, ":x"); if (paramidx) sqlite3_bind_int(stmt, paramidx, tile->x); /* tile->y */ paramidx = sqlite3_bind_parameter_index(stmt, ":y"); if (paramidx) sqlite3_bind_int(stmt, paramidx, tile->y); /* tile->y */ paramidx = sqlite3_bind_parameter_index(stmt, ":z"); if (paramidx) sqlite3_bind_int(stmt, paramidx, tile->z); /* eventual dimensions */ paramidx = sqlite3_bind_parameter_index(stmt, ":dim"); if (paramidx) { if (tile->dimensions) { char *dim = mapcache_util_get_tile_dimkey(ctx, tile, NULL, NULL); sqlite3_bind_text(stmt, paramidx, dim, -1, SQLITE_STATIC); } else { sqlite3_bind_text(stmt, paramidx, "", -1, SQLITE_STATIC); } } /* grid */ paramidx = sqlite3_bind_parameter_index(stmt, ":grid"); if (paramidx) sqlite3_bind_text(stmt, paramidx, tile->grid_link->grid->name, -1, SQLITE_STATIC); /* tileset */ paramidx = sqlite3_bind_parameter_index(stmt, ":tileset"); if (paramidx) sqlite3_bind_text(stmt, paramidx, tile->tileset->name, -1, SQLITE_STATIC); /* tile blob data */ paramidx = sqlite3_bind_parameter_index(stmt, ":data"); if (paramidx) { int written = 0; if(((mapcache_cache_sqlite*)tile->tileset->cache)->detect_blank && tile->grid_link->grid->tile_sx == 256 && tile->grid_link->grid->tile_sy == 256) { if(!tile->raw_image) { tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data); GC_CHECK_ERROR(ctx); } if(mapcache_image_blank_color(tile->raw_image) != MAPCACHE_FALSE) { char *buf = apr_palloc(ctx->pool, 5* sizeof(char)); buf[0] = '#'; memcpy(buf+1,tile->raw_image->data,4); written = 1; sqlite3_bind_blob(stmt, paramidx, buf, 5, SQLITE_STATIC); } } if(!written) { if (!tile->encoded_data) { tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format); GC_CHECK_ERROR(ctx); } if (tile->encoded_data && tile->encoded_data->size) { sqlite3_bind_blob(stmt, paramidx, tile->encoded_data->buf, tile->encoded_data->size, SQLITE_STATIC); } else { sqlite3_bind_text(stmt, paramidx, "", -1, SQLITE_STATIC); } } } }
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 write tile data to disk * * writes the content of mapcache_tile::data to disk. * \returns MAPCACHE_FAILURE if there is no data to write, or if the tile isn't locked * \returns MAPCACHE_SUCCESS if the tile has been successfully written to disk * \private \memberof mapcache_cache_disk * \sa mapcache_cache::tile_set() */ static void _mapcache_cache_disk_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { apr_size_t bytes; apr_file_t *f; apr_status_t ret; char errmsg[120]; char *filename, *hackptr1, *hackptr2=NULL; mapcache_cache_disk *cache = (mapcache_cache_disk*)pcache; const int creation_retry = cache->creation_retry; int retry_count_create_file = 0; #ifdef DEBUG /* all this should be checked at a higher level */ if(!tile->encoded_data && !tile->raw_image) { ctx->set_error(ctx,500,"attempting to write empty tile to disk"); return; } if(!tile->encoded_data && !tile->tileset->format) { ctx->set_error(ctx,500,"received a raw tile image for a tileset with no format"); return; } #endif cache->tile_key(ctx, cache, tile, &filename); GC_CHECK_ERROR(ctx); /* find the location of the last '/' in the string */ hackptr1 = filename; while(*hackptr1) { if(*hackptr1 == '/') hackptr2 = hackptr1; hackptr1++; } *hackptr2 = '\0'; if(APR_SUCCESS != (ret = apr_dir_make_recursive(filename,APR_OS_DEFAULT,ctx->pool))) { /* * apr_dir_make_recursive sometimes sends back this error, although it should not. * ignore this one */ if(!APR_STATUS_IS_EEXIST(ret)) { ctx->set_error(ctx, 500, "failed to create directory %s: %s",filename, apr_strerror(ret,errmsg,120)); return; } } *hackptr2 = '/'; ret = apr_file_remove(filename,ctx->pool); if(ret != APR_SUCCESS && !APR_STATUS_IS_ENOENT(ret)) { ctx->set_error(ctx, 500, "failed to remove file %s: %s",filename, apr_strerror(ret,errmsg,120)); } #ifdef HAVE_SYMLINK if(cache->symlink_blank) { if(!tile->raw_image) { tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data); GC_CHECK_ERROR(ctx); } if(mapcache_image_blank_color(tile->raw_image) != MAPCACHE_FALSE) { char *blankname; int retry_count_create_symlink = 0; char *blankname_rel = NULL; _mapcache_cache_disk_blank_tile_key(ctx,cache,tile,tile->raw_image->data,&blankname); if(apr_file_open(&f, blankname, APR_FOPEN_READ, APR_OS_DEFAULT, ctx->pool) != APR_SUCCESS) { int isLocked; void *lock; char *blankdirname; if(!tile->encoded_data) { tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format); GC_CHECK_ERROR(ctx); } /* create the blank file */ blankdirname = apr_psprintf(ctx->pool, "%s/%s/%s/blanks", cache->base_directory, tile->tileset->name, tile->grid_link->grid->name); if(APR_SUCCESS != (ret = apr_dir_make_recursive( blankdirname, APR_OS_DEFAULT,ctx->pool))) { if(!APR_STATUS_IS_EEXIST(ret)) { ctx->set_error(ctx, 500, "failed to create directory %s for blank tiles",blankdirname, apr_strerror(ret,errmsg,120)); return; } } /* aquire a lock on the blank file */ isLocked = mapcache_lock_or_wait_for_resource(ctx,ctx->config->locker,blankname, &lock); if(isLocked == MAPCACHE_TRUE) { if((ret = apr_file_open(&f, blankname, APR_FOPEN_CREATE|APR_FOPEN_WRITE|APR_FOPEN_BUFFERED|APR_FOPEN_BINARY, APR_OS_DEFAULT, ctx->pool)) != APR_SUCCESS) { ctx->set_error(ctx, 500, "failed to create file %s: %s",blankname, apr_strerror(ret,errmsg,120)); mapcache_unlock_resource(ctx,ctx->config->locker,blankname, lock); return; /* we could not create the file */ } bytes = (apr_size_t)tile->encoded_data->size; ret = apr_file_write(f,(void*)tile->encoded_data->buf,&bytes); if(ret != APR_SUCCESS) { ctx->set_error(ctx, 500, "failed to write data to file %s (wrote %d of %d bytes): %s",blankname, (int)bytes, (int)tile->encoded_data->size, apr_strerror(ret,errmsg,120)); mapcache_unlock_resource(ctx,ctx->config->locker,blankname, lock); return; /* we could not create the file */ } if(bytes != tile->encoded_data->size) { ctx->set_error(ctx, 500, "failed to write image data to %s, wrote %d of %d bytes", blankname, (int)bytes, (int)tile->encoded_data->size); mapcache_unlock_resource(ctx,ctx->config->locker,blankname, lock); return; } apr_file_close(f); mapcache_unlock_resource(ctx,ctx->config->locker,blankname, lock); #ifdef DEBUG ctx->log(ctx,MAPCACHE_DEBUG,"created blank tile %s",blankname); #endif } } else { apr_file_close(f); } /* * compute the relative path between tile and blank tile */ blankname_rel = relative_path(ctx,filename, blankname); GC_CHECK_ERROR(ctx); /* * depending on configuration symlink creation will retry if it fails. * this can happen on nfs mounted network storage. * the solution is to create the containing directory again and retry the symlink creation. */ while(symlink(blankname_rel, filename) != 0) { retry_count_create_symlink++; if(retry_count_create_symlink > creation_retry) { char *error = strerror(errno); ctx->set_error(ctx, 500, "failed to link tile %s to %s: %s",filename, blankname_rel, error); return; /* we could not create the file */ } *hackptr2 = '\0'; if(APR_SUCCESS != (ret = apr_dir_make_recursive(filename,APR_OS_DEFAULT,ctx->pool))) { if(!APR_STATUS_IS_EEXIST(ret)) { ctx->set_error(ctx, 500, "failed to create symlink, can not create directory %s: %s",filename, apr_strerror(ret,errmsg,120)); return; /* we could not create the file */ } } *hackptr2 = '/'; } #ifdef DEBUG ctx->log(ctx, MAPCACHE_DEBUG, "linked blank tile %s to %s",filename,blankname); #endif return; } } #endif /*HAVE_SYMLINK*/ /* go the normal way: either we haven't configured blank tile detection, or the tile was not blank */ if(!tile->encoded_data) { tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format); GC_CHECK_ERROR(ctx); } /* * depending on configuration file creation will retry if it fails. * this can happen on nfs mounted network storage. * the solution is to create the containing directory again and retry the file creation. */ while((ret = apr_file_open(&f, filename, APR_FOPEN_CREATE|APR_FOPEN_WRITE|APR_FOPEN_BUFFERED|APR_FOPEN_BINARY, APR_OS_DEFAULT, ctx->pool)) != APR_SUCCESS) { retry_count_create_file++; if(retry_count_create_file > creation_retry) { ctx->set_error(ctx, 500, "failed to create file %s: %s",filename, apr_strerror(ret,errmsg,120)); return; /* we could not create the file */ } *hackptr2 = '\0'; if(APR_SUCCESS != (ret = apr_dir_make_recursive(filename,APR_OS_DEFAULT,ctx->pool))) { if(!APR_STATUS_IS_EEXIST(ret)) { ctx->set_error(ctx, 500, "failed to create file, can not create directory %s: %s",filename, apr_strerror(ret,errmsg,120)); return; /* we could not create the file */ } } *hackptr2 = '/'; } bytes = (apr_size_t)tile->encoded_data->size; ret = apr_file_write(f,(void*)tile->encoded_data->buf,&bytes); if(ret != APR_SUCCESS) { ctx->set_error(ctx, 500, "failed to write data to file %s (wrote %d of %d bytes): %s",filename, (int)bytes, (int)tile->encoded_data->size, apr_strerror(ret,errmsg,120)); return; /* we could not create the file */ } if(bytes != tile->encoded_data->size) { ctx->set_error(ctx, 500, "failed to write image data to %s, wrote %d of %d bytes", filename, (int)bytes, (int)tile->encoded_data->size); } ret = apr_file_close(f); if(ret != APR_SUCCESS) { ctx->set_error(ctx, 500, "failed to close file %s:%s",filename, apr_strerror(ret,errmsg,120)); return; /* we could not create the file */ } }