pax_global_header00006660000000000000000000000064125556766210014531gustar00rootroot0000000000000052 comment=3a89c996b23d58e24d1162280cc13498566bbede mapcache-1.4.0/000077500000000000000000000000001255567662100132745ustar00rootroot00000000000000mapcache-1.4.0/.gitignore000066400000000000000000000000651255567662100152650ustar00rootroot00000000000000.*.swp nbproject/ /build/ /build_vagrant/ /.vagrant/ mapcache-1.4.0/CMakeLists.txt000066400000000000000000000231661255567662100160440ustar00rootroot00000000000000cmake_minimum_required (VERSION 2.6) project (MapCache) include(CheckFunctionExists) include(CheckIncludeFile) include(CheckCSourceCompiles) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") add_definitions(-DDEBUG) endif () set (MAPCACHE_VERSION_MAJOR 1) set (MAPCACHE_VERSION_MINOR 4) set (MAPCACHE_VERSION_REVISION 0) if(NOT DEFINED CMAKE_INSTALL_LIBDIR) set(CMAKE_INSTALL_LIBDIR lib) endif(NOT DEFINED CMAKE_INSTALL_LIBDIR) if(NOT DEFINED CMAKE_INSTALL_BINDIR) set(CMAKE_INSTALL_BINDIR bin) endif(NOT DEFINED CMAKE_INSTALL_BINDIR) MATH(EXPR MAPCACHE_IS_DEV_VERSION "1-${MAPCACHE_VERSION_MINOR}%2") if(MAPCACHE_IS_DEV_VERSION) set (MAPCACHE_VERSION_STRING "${MAPCACHE_VERSION_MAJOR}.${MAPCACHE_VERSION_MINOR}.${MAPCACHE_VERSION_REVISION}") else(MAPCACHE_IS_DEV_VERSION) set (MAPCACHE_VERSION_STRING "${MAPCACHE_VERSION_MAJOR}.${MAPCACHE_VERSION_MINOR}dev") endif(MAPCACHE_IS_DEV_VERSION) MATH(EXPR MAPCACHE_VERSION_NUM "${MAPCACHE_VERSION_MAJOR}*10000+${MAPCACHE_VERSION_MINOR}*100+${MAPCACHE_VERSION_REVISION}") SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH}) if (APPLE) set(CMAKE_FIND_FRAMEWORK "LAST") endif (APPLE) macro( report_optional_not_found component ) message(SEND_ERROR "${component} library/component/dependency could not be found. HINTS: - disable ${component} support by adding -DWITH_${component}=0 - add the ${component} install directory to the CMAKE_PREFIX_PATH variable (-DCMAKE_PREFIX_PATH=\"/path/to/${component}-install-dir;/path/to/other/dirs\"") endmacro() macro( report_mandatory_not_found component ) message(SEND_ERROR "${component} library/component could not be found and is a mandatory dependency HINT: - add the ${component} install directory to the CMAKE_PREFIX_PATH variable (-DCMAKE_PREFIX_PATH=\"/path/to/${component}-install-dir;/path/to/other/dirs\"") endmacro() macro( report_dependency_error component dependency) message(SEND_ERROR "${component} support requires ${dependency} support, however ${dependency} support has been disabled. HINTS: - re-run with -DWITH_${dependency}=1 (or without -DWITH_${dependency}=0) - disable ${component} support by adding -DWITH_${component}=0" ) endmacro() check_function_exists("strncasecmp" HAVE_STRNCASECMP) check_function_exists("symlink" HAVE_SYMLINK) set(CMAKE_SKIP_BUILD_RPATH FALSE) if(APPLE) set(CMAKE_MACOSX_RPATH ON) endif() set(CMAKE_LINK_INTERFACE_LIBRARY "") file(GLOB mapcache_SOURCES lib/*.c ) add_library(mapcache SHARED ${mapcache_SOURCES}) set_target_properties(mapcache PROPERTIES VERSION ${MAPCACHE_VERSION_STRING} SOVERSION 1 ) #options suported by the cmake builder option(WITH_PIXMAN "Use pixman for SSE optimized image manipulations" ON) option(WITH_SQLITE "Use sqlite as a cache backend" ON) option(WITH_BERKELEY_DB "Use Berkeley DB as a cache backend" OFF) option(WITH_MEMCACHE "Use memcache as a cache backend (requires recent apr-util)" OFF) option(WITH_TIFF "Use TIFFs as a cache backend" OFF) option(WITH_TIFF_WRITE_SUPPORT "Enable (experimental) support for writable TIFF cache backends" OFF) option(WITH_GEOTIFF "Allow GeoTIFF metadata creation for TIFF cache backends" OFF) 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) find_package(PNG) if(PNG_FOUND) include_directories(${PNG_INCLUDE_DIR}) target_link_libraries(mapcache ${PNG_LIBRARY}) else(PNG_FOUND) report_mandatory_not_found(PNG) endif(PNG_FOUND) find_package(JPEG) if(JPEG_FOUND) include_directories(${JPEG_INCLUDE_DIR}) target_link_libraries(mapcache ${JPEG_LIBRARY}) else(JPEG_FOUND) endif(JPEG_FOUND) find_package(CURL) if(CURL_FOUND) include_directories(${CURL_INCLUDE_DIR}) target_link_libraries(mapcache ${CURL_LIBRARY}) else(CURL_FOUND) report_mandatory_not_found(CURL) endif(CURL_FOUND) find_package(APR) if(APR_FOUND) include_directories(${APR_INCLUDE_DIR} ${APU_INCLUDE_DIR}) target_link_libraries(mapcache ${APR_LIBRARY} ${APU_LIBRARY}) if(DEFINED APR_CPPFLAGS) add_definitions("${APR_CPPFLAGS}") endif(DEFINED APR_CPPFLAGS) else(APR_FOUND) report_mandatory_not_found(APR) endif(APR_FOUND) if(WITH_MEMCACHE) include(CheckSymbolExists) FIND_PATH(tmp_APU_MEMCACHE_DIR NAMES apr_memcache.h HINTS ${APU_INCLUDE_DIR} ) if(tmp_APU_MEMCACHE_DIR) set(CMAKE_REQUIRED_INCLUDE ${APR_INCLUDE_DIR} ${APU_INCLUDE_DIR}) set(CMAKE_REQUIRED_LIBRARIES ${APR_LIBRARY} ${APU_LIBRARY}) check_symbol_exists(apr_memcache_hash "${tmp_APU_MEMCACHE_DIR}/apr_memcache.h" tmp_MEMCACHE_USABLE) if(tmp_MEMCACHE_USABLE) set(USE_MEMCACHE 1) else(tmp_MEMCACHE_USABLE) MESSAGE(SEND_ERROR "apr_memcache.h found, but seems too old (missing apr_memcache_hash function)") report_optional_not_found(MEMCACHE) endif(tmp_MEMCACHE_USABLE) else(tmp_APU_MEMCACHE_DIR) MESSAGE(SEND_ERROR "apr_memcache.h not found, your apr-util version may be configured without memcache support") report_optional_not_found(MEMCACHE) endif(tmp_APU_MEMCACHE_DIR) endif(WITH_MEMCACHE) if(WITH_PIXMAN) find_package(Pixman) if(PIXMAN_FOUND) include_directories(${PIXMAN_INCLUDE_DIR}) target_link_libraries(mapcache ${PIXMAN_LIBRARY}) set (USE_PIXMAN 1) else(PIXMAN_FOUND) report_optional_not_found(PIXMAN) endif(PIXMAN_FOUND) endif (WITH_PIXMAN) if(WITH_PCRE) find_package(PCRE) if(PCRE_FOUND) include_directories(${PCRE_INCLUDE_DIR}) target_link_libraries(mapcache ${PCRE_LIBRARY}) set (USE_PCRE 1) else(PCRE_FOUND) report_optional_not_found(PCRE) endif(PCRE_FOUND) endif (WITH_PCRE) if(WITH_SQLITE) find_package(SQLITE) if(SQLITE_FOUND) include_directories(${SQLITE_INCLUDE_DIR}) target_link_libraries(mapcache ${SQLITE_LIBRARY}) set (USE_SQLITE 1) else(SQLITE_FOUND) report_optional_not_found(SQLITE) endif(SQLITE_FOUND) endif (WITH_SQLITE) if(WITH_BERKELEY_DB) if(NOT BERKELEYDB_FIND_VERSION) set(BERKELEYDB_FIND_VERSION "4.6") endif(NOT BERKELEYDB_FIND_VERSION) find_package(BerkeleyDB) if(BERKELEYDB_FOUND) include_directories(${BERKELEYDB_INCLUDE_DIR}) target_link_libraries(mapcache ${BERKELEYDB_LIBRARY}) set (USE_BDB 1) else(BERKELEYDB_FOUND) report_optional_not_found(BERKELEY_DB) endif(BERKELEYDB_FOUND) endif (WITH_BERKELEY_DB) if(WITH_TIFF) find_package(TIFF) if(TIFF_FOUND) include_directories(${TIFF_INCLUDE_DIR}) target_link_libraries(mapcache ${TIFF_LIBRARY}) set (USE_TIFF 1) else(TIFF_FOUND) report_optional_not_found(TIFF) endif(TIFF_FOUND) endif (WITH_TIFF) if(WITH_TIFF_WRITE_SUPPORT) if(USE_TIFF) set (USE_TIFF_WRITE 1) else(USE_TIFF) report_dependency_error(TIFF_WRITE_SUPPORT TIFF) endif(USE_TIFF) endif(WITH_TIFF_WRITE_SUPPORT) if(WITH_GEOTIFF) find_package(GEOTIFF) if(GEOTIFF_FOUND) include_directories(${GEOTIFF_INCLUDE_DIR}) target_link_libraries(mapcache ${GEOTIFF_LIBRARY}) set (USE_GEOTIFF 1) else(GEOTIFF_FOUND) report_optional_not_found(GEOTIFF) endif(GEOTIFF_FOUND) endif (WITH_GEOTIFF) if(WITH_MAPSERVER) find_package(MAPSERVER) if(MAPSERVER_FOUND) include_directories(${MAPSERVER_INCLUDE_DIR}) target_link_libraries(mapcache ${MAPSERVER_LIBRARY}) set (USE_MAPSERVER 1) else(MAPSERVER_FOUND) report_optional_not_found(MAPSERVER) endif(MAPSERVER_FOUND) endif (WITH_MAPSERVER) if(WITH_RIAK) find_package(RIAK) if(RIAK_FOUND) include_directories(${RIAK_INCLUDE_DIR}) target_link_libraries(mapcache ${RIAK_LIBRARY}) set (USE_RIAK 1) else(RIAK_FOUND) report_optional_not_found(RIAK) endif(RIAK_FOUND) endif (WITH_RIAK) if(UNIX) target_link_libraries(mapcache ${CMAKE_DL_LIBS} m ) endif(UNIX) configure_file ( "${PROJECT_SOURCE_DIR}/include/mapcache-config.h.in" "${PROJECT_BINARY_DIR}/include/mapcache-config.h" ) configure_file ( "${PROJECT_SOURCE_DIR}/include/mapcache-version.h.in" "${PROJECT_BINARY_DIR}/include/mapcache-version.h" ) include_directories(include ${PROJECT_BINARY_DIR}/include) macro(status_optional_component component enabled libpath) if("${enabled}" EQUAL "1") message(STATUS " * ${component}: ${libpath}") else() message(STATUS " * ${component}: disabled") endif() endmacro() macro(status_optional_feature feature enabled) if("${enabled}" EQUAL "1") message(STATUS " * ${feature}: ENABLED") else() message(STATUS " * ${feature}: disabled") endif() endmacro() message(STATUS "* Configured options for the mapcache library") message(STATUS " * Mandatory components") message(STATUS " * png: ${PNG_LIBRARY}") message(STATUS " * jpeg: ${JPEG_LIBRARY}") message(STATUS " * Curl: ${CURL_LIBRARY}") message(STATUS " * Apr: ${APR_LIBRARY}") message(STATUS " * Optional components") status_optional_component("PIXMAN" "${USE_PIXMAN}" "${PIXMAN_LIBRARY}") status_optional_component("SQLITE" "${USE_SQLITE}" "${SQLITE_LIBRARY}") status_optional_component("Berkeley DB" "${USE_BDB}" "${BERKELEYDB_LIBRARY}") status_optional_component("Memcache" "${USE_MEMCACHE}" "${APU_LIBRARY}") status_optional_component("TIFF" "${USE_TIFF}" "${TIFF_LIBRARY}") status_optional_component("GeoTIFF" "${USE_GEOTIFF}" "${GEOTIFF_LIBRARY}") status_optional_component("Experimental TIFF write support" "${USE_TIFF_WRITE}" "${TIFF_LIBRARY}") 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}") INSTALL(TARGETS mapcache DESTINATION ${CMAKE_INSTALL_LIBDIR}) add_subdirectory(util) add_subdirectory(cgi) add_subdirectory(apache) add_subdirectory(nginx) mapcache-1.4.0/INSTALL000066400000000000000000000006741255567662100143340ustar00rootroot00000000000000Unix compilation instructions ----------------------------- Mapcache now builds with cmake. It seems much happier if it can find apxs, which means you might need apache2-prefork installed (on ubuntu apt-get install apache2-prefork-dev). cd mapcache mkdir build cd build cmake .. make sudo make install Detailed instructions and configuration options are maintained in the mapcache documentation : http://mapserver.org/mapcache/install.html mapcache-1.4.0/LICENSE000066400000000000000000000027711255567662100143100ustar00rootroot00000000000000/****************************************************************************** * $Id$ * * Project: MapServer * Purpose: MapCache tile caching program. * Author: Thomas Bonfort and the MapServer team. * ****************************************************************************** * Copyright (c) 1996-2012 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. *****************************************************************************/ mapcache-1.4.0/Makefile.vc000066400000000000000000000056321255567662100153510ustar00rootroot00000000000000# # makefile.vc - Main mapcache makefile for MSVC++ # # # To use the makefile: # - Open a DOS prompt window # - Run the VCVARS32.BAT script to initialize the VC++ environment variables # - Start the build with: nmake /f makefile.vc # # $Id: $ # !INCLUDE nmake.opt BASE_CFLAGS = $(OPTFLAGS) CFLAGS=$(BASE_CFLAGS) $(MAPCACHE_CFLAGS) CC= cl LINK= link # # Main mapcache library. # MAPCACHE_OBJS = lib\axisorder.obj lib\dimension.obj lib\imageio_mixed.obj lib\service_wms.obj \ lib\buffer.obj lib\ezxml.obj lib\imageio_png.obj lib\service_wmts.obj \ lib\cache_disk.obj lib\lock.obj lib\services.obj lib\cache_bdb.obj \ lib\cache_memcache.obj lib\grid.obj lib\source.obj \ lib\cache_sqlite.obj lib\http.obj lib\source_gdal.obj lib\source_dummy.obj \ lib\cache_tiff.obj lib\image.obj lib\service_demo.obj lib\source_mapserver.obj \ lib\configuration.obj lib\image_error.obj lib\service_kml.obj lib\source_wms.obj \ lib\configuration_xml.obj lib\imageio.obj lib\service_tms.obj lib\tileset.obj \ lib\core.obj lib\imageio_jpeg.obj lib\service_ve.obj lib\util.obj lib\strptime.obj \ $(REGEX_OBJ) MAPCACHE_FCGI = mapcache.exe MAPCACHE_APACHE = mod_mapcache.dll MAPCACHE_SEED = mapcache_seed.exe # # # default: all all: $(MAPCACHE_LIB) $(MAPCACHE_FCGI) $(MAPCACHE_APACHE) $(MAPCACHE_SEED) $(MAPCACHE_LIB): $(MAPCACHE_OBJS) lib /debug /out:$(MAPCACHE_LIB) $(MAPCACHE_OBJS) $(MAPCACHE_FCGI): $(MAPCACHE_LIB) $(CC) $(CFLAGS) cgi\mapcache.c /Fecgi\mapcache.exe $(LIBS) if exist cgi\$(MAPCACHE_FCGI).manifest mt -manifest cgi\$(MAPCACHE_FCGI).manifest -outputresource:cgi\$(MAPCACHE_FCGI);1 $(MAPCACHE_APACHE): $(MAPCACHE_LIB) $(CC) $(CFLAGS) apache\mod_mapcache.c /link /DLL /out:apache\mod_mapcache.dll $(LIBS) if exist apache\$(MAPCACHE_APACHE).manifest mt -manifest apache\$(MAPCACHE_APACHE).manifest -outputresource:apache\$(MAPCACHE_APACHE);2 $(MAPCACHE_SEED): $(MAPCACHE_LIB) $(CC) $(CFLAGS) util\mapcache_seed.c /Feutil\mapcache_seed.exe $(LIBS) if exist util\$(MAPCACHE_SEED).manifest mt -manifest util\$(MAPCACHE_SEED).manifest -outputresource:util\$(MAPCACHE_SEED);1 .c.obj: $(CC) $(CFLAGS) /c $*.c /Fo$*.obj .cpp.obj: $(CC) $(CFLAGS) /c $*.cpp /Fo$*.obj clean: del lib\*.obj del *.obj del *.exp del apache\$(MAPCACHE_APACHE) del apache\*.manifest del apache\*.exp del apache\*.lib del apache\*.pdb del apache\*.ilk del cgi\$(MAPCACHE_FCGI) del cgi\*.manifest del cgi\*.exp del cgi\*.lib del cgi\*.pdb del cgi\*.ilk del util\$(MAPCACHE_SEED) del util\*.manifest del util\*.exp del util\*.lib del util\*.pdb del util\*.ilk del *.lib del *.manifest install: $(MAPCACHE_EXE) -mkdir $(BINDIR) copy *.exe $(BINDIR) mapcache-1.4.0/README000066400000000000000000000001011255567662100141440ustar00rootroot00000000000000new code repository is at https://github.com/mapserver/mapcache mapcache-1.4.0/Vagrantfile000066400000000000000000000015621255567662100154650ustar00rootroot00000000000000# -*- mode: ruby -*- # vi: set ft=ruby : require 'socket' # Vagrantfile API/syntax version. Don't touch unless you know what you're doing! VAGRANTFILE_API_VERSION = "2" Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.box = "precise64" config.vm.box_url = "http://files.vagrantup.com/precise64.box" config.vm.hostname = "mapcache-vagrant" config.vm.network :forwarded_port, guest: 80, host: 8080 config.vm.provider "virtualbox" do |v| v.customize ["modifyvm", :id, "--memory", 1024, "--cpus", 2] v.customize ["modifyvm", :id, "--ioapic", "on", "--largepages", "off", "--vtxvpid", "off"] v.name = "mapcache-vagrant" end config.vm.provision "shell", path: "scripts/vagrant/virtualbox-fix.sh" config.vm.provision "shell", path: "scripts/vagrant/packages.sh" config.vm.provision "shell", path: "scripts/vagrant/mapcache.sh" end mapcache-1.4.0/apache/000077500000000000000000000000001255567662100145155ustar00rootroot00000000000000mapcache-1.4.0/apache/CMakeLists.txt000066400000000000000000000031301255567662100172520ustar00rootroot00000000000000option(WITH_VERSION_STRING "Show MapCache in server version string" ON) option(WITH_APACHE "Build Apache Module" ON) if(WITH_APACHE) add_library(mod_mapcache MODULE mod_mapcache.c) set_target_properties(mod_mapcache PROPERTIES PREFIX "") target_link_libraries(mod_mapcache mapcache) find_package(APACHE) if(APACHE_FOUND) include_directories(${APACHE_INCLUDE_DIR}) target_link_libraries(mod_mapcache ${APACHE_LIBRARY}) else(APACHE_FOUND) report_mandatory_not_found(APACHE) endif(APACHE_FOUND) if(WITH_VERSION_STRING) set(USE_VERSION_STRING 1) endif(WITH_VERSION_STRING) configure_file ( "${PROJECT_SOURCE_DIR}/apache/mod_mapcache-config.h.in" "${PROJECT_BINARY_DIR}/apache/mod_mapcache-config.h" ) include_directories("${PROJECT_BINARY_DIR}/apache/") IF(APPLE) set_target_properties(mod_mapcache PROPERTIES LINK_FLAGS "-Wl,-flat_namespace -Wl,-undefined -Wl,suppress") ENDIF(APPLE) message(STATUS "* Apache Module support status:") status_optional_component("Mapcache Version String" "${USE_VERSION_STRING}" "mod_mapcache/${MAPCACHE_VERSION_STRING}") if(APACHE_MODULE_DIR) message(STATUS " * Module will be installed to : ${APACHE_MODULE_DIR}") else(APACHE_MODULE_DIR) message(WARNING " * Module will not be automatically installed: module directory not found") endif(APACHE_MODULE_DIR) if(APACHE_MODULE_DIR) INSTALL(TARGETS mod_mapcache DESTINATION ${APACHE_MODULE_DIR}) endif(APACHE_MODULE_DIR) else(WITH_APACHE) message(STATUS " * Apache Module support status: DISABLED") endif(WITH_APACHE) mapcache-1.4.0/apache/mod_mapcache-config.h.in000066400000000000000000000001511255567662100211330ustar00rootroot00000000000000#ifndef _MOD_MAPCACHE_CONFIG_H #define _MOD_MAPCACHE_CONFIG_H #cmakedefine USE_VERSION_STRING 1 #endif mapcache-1.4.0/apache/mod_mapcache.c000066400000000000000000000475161255567662100172760ustar00rootroot00000000000000/****************************************************************************** * $Id$ * * Project: MapServer * Purpose: MapCache tile caching apache module implementation * 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 the core server components. */ #include "mod_mapcache-config.h" #include #include #include #include #include #include #include #include #include "mapcache.h" #ifdef APR_HAS_THREADS #include apr_thread_mutex_t *thread_mutex = NULL; #endif #ifndef _WIN32 #include #endif #ifdef AP_NEED_SET_MUTEX_PERMS #include "unixd.h" #endif module AP_MODULE_DECLARE_DATA mapcache_module; typedef struct mapcache_context_apache mapcache_context_apache; typedef struct mapcache_context_apache_request mapcache_context_apache_request; typedef struct mapcache_context_apache_server mapcache_context_apache_server; struct mapcache_context_apache { mapcache_context ctx; }; struct mapcache_context_apache_server { mapcache_context_apache ctx; server_rec *server; }; struct mapcache_context_apache_request { mapcache_context_apache ctx; request_rec *request; }; void apache_context_server_log(mapcache_context *c, mapcache_log_level level, char *message, ...) { mapcache_context_apache_server *ctx = (mapcache_context_apache_server*)c; va_list args; char *msg; int ap_log_level; va_start(args,message); msg = apr_pvsprintf(c->pool,message,args); va_end(args); switch(level) { case MAPCACHE_DEBUG: ap_log_level = APLOG_DEBUG; break; case MAPCACHE_INFO: ap_log_level = APLOG_INFO; break; case MAPCACHE_NOTICE: ap_log_level = APLOG_NOTICE; break; case MAPCACHE_WARN: ap_log_level = APLOG_WARNING; break; case MAPCACHE_ERROR: ap_log_level = APLOG_ERR; break; case MAPCACHE_CRIT: ap_log_level = APLOG_CRIT; break; case MAPCACHE_ALERT: ap_log_level = APLOG_ALERT; break; case MAPCACHE_EMERG: ap_log_level = APLOG_EMERG; break; default: ap_log_level = APLOG_WARNING; } ap_log_error(APLOG_MARK, ap_log_level, 0, ctx->server,"%s",msg); } void apache_context_request_log(mapcache_context *c, mapcache_log_level level, char *message, ...) { mapcache_context_apache_request *ctx = (mapcache_context_apache_request*)c; va_list args; char *res; int ap_log_level; va_start(args,message); res = apr_pvsprintf(c->pool, message, args); va_end(args); switch(level) { case MAPCACHE_DEBUG: ap_log_level = APLOG_DEBUG; break; case MAPCACHE_INFO: ap_log_level = APLOG_INFO; break; case MAPCACHE_NOTICE: ap_log_level = APLOG_NOTICE; break; case MAPCACHE_WARN: ap_log_level = APLOG_WARNING; break; case MAPCACHE_ERROR: ap_log_level = APLOG_ERR; break; case MAPCACHE_CRIT: ap_log_level = APLOG_CRIT; break; case MAPCACHE_ALERT: ap_log_level = APLOG_ALERT; break; case MAPCACHE_EMERG: ap_log_level = APLOG_EMERG; break; default: ap_log_level = APLOG_WARNING; } ap_log_rerror(APLOG_MARK, ap_log_level, 0, ctx->request, "%s", res); } mapcache_context *mapcache_context_request_clone(mapcache_context *ctx) { mapcache_context_apache_request *newctx = (mapcache_context_apache_request*)apr_pcalloc(ctx->pool, sizeof(mapcache_context_apache_request)); mapcache_context *nctx = (mapcache_context*)newctx; mapcache_context_copy(ctx,nctx); //apr_pool_create(&nctx->pool,ctx->pool); apr_pool_create(&nctx->pool,NULL); apr_pool_cleanup_register(ctx->pool, nctx->pool,(void*)apr_pool_destroy, apr_pool_cleanup_null); newctx->request = ((mapcache_context_apache_request*)ctx)->request; 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 *ctx = apr_pcalloc(r->pool, sizeof(mapcache_context_apache_request)); mapcache_server_cfg *cfg = NULL; mapcache_cfg *config = NULL; ctx->ctx.ctx.pool = r->pool; #ifdef APR_HAS_THREADS ctx->ctx.ctx.threadlock = thread_mutex; #endif /* lookup the configuration object given the configuration file name */ cfg = ap_get_module_config(r->server->module_config, &mapcache_module); config = apr_hash_get(cfg->aliases,(void*)r->filename,APR_HASH_KEY_STRING); ctx->ctx.ctx.config = config; ctx->request = r; ctx->ctx.ctx.connection_pool = cfg->cp; init_apache_request_context(ctx); return ctx; } static mapcache_context_apache_server* apache_server_context_create(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; } /* read post body. code taken from "The apache modules book, Nick Kew" */ static void read_post_body(mapcache_context_apache_request *ctx, mapcache_request_proxy *p) { request_rec *r = ctx->request; mapcache_context *mctx = (mapcache_context*)ctx; int bytes,eos; apr_bucket_brigade *bb, *bbin; apr_bucket *b; apr_status_t rv; const char *clen = apr_table_get(r->headers_in, "Content-Length"); if(clen) { bytes = strtol(clen, NULL, 0); if(bytes >= p->rule->max_post_len) { mctx->set_error(mctx, HTTP_REQUEST_ENTITY_TOO_LARGE, "post request too big"); return; } } else { bytes = p->rule->max_post_len; } bb = apr_brigade_create(mctx->pool, r->connection->bucket_alloc); bbin = apr_brigade_create(mctx->pool, r->connection->bucket_alloc); p->post_len = 0; do { rv = ap_get_brigade(r->input_filters, bbin, AP_MODE_READBYTES, APR_BLOCK_READ, bytes); if(rv != APR_SUCCESS) { mctx->set_error(mctx, 500, "failed to read form input"); return; } for(b = APR_BRIGADE_FIRST(bbin); b != APR_BRIGADE_SENTINEL(bbin); b = APR_BUCKET_NEXT(b)) { if(APR_BUCKET_IS_EOS(b)) { eos = 1; } } if(!APR_BUCKET_IS_METADATA(b)) { if(b->length != (apr_size_t)(-1)) { p->post_len += b->length; if(p->post_len > p->rule->max_post_len) { apr_bucket_delete(b); } } } if(p->post_len <= p->rule->max_post_len) { APR_BUCKET_REMOVE(b); APR_BRIGADE_INSERT_TAIL(bb, b); } } while (!eos); if(p->post_len > p->rule->max_post_len) { mctx->set_error(mctx, HTTP_REQUEST_ENTITY_TOO_LARGE, "request too big"); return; } p->post_buf = apr_palloc(mctx->pool, p->post_len+1); rv = apr_brigade_flatten(bb, p->post_buf, &(p->post_len)); if(rv != APR_SUCCESS) { mctx->set_error(mctx, 500, "error (flatten) reading form data"); return; } p->post_buf[p->post_len] = 0; } static int write_http_response(mapcache_context_apache_request *ctx, mapcache_http_response *response) { request_rec *r = ctx->request; int rc; char *timestr; if(response->mtime) { ap_update_mtime(r, response->mtime); if((rc = ap_meets_conditions(r)) != OK) { return rc; } timestr = apr_palloc(r->pool, APR_RFC822_DATE_LEN); apr_rfc822_date(timestr, response->mtime); apr_table_setn(r->headers_out, "Last-Modified", timestr); } if(response->headers && !apr_is_empty_table(response->headers)) { const apr_array_header_t *elts = apr_table_elts(response->headers); int i; for(i=0; inelts; i++) { apr_table_entry_t entry = APR_ARRAY_IDX(elts,i,apr_table_entry_t); if(!strcasecmp(entry.key,"Content-Type")) { ap_set_content_type(r,entry.val); } else { apr_table_set(r->headers_out, entry.key, entry.val); } } } if(response->data && response->data->size) { ap_set_content_length(r,response->data->size); ap_rwrite((void*)response->data->buf, response->data->size, r); } r->status = response->code; return OK; } static void mod_mapcache_child_init(apr_pool_t *pool, server_rec *s) { int rv; mapcache_server_cfg* cfg = ap_get_module_config(s->module_config, &mapcache_module); #ifdef APR_HAS_THREADS apr_thread_mutex_create(&thread_mutex,APR_THREAD_MUTEX_DEFAULT,pool); #endif rv = mapcache_connection_pool_create(&(cfg->cp),pool); ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "creating a mapcache connection pool for server"); 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) { apr_table_t *params; mapcache_request *request = NULL; mapcache_context_apache_request *apache_ctx = NULL; 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); global_ctx = (mapcache_context*)apache_ctx; global_ctx->supports_redirects = 1; global_ctx->headers_in = r->headers_in; params = mapcache_http_parse_param_string(global_ctx, r->args); mapcache_service_dispatch_request(global_ctx,&request,r->path_info,params,global_ctx->config); if(GC_HAS_ERROR(global_ctx) || !request) { return write_http_response(apache_ctx, mapcache_core_respond_to_error(global_ctx)); } if(request->type == MAPCACHE_REQUEST_GET_CAPABILITIES) { mapcache_request_get_capabilities *req_caps = (mapcache_request_get_capabilities*)request; request_rec *original; char *url; if(r->main) original = r->main; else original = r; url = ap_construct_url(r->pool,original->uri,original); /* * remove the path_info from the end of the url (we want the url of the base of the service) * TODO: is there an apache api to access this ? */ if(*(original->path_info) && strcmp(original->path_info,"/")) { char *end = strstr(url,original->path_info); if(end) { /* make sure our url ends with a single '/' */ if(*end == '/') { char *slash = end; while((*(--slash))=='/') end--; end++; } *end = '\0'; } } http_response = mapcache_core_get_capabilities(global_ctx,request->service,req_caps, url,original->path_info,global_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); } 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(!req_proxy->headers) { req_proxy->headers = apr_table_make(global_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)); #else apr_table_set(req_proxy->headers, "X-Forwarded-For", apr_psprintf(global_ctx->pool,"%s, %s", buf, r->connection->client_ip)); #endif } else { #if (AP_SERVER_MAJORVERSION_NUMBER == 2) && (AP_SERVER_MINORVERSION_NUMBER < 4) apr_table_set(req_proxy->headers, "X-Forwarded-For", r->connection->remote_ip); #else apr_table_set(req_proxy->headers, "X-Forwarded-For", r->connection->client_ip); #endif } 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)); } 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)); } else { apr_table_set(req_proxy->headers, "X-Forwarded-Server", r->server->server_hostname); } } http_response = mapcache_core_proxy_request(global_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); } 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); } else { global_ctx->set_error(global_ctx,500,"###BUG### unknown request type"); } if(GC_HAS_ERROR(global_ctx)) { return write_http_response(apache_ctx, mapcache_core_respond_to_error(global_ctx)); } return write_http_response(apache_ctx,http_response); } 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); if(!cfg) { ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, "configuration not found in server context"); return 1; } #ifdef USE_VERSION_STRING ap_add_version_component(p, MAPCACHE_USERAGENT); #endif return 0; } static int mapcache_alias_matches(const char *uri, const char *alias_fakename) { /* Code for this function from Apache mod_alias module. */ const char *aliasp = alias_fakename, *urip = uri; while (*aliasp) { if (*aliasp == '/') { /* any number of '/' in the alias matches any number in * the supplied URI, but there must be at least one... */ if (*urip != '/') return 0; do { ++aliasp; } while (*aliasp == '/'); do { ++urip; } while (*urip == '/'); } else { /* Other characters are compared literally */ if (*urip++ != *aliasp++) return 0; } } /* Check last alias path component matched all the way */ if (aliasp[-1] != '/' && *urip != '\0' && *urip != '/') return 0; /* Return number of characters from URI which matched (may be * greater than length of alias, since we may have matched * doubled slashes) */ return urip - uri; } static int mapcache_hook_intercept(request_rec *r) { mapcache_server_cfg *sconfig = ap_get_module_config(r->server->module_config, &mapcache_module); apr_hash_index_t *entry; if (!sconfig->aliases) return DECLINED; if (r->uri[0] != '/' && r->uri[0]) return DECLINED; entry = apr_hash_first(r->pool,sconfig->aliases); /* loop through the entries to find one where the alias matches */ while (entry) { int l = 0; const char *alias; apr_ssize_t aliaslen; mapcache_cfg *c; apr_hash_this(entry,(const void**)&alias,&aliaslen,(void**)&c); if((l=mapcache_alias_matches(r->uri, c->endpoint))>0) { r->handler = "mapcache"; r->filename = c->configFile; r->path_info = &(r->uri[l]); return OK; } entry = apr_hash_next(entry); } return DECLINED; } static void mod_mapcache_register_hooks(apr_pool_t *p) { static const char * const p1[] = { "mod_alias.c", "mod_rewrite.c", NULL }; static const char * const n1[]= { "mod_userdir.c", "mod_vhost_alias.c", NULL }; 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_translate_name(mapcache_hook_intercept, p1, n1, APR_HOOK_MIDDLE); } 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 = NULL; return cfg; } static void *mod_mapcache_merge_server_conf(apr_pool_t *p, void *base_, void *vhost_) { mapcache_server_cfg *base = (mapcache_server_cfg*)base_; mapcache_server_cfg *vhost = (mapcache_server_cfg*)vhost_; mapcache_server_cfg *cfg = apr_pcalloc(p,sizeof(mapcache_server_cfg)); if (base->aliases && vhost->aliases) { cfg->aliases = apr_hash_overlay(p, vhost->aliases,base->aliases); } else if (vhost->aliases) { cfg->aliases = apr_hash_copy(p,vhost->aliases); } else if (base->aliases) { cfg->aliases = apr_hash_copy(p,base->aliases); } return vhost; } static const char* mapcache_add_alias(cmd_parms *cmd, void *cfg, const char *alias, const char* configfile) { mapcache_server_cfg *sconfig = ap_get_module_config(cmd->server->module_config, &mapcache_module); mapcache_cfg *config = mapcache_configuration_create(cmd->pool); mapcache_context *ctx = (mapcache_context*)apache_server_context_create(cmd->server,cmd->pool); char *msg = NULL; config->configFile = apr_pstrdup(cmd->pool,configfile); config->endpoint = alias; mapcache_configuration_parse(ctx,configfile,config,0); if(GC_HAS_ERROR(ctx)) { return ctx->get_error_message(ctx); } mapcache_configuration_post_config(ctx, config); if(GC_HAS_ERROR(ctx)) { return ctx->get_error_message(ctx); } ap_log_error(APLOG_MARK, APLOG_INFO, 0, cmd->server, "loaded mapcache configuration file from %s on alias %s", config->configFile, alias); if(!sconfig->aliases) { sconfig->aliases = apr_hash_make(cmd->pool); } apr_hash_set(sconfig->aliases,configfile,APR_HASH_KEY_STRING,config); return msg; } static const command_rec mod_mapcache_cmds[] = { AP_INIT_TAKE2("MapCacheAlias", mapcache_add_alias ,NULL,RSRC_CONF,"Aliased location of configuration file"), { NULL } } ; module AP_MODULE_DECLARE_DATA mapcache_module = { STANDARD20_MODULE_STUFF, NULL, NULL, mod_mapcache_create_server_conf, mod_mapcache_merge_server_conf, mod_mapcache_cmds, mod_mapcache_register_hooks }; /* vim: ts=2 sts=2 et sw=2 */ mapcache-1.4.0/astyle.sh000077500000000000000000000002571255567662100151400ustar00rootroot00000000000000#!/bin/bash ASTYLEOPTS="--style=kr --indent=spaces=2 -c --lineend=linux -S" ASTYLEBIN=astyle $ASTYLEBIN $ASTYLEOPTS -R '*.c' '*.h' #find . -name '*.orig' -exec rm -f {} \; mapcache-1.4.0/benchmark.py000066400000000000000000000042171255567662100156040ustar00rootroot00000000000000#! /usr/bin/env python import commands import os import re def do_ab_call(url,nthreads,reqs): cmd="ab -k -c %d -n %d '%s'" % (nthreads,reqs,url) print cmd summary={} ret = commands.getoutput(cmd) sList = ret.split(os.linesep) for i, line in enumerate(sList): if re.match("Requests per second", line) is not None: val = line.split() summary['reqspersec']=val[3] if re.match("Document Length", line) is not None: val = line.split() summary['size']=val[2] return summary base="http://localhost:8081" params="LAYERS=test,test3&FORMAT=image%2Fpng&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&EXCEPTIONS=application%2Fvnd.ogc.se_inimage&SRS=EPSG%3A4326&BBOX=-2.8125,47.8125,0,50.625&WIDTH=256&HEIGHT=256" urls={} nreqs=400 title="tile merging" filebase=title urls['tilecache']="%s/%s?%s" % (base,'tilecache',params) urls['mapcache best compression']="%s/%s?%s" % (base,'mapcache-best',params) urls['mapcache default compression']="%s/%s?%s" % (base,'mapcache-default',params) urls['mapcache fast compression']="%s/%s?%s" % (base,'mapcache-fast',params) urls['mapcache png quantization']="%s/%s?%s" % (base,'mapcache-pngq',params) #urls['mapproxy']="http://localhost:8080/service?%s" % (params) plotfile = open("%s.plot"%(filebase),"w") datafile = open("%s.dat"%(filebase),"w") plotfile.write("set terminal pdf\nset key autotitle columnhead\nset output \"%s.pdf\"\nset style data lines\n"%(filebase)) plotfile.write("set xlabel \"concurrent requests\"\n") plotfile.write("set ylabel \"throughput (requests/sec)\"\n") plotfile.write("set title \"%s\"\n"%(title)) count=0 for title,url in urls.iteritems(): if count == 0: plotfile.write("plot \"%s.dat\" using 2:xticlabel(1) index 0"%(filebase)) else: plotfile.write(",\"\" using 2 index %d"%(count)) count += 1 for nthreads in [1,2,3,4]: reqs = min(nthreads,4) * nreqs res = do_ab_call(url,nthreads,reqs) if nthreads == 1: datafile.write("\n\nthreads \"%s (%s bytes)\"\n"%(title,res['size'])) datafile.write("%d %s\n"%(nthreads,res['reqspersec'])) plotfile.write("\n") mapcache-1.4.0/cgi/000077500000000000000000000000001255567662100140365ustar00rootroot00000000000000mapcache-1.4.0/cgi/CMakeLists.txt000066400000000000000000000022071255567662100165770ustar00rootroot00000000000000option(WITH_CGI "Choose if CGI executable should be built" ON) option(WITH_FCGI "Choose if CGI executable should support FastCGI" ON) if(WITH_CGI) if(NOT DEFINED CMAKE_INSTALL_CGIBINDIR) set(CMAKE_INSTALL_CGIBINDIR ${CMAKE_INSTALL_BINDIR}) endif(NOT DEFINED CMAKE_INSTALL_CGIBINDIR) add_executable(mapcache.fcgi mapcache.c) target_link_libraries(mapcache.fcgi mapcache) if(WITH_FCGI) find_package(FCGI) if(FCGI_FOUND) include_directories(${FCGI_INCLUDE_DIR}) target_link_libraries(mapcache.fcgi ${FCGI_LIBRARY}) set (USE_FASTCGI 1) else(FCGI_FOUND) report_optional_not_found(FCGI) endif(FCGI_FOUND) endif (WITH_FCGI) configure_file ( "${PROJECT_SOURCE_DIR}/cgi/mapcache-cgi-config.h.in" "${PROJECT_BINARY_DIR}/cgi/mapcache-cgi-config.h" ) include_directories("${PROJECT_BINARY_DIR}/cgi/") message(STATUS "* CGI Configuration Options:") status_optional_component("FastCGI" "${USE_FASTCGI}" "${FCGI_LIBRARY}") INSTALL(TARGETS mapcache.fcgi RUNTIME DESTINATION ${CMAKE_INSTALL_CGIBINDIR}) else(WITH_CGI) message(STATUS "* CGI Configuration Options: DISABLED") endif(WITH_CGI) mapcache-1.4.0/cgi/mapcache-cgi-config.h.in000066400000000000000000000001421255567662100203550ustar00rootroot00000000000000#ifndef _MAPCACHE_CGI_CONFIG_H #define _MAPCACHE_CGI_CONFIG_H #cmakedefine USE_FASTCGI 1 #endif mapcache-1.4.0/cgi/mapcache.c000066400000000000000000000261021255567662100157440ustar00rootroot00000000000000/****************************************************************************** * $Id$ * * Project: MapServer * Purpose: MapCache tile caching CGI and FastCGI main program * 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-cgi-config.h" #include "mapcache.h" #include #include #include #include #include #include #ifdef USE_FASTCGI #include #endif typedef struct mapcache_context_fcgi mapcache_context_fcgi; typedef struct mapcache_context_fcgi_request mapcache_context_fcgi_request; static char *err400 = "Bad Request"; static char *err404 = "Not Found"; static char *err500 = "Internal Server Error"; static char *err501 = "Not Implemented"; static char *err502 = "Bad Gateway"; static char *errother = "No Description"; apr_pool_t *global_pool = NULL,*config_pool, *tmp_config_pool; static char* err_msg(int code) { switch(code) { case 400: return err400; case 404: return err404; case 500: return err500; case 501: return err501; case 502: return err502; default: return errother; } } struct mapcache_context_fcgi { mapcache_context ctx; }; static mapcache_context* fcgi_context_clone(mapcache_context *ctx) { mapcache_context_fcgi *newctx = (mapcache_context_fcgi*)apr_pcalloc(ctx->pool, sizeof(mapcache_context_fcgi)); mapcache_context *nctx = (mapcache_context*)newctx; mapcache_context_copy(ctx,nctx); apr_pool_create(&nctx->pool,ctx->pool); return nctx; } static void fcgi_context_log(mapcache_context *c, mapcache_log_level level, char *message, ...) { va_list args; if(!c->config || level >= c->config->loglevel) { va_start(args,message); fprintf(stderr,"%s\n",apr_pvsprintf(c->pool,message,args)); va_end(args); } } static void handle_signal(int signal) { apr_pool_destroy(global_pool); exit(signal); } static mapcache_context_fcgi* fcgi_context_create() { mapcache_context_fcgi *ctx = apr_pcalloc(global_pool, sizeof(mapcache_context_fcgi)); if(!ctx) { return NULL; } ctx->ctx.pool = global_pool; mapcache_context_init((mapcache_context*)ctx); ctx->ctx.log = fcgi_context_log; ctx->ctx.clone = fcgi_context_clone; ctx->ctx.config = NULL; return ctx; } static void fcgi_write_response(mapcache_context_fcgi *ctx, mapcache_http_response *response) { if(response->code != 200) { printf("Status: %ld %s\r\n",response->code, err_msg(response->code)); } if(response->headers && !apr_is_empty_table(response->headers)) { const apr_array_header_t *elts = apr_table_elts(response->headers); int i; for(i=0; inelts; i++) { apr_table_entry_t entry = APR_ARRAY_IDX(elts,i,apr_table_entry_t); printf("%s: %s\r\n", entry.key, entry.val); } } if(response->mtime) { char *datestr; char *if_modified_since = getenv("HTTP_IF_MODIFIED_SINCE"); if(if_modified_since) { apr_time_t ims_time; apr_int64_t ims,mtime; mtime = apr_time_sec(response->mtime); ims_time = apr_date_parse_http(if_modified_since); ims = apr_time_sec(ims_time); if(ims >= mtime) { printf("Status: 304 Not Modified\r\n"); } } datestr = apr_palloc(ctx->ctx.pool, APR_RFC822_DATE_LEN); apr_rfc822_date(datestr, response->mtime); printf("Last-Modified: %s\r\n", datestr); } if(response->data) { printf("Content-Length: %ld\r\n\r\n", response->data->size); fwrite((char*)response->data->buf, response->data->size,1,stdout); } } apr_time_t mtime; char *conffile; static void load_config(mapcache_context *ctx, char *filename) { apr_file_t *f; apr_finfo_t finfo; mapcache_cfg *old_cfg; mapcache_cfg *cfg; if((apr_file_open(&f, filename, APR_FOPEN_READ, APR_UREAD | APR_GREAD, global_pool)) == APR_SUCCESS) { apr_file_info_get(&finfo, APR_FINFO_MTIME, f); apr_file_close(f); } else { if(!ctx->pool) ctx->pool = global_pool; ctx->set_error(ctx,500,"failed to open config file %s",filename); return; } if(ctx->config) { //we already have a loaded configuration, check that the config file hasn't changed if(finfo.mtime > mtime) { ctx->log(ctx,MAPCACHE_INFO,"config file has changed, reloading"); } else { return; } } mtime = finfo.mtime; /* either we have no config, or it has changed */ old_cfg = ctx->config; apr_pool_create(&tmp_config_pool,global_pool); cfg = mapcache_configuration_create(tmp_config_pool); ctx->config = cfg; ctx->pool = tmp_config_pool; mapcache_configuration_parse(ctx,conffile,cfg,1); if(GC_HAS_ERROR(ctx)) goto failed_load; mapcache_configuration_post_config(ctx, cfg); if(GC_HAS_ERROR(ctx)) goto failed_load; /* no error, destroy the previous pool if we are reloading the config */ if(config_pool) { apr_pool_destroy(config_pool); } config_pool = tmp_config_pool; mapcache_connection_pool_create(&ctx->connection_pool, config_pool); return; failed_load: /* we failed to load the config file */ if(config_pool) { /* we already have a running configuration, keep it and only log the error to not * interrupt the already running service */ ctx->log(ctx,MAPCACHE_ERROR,"failed to reload config file %s: %s", conffile,ctx->get_error_message(ctx)); ctx->clear_errors(ctx); ctx->config = old_cfg; ctx->pool = config_pool; apr_pool_destroy(tmp_config_pool); } } int main(int argc, const char **argv) { mapcache_context_fcgi* globalctx; mapcache_context* ctx; apr_table_t *params; mapcache_request *request = NULL; char *pathInfo; mapcache_http_response *http_response; (void) signal(SIGTERM,handle_signal); #ifndef _WIN32 (void) signal(SIGUSR1,handle_signal); #endif apr_initialize(); atexit(apr_terminate); if(apr_pool_create(&global_pool,NULL) != APR_SUCCESS) { return 1; } config_pool = NULL; globalctx = fcgi_context_create(); ctx = (mapcache_context*)globalctx; conffile = getenv("MAPCACHE_CONFIG_FILE"); #ifdef DEBUG if(!conffile) { int i; for(i=1; ilog(ctx,MAPCACHE_INFO,"mapcache fcgi conf file: %s",conffile); #ifdef USE_FASTCGI while (FCGI_Accept() >= 0) { #endif ctx->pool = config_pool; if(!ctx->config || ctx->config->autoreload) { load_config(ctx,conffile); if(GC_HAS_ERROR(ctx)) { fcgi_write_response(globalctx, mapcache_core_respond_to_error(ctx)); goto cleanup; } } apr_pool_create(&(ctx->pool),config_pool); ctx->threadlock = NULL; request = NULL; pathInfo = getenv("PATH_INFO"); params = mapcache_http_parse_param_string(ctx, getenv("QUERY_STRING")); mapcache_service_dispatch_request(ctx,&request,pathInfo,params,ctx->config); if(GC_HAS_ERROR(ctx) || !request) { fcgi_write_response(globalctx, mapcache_core_respond_to_error(ctx)); goto cleanup; } http_response = NULL; if(request->type == MAPCACHE_REQUEST_GET_CAPABILITIES) { mapcache_request_get_capabilities *req = (mapcache_request_get_capabilities*)request; char *host = getenv("SERVER_NAME"); char *port = getenv("SERVER_PORT"); char *fullhost; char *url; if(getenv("HTTPS")) { if(!port || !strcmp(port,"443")) { fullhost = apr_psprintf(ctx->pool,"https://%s",host); } else { fullhost = apr_psprintf(ctx->pool,"https://%s:%s",host,port); } } else { if(!port || !strcmp(port,"80")) { fullhost = apr_psprintf(ctx->pool,"http://%s",host); } else { fullhost = apr_psprintf(ctx->pool,"http://%s:%s",host,port); } } url = apr_psprintf(ctx->pool,"%s%s/", fullhost, getenv("SCRIPT_NAME") ); http_response = mapcache_core_get_capabilities(ctx,request->service,req,url,pathInfo,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(ctx,req_tile); } else if( request->type == MAPCACHE_REQUEST_PROXY ) { mapcache_request_proxy *req_proxy = (mapcache_request_proxy*)request; 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(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(ctx,req_fi); #ifdef DEBUG } else { ctx->set_error(ctx,500,"###BUG### unknown request type"); #endif } if(GC_HAS_ERROR(ctx)) { fcgi_write_response(globalctx, mapcache_core_respond_to_error(ctx)); goto cleanup; } #ifdef DEBUG if(!http_response) { ctx->set_error(ctx,500,"###BUG### NULL response"); fcgi_write_response(globalctx, mapcache_core_respond_to_error(ctx)); goto cleanup; } #endif fcgi_write_response(globalctx,http_response); cleanup: #ifdef USE_FASTCGI apr_pool_destroy(ctx->pool); ctx->clear_errors(ctx); } #endif apr_pool_destroy(global_pool); apr_terminate(); return 0; } /* vim: ts=2 sts=2 et sw=2 */ mapcache-1.4.0/cmake/000077500000000000000000000000001255567662100143545ustar00rootroot00000000000000mapcache-1.4.0/cmake/FindAPACHE.cmake000066400000000000000000000015671255567662100171110ustar00rootroot00000000000000# # APACHE_FOUND - System has APACHE # APACHE_INCLUDE_DIR - The APACHE include directory # # APACHE_LOCATION # setting this enables search for apache libraries / headers in this location # # Include directories # find_path(APACHE_INCLUDE_DIR NAMES httpd.h PATH_SUFFIXES httpd apache apache2 ) if(NOT DEFINED APACHE_MODULE_DIR) find_program(APXS_BIN NAMES apxs apxs2 PATH_SUFFIXES httpd apache apache2 ) if(APXS_BIN) EXEC_PROGRAM(${APXS_BIN} ARGS -q LIBEXECDIR OUTPUT_VARIABLE APACHE_MODULE_DIR ) endif(APXS_BIN) endif(NOT DEFINED APACHE_MODULE_DIR) include(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set APACHE_FOUND to TRUE if # all listed variables are TRUE find_package_handle_standard_args(APACHE DEFAULT_MSG APACHE_INCLUDE_DIR ) mark_as_advanced(APACHE_INCLUDE_DIR) mapcache-1.4.0/cmake/FindAPR.cmake000066400000000000000000000046031255567662100166040ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # - Find Apache Portable Runtime # Find the APR includes and libraries # This module defines # APR_INCLUDE_DIR and APRUTIL_INCLUDE_DIR, where to find apr.h, etc. # APR_LIBRARIES and APRUTIL_LIBRARIES, the libraries needed to use APR. # APR_FOUND and APRUTIL_FOUND, If false, do not try to use APR. # also defined, but not for general use are # APR_LIBRARY and APRUTIL_LIBRARY, where to find the APR library. # APR first. FIND_PROGRAM(APR_CONFIG_BIN NAMES apr-config apr-1-config ) if(APR_CONFIG_BIN) execute_process( COMMAND ${APR_CONFIG_BIN} --includedir OUTPUT_VARIABLE HINT_APR_INCLUDE_DIR OUTPUT_STRIP_TRAILING_WHITESPACE ) execute_process( COMMAND ${APR_CONFIG_BIN} --cppflags OUTPUT_VARIABLE APR_CPPFLAGS OUTPUT_STRIP_TRAILING_WHITESPACE ) endif(APR_CONFIG_BIN) FIND_PATH(APR_INCLUDE_DIR NAMES apr.h HINTS ${HINT_APR_INCLUDE_DIR} PATH_SUFFIXES apr-1 apr-1.0 apr ) FIND_LIBRARY(APR_LIBRARY NAMES apr-1 apr PATH_SUFFIXES apr-1 apr-1.0 apr ) set(APR_INCLUDE_DIRS ${APR_INCLUDE_DIR}) set(APR_LIBRARIES ${APR_LIBRARY}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(APR DEFAULT_MSG APR_LIBRARY APR_INCLUDE_DIR) mark_as_advanced(APR_LIBRARY APR_INCLUDE_DIR) # Next, APRUTIL. FIND_PATH(APU_INCLUDE_DIR NAMES apu.h PATH_SUFFIXES apr-1 apr-1.0 apr ) FIND_LIBRARY(APU_LIBRARY NAMES aprutil-1 aprutil PATH_SUFFIXES apr-1 apr-1.0 apr ) set(APU_INCLUDE_DIRS ${APU_INCLUDE_DIR}) set(APU_LIBRARIES ${APU_LIBRARY}) find_package_handle_standard_args(APU DEFAULT_MSG APU_LIBRARY APU_INCLUDE_DIR) mark_as_advanced(APU_LIBRARY APU_INCLUDE_DIR) mapcache-1.4.0/cmake/FindBerkeleyDB.cmake000066400000000000000000000071051255567662100201320ustar00rootroot00000000000000set(BERKELEYDB_FOUND TRUE) # set the search path if (WIN32) file(GLOB BERKELEYDB_SEARCH_PATH "C:/Program Files/Oracle/Berkeley DB*") if (NOT BERKELEYDB_SEARCH_PATH) file(GLOB BERKELEYDB_SEARCH_PATH "C:/Program Files (x86)/Oracle/Berkeley DB*") endif (NOT BERKELEYDB_SEARCH_PATH) else (WIN32) file(GLOB BERKELEYDB_SEARCH_PATH "/usr/local/BerkeleyDB*") file(GLOB BERKELEYDB_INC_SEARCH_PATH "/usr/local/BerkeleyDB*/include") file(GLOB BERKELEYDB_LIB_SEARCH_PATH "/usr/local/BerkeleyDB*/lib") endif (WIN32) # search for header find_path(BERKELEYDB_INCLUDE_DIR NAMES "db.h" HINTS ${BERKELEYDB_SEARCH_PATH} ${BERKELEYDB_INC_SEARCH_PATH} ) # header is found if (BERKELEYDB_INCLUDE_DIR) # retrieve version information from the header file(READ "${BERKELEYDB_INCLUDE_DIR}/db.h" DB_H_FILE) string(REGEX REPLACE ".*#define[ \t]+DB_VERSION_STRING[ \t]+\"([^\"]+)\".*" "\\1" BERKELEYDB_VERSION "${DB_H_FILE}") string(REGEX REPLACE ".*#define[ \t]+DB_VERSION_MAJOR[ \t]+([0-9]+).*" "\\1" BERKELEYDB_VERSION_MAJOR "${DB_H_FILE}") string(REGEX REPLACE ".*#define[ \t]+DB_VERSION_MINOR[ \t]+([0-9]+).*" "\\1" BERKELEYDB_VERSION_MINOR "${DB_H_FILE}") string(REGEX REPLACE ".*#define[ \t]+DB_VERSION_PATCH[ \t]+([0-9]+).*" "\\1" BERKELEYDB_VERSION_PATCH "${DB_H_FILE}") # search for library if (WIN32) file(GLOB BERKELEYDB_LIBRARIES "${DBROOTDIR}/lib/libdb${BERKELEYDB_VERSION_MAJOR}${BERKELEYDB_VERSION_MINOR}.lib" "${BERKELEYDB_SEARCH_PATH}/lib/libdb${BERKELEYDB_VERSION_MAJOR}${BERKELEYDB_VERSION_MINOR}.lib") else (WIN32) find_library(BERKELEYDB_LIBRARY NAMES "db-${BERKELEYDB_VERSION_MAJOR}.${BERKELEYDB_VERSION_MINOR}" db HINTS ${BERKELEYDB_SEARCH_PATH} ${BERKELEYDB_LIB_SEARCH_PATH} ) endif (WIN32) endif (BERKELEYDB_INCLUDE_DIR) # header is not found if (NOT BERKELEYDB_INCLUDE_DIR OR NOT BERKELEYDB_LIBRARY) set(BERKELEYDB_FOUND_TMP FALSE) else (NOT BERKELEYDB_INCLUDE_DIR OR NOT BERKELEYDB_LIBRARY) set(BERKELEYDB_FOUND_TMP TRUE) endif (NOT BERKELEYDB_INCLUDE_DIR OR NOT BERKELEYDB_LIBRARY) # check found version if (BERKELEYDB_FIND_VERSION AND BERKELEYDB_FOUND_TMP) set(BERKELEYDB_FOUND_VERSION "${BERKELEYDB_VERSION_MAJOR}.${BERKELEYDB_VERSION_MINOR}.${BERKELEYDB_VERSION_PATCH}") if (BERKELEYDB_FIND_VERSION_EXACT) if (NOT ${BERKELEYDB_FOUND_VERSION} VERSION_EQUAL ${BERKELEYDB_FIND_VERSION}) set(BERKELEYDB_FOUND_TMP FALSE) endif (NOT ${BERKELEYDB_FOUND_VERSION} VERSION_EQUAL ${BERKELEYDB_FIND_VERSION}) else (BERKELEYDB_FIND_VERSION_EXACT) if (${BERKELEYDB_FOUND_VERSION} VERSION_LESS ${BERKELEYDB_FIND_VERSION}) set(BERKELEYDB_FOUND_TMP FALSE) endif (${BERKELEYDB_FOUND_VERSION} VERSION_LESS ${BERKELEYDB_FIND_VERSION}) endif (BERKELEYDB_FIND_VERSION_EXACT) if (NOT BERKELEYDB_FOUND_TMP) message(SEND_ERROR "Berkeley DB library found, but with wrong version v${BERKELEYDB_FIND_VERSION} (${BERKELEYDB_FOUND_VERSION} was found)") unset(BERKELEYDB_INCLUDE_DIR) unset(BERKELEYDB_LIBRARY) endif (NOT BERKELEYDB_FOUND_TMP) endif (BERKELEYDB_FIND_VERSION AND BERKELEYDB_FOUND_TMP) set(BERKELEYDB_INCLUDE_DIRS ${BERKELEYDB_INCLUDE_DIR}) set(BERKELEYDB_LIBRARIES ${BERKELEYDB_LIBRARY}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(BERKELEYDB DEFAULT_MSG BERKELEYDB_LIBRARY BERKELEYDB_INCLUDE_DIR) mark_as_advanced(BERKELEYDB_LIBRARY BERKELEYDB_INCLUDE_DIR) mapcache-1.4.0/cmake/FindFCGI.cmake000066400000000000000000000006001255567662100166630ustar00rootroot00000000000000# Look for the header file. find_path(FCGI_INCLUDE_DIR NAMES fastcgi.h) # Look for the library. find_library(FCGI_LIBRARY NAMES fcgi) set(FCGI_INCLUDE_DIRS ${FCGI_INCLUDE_DIR}) set(FCGI_LIBRARIES ${FCGI_LIBRARY}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(FCGI DEFAULT_MSG FCGI_LIBRARY FCGI_INCLUDE_DIR) mark_as_advanced(FCGI_LIBRARY FCGI_INCLUDE_DIR) mapcache-1.4.0/cmake/FindGEOS.cmake000066400000000000000000000016501255567662100167160ustar00rootroot00000000000000# Find the native GEOS(Geometry Engine - Open Source) includes and libraries. # # This module defines: # # GEOS_INCLUDE_DIR, where to find geos.h, etc. # GEOS_LIBRARY, libraries to link against to use GEOS, using the geos_c library # GEOS_FOUND, True if found, false if one of the above are not found. #--- # Find include path: # Note: Version < 3.3.0 do not have geos.h in the geos sub directory; hence, # the check for both "geos/geos.h" and "geos.h". #--- find_path( GEOS_INCLUDE_DIR NAMES geos_c.h PATH_SUFFIXES geos) # Find GEOS C library: find_library( GEOS_LIBRARY NAMES geos_c ) set(GEOS_INCLUDE_DIRS ${GEOS_INCLUDE_DIR}) set(GEOS_LIBRARIES ${GEOS_LIBRARY}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args( GEOS DEFAULT_MSG GEOS_LIBRARY GEOS_INCLUDE_DIR ) MARK_AS_ADVANCED(GEOS_LIBRARY GEOS_INCLUDE_DIR) mapcache-1.4.0/cmake/FindGEOTIFF.cmake000066400000000000000000000006751255567662100172520ustar00rootroot00000000000000find_path(GEOTIFF_INCLUDE_DIR NAMES geotiff.h PATH_SUFFIXES libgeotiff geotiff) find_library(GEOTIFF_LIBRARY NAMES geotiff geotiff3 PATH_SUFFIXES geotiff ) set(GEOTIFF_INCLUDE_DIRS ${GEOTIFF_INCLUDE_DIR}) set(GEOTIFF_LIBRARIES ${GEOTIFF_LIBRARY}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GEOTIFF DEFAULT_MSG GEOTIFF_LIBRARY GEOTIFF_INCLUDE_DIR) mark_as_advanced(GEOTIFF_LIBRARY GEOTIFF_INCLUDE_DIR) mapcache-1.4.0/cmake/FindMAPSERVER.cmake000066400000000000000000000006611255567662100175260ustar00rootroot00000000000000 FIND_PATH(MAPSERVER_INCLUDE_DIR NAMES mapserver.h PATH_SUFFIXES mapserver ) FIND_LIBRARY(MAPSERVER_LIBRARY NAMES mapserver ) set(MAPSERVER_INCLUDE_DIRS ${MAPSERVER_INCLUDE_DIR}) set(MAPSERVER_LIBRARIES ${MAPSERVER_LIBRARY}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(MAPSERVER DEFAULT_MSG MAPSERVER_LIBRARY MAPSERVER_INCLUDE_DIR) mark_as_advanced(MAPSERVER_LIBRARY MAPSERVER_INCLUDE_DIR) mapcache-1.4.0/cmake/FindPCRE.cmake000066400000000000000000000005321255567662100167100ustar00rootroot00000000000000 FIND_PATH(PCRE_INCLUDE_DIR NAMES pcre.h ) FIND_LIBRARY(PCRE_LIBRARY NAMES pcre pcred ) set(PCRE_INCLUDE_DIRS ${PCRE_INCLUDE_DIR}) set(PCRE_LIBRARIES ${PCRE_LIBRARY}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(PCRE DEFAULT_MSG PCRE_LIBRARY PCRE_INCLUDE_DIR) mark_as_advanced(PCRE_LIBRARY PCRE_INCLUDE_DIR) mapcache-1.4.0/cmake/FindPixman.cmake000066400000000000000000000012721255567662100174150ustar00rootroot00000000000000FIND_PACKAGE(PkgConfig) PKG_CHECK_MODULES(PC_PIXMAN pixman) if(NOT PC_PIXMAN_FOUND) PKG_CHECK_MODULES(PC_PIXMAN pixman-1) endif(NOT PC_PIXMAN_FOUND) FIND_PATH(PIXMAN_INCLUDE_DIR NAMES pixman.h HINTS ${PC_PIXMAN_INCLUDEDIR} ${PC_PIXMAN_INCLUDE_DIR} PATH_SUFFIXES pixman pixman-1 ) FIND_LIBRARY(PIXMAN_LIBRARY NAMES pixman pixman-1 HINTS ${PC_PIXMAN_LIBDIR} ${PC_PIXMAN_LIBRARY_DIRS} ) set(PIXMAN_INCLUDE_DIRS ${PIXMAN_INCLUDE_DIR}) set(PIXMAN_LIBRARIES ${PIXMAN_LIBRARY}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(PIXMAN DEFAULT_MSG PIXMAN_LIBRARY PIXMAN_INCLUDE_DIR) mark_as_advanced(PIXMAN_LIBRARY PIXMAN_INCLUDE_DIR) mapcache-1.4.0/cmake/FindRIAK.cmake000066400000000000000000000005261255567662100167100ustar00rootroot00000000000000 FIND_PATH(RIAK_INCLUDE_DIR NAMES riack.h ) FIND_LIBRARY(RIAK_LIBRARY NAMES riack ) set(RIAK_INCLUDE_DIRS ${RIAK_INCLUDE_DIR}) set(RIAK_LIBRARIES ${RIAK_LIBRARY}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(RIAK DEFAULT_MSG RIAK_LIBRARY RIAK_INCLUDE_DIR) mark_as_advanced(RIAK_LIBRARY RIAK_INCLUDE_DIR) mapcache-1.4.0/cmake/FindSQLITE.cmake000066400000000000000000000006331255567662100171620ustar00rootroot00000000000000 FIND_PATH(SQLITE_INCLUDE_DIR NAMES sqlite3.h PATH_SUFFIXES sqlite sqlite3 ) FIND_LIBRARY(SQLITE_LIBRARY NAMES sqlite3 sqlite3_i ) set(SQLITE_INCLUDE_DIRS ${SQLITE_INCLUDE_DIR}) set(SQLITE_LIBRARIES ${SQLITE_LIBRARY}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(SQLITE DEFAULT_MSG SQLITE_LIBRARY SQLITE_INCLUDE_DIR) mark_as_advanced(SQLITE_LIBRARY SQLITE_INCLUDE_DIR) mapcache-1.4.0/include/000077500000000000000000000000001255567662100147175ustar00rootroot00000000000000mapcache-1.4.0/include/errors.h000066400000000000000000000036341255567662100164120ustar00rootroot00000000000000/****************************************************************************** * $Id$ * * Project: MapServer * 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. *****************************************************************************/ #ifndef MAPCACHE_ERRORS_H_ #define MAPCACHE_ERRORS_H_ typedef enum { MAPCACHE_DEBUG, MAPCACHE_INFO, MAPCACHE_NOTICE, MAPCACHE_WARN, MAPCACHE_ERROR, MAPCACHE_CRIT, MAPCACHE_ALERT, MAPCACHE_EMERG } mapcache_log_level; typedef enum { MAPCACHE_REPORT_LOG, MAPCACHE_REPORT_MSG, MAPCACHE_REPORT_ERROR_IMG, MAPCACHE_REPORT_EMPTY_IMG, MAPCACHE_REPORT_CUSTOM_IMG } mapcache_error_reporting; #endif /* ERRORS_H_ */ /* vim: ts=2 sts=2 et sw=2 */ mapcache-1.4.0/include/ezxml.h000066400000000000000000000152701255567662100162340ustar00rootroot00000000000000/* ezxml.h * * Copyright 2004-2006 Aaron Voisine * * 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 or substantial portions of the 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. */ #ifndef _EZXML_H #define _EZXML_H #include #include #include #include #ifdef __cplusplus extern "C" { #endif #define EZXML_BUFSIZE 1024 // size of internal memory buffers #define EZXML_NAMEM 0x80 // name is malloced #define EZXML_TXTM 0x40 // txt is malloced #define EZXML_DUP 0x20 // attribute name and value are strduped typedef struct ezxml *ezxml_t; struct ezxml { char *name; // tag name char **attr; // tag attributes { name, value, name, value, ... NULL } char *txt; // tag character content, empty string if none size_t off; // tag offset from start of parent tag character content ezxml_t next; // next tag with same name in this section at this depth ezxml_t sibling; // next tag with different name in same section and depth ezxml_t ordered; // next tag, same section and depth, in original order ezxml_t child; // head of sub tag list, NULL if none ezxml_t parent; // parent tag, NULL if current tag is root tag short flags; // additional information }; // Given a string of xml data and its length, parses it and creates an ezxml // structure. For efficiency, modifies the data by adding null terminators // and decoding ampersand sequences. If you don't want this, copy the data and // pass in the copy. Returns NULL on failure. ezxml_t ezxml_parse_str(char *s, size_t len); // A wrapper for ezxml_parse_str() that accepts a file descriptor. First // attempts to mem map the file. Failing that, reads the file into memory. // Returns NULL on failure. ezxml_t ezxml_parse_fd(int fd); // a wrapper for ezxml_parse_fd() that accepts a file name ezxml_t ezxml_parse_file(const char *file); // Wrapper for ezxml_parse_str() that accepts a file stream. Reads the entire // stream into memory and then parses it. For xml files, use ezxml_parse_file() // or ezxml_parse_fd() ezxml_t ezxml_parse_fp(FILE *fp); // returns the first child tag (one level deeper) with the given name or NULL // if not found ezxml_t ezxml_child(ezxml_t xml, const char *name); // returns the next tag of the same name in the same section and depth or NULL // if not found #define ezxml_next(xml) ((xml) ? xml->next : NULL) // Returns the Nth tag with the same name in the same section at the same depth // or NULL if not found. An index of 0 returns the tag given. ezxml_t ezxml_idx(ezxml_t xml, int idx); // returns the name of the given tag #define ezxml_name(xml) ((xml) ? xml->name : NULL) // returns the given tag's character content or empty string if none #define ezxml_txt(xml) ((xml) ? xml->txt : "") // returns the value of the requested tag attribute, or NULL if not found const char *ezxml_attr(ezxml_t xml, const char *attr); // Traverses the ezxml sturcture to retrieve a specific subtag. Takes a // variable length list of tag names and indexes. The argument list must be // terminated by either an index of -1 or an empty string tag name. Example: // title = ezxml_get(library, "shelf", 0, "book", 2, "title", -1); // This retrieves the title of the 3rd book on the 1st shelf of library. // Returns NULL if not found. ezxml_t ezxml_get(ezxml_t xml, ...); // Converts an ezxml structure back to xml. Returns a string of xml data that // must be freed. char *ezxml_toxml(ezxml_t xml); // returns a NULL terminated array of processing instructions for the given // target const char **ezxml_pi(ezxml_t xml, const char *target); // frees the memory allocated for an ezxml structure void ezxml_free(ezxml_t xml); // returns parser error message or empty string if none const char *ezxml_error(ezxml_t xml); // returns a new empty ezxml structure with the given root tag name ezxml_t ezxml_new(const char *name); // wrapper for ezxml_new() that strdup()s name #define ezxml_new_d(name) ezxml_set_flag(ezxml_new(strdup(name)), EZXML_NAMEM) // Adds a child tag. off is the offset of the child tag relative to the start // of the parent tag's character content. Returns the child tag. ezxml_t ezxml_add_child(ezxml_t xml, const char *name, size_t off); // wrapper for ezxml_add_child() that strdup()s name #define ezxml_add_child_d(xml, name, off) \ ezxml_set_flag(ezxml_add_child(xml, strdup(name), off), EZXML_NAMEM) // sets the character content for the given tag and returns the tag ezxml_t ezxml_set_txt(ezxml_t xml, const char *txt); // wrapper for ezxml_set_txt() that strdup()s txt #define ezxml_set_txt_d(xml, txt) \ ezxml_set_flag(ezxml_set_txt(xml, strdup(txt)), EZXML_TXTM) // Sets the given tag attribute or adds a new attribute if not found. A value // of NULL will remove the specified attribute. Returns the tag given. ezxml_t ezxml_set_attr(ezxml_t xml, const char *name, const char *value); // Wrapper for ezxml_set_attr() that strdup()s name/value. Value cannot be NULL #define ezxml_set_attr_d(xml, name, value) \ ezxml_set_attr(ezxml_set_flag(xml, EZXML_DUP), strdup(name), strdup(value)) // sets a flag for the given tag and returns the tag ezxml_t ezxml_set_flag(ezxml_t xml, short flag); // removes a tag along with its subtags without freeing its memory ezxml_t ezxml_cut(ezxml_t xml); // inserts an existing tag into an ezxml structure ezxml_t ezxml_insert(ezxml_t xml, ezxml_t dest, size_t off); // Moves an existing tag to become a subtag of dest at the given offset from // the start of dest's character content. Returns the moved tag. #define ezxml_move(xml, dest, off) ezxml_insert(ezxml_cut(xml), dest, off) // removes a tag along with all its subtags #define ezxml_remove(xml) ezxml_free(ezxml_cut(xml)) #ifdef __cplusplus } #endif #endif // _EZXML_H mapcache-1.4.0/include/mapcache-config.h.in000066400000000000000000000006421255567662100205030ustar00rootroot00000000000000#ifndef _MAPCACHE_CONFIG_H #define _MAPCACHE_CONFIG_H #cmakedefine USE_PIXMAN 1 #cmakedefine USE_FASTCGI 1 #cmakedefine USE_SQLITE 1 #cmakedefine USE_BDB 1 #cmakedefine USE_MEMCACHE 1 #cmakedefine USE_TIFF 1 #cmakedefine USE_TIFF_WRITE 1 #cmakedefine USE_GEOTIFF 1 #cmakedefine USE_PCRE 1 #cmakedefine USE_MAPSERVER 1 #cmakedefine USE_RIAK 1 #cmakedefine HAVE_STRNCASECMP 1 #cmakedefine HAVE_SYMLINK 1 #endif mapcache-1.4.0/include/mapcache-version.h.in000066400000000000000000000005261255567662100207240ustar00rootroot00000000000000#ifndef _MAPCACHE_VERSION_H #define _MAPCACHE_VERSION_H #define MAPCACHE_VERSION_MAJOR @MAPCACHE_VERSION_MAJOR@ #define MAPCACHE_VERSION_MINOR @MAPCACHE_VERSION_MINOR@ #define MAPCACHE_VERSION_REV @MAPCACHE_VERSION_REVISION@ #define MAPCACHE_VERSION "@MAPCACHE_VERSION_STRING@" #define MAPCACHE_VERSION_NUM @MAPCACHE_VERSION_NUM@ #endif mapcache-1.4.0/include/mapcache.h000066400000000000000000001717011255567662100166400ustar00rootroot00000000000000/****************************************************************************** * $Id$ * * Project: MapServer * 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. *****************************************************************************/ /*! \file mapcache.h \brief global function and structure declarations */ #ifndef MAPCACHE_H_ #define MAPCACHE_H_ #include "mapcache-config.h" #include "mapcache-version.h" #include #include #include #include "util.h" #include "ezxml.h" #include "errors.h" #if 0 #ifdef USE_GDAL #include #include #endif #endif #include #include #include #ifdef USE_PCRE #include #else #include #endif #ifdef USE_MEMCACHE #include #endif #ifdef USE_COUCHBASE #include #endif #ifdef USE_RIAK #include #endif #define MAPCACHE_SUCCESS 0 #define MAPCACHE_FAILURE 1 #define MAPCACHE_TRUE 1 #define MAPCACHE_FALSE 0 #define MAPCACHE_TILESET_WRONG_SIZE 2 #define MAPCACHE_TILESET_WRONG_RESOLUTION 3 #define MAPCACHE_TILESET_WRONG_EXTENT 4 #define MAPCACHE_CACHE_MISS 5 #define MAPCACHE_FILE_LOCKED 6 #define MAPCACHE_CACHE_RELOAD 7 #define MAPCACHE_MAX_NUM_TILES 1000 #define MAPCACHE_USERAGENT "mod-mapcache/"MAPCACHE_VERSION typedef struct mapcache_image_format mapcache_image_format; typedef struct mapcache_image_format_mixed mapcache_image_format_mixed; typedef struct mapcache_image_format_png mapcache_image_format_png; typedef struct mapcache_image_format_png_q mapcache_image_format_png_q; typedef struct mapcache_image_format_jpeg mapcache_image_format_jpeg; typedef struct mapcache_cfg mapcache_cfg; typedef struct mapcache_tileset mapcache_tileset; typedef struct mapcache_cache mapcache_cache; typedef struct mapcache_source mapcache_source; typedef struct mapcache_buffer mapcache_buffer; typedef struct mapcache_tile mapcache_tile; typedef struct mapcache_metatile mapcache_metatile; 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; 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_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; /** \defgroup utility Utility */ /** @{ */ struct mapcache_extent { double minx; double miny; double maxx; double maxy; }; struct mapcache_extent_i { int minx; int miny; int maxx; int maxy; }; mapcache_image *mapcache_error_image(mapcache_context *ctx, int width, int height, char *msg); /** * \interface mapcache_context * \brief structure passed to most mapcache functions to abstract common functions */ struct mapcache_context { /** * \brief indicate that an error has happened * \memberof mapcache_context * \param c * \param code the error code * \param message human readable message of what happened */ void (*set_error)(mapcache_context *ctx, int code, char *message, ...); void (*set_exception)(mapcache_context *ctx, char *key, char *message, ...); /** * \brief query context to know if an error has occured * \memberof mapcache_context */ int (*get_error)(mapcache_context * ctx); /** * \brief get human readable message for the error * \memberof mapcache_context */ char* (*get_error_message)(mapcache_context * ctx); /** * \brief get human readable message for the error * \memberof mapcache_context */ void (*clear_errors)(mapcache_context * ctx); /** * \brief clear current error and store it in mapcache_error * \memberof mapcache_context */ void (*pop_errors)(mapcache_context * ctx, void **error); /** * \brief restore error status from mapcache_error * \memberof mapcache_context */ void (*push_errors)(mapcache_context * ctx, void *error); /** * \brief log a message * \memberof mapcache_context */ void (*log)(mapcache_context *ctx, mapcache_log_level level, char *message, ...); const char* (*get_instance_id)(mapcache_context * ctx); mapcache_context* (*clone)(mapcache_context *ctx); apr_pool_t *pool; mapcache_connection_pool *connection_pool; void *threadlock; char *_contenttype; char *_errmsg; int _errcode; mapcache_cfg *config; mapcache_service *service; apr_table_t *exceptions; int supports_redirects; apr_table_t *headers_in; }; void mapcache_context_init(mapcache_context *ctx); void mapcache_context_copy(mapcache_context *src, mapcache_context *dst); #define GC_CHECK_ERROR_RETURN(ctx) if(((mapcache_context*)ctx)->_errcode) return MAPCACHE_FAILURE; #define GC_CHECK_ERROR(ctx) if(((mapcache_context*)ctx)->_errcode) return; #define GC_HAS_ERROR(ctx) (((mapcache_context*)ctx)->_errcode > 0) /** * \brief autoexpanding buffer that allocates memory from a pool * \sa mapcache_buffer_create() * \sa mapcache_buffer_append() * */ struct mapcache_buffer { void* buf; /**< pointer to the actual data contained in buffer */ size_t size; /**< number of bytes actually used in the buffer */ size_t avail; /**< number of bytes allocated */ apr_pool_t* pool; /**< apache pool to allocate from */ }; /* in buffer.c */ /** * \brief create and initialize a mapcache_buffer * \memberof mapcache_buffer * \param initialStorage the initial size that should be allocated in the buffer. * defaults to #INITIAL_BUFFER_SIZE. * \param pool the pool from which to allocate memory. */ mapcache_buffer *mapcache_buffer_create(size_t initialStorage, apr_pool_t* pool); /** * \brief append data * \memberof mapcache_buffer * \param buffer * \param len the lenght of the data to append. * \param data the data to append */ int mapcache_buffer_append(mapcache_buffer *buffer, size_t len, void *data); /** @} */ /** \defgroup source Sources */ /** @{ */ typedef enum { MAPCACHE_SOURCE_WMS, MAPCACHE_SOURCE_MAPSERVER, MAPCACHE_SOURCE_DUMMY, MAPCACHE_SOURCE_GDAL } mapcache_source_type; /**\interface mapcache_source * \brief a source of data that can return image data */ struct mapcache_source { char *name; /**< the key this source can be referenced by */ mapcache_extent data_extent; /**< extent in which this source can produce data */ mapcache_source_type type; apr_table_t *metadata; apr_array_header_t *info_formats; /** * \brief get the data for the metatile * * sets the mapcache_metatile::tile::data for the given tile */ void (*render_map)(mapcache_context *ctx, mapcache_map *map); void (*query_info)(mapcache_context *ctx, mapcache_feature_info *fi); void (*configuration_parse_xml)(mapcache_context *ctx, ezxml_t xml, mapcache_source * source); void (*configuration_check)(mapcache_context *ctx, mapcache_cfg *cfg, mapcache_source * source); }; mapcache_http* mapcache_http_configuration_parse_xml(mapcache_context *ctx,ezxml_t node); mapcache_http* mapcache_http_clone(mapcache_context *ctx, mapcache_http *orig); struct mapcache_http { char *url; /**< the base url to request */ apr_table_t *headers; /**< additional headers to add to the http request, eg, Referer */ char *post_body; size_t post_len; int connection_timeout; int timeout; /* 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_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 * \brief a place to cache a mapcache_tile */ struct mapcache_cache { char *name; /**< key this cache is referenced by */ mapcache_cache_type type; apr_table_t *metadata; /** * get tile content from cache * \returns MAPCACHE_SUCCESS if the data was correctly loaded from the disk * \returns MAPCACHE_FAILURE if the file exists but contains no data * \returns MAPCACHE_CACHE_MISS if the file does not exist on the disk * \memberof mapcache_cache */ 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); 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 (*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 #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 */ 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; }; /** * \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 */ struct mapcache_cache_riak { mapcache_cache cache; char *host; int port; RIACK_STRING bucket; }; /** * \memberof mapcache_cache_riak */ mapcache_cache* mapcache_cache_riak_create(mapcache_context *ctx); #endif /** @} */ typedef enum { MAPCACHE_REQUEST_UNKNOWN, MAPCACHE_REQUEST_GET_TILE, MAPCACHE_REQUEST_GET_MAP, MAPCACHE_REQUEST_GET_CAPABILITIES, MAPCACHE_REQUEST_GET_FEATUREINFO, MAPCACHE_REQUEST_PROXY } mapcache_request_type; typedef enum { MAPCACHE_GETMAP_ERROR, MAPCACHE_GETMAP_ASSEMBLE, MAPCACHE_GETMAP_FORWARD } mapcache_getmap_strategy; typedef enum { MAPCACHE_RESAMPLE_NEAREST, MAPCACHE_RESAMPLE_BILINEAR } mapcache_resample_mode; /** * \brief a request sent by a client */ struct mapcache_request { mapcache_request_type type; mapcache_service *service; }; struct mapcache_request_image { mapcache_request request; mapcache_image_format *format; }; struct mapcache_request_get_tile { mapcache_request_image image_request; /** * a list of tiles requested by the client */ mapcache_tile **tiles; /** * the number of tiles requested by the client. * If more than one, and merging is enabled, * the supplied tiles will be merged together * before being returned to the client */ int ntiles; int allow_redirect; }; struct mapcache_http_response { mapcache_buffer *data; apr_table_t *headers; long code; apr_time_t mtime; }; struct mapcache_map { mapcache_tileset *tileset; mapcache_grid_link *grid_link; apr_table_t *dimensions; mapcache_buffer *encoded_data; mapcache_image *raw_image; int nodata; /**< \sa mapcache_tile::nodata */ int width, height; mapcache_extent extent; apr_time_t mtime; /**< last modification time */ int expires; /**< time in seconds after which the tile should be rechecked for validity */ }; struct mapcache_feature_info { mapcache_map map; int i,j; char *format; mapcache_buffer *data; }; struct mapcache_request_get_feature_info { mapcache_request request; mapcache_feature_info *fi; }; struct mapcache_request_get_map { mapcache_request_image image_request; mapcache_map **maps; int nmaps; mapcache_getmap_strategy getmap_strategy; mapcache_resample_mode resample_mode; }; struct mapcache_request_get_capabilities { mapcache_request request; /** * the body of the capabilities */ char *capabilities; /** * the mime type */ 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; mapcache_http *http; apr_array_header_t *match_params; /* actually those are mapcache_dimensions */ int append_pathinfo; size_t max_post_len; }; struct mapcache_request_proxy { mapcache_request request; mapcache_forwarding_rule *rule; apr_table_t *params; apr_table_t *headers; const char *pathinfo; char *post_buf; size_t post_len; }; /** \defgroup services Services*/ /** @{ */ #define MAPCACHE_SERVICES_COUNT 8 typedef enum { MAPCACHE_SERVICE_TMS=0, MAPCACHE_SERVICE_WMTS, MAPCACHE_SERVICE_DEMO, MAPCACHE_SERVICE_GMAPS, MAPCACHE_SERVICE_KML, MAPCACHE_SERVICE_VE, MAPCACHE_SERVICE_MAPGUIDE, MAPCACHE_SERVICE_WMS } mapcache_service_type; #define MAPCACHE_UNITS_COUNT 3 typedef enum { MAPCACHE_UNIT_METERS=0, MAPCACHE_UNIT_DEGREES, MAPCACHE_UNIT_FEET } mapcache_unit; /* defined in util.c*/ extern const double mapcache_meters_per_unit[MAPCACHE_UNITS_COUNT]; /** \interface mapcache_service * \brief a standard service (eg WMS, TMS) */ struct mapcache_service { char *name; mapcache_service_type type; /** * the pathinfo prefix of the url that routes to this service * eg, for accessing a wms service on http://host/mapcache/mywmsservice? , * url_prefix would take the value "mywmsservice" */ char *url_prefix; /** * \brief allocates and populates a mapcache_request corresponding to the parameters received */ void (*parse_request)(mapcache_context *ctx, mapcache_service *service, mapcache_request **request, const char *path_info, apr_table_t *params, mapcache_cfg * config); /** * \param request the received request (should be of type MAPCACHE_REQUEST_CAPABILITIES * \param url the full url at which the service is available */ void (*create_capabilities_response)(mapcache_context *ctx, mapcache_request_get_capabilities *request, char *url, char *path_info, mapcache_cfg *config); /** * parse advanced configuration options for the selected service */ void (*configuration_parse_xml)(mapcache_context *ctx, ezxml_t xml, mapcache_service * service, mapcache_cfg *config); void (*format_error)(mapcache_context *ctx, mapcache_service * service, char *err_msg, 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 * \memberof mapcache_service_wms */ mapcache_service* mapcache_service_wms_create(mapcache_context *ctx); /** * \brief create and initialize a mapcache_service_ve * \memberof mapcache_service_ve */ mapcache_service* mapcache_service_ve_create(mapcache_context *ctx); /** * \brief create and initialize a mapcache_service_mapguide * \memberof mapcache_service_mapguide */ mapcache_service* mapcache_service_mapguide_create(mapcache_context *ctx); /** * \brief create and initialize a mapcache_service_gmaps * \memberof mapcache_service_gmaps */ mapcache_service* mapcache_service_gmaps_create(mapcache_context *ctx); /** * \brief create and initialize a mapcache_service_kml * \memberof mapcache_service_kml */ mapcache_service* mapcache_service_kml_create(mapcache_context *ctx); /** * \brief create and initialize a mapcache_service_tms * \memberof mapcache_service_tms */ mapcache_service* mapcache_service_tms_create(mapcache_context *ctx); /** * \brief create and initialize a mapcache_service_wmts * \memberof mapcache_service_wtms */ mapcache_service* mapcache_service_wmts_create(mapcache_context *ctx); /** * \brief create and initialize a mapcache_service_demo * \memberof mapcache_service_demo */ mapcache_service* mapcache_service_demo_create(mapcache_context *ctx); /** * \brief return the request that corresponds to the given url */ void mapcache_service_dispatch_request(mapcache_context *ctx, mapcache_request **request, char *pathinfo, apr_table_t *params, mapcache_cfg *config); /** @} */ /** \defgroup image Image Data Handling */ /** @{ */ typedef enum { GC_UNKNOWN, GC_PNG, GC_JPEG } mapcache_image_format_type; typedef enum { MC_EMPTY_UNKNOWN, MC_EMPTY_YES, MC_EMPTY_NO } mapcache_image_blank_type; typedef enum { MC_ALPHA_UNKNOWN, MC_ALPHA_YES, MC_ALPHA_NO } mapcache_image_alpha_type; /**\class mapcache_image * \brief representation of an RGBA image * * to access a pixel at position x,y, you should use the #GET_IMG_PIXEL macro */ struct mapcache_image { unsigned char *data; /**< pointer to the beginning of image data, stored in rgba order */ size_t w; /**< width of the image */ size_t h; /**< height of the image */ size_t stride; /**< stride of an image row */ mapcache_image_blank_type is_blank; mapcache_image_alpha_type has_alpha; }; /** \def GET_IMG_PIXEL * return the address of a pixel * \param y the row * \param x the column * \param img the mapcache_image * \returns a pointer to the pixel */ #define GET_IMG_PIXEL(img,x,y) (&((img).data[(y)*(img).stride + (x)*4])) /** * \brief initialize a new mapcache_image */ mapcache_image* mapcache_image_create(mapcache_context *ctx); mapcache_image* mapcache_image_create_with_data(mapcache_context *ctx, int width, int height); void mapcache_image_copy_resampled_nearest(mapcache_context *ctx, mapcache_image *src, mapcache_image *dst, double off_x, double off_y, double scale_x, double scale_y); void mapcache_image_copy_resampled_bilinear(mapcache_context *ctx, mapcache_image *src, mapcache_image *dst, double off_x, double off_y, double scale_x, double scale_y, int reflect_edges); /** * \brief merge two images * \param base the imae to merge onto * \param overlay the image to overlay onto * \param ctx the context * when finished, base will be modified and have overlay merged onto it */ void mapcache_image_merge(mapcache_context *ctx, mapcache_image *base, mapcache_image *overlay); void mapcache_image_copy_resampled(mapcache_context *ctx, mapcache_image *src, mapcache_image *dst, int srcX, int srcY, int srcW, int srcH, int dstX, int dstY, int dstW, int dstH); /** * \brief split the given metatile into tiles * \param mt the metatile to split * \param r the context */ void mapcache_image_metatile_split(mapcache_context *ctx, mapcache_metatile *mt); /** * \brief check if given image is composed of a unique color * \param image the mapcache_image to process * \returns MAPCACHE_TRUE if the image contains a single color * \returns MAPCACHE_FALSE if the image has more than one color */ 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); void mapcache_image_fill(mapcache_context *ctx, mapcache_image *image, const unsigned char *fill_color); /** @} */ /** \defgroup http HTTP Request handling*/ /** @{ */ void mapcache_http_do_request(mapcache_context *ctx, mapcache_http *req, mapcache_buffer *data, apr_table_t *headers, long *http_code); char* mapcache_http_build_url(mapcache_context *ctx, char *base, apr_table_t *params); apr_table_t *mapcache_http_parse_param_string(mapcache_context *ctx, char *args); /** @} */ /** \defgroup configuration Configuration*/ /** @{ */ struct mapcache_server_cfg { apr_hash_t *aliases; /**< list of mapcache configurations aliased to a server uri */ mapcache_connection_pool *cp; }; typedef enum { MAPCACHE_MODE_NORMAL, MAPCACHE_MODE_MIRROR_COMBINED, MAPCACHE_MODE_MIRROR_SPLIT } mapcache_mode; typedef enum { MAPCACHE_LOCKER_DISK, MAPCACHE_LOCKER_MEMCACHE, MAPCACHE_LOCKER_FALLBACK } mapcache_lock_mode; typedef enum { MAPCACHE_LOCK_AQUIRED, MAPCACHE_LOCK_LOCKED, MAPCACHE_LOCK_NOENT } mapcache_lock_result; 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); void (*parse_xml)(mapcache_context *ctx, mapcache_locker *self, ezxml_t node); mapcache_lock_mode type; double timeout; 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); /** * a configuration that will be served */ struct mapcache_cfg { char *configFile; /**< the filename from which this configuration was loaded */ /** * a list of services that will be responded to */ mapcache_service * services[MAPCACHE_SERVICES_COUNT]; /** * hashtable containing configured mapcache_source%s */ apr_hash_t *sources; /** * hashtable containing configured mapcache_cache%s */ apr_hash_t *caches; /** * hashtable containing configured mapcache_tileset%s */ apr_hash_t *tilesets; /** * hashtable containing configured mapcache_image_format%s */ apr_hash_t *image_formats; /** * hashtable containing (pre)defined grids */ apr_hash_t *grids; /** * the format to use for some miscelaneaous operations: * - creating an empty image * - creating an error image * - as a fallback when merging multiple tiles */ mapcache_image_format *default_image_format; /** * how should error messages be reported to the user */ mapcache_error_reporting reporting; /** * encoded empty (tranpsarent) image that will be returned to clients if cofigured * to return blank images upon error */ mapcache_buffer *empty_image; apr_table_t *metadata; mapcache_locker *locker; int threaded_fetching; /** * the uri where the base of the service is mapped */ const char *endpoint; /* for fastcgi only */ int autoreload; /* should the modification time of the config file be recorded and the file be reparsed if it is modified. */ mapcache_log_level loglevel; /* logging verbosity. Ignored for the apache module as in that case the apache LogLevel directive is used. */ mapcache_mode mode; /* return 404 on potentially blocking operations (proxying, source getmaps, locks on metatile waiting, ... Used for nginx module */ int non_blocking; }; /** * * @param filename * @param config * @param pool * @return */ void mapcache_configuration_parse(mapcache_context *ctx, const char *filename, mapcache_cfg *config, int cgi); void mapcache_configuration_post_config(mapcache_context *ctx, mapcache_cfg *config); void mapcache_configuration_parse_xml(mapcache_context *ctx, const char *filename, mapcache_cfg *config); mapcache_cfg* mapcache_configuration_create(apr_pool_t *pool); mapcache_source* mapcache_configuration_get_source(mapcache_cfg *config, const char *key); mapcache_cache* mapcache_configuration_get_cache(mapcache_cfg *config, const char *key); mapcache_grid *mapcache_configuration_get_grid(mapcache_cfg *config, const char *key); mapcache_tileset* mapcache_configuration_get_tileset(mapcache_cfg *config, const char *key); mapcache_image_format *mapcache_configuration_get_image_format(mapcache_cfg *config, const char *key); void mapcache_configuration_add_image_format(mapcache_cfg *config, mapcache_image_format *format, const char * key); void mapcache_configuration_add_source(mapcache_cfg *config, mapcache_source *source, const char * key); void mapcache_configuration_add_grid(mapcache_cfg *config, mapcache_grid *grid, const char * key); void mapcache_configuration_add_tileset(mapcache_cfg *config, mapcache_tileset *tileset, const char * key); void mapcache_configuration_add_cache(mapcache_cfg *config, mapcache_cache *cache, const char * key); /** @} */ /** * \memberof mapcache_source */ void mapcache_source_init(mapcache_context *ctx, mapcache_source *source); /** * \memberof mapcache_source_gdal */ mapcache_source* mapcache_source_gdal_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); /** * \memberof mapcache_cache_disk */ mapcache_cache* mapcache_cache_disk_create(mapcache_context *ctx); /** * \memberof mapcache_cache_rest */ mapcache_cache* mapcache_cache_rest_create(mapcache_context *ctx); 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); mapcache_cache* mapcache_cache_multitier_create(mapcache_context *ctx); /** \defgroup tileset Tilesets*/ /** @{ */ /** * \brief Tile * \sa mapcache_metatile * \sa mapcache_tileset::metasize_x mapcache_tileset::metasize_x mapcache_tileset::metabuffer */ struct mapcache_tile { mapcache_tileset *tileset; /**< the mapcache_tileset that corresponds to the tile*/ mapcache_grid_link *grid_link; int x; /**< tile x index */ int y; /**< tile y index */ int z; /**< tile z index (zoom level) */ /** * encoded image data for the tile. * \sa mapcache_cache::tile_get() * \sa mapcache_source::render_map() * \sa mapcache_image_format */ mapcache_buffer *encoded_data; char *redirect; int allow_redirect; mapcache_image *raw_image; 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; /** * 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 * no way to get data back from the cache for this tile. This will happen for * a tileset with no configured, for tiles that have not been preseeded. * Tile assembling functions should look for this flag and ignore such a tile when * compositing image data */ int nodata; }; /** * \brief MetaTile * \extends mapcache_tile */ struct mapcache_metatile { mapcache_map map; int x,y,z; int metasize_x, metasize_y; int ntiles; /**< the number of mapcache_metatile::tiles contained in this metatile */ mapcache_tile *tiles; /**< the list of mapcache_tile s contained in this metatile */ }; struct mapcache_grid_level { double resolution; unsigned int maxx, maxy; }; /** * \brief mapcache_grid_origin * determines at which extent extrema the tiles will originate from. Only * BOTTOM_LEFT and TOP_LEFT are implemented */ typedef enum { MAPCACHE_GRID_ORIGIN_BOTTOM_LEFT, MAPCACHE_GRID_ORIGIN_TOP_LEFT, MAPCACHE_GRID_ORIGIN_BOTTOM_RIGHT, MAPCACHE_GRID_ORIGIN_TOP_RIGHT, } mapcache_grid_origin; struct mapcache_grid { char *name; int nlevels; char *srs; apr_array_header_t *srs_aliases; mapcache_extent extent; mapcache_unit unit; int tile_sx, tile_sy; /** element */ apr_array_header_t* (*print_ogc_formatted_values)(mapcache_context *context, mapcache_dimension *dimension); /** * \brief parse the value given in the configuration */ 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; 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 int mapcache_is_axis_inverted(const char *srs); typedef struct mapcache_pooled_connection_container mapcache_pooled_connection_container; typedef struct mapcache_pooled_connection mapcache_pooled_connection; typedef struct mapcache_pooled_connection_private_data mapcache_pooled_connection_private_data; struct mapcache_pooled_connection { mapcache_pooled_connection_private_data *private; void *connection; }; typedef void (*mapcache_connection_constructor)(mapcache_context *ctx, void **connection, void *params, apr_pool_t *process_pool); typedef void (*mapcache_connection_destructor)(void *connection, apr_pool_t *process_pool); apr_status_t mapcache_connection_pool_create(mapcache_connection_pool **cp, apr_pool_t *server_pool); mapcache_pooled_connection* mapcache_connection_pool_get_connection(mapcache_context *ctx, char *key, mapcache_connection_constructor constructor, mapcache_connection_destructor destructor, void *params); void mapcache_connection_pool_invalidate_connection(mapcache_context *ctx, mapcache_pooled_connection *connection); void mapcache_connection_pool_release_connection(mapcache_context *ctx, mapcache_pooled_connection *connection); #endif /* MAPCACHE_H_ */ /* vim: ts=2 sts=2 et sw=2 */ mapcache-1.4.0/include/util.h000066400000000000000000000062311255567662100160470ustar00rootroot00000000000000/****************************************************************************** * $Id$ * * Project: MapServer * 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 #include #ifndef UTIL_H_ #define UTIL_H_ #define MAPCACHE_MAX(a,b) (((a)>(b))?(a):(b)) #define MAPCACHE_MIN(a,b) (((a)<(b))?(a):(b)) #ifndef APR_ARRAY_IDX #define APR_ARRAY_IDX(ary,i,type) (((type *)(ary)->elts)[i]) #endif #ifndef APR_ARRAY_PUSH #define APR_ARRAY_PUSH(ary,type) (*((type *)apr_array_push(ary))) #endif #if APR_MAJOR_VERSION < 1 || (APR_MAJOR_VERSION < 2 && APR_MINOR_VERSION < 3) /** * Create a new table whose contents are deep copied from the given * table. A deep copy operation copies all fields, and makes copies * of dynamically allocated memory pointed to by the fields. * @param p The pool to allocate the new table out of * @param t The table to clone * @return A deep copy of the table passed in */ APR_DECLARE(apr_table_t *) apr_table_clone(apr_pool_t *p, const apr_table_t *t); #endif #if APR_MAJOR_VERSION < 1 #ifndef APR_FOPEN_READ #define APR_FOPEN_READ APR_READ #endif #ifndef APR_FOPEN_BUFFERED #define APR_FOPEN_BUFFERED APR_BUFFERED #endif #ifndef APR_FOPEN_BINARY #define APR_FOPEN_BINARY APR_BINARY #endif #ifndef APR_FOPEN_CREATE #define APR_FOPEN_CREATE APR_CREATE #endif #ifndef APR_FOPEN_WRITE #define APR_FOPEN_WRITE APR_WRITE #endif #ifndef APR_FOPEN_SHARELOCK #define APR_FOPEN_SHARELOCK APR_SHARELOCK #endif #endif #if defined(_WIN32) struct mctimeval { long tv_sec; /* seconds */ long tv_usec; /* and microseconds */ }; void mapcache_gettimeofday(struct mctimeval *t, void *__not_used_here__); #else # include /* for gettimeofday() */ # define mctimeval timeval # define mapcache_gettimeofday(t,u) gettimeofday(t,u) #endif #endif /* UTIL_H_ */ /* vim: ts=2 sts=2 et sw=2 */ mapcache-1.4.0/lib/000077500000000000000000000000001255567662100140425ustar00rootroot00000000000000mapcache-1.4.0/lib/axisorder.c000066400000000000000000000544031255567662100162140ustar00rootroot00000000000000/****************************************************************************** * $Id$ * * Project: MapServer * Purpose: Axis lookup table * 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" static struct axisOrientationEpsgCodes_s { int code; } axisOrientationEpsgCodes[] = { { 4326 }, { 4258 }, { 31466 }, { 31467 }, { 31468 }, { 31469 }, { 2166 }, { 2167 }, { 2168 }, { 2036 }, { 2044 }, { 2045 }, { 2065 }, { 2081 }, { 2082 }, { 2083 }, { 2085 }, { 2086 }, { 2091 }, { 2092 }, { 2093 }, { 2096 }, { 2097 }, { 2098 }, { 2105 }, { 2106 }, { 2107 }, { 2108 }, { 2109 }, { 2110 }, { 2111 }, { 2112 }, { 2113 }, { 2114 }, { 2115 }, { 2116 }, { 2117 }, { 2118 }, { 2119 }, { 2120 }, { 2121 }, { 2122 }, { 2123 }, { 2124 }, { 2125 }, { 2126 }, { 2127 }, { 2128 }, { 2129 }, { 2130 }, { 2131 }, { 2132 }, { 2169 }, { 2170 }, { 2171 }, { 2172 }, { 2173 }, { 2174 }, { 2175 }, { 2176 }, { 2177 }, { 2178 }, { 2179 }, { 2180 }, { 2193 }, { 2199 }, { 2200 }, { 2206 }, { 2207 }, { 2208 }, { 2209 }, { 2210 }, { 2211 }, { 2212 }, { 2319 }, { 2320 }, { 2321 }, { 2322 }, { 2323 }, { 2324 }, { 2325 }, { 2326 }, { 2327 }, { 2328 }, { 2329 }, { 2330 }, { 2331 }, { 2332 }, { 2333 }, { 2334 }, { 2335 }, { 2336 }, { 2337 }, { 2338 }, { 2339 }, { 2340 }, { 2341 }, { 2342 }, { 2343 }, { 2344 }, { 2345 }, { 2346 }, { 2347 }, { 2348 }, { 2349 }, { 2350 }, { 2351 }, { 2352 }, { 2353 }, { 2354 }, { 2355 }, { 2356 }, { 2357 }, { 2358 }, { 2359 }, { 2360 }, { 2361 }, { 2362 }, { 2363 }, { 2364 }, { 2365 }, { 2366 }, { 2367 }, { 2368 }, { 2369 }, { 2370 }, { 2371 }, { 2372 }, { 2373 }, { 2374 }, { 2375 }, { 2376 }, { 2377 }, { 2378 }, { 2379 }, { 2380 }, { 2381 }, { 2382 }, { 2383 }, { 2384 }, { 2385 }, { 2386 }, { 2387 }, { 2388 }, { 2389 }, { 2390 }, { 2391 }, { 2392 }, { 2393 }, { 2394 }, { 2395 }, { 2396 }, { 2397 }, { 2398 }, { 2399 }, { 2400 }, { 2401 }, { 2402 }, { 2403 }, { 2404 }, { 2405 }, { 2406 }, { 2407 }, { 2408 }, { 2409 }, { 2410 }, { 2411 }, { 2412 }, { 2413 }, { 2414 }, { 2415 }, { 2416 }, { 2417 }, { 2418 }, { 2419 }, { 2420 }, { 2421 }, { 2422 }, { 2423 }, { 2424 }, { 2425 }, { 2426 }, { 2427 }, { 2428 }, { 2429 }, { 2430 }, { 2431 }, { 2432 }, { 2433 }, { 2434 }, { 2435 }, { 2436 }, { 2437 }, { 2438 }, { 2439 }, { 2440 }, { 2441 }, { 2442 }, { 2443 }, { 2444 }, { 2445 }, { 2446 }, { 2447 }, { 2448 }, { 2449 }, { 2450 }, { 2451 }, { 2452 }, { 2453 }, { 2454 }, { 2455 }, { 2456 }, { 2457 }, { 2458 }, { 2459 }, { 2460 }, { 2461 }, { 2462 }, { 2463 }, { 2464 }, { 2465 }, { 2466 }, { 2467 }, { 2468 }, { 2469 }, { 2470 }, { 2471 }, { 2472 }, { 2473 }, { 2474 }, { 2475 }, { 2476 }, { 2477 }, { 2478 }, { 2479 }, { 2480 }, { 2481 }, { 2482 }, { 2483 }, { 2484 }, { 2485 }, { 2486 }, { 2487 }, { 2488 }, { 2489 }, { 2490 }, { 2491 }, { 2492 }, { 2493 }, { 2494 }, { 2495 }, { 2496 }, { 2497 }, { 2498 }, { 2499 }, { 2500 }, { 2501 }, { 2502 }, { 2503 }, { 2504 }, { 2505 }, { 2506 }, { 2507 }, { 2508 }, { 2509 }, { 2510 }, { 2511 }, { 2512 }, { 2513 }, { 2514 }, { 2515 }, { 2516 }, { 2517 }, { 2518 }, { 2519 }, { 2520 }, { 2521 }, { 2522 }, { 2523 }, { 2524 }, { 2525 }, { 2526 }, { 2527 }, { 2528 }, { 2529 }, { 2530 }, { 2531 }, { 2532 }, { 2533 }, { 2534 }, { 2535 }, { 2536 }, { 2537 }, { 2538 }, { 2539 }, { 2540 }, { 2541 }, { 2542 }, { 2543 }, { 2544 }, { 2545 }, { 2546 }, { 2547 }, { 2548 }, { 2549 }, { 2551 }, { 2552 }, { 2553 }, { 2554 }, { 2555 }, { 2556 }, { 2557 }, { 2558 }, { 2559 }, { 2560 }, { 2561 }, { 2562 }, { 2563 }, { 2564 }, { 2565 }, { 2566 }, { 2567 }, { 2568 }, { 2569 }, { 2570 }, { 2571 }, { 2572 }, { 2573 }, { 2574 }, { 2575 }, { 2576 }, { 2577 }, { 2578 }, { 2579 }, { 2580 }, { 2581 }, { 2582 }, { 2583 }, { 2584 }, { 2585 }, { 2586 }, { 2587 }, { 2588 }, { 2589 }, { 2590 }, { 2591 }, { 2592 }, { 2593 }, { 2594 }, { 2595 }, { 2596 }, { 2597 }, { 2598 }, { 2599 }, { 2600 }, { 2601 }, { 2602 }, { 2603 }, { 2604 }, { 2605 }, { 2606 }, { 2607 }, { 2608 }, { 2609 }, { 2610 }, { 2611 }, { 2612 }, { 2613 }, { 2614 }, { 2615 }, { 2616 }, { 2617 }, { 2618 }, { 2619 }, { 2620 }, { 2621 }, { 2622 }, { 2623 }, { 2624 }, { 2625 }, { 2626 }, { 2627 }, { 2628 }, { 2629 }, { 2630 }, { 2631 }, { 2632 }, { 2633 }, { 2634 }, { 2635 }, { 2636 }, { 2637 }, { 2638 }, { 2639 }, { 2640 }, { 2641 }, { 2642 }, { 2643 }, { 2644 }, { 2645 }, { 2646 }, { 2647 }, { 2648 }, { 2649 }, { 2650 }, { 2651 }, { 2652 }, { 2653 }, { 2654 }, { 2655 }, { 2656 }, { 2657 }, { 2658 }, { 2659 }, { 2660 }, { 2661 }, { 2662 }, { 2663 }, { 2664 }, { 2665 }, { 2666 }, { 2667 }, { 2668 }, { 2669 }, { 2670 }, { 2671 }, { 2672 }, { 2673 }, { 2674 }, { 2675 }, { 2676 }, { 2677 }, { 2678 }, { 2679 }, { 2680 }, { 2681 }, { 2682 }, { 2683 }, { 2684 }, { 2685 }, { 2686 }, { 2687 }, { 2688 }, { 2689 }, { 2690 }, { 2691 }, { 2692 }, { 2693 }, { 2694 }, { 2695 }, { 2696 }, { 2697 }, { 2698 }, { 2699 }, { 2700 }, { 2701 }, { 2702 }, { 2703 }, { 2704 }, { 2705 }, { 2706 }, { 2707 }, { 2708 }, { 2709 }, { 2710 }, { 2711 }, { 2712 }, { 2713 }, { 2714 }, { 2715 }, { 2716 }, { 2717 }, { 2718 }, { 2719 }, { 2720 }, { 2721 }, { 2722 }, { 2723 }, { 2724 }, { 2725 }, { 2726 }, { 2727 }, { 2728 }, { 2729 }, { 2730 }, { 2731 }, { 2732 }, { 2733 }, { 2734 }, { 2735 }, { 2738 }, { 2739 }, { 2740 }, { 2741 }, { 2742 }, { 2743 }, { 2744 }, { 2745 }, { 2746 }, { 2747 }, { 2748 }, { 2749 }, { 2750 }, { 2751 }, { 2752 }, { 2753 }, { 2754 }, { 2755 }, { 2756 }, { 2757 }, { 2758 }, { 2935 }, { 2936 }, { 2937 }, { 2938 }, { 2939 }, { 2940 }, { 2941 }, { 2953 }, { 2963 }, { 3006 }, { 3007 }, { 3008 }, { 3009 }, { 3010 }, { 3011 }, { 3012 }, { 3013 }, { 3014 }, { 3015 }, { 3016 }, { 3017 }, { 3018 }, { 3019 }, { 3020 }, { 3021 }, { 3022 }, { 3023 }, { 3024 }, { 3025 }, { 3026 }, { 3027 }, { 3028 }, { 3029 }, { 3030 }, { 3034 }, { 3035 }, { 3038 }, { 3039 }, { 3040 }, { 3041 }, { 3042 }, { 3043 }, { 3044 }, { 3045 }, { 3046 }, { 3047 }, { 3048 }, { 3049 }, { 3050 }, { 3051 }, { 3058 }, { 3059 }, { 3068 }, { 3114 }, { 3115 }, { 3116 }, { 3117 }, { 3118 }, { 3120 }, { 3126 }, { 3127 }, { 3128 }, { 3129 }, { 3130 }, { 3131 }, { 3132 }, { 3133 }, { 3134 }, { 3135 }, { 3136 }, { 3137 }, { 3138 }, { 3139 }, { 3140 }, { 3146 }, { 3147 }, { 3150 }, { 3151 }, { 3152 }, { 3300 }, { 3301 }, { 3328 }, { 3329 }, { 3330 }, { 3331 }, { 3332 }, { 3333 }, { 3334 }, { 3335 }, { 3346 }, { 3350 }, { 3351 }, { 3352 }, { 3366 }, { 3386 }, { 3387 }, { 3388 }, { 3389 }, { 3390 }, { 3396 }, { 3397 }, { 3398 }, { 3399 }, { 3407 }, { 3414 }, { 3416 }, { 3764 }, { 3788 }, { 3789 }, { 3790 }, { 3791 }, { 3793 }, { 3795 }, { 3796 }, { 3819 }, { 3821 }, { 3823 }, { 3824 }, { 3833 }, { 3834 }, { 3835 }, { 3836 }, { 3837 }, { 3838 }, { 3839 }, { 3840 }, { 3841 }, { 3842 }, { 3843 }, { 3844 }, { 3845 }, { 3846 }, { 3847 }, { 3848 }, { 3849 }, { 3850 }, { 3851 }, { 3852 }, { 3854 }, { 3873 }, { 3874 }, { 3875 }, { 3876 }, { 3877 }, { 3878 }, { 3879 }, { 3880 }, { 3881 }, { 3882 }, { 3883 }, { 3884 }, { 3885 }, { 3888 }, { 3889 }, { 3906 }, { 3907 }, { 3908 }, { 3909 }, { 3910 }, { 3911 }, { 4001 }, { 4002 }, { 4003 }, { 4004 }, { 4005 }, { 4006 }, { 4007 }, { 4008 }, { 4009 }, { 4010 }, { 4011 }, { 4012 }, { 4013 }, { 4014 }, { 4015 }, { 4016 }, { 4017 }, { 4018 }, { 4019 }, { 4020 }, { 4021 }, { 4022 }, { 4023 }, { 4024 }, { 4025 }, { 4026 }, { 4027 }, { 4028 }, { 4029 }, { 4030 }, { 4031 }, { 4032 }, { 4033 }, { 4034 }, { 4035 }, { 4036 }, { 4037 }, { 4038 }, { 4040 }, { 4041 }, { 4042 }, { 4043 }, { 4044 }, { 4045 }, { 4046 }, { 4047 }, { 4052 }, { 4053 }, { 4054 }, { 4055 }, { 4074 }, { 4075 }, { 4080 }, { 4081 }, { 4120 }, { 4121 }, { 4122 }, { 4123 }, { 4124 }, { 4125 }, { 4126 }, { 4127 }, { 4128 }, { 4129 }, { 4130 }, { 4131 }, { 4132 }, { 4133 }, { 4134 }, { 4135 }, { 4136 }, { 4137 }, { 4138 }, { 4139 }, { 4140 }, { 4141 }, { 4142 }, { 4143 }, { 4144 }, { 4145 }, { 4146 }, { 4147 }, { 4148 }, { 4149 }, { 4150 }, { 4151 }, { 4152 }, { 4153 }, { 4154 }, { 4155 }, { 4156 }, { 4157 }, { 4158 }, { 4159 }, { 4160 }, { 4161 }, { 4162 }, { 4163 }, { 4164 }, { 4165 }, { 4166 }, { 4167 }, { 4168 }, { 4169 }, { 4170 }, { 4171 }, { 4172 }, { 4173 }, { 4174 }, { 4175 }, { 4176 }, { 4178 }, { 4179 }, { 4180 }, { 4181 }, { 4182 }, { 4183 }, { 4184 }, { 4185 }, { 4188 }, { 4189 }, { 4190 }, { 4191 }, { 4192 }, { 4193 }, { 4194 }, { 4195 }, { 4196 }, { 4197 }, { 4198 }, { 4199 }, { 4200 }, { 4201 }, { 4202 }, { 4203 }, { 4204 }, { 4205 }, { 4206 }, { 4207 }, { 4208 }, { 4209 }, { 4210 }, { 4211 }, { 4212 }, { 4213 }, { 4214 }, { 4215 }, { 4216 }, { 4218 }, { 4219 }, { 4220 }, { 4221 }, { 4222 }, { 4223 }, { 4224 }, { 4225 }, { 4226 }, { 4227 }, { 4228 }, { 4229 }, { 4230 }, { 4231 }, { 4232 }, { 4233 }, { 4234 }, { 4235 }, { 4236 }, { 4237 }, { 4238 }, { 4239 }, { 4240 }, { 4241 }, { 4242 }, { 4243 }, { 4244 }, { 4245 }, { 4246 }, { 4247 }, { 4248 }, { 4249 }, { 4250 }, { 4251 }, { 4252 }, { 4253 }, { 4254 }, { 4255 }, { 4256 }, { 4257 }, { 4259 }, { 4260 }, { 4261 }, { 4262 }, { 4263 }, { 4264 }, { 4265 }, { 4266 }, { 4267 }, { 4268 }, { 4269 }, { 4270 }, { 4271 }, { 4272 }, { 4273 }, { 4274 }, { 4275 }, { 4276 }, { 4277 }, { 4278 }, { 4279 }, { 4280 }, { 4281 }, { 4282 }, { 4283 }, { 4284 }, { 4285 }, { 4286 }, { 4287 }, { 4288 }, { 4289 }, { 4291 }, { 4292 }, { 4293 }, { 4294 }, { 4295 }, { 4296 }, { 4297 }, { 4298 }, { 4299 }, { 4300 }, { 4301 }, { 4302 }, { 4303 }, { 4304 }, { 4306 }, { 4307 }, { 4308 }, { 4309 }, { 4310 }, { 4311 }, { 4312 }, { 4313 }, { 4314 }, { 4315 }, { 4316 }, { 4317 }, { 4318 }, { 4319 }, { 4322 }, { 4324 }, { 4327 }, { 4329 }, { 4339 }, { 4341 }, { 4343 }, { 4345 }, { 4347 }, { 4349 }, { 4351 }, { 4353 }, { 4355 }, { 4357 }, { 4359 }, { 4361 }, { 4363 }, { 4365 }, { 4367 }, { 4369 }, { 4371 }, { 4373 }, { 4375 }, { 4377 }, { 4379 }, { 4381 }, { 4383 }, { 4386 }, { 4388 }, { 4417 }, { 4434 }, { 4463 }, { 4466 }, { 4469 }, { 4470 }, { 4472 }, { 4475 }, { 4480 }, { 4482 }, { 4483 }, { 4490 }, { 4491 }, { 4492 }, { 4493 }, { 4494 }, { 4495 }, { 4496 }, { 4497 }, { 4498 }, { 4499 }, { 4500 }, { 4501 }, { 4502 }, { 4503 }, { 4504 }, { 4505 }, { 4506 }, { 4507 }, { 4508 }, { 4509 }, { 4510 }, { 4511 }, { 4512 }, { 4513 }, { 4514 }, { 4515 }, { 4516 }, { 4517 }, { 4518 }, { 4519 }, { 4520 }, { 4521 }, { 4522 }, { 4523 }, { 4524 }, { 4525 }, { 4526 }, { 4527 }, { 4528 }, { 4529 }, { 4530 }, { 4531 }, { 4532 }, { 4533 }, { 4534 }, { 4535 }, { 4536 }, { 4537 }, { 4538 }, { 4539 }, { 4540 }, { 4541 }, { 4542 }, { 4543 }, { 4544 }, { 4545 }, { 4546 }, { 4547 }, { 4548 }, { 4549 }, { 4550 }, { 4551 }, { 4552 }, { 4553 }, { 4554 }, { 4555 }, { 4557 }, { 4558 }, { 4568 }, { 4569 }, { 4570 }, { 4571 }, { 4572 }, { 4573 }, { 4574 }, { 4575 }, { 4576 }, { 4577 }, { 4578 }, { 4579 }, { 4580 }, { 4581 }, { 4582 }, { 4583 }, { 4584 }, { 4585 }, { 4586 }, { 4587 }, { 4588 }, { 4589 }, { 4600 }, { 4601 }, { 4602 }, { 4603 }, { 4604 }, { 4605 }, { 4606 }, { 4607 }, { 4608 }, { 4609 }, { 4610 }, { 4611 }, { 4612 }, { 4613 }, { 4614 }, { 4615 }, { 4616 }, { 4617 }, { 4618 }, { 4619 }, { 4620 }, { 4621 }, { 4622 }, { 4623 }, { 4624 }, { 4625 }, { 4626 }, { 4627 }, { 4628 }, { 4629 }, { 4630 }, { 4631 }, { 4632 }, { 4633 }, { 4634 }, { 4635 }, { 4636 }, { 4637 }, { 4638 }, { 4639 }, { 4640 }, { 4641 }, { 4642 }, { 4643 }, { 4644 }, { 4645 }, { 4646 }, { 4652 }, { 4653 }, { 4654 }, { 4655 }, { 4656 }, { 4657 }, { 4658 }, { 4659 }, { 4660 }, { 4661 }, { 4662 }, { 4663 }, { 4664 }, { 4665 }, { 4666 }, { 4667 }, { 4668 }, { 4669 }, { 4670 }, { 4671 }, { 4672 }, { 4673 }, { 4674 }, { 4675 }, { 4676 }, { 4677 }, { 4678 }, { 4679 }, { 4680 }, { 4681 }, { 4682 }, { 4683 }, { 4684 }, { 4685 }, { 4686 }, { 4687 }, { 4688 }, { 4689 }, { 4690 }, { 4691 }, { 4692 }, { 4693 }, { 4694 }, { 4695 }, { 4696 }, { 4697 }, { 4698 }, { 4699 }, { 4700 }, { 4701 }, { 4702 }, { 4703 }, { 4704 }, { 4705 }, { 4706 }, { 4707 }, { 4708 }, { 4709 }, { 4710 }, { 4711 }, { 4712 }, { 4713 }, { 4714 }, { 4715 }, { 4716 }, { 4717 }, { 4718 }, { 4719 }, { 4720 }, { 4721 }, { 4722 }, { 4723 }, { 4724 }, { 4725 }, { 4726 }, { 4727 }, { 4728 }, { 4729 }, { 4730 }, { 4731 }, { 4732 }, { 4733 }, { 4734 }, { 4735 }, { 4736 }, { 4737 }, { 4738 }, { 4739 }, { 4740 }, { 4741 }, { 4742 }, { 4743 }, { 4744 }, { 4745 }, { 4746 }, { 4747 }, { 4748 }, { 4749 }, { 4750 }, { 4751 }, { 4752 }, { 4753 }, { 4754 }, { 4755 }, { 4756 }, { 4757 }, { 4758 }, { 4759 }, { 4760 }, { 4761 }, { 4762 }, { 4763 }, { 4764 }, { 4765 }, { 4766 }, { 4767 }, { 4768 }, { 4769 }, { 4770 }, { 4771 }, { 4772 }, { 4773 }, { 4774 }, { 4775 }, { 4776 }, { 4777 }, { 4778 }, { 4779 }, { 4780 }, { 4781 }, { 4782 }, { 4783 }, { 4784 }, { 4785 }, { 4786 }, { 4787 }, { 4788 }, { 4789 }, { 4790 }, { 4791 }, { 4792 }, { 4793 }, { 4794 }, { 4795 }, { 4796 }, { 4797 }, { 4798 }, { 4799 }, { 4800 }, { 4801 }, { 4802 }, { 4803 }, { 4804 }, { 4805 }, { 4806 }, { 4807 }, { 4808 }, { 4809 }, { 4810 }, { 4811 }, { 4812 }, { 4813 }, { 4814 }, { 4815 }, { 4816 }, { 4817 }, { 4818 }, { 4819 }, { 4820 }, { 4821 }, { 4822 }, { 4823 }, { 4824 }, { 4839 }, { 4855 }, { 4856 }, { 4857 }, { 4858 }, { 4859 }, { 4860 }, { 4861 }, { 4862 }, { 4863 }, { 4864 }, { 4865 }, { 4866 }, { 4867 }, { 4868 }, { 4869 }, { 4870 }, { 4871 }, { 4872 }, { 4873 }, { 4874 }, { 4875 }, { 4876 }, { 4877 }, { 4878 }, { 4879 }, { 4880 }, { 4883 }, { 4885 }, { 4887 }, { 4889 }, { 4891 }, { 4893 }, { 4895 }, { 4898 }, { 4900 }, { 4901 }, { 4902 }, { 4903 }, { 4904 }, { 4907 }, { 4909 }, { 4921 }, { 4923 }, { 4925 }, { 4927 }, { 4929 }, { 4931 }, { 4933 }, { 4935 }, { 4937 }, { 4939 }, { 4941 }, { 4943 }, { 4945 }, { 4947 }, { 4949 }, { 4951 }, { 4953 }, { 4955 }, { 4957 }, { 4959 }, { 4961 }, { 4963 }, { 4965 }, { 4967 }, { 4969 }, { 4971 }, { 4973 }, { 4975 }, { 4977 }, { 4979 }, { 4981 }, { 4983 }, { 4985 }, { 4987 }, { 4989 }, { 4991 }, { 4993 }, { 4995 }, { 4997 }, { 4999 }, { 5012 }, { 5013 }, { 5017 }, { 5048 }, { 5105 }, { 5106 }, { 5107 }, { 5108 }, { 5109 }, { 5110 }, { 5111 }, { 5112 }, { 5113 }, { 5114 }, { 5115 }, { 5116 }, { 5117 }, { 5118 }, { 5119 }, { 5120 }, { 5121 }, { 5122 }, { 5123 }, { 5124 }, { 5125 }, { 5126 }, { 5127 }, { 5128 }, { 5129 }, { 5130 }, { 5132 }, { 5167 }, { 5168 }, { 5169 }, { 5170 }, { 5171 }, { 5172 }, { 5173 }, { 5174 }, { 5175 }, { 5176 }, { 5177 }, { 5178 }, { 5179 }, { 5180 }, { 5181 }, { 5182 }, { 5183 }, { 5184 }, { 5185 }, { 5186 }, { 5187 }, { 5188 }, { 5224 }, { 5228 }, { 5229 }, { 5233 }, { 5245 }, { 5246 }, { 5251 }, { 5252 }, { 5253 }, { 5254 }, { 5255 }, { 5256 }, { 5257 }, { 5258 }, { 5259 }, { 5263 }, { 5264 }, { 5269 }, { 5270 }, { 5271 }, { 5272 }, { 5273 }, { 5274 }, { 5275 }, { 5801 }, { 5802 }, { 5803 }, { 5804 }, { 5808 }, { 5809 }, { 5810 }, { 5811 }, { 5812 }, { 5813 }, { 5814 }, { 5815 }, { 5816 }, { 20004 }, { 20005 }, { 20006 }, { 20007 }, { 20008 }, { 20009 }, { 20010 }, { 20011 }, { 20012 }, { 20013 }, { 20014 }, { 20015 }, { 20016 }, { 20017 }, { 20018 }, { 20019 }, { 20020 }, { 20021 }, { 20022 }, { 20023 }, { 20024 }, { 20025 }, { 20026 }, { 20027 }, { 20028 }, { 20029 }, { 20030 }, { 20031 }, { 20032 }, { 20064 }, { 20065 }, { 20066 }, { 20067 }, { 20068 }, { 20069 }, { 20070 }, { 20071 }, { 20072 }, { 20073 }, { 20074 }, { 20075 }, { 20076 }, { 20077 }, { 20078 }, { 20079 }, { 20080 }, { 20081 }, { 20082 }, { 20083 }, { 20084 }, { 20085 }, { 20086 }, { 20087 }, { 20088 }, { 20089 }, { 20090 }, { 20091 }, { 20092 }, { 21413 }, { 21414 }, { 21415 }, { 21416 }, { 21417 }, { 21418 }, { 21419 }, { 21420 }, { 21421 }, { 21422 }, { 21423 }, { 21453 }, { 21454 }, { 21455 }, { 21456 }, { 21457 }, { 21458 }, { 21459 }, { 21460 }, { 21461 }, { 21462 }, { 21463 }, { 21473 }, { 21474 }, { 21475 }, { 21476 }, { 21477 }, { 21478 }, { 21479 }, { 21480 }, { 21481 }, { 21482 }, { 21483 }, { 21896 }, { 21897 }, { 21898 }, { 21899 }, { 22171 }, { 22172 }, { 22173 }, { 22174 }, { 22175 }, { 22176 }, { 22177 }, { 22181 }, { 22182 }, { 22183 }, { 22184 }, { 22185 }, { 22186 }, { 22187 }, { 22191 }, { 22192 }, { 22193 }, { 22194 }, { 22195 }, { 22196 }, { 22197 }, { 25884 }, { 27205 }, { 27206 }, { 27207 }, { 27208 }, { 27209 }, { 27210 }, { 27211 }, { 27212 }, { 27213 }, { 27214 }, { 27215 }, { 27216 }, { 27217 }, { 27218 }, { 27219 }, { 27220 }, { 27221 }, { 27222 }, { 27223 }, { 27224 }, { 27225 }, { 27226 }, { 27227 }, { 27228 }, { 27229 }, { 27230 }, { 27231 }, { 27232 }, { 27391 }, { 27392 }, { 27393 }, { 27394 }, { 27395 }, { 27396 }, { 27397 }, { 27398 }, { 27492 }, { 28402 }, { 28403 }, { 28404 }, { 28405 }, { 28406 }, { 28407 }, { 28408 }, { 28409 }, { 28410 }, { 28411 }, { 28412 }, { 28413 }, { 28414 }, { 28415 }, { 28416 }, { 28417 }, { 28418 }, { 28419 }, { 28420 }, { 28421 }, { 28422 }, { 28423 }, { 28424 }, { 28425 }, { 28426 }, { 28427 }, { 28428 }, { 28429 }, { 28430 }, { 28431 }, { 28432 }, { 28462 }, { 28463 }, { 28464 }, { 28465 }, { 28466 }, { 28467 }, { 28468 }, { 28469 }, { 28470 }, { 28471 }, { 28472 }, { 28473 }, { 28474 }, { 28475 }, { 28476 }, { 28477 }, { 28478 }, { 28479 }, { 28480 }, { 28481 }, { 28482 }, { 28483 }, { 28484 }, { 28485 }, { 28486 }, { 28487 }, { 28488 }, { 28489 }, { 28490 }, { 28491 }, { 28492 }, { 29701 }, { 29702 }, { 30161 }, { 30162 }, { 30163 }, { 30164 }, { 30165 }, { 30166 }, { 30167 }, { 30168 }, { 30169 }, { 30170 }, { 30171 }, { 30172 }, { 30173 }, { 30174 }, { 30175 }, { 30176 }, { 30177 }, { 30178 }, { 30179 }, { 30800 }, { 31251 }, { 31252 }, { 31253 }, { 31254 }, { 31255 }, { 31256 }, { 31257 }, { 31258 }, { 31259 }, { 31275 }, { 31276 }, { 31277 }, { 31278 }, { 31279 }, { 31281 }, { 31282 }, { 31283 }, { 31284 }, { 31285 }, { 31286 }, { 31287 }, { 31288 }, { 31289 }, { 31290 }, { 31700 }, }; #define AXIS_ORIENTATION_TABLE_SIZE 1703 int mapcache_is_axis_inverted(const char *srs) { int i,code; if(strncasecmp(srs,"epsg:",5) || strlen(srs)<6) { /* if we don't have an epsg formated srs */ return MAPCACHE_FALSE; } code = atoi(&(srs[5])); /*check the static table*/ for (i=0; i #define INITIAL_BUFFER_SIZE 100 static void _mapcache_buffer_realloc(mapcache_buffer *buffer, apr_off_t len) { if(buffer->avail) { unsigned char* newbuf ; while ( len > buffer->avail ) { buffer->avail += buffer->avail; } newbuf = realloc(buffer->buf, buffer->avail) ; if ( newbuf != buffer->buf ) { if ( buffer->buf ) apr_pool_cleanup_kill(buffer->pool, buffer->buf, (void*)free) ; apr_pool_cleanup_register(buffer->pool, newbuf,(void*)free, apr_pool_cleanup_null); buffer->buf = newbuf ; } } else { buffer->avail = len; buffer->buf = malloc(buffer->avail); apr_pool_cleanup_register(buffer->pool, buffer->buf,(void*)free, apr_pool_cleanup_null); } } 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); apr_pool_cleanup_register(buffer->pool, buffer->buf,(void*)free, apr_pool_cleanup_null); } else { buffer->buf = NULL; } return buffer; } int mapcache_buffer_append(mapcache_buffer *buffer, size_t len, void *data) { size_t total = buffer->size + len; if(total > buffer->avail) _mapcache_buffer_realloc(buffer,total); memcpy(((unsigned char*)buffer->buf) + buffer->size, data, len); buffer->size += len; return len; } /* vim: ts=2 sts=2 et sw=2 */ mapcache-1.4.0/lib/cache_bdb.c000066400000000000000000000337531255567662100160730ustar00rootroot00000000000000/****************************************************************************** * $Id$ * * Project: MapServer * Purpose: MapCache tile caching support file: Berkeley DB cache backend * 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-config.h" #ifdef USE_BDB #include "mapcache.h" #include #include #include #include #include #include #include #ifdef APR_HAS_THREADS #include #endif #ifndef _WIN32 #include #endif #include #define PAGESIZE 64*1024 #define CACHESIZE 1024*1024 struct bdb_env { DB* db; DB_ENV *env; int readonly; char *errmsg; }; void mapcache_bdb_connection_constructor(mapcache_context *ctx, void **conn_, void *params, apr_pool_t *pool) { int ret; int env_flags; int mode; mapcache_cache_bdb *cache = (mapcache_cache_bdb*)params; char *dbfile = apr_pstrcat(pool,cache->basedir,"/",cache->cache.name,".db",NULL); struct bdb_env *benv = calloc(1,sizeof(struct bdb_env)); *conn_ = benv; ret = db_env_create(&benv->env, 0); if(ret) { ctx->set_error(ctx, 500, "bdb cache failure for db_env_create: %s", db_strerror(ret)); free(benv); return; } ret = benv->env->set_cachesize(benv->env,0,CACHESIZE,1); /* set a larger cache size than default */ if(ret) { ctx->set_error(ctx, 500, "bdb cache failure for db->set_cachesize: %s", db_strerror(ret)); free(benv); return; } env_flags = DB_INIT_CDB|DB_INIT_MPOOL|DB_CREATE; ret = benv->env->open(benv->env,cache->basedir,env_flags,0); if(ret) { ctx->set_error(ctx,500,"bdb cache failure for env->open: %s", db_strerror(ret)); free(benv); return; } if ((ret = db_create(&benv->db, benv->env, 0)) != 0) { ctx->set_error(ctx,500,"bdb cache failure for db_create: %s", db_strerror(ret)); free(benv); } mode = DB_BTREE; ret = benv->db->set_pagesize(benv->db,PAGESIZE); /* set pagesize to maximum allowed, as tile data is usually pretty large */ if(ret) { ctx->set_error(ctx,500,"bdb cache failure for db->set_pagesize: %s", db_strerror(ret)); free(benv); return; } if ((ret = benv->db->open(benv->db, NULL, dbfile, NULL, mode, DB_CREATE, 0664)) != 0) { ctx->set_error(ctx,500,"bdb cache failure 1 for db->open: %s", db_strerror(ret)); free(benv); return; } } void mapcache_bdb_connection_destructor(void *conn_, apr_pool_t *pool) { struct bdb_env *benv = (struct bdb_env*)conn_; benv->db->close(benv->db,0); benv->env->close(benv->env,0); free(benv); } static mapcache_pooled_connection* _bdb_get_conn(mapcache_context *ctx, mapcache_cache_bdb *cache, mapcache_tile* tile, int readonly) { struct bdb_env *benv; mapcache_pooled_connection *pc; char *conn_key = apr_pstrcat(ctx->pool,readonly?"ro_":"rw_",cache->cache.name,NULL); pc = mapcache_connection_pool_get_connection(ctx,conn_key,mapcache_bdb_connection_constructor, mapcache_bdb_connection_destructor, cache); if(GC_HAS_ERROR(ctx)) return NULL; benv = pc->connection; benv->readonly = readonly; return pc; } static void _bdb_release_conn(mapcache_context *ctx, mapcache_cache_bdb *cache, mapcache_tile *tile, mapcache_pooled_connection *pc) { if(GC_HAS_ERROR(ctx)) { mapcache_connection_pool_invalidate_connection(ctx, pc); } else { mapcache_connection_pool_release_connection(ctx,pc); } } static int _mapcache_cache_bdb_has_tile(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { int ret; DBT key; mapcache_cache_bdb *cache = (mapcache_cache_bdb*)pcache; char *skey = mapcache_util_get_tile_key(ctx,tile,cache->key_template,NULL,NULL); mapcache_pooled_connection *pc; struct bdb_env *benv; pc = _bdb_get_conn(ctx,cache,tile,1); if(GC_HAS_ERROR(ctx)) return MAPCACHE_FALSE; benv = pc->connection; memset(&key, 0, sizeof(DBT)); key.data = skey; key.size = strlen(skey)+1; ret = benv->db->exists(benv->db, NULL, &key, 0); if(ret == 0) { ret = MAPCACHE_TRUE; } else if(ret == DB_NOTFOUND) { ret = MAPCACHE_FALSE; } else { ctx->set_error(ctx,500,"bdb backend failure on tile_exists: %s",db_strerror(ret)); ret= MAPCACHE_FALSE; } _bdb_release_conn(ctx,cache,tile,pc); return ret; } static void _mapcache_cache_bdb_delete(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { DBT key; int ret; mapcache_cache_bdb *cache = (mapcache_cache_bdb*)pcache; char *skey = mapcache_util_get_tile_key(ctx,tile,cache->key_template,NULL,NULL); mapcache_pooled_connection *pc; struct bdb_env *benv; pc = _bdb_get_conn(ctx,cache,tile,0); GC_CHECK_ERROR(ctx); benv = pc->connection; memset(&key, 0, sizeof(DBT)); key.data = skey; key.size = strlen(skey)+1; ret = benv->db->del(benv->db, NULL, &key, 0); if(ret && ret != DB_NOTFOUND) { ctx->set_error(ctx,500,"bdb backend failure on tile_delete: %s",db_strerror(ret)); } else { ret = benv->db->sync(benv->db,0); if(ret) ctx->set_error(ctx,500,"bdb backend sync failure on tile_delete: %s",db_strerror(ret)); } _bdb_release_conn(ctx,cache,tile,pc); } static int _mapcache_cache_bdb_get(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { DBT key,data; int ret; mapcache_cache_bdb *cache = (mapcache_cache_bdb*)pcache; mapcache_pooled_connection *pc; struct bdb_env *benv; pc = _bdb_get_conn(ctx,cache,tile,1); if(GC_HAS_ERROR(ctx)) return MAPCACHE_FALSE; benv = pc->connection; char *skey = mapcache_util_get_tile_key(ctx,tile,cache->key_template,NULL,NULL); memset(&key, 0, sizeof(DBT)); memset(&data, 0, sizeof(DBT)); data.flags = DB_DBT_MALLOC; key.data = skey; key.size = strlen(skey)+1; ret = benv->db->get(benv->db, NULL, &key, &data, 0); if(ret == 0) { if(((char*)(data.data))[0] == '#') { tile->encoded_data = mapcache_empty_png_decode(ctx,tile->grid_link->grid->tile_sx, tile->grid_link->grid->tile_sy, (unsigned char*)data.data,&tile->nodata); } else { tile->encoded_data = mapcache_buffer_create(0,ctx->pool); tile->encoded_data->buf = data.data; tile->encoded_data->size = data.size-sizeof(apr_time_t); tile->encoded_data->avail = data.size; apr_pool_cleanup_register(ctx->pool, tile->encoded_data->buf,(void*)free, apr_pool_cleanup_null); } tile->mtime = *((apr_time_t*)(((char*)data.data)+data.size-sizeof(apr_time_t))); ret = MAPCACHE_SUCCESS; } else if(ret == DB_NOTFOUND) { ret = MAPCACHE_CACHE_MISS; } else { ctx->set_error(ctx,500,"bdb backend failure on tile_get: %s",db_strerror(ret)); ret = MAPCACHE_FAILURE; } _bdb_release_conn(ctx,cache,tile,pc); return ret; } static void _mapcache_cache_bdb_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { DBT key,data; int ret; apr_time_t now; mapcache_cache_bdb *cache = (mapcache_cache_bdb*)pcache; char *skey = mapcache_util_get_tile_key(ctx,tile,cache->key_template,NULL,NULL); mapcache_pooled_connection *pc; struct bdb_env *benv; now = apr_time_now(); memset(&key, 0, sizeof(DBT)); memset(&data, 0, sizeof(DBT)); key.data = skey; key.size = strlen(skey)+1; if(!tile->raw_image) { tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data); GC_CHECK_ERROR(ctx); } if(tile->raw_image->h==256 && tile->raw_image->w==256 && mapcache_image_blank_color(tile->raw_image) != MAPCACHE_FALSE) { data.size = 5+sizeof(apr_time_t); data.data = apr_palloc(ctx->pool,data.size); (((char*)data.data)[0])='#'; memcpy(((char*)data.data)+1,tile->raw_image->data,4); memcpy(((char*)data.data)+5,&now,sizeof(apr_time_t)); } else { if(!tile->encoded_data) { tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format); GC_CHECK_ERROR(ctx); } mapcache_buffer_append(tile->encoded_data,sizeof(apr_time_t),&now); data.data = tile->encoded_data->buf; data.size = tile->encoded_data->size; tile->encoded_data->size -= sizeof(apr_time_t); } pc = _bdb_get_conn(ctx,cache,tile,0); GC_CHECK_ERROR(ctx); benv = pc->connection; ret = benv->db->put(benv->db,NULL,&key,&data,0); if(ret != 0) { ctx->set_error(ctx,500,"dbd backend failed on tile_set: %s", db_strerror(ret)); } else { ret = benv->db->sync(benv->db,0); if(ret) ctx->set_error(ctx,500,"bdb backend sync failure on tile_set: %s",db_strerror(ret)); } _bdb_release_conn(ctx,cache,tile,pc); } static void _mapcache_cache_bdb_multiset(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tiles, int ntiles) { DBT key,data; int ret,i; apr_time_t now; mapcache_cache_bdb *cache = (mapcache_cache_bdb*)pcache; mapcache_pooled_connection *pc; struct bdb_env *benv; now = apr_time_now(); memset(&key, 0, sizeof(DBT)); memset(&data, 0, sizeof(DBT)); pc = _bdb_get_conn(ctx,cache,&tiles[0],0); GC_CHECK_ERROR(ctx); benv = pc->connection; for(i=0; ikey_template,NULL,NULL); if(!tile->raw_image) { tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data); if(GC_HAS_ERROR(ctx)) { _bdb_release_conn(ctx,cache,&tiles[0],pc); return; } } if(tile->raw_image->h==256 && tile->raw_image->w==256 && mapcache_image_blank_color(tile->raw_image) != MAPCACHE_FALSE) { data.size = 5+sizeof(apr_time_t); data.data = apr_palloc(ctx->pool,data.size); (((char*)data.data)[0])='#'; memcpy(((char*)data.data)+1,tile->raw_image->data,4); memcpy(((char*)data.data)+5,&now,sizeof(apr_time_t)); } else { if(!tile->encoded_data) { tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format); if(GC_HAS_ERROR(ctx)) { _bdb_release_conn(ctx,cache,&tiles[0],pc); return; } } mapcache_buffer_append(tile->encoded_data,sizeof(apr_time_t),&now); data.data = tile->encoded_data->buf; data.size = tile->encoded_data->size; tile->encoded_data->size -= sizeof(apr_time_t); } key.data = skey; key.size = strlen(skey)+1; ret = benv->db->put(benv->db,NULL,&key,&data,0); if(ret != 0) { ctx->set_error(ctx,500,"dbd backend failed on tile_multiset: %s", db_strerror(ret)); break; } } if(ret == 0) { ret = benv->db->sync(benv->db,0); if(ret) ctx->set_error(ctx,500,"bdb backend sync failure on sync in tile_multiset: %s",db_strerror(ret)); } _bdb_release_conn(ctx,cache,&tiles[0],pc); } static void _mapcache_cache_bdb_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *cache, mapcache_cfg *config) { ezxml_t cur_node; mapcache_cache_bdb *dcache = (mapcache_cache_bdb*)cache; if ((cur_node = ezxml_child(node,"base")) != NULL) { dcache->basedir = apr_pstrdup(ctx->pool,cur_node->txt); } if ((cur_node = ezxml_child(node,"key_template")) != NULL) { dcache->key_template = apr_pstrdup(ctx->pool,cur_node->txt); } else { dcache->key_template = apr_pstrdup(ctx->pool,"{tileset}-{grid}-{dim}-{z}-{y}-{x}.{ext}"); } if(!dcache->basedir) { ctx->set_error(ctx,500,"dbd cache \"%s\" is missing entry",cache->name); return; } } /** * \private \memberof mapcache_cache_dbd */ static void _mapcache_cache_bdb_configuration_post_config(mapcache_context *ctx, mapcache_cache *cache, mapcache_cfg *cfg) { mapcache_cache_bdb *dcache = (mapcache_cache_bdb*)cache; apr_status_t rv; apr_dir_t *dir; rv = apr_dir_open(&dir, dcache->basedir, ctx->pool); if(rv != APR_SUCCESS) { char errmsg[120]; ctx->set_error(ctx,500,"bdb failed to open directory %s:%s",dcache->basedir,apr_strerror(rv,errmsg,120)); } } /** * \brief creates and initializes a mapcache_dbd_cache */ mapcache_cache* mapcache_cache_bdb_create(mapcache_context *ctx) { mapcache_cache_bdb *cache = apr_pcalloc(ctx->pool,sizeof(mapcache_cache_bdb)); if(!cache) { ctx->set_error(ctx, 500, "failed to allocate berkeley db cache"); return NULL; } 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.configuration_post_config = _mapcache_cache_bdb_configuration_post_config; cache->cache.configuration_parse_xml = _mapcache_cache_bdb_configuration_parse_xml; cache->basedir = NULL; cache->key_template = NULL; return (mapcache_cache*)cache; } #endif /* vim: ts=2 sts=2 et sw=2 */ mapcache-1.4.0/lib/cache_composite.c000066400000000000000000000177731255567662100173520ustar00rootroot00000000000000/****************************************************************************** * * Project: MapServer * Purpose: MapCache tile caching: composite cache backend. * 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 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" 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)); cl->cache=NULL; cl->dimensions=NULL; cl->grids=NULL; cl->maxzoom=-1; cl->minzoom=-1; return cl; } /** * returns the mapcache_cache to use for a given tile * @param ctx * @param tile * @return */ static mapcache_cache* _mapcache_composite_cache_get(mapcache_context *ctx, mapcache_cache_composite *cache, mapcache_tile *tile) { int i; for(i=0; icache_links->nelts; i++) { mapcache_cache_composite_cache_link *cache_link = APR_ARRAY_IDX(cache->cache_links,i,mapcache_cache_composite_cache_link*); if(cache_link->minzoom != -1 && tile->z < cache_link->minzoom) continue; if(cache_link->maxzoom != -1 && tile->z > cache_link->maxzoom) continue; if(cache_link->grids) { int j; for(j=0;jgrids->nelts;j++) { char *grid_name = APR_ARRAY_IDX(cache_link->grids,j,char*); if(!strcmp(tile->grid_link->grid->name,grid_name)) break; } /* not found */ if(j == cache_link->grids->nelts) continue; } 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; mapcache_cache *subcache; subcache = _mapcache_composite_cache_get(ctx, cache, tile); if(GC_HAS_ERROR(ctx) || !subcache) return MAPCACHE_FAILURE; return subcache->tile_exists(ctx, subcache, tile); } static void _mapcache_cache_composite_tile_delete(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { mapcache_cache_composite *cache = (mapcache_cache_composite*)pcache; mapcache_cache *subcache; subcache = _mapcache_composite_cache_get(ctx, cache, tile); GC_CHECK_ERROR(ctx); /*delete the tile itself*/ subcache->tile_delete(ctx,subcache,tile); } /** * \brief get content of given tile * * fills the mapcache_tile::data of the given tile with content stored on the composite server * \private \memberof mapcache_cache_composite * \sa mapcache_cache::tile_get() */ static int _mapcache_cache_composite_tile_get(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { mapcache_cache_composite *cache = (mapcache_cache_composite*)pcache; mapcache_cache *subcache; subcache = _mapcache_composite_cache_get(ctx, cache, tile); GC_CHECK_ERROR_RETURN(ctx); return subcache->tile_get(ctx,subcache,tile); } static void _mapcache_cache_composite_tile_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { mapcache_cache_composite *cache = (mapcache_cache_composite*)pcache; mapcache_cache *subcache; subcache = _mapcache_composite_cache_get(ctx, cache, tile); GC_CHECK_ERROR(ctx); return subcache->tile_set(ctx,subcache,tile); } static void _mapcache_cache_composite_tile_multi_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tiles, int ntiles) { mapcache_cache_composite *cache = (mapcache_cache_composite*)pcache; 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; itile_set(ctx, subcache, &tiles[i]); } } } /** * \private \memberof mapcache_cache_composite */ static void _mapcache_cache_composite_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *pcache, mapcache_cfg *config) { ezxml_t cur_node; mapcache_cache_composite *cache = (mapcache_cache_composite*)pcache; cache->cache_links = apr_array_make(ctx->pool,3,sizeof(mapcache_cache_composite_cache_link*)); for(cur_node = ezxml_child(node,"cache"); cur_node; cur_node = cur_node->next) { char *sZoom; int zoom; mapcache_cache *refcache = mapcache_configuration_get_cache(config, cur_node->txt); mapcache_cache_composite_cache_link *cachelink; if(!refcache) { ctx->set_error(ctx, 400, "composite cache \"%s\" references cache \"%s\"," " but it is not configured (hint:referenced caches must be declared before this composite cache in the xml file)", pcache->name, cur_node->txt); return; } cachelink = _mapcache_cache_link_create(ctx->pool); cachelink->cache = refcache; sZoom = (char*)ezxml_attr(cur_node,"max-zoom"); if(sZoom) { char *endptr; zoom = (int)strtol(sZoom,&endptr,10); if(*endptr != 0 || zoom < 0) { ctx->set_error(ctx, 400, "failed to parse cache max-zoom %s (expecting a positive integer)", sZoom); return; } cachelink->maxzoom = zoom; } sZoom = (char*)ezxml_attr(cur_node,"min-zoom"); if(sZoom) { char *endptr; zoom = (int)strtol(sZoom,&endptr,10); if(*endptr != 0 || zoom < 0) { ctx->set_error(ctx, 400, "failed to parse cache min-zoom %s (expecting a positive integer)", sZoom); return; } cachelink->minzoom = zoom; } APR_ARRAY_PUSH(cache->cache_links,mapcache_cache_composite_cache_link*) = cachelink; } } /** * \private \memberof mapcache_cache_composite */ static void _mapcache_cache_composite_configuration_post_config(mapcache_context *ctx, mapcache_cache *cache, mapcache_cfg *cfg) { } /** * \brief creates and initializes a mapcache_cache_composite */ mapcache_cache* mapcache_cache_composite_create(mapcache_context *ctx) { mapcache_cache_composite *cache = apr_pcalloc(ctx->pool,sizeof(mapcache_cache_composite)); if(!cache) { ctx->set_error(ctx, 500, "failed to allocate composite cache"); return NULL; } 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.configuration_post_config = _mapcache_cache_composite_configuration_post_config; cache->cache.configuration_parse_xml = _mapcache_cache_composite_configuration_parse_xml; return (mapcache_cache*)cache; } mapcache-1.4.0/lib/cache_couchbase.c000066400000000000000000000374451255567662100173020ustar00rootroot00000000000000/****************************************************************************** * $Id$ * * Project: MapServer * Purpose: MapCache tile caching support file: couchbase cache backend. * Author: Michael Downey 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-config.h" #ifdef USE_COUCHBASE #include "mapcache.h" #include #include #include typedef struct getStruct { mapcache_buffer *tileBuffer; libcouchbase_error_t error; } getStruct_t; /* Not sure if we need this. */ static void _couchbase_error_callback(libcouchbase_t instance, libcouchbase_error_t error, const char *errinfo) { /* Ignore timeouts... */ if (error != LIBCOUCHBASE_ETIMEDOUT) { fprintf(stderr, "\nFATAL ERROR: %s\n", libcouchbase_strerror(instance, error)); if (errinfo && strlen(errinfo) != 0) { fprintf(stderr, "\t\"%s\"\n", errinfo); } } } static void _couchbase_get_callback(libcouchbase_t instance, const void *cookie, libcouchbase_error_t error, const void *key, libcouchbase_size_t nkey, const void *bytes, libcouchbase_size_t nbytes, libcouchbase_uint32_t flags, libcouchbase_cas_t cas) { (void)instance; (void)key; (void)nkey; (void)flags; (void)cas; if (cookie) { getStruct_t *request = (getStruct_t*)cookie; request->error = error; if (error == LIBCOUCHBASE_SUCCESS && request->tileBuffer) { mapcache_buffer_append(request->tileBuffer, nbytes, (void*)bytes); } } } static void _couchbase_store_callback(libcouchbase_t instance, const void* cookie, libcouchbase_storage_t unknown, libcouchbase_error_t error, const void* unknown2, libcouchbase_size_t unknown3, libcouchbase_cas_t cas) { (void)instance; (void)unknown; (void)unknown2; (void)unknown3; (void)cas; libcouchbase_error_t* userError = (libcouchbase_error_t*)cookie; *userError = error; } static apr_status_t _couchbase_reslist_get_connection(void **conn_, void *params, apr_pool_t *pool) { mapcache_cache_couchbase *cache = (mapcache_cache_couchbase*)params; libcouchbase_t *instance = apr_pcalloc(pool,sizeof(libcouchbase_t)); const char *host = cache->host; const char *username = cache->username; const char *passwd = cache->password; const char *bucket = "default"; *instance = libcouchbase_create(host, username, passwd, bucket, NULL); if (*instance == NULL) { return APR_EGENERAL; } libcouchbase_set_error_callback(*instance, _couchbase_error_callback); libcouchbase_set_get_callback(*instance, _couchbase_get_callback); libcouchbase_set_storage_callback(*instance, _couchbase_store_callback); if (libcouchbase_connect(*instance) != LIBCOUCHBASE_SUCCESS) { return APR_EGENERAL; } /* Wait for the connect to compelete */ libcouchbase_wait(*instance); *conn_ = instance; return APR_SUCCESS; } static apr_status_t _couchbase_reslist_free_connection(void *conn_, void *params, apr_pool_t *pool) { libcouchbase_t *instance = (libcouchbase_t*)conn_; libcouchbase_destroy(*instance); return APR_SUCCESS; } static libcouchbase_t* _couchbase_get_connection(mapcache_context *ctx, mapcache_tile *tile) { apr_status_t rv; libcouchbase_t *instance; mapcache_cache_couchbase *cache = (mapcache_cache_couchbase*)tile->tileset->cache; rv = apr_reslist_acquire(cache->connection_pool, (void **)&instance); if(rv != APR_SUCCESS) { ctx->set_error(ctx, 500, "failed to aquire connection to couchbase backend: %s", ctx->get_error_message(ctx)); return NULL; } return instance; } static void _couchbase_release_connection(mapcache_tile *tile, libcouchbase_t* instance) { mapcache_cache_couchbase* cache = (mapcache_cache_couchbase*)tile->tileset->cache; apr_reslist_release(cache->connection_pool, (void*)instance); } static void _couchbase_invalidate_connection(mapcache_tile *tile, libcouchbase_t* instance) { mapcache_cache_couchbase* cache = (mapcache_cache_couchbase*)tile->tileset->cache; apr_reslist_invalidate(cache->connection_pool, (void*)instance); } static int _mapcache_cache_couchbase_has_tile(mapcache_context *ctx, mapcache_tile *tile) { char *key[1]; libcouchbase_t *instance; libcouchbase_error_t error; size_t keySize[1]; getStruct_t request; key[0] = mapcache_util_get_tile_key(ctx, tile, NULL, " \r\n\t\f\e\a\b", "#"); if(GC_HAS_ERROR(ctx)) { return MAPCACHE_FALSE; } keySize[0] = strlen(key[0]); instance = _couchbase_get_connection(ctx, tile); request.tileBuffer = 0; request.error = LIBCOUCHBASE_KEY_ENOENT; error = libcouchbase_mget(*instance, &request, 1, (const void * const*)key, keySize, 0); if (error != LIBCOUCHBASE_SUCCESS) { ctx->set_error(ctx, 500, "couchbase: failed to get key %s: %s", key, libcouchbase_strerror(*instance, error)); _couchbase_invalidate_connection(tile, instance); return MAPCACHE_FALSE; } libcouchbase_wait(*instance); error = request.error; if (error != LIBCOUCHBASE_SUCCESS) { ctx->set_error(ctx, 500, "couchbase: failed to get key %s: %s", key, libcouchbase_strerror(*instance, error)); _couchbase_invalidate_connection(tile, instance); return MAPCACHE_FALSE; } _couchbase_release_connection(tile, instance); return MAPCACHE_TRUE; } static void _mapcache_cache_couchbase_delete(mapcache_context *ctx, mapcache_tile *tile) { char *key; libcouchbase_t *instance; libcouchbase_error_t error; key = mapcache_util_get_tile_key(ctx, tile,NULL," \r\n\t\f\e\a\b","#"); GC_CHECK_ERROR(ctx); instance = _couchbase_get_connection(ctx, tile); error = libcouchbase_remove(*instance, 0, key, strlen(key), 0); if (error != LIBCOUCHBASE_SUCCESS) { ctx->set_error(ctx, 500, "couchbase: failed to delete key %s: %s", key, libcouchbase_strerror(*instance, error)); } libcouchbase_wait(*instance); error = libcouchbase_get_last_error(*instance); if (error != LIBCOUCHBASE_SUCCESS) { ctx->set_error(ctx, 500, "couchbase: failed to delete key %s: %s", key, libcouchbase_strerror(*instance, error)); } _couchbase_release_connection(tile, instance); } /** * \brief get content of given tile * * fills the mapcache_tile::data of the given tile with content stored on the couchbase server * \private \memberof mapcache_cache_couchbase * \sa mapcache_cache::tile_get() */ static int _mapcache_cache_couchbase_get(mapcache_context *ctx, mapcache_tile *tile) { char *key[1]; size_t keySize[1]; libcouchbase_t *instance; libcouchbase_error_t error; getStruct_t request; key[0] = mapcache_util_get_tile_key(ctx, tile, NULL, " \r\n\t\f\e\a\b", "#"); if(GC_HAS_ERROR(ctx)) { return MAPCACHE_FAILURE; } keySize[0] = strlen(key[0]); tile->encoded_data = mapcache_buffer_create(0, ctx->pool); libcouchbase_time_t expires = 86400; if(tile->tileset->auto_expire) expires = tile->tileset->auto_expire; instance = _couchbase_get_connection(ctx, tile); if (GC_HAS_ERROR(ctx)) { return MAPCACHE_FAILURE; } request.tileBuffer = tile->encoded_data; error = libcouchbase_mget(*instance, &request, 1, (const void * const*)key, keySize, &expires); if (error != LIBCOUCHBASE_SUCCESS) { ctx->set_error(ctx, 500, "couchbase cache returned error on mget %s", libcouchbase_strerror(*instance, error)); _couchbase_invalidate_connection(tile, instance); return MAPCACHE_FAILURE; } libcouchbase_wait(*instance); if(request.error != LIBCOUCHBASE_SUCCESS) { _couchbase_release_connection(tile, instance); return MAPCACHE_CACHE_MISS; } if (tile->encoded_data->size == 0) { _couchbase_release_connection(tile, instance); ctx->set_error(ctx, 500, "couchbase cache returned 0-length data for tile %d %d %d", tile->x, tile->y, tile->z); return MAPCACHE_FAILURE; } apr_time_t now = apr_time_now(); tile->mtime = now; _couchbase_release_connection(tile, instance); return MAPCACHE_SUCCESS; } /** * \brief push tile data to couchbase * * writes the content of mapcache_tile::data to the configured couchbased instance(s) * \private \memberof mapcache_cache_couchbase * \sa mapcache_cache::tile_set() */ static void _mapcache_cache_couchbase_set(mapcache_context *ctx, mapcache_tile *tile) { char *key; libcouchbase_t *instance; libcouchbase_error_t error; const int max_retries = 3; int retries = max_retries; apr_interval_time_t delay; /* set expiration to one day if not configured */ libcouchbase_time_t expires = 86400; if(tile->tileset->auto_expire) expires = tile->tileset->auto_expire; mapcache_cache_couchbase *cache = (mapcache_cache_couchbase*)tile->tileset->cache; key = mapcache_util_get_tile_key(ctx, tile, NULL, " \r\n\t\f\e\a\b", "#"); GC_CHECK_ERROR(ctx); if(!tile->encoded_data) { tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format); GC_CHECK_ERROR(ctx); } instance = _couchbase_get_connection(ctx, tile); GC_CHECK_ERROR(ctx); do { error = libcouchbase_store(*instance, &error, LIBCOUCHBASE_SET, key, strlen(key), tile->encoded_data->buf, tile->encoded_data->size, 0, expires, 0); if (error != LIBCOUCHBASE_SUCCESS) { _couchbase_release_connection(tile, instance); ctx->set_error(ctx, 500, "failed to store tile %d %d %d to couchbase cache %s due to eror %s.", tile->x, tile->y, tile->z, cache->cache.name, libcouchbase_strerror(*instance, error)); return; } libcouchbase_wait(*instance); if (error == LIBCOUCHBASE_ETMPFAIL) { if (retries > 0) { delay = 100000 * (1 << (max_retries - retries)); // Do an exponential back off of starting at 100 milliseconds apr_sleep(delay); } else { _couchbase_release_connection(tile, instance); ctx->set_error(ctx, 500, "failed to store tile %d %d %d to couchbase cache %s due to %s. Maximum number of retries used.", tile->x, tile->y, tile->z, cache->cache.name, libcouchbase_strerror(*instance, error)); return; } --retries; } else if (error != LIBCOUCHBASE_SUCCESS) { _couchbase_release_connection(tile, instance); ctx->set_error(ctx, 500, "failed to store tile %d %d %d to couchbase cache %s due to error %s.", tile->x, tile->y, tile->z, cache->cache.name, libcouchbase_strerror(*instance, error)); return; } } while (error == LIBCOUCHBASE_ETMPFAIL); _couchbase_release_connection(tile, instance); } /** * \private \memberof mapcache_cache_couchbase */ static void _mapcache_cache_couchbase_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *cache, mapcache_cfg *config) { ezxml_t cur_node; apr_status_t rv; mapcache_cache_couchbase *dcache = (mapcache_cache_couchbase*)cache; int servercount = 0; for(cur_node = ezxml_child(node,"server"); cur_node; cur_node = cur_node->next) { servercount++; } if(!servercount) { ctx->set_error(ctx, 400, "couchbase cache %s has no s configured", cache->name); return; } if(servercount > 1) { ctx->set_error(ctx, 400, "couchbase cache %s has more than 1 server configured", cache->name); return; } cur_node = ezxml_child(node, "server"); ezxml_t xhost = ezxml_child(cur_node, "host"); /* Host should contain server:port */ ezxml_t xusername = ezxml_child(cur_node, "username"); ezxml_t xpasswd = ezxml_child(cur_node, "password"); ezxml_t xbucket = ezxml_child(cur_node, "bucket"); if(!xhost || !xhost->txt || ! *xhost->txt) { ctx->set_error(ctx, 400, "cache %s: with no ", 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(xusername && xusername->txt && *xusername->txt) { dcache->username = apr_pstrdup(ctx->pool, xusername->txt); } if(xpasswd && xpasswd->txt && *xpasswd->txt) { dcache->password = apr_pstrdup(ctx->pool, xpasswd->txt); } if(xbucket && xbucket->txt && *xbucket->txt) { dcache->bucket = apr_pstrdup(ctx->pool, xbucket->txt); } dcache->ctx = ctx; rv = apr_reslist_create(&(dcache->connection_pool), 0 /* min */, 10 /* soft max */, 200 /* hard max */, 60*1000000 /*60 seconds, ttl*/, _couchbase_reslist_get_connection, /* resource constructor */ _couchbase_reslist_free_connection, /* resource destructor */ dcache, ctx->pool); if(rv != APR_SUCCESS) { ctx->set_error(ctx, 500, "failed to create couchbase connection pool"); return; } } /** * \private \memberof mapcache_cache_couchbase */ static void _mapcache_cache_couchbase_configuration_post_config(mapcache_context *ctx, mapcache_cache *cache, mapcache_cfg *cfg) { } /** * \brief creates and initializes a mapcache_couchbase_cache */ mapcache_cache* mapcache_cache_couchbase_create(mapcache_context *ctx) { mapcache_cache_couchbase *cache = apr_pcalloc(ctx->pool,sizeof(mapcache_cache_couchbase)); if(!cache) { ctx->set_error(ctx, 500, "failed to allocate couchbase cache"); return NULL; } cache->cache.metadata = apr_table_make(ctx->pool, 3); cache->cache.type = MAPCACHE_CACHE_COUCHBASE; cache->cache.tile_get = _mapcache_cache_couchbase_get; cache->cache.tile_exists = _mapcache_cache_couchbase_has_tile; cache->cache.tile_set = _mapcache_cache_couchbase_set; cache->cache.tile_delete = _mapcache_cache_couchbase_delete; cache->cache.configuration_parse_xml = _mapcache_cache_couchbase_configuration_parse_xml; cache->cache.configuration_post_config = _mapcache_cache_couchbase_configuration_post_config; cache->host = NULL; cache->username = NULL; cache->password = NULL; cache->bucket = NULL; return (mapcache_cache*)cache; } #endif /* vim: ai ts=3 sts=3 et sw=3 */ mapcache-1.4.0/lib/cache_disk.c000066400000000000000000000672531255567662100163000ustar00rootroot00000000000000/****************************************************************************** * $Id$ * * Project: MapServer * Purpose: MapCache tile caching: filesytem cache backend. * 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 #include #include #include #include #include #ifdef HAVE_SYMLINK #include #endif /** * \brief computes the relative path between two destinations * * \param tilename the absolute filename of the tile * \param blankname the absolute path of the blank tile image */ char* relative_path(mapcache_context *ctx, char* tilename, char* blankname) { int updir_cnt = 0; char *blank_rel = ""; /* work up the directory paths of the tile and blank filename to find the common root */ char *tile_it = tilename, *blank_it = blankname; if(*tile_it != *blank_it) { /* the two files have no common root. * This really shouldn't happen on a unix FS hierarchy, and symbolic linking * is enabled only on these platforms, so this case should in practice never * happen. * we return the absolute path, and should probably set a warning message */ return apr_pstrdup(ctx->pool, blankname); } while(*(tile_it+1) && *(blank_it+1) && *(tile_it+1) == *(blank_it+1)) { tile_it++; blank_it++; } /* tile_it and blank_it point on the last common character of the two filenames, which should be a '/'. If not, return the full blank name * (and set a warning message? )*/ if(*tile_it != *blank_it || *tile_it != '/') { return apr_pstrdup(ctx->pool, blankname); } blank_it++; while(*tile_it == '/') tile_it++; /*skip leading '/'s*/ /* blank_it now contains the path that must be appended after the relative part of the constructed path,e.g.: - tilename = "/basepath/tilesetname/gridname/03/000/05/08.png" - blankname = "/basepath/tilesetname/gridname/blanks/005599FF.png" then - tile_it is "03/000/05/08.png" - blank_it is "blanks/005599FF.png" */ /* we now count the number of '/' in the remaining tilename */ while(*tile_it) { if(*tile_it == '/') { updir_cnt++; /* also skip consecutive '/'s */ while(*(tile_it+1)=='/') tile_it++; } tile_it ++; } while(updir_cnt--) { blank_rel = apr_pstrcat(ctx->pool, blank_rel, "../", NULL); } blank_rel = apr_pstrcat(ctx->pool,blank_rel,blank_it,NULL); return blank_rel; } /** * \brief returns base path for given tile * * \param tile the tile to get base path from * \param path pointer to a char* that will contain the filename * \private \memberof mapcache_cache_disk */ static void _mapcache_cache_disk_base_tile_key(mapcache_context *ctx, mapcache_cache_disk *cache, mapcache_tile *tile, char **path) { *path = apr_pstrcat(ctx->pool, cache->base_directory,"/", tile->tileset->name,"/", tile->grid_link->grid->name, NULL); if(tile->dimensions) { 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)); const char *dimval = mapcache_util_str_sanitize(ctx->pool,entry->val,"/.",'#'); *path = apr_pstrcat(ctx->pool,*path,"/",dimval,NULL); } } } static void _mapcache_cache_disk_blank_tile_key(mapcache_context *ctx, mapcache_cache_disk *cache, mapcache_tile *tile, unsigned char *color, char **path) { /* not implemented for template caches, as symlink_blank will never be set */ *path = apr_psprintf(ctx->pool,"%s/%s/%s/blanks/%02X%02X%02X%02X.%s", cache->base_directory, tile->tileset->name, tile->grid_link->grid->name, color[0], color[1], color[2], color[3], tile->tileset->format?tile->tileset->format->extension:"png"); if(!*path) { ctx->set_error(ctx,500, "failed to allocate blank tile key"); } } /** * \brief return filename for given tile * * \param tile the tile to get the key from * \param path pointer to a char* that will contain the filename * \param r * \private \memberof mapcache_cache_disk */ static void _mapcache_cache_disk_tilecache_tile_key(mapcache_context *ctx, mapcache_cache_disk *cache, mapcache_tile *tile, char **path) { if(cache->base_directory) { char *start; _mapcache_cache_disk_base_tile_key(ctx, cache, tile, &start); *path = apr_psprintf(ctx->pool,"%s/%02d/%03d/%03d/%03d/%03d/%03d/%03d.%s", start, tile->z, tile->x / 1000000, (tile->x / 1000) % 1000, tile->x % 1000, tile->y / 1000000, (tile->y / 1000) % 1000, tile->y % 1000, tile->tileset->format?tile->tileset->format->extension:"png"); } else { *path = cache->filename_template; *path = mapcache_util_str_replace(ctx->pool,*path, "{tileset}", tile->tileset->name); *path = mapcache_util_str_replace(ctx->pool,*path, "{grid}", tile->grid_link->grid->name); *path = mapcache_util_str_replace(ctx->pool,*path, "{ext}", tile->tileset->format?tile->tileset->format->extension:"png"); if(strstr(*path,"{x}")) *path = mapcache_util_str_replace(ctx->pool,*path, "{x}", apr_psprintf(ctx->pool,"%d",tile->x)); else *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 *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 *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) { 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)); char *dimval = apr_pstrdup(ctx->pool,entry->val); char *iter = dimval; while(*iter) { /* replace dangerous characters by '#' */ if(*iter == '.' || *iter == '/') { *iter = '#'; } iter++; } dimstring = apr_pstrcat(ctx->pool,dimstring,"#",entry->key,"#",dimval,NULL); } *path = mapcache_util_str_replace(ctx->pool,*path, "{dim}", dimstring); } } if(!*path) { ctx->set_error(ctx,500, "failed to allocate tile key"); } } static void _mapcache_cache_disk_template_tile_key(mapcache_context *ctx, mapcache_cache_disk *cache, mapcache_tile *tile, char **path) { *path = cache->filename_template; *path = mapcache_util_str_replace(ctx->pool,*path, "{tileset}", tile->tileset->name); *path = mapcache_util_str_replace(ctx->pool,*path, "{grid}", tile->grid_link->grid->name); *path = mapcache_util_str_replace(ctx->pool,*path, "{ext}", tile->tileset->format?tile->tileset->format->extension:"png"); if(strstr(*path,"{x}")) *path = mapcache_util_str_replace(ctx->pool,*path, "{x}", apr_psprintf(ctx->pool,"%d",tile->x)); else *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 *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 *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) { 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)); char *dimval = apr_pstrdup(ctx->pool,entry->val); char *iter = dimval; while(*iter) { /* replace dangerous characters by '#' */ if(*iter == '.' || *iter == '/') { *iter = '#'; } iter++; } dimstring = apr_pstrcat(ctx->pool,dimstring,"#",entry->key,"#",dimval,NULL); } *path = mapcache_util_str_replace(ctx->pool,*path, "{dim}", dimstring); } if(!*path) { ctx->set_error(ctx,500, "failed to allocate tile key"); } } static void _mapcache_cache_disk_arcgis_tile_key(mapcache_context *ctx, mapcache_cache_disk *cache, mapcache_tile *tile, char **path) { if(cache->base_directory) { char *start; _mapcache_cache_disk_base_tile_key(ctx, cache, tile, &start); *path = apr_psprintf(ctx->pool,"%s/L%02d/R%08x/C%08x.%s" , start, tile->z, tile->y, tile->x, tile->tileset->format?tile->tileset->format->extension:"png"); } if(!*path) { ctx->set_error(ctx,500, "failed to allocate tile key"); } } static void _mapcache_cache_disk_worldwind_tile_key(mapcache_context *ctx, mapcache_cache_disk *cache, mapcache_tile *tile, char **path) { if(cache->base_directory) { *path = apr_psprintf(ctx->pool,"%s/%d/%04d/%04d_%04d.%s" , cache->base_directory, tile->z, tile->y, tile->y, tile->x, tile->tileset->format?tile->tileset->format->extension:"png"); } if(!*path) { ctx->set_error(ctx,500, "failed to allocate tile key"); } } static int _mapcache_cache_disk_has_tile(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { char *filename; apr_finfo_t finfo; int rv; mapcache_cache_disk *cache = (mapcache_cache_disk*)pcache; cache->tile_key(ctx, cache, tile, &filename); if(GC_HAS_ERROR(ctx)) { return MAPCACHE_FALSE; } rv = apr_stat(&finfo,filename,0,ctx->pool); if(rv != APR_SUCCESS) { return MAPCACHE_FALSE; } else { return MAPCACHE_TRUE; } } static void _mapcache_cache_disk_delete(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { apr_status_t ret; char errmsg[120]; char *filename; mapcache_cache_disk *cache = (mapcache_cache_disk*)pcache; cache->tile_key(ctx, cache, tile, &filename); GC_CHECK_ERROR(ctx); ret = apr_file_remove(filename,ctx->pool); if(ret != APR_SUCCESS && !APR_STATUS_IS_ENOENT(ret)) { ctx->set_error(ctx, 500, "failed to remove file %s: %s",filename, apr_strerror(ret,errmsg,120)); } } /** * \brief get file content of given tile * * fills the mapcache_tile::data of the given tile with content stored in the file * \private \memberof mapcache_cache_disk * \sa mapcache_cache::tile_get() */ static int _mapcache_cache_disk_get(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { char *filename; apr_file_t *f; apr_finfo_t finfo; apr_status_t rv; apr_size_t size; apr_mmap_t *tilemmap; mapcache_cache_disk *cache = (mapcache_cache_disk*)pcache; cache->tile_key(ctx, cache, tile, &filename); if(GC_HAS_ERROR(ctx)) { return MAPCACHE_FAILURE; } ctx->log(ctx,MAPCACHE_DEBUG,"checking for tile %s",filename); if((rv=apr_file_open(&f, filename, #ifndef NOMMAP APR_FOPEN_READ, APR_UREAD | APR_GREAD, #else APR_FOPEN_READ|APR_FOPEN_BUFFERED|APR_FOPEN_BINARY,APR_OS_DEFAULT, #endif 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; } size = finfo.size; /* * at this stage, we have a handle to an open file that contains data. * idealy, we should aquire a read lock, in case the data contained inside the file * is incomplete (i.e. if another process is currently writing to the tile). * currently such a lock is not set, as we don't want to loose performance on tile accesses. * any error that might happen at this stage should only occur if the tile isn't already cached, * i.e. normally only once. */ tile->mtime = finfo.mtime; tile->encoded_data = mapcache_buffer_create(size,ctx->pool); #ifndef NOMMAP rv = apr_mmap_create(&tilemmap,f,0,finfo.size,APR_MMAP_READ,ctx->pool); if(rv != APR_SUCCESS) { char errmsg[120]; ctx->set_error(ctx, 500, "mmap error: %s",apr_strerror(rv,errmsg,120)); return MAPCACHE_FAILURE; } tile->encoded_data->buf = tilemmap->mm; tile->encoded_data->size = tile->encoded_data->avail = finfo.size; #else //manually add the data to our buffer apr_file_read(f,(void*)tile->encoded_data->buf,&size); tile->encoded_data->size = size; tile->encoded_data->avail = size; #endif apr_file_close(f); if(tile->encoded_data->size != finfo.size) { ctx->set_error(ctx, 500, "failed to copy image data, got %d of %d bytes",(int)size, (int)finfo.size); return MAPCACHE_FAILURE; } return MAPCACHE_SUCCESS; } else { if(APR_STATUS_IS_ENOENT(rv)) { /* the file doesn't exist on the disk */ return MAPCACHE_CACHE_MISS; } else { char *error = strerror(rv); ctx->set_error(ctx, 500, "failed to open file %s: %s",filename, error); return MAPCACHE_FAILURE; } } } /** * \brief write tile data to disk * * writes the content of mapcache_tile::data to disk. * \returns MAPCACHE_FAILURE if there is no data to write, or if the tile isn't locked * \returns MAPCACHE_SUCCESS if the tile has been successfully written to disk * \private \memberof mapcache_cache_disk * \sa mapcache_cache::tile_set() */ static void _mapcache_cache_disk_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { apr_size_t bytes; apr_file_t *f; apr_status_t ret; char errmsg[120]; char *filename, *hackptr1, *hackptr2=NULL; mapcache_cache_disk *cache = (mapcache_cache_disk*)pcache; const int creation_retry = cache->creation_retry; int retry_count_create_file = 0; #ifdef DEBUG /* all this should be checked at a higher level */ if(!tile->encoded_data && !tile->raw_image) { ctx->set_error(ctx,500,"attempting to write empty tile to disk"); return; } if(!tile->encoded_data && !tile->tileset->format) { ctx->set_error(ctx,500,"received a raw tile image for a tileset with no format"); return; } #endif cache->tile_key(ctx, cache, tile, &filename); GC_CHECK_ERROR(ctx); /* find the location of the last '/' in the string */ hackptr1 = filename; while(*hackptr1) { if(*hackptr1 == '/') hackptr2 = hackptr1; hackptr1++; } *hackptr2 = '\0'; if(APR_SUCCESS != (ret = apr_dir_make_recursive(filename,APR_OS_DEFAULT,ctx->pool))) { /* * apr_dir_make_recursive sometimes sends back this error, although it should not. * ignore this one */ if(!APR_STATUS_IS_EEXIST(ret)) { ctx->set_error(ctx, 500, "failed to create directory %s: %s",filename, apr_strerror(ret,errmsg,120)); return; } } *hackptr2 = '/'; ret = apr_file_remove(filename,ctx->pool); if(ret != APR_SUCCESS && !APR_STATUS_IS_ENOENT(ret)) { ctx->set_error(ctx, 500, "failed to remove file %s: %s",filename, apr_strerror(ret,errmsg,120)); } #ifdef HAVE_SYMLINK if(cache->symlink_blank) { if(!tile->raw_image) { tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data); GC_CHECK_ERROR(ctx); } if(mapcache_image_blank_color(tile->raw_image) != MAPCACHE_FALSE) { char *blankname; _mapcache_cache_disk_blank_tile_key(ctx,cache,tile,tile->raw_image->data,&blankname); if(apr_file_open(&f, blankname, APR_FOPEN_READ, APR_OS_DEFAULT, ctx->pool) != APR_SUCCESS) { int isLocked; void *lock; if(!tile->encoded_data) { tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format); GC_CHECK_ERROR(ctx); } /* create the blank file */ char *blankdirname = apr_psprintf(ctx->pool, "%s/%s/%s/blanks", cache->base_directory, tile->tileset->name, tile->grid_link->grid->name); if(APR_SUCCESS != (ret = apr_dir_make_recursive( blankdirname, APR_OS_DEFAULT,ctx->pool))) { if(!APR_STATUS_IS_EEXIST(ret)) { ctx->set_error(ctx, 500, "failed to create directory %s for blank tiles",blankdirname, apr_strerror(ret,errmsg,120)); return; } } /* aquire a lock on the blank file */ isLocked = mapcache_lock_or_wait_for_resource(ctx,ctx->config->locker,blankname, &lock); if(isLocked == MAPCACHE_TRUE) { if((ret = apr_file_open(&f, blankname, APR_FOPEN_CREATE|APR_FOPEN_WRITE|APR_FOPEN_BUFFERED|APR_FOPEN_BINARY, APR_OS_DEFAULT, ctx->pool)) != APR_SUCCESS) { ctx->set_error(ctx, 500, "failed to create file %s: %s",blankname, apr_strerror(ret,errmsg,120)); mapcache_unlock_resource(ctx,ctx->config->locker,blankname, lock); return; /* we could not create the file */ } bytes = (apr_size_t)tile->encoded_data->size; ret = apr_file_write(f,(void*)tile->encoded_data->buf,&bytes); if(ret != APR_SUCCESS) { ctx->set_error(ctx, 500, "failed to write data to file %s (wrote %d of %d bytes): %s",blankname, (int)bytes, (int)tile->encoded_data->size, apr_strerror(ret,errmsg,120)); mapcache_unlock_resource(ctx,ctx->config->locker,blankname, lock); return; /* we could not create the file */ } if(bytes != tile->encoded_data->size) { ctx->set_error(ctx, 500, "failed to write image data to %s, wrote %d of %d bytes", blankname, (int)bytes, (int)tile->encoded_data->size); mapcache_unlock_resource(ctx,ctx->config->locker,blankname, lock); return; } apr_file_close(f); mapcache_unlock_resource(ctx,ctx->config->locker,blankname, lock); #ifdef DEBUG ctx->log(ctx,MAPCACHE_DEBUG,"created blank tile %s",blankname); #endif } } else { apr_file_close(f); } int retry_count_create_symlink = 0; /* * compute the relative path between tile and blank tile */ char *blankname_rel = NULL; blankname_rel = relative_path(ctx,filename, blankname); GC_CHECK_ERROR(ctx); /* * depending on configuration symlink creation will retry if it fails. * this can happen on nfs mounted network storage. * the solution is to create the containing directory again and retry the symlink creation. */ while(symlink(blankname_rel, filename) != 0) { retry_count_create_symlink++; if(retry_count_create_symlink > creation_retry) { char *error = strerror(errno); ctx->set_error(ctx, 500, "failed to link tile %s to %s: %s",filename, blankname_rel, error); return; /* we could not create the file */ } *hackptr2 = '\0'; if(APR_SUCCESS != (ret = apr_dir_make_recursive(filename,APR_OS_DEFAULT,ctx->pool))) { if(!APR_STATUS_IS_EEXIST(ret)) { ctx->set_error(ctx, 500, "failed to create symlink, can not create directory %s: %s",filename, apr_strerror(ret,errmsg,120)); return; /* we could not create the file */ } } *hackptr2 = '/'; } #ifdef DEBUG ctx->log(ctx, MAPCACHE_DEBUG, "linked blank tile %s to %s",filename,blankname); #endif return; } } #endif /*HAVE_SYMLINK*/ /* go the normal way: either we haven't configured blank tile detection, or the tile was not blank */ if(!tile->encoded_data) { tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format); GC_CHECK_ERROR(ctx); } /* * depending on configuration file creation will retry if it fails. * this can happen on nfs mounted network storage. * the solution is to create the containing directory again and retry the file creation. */ while((ret = apr_file_open(&f, filename, APR_FOPEN_CREATE|APR_FOPEN_WRITE|APR_FOPEN_BUFFERED|APR_FOPEN_BINARY, APR_OS_DEFAULT, ctx->pool)) != APR_SUCCESS) { retry_count_create_file++; if(retry_count_create_file > creation_retry) { ctx->set_error(ctx, 500, "failed to create file %s: %s",filename, apr_strerror(ret,errmsg,120)); return; /* we could not create the file */ } *hackptr2 = '\0'; if(APR_SUCCESS != (ret = apr_dir_make_recursive(filename,APR_OS_DEFAULT,ctx->pool))) { if(!APR_STATUS_IS_EEXIST(ret)) { ctx->set_error(ctx, 500, "failed to create file, can not create directory %s: %s",filename, apr_strerror(ret,errmsg,120)); return; /* we could not create the file */ } } *hackptr2 = '/'; } bytes = (apr_size_t)tile->encoded_data->size; ret = apr_file_write(f,(void*)tile->encoded_data->buf,&bytes); if(ret != APR_SUCCESS) { ctx->set_error(ctx, 500, "failed to write data to file %s (wrote %d of %d bytes): %s",filename, (int)bytes, (int)tile->encoded_data->size, apr_strerror(ret,errmsg,120)); return; /* we could not create the file */ } if(bytes != tile->encoded_data->size) { ctx->set_error(ctx, 500, "failed to write image data to %s, wrote %d of %d bytes", filename, (int)bytes, (int)tile->encoded_data->size); } ret = apr_file_close(f); if(ret != APR_SUCCESS) { ctx->set_error(ctx, 500, "failed to close file %s:%s",filename, apr_strerror(ret,errmsg,120)); return; /* we could not create the file */ } } /** * \private \memberof mapcache_cache_disk */ static void _mapcache_cache_disk_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *cache, mapcache_cfg *config) { ezxml_t cur_node; mapcache_cache_disk *dcache = (mapcache_cache_disk*)cache; char *layout = NULL; int template_layout = MAPCACHE_FALSE; layout = (char*)ezxml_attr(node,"layout"); if (!layout || !strlen(layout) || !strcmp(layout,"tilecache")) { dcache->tile_key = _mapcache_cache_disk_tilecache_tile_key; } else if(!strcmp(layout,"arcgis")) { dcache->tile_key = _mapcache_cache_disk_arcgis_tile_key; } else if(!strcmp(layout,"worldwind")) { dcache->tile_key = _mapcache_cache_disk_worldwind_tile_key; } else if (!strcmp(layout,"template")) { dcache->tile_key = _mapcache_cache_disk_template_tile_key; template_layout = MAPCACHE_TRUE; if ((cur_node = ezxml_child(node,"template")) != NULL) { dcache->filename_template = apr_pstrdup(ctx->pool,cur_node->txt); } else { ctx->set_error(ctx, 400, "no template specified for cache \"%s\"", cache->name); return; } } else { ctx->set_error(ctx, 400, "unknown layout type %s for cache \"%s\"", layout, cache->name); return; } if (!template_layout && (cur_node = ezxml_child(node,"base")) != NULL) { dcache->base_directory = apr_pstrdup(ctx->pool,cur_node->txt); } if (!template_layout && (cur_node = ezxml_child(node,"symlink_blank")) != NULL) { if(strcasecmp(cur_node->txt,"false")) { #ifdef HAVE_SYMLINK dcache->symlink_blank=1; #else ctx->set_error(ctx,400,"cache %s: host system does not support file symbolic linking",cache->name); return; #endif } } if ((cur_node = ezxml_child(node,"creation_retry")) != NULL) { dcache->creation_retry = atoi(cur_node->txt); } } /** * \private \memberof mapcache_cache_disk */ static void _mapcache_cache_disk_configuration_post_config(mapcache_context *ctx, mapcache_cache *cache, mapcache_cfg *cfg) { mapcache_cache_disk *dcache = (mapcache_cache_disk*)cache; /* check all required parameters are configured */ if((!dcache->base_directory || !strlen(dcache->base_directory)) && (!dcache->filename_template || !strlen(dcache->filename_template))) { ctx->set_error(ctx, 400, "disk cache %s has no base directory or template",dcache->cache.name); return; } } /** * \brief creates and initializes a mapcache_disk_cache */ mapcache_cache* mapcache_cache_disk_create(mapcache_context *ctx) { mapcache_cache_disk *cache = apr_pcalloc(ctx->pool,sizeof(mapcache_cache_disk)); if(!cache) { ctx->set_error(ctx, 500, "failed to allocate disk cache"); return NULL; } cache->symlink_blank = 0; 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.configuration_post_config = _mapcache_cache_disk_configuration_post_config; cache->cache.configuration_parse_xml = _mapcache_cache_disk_configuration_parse_xml; return (mapcache_cache*)cache; } /* vim: ts=2 sts=2 et sw=2 */ mapcache-1.4.0/lib/cache_fallback.c000066400000000000000000000203751255567662100170770ustar00rootroot00000000000000/****************************************************************************** * * Project: MapServer * Purpose: MapCache tile caching: fallback cache backend. * 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 fallbackriction, 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" 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); } static void _mapcache_cache_fallback_tile_delete(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { mapcache_cache_fallback *cache = (mapcache_cache_fallback*)pcache; int i; for(i=0; icaches->nelts; i++) { mapcache_cache *subcache = APR_ARRAY_IDX(cache->caches,i,mapcache_cache*); subcache->tile_delete(ctx, subcache, tile); ctx->clear_errors(ctx); /* ignore errors */ } } /** * \brief get content of given tile * * fills the mapcache_tile::data of the given tile with content stored on the fallback server * \private \memberof mapcache_cache_fallback * \sa mapcache_cache::tile_get() */ static int _mapcache_cache_fallback_tile_get(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { mapcache_cache_fallback *cache = (mapcache_cache_fallback*)pcache; mapcache_cache *subcache; int i,ret; subcache = APR_ARRAY_IDX(cache->caches,0,mapcache_cache*); ret = subcache->tile_get(ctx, subcache, tile); if(ret == MAPCACHE_FAILURE) { int first_error = ctx->get_error(ctx); char *first_error_message = ctx->get_error_message(ctx); ctx->log(ctx,MAPCACHE_WARN,"failed \"GET\" on primary cache \"%s\" for tile (z=%d,x=%d,y=%d) of tileset \"%s\". Falling back on secondary caches", APR_ARRAY_IDX(cache->caches,0,mapcache_cache*)->name,tile->z,tile->x,tile->y,tile->tileset->name); ctx->clear_errors(ctx); for(i=1; icaches->nelts; i++) { subcache = APR_ARRAY_IDX(cache->caches,i,mapcache_cache*); if((ret = subcache->tile_get(ctx, subcache, tile)) == MAPCACHE_FAILURE) { ctx->log(ctx,MAPCACHE_WARN,"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); continue; } else { return ret; } } /* all backends failed, return primary error message */ ctx->set_error(ctx,first_error,first_error_message); return MAPCACHE_FAILURE; } else { /* success or notfound */ return ret; } } 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; char *first_error_message; for(i=0; icaches->nelts; i++) { mapcache_cache *subcache = APR_ARRAY_IDX(cache->caches,i,mapcache_cache*); subcache->tile_set(ctx, subcache, tile); if(GC_HAS_ERROR(ctx)) { if(!first_error) { first_error = ctx->get_error(ctx); first_error_message = ctx->get_error_message(ctx); } ctx->log(ctx,MAPCACHE_WARN,"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) { ctx->set_error(ctx,first_error,first_error_message); } } 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; char *first_error_message; for(i=0; icaches->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;jtile_set(ctx,subcache,&tiles[j]); if(GC_HAS_ERROR(ctx)) break; } } if(GC_HAS_ERROR(ctx)) { if(!first_error) { first_error = ctx->get_error(ctx); first_error_message = ctx->get_error_message(ctx); } ctx->log(ctx,MAPCACHE_WARN,"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) { ctx->set_error(ctx,first_error,first_error_message); } } /** * \private \memberof mapcache_cache_fallback */ static void _mapcache_cache_fallback_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *pcache, mapcache_cfg *config) { ezxml_t cur_node; mapcache_cache_fallback *cache = (mapcache_cache_fallback*)pcache; cache->caches = apr_array_make(ctx->pool,3,sizeof(mapcache_cache*)); for(cur_node = ezxml_child(node,"cache"); cur_node; cur_node = cur_node->next) { mapcache_cache *refcache = mapcache_configuration_get_cache(config, cur_node->txt); if(!refcache) { ctx->set_error(ctx, 400, "fallback cache \"%s\" references cache \"%s\"," " but it is not configured (hint:referenced caches must be declared before this fallback cache in the xml file)", pcache->name, cur_node->txt); return; } APR_ARRAY_PUSH(cache->caches,mapcache_cache*) = refcache; } if(cache->caches->nelts == 0) { ctx->set_error(ctx,400,"fallback cache \"%s\" does not reference any child caches", pcache->name); } } /** * \private \memberof mapcache_cache_fallback */ static void _mapcache_cache_fallback_configuration_post_config(mapcache_context *ctx, mapcache_cache *cache, mapcache_cfg *cfg) { } /** * \brief creates and initializes a mapcache_cache_fallback */ mapcache_cache* mapcache_cache_fallback_create(mapcache_context *ctx) { mapcache_cache_fallback *cache = apr_pcalloc(ctx->pool,sizeof(mapcache_cache_fallback)); if(!cache) { ctx->set_error(ctx, 500, "failed to allocate fallback cache"); return NULL; } 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.configuration_post_config = _mapcache_cache_fallback_configuration_post_config; cache->cache.configuration_parse_xml = _mapcache_cache_fallback_configuration_parse_xml; return (mapcache_cache*)cache; } mapcache-1.4.0/lib/cache_memcache.c000066400000000000000000000313521255567662100170770ustar00rootroot00000000000000/****************************************************************************** * $Id$ * * Project: MapServer * Purpose: MapCache tile caching support file: memcache cache backend. * 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-config.h" #ifdef USE_MEMCACHE #include "mapcache.h" struct mapcache_memcache_conn_param { mapcache_cache_memcache *cache; }; struct mapcache_memcache_pooled_connection { apr_memcache_t *memcache; apr_pool_t *pool; }; void mapcache_memcache_connection_constructor(mapcache_context *ctx, void **conn_, void *params, apr_pool_t *process_pool) { struct mapcache_memcache_conn_param *param = params; mapcache_cache_memcache *cache = param->cache; struct mapcache_memcache_pooled_connection *pc; int i; pc = calloc(1,sizeof(struct mapcache_memcache_pooled_connection)); apr_pool_create(&pc->pool,process_pool); if(APR_SUCCESS != apr_memcache_create(pc->pool, cache->nservers, 0, &(pc->memcache))) { ctx->set_error(ctx,500,"cache %s: failed to create memcache backend", cache->cache.name); return; } for(i=0; icache->nservers; i++) { apr_memcache_server_t *server; if(APR_SUCCESS != apr_memcache_server_create(pc->pool,cache->servers[i].host,cache->servers[i].port,4,5,50,10000,&server)) { ctx->set_error(ctx,500,"cache %s: failed to create server %s:%d",cache->cache.name,cache->servers[i].host,cache->servers[i].port); return; } if(APR_SUCCESS != apr_memcache_add_server(pc->memcache,server)) { ctx->set_error(ctx,500,"cache %s: failed to add server %s:%d",cache->cache.name,cache->servers[i].host,cache->servers[i].port); return; } } *conn_ = pc; } void mapcache_memcache_connection_destructor(void *conn_, apr_pool_t *process_pool) { struct mapcache_memcache_pooled_connection *pc = conn_; apr_pool_destroy(pc->pool); free(pc); } static mapcache_pooled_connection* _mapcache_memcache_get_conn(mapcache_context *ctx, mapcache_cache_memcache *cache, mapcache_tile *tile) { mapcache_pooled_connection *pc; struct mapcache_memcache_conn_param param; param.cache = cache; pc = mapcache_connection_pool_get_connection(ctx,cache->cache.name, mapcache_memcache_connection_constructor, mapcache_memcache_connection_destructor, ¶m); return pc; } static void _mapcache_memcache_release_conn(mapcache_context *ctx, mapcache_pooled_connection *con) { mapcache_connection_pool_release_connection(ctx, con); } static int _mapcache_cache_memcache_has_tile(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { char *key; char *tmpdata; int rv; size_t tmpdatasize; mapcache_cache_memcache *cache = (mapcache_cache_memcache*)pcache; mapcache_pooled_connection *pc; struct mapcache_memcache_pooled_connection *mpc; pc = _mapcache_memcache_get_conn(ctx,cache,tile); if(GC_HAS_ERROR(ctx)) return MAPCACHE_FALSE; mpc = pc->connection; key = mapcache_util_get_tile_key(ctx, tile, NULL, " \r\n\t\f\e\a\b","#"); if(GC_HAS_ERROR(ctx)) { rv = MAPCACHE_FALSE; goto cleanup; } rv = apr_memcache_getp(mpc->memcache,ctx->pool,key,&tmpdata,&tmpdatasize,NULL); if(rv != APR_SUCCESS) { rv = MAPCACHE_FALSE; goto cleanup; } if(tmpdatasize == 0) { rv = MAPCACHE_FALSE; goto cleanup; } rv = MAPCACHE_TRUE; cleanup: _mapcache_memcache_release_conn(ctx,pc); return rv; } static void _mapcache_cache_memcache_delete(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { char *key; int rv; char errmsg[120]; mapcache_cache_memcache *cache = (mapcache_cache_memcache*)pcache; mapcache_pooled_connection *pc; struct mapcache_memcache_pooled_connection *mpc; pc = _mapcache_memcache_get_conn(ctx,cache,tile); GC_CHECK_ERROR(ctx); mpc = pc->connection; key = mapcache_util_get_tile_key(ctx, tile,NULL," \r\n\t\f\e\a\b","#"); if(GC_HAS_ERROR(ctx)) goto cleanup; rv = apr_memcache_delete(mpc->memcache,key,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)); goto cleanup; } cleanup: _mapcache_memcache_release_conn(ctx,pc); } /** * \brief get content of given tile * * fills the mapcache_tile::data of the given tile with content stored on the memcache server * \private \memberof mapcache_cache_memcache * \sa mapcache_cache::tile_get() */ static int _mapcache_cache_memcache_get(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { char *key; int rv; mapcache_cache_memcache *cache = (mapcache_cache_memcache*)pcache; mapcache_pooled_connection *pc; mapcache_buffer *encoded_data; struct mapcache_memcache_pooled_connection *mpc; pc = _mapcache_memcache_get_conn(ctx,cache,tile); if(GC_HAS_ERROR(ctx)) { return MAPCACHE_FAILURE; } mpc = pc->connection; key = mapcache_util_get_tile_key(ctx, tile,NULL," \r\n\t\f\e\a\b","#"); if(GC_HAS_ERROR(ctx)) { rv = MAPCACHE_FAILURE; goto cleanup; } encoded_data = mapcache_buffer_create(0,ctx->pool); rv = apr_memcache_getp(mpc->memcache,ctx->pool,key,(char**)&encoded_data->buf,&encoded_data->size,NULL); if(rv != APR_SUCCESS) { rv = MAPCACHE_CACHE_MISS; goto cleanup; } if(encoded_data->size == 0) { ctx->set_error(ctx,500,"memcache cache returned 0-length data for tile %d %d %d\n",tile->x,tile->y,tile->z); rv = MAPCACHE_FAILURE; goto cleanup; } /* extract the tile modification time from the end of the data returned */ memcpy( &tile->mtime, &(((char*)encoded_data->buf)[encoded_data->size-sizeof(apr_time_t)]), sizeof(apr_time_t)); ((char*)encoded_data->buf)[encoded_data->size+sizeof(apr_time_t)]='\0'; encoded_data->avail = encoded_data->size; encoded_data->size -= sizeof(apr_time_t); if(((char*)encoded_data->buf)[0] == '#' && encoded_data->size > 1) { tile->encoded_data = mapcache_empty_png_decode(ctx,tile->grid_link->grid->tile_sx, tile->grid_link->grid->tile_sy ,encoded_data->buf,&tile->nodata); } else { tile->encoded_data = encoded_data; } rv = MAPCACHE_SUCCESS; cleanup: _mapcache_memcache_release_conn(ctx,pc); return rv; } /** * \brief push tile data to memcached * * writes the content of mapcache_tile::data to the configured memcached instance(s) * \returns MAPCACHE_FAILURE if there is no data to write, or if the tile isn't locked * \returns MAPCACHE_SUCCESS if the tile has been successfully written * \private \memberof mapcache_cache_memcache * \sa mapcache_cache::tile_set() */ static void _mapcache_cache_memcache_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { char *key; int rv; /* set no expiration if not configured */ int expires =0; mapcache_buffer *encoded_data = NULL; mapcache_cache_memcache *cache = (mapcache_cache_memcache*)pcache; mapcache_pooled_connection *pc; struct mapcache_memcache_pooled_connection *mpc; pc = _mapcache_memcache_get_conn(ctx,cache,tile); GC_CHECK_ERROR(ctx); mpc = pc->connection; key = mapcache_util_get_tile_key(ctx, tile,NULL," \r\n\t\f\e\a\b","#"); if(GC_HAS_ERROR(ctx)) goto cleanup; if(tile->tileset->auto_expire) expires = tile->tileset->auto_expire; if(cache->detect_blank) { if(!tile->raw_image) { tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data); GC_CHECK_ERROR(ctx); } if(mapcache_image_blank_color(tile->raw_image) != MAPCACHE_FALSE) { encoded_data = mapcache_buffer_create(5,ctx->pool); ((char*)encoded_data->buf)[0] = '#'; memcpy(((char*)encoded_data->buf)+1,tile->raw_image->data,4); encoded_data->size = 5; } } if(!encoded_data) { if(!tile->encoded_data) { tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format); if(GC_HAS_ERROR(ctx)) goto cleanup; } encoded_data = tile->encoded_data; } /* concatenate the current time to the end of the memcache data so we can extract it out * when we re-get the tile */ char *data = calloc(1,encoded_data->size+sizeof(apr_time_t)); apr_time_t now = apr_time_now(); apr_pool_cleanup_register(ctx->pool, data, (void*)free, apr_pool_cleanup_null); memcpy(data,encoded_data->buf,encoded_data->size); memcpy(&(data[encoded_data->size]),&now,sizeof(apr_time_t)); rv = apr_memcache_set(mpc->memcache,key,data,encoded_data->size+sizeof(apr_time_t),expires,0); if(rv != APR_SUCCESS) { ctx->set_error(ctx,500,"failed to store tile %d %d %d to memcache cache %s", tile->x,tile->y,tile->z,cache->cache.name); goto cleanup; } cleanup: _mapcache_memcache_release_conn(ctx,pc); } /** * \private \memberof mapcache_cache_memcache */ static void _mapcache_cache_memcache_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *cache, mapcache_cfg *config) { ezxml_t cur_node; int i = 0; mapcache_cache_memcache *dcache = (mapcache_cache_memcache*)cache; for(cur_node = ezxml_child(node,"server"); cur_node; cur_node = cur_node->next) { dcache->nservers++; } if(!dcache->nservers) { ctx->set_error(ctx,400,"memcache cache %s has no s configured",cache->name); return; } dcache->servers = apr_pcalloc(ctx->pool, dcache->nservers * sizeof(struct mapcache_cache_memcache_server)); for(cur_node = ezxml_child(node,"server"); cur_node; cur_node = cur_node->next) { ezxml_t xhost = ezxml_child(cur_node,"host"); ezxml_t xport = ezxml_child(cur_node,"port"); if(!xhost || !xhost->txt || ! *xhost->txt) { ctx->set_error(ctx,400,"cache %s: with no ",cache->name); return; } else { dcache->servers[i].host = apr_pstrdup(ctx->pool,xhost->txt); } if(!xport || !xport->txt || ! *xport->txt) { ctx->set_error(ctx,400,"cache %s: with no ", cache->name); return; } else { char *endptr; int iport = (int)strtol(xport->txt,&endptr,10); if(*endptr != 0) { ctx->set_error(ctx,400,"failed to parse value %s for memcache cache %s", xport->txt,cache->name); return; } dcache->servers[i].port = iport; } i++; } dcache->detect_blank = 0; if ((cur_node = ezxml_child(node, "detect_blank")) != NULL) { if(!strcasecmp(cur_node->txt,"true")) { dcache->detect_blank = 1; } } } /** * \private \memberof mapcache_cache_memcache */ static void _mapcache_cache_memcache_configuration_post_config(mapcache_context *ctx, mapcache_cache *cache, mapcache_cfg *cfg) { mapcache_cache_memcache *dcache = (mapcache_cache_memcache*)cache; if(!dcache->nservers) { ctx->set_error(ctx,400,"cache %s has no servers configured",cache->name); } } /** * \brief creates and initializes a mapcache_memcache_cache */ mapcache_cache* mapcache_cache_memcache_create(mapcache_context *ctx) { mapcache_cache_memcache *cache = apr_pcalloc(ctx->pool,sizeof(mapcache_cache_memcache)); if(!cache) { ctx->set_error(ctx, 500, "failed to allocate memcache cache"); return NULL; } 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.configuration_post_config = _mapcache_cache_memcache_configuration_post_config; cache->cache.configuration_parse_xml = _mapcache_cache_memcache_configuration_parse_xml; return (mapcache_cache*)cache; } #endif /* vim: ts=2 sts=2 et sw=2 */ mapcache-1.4.0/lib/cache_multitier.c000066400000000000000000000157541255567662100173630ustar00rootroot00000000000000/****************************************************************************** * * Project: MapServer * Purpose: MapCache tile caching: multitier cache backend. * 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 multitierriction, 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" 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; icaches->nelts; i++) { mapcache_cache *subcache = APR_ARRAY_IDX(cache->caches,i,mapcache_cache*); if(subcache->tile_exists(ctx, subcache, tile) == MAPCACHE_TRUE) { return MAPCACHE_TRUE; } } return MAPCACHE_FALSE; } static void _mapcache_cache_multitier_tile_delete(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { mapcache_cache_multitier *cache = (mapcache_cache_multitier*)pcache; int i; for(i=0; icaches->nelts; i++) { mapcache_cache *subcache = APR_ARRAY_IDX(cache->caches,i,mapcache_cache*); subcache->tile_delete(ctx, subcache, tile); ctx->clear_errors(ctx); /* ignore errors */ } } /** * \brief get content of given tile * * fills the mapcache_tile::data of the given tile with content stored on the multitier server * \private \memberof mapcache_cache_multitier * \sa mapcache_cache::tile_get() */ static int _mapcache_cache_multitier_tile_get(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { mapcache_cache_multitier *cache = (mapcache_cache_multitier*)pcache; mapcache_cache *subcache; int i,ret; subcache = APR_ARRAY_IDX(cache->caches,0,mapcache_cache*); ret = subcache->tile_get(ctx, subcache, tile); if(ret == MAPCACHE_CACHE_MISS) { for(i=1; icaches->nelts; i++) { subcache = APR_ARRAY_IDX(cache->caches,i,mapcache_cache*); if(subcache->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); 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); } return MAPCACHE_SUCCESS; } } return MAPCACHE_CACHE_MISS; } else { ctx->log(ctx,MAPCACHE_DEBUG,"got tile (%s,z=%d,y=%d,x=%d) from primary cache (%s)",tile->tileset->name, tile->z, tile->y, tile->x, subcache->name); return ret; } } static void _mapcache_cache_multitier_tile_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { 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); } 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;itile_set(ctx, subcache, &tiles[i]); GC_CHECK_ERROR(ctx); } } } /** * \private \memberof mapcache_cache_multitier */ static void _mapcache_cache_multitier_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *pcache, mapcache_cfg *config) { ezxml_t cur_node; mapcache_cache_multitier *cache = (mapcache_cache_multitier*)pcache; cache->caches = apr_array_make(ctx->pool,3,sizeof(mapcache_cache*)); for(cur_node = ezxml_child(node,"cache"); cur_node; cur_node = cur_node->next) { mapcache_cache *refcache = mapcache_configuration_get_cache(config, cur_node->txt); if(!refcache) { ctx->set_error(ctx, 400, "multitier cache \"%s\" references cache \"%s\"," " but it is not configured (hint:referenced caches must be declared before this multitier cache in the xml file)", pcache->name, cur_node->txt); return; } APR_ARRAY_PUSH(cache->caches,mapcache_cache*) = refcache; } if(cache->caches->nelts == 0) { ctx->set_error(ctx,400,"multitier cache \"%s\" does not reference any child caches", pcache->name); } } /** * \private \memberof mapcache_cache_multitier */ static void _mapcache_cache_multitier_configuration_post_config(mapcache_context *ctx, mapcache_cache *cache, mapcache_cfg *cfg) { } /** * \brief creates and initializes a mapcache_cache_multitier */ mapcache_cache* mapcache_cache_multitier_create(mapcache_context *ctx) { mapcache_cache_multitier *cache = apr_pcalloc(ctx->pool,sizeof(mapcache_cache_multitier)); if(!cache) { ctx->set_error(ctx, 500, "failed to allocate multitier cache"); return NULL; } 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.configuration_post_config = _mapcache_cache_multitier_configuration_post_config; cache->cache.configuration_parse_xml = _mapcache_cache_multitier_configuration_parse_xml; return (mapcache_cache*)cache; } mapcache-1.4.0/lib/cache_rest.c000066400000000000000000001244701255567662100163160ustar00rootroot00000000000000/****************************************************************************** * $Id$ * * Project: MapServer * Purpose: MapCache tile caching: HTTP Rest cache backend. * Author: Thomas Bonfort and the MapServer team. * ****************************************************************************** * Copyright (c) 2014 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 #include #include #include #include #include #include typedef struct { mapcache_buffer *buffer; size_t offset; } buffer_struct; static size_t buffer_read_callback(void *ptr, size_t size, size_t nmemb, void *stream) { buffer_struct *buffer = (buffer_struct*)stream; void *start = ((char*)(buffer->buffer->buf)) + buffer->offset; size_t bytes = MAPCACHE_MIN((buffer->buffer->size-buffer->offset),(size * nmemb)); if(bytes) { memcpy(ptr,start,bytes); buffer->offset += bytes; } return bytes; } size_t buffer_write_callback(void *ptr, size_t size, size_t nmemb, void *data) { mapcache_buffer *buffer = (mapcache_buffer*)data; size_t realsize = size * nmemb; return mapcache_buffer_append(buffer, realsize, ptr); } static void _set_headers(mapcache_context *ctx, CURL *curl, apr_table_t *headers) { if(!headers) { return; } else { struct curl_slist *curl_headers=NULL; const apr_array_header_t *array = apr_table_elts(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)); } curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_headers); } } static void _put_request(mapcache_context *ctx, CURL *curl, mapcache_buffer *buffer, char *url, apr_table_t *headers) { CURLcode res; buffer_struct data; mapcache_buffer *response; data.buffer = buffer; data.offset = 0; response = mapcache_buffer_create(10,ctx->pool); #if LIBCURL_VERSION_NUM < 0x071700 /* * hack around a bug in curl <= 7.22 where the content-length is added * a second time even if ti was present in the manually set headers */ apr_table_unset(headers, "Content-Length"); #endif _set_headers(ctx, curl, headers); curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); /* we want to use our own read function */ curl_easy_setopt(curl, CURLOPT_READFUNCTION, buffer_read_callback); /* enable uploading */ curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); /* HTTP PUT please */ curl_easy_setopt(curl, CURLOPT_PUT, 1L); /* specify target URL, and note that this URL should include a file * name, not only a directory */ curl_easy_setopt(curl, CURLOPT_URL, url); /* now specify which file to upload */ curl_easy_setopt(curl, CURLOPT_READDATA, &data); /* provide the size of the upload, we specicially typecast the value * to curl_off_t since we must be sure to use the correct data size */ curl_easy_setopt(curl, CURLOPT_INFILESIZE, buffer->size); /* send all data to this function */ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, buffer_write_callback); /* we pass our mapcache_buffer struct to the callback function */ curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)response); /* 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 put: %s",curl_easy_strerror(res)); } else { long http_code; curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code); if(http_code != 200 && http_code != 201 && http_code != 204) { char *msg = response->buf; msg[response->size]=0; ctx->set_error(ctx, 500, "curl_easy_perform() failed in rest put with code %ld: %s", http_code, msg); } } } static int _head_request(mapcache_context *ctx, 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); /* specify target URL, and note that this URL should include a file * name, not only a directory */ curl_easy_setopt(curl, CURLOPT_URL, url); 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)); http_code = 500; } else { 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) { 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); /* specify target URL, and note that this URL should include a file * name, not only a directory */ curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); /* 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)); 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) { 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); data = mapcache_buffer_create(4000, ctx->pool); /* send all data to this function */ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, buffer_write_callback); /* we pass our mapcache_buffer struct to the callback function */ curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)data); /* specify target URL, and note that this URL should include a file * name, not only a directory */ curl_easy_setopt(curl, CURLOPT_URL, url); /* 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 get: %s",curl_easy_strerror(res)); data = NULL; } else { curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code); /* handle special behavior of s3 */ if(http_code == 403) { char *msg = data->buf; while(msg && *msg) { if(!strncmp(msg,"NoSuchKey",strlen("NoSuchKey"))) { ctx->set_error(ctx, 500, "curl_easy_perform() failed in rest get with code %ld: %s", http_code, msg); http_code = 404; data = NULL; break; } msg++; } } if(http_code != 200 && http_code != 404) { char *msg = data->buf; msg[data->size]=0; ctx->set_error(ctx, 500, "curl_easy_perform() failed in rest get with code %ld: %s", http_code, msg); } if(http_code == 404) { data = NULL; /* not an error */ } } /* always cleanup */ curl_easy_cleanup(curl); return data; } 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) { array = apr_table_elts(config->common_headers); apr_table_entry_t *elts = (apr_table_entry_t *) array->elts; int i; for (i = 0; i < array->nelts; i++) { apr_table_set(ret, elts[i].key,elts[i].val); } } if(operation->headers) { array = apr_table_elts(operation->headers); apr_table_entry_t *elts = (apr_table_entry_t *) array->elts; int i; for (i = 0; i < array->nelts; i++) { apr_table_set(ret, elts[i].key,elts[i].val); } } return ret; } /* Converts an integer value to its hex character*/ static char to_hex(char code) { static char hex[] = "0123456789ABCDEF"; return hex[code & 15]; } /* Returns a url-encoded version of str */ static char *url_encode(apr_pool_t *pool, char *str) { char *pstr = str, *buf = apr_pcalloc(pool, strlen(str) * 3 + 1), *pbuf = buf; while (*pstr) { if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~' || *pstr=='/') *pbuf++ = *pstr; else if (*pstr == ' ') *pbuf++ = '+'; else *pbuf++ = '%', *pbuf++ = to_hex(*pstr >> 4), *pbuf++ = to_hex(*pstr & 15); pstr++; } *pbuf = '\0'; return buf; } /** * \brief return url for given tile given a template * * \param tile the tile to get the key from * \param template the template to build the url from * \param path pointer to a char* that will contain the url * \param r * \private \memberof mapcache_cache_rest */ static void _mapcache_cache_rest_tile_url(mapcache_context *ctx, mapcache_tile *tile, mapcache_rest_configuration *config, mapcache_rest_operation *operation, char **url) { char *slashptr,*path; int cnt=0; if(operation && operation->tile_url) { *url = apr_pstrdup(ctx->pool, operation->tile_url); } else { *url = apr_pstrdup(ctx->pool, config->tile_url); } *url = mapcache_util_str_replace(ctx->pool, *url, "{tileset}", tile->tileset->name); *url = mapcache_util_str_replace(ctx->pool, *url, "{grid}", tile->grid_link->grid->name); *url = mapcache_util_str_replace(ctx->pool, *url, "{ext}", tile->tileset->format?tile->tileset->format->extension:"png"); if(strstr(*url,"{x}")) *url = mapcache_util_str_replace(ctx->pool,*url, "{x}", apr_psprintf(ctx->pool,"%d",tile->x)); else *url = mapcache_util_str_replace(ctx->pool,*url, "{inv_x}", apr_psprintf(ctx->pool,"%d", tile->grid_link->grid->levels[tile->z]->maxx - tile->x - 1)); if(strstr(*url,"{y}")) *url = mapcache_util_str_replace(ctx->pool,*url, "{y}", apr_psprintf(ctx->pool,"%d",tile->y)); else *url = mapcache_util_str_replace(ctx->pool,*url, "{inv_y}", apr_psprintf(ctx->pool,"%d", tile->grid_link->grid->levels[tile->z]->maxy - tile->y - 1)); if(strstr(*url,"{z}")) *url = mapcache_util_str_replace(ctx->pool,*url, "{z}", apr_psprintf(ctx->pool,"%d",tile->z)); else *url = mapcache_util_str_replace(ctx->pool,*url, "{inv_z}", 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); } *url = mapcache_util_str_replace(ctx->pool,*url, "{dim}", dimstring); } /* url-encode everything after the host name */ /* find occurence of third "/" in url */ slashptr = *url; while(*slashptr) { if(*slashptr == '/') cnt++; if(cnt == 3) break; slashptr++; } if(!*slashptr) { ctx->set_error(ctx,500,"invalid rest url provided, expecting http(s)://server/path format"); return; } path=slashptr; path = url_encode(ctx->pool,path); *slashptr=0; *url = apr_pstrcat(ctx->pool,*url,path,NULL); /*ctx->log(ctx,MAPCACHE_WARN,"rest url: %s",*url);*/ } // Simple comparison function for comparing two HTTP header names that are // embedded within an HTTP header line, returning true if header1 comes // before header2 alphabetically, false if not static int headerle(const char *header1, const char *header2) { while (1) { if (*header1 == ':') { return (*header2 == ':'); } else if (*header2 == ':') { return 0; } else if (*header2 < *header1) { return 0; } else if (*header2 > *header1) { return 1; } header1++, header2++; } } // Replace this with merge sort eventually, it's the best stable sort. But // since typically the number of elements being sorted is small, it doesn't // matter that much which sort is used, and gnome sort is the world's simplest // stable sort. Added a slight twist to the standard gnome_sort - don't go // forward +1, go forward to the last highest index considered. This saves // all the string comparisons that would be done "going forward", and thus // only does the necessary string comparisons to move values back into their // sorted position. static void header_gnome_sort(char **headers, int size) { int i = 0, last_highest = 0; while (i < size) { if ((i == 0) || headerle(headers[i - 1], headers[i])) { i = ++last_highest; } else { char *tmp = headers[i]; headers[i] = headers[i - 1]; headers[--i] = tmp; } } } 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]; const char *head; const apr_array_header_t *ahead; apr_table_entry_t *elts; int i,nCanonicalHeaders=0,cnt=0; assert(rcache->provider == MAPCACHE_REST_PROVIDER_GOOGLE); mapcache_cache_google *google = (mapcache_cache_google*)rcache; time_t now = time(NULL); struct tm *tnow = gmtime(&now); unsigned char sha[65]; char b64[150]; sha[64]=0; strftime(x_amz_date, 64 , "%a, %d %b %Y %H:%M:%S GMT", tnow); apr_table_set(headers,"x-amz-date",x_amz_date); if(!strcmp(method,"PUT")) { assert(tile->encoded_data); apr_md5(sha,tile->encoded_data->buf,tile->encoded_data->size); apr_base64_encode(b64, (char*)sha, 16); apr_table_set(headers, "Content-MD5", b64); } head = apr_table_get(headers, "Content-MD5"); if(!head) head = ""; 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); /* Date: header, left empty as we are using x-amz-date */ stringToSign=apr_pstrcat(ctx->pool, stringToSign, "\n", NULL); ahead = apr_table_elts(headers); aheaders = apr_pcalloc(ctx->pool, ahead->nelts * sizeof(char*)); elts = (apr_table_entry_t *) ahead->elts; for (i = 0; i < ahead->nelts; i++) { if(!strncmp(elts[i].key,"x-amz-",6)) { char *k = aheaders[nCanonicalHeaders] = apr_pstrdup(ctx->pool, elts[i].key); while(*k) { *k = tolower(*k); k++; } nCanonicalHeaders++; } } header_gnome_sort(aheaders, nCanonicalHeaders); for(i=0; ipool, stringToSign, aheaders[i],":",apr_table_get(headers,aheaders[i]),"\n",NULL); } /* find occurence of third "/" in url */ while(*resource) { if(*resource == '/') cnt++; if(cnt == 3) break; resource++; } if(!*resource) { ctx->set_error(ctx,500,"invalid google url provided"); return; } stringToSign = apr_pstrcat(ctx->pool, stringToSign, resource, NULL); hmac_sha1(stringToSign, strlen(stringToSign), (unsigned char*)google->secret, strlen(google->secret), sha); apr_base64_encode(b64, (char*)sha, 20); 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]; const char *head; const apr_array_header_t *ahead; apr_table_entry_t *elts; int i,nCanonicalHeaders=0,cnt=0; assert(rcache->provider == MAPCACHE_REST_PROVIDER_AZURE); mapcache_cache_azure *azure = (mapcache_cache_azure*)rcache; time_t now = time(NULL); struct tm *tnow = gmtime(&now); unsigned char sha[65]; char *b64sign,*keyub64; sha[64]=0; strftime(x_ms_date, sizeof(x_ms_date), "%a, %d %b %Y %H:%M:%S GMT", tnow); apr_table_set(headers,"x-ms-date",x_ms_date); apr_table_set(headers,"x-ms-version","2009-09-19"); 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); ahead = apr_table_elts(headers); aheaders = apr_pcalloc(ctx->pool, ahead->nelts * sizeof(char*)); elts = (apr_table_entry_t *) ahead->elts; for (i = 0; i < ahead->nelts; i++) { if(strncmp(elts[i].key,"x-ms-",5) || elts[i].key[5]==0) continue; char *k = aheaders[nCanonicalHeaders] = apr_pstrdup(ctx->pool, elts[i].key); while(*k) { *k = tolower(*k); k++; } nCanonicalHeaders++; } header_gnome_sort(aheaders, nCanonicalHeaders); for(i=0; ipool, canonical_headers, aheaders[i],":",apr_table_get(headers,aheaders[i]),"\n",NULL); } /* find occurence of third "/" in url */ while(*resource) { if(*resource == '/') cnt++; if(cnt == 3) break; resource++; } if(!*resource) { ctx->set_error(ctx,500,"invalid azure url provided"); return; } canonical_resource = apr_pstrcat(ctx->pool, "/", azure->id, resource, NULL); stringToSign = apr_pstrcat(ctx->pool, stringToSign, canonical_headers, canonical_resource, NULL); keyub64 = (char*)apr_pcalloc(ctx->pool, apr_base64_decode_len(azure->secret)); apr_base64_decode(keyub64, azure->secret); hmac_sha256((unsigned char*)stringToSign, strlen(stringToSign), (unsigned char*)keyub64, strlen(keyub64), sha, 32); b64sign = (char*)apr_pcalloc(ctx->pool, apr_base64_encode_len(32)); apr_base64_encode(b64sign, (char*)sha, 32); apr_table_set( headers, "Authorization", apr_pstrcat(ctx->pool,"SharedKey ", azure->id, ":", b64sign, NULL)); } 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]; int cnt=0,i; time_t now = time(NULL); struct tm *tnow = gmtime(&now); const apr_array_header_t *ahead; char *tosign, *key, *canonical_request, x_amz_date[64], *resource = url, **aheaders, *auth; apr_table_entry_t *elts; sha1[64]=sha2[64]=0; assert(rcache->provider == MAPCACHE_REST_PROVIDER_S3); mapcache_cache_s3 *s3 = (mapcache_cache_s3*)rcache; if(!strcmp(method,"PUT")) { assert(tile->encoded_data); sha256((unsigned char*)tile->encoded_data->buf, tile->encoded_data->size, sha1); sha_hex_encode(sha1,32); } else { /* sha256 hash of empty string */ memcpy(sha1,"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",64); } apr_table_set(headers,"x-amz-content-sha256", (char*)sha1); /* sha1 contains the hash of the payload */ /* find occurence of third "/" in url */ while(*resource) { if(*resource == '/') cnt++; if(cnt == 3) break; resource++; } if(!*resource) { ctx->set_error(ctx,500,"invalid s3 url provided"); return; } 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); canonical_request = apr_pstrcat(ctx->pool, method, "\n" ,resource, "\n\n",NULL); ahead = apr_table_elts(headers); aheaders = apr_pcalloc(ctx->pool, ahead->nelts * sizeof(char*)); elts = (apr_table_entry_t *) ahead->elts; for (i = 0; i < ahead->nelts; i++) { char *k = aheaders[i] = apr_pstrdup(ctx->pool, elts[i].key); while(*k) { *k = tolower(*k); k++; } } header_gnome_sort(aheaders, ahead->nelts); for(i=0; inelts; i++) { canonical_request = apr_pstrcat(ctx->pool, canonical_request, aheaders[i],":",apr_table_get(headers,aheaders[i]),"\n",NULL); } canonical_request = apr_pstrcat(ctx->pool, canonical_request, "\n", NULL); for(i=0; inelts; i++) { if(i==ahead->nelts-1) { canonical_request = apr_pstrcat(ctx->pool, canonical_request, aheaders[i],NULL); } else { canonical_request = apr_pstrcat(ctx->pool, canonical_request, aheaders[i],";",NULL); } } canonical_request = apr_pstrcat(ctx->pool, canonical_request, "\n", sha1, NULL); //printf("canonical request: %s\n",canonical_request); tosign = apr_pstrcat(ctx->pool, "AWS4-HMAC-SHA256\n",x_amz_date,"\n",NULL); x_amz_date[8]=0; sha256((unsigned char*)canonical_request, strlen(canonical_request), sha1); sha_hex_encode(sha1,32); 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); 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); hmac_sha256((unsigned char*)"aws4_request", 12, sha1, 32, sha2, 32); hmac_sha256((unsigned char*)tosign, strlen(tosign), sha2, 32, sha1, 32); 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); for(i=0; inelts; i++) { if(i==ahead->nelts-1) { auth = apr_pstrcat(ctx->pool, auth, aheaders[i],NULL); } else { auth = apr_pstrcat(ctx->pool, auth, aheaders[i],";",NULL); } } auth = apr_pstrcat(ctx->pool, auth, ",Signature=", sha1, NULL); apr_table_set(headers, "Authorization", auth); } static void _mapcache_cache_s3_put_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) { _mapcache_cache_s3_headers_add(ctx, "PUT", pcache, tile, url, headers); } static void _mapcache_cache_s3_get_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) { _mapcache_cache_s3_headers_add(ctx, "GET", pcache, tile, url, headers); } static void _mapcache_cache_s3_head_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) { _mapcache_cache_s3_headers_add(ctx, "HEAD", pcache, tile, url, headers); } static void _mapcache_cache_s3_delete_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) { _mapcache_cache_s3_headers_add(ctx, "DELETE", pcache, tile, url, headers); } static void _mapcache_cache_azure_put_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) { _mapcache_cache_azure_headers_add(ctx, "PUT", pcache, tile, url, headers); } static void _mapcache_cache_azure_get_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) { _mapcache_cache_azure_headers_add(ctx, "GET", pcache, tile, url, headers); } static void _mapcache_cache_azure_head_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) { _mapcache_cache_azure_headers_add(ctx, "HEAD", pcache, tile, url, headers); } static void _mapcache_cache_azure_delete_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) { _mapcache_cache_azure_headers_add(ctx, "DELETE", pcache, tile, url, headers); } static void _mapcache_cache_google_put_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) { _mapcache_cache_google_headers_add(ctx, "PUT", pcache, tile, url, headers); } static void _mapcache_cache_google_get_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) { _mapcache_cache_google_headers_add(ctx, "GET", pcache, tile, url, headers); } static void _mapcache_cache_google_head_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) { _mapcache_cache_google_headers_add(ctx, "HEAD", pcache, tile, url, headers); } static void _mapcache_cache_google_delete_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) { _mapcache_cache_google_headers_add(ctx, "DELETE", pcache, tile, url, headers); } static int _mapcache_cache_rest_has_tile(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { mapcache_cache_rest *rcache = (mapcache_cache_rest*)pcache; char *url; apr_table_t *headers; int status; _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(rcache->rest.add_headers) { rcache->rest.add_headers(ctx,rcache,tile,url,headers); } if(rcache->rest.has_tile.add_headers) { rcache->rest.has_tile.add_headers(ctx,rcache,tile,url,headers); } status = _head_request(ctx, url, headers); if(GC_HAS_ERROR(ctx)) return MAPCACHE_FAILURE; if( status == 200) return MAPCACHE_TRUE; else return MAPCACHE_FALSE; } static void _mapcache_cache_rest_delete(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { mapcache_cache_rest *rcache = (mapcache_cache_rest*)pcache; char *url; apr_table_t *headers; int status; _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); if(rcache->rest.add_headers) { rcache->rest.add_headers(ctx,rcache,tile,url,headers); } if(rcache->rest.delete_tile.add_headers) { rcache->rest.delete_tile.add_headers(ctx,rcache,tile,url,headers); } status = _delete_request(ctx, url, headers); GC_CHECK_ERROR(ctx); if(status!=200 && status!=202 && status!=204) { //ctx->set_error(ctx,500,"rest delete returned code %d", status); } } /** * \brief get file content of given tile * * fills the mapcache_tile::data of the given tile with content stored in the file * \private \memberof mapcache_cache_rest * \sa mapcache_cache::tile_get() */ static int _mapcache_cache_rest_get(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { mapcache_cache_rest *rcache = (mapcache_cache_rest*)pcache; char *url; apr_table_t *headers; _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(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); if(GC_HAS_ERROR(ctx)) return MAPCACHE_FAILURE; 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) { 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; } for(i=0; irest, &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; } } 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(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; } } multi_put_cleanup: /* always cleanup */ curl_easy_cleanup(curl); } /** * \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); } static void _mapcache_cache_rest_operation_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *cache, mapcache_rest_operation *op) { ezxml_t cur_node; if ((cur_node = ezxml_child(node,"headers")) != NULL) { ezxml_t header_node; op->headers = apr_table_make(ctx->pool,3); for(header_node = cur_node->child; header_node; header_node = header_node->sibling) { apr_table_set(op->headers, header_node->name, header_node->txt); } } } /** * \private \memberof mapcache_cache_rest */ static void _mapcache_cache_rest_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *cache, mapcache_cfg *config) { ezxml_t cur_node; mapcache_cache_rest *dcache = (mapcache_cache_rest*)cache; if ((cur_node = ezxml_child(node,"url")) != NULL) { dcache->rest.tile_url = apr_pstrdup(ctx->pool,cur_node->txt); } if ((cur_node = ezxml_child(node,"use_redirects")) != NULL) { if(!strcasecmp(cur_node->txt,"true")) { dcache->use_redirects = 1; } } if ((cur_node = ezxml_child(node,"headers")) != NULL) { ezxml_t header_node; dcache->rest.common_headers = apr_table_make(ctx->pool,3); for(header_node = cur_node->child; header_node; header_node = header_node->sibling) { apr_table_set(dcache->rest.common_headers, header_node->name, header_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) { ctx->set_error(ctx,400," with no \"type\" attribute in cache (%s)", cache->name); return; } if(!strcasecmp(type,"put")) { _mapcache_cache_rest_operation_parse_xml(ctx,cur_node,cache,&dcache->rest.set_tile); GC_CHECK_ERROR(ctx); } else if(!strcasecmp(type,"get")) { _mapcache_cache_rest_operation_parse_xml(ctx,cur_node,cache,&dcache->rest.get_tile); GC_CHECK_ERROR(ctx); } else if(!strcasecmp(type,"head")) { _mapcache_cache_rest_operation_parse_xml(ctx,cur_node,cache,&dcache->rest.has_tile); GC_CHECK_ERROR(ctx); } else if(!strcasecmp(type,"delete")) { _mapcache_cache_rest_operation_parse_xml(ctx,cur_node,cache,&dcache->rest.delete_tile); GC_CHECK_ERROR(ctx); } else { ctx->set_error(ctx,400," with unknown \"type\" (%s) attribute in cache (%s) (expecting put, get, head or delete)", type, cache->name); return; } } } static void _mapcache_cache_google_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *cache, mapcache_cfg *config) { ezxml_t cur_node; mapcache_cache_google *google = (mapcache_cache_google*)cache; _mapcache_cache_rest_configuration_parse_xml(ctx, node, cache, config); GC_CHECK_ERROR(ctx); if ((cur_node = ezxml_child(node,"access")) != NULL) { google->access = apr_pstrdup(ctx->pool, cur_node->txt); } else { ctx->set_error(ctx,400,"google cache (%s) is missing required child", cache->name); return; } if ((cur_node = ezxml_child(node,"secret")) != NULL) { google->secret = apr_pstrdup(ctx->pool, cur_node->txt); } else { ctx->set_error(ctx,400,"google cache (%s) is missing required child", cache->name); return; } } static void _mapcache_cache_s3_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *cache, mapcache_cfg *config) { ezxml_t cur_node; 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); } else { ctx->set_error(ctx,400,"s3 cache (%s) is missing required 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 child", cache->name); return; } if ((cur_node = ezxml_child(node,"region")) != NULL) { s3->region = apr_pstrdup(ctx->pool, cur_node->txt); } else { ctx->set_error(ctx,400,"s3 cache (%s) is missing required child", cache->name); return; } } static void _mapcache_cache_azure_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *cache, mapcache_cfg *config) { ezxml_t cur_node; mapcache_cache_azure *azure = (mapcache_cache_azure*)cache; _mapcache_cache_rest_configuration_parse_xml(ctx, node, cache, config); GC_CHECK_ERROR(ctx); if ((cur_node = ezxml_child(node,"id")) != NULL) { azure->id = apr_pstrdup(ctx->pool, cur_node->txt); } else { ctx->set_error(ctx,400,"azure cache (%s) is missing required child", cache->name); return; } if ((cur_node = ezxml_child(node,"secret")) != NULL) { azure->secret = apr_pstrdup(ctx->pool, cur_node->txt); } else { ctx->set_error(ctx,400,"azure cache (%s) is missing required child", cache->name); return; } if ((cur_node = ezxml_child(node,"container")) != NULL) { azure->container = apr_pstrdup(ctx->pool, cur_node->txt); } else { ctx->set_error(ctx,400,"azure cache (%s) is missing required child", cache->name); return; } } /** * \private \memberof mapcache_cache_rest */ static void _mapcache_cache_rest_configuration_post_config(mapcache_context *ctx, mapcache_cache *cache, mapcache_cfg *cfg) { mapcache_cache_rest *dcache = (mapcache_cache_rest*)cache; if(!dcache->rest.tile_url) { if(!dcache->rest.delete_tile.tile_url) { ctx->set_error(ctx,400, "rest cache (%s) has no global and no for delete_tile operation", cache->name); return; } if(!dcache->rest.get_tile.tile_url) { ctx->set_error(ctx,400, "rest cache (%s) has no global and no for get_tile operation", cache->name); return; } if(!dcache->rest.set_tile.tile_url) { ctx->set_error(ctx,400, "rest cache (%s) has no global and no for set_tile operation", cache->name); return; } } } 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; cache->rest.delete_tile.method = MAPCACHE_REST_METHOD_DELETE; cache->rest.multi_set_tile.method = MAPCACHE_REST_METHOD_PUT; 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.configuration_post_config = _mapcache_cache_rest_configuration_post_config; cache->cache.configuration_parse_xml = _mapcache_cache_rest_configuration_parse_xml; } /** * \brief creates and initializes a mapcache_rest_cache */ mapcache_cache* mapcache_cache_rest_create(mapcache_context *ctx) { mapcache_cache_rest *cache = apr_pcalloc(ctx->pool,sizeof(mapcache_cache_rest)); if(!cache) { ctx->set_error(ctx, 500, "failed to allocate rest cache"); return NULL; } mapcache_cache_rest_init(ctx,cache); cache->provider = MAPCACHE_REST_PROVIDER_NONE; return (mapcache_cache*)cache; } /** * \brief creates and initializes a mapcache_s3_cache */ mapcache_cache* mapcache_cache_s3_create(mapcache_context *ctx) { mapcache_cache_s3 *cache = apr_pcalloc(ctx->pool,sizeof(mapcache_cache_s3)); if(!cache) { ctx->set_error(ctx, 500, "failed to allocate s3 cache"); return NULL; } mapcache_cache_rest_init(ctx,&cache->cache); cache->cache.provider = MAPCACHE_REST_PROVIDER_S3; cache->cache.cache.configuration_parse_xml = _mapcache_cache_s3_configuration_parse_xml; cache->cache.rest.get_tile.add_headers = _mapcache_cache_s3_get_headers_add; cache->cache.rest.has_tile.add_headers = _mapcache_cache_s3_head_headers_add; cache->cache.rest.set_tile.add_headers = _mapcache_cache_s3_put_headers_add; cache->cache.rest.delete_tile.add_headers = _mapcache_cache_s3_delete_headers_add; return (mapcache_cache*)cache; } /** * \brief creates and initializes a mapcache_azure_cache */ mapcache_cache* mapcache_cache_azure_create(mapcache_context *ctx) { mapcache_cache_azure *cache = apr_pcalloc(ctx->pool,sizeof(mapcache_cache_azure)); if(!cache) { ctx->set_error(ctx, 500, "failed to allocate azure cache"); return NULL; } mapcache_cache_rest_init(ctx,&cache->cache); cache->cache.provider = MAPCACHE_REST_PROVIDER_AZURE; cache->cache.cache.configuration_parse_xml = _mapcache_cache_azure_configuration_parse_xml; cache->cache.rest.get_tile.add_headers = _mapcache_cache_azure_get_headers_add; cache->cache.rest.has_tile.add_headers = _mapcache_cache_azure_head_headers_add; cache->cache.rest.set_tile.add_headers = _mapcache_cache_azure_put_headers_add; cache->cache.rest.delete_tile.add_headers = _mapcache_cache_azure_delete_headers_add; return (mapcache_cache*)cache; } /** * \brief creates and initializes a mapcache_google_cache */ mapcache_cache* mapcache_cache_google_create(mapcache_context *ctx) { mapcache_cache_google *cache = apr_pcalloc(ctx->pool,sizeof(mapcache_cache_google)); if(!cache) { ctx->set_error(ctx, 500, "failed to allocate google cache"); return NULL; } mapcache_cache_rest_init(ctx,&cache->cache); cache->cache.provider = MAPCACHE_REST_PROVIDER_GOOGLE; cache->cache.cache.configuration_parse_xml = _mapcache_cache_google_configuration_parse_xml; cache->cache.rest.get_tile.add_headers = _mapcache_cache_google_get_headers_add; cache->cache.rest.has_tile.add_headers = _mapcache_cache_google_head_headers_add; cache->cache.rest.set_tile.add_headers = _mapcache_cache_google_put_headers_add; cache->cache.rest.delete_tile.add_headers = _mapcache_cache_google_delete_headers_add; return (mapcache_cache*)cache; } /* vim: ts=2 sts=2 et sw=2 */ mapcache-1.4.0/lib/cache_riak.c000066400000000000000000000370001255567662100162570ustar00rootroot00000000000000/****************************************************************************** * $Id$ * * Project: MapServer * Purpose: MapCache tile caching support file: riak cache backend. * Author: Michael Downey and the MapServer team. * ****************************************************************************** * Copyright (c) 1996-2013 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-config.h" #ifdef USE_RIAK #include "mapcache.h" #include #include #include #include #include /* * 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. */ struct riak_conn_params { mapcache_cache_riak *cache; }; void mapcache_riak_connection_constructor(mapcache_context *ctx, void **conn_, void *params, apr_pool_t *pool) { mapcache_cache_riak *cache = ((struct riak_conn_params*)params)->cache; struct RIACK_CONNECTION_OPTIONS options; struct RIACK_CLIENT *client = riack_new_client(0); if (client == NULL) { ctx->set_error(ctx,500,"failed to riack_new_client(0)"); return; } options.recv_timeout_ms = 2000; options.send_timeout_ms = 2000; if (riack_connect(client, cache->host, cache->port, &options) != RIACK_SUCCESS) { riack_free(client); ctx->set_error(ctx,500,"failed to riack_connect()"); return; } if (riack_ping(client) != RIACK_SUCCESS) { riack_free(client); ctx->set_error(ctx,500,"failed to riack_ping()"); return; } *conn_ = client; } void mapcache_riak_connection_destructor(void *conn_, apr_pool_t *process_pool) { struct RIACK_CLIENT *client = (struct RIACK_CLIENT *)conn_; riack_free(client); } static mapcache_pooled_connection* _riak_get_connection(mapcache_context *ctx, mapcache_cache_riak *cache, mapcache_tile *tile) { mapcache_pooled_connection *pc; struct riak_conn_params params; params.cache = cache; pc = mapcache_connection_pool_get_connection(ctx,cache->cache.name,mapcache_riak_connection_constructor, mapcache_riak_connection_destructor, ¶ms); return pc; } static int _mapcache_cache_riak_has_tile(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { int error; int retries = 3; RIACK_STRING key; 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", "#"); if (GC_HAS_ERROR(ctx)) { return MAPCACHE_FALSE; } key.len = strlen(key.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); 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; } if (obj.object.content_count < 1 || obj.object.content[0].data_len == 0) { error = MAPCACHE_FALSE; } else { error = MAPCACHE_TRUE; } riack_free_get_object(client, &obj); // riack_get allocates the returned object so we need to deallocate it. mapcache_connection_pool_release_connection(ctx,pc); return error; } static void _mapcache_cache_riak_delete(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { int error; RIACK_STRING key; struct RIACK_CLIENT *client; struct RIACK_DEL_PROPERTIES properties; mapcache_pooled_connection *pc; mapcache_cache_riak *cache = (mapcache_cache_riak*)pcache; 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", "#"); GC_CHECK_ERROR(ctx); key.len = strlen(key.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); mapcache_connection_pool_release_connection(ctx,pc); if (error != RIACK_SUCCESS) { ctx->set_error(ctx, 500, "riak: failed to delete key %s: %d", key, error); } } /** * \brief get content of given tile * * fills the mapcache_tile::data of the given tile with content stored on the riak server * \private \memberof mapcache_cache_riak * \sa mapcache_cache::tile_get() */ 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; struct RIACK_GET_OBJECT obj; struct RIACK_GET_PROPERTIES properties; struct RIACK_CLIENT *client; mapcache_pooled_connection *pc; mapcache_cache_riak *cache = (mapcache_cache_riak*)pcache; memset(&properties, 0, sizeof(struct RIACK_GET_PROPERTIES)); //Use Buckets defaults instead of setting the read/write attributes /* properties.r_use = 1; properties.r = 1; */ key.value = mapcache_util_get_tile_key(ctx, tile, NULL, " \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); pc = _riak_get_connection(ctx, cache, tile); if (GC_HAS_ERROR(ctx)) { return MAPCACHE_FAILURE; } 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); if (error != RIACK_SUCCESS) { if (connect_error != RIACK_SUCCESS) mapcache_connection_pool_invalidate_connection(ctx,pc); else mapcache_connection_pool_release_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; } // Check if tile exists. If it doesn't we need to return CACHE_MISS or things go wrong. // Mapcache doesn't appear to use the has_tile function and uses _get instead so we need // to do this sort of test here instead of erroring. if (obj.object.content_count < 1 || obj.object.content[0].data_len == 0) { riack_free_get_object(client, &obj); // Need to free the object here as well. mapcache_connection_pool_release_connection(ctx,pc); return MAPCACHE_CACHE_MISS; } // Copy the data into the buffer 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. mapcache_connection_pool_release_connection(ctx,pc); return MAPCACHE_SUCCESS; } /** * \brief push tile data to riak * * writes the content of mapcache_tile::data to the configured riak instance(s) * \private \memberof mapcache_cache_riak * \sa mapcache_cache::tile_set() */ static void _mapcache_cache_riak_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { char *key; int error; int connect_error = RIACK_SUCCESS; int retries = 3; struct RIACK_OBJECT object; struct RIACK_CONTENT content; struct RIACK_PUT_PROPERTIES properties; struct RIACK_CLIENT *client; mapcache_pooled_connection *pc; mapcache_cache_riak *cache = (mapcache_cache_riak*)pcache; memset(&content, 0, sizeof(struct RIACK_CONTENT)); memset(&object, 0, sizeof(struct RIACK_OBJECT)); memset(&properties, 0, sizeof(struct RIACK_PUT_PROPERTIES)); //Use Buckets defaults instead of setting the read/write attributes /* properties.w_use = 1; properties.w = 1; properties.dw_use = 1; properties.dw = 0;*/ key = mapcache_util_get_tile_key(ctx, tile, NULL, " \r\n\t\f\e\a\b", "#"); GC_CHECK_ERROR(ctx); if (!tile->encoded_data) { tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format); GC_CHECK_ERROR(ctx); } pc = _riak_get_connection(ctx, cache, tile); GC_CHECK_ERROR(ctx); client = pc->connection; // Set up the riak object to put. Need to do this after we get the client connection object.bucket.value = cache->bucket.value; object.bucket.len = cache->bucket.len; object.key.value = key; object.key.len = strlen(key); object.vclock.len = 0; object.content_count = 1; object.content = &content; content.content_type.value = tile->tileset->format->mime_type; content.content_type.len = strlen(tile->tileset->format->mime_type); content.data = (uint8_t*)tile->encoded_data->buf; content.data_len = tile->encoded_data->size; // If we get an error it is advised that we call reconnect. It also appears // that every now and then we get an error and need to retry once again to // get it to work. do { error = riack_put(client, object, 0, &properties); if (error != RIACK_SUCCESS) { ctx->log(ctx, MAPCACHE_WARN, "Retry %d in riak_set for tile %s from cache %s due to eror %d", (4 - retries), key, cache->cache.name, error); for (connect_error = riack_reconnect(client); connect_error != RIACK_SUCCESS && retries > 0; connect_error = riack_reconnect(client)) { --retries; } --retries; } } while (error != RIACK_SUCCESS && retries >= 0); if (connect_error != RIACK_SUCCESS) mapcache_connection_pool_invalidate_connection(ctx,pc); else mapcache_connection_pool_release_connection(ctx,pc); if (error != RIACK_SUCCESS) { ctx->set_error(ctx, 500, "failed to store tile %s to cache %s due to error %d.", key, cache->cache.name, error); } } /** * \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; mapcache_cache_riak *dcache = (mapcache_cache_riak*)cache; int servercount = 0; for (cur_node = ezxml_child(node,"server"); cur_node; cur_node = cur_node->next) { servercount++; } if (!servercount) { ctx->set_error(ctx, 400, "riak cache %s has no s configured", cache->name); return; } if (servercount > 1) { ctx->set_error(ctx, 400, "riak cache %s has more than 1 server configured", cache->name); return; } cur_node = ezxml_child(node, "server"); xhost = ezxml_child(cur_node, "host"); /* Host should contain just server */ xport = ezxml_child(cur_node, "port"); xbucket = ezxml_child(cur_node, "bucket"); if (!xhost || !xhost->txt || ! *xhost->txt) { ctx->set_error(ctx, 400, "cache %s: with no ", 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) { ctx->set_error(ctx, 400, "cache %s: with no ", cache->name); return; } else { dcache->port = atoi(xport->txt); } if (!xbucket || !xbucket->txt || ! *xbucket->txt) { ctx->set_error(ctx, 400, "cache %s: with no ", 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); } } /** * \private \memberof mapcache_cache_riak */ static void _mapcache_cache_riak_configuration_post_config(mapcache_context *ctx, mapcache_cache *cache, mapcache_cfg *cfg) { riack_init(); } /** * \brief creates and initializes a mapcache_riak_cache */ mapcache_cache* mapcache_cache_riak_create(mapcache_context *ctx) { mapcache_cache_riak *cache = apr_pcalloc(ctx->pool,sizeof(mapcache_cache_riak)); if (!cache) { ctx->set_error(ctx, 500, "failed to allocate riak cache"); return NULL; } 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.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 return (mapcache_cache*)cache; } #endif mapcache-1.4.0/lib/cache_sqlite.c000066400000000000000000001157261255567662100166460ustar00rootroot00000000000000/****************************************************************************** * $Id$ * * Project: MapServer * Purpose: MapCache tile caching support file: sqlite cache backend * 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-config.h" #ifdef USE_SQLITE #include "mapcache.h" #include #include #include #include #include #include #ifdef APR_HAS_THREADS #include #endif #ifndef _WIN32 #include #endif #include struct sqlite_conn_params { mapcache_cache_sqlite *cache; char *dbfile; int readonly; }; struct sqlite_conn { sqlite3 *handle; int nstatements; sqlite3_stmt **prepared_statements; }; #define SQLITE_CONN(pooled_connection) ((struct sqlite_conn*)((pooled_connection)->connection)) #define HAS_TILE_STMT_IDX 0 #define GET_TILE_STMT_IDX 1 #define SQLITE_SET_TILE_STMT_IDX 2 #define SQLITE_DEL_TILE_STMT_IDX 3 #define MBTILES_SET_EMPTY_TILE_STMT1_IDX 2 #define MBTILES_SET_EMPTY_TILE_STMT2_IDX 3 #define MBTILES_SET_TILE_STMT1_IDX 4 #define MBTILES_SET_TILE_STMT2_IDX 5 #define MBTILES_DEL_TILE_SELECT_STMT_IDX 6 #define MBTILES_DEL_TILE_STMT1_IDX 7 #define MBTILES_DEL_TILE_STMT2_IDX 8 static void _mapcache_cache_sqlite_filename_for_tile(mapcache_context *ctx, mapcache_cache_sqlite *dcache, mapcache_tile *tile, char **path); static void _sqlite_set_pragmas(mapcache_context *ctx, mapcache_cache_sqlite* cache, struct sqlite_conn *conn) { if (cache->pragmas && !apr_is_empty_table(cache->pragmas)) { const apr_array_header_t *elts = apr_table_elts(cache->pragmas); /* FIXME dynamically allocate this string */ int i,ret; char *pragma_stmt; for (i = 0; i < elts->nelts; i++) { apr_table_entry_t entry = APR_ARRAY_IDX(elts, i, apr_table_entry_t); pragma_stmt = apr_psprintf(ctx->pool,"PRAGMA %s=%s",entry.key,entry.val); do { ret = sqlite3_exec(conn->handle, pragma_stmt, 0, 0, NULL); if (ret != SQLITE_OK && ret != SQLITE_BUSY && ret != SQLITE_LOCKED) { break; } } while (ret == SQLITE_BUSY || ret == SQLITE_LOCKED); if (ret != SQLITE_OK) { ctx->set_error(ctx, 500, "failed to execute pragma statement %s",pragma_stmt); return; } } } return; } static void mapcache_sqlite_release_conn(mapcache_context *ctx, mapcache_pooled_connection *conn) { mapcache_connection_pool_release_connection(ctx,conn); } void mapcache_sqlite_connection_constructor(mapcache_context *ctx, void **conn_, void *params, apr_pool_t *process_pool) { int ret; int flags; struct sqlite_conn_params *sq_params = (struct sqlite_conn_params*)params; struct sqlite_conn *conn = calloc(1, sizeof (struct sqlite_conn)); *conn_ = conn; if(sq_params->readonly) { flags = SQLITE_OPEN_READONLY | SQLITE_OPEN_NOMUTEX; } else { flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_CREATE; } ret = sqlite3_open_v2(sq_params->dbfile, &conn->handle, flags, NULL); if (ret != SQLITE_OK) { ctx->set_error(ctx,500,"sqlite backend failed to open db %s: %s", sq_params->dbfile, sqlite3_errmsg(conn->handle)); return; } sqlite3_busy_timeout(conn->handle, 300000); do { ret = sqlite3_exec(conn->handle, sq_params->cache->create_stmt.sql, 0, 0, NULL); if (ret != SQLITE_OK && ret != SQLITE_BUSY && ret != SQLITE_LOCKED) { break; } } while (ret == SQLITE_BUSY || ret == SQLITE_LOCKED); if (ret != SQLITE_OK) { ctx->set_error(ctx,500, "sqlite backend failed to create db schema on %s: %s", sq_params->dbfile, sqlite3_errmsg(conn->handle)); sqlite3_close(conn->handle); return; } _sqlite_set_pragmas(ctx, sq_params->cache, conn); if(GC_HAS_ERROR(ctx)) { sqlite3_close(conn->handle); return; } conn->prepared_statements = calloc(sq_params->cache->n_prepared_statements,sizeof(sqlite3_stmt*)); conn->nstatements = sq_params->cache->n_prepared_statements; } void mapcache_sqlite_connection_destructor(void *conn_, apr_pool_t *process_pool) { struct sqlite_conn *conn = (struct sqlite_conn*) conn_; int i; for(i=0; instatements; i++) { if(conn->prepared_statements[i]) { sqlite3_finalize(conn->prepared_statements[i]); } } free(conn->prepared_statements); sqlite3_close(conn->handle); } static mapcache_pooled_connection* mapcache_sqlite_get_conn(mapcache_context *ctx, mapcache_cache_sqlite *cache, mapcache_tile *tile, int readonly) { struct sqlite_conn_params params; mapcache_pooled_connection *pc; char *key; _mapcache_cache_sqlite_filename_for_tile(ctx,cache,tile,¶ms.dbfile); params.cache = cache; params.readonly = readonly; if(!strstr(cache->dbfile,"{")) { key = apr_pstrcat(ctx->pool,readonly?"ro_":"rw_",cache->cache.name,NULL); } else { key = apr_pstrcat(ctx->pool,readonly?"ro_":"rw_",params.dbfile,NULL); } pc = mapcache_connection_pool_get_connection(ctx,key,mapcache_sqlite_connection_constructor,mapcache_sqlite_connection_destructor,¶ms); return pc; } /** * \brief return sqlite db filename for given tile * * \param tile the tile to get the key from * \param path pointer to a char* that will contain the filename * \param r * \private \memberof mapcache_cache_sqlite */ static void _mapcache_cache_sqlite_filename_for_tile(mapcache_context *ctx, mapcache_cache_sqlite *dcache, mapcache_tile *tile, char **path) { *path = dcache->dbfile; if(strstr(*path,"{")) { /* * generic template substitutions */ if(strstr(*path,"{tileset}")) *path = mapcache_util_str_replace(ctx->pool,*path, "{tileset}", tile->tileset->name); if(strstr(*path,"{grid}")) *path = mapcache_util_str_replace(ctx->pool,*path, "{grid}", tile->grid_link->grid->name); if(tile->dimensions && strstr(*path,"{dim}")) { 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)); const char *dimval = mapcache_util_str_sanitize(ctx->pool,entry->val,"/.",'#'); dimstring = apr_pstrcat(ctx->pool,dimstring,"#",dimval,NULL); } *path = mapcache_util_str_replace(ctx->pool,*path, "{dim}", dimstring); } while(strstr(*path,"{z}")) *path = mapcache_util_str_replace(ctx->pool,*path, "{z}", apr_psprintf(ctx->pool,dcache->z_fmt,tile->z)); if(dcache->count_x > 0) { while(strstr(*path,"{div_x}")) *path = mapcache_util_str_replace(ctx->pool,*path, "{div_x}", apr_psprintf(ctx->pool,dcache->div_x_fmt,tile->x/dcache->count_x)); while(strstr(*path,"{inv_div_x}")) *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_div_x}", apr_psprintf(ctx->pool,dcache->inv_div_x_fmt,(tile->grid_link->grid->levels[tile->z]->maxx - tile->x - 1)/dcache->count_x)); while(strstr(*path,"{x}")) *path = mapcache_util_str_replace(ctx->pool,*path, "{x}", apr_psprintf(ctx->pool,dcache->x_fmt,tile->x/dcache->count_x*dcache->count_x)); while(strstr(*path,"{inv_x}")) *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_x}", apr_psprintf(ctx->pool,dcache->inv_x_fmt,(tile->grid_link->grid->levels[tile->z]->maxx - tile->x - 1)/dcache->count_x*dcache->count_x)); } if(dcache->count_y > 0) { while(strstr(*path,"{div_y}")) *path = mapcache_util_str_replace(ctx->pool,*path, "{div_y}", apr_psprintf(ctx->pool,dcache->div_y_fmt,tile->y/dcache->count_y)); while(strstr(*path,"{inv_div_y}")) *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_div_y}", apr_psprintf(ctx->pool,dcache->inv_div_y_fmt,(tile->grid_link->grid->levels[tile->z]->maxy - tile->y - 1)/dcache->count_y)); while(strstr(*path,"{y}")) *path = mapcache_util_str_replace(ctx->pool,*path, "{y}", apr_psprintf(ctx->pool,dcache->y_fmt,tile->y/dcache->count_y*dcache->count_y)); while(strstr(*path,"{inv_y}")) *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_y}", apr_psprintf(ctx->pool,dcache->inv_y_fmt,(tile->grid_link->grid->levels[tile->z]->maxy - tile->y - 1)/dcache->count_y*dcache->count_y)); } } if(!*path) { ctx->set_error(ctx,500, "failed to allocate tile key"); } } /** * \brief apply appropriate tile properties to the sqlite statement */ static void _bind_sqlite_params(mapcache_context *ctx, void *vstmt, mapcache_cache_sqlite *cache, mapcache_tile *tile) { sqlite3_stmt *stmt = vstmt; int paramidx; /* tile->x */ paramidx = sqlite3_bind_parameter_index(stmt, ":x"); if (paramidx) sqlite3_bind_int(stmt, paramidx, tile->x); /* tile->y */ paramidx = sqlite3_bind_parameter_index(stmt, ":y"); if (paramidx) sqlite3_bind_int(stmt, paramidx, tile->y); /* tile->y */ paramidx = sqlite3_bind_parameter_index(stmt, ":z"); if (paramidx) sqlite3_bind_int(stmt, paramidx, tile->z); /* eventual dimensions */ paramidx = sqlite3_bind_parameter_index(stmt, ":dim"); if (paramidx) { if (tile->dimensions) { char *dim = mapcache_util_get_tile_dimkey(ctx, tile, NULL, NULL); sqlite3_bind_text(stmt, paramidx, dim, -1, SQLITE_STATIC); } else { sqlite3_bind_text(stmt, paramidx, "", -1, SQLITE_STATIC); } } /* grid */ paramidx = sqlite3_bind_parameter_index(stmt, ":grid"); if (paramidx) sqlite3_bind_text(stmt, paramidx, tile->grid_link->grid->name, -1, SQLITE_STATIC); /* tileset */ paramidx = sqlite3_bind_parameter_index(stmt, ":tileset"); if (paramidx) sqlite3_bind_text(stmt, paramidx, tile->tileset->name, -1, SQLITE_STATIC); /* tile blob data */ paramidx = sqlite3_bind_parameter_index(stmt, ":data"); if (paramidx) { int written = 0; if(cache->detect_blank) { if(!tile->raw_image) { tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data); GC_CHECK_ERROR(ctx); } if(mapcache_image_blank_color(tile->raw_image) != MAPCACHE_FALSE) { char *buf = apr_palloc(ctx->pool, 5* sizeof(char)); buf[0] = '#'; memcpy(buf+1,tile->raw_image->data,4); written = 1; sqlite3_bind_blob(stmt, paramidx, buf, 5, SQLITE_STATIC); } } if(!written) { if (!tile->encoded_data) { tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format); GC_CHECK_ERROR(ctx); } if (tile->encoded_data && tile->encoded_data->size) { sqlite3_bind_blob(stmt, paramidx, tile->encoded_data->buf, tile->encoded_data->size, SQLITE_STATIC); } else { sqlite3_bind_text(stmt, paramidx, "", -1, SQLITE_STATIC); } } } } static void _bind_mbtiles_params(mapcache_context *ctx, void *vstmt, mapcache_cache_sqlite *cache, mapcache_tile *tile) { sqlite3_stmt *stmt = vstmt; int paramidx; paramidx = sqlite3_bind_parameter_index(stmt, ":x"); if (paramidx) sqlite3_bind_int(stmt, paramidx, tile->x); /* tile->y */ paramidx = sqlite3_bind_parameter_index(stmt, ":y"); if (paramidx) sqlite3_bind_int(stmt, paramidx, tile->y); /* tile->y */ paramidx = sqlite3_bind_parameter_index(stmt, ":z"); if (paramidx) sqlite3_bind_int(stmt, paramidx, tile->z); /* mbtiles foreign key */ paramidx = sqlite3_bind_parameter_index(stmt, ":key"); if (paramidx) { char *key = apr_psprintf(ctx->pool,"%d-%d-%d",tile->x,tile->y,tile->z); sqlite3_bind_text(stmt, paramidx, key, -1, SQLITE_STATIC); } paramidx = sqlite3_bind_parameter_index(stmt, ":color"); if (paramidx) { char *key; assert(tile->raw_image); key = apr_psprintf(ctx->pool,"#%02x%02x%02x%02x", tile->raw_image->data[0], tile->raw_image->data[1], tile->raw_image->data[2], tile->raw_image->data[3]); sqlite3_bind_text(stmt, paramidx, key, -1, SQLITE_STATIC); } /* tile blob data */ paramidx = sqlite3_bind_parameter_index(stmt, ":data"); if (paramidx) { if (!tile->encoded_data) { tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format); GC_CHECK_ERROR(ctx); } if (tile->encoded_data && tile->encoded_data->size) { sqlite3_bind_blob(stmt, paramidx, tile->encoded_data->buf, tile->encoded_data->size, SQLITE_STATIC); } else { sqlite3_bind_text(stmt, paramidx, "", -1, SQLITE_STATIC); } } } static int _mapcache_cache_sqlite_has_tile(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*) pcache; mapcache_pooled_connection *pc; struct sqlite_conn *conn; sqlite3_stmt *stmt; int ret; pc = mapcache_sqlite_get_conn(ctx,cache,tile,1); if (GC_HAS_ERROR(ctx)) { if(pc) mapcache_sqlite_release_conn(ctx, pc); if(!tile->tileset->read_only && tile->tileset->source) { /* not an error in this case, as the db file may not have been created yet */ ctx->clear_errors(ctx); } return MAPCACHE_FALSE; } conn = SQLITE_CONN(pc); stmt = conn->prepared_statements[HAS_TILE_STMT_IDX]; if(!stmt) { sqlite3_prepare(conn->handle, cache->exists_stmt.sql, -1, &conn->prepared_statements[HAS_TILE_STMT_IDX], NULL); stmt = conn->prepared_statements[HAS_TILE_STMT_IDX]; } cache->bind_stmt(ctx, stmt, cache, tile); ret = sqlite3_step(stmt); if (ret != SQLITE_DONE && ret != SQLITE_ROW) { ctx->set_error(ctx, 500, "sqlite backend failed on has_tile: %s", sqlite3_errmsg(conn->handle)); } if (ret == SQLITE_DONE) { ret = MAPCACHE_FALSE; } else if (ret == SQLITE_ROW) { ret = MAPCACHE_TRUE; } sqlite3_reset(stmt); mapcache_sqlite_release_conn(ctx, pc); return ret; } static void _mapcache_cache_sqlite_delete(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*) pcache; mapcache_pooled_connection *pc; struct sqlite_conn *conn; sqlite3_stmt *stmt; int ret; pc = mapcache_sqlite_get_conn(ctx,cache,tile,0); if (GC_HAS_ERROR(ctx)) { mapcache_sqlite_release_conn(ctx, pc); return; } conn = SQLITE_CONN(pc); stmt = conn->prepared_statements[SQLITE_DEL_TILE_STMT_IDX]; if(!stmt) { sqlite3_prepare(conn->handle, cache->delete_stmt.sql, -1, &conn->prepared_statements[SQLITE_DEL_TILE_STMT_IDX], NULL); stmt = conn->prepared_statements[SQLITE_DEL_TILE_STMT_IDX]; } cache->bind_stmt(ctx, stmt, cache, tile); ret = sqlite3_step(stmt); if (ret != SQLITE_DONE && ret != SQLITE_ROW) { ctx->set_error(ctx, 500, "sqlite backend failed on delete: %s", sqlite3_errmsg(conn->handle)); } sqlite3_reset(stmt); mapcache_sqlite_release_conn(ctx, pc); } static void _mapcache_cache_mbtiles_delete(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*) pcache; mapcache_pooled_connection *pc; struct sqlite_conn *conn; sqlite3_stmt *stmt1,*stmt2,*stmt3; int ret; const char *tile_id; size_t tile_id_size; pc = mapcache_sqlite_get_conn(ctx,cache,tile,0); if (GC_HAS_ERROR(ctx)) { mapcache_sqlite_release_conn(ctx, pc); return; } conn = SQLITE_CONN(pc); stmt1 = conn->prepared_statements[MBTILES_DEL_TILE_SELECT_STMT_IDX]; stmt2 = conn->prepared_statements[MBTILES_DEL_TILE_STMT1_IDX]; stmt3 = conn->prepared_statements[MBTILES_DEL_TILE_STMT2_IDX]; if(!stmt1) { sqlite3_prepare(conn->handle, "select tile_id from map where tile_col=:x and tile_row=:y and zoom_level=:z",-1,&conn->prepared_statements[MBTILES_DEL_TILE_SELECT_STMT_IDX], NULL); sqlite3_prepare(conn->handle, "delete from map where tile_col=:x and tile_row=:y and zoom_level=:z", -1, &conn->prepared_statements[MBTILES_DEL_TILE_STMT1_IDX], NULL); sqlite3_prepare(conn->handle, "delete from images where tile_id=:foobar", -1, &conn->prepared_statements[MBTILES_DEL_TILE_STMT2_IDX], NULL); stmt1 = conn->prepared_statements[MBTILES_DEL_TILE_SELECT_STMT_IDX]; stmt2 = conn->prepared_statements[MBTILES_DEL_TILE_STMT1_IDX]; stmt3 = conn->prepared_statements[MBTILES_DEL_TILE_STMT2_IDX]; } /* first extract tile_id from the tile we will delete. We need this because we do not know * if the tile is empty or not. * If it is empty, we will not delete the image blob data from the images table */ cache->bind_stmt(ctx, stmt1, cache, tile); do { ret = sqlite3_step(stmt1); if (ret != SQLITE_DONE && ret != SQLITE_ROW && ret != SQLITE_BUSY && ret != SQLITE_LOCKED) { ctx->set_error(ctx, 500, "sqlite backend failed on mbtile del 1: %s", sqlite3_errmsg(conn->handle)); sqlite3_reset(stmt1); mapcache_sqlite_release_conn(ctx, pc); return; } } while (ret == SQLITE_BUSY || ret == SQLITE_LOCKED); if (ret == SQLITE_DONE) { /* tile does not exist, ignore */ sqlite3_reset(stmt1); mapcache_sqlite_release_conn(ctx, pc); return; } else { tile_id = (const char*) sqlite3_column_text(stmt1, 0); tile_id_size = sqlite3_column_bytes(stmt1, 0); } /* delete the tile from the "map" table */ cache->bind_stmt(ctx,stmt2, cache, tile); ret = sqlite3_step(stmt2); if (ret != SQLITE_DONE && ret != SQLITE_ROW) { ctx->set_error(ctx, 500, "sqlite backend failed on mbtile del 2: %s", sqlite3_errmsg(conn->handle)); sqlite3_reset(stmt1); sqlite3_reset(stmt2); mapcache_sqlite_release_conn(ctx, pc); return; } if(tile_id[0] != '#') { /* the tile isn't empty, we must also delete from the images table */ int paramidx = sqlite3_bind_parameter_index(stmt3, ":foobar"); if (paramidx) { sqlite3_bind_text(stmt3, paramidx, tile_id, tile_id_size, SQLITE_STATIC); } ret = sqlite3_step(stmt3); if (ret != SQLITE_DONE && ret != SQLITE_ROW) { ctx->set_error(ctx, 500, "sqlite backend failed on mbtile del 3: %s", sqlite3_errmsg(conn->handle)); sqlite3_reset(stmt1); sqlite3_reset(stmt2); sqlite3_reset(stmt3); mapcache_sqlite_release_conn(ctx, pc); return; } } sqlite3_reset(stmt1); sqlite3_reset(stmt2); sqlite3_reset(stmt3); mapcache_sqlite_release_conn(ctx, pc); } static void _single_mbtile_set(mapcache_context *ctx, mapcache_cache_sqlite *cache, mapcache_tile *tile, struct sqlite_conn *conn) { sqlite3_stmt *stmt1,*stmt2; int ret; if(!tile->raw_image) { tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data); GC_CHECK_ERROR(ctx); } if(mapcache_image_blank_color(tile->raw_image) != MAPCACHE_FALSE) { stmt1 = conn->prepared_statements[MBTILES_SET_EMPTY_TILE_STMT1_IDX]; stmt2 = conn->prepared_statements[MBTILES_SET_EMPTY_TILE_STMT2_IDX]; if(!stmt1) { sqlite3_prepare(conn->handle, "insert or ignore into images(tile_id,tile_data) values (:color,:data);", -1, &conn->prepared_statements[MBTILES_SET_EMPTY_TILE_STMT1_IDX], NULL); sqlite3_prepare(conn->handle, "insert or replace into map(tile_column,tile_row,zoom_level,tile_id) values (:x,:y,:z,:color);", -1, &conn->prepared_statements[MBTILES_SET_EMPTY_TILE_STMT2_IDX], NULL); stmt1 = conn->prepared_statements[MBTILES_SET_EMPTY_TILE_STMT1_IDX]; stmt2 = conn->prepared_statements[MBTILES_SET_EMPTY_TILE_STMT2_IDX]; } cache->bind_stmt(ctx, stmt1, cache, tile); cache->bind_stmt(ctx, stmt2, cache, tile); } else { stmt1 = conn->prepared_statements[MBTILES_SET_TILE_STMT1_IDX]; stmt2 = conn->prepared_statements[MBTILES_SET_TILE_STMT2_IDX]; if(!stmt1) { sqlite3_prepare(conn->handle, "insert or replace into images(tile_id,tile_data) values (:key,:data);", -1, &conn->prepared_statements[MBTILES_SET_TILE_STMT1_IDX], NULL); sqlite3_prepare(conn->handle, "insert or replace into map(tile_column,tile_row,zoom_level,tile_id) values (:x,:y,:z,:key);", -1, &conn->prepared_statements[MBTILES_SET_TILE_STMT2_IDX], NULL); stmt1 = conn->prepared_statements[MBTILES_SET_TILE_STMT1_IDX]; stmt2 = conn->prepared_statements[MBTILES_SET_TILE_STMT2_IDX]; } cache->bind_stmt(ctx, stmt1, cache, tile); cache->bind_stmt(ctx, stmt2, cache, tile); } do { ret = sqlite3_step(stmt1); if (ret != SQLITE_DONE && ret != SQLITE_ROW && ret != SQLITE_BUSY && ret != SQLITE_LOCKED) { ctx->set_error(ctx, 500, "mbtiles backend failed on image set: %s (%d)", sqlite3_errmsg(conn->handle), ret); break; } if (ret == SQLITE_BUSY) { sqlite3_reset(stmt1); } } while (ret == SQLITE_BUSY || ret == SQLITE_LOCKED); if(ret == SQLITE_DONE) { do { ret = sqlite3_step(stmt2); if (ret != SQLITE_DONE && ret != SQLITE_ROW && ret != SQLITE_BUSY && ret != SQLITE_LOCKED) { ctx->set_error(ctx, 500, "mbtiles backend failed on map set: %s (%d)", sqlite3_errmsg(conn->handle), ret); break; } if (ret == SQLITE_BUSY) { sqlite3_reset(stmt2); } } while (ret == SQLITE_BUSY || ret == SQLITE_LOCKED); } sqlite3_reset(stmt1); sqlite3_reset(stmt2); } static int _mapcache_cache_sqlite_get(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*) pcache; struct sqlite_conn *conn; sqlite3_stmt *stmt; int ret; mapcache_pooled_connection *pc = mapcache_sqlite_get_conn(ctx,cache,tile,1); if (GC_HAS_ERROR(ctx)) { if(tile->tileset->read_only || !tile->tileset->source) { mapcache_sqlite_release_conn(ctx, pc); return MAPCACHE_FAILURE; } else { /* not an error in this case, as the db file may not have been created yet */ ctx->clear_errors(ctx); mapcache_sqlite_release_conn(ctx, pc); return MAPCACHE_CACHE_MISS; } } conn = SQLITE_CONN(pc); stmt = conn->prepared_statements[GET_TILE_STMT_IDX]; if(!stmt) { sqlite3_prepare(conn->handle, cache->get_stmt.sql, -1, &conn->prepared_statements[GET_TILE_STMT_IDX], NULL); stmt = conn->prepared_statements[GET_TILE_STMT_IDX]; } cache->bind_stmt(ctx, stmt, cache, tile); do { ret = sqlite3_step(stmt); if (ret != SQLITE_DONE && ret != SQLITE_ROW && ret != SQLITE_BUSY && ret != SQLITE_LOCKED) { ctx->set_error(ctx, 500, "sqlite backend failed on get: %s", sqlite3_errmsg(conn->handle)); sqlite3_reset(stmt); mapcache_sqlite_release_conn(ctx, pc); return MAPCACHE_FAILURE; } } while (ret == SQLITE_BUSY || ret == SQLITE_LOCKED); if (ret == SQLITE_DONE) { sqlite3_reset(stmt); mapcache_sqlite_release_conn(ctx, pc); return MAPCACHE_CACHE_MISS; } else { const void *blob = sqlite3_column_blob(stmt, 0); int size = sqlite3_column_bytes(stmt, 0); if(size>0 && ((char*)blob)[0] == '#') { tile->encoded_data = mapcache_empty_png_decode(ctx,tile->grid_link->grid->tile_sx, tile->grid_link->grid->tile_sy ,blob,&tile->nodata); } else { tile->encoded_data = mapcache_buffer_create(size, ctx->pool); memcpy(tile->encoded_data->buf, blob, size); tile->encoded_data->size = size; } if (sqlite3_column_count(stmt) > 1) { time_t mtime = sqlite3_column_int64(stmt, 1); apr_time_ansi_put(&(tile->mtime), mtime); } sqlite3_reset(stmt); mapcache_sqlite_release_conn(ctx, pc); return MAPCACHE_SUCCESS; } } static void _single_sqlitetile_set(mapcache_context *ctx, mapcache_cache_sqlite *cache, mapcache_tile *tile, struct sqlite_conn *conn) { sqlite3_stmt *stmt = conn->prepared_statements[SQLITE_SET_TILE_STMT_IDX]; int ret; if(!stmt) { sqlite3_prepare(conn->handle, cache->set_stmt.sql, -1, &conn->prepared_statements[SQLITE_SET_TILE_STMT_IDX], NULL); stmt = conn->prepared_statements[SQLITE_SET_TILE_STMT_IDX]; } cache->bind_stmt(ctx, stmt, cache, tile); do { ret = sqlite3_step(stmt); if (ret != SQLITE_DONE && ret != SQLITE_ROW && ret != SQLITE_BUSY && ret != SQLITE_LOCKED) { ctx->set_error(ctx, 500, "sqlite backend failed on set: %s (%d)", sqlite3_errmsg(conn->handle), ret); break; } if (ret == SQLITE_BUSY) { sqlite3_reset(stmt); } } while (ret == SQLITE_BUSY || ret == SQLITE_LOCKED); sqlite3_reset(stmt); } static void _mapcache_cache_sqlite_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*)pcache; struct sqlite_conn *conn; mapcache_pooled_connection *pc = mapcache_sqlite_get_conn(ctx,cache,tile,0); if (GC_HAS_ERROR(ctx)) { mapcache_sqlite_release_conn(ctx, pc); return; } conn = SQLITE_CONN(pc); sqlite3_exec(conn->handle, "BEGIN TRANSACTION", 0, 0, 0); _single_sqlitetile_set(ctx,cache, tile,conn); if (GC_HAS_ERROR(ctx)) { sqlite3_exec(conn->handle, "ROLLBACK TRANSACTION", 0, 0, 0); } else { sqlite3_exec(conn->handle, "END TRANSACTION", 0, 0, 0); } mapcache_sqlite_release_conn(ctx, pc); } static void _mapcache_cache_sqlite_multi_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tiles, int ntiles) { mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*)pcache; int i; struct sqlite_conn *conn; mapcache_pooled_connection *pc = mapcache_sqlite_get_conn(ctx,cache,&tiles[0],0); if (GC_HAS_ERROR(ctx)) { mapcache_sqlite_release_conn(ctx, pc); return; } conn = SQLITE_CONN(pc); sqlite3_exec(conn->handle, "BEGIN TRANSACTION", 0, 0, 0); for (i = 0; i < ntiles; i++) { mapcache_tile *tile = &tiles[i]; _single_sqlitetile_set(ctx,cache, tile,conn); if(GC_HAS_ERROR(ctx)) break; } if (GC_HAS_ERROR(ctx)) { sqlite3_exec(conn->handle, "ROLLBACK TRANSACTION", 0, 0, 0); } else { sqlite3_exec(conn->handle, "END TRANSACTION", 0, 0, 0); } mapcache_sqlite_release_conn(ctx, pc); } static void _mapcache_cache_mbtiles_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*)pcache; struct sqlite_conn *conn; mapcache_pooled_connection *pc = mapcache_sqlite_get_conn(ctx,cache,tile,0); if (GC_HAS_ERROR(ctx)) { mapcache_sqlite_release_conn(ctx, pc); return; } conn = SQLITE_CONN(pc); if(!tile->raw_image) { tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data); if(GC_HAS_ERROR(ctx)) { mapcache_sqlite_release_conn(ctx, pc); return; } } sqlite3_exec(conn->handle, "BEGIN TRANSACTION", 0, 0, 0); _single_mbtile_set(ctx, cache, tile,conn); if (GC_HAS_ERROR(ctx)) { sqlite3_exec(conn->handle, "ROLLBACK TRANSACTION", 0, 0, 0); } else { sqlite3_exec(conn->handle, "END TRANSACTION", 0, 0, 0); } mapcache_sqlite_release_conn(ctx, pc); } static void _mapcache_cache_mbtiles_multi_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tiles, int ntiles) { int i; mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*)pcache; mapcache_pooled_connection *pc; struct sqlite_conn *conn; /* decode/encode image data before going into the sqlite write lock */ for (i = 0; i < ntiles; i++) { mapcache_tile *tile = &tiles[i]; if(!tile->raw_image) { tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data); GC_CHECK_ERROR(ctx); } /* only encode to image format if tile is not blank */ if (mapcache_image_blank_color(tile->raw_image) != MAPCACHE_TRUE && !tile->encoded_data) { tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format); GC_CHECK_ERROR(ctx); } } pc = mapcache_sqlite_get_conn(ctx,cache,&tiles[0],0); if (GC_HAS_ERROR(ctx)) { mapcache_sqlite_release_conn(ctx, pc); return; } conn = SQLITE_CONN(pc); sqlite3_exec(conn->handle, "BEGIN TRANSACTION", 0, 0, 0); for (i = 0; i < ntiles; i++) { mapcache_tile *tile = &tiles[i]; _single_mbtile_set(ctx,cache,tile,conn); if(GC_HAS_ERROR(ctx)) break; } if (GC_HAS_ERROR(ctx)) { sqlite3_exec(conn->handle, "ROLLBACK TRANSACTION", 0, 0, 0); } else { sqlite3_exec(conn->handle, "END TRANSACTION", 0, 0, 0); } mapcache_sqlite_release_conn(ctx, pc); } static void _mapcache_cache_sqlite_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *pcache, mapcache_cfg *config) { ezxml_t cur_node; mapcache_cache_sqlite *cache; sqlite3_initialize(); sqlite3_config(SQLITE_CONFIG_MULTITHREAD); cache = (mapcache_cache_sqlite*) pcache; if ((cur_node = ezxml_child(node, "base")) != NULL) { ctx->set_error(ctx, 500, "sqlite config not supported anymore, use "); return; } if ((cur_node = ezxml_child(node, "dbname_template")) != NULL) { ctx->set_error(ctx, 500, "sqlite config not supported anymore, use a \"multi-sqlite3\" cache type"); return; } if ((cur_node = ezxml_child(node, "dbfile")) != NULL) { char *fmt; cache->dbfile = apr_pstrdup(ctx->pool, cur_node->txt); fmt = (char*)ezxml_attr(cur_node,"x_fmt"); if(fmt && *fmt) { cache->x_fmt = apr_pstrdup(ctx->pool,fmt); } fmt = (char*)ezxml_attr(cur_node,"y_fmt"); if(fmt && *fmt) { cache->y_fmt = apr_pstrdup(ctx->pool,fmt); } fmt = (char*)ezxml_attr(cur_node,"z_fmt"); if(fmt && *fmt) { cache->z_fmt = apr_pstrdup(ctx->pool,fmt); } fmt = (char*)ezxml_attr(cur_node,"inv_x_fmt"); if(fmt && *fmt) { cache->inv_x_fmt = apr_pstrdup(ctx->pool,fmt); } fmt = (char*)ezxml_attr(cur_node,"inv_y_fmt"); if(fmt && *fmt) { cache->inv_y_fmt = apr_pstrdup(ctx->pool,fmt); } fmt = (char*)ezxml_attr(cur_node,"div_x_fmt"); if(fmt && *fmt) { cache->div_x_fmt = apr_pstrdup(ctx->pool,fmt); } fmt = (char*)ezxml_attr(cur_node,"div_y_fmt"); if(fmt && *fmt) { cache->div_y_fmt = apr_pstrdup(ctx->pool,fmt); } fmt = (char*)ezxml_attr(cur_node,"inv_div_x_fmt"); if(fmt && *fmt) { cache->inv_div_x_fmt = apr_pstrdup(ctx->pool,fmt); } fmt = (char*)ezxml_attr(cur_node,"inv_div_y_fmt"); if(fmt && *fmt) { cache->inv_div_y_fmt = apr_pstrdup(ctx->pool,fmt); } } cache->detect_blank = 0; if ((cur_node = ezxml_child(node, "detect_blank")) != NULL) { if(strcasecmp(cur_node->txt,"false")) { cache->detect_blank = 1; } } if ((cur_node = ezxml_child(node, "hitstats")) != NULL) { if (!strcasecmp(cur_node->txt, "true")) { ctx->set_error(ctx, 500, "sqlite config not supported anymore"); } } if ((cur_node = ezxml_child(node, "pragma")) != NULL) { cache->pragmas = apr_table_make(ctx->pool,1); while(cur_node) { char *name = (char*)ezxml_attr(cur_node,"name"); if(!name || !cur_node->txt || !strlen(cur_node->txt)) { ctx->set_error(ctx,500," missing name attribute"); return; } apr_table_set(cache->pragmas,name,cur_node->txt); cur_node = cur_node->next; } } if ((cur_node = ezxml_child(node, "queries")) != NULL) { ezxml_t query_node; if ((query_node = ezxml_child(cur_node, "exists")) != NULL) { cache->exists_stmt.sql = apr_pstrdup(ctx->pool,query_node->txt); } if ((query_node = ezxml_child(cur_node, "get")) != NULL) { cache->get_stmt.sql = apr_pstrdup(ctx->pool,query_node->txt); } if ((query_node = ezxml_child(cur_node, "set")) != NULL) { cache->set_stmt.sql = apr_pstrdup(ctx->pool,query_node->txt); } if ((query_node = ezxml_child(cur_node, "delete")) != NULL) { cache->delete_stmt.sql = apr_pstrdup(ctx->pool,query_node->txt); } if ((query_node = ezxml_child(cur_node, "create")) != NULL) { cache->create_stmt.sql = apr_pstrdup(ctx->pool,query_node->txt); } } cur_node = ezxml_child(node,"xcount"); if(cur_node && cur_node->txt && *cur_node->txt) { char *endptr; cache->count_x = (int)strtol(cur_node->txt,&endptr,10); if(*endptr != 0) { ctx->set_error(ctx,400,"failed to parse xcount value %s for sqlite cache %s", cur_node->txt,cache->cache.name); return; } } cur_node = ezxml_child(node,"ycount"); if(cur_node && cur_node->txt && *cur_node->txt) { char *endptr; cache->count_y = (int)strtol(cur_node->txt,&endptr,10); if(*endptr != 0) { ctx->set_error(ctx,400,"failed to parse ycount value %s for sqlite cache %s", cur_node->txt,cache->cache.name); return; } } } /** * \private \memberof mapcache_cache_sqlite */ static void _mapcache_cache_sqlite_configuration_post_config(mapcache_context *ctx, mapcache_cache *pcache, mapcache_cfg *cfg) { mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*)pcache; if (!cache->dbfile) { ctx->set_error(ctx, 500, "sqlite cache \"%s\" is missing entry", pcache->name); return; } } /** * \private \memberof mapcache_cache_sqlite */ static void _mapcache_cache_mbtiles_configuration_post_config(mapcache_context *ctx, mapcache_cache *pcache, mapcache_cfg *cfg) { /* check that only one tileset/grid references this cache, as mbtiles does not support multiple tilesets/grids per cache */ #ifdef FIXME int nrefs = 0; apr_hash_index_t *tileseti = apr_hash_first(ctx->pool,cfg->tilesets); while(tileseti) { mapcache_tileset *tileset; const void *key; apr_ssize_t keylen; apr_hash_this(tileseti,&key,&keylen,(void**)&tileset); if(tileset->cache == cache) { nrefs++; if(nrefs>1) { ctx->set_error(ctx,500,"mbtiles cache %s is referenced by more than 1 tileset, which is not supported",cache->name); return; } if(tileset->grid_links->nelts > 1) { ctx->set_error(ctx,500,"mbtiles cache %s is referenced by tileset %s which has more than 1 grid, which is not supported",cache->name,tileset->name); return; } } tileseti = apr_hash_next(tileseti); } #endif } mapcache_cache* mapcache_cache_sqlite_create(mapcache_context *ctx) { mapcache_cache_sqlite *cache = apr_pcalloc(ctx->pool, sizeof (mapcache_cache_sqlite)); if (!cache) { ctx->set_error(ctx, 500, "failed to allocate sqlite cache"); return NULL; } 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.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, "create table if not exists tiles(tileset text, grid text, x integer, y integer, z integer, data blob, dim text, ctime datetime, primary key(tileset,grid,x,y,z,dim))"); cache->exists_stmt.sql = apr_pstrdup(ctx->pool, "select 1 from tiles where x=:x and y=:y and z=:z and dim=:dim and tileset=:tileset and grid=:grid"); cache->get_stmt.sql = apr_pstrdup(ctx->pool, "select data,strftime(\"%s\",ctime) from tiles where tileset=:tileset and grid=:grid and x=:x and y=:y and z=:z and dim=:dim"); cache->set_stmt.sql = apr_pstrdup(ctx->pool, "insert or replace into tiles(tileset,grid,x,y,z,data,dim,ctime) values (:tileset,:grid,:x,:y,:z,:data,:dim,datetime('now'))"); cache->delete_stmt.sql = apr_pstrdup(ctx->pool, "delete from tiles where x=:x and y=:y and z=:z and dim=:dim and tileset=:tileset and grid=:grid"); cache->n_prepared_statements = 4; cache->bind_stmt = _bind_sqlite_params; cache->detect_blank = 1; cache->x_fmt = cache->y_fmt = cache->z_fmt = cache->inv_x_fmt = cache->inv_y_fmt = cache->div_x_fmt = cache->div_y_fmt = cache->inv_div_x_fmt = cache->inv_div_y_fmt = apr_pstrdup(ctx->pool,"%d"); cache->count_x = cache->count_y = -1; return (mapcache_cache*)cache; } /** * \brief creates and initializes a mapcache_sqlite_cache */ mapcache_cache* mapcache_cache_mbtiles_create(mapcache_context *ctx) { mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*) mapcache_cache_sqlite_create(ctx); if (!cache) { 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->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));"\ "create table if not exists metadata(name text, value text);"\ "create view if not exists tiles AS SELECT map.zoom_level AS zoom_level, map.tile_column AS tile_column, map.tile_row AS tile_row, images.tile_data AS tile_data FROM map JOIN images ON images.tile_id = map.tile_id;" ); cache->exists_stmt.sql = apr_pstrdup(ctx->pool, "select 1 from tiles where tile_column=:x and tile_row=:y and zoom_level=:z"); cache->get_stmt.sql = apr_pstrdup(ctx->pool, "select tile_data from tiles where tile_column=:x and tile_row=:y and zoom_level=:z"); cache->delete_stmt.sql = apr_pstrdup(ctx->pool, "delete from tiles where tile_column=:x and tile_row=:y and zoom_level=:z"); cache->n_prepared_statements = 9; cache->bind_stmt = _bind_mbtiles_params; return (mapcache_cache*) cache; } #endif /* vim: ts=2 sts=2 et sw=2 */ mapcache-1.4.0/lib/cache_tiff.c000066400000000000000000000750731255567662100162750ustar00rootroot00000000000000/****************************************************************************** * $Id$ * * Project: MapServer * Purpose: MapCache tile caching: tiled tiff filesytem cache backend. * Author: Thomas Bonfort, Frank Warmerdam and the MapServer team. * ****************************************************************************** * Copyright (c) 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-config.h" #ifdef USE_TIFF #include "mapcache.h" #include #include #include #include #include #include #include #ifdef USE_GEOTIFF #include "xtiffio.h" #include "geovalues.h" #include "geotiff.h" #include "geo_normalize.h" #include "geo_tiffp.h" #include "geo_keyp.h" #define MyTIFFOpen XTIFFOpen #define MyTIFFClose XTIFFClose #else #define MyTIFFOpen TIFFOpen #define MyTIFFClose TIFFClose #endif /** * \brief return filename for given tile * * \param tile the tile to get the key from * \param path pointer to a char* that will contain the filename * \param r * \private \memberof mapcache_cache_tiff */ static void _mapcache_cache_tiff_tile_key(mapcache_context *ctx, mapcache_cache_tiff *cache, mapcache_tile *tile, char **path) { *path = cache->filename_template; /* * generic template substitutions */ if(strstr(*path,"{tileset}")) *path = mapcache_util_str_replace(ctx->pool,*path, "{tileset}", tile->tileset->name); if(strstr(*path,"{grid}")) *path = mapcache_util_str_replace(ctx->pool,*path, "{grid}", tile->grid_link->grid->name); if(tile->dimensions && strstr(*path,"{dim}")) { 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)); const char *dimval = mapcache_util_str_sanitize(ctx->pool,entry->val,"/.",'#'); dimstring = apr_pstrcat(ctx->pool,dimstring,"#",dimval,NULL); } *path = mapcache_util_str_replace(ctx->pool,*path, "{dim}", dimstring); } while(strstr(*path,"{z}")) *path = mapcache_util_str_replace(ctx->pool,*path, "{z}", apr_psprintf(ctx->pool,cache->z_fmt,tile->z)); /* * x and y replacing, when the tiff files are numbered with an increasing * x,y scheme (adjacent tiffs have x-x'=1 or y-y'=1 */ while(strstr(*path,"{div_x}")) *path = mapcache_util_str_replace(ctx->pool,*path, "{div_x}", apr_psprintf(ctx->pool,cache->div_x_fmt,tile->x/cache->count_x)); while(strstr(*path,"{div_y}")) *path = mapcache_util_str_replace(ctx->pool,*path, "{div_y}", apr_psprintf(ctx->pool,cache->div_y_fmt,tile->y/cache->count_y)); while(strstr(*path,"{inv_div_y}")) *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_div_y}", apr_psprintf(ctx->pool,cache->inv_div_y_fmt,(tile->grid_link->grid->levels[tile->z]->maxy - tile->y - 1)/cache->count_y)); while(strstr(*path,"{inv_div_x}")) *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_div_x}", apr_psprintf(ctx->pool,cache->inv_div_x_fmt,(tile->grid_link->grid->levels[tile->z]->maxx - tile->x - 1)/cache->count_x)); /* * x and y replacing, when the tiff files are numbered with the index * of their bottom-left tile * adjacent tiffs have x-x'=count_x or y-y'=count_y */ while(strstr(*path,"{x}")) *path = mapcache_util_str_replace(ctx->pool,*path, "{x}", apr_psprintf(ctx->pool,cache->x_fmt,tile->x/cache->count_x*cache->count_x)); while(strstr(*path,"{y}")) *path = mapcache_util_str_replace(ctx->pool,*path, "{y}", apr_psprintf(ctx->pool,cache->y_fmt,tile->y/cache->count_y*cache->count_y)); while(strstr(*path,"{inv_y}")) *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_y}", apr_psprintf(ctx->pool,cache->inv_y_fmt,(tile->grid_link->grid->levels[tile->z]->maxy - tile->y - 1)/cache->count_y*cache->count_y)); while(strstr(*path,"{inv_x}")) *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_x}", apr_psprintf(ctx->pool,cache->inv_x_fmt,(tile->grid_link->grid->levels[tile->z]->maxx - tile->x - 1)/cache->count_x*cache->count_y)); if(!*path) { ctx->set_error(ctx,500, "failed to allocate tile key"); } } #ifdef DEBUG static void check_tiff_format(mapcache_context *ctx, mapcache_cache_tiff *cache, mapcache_tile *tile, TIFF *hTIFF, const char *filename) { uint32 imwidth,imheight,tilewidth,tileheight; int16 planarconfig,orientation; uint16 compression; uint16 photometric; int rv; mapcache_grid_level *level; int ntilesx; int ntilesy; TIFFGetField( hTIFF, TIFFTAG_IMAGEWIDTH, &imwidth ); TIFFGetField( hTIFF, TIFFTAG_IMAGELENGTH, &imheight ); TIFFGetField( hTIFF, TIFFTAG_TILEWIDTH, &tilewidth ); TIFFGetField( hTIFF, TIFFTAG_TILELENGTH, &tileheight ); /* Test that the TIFF is tiled and not stripped */ if(!TIFFIsTiled(hTIFF)) { ctx->set_error(ctx,500,"TIFF file \"%s\" is not tiled", filename); return; } /* check we have jpeg compression */ rv = TIFFGetField( hTIFF, TIFFTAG_COMPRESSION, &compression ); if(rv == 1 && compression != COMPRESSION_JPEG) { ctx->set_error(ctx,500,"TIFF file \"%s\" is not jpeg compressed", filename); return; } /* tiff must be pixel interleaved, not with a single image per band */ rv = TIFFGetField( hTIFF, TIFFTAG_PLANARCONFIG, &planarconfig ); if(rv == 1 && planarconfig != PLANARCONFIG_CONTIG) { ctx->set_error(ctx,500,"TIFF file \"%s\" is not pixel interleaved", filename); return; } /* is this test needed once we now we have JPEG ? */ rv = TIFFGetField( hTIFF, TIFFTAG_PHOTOMETRIC, &photometric ); if(rv == 1 && (photometric != PHOTOMETRIC_RGB && photometric != PHOTOMETRIC_YCBCR)) { ctx->set_error(ctx,500,"TIFF file \"%s\" is not RGB: %d", filename); return; } /* the default is top-left, but check just in case */ rv = TIFFGetField( hTIFF, TIFFTAG_ORIENTATION, &orientation ); if(rv == 1 && orientation != ORIENTATION_TOPLEFT) { ctx->set_error(ctx,500,"TIFF file \"%s\" is not top-left oriented", filename); return; } /* check that the tiff internal tiling aligns with the mapcache_grid we are using: * - the tiff tile size must match the grid tile size * - the number of tiles in each direction in the tiff must match what has been * configured for the cache */ level = tile->grid_link->grid->levels[tile->z]; ntilesx = MAPCACHE_MIN(cache->count_x, level->maxx); ntilesy = MAPCACHE_MIN(cache->count_y, level->maxy); if( tilewidth != tile->grid_link->grid->tile_sx || tileheight != tile->grid_link->grid->tile_sy || imwidth != tile->grid_link->grid->tile_sx * ntilesx || imheight != tile->grid_link->grid->tile_sy * ntilesy ) { ctx->set_error(ctx,500,"TIFF file %s imagesize (%d,%d) and tilesize (%d,%d).\ Expected (%d,%d),(%d,%d)",filename,imwidth,imheight,tilewidth,tileheight, tile->grid_link->grid->tile_sx * ntilesx, tile->grid_link->grid->tile_sy * ntilesy, tile->grid_link->grid->tile_sx, tile->grid_link->grid->tile_sy); return; } /* TODO: more tests ? */ /* ok, success */ } #endif static int _mapcache_cache_tiff_has_tile(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { char *filename; TIFF *hTIFF; mapcache_cache_tiff *cache = (mapcache_cache_tiff*)pcache; _mapcache_cache_tiff_tile_key(ctx, cache, tile, &filename); if(GC_HAS_ERROR(ctx)) { return MAPCACHE_FALSE; } hTIFF = MyTIFFOpen(filename,"r"); if(hTIFF) { do { uint32 nSubType = 0; int tiff_offx, tiff_offy; /* the x and y offset of the tile inside the tiff image */ int tiff_off; /* the index of the tile inside the list of tiles of the tiff image */ mapcache_grid_level *level; int ntilesx; int ntilesy; toff_t *offsets=NULL, *sizes=NULL; if( !TIFFGetField(hTIFF, TIFFTAG_SUBFILETYPE, &nSubType) ) nSubType = 0; /* skip overviews and masks */ if( (nSubType & FILETYPE_REDUCEDIMAGE) || (nSubType & FILETYPE_MASK) ) continue; #ifdef DEBUG check_tiff_format(ctx,cache,tile,hTIFF,filename); if(GC_HAS_ERROR(ctx)) { MyTIFFClose(hTIFF); return MAPCACHE_FALSE; } #endif level = tile->grid_link->grid->levels[tile->z]; ntilesx = MAPCACHE_MIN(cache->count_x, level->maxx); ntilesy = MAPCACHE_MIN(cache->count_y, level->maxy); /* x offset of the tile along a row */ tiff_offx = tile->x % ntilesx; /* * y offset of the requested row. we inverse it as the rows are ordered * from top to bottom, whereas the tile y is bottom to top */ tiff_offy = ntilesy - (tile->y % ntilesy) -1; tiff_off = tiff_offy * ntilesx + tiff_offx; if(1 != TIFFGetField( hTIFF, TIFFTAG_TILEOFFSETS, &offsets )) { MyTIFFClose(hTIFF); return MAPCACHE_FALSE; } if(1 != TIFFGetField( hTIFF, TIFFTAG_TILEBYTECOUNTS, &sizes )) { MyTIFFClose(hTIFF); return MAPCACHE_FALSE; } MyTIFFClose(hTIFF); if( offsets[tiff_off] > 0 && sizes[tiff_off] > 0 ) { return MAPCACHE_TRUE; } else { return MAPCACHE_FALSE; } } while( TIFFReadDirectory( hTIFF ) ); return MAPCACHE_FALSE; /* TIFF only contains overviews ? */ } else return MAPCACHE_FALSE; } static void _mapcache_cache_tiff_delete(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { ctx->set_error(ctx,500,"TIFF cache tile deleting not implemented"); } /** * \brief get file content of given tile * * fills the mapcache_tile::data of the given tile with content stored in the file * \private \memberof mapcache_cache_tiff * \sa mapcache_cache::tile_get() */ static int _mapcache_cache_tiff_get(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { char *filename; TIFF *hTIFF = NULL; int rv; mapcache_cache_tiff *cache = (mapcache_cache_tiff*)pcache; _mapcache_cache_tiff_tile_key(ctx, cache, tile, &filename); if(GC_HAS_ERROR(ctx)) { return MAPCACHE_FALSE; } #ifdef DEBUG ctx->log(ctx,MAPCACHE_DEBUG,"tile (%d,%d,%d) => filename %s)", tile->x,tile->y,tile->z,filename); #endif hTIFF = MyTIFFOpen(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...) * * we ignore this case here and hope that further parts of the code will be * able to detect what's happening more precisely */ if(hTIFF) { do { uint32 nSubType = 0; int tiff_offx, tiff_offy; /* the x and y offset of the tile inside the tiff image */ int tiff_off; /* the index of the tile inside the list of tiles of the tiff image */ mapcache_grid_level *level; int ntilesx; int ntilesy; toff_t *offsets=NULL, *sizes=NULL; if( !TIFFGetField(hTIFF, TIFFTAG_SUBFILETYPE, &nSubType) ) nSubType = 0; /* skip overviews */ if( nSubType & FILETYPE_REDUCEDIMAGE ) continue; #ifdef DEBUG check_tiff_format(ctx,cache,tile,hTIFF,filename); if(GC_HAS_ERROR(ctx)) { MyTIFFClose(hTIFF); return MAPCACHE_FAILURE; } #endif /* * compute the width and height of the full tiff file. This * is not simply the tile size times the number of tiles per * file for lower zoom levels */ level = tile->grid_link->grid->levels[tile->z]; ntilesx = MAPCACHE_MIN(cache->count_x, level->maxx); ntilesy = MAPCACHE_MIN(cache->count_y, level->maxy); /* x offset of the tile along a row */ tiff_offx = tile->x % ntilesx; /* * y offset of the requested row. we inverse it as the rows are ordered * from top to bottom, whereas the tile y is bottom to top */ tiff_offy = ntilesy - (tile->y % ntilesy) -1; tiff_off = tiff_offy * ntilesx + tiff_offx; /* get the offset of the jpeg data from the start of the file for each tile */ rv = TIFFGetField( hTIFF, TIFFTAG_TILEOFFSETS, &offsets ); if( rv != 1 ) { ctx->set_error(ctx,500,"Failed to read TIFF file \"%s\" tile offsets", filename); MyTIFFClose(hTIFF); return MAPCACHE_FAILURE; } /* get the size of the jpeg data for each tile */ rv = TIFFGetField( hTIFF, TIFFTAG_TILEBYTECOUNTS, &sizes ); if( rv != 1 ) { ctx->set_error(ctx,500,"Failed to read TIFF file \"%s\" tile sizes", filename); MyTIFFClose(hTIFF); return MAPCACHE_FAILURE; } /* * the tile data exists for the given tiff_off if both offsets and size * 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 ) { apr_file_t *f; apr_finfo_t finfo; apr_status_t ret; /* read the jpeg header (common to all tiles) */ uint32 jpegtable_size = 0; unsigned char* jpegtable_ptr; rv = TIFFGetField( hTIFF, TIFFTAG_JPEGTABLES, &jpegtable_size, &jpegtable_ptr ); if( rv != 1 || !jpegtable_ptr || !jpegtable_size) { /* 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); return MAPCACHE_FAILURE; } /* * open the tiff file directly to access the jpeg image data with the given * offset */ if((ret=apr_file_open(&f, filename, APR_FOPEN_READ|APR_FOPEN_BUFFERED|APR_FOPEN_BINARY,APR_OS_DEFAULT, ctx->pool)) == APR_SUCCESS) { char *bufptr; apr_off_t off; apr_size_t bytes; ret = apr_file_info_get(&finfo, APR_FINFO_MTIME, f); if(ret == APR_SUCCESS) { /* * 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 = finfo.mtime; } /* 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 = tile->encoded_data->buf + (jpegtable_size-2); /* go to the specified offset in the tiff file, plus 2 bytes */ off = offsets[tiff_off]+2; apr_file_seek(f,APR_SET,&off); /* * copy the jpeg body at the end of the memory buffer, accounting * for the two bytes we omitted in the previous step */ bytes = sizes[tiff_off]-2; apr_file_read(f,bufptr,&bytes); /* check we have correctly read the requested number of bytes */ 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); MyTIFFClose(hTIFF); return MAPCACHE_FAILURE; } tile->encoded_data->size = (jpegtable_size+sizes[tiff_off]-4); /* finalize and cleanup */ apr_file_close(f); MyTIFFClose(hTIFF); return MAPCACHE_SUCCESS; } else { /* shouldn't usually happen. we managed to open the file with TIFFOpen, * but apr_file_open failed to do so. * nothing much to do except bail out. */ ctx->set_error(ctx,500,"apr_file_open failed on already open tiff file \"%s\", giving up .... ", filename); MyTIFFClose(hTIFF); return MAPCACHE_FAILURE; } } else { /* sparse tiff file without the requested tile */ MyTIFFClose(hTIFF); return MAPCACHE_CACHE_MISS; } } /* loop through the tiff directories if there are multiple ones */ while( TIFFReadDirectory( hTIFF ) ); /* * should not happen? * finished looping through directories and didn't find anything suitable. * does the file only contain overviews? */ MyTIFFClose(hTIFF); return MAPCACHE_CACHE_MISS; } /* failed to open tiff file */ return MAPCACHE_CACHE_MISS; } /** * \brief write tile data to tiff * * writes the content of mapcache_tile::data to tiff. * \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 tiff * \private \memberof mapcache_cache_tiff * \sa mapcache_cache::tile_set() */ static void _mapcache_cache_tiff_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { #ifdef USE_TIFF_WRITE char *filename; TIFF *hTIFF = NULL; int rv; void *lock; int create; char errmsg[120]; mapcache_cache_tiff *cache; mapcache_image_format_jpeg *format; char *hackptr1,*hackptr2; int tilew; int tileh; unsigned char *rgb; int r,c; apr_finfo_t finfo; mapcache_grid_level *level; int ntilesx; int ntilesy; int tiff_offx, tiff_offy; /* the x and y offset of the tile inside the tiff image */ int tiff_off; /* the index of the tile inside the list of tiles of the tiff image */ cache = (mapcache_cache_tiff*)pcache; _mapcache_cache_tiff_tile_key(ctx, cache, tile, &filename); format = (mapcache_image_format_jpeg*) cache->format; if(GC_HAS_ERROR(ctx)) { return; } #ifdef DEBUG ctx->log(ctx,MAPCACHE_DEBUG,"tile write (%d,%d,%d) => filename %s)", tile->x,tile->y,tile->z,filename); #endif /* * create the directory where the tiff file will be stored */ /* find the location of the last '/' in the string */ hackptr2 = hackptr1 = filename; while(*hackptr1) { if(*hackptr1 == '/') hackptr2 = hackptr1; hackptr1++; } *hackptr2 = '\0'; if(APR_SUCCESS != (rv = apr_dir_make_recursive(filename,APR_OS_DEFAULT,ctx->pool))) { /* * apr_dir_make_recursive sometimes sends back this error, although it should not. * ignore this one */ if(!APR_STATUS_IS_EEXIST(rv)) { ctx->set_error(ctx, 500, "failed to create directory %s: %s",filename, apr_strerror(rv,errmsg,120)); return; } } *hackptr2 = '/'; tilew = tile->grid_link->grid->tile_sx; tileh = tile->grid_link->grid->tile_sy; if(!tile->raw_image) { tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data); GC_CHECK_ERROR(ctx); } /* remap xrgb to rgb */ rgb = (unsigned char*)malloc(tilew*tileh*3); for(r=0; rraw_image->h; r++) { unsigned char *imptr = tile->raw_image->data + r * tile->raw_image->stride; unsigned char *rgbptr = rgb + r * tilew * 3; for(c=0; craw_image->w; c++) { rgbptr[0] = imptr[2]; rgbptr[1] = imptr[1]; rgbptr[2] = imptr[0]; rgbptr += 3; imptr += 4; } } /* * aquire a lock on the tiff file. */ while(mapcache_lock_or_wait_for_resource(ctx,(cache->locker?cache->locker:ctx->config->locker),filename, &lock) == MAPCACHE_FALSE); /* 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+"); create = 0; } else { hTIFF = MyTIFFOpen(filename,"w+"); create = 1; } if(!hTIFF) { ctx->set_error(ctx,500,"failed to open/create tiff file %s\n",filename); goto close_tiff; } /* * compute the width and height of the full tiff file. This * is not simply the tile size times the number of tiles per * file for lower zoom levels */ level = tile->grid_link->grid->levels[tile->z]; ntilesx = MAPCACHE_MIN(cache->count_x, level->maxx); ntilesy = MAPCACHE_MIN(cache->count_y, level->maxy); if(create) { #ifdef USE_GEOTIFF double adfPixelScale[3], adfTiePoints[6]; mapcache_extent bbox; GTIF *gtif; int x,y; #endif /* populate the TIFF tags if we are creating the file */ TIFFSetField( hTIFF, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT ); TIFFSetField( hTIFF, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG ); TIFFSetField( hTIFF, TIFFTAG_BITSPERSAMPLE, 8 ); TIFFSetField( hTIFF, TIFFTAG_COMPRESSION, COMPRESSION_JPEG ); TIFFSetField( hTIFF, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB ); TIFFSetField( hTIFF, TIFFTAG_TILEWIDTH, tilew ); TIFFSetField( hTIFF, TIFFTAG_TILELENGTH, tileh ); TIFFSetField( hTIFF, TIFFTAG_IMAGEWIDTH, ntilesx * tilew ); TIFFSetField( hTIFF, TIFFTAG_IMAGELENGTH, ntilesy * tileh ); TIFFSetField( hTIFF, TIFFTAG_SAMPLESPERPIXEL,3 ); #ifdef USE_GEOTIFF gtif = GTIFNew(hTIFF); if(gtif) { GTIFKeySet(gtif, GTRasterTypeGeoKey, TYPE_SHORT, 1, RasterPixelIsArea); GTIFKeySet( gtif, GeographicTypeGeoKey, TYPE_SHORT, 1, 0 ); GTIFKeySet( gtif, GeogGeodeticDatumGeoKey, TYPE_SHORT, 1, 0 ); GTIFKeySet( gtif, GeogEllipsoidGeoKey, TYPE_SHORT, 1, 0 ); GTIFKeySet( gtif, GeogSemiMajorAxisGeoKey, TYPE_DOUBLE, 1, 0.0 ); GTIFKeySet( gtif, GeogSemiMinorAxisGeoKey, TYPE_DOUBLE, 1, 0.0 ); switch(tile->grid_link->grid->unit) { case MAPCACHE_UNIT_FEET: GTIFKeySet( gtif, ProjLinearUnitsGeoKey, TYPE_SHORT, 1, Linear_Foot ); break; case MAPCACHE_UNIT_METERS: GTIFKeySet( gtif, ProjLinearUnitsGeoKey, TYPE_SHORT, 1, Linear_Meter ); break; case MAPCACHE_UNIT_DEGREES: GTIFKeySet(gtif, GeogAngularUnitsGeoKey, TYPE_SHORT, 0, Angular_Degree ); break; default: break; } GTIFWriteKeys(gtif); GTIFFree(gtif); adfPixelScale[0] = adfPixelScale[1] = level->resolution; adfPixelScale[2] = 0.0; TIFFSetField( hTIFF, TIFFTAG_GEOPIXELSCALE, 3, adfPixelScale ); /* top left tile x,y */ 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, x,y,tile->z,&bbox); adfTiePoints[0] = 0.0; adfTiePoints[1] = 0.0; adfTiePoints[2] = 0.0; adfTiePoints[3] = bbox.minx; adfTiePoints[4] = bbox.maxy; adfTiePoints[5] = 0.0; TIFFSetField( hTIFF, TIFFTAG_GEOTIEPOINTS, 6, adfTiePoints ); } #endif } TIFFSetField(hTIFF, TIFFTAG_JPEGQUALITY, format->quality); if(format->photometric == MAPCACHE_PHOTOMETRIC_RGB) { TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); } else { TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_YCBCR); } TIFFSetField( hTIFF, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB ); /* x offset of the tile along a row */ tiff_offx = tile->x % ntilesx; /* * y offset of the requested row. we inverse it as the rows are ordered * from top to bottom, whereas the tile y is bottom to top */ tiff_offy = ntilesy - (tile->y % ntilesy) -1; tiff_off = tiff_offy * ntilesx + tiff_offx; rv = TIFFWriteEncodedTile(hTIFF, tiff_off, rgb, tilew*tileh*3); free(rgb); if(!rv) { ctx->set_error(ctx,500,"failed TIFFWriteEncodedTile to %s",filename); goto close_tiff; } rv = TIFFWriteCheck( hTIFF, 1, "cache_set()"); if(!rv) { ctx->set_error(ctx,500,"failed TIFFWriteCheck %s",filename); goto close_tiff; } if(create) { rv = TIFFWriteDirectory(hTIFF); if(!rv) { ctx->set_error(ctx,500,"failed TIFFWriteDirectory to %s",filename); goto close_tiff; } } close_tiff: if(hTIFF) MyTIFFClose(hTIFF); mapcache_unlock_resource(ctx,cache->locker?cache->locker:ctx->config->locker,filename, lock); #else ctx->set_error(ctx,500,"tiff write support disabled by default"); #endif } /** * \private \memberof mapcache_cache_tiff */ static void _mapcache_cache_tiff_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *pcache, mapcache_cfg *config) { ezxml_t cur_node; mapcache_cache_tiff *cache = (mapcache_cache_tiff*)pcache; char * format_name; mapcache_image_format *pformat; if ((cur_node = ezxml_child(node,"template")) != NULL) { char *fmt; cache->filename_template = apr_pstrdup(ctx->pool,cur_node->txt); fmt = (char*)ezxml_attr(cur_node,"x_fmt"); if(fmt && *fmt) { cache->x_fmt = apr_pstrdup(ctx->pool,fmt); } fmt = (char*)ezxml_attr(cur_node,"y_fmt"); if(fmt && *fmt) { cache->y_fmt = apr_pstrdup(ctx->pool,fmt); } fmt = (char*)ezxml_attr(cur_node,"z_fmt"); if(fmt && *fmt) { cache->z_fmt = apr_pstrdup(ctx->pool,fmt); } fmt = (char*)ezxml_attr(cur_node,"inv_x_fmt"); if(fmt && *fmt) { cache->inv_x_fmt = apr_pstrdup(ctx->pool,fmt); } fmt = (char*)ezxml_attr(cur_node,"inv_y_fmt"); if(fmt && *fmt) { cache->inv_y_fmt = apr_pstrdup(ctx->pool,fmt); } fmt = (char*)ezxml_attr(cur_node,"div_x_fmt"); if(fmt && *fmt) { cache->div_x_fmt = apr_pstrdup(ctx->pool,fmt); } fmt = (char*)ezxml_attr(cur_node,"div_y_fmt"); if(fmt && *fmt) { cache->div_y_fmt = apr_pstrdup(ctx->pool,fmt); } fmt = (char*)ezxml_attr(cur_node,"inv_div_x_fmt"); if(fmt && *fmt) { cache->inv_div_x_fmt = apr_pstrdup(ctx->pool,fmt); } fmt = (char*)ezxml_attr(cur_node,"inv_div_y_fmt"); if(fmt && *fmt) { cache->inv_div_y_fmt = apr_pstrdup(ctx->pool,fmt); } } cur_node = ezxml_child(node,"xcount"); if(cur_node && cur_node->txt && *cur_node->txt) { char *endptr; cache->count_x = (int)strtol(cur_node->txt,&endptr,10); if(*endptr != 0) { ctx->set_error(ctx,400,"failed to parse xcount value %s for tiff cache %s", cur_node->txt,pcache->name); return; } } cur_node = ezxml_child(node,"ycount"); if(cur_node && cur_node->txt && *cur_node->txt) { char *endptr; cache->count_y = (int)strtol(cur_node->txt,&endptr,10); if(*endptr != 0) { ctx->set_error(ctx,400,"failed to parse ycount value %s for tiff cache %s", cur_node->txt,pcache->name); return; } } cur_node = ezxml_child(node,"format"); if(cur_node && cur_node->txt && *cur_node->txt) { format_name = cur_node->txt; } else { format_name = "JPEG"; } pformat = mapcache_configuration_get_image_format( config,format_name); if(!pformat) { ctx->set_error(ctx,500,"TIFF cache %s references unknown image format %s", pcache->name, format_name); return; } if(pformat->type != GC_JPEG) { ctx->set_error(ctx,500,"TIFF cache %s can only reference a JPEG image format", pcache->name); return; } cache->format = (mapcache_image_format_jpeg*)pformat; cur_node = ezxml_child(node,"locker"); if(cur_node) { mapcache_config_parse_locker(ctx, cur_node, &cache->locker); } } /** * \private \memberof mapcache_cache_tiff */ static void _mapcache_cache_tiff_configuration_post_config(mapcache_context *ctx, mapcache_cache *pcache, mapcache_cfg *cfg) { mapcache_cache_tiff *cache = (mapcache_cache_tiff*)pcache; /* check all required parameters are configured */ if((!cache->filename_template || !strlen(cache->filename_template))) { ctx->set_error(ctx, 400, "tiff cache %s has no template pattern",cache->cache.name); return; } if(cache->count_x <= 0 || cache->count_y <= 0) { ctx->set_error(ctx, 400, "tiff cache %s has invalid count (%d,%d)",cache->count_x,cache->count_y); return; } } /** * \brief creates and initializes a mapcache_tiff_cache */ mapcache_cache* mapcache_cache_tiff_create(mapcache_context *ctx) { mapcache_cache_tiff *cache = apr_pcalloc(ctx->pool,sizeof(mapcache_cache_tiff)); if(!cache) { ctx->set_error(ctx, 500, "failed to allocate tiff cache"); return NULL; } 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.configuration_post_config = _mapcache_cache_tiff_configuration_post_config; cache->cache.configuration_parse_xml = _mapcache_cache_tiff_configuration_parse_xml; cache->count_x = 10; cache->count_y = 10; cache->x_fmt = cache->y_fmt = cache->z_fmt = cache->inv_x_fmt = cache->inv_y_fmt = cache->div_x_fmt = cache->div_y_fmt = cache->inv_div_x_fmt = cache->inv_div_y_fmt = apr_pstrdup(ctx->pool,"%d"); #ifndef DEBUG TIFFSetWarningHandler(NULL); TIFFSetErrorHandler(NULL); #endif return (mapcache_cache*)cache; } #endif /* vim: ts=2 sts=2 et sw=2 */ mapcache-1.4.0/lib/cache_tokyocabinet.c000066400000000000000000000176231255567662100200350ustar00rootroot00000000000000/****************************************************************************** * $Id$ * * Project: MapServer * Purpose: MapCache tile caching support file: Tokyo Cabinet cache backend * 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. *****************************************************************************/ #ifdef USE_TC #include "mapcache.h" #include #include #include #include #include #include #include #include #ifndef _WIN32 #include #endif struct tc_conn { TCBDB *bdb; int readonly; }; static struct tc_conn _tc_get_conn(mapcache_context *ctx, mapcache_tile* tile, int readonly) { struct tc_conn conn; /* create the object */ conn.bdb = tcbdbnew(); mapcache_cache_tc *cache = (mapcache_cache_tc*)tile->tileset->cache; /* open the database */ if(!readonly) { if(!tcbdbopen(conn.bdb, apr_pstrcat(ctx->pool, cache->basedir,"/tc.tcb",NULL), BDBOWRITER | BDBOCREAT)) { int ecode = tcbdbecode(conn.bdb); ctx->set_error(ctx,500, "tokyocabinet open error on %s: %s\n",apr_pstrcat(ctx->pool, cache->basedir,"/tc.tcf",NULL),tcbdberrmsg(ecode)); } conn.readonly = 0; } else { if(!tcbdbopen(conn.bdb, apr_pstrcat(ctx->pool, cache->basedir,"/tc.tcb",NULL), BDBOREADER)) { if(!tcbdbopen(conn.bdb, apr_pstrcat(ctx->pool, cache->basedir,"/tc.tcb",NULL), BDBOWRITER | BDBOCREAT)) { int ecode = tcbdbecode(conn.bdb); ctx->set_error(ctx,500, "tokyocabinet open error on %s: %s\n",apr_pstrcat(ctx->pool, cache->basedir,"/tc.tcf",NULL),tcbdberrmsg(ecode)); } conn.readonly = 0; } conn.readonly = 1; } return conn; } static void _tc_release_conn(mapcache_context *ctx, mapcache_tile *tile, struct tc_conn conn) { if(!conn.readonly) tcbdbsync(conn.bdb); if(!tcbdbclose(conn.bdb)) { int ecode = tcbdbecode(conn.bdb); ctx->set_error(ctx,500, "tokyocabinet close error: %s\n",tcbdberrmsg(ecode)); } tcbdbdel(conn.bdb); } static int _mapcache_cache_tc_has_tile(mapcache_context *ctx, mapcache_tile *tile) { int ret; struct tc_conn conn; int nrecords = 0; mapcache_cache_tc *cache = (mapcache_cache_tc*)tile->tileset->cache; char *skey = mapcache_util_get_tile_key(ctx,tile,cache->key_template,NULL,NULL); conn = _tc_get_conn(ctx,tile,1); if(GC_HAS_ERROR(ctx)) return MAPCACHE_FALSE; nrecords = tcbdbvnum2(conn.bdb, skey); if(nrecords == 0) ret = MAPCACHE_FALSE; else ret = MAPCACHE_TRUE; _tc_release_conn(ctx,tile,conn); return ret; } static void _mapcache_cache_tc_delete(mapcache_context *ctx, mapcache_tile *tile) { struct tc_conn conn; mapcache_cache_tc *cache = (mapcache_cache_tc*)tile->tileset->cache; char *skey = mapcache_util_get_tile_key(ctx,tile,cache->key_template,NULL,NULL); conn = _tc_get_conn(ctx,tile,0); GC_CHECK_ERROR(ctx); tcbdbout2(conn.bdb, skey); _tc_release_conn(ctx,tile,conn); } static int _mapcache_cache_tc_get(mapcache_context *ctx, mapcache_tile *tile) { int ret; struct tc_conn conn; mapcache_cache_tc *cache = (mapcache_cache_tc*)tile->tileset->cache; char *skey = mapcache_util_get_tile_key(ctx,tile,cache->key_template,NULL,NULL); conn = _tc_get_conn(ctx,tile,1); int size; if(GC_HAS_ERROR(ctx)) return MAPCACHE_FAILURE; tile->encoded_data = mapcache_buffer_create(0,ctx->pool); tile->encoded_data->buf = tcbdbget(conn.bdb, skey, strlen(skey), &size); if(tile->encoded_data->buf) { tile->encoded_data->avail = size; tile->encoded_data->size = size - sizeof(apr_time_t); apr_pool_cleanup_register(ctx->pool, tile->encoded_data->buf,(void*)free, apr_pool_cleanup_null); tile->mtime = *((apr_time_t*)(&tile->encoded_data->buf[tile->encoded_data->size])); ret = MAPCACHE_SUCCESS; } else { ret = MAPCACHE_CACHE_MISS; } _tc_release_conn(ctx,tile,conn); return ret; } static void _mapcache_cache_tc_set(mapcache_context *ctx, mapcache_tile *tile) { struct tc_conn conn; mapcache_cache_tc *cache = (mapcache_cache_tc*)tile->tileset->cache; char *skey = mapcache_util_get_tile_key(ctx,tile,cache->key_template,NULL,NULL); apr_time_t now = apr_time_now(); conn = _tc_get_conn(ctx,tile,0); 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); } mapcache_buffer_append(tile->encoded_data,sizeof(apr_time_t),&now); if(!tcbdbput(conn.bdb, skey, strlen(skey), tile->encoded_data->buf, tile->encoded_data->size)) { int ecode = tcbdbecode(conn.bdb); ctx->set_error(ctx,500, "tokyocabinet put error: %s\n",tcbdberrmsg(ecode)); } _tc_release_conn(ctx,tile,conn); } static void _mapcache_cache_tc_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *cache, mapcache_cfg *config) { ezxml_t cur_node; mapcache_cache_tc *dcache = (mapcache_cache_tc*)cache; if ((cur_node = ezxml_child(node,"base")) != NULL) { dcache->basedir = apr_pstrdup(ctx->pool,cur_node->txt); } if ((cur_node = ezxml_child(node,"key_template")) != NULL) { dcache->key_template = apr_pstrdup(ctx->pool,cur_node->txt); } else { dcache->key_template = apr_pstrdup(ctx->pool,"{tileset}-{grid}-{dim}-{z}-{y}-{x}.{ext}"); } if(!dcache->basedir) { ctx->set_error(ctx,500,"tokyocabinet cache \"%s\" is missing entry",cache->name); return; } } static void _mapcache_cache_tc_configuration_post_config(mapcache_context *ctx, mapcache_cache *cache, mapcache_cfg *cfg) { mapcache_cache_tc *dcache = (mapcache_cache_tc*)cache; apr_status_t rv; apr_dir_t *dir; rv = apr_dir_open(&dir, dcache->basedir, ctx->pool); if(rv != APR_SUCCESS) { char errmsg[120]; ctx->set_error(ctx,500,"bdb failed to open directory %s:%s",dcache->basedir,apr_strerror(rv,errmsg,120)); } } /** * \brief creates and initializes a mapcache_dbd_cache */ mapcache_cache* mapcache_cache_tc_create(mapcache_context *ctx) { mapcache_cache_tc *cache = apr_pcalloc(ctx->pool,sizeof(mapcache_cache_tc)); if(!cache) { ctx->set_error(ctx, 500, "failed to allocate tokyo cabinet cache"); return NULL; } cache->cache.metadata = apr_table_make(ctx->pool,3); cache->cache.type = MAPCACHE_CACHE_TC; cache->cache.tile_delete = _mapcache_cache_tc_delete; cache->cache.tile_get = _mapcache_cache_tc_get; cache->cache.tile_exists = _mapcache_cache_tc_has_tile; cache->cache.tile_set = _mapcache_cache_tc_set; cache->cache.configuration_post_config = _mapcache_cache_tc_configuration_post_config; cache->cache.configuration_parse_xml = _mapcache_cache_tc_configuration_parse_xml; cache->basedir = NULL; cache->key_template = NULL; return (mapcache_cache*)cache; } #endif /* vim: ts=2 sts=2 et sw=2 */ mapcache-1.4.0/lib/configuration.c000066400000000000000000000251601255567662100170610ustar00rootroot00000000000000/****************************************************************************** * $Id$ * * Project: MapServer * Purpose: MapCache tile caching support file: high level configuration file parser * 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 #include #include #include void mapcache_configuration_parse(mapcache_context *ctx, const char *filename, mapcache_cfg *config, int cgi) { char *url; mapcache_configuration_parse_xml(ctx,filename,config); GC_CHECK_ERROR(ctx); /* if we were suppplied with an onlineresource, make sure it ends with a / */ if(NULL != (url = (char*)apr_table_get(config->metadata,"url"))) { char *urlend = url + strlen(url)-1; if(*urlend != '/') { url = apr_pstrcat(ctx->pool,url,"/",NULL); apr_table_setn(config->metadata,"url",url); } } } void mapcache_configuration_post_config(mapcache_context *ctx, mapcache_cfg *config) { apr_hash_index_t *cachei = apr_hash_first(ctx->pool,config->caches); while(cachei) { mapcache_cache *cache; const void *key; apr_ssize_t keylen; apr_hash_this(cachei,&key,&keylen,(void**)&cache); cache->configuration_post_config(ctx,cache,config); GC_CHECK_ERROR(ctx); cachei = apr_hash_next(cachei); } } mapcache_cfg* mapcache_configuration_create(apr_pool_t *pool) { mapcache_grid *grid; int i; double wgs84_resolutions[18]= { 0.703125000000000, 0.351562500000000, 0.175781250000000, 8.78906250000000e-2, 4.39453125000000e-2, 2.19726562500000e-2, 1.09863281250000e-2, 5.49316406250000e-3, 2.74658203125000e-3, 1.37329101562500e-3, 6.86645507812500e-4, 3.43322753906250e-4, 1.71661376953125e-4, 8.58306884765625e-5, 4.29153442382812e-5, 2.14576721191406e-5, 1.07288360595703e-5, 5.36441802978516e-6 }; double google_resolutions[19] = { 156543.0339280410, 78271.51696402048, 39135.75848201023, 19567.87924100512, 9783.939620502561, 4891.969810251280, 2445.984905125640, 1222.992452562820, 611.4962262814100, 305.7481131407048, 152.8740565703525, 76.43702828517624, 38.21851414258813, 19.10925707129406, 9.554628535647032, 4.777314267823516, 2.388657133911758, 1.194328566955879, 0.5971642834779395 }; mapcache_extent wgs84_extent= {-180,-90,180,90}; mapcache_extent google_extent= {-20037508.3427892480,-20037508.3427892480,20037508.3427892480,20037508.3427892480}; double unitwidth,unitheight; mapcache_cfg *cfg = (mapcache_cfg*)apr_pcalloc(pool, sizeof(mapcache_cfg)); cfg->caches = apr_hash_make(pool); cfg->sources = apr_hash_make(pool); cfg->tilesets = apr_hash_make(pool); cfg->grids = apr_hash_make(pool); cfg->image_formats = apr_hash_make(pool); cfg->metadata = apr_table_make(pool,3); mapcache_configuration_add_image_format(cfg, mapcache_imageio_create_png_format(pool,"PNG",MAPCACHE_COMPRESSION_FAST), "PNG"); mapcache_configuration_add_image_format(cfg, 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), "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")), "mixed"); cfg->default_image_format = mapcache_configuration_get_image_format(cfg,"mixed"); cfg->reporting = MAPCACHE_REPORT_MSG; grid = mapcache_grid_create(pool); grid->name = apr_pstrdup(pool,"WGS84"); apr_table_add(grid->metadata,"title","GoogleCRS84Quad"); apr_table_add(grid->metadata,"wellKnownScaleSet","urn:ogc:def:wkss:OGC:1.0:GoogleCRS84Quad"); apr_table_add(grid->metadata,"profile","global-geodetic"); grid->srs = apr_pstrdup(pool,"EPSG:4326"); grid->unit = MAPCACHE_UNIT_DEGREES; grid->tile_sx = grid->tile_sy = 256; grid->nlevels = 18; grid->extent = wgs84_extent; grid->levels = (mapcache_grid_level**)apr_pcalloc(pool, grid->nlevels*sizeof(mapcache_grid_level*)); for(i=0; inlevels; i++) { mapcache_grid_level *level = (mapcache_grid_level*)apr_pcalloc(pool,sizeof(mapcache_grid_level)); level->resolution = wgs84_resolutions[i]; unitheight = grid->tile_sy * level->resolution; unitwidth = grid->tile_sx * level->resolution; level->maxy = ceil((grid->extent.maxy-grid->extent.miny - 0.01* unitheight)/unitheight); level->maxx = ceil((grid->extent.maxx-grid->extent.minx - 0.01* unitwidth)/unitwidth); grid->levels[i] = level; } mapcache_configuration_add_grid(cfg,grid,"WGS84"); grid = mapcache_grid_create(pool); grid->name = apr_pstrdup(pool,"GoogleMapsCompatible"); grid->srs = apr_pstrdup(pool,"EPSG:3857"); APR_ARRAY_PUSH(grid->srs_aliases,char*) = apr_pstrdup(pool,"EPSG:900913"); apr_table_add(grid->metadata,"title","GoogleMapsCompatible"); apr_table_add(grid->metadata,"profile","global-mercator"); apr_table_add(grid->metadata,"wellKnownScaleSet","urn:ogc:def:wkss:OGC:1.0:GoogleMapsCompatible"); grid->tile_sx = grid->tile_sy = 256; grid->nlevels = 19; grid->unit = MAPCACHE_UNIT_METERS; grid->extent = google_extent; grid->levels = (mapcache_grid_level**)apr_pcalloc(pool, grid->nlevels*sizeof(mapcache_grid_level*)); for(i=0; inlevels; i++) { mapcache_grid_level *level = (mapcache_grid_level*)apr_pcalloc(pool,sizeof(mapcache_grid_level)); level->resolution = google_resolutions[i]; unitheight = grid->tile_sy * level->resolution; unitwidth = grid->tile_sx * level->resolution; level->maxy = ceil((grid->extent.maxy-grid->extent.miny - 0.01* unitheight)/unitheight); level->maxx = ceil((grid->extent.maxx-grid->extent.minx - 0.01* unitwidth)/unitwidth); grid->levels[i] = level; } mapcache_configuration_add_grid(cfg,grid,"GoogleMapsCompatible"); grid = mapcache_grid_create(pool); grid->name = apr_pstrdup(pool,"g"); grid->srs = apr_pstrdup(pool,"EPSG:900913"); APR_ARRAY_PUSH(grid->srs_aliases,char*) = apr_pstrdup(pool,"EPSG:3857"); apr_table_add(grid->metadata,"title","GoogleMapsCompatible"); apr_table_add(grid->metadata,"profile","global-mercator"); apr_table_add(grid->metadata,"wellKnownScaleSet","urn:ogc:def:wkss:OGC:1.0:GoogleMapsCompatible"); grid->tile_sx = grid->tile_sy = 256; grid->nlevels = 19; grid->unit = MAPCACHE_UNIT_METERS; grid->extent = google_extent; grid->levels = (mapcache_grid_level**)apr_pcalloc(pool, grid->nlevels*sizeof(mapcache_grid_level*)); for(i=0; inlevels; i++) { mapcache_grid_level *level = (mapcache_grid_level*)apr_pcalloc(pool,sizeof(mapcache_grid_level)); level->resolution = google_resolutions[i]; unitheight = grid->tile_sy * level->resolution; unitwidth = grid->tile_sx * level->resolution; level->maxy = ceil((grid->extent.maxy-grid->extent.miny - 0.01* unitheight)/unitheight); level->maxx = ceil((grid->extent.maxx-grid->extent.minx - 0.01* unitwidth)/unitwidth); grid->levels[i] = level; } mapcache_configuration_add_grid(cfg,grid,"g"); cfg->loglevel = MAPCACHE_WARN; cfg->autoreload = 0; return cfg; } mapcache_source *mapcache_configuration_get_source(mapcache_cfg *config, const char *key) { return (mapcache_source*)apr_hash_get(config->sources, (void*)key, APR_HASH_KEY_STRING); } mapcache_cache *mapcache_configuration_get_cache(mapcache_cfg *config, const char *key) { return (mapcache_cache*)apr_hash_get(config->caches, (void*)key, APR_HASH_KEY_STRING); } mapcache_grid *mapcache_configuration_get_grid(mapcache_cfg *config, const char *key) { return (mapcache_grid*)apr_hash_get(config->grids, (void*)key, APR_HASH_KEY_STRING); } mapcache_tileset *mapcache_configuration_get_tileset(mapcache_cfg *config, const char *key) { if(config->mode == MAPCACHE_MODE_NORMAL) { return (mapcache_tileset*)apr_hash_get(config->tilesets, (void*)key, APR_HASH_KEY_STRING); } else { return (mapcache_tileset*)apr_hash_get(config->tilesets, (void*)"mirror", APR_HASH_KEY_STRING); } } mapcache_image_format *mapcache_configuration_get_image_format(mapcache_cfg *config, const char *key) { return (mapcache_image_format*)apr_hash_get(config->image_formats, (void*)key, APR_HASH_KEY_STRING); } void mapcache_configuration_add_source(mapcache_cfg *config, mapcache_source *source, const char * key) { apr_hash_set(config->sources, key, APR_HASH_KEY_STRING, (void*)source); } void mapcache_configuration_add_grid(mapcache_cfg *config, mapcache_grid *grid, const char * key) { apr_hash_set(config->grids, key, APR_HASH_KEY_STRING, (void*)grid); } void mapcache_configuration_add_tileset(mapcache_cfg *config, mapcache_tileset *tileset, const char * key) { tileset->config = config; apr_hash_set(config->tilesets, key, APR_HASH_KEY_STRING, (void*)tileset); } void mapcache_configuration_add_cache(mapcache_cfg *config, mapcache_cache *cache, const char * key) { apr_hash_set(config->caches, key, APR_HASH_KEY_STRING, (void*)cache); } void mapcache_configuration_add_image_format(mapcache_cfg *config, mapcache_image_format *format, const char * key) { apr_hash_set(config->image_formats, key, APR_HASH_KEY_STRING, (void*)format); } mapcache-1.4.0/lib/configuration_xml.c000066400000000000000000001330161255567662100177410ustar00rootroot00000000000000/****************************************************************************** * $Id$ * * Project: MapServer * Purpose: MapCache tile caching support file: xml configuration parser * 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 #include #include #include #include #include #include #include void parseMetadata(mapcache_context *ctx, ezxml_t node, apr_table_t *metadata) { ezxml_t cur_node; for(cur_node = node->child; cur_node; cur_node = cur_node->sibling) { apr_table_add(metadata,cur_node->name, cur_node->txt); } } 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 ", 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 . Expecting one of (sqlite)",attr,tileset->name); return; } } else { ctx->set_error(ctx,400,"missing \"type\" attribute for %s's ",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 %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; apr_array_header_t *dimensions = apr_array_make(ctx->pool,1,sizeof(mapcache_dimension*)); for(dimension_node = ezxml_child(node,"dimension"); dimension_node; dimension_node = dimension_node->next) { char *name = (char*)ezxml_attr(dimension_node,"name"); 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; if(!name || !strlen(name)) { ctx->set_error(ctx, 400, "mandatory attribute \"name\" not found in "); return; } if(type && *type) { if(!strcmp(type,"values")) { dimension = mapcache_dimension_values_create(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); } else if(!strcmp(type,"sqlite")) { dimension = mapcache_dimension_sqlite_create(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); } else { ctx->set_error(ctx,400,"unknown dimension type \"%s\"",type); return; } } else { ctx->set_error(ctx,400, "mandatory attribute \"type\" not found in "); return; } dimension->name = apr_pstrdup(ctx->pool,name); if(unit && *unit) { 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); APR_ARRAY_PUSH(dimensions,mapcache_dimension*) = dimension; } if(apr_is_empty_array(dimensions)) { ctx->set_error(ctx, 400, " for tileset \"%s\" has no dimensions defined (expecting children)",tileset->name); return; } tileset->dimensions = dimensions; } void parseGrid(mapcache_context *ctx, ezxml_t node, mapcache_cfg *config) { char *name; mapcache_extent extent = {0,0,0,0}; mapcache_grid *grid; ezxml_t cur_node; char *value; name = (char*)ezxml_attr(node,"name"); if(!name || !strlen(name)) { ctx->set_error(ctx, 400, "mandatory attribute \"name\" not found in "); return; } else { name = apr_pstrdup(ctx->pool, name); /* check we don't already have a grid defined with this name */ if(mapcache_configuration_get_grid(config, name)) { ctx->set_error(ctx, 400, "duplicate grid with name \"%s\"",name); return; } } grid = mapcache_grid_create(ctx->pool); grid->name = name; if ((cur_node = ezxml_child(node,"extent")) != NULL) { double *values; int nvalues; value = apr_pstrdup(ctx->pool,cur_node->txt); if(MAPCACHE_SUCCESS != mapcache_util_extract_double_list(ctx, value, NULL, &values, &nvalues) || nvalues != 4) { ctx->set_error(ctx, 400, "failed to parse extent array %s." "(expecting 4 space separated numbers, got %d (%f %f %f %f)" "eg -180 -90 180 90", value,nvalues,values[0],values[1],values[2],values[3]); return; } extent.minx = values[0]; extent.miny = values[1]; extent.maxx = values[2]; extent.maxy = values[3]; } if ((cur_node = ezxml_child(node,"metadata")) != NULL) { parseMetadata(ctx, cur_node, grid->metadata); GC_CHECK_ERROR(ctx); } if ((cur_node = ezxml_child(node,"units")) != NULL) { if(!strcasecmp(cur_node->txt,"dd")) { grid->unit = MAPCACHE_UNIT_DEGREES; } else if(!strcasecmp(cur_node->txt,"m")) { grid->unit = MAPCACHE_UNIT_METERS; } else if(!strcasecmp(cur_node->txt,"ft")) { grid->unit = MAPCACHE_UNIT_FEET; } else { ctx->set_error(ctx, 400, "unknown unit %s for grid %s (valid values are \"dd\", \"m\", and \"ft\"", cur_node->txt, grid->name); return; } } if ((cur_node = ezxml_child(node,"srs")) != NULL) { grid->srs = apr_pstrdup(ctx->pool,cur_node->txt); } for(cur_node = ezxml_child(node,"srsalias"); cur_node; cur_node = cur_node->next) { value = apr_pstrdup(ctx->pool,cur_node->txt); APR_ARRAY_PUSH(grid->srs_aliases,char*) = value; } if ((cur_node = ezxml_child(node,"origin")) != NULL) { if(!strcasecmp(cur_node->txt,"top-left")) { grid->origin = MAPCACHE_GRID_ORIGIN_TOP_LEFT; } else if(!strcasecmp(cur_node->txt,"bottom-left")) { grid->origin = MAPCACHE_GRID_ORIGIN_BOTTOM_LEFT; } else if(!strcasecmp(cur_node->txt,"top-right")) { grid->origin = MAPCACHE_GRID_ORIGIN_TOP_RIGHT; } else if(!strcasecmp(cur_node->txt,"bottom-right")) { grid->origin = MAPCACHE_GRID_ORIGIN_BOTTOM_RIGHT; } else { ctx->set_error(ctx, 400, "unknown origin %s for grid %s (valid values are \"top-left\", \"bottom-left\", \"top-right\" and \"bottom-right\"", cur_node->txt, grid->name); return; } if(grid->origin == MAPCACHE_GRID_ORIGIN_BOTTOM_RIGHT || grid->origin == MAPCACHE_GRID_ORIGIN_TOP_RIGHT) { ctx->set_error(ctx,500,"grid origin %s not implemented",cur_node->txt); return; } } if ((cur_node = ezxml_child(node,"size")) != NULL) { int *sizes, nsizes; value = apr_pstrdup(ctx->pool,cur_node->txt); if(MAPCACHE_SUCCESS != mapcache_util_extract_int_list(ctx, value, NULL, &sizes, &nsizes) || nsizes != 2) { ctx->set_error(ctx, 400, "failed to parse size array %s in grid %s" "(expecting two space separated integers, eg 256 256", value, grid->name); return; } grid->tile_sx = sizes[0]; grid->tile_sy = sizes[1]; } if ((cur_node = ezxml_child(node,"resolutions")) != NULL) { int nvalues; double *values; value = apr_pstrdup(ctx->pool,cur_node->txt); if(MAPCACHE_SUCCESS != mapcache_util_extract_double_list(ctx, value, NULL, &values, &nvalues) || !nvalues) { ctx->set_error(ctx, 400, "failed to parse resolutions array %s." "(expecting space separated numbers, " "eg 1 2 4 8 16 32", value); return; } grid->nlevels = nvalues; grid->levels = (mapcache_grid_level**)apr_pcalloc(ctx->pool, grid->nlevels*sizeof(mapcache_grid_level)); while(nvalues--) { double unitheight; double unitwidth; mapcache_grid_level *level = (mapcache_grid_level*)apr_pcalloc(ctx->pool,sizeof(mapcache_grid_level)); level->resolution = values[nvalues]; unitheight = grid->tile_sy * level->resolution; unitwidth = grid->tile_sx * level->resolution; level->maxy = ceil((extent.maxy-extent.miny - 0.01* unitheight)/unitheight); level->maxx = ceil((extent.maxx-extent.minx - 0.01* unitwidth)/unitwidth); grid->levels[nvalues] = level; } } if(grid->srs == NULL) { ctx->set_error(ctx, 400, "grid \"%s\" has no srs configured." " You must add a tag.", grid->name); return; } if(extent.minx >= extent.maxx || extent.miny >= extent.maxy) { ctx->set_error(ctx, 400, "grid \"%s\" has no (or invalid) extent configured" " You must add/correct a tag.", grid->name); return; } else { grid->extent = extent; } if(grid->tile_sx <= 0 || grid->tile_sy <= 0) { ctx->set_error(ctx, 400, "grid \"%s\" has no (or invalid) tile size configured" " You must add/correct a tag.", grid->name); return; } if(!grid->nlevels) { ctx->set_error(ctx, 400, "grid \"%s\" has no resolutions configured." " You must add a tag.", grid->name); return; } mapcache_configuration_add_grid(config,grid,name); } void parseSource(mapcache_context *ctx, ezxml_t node, mapcache_cfg *config) { ezxml_t cur_node; char *name = NULL, *type = NULL; mapcache_source *source; name = (char*)ezxml_attr(node,"name"); type = (char*)ezxml_attr(node,"type"); if(!name || !strlen(name)) { ctx->set_error(ctx, 400, "mandatory attribute \"name\" not found in "); return; } else { name = apr_pstrdup(ctx->pool, name); /* check we don't already have a source defined with this name */ if(mapcache_configuration_get_source(config, name)) { ctx->set_error(ctx, 400, "duplicate source with name \"%s\"",name); return; } } if(!type || !strlen(type)) { ctx->set_error(ctx, 400, "mandatory attribute \"type\" not found in "); return; } 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 { ctx->set_error(ctx, 400, "unknown source type %s for source \"%s\"", type, name); return; } if(source == NULL) { ctx->set_error(ctx, 400, "failed to parse source \"%s\"", name); return; } source->name = name; if ((cur_node = ezxml_child(node,"metadata")) != NULL) { parseMetadata(ctx, cur_node, source->metadata); GC_CHECK_ERROR(ctx); } source->configuration_parse_xml(ctx,node,source); GC_CHECK_ERROR(ctx); source->configuration_check(ctx,config,source); GC_CHECK_ERROR(ctx); mapcache_configuration_add_source(config,source,name); } void parseFormat(mapcache_context *ctx, ezxml_t node, mapcache_cfg *config) { char *name = NULL, *type = NULL; mapcache_image_format *format = NULL; ezxml_t cur_node; name = (char*)ezxml_attr(node,"name"); type = (char*)ezxml_attr(node,"type"); if(!name || !strlen(name)) { ctx->set_error(ctx, 400, "mandatory attribute \"name\" not found in "); return; } name = apr_pstrdup(ctx->pool, name); if(!type || !strlen(type)) { ctx->set_error(ctx, 400, "mandatory attribute \"type\" not found in "); return; } if(!strcmp(type,"PNG")) { int colors = -1; mapcache_compression_type compression = MAPCACHE_COMPRESSION_DEFAULT; if ((cur_node = ezxml_child(node,"compression")) != NULL) { if(!strcmp(cur_node->txt, "fast")) { compression = MAPCACHE_COMPRESSION_FAST; } else if(!strcmp(cur_node->txt, "best")) { compression = MAPCACHE_COMPRESSION_BEST; } else if(!strcmp(cur_node->txt, "none")) { compression = MAPCACHE_COMPRESSION_DISABLE; } else { ctx->set_error(ctx, 400, "unknown compression type %s for format \"%s\"", cur_node->txt, name); return; } } if ((cur_node = ezxml_child(node,"colors")) != NULL) { char *endptr; colors = (int)strtol(cur_node->txt,&endptr,10); if(*endptr != 0 || colors < 2 || colors > 256) { ctx->set_error(ctx, 400, "failed to parse colors \"%s\" for format \"%s\"" "(expecting an integer between 2 and 256 " "eg 256", cur_node->txt,name); return; } } if(colors == -1) { format = mapcache_imageio_create_png_format(ctx->pool, name,compression); } else { format = mapcache_imageio_create_png_q_format(ctx->pool, name,compression, colors); } } else if(!strcmp(type,"JPEG")) { int quality = 95; mapcache_photometric photometric = MAPCACHE_PHOTOMETRIC_YCBCR; if ((cur_node = ezxml_child(node,"quality")) != NULL) { char *endptr; quality = (int)strtol(cur_node->txt,&endptr,10); if(*endptr != 0 || quality < 1 || quality > 100) { ctx->set_error(ctx, 400, "failed to parse quality \"%s\" for format \"%s\"" "(expecting an integer between 1 and 100 " "eg 90", cur_node->txt,name); return; } } if ((cur_node = ezxml_child(node,"photometric")) != NULL) { if(!strcasecmp(cur_node->txt,"RGB")) photometric = MAPCACHE_PHOTOMETRIC_RGB; else if(!strcasecmp(cur_node->txt,"YCBCR")) photometric = MAPCACHE_PHOTOMETRIC_YCBCR; else { ctx->set_error(ctx,500,"failed to parse jpeg format %s photometric %s. expecting rgb or ycbcr", name,cur_node->txt); return; } } format = mapcache_imageio_create_jpeg_format(ctx->pool, name,quality,photometric); } else if(!strcasecmp(type,"MIXED")) { mapcache_image_format *transparent=NULL, *opaque=NULL; if ((cur_node = ezxml_child(node,"transparent")) != NULL) { transparent = mapcache_configuration_get_image_format(config,cur_node->txt); } if(!transparent) { ctx->set_error(ctx,400, "mixed format %s references unknown transparent format %s" "(order is important, format %s should appear first)", name,cur_node->txt,cur_node->txt); return; } if ((cur_node = ezxml_child(node,"opaque")) != NULL) { opaque = mapcache_configuration_get_image_format(config,cur_node->txt); } if(!opaque) { ctx->set_error(ctx,400, "mixed format %s references unknown opaque format %s" "(order is important, format %s should appear first)", name,cur_node->txt,cur_node->txt); return; } format = mapcache_imageio_create_mixed_format(ctx->pool,name,transparent, opaque); } else { ctx->set_error(ctx, 400, "unknown format type %s for format \"%s\"", type, name); return; } if(format == NULL) { ctx->set_error(ctx, 400, "failed to parse format \"%s\"", name); return; } mapcache_configuration_add_image_format(config,format,name); return; } void parseCache(mapcache_context *ctx, ezxml_t node, mapcache_cfg *config) { char *name = NULL, *type = NULL; mapcache_cache *cache = NULL; name = (char*)ezxml_attr(node,"name"); type = (char*)ezxml_attr(node,"type"); if(!name || !strlen(name)) { ctx->set_error(ctx, 400, "mandatory attribute \"name\" not found in "); return; } else { name = apr_pstrdup(ctx->pool, name); /* check we don't already have a cache defined with this name */ if(mapcache_configuration_get_cache(config, name)) { ctx->set_error(ctx, 400, "duplicate cache with name \"%s\"",name); return; } } if(!type || !strlen(type)) { ctx->set_error(ctx, 400, "mandatory attribute \"type\" not found in "); return; } if(!strcmp(type,"disk")) { cache = mapcache_cache_disk_create(ctx); } else if(!strcmp(type,"fallback")) { cache = mapcache_cache_fallback_create(ctx); } else if(!strcmp(type,"multitier")) { cache = mapcache_cache_multitier_create(ctx); } else if(!strcmp(type,"composite")) { cache = mapcache_cache_composite_create(ctx); } else if(!strcmp(type,"rest")) { cache = mapcache_cache_rest_create(ctx); } else if(!strcmp(type,"s3")) { cache = mapcache_cache_s3_create(ctx); } else if(!strcmp(type,"azure")) { cache = mapcache_cache_azure_create(ctx); } 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; } if(cache == NULL) { ctx->set_error(ctx, 400, "failed to parse cache \"%s\"", name); return; } cache->name = name; cache->configuration_parse_xml(ctx,node,cache,config); GC_CHECK_ERROR(ctx); mapcache_configuration_add_cache(config,cache,name); return; } void parseTileset(mapcache_context *ctx, ezxml_t node, mapcache_cfg *config) { char *name = NULL; mapcache_tileset *tileset = NULL; ezxml_t cur_node; char* value; int havewgs84bbox=0; if(config->mode == MAPCACHE_MODE_NORMAL) { name = (char*)ezxml_attr(node,"name"); } else { name = "mirror"; } if(!name || !strlen(name)) { ctx->set_error(ctx, 400, "mandatory attribute \"name\" not found in "); return; } else { name = apr_pstrdup(ctx->pool, name); /* check we don't already have a cache defined with this name */ if(mapcache_configuration_get_tileset(config, name)) { ctx->set_error(ctx, 400, "duplicate tileset with name \"%s\"",name); return; } } tileset = mapcache_tileset_create(ctx); tileset->name = name; if ((cur_node = ezxml_child(node,"read-only")) != NULL) { if(cur_node->txt && !strcmp(cur_node->txt,"true")) tileset->read_only = 1; } if ((cur_node = ezxml_child(node,"metadata")) != NULL) { parseMetadata(ctx, cur_node, tileset->metadata); GC_CHECK_ERROR(ctx); } if ((value = (char*)apr_table_get(tileset->metadata,"wgs84boundingbox")) != NULL) { double *values; int nvalues; value = apr_pstrdup(ctx->pool,value); if(MAPCACHE_SUCCESS != mapcache_util_extract_double_list(ctx, value, NULL, &values, &nvalues) || nvalues != 4) { ctx->set_error(ctx, 400, "failed to parse extent array %s." "(expecting 4 space separated numbers, got %d (%f %f %f %f)" "eg -180 -90 180 90", value,nvalues,values[0],values[1],values[2],values[3]); return; } tileset->wgs84bbox.minx = values[0]; tileset->wgs84bbox.miny = values[1]; tileset->wgs84bbox.maxx = values[2]; tileset->wgs84bbox.maxy = values[3]; havewgs84bbox = 1; } for(cur_node = ezxml_child(node,"grid"); cur_node; cur_node = cur_node->next) { mapcache_grid *grid; mapcache_grid_link *gridlink; char *restrictedExtent = NULL, *sTolerance = NULL; mapcache_extent *extent; int tolerance; if (tileset->grid_links == NULL) { tileset->grid_links = apr_array_make(ctx->pool,1,sizeof(mapcache_grid_link*)); } grid = mapcache_configuration_get_grid(config, cur_node->txt); if(!grid) { ctx->set_error(ctx, 400, "tileset \"%s\" references grid \"%s\"," " but it is not configured", name, cur_node->txt); return; } gridlink = apr_pcalloc(ctx->pool,sizeof(mapcache_grid_link)); gridlink->grid = grid; gridlink->minz = 0; gridlink->maxz = grid->nlevels; gridlink->grid_limits = (mapcache_extent_i*)apr_pcalloc(ctx->pool,grid->nlevels*sizeof(mapcache_extent_i)); gridlink->outofzoom_strategy = MAPCACHE_OUTOFZOOM_NOTCONFIGURED; gridlink->intermediate_grids = apr_array_make(ctx->pool,1,sizeof(mapcache_grid_link*)); restrictedExtent = (char*)ezxml_attr(cur_node,"restricted_extent"); if(restrictedExtent) { int nvalues; double *values; restrictedExtent = apr_pstrdup(ctx->pool,restrictedExtent); if(MAPCACHE_SUCCESS != mapcache_util_extract_double_list(ctx, restrictedExtent, NULL, &values, &nvalues) || nvalues != 4) { ctx->set_error(ctx, 400, "failed to parse extent array %s." "(expecting 4 space separated numbers, " "eg foo", restrictedExtent); return; } gridlink->restricted_extent = (mapcache_extent*) apr_pcalloc(ctx->pool, sizeof(mapcache_extent)); gridlink->restricted_extent->minx = values[0]; gridlink->restricted_extent->miny = values[1]; gridlink->restricted_extent->maxx = values[2]; gridlink->restricted_extent->maxy = values[3]; extent = gridlink->restricted_extent; } else { extent = &grid->extent; } tolerance = 5; sTolerance = (char*)ezxml_attr(cur_node,"tolerance"); if(sTolerance) { char *endptr; tolerance = (int)strtol(sTolerance,&endptr,10); if(*endptr != 0 || tolerance < 0) { ctx->set_error(ctx, 400, "failed to parse grid tolerance %s (expecting a positive integer)", sTolerance); return; } } sTolerance = (char*)ezxml_attr(cur_node,"use_wms_intermediate_resolutions"); if(sTolerance && !strcmp(sTolerance,"true")) { mapcache_grid_link *intermediate_gridlink = apr_pcalloc(ctx->pool,sizeof(mapcache_grid_link)); APR_ARRAY_PUSH(gridlink->intermediate_grids,mapcache_grid_link*) = intermediate_gridlink; } mapcache_grid_compute_limits(grid,extent,gridlink->grid_limits,tolerance); sTolerance = (char*)ezxml_attr(cur_node,"minzoom"); if(sTolerance) { char *endptr; tolerance = (int)strtol(sTolerance,&endptr,10); if(*endptr != 0 || tolerance < 0) { ctx->set_error(ctx, 400, "failed to parse grid minzoom %s (expecting a positive integer)", sTolerance); return; } gridlink->minz = tolerance; } sTolerance = (char*)ezxml_attr(cur_node,"maxzoom"); if(sTolerance) { char *endptr; tolerance = (int)strtol(sTolerance,&endptr,10); if(*endptr != 0 || tolerance < 0) { ctx->set_error(ctx, 400, "failed to parse grid maxzoom %s (expecting a positive integer)", sTolerance); return; } gridlink->maxz = tolerance + 1; } if(gridlink->minz<0 || gridlink->maxz>grid->nlevels || gridlink->minz>=gridlink->maxz) { ctx->set_error(ctx, 400, "invalid grid maxzoom/minzoom %d/%d", gridlink->minz,gridlink->maxz); return; } sTolerance = (char*)ezxml_attr(cur_node,"max-cached-zoom"); /* RFC97 implementation: check for a maximum zoomlevel to cache */ if(sTolerance) { char *endptr; tolerance = (int)strtol(sTolerance,&endptr,10); if(*endptr != 0 || tolerance < 0) { ctx->set_error(ctx, 400, "failed to parse grid max-cached-zoom %s (expecting a positive integer)", sTolerance); return; } if(tolerance > gridlink->maxz) { ctx->set_error(ctx, 400, "failed to parse grid max-cached-zoom %s (max cached zoom is greater than grid's max zoom)", sTolerance); return; } gridlink->max_cached_zoom = tolerance; /* default to reassembling */ gridlink->outofzoom_strategy = MAPCACHE_OUTOFZOOM_REASSEMBLE; sTolerance = (char*)ezxml_attr(cur_node,"out-of-zoom-strategy"); if(sTolerance) { if(!strcasecmp(sTolerance,"reassemble")) { gridlink->outofzoom_strategy = MAPCACHE_OUTOFZOOM_REASSEMBLE; } else if(!strcasecmp(sTolerance,"proxy")) { gridlink->outofzoom_strategy = MAPCACHE_OUTOFZOOM_PROXY; } else { ctx->set_error(ctx, 400, "failed to parse grid out-of-zoom-strategy %s (expecting \"reassemble\" or \"proxy\")", sTolerance); return; } } } /* compute wgs84 bbox if it wasn't supplied already */ if(!havewgs84bbox && !strcasecmp(grid->srs,"EPSG:4326")) { tileset->wgs84bbox = *extent; } if(gridlink->intermediate_grids->nelts > 0) { double factor = 0.5, unitheight,unitwidth; int i; mapcache_grid_link *igl = APR_ARRAY_IDX(gridlink->intermediate_grids, 0, mapcache_grid_link*); igl->restricted_extent = gridlink->restricted_extent; igl->minz = gridlink->minz; igl->max_cached_zoom = gridlink->max_cached_zoom - 1; igl->maxz = gridlink->maxz - 1; igl->outofzoom_strategy = gridlink->outofzoom_strategy; igl->grid = mapcache_grid_create(ctx->pool); igl->grid->extent = gridlink->grid->extent; igl->grid->name = apr_psprintf(ctx->pool,"%s_intermediate_%g",gridlink->grid->name,factor); igl->grid->nlevels = gridlink->grid->nlevels - 1; igl->grid->origin = gridlink->grid->origin; igl->grid->srs = gridlink->grid->srs; igl->grid->srs_aliases = gridlink->grid->srs_aliases; igl->grid->unit = gridlink->grid->unit; igl->grid->tile_sx = gridlink->grid->tile_sx + gridlink->grid->tile_sx * factor; igl->grid->tile_sy = gridlink->grid->tile_sy + gridlink->grid->tile_sy * factor; igl->grid->levels = (mapcache_grid_level**)apr_pcalloc(ctx->pool, igl->grid->nlevels*sizeof(mapcache_grid_level*)); for(i=0; igrid->nlevels; i++) { mapcache_grid_level *level = (mapcache_grid_level*)apr_pcalloc(ctx->pool,sizeof(mapcache_grid_level)); level->resolution = gridlink->grid->levels[i]->resolution + (gridlink->grid->levels[i+1]->resolution - gridlink->grid->levels[i]->resolution) * factor; unitheight = igl->grid->tile_sy * level->resolution; unitwidth = igl->grid->tile_sx * level->resolution; level->maxy = ceil((igl->grid->extent.maxy-igl->grid->extent.miny - 0.01* unitheight)/unitheight); level->maxx = ceil((igl->grid->extent.maxx-igl->grid->extent.minx - 0.01* unitwidth)/unitwidth); igl->grid->levels[i] = level; } igl->grid_limits = (mapcache_extent_i*)apr_pcalloc(ctx->pool,igl->grid->nlevels*sizeof(mapcache_extent_i)); mapcache_grid_compute_limits(igl->grid,extent,igl->grid_limits,tolerance); } APR_ARRAY_PUSH(tileset->grid_links,mapcache_grid_link*) = gridlink; } if ((cur_node = ezxml_child(node,"dimensions")) != NULL) { parseDimensions(ctx, cur_node, tileset); 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) { ctx->set_error(ctx, 400, "tileset \"%s\" references cache \"%s\"," " but it is not configured", name, cur_node->txt); return; } tileset->_cache = cache; } if ((cur_node = ezxml_child(node,"source")) != NULL) { mapcache_source *source = mapcache_configuration_get_source(config, cur_node->txt); if(!source) { ctx->set_error(ctx, 400, "tileset \"%s\" references source \"%s\"," " but it is not configured", name, cur_node->txt); return; } tileset->source = source; } if ((cur_node = ezxml_child(node,"metatile")) != NULL) { int *values, nvalues; value = apr_pstrdup(ctx->pool,cur_node->txt); if(MAPCACHE_SUCCESS != mapcache_util_extract_int_list(ctx, cur_node->txt, NULL, &values, &nvalues) || nvalues != 2) { ctx->set_error(ctx, 400, "failed to parse metatile dimension %s." "(expecting 2 space separated integers, " "eg 5 5", cur_node->txt); return; } tileset->metasize_x = values[0]; tileset->metasize_y = values[1]; } if ((cur_node = ezxml_child(node,"watermark")) != NULL) { if(!*cur_node->txt) { ctx->set_error(ctx,400, "watermark config entry empty"); return; } mapcache_tileset_add_watermark(ctx,tileset,cur_node->txt); GC_CHECK_ERROR(ctx); } if ((cur_node = ezxml_child(node,"expires")) != NULL) { char *endptr; tileset->expires = (int)strtol(cur_node->txt,&endptr,10); if(*endptr != 0) { ctx->set_error(ctx, 400, "failed to parse expires %s." "(expecting an integer, " "eg 3600", cur_node->txt); return; } } if ((cur_node = ezxml_child(node,"auto_expire")) != NULL) { char *endptr; tileset->auto_expire = (int)strtol(cur_node->txt,&endptr,10); if(*endptr != 0) { ctx->set_error(ctx, 400, "failed to parse auto_expire %s." "(expecting an integer, " "eg 3600", cur_node->txt); return; } } if ((cur_node = ezxml_child(node,"metabuffer")) != NULL) { char *endptr; tileset->metabuffer = (int)strtol(cur_node->txt,&endptr,10); if(*endptr != 0) { ctx->set_error(ctx, 400, "failed to parse metabuffer %s." "(expecting an integer, " "eg 1", cur_node->txt); return; } } if ((cur_node = ezxml_child(node,"format")) != NULL) { mapcache_image_format *format = mapcache_configuration_get_image_format(config,cur_node->txt); if(!format) { ctx->set_error(ctx, 400, "tileset \"%s\" references format \"%s\"," " but it is not configured",name,cur_node->txt); return; } tileset->format = format; } mapcache_tileset_configuration_check(ctx,tileset); GC_CHECK_ERROR(ctx); mapcache_configuration_add_tileset(config,tileset,name); return; } void parseServices(mapcache_context *ctx, ezxml_t root, mapcache_cfg *config) { ezxml_t node; if ((node = ezxml_child(root,"wms")) != NULL) { if(!node->txt || !*node->txt || strcmp(node->txt, "false")) { config->services[MAPCACHE_SERVICE_WMS] = mapcache_service_wms_create(ctx); } } if ((node = ezxml_child(root,"wmts")) != NULL) { if(!node->txt || !*node->txt || strcmp(node->txt, "false")) { config->services[MAPCACHE_SERVICE_WMTS] = mapcache_service_wmts_create(ctx); } } if ((node = ezxml_child(root,"ve")) != NULL) { if(!node->txt || !*node->txt || strcmp(node->txt, "false")) { config->services[MAPCACHE_SERVICE_VE] = mapcache_service_ve_create(ctx); } } if ((node = ezxml_child(root,"tms")) != NULL) { if(!node->txt || !*node->txt || strcmp(node->txt, "false")) { config->services[MAPCACHE_SERVICE_TMS] = mapcache_service_tms_create(ctx); } } if ((node = ezxml_child(root,"kml")) != NULL) { if(!node->txt || !*node->txt || strcmp(node->txt, "false")) { if(!config->services[MAPCACHE_SERVICE_TMS]) { ctx->set_error(ctx,400,"kml service requires the tms service to be active"); return; } config->services[MAPCACHE_SERVICE_KML] = mapcache_service_kml_create(ctx); } } if ((node = ezxml_child(root,"gmaps")) != NULL) { if(!node->txt || !*node->txt || strcmp(node->txt, "false")) { config->services[MAPCACHE_SERVICE_GMAPS] = mapcache_service_gmaps_create(ctx); } } if ((node = ezxml_child(root,"demo")) != NULL) { if(!node->txt || !*node->txt || strcmp(node->txt, "false")) { config->services[MAPCACHE_SERVICE_DEMO] = mapcache_service_demo_create(ctx); if(!config->services[MAPCACHE_SERVICE_WMS]) config->services[MAPCACHE_SERVICE_WMS] = mapcache_service_wms_create(ctx); } } if(!config->services[MAPCACHE_SERVICE_WMS] && !config->services[MAPCACHE_SERVICE_TMS] && !config->services[MAPCACHE_SERVICE_WMTS]) { ctx->set_error(ctx, 400, "no services configured." " You must add a tag with or children"); return; } } void mapcache_configuration_parse_xml(mapcache_context *ctx, const char *filename, mapcache_cfg *config) { ezxml_t doc, node; const char *mode; doc = ezxml_parse_file(filename); if (doc == NULL) { ctx->set_error(ctx,400, "failed to parse file %s. Is it valid XML?", filename); goto cleanup; } else { const char *err = ezxml_error(doc); if(err && *err) { ctx->set_error(ctx,400, "failed to parse file %s: %s", filename, err); goto cleanup; } } if(strcmp(doc->name,"mapcache")) { ctx->set_error(ctx,400, "failed to parse file %s. first node is not ", filename); goto cleanup; } mode = ezxml_attr(doc,"mode"); if(mode) { if(!strcmp(mode,"combined_mirror")) { config->mode = MAPCACHE_MODE_MIRROR_COMBINED; } else if(!strcmp(mode,"split_mirror")) { config->mode = MAPCACHE_MODE_MIRROR_SPLIT; } else if(!strcmp(mode,"normal")) { config->mode = MAPCACHE_MODE_NORMAL; } else { ctx->set_error(ctx,400,"unknown mode \"%s\" for ",mode); goto cleanup; } } else { config->mode = MAPCACHE_MODE_NORMAL; } for(node = ezxml_child(doc,"metadata"); node; node = node->next) { parseMetadata(ctx, node, config->metadata); if(GC_HAS_ERROR(ctx)) goto cleanup; } for(node = ezxml_child(doc,"source"); node; node = node->next) { parseSource(ctx, node, config); if(GC_HAS_ERROR(ctx)) goto cleanup; } for(node = ezxml_child(doc,"grid"); node; node = node->next) { parseGrid(ctx, node, config); if(GC_HAS_ERROR(ctx)) goto cleanup; } for(node = ezxml_child(doc,"format"); node; node = node->next) { parseFormat(ctx, node, config); if(GC_HAS_ERROR(ctx)) goto cleanup; } for(node = ezxml_child(doc,"cache"); node; node = node->next) { parseCache(ctx, node, config); if(GC_HAS_ERROR(ctx)) goto cleanup; } for(node = ezxml_child(doc,"tileset"); node; node = node->next) { parseTileset(ctx, node, config); if(GC_HAS_ERROR(ctx)) goto cleanup; } if ((node = ezxml_child(doc,"service")) != NULL) { ezxml_t service_node; for(service_node = node; service_node; service_node = service_node->next) { char *enabled = (char*)ezxml_attr(service_node,"enabled"); char *type = (char*)ezxml_attr(service_node,"type"); if(!strcasecmp(enabled,"true")) { if (!strcasecmp(type,"wms")) { mapcache_service *new_service = mapcache_service_wms_create(ctx); if(new_service->configuration_parse_xml) { new_service->configuration_parse_xml(ctx,service_node,new_service,config); } config->services[MAPCACHE_SERVICE_WMS] = new_service; } else if (!strcasecmp(type,"tms")) { mapcache_service *new_service = mapcache_service_tms_create(ctx); if(new_service->configuration_parse_xml) { new_service->configuration_parse_xml(ctx,service_node,new_service,config); } config->services[MAPCACHE_SERVICE_TMS] = new_service; } else if (!strcasecmp(type,"wmts")) { mapcache_service *new_service = mapcache_service_wmts_create(ctx); if(new_service->configuration_parse_xml) { new_service->configuration_parse_xml(ctx,service_node,new_service,config); } config->services[MAPCACHE_SERVICE_WMTS] = new_service; } else if (!strcasecmp(type,"kml")) { mapcache_service *new_service = mapcache_service_kml_create(ctx); if(new_service->configuration_parse_xml) { new_service->configuration_parse_xml(ctx,service_node,new_service,config); } config->services[MAPCACHE_SERVICE_KML] = new_service; } else if (!strcasecmp(type,"gmaps")) { mapcache_service *new_service = mapcache_service_gmaps_create(ctx); if(new_service->configuration_parse_xml) { new_service->configuration_parse_xml(ctx,service_node,new_service,config); } config->services[MAPCACHE_SERVICE_GMAPS] = new_service; } else if (!strcasecmp(type,"mapguide")) { mapcache_service *new_service = mapcache_service_mapguide_create(ctx); if(new_service->configuration_parse_xml) { new_service->configuration_parse_xml(ctx,service_node,new_service,config); } config->services[MAPCACHE_SERVICE_MAPGUIDE] = new_service; } else if (!strcasecmp(type,"ve")) { mapcache_service *new_service = mapcache_service_ve_create(ctx); if(new_service->configuration_parse_xml) { new_service->configuration_parse_xml(ctx,service_node,new_service,config); } config->services[MAPCACHE_SERVICE_VE] = new_service; } else if (!strcasecmp(type,"demo")) { mapcache_service *new_service = mapcache_service_demo_create(ctx); if(new_service->configuration_parse_xml) { new_service->configuration_parse_xml(ctx,service_node,new_service,config); } config->services[MAPCACHE_SERVICE_DEMO] = new_service; } else { ctx->set_error(ctx,400,"unknown type %s",type); } if(GC_HAS_ERROR(ctx)) goto cleanup; } } } else if ((node = ezxml_child(doc,"services")) != NULL) { ctx->log(ctx,MAPCACHE_WARN," tag is deprecated, use "); parseServices(ctx, node, config); } else { ctx->set_error(ctx, 400, "no configured"); } if(GC_HAS_ERROR(ctx)) goto cleanup; node = ezxml_child(doc,"default_format"); if(!node) node = ezxml_child(doc,"merge_format"); if (node) { mapcache_image_format *format = mapcache_configuration_get_image_format(config,node->txt); if(!format) { ctx->set_error(ctx, 400, "default_format tag references format %s but it is not configured", node->txt); goto cleanup; } config->default_image_format = format; } if ((node = ezxml_child(doc,"errors")) != NULL) { if(!strcmp(node->txt,"log")) { config->reporting = MAPCACHE_REPORT_LOG; } else if(!strcmp(node->txt,"report")) { config->reporting = MAPCACHE_REPORT_MSG; } else if(!strcmp(node->txt,"empty_img")) { config->reporting = MAPCACHE_REPORT_EMPTY_IMG; mapcache_image_create_empty(ctx, config); if(GC_HAS_ERROR(ctx)) goto cleanup; } else if(!strcmp(node->txt, "report_img")) { config->reporting = MAPCACHE_REPORT_ERROR_IMG; ctx->set_error(ctx,501,": report_img not implemented"); goto cleanup; } else { ctx->set_error(ctx,400,": unknown value %s (allowed are log, report, empty_img, report_img)", node->txt); goto cleanup; } } if((node = ezxml_child(doc,"locker")) != NULL) { 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; } if((node = ezxml_child(doc,"threaded_fetching")) != NULL) { if(!strcasecmp(node->txt,"true")) { config->threaded_fetching = 1; } else if(strcasecmp(node->txt,"false")) { ctx->set_error(ctx, 400, "failed to parse threaded_fetching \"%s\". Expecting true or false",node->txt); return; } } if((node = ezxml_child(doc,"log_level")) != NULL) { if(!strcasecmp(node->txt,"debug")) { config->loglevel = MAPCACHE_DEBUG; } else if(!strcasecmp(node->txt,"info")) { config->loglevel = MAPCACHE_INFO; } else if(!strcasecmp(node->txt,"notice")) { config->loglevel = MAPCACHE_NOTICE; } else if(!strcasecmp(node->txt,"warn")) { config->loglevel = MAPCACHE_WARN; } else if(!strcasecmp(node->txt,"error")) { config->loglevel = MAPCACHE_ERROR; } else if(!strcasecmp(node->txt,"crit")) { config->loglevel = MAPCACHE_CRIT; } else if(!strcasecmp(node->txt,"alert")) { config->loglevel = MAPCACHE_ALERT; } else if(!strcasecmp(node->txt,"emerg")) { config->loglevel = MAPCACHE_EMERG; } else { ctx->set_error(ctx,500,"failed to parse \"%s\". Expecting debug, info, notice, warn, error, crit, alert or emerg",node->txt); return; } } if((node = ezxml_child(doc,"auto_reload")) != NULL) { if(!strcasecmp(node->txt,"true")) { config->autoreload = 1; } else if(!strcasecmp(node->txt,"false")) { config->autoreload = 0; } else { ctx->set_error(ctx,500,"failed to parse \"%s\". Expecting true or false",node->txt); return; } } cleanup: ezxml_free(doc); return; } /* vim: ts=2 sts=2 et sw=2 */ mapcache-1.4.0/lib/connection_pool.c000066400000000000000000000151421255567662100174010ustar00rootroot00000000000000/****************************************************************************** * * Project: MapServer * Purpose: MapCache connection pooling * 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 #include "mapcache.h" struct mapcache_connection_pool { apr_pool_t *server_pool; apr_reslist_t *connexions; }; struct mapcache_pooled_connection_container { mapcache_pooled_connection *head; apr_pool_t *pool; unsigned int max_list_size; }; struct mapcache_pooled_connection_private_data { char *key; mapcache_connection_destructor destructor; mapcache_pooled_connection *next; mapcache_pooled_connection_container *pcc; }; static apr_status_t mapcache_connection_container_creator(void **conn_, void *params, apr_pool_t *pool) { mapcache_pooled_connection_container *pcc; pcc = calloc(1, sizeof(mapcache_pooled_connection_container)); pcc->max_list_size = 10; pcc->pool = pool; *conn_ = pcc; return APR_SUCCESS; } static apr_status_t mapcache_connection_container_destructor(void *conn_, void *params, apr_pool_t *pool) { mapcache_pooled_connection_container *pcc = (mapcache_pooled_connection_container*)conn_; mapcache_pooled_connection *pc = pcc->head; while(pc) { mapcache_pooled_connection *this = pc; this->private->destructor(this->connection, pcc->pool); free(this->private->key); pc = this->private->next; free(this); } free(pcc); return MAPCACHE_SUCCESS; } apr_status_t mapcache_connection_pool_create(mapcache_connection_pool **cp, apr_pool_t *server_pool) { apr_status_t rv; *cp = apr_pcalloc(server_pool, sizeof(mapcache_connection_pool)); (*cp)->server_pool = server_pool; rv = apr_reslist_create(&((*cp)->connexions), 1, 5, 200, 60*1000000, mapcache_connection_container_creator, mapcache_connection_container_destructor, NULL, server_pool); return rv; } mapcache_pooled_connection* mapcache_connection_pool_get_connection(mapcache_context *ctx, char *key, mapcache_connection_constructor constructor, mapcache_connection_destructor destructor, void *params) { apr_status_t rv; int count = 0; mapcache_pooled_connection_container *pcc; mapcache_pooled_connection *pc,*pred=NULL; rv = apr_reslist_acquire(ctx->connection_pool->connexions, (void**)&pcc); if(rv != APR_SUCCESS || !pcc) { char errmsg[120]; ctx->set_error(ctx,500, "failed to acquire connection from mapcache connection pool: (%s)", apr_strerror(rv, errmsg,120)); return NULL; } /* loop through existing connections to see if we find one matching the given key */ pc = pcc->head; while(pc) { count++; if(!strcmp(key,pc->private->key)) { /* move current connection to head of list, and return it. We only move the connection to the front of the list if it wasn't in the first 2 connections, as in the seeding case we are always alternating between read and write operations (i.e. potentially 2 different connections and in that cas we end up switching connections each time there's an access */ if(pc != pcc->head && count>2) { assert(pred); pred->private->next = pc->private->next; pc->private->next = pcc->head; pcc->head = pc; } return pc; } pred = pc; pc = pc->private->next; } /* connection not found in pool */ pc = calloc(1,sizeof(mapcache_pooled_connection)); /* ctx->log(ctx, MAPCACHE_DEBUG, "calling constructor for pooled connection (%s)", key); */ constructor(ctx, &pc->connection, params, pcc->pool); if(GC_HAS_ERROR(ctx)) { free(pc); apr_reslist_release(ctx->connection_pool->connexions, pcc); return NULL; } pc->private = calloc(1,sizeof(mapcache_pooled_connection_private_data)); pc->private->key = strdup(key); pc->private->destructor = destructor; pc->private->next = pcc->head; pc->private->pcc = pcc; if(count == pcc->max_list_size) { /* max number of connections atained, we must destroy the last one that was used */ mapcache_pooled_connection *opc; opc = pcc->head; count = 1; while(count < pcc->max_list_size) { pred = opc; opc = opc->private->next; count++; } ctx->log(ctx, MAPCACHE_DEBUG, "tearing down pooled connection (%s) to make room", opc->private->key); opc->private->destructor(opc->connection, pcc->pool); free(opc->private->key); free(opc->private); free(opc); if(pred) { pred->private->next = NULL; } } pcc->head = pc; return pc; } void mapcache_connection_pool_invalidate_connection(mapcache_context *ctx, mapcache_pooled_connection *connection) { mapcache_pooled_connection_container *pcc = connection->private->pcc; mapcache_pooled_connection *pc = pcc->head, *pred=NULL; while(pc) { if(pc == connection) { if(pred) { pred->private->next = pc->private->next; } else { pcc->head = pc->private->next; } pc->private->destructor(pc->connection, pcc->pool); free(pc->private->key); free(pc); } pred = pc; pc = pc->private->next; } } void mapcache_connection_pool_release_connection(mapcache_context *ctx, mapcache_pooled_connection *connection) { if(connection) { mapcache_pooled_connection_container *pcc = connection->private->pcc; apr_reslist_release(ctx->connection_pool->connexions,(void*)pcc); } } mapcache-1.4.0/lib/core.c000066400000000000000000000570541255567662100151510ustar00rootroot00000000000000/****************************************************************************** * $Id$ * * Project: MapServer * Purpose: MapCache tile caching support file: high level functions called * from the CGI or apache-module implementations * 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 #include "mapcache.h" #if APR_HAS_THREADS #include "apu_version.h" #if (APU_MAJOR_VERSION <= 1 && APU_MINOR_VERSION <= 3) #define USE_THREADPOOL 0 #else #define USE_THREADPOOL 1 #endif /* use a thread pool if using 1.3.12 or higher apu */ #if !USE_THREADPOOL #include #else #include #endif typedef struct { mapcache_tile *tile; mapcache_context *ctx; int launch; } _thread_tile; static void* APR_THREAD_FUNC _thread_get_tile(apr_thread_t *thread, void *data) { _thread_tile* t = (_thread_tile*)data; mapcache_tileset_tile_get(t->ctx, t->tile); #if !USE_THREADPOOL apr_thread_exit(thread, APR_SUCCESS); #endif return NULL; } #endif mapcache_http_response *mapcache_http_response_create(apr_pool_t *pool) { mapcache_http_response *response = (mapcache_http_response*) apr_pcalloc(pool, sizeof(mapcache_http_response)); /* make room for at least Expires, Cache-Control, and Content-Type */ response->headers = apr_table_make(pool,3); response->code = 200; return response; } void mapcache_prefetch_tiles(mapcache_context *ctx, mapcache_tile **tiles, int ntiles) { apr_thread_t **threads; apr_threadattr_t *thread_attrs; int nthreads; #if !APR_HAS_THREADS int i; for(i=0; iconfig->threaded_fetching == 0) { /* if threads disabled, or only fetching a single tile, don't launch a thread for the operation */ for(i=0; ipool,ntiles*sizeof(_thread_tile)); #if 1 || !USE_THREADPOOL /* use multiple threads, to fetch from multiple metatiles and/or multiple tilesets */ apr_threadattr_create(&thread_attrs, ctx->pool); threads = (apr_thread_t**)apr_pcalloc(ctx->pool, ntiles*sizeof(apr_thread_t*)); nthreads = 0; for(i=0; i=0) { /* check that the given metatile hasn't been rendered yet */ if(thread_tiles[j].launch && (thread_tiles[i].tile->tileset == thread_tiles[j].tile->tileset) && (thread_tiles[i].tile->x / thread_tiles[i].tile->tileset->metasize_x == thread_tiles[j].tile->x / thread_tiles[j].tile->tileset->metasize_x)&& (thread_tiles[i].tile->y / thread_tiles[i].tile->tileset->metasize_y == thread_tiles[j].tile->y / thread_tiles[j].tile->tileset->metasize_y)) { thread_tiles[i].launch = 0; /* this tile will not have a thread spawned for it */ break; } j--; } if(thread_tiles[i].launch) thread_tiles[i].ctx = ctx->clone(ctx); } for(i=0; ipool); if(rv != APR_SUCCESS) { ctx->set_error(ctx,500, "failed to create thread %d of %d\n",i,ntiles); break; } nthreads++; } /* wait for launched threads to finish */ for(i=0; iset_error(ctx,500, "thread %d of %d failed on exit\n",i,ntiles); } if(GC_HAS_ERROR(thread_tiles[i].ctx)) { /* transfer error message from child thread to main context */ ctx->set_error(ctx,thread_tiles[i].ctx->get_error(thread_tiles[i].ctx), thread_tiles[i].ctx->get_error_message(thread_tiles[i].ctx)); } } for(i=0; iconfig->download_threads,ctx->pool); for(i=0; ilog(ctx,MAPCACHE_DEBUG,"starting thread for tile %s",tiles[i]->tileset->name); thread_tiles[i].tile = tiles[i]; thread_tiles[i].ctx = ctx->clone(ctx); rv = apr_thread_pool_push(thread_pool,_thread_get_tile,(void*)&(thread_tiles[i]), 0,NULL); if(rv != APR_SUCCESS) { ctx->set_error(ctx,500, "failed to push thread %d of %d in thread pool\n",i,ntiles); break; } } GC_CHECK_ERROR(ctx); while(apr_thread_pool_tasks_run_count(thread_pool) != ntiles || apr_thread_pool_busy_count(thread_pool)>0) apr_sleep(10000); apr_thread_pool_destroy(thread_pool); for(i=0; iset_error(ctx,thread_tiles[i].ctx->get_error(thread_tiles[i].ctx), thread_tiles[i].ctx->get_error_message(thread_tiles[i].ctx)); } } #endif #endif } mapcache_http_response *mapcache_core_get_tile(mapcache_context *ctx, mapcache_request_get_tile *req_tile) { int expires = 0; mapcache_http_response *response; int i,is_empty=1; /* response image is initially empty */; char *timestr; mapcache_image *base=NULL; mapcache_image_format *format = NULL; #ifdef DEBUG if(req_tile->ntiles ==0) { ctx->set_error(ctx,500,"BUG: get_tile called with 0 tiles"); return NULL; } #endif response = mapcache_http_response_create(ctx->pool); if(ctx->supports_redirects && req_tile->ntiles == 1) { req_tile->tiles[0]->allow_redirect = 1; } mapcache_prefetch_tiles(ctx,req_tile->tiles,req_tile->ntiles); if(GC_HAS_ERROR(ctx)) return NULL; if(req_tile->tiles[0]->redirect) { response->code = 302; apr_table_set(response->headers,"Location",req_tile->tiles[0]->redirect); response->data = mapcache_buffer_create(0, ctx->pool); return response; } /* loop through tiles, and eventually merge them vertically together */ for(i=0; intiles; i++) { mapcache_tile *tile = req_tile->tiles[i]; /* shortcut */ if(tile->mtime && (tile->mtime < response->mtime || response->mtime == 0)) response->mtime = tile->mtime; if(tile->expires && (tile->expires < expires || expires == 0)) { expires = tile->expires; } if(tile->nodata) { /* treat the special case where the cache explicitely stated that the tile was empty, and we don't have any vertical merging to do */ if(tile->encoded_data && req_tile->ntiles == 1) { response->data = tile->encoded_data; /* we don't touch is_empty, as we have access to the encoded empty image, but the resulting tile is empty */ } continue; } /* treat the most common case: - we have a single tile request (i.e. isempty is true) - the cache returned the encoded image */ if(is_empty && tile->encoded_data) { response->data = tile->encoded_data; /* just in case we also have the raw image data available, keep a ref to it if we need to merge another tile ontop of it*/ if(tile->raw_image) { base = tile->raw_image; } is_empty = 0; /* we now know we might need to do some vertical merging */ continue; } /* if we're here, either * - we need to merge the current tile onto the previous one(s), or * - we only have the tile's raw data available */ if(!is_empty) { /* we have an existing tile, so we know we need to merge the current one into it */ if(!base) { /* the existing tile has not been decoded yet, but we need the access to the raw pixels*/ base = mapcache_imageio_decode(ctx, response->data); if(!base) return NULL; } response->data = NULL; /* the encoded data is now obsolete, as we will be merging the current tile */ /* we need to access the current tile's pixel data */ if(!tile->raw_image) { tile->raw_image = mapcache_imageio_decode(ctx,tile->encoded_data); if(!tile->raw_image) return NULL; } mapcache_image_merge(ctx, base, tile->raw_image); } else { /* we don't need to merge onto an existing tile and don't have access to the tile's encoded data. * * we don't encode the tile's raw image data just yet because we might need to merge another one on top * of it later. */ base = tile->raw_image; is_empty = 0; } } if(!response->data) { /* we need to encode the raw image data*/ if(base) { if(req_tile->image_request.format) { format = req_tile->image_request.format; } else { format = req_tile->tiles[0]->tileset->format; if(!format) { format = ctx->config->default_image_format; /* this one is always defined */ } } response->data = format->write(ctx, base, format); if(GC_HAS_ERROR(ctx)) { return NULL; } } else { #ifdef DEBUG if(!is_empty) { ctx->set_error(ctx,500,"BUG: no image data to encode, but tile not marked as empty"); return NULL; } #endif unsigned char empty[5] = {'#',0,0,0,0}; response->data = mapcache_empty_png_decode(ctx,req_tile->tiles[0]->grid_link->grid->tile_sx, req_tile->tiles[0]->grid_link->grid->tile_sy, empty,&is_empty); /* is_empty is unchanged and left to 1 */ format = mapcache_configuration_get_image_format(ctx->config,"PNG8"); } } /* compute the content-type */ mapcache_image_format_type t = mapcache_imageio_header_sniff(ctx,response->data); if(t == GC_PNG) apr_table_set(response->headers,"Content-Type","image/png"); else if(t == GC_JPEG) apr_table_set(response->headers,"Content-Type","image/jpeg"); /* compute expiry headers */ if(expires) { apr_time_t now = apr_time_now(); apr_time_t additional = apr_time_from_sec(expires); apr_time_t texpires = now + additional; apr_table_set(response->headers, "Cache-Control",apr_psprintf(ctx->pool, "max-age=%d", expires)); timestr = apr_palloc(ctx->pool, APR_RFC822_DATE_LEN); apr_rfc822_date(timestr, texpires); apr_table_setn(response->headers, "Expires", timestr); } return response; } mapcache_map* mapcache_assemble_maps(mapcache_context *ctx, mapcache_map **maps, int nmaps, mapcache_resample_mode mode) { mapcache_tile ***maptiles; int *nmaptiles; mapcache_tile **tiles; mapcache_map *basemap = NULL; int ntiles = 0; int i; maptiles = apr_pcalloc(ctx->pool,nmaps*sizeof(mapcache_tile**)); nmaptiles = apr_pcalloc(ctx->pool,nmaps*sizeof(int)); mapcache_grid_link **effectively_used_grid_links = apr_pcalloc(ctx->pool,nmaps*sizeof(mapcache_grid_link*)); for(i=0; itileset,maps[i]->grid_link, &maps[i]->extent, maps[i]->width, maps[i]->height, &(nmaptiles[i]), &(maptiles[i]), &(effectively_used_grid_links[i])); if(GC_HAS_ERROR(ctx)) return NULL; ntiles += nmaptiles[i]; } tiles = apr_pcalloc(ctx->pool,ntiles * sizeof(mapcache_tile*)); ntiles = 0; for(i=0; idimensions = maps[i]->dimensions; ntiles++; } } mapcache_prefetch_tiles(ctx,tiles,ntiles); if(GC_HAS_ERROR(ctx)) return NULL; for(i=0; inodata) { continue; } hasdata++; /* update the map modification time if it is older than the tile mtime */ if(tile->mtime>maps[i]->mtime) { maps[i]->mtime = tile->mtime; } /* set the map expiration delay to the tile expiration delay, * either if the map hasn't got an expiration delay yet * or if the tile expiration is shorter than the map expiration */ if(!maps[i]->expires || tile->expiresexpires) { maps[i]->expires = tile->expires; } } if(hasdata) { maps[i]->raw_image = mapcache_tileset_assemble_map_tiles(ctx,maps[i]->tileset,effectively_used_grid_links[i], &maps[i]->extent, maps[i]->width, maps[i]->height, nmaptiles[i], maptiles[i], mode); if(!basemap) { basemap = maps[i]; } else { mapcache_image_merge(ctx,basemap->raw_image,maps[i]->raw_image); if(GC_HAS_ERROR(ctx)) return NULL; if(maps[i]->mtime > basemap->mtime) basemap->mtime = maps[i]->mtime; if(!basemap->expires || maps[i]->expiresexpires) basemap->expires = maps[i]->expires; apr_pool_cleanup_run(ctx->pool, maps[i]->raw_image->data, (void*)free) ; maps[i]->raw_image = NULL; } } else { maps[i]->nodata = 1; } } if(!basemap) { ctx->set_error(ctx,404, "no tiles containing image data could be retrieved to create map (not in cache, and/or no source configured)"); return NULL; } return basemap; } mapcache_http_response *mapcache_core_get_map(mapcache_context *ctx, mapcache_request_get_map *req_map) { mapcache_image_format *format = NULL; mapcache_http_response *response; mapcache_map *basemap = NULL; char *timestr; #ifdef DEBUG if(req_map->nmaps ==0) { ctx->set_error(ctx,500,"BUG: get_map called with 0 maps"); return NULL; } #endif if(req_map->getmap_strategy == MAPCACHE_GETMAP_ERROR) { ctx->set_error(ctx, 404, "full wms support disabled"); return NULL; } format = NULL; response = mapcache_http_response_create(ctx->pool); if(req_map->getmap_strategy == MAPCACHE_GETMAP_ASSEMBLE) { basemap = mapcache_assemble_maps(ctx, req_map->maps, req_map->nmaps, req_map->resample_mode); if(GC_HAS_ERROR(ctx)) return NULL; } else if(!ctx->config->non_blocking && req_map->getmap_strategy == MAPCACHE_GETMAP_FORWARD) { int i; basemap = req_map->maps[0]; for(i=0; inmaps; i++) { if(!req_map->maps[i]->tileset->source) { ctx->set_error(ctx,404,"cannot forward request for tileset %s: no source configured", req_map->maps[i]->tileset->name); return NULL; } } basemap->tileset->source->render_map(ctx, basemap); if(GC_HAS_ERROR(ctx)) return NULL; if(req_map->nmaps>1) { if(!basemap->raw_image) { basemap->raw_image = mapcache_imageio_decode(ctx,basemap->encoded_data); if(GC_HAS_ERROR(ctx)) return NULL; } for(i=1; inmaps; i++) { mapcache_map *overlaymap = req_map->maps[i]; overlaymap->tileset->source->render_map(ctx, overlaymap); if(GC_HAS_ERROR(ctx)) return NULL; if(!overlaymap->raw_image) { overlaymap->raw_image = mapcache_imageio_decode(ctx,overlaymap->encoded_data); if(GC_HAS_ERROR(ctx)) return NULL; } if(GC_HAS_ERROR(ctx)) return NULL; mapcache_image_merge(ctx,basemap->raw_image,overlaymap->raw_image); if(GC_HAS_ERROR(ctx)) return NULL; if(!basemap->expires || overlaymap->expiresexpires) basemap->expires = overlaymap->expires; } } } else { ctx->set_error(ctx,400,"failed getmap, readonly mode"); return NULL; } if(basemap->raw_image) { format = req_map->image_request.format; /* always defined, defaults to JPEG */ response->data = format->write(ctx,basemap->raw_image,format); if(GC_HAS_ERROR(ctx)) { return NULL; } } else { /* this case happens when we have a forward strategy for a single tileset */ #ifdef DEBUG if(!basemap->encoded_data) { ctx->set_error(ctx,500,"###BUG### core_get_map failed with null encoded_data"); return NULL; } #endif response->data = basemap->encoded_data; } /* compute the content-type */ if(format && format->mime_type) { apr_table_set(response->headers,"Content-Type",format->mime_type); } else { mapcache_image_format_type t = mapcache_imageio_header_sniff(ctx,response->data); if(t == GC_PNG) apr_table_set(response->headers,"Content-Type","image/png"); else if(t == GC_JPEG) apr_table_set(response->headers,"Content-Type","image/jpeg"); } /* compute expiry headers */ if(basemap->expires) { apr_time_t now = apr_time_now(); apr_time_t additional = apr_time_from_sec(basemap->expires); apr_time_t texpires = now + additional; apr_table_set(response->headers, "Cache-Control", apr_psprintf(ctx->pool, "max-age=%d", basemap->expires)); timestr = apr_palloc(ctx->pool, APR_RFC822_DATE_LEN); apr_rfc822_date(timestr, texpires); apr_table_setn(response->headers, "Expires", timestr); } response->mtime = basemap->mtime; return response; } mapcache_http_response *mapcache_core_proxy_request(mapcache_context *ctx, mapcache_request_proxy *req_proxy) { mapcache_http *http; mapcache_http_response *response = mapcache_http_response_create(ctx->pool); response->data = mapcache_buffer_create(30000,ctx->pool); http = mapcache_http_clone(ctx, req_proxy->rule->http); if(req_proxy->pathinfo) { if( (*(req_proxy->pathinfo)) == '/' || http->url[strlen(http->url)-1] == '/') http->url = apr_pstrcat(ctx->pool,http->url,req_proxy->pathinfo,NULL); else http->url = apr_pstrcat(ctx->pool,http->url,"/",req_proxy->pathinfo,NULL); } http->url = mapcache_http_build_url(ctx,http->url,req_proxy->params); http->post_body = req_proxy->post_buf; http->post_len = req_proxy->post_len; if(req_proxy->headers) { apr_table_overlap(http->headers, req_proxy->headers, APR_OVERLAP_TABLES_SET); } mapcache_http_do_request(ctx,http, response->data,response->headers,&response->code); if(response->code !=0 && GC_HAS_ERROR(ctx)) { /* the http request was successful, but the server returned an error */ ctx->clear_errors(ctx); } /*remove some headers that should not be sent back to the client*/ apr_table_unset(response->headers,"Transfer-Encoding"); apr_table_unset(response->headers,"Connection"); return response; } mapcache_http_response *mapcache_core_get_featureinfo(mapcache_context *ctx, mapcache_request_get_feature_info *req_fi) { mapcache_feature_info *fi = req_fi->fi; mapcache_tileset *tileset = fi->map.tileset; if(!tileset->source) { ctx->set_error(ctx,404,"cannot query tileset %s: no source defined",tileset->name); return NULL; } if(tileset->source->info_formats) { int i; mapcache_http_response *response; for(i=0; isource->info_formats->nelts; i++) { if(!strcmp(fi->format, APR_ARRAY_IDX(tileset->source->info_formats,i,char*))) { break; } } if(i == tileset->source->info_formats->nelts) { ctx->set_error(ctx,404, "unsupported feature info format %s",fi->format); return NULL; } tileset->source->query_info(ctx,fi); if(GC_HAS_ERROR(ctx)) return NULL; response = mapcache_http_response_create(ctx->pool); response->data = fi->data; apr_table_set(response->headers,"Content-Type",fi->format); return response; } else { ctx->set_error(ctx,404, "tileset %s does not support feature info requests"); return NULL; } } mapcache_http_response* mapcache_core_get_capabilities(mapcache_context *ctx, mapcache_service *service, mapcache_request_get_capabilities *req_caps, char *url, char *path_info, mapcache_cfg *config) { mapcache_http_response *response; service->create_capabilities_response(ctx,req_caps,url,path_info,config); if(GC_HAS_ERROR(ctx)) { return NULL; } response = mapcache_http_response_create(ctx->pool); response->data = mapcache_buffer_create(0,ctx->pool); response->data->size = strlen(req_caps->capabilities); response->data->buf = req_caps->capabilities; response->data->avail = response->data->size; apr_table_set(response->headers,"Content-Type",req_caps->mime_type); return response; } mapcache_http_response* mapcache_core_respond_to_error(mapcache_context *ctx) { char *msg; //TODO: have the service format the error response mapcache_http_response *response = mapcache_http_response_create(ctx->pool); /* extract code and message from context */ response->code = ctx->_errcode; if(!response->code) response->code = 500; msg = ctx->_errmsg; if(!msg) { msg = apr_pstrdup(ctx->pool,"an unspecified error has occured"); } ctx->log(ctx,MAPCACHE_ERROR,msg); if(ctx->config && ctx->config->reporting == MAPCACHE_REPORT_MSG) { char *err_body = msg; apr_table_set(response->headers, "Content-Type", "text/plain"); if(ctx->service && ctx->service->format_error) { ctx->service->format_error(ctx,ctx->service,msg,&err_body,response->headers); } /* manually populate the mapcache_buffer with the error message */ response->data = mapcache_buffer_create(0,ctx->pool); response->data->size = strlen(err_body); response->data->buf = err_body; response->data->avail = response->data->size; } else if(ctx->config && ctx->config->reporting == MAPCACHE_REPORT_EMPTY_IMG) { response->data = ctx->config->empty_image; apr_table_set(response->headers, "Content-Type", ctx->config->default_image_format->mime_type); apr_table_set(response->headers, "X-Mapcache-Error", msg); } else if(ctx->config && ctx->config->reporting == MAPCACHE_REPORT_ERROR_IMG) { mapcache_image *errim = mapcache_error_image(ctx,256,256,msg); mapcache_buffer *buf = ctx->config->default_image_format->write(ctx,errim,ctx->config->default_image_format); response->data = buf; apr_table_set(response->headers, "Content-Type", ctx->config->default_image_format->mime_type); apr_table_set(response->headers, "X-Mapcache-Error", msg); } return response; } /* vim: ts=2 sts=2 et sw=2 */ mapcache-1.4.0/lib/dimension.c000066400000000000000000000657531255567662100162130ustar00rootroot00000000000000/****************************************************************************** * $Id$ * * Project: MapServer * Purpose: MapCache tile caching support file: OGC dimensions * 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 #include #include #include #include #ifdef USE_SQLITE #include #include #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; inintervals; i++) { double diff; mapcache_interval *interval = &dimension->intervals[i]; if(valstart || 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; inintervals; 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); } 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"); 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); return; } key = endptr+1; 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); return; } dimension->nintervals++; } if(!dimension->nintervals) { ctx->set_error(ctx, 400, " \"%s\" has no intervals",dim->name); return; } } static int _mapcache_dimension_regex_validate(mapcache_context *ctx, mapcache_dimension *dim, char **value) { mapcache_dimension_regex *dimension = (mapcache_dimension_regex*)dim; #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; #else if(!regexec(dimension->regex,*value,0,0,0)) { return MAPCACHE_SUCCESS; } #endif return MAPCACHE_FAILURE; } static apr_array_header_t* _mapcache_dimension_regex_print(mapcache_context *ctx, mapcache_dimension *dim) { mapcache_dimension_regex *dimension = (mapcache_dimension_regex*)dim; apr_array_header_t *ret = apr_array_make(ctx->pool,1,sizeof(char*)); APR_ARRAY_PUSH(ret,char*) = apr_pstrdup(ctx->pool,dimension->regex_string); return ret; } static void _mapcache_dimension_regex_parse_xml(mapcache_context *ctx, mapcache_dimension *dim, 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"); 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); if(!dimension->pcregex) { ctx->set_error(ctx,400,"failed to compile regular expression \"%s\" for dimension \"%s\": %s", entry,dim->name,pcre_err); return; } } #else { int rc = regcomp(dimension->regex, entry, 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); return; } } #endif } static int _mapcache_dimension_values_validate(mapcache_context *ctx, mapcache_dimension *dim, char **value) { int i; mapcache_dimension_values *dimension = (mapcache_dimension_values*)dim; for(i=0; invalues; i++) { if(dimension->case_sensitive) { if(!strcmp(*value,dimension->values[i])) return MAPCACHE_SUCCESS; } else { if(!strcasecmp(*value,dimension->values[i])) return MAPCACHE_SUCCESS; } } return MAPCACHE_FAILURE; } static apr_array_header_t* _mapcache_dimension_values_print(mapcache_context *ctx, mapcache_dimension *dim) { mapcache_dimension_values *dimension = (mapcache_dimension_values*)dim; apr_array_header_t *ret = apr_array_make(ctx->pool,dimension->nvalues,sizeof(char*)); int i; for(i=0; invalues; i++) { APR_ARRAY_PUSH(ret,char*) = apr_pstrdup(ctx->pool,dimension->values[i]); } return ret; } 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"); return; } dimension = (mapcache_dimension_values*)dim; case_sensitive = ezxml_attr(node,"case_sensitive"); if(case_sensitive && !strcasecmp(case_sensitive,"true")) { dimension->case_sensitive = 1; } 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++; } if(!dimension->nvalues) { ctx->set_error(ctx, 400, " \"%s\" has no values",dim->name); return; } } mapcache_dimension* mapcache_dimension_values_create(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->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; 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_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.configuration_parse_xml = _mapcache_dimension_regex_parse_xml; dimension->dimension.print_ogc_formatted_values = _mapcache_dimension_regex_print; return (mapcache_dimension*)dimension; } #ifdef USE_SQLITE struct sqlite_dimension_conn { sqlite3 *handle; sqlite3_stmt **prepared_statements; int n_statements; }; void mapcache_sqlite_dimension_connection_constructor(mapcache_context *ctx, void **conn_, void *params, apr_pool_t *pool) { int ret; int flags; char *dbfile = (char*) params; struct sqlite_dimension_conn *conn = calloc(1, sizeof (struct sqlite_dimension_conn)); *conn_ = conn; flags = SQLITE_OPEN_READONLY | SQLITE_OPEN_NOMUTEX; ret = sqlite3_open_v2(dbfile, &conn->handle, flags, NULL); if (ret != SQLITE_OK) { ctx->set_error(ctx,500,"failed to open sqlite dimension dbfile (%s): %s",dbfile,sqlite3_errmsg(conn->handle)); sqlite3_close(conn->handle); *conn_=NULL; return; } sqlite3_busy_timeout(conn->handle, 300000); } void mapcache_sqlite_dimension_connection_destructor(void *conn_, apr_pool_t *pool) { struct sqlite_dimension_conn *conn = (struct sqlite_dimension_conn*) conn_; while(conn->n_statements) { conn->n_statements--; if(conn->prepared_statements[conn->n_statements]) { sqlite3_finalize(conn->prepared_statements[conn->n_statements]); } } free(conn->prepared_statements); sqlite3_close(conn->handle); 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, mapcache_sqlite_dimension_connection_constructor, mapcache_sqlite_dimension_connection_destructor, dim->dbfile); return pc; } static void _sqlite_dimension_release_conn(mapcache_context *ctx, mapcache_pooled_connection *pc) { if(GC_HAS_ERROR(ctx)) { mapcache_connection_pool_invalidate_connection(ctx,pc); } else { mapcache_connection_pool_release_connection(ctx,pc); } } static int _mapcache_dimension_sqlite_validate(mapcache_context *ctx, mapcache_dimension *dim, char **value) { 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); if (GC_HAS_ERROR(ctx)) { return ret; } conn = pc->connection; if(!conn->prepared_statements) { conn->prepared_statements = calloc(2,sizeof(sqlite3_stmt*)); 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) { 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; } } do { sqliteret = sqlite3_step(conn->prepared_statements[0]); if (sqliteret != SQLITE_DONE && sqliteret != SQLITE_ROW && sqliteret != SQLITE_BUSY && sqliteret != SQLITE_LOCKED) { ctx->set_error(ctx, 500, "sqlite dimension backend failed on query : %s (%d)", sqlite3_errmsg(conn->handle), sqliteret); 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; } } while (sqliteret == SQLITE_ROW || sqliteret == SQLITE_BUSY || sqliteret == SQLITE_LOCKED); cleanup: if(conn->prepared_statements[0]) { sqlite3_reset(conn->prepared_statements[0]); } _sqlite_dimension_release_conn(ctx,pc); return ret; } static apr_array_header_t* _mapcache_dimension_sqlite_print(mapcache_context *ctx, mapcache_dimension *dim) { 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); if (GC_HAS_ERROR(ctx)) { return ret; } conn = pc->connection; if(!conn->prepared_statements) { conn->prepared_statements = calloc(2,sizeof(sqlite3_stmt*)); conn->n_statements = 2; } if(!conn->prepared_statements[1]) { sqliteret = sqlite3_prepare_v2(conn->handle, dimension->list_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; } } do { sqliteret = sqlite3_step(conn->prepared_statements[1]); if (sqliteret != SQLITE_DONE && sqliteret != SQLITE_ROW && sqliteret != SQLITE_BUSY && sqliteret != SQLITE_LOCKED) { ctx->set_error(ctx, 500, "sqlite dimension backend failed on query : %s (%d)", sqlite3_errmsg(conn->handle), sqliteret); goto cleanup; } if(sqliteret == SQLITE_ROW) { const char* sqdim = (const char*) sqlite3_column_text(conn->prepared_statements[1], 0); APR_ARRAY_PUSH(ret,char*) = apr_pstrdup(ctx->pool,sqdim); } } while (sqliteret == SQLITE_ROW || sqliteret == SQLITE_BUSY || sqliteret == SQLITE_LOCKED); cleanup: if(conn->prepared_statements[1]) { sqlite3_reset(conn->prepared_statements[1]); } _sqlite_dimension_release_conn(ctx,pc); return ret; } static void _mapcache_dimension_sqlite_parse_xml(mapcache_context *ctx, mapcache_dimension *dim, ezxml_t node) { mapcache_dimension_sqlite *dimension; ezxml_t child; dimension = (mapcache_dimension_sqlite*)dim; child = ezxml_child(node,"dbfile"); if(child) { dimension->dbfile = apr_pstrdup(ctx->pool, child->txt); } else { ctx->set_error(ctx,400,"sqlite dimension \"%s\" has no node", dim->name); return; } child = ezxml_child(node,"validate_query"); if(child) { dimension->validate_query = apr_pstrdup(ctx->pool, child->txt); } else { ctx->set_error(ctx,400,"sqlite dimension \"%s\" has no node", dim->name); return; } child = ezxml_child(node,"list_query"); if(child) { dimension->list_query = apr_pstrdup(ctx->pool, child->txt); } else { ctx->set_error(ctx,400,"sqlite dimension \"%s\" has no node", dim->name); return; } } static void _bind_sqlite_timedimension_params(mapcache_context *ctx, sqlite3_stmt *stmt, sqlite3 *handle, 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; } } paramidx = sqlite3_bind_parameter_index(stmt, ":start_timestamp"); if (paramidx) { ret = sqlite3_bind_int64(stmt, paramidx, start); if(ret != SQLITE_OK) { ctx->set_error(ctx,400, "failed to bind :start_timestamp: %s", sqlite3_errmsg(handle)); return; } } paramidx = sqlite3_bind_parameter_index(stmt, ":end_timestamp"); if (paramidx) { ret = sqlite3_bind_int64(stmt, paramidx, end); if(ret != SQLITE_OK) { ctx->set_error(ctx,400, "failed to bind :end_timestamp: %s", sqlite3_errmsg(handle)); return; } } } 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 *time_ids = NULL; mapcache_pooled_connection *pc; struct sqlite_dimension_conn *conn; pc = _sqlite_time_dimension_get_conn(ctx,sdim); if (GC_HAS_ERROR(ctx)) { return NULL; } conn = pc->connection; if(!conn->prepared_statements) { conn->prepared_statements = calloc(1,sizeof(sqlite3_stmt*)); conn->n_statements = 1; } if(!conn->prepared_statements[0]) { ret = sqlite3_prepare_v2(conn->handle, sdim->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; } } _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 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); } #endif typedef enum { MAPCACHE_TINTERVAL_SECOND, MAPCACHE_TINTERVAL_MINUTE, MAPCACHE_TINTERVAL_HOUR, MAPCACHE_TINTERVAL_DAY, MAPCACHE_TINTERVAL_MONTH, MAPCACHE_TINTERVAL_YEAR } mapcache_time_interval_t; #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; 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 entry for %s",dim->key); return; } child = ezxml_child(node,"query"); if(child && child->txt && *child->txt) { sdim->query = apr_pstrdup(ctx->pool,child->txt); } else { ctx->set_error(ctx,400,"no entry for %s",dim->key); return; } } #endif char *mapcache_ogc_strptime(const char *value, struct tm *ts, mapcache_time_interval_t *ti) { memset (ts, '\0', sizeof (*ts)); char *valueptr; valueptr = strptime(value,"%Y-%m-%dT%H:%M:%SZ",ts); *ti = MAPCACHE_TINTERVAL_SECOND; if(valueptr) return valueptr; valueptr = strptime(value,"%Y-%m-%dT%H:%MZ",ts); *ti = MAPCACHE_TINTERVAL_MINUTE; if(valueptr) return valueptr; valueptr = strptime(value,"%Y-%m-%dT%HZ",ts); *ti = MAPCACHE_TINTERVAL_HOUR; if(valueptr) return valueptr; valueptr = strptime(value,"%Y-%m-%d",ts); *ti = MAPCACHE_TINTERVAL_DAY; if(valueptr) return valueptr; valueptr = strptime(value,"%Y-%m",ts); *ti = MAPCACHE_TINTERVAL_MONTH; if(valueptr) return valueptr; valueptr = strptime(value,"%Y",ts); *ti = MAPCACHE_TINTERVAL_YEAR; if(valueptr) return valueptr; 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) { /* 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; 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); 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; } 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; } end = timegm(&tm_end); } return timedimension->get_entries_for_interval(ctx,timedimension,tileset,grid,extent,start,end); /* end loop */ } mapcache_dimension* mapcache_dimension_sqlite_create(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.configuration_parse_xml = _mapcache_dimension_sqlite_parse_xml; dimension->dimension.print_ogc_formatted_values = _mapcache_dimension_sqlite_print; return (mapcache_dimension*)dimension; #else return NULL; #endif } #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; } #endif /* vim: ts=2 sts=2 et sw=2 */ mapcache-1.4.0/lib/ezxml.c000066400000000000000000001106141255567662100153500ustar00rootroot00000000000000/* ezxml.c * * Copyright 2004-2006 Aaron Voisine * * 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 or substantial portions of the 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 #include #include #include #include #ifndef _WIN32 #include #else #define EZXML_NOMMAP 1 #define snprintf _snprintf #define vsnprintf _vsnprintf #endif #include #ifndef EZXML_NOMMAP #include #endif // EZXML_NOMMAP #include #include "ezxml.h" #define EZXML_WS "\t\r\n " // whitespace #define EZXML_ERRL 128 // maximum error string length typedef struct ezxml_root *ezxml_root_t; struct ezxml_root { // additional data for the root tag struct ezxml xml; // is a super-struct built on top of ezxml struct ezxml_t cur; // current xml tree insertion point char *m; // original xml string size_t len; // length of allocated memory for mmap, -1 for malloc char *u; // UTF-8 conversion of string if original was UTF-16 char *s; // start of work area char *e; // end of work area char **ent; // general entities (ampersand sequences) char ***attr; // default attributes char ***pi; // processing instructions short standalone; // non-zero if char err[EZXML_ERRL]; // error string }; char *EZXML_NIL[] = { NULL }; // empty, null terminated array of strings // returns the first child tag with the given name or NULL if not found ezxml_t ezxml_child(ezxml_t xml, const char *name) { xml = (xml) ? xml->child : NULL; while (xml && strcmp(name, xml->name)) xml = xml->sibling; return xml; } // returns the Nth tag with the same name in the same subsection or NULL if not // found ezxml_t ezxml_idx(ezxml_t xml, int idx) { for (; xml && idx; idx--) xml = xml->next; return xml; } // returns the value of the requested tag attribute or NULL if not found const char *ezxml_attr(ezxml_t xml, const char *attr) { int i = 0, j = 1; ezxml_root_t root = (ezxml_root_t)xml; if (! xml || ! xml->attr) return NULL; while (xml->attr[i] && strcmp(attr, xml->attr[i])) i += 2; if (xml->attr[i]) return xml->attr[i + 1]; // found attribute while (root->xml.parent) root = (ezxml_root_t)root->xml.parent; // root tag for (i = 0; root->attr[i] && strcmp(xml->name, root->attr[i][0]); i++); if (! root->attr[i]) return NULL; // no matching default attributes while (root->attr[i][j] && strcmp(attr, root->attr[i][j])) j += 3; return (root->attr[i][j]) ? root->attr[i][j + 1] : NULL; // found default } // same as ezxml_get but takes an already initialized va_list ezxml_t ezxml_vget(ezxml_t xml, va_list ap) { char *name = va_arg(ap, char *); int idx = -1; if (name && *name) { idx = va_arg(ap, int); xml = ezxml_child(xml, name); } return (idx < 0) ? xml : ezxml_vget(ezxml_idx(xml, idx), ap); } // Traverses the xml tree to retrieve a specific subtag. Takes a variable // length list of tag names and indexes. The argument list must be terminated // by either an index of -1 or an empty string tag name. Example: // title = ezxml_get(library, "shelf", 0, "book", 2, "title", -1); // This retrieves the title of the 3rd book on the 1st shelf of library. // Returns NULL if not found. ezxml_t ezxml_get(ezxml_t xml, ...) { va_list ap; ezxml_t r; va_start(ap, xml); r = ezxml_vget(xml, ap); va_end(ap); return r; } // returns a null terminated array of processing instructions for the given // target const char **ezxml_pi(ezxml_t xml, const char *target) { ezxml_root_t root = (ezxml_root_t)xml; int i = 0; if (! root) return (const char **)EZXML_NIL; while (root->xml.parent) root = (ezxml_root_t)root->xml.parent; // root tag while (root->pi[i] && strcmp(target, root->pi[i][0])) i++; // find target return (const char **)((root->pi[i]) ? root->pi[i] + 1 : EZXML_NIL); } // set an error string and return root ezxml_t ezxml_err(ezxml_root_t root, char *s, const char *err, ...) { va_list ap; int line = 1; char *t, fmt[EZXML_ERRL]; for (t = root->s; t < s; t++) if (*t == '\n') line++; snprintf(fmt, EZXML_ERRL, "[error near line %d]: %s", line, err); va_start(ap, err); vsnprintf(root->err, EZXML_ERRL, fmt, ap); va_end(ap); return &root->xml; } // Recursively decodes entity and character references and normalizes new lines // ent is a null terminated array of alternating entity names and values. set t // to '&' for general entity decoding, '%' for parameter entity decoding, 'c' // for cdata sections, ' ' for attribute normalization, or '*' for non-cdata // attribute normalization. Returns s, or if the decoded string is longer than // s, returns a malloced string that must be freed. char *ezxml_decode(char *s, char **ent, char t) { char *e, *r = s, *m = s; long b, c, d, l; for (; *s; s++) { // normalize line endings while (*s == '\r') { *(s++) = '\n'; if (*s == '\n') memmove(s, (s + 1), strlen(s)); } } for (s = r; ; ) { while (*s && *s != '&' && (*s != '%' || t != '%') && !isspace(*s)) s++; if (! *s) break; else if (t != 'c' && ! strncmp(s, "&#", 2)) { // character reference if (s[2] == 'x') c = strtol(s + 3, &e, 16); // base 16 else c = strtol(s + 2, &e, 10); // base 10 if (! c || *e != ';') { s++; // not a character ref continue; } if (c < 0x80) *(s++) = c; // US-ASCII subset else { // multi-byte UTF-8 sequence for (b = 0, d = c; d; d /= 2) b++; // number of bits in c b = (b - 2) / 5; // number of bytes in payload *(s++) = (0xFF << (7 - b)) | (c >> (6 * b)); // head while (b) *(s++) = 0x80 | ((c >> (6 * --b)) & 0x3F); // payload } memmove(s, strchr(s, ';') + 1, strlen(strchr(s, ';'))); } else if ((*s == '&' && (t == '&' || t == ' ' || t == '*')) || (*s == '%' && t == '%')) { // entity reference for (b = 0; ent[b] && strncmp(s + 1, ent[b], strlen(ent[b])); b += 2); // find entity in entity list if (ent[b++]) { // found a match if ((c = strlen(ent[b])) - 1 > (e = strchr(s, ';')) - s) { l = (d = (s - r)) + c + strlen(e); // new length r = (r == m) ? strcpy(malloc(l), r) : realloc(r, l); e = strchr((s = r + d), ';'); // fix up pointers } memmove(s + c, e + 1, strlen(e)); // shift rest of string strncpy(s, ent[b], c); // copy in replacement text } else s++; // not a known entity } else if ((t == ' ' || t == '*') && isspace(*s)) *(s++) = ' '; else s++; // no decoding needed } if (t == '*') { // normalize spaces for non-cdata attributes for (s = r; *s; s++) { if ((l = strspn(s, " "))) memmove(s, s + l, strlen(s + l) + 1); while (*s && *s != ' ') s++; } if (--s >= r && *s == ' ') *s = '\0'; // trim any trailing space } return r; } // called when parser finds start of new tag void ezxml_open_tag(ezxml_root_t root, char *name, char **attr) { ezxml_t xml = root->cur; if (xml->name) xml = ezxml_add_child(xml, name, strlen(xml->txt)); else xml->name = name; // first open tag xml->attr = attr; root->cur = xml; // update tag insertion point } // called when parser finds character content between open and closing tag void ezxml_char_content(ezxml_root_t root, char *s, size_t len, char t) { ezxml_t xml = root->cur; char *m = s; size_t l; if (! xml || ! xml->name || ! len) return; // sanity check s[len] = '\0'; // null terminate text (calling functions anticipate this) len = strlen(s = ezxml_decode(s, root->ent, t)) + 1; if (! *(xml->txt)) xml->txt = s; // initial character content else { // allocate our own memory and make a copy xml->txt = (xml->flags & EZXML_TXTM) // allocate some space ? realloc(xml->txt, (l = strlen(xml->txt)) + len) : strcpy(malloc((l = strlen(xml->txt)) + len), xml->txt); strcpy(xml->txt + l, s); // add new char content if (s != m) free(s); // free s if it was malloced by ezxml_decode() } if (xml->txt != m) ezxml_set_flag(xml, EZXML_TXTM); } // called when parser finds closing tag ezxml_t ezxml_close_tag(ezxml_root_t root, char *name, char *s) { if (! root->cur || ! root->cur->name || strcmp(name, root->cur->name)) return ezxml_err(root, s, "unexpected closing tag ", name); root->cur = root->cur->parent; return NULL; } // checks for circular entity references, returns non-zero if no circular // references are found, zero otherwise int ezxml_ent_ok(char *name, char *s, char **ent) { int i; for (; ; s++) { while (*s && *s != '&') s++; // find next entity reference if (! *s) return 1; if (! strncmp(s + 1, name, strlen(name))) return 0; // circular ref. for (i = 0; ent[i] && strncmp(ent[i], s + 1, strlen(ent[i])); i += 2); if (ent[i] && ! ezxml_ent_ok(name, ent[i + 1], ent)) return 0; } } // called when the parser finds a processing instruction void ezxml_proc_inst(ezxml_root_t root, char *s, size_t len) { int i = 0, j = 1; char *target = s; s[len] = '\0'; // null terminate instruction if (*(s += strcspn(s, EZXML_WS))) { *s = '\0'; // null terminate target s += strspn(s + 1, EZXML_WS) + 1; // skip whitespace after target } if (! strcmp(target, "xml")) { // if ((s = strstr(s, "standalone")) && ! strncmp(s + strspn(s + 10, EZXML_WS "='\"") + 10, "yes", 3)) root->standalone = 1; return; } if (! root->pi[0]) *(root->pi = malloc(sizeof(char **))) = NULL; //first pi while (root->pi[i] && strcmp(target, root->pi[i][0])) i++; // find target if (! root->pi[i]) { // new target root->pi = realloc(root->pi, sizeof(char **) * (i + 2)); root->pi[i] = malloc(sizeof(char *) * 3); root->pi[i][0] = target; root->pi[i][1] = (char *)(root->pi[i + 1] = NULL); // terminate pi list root->pi[i][2] = strdup(""); // empty document position list } while (root->pi[i][j]) j++; // find end of instruction list for this target root->pi[i] = realloc(root->pi[i], sizeof(char *) * (j + 3)); root->pi[i][j + 2] = realloc(root->pi[i][j + 1], j + 1); strcpy(root->pi[i][j + 2] + j - 1, (root->xml.name) ? ">" : "<"); root->pi[i][j + 1] = NULL; // null terminate pi list for this target root->pi[i][j] = s; // set instruction } // called when the parser finds an internal doctype subset short ezxml_internal_dtd(ezxml_root_t root, char *s, size_t len) { char q, *c, *t, *n = NULL, *v, **ent, **pe; int i, j; pe = memcpy(malloc(sizeof(EZXML_NIL)), EZXML_NIL, sizeof(EZXML_NIL)); for (s[len] = '\0'; s; ) { while (*s && *s != '<' && *s != '%') s++; // find next declaration if (! *s) break; else if (! strncmp(s, "'); continue; } for (i = 0, ent = (*c == '%') ? pe : root->ent; ent[i]; i++); ent = realloc(ent, (i + 3) * sizeof(char *)); // space for next ent if (*c == '%') pe = ent; else root->ent = ent; *(++s) = '\0'; // null terminate name if ((s = strchr(v, q))) *(s++) = '\0'; // null terminate value ent[i + 1] = ezxml_decode(v, pe, '%'); // set value ent[i + 2] = NULL; // null terminate entity list if (! ezxml_ent_ok(n, ent[i + 1], ent)) { // circular reference if (ent[i + 1] != v) free(ent[i + 1]); ezxml_err(root, v, "circular entity declaration &%s", n); break; } else ent[i] = n; // set entity name } else if (! strncmp(s, "")) == '>') continue; else *s = '\0'; // null terminate tag name for (i = 0; root->attr[i] && strcmp(n, root->attr[i][0]); i++); while (*(n = ++s + strspn(s, EZXML_WS)) && *n != '>') { if (*(s = n + strcspn(n, EZXML_WS))) *s = '\0'; // attr name else { ezxml_err(root, t, "malformed ") - 1; if (*c == ' ') continue; // cdata is default, nothing to do v = NULL; } else if ((*s == '"' || *s == '\'') && // default value (s = strchr(v = s + 1, *s))) *s = '\0'; else { ezxml_err(root, t, "malformed attr[i]) { // new tag name root->attr = (! i) ? malloc(2 * sizeof(char **)) : realloc(root->attr, (i + 2) * sizeof(char **)); root->attr[i] = malloc(2 * sizeof(char *)); root->attr[i][0] = t; // set tag name root->attr[i][1] = (char *)(root->attr[i + 1] = NULL); } for (j = 1; root->attr[i][j]; j += 3); // find end of list root->attr[i] = realloc(root->attr[i], (j + 4) * sizeof(char *)); root->attr[i][j + 3] = NULL; // null terminate list root->attr[i][j + 2] = c; // is it cdata? root->attr[i][j + 1] = (v) ? ezxml_decode(v, root->ent, *c) : NULL; root->attr[i][j] = n; // attribute name } } else if (! strncmp(s, ""); // comments else if (! strncmp(s, ""))) ezxml_proc_inst(root, c, s++ - c); } else if (*s == '<') s = strchr(s, '>'); // skip other declarations else if (*(s++) == '%' && ! root->standalone) break; } free(pe); return ! *root->err; } // Converts a UTF-16 string to UTF-8. Returns a new string that must be freed // or NULL if no conversion was needed. char *ezxml_str2utf8(char **s, size_t *len) { char *u; size_t l = 0, sl, max = *len; long c, d; int b, be = (**s == '\xFE') ? 1 : (**s == '\xFF') ? 0 : -1; if (be == -1) return NULL; // not UTF-16 u = malloc(max); for (sl = 2; sl < *len - 1; sl += 2) { c = (be) ? (((*s)[sl] & 0xFF) << 8) | ((*s)[sl + 1] & 0xFF) //UTF-16BE : (((*s)[sl + 1] & 0xFF) << 8) | ((*s)[sl] & 0xFF); //UTF-16LE if (c >= 0xD800 && c <= 0xDFFF && (sl += 2) < *len - 1) { // high-half d = (be) ? (((*s)[sl] & 0xFF) << 8) | ((*s)[sl + 1] & 0xFF) : (((*s)[sl + 1] & 0xFF) << 8) | ((*s)[sl] & 0xFF); c = (((c & 0x3FF) << 10) | (d & 0x3FF)) + 0x10000; } while (l + 6 > max) u = realloc(u, max += EZXML_BUFSIZE); if (c < 0x80) u[l++] = c; // US-ASCII subset else { // multi-byte UTF-8 sequence for (b = 0, d = c; d; d /= 2) b++; // bits in c b = (b - 2) / 5; // bytes in payload u[l++] = (0xFF << (7 - b)) | (c >> (6 * b)); // head while (b) u[l++] = 0x80 | ((c >> (6 * --b)) & 0x3F); // payload } } return *s = realloc(u, *len = l); } // frees a tag attribute list void ezxml_free_attr(char **attr) { int i = 0; char *m; if (! attr || attr == EZXML_NIL) return; // nothing to free while (attr[i]) i += 2; // find end of attribute list m = attr[i + 1]; // list of which names and values are malloced for (i = 0; m[i]; i++) { if (m[i] & EZXML_NAMEM) free(attr[i * 2]); if (m[i] & EZXML_TXTM) free(attr[(i * 2) + 1]); } free(m); free(attr); } // parse the given xml string and return an ezxml structure ezxml_t ezxml_parse_str(char *s, size_t len) { ezxml_root_t root = (ezxml_root_t)ezxml_new(NULL); char q, e, *d, **attr, **a = NULL; // initialize a to avoid compile warning int l, i, j; root->m = s; if (! len) return ezxml_err(root, NULL, "root tag missing"); root->u = ezxml_str2utf8(&s, &len); // convert utf-16 to utf-8 root->e = (root->s = s) + len; // record start and end of work area e = s[len - 1]; // save end char s[len - 1] = '\0'; // turn end char into null terminator while (*s && *s != '<') s++; // find first tag if (! *s) return ezxml_err(root, s, "root tag missing"); for (; ; ) { attr = (char **)EZXML_NIL; d = ++s; if (isalpha(*s) || *s == '_' || *s == ':' || *s < '\0') { // new tag if (! root->cur) return ezxml_err(root, d, "markup outside of root element"); s += strcspn(s, EZXML_WS "/>"); while (isspace(*s)) *(s++) = '\0'; // null terminate tag name if (*s && *s != '/' && *s != '>') // find tag in default attr list for (i = 0; (a = root->attr[i]) && strcmp(a[0], d); i++); for (l = 0; *s && *s != '/' && *s != '>'; l += 2) { // new attrib attr = (l) ? realloc(attr, (l + 4) * sizeof(char *)) : malloc(4 * sizeof(char *)); // allocate space attr[l + 3] = (l) ? realloc(attr[l + 1], (l / 2) + 2) : malloc(2); // mem for list of maloced vals strcpy(attr[l + 3] + (l / 2), " "); // value is not malloced attr[l + 2] = NULL; // null terminate list attr[l + 1] = ""; // temporary attribute value attr[l] = s; // set attribute name s += strcspn(s, EZXML_WS "=/>"); if (*s == '=' || isspace(*s)) { *(s++) = '\0'; // null terminate tag attribute name q = *(s += strspn(s, EZXML_WS "=")); if (q == '"' || q == '\'') { // attribute value attr[l + 1] = ++s; while (*s && *s != q) s++; if (*s) *(s++) = '\0'; // null terminate attribute val else { ezxml_free_attr(attr); return ezxml_err(root, d, "missing %c", q); } for (j = 1; a && a[j] && strcmp(a[j], attr[l]); j +=3); attr[l + 1] = ezxml_decode(attr[l + 1], root->ent, (a && a[j]) ? *a[j + 2] : ' '); if (attr[l + 1] < d || attr[l + 1] > s) attr[l + 3][l / 2] = EZXML_TXTM; // value malloced } } while (isspace(*s)) s++; } if (*s == '/') { // self closing tag *(s++) = '\0'; if ((*s && *s != '>') || (! *s && e != '>')) { if (l) ezxml_free_attr(attr); return ezxml_err(root, d, "missing >"); } ezxml_open_tag(root, d, attr); ezxml_close_tag(root, d, s); } else if ((q = *s) == '>' || (! *s && e == '>')) { // open tag *s = '\0'; // temporarily null terminate tag name ezxml_open_tag(root, d, attr); *s = q; } else { if (l) ezxml_free_attr(attr); return ezxml_err(root, d, "missing >"); } } else if (*s == '/') { // close tag s += strcspn(d = s + 1, EZXML_WS ">") + 1; if (! (q = *s) && e != '>') return ezxml_err(root, d, "missing >"); *s = '\0'; // temporarily null terminate tag name if (ezxml_close_tag(root, d, s)) return &root->xml; if (isspace(*s = q)) s += strspn(s, EZXML_WS); } else if (! strncmp(s, "!--", 3)) { // xml comment if (! (s = strstr(s + 3, "--")) || (*(s += 2) != '>' && *s) || (! *s && e != '>')) return ezxml_err(root, d, "unclosed \ %s\ "; if(!ctx->exceptions) { *err_body = msg; return; } exceptions=""; array = apr_table_elts(ctx->exceptions); elts = (apr_table_entry_t *) array->elts; for (i = 0; i < array->nelts; i++) { exceptions = apr_pstrcat(ctx->pool,exceptions,apr_psprintf(ctx->pool, "",elts[i].key,elts[i].val),NULL); } *err_body = apr_psprintf(ctx->pool,template,msg,exceptions); apr_table_set(headers, "Content-Type", "application/xml"); } mapcache_service* mapcache_service_wmts_create(mapcache_context *ctx) { mapcache_service_wmts* service = (mapcache_service_wmts*)apr_pcalloc(ctx->pool, sizeof(mapcache_service_wmts)); if(!service) { ctx->set_error(ctx, 500, "failed to allocate wtms service"); return NULL; } service->service.url_prefix = apr_pstrdup(ctx->pool,"wmts"); service->service.name = apr_pstrdup(ctx->pool,"wmts"); service->service.type = MAPCACHE_SERVICE_WMTS; service->service.parse_request = _mapcache_service_wmts_parse_request; service->service.create_capabilities_response = _create_capabilities_wmts; service->service.format_error = _error_report_wmts; return (mapcache_service*)service; } /** @} */ /* vim: ts=2 sts=2 et sw=2 */ mapcache-1.4.0/lib/services.c000066400000000000000000000056261255567662100160420ustar00rootroot00000000000000/****************************************************************************** * $Id$ * * Project: MapServer * Purpose: MapCache tile caching support file: high level service dispatching * 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 #include /** \addtogroup services */ /** @{ */ void mapcache_service_dispatch_request(mapcache_context *ctx, mapcache_request **request, char *pathinfo, apr_table_t *params, mapcache_cfg *config) { int i; /* skip empty pathinfo */ if(!pathinfo) { ctx->set_error(ctx,404,"missing a service"); return; } /*skip leading /'s */ while((*pathinfo) == '/') ++pathinfo; for(i=0; iservices[i]; if(!service) continue; /* skip an unconfigured service */ prefixlen = strlen(service->url_prefix); if(strncmp(service->url_prefix,pathinfo, prefixlen)) continue; /*skip a service who's prefix does not correspond */ ctx->service = service; //if(*(pathinfo+prefixlen)!='/' && *(pathinfo+prefixlen)!='\0') continue; /*we matched the prefix but there are trailing characters*/ pathinfo += prefixlen; /* advance pathinfo to after the service prefix */ service->parse_request(ctx,service,request,pathinfo,params,config); if(*request) (*request)->service = service; /* stop looping on services */ return; } ctx->set_error(ctx,404,"unknown service %s",pathinfo); } /** @} */ /* vim: ts=2 sts=2 et sw=2 */ mapcache-1.4.0/lib/source.c000066400000000000000000000034431255567662100155120ustar00rootroot00000000000000/****************************************************************************** * $Id$ * * Project: MapServer * Purpose: MapCache tile caching support file: common datasource functions * 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" 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); } /* vim: ts=2 sts=2 et sw=2 */ mapcache-1.4.0/lib/source_dummy.c000066400000000000000000000067061255567662100167320ustar00rootroot00000000000000/****************************************************************************** * $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 #include /** * \private \memberof mapcache_source_dummy * \sa mapcache_source::render_map() */ void _mapcache_source_dummy_render_map(mapcache_context *ctx, mapcache_map *map) { map->raw_image = mapcache_image_create(ctx); map->raw_image->w = map->width; map->raw_image->h = map->height; map->raw_image->stride = 4 * map->width; map->raw_image->data = malloc(map->width*map->height*4); memset(map->raw_image->data,255,map->width*map->height*4); 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) { ctx->set_error(ctx,500,"dummy source does not support queries"); } /** * \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) { } /** * \private \memberof mapcache_source_dummy * \sa mapcache_source::configuration_check() */ void _mapcache_source_dummy_configuration_check(mapcache_context *ctx, mapcache_cfg *cfg, mapcache_source *source) { } mapcache_source* mapcache_source_dummy_create(mapcache_context *ctx) { mapcache_source_dummy *source = apr_pcalloc(ctx->pool, sizeof(mapcache_source_dummy)); if(!source) { ctx->set_error(ctx, 500, "failed to allocate dummy source"); return NULL; } mapcache_source_init(ctx, &(source->source)); source->source.type = MAPCACHE_SOURCE_DUMMY; 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; return (mapcache_source*)source; } /* vim: ts=2 sts=2 et sw=2 */ mapcache-1.4.0/lib/source_gdal.c000066400000000000000000000253261255567662100165050ustar00rootroot00000000000000/****************************************************************************** * $Id$ * * Project: MapServer * Purpose: MapCache tile caching support file: GDAL datasource support (incomplete and disabled) * 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 #include #ifdef USE_GDAL #include #include #include "gdal_alg.h" #include "cpl_string.h" #include "ogr_srs_api.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; GDALAllRegister(); OGRSpatialReferenceH hSRS; CPLErrorReset(); 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; } OSRDestroySpatialReference( hSRS ); hDataset = GDALOpen( gdal->datastr, GA_ReadOnly ); if( hDataset == NULL ) { ctx->set_error(ctx,MAPCACHE_SOURCE_GDAL_ERROR,"GDAL failed to open %s",gdal->datastr); return; } /* -------------------------------------------------------------------- */ /* Check that there's at least one raster band */ /* -------------------------------------------------------------------- */ if ( GDALGetRasterCount(hDataset) == 0 ) { ctx->set_error(ctx,MAPCACHE_SOURCE_GDAL_ERROR,"raster %s has no bands",gdal->datastr); 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"); 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; } GDALDestroyGenImgProjTransformer( hTransformArg ); double dfXRes = (tile->bbox[2] - tile->bbox[0]) / tile->sx; double dfYRes = (tile->bbox[3] - tile->bbox[1]) / tile->sy; 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 ); /* -------------------------------------------------------------------- */ /* Write out the projection definition. */ /* -------------------------------------------------------------------- */ GDALSetProjection( hDstDS, dstSRS ); GDALSetGeoTransform( hDstDS, adfDstGeoTransform ); char **papszWarpOptions = NULL; papszWarpOptions = CSLSetNameValue( papszWarpOptions, "INIT", "0" ); /* -------------------------------------------------------------------- */ /* 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 ); if( hTransformArg == NULL ) exit( 1 ); pfnTransformer = GDALGenImgProjTransform; hTransformArg = hApproxArg = GDALCreateApproxTransformer( GDALGenImgProjTransform, hGenImgProjArg, 0.125 ); pfnTransformer = GDALApproxTransform; /* -------------------------------------------------------------------- */ /* Now actually invoke the warper to do the work. */ /* -------------------------------------------------------------------- */ GDALSimpleImageWarp( hDataset, hDstDS, 0, NULL, pfnTransformer, hTransformArg, GDALDummyProgress, NULL, papszWarpOptions ); CSLDestroy( papszWarpOptions ); if( hApproxArg != NULL ) GDALDestroyApproxTransformer( hApproxArg ); if( hGenImgProjArg != NULL ) GDALDestroyGenImgProjTransformer( hGenImgProjArg ); if(GDALGetRasterCount(hDstDS) != 4) { ctx->set_error(ctx,MAPCACHE_SOURCE_GDAL_ERROR,"gdal did not create a 4 band image"); return; } 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; 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; isx*tile->sy; i++, alphaptr+=4) { *alphaptr = 255; } } 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; GDALClose( hDstDS ); GDALClose( hDataset); } /** * \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) { ezxml_t cur_node; mapcache_source_gdal *src = (mapcache_source_gdal*)source; if ((cur_node = ezxml_child(node,"data")) != NULL) { src->datastr = apr_pstrdup(ctx->pool,cur_node->txt); } 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); } } } /** * \private \memberof mapcache_source_gdal * \sa mapcache_source::configuration_check() */ void _mapcache_source_gdal_configuration_check(mapcache_context *ctx, mapcache_cfg *cfg, mapcache_source *source) { mapcache_source_gdal *src = (mapcache_source_gdal*)source; /* 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); 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); return; } } #endif //USE_GDAL 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"); 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.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); return (mapcache_source*)source; #else ctx->set_error(ctx, 400, "failed to create gdal source, GDAL support is not compiled in this version"); return NULL; #endif } /* vim: ts=2 sts=2 et sw=2 */ mapcache-1.4.0/lib/source_mapserver.c000066400000000000000000000236331255567662100176010ustar00rootroot00000000000000/****************************************************************************** * $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" #ifdef USE_MAPSERVER #include #include #ifdef APR_HAS_THREADS #include #endif #include #include #include /* hash table key = source->name, value = apr_reslist_t of mapObjs */ static apr_hash_t *mapobj_container = NULL; struct mc_mapobj { mapObj *map; mapcache_grid_link *grid_link; char *error; }; static apr_status_t _ms_get_mapobj(void **conn_, void *params, apr_pool_t *pool) { 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); errors = msGetErrorObj(); mcmap->error = apr_psprintf(pool,"Failed to load mapfile '%s'. Mapserver reports: %s",src->mapfile, errors->message); return APR_EGENERAL; } msMapSetLayerProjections(mcmap->map); return APR_SUCCESS; } static apr_status_t _ms_free_mapobj(void *conn_, void *params, apr_pool_t *pool) { 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) { 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); } } /** * \private \memberof mapcache_source_mapserver * \sa mapcache_source::render_map() */ void _mapcache_source_mapserver_render_map(mapcache_context *ctx, mapcache_map *map) { errorObj *errors = NULL; struct mc_mapobj *mcmap = _get_mapboj(ctx,map); GC_CHECK_ERROR(ctx); if(mcmap->grid_link != map->grid_link) { if (msLoadProjectionString(&(mcmap->map->projection), map->grid_link->grid->srs) != 0) { errors = msGetErrorObj(); ctx->set_error(ctx,500, "Unable to set projection on mapObj. MapServer reports: %s", errors->message); _release_mapboj(ctx,map,mcmap); return; } switch(map->grid_link->grid->unit) { case MAPCACHE_UNIT_DEGREES: mcmap->map->units = MS_DD; break; case MAPCACHE_UNIT_FEET: mcmap->map->units = MS_FEET; break; case MAPCACHE_UNIT_METERS: mcmap->map->units = MS_METERS; break; } mcmap->grid_link = map->grid_link; } /* ** WMS extents are edge to edge while MapServer extents are center of ** 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); mcmap->map->extent.minx = map->extent.minx + dx; mcmap->map->extent.miny = map->extent.miny + dy; mcmap->map->extent.maxx = map->extent.maxx - dx; mcmap->map->extent.maxy = map->extent.maxy - dy; msMapSetSize(mcmap->map, map->width, map->height); imageObj *image = msDrawMap(mcmap->map, MS_FALSE); if(!image) { errors = msGetErrorObj(); ctx->set_error(ctx,500, "MapServer failed to create image. MapServer reports: %s", errors->message); _release_mapboj(ctx,map,mcmap); return; } rasterBufferObj rb; if(image->format->vtable->supports_pixel_buffer) { image->format->vtable->getRasterBufferHandle(image,&rb); } else { ctx->set_error(ctx,500,"format %s has no pixel export",image->format->name); _release_mapboj(ctx,map,mcmap); return; } map->raw_image = mapcache_image_create(ctx); map->raw_image->w = map->width; map->raw_image->h = map->height; map->raw_image->stride = 4 * map->width; map->raw_image->data = malloc(map->width*map->height*4); 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); } void _mapcache_source_mapserver_query(mapcache_context *ctx, mapcache_feature_info *fi) { ctx->set_error(ctx,500,"mapserver source does not support queries"); } /** * \private \memberof mapcache_source_mapserver * \sa mapcache_source::configuration_parse() */ void _mapcache_source_mapserver_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_source *source) { ezxml_t cur_node; mapcache_source_mapserver *src = (mapcache_source_mapserver*)source; if ((cur_node = ezxml_child(node,"mapfile")) != NULL) { src->mapfile = apr_pstrdup(ctx->pool,cur_node->txt); } } /** * \private \memberof mapcache_source_mapserver * \sa mapcache_source::configuration_check() */ void _mapcache_source_mapserver_configuration_check(mapcache_context *ctx, mapcache_cfg *cfg, mapcache_source *source) { mapcache_source_mapserver *src = (mapcache_source_mapserver*)source; /* check all required parameters are configured */ if(!src->mapfile) { ctx->set_error(ctx, 400, "mapserver source %s has no configured",source->name); } if(!src->mapfile) { ctx->set_error(ctx,400,"mapserver source \"%s\" has no mapfile configured",src->source.name); return; } msSetup(); /* do a test load to check the mapfile is correct */ mapObj *map = msLoadMap(src->mapfile, NULL); if(!map) { msWriteError(stderr); ctx->set_error(ctx,400,"failed to load mapfile \"%s\"",src->mapfile); return; } msFreeMap(map); } mapcache_source* mapcache_source_mapserver_create(mapcache_context *ctx) { mapcache_source_mapserver *source = apr_pcalloc(ctx->pool, sizeof(mapcache_source_mapserver)); if(!source) { ctx->set_error(ctx, 500, "failed to allocate mapserver source"); return NULL; } mapcache_source_init(ctx, &(source->source)); source->source.type = MAPCACHE_SOURCE_MAPSERVER; source->source.render_map = _mapcache_source_mapserver_render_map; source->source.configuration_check = _mapcache_source_mapserver_configuration_check; source->source.configuration_parse_xml = _mapcache_source_mapserver_configuration_parse_xml; source->source.query_info = _mapcache_source_mapserver_query; return (mapcache_source*)source; } #else mapcache_source* mapcache_source_mapserver_create(mapcache_context *ctx) { ctx->set_error(ctx, 500, "mapserver source not configured for this build"); return NULL; } #endif /* vim: ts=2 sts=2 et sw=2 */ mapcache-1.4.0/lib/source_wms.c000066400000000000000000000224441255567662100164020ustar00rootroot00000000000000/****************************************************************************** * $Id$ * * Project: MapServer * Purpose: MapCache tile caching support file: WMS datasources * 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 #include /** * \private \memberof mapcache_source_wms * \sa mapcache_source::render_map() */ void _mapcache_source_wms_render_map(mapcache_context *ctx, mapcache_map *map) { mapcache_source_wms *wms = (mapcache_source_wms*)map->tileset->source; 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", map->extent.minx,map->extent.miny,map->extent.maxx,map->extent.maxy)); apr_table_setn(params,"WIDTH",apr_psprintf(ctx->pool,"%d",map->width)); apr_table_setn(params,"HEIGHT",apr_psprintf(ctx->pool,"%d",map->height)); apr_table_setn(params,"FORMAT","image/png"); 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); int i; for(i=0; inelts; i++) { apr_table_entry_t entry = APR_ARRAY_IDX(elts,i,apr_table_entry_t); /* 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); } } } /* 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 */ if(!apr_table_get(params,"layers")) { apr_table_set(params,"LAYERS",map->tileset->name); } map->encoded_data = mapcache_buffer_create(30000,ctx->pool); http = mapcache_http_clone(ctx, wms->http); http->url = mapcache_http_build_url(ctx,http->url,params); mapcache_http_do_request(ctx,http,map->encoded_data,NULL,NULL); GC_CHECK_ERROR(ctx); if(!mapcache_imageio_is_valid_format(ctx,map->encoded_data)) { char *returned_data = apr_pstrndup(ctx->pool,(char*)map->encoded_data->buf,map->encoded_data->size); ctx->set_error(ctx, 502, "wms request for tileset %s returned an unsupported format:\n%s", map->tileset->name, returned_data); } } void _mapcache_source_wms_query(mapcache_context *ctx, mapcache_feature_info *fi) { mapcache_map *map = (mapcache_map*)fi; mapcache_http *http; mapcache_source_wms *wms = (mapcache_source_wms*)map->tileset->source; apr_table_t *params = apr_table_clone(ctx->pool,wms->wms_default_params); apr_table_overlap(params,wms->getmap_params,0); apr_table_setn(params,"BBOX",apr_psprintf(ctx->pool,"%f,%f,%f,%f", map->extent.minx,map->extent.miny,map->extent.maxx,map->extent.maxy)); apr_table_setn(params,"REQUEST","GetFeatureInfo"); apr_table_setn(params,"WIDTH",apr_psprintf(ctx->pool,"%d",map->width)); apr_table_setn(params,"HEIGHT",apr_psprintf(ctx->pool,"%d",map->height)); apr_table_setn(params,"SRS",map->grid_link->grid->srs); apr_table_setn(params,"X",apr_psprintf(ctx->pool,"%d",fi->i)); apr_table_setn(params,"Y",apr_psprintf(ctx->pool,"%d",fi->j)); 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); int i; for(i=0; inelts; i++) { apr_table_entry_t entry = APR_ARRAY_IDX(elts,i,apr_table_entry_t); apr_table_setn(params,entry.key,entry.val); } } fi->data = mapcache_buffer_create(30000,ctx->pool); http = mapcache_http_clone(ctx, wms->http); http->url = mapcache_http_build_url(ctx,http->url,params); mapcache_http_do_request(ctx,http,fi->data,NULL,NULL); GC_CHECK_ERROR(ctx); } /** * \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) { ezxml_t cur_node; mapcache_source_wms *src = (mapcache_source_wms*)source; if ((cur_node = ezxml_child(node,"getmap")) != NULL) { ezxml_t gm_node; if ((gm_node = ezxml_child(cur_node,"params")) != NULL) { for(gm_node = gm_node->child; gm_node; gm_node = gm_node->sibling) { apr_table_set(src->getmap_params, gm_node->name, gm_node->txt); } } else { ctx->set_error(ctx,400,"wms source %s has no block (should contain at least child)",source->name); return; } } else { ctx->set_error(ctx,400,"wms source %s has no block",source->name); return; } if ((cur_node = ezxml_child(node,"getfeatureinfo")) != NULL) { ezxml_t fi_node; if ((fi_node = ezxml_child(cur_node,"info_formats")) != NULL) { char *key,*last; char *iformats; source->info_formats = apr_array_make(ctx->pool,3,sizeof(char*)); iformats = apr_pstrdup(ctx->pool,fi_node->txt); for (key = apr_strtok(iformats, "," , &last); key != NULL; key = apr_strtok(NULL, ",", &last)) { APR_ARRAY_PUSH(source->info_formats,char*) = key; } } else { ctx->set_error(ctx,400,"wms source %s has no tag",source->name); return; } if ((fi_node = ezxml_child(cur_node,"params")) != NULL) { for(fi_node = fi_node->child; fi_node; fi_node = fi_node->sibling) { apr_table_set(src->getfeatureinfo_params, fi_node->name, fi_node->txt); } } else { ctx->set_error(ctx,400,"wms source %s has no block (should contain at least child)",source->name); return; } } if ((cur_node = ezxml_child(node,"http")) != NULL) { src->http = mapcache_http_configuration_parse_xml(ctx,cur_node); } } /** * \private \memberof mapcache_source_wms * \sa mapcache_source::configuration_check() */ void _mapcache_source_wms_configuration_check(mapcache_context *ctx, mapcache_cfg *cfg, mapcache_source *source) { mapcache_source_wms *src = (mapcache_source_wms*)source; /* check all required parameters are configured */ if(!src->http) { ctx->set_error(ctx, 400, "wms source %s has no request configured",source->name); } if(!apr_table_get(src->getmap_params,"LAYERS")) { if(cfg->mode == MAPCACHE_MODE_NORMAL) { ctx->set_error(ctx, 400, "wms source %s has no LAYERS", source->name); } } if(source->info_formats) { if(!apr_table_get(src->getfeatureinfo_params,"QUERY_LAYERS")) { ctx->set_error(ctx, 400, "wms source %s has no QUERY_LAYERS", source->name); } } } mapcache_source* mapcache_source_wms_create(mapcache_context *ctx) { mapcache_source_wms *source = apr_pcalloc(ctx->pool, sizeof(mapcache_source_wms)); if(!source) { ctx->set_error(ctx, 500, "failed to allocate wms source"); return NULL; } mapcache_source_init(ctx, &(source->source)); source->source.type = MAPCACHE_SOURCE_WMS; 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->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); apr_table_add(source->wms_default_params,"VERSION","1.1.1"); apr_table_add(source->wms_default_params,"REQUEST","GetMap"); apr_table_add(source->wms_default_params,"SERVICE","WMS"); apr_table_add(source->wms_default_params,"STYLES",""); return (mapcache_source*)source; } /* vim: ts=2 sts=2 et sw=2 */ mapcache-1.4.0/lib/strptime.c000066400000000000000000000242321255567662100160600ustar00rootroot00000000000000/* * Copyright (c) 1999 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. Neither the name of KTH nor the names of its contributors may be * used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #if defined(_WIN32) && !defined(__CYGWIN__) #include #include #include #include #include #include #include "util.h" static const char *abb_weekdays[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL }; static const char *full_weekdays[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", NULL }; static const char *abb_month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL }; static const char *full_month[] = { "January", "February", "Mars", "April", "May", "June", "July", "August", "September", "October", "November", "December", NULL, }; static const char *ampm[] = { "am", "pm", NULL }; /* * tm_year is relative this year */ const int tm_year_base = 1900; /* * Return TRUE iff `year' was a leap year. * Needed for strptime. */ static int is_leap_year (int year) { return (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0); } /* Needed for strptime. */ static int match_string (const char **buf, const char **strs) { int i = 0; for (i = 0; strs[i] != NULL; ++i) { int len = strlen (strs[i]); if (strncasecmp (*buf, strs[i], len) == 0) { *buf += len; return i; } } return -1; } /* Needed for strptime. */ static int first_day (int year) { int ret = 4; for (; year > 1970; --year) ret = (ret + 365 + is_leap_year (year) ? 1 : 0) % 7; return ret; } /* * Set `timeptr' given `wnum' (week number [0, 53]) * Needed for strptime */ static void set_week_number_sun (struct tm *timeptr, int wnum) { int fday = first_day (timeptr->tm_year + tm_year_base); timeptr->tm_yday = wnum * 7 + timeptr->tm_wday - fday; if (timeptr->tm_yday < 0) { timeptr->tm_wday = fday; timeptr->tm_yday = 0; } } /* * Set `timeptr' given `wnum' (week number [0, 53]) * Needed for strptime */ static void set_week_number_mon (struct tm *timeptr, int wnum) { int fday = (first_day (timeptr->tm_year + tm_year_base) + 6) % 7; timeptr->tm_yday = wnum * 7 + (timeptr->tm_wday + 6) % 7 - fday; if (timeptr->tm_yday < 0) { timeptr->tm_wday = (fday + 1) % 7; timeptr->tm_yday = 0; } } /* * Set `timeptr' given `wnum' (week number [0, 53]) * Needed for strptime */ static void set_week_number_mon4 (struct tm *timeptr, int wnum) { int fday = (first_day (timeptr->tm_year + tm_year_base) + 6) % 7; int offset = 0; if (fday < 4) offset += 7; timeptr->tm_yday = offset + (wnum - 1) * 7 + timeptr->tm_wday - fday; if (timeptr->tm_yday < 0) { timeptr->tm_wday = fday; timeptr->tm_yday = 0; } } /* strptime: roken */ /* extern "C" */ char * /* strptime (const char *buf, const char *format, struct tm *timeptr) */ strptime (const char *buf, const char *format, struct tm *timeptr) { char c; for (; (c = *format) != '\0'; ++format) { char *s; int ret; if (isspace (c)) { while (isspace (*buf)) ++buf; } else if (c == '%' && format[1] != '\0') { c = *++format; if (c == 'E' || c == 'O') c = *++format; switch (c) { case 'A' : ret = match_string (&buf, full_weekdays); if (ret < 0) return NULL; timeptr->tm_wday = ret; break; case 'a' : ret = match_string (&buf, abb_weekdays); if (ret < 0) return NULL; timeptr->tm_wday = ret; break; case 'B' : ret = match_string (&buf, full_month); if (ret < 0) return NULL; timeptr->tm_mon = ret; break; case 'b' : case 'h' : ret = match_string (&buf, abb_month); if (ret < 0) return NULL; timeptr->tm_mon = ret; break; case 'C' : ret = strtol (buf, &s, 10); if (s == buf) return NULL; timeptr->tm_year = (ret * 100) - tm_year_base; buf = s; break; case 'c' : abort (); case 'D' : /* %m/%d/%y */ s = strptime (buf, "%m/%d/%y", timeptr); if (s == NULL) return NULL; buf = s; break; case 'd' : case 'e' : ret = strtol (buf, &s, 10); if (s == buf) return NULL; timeptr->tm_mday = ret; buf = s; break; case 'H' : case 'k' : ret = strtol (buf, &s, 10); if (s == buf) return NULL; timeptr->tm_hour = ret; buf = s; break; case 'I' : case 'l' : ret = strtol (buf, &s, 10); if (s == buf) return NULL; if (ret == 12) timeptr->tm_hour = 0; else timeptr->tm_hour = ret; buf = s; break; case 'j' : ret = strtol (buf, &s, 10); if (s == buf) return NULL; timeptr->tm_yday = ret - 1; buf = s; break; case 'm' : ret = strtol (buf, &s, 10); if (s == buf) return NULL; timeptr->tm_mon = ret - 1; buf = s; break; case 'M' : ret = strtol (buf, &s, 10); if (s == buf) return NULL; timeptr->tm_min = ret; buf = s; break; case 'n' : if (*buf == '\n') ++buf; else return NULL; break; case 'p' : ret = match_string (&buf, ampm); if (ret < 0) return NULL; if (timeptr->tm_hour == 0) { if (ret == 1) timeptr->tm_hour = 12; } else timeptr->tm_hour += 12; break; case 'r' : /* %I:%M:%S %p */ s = strptime (buf, "%I:%M:%S %p", timeptr); if (s == NULL) return NULL; buf = s; break; case 'R' : /* %H:%M */ s = strptime (buf, "%H:%M", timeptr); if (s == NULL) return NULL; buf = s; break; case 'S' : ret = strtol (buf, &s, 10); if (s == buf) return NULL; timeptr->tm_sec = ret; buf = s; break; case 't' : if (*buf == '\t') ++buf; else return NULL; break; case 'T' : /* %H:%M:%S */ case 'X' : s = strptime (buf, "%H:%M:%S", timeptr); if (s == NULL) return NULL; buf = s; break; case 'u' : ret = strtol (buf, &s, 10); if (s == buf) return NULL; timeptr->tm_wday = ret - 1; buf = s; break; case 'w' : ret = strtol (buf, &s, 10); if (s == buf) return NULL; timeptr->tm_wday = ret; buf = s; break; case 'U' : ret = strtol (buf, &s, 10); if (s == buf) return NULL; set_week_number_sun (timeptr, ret); buf = s; break; case 'V' : ret = strtol (buf, &s, 10); if (s == buf) return NULL; set_week_number_mon4 (timeptr, ret); buf = s; break; case 'W' : ret = strtol (buf, &s, 10); if (s == buf) return NULL; set_week_number_mon (timeptr, ret); buf = s; break; case 'x' : s = strptime (buf, "%Y:%m:%d", timeptr); if (s == NULL) return NULL; buf = s; break; case 'y' : ret = strtol (buf, &s, 10); if (s == buf) return NULL; if (ret < 70) timeptr->tm_year = 100 + ret; else timeptr->tm_year = ret; buf = s; break; case 'Y' : ret = strtol (buf, &s, 10); if (s == buf) return NULL; timeptr->tm_year = ret - tm_year_base; buf = s; break; case 'Z' : abort (); case '\0' : --format; /* FALLTHROUGH */ case '%' : if (*buf == '%') ++buf; else return NULL; break; default : if (*buf == '%' || *++buf == c) ++buf; else return NULL; break; } } else { if (*buf == c) ++buf; else return NULL; } } return (char *)buf; } #endif mapcache-1.4.0/lib/tileset.c000066400000000000000000001041611255567662100156620ustar00rootroot00000000000000/****************************************************************************** * $Id$ * * Project: MapServer * Purpose: MapCache tile caching support file: high level tile access * 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 #include #include #include #ifdef _WIN32 #include #endif char* mapcache_tileset_metatile_resource_key(mapcache_context *ctx, mapcache_metatile *mt) { char *lockname = apr_psprintf(ctx->pool, "%d-%d-%d-%s", mt->z,mt->y,mt->x, mt->map.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(mt->map.dimensions && !apr_is_empty_table(mt->map.dimensions)) { const apr_array_header_t *elts = apr_table_elts(mt->map.dimensions); int i; for(i=0; inelts; i++) { apr_table_entry_t entry = APR_ARRAY_IDX(elts,i,apr_table_entry_t); char *dimvalue = apr_pstrdup(ctx->pool,entry.val); char *iter = dimvalue; while(*iter) { if(*iter == '/') *iter='_'; iter++; } lockname = apr_pstrcat(ctx->pool,lockname,dimvalue,NULL); } } return lockname; } void mapcache_tileset_configuration_check(mapcache_context *ctx, mapcache_tileset *tileset) { /* check we have all we want */ if(tileset->_cache == NULL) { /* TODO: we should allow tilesets with no caches */ ctx->set_error(ctx, 400, "tileset \"%s\" has no cache configured.", tileset->name); return; } if(apr_is_empty_array(tileset->grid_links)) { ctx->set_error(ctx, 400, "tileset \"%s\" has no grids configured", tileset->name); return; } #ifdef USE_PROJ /* not implemented yet, will be used to automatically calculate wgs84bbox with proj */ else if(tileset->wgs84bbox[0]>=tileset->wgs84bbox[2]) { mapcache_grid_link *sgrid = APR_ARRAY_IDX(tileset->grid_links,0,mapcache_grid_link*); double *extent = sgrid->grid->extent; if(sgrid->restricted_extent) { extent = sgrid->restricted_extent; } } #endif if(!tileset->format && tileset->source && tileset->source->type == MAPCACHE_SOURCE_GDAL) { ctx->set_error(ctx,400, "tileset \"%s\" references a gdal source. tag is missing and mandatory in this case", tileset->name); return; } if(tileset->metabuffer < 0 || tileset->metasize_x < 1 || tileset->metasize_y < 1) { ctx->set_error(ctx,400,"tileset \"%s\" has invalid metasize %d,%d or metabuffer %d", tileset->name,tileset->metasize_x,tileset->metasize_y,tileset->metabuffer); return; } if(!tileset->format && ( tileset->metasize_x != 1 || tileset->metasize_y != 1 || tileset->metabuffer != 0 || tileset->watermark)) { if(tileset->watermark) { ctx->set_error(ctx,400,"tileset \"%s\" has no configured, but it is needed for the watermark", tileset->name); return; } else { ctx->set_error(ctx,400,"tileset \"%s\" has no configured, but it is needed for metatiling", tileset->name); return; } } } void mapcache_tileset_add_watermark(mapcache_context *ctx, mapcache_tileset *tileset, const char *filename) { apr_file_t *f; apr_finfo_t finfo; int rv; mapcache_buffer *watermarkdata; apr_size_t size; if(apr_file_open(&f, filename, APR_FOPEN_READ|APR_FOPEN_BUFFERED|APR_FOPEN_BINARY, APR_OS_DEFAULT, ctx->pool) != APR_SUCCESS) { ctx->set_error(ctx,500, "failed to open watermark image %s",filename); return; } rv = apr_file_info_get(&finfo, APR_FINFO_SIZE, f); if(rv != APR_SUCCESS || !finfo.size) { ctx->set_error(ctx, 500, "watermark %s has no data",filename); return; } watermarkdata = mapcache_buffer_create(finfo.size,ctx->pool); //manually add the data to our buffer size = finfo.size; apr_file_read(f,watermarkdata->buf,&size); watermarkdata->size = size; if(size != finfo.size) { ctx->set_error(ctx, 500, "failed to copy watermark image data, got %d of %d bytes",(int)size, (int)finfo.size); return; } apr_file_close(f); tileset->watermark = mapcache_imageio_decode(ctx,watermarkdata); } void mapcache_tileset_tile_validate(mapcache_context *ctx, mapcache_tile *tile) { mapcache_extent_i limits; if(tile->z < tile->grid_link->minz || tile->z >= tile->grid_link->maxz) { ctx->set_error(ctx,404,"invalid tile z level"); return; } limits = tile->grid_link->grid_limits[tile->z]; if(tile->xx>=limits.maxx) { ctx->set_error(ctx, 404, "tile x=%d not in [%d,%d[", tile->x,limits.minx,limits.maxx); return; } if(tile->yy>=limits.maxy) { ctx->set_error(ctx, 404, "tile y=%d not in [%d,%d[", tile->y,limits.miny,limits.maxy); return; } } void mapcache_tileset_get_map_tiles(mapcache_context *ctx, mapcache_tileset *tileset, mapcache_grid_link *grid_link, mapcache_extent *bbox, int width, int height, int *ntiles, mapcache_tile ***tiles, mapcache_grid_link **effectively_used_grid_link) { double resolution; int level; int bl_x,bl_y,tr_x,tr_y; int mx,my,Mx,My; int x,y; int i=0; resolution = mapcache_grid_get_resolution(bbox, width, height); *effectively_used_grid_link = mapcache_grid_get_closest_wms_level(ctx,grid_link,resolution,&level); /* we don't want to assemble tiles that have already been reassembled from a lower level */ if((*effectively_used_grid_link)->outofzoom_strategy == MAPCACHE_OUTOFZOOM_REASSEMBLE && level > (*effectively_used_grid_link)->max_cached_zoom) { level = (*effectively_used_grid_link)->max_cached_zoom; } mapcache_grid_get_xy(ctx,(*effectively_used_grid_link)->grid,bbox->minx,bbox->miny,level,&bl_x,&bl_y); mapcache_grid_get_xy(ctx,(*effectively_used_grid_link)->grid,bbox->maxx,bbox->maxy,level,&tr_x,&tr_y); Mx = MAPCACHE_MAX(MAPCACHE_MIN(MAPCACHE_MAX(tr_x,bl_x),(*effectively_used_grid_link)->grid_limits[level].maxx),(*effectively_used_grid_link)->grid_limits[level].minx); My = MAPCACHE_MAX(MAPCACHE_MIN(MAPCACHE_MAX(tr_y,bl_y),(*effectively_used_grid_link)->grid_limits[level].maxy),(*effectively_used_grid_link)->grid_limits[level].miny); mx = MAPCACHE_MIN(MAPCACHE_MAX(MAPCACHE_MIN(tr_x,bl_x),(*effectively_used_grid_link)->grid_limits[level].minx),(*effectively_used_grid_link)->grid_limits[level].maxx); my = MAPCACHE_MIN(MAPCACHE_MAX(MAPCACHE_MIN(tr_y,bl_y),(*effectively_used_grid_link)->grid_limits[level].miny),(*effectively_used_grid_link)->grid_limits[level].maxy); *ntiles = (Mx-mx+1)*(My-my+1); i=0; *tiles = (mapcache_tile**)apr_pcalloc(ctx->pool, *ntiles*sizeof(mapcache_tile*)); for(x=mx; x<=Mx; x++) { for(y=my; y<=My; y++) { mapcache_tile *tile = mapcache_tileset_tile_create(ctx->pool,tileset, (*effectively_used_grid_link)); tile->x = x; tile->y = y; tile->z = level; mapcache_tileset_tile_validate(ctx,tile); if(GC_HAS_ERROR(ctx)) { //clear the error message ctx->clear_errors(ctx); } else { (*tiles)[i++]=tile; } } } *ntiles = i; } mapcache_image* mapcache_tileset_assemble_map_tiles(mapcache_context *ctx, mapcache_tileset *tileset, mapcache_grid_link *grid_link, mapcache_extent *bbox, int width, int height, int ntiles, mapcache_tile **tiles, mapcache_resample_mode mode) { double hresolution = mapcache_grid_get_horizontal_resolution(bbox, width); double vresolution = mapcache_grid_get_vertical_resolution(bbox, height); mapcache_extent tilebbox; mapcache_tile *toplefttile=NULL; int mx=INT_MAX,my=INT_MAX,Mx=INT_MIN,My=INT_MIN; int i; mapcache_image *image; mapcache_image *srcimage; double tileresolution, dstminx, dstminy, hf, vf; #ifdef DEBUG /* we know at least one tile contains data */ for(i=0; inodata) { break; } } if(i==ntiles) { ctx->set_error(ctx,500,"###BUG#### mapcache_tileset_assemble_map_tiles called with no tiles containing data"); return NULL; } #endif image = mapcache_image_create_with_data(ctx,width,height); if(ntiles == 0) { image->has_alpha = MC_ALPHA_YES; image->is_blank = MC_EMPTY_YES; return image; } /* compute the number of tiles horizontally and vertically */ for(i=0; ix < mx) mx = tile->x; if(tile->y < my) my = tile->y; if(tile->x > Mx) Mx = tile->x; if(tile->y > My) My = tile->y; } /* create image that will contain the unscaled tiles data */ srcimage = mapcache_image_create_with_data(ctx, (Mx-mx+1)*tiles[0]->grid_link->grid->tile_sx, (My-my+1)*tiles[0]->grid_link->grid->tile_sy); /* copy the tiles data into the src image */ for(i=0; igrid->origin) { case MAPCACHE_GRID_ORIGIN_BOTTOM_LEFT: if(tile->x == mx && tile->y == My) { toplefttile = tile; } ox = (tile->x - mx) * tile->grid_link->grid->tile_sx; oy = (My - tile->y) * tile->grid_link->grid->tile_sy; break; case MAPCACHE_GRID_ORIGIN_TOP_LEFT: if(tile->x == mx && tile->y == my) { toplefttile = tile; } ox = (tile->x - mx) * tile->grid_link->grid->tile_sx; oy = (tile->y - my) * tile->grid_link->grid->tile_sy; break; case MAPCACHE_GRID_ORIGIN_BOTTOM_RIGHT: if(tile->x == Mx && tile->y == My) { toplefttile = tile; } ox = (Mx - tile->x) * tile->grid_link->grid->tile_sx; oy = (My - tile->y) * tile->grid_link->grid->tile_sy; break; case MAPCACHE_GRID_ORIGIN_TOP_RIGHT: if(tile->x == Mx && tile->y == my) { toplefttile = tile; } ox = (Mx - tile->x) * tile->grid_link->grid->tile_sx; oy = (tile->y - my) * tile->grid_link->grid->tile_sy; break; } if(tile->nodata) continue; fakeimg.stride = srcimage->stride; fakeimg.data = &(srcimage->data[oy*srcimage->stride+ox*4]); if(!tile->raw_image) { mapcache_imageio_decode_to_image(ctx,tile->encoded_data,&fakeimg); } else { int r; unsigned char *srcptr = tile->raw_image->data; unsigned char *dstptr = fakeimg.data; for(r=0; rraw_image->h; r++) { memcpy(dstptr,srcptr,tile->raw_image->stride); srcptr += tile->raw_image->stride; dstptr += fakeimg.stride; } } } assert(toplefttile); /* 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, toplefttile->x, toplefttile->y, toplefttile->z, &tilebbox); /*compute the pixel position of top left corner*/ dstminx = (tilebbox.minx-bbox->minx)/hresolution; dstminy = (bbox->maxy-tilebbox.maxy)/vresolution; hf = tileresolution/hresolution; vf = tileresolution/vresolution; if(fabs(hf-1)<0.0001 && fabs(vf-1)<0.0001) { //use nearest resampling if we are at the resolution of the tiles mapcache_image_copy_resampled_nearest(ctx,srcimage,image,dstminx,dstminy,hf,vf); } else { switch(mode) { case MAPCACHE_RESAMPLE_BILINEAR: mapcache_image_copy_resampled_bilinear(ctx,srcimage,image,dstminx,dstminy,hf,vf,0); break; default: mapcache_image_copy_resampled_nearest(ctx,srcimage,image,dstminx,dstminy,hf,vf); break; } } /* free the memory of the temporary source image */ apr_pool_cleanup_run(ctx->pool, srcimage->data, (void*)free) ; return image; } /* * compute the metatile that should be rendered for the given tile */ mapcache_metatile* mapcache_tileset_metatile_get(mapcache_context *ctx, mapcache_tile *tile) { mapcache_metatile *mt = (mapcache_metatile*)apr_pcalloc(ctx->pool, sizeof(mapcache_metatile)); int i,j,blx,bly; mapcache_tileset *tileset = tile->tileset; mapcache_grid *grid = tile->grid_link->grid; double res = grid->levels[tile->z]->resolution; double gbuffer,gwidth,gheight,fullgwidth,fullgheight; mt->map.tileset = tileset; mt->map.grid_link = tile->grid_link; mt->z = tile->z; mt->x = tile->x / tileset->metasize_x; if(tile->x < 0) mt->x --; mt->y = tile->y / tileset->metasize_y; if(tile->y < 0) mt->y --; blx = mt->x * tileset->metasize_x; bly = mt->y * 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) { mt->metasize_x = grid->levels[tile->z]->maxx - blx; } else { mt->metasize_x = tileset->metasize_x; } if(bly+tileset->metasize_y-1 >= grid->levels[tile->z]->maxy) { mt->metasize_y = grid->levels[tile->z]->maxy - bly; } else { mt->metasize_y = tileset->metasize_y; } mt->ntiles = mt->metasize_x * mt->metasize_y; mt->tiles = (mapcache_tile*)apr_pcalloc(ctx->pool, mt->ntiles * sizeof(mapcache_tile)); mt->map.width = mt->metasize_x * grid->tile_sx + 2 * tileset->metabuffer; mt->map.height = mt->metasize_y * grid->tile_sy + 2 * tileset->metabuffer; mt->map.dimensions = tile->dimensions; /* buffer in geographical units */ gbuffer = res * tileset->metabuffer; /* adjusted metatile size in geographical units */ gwidth = res * mt->metasize_x * grid->tile_sx; gheight = res * mt->metasize_y * 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: mt->map.extent.minx = grid->extent.minx + mt->x * fullgwidth - gbuffer; mt->map.extent.miny = grid->extent.miny + mt->y * fullgheight - gbuffer; mt->map.extent.maxx = mt->map.extent.minx + gwidth + 2 * gbuffer; mt->map.extent.maxy = mt->map.extent.miny + gheight + 2 * gbuffer; break; case MAPCACHE_GRID_ORIGIN_TOP_LEFT: mt->map.extent.minx = grid->extent.minx + mt->x * fullgwidth - gbuffer; mt->map.extent.maxy = grid->extent.maxy - mt->y * fullgheight + gbuffer; mt->map.extent.maxx = mt->map.extent.minx + gwidth + 2 * gbuffer; mt->map.extent.miny = mt->map.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"); return NULL; } for(i=0; imetasize_x; i++) { for(j=0; jmetasize_y; j++) { mapcache_tile *t = &(mt->tiles[i*mt->metasize_y+j]); t->dimensions = tile->dimensions; t->grid_link = tile->grid_link; t->z = tile->z; t->x = blx + i; t->y = bly + j; t->tileset = tile->tileset; } } return mt; } /* * do the actual rendering and saving of a metatile: * - query the datasource for the image data * - split the resulting image along the metabuffer / metatiles * - save each tile to cache */ 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"); return; } #endif mt->map.tileset->source->render_map(ctx, &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; intiles; i++) { mapcache_tile *tile = &(mt->tiles[i]); mt->map.tileset->_cache->tile_set(ctx, mt->map.tileset->_cache, tile); GC_CHECK_ERROR(ctx); } } } /* * allocate and initialize a new tileset */ mapcache_tileset* mapcache_tileset_create(mapcache_context *ctx) { mapcache_tileset* tileset = (mapcache_tileset*)apr_pcalloc(ctx->pool, sizeof(mapcache_tileset)); tileset->metasize_x = tileset->metasize_y = 1; tileset->metabuffer = 0; tileset->expires = 300; /*set a reasonable default to 5 mins */ tileset->auto_expire = 0; tileset->read_only = 0; tileset->metadata = apr_table_make(ctx->pool,3); tileset->dimensions = NULL; tileset->format = NULL; tileset->grid_links = NULL; tileset->config = NULL; return tileset; } mapcache_tileset* mapcache_tileset_clone(mapcache_context *ctx, mapcache_tileset *src) { mapcache_tileset* dst = (mapcache_tileset*)apr_pcalloc(ctx->pool, sizeof(mapcache_tileset)); dst->metasize_x = src->metasize_x; dst->metasize_y = src->metasize_y; dst->metabuffer = src->metabuffer; dst->expires = src->expires; dst->auto_expire = src->auto_expire; dst->metadata = src->metadata; dst->dimensions = src->dimensions; dst->format = src->format; dst->grid_links = src->grid_links; dst->config = src->config; dst->name = src->name; dst->_cache = src->_cache; dst->source = src->source; dst->watermark = src->watermark; dst->wgs84bbox = src->wgs84bbox; dst->format = src->format; return dst; } /* * allocate and initialize a tile for a given tileset */ mapcache_tile* mapcache_tileset_tile_create(apr_pool_t *pool, mapcache_tileset *tileset, mapcache_grid_link *grid_link) { mapcache_tile *tile = (mapcache_tile*)apr_pcalloc(pool, sizeof(mapcache_tile)); tile->tileset = tileset; if(tileset->auto_expire) { tile->expires = tileset->auto_expire; } else { tile->expires = tileset->expires; } tile->grid_link = grid_link; if(tileset->dimensions) { int i; tile->dimensions = apr_table_make(pool,tileset->dimensions->nelts); for(i=0; idimensions->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); } apr_table_set(tile->dimensions,tileset->timedimension->key,tileset->timedimension->default_value); } return tile; } mapcache_tile* mapcache_tileset_tile_clone(apr_pool_t *pool, mapcache_tile *src) { mapcache_tile *tile = (mapcache_tile*)apr_pcalloc(pool, sizeof(mapcache_tile)); 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->x = src->x; tile->y = src->y; tile->z = src->z; tile->allow_redirect = src->allow_redirect; return tile; } mapcache_map* mapcache_tileset_map_clone(apr_pool_t *pool, mapcache_map *src) { mapcache_map *map = (mapcache_map*)apr_pcalloc(pool, sizeof(mapcache_map)); map->tileset = src->tileset; map->expires = src->expires; map->grid_link = src->grid_link; map->dimensions = apr_table_clone(pool,src->dimensions); map->height = src->height; map->width = src->width; map->extent = src->extent; return map; } /* * allocate and initialize a map for a given tileset */ mapcache_map* mapcache_tileset_map_create(apr_pool_t *pool, mapcache_tileset *tileset, mapcache_grid_link *grid_link) { mapcache_map *map = (mapcache_map*)apr_pcalloc(pool, sizeof(mapcache_map)); map->tileset = tileset; map->grid_link = grid_link; if(tileset->dimensions) { int i; map->dimensions = apr_table_make(pool,tileset->dimensions->nelts); for(i=0; idimensions->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); } apr_table_set(map->dimensions,tileset->timedimension->key,tileset->timedimension->default_value); } return map; } /* * allocate and initialize a feature_info for a given tileset */ mapcache_feature_info* mapcache_tileset_feature_info_create(apr_pool_t *pool, mapcache_tileset *tileset, mapcache_grid_link *grid_link) { mapcache_feature_info *fi = (mapcache_feature_info*)apr_pcalloc(pool, sizeof(mapcache_feature_info)); fi->map.tileset = tileset; fi->map.grid_link = grid_link; if(tileset->dimensions) { int i; fi->map.dimensions = apr_table_make(pool,tileset->dimensions->nelts); for(i=0; idimensions->nelts; i++) { mapcache_dimension *dimension = APR_ARRAY_IDX(tileset->dimensions,i,mapcache_dimension*); apr_table_set(fi->map.dimensions,dimension->name,dimension->default_value); } } return fi; } void mapcache_tileset_assemble_out_of_zoom_tile(mapcache_context *ctx, mapcache_tile *tile) { assert(tile->grid_link->outofzoom_strategy == MAPCACHE_OUTOFZOOM_REASSEMBLE); /* we have at most 4 tiles composing the requested tile */ mapcache_extent tile_bbox; double shrink_x, shrink_y, scalefactor; int x[4],y[4]; int i, n=1; mapcache_grid_get_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 errors when computing the x,y of the lower level tile(s) we will need */ shrink_x = (tile_bbox.maxx - tile_bbox.minx) / (tile->grid_link->grid->tile_sx * 1000); /* 1/1000th of a pixel */ shrink_y = (tile_bbox.maxy - tile_bbox.miny) / (tile->grid_link->grid->tile_sy * 1000); /* 1/1000th of a pixel */ tile_bbox.maxx -= shrink_x; tile_bbox.maxy -= shrink_y; tile_bbox.minx += shrink_x; tile_bbox.miny += shrink_y; /* compute the x,y of the lower level tiles we'll use for reassembling (we take them from the grid_link->max_cached_zoom, * which is the closest level were we can consume tiles from the cache */ mapcache_grid_get_xy(ctx,tile->grid_link->grid,tile_bbox.minx, tile_bbox.miny, tile->grid_link->max_cached_zoom, &x[0], &y[0]); mapcache_grid_get_xy(ctx,tile->grid_link->grid,tile_bbox.maxx, tile_bbox.maxy, tile->grid_link->max_cached_zoom, &x[1], &y[1]); if(x[0] != x[1] || y[0] != y[1]) { /* no use computing these if the first two were identical */ n = 4; mapcache_grid_get_xy(ctx,tile->grid_link->grid,tile_bbox.minx, tile_bbox.maxy, tile->grid_link->max_cached_zoom, &x[2], &y[2]); mapcache_grid_get_xy(ctx,tile->grid_link->grid,tile_bbox.maxx, tile_bbox.miny, tile->grid_link->max_cached_zoom, &x[3], &y[3]); } tile_bbox.maxx += shrink_x; tile_bbox.maxy += shrink_y; tile_bbox.minx -= shrink_x; tile_bbox.miny -= shrink_y; mapcache_tile *childtile = mapcache_tileset_tile_clone(ctx->pool,tile); childtile->z = tile->grid_link->max_cached_zoom; scalefactor = childtile->grid_link->grid->levels[childtile->z]->resolution/tile->grid_link->grid->levels[tile->z]->resolution; tile->nodata = 1; for(i=0;ix = x[i]; childtile->y = y[i]; mapcache_extent childtile_bbox; double dstminx,dstminy; mapcache_tileset_tile_get(ctx,childtile); GC_CHECK_ERROR(ctx); if(childtile->nodata) { /* silently skip empty tiles */ childtile->nodata = 0; /* reset flag */ continue; } if(!childtile->raw_image) { childtile->raw_image = mapcache_imageio_decode(ctx, childtile->encoded_data); GC_CHECK_ERROR(ctx); } if(tile->nodata) { /* we defer the creation of the actual image bytes, no use allocating before knowing that one of the child tiles actually contains data*/ tile->raw_image = mapcache_image_create_with_data(ctx,tile->grid_link->grid->tile_sx, tile->grid_link->grid->tile_sy); tile->nodata = 0; } /* now copy/scale the srcimage onto the destination image */ mapcache_grid_get_extent(ctx,childtile->grid_link->grid, childtile->x, childtile->y, childtile->z, &childtile_bbox); /*compute the pixel position of top left corner*/ dstminx = (childtile_bbox.minx-tile_bbox.minx)/tile->grid_link->grid->levels[tile->z]->resolution; dstminy = (tile_bbox.maxy-childtile_bbox.maxy)/tile->grid_link->grid->levels[tile->z]->resolution; /* * ctx->log(ctx, MAPCACHE_DEBUG, "factor: %g. start: %g,%g (im size: %g)",scalefactor,dstminx,dstminy,scalefactor*256); */ if(scalefactor <= tile->grid_link->grid->tile_sx/2) /*FIXME: might fail for non-square tiles, also check tile_sy */ mapcache_image_copy_resampled_bilinear(ctx,childtile->raw_image,tile->raw_image,dstminx,dstminy,scalefactor,scalefactor,1); else { /* no use going through bilinear resampling if the requested scalefactor maps less than 4 pixels onto the * resulting tile, plus pixman has some rounding bugs in this case, see * https://bugs.freedesktop.org/show_bug.cgi?id=46277 */ unsigned int row,col; unsigned char *srcpixptr; unsigned int dstminxi = - dstminx / scalefactor; unsigned int dstminyi = - dstminy / scalefactor; srcpixptr = &(childtile->raw_image->data[dstminyi * childtile->raw_image->stride + dstminxi * 4]); /* ctx->log(ctx, MAPCACHE_WARN, "factor: %g. pixel: %d,%d (val:%d)",scalefactor,dstminxi,dstminyi,*((unsigned int*)srcpixptr)); */ unsigned char *row_ptr = tile->raw_image->data; for(row=0;rowraw_image->h;row++) { unsigned char *pix_ptr = row_ptr; for(col=0;colraw_image->w;col++) { *((unsigned int*)pix_ptr) = *((unsigned int*)srcpixptr); pix_ptr += 4; } row_ptr += tile->raw_image->stride; } } /* do some cleanup, a bit in advance as we won't be using this tile's data anymore */ apr_pool_cleanup_run(ctx->pool,childtile->raw_image->data,(void*)free); childtile->raw_image = NULL; childtile->encoded_data = NULL; } } void mapcache_tileset_outofzoom_get(mapcache_context *ctx, mapcache_tile *tile) { assert(tile->grid_link->outofzoom_strategy != MAPCACHE_OUTOFZOOM_NOTCONFIGURED); if(tile->grid_link->outofzoom_strategy == MAPCACHE_OUTOFZOOM_REASSEMBLE) { mapcache_tileset_assemble_out_of_zoom_tile(ctx, tile); } else {/* if(tile->grid_link->outofzoom_strategy == MAPCACHE_OUTOFZOOM_PROXY) */ if(ctx->config->non_blocking) { ctx->set_error(ctx,404,"cannot proxy out-of-zoom tile, I'm configured in non-blocking mode"); return; } ctx->set_error(ctx,500,"Proxying out of zoom tiles not implemented"); } } /** * \brief return the image data for a given tile * this call uses a global (interprocess+interthread) mutex if the tile was not found * in the cache. * the processing here is: * - if the tile is found in the cache, return it. done * - if it isn't found: * - aquire mutex * - check if the tile isn't being rendered by another thread/process * - if another thread is rendering, wait for it to finish and return it's data * - otherwise, lock all the tiles corresponding to the request (a metatile has multiple tiles) * - release mutex * - call the source to render the metatile, and save the tiles to disk * - aquire mutex * - unlock the tiles we have rendered * - release mutex * */ void mapcache_tileset_tile_get(mapcache_context *ctx, mapcache_tile *tile) { 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); GC_CHECK_ERROR(ctx); 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(staletileset->read_only || !tile->tileset->source) { /* 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", tile->tileset->name); */ tile->nodata = 1; return; } /* bail out in non-blocking mode */ if(ctx->config->non_blocking) { ctx->set_error(ctx,404,"tile not in cache, and configured for readonly mode"); return; } } if (ret == MAPCACHE_CACHE_MISS || ret == MAPCACHE_CACHE_RELOAD) { int isLocked; 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) { /* * is the tile already being rendered by another thread ? * the call is protected by the same mutex that sets the lock on the tile, * so we can assure that: * - if the lock does not exist, then this thread should do the rendering * - if the lock exists, we should wait for the other thread to finish */ /* aquire a lock on the metatile */ mt = mapcache_tileset_metatile_get(ctx, tile); isLocked = mapcache_lock_or_wait_for_resource(ctx, ctx->config->locker, mapcache_tileset_metatile_resource_key(ctx,mt), &lock); GC_CHECK_ERROR(ctx); if(isLocked == MAPCACHE_TRUE) { /* no other thread is doing the rendering, do it ourselves */ #ifdef DEBUG ctx->log(ctx, MAPCACHE_DEBUG, "cache miss/reload: tileset %s - tile %d %d %d", tile->tileset->name,tile->x, tile->y,tile->z); #endif /* this will query the source to create the tiles, and save them to the cache */ mapcache_tileset_render_metatile(ctx, mt); if(GC_HAS_ERROR(ctx)) { /* 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); ctx->push_errors(ctx,error); } else { mapcache_unlock_resource(ctx, ctx->config->locker, mapcache_tileset_metatile_resource_key(ctx,mt), lock); } } } if (ret == MAPCACHE_CACHE_RELOAD && GC_HAS_ERROR(ctx)) /* If we tried to reload a stale tile but failed, we know we have already * fetched it from the cache. We can then ignore errors and just use old tile. */ ctx->clear_errors(ctx); else { /* 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); GC_CHECK_ERROR(ctx); if(ret != MAPCACHE_SUCCESS) { if(isLocked == MAPCACHE_FALSE) { ctx->set_error(ctx, 500, "tileset %s: unknown error (another thread/process failed to create the tile I was waiting for)", tile->tileset->name); } else { /* shouldn't really happen, as the error ought to have been caught beforehand */ ctx->set_error(ctx, 500, "tileset %s: failed to re-get tile %d %d %d from cache after set", tile->tileset->name,tile->x,tile->y,tile->z); } } } } /* 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); } } 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); GC_CHECK_ERROR(ctx); if(whole_metatile) { mapcache_metatile *mt = mapcache_tileset_metatile_get(ctx, tile); for(i=0; intiles; i++) { 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); /* silently pass failure if the tile was not found */ if(ctx->get_error(ctx) == 404) { ctx->clear_errors(ctx); } GC_CHECK_ERROR(ctx); } } } /* vim: ts=2 sts=2 et sw=2 */ mapcache-1.4.0/lib/util.c000066400000000000000000000332441255567662100151710ustar00rootroot00000000000000/****************************************************************************** * $Id$ * * Project: MapServer * Purpose: MapCache tile caching support file: common utility functions * 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 "util.h" #include #include #include #include #ifndef _WIN32 #include #endif #ifndef M_PI #define M_PI 3.14159265358979323846264338327 #endif const double mapcache_meters_per_unit[MAPCACHE_UNITS_COUNT] = {1.0,6378137.0 * 2.0 * M_PI / 360,0.3048}; static char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; static int mod_table[] = {0, 2, 1}; char *base64_encode(apr_pool_t *pool, const unsigned char *data, size_t input_length) { int i,j; char *encoded_data; size_t output_length = 4 * ((input_length + 2) / 3) + 1; encoded_data = (char*)apr_pcalloc(pool,output_length*sizeof(char)); if (encoded_data == NULL) return NULL; for (i = 0, j = 0; i < input_length;) { uint32_t octet_a = i < input_length ? (unsigned char)data[i++] : 0; uint32_t octet_b = i < input_length ? (unsigned char)data[i++] : 0; uint32_t octet_c = i < input_length ? (unsigned char)data[i++] : 0; uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c; encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F]; encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F]; encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F]; encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F]; } for (i = 0; i < mod_table[input_length % 3]; i++) encoded_data[output_length - 2 - i] = '='; encoded_data[output_length-1]=0; return encoded_data; } int mapcache_util_extract_int_list(mapcache_context *ctx, const char* cargs, const char *sdelim, int **numbers, int *numbers_count) { char *last, *key, *endptr; char *args = apr_pstrdup(ctx->pool,cargs); int tmpcount=1; const char *delim = (sdelim)?sdelim:" ,\t\r\n"; char sep; int i; *numbers_count = 0; i=strlen(delim); while(i--) { sep = delim[i]; for(key=args; *key; key++) { if(*key == sep) tmpcount++; } } *numbers = (int*)apr_pcalloc(ctx->pool,tmpcount*sizeof(int)); for (key = apr_strtok(args, delim, &last); key != NULL; key = apr_strtok(NULL, delim, &last)) { (*numbers)[(*numbers_count)++] = (int)strtol(key,&endptr,10); if(*endptr != 0) return MAPCACHE_FAILURE; } return MAPCACHE_SUCCESS; } int mapcache_util_extract_double_list(mapcache_context *ctx, const char* cargs, const char *sdelim, double **numbers, int *numbers_count) { char *last, *key, *endptr; char *args = apr_pstrdup(ctx->pool,cargs); int tmpcount=1; const char *delim = (sdelim)?sdelim:" ,\t\r\n"; char sep; int i; *numbers_count = 0; i=strlen(delim); while(i--) { sep = delim[i]; for(key=args; *key; key++) { if(*key == sep) tmpcount++; } } *numbers = (double*)apr_pcalloc(ctx->pool,tmpcount*sizeof(double)); for (key = apr_strtok(args, delim, &last); key != NULL; key = apr_strtok(NULL, delim, &last)) { (*numbers)[(*numbers_count)++] = strtod(key,&endptr); if(*endptr != 0) return MAPCACHE_FAILURE; } return MAPCACHE_SUCCESS; } char *mapcache_util_str_replace(apr_pool_t *pool, const char *string, const char *substr, const char *replacement ) { char *tok = NULL; char *newstr = NULL; tok = strstr( string, substr ); if( tok == NULL ) return apr_pstrdup( pool, string ); newstr = apr_pcalloc(pool, strlen( string ) - strlen( substr ) + strlen( replacement ) + 1 ); memcpy( newstr, string, tok - string ); memcpy( newstr + (tok - string), replacement, strlen( replacement ) ); memcpy( newstr + (tok - string) + strlen( replacement ), tok + strlen( substr ), strlen( string ) - strlen( substr ) - ( tok - string ) ); memset( newstr + strlen( string ) - strlen( substr ) + strlen( replacement ), 0, 1 ); return newstr; } char* mapcache_util_str_sanitize(apr_pool_t *pool, const char *str, const char* from, char to) { char *pstr = apr_pstrdup(pool,str); size_t pos = strcspn(pstr,from); if(pstr[pos]) { pstr = apr_pstrdup(pool,pstr); while(pstr[pos]) { ((char*)pstr)[pos]=to; pos += strcspn(&pstr[pos],from); } } return pstr; } #if APR_MAJOR_VERSION < 1 || (APR_MAJOR_VERSION < 2 && APR_MINOR_VERSION < 3) APR_DECLARE(apr_table_t *) apr_table_clone(apr_pool_t *p, const apr_table_t *t) { const apr_array_header_t *array = apr_table_elts(t); apr_table_entry_t *elts = (apr_table_entry_t *) array->elts; apr_table_t *new = apr_table_make(p, array->nelts); int i; for (i = 0; i < array->nelts; i++) { apr_table_add(new, elts[i].key, elts[i].val); } return new; } #endif int _mapcache_context_get_error_default(mapcache_context *ctx) { return ctx->_errcode; } char* _mapcache_context_get_error_msg_default(mapcache_context *ctx) { return ctx->_errmsg; } void _mapcache_context_set_exception_default(mapcache_context *ctx, char *key, char *msg, ...) { char *fullmsg; va_list args; if(!ctx->exceptions) { ctx->exceptions = apr_table_make(ctx->pool,1); } va_start(args,msg); fullmsg = apr_pvsprintf(ctx->pool,msg,args); va_end(args); apr_table_set(ctx->exceptions,key,fullmsg); } void _mapcache_context_set_error_default(mapcache_context *ctx, int code, char *msg, ...) { char *new_msg; va_list args; va_start(args,msg); new_msg = apr_pvsprintf(ctx->pool,msg,args); va_end(args); if(ctx->_errmsg) { ctx->_errmsg = apr_pstrcat(ctx->pool, ctx->_errmsg, "\n", new_msg, NULL); } else { ctx->_errmsg = new_msg; ctx->_errcode = code; } } void _mapcache_context_clear_error_default(mapcache_context *ctx) { ctx->_errcode = 0; ctx->_errmsg = NULL; if(ctx->exceptions) { apr_table_clear(ctx->exceptions); } } struct _error_log { int _errcode; char *_errmsg; apr_table_t *exceptions; }; void _mapcache_context_pop_errors(mapcache_context *ctx, void **error) { struct _error_log *e = (struct _error_log*)apr_pcalloc(ctx->pool, sizeof(struct _error_log)); e->_errcode = ctx->_errcode; e->_errmsg = ctx->_errmsg; e->exceptions = ctx->exceptions; ctx->_errcode = 0; ctx->_errmsg = NULL; ctx->exceptions = NULL; *error = e; } void _mapcache_context_push_errors(mapcache_context *ctx, void *error) { struct _error_log *e = (struct _error_log*)error; ctx->_errcode = e->_errcode; if(e->_errmsg) { if(ctx->_errmsg) { ctx->_errmsg = apr_psprintf(ctx->pool,"%s\n%s",ctx->_errmsg,e->_errmsg); } else { ctx->_errmsg = e->_errmsg; } } if(e->exceptions) { if(ctx->exceptions) { apr_table_overlap(ctx->exceptions, e->exceptions, APR_OVERLAP_TABLES_SET); } else { ctx->exceptions = e->exceptions; } } } void mapcache_context_init(mapcache_context *ctx) { ctx->_errcode = 0; ctx->_errmsg = NULL; ctx->get_error = _mapcache_context_get_error_default; ctx->get_error_message = _mapcache_context_get_error_msg_default; ctx->set_error = _mapcache_context_set_error_default; ctx->set_exception = _mapcache_context_set_exception_default; ctx->clear_errors = _mapcache_context_clear_error_default; ctx->pop_errors = _mapcache_context_pop_errors; ctx->push_errors = _mapcache_context_push_errors; ctx->headers_in = NULL; } void mapcache_context_copy(mapcache_context *src, mapcache_context *dst) { dst->_contenttype = src->_contenttype; dst->_errcode = src->_errcode; dst->_errmsg = src->_errmsg; dst->clear_errors = src->clear_errors; dst->clone = src->clone; dst->config = src->config; dst->get_error = src->get_error; dst->get_error_message = src->get_error_message; dst->get_instance_id = src->get_instance_id; dst->log = src->log; dst->set_error = src->set_error; dst->pool = src->pool; 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; dst->connection_pool = src->connection_pool; dst->headers_in = src->headers_in; } char* mapcache_util_get_tile_dimkey(mapcache_context *ctx, mapcache_tile *tile, char* sanitized_chars, char *sanitize_to) { char *key = apr_pstrdup(ctx->pool,""); if(tile->dimensions) { const apr_array_header_t *elts = apr_table_elts(tile->dimensions); int i = elts->nelts; if(i>1) { while(i--) { apr_table_entry_t *entry = &(APR_ARRAY_IDX(elts,i,apr_table_entry_t)); if(i) { key = apr_pstrcat(ctx->pool,key,entry->val,(sanitized_chars?sanitize_to:"#"),NULL); } else { key = apr_pstrcat(ctx->pool,key,entry->val,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); } if(sanitized_chars) key = mapcache_util_str_sanitize(ctx->pool,key,sanitized_chars,*sanitize_to); } 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)); } if(strstr(path,"{tileset}")) path = mapcache_util_str_replace(ctx->pool, path, "{tileset}", tile->tileset->name); if(strstr(path,"{grid}")) path = mapcache_util_str_replace(ctx->pool, path, "{grid}", tile->grid_link->grid->name); if(strstr(path,"{ext}")) path = mapcache_util_str_replace(ctx->pool, path, "{ext}", tile->tileset->format ? tile->tileset->format->extension : "png"); } else { char *separator = "/"; /* we'll concatenate the entries ourself */ path = apr_pstrcat(ctx->pool, tile->tileset->name,separator, tile->grid_link->grid->name,separator, NULL); if(tile->dimensions) { path = apr_pstrcat(ctx->pool,path, mapcache_util_get_tile_dimkey(ctx,tile,sanitized_chars,sanitize_to), separator,NULL); } path = apr_pstrcat(ctx->pool,path, apr_psprintf(ctx->pool, "%d", tile->z),separator, apr_psprintf(ctx->pool, "%d", tile->y),separator, apr_psprintf(ctx->pool, "%d", tile->x),separator, tile->tileset->format?tile->tileset->format->extension:"png", NULL); } return path; } /* vim: ts=2 sts=2 et sw=2 */ #if defined(_WIN32) && !defined(__CYGWIN__) int strncasecmp(const char *s1, const char *s2, int len) { register const char *cp1, *cp2; int cmp = 0; cp1 = s1; cp2 = s2; if(len == 0) return(0); if (!*cp1) return -1; else if (!*cp2) return 1; while(*cp1 && *cp2 && len) { if((cmp = (toupper(*cp1) - toupper(*cp2))) != 0) return(cmp); cp1++; cp2++; len--; } if(len == 0) { return(0); } if(*cp1 || *cp2) { if (*cp1) return(1); else return (-1); } return(0); } #include void mapcache_gettimeofday(struct mctimeval* tp, void* tzp) { struct _timeb theTime; _ftime(&theTime); tp->tv_sec = theTime.time; tp->tv_usec = theTime.millitm * 1000; } #endif mapcache-1.4.0/mapcache.xml000066400000000000000000000030761255567662100155650ustar00rootroot00000000000000 /tmp /tmp/{tileset}-{z}-{grid}.db image/png basic http://vmap0.tiles.osgeo.org/wms/vmap0 vmap0 sqlite WGS84 GoogleMapsCompatible PNG 5 5 10 3600 JPEG assemble bilinear JPEG 4096 report /tmp 300 mapcache-1.4.0/mapcache.xml.sample000066400000000000000000001143011255567662100170370ustar00rootroot00000000000000 my mapcache service woot! this is a service abstract! this is a custom grid I made up for an example EPSG:4326 EPSG:foobar 256 256 -180 -90 180 90 0.1 0.05 0.025 0.0125 Lambert 1993 -357823.2365 6037008.6939 1313632.3628 7230727.3772 EPSG:2154 IGNF:LAMB93 2048 1024 512 256 128 64 32 16 8 4 2 1 0.5 0.25 0.125 0.0625 m 256 256 -9.62 41.18 10.3 51.54 EPSG:4171 IGNF:RGF93 0.026726773770953713 0.013363386885476856 0.006681693442738428 0.003340846721369214 0.001670423360684607 0.0008352116803423035 0.00041760584017115176 0.00020880292008557588 0.00010440146004278794 0.00005220073002139397 0.000026100365010696985 0.000013050182505348493 0.000006525091252674246 0.000003262545626337123 0.0000016312728131685616 0.0000008156364065842808 dd 256 256 Géoportail - France métropolitaine -524288 4456448 786432 5767168 EPSG:310024802 IGNF:GEOPORTALFXX EPSG:310024001 2048 1024 512 256 128 64 32 16 8 4 2 1 0.5 0.25 0.125 0.0625 m 256 256 Géoportail - Antilles françaises -6791168 1761280 -6553600 2023424 EPSG:310915814 IGNF:GEOPORTALANF EPSG:310495002 2048 1024 512 256 128 64 32 16 8 4 2 1 0.5 0.25 0.125 0.0625 m 256 256 Géoportail - Guyane -6070272 235047 -5726208 647168 EPSG:310486805 IGNF:GEOPORTALGUF EPSG:310486003 2048 1024 512 256 128 64 32 16 8 4 2 1 0.5 0.25 0.125 0.0625 m 256 256 Géoportail - Mayotte 4901888 -1447936 4932608 -1405952 EPSG:310702807 IGNF:GEOPORTALMYT EPSG:310702005 2048 1024 512 256 128 64 32 16 8 4 2 1 0.5 0.25 0.125 0.0625 m 256 256 Géoportail - Réunion et dépendances 5734400 -2383872 5808128 -2318336 EPSG:310700806 IGNF:GEOPORTALREU EPSG:310700004 2048 1024 512 256 128 64 32 16 8 4 2 1 0.5 0.25 0.125 0.0625 m 256 256 Géoportail - Nouvelle-Calédonie 16777216 -2555904 17301504 -2162688 EPSG:310547809 IGNF:GEOPORTALNCL 2048 1024 512 256 128 64 32 16 8 4 2 1 0.5 0.25 0.125 0.0625 m 256 256 Géoportail - Saint-Pierre et Miquelon -4325376 5177344 -4259840 5308416 EPSG:310706808 IGNF:GEOPORTALSPM EPSG:310706006 2048 1024 512 256 128 64 32 16 8 4 2 1 0.5 0.25 0.125 0.0625 m 256 256 Géoportail - Wallis et Futuna -19267584 -1638400 -19005440 -1441792 EPSG:310642810 IGNF:GEOPORTALWLF 2048 1024 512 256 128 64 32 16 8 4 2 1 0.5 0.25 0.125 0.0625 m 256 256 Géoportail - Crozet 3801088 -5242880 4128768 -5046272 EPSG:310642801 IGNF:GEOPORTALCRZ 2048 1024 512 256 128 64 32 16 8 4 2 1 0.5 0.25 0.125 0.0625 m 256 256 Géoportail - Kerguelen 4915200 -5636096 5177344 -5373952 EPSG:310642812 IGNF:GEOPORTALKER 2048 1024 512 256 128 64 32 16 8 4 2 1 0.5 0.25 0.125 0.0625 m 256 256 Géoportail - Polynésie française -16384000 -2097152 -15990784 -1703936 EPSG:310032811 IGNF:GEOPORTALPYF EPSG:310032009 2048 1024 512 256 128 64 32 16 8 4 2 1 0.5 0.25 0.125 0.0625 m 256 256 Géoportail - Amsterdam et Saint-Paul -6791168 1761280 -6553600 2023424 EPSG:310642813 IGNF:GEOPORTALASP 2048 1024 512 256 128 64 32 16 8 4 2 1 0.5 0.25 0.125 0.0625 m 256 256 Terre Adélie 1950 196098 225276 720386 749564 EPSG:2986 IGNF:TERA50STEREO 2048 1024 512 256 128 64 32 16 8 4 2 1 0.5 0.25 0.125 0.0625 m 256 256 Géoportail - Monde -20037504 -10018752 20037504 10018752 EPSG:310642901 IGNF:MILLER 39135.75 19567.875 9783.9375 4891.96875 2445.984375 m 256 256 /tmp /tmp/mysqlitetiles.db value create table if not exists tiles(tileset text, grid text, x integer, y integer, z integer, data blob, dim text, ctime datetime, primary key(tileset,grid,x,y,z,dim)) select 1 from tiles where x=:x and y=:y and z=:z and dim=:dim and tileset=:tileset and grid=:grid select data,strftime("%s",ctime) from tiles where tileset=:tileset and grid=:grid and x=:x and y=:y and z=:z and dim=:dim insert or replace into tiles(tileset,grid,x,y,z,data,dim,ctime) values (:tileset,:grid,:x,:y,:z,:data,:dim,datetime('now')) delete from tiles where x=:x and y=:y and z=:z and dim=:dim and tileset=:tileset and grid=:grid /tmp/foo/ /tmp/ {tileset}-{grid}-{dim}-{z}-{y}-{x}.{ext} https://myserver/webdav/{tileset}/{grid}/{z}/{x}/{y}.{ext} my-virtualhost-alias.domain.com foo foo foo foo https://foo.s3.amazonaws.com/tiles/{tileset}/{grid}/{z}/{x}/{y}/{ext} foo.s3.amazonaws.com foo foo/sdsvd eu-west-1 REDUCED_REDUNDANCY public-read https://foo.blob.core.windows.net/tiles/{tileset}/{grid}/{z}/{x}/{y}/{ext} foo.blob.core.windows.net foo foobarcccccccccccccccccccccyA== tiles https://storage.googleapis.com/mytiles-mapcache/{tileset}/{grid}/{z}/{x}/{y}.{ext} GOOGPGDWFDG345SDFGSD sdfgsdSDFwedfwefr2345324dfsGdsfgs public-read fast 256 75 RGB best PNG_BEST JPEG image/png basic http://vmap0.tiles.osgeo.org/wms/vmap0 30 300 http://localhost/cgi-bin/mapserv? image/png default /Users/tbonfort/dev/mapserver-utils/osm-google.map http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi image/png nexrad_base_reflect true http://geoservices.brgm.fr/geologie? image/png GRAVI_BASE true text/plain,application/vnd.ogc.gml GRAVI_BASE vmap0 sqlite WGS84 g vmap0 map blabla PNG 5 5 10 3600 86400 foobar,foobarbaz,foo,bar ^(?!.*\.\.)[a-zA-Z0-9\./]*\.map$ 0/5000/1000 nexrad disk mixed WGS84 basic disk PNG WGS84 5 5 30 osm mapserver served map of midi-pyrénées see http://mapserver-utils.googlecode.com osm sqlite PNG FXX MILLER WGS84 g 5 5 1 10 JPEG assemble bilinear myjpeg 4096 report /tmp 0.01 /tmp localhost 11211 memcache-host 11212 0.3 true info true mapcache-1.4.0/mod_geocache.doxyfile000066400000000000000000002043141255567662100174420ustar00rootroot00000000000000# Doxyfile 1.7.2 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" "). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = mod-mapcache # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = 0.1-dev # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = doc # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful if your file system # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this # tag. The format is ext=language, where ext is a file extension, and language # is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, # C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make # doxygen treat .inc files as Fortran files (default is PHP), and .f files as C # (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions # you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also makes the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penality. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will roughly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = YES # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespaces are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = YES # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = YES # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST = YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or macro consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and macros in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. The create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_NO_PARAMDOC option can be enabled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = src include # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh # *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py # *.f90 *.f *.vhd *.vhdl FILE_PATTERNS = # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = YES # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the stylesheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [0,1..20]) # that doxygen will group on one line in the generated HTML documentation. # Note that a value of 0 will completely suppress the enum values from appearing in the overview section. ENUM_VALUES_PER_LINE = 4 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list. USE_INLINE_TREES = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax # (see http://www.mathjax.org) which uses client side Javascript for the # rendering instead of using prerendered bitmaps. Use this if you do not # have LaTeX installed or if you want to formulas look prettier in the HTML # output. When enabled you also need to install MathJax separately and # configure the path to it using the MATHJAX_RELPATH option. USE_MATHJAX = NO # When MathJax is enabled you need to specify the location relative to the # HTML output directory using the MATHJAX_RELPATH option. The destination # directory should contain the MathJax.js script. For instance, if the mathjax # directory is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the mathjax.org site, so you can quickly see the result without installing # MathJax, but it is strongly recommended to install a local copy of MathJax # before deployment. MATHJAX_RELPATH = http://www.mathjax.org/mathjax # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client # using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server # based approach is that it scales better to large projects and allows # full text search. The disadvantages are that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4 # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option also works with HAVE_DOT disabled, but it is recommended to # install and use dot, since it yields more powerful graphs. CLASS_DIAGRAMS = NO # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = NO # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = YES # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will write a font called FreeSans.ttf to the output # directory and reference it in all dot files that doxygen generates. This # font does not include all possible unicode characters however, so when you need # these (or just want a differently looking font) you can specify the font name # using DOT_FONTNAME. You need need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = FreeSans.ttf # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will generate a graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif. # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the # \mscfile command). MSCFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES mapcache-1.4.0/nginx/000077500000000000000000000000001255567662100144175ustar00rootroot00000000000000mapcache-1.4.0/nginx/CMakeLists.txt000066400000000000000000000001461255567662100171600ustar00rootroot00000000000000 configure_file ( "${PROJECT_SOURCE_DIR}/nginx/config.in" "${PROJECT_BINARY_DIR}/nginx/config" ) mapcache-1.4.0/nginx/README000066400000000000000000000045261255567662100153060ustar00rootroot00000000000000minimal install instructions: - configure nginx with ./configure --add-module=/path/to/mapcache/nginx the supplied nginx.conf contains an example configuration to load mapcache. the relevant part is: location ~ ^/mapcache(?/.*|$) { set $url_prefix "/mapcache"; mapcache /home/tbonfort/dev/mapserver-trunk/mapcache/mapcache.xml; } the and $url_prefix are important and are used by mapcache to parse the incoming requests. before running nginx, set LD_LIBRARY_PATH to where libmapcache was installed if that is a non standard location. Note that nginx processes are scarse and would be locked when mapcache is doing a curl request for a metatile, or when waiting for a metatile to be rendered. So as not to lock down the whole nginx server in this case, mapcache will fail with a 404 error if a requested tile cannot be found in it's cache. It is up to the administrator of the server to forward these failed requests to an external fastcgi or apache mapcache instance running with the same configuration file. Here is a configuration block which forwards these 404 requests: location ~ ^/mapcache(?/.*|$) { set $url_prefix "/mapcache"; mapcache /path/to/etc/mapcache.xml; #error_page 404 = @apache_mapcache; error_page 404 = @fcgi_mapcache; } #proxy for a mapcache instance running on another server location @apache_mapcache { proxy_pass http://localhost:8081; # this supposes you have mapcache as a module running in an apache httpd server, where # you would have set the httpd.conf to contain: # MapcacheAlias /mapcache /path/to/etc/mapcache.xml; } #mapcache as fcgi running with an external spawning manager (e.g. spawn-fcgi or fcgistarter) #(make sure mapcache is built with fastcgi support) #eg: # export MAPCACHE_CONFIG_FILE=/path/to/etc/mapcache.xml # /usr/local/httpd-2.4/bin/fcgistarter -c /usr/local/bin/mapcache -p 9001 -N 20 location @fcgi_mapcache { fastcgi_pass localhost:9001; fastcgi_param QUERY_STRING $query_string; fastcgi_param REQUEST_METHOD $request_method; fastcgi_param CONTENT_TYPE $content_type; fastcgi_param CONTENT_LENGTH $content_length; fastcgi_param PATH_INFO $path_info; } mapcache-1.4.0/nginx/config.in000066400000000000000000000005331255567662100162150ustar00rootroot00000000000000ngx_addon_name=ngx_http_mapcache_module HTTP_MODULES="$HTTP_MODULES ngx_http_mapcache_module" NGX_ADDON_SRCS="$NGX_ADDON_SRCS @CMAKE_SOURCE_DIR@/nginx/ngx_http_mapcache_module.c" CORE_LIBS="$CORE_LIBS -lmapcache @APR_LIBRARIES@ @APU_LIBRARIES@" CFLAGS="$CFLAGS @APR_CPPFLAGS@" CORE_INCS="$CORE_INCS @PROJECT_BINARY_DIR@/include @APR_INCLUDE_DIR@" mapcache-1.4.0/nginx/nginx.conf000066400000000000000000000074121255567662100164150ustar00rootroot00000000000000 #user nobody; worker_processes 4; #master_process on; #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; #pid logs/nginx.pid; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' # '$status $body_bytes_sent "$http_referer" ' # '"$http_user_agent" "$http_x_forwarded_for"'; #access_log logs/access.log main; sendfile on; #tcp_nopush on; #keepalive_timeout 0; keepalive_timeout 5; #gzip on; server { listen 8083; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; location / { root html; index index.html index.htm; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } location ~ ^/mapcache(?/.*|$) { set $url_prefix "/mapcache"; mapcache /home/tbonfort/dev/mapserver-trunk/mapcache/mapcache.xml; } location ~ ^/mapcache(?/.*|$) { set $url_prefix "/mapcache"; mapcache /home/tbonfort/dev/mapcache/mapcache-local.xml; #don't render uncached tiles ourself, forward them to fcgi or apache instance #error_page 404 = @apache_mapcache; error_page 404 = @fcgi_mapcache; } #proxy for a mapcache instance running on another server location @apache_mapcache { proxy_pass http://localhost:8081; } #mapcache as fcgi running with an external spawning manager (e.g. spawn-fcgi or fcgistarter) location @fcgi_mapcache { fastcgi_pass localhost:9001; fastcgi_param QUERY_STRING $query_string; fastcgi_param REQUEST_METHOD $request_method; fastcgi_param CONTENT_TYPE $content_type; fastcgi_param CONTENT_LENGTH $content_length; fastcgi_param PATH_INFO $path_info; } # proxy the PHP scripts to Apache listening on 127.0.0.1:80 # #location ~ \.php$ { # proxy_pass http://127.0.0.1; #} # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # #location ~ \.php$ { # root html; # fastcgi_pass 127.0.0.1:9000; # fastcgi_index index.php; # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; # include fastcgi_params; #} # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # #location ~ /\.ht { # deny all; #} } # another virtual host using mix of IP-, name-, and port-based configuration # #server { # listen 8000; # listen somename:8080; # server_name somename alias another.alias; # location / { # root html; # index index.html index.htm; # } #} # HTTPS server # #server { # listen 443; # server_name localhost; # ssl on; # ssl_certificate cert.pem; # ssl_certificate_key cert.key; # ssl_session_timeout 5m; # ssl_protocols SSLv2 SSLv3 TLSv1; # ssl_ciphers HIGH:!aNULL:!MD5; # ssl_prefer_server_ciphers on; # location / { # root html; # index index.html index.htm; # } #} } mapcache-1.4.0/nginx/ngx_http_mapcache_module.c000066400000000000000000000245011255567662100216060ustar00rootroot00000000000000#include #include #include #include "../include/mapcache.h" #include #include #include apr_pool_t *process_pool = NULL; static char *ngx_http_mapcache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static ngx_command_t ngx_http_mapcache_commands[] = { { ngx_string("mapcache"), NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_http_mapcache, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, ngx_null_command }; typedef struct { mapcache_context ctx; ngx_http_request_t *r; } mapcache_ngx_context; static void ngx_mapcache_context_log(mapcache_context *c, mapcache_log_level level, char *message, ...) { mapcache_ngx_context *ctx = (mapcache_ngx_context*)c; va_list args; if(!c->config || level >= c->config->loglevel) { va_start(args,message); ngx_log_error(NGX_LOG_ALERT, ctx->r->connection->log, 0, apr_pvsprintf(c->pool,message,args)); va_end(args); } } static mapcache_context* ngx_mapcache_context_clone(mapcache_context *ctx) { mapcache_context *nctx = (mapcache_context*)apr_pcalloc(ctx->pool, sizeof(mapcache_ngx_context)); mapcache_context_copy(ctx,nctx); ((mapcache_ngx_context*)nctx)->r = ((mapcache_ngx_context*)ctx)->r; apr_pool_create(&nctx->pool,ctx->pool); return nctx; } static void * ngx_http_mapcache_create_conf(ngx_conf_t *cf) { apr_initialize(); atexit(apr_terminate); apr_pool_initialize(); apr_pool_create(&process_pool,NULL); 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; ctx->config = NULL; return ctx; } static void ngx_http_mapcache_write_response(mapcache_context *ctx, ngx_http_request_t *r, mapcache_http_response *response) { if(response->mtime) { time_t if_modified_since; if(r->headers_in.if_modified_since) { if_modified_since = ngx_http_parse_time(r->headers_in.if_modified_since->value.data, r->headers_in.if_modified_since->value.len); if (if_modified_since != NGX_ERROR) { apr_time_t apr_if_m_s; apr_time_ansi_put ( &apr_if_m_s, if_modified_since); if(apr_if_m_smtime) { r->headers_out.status = NGX_HTTP_NOT_MODIFIED; ngx_http_send_header(r); return; } } } char *datestr; datestr = apr_palloc(ctx->pool, APR_RFC822_DATE_LEN); apr_rfc822_date(datestr, response->mtime); apr_table_setn(response->headers,"Last-Modified",datestr); } if(response->headers && !apr_is_empty_table(response->headers)) { const apr_array_header_t *elts = apr_table_elts(response->headers); int i; for(i=0; inelts; i++) { apr_table_entry_t entry = APR_ARRAY_IDX(elts,i,apr_table_entry_t); if(!strcasecmp(entry.key,"Content-Type")) { r->headers_out.content_type.len = strlen(entry.val); r->headers_out.content_type.data = (u_char*)entry.val; } else { ngx_table_elt_t *h; h = ngx_list_push(&r->headers_out.headers); if (h == NULL) { return; } h->key.len = strlen(entry.key) ; h->key.data = (u_char*)entry.key ; h->value.len = strlen(entry.val) ; h->value.data = (u_char*)entry.val ; h->hash = 1; } } } if(response->data) { r->headers_out.content_length_n = response->data->size; } int rc; r->headers_out.status = response->code; rc = ngx_http_send_header(r); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return; } if(response->data) { ngx_buf_t *b; ngx_chain_t out; b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); if (b == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Failed to allocate response buffer."); return; } b->pos = ngx_pcalloc(r->pool,response->data->size); memcpy(b->pos,response->data->buf,response->data->size); b->last = b->pos + response->data->size; b->memory = 1; b->last_buf = 1; b->flush = 1; out.buf = b; out.next = NULL; ngx_http_output_filter(r, &out); } } static ngx_http_module_t ngx_http_mapcache_module_ctx = { NULL, /* preconfiguration */ NULL, /* postconfiguration */ NULL, /* create main configuration */ NULL, /* init main configuration */ NULL, /* create server configuration */ NULL, /* merge server configuration */ ngx_http_mapcache_create_conf, /* create location configuration */ NULL /* merge location configuration */ }; static ngx_int_t ngx_mapcache_init_process(ngx_cycle_t *cycle) { apr_initialize(); atexit(apr_terminate); apr_pool_initialize(); apr_pool_create(&process_pool,NULL); return NGX_OK; } static void ngx_mapcache_exit_process(ngx_cycle_t *cycle) { apr_pool_destroy(process_pool); } ngx_module_t ngx_http_mapcache_module = { NGX_MODULE_V1, &ngx_http_mapcache_module_ctx, /* module context */ ngx_http_mapcache_commands, /* module directives */ NGX_HTTP_MODULE, /* module type */ NULL, /* init master */ NULL, /* init module */ ngx_mapcache_init_process,/* init process */ NULL, /* init thread */ NULL, /* exit thread */ ngx_mapcache_exit_process, /* exit process */ ngx_mapcache_exit_process, /* exit master */ NGX_MODULE_V1_PADDING }; static ngx_str_t pathinfo_str = ngx_string("path_info"); static ngx_int_t pathinfo_index; static ngx_str_t urlprefix_str = ngx_string("url_prefix"); static ngx_int_t urlprefix_index; static ngx_int_t ngx_http_mapcache_handler(ngx_http_request_t *r) { int ret = NGX_HTTP_OK; if (!(r->method & (NGX_HTTP_GET))) { return NGX_HTTP_NOT_ALLOWED; } mapcache_ngx_context *ngctx = ngx_http_get_module_loc_conf(r, ngx_http_mapcache_module); mapcache_context *ctx = (mapcache_context*)ngctx; apr_pool_create(&(ctx->pool),process_pool); ngctx->r = r; mapcache_request *request = NULL; mapcache_http_response *http_response; ngx_http_variable_value_t *pathinfovv = ngx_http_get_indexed_variable(r, pathinfo_index); char* pathInfo = apr_pstrndup(ctx->pool, (char*)pathinfovv->data, pathinfovv->len); char *sparams = apr_pstrndup(ctx->pool, (char*)r->args.data, r->args.len); apr_table_t *params = mapcache_http_parse_param_string(ctx, sparams); mapcache_service_dispatch_request(ctx,&request,pathInfo,params,ctx->config); if(GC_HAS_ERROR(ctx) || !request) { ngx_http_mapcache_write_response(ctx,r, mapcache_core_respond_to_error(ctx)); goto cleanup; } http_response = NULL; if(request->type == MAPCACHE_REQUEST_GET_CAPABILITIES) { mapcache_request_get_capabilities *req = (mapcache_request_get_capabilities*)request; ngx_http_variable_value_t *urlprefixvv = ngx_http_get_indexed_variable(r, urlprefix_index); char *url = apr_pstrcat(ctx->pool, "http://", apr_pstrndup(ctx->pool, (char*)r->headers_in.host->value.data, r->headers_in.host->value.len), apr_pstrndup(ctx->pool, (char*)urlprefixvv->data, urlprefixvv->len), "/", NULL ); http_response = mapcache_core_get_capabilities(ctx,request->service,req,url,pathInfo,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(ctx,req_tile); } 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(ctx,req_map); #ifdef NGINX_RW } else if( request->type == MAPCACHE_REQUEST_PROXY ) { mapcache_request_proxy *req_proxy = (mapcache_request_proxy*)request; http_response = mapcache_core_proxy_request(ctx, req_proxy); } 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(ctx,req_fi); #endif #ifdef DEBUG } else { ctx->set_error(ctx,500,"###BUG### unknown request type"); #endif } if(GC_HAS_ERROR(ctx)) { // ngx_http_mapcache_write_response(ctx,r, mapcache_core_respond_to_error(ctx)); goto cleanup; } #ifdef DEBUG if(!http_response) { ctx->set_error(ctx,500,"###BUG### NULL response"); ngx_http_mapcache_write_response(ctx,r, mapcache_core_respond_to_error(ctx)); goto cleanup; } #endif ngx_http_mapcache_write_response(ctx,r,http_response); cleanup: if(GC_HAS_ERROR(ctx)) ret = ctx->_errcode?ctx->_errcode:500; ctx->clear_errors(ctx); apr_pool_destroy(ctx->pool); return ret; } static char * ngx_http_mapcache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { mapcache_context *ctx = conf; ngx_str_t *value; value = cf->args->elts; char *conffile = (char*)value[1].data; ctx->config = mapcache_configuration_create(ctx->pool); mapcache_configuration_parse(ctx,conffile,ctx->config,1); if(GC_HAS_ERROR(ctx)) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,ctx->get_error_message(ctx)); return NGX_CONF_ERROR; } mapcache_configuration_post_config(ctx, ctx->config); if(GC_HAS_ERROR(ctx)) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,ctx->get_error_message(ctx)); return NGX_CONF_ERROR; } mapcache_connection_pool_create(&ctx->connection_pool,ctx->pool); ctx->config->non_blocking = 1; ngx_http_core_loc_conf_t *clcf; clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); clcf->handler = ngx_http_mapcache_handler; pathinfo_index = ngx_http_get_variable_index(cf, &pathinfo_str); if (pathinfo_index == NGX_ERROR) { return NGX_CONF_ERROR; } urlprefix_index = ngx_http_get_variable_index(cf, &urlprefix_str); if (urlprefix_index == NGX_ERROR) { return NGX_CONF_ERROR; } return NGX_CONF_OK; } mapcache-1.4.0/nmake.opt000066400000000000000000000233771255567662100151270ustar00rootroot00000000000000 ######################################################################## # nmake.opt - mapcache ######################################################################## #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Uncomment the following if you are building for 64-bit windows # (x64). You'll need to have PATH, INCLUDE and LIB set up for 64-bit # compiles. #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #WIN64=YES #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Check compiler version given in command line # nmake -f makefile.vc MSVC_VER=xxxx # 1310 = 7.1 (2003) 1400 = 8.0 (2005) #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !IFNDEF MSVC_VER #assume msvc 7.1 MSVC_VER=1500 !ENDIF #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # File locations and where to install things # ---------------------------------------------------------------------- # If you are using the MapServer Build Kit, almost everything should be # relative to this directory throughout this option file. #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Set the following to point to the directory adjacent to the dependent libs. !IFNDEF MAPCACHE_BASE MAPCACHE_BASE = D:\build\mapserver-buildkit-2008\mapcache-git-master #MAPCACHE_BASE = . !ENDIF #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Optmization, debug, and related compile flags. #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !IF $(MSVC_VER) == 1400 # Optimized, with using MSVCRT. OPTFLAGS = /nologo /Ox /MD $(WARNING_LEVEL) $(DEBUG) /EHsc /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE # Debug with MSVCRT #OPTFLAGS = /nologo /Zi /MD $(WARNING_LEVEL) $(DEBUG) /EHsc /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE #LDFLAGS = /NODEFAULTLIB:msvcrt /NODEFAULTLIB:libcd /DEBUG !ELSE # Optimized, with using MSVCRT. OPTFLAGS = /nologo /Ox /MD $(WARNING_LEVEL) $(DEBUG) /EHsc # Debug with MSVCRT #OPTFLAGS = /nologo /Zi /MDd $(WARNING_LEVEL) $(DEBUG) /EHsc #LDFLAGS = /NODEFAULTLIB:msvcrt /NODEFAULTLIB:libcd /DEBUG !ENDIF # Set the Warning level for the compiler (defaults to W1) WARNING_LEVEL=/W3 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Debugging Options # ---------------------------------------------------------------------- #NEED_NONBLOCKING_STDERR=-DNEED_NONBLOCKING_STDERR ENABLE_STDERR_DEBUG=-DENABLE_STDERR_DEBUG # Set the linker debug option LDEBUG=/debug # DEBUG Build flags # Set the DEBUG flag if you wish to make a debug build DEBUG=/DDEBUG #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # APR: Apache Protable Runtime library #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ APR_DIR = $(MAPCACHE_BASE)\..\apr-1.4.5 APR_ICONV_DIR = $(MAPCACHE_BASE)\..\apr-iconv-1.2.1 APR_UTIL_DIR = $(MAPCACHE_BASE)\..\apr-util-1.4.1 # APACHE HTTPD (Only required for apache module) APACHE_DIR=$(MAPCACHE_BASE)\..\httpd-2.2.22 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # PNG support #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PNG_DIR=$(MAPCACHE_BASE)\..\libpng-1.5.10 ZLIB_DIR=$(MAPCACHE_BASE)\..\zlib-1.2.7 ZLIB_INC=-I$(ZLIB_DIR) CURL=-DUSE_CURL CURL_DIR=$(MAPCACHE_BASE)\..\curl-7.25.0 WINSOCK_LIB ="C:\Program Files\Microsoft SDKs\Windows\v6.1\Lib\WS2_32.Lib" JPEG_DIR=$(MAPCACHE_BASE)\..\jpeg-8d JPEG_INC = -I$(JPEG_DIR) #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # SQLite3 Support # ---------------------------------------------------------------------- # Uncomment, and update accordingly. SQLITE_DEF=-DUSE_SQLITE SQLITE_DIR=$(MAPCACHE_BASE)\..\libspatialite-amalgamation-2.3.1 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # TIFF Support # ---------------------------------------------------------------------- # Uncomment, and update accordingly. TIFF_DEF=-DUSE_TIFF -DUSE_TIFF_WRITE TIFF_DIR=$(MAPCACHE_BASE)\..\libtiff-4.0.1 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # GEOTIFF Support # ---------------------------------------------------------------------- # Uncomment, and update accordingly. GEOTIFF_DEF=-DUSE_GEOTIFF GEOTIFF_DIR=$(MAPCACHE_BASE)\..\libgeotiff-svn-05232012 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # FastCGI Support # ---------------------------------------------------------------------- # Uncomment, and update accordingly. FCGI_DEF=-DUSE_FASTCGI FCGI_DIR=$(MAPCACHE_BASE)\..\fcgi-2.4.0 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # GDAL/OGR Support # ---------------------------------------------------------------------- # Uncomment, and update accordingly. #GDAL_DEF=-DUSE_GDAL #GDAL_DIR=$(MAPCACHE_BASE)\..\gdal-trunk #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # GEOS Support # ---------------------------------------------------------------------- # Uncomment, and update accordingly. #GEOS_DEF=-DUSE_GEOS #GEOS_DIR=$(MAPCACHE_BASE)\..\geos-3.3.7 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Berkeley DB Support # ---------------------------------------------------------------------- # Uncomment, and update accordingly. BDB_DEF=-DUSE_BDB BDB_DIR=$(MAPCACHE_BASE)\..\berkeley-db-5.3.21 ######################################################################## # Section II: Mapserver Rendering Configuration ######################################################################## ######################################################################## # Section VI: Support Libraries. ######################################################################## #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # REGEX Libary # ---------------------------------------------------------------------- # VC++ does not include the REGEX library... so we must provide our one. # The following definitions will try to build GNU regex-0.12 located in the # regex-0.12 sub-directory. # If it was not included in the source distribution, then you can get it from: # ftp://ftp.gnu.org/pub/gnu/regex/regex-0.12.tar.gz # Provide the full path to the REGEX project directory # You do not need this library if you are compiling for PHP mapscript. # In that case the PHP regex library will be used instead #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ REGEX_DIR=$(MAPCACHE_BASE)\..\regex-0.12 # Set the png support libaries !IFDEF PNG_DIR !IFNDEF PNG_LIB PNG_LIB=$(PNG_DIR)\projects\visualc71\Win32_LIB_Release\libpng.lib $(ZLIB_DIR)/contrib/vstudio/vc9/x86/ZlibDllRelease/zlibwapi.lib !ENDIF PNG_INC=-I$(PNG_DIR) !ENDIF CURL_INC = -I$(CURL_DIR)/include CURL_LIB = $(CURL_DIR)/lib/libcurl_imp.lib !IFDEF JPEG_DIR JPEG_LIB=$(JPEG_DIR)/libjpeg.lib !ENDIF !IFDEF SQLITE_DIR SQLITE_LIB=$(SQLITE_DIR)\lib\spatialite_i.lib SQLITE_INC=-I$(SQLITE_DIR)\include !ENDIF !IFDEF TIFF_DIR TIFF_LIB=$(TIFF_DIR)\libtiff\libtiff_i.lib TIFF_INC=-I$(TIFF_DIR)\libtiff !ENDIF !IFDEF GEOTIFF_DIR GEOTIFF_LIB=$(GEOTIFF_DIR)\geotiff_i.lib GEOTIFF_INC=-I$(GEOTIFF_DIR) -I$(GEOTIFF_DIR)\libxtiff !ENDIF FCGI_LIB=$(FCGI_DIR)\libfcgi\Release\libfcgi.lib FCGI_INC=-I$(FCGI_DIR)\include !IFDEF GDAL_DIR GDAL_INC = -I$(GDAL_DIR)/gcore -I$(GDAL_DIR)/alg -I$(GDAL_DIR)/ogr -I$(GDAL_DIR)/port GDAL_LIB = $(GDAL_DIR)/gdal_i.lib !ENDIF !IFDEF GEOS_DIR GEOS_LIB=$(GEOS_DIR)/src/geos_c_i.lib GEOS_INC=-I$(GEOS_DIR)/include -I$(GEOS_DIR)/capi !ENDIF !IFDEF BDB_DIR BDB_LIB=$(BDB_DIR)/build_windows/Win32/Release/libdb53.lib BDB_INC=-I$(BDB_DIR)/build_windows !ENDIF ######################################################################## # Section VII: Variable Setup ######################################################################## # Should not need to be updated. ######################################################################## MAPCACHE_LIB = mapcache.lib MAPCACHE_INC= -I$(MAPCACHE_BASE)\include #APR APR_INC=-I$(APR_DIR)\include -I$(APR_ICONV_DIR)\include -I$(APR_UTIL_DIR)\include #APR_LIB=$(APR_DIR)/lib/apr-1.lib $(APR_DIR)/lib/aprutil-1.lib $(APR_DIR)/lib/libapr-1.lib $(APR_DIR)/lib/libaprutil-1.lib #APR_LIB=$(APR_DIR)/LibR/apr-1.lib $(APR_DIR)/Release/libapr-1.lib $(APR_ICONV_DIR)/LibR/apriconv-1.lib $(APR_UTIL_DIR)/LibR/aprutil-1.lib $(APR_UTIL_DIR)/Release/libaprutil-1.lib APR_LIB=$(APR_DIR)/Release/libapr-1.lib $(APR_ICONV_DIR)/Release/libapriconv-1.lib $(APR_UTIL_DIR)/Release/libaprutil-1.lib #APACHE APACHE_INC=-I$(APACHE_DIR)\include APACHE_LIB=$(APACHE_DIR)\Release\libhttpd.lib # Setup REGEX object and includes REGEX_OBJ=$(REGEX_DIR)\regex.obj REGEX_INC=-I$(REGEX_DIR) # REGEX needs some special flags... here they are for VC++ 6.0 REGEX_OPT=-DHAVE_STRING_H -DREGEX_MALLOC ####################################################################### # Section IX: Collect compiler flags ######################################################################## # Should not need to be updated. ######################################################################## !IFNDEF EXTERNAL_LIBS EXTERNAL_LIBS= $(PNG_LIB) $(CURL_LIB) $(JPEG_LIB) $(APR_LIB) $(APACHE_LIB) $(FRIBIDI_LIB) $(SQLITE_LIB) $(TIFF_LIB) $(GEOTIFF_LIB) $(FCGI_LIB) $(GDAL_LIB) $(GEOS_LIB) $(BDB_LIB) !ENDIF LIBS=$(MAPCACHE_LIB) $(EXTERNAL_LIBS) !IFNDEF INCLUDES INCLUDES=$(MAPCACHE_INC) $(APR_INC) $(APACHE_INC) $(REGEX_INC) $(PNG_INC) $(ZLIB_INC) $(CURL_INC) $(JPEG_INC) $(SQLITE_INC) $(TIFF_INC) $(GEOTIFF_INC) $(FCGI_INC) $(GDAL_INC) $(GEOS_INC) $(BDB_INC) !ENDIF MAPCACHE_DEFS =$(REGEX_OPT) $(SQLITE_DEF) $(TIFF_DEF) $(GEOTIFF_DEF) $(FCGI_DEF) $(GDAL_DEF) $(GEOS_DEF) $(BDB_DEF) !IFDEF WIN64 MAPCACHE_CFLAGS=$(INCLUDES) $(MAPCACHE_DEFS) -DWIN32 -D_WIN32 -DUSE_GENERIC_MS_NINT !ELSE MAPCACHE_CFLAGS=$(INCLUDES) $(MAPCACHE_DEFS) -DWIN32 -D_WIN32 !ENDIF mapcache-1.4.0/packaging/000077500000000000000000000000001255567662100152205ustar00rootroot00000000000000mapcache-1.4.0/packaging/centos/000077500000000000000000000000001255567662100165135ustar00rootroot00000000000000mapcache-1.4.0/packaging/centos/mapcache.spec000066400000000000000000000025721255567662100211360ustar00rootroot00000000000000Name: mapcache Version: 1.1dev Release: 1%{?dist} Summary: Caching server for WMS layers Group: Development/Tools License: MIT URL: http://mapserver.org/trunk/en/mapcache/ Source: mapcache-%{version}.tar.gz #Obtain source using git archive available at https://github.com/mapserver/mapcache: #git archive --format=tar --prefix=mapcache-1.1dev/ master | gzip > mapcache-1.1dev.tar.gz #or adjust archive available at: https://github.com/mapserver/mapcache/archive/master.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) Requires: webserver BuildRequires: httpd-devel fcgi-devel cmake libcurl-devel BuildRequires: geos-devel proj-devel gdal-devel libjpeg-turbo-devel BuildRequires: libpng-devel libtiff-devel pixman-devel sqlite-devel %description MapCache is a server that implements tile caching to speed up access to WMS layers. The primary objectives are to be fast and easily deployable, while offering the essential features (and more!) expected from a tile caching solution. %prep %setup -q -n %{name}-%{version} %build %cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr . make %{?_smp_mflags} %install rm -rf %{buildroot} make DESTDIR=%{buildroot} \ install %clean rm -rf %{buildroot} %files %defattr(-,root,root) %doc INSTALL README* LICENSE %{_bindir}/* %{_libdir}/* %changelog mapcache-1.4.0/release-notes.sh000077500000000000000000000005131255567662100164000ustar00rootroot00000000000000#!/bin/bash commitdiff=$1 if test -z $commitdiff; then echo "usage: $0 startcommit..endcommit" exit fi git --no-pager log --no-merges --pretty=format:'%s (%an) : `%h `__' $commitdiff | gsed 's!#\([0-9]\+\)! `#\1 `__ !g' mapcache-1.4.0/scripts/000077500000000000000000000000001255567662100147635ustar00rootroot00000000000000mapcache-1.4.0/scripts/vagrant/000077500000000000000000000000001255567662100164255ustar00rootroot00000000000000mapcache-1.4.0/scripts/vagrant/mapcache.sh000077500000000000000000000002661255567662100205310ustar00rootroot00000000000000#!/bin/sh NUMTHREADS=2 # we have 2 cpus configured export NUMTHREADS cd /vagrant mkdir build_vagrant cd build_vagrant cmake -DWITH_MEMCACHE=1 .. make -j $NUMTHREADS make install mapcache-1.4.0/scripts/vagrant/packages.sh000077500000000000000000000011501255567662100205370ustar00rootroot00000000000000#!/bin/sh sed -i 's#deb http://us.archive.ubuntu.com/ubuntu/#deb mirror://mirrors.ubuntu.com/mirrors.txt#' /etc/apt/sources.list export DEBIAN_FRONTEND=noninteractive apt-get update apt-get install -y python-software-properties add-apt-repository -y ppa:ubuntugis/ubuntugis-unstable apt-get update apt-get -y upgrade # install packages we need apt-get install -q -y build-essential pkg-config cmake libgeos-dev rake vim \ bison flex libgdal1-dev libproj-dev libpng12-dev libjpeg-dev libfcgi-dev \ libcurl4-gnutls-dev apache2-prefork-dev libtiff4-dev libpixman-1-dev \ libsqlite3-dev libmemcached-dev mapcache-1.4.0/scripts/vagrant/virtualbox-fix.sh000077500000000000000000000002321255567662100217440ustar00rootroot00000000000000#!/bin/sh if [ ! -e "/usr/lib/VBoxGuestAdditions" ]; then ln -s /opt/VBoxGuestAdditions-4.3.10/lib/VBoxGuestAdditions /usr/lib/VBoxGuestAdditions fi mapcache-1.4.0/static/000077500000000000000000000000001255567662100145635ustar00rootroot00000000000000mapcache-1.4.0/static/bench_cached.png000066400000000000000000000556471255567662100176600ustar00rootroot00000000000000‰PNG  IHDR€§¨îqgsRGB®Îé pHYs  šœtIMEÚ  ’ʺ›tEXtCommentCreated with GIMPW IDATxÚìÝy\TåþðÏ0# .‚ ‹b,"™ † *¨¸äŽ–VšËÕPÔT®zͬkÞ{³ÅLSsé§¢’-"¸ç’"®€$:‚šš²+² œß‡FB aø¼_½zÍ<œ9sæ;3~æyÎsΑ‚"""ª[:,˜ˆˆˆLDDD `"""01€‰ˆˆÀDDDÄ&""b˜ˆˆˆLDDÄ&"""0˜ˆˆˆÀDDD `"""b1€‰ˆˆˆLÔÐ8;;oܸ±Ú744<{ölå˸»»¯Y³æùÛDÄ&"Mrss[·núî°aÃY–ªÖHãd,Q“²lÙ2íÚà‚‚===¾qÄ0UH¥R­X±ÂÂÂBWW×ÖÖöàÁƒvìØÑ­[7¹\nllìíí——WÉÂrrr¦L™¢P(,--Õ /_¾¼C‡r¹ÜÅÅ%,,LlÏÌÌœ0a‚¾¾~çÎ8ðü&Mž<ùâÅ‹‹-’H$mÛ¶EÅÃÎ=…ZPPµµµx{×®]‰ääÉ“  ÅåË—8;;¯^½ú7Þ000èڵ믿þúÛo¿õêÕ«Y³fC† ÉÌÌîïïocc#“ÉŒß|óÍç7ÆÙÙyÅŠ#FŒhÕªÕ—_~ù‚/_=ß±cÇ€€õÚ /\¸PÉk|~{ž¯‘æ D¤!sæÌ±´´}ÿþýsçÎuïÞ}éÒ¥•,ìääÔ¦M›mÛ¶ÅÅÅ-_¾ÜÐÐðÉ“'‚ ÌŸ?¿OŸ>¡¡¡÷îÝ[½zµ‘‘Ñ£GAxýõ×»wï}õêUWWW™L\f«\]]¿úê+õÝ|ñÅÏß®è)ÔRSSÄÇÇ ‚ðüÃØØxÅŠ‚ „††êëëççç«·ûöí7nÜxóÍ7ííí=<<Μ9ó믿ÚÙÙ‰¯=))I&“íÞ½;!!A©TîØ±ãùJ:99µjÕêìÙ³‚ dggWòòœœ~ûí·˜˜˜þýû«_¾••Õ¾}ûÔkS(çÏŸ¯è5V´=eêF¤q `"ÍHOO—ÉdÇŽ«d™#GŽØØØT²°““Ó¬Y³ÄÛ*•ÊÐÐ0888##CWW7..N½X¯^½vîÜùðáC .ˆ¡¡¡ªÀ=E™UÙÛÛïÝ»W›U«Võïß_„/¾øÂÝÝýùíW*•<(Þ]»v­‹‹‹ 1112™¬Lº?_oooñvå/?,,LlŒˆˆP¿ür¸¢õT´= `ªmÜL¤J¥R¥R¹»»—i¿xñâ¿þõ¯¨¨¨ÜÜ\-Z´¨daêRR©ÔØØ833S©TÚÙÙ•^,11166Võ°­T*­öÆ—ûeëׯßùó瘒’âããóé§Ÿœ?¾_¿~Ïo›6mÊ܇ \]]mllFŒáéé9aƒç7ÉÞÞ¾òm‹_µØÒ£G™LV×ø‚ÛCÄ}ÀDÚ$77wäÈ‘C‡ýã?Š‹‹ƒƒƒ‹ŠŠþæ;©£Sf'Qqq1€¬¬¬Ò¿—/_..¬]©TZí®ä)Êð¹sçÎ;×·o_ccã—^z),,ìâÅ‹¥¸Ìö—¾+‚ØræÌ™Ÿ~ú©C‡ÿùÏzôè‘““óü&©Óô·­4‰DR¦†•¬ç·‡ˆLÔ@988Èd²Ò·nÝÊÈÈX²d‰‰‰‰D"‰‰‰©dáÊ×|âĉçûˆ‚ ÄÅÅ©Ÿ«  àù‡K¥R1ªñϰR©ýñãÇòóó£££ûõë'.`jjjooVÕv~2ˆˆ¨VÉ´b+:äååUTTdmm}âÄ ]]]ýû÷wssëØ±ã­[·Þÿý±cdž„„¤¥¥™ššªkff–ššZÕv~2ˆˆˆ èèè”””õë×1"""¢yóæ^^^â_­­­{÷î}õêU“2•H$害ªí/òW""jjª½'W;¸yóæŽŽŽŽŽŽýúõ366>zôè¤I“J/гgOñññvvvR©´t699ÙÃÃÃØØ¸JíµQëB"‘ð%ð%ð-àKàKÐÔK¨öcµì8ࢢ¢r§(GFFèСƒ\.ïÞ½ûùóçÅöÔÔÔØØX—ª¶ó7Õzß¹›;wî‘#G"""Ž;6dÈccã´´4A¼½½>|åÊ•½{÷ZYY¹¹¹‰ËïÚµK¡P)•ÊQ£F9::ªTªj´—K+*V9¾¾n?__BCx Z0ýäÉooïäää-Z¸¹¹…„„´nÝ@ZZÚÌ™3=zdll,•J«ÑNDDT‹Ã×Ú>þ^÷ÃýÜéR¿¢ÝãÛGgu‡» ø.pûùø´ô%ð\ФMŠPä o¼‚/ñe;´Š¡_á«XIJ2D¤uÀ¤M¶`K34CoœÆé$øÀ'±žð´†õ|Ì?†c¹Èe•ˆH;zÏ‚®Z½$¬X½I@BôAHt)ó§ÄÇñã8ˆÞè= Æc¸=ìY4"j°¡À0i…Xè ïçÓÀËxy –¨»Å7pƒÝb¢ÒÜÝÝ׬YóüíÚãìì¼qãFVžLZï0_ÅÕðAå‹Âp Ælöxp-aɽÅÔ4¹¹¹­[·N}wذaŽŽŽ,Kƒ"c ¨áËFö{xïÿðÍÐìÅõ2^{ÆÙÈ>ÓGqt-ÖêAï5¼6õz5QU-[¶ŒE`˜¨ÊVbe?ô„AÕ{xénñ`·˜½É“'_¼xqÑ¢E‰¤mÛ¶¨xØY¥R-_¾\< ‹‹‹úZp*•jÅŠººº¶¶¶°cÇŽnݺÉårcccoooõ¥ÙË]@NNΔ)S …¥¥e```åÏÈ&jp¢åÿ/ñ¥FÖVfo±JOx¾„—¸·˜WWׯ¾úJ„äääJ–ôõõ={öl``àÍ›7Ç7|øð´´4ï½÷Þž={¶mÛvçέ[·J¥ëÖ­»}ûöÂÃÃW®\)®¤Ü…|ùå—ýû÷ŠŠš2eÊÔ©S³²²*yƦH &vâ4í¢T¯ ¯n¶×ê³\®~.|>PØBh1D²VX{C¸Áâ“VS°hÀ€_|ñE™ÛºººqqqêÅzõêµsçÎôôt™LvìØ±JÖäÈA*ZØÉÉiÖ¬Y%_d•ÊÐÐ088¸¢gä©(‰œÍجýé˜^«ÏRzoñ/øEÜ[,‡|8†so15bJ¥²°°ÐÎήtcbb¢R©T©Tîîîe–¿xñâ¿þõ¯¨¨¨ÜÜ\-Z´WRîÂÔÓ¾¤R©±±qfffEÏXþöÕ×å_ëêXS05\ HX…U!‘ Ž¾‡†0‹±c1[ü%¾|oòØbj”Š‹‹dee– ÚçÎÍÍ9r¤¯¯ïŒCBBFŽYùúuttÊ ¸VôŒõ„õ…û€©áZ€³1»Üë@é½Ås0GÜ[lî-&­ •Jÿö2™ìĉ嶇„„”n¼uëVFFÆ’%KLLL$ILLL% Wõ›&05P‡q81˱¼Þ·Dì‹oàA 9‰š´‚……Å¥K—’““ÓÓÓ+ZÆÈÈÈÇÇgÞ¼yAAAñññ‘‘‘+V¬P*•FFF³fÍòöö>~üx||ü™3gNž€VhUÑ/ñšš¿ö<ä=ÅÓj?\U²j²È(]ó"½ŠW_Çë¯á5üöÕ½Gxt'žà‰–™È,F±ø9)“ å6й¨þÁ*æ¢ø9‘Fñƒýâµ Œp2ãáø1>f)*’‹Ü|ä—û'B2*z ø¯Õ †SiâP[Í·\ú5™Ó.ƒ¬9š×dÊüÈÈGþE\ @@(Bcðëx}FŸ±Ú–ˆÄ "0‘.p±†u ´Ð-¤Ša)…ôùFu.–nÔêP`œ0€ŠÃ8¼‹Ão íÐ#jÜÒ‘~÷aß%\ˆ0aFÕ0ìggçiӦ͛7¯~7£÷¤Þ¦³MÓÜÓâ7#½à5CÁ—½&¡ÀqPƒìù˜¿›™¾TÇZ£õtL?†cwqw ÆìÅ^ XŒÅXø‹žT†››Ûºuë^p±8Ä­Æjg8_ßq]ÕYõoü; I~ðÑü²3€©Aø7þ=b KAõÅFS1õŽÜýñ¿û,`1£wcw%cûT®«¸zÿ÷?Ÿòù` NBÒ—ø2à ã˜Å±!¢ ]Ö‡L E¢¾Ãwk°†¥ † ZMÁ”ƒ8ø&bb‚¬`5#ýà÷kã+ºÐ½³³óûï¿?tèP ‹Î;«/t_Ñò*•jÅŠººº¶¶¶êåsrr¦L™¢P(,--Õ /_¾¼C‡r¹ÜÅÅ%,,ìù ó÷÷·±±‘ÉdÆÆÆo¾ù&€É“'_¼xqÑ¢E‰¤mÛ¶Ùc¯Õ^‹U‹m`ãšêúàу¤‘Iñ:ñ?µýi r¤>CVEÛÙä𪙼p½_ñ×YpÞ)ìd)¨Áz"<Ù+ìõ¼Z-† ÷ Û 4¸~??¿Ó§Oß¿ÿܹsÝ»w_ºt©ú’ºÍ›7 áØ±cÍš5‹­dù9sæXZZ;vìþýûâ9™Å•´iÓfÛ¶mqqqË—/744|òä‰ óçÏïÓ§Ohhè½{÷V¯^mddôèÑ_^TRR’L&Û½{wBB‚R©Ü±cG¹WÞ¹kçº_×ýãÉ?Ìžš5»Ó¬Ï/}"…ÈÊ/H\îv6ÁP`œ0€ëÙ×ÂׄÅB1KA _¶ýƒðÃaBK¡åaȷ·…‡š} õ…îÅìœ1c†úOÆ Sgíó˧§§Ëd²cÇŽ•YÀÉÉiÖ¬Y%¿wU*CCÃàààŒŒ ]]ݸ¸8õb½zõÚ¹ó/¿ƒcbbd2Y™TV'k¡PxJ85[˜ÝVhÛCèñá?JAYzã+ àŠ¶³ †‡ ©>‰WüÝŠ­æ´Ô¸) ˜ˆ‰û°/‰31óN½„—<á¹ ÛâaµW{ñâE777…B!‘HFŽ™’’¢þ“úšö^~ùeñÂAå.¯T*U*•»»ûóëW¯D*•gff*•ÊÂÂB;;;ÉŸ®\¹’˜˜XúQ®®®666S¦LÙ½{wnn.€|ä?îûø{Ïïۡ݇øÐÖ_ÿúµ›ÁjÅj‰C™/W%ÛY†’zùû€©I˜ù>ðáu HëÀàu¼þ#~LBÒlÌF°-laÐlIAJ•V%^è~èСüñGqqqpppQQ‘ú¯*•J}»°°ðo—/ÿz2{‹‹‹dee•î-_¾¼Ì£Îœ9óÓO?uèÐaÕšU6Ël&ª&¶C»„·ÌšG!ê2.ÏÍûîàw«´1UØC ¡^þcSãw‡¯ãzC¸â/QµéC<Æï‘8óÏã|tñ€Ç&lJFò‹¬¡¢ Ý‹JÏ spp¨hyñZ÷!!!/ò¤âÂ'Nœ¨|±lìÔÁ©·?½ýð·‡I£’:Üî‹ØW¼âåfóÊ7^*•–{€l•¶³qcSýÈFö<ÌÛ‚-•œX‘H»’x,Æ~‡ï‘¸‹Bꇰ‘XÉ+ºÐ½è—_~Ù¾}{BBÂgŸ}1}úôŠ–722š5k–··÷ñãÇãããÅÉM=©‘‘‘ϼyó‚‚‚âãã###W¬X!ŽoHCÚNìì—ÙÏ,ßìÿ²þ¯_F¿ÿíýŸÎpyzóLajaaqéÒ¥äääôôôJ6¾ôbežúÅ·³‘㬠Nª¾‚ï;Â;¬5bO…§‡…ÃS…©­…Ön‚Û:aÝáA¹KîÝ»×ÜÜÜÂÂÂÕÕuÓ¦M …B=jÕªU£FjÖ¬YÇŽ+_¾  `Ù²eíÚµ“Éd¶¶¶‡W²aÃõsYYY‰²V­Zeee%“ÉÌÌÌ&Mšó0f³°y°0¸¥Ðòuáõµ kŒвeK©TÚ¥K—~øA\Cttt×®]¥R©™™Y%Sf±Ò³ ËÝÎ& <±bÕðT”…¨av×M`ÂjP£W€‚_ðKâ =ì'`ÂxŒ·„åß>°nÎ"y÷ˆÀë¸þ^ñC1´!œf¹Ñ‡‚Œå£:V„¢wñîgøŒéKM„ô^Ãk¯áµBžÆé|‚O^ÂKbwDÇzÙªÛ¸½û÷cÿ=܃1ïãýA¤=¾_u†Luí|£€b*¦²ÔÔèBw† ð-Ørgð*^툎¯ãõ ˜Ð ê`®áZ ÷cÿ#<‡qŸâÓ …”ïNÝÓ‚IX_}õU—.]är¹B¡4hPtt´úOkÖ¬177×××:tè½{÷4ÞNxøK$ƒl†lö$$}‚O~Çï½ÑÛΟá³ßñ»z±ˆˆM?ÿŠ_—c¹=ìGaT&27có<؈1é[oþ.n??¿ü1<<üĉžžž&&&¹¹¹‚ ìÚµK¡P*•ÊQ£F½üòË*•Jƒíœ„UÆ ãþ-ü›u *C%¨N §çsÄK}"|rS¸YÃu E…‹¾‚oG¡£`÷¾ð~„ÁR7œIXZ'ׯ_ ž>ÍÉÉiñâÅb{JJŠT*=uê”ÛÀwP8h'Ø=ž²D•$ñYáì\an;¡Ý+Â+ÿþ+ÄV#Ëç sÛ í» Ý>>ŽbXØÀÚtpffæŽ;lmm;uꔟŸݯ_?ñO¦¦¦öööaaašjçЈƩ¯øË‰*!…tlÄÆxÄoÀ†T¤ nè¶ «nàF%,@ÁÏøyfµC»eXf‹„ü†ß>ÂGŽp¬ûâìì¼qãF¾¡•î‰Ð‡òòò***²¶¶>qâ„®®nbbbQQ‘©©©z33³ÔÔÔ´´4´ó“¡qá#xxÀƒ¥ z:Ðé‡~ýÐoÖ]Æå Á–h9&`‚:Sów'öcÿ1늮^ðú¾È1NÄ~!ÑÑÑ)))ëׯ1bDDDÄóËH$åOêÑT{å ðààÊE"r/ö^Ã5–‚¨Iì WW¸®ÅÚP„ `F( p…k&2Oâd/ôñ_à‹¶hËrÕž¿M‡ª¿³Ú yóæŽŽŽƒ HLLå¿D5 Hú Ï—øòîíÄÎ6h3ÃÇï§pj6f‹é›žžîå奯¯ß¹sç   CCógÏP©TË—/ïСƒ\.wqqQïhËÉÉ™9s¦‘‘‘B¡7nœú‚H*•jÅŠººº¶¶¶°cÇŽnݺÉårcccooï¼¼¼JW>eÊ…Baii¨^¸Ü-iø4þ¿– º¨¨H¼Ô†\.ïÞ½ûùóçÅöÔÔÔØØXMµó«®A›°É†<ð—HƒIì—OðÉtL7†qé?ýãÿHLL ?tèÐÆóóóÅv__ß³gÏÞ¼ysܸqÇOKK0kÖ¬K—.?~<<<<33sôèÑb¨¼÷Þ{{öìÙ¶mÛ;w¶nÝj``@*•®[·îöíÛ_¹r¥¸òrðå—_öïß?**jÊ”)S§NÍÊʪdKš¢†?ÇlîܹGމˆˆ8vìØ!CŒÓÒÒÔ‡‰‡9::–>¬¨æíœ­„&‚IU§qQ5¤¦¦J$’ˆˆ’c¢¢¢gddèêêŠÇˆzõêµsç·êèèœ>}ºäÛúàD"¹téRzzºL&;vìX%ÏuäÈA*ZØÉÉiÖ¬Y%³U*CCÃJ¶¤i΂ւ}ÀOž<ñööNNNnÑ¢…››[HHHëÖ­¼óÎ;)))sçÎMKKëß¿ÿáÇ¥R©ÛI#ÞÃ{s1—Wü%ªqqq‰¤GâÝW^yEWW€R©,,,´³ûË×0111..®¸¸¸OŸ>b‹xM¥RY\\¬R©ÜÝÝˬÿâÅ‹ÿú׿¢¢¢rss´hÑB\y¹ pt,™)&•J333+Ú’ò;úõt¶ž:Û©¨¼{÷îŠþ´dÉ’%K–Ô^;ÕÐATBù=¾g)ˆêQqq1€¬¬,CCÃ2Šž[”››;räH__߇„„Œ9²ò‡èèè”p­hKê7ë ¯Lµ%Ùïá=^ñ—¨ÎØÙÙ©GžüöÛo………d2Ù‰'ž_^GG'44T¼ÿàÁƒ®]»ŠË‡„„”^øÖ­[K–,111‘H$111b{¹ W¤¢-išÀT[>ć1Ðî,QÝhӦ͘1cæÎ{íÚ5¥RùÏþS&“I$###ŸyóæÅÇÇGFF®X±B©Tš˜˜Lœ8qþüùW®\¹~ýúÔ©S{öìéââbdd4kÖ,ooïãÇÇÇÇŸ9sæäÉ“–––r¹üÈ‘#nß¾½víZñIË]¸¢-¬hKšèÆi u¶¿½IùUøÕL0{(rsñô)òò—‡§Oa`¹††ÐÕEóæÉТ¤R´j‰FFHЪ¤R´h™ Í›—d¹\ƒ¿d¹B=½’U˜Ü Çð·ñödL®ÆcÓÓñí·Ø¸/½„ 0z´&wå>}ŠK—JÂ8.nn%aüç‰â‰¤$ÄÆ–Ä­x#9/½;;ØÙÁÞ¾äÆßþ6ÍÉAA²³QXˆ¬,¨TÈÌDq122 xüÅÅÈÌDQžSùÒÓŸMŠÅxðææ%qkk {{técc–Š¡ÀfkÚ@ œƒ9¯ãõЏpë×#83f`î\­9B75µ¤[|êttJƨ B›6MúíV©šŠ§OѲeÉ䚦£¨wï"6ö/ó¤ Kz´bïÖÞ;s²=CÌ®}±ˆˆ÷pOeÿÉ)(À?býzdgã½÷0u* ­}™±%a|ö,¬­KƨÝÜgüøÂ·š}‚OÊô·nÅæÍxùe,\ˆaÃJf 6Žþ_xxI·8:..%cÔ=zhÍY0 þ®IIHMEBRS‘”„ädèê¢}{˜š¢}{˜™¡m[´kSStèSS˜™•}732J’øñã’ÃWJß翈7òòž-œ‘Qr¸KFFÙ´nÕªäðñ†¾>Z¶|vC<ãD™5ìqã?žMC7'ç/Ó£ÄÛÏOˆ%†˜\òg Ë+¸ÒÅ–ë×ñå—8po¼ Ð¥Kc~ùÙÙ.駤`àÀ’aêNêyÃÒÓ‘œü¬ÏZ¦#›™Y’¦Ï‡k»vhÛ¶œ6j›x°Jff97rrðôiɼ<-¹Q\Œ- P@_-ZÀÐÍš•ÜÐ×GóæÏn4oŽfÍ —##ãÙ<©›7ajú,nÅ:ð+N `pCµ »~ÂOGqTÝ;45ÅìÙX¼­[7­R$$<; ¦¡aɵ‡G­ÔáéÓrµtGV¡@Û¶03+¿#Û(¯+@’¼¼¿ÜÈÊÂÓ§%7òò]r#3ffÏæIÙÙ5­ÙÄfk=W¸þ ÿÑâÝŸ~ÂæÍnÒ5\»VÆ/ÂÞ¾dŒºoß* “¦¦>~>ksržõYËíÈr˜”ˆÌZ7fWqu$FÞÁJfìÓË–aÌÖ¦DAÁ³³`*•pu-£~é%¤¤”äk¹Y#£gÂÏwd›Úè˜À៶hû>bÊÄÅñмå{ü¸d‡ñ©S¸}:ý¥ÏÚ¾=Ú¶}Ö‘åYï‰ÀÄ._6²­`uW; d¦ÊĉpsÃüù¬ 1þ»*TM{±w¨Ó÷þ}œ9ƒéÓY""0Õ¦­Øê oõÝ 0m/c@Dô¢x"pªŽ+¸’ OxŠw³³áç‡_eaˆˆØ¦Ú´[ÞÅ»:~~vîÄÀ°´daˆˆ^§U±^œ„d £:ÝÄÍ6h@àà€Í›áîÎ1ئZ³{†a˜˜¾Nž„¾>Ó—ˆˆLµl+¶ÎÆlõݯ¾ÂÂ…¬ ˜jÓyœ/Fqôï*•ˆ‰Á¤I, ˜j¹ûû.Þ• ärtëÖaÎ^{œˆ¨Ê8¥¨ŠõjÚ“°á‘ lîàŽŒzô袢¢j´S„oñí»xW¼[P€Í›9ýŠˆ¨Úÿªj•   ‚ ,X°àí·ß~~''§Å‹‹·SRR¤Ré©S§ªÑ^.­«˜N½"¼¢¾»k—0t¨@DÔ”Õ$´lVNNŽB¡000ï:tH.—›˜˜LŸ>ýñãÇòóó£££ûõë'.`jjjooVÕvþ2{Þl)}ôѺuX´ˆU!"jCÐ|XSëÏÊÊZµjÕüùóuuuôïßÇŽ/^ܰaCppðرc¤¥¥™ššªeff–ššZÕv~2ÊHBÒœyo‹wÏžE~>† aaˆˆªI“cX²dÉÖ­[ÿï::‹/5jTÍWžŸŸ?~üx ‹?þXlñòòo8;;[[[÷îÝûêÕ«&ÏÍ’H$å®°ªí•/Ðè§FïÀŽ7ðFs4Ww,Àß•Šˆ¨ñhúŸ>ÞØØX*•–îÂ&''›ššVµ½’©h7@#VŒâmئ¾.à­·ø}$¢&Dãÿøk2€ÍÌÌ®]»VºåÚµk•‡Ù‹(((?~|ZZÚÑ£GÕ{ˈŒŒСC¹\Þ½{÷óçÏ‹í©©©±±±...UmçG­´Ÿñs[´íŽîâÝ 0}:/ýKDT ‘^=Ë–-³´´üùçŸssssss;fii¹lÙ²š¬³¸¸xäÈ‘íÚµ Vq”ŸŸ/‚··÷áǯ\¹²wï^+++77·?gçîR(AAAJ¥rÔ¨QŽŽŽ*•ªíœ­6R¹CØ!ÞÎÊLL„?þàäG"¢…‚&㤠 ÀÛÛ[&+Ù¯,“É||| k²Î¼¼¼ç4ܺuK„ &˜™™I¥RSSÓ™3g¦§§«õù矷oß^.—{zzÞ½{·Úí `Aþþh-´ÎrÅ»_-¼ñ¿tDD5 ÍŸXññãÇJ¥€ƒƒƒ‘‘QãÛ /4±SQ~ˆŸàÉz¬P\ ;;ìÙƒÞ½9xDDÔNE™––vðàÁWWW###¥RÉCz´š ªíØî oñîáÃ01aúi€&844ÔÆÆæ›o¾ùàƒÄÿEøÕWX¸%!"jØüÓO?eff²šZí2.ç"w CJ FbUˆˆvoܸ±mÛ¶kÖ¬¹qã˪¥Ýßwñ®ß|èp|„ˆ¨ÖhlJÑãÇ?~äÈ‘ãÇ·lÙrĈ#GŽtww—Ëåª^tÖc<¶†õ-Ü2IRñûï¼ø Q-†‚æã¤¨¨èòåËG=räÈÝ»w 4bĈ1cƘ™™1€¬uXøøðCdfâë¯ùÍ""Òª.íþýûb0`éÒ¥ à†I€à‡oñ­Üòòб#.^ÄK/ñ›ED¤µÌZk…³8;ó®á€o¿ÅÑ£8p€o5Q톂Œå£­Ø*žüY°n6mbIˆˆjç¹6u©H=“1ÀÉ“ËáîΪ1€©–íÄÎq× ­À“oÕ! ìÑ<{ölå ¸7¢.U#Û\ŒbØü€z¡—R OOܽ ==~/ˆˆj=4°xäÈ‘êÛùùù*•J*•J¥Ò‚‚‰Db``Í7©a:‰“F0ê…^֭Ü9L_"¢:¢!èì?ùûû÷îÝ;,,¬   ???..ÎËËkݺu¬rƒµ ÛÞÅ»=Âþý˜=›%!"ª«Þ³T­­­:äèè¨n)((èÙ³çµFtI÷Æ4€„nèöþ0„á'ŸàömìØÁoQ…‚&CJJJ*³‚ ¤¦¦òj˜þÿ7 “ a¨RaëVûKDT§49 zøðáÓ¦M»té’J¥áÆ'Nìß¿?«Ü© ÚŽíâÅyé_""íàíÛ·wêÔ©_¿~zzzºººŽŽŽzzzÛ·og• c8f‹nè}DD¤íldd’’rîܹ³gϦ¤¤üôÓO-[¶¬ájW¯^Ý£G¹\Þºuë·Þz«ô˜öš5kÌÍÍõõõ‡zïÞ=·7b[°E<ûUh(=⥉ˆêœ Q=Ú¹sçÿþ÷?ñîõë×SRRj¸Î¾}û~ýõ×—/_Þ·oŸµµµ›››Ø¾k×.…B¨T*GõòË/‹Cßšj/—Æ+V/î wM“\!W„7Þ¾þZ "¢j¨I(h2N._¾lddÔ«W/õ½ÿþûo½õ–Ÿ"((@FF† NNN‹/ÛSRR¤Ré©S§4ØÞˆø}áýEÂ"AþøC01²²ø%""ªëÖäô‚ –.]®nñòò ÖàSäää( ƒüüüèèè~ýú‰í¦¦¦öööaaašjoÄc(؉âô« 0m 9DDT×4yRTTÔ¾}ûJ·˜™™=|øPSëÏÊÊZµjÕüùóuuu‹ŠŠLMMK?WjjjZZšFÚñ[~º ‹ì²³áç‡_å·€ˆ¨h²lbb’””Tº%<<ÜÜÜ\#+ÏÏÏ?~¼……ÅÇ\VÛK/ð<-zË·b«ØýݹÂÒ’ß"¢¿§ñü5ÀÓ¦Móõõ½yó&€ìì쀀Ÿ3fh$}½¼¼ž>}zèÐ!===ÆÆÆR©´tW599ÙÔÔTSí•lLE»´Â-ÜRB9㊋ñõ×X´ˆß)"¢¢ñü5À«V­rrrOEÙ¼yó7ß|sÒ¤IË—/¯áj ÆŸ––vôèQ±Q.—wïÞýüùóâÝÔÔÔØØXMµ7Öü&c²ô†‰ z÷æwŠˆ¨!EzMddd\ºtéÂ… ééé5_[qqñÈ‘#Ûµkõ§üü|õáCAAAâáCŽŽŽ¥+ªy{ã›]$™ æ×„k‚ $øûs#Q£8 )((èéÓ§š}myyy匣޺%þõóÏ?oß¾½\.÷ôô¼{÷®úQšjod|\8î$8 ‚pýºÐ¾½ŸÏïQ½°&¯íÓºuëâââ±cÇNœ8ÑÓÓS&“5¾­¾ÒÛx»/úÎÅÜwß…¥%V¬àQ½…‚&㤰°ðäÉ“?þøãÁƒuuu½¼¼&Mš4`À©TÊZ×»LdvDÇßñ{ñ£Övvˆ‹ƒ‰ ¿;DD"€Õòóó?þã?>|X¡P$''³Öõn+¶žÁ™ñãÿ‹û÷±m¿8DDõ :µ±Ar¹ÜÊʪcÇŽ&&&ééé|‡?ø½ƒw °y3¯}DDTÿ4À111~ø¡­­­³³ó•+WV¬X‘’’Â*×»XÄþ?†bèñãèÜ, Q=Óä<©.]ºÜ¼yÓÍÍmáÂ…¯¿þz›6mXßbvMÁdAAxã Öƒˆ¨þilfAA……Å… llls½´ppЬ`u §^*ìÒ¶-®^E‡üäÕs(hlZOO/??¿q§¯–:‰“æ0ï‚.ÁÁ°³cú5šÜÜ·o_õ9©áðƒßTL°?¼¼X"¢A“û€{ôè1f̘‰'ÚÙÙ•> ǼyóXèúòOâäl)*ÂÁƒ¸|™%!"j4¹GÓÙÙ¹ÜöˆˆˆÆS/mÛ¼›ÏáÜ÷ø>$‹!2’Ÿy"¢ šì7¦ m4üà· «büxÖƒˆ¨¡ÐðqÀiii~~~Ÿ|ò‰xW©T–¾Ô.ձ븞€„Á, `"¢ÆÀ¡¡¡666ß|óÍ| ¶øûû/â5ßëxø¯Òðp´h{{–„ˆ¨1ð‚ –.]®nñòò f•ë… *øOÃ4û÷³ûKDÔ°hrpTTÔ¾}ûJ·˜™™=|øU®'p¢:ÙÁ@` X"¢FÚ611IJJ*ÝnnnÎ*× õá¿ÑÑн;KBDÔHxÚ´i¾¾¾7oÞàãã3cÆ V¹î¥!íNMÄD±ûËóo54š<ªU¥RùúúnÙ²¥°°€L&óññY»v­T*m<õÒ’ã€7aÓ%\úßèÚ;vÀÅ…Ÿv"¢ š“ÌÌL¥RY\\ìàà`ddÄZ× g8¯ÆjOxÞ¸!Cpÿ>$~SˆˆP(èh|kZ¶lÙ§OŸîÝ»GFFÞ¹s‡oOÝ‹AÌC<„Aøsü™éKDÔÐh2€'Mšäçç 77·GÆ ³µµ àìÛ:ç¿É˜¬ð DDM!€Ož<Ù·o_âÁHÛ¶m[¹r%«\—TPíÅ^ñðß»w‘˜ˆ~ýX"¢FÀOž<144ðË/¿xyy)Š¡C‡ÆÅűÊuégüü^²Øý3::¬ Q£`++«Ó§O§¥¥=zÔÓÓSì‹‘Luf'vŠÝ_˜0%!"jˆ4y&¬>úhúôé‰ÄÕÕÕÃÃÀÏ?ÿܳgOV¹Î<ÄÃ`ïÂ. ˆ‹ƒ»;«BDÔiø š¤¤¤´´4†††öè" ü0¤¯ñõ\Ùƒ=6nį¿bçN~ȉˆb(hÙååÀ•뉞k°f ððÀ¢E=šo˜\›~Ãoc1öwü®ÔTØÙ!9r9ß4"¢† œ ÛxìÄÎ)˜"þ{ð †eú5\2– q(@Á÷øþ.‰w÷ïÇÌ™¬ QÃU[=àœœœÓ§OóT”uægülûÎè #—/cØ0V…ˆ¨i0OEYvbçtLo:„AƒÀ°‰ˆšJóT”õ%©çpnJNºÁ 5­æ©(ë‹?üGa”! dg#8£F±*DDM&€y*Êú² »ÔãÏÇŽÁÕ-[²*DDM&€ÅSQ¶mÛ¶[·n<åáÇ===ŒŒ$I||¼º}áÂ…’RfÏž­þÓš5kÌÍÍõõõ‡zïÞ½j·k…(De!kˆw÷ïÇøñü`5¥ž:uꃢ¢¢Îœ9#žŠÒÝÝ}Ó¦M5\mNNNÿþý?úè£çÿôÚk¯EýiÅŠbãîÝ»W®\¹aÆÈÈH¹\>zô袢¢j´k ñê H<}Š'0f ?ØDD ž 9fffeZÒÒÒžo¬ž+W®xðàºeÁ‚o¿ýöóK:99-^¼X¼’’"•JO:Uöri¶b5—/ä›&w…»âÝ„""ª5 Mö€SRRÊ´äçç?zô¨ö~=:tH.—›˜˜LŸ>ýñãÇâ3FGG÷ûóô¦¦¦öööaaaUm×–ßO‡pÈŽÑQ=þÌùÏDDZA3gÂZ·n]™â‚K—.988ÔÒ¦÷ïßßÍÍ­cÇŽ·nÝzÿý÷ÇŽ’––VTTdjjª^ÌÌÌ,55µªíÚòþíÆnõô«ÂB=ŠO?å§šˆ¨É°úl¥O»!•J-,,üýýkiÓ½þìë9;;[[[÷îÝûêÕ«&&&e“H$å>¼ªí•/P/WhHAÊ\øß‹wOŸ†½=Ú·ç§šˆHóþ6ê'€/\¸`òäɵ·•çZÇÇÇÛÙÙI¥ÒÒ]Øäädccã*µWò\ çjH{°Ç ^ (Ä»˜0ß"¢ZQî?þ5IeMî.¾u|.èÈÈH:tËåÝ»w?þ¼ØžššëââRÕv­ø4øÁï¼#Þ.*Â7Žß""í‰tM™8qâÎ;AÈÉɱ±±‘ÉdR©tß¾}5\íãÇ£¢¢¾ûî;'NœˆŠŠÊÊÊÁÛÛûðáÃW®\Ù»w¯•••›››¸ü®]» EPPR©5j”£££J¥ªF{Ÿ.„w: ÅâÝà`ÁÙ™‰ˆ´f´&ãÄÈÈ(..N???›ìììíÛ·wíÚµ†«ýþûïËüha„ fffR©ÔÔÔtæÌ™éééê‡|þùçíÛ·—ËåžžžwïÞ­v{Cà¹ÂÜÿÿQß7Oøßÿø] "Òš–hp¦L&»ÿ~ûöí§L™Ò¡C‡O?ý4!!¡cÇŽ………i'|CØœ|s˜ÿŠ_-a @`a3g`kË1""íž Z+ÄÁîè.¦/€ÐP´jÅô%"Ò&2 ®K<´D"quuÕ๠éy~𛊩ê»û÷sþ3‘¶õž5; š”””––æàà ž :""ÂÐÐÐÞÞž£ š,2’áø ` ¶X[#(¯¼ÂÏ3‘Ö„‚Žf7EOO/""âÓ?ÏÆd``кuk¾Cšµ{Æc¼:}##!“1}‰ˆ´Œ&844ÔÆÆæ›o¾ùàƒÄÿE‹±Êšå¿i˜¦¾ÈɈšv/X°`éÒ¥áááê//¯àà`VY“¿rª‚ª/úª[¸˜ˆHirVTTÔ¾}ûJ·˜™™=|øUÖ ]ØUºû«T"7ÎÎ, Q`“¤¤$KKKuKxx¸¹¹9«¬)OñtöE#ºt÷×Ë š>C8Õ:MAO›6Í××÷æÍ›²³³|||f̘Á*kÊp†³9žý¦ 䀉ˆ´’Ì$ÖûIDAT&ªQ©T¾¾¾[¶lO}%“É|||Ö®]+•JO½êõ0¤a6 Ó&a’x÷÷ßáæ†„èèð“LD¤e¡ ù8ÉÌÌT*•ÅÅÅFFF¬µ¦Ä#¾º% AúbË_àÎlÞ̯‘ö…‚&÷‹WV»~ýºxÃÍÍoRÍùÃÿ ¼¡N_û÷ã¿ÿeaˆˆ´3¼5ØŸ+}ÚgArssuttôõõ³³³ùc§æìaï¿Þè]Ò!ŽGHL„®.?ÆDDM»\&hSRRþùÏŽ1‚ïPÍ]Â%è¨ÓWìþŽÅô%"b¸==µ®Ñïäu@‡Ät@‡?G`oädÈåüüie(hr:  ôÝÌÌÌÝ»w÷éÓ‡ïP !¨7z«ÓÀÁƒxí5¦/‘Ód/^¼¸ô‚-Zxxx¬\¹’U®!?øÍÄÌ¿þÖ·7 CD¤Í½çz¿º­–ի· ïã~OôL@‚%ÞÇÑ©a`À7„ˆH[CA“=à³gÏV¾€»»;ß­ªÚÝoà uú8xƒ1}‰ˆØþ“¡¡a~~¾J¥’ÉdÄòR;*ÁÁuÜ ØÁÎþ¯âUuã˜1˜8o½ÅO/‘‡‚&O"ìïïß·o߈ˆˆüüüüüüˆˆˆ>}úìÙ³'ûO|«ªê".êA¯túfeáìYðàj""ö€Ÿ±¶¶>t裣£ºåÚµkcÆŒùý÷ßùc§zfb¦=ìãÙì¶~€¿?ŽáG—ˆH»CA“û€“’’ÊlGqqqbb"ß¡êÉAN ¯ãzéFñÀDD¤í49=|øðéÓ§‡‡‡‡‡‡Ï˜1ƒ§¢¬¶@öEßvh§nÉËéS3†µ!"b—²}ûöŽ;öéÓGWWWWW·OŸ>ÖÖÖÛ·og•«Ç~Ó0­tËñãpv†±1kCD¤õ4¿GóÑ£G±±±ìííMLL[½êjð=Ü{¯Æ#^zêÆ)Sз/æÌáç–ˆHëCAG³›’––väÈ‘sçι¹¹™˜˜(•ÊÔÔT¾CÕ°»'bbéô-(ÀÑ£7޵!"j 4À¡¡¡666ß|óÍ| ¶øûû/Z´ˆU®*ÂóãϧO£kW´mËò1€ÿjÁ‚K—. W·xyy³ÊUuç aè§Òû÷cüxÖ†ˆ¨‘ÐäM==½Û·o[ZZªÇÄ›*lff–ššª©ö†óqü™ˆˆ\©ˆˆˆz|%‰¤VÛ+_ ö¦Fà»ïøA%"ªg›U¥£¥…066–J¥¥»ªÉÉɦ¦¦šj¯ä©+Ú P®_GAœœøÉ'"ªgÿÇ_[X.—wïÞýüùóâÝÔÔÔØØXMµ7—€qã é_]DDÔP#½AyüøqTTÔwß}àĉQQQYYY‚ ìÚµK¡P)•ÊQ£F9::ªT* ¶×Ë,è2ºu.\à4C"¢F8 Z øûï¿/ó£!88XüÓçŸÞ¾}{¹\îééy÷î]õC4Õ^¿|ó¦Ð®PTÄO8Q# ຸ¶O#Û _gûì3Ü¿M›Xu"¢F :,_ƒµ?/À@DÔxÛ=à†Ù¾ÎÎHL„Lƪ±Lu%0£F1}‰ˆ-pà à X"¢ÆÛ{ætÕêU'CÐÉÉpp@Rär–œˆ¨q†{À Ñ1‚éKDÔ˜1€¢ýû1nË@DÔ¨{Ï‚®Z½j:- /½„ÄDèë³ÞDD6Ønp AL_"¢FŽÜàìßÏùÏDDM ÷Ì!èªÕ«–‡ Ÿ<…<@‹,6Qcö€–#G0`Ó—ˆ¨ñc7,7oâõ×Y"¢&Ð{ætÕê%aňˆH¡À0Q=`1€‰ˆˆÀDDDÄ&""b˜ˆˆˆLDDD `"""01€‰ˆˆÀDDD `"""b5*2–€¨Ö=~Œôô¿üÿÉ“­P¥BV–æ·33ÅÅ5ZC³fÐ×±ÿ:hÙòEWkh]ÝZRO EÕ6 U+H$hÞ2 èéUáUÕ¯n[ÅzñzÀ$ÊÍ-'V+ú«V02BëÖhÝºäÆ‹gOù¿œehÞ\ó/êÿÛ»û ¦®¼࿼à- VCÁ ‚e ¢ÔáE °hQT@Ƕ®£¬-X«®•긫u[-Ö±Û—ˈ Sm¥Z‹ºMa!#ˆXDÒ–²$% U!Ï×'eC‚›6¾Ÿ?2ÉåÜ䜓¾÷œ{ļU±®.zð`H%ûú¨½}¨O{ïõö©dw7ut˜W»w‰ˆ~ù…´Zêè î°±!‘È0§Ç#àrš-&иqDDãÇ‘‘bì‘A±‘Íà½èì$f8ï)Kßu¿¹Ç{/xNNÃÄ þŸVkF¦òù£Ôè­ÁC>ÎõpPOÝ¿OZíÃåŠ9ýàuu=ŒƒbìZÂýûÔÓc¤˜NGmm¿æ4;¿·µ¥'ž0žÓìÑÀbØÒc_Ýèò†¾FT4êìüõ!ÛX=¶ú¯îôAý—v *ÉÖ\]r0kx>æºÑ T wï"€À`zòd*D Þ¿oF¦b‰ßÀœfÃMD}Lö/ÆÎ# 2’e°rÎο.oðxäèhrA…aÈÎîׇvvÄ0¿>dŒN-7I¡¡€8AsPw÷P§ªwïÃ<:VÙûý% €ÀÆ.ⱋoì-{äÎÞ²gÚØöVÿ#æá.ìQ?{Ëž4úå“Ë¿F7öŸ €߆ öï߯˜œœüᇲ÷333³²²ÔjuXXØG}äáá1¼í`ó¨Ttì}ü1)•×ÐØ¥-ö*ö„{Ë®q±·ìº{~ˆ½e¤/úç?É×—&L ëב¾˜ƒå•—SJ ‰DôÕWäãƒþÀ ,ìÎZ»––,¡ è¤/,L§£ìlòõ%†¡šzé%âñÐ+œ†%h«'—Sj*ÑÙ³äïþÀ ,¬½^{æÏ§¤$º|é € ÓéèØ1òõ%†®_§¿þkÎ# – ­Om-¥¦Ò½{TTD³f¡?0ËÛ¿ŸÂÃiÙ2úö[¤/ÀÆÓétè3ú‹gá»vþô'š0] 0²Cle £#° ð@ €À€@ `0üáxÜÿÇJhê&  `@Wff¦T*µµµ‰‰Q(è@[\nnîŽ;8PYYÉ0L\\œV«E·€EáûP```DDDFF©Tª‰'ž;w.**Êxqÿ¿!¡ hê&  ÖЄÑ>Öh4r¹<44”}(‘H¼½½e2ÍÀ¢F{«Õj­V+‘Hô[\\\T*>`QBtG^KÿÑ4oš€& €—X,ý§¼---¦Êã”9ü&Fû4Ã0þþþeeeìC•JUWW„OXvö)]nnnJJJ^^ž——WZZZcc£\.øp€åà0­\¹R©T¦¦¦ªÕê°°°ââb¤/` 0á/a €À€@<’effJ¥R[[Û˜˜…BÁ¹ú¿óÎ; Ã899½øâ‹œþ·Ë—/çñxÇçbåoܸ+‰D"Qddd__·ê¯V«W¬Xáää4fÌ??¿¢¢"NT»¸¸8::züøñ<¯©©‰‹CÛT84´y81´©¿¹ãl†ÜÜÜ;v8p ²²’a˜¸¸8­VË­&”””$%%]ºtéã?–Éd }/ ”J%Ã0\¬ü?þ*•J?ûì³ÒÒÒeË–q® )))2™¬¨¨¨¦¦fÞ¼yK—.½uë–õW»££#,,ìÍ7ßäîÐ6Õ mSMàÊÐ6UÿáŒk ÙÌ™37oÞÌÞW*•àüùóÜm;kikkã\Í›ššÜÜÜnß¾Í0LAAçê¿f͚ŋsz,x{{ïÚµ‹½ßÓÓÃçóOŸ>͕ʗ——³¿.¹;´6sCÛh84´Öã3à¡Òh4r¹<44”}(‘H¼½½e2w[ÔÑÑaoooggÇ­jëtº¤¤¤­[·Nž<™£=_\\ìïï)‹gÏž]ZZʹ&<ÿüó%%%J¥²¯¯ïðáÃŽŽŽsæÌÁÐÆÐÍC{ãèëë[»v-G{¾««K©Tfdd,[¶ì믿 ]°`ÁÍ›7¹ÕŠŒŒ wwwWWW†aÞxã’’’ &`hchÚ¡=¼q>îþiF“àææöÖ[oq«æ o¿ýö¡C‡¸Ûùìu‹-Z»v­¯¯ïÞ½{ÝÝÝsss¹ÕŠÍ›7×ÔÔ|ùå—UUUÉÉÉñññ?üð†6†ö¨ÚÃ×à¡‹Å ÿqqKKKÿ£f Ñøøø®®®3gÎŒ3†[•///W*•S¦L …B¡P£Ñ¼ôÒK111j‚ÝØ±cŸzê)ýOOÏææf5¡³³óý÷ßOOOŒŒœ1cÆîÝ»œœŽ9‚¡¡=j‡öðÆ5þáP1 ãïï_VVGD*•ª®®.((ˆ[­èîîNHHP«ÕçÏŸçÜ)""š?þµk×ôgΜ¹{÷îÄÄDnµbΜ9ý¯nhhð÷÷çPýµZ­V«åóùý§ŒCC{4íaŒk°Ö¯_Ÿ’’ìåå•––6}úôˆˆÕ_§Ó%$$TVVæçç744°}||8t°ìàààààÐÿ÷¾T*õððàÖiãÆ‹-ŠŠŠ ÉÎÎnll\¹r%‡ê?vìØ°°°mÛ¶9::º¸¸=z´¶¶ö“O>±þš·µµ)Šï¾ûŽˆjkk[[[§M›&‰84´6ÁÞÞžCCÛh84´M}І3®ñå"³ìÙ³gâĉ ÃDGG766r«ò<ø¨¯¯çîÛÁѯ!étºœœœ)S¦ØØØøùù}ñÅœ«ssóÒ¥K„B¡¯¯o^^'ª]PP`ðù¿xñ"·†¶Ñ&pkhò.pbhRsÇ5O§Óaj ð;ÃEX`0 €À€@ `0À`à¦ðððÌÌÌ÷‡RÀ’••…~0X‹îîîür`° ÀÀÀ­[·ÆÄĸ¹¹M:õôéÓìöììl???†aÄbqrròƒôå·oßëèè¸wï^SÅLéííݶmÛ¤I“† ’Éd¬^ÿ—3µ{{{{bb¢­­íÔ©SO:%‰¾úê+"òðð(,,Ô?›H$º|ùò ÕÈËËóôô …b±ø…^ ¢+V\¹reãÆ<ÏÕÕÕh+'DX§ƒ–––Ξ=ûìÙ³ñññr¹ÜËËK deeyzz*Šõë×ïØ±#==]_þÔ©SsçÎíèè(,,4ŲM›6UTTœ?55U£Ñ Þd£ÏÓÓÓ³zõêìììçž{®½½ý›o¾aãV¡P$&&nذˆZZZ–°v:°>3gÎLJJÒ?œ7oÞ–-[ Ê”””xzzêË'''}ªþÅæÎ›‘‘ap¿­­ÍÆÆææÍ›ú]fÍš•““cPÞ zú—3µûÏ?ÿÌçóe2»±¢¢‚ˆ.^¼¨ÓéÜÝÝ?ýôS}y{{û²²2SÏS]]- [[[ êüÞ{ï±÷M•°f˜X©3fèï?ýôÓµµµDtåÊ•´´´ªªªÎÎN"7nœ¾Œ···þþ Ū­­íééñòò꿱¹¹yðêé_ÎÔîuuuìb5»% @(£>>>ÁÁÁžžž±±±ÑÑщ‰‰vvvû¥ €µÁ9`+ÕÛÛ«¿ßÓÓCD ,ˆ‰‰ùþûïûúú.^¼¨Õjõeôñ6x±úúúˆèÞ½{ýÍ·mÛ6xõô/7ŒÝy<žÁ:Ü ÏÃçó/\¸pâĉI“&íÚµ+  ££ÃðÙÊ €`Hú_ %“É|||êëëÛÚÚ^ýugggW]]mtÇ!ë?} …ŸþùðêijwvŠÌ®<QUU•þbܸqíííì}¥RÉNÓ©ŸÏŠŠz÷Ýw«ªªn߾͞âlrR f+--=tèÐO?ý”žž^QQ±zõê'Ÿ|’a˜’’"ºuëÖ¾}ûŒî8ÄbzãÇOIIY·n]QQQSSSeeåöíÛÙï¡0µ»³³ó’%KRRRª««kjj6mÚ¤Ÿ4‡‡‡çååuuuuuumÙ²…Ïçò<ûöí»qãFssó‰'t:ÝäÉ“‰ÈÍÍíêÕ«---wîÜ1UÀªá48€u^„µsçÎ… >ñÄ'Ožd·çççK¥R77·àààƒÚÛÛëË8p@¿»©bF/ÂÒét½½½;wîtww …...Ë—/W*•ƒ_„ÕÿåLí~çÎ%K–°M(,,´··g/ÂR«ÕóæÍsttœ>}z~~>{–©ç¹qãFdd¤ƒƒƒ@ ˜>}úñãÇÙ•Ëå¾¾¾ÀÅÅÅTkÆë¿†V"00pÕªUëÖ­I‰D%%%áááx° €5° €0À#ÇÿÿÉPÿðIEND®B`‚ mapcache-1.4.0/static/bench_merging.png000066400000000000000000000124771255567662100200730ustar00rootroot00000000000000‰PNG  IHDR€à,Ö PLTEÿÿÿ   ÿÀ€ÿÀÿîîÀ@ÈÈAiáÿÀ €@À€ÿ0`€‹@€ÿ€ÿÿÔ¥**ÿÿ@àÐ333MMMfff™™™³³³ÀÀÀÌÌÌåååÿÿÿð22î­ØæðUðàÿÿîÝ‚ÿ¶Á¯îîÿ×ÿdÿ"‹".‹Wÿ‹p€Í‡ÎëÿÿÿÿÎÑÿ“ÿPð€€ÿEú€ré–zðæŒ½·k¸† õõÜ € ÿ¥î‚î”ÓÝ ÝP@Uk/€€€@€@€€`À€`ÿ€€ÿ€@ÿ @ÿ `ÿ pÿÀÀÿÿ€ÿÿÀÍ·žðÿð ¶ÍÁÿÁÍÀ°|ÿ@ ÿ ¾¾¾ÌKÙIDATxœí‹¶«(@uuÏÜÿÿã™{N¢ ˆPTÁÞ½Vç&JAâ>…àkÛ°ï¿ÿß«ÊTòèóŠ€ ËE |Qöý·×ݽ ¸»]ñÿÿù]e÷ ìßr—ЉuÁ¿¢küèõùÐñ øå ˆ è¦ÀCÀÃÃmC@hD*úkøæý¾~{ës¿ë(àÜÃû¾ »àkß»9‹¯àçÐâèfƒAˆ“Þü èï’AŠèîû€ F4Ù‘`óžs Ä Oò#r–9€¿Úýž÷}9< Ò¿‹}Ž€-ƒÝ¦Ûmy]Ó?êF»àÕ~ Ømy}ÓPv[þJÀè4ÌŠ¿Åpì¶¼}Ó ÿ0”#:âë:u¼°æ’Õ^| ”〴¼ ãÞÄá˜ù^-¹¬4²¾½(‡¼€w×ÓoŸ‹œ#Ög[†€ùI!×YìeÚîÖtÖq–mÎ*ÑâÎË–ŽéÔþ}ñ¬ôxÎýF*za ¾ÅŸŒ §¦î–¥ŠûÛóºJd6Qƒ›Ð.a¿F \»à eAL§À?9üŸ¯ÍVèP;I/~:ljü5£Å¯}¢û殢Dñ¬€—ð^k}½úÈ€# 6}D¹È»£ôåð ‹GŒ¬|+`àÎCý0Õþ!àkrz¤;Èt¹PÉH”„÷n Yï øµ;¦*ªûùj ŠTOnô±»Iò|ç”Ëm·ƒHêKe‚Ú¨÷. tJDê«ÿõªKJÔN°a媡N|?½!W«x…]ªD@ ÂPPŽDÖ±¿\XmÉj BLßLûuf0½j£Ýe±Qp8Sž É4Œl;!FdЛ|I4Õñ“4òÓÁ”ßQü%\ó¬Á›ôæ…ï«ÝÒ1¿ÿ߯“Û›'”3ãí¬rý*Õ"à[Â$²{‡î–mž»w$$]çŸL‡>"D²œt»ˆt®rY1hYÓ)ðoÿç{ùó¬+àÕ‹ß…ûîµv±\2`þ|xX,Øôå"ïÎâβ-b@¤xDÀÈÊ·¦–• 6¿<8Äûã‘+–Ó#ÝAºåÃÍìoãH”„÷n ô%ókw*LUT~I™–Õ17úØÝ$y¾;‹Qœe·ƒHêKz‚ÚýŠ­¾|½Ý/q_àø¡°ãýølXTÙ¥NüåéŽß·Ø£Þº%âúy»]XõEhãÎ8í‚€é" :–€€[ÿÂÞ*ÜyÜb}ZÛ~î6XaM%«=iôå‡@À.ù/¹E¾ ’éñí¼ZY3¾Ëüòå%#ÒýôÏ=גϾQ{öè³úÿŒ­;ãΟÜMT†>3,׌×_¤wÀÎ<úÁ~ŽågþÎɳ딟ŸnŽÉœp‡ó€Þ¼ð}[¶ IG[¼¹æeûË•Õ1« øèï5š(œ|q·,RüX²ÇÒ]Ú·×¼k‹,lKN°  Þ¯GI‚˜NÿäðÓ' [>ë/uü­ý»pß½çšÑân¼Ðèx)6̯áL6nætW#V!{ÞA`ìåÃ"ý—¤aN˜¨V¡;ôÃTû·¶€Ïý;^Ê:ÁÂM0UÎÍ£÷M*°¼ÕN…©ŠªXYÀçÃßÜècw“äùÎ-¾;®:}vPn»„vÌKµl÷ê» ¸Ý•k£Ý‘Ê#|uI©€Ý¨ñïÕoÝ”íèQçºÖLÿù m(â éôÝ—ƒ³:XU@üS¢Îàßw¼œûÅS+ö¨%«1yˆiÿ¼1iÁÆO¬pø›D“ž†‰ ó_”Å´eFäWx2˜&,`8å´·¶%rX÷ÏŸ)Ìf²=–׎bÇ›´€þôcÿ·¥—¥üü#úœõœ´¸k8A4àKús£#¯%³Ü–Xv.p†ÎFÿ~染p]v©ýZóv)¾ Ü5?¡ƒ¯YÿëU—” ø ÿÞm€X‡œ\>–m™\@‰ù¿ i=,}L€ï¾f:¬ú€oP1ÿ wL- þégfñÏ ˆ˜W@ü3Á¬rú½&ý¬0§€øg†)Ä?;Ì( þbBñÏó ˆ¦˜N@ü³ÅlâŸ1&ÿ¬1—€øgŽ©Ä?{Ì$ þd"ñÏ"óˆ&™F@ü³É,âŸQ&ÿ¬2‡€øg–)Ä?»L —Xƾ€ègóâŸmê|IÜ(¸>`5øgœ*_no¸Ø®màŸuêïl©A@ü3Ï›.x?nªîÝÓºÏ}äbàŸm^¹âÛ7$âßî‚ñoìBðo ÌNÃàßXˆÆ¿I0* þÍ‚MñoL ˆó`Q@ü›ƒâßLØÿ¦Âœ€ø7ÖĿɰ% —LGÆ—ç§ËôýæãÖ—}~ÆVGñoBî|qžþÞ&à;ðoFn,XçQÀWàß”˜„àßœXÿ&%çËþT©>â߬d§aÜÓí¬ÿ¦%'`Á:VóbA@ü›˜ü‘áó€ø73ÙAÈð#!ø75ê§aðonòpì4 þMNÁÙ0#÷ñov²£à§3ÑMÄ¿éQ- þÍæiü[ÅÓ0ø·j§a¸üc ´ž ƒ~‹?fÈ> þ­Bþd„‡3ÑMÄ¿eP9 ƒë ñHþ-„ÂAþ­„¾i˜qþý{ϰvMÍý…é–0Ì¿¬a?Ñ·ŠÌžëôRÀqþõ޾Qî}©0ð€£üÓ¾‰ßé«Yç‚cÁmÞ2Ì¿Aõ¢­ÎïôÎú"y6Ì ÿt¥ó<ÓµÀ—ˆ€ßǵž¯ç¢ú–òoLµðC•/ÎS Ïwoþ0Æ?ÒßXª&¢Ï§Uåk à ÿ†Ô ÕgÃxkmðÄô!þ‘þ†RàJêl˜£`«'¦ñoD¥àQw6Œ›ðštÁ#ü#ýi êlôÑ@À!þ ¨*!ÞüËÛi˜—þ” àlÒßÊ”LD·èCú[šü(¸óÑ#ü“¯RØõšyÿHª¸õe¯xV×3ø'^#ÜQ"`»€WÄý#ýi£¤ nÐCÞ?é !ÇÈiiÿH xY¦¸ÂõA ãî #ìéO'Ãî #íŸluPʨ{ÃÈúGúSË {Ãû'ZÌ ØÝ¿ÎñAŒü-zÓKÎg_XÝÙ?Òß$”=°:Utsíó2`oÿú†Qêüõ-Ñ÷õô7UÓ0·¬îì_×è N¥€MX]éo:†Ü¢·ô›C’þfäΗ½`G_~Sr+àw¯UÀþ&åÖ—îÏ .ýf¥à„Ô¦« ýÍ‹…AúMŒ~IS£^@ô›å’þfG·€¢úý§+’ßÄšM½é«·]û 8“~ƒQ¬¯Z%Óßäú½¥«¾ZD¿YÈøYàË€Cq‚éýÆRrƒJñ“ÐoJ® P.ý¡ßxJΆ‘ý–BÛéXbéýtPy]pË.è·ª¦a¤Òúé¡ÛíÙ*@¿Ñ# Lúë}d R⋈€BúIÔP" HúC?…èè‚ÑoYJl0†DúC?¥(˜†A¿•)¸IyÛ€WÒú)fpŒ~«Sr:VË€>è·<#ìŸþÐO=EÓ0OzáòUÑ Ø=ý¡Ÿ FMàüP0 ÓáHHçôÇvr(®·~]ÃCSœÝ7ý¡Ÿ-äD?pÈܤ¼yÜ5ý¡Ÿ=„D?𩜆9Ñå<±+°gúC?›Ý%?Xéû‘Ï÷ö7ÑJNÇ :áÝÉ€ÅvLèg—ü(xÿùïúù7FŸ˜ Š~p¥ì‰éi?ÿž˜¡_úC?ÛTžŒà ˜í‚Ñä÷c ðÚß Ø-ý¡Ÿ}ª¦až=1½“~œq0ÝOFè”þ°oz ˆ~pKß[sôIè7]D?Èѱ î’þÐo2ú]˜Ž~P@¯‹’z¤?ô›’‹’*¢”Ñe°CúC¿I)¹&ä©€èÅ´°}úC¿‰r]ð8ä;7!­À¾Ùuo˜"Ðo~²°uÀrÐo²û€ƒ~k0àÞ0% ß*¨ýÖAá4 ú­„:Ño-”Màßj”LD‹e@ô[E]0ú­ˆšQ0ú­‰9ã`Y„ïûf¼€è·4£§aÐoq |éx> ú-OÁ ¤[Œ~Ð墤2жŒ/!è?”Ø.àôƒ#&¢Ñä§aÔê÷ß £Û7'ÒjÕ¯@°œ è[ƒ¬€Jõ“ðð¾ïšžAP@g ß¼Etu G×o&& VûF7au„Ô¨öi@D@…úaŸÔ§öé¡RÀßÃ#%êR§ö©¢NÀ½ôa…Êôë>¦ƒ§T x<1=# .ýO#oºàóaÕÑ'¦«ÒûòüT§èoùôÓ5é‡}zy™S]°"ý°O5]Ô£öi§nrûÄt-ú1äµ@û‰hú!ŸF_Üì³Ã|bŸ)&û¬1“€ØgYdÈk”)D>»ØûLc\@쳎e±oÌ ˆ}s`R@†¼ó`O@ä› cbßlXû&ÄŒ€Ø7'&dÐ1/úD¾©Q. öÍŽf±oÔ ˆ}k S@ì[}2ä] e"ßjhûD€Ø·&:ľeQ ö­Ì`ò®ÎH‘Æ ˆ}ð—1b| öÁ‰´€Ø’2ä…1‘bȈ}@@@ìƒ4½Ä>¸¥«€Ø9º ÈJè# òA!o¼<±ëû œw/Îà ±ð:6h¬ÃÛ xóÄt€{Z¸’|b:@ Mœ  þÆÐ¦ ¶+à÷wÝŽeyåKü‰é&@<%(¸,SÄSÆ*"žRfñ”3«€ˆg„ÙDEÀ‚ç¬ íÉkï«®³ ~ÅÐÞûÛC@Ú›Úžoæ5@{{¶Çpr@Àq8G|]'°‚Á[N)V±8Ž i £ÝÄá9в~ZoÄh½¾8Žñ†0OÝ©á<ŽßÄ9"s½œÎA[¨¤RFYv(EœÕ•—ÃSiýð3½v˜?EÜíUˆ€µè“QöÔTh™¯º¾?Í"ŽIXO ÷ÿ¬foù wl­™S©ñW ýëk²z\¯Ÿ…Êy"½¤³ºZTk!Ò§zTÀᅵâš+hôÔ§ 8k×;”s¼»kZ8ª;t ¨¹cþi$e@5L±XMLß‹RÝ^f)éˆâñÞÊd!íe>•#)3óë+­8,`„…ÁM‰>N5I>ï”Úv½#8q¤>ïPÆj]‰j7«'Bm•ŽöÊ¿½âš£†Ñ¤ûqÒy ¬;† Mvis+=E:k©OoŒbx›6óå–{äÖrR/.±ú¢åXÞ-à(3ÚXééÂ:öàÁȉͦ“:™›™²•tï¦ÓœôÍ&æmÛÃf{º&Rè4Ìj]c$Mfì»CŒÀ<à‰€I,âßçü½gÐåäéÚÁv*ÒZÐæµyáð,äáyý_ð÷_‡Ù¸:ý蜶,7kÑ÷¡3¥èzÓä© ñ·ð»×CÍÞV«‡ä|Rh¤ v.Ó2§Þúñä<Ǧpÿîã'ïË}ˆ¥îJµúEY„8RŸÕl’¥?V½{UNµq½Gwmfù·W\sT@Ëhf–ÍÖ“¦iOÞ*à2þ™ m\«ÃìÒäK\È?¨a*ïpyÿ¼½•1¶p,oùƒ%¶”RLÜ d í)Á§@# Caž¯õ¢Õ4b¸C ›†YÞ¿T½ 1ó€êD3ú™çßïi«øÌß3yfNù©s}Ê 2™£NªõÔ’Ú¼px]k•î˜Ïÿ¯”•°æï¦Ý)³Ø@ÌÀ™/”yÀÐ2Gõ{ÉiSÐö§]Ï×Ù¬#Ë©AM?у£ú³~Fʼ#ÿCÿNkwŠIg&@Ÿ…ç© xJ:««0.`¤u+¦R3, þ×sÚ%=jíÉÊ€­½ìPÎñî®mk|˜* ùr˜¾èaÜj˾ïÃ6f4·¢Œ÷ 8Õ¿ûÅi‚ïE©íÌîO}õTK«”. ]Äj]iÐ×PÙwZZqX@ƒ©#àØèãT“äóN­~*Ì•ä„8RŸk•N­=SÀÿ)‡Zò4>to{ùWZ\sT@ƒ™Zûp"3Ö£G›opЦ2|E:m;ÂT^& þíÆ»Ä?…k¼ûÆ}{Ô”bb!x¨_ím´¼óãиÞYÝmô4Œk˜_0 üûC’ñþy@}°¿¢€öT”²¾¥k’ϼܩ|ýÁLvºòÚ]í~ãPŸ~tçÊbËÍ=U« ÿ~quÁ¶:‘¼¦xû„{J¦œŠÓb*‘ÿ¡oMáw ýQèëÛk® ÿ>¸üžH¹)ïÔÜe h߸¯IP :|ÐV*sK*\Qÿдõ- ?ø2 þâÐÆÑƒ%ŸÁV×bð1 Ò¤½¾¥càß—K&zQÃTµT¸1 Ú2ìŽ:aÌDœjWª)f§TpG¹[Órî#„¦ŠW% þͧÇÁ}óÄ¿Ùt\" Lå%âß® à8ú̆¢[L\”bÃ!#þa ÿ hL&/³ÖÆ=ˆ¶Ë…BN˜ðØÅ¿N–¶ôHú¾‹†—ë‚€ÕXI+³v8Ø2Öm¦?ìâ×÷ªX\@üÛ„©¬- þmÂT–ÿögeñOSYX@ü“ÂTÖÿD°¬€ø'„©¬* þ a*‹ ˆRXS@üÂT–ÿ䀀0•Ä?A,( þIa*ë ˆ¢@@˜ÊrâŸ,Vÿ„€0•2_<7 .xÒ(ò%xÃŪÕA@i”ßÙ²‡€ø'Žš.ø¼oª®ÝÓºü>rø'‹W ûÚd@”ÇJ]0þ d¥A d¡iü“È:Ñø'„©,# þÉa*«ˆBA@˜Ê"âŸTÖÿÄ‚€0•%Ä?¹D|É¿\!‡ /ç™ÅV€ø'˜/ÊÓßÛtƒ’ ˜P&+ ”ÌüAþ‰a*1_Î\¥rÄ?ÙD§aÔËí4Á?áÄL(“Ð…3Y@ü“NüLH×y@”NtÒõLþ‰gê4 þA<vœ†A@H¸¦Û1 þA|œ;€ÃDñfNÃà3§aމÓ0ø˜v5 ÂâWÃô9Ä?ø%~1BæLtZaüƒ“¦a>Ì9‚ðeÎ áË”iüƒ‹ðÓü‚p¹3B¾‚9„})00^ÿà!á\pÛ€*Q_š_ ƒ  —CÀëq­Ïkr@ü•¢iå)…ϻĀ*EÑÏÓª/ù’Ä?Ð(¾F{\kúÓn.·÷] sWÌ|b:þNÙÕ0jÂËé‚ñ Š®†ÑGåB´ù—äiü“¡Wà ˜¤LD· ˆ`7»"ÿÀ&AÀV¿ A@° Êu<«Ë[ÿÀAŠ€m" 8Hé‚›Ä?p1jÿÀɨŸe" 8toü7ƒî ƒ€àf̽að< ¹7 þ!ƒ#¦að¼Ä˜^Ÿ¼ 8Ä?ðÓÜÙ¿¿mú6My»€.ÛNbéªÄ»àÊ3!ãýK¬‰¦Kpw¬ºAHW» ‚¥½é= ónÿÒÀÒ :wÁüKƒ.ßÍ‹Üqoɳ4E®rñ¯[YúZ_ó Oâ-]~×.ÿV'MÓž¤˜µIJiüƒ(=§aú ˆÛÔç$Á§(þAœ7 ˆ‘rs¢²€ø tÿ …n£à^âß^$\ Sÿ ‰^Ó0Ä¿Ýèô£$üƒ4úü( ÿ ‘>¿ é# þmHñRér5LñoKÊ~”¤?"É|RþA2EÓ0úCâÌgÅᤓpw,LJÃÄ¿]‰ß¢×¿äyV°úÀêŸì“'qðoKÒXí«z¨ö©6\Ã/ø·/e~|Ë^pø·1EÓ0Ŭ.ÿv¦PÀ²V[3â½UàßÞ¬. þmNÈ—3¡LVÀ|ðow‚^Gx­fƒÛô¥éó‚ À¿ýI¸ µiÀðO BðOë ˆ"XV@ü“ÁªâŸÿ¤°¦€ø'†%Ä?9¬( þ bAñO ¾ >‡¢H¹AåЋðO)¿ ) þ #åj˜âŸ4Öº ÿÄQø»à–5ðO+MÃàŸ@ºÜž­ ü“È:âŸHR|" þÉdñO(‹tÁø'•[tƒbYbÿä’p“ò¶àŸ`è‚ñO2)—cµ hƒ¢™. þÉ&i&§Îÿ„3YÀzÿþ Pº3w¦Ú¿°d!9w ¦aú ©÷¯6€',âc橸Uý+qó™xEt­ûì¸Rqwzž€ÕþUÖßœR÷ r“ò~]p¥ïù‡ ³¬õ¯®:,Cá4Ìýˆ.å‰]9ñ>$Ý%ß*t=AäûæÈ}Xat¿‘r9–Õ ŸJ,°Ò¿ªÚ°ñQðùûŸùù•í'¦Ç«ü#ýíCÚÓý~ÿa<1=Úl5•a= /FÐÌê‚ñâÇ€®hvÁÖøG÷»EÓ0OL¯ò¯¢.,Êà‹*ü#ýmÉXkü+¯ 3ôÖø&#,÷îw[vÁþׄÕ÷ÃôbÿH;3ìGIåþ•V„7ò£¤ñœ :,õîwwR~R/`±…õà5 °Ð?ÒŸFtÁ¥þ•UƒW1`‚à§ÿ4L™t¿BˆfÀÚ€…þÕ‚÷=¬ XäéOoÍQæ_I%x'}Ä?ˆÐu¦Ä?º_Yô°È¿‚:ðb:NÃøGúGÊDtY,ñ/¿ ¼œn]0þA ½FÁùþÑýФ“€þe×€ès‡ÔlÿHRé"`¾¹`zLÃà$“ `îõ€¹þÑýJ&a’Ùgû—Y¶bÌ’üþ„ô¥çƒj> ŸtRlÐäUþýë`ö:½ŸÎ¤yA÷“Í%å›ä ­¥k0ìÞ06‹ê×L¢¹röÕ¦´=›i.•þƺqTÊ9X­¾Ìpº~ëî­mÜJb’€süÛv/¾˜)Žì~‘nmfØ[¿}û« /`Ÿô‡s/e¸€õCº—S(àçôHÁ£ºÚú‡x¯§LÀ³ða…M»_òÞ x?1=SÀ¶ú5 Ө邟‡U'=1½aú#ùmAþ¥.JÕOýŒ'¦·Ô¯Y(˜MeLï‚[ùGòÛ‹A¶ê~±o7Ê!¹OLo£ÉoCFLD·IØ·%l¡ÉoWú ØÀ?ìÛ—ÞÖw¿$¿­é,`ýj#ÀÒt°6ý‘üö§§€ÕúÕÕ‡7ÐQÀ*ÿH~Bè&`U÷‹}bè%`…~$?Iô°"ýaŸ,ºX¬ÉO=,õûÒ^ÀÂî—ä'“‰wÇRÁ>©,! úÉe¾€ô½¢™- ö gª€$?˜( öÁ<I~ð˱¾LäÃÄ>P+ É F ˆ}`1L@’¸$ ö›’üÀK±täaº ˆ}£Ÿ€$?H —€ØItä©tû ö¢d0û7! „© La*S©ñÅxbW}@G€÷KâÓ ª3 B µ0ã‰é:-\Ib:€ƒ&ÒC)mº`„Bª|I~b:€&¢a*SA@˜ ÂT¦‚€0„©¼\À¿`J÷àk¬ÛlhM©¸/õvâU¢Þ~¼D@ÔÛ•åD½½YX@Ô“À’¢žõ¤±Œ€¨'“D=ÁÌ´'žY¢ü2^@Ô…‘¢XŒõÀCoQ‚ôõ ¢$Ó^@Ôƒ V8‚A@˜ ÂT¦‚€0„© La*SA@˜ ÂT¦‚€0„©¼^@Ú£½©ê¢=ñíÍ}X!í‰oioj{Ž'¦¤ÒB@õ‰éƒÑ»`€Á Ì¥MOð6‡GÀ' øG6ØêÜDVƒ­6mnôšñ'6²±qm]í5msô9¹sà;§Üñ ð !Wjq|—¿u†ï üø|´÷1ÙÞ¾Ãö ?BÚÜè&‡ \¸52Òòíþ‹}ÿ´AJ“CÛyŒЖÒ3žœ)…& Ôñ½lãórï®[Ô¹–i>±N=æy<‘¼ \nÉÊ^ôL–]Û?‰ç»ûmKôÕ’F^sS‹¨yô<¬h#6Ö¥@@u™G@Ìkñýe“Øk­j©rº¡Z.\Ã{q<¢è%}¢Å #include #include #include #include #ifndef _WIN32 #include #define USE_FORK #include #endif #include #include #ifdef USE_FORK int msqid; #include #include #include #endif #include apr_queue_t *work_queue; apr_queue_t *log_queue; #if defined(USE_OGR) && defined(USE_GEOS) #define USE_CLIPPERS #endif #ifdef USE_CLIPPERS #include "ogr_api.h" #include "geos_c.h" int nClippers = 0; const GEOSPreparedGeometry **clippers=NULL; #endif mapcache_tileset *tileset; mapcache_tileset *tileset_transfer; mapcache_cfg *cfg; mapcache_context ctx; apr_table_t *dimensions=NULL; int minzoom=-1; int maxzoom=-1; mapcache_grid_link *grid_link; int nthreads=0; int nprocesses=0; int quiet = 0; int verbose = 0; int force = 0; int sig_int_received = 0; int error_detected = 0; double percent_failed_allowed = 1.0; int n_metatiles_tot=0; FILE *failed_log = NULL, *retry_log = NULL; #define FAIL_BACKLOG_COUNT 1000 apr_time_t age_limit = 0; struct mctimeval starttime; typedef enum { MAPCACHE_CMD_SEED, MAPCACHE_CMD_STOP, MAPCACHE_CMD_DELETE, MAPCACHE_CMD_SKIP, MAPCACHE_CMD_TRANSFER } cmd; typedef enum { MAPCACHE_ITERATION_UNSET, MAPCACHE_ITERATION_DEPTH_FIRST, MAPCACHE_ITERATION_LEVEL_FIRST, MAPCACHE_ITERATION_LOG } mapcache_iteration_mode; mapcache_iteration_mode iteration_mode = MAPCACHE_ITERATION_UNSET; struct seed_cmd { cmd command; int x; int y; int z; }; typedef enum { MAPCACHE_STATUS_OK, MAPCACHE_STATUS_FAIL, MAPCACHE_STATUS_FINISHED } s_status; struct seed_status { s_status status; int x,y,z; char *msg; }; #ifdef USE_FORK struct msg_cmd { long mtype; struct seed_cmd cmd; }; #endif cmd mode = MAPCACHE_CMD_SEED; /* the mode the utility will be running in: either seed or delete */ int push_queue(struct seed_cmd cmd) { #ifdef USE_FORK if(nprocesses > 1) { struct msg_cmd mcmd; mcmd.mtype = 1; mcmd.cmd = cmd; if (msgsnd(msqid, &mcmd, sizeof(struct seed_cmd), 0) == -1) { printf("failed to push tile %d %d %d\n",cmd.z,cmd.y,cmd.x); return APR_EGENERAL; } return APR_SUCCESS; } #endif struct seed_cmd *pcmd = calloc(1,sizeof(struct seed_cmd)); *pcmd = cmd; return apr_queue_push(work_queue,pcmd); } int pop_queue(struct seed_cmd *cmd) { int ret; struct seed_cmd *pcmd; #ifdef USE_FORK if(nprocesses > 1) { struct msg_cmd mcmd; if (msgrcv(msqid, &mcmd, sizeof(struct seed_cmd), 1, 0) == -1) { printf("failed to pop tile\n"); return APR_EGENERAL; } *cmd = mcmd.cmd; return APR_SUCCESS; } #endif ret = apr_queue_pop(work_queue, (void**)&pcmd); if(ret == APR_SUCCESS) { *cmd = *pcmd; free(pcmd); } return ret; } int trypop_queue(struct seed_cmd *cmd) { int ret; struct seed_cmd *pcmd; #ifdef USE_FORK if(nprocesses>1) { struct msg_cmd mcmd; ret = msgrcv(msqid, &mcmd, sizeof(struct seed_cmd), 1, IPC_NOWAIT); if(errno == ENOMSG) return APR_EAGAIN; if(ret>0) { *cmd = mcmd.cmd; return APR_SUCCESS; } else { printf("failed to trypop tile\n"); return APR_EGENERAL; } } #endif ret = apr_queue_trypop(work_queue,(void**)&pcmd); if(ret == APR_SUCCESS) { *cmd = *pcmd; free(pcmd); } return ret; } 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)"}, { "cache", 'C', TRUE, "override cache used by selected tileset (useful for selectively seeding fallback/multitier caches)"}, #ifdef USE_CLIPPERS { "ogr-datasource", 'd', TRUE, "ogr datasource to get features from"}, #endif { "dimension", 'D', TRUE, "set the value of a dimension (format DIMENSIONNAME=VALUE). Can be used multiple times for multiple dimensions" }, { "extent", 'e', TRUE, "extent to seed, format: minx,miny,maxx,maxy" }, { "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." }, #ifdef USE_CLIPPERS { "ogr-layer", 'l', TRUE, "layer inside datasource"}, #endif { "log-failed", 'L', TRUE, "log failed tiles to [file]"}, { "mode", 'm', TRUE, "mode: seed (default), delete or transfer" }, { "metasize", 'M', TRUE, "override metatile size while seeding, eg 8,8" }, { "nthreads", 'n', TRUE, "number of parallel threads to use (incompatible with -p/--nprocesses)" }, { "older", 'o', TRUE, "reseed tiles older than supplied date (format: year/month/day hour:minute, eg: 2011/01/31 20:45" }, { "nprocesses", 'p', TRUE, "number of parallel processes to use (incompatible with -n/--nthreads)" }, { "percent", 'P', TRUE, "percent of failed requests allowed from the last 1000 before we abort (default: 1(%), set to 0 to abort on first error)" }, { "quiet", 'q', FALSE, "don't show progress info" }, { "retry-failed", 'R', TRUE, "retry failed requests logged to [file] by --log-failed" }, #ifdef USE_CLIPPERS { "ogr-sql", 's', TRUE, "sql to filter inside layer"}, #endif { "tileset", 't', TRUE, "tileset to seed" }, { "verbose", 'v', FALSE, "show debug log messages" }, #ifdef USE_CLIPPERS { "ogr-where", 'w', TRUE, "filter to apply on layer features"}, #endif { "transfer", 'x', TRUE, "tileset to transfer" }, { "zoom", 'z', TRUE, "min and max zoomlevels to seed, separated by a comma. eg 0,6" }, { NULL, 0, 0, NULL } }; void handle_sig_int(int signal) { 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"); sig_int_received = 1; } else { exit(signal); } } void seed_log(mapcache_context *ctx, mapcache_log_level level, char *msg, ...) { if(verbose) { va_list args; va_start(args,msg); vfprintf(stderr,msg,args); va_end(args); printf("\n"); } } void mapcache_context_seeding_log(mapcache_context *ctx, mapcache_log_level level, char *msg, ...) { va_list args; va_start(args,msg); vfprintf(stderr,msg,args); va_end(args); printf("\n"); } #ifdef USE_CLIPPERS int ogr_features_intersect_tile(mapcache_context *ctx, mapcache_tile *tile) { mapcache_metatile *mt = mapcache_tileset_metatile_get(ctx,tile); GEOSCoordSequence *mtbboxls = GEOSCoordSeq_create(5,2); GEOSCoordSeq_setX(mtbboxls,0,mt->map.extent.minx); GEOSCoordSeq_setY(mtbboxls,0,mt->map.extent.miny); GEOSCoordSeq_setX(mtbboxls,1,mt->map.extent.maxx); GEOSCoordSeq_setY(mtbboxls,1,mt->map.extent.miny); GEOSCoordSeq_setX(mtbboxls,2,mt->map.extent.maxx); GEOSCoordSeq_setY(mtbboxls,2,mt->map.extent.maxy); GEOSCoordSeq_setX(mtbboxls,3,mt->map.extent.minx); GEOSCoordSeq_setY(mtbboxls,3,mt->map.extent.maxy); GEOSCoordSeq_setX(mtbboxls,4,mt->map.extent.minx); GEOSCoordSeq_setY(mtbboxls,4,mt->map.extent.miny); GEOSGeometry *mtbbox = GEOSGeom_createLinearRing(mtbboxls); GEOSGeometry *mtbboxg = GEOSGeom_createPolygon(mtbbox,NULL,0); int i; int intersects = 0; for(i=0; i_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(tile->mtime && tile->mtime 0) { intersects = ogr_features_intersect_tile(ctx,tile); } #endif if(intersects != 0) { /* the tile intersects the ogr features, or there was no clipping asked for: seed it */ 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); } tile->tileset = tileset; } action = mode; } else { //if(action == MAPCACHE_CMD_DELETE) action = MAPCACHE_CMD_DELETE; } } else { /* the tile does not intersect the ogr features, and already exists, do nothing */ action = MAPCACHE_CMD_SKIP; } } } else { //BUG: tile_exists returned true, but tile_get returned a failure. not sure what to do. action = MAPCACHE_CMD_SKIP; } } else { if(mode == MAPCACHE_CMD_DELETE) { //the tile exists and we are in delete mode: delete it action = MAPCACHE_CMD_DELETE; } else if (mode == MAPCACHE_CMD_TRANSFER) { /* 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)) { action = MAPCACHE_CMD_SKIP; } else { action = MAPCACHE_CMD_TRANSFER; } tile->tileset = tileset; } else { // the tile exists and we are in seed mode, skip to next one action = MAPCACHE_CMD_SKIP; } } } else { // the tile does not exist if(mode == MAPCACHE_CMD_SEED || mode == MAPCACHE_CMD_TRANSFER) { #ifdef USE_CLIPPERS /* check we are in the requested features before deleting the tile */ if(nClippers > 0) { if(ogr_features_intersect_tile(ctx,tile)) { action = mode; } else { action = MAPCACHE_CMD_SKIP; } } else { action = mode; } #else action = mode; #endif } else { action = MAPCACHE_CMD_SKIP; } } return action; } void cmd_recurse(mapcache_context *cmd_ctx, mapcache_tile *tile) { cmd action; int curx, cury, curz; int blchildx,trchildx,blchildy,trchildy; int minchildx,maxchildx,minchildy,maxchildy; mapcache_extent bboxbl,bboxtr; double epsilon; apr_pool_clear(cmd_ctx->pool); if(sig_int_received || error_detected) { //stop if we were asked to stop by hitting ctrl-c //remove all items from the queue struct seed_cmd entry; while (trypop_queue(&entry)!=APR_EAGAIN) /*do nothing*/; return; } action = examine_tile(cmd_ctx, tile); if(action == MAPCACHE_CMD_SEED || action == MAPCACHE_CMD_DELETE || action == MAPCACHE_CMD_TRANSFER) { //current x,y,z needs seeding, add it to the queue struct seed_cmd cmd; cmd.x = tile->x; cmd.y = tile->y; cmd.z = tile->z; cmd.command = action; push_queue(cmd); } //recurse into our 4 child metatiles curx = tile->x; cury = tile->y; curz = tile->z; tile->z += 1; if(tile->z > maxzoom) { tile->z -= 1; return; } /* * compute the x,y limits of the next zoom level that intersect the * current metatile */ mapcache_grid_get_extent(cmd_ctx, grid_link->grid, curx, cury, curz, &bboxbl); mapcache_grid_get_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, bboxbl.minx + epsilon, bboxbl.miny + epsilon, tile->z,&blchildx,&blchildy); mapcache_grid_get_xy(cmd_ctx,grid_link->grid, bboxtr.maxx - epsilon, bboxtr.maxy - epsilon, tile->z,&trchildx,&trchildy); minchildx = (MAPCACHE_MIN(blchildx,trchildx) / tileset->metasize_x)*tileset->metasize_x; minchildy = (MAPCACHE_MIN(blchildy,trchildy) / tileset->metasize_y)*tileset->metasize_y; maxchildx = (MAPCACHE_MAX(blchildx,trchildx) / tileset->metasize_x + 1)*tileset->metasize_x; maxchildy = (MAPCACHE_MAX(blchildy,trchildy) / tileset->metasize_y + 1)*tileset->metasize_y; for(tile->x = minchildx; tile->x < maxchildx; tile->x += tileset->metasize_x) { if(tile->x >= grid_link->grid_limits[tile->z].minx && tile->x < grid_link->grid_limits[tile->z].maxx) { for(tile->y = minchildy; tile->y < maxchildy; tile->y += tileset->metasize_y) { if(tile->y >= grid_link->grid_limits[tile->z].miny && tile->y < grid_link->grid_limits[tile->z].maxy) { cmd_recurse(cmd_ctx,tile); } } } } tile->x = curx; tile->y = cury; tile->z = curz; } void cmd_worker() { int n; mapcache_tile *tile; int z = minzoom; int x = grid_link->grid_limits[z].minx; int y = grid_link->grid_limits[z].miny; mapcache_context cmd_ctx = ctx; int nworkers = nthreads; 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; if(iteration_mode == MAPCACHE_ITERATION_DEPTH_FIRST) { do { tile->x = x; tile->y = y; tile->z = z; cmd_recurse(&cmd_ctx,tile); x += tileset->metasize_x; if( x >= grid_link->grid_limits[z].maxx ) { y += tileset->metasize_y; if( y < grid_link->grid_limits[z].maxy) { x = grid_link->grid_limits[z].minx; } } } while ( x < grid_link->grid_limits[z].maxx && y < grid_link->grid_limits[z].maxy ); } else { while(1) { int action; apr_pool_clear(cmd_ctx.pool); if(sig_int_received || error_detected) { //stop if we were asked to stop by hitting ctrl-c //remove all items from the queue struct seed_cmd entry; while (trypop_queue(&entry)!=APR_EAGAIN) /* do nothing */; break; } if(iteration_mode == MAPCACHE_ITERATION_LOG) { if(3 != fscanf(retry_log,"%d,%d,%d\n",&x,&y,&z)) { break; } else { printf("from log: %d %d %d\n",x,y,z); } } tile->x = x; tile->y = y; tile->z = z; action = examine_tile(&cmd_ctx, tile); if(action == MAPCACHE_CMD_SEED || action == MAPCACHE_CMD_DELETE || action == MAPCACHE_CMD_TRANSFER) { //current x,y,z needs seeding, add it to the queue struct seed_cmd cmd; cmd.x = x; cmd.y = y; cmd.z = z; cmd.command = action; push_queue(cmd); } //compute next x,y,z x += tileset->metasize_x; if(x >= grid_link->grid_limits[z].maxx) { //x is too big, increment y y += tileset->metasize_y; if(y >= grid_link->grid_limits[z].maxy) { //y is too big, increment z z += 1; if(z > maxzoom) break; //we've finished seeding y = grid_link->grid_limits[z].miny; //set y to the smallest value for current z } x = grid_link->grid_limits[z].minx; //set x to smallest value for current z } } } //instruct rendering threads to stop working for(n=0; ndimensions = dimensions; while(1) { struct seed_cmd cmd; apr_status_t ret; apr_pool_clear(seed_ctx.pool); ret = pop_queue(&cmd); if(ret != APR_SUCCESS || cmd.command == MAPCACHE_CMD_STOP) break; tile->x = cmd.x; tile->y = cmd.y; tile->z = cmd.z; 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) { /* 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 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]; mapcache_tileset_tile_get(&seed_ctx, subtile); subtile->tileset = tileset_transfer; subtile->tileset->_cache->tile_set(&seed_ctx, subtile->tileset->_cache, subtile); } } else { //CMD_DELETE mapcache_tileset_tile_delete(&seed_ctx,tile,MAPCACHE_TRUE); } { struct seed_status *st = calloc(1,sizeof(struct seed_status)); st->x=tile->x; st->y=tile->y; st->z=tile->z; if(seed_ctx.get_error(&seed_ctx)) { st->status = MAPCACHE_STATUS_FAIL; st->msg = strdup(seed_ctx.get_error_message(&seed_ctx)); seed_ctx.clear_errors(&seed_ctx); } else { st->status = MAPCACHE_STATUS_OK; } ret = apr_queue_push(log_queue,(void*)st); if(ret != APR_SUCCESS) { printf("FATAL ERROR: unable to log progress\n"); break; } } } } #ifdef USE_FORK int seed_process() { seed_worker(); 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 log_thread_fn(apr_thread_t *thread, void *data) { char failed[FAIL_BACKLOG_COUNT]; memset(failed,-1,FAIL_BACKLOG_COUNT); size_t cur=0; double last_time=0, now_time; while(1) { struct seed_status *st; apr_status_t 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(!quiet) { struct mctimeval now; mapcache_gettimeofday(&now,NULL); now_time = now.tv_sec + now.tv_usec / 1000000.0; if((now_time - last_time) > 1.0) { printf(" \r"); printf("seeded %d tiles, now at z%d x%d y%d\r",n_metatiles_tot*tileset->metasize_x*tileset->metasize_y, st->z,st->x,st->y); fflush(stdout); last_time = now_time; } } } else { /* count how many errors and successes we have */ failed[cur]=1; int i,nfailed=0,ntotal=0; if(failed_log) { fprintf(failed_log,"%d,%d,%d\n",st->x,st->y,st->z); } for(i=0; i=0) ntotal++; if(failed[i]==1) nfailed++; } ctx.log(&ctx, MAPCACHE_WARN, "failed to seed tile z%d,x%d,y%d:\n%s\n", st->z,st->x,st->y,st->msg); double pct = ((double)nfailed / (double)ntotal) * 100; 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); free(st); cur++; cur %= FAIL_BACKLOG_COUNT; } return NULL; } void notice(const char *fmt, ...) { va_list ap; fprintf( stdout, "NOTICE: "); va_start (ap, fmt); vfprintf( stdout, fmt, ap); va_end(ap); fprintf( stdout, "\n" ); } void log_and_exit(const char *fmt, ...) { va_list ap; fprintf( stdout, "ERROR: "); va_start (ap, fmt); vfprintf( stdout, fmt, ap); va_end(ap); fprintf( stdout, "\n" ); exit(1); } int usage(const char *progname, char *msg, ...) { int i=0; if(msg) { va_list args; va_start(args,msg); printf("%s\n",progname); vprintf(msg,args); printf("\noptions:\n"); va_end(args); } else 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); } else { printf("-%c|--%s: %s\n",seed_options[i].optch,seed_options[i].name, seed_options[i].description); } i++; } apr_terminate(); return 1; } static int isPowerOfTwo(int x) { return (x & (x - 1)) == 0; } 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; const char *tileset_name=NULL; const char *tileset_transfer_name=NULL; const char *grid_name = NULL; const char *cache_override = NULL; int *zooms = NULL;//[2]; mapcache_extent *extent = NULL;//[4]; int optch; int rv,n; const char *old = NULL; const char *optarg; apr_table_t *argdimensions; char *dimkey=NULL, *dimvalue=NULL,*key, *last, *optargcpy=NULL; int keyidx; int *metasizes = NULL;//[2]; int metax=-1,metay=-1; double *extent_array = NULL; #ifdef USE_CLIPPERS const char *ogr_where = NULL; const char *ogr_layer = NULL; const char *ogr_sql = NULL; const char *ogr_datasource = NULL; #endif apr_initialize(); (void) signal(SIGINT,handle_sig_int); apr_pool_create(&ctx.pool,NULL); mapcache_context_init(&ctx); cfg = mapcache_configuration_create(ctx.pool); ctx.config = cfg; ctx.log= mapcache_context_seeding_log; apr_getopt_init(&opt, ctx.pool, argc, argv); mapcache_gettimeofday(&starttime,NULL); argdimensions = apr_table_make(ctx.pool,3); /* parse the all options based on opt_option[] */ while ((rv = apr_getopt_long(opt, seed_options, &optch, &optarg)) == APR_SUCCESS) { switch (optch) { case 'h': return usage(argv[0],NULL); break; case 'f': force = 1; break; case 'q': quiet = 1; break; case 'v': verbose = 1; break; case 'c': configfile = optarg; break; case 'C': cache_override = optarg; break; case 'g': grid_name = optarg; break; case 't': tileset_name = optarg; break; case 'x': tileset_transfer_name = optarg; break; case 'i': if(!strcmp(optarg,"drill-down")) { iteration_mode = MAPCACHE_ITERATION_DEPTH_FIRST; } else if(!strcmp(optarg,"level-by-level")) { iteration_mode = MAPCACHE_ITERATION_LEVEL_FIRST; } else { return usage(argv[0],"invalid iteration mode, expecting \"drill-down\" or \"level-by-level\""); } break; case 'L': failed_log = fopen(optarg,"w"); if(!failed_log) { return usage(argv[0],"failed to open -L|--log-failed file for writing"); } break; case 'R': retry_log = fopen(optarg,"r"); if(!retry_log) { return usage(argv[0],"failed to open -R|--retry_failed file for writing"); } break; case 'm': if(!strcmp(optarg,"delete")) { mode = MAPCACHE_CMD_DELETE; } else if(!strcmp(optarg,"transfer")) { mode = MAPCACHE_CMD_TRANSFER; } else if(strcmp(optarg,"seed")) { return usage(argv[0],"invalid mode, expecting \"seed\", \"delete\" or \"transfer\""); } else { mode = MAPCACHE_CMD_SEED; } break; case 'n': nthreads = (int)strtol(optarg, NULL, 10); if(nthreads <=0 ) return usage(argv[0], "failed to parse nthreads, expecting positive integer"); break; case 'p': #ifdef USE_FORK nprocesses = (int)strtol(optarg, NULL, 10); if(nprocesses <=0 ) return usage(argv[0], "failed to parse nprocesses, expecting positive integer"); break; #else return usage(argv[0], "multi process seeding not available on this platform"); #endif case 'P': percent_failed_allowed = (double)strtol(optarg, NULL, 10); if(percent_failed_allowed<0 || percent_failed_allowed>100 ) return usage(argv[0], "failed to parse percent, expecting number between 0 and 100"); break; case 'e': if ( MAPCACHE_SUCCESS != mapcache_util_extract_double_list(&ctx, (char*)optarg, ",", &extent_array, &n) || n != 4 || extent_array[0] >= extent_array[2] || extent_array[1] >= extent_array[3] ) { return usage(argv[0], "failed to parse extent, expecting comma separated 4 doubles"); } extent = apr_palloc(ctx.pool,sizeof(mapcache_extent)); extent->minx = extent_array[0]; extent->miny = extent_array[1]; extent->maxx = extent_array[2]; extent->maxy = extent_array[3]; break; case 'z': if ( MAPCACHE_SUCCESS != mapcache_util_extract_int_list(&ctx, (char*)optarg, ",", &zooms, &n) || n != 2 || zooms[0] > zooms[1]) { return usage(argv[0], "failed to parse zooms, expecting comma separated 2 ints"); } else { minzoom = zooms[0]; maxzoom = zooms[1]; } break; case 'M': if ( MAPCACHE_SUCCESS != mapcache_util_extract_int_list(&ctx, (char*)optarg, ",", &metasizes, &n) || n != 2 || metasizes[0] <= 0 || metasizes[1] <=0) { return usage(argv[0], "failed to parse metasize, expecting comma separated 2 positive ints (e.g. -M 8,8"); } else { metax = metasizes[0]; metay = metasizes[1]; } break; case 'o': old = optarg; break; case 'D': optargcpy = apr_pstrdup(ctx.pool,optarg); keyidx = 0; for (key = apr_strtok(optargcpy, "=", &last); key != NULL; key = apr_strtok(NULL, "=", &last)) { if(keyidx == 0) { dimkey = key; } else { dimvalue = key; } keyidx++; } if(keyidx!=2 || !dimkey || !dimvalue || !*dimkey || !*dimvalue) { return usage(argv[0], "failed to parse dimension, expecting DIMNAME=DIMVALUE"); } apr_table_set(argdimensions,dimkey,dimvalue); break; #ifdef USE_CLIPPERS case 'd': ogr_datasource = optarg; break; case 's': ogr_sql = optarg; break; case 'l': ogr_layer = optarg; break; case 'w': ogr_where = optarg; break; #endif } } if (rv != APR_EOF) { return usage(argv[0],"bad options"); } if( ! configfile ) { return usage(argv[0],"config not specified"); } else { mapcache_configuration_parse(&ctx,configfile,cfg,0); if(ctx.get_error(&ctx)) return usage(argv[0],ctx.get_error_message(&ctx)); mapcache_configuration_post_config(&ctx,cfg); if(ctx.get_error(&ctx)) return usage(argv[0],ctx.get_error_message(&ctx)); mapcache_connection_pool_create(&ctx.connection_pool, ctx.pool); } #ifdef USE_CLIPPERS if(extent && ogr_datasource) { return usage(argv[0], "cannot specify both extent and ogr-datasource"); } if( ogr_sql && ( ogr_where || ogr_layer )) { return usage(argv[0], "ogr-where or ogr_layer cannot be used in conjunction with ogr-sql"); } if(ogr_datasource) { OGRDataSourceH hDS = NULL; OGRLayerH layer = NULL; OGRRegisterAll(); hDS = OGROpen( ogr_datasource, FALSE, NULL ); if( hDS == NULL ) { printf( "OGR Open failed\n" ); exit( 1 ); } if(ogr_sql) { layer = OGR_DS_ExecuteSQL( hDS, ogr_sql, NULL, NULL); if(!layer) { return usage(argv[0],"aborting"); } } else { int nLayers = OGR_DS_GetLayerCount(hDS); if(nLayers>1 && !ogr_layer) { return usage(argv[0],"ogr datastore contains more than one layer. please specify which one to use with --ogr-layer"); } else { if(ogr_layer) { layer = OGR_DS_GetLayerByName(hDS,ogr_layer); } else { layer = OGR_DS_GetLayer(hDS,0); } if(!layer) { return usage(argv[0],"aborting"); } if(ogr_where) { if(OGRERR_NONE != OGR_L_SetAttributeFilter(layer, ogr_where)) { return usage(argv[0],"aborting"); } } } } if((nClippers=OGR_L_GetFeatureCount(layer, TRUE)) == 0) { printf("no features in provided ogr parameters, cannot continue\n"); apr_terminate(); exit(0); } initGEOS(notice, log_and_exit); clippers = (const GEOSPreparedGeometry**)malloc(nClippers*sizeof(GEOSPreparedGeometry*)); OGRFeatureH hFeature; GEOSWKTReader *geoswktreader = GEOSWKTReader_create(); OGR_L_ResetReading(layer); extent = apr_palloc(ctx.pool,4*sizeof(mapcache_extent)); int f=0; while( (hFeature = OGR_L_GetNextFeature(layer)) != NULL ) { OGRGeometryH geom = OGR_F_GetGeometryRef(hFeature); if(!geom || !OGR_G_IsValid(geom)) continue; char *wkt; OGR_G_ExportToWkt(geom,&wkt); GEOSGeometry *geosgeom = GEOSWKTReader_read(geoswktreader,wkt); free(wkt); clippers[f] = GEOSPrepare(geosgeom); //GEOSGeom_destroy(geosgeom); OGREnvelope ogr_extent; OGR_G_GetEnvelope (geom, &ogr_extent); if(f == 0) { extent->minx = ogr_extent.MinX; extent->miny = ogr_extent.MinY; extent->maxx = ogr_extent.MaxX; extent->maxy = ogr_extent.MaxY; } else { extent->minx = MAPCACHE_MIN(ogr_extent.MinX, extent->minx); extent->miny = MAPCACHE_MIN(ogr_extent.MinY, extent->miny); extent->maxx = MAPCACHE_MAX(ogr_extent.MaxX, extent->maxx); extent->maxy = MAPCACHE_MAX(ogr_extent.MaxY, extent->maxy); } OGR_F_Destroy( hFeature ); f++; } nClippers = f; } #endif if( ! tileset_name ) { return usage(argv[0],"tileset not specified"); } else { tileset = mapcache_configuration_get_tileset(cfg,tileset_name); 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); tileset->read_only = 0; } if( ! grid_name ) { grid_link = APR_ARRAY_IDX(tileset->grid_links,0,mapcache_grid_link*); } else { int i; for(i=0; igrid_links->nelts; i++) { mapcache_grid_link *sgrid = APR_ARRAY_IDX(tileset->grid_links,i,mapcache_grid_link*); if(!strcmp(sgrid->grid->name,grid_name)) { grid_link = sgrid; break; } } if(!grid_link) { return usage(argv[0],"grid not configured for tileset"); } } if(iteration_mode == MAPCACHE_ITERATION_UNSET) { if(!strcmp(grid_link->grid->name,"g") || !strcmp(grid_link->grid->name,"WGS84") || !strcmp(grid_link->grid->name,"GoogleMapsCompatible")) { iteration_mode = MAPCACHE_ITERATION_DEPTH_FIRST; } else { iteration_mode = MAPCACHE_ITERATION_LEVEL_FIRST; } } if(retry_log) { iteration_mode = MAPCACHE_ITERATION_LOG; } if(minzoom == -1 && maxzoom == -1) { minzoom = grid_link->minz; maxzoom = grid_link->maxz - 1; } if(minzoomminz) 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; } /* adjust metasize */ if(metax>0) { tileset->metasize_x = metax; tileset->metasize_y = metay; } /* 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\""); } } 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); } else { tileset->_cache = co; } } } if (mode == MAPCACHE_CMD_TRANSFER) { if (!tileset_transfer_name) return usage(argv[0],"tileset where tiles should be transferred to not specified"); tileset_transfer = mapcache_configuration_get_tileset(cfg,tileset_transfer_name); if(!tileset_transfer) return usage(argv[0], "tileset where tiles should be transferred to not found in configuration"); } if(old) { if(strcasecmp(old,"now")) { struct tm oldtime; char *ret; memset(&oldtime,0,sizeof(oldtime)); ret = strptime(old,"%Y/%m/%d %H:%M",&oldtime); if(!ret || *ret) { return usage(argv[0],"failed to parse time"); } if(APR_SUCCESS != apr_time_ansi_put(&age_limit,mktime(&oldtime))) { return usage(argv[0],"failed to convert time"); } } else { age_limit = apr_time_now(); } } if(extent) { // update the grid limits mapcache_grid_compute_limits(grid_link->grid,extent,grid_link->grid_limits,0); } /* adjust our grid limits so they align on the metatile limits * we need to do this because the seeder does not check for individual tiles, it * goes from one metatile to the next*/ for(n=0; ngrid->nlevels; n++) { if(tileset->metasize_x > 1) { grid_link->grid_limits[n].minx = (grid_link->grid_limits[n].minx/tileset->metasize_x)*tileset->metasize_x; grid_link->grid_limits[n].maxx = (grid_link->grid_limits[n].maxx/tileset->metasize_x+1)*tileset->metasize_x; if( grid_link->grid_limits[n].maxx > grid_link->grid->levels[n]->maxx) grid_link->grid_limits[n].maxx = grid_link->grid->levels[n]->maxx; } if(tileset->metasize_y > 1) { grid_link->grid_limits[n].miny = (grid_link->grid_limits[n].miny/tileset->metasize_y)*tileset->metasize_y; grid_link->grid_limits[n].maxy = (grid_link->grid_limits[n].maxy/tileset->metasize_y+1)*tileset->metasize_y; if( grid_link->grid_limits[n].maxy > grid_link->grid->levels[n]->maxy) grid_link->grid_limits[n].maxy = grid_link->grid->levels[n]->maxy; } } /* validate the supplied dimensions */ if (!apr_is_empty_array(tileset->dimensions) || tileset->timedimension) { int i; const char *value; dimensions = apr_table_make(ctx.pool,3); if (!apr_is_empty_array(tileset->dimensions)) { for(i=0; idimensions->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); } } } 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=",tileset->timedimension->key); } } } { /* 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); //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"); } if (-1 == msgctl(msqid, IPC_STAT, &queue_ds)) { return usage(argv[0], "\nFailure in msgctl() stat"); } queue_ds.msg_qbytes = nprocesses*sizeof(struct seed_cmd); if(-1 == msgctl(msqid, IPC_SET, &queue_ds)) { switch(errno) { case EACCES: return usage(argv[0], "\nFailure in msgctl() set qbytes: EACCESS (should not happen here)"); case EFAULT: return usage(argv[0], "\nFailure in msgctl() set qbytes: EFAULT queue not accessible"); case EIDRM: return usage(argv[0], "\nFailure in msgctl() set qbytes: EIDRM message queue removed"); case EINVAL: return usage(argv[0], "\nFailure in msgctl() set qbytes: EINVAL invalid value for msg_qbytes"); case EPERM: return usage(argv[0], "\nFailure in msgctl() set qbytes: EPERM permission denied on msg_qbytes"); default: return usage(argv[0], "\nFailure in msgctl() set qbytes: unknown"); } } for(i=0; istatus = MAPCACHE_STATUS_FINISHED; apr_queue_push(log_queue,(void*)st); apr_thread_join(&rv, log_thread); } if(n_metatiles_tot>0) { struct mctimeval now_t; float duration; int ntilestot = n_metatiles_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); } else { if(!error_detected) { printf("0 tiles needed to be seeded, exiting\n"); } } apr_terminate(); if (error_detected > 0) { exit(1); } return 0; } /* vim: ts=2 sts=2 et sw=2 */