Skip to content
Commits on Source (6)
......@@ -12,7 +12,7 @@ endif ()
set (MAPCACHE_VERSION_MAJOR 1)
set (MAPCACHE_VERSION_MINOR 8)
set (MAPCACHE_VERSION_MINOR 10)
set (MAPCACHE_VERSION_REVISION 0)
if(NOT DEFINED CMAKE_INSTALL_LIBDIR)
......@@ -86,7 +86,7 @@ endif()
#options suported by the cmake builder
option(WITH_PIXMAN "Use pixman for SSE optimized image manipulations" ON)
option(WITH_SQLITE "Use sqlite as a cache/dimension backend" ON)
option(WITH_POSTGRESQL "Use sqlite as a dimension backend" OFF)
option(WITH_POSTGRESQL "Use PostgreSQL as a dimension backend" OFF)
option(WITH_BERKELEY_DB "Use Berkeley DB as a cache backend" OFF)
option(WITH_MEMCACHE "Use memcache as a cache backend (requires recent apr-util)" OFF)
option(WITH_TIFF "Use TIFFs as a cache backend" OFF)
......
Migrating from Mapcache 1.8 to 1.10
===================================
* No backward compatibility issue is expected.
See [MapCache 1.10 Changelog](https://mapserver.org/development/changelog/mapcache/changelog-1-10.html)
for a list of bug fixes and new features.
Migrating from Mapcache 1.6 to 1.8
===================================
......
......@@ -312,7 +312,7 @@ static void mod_mapcache_child_init(apr_pool_t *pool, server_rec *s)
int i,rv;
for(i=0;i<cfg->aliases->nelts;i++) {
mapcache_alias_entry *alias_entry = APR_ARRAY_IDX(cfg->aliases,i,mapcache_alias_entry*);
rv = mapcache_connection_pool_create(&(alias_entry->cp),pool);
rv = mapcache_connection_pool_create(alias_entry->cfg, &(alias_entry->cp),pool);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "creating a child process mapcache connection pool on server %s for alias %s", s->server_hostname, alias_entry->endpoint);
if(rv!=APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, "failed to create mapcache connection pool");
......@@ -320,7 +320,7 @@ static void mod_mapcache_child_init(apr_pool_t *pool, server_rec *s)
}
for(i=0;i<cfg->quickaliases->nelts;i++) {
mapcache_alias_entry *alias_entry = APR_ARRAY_IDX(cfg->quickaliases,i,mapcache_alias_entry*);
rv = mapcache_connection_pool_create(&(alias_entry->cp),pool);
rv = mapcache_connection_pool_create(alias_entry->cfg, &(alias_entry->cp),pool);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "creating a child process mapcache connection pool on server %s for alias %s", s->server_hostname, alias_entry->endpoint);
if(rv!=APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, "failed to create mapcache connection pool");
......@@ -697,6 +697,10 @@ static const command_rec mod_mapcache_cmds[] = {
{ NULL }
} ;
#ifdef APLOG_USE_MODULE
APLOG_USE_MODULE(mapcache);
#endif
module AP_MODULE_DECLARE_DATA mapcache_module = {
STANDARD20_MODULE_STUFF,
NULL,
......
......@@ -210,7 +210,7 @@ static void load_config(mapcache_context *ctx, char *filename)
apr_pool_destroy(config_pool);
}
config_pool = tmp_config_pool;
mapcache_connection_pool_create(&ctx->connection_pool, config_pool);
mapcache_connection_pool_create(cfg, &ctx->connection_pool, config_pool);
return;
......
......@@ -19,9 +19,8 @@ if(NOT DEFINED APACHE_MODULE_DIR)
)
if(APXS_BIN)
EXEC_PROGRAM(${APXS_BIN}
ARGS -q LIBEXECDIR
OUTPUT_VARIABLE APACHE_MODULE_DIR )
EXECUTE_PROCESS(COMMAND ${APXS_BIN} -q LIBEXECDIR
OUTPUT_VARIABLE APACHE_MODULE_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)
endif(APXS_BIN)
endif(NOT DEFINED APACHE_MODULE_DIR)
......
......@@ -398,7 +398,7 @@ char * str_replace_all(apr_pool_t *pool, const char *string,
char * dbfilename(apr_pool_t * pool, char * template,
mapcache_tileset * tileset, mapcache_grid * grid,
apr_array_header_t * dimensions, apr_hash_t * fmt, int z,
int dbx, int dby, int xcount, int ycount)
int dbx, int dby, int xcount, int ycount, int top)
{
int tilx = dbx * xcount;
int tily = dby * ycount;
......@@ -469,6 +469,26 @@ char * dbfilename(apr_pool_t * pool, char * template,
apr_psprintf(pool, curfmt, dby));
}
// Z top
if (top > 0) {
char * curfmt;
curfmt = apr_hash_get(fmt, "top", APR_HASH_KEY_STRING);
path = str_replace_all(pool, path, "{top}",
apr_psprintf(pool, curfmt, top));
curfmt = apr_hash_get(fmt, "top_x", APR_HASH_KEY_STRING);
path = str_replace_all(pool, path, "{top_x}",
apr_psprintf(pool, curfmt, dbx));
curfmt = apr_hash_get(fmt, "inv_top_x", APR_HASH_KEY_STRING);
path = str_replace_all(pool, path, "{inv_top_x}",
apr_psprintf(pool, curfmt, dbx));
curfmt = apr_hash_get(fmt, "top_y", APR_HASH_KEY_STRING);
path = str_replace_all(pool, path, "{top_y}",
apr_psprintf(pool, curfmt, dby));
curfmt = apr_hash_get(fmt, "inv_top_y", APR_HASH_KEY_STRING);
path = str_replace_all(pool, path, "{inv_top_y}",
apr_psprintf(pool, curfmt, dby));
}
return path;
}
......@@ -659,7 +679,7 @@ int main(int argc, char * argv[])
int minzoom, maxzoom;
char * dbfile;
apr_hash_t * formats;
int xcount, ycount;
int xcount, ycount, top;
} *cache;
apr_array_header_t * caches = NULL;
int i, ix, iy, iz;
......@@ -694,7 +714,7 @@ int main(int argc, char * argv[])
mapcache_context_init(&ctx);
ctx.config = mapcache_configuration_create(ctx.pool);
ctx.log = mapcache_log;
mapcache_connection_pool_create(&ctx.connection_pool, ctx.pool);
mapcache_connection_pool_create(ctx.config, &ctx.connection_pool, ctx.pool);
/////////////////////////////////////////////////////////////////////////////
......@@ -1067,6 +1087,11 @@ int main(int argc, char * argv[])
apr_hash_set(c->formats, "div_y", APR_HASH_KEY_STRING, "(not set)");
apr_hash_set(c->formats, "inv_div_x", APR_HASH_KEY_STRING, "(not set)");
apr_hash_set(c->formats, "inv_div_y", APR_HASH_KEY_STRING, "(not set)");
apr_hash_set(c->formats, "top", APR_HASH_KEY_STRING, "(not set)");
apr_hash_set(c->formats, "top_x", APR_HASH_KEY_STRING, "(not set)");
apr_hash_set(c->formats, "top_y", APR_HASH_KEY_STRING, "(not set)");
apr_hash_set(c->formats, "inv_top_x", APR_HASH_KEY_STRING, "(not set)");
apr_hash_set(c->formats, "inv_top_y", APR_HASH_KEY_STRING, "(not set)");
for (hi = apr_hash_first(ctx.pool, c->formats)
; hi
; hi = apr_hash_next(hi))
......@@ -1089,6 +1114,13 @@ int main(int argc, char * argv[])
text = NULL;
if (node) text = node->txt;
if (text) c->ycount = (int)strtol(text, NULL, 10);
// Read top
c->top = -1;
node = ezxml_child(c->node, "top");
text = NULL;
if (node) text = node->txt;
if (text) c->top = (int)strtol(text, NULL, 10);
}
......@@ -1237,6 +1269,7 @@ int main(int argc, char * argv[])
mapcache_extent_i db_region_bbox;
int dbx_has_inv = FALSE;
int dby_has_inv = FALSE;
int file_zoom_level;
// Select cache according to zoom level
for ( cid=0 ; cid < caches->nelts ; cid++ ) {
......@@ -1260,16 +1293,45 @@ int main(int argc, char * argv[])
// Compute region bounding box expressed in tiles and in DB files for the
// current zoom level
mapcache_grid_get_xy(&ctx, grid, region_bbox.minx, region_bbox.miny, iz,
&(til_region_bbox.minx), &(til_region_bbox.miny));
mapcache_grid_get_xy(&ctx, grid, region_bbox.maxx, region_bbox.maxy, iz,
&(til_region_bbox.maxx), &(til_region_bbox.maxy));
file_zoom_level = iz;
if (cache->top > 0) file_zoom_level = cache->top;
mapcache_grid_get_xy(&ctx, grid, region_bbox.minx, region_bbox.miny,
file_zoom_level, &(til_region_bbox.minx), &(til_region_bbox.miny));
mapcache_grid_get_xy(&ctx, grid, region_bbox.maxx, region_bbox.maxy,
file_zoom_level, &(til_region_bbox.maxx), &(til_region_bbox.maxy));
if (til_region_bbox.minx > til_region_bbox.maxx) {
int swap = til_region_bbox.maxx;
til_region_bbox.maxx = til_region_bbox.minx;
til_region_bbox.minx = swap;
}
if (til_region_bbox.miny > til_region_bbox.maxy) {
int swap = til_region_bbox.maxy;
til_region_bbox.maxy = til_region_bbox.miny;
til_region_bbox.miny = swap;
}
dbx_has_inv = strstr(cache->dbfile,"{inv_x}")
|| strstr(cache->dbfile,"{inv_div_x}");
|| strstr(cache->dbfile,"{inv_div_x}")
|| strstr(cache->dbfile,"{inv_top_x}");
dby_has_inv = strstr(cache->dbfile,"{inv_y}")
|| strstr(cache->dbfile,"{inv_div_y}");
if ((cache->xcount > 0) && (cache->ycount > 0)) {
|| strstr(cache->dbfile,"{inv_div_y}")
|| strstr(cache->dbfile,"{inv_top_y}");
if (cache->top > 0) {
if (dbx_has_inv) {
db_region_bbox.minx = grid->levels[cache->top]->maxx-1 - til_region_bbox.maxx;
db_region_bbox.maxx = grid->levels[cache->top]->maxx-1 - til_region_bbox.minx;
} else {
db_region_bbox.minx = til_region_bbox.minx;
db_region_bbox.maxx = til_region_bbox.maxx;
}
if (dby_has_inv) {
db_region_bbox.miny = grid->levels[cache->top]->maxy-1 - til_region_bbox.maxy;
db_region_bbox.maxy = grid->levels[cache->top]->maxy-1 - til_region_bbox.miny;
} else {
db_region_bbox.miny = til_region_bbox.miny;
db_region_bbox.maxy = til_region_bbox.maxy;
}
} else if ((cache->xcount > 0) && (cache->ycount > 0)) {
if (dbx_has_inv) {
int inv_minx = grid->levels[iz]->maxx - til_region_bbox.minx;
int inv_maxx = grid->levels[iz]->maxx - til_region_bbox.maxx;
......@@ -1340,7 +1402,7 @@ int main(int argc, char * argv[])
// Retrieve DB file name and check for its existence (read access)
file_name = dbfilename(ctx.pool, cache->dbfile, tileset, grid,
dimensions, cache->formats, iz, ix, iy, cache->xcount,
cache->ycount);
cache->ycount, cache->top);
// Unless this has already been done on this file,
// Retrieve file size and count cached tiles regardless the region of
......@@ -1379,7 +1441,22 @@ int main(int argc, char * argv[])
}
// Compute file bounding box expressed in tiles
if ((cache->xcount > 0) && (cache->ycount > 0)) {
if (cache->top > 0) {
if (dbx_has_inv) {
til_file_bbox.minx = grid->levels[cache->top]->maxx-1 - ix;
til_file_bbox.maxx = til_file_bbox.minx;
} else {
til_file_bbox.minx = ix;
til_file_bbox.maxx = til_file_bbox.minx;
}
if (dby_has_inv) {
til_file_bbox.miny = grid->levels[cache->top]->maxy-1 - iy;
til_file_bbox.maxy = til_file_bbox.miny;
} else {
til_file_bbox.miny = iy;
til_file_bbox.maxy = til_file_bbox.miny;
}
} else if ((cache->xcount > 0) && (cache->ycount > 0)) {
if (dbx_has_inv) {
til_file_bbox.maxx = grid->levels[iz]->maxx-1 - ix * cache->xcount;
til_file_bbox.minx = til_file_bbox.maxx + cache->xcount + 1;
......@@ -1412,15 +1489,25 @@ int main(int argc, char * argv[])
// Compute file bounding box expressed in grid units for the current
// zoom level
mapcache_grid_get_tile_extent(&ctx, grid, til_file_bbox.minx,
til_file_bbox.miny, iz, &temp_bbox);
til_file_bbox.miny, file_zoom_level, &temp_bbox);
if (GC_HAS_ERROR(&ctx)) goto failure;
file_bbox.minx = temp_bbox.minx;
file_bbox.miny = temp_bbox.miny;
mapcache_grid_get_tile_extent(&ctx, grid, til_file_bbox.maxx,
til_file_bbox.maxy, iz, &temp_bbox);
til_file_bbox.maxy, file_zoom_level, &temp_bbox);
if (GC_HAS_ERROR(&ctx)) goto failure;
file_bbox.maxx = temp_bbox.maxx;
file_bbox.maxy = temp_bbox.maxy;
if (file_bbox.minx > file_bbox.maxx) {
int swap = file_bbox.maxx;
file_bbox.maxx = file_bbox.minx;
file_bbox.minx = swap;
}
if (file_bbox.miny > file_bbox.maxy) {
int swap = file_bbox.maxy;
file_bbox.maxy = file_bbox.miny;
file_bbox.miny = swap;
}
// Compute part of region of interest within file bounding box
#ifdef USE_CLIPPERS
......@@ -1487,6 +1574,16 @@ int main(int argc, char * argv[])
mapcache_grid_get_xy(&ctx, grid, region_in_file_bbox.maxx-res,
region_in_file_bbox.maxy-res, iz, &(til_region_in_file_bbox.maxx),
&(til_region_in_file_bbox.maxy));
if (til_region_in_file_bbox.minx > til_region_in_file_bbox.maxx) {
int swap = til_region_in_file_bbox.maxx;
til_region_in_file_bbox.maxx = til_region_in_file_bbox.minx;
til_region_in_file_bbox.minx = swap;
}
if (til_region_in_file_bbox.miny > til_region_in_file_bbox.maxy) {
int swap = til_region_in_file_bbox.maxy;
til_region_in_file_bbox.maxy = til_region_in_file_bbox.miny;
til_region_in_file_bbox.miny = swap;
}
if ((cache->xcount > 0) && (cache->ycount > 0)) {
if (til_region_in_file_bbox.maxx>(til_file_bbox.minx+cache->xcount-1))
{
......
mapcache (1.8.0-2) UNRELEASED; urgency=medium
mapcache (1.10.0-1) unstable; urgency=medium
* New upstream release.
* Bump Standards-Version to 4.5.0, no changes.
* Drop Name field from upstream metadata.
* Drop unused override for file-references-package-build-path.
* Update symbols for 1.10.0.
-- Bas Couwenberg <sebastic@debian.org> Mon, 30 Sep 2019 19:23:12 +0200
-- Bas Couwenberg <sebastic@debian.org> Mon, 24 Feb 2020 16:02:26 +0100
mapcache (1.8.0-1) unstable; urgency=medium
......
# False positive, dependency set by dh_apache2
apache2-module-depends-on-real-apache2-package apache2-bin
# Cannot easily be fixed
file-references-package-build-path *
# SymbolsHelper-Confirmed: 1.8.0 amd64
# SymbolsHelper-Confirmed: 1.10.0 amd64
libmapcache.so.1 #PACKAGE# #MINVER#
* Build-Depends-Package: libmapcache1-dev
EZXML_NIL@Base 1.0.0
......@@ -228,12 +228,14 @@ libmapcache.so.1 #PACKAGE# #MINVER#
mapcache_configuration_add_cache@Base 1.0.0
mapcache_configuration_add_grid@Base 1.0.0
mapcache_configuration_add_image_format@Base 1.0.0
mapcache_configuration_add_ruleset@Base 1.10.0
mapcache_configuration_add_source@Base 1.0.0
mapcache_configuration_add_tileset@Base 1.0.0
mapcache_configuration_create@Base 1.0.0
mapcache_configuration_get_cache@Base 1.0.0
mapcache_configuration_get_grid@Base 1.0.0
mapcache_configuration_get_image_format@Base 1.0.0
mapcache_configuration_get_ruleset@Base 1.10.0
mapcache_configuration_get_source@Base 1.0.0
mapcache_configuration_get_tileset@Base 1.0.0
mapcache_configuration_parse@Base 1.0.0
......@@ -262,6 +264,7 @@ libmapcache.so.1 #PACKAGE# #MINVER#
mapcache_empty_png_decode@Base 1.2.0
mapcache_error_image@Base 1.0.0
mapcache_grid_compute_limits@Base 1.0.0
mapcache_grid_compute_limits_at_level@Base 1.10.0
mapcache_grid_create@Base 1.0.0
mapcache_grid_get_cell@Base 1.0.0
mapcache_grid_get_closest_wms_level@Base 1.4.0
......@@ -291,6 +294,7 @@ libmapcache.so.1 #PACKAGE# #MINVER#
mapcache_image_has_alpha@Base 1.0.0
mapcache_image_merge@Base 1.0.0
mapcache_image_metatile_split@Base 1.0.0
mapcache_imageio_alpha_sniff@Base 1.10.0
mapcache_imageio_create_jpeg_format@Base 1.0.0
mapcache_imageio_create_mixed_format@Base 1.0.0
mapcache_imageio_create_png_format@Base 1.0.0
......@@ -329,6 +333,12 @@ libmapcache.so.1 #PACKAGE# #MINVER#
mapcache_requested_dimensions_clone@Base 1.6.0
mapcache_rest_connection_constructor@Base 1.6.0
mapcache_rest_connection_destructor@Base 1.6.0
mapcache_ruleset_create@Base 1.10.0
mapcache_ruleset_is_visible_tile@Base 1.10.0
mapcache_ruleset_rule_clone@Base 1.10.0
mapcache_ruleset_rule_create@Base 1.10.0
mapcache_ruleset_rule_find@Base 1.10.0
mapcache_ruleset_rule_get@Base 1.10.0
mapcache_service_demo_create@Base 1.0.0
mapcache_service_dispatch_request@Base 1.0.0
mapcache_service_gmaps_create@Base 1.0.0
......@@ -400,6 +410,7 @@ libmapcache.so.1 #PACKAGE# #MINVER#
parseFormat@Base 1.0.0
parseGrid@Base 1.0.0
parseMetadata@Base 1.0.0
parseRuleset@Base 1.10.0
parseServices@Base 1.0.0
parseSource@Base 1.0.0
parseTileset@Base 1.0.0
......
......@@ -105,6 +105,8 @@ typedef struct mapcache_image mapcache_image;
typedef struct mapcache_grid mapcache_grid;
typedef struct mapcache_grid_level mapcache_grid_level;
typedef struct mapcache_grid_link mapcache_grid_link;
typedef struct mapcache_rule mapcache_rule;
typedef struct mapcache_ruleset mapcache_ruleset;
typedef struct mapcache_context mapcache_context;
typedef struct mapcache_dimension mapcache_dimension;
typedef struct mapcache_requested_dimension mapcache_requested_dimension;
......@@ -839,6 +841,11 @@ struct mapcache_cfg {
*/
apr_hash_t *grids;
/**
* hashtable containing (pre)defined rulesets
*/
apr_hash_t *rulesets;
/**
* the format to use for some miscelaneaous operations:
* - creating an empty image
......@@ -875,6 +882,12 @@ struct mapcache_cfg {
/* return 404 on potentially blocking operations (proxying, source getmaps,
locks on metatile waiting, ... Used for nginx module */
int non_blocking;
// Parameters for connection_pool:
// - cp_hmax defines the maximum number of open connections at the same time
// - cp_ttl defines the maximum amount of time in microseconds an unused connection is valid
int cp_hmax;
int cp_ttl;
};
/**
......@@ -893,9 +906,11 @@ MS_DLL_EXPORT mapcache_cache* mapcache_configuration_get_cache(mapcache_cfg *con
mapcache_grid *mapcache_configuration_get_grid(mapcache_cfg *config, const char *key);
MS_DLL_EXPORT mapcache_tileset* mapcache_configuration_get_tileset(mapcache_cfg *config, const char *key);
mapcache_image_format *mapcache_configuration_get_image_format(mapcache_cfg *config, const char *key);
mapcache_ruleset *mapcache_configuration_get_ruleset(mapcache_cfg *config, const char *key);
void mapcache_configuration_add_image_format(mapcache_cfg *config, mapcache_image_format *format, const char * key);
void mapcache_configuration_add_source(mapcache_cfg *config, mapcache_source *source, const char * key);
void mapcache_configuration_add_grid(mapcache_cfg *config, mapcache_grid *grid, const char * key);
void mapcache_configuration_add_ruleset(mapcache_cfg *config, mapcache_ruleset *ruleset, const char * key);
void mapcache_configuration_add_tileset(mapcache_cfg *config, mapcache_tileset *tileset, const char * key);
void mapcache_configuration_add_cache(mapcache_cfg *config, mapcache_cache *cache, const char * key);
......@@ -1057,6 +1072,12 @@ struct mapcache_grid_link {
mapcache_extent_i *grid_limits;
int minz,maxz;
/**
* rules (mapcache_rule) for each zoom level
* index in array = zoom level
*/
apr_array_header_t *rules;
/**
* tiles above this zoom level will not be stored to the cache, but will be
* dynamically generated (either by reconstructing from lower level tiles, or
......@@ -1069,6 +1090,46 @@ struct mapcache_grid_link {
apr_array_header_t *intermediate_grids;
};
/**\class mapcache_rule
* \brief a zoom level rule
*/
struct mapcache_rule {
/**
* rule for zoom level
*/
int zoom_level;
/**
* color of tiles when outside visible extent, ARGB
*/
unsigned int hidden_color;
/**
* tile to return when outside visible extent
*/
mapcache_buffer *hidden_tile;
/**
* visible extents, array of mapcache_extent
*/
apr_array_header_t *visible_extents;
/**
* visible limits, array of mapcache_extent_i
*/
apr_array_header_t *visible_limits;
};
/**\class mapcache_ruleset
* \brief a set of rules
*/
struct mapcache_ruleset {
/**
* the name of this ruleset
*/
char *name;
/**
* rules (mapcache_rule)
*/
apr_array_header_t *rules;
};
/**\class mapcache_tileset
* \brief a set of tiles that can be requested by a client, created from a mapcache_source
* stored by a mapcache_cache in a mapcache_format
......@@ -1142,6 +1203,12 @@ struct mapcache_tileset {
mapcache_dimension_assembly_type dimension_assembly_type;
/**
* Maximum zoom level for activating multithreaded subtile retrieval
* -1 means 'not activated'
*/
int assembly_threaded_fetching_maxzoom;
/**
* image to be used as a watermark
*/
......@@ -1163,7 +1230,8 @@ void mapcache_tileset_get_map_tiles(mapcache_context *ctx, mapcache_tileset *til
mapcache_extent *bbox, int width, int height,
int *ntiles,
mapcache_tile ***tiles,
mapcache_grid_link **effectively_used_grid_link);
mapcache_grid_link **effectively_used_grid_link,
apr_array_header_t *dimensions);
mapcache_image* mapcache_tileset_assemble_map_tiles(mapcache_context *ctx, mapcache_tileset *tileset,
mapcache_grid_link *grid_link,
......@@ -1278,6 +1346,49 @@ MS_DLL_EXPORT mapcache_http_response* mapcache_core_proxy_request(mapcache_conte
MS_DLL_EXPORT mapcache_http_response* mapcache_core_respond_to_error(mapcache_context *ctx);
/* in ruleset.c */
/**
* \brief allocate and initialize a new ruleset
* @param pool
*/
mapcache_ruleset* mapcache_ruleset_create(apr_pool_t *pool);
/**
* \brief allocate and initialize a new rule
* @param pool
*/
mapcache_rule* mapcache_ruleset_rule_create(apr_pool_t *pool);
/**
* \brief clone a rule
* @param pool
* @param rule
*/
mapcache_rule* mapcache_ruleset_rule_clone(apr_pool_t *pool, mapcache_rule *rule);
/**
* \brief get rule for zoom level, or NULL if none exist
* @param ruleset
* @param zoom_level
*/
mapcache_rule* mapcache_ruleset_rule_find(apr_array_header_t *rules, int zoom_level);
/**
* \brief get rule at index, or NULL if none exist
* @param rules
* @param idx
*/
mapcache_rule* mapcache_ruleset_rule_get(apr_array_header_t *rules, int idx);
/**
* \brief check if tile is within visible extent
* @param rule
* @param tile
*/
int mapcache_ruleset_is_visible_tile(mapcache_rule* rule, mapcache_tile *tile);
/* in grid.c */
mapcache_grid* mapcache_grid_create(apr_pool_t *pool);
......@@ -1320,6 +1431,7 @@ int mapcache_grid_get_level(mapcache_context *ctx, mapcache_grid *grid, double *
* \param tolerance the number of tiles around the given extent that can be requested without returning an error.
*/
MS_DLL_EXPORT void mapcache_grid_compute_limits(const mapcache_grid *grid, const mapcache_extent *extent, mapcache_extent_i *limits, int tolerance);
void mapcache_grid_compute_limits_at_level(const mapcache_grid *grid, const mapcache_extent *extent, mapcache_extent_i *limits_ptr, int tolerance, int zoom_level);
/* in util.c */
MS_DLL_EXPORT int mapcache_util_extract_int_list(mapcache_context *ctx, const char* args, const char *sep, int **numbers,
......@@ -1534,6 +1646,11 @@ void _mapcache_imageio_jpeg_decode_to_image(mapcache_context *ctx, mapcache_buff
*/
mapcache_image_format_type mapcache_imageio_header_sniff(mapcache_context *ctx, mapcache_buffer *buffer);
/**
* \brief lookup the first few bytes of a buffer to check for alpha channel
*/
mapcache_image_alpha_type mapcache_imageio_alpha_sniff(mapcache_context *ctx, mapcache_buffer *buffer);
/**
* \brief checks if the given buffer is a recognized image format
*/
......@@ -1563,6 +1680,7 @@ struct mapcache_requested_dimension {
mapcache_dimension *dimension;
char *requested_value;
char *cached_value;
apr_array_header_t *cached_entries_for_value;
};
void mapcache_tile_set_cached_dimension(mapcache_context *ctx, mapcache_tile *tile, const char *name, const char *value);
......@@ -1575,7 +1693,9 @@ MS_DLL_EXPORT apr_array_header_t *mapcache_requested_dimensions_clone(apr_pool_t
struct mapcache_dimension {
mapcache_dimension_type type;
const char *class_name;
int isTime;
int wms_querybymap_minzoom;
char *name;
char *unit;
apr_table_t *metadata;
......@@ -1638,7 +1758,7 @@ struct mapcache_pooled_connection {
typedef void (*mapcache_connection_constructor)(mapcache_context *ctx, void **connection, void *params);
typedef void (*mapcache_connection_destructor)(void *connection);
MS_DLL_EXPORT apr_status_t mapcache_connection_pool_create(mapcache_connection_pool **cp, apr_pool_t *server_pool);
MS_DLL_EXPORT apr_status_t mapcache_connection_pool_create(mapcache_cfg *cfg, mapcache_connection_pool **cp, apr_pool_t *server_pool);
mapcache_pooled_connection* mapcache_connection_pool_get_connection(mapcache_context *ctx, char *key,
mapcache_connection_constructor constructor,
mapcache_connection_destructor destructor,
......
......@@ -30,9 +30,18 @@
int mapcache_cache_tile_get(mapcache_context *ctx, mapcache_cache *cache, mapcache_tile *tile) {
int i,rv;
mapcache_rule *rule = mapcache_ruleset_rule_get(tile->grid_link->rules, tile->z);
#ifdef DEBUG
ctx->log(ctx,MAPCACHE_DEBUG,"calling tile_get on cache (%s): (tileset=%s, grid=%s, z=%d, x=%d, y=%d",cache->name,tile->tileset->name,tile->grid_link->grid->name,tile->z,tile->x, tile->y);
#endif
/* if tile is outside visible limits, return a blank tile */
if (mapcache_ruleset_is_visible_tile(rule, tile) == MAPCACHE_FALSE) {
tile->encoded_data = mapcache_buffer_create(0, ctx->pool);
mapcache_buffer_append(tile->encoded_data, rule->hidden_tile->size, rule->hidden_tile->buf);
return MAPCACHE_SUCCESS;
}
for(i=0;i<=cache->retry_count;i++) {
if(i) {
ctx->log(ctx,MAPCACHE_INFO,"cache (%s) get retry %d of %d. previous try returned error: %s",cache->name,i,cache->retry_count,ctx->get_error_message(ctx));
......@@ -79,9 +88,17 @@ void mapcache_cache_tile_delete(mapcache_context *ctx, mapcache_cache *cache, ma
int mapcache_cache_tile_exists(mapcache_context *ctx, mapcache_cache *cache, mapcache_tile *tile) {
int i,rv;
mapcache_rule *rule = mapcache_ruleset_rule_get(tile->grid_link->rules, tile->z);
#ifdef DEBUG
ctx->log(ctx,MAPCACHE_DEBUG,"calling tile_exists on cache (%s): (tileset=%s, grid=%s, z=%d, x=%d, y=%d",cache->name,tile->tileset->name,tile->grid_link->grid->name,tile->z,tile->x, tile->y);
#endif
/* if tile is outside visible limits return TRUE
a blank tile will be returned on subsequent get call on cache */
if (mapcache_ruleset_is_visible_tile(rule, tile) == MAPCACHE_FALSE) {
return MAPCACHE_TRUE;
}
for(i=0;i<=cache->retry_count;i++) {
if(i) {
ctx->log(ctx,MAPCACHE_INFO,"cache (%s) exists retry %d of %d. previous try returned error: %s",cache->name,i,cache->retry_count,ctx->get_error_message(ctx));
......
......@@ -187,9 +187,9 @@ size_t buffer_write_callback(void *ptr, size_t size, size_t nmemb, void *data)
return mapcache_buffer_append(buffer, realsize, ptr);
}
static void _set_headers(mapcache_context *ctx, CURL *curl, apr_table_t *headers) {
static struct curl_slist* _set_headers(mapcache_context *ctx, CURL *curl, apr_table_t *headers) {
if(!headers) {
return;
return NULL;
} else {
struct curl_slist *curl_headers=NULL;
const apr_array_header_t *array = apr_table_elts(headers);
......@@ -203,6 +203,7 @@ static void _set_headers(mapcache_context *ctx, CURL *curl, apr_table_t *headers
}
}
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_headers);
return curl_headers;
}
}
......@@ -210,6 +211,7 @@ static void _put_request(mapcache_context *ctx, CURL *curl, mapcache_buffer *buf
CURLcode res;
buffer_struct data;
mapcache_buffer *response;
struct curl_slist *curl_header_data;
data.buffer = buffer;
data.offset = 0;
......@@ -238,7 +240,7 @@ static void _put_request(mapcache_context *ctx, CURL *curl, mapcache_buffer *buf
/* don't use an Expect: 100 Continue header */
apr_table_set(headers, "Expect", "");
_set_headers(ctx, curl, headers);
curl_header_data = _set_headers(ctx, curl, headers);
/* specify target URL, and note that this URL should include a file
* name, not only a directory */
......@@ -272,14 +274,16 @@ static void _put_request(mapcache_context *ctx, CURL *curl, mapcache_buffer *buf
}
}
curl_slist_free_all(curl_header_data);
}
static int _head_request(mapcache_context *ctx, CURL *curl, char *url, apr_table_t *headers) {
CURLcode res;
long http_code;
struct curl_slist *curl_header_data;
_set_headers(ctx, curl, headers);
curl_header_data = _set_headers(ctx, curl, headers);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
......@@ -299,6 +303,8 @@ static int _head_request(mapcache_context *ctx, CURL *curl, char *url, apr_table
curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code);
}
curl_slist_free_all(curl_header_data);
return (int)http_code;
}
......@@ -306,8 +312,9 @@ static int _delete_request(mapcache_context *ctx, CURL *curl, char *url, apr_tab
CURLcode res;
long http_code;
struct curl_slist *curl_header_data;
_set_headers(ctx, curl, headers);
curl_header_data = _set_headers(ctx, curl, headers);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
......@@ -328,6 +335,8 @@ static int _delete_request(mapcache_context *ctx, CURL *curl, char *url, apr_tab
curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code);
}
curl_slist_free_all(curl_header_data);
return (int)http_code;
}
......@@ -336,8 +345,9 @@ static mapcache_buffer* _get_request(mapcache_context *ctx, CURL *curl, char *ur
CURLcode res;
mapcache_buffer *data = NULL;
long http_code;
struct curl_slist *curl_header_data;
_set_headers(ctx, curl, headers);
curl_header_data = _set_headers(ctx, curl, headers);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
......@@ -384,6 +394,8 @@ static mapcache_buffer* _get_request(mapcache_context *ctx, CURL *curl, char *ur
}
}
curl_slist_free_all(curl_header_data);
return data;
}
......
......@@ -71,8 +71,10 @@ struct mapcache_cache_sqlite {
void (*bind_stmt)(mapcache_context *ctx, void *stmt, mapcache_cache_sqlite *cache, mapcache_tile *tile);
int n_prepared_statements;
int detect_blank;
char *x_fmt,*y_fmt,*z_fmt,*inv_x_fmt,*inv_y_fmt,*div_x_fmt,*div_y_fmt,*inv_div_x_fmt,*inv_div_y_fmt;
char *x_fmt,*y_fmt,*z_fmt,*inv_x_fmt,*inv_y_fmt,*div_x_fmt,*div_y_fmt,*inv_div_x_fmt,*inv_div_y_fmt,
*top_fmt,*top_x_fmt,*top_y_fmt,*inv_top_x_fmt,*inv_top_y_fmt;
int count_x, count_y;
int top;
};
......@@ -277,6 +279,34 @@ static void _mapcache_cache_sqlite_filename_for_tile(mapcache_context *ctx, mapc
apr_psprintf(ctx->pool,dcache->inv_y_fmt,(tile->grid_link->grid->levels[tile->z]->maxy - tile->y - 1)/dcache->count_y*dcache->count_y));
}
if (dcache->top > 0) {
while(strstr(*path,"{top}"))
*path = mapcache_util_str_replace(ctx->pool,*path, "{top}",
apr_psprintf(ctx->pool,dcache->top_fmt,dcache->top));
while(strstr(*path,"{top_x}"))
*path = mapcache_util_str_replace(ctx->pool,*path, "{top_x}",
apr_psprintf(ctx->pool,dcache->top_x_fmt,
tile->x * tile->grid_link->grid->levels[dcache->top]->maxx
/ tile->grid_link->grid->levels[tile->z]->maxx));
while(strstr(*path,"{top_y}"))
*path = mapcache_util_str_replace(ctx->pool,*path, "{top_y}",
apr_psprintf(ctx->pool,dcache->top_y_fmt,
tile->y * tile->grid_link->grid->levels[dcache->top]->maxy
/ tile->grid_link->grid->levels[tile->z]->maxy));
while(strstr(*path,"{inv_top_x}"))
*path = mapcache_util_str_replace(ctx->pool,*path, "{inv_top_x}",
apr_psprintf(ctx->pool,dcache->inv_top_x_fmt,
tile->grid_link->grid->levels[dcache->top]->maxx - 1
- tile->x * tile->grid_link->grid->levels[dcache->top]->maxx
/ tile->grid_link->grid->levels[tile->z]->maxx));
while(strstr(*path,"{inv_top_y}"))
*path = mapcache_util_str_replace(ctx->pool,*path, "{inv_top_y}",
apr_psprintf(ctx->pool,dcache->inv_top_y_fmt,
tile->grid_link->grid->levels[dcache->top]->maxy - 1
- tile->y * tile->grid_link->grid->levels[dcache->top]->maxy
/ tile->grid_link->grid->levels[tile->z]->maxy));
}
}
if(!*path) {
......@@ -876,6 +906,26 @@ static void _mapcache_cache_sqlite_configuration_parse_xml(mapcache_context *ctx
if(fmt && *fmt) {
cache->inv_div_y_fmt = apr_pstrdup(ctx->pool,fmt);
}
fmt = (char*)ezxml_attr(cur_node,"top_fmt");
if(fmt && *fmt) {
cache->top_fmt = apr_pstrdup(ctx->pool,fmt);
}
fmt = (char*)ezxml_attr(cur_node,"top_x_fmt");
if(fmt && *fmt) {
cache->top_x_fmt = apr_pstrdup(ctx->pool,fmt);
}
fmt = (char*)ezxml_attr(cur_node,"top_y_fmt");
if(fmt && *fmt) {
cache->top_y_fmt = apr_pstrdup(ctx->pool,fmt);
}
fmt = (char*)ezxml_attr(cur_node,"inv_top_x_fmt");
if(fmt && *fmt) {
cache->inv_top_x_fmt = apr_pstrdup(ctx->pool,fmt);
}
fmt = (char*)ezxml_attr(cur_node,"inv_top_y_fmt");
if(fmt && *fmt) {
cache->inv_top_y_fmt = apr_pstrdup(ctx->pool,fmt);
}
}
cache->detect_blank = 0;
......@@ -939,6 +989,16 @@ static void _mapcache_cache_sqlite_configuration_parse_xml(mapcache_context *ctx
return;
}
}
cur_node = ezxml_child(node,"top");
if(cur_node && cur_node->txt && *cur_node->txt) {
char *endptr;
cache->top = (int)strtol(cur_node->txt,&endptr,10);
if(*endptr != 0) {
ctx->set_error(ctx,400,"failed to parse top value %s for sqlite cache %s", cur_node->txt,cache->cache.name);
return;
}
}
}
/**
......@@ -1018,8 +1078,11 @@ mapcache_cache* mapcache_cache_sqlite_create(mapcache_context *ctx)
cache->x_fmt = cache->y_fmt = cache->z_fmt
= cache->inv_x_fmt = cache->inv_y_fmt
= cache->div_x_fmt = cache->div_y_fmt
= cache->inv_div_x_fmt = cache->inv_div_y_fmt = apr_pstrdup(ctx->pool,"%d");
= cache->inv_div_x_fmt = cache->inv_div_y_fmt
= cache->top_fmt = cache->top_x_fmt = cache->top_y_fmt
= cache->inv_top_x_fmt = cache->inv_top_y_fmt = apr_pstrdup(ctx->pool,"%d");
cache->count_x = cache->count_y = -1;
cache->top = -1;
return (mapcache_cache*)cache;
}
......
......@@ -127,6 +127,7 @@ mapcache_cfg* mapcache_configuration_create(apr_pool_t *pool)
cfg->grids = apr_hash_make(pool);
cfg->image_formats = apr_hash_make(pool);
cfg->metadata = apr_table_make(pool,3);
cfg->rulesets = apr_hash_make(pool);
mapcache_configuration_add_image_format(cfg,
mapcache_imageio_create_png_format(pool,"PNG",MAPCACHE_COMPRESSION_FAST),
......@@ -241,6 +242,11 @@ mapcache_grid *mapcache_configuration_get_grid(mapcache_cfg *config, const char
return (mapcache_grid*)apr_hash_get(config->grids, (void*)key, APR_HASH_KEY_STRING);
}
mapcache_ruleset *mapcache_configuration_get_ruleset(mapcache_cfg *config, const char *key)
{
return (mapcache_ruleset*)apr_hash_get(config->rulesets, (void*)key, APR_HASH_KEY_STRING);
}
mapcache_tileset *mapcache_configuration_get_tileset(mapcache_cfg *config, const char *key)
{
if(config->mode == MAPCACHE_MODE_NORMAL) {
......@@ -265,6 +271,11 @@ void mapcache_configuration_add_grid(mapcache_cfg *config, mapcache_grid *grid,
apr_hash_set(config->grids, key, APR_HASH_KEY_STRING, (void*)grid);
}
void mapcache_configuration_add_ruleset(mapcache_cfg *config, mapcache_ruleset *ruleset, const char * key)
{
apr_hash_set(config->rulesets, key, APR_HASH_KEY_STRING, (void*)ruleset);
}
void mapcache_configuration_add_tileset(mapcache_cfg *config, mapcache_tileset *tileset, const char * key)
{
tileset->config = config;
......
......@@ -63,6 +63,7 @@ void parseMetadata(mapcache_context *ctx, ezxml_t node, apr_table_t *metadata)
void parseDimensions(mapcache_context *ctx, ezxml_t node, mapcache_tileset *tileset)
{
ezxml_t dimension_node;
ezxml_t wms_querybymap_node;
apr_array_header_t *dimensions = apr_array_make(ctx->pool,1,sizeof(mapcache_dimension*));
for(dimension_node = ezxml_child(node,"dimension"); dimension_node; dimension_node = dimension_node->next) {
char *name = (char*)ezxml_attr(dimension_node,"name");
......@@ -120,6 +121,26 @@ void parseDimensions(mapcache_context *ctx, ezxml_t node, mapcache_tileset *tile
return;
}
dimension->wms_querybymap_minzoom = -1;
wms_querybymap_node = ezxml_child(dimension_node,"wms_querybymap");
if (wms_querybymap_node && wms_querybymap_node->txt) {
if (!strcasecmp(wms_querybymap_node->txt,"true")) {
const char * minzoom = ezxml_attr(wms_querybymap_node,"minzoom");
dimension->wms_querybymap_minzoom = 0;
if (minzoom && *minzoom) {
char *endptr;
dimension->wms_querybymap_minzoom = strtol(minzoom,&endptr,10);
if (*endptr != 0 || dimension->wms_querybymap_minzoom < 0) {
ctx->set_error(ctx, 400, "failed to parse minzoom \"%s\" for <wms_querybymap>"
"expecting an integer starting from 0",minzoom);
return;
}
}
} else if (strcasecmp(wms_querybymap_node->txt,"false")) {
ctx->set_error(ctx,400,"failed to parse <wms_querybymap> (%s), expecting \"true\" or \"false\"",wms_querybymap_node->txt);
return;
}
}
dimension->configuration_parse_xml(ctx,dimension,dimension_node);
GC_CHECK_ERROR(ctx);
......@@ -158,6 +179,31 @@ void parseDimensions(mapcache_context *ctx, ezxml_t node, mapcache_tileset *tile
}
}
tileset->assembly_threaded_fetching_maxzoom = -1;
dimension_node = ezxml_child(node,"assembly_threaded_fetching");
if (dimension_node) {
if (dimension_node && dimension_node->txt) {
if (!strcmp(dimension_node->txt,"true")) {
int maxzoom = INT_MAX;
char * smaxzoom = (char*)ezxml_attr(dimension_node,"maxzoom");;
if (smaxzoom && *smaxzoom) {
char *endptr;
maxzoom = (int)strtol(smaxzoom,&endptr,10);
if(*endptr != 0 || maxzoom < 0) {
ctx->set_error(ctx, 400, "failed to parse assembly_threaded_fetching"
" maxzoom %s (expecting a positive integer)", smaxzoom);
return;
}
}
tileset->assembly_threaded_fetching_maxzoom = maxzoom;
} else if (strcmp(dimension_node->txt,"false")) {
ctx->set_error(ctx,400,"failed to parse <assembly_threaded_fetching>"
" (%s), expecting \"true\" or \"false\"",dimension_node->txt);
return;
}
}
}
/* should we create subdimensions from source if not found in cache.
e.g. if dimension=mosaic returns dimension=val1,val2,val3 should we
query the wms source with dimension=val1 , dimension=val2 and/or
......@@ -177,6 +223,108 @@ void parseDimensions(mapcache_context *ctx, ezxml_t node, mapcache_tileset *tile
}
}
void parseRuleset(mapcache_context *ctx, ezxml_t node, mapcache_cfg *config)
{
char *name;
mapcache_ruleset *ruleset;
ezxml_t cur_node;
int i;
name = (char*)ezxml_attr(node,"name");
if(!name || !strlen(name)) {
ctx->set_error(ctx, 400, "mandatory attribute \"name\" not found in <ruleset>");
return;
} else {
name = apr_pstrdup(ctx->pool, name);
/* check we don't already have a ruleset defined with this name */
if(mapcache_configuration_get_ruleset(config, name)) {
ctx->set_error(ctx, 400, "duplicate ruleset with name \"%s\"",name);
return;
}
}
ruleset = mapcache_ruleset_create(ctx->pool);
ruleset->name = name;
/* parse rules, <rule> */
for(cur_node = ezxml_child(node,"rule"), i = 0; cur_node; cur_node = cur_node->next, i++) {
int *zoom, nzoom, j;
char* zoom_attr = (char*)ezxml_attr(cur_node, "zoom_level");
ezxml_t visibility = ezxml_child(cur_node, "visibility");
mapcache_rule *rule = mapcache_ruleset_rule_create(ctx->pool);
/* parse zoom_level attribute */
if(zoom_attr && *zoom_attr) {
char *value = apr_pstrdup(ctx->pool, zoom_attr);
if(MAPCACHE_SUCCESS != mapcache_util_extract_int_list(ctx, value, NULL, &zoom, &nzoom) || nzoom < 1) {
ctx->set_error(ctx, 400, "failed to parse zoom_level array %s in ruleset %s, rule %d. "
"(expecting space separated integers, eg <rule zoom_level=\"0 1 2\">)",
value, ruleset->name, i+1);
return;
}
} else {
ctx->set_error(ctx, 400, "zoom_level not set in rule %d", i+1);
return;
}
/* parse visibility, <visibility> */
if(visibility) {
char *hidden_color = (char*)ezxml_attr(visibility, "hidden_color");
ezxml_t extent_node;
if (hidden_color && *hidden_color) {
/* parse color, base 16 */
rule->hidden_color = (unsigned int)strtol(hidden_color, NULL, 16);
if (strlen(hidden_color) <= 6) {
/* if color is set, but no alpha value. Assume no transparency. */
rule->hidden_color += 0xff000000;
}
}
/* parse extents, <extent> */
for (extent_node = ezxml_child(visibility,"extent"); extent_node; extent_node = extent_node->next) {
double *values;
int nvalues;
char *value = apr_pstrdup(ctx->pool,extent_node->txt);
mapcache_extent extent = {0,0,0,0};
mapcache_extent *pextent;
if(MAPCACHE_SUCCESS != mapcache_util_extract_double_list(ctx, value, NULL, &values, &nvalues) ||
nvalues != 4) {
ctx->set_error(ctx, 400, "failed to parse extent array %s in ruleset %s, rule %d. "
"(expecting 4 space separated numbers, got %d (%f %f %f %f)"
"eg <extent>-180 -90 180 90</extent>)",
value,ruleset->name,i+1,nvalues,values[0],values[1],values[2],values[3]);
return;
}
extent.minx = values[0];
extent.miny = values[1];
extent.maxx = values[2];
extent.maxy = values[3];
pextent = (mapcache_extent*)apr_pcalloc(ctx->pool, sizeof(mapcache_extent));
*pextent = extent;
APR_ARRAY_PUSH(rule->visible_extents, mapcache_extent*) = pextent;
}
}
/* add this rule for given zoom_levels */
for(j = 0; j < nzoom; j++) {
mapcache_rule *clone_rule = mapcache_ruleset_rule_clone(ctx->pool, rule);
/* check for duplicate rule for this zoom level */
if(mapcache_ruleset_rule_find(ruleset->rules, zoom[j]) != NULL) {
ctx->set_error(ctx, 400, "found duplicate rule for zoom_level %d", zoom[j]);
return;
}
clone_rule->zoom_level = zoom[j];
APR_ARRAY_PUSH(ruleset->rules, mapcache_rule*) = clone_rule;
}
}
mapcache_configuration_add_ruleset(config,ruleset,ruleset->name);
}
void parseGrid(mapcache_context *ctx, ezxml_t node, mapcache_cfg *config)
{
char *name;
......@@ -683,12 +831,24 @@ void parseTileset(mapcache_context *ctx, ezxml_t node, mapcache_cfg *config)
havewgs84bbox = 1;
}
if ((cur_node = ezxml_child(node,"format")) != NULL) {
mapcache_image_format *format = mapcache_configuration_get_image_format(config,cur_node->txt);
if(!format) {
ctx->set_error(ctx, 400, "tileset \"%s\" references format \"%s\","
" but it is not configured",name,cur_node->txt);
return;
}
tileset->format = format;
}
for(cur_node = ezxml_child(node,"grid"); cur_node; cur_node = cur_node->next) {
mapcache_grid *grid;
mapcache_grid_link *gridlink;
char *restrictedExtent = NULL, *sTolerance = NULL;
mapcache_ruleset *ruleset = NULL;
char *restrictedExtent = NULL, *sTolerance = NULL, *ruleset_name = NULL;
mapcache_extent *extent;
int tolerance;
int i;
if (tileset->grid_links == NULL) {
tileset->grid_links = apr_array_make(ctx->pool,1,sizeof(mapcache_grid_link*));
......@@ -706,6 +866,17 @@ void parseTileset(mapcache_context *ctx, ezxml_t node, mapcache_cfg *config)
gridlink->grid_limits = (mapcache_extent_i*)apr_pcalloc(ctx->pool,grid->nlevels*sizeof(mapcache_extent_i));
gridlink->outofzoom_strategy = MAPCACHE_OUTOFZOOM_NOTCONFIGURED;
gridlink->intermediate_grids = apr_array_make(ctx->pool,1,sizeof(mapcache_grid_link*));
gridlink->rules = apr_array_make(ctx->pool,0,sizeof(mapcache_rule*));
ruleset_name = (char*)ezxml_attr(cur_node,"ruleset");
if(ruleset_name) {
ruleset = mapcache_configuration_get_ruleset(config, ruleset_name);
if(!ruleset) {
ctx->set_error(ctx, 400, "grid \"%s\" in tileset \"%s\" references ruleset \"%s\","
" but it is not configured", cur_node->txt, name, ruleset_name);
return;
}
}
restrictedExtent = (char*)ezxml_attr(cur_node,"restricted_extent");
if(restrictedExtent) {
......@@ -749,6 +920,55 @@ void parseTileset(mapcache_context *ctx, ezxml_t node, mapcache_cfg *config)
mapcache_grid_compute_limits(grid,extent,gridlink->grid_limits,tolerance);
// setup zoom level rules if configured
if(ruleset) {
mapcache_buffer *last_hidden_tile = NULL;
unsigned int last_hidden_color;
// prepare one rule per zoom level. if rule is missing it will be NULL
for(i = 0; i < grid->nlevels; i++) {
mapcache_rule *rule = mapcache_ruleset_rule_find(ruleset->rules, i);
if(rule) {
mapcache_rule *rule_clone = mapcache_ruleset_rule_clone(ctx->pool, rule);
if(rule->visible_extents) {
int j;
// create blank tile in configured format to return when outside visible extent
// try to reuse last tile if possible
if(last_hidden_tile == NULL || last_hidden_color != rule->hidden_color) {
if(tileset->format) {
last_hidden_tile = tileset->format->create_empty_image(ctx, tileset->format, grid->tile_sx, grid->tile_sy, rule->hidden_color);
} else {
last_hidden_tile = config->default_image_format->create_empty_image(ctx, config->default_image_format, grid->tile_sx, grid->tile_sy, rule->hidden_color);
}
if(GC_HAS_ERROR(ctx)) {
return;
}
last_hidden_color = rule->hidden_color;
}
rule_clone->hidden_tile = last_hidden_tile;
// compute limits for extents
for(j = 0; j < rule->visible_extents->nelts; j++) {
mapcache_extent *visible_extent = APR_ARRAY_IDX(rule->visible_extents, j, mapcache_extent*);
mapcache_extent_i *visible_limit = apr_pcalloc(ctx->pool, sizeof(mapcache_extent_i));
mapcache_grid_compute_limits_at_level(grid,visible_extent,visible_limit,tolerance,i);
APR_ARRAY_PUSH(rule_clone->visible_limits, mapcache_extent_i*) = visible_limit;
}
}
APR_ARRAY_PUSH(gridlink->rules, mapcache_rule*) = rule_clone;
} else {
// no rule for zoom level
APR_ARRAY_PUSH(gridlink->rules, mapcache_rule*) = NULL;
}
}
}
sTolerance = (char*)ezxml_attr(cur_node,"minzoom");
if(sTolerance) {
char *endptr;
......@@ -941,16 +1161,6 @@ void parseTileset(mapcache_context *ctx, ezxml_t node, mapcache_cfg *config)
}
}
if ((cur_node = ezxml_child(node,"format")) != NULL) {
mapcache_image_format *format = mapcache_configuration_get_image_format(config,cur_node->txt);
if(!format) {
ctx->set_error(ctx, 400, "tileset \"%s\" references format \"%s\","
" but it is not configured",name,cur_node->txt);
return;
}
tileset->format = format;
}
mapcache_tileset_configuration_check(ctx,tileset);
GC_CHECK_ERROR(ctx);
mapcache_configuration_add_tileset(config,tileset,name);
......@@ -1075,6 +1285,11 @@ void mapcache_configuration_parse_xml(mapcache_context *ctx, const char *filenam
if(GC_HAS_ERROR(ctx)) goto cleanup;
}
for(node = ezxml_child(doc,"ruleset"); node; node = node->next) {
parseRuleset(ctx, node, config);
if(GC_HAS_ERROR(ctx)) goto cleanup;
}
for(node = ezxml_child(doc,"tileset"); node; node = node->next) {
parseTileset(ctx, node, config);
if(GC_HAS_ERROR(ctx)) goto cleanup;
......@@ -1230,6 +1445,28 @@ void mapcache_configuration_parse_xml(mapcache_context *ctx, const char *filenam
}
}
config->cp_hmax = 1024;
config->cp_ttl = 60*1000*1000;
if((node = ezxml_child(doc,"connection_pool")) != NULL) {
ezxml_t cp_param_node;
char *endptr;
if ((cp_param_node = ezxml_child(node,"max_connections")) != NULL) {
config->cp_hmax = (int)strtol(cp_param_node->txt,&endptr,10);
if (*endptr != 0 || config->cp_hmax < 0) {
ctx->set_error(ctx, 400, "failed to parse max_connections %s "
"(expecting a positive integer)", cp_param_node->txt);
return;
}
}
if ((cp_param_node = ezxml_child(node,"time_to_live_us")) != NULL) {
config->cp_ttl = (int)strtol(cp_param_node->txt,&endptr,10);
if (*endptr != 0 || config->cp_ttl < 0) {
ctx->set_error(ctx, 400, "failed to parse time_to_live_us %s "
"(expecting a positive integer)", cp_param_node->txt);
return;
}
}
}
cleanup:
ezxml_free(doc);
......
......@@ -74,11 +74,11 @@ static apr_status_t mapcache_connection_container_destructor(void *conn_, void *
}
apr_status_t mapcache_connection_pool_create(mapcache_connection_pool **cp, apr_pool_t *server_pool) {
apr_status_t mapcache_connection_pool_create(mapcache_cfg *cfg, mapcache_connection_pool **cp, apr_pool_t *server_pool) {
apr_status_t rv;
*cp = apr_pcalloc(server_pool, sizeof(mapcache_connection_pool));
(*cp)->server_pool = server_pool;
rv = apr_reslist_create(&((*cp)->connexions), 1, 5, 1024, 60*1000000,
rv = apr_reslist_create(&((*cp)->connexions), 1, 5, cfg->cp_hmax, cfg->cp_ttl,
mapcache_connection_container_creator,
mapcache_connection_container_destructor,
NULL,
......
......@@ -369,7 +369,8 @@ mapcache_map* mapcache_assemble_maps(mapcache_context *ctx, mapcache_map **maps,
for(i=0; i<nmaps; i++) {
mapcache_tileset_get_map_tiles(ctx,maps[i]->tileset,maps[i]->grid_link,
&maps[i]->extent, maps[i]->width, maps[i]->height,
&(nmaptiles[i]), &(maptiles[i]), &(effectively_used_grid_links[i]));
&(nmaptiles[i]), &(maptiles[i]), &(effectively_used_grid_links[i]),
maps[i]->dimensions);
if(GC_HAS_ERROR(ctx)) return NULL;
ntiles += nmaptiles[i];
}
......@@ -379,7 +380,7 @@ mapcache_map* mapcache_assemble_maps(mapcache_context *ctx, mapcache_map **maps,
int j;
for(j=0; j<nmaptiles[i]; j++) {
tiles[ntiles] = maptiles[i][j];
tiles[ntiles]->dimensions = maps[i]->dimensions;
tiles[ntiles]->dimensions = mapcache_requested_dimensions_clone(ctx->pool, maps[i]->dimensions);
ntiles++;
}
}
......
......@@ -139,7 +139,7 @@ static apr_array_header_t* _mapcache_dimension_regex_get_entries_for_value(mapca
}
#endif
else {
ctx->set_error(ctx,400,"failed to validate requested value for dimension (%s)",dim->name);
ctx->set_error(ctx,400,"failed to validate requested value for %s (%s)",dim->class_name,dim->name);
}
return values;
}
......@@ -165,7 +165,7 @@ static void _mapcache_dimension_regex_parse_xml(mapcache_context *ctx, mapcache_
if(child_node && child_node->txt && *child_node->txt) {
dimension->regex_string = apr_pstrdup(ctx->pool,child_node->txt);
} else {
ctx->set_error(ctx,400,"failed to parse dimension regex: no <regex> child supplied");
ctx->set_error(ctx,400,"failed to parse %s regex: no <regex> child supplied",dim->class_name);
return;
}
#ifdef USE_PCRE
......@@ -174,8 +174,8 @@ static void _mapcache_dimension_regex_parse_xml(mapcache_context *ctx, mapcache_
int pcre_offset;
dimension->pcregex = pcre_compile(dimension->regex_string,0,&pcre_err, &pcre_offset,0);
if(!dimension->pcregex) {
ctx->set_error(ctx,400,"failed to compile regular expression \"%s\" for dimension \"%s\": %s",
dimension->regex_string,dim->name,pcre_err);
ctx->set_error(ctx,400,"failed to compile regular expression \"%s\" for %s \"%s\": %s",
dimension->regex_string,dim->class_name,dim->name,pcre_err);
return;
}
}
......@@ -185,8 +185,8 @@ static void _mapcache_dimension_regex_parse_xml(mapcache_context *ctx, mapcache_
if(rc) {
char errmsg[200];
regerror(rc,dimension->regex,errmsg,200);
ctx->set_error(ctx,400,"failed to compile regular expression \"%s\" for dimension \"%s\": %s",
dimension->regex_string,dim->name,errmsg);
ctx->set_error(ctx,400,"failed to compile regular expression \"%s\" for %s \"%s\": %s",
dimension->regex_string,dim->class_name,dim->name,errmsg);
return;
}
}
......@@ -215,7 +215,7 @@ static apr_array_header_t* _mapcache_dimension_values_get_entries_for_value(mapc
}
}
if(i == dimension->values->nelts) {
ctx->set_error(ctx,400,"failed to validate requested value for dimension (%s)",dim->name);
ctx->set_error(ctx,400,"failed to validate requested value for %s (%s)",dim->class_name,dim->name);
}
return values;
}
......@@ -241,13 +241,13 @@ static void _mapcache_dimension_values_parse_xml(mapcache_context *ctx, mapcache
dimension = (mapcache_dimension_values*)dim;
if(!child_node) {
ctx->set_error(ctx,400,"failed to parse dimension values: no <value> children supplied");
ctx->set_error(ctx,400,"failed to parse %s values: no <value> children supplied",dim->class_name);
return;
}
for(; child_node; child_node = child_node->next) {
const char* entry = child_node->txt;
if(!entry || !*entry) {
ctx->set_error(ctx,400,"failed to parse dimension values: empty <value>");
ctx->set_error(ctx,400,"failed to parse %s values: empty <value>",dim->class_name);
return;
}
APR_ARRAY_PUSH(dimension->values,char*) = apr_pstrdup(ctx->pool,entry);
......@@ -280,6 +280,7 @@ mapcache_dimension* mapcache_dimension_values_create(mapcache_context *ctx, apr_
{
mapcache_dimension_values *dimension = apr_pcalloc(pool, sizeof(mapcache_dimension_values));
dimension->dimension.type = MAPCACHE_DIMENSION_VALUES;
dimension->dimension.class_name = "dimension";
dimension->values = apr_array_make(pool,1,sizeof(char*));
dimension->dimension._get_entries_for_value = _mapcache_dimension_values_get_entries_for_value;
dimension->dimension.configuration_parse_xml = _mapcache_dimension_values_parse_xml;
......@@ -292,6 +293,7 @@ mapcache_dimension* mapcache_dimension_regex_create(mapcache_context *ctx, apr_p
{
mapcache_dimension_regex *dimension = apr_pcalloc(pool, sizeof(mapcache_dimension_regex));
dimension->dimension.type = MAPCACHE_DIMENSION_REGEX;
dimension->dimension.class_name = "dimension";
#ifndef USE_PCRE
dimension->regex = (regex_t*)apr_pcalloc(pool, sizeof(regex_t));
#endif
......
......@@ -43,22 +43,6 @@ struct mapcache_dimension_elasticsearch {
};
// Hook cJSON memory allocation on APR pool mechanism
static apr_pool_t * _pool_for_cJSON_malloc_hook;
static void * _malloc_for_cJSON(size_t size) {
return apr_palloc(_pool_for_cJSON_malloc_hook,size);
}
static void _free_for_cJSON(void *ptr) { }
static void _create_json_pool(apr_pool_t * parent_pool) {
cJSON_Hooks hooks = { _malloc_for_cJSON, _free_for_cJSON };
apr_pool_create(&_pool_for_cJSON_malloc_hook,parent_pool);
cJSON_InitHooks(&hooks);
}
static void _destroy_json_pool() {
apr_pool_destroy(_pool_for_cJSON_malloc_hook);
}
static void _mapcache_dimension_elasticsearch_parse_xml(mapcache_context *ctx, mapcache_dimension *dim, ezxml_t node)
{
mapcache_dimension_elasticsearch *dimension;
......@@ -113,14 +97,12 @@ static char * _mapcache_dimension_elasticsearch_bind_parameters(mapcache_context
if (value) {
// Sanitize dimension value for safe insertion in JSON request
cJSON * json_str;
_create_json_pool(ctx->pool);
json_str = cJSON_CreateString(value);
val = cJSON_Print(json_str);
if (val) {
// Discard double quotes while copying
val = apr_pstrndup(ctx->pool,val+1,strlen(val)-2);
}
_destroy_json_pool();
}
res = mapcache_util_str_replace_all(ctx->pool,req,":dim",val);
......@@ -164,8 +146,6 @@ static apr_array_header_t * _mapcache_dimension_elasticsearch_do_query(mapcache_
mapcache_buffer_append(buffer,1,"");
resp = (char*)buffer->buf;
_create_json_pool(ctx->pool);
// Parse response format: this should be a list of keys or integers
json_fmt = cJSON_Parse(response_format);
if (!json_fmt) {
......@@ -230,7 +210,6 @@ static apr_array_header_t * _mapcache_dimension_elasticsearch_do_query(mapcache_
}
cleanup:
_destroy_json_pool();
return table;
}
......
......@@ -150,40 +150,44 @@ const char* mapcache_grid_get_srs(mapcache_context *ctx, mapcache_grid *grid)
return (const char*)grid->srs;
}
void mapcache_grid_compute_limits(const mapcache_grid *grid, const mapcache_extent *extent, mapcache_extent_i *limits, int tolerance)
void mapcache_grid_compute_limits_at_level(const mapcache_grid *grid, const mapcache_extent *extent, mapcache_extent_i *limits_ptr, int tolerance, int zoom_level)
{
int i;
double epsilon = 0.0000001;
for(i=0; i<grid->nlevels; i++) {
mapcache_grid_level *level = grid->levels[i];
mapcache_grid_level *level = grid->levels[zoom_level];
double unitheight = grid->tile_sy * level->resolution;
double unitwidth = grid->tile_sx * level->resolution;
switch(grid->origin) {
case MAPCACHE_GRID_ORIGIN_BOTTOM_LEFT:
limits[i].minx = floor((extent->minx - grid->extent.minx) / unitwidth + epsilon) - tolerance;
limits[i].maxx = ceil((extent->maxx - grid->extent.minx) / unitwidth - epsilon) + tolerance;
limits[i].miny = floor((extent->miny - grid->extent.miny) / unitheight + epsilon) - tolerance;
limits[i].maxy = ceil((extent->maxy - grid->extent.miny) / unitheight - epsilon) + tolerance;
limits_ptr->minx = floor((extent->minx - grid->extent.minx) / unitwidth + epsilon) - tolerance;
limits_ptr->maxx = ceil((extent->maxx - grid->extent.minx) / unitwidth - epsilon) + tolerance;
limits_ptr->miny = floor((extent->miny - grid->extent.miny) / unitheight + epsilon) - tolerance;
limits_ptr->maxy = ceil((extent->maxy - grid->extent.miny) / unitheight - epsilon) + tolerance;
break;
case MAPCACHE_GRID_ORIGIN_TOP_LEFT:
limits[i].minx = floor((extent->minx - grid->extent.minx) / unitwidth + epsilon) - tolerance;
limits[i].maxx = ceil((extent->maxx - grid->extent.minx) / unitwidth - epsilon) + tolerance;
limits[i].miny = floor((grid->extent.maxy - extent->maxy) / unitheight + epsilon) - tolerance;
//limits[i].maxy = level->maxy - floor((extent->miny - grid->extent.miny) / unitheight + epsilon) + tolerance;
limits[i].maxy = ceil((grid->extent.maxy - extent->miny) / unitheight - epsilon) + tolerance;
//printf("%d: %d %d %d %d\n",i,limits[i].minx,limits[i].miny,limits[i].maxx,limits[i].maxy);
limits_ptr->minx = floor((extent->minx - grid->extent.minx) / unitwidth + epsilon) - tolerance;
limits_ptr->maxx = ceil((extent->maxx - grid->extent.minx) / unitwidth - epsilon) + tolerance;
limits_ptr->miny = floor((grid->extent.maxy - extent->maxy) / unitheight + epsilon) - tolerance;
//limits_ptr->maxy = level->maxy - floor((extent->miny - grid->extent.miny) / unitheight + epsilon) + tolerance;
limits_ptr->maxy = ceil((grid->extent.maxy - extent->miny) / unitheight - epsilon) + tolerance;
//printf("%d: %d %d %d %d\n",i,limits_ptr->minx,limits_ptr->miny,limits_ptr->maxx,limits_ptr->maxy);
break;
case MAPCACHE_GRID_ORIGIN_TOP_RIGHT:
case MAPCACHE_GRID_ORIGIN_BOTTOM_RIGHT:
break; /* not implemented */
}
// to avoid requesting out-of-range tiles
if (limits[i].minx < 0) limits[i].minx = 0;
if (limits[i].maxx > level->maxx) limits[i].maxx = level->maxx;
if (limits[i].miny < 0) limits[i].miny = 0;
if (limits[i].maxy > level->maxy) limits[i].maxy = level->maxy;
if (limits_ptr->minx < 0) limits_ptr->minx = 0;
if (limits_ptr->maxx > level->maxx) limits_ptr->maxx = level->maxx;
if (limits_ptr->miny < 0) limits_ptr->miny = 0;
if (limits_ptr->maxy > level->maxy) limits_ptr->maxy = level->maxy;
}
void mapcache_grid_compute_limits(const mapcache_grid *grid, const mapcache_extent *extent, mapcache_extent_i *limits, int tolerance)
{
int i;
for(i=0; i<grid->nlevels; i++) {
mapcache_grid_compute_limits_at_level(grid, extent, &limits[i], tolerance, i);
}
}
......