[mapcache] 01/08: Imported Upstream version 1.6.0
Bas Couwenberg
sebastic at debian.org
Thu Apr 13 18:33:40 UTC 2017
This is an automated email from the git hooks/post-receive script.
sebastic pushed a commit to branch master
in repository mapcache.
commit 6c6a6502d3c0873da29871b3fac2b248e425bf9d
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date: Thu Apr 13 18:46:54 2017 +0200
Imported Upstream version 1.6.0
---
.travis.yml | 56 +++
CMakeLists.txt | 24 +-
MIGRATION_GUIDE.txt | 16 +
apache/mod_mapcache.c | 245 ++++++------
cgi/mapcache.c | 5 +-
include/mapcache-config.h.in | 4 +-
include/mapcache.h | 715 +++++++---------------------------
include/mapcache_services.h | 162 ++++++++
lib/buffer.c | 1 -
lib/cache.c | 159 ++++++++
lib/cache_bdb.c | 29 +-
lib/cache_composite.c | 95 ++++-
lib/cache_couchbase.c | 27 +-
lib/cache_disk.c | 119 ++++--
lib/cache_fallback.c | 51 ++-
lib/cache_memcache.c | 36 +-
lib/cache_multitier.c | 40 +-
lib/cache_rest.c | 643 +++++++++++++++++++++++--------
lib/cache_riak.c | 174 ++++-----
lib/cache_sqlite.c | 73 +++-
lib/cache_tiff.c | 607 +++++++++++++++++++++++++++--
lib/cache_tokyocabinet.c | 17 +-
lib/configuration.c | 4 +-
lib/configuration_xml.c | 237 ++++++------
lib/connection_pool.c | 4 +-
lib/core.c | 8 +-
lib/dimension.c | 726 +++++++++++++++++++----------------
lib/grid.c | 61 ++-
lib/http.c | 6 +-
lib/image.c | 29 +-
lib/imageio_jpeg.c | 15 +-
lib/imageio_mixed.c | 6 +-
lib/imageio_png.c | 2 +-
lib/lock.c | 127 ++++--
lib/service_demo.c | 167 ++++----
lib/service_kml.c | 8 +-
lib/service_mapguide.c | 2 +-
lib/service_tms.c | 270 ++++++++-----
lib/service_ve.c | 26 +-
lib/service_wms.c | 171 +++------
lib/service_wmts.c | 174 +++------
lib/services.c | 9 +
lib/source.c | 55 +++
lib/source_dummy.c | 17 +-
lib/source_fallback.c | 165 ++++++++
lib/source_gdal.c | 723 +++++++++++++++++++++++++++-------
lib/source_mapserver.c | 143 +++----
lib/source_wms.c | 63 +--
lib/tileset.c | 416 ++++++++++++++++----
lib/util.c | 120 +++++-
mapcache.xml.sample | 26 ++
nginx/ngx_http_mapcache_module.c | 5 +-
tests/data/world.tif | Bin 0 -> 702072 bytes
tests/expected/wms_capabilities.xml | 75 ++++
tests/expected/wmts_capabilities.xml | 247 ++++++++++++
tests/run_tests.sh | 49 +++
tests/travis_setup.sh | 65 ++++
util/mapcache_seed.c | 434 ++++++++++++++-------
58 files changed, 5421 insertions(+), 2532 deletions(-)
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..6f59348
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,56 @@
+matrix:
+ fast_finish: true
+ include:
+ - os: linux
+ dist: trusty
+ language: c
+ sudo: required
+ env:
+ - DISTRO=trusty
+ - BUILD_TYPE=maximum
+
+ - os: linux
+ dist: trusty
+ language: c
+ sudo: required
+ env:
+ - DISTRO=trusty
+ - BUILD_TYPE=minimum
+
+ - os: linux
+ language: c
+ sudo: required
+ env:
+ - DISTRO=precise
+ - BUILD_TYPE=maximum
+
+language: c
+
+before_install:
+ - sudo mv /etc/apt/sources.list.d/pgdg* /tmp
+ - sudo apt-get purge -y libgdal* libgeos* libspatialite*
+ - sudo add-apt-repository -y ppa:ubuntugis/ubuntugis-unstable
+ - sudo apt-get update
+ - sudo apt-get install cmake libspatialite-dev libfcgi-dev libproj-dev libgeos-dev libgdal-dev libtiff-dev libgeotiff-dev apache2-dev libpcre3-dev libsqlite3-dev libdb-dev
+# For testing
+ - sudo apt-get install libxml2-utils apache2 gdal-bin
+
+script:
+ - mkdir build
+ - cd build
+ - if test "$BUILD_TYPE" = "maximum"; then cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DWITH_TIFF=ON -DWITH_GEOTIFF=ON -DWITH_TIFF_WRITE_SUPPORT=ON -DWITH_PCRE=ON -DWITH_SQLITE=ON -DWITH_BERKELEY_DB=ON; else cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DWITH_TIFF=OFF -DWITH_GEOTIFF=OFF -DWITH_TIFF_WRITE_SUPPORT=OFF -DWITH_PCRE=OFF -DWITH_SQLITE=OFF -DWITH_BERKELEY_DB=OFF -DWITH_GDAL=OFF -DWITH_GEOS=OFF -DWITH_FCGI=OFF -DWITH_CGI=OFF -DWITH_APACHE=OFF -DWITH_OGR=OFF -DWITH_MAPSERVER=OFF; fi
+ - make -j3
+ - sudo make install
+# Only test with Apache 2.4
+ - if test "$DISTRO" = "trusty" -a "$BUILD_TYPE" = "maximum"; then cd ../tests; sh ./travis_setup.sh; sh ./run_tests.sh; fi
+
+
+#notifications:
+# email:
+# recipients:
+# - thomas.bonfort at gmail.com
+# irc:
+# channels:
+# - "irc.freenode.org#mapserver"
+# use_notice: true
+
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ea1e62a..3d4edc5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,6 +1,6 @@
cmake_minimum_required (VERSION 2.6)
-project (MapCache)
+project (MapCache C)
include(CheckFunctionExists)
include(CheckIncludeFile)
@@ -12,9 +12,8 @@ endif ()
set (MAPCACHE_VERSION_MAJOR 1)
-set (MAPCACHE_VERSION_MINOR 4)
-set (MAPCACHE_VERSION_REVISION 1)
-
+set (MAPCACHE_VERSION_MINOR 6)
+set (MAPCACHE_VERSION_REVISION 0)
if(NOT DEFINED CMAKE_INSTALL_LIBDIR)
set(CMAKE_INSTALL_LIBDIR lib)
@@ -58,6 +57,7 @@ endmacro()
check_function_exists("strncasecmp" HAVE_STRNCASECMP)
check_function_exists("symlink" HAVE_SYMLINK)
check_function_exists ("timegm" HAVE_TIMEGM)
+check_function_exists ("strptime" HAVE_STRPTIME)
set(CMAKE_SKIP_BUILD_RPATH FALSE)
if(APPLE)
@@ -66,8 +66,9 @@ endif()
set(CMAKE_LINK_INTERFACE_LIBRARY "")
file(GLOB mapcache_SOURCES lib/*.c )
+file(GLOB mapcache_HEADERS include/*.h)
-add_library(mapcache SHARED ${mapcache_SOURCES})
+add_library(mapcache SHARED ${mapcache_SOURCES} ${mapcache_HEADERS})
set_target_properties(mapcache PROPERTIES
VERSION ${MAPCACHE_VERSION_STRING}
SOVERSION 1
@@ -93,6 +94,7 @@ option(WITH_GEOTIFF "Allow GeoTIFF metadata creation for TIFF cache backends" OF
option(WITH_PCRE "Use PCRE for regex tests" OFF)
option(WITH_MAPSERVER "Enable (experimental) support for the mapserver library" OFF)
option(WITH_RIAK "Use Riak as a cache backend" OFF)
+option(WITH_GDAL "Choose if GDAL raster support should be built in" ON)
find_package(PNG)
if(PNG_FOUND)
@@ -162,6 +164,17 @@ if(WITH_PIXMAN)
endif(PIXMAN_FOUND)
endif (WITH_PIXMAN)
+if(WITH_GDAL)
+ find_package(GDAL)
+ if(GDAL_FOUND)
+ include_directories(${GDAL_INCLUDE_DIR})
+ target_link_libraries(mapcache ${GDAL_LIBRARY})
+ set (USE_GDAL 1)
+ else(GDAL_FOUND)
+ report_optional_not_found(GDAL)
+ endif(GDAL_FOUND)
+endif(WITH_GDAL)
+
if(WITH_PCRE)
find_package(PCRE)
if(PCRE_FOUND)
@@ -300,6 +313,7 @@ status_optional_component("Experimental TIFF write support" "${USE_TIFF_WRITE}"
status_optional_component("PCRE" "${USE_PCRE}" "${PCRE_LIBRARY}")
status_optional_component("Experimental mapserver support" "${USE_MAPSERVER}" "${MAPSERVER_LIBRARY}")
status_optional_component("RIAK" "${USE_RIAK}" "${RIAK_LIBRARY}")
+status_optional_component("GDAL" "${USE_GDAL}" "${GDAL_LIBRARY}")
INSTALL(TARGETS mapcache DESTINATION ${CMAKE_INSTALL_LIBDIR})
diff --git a/MIGRATION_GUIDE.txt b/MIGRATION_GUIDE.txt
new file mode 100644
index 0000000..c6a998b
--- /dev/null
+++ b/MIGRATION_GUIDE.txt
@@ -0,0 +1,16 @@
+Migrating from Mapcache 1.4 to 1.6
+===================================
+
+* The <timedimension> tileset child has been removed. Time dimensions are now added with <dimension type="time">
+
+* <dimension type="values" ...>val1,val2,val3</dimension> should be replaced by
+ <dimension type="values"><value>val1</value><value>val2</value><value>val3</value></dimension>
+
+* <dimension type="values" case_sensitive="true">...</dimension> should be replaced by
+ <dimension type="values"><case_sensitive>true</case_sensitive>....</dimension>
+
+* <dimension type="regex" ...>^abc$</dimension> should be replaced by
+ <dimension type="regex"><regex>^abc$</regex></dimension>
+
+* <dimension ... assembly="stack">...</dimension> should be replaced by
+ <dimensions><assembly_type>stack</assembly_type><dimension ...>....</dimension></dimensions>
diff --git a/apache/mod_mapcache.c b/apache/mod_mapcache.c
index 832bad2..67db5bf 100644
--- a/apache/mod_mapcache.c
+++ b/apache/mod_mapcache.c
@@ -40,10 +40,6 @@
#include <apr_time.h>
#include <http_log.h>
#include "mapcache.h"
-#ifdef APR_HAS_THREADS
-#include <apr_thread_mutex.h>
-apr_thread_mutex_t *thread_mutex = NULL;
-#endif
#ifndef _WIN32
#include <unistd.h>
@@ -84,7 +80,9 @@ struct mapcache_alias_entry {
struct mapcache_server_cfg {
apr_array_header_t *aliases; /**< list of mapcache configurations aliased to a server uri */
+ apr_array_header_t *quickaliases; /**< list of mapcache configurations aliased to a server uri */
};
+static int mapcache_alias_matches(const char *uri, const char *alias_fakename);
void apache_context_server_log(mapcache_context *c, mapcache_log_level level, char *message, ...)
{
@@ -179,69 +177,28 @@ mapcache_context *mapcache_context_request_clone(mapcache_context *ctx)
return nctx;
}
-void init_apache_request_context(mapcache_context_apache_request *ctx)
-{
- mapcache_context_init((mapcache_context*)ctx);
- ctx->ctx.ctx.log = apache_context_request_log;
- ctx->ctx.ctx.clone = mapcache_context_request_clone;
-}
-
-void init_apache_server_context(mapcache_context_apache_server *ctx)
-{
- mapcache_context_init((mapcache_context*)ctx);
- ctx->ctx.ctx.log = apache_context_server_log;
-}
-
-static mapcache_context_apache_request* apache_request_context_create(request_rec *r)
+mapcache_context_apache_request* create_apache_request_context(request_rec *r)
{
- mapcache_context_apache_request *ctx = apr_pcalloc(r->pool, sizeof(mapcache_context_apache_request));
- mapcache_server_cfg *cfg = NULL;
- const char *mapcache_alias;
- mapcache_alias_entry *alias_entry;
- int i;
- mapcache_context *mctx = (mapcache_context*)ctx;
-
- mctx->pool = r->pool;
-#ifdef APR_HAS_THREADS
- mctx->threadlock = thread_mutex;
-#endif
-
- /* lookup the configuration object given the configuration file name */
- cfg = ap_get_module_config(r->server->module_config, &mapcache_module);
- if(!cfg || !cfg->aliases) {
- return NULL;
- }
-
- mapcache_alias = apr_table_get(r->notes,"mapcache_alias_entry");
- //ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "using mapcache config %s", mapcache_config_file);
- if(!mapcache_alias) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "mapcache module bug? no mapcache_alias_entry found");
- return NULL;
- }
-
- for(i=0; i<cfg->aliases->nelts; i++) {
- alias_entry = APR_ARRAY_IDX(cfg->aliases,i,mapcache_alias_entry*);
- if(strcmp(alias_entry->endpoint,mapcache_alias))
- continue;
-
- mctx->config = alias_entry->cfg;
- ctx->request = r;
- mctx->connection_pool = alias_entry->cp;
- init_apache_request_context(ctx);
- return ctx;
- }
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "mapcache module bug? no mapcache_alias_entry found for %s",mapcache_alias);
- return NULL;
+ mapcache_context_apache_request *rctx = apr_pcalloc(r->pool, sizeof(mapcache_context_apache_request));
+ mapcache_context *ctx = (mapcache_context*)rctx;
+ mapcache_context_init(ctx);
+ ctx->pool = r->pool;
+ rctx->request = r;
+ ctx->log = apache_context_request_log;
+ ctx->clone = mapcache_context_request_clone;
+ return rctx;
}
-static mapcache_context_apache_server* apache_server_context_create(server_rec *s, apr_pool_t *pool)
+static mapcache_context_apache_server* create_apache_server_context(server_rec *s, apr_pool_t *pool)
{
- mapcache_context_apache_server *ctx = apr_pcalloc(pool, sizeof(mapcache_context_apache_server));
- ctx->ctx.ctx.pool = pool;
- ctx->ctx.ctx.config = NULL;
- ctx->server = s;
- init_apache_server_context(ctx);
- return ctx;
+ mapcache_context_apache_server *actx = apr_pcalloc(pool, sizeof(mapcache_context_apache_server));
+ mapcache_context *ctx = (mapcache_context*)actx;
+ mapcache_context_init(ctx);
+ ctx->pool = pool;
+ ctx->config = NULL;
+ ctx->log = apache_context_server_log;
+ actx->server = s;
+ return actx;
}
/* read post body. code taken from "The apache modules book, Nick Kew" */
@@ -350,9 +307,6 @@ static int write_http_response(mapcache_context_apache_request *ctx, mapcache_ht
static void mod_mapcache_child_init(apr_pool_t *pool, server_rec *s)
{
-#ifdef APR_HAS_THREADS
- apr_thread_mutex_create(&thread_mutex,APR_THREAD_MUTEX_DEFAULT,pool);
-#endif
for( ; s ; s=s->next) {
mapcache_server_cfg* cfg = ap_get_module_config(s->module_config, &mapcache_module);
int i,rv;
@@ -364,43 +318,37 @@ static void mod_mapcache_child_init(apr_pool_t *pool, server_rec *s)
ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, "failed to create mapcache connection pool");
}
}
+ 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);
+ 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");
+ }
+ }
}
}
-static int mod_mapcache_request_handler(request_rec *r)
-{
+static int mapcache_handler(request_rec *r, mapcache_alias_entry *alias_entry) {
apr_table_t *params;
mapcache_request *request = NULL;
- mapcache_context_apache_request *apache_ctx = NULL;
+ mapcache_context_apache_request *apache_ctx = create_apache_request_context(r);
+ mapcache_context *ctx = (mapcache_context*)apache_ctx;
mapcache_http_response *http_response = NULL;
- mapcache_context *global_ctx = NULL;
-
- if (!r->handler || strcmp(r->handler, "mapcache")) {
- return DECLINED;
- }
- if (r->method_number != M_GET && r->method_number != M_POST) {
- return HTTP_METHOD_NOT_ALLOWED;
- }
-
-
- apache_ctx = apache_request_context_create(r);
-
- if(!apache_ctx) {
- return DECLINED;
- }
- global_ctx = (mapcache_context*)apache_ctx;
- global_ctx->supports_redirects = 1;
- global_ctx->headers_in = r->headers_in;
+ ctx->config = alias_entry->cfg;
+ ctx->connection_pool = alias_entry->cp;
+ ctx->supports_redirects = 1;
+ ctx->headers_in = r->headers_in;
- params = mapcache_http_parse_param_string(global_ctx, r->args);
+ params = mapcache_http_parse_param_string(ctx, r->args);
//ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "mapcache dispatch %s",r->path_info);
- mapcache_service_dispatch_request(global_ctx,&request,r->path_info,params,global_ctx->config);
- if(GC_HAS_ERROR(global_ctx) || !request) {
+ mapcache_service_dispatch_request(ctx,&request,r->path_info,params,ctx->config);
+ if(GC_HAS_ERROR(ctx) || !request) {
return write_http_response(apache_ctx,
- mapcache_core_respond_to_error(global_ctx));
+ mapcache_core_respond_to_error(ctx));
}
if(request->type == MAPCACHE_REQUEST_GET_CAPABILITIES) {
@@ -429,28 +377,28 @@ static int mod_mapcache_request_handler(request_rec *r)
*end = '\0';
}
}
- http_response = mapcache_core_get_capabilities(global_ctx,request->service,req_caps,
- url,original->path_info,global_ctx->config);
+ http_response = mapcache_core_get_capabilities(ctx,request->service,req_caps,
+ url,original->path_info,ctx->config);
} else if( request->type == MAPCACHE_REQUEST_GET_TILE) {
mapcache_request_get_tile *req_tile = (mapcache_request_get_tile*)request;
- http_response = mapcache_core_get_tile(global_ctx,req_tile);
+ http_response = mapcache_core_get_tile(ctx,req_tile);
} else if( request->type == MAPCACHE_REQUEST_PROXY ) {
const char *buf;
mapcache_request_proxy *req_proxy = (mapcache_request_proxy*)request;
if(r->method_number == M_POST) {
read_post_body(apache_ctx, req_proxy);
- if(GC_HAS_ERROR(global_ctx)) {
- return write_http_response(apache_ctx, mapcache_core_respond_to_error(global_ctx));
+ if(GC_HAS_ERROR(ctx)) {
+ return write_http_response(apache_ctx, mapcache_core_respond_to_error(ctx));
}
if(!req_proxy->headers) {
- req_proxy->headers = apr_table_make(global_ctx->pool, 2);
+ req_proxy->headers = apr_table_make(ctx->pool, 2);
}
apr_table_set(req_proxy->headers, "Content-Type", r->content_type);
if((buf = apr_table_get(r->headers_in,"X-Forwarded-For"))) {
#if (AP_SERVER_MAJORVERSION_NUMBER == 2) && (AP_SERVER_MINORVERSION_NUMBER < 4)
- apr_table_set(req_proxy->headers, "X-Forwarded-For", apr_psprintf(global_ctx->pool,"%s, %s", buf, r->connection->remote_ip));
+ apr_table_set(req_proxy->headers, "X-Forwarded-For", apr_psprintf(ctx->pool,"%s, %s", buf, r->connection->remote_ip));
#else
- apr_table_set(req_proxy->headers, "X-Forwarded-For", apr_psprintf(global_ctx->pool,"%s, %s", buf, r->connection->client_ip));
+ apr_table_set(req_proxy->headers, "X-Forwarded-For", apr_psprintf(ctx->pool,"%s, %s", buf, r->connection->client_ip));
#endif
} else {
#if (AP_SERVER_MAJORVERSION_NUMBER == 2) && (AP_SERVER_MINORVERSION_NUMBER < 4)
@@ -462,36 +410,98 @@ static int mod_mapcache_request_handler(request_rec *r)
if ((buf = apr_table_get(r->headers_in, "Host"))) {
const char *buf2;
if((buf2 = apr_table_get(r->headers_in,"X-Forwarded-Host"))) {
- apr_table_set(req_proxy->headers, "X-Forwarded-Host", apr_psprintf(global_ctx->pool,"%s, %s",buf2,buf));
+ apr_table_set(req_proxy->headers, "X-Forwarded-Host", apr_psprintf(ctx->pool,"%s, %s",buf2,buf));
} else {
apr_table_set(req_proxy->headers, "X-Forwarded-Host", buf);
}
}
if ((buf = apr_table_get(r->headers_in, "X-Forwarded-Server"))) {
- apr_table_set(req_proxy->headers, "X-Forwarded-Server", apr_psprintf(global_ctx->pool, "%s, %s", buf, r->server->server_hostname));
+ apr_table_set(req_proxy->headers, "X-Forwarded-Server", apr_psprintf(ctx->pool, "%s, %s", buf, r->server->server_hostname));
} else {
apr_table_set(req_proxy->headers, "X-Forwarded-Server", r->server->server_hostname);
}
}
- http_response = mapcache_core_proxy_request(global_ctx, req_proxy);
+ http_response = mapcache_core_proxy_request(ctx, req_proxy);
} else if( request->type == MAPCACHE_REQUEST_GET_MAP) {
mapcache_request_get_map *req_map = (mapcache_request_get_map*)request;
- http_response = mapcache_core_get_map(global_ctx,req_map);
+ http_response = mapcache_core_get_map(ctx,req_map);
} else if( request->type == MAPCACHE_REQUEST_GET_FEATUREINFO) {
mapcache_request_get_feature_info *req_fi = (mapcache_request_get_feature_info*)request;
- http_response = mapcache_core_get_featureinfo(global_ctx,req_fi);
+ http_response = mapcache_core_get_featureinfo(ctx,req_fi);
} else {
- global_ctx->set_error(global_ctx,500,"###BUG### unknown request type");
+ ctx->set_error(ctx,500,"###BUG### unknown request type");
}
- if(GC_HAS_ERROR(global_ctx)) {
+ if(GC_HAS_ERROR(ctx)) {
return write_http_response(apache_ctx,
- mapcache_core_respond_to_error(global_ctx));
+ mapcache_core_respond_to_error(ctx));
}
return write_http_response(apache_ctx,http_response);
}
+static int mod_mapcache_quick_handler(request_rec *r, int lookup) {
+ mapcache_server_cfg *sconfig = ap_get_module_config(r->server->module_config, &mapcache_module);
+ mapcache_alias_entry *alias_entry;
+ int i;
+
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "mapcache quick handler hook on uri %s",r->uri);
+
+ if (!sconfig || !sconfig->quickaliases)
+ return DECLINED;
+
+ if (r->uri[0] != '/' && r->uri[0])
+ return DECLINED;
+
+ if(lookup) {
+ return DECLINED;
+ }
+
+ /* loop through the entries to find one where the alias matches */
+ for(i=0; i<sconfig->quickaliases->nelts; i++) {
+ int l;
+ alias_entry = APR_ARRAY_IDX(sconfig->quickaliases,i,mapcache_alias_entry*);
+ //ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "cheking mapcache alias %s against %s",r->uri,alias_entry->endpoint);
+
+ if((l=mapcache_alias_matches(r->uri, alias_entry->endpoint))>0) {
+ if (r->method_number != M_GET && r->method_number != M_POST) {
+ return HTTP_METHOD_NOT_ALLOWED;
+ }
+ r->path_info = &(r->uri[l]);
+ return mapcache_handler(r,alias_entry);
+ }
+ }
+ return DECLINED;
+}
+
+static int mod_mapcache_request_handler(request_rec *r)
+{
+ const char *mapcache_alias;
+ int i;
+ mapcache_server_cfg* cfg;
+ if (!r->handler || strcmp(r->handler, "mapcache")) {
+ return DECLINED;
+ }
+ if (r->method_number != M_GET && r->method_number != M_POST) {
+ return HTTP_METHOD_NOT_ALLOWED;
+ }
+ cfg = ap_get_module_config(r->server->module_config, &mapcache_module);
+ mapcache_alias = apr_table_get(r->notes,"mapcache_alias_entry");
+ //ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "using mapcache config %s", mapcache_config_file);
+ if(!mapcache_alias) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "mapcache module bug? no mapcache_alias_entry found");
+ return DECLINED;
+ }
+
+ for(i=0; i<cfg->aliases->nelts; i++) {
+ mapcache_alias_entry *alias_entry = APR_ARRAY_IDX(cfg->aliases,i,mapcache_alias_entry*);
+ if(strcmp(alias_entry->endpoint,mapcache_alias))
+ continue;
+ return mapcache_handler(r,alias_entry);
+ }
+ return DECLINED; /*should never happen, the fixup phase would not have oriented us here*/
+}
+
static int mod_mapcache_post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
{
mapcache_server_cfg* cfg = ap_get_module_config(s->module_config, &mapcache_module);
@@ -586,6 +596,7 @@ static void mod_mapcache_register_hooks(apr_pool_t *p)
ap_hook_child_init(mod_mapcache_child_init, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_post_config(mod_mapcache_post_config, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_handler(mod_mapcache_request_handler, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_quick_handler(mod_mapcache_quick_handler, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_fixups(mapcache_hook_fixups, NULL, NULL, APR_HOOK_MIDDLE);
}
@@ -594,6 +605,7 @@ static void* mod_mapcache_create_server_conf(apr_pool_t *pool, server_rec *s)
{
mapcache_server_cfg *cfg = apr_pcalloc(pool, sizeof(mapcache_server_cfg));
cfg->aliases = apr_array_make(pool,1,sizeof(mapcache_alias_entry*));
+ cfg->quickaliases = apr_array_make(pool,1,sizeof(mapcache_alias_entry*));
return cfg;
}
@@ -605,6 +617,7 @@ static void *mod_mapcache_merge_server_conf(apr_pool_t *p, void *base_, void *vh
mapcache_server_cfg *cfg = apr_pcalloc(p,sizeof(mapcache_server_cfg));
cfg->aliases = apr_array_append(p, vhost->aliases,base->aliases);
+ cfg->quickaliases = apr_array_append(p, vhost->quickaliases,base->quickaliases);
#if 0
{
@@ -628,7 +641,7 @@ static void *mod_mapcache_merge_server_conf(apr_pool_t *p, void *base_, void *vh
return cfg;
}
-static const char* mapcache_add_alias(cmd_parms *cmd, void *cfg, const char *alias, const char* configfile)
+static const char* mapcache_add_alias(cmd_parms *cmd, void *cfg, const char *alias, const char* configfile, const char *quick)
{
mapcache_server_cfg *sconfig;
mapcache_alias_entry *alias_entry;
@@ -650,7 +663,7 @@ static const char* mapcache_add_alias(cmd_parms *cmd, void *cfg, const char *ali
return "no mapcache module config, server bug?";
alias_entry = apr_pcalloc(cmd->pool,sizeof(mapcache_alias_entry));
- ctx = (mapcache_context*)apache_server_context_create(cmd->server,cmd->pool);
+ ctx = (mapcache_context*)create_apache_server_context(cmd->server,cmd->pool);
alias_entry->cfg = mapcache_configuration_create(cmd->pool);
alias_entry->configfile = apr_pstrdup(cmd->pool,configfile);
@@ -663,8 +676,16 @@ static const char* mapcache_add_alias(cmd_parms *cmd, void *cfg, const char *ali
if(GC_HAS_ERROR(ctx)) {
return ctx->get_error_message(ctx);
}
- APR_ARRAY_PUSH(sconfig->aliases,mapcache_alias_entry*) = alias_entry;
- ap_log_error(APLOG_MARK, APLOG_INFO, 0, cmd->server, "loaded mapcache configuration file from %s on endpoint %s", alias_entry->configfile, alias_entry->endpoint);
+ if(mapcache_config_services_enabled(ctx, alias_entry->cfg) <= 0) {
+ return "no mapcache <service>s configured/enabled, no point in continuing.";
+ }
+ if(quick && !strcmp(quick,"quick")) {
+ APR_ARRAY_PUSH(sconfig->quickaliases,mapcache_alias_entry*) = alias_entry;
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, cmd->server, "loaded mapcache configuration file from %s on (quick) endpoint %s", alias_entry->configfile, alias_entry->endpoint);
+ } else {
+ APR_ARRAY_PUSH(sconfig->aliases,mapcache_alias_entry*) = alias_entry;
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, cmd->server, "loaded mapcache configuration file from %s on endpoint %s", alias_entry->configfile, alias_entry->endpoint);
+ }
return NULL;
}
@@ -672,7 +693,7 @@ static const char* mapcache_add_alias(cmd_parms *cmd, void *cfg, const char *ali
static const command_rec mod_mapcache_cmds[] = {
- AP_INIT_TAKE2("MapCacheAlias", mapcache_add_alias ,NULL,RSRC_CONF,"Aliased location of configuration file"),
+ AP_INIT_TAKE23("MapCacheAlias", mapcache_add_alias ,NULL,RSRC_CONF,"Aliased location of configuration file"),
{ NULL }
} ;
diff --git a/cgi/mapcache.c b/cgi/mapcache.c
index 75557e9..bb10ba4 100644
--- a/cgi/mapcache.c
+++ b/cgi/mapcache.c
@@ -200,6 +200,10 @@ static void load_config(mapcache_context *ctx, char *filename)
if(GC_HAS_ERROR(ctx)) goto failed_load;
mapcache_configuration_post_config(ctx, cfg);
if(GC_HAS_ERROR(ctx)) goto failed_load;
+ if(mapcache_config_services_enabled(ctx,cfg) <= 0) {
+ ctx->set_error(ctx,500,"no mapcache <service>s configured/enabled, no point in continuing.");
+ goto failed_load;
+ }
/* no error, destroy the previous pool if we are reloading the config */
if(config_pool) {
@@ -282,7 +286,6 @@ int main(int argc, const char **argv)
}
}
apr_pool_create(&(ctx->pool),config_pool);
- ctx->threadlock = NULL;
request = NULL;
pathInfo = getenv("PATH_INFO");
diff --git a/include/mapcache-config.h.in b/include/mapcache-config.h.in
index 805a394..4777b4c 100644
--- a/include/mapcache-config.h.in
+++ b/include/mapcache-config.h.in
@@ -12,9 +12,11 @@
#cmakedefine USE_PCRE 1
#cmakedefine USE_MAPSERVER 1
#cmakedefine USE_RIAK 1
+#cmakedefine USE_GDAL 1
#cmakedefine HAVE_STRNCASECMP 1
#cmakedefine HAVE_SYMLINK 1
-
+#cmakedefine HAVE_STRPTIME 1
+#cmakedefine HAVE_TIMEGM 1
#endif
diff --git a/include/mapcache.h b/include/mapcache.h
index 8f24326..2622e72 100644
--- a/include/mapcache.h
+++ b/include/mapcache.h
@@ -1,7 +1,6 @@
/******************************************************************************
- * $Id$
*
- * Project: MapServer
+ * Project: MapCache
* Author: Thomas Bonfort and the MapServer team.
*
******************************************************************************
@@ -46,34 +45,11 @@
#include "errors.h"
-#if 0
-#ifdef USE_GDAL
-#include <gdal.h>
-#include <cpl_conv.h>
-#endif
-#endif
#include <assert.h>
#include <time.h>
#include <apr_time.h>
-#ifdef USE_PCRE
-#include <pcre.h>
-#else
-#include <regex.h>
-#endif
-
-#ifdef USE_MEMCACHE
-#include <apr_memcache.h>
-#endif
-
-#ifdef USE_COUCHBASE
-#include <libcouchbase/couchbase.h>
-#endif
-
-#ifdef USE_RIAK
-#include <riack.h>
-#endif
#define MAPCACHE_SUCCESS 0
#define MAPCACHE_FAILURE 1
@@ -113,44 +89,16 @@ typedef struct mapcache_feature_info mapcache_feature_info;
typedef struct mapcache_request_get_feature_info mapcache_request_get_feature_info;
typedef struct mapcache_map mapcache_map;
typedef struct mapcache_http_response mapcache_http_response;
-typedef struct mapcache_source_wms mapcache_source_wms;
-#if 0
-typedef struct mapcache_source_gdal mapcache_source_gdal;
-#endif
-typedef struct mapcache_cache_disk mapcache_cache_disk;
-typedef struct mapcache_cache_composite mapcache_cache_composite;
-typedef struct mapcache_cache_fallback mapcache_cache_fallback;
-typedef struct mapcache_cache_multitier mapcache_cache_multitier;
-typedef struct mapcache_cache_rest mapcache_cache_rest;
-typedef struct mapcache_cache_s3 mapcache_cache_s3;
-typedef struct mapcache_cache_azure mapcache_cache_azure;
-typedef struct mapcache_cache_google mapcache_cache_google;
-#ifdef USE_TIFF
-typedef struct mapcache_cache_tiff mapcache_cache_tiff;
-#endif
typedef struct mapcache_http mapcache_http;
typedef struct mapcache_request mapcache_request;
typedef struct mapcache_request_image mapcache_request_image;
typedef struct mapcache_request_proxy mapcache_request_proxy;
typedef struct mapcache_request_get_capabilities mapcache_request_get_capabilities;
-typedef struct mapcache_request_get_capabilities_demo mapcache_request_get_capabilities_demo;
-typedef struct mapcache_request_get_capabilities_wms mapcache_request_get_capabilities_wms;
-typedef struct mapcache_request_get_capabilities_wmts mapcache_request_get_capabilities_wmts;
typedef struct mapcache_forwarding_rule mapcache_forwarding_rule;
-typedef struct mapcache_request_get_capabilities_tms mapcache_request_get_capabilities_tms;
-typedef struct mapcache_request_get_capabilities_kml mapcache_request_get_capabilities_kml;
typedef struct mapcache_request_get_tile mapcache_request_get_tile;
typedef struct mapcache_request_get_map mapcache_request_get_map;
typedef struct mapcache_service mapcache_service;
-typedef struct mapcache_service_wms mapcache_service_wms;
-typedef struct mapcache_service_wmts mapcache_service_wmts;
-typedef struct mapcache_service_gmaps mapcache_service_gmaps;
-typedef struct mapcache_service_ve mapcache_service_ve;
-typedef struct mapcache_service_tms mapcache_service_tms;
-typedef struct mapcache_service_kml mapcache_service_kml;
-typedef struct mapcache_service_mapguide mapcache_service_mapguide;
-typedef struct mapcache_service_demo mapcache_service_demo;
typedef struct mapcache_server_cfg mapcache_server_cfg;
typedef struct mapcache_image mapcache_image;
typedef struct mapcache_grid mapcache_grid;
@@ -158,17 +106,28 @@ typedef struct mapcache_grid_level mapcache_grid_level;
typedef struct mapcache_grid_link mapcache_grid_link;
typedef struct mapcache_context mapcache_context;
typedef struct mapcache_dimension mapcache_dimension;
-typedef struct mapcache_dimension_time mapcache_dimension_time;
-typedef struct mapcache_timedimension mapcache_timedimension;
-typedef struct mapcache_dimension_intervals mapcache_dimension_intervals;
-typedef struct mapcache_dimension_values mapcache_dimension_values;
-typedef struct mapcache_dimension_sqlite mapcache_dimension_sqlite;
-typedef struct mapcache_dimension_regex mapcache_dimension_regex;
+typedef struct mapcache_requested_dimension mapcache_requested_dimension;
typedef struct mapcache_extent mapcache_extent;
typedef struct mapcache_extent_i mapcache_extent_i;
typedef struct mapcache_connection_pool mapcache_connection_pool;
typedef struct mapcache_locker mapcache_locker;
+typedef enum {
+ MAPCACHE_DIMENSION_VALUES,
+ MAPCACHE_DIMENSION_REGEX,
+ MAPCACHE_DIMENSION_INTERVALS,
+ MAPCACHE_DIMENSION_TIME,
+ MAPCACHE_DIMENSION_SQLITE
+} mapcache_dimension_type;
+
+typedef enum {
+ MAPCACHE_DIMENSION_ASSEMBLY_NONE,
+ MAPCACHE_DIMENSION_ASSEMBLY_STACK,
+ MAPCACHE_DIMENSION_ASSEMBLY_ANIMATE
+} mapcache_dimension_assembly_type;
+
+
+
/** \defgroup utility Utility */
/** @{ */
@@ -207,7 +166,7 @@ struct mapcache_context {
void (*set_exception)(mapcache_context *ctx, char *key, char *message, ...);
/**
- * \brief query context to know if an error has occured
+ * \brief query context to know if an error has occurred
* \memberof mapcache_context
*/
int (*get_error)(mapcache_context * ctx);
@@ -247,7 +206,6 @@ struct mapcache_context {
mapcache_context* (*clone)(mapcache_context *ctx);
apr_pool_t *pool;
mapcache_connection_pool *connection_pool;
- void *threadlock;
char *_contenttype;
char *_errmsg;
int _errcode;
@@ -307,7 +265,8 @@ typedef enum {
MAPCACHE_SOURCE_WMS,
MAPCACHE_SOURCE_MAPSERVER,
MAPCACHE_SOURCE_DUMMY,
- MAPCACHE_SOURCE_GDAL
+ MAPCACHE_SOURCE_GDAL,
+ MAPCACHE_SOURCE_FALLBACK
} mapcache_source_type;
/**\interface mapcache_source
@@ -318,6 +277,8 @@ struct mapcache_source {
mapcache_extent data_extent; /**< extent in which this source can produce data */
mapcache_source_type type;
apr_table_t *metadata;
+ unsigned int retry_count;
+ double retry_delay;
apr_array_header_t *info_formats;
/**
@@ -325,11 +286,11 @@ struct mapcache_source {
*
* sets the mapcache_metatile::tile::data for the given tile
*/
- void (*render_map)(mapcache_context *ctx, mapcache_map *map);
+ void (*_render_map)(mapcache_context *ctx, mapcache_source *psource, mapcache_map *map);
- void (*query_info)(mapcache_context *ctx, mapcache_feature_info *fi);
+ void (*_query_info)(mapcache_context *ctx, mapcache_source *psource, mapcache_feature_info *fi);
- void (*configuration_parse_xml)(mapcache_context *ctx, ezxml_t xml, mapcache_source * source);
+ void (*configuration_parse_xml)(mapcache_context *ctx, ezxml_t xml, mapcache_source * source, mapcache_cfg *config);
void (*configuration_check)(mapcache_context *ctx, mapcache_cfg *cfg, mapcache_source * source);
};
@@ -346,81 +307,23 @@ struct mapcache_http {
/* TODO: authentication */
};
-/**\class mapcache_source_wms
- * \brief WMS mapcache_source
- * \implements mapcache_source
- */
-struct mapcache_source_wms {
- mapcache_source source;
- apr_table_t *wms_default_params; /**< default WMS parameters (SERVICE,REQUEST,STYLES,VERSION) */
- apr_table_t *getmap_params; /**< WMS parameters specified in configuration */
- apr_table_t *getfeatureinfo_params; /**< WMS parameters specified in configuration */
- mapcache_http *http;
-};
-
-#ifdef USE_MAPSERVER
-/**\class mapcache_source_mapserver
- * \brief WMS mapcache_source
- * \implements mapcache_source
- */
-typedef struct mapcache_source_mapserver mapcache_source_mapserver;
-struct mapcache_source_mapserver {
- mapcache_source source;
- char *mapfile;
-};
-#endif
-typedef struct mapcache_source_dummy mapcache_source_dummy;
-struct mapcache_source_dummy {
- mapcache_source source;
- char *mapfile;
- void *mapobj;
-};
-#if 0
-#ifdef USE_GDAL
-/**\class mapcache_source_gdal
- * \brief GDAL mapcache_source
- * \implements mapcache_source
- */
-struct mapcache_source_gdal {
- mapcache_source source;
- char *datastr; /**< the gdal source string*/
- apr_table_t *gdal_params; /**< GDAL parameters specified in configuration */
- GDALDatasetH *poDataset;
-};
-#endif
-/** @} */
-#endif
/** \defgroup cache Caches */
/** @{ */
typedef enum {
- MAPCACHE_CACHE_DISK,
- MAPCACHE_CACHE_REST
-#ifdef USE_MEMCACHE
+ MAPCACHE_CACHE_DISK
+ ,MAPCACHE_CACHE_REST
,MAPCACHE_CACHE_MEMCACHE
-#endif
-#ifdef USE_SQLITE
,MAPCACHE_CACHE_SQLITE
-#endif
-#ifdef USE_BDB
,MAPCACHE_CACHE_BDB
-#endif
-#ifdef USE_TC
,MAPCACHE_CACHE_TC
-#endif
-#ifdef USE_TIFF
,MAPCACHE_CACHE_TIFF
-#endif
,MAPCACHE_CACHE_COMPOSITE
-#ifdef USE_COUCHBASE
,MAPCACHE_CACHE_COUCHBASE
-#endif
-#ifdef USE_RIAK
,MAPCACHE_CACHE_RIAK
-#endif
} mapcache_cache_type;
/** \interface mapcache_cache
@@ -430,293 +333,77 @@ struct mapcache_cache {
char *name; /**< key this cache is referenced by */
mapcache_cache_type type;
apr_table_t *metadata;
+ unsigned int retry_count;
+ double retry_delay;
+
/**
* get tile content from cache
- * \returns MAPCACHE_SUCCESS if the data was correctly loaded from the disk
+ * \returns MAPCACHE_SUCCESS if the data was correctly loaded from the cache
* \returns MAPCACHE_FAILURE if the file exists but contains no data
- * \returns MAPCACHE_CACHE_MISS if the file does not exist on the disk
+ * \returns MAPCACHE_CACHE_MISS if the file does not exist on the cache
* \memberof mapcache_cache
*/
- int (*tile_get)(mapcache_context *ctx, mapcache_cache *cache, mapcache_tile * tile);
+ int (*_tile_get)(mapcache_context *ctx, mapcache_cache *cache, mapcache_tile * tile);
/**
* delete tile from cache
*
* \memberof mapcache_cache
*/
- void (*tile_delete)(mapcache_context *ctx, mapcache_cache *cache, mapcache_tile * tile);
+ void (*_tile_delete)(mapcache_context *ctx, mapcache_cache *cache, mapcache_tile * tile);
- int (*tile_exists)(mapcache_context *ctx, mapcache_cache *cache, mapcache_tile * tile);
+ int (*_tile_exists)(mapcache_context *ctx, mapcache_cache *cache, mapcache_tile * tile);
/**
* set tile content to cache
* \memberof mapcache_cache
*/
- void (*tile_set)(mapcache_context *ctx, mapcache_cache *cache, mapcache_tile * tile);
- void (*tile_multi_set)(mapcache_context *ctx, mapcache_cache *cache, mapcache_tile *tiles, int ntiles);
+ void (*_tile_set)(mapcache_context *ctx, mapcache_cache *cache, mapcache_tile * tile);
+ void (*_tile_multi_set)(mapcache_context *ctx, mapcache_cache *cache, mapcache_tile *tiles, int ntiles);
void (*configuration_parse_xml)(mapcache_context *ctx, ezxml_t xml, mapcache_cache * cache, mapcache_cfg *config);
void (*configuration_post_config)(mapcache_context *ctx, mapcache_cache * cache, mapcache_cfg *config);
};
-/**\class mapcache_cache_disk
- * \brief a mapcache_cache on a filesytem
- * \implements mapcache_cache
- */
-struct mapcache_cache_disk {
- mapcache_cache cache;
- char *base_directory;
- char *filename_template;
- int symlink_blank;
- int creation_retry;
-
- /**
- * Set filename for a given tile
- * \memberof mapcache_cache_disk
- */
- void (*tile_key)(mapcache_context *ctx, mapcache_cache_disk *cache, mapcache_tile *tile, char **path);
-};
-
-typedef struct mapcache_cache_composite_cache_link mapcache_cache_composite_cache_link;
-struct mapcache_cache_composite_cache_link {
- mapcache_cache *cache;
- int minzoom;
- int maxzoom;
- apr_array_header_t *grids;
- apr_array_header_t *dimensions; //TODO
-};
-
-struct mapcache_cache_composite {
- mapcache_cache cache;
- apr_array_header_t *cache_links;
-};
-
-struct mapcache_cache_fallback {
- mapcache_cache cache;
- apr_array_header_t *caches;
-};
-
-struct mapcache_cache_multitier {
- mapcache_cache cache;
- apr_array_header_t *caches;
-};
-
-
-typedef enum {
- MAPCACHE_REST_METHOD_GET,
- MAPCACHE_REST_METHOD_HEAD,
- MAPCACHE_REST_METHOD_PUT,
- MAPCACHE_REST_METHOD_POST,
- MAPCACHE_REST_METHOD_DELETE
-} mapcache_rest_method;
-
-typedef enum {
- MAPCACHE_REST_PROVIDER_NONE,
- MAPCACHE_REST_PROVIDER_S3,
- MAPCACHE_REST_PROVIDER_AZURE,
- MAPCACHE_REST_PROVIDER_GOOGLE
-} mapcache_rest_provider;
-
-void sha256(const unsigned char *message, unsigned int len, unsigned char *digest);
-void hmac_sha256(const unsigned char *message, unsigned int message_len,
- const unsigned char *key, unsigned int key_size,
- unsigned char *mac, unsigned mac_size);
-void hmac_sha1(const char *message, unsigned int message_len,
- const unsigned char *key, unsigned int key_size,
- void *mac);
-void sha_hex_encode(unsigned char *sha, unsigned int sha_size);
-char *base64_encode(apr_pool_t *pool, const unsigned char *data, size_t input_length);
-
-typedef struct mapcache_rest_operation mapcache_rest_operation;
-struct mapcache_rest_operation {
- apr_table_t *headers;
- mapcache_rest_method method;
- char *tile_url;
- void (*add_headers)(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers);
-};
-
-typedef struct mapcache_rest_configuration mapcache_rest_configuration;
-struct mapcache_rest_configuration {
- apr_table_t *common_headers;
- char *tile_url;
- mapcache_rest_operation has_tile;
- mapcache_rest_operation get_tile;
- mapcache_rest_operation set_tile;
- mapcache_rest_operation multi_set_tile;
- mapcache_rest_operation delete_tile;
- void (*add_headers)(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers);
-};
-
-/**\class mapcache_cache_rest
- * \brief a mapcache_cache on a 3rd party HTTP Rest API
- * \implements mapcache_cache
- */
-struct mapcache_cache_rest {
- mapcache_cache cache;
- mapcache_rest_configuration rest;
- int use_redirects;
- int retry_count;
- mapcache_rest_provider provider;
-};
-
-struct mapcache_cache_s3 {
- mapcache_cache_rest cache;
- char *id;
- char *secret;
- char *region;
-};
-
-struct mapcache_cache_azure {
- mapcache_cache_rest cache;
- char *id;
- char *secret;
- char *container;
-};
-
-struct mapcache_cache_google {
- mapcache_cache_rest cache;
- char *access;
- char *secret;
-};
-
-#ifdef USE_TIFF
-struct mapcache_cache_tiff {
- mapcache_cache cache;
- char *filename_template;
- 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;
- int count_x;
- int count_y;
- mapcache_image_format_jpeg *format;
- mapcache_locker *locker;
-};
-#endif
+MS_DLL_EXPORT int mapcache_cache_tile_get(mapcache_context *ctx, mapcache_cache *cache, mapcache_tile *tile);
+void mapcache_cache_tile_delete(mapcache_context *ctx, mapcache_cache *cache, mapcache_tile *tile);
+MS_DLL_EXPORT int mapcache_cache_tile_exists(mapcache_context *ctx, mapcache_cache *cache, mapcache_tile *tile);
+MS_DLL_EXPORT void mapcache_cache_tile_set(mapcache_context *ctx, mapcache_cache *cache, mapcache_tile *tile);
+void mapcache_cache_tile_multi_set(mapcache_context *ctx, mapcache_cache *cache, mapcache_tile *tiles, int ntiles);
-#ifdef USE_SQLITE
-/**\class mapcache_cache_sqlite
- * \brief a mapcache_cache on a filesytem
- * \implements mapcache_cache
- */
-typedef struct mapcache_cache_sqlite mapcache_cache_sqlite;
-typedef struct mapcache_cache_sqlite_stmt mapcache_cache_sqlite_stmt;
-struct mapcache_cache_sqlite_stmt {
- char *sql;
-};
-
-struct sqlite_conn;
-
-struct mapcache_cache_sqlite {
- mapcache_cache cache;
- char *dbfile;
- mapcache_cache_sqlite_stmt create_stmt;
- mapcache_cache_sqlite_stmt exists_stmt;
- mapcache_cache_sqlite_stmt get_stmt;
- mapcache_cache_sqlite_stmt set_stmt;
- mapcache_cache_sqlite_stmt delete_stmt;
- apr_table_t *pragmas;
- 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;
- int count_x, count_y;
-};
/**
* \memberof mapcache_cache_sqlite
*/
mapcache_cache* mapcache_cache_sqlite_create(mapcache_context *ctx);
mapcache_cache* mapcache_cache_mbtiles_create(mapcache_context *ctx);
-#endif
-
-#ifdef USE_BDB
-typedef struct mapcache_cache_bdb mapcache_cache_bdb;
-struct mapcache_cache_bdb {
- mapcache_cache cache;
- char *basedir;
- char *key_template;
-};
-mapcache_cache *mapcache_cache_bdb_create(mapcache_context *ctx);
-#endif
-
-#ifdef USE_TC
-typedef struct mapcache_cache_tc mapcache_cache_tc;
-struct mapcache_cache_tc {
- mapcache_cache cache;
- char *basedir;
- char *key_template;
- mapcache_context *ctx;
-};
-mapcache_cache *mapcache_cache_tc_create(mapcache_context *ctx);
-#endif
-#ifdef USE_MEMCACHE
-typedef struct mapcache_cache_memcache mapcache_cache_memcache;
-/**\class mapcache_cache_memcache
- * \brief a mapcache_cache on memcached servers
- * \implements mapcache_cache
+/**
+ * \memberof mapcache_cache_bdb
*/
-
-struct mapcache_cache_memcache_server {
- char* host;
- int port;
-};
-
-struct mapcache_cache_memcache {
- mapcache_cache cache;
- int nservers;
- struct mapcache_cache_memcache_server *servers;
- int detect_blank;
-};
+mapcache_cache *mapcache_cache_bdb_create(mapcache_context *ctx);
/**
* \memberof mapcache_cache_memcache
*/
mapcache_cache* mapcache_cache_memcache_create(mapcache_context *ctx);
-#endif
-
-#ifdef USE_COUCHBASE
-typedef struct mapcache_cache_couchbase mapcache_cache_couchbase;
-
-/**\class mapcache_cache_couchbase
- * \brief a mapcache_cache on couchbase servers
- * \implements mapcache_cache
- */
-struct mapcache_cache_couchbase {
- mapcache_cache cache;
-// apr_reslist_t *connection_pool;
- char *host;
- char *username;
- char *password;
- char *bucket;
- mapcache_context *ctx;
-};
/**
* \memberof mapcache_cache_couchbase
*/
mapcache_cache* mapcache_cache_couchbase_create(mapcache_context *ctx);
-#endif
-#ifdef USE_RIAK
-typedef struct mapcache_cache_riak mapcache_cache_riak;
-
-/**\class mapcache_cache_riak
- * \brief a mapcache_cache for riak servers
- * \implements mapcache_cache
+/**
+ * \memberof mapcache_cache_tc
*/
-struct mapcache_cache_riak {
- mapcache_cache cache;
- char *host;
- int port;
- RIACK_STRING bucket;
-};
+mapcache_cache* mapcache_cache_tc_create(mapcache_context *ctx);
/**
* \memberof mapcache_cache_riak
*/
mapcache_cache* mapcache_cache_riak_create(mapcache_context *ctx);
-#endif
/** @} */
@@ -783,7 +470,7 @@ struct mapcache_http_response {
struct mapcache_map {
mapcache_tileset *tileset;
mapcache_grid_link *grid_link;
- apr_table_t *dimensions;
+ apr_array_header_t *dimensions;
mapcache_buffer *encoded_data;
mapcache_image *raw_image;
int nodata; /**< \sa mapcache_tile::nodata */
@@ -827,36 +514,7 @@ struct mapcache_request_get_capabilities {
char *mime_type;
};
-struct mapcache_request_get_capabilities_tms {
- mapcache_request_get_capabilities request;
- mapcache_tileset *tileset;
- mapcache_grid_link *grid_link;
- char *version;
-};
-struct mapcache_request_get_capabilities_kml {
- mapcache_request_get_capabilities request;
- mapcache_tile *tile;
- mapcache_tileset *tileset;
- mapcache_grid_link *grid;
-};
-
-struct mapcache_request_get_capabilities_wms {
- mapcache_request_get_capabilities request;
-};
-
-struct mapcache_request_get_capabilities_wmts {
- mapcache_request_get_capabilities request;
-};
-
-/**
- * the capabilities request for a specific service, to be able to create
- * demo pages specific to a given service
- */
-struct mapcache_request_get_capabilities_demo {
- mapcache_request_get_capabilities request;
- mapcache_service *service;
-};
struct mapcache_forwarding_rule {
char *name;
@@ -932,67 +590,9 @@ struct mapcache_service {
char **err_body, apr_table_t *headers);
};
-/**\class mapcache_service_wms
- * \brief an OGC WMS service
- * \implements mapcache_service
- */
-struct mapcache_service_wms {
- mapcache_service service;
- int maxsize;
- apr_array_header_t *forwarding_rules;
- mapcache_getmap_strategy getmap_strategy;
- mapcache_resample_mode resample_mode;
- mapcache_image_format *getmap_format;
- int allow_format_override; /* can the client specify which image format should be returned */
-};
-
-/**\class mapcache_service_kml
- * \brief a KML superoverlay service
- * \implements mapcache_service
- */
-struct mapcache_service_kml {
- mapcache_service service;
-};
-
-/**\class mapcache_service_tms
- * \brief a TMS service
- * \implements mapcache_service
- */
-struct mapcache_service_tms {
- mapcache_service service;
- int reverse_y;
-};
-
-struct mapcache_service_mapguide {
- mapcache_service service;
- int rows_per_folder;
- int cols_per_folder;
-};
-/**\class mapcache_service_wmts
- * \brief a WMTS service
- * \implements mapcache_service
- */
-struct mapcache_service_wmts {
- mapcache_service service;
-};
-/**\class mapcache_service_demo
- * \brief a demo service
- * \implements mapcache_service
- */
-struct mapcache_service_demo {
- mapcache_service service;
-};
-
-/**\class mapcache_service_ve
- * \brief a virtualearth service
- * \implements mapcache_service
- */
-struct mapcache_service_ve {
- mapcache_service service;
-};
/**
* \brief create and initialize a mapcache_service_wms
@@ -1050,6 +650,10 @@ MS_DLL_EXPORT void mapcache_service_dispatch_request(mapcache_context *ctx,
char *pathinfo,
apr_table_t *params,
mapcache_cfg *config);
+/**
+ * \brief return the number of enabled/configured services
+ */
+MS_DLL_EXPORT int mapcache_config_services_enabled(mapcache_context *ctx, mapcache_cfg *config);
/** @} */
@@ -1140,7 +744,7 @@ int mapcache_image_blank_color(mapcache_image* image);
/**
* \brief check if image has some non opaque pixels
*/
-int mapcache_image_has_alpha(mapcache_image *img);
+int mapcache_image_has_alpha(mapcache_image *img, unsigned int cutoff);
void mapcache_image_fill(mapcache_context *ctx, mapcache_image *image, const unsigned char *fill_color);
@@ -1182,8 +786,8 @@ typedef enum {
struct mapcache_locker{
mapcache_lock_result (*aquire_lock)(mapcache_context *ctx, mapcache_locker *self, char *resource, void **lock);
- mapcache_lock_result (*ping_lock)(mapcache_context *ctx, mapcache_locker *self, char *resource, void *lock);
- void (*release_lock)(mapcache_context *ctx, mapcache_locker *self, char *resource, void *lock);
+ mapcache_lock_result (*ping_lock)(mapcache_context *ctx, mapcache_locker *self, void *lock);
+ void (*release_lock)(mapcache_context *ctx, mapcache_locker *self, void *lock);
void (*parse_xml)(mapcache_context *ctx, mapcache_locker *self, ezxml_t node);
mapcache_lock_mode type;
@@ -1191,43 +795,14 @@ struct mapcache_locker{
double retry_interval; /* time to wait before checking again on a lock, in seconds */
};
-typedef struct {
- mapcache_locker locker;
-
- /**
- * directory where lock files will be placed.
- * Must be readable and writable by the apache user.
- * Must be placed on a network mounted shared directory if multiple mapcache instances
- * need to be synchronized
- */
- const char *dir;
-} mapcache_locker_disk;
-
mapcache_locker* mapcache_locker_disk_create(mapcache_context *ctx);
-#ifdef USE_MEMCACHE
-typedef struct {
- char *host;
- int port;
-} mapcache_locker_memcache_server;
-
-typedef struct {
- mapcache_locker locker;
- int nservers;
- mapcache_locker_memcache_server *servers;
-} mapcache_locker_memcache;
-
mapcache_locker* mapcache_locker_memcache_create(mapcache_context *ctx);
-#endif
-
-typedef struct {
- mapcache_locker locker;
- apr_array_header_t *lockers;
-} mapcache_locker_fallback;
mapcache_locker* mapcache_locker_fallback_create(mapcache_context *ctx);
void mapcache_config_parse_locker(mapcache_context *ctx, ezxml_t node, mapcache_locker **locker);
+void mapcache_config_parse_locker_old(mapcache_context *ctx, ezxml_t doc, mapcache_cfg *config);
/**
* a configuration that will be served
@@ -1330,21 +905,31 @@ void mapcache_configuration_add_cache(mapcache_cfg *config, mapcache_cache *cach
void mapcache_source_init(mapcache_context *ctx, mapcache_source *source);
/**
+ * \memberof mapcache_source
+ */
+void mapcache_source_render_map(mapcache_context *ctx, mapcache_source *source, mapcache_map *map);
+void mapcache_source_query_info(mapcache_context *ctx, mapcache_source *source,
+ mapcache_feature_info *fi);
+
+/**
* \memberof mapcache_source_gdal
*/
mapcache_source* mapcache_source_gdal_create(mapcache_context *ctx);
/**
+ * \memberof mapcache_source_fallback
+ */
+mapcache_source* mapcache_source_fallback_create(mapcache_context *ctx);
+
+/**
* \memberof mapcache_source_wms
*/
mapcache_source* mapcache_source_wms_create(mapcache_context *ctx);
-#ifdef USE_MAPSERVER
/**
* \memberof mapcache_source_wms
*/
mapcache_source* mapcache_source_mapserver_create(mapcache_context *ctx);
-#endif
mapcache_source* mapcache_source_dummy_create(mapcache_context *ctx);
@@ -1361,12 +946,10 @@ mapcache_cache* mapcache_cache_s3_create(mapcache_context *ctx);
mapcache_cache* mapcache_cache_azure_create(mapcache_context *ctx);
mapcache_cache* mapcache_cache_google_create(mapcache_context *ctx);
-#ifdef USE_TIFF
/**
* \memberof mapcache_cache_tiff
*/
mapcache_cache* mapcache_cache_tiff_create(mapcache_context *ctx);
-#endif
mapcache_cache* mapcache_cache_composite_create(mapcache_context *ctx);
mapcache_cache* mapcache_cache_fallback_create(mapcache_context *ctx);
@@ -1400,7 +983,8 @@ struct mapcache_tile {
apr_time_t mtime; /**< last modification time */
int expires; /**< time in seconds after which the tile should be rechecked for validity */
- apr_table_t *dimensions;
+ apr_array_header_t *dimensions;
+
/**
* flag stating the tile is empty (i.e. fully transparent).
* if set, this indicates that there was no error per se, but that there was
@@ -1531,6 +1115,7 @@ struct mapcache_tileset {
int auto_expire;
int read_only;
+ int subdimension_read_only;
/**
* the cache in which the tiles should be stored
@@ -1552,7 +1137,9 @@ struct mapcache_tileset {
*/
apr_array_header_t *dimensions;
- mapcache_timedimension *timedimension;
+ int store_dimension_assemblies;/**< should multiple sub-dimensions be assembled dynamically (per-request) or should they be cached once assembled */
+
+ mapcache_dimension_assembly_type dimension_assembly_type;
/**
* image to be used as a watermark
@@ -1599,6 +1186,9 @@ int mapcache_grid_get_cell(mapcache_context *ctx, mapcache_grid *grid, mapcache_
* @return
*/
void mapcache_tileset_tile_validate(mapcache_context *ctx, mapcache_tile *tile);
+void mapcache_tileset_tile_validate_z(mapcache_context *ctx, mapcache_tile *tile);
+void mapcache_tileset_tile_validate_x(mapcache_context *ctx, mapcache_tile *tile);
+void mapcache_tileset_tile_validate_y(mapcache_context *ctx, mapcache_tile *tile);
/**
* compute level for a given resolution
@@ -1614,6 +1204,7 @@ void mapcache_tileset_get_level(mapcache_context *ctx, mapcache_tileset *tileset
mapcache_grid_link* mapcache_grid_get_closest_wms_level(mapcache_context *ctx, mapcache_grid_link *grid, double resolution, int *level);
MS_DLL_EXPORT void mapcache_tileset_tile_get(mapcache_context *ctx, mapcache_tile *tile);
+MS_DLL_EXPORT void mapcache_tileset_tile_set_get_with_subdimensions(mapcache_context *ctx, mapcache_tile *tile);
/**
* \brief delete tile from cache
@@ -1664,7 +1255,7 @@ void mapcache_tileset_add_watermark(mapcache_context *ctx, mapcache_tileset *til
MS_DLL_EXPORT int mapcache_lock_or_wait_for_resource(mapcache_context *ctx, mapcache_locker *locker, char *resource, void **lock);
-MS_DLL_EXPORT void mapcache_unlock_resource(mapcache_context *ctx, mapcache_locker *locker, char *resource, void *lock);
+MS_DLL_EXPORT void mapcache_unlock_resource(mapcache_context *ctx, mapcache_locker *locker, void *lock);
MS_DLL_EXPORT mapcache_metatile* mapcache_tileset_metatile_get(mapcache_context *ctx, mapcache_tile *tile);
MS_DLL_EXPORT void mapcache_tileset_render_metatile(mapcache_context *ctx, mapcache_metatile *mt);
@@ -1692,8 +1283,11 @@ mapcache_grid* mapcache_grid_create(apr_pool_t *pool);
const char* mapcache_grid_get_crs(mapcache_context *ctx, mapcache_grid *grid);
const char* mapcache_grid_get_srs(mapcache_context *ctx, mapcache_grid *grid);
-MS_DLL_EXPORT void mapcache_grid_get_extent(mapcache_context *ctx, mapcache_grid *grid,
+MS_DLL_EXPORT void mapcache_grid_get_tile_extent(mapcache_context *ctx, mapcache_grid *grid,
int x, int y, int z, mapcache_extent *bbox);
+
+MS_DLL_EXPORT void mapcache_grid_get_metatile_extent(mapcache_context *ctx, mapcache_tile *tile, mapcache_extent *bbox);
+
/**
* \brief compute x y value for given lon/lat (dx/dy) and given zoomlevel
* @param ctx
@@ -1733,6 +1327,9 @@ MS_DLL_EXPORT int mapcache_util_extract_double_list(mapcache_context *ctx, const
int *numbers_count);
char *mapcache_util_str_replace(apr_pool_t *pool, const char *string, const char *substr,
const char *replacement );
+void mapcache_util_quadkey_decode(mapcache_context *ctx, const char *quadkey, int *x, int *y, int *z);
+
+char* mapcache_util_quadkey_encode(mapcache_context *ctx, int x, int y, int z);
/**
* \brief replace dangerous characters in string
@@ -1771,6 +1368,15 @@ typedef enum {
MAPCACHE_PHOTOMETRIC_YCBCR
} mapcache_photometric;
+/**
+ * optimization settings (mostly) for jpeg
+ */
+typedef enum {
+ MAPCACHE_OPTIMIZE_NO,
+ MAPCACHE_OPTIMIZE_YES,
+ MAPCACHE_OPTIMIZE_ARITHMETIC
+} mapcache_optimization;
+
/**\interface mapcache_image_format
* \brief an image format
* \sa mapcache_image_format_jpeg
@@ -1809,13 +1415,14 @@ struct mapcache_image_format_mixed {
mapcache_image_format format;
mapcache_image_format *transparent;
mapcache_image_format *opaque;
+ unsigned int alpha_cutoff; /* default 255. pixels with alpha >= alpha_cutoff will be considered fully opaque */
};
mapcache_buffer* mapcache_empty_png_decode(mapcache_context *ctx, int width, int height, const unsigned char *hex_color, int *is_empty);
mapcache_image_format* mapcache_imageio_create_mixed_format(apr_pool_t *pool,
- char *name, mapcache_image_format *transparent, mapcache_image_format *opaque);
+ char *name, mapcache_image_format *transparent, mapcache_image_format *opaque, unsigned int alpha_cutoff);
/**\class mapcache_image_format_png_q
* \brief Quantized PNG format
@@ -1878,10 +1485,11 @@ struct mapcache_image_format_jpeg {
mapcache_image_format format;
int quality; /**< JPEG quality, 1-100 */
mapcache_photometric photometric;
+ mapcache_optimization optimize;
};
mapcache_image_format* mapcache_imageio_create_jpeg_format(apr_pool_t *pool, char *name, int quality,
- mapcache_photometric photometric);
+ mapcache_photometric photometric, mapcache_optimization optimize);
/**
* @param r
@@ -1930,13 +1538,19 @@ typedef struct {
double resolution;
} mapcache_interval;
-typedef enum {
- MAPCACHE_DIMENSION_VALUES,
- MAPCACHE_DIMENSION_REGEX,
- MAPCACHE_DIMENSION_INTERVALS,
- MAPCACHE_DIMENSION_TIME,
- MAPCACHE_DIMENSION_SQLITE
-} mapcache_dimension_type;
+struct mapcache_requested_dimension {
+ mapcache_dimension *dimension;
+ char *requested_value;
+ char *cached_value;
+};
+
+void mapcache_tile_set_cached_dimension(mapcache_context *ctx, mapcache_tile *tile, const char *name, const char *value);
+void mapcache_map_set_cached_dimension(mapcache_context *ctx, mapcache_map *map, const char *name, const char *value);
+void mapcache_tile_set_requested_dimension(mapcache_context *ctx, mapcache_tile *tile, const char *name, const char *value);
+void mapcache_map_set_requested_dimension(mapcache_context *ctx, mapcache_map *map, const char *name, const char *value);
+void mapcache_set_requested_dimension(mapcache_context *ctx, apr_array_header_t *dimensions, const char *name, const char *value);
+void mapcache_set_cached_dimension(mapcache_context *ctx, apr_array_header_t *dimensions, const char *name, const char *value);
+MS_DLL_EXPORT apr_array_header_t *mapcache_requested_dimensions_clone(apr_pool_t *pool, apr_array_header_t *src);
struct mapcache_dimension {
mapcache_dimension_type type;
@@ -1944,24 +1558,24 @@ struct mapcache_dimension {
char *unit;
apr_table_t *metadata;
char *default_value;
- int skip_validation;
/**
- * \brief validate the given value
- *
- * \param value is updated in case the given value is correct but has to be represented otherwise,
- * e.g. to round off a value
- * \returns MAPCACHE_SUCCESS if the given value is correct for the current dimension
- * \returns MAPCACHE_FAILURE if not
+ * \brief return the list of dimension values that match the requested entry
*/
- int (*validate)(mapcache_context *context, mapcache_dimension *dimension, char **value);
+ apr_array_header_t* (*get_entries_for_value)(mapcache_context *ctx, mapcache_dimension *dimension, const char *value,
+ mapcache_tileset *tileset, mapcache_extent *extent, mapcache_grid *grid);
/**
- * \brief returns a list of values that are authorized for this dimension
- *
- * \returns a list of character strings that will be included in the capabilities <dimension> element
+ * \brief return all possible values
+ */
+ apr_array_header_t* (*get_all_entries)(mapcache_context *ctx, mapcache_dimension *dimension,
+ mapcache_tileset *tileset, mapcache_extent *extent, mapcache_grid *grid);
+
+ /**
+ * \brief return all possible values formatted in a way compatible with OGC capabilities <dimension> element
*/
- apr_array_header_t* (*print_ogc_formatted_values)(mapcache_context *context, mapcache_dimension *dimension);
+ apr_array_header_t* (*get_all_ogc_formatted_entries)(mapcache_context *ctx, mapcache_dimension *dimension,
+ mapcache_tileset *tileset, mapcache_extent *extent, mapcache_grid *grid);
/**
* \brief parse the value given in the configuration
@@ -1969,79 +1583,10 @@ struct mapcache_dimension {
void (*configuration_parse_xml)(mapcache_context *context, mapcache_dimension *dim, ezxml_t node);
};
-struct mapcache_dimension_values {
- mapcache_dimension dimension;
- int nvalues;
- char **values;
- int case_sensitive;
-};
-
-struct mapcache_dimension_sqlite {
- mapcache_dimension dimension;
- char *dbfile;
- char *validate_query;
- char *list_query;
-};
-
-struct mapcache_dimension_regex {
- mapcache_dimension dimension;
- char *regex_string;
-#ifdef USE_PCRE
- pcre *pcregex;
-#else
- regex_t *regex;
-#endif
-};
-
-struct mapcache_dimension_intervals {
- mapcache_dimension dimension;
- int nintervals;
- mapcache_interval *intervals;
-};
-
-struct mapcache_dimension_time {
- mapcache_dimension dimension;
- int nintervals;
- mapcache_interval *intervals;
-};
-
-mapcache_dimension* mapcache_dimension_values_create(apr_pool_t *pool);
-mapcache_dimension* mapcache_dimension_sqlite_create(apr_pool_t *pool);
-mapcache_dimension* mapcache_dimension_regex_create(apr_pool_t *pool);
-mapcache_dimension* mapcache_dimension_intervals_create(apr_pool_t *pool);
-mapcache_dimension* mapcache_dimension_time_create(apr_pool_t *pool);
-
-typedef enum {
- MAPCACHE_TIMEDIMENSION_ASSEMBLY_STACK,
- MAPCACHE_TIMEDIMENSION_ASSEMBLY_ANIMATE
-} mapcache_timedimension_assembly_type;
-
-typedef enum {
- MAPCACHE_TIMEDIMENSION_SOURCE_SQLITE
-} mapcache_timedimension_source_type;
-
-MS_DLL_EXPORT apr_array_header_t* mapcache_timedimension_get_entries_for_value(mapcache_context *ctx, mapcache_timedimension *timedimesnion,
- mapcache_tileset *tileset, mapcache_grid *grid, mapcache_extent *extent, const char *value);
-
-struct mapcache_timedimension {
- mapcache_timedimension_assembly_type assembly_type;
- void (*configuration_parse_xml)(mapcache_context *context, mapcache_timedimension *dim, ezxml_t node);
- apr_array_header_t* (*get_entries_for_interval)(mapcache_context *ctx, mapcache_timedimension *dim, mapcache_tileset *tileset,
- mapcache_grid *grid, mapcache_extent *extent, time_t start, time_t end);
- apr_array_header_t* (*get_all_entries)(mapcache_context *ctx, mapcache_timedimension *dim, mapcache_tileset *tileset);
- char *default_value;
- char *key; /* TIME, hardcoded */
-};
-
-#ifdef USE_SQLITE
-typedef struct mapcache_timedimension_sqlite mapcache_timedimension_sqlite;
-struct mapcache_timedimension_sqlite {
- mapcache_timedimension timedimension;
- char *dbfile;
- char *query;
-};
-mapcache_timedimension* mapcache_timedimension_sqlite_create(apr_pool_t *pool);
-#endif
+mapcache_dimension* mapcache_dimension_values_create(mapcache_context *ctx, apr_pool_t *pool);
+mapcache_dimension* mapcache_dimension_sqlite_create(mapcache_context *ctx, apr_pool_t *pool);
+mapcache_dimension* mapcache_dimension_regex_create(mapcache_context *ctx, apr_pool_t *pool);
+mapcache_dimension* mapcache_dimension_time_create(mapcache_context *ctx, apr_pool_t *pool);
int mapcache_is_axis_inverted(const char *srs);
diff --git a/include/mapcache_services.h b/include/mapcache_services.h
new file mode 100644
index 0000000..1e28308
--- /dev/null
+++ b/include/mapcache_services.h
@@ -0,0 +1,162 @@
+/******************************************************************************
+ *
+ * Project: MapCache
+ * Author: Thomas Bonfort and the MapServer team.
+ *
+ ******************************************************************************
+ * Copyright (c) 1996-2016 Regents of the University of Minnesota.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies of this Software or works derived from this Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *****************************************************************************/
+
+/*! \file mapcache_services.h
+ \brief structure declarations for supported services
+ */
+
+
+#ifndef MAPCACHE_SERVICES_H_
+#define MAPCACHE_SERVICES_H_
+
+#include "mapcache.h"
+
+
+/** \addtogroup services */
+/** @{ */
+
+typedef struct mapcache_request_get_capabilities_wmts mapcache_request_get_capabilities_wmts;
+typedef struct mapcache_service_wmts mapcache_service_wmts;
+
+struct mapcache_request_get_capabilities_wmts {
+ mapcache_request_get_capabilities request;
+};
+
+/**\class mapcache_service_wmts
+ * \brief a WMTS service
+ * \implements mapcache_service
+ */
+struct mapcache_service_wmts {
+ mapcache_service service;
+};
+
+
+
+typedef struct mapcache_service_wms mapcache_service_wms;
+typedef struct mapcache_request_get_capabilities_wms mapcache_request_get_capabilities_wms;
+
+struct mapcache_request_get_capabilities_wms {
+ mapcache_request_get_capabilities request;
+};
+
+/**\class mapcache_service_wms
+ * \brief an OGC WMS service
+ * \implements mapcache_service
+ */
+struct mapcache_service_wms {
+ mapcache_service service;
+ int maxsize;
+ apr_array_header_t *forwarding_rules;
+ mapcache_getmap_strategy getmap_strategy;
+ mapcache_resample_mode resample_mode;
+ mapcache_image_format *getmap_format;
+ int allow_format_override; /* can the client specify which image format should be returned */
+};
+
+typedef struct mapcache_service_ve mapcache_service_ve;
+
+/**\class mapcache_service_ve
+ * \brief a virtualearth service
+ * \implements mapcache_service
+ */
+struct mapcache_service_ve {
+ mapcache_service service;
+};
+
+
+typedef struct mapcache_service_gmaps mapcache_service_gmaps;
+typedef struct mapcache_service_tms mapcache_service_tms;
+typedef struct mapcache_request_get_capabilities_tms mapcache_request_get_capabilities_tms;
+
+/**\class mapcache_service_tms
+ * \brief a TMS service
+ * \implements mapcache_service
+ */
+struct mapcache_service_tms {
+ mapcache_service service;
+ int reverse_y;
+};
+
+struct mapcache_request_get_capabilities_tms {
+ mapcache_request_get_capabilities request;
+ mapcache_tileset *tileset;
+ mapcache_grid_link *grid_link;
+ char *version;
+};
+
+
+typedef struct mapcache_service_mapguide mapcache_service_mapguide;
+
+struct mapcache_service_mapguide {
+ mapcache_service service;
+ int rows_per_folder;
+ int cols_per_folder;
+};
+
+
+
+typedef struct mapcache_service_kml mapcache_service_kml;
+typedef struct mapcache_request_get_capabilities_kml mapcache_request_get_capabilities_kml;
+struct mapcache_request_get_capabilities_kml {
+ mapcache_request_get_capabilities request;
+ mapcache_tile *tile;
+ mapcache_tileset *tileset;
+ mapcache_grid_link *grid;
+};
+/**\class mapcache_service_kml
+ * \brief a KML superoverlay service
+ * \implements mapcache_service
+ */
+struct mapcache_service_kml {
+ mapcache_service service;
+};
+
+typedef struct mapcache_request_get_capabilities_demo mapcache_request_get_capabilities_demo;
+typedef struct mapcache_service_demo mapcache_service_demo;
+
+/**
+ * the capabilities request for a specific service, to be able to create
+ * demo pages specific to a given service
+ */
+struct mapcache_request_get_capabilities_demo {
+ mapcache_request_get_capabilities request;
+ mapcache_service *service;
+};
+
+/**\class mapcache_service_demo
+ * \brief a demo service
+ * \implements mapcache_service
+ */
+struct mapcache_service_demo {
+ mapcache_service service;
+
+};
+
+
+
+
+#endif /*MAPCACHE_SERVICES_H*/
diff --git a/lib/buffer.c b/lib/buffer.c
index 4f0fd3b..4282a57 100644
--- a/lib/buffer.c
+++ b/lib/buffer.c
@@ -57,7 +57,6 @@ mapcache_buffer *mapcache_buffer_create(size_t initialStorage, apr_pool_t* pool)
mapcache_buffer *buffer = apr_pcalloc(pool, sizeof(mapcache_buffer));
if(!buffer) return NULL;
buffer->pool = pool;
- if(initialStorage <=0) initialStorage = 1;
buffer->avail = initialStorage;
if(buffer->avail) {
buffer->buf = malloc(buffer->avail);
diff --git a/lib/cache.c b/lib/cache.c
new file mode 100644
index 0000000..fa7583f
--- /dev/null
+++ b/lib/cache.c
@@ -0,0 +1,159 @@
+/******************************************************************************
+ *
+ * Project: MapServer
+ * Purpose: MapCache tile caching: generic cache access
+ * Author: Thomas Bonfort and the MapServer team.
+ *
+ ******************************************************************************
+ * Copyright (c) 1996-2015 Regents of the University of Minnesota.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without compositeriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies of this Software or works derived from this Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *****************************************************************************/
+#include "mapcache.h"
+#include <apr_time.h>
+
+int mapcache_cache_tile_get(mapcache_context *ctx, mapcache_cache *cache, mapcache_tile *tile) {
+ int i,rv;
+#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
+ 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));
+ ctx->clear_errors(ctx);
+ if(cache->retry_delay > 0) {
+ double wait = cache->retry_delay;
+ int j = 0;
+ for(j=1;j<i;j++) /* sleep twice as long as before previous retry */
+ wait *= 2;
+ apr_sleep((int)(wait*1000000)); /* apr_sleep expects microseconds */
+ }
+ }
+ rv = cache->_tile_get(ctx,cache,tile);
+ if(!GC_HAS_ERROR(ctx))
+ break;
+ }
+ return rv;
+}
+
+void mapcache_cache_tile_delete(mapcache_context *ctx, mapcache_cache *cache, mapcache_tile *tile) {
+ int i;
+#ifdef DEBUG
+ ctx->log(ctx,MAPCACHE_DEBUG,"calling tile_delete 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->tileset->read_only)
+ return;
+ for(i=0;i<=cache->retry_count;i++) {
+ if(i) {
+ ctx->log(ctx,MAPCACHE_INFO,"cache (%s) delete retry %d of %d. previous try returned error: %s",cache->name,i,cache->retry_count,ctx->get_error_message(ctx));
+ ctx->clear_errors(ctx);
+ if(cache->retry_delay > 0) {
+ double wait = cache->retry_delay;
+ int j = 0;
+ for(j=1;j<i;j++) /* sleep twice as long as before previous retry */
+ wait *= 2;
+ apr_sleep((int)(wait*1000000)); /* apr_sleep expects microseconds */
+ }
+ }
+ cache->_tile_delete(ctx,cache,tile);
+ if(!GC_HAS_ERROR(ctx))
+ break;
+ }
+}
+
+int mapcache_cache_tile_exists(mapcache_context *ctx, mapcache_cache *cache, mapcache_tile *tile) {
+ int i,rv;
+#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
+ 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));
+ ctx->clear_errors(ctx);
+ if(cache->retry_delay > 0) {
+ double wait = cache->retry_delay;
+ int j = 0;
+ for(j=1;j<i;j++) /* sleep twice as long as before previous retry */
+ wait *= 2;
+ apr_sleep((int)(wait*1000000)); /* apr_sleep expects microseconds */
+ }
+ }
+ rv = cache->_tile_exists(ctx,cache,tile);
+ if(!GC_HAS_ERROR(ctx))
+ break;
+ }
+ return rv;
+}
+
+void mapcache_cache_tile_set(mapcache_context *ctx, mapcache_cache *cache, mapcache_tile *tile) {
+ int i;
+#ifdef DEBUG
+ ctx->log(ctx,MAPCACHE_DEBUG,"calling tile_set 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->tileset->read_only)
+ return;
+ for(i=0;i<=cache->retry_count;i++) {
+ if(i) {
+ ctx->log(ctx,MAPCACHE_INFO,"cache (%s) set retry %d of %d. previous try returned error: %s",cache->name,i,cache->retry_count,ctx->get_error_message(ctx));
+ ctx->clear_errors(ctx);
+ if(cache->retry_delay > 0) {
+ double wait = cache->retry_delay;
+ int j = 0;
+ for(j=1;j<i;j++) /* sleep twice as long as before previous retry */
+ wait *= 2;
+ apr_sleep((int)(wait*1000000)); /* apr_sleep expects microseconds */
+ }
+ }
+ cache->_tile_set(ctx,cache,tile);
+ if(!GC_HAS_ERROR(ctx))
+ break;
+ }
+}
+
+void mapcache_cache_tile_multi_set(mapcache_context *ctx, mapcache_cache *cache, mapcache_tile *tiles, int ntiles) {
+ int i;
+#ifdef DEBUG
+ ctx->log(ctx,MAPCACHE_DEBUG,"calling tile_multi_set on cache (%s): (tileset=%s, grid=%s, first tile: z=%d, x=%d, y=%d",cache->name,tiles[0].tileset->name,tiles[0].grid_link->grid->name,
+ tiles[0].z,tiles[0].x, tiles[0].y);
+#endif
+ if((&tiles[0])->tileset->read_only)
+ return;
+ if(cache->_tile_multi_set) {
+ for(i=0;i<=cache->retry_count;i++) {
+ if(i) {
+ ctx->log(ctx,MAPCACHE_INFO,"cache (%s) multi-set retry %d of %d. previous try returned error: %s",cache->name,i,cache->retry_count,ctx->get_error_message(ctx));
+ ctx->clear_errors(ctx);
+ if(cache->retry_delay > 0) {
+ double wait = cache->retry_delay;
+ int j = 0;
+ for(j=1;j<i;j++) /* sleep twice as long as before previous retry */
+ wait *= 2;
+ apr_sleep((int)(wait*1000000)); /* apr_sleep expects microseconds */
+ }
+ }
+ cache->_tile_multi_set(ctx,cache,tiles,ntiles);
+ if(!GC_HAS_ERROR(ctx))
+ break;
+ }
+ } else {
+ for( i=0;i<ntiles;i++ ) {
+ mapcache_cache_tile_set(ctx, cache, tiles+i);
+ }
+ }
+}
diff --git a/lib/cache_bdb.c b/lib/cache_bdb.c
index a45ca4b..47a9bbc 100644
--- a/lib/cache_bdb.c
+++ b/lib/cache_bdb.c
@@ -1,7 +1,6 @@
/******************************************************************************
- * $Id$
*
- * Project: MapServer
+ * Project: MapCache
* Purpose: MapCache tile caching support file: Berkeley DB cache backend
* Author: Thomas Bonfort and the MapServer team.
*
@@ -27,10 +26,8 @@
* DEALINGS IN THE SOFTWARE.
*****************************************************************************/
-#include "mapcache-config.h"
-#ifdef USE_BDB
-
#include "mapcache.h"
+#ifdef USE_BDB
#include <apr_strings.h>
#include <apr_reslist.h>
#include <apr_file_info.h>
@@ -51,6 +48,13 @@
#define PAGESIZE 64*1024
#define CACHESIZE 1024*1024
+typedef struct mapcache_cache_bdb mapcache_cache_bdb;
+struct mapcache_cache_bdb {
+ mapcache_cache cache;
+ char *basedir;
+ char *key_template;
+};
+
struct bdb_env {
DB* db;
DB_ENV *env;
@@ -406,11 +410,11 @@ mapcache_cache* mapcache_cache_bdb_create(mapcache_context *ctx)
}
cache->cache.metadata = apr_table_make(ctx->pool,3);
cache->cache.type = MAPCACHE_CACHE_BDB;
- cache->cache.tile_delete = _mapcache_cache_bdb_delete;
- cache->cache.tile_get = _mapcache_cache_bdb_get;
- cache->cache.tile_exists = _mapcache_cache_bdb_has_tile;
- cache->cache.tile_set = _mapcache_cache_bdb_set;
- cache->cache.tile_multi_set = _mapcache_cache_bdb_multiset;
+ cache->cache._tile_delete = _mapcache_cache_bdb_delete;
+ cache->cache._tile_get = _mapcache_cache_bdb_get;
+ cache->cache._tile_exists = _mapcache_cache_bdb_has_tile;
+ cache->cache._tile_set = _mapcache_cache_bdb_set;
+ cache->cache._tile_multi_set = _mapcache_cache_bdb_multiset;
cache->cache.configuration_post_config = _mapcache_cache_bdb_configuration_post_config;
cache->cache.configuration_parse_xml = _mapcache_cache_bdb_configuration_parse_xml;
cache->basedir = NULL;
@@ -418,6 +422,11 @@ mapcache_cache* mapcache_cache_bdb_create(mapcache_context *ctx)
return (mapcache_cache*)cache;
}
+#else
+mapcache_cache* mapcache_cache_bdb_create(mapcache_context *ctx) {
+ ctx->set_error(ctx,400,"BERKELEYDB support not compiled in this version");
+ return NULL;
+}
#endif
/* vim: ts=2 sts=2 et sw=2
diff --git a/lib/cache_composite.c b/lib/cache_composite.c
index 4b307ef..b29c389 100644
--- a/lib/cache_composite.c
+++ b/lib/cache_composite.c
@@ -27,6 +27,23 @@
*****************************************************************************/
#include "mapcache.h"
+#include <apr_strings.h>
+
+typedef struct mapcache_cache_composite mapcache_cache_composite;
+
+typedef struct mapcache_cache_composite_cache_link mapcache_cache_composite_cache_link;
+struct mapcache_cache_composite_cache_link {
+ mapcache_cache *cache;
+ int minzoom;
+ int maxzoom;
+ apr_array_header_t *grids;
+ apr_table_t *dimensions; /* key/value pairs of dimensions */
+};
+
+struct mapcache_cache_composite {
+ mapcache_cache cache;
+ apr_array_header_t *cache_links;
+};
static mapcache_cache_composite_cache_link* _mapcache_cache_link_create(apr_pool_t *pool) {
mapcache_cache_composite_cache_link *cl = apr_pcalloc(pool, sizeof(mapcache_cache_composite_cache_link));
@@ -59,11 +76,31 @@ static mapcache_cache* _mapcache_composite_cache_get(mapcache_context *ctx, mapc
/* not found */
if(j == cache_link->grids->nelts) continue;
}
+ if(cache_link->dimensions) {
+ const apr_array_header_t *array = apr_table_elts(cache_link->dimensions);
+ apr_table_entry_t *elts = (apr_table_entry_t *) array->elts;
+ int j;
+ if(!tile->dimensions) continue; /* the cache link refers to dimensions, but this tile does not have any, it cannot match */
+
+ for (j = 0; j < array->nelts; j++) {
+ char *dim = elts[j].key;
+ char *dimval = elts[j].val;
+ int k;
+ for(k=0;k<tile->dimensions->nelts;k++) {
+ mapcache_requested_dimension *rdim = APR_ARRAY_IDX(tile->dimensions,i,mapcache_requested_dimension*);
+ if(!strcmp(rdim->dimension->name,dim) && !strcmp(rdim->cached_value,dimval))
+ break;
+ }
+ if(k == tile->dimensions->nelts) break; /* no tile dimension matched the current cache dimension */
+ }
+ if(j != array->nelts) continue; /* we broke out early from the cache dimension loop, so at least one was not correct */
+ }
return cache_link->cache;
}
ctx->set_error(ctx, 500, "no cache matches for given tile request");
return NULL;
}
+
static int _mapcache_cache_composite_tile_exists(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
{
mapcache_cache_composite *cache = (mapcache_cache_composite*)pcache;
@@ -71,7 +108,7 @@ static int _mapcache_cache_composite_tile_exists(mapcache_context *ctx, mapcache
subcache = _mapcache_composite_cache_get(ctx, cache, tile);
if(GC_HAS_ERROR(ctx) || !subcache)
return MAPCACHE_FAILURE;
- return subcache->tile_exists(ctx, subcache, tile);
+ return mapcache_cache_tile_exists(ctx, subcache, tile);
}
static void _mapcache_cache_composite_tile_delete(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
@@ -81,7 +118,7 @@ static void _mapcache_cache_composite_tile_delete(mapcache_context *ctx, mapcach
subcache = _mapcache_composite_cache_get(ctx, cache, tile);
GC_CHECK_ERROR(ctx);
/*delete the tile itself*/
- subcache->tile_delete(ctx,subcache,tile);
+ mapcache_cache_tile_delete(ctx,subcache,tile);
}
/**
@@ -97,7 +134,7 @@ static int _mapcache_cache_composite_tile_get(mapcache_context *ctx, mapcache_ca
mapcache_cache *subcache;
subcache = _mapcache_composite_cache_get(ctx, cache, tile);
GC_CHECK_ERROR_RETURN(ctx);
- return subcache->tile_get(ctx,subcache,tile);
+ return mapcache_cache_tile_get(ctx,subcache,tile);
}
static void _mapcache_cache_composite_tile_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
@@ -106,7 +143,7 @@ static void _mapcache_cache_composite_tile_set(mapcache_context *ctx, mapcache_c
mapcache_cache *subcache;
subcache = _mapcache_composite_cache_get(ctx, cache, tile);
GC_CHECK_ERROR(ctx);
- return subcache->tile_set(ctx,subcache,tile);
+ return mapcache_cache_tile_set(ctx,subcache,tile);
}
static void _mapcache_cache_composite_tile_multi_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tiles, int ntiles)
@@ -115,14 +152,7 @@ static void _mapcache_cache_composite_tile_multi_set(mapcache_context *ctx, mapc
mapcache_cache *subcache;
subcache = _mapcache_composite_cache_get(ctx, cache, &tiles[0]);
GC_CHECK_ERROR(ctx);
- if(subcache->tile_multi_set) {
- return subcache->tile_multi_set(ctx,subcache,tiles,ntiles);
- } else {
- int i;
- for(i=0; i<ntiles; i++) {
- subcache->tile_set(ctx, subcache, &tiles[i]);
- }
- }
+ return mapcache_cache_tile_multi_set(ctx,subcache,tiles,ntiles);
}
/**
@@ -168,6 +198,37 @@ static void _mapcache_cache_composite_configuration_parse_xml(mapcache_context *
}
cachelink->minzoom = zoom;
}
+ sZoom = (char*)ezxml_attr(cur_node,"grids");
+ if(sZoom) {
+ char *grids = apr_pstrdup(ctx->pool,sZoom),*key,*last;
+ for(key = apr_strtok(grids, ",", &last); key; key = apr_strtok(NULL,",",&last)) {
+ /*loop through grids*/
+ if(!cachelink->grids) {
+ cachelink->grids =apr_array_make(ctx->pool,1,sizeof(char*));
+ }
+ APR_ARRAY_PUSH(cachelink->grids,char*) = key;
+ }
+ }
+ sZoom = (char*)ezxml_attr(cur_node,"dimensions");
+ if(sZoom) {
+ char *dims = apr_pstrdup(ctx->pool,sZoom),*key,*last;
+ for(key = apr_strtok(dims, ",", &last); key; key = apr_strtok(NULL,",",&last)) {
+ char *dimname;
+ /*loop through dims*/
+ if(!cachelink->dimensions) {
+ cachelink->dimensions =apr_table_make(ctx->pool,1);
+ }
+ dimname = key;
+ while(*key && *key!='=') key++;
+ if(!(*key)) {
+ ctx->set_error(ctx,400,"failed to parse composite cache dimensions. expecting dimensions=\"dim1=val1,dim2=val2\"");
+ return;
+ }
+ *key = 0;
+ key++;
+ apr_table_set(cachelink->dimensions,dimname,key);
+ }
+ }
APR_ARRAY_PUSH(cache->cache_links,mapcache_cache_composite_cache_link*) = cachelink;
}
@@ -194,11 +255,11 @@ mapcache_cache* mapcache_cache_composite_create(mapcache_context *ctx)
}
cache->cache.metadata = apr_table_make(ctx->pool,3);
cache->cache.type = MAPCACHE_CACHE_COMPOSITE;
- cache->cache.tile_delete = _mapcache_cache_composite_tile_delete;
- cache->cache.tile_get = _mapcache_cache_composite_tile_get;
- cache->cache.tile_exists = _mapcache_cache_composite_tile_exists;
- cache->cache.tile_set = _mapcache_cache_composite_tile_set;
- cache->cache.tile_multi_set = _mapcache_cache_composite_tile_multi_set;
+ cache->cache._tile_delete = _mapcache_cache_composite_tile_delete;
+ cache->cache._tile_get = _mapcache_cache_composite_tile_get;
+ cache->cache._tile_exists = _mapcache_cache_composite_tile_exists;
+ cache->cache._tile_set = _mapcache_cache_composite_tile_set;
+ cache->cache._tile_multi_set = _mapcache_cache_composite_tile_multi_set;
cache->cache.configuration_post_config = _mapcache_cache_composite_configuration_post_config;
cache->cache.configuration_parse_xml = _mapcache_cache_composite_configuration_parse_xml;
return (mapcache_cache*)cache;
diff --git a/lib/cache_couchbase.c b/lib/cache_couchbase.c
index 356402d..e81fe60 100644
--- a/lib/cache_couchbase.c
+++ b/lib/cache_couchbase.c
@@ -27,13 +27,31 @@
* DEALINGS IN THE SOFTWARE.
*****************************************************************************/
-#include "mapcache-config.h"
+#include "mapcache.h"
+
#ifdef USE_COUCHBASE
-#include "mapcache.h"
#include <apr_strings.h>
#include <string.h>
#include <errno.h>
+#include <libcouchbase/couchbase.h>
+
+typedef struct mapcache_cache_couchbase mapcache_cache_couchbase;
+
+/**\class mapcache_cache_couchbase
+ * \brief a mapcache_cache on couchbase servers
+ * \implements mapcache_cache
+ */
+struct mapcache_cache_couchbase {
+ mapcache_cache cache;
+// apr_reslist_t *connection_pool;
+ char *host;
+ char *username;
+ char *password;
+ char *bucket;
+ mapcache_context *ctx;
+};
+
typedef struct getStruct
{
@@ -456,6 +474,11 @@ mapcache_cache* mapcache_cache_couchbase_create(mapcache_context *ctx) {
return (mapcache_cache*)cache;
}
+#else
+mapcache_cache* mapcache_cache_couchbase_create(mapcache_context *ctx) {
+ ctx->set_error(ctx,400,"COUCHBASE support not compiled in this version");
+ return NULL;
+}
#endif
/* vim: ai ts=3 sts=3 et sw=3
diff --git a/lib/cache_disk.c b/lib/cache_disk.c
index 2ffbb0a..ff45cde 100644
--- a/lib/cache_disk.c
+++ b/lib/cache_disk.c
@@ -39,6 +39,26 @@
#include <unistd.h>
#endif
+/**\class mapcache_cache_disk
+ * \brief a mapcache_cache on a filesytem
+ * \implements mapcache_cache
+ */
+typedef struct mapcache_cache_disk mapcache_cache_disk;
+struct mapcache_cache_disk {
+ mapcache_cache cache;
+ char *base_directory;
+ char *filename_template;
+ int symlink_blank;
+ int creation_retry;
+
+ /**
+ * Set filename for a given tile
+ * \memberof mapcache_cache_disk
+ */
+ void (*tile_key)(mapcache_context *ctx, mapcache_cache_disk *cache, mapcache_tile *tile, char **path);
+};
+
+
/**
* \brief computes the relative path between two destinations
*
@@ -119,11 +139,15 @@ static void _mapcache_cache_disk_base_tile_key(mapcache_context *ctx, mapcache_c
tile->grid_link->grid->name,
NULL);
if(tile->dimensions) {
- const apr_array_header_t *elts = apr_table_elts(tile->dimensions);
- int i = elts->nelts;
+ int i = tile->dimensions->nelts;
while(i--) {
- apr_table_entry_t *entry = &(APR_ARRAY_IDX(elts,i,apr_table_entry_t));
- const char *dimval = mapcache_util_str_sanitize(ctx->pool,entry->val,"/.",'#');
+ mapcache_requested_dimension *entry = APR_ARRAY_IDX(tile->dimensions,i,mapcache_requested_dimension*);
+ char *dimval;
+ if(!entry->cached_value) {
+ ctx->set_error(ctx,500,"BUG: dimension (%s) not set",entry->dimension->name);
+ return;
+ }
+ dimval = mapcache_util_str_sanitize(ctx->pool,entry->cached_value,"/.",'#');
*path = apr_pstrcat(ctx->pool,*path,"/",dimval,NULL);
}
}
@@ -196,14 +220,20 @@ static void _mapcache_cache_disk_tilecache_tile_key(mapcache_context *ctx, mapca
*path = mapcache_util_str_replace(ctx->pool,*path, "{inv_z}",
apr_psprintf(ctx->pool,"%d",
tile->grid_link->grid->nlevels - tile->z - 1));
- if(tile->dimensions) {
+ if(tile->dimensions && strstr(*path,"{dim")) {
char *dimstring="";
- const apr_array_header_t *elts = apr_table_elts(tile->dimensions);
- int i = elts->nelts;
+ int i = tile->dimensions->nelts;
while(i--) {
- apr_table_entry_t *entry = &(APR_ARRAY_IDX(elts,i,apr_table_entry_t));
- char *dimval = apr_pstrdup(ctx->pool,entry->val);
- char *iter = dimval;
+ mapcache_requested_dimension *entry = APR_ARRAY_IDX(tile->dimensions,i, mapcache_requested_dimension*);
+ char *dimval;
+ char *single_dim;
+ char *iter;
+ if(!entry->cached_value) {
+ ctx->set_error(ctx,500,"BUG: dimension (%s) not set",entry->dimension->name);
+ return;
+ }
+ dimval = apr_pstrdup(ctx->pool,entry->cached_value);
+ iter = dimval;
while(*iter) {
/* replace dangerous characters by '#' */
if(*iter == '.' || *iter == '/') {
@@ -211,7 +241,11 @@ static void _mapcache_cache_disk_tilecache_tile_key(mapcache_context *ctx, mapca
}
iter++;
}
- dimstring = apr_pstrcat(ctx->pool,dimstring,"#",entry->key,"#",dimval,NULL);
+ dimstring = apr_pstrcat(ctx->pool,dimstring,"#",entry->dimension->name,"#",dimval,NULL);
+ single_dim = apr_pstrcat(ctx->pool,"{dim:",entry->dimension->name,"}",NULL);
+ if(strstr(*path,single_dim)) {
+ *path = mapcache_util_str_replace(ctx->pool,*path, single_dim, dimval);
+ }
}
*path = mapcache_util_str_replace(ctx->pool,*path, "{dim}", dimstring);
}
@@ -251,14 +285,20 @@ static void _mapcache_cache_disk_template_tile_key(mapcache_context *ctx, mapcac
*path = mapcache_util_str_replace(ctx->pool,*path, "{inv_z}",
apr_psprintf(ctx->pool,"%d",
tile->grid_link->grid->nlevels - tile->z - 1));
- if(tile->dimensions) {
+ if(tile->dimensions && strstr(*path,"{dim")) {
char *dimstring="";
- const apr_array_header_t *elts = apr_table_elts(tile->dimensions);
- int i = elts->nelts;
+ int i = tile->dimensions->nelts;
while(i--) {
- apr_table_entry_t *entry = &(APR_ARRAY_IDX(elts,i,apr_table_entry_t));
- char *dimval = apr_pstrdup(ctx->pool,entry->val);
- char *iter = dimval;
+ mapcache_requested_dimension *entry = APR_ARRAY_IDX(tile->dimensions,i, mapcache_requested_dimension*);
+ char *dimval;
+ char *single_dim;
+ char *iter;
+ if(!entry->cached_value) {
+ ctx->set_error(ctx,500,"BUG: dimension (%s) not set",entry->dimension->name);
+ return;
+ }
+ dimval = apr_pstrdup(ctx->pool,entry->cached_value);
+ iter = dimval;
while(*iter) {
/* replace dangerous characters by '#' */
if(*iter == '.' || *iter == '/') {
@@ -266,7 +306,11 @@ static void _mapcache_cache_disk_template_tile_key(mapcache_context *ctx, mapcac
}
iter++;
}
- dimstring = apr_pstrcat(ctx->pool,dimstring,"#",entry->key,"#",dimval,NULL);
+ dimstring = apr_pstrcat(ctx->pool,dimstring,"#",entry->dimension->name,"#",dimval,NULL);
+ single_dim = apr_pstrcat(ctx->pool,"{dim:",entry->dimension->name,"}",NULL);
+ if(strstr(*path,single_dim)) {
+ *path = mapcache_util_str_replace(ctx->pool,*path, single_dim, dimval);
+ }
}
*path = mapcache_util_str_replace(ctx->pool,*path, "{dim}", dimstring);
}
@@ -377,8 +421,8 @@ static int _mapcache_cache_disk_get(mapcache_context *ctx, mapcache_cache *pcach
ctx->pool)) == APR_SUCCESS) {
rv = apr_file_info_get(&finfo, APR_FINFO_SIZE|APR_FINFO_MTIME, f);
if(!finfo.size) {
- ctx->set_error(ctx, 500, "tile %s has no data",filename);
- return MAPCACHE_FAILURE;
+ ctx->log(ctx, MAPCACHE_WARN, "tile %s has 0 length data",filename);
+ return MAPCACHE_CACHE_MISS;
}
size = finfo.size;
@@ -391,10 +435,10 @@ static int _mapcache_cache_disk_get(mapcache_context *ctx, mapcache_cache *pcach
* i.e. normally only once.
*/
tile->mtime = finfo.mtime;
- tile->encoded_data = mapcache_buffer_create(size,ctx->pool);
#ifndef NOMMAP
+ tile->encoded_data = mapcache_buffer_create(0,ctx->pool);
rv = apr_mmap_create(&tilemmap,f,0,finfo.size,APR_MMAP_READ,ctx->pool);
if(rv != APR_SUCCESS) {
char errmsg[120];
@@ -404,6 +448,7 @@ static int _mapcache_cache_disk_get(mapcache_context *ctx, mapcache_cache *pcach
tile->encoded_data->buf = tilemmap->mm;
tile->encoded_data->size = tile->encoded_data->avail = finfo.size;
#else
+ tile->encoded_data = mapcache_buffer_create(size,ctx->pool);
//manually add the data to our buffer
apr_file_read(f,(void*)tile->encoded_data->buf,&size);
tile->encoded_data->size = size;
@@ -502,7 +547,7 @@ static void _mapcache_cache_disk_set(mapcache_context *ctx, mapcache_cache *pcac
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);
+ mapcache_unlock_resource(ctx,ctx->config->locker, lock);
return; /* we could not create the file */
}
@@ -510,17 +555,17 @@ static void _mapcache_cache_disk_set(mapcache_context *ctx, mapcache_cache *pcac
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);
+ mapcache_unlock_resource(ctx,ctx->config->locker, 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);
+ mapcache_unlock_resource(ctx,ctx->config->locker, lock);
return;
}
apr_file_close(f);
- mapcache_unlock_resource(ctx,ctx->config->locker,blankname, lock);
+ mapcache_unlock_resource(ctx,ctx->config->locker, lock);
#ifdef DEBUG
ctx->log(ctx,MAPCACHE_DEBUG,"created blank tile %s",blankname);
#endif
@@ -567,6 +612,12 @@ static void _mapcache_cache_disk_set(mapcache_context *ctx, mapcache_cache *pcac
GC_CHECK_ERROR(ctx);
}
+ bytes = (apr_size_t)tile->encoded_data->size;
+ if(bytes == 0) {
+ ctx->set_error(ctx, 500, "attempting to write 0 length tile to %s",filename);
+ return; /* we could not create the file */
+ }
+
/*
* depending on configuration file creation will retry if it fails.
* this can happen on nfs mounted network storage.
@@ -586,20 +637,20 @@ static void _mapcache_cache_disk_set(mapcache_context *ctx, mapcache_cache *pcac
GC_CHECK_ERROR(ctx);
}
- 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 */
+ }
+
+ 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);
+ apr_file_remove(filename, ctx->pool);
}
}
@@ -684,10 +735,10 @@ mapcache_cache* mapcache_cache_disk_create(mapcache_context *ctx)
cache->creation_retry = 0;
cache->cache.metadata = apr_table_make(ctx->pool,3);
cache->cache.type = MAPCACHE_CACHE_DISK;
- cache->cache.tile_delete = _mapcache_cache_disk_delete;
- cache->cache.tile_get = _mapcache_cache_disk_get;
- cache->cache.tile_exists = _mapcache_cache_disk_has_tile;
- cache->cache.tile_set = _mapcache_cache_disk_set;
+ cache->cache._tile_delete = _mapcache_cache_disk_delete;
+ cache->cache._tile_get = _mapcache_cache_disk_get;
+ cache->cache._tile_exists = _mapcache_cache_disk_has_tile;
+ cache->cache._tile_set = _mapcache_cache_disk_set;
cache->cache.configuration_post_config = _mapcache_cache_disk_configuration_post_config;
cache->cache.configuration_parse_xml = _mapcache_cache_disk_configuration_parse_xml;
return (mapcache_cache*)cache;
diff --git a/lib/cache_fallback.c b/lib/cache_fallback.c
index 28c3c47..70edcd3 100644
--- a/lib/cache_fallback.c
+++ b/lib/cache_fallback.c
@@ -28,11 +28,18 @@
#include "mapcache.h"
+typedef struct mapcache_cache_fallback mapcache_cache_fallback;
+
+struct mapcache_cache_fallback {
+ mapcache_cache cache;
+ apr_array_header_t *caches;
+};
+
static int _mapcache_cache_fallback_tile_exists(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
{
mapcache_cache_fallback *cache = (mapcache_cache_fallback*)pcache;
mapcache_cache *subcache = APR_ARRAY_IDX(cache->caches,0,mapcache_cache*);
- return subcache->tile_exists(ctx, subcache, tile);
+ return mapcache_cache_tile_exists(ctx, subcache, tile);
}
static void _mapcache_cache_fallback_tile_delete(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
@@ -41,7 +48,7 @@ static void _mapcache_cache_fallback_tile_delete(mapcache_context *ctx, mapcache
int i;
for(i=0; i<cache->caches->nelts; i++) {
mapcache_cache *subcache = APR_ARRAY_IDX(cache->caches,i,mapcache_cache*);
- subcache->tile_delete(ctx, subcache, tile);
+ mapcache_cache_tile_delete(ctx, subcache, tile);
ctx->clear_errors(ctx); /* ignore errors */
}
}
@@ -59,7 +66,7 @@ static int _mapcache_cache_fallback_tile_get(mapcache_context *ctx, mapcache_cac
mapcache_cache *subcache;
int i,ret;
subcache = APR_ARRAY_IDX(cache->caches,0,mapcache_cache*);
- ret = subcache->tile_get(ctx, subcache, tile);
+ ret = mapcache_cache_tile_get(ctx, subcache, tile);
if(ret == MAPCACHE_FAILURE) {
int first_error = ctx->get_error(ctx);
@@ -69,7 +76,7 @@ static int _mapcache_cache_fallback_tile_get(mapcache_context *ctx, mapcache_cac
ctx->clear_errors(ctx);
for(i=1; i<cache->caches->nelts; i++) {
subcache = APR_ARRAY_IDX(cache->caches,i,mapcache_cache*);
- if((ret = subcache->tile_get(ctx, subcache, tile)) == MAPCACHE_FAILURE) {
+ if((ret = mapcache_cache_tile_get(ctx, subcache, tile)) == MAPCACHE_FAILURE) {
ctx->log(ctx,MAPCACHE_DEBUG,"failed \"GET\" on fallback cache \"%s\" for tile (z=%d,x=%d,y=%d) of tileset \"%s\". Continuing with other fallback caches if available",
APR_ARRAY_IDX(cache->caches,0,mapcache_cache*)->name,tile->z,tile->x,tile->y,tile->tileset->name);
ctx->clear_errors(ctx);
@@ -90,11 +97,11 @@ static int _mapcache_cache_fallback_tile_get(mapcache_context *ctx, mapcache_cac
static void _mapcache_cache_fallback_tile_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
{
mapcache_cache_fallback *cache = (mapcache_cache_fallback*)pcache;
- int i,oneok=0,first_error=0;
+ int i,first_error=0;
char *first_error_message;
for(i=0; i<cache->caches->nelts; i++) {
mapcache_cache *subcache = APR_ARRAY_IDX(cache->caches,i,mapcache_cache*);
- subcache->tile_set(ctx, subcache, tile);
+ mapcache_cache_tile_set(ctx, subcache, tile);
if(GC_HAS_ERROR(ctx)) {
if(!first_error) {
first_error = ctx->get_error(ctx);
@@ -103,11 +110,9 @@ static void _mapcache_cache_fallback_tile_set(mapcache_context *ctx, mapcache_ca
ctx->log(ctx,MAPCACHE_DEBUG,"failed \"SET\" on subcache \"%s\" for tile (z=%d,x=%d,y=%d) of tileset \"%s\"",
APR_ARRAY_IDX(cache->caches,i,mapcache_cache*)->name,tile->z,tile->x,tile->y,tile->tileset->name);
ctx->clear_errors(ctx);
- } else {
- oneok = 1;
}
}
- if(!oneok) {
+ if(first_error) {
ctx->set_error(ctx,first_error,first_error_message);
}
}
@@ -115,21 +120,11 @@ static void _mapcache_cache_fallback_tile_set(mapcache_context *ctx, mapcache_ca
static void _mapcache_cache_fallback_tile_multi_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tiles, int ntiles)
{
mapcache_cache_fallback *cache = (mapcache_cache_fallback*)pcache;
- int i,oneok=0,first_error=0;
+ int i,first_error=0;
char *first_error_message;
for(i=0; i<cache->caches->nelts; i++) {
mapcache_cache *subcache = APR_ARRAY_IDX(cache->caches,i,mapcache_cache*);
- if(subcache->tile_multi_set) {
- subcache->tile_multi_set(ctx, subcache, tiles, ntiles);
- }
- else {
- int j;
- for(j=0;j<ntiles;j++) {
- subcache->tile_set(ctx,subcache,&tiles[j]);
- if(GC_HAS_ERROR(ctx))
- break;
- }
- }
+ mapcache_cache_tile_multi_set(ctx, subcache, tiles, ntiles);
if(GC_HAS_ERROR(ctx)) {
if(!first_error) {
first_error = ctx->get_error(ctx);
@@ -138,11 +133,9 @@ static void _mapcache_cache_fallback_tile_multi_set(mapcache_context *ctx, mapca
ctx->log(ctx,MAPCACHE_DEBUG,"failed \"MULTISET\" on subcache \"%s\" for tile (z=%d,x=%d,y=%d) of tileset \"%s\"",
APR_ARRAY_IDX(cache->caches,i,mapcache_cache*)->name,tiles[0].z,tiles[0].x,tiles[0].y,tiles[0].tileset->name);
ctx->clear_errors(ctx);
- } else {
- oneok = 1;
}
}
- if(!oneok) {
+ if(first_error) {
ctx->set_error(ctx,first_error,first_error_message);
}
}
@@ -190,11 +183,11 @@ mapcache_cache* mapcache_cache_fallback_create(mapcache_context *ctx)
}
cache->cache.metadata = apr_table_make(ctx->pool,3);
cache->cache.type = MAPCACHE_CACHE_COMPOSITE;
- cache->cache.tile_delete = _mapcache_cache_fallback_tile_delete;
- cache->cache.tile_get = _mapcache_cache_fallback_tile_get;
- cache->cache.tile_exists = _mapcache_cache_fallback_tile_exists;
- cache->cache.tile_set = _mapcache_cache_fallback_tile_set;
- cache->cache.tile_multi_set = _mapcache_cache_fallback_tile_multi_set;
+ cache->cache._tile_delete = _mapcache_cache_fallback_tile_delete;
+ cache->cache._tile_get = _mapcache_cache_fallback_tile_get;
+ cache->cache._tile_exists = _mapcache_cache_fallback_tile_exists;
+ cache->cache._tile_set = _mapcache_cache_fallback_tile_set;
+ cache->cache._tile_multi_set = _mapcache_cache_fallback_tile_multi_set;
cache->cache.configuration_post_config = _mapcache_cache_fallback_configuration_post_config;
cache->cache.configuration_parse_xml = _mapcache_cache_fallback_configuration_parse_xml;
return (mapcache_cache*)cache;
diff --git a/lib/cache_memcache.c b/lib/cache_memcache.c
index a5d1a71..3b2c763 100644
--- a/lib/cache_memcache.c
+++ b/lib/cache_memcache.c
@@ -27,10 +27,29 @@
* DEALINGS IN THE SOFTWARE.
*****************************************************************************/
-#include "mapcache-config.h"
+#include "mapcache.h"
#ifdef USE_MEMCACHE
-#include "mapcache.h"
+#include <apr_memcache.h>
+
+typedef struct mapcache_cache_memcache mapcache_cache_memcache;
+/**\class mapcache_cache_memcache
+ * \brief a mapcache_cache on memcached servers
+ * \implements mapcache_cache
+ */
+
+struct mapcache_cache_memcache_server {
+ char* host;
+ int port;
+};
+
+struct mapcache_cache_memcache {
+ mapcache_cache cache;
+ int nservers;
+ struct mapcache_cache_memcache_server *servers;
+ int detect_blank;
+};
+
struct mapcache_memcache_conn_param {
mapcache_cache_memcache *cache;
};
@@ -345,15 +364,20 @@ mapcache_cache* mapcache_cache_memcache_create(mapcache_context *ctx)
}
cache->cache.metadata = apr_table_make(ctx->pool,3);
cache->cache.type = MAPCACHE_CACHE_MEMCACHE;
- cache->cache.tile_get = _mapcache_cache_memcache_get;
- cache->cache.tile_exists = _mapcache_cache_memcache_has_tile;
- cache->cache.tile_set = _mapcache_cache_memcache_set;
- cache->cache.tile_delete = _mapcache_cache_memcache_delete;
+ cache->cache._tile_get = _mapcache_cache_memcache_get;
+ cache->cache._tile_exists = _mapcache_cache_memcache_has_tile;
+ cache->cache._tile_set = _mapcache_cache_memcache_set;
+ cache->cache._tile_delete = _mapcache_cache_memcache_delete;
cache->cache.configuration_post_config = _mapcache_cache_memcache_configuration_post_config;
cache->cache.configuration_parse_xml = _mapcache_cache_memcache_configuration_parse_xml;
return (mapcache_cache*)cache;
}
+#else
+mapcache_cache* mapcache_cache_memcache_create(mapcache_context *ctx) {
+ ctx->set_error(ctx,400,"MEMCACHE support not compiled in this version");
+ return NULL;
+}
#endif
/* vim: ts=2 sts=2 et sw=2
diff --git a/lib/cache_multitier.c b/lib/cache_multitier.c
index 44ea853..6e68ed6 100644
--- a/lib/cache_multitier.c
+++ b/lib/cache_multitier.c
@@ -28,13 +28,21 @@
#include "mapcache.h"
+typedef struct mapcache_cache_multitier mapcache_cache_multitier;
+
+struct mapcache_cache_multitier {
+ mapcache_cache cache;
+ apr_array_header_t *caches;
+};
+
+
static int _mapcache_cache_multitier_tile_exists(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
{
mapcache_cache_multitier *cache = (mapcache_cache_multitier*)pcache;
int i;
for(i=0; i<cache->caches->nelts; i++) {
mapcache_cache *subcache = APR_ARRAY_IDX(cache->caches,i,mapcache_cache*);
- if(subcache->tile_exists(ctx, subcache, tile) == MAPCACHE_TRUE) {
+ if(mapcache_cache_tile_exists(ctx, subcache, tile) == MAPCACHE_TRUE) {
return MAPCACHE_TRUE;
}
}
@@ -47,7 +55,7 @@ static void _mapcache_cache_multitier_tile_delete(mapcache_context *ctx, mapcach
int i;
for(i=0; i<cache->caches->nelts; i++) {
mapcache_cache *subcache = APR_ARRAY_IDX(cache->caches,i,mapcache_cache*);
- subcache->tile_delete(ctx, subcache, tile);
+ mapcache_cache_tile_delete(ctx, subcache, tile);
ctx->clear_errors(ctx); /* ignore errors */
}
}
@@ -65,16 +73,16 @@ static int _mapcache_cache_multitier_tile_get(mapcache_context *ctx, mapcache_ca
mapcache_cache *subcache;
int i,ret;
subcache = APR_ARRAY_IDX(cache->caches,0,mapcache_cache*);
- ret = subcache->tile_get(ctx, subcache, tile);
+ ret = mapcache_cache_tile_get(ctx, subcache, tile);
if(ret == MAPCACHE_CACHE_MISS) {
for(i=1; i<cache->caches->nelts; i++) {
subcache = APR_ARRAY_IDX(cache->caches,i,mapcache_cache*);
- if(subcache->tile_get(ctx, subcache, tile) == MAPCACHE_SUCCESS) {
+ if(mapcache_cache_tile_get(ctx, subcache, tile) == MAPCACHE_SUCCESS) {
ctx->log(ctx,MAPCACHE_DEBUG,"got tile (%s,z=%d,y=%d,x=%d) from secondary cache (%s)",tile->tileset->name, tile->z, tile->y, tile->x, subcache->name);
for(--i;i>=0;i--) {
subcache = APR_ARRAY_IDX(cache->caches,i,mapcache_cache*);
- subcache->tile_set(ctx, subcache, tile);
+ mapcache_cache_tile_set(ctx, subcache, tile);
ctx->clear_errors(ctx); /* silently ignore these errors */
ctx->log(ctx,MAPCACHE_DEBUG,"transferring tile (%s,z=%d,y=%d,x=%d) to cache (%s)",tile->tileset->name, tile->z, tile->y, tile->x, subcache->name);
}
@@ -92,22 +100,14 @@ static void _mapcache_cache_multitier_tile_set(mapcache_context *ctx, mapcache_c
{
mapcache_cache_multitier *cache = (mapcache_cache_multitier*)pcache;
mapcache_cache *subcache = APR_ARRAY_IDX(cache->caches,cache->caches->nelts-1,mapcache_cache*);
- return subcache->tile_set(ctx, subcache, tile);
+ return mapcache_cache_tile_set(ctx, subcache, tile);
}
static void _mapcache_cache_multitier_tile_multi_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tiles, int ntiles)
{
mapcache_cache_multitier *cache = (mapcache_cache_multitier*)pcache;
mapcache_cache *subcache = APR_ARRAY_IDX(cache->caches,cache->caches->nelts-1,mapcache_cache*);
- if(subcache->tile_multi_set) {
- return subcache->tile_multi_set(ctx, subcache, tiles, ntiles);
- } else {
- int i;
- for( i=0;i<ntiles;i++ ) {
- subcache->tile_set(ctx, subcache, &tiles[i]);
- GC_CHECK_ERROR(ctx);
- }
- }
+ return mapcache_cache_tile_multi_set(ctx, subcache, tiles, ntiles);
}
/**
@@ -153,11 +153,11 @@ mapcache_cache* mapcache_cache_multitier_create(mapcache_context *ctx)
}
cache->cache.metadata = apr_table_make(ctx->pool,3);
cache->cache.type = MAPCACHE_CACHE_COMPOSITE;
- cache->cache.tile_delete = _mapcache_cache_multitier_tile_delete;
- cache->cache.tile_get = _mapcache_cache_multitier_tile_get;
- cache->cache.tile_exists = _mapcache_cache_multitier_tile_exists;
- cache->cache.tile_set = _mapcache_cache_multitier_tile_set;
- cache->cache.tile_multi_set = _mapcache_cache_multitier_tile_multi_set;
+ cache->cache._tile_delete = _mapcache_cache_multitier_tile_delete;
+ cache->cache._tile_get = _mapcache_cache_multitier_tile_get;
+ cache->cache._tile_exists = _mapcache_cache_multitier_tile_exists;
+ cache->cache._tile_set = _mapcache_cache_multitier_tile_set;
+ cache->cache._tile_multi_set = _mapcache_cache_multitier_tile_multi_set;
cache->cache.configuration_post_config = _mapcache_cache_multitier_configuration_post_config;
cache->cache.configuration_parse_xml = _mapcache_cache_multitier_configuration_parse_xml;
return (mapcache_cache*)cache;
diff --git a/lib/cache_rest.c b/lib/cache_rest.c
index 401fd46..2efc407 100644
--- a/lib/cache_rest.c
+++ b/lib/cache_rest.c
@@ -35,12 +35,139 @@
#include <curl/curl.h>
#include <apr_base64.h>
#include <apr_md5.h>
+#include <math.h>
+#include <apr_file_io.h>
+
+typedef struct mapcache_cache_rest mapcache_cache_rest;
+typedef struct mapcache_cache_s3 mapcache_cache_s3;
+typedef struct mapcache_cache_azure mapcache_cache_azure;
+typedef struct mapcache_cache_google mapcache_cache_google;
+
+typedef enum {
+ MAPCACHE_REST_METHOD_GET,
+ MAPCACHE_REST_METHOD_HEAD,
+ MAPCACHE_REST_METHOD_PUT,
+ MAPCACHE_REST_METHOD_POST,
+ MAPCACHE_REST_METHOD_DELETE
+} mapcache_rest_method;
+
+typedef enum {
+ MAPCACHE_REST_PROVIDER_NONE,
+ MAPCACHE_REST_PROVIDER_S3,
+ MAPCACHE_REST_PROVIDER_AZURE,
+ MAPCACHE_REST_PROVIDER_GOOGLE
+} mapcache_rest_provider;
+
+void sha256(const unsigned char *message, unsigned int len, unsigned char *digest);
+void hmac_sha256(const unsigned char *message, unsigned int message_len,
+ const unsigned char *key, unsigned int key_size,
+ unsigned char *mac, unsigned mac_size);
+void hmac_sha1(const char *message, unsigned int message_len,
+ const unsigned char *key, unsigned int key_size,
+ void *mac);
+void sha_hex_encode(unsigned char *sha, unsigned int sha_size);
+char *base64_encode(apr_pool_t *pool, const unsigned char *data, size_t input_length);
+
+typedef struct mapcache_rest_operation mapcache_rest_operation;
+struct mapcache_rest_operation {
+ apr_table_t *headers;
+ mapcache_rest_method method;
+ char *tile_url;
+ char *header_file;
+ void (*add_headers)(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers);
+};
+
+typedef struct mapcache_rest_configuration mapcache_rest_configuration;
+struct mapcache_rest_configuration {
+ apr_table_t *common_headers;
+ char *tile_url;
+ char *header_file;
+ mapcache_rest_operation has_tile;
+ mapcache_rest_operation get_tile;
+ mapcache_rest_operation set_tile;
+ mapcache_rest_operation multi_set_tile;
+ mapcache_rest_operation delete_tile;
+ void (*add_headers)(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers);
+};
+
+/**\class mapcache_cache_rest
+ * \brief a mapcache_cache on a 3rd party HTTP Rest API
+ * \implements mapcache_cache
+ */
+struct mapcache_cache_rest {
+ mapcache_cache cache;
+ mapcache_rest_configuration rest;
+ int use_redirects;
+ int timeout;
+ int connection_timeout;
+ int detect_blank;
+ mapcache_rest_provider provider;
+};
+
+struct mapcache_cache_s3 {
+ mapcache_cache_rest cache;
+ char *id;
+ char *secret;
+ char *region;
+ char *credentials_file;
+};
+
+struct mapcache_cache_azure {
+ mapcache_cache_rest cache;
+ char *id;
+ char *secret;
+ char *container;
+};
+
+struct mapcache_cache_google {
+ mapcache_cache_rest cache;
+ char *access;
+ char *secret;
+};
typedef struct {
mapcache_buffer *buffer;
size_t offset;
} buffer_struct;
+struct rest_conn_params {
+ mapcache_cache_rest *cache;
+};
+
+void mapcache_rest_connection_constructor(mapcache_context *ctx, void **conn_, void *params) {
+ CURL *curl_handle = curl_easy_init();
+ if(!curl_handle) {
+ ctx->set_error(ctx,500,"failed to create curl handle");
+ *conn_ = NULL;
+ return;
+ }
+ *conn_ = curl_handle;
+}
+
+void mapcache_rest_connection_destructor(void *conn_) {
+ CURL *curl_handle = (CURL*) conn_;
+ curl_easy_cleanup(curl_handle);
+}
+
+static mapcache_pooled_connection* _rest_get_connection(mapcache_context *ctx, mapcache_cache_rest *cache, mapcache_tile *tile)
+{
+ mapcache_pooled_connection *pc;
+ struct rest_conn_params params;
+
+ params.cache = cache;
+
+ pc = mapcache_connection_pool_get_connection(ctx,cache->cache.name,mapcache_rest_connection_constructor,
+ mapcache_rest_connection_destructor, ¶ms);
+ if(!GC_HAS_ERROR(ctx) && pc && pc->connection) {
+ CURL *curl_handle = (CURL*)pc->connection;
+ curl_easy_reset(curl_handle);
+ curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT, cache->connection_timeout);
+ curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, cache->timeout);
+ }
+
+ return pc;
+}
+
static size_t buffer_read_callback(void *ptr, size_t size, size_t nmemb, void *stream)
{
buffer_struct *buffer = (buffer_struct*)stream;
@@ -69,7 +196,11 @@ static void _set_headers(mapcache_context *ctx, CURL *curl, apr_table_t *headers
apr_table_entry_t *elts = (apr_table_entry_t *) array->elts;
int i;
for (i = 0; i < array->nelts; i++) {
- curl_headers = curl_slist_append(curl_headers, apr_pstrcat(ctx->pool,elts[i].key,": ",elts[i].val,NULL));
+ if(strlen(elts[i].val) > 0) {
+ curl_headers = curl_slist_append(curl_headers, apr_pstrcat(ctx->pool,elts[i].key,": ",elts[i].val,NULL));
+ } else {
+ curl_headers = curl_slist_append(curl_headers, apr_pstrcat(ctx->pool,elts[i].key,":",NULL));
+ }
}
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_headers);
}
@@ -93,7 +224,6 @@ static void _put_request(mapcache_context *ctx, CURL *curl, mapcache_buffer *buf
apr_table_unset(headers, "Content-Length");
#endif
- _set_headers(ctx, curl, headers);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
@@ -106,6 +236,10 @@ static void _put_request(mapcache_context *ctx, CURL *curl, mapcache_buffer *buf
/* HTTP PUT please */
curl_easy_setopt(curl, CURLOPT_PUT, 1L);
+ /* don't use an Expect: 100 Continue header */
+ apr_table_set(headers, "Expect", "");
+ _set_headers(ctx, curl, headers);
+
/* specify target URL, and note that this URL should include a file
* name, not only a directory */
curl_easy_setopt(curl, CURLOPT_URL, url);
@@ -140,19 +274,11 @@ static void _put_request(mapcache_context *ctx, CURL *curl, mapcache_buffer *buf
}
-static int _head_request(mapcache_context *ctx, char *url, apr_table_t *headers) {
+static int _head_request(mapcache_context *ctx, CURL *curl, char *url, apr_table_t *headers) {
- CURL *curl;
CURLcode res;
long http_code;
-
- curl = curl_easy_init();
-
- if(!curl) {
- ctx->set_error(ctx,500,"failed to create curl handle");
- return -1;
- }
-
+
_set_headers(ctx, curl, headers);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
@@ -173,24 +299,14 @@ static int _head_request(mapcache_context *ctx, char *url, apr_table_t *headers)
curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code);
}
- /* always cleanup */
- curl_easy_cleanup(curl);
-
return (int)http_code;
}
-static int _delete_request(mapcache_context *ctx, char *url, apr_table_t *headers) {
+static int _delete_request(mapcache_context *ctx, CURL *curl, char *url, apr_table_t *headers) {
- CURL *curl;
CURLcode res;
long http_code;
- curl = curl_easy_init();
- if(!curl) {
- ctx->set_error(ctx,500,"failed to create curl handle");
- return -1;
- }
-
_set_headers(ctx, curl, headers);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
@@ -200,36 +316,27 @@ static int _delete_request(mapcache_context *ctx, char *url, apr_table_t *header
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
+ curl_easy_setopt(curl, CURLOPT_NOBODY, 1);
/* Now run off and do what you've been told! */
res = curl_easy_perform(curl);
/* Check for errors */
if(res != CURLE_OK) {
- ctx->set_error(ctx, 500, "curl_easy_perform() failed in rest head %s",curl_easy_strerror(res));
+ ctx->set_error(ctx, 500, "curl_easy_perform() failed in rest delete %s",curl_easy_strerror(res));
http_code = 500;
} else {
curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code);
}
- /* always cleanup */
- curl_easy_cleanup(curl);
-
return (int)http_code;
}
-static mapcache_buffer* _get_request(mapcache_context *ctx, char *url, apr_table_t *headers) {
+static mapcache_buffer* _get_request(mapcache_context *ctx, CURL *curl, char *url, apr_table_t *headers) {
- CURL *curl;
CURLcode res;
mapcache_buffer *data = NULL;
long http_code;
- curl = curl_easy_init();
- if(!curl) {
- ctx->set_error(ctx,500,"failed to create curl handle");
- return NULL;
- }
-
_set_headers(ctx, curl, headers);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
@@ -277,28 +384,89 @@ static mapcache_buffer* _get_request(mapcache_context *ctx, char *url, apr_table
}
}
- /* always cleanup */
- curl_easy_cleanup(curl);
-
return data;
}
+/**
+ * @brief _mapcache_cache_rest_add_headers_from_file populate header table from entries found in file
+ * @param ctx
+ * @param file the file from which headers should be read
+ * @param headers the output table which will be populated
+ */
+void _mapcache_cache_rest_add_headers_from_file(mapcache_context *ctx, char *file, apr_table_t *headers) {
+ apr_status_t rv;
+ apr_file_t *f;
+ if((rv=apr_file_open(&f, file, APR_FOPEN_READ|APR_FOPEN_BUFFERED|APR_FOPEN_BINARY,APR_OS_DEFAULT,
+ ctx->pool)) == APR_SUCCESS) {
+ char line[8096];
+ while( (rv = apr_file_gets(line,8096,f))== APR_SUCCESS) {
+ char *header_name=line, *header_val=line, *header_endval;
+ int found_token = MAPCACHE_FALSE;
+ /*search for header delimiter (:)*/
+ while(header_val && *header_val) {
+ if(*header_val == ':') {
+ *header_val = '\0';
+ found_token = MAPCACHE_TRUE;
+ break;
+ }
+ header_val++;
+ }
+ if(!found_token) {
+ /* malformed line, silently skip it */
+ continue;
+ }
+
+ header_val++;
+
+ if(!*header_val) {
+ /* malformed/empty line, skip it */
+ continue;
+ }
+
+ header_endval = header_val;
+ while(*header_endval) {
+ if(*header_endval == '\r' || *header_endval == '\n') {
+ *header_endval = '\0';
+ break;
+ }
+ header_endval++;
+ }
+
+ if(!*header_val) {
+ /* empty header value, skip it */
+ continue;
+ }
+
+ apr_table_set(headers, header_name, header_val);
+ }
+ apr_file_close(f);
+ } else {
+ ctx->set_error(ctx,500,"rest cache: failed to access header file");
+ }
+}
+
apr_table_t* _mapcache_cache_rest_headers(mapcache_context *ctx, mapcache_tile *tile, mapcache_rest_configuration *config,
mapcache_rest_operation *operation) {
apr_table_t *ret = apr_table_make(ctx->pool,3);
const apr_array_header_t *array;
if(config->common_headers) {
- apr_table_entry_t *elts;
- int i;
+ apr_table_entry_t *elts;
+ int i;
array = apr_table_elts(config->common_headers);
elts = (apr_table_entry_t *) array->elts;
for (i = 0; i < array->nelts; i++) {
apr_table_set(ret, elts[i].key,elts[i].val);
}
}
+ if(config->header_file) {
+ _mapcache_cache_rest_add_headers_from_file(ctx,config->header_file,ret);
+ if(GC_HAS_ERROR(ctx)) {
+ return NULL;
+ }
+ }
if(operation->headers) {
- apr_table_entry_t *elts;
+ apr_table_entry_t *elts;
int i;
array = apr_table_elts(operation->headers);
elts = (apr_table_entry_t *) array->elts;
@@ -306,6 +474,12 @@ apr_table_t* _mapcache_cache_rest_headers(mapcache_context *ctx, mapcache_tile *
apr_table_set(ret, elts[i].key,elts[i].val);
}
}
+ if(operation->header_file) {
+ _mapcache_cache_rest_add_headers_from_file(ctx,operation->header_file,ret);
+ if(GC_HAS_ERROR(ctx)) {
+ return NULL;
+ }
+ }
return ret;
}
@@ -376,14 +550,25 @@ static void _mapcache_cache_rest_tile_url(mapcache_context *ctx, mapcache_tile *
apr_psprintf(ctx->pool,"%d",
tile->grid_link->grid->nlevels - tile->z - 1));
if(tile->dimensions) {
- char *dimstring="";
- const apr_array_header_t *elts = apr_table_elts(tile->dimensions);
- int i = elts->nelts;
- while(i--) {
- apr_table_entry_t *entry = &(APR_ARRAY_IDX(elts,i,apr_table_entry_t));
- dimstring = apr_pstrcat(ctx->pool,dimstring,"#",entry->key,"#",entry->val,NULL);
+ int i;
+ if(strstr(*url,"{dim")) {
+ char *dimstring="";
+ i = tile->dimensions->nelts;
+ while(i--) {
+ char *single_dim;
+ mapcache_requested_dimension *entry = APR_ARRAY_IDX(tile->dimensions,i,mapcache_requested_dimension*);
+ if(!entry->cached_value) {
+ ctx->set_error(ctx,500,"BUG: dimension (%s) not defined",entry->dimension->name);
+ return;
+ }
+ dimstring = apr_pstrcat(ctx->pool,dimstring,"#",entry->dimension->name,"#",entry->cached_value,NULL);
+ single_dim = apr_pstrcat(ctx->pool,"{dim:",entry->dimension->name,"}",NULL);
+ if(strstr(*url,single_dim)) {
+ *url = mapcache_util_str_replace(ctx->pool,*url, single_dim, entry->cached_value);
+ }
+ }
+ *url = mapcache_util_str_replace(ctx->pool,*url, "{dim}", dimstring);
}
- *url = mapcache_util_str_replace(ctx->pool,*url, "{dim}", dimstring);
}
/* url-encode everything after the host name */
@@ -404,7 +589,7 @@ static void _mapcache_cache_rest_tile_url(mapcache_context *ctx, mapcache_tile *
*url = apr_pstrcat(ctx->pool,*url,path,NULL);
- /*ctx->log(ctx,MAPCACHE_WARN,"rest url: %s",*url);*/
+ /*ctx->log(ctx,MAPCACHE_DEBUG,"rest url: %s",*url);*/
}
@@ -455,6 +640,13 @@ static void header_gnome_sort(char **headers, int size)
}
}
+static const char* my_apr_table_get(apr_table_t *t, char *key) {
+ const char *val = apr_table_get(t,key);
+ if(!val) val = "";
+ return val;
+}
+
+
static void _mapcache_cache_google_headers_add(mapcache_context *ctx, const char* method, mapcache_cache_rest *rcache, mapcache_tile *tile, char *url, apr_table_t *headers)
{
char *stringToSign, **aheaders, *resource = url, x_amz_date[64];
@@ -483,11 +675,10 @@ static void _mapcache_cache_google_headers_add(mapcache_context *ctx, const char
apr_table_set(headers, "Content-MD5", b64);
}
- head = apr_table_get(headers, "Content-MD5");
- if(!head) head = "";
+ head = my_apr_table_get(headers, "Content-MD5");
stringToSign=apr_pstrcat(ctx->pool, method, "\n", head, "\n", NULL);
- head = apr_table_get(headers, "Content-Type");
- if(!head) head = ""; stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
+ head = my_apr_table_get(headers, "Content-Type");
+ stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
/* Date: header, left empty as we are using x-amz-date */
stringToSign=apr_pstrcat(ctx->pool, stringToSign, "\n", NULL);
@@ -532,6 +723,7 @@ static void _mapcache_cache_google_headers_add(mapcache_context *ctx, const char
apr_table_set( headers, "Authorization", apr_pstrcat(ctx->pool,"AWS ", google->access, ":", b64, NULL));
}
+
static void _mapcache_cache_azure_headers_add(mapcache_context *ctx, const char* method, mapcache_cache_rest *rcache, mapcache_tile *tile, char *url, apr_table_t *headers)
{
char *stringToSign, **aheaders, *canonical_headers="", *canonical_resource=NULL, *resource = url, x_ms_date[64];
@@ -556,28 +748,28 @@ static void _mapcache_cache_azure_headers_add(mapcache_context *ctx, const char*
apr_table_set(headers,"x-ms-blob-type","BlockBlob");
stringToSign = apr_pstrcat(ctx->pool, method, "\n", NULL);
- head = apr_table_get(headers, "Content-Encoding");
- if(!head) head = ""; stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
- head = apr_table_get(headers, "Content-Language");
- if(!head) head = ""; stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
- head = apr_table_get(headers, "Content-Length");
- if(!head) head = ""; stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
- head = apr_table_get(headers, "Content-MD5");
- if(!head) head = ""; stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
- head = apr_table_get(headers, "Content-Type");
- if(!head) head = ""; stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
- head = apr_table_get(headers, "Date");
- if(!head) head = ""; stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
- head = apr_table_get(headers, "If-Modified-Since");
- if(!head) head = ""; stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
- head = apr_table_get(headers, "If-Match");
- if(!head) head = ""; stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
- head = apr_table_get(headers, "If-None-Match");
- if(!head) head = ""; stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
- head = apr_table_get(headers, "If-Unmodified-Since");
- if(!head) head = ""; stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
- head = apr_table_get(headers, "Range");
- if(!head) head = ""; stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
+ head = my_apr_table_get(headers, "Content-Encoding");
+ stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
+ head = my_apr_table_get(headers, "Content-Language");
+ stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
+ head = my_apr_table_get(headers, "Content-Length");
+ stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
+ head = my_apr_table_get(headers, "Content-MD5");
+ stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
+ head = my_apr_table_get(headers, "Content-Type");
+ stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
+ head = my_apr_table_get(headers, "Date");
+ stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
+ head = my_apr_table_get(headers, "If-Modified-Since");
+ stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
+ head = my_apr_table_get(headers, "If-Match");
+ stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
+ head = my_apr_table_get(headers, "If-None-Match");
+ stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
+ head = my_apr_table_get(headers, "If-Unmodified-Since");
+ stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
+ head = my_apr_table_get(headers, "Range");
+ stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
ahead = apr_table_elts(headers);
aheaders = apr_pcalloc(ctx->pool, ahead->nelts * sizeof(char*));
@@ -627,6 +819,21 @@ static void _mapcache_cache_azure_headers_add(mapcache_context *ctx, const char*
}
+
+static void _remove_lineends(char *str) {
+ if(str) {
+ size_t len = strlen(str);
+ while(len>0) {
+ if(str[len-1] == '\n' || str[len-1] == '\r') {
+ str[len-1] = '\0';
+ len--;
+ } else {
+ break;
+ }
+ }
+ }
+}
+
static void _mapcache_cache_s3_headers_add(mapcache_context *ctx, const char* method, mapcache_cache_rest *rcache, mapcache_tile *tile, char *url, apr_table_t *headers)
{
unsigned char sha1[65],sha2[65];
@@ -637,11 +844,47 @@ static void _mapcache_cache_s3_headers_add(mapcache_context *ctx, const char* me
char *tosign, *key, *canonical_request, x_amz_date[64], *resource = url, **aheaders, *auth;
apr_table_entry_t *elts;
mapcache_cache_s3 *s3;
+ char *aws_access_key_id = NULL, *aws_secret_access_key = NULL, *aws_security_token = NULL;
sha1[64]=sha2[64]=0;
assert(rcache->provider == MAPCACHE_REST_PROVIDER_S3);
s3 = (mapcache_cache_s3*)rcache;
+ if(s3->credentials_file) {
+ apr_status_t rv;
+ apr_file_t *f;
+ if((rv=apr_file_open(&f, s3->credentials_file,
+ APR_FOPEN_READ|APR_FOPEN_BUFFERED|APR_FOPEN_BINARY,APR_OS_DEFAULT,
+ ctx->pool)) == APR_SUCCESS) {
+ char line[1024];
+ if( (rv = apr_file_gets(line,1024,f))== APR_SUCCESS) {
+ _remove_lineends(line);
+ aws_access_key_id = apr_pstrdup(ctx->pool,line);
+ }
+ if( (rv = apr_file_gets(line,1024,f))== APR_SUCCESS) {
+ _remove_lineends(line);
+ aws_secret_access_key = apr_pstrdup(ctx->pool,line);
+ }
+ if( (rv = apr_file_gets(line,1024,f))== APR_SUCCESS) {
+ _remove_lineends(line);
+ aws_security_token = apr_pstrdup(ctx->pool,line);
+ }
+ apr_file_close(f);
+ if(!aws_access_key_id || !*aws_access_key_id|| !aws_secret_access_key ||!*aws_secret_access_key) {
+ ctx->set_error(ctx,500,"failed to read access or secret key from credentials file");
+ }
+ if(aws_security_token && !*aws_security_token) {
+ aws_security_token = NULL;
+ }
+ } else {
+ ctx->set_error(ctx,500,"failed to access S3 credential config");
+ }
+ } else {
+ aws_access_key_id = s3->id;
+ aws_secret_access_key = s3->secret;
+ aws_security_token = NULL;
+ }
+
if(!strcmp(method,"PUT")) {
assert(tile->encoded_data);
sha256((unsigned char*)tile->encoded_data->buf, tile->encoded_data->size, sha1);
@@ -667,6 +910,10 @@ static void _mapcache_cache_s3_headers_add(mapcache_context *ctx, const char* me
strftime(x_amz_date, sizeof(x_amz_date), "%Y%m%dT%H%M%SZ", tnow);
apr_table_set(headers, "x-amz-date", x_amz_date);
+ if(aws_security_token) {
+ apr_table_set(headers, "x-amz-security-token", aws_security_token);
+ }
+
canonical_request = apr_pstrcat(ctx->pool, method, "\n" ,resource, "\n\n",NULL);
ahead = apr_table_elts(headers);
@@ -703,7 +950,7 @@ static void _mapcache_cache_s3_headers_add(mapcache_context *ctx, const char* me
tosign = apr_pstrcat(ctx->pool, tosign, x_amz_date, "/", s3->region, "/s3/aws4_request\n", sha1,NULL);
//printf("key to sign: %s\n",tosign);
- key = apr_pstrcat(ctx->pool, "AWS4", s3->secret, NULL);
+ key = apr_pstrcat(ctx->pool, "AWS4", aws_secret_access_key, NULL);
hmac_sha256((unsigned char*)x_amz_date, 8, (unsigned char*)key, strlen(key), sha1, 32);
hmac_sha256((unsigned char*)s3->region, strlen(s3->region), sha1, 32, sha2, 32);
hmac_sha256((unsigned char*)"s3", 2, sha2, 32, sha1, 32);
@@ -712,7 +959,7 @@ static void _mapcache_cache_s3_headers_add(mapcache_context *ctx, const char* me
sha_hex_encode(sha1,32);
- auth = apr_pstrcat(ctx->pool, "AWS4-HMAC-SHA256 Credential=",s3->id,"/",x_amz_date,"/",s3->region,"/s3/aws4_request,SignedHeaders=",NULL);
+ auth = apr_pstrcat(ctx->pool, "AWS4-HMAC-SHA256 Credential=",aws_access_key_id,"/",x_amz_date,"/",s3->region,"/s3/aws4_request,SignedHeaders=",NULL);
for(i=0; i<ahead->nelts; i++) {
if(i==ahead->nelts-1) {
@@ -770,8 +1017,15 @@ static int _mapcache_cache_rest_has_tile(mapcache_context *ctx, mapcache_cache *
char *url;
apr_table_t *headers;
int status;
+ mapcache_pooled_connection *pc;
+ CURL *curl;
+
_mapcache_cache_rest_tile_url(ctx, tile, &rcache->rest, &rcache->rest.has_tile, &url);
headers = _mapcache_cache_rest_headers(ctx, tile, &rcache->rest, &rcache->rest.has_tile);
+
+ if(GC_HAS_ERROR(ctx))
+ return MAPCACHE_FAILURE;
+
if(rcache->rest.add_headers) {
rcache->rest.add_headers(ctx,rcache,tile,url,headers);
}
@@ -779,11 +1033,22 @@ static int _mapcache_cache_rest_has_tile(mapcache_context *ctx, mapcache_cache *
rcache->rest.has_tile.add_headers(ctx,rcache,tile,url,headers);
}
- status = _head_request(ctx, url, headers);
-
+ pc = _rest_get_connection(ctx, rcache, tile);
if(GC_HAS_ERROR(ctx))
return MAPCACHE_FAILURE;
+ curl = pc->connection;
+
+ status = _head_request(ctx, curl, url, headers);
+
+
+ if(GC_HAS_ERROR(ctx)) {
+ mapcache_connection_pool_invalidate_connection(ctx,pc);
+ return MAPCACHE_FAILURE;
+ }
+
+ mapcache_connection_pool_release_connection(ctx,pc);
+
if( status == 200)
return MAPCACHE_TRUE;
else
@@ -796,8 +1061,12 @@ static void _mapcache_cache_rest_delete(mapcache_context *ctx, mapcache_cache *p
char *url;
apr_table_t *headers;
int status;
+ mapcache_pooled_connection *pc;
+ CURL *curl;
_mapcache_cache_rest_tile_url(ctx, tile, &rcache->rest, &rcache->rest.delete_tile, &url);
headers = _mapcache_cache_rest_headers(ctx, tile, &rcache->rest, &rcache->rest.delete_tile);
+ GC_CHECK_ERROR(ctx);
+
if(rcache->rest.add_headers) {
rcache->rest.add_headers(ctx,rcache,tile,url,headers);
}
@@ -805,10 +1074,19 @@ static void _mapcache_cache_rest_delete(mapcache_context *ctx, mapcache_cache *p
rcache->rest.delete_tile.add_headers(ctx,rcache,tile,url,headers);
}
- status = _delete_request(ctx, url, headers);
+ pc = _rest_get_connection(ctx, rcache, tile);
GC_CHECK_ERROR(ctx);
- if(status!=200 && status!=202 && status!=204) {
+ curl = pc->connection;
+
+ status = _delete_request(ctx, curl, url, headers);
+ if(GC_HAS_ERROR(ctx)) {
+ mapcache_connection_pool_invalidate_connection(ctx,pc);
+ return;
+ }
+ mapcache_connection_pool_release_connection(ctx,pc);
+
+ if(status!=200 && status!=202 && status!=204 && status!=404 && status!=410) {
//ctx->set_error(ctx,500,"rest delete returned code %d", status);
}
}
@@ -826,99 +1104,110 @@ static int _mapcache_cache_rest_get(mapcache_context *ctx, mapcache_cache *pcach
mapcache_cache_rest *rcache = (mapcache_cache_rest*)pcache;
char *url;
apr_table_t *headers;
+ mapcache_pooled_connection *pc;
+ CURL *curl;
_mapcache_cache_rest_tile_url(ctx, tile, &rcache->rest, &rcache->rest.get_tile, &url);
if(tile->allow_redirect && rcache->use_redirects) {
tile->redirect = url;
return MAPCACHE_SUCCESS;
}
headers = _mapcache_cache_rest_headers(ctx, tile, &rcache->rest, &rcache->rest.get_tile);
+
+ if(GC_HAS_ERROR(ctx))
+ return MAPCACHE_FAILURE;
+
if(rcache->rest.add_headers) {
rcache->rest.add_headers(ctx,rcache,tile,url,headers);
}
if(rcache->rest.get_tile.add_headers) {
rcache->rest.get_tile.add_headers(ctx,rcache,tile,url,headers);
}
- tile->encoded_data = _get_request(ctx, url, headers);
+
+ pc = _rest_get_connection(ctx, rcache, tile);
if(GC_HAS_ERROR(ctx))
return MAPCACHE_FAILURE;
+ curl = pc->connection;
+
+ tile->encoded_data = _get_request(ctx, curl, url, headers);
+
+ if(GC_HAS_ERROR(ctx)) {
+ mapcache_connection_pool_invalidate_connection(ctx,pc);
+ return MAPCACHE_FAILURE;
+ }
+ mapcache_connection_pool_release_connection(ctx,pc);
+
if(!tile->encoded_data)
return MAPCACHE_CACHE_MISS;
return MAPCACHE_SUCCESS;
}
-static void _mapcache_cache_rest_multi_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tiles, int ntiles) {
+static void _mapcache_cache_rest_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) {
mapcache_cache_rest *rcache = (mapcache_cache_rest*)pcache;
char *url;
apr_table_t *headers;
- CURL *curl = curl_easy_init();
- int i;
-
- if(!curl) {
- ctx->set_error(ctx,500,"failed to create curl handle");
- return;
- }
+ mapcache_pooled_connection *pc;
+ CURL *curl;
- for(i=0; i<ntiles; i++) {
- mapcache_tile *tile;
- if(i)
- curl_easy_reset(curl);
- tile = tiles + i;
- _mapcache_cache_rest_tile_url(ctx, tile, &rcache->rest, &rcache->rest.set_tile, &url);
- headers = _mapcache_cache_rest_headers(ctx, tile, &rcache->rest, &rcache->rest.set_tile);
- if(!tile->encoded_data) {
- tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format);
- if(GC_HAS_ERROR(ctx)) {
- goto multi_put_cleanup;
- }
+ if(rcache->detect_blank) {
+ if(tile->nodata) {
+ return;
}
-
- apr_table_set(headers,"Content-Length",apr_psprintf(ctx->pool,"%lu",tile->encoded_data->size));
- if(tile->tileset->format && tile->tileset->format->mime_type)
- apr_table_set(headers, "Content-Type", tile->tileset->format->mime_type);
- else {
- mapcache_image_format_type imgfmt = mapcache_imageio_header_sniff(ctx,tile->encoded_data);
- if(imgfmt == GC_JPEG) {
- apr_table_set(headers, "Content-Type", "image/jpeg");
- } else if (imgfmt == GC_PNG) {
- apr_table_set(headers, "Content-Type", "image/png");
+ 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) {
+ if(tile->raw_image->data[3] == 0) {
+ /* We have a blank (uniform) image who's first pixel is fully transparent, thus the whole image is transparent */
+ tile->nodata = 1;
+ return;
}
}
+ }
- if(rcache->rest.add_headers) {
- rcache->rest.add_headers(ctx,rcache,tile,url,headers);
- }
- if(rcache->rest.set_tile.add_headers) {
- rcache->rest.set_tile.add_headers(ctx,rcache,tile,url,headers);
- }
- _put_request(ctx, curl, tile->encoded_data, url, headers);
- if(GC_HAS_ERROR(ctx)) {
- goto multi_put_cleanup;
+ _mapcache_cache_rest_tile_url(ctx, tile, &rcache->rest, &rcache->rest.set_tile, &url);
+ headers = _mapcache_cache_rest_headers(ctx, tile, &rcache->rest, &rcache->rest.set_tile);
+ 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);
+ }
+
+ apr_table_set(headers,"Content-Length",apr_psprintf(ctx->pool,"%lu",tile->encoded_data->size));
+ if(tile->tileset->format && tile->tileset->format->mime_type)
+ apr_table_set(headers, "Content-Type", tile->tileset->format->mime_type);
+ else {
+ mapcache_image_format_type imgfmt = mapcache_imageio_header_sniff(ctx,tile->encoded_data);
+ if(imgfmt == GC_JPEG) {
+ apr_table_set(headers, "Content-Type", "image/jpeg");
+ } else if (imgfmt == GC_PNG) {
+ apr_table_set(headers, "Content-Type", "image/png");
}
}
-multi_put_cleanup:
- /* always cleanup */
- curl_easy_cleanup(curl);
+ if(rcache->rest.add_headers) {
+ rcache->rest.add_headers(ctx,rcache,tile,url,headers);
+ }
+ if(rcache->rest.set_tile.add_headers) {
+ rcache->rest.set_tile.add_headers(ctx,rcache,tile,url,headers);
+ }
-}
+ pc = _rest_get_connection(ctx, rcache, tile);
+ GC_CHECK_ERROR(ctx);
+ curl = pc->connection;
-/**
- * \brief write tile data to rest backend
- *
- * 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_rest
- * \sa mapcache_cache::tile_set()
- */
-static void _mapcache_cache_rest_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
-{
- return _mapcache_cache_rest_multi_set(ctx, pcache, tile, 1);
-}
+ _put_request(ctx, curl, tile->encoded_data, url, headers);
+ if(GC_HAS_ERROR(ctx)) {
+ mapcache_connection_pool_invalidate_connection(ctx,pc);
+ return;
+ }
+ mapcache_connection_pool_release_connection(ctx,pc);
+}
static void _mapcache_cache_rest_operation_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *cache, mapcache_rest_operation *op)
{
@@ -930,6 +1219,9 @@ static void _mapcache_cache_rest_operation_parse_xml(mapcache_context *ctx, ezxm
apr_table_set(op->headers, header_node->name, header_node->txt);
}
}
+ if ((cur_node = ezxml_child(node,"header_file")) != NULL) {
+ op->header_file = apr_pstrdup(ctx->pool, cur_node->txt);
+ }
}
/**
@@ -947,6 +1239,37 @@ static void _mapcache_cache_rest_configuration_parse_xml(mapcache_context *ctx,
dcache->use_redirects = 1;
}
}
+ if ((cur_node = ezxml_child(node,"connection_timeout")) != NULL) {
+ char *endptr;
+ dcache->connection_timeout = (int)strtol(cur_node->txt,&endptr,10);
+ if(*endptr != 0 || dcache->connection_timeout<1) {
+ ctx->set_error(ctx,400,"invalid rest cache <connection_timeout> \"%s\" (positive integer expected)",
+ cur_node->txt);
+ return;
+ }
+ } else {
+ dcache->connection_timeout = 30;
+ }
+
+ if ((cur_node = ezxml_child(node,"timeout")) != NULL) {
+ char *endptr;
+ dcache->timeout = (int)strtol(cur_node->txt,&endptr,10);
+ if(*endptr != 0 || dcache->timeout<1) {
+ ctx->set_error(ctx,400,"invalid rest cache <timeout> \"%s\" (positive integer expected)",
+ cur_node->txt);
+ return;
+ }
+ } else {
+ dcache->timeout = 120;
+ }
+
+ dcache->detect_blank = 0;
+ if ((cur_node = ezxml_child(node, "detect_blank")) != NULL) {
+ if(strcasecmp(cur_node->txt,"false")) {
+ dcache->detect_blank = 1;
+ }
+ }
+
if ((cur_node = ezxml_child(node,"headers")) != NULL) {
ezxml_t header_node;
dcache->rest.common_headers = apr_table_make(ctx->pool,3);
@@ -954,6 +1277,12 @@ static void _mapcache_cache_rest_configuration_parse_xml(mapcache_context *ctx,
apr_table_set(dcache->rest.common_headers, header_node->name, header_node->txt);
}
}
+
+ if ((cur_node = ezxml_child(node,"header_file")) != NULL) {
+ dcache->rest.header_file = apr_pstrdup(ctx->pool, cur_node->txt);
+ }
+
+
for(cur_node = ezxml_child(node,"operation"); cur_node; cur_node = cur_node->next) {
char *type = (char*)ezxml_attr(cur_node,"type");
if(!type) {
@@ -1006,17 +1335,25 @@ static void _mapcache_cache_s3_configuration_parse_xml(mapcache_context *ctx, ez
mapcache_cache_s3 *s3 = (mapcache_cache_s3*)cache;
_mapcache_cache_rest_configuration_parse_xml(ctx, node, cache, config);
GC_CHECK_ERROR(ctx);
- if ((cur_node = ezxml_child(node,"id")) != NULL) {
- s3->id = apr_pstrdup(ctx->pool, cur_node->txt);
+ if ((cur_node = ezxml_child(node,"credentials_file")) != NULL) {
+ s3->credentials_file = apr_pstrdup(ctx->pool, cur_node->txt);
} else {
- ctx->set_error(ctx,400,"s3 cache (%s) is missing required <id> child", cache->name);
- return;
- }
- if ((cur_node = ezxml_child(node,"secret")) != NULL) {
- s3->secret = apr_pstrdup(ctx->pool, cur_node->txt);
- } else {
- ctx->set_error(ctx,400,"s3 cache (%s) is missing required <secret> child", cache->name);
- return;
+ if ((cur_node = ezxml_child(node,"id")) != NULL) {
+ s3->id = apr_pstrdup(ctx->pool, cur_node->txt);
+ } else if ( getenv("AWS_ACCESS_KEY_ID")) {
+ s3->id = apr_pstrdup(ctx->pool,getenv("AWS_ACCESS_KEY_ID"));
+ } else {
+ ctx->set_error(ctx,400,"s3 cache (%s) is missing required <id> child or AWS_ACCESS_KEY_ID environment", cache->name);
+ return;
+ }
+ if ((cur_node = ezxml_child(node,"secret")) != NULL) {
+ s3->secret = apr_pstrdup(ctx->pool, cur_node->txt);
+ } else if ( getenv("AWS_SECRET_ACCESS_KEY")) {
+ s3->secret = apr_pstrdup(ctx->pool,getenv("AWS_SECRET_ACCESS_KEY"));
+ } else {
+ ctx->set_error(ctx,400,"s3 cache (%s) is missing required <secret> child or AWS_SECRET_ACCESS_KEY environment", cache->name);
+ return;
+ }
}
if ((cur_node = ezxml_child(node,"region")) != NULL) {
s3->region = apr_pstrdup(ctx->pool, cur_node->txt);
@@ -1078,7 +1415,6 @@ static void _mapcache_cache_rest_configuration_post_config(mapcache_context *ctx
}
void mapcache_cache_rest_init(mapcache_context *ctx, mapcache_cache_rest *cache) {
- cache->retry_count = 0;
cache->use_redirects = 0;
cache->rest.get_tile.method = MAPCACHE_REST_METHOD_GET;
cache->rest.set_tile.method = MAPCACHE_REST_METHOD_PUT;
@@ -1087,11 +1423,10 @@ void mapcache_cache_rest_init(mapcache_context *ctx, mapcache_cache_rest *cache)
cache->rest.has_tile.method = MAPCACHE_REST_METHOD_HEAD;
cache->cache.metadata = apr_table_make(ctx->pool,3);
cache->cache.type = MAPCACHE_CACHE_REST;
- cache->cache.tile_delete = _mapcache_cache_rest_delete;
- cache->cache.tile_get = _mapcache_cache_rest_get;
- cache->cache.tile_exists = _mapcache_cache_rest_has_tile;
- cache->cache.tile_set = _mapcache_cache_rest_set;
- cache->cache.tile_multi_set = _mapcache_cache_rest_multi_set;
+ cache->cache._tile_delete = _mapcache_cache_rest_delete;
+ cache->cache._tile_get = _mapcache_cache_rest_get;
+ cache->cache._tile_exists = _mapcache_cache_rest_has_tile;
+ cache->cache._tile_set = _mapcache_cache_rest_set;
cache->cache.configuration_post_config = _mapcache_cache_rest_configuration_post_config;
cache->cache.configuration_parse_xml = _mapcache_cache_rest_configuration_parse_xml;
}
diff --git a/lib/cache_riak.c b/lib/cache_riak.c
index 4c79862..f8230d5 100644
--- a/lib/cache_riak.c
+++ b/lib/cache_riak.c
@@ -27,10 +27,10 @@
* DEALINGS IN THE SOFTWARE.
*****************************************************************************/
-#include "mapcache-config.h"
+#include "mapcache.h"
#ifdef USE_RIAK
-#include "mapcache.h"
+#include <riack.h>
#include <apr_strings.h>
#include <apr_reslist.h>
@@ -39,11 +39,19 @@
#include <string.h>
#include <errno.h>
-/*
- * Since we don't construct the connection pool and store it in the cache object
- * we have to store all the connections in a hash map in case there are multiple
- * riak caches defined.
+typedef struct mapcache_cache_riak mapcache_cache_riak;
+
+/**\class mapcache_cache_riak
+ * \brief a mapcache_cache for riak servers
+ * \implements mapcache_cache
*/
+struct mapcache_cache_riak {
+ mapcache_cache cache;
+ char *host;
+ int port;
+ char *key_template;
+ char *bucket_template;
+};
struct riak_conn_params {
mapcache_cache_riak *cache;
@@ -96,44 +104,34 @@ static mapcache_pooled_connection* _riak_get_connection(mapcache_context *ctx, m
static int _mapcache_cache_riak_has_tile(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) {
int error;
- int retries = 3;
- RIACK_STRING key;
+ RIACK_STRING key,bucket;
struct RIACK_GET_OBJECT obj;
struct RIACK_CLIENT *client;
mapcache_pooled_connection *pc;
mapcache_cache_riak *cache = (mapcache_cache_riak*)pcache;
- key.value = mapcache_util_get_tile_key(ctx, tile, NULL, " \r\n\t\f\e\a\b", "#");
+ key.value = mapcache_util_get_tile_key(ctx, tile, cache->key_template, " \r\n\t\f\e\a\b", "#");
if (GC_HAS_ERROR(ctx)) {
return MAPCACHE_FALSE;
}
key.len = strlen(key.value);
+ if(strchr(cache->bucket_template,'{')) {
+ bucket.value = mapcache_util_get_tile_key(ctx, tile, cache->bucket_template, " \r\n\t\f\e\a\b", "#");
+ } else {
+ bucket.value = cache->bucket_template;
+ }
+ bucket.len = strlen(bucket.value);
+
pc = _riak_get_connection(ctx, cache, tile);
if (GC_HAS_ERROR(ctx)) {
return MAPCACHE_FALSE;
}
client = pc->connection;
- do
- {
- error = riack_get(client, cache->bucket, key, 0, &obj);
- if (error != RIACK_SUCCESS) {
- ctx->log(ctx, MAPCACHE_WARN, "Retry %d in riak_has_tile for tile %s from cache %s due to error %d", (4-retries), key.value, cache->cache.name, error);
- for (error = riack_reconnect(client);
- error != RIACK_SUCCESS && retries > 0;
- error = riack_reconnect(client))
- {
- --retries;
- }
-
- --retries;
- }
- }
- while (error != RIACK_SUCCESS && retries >= 0);
+ error = riack_get(client, bucket, key, 0, &obj);
if (error != RIACK_SUCCESS) {
- riack_free_get_object(client, &obj); // riack_get allocates the returned object so we need to deallocate it.
mapcache_connection_pool_invalidate_connection(ctx,pc);
ctx->set_error(ctx, 500, "riak: failed to get key %s: %d", key, error);
return MAPCACHE_FALSE;
@@ -153,7 +151,7 @@ static int _mapcache_cache_riak_has_tile(mapcache_context *ctx, mapcache_cache *
static void _mapcache_cache_riak_delete(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) {
int error;
- RIACK_STRING key;
+ RIACK_STRING key,bucket;
struct RIACK_CLIENT *client;
struct RIACK_DEL_PROPERTIES properties;
mapcache_pooled_connection *pc;
@@ -162,17 +160,24 @@ static void _mapcache_cache_riak_delete(mapcache_context *ctx, mapcache_cache *p
memset(&properties, 0, sizeof(struct RIACK_DEL_PROPERTIES));
- key.value = mapcache_util_get_tile_key(ctx, tile, NULL, " \r\n\t\f\e\a\b", "#");
+ key.value = mapcache_util_get_tile_key(ctx, tile, cache->key_template, " \r\n\t\f\e\a\b", "#");
GC_CHECK_ERROR(ctx);
key.len = strlen(key.value);
+ if(strchr(cache->bucket_template,'{')) {
+ bucket.value = mapcache_util_get_tile_key(ctx, tile, cache->bucket_template, " \r\n\t\f\e\a\b", "#");
+ } else {
+ bucket.value = cache->bucket_template;
+ }
+ bucket.len = strlen(bucket.value);
+
pc = _riak_get_connection(ctx, cache, tile);
GC_CHECK_ERROR(ctx);
client = pc->connection;
properties.rw_use = 1;
properties.rw = (4294967295 - 3); // Special value meaning "ALL"
- error = riack_delete(client, cache->bucket, key, &properties);
+ error = riack_delete(client, bucket, key, &properties);
mapcache_connection_pool_release_connection(ctx,pc);
@@ -190,9 +195,7 @@ static void _mapcache_cache_riak_delete(mapcache_context *ctx, mapcache_cache *p
*/
static int _mapcache_cache_riak_get(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) {
int error;
- int connect_error = RIACK_SUCCESS;
- int retries = 3;
- RIACK_STRING key;
+ RIACK_STRING key,bucket;
struct RIACK_GET_OBJECT obj;
struct RIACK_GET_PROPERTIES properties;
struct RIACK_CLIENT *client;
@@ -208,13 +211,19 @@ static int _mapcache_cache_riak_get(mapcache_context *ctx, mapcache_cache *pcach
*/
- key.value = mapcache_util_get_tile_key(ctx, tile, NULL, " \r\n\t\f\e\a\b", "#");
+ key.value = mapcache_util_get_tile_key(ctx, tile, cache->key_template, " \r\n\t\f\e\a\b", "#");
if (GC_HAS_ERROR(ctx)) {
return MAPCACHE_FAILURE;
}
key.len = strlen(key.value);
- tile->encoded_data = mapcache_buffer_create(0, ctx->pool);
+ if(strchr(cache->bucket_template,'{')) {
+ bucket.value = mapcache_util_get_tile_key(ctx, tile, cache->bucket_template, " \r\n\t\f\e\a\b", "#");
+ } else {
+ bucket.value = cache->bucket_template;
+ }
+ bucket.len = strlen(bucket.value);
+
pc = _riak_get_connection(ctx, cache, tile);
if (GC_HAS_ERROR(ctx)) {
@@ -222,33 +231,11 @@ static int _mapcache_cache_riak_get(mapcache_context *ctx, mapcache_cache *pcach
}
client = pc->connection;
- // 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_get(client, cache->bucket, key, &properties, &obj);
- if (error != RIACK_SUCCESS) {
- ctx->log(ctx, MAPCACHE_WARN, "Retry %d in riak_get for tile %s from cache %s due to error %d", (4-retries), key.value, 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);
+ error = riack_get(client, bucket, key, &properties, &obj);
if (error != RIACK_SUCCESS)
{
- if (connect_error != RIACK_SUCCESS)
- mapcache_connection_pool_invalidate_connection(ctx,pc);
- else
- mapcache_connection_pool_release_connection(ctx,pc);
-
+ mapcache_connection_pool_invalidate_connection(ctx,pc);
ctx->set_error(ctx, 500, "Failed to get tile %s from cache %s due to error %d", key.value, cache->cache.name, error);
return MAPCACHE_FAILURE;
}
@@ -263,6 +250,7 @@ static int _mapcache_cache_riak_get(mapcache_context *ctx, mapcache_cache *pcach
}
// Copy the data into the buffer
+ tile->encoded_data = mapcache_buffer_create(0, ctx->pool);
mapcache_buffer_append(tile->encoded_data, obj.object.content[0].data_len, obj.object.content[0].data);
riack_free_get_object(client, &obj); // riack_get allocates the returned object so we need to deallocate it.
@@ -282,12 +270,11 @@ static int _mapcache_cache_riak_get(mapcache_context *ctx, mapcache_cache *pcach
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;
+ RIACK_STRING bucket;
mapcache_pooled_connection *pc;
mapcache_cache_riak *cache = (mapcache_cache_riak*)pcache;
@@ -304,9 +291,16 @@ static void _mapcache_cache_riak_set(mapcache_context *ctx, mapcache_cache *pcac
properties.dw = 0;*/
- key = mapcache_util_get_tile_key(ctx, tile, NULL, " \r\n\t\f\e\a\b", "#");
+ key = mapcache_util_get_tile_key(ctx, tile, cache->key_template, " \r\n\t\f\e\a\b", "#");
GC_CHECK_ERROR(ctx);
+ if(strchr(cache->bucket_template,'{')) {
+ bucket.value = mapcache_util_get_tile_key(ctx, tile, cache->bucket_template, " \r\n\t\f\e\a\b", "#");
+ } else {
+ bucket.value = cache->bucket_template;
+ }
+ bucket.len = strlen(bucket.value);
+
if (!tile->encoded_data) {
tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format);
GC_CHECK_ERROR(ctx);
@@ -327,8 +321,7 @@ static void _mapcache_cache_riak_set(mapcache_context *ctx, mapcache_cache *pcac
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.bucket = bucket;
object.key.value = key;
object.key.len = strlen(key);
object.vclock.len = 0;
@@ -339,27 +332,9 @@ static void _mapcache_cache_riak_set(mapcache_context *ctx, mapcache_cache *pcac
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);
+ error = riack_put(client, object, 0, &properties);
- if (connect_error != RIACK_SUCCESS)
+ if (error != RIACK_SUCCESS)
mapcache_connection_pool_invalidate_connection(ctx,pc);
else
mapcache_connection_pool_release_connection(ctx,pc);
@@ -374,7 +349,7 @@ static void _mapcache_cache_riak_set(mapcache_context *ctx, mapcache_cache *pcac
* \private \memberof mapcache_cache_riak
*/
static void _mapcache_cache_riak_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *cache, mapcache_cfg *config) {
- ezxml_t cur_node,xhost,xport,xbucket;
+ ezxml_t cur_node,xhost,xport,xbucket,xkey;
mapcache_cache_riak *dcache = (mapcache_cache_riak*)cache;
int servercount = 0;
@@ -396,16 +371,13 @@ static void _mapcache_cache_riak_configuration_parse_xml(mapcache_context *ctx,
xhost = ezxml_child(cur_node, "host"); /* Host should contain just server */
xport = ezxml_child(cur_node, "port");
xbucket = ezxml_child(cur_node, "bucket");
+ xkey = ezxml_child(cur_node, "key");
if (!xhost || !xhost->txt || ! *xhost->txt) {
ctx->set_error(ctx, 400, "cache %s: <server> with no <host>", cache->name);
return;
} else {
dcache->host = apr_pstrdup(ctx->pool, xhost->txt);
- if (dcache->host == NULL) {
- ctx->set_error(ctx, 400, "cache %s: failed to allocate host string!", cache->name);
- return;
- }
}
if (!xport || !xport->txt || ! *xport->txt) {
@@ -419,12 +391,11 @@ static void _mapcache_cache_riak_configuration_parse_xml(mapcache_context *ctx,
ctx->set_error(ctx, 400, "cache %s: <server> with no <bucket>", cache->name);
return;
} else {
- dcache->bucket.value = apr_pstrdup(ctx->pool, xbucket->txt);
- if (dcache->bucket.value == NULL) {
- ctx->set_error(ctx, 400, "cache %s: failed to allocate bucket string!", cache->name);
- return;
- }
- dcache->bucket.len = strlen(dcache->bucket.value);
+ dcache->bucket_template = apr_pstrdup(ctx->pool, xbucket->txt);
+ }
+
+ if(xkey && xkey->txt && *xkey->txt) {
+ dcache->key_template = apr_pstrdup(ctx->pool, xkey->txt);
}
}
@@ -447,16 +418,23 @@ mapcache_cache* mapcache_cache_riak_create(mapcache_context *ctx) {
cache->cache.metadata = apr_table_make(ctx->pool, 3);
cache->cache.type = MAPCACHE_CACHE_RIAK;
- cache->cache.tile_get = _mapcache_cache_riak_get;
- cache->cache.tile_exists = _mapcache_cache_riak_has_tile;
- cache->cache.tile_set = _mapcache_cache_riak_set;
- cache->cache.tile_delete = _mapcache_cache_riak_delete;
+ cache->cache._tile_get = _mapcache_cache_riak_get;
+ cache->cache._tile_exists = _mapcache_cache_riak_has_tile;
+ cache->cache._tile_set = _mapcache_cache_riak_set;
+ cache->cache._tile_delete = _mapcache_cache_riak_delete;
cache->cache.configuration_parse_xml = _mapcache_cache_riak_configuration_parse_xml;
cache->cache.configuration_post_config = _mapcache_cache_riak_configuration_post_config;
cache->host = NULL;
cache->port = 8087; // Default RIAK port used for protobuf
+ cache->bucket_template = NULL;
+ cache->key_template = NULL;
return (mapcache_cache*)cache;
}
+#else
+mapcache_cache* mapcache_cache_riak_create(mapcache_context *ctx) {
+ ctx->set_error(ctx,400,"RIAK support not compiled in this version");
+ return NULL;
+}
#endif
diff --git a/lib/cache_sqlite.c b/lib/cache_sqlite.c
index d05ae06..917ada0 100644
--- a/lib/cache_sqlite.c
+++ b/lib/cache_sqlite.c
@@ -27,10 +27,9 @@
* DEALINGS IN THE SOFTWARE.
*****************************************************************************/
-#include "mapcache-config.h"
+#include "mapcache.h"
#ifdef USE_SQLITE
-#include "mapcache.h"
#include <apr_strings.h>
#include <string.h>
#include <errno.h>
@@ -47,6 +46,36 @@
#include <sqlite3.h>
+/**\class mapcache_cache_sqlite
+ * \brief a mapcache_cache on a filesytem
+ * \implements mapcache_cache
+ */
+typedef struct mapcache_cache_sqlite mapcache_cache_sqlite;
+typedef struct mapcache_cache_sqlite_stmt mapcache_cache_sqlite_stmt;
+
+struct mapcache_cache_sqlite_stmt {
+ char *sql;
+};
+
+struct sqlite_conn;
+
+struct mapcache_cache_sqlite {
+ mapcache_cache cache;
+ char *dbfile;
+ mapcache_cache_sqlite_stmt create_stmt;
+ mapcache_cache_sqlite_stmt exists_stmt;
+ mapcache_cache_sqlite_stmt get_stmt;
+ mapcache_cache_sqlite_stmt set_stmt;
+ mapcache_cache_sqlite_stmt delete_stmt;
+ apr_table_t *pragmas;
+ 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;
+ int count_x, count_y;
+};
+
+
struct sqlite_conn_params {
mapcache_cache_sqlite *cache;
char *dbfile;
@@ -198,14 +227,17 @@ static void _mapcache_cache_sqlite_filename_for_tile(mapcache_context *ctx, mapc
if(strstr(*path,"{grid}"))
*path = mapcache_util_str_replace(ctx->pool,*path, "{grid}",
tile->grid_link->grid->name);
- if(tile->dimensions && strstr(*path,"{dim}")) {
+ if(tile->dimensions && strstr(*path,"{dim")) {
char *dimstring="";
- const apr_array_header_t *elts = apr_table_elts(tile->dimensions);
- int i = elts->nelts;
+ int i = tile->dimensions->nelts;
while(i--) {
- apr_table_entry_t *entry = &(APR_ARRAY_IDX(elts,i,apr_table_entry_t));
- const char *dimval = mapcache_util_str_sanitize(ctx->pool,entry->val,"/.",'#');
+ mapcache_requested_dimension *entry = APR_ARRAY_IDX(tile->dimensions,i,mapcache_requested_dimension*);
+ const char *dimval = mapcache_util_str_sanitize(ctx->pool,entry->cached_value,"/.",'#');
+ char *single_dim = apr_pstrcat(ctx->pool,"{dim:",entry->dimension->name,"}",NULL);
dimstring = apr_pstrcat(ctx->pool,dimstring,"#",dimval,NULL);
+ if(strstr(*path,single_dim)) {
+ *path = mapcache_util_str_replace(ctx->pool,*path, single_dim, dimval);
+ }
}
*path = mapcache_util_str_replace(ctx->pool,*path, "{dim}", dimstring);
}
@@ -955,11 +987,11 @@ mapcache_cache* mapcache_cache_sqlite_create(mapcache_context *ctx)
}
cache->cache.metadata = apr_table_make(ctx->pool, 3);
cache->cache.type = MAPCACHE_CACHE_SQLITE;
- cache->cache.tile_delete = _mapcache_cache_sqlite_delete;
- cache->cache.tile_get = _mapcache_cache_sqlite_get;
- cache->cache.tile_exists = _mapcache_cache_sqlite_has_tile;
- cache->cache.tile_set = _mapcache_cache_sqlite_set;
- cache->cache.tile_multi_set = _mapcache_cache_sqlite_multi_set;
+ cache->cache._tile_delete = _mapcache_cache_sqlite_delete;
+ cache->cache._tile_get = _mapcache_cache_sqlite_get;
+ cache->cache._tile_exists = _mapcache_cache_sqlite_has_tile;
+ cache->cache._tile_set = _mapcache_cache_sqlite_set;
+ cache->cache._tile_multi_set = _mapcache_cache_sqlite_multi_set;
cache->cache.configuration_post_config = _mapcache_cache_sqlite_configuration_post_config;
cache->cache.configuration_parse_xml = _mapcache_cache_sqlite_configuration_parse_xml;
cache->create_stmt.sql = apr_pstrdup(ctx->pool,
@@ -994,9 +1026,9 @@ mapcache_cache* mapcache_cache_mbtiles_create(mapcache_context *ctx)
return NULL;
}
cache->cache.configuration_post_config = _mapcache_cache_mbtiles_configuration_post_config;
- cache->cache.tile_set = _mapcache_cache_mbtiles_set;
- cache->cache.tile_multi_set = _mapcache_cache_mbtiles_multi_set;
- cache->cache.tile_delete = _mapcache_cache_mbtiles_delete;
+ cache->cache._tile_set = _mapcache_cache_mbtiles_set;
+ cache->cache._tile_multi_set = _mapcache_cache_mbtiles_multi_set;
+ cache->cache._tile_delete = _mapcache_cache_mbtiles_delete;
cache->create_stmt.sql = apr_pstrdup(ctx->pool,
"create table if not exists images(tile_id text, tile_data blob, primary key(tile_id));"\
"CREATE TABLE IF NOT EXISTS map (zoom_level integer, tile_column integer, tile_row integer, tile_id text, foreign key(tile_id) references images(tile_id), primary key(tile_row,tile_column,zoom_level));"\
@@ -1013,6 +1045,17 @@ mapcache_cache* mapcache_cache_mbtiles_create(mapcache_context *ctx)
cache->bind_stmt = _bind_mbtiles_params;
return (mapcache_cache*) cache;
}
+#else
+
+mapcache_cache* mapcache_cache_sqlite_create(mapcache_context *ctx) {
+ ctx->set_error(ctx,400,"SQLITE support not compiled in this version");
+ return NULL;
+}
+mapcache_cache* mapcache_cache_mbtiles_create(mapcache_context *ctx) {
+ ctx->set_error(ctx,400,"SQLITE (MBtiles) support not compiled in this version");
+ return NULL;
+}
+
#endif
diff --git a/lib/cache_tiff.c b/lib/cache_tiff.c
index 827e7b1..6a4f115 100644
--- a/lib/cache_tiff.c
+++ b/lib/cache_tiff.c
@@ -3,10 +3,13 @@
*
* Project: MapServer
* Purpose: MapCache tile caching: tiled tiff filesytem cache backend.
- * Author: Thomas Bonfort, Frank Warmerdam and the MapServer team.
+ * Author: Thomas Bonfort
+ * Frank Warmerdam
+ * Even Rouault
+ * MapServer team.
*
******************************************************************************
- * Copyright (c) 2011 Regents of the University of Minnesota.
+ * Copyright (c) 2011-2017 Regents of the University of Minnesota.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@@ -27,10 +30,9 @@
* DEALINGS IN THE SOFTWARE.
*****************************************************************************/
-#include "mapcache-config.h"
+#include "mapcache.h"
#ifdef USE_TIFF
-#include "mapcache.h"
#include <apr_file_info.h>
#include <apr_strings.h>
#include <apr_file_io.h>
@@ -39,6 +41,12 @@
#include <stdlib.h>
#include <tiffio.h>
+#ifdef USE_GDAL
+#include "cpl_vsi.h"
+#include "cpl_conv.h"
+#define CPL_SERV_H_INCLUDED
+#endif
+
#ifdef USE_GEOTIFF
#include "xtiffio.h"
#include "geovalues.h"
@@ -53,6 +61,278 @@
#define MyTIFFClose TIFFClose
#endif
+typedef enum
+{
+ MAPCACHE_TIFF_STORAGE_FILE,
+ MAPCACHE_TIFF_STORAGE_REST,
+ MAPCACHE_TIFF_STORAGE_GOOGLE
+} mapcache_cache_tiff_storage_type;
+
+
+typedef struct mapcache_cache_tiff mapcache_cache_tiff;
+
+struct mapcache_cache_tiff {
+ mapcache_cache cache;
+ char *filename_template;
+ 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;
+ int count_x;
+ int count_y;
+ mapcache_image_format_jpeg *format;
+ mapcache_locker *locker;
+ struct {
+ mapcache_cache_tiff_storage_type type;
+ int connection_timeout;
+ int timeout;
+ char *header_file;
+ union
+ {
+ struct
+ {
+ char* access;
+ char* secret;
+ } google;
+ } u;
+ } storage;
+};
+
+#ifdef USE_GDAL
+
+static tsize_t
+_tiffReadProc( thandle_t th, tdata_t buf, tsize_t size )
+{
+ VSILFILE* fp = (VSILFILE*)th;
+ return VSIFReadL( buf, 1, size, fp );
+}
+
+static tsize_t
+_tiffWriteProc( thandle_t th, tdata_t buf, tsize_t size )
+{
+ VSILFILE* fp = (VSILFILE*)th;
+ return VSIFWriteL( buf, 1, size, fp );
+}
+
+static toff_t
+_tiffSeekProc( thandle_t th, toff_t off, int whence )
+{
+ VSILFILE* fp = (VSILFILE*)th;
+ if( VSIFSeekL( fp, off, whence ) != 0 )
+ {
+ TIFFErrorExt( th, "_tiffSeekProc", "%s", VSIStrerror( errno ) );
+ return (toff_t)( -1 );
+ }
+ return VSIFTellL( fp );
+}
+
+static int
+_tiffCloseProc( thandle_t th )
+{
+ VSILFILE* fp = (VSILFILE*)th;
+ VSIFCloseL(fp);
+ return 0;
+}
+
+static toff_t
+_tiffSizeProc( thandle_t th )
+{
+ vsi_l_offset old_off;
+ toff_t file_size;
+ VSILFILE* fp = (VSILFILE*)th;
+
+ old_off = VSIFTellL( fp );
+ (void)VSIFSeekL( fp, 0, SEEK_END );
+
+ file_size = (toff_t) VSIFTellL( fp );
+ (void)VSIFSeekL( fp, old_off, SEEK_SET );
+
+ return file_size;
+}
+
+static int
+_tiffMapProc( thandle_t th, tdata_t* pbase, toff_t* psize )
+{
+ (void)th;
+ (void)pbase;
+ (void)psize;
+ /* Unimplemented */
+ return 0;
+}
+
+static void
+_tiffUnmapProc( thandle_t th, tdata_t base, toff_t size )
+{
+ (void)th;
+ (void)base;
+ (void)size;
+ /* Unimplemented */
+}
+
+static char* set_conf_value(const char* key, const char* value)
+{
+ const char* old_val_const;
+ char* old_val = NULL;
+ old_val_const = CPLGetConfigOption(key, NULL);
+ if( old_val_const != NULL )
+ old_val = strdup(old_val_const);
+ /* Prevent a directory listing to be done */
+ CPLSetConfigOption(key, value);
+ return old_val;
+}
+
+static void restore_conf_value(const char* key, char* old_val)
+{
+ CPLSetConfigOption(key, old_val);
+ free(old_val);
+}
+
+typedef struct
+{
+ char* old_val_disable_readdir;
+ char* old_val_headerfile;
+ char* old_val_secret;
+ char* old_val_access;
+} mapache_gdal_env_context;
+
+static void set_gdal_context(mapcache_cache_tiff *cache,
+ mapache_gdal_env_context* pcontext)
+{
+ memset(pcontext, 0, sizeof(mapache_gdal_env_context));
+ /* Prevent a directory listing to be done */
+ pcontext->old_val_disable_readdir =
+ set_conf_value("GDAL_DISABLE_READDIR_ON_OPEN", "YES");
+
+ if( cache->storage.header_file ) {
+ pcontext->old_val_headerfile = set_conf_value("GDAL_HTTP_HEADER_FILE",
+ cache->storage.header_file);
+ }
+ if( cache->storage.type == MAPCACHE_TIFF_STORAGE_GOOGLE ) {
+ pcontext->old_val_secret = set_conf_value("GS_SECRET_ACCESS_KEY",
+ cache->storage.u.google.secret);
+ pcontext->old_val_access = set_conf_value("GS_ACCESS_KEY_ID",
+ cache->storage.u.google.access);
+ }
+}
+
+static void restore_gdal_context(mapcache_cache_tiff *cache,
+ mapache_gdal_env_context* pcontext)
+{
+
+ restore_conf_value("GDAL_DISABLE_READDIR_ON_OPEN",
+ pcontext->old_val_disable_readdir);
+ if( cache->storage.header_file ) {
+ restore_conf_value("GDAL_HTTP_HEADER_FILE",
+ pcontext->old_val_headerfile);
+ }
+ if( cache->storage.type == MAPCACHE_TIFF_STORAGE_GOOGLE ) {
+ restore_conf_value("GS_SECRET_ACCESS_KEY",
+ pcontext->old_val_secret);
+ restore_conf_value("GS_ACCESS_KEY_ID",
+ pcontext->old_val_access);
+ }
+}
+
+static int mapcache_cache_tiff_vsi_stat(
+ mapcache_cache_tiff *cache,
+ const char* name,
+ VSIStatBufL* pstat)
+{
+ mapache_gdal_env_context context;
+ int ret;
+
+ set_gdal_context(cache, &context);
+ ret = VSIStatL(name, pstat);
+ restore_gdal_context(cache, &context);
+
+ return ret;
+}
+
+static VSILFILE* mapcache_cache_tiff_vsi_open(
+ mapcache_cache_tiff *cache,
+ const char* name, const char* mode )
+{
+ mapache_gdal_env_context context;
+ VSILFILE* fp;
+
+ set_gdal_context(cache, &context);
+ fp = VSIFOpenL(name, mode);
+ restore_gdal_context(cache, &context);
+
+ return fp;
+}
+
+static TIFF* mapcache_cache_tiff_open(mapcache_context *ctx,
+ mapcache_cache_tiff *cache,
+ const char* name, const char* mode )
+{
+ char chDummy;
+ VSILFILE* fp;
+
+ /* If writing or using a regular filename, then use standard */
+ /* libtiff/libgeotiff I/O layer */
+ if( strcmp(mode, "r") != 0 || strncmp(name, "/vsi", 4) != 0 )
+ {
+ return MyTIFFOpen(name, mode);
+ }
+
+ fp = mapcache_cache_tiff_vsi_open(cache, name, mode);
+ if( fp == NULL )
+ return NULL;
+
+ if( strcmp(mode, "r") == 0 )
+ {
+ /* But then the file descriptor may point to an invalid resource */
+ /* so try reading a byte from it */
+ if(VSIFReadL(&chDummy, 1, 1, fp) != 1)
+ {
+ VSIFCloseL(fp);
+ return NULL;
+ }
+ VSIFSeekL(fp, 0, SEEK_SET);
+ }
+
+ return
+#ifdef USE_GEOTIFF
+ XTIFFClientOpen
+#else
+ TIFFClientOpen
+#endif
+ ( name, mode,
+ (thandle_t) fp,
+ _tiffReadProc, _tiffWriteProc,
+ _tiffSeekProc, _tiffCloseProc, _tiffSizeProc,
+ _tiffMapProc, _tiffUnmapProc );
+}
+
+static void mapcache_cache_tiff_gdal_error_handler(CPLErr eErr,
+ int error_num,
+ const char* pszMsg)
+{
+#ifdef DEBUG
+ mapcache_context *ctx = (mapcache_context *) CPLGetErrorHandlerUserData();
+ ctx->log(ctx,MAPCACHE_DEBUG,"GDAL %s, %d: %s",
+ (eErr == CE_Failure) ? "Failure":
+ (eErr == CE_Warning) ? "Warning":
+ "Debug",
+ error_num,
+ pszMsg);
+#endif
+}
+
+
+#else
+
+static TIFF* mapcache_cache_tiff_open(mapcache_context *ctx,
+ mapcache_cache_tiff *cache,
+ const char* name, const char* mode )
+{
+ (void)ctx;
+ (void)cache;
+ return MyTIFFOpen(name, mode);
+
+}
+
+#endif /* USE_GDAL */
+
+#undef MyTIFFOpen
/**
* \brief return filename for given tile
@@ -64,7 +344,20 @@
*/
static void _mapcache_cache_tiff_tile_key(mapcache_context *ctx, mapcache_cache_tiff *cache, mapcache_tile *tile, char **path)
{
- *path = cache->filename_template;
+ if( cache->storage.type == MAPCACHE_TIFF_STORAGE_REST ) {
+ *path = apr_pstrcat(ctx->pool, "/vsicurl/", cache->filename_template, NULL);
+ }
+ else if( cache->storage.type == MAPCACHE_TIFF_STORAGE_GOOGLE &&
+ strncmp(cache->filename_template,
+ "https://storage.googleapis.com/",
+ strlen("https://storage.googleapis.com/")) == 0) {
+ *path = apr_pstrcat(ctx->pool, "/vsigs/",
+ cache->filename_template +
+ strlen("https://storage.googleapis.com/"), NULL);
+ }
+ else {
+ *path = apr_pstrdup(ctx->pool, cache->filename_template);
+ }
/*
* generic template substitutions
@@ -75,14 +368,17 @@ static void _mapcache_cache_tiff_tile_key(mapcache_context *ctx, mapcache_cache_
if(strstr(*path,"{grid}"))
*path = mapcache_util_str_replace(ctx->pool,*path, "{grid}",
tile->grid_link->grid->name);
- if(tile->dimensions && strstr(*path,"{dim}")) {
+ if(tile->dimensions && strstr(*path,"{dim")) {
char *dimstring="";
- const apr_array_header_t *elts = apr_table_elts(tile->dimensions);
- int i = elts->nelts;
+ int i = tile->dimensions->nelts;
while(i--) {
- apr_table_entry_t *entry = &(APR_ARRAY_IDX(elts,i,apr_table_entry_t));
- const char *dimval = mapcache_util_str_sanitize(ctx->pool,entry->val,"/.",'#');
+ mapcache_requested_dimension *rdim = APR_ARRAY_IDX(tile->dimensions,i,mapcache_requested_dimension*);
+ const char *dimval = mapcache_util_str_sanitize(ctx->pool,rdim->cached_value,"/.",'#');
+ char *dim_key = apr_pstrcat(ctx->pool,"{dim:",rdim->dimension->name,"}",NULL);
dimstring = apr_pstrcat(ctx->pool,dimstring,"#",dimval,NULL);
+ if(strstr(*path,dim_key)) {
+ *path = mapcache_util_str_replace(ctx->pool,*path, dim_key, dimval);
+ }
}
*path = mapcache_util_str_replace(ctx->pool,*path, "{dim}", dimstring);
}
@@ -220,7 +516,12 @@ static int _mapcache_cache_tiff_has_tile(mapcache_context *ctx, mapcache_cache *
if(GC_HAS_ERROR(ctx)) {
return MAPCACHE_FALSE;
}
- hTIFF = MyTIFFOpen(filename,"r");
+
+#ifdef USE_GDAL
+ CPLPushErrorHandlerEx(mapcache_cache_tiff_gdal_error_handler, ctx);
+#endif
+
+ hTIFF = mapcache_cache_tiff_open(ctx,cache,filename,"r");
if(hTIFF) {
do {
@@ -246,6 +547,9 @@ static int _mapcache_cache_tiff_has_tile(mapcache_context *ctx, mapcache_cache *
check_tiff_format(ctx,cache,tile,hTIFF,filename);
if(GC_HAS_ERROR(ctx)) {
MyTIFFClose(hTIFF);
+#ifdef USE_GDAL
+ CPLPopErrorHandler();
+#endif
return MAPCACHE_FALSE;
}
#endif
@@ -265,22 +569,37 @@ static int _mapcache_cache_tiff_has_tile(mapcache_context *ctx, mapcache_cache *
if(1 != TIFFGetField( hTIFF, TIFFTAG_TILEOFFSETS, &offsets )) {
MyTIFFClose(hTIFF);
+#ifdef USE_GDAL
+ CPLPopErrorHandler();
+#endif
return MAPCACHE_FALSE;
}
if(1 != TIFFGetField( hTIFF, TIFFTAG_TILEBYTECOUNTS, &sizes )) {
MyTIFFClose(hTIFF);
+#ifdef USE_GDAL
+ CPLPopErrorHandler();
+#endif
return MAPCACHE_FALSE;
}
MyTIFFClose(hTIFF);
if( offsets[tiff_off] > 0 && sizes[tiff_off] > 0 ) {
+#ifdef USE_GDAL
+ CPLPopErrorHandler();
+#endif
return MAPCACHE_TRUE;
} else {
+#ifdef USE_GDAL
+ CPLPopErrorHandler();
+#endif
return MAPCACHE_FALSE;
}
} while( TIFFReadDirectory( hTIFF ) );
- return MAPCACHE_FALSE; /* TIFF only contains overviews ? */
- } else
- return MAPCACHE_FALSE;
+ /* TIFF only contains overviews ? */
+ }
+#ifdef USE_GDAL
+ CPLPopErrorHandler();
+#endif
+ return MAPCACHE_FALSE;
}
static void _mapcache_cache_tiff_delete(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
@@ -311,13 +630,17 @@ static int _mapcache_cache_tiff_get(mapcache_context *ctx, mapcache_cache *pcach
tile->x,tile->y,tile->z,filename);
#endif
- hTIFF = MyTIFFOpen(filename,"r");
+#ifdef USE_GDAL
+ CPLPushErrorHandlerEx(mapcache_cache_tiff_gdal_error_handler, ctx);
+#endif
+
+ hTIFF = mapcache_cache_tiff_open(ctx,cache,filename,"r");
/*
* we currrently have no way of knowing if the opening failed because the tif
* file does not exist (which is not an error condition, as it only signals
* that the requested tile does not exist in the cache), or if an other error
- * that should be signaled occured (access denied, not a tiff file, etc...)
+ * that should be signaled occurred (access denied, not a tiff file, etc...)
*
* we ignore this case here and hope that further parts of the code will be
* able to detect what's happening more precisely
@@ -346,6 +669,9 @@ static int _mapcache_cache_tiff_get(mapcache_context *ctx, mapcache_cache *pcach
check_tiff_format(ctx,cache,tile,hTIFF,filename);
if(GC_HAS_ERROR(ctx)) {
MyTIFFClose(hTIFF);
+#ifdef USE_GDAL
+ CPLPopErrorHandler();
+#endif
return MAPCACHE_FAILURE;
}
#endif
@@ -374,6 +700,9 @@ static int _mapcache_cache_tiff_get(mapcache_context *ctx, mapcache_cache *pcach
ctx->set_error(ctx,500,"Failed to read TIFF file \"%s\" tile offsets",
filename);
MyTIFFClose(hTIFF);
+#ifdef USE_GDAL
+ CPLPopErrorHandler();
+#endif
return MAPCACHE_FAILURE;
}
@@ -383,6 +712,9 @@ static int _mapcache_cache_tiff_get(mapcache_context *ctx, mapcache_cache *pcach
ctx->set_error(ctx,500,"Failed to read TIFF file \"%s\" tile sizes",
filename);
MyTIFFClose(hTIFF);
+#ifdef USE_GDAL
+ CPLPopErrorHandler();
+#endif
return MAPCACHE_FAILURE;
}
@@ -391,7 +723,7 @@ static int _mapcache_cache_tiff_get(mapcache_context *ctx, mapcache_cache *pcach
* are not zero for that index.
* if not, the tiff file is sparse and is missing the requested tile
*/
- if( offsets[tiff_off] > 0 && sizes[tiff_off] > 0 ) {
+ if( offsets[tiff_off] > 0 && sizes[tiff_off] >= 2 ) {
apr_file_t *f;
apr_finfo_t finfo;
apr_status_t ret;
@@ -400,11 +732,14 @@ static int _mapcache_cache_tiff_get(mapcache_context *ctx, mapcache_cache *pcach
uint32 jpegtable_size = 0;
unsigned char* jpegtable_ptr;
rv = TIFFGetField( hTIFF, TIFFTAG_JPEGTABLES, &jpegtable_size, &jpegtable_ptr );
- if( rv != 1 || !jpegtable_ptr || !jpegtable_size) {
+ if( rv != 1 || !jpegtable_ptr || jpegtable_size < 2) {
/* there is no common jpeg header in the tiff tags */
ctx->set_error(ctx,500,"Failed to read TIFF file \"%s\" jpeg table",
filename);
MyTIFFClose(hTIFF);
+#ifdef USE_GDAL
+ CPLPopErrorHandler();
+#endif
return MAPCACHE_FAILURE;
}
@@ -412,6 +747,90 @@ static int _mapcache_cache_tiff_get(mapcache_context *ctx, mapcache_cache *pcach
* open the tiff file directly to access the jpeg image data with the given
* offset
*/
+#ifdef USE_GDAL
+ if( cache->storage.type != MAPCACHE_TIFF_STORAGE_FILE )
+ {
+ char *bufptr;
+ apr_off_t off;
+ apr_size_t bytes_to_read;
+ size_t bytes_read;
+ VSIStatBufL sStat;
+ VSILFILE* fp;
+
+ fp = mapcache_cache_tiff_vsi_open(cache, filename, "r");
+ if( fp == NULL )
+ {
+ /*
+ * shouldn't usually happen. we managed to open the file before,
+ * nothing much to do except bail out.
+ */
+ ctx->set_error(ctx,500,
+ "VSIFOpenL() failed on already open tiff "
+ "file \"%s\", giving up .... ",
+ filename);
+ MyTIFFClose(hTIFF);
+ CPLPopErrorHandler();
+ return MAPCACHE_FAILURE;
+ }
+
+ if( mapcache_cache_tiff_vsi_stat(cache, filename, &sStat) == 0 ) {
+ /*
+ * extract the file modification time. this isn't guaranteed to be the
+ * modification time of the actual tile, but it's the best we can do
+ */
+ tile->mtime = sStat.st_mtime;
+ }
+
+#ifdef DEBUG
+ ctx->log(ctx,MAPCACHE_DEBUG,"tile (%d,%d,%d) => mtime = %d)",
+ tile->x,tile->y,tile->z,tile->mtime);
+#endif
+
+
+ /* create a memory buffer to contain the jpeg data */
+ tile->encoded_data = mapcache_buffer_create(
+ (jpegtable_size+sizes[tiff_off]-4),ctx->pool);
+
+ /*
+ * copy the jpeg header to the beginning of the memory buffer,
+ * omitting the last 2 bytes
+ */
+ memcpy(tile->encoded_data->buf,jpegtable_ptr,(jpegtable_size-2));
+
+ /* advance the data pointer to after the header data */
+ bufptr = ((char *)tile->encoded_data->buf) + (jpegtable_size-2);
+
+
+ /* go to the specified offset in the tiff file, plus 2 bytes */
+ off = offsets[tiff_off]+2;
+ VSIFSeekL(fp, (vsi_l_offset)off, SEEK_SET);
+
+ /*
+ * copy the jpeg body at the end of the memory buffer, accounting
+ * for the two bytes we omitted in the previous step
+ */
+ bytes_to_read = sizes[tiff_off]-2;
+ bytes_read = VSIFReadL(bufptr, 1, bytes_to_read, fp);
+
+ /* check we have correctly read the requested number of bytes */
+ if(bytes_to_read != bytes_read) {
+ ctx->set_error(ctx,500,"failed to read jpeg body in \"%s\".\
+ (read %d of %d bytes)",
+ filename,(int)bytes_read,(int)sizes[tiff_off]-2);
+ VSIFCloseL(fp);
+ MyTIFFClose(hTIFF);
+ CPLPopErrorHandler();
+ return MAPCACHE_FAILURE;
+ }
+
+ tile->encoded_data->size = (jpegtable_size+sizes[tiff_off]-4);
+
+ VSIFCloseL(fp);
+ CPLPopErrorHandler();
+ return MAPCACHE_SUCCESS;
+ }
+ else
+#endif
if((ret=apr_file_open(&f, filename,
APR_FOPEN_READ|APR_FOPEN_BUFFERED|APR_FOPEN_BINARY,APR_OS_DEFAULT,
ctx->pool)) == APR_SUCCESS) {
@@ -455,7 +874,11 @@ static int _mapcache_cache_tiff_get(mapcache_context *ctx, mapcache_cache *pcach
if(bytes != sizes[tiff_off]-2) {
ctx->set_error(ctx,500,"failed to read jpeg body in \"%s\".\
(read %d of %d bytes)", filename,bytes,sizes[tiff_off]-2);
+ apr_file_close(f);
MyTIFFClose(hTIFF);
+#ifdef USE_GDAL
+ CPLPopErrorHandler();
+#endif
return MAPCACHE_FAILURE;
}
@@ -464,6 +887,9 @@ static int _mapcache_cache_tiff_get(mapcache_context *ctx, mapcache_cache *pcach
/* finalize and cleanup */
apr_file_close(f);
MyTIFFClose(hTIFF);
+#ifdef USE_GDAL
+ CPLPopErrorHandler();
+#endif
return MAPCACHE_SUCCESS;
} else {
/* shouldn't usually happen. we managed to open the file with TIFFOpen,
@@ -473,11 +899,17 @@ static int _mapcache_cache_tiff_get(mapcache_context *ctx, mapcache_cache *pcach
ctx->set_error(ctx,500,"apr_file_open failed on already open tiff file \"%s\", giving up .... ",
filename);
MyTIFFClose(hTIFF);
+#ifdef USE_GDAL
+ CPLPopErrorHandler();
+#endif
return MAPCACHE_FAILURE;
}
} else {
/* sparse tiff file without the requested tile */
MyTIFFClose(hTIFF);
+#ifdef USE_GDAL
+ CPLPopErrorHandler();
+#endif
return MAPCACHE_CACHE_MISS;
}
} /* loop through the tiff directories if there are multiple ones */
@@ -489,8 +921,10 @@ static int _mapcache_cache_tiff_get(mapcache_context *ctx, mapcache_cache *pcach
* does the file only contain overviews?
*/
MyTIFFClose(hTIFF);
- return MAPCACHE_CACHE_MISS;
}
+#ifdef USE_GDAL
+ CPLPopErrorHandler();
+#endif
/* failed to open tiff file */
return MAPCACHE_CACHE_MISS;
}
@@ -526,6 +960,11 @@ static void _mapcache_cache_tiff_set(mapcache_context *ctx, mapcache_cache *pcac
int tiff_off; /* the index of the tile inside the list of tiles of the tiff image */
cache = (mapcache_cache_tiff*)pcache;
+ if( cache->storage.type != MAPCACHE_TIFF_STORAGE_FILE )
+ {
+ ctx->set_error(ctx,500,"tiff cache %s is read-only\n",pcache->name);
+ return;
+ }
_mapcache_cache_tiff_tile_key(ctx, cache, tile, &filename);
format = (mapcache_image_format_jpeg*) cache->format;
if(GC_HAS_ERROR(ctx)) {
@@ -573,10 +1012,10 @@ static void _mapcache_cache_tiff_set(mapcache_context *ctx, mapcache_cache *pcac
/* check if the tiff file exists already */
rv = apr_stat(&finfo,filename,0,ctx->pool);
if(!APR_STATUS_IS_ENOENT(rv)) {
- hTIFF = MyTIFFOpen(filename,"r+");
+ hTIFF = mapcache_cache_tiff_open(ctx,cache,filename,"r+");
create = 0;
} else {
- hTIFF = MyTIFFOpen(filename,"w+");
+ hTIFF = mapcache_cache_tiff_open(ctx,cache,filename,"w+");
create = 1;
}
if(!hTIFF) {
@@ -659,7 +1098,7 @@ static void _mapcache_cache_tiff_set(mapcache_context *ctx, mapcache_cache *pcac
x = (tile->x / cache->count_x)*(cache->count_x);
y = (tile->y / cache->count_y)*(cache->count_y) + ntilesy - 1;
- mapcache_grid_get_extent(ctx, tile->grid_link->grid,
+ mapcache_grid_get_tile_extent(ctx, tile->grid_link->grid,
x,y,tile->z,&bbox);
adfTiePoints[0] = 0.0;
adfTiePoints[1] = 0.0;
@@ -714,7 +1153,7 @@ static void _mapcache_cache_tiff_set(mapcache_context *ctx, mapcache_cache *pcac
close_tiff:
if(hTIFF)
MyTIFFClose(hTIFF);
- mapcache_unlock_resource(ctx,cache->locker?cache->locker:ctx->config->locker,filename, lock);
+ mapcache_unlock_resource(ctx,cache->locker?cache->locker:ctx->config->locker, lock);
#else
ctx->set_error(ctx,500,"tiff write support disabled by default");
#endif
@@ -813,6 +1252,86 @@ static void _mapcache_cache_tiff_configuration_parse_xml(mapcache_context *ctx,
mapcache_config_parse_locker(ctx, cur_node, &cache->locker);
}
+ cache->storage.type = MAPCACHE_TIFF_STORAGE_FILE;
+ cur_node = ezxml_child(node,"storage");
+ if (cur_node) {
+ ezxml_t child_node;
+ const char *type = ezxml_attr(cur_node,"type");
+ if( !type ) {
+ ctx->set_error(ctx,400,
+ "<storage> with no \"type\" attribute in cache (%s)",
+ pcache->name);
+ return;
+ }
+
+ if( strcmp(type, "rest") == 0 ) {
+ cache->storage.type = MAPCACHE_TIFF_STORAGE_REST;
+ }
+ else if( strcmp(type, "google") == 0 ) {
+ cache->storage.type = MAPCACHE_TIFF_STORAGE_GOOGLE;
+ if ((child_node = ezxml_child(cur_node,"access")) != NULL) {
+ cache->storage.u.google.access =
+ apr_pstrdup(ctx->pool, child_node->txt);
+ } else if ( getenv("GS_ACCESS_KEY_ID")) {
+ cache->storage.u.google.access =
+ apr_pstrdup(ctx->pool,getenv("GS_ACCESS_KEY_ID"));
+ } else {
+ ctx->set_error(ctx,400,
+ "google storage in cache (%s) is missing "
+ "required <access> child", pcache->name);
+ return;
+ }
+ if ((child_node = ezxml_child(cur_node,"secret")) != NULL) {
+ cache->storage.u.google.secret =
+ apr_pstrdup(ctx->pool, child_node->txt);
+ } else if ( getenv("GS_SECRET_ACCESS_KEY")) {
+ cache->storage.u.google.access =
+ apr_pstrdup(ctx->pool,getenv("GS_SECRET_ACCESS_KEY"));
+ } else {
+ ctx->set_error(ctx,400,
+ "google storage in cache (%s) is missing "
+ "required <secret> child", pcache->name);
+ return;
+ }
+ }
+ else {
+ ctx->set_error(ctx, 400, "unknown cache type %s for cache \"%s\"",
+ type, pcache->name);
+ return;
+ }
+
+ if ((child_node = ezxml_child(cur_node,"connection_timeout")) != NULL) {
+ char *endptr;
+ cache->storage.connection_timeout = (int)strtol(child_node->txt,&endptr,10);
+ if(*endptr != 0 || cache->storage.connection_timeout<1) {
+ ctx->set_error(ctx,400,"invalid rest cache <connection_timeout> "
+ "\"%s\" (positive integer expected)",
+ child_node->txt);
+ return;
+ }
+ } else {
+ cache->storage.connection_timeout = 30;
+ }
+
+ if ((child_node = ezxml_child(cur_node,"timeout")) != NULL) {
+ char *endptr;
+ cache->storage.timeout = (int)strtol(child_node->txt,&endptr,10);
+ if(*endptr != 0 || cache->storage.timeout<1) {
+ ctx->set_error(ctx,400,"invalid rest cache <timeout> \"%s\" "
+ "(positive integer expected)",
+ child_node->txt);
+ return;
+ }
+ } else {
+ cache->storage.timeout = 120;
+ }
+
+ if ((child_node = ezxml_child(cur_node,"header_file")) != NULL) {
+ cache->storage.header_file = apr_pstrdup(ctx->pool, child_node->txt);
+ }
+
+ }
+
}
/**
@@ -831,6 +1350,33 @@ static void _mapcache_cache_tiff_configuration_post_config(mapcache_context *ctx
ctx->set_error(ctx, 400, "tiff cache %s has invalid count (%d,%d)",cache->count_x,cache->count_y);
return;
}
+
+#ifdef USE_GDAL
+ if(cache->storage.type == MAPCACHE_TIFF_STORAGE_REST &&
+ strncmp(cache->filename_template, "http://", 6) != 0 &&
+ strncmp(cache->filename_template, "https://", 7) != 0 ) {
+ ctx->set_error(ctx, 400, "tiff cache %s template pattern should begin with http:// or https://",cache->cache.name);
+ return;
+ }
+
+ if(cache->storage.type == MAPCACHE_TIFF_STORAGE_GOOGLE &&
+ strncmp(cache->filename_template, "https://storage.googleapis.com/",
+ strlen("https://storage.googleapis.com/")) != 0 &&
+ strncmp(cache->filename_template, "/vsigs/",
+ strlen("/vsigs/")) != 0 ) {
+ ctx->set_error(ctx, 400, "tiff cache %s template pattern should begin "
+ "with https://storage.googleapis.com/ or /vsigs/",cache->cache.name);
+ return;
+ }
+#else
+ if(cache->storage.type != MAPCACHE_TIFF_STORAGE_FILE )
+ {
+ ctx->set_error(ctx, 400, "tiff cache %s cannot use a network based "
+ "storage due to mising GDAL dependency",
+ cache->cache.name);
+ return;
+ }
+#endif
}
/**
@@ -845,10 +1391,10 @@ mapcache_cache* mapcache_cache_tiff_create(mapcache_context *ctx)
}
cache->cache.metadata = apr_table_make(ctx->pool,3);
cache->cache.type = MAPCACHE_CACHE_TIFF;
- cache->cache.tile_delete = _mapcache_cache_tiff_delete;
- cache->cache.tile_get = _mapcache_cache_tiff_get;
- cache->cache.tile_exists = _mapcache_cache_tiff_has_tile;
- cache->cache.tile_set = _mapcache_cache_tiff_set;
+ cache->cache._tile_delete = _mapcache_cache_tiff_delete;
+ cache->cache._tile_get = _mapcache_cache_tiff_get;
+ cache->cache._tile_exists = _mapcache_cache_tiff_has_tile;
+ cache->cache._tile_set = _mapcache_cache_tiff_set;
cache->cache.configuration_post_config = _mapcache_cache_tiff_configuration_post_config;
cache->cache.configuration_parse_xml = _mapcache_cache_tiff_configuration_parse_xml;
cache->count_x = 10;
@@ -864,6 +1410,13 @@ mapcache_cache* mapcache_cache_tiff_create(mapcache_context *ctx)
return (mapcache_cache*)cache;
}
+#else
+
+mapcache_cache* mapcache_cache_tiff_create(mapcache_context *ctx) {
+ ctx->set_error(ctx,400,"TIFF support not compiled in this version");
+ return NULL;
+}
+
#endif
/* vim: ts=2 sts=2 et sw=2
diff --git a/lib/cache_tokyocabinet.c b/lib/cache_tokyocabinet.c
index 27a055e..54dc976 100644
--- a/lib/cache_tokyocabinet.c
+++ b/lib/cache_tokyocabinet.c
@@ -27,9 +27,9 @@
* DEALINGS IN THE SOFTWARE.
*****************************************************************************/
+#include "mapcache.h"
#ifdef USE_TC
-#include "mapcache.h"
#include <apr_strings.h>
#include <apr_reslist.h>
#include <apr_file_info.h>
@@ -43,6 +43,14 @@
#include <unistd.h>
#endif
+typedef struct mapcache_cache_tc mapcache_cache_tc;
+struct mapcache_cache_tc {
+ mapcache_cache cache;
+ char *basedir;
+ char *key_template;
+ mapcache_context *ctx;
+};
+mapcache_cache *mapcache_cache_tc_create(mapcache_context *ctx);
struct tc_conn {
TCBDB *bdb;
@@ -216,6 +224,13 @@ mapcache_cache* mapcache_cache_tc_create(mapcache_context *ctx)
return (mapcache_cache*)cache;
}
+#else
+
+mapcache_cache* mapcache_cache_tc_create(mapcache_context *ctx) {
+ ctx->set_error(ctx,400,"TokyoCabinet support not compiled in this version");
+ return NULL;
+}
+
#endif
/* vim: ts=2 sts=2 et sw=2
diff --git a/lib/configuration.c b/lib/configuration.c
index 778c684..723e6bc 100644
--- a/lib/configuration.c
+++ b/lib/configuration.c
@@ -135,12 +135,12 @@ mapcache_cfg* mapcache_configuration_create(apr_pool_t *pool)
mapcache_imageio_create_png_q_format(pool,"PNG8",MAPCACHE_COMPRESSION_FAST,256),
"PNG8");
mapcache_configuration_add_image_format(cfg,
- mapcache_imageio_create_jpeg_format(pool,"JPEG",90,MAPCACHE_PHOTOMETRIC_YCBCR),
+ mapcache_imageio_create_jpeg_format(pool,"JPEG",90,MAPCACHE_PHOTOMETRIC_YCBCR,MAPCACHE_OPTIMIZE_YES),
"JPEG");
mapcache_configuration_add_image_format(cfg,
mapcache_imageio_create_mixed_format(pool,"mixed",
mapcache_configuration_get_image_format(cfg,"PNG"),
- mapcache_configuration_get_image_format(cfg,"JPEG")),
+ mapcache_configuration_get_image_format(cfg,"JPEG"), 255),
"mixed");
cfg->default_image_format = mapcache_configuration_get_image_format(cfg,"mixed");
cfg->reporting = MAPCACHE_REPORT_MSG;
diff --git a/lib/configuration_xml.c b/lib/configuration_xml.c
index a681de7..fdd9a7c 100644
--- a/lib/configuration_xml.c
+++ b/lib/configuration_xml.c
@@ -47,47 +47,6 @@ void parseMetadata(mapcache_context *ctx, ezxml_t node, apr_table_t *metadata)
}
}
-void parseTimeDimension(mapcache_context *ctx, ezxml_t node, mapcache_tileset *tileset) {
- const char *attr = NULL;
- if(tileset->timedimension) {
- ctx->set_error(ctx,400,"tileset \"%s\" can only have a single <timedimension>", tileset->name);
- return;
- }
- attr = ezxml_attr(node,"type");
- if(attr && *attr) {
- if(!strcmp(attr,"sqlite")) {
-#ifdef USE_SQLITE
- tileset->timedimension = mapcache_timedimension_sqlite_create(ctx->pool);
-#else
- ctx->set_error(ctx,400, "failed to add sqlite timedimension: Sqlite support is not available on this build");
- return;
-#endif
- } else {
- ctx->set_error(ctx,400,"unknown \"type\" attribute \"%s\" for %s's <timedimension>. Expecting one of (sqlite)",attr,tileset->name);
- return;
- }
-
- } else {
- ctx->set_error(ctx,400,"missing \"type\" attribute for %s's <timedimension>",tileset->name);
- return;
- }
- attr = ezxml_attr(node,"name");
- if(attr && *attr) {
- tileset->timedimension->key = apr_pstrdup(ctx->pool,attr);
- } else {
- tileset->timedimension->key = apr_pstrdup(ctx->pool,"TIME");
- }
-
- attr = ezxml_attr(node,"default");
- if(attr && *attr) {
- tileset->timedimension->default_value = apr_pstrdup(ctx->pool,attr);
- } else {
- ctx->set_error(ctx,400,"no \"default\" attribute for <timedimension> %s",tileset->timedimension->key);
- return;
- }
- tileset->timedimension->configuration_parse_xml(ctx,tileset->timedimension,node);
-}
-
void parseDimensions(mapcache_context *ctx, ezxml_t node, mapcache_tileset *tileset)
{
ezxml_t dimension_node;
@@ -96,7 +55,6 @@ void parseDimensions(mapcache_context *ctx, ezxml_t node, mapcache_tileset *tile
char *name = (char*)ezxml_attr(dimension_node,"name");
char *type = (char*)ezxml_attr(dimension_node,"type");
char *unit = (char*)ezxml_attr(dimension_node,"unit");
- char *skip_validation = (char*)ezxml_attr(dimension_node,"skip_validation");
char *default_value = (char*)ezxml_attr(dimension_node,"default");
mapcache_dimension *dimension = NULL;
@@ -108,17 +66,13 @@ void parseDimensions(mapcache_context *ctx, ezxml_t node, mapcache_tileset *tile
if(type && *type) {
if(!strcmp(type,"values")) {
- dimension = mapcache_dimension_values_create(ctx->pool);
+ dimension = mapcache_dimension_values_create(ctx,ctx->pool);
} else if(!strcmp(type,"regex")) {
- dimension = mapcache_dimension_regex_create(ctx->pool);
- } else if(!strcmp(type,"intervals")) {
- dimension = mapcache_dimension_intervals_create(ctx->pool);
+ dimension = mapcache_dimension_regex_create(ctx,ctx->pool);
} else if(!strcmp(type,"sqlite")) {
- dimension = mapcache_dimension_sqlite_create(ctx->pool);
+ dimension = mapcache_dimension_sqlite_create(ctx,ctx->pool);
} else if(!strcmp(type,"time")) {
- ctx->set_error(ctx,501,"time dimension type not implemented yet");
- return;
- dimension = mapcache_dimension_time_create(ctx->pool);
+ dimension = mapcache_dimension_time_create(ctx,ctx->pool);
} else {
ctx->set_error(ctx,400,"unknown dimension type \"%s\"",type);
return;
@@ -127,6 +81,7 @@ void parseDimensions(mapcache_context *ctx, ezxml_t node, mapcache_tileset *tile
ctx->set_error(ctx,400, "mandatory attribute \"type\" not found in <dimensions>");
return;
}
+ GC_CHECK_ERROR(ctx);
dimension->name = apr_pstrdup(ctx->pool,name);
@@ -134,16 +89,13 @@ void parseDimensions(mapcache_context *ctx, ezxml_t node, mapcache_tileset *tile
dimension->unit = apr_pstrdup(ctx->pool,unit);
}
- if(skip_validation && !strcmp(skip_validation,"true")) {
- dimension->skip_validation = MAPCACHE_TRUE;
- }
-
if(default_value && *default_value) {
dimension->default_value = apr_pstrdup(ctx->pool,default_value);
} else {
ctx->set_error(ctx,400,"dimension \"%s\" has no \"default\" attribute",dimension->name);
return;
}
+
dimension->configuration_parse_xml(ctx,dimension,dimension_node);
GC_CHECK_ERROR(ctx);
@@ -154,7 +106,51 @@ void parseDimensions(mapcache_context *ctx, ezxml_t node, mapcache_tileset *tile
ctx->set_error(ctx, 400, "<dimensions> for tileset \"%s\" has no dimensions defined (expecting <dimension> children)",tileset->name);
return;
}
+
tileset->dimensions = dimensions;
+ dimension_node = ezxml_child(node,"store_assemblies");
+ if(dimension_node && dimension_node->txt) {
+ if(!strcmp(dimension_node->txt,"false")) {
+ tileset->store_dimension_assemblies = 0;
+ } else if(strcmp(dimension_node->txt,"true")) {
+ ctx->set_error(ctx,400,"failed to parse <store_assemblies> (%s), expecting \"true\" or \"false\"",dimension_node->txt);
+ return;
+ }
+ }
+
+ dimension_node = ezxml_child(node,"assembly_type");
+ if(dimension_node) {
+ if(!strcmp(dimension_node->txt,"stack")) {
+ tileset->dimension_assembly_type = MAPCACHE_DIMENSION_ASSEMBLY_STACK;
+ } else if(!strcmp(dimension_node->txt,"animate")) {
+ tileset->dimension_assembly_type = MAPCACHE_DIMENSION_ASSEMBLY_ANIMATE;
+ ctx->set_error(ctx,400,"animate dimension assembly mode not implemented");
+ return;
+ } else if(strcmp(dimension_node->txt,"none")) {
+ ctx->set_error(ctx,400,"unknown dimension assembly mode (%s). Can be one of \"stack\" or \"none\"",dimension_node->txt);
+ return;
+ } else {
+ tileset->dimension_assembly_type = MAPCACHE_DIMENSION_ASSEMBLY_NONE;
+ }
+ }
+
+ /* 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
+ dimension=val3 if they are not found in cache */
+ dimension_node = ezxml_child(node,"subdimensions_read_only");
+ if(dimension_node) {
+ if(tileset->dimension_assembly_type == MAPCACHE_DIMENSION_ASSEMBLY_NONE) {
+ ctx->set_error(ctx,400,"<subdimensions_read_only> used on a tileset with no <assembly_type> set, which makes no sense");
+ return;
+ }
+ if(dimension_node && dimension_node->txt && !strcmp(dimension_node->txt,"true")) {
+ tileset->subdimension_read_only = 1;
+ } else if(strcmp(dimension_node->txt,"false")) {
+ ctx->set_error(ctx,400,"failed to parse <subdimensions_read_only> (%s), expecting \"true\" or \"false\"",dimension_node->txt);
+ return;
+ }
+ }
}
void parseGrid(mapcache_context *ctx, ezxml_t node, mapcache_cfg *config)
@@ -340,14 +336,14 @@ void parseSource(mapcache_context *ctx, ezxml_t node, mapcache_cfg *config)
source = NULL;
if(!strcmp(type,"wms")) {
source = mapcache_source_wms_create(ctx);
-#ifdef USE_MAPSERVER
} else if(!strcmp(type,"mapserver")) {
source = mapcache_source_mapserver_create(ctx);
-#endif
} else if(!strcmp(type,"gdal")) {
source = mapcache_source_gdal_create(ctx);
} else if(!strcmp(type,"dummy")) {
source = mapcache_source_dummy_create(ctx);
+ } else if(!strcmp(type,"fallback")) {
+ source = mapcache_source_fallback_create(ctx);
} else {
ctx->set_error(ctx, 400, "unknown source type %s for source \"%s\"", type, name);
return;
@@ -362,8 +358,22 @@ void parseSource(mapcache_context *ctx, ezxml_t node, mapcache_cfg *config)
parseMetadata(ctx, cur_node, source->metadata);
GC_CHECK_ERROR(ctx);
}
+ if ((cur_node = ezxml_child(node,"retries")) != NULL) {
+ source->retry_count = atoi(cur_node->txt);
+ if(source->retry_count > 10) {
+ ctx->set_error(ctx,400,"source (%s) <retries> count of %d is unreasonably large. max is 10", source->name, source->retry_count);
+ return;
+ }
+ }
+ if ((cur_node = ezxml_child(node,"retry_delay")) != NULL) {
+ source->retry_delay = (double)atof(cur_node->txt);
+ if(source->retry_delay < 0) {
+ ctx->set_error(ctx,400,"source (%s) retry delay of %f must be positive",source->name, source->retry_delay);
+ return;
+ }
+ }
- source->configuration_parse_xml(ctx,node,source);
+ source->configuration_parse_xml(ctx,node,source, config);
GC_CHECK_ERROR(ctx);
source->configuration_check(ctx,config,source);
GC_CHECK_ERROR(ctx);
@@ -422,6 +432,7 @@ void parseFormat(mapcache_context *ctx, ezxml_t node, mapcache_cfg *config)
}
} else if(!strcmp(type,"JPEG")) {
int quality = 95;
+ int optimize = TRUE;
mapcache_photometric photometric = MAPCACHE_PHOTOMETRIC_YCBCR;
if ((cur_node = ezxml_child(node,"quality")) != NULL) {
char *endptr;
@@ -445,10 +456,24 @@ void parseFormat(mapcache_context *ctx, ezxml_t node, mapcache_cfg *config)
return;
}
}
+ if ((cur_node = ezxml_child(node,"optimize")) != NULL) {
+ if(cur_node->txt && !strcasecmp(cur_node->txt,"false"))
+ optimize = MAPCACHE_OPTIMIZE_NO;
+ else if(cur_node->txt && !strcasecmp(cur_node->txt,"true"))
+ optimize = MAPCACHE_OPTIMIZE_YES;
+ else if(cur_node->txt && !strcasecmp(cur_node->txt,"arithmetic"))
+ optimize = MAPCACHE_OPTIMIZE_ARITHMETIC;
+ else {
+ ctx->set_error(ctx,500,"failed to parse jpeg format %s optimize %s. expecting true, false or arithmetic",
+ name,cur_node->txt);
+ return;
+ }
+ }
format = mapcache_imageio_create_jpeg_format(ctx->pool,
- name,quality,photometric);
+ name,quality,photometric,optimize);
} else if(!strcasecmp(type,"MIXED")) {
mapcache_image_format *transparent=NULL, *opaque=NULL;
+ unsigned int alpha_cutoff=255;
if ((cur_node = ezxml_child(node,"transparent")) != NULL) {
transparent = mapcache_configuration_get_image_format(config,cur_node->txt);
}
@@ -467,7 +492,10 @@ void parseFormat(mapcache_context *ctx, ezxml_t node, mapcache_cfg *config)
name,cur_node->txt,cur_node->txt);
return;
}
- format = mapcache_imageio_create_mixed_format(ctx->pool,name,transparent, opaque);
+ if ((cur_node = ezxml_child(node,"alpha_cutoff")) != NULL) {
+ alpha_cutoff = atoi(cur_node->txt);
+ }
+ format = mapcache_imageio_create_mixed_format(ctx->pool,name,transparent, opaque, alpha_cutoff);
} else {
ctx->set_error(ctx, 400, "unknown format type %s for format \"%s\"", type, name);
return;
@@ -486,6 +514,7 @@ void parseCache(mapcache_context *ctx, ezxml_t node, mapcache_cfg *config)
{
char *name = NULL, *type = NULL;
mapcache_cache *cache = NULL;
+ ezxml_t cur_node;
name = (char*)ezxml_attr(node,"name");
type = (char*)ezxml_attr(node,"type");
if(!name || !strlen(name)) {
@@ -520,69 +549,48 @@ void parseCache(mapcache_context *ctx, ezxml_t node, mapcache_cfg *config)
} else if(!strcmp(type,"google")) {
cache = mapcache_cache_google_create(ctx);
} else if(!strcmp(type,"bdb")) {
-#ifdef USE_BDB
cache = mapcache_cache_bdb_create(ctx);
-#else
- ctx->set_error(ctx,400, "failed to add cache \"%s\": Berkeley DB support is not available on this build",name);
- return;
-#endif
} else if(!strcmp(type,"tokyocabinet")) {
-#ifdef USE_TC
cache = mapcache_cache_tc_create(ctx);
-#else
- ctx->set_error(ctx,400, "failed to add cache \"%s\": Tokyo Cabinet support is not available on this build",name);
- return;
-#endif
} else if(!strcmp(type,"sqlite3")) {
-#ifdef USE_SQLITE
cache = mapcache_cache_sqlite_create(ctx);
-#else
- ctx->set_error(ctx,400, "failed to add cache \"%s\": sqlite support is not available on this build",name);
- return;
-#endif
} else if(!strcmp(type,"mbtiles")) {
-#ifdef USE_SQLITE
cache = mapcache_cache_mbtiles_create(ctx);
-#else
- ctx->set_error(ctx,400, "failed to add cache \"%s\": sqlite support is not available on this build",name);
- return;
-#endif
} else if(!strcmp(type,"memcache")) {
-#ifdef USE_MEMCACHE
cache = mapcache_cache_memcache_create(ctx);
-#else
- ctx->set_error(ctx,400, "failed to add cache \"%s\": memcache support is not available on this build",name);
- return;
-#endif
} else if(!strcmp(type,"tiff")) {
-#ifdef USE_TIFF
cache = mapcache_cache_tiff_create(ctx);
-#else
- ctx->set_error(ctx,400, "failed to add cache \"%s\": tiff support is not available on this build",name);
- return;
-#endif
} else if(!strcmp(type,"couchbase")) {
-#ifdef USE_COUCHBASE
cache = mapcache_cache_couchbase_create(ctx);
-#else
- ctx->set_error(ctx, 400, "failed to add cache \"%s\": couchbase support is not available on this build", name);
-#endif
} else if(!strcmp(type,"riak")) {
-#ifdef USE_RIAK
cache = mapcache_cache_riak_create(ctx);
-#else
- ctx->set_error(ctx, 400, "failed to add cache \"%s\": riak support is not available on this build", name);
-#endif
} else {
ctx->set_error(ctx, 400, "unknown cache type %s for cache \"%s\"", type, name);
return;
}
+ GC_CHECK_ERROR(ctx);
if(cache == NULL) {
ctx->set_error(ctx, 400, "failed to parse cache \"%s\"", name);
return;
}
cache->name = name;
+ if ((cur_node = ezxml_child(node,"retries")) != NULL) {
+ cache->retry_count = atoi(cur_node->txt);
+ if(cache->retry_count > 10) {
+ ctx->set_error(ctx,400,"cache (%s) <retries> count of %d is unreasonably large. max is 10", cache->name, cache->retry_count);
+ return;
+ }
+ }
+ if ((cur_node = ezxml_child(node,"retry_delay")) != NULL) {
+ cache->retry_delay = (double)atof(cur_node->txt);
+ if(cache->retry_delay < 0) {
+ ctx->set_error(ctx,400,"cache (%s) retry delay of %f must be positive",cache->name, cache->retry_delay);
+ return;
+ }
+ }
+
+
cache->configuration_parse_xml(ctx,node,cache,config);
GC_CHECK_ERROR(ctx);
mapcache_configuration_add_cache(config,cache,name);
@@ -824,12 +832,6 @@ void parseTileset(mapcache_context *ctx, ezxml_t node, mapcache_cfg *config)
GC_CHECK_ERROR(ctx);
}
- if ((cur_node = ezxml_child(node,"timedimension")) != NULL) {
- parseTimeDimension(ctx, cur_node, tileset);
- GC_CHECK_ERROR(ctx);
- }
-
-
if ((cur_node = ezxml_child(node,"cache")) != NULL) {
mapcache_cache *cache = mapcache_configuration_get_cache(config, cur_node->txt);
if(!cache) {
@@ -1115,8 +1117,6 @@ void mapcache_configuration_parse_xml(mapcache_context *ctx, const char *filenam
} else if ((node = ezxml_child(doc,"services")) != NULL) {
ctx->log(ctx,MAPCACHE_WARN,"<services> tag is deprecated, use <service type=\"wms\" enabled=\"true|false\">");
parseServices(ctx, node, config);
- } else {
- ctx->set_error(ctx, 400, "no <services> configured");
}
if(GC_HAS_ERROR(ctx)) goto cleanup;
@@ -1158,31 +1158,8 @@ void mapcache_configuration_parse_xml(mapcache_context *ctx, const char *filenam
mapcache_config_parse_locker(ctx,node,&config->locker);
GC_CHECK_ERROR(ctx);
} else {
- /* backwards compatibility */
- int micro_retry;
- mapcache_locker_disk *ldisk;
- config->locker = mapcache_locker_disk_create(ctx);
- ldisk = (mapcache_locker_disk*)config->locker;
- if((node = ezxml_child(doc,"lock_dir")) != NULL) {
- ldisk->dir = apr_pstrdup(ctx->pool, node->txt);
- } else {
- ldisk->dir = apr_pstrdup(ctx->pool,"/tmp");
- }
-
- if((node = ezxml_child(doc,"lock_retry")) != NULL) {
- char *endptr;
- micro_retry = strtol(node->txt,&endptr,10);
- if(*endptr != 0 || micro_retry <= 0) {
- ctx->set_error(ctx, 400, "failed to parse lock_retry microseconds \"%s\". Expecting a positive integer",
- node->txt);
- return;
- }
- } else {
- /* default retry interval is 1/100th of a second, i.e. 10000 microseconds */
- micro_retry = 10000;
- }
- config->locker->retry_interval = micro_retry / 1000000.0;
- config->locker->timeout=120;
+ mapcache_config_parse_locker_old(ctx,doc,config);
+ GC_CHECK_ERROR(ctx);
}
if((node = ezxml_child(doc,"threaded_fetching")) != NULL) {
diff --git a/lib/connection_pool.c b/lib/connection_pool.c
index 3bf3901..7f2e02a 100644
--- a/lib/connection_pool.c
+++ b/lib/connection_pool.c
@@ -78,7 +78,7 @@ apr_status_t mapcache_connection_pool_create(mapcache_connection_pool **cp, apr_
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, 200, 60*1000000,
+ rv = apr_reslist_create(&((*cp)->connexions), 1, 5, 1024, 60*1000000,
mapcache_connection_container_creator,
mapcache_connection_container_destructor,
NULL,
@@ -176,10 +176,12 @@ void mapcache_connection_pool_invalidate_connection(mapcache_context *ctx, mapca
pc->private->destructor(pc->connection);
free(pc->private->key);
free(pc);
+ break;
}
pred = pc;
pc = pc->private->next;
}
+ apr_reslist_release(ctx->connection_pool->connexions,(void*)pcc);
}
void mapcache_connection_pool_release_connection(mapcache_context *ctx, mapcache_pooled_connection *connection) {
diff --git a/lib/core.c b/lib/core.c
index 63f4244..9bc2952 100644
--- a/lib/core.c
+++ b/lib/core.c
@@ -458,7 +458,7 @@ mapcache_http_response *mapcache_core_get_map(mapcache_context *ctx, mapcache_re
return NULL;
}
}
- basemap->tileset->source->render_map(ctx, basemap);
+ mapcache_source_render_map(ctx, basemap->tileset->source, basemap);
if(GC_HAS_ERROR(ctx)) return NULL;
if(req_map->nmaps>1) {
if(!basemap->raw_image) {
@@ -467,7 +467,7 @@ mapcache_http_response *mapcache_core_get_map(mapcache_context *ctx, mapcache_re
}
for(i=1; i<req_map->nmaps; i++) {
mapcache_map *overlaymap = req_map->maps[i];
- overlaymap->tileset->source->render_map(ctx, overlaymap);
+ mapcache_source_render_map(ctx, overlaymap->tileset->source, overlaymap);
if(GC_HAS_ERROR(ctx)) return NULL;
if(!overlaymap->raw_image) {
overlaymap->raw_image = mapcache_imageio_decode(ctx,overlaymap->encoded_data);
@@ -579,7 +579,7 @@ mapcache_http_response *mapcache_core_get_featureinfo(mapcache_context *ctx,
ctx->set_error(ctx,404, "unsupported feature info format %s",fi->format);
return NULL;
}
- tileset->source->query_info(ctx,fi);
+ mapcache_source_query_info(ctx, tileset->source, fi);
if(GC_HAS_ERROR(ctx)) return NULL;
response = mapcache_http_response_create(ctx->pool);
response->data = fi->data;
@@ -620,7 +620,7 @@ mapcache_http_response* mapcache_core_respond_to_error(mapcache_context *ctx)
msg = ctx->_errmsg;
if(!msg) {
- msg = apr_pstrdup(ctx->pool,"an unspecified error has occured");
+ msg = apr_pstrdup(ctx->pool,"an unspecified error has occurred");
}
ctx->log(ctx,MAPCACHE_ERROR,msg);
diff --git a/lib/dimension.c b/lib/dimension.c
index cde7db6..5d55dd1 100644
--- a/lib/dimension.c
+++ b/lib/dimension.c
@@ -37,6 +37,47 @@
#include <sqlite3.h>
#include <float.h>
#endif
+#ifdef USE_PCRE
+#include <pcre.h>
+#else
+#include <regex.h>
+#endif
+
+typedef struct mapcache_dimension_time mapcache_dimension_time;
+typedef struct mapcache_dimension_time_sqlite mapcache_dimension_time_sqlite;
+typedef struct mapcache_dimension_intervals mapcache_dimension_intervals;
+typedef struct mapcache_dimension_values mapcache_dimension_values;
+typedef struct mapcache_dimension_sqlite mapcache_dimension_sqlite;
+typedef struct mapcache_dimension_regex mapcache_dimension_regex;
+typedef struct mapcache_dimension_composite mapcache_dimension_composite;
+
+struct mapcache_dimension_values {
+ mapcache_dimension dimension;
+ apr_array_header_t *values;
+ int case_sensitive;
+};
+
+struct mapcache_dimension_sqlite {
+ mapcache_dimension dimension;
+ char *dbfile;
+ char *get_values_for_entry_query;
+ char *get_all_values_query;
+};
+
+struct mapcache_dimension_regex {
+ mapcache_dimension dimension;
+ char *regex_string;
+#ifdef USE_PCRE
+ pcre *pcregex;
+#else
+ regex_t *regex;
+#endif
+};
+
+struct mapcache_dimension_time {
+ mapcache_dimension_sqlite dimension;
+};
+
#ifndef HAVE_TIMEGM
time_t timegm(struct tm *tm)
@@ -57,111 +98,92 @@ time_t timegm(struct tm *tm)
}
#endif
-static int _mapcache_dimension_intervals_validate(mapcache_context *ctx, mapcache_dimension *dim, char **value)
-{
- int i;
- char *endptr;
- mapcache_dimension_intervals *dimension;
- double val = strtod(*value,&endptr);
- *value = apr_psprintf(ctx->pool,"%g",val);
- if(*endptr != 0) {
- return MAPCACHE_FAILURE;
- }
- dimension = (mapcache_dimension_intervals*)dim;
- for(i=0; i<dimension->nintervals; i++) {
- double diff;
- mapcache_interval *interval = &dimension->intervals[i];
- if(val<interval->start || val>interval->end)
- continue;
- if(interval->resolution == 0)
- return MAPCACHE_SUCCESS;
- diff = fmod((val - interval->start),interval->resolution);
- if(diff == 0.0)
- return MAPCACHE_SUCCESS;
- }
- return MAPCACHE_FAILURE;
-}
-
-static apr_array_header_t* _mapcache_dimension_intervals_print(mapcache_context *ctx, mapcache_dimension *dim)
-{
- mapcache_dimension_intervals *dimension = (mapcache_dimension_intervals*)dim;
- apr_array_header_t *ret = apr_array_make(ctx->pool,dimension->nintervals,sizeof(char*));
- int i;
- for(i=0; i<dimension->nintervals; i++) {
- mapcache_interval *interval = &dimension->intervals[i];
- APR_ARRAY_PUSH(ret,char*) = apr_psprintf(ctx->pool,"%g/%g/%g",interval->start,interval->end,interval->resolution);
+apr_array_header_t *mapcache_requested_dimensions_clone(apr_pool_t *pool, apr_array_header_t *src) {
+ apr_array_header_t *ret = NULL;
+ if(src) {
+ int i;
+ ret = apr_array_make(pool,src->nelts,sizeof(mapcache_requested_dimension*));
+ for(i=0; i<src->nelts; i++) {
+ mapcache_requested_dimension *tiledim = apr_pcalloc(pool,sizeof(mapcache_requested_dimension));
+ mapcache_requested_dimension *srcdim = APR_ARRAY_IDX(src,i,mapcache_requested_dimension*);
+ *tiledim = *srcdim;
+ APR_ARRAY_PUSH(ret,mapcache_requested_dimension*) = tiledim;
+ }
}
return ret;
}
-
-static void _mapcache_dimension_intervals_parse_xml(mapcache_context *ctx, mapcache_dimension *dim,
- ezxml_t node)
-{
- mapcache_dimension_intervals *dimension;
- char *key,*last;
- char *values;
- const char *entry = node->txt;
- int count = 1;
- if(!entry || !*entry) {
- ctx->set_error(ctx,400,"failed to parse dimension values: none supplied");
+void mapcache_set_requested_dimension(mapcache_context *ctx, apr_array_header_t *dimensions, const char *name, const char *value) {
+ int i;
+ if(!dimensions || dimensions->nelts <= 0) {
+ ctx->set_error(ctx,500,"BUG: no dimensions configure for tile/map");
return;
}
- dimension = (mapcache_dimension_intervals*)dim;
- values = apr_pstrdup(ctx->pool,entry);
-
- for(key=values; *key; key++) if(*key == ',') count++;
-
- dimension->intervals = (mapcache_interval*)apr_pcalloc(ctx->pool,count*sizeof(mapcache_interval));
-
- for (key = apr_strtok(values, ",", &last); key != NULL;
- key = apr_strtok(NULL, ",", &last)) {
- char *endptr;
- mapcache_interval *interval = &dimension->intervals[dimension->nintervals];
- interval->start = strtod(key,&endptr);
- if(*endptr != '/') {
- ctx->set_error(ctx,400,"failed to parse min dimension value \"%s\" in \"%s\" for dimension %s",key,entry,dim->name);
+ for(i=0;i<dimensions->nelts;i++) {
+ mapcache_requested_dimension *dim = APR_ARRAY_IDX(dimensions,i,mapcache_requested_dimension*);
+ if(!strcasecmp(dim->dimension->name,name)) {
+ dim->requested_value = value?apr_pstrdup(ctx->pool,value):NULL;
return;
}
- key = endptr+1;
+ }
+ ctx->set_error(ctx,500,"BUG: dimension (%s) not found in tile/map",name);
+}
- interval->end = strtod(key,&endptr);
- if(*endptr != '/') {
- ctx->set_error(ctx,400,"failed to parse max dimension value \"%s\" in \"%s\" for dimension %s",key,entry,dim->name);
- return;
- }
- key = endptr+1;
- interval->resolution = strtod(key,&endptr);
- if(*endptr != '\0') {
- ctx->set_error(ctx,400,"failed to parse resolution dimension value \"%s\" in \"%s\" for dimension %s",key,entry,dim->name);
+void mapcache_set_cached_dimension(mapcache_context *ctx, apr_array_header_t *dimensions, const char *name, const char *value) {
+ int i;
+ if(!dimensions || dimensions->nelts <= 0) {
+ ctx->set_error(ctx,500,"BUG: no dimensions configure for tile/map");
+ return;
+ }
+ for(i=0;i<dimensions->nelts;i++) {
+ mapcache_requested_dimension *dim = APR_ARRAY_IDX(dimensions,i,mapcache_requested_dimension*);
+ if(!strcasecmp(dim->dimension->name,name)) {
+ dim->cached_value = value?apr_pstrdup(ctx->pool,value):NULL;
return;
}
- dimension->nintervals++;
}
+ ctx->set_error(ctx,500,"BUG: dimension (%s) not found in tile/map",name);
+}
- if(!dimension->nintervals) {
- ctx->set_error(ctx, 400, "<dimension> \"%s\" has no intervals",dim->name);
- return;
- }
+void mapcache_tile_set_cached_dimension(mapcache_context *ctx, mapcache_tile *tile, const char *name, const char *value) {
+ mapcache_set_cached_dimension(ctx,tile->dimensions,name,value);
+}
+
+void mapcache_map_set_cached_dimension(mapcache_context *ctx, mapcache_map *map, const char *name, const char *value) {
+ mapcache_set_cached_dimension(ctx,map->dimensions,name,value);
+}
+void mapcache_tile_set_requested_dimension(mapcache_context *ctx, mapcache_tile *tile, const char *name, const char *value) {
+ mapcache_set_requested_dimension(ctx,tile->dimensions,name,value);
}
-static int _mapcache_dimension_regex_validate(mapcache_context *ctx, mapcache_dimension *dim, char **value)
+void mapcache_map_set_requested_dimension(mapcache_context *ctx, mapcache_map *map, const char *name, const char *value) {
+ mapcache_set_requested_dimension(ctx,map->dimensions,name,value);
+}
+
+static apr_array_header_t* _mapcache_dimension_regex_get_entries_for_value(mapcache_context *ctx, mapcache_dimension *dim, const char *value,
+ mapcache_tileset *tileset, mapcache_extent *extent, mapcache_grid *grid)
{
mapcache_dimension_regex *dimension = (mapcache_dimension_regex*)dim;
+ apr_array_header_t *values = apr_array_make(ctx->pool,1,sizeof(char*));
#ifdef USE_PCRE
int ovector[30];
- int rc = pcre_exec(dimension->pcregex,NULL,*value,strlen(*value),0,0,ovector,30);
- if(rc>0)
- return MAPCACHE_SUCCESS;
+ int rc = pcre_exec(dimension->pcregex,NULL,value,strlen(value),0,0,ovector,30);
+ if(rc>0) {
+ APR_ARRAY_PUSH(values,char*) = apr_pstrdup(ctx->pool,value);
+ }
#else
- if(!regexec(dimension->regex,*value,0,0,0)) {
- return MAPCACHE_SUCCESS;
+ if(!regexec(dimension->regex,value,0,0,0)) {
+ APR_ARRAY_PUSH(values,char*) = apr_pstrdup(ctx->pool,value);
}
#endif
- return MAPCACHE_FAILURE;
+ else {
+ ctx->set_error(ctx,400,"failed to validate requested value for dimension (%s)",dim->name);
+ }
+ return values;
}
-static apr_array_header_t* _mapcache_dimension_regex_print(mapcache_context *ctx, mapcache_dimension *dim)
+static apr_array_header_t* _mapcache_dimension_regex_get_all_entries(mapcache_context *ctx, mapcache_dimension *dim,
+ mapcache_tileset *tileset, mapcache_extent *extent, mapcache_grid *grid)
{
mapcache_dimension_regex *dimension = (mapcache_dimension_regex*)dim;
apr_array_header_t *ret = apr_array_make(ctx->pool,1,sizeof(char*));
@@ -174,32 +196,35 @@ static void _mapcache_dimension_regex_parse_xml(mapcache_context *ctx, mapcache_
ezxml_t node)
{
mapcache_dimension_regex *dimension;
- const char *entry = node->txt;
- if(!entry || !*entry) {
- ctx->set_error(ctx,400,"failed to parse dimension regex: none supplied");
+ ezxml_t child_node = ezxml_child(node,"regex");
+
+ dimension = (mapcache_dimension_regex*)dim;
+
+ 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");
return;
}
- dimension = (mapcache_dimension_regex*)dim;
- dimension->regex_string = apr_pstrdup(ctx->pool,entry);
#ifdef USE_PCRE
{
const char *pcre_err;
int pcre_offset;
- dimension->pcregex = pcre_compile(entry,0,&pcre_err, &pcre_offset,0);
+ 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",
- entry,dim->name,pcre_err);
+ dimension->regex_string,dim->name,pcre_err);
return;
}
}
#else
{
- int rc = regcomp(dimension->regex, entry, REG_EXTENDED);
+ int rc = regcomp(dimension->regex, dimension->regex_string, REG_EXTENDED);
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",
- entry,dim->name,errmsg);
+ dimension->regex_string,dim->name,errmsg);
return;
}
}
@@ -207,29 +232,40 @@ static void _mapcache_dimension_regex_parse_xml(mapcache_context *ctx, mapcache_
}
-static int _mapcache_dimension_values_validate(mapcache_context *ctx, mapcache_dimension *dim, char **value)
+static apr_array_header_t* _mapcache_dimension_values_get_entries_for_value(mapcache_context *ctx, mapcache_dimension *dim, const char *value,
+ mapcache_tileset *tileset, mapcache_extent *extent, mapcache_grid *grid)
{
int i;
mapcache_dimension_values *dimension = (mapcache_dimension_values*)dim;
- for(i=0; i<dimension->nvalues; i++) {
+ apr_array_header_t *values = apr_array_make(ctx->pool,1,sizeof(char*));
+ for(i=0; i<dimension->values->nelts; i++) {
+ char *cur_val = APR_ARRAY_IDX(dimension->values,i,char*);
if(dimension->case_sensitive) {
- if(!strcmp(*value,dimension->values[i]))
- return MAPCACHE_SUCCESS;
+ if(!strcmp(value,cur_val)) {
+ APR_ARRAY_PUSH(values,char*) = apr_pstrdup(ctx->pool,value);
+ break;
+ }
} else {
- if(!strcasecmp(*value,dimension->values[i]))
- return MAPCACHE_SUCCESS;
+ if(!strcasecmp(value,cur_val)) {
+ APR_ARRAY_PUSH(values,char*) = apr_pstrdup(ctx->pool,value);
+ break;
+ }
}
}
- return MAPCACHE_FAILURE;
+ if(i == dimension->values->nelts) {
+ ctx->set_error(ctx,400,"failed to validate requested value for dimension (%s)",dim->name);
+ }
+ return values;
}
-static apr_array_header_t* _mapcache_dimension_values_print(mapcache_context *ctx, mapcache_dimension *dim)
+static apr_array_header_t* _mapcache_dimension_values_get_all_entries(mapcache_context *ctx, mapcache_dimension *dim,
+ mapcache_tileset *tileset, mapcache_extent *extent, mapcache_grid *grid)
{
mapcache_dimension_values *dimension = (mapcache_dimension_values*)dim;
- apr_array_header_t *ret = apr_array_make(ctx->pool,dimension->nvalues,sizeof(char*));
+ apr_array_header_t *ret = apr_array_make(ctx->pool,dimension->values->nelts,sizeof(char*));
int i;
- for(i=0; i<dimension->nvalues; i++) {
- APR_ARRAY_PUSH(ret,char*) = apr_pstrdup(ctx->pool,dimension->values[i]);
+ for(i=0; i<dimension->values->nelts; i++) {
+ APR_ARRAY_PUSH(ret,char*) = apr_pstrdup(ctx->pool,APR_ARRAY_IDX(dimension->values,i,char*));
}
return ret;
}
@@ -238,81 +274,59 @@ static apr_array_header_t* _mapcache_dimension_values_print(mapcache_context *ct
static void _mapcache_dimension_values_parse_xml(mapcache_context *ctx, mapcache_dimension *dim,
ezxml_t node)
{
- int count = 1;
mapcache_dimension_values *dimension;
- const char *case_sensitive;
- char *key,*last;
- char *values;
- const char *entry = node->txt;
- if(!entry || !*entry) {
- ctx->set_error(ctx,400,"failed to parse dimension values: none supplied");
+ ezxml_t child_node = ezxml_child(node,"value");
+ dimension = (mapcache_dimension_values*)dim;
+
+ if(!child_node) {
+ ctx->set_error(ctx,400,"failed to parse dimension values: no <value> children supplied");
return;
}
-
- dimension = (mapcache_dimension_values*)dim;
- case_sensitive = ezxml_attr(node,"case_sensitive");
- if(case_sensitive && !strcasecmp(case_sensitive,"true")) {
- dimension->case_sensitive = 1;
+ 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>");
+ return;
+ }
+ APR_ARRAY_PUSH(dimension->values,char*) = apr_pstrdup(ctx->pool,entry);
}
- values = apr_pstrdup(ctx->pool,entry);
- for(key=values; *key; key++) if(*key == ',') count++;
-
- dimension->values = (char**)apr_pcalloc(ctx->pool,count*sizeof(char*));
-
- for (key = apr_strtok(values, ",", &last); key != NULL;
- key = apr_strtok(NULL, ",", &last)) {
- dimension->values[dimension->nvalues]=key;
- dimension->nvalues++;
+ child_node = ezxml_child(node,"case_sensitive");
+ if(child_node && child_node->txt) {
+ if(!strcasecmp(child_node->txt,"true")) {
+ dimension->case_sensitive = 1;
+ }
}
- if(!dimension->nvalues) {
+
+ if(!dimension->values->nelts) {
ctx->set_error(ctx, 400, "<dimension> \"%s\" has no values",dim->name);
return;
}
}
-mapcache_dimension* mapcache_dimension_values_create(apr_pool_t *pool)
+mapcache_dimension* mapcache_dimension_values_create(mapcache_context *ctx, apr_pool_t *pool)
{
mapcache_dimension_values *dimension = apr_pcalloc(pool, sizeof(mapcache_dimension_values));
dimension->dimension.type = MAPCACHE_DIMENSION_VALUES;
- dimension->nvalues = 0;
- dimension->dimension.validate = _mapcache_dimension_values_validate;
+ 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;
- dimension->dimension.print_ogc_formatted_values = _mapcache_dimension_values_print;
- return (mapcache_dimension*)dimension;
-}
-
-mapcache_dimension* mapcache_dimension_time_create(apr_pool_t *pool)
-{
- mapcache_dimension_time *dimension = apr_pcalloc(pool, sizeof(mapcache_dimension_time));
- dimension->dimension.type = MAPCACHE_DIMENSION_TIME;
- dimension->nintervals = 0;
-// dimension->dimension.validate = _mapcache_dimension_time_validate;
-// dimension->dimension.parse = _mapcache_dimension_time_parse;
-// dimension->dimension.print_ogc_formatted_values = _mapcache_dimension_time_print;
+ dimension->dimension.get_all_entries = _mapcache_dimension_values_get_all_entries;
+ dimension->dimension.get_all_ogc_formatted_entries = _mapcache_dimension_values_get_all_entries;
return (mapcache_dimension*)dimension;
}
-mapcache_dimension* mapcache_dimension_intervals_create(apr_pool_t *pool)
-{
- mapcache_dimension_intervals *dimension = apr_pcalloc(pool, sizeof(mapcache_dimension_intervals));
- dimension->dimension.type = MAPCACHE_DIMENSION_INTERVALS;
- dimension->nintervals = 0;
- dimension->dimension.validate = _mapcache_dimension_intervals_validate;
- dimension->dimension.configuration_parse_xml = _mapcache_dimension_intervals_parse_xml;
- dimension->dimension.print_ogc_formatted_values = _mapcache_dimension_intervals_print;
- return (mapcache_dimension*)dimension;
-}
-mapcache_dimension* mapcache_dimension_regex_create(apr_pool_t *pool)
+mapcache_dimension* mapcache_dimension_regex_create(mapcache_context *ctx, apr_pool_t *pool)
{
mapcache_dimension_regex *dimension = apr_pcalloc(pool, sizeof(mapcache_dimension_regex));
dimension->dimension.type = MAPCACHE_DIMENSION_REGEX;
#ifndef USE_PCRE
dimension->regex = (regex_t*)apr_pcalloc(pool, sizeof(regex_t));
#endif
- dimension->dimension.validate = _mapcache_dimension_regex_validate;
+ dimension->dimension.get_entries_for_value = _mapcache_dimension_regex_get_entries_for_value;
dimension->dimension.configuration_parse_xml = _mapcache_dimension_regex_parse_xml;
- dimension->dimension.print_ogc_formatted_values = _mapcache_dimension_regex_print;
+ dimension->dimension.get_all_entries = _mapcache_dimension_regex_get_all_entries;
+ dimension->dimension.get_all_ogc_formatted_entries = _mapcache_dimension_regex_get_all_entries;
return (mapcache_dimension*)dimension;
}
@@ -357,14 +371,10 @@ void mapcache_sqlite_dimension_connection_destructor(void *conn_)
free(conn);
}
-static mapcache_pooled_connection* _sqlite_time_dimension_get_conn(mapcache_context *ctx, mapcache_timedimension_sqlite *dim) {
- mapcache_pooled_connection *pc = mapcache_connection_pool_get_connection(ctx,dim->timedimension.key,
- mapcache_sqlite_dimension_connection_constructor,
- mapcache_sqlite_dimension_connection_destructor, dim->dbfile);
- return pc;
-}
-static mapcache_pooled_connection* _sqlite_dimension_get_conn(mapcache_context *ctx, mapcache_dimension_sqlite *dim) {
- mapcache_pooled_connection *pc = mapcache_connection_pool_get_connection(ctx,dim->dimension.name,
+static mapcache_pooled_connection* _sqlite_dimension_get_conn(mapcache_context *ctx, mapcache_tileset *tileset, mapcache_dimension_sqlite *dim) {
+ mapcache_dimension *pdim = (mapcache_dimension*)dim;
+ char *conn_key = apr_pstrcat(ctx->pool,"dim_",tileset?tileset->name:"","_",pdim->name,NULL);
+ mapcache_pooled_connection *pc = mapcache_connection_pool_get_connection(ctx,conn_key,
mapcache_sqlite_dimension_connection_constructor,
mapcache_sqlite_dimension_connection_destructor, dim->dbfile);
return pc;
@@ -379,16 +389,85 @@ static void _sqlite_dimension_release_conn(mapcache_context *ctx, mapcache_poole
}
}
+static void _mapcache_dimension_sqlite_bind_parameters(mapcache_context *ctx, sqlite3_stmt *stmt, sqlite3 *handle,
+ const char *value,
+ mapcache_tileset *tileset, mapcache_extent *extent, mapcache_grid *grid) {
+ int paramidx,ret;
+ paramidx = sqlite3_bind_parameter_index(stmt, ":dim");
+ if (paramidx) {
+ ret = sqlite3_bind_text(stmt, paramidx, value, -1, SQLITE_STATIC);
+ if(ret != SQLITE_OK) {
+ ctx->set_error(ctx,400, "sqlite dimension failed to bind :dim : %s", sqlite3_errmsg(handle));
+ return;
+ }
+ }
+
+ if(tileset) {
+ paramidx = sqlite3_bind_parameter_index(stmt, ":tileset");
+ if (paramidx) {
+ ret = sqlite3_bind_text(stmt, paramidx, tileset->name, -1, SQLITE_STATIC);
+ if(ret != SQLITE_OK) {
+ ctx->set_error(ctx,400, "sqlite dimension failed to bind :tileset : %s", sqlite3_errmsg(handle));
+ return;
+ }
+ }
+ }
-static int _mapcache_dimension_sqlite_validate(mapcache_context *ctx, mapcache_dimension *dim, char **value)
+ if(grid) {
+ paramidx = sqlite3_bind_parameter_index(stmt, ":gridsrs");
+ if (paramidx) {
+ ret = sqlite3_bind_text(stmt, paramidx, grid->srs, -1, SQLITE_STATIC);
+ if(ret != SQLITE_OK) {
+ ctx->set_error(ctx,400, "failed to bind :gridsrs %s", sqlite3_errmsg(handle));
+ return;
+ }
+ }
+ }
+
+ paramidx = sqlite3_bind_parameter_index(stmt, ":minx");
+ if (paramidx) {
+ ret = sqlite3_bind_double(stmt, paramidx, extent?extent->minx:-DBL_MAX);
+ if(ret != SQLITE_OK) {
+ ctx->set_error(ctx,400, "failed to bind :minx %s", sqlite3_errmsg(handle));
+ return;
+ }
+ }
+ paramidx = sqlite3_bind_parameter_index(stmt, ":miny");
+ if (paramidx) {
+ ret = sqlite3_bind_double(stmt, paramidx, extent?extent->miny:-DBL_MAX);
+ if(ret != SQLITE_OK) {
+ ctx->set_error(ctx,400, "failed to bind :miny %s", sqlite3_errmsg(handle));
+ return;
+ }
+ }
+ paramidx = sqlite3_bind_parameter_index(stmt, ":maxx");
+ if (paramidx) {
+ ret = sqlite3_bind_double(stmt, paramidx, extent?extent->maxx:DBL_MAX);
+ if(ret != SQLITE_OK) {
+ ctx->set_error(ctx,400, "failed to bind :maxx %s", sqlite3_errmsg(handle));
+ return;
+ }
+ }
+ paramidx = sqlite3_bind_parameter_index(stmt, ":maxy");
+ if (paramidx) {
+ ret = sqlite3_bind_double(stmt, paramidx, extent?extent->maxy:DBL_MAX);
+ if(ret != SQLITE_OK) {
+ ctx->set_error(ctx,400, "failed to bind :maxy %s", sqlite3_errmsg(handle));
+ return;
+ }
+ }
+}
+static apr_array_header_t* _mapcache_dimension_sqlite_get_entries_for_value(mapcache_context *ctx, mapcache_dimension *dim, const char *value,
+ mapcache_tileset *tileset, mapcache_extent *extent, mapcache_grid *grid)
{
mapcache_dimension_sqlite *dimension = (mapcache_dimension_sqlite*)dim;
struct sqlite_dimension_conn *conn = NULL;
- int sqliteret,paramidx,ret=MAPCACHE_FAILURE;
mapcache_pooled_connection *pc;
- pc = _sqlite_dimension_get_conn(ctx,dimension);
+ apr_array_header_t *values = apr_array_make(ctx->pool,1,sizeof(char*));
+ int sqliteret;
+ pc = _sqlite_dimension_get_conn(ctx,tileset,dimension);
if (GC_HAS_ERROR(ctx)) {
- return ret;
+ return values;
}
conn = pc->connection;
if(!conn->prepared_statements) {
@@ -396,21 +475,19 @@ static int _mapcache_dimension_sqlite_validate(mapcache_context *ctx, mapcache_d
conn->n_statements = 2;
}
if(!conn->prepared_statements[0]) {
- sqliteret = sqlite3_prepare_v2(conn->handle, dimension->validate_query, -1, &conn->prepared_statements[0], NULL);
- if(sqliteret != SQLITE_OK) {
+ if(SQLITE_OK != sqlite3_prepare_v2(conn->handle, dimension->get_values_for_entry_query, -1, &conn->prepared_statements[0], NULL)) {
ctx->set_error(ctx, 500, "sqlite dimension backend failed on preparing query: %s", sqlite3_errmsg(conn->handle));
goto cleanup;
}
}
- paramidx = sqlite3_bind_parameter_index(conn->prepared_statements[0], ":dim");
- if (paramidx) {
- sqliteret = sqlite3_bind_text(conn->prepared_statements[0], paramidx, *value, -1, SQLITE_STATIC);
- if(sqliteret != SQLITE_OK) {
- ctx->set_error(ctx,400, "sqlite dimension failed to bind :dim : %s", sqlite3_errmsg(conn->handle));
- goto cleanup;
- }
+ _mapcache_dimension_sqlite_bind_parameters(ctx,conn->prepared_statements[0],conn->handle,value,
+ tileset,extent,grid);
+ if (GC_HAS_ERROR(ctx)) {
+ return values;
}
+
+
do {
sqliteret = sqlite3_step(conn->prepared_statements[0]);
if (sqliteret != SQLITE_DONE && sqliteret != SQLITE_ROW && sqliteret != SQLITE_BUSY && sqliteret != SQLITE_LOCKED) {
@@ -418,11 +495,8 @@ static int _mapcache_dimension_sqlite_validate(mapcache_context *ctx, mapcache_d
goto cleanup;
}
if(sqliteret == SQLITE_ROW) {
- const char* dim_modified = (const char*) sqlite3_column_text(conn->prepared_statements[0], 0);
- if(strcmp(dim_modified, *value)) {
- *value = apr_pstrdup(ctx->pool, dim_modified);
- }
- ret = MAPCACHE_SUCCESS;
+ const char* dimrow = (const char*) sqlite3_column_text(conn->prepared_statements[0], 0);
+ APR_ARRAY_PUSH(values,char*) = apr_pstrdup(ctx->pool,dimrow);
}
} while (sqliteret == SQLITE_ROW || sqliteret == SQLITE_BUSY || sqliteret == SQLITE_LOCKED);
@@ -432,17 +506,18 @@ cleanup:
}
_sqlite_dimension_release_conn(ctx,pc);
- return ret;
+ return values;
}
-static apr_array_header_t* _mapcache_dimension_sqlite_print(mapcache_context *ctx, mapcache_dimension *dim)
+static apr_array_header_t* _mapcache_dimension_sqlite_get_all_entries(mapcache_context *ctx, mapcache_dimension *dim,
+ mapcache_tileset *tileset, mapcache_extent *extent, mapcache_grid *grid)
{
mapcache_dimension_sqlite *dimension = (mapcache_dimension_sqlite*)dim;
struct sqlite_dimension_conn *conn = NULL;
int sqliteret;
apr_array_header_t *ret = apr_array_make(ctx->pool,0,sizeof(char*));
mapcache_pooled_connection *pc;
- pc = _sqlite_dimension_get_conn(ctx,dimension);
+ pc = _sqlite_dimension_get_conn(ctx,tileset,dimension);
if (GC_HAS_ERROR(ctx)) {
return ret;
}
@@ -453,12 +528,16 @@ static apr_array_header_t* _mapcache_dimension_sqlite_print(mapcache_context *ct
}
if(!conn->prepared_statements[1]) {
- sqliteret = sqlite3_prepare_v2(conn->handle, dimension->list_query, -1, &conn->prepared_statements[1], NULL);
+ sqliteret = sqlite3_prepare_v2(conn->handle, dimension->get_all_values_query, -1, &conn->prepared_statements[1], NULL);
if(sqliteret != SQLITE_OK) {
ctx->set_error(ctx, 500, "sqlite dimension backend failed on preparing query: %s", sqlite3_errmsg(conn->handle));
goto cleanup;
}
}
+ _mapcache_dimension_sqlite_bind_parameters(ctx,conn->prepared_statements[1],conn->handle,NULL,tileset,extent,grid);
+ if (GC_HAS_ERROR(ctx)) {
+ return ret;
+ }
do {
sqliteret = sqlite3_step(conn->prepared_statements[1]);
if (sqliteret != SQLITE_DONE && sqliteret != SQLITE_ROW && sqliteret != SQLITE_BUSY && sqliteret != SQLITE_LOCKED) {
@@ -498,14 +577,14 @@ static void _mapcache_dimension_sqlite_parse_xml(mapcache_context *ctx, mapcache
}
child = ezxml_child(node,"validate_query");
if(child) {
- dimension->validate_query = apr_pstrdup(ctx->pool, child->txt);
+ dimension->get_values_for_entry_query = apr_pstrdup(ctx->pool, child->txt);
} else {
ctx->set_error(ctx,400,"sqlite dimension \"%s\" has no <validate_query> node", dim->name);
return;
}
child = ezxml_child(node,"list_query");
if(child) {
- dimension->list_query = apr_pstrdup(ctx->pool, child->txt);
+ dimension->get_all_values_query = apr_pstrdup(ctx->pool, child->txt);
} else {
ctx->set_error(ctx,400,"sqlite dimension \"%s\" has no <list_query> node", dim->name);
return;
@@ -514,63 +593,13 @@ static void _mapcache_dimension_sqlite_parse_xml(mapcache_context *ctx, mapcache
}
-static void _bind_sqlite_timedimension_params(mapcache_context *ctx, sqlite3_stmt *stmt,
- sqlite3 *handle, mapcache_tileset *tileset, mapcache_grid *grid, mapcache_extent *extent,
+static void _bind_sqlite_dimension_time_params(mapcache_context *ctx, sqlite3_stmt *stmt,
+ sqlite3 *handle, const char *dim_value, mapcache_tileset *tileset, mapcache_grid *grid, mapcache_extent *extent,
time_t start, time_t end)
{
int paramidx,ret;
- paramidx = sqlite3_bind_parameter_index(stmt, ":tileset");
- if (paramidx) {
- ret = sqlite3_bind_text(stmt, paramidx, tileset->name, -1, SQLITE_STATIC);
- if(ret != SQLITE_OK) {
- ctx->set_error(ctx,400, "failed to bind :tileset: %s", sqlite3_errmsg(handle));
- return;
- }
- }
-
- if(grid) {
- paramidx = sqlite3_bind_parameter_index(stmt, ":gridsrs");
- if (paramidx) {
- ret = sqlite3_bind_text(stmt, paramidx, grid->srs, -1, SQLITE_STATIC);
- if(ret != SQLITE_OK) {
- ctx->set_error(ctx,400, "failed to bind :gridsrs %s", sqlite3_errmsg(handle));
- return;
- }
- }
- }
-
- paramidx = sqlite3_bind_parameter_index(stmt, ":minx");
- if (paramidx) {
- ret = sqlite3_bind_double(stmt, paramidx, extent?extent->minx:-DBL_MAX);
- if(ret != SQLITE_OK) {
- ctx->set_error(ctx,400, "failed to bind :minx %s", sqlite3_errmsg(handle));
- return;
- }
- }
- paramidx = sqlite3_bind_parameter_index(stmt, ":miny");
- if (paramidx) {
- ret = sqlite3_bind_double(stmt, paramidx, extent?extent->miny:-DBL_MAX);
- if(ret != SQLITE_OK) {
- ctx->set_error(ctx,400, "failed to bind :miny %s", sqlite3_errmsg(handle));
- return;
- }
- }
- paramidx = sqlite3_bind_parameter_index(stmt, ":maxx");
- if (paramidx) {
- ret = sqlite3_bind_double(stmt, paramidx, extent?extent->maxx:DBL_MAX);
- if(ret != SQLITE_OK) {
- ctx->set_error(ctx,400, "failed to bind :maxx %s", sqlite3_errmsg(handle));
- return;
- }
- }
- paramidx = sqlite3_bind_parameter_index(stmt, ":maxy");
- if (paramidx) {
- ret = sqlite3_bind_double(stmt, paramidx, extent?extent->maxy:DBL_MAX);
- if(ret != SQLITE_OK) {
- ctx->set_error(ctx,400, "failed to bind :maxy %s", sqlite3_errmsg(handle));
- return;
- }
- }
+
+ _mapcache_dimension_sqlite_bind_parameters(ctx,stmt,handle,dim_value,tileset,extent,grid);
paramidx = sqlite3_bind_parameter_index(stmt, ":start_timestamp");
if (paramidx) {
@@ -591,14 +620,14 @@ static void _bind_sqlite_timedimension_params(mapcache_context *ctx, sqlite3_stm
}
}
-apr_array_header_t *_mapcache_timedimension_sqlite_get_entries(mapcache_context *ctx, mapcache_timedimension *dim,
- mapcache_tileset *tileset, mapcache_grid *grid, mapcache_extent *extent, time_t start, time_t end) {
- mapcache_timedimension_sqlite *sdim = (mapcache_timedimension_sqlite*)dim;
- int ret;
+apr_array_header_t *_mapcache_dimension_time_get_entries(mapcache_context *ctx, mapcache_dimension_time *dim, const char *dim_value,
+ mapcache_tileset *tileset, mapcache_extent *extent, mapcache_grid *grid, time_t *intervals, int n_intervals) {
+ mapcache_dimension_sqlite *sdim = (mapcache_dimension_sqlite*)dim;
+ int i,ret;
apr_array_header_t *time_ids = NULL;
mapcache_pooled_connection *pc;
struct sqlite_dimension_conn *conn;
- pc = _sqlite_time_dimension_get_conn(ctx,sdim);
+ pc = _sqlite_dimension_get_conn(ctx,tileset,sdim);
if (GC_HAS_ERROR(ctx)) {
return NULL;
}
@@ -608,41 +637,46 @@ apr_array_header_t *_mapcache_timedimension_sqlite_get_entries(mapcache_context
conn->n_statements = 1;
}
if(!conn->prepared_statements[0]) {
- ret = sqlite3_prepare_v2(conn->handle, sdim->query, -1, &conn->prepared_statements[0], NULL);
+ ret = sqlite3_prepare_v2(conn->handle, sdim->get_values_for_entry_query, -1, &conn->prepared_statements[0], NULL);
if(ret != SQLITE_OK) {
ctx->set_error(ctx, 500, "time sqlite backend failed on preparing query: %s", sqlite3_errmsg(conn->handle));
_sqlite_dimension_release_conn(ctx, pc);
return NULL;
}
}
+
+ for(i=0;i<n_intervals;i++) {
+ _bind_sqlite_dimension_time_params(ctx,conn->prepared_statements[0],conn->handle,dim_value,tileset,grid,extent,intervals[i*2],intervals[i*2+1]);
+ if(GC_HAS_ERROR(ctx)) {
+ _sqlite_dimension_release_conn(ctx, pc);
+ return NULL;
+ }
- _bind_sqlite_timedimension_params(ctx,conn->prepared_statements[0],conn->handle,tileset,grid,extent,start,end);
- if(GC_HAS_ERROR(ctx)) {
- _sqlite_dimension_release_conn(ctx, pc);
- return NULL;
+ time_ids = apr_array_make(ctx->pool,0,sizeof(char*));
+ do {
+ ret = sqlite3_step(conn->prepared_statements[0]);
+ if (ret != SQLITE_DONE && ret != SQLITE_ROW && ret != SQLITE_BUSY && ret != SQLITE_LOCKED) {
+ ctx->set_error(ctx, 500, "sqlite backend failed on dimension_time query : %s (%d)", sqlite3_errmsg(conn->handle), ret);
+ _sqlite_dimension_release_conn(ctx, pc);
+ return NULL;
+ }
+ if(ret == SQLITE_ROW) {
+ const char* time_id = (const char*) sqlite3_column_text(conn->prepared_statements[0], 0);
+ APR_ARRAY_PUSH(time_ids,char*) = apr_pstrdup(ctx->pool,time_id);
+ }
+ } while (ret == SQLITE_ROW || ret == SQLITE_BUSY || ret == SQLITE_LOCKED);
+ sqlite3_reset(conn->prepared_statements[0]);
}
-
- time_ids = apr_array_make(ctx->pool,0,sizeof(char*));
- do {
- ret = sqlite3_step(conn->prepared_statements[0]);
- if (ret != SQLITE_DONE && ret != SQLITE_ROW && ret != SQLITE_BUSY && ret != SQLITE_LOCKED) {
- ctx->set_error(ctx, 500, "sqlite backend failed on timedimension query : %s (%d)", sqlite3_errmsg(conn->handle), ret);
- _sqlite_dimension_release_conn(ctx, pc);
- return NULL;
- }
- if(ret == SQLITE_ROW) {
- const char* time_id = (const char*) sqlite3_column_text(conn->prepared_statements[0], 0);
- APR_ARRAY_PUSH(time_ids,char*) = apr_pstrdup(ctx->pool,time_id);
- }
- } while (ret == SQLITE_ROW || ret == SQLITE_BUSY || ret == SQLITE_LOCKED);
- sqlite3_reset(conn->prepared_statements[0]);
_sqlite_dimension_release_conn(ctx, pc);
return time_ids;
}
-apr_array_header_t *_mapcache_timedimension_sqlite_get_all_entries(mapcache_context *ctx, mapcache_timedimension *dim,
- mapcache_tileset *tileset) {
- return _mapcache_timedimension_sqlite_get_entries(ctx,dim,tileset,NULL,NULL,0,INT_MAX);
+
+apr_array_header_t* _mapcache_dimension_time_get_all_entries(mapcache_context *ctx, mapcache_dimension *dim,
+ mapcache_tileset *tileset, mapcache_extent *extent, mapcache_grid *grid) {
+ mapcache_dimension_time *tdim = (mapcache_dimension_time*)dim;
+ time_t all[2] = {0,INT_MAX};
+ return _mapcache_dimension_time_get_entries(ctx,tdim,NULL,tileset,extent,grid,all,1);
}
#endif
@@ -657,22 +691,23 @@ typedef enum {
#ifdef USE_SQLITE
-void _mapcache_timedimension_sqlite_parse_xml(mapcache_context *ctx, mapcache_timedimension *dim, ezxml_t node) {
- mapcache_timedimension_sqlite *sdim = (mapcache_timedimension_sqlite*)dim;
+void _mapcache_dimension_time_parse_xml(mapcache_context *ctx, mapcache_dimension *dim, ezxml_t node) {
+ mapcache_dimension_sqlite *sdim = (mapcache_dimension_sqlite*)dim;
+ mapcache_dimension *pdim = (mapcache_dimension*)dim;
ezxml_t child;
child = ezxml_child(node,"dbfile");
if(child && child->txt && *child->txt) {
sdim->dbfile = apr_pstrdup(ctx->pool,child->txt);
} else {
- ctx->set_error(ctx,400,"no <dbfile> entry for <timedimension> %s",dim->key);
+ ctx->set_error(ctx,400,"no <dbfile> entry for <dimension_time> %s",pdim->name);
return;
}
child = ezxml_child(node,"query");
if(child && child->txt && *child->txt) {
- sdim->query = apr_pstrdup(ctx->pool,child->txt);
+ sdim->get_values_for_entry_query = apr_pstrdup(ctx->pool,child->txt);
} else {
- ctx->set_error(ctx,400,"no <query> entry for <timedimension> %s",dim->key);
+ ctx->set_error(ctx,400,"no <query> entry for <dimension_time> %s",pdim->name);
return;
}
}
@@ -702,97 +737,122 @@ char *mapcache_ogc_strptime(const char *value, struct tm *ts, mapcache_time_inte
return NULL;
}
-apr_array_header_t* mapcache_timedimension_get_entries_for_value(mapcache_context *ctx, mapcache_timedimension *timedimension,
- mapcache_tileset *tileset, mapcache_grid *grid, mapcache_extent *extent, const char *value) {
+#ifdef USE_SQLITE
+apr_array_header_t* _mapcache_dimension_time_get_entries_for_value(mapcache_context *ctx, mapcache_dimension *dimension, const char *value,
+ mapcache_tileset *tileset, mapcache_extent *extent, mapcache_grid *grid) {
+
/* look if supplied value is a predefined key */
/* split multiple values, loop */
/* extract start and end values */
struct tm tm_start,tm_end;
- time_t start,end;
+ time_t *intervals;
mapcache_time_interval_t tis,tie;
- char *valueptr = (char*)value;
- valueptr = mapcache_ogc_strptime(value,&tm_start,&tis);
- if(!valueptr) {
- ctx->set_error(ctx,400,"failed to parse time %s",value);
- return NULL;
- }
-
- if(*valueptr == '/' || (*valueptr == '-' && *(valueptr+1) == '-')) {
- /* we have a second (end) time */
- if (*valueptr == '/') {
- valueptr++;
- }
- else {
- valueptr += 2;
- }
- valueptr = mapcache_ogc_strptime(valueptr,&tm_end,&tie);
+ char *valueptr = apr_pstrdup(ctx->pool,value);
+ char *last,*key;
+ int count=0;
+ mapcache_dimension_time *dimension_time = (mapcache_dimension_time*)dimension;
+
+ /*count how many time entries were supplied*/
+ for(; *value; value++) if(*value == ',') count++;
+
+ intervals = apr_pcalloc(ctx->pool,2*count*sizeof(time_t));
+ count = 0;
+
+
+ /* Split the input on '&' */
+ for (key = apr_strtok(valueptr, ",", &last); key != NULL;
+ key = apr_strtok(NULL, ",", &last)) {
+ valueptr = mapcache_ogc_strptime(key,&tm_start,&tis);
if(!valueptr) {
- ctx->set_error(ctx,400,"failed to parse end time in %s",value);
+ ctx->set_error(ctx,400,"failed to parse time %s",value);
return NULL;
}
- } else if(*valueptr == 0) {
- tie = tis;
- tm_end = tm_start;
- } else {
- ctx->set_error(ctx,400,"failed (2) to parse time %s",value);
- return NULL;
- }
- end = timegm(&tm_end);
- start = timegm(&tm_start);
- if(difftime(start,end) == 0) {
- switch(tie) {
- case MAPCACHE_TINTERVAL_SECOND:
- tm_end.tm_sec += 1;
- break;
- case MAPCACHE_TINTERVAL_MINUTE:
- tm_end.tm_min += 1;
- break;
- case MAPCACHE_TINTERVAL_HOUR:
- tm_end.tm_hour += 1;
- break;
- case MAPCACHE_TINTERVAL_DAY:
- tm_end.tm_mday += 1;
- break;
- case MAPCACHE_TINTERVAL_MONTH:
- tm_end.tm_mon += 1;
- break;
- case MAPCACHE_TINTERVAL_YEAR:
- tm_end.tm_year += 1;
- break;
+
+ if(*valueptr == '/' || (*valueptr == '-' && *(valueptr+1) == '-')) {
+ /* we have a second (end) time */
+ if (*valueptr == '/') {
+ valueptr++;
+ }
+ else {
+ valueptr += 2;
+ }
+ valueptr = mapcache_ogc_strptime(valueptr,&tm_end,&tie);
+ if(!valueptr) {
+ ctx->set_error(ctx,400,"failed to parse end time in %s",value);
+ return NULL;
+ }
+ } else if(*valueptr == 0) {
+ tie = tis;
+ tm_end = tm_start;
+ } else {
+ ctx->set_error(ctx,400,"failed (2) to parse time %s",value);
+ return NULL;
+ }
+ intervals[count*2+1] = timegm(&tm_end);
+ intervals[count*2] = timegm(&tm_start);
+ if(difftime(intervals[count*2],intervals[count*2+1]) == 0) {
+ switch(tie) {
+ case MAPCACHE_TINTERVAL_SECOND:
+ tm_end.tm_sec += 1;
+ break;
+ case MAPCACHE_TINTERVAL_MINUTE:
+ tm_end.tm_min += 1;
+ break;
+ case MAPCACHE_TINTERVAL_HOUR:
+ tm_end.tm_hour += 1;
+ break;
+ case MAPCACHE_TINTERVAL_DAY:
+ tm_end.tm_mday += 1;
+ break;
+ case MAPCACHE_TINTERVAL_MONTH:
+ tm_end.tm_mon += 1;
+ break;
+ case MAPCACHE_TINTERVAL_YEAR:
+ tm_end.tm_year += 1;
+ break;
+ }
+ intervals[count*2+1] = timegm(&tm_end);
}
- end = timegm(&tm_end);
+ count++;
}
-
- return timedimension->get_entries_for_interval(ctx,timedimension,tileset,grid,extent,start,end);
+ return _mapcache_dimension_time_get_entries(ctx,dimension_time,value,tileset,extent,grid,intervals,count);
/* end loop */
}
+#endif
-mapcache_dimension* mapcache_dimension_sqlite_create(apr_pool_t *pool)
+mapcache_dimension* mapcache_dimension_sqlite_create(mapcache_context *ctx, apr_pool_t *pool)
{
#ifdef USE_SQLITE
mapcache_dimension_sqlite *dimension = apr_pcalloc(pool, sizeof(mapcache_dimension_sqlite));
dimension->dimension.type = MAPCACHE_DIMENSION_SQLITE;
dimension->dbfile = NULL;
- dimension->dimension.validate = _mapcache_dimension_sqlite_validate;
+ dimension->dimension.get_entries_for_value = _mapcache_dimension_sqlite_get_entries_for_value;
dimension->dimension.configuration_parse_xml = _mapcache_dimension_sqlite_parse_xml;
- dimension->dimension.print_ogc_formatted_values = _mapcache_dimension_sqlite_print;
+ dimension->dimension.get_all_entries = _mapcache_dimension_sqlite_get_all_entries;
+ dimension->dimension.get_all_ogc_formatted_entries = _mapcache_dimension_sqlite_get_all_entries;
return (mapcache_dimension*)dimension;
#else
+ ctx->set_error(ctx,400,"Sqlite dimension support requires SQLITE support to be built in");
return NULL;
#endif
}
+mapcache_dimension* mapcache_dimension_time_create(mapcache_context *ctx, apr_pool_t *pool) {
#ifdef USE_SQLITE
-mapcache_timedimension* mapcache_timedimension_sqlite_create(apr_pool_t *pool) {
- mapcache_timedimension_sqlite *dim = apr_pcalloc(pool, sizeof(mapcache_timedimension_sqlite));
- dim->timedimension.assembly_type = MAPCACHE_TIMEDIMENSION_ASSEMBLY_STACK;
- dim->timedimension.get_entries_for_interval = _mapcache_timedimension_sqlite_get_entries;
- dim->timedimension.get_all_entries = _mapcache_timedimension_sqlite_get_all_entries;
- dim->timedimension.configuration_parse_xml = _mapcache_timedimension_sqlite_parse_xml;
- return (mapcache_timedimension*)dim;
-}
+ mapcache_dimension_time *dim = apr_pcalloc(pool, sizeof(mapcache_dimension_time));
+ mapcache_dimension *pdim = (mapcache_dimension*)dim;
+ pdim->get_entries_for_value = _mapcache_dimension_time_get_entries_for_value;
+ pdim->get_all_entries = _mapcache_dimension_time_get_all_entries;
+ pdim->get_all_ogc_formatted_entries = _mapcache_dimension_time_get_all_entries;
+ pdim->configuration_parse_xml = _mapcache_dimension_time_parse_xml;
+ return pdim;
+#else
+ ctx->set_error(ctx,400,"TIME dimension support requires SQLITE support to be built in");
+ return NULL;
#endif
+}
+
/* vim: ts=2 sts=2 et sw=2
*/
diff --git a/lib/grid.c b/lib/grid.c
index d3e92a3..befa3f3 100644
--- a/lib/grid.c
+++ b/lib/grid.c
@@ -48,7 +48,7 @@ mapcache_grid* mapcache_grid_create(apr_pool_t *pool)
* \brief compute the extent of a given tile in the grid given its x, y, and z.
* \returns \extent the tile's extent
*/
-void mapcache_grid_get_extent(mapcache_context *ctx, mapcache_grid *grid,
+void mapcache_grid_get_tile_extent(mapcache_context *ctx, mapcache_grid *grid,
int x, int y, int z, mapcache_extent *bbox)
{
double res = grid->levels[z]->resolution;
@@ -71,6 +71,65 @@ void mapcache_grid_get_extent(mapcache_context *ctx, mapcache_grid *grid,
}
}
+void mapcache_grid_get_metatile_extent(mapcache_context *ctx, mapcache_tile *tile, mapcache_extent *extent) {
+ mapcache_grid *grid = tile->grid_link->grid;
+ double res = grid->levels[tile->z]->resolution;
+ double gbuffer,gwidth,gheight,fullgwidth,fullgheight;
+ mapcache_tileset *tileset = tile->tileset;
+ int mtx,mty,blx,bly,mtsx,mtsy;
+ mtx = tile->x / tileset->metasize_x;
+ if(tile->x < 0)
+ mtx --;
+ mty = tile->y / tileset->metasize_y;
+ if(tile->y < 0)
+ mty --;
+ blx = mtx * tileset->metasize_x;
+ bly = mty * tileset->metasize_y;
+
+ /* adjust the size of the the metatile so it does not extend past the grid limits.
+ * If we don't do this, we end up with cut labels on the edges of the tile grid
+ */
+ if(blx+tileset->metasize_x-1 >= grid->levels[tile->z]->maxx) {
+ mtsx = grid->levels[tile->z]->maxx - blx;
+ } else {
+ mtsx = tileset->metasize_x;
+ }
+ if(bly+tileset->metasize_y-1 >= grid->levels[tile->z]->maxy) {
+ mtsy = grid->levels[tile->z]->maxy - bly;
+ } else {
+ mtsy = tileset->metasize_y;
+ }
+
+ /* buffer in geographical units */
+ gbuffer = res * tileset->metabuffer;
+
+ /* adjusted metatile size in geographical units */
+ gwidth = res * mtsx * grid->tile_sx;
+ gheight = res * mtsy * grid->tile_sy;
+
+ /* configured metatile size in geographical units */
+ fullgwidth = res * tileset->metasize_x * grid->tile_sx;
+ fullgheight = res * tileset->metasize_y * grid->tile_sy;
+
+ switch(grid->origin) {
+ case MAPCACHE_GRID_ORIGIN_BOTTOM_LEFT:
+ extent->minx = grid->extent.minx + mtx * fullgwidth - gbuffer;
+ extent->miny = grid->extent.miny + mty * fullgheight - gbuffer;
+ extent->maxx = extent->minx + gwidth + 2 * gbuffer;
+ extent->maxy = extent->miny + gheight + 2 * gbuffer;
+ break;
+ case MAPCACHE_GRID_ORIGIN_TOP_LEFT:
+ extent->minx = grid->extent.minx + mtx * fullgwidth - gbuffer;
+ extent->maxy = grid->extent.maxy - mty * fullgheight + gbuffer;
+ extent->maxx = extent->minx + gwidth + 2 * gbuffer;
+ extent->miny = extent->maxy - gheight - 2 * gbuffer;
+ break;
+ case MAPCACHE_GRID_ORIGIN_BOTTOM_RIGHT:
+ case MAPCACHE_GRID_ORIGIN_TOP_RIGHT:
+ ctx->set_error(ctx,500,"origin not implemented");
+ }
+}
+
const char* mapcache_grid_get_crs(mapcache_context *ctx, mapcache_grid *grid)
{
char *epsgnum;
diff --git a/lib/http.c b/lib/http.c
index e4aad04..4c3512f 100644
--- a/lib/http.c
+++ b/lib/http.c
@@ -156,12 +156,14 @@ void mapcache_http_do_request(mapcache_context *ctx, mapcache_http *req, mapcach
if(req->post_body && req->post_len>0) {
curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, req->post_body);
}
+
+ if(!http_code)
+ curl_easy_setopt(curl_handle, CURLOPT_FAILONERROR, 1);
+
/* get it! */
ret = curl_easy_perform(curl_handle);
if(http_code)
curl_easy_getinfo (curl_handle, CURLINFO_RESPONSE_CODE, http_code);
- else
- curl_easy_setopt(curl_handle, CURLOPT_FAILONERROR, 1);
if(ret != CURLE_OK) {
ctx->set_error(ctx, 502, "curl failed to request url %s : %s", req->url, error_msg);
diff --git a/lib/image.c b/lib/image.c
index 4dd11d1..f8287ea 100644
--- a/lib/image.c
+++ b/lib/image.c
@@ -56,7 +56,7 @@ mapcache_image* mapcache_image_create_with_data(mapcache_context *ctx, int width
return img;
}
-int mapcache_image_has_alpha(mapcache_image *img)
+int mapcache_image_has_alpha(mapcache_image *img, unsigned int cutoff)
{
size_t i,j;
if(img->has_alpha == MC_ALPHA_UNKNOWN) {
@@ -64,7 +64,7 @@ int mapcache_image_has_alpha(mapcache_image *img)
for(i=0; i<img->h; i++) {
ptr = rptr;
for(j=0; j<img->w; j++) {
- if(ptr[3]<(unsigned char)255) {
+ if(ptr[3]<(unsigned char)cutoff) {
img->has_alpha = MC_ALPHA_YES;
return 1;
}
@@ -85,10 +85,11 @@ int mapcache_image_has_alpha(mapcache_image *img)
void mapcache_image_merge(mapcache_context *ctx, mapcache_image *base, mapcache_image *overlay)
{
int starti,startj;
+#ifdef USE_PIXMAN
pixman_image_t *si;
pixman_image_t *bi;
pixman_transform_t transform;
-#ifndef USE_PIXMAN
+#else
int i,j;
unsigned char *browptr, *orowptr, *bptr, *optr;
#endif
@@ -97,6 +98,7 @@ void mapcache_image_merge(mapcache_context *ctx, mapcache_image *base, mapcache_
ctx->set_error(ctx, 500, "attempting to merge an larger image onto another");
return;
}
+
starti = (base->h - overlay->h)/2;
startj = (base->w - overlay->w)/2;
#ifdef USE_PIXMAN
@@ -104,12 +106,14 @@ void mapcache_image_merge(mapcache_context *ctx, mapcache_image *base, mapcache_
(uint32_t*)overlay->data,overlay->stride);
bi = pixman_image_create_bits(PIXMAN_a8r8g8b8,base->w,base->h,
(uint32_t*)base->data,base->stride);
- pixman_transform_init_translate(&transform,
- pixman_int_to_fixed(-startj),
- pixman_int_to_fixed(-starti));
pixman_image_set_filter(si,PIXMAN_FILTER_NEAREST, NULL, 0);
- pixman_image_set_transform (si, &transform);
- pixman_image_composite (PIXMAN_OP_OVER, si, si, bi,
+ if(starti || startj) {
+ pixman_transform_init_translate(&transform,
+ pixman_int_to_fixed(-startj),
+ pixman_int_to_fixed(-starti));
+ pixman_image_set_transform (si, &transform);
+ }
+ pixman_image_composite (PIXMAN_OP_OVER, si, NULL, bi,
0, 0, 0, 0, 0, 0, base->w,base->h);
pixman_image_unref(si);
pixman_image_unref(bi);
@@ -291,6 +295,12 @@ void mapcache_image_metatile_split(mapcache_context *ctx, mapcache_metatile *mt)
ctx->set_error(ctx, 500, "failed to load image data from metatile");
return;
}
+ if(metatile->w != mt->map.width ||
+ metatile->h != mt->map.height) {
+ ctx->set_error(ctx, 500, "image size does not correspond to metatile size");
+ return;
+ }
+
for(i=0; i<mt->metasize_x; i++) {
for(j=0; j<mt->metasize_y; j++) {
tileimg = mapcache_image_create(ctx);
@@ -314,6 +324,9 @@ void mapcache_image_metatile_split(mapcache_context *ctx, mapcache_metatile *mt)
sx = mt->map.tileset->metabuffer + i * tileimg->w;
sy = mt->map.height - (mt->map.tileset->metabuffer + (j+1) * tileimg->h);
break;
+ default:
+ ctx->set_error(ctx,500,"BUG: unknown grid origin");
+ return;
}
tileimg->data = &(metatile->data[sy*metatile->stride + 4 * sx]);
if(mt->map.tileset->watermark) {
diff --git a/lib/imageio_jpeg.c b/lib/imageio_jpeg.c
index 6e585ea..cdf4c97 100644
--- a/lib/imageio_jpeg.c
+++ b/lib/imageio_jpeg.c
@@ -188,6 +188,18 @@ mapcache_buffer* _mapcache_imageio_jpeg_encode(mapcache_context *ctx, mapcache_i
default:
jpeg_set_colorspace(&cinfo, JCS_YCbCr);
}
+ switch(((mapcache_image_format_jpeg*)format)->optimize) {
+ case MAPCACHE_OPTIMIZE_NO:
+ cinfo.optimize_coding = FALSE;
+ break;
+ case MAPCACHE_OPTIMIZE_ARITHMETIC:
+ cinfo.optimize_coding = FALSE;
+ cinfo.arith_code = TRUE;
+ break;
+ case MAPCACHE_OPTIMIZE_YES:
+ default:
+ cinfo.optimize_coding = TRUE;
+ }
jpeg_start_compress(&cinfo, TRUE);
rowdata = (JSAMPLE*)malloc(img->w*cinfo.input_components*sizeof(JSAMPLE));
@@ -317,7 +329,7 @@ static mapcache_buffer* _mapcache_imageio_jpg_create_empty(mapcache_context *ctx
}
mapcache_image_format* mapcache_imageio_create_jpeg_format(apr_pool_t *pool, char *name, int quality,
- mapcache_photometric photometric)
+ mapcache_photometric photometric, mapcache_optimization optimize)
{
mapcache_image_format_jpeg *format = apr_pcalloc(pool, sizeof(mapcache_image_format_jpeg));
format->format.name = name;
@@ -327,6 +339,7 @@ mapcache_image_format* mapcache_imageio_create_jpeg_format(apr_pool_t *pool, cha
format->format.create_empty_image = _mapcache_imageio_jpg_create_empty;
format->format.write = _mapcache_imageio_jpeg_encode;
format->quality = quality;
+ format->optimize = optimize;
format->photometric = photometric;
format->format.type = GC_JPEG;
return (mapcache_image_format*)format;
diff --git a/lib/imageio_mixed.c b/lib/imageio_mixed.c
index 30e2dce..57ec3c2 100644
--- a/lib/imageio_mixed.c
+++ b/lib/imageio_mixed.c
@@ -46,7 +46,7 @@ static mapcache_buffer* _mapcache_imageio_mixed_encode( mapcache_context *ctx,
mapcache_image *image, mapcache_image_format *format)
{
mapcache_image_format_mixed *f = (mapcache_image_format_mixed*)format;
- if(mapcache_image_has_alpha(image)) {
+ if(mapcache_image_has_alpha(image,f->alpha_cutoff)) {
return f->transparent->write(ctx,image,f->transparent);
} else {
return f->opaque->write(ctx,image,f->opaque);
@@ -54,12 +54,14 @@ static mapcache_buffer* _mapcache_imageio_mixed_encode( mapcache_context *ctx,
}
mapcache_image_format* mapcache_imageio_create_mixed_format(apr_pool_t *pool,
- char *name, mapcache_image_format *transparent, mapcache_image_format *opaque)
+ char *name, mapcache_image_format *transparent, mapcache_image_format *opaque,
+ unsigned int alpha_cutoff)
{
mapcache_image_format_mixed *format = apr_pcalloc(pool, sizeof(mapcache_image_format_mixed));
format->format.name = name;
format->transparent = transparent;
format->opaque = opaque;
+ format->alpha_cutoff = alpha_cutoff;
format->format.extension = apr_pstrdup(pool,"xxx");
format->format.mime_type = NULL;
format->format.write = _mapcache_imageio_mixed_encode;
diff --git a/lib/imageio_png.c b/lib/imageio_png.c
index fbd32a5..406eda7 100644
--- a/lib/imageio_png.c
+++ b/lib/imageio_png.c
@@ -536,7 +536,7 @@ mapcache_buffer* _mapcache_imageio_png_encode(mapcache_context *ctx, mapcache_im
png_set_write_fn(png_ptr, buffer, _mapcache_imageio_png_write_func, _mapcache_imageio_png_flush_func);
- if(mapcache_image_has_alpha(img))
+ if(mapcache_image_has_alpha(img,255))
color_type = PNG_COLOR_TYPE_RGB_ALPHA;
else
color_type = PNG_COLOR_TYPE_RGB;
diff --git a/lib/lock.c b/lib/lock.c
index 3275d50..be918e2 100644
--- a/lib/lock.c
+++ b/lib/lock.c
@@ -35,6 +35,24 @@
#include <unistd.h>
#endif
+typedef struct {
+ mapcache_locker locker;
+
+ /**
+ * directory where lock files will be placed.
+ * Must be readable and writable by the apache user.
+ * Must be placed on a network mounted shared directory if multiple mapcache instances
+ * need to be synchronized
+ */
+ const char *dir;
+} mapcache_locker_disk;
+
+typedef struct {
+ mapcache_locker locker;
+ apr_array_header_t *lockers;
+} mapcache_locker_fallback;
+
+
#define MAPCACHE_LOCKFILE_PREFIX "_gc_lock"
char* lock_filename_for_resource(mapcache_context *ctx, mapcache_locker_disk *ldisk, const char *resource)
@@ -66,12 +84,12 @@ int mapcache_lock_or_wait_for_resource(mapcache_context *ctx, mapcache_locker *l
while(rv != MAPCACHE_LOCK_NOENT) {
unsigned int waited = apr_time_as_msec(apr_time_now()-start_wait);
if(waited > locker->timeout*1000) {
- mapcache_unlock_resource(ctx,locker,resource, *lock);
+ mapcache_unlock_resource(ctx,locker,*lock);
ctx->log(ctx,MAPCACHE_ERROR,"deleting a possibly stale lock after waiting on it for %g seconds",waited/1000.0);
return MAPCACHE_FALSE;
}
apr_sleep(locker->retry_interval * 1000000);
- rv = locker->ping_lock(ctx,locker,resource, *lock);
+ rv = locker->ping_lock(ctx,locker, *lock);
}
return MAPCACHE_FALSE;
}
@@ -86,9 +104,10 @@ mapcache_lock_result mapcache_locker_disk_aquire_lock(mapcache_context *ctx, map
assert(self->type == MAPCACHE_LOCKER_DISK);
ldisk = (mapcache_locker_disk*)self;
- *lock = NULL; /*unused*/
lockname = lock_filename_for_resource(ctx,ldisk,resource);
+ *lock = lockname;
+
/* create the lockfile */
rv = apr_file_open(&lockfile,lockname,APR_WRITE|APR_CREATE|APR_EXCL|APR_XTHREAD,APR_OS_DEFAULT,ctx->pool);
@@ -114,12 +133,11 @@ mapcache_lock_result mapcache_locker_disk_aquire_lock(mapcache_context *ctx, map
}
}
-mapcache_lock_result mapcache_locker_disk_ping_lock(mapcache_context *ctx, mapcache_locker *self, char *resource, void *lock) {
+mapcache_lock_result mapcache_locker_disk_ping_lock(mapcache_context *ctx, mapcache_locker *self, void *lock) {
apr_finfo_t info;
apr_status_t rv;
char *lockname;
- mapcache_locker_disk *ldisk = (mapcache_locker_disk*)self;
- lockname = lock_filename_for_resource(ctx,ldisk,resource);
+ lockname = (char*)lock;
rv = apr_stat(&info,lockname,0,ctx->pool);
if(APR_STATUS_IS_ENOENT(rv)) {
return MAPCACHE_LOCK_NOENT;
@@ -128,15 +146,14 @@ mapcache_lock_result mapcache_locker_disk_ping_lock(mapcache_context *ctx, mapca
}
}
-void mapcache_locker_disk_release_lock(mapcache_context *ctx, mapcache_locker *self, char *resource, void *lock)
+void mapcache_locker_disk_release_lock(mapcache_context *ctx, mapcache_locker *self, void *lock)
{
- mapcache_locker_disk *ld = (mapcache_locker_disk*)self;
- char *lockname = lock_filename_for_resource(ctx,ld,resource);
+ char *lockname = (char*)lock;
apr_file_remove(lockname,ctx->pool);
}
-void mapcache_unlock_resource(mapcache_context *ctx, mapcache_locker *locker, char *resource, void *lock) {
- locker->release_lock(ctx, locker, resource, lock);
+void mapcache_unlock_resource(mapcache_context *ctx, mapcache_locker *locker, void *lock) {
+ locker->release_lock(ctx, locker, lock);
}
void mapcache_locker_disk_parse_xml(mapcache_context *ctx, mapcache_locker *self, ezxml_t doc) {
@@ -165,14 +182,14 @@ struct mapcache_locker_fallback_lock {
void *lock; /*the opaque lock returned by the locker*/
};
-void mapcache_locker_fallback_release_lock(mapcache_context *ctx, mapcache_locker *self, char *resource, void *lock) {
+void mapcache_locker_fallback_release_lock(mapcache_context *ctx, mapcache_locker *self, void *lock) {
struct mapcache_locker_fallback_lock *flock = lock;
- flock->locker->release_lock(ctx,flock->locker,resource,flock->lock);
+ flock->locker->release_lock(ctx,flock->locker,flock->lock);
}
-mapcache_lock_result mapcache_locker_fallback_ping_lock(mapcache_context *ctx, mapcache_locker *self, char *resource, void *lock) {
+mapcache_lock_result mapcache_locker_fallback_ping_lock(mapcache_context *ctx, mapcache_locker *self, void *lock) {
struct mapcache_locker_fallback_lock *flock = lock;
- return flock->locker->ping_lock(ctx,flock->locker,resource,flock->lock);
+ return flock->locker->ping_lock(ctx,flock->locker,flock->lock);
}
mapcache_lock_result mapcache_locker_fallback_aquire_lock(mapcache_context *ctx, mapcache_locker *self, char *resource, void **lock) {
@@ -215,6 +232,20 @@ void mapcache_locker_fallback_parse_xml(mapcache_context *ctx, mapcache_locker *
}
#ifdef USE_MEMCACHE
+
+#include <apr_memcache.h>
+
+typedef struct {
+ char *host;
+ int port;
+} mapcache_locker_memcache_server;
+
+typedef struct {
+ mapcache_locker locker;
+ int nservers;
+ mapcache_locker_memcache_server *servers;
+} mapcache_locker_memcache;
+
void mapcache_locker_memcache_parse_xml(mapcache_context *ctx, mapcache_locker *self, ezxml_t doc) {
mapcache_locker_memcache *lm = (mapcache_locker_memcache*)self;
ezxml_t node,server_node;
@@ -288,16 +319,19 @@ apr_memcache_t* create_memcache(mapcache_context *ctx, mapcache_locker_memcache
return memcache;
}
-mapcache_lock_result mapcache_locker_memcache_ping_lock(mapcache_context *ctx, mapcache_locker *self, char *resource, void *lock) {
+typedef struct {
+ apr_memcache_t *memcache;
+ char *lockname;
+} mapcache_lock_memcache;
+
+mapcache_lock_result mapcache_locker_memcache_ping_lock(mapcache_context *ctx, mapcache_locker *self, void *lock) {
apr_status_t rv;
char *one;
size_t ione;
- mapcache_locker_memcache *lm = (mapcache_locker_memcache*)self;
- char *key = memcache_key_for_resource(ctx, lm, resource);
- apr_memcache_t *memcache = (apr_memcache_t*)lock;
- if(!memcache)
+ mapcache_lock_memcache *mlock = (mapcache_lock_memcache*)lock;
+ if(!mlock || !mlock->lockname || !mlock->memcache)
return MAPCACHE_LOCK_NOENT;
- rv = apr_memcache_getp(memcache,ctx->pool,key,&one,&ione,NULL);
+ rv = apr_memcache_getp(mlock->memcache,ctx->pool,mlock->lockname,&one,&ione,NULL);
if(rv == APR_SUCCESS)
return MAPCACHE_LOCK_LOCKED;
else
@@ -309,13 +343,14 @@ mapcache_lock_result mapcache_locker_memcache_aquire_lock(mapcache_context *ctx,
apr_status_t rv;
mapcache_locker_memcache *lm = (mapcache_locker_memcache*)self;
char errmsg[120];
- char *key = memcache_key_for_resource(ctx, lm, resource);
- apr_memcache_t *memcache = create_memcache(ctx,lm);
+ mapcache_lock_memcache *mlock = apr_pcalloc(ctx->pool, sizeof(mapcache_lock_memcache));
+ mlock->lockname = memcache_key_for_resource(ctx, lm, resource);
+ mlock->memcache = create_memcache(ctx,lm);
if(GC_HAS_ERROR(ctx)) {
return MAPCACHE_LOCK_NOENT;
}
- *lock = memcache;
- rv = apr_memcache_add(memcache,key,"1",1,self->timeout,0);
+ *lock = mlock;
+ rv = apr_memcache_add(mlock->memcache,mlock->lockname,"1",1,self->timeout,0);
if( rv == APR_SUCCESS) {
return MAPCACHE_LOCK_AQUIRED;
} else if ( rv == APR_EEXIST ) {
@@ -326,20 +361,18 @@ mapcache_lock_result mapcache_locker_memcache_aquire_lock(mapcache_context *ctx,
}
}
-void mapcache_locker_memcache_release_lock(mapcache_context *ctx, mapcache_locker *self, char *resource, void *lock) {
+void mapcache_locker_memcache_release_lock(mapcache_context *ctx, mapcache_locker *self, void *lock) {
apr_status_t rv;
- mapcache_locker_memcache *lm = (mapcache_locker_memcache*)self;
+ mapcache_lock_memcache *mlock = (mapcache_lock_memcache*)lock;
char errmsg[120];
- char *key = memcache_key_for_resource(ctx, lm, resource);
- apr_memcache_t *memcache = (apr_memcache_t*)lock;
- if(!memcache) {
+ if(!mlock || !mlock->memcache || !mlock->lockname) {
/*error*/
return;
}
- rv = apr_memcache_delete(memcache,key,0);
+ rv = apr_memcache_delete(mlock->memcache,mlock->lockname,0);
if(rv != APR_SUCCESS && rv!= APR_NOTFOUND) {
- ctx->set_error(ctx,500,"memcache: failed to delete key %s: %s", key, apr_strerror(rv,errmsg,120));
+ ctx->set_error(ctx,500,"memcache: failed to delete key %s: %s", mlock->lockname, apr_strerror(rv,errmsg,120));
}
}
@@ -370,6 +403,36 @@ mapcache_locker* mapcache_locker_fallback_create(mapcache_context *ctx) {
return l;
}
+void mapcache_config_parse_locker_old(mapcache_context *ctx, ezxml_t doc, mapcache_cfg *config) {
+ /* backwards compatibility */
+ int micro_retry;
+ ezxml_t node;
+ mapcache_locker_disk *ldisk;
+ config->locker = mapcache_locker_disk_create(ctx);
+ ldisk = (mapcache_locker_disk*)config->locker;
+ if((node = ezxml_child(doc,"lock_dir")) != NULL) {
+ ldisk->dir = apr_pstrdup(ctx->pool, node->txt);
+ } else {
+ ldisk->dir = apr_pstrdup(ctx->pool,"/tmp");
+ }
+
+ if((node = ezxml_child(doc,"lock_retry")) != NULL) {
+ char *endptr;
+ micro_retry = strtol(node->txt,&endptr,10);
+ if(*endptr != 0 || micro_retry <= 0) {
+ ctx->set_error(ctx, 400, "failed to parse lock_retry microseconds \"%s\". Expecting a positive integer",
+ node->txt);
+ return;
+ }
+ } else {
+ /* default retry interval is 1/100th of a second, i.e. 10000 microseconds */
+ micro_retry = 10000;
+ }
+ config->locker->retry_interval = micro_retry / 1000000.0;
+ config->locker->timeout=120;
+}
+
+
void mapcache_config_parse_locker(mapcache_context *ctx, ezxml_t node, mapcache_locker **locker) {
ezxml_t cur_node;
const char *ltype = ezxml_attr(node, "type");
diff --git a/lib/service_demo.c b/lib/service_demo.c
index c6512a5..94114ad 100644
--- a/lib/service_demo.c
+++ b/lib/service_demo.c
@@ -29,12 +29,11 @@
#include <ctype.h>
#include "mapcache.h"
+#include "mapcache_services.h"
#include <apr_strings.h>
#include <math.h>
#include <apr_tables.h>
-/** \addtogroup services */
-/** @{ */
static char *demo_head =
"<!DOCTYPE html>\n"
@@ -282,7 +281,7 @@ static char *demo_head_gmaps =
" return null;\n"
" }\n"
"\n"
- " // repeat accross x-axis\n"
+ " // repeat across x-axis\n"
" if (x < 0 || x >= tileRange) {\n"
" x = (x % tileRange + tileRange) % tileRange;\n"
" }\n"
@@ -507,7 +506,7 @@ void _create_demo_wms(mapcache_context *ctx, mapcache_request_get_capabilities *
resolutions = apr_psprintf(ctx->pool,"%s,%.20f",resolutions,grid->levels[i]->resolution);
}
- if(!tileset->timedimension) {
+ if(!tileset->dimensions) {
ol_layer_name = apr_psprintf(ctx->pool, "%s_%s", tileset->name, grid->name);
/* normalize name to something that is a valid variable name */
for(i=0; i<strlen(ol_layer_name); i++)
@@ -549,44 +548,26 @@ void _create_demo_wms(mapcache_context *ctx, mapcache_request_get_capabilities *
caps = apr_psprintf(ctx->pool,"%s%s",caps,ol_layer);
}
} else {
- int id;
- apr_array_header_t *timedimvals = tileset->timedimension->get_all_entries(
- ctx,tileset->timedimension,tileset);
- for(id=0;id<timedimvals->nelts;id++) {
- char *idval;
- char *dimparam_wms;
- char *dimparam_singletile;
- if(id>1) break;
- idval = APR_ARRAY_IDX(timedimvals,id,char*);
- dimparam_wms = " %s_wms_layer.mergeNewParams({%s:\"%s\"});\n";
- dimparam_singletile = " %s_slayer.mergeNewParams({%s:\"%s\"});\n";
- ol_layer_name = apr_psprintf(ctx->pool, "%s_%s_%s", tileset->name, grid->name, idval);
- /* normalize name to something that is a valid variable name */
- for(i=0; i<strlen(ol_layer_name); i++)
- if ((!i && !isalpha(ol_layer_name[i]) && ol_layer_name[i] != '_')
- || (!isalnum(ol_layer_name[i]) && ol_layer_name[i] != '_'))
- ol_layer_name[i] = '_';
- ol_layer = apr_psprintf(ctx->pool, demo_layer_wms,
- ol_layer_name,
- tileset->name,
- grid->name,
- apr_pstrcat(ctx->pool,url_prefix,"?",NULL),
- tileset->name,
- resolutions,
- unit,
- grid->extent.minx,
- grid->extent.miny,
- grid->extent.maxx,
- grid->extent.maxy,
- grid->srs,
- smerc,
- ol_layer_name);
- caps = apr_psprintf(ctx->pool,"%s%s",caps,ol_layer);
- caps = apr_psprintf(ctx->pool,"%s%s",caps,
- apr_psprintf(ctx->pool,dimparam_wms,ol_layer_name,tileset->timedimension->key,idval));
-
- if(service->getmap_strategy == MAPCACHE_GETMAP_ASSEMBLE) {
- ol_layer = apr_psprintf(ctx->pool, demo_layer_singletile,
+ int dim;
+ for(dim=0;dim<tileset->dimensions->nelts;dim++) {
+ int id;
+ mapcache_dimension *dimension = APR_ARRAY_IDX(tileset->dimensions,dim,mapcache_dimension*);
+ apr_array_header_t *dimvals = dimension->get_all_entries(ctx,dimension,tileset,NULL,NULL);
+ for(id=0;id<dimvals->nelts;id++) {
+ char *idval;
+ char *dimparam_wms;
+ char *dimparam_singletile;
+ if(id>1) break;
+ idval = APR_ARRAY_IDX(dimvals,id,char*);
+ dimparam_wms = " %s_wms_layer.mergeNewParams({%s:\"%s\"});\n";
+ dimparam_singletile = " %s_slayer.mergeNewParams({%s:\"%s\"});\n";
+ ol_layer_name = apr_psprintf(ctx->pool, "%s_%s_%s_%s", tileset->name, grid->name, dimension->name, idval);
+ /* normalize name to something that is a valid variable name */
+ for(i=0; i<strlen(ol_layer_name); i++)
+ if ((!i && !isalpha(ol_layer_name[i]) && ol_layer_name[i] != '_')
+ || (!isalnum(ol_layer_name[i]) && ol_layer_name[i] != '_'))
+ ol_layer_name[i] = '_';
+ ol_layer = apr_psprintf(ctx->pool, demo_layer_wms,
ol_layer_name,
tileset->name,
grid->name,
@@ -603,7 +584,28 @@ void _create_demo_wms(mapcache_context *ctx, mapcache_request_get_capabilities *
ol_layer_name);
caps = apr_psprintf(ctx->pool,"%s%s",caps,ol_layer);
caps = apr_psprintf(ctx->pool,"%s%s",caps,
- apr_psprintf(ctx->pool,dimparam_singletile,ol_layer_name,tileset->timedimension->key,idval));
+ apr_psprintf(ctx->pool,dimparam_wms,ol_layer_name,dimension->name,idval));
+
+ if(service->getmap_strategy == MAPCACHE_GETMAP_ASSEMBLE) {
+ ol_layer = apr_psprintf(ctx->pool, demo_layer_singletile,
+ ol_layer_name,
+ tileset->name,
+ grid->name,
+ apr_pstrcat(ctx->pool,url_prefix,"?",NULL),
+ tileset->name,
+ resolutions,
+ unit,
+ grid->extent.minx,
+ grid->extent.miny,
+ grid->extent.maxx,
+ grid->extent.maxy,
+ grid->srs,
+ smerc,
+ ol_layer_name);
+ caps = apr_psprintf(ctx->pool,"%s%s",caps,ol_layer);
+ caps = apr_psprintf(ctx->pool,"%s%s",caps,
+ apr_psprintf(ctx->pool,dimparam_singletile,ol_layer_name,dimension->name,idval));
+ }
}
}
}
@@ -840,7 +842,7 @@ void _create_demo_wmts(mapcache_context *ctx, mapcache_request_get_capabilities
resolutions = apr_psprintf(ctx->pool,"%s,%.20f",resolutions,grid->levels[i]->resolution);
}
- if(!tileset->timedimension) {
+ if(!tileset->dimensions) {
ol_layer_name = apr_psprintf(ctx->pool, "%s_%s", tileset->name, grid->name);
/* normalize name to something that is a valid variable name */
for(i=0; i<strlen(ol_layer_name); i++)
@@ -867,44 +869,47 @@ void _create_demo_wmts(mapcache_context *ctx, mapcache_request_get_capabilities
ol_layer_name);
caps = apr_psprintf(ctx->pool,"%s%s",caps,ol_layer);
} else {
- int id;
- apr_array_header_t *timedimvals = tileset->timedimension->get_all_entries(
- ctx,tileset->timedimension,tileset);
- GC_CHECK_ERROR(ctx);
- for(id=0;id<timedimvals->nelts;id++) {
- char *idval;
- char *dimparam;
- if(id>1) break; /* we don't want all the entries in the demo interface */
- idval = APR_ARRAY_IDX(timedimvals,id,char*);
- dimparam = "%s_wmts_layer.mergeNewParams({%s:\"%s\"});\n";
- ol_layer_name = apr_psprintf(ctx->pool, "%s_%s_%s", tileset->name, grid->name, idval);
- /* normalize name to something that is a valid variable name */
- for(i=0; i<strlen(ol_layer_name); i++)
- if ((!i && !isalpha(ol_layer_name[i]) && ol_layer_name[i] != '_')
- || (!isalnum(ol_layer_name[i]) && ol_layer_name[i] != '_'))
- ol_layer_name[i] = '_';
- ol_layer = apr_psprintf(ctx->pool, demo_layer_wmts,
- ol_layer_name,
- tileset->name,
- grid->name,
- apr_pstrcat(ctx->pool,url_prefix,"wmts/",NULL),
- tileset->name,
- grid->name,
- mime_type,
- resolutions,
- grid_link->minz,
- unit,
- grid->extent.minx,
- grid->extent.miny,
- grid->extent.maxx,
- grid->extent.maxy,
- grid->srs,
- smerc,
- ol_layer_name);
- caps = apr_psprintf(ctx->pool,"%s%s",caps,ol_layer);
- caps = apr_psprintf(ctx->pool,"%s%s",caps,
- apr_psprintf(ctx->pool,dimparam,ol_layer_name,tileset->timedimension->key,idval));
+ int dim;
+ for(dim=0;dim<tileset->dimensions->nelts;dim++) {
+ int id;
+ mapcache_dimension *dimension = APR_ARRAY_IDX(tileset->dimensions,dim,mapcache_dimension*);
+ apr_array_header_t *dimvals = dimension->get_all_entries(ctx,dimension,tileset,NULL,NULL);
+ GC_CHECK_ERROR(ctx);
+ for(id=0;id<dimvals->nelts;id++) {
+ char *idval;
+ char *dimparam;
+ if(id>1) break; /* we don't want all the entries in the demo interface */
+ idval = APR_ARRAY_IDX(dimvals,id,char*);
+ dimparam = "%s_wmts_layer.mergeNewParams({%s:\"%s\"});\n";
+ ol_layer_name = apr_psprintf(ctx->pool, "%s_%s_%s_%s", tileset->name, grid->name, dimension->name, idval);
+ /* normalize name to something that is a valid variable name */
+ for(i=0; i<strlen(ol_layer_name); i++)
+ if ((!i && !isalpha(ol_layer_name[i]) && ol_layer_name[i] != '_')
+ || (!isalnum(ol_layer_name[i]) && ol_layer_name[i] != '_'))
+ ol_layer_name[i] = '_';
+ ol_layer = apr_psprintf(ctx->pool, demo_layer_wmts,
+ ol_layer_name,
+ tileset->name,
+ grid->name,
+ apr_pstrcat(ctx->pool,url_prefix,"wmts/",NULL),
+ tileset->name,
+ grid->name,
+ mime_type,
+ resolutions,
+ grid_link->minz,
+ unit,
+ grid->extent.minx,
+ grid->extent.miny,
+ grid->extent.maxx,
+ grid->extent.maxy,
+ grid->srs,
+ smerc,
+ ol_layer_name);
+ caps = apr_psprintf(ctx->pool,"%s%s",caps,ol_layer);
+ caps = apr_psprintf(ctx->pool,"%s%s",caps,
+ apr_psprintf(ctx->pool,dimparam,ol_layer_name,dimension->name,idval));
+ }
}
}
}
diff --git a/lib/service_kml.c b/lib/service_kml.c
index 9b42e04..424ec6f 100644
--- a/lib/service_kml.c
+++ b/lib/service_kml.c
@@ -29,8 +29,10 @@
#include "mapcache.h"
#include <apr_strings.h>
+#include "mapcache_services.h"
#include <math.h>
+
/** \addtogroup services */
/** @{ */
@@ -72,7 +74,7 @@ void _create_capabilities_kml(mapcache_context *ctx, mapcache_request_get_capabi
t->x = i;
t->y = j;
t->z = 0;
- mapcache_grid_get_extent(ctx, t->grid_link->grid,
+ mapcache_grid_get_tile_extent(ctx, t->grid_link->grid,
t->x, t->y, t->z, &bb);
caps = apr_psprintf(ctx->pool, "%s"
@@ -102,7 +104,7 @@ void _create_capabilities_kml(mapcache_context *ctx, mapcache_request_get_capabi
} else {
mapcache_extent bbox;
- mapcache_grid_get_extent(ctx, request->tile->grid_link->grid,
+ mapcache_grid_get_tile_extent(ctx, request->tile->grid_link->grid,
request->tile->x, request->tile->y, request->tile->z, &bbox);
@@ -144,7 +146,7 @@ void _create_capabilities_kml(mapcache_context *ctx, mapcache_request_get_capabi
t->x = (request->tile->x << 1) + i;
t->y = (request->tile->y << 1) + j;
t->z = request->tile->z + 1;
- mapcache_grid_get_extent(ctx, t->grid_link->grid,
+ mapcache_grid_get_tile_extent(ctx, t->grid_link->grid,
t->x, t->y, t->z, &bb);
caps = apr_psprintf(ctx->pool, "%s"
diff --git a/lib/service_mapguide.c b/lib/service_mapguide.c
index f1e72e8..67e9f62 100644
--- a/lib/service_mapguide.c
+++ b/lib/service_mapguide.c
@@ -30,12 +30,12 @@
#include "mapcache.h"
#include <apr_strings.h>
#include <math.h>
+#include "mapcache_services.h"
/** \addtogroup services */
/** @{ */
-
void _create_capabilities_mg(mapcache_context *ctx, mapcache_request_get_capabilities *req, char *url, char *path_info, mapcache_cfg *cfg)
{
ctx->set_error(ctx, 501, "mapguide service does not support capapbilities");
diff --git a/lib/service_tms.c b/lib/service_tms.c
index 3de8d8a..8085d8b 100644
--- a/lib/service_tms.c
+++ b/lib/service_tms.c
@@ -30,7 +30,8 @@
#include "mapcache.h"
#include <apr_strings.h>
#include <math.h>
-#include <ezxml.h>
+#include "mapcache_services.h"
+
/** \addtogroup services */
/** @{ */
@@ -174,6 +175,133 @@ void _create_capabilities_tms(mapcache_context *ctx, mapcache_request_get_capabi
}
+struct requested_tms_layer{
+ mapcache_tileset *tileset;
+ mapcache_grid_link *grid_link;
+ apr_array_header_t *dimensions;
+};
+
+/**
+ * @brief parse a TMS key representing a layer
+ * @param ctx
+ * @param key the string representing the requested layer. can be of the form
+ * layer
+ * layer at gridname
+ * layer[dim1=value1]
+ * layer[dim1=val1][dim2=val2]
+ * layer[dim=val]@gridname
+ * @return
+ */
+static struct requested_tms_layer* _mapcache_service_tms_parse_layer_key(mapcache_context *ctx, char *key) {
+ char *grid_name = NULL;
+ struct requested_tms_layer *rtl = apr_pcalloc(ctx->pool, sizeof(struct requested_tms_layer));
+ char *at_ptr = strchr(key,'@');
+ char *dim_ptr = strchr(key,'[');
+ if(!at_ptr && !dim_ptr) {
+ rtl->tileset = mapcache_configuration_get_tileset(ctx->config,key);
+ if(!rtl->tileset) {
+ ctx->set_error(ctx,400,"received TMS with invalid layer name");
+ return NULL;
+ }
+ return rtl;
+ } else {
+ key = apr_pstrdup(ctx->pool,key);
+ if(at_ptr) {
+ at_ptr = strchr(key,'@');
+ *at_ptr = 0;
+ }
+ if(dim_ptr) {
+ dim_ptr = strchr(key,'[');
+ *dim_ptr = 0;
+ }
+ rtl->tileset = mapcache_configuration_get_tileset(ctx->config,key);
+ if(!rtl->tileset) {
+ ctx->set_error(ctx,400,"received TMS with invalid layer name");
+ return NULL;
+ }
+ /*reapply opening bracket*/
+ if(dim_ptr) {
+ *dim_ptr = '[';
+ }
+ }
+
+ /* if here, we have a valid rtl->tileset defined */
+
+ /* if we have a specific grid requested */
+ if(at_ptr) {
+ int i;
+ grid_name = at_ptr + 1;
+ if(!*grid_name) {
+ ctx->set_error(ctx,400,"received invalid tms layer name. expecting layer_name at grid_name");
+ return NULL;
+ }
+ for(i=0; i<rtl->tileset->grid_links->nelts; i++) {
+ mapcache_grid_link *sgrid = APR_ARRAY_IDX(rtl->tileset->grid_links,i,mapcache_grid_link*);
+ if(!strcmp(sgrid->grid->name,grid_name)) {
+ rtl->grid_link = sgrid;
+ break;
+ }
+ }
+ if(!rtl->grid_link) {
+ ctx->set_error(ctx,400,"received invalid tms layer. grid not configured for requested layer");
+ return NULL;
+ }
+ }
+
+ /*if we have dimensions requested*/
+ if(dim_ptr) {
+ int i;
+ if(!rtl->tileset->dimensions || rtl->tileset->dimensions->nelts < 1) {
+ ctx->set_error(ctx,400,"received invalid tms layer. no dimensions configured for tileset");
+ return NULL;
+ }
+ for(i=0;i<rtl->tileset->dimensions->nelts;i++) {
+ mapcache_dimension *dimension = APR_ARRAY_IDX(rtl->tileset->dimensions,i,mapcache_dimension*);
+ char *individual_dim_ptr;
+ char *dim_lookup = apr_pstrcat(ctx->pool,"[",dimension->name,"=",NULL);
+ individual_dim_ptr = strstr(dim_ptr,dim_lookup);
+ if(individual_dim_ptr) {
+ mapcache_requested_dimension rdim;
+ char *dim_value;
+ char *dim_value_end;
+ if(!rtl->dimensions) {
+ rtl->dimensions = apr_array_make(ctx->pool,1,sizeof(mapcache_requested_dimension));
+ }
+ dim_value = individual_dim_ptr + strlen(dim_lookup);
+ if(!*dim_value || *dim_value == ']') {
+ ctx->set_error(ctx,400,"received invalid tms layer. failed (1) to parse dimension value");
+ return NULL;
+ }
+ dim_value_end = strchr(dim_value,']');
+ if(!dim_value_end) {
+ ctx->set_error(ctx,400,"received invalid tms layer. failed (2) to parse dimension value");
+ return NULL;
+ }
+ *dim_value_end = 0;
+ rdim.dimension = dimension;
+ rdim.requested_value = apr_pstrdup(ctx->pool,dim_value);
+ *dim_value_end = ']';
+ APR_ARRAY_PUSH(rtl->dimensions,mapcache_requested_dimension) = rdim;
+ }
+ }
+ /* sanity check: make sure we have as many requested dimensions as '[' characters in key */
+ {
+ int i, count;
+ if(!rtl->dimensions) {
+ ctx->set_error(ctx,400,"received invalid tms layer. failed (3) to parse dimension values");
+ return NULL;
+ }
+ for (i=0, count=0; dim_ptr[i]; i++)
+ count += (dim_ptr[i] == '[');
+ if(count != rtl->dimensions->nelts) {
+ ctx->set_error(ctx,400,"received invalid tms layer. failed (4) to parse dimension values");
+ return NULL;
+ }
+ }
+ }
+ return rtl;
+}
+
/**
* \brief parse a TMS request
* \private \memberof mapcache_service_tms
@@ -185,8 +313,6 @@ void _mapcache_service_tms_parse_request(mapcache_context *ctx, mapcache_service
int index = 0;
char *last, *key, *endptr;
char *sTileset = NULL;
- mapcache_tileset *tileset = NULL;
- mapcache_grid_link *grid_link = NULL;
char *pathinfo = NULL;
int x=-1,y=-1,z=-1;
@@ -238,93 +364,73 @@ void _mapcache_service_tms_parse_request(mapcache_context *ctx, mapcache_service
}
}
if(index == 5) {
- char *gridname;
+ char *iter,*gridname=NULL;
mapcache_request_get_tile *req = (mapcache_request_get_tile*)apr_pcalloc(ctx->pool,sizeof(mapcache_request_get_tile));
((mapcache_request*)req)->type = MAPCACHE_REQUEST_GET_TILE;
- gridname = sTileset; /*hijack the char* pointer while counting the number of commas */
- while(*gridname) {
- if(*gridname == ';') req->ntiles++;
- gridname++;
+ iter = sTileset;
+ while(*iter) {
+ if(*iter == ';') req->ntiles++;
+ iter++;
}
req->tiles = (mapcache_tile**)apr_pcalloc(ctx->pool,(req->ntiles+1) * sizeof(mapcache_tile*));
- /* reset the hijacked variables back to default value */
- gridname = NULL;
req->ntiles = 0;
for (key = apr_strtok(sTileset, ";", &last); key != NULL;
key = apr_strtok(NULL, ";", &last)) {
- tileset = mapcache_configuration_get_tileset(config,key);
- if(!tileset) {
- /*tileset not found directly, test if it was given as "name at grid" notation*/
- char *tname = apr_pstrdup(ctx->pool,key);
- char *gname = tname;
- int i;
- while(*gname) {
- if(*gname == '@') {
- *gname = '\0';
- gname++;
- break;
- }
- gname++;
- }
- if(!gname) {
- ctx->set_error(ctx,404, "received tms request with invalid layer %s", key);
- return;
- }
- tileset = mapcache_configuration_get_tileset(config,tname);
- if(!tileset) {
- ctx->set_error(ctx,404, "received tms request with invalid layer %s", tname);
- return;
- }
- for(i=0; i<tileset->grid_links->nelts; i++) {
- mapcache_grid_link *sgrid = APR_ARRAY_IDX(tileset->grid_links,i,mapcache_grid_link*);
- if(!strcmp(sgrid->grid->name,gname)) {
- grid_link = sgrid;
- break;
- }
- }
- if(!grid_link) {
- ctx->set_error(ctx,404, "received tms request with invalid grid %s", gname);
- return;
- }
-
- } else {
- grid_link = APR_ARRAY_IDX(tileset->grid_links,0,mapcache_grid_link*);
+ struct requested_tms_layer *rtl = _mapcache_service_tms_parse_layer_key(ctx,key);
+ GC_CHECK_ERROR(ctx);
+ if(!rtl->grid_link) {
+ rtl->grid_link = APR_ARRAY_IDX(rtl->tileset->grid_links,0,mapcache_grid_link*);
}
+
+ /*make sure all our requested layers have the same grid*/
if(!gridname) {
- gridname = grid_link->grid->name;
+ gridname = rtl->grid_link->grid->name;
} else {
- if(strcmp(gridname,grid_link->grid->name)) {
+ if(strcmp(gridname,rtl->grid_link->grid->name)) {
ctx->set_error(ctx,400,"received tms request with conflicting grids %s and %s",
- gridname,grid_link->grid->name);
+ gridname,rtl->grid_link->grid->name);
return;
}
}
if(((mapcache_service_tms*)this)->reverse_y) {
- y = grid_link->grid->levels[z]->maxy - y - 1;
+ y = rtl->grid_link->grid->levels[z]->maxy - y - 1;
}
- req->tiles[req->ntiles] = mapcache_tileset_tile_create(ctx->pool, tileset, grid_link);
- switch(grid_link->grid->origin) {
+ req->tiles[req->ntiles] = mapcache_tileset_tile_create(ctx->pool, rtl->tileset, rtl->grid_link);
+ switch(rtl->grid_link->grid->origin) {
case MAPCACHE_GRID_ORIGIN_BOTTOM_LEFT:
req->tiles[req->ntiles]->x = x;
req->tiles[req->ntiles]->y = y;
break;
case MAPCACHE_GRID_ORIGIN_TOP_LEFT:
req->tiles[req->ntiles]->x = x;
- req->tiles[req->ntiles]->y = grid_link->grid->levels[z]->maxy - y - 1;
+ req->tiles[req->ntiles]->y = rtl->grid_link->grid->levels[z]->maxy - y - 1;
break;
case MAPCACHE_GRID_ORIGIN_BOTTOM_RIGHT:
- req->tiles[req->ntiles]->x = grid_link->grid->levels[z]->maxx - x - 1;
+ req->tiles[req->ntiles]->x = rtl->grid_link->grid->levels[z]->maxx - x - 1;
req->tiles[req->ntiles]->y = y;
break;
case MAPCACHE_GRID_ORIGIN_TOP_RIGHT:
- req->tiles[req->ntiles]->x = grid_link->grid->levels[z]->maxx - x - 1;
- req->tiles[req->ntiles]->y = grid_link->grid->levels[z]->maxy - y - 1;
+ req->tiles[req->ntiles]->x = rtl->grid_link->grid->levels[z]->maxx - x - 1;
+ req->tiles[req->ntiles]->y = rtl->grid_link->grid->levels[z]->maxy - y - 1;
break;
}
req->tiles[req->ntiles]->z = z;
mapcache_tileset_tile_validate(ctx,req->tiles[req->ntiles]);
+ if(rtl->dimensions) {
+ int i;
+ for(i=0;i<rtl->dimensions->nelts;i++) {
+ mapcache_requested_dimension rdim = APR_ARRAY_IDX(rtl->dimensions,i,mapcache_requested_dimension);
+ int j;
+ for(j=0;j<req->tiles[req->ntiles]->dimensions->nelts;j++) {
+ mapcache_requested_dimension *tile_dim = APR_ARRAY_IDX(req->tiles[req->ntiles]->dimensions,j,mapcache_requested_dimension*);
+ if(!strcasecmp(tile_dim->dimension->name,rdim.dimension->name)) {
+ tile_dim->requested_value = rdim.requested_value;
+ }
+ }
+ }
+ }
req->ntiles++;
GC_CHECK_ERROR(ctx);
}
@@ -335,45 +441,19 @@ void _mapcache_service_tms_parse_request(mapcache_context *ctx, mapcache_service
ctx->pool,sizeof(mapcache_request_get_capabilities_tms));
req->request.request.type = MAPCACHE_REQUEST_GET_CAPABILITIES;
if(index == 2) {
- tileset = mapcache_configuration_get_tileset(config,sTileset);
- if(!tileset) {
- /*tileset not found directly, test if it was given as "name at grid" notation*/
- char *tname = apr_pstrdup(ctx->pool,sTileset);
- char *gname = tname;
- int i;
- while(*gname) {
- if(*gname == '@') {
- *gname = '\0';
- gname++;
- break;
- }
- gname++;
- }
- if(!gname) {
- ctx->set_error(ctx,404, "received tms request with invalid layer %s", key);
- return;
- }
- tileset = mapcache_configuration_get_tileset(config,tname);
- if(!tileset) {
- ctx->set_error(ctx,404, "received tms request with invalid layer %s", tname);
- return;
- }
- for(i=0; i<tileset->grid_links->nelts; i++) {
- mapcache_grid_link *sgrid = APR_ARRAY_IDX(tileset->grid_links,i,mapcache_grid_link*);
- if(!strcmp(sgrid->grid->name,gname)) {
- grid_link = sgrid;
- break;
- }
- }
- if(!grid_link) {
- ctx->set_error(ctx,404, "received tms request with invalid grid %s", gname);
- return;
- }
- } else {
- grid_link = APR_ARRAY_IDX(tileset->grid_links,0,mapcache_grid_link*);
+ struct requested_tms_layer *rtl;
+ if(strchr(sTileset,';')) {
+ ctx->set_error(ctx,400,"tms caps: invalid tileset name");
+ return;
+ }
+
+ rtl = _mapcache_service_tms_parse_layer_key(ctx,sTileset);
+ GC_CHECK_ERROR(ctx);
+ if(!rtl->grid_link) {
+ rtl->grid_link = APR_ARRAY_IDX(rtl->tileset->grid_links,0,mapcache_grid_link*);
}
- req->tileset = tileset;
- req->grid_link = grid_link;
+ req->tileset = rtl->tileset;
+ req->grid_link = rtl->grid_link;
}
if(index >= 1) {
req->version = apr_pstrdup(ctx->pool,"1.0.0");
diff --git a/lib/service_ve.c b/lib/service_ve.c
index 0bcdb95..658d168 100644
--- a/lib/service_ve.c
+++ b/lib/service_ve.c
@@ -30,13 +30,13 @@
#include "mapcache.h"
#include <apr_strings.h>
#include <math.h>
+#include "mapcache_services.h"
/** \addtogroup services */
/** @{ */
-
void _create_capabilities_ve(mapcache_context *ctx, mapcache_request_get_capabilities *req, char *url, char *path_info, mapcache_cfg *cfg)
{
ctx->set_error(ctx, 501, "ve service does not support capapbilities");
@@ -98,32 +98,12 @@ void _mapcache_service_ve_parse_request(mapcache_context *ctx, mapcache_service
quadkey = apr_table_get(params, "tile");
tile = mapcache_tileset_tile_create(ctx->pool, tileset, grid_link);
if (quadkey) {
- z = strlen(quadkey);
+ mapcache_util_quadkey_decode(ctx, quadkey, &x, &y, &z);
+ GC_CHECK_ERROR(ctx);
if (z < 1 || z >= grid_link->grid->nlevels) {
ctx->set_error(ctx, 404, "received ve request with invalid z level %d\n", z);
return;
}
- x = y = 0;
- for (i = z; i; i--) {
- int mask = 1 << (i - 1);
- switch (quadkey[z - i]) {
- case '0':
- break;
- case '1':
- x |= mask;
- break;
- case '2':
- y |= mask;
- break;
- case '3':
- x |= mask;
- y |= mask;
- break;
- default:
- ctx->set_error(ctx, 404, "Invalid Quadkey sequence");
- return;
- }
- }
} else {
ctx->set_error(ctx, 400, "received ve request with no tile quadkey");
return;
diff --git a/lib/service_wms.c b/lib/service_wms.c
index 6e6c34f..0ac1845 100644
--- a/lib/service_wms.c
+++ b/lib/service_wms.c
@@ -30,7 +30,14 @@
#include "mapcache.h"
#include <apr_strings.h>
#include <math.h>
-#include "ezxml.h"
+#include "mapcache_services.h"
+
+static int sort_strings(const void* pa, const void* pb)
+{
+ char** ppszA = (char**)pa;
+ char** ppszB = (char**)pb;
+ return strcmp(*ppszA, *ppszB);
+}
/** \addtogroup services */
/** @{ */
@@ -158,7 +165,7 @@ void _create_capabilities_wms(mapcache_context *ctx, mapcache_request_get_capabi
"</Exception>\n"
*/
- tmpxml = ezxml_add_child(capxml,"Exceptions",0);
+ tmpxml = ezxml_add_child(capxml,"Exception",0);
ezxml_set_txt(ezxml_add_child(tmpxml,"Format",0),"text/plain");
vendorxml = ezxml_add_child(capxml,"VendorSpecificCapabilities",0);
@@ -173,16 +180,28 @@ void _create_capabilities_wms(mapcache_context *ctx, mapcache_request_get_capabi
*
* TODO: check for duplicates in gris srs
*/
- grid_index = apr_hash_first(ctx->pool,cfg->grids);
- while(grid_index) {
- const void *key;
- apr_ssize_t keylen;
- mapcache_grid *grid = NULL;
- apr_hash_this(grid_index,&key,&keylen,(void**)&grid);
- ezxml_set_txt(ezxml_add_child(toplayer,"SRS",0),grid->srs);
- grid_index = apr_hash_next(grid_index);
- }
+ {
+ int srs_count = (int)apr_hash_count(cfg->grids);
+ char** srs_list = (char**)malloc(srs_count * sizeof(char*));
+ int srs_iter = 0;
+ int i;
+ grid_index = apr_hash_first(ctx->pool,cfg->grids);
+ while(grid_index) {
+ const void *key;
+ apr_ssize_t keylen;
+ mapcache_grid *grid = NULL;
+ apr_hash_this(grid_index,&key,&keylen,(void**)&grid);
+ srs_list[srs_iter++] = grid->srs;
+ grid_index = apr_hash_next(grid_index);
+ }
+ qsort(srs_list, srs_count, sizeof(char*), sort_strings);
+ for(i = 0; i < srs_count; i ++)
+ {
+ ezxml_set_txt(ezxml_add_child(toplayer,"SRS",0),srs_list[i]);
+ }
+ free(srs_list);
+ }
tileindex_index = apr_hash_first(ctx->pool,cfg->tilesets);
@@ -239,7 +258,7 @@ void _create_capabilities_wms(mapcache_context *ctx, mapcache_request_get_capabi
if(dimension->unit) {
ezxml_set_attr(dimxml,"units",dimension->unit);
}
- values = dimension->print_ogc_formatted_values(ctx,dimension);
+ values = dimension->get_all_ogc_formatted_entries(ctx,dimension,tileset,NULL,NULL);
for(value_idx=0;value_idx<values->nelts;value_idx++) {
char *idval = APR_ARRAY_IDX(values,value_idx,char*);
if(dimval) {
@@ -320,6 +339,26 @@ void _create_capabilities_wms(mapcache_context *ctx, mapcache_request_get_capabi
request->request.mime_type = apr_pstrdup(ctx->pool,"text/xml");
}
+static char *_lookup_auto_projection(mapcache_context *ctx, const char *srs) {
+ if(!strcasecmp(srs,"auto:42001")) {
+ char *srsdup = apr_pstrdup(ctx->pool,srs);
+ char *lon = strchr(srsdup,','),*lat;
+ int nLon,nLat;
+ if(!lon) return srsdup;
+ lon = strchr(lon+1,',');
+ if(!lon) return srsdup;
+ lon++;
+ lat = strchr(lon,',');
+ if(!lat) return srsdup;
+ *lat = 0;
+ lat++;
+ nLon = (int)(floor( (atof(lon) + 180.0) / 6.0 ))*6 + 3 - 180;
+ nLat = (atof(lat)>=0)?45:-45;
+ return apr_psprintf(ctx->pool,"auto:42001,9001,%d,%d",nLon,nLat);
+ }
+ return (char*)srs;
+}
+
/**
* \brief parse a WMS request
* \private \memberof mapcache_service_wms
@@ -478,7 +517,6 @@ void _mapcache_service_wms_parse_request(mapcache_context *ctx, mapcache_service
char *last, *layers;
const char *key;
int count=1;
- int nallocated = 0;
int i,layeridx;
int x,y,z;
mapcache_request_get_map *map_req = NULL;
@@ -519,6 +557,8 @@ void _mapcache_service_wms_parse_request(mapcache_context *ctx, mapcache_service
main_tileset->name = (char*)key;
}
+ srs = _lookup_auto_projection(ctx,srs);
+
for(i=0; i<main_tileset->grid_links->nelts; i++) {
mapcache_grid_link *sgrid = APR_ARRAY_IDX(main_tileset->grid_links,i,mapcache_grid_link*);
/* look for a grid with a matching srs */
@@ -584,7 +624,6 @@ void _mapcache_service_wms_parse_request(mapcache_context *ctx, mapcache_service
*request = (mapcache_request*)map_req;
(*request)->type = MAPCACHE_REQUEST_GET_MAP;
}
- nallocated = count;
/*
@@ -599,7 +638,7 @@ void _mapcache_service_wms_parse_request(mapcache_context *ctx, mapcache_service
int i;
mapcache_tileset *tileset = main_tileset;
mapcache_grid_link *grid_link = main_grid_link;
- apr_table_t *dimtable = NULL;
+ apr_array_header_t *dimtable = NULL;
if(layeridx) {
/*
@@ -671,94 +710,16 @@ void _mapcache_service_wms_parse_request(mapcache_context *ctx, mapcache_service
if(strcasecmp(dimension->name,"TIME") && strcasecmp(dimension->name,"ELEVATION")) {
/* also test for the dimension without the DIM_ prefix if the latter was not found in the KVP params */
dim_name = dimension->name;
- value = (char*)apr_table_get(params,dimension->name);
+ value = (char*)apr_table_get(params,dim_name);
}
}
if(value) {
- char *tmpval = apr_pstrdup(ctx->pool,value);
- int ok;
- if(dimension->skip_validation) {
- ok = MAPCACHE_SUCCESS;
- } else {
- ok = dimension->validate(ctx,dimension,&tmpval);
- GC_CHECK_ERROR(ctx);
- }
- if(ok == MAPCACHE_SUCCESS)
- apr_table_setn(dimtable,dimension->name,tmpval);
- else {
- errcode = 400;
- errmsg = apr_psprintf(ctx->pool, "dimension \"%s\" value \"%s\" fails to validate",
- dim_name, value);
- goto proxies;
- }
+ mapcache_set_requested_dimension(ctx,dimtable,dimension->name,value);
+ GC_CHECK_ERROR(ctx);
}
}
}
- if(tileset->timedimension) {
- /* possibly duplicate the created map/tile for each entry of the requested time dimension */
- apr_array_header_t *timedim_selected;
- value = apr_table_get(params,tileset->timedimension->key);
- if(!value)
- value = tileset->timedimension->default_value;
- timedim_selected = mapcache_timedimension_get_entries_for_value(ctx,
- tileset->timedimension, tileset, grid_link->grid, &extent, value);
- GC_CHECK_ERROR(ctx);
- if(!timedim_selected || timedim_selected->nelts == 0) {
- errcode = 404;
- errmsg = apr_psprintf(ctx->pool,"no matching entry for given TIME dimension \"%s\" in tileset \"%s\"",
- tileset->timedimension->key, tileset->name);
- goto proxies;
- }
- if(type == MAPCACHE_REQUEST_GET_TILE) {
- int i;
- /* we need to create more tile/map entries */
- if(timedim_selected->nelts > 1) {
- /* apr pools have no realloc */
- mapcache_tile** tmptiles;
- nallocated = nallocated + timedim_selected->nelts - 1;
- tmptiles = apr_palloc(ctx->pool, nallocated * sizeof(mapcache_tile*));
- for(i=0;i<tile_req->ntiles;i++) {
- tmptiles[i] = tile_req->tiles[i];
- }
- tile_req->tiles = tmptiles;
- /* end realloc workaround */
- }
- for(i=0;i<timedim_selected->nelts;i++) {
- if(i) {
- tile_req->tiles[tile_req->ntiles] =
- mapcache_tileset_tile_clone(ctx->pool,tile_req->tiles[tile_req->ntiles-1]);
- tile_req->ntiles++;
- }
- apr_table_set(tile_req->tiles[tile_req->ntiles-1]->dimensions,tileset->timedimension->key,
- APR_ARRAY_IDX(timedim_selected,i,char*));
- }
- } else {
- int i;
- /* we need to create more tile/map entries */
- if(timedim_selected->nelts > 1) {
- /* apr pools have no realloc */
- mapcache_map** tmpmaps;
- nallocated = nallocated + timedim_selected->nelts - 1;
- tmpmaps = apr_palloc(ctx->pool, nallocated * sizeof(mapcache_map*));
- for(i=0;i<map_req->nmaps;i++) {
- tmpmaps[i] = map_req->maps[i];
- }
- map_req->maps = tmpmaps;
- /* end realloc workaround */
- }
- for(i=0;i<timedim_selected->nelts;i++) {
- if(i) {
- map_req->maps[map_req->nmaps] =
- mapcache_tileset_map_clone(ctx->pool,map_req->maps[map_req->nmaps-1]);
- map_req->nmaps++;
- }
- apr_table_set(map_req->maps[map_req->nmaps-1]->dimensions,tileset->timedimension->key,
- APR_ARRAY_IDX(timedim_selected,i,char*));
- }
-
- }
- }
}
}
if(tile_req && tile_req->ntiles == 0) {
@@ -856,17 +817,8 @@ void _mapcache_service_wms_parse_request(mapcache_context *ctx, mapcache_service
mapcache_dimension *dimension = APR_ARRAY_IDX(tileset->dimensions,i,mapcache_dimension*);
const char *value;
if((value = (char*)apr_table_get(params,dimension->name)) != NULL) {
- char *tmpval = apr_pstrdup(ctx->pool,value);
- int ok = dimension->validate(ctx,dimension,&tmpval);
+ mapcache_map_set_cached_dimension(ctx,&fi->map,dimension->name,value);
GC_CHECK_ERROR(ctx);
- if(ok == MAPCACHE_SUCCESS)
- apr_table_setn(fi->map.dimensions,dimension->name,tmpval);
- else {
- errcode = 400;
- errmsg = apr_psprintf(ctx->pool,"dimension \"%s\" value \"%s\" fails to validate",
- dimension->name, value);
- goto proxies;
- }
}
}
}
@@ -908,7 +860,7 @@ proxies:
for(j=0; j<rule->match_params->nelts; j++) {
mapcache_dimension *match_param = APR_ARRAY_IDX(rule->match_params,j,mapcache_dimension*);
const char *value = apr_table_get(params,match_param->name);
- if(!value || match_param->validate(ctx,match_param,(char**)&value) == MAPCACHE_FAILURE) {
+ if(!value || match_param->get_entries_for_value(ctx,match_param,value,NULL,NULL,NULL)->nelts == 0) {
/* the parameter was not supplied, or did not validate: we don't apply this rule */
got_a_match = 0;
break;
@@ -1005,9 +957,9 @@ void _configuration_parse_wms_xml(mapcache_context *ctx, ezxml_t node, mapcache_
if(type && *type) {
if(!strcmp(type,"values")) {
- dimension = mapcache_dimension_values_create(ctx->pool);
+ dimension = mapcache_dimension_values_create(ctx,ctx->pool);
} else if(!strcmp(type,"regex")) {
- dimension = mapcache_dimension_regex_create(ctx->pool);
+ dimension = mapcache_dimension_regex_create(ctx,ctx->pool);
} else {
ctx->set_error(ctx,400,"unknown <param> type \"%s\". expecting \"values\" or \"regex\".",type);
return;
@@ -1016,6 +968,7 @@ void _configuration_parse_wms_xml(mapcache_context *ctx, ezxml_t node, mapcache_
ctx->set_error(ctx,400, "mandatory attribute \"type\" not found in <dimensions>");
return;
}
+ GC_CHECK_ERROR(ctx);
dimension->name = apr_pstrdup(ctx->pool,name);
diff --git a/lib/service_wmts.c b/lib/service_wmts.c
index 569bbc7..f12f1d4 100644
--- a/lib/service_wmts.c
+++ b/lib/service_wmts.c
@@ -31,13 +31,12 @@
#include <apr_strings.h>
#include <math.h>
#include <apr_tables.h>
-#include "ezxml.h"
+#include "mapcache_services.h"
/** \addtogroup services */
/** @{ */
-
static ezxml_t _wmts_capabilities(mapcache_context *ctx, mapcache_cfg *cfg)
{
char *schemaLocation = "http://www.opengis.net/wmts/1.0 http://schemas.opengis.net/wmts/1.0/wmtsGetCapabilities_response.xsd";
@@ -382,7 +381,7 @@ void _create_capabilities_wmts(mapcache_context *ctx, mapcache_request_get_capab
if(dimension->unit) {
ezxml_set_txt(ezxml_add_child(dim,"UOM",0),dimension->unit);
}
- values = dimension->print_ogc_formatted_values(ctx,dimension);
+ values = dimension->get_all_ogc_formatted_entries(ctx,dimension,tileset,NULL,NULL);
for(value_idx=0;value_idx<values->nelts;value_idx++) {
char *idval = APR_ARRAY_IDX(values,value_idx,char*);
ezxml_set_txt(ezxml_add_child(dim,"Value",0),idval);
@@ -414,7 +413,6 @@ void _create_capabilities_wmts(mapcache_context *ctx, mapcache_request_get_capab
int j;
for(j=0; j<grid_link->grid->nlevels; j++) {
ezxml_t matrixlimits = ezxml_add_child(limits,"TileMatrixLimits",0);
- int row;
ezxml_set_txt(ezxml_add_child(matrixlimits,"TileMatrix",0),
apr_psprintf(ctx->pool,"%s:%d",grid_link->grid->name,j));
ezxml_set_txt(ezxml_add_child(matrixlimits,"MinTileRow",0),
@@ -498,10 +496,18 @@ void _create_capabilities_wmts(mapcache_context *ctx, mapcache_request_get_capab
bbox = ezxml_add_child(tmset,"ows:BoundingBox",0);
- ezxml_set_txt(ezxml_add_child(bbox,"ows:LowerCorner",0),apr_psprintf(ctx->pool,"%f %f",
- grid->extent.minx, grid->extent.miny));
- ezxml_set_txt(ezxml_add_child(bbox,"ows:UpperCorner",0),apr_psprintf(ctx->pool,"%f %f",
- grid->extent.maxx, grid->extent.maxy));
+ if(mapcache_is_axis_inverted(grid->srs)) {
+ ezxml_set_txt(ezxml_add_child(bbox,"ows:LowerCorner",0),apr_psprintf(ctx->pool,"%f %f",
+ grid->extent.miny, grid->extent.minx));
+ ezxml_set_txt(ezxml_add_child(bbox,"ows:UpperCorner",0),apr_psprintf(ctx->pool,"%f %f",
+ grid->extent.maxy, grid->extent.maxx));
+ } else {
+ ezxml_set_txt(ezxml_add_child(bbox,"ows:LowerCorner",0),apr_psprintf(ctx->pool,"%f %f",
+ grid->extent.minx, grid->extent.miny));
+ ezxml_set_txt(ezxml_add_child(bbox,"ows:UpperCorner",0),apr_psprintf(ctx->pool,"%f %f",
+ grid->extent.maxx, grid->extent.maxy));
+ }
+
ezxml_set_attr(bbox,"crs",mapcache_grid_get_crs(ctx,grid));
ezxml_set_txt(ezxml_add_child(tmset,"ows:SupportedCRS",0),mapcache_grid_get_crs(ctx,grid));
@@ -528,6 +534,7 @@ void _create_capabilities_wmts(mapcache_context *ctx, mapcache_request_get_capab
break;
case MAPCACHE_GRID_ORIGIN_BOTTOM_RIGHT:
case MAPCACHE_GRID_ORIGIN_TOP_RIGHT:
+ default:
ctx->set_error(ctx,500,"origin not implemented");
return;
}
@@ -567,8 +574,6 @@ void _mapcache_service_wmts_parse_request(mapcache_context *ctx, mapcache_servic
*infoformat = NULL, *fi_i = NULL, *fi_j = NULL;
apr_table_t *dimtable = NULL;
mapcache_extent *extent = NULL;
- char *timedim = NULL;
- apr_array_header_t *timedim_selected; /* the individual time entries that corresponded to the input request */
mapcache_tileset *tileset = NULL;
int row,col,level,x,y;
int kvp = 0;
@@ -631,14 +636,6 @@ void _mapcache_service_wmts_parse_request(mapcache_context *ctx, mapcache_servic
}
}
}
- if(tileset->timedimension) {
- const char* value;
- if((value = apr_table_get(params,tileset->timedimension->key)) != NULL) {
- timedim = apr_pstrdup(ctx->pool,value);
- } else {
- timedim = apr_pstrdup(ctx->pool,tileset->timedimension->default_value);
- }
- }
if(!strcasecmp(str,"getfeatureinfo")) {
infoformat = apr_table_get(params,"INFOFORMAT");
fi_i = apr_table_get(params,"I");
@@ -703,10 +700,6 @@ void _mapcache_service_wmts_parse_request(mapcache_context *ctx, mapcache_servic
continue;
}
}
- if(!timedim && tileset->timedimension) {
- timedim = key;
- continue;
- }
if(!matrixset) {
matrixset = key;
continue;
@@ -769,41 +762,9 @@ void _mapcache_service_wmts_parse_request(mapcache_context *ctx, mapcache_servic
return;
}
- /*validate dimensions*/
- if(tileset->dimensions) {
- int i;
- if(!dimtable) {
- ctx->set_error(ctx,404, "received request with no dimensions");
- if(kvp) ctx->set_exception(ctx,"InvalidParameterValue","dim");
- return;
- }
-
- for(i=0; i<tileset->dimensions->nelts; i++) {
- char *tmpval;
- int ok;
- const char *value;
- mapcache_dimension *dimension = APR_ARRAY_IDX(tileset->dimensions,i,mapcache_dimension*);
- if(dimension->skip_validation) continue;
- value = apr_table_get(dimtable,dimension->name);
- if(value) {
- tmpval = apr_pstrdup(ctx->pool,value);
- ok = dimension->validate(ctx,dimension,&tmpval);
- GC_CHECK_ERROR(ctx);
- if(ok != MAPCACHE_SUCCESS) {
- ctx->set_error(ctx,404,"dimension \"%s\" value \"%s\" fails to validate",
- dimension->name, value);
- if(kvp) ctx->set_exception(ctx,"InvalidParameterValue","%s",dimension->name);
- return;
- }
- /* re-set the eventually modified value in the dimension table */
- apr_table_set(dimtable,dimension->name,tmpval);
- }
- }
- }
-
if(!matrixset) {
ctx->set_error(ctx, 404, "received wmts request with no TILEMATRIXSET");
- if(kvp) ctx->set_exception(ctx,"MissingParameterValue","tilematrixset");
+ if(kvp) ctx->set_exception(ctx,"MissingParameterValue","TileMatrixSet");
return;
} else {
int i;
@@ -815,49 +776,49 @@ void _mapcache_service_wmts_parse_request(mapcache_context *ctx, mapcache_servic
}
if(!grid_link) {
ctx->set_error(ctx, 404, "received wmts request with invalid TILEMATRIXSET %s",matrixset);
- if(kvp) ctx->set_exception(ctx,"InvalidParameterValue","tilematrixset");
+ if(kvp) ctx->set_exception(ctx,"InvalidParameterValue","TileMatrixSet");
return;
}
}
if(!matrix) {
ctx->set_error(ctx, 404, "received wmts request with no TILEMATRIX");
- if(kvp) ctx->set_exception(ctx,"MissingParameterValue","tilematrix");
+ if(kvp) ctx->set_exception(ctx,"MissingParameterValue","TileMatrix");
return;
} else {
char *endptr;
level = (int)strtol(matrix,&endptr,10);
if(*endptr != 0 || level < grid_link->minz || level >= grid_link->maxz) {
ctx->set_error(ctx, 404, "received wmts request with invalid TILEMATRIX %s", matrix);
- if(kvp) ctx->set_exception(ctx,"InvalidParameterValue","tilematrix");
+ if(kvp) ctx->set_exception(ctx,"InvalidParameterValue","TileMatrix");
return;
}
}
if(!tilerow) {
ctx->set_error(ctx, 404, "received wmts request with no TILEROW");
- if(kvp) ctx->set_exception(ctx,"MissingParameterValue","tilerow");
+ if(kvp) ctx->set_exception(ctx,"MissingParameterValue","TileRow");
return;
} else {
char *endptr;
row = (int)strtol(tilerow,&endptr,10);
if(*endptr != 0 || row < 0) {
ctx->set_error(ctx, 404, "received wmts request with invalid TILEROW %s",tilerow);
- if(kvp) ctx->set_exception(ctx,"InvalidParameterValue","tilerow");
+ if(kvp) ctx->set_exception(ctx,"InvalidParameterValue","TileRow");
return;
}
}
if(!tilecol) {
ctx->set_error(ctx, 404, "received wmts request with no TILECOL");
- if(kvp) ctx->set_exception(ctx,"MissingParameterValue","tilecol");
+ if(kvp) ctx->set_exception(ctx,"MissingParameterValue","TileCol");
return;
} else {
char *endptr;
col = (int)strtol(tilecol,&endptr,10);
if(endptr == tilecol || col < 0) {
ctx->set_error(ctx, 404, "received wmts request with invalid TILECOL %s",tilecol);
- if(kvp) ctx->set_exception(ctx,"InvalidParameterValue","tilecol");
+ if(kvp) ctx->set_exception(ctx,"InvalidParameterValue","TileCol");
return;
}
}
@@ -880,17 +841,19 @@ void _mapcache_service_wmts_parse_request(mapcache_context *ctx, mapcache_servic
x = grid_link->grid->levels[level]->maxx - col - 1;
y = row;
break;
+ default:
+ ctx->set_error(ctx,500,"BUG: invalid grid origin");
+ return;
}
- if(fi_j || timedim) {
+ if(fi_j) {
//we need the extent of the request, compute it here
extent = apr_pcalloc(ctx->pool, sizeof(mapcache_extent));
- mapcache_grid_get_extent(ctx,grid_link->grid,x,y,level,extent);
+ mapcache_grid_get_tile_extent(ctx,grid_link->grid,x,y,level,extent);
}
if(!fi_j) { /*we have a getTile request*/
- int i;
#ifdef PEDANTIC_WMTS_FORMAT_CHECK
if(tileset->format) {
@@ -917,57 +880,46 @@ void _mapcache_service_wmts_parse_request(mapcache_context *ctx, mapcache_servic
ctx->pool,sizeof(mapcache_request_get_tile));
((mapcache_request*)req)->type = MAPCACHE_REQUEST_GET_TILE;
- if(timedim) {
- timedim_selected = mapcache_timedimension_get_entries_for_value(ctx,
- tileset->timedimension, tileset, grid_link->grid, extent, timedim);
- GC_CHECK_ERROR(ctx);
- if(!timedim_selected || timedim_selected->nelts == 0) {
- ctx->set_error(ctx, 404, "no matching entry for given TIME dimension");
- if(kvp) ctx->set_exception(ctx,"InvalidParameterValue","TIME");
- return;
- }
- req->ntiles = timedim_selected->nelts;
- } else {
- req->ntiles = 1;
- }
+ req->ntiles = 1;
req->tiles = (mapcache_tile**)apr_pcalloc(ctx->pool,req->ntiles * sizeof(mapcache_tile*));
- for(i=0;i<req->ntiles;i++) {
- req->tiles[i] = mapcache_tileset_tile_create(ctx->pool, tileset, grid_link);
- if(!req->tiles[i]) {
- ctx->set_error(ctx, 500, "failed to allocate tile");
- if(kvp) ctx->set_exception(ctx,"NoApplicableCode","");
- return;
- }
-
- /*populate dimensions*/
- if(tileset->dimensions) {
- int d;
- for(d=0; d<tileset->dimensions->nelts; d++) {
- mapcache_dimension *dimension = APR_ARRAY_IDX(tileset->dimensions,d,mapcache_dimension*);
- const char *value = apr_table_get(dimtable,dimension->name);
- if(value) {
- apr_table_set(req->tiles[i]->dimensions,dimension->name,value);
- }
- }
- }
- if(tileset->timedimension) {
- apr_table_set(req->tiles[i]->dimensions,tileset->timedimension->key,
- APR_ARRAY_IDX(timedim_selected,i,char*));
- }
+ req->tiles[0] = mapcache_tileset_tile_create(ctx->pool, tileset, grid_link);
+ if(!req->tiles[0]) {
+ ctx->set_error(ctx, 500, "failed to allocate tile");
+ if(kvp) ctx->set_exception(ctx,"NoApplicableCode","");
+ return;
+ }
- req->tiles[i]->z = level;
- req->tiles[i]->x = x;
- req->tiles[i]->y = y;
- if(i==0) {
- /* no need to validate all the tiles as they all have the same x,y,z */
- mapcache_tileset_tile_validate(ctx,req->tiles[0]);
- if(GC_HAS_ERROR(ctx)) {
- if(kvp) ctx->set_exception(ctx,"TileOutOfRange","");
- return;
+ /*populate dimensions*/
+ if(tileset->dimensions) {
+ int d;
+ for(d=0; d<tileset->dimensions->nelts; d++) {
+ mapcache_dimension *dimension = APR_ARRAY_IDX(tileset->dimensions,d,mapcache_dimension*);
+ const char *value = apr_table_get(dimtable,dimension->name);
+ if(value) {
+ mapcache_tile_set_requested_dimension(ctx,req->tiles[0],dimension->name,value);
}
}
}
+ req->tiles[0]->z = level;
+ req->tiles[0]->x = x;
+ req->tiles[0]->y = y;
+ /* no need to validate all the tiles as they all have the same x,y,z */
+ mapcache_tileset_tile_validate_z(ctx,req->tiles[0]);
+ if(GC_HAS_ERROR(ctx)) {
+ if(kvp) ctx->set_exception(ctx,"InvalidParameterValue","TileMatrix");
+ return;
+ }
+ mapcache_tileset_tile_validate_x(ctx,req->tiles[0]);
+ if(GC_HAS_ERROR(ctx)) {
+ if(kvp) ctx->set_exception(ctx,"TileOutOfRange","TileCol");
+ return;
+ }
+ mapcache_tileset_tile_validate_y(ctx,req->tiles[0]);
+ if(GC_HAS_ERROR(ctx)) {
+ if(kvp) ctx->set_exception(ctx,"TileOutOfRange","TileRow");
+ return;
+ }
*request = (mapcache_request*)req;
return;
@@ -1024,7 +976,7 @@ void _mapcache_service_wmts_parse_request(mapcache_context *ctx, mapcache_servic
mapcache_dimension *dimension = APR_ARRAY_IDX(tileset->dimensions,d,mapcache_dimension*);
const char *value = apr_table_get(dimtable,dimension->name);
if(value) {
- apr_table_set(fi->map.dimensions,dimension->name,value);
+ mapcache_map_set_requested_dimension(ctx,&fi->map,dimension->name,value);
}
}
}
diff --git a/lib/services.c b/lib/services.c
index 53e524b..3cea927 100644
--- a/lib/services.c
+++ b/lib/services.c
@@ -70,6 +70,15 @@ void mapcache_service_dispatch_request(mapcache_context *ctx, mapcache_request *
ctx->set_error(ctx,404,"unknown service %s",pathinfo);
}
+int mapcache_config_services_enabled(mapcache_context *ctx, mapcache_cfg *config) {
+ int count=0,i;
+ for(i=0; i<MAPCACHE_SERVICES_COUNT; i++) {
+ if(config->services[i])
+ count++;
+ }
+ return count;
+}
+
/** @} */
diff --git a/lib/source.c b/lib/source.c
index c5125c1..28870df 100644
--- a/lib/source.c
+++ b/lib/source.c
@@ -28,6 +28,7 @@
*****************************************************************************/
#include "mapcache.h"
+#include <apr_time.h>
@@ -36,6 +37,60 @@ void mapcache_source_init(mapcache_context *ctx, mapcache_source *source)
mapcache_extent tmp_extent = {-1,-1,-1,-1};
source->data_extent = tmp_extent;
source->metadata = apr_table_make(ctx->pool,3);
+ source->retry_count = 1;
+ source->retry_delay = 0.1;
+}
+
+
+void mapcache_source_render_map(mapcache_context *ctx, mapcache_source *source, mapcache_map *map) {
+ int i;
+#ifdef DEBUG
+ ctx->log(ctx, MAPCACHE_DEBUG, "calling render_map on source (%s): tileset=%s, grid=%s, extent=(%f,%f,%f,%f)",
+ source->name, map->tileset->name, map->grid_link->grid->name,
+ map->extent.minx, map->extent.miny, map->extent.maxx, map->extent.maxy);
+#endif
+ for(i=0;i<=source->retry_count;i++) {
+ if(i) { /* not our first try */
+ ctx->log(ctx, MAPCACHE_INFO, "source (%s) render_map retry %d of %d. previous try returned error: %s",
+ source->name, i, source->retry_count, ctx->get_error_message(ctx));
+ ctx->clear_errors(ctx);
+ if(source->retry_delay > 0) {
+ double wait = source->retry_delay;
+ int j = 0;
+ for(j=1;j<i;j++) /* sleep twice as long as before previous retry */
+ wait *= 2;
+ apr_sleep((int)(wait*1000000)); /* apr_sleep expects microseconds */
+ }
+ }
+ source->_render_map(ctx, source, map);
+ if(!GC_HAS_ERROR(ctx))
+ break;
+ }
+}
+
+void mapcache_source_query_info(mapcache_context *ctx, mapcache_source *source, mapcache_feature_info *fi) {
+ int i;
+#ifdef DEBUG
+ ctx->log(ctx, MAPCACHE_DEBUG, "calling query_info on source (%s): tileset=%s, grid=%s,",
+ source->name, fi->map.tileset->name, fi->map.grid_link->grid->name);
+#endif
+ for(i=0;i<=source->retry_count;i++) {
+ if(i) { /* not our first try */
+ ctx->log(ctx, MAPCACHE_INFO, "source (%s) render_map retry %d of %d. previous try returned error: %s",
+ source->name, i, source->retry_count, ctx->get_error_message(ctx));
+ ctx->clear_errors(ctx);
+ if(source->retry_delay > 0) {
+ double wait = source->retry_delay;
+ int j = 0;
+ for(j=1;j<i;j++) /* sleep twice as long as before previous retry */
+ wait *= 2;
+ apr_sleep((int)(wait*1000000)); /* apr_sleep expects microseconds */
+ }
+ }
+ source->_query_info(ctx, source, fi);
+ if(!GC_HAS_ERROR(ctx))
+ break;
+ }
}
/* vim: ts=2 sts=2 et sw=2
*/
diff --git a/lib/source_dummy.c b/lib/source_dummy.c
index 1646e5f..d0aee2f 100644
--- a/lib/source_dummy.c
+++ b/lib/source_dummy.c
@@ -33,11 +33,18 @@
#include <apr_tables.h>
#include <apr_strings.h>
+typedef struct mapcache_source_dummy mapcache_source_dummy;
+struct mapcache_source_dummy {
+ mapcache_source source;
+ char *mapfile;
+ void *mapobj;
+};
+
/**
* \private \memberof mapcache_source_dummy
* \sa mapcache_source::render_map()
*/
-void _mapcache_source_dummy_render_map(mapcache_context *ctx, mapcache_map *map)
+void _mapcache_source_dummy_render_map(mapcache_context *ctx, mapcache_source *psource, mapcache_map *map)
{
map->raw_image = mapcache_image_create(ctx);
map->raw_image->w = map->width;
@@ -48,7 +55,7 @@ void _mapcache_source_dummy_render_map(mapcache_context *ctx, mapcache_map *map)
apr_pool_cleanup_register(ctx->pool, map->raw_image->data,(void*)free, apr_pool_cleanup_null);
}
-void _mapcache_source_dummy_query(mapcache_context *ctx, mapcache_feature_info *fi)
+void _mapcache_source_dummy_query(mapcache_context *ctx, mapcache_source *psource, mapcache_feature_info *fi)
{
ctx->set_error(ctx,500,"dummy source does not support queries");
}
@@ -57,7 +64,7 @@ void _mapcache_source_dummy_query(mapcache_context *ctx, mapcache_feature_info *
* \private \memberof mapcache_source_dummy
* \sa mapcache_source::configuration_parse()
*/
-void _mapcache_source_dummy_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_source *source)
+void _mapcache_source_dummy_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_source *source, mapcache_cfg *config)
{
}
@@ -79,10 +86,10 @@ mapcache_source* mapcache_source_dummy_create(mapcache_context *ctx)
}
mapcache_source_init(ctx, &(source->source));
source->source.type = MAPCACHE_SOURCE_DUMMY;
- source->source.render_map = _mapcache_source_dummy_render_map;
+ source->source._render_map = _mapcache_source_dummy_render_map;
source->source.configuration_check = _mapcache_source_dummy_configuration_check;
source->source.configuration_parse_xml = _mapcache_source_dummy_configuration_parse_xml;
- source->source.query_info = _mapcache_source_dummy_query;
+ source->source._query_info = _mapcache_source_dummy_query;
return (mapcache_source*)source;
}
diff --git a/lib/source_fallback.c b/lib/source_fallback.c
new file mode 100644
index 0000000..f06c2a5
--- /dev/null
+++ b/lib/source_fallback.c
@@ -0,0 +1,165 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project: MapServer
+ * Purpose: MapCache tile caching support file: Mapserver Mapfile datasource
+ * Author: Thomas Bonfort and the MapServer team.
+ *
+ ******************************************************************************
+ * Copyright (c) 1996-2011 Regents of the University of Minnesota.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies of this Software or works derived from this Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *****************************************************************************/
+
+
+#include "mapcache.h"
+#include "ezxml.h"
+#include <apr_tables.h>
+#include <apr_strings.h>
+
+typedef struct mapcache_source_fallback mapcache_source_fallback;
+struct mapcache_source_fallback {
+ mapcache_source source;
+ apr_array_header_t *sources;
+};
+
+/**
+ * \private \memberof mapcache_source_fallback
+ * \sa mapcache_source::render_map()
+ */
+void _mapcache_source_fallback_render_map(mapcache_context *ctx, mapcache_source *psource, mapcache_map *map)
+{
+ mapcache_source_fallback *source = (mapcache_source_fallback*)psource;
+ mapcache_source *subsource;
+ int i;
+ subsource = APR_ARRAY_IDX(source->sources,0,mapcache_source*);
+ mapcache_source_render_map(ctx, subsource, map);
+
+ if(GC_HAS_ERROR(ctx)) {
+ int first_error = ctx->get_error(ctx);
+ char *first_error_message = ctx->get_error_message(ctx);
+ ctx->log(ctx,MAPCACHE_INFO,
+ "failed render on primary source \"%s\" on tileset \"%s\". Falling back on secondary sources",
+ subsource->name,map->tileset->name);
+ ctx->clear_errors(ctx);
+ for(i=1; i<source->sources->nelts; i++) {
+ subsource = APR_ARRAY_IDX(source->sources,i,mapcache_source*);
+ mapcache_source_render_map(ctx, subsource, map);
+ if(GC_HAS_ERROR(ctx)) {
+ ctx->log(ctx,MAPCACHE_INFO,
+ "failed render on fallback source \"%s\" of tileset \"%s\". Continuing with other fallback sources if available",
+ subsource->name,map->tileset->name);
+ ctx->clear_errors(ctx);
+ continue;
+ } else {
+ return;
+ }
+ }
+ /* all backends failed, return primary error message */
+ ctx->set_error(ctx,first_error,first_error_message);
+ return;
+ }
+}
+
+void _mapcache_source_fallback_query(mapcache_context *ctx, mapcache_source *psource, mapcache_feature_info *fi)
+{
+ mapcache_source_fallback *source = (mapcache_source_fallback*)psource;
+ mapcache_source *subsource;
+ int i;
+ subsource = APR_ARRAY_IDX(source->sources,0,mapcache_source*);
+ mapcache_source_query_info(ctx, subsource, fi);
+
+ if(GC_HAS_ERROR(ctx)) {
+ int first_error = ctx->get_error(ctx);
+ char *first_error_message = ctx->get_error_message(ctx);
+ ctx->log(ctx,MAPCACHE_INFO,
+ "failed query_info on primary source \"%s\" on tileset \"%s\". Falling back on secondary sources",
+ subsource->name,fi->map.tileset->name);
+ ctx->clear_errors(ctx);
+ for(i=1; i<source->sources->nelts; i++) {
+ subsource = APR_ARRAY_IDX(source->sources,i,mapcache_source*);
+ mapcache_source_query_info(ctx, subsource, fi);
+ if(GC_HAS_ERROR(ctx)) {
+ ctx->log(ctx,MAPCACHE_INFO,
+ "failed query_info on fallback source \"%s\" of tileset \"%s\". Continuing with other fallback sources if available",
+ subsource->name,fi->map.tileset->name);
+ ctx->clear_errors(ctx);
+ continue;
+ } else {
+ return;
+ }
+ }
+ /* all backends failed, return primary error message */
+ ctx->set_error(ctx,first_error,first_error_message);
+ return;
+ }
+ ctx->set_error(ctx,500,"fallback source does not support queries");
+}
+
+/**
+ * \private \memberof mapcache_source_fallback
+ * \sa mapcache_source::configuration_parse()
+ */
+void _mapcache_source_fallback_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_source *psource, mapcache_cfg *config)
+{
+ ezxml_t cur_node;
+ mapcache_source_fallback *source = (mapcache_source_fallback*)psource;
+ source->sources = apr_array_make(ctx->pool,3,sizeof(mapcache_source*));
+ for(cur_node = ezxml_child(node,"source"); cur_node; cur_node = cur_node->next) {
+ mapcache_source *refsource = mapcache_configuration_get_source(config, cur_node->txt);
+ if(!refsource) {
+ ctx->set_error(ctx, 400, "fallback source \"%s\" references source \"%s\","
+ " but it is not configured (hint:referenced sources must be declared before this fallback source in the xml file)", psource->name, cur_node->txt);
+ return;
+ }
+ APR_ARRAY_PUSH(source->sources,mapcache_source*) = refsource;
+ }
+ if(source->sources->nelts == 0) {
+ ctx->set_error(ctx,400,"fallback source \"%s\" does not reference any child sources", psource->name);
+ }
+}
+
+/**
+ * \private \memberof mapcache_source_fallback
+ * \sa mapcache_source::configuration_check()
+ */
+void _mapcache_source_fallback_configuration_check(mapcache_context *ctx, mapcache_cfg *cfg,
+ mapcache_source *source)
+{
+}
+
+mapcache_source* mapcache_source_fallback_create(mapcache_context *ctx)
+{
+ mapcache_source_fallback *source = apr_pcalloc(ctx->pool, sizeof(mapcache_source_fallback));
+ if(!source) {
+ ctx->set_error(ctx, 500, "failed to allocate fallback source");
+ return NULL;
+ }
+ mapcache_source_init(ctx, &(source->source));
+ source->source.type = MAPCACHE_SOURCE_FALLBACK;
+ source->source._render_map = _mapcache_source_fallback_render_map;
+ source->source.configuration_check = _mapcache_source_fallback_configuration_check;
+ source->source.configuration_parse_xml = _mapcache_source_fallback_configuration_parse_xml;
+ source->source._query_info = _mapcache_source_fallback_query;
+ return (mapcache_source*)source;
+}
+
+
+/* vim: ts=2 sts=2 et sw=2
+*/
diff --git a/lib/source_gdal.c b/lib/source_gdal.c
index dbef153..440d10d 100644
--- a/lib/source_gdal.c
+++ b/lib/source_gdal.c
@@ -3,10 +3,11 @@
*
* Project: MapServer
* Purpose: MapCache tile caching support file: GDAL datasource support (incomplete and disabled)
- * Author: Thomas Bonfort and the MapServer team.
+ * Author: Thomas Bonfort, Even Rouault and the MapServer team.
*
******************************************************************************
* Copyright (c) 1996-2011 Regents of the University of Minnesota.
+ * Copyright (c) 2004, Frank Warmerdam <warmerdam at pobox.com> (for GDALAutoCreateWarpedVRT who CreateWarpedVRT is derived from)
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@@ -37,190 +38,573 @@
#include <gdal.h>
#include <cpl_conv.h>
-#include "gdal_alg.h"
+#include "gdalwarper.h"
#include "cpl_string.h"
#include "ogr_srs_api.h"
+#include "gdal_vrt.h"
-/**
- * \private \memberof mapcache_source_gdal
- * \sa mapcache_source::render_metatile()
- */
-void _mapcache_source_gdal_render_metatile(mapcache_context *ctx, mapcache_metatile *tile)
-{
- mapcache_source_gdal *gdal = (mapcache_source_gdal*)tile->tile.tileset->source;
- char *srcSRS = "", *dstSRS;
- mapcache_buffer *data = mapcache_buffer_create(0,ctx->pool);
- GC_CHECK_ERROR(ctx);
- GDALDatasetH hDataset;
+#define MAPCACHE_DEFAULT_RESAMPLE_ALG GRA_Bilinear
- GDALAllRegister();
- OGRSpatialReferenceH hSRS;
- CPLErrorReset();
+/* Note: this will also work with GDAL >= 2.0 */
+#if GDAL_VERSION_MAJOR < 2
+#define USE_PRE_GDAL2_METHOD
+#endif
+/*#define USE_PRE_GDAL2_METHOD*/
- hSRS = OSRNewSpatialReference( NULL );
- if( OSRSetFromUserInput( hSRS, tile->tile.grid->srs ) == OGRERR_NONE )
- OSRExportToWkt( hSRS, &dstSRS );
- else {
- ctx->set_error(ctx,MAPCACHE_SOURCE_GDAL_ERROR,"failed to parse gdal srs %s",tile->tile.grid->srs);
- return;
- }
+typedef struct mapcache_source_gdal mapcache_source_gdal;
- OSRDestroySpatialReference( hSRS );
+/**\class mapcache_source_gdal
+ * \brief GDAL mapcache_source
+ * \implements mapcache_source
+ */
+struct mapcache_source_gdal {
+ mapcache_source source;
+ const char *datastr; /**< the gdal source string*/
+ const char *srs_wkt;
+ GDALResampleAlg eResampleAlg; /**< resampling algorithm */
+ const char *srcOvrLevel; /**< strategy to pickup source overview: AUTO, NONE,
+ AUTO-xxx, xxxx. See -ovr doc in http://www.gdal.org/gdalwarp.html.
+ Only used for GDAL >= 2.0 (could probably be made to work for USE_PRE_GDAL2_METHOD with more work) */
+ int bUseConnectionPool;
+};
+
+typedef struct {
+ mapcache_source_gdal *gdal;
+ const char *gdal_data;
+ const char *dst_srs;
+} gdal_connection_params;
+
+typedef struct {
+ GDALDatasetH hSrcDS;
+ char *dst_srs_wkt;
+} gdal_connection;
+
+void mapcache_source_gdal_connection_constructor(mapcache_context *ctx, void **conn_, void *params) {
+ gdal_connection_params *p = (gdal_connection_params*)params;
+ gdal_connection *c = malloc(sizeof(gdal_connection));
+ OGRSpatialReferenceH hDstSRS;
+
+ *conn_ = NULL;
+ /* -------------------------------------------------------------------- */
+ /* Open source dataset. */
+ /* -------------------------------------------------------------------- */
+ c->hSrcDS = GDALOpen( p->gdal_data, GA_ReadOnly );
- hDataset = GDALOpen( gdal->datastr, GA_ReadOnly );
- if( hDataset == NULL ) {
- ctx->set_error(ctx,MAPCACHE_SOURCE_GDAL_ERROR,"GDAL failed to open %s",gdal->datastr);
+ if( c->hSrcDS == NULL ) {
+ ctx->set_error(ctx, 500, "Cannot open gdal source for %s .\n", p->gdal->source.name );
+ free(c);
return;
}
/* -------------------------------------------------------------------- */
- /* Check that there's at least one raster band */
+ /* Check that there's 3 or 4 raster bands. */
/* -------------------------------------------------------------------- */
- if ( GDALGetRasterCount(hDataset) == 0 ) {
- ctx->set_error(ctx,MAPCACHE_SOURCE_GDAL_ERROR,"raster %s has no bands",gdal->datastr);
+ if ( GDALGetRasterCount(c->hSrcDS) != 3 && GDALGetRasterCount(c->hSrcDS) != 4) {
+ ctx->set_error(ctx, 500, "Input gdal source for %s has %d raster bands, but only 3 or 4 are supported.\n",
+ p->gdal->source.name, GDALGetRasterCount(c->hSrcDS) );
+ GDALClose(c->hSrcDS);
+ free(c);
return;
}
- if( GDALGetProjectionRef( hDataset ) != NULL
- && strlen(GDALGetProjectionRef( hDataset )) > 0 )
- srcSRS = apr_pstrdup(ctx->pool,GDALGetProjectionRef( hDataset ));
- else if( GDALGetGCPProjection( hDataset ) != NULL
- && strlen(GDALGetGCPProjection(hDataset)) > 0
- && GDALGetGCPCount( hDataset ) > 1 )
- srcSRS = apr_pstrdup(ctx->pool,GDALGetGCPProjection( hDataset ));
- GDALDriverH hDriver = GDALGetDriverByName( "MEM" );
- GDALDatasetH hDstDS;
- /* -------------------------------------------------------------------- */
- /* Create a transformation object from the source to */
- /* destination coordinate system. */
- /* -------------------------------------------------------------------- */
- void *hTransformArg =
- GDALCreateGenImgProjTransformer( hDataset, srcSRS,
- NULL, dstSRS,
- TRUE, 1000.0, 0 );
-
- if( hTransformArg == NULL ) {
- ctx->set_error(ctx,MAPCACHE_SOURCE_GDAL_ERROR,"gdal failed to create SRS transformation object");
+ hDstSRS = OSRNewSpatialReference( NULL );
+ if( OSRSetFromUserInput( hDstSRS, p->dst_srs ) == OGRERR_NONE ) {
+ c->dst_srs_wkt = NULL;
+ OSRExportToWkt( hDstSRS, &c->dst_srs_wkt );
+ }
+ else {
+ ctx->set_error(ctx,500,"failed to parse gdal srs %s",p->dst_srs);
+ GDALClose(c->hSrcDS);
+ free(c);
return;
}
- /* -------------------------------------------------------------------- */
- /* Get approximate output definition. */
- /* -------------------------------------------------------------------- */
- int nPixels, nLines;
- double adfDstGeoTransform[6];
- if( GDALSuggestedWarpOutput( hDataset,
- GDALGenImgProjTransform, hTransformArg,
- adfDstGeoTransform, &nPixels, &nLines )
- != CE_None ) {
- ctx->set_error(ctx,MAPCACHE_SOURCE_GDAL_ERROR,"gdal failed to create suggested warp output");
- return;
+ OSRDestroySpatialReference( hDstSRS );
+ *conn_ = c;
+}
+
+void mapcache_source_gdal_connection_destructor(void *conn_) {
+ gdal_connection *c = (gdal_connection*)conn_;
+ CPLFree(c->dst_srs_wkt);
+ GDALClose(c->hSrcDS);
+ free(c);
+}
+
+static mapcache_pooled_connection* _gdal_get_connection(mapcache_context *ctx, mapcache_source_gdal *gdal, const char *dst_srs, const char *gdal_data)
+{
+ mapcache_pooled_connection *pc;
+ gdal_connection_params params;
+ char *key;
+
+ params.gdal = gdal;
+ params.dst_srs = dst_srs;
+ params.gdal_data = gdal_data;
+
+ key = apr_pstrcat(ctx->pool, gdal_data, dst_srs, NULL);
+
+ pc = mapcache_connection_pool_get_connection(ctx,key,mapcache_source_gdal_connection_constructor,
+ mapcache_source_gdal_connection_destructor, ¶ms);
+ return pc;
+}
+#ifdef USE_PRE_GDAL2_METHOD
+/* Creates a (virtual) dataset that matches an overview level of the source
+ dataset. This dataset has references on the source dataset, so it should
+ be closed before it.
+*/
+static GDALDatasetH CreateOverviewVRTDataset(GDALDatasetH hSrcDS,
+ int iOvrLevel)
+{
+ double adfSrcGeoTransform[6];
+ int iBand;
+ GDALRasterBandH hFirstBand = GDALGetRasterBand(hSrcDS, 1);
+ GDALRasterBandH hOvr = GDALGetOverview( hFirstBand, iOvrLevel );
+ int nFullResXSize = GDALGetRasterBandXSize(hFirstBand);
+ int nFullResYSize = GDALGetRasterBandYSize(hFirstBand);
+ int nVRTXSize = GDALGetRasterBandXSize(hOvr);
+ int nVRTYSize = GDALGetRasterBandYSize(hOvr);
+
+ VRTDatasetH hVRTDS = VRTCreate( nVRTXSize, nVRTYSize );
+
+ /* Scale geotransform */
+ if( GDALGetGeoTransform( hSrcDS, adfSrcGeoTransform) == CE_None )
+ {
+ adfSrcGeoTransform[1] *= (double)nFullResXSize / nVRTXSize;
+ adfSrcGeoTransform[2] *= (double)nFullResYSize / nVRTYSize;
+ adfSrcGeoTransform[4] *= (double)nFullResXSize / nVRTXSize;
+ adfSrcGeoTransform[5] *= (double)nFullResYSize / nVRTYSize;
+ GDALSetProjection( hVRTDS, GDALGetProjectionRef(hSrcDS) );
+ GDALSetGeoTransform( hVRTDS, adfSrcGeoTransform );
}
- GDALDestroyGenImgProjTransformer( hTransformArg );
- double dfXRes = (tile->bbox[2] - tile->bbox[0]) / tile->sx;
- double dfYRes = (tile->bbox[3] - tile->bbox[1]) / tile->sy;
+ /* Scale GCPs */
+ if( GDALGetGCPCount(hSrcDS) > 0 )
+ {
+ const GDAL_GCP* pasGCPsMain = GDALGetGCPs(hSrcDS);
+ int nGCPCount = GDALGetGCPCount(hSrcDS);
+ GDAL_GCP* pasGCPList = GDALDuplicateGCPs( nGCPCount, pasGCPsMain );
+ int i;
+ for(i = 0; i < nGCPCount; i++)
+ {
+ pasGCPList[i].dfGCPPixel *= (double)nVRTXSize / nFullResXSize;
+ pasGCPList[i].dfGCPLine *= (double)nVRTYSize / nFullResYSize;
+ }
+ GDALSetGCPs( hVRTDS, nGCPCount, pasGCPList, GDALGetGCPProjection(hSrcDS) );
+ GDALDeinitGCPs( nGCPCount, pasGCPList );
+ CPLFree( pasGCPList );
+ }
- adfDstGeoTransform[0] = tile->bbox[0];
- adfDstGeoTransform[3] = tile->bbox[3];
- adfDstGeoTransform[1] = dfXRes;
- adfDstGeoTransform[5] = -dfYRes;
- hDstDS = GDALCreate( hDriver, "tempd_gdal_image", tile->sx, tile->sy, 4, GDT_Byte, NULL );
+ /* Create bands */
+ for(iBand = 1; iBand <= GDALGetRasterCount(hSrcDS); iBand++ )
+ {
+ GDALRasterBandH hSrcBand;
+ GDALRasterBandH hVRTBand;
+ int bNoDataSet = FALSE;
+ double dfNoData;
+
+ VRTAddBand( hVRTDS, GDT_Byte, NULL );
+ hVRTBand = GDALGetRasterBand(hVRTDS, iBand);
+ hSrcBand = GDALGetOverview(GDALGetRasterBand(hSrcDS, iBand), iOvrLevel);
+ dfNoData = GDALGetRasterNoDataValue(hSrcBand, &bNoDataSet);
+ if( bNoDataSet )
+ GDALSetRasterNoDataValue(hVRTBand, dfNoData);
+
+ /* Note: the consumer of this VRT is the warper, which doesn't do any */
+ /* subsampled RasterIO requests, so NEAR is fine */
+ VRTAddSimpleSource( hVRTBand, hSrcBand,
+ 0, 0, nVRTXSize, nVRTYSize,
+ 0, 0, nVRTXSize, nVRTYSize,
+ "NEAR", VRT_NODATA_UNSET );
+ }
- /* -------------------------------------------------------------------- */
- /* Write out the projection definition. */
- /* -------------------------------------------------------------------- */
- GDALSetProjection( hDstDS, dstSRS );
- GDALSetGeoTransform( hDstDS, adfDstGeoTransform );
- char **papszWarpOptions = NULL;
- papszWarpOptions = CSLSetNameValue( papszWarpOptions, "INIT", "0" );
+ return hVRTDS;
+}
+#endif
+/* Derived from GDALAutoCreateWarpedVRT(), with various improvements. */
+/* Returns a warped VRT that covers the passed extent, in pszDstWKT. */
+/* The provided width and height are used, but the size of the returned dataset */
+/* may not match those values. In the USE_PRE_GDAL2_METHOD, it should match them. */
+/* In the non USE_PRE_GDAL2_METHOD case, it might be a multiple of those values. */
+/* phTmpDS is an output parameter (a temporary VRT in the USE_PRE_GDAL2_METHOD case). */
+static GDALDatasetH
+CreateWarpedVRT( GDALDatasetH hSrcDS,
+ const char *pszSrcWKT,
+ const char *pszDstWKT,
+ int width, int height,
+ const mapcache_extent *extent,
+ GDALResampleAlg eResampleAlg,
+ double dfMaxError,
+ char** papszWarpOptions,
+ GDALDatasetH *phTmpDS )
+{
+ int i;
+ GDALWarpOptions *psWO;
+ double adfDstGeoTransform[6];
+ GDALDatasetH hDstDS;
+ int nDstPixels, nDstLines;
+ CPLErr eErr;
+ int bHaveNodata = FALSE;
+ char** papszOptions = NULL;
+
+/* -------------------------------------------------------------------- */
+/* Populate the warp options. */
+/* -------------------------------------------------------------------- */
+
+ psWO = GDALCreateWarpOptions();
+ psWO->papszWarpOptions = CSLDuplicate(papszWarpOptions);
+
+ psWO->eResampleAlg = eResampleAlg;
+
+ psWO->hSrcDS = hSrcDS;
+
+ psWO->nBandCount = GDALGetRasterCount( hSrcDS );
+ if( psWO->nBandCount == 4 )
+ {
+ psWO->nBandCount = 3;
+ psWO->nSrcAlphaBand = 4;
+ psWO->nDstAlphaBand = 4;
+ }
+ /* Due to the reprojection, we might get transparency in the edges */
+ else if( psWO->nBandCount == 3 )
+ psWO->nDstAlphaBand = 4;
- /* -------------------------------------------------------------------- */
- /* Create a transformation object from the source to */
- /* destination coordinate system. */
- /* -------------------------------------------------------------------- */
- GDALTransformerFunc pfnTransformer = NULL;
- void *hGenImgProjArg=NULL, *hApproxArg=NULL;
- hTransformArg = hGenImgProjArg =
- GDALCreateGenImgProjTransformer( hDataset, srcSRS,
- hDstDS, dstSRS,
- TRUE, 1000.0, 0 );
+ psWO->panSrcBands = (int*)CPLMalloc( sizeof(int) * psWO->nBandCount );
+ psWO->panDstBands = (int*)CPLMalloc( sizeof(int) * psWO->nBandCount );
- if( hTransformArg == NULL )
- exit( 1 );
+ for( i = 0; i < psWO->nBandCount; i++ )
+ {
+ psWO->panSrcBands[i] = i+1;
+ psWO->panDstBands[i] = i+1;
+ }
- pfnTransformer = GDALGenImgProjTransform;
+/* -------------------------------------------------------------------- */
+/* Set nodata values if existing */
+/* -------------------------------------------------------------------- */
+ GDALGetRasterNoDataValue( GDALGetRasterBand(hSrcDS, 1), &bHaveNodata);
+ if( bHaveNodata )
+ {
+ psWO->padfSrcNoDataReal = (double *)
+ CPLMalloc(psWO->nBandCount*sizeof(double));
+ psWO->padfSrcNoDataImag = (double *)
+ CPLCalloc(psWO->nBandCount, sizeof(double)); /* zero initialized */
+
+ for( i = 0; i < psWO->nBandCount; i++ )
+ {
+ GDALRasterBandH hBand = GDALGetRasterBand( hSrcDS, i+1 );
+
+ double dfReal = GDALGetRasterNoDataValue( hBand, &bHaveNodata );
+
+ if( bHaveNodata )
+ {
+ psWO->padfSrcNoDataReal[i] = dfReal;
+ }
+ else
+ {
+ psWO->padfSrcNoDataReal[i] = -123456.789;
+ }
+ }
+ }
- hTransformArg = hApproxArg =
- GDALCreateApproxTransformer( GDALGenImgProjTransform,
- hGenImgProjArg, 0.125 );
- pfnTransformer = GDALApproxTransform;
+/* -------------------------------------------------------------------- */
+/* Create the transformer. */
+/* -------------------------------------------------------------------- */
+ psWO->pfnTransformer = GDALGenImgProjTransform;
+ psWO->pTransformerArg =
+ GDALCreateGenImgProjTransformer( psWO->hSrcDS, pszSrcWKT,
+ NULL, pszDstWKT,
+ TRUE, 1.0, 0 );
+
+ if( psWO->pTransformerArg == NULL )
+ {
+ GDALDestroyWarpOptions( psWO );
+ return NULL;
+ }
- /* -------------------------------------------------------------------- */
- /* Now actually invoke the warper to do the work. */
- /* -------------------------------------------------------------------- */
- GDALSimpleImageWarp( hDataset, hDstDS, 0, NULL,
- pfnTransformer, hTransformArg,
- GDALDummyProgress, NULL, papszWarpOptions );
+/* -------------------------------------------------------------------- */
+/* Figure out the desired output bounds and resolution. */
+/* -------------------------------------------------------------------- */
+ eErr =
+ GDALSuggestedWarpOutput( hSrcDS, psWO->pfnTransformer,
+ psWO->pTransformerArg,
+ adfDstGeoTransform, &nDstPixels, &nDstLines );
+ if( eErr != CE_None )
+ {
+ GDALDestroyTransformer( psWO->pTransformerArg );
+ GDALDestroyWarpOptions( psWO );
+ return NULL;
+ }
- CSLDestroy( papszWarpOptions );
+/* -------------------------------------------------------------------- */
+/* To minimize the risk of extra resampling done by generic */
+/* RasterIO itself and maximize resampling done in the wraper, */
+/* adjust the resolution so that the overview factor of the output */
+/* dataset that will indirectly query matches an exiting overview */
+/* factor of the input dataset. */
+/* -------------------------------------------------------------------- */
+ {
+ double dfDesiredXRes = (extent->maxx - extent->minx) / width;
+ double dfDesiredYRes = (extent->maxy - extent->miny) / height;
+ double dfDesiredRes = MIN( dfDesiredXRes, dfDesiredYRes );
+ double dfGuessedFullRes = MIN( adfDstGeoTransform[1],
+ fabs(adfDstGeoTransform[5]) );
+ double dfApproxDstOvrRatio = dfDesiredRes / dfGuessedFullRes;
+
+ GDALRasterBandH hFirstBand = GDALGetRasterBand(hSrcDS, 1);
+ int nOvrCount = GDALGetOverviewCount(hFirstBand);
+ int nSrcXSize = GDALGetRasterBandXSize(hFirstBand);
+ int i;
+ double dfSrcOvrRatio = 1.0;
+#ifdef USE_PRE_GDAL2_METHOD
+ int iSelectedOvr = -1;
+#endif
+ for( i = 0; *phTmpDS == NULL && i < nOvrCount; i ++)
+ {
+ GDALRasterBandH hOvr = GDALGetOverview(hFirstBand, i);
+ int nOvrXSize = GDALGetRasterBandXSize(hOvr);
+ double dfCurOvrRatio = (double)nSrcXSize / nOvrXSize;
+ if(dfCurOvrRatio > dfApproxDstOvrRatio+0.1 ) /* +0.1 to avoid rounding issues */
+ {
+ break;
+ }
+ dfSrcOvrRatio = dfCurOvrRatio;
+#ifdef USE_PRE_GDAL2_METHOD
+ iSelectedOvr = i;
+#endif
+ }
+
+#ifdef USE_PRE_GDAL2_METHOD
+ if( iSelectedOvr >= 0 )
+ {
+ GDALDestroyTransformer( psWO->pTransformerArg );
+ GDALDestroyWarpOptions( psWO );
+
+ *phTmpDS = CreateOverviewVRTDataset(hSrcDS, iSelectedOvr);
+ return CreateWarpedVRT( *phTmpDS,
+ pszSrcWKT,
+ pszDstWKT,
+ width, height,
+ extent,
+ eResampleAlg,
+ dfMaxError,
+ papszWarpOptions,
+ phTmpDS );
+ }
+#endif
- if( hApproxArg != NULL )
- GDALDestroyApproxTransformer( hApproxArg );
+ adfDstGeoTransform[1] = dfDesiredXRes / dfSrcOvrRatio;
+ adfDstGeoTransform[5] = -dfDesiredYRes / dfSrcOvrRatio;
+ }
- if( hGenImgProjArg != NULL )
- GDALDestroyGenImgProjTransformer( hGenImgProjArg );
+/* -------------------------------------------------------------------- */
+/* Compute geotransform and raster dimension for our extent of */
+/* interest. */
+/* -------------------------------------------------------------------- */
+ adfDstGeoTransform[0] = extent->minx;
+ adfDstGeoTransform[2] = 0.0;
+ adfDstGeoTransform[3] = extent->maxy;
+ adfDstGeoTransform[4] = 0.0;
+ nDstPixels = (int)( (extent->maxx - extent->minx) / adfDstGeoTransform[1] + 0.5 );
+ nDstLines = (int)( (extent->maxy - extent->miny) / fabs(adfDstGeoTransform[5]) + 0.5 );
+ /*printf("nDstPixels=%d nDstLines=%d\n", nDstPixels, nDstLines);*/
+
+/* -------------------------------------------------------------------- */
+/* Update the transformer to include an output geotransform */
+/* back to pixel/line coordinates. */
+/* -------------------------------------------------------------------- */
+
+ GDALSetGenImgProjTransformerDstGeoTransform(
+ psWO->pTransformerArg, adfDstGeoTransform );
+
+/* -------------------------------------------------------------------- */
+/* Do we want to apply an approximating transformation? */
+/* -------------------------------------------------------------------- */
+ if( dfMaxError > 0.0 )
+ {
+ psWO->pTransformerArg =
+ GDALCreateApproxTransformer( psWO->pfnTransformer,
+ psWO->pTransformerArg,
+ dfMaxError );
+ psWO->pfnTransformer = GDALApproxTransform;
+ GDALApproxTransformerOwnsSubtransformer(psWO->pTransformerArg, TRUE);
+ }
- if(GDALGetRasterCount(hDstDS) != 4) {
- ctx->set_error(ctx,MAPCACHE_SOURCE_GDAL_ERROR,"gdal did not create a 4 band image");
- return;
+/* -------------------------------------------------------------------- */
+/* Create the VRT file. */
+/* -------------------------------------------------------------------- */
+
+ /* We could potentially used GDALCreateWarpedVRT() instead of this logic */
+ /* but GDALCreateWarpedVRT() in GDAL < 2.0.1 doesn't create the destination */
+ /* alpha band. */
+
+ papszOptions = CSLSetNameValue(NULL, "SUBCLASS", "VRTWarpedDataset");
+ hDstDS = GDALCreate( GDALGetDriverByName("VRT"), "", nDstPixels, nDstLines,
+ psWO->nBandCount + ((psWO->nDstAlphaBand != 0) ? 1 : 0),
+ GDT_Byte, papszOptions );
+ CSLDestroy(papszOptions);
+ if( hDstDS == NULL )
+ {
+ GDALDestroyWarpOptions( psWO );
+ return NULL;
+ }
+
+ psWO->hDstDS = hDstDS;
+
+ GDALSetGeoTransform( hDstDS, adfDstGeoTransform );
+ if( GDALInitializeWarpedVRT( hDstDS, psWO ) != CE_None )
+ {
+ GDALClose(hDstDS);
+ GDALDestroyWarpOptions( psWO );
+ return NULL;
+ }
+
+ GDALDestroyWarpOptions( psWO );
+
+ return hDstDS;
+}
+
+
+/**
+ * \private \memberof mapcache_source_gdal
+ * \sa mapcache_source::render_metatile()
+ */
+void _mapcache_source_gdal_render_metatile(mapcache_context *ctx, mapcache_source *psource, mapcache_map *map)
+{
+ mapcache_source_gdal *gdal = (mapcache_source_gdal*)psource;
+ gdal_connection *gdal_conn;
+ GDALDatasetH hDstDS;
+ GDALDatasetH hTmpDS = NULL;
+ mapcache_buffer *data;
+ unsigned char *rasterdata;
+ CPLErr eErr;
+ mapcache_pooled_connection *pc = NULL;
+ int bands_bgra[] = { 3, 2, 1, 4 }; /* mapcache buffer order is BGRA */
+
+ CPLErrorReset();
+
+ if(gdal->bUseConnectionPool == MAPCACHE_TRUE) {
+ pc = _gdal_get_connection(ctx, gdal, map->grid_link->grid->srs, gdal->datastr );
+ GC_CHECK_ERROR(ctx);
+ gdal_conn = (gdal_connection*) pc->connection;
+ } else {
+ gdal_connection_params params;
+ params.gdal = gdal;
+ params.dst_srs = map->grid_link->grid->srs;
+ params.gdal_data = gdal->datastr;
+ mapcache_source_gdal_connection_constructor(ctx, (void**)(&gdal_conn), ¶ms);
+ GC_CHECK_ERROR(ctx);
}
- GDALRasterBandH *redband, *greenband, *blueband, *alphaband;
- redband = GDALGetRasterBand(hDstDS,1);
- greenband = GDALGetRasterBand(hDstDS,2);
- blueband = GDALGetRasterBand(hDstDS,3);
- alphaband = GDALGetRasterBand(hDstDS,4);
- unsigned char *rasterdata = apr_palloc(ctx->pool,tile->sx*tile->sy*4);
- data->buf = rasterdata;
- data->avail = tile->sx*tile->sy*4;
- data->size = tile->sx*tile->sy*4;
+ hDstDS = CreateWarpedVRT( gdal_conn->hSrcDS, gdal->srs_wkt, gdal_conn->dst_srs_wkt,
+ map->width, map->height,
+ &map->extent,
+ gdal->eResampleAlg, 0.125, NULL, &hTmpDS );
- GDALRasterIO(redband,GF_Read,0,0,tile->sx,tile->sy,(void*)(rasterdata),tile->sx,tile->sy,GDT_Byte,4,4*tile->sx);
- GDALRasterIO(greenband,GF_Read,0,0,tile->sx,tile->sy,(void*)(rasterdata+1),tile->sx,tile->sy,GDT_Byte,4,4*tile->sx);
- GDALRasterIO(blueband,GF_Read,0,0,tile->sx,tile->sy,(void*)(rasterdata+2),tile->sx,tile->sy,GDT_Byte,4,4*tile->sx);
- if(GDALGetRasterCount(hDataset)==4)
- GDALRasterIO(alphaband,GF_Read,0,0,tile->sx,tile->sy,(void*)(rasterdata+3),tile->sx,tile->sy,GDT_Byte,4,4*tile->sx);
- else {
- unsigned char *alphaptr;
- int i;
- for(alphaptr = rasterdata+3, i=0; i<tile->sx*tile->sy; i++, alphaptr+=4) {
- *alphaptr = 255;
+ if( hDstDS == NULL ) {
+ ctx->set_error(ctx, 500,"CreateWarpedVRT() failed");
+ if(gdal->bUseConnectionPool == MAPCACHE_TRUE) {
+ mapcache_connection_pool_invalidate_connection(ctx,pc);
+ } else {
+ mapcache_source_gdal_connection_destructor(gdal_conn);
+ }
+ return;
+ }
+
+ if(GDALGetRasterCount(hDstDS) != 4) {
+ ctx->set_error(ctx, 500,"gdal did not create a 4 band image");
+ GDALClose(hDstDS); /* close first this one, as it references hSrcDS */
+ if(gdal->bUseConnectionPool == MAPCACHE_TRUE) {
+ mapcache_connection_pool_invalidate_connection(ctx,pc);
+ } else {
+ mapcache_source_gdal_connection_destructor(gdal_conn);
}
+ return;
}
- tile->imdata = mapcache_image_create(ctx);
- tile->imdata->w = tile->sx;
- tile->imdata->h = tile->sy;
- tile->imdata->stride = tile->sx * 4;
- tile->imdata->data = rasterdata;
+ data = mapcache_buffer_create(map->height*map->width*4,ctx->pool);
+ rasterdata = data->buf;
+
+#if GDAL_VERSION_MAJOR >= 2
+ {
+ GDALRasterIOExtraArg sExtraArg;
+ INIT_RASTERIO_EXTRA_ARG(sExtraArg);
+ if( gdal->eResampleAlg == GRA_Bilinear )
+ sExtraArg.eResampleAlg = GRIORA_Bilinear;
+ else if( gdal->eResampleAlg == GRA_Cubic )
+ sExtraArg.eResampleAlg = GRIORA_Cubic;
+ else if( gdal->eResampleAlg == GRA_CubicSpline )
+ sExtraArg.eResampleAlg = GRIORA_CubicSpline;
+ else if( gdal->eResampleAlg == GRA_Lanczos )
+ sExtraArg.eResampleAlg = GRIORA_Lanczos;
+ else if( gdal->eResampleAlg == GRA_Average )
+ sExtraArg.eResampleAlg = GRIORA_Average;
+
+ if( gdal->srcOvrLevel != NULL )
+ {
+ /* If the user specified a particular strategy to choose the source */
+ /* overview level, apply it now */
+ GDALSetMetadataItem(hDstDS, "SrcOvrLevel",gdal->srcOvrLevel, NULL);
+ }
+
+ /* Hopefully, given how we adjust hDstDS resolution, we should query */
+ /* exactly at the resolution of one overview level of hDstDS, and not */
+ /* do extra resampling in generic RasterIO, but just in case specify */
+ /* the resampling alg in sExtraArg. */
+ eErr = GDALDatasetRasterIOEx( hDstDS, GF_Read,0,0,
+ GDALGetRasterXSize(hDstDS),
+ GDALGetRasterYSize(hDstDS),
+ rasterdata,map->width,map->height,GDT_Byte,
+ 4, bands_bgra,
+ 4,4*map->width,1, &sExtraArg );
+ }
+#else
+ eErr = GDALDatasetRasterIO( hDstDS, GF_Read,0,0,
+ GDALGetRasterXSize(hDstDS),
+ GDALGetRasterYSize(hDstDS),
+ rasterdata,map->width,map->height,GDT_Byte,
+ 4, bands_bgra,
+ 4,4*map->width,1 );
+#endif
+ if( eErr != CE_None ) {
+ ctx->set_error(ctx, 500,"GDAL I/O error occured");
+ GDALClose(hDstDS); /* close first this one, as it references hTmpDS or hSrcDS */
+ if( hTmpDS )
+ GDALClose(hTmpDS); /* references hSrcDS, so close before */
+ if(gdal->bUseConnectionPool == MAPCACHE_TRUE) {
+ mapcache_connection_pool_invalidate_connection(ctx,pc);
+ } else {
+ mapcache_source_gdal_connection_destructor(gdal_conn);
+ }
+ return;
+ }
- GDALClose( hDstDS );
- GDALClose( hDataset);
+ map->raw_image = mapcache_image_create(ctx);
+ map->raw_image->w = map->width;
+ map->raw_image->h = map->height;
+ map->raw_image->stride = map->width * 4;
+ map->raw_image->data = rasterdata;
+ map->raw_image->has_alpha = MC_ALPHA_UNKNOWN;
+
+ GDALClose( hDstDS ); /* close first this one, as it references hTmpDS or hSrcDS */
+ if( hTmpDS )
+ GDALClose(hTmpDS); /* references hSrcDS, so close before */
+ if(gdal->bUseConnectionPool == MAPCACHE_TRUE) {
+ mapcache_connection_pool_release_connection(ctx,pc);
+ } else {
+ mapcache_source_gdal_connection_destructor(gdal_conn);
+ }
}
/**
* \private \memberof mapcache_source_gdal
* \sa mapcache_source::configuration_parse()
*/
-void _mapcache_source_gdal_configuration_parse(mapcache_context *ctx, ezxml_t node, mapcache_source *source)
+void _mapcache_source_gdal_configuration_parse(mapcache_context *ctx, ezxml_t node, mapcache_source *source, mapcache_cfg *config)
{
ezxml_t cur_node;
mapcache_source_gdal *src = (mapcache_source_gdal*)source;
@@ -228,12 +612,41 @@ void _mapcache_source_gdal_configuration_parse(mapcache_context *ctx, ezxml_t no
if ((cur_node = ezxml_child(node,"data")) != NULL) {
src->datastr = apr_pstrdup(ctx->pool,cur_node->txt);
}
+ if ((cur_node = ezxml_child(node,"connection_pooled")) != NULL) {
+ if(!strcasecmp(cur_node->txt,"false")) {
+ src->bUseConnectionPool = MAPCACHE_FALSE;
+ } else if(!strcasecmp(cur_node->txt,"true")) {
+ src->bUseConnectionPool = MAPCACHE_TRUE;
+ } else {
+ ctx->set_error(ctx,400,"failed to parse <connection_pooled> (%s). Expecting true or false",cur_node->txt);
+ return;
+ }
+ }
- if ((cur_node = ezxml_child(node,"gdalparams")) != NULL) {
- for(cur_node = cur_node->child; cur_node; cur_node = cur_node->sibling) {
- apr_table_set(src->gdal_params, cur_node->name, cur_node->txt);
+ if ((cur_node = ezxml_child(node,"resample")) != NULL && *cur_node->txt) {
+ if( EQUALN( cur_node->txt, "NEAR", 4) )
+ src->eResampleAlg = GRA_NearestNeighbour;
+ else if( EQUAL( cur_node->txt, "BILINEAR") )
+ src->eResampleAlg = GRA_Bilinear;
+ else if( EQUAL( cur_node->txt, "CUBIC") )
+ src->eResampleAlg = GRA_Cubic;
+ else if( EQUAL( cur_node->txt, "CUBICSPLINE") )
+ src->eResampleAlg = GRA_CubicSpline;
+ else if( EQUAL( cur_node->txt, "LANCZOS") )
+ src->eResampleAlg = GRA_Lanczos;
+#if GDAL_VERSION_MAJOR >= 2
+ else if( EQUAL( cur_node->txt, "AVERAGE") )
+ src->eResampleAlg = GRA_Average;
+#endif
+ else {
+ ctx->set_error(ctx, 500, "unsupported gdal <resample>: %s", cur_node->txt);
+ return;
}
}
+
+ if ((cur_node = ezxml_child(node,"overview-strategy")) != NULL && *cur_node->txt) {
+ src->srcOvrLevel = apr_pstrdup(ctx->pool,cur_node->txt);
+ }
}
/**
@@ -244,16 +657,31 @@ void _mapcache_source_gdal_configuration_check(mapcache_context *ctx, mapcache_c
mapcache_source *source)
{
mapcache_source_gdal *src = (mapcache_source_gdal*)source;
+ GDALDatasetH hDataset;
+
/* check all required parameters are configured */
- if(!strlen(src->datastr)) {
- ctx->set_error(ctx, MAPCACHE_SOURCE_GDAL_ERROR, "gdal source %s has no data",source->name);
+ if( src->datastr == NULL || !strlen(src->datastr)) {
+ ctx->set_error(ctx, 500, "gdal source %s has no data",source->name);
return;
}
- src->poDataset = (GDALDatasetH*)GDALOpen(src->datastr,GA_ReadOnly);
- if( src->poDataset == NULL ) {
- ctx->set_error(ctx, MAPCACHE_SOURCE_GDAL_ERROR, "gdalOpen failed on data %s", src->datastr);
+ hDataset = GDALOpen(src->datastr,GA_ReadOnly);
+ if( hDataset == NULL ) {
+ ctx->set_error(ctx, 500, "gdalOpen failed on data %s", src->datastr);
return;
}
+ if( GDALGetProjectionRef( hDataset ) != NULL
+ && strlen(GDALGetProjectionRef( hDataset )) > 0 ) {
+ src->srs_wkt = apr_pstrdup(ctx->pool,GDALGetProjectionRef( hDataset ));
+ } else if( GDALGetGCPProjection( hDataset ) != NULL
+ && strlen(GDALGetGCPProjection(hDataset)) > 0
+ && GDALGetGCPCount( hDataset ) > 1 ) {
+ src->srs_wkt = apr_pstrdup(ctx->pool,GDALGetGCPProjection( hDataset ));
+ } else {
+ ctx->set_error(ctx, 500, "Input gdal source for %s has no defined SRS\n", source->name );
+ GDALClose(hDataset);
+ return;
+ }
+ GDALClose(hDataset);
}
#endif //USE_GDAL
@@ -261,18 +689,19 @@ void _mapcache_source_gdal_configuration_check(mapcache_context *ctx, mapcache_c
mapcache_source* mapcache_source_gdal_create(mapcache_context *ctx)
{
#ifdef USE_GDAL
- GDALAllRegister();
mapcache_source_gdal *source = apr_pcalloc(ctx->pool, sizeof(mapcache_source_gdal));
if(!source) {
- ctx->set_error(ctx, MAPCACHE_ALLOC_ERROR, "failed to allocate gdal source");
+ ctx->set_error(ctx, 500, "failed to allocate gdal source");
return NULL;
}
mapcache_source_init(ctx, &(source->source));
source->source.type = MAPCACHE_SOURCE_GDAL;
- source->source.render_metatile = _mapcache_source_gdal_render_metatile;
+ source->source._render_map = _mapcache_source_gdal_render_metatile;
source->source.configuration_check = _mapcache_source_gdal_configuration_check;
- source->source.configuration_parse = _mapcache_source_gdal_configuration_parse;
- source->gdal_params = apr_table_make(ctx->pool,4);
+ source->source.configuration_parse_xml = _mapcache_source_gdal_configuration_parse;
+ source->eResampleAlg = MAPCACHE_DEFAULT_RESAMPLE_ALG;
+ source->bUseConnectionPool = MAPCACHE_TRUE;
+ GDALAllRegister();
return (mapcache_source*)source;
#else
ctx->set_error(ctx, 400, "failed to create gdal source, GDAL support is not compiled in this version");
diff --git a/lib/source_mapserver.c b/lib/source_mapserver.c
index 4321e49..44396e1 100644
--- a/lib/source_mapserver.c
+++ b/lib/source_mapserver.c
@@ -41,8 +41,15 @@
#include <apr_reslist.h>
#include <mapserver.h>
-/* hash table key = source->name, value = apr_reslist_t of mapObjs */
-static apr_hash_t *mapobj_container = NULL;
+/**\class mapcache_source_mapserver
+ * \brief WMS mapcache_source
+ * \implements mapcache_source
+ */
+typedef struct mapcache_source_mapserver mapcache_source_mapserver;
+struct mapcache_source_mapserver {
+ mapcache_source source;
+ char *mapfile;
+};
struct mc_mapobj {
mapObj *map;
@@ -50,91 +57,44 @@ struct mc_mapobj {
char *error;
};
-static apr_status_t _ms_get_mapobj(void **conn_, void *params, apr_pool_t *pool)
-{
+void mapcache_mapserver_connection_constructor(mapcache_context *ctx, void **conn_, void *params) {
mapcache_source_mapserver *src = (mapcache_source_mapserver*) params;
struct mc_mapobj *mcmap = calloc(1,sizeof(struct mc_mapobj));
- *conn_ = mcmap;
mcmap->map = msLoadMap(src->mapfile,NULL);
if(!mcmap->map) {
errorObj *errors = NULL;
- msWriteError(stderr);
+ ctx->set_error(ctx, 500, "Failed to load mapfile '%s'",src->mapfile);
errors = msGetErrorObj();
- mcmap->error = apr_psprintf(pool,"Failed to load mapfile '%s'. Mapserver reports: %s",src->mapfile, errors->message);
- return APR_EGENERAL;
+ while(errors) {
+ ctx->set_error(ctx, 500, "Failed to load mapfile '%s'. Mapserver reports: %s",src->mapfile, errors->message);
+ errors = errors->next;
+ }
+ return;
}
msMapSetLayerProjections(mcmap->map);
- return APR_SUCCESS;
+ *conn_ = mcmap;
}
-static apr_status_t _ms_free_mapobj(void *conn_, void *params, apr_pool_t *pool)
-{
+void mapcache_mapserver_connection_destructor(void *conn_) {
struct mc_mapobj *mcmap = (struct mc_mapobj*) conn_;
msFreeMap(mcmap->map);
free(mcmap);
- return APR_SUCCESS;
}
-static struct mc_mapobj* _get_mapboj(mapcache_context *ctx, mapcache_map *map) {
- apr_status_t rv;
- mapcache_source_mapserver *src = (mapcache_source_mapserver*) map->tileset->source;
- struct mc_mapobj *mcmap;
- apr_reslist_t *mapobjs = NULL;
- if(!mapobj_container || NULL == (mapobjs = apr_hash_get(mapobj_container,src->source.name,APR_HASH_KEY_STRING))) {
-#ifdef APR_HAS_THREADS
- if(ctx->threadlock)
- apr_thread_mutex_lock((apr_thread_mutex_t*)ctx->threadlock);
-#endif
- if(!mapobj_container) {
- mapobj_container = apr_hash_make(ctx->process_pool);
- }
- mapobjs = apr_hash_get(mapobj_container,src->source.name,APR_HASH_KEY_STRING);
- if(!mapobjs) {
- apr_status_t rv;
- rv = apr_reslist_create(&mapobjs,
- 0 /* min */,
- 1 /* soft max */,
- 30 /* hard max */,
- 6 * 1000000 /*6 seconds, ttl*/,
- _ms_get_mapobj, /* resource constructor */
- _ms_free_mapobj, /* resource destructor */
- src, ctx->process_pool);
- if (rv != APR_SUCCESS) {
- ctx->set_error(ctx, 500, "failed to create mapobj connection pool for cache %s", src->source.name);
-#ifdef APR_HAS_THREADS
- if(ctx->threadlock)
- apr_thread_mutex_unlock((apr_thread_mutex_t*)ctx->threadlock);
-#endif
- return NULL;
- }
- apr_hash_set(mapobj_container,src->source.name,APR_HASH_KEY_STRING,mapobjs);
- }
- assert(mapobjs);
-#ifdef APR_HAS_THREADS
- if(ctx->threadlock)
- apr_thread_mutex_unlock((apr_thread_mutex_t*)ctx->threadlock);
-#endif
- }
- rv = apr_reslist_acquire(mapobjs, (void **) &mcmap);
- if (rv != APR_SUCCESS) {
- ctx->set_error(ctx, 500, "failed to aquire mappObj instance: %s", mcmap->error);
- return NULL;
- }
- return mcmap;
-}
-
-static void _release_mapboj(mapcache_context *ctx, mapcache_map *map, struct mc_mapobj *mcmap)
+static mapcache_pooled_connection* _mapserver_get_connection(mapcache_context *ctx, mapcache_map *map)
{
- mapcache_source_mapserver *src = (mapcache_source_mapserver*) map->tileset->source;
- msFreeLabelCache(&mcmap->map->labelcache);
- apr_reslist_t *mapobjs = apr_hash_get(mapobj_container,src->source.name, APR_HASH_KEY_STRING);
- assert(mapobjs);
- if (GC_HAS_ERROR(ctx)) {
- apr_reslist_invalidate(mapobjs, (void*) mcmap);
- } else {
- apr_reslist_release(mapobjs, (void*) mcmap);
+ mapcache_pooled_connection *pc;
+ char *key = apr_psprintf(ctx->pool, "ms_src_%s", map->tileset->source->name);
+
+ pc = mapcache_connection_pool_get_connection(ctx, key, mapcache_mapserver_connection_constructor,
+ mapcache_mapserver_connection_destructor, map->tileset->source);
+ if(!GC_HAS_ERROR(ctx) && pc && pc->connection) {
}
+
+ return pc;
}
+
+
/**
* \private \memberof mapcache_source_mapserver
* \sa mapcache_source::render_map()
@@ -142,15 +102,27 @@ static void _release_mapboj(mapcache_context *ctx, mapcache_map *map, struct mc_
void _mapcache_source_mapserver_render_map(mapcache_context *ctx, mapcache_map *map)
{
errorObj *errors = NULL;
+ mapcache_pooled_connection *pc;
+ struct mc_mapobj *mcmap;
+ double dx, dy;
+ rasterBufferObj rb;
+ imageObj *image;
+
+ pc = _mapserver_get_connection(ctx, map);
+ GC_CHECK_ERROR(ctx);
- struct mc_mapobj *mcmap = _get_mapboj(ctx,map);
+ mcmap = pc->connection;
GC_CHECK_ERROR(ctx);
if(mcmap->grid_link != map->grid_link) {
if (msLoadProjectionString(&(mcmap->map->projection), map->grid_link->grid->srs) != 0) {
+ ctx->set_error(ctx,500, "Unable to set projection on mapObj.");
errors = msGetErrorObj();
- ctx->set_error(ctx,500, "Unable to set projection on mapObj. MapServer reports: %s", errors->message);
- _release_mapboj(ctx,map,mcmap);
+ while(errors) {
+ ctx->set_error(ctx,500, "Unable to set projection on mapObj. MapServer reports: %s", errors->message);
+ errors = errors->next;
+ }
+ mapcache_connection_pool_invalidate_connection(ctx,pc);
return;
}
switch(map->grid_link->grid->unit) {
@@ -173,7 +145,6 @@ void _mapcache_source_mapserver_render_map(mapcache_context *ctx, mapcache_map *
** pixel to center of pixel. Here we try to adjust the WMS extents
** in by half a pixel.
*/
- double dx, dy;
dx = (map->extent.maxx - map->extent.minx) / (map->width*2);
dy = (map->extent.maxy - map->extent.miny) / (map->height*2);
@@ -183,20 +154,27 @@ void _mapcache_source_mapserver_render_map(mapcache_context *ctx, mapcache_map *
mcmap->map->extent.maxy = map->extent.maxy - dy;
msMapSetSize(mcmap->map, map->width, map->height);
- imageObj *image = msDrawMap(mcmap->map, MS_FALSE);
+ image = msDrawMap(mcmap->map, MS_FALSE);
if(!image) {
+ ctx->set_error(ctx,500, "MapServer failed to create image.");
errors = msGetErrorObj();
- ctx->set_error(ctx,500, "MapServer failed to create image. MapServer reports: %s", errors->message);
- _release_mapboj(ctx,map,mcmap);
+ while(errors) {
+ ctx->set_error(ctx,500, "MapServer reports: %s", errors->message);
+ errors = errors->next;
+ }
+ mapcache_connection_pool_invalidate_connection(ctx,pc);
return;
}
- rasterBufferObj rb;
if(image->format->vtable->supports_pixel_buffer) {
- image->format->vtable->getRasterBufferHandle(image,&rb);
+ if( MS_SUCCESS != image->format->vtable->getRasterBufferHandle(image,&rb)) {
+ ctx->set_error(ctx,500,"failed to get mapserver raster buffer handle");
+ mapcache_connection_pool_invalidate_connection(ctx,pc);
+ return;
+ }
} else {
ctx->set_error(ctx,500,"format %s has no pixel export",image->format->name);
- _release_mapboj(ctx,map,mcmap);
+ mapcache_connection_pool_invalidate_connection(ctx,pc);
return;
}
@@ -208,11 +186,11 @@ void _mapcache_source_mapserver_render_map(mapcache_context *ctx, mapcache_map *
memcpy(map->raw_image->data,rb.data.rgba.pixels,map->width*map->height*4);
apr_pool_cleanup_register(ctx->pool, map->raw_image->data,(void*)free, apr_pool_cleanup_null);
msFreeImage(image);
- _release_mapboj(ctx,map,mcmap);
+ mapcache_connection_pool_release_connection(ctx,pc);
}
-void _mapcache_source_mapserver_query(mapcache_context *ctx, mapcache_feature_info *fi)
+void _mapcache_source_mapserver_query(mapcache_context *ctx, mapcache_source *psource, mapcache_feature_info *fi)
{
ctx->set_error(ctx,500,"mapserver source does not support queries");
}
@@ -238,6 +216,7 @@ void _mapcache_source_mapserver_configuration_check(mapcache_context *ctx, mapca
mapcache_source *source)
{
mapcache_source_mapserver *src = (mapcache_source_mapserver*)source;
+ mapObj *map;
/* check all required parameters are configured */
if(!src->mapfile) {
ctx->set_error(ctx, 400, "mapserver source %s has no <mapfile> configured",source->name);
@@ -250,7 +229,7 @@ void _mapcache_source_mapserver_configuration_check(mapcache_context *ctx, mapca
msSetup();
/* do a test load to check the mapfile is correct */
- mapObj *map = msLoadMap(src->mapfile, NULL);
+ map = msLoadMap(src->mapfile, NULL);
if(!map) {
msWriteError(stderr);
ctx->set_error(ctx,400,"failed to load mapfile \"%s\"",src->mapfile);
diff --git a/lib/source_wms.c b/lib/source_wms.c
index 250a440..7dcca29 100644
--- a/lib/source_wms.c
+++ b/lib/source_wms.c
@@ -33,13 +33,27 @@
#include <apr_tables.h>
#include <apr_strings.h>
+typedef struct mapcache_source_wms mapcache_source_wms;
+
+/**\class mapcache_source_wms
+ * \brief WMS mapcache_source
+ * \implements mapcache_source
+ */
+struct mapcache_source_wms {
+ mapcache_source source;
+ apr_table_t *wms_default_params; /**< default WMS parameters (SERVICE,REQUEST,STYLES,VERSION) */
+ apr_table_t *getmap_params; /**< WMS parameters specified in configuration */
+ apr_table_t *getfeatureinfo_params; /**< WMS parameters specified in configuration */
+ mapcache_http *http;
+};
+
/**
* \private \memberof mapcache_source_wms
* \sa mapcache_source::render_map()
*/
-void _mapcache_source_wms_render_map(mapcache_context *ctx, mapcache_map *map)
+void _mapcache_source_wms_render_map(mapcache_context *ctx, mapcache_source *psource, mapcache_map *map)
{
- mapcache_source_wms *wms = (mapcache_source_wms*)map->tileset->source;
+ mapcache_source_wms *wms = (mapcache_source_wms*)psource;
mapcache_http *http;
apr_table_t *params = apr_table_clone(ctx->pool,wms->wms_default_params);
apr_table_setn(params,"BBOX",apr_psprintf(ctx->pool,"%f,%f,%f,%f",
@@ -50,21 +64,20 @@ void _mapcache_source_wms_render_map(mapcache_context *ctx, mapcache_map *map)
apr_table_setn(params,"SRS",map->grid_link->grid->srs);
apr_table_overlap(params,wms->getmap_params,APR_OVERLAP_TABLES_SET);
- if(map->dimensions && !apr_is_empty_table(map->dimensions)) {
- const apr_array_header_t *elts = apr_table_elts(map->dimensions);
+
+ if(map->dimensions && map->dimensions->nelts>0) {
int i;
- for(i=0; i<elts->nelts; i++) {
- apr_table_entry_t entry = APR_ARRAY_IDX(elts,i,apr_table_entry_t);
+ for(i=0; i<map->dimensions->nelts; i++) {
+ mapcache_requested_dimension *rdim = APR_ARRAY_IDX(map->dimensions,i,mapcache_requested_dimension*);
/* set both DIM_key=val and key=val KVP params */
- apr_table_setn(params,entry.key,entry.val);
- if(strcasecmp(entry.key,"TIME") && strcasecmp(entry.key,"ELEVATION")) {
- char *dim_name = apr_pstrcat(ctx->pool,"DIM_",entry.key,NULL);
- apr_table_setn(params,dim_name,entry.val);
+ apr_table_setn(params,rdim->dimension->name,rdim->cached_value);
+ if(strcasecmp(rdim->dimension->name,"TIME") && strcasecmp(rdim->dimension->name,"ELEVATION")) {
+ char *dim_name = apr_pstrcat(ctx->pool,"DIM_",rdim->dimension->name,NULL);
+ apr_table_setn(params,dim_name,rdim->cached_value);
}
}
-
}
-
+
/* if the source has no LAYERS parameter defined, then use the tileset name
* as the LAYERS to request. When using mirror-mode, the source has no layers
* defined, it is added based on the incoming request
@@ -86,11 +99,11 @@ void _mapcache_source_wms_render_map(mapcache_context *ctx, mapcache_map *map)
}
}
-void _mapcache_source_wms_query(mapcache_context *ctx, mapcache_feature_info *fi)
+void _mapcache_source_wms_query(mapcache_context *ctx, mapcache_source *source, mapcache_feature_info *fi)
{
mapcache_map *map = (mapcache_map*)fi;
mapcache_http *http;
- mapcache_source_wms *wms = (mapcache_source_wms*)map->tileset->source;
+ mapcache_source_wms *wms = (mapcache_source_wms*)source;
apr_table_t *params = apr_table_clone(ctx->pool,wms->wms_default_params);
apr_table_overlap(params,wms->getmap_params,0);
@@ -105,14 +118,18 @@ void _mapcache_source_wms_query(mapcache_context *ctx, mapcache_feature_info *fi
apr_table_setn(params,"INFO_FORMAT",fi->format);
apr_table_overlap(params,wms->getfeatureinfo_params,0);
- if(map->dimensions && !apr_is_empty_table(map->dimensions)) {
- const apr_array_header_t *elts = apr_table_elts(map->dimensions);
+
+ if(map->dimensions && map->dimensions->nelts>0) {
int i;
- for(i=0; i<elts->nelts; i++) {
- apr_table_entry_t entry = APR_ARRAY_IDX(elts,i,apr_table_entry_t);
- apr_table_setn(params,entry.key,entry.val);
+ for(i=0; i<map->dimensions->nelts; i++) {
+ mapcache_requested_dimension *rdim = APR_ARRAY_IDX(map->dimensions,i,mapcache_requested_dimension*);
+ /* set both DIM_key=val and key=val KVP params */
+ apr_table_setn(params,rdim->dimension->name,rdim->cached_value);
+ if(strcasecmp(rdim->dimension->name,"TIME") && strcasecmp(rdim->dimension->name,"ELEVATION")) {
+ char *dim_name = apr_pstrcat(ctx->pool,"DIM_",rdim->dimension->name,NULL);
+ apr_table_setn(params,dim_name,rdim->cached_value);
+ }
}
-
}
fi->data = mapcache_buffer_create(30000,ctx->pool);
@@ -127,7 +144,7 @@ void _mapcache_source_wms_query(mapcache_context *ctx, mapcache_feature_info *fi
* \private \memberof mapcache_source_wms
* \sa mapcache_source::configuration_parse()
*/
-void _mapcache_source_wms_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_source *source)
+void _mapcache_source_wms_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_source *source, mapcache_cfg *config)
{
ezxml_t cur_node;
mapcache_source_wms *src = (mapcache_source_wms*)source;
@@ -210,10 +227,10 @@ mapcache_source* mapcache_source_wms_create(mapcache_context *ctx)
}
mapcache_source_init(ctx, &(source->source));
source->source.type = MAPCACHE_SOURCE_WMS;
- source->source.render_map = _mapcache_source_wms_render_map;
+ source->source._render_map = _mapcache_source_wms_render_map;
source->source.configuration_check = _mapcache_source_wms_configuration_check;
source->source.configuration_parse_xml = _mapcache_source_wms_configuration_parse_xml;
- source->source.query_info = _mapcache_source_wms_query;
+ source->source._query_info = _mapcache_source_wms_query;
source->wms_default_params = apr_table_make(ctx->pool,4);;
source->getmap_params = apr_table_make(ctx->pool,4);
source->getfeatureinfo_params = apr_table_make(ctx->pool,4);
diff --git a/lib/tileset.c b/lib/tileset.c
index df81b75..5aa3fc3 100644
--- a/lib/tileset.c
+++ b/lib/tileset.c
@@ -37,24 +37,22 @@
#include <limits.h>
#endif
-char* mapcache_tileset_metatile_resource_key(mapcache_context *ctx, mapcache_metatile *mt)
-{
+char* mapcache_tileset_tile_resource_key(mapcache_context *ctx, mapcache_tile *tile) {
char *lockname = apr_psprintf(ctx->pool,
"%d-%d-%d-%s",
- mt->z,mt->y,mt->x,
- mt->map.tileset->name);
+ tile->z,tile->y/tile->tileset->metasize_y,tile->x/tile->tileset->metasize_x,
+ tile->tileset->name);
/* if the tileset has multiple grids, add the name of the current grid to the lock key*/
- if(mt->map.tileset->grid_links->nelts > 1) {
- lockname = apr_pstrcat(ctx->pool,lockname,mt->map.grid_link->grid->name,NULL);
+ if(tile->tileset->grid_links->nelts > 1) {
+ lockname = apr_pstrcat(ctx->pool,lockname,tile->grid_link->grid->name,NULL);
}
- if(mt->map.dimensions && !apr_is_empty_table(mt->map.dimensions)) {
- const apr_array_header_t *elts = apr_table_elts(mt->map.dimensions);
+ if(tile->dimensions && tile->dimensions->nelts>0) {
int i;
- for(i=0; i<elts->nelts; i++) {
- apr_table_entry_t entry = APR_ARRAY_IDX(elts,i,apr_table_entry_t);
- char *dimvalue = apr_pstrdup(ctx->pool,entry.val);
+ for(i=0; i<tile->dimensions->nelts; i++) {
+ mapcache_requested_dimension *rdim = APR_ARRAY_IDX(tile->dimensions,i,mapcache_requested_dimension*);
+ char *dimvalue = apr_pstrdup(ctx->pool,rdim->cached_value);
char *iter = dimvalue;
while(*iter) {
if(*iter == '/') *iter='_';
@@ -67,6 +65,11 @@ char* mapcache_tileset_metatile_resource_key(mapcache_context *ctx, mapcache_met
return lockname;
}
+char* mapcache_tileset_metatile_resource_key(mapcache_context *ctx, mapcache_metatile *mt)
+{
+ return mapcache_tileset_tile_resource_key(ctx,&mt->tiles[0]);
+}
+
void mapcache_tileset_configuration_check(mapcache_context *ctx, mapcache_tileset *tileset)
{
@@ -151,6 +154,30 @@ void mapcache_tileset_add_watermark(mapcache_context *ctx, mapcache_tileset *til
tileset->watermark = mapcache_imageio_decode(ctx,watermarkdata);
}
+void mapcache_tileset_tile_validate_z(mapcache_context *ctx, mapcache_tile *tile) {
+ if(tile->z < tile->grid_link->minz || tile->z >= tile->grid_link->maxz) {
+ ctx->set_error(ctx,404,"invalid tile z level");
+ }
+}
+
+void mapcache_tileset_tile_validate_x(mapcache_context *ctx, mapcache_tile *tile) {
+ mapcache_extent_i limits;
+ limits = tile->grid_link->grid_limits[tile->z];
+ if(tile->x<limits.minx || tile->x>=limits.maxx) {
+ ctx->set_error(ctx, 404, "tile x=%d not in [%d,%d[",
+ tile->x,limits.minx,limits.maxx);
+ }
+}
+
+void mapcache_tileset_tile_validate_y(mapcache_context *ctx, mapcache_tile *tile) {
+ mapcache_extent_i limits;
+ limits = tile->grid_link->grid_limits[tile->z];
+ if(tile->y<limits.miny || tile->y>=limits.maxy) {
+ ctx->set_error(ctx, 404, "tile y=%d not in [%d,%d[",
+ tile->y,limits.miny,limits.maxy);
+ }
+}
+
void mapcache_tileset_tile_validate(mapcache_context *ctx, mapcache_tile *tile)
{
mapcache_extent_i limits;
@@ -303,6 +330,9 @@ mapcache_image* mapcache_tileset_assemble_map_tiles(mapcache_context *ctx, mapca
ox = (Mx - tile->x) * tile->grid_link->grid->tile_sx;
oy = (tile->y - my) * tile->grid_link->grid->tile_sy;
break;
+ default:
+ ctx->set_error(ctx,500,"BUG: invalid grid origin");
+ return NULL;
}
if(tile->nodata) continue;
@@ -327,7 +357,7 @@ mapcache_image* mapcache_tileset_assemble_map_tiles(mapcache_context *ctx, mapca
/* copy/scale the srcimage onto the destination image */
tileresolution = toplefttile->grid_link->grid->levels[toplefttile->z]->resolution;
- mapcache_grid_get_extent(ctx,toplefttile->grid_link->grid,
+ mapcache_grid_get_tile_extent(ctx,toplefttile->grid_link->grid,
toplefttile->x, toplefttile->y, toplefttile->z, &tilebbox);
/*compute the pixel position of top left corner*/
@@ -449,26 +479,16 @@ mapcache_metatile* mapcache_tileset_metatile_get(mapcache_context *ctx, mapcache
*/
void mapcache_tileset_render_metatile(mapcache_context *ctx, mapcache_metatile *mt)
{
- int i;
-#ifdef DEBUG
- if(!mt->map.tileset->source || mt->map.tileset->read_only) {
- ctx->set_error(ctx,500,"###BUG### tileset_render_metatile called on tileset with no source or that is read-only");
+ mapcache_tileset *tileset = mt->map.tileset;
+ if(!tileset->source || tileset->read_only) {
+ ctx->set_error(ctx,500,"tileset_render_metatile called on tileset with no source or that is read-only");
return;
}
-#endif
- mt->map.tileset->source->render_map(ctx, &mt->map);
+ mapcache_source_render_map(ctx, tileset->source, &mt->map);
GC_CHECK_ERROR(ctx);
mapcache_image_metatile_split(ctx, mt);
GC_CHECK_ERROR(ctx);
- if(mt->map.tileset->_cache->tile_multi_set) {
- mt->map.tileset->_cache->tile_multi_set(ctx, mt->map.tileset->_cache, mt->tiles, mt->ntiles);
- } else {
- for(i=0; i<mt->ntiles; i++) {
- mapcache_tile *tile = &(mt->tiles[i]);
- mt->map.tileset->_cache->tile_set(ctx, mt->map.tileset->_cache, tile);
- GC_CHECK_ERROR(ctx);
- }
- }
+ mapcache_cache_tile_multi_set(ctx, tileset->_cache, mt->tiles, mt->ntiles);
}
@@ -488,6 +508,9 @@ mapcache_tileset* mapcache_tileset_create(mapcache_context *ctx)
tileset->format = NULL;
tileset->grid_links = NULL;
tileset->config = NULL;
+ tileset->store_dimension_assemblies = 1;
+ tileset->dimension_assembly_type = MAPCACHE_DIMENSION_ASSEMBLY_NONE;
+ tileset->subdimension_read_only = 0;
return tileset;
}
@@ -510,6 +533,9 @@ mapcache_tileset* mapcache_tileset_clone(mapcache_context *ctx, mapcache_tileset
dst->watermark = src->watermark;
dst->wgs84bbox = src->wgs84bbox;
dst->format = src->format;
+ dst->store_dimension_assemblies = src->store_dimension_assemblies;
+ dst->dimension_assembly_type = src->dimension_assembly_type;
+ dst->subdimension_read_only = src->subdimension_read_only;
return dst;
}
@@ -528,17 +554,15 @@ mapcache_tile* mapcache_tileset_tile_create(apr_pool_t *pool, mapcache_tileset *
tile->grid_link = grid_link;
if(tileset->dimensions) {
int i;
- tile->dimensions = apr_table_make(pool,tileset->dimensions->nelts);
+ tile->dimensions = apr_array_make(pool,tileset->dimensions->nelts,sizeof(mapcache_requested_dimension*));
for(i=0; i<tileset->dimensions->nelts; i++) {
mapcache_dimension *dimension = APR_ARRAY_IDX(tileset->dimensions,i,mapcache_dimension*);
- apr_table_set(tile->dimensions,dimension->name,dimension->default_value);
- }
- }
- if(tileset->timedimension) {
- if(!tile->dimensions) {
- tile->dimensions = apr_table_make(pool,1);
+ mapcache_requested_dimension *rdim = apr_pcalloc(pool,sizeof(mapcache_requested_dimension));
+ rdim->requested_value = dimension->default_value;
+ rdim->cached_value = NULL;
+ rdim->dimension = dimension;
+ APR_ARRAY_PUSH(tile->dimensions,mapcache_requested_dimension*) = rdim;
}
- apr_table_set(tile->dimensions,tileset->timedimension->key,tileset->timedimension->default_value);
}
return tile;
}
@@ -549,9 +573,7 @@ mapcache_tile* mapcache_tileset_tile_clone(apr_pool_t *pool, mapcache_tile *src)
tile->tileset = src->tileset;
tile->expires = src->expires;
tile->grid_link = src->grid_link;
- if(src->dimensions) {
- tile->dimensions = apr_table_clone(pool,src->dimensions);
- }
+ tile->dimensions = mapcache_requested_dimensions_clone(pool, src->dimensions);
tile->x = src->x;
tile->y = src->y;
tile->z = src->z;
@@ -565,7 +587,7 @@ mapcache_map* mapcache_tileset_map_clone(apr_pool_t *pool, mapcache_map *src)
map->tileset = src->tileset;
map->expires = src->expires;
map->grid_link = src->grid_link;
- map->dimensions = apr_table_clone(pool,src->dimensions);
+ map->dimensions = mapcache_requested_dimensions_clone(pool, src->dimensions);
map->height = src->height;
map->width = src->width;
map->extent = src->extent;
@@ -582,17 +604,15 @@ mapcache_map* mapcache_tileset_map_create(apr_pool_t *pool, mapcache_tileset *ti
map->grid_link = grid_link;
if(tileset->dimensions) {
int i;
- map->dimensions = apr_table_make(pool,tileset->dimensions->nelts);
+ map->dimensions = apr_array_make(pool,tileset->dimensions->nelts,sizeof(mapcache_requested_dimension*));
for(i=0; i<tileset->dimensions->nelts; i++) {
mapcache_dimension *dimension = APR_ARRAY_IDX(tileset->dimensions,i,mapcache_dimension*);
- apr_table_set(map->dimensions,dimension->name,dimension->default_value);
- }
- }
- if(tileset->timedimension) {
- if(!map->dimensions) {
- map->dimensions = apr_table_make(pool,1);
+ mapcache_requested_dimension *rdim = apr_pcalloc(pool,sizeof(mapcache_requested_dimension));
+ rdim->requested_value = dimension->default_value;
+ rdim->cached_value = NULL;
+ rdim->dimension = dimension;
+ APR_ARRAY_PUSH(map->dimensions,mapcache_requested_dimension*) = rdim;
}
- apr_table_set(map->dimensions,tileset->timedimension->key,tileset->timedimension->default_value);
}
return map;
}
@@ -608,10 +628,14 @@ mapcache_feature_info* mapcache_tileset_feature_info_create(apr_pool_t *pool, ma
fi->map.grid_link = grid_link;
if(tileset->dimensions) {
int i;
- fi->map.dimensions = apr_table_make(pool,tileset->dimensions->nelts);
+ fi->map.dimensions = apr_array_make(pool,tileset->dimensions->nelts,sizeof(mapcache_requested_dimension*));
for(i=0; i<tileset->dimensions->nelts; i++) {
mapcache_dimension *dimension = APR_ARRAY_IDX(tileset->dimensions,i,mapcache_dimension*);
- apr_table_set(fi->map.dimensions,dimension->name,dimension->default_value);
+ mapcache_requested_dimension *rdim = apr_pcalloc(pool,sizeof(mapcache_requested_dimension));
+ rdim->requested_value = dimension->default_value;
+ rdim->cached_value = NULL;
+ rdim->dimension = dimension;
+ APR_ARRAY_PUSH(fi->map.dimensions,mapcache_requested_dimension*) = rdim;
}
}
return fi;
@@ -626,7 +650,7 @@ void mapcache_tileset_assemble_out_of_zoom_tile(mapcache_context *ctx, mapcache_
assert(tile->grid_link->outofzoom_strategy == MAPCACHE_OUTOFZOOM_REASSEMBLE);
/* we have at most 4 tiles composing the requested tile */
- mapcache_grid_get_extent(ctx,tile->grid_link->grid,tile->x,tile->y,tile->z, &tile_bbox);
+ mapcache_grid_get_tile_extent(ctx,tile->grid_link->grid,tile->x,tile->y,tile->z, &tile_bbox);
/*
shrink the extent so we do not fall exactly on a tile boundary, to avoid rounding
@@ -684,7 +708,7 @@ void mapcache_tileset_assemble_out_of_zoom_tile(mapcache_context *ctx, mapcache_
tile->nodata = 0;
}
/* now copy/scale the srcimage onto the destination image */
- mapcache_grid_get_extent(ctx,childtile->grid_link->grid,
+ mapcache_grid_get_tile_extent(ctx,childtile->grid_link->grid,
childtile->x, childtile->y, childtile->z, &childtile_bbox);
/*compute the pixel position of top left corner*/
@@ -725,11 +749,6 @@ void mapcache_tileset_assemble_out_of_zoom_tile(mapcache_context *ctx, mapcache_
childtile->raw_image = NULL;
childtile->encoded_data = NULL;
}
-
-
-
-
-
}
void mapcache_tileset_outofzoom_get(mapcache_context *ctx, mapcache_tile *tile) {
@@ -745,6 +764,228 @@ void mapcache_tileset_outofzoom_get(mapcache_context *ctx, mapcache_tile *tile)
}
}
+int mapcache_tileset_tile_get_readonly(mapcache_context *ctx, mapcache_tile *tile) {
+ int ret = mapcache_cache_tile_get(ctx, tile->tileset->_cache, tile);
+ if(GC_HAS_ERROR(ctx))
+ return ret;
+
+ if(ret == MAPCACHE_SUCCESS && tile->tileset->auto_expire && tile->mtime && tile->tileset->source && !tile->tileset->read_only) {
+ /* the cache is in auto-expire mode, and can return the tile modification date,
+ * and there is a source configured so we can possibly update it,
+ * so we check to see if it is stale */
+ apr_time_t now = apr_time_now();
+ apr_time_t stale = tile->mtime + apr_time_from_sec(tile->tileset->auto_expire);
+ if(stale<now) {
+ mapcache_tileset_tile_delete(ctx,tile,0);
+ if(ctx->get_error(ctx) == 404) {
+ ctx->clear_errors(ctx);
+ }
+ ret = MAPCACHE_CACHE_MISS;
+ }
+ }
+ return ret;
+}
+
+typedef struct {
+ mapcache_tile *tile;
+ int cache_status;
+} mapcache_subtile;
+
+static void mapcache_tileset_tile_get_without_subdimensions(mapcache_context *ctx, mapcache_tile *tile, int read_only);
+
+void mapcache_tileset_tile_set_get_with_subdimensions(mapcache_context *ctx, mapcache_tile *tile) {
+ apr_array_header_t *subtiles;
+ mapcache_extent extent;
+ mapcache_subtile st;
+ mapcache_image *assembled_image = NULL;
+ mapcache_buffer *assembled_buffer = NULL;
+ int i,j,k,n_subtiles = 1,assembled_nodata = 1;
+ /* we can be here in two cases:
+ * - either we didn't look up the tile directly (need to split dimension into sub-dimension and reassemble dynamically)
+ * - either the direct lookup failed and we need to render/assemble the tiles from subdimensions
+ */
+ subtiles = apr_array_make(ctx->pool,1,sizeof(mapcache_subtile));
+ st.tile = tile;
+ APR_ARRAY_PUSH(subtiles,mapcache_subtile) = st;
+ mapcache_grid_get_tile_extent(ctx,tile->grid_link->grid,tile->x,tile->y,tile->z,&extent);
+ if(GC_HAS_ERROR(ctx)) goto cleanup;
+
+ for(i=0;i<tile->dimensions->nelts; i++) {
+ mapcache_requested_dimension *rdim = APR_ARRAY_IDX(tile->dimensions,i,mapcache_requested_dimension*);
+ apr_array_header_t *single_subdimension = rdim->dimension->get_entries_for_value(ctx,rdim->dimension,rdim->requested_value,
+ tile->tileset, &extent, tile->grid_link->grid);
+ if(GC_HAS_ERROR(ctx)) /* invalid dimension given */
+ goto cleanup;
+#ifdef DEBUG
+ {
+ char *dims = "";
+ int i;
+ for(i=0;i<single_subdimension->nelts;i++)
+ dims = apr_pstrcat(ctx->pool,dims,APR_ARRAY_IDX(single_subdimension,i,char*)," ",NULL);
+ ctx->log(ctx,MAPCACHE_DEBUG,"tile (%d,%d,%d) dimension (%s) returned: %s",
+ tile->z,tile->y,tile->x,rdim->dimension->name,dims);
+ }
+#endif
+
+ if(single_subdimension->nelts == 0) {
+ /* not an error, but no subdimension was found: we need to return an empty tile */
+ tile->nodata = 1;
+ if(tile->tileset->store_dimension_assemblies) {
+ tile->raw_image = mapcache_image_create_with_data(ctx,tile->grid_link->grid->tile_sx, tile->grid_link->grid->tile_sy);
+ tile->raw_image->has_alpha = MC_ALPHA_YES;
+ tile->raw_image->is_blank = MC_EMPTY_YES;
+ tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format);
+ /* set the key for the dimension so it can be stored with the requested dimension */
+ for(j=0;j<tile->dimensions->nelts;j++) {
+ mapcache_requested_dimension *dim = APR_ARRAY_IDX(tile->dimensions,j,mapcache_requested_dimension*);
+ dim->cached_value = dim->requested_value;
+ }
+ mapcache_cache_tile_set(ctx, tile->tileset->_cache, tile);
+ GC_CHECK_ERROR(ctx);
+ }
+ return;
+ } else {
+ for(j=0;j<n_subtiles;j++) {
+ /* clone the existing subtiles if we have more than one sub-dimension to assemble for the the current dimension */
+ for(k=1;k<single_subdimension->nelts;k++) {
+ st.tile = mapcache_tileset_tile_clone(ctx->pool,APR_ARRAY_IDX(subtiles,j,mapcache_subtile).tile);
+ APR_ARRAY_PUSH(subtiles,mapcache_subtile)=st;
+ }
+ }
+ n_subtiles *= single_subdimension->nelts;
+ /* foreach of the subtiles, now set the actual subdimension we are going to be using
+ the "j%nelts" part takes care of looping over and over through the individual subdimensions */
+ for(j=0;j<n_subtiles;j++) {
+ mapcache_tile_set_cached_dimension(ctx,APR_ARRAY_IDX(subtiles,j,mapcache_subtile).tile,rdim->dimension->name,
+ APR_ARRAY_IDX(single_subdimension,j%single_subdimension->nelts,char*));
+
+ }
+ }
+ }
+
+ /* our subtiles array now contains a list of tiles with subdimensions split up, we now need to fetch them from the cache */
+ /* note that subtiles[0].tile == tile */
+
+ for(i=subtiles->nelts-1; i>=0; i--) {
+ mapcache_tile *subtile = APR_ARRAY_IDX(subtiles,i,mapcache_subtile).tile;
+ mapcache_tileset_tile_get_without_subdimensions(ctx, subtile, (tile->tileset->subdimension_read_only||!tile->tileset->source)?1:0); /* creates the tile from the source, takes care of metatiling */
+ if(GC_HAS_ERROR(ctx))
+ goto cleanup;
+ if(!subtile->nodata) {
+ assembled_nodata = 0;
+ if(!assembled_buffer && !assembled_image) {
+ /* first "usable" subtile */
+ assembled_buffer = subtile->encoded_data;
+ assembled_image = subtile->raw_image;
+ } else {
+ /* need to merge current assembled tile over this subtile */
+ if(!assembled_image) {
+ assembled_image = mapcache_imageio_decode(ctx,assembled_buffer);
+ if(GC_HAS_ERROR(ctx))
+ goto cleanup;
+ assembled_buffer = NULL; /* the image data went stale as we're merging something */
+ }
+ if(!subtile->raw_image) {
+ subtile->raw_image = mapcache_imageio_decode(ctx,subtile->encoded_data);
+ if(GC_HAS_ERROR(ctx))
+ goto cleanup;
+ }
+ mapcache_image_merge(ctx, subtile->raw_image, assembled_image);
+ assembled_image = subtile->raw_image;
+ assembled_image->has_alpha = MC_ALPHA_UNKNOWN; /* we've merged two images, we now have no idea if it's transparent or not */
+ if(GC_HAS_ERROR(ctx))
+ goto cleanup;
+ }
+ if((subtile->encoded_data && mapcache_imageio_header_sniff(ctx,subtile->encoded_data) == GC_JPEG)||
+ (subtile->raw_image && subtile->raw_image->has_alpha == MC_ALPHA_NO)) {
+ /* the returned image is fully opaque, we don't need to get/decode/merge any further subtiles */
+ if(assembled_image)
+ assembled_image->has_alpha = MC_ALPHA_NO;
+ break;
+ }
+ }
+ }
+
+ tile->encoded_data = assembled_buffer;
+ tile->raw_image = assembled_image;
+ tile->nodata = assembled_nodata;
+
+ /* TODO: how should the no data case be handled generically?
+ * uncomment the following block if this nodata state should be returned to
+ * the requester immediately, without this info being stored to the cache.
+ * Leaving this uncommented will cause a no-data tile to be (maybe, depending
+ * on the cache's actual configuration) written to the cache
+ */
+ /*
+ if(tile->nodata) {
+ goto cleanup;
+ }
+ */
+
+ if(!tile->nodata && !tile->encoded_data) {
+ tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format);
+ GC_CHECK_ERROR(ctx);
+ }
+ if(tile->tileset->store_dimension_assemblies) {
+ int already_stored = 1; /*depending on the type of dimension, we may have no nead to store the resulting tile*/
+
+ if(n_subtiles != 1)
+ already_stored = 0; /*if we had to merge multiple subdimensions, then we always have to store the resulting assembly*/
+
+ /* set the key for the dimension so it can be stored with the requested dimension */
+ for(j=0;j<tile->dimensions->nelts;j++) {
+ mapcache_requested_dimension *dim = APR_ARRAY_IDX(tile->dimensions,j,mapcache_requested_dimension*);
+ if(strcmp(dim->cached_value,dim->requested_value)) {
+ already_stored = 0; /*the subdimension is different than the requested dimension, we need to store the resulting tile*/
+ }
+ dim->cached_value = dim->requested_value;
+ }
+ if(!already_stored) {
+ if(tile->nodata) {
+ tile->raw_image = mapcache_image_create_with_data(ctx,tile->grid_link->grid->tile_sx, tile->grid_link->grid->tile_sy);
+ tile->raw_image->has_alpha = MC_ALPHA_YES;
+ tile->raw_image->is_blank = MC_EMPTY_YES;
+ tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format);
+ GC_CHECK_ERROR(ctx);
+ }
+ mapcache_cache_tile_set(ctx, tile->tileset->_cache, tile);
+ GC_CHECK_ERROR(ctx);
+ }
+ }
+
+cleanup:
+ return;
+}
+
+void mapcache_tileset_tile_get_with_subdimensions(mapcache_context *ctx, mapcache_tile *tile) {
+ int i,ret;
+ assert(tile->dimensions);
+ if(tile->tileset->store_dimension_assemblies) {
+ for(i=0;i<tile->dimensions->nelts;i++) {
+ mapcache_requested_dimension *dim = APR_ARRAY_IDX(tile->dimensions,i,mapcache_requested_dimension*);
+ dim->cached_value = dim->requested_value;
+ }
+ ret = mapcache_tileset_tile_get_readonly(ctx,tile);
+ GC_CHECK_ERROR(ctx);
+ if(ret == MAPCACHE_SUCCESS) {
+ /* update the tile expiration time */
+ if(tile->tileset->auto_expire && tile->mtime) {
+ apr_time_t now = apr_time_now();
+ apr_time_t expire_time = tile->mtime + apr_time_from_sec(tile->tileset->auto_expire);
+ tile->expires = apr_time_sec(expire_time-now);
+ }
+ return;
+ }
+ for(i=0;i<tile->dimensions->nelts;i++) {
+ /* unset the cached dimension we setup earlier on */
+ mapcache_requested_dimension *dim = APR_ARRAY_IDX(tile->dimensions,i,mapcache_requested_dimension*);
+ dim->cached_value = NULL;
+ }
+ }
+ return mapcache_tileset_tile_set_get_with_subdimensions(ctx,tile);
+
+}
+
/**
* \brief return the image data for a given tile
* this call uses a global (interprocess+interthread) mutex if the tile was not found
@@ -763,16 +1004,12 @@ void mapcache_tileset_outofzoom_get(mapcache_context *ctx, mapcache_tile *tile)
* - release mutex
*
*/
-void mapcache_tileset_tile_get(mapcache_context *ctx, mapcache_tile *tile)
+
+static void mapcache_tileset_tile_get_without_subdimensions(mapcache_context *ctx, mapcache_tile *tile, int read_only)
{
int ret;
mapcache_metatile *mt=NULL;
- if(tile->grid_link->outofzoom_strategy != MAPCACHE_OUTOFZOOM_NOTCONFIGURED &&
- tile->z > tile->grid_link->max_cached_zoom) {
- mapcache_tileset_outofzoom_get(ctx, tile);
- return;
- }
- ret = tile->tileset->_cache->tile_get(ctx, tile->tileset->_cache, tile);
+ ret = mapcache_cache_tile_get(ctx, tile->tileset->_cache, tile);
GC_CHECK_ERROR(ctx);
if(ret == MAPCACHE_SUCCESS && tile->tileset->auto_expire && tile->mtime && tile->tileset->source && !tile->tileset->read_only) {
@@ -789,7 +1026,7 @@ void mapcache_tileset_tile_get(mapcache_context *ctx, mapcache_tile *tile)
if (ret == MAPCACHE_CACHE_MISS) {
/* bail out straight away if the tileset has no source or is read-only */
- if(tile->tileset->read_only || !tile->tileset->source) {
+ if(read_only) {
/* there is no source configured for this tile. not an error, let caller now*/
/*
ctx->set_error(ctx,404,"tile not in cache, and no source configured for tileset %s",
@@ -808,11 +1045,11 @@ void mapcache_tileset_tile_get(mapcache_context *ctx, mapcache_tile *tile)
if (ret == MAPCACHE_CACHE_MISS || ret == MAPCACHE_CACHE_RELOAD) {
- int isLocked;
+ int isLocked = MAPCACHE_FALSE;
void *lock;
/* If the tile does not exist or stale, we must take action before re-asking for it */
- if( !tile->tileset->read_only && tile->tileset->source && !ctx->config->non_blocking) {
+ if( !read_only && !ctx->config->non_blocking) {
/*
* is the tile already being rendered by another thread ?
* the call is protected by the same mutex that sets the lock on the tile,
@@ -840,10 +1077,10 @@ void mapcache_tileset_tile_get(mapcache_context *ctx, mapcache_tile *tile)
/* temporarily clear error state so we don't mess up with error handling in the locker */
void *error;
ctx->pop_errors(ctx,&error);
- mapcache_unlock_resource(ctx, ctx->config->locker, mapcache_tileset_metatile_resource_key(ctx,mt), lock);
+ mapcache_unlock_resource(ctx, ctx->config->locker, lock);
ctx->push_errors(ctx,error);
} else {
- mapcache_unlock_resource(ctx, ctx->config->locker, mapcache_tileset_metatile_resource_key(ctx,mt), lock);
+ mapcache_unlock_resource(ctx, ctx->config->locker, lock);
}
}
}
@@ -858,7 +1095,7 @@ void mapcache_tileset_tile_get(mapcache_context *ctx, mapcache_tile *tile)
/* Else, check for errors and try to fetch the tile from the cache.
*/
GC_CHECK_ERROR(ctx);
- ret = tile->tileset->_cache->tile_get(ctx, tile->tileset->_cache, tile);
+ ret = mapcache_cache_tile_get(ctx, tile->tileset->_cache, tile);
GC_CHECK_ERROR(ctx);
if(ret != MAPCACHE_SUCCESS) {
@@ -880,11 +1117,48 @@ void mapcache_tileset_tile_get(mapcache_context *ctx, mapcache_tile *tile)
}
}
+void mapcache_tileset_tile_get(mapcache_context *ctx, mapcache_tile *tile) {
+ if(tile->grid_link->outofzoom_strategy != MAPCACHE_OUTOFZOOM_NOTCONFIGURED &&
+ tile->z > tile->grid_link->max_cached_zoom) {
+ mapcache_tileset_outofzoom_get(ctx, tile);
+ return;
+ }
+ if(tile->dimensions) {
+ if(tile->tileset->dimension_assembly_type != MAPCACHE_DIMENSION_ASSEMBLY_NONE) {
+ return mapcache_tileset_tile_get_with_subdimensions(ctx,tile);
+ } else {
+ int i;
+ mapcache_requested_dimension *rdim;
+ mapcache_extent extent;
+
+ mapcache_grid_get_tile_extent(ctx,tile->grid_link->grid,tile->x,tile->y,tile->z,&extent);
+ for(i=0; i<tile->dimensions->nelts; i++) {
+ apr_array_header_t *rdim_vals;
+ rdim = APR_ARRAY_IDX(tile->dimensions,i,mapcache_requested_dimension*);
+ rdim_vals = rdim->dimension->get_entries_for_value(ctx,rdim->dimension,rdim->requested_value, tile->tileset, NULL, tile->grid_link->grid);
+ GC_CHECK_ERROR(ctx);
+ if(rdim_vals->nelts > 1) {
+ ctx->set_error(ctx,500,"dimension (%s) for tileset (%s) returned invalid number (%d) of subdimensions (1 expected)",
+ rdim->dimension->name, tile->tileset->name, rdim_vals->nelts);
+ return;
+ }
+ if(rdim_vals->nelts == 0) {
+ ctx->set_error(ctx,404,"dimension (%s) for tileset (%s) returned no subdimensions (1 expected)",rdim->dimension->name, tile->tileset->name);
+ return;
+ }
+ rdim->cached_value = APR_ARRAY_IDX(rdim_vals,0,char*);
+ }
+ }
+ }
+ return mapcache_tileset_tile_get_without_subdimensions(ctx,tile, (tile->tileset->read_only||!tile->tileset->source)?1:0);
+
+}
+
void mapcache_tileset_tile_delete(mapcache_context *ctx, mapcache_tile *tile, int whole_metatile)
{
int i;
/*delete the tile itself*/
- tile->tileset->_cache->tile_delete(ctx,tile->tileset->_cache, tile);
+ mapcache_cache_tile_delete(ctx,tile->tileset->_cache, tile);
GC_CHECK_ERROR(ctx);
if(whole_metatile) {
@@ -893,7 +1167,7 @@ void mapcache_tileset_tile_delete(mapcache_context *ctx, mapcache_tile *tile, in
mapcache_tile *subtile = &mt->tiles[i];
/* skip deleting the actual tile */
if(subtile->x == tile->x && subtile->y == tile->y) continue;
- subtile->tileset->_cache->tile_delete(ctx,subtile->tileset->_cache,subtile);
+ mapcache_cache_tile_delete(ctx,subtile->tileset->_cache,subtile);
/* silently pass failure if the tile was not found */
if(ctx->get_error(ctx) == 404) {
ctx->clear_errors(ctx);
diff --git a/lib/util.c b/lib/util.c
index fe9e1cb..bd01d25 100644
--- a/lib/util.c
+++ b/lib/util.c
@@ -324,7 +324,6 @@ void mapcache_context_copy(mapcache_context *src, mapcache_context *dst)
dst->set_exception = src->set_exception;
dst->service = src->service;
dst->exceptions = src->exceptions;
- dst->threadlock = src->threadlock;
dst->supports_redirects = src->supports_redirects;
dst->pop_errors = src->pop_errors;
dst->push_errors = src->push_errors;
@@ -336,21 +335,20 @@ char* mapcache_util_get_tile_dimkey(mapcache_context *ctx, mapcache_tile *tile,
{
char *key = apr_pstrdup(ctx->pool,"");
if(tile->dimensions) {
- const apr_array_header_t *elts = apr_table_elts(tile->dimensions);
- int i = elts->nelts;
+ int i = tile->dimensions->nelts;
if(i>1) {
while(i--) {
- apr_table_entry_t *entry = &(APR_ARRAY_IDX(elts,i,apr_table_entry_t));
+ mapcache_requested_dimension *entry = APR_ARRAY_IDX(tile->dimensions,i,mapcache_requested_dimension*);
if(i) {
- key = apr_pstrcat(ctx->pool,key,entry->val,(sanitized_chars?sanitize_to:"#"),NULL);
+ key = apr_pstrcat(ctx->pool,key,entry->cached_value,(sanitized_chars?sanitize_to:"#"),NULL);
} else {
- key = apr_pstrcat(ctx->pool,key,entry->val,NULL);
+ key = apr_pstrcat(ctx->pool,key,entry->cached_value,NULL);
}
}
return key;
} else if(i) {
- apr_table_entry_t *entry = &(APR_ARRAY_IDX(elts,0,apr_table_entry_t));
- key = apr_pstrdup(ctx->pool,entry->val);
+ mapcache_requested_dimension *entry = APR_ARRAY_IDX(tile->dimensions,0,mapcache_requested_dimension*);
+ key = apr_pstrdup(ctx->pool,entry->cached_value);
}
if(sanitized_chars)
key = mapcache_util_str_sanitize(ctx->pool,key,sanitized_chars,*sanitize_to);
@@ -358,19 +356,109 @@ char* mapcache_util_get_tile_dimkey(mapcache_context *ctx, mapcache_tile *tile,
return key;
}
+void mapcache_util_quadkey_decode(mapcache_context *ctx, const char *quadkey, int *x, int *y, int *z) {
+ int i;
+ if(!quadkey || !*quadkey) {
+ *z = *x = *y = 0;
+ return;
+ }
+ *z = strlen(quadkey);
+ *x = *y = 0;
+ for (i = *z; i; i--) {
+ int mask = 1 << (i - 1);
+ switch (quadkey[*z - i]) {
+ case '0':
+ break;
+ case '1':
+ *x |= mask;
+ break;
+ case '2':
+ *y |= mask;
+ break;
+ case '3':
+ *x |= mask;
+ *y |= mask;
+ break;
+ default:
+ ctx->set_error(ctx, 400, "Invalid Quadkey sequence");
+ return;
+ }
+ }
+}
+
+char* mapcache_util_quadkey_encode(mapcache_context *ctx, int x, int y, int z) {
+ int i;
+ char *key = apr_pcalloc(ctx->pool, z+1);
+ memset(key,'0',z);
+ for (i = z; i > 0; i--) {
+ int mask = 1 << (i - 1);
+ if ((x & mask) != 0)
+ {
+ key[z - i]++;
+ }
+ if ((y & mask) != 0)
+ {
+ key[z - i] += 2;
+ }
+ }
+ return key;
+}
+
char* mapcache_util_get_tile_key(mapcache_context *ctx, mapcache_tile *tile, char *template,
char* sanitized_chars, char *sanitize_to)
{
char *path;
if(template) {
- path = mapcache_util_str_replace(ctx->pool, template, "{x}",
- apr_psprintf(ctx->pool, "%d", tile->x));
- path = mapcache_util_str_replace(ctx->pool, path, "{y}",
- apr_psprintf(ctx->pool, "%d", tile->y));
- path = mapcache_util_str_replace(ctx->pool, path, "{z}",
- apr_psprintf(ctx->pool, "%d", tile->z));
- if(strstr(path,"{dim}")) {
- path = mapcache_util_str_replace(ctx->pool, path, "{dim}", mapcache_util_get_tile_dimkey(ctx,tile,sanitized_chars,sanitize_to));
+ path = apr_pstrdup(ctx->pool, template);
+
+ if(strstr(path,"{x}"))
+ path = mapcache_util_str_replace(ctx->pool,path, "{x}",
+ apr_psprintf(ctx->pool,"%d",tile->x));
+ else if(strstr(path,"{inv_x}"))
+ path = mapcache_util_str_replace(ctx->pool,path, "{inv_x}",
+ apr_psprintf(ctx->pool,"%d",
+ tile->grid_link->grid->levels[tile->z]->maxx - tile->x - 1));
+ if(strstr(path,"{y}"))
+ path = mapcache_util_str_replace(ctx->pool,path, "{y}",
+ apr_psprintf(ctx->pool,"%d",tile->y));
+ else if(strstr(path,"{inv_y}"))
+ path = mapcache_util_str_replace(ctx->pool,path, "{inv_y}",
+ apr_psprintf(ctx->pool,"%d",
+ tile->grid_link->grid->levels[tile->z]->maxy - tile->y - 1));
+ if(strstr(path,"{z}"))
+ path = mapcache_util_str_replace(ctx->pool,path, "{z}",
+ apr_psprintf(ctx->pool,"%d",tile->z));
+ else if(strstr(path,"{inv_z}"))
+ path = mapcache_util_str_replace(ctx->pool,path, "{inv_z}",
+ apr_psprintf(ctx->pool,"%d",
+ tile->grid_link->grid->nlevels - tile->z - 1));
+ if(strstr(path,"{quadkey}")) {
+ char *quadkey = mapcache_util_quadkey_encode(ctx, tile->x, tile->y, tile->z);
+ path = mapcache_util_str_replace(ctx->pool,path, "{quadkey}", quadkey);
+ }
+
+ if(tile->dimensions) {
+ if(strstr(path,"{dim:")) {
+ char *dimstring="";
+ int i = tile->dimensions->nelts;
+ while(i--) {
+ char *single_dim;
+ mapcache_requested_dimension *entry = APR_ARRAY_IDX(tile->dimensions,i,mapcache_requested_dimension*);
+
+ /* compute value for eventual {dim} replacement */
+ dimstring = apr_pstrcat(ctx->pool,dimstring,"#",entry->dimension->name,"#",entry->cached_value,NULL);
+
+ /* check for {dim:name} replacement */
+ single_dim = apr_pstrcat(ctx->pool,"{dim:",entry->dimension->name,"}",NULL);
+ if(strstr(path,single_dim)) {
+ path = mapcache_util_str_replace(ctx->pool,path, single_dim, entry->cached_value);
+ }
+ }
+ }
+ if(strstr(path,"{dim}")) {
+ path = mapcache_util_str_replace(ctx->pool,path, "{dim}",
+ mapcache_util_get_tile_dimkey(ctx,tile,sanitized_chars,sanitize_to));
+ }
}
if(strstr(path,"{tileset}"))
path = mapcache_util_str_replace(ctx->pool, path, "{tileset}", tile->tileset->name);
diff --git a/mapcache.xml.sample b/mapcache.xml.sample
index 4e7a976..defb8ff 100644
--- a/mapcache.xml.sample
+++ b/mapcache.xml.sample
@@ -523,6 +523,31 @@
</operation>
</cache>
+ <!-- TIFF cache on local disk (read/write) -->
+ <cache name="my_tiff_cache" type="tiff">
+ <template>cache_tiff/{tileset}/{grid}/L{z}/R{inv_y}/C{x}.tif</template>
+ </cache>
+
+ <!-- TIFF cache in URL (read-only) -->
+ <cache name="my_tiff_cache_rest_storage" type="tiff">
+ <template>https://example.com/cache_tiff/{tileset}/{grid}/L{z}/R{inv_y}/C{x}.tif</template>
+ <storage type="rest">
+ <header_file>/dev/shm/google-mapcache.header</header_file> <!-- optional -->
+ <timeout>10</timeout> <!-- optional -->
+ <connection_timeout>3</connection_timeout> <!-- optional -->
+ </storage>
+ </cache>
+
+ <!-- TIFF cache in Google Cloud Storage (read-only) -->
+ <cache name="my_tiff_cache_google_storage" type="tiff">
+ <template>https://storage.googleapis.com/cache_tiff/{tileset}/{grid}/L{z}/R{inv_y}/C{x}.tif</template>
+ <storage type="google">
+ <access>GOOGPGDWFDG345SDFGSD</access>
+ <secret>sdfgsdSDFwedfwefr2345324dfsGdsfgs</secret>
+ <timeout>10</timeout> <!-- optional -->
+ <connection_timeout>3</connection_timeout> <!-- optional -->
+ </storage>
+ </cache>
<!-- format
@@ -559,6 +584,7 @@
<quality>75</quality>
<photometric>RGB</photometric> <!-- RGB | YCBCR -->
+ <optimize>true</optimize> <!-- true | false | arithmetic -->
</format>
<format name="PNG_BEST" type ="PNG">
<compression>best</compression>
diff --git a/nginx/ngx_http_mapcache_module.c b/nginx/ngx_http_mapcache_module.c
index 36bd7a6..5301a3f 100644
--- a/nginx/ngx_http_mapcache_module.c
+++ b/nginx/ngx_http_mapcache_module.c
@@ -63,7 +63,6 @@ ngx_http_mapcache_create_conf(ngx_conf_t *cf)
mapcache_context *ctx = apr_pcalloc(process_pool, sizeof(mapcache_ngx_context));
ctx->pool = process_pool;
ctx->connection_pool = NULL;
- ctx->threadlock = NULL;
mapcache_context_init(ctx);
ctx->log = ngx_mapcache_context_log;
ctx->clone = ngx_mapcache_context_clone;
@@ -297,6 +296,10 @@ ngx_http_mapcache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,ctx->get_error_message(ctx));
return NGX_CONF_ERROR;
}
+ if(mapcache_config_services_enabled(ctx, ctx->config) <= 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "no mapcache <service>s configured/enabled, no point in continuing.");
+ return NGX_CONF_ERROR;
+ }
mapcache_connection_pool_create(&ctx->connection_pool,ctx->pool);
ctx->config->non_blocking = 1;
diff --git a/tests/data/world.tif b/tests/data/world.tif
new file mode 100644
index 0000000..2e6f9dd
Binary files /dev/null and b/tests/data/world.tif differ
diff --git a/tests/expected/wms_capabilities.xml b/tests/expected/wms_capabilities.xml
new file mode 100644
index 0000000..8df567f
--- /dev/null
+++ b/tests/expected/wms_capabilities.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE WMT_MS_Capabilities SYSTEM "http://schemas.opengis.net/wms/1.1.0/capabilities_1_1_0.dtd" [
+<!ELEMENT VendorSpecificCapabilities EMPTY>
+]>
+<WMT_MS_Capabilities version="1.1.1">
+ <Service>
+ <Name>OGC:WMS</Name>
+ <Title>no title set, add some in metadata</Title>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://localhost/mapcache/?"/>
+ </Service>
+ <Capability>
+ <Request>
+ <GetCapabilities>
+ <Format>application/vnd.ogc.wms_xml</Format>
+ <DCPType>
+ <HTTP>
+ <Get>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://localhost/mapcache/?"/>
+ </Get>
+ </HTTP>
+ </DCPType>
+ </GetCapabilities>
+ <GetMap>
+ <Format>image/png</Format>
+ <Format>image/jpeg</Format>
+ <DCPType>
+ <HTTP>
+ <Get>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://localhost/mapcache/?"/>
+ </Get>
+ </HTTP>
+ </DCPType>
+ </GetMap>
+ <GetFeatureInfo>
+ <Format>text/plain</Format>
+ <Format>application/vnd.ogc.gml</Format>
+ <DCPType>
+ <HTTP>
+ <Get>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://localhost/mapcache/?"/>
+ </Get>
+ </HTTP>
+ </DCPType>
+ </GetFeatureInfo>
+ </Request>
+ <Exception>
+ <Format>text/plain</Format>
+ </Exception>
+ <VendorSpecificCapabilities>
+ <TileSet>
+ <SRS>EPSG:3857</SRS>
+ <BoundingBox SRS="EPSG:3857" minx="-20037508.342789" miny="-20037508.342789" maxx="20037508.342789" maxy="20037508.342789"/>
+ <Resolutions>156543.03392804099712520838 78271.51696402048401068896 39135.75848201022745342925 19567.87924100512100267224 9783.93962050256050133612 4891.96981025128025066806 2445.98490512564012533403 1222.99245256282006266702 611.49622628141003133351 305.74811314070478829308 152.87405657035250783338 76.43702828517623970583 38.21851414258812695834 19.10925707129405992646 9.55462853564703173959 4.77731426782351586979 2.38865713391175793490 1.19432856695587897633 </Resolutions>
+ <Width>256</Width>
+ <Height>256</Height>
+ <Format>image/jpeg</Format>
+ <Layers>global</Layers>
+ <Styles/>
+ </TileSet>
+ </VendorSpecificCapabilities>
+ <Layer>
+ <Title>no title set, add some in metadata</Title>
+ <SRS>EPSG:3857</SRS>
+ <SRS>EPSG:4326</SRS>
+ <SRS>EPSG:900913</SRS>
+ <Layer cascaded="1" queryable="0">
+ <Name>global</Name>
+ <Title>global</Title>
+ <BoundingBox SRS="EPSG:3857" minx="-20037508.342789" miny="-20037508.342789" maxx="20037508.342789" maxy="20037508.342789"/>
+ <SRS>EPSG:3857</SRS>
+ <SRS>EPSG:900913</SRS>
+ </Layer>
+ </Layer>
+ </Capability>
+</WMT_MS_Capabilities>
diff --git a/tests/expected/wmts_capabilities.xml b/tests/expected/wmts_capabilities.xml
new file mode 100644
index 0000000..e756979
--- /dev/null
+++ b/tests/expected/wmts_capabilities.xml
@@ -0,0 +1,247 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Capabilities xmlns="http://www.opengis.net/wmts/1.0" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:gml="http://www.opengis.net/gml" xsi:schemaLocation="http://www.opengis.net/wmts/1.0 http://schemas.opengis.net/wmts/1.0/wmtsGetCapabilities_response.xsd" version="1.0.0">
+ <ows:ServiceIdentification>
+ <ows:ServiceType>OGC WMTS</ows:ServiceType>
+ <ows:ServiceTypeVersion>1.0.0</ows:ServiceTypeVersion>
+ </ows:ServiceIdentification>
+ <ows:ServiceProvider>
+ <ows:ServiceContact>
+ <ows:ContactInfo/>
+ </ows:ServiceContact>
+ </ows:ServiceProvider>
+ <ows:OperationsMetadata>
+ <ows:Operation name="GetCapabilities">
+ <ows:DCP>
+ <ows:HTTP>
+ <ows:Get xlink:href="http://localhost/mapcache/wmts?">
+ <ows:Constraint name="GetEncoding">
+ <ows:AllowedValues>
+ <ows:Value>KVP</ows:Value>
+ </ows:AllowedValues>
+ </ows:Constraint>
+ </ows:Get>
+ </ows:HTTP>
+ </ows:DCP>
+ </ows:Operation>
+ <ows:Operation name="GetTile">
+ <ows:DCP>
+ <ows:HTTP>
+ <ows:Get xlink:href="http://localhost/mapcache/wmts?">
+ <ows:Constraint name="GetEncoding">
+ <ows:AllowedValues>
+ <ows:Value>KVP</ows:Value>
+ </ows:AllowedValues>
+ </ows:Constraint>
+ </ows:Get>
+ </ows:HTTP>
+ </ows:DCP>
+ </ows:Operation>
+ <ows:Operation name="GetFeatureInfo">
+ <ows:DCP>
+ <ows:HTTP>
+ <ows:Get xlink:href="http://localhost/mapcache/wmts?">
+ <ows:Constraint name="GetEncoding">
+ <ows:AllowedValues>
+ <ows:Value>KVP</ows:Value>
+ </ows:AllowedValues>
+ </ows:Constraint>
+ </ows:Get>
+ </ows:HTTP>
+ </ows:DCP>
+ </ows:Operation>
+ </ows:OperationsMetadata>
+ <Contents>
+ <Layer>
+ <ows:Title>global</ows:Title>
+ <ows:Identifier>global</ows:Identifier>
+ <Style isDefault="true">
+ <ows:Identifier>default</ows:Identifier>
+ </Style>
+ <Format>image/jpeg</Format>
+ <TileMatrixSetLink>
+ <TileMatrixSet>GoogleMapsCompatible</TileMatrixSet>
+ </TileMatrixSetLink>
+ <ResourceURL format="image/jpeg" resourceType="tile" template="http://localhost/mapcache/wmts/1.0.0/global/default/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.jpg"/>
+ </Layer>
+ <TileMatrixSet>
+ <ows:Identifier>GoogleMapsCompatible</ows:Identifier>
+ <ows:BoundingBox crs="urn:ogc:def:crs:EPSG:6.3:3857">
+ <ows:LowerCorner>-20037508.342789 -20037508.342789</ows:LowerCorner>
+ <ows:UpperCorner>20037508.342789 20037508.342789</ows:UpperCorner>
+ </ows:BoundingBox>
+ <ows:SupportedCRS>urn:ogc:def:crs:EPSG:6.3:3857</ows:SupportedCRS>
+ <WellKnownScaleSet>urn:ogc:def:wkss:OGC:1.0:GoogleMapsCompatible</WellKnownScaleSet>
+ <TileMatrix>
+ <ows:Identifier>0</ows:Identifier>
+ <ScaleDenominator>559082264.02871787548065185547</ScaleDenominator>
+ <TopLeftCorner>-20037508.342789 20037508.342789</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>1</MatrixWidth>
+ <MatrixHeight>1</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>1</ows:Identifier>
+ <ScaleDenominator>279541132.01435887813568115234</ScaleDenominator>
+ <TopLeftCorner>-20037508.342789 20037508.342789</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>2</MatrixWidth>
+ <MatrixHeight>2</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>2</ows:Identifier>
+ <ScaleDenominator>139770566.00717940926551818848</ScaleDenominator>
+ <TopLeftCorner>-20037508.342789 20037508.342789</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>4</MatrixWidth>
+ <MatrixHeight>4</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>3</ows:Identifier>
+ <ScaleDenominator>69885283.00358971953392028809</ScaleDenominator>
+ <TopLeftCorner>-20037508.342789 20037508.342789</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>8</MatrixWidth>
+ <MatrixHeight>8</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>4</ows:Identifier>
+ <ScaleDenominator>34942641.50179485976696014404</ScaleDenominator>
+ <TopLeftCorner>-20037508.342789 20037508.342789</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>16</MatrixWidth>
+ <MatrixHeight>16</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>5</ows:Identifier>
+ <ScaleDenominator>17471320.75089742988348007202</ScaleDenominator>
+ <TopLeftCorner>-20037508.342789 20037508.342789</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>32</MatrixWidth>
+ <MatrixHeight>32</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>6</ows:Identifier>
+ <ScaleDenominator>8735660.37544871494174003601</ScaleDenominator>
+ <TopLeftCorner>-20037508.342789 20037508.342789</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>64</MatrixWidth>
+ <MatrixHeight>64</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>7</ows:Identifier>
+ <ScaleDenominator>4367830.18772435747087001801</ScaleDenominator>
+ <TopLeftCorner>-20037508.342789 20037508.342789</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>128</MatrixWidth>
+ <MatrixHeight>128</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>8</ows:Identifier>
+ <ScaleDenominator>2183915.09386217873543500900</ScaleDenominator>
+ <TopLeftCorner>-20037508.342789 20037508.342789</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>256</MatrixWidth>
+ <MatrixHeight>256</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>9</ows:Identifier>
+ <ScaleDenominator>1091957.54693108866922557354</ScaleDenominator>
+ <TopLeftCorner>-20037508.342789 20037508.342789</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>512</MatrixWidth>
+ <MatrixHeight>512</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>10</ows:Identifier>
+ <ScaleDenominator>545978.77346554468385875225</ScaleDenominator>
+ <TopLeftCorner>-20037508.342789 20037508.342789</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>1024</MatrixWidth>
+ <MatrixHeight>1024</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>11</ows:Identifier>
+ <ScaleDenominator>272989.38673277228372171521</ScaleDenominator>
+ <TopLeftCorner>-20037508.342789 20037508.342789</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>2048</MatrixWidth>
+ <MatrixHeight>2048</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>12</ows:Identifier>
+ <ScaleDenominator>136494.69336638617096468806</ScaleDenominator>
+ <TopLeftCorner>-20037508.342789 20037508.342789</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>4096</MatrixWidth>
+ <MatrixHeight>4096</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>13</ows:Identifier>
+ <ScaleDenominator>68247.34668319307093042880</ScaleDenominator>
+ <TopLeftCorner>-20037508.342789 20037508.342789</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>8192</MatrixWidth>
+ <MatrixHeight>8192</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>14</ows:Identifier>
+ <ScaleDenominator>34123.67334159654274117202</ScaleDenominator>
+ <TopLeftCorner>-20037508.342789 20037508.342789</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>16384</MatrixWidth>
+ <MatrixHeight>16384</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>15</ows:Identifier>
+ <ScaleDenominator>17061.83667079827137058601</ScaleDenominator>
+ <TopLeftCorner>-20037508.342789 20037508.342789</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>32768</MatrixWidth>
+ <MatrixHeight>32768</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>16</ows:Identifier>
+ <ScaleDenominator>8530.91833539913568529300</ScaleDenominator>
+ <TopLeftCorner>-20037508.342789 20037508.342789</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>65536</MatrixWidth>
+ <MatrixHeight>65536</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>17</ows:Identifier>
+ <ScaleDenominator>4265.45916769956784264650</ScaleDenominator>
+ <TopLeftCorner>-20037508.342789 20037508.342789</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>131072</MatrixWidth>
+ <MatrixHeight>131072</MatrixHeight>
+ </TileMatrix>
+ <TileMatrix>
+ <ows:Identifier>18</ows:Identifier>
+ <ScaleDenominator>2132.72958384978392132325</ScaleDenominator>
+ <TopLeftCorner>-20037508.342789 20037508.342789</TopLeftCorner>
+ <TileWidth>256</TileWidth>
+ <TileHeight>256</TileHeight>
+ <MatrixWidth>262144</MatrixWidth>
+ <MatrixHeight>262144</MatrixHeight>
+ </TileMatrix>
+ </TileMatrixSet>
+ </Contents>
+</Capabilities>
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
new file mode 100755
index 0000000..1026090
--- /dev/null
+++ b/tests/run_tests.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+# Project: MapCache
+# Purpose: MapCache tests
+# Author: Even Rouault
+#
+#*****************************************************************************
+# Copyright (c) 2017 Regents of the University of Minnesota.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies of this Software or works derived from this Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+#****************************************************************************/
+
+set -e
+
+MAPCACHE_CONF=/tmp/mc/mapcache.xml
+
+sudo rm -rf /tmp/mc/global
+mapcache_seed -c /tmp/mc/mapcache.xml -t global --force -z 0,1
+gdalinfo -checksum /tmp/mc/global/GoogleMapsCompatible/00/000/000/000/000/000/000.jpg | grep Checksum=20574 >/dev/null || (echo "Did not get expected checksum"; gdalinfo -checksum /tmp/mc/global/GoogleMapsCompatible/00/000/000/000/000/000/000.jpg; /bin/false)
+sudo rm -rf /tmp/mc/global
+
+curl -s "http://localhost/mapcache/?SERVICE=WMS&REQUEST=GetCapabilities" | xmllint --format - > /tmp/wms_capabilities.xml
+diff -u /tmp/wms_capabilities.xml expected
+
+curl -s "http://localhost/mapcache/wmts?SERVICE=WMTS&REQUEST=GetCapabilities" | xmllint --format - > /tmp/wmts_capabilities.xml
+diff -u /tmp/wmts_capabilities.xml expected
+
+curl -s "http://localhost/mapcache/wmts/1.0.0/global/default/GoogleMapsCompatible/0/0/0.jpg" > /tmp/0.jpg
+gdalinfo -checksum /tmp/0.jpg | grep Checksum=20574 >/dev/null || (echo "Did not get expected checksum"; gdalinfo -checksum /tmp/0.jpg; /bin/false)
+
+curl -s "http://localhost/mapcache/wmts/1.0.0/global/default/GoogleMapsCompatible/0/0/0.jpg" > /tmp/0_bis.jpg
+diff /tmp/0.jpg /tmp/0_bis.jpg
+
diff --git a/tests/travis_setup.sh b/tests/travis_setup.sh
new file mode 100755
index 0000000..e569264
--- /dev/null
+++ b/tests/travis_setup.sh
@@ -0,0 +1,65 @@
+#!/bin/sh
+
+# Project: MapCache
+# Purpose: MapCache tests
+# Author: Even Rouault
+#
+#*****************************************************************************
+# Copyright (c) 2017 Regents of the University of Minnesota.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies of this Software or works derived from this Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+#****************************************************************************/
+
+set -e
+
+mkdir /tmp/mc
+sudo chmod -R a+rw /tmp/mc
+
+MAPCACHE_CONF=/tmp/mc/mapcache.xml
+echo '<?xml version="1.0" encoding="UTF-8"?>' >> $MAPCACHE_CONF
+echo '<mapcache>' >> $MAPCACHE_CONF
+echo ' <source name="global-tif" type="gdal">' >> $MAPCACHE_CONF
+echo ' <data>/tmp/mc/world.tif</data>' >> $MAPCACHE_CONF
+echo ' </source>' >> $MAPCACHE_CONF
+echo ' <cache name="disk" type="disk">' >> $MAPCACHE_CONF
+echo ' <base>/tmp/mc</base>' >> $MAPCACHE_CONF
+echo ' </cache>' >> $MAPCACHE_CONF
+echo ' <tileset name="global">' >> $MAPCACHE_CONF
+echo ' <cache>disk</cache>' >> $MAPCACHE_CONF
+echo ' <source>global-tif</source>' >> $MAPCACHE_CONF
+echo ' <grid maxzoom="17">GoogleMapsCompatible</grid>' >> $MAPCACHE_CONF
+echo ' <format>JPEG</format>' >> $MAPCACHE_CONF
+echo ' <metatile>1 1</metatile>' >> $MAPCACHE_CONF
+echo ' </tileset>' >> $MAPCACHE_CONF
+echo ' <service type="wmts" enabled="true"/>' >> $MAPCACHE_CONF
+echo ' <service type="wms" enabled="true"/>' >> $MAPCACHE_CONF
+echo ' <log_level>debug</log_level>' >> $MAPCACHE_CONF
+echo '</mapcache>' >> $MAPCACHE_CONF
+
+cp data/world.tif /tmp/mc
+
+sudo su -c "echo 'LoadModule mapcache_module /usr/lib/apache2/modules/mod_mapcache.so' >> /etc/apache2/apache2.conf"
+sudo su -c "echo '<IfModule mapcache_module>' >> /etc/apache2/apache2.conf"
+sudo su -c "echo ' <Directory /tmp/mc>' >> /etc/apache2/apache2.conf"
+sudo su -c "echo ' Require all granted' >> /etc/apache2/apache2.conf"
+sudo su -c "echo ' </Directory>' >> /etc/apache2/apache2.conf"
+sudo su -c "echo ' MapCacheAlias /mapcache \"/tmp/mc/mapcache.xml\"' >> /etc/apache2/apache2.conf"
+sudo su -c "echo '</IfModule>' >> /etc/apache2/apache2.conf"
+
+sudo service apache2 restart
diff --git a/util/mapcache_seed.c b/util/mapcache_seed.c
index 56ac570..3bf90a6 100644
--- a/util/mapcache_seed.c
+++ b/util/mapcache_seed.c
@@ -71,7 +71,7 @@ mapcache_tileset *tileset;
mapcache_tileset *tileset_transfer;
mapcache_cfg *cfg;
mapcache_context ctx;
-apr_table_t *dimensions=NULL;
+apr_array_header_t *dimensions=NULL;
int minzoom=-1;
int maxzoom=-1;
mapcache_grid_link *grid_link;
@@ -83,7 +83,9 @@ int force = 0;
int sig_int_received = 0;
int error_detected = 0;
double percent_failed_allowed = 1.0;
-int n_metatiles_tot=0;
+int n_metatiles_tot = 0;
+int n_nodata_tot = 0;
+int rate_limit = 0;
FILE *failed_log = NULL, *retry_log = NULL;
#define FAIL_BACKLOG_COUNT 1000
@@ -95,7 +97,8 @@ typedef enum {
MAPCACHE_CMD_STOP,
MAPCACHE_CMD_DELETE,
MAPCACHE_CMD_SKIP,
- MAPCACHE_CMD_TRANSFER
+ MAPCACHE_CMD_TRANSFER,
+ MAPCACHE_CMD_STOP_RECURSION
} cmd;
typedef enum {
@@ -123,6 +126,7 @@ typedef enum {
struct seed_status {
s_status status;
int x,y,z;
+ int nodata;
char *msg;
};
@@ -138,6 +142,8 @@ cmd mode = MAPCACHE_CMD_SEED; /* the mode the utility will be running in: either
int push_queue(struct seed_cmd cmd)
{
struct seed_cmd *pcmd;
+ int retries=0;
+ int ret;
#ifdef USE_FORK
if(nprocesses > 1) {
struct msg_cmd mcmd;
@@ -152,12 +158,21 @@ int push_queue(struct seed_cmd cmd)
#endif
pcmd = calloc(1,sizeof(struct seed_cmd));
*pcmd = cmd;
- return apr_queue_push(work_queue,pcmd);
+ ret = apr_queue_push(work_queue,pcmd);
+ while (ret == APR_EINTR && retries < 10) {
+ retries++;
+ ret = apr_queue_push(work_queue,pcmd);
+ }
+ if(ret == APR_EINTR) {
+ printf("failed to push tile %d %d %d after 10 retries\n",cmd.z,cmd.y,cmd.x);
+ return APR_EGENERAL;
+ }
+ return ret;
}
int pop_queue(struct seed_cmd *cmd)
{
- int ret;
+ int ret,retries=0;
struct seed_cmd *pcmd;
#ifdef USE_FORK
@@ -173,6 +188,10 @@ int pop_queue(struct seed_cmd *cmd)
#endif
ret = apr_queue_pop(work_queue, (void**)&pcmd);
+ while(ret == APR_EINTR && retries<10) {
+ retries++;
+ ret = apr_queue_pop(work_queue, (void**)&pcmd);
+ }
if(ret == APR_SUCCESS) {
*cmd = *pcmd;
free(pcmd);
@@ -182,7 +201,7 @@ int pop_queue(struct seed_cmd *cmd)
int trypop_queue(struct seed_cmd *cmd)
{
- int ret;
+ int ret,retries=0;
struct seed_cmd *pcmd;
#ifdef USE_FORK
@@ -200,6 +219,10 @@ int trypop_queue(struct seed_cmd *cmd)
}
#endif
ret = apr_queue_trypop(work_queue,(void**)&pcmd);
+ while(ret == APR_EINTR && retries<10) {
+ retries++;
+ ret = apr_queue_trypop(work_queue,(void**)&pcmd);
+ }
if(ret == APR_SUCCESS) {
*cmd = *pcmd;
free(pcmd);
@@ -207,6 +230,9 @@ int trypop_queue(struct seed_cmd *cmd)
return ret;
}
+#define SEEDER_OPT_THREAD_DELAY 256
+#define SEEDER_OPT_RATE_LIMIT 257
+
static const apr_getopt_option_t seed_options[] = {
/* long-option, short-option, has-arg flag, description */
{ "config", 'c', TRUE, "configuration file (/path/to/mapcache.xml)"},
@@ -219,7 +245,7 @@ static const apr_getopt_option_t seed_options[] = {
{ "force", 'f', FALSE, "force tile recreation even if it already exists" },
{ "grid", 'g', TRUE, "grid to seed" },
{ "help", 'h', FALSE, "show help" },
- { "iteration-mode", 'i', TRUE, "either \"drill-down\" or \"level-by-level\". Default is to use drill-down for g, WGS84 and GoogleMapsCompatible grids, and level-by-level for others. Use this flag to override." },
+ { "iteration-mode", 'i', TRUE, "either \"drill-down\" or \"scanline\". Default is to use drill-down for g, WGS84 and GoogleMapsCompatible grids, and scanline for others. Use this flag to override." },
#ifdef USE_CLIPPERS
{ "ogr-layer", 'l', TRUE, "layer inside datasource"},
#endif
@@ -242,14 +268,19 @@ static const apr_getopt_option_t seed_options[] = {
#endif
{ "transfer", 'x', TRUE, "tileset to transfer" },
{ "zoom", 'z', TRUE, "min and max zoomlevels to seed, separated by a comma. eg 0,6" },
+ { "rate-limit", SEEDER_OPT_RATE_LIMIT, TRUE, "maximum number of tiles/second to seed"},
+ { "thread-delay", SEEDER_OPT_THREAD_DELAY, TRUE, "delay in seconds between rendering thread creation (ramp up)"},
{ NULL, 0, 0, NULL }
};
void handle_sig_int(int signal)
{
+#define signal_msg "SIGINT received, waiting for threads to finish\npress ctrl-C again to force terminate\n"
if(!sig_int_received) {
- fprintf(stderr,"SIGINT received, waiting for threads to finish\n");
- fprintf(stderr,"press ctrl-C again to force terminate, you might end up with locked tiles\n");
+ int err = write(2,signal_msg,strlen(signal_msg));
+ if(err) {
+ //nothing we can really do here
+ }
sig_int_received = 1;
} else {
exit(signal);
@@ -258,12 +289,12 @@ void handle_sig_int(int signal)
void seed_log(mapcache_context *ctx, mapcache_log_level level, char *msg, ...)
{
- if(verbose) {
+ if(level >= MAPCACHE_WARN || verbose) {
va_list args;
va_start(args,msg);
vfprintf(stderr,msg,args);
va_end(args);
- printf("\n");
+ fprintf(stderr,"\n");
}
}
@@ -274,7 +305,7 @@ void mapcache_context_seeding_log(mapcache_context *ctx, mapcache_log_level leve
va_start(args,msg);
vfprintf(stderr,msg,args);
va_end(args);
- printf("\n");
+ fprintf(stderr,"\n");
}
#ifdef USE_CLIPPERS
@@ -317,31 +348,68 @@ cmd examine_tile(mapcache_context *ctx, mapcache_tile *tile)
#ifdef USE_CLIPPERS
/* check we are in the requested features before checking the tile */
if(nClippers > 0 && ogr_features_intersect_tile(ctx,tile) == 0)
- return MAPCACHE_CMD_SKIP;
+ return MAPCACHE_CMD_STOP_RECURSION;
#endif
- tile_exists = force?0:tileset->_cache->tile_exists(ctx,tileset->_cache,tile);
+ if(mode != MAPCACHE_CMD_TRANSFER && force) {
+ if(mode == MAPCACHE_CMD_DELETE) {
+ tile_exists = 1;
+ } else {
+ tile_exists = 0;
+ }
+ } else {
+ int i;
+ if(tile->tileset->dimension_assembly_type != MAPCACHE_DIMENSION_ASSEMBLY_NONE) {
+ for(i=0; i<tile->dimensions->nelts; i++) {
+ mapcache_requested_dimension *rdim = APR_ARRAY_IDX(tile->dimensions,i,mapcache_requested_dimension*);
+ rdim->cached_value = rdim->requested_value;
+ }
+ } else {
+ if(tile->dimensions) {
+ mapcache_extent extent;
+ mapcache_grid_get_tile_extent(ctx,tile->grid_link->grid,tile->x,tile->y,tile->z,&extent);
+ for(i=0; i<tile->dimensions->nelts; i++) {
+ apr_array_header_t *rdim_vals;
+ mapcache_requested_dimension *rdim = APR_ARRAY_IDX(tile->dimensions,i,mapcache_requested_dimension*);
+ rdim_vals = rdim->dimension->get_entries_for_value(ctx,rdim->dimension,rdim->requested_value, tile->tileset, NULL, tile->grid_link->grid);
+ if(GC_HAS_ERROR(ctx)) {
+ return MAPCACHE_CMD_SKIP;
+ }
+ if(rdim_vals->nelts > 1) {
+ ctx->set_error(ctx,500,"dimension (%s) for tileset (%s) returned invalid number of subdimensions (1 expected)",rdim->dimension->name, tile->tileset->name);
+ return MAPCACHE_CMD_SKIP;
+ }
+ if(rdim_vals->nelts == 0) {
+ ctx->set_error(ctx,404,"dimension (%s) for tileset (%s) returned no subdimensions (1 expected)",rdim->dimension->name, tile->tileset->name);
+ return MAPCACHE_CMD_SKIP;
+ }
+ rdim->cached_value = APR_ARRAY_IDX(rdim_vals,0,char*);
+ }
+ }
+ }
+ tile_exists = mapcache_cache_tile_exists(ctx,tileset->_cache,tile);
+ }
/* if the tile exists and a time limit was specified, check the tile modification date */
if(tile_exists) {
if(age_limit) {
- if(tileset->_cache->tile_get(ctx,tileset->_cache, tile) == MAPCACHE_SUCCESS) {
+ if(mapcache_cache_tile_get(ctx,tileset->_cache, tile) == MAPCACHE_SUCCESS) {
if(tile->mtime && tile->mtime<age_limit) {
/* the tile modification time is older than the specified limit */
- if(mode == MAPCACHE_CMD_SEED || mode == MAPCACHE_CMD_TRANSFER) {
- mapcache_tileset_tile_delete(ctx,tile,MAPCACHE_TRUE);
- /* if we are in mode transfer, delete it from the dst tileset */
- if (mode == MAPCACHE_CMD_TRANSFER) {
- tile->tileset = tileset_transfer;
- if (tile->tileset->_cache->tile_exists(ctx,tile->tileset->_cache, tile)) {
- mapcache_tileset_tile_delete(ctx,tile,MAPCACHE_TRUE);
+ if(mode == MAPCACHE_CMD_SEED || mode == MAPCACHE_CMD_TRANSFER) {
+ mapcache_tileset_tile_delete(ctx,tile,MAPCACHE_TRUE);
+ /* if we are in mode transfer, delete it from the dst tileset */
+ if (mode == MAPCACHE_CMD_TRANSFER) {
+ tile->tileset = tileset_transfer;
+ if (mapcache_cache_tile_exists(ctx,tile->tileset->_cache, tile)) {
+ mapcache_tileset_tile_delete(ctx,tile,MAPCACHE_TRUE);
+ }
+ tile->tileset = tileset;
+ }
+ action = mode;
+ } else { //if(action == MAPCACHE_CMD_DELETE)
+ action = MAPCACHE_CMD_DELETE;
}
- tile->tileset = tileset;
- }
- action = mode;
- } else { //if(action == MAPCACHE_CMD_DELETE)
- action = MAPCACHE_CMD_DELETE;
- }
}
} else {
//BUG: tile_exists returned true, but tile_get returned a failure. not sure what to do.
@@ -355,7 +423,7 @@ cmd examine_tile(mapcache_context *ctx, mapcache_tile *tile)
/* the tile exists in the source tileset,
check if the tile exists in the destination cache */
tile->tileset = tileset_transfer;
- if (tile->tileset->_cache->tile_exists(ctx,tile->tileset->_cache, tile)) {
+ if (!force && mapcache_cache_tile_exists(ctx,tile->tileset->_cache, tile)) {
action = MAPCACHE_CMD_SKIP;
} else {
action = MAPCACHE_CMD_TRANSFER;
@@ -368,7 +436,7 @@ cmd examine_tile(mapcache_context *ctx, mapcache_tile *tile)
}
} else {
// the tile does not exist
- if(mode == MAPCACHE_CMD_SEED || mode == MAPCACHE_CMD_TRANSFER) {
+ if (mode == MAPCACHE_CMD_SEED) {
action = mode;
} else {
action = MAPCACHE_CMD_SKIP;
@@ -378,6 +446,27 @@ cmd examine_tile(mapcache_context *ctx, mapcache_tile *tile)
return action;
}
+double rate_limit_last_time = 0;
+double rate_limit_delay = 0.0;
+
+void rate_limit_sleep() {
+ if(rate_limit > 0) {
+ struct mctimeval now;
+ double now_time;
+ mapcache_gettimeofday(&now,NULL);
+ now_time = now.tv_sec + now.tv_usec / 1000000.0;
+
+ if((now_time - rate_limit_last_time) < rate_limit_delay) {
+ apr_sleep((int)((rate_limit_delay - (now_time - rate_limit_last_time)) * 1000000));
+ /*last_time = now_time + delay - (now_time - last_time); //now plus the time we slept */
+ rate_limit_last_time = rate_limit_delay + rate_limit_last_time;
+ } else {
+ rate_limit_last_time = now_time;
+ }
+
+ }
+}
+
void cmd_recurse(mapcache_context *cmd_ctx, mapcache_tile *tile)
{
cmd action;
@@ -404,8 +493,13 @@ void cmd_recurse(mapcache_context *cmd_ctx, mapcache_tile *tile)
cmd.y = tile->y;
cmd.z = tile->z;
cmd.command = action;
+ if(rate_limit > 0)
+ rate_limit_sleep();
push_queue(cmd);
}
+
+ if(action == MAPCACHE_CMD_STOP_RECURSION)
+ return;
//recurse into our 4 child metatiles
@@ -424,9 +518,9 @@ void cmd_recurse(mapcache_context *cmd_ctx, mapcache_tile *tile)
*/
- mapcache_grid_get_extent(cmd_ctx, grid_link->grid,
+ mapcache_grid_get_tile_extent(cmd_ctx, grid_link->grid,
curx, cury, curz, &bboxbl);
- mapcache_grid_get_extent(cmd_ctx, grid_link->grid,
+ mapcache_grid_get_tile_extent(cmd_ctx, grid_link->grid,
curx+tileset->metasize_x-1, cury+tileset->metasize_y-1, curz, &bboxtr);
epsilon = (bboxbl.maxx-bboxbl.minx)*0.01;
mapcache_grid_get_xy(cmd_ctx,grid_link->grid,
@@ -458,7 +552,7 @@ void cmd_recurse(mapcache_context *cmd_ctx, mapcache_tile *tile)
tile->z = curz;
}
-void cmd_worker()
+void feed_worker()
{
int n;
mapcache_tile *tile;
@@ -470,7 +564,11 @@ void cmd_worker()
if(nprocesses >= 1) nworkers = nprocesses;
apr_pool_create(&cmd_ctx.pool,ctx.pool);
tile = mapcache_tileset_tile_create(ctx.pool, tileset, grid_link);
- tile->dimensions = dimensions;
+ tile->dimensions = mapcache_requested_dimensions_clone(ctx.pool,dimensions);
+ if(rate_limit > 0) {
+ /* compute time between seed commands accounting for max rate-limit and current metasize */
+ rate_limit_delay = (tileset->metasize_x * tileset->metasize_y) / (double)rate_limit;
+ }
if(iteration_mode == MAPCACHE_ITERATION_DEPTH_FIRST) {
do {
tile->x = x;
@@ -518,6 +616,8 @@ void cmd_worker()
cmd.y = y;
cmd.z = z;
cmd.command = action;
+ if(rate_limit > 0)
+ rate_limit_sleep();
push_queue(cmd);
}
@@ -555,7 +655,9 @@ void seed_worker()
apr_pool_create(&seed_ctx.pool,ctx.pool);
apr_pool_create(&tpool,ctx.pool);
tile = mapcache_tileset_tile_create(tpool, tileset, grid_link);
- tile->dimensions = dimensions;
+ if(dimensions) {
+ tile->dimensions = mapcache_requested_dimensions_clone(tpool,dimensions);
+ }
while(1) {
struct seed_cmd cmd;
apr_status_t ret;
@@ -566,35 +668,51 @@ void seed_worker()
tile->x = cmd.x;
tile->y = cmd.y;
tile->z = cmd.z;
+ tile->nodata = 0;
+ tile->encoded_data = NULL;
+ tile->raw_image = NULL;
+ if(tile->dimensions) {
+ int i;
+ if(tileset->dimension_assembly_type == MAPCACHE_DIMENSION_ASSEMBLY_NONE) {
+ mapcache_extent extent;
+ mapcache_grid_get_tile_extent(&seed_ctx,tile->grid_link->grid,tile->x,tile->y,tile->z,&extent);
+ for(i=0; i<tile->dimensions->nelts; i++) {
+ apr_array_header_t *rdim_vals;
+ mapcache_requested_dimension *rdim = APR_ARRAY_IDX(tile->dimensions,i,mapcache_requested_dimension*);
+ rdim_vals = rdim->dimension->get_entries_for_value(&seed_ctx,rdim->dimension,rdim->requested_value, tile->tileset, NULL, tile->grid_link->grid);
+ GC_CHECK_ERROR(&seed_ctx);
+ if(rdim_vals->nelts > 1) {
+ seed_ctx.set_error(&seed_ctx,500,"dimension (%s) for tileset (%s) returned invalid number of subdimensions (1 expected)",rdim->dimension->name, tile->tileset->name);
+ return;
+ }
+ if(rdim_vals->nelts == 0) {
+ seed_ctx.set_error(&seed_ctx,404,"dimension (%s) for tileset (%s) returned no subdimensions (1 expected)",rdim->dimension->name, tile->tileset->name);
+ return;
+ }
+ rdim->cached_value = APR_ARRAY_IDX(rdim_vals,0,char*);
+ }
+ } else {
+ for(i=0; i<tile->dimensions->nelts; i++) {
+ mapcache_requested_dimension *rdim = APR_ARRAY_IDX(tile->dimensions,i,mapcache_requested_dimension*);
+ rdim->cached_value = NULL;
+ }
+ }
+ }
if(cmd.command == MAPCACHE_CMD_SEED) {
- /* aquire a lock on the metatile ?*/
- mapcache_metatile *mt = mapcache_tileset_metatile_get(&seed_ctx, tile);
- void *lock;
- int isLocked = mapcache_lock_or_wait_for_resource(&seed_ctx, seed_ctx.config->locker, mapcache_tileset_metatile_resource_key(&seed_ctx,mt), &lock);
- if(isLocked == MAPCACHE_TRUE) {
+ if(!tile->dimensions || tileset->dimension_assembly_type == MAPCACHE_DIMENSION_ASSEMBLY_NONE) {
+ mapcache_metatile *mt = mapcache_tileset_metatile_get(&seed_ctx, tile);
/* this will query the source to create the tiles, and save them to the cache */
mapcache_tileset_render_metatile(&seed_ctx, mt);
- if(GC_HAS_ERROR(&seed_ctx)) {
- /* temporarily clear error state so we don't mess up with error handling in the locker */
- void *error;
- seed_ctx.pop_errors(&seed_ctx,&error);
- mapcache_unlock_resource(&seed_ctx, seed_ctx.config->locker, mapcache_tileset_metatile_resource_key(&seed_ctx,mt), lock);
- seed_ctx.push_errors(&seed_ctx,error);
- } else {
- mapcache_unlock_resource(&seed_ctx, seed_ctx.config->locker, mapcache_tileset_metatile_resource_key(&seed_ctx,mt), lock);
- }
+ } else {
+ mapcache_tileset_tile_set_get_with_subdimensions(&seed_ctx,tile);
}
} else if (cmd.command == MAPCACHE_CMD_TRANSFER) {
- int i;
- mapcache_metatile *mt = mapcache_tileset_metatile_get(&seed_ctx, tile);
- for (i = 0; i < mt->ntiles; i++) {
- mapcache_tile *subtile = &mt->tiles[i];
- int cache_ret;
- cache_ret = tileset->_cache->tile_get(&seed_ctx, tileset->_cache, subtile);
- if(cache_ret == MAPCACHE_SUCCESS && !GC_HAS_ERROR(&seed_ctx)) {
- subtile->tileset = tileset_transfer;
- subtile->tileset->_cache->tile_set(&seed_ctx, subtile->tileset->_cache, subtile);
- }
+ mapcache_tileset_tile_get(&seed_ctx, tile);
+ if(!tile->nodata && !GC_HAS_ERROR(&seed_ctx)) {
+ mapcache_tileset *tmp_tileset = tile->tileset;
+ tile->tileset = tileset_transfer;
+ mapcache_cache_tile_set(&seed_ctx, tile->tileset->_cache, tile);
+ tile->tileset = tmp_tileset;
}
} else { //CMD_DELETE
mapcache_tileset_tile_delete(&seed_ctx,tile,MAPCACHE_TRUE);
@@ -602,9 +720,11 @@ void seed_worker()
{
struct seed_status *st = calloc(1,sizeof(struct seed_status));
+ int retries=0;
st->x=tile->x;
st->y=tile->y;
st->z=tile->z;
+ st->nodata = tile->nodata;
if(seed_ctx.get_error(&seed_ctx)) {
st->status = MAPCACHE_STATUS_FAIL;
st->msg = strdup(seed_ctx.get_error_message(&seed_ctx));
@@ -613,6 +733,14 @@ void seed_worker()
st->status = MAPCACHE_STATUS_OK;
}
ret = apr_queue_push(log_queue,(void*)st);
+ while( ret == APR_EINTR && retries < 10) {
+ retries++;
+ ret = apr_queue_push(log_queue,(void*)st);
+ }
+ if( ret == APR_EINTR) {
+ printf("FATAL ERROR: unable to log progress after 10 retries, aborting\n");
+ break;
+ }
if(ret != APR_SUCCESS)
{
printf("FATAL ERROR: unable to log progress\n");
@@ -628,11 +756,18 @@ int seed_process() {
return 0;
}
#endif
+
static void* APR_THREAD_FUNC seed_thread(apr_thread_t *thread, void *data) {
seed_worker();
return NULL;
}
+static void* APR_THREAD_FUNC feed_thread_fn(apr_thread_t *thread, void *data) {
+ //start the thread that will populate the queue.
+ feed_worker();
+ return NULL;
+}
+
static void* APR_THREAD_FUNC log_thread_fn(apr_thread_t *thread, void *data) {
size_t cur;
double last_time;
@@ -646,14 +781,22 @@ static void* APR_THREAD_FUNC log_thread_fn(apr_thread_t *thread, void *data) {
cur=0;
last_time=0;
while(1) {
+ int retries = 0;
struct seed_status *st;
apr_status_t ret = apr_queue_pop(log_queue, (void**)&st);
+ while(ret == APR_EINTR && retries<10) {
+ retries++;
+ ret = apr_queue_pop(log_queue, (void**)&st);
+ }
if(ret != APR_SUCCESS || !st) break;
if(st->status == MAPCACHE_STATUS_FINISHED)
return NULL;
if(st->status == MAPCACHE_STATUS_OK) {
failed[cur]=0;
n_metatiles_tot++;
+ if(st->nodata) {
+ n_nodata_tot++;
+ }
if(!quiet) {
struct mctimeval now;
mapcache_gettimeofday(&now,NULL);
@@ -682,7 +825,6 @@ static void* APR_THREAD_FUNC log_thread_fn(apr_thread_t *thread, void *data) {
if(pct > percent_failed_allowed) {
ctx.log(&ctx, MAPCACHE_ERROR, "aborting seed as %.1f%% of the last %d requests failed\n", pct, FAIL_BACKLOG_COUNT);
error_detected = 1;
- return NULL;
}
}
if(st->msg) free(st->msg);
@@ -736,10 +878,18 @@ int usage(const char *progname, char *msg, ...)
printf("usage: %s options\n",progname);
while(seed_options[i].name) {
- if(seed_options[i].has_arg==TRUE) {
- printf("-%c|--%s [value]: %s\n",seed_options[i].optch,seed_options[i].name, seed_options[i].description);
+ if(seed_options[i].optch<256) {
+ if(seed_options[i].has_arg==TRUE) {
+ printf("-%c|--%s [value]: %s\n",seed_options[i].optch,seed_options[i].name, seed_options[i].description);
+ } else {
+ printf("-%c|--%s: %s\n",seed_options[i].optch,seed_options[i].name, seed_options[i].description);
+ }
} else {
- printf("-%c|--%s: %s\n",seed_options[i].optch,seed_options[i].name, seed_options[i].description);
+ if(seed_options[i].has_arg==TRUE) {
+ printf(" --%s [value]: %s\n",seed_options[i].name, seed_options[i].description);
+ } else {
+ printf(" --%s: %s\n",seed_options[i].name, seed_options[i].description);
+ }
}
i++;
}
@@ -757,9 +907,8 @@ int main(int argc, const char **argv)
/* initialize apr_getopt_t */
apr_getopt_t *opt;
const char *configfile=NULL;
- apr_thread_t **threads;
- apr_thread_t *log_thread;
- apr_threadattr_t *thread_attrs;
+ apr_thread_t **seed_threads;
+ apr_thread_t *log_thread,*feed_thread;
const char *tileset_name=NULL;
const char *tileset_transfer_name=NULL;
const char *grid_name = NULL;
@@ -776,6 +925,7 @@ int main(int argc, const char **argv)
int *metasizes = NULL;//[2];
int metax=-1,metay=-1;
double *extent_array = NULL;
+ double thread_delay = 0.0;
#ifdef USE_CLIPPERS
OGRFeatureH hFeature;
@@ -832,10 +982,10 @@ int main(int argc, const char **argv)
case 'i':
if(!strcmp(optarg,"drill-down")) {
iteration_mode = MAPCACHE_ITERATION_DEPTH_FIRST;
- } else if(!strcmp(optarg,"level-by-level")) {
+ } else if(!strcmp(optarg,"level-by-level") || !strcmp(optarg, "scanline")) {
iteration_mode = MAPCACHE_ITERATION_LEVEL_FIRST;
} else {
- return usage(argv[0],"invalid iteration mode, expecting \"drill-down\" or \"level-by-level\"");
+ return usage(argv[0],"invalid iteration mode, expecting \"drill-down\" or \"scanline\"");
}
break;
case 'L':
@@ -930,6 +1080,16 @@ int main(int argc, const char **argv)
}
apr_table_set(argdimensions,dimkey,dimvalue);
break;
+ case SEEDER_OPT_THREAD_DELAY:
+ thread_delay = strtod(optarg, NULL);
+ if(thread_delay < 0.0 )
+ return usage(argv[0], "failed to parse thread-delay, expecting positive number of seconds");
+ break;
+ case SEEDER_OPT_RATE_LIMIT:
+ rate_limit = (int)strtol(optarg, NULL, 10);
+ if(rate_limit <= 0 )
+ return usage(argv[0], "failed to parse rate-limit, expecting positive number of tiles per seconds");
+ break;
#ifdef USE_CLIPPERS
case 'd':
ogr_datasource = optarg;
@@ -1064,10 +1224,16 @@ int main(int argc, const char **argv)
if(!tileset) {
return usage(argv[0], "tileset not found in configuration");
}
- if(tileset->read_only) {
- printf("tileset %s is read-only, switching it to read-write for seeding\n",tileset_name);
+ if(tileset->read_only && mode != MAPCACHE_CMD_TRANSFER) {
+ printf("tileset (%s) is read-only, switching it to read-write for seeding\n",tileset_name);
tileset->read_only = 0;
}
+ if( mode == MAPCACHE_CMD_TRANSFER ) {
+ if( tileset->read_only == 0 ) {
+ printf("switching tileset (%s) to read-only as we are in transfer mode\n",tileset_name);
+ }
+ tileset->read_only = 1;
+ }
if( ! grid_name ) {
grid_link = APR_ARRAY_IDX(tileset->grid_links,0,mapcache_grid_link*);
} else {
@@ -1134,7 +1300,7 @@ int main(int argc, const char **argv)
if(minzoom<grid_link->minz) minzoom = grid_link->minz;
if(maxzoom>= grid_link->maxz) maxzoom = grid_link->maxz - 1;
if(grid_link->outofzoom_strategy != MAPCACHE_OUTOFZOOM_NOTCONFIGURED && maxzoom > grid_link->max_cached_zoom) {
- maxzoom = grid_link->max_cached_zoom;
+ return usage(argv[0],"requested maxzoom %d is higher than configured max-cached-zoom %d for grid",maxzoom,grid_link->max_cached_zoom);
}
/* adjust metasize */
@@ -1146,14 +1312,14 @@ int main(int argc, const char **argv)
/* ensure our metasize is a power of 2 in drill down mode */
if(iteration_mode == MAPCACHE_ITERATION_DEPTH_FIRST) {
if(!isPowerOfTwo(tileset->metasize_x) || !isPowerOfTwo(tileset->metasize_y)) {
- return usage(argv[0],"metatile size is not set to a power of two and iteration mode set to \"drill-down\", rerun with e.g -M 8,8, or force iteration mode to \"level-by-level\"");
+ return usage(argv[0],"metatile size is not set to a power of two and iteration mode set to \"drill-down\", rerun with e.g -M 8,8, or force iteration mode to \"scanline\"");
}
}
if(cache_override) {
mapcache_cache *co = mapcache_configuration_get_cache(cfg, cache_override);
if(!co) {
- return usage(argv[0], "overrided cache\"%s\" to not found in configuration", cache_override);
+ return usage(argv[0], "overridden cache\"%s\" not found in configuration", cache_override);
} else {
tileset->_cache = co;
}
@@ -1162,6 +1328,7 @@ int main(int argc, const char **argv)
}
if (mode == MAPCACHE_CMD_TRANSFER) {
+ tileset->metasize_x = tileset->metasize_y = 1;
if (!tileset_transfer_name)
return usage(argv[0],"tileset where tiles should be transferred to not specified");
@@ -1211,75 +1378,55 @@ int main(int argc, const char **argv)
}
/* validate the supplied dimensions */
- if (!apr_is_empty_array(tileset->dimensions) || tileset->timedimension) {
+ if (!apr_is_empty_array(tileset->dimensions)) {
int i;
const char *value;
- dimensions = apr_table_make(ctx.pool,3);
- if (!apr_is_empty_array(tileset->dimensions)) {
- for(i=0; i<tileset->dimensions->nelts; i++) {
- mapcache_dimension *dimension = APR_ARRAY_IDX(tileset->dimensions,i,mapcache_dimension*);
- if((value = (char*)apr_table_get(argdimensions,dimension->name)) != NULL) {
- char *tmpval = apr_pstrdup(ctx.pool,value);
- int ok = dimension->validate(&ctx,dimension,&tmpval);
- if(GC_HAS_ERROR(&ctx) || ok != MAPCACHE_SUCCESS ) {
- return usage(argv[0],"failed to validate dimension");
- return 1;
- } else {
- /* validate may have changed the dimension value, so set that value into the dimensions table */
- apr_table_setn(dimensions,dimension->name,tmpval);
- }
- } else {
- /* a dimension was not specified on the command line, add the default value */
- apr_table_setn(dimensions, dimension->name, dimension->default_value);
- }
+ dimensions = apr_array_make(ctx.pool,tileset->dimensions->nelts,sizeof(mapcache_requested_dimension*));
+ for(i=0; i<tileset->dimensions->nelts; i++) {
+ mapcache_dimension *dimension = APR_ARRAY_IDX(tileset->dimensions,i,mapcache_dimension*);
+ mapcache_requested_dimension *rdim = apr_pcalloc(ctx.pool,sizeof(mapcache_requested_dimension));
+ rdim->dimension = dimension;
+ if((value = apr_table_get(argdimensions,dimension->name)) == NULL) {
+ value = dimension->default_value;
}
+ rdim->requested_value = apr_pstrdup(ctx.pool,value);
+ APR_ARRAY_PUSH(dimensions,mapcache_requested_dimension*)=rdim;
}
- if(tileset->timedimension) {
- if((value = (char*)apr_table_get(argdimensions,tileset->timedimension->key)) != NULL) {
- apr_array_header_t *timedim_selected = mapcache_timedimension_get_entries_for_value(&ctx,tileset->timedimension, tileset, grid_link->grid, extent, value);
- if(GC_HAS_ERROR(&ctx) || !timedim_selected) {
- return usage(argv[0],"failed to validate time dimension");
- }
- if(timedim_selected->nelts == 0) {
- return usage(argv[0],"Time dimension %s=%s returns no configured entry",tileset->timedimension->key,
- value);
- }
- if(timedim_selected->nelts > 1) {
- return usage(argv[0],"Time dimension %s=%s returns more than 1 configured entries",tileset->timedimension->key,
- value);
- }
- apr_table_set(dimensions,tileset->timedimension->key,APR_ARRAY_IDX(timedim_selected,0,char*));
- } else {
- return usage(argv[0],"tileset references a TIME dimension, but none supplied on commandline. (hint: -D %s=<timestamp>",tileset->timedimension->key);
-
+ if(tileset->dimension_assembly_type != MAPCACHE_DIMENSION_ASSEMBLY_NONE) {
+ if(!tileset->store_dimension_assemblies) {
+ return usage(argv[0],"cannot seed a layer with dimension assembling and no caching of resulting assembly");
+ }
+ if(tileset->metasize_x != 1 || tileset->metasize_y != 1) {
+ return usage(argv[0],"cannot seed a layer with dimension assembling and metatiling enabled (hint: rerun with -M 1,1 to disable metatiling");
}
}
}
+
+ if(nthreads == 0 && nprocesses == 0) {
+ nthreads = 1;
+ }
+ if(nthreads >= 1 && nprocesses >= 1) {
+ return usage(argv[0],"cannot set both nthreads and nprocesses");
+ }
+
{
/* start the logging thread */
//create the queue where the seeding statuses will be put
apr_threadattr_t *log_thread_attrs;
- apr_queue_create(&log_queue,2,ctx.pool);
+ apr_queue_create(&log_queue,MAPCACHE_MAX(nthreads,nprocesses),ctx.pool);
//start the rendering threads.
apr_threadattr_create(&log_thread_attrs, ctx.pool);
apr_thread_create(&log_thread, log_thread_attrs, log_thread_fn, NULL, ctx.pool);
}
-
- if(nthreads == 0 && nprocesses == 0) {
- nthreads = 1;
- }
- if(nthreads >= 1 && nprocesses >= 1) {
- return usage(argv[0],"cannot set both nthreads and nprocesses");
- }
+
if(nprocesses > 1) {
#ifdef USE_FORK
key_t key;
int i;
pid_t *pids = malloc(nprocesses*sizeof(pid_t));
struct msqid_ds queue_ds;
- ctx.threadlock = NULL;
key = ftok(argv[0], 'B');
if ((msqid = msgget(key, 0644 | IPC_CREAT|S_IRUSR|S_IWUSR)) == -1) {
return usage(argv[0],"failed to create sysv ipc message queue");
@@ -1314,7 +1461,7 @@ int main(int argc, const char **argv)
pids[i] = pid;
}
}
- cmd_worker();
+ feed_worker();
for(i=0; i<nprocesses; i++) {
int stat_loc;
waitpid(pids[i],&stat_loc,0);
@@ -1324,26 +1471,48 @@ int main(int argc, const char **argv)
return usage(argv[0],"bug: multi process support not available");
#endif
} else {
- //start the thread that will populate the queue.
- apr_thread_mutex_create((apr_thread_mutex_t**)&ctx.threadlock,APR_THREAD_MUTEX_DEFAULT,ctx.pool);
+ apr_threadattr_t *seed_thread_attrs;
//create the queue where tile requests will be put
apr_queue_create(&work_queue,nthreads,ctx.pool);
+ {
+ /* start the feeding thread */
+ apr_threadattr_t *feed_thread_attrs;
+ //start the rendering threads.
+ apr_threadattr_create(&feed_thread_attrs, ctx.pool);
+ apr_thread_create(&feed_thread, feed_thread_attrs, feed_thread_fn, NULL, ctx.pool);
+ }
+
+
+
//start the rendering threads.
- apr_threadattr_create(&thread_attrs, ctx.pool);
- threads = (apr_thread_t**)apr_pcalloc(ctx.pool, nthreads*sizeof(apr_thread_t*));
+ apr_threadattr_create(&seed_thread_attrs, ctx.pool);
+ seed_threads = (apr_thread_t**)apr_pcalloc(ctx.pool, nthreads*sizeof(apr_thread_t*));
for(n=0; n<nthreads; n++) {
- apr_thread_create(&threads[n], thread_attrs, seed_thread, NULL, ctx.pool);
+ if(n && thread_delay > 0) {
+ apr_sleep((int)(thread_delay * 1000000));
+ }
+ apr_thread_create(&seed_threads[n], seed_thread_attrs, seed_thread, NULL, ctx.pool);
}
- cmd_worker();
+
+
+ //the worker has finished generating the list of tiles to be seeded, now wait for the rendering threads to finish
for(n=0; n<nthreads; n++) {
- apr_thread_join(&rv, threads[n]);
+ apr_thread_join(&rv, seed_threads[n]);
}
+
+ apr_thread_join(&rv, feed_thread);
}
{
+ int retries=0;
+ int ret;
struct seed_status *st = calloc(1,sizeof(struct seed_status));
st->status = MAPCACHE_STATUS_FINISHED;
- apr_queue_push(log_queue,(void*)st);
+ ret = apr_queue_push(log_queue,(void*)st);
+ while (ret == APR_EINTR && retries<10) {
+ retries++;
+ ret = apr_queue_push(log_queue,(void*)st);
+ }
apr_thread_join(&rv, log_thread);
}
@@ -1351,10 +1520,17 @@ int main(int argc, const char **argv)
struct mctimeval now_t;
float duration;
int ntilestot = n_metatiles_tot*tileset->metasize_x*tileset->metasize_y;
+ int nnodatatot = n_nodata_tot*tileset->metasize_x*tileset->metasize_y;
mapcache_gettimeofday(&now_t,NULL);
duration = ((now_t.tv_sec-starttime.tv_sec)*1000000+(now_t.tv_usec-starttime.tv_usec))/1000000.0;
- printf("\nseeded %d metatiles (%d tiles) in %.1f seconds at %.1f tiles/sec\n",n_metatiles_tot, ntilestot, duration, ntilestot/duration);
+ printf("\nseeded %d metatiles (%d total tiles, %d non-empty tiles) in %.1f seconds at %.1f tiles/sec (%.1f non-empty tiles/sec)\n",
+ n_metatiles_tot,
+ ntilestot,
+ ntilestot-nnodatatot,
+ duration,
+ ntilestot/duration,
+ (ntilestot-nnodatatot)/duration);
} else {
if(!error_detected) {
printf("0 tiles needed to be seeded, exiting\n");
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/mapcache.git
More information about the Pkg-grass-devel
mailing list