pax_global_header00006660000000000000000000000064122615202360014511gustar00rootroot0000000000000052 comment=70d3c3c7140c7e97953fabd3df670de82db1c8cb mapcache-rel-1-2-1/000077500000000000000000000000001226152023600140315ustar00rootroot00000000000000mapcache-rel-1-2-1/.gitignore000066400000000000000000000000321226152023600160140ustar00rootroot00000000000000.*.swp nbproject/ /build/ mapcache-rel-1-2-1/CMakeLists.txt000066400000000000000000000223121226152023600165710ustar00rootroot00000000000000cmake_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 2) set (MAPCACHE_VERSION_REVISION 1) 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) 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) 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(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}") INSTALL(TARGETS mapcache DESTINATION ${CMAKE_INSTALL_LIBDIR}) add_subdirectory(util) add_subdirectory(cgi) add_subdirectory(apache) add_subdirectory(nginx) mapcache-rel-1-2-1/INSTALL000066400000000000000000000006741226152023600150710ustar00rootroot00000000000000Unix 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-rel-1-2-1/LICENSE000066400000000000000000000027711226152023600150450ustar00rootroot00000000000000/****************************************************************************** * $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-rel-1-2-1/Makefile.vc000066400000000000000000000056321226152023600161060ustar00rootroot00000000000000# # 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-rel-1-2-1/README000066400000000000000000000001011226152023600147010ustar00rootroot00000000000000new code repository is at https://github.com/mapserver/mapcache mapcache-rel-1-2-1/apache/000077500000000000000000000000001226152023600152525ustar00rootroot00000000000000mapcache-rel-1-2-1/apache/CMakeLists.txt000066400000000000000000000031301226152023600200070ustar00rootroot00000000000000option(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-rel-1-2-1/apache/mod_mapcache-config.h.in000066400000000000000000000001511226152023600216700ustar00rootroot00000000000000#ifndef _MOD_MAPCACHE_CONFIG_H #define _MOD_MAPCACHE_CONFIG_H #cmakedefine USE_VERSION_STRING 1 #endif mapcache-rel-1-2-1/apache/mod_mapcache.c000066400000000000000000000372511226152023600200260ustar00rootroot00000000000000/****************************************************************************** * $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; apr_pool_t *pchild = NULL; 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; ctx->ctx.ctx.process_pool = pchild; #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; 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; } 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) { 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) { pchild = pool; #ifdef APR_HAS_THREADS apr_thread_mutex_create(&thread_mutex,APR_THREAD_MUTEX_DEFAULT,pool); #endif } 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) { return HTTP_METHOD_NOT_ALLOWED; } apache_ctx = apache_request_context_create(r); global_ctx = (mapcache_context*)apache_ctx; 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 ) { mapcache_request_proxy *req_proxy = (mapcache_request_proxy*)request; 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-rel-1-2-1/astyle.sh000077500000000000000000000002571226152023600156750ustar00rootroot00000000000000#!/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-rel-1-2-1/benchmark.py000066400000000000000000000042171226152023600163410ustar00rootroot00000000000000#! /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-rel-1-2-1/cgi/000077500000000000000000000000001226152023600145735ustar00rootroot00000000000000mapcache-rel-1-2-1/cgi/CMakeLists.txt000066400000000000000000000022071226152023600173340ustar00rootroot00000000000000option(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-rel-1-2-1/cgi/mapcache-cgi-config.h.in000066400000000000000000000001421226152023600211120ustar00rootroot00000000000000#ifndef _MAPCACHE_CGI_CONFIG_H #define _MAPCACHE_CGI_CONFIG_H #cmakedefine USE_FASTCGI 1 #endif mapcache-rel-1-2-1/cgi/mapcache.c000066400000000000000000000260401226152023600165020ustar00rootroot00000000000000/****************************************************************************** * $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; 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->process_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-rel-1-2-1/cmake/000077500000000000000000000000001226152023600151115ustar00rootroot00000000000000mapcache-rel-1-2-1/cmake/FindAPACHE.cmake000066400000000000000000000015671226152023600176460ustar00rootroot00000000000000# # 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-rel-1-2-1/cmake/FindAPR.cmake000066400000000000000000000046031226152023600173410ustar00rootroot00000000000000# 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-rel-1-2-1/cmake/FindBerkeleyDB.cmake000066400000000000000000000071051226152023600206670ustar00rootroot00000000000000set(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-rel-1-2-1/cmake/FindFCGI.cmake000066400000000000000000000006001226152023600174200ustar00rootroot00000000000000# 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-rel-1-2-1/cmake/FindGEOS.cmake000066400000000000000000000016501226152023600174530ustar00rootroot00000000000000# 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-rel-1-2-1/cmake/FindGEOTIFF.cmake000066400000000000000000000006751226152023600200070ustar00rootroot00000000000000find_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-rel-1-2-1/cmake/FindMAPSERVER.cmake000066400000000000000000000006611226152023600202630ustar00rootroot00000000000000 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-rel-1-2-1/cmake/FindPCRE.cmake000066400000000000000000000005321226152023600174450ustar00rootroot00000000000000 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-rel-1-2-1/cmake/FindPixman.cmake000066400000000000000000000012721226152023600201520ustar00rootroot00000000000000FIND_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-rel-1-2-1/cmake/FindSQLITE.cmake000066400000000000000000000006331226152023600177170ustar00rootroot00000000000000 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-rel-1-2-1/include/000077500000000000000000000000001226152023600154545ustar00rootroot00000000000000mapcache-rel-1-2-1/include/errors.h000066400000000000000000000036341226152023600171470ustar00rootroot00000000000000/****************************************************************************** * $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-rel-1-2-1/include/ezxml.h000066400000000000000000000152701226152023600167710ustar00rootroot00000000000000/* 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-rel-1-2-1/include/mapcache-config.h.in000066400000000000000000000006121226152023600212350ustar00rootroot00000000000000#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 HAVE_STRNCASECMP 1 #cmakedefine HAVE_SYMLINK 1 #endif mapcache-rel-1-2-1/include/mapcache-version.h.in000066400000000000000000000005261226152023600214610ustar00rootroot00000000000000#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-rel-1-2-1/include/mapcache.h000066400000000000000000001472011226152023600173730ustar00rootroot00000000000000/****************************************************************************** * $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 "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 #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_USERAGENT "mod-mapcache/"MAPCACHE_VERSION #define MAPCACHE_LOCKFILE_PREFIX "_gc_lock" 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; #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_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_regex mapcache_dimension_regex; typedef struct mapcache_extent mapcache_extent; typedef struct mapcache_extent_i mapcache_extent_i; /** \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 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; apr_pool_t *process_pool; void *threadlock; char *_contenttype; char *_errmsg; int _errcode; mapcache_cfg *config; mapcache_service *service; apr_table_t *exceptions; }; 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 */ 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 #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_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_tile * tile); /** * delete tile from cache * * \memberof mapcache_cache */ void (*tile_delete)(mapcache_context *ctx, mapcache_tile * tile); int (*tile_exists)(mapcache_context *ctx, mapcache_tile * tile); /** * set tile content to cache * \memberof mapcache_cache */ void (*tile_set)(mapcache_context *ctx, mapcache_tile * tile); void (*tile_multi_set)(mapcache_context *ctx, 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_tile *tile, char **path); }; #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; }; #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 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_tile *tile); int n_prepared_statements; int detect_blank; }; /** * \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 { mapcache_cache cache; apr_memcache_t *memcache; }; /** * \memberof mapcache_cache_memcache */ mapcache_cache* mapcache_cache_memcache_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_get_tile { mapcache_request 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; mapcache_image_format *format; }; 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 request; mapcache_map **maps; int nmaps; mapcache_getmap_strategy getmap_strategy; mapcache_resample_mode resample_mode; mapcache_image_format *getmap_format; }; 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_request_proxy { mapcache_request request; mapcache_http *http; apr_table_t *params; const char *pathinfo; }; struct mapcache_forwarding_rule { char *name; mapcache_http *http; apr_array_header_t *match_params; /* actually those are mapcache_dimensions */ int append_pathinfo; }; /** \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; }; /**\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); /** @} */ /** \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); void mapcache_http_do_request_with_params(mapcache_context *ctx, mapcache_http *req, apr_table_t *params, 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 */ }; typedef enum { MAPCACHE_MODE_NORMAL, MAPCACHE_MODE_MIRROR_COMBINED, MAPCACHE_MODE_MIRROR_SPLIT } mapcache_mode; /** * 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; /** * 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 *lockdir; /** * time in nanoseconds to wait before rechecking for lockfile presence */ apr_interval_time_t lock_retry_interval; /* time in nanoseconds to wait before rechecking for lockfile presence */ 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); #ifdef USE_TIFF /** * \memberof mapcache_cache_tiff */ mapcache_cache* mapcache_cache_tiff_create(mapcache_context *ctx); #endif /** \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; 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 */ const char** (*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_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_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); #endif /* MAPCACHE_H_ */ /* vim: ts=2 sts=2 et sw=2 */ mapcache-rel-1-2-1/include/util.h000066400000000000000000000062311226152023600166040ustar00rootroot00000000000000/****************************************************************************** * $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-rel-1-2-1/lib/000077500000000000000000000000001226152023600145775ustar00rootroot00000000000000mapcache-rel-1-2-1/lib/axisorder.c000066400000000000000000000544031226152023600167510ustar00rootroot00000000000000/****************************************************************************** * $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; 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-rel-1-2-1/lib/cache_bdb.c000066400000000000000000000406571226152023600166310ustar00rootroot00000000000000/****************************************************************************** * $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 static apr_hash_t *ro_connection_pools = NULL; static apr_hash_t *rw_connection_pools = NULL; struct bdb_env { DB* db; DB_ENV *env; int readonly; char *errmsg; }; static apr_status_t _bdb_reslist_get_connection(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) { benv->errmsg = apr_psprintf(pool,"bdb cache failure for db_env_create: %s", db_strerror(ret)); return APR_EGENERAL; } ret = benv->env->set_cachesize(benv->env,0,CACHESIZE,1); /* set a larger cache size than default */ if(ret) { benv->errmsg = apr_psprintf(pool, "bdb cache failure for db->set_cachesize: %s", db_strerror(ret)); return APR_EGENERAL; } env_flags = DB_INIT_CDB|DB_INIT_MPOOL|DB_CREATE; ret = benv->env->open(benv->env,cache->basedir,env_flags,0); if(ret) { benv->errmsg = apr_psprintf(pool,"bdb cache failure for env->open: %s", db_strerror(ret)); return APR_EGENERAL; } if ((ret = db_create(&benv->db, benv->env, 0)) != 0) { benv->errmsg = apr_psprintf(pool,"bdb cache failure for db_create: %s", db_strerror(ret)); return APR_EGENERAL; } 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) { benv->errmsg = apr_psprintf(pool,"bdb cache failure for db->set_pagesize: %s", db_strerror(ret)); return APR_EGENERAL; } if ((ret = benv->db->open(benv->db, NULL, dbfile, NULL, mode, DB_CREATE, 0664)) != 0) { benv->errmsg = apr_psprintf(pool,"bdb cache failure 1 for db->open: %s", db_strerror(ret)); return APR_EGENERAL; } return APR_SUCCESS; } static apr_status_t _bdb_reslist_free_connection(void *conn_, void *params, 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); return APR_SUCCESS; } static struct bdb_env* _bdb_get_conn(mapcache_context *ctx, mapcache_tile* tile, int readonly) { apr_status_t rv; mapcache_cache_bdb *cache = (mapcache_cache_bdb*)tile->tileset->cache; struct bdb_env *benv; apr_hash_t *pool_container; apr_reslist_t *pool = NULL; if(readonly) { pool_container = ro_connection_pools; } else { pool_container = rw_connection_pools; } if(!pool_container || NULL == (pool = apr_hash_get(pool_container,cache->cache.name, APR_HASH_KEY_STRING)) ) { #ifdef APR_HAS_THREADS if(ctx->threadlock) apr_thread_mutex_lock((apr_thread_mutex_t*)ctx->threadlock); #endif if(!ro_connection_pools) { ro_connection_pools = apr_hash_make(ctx->process_pool); rw_connection_pools = apr_hash_make(ctx->process_pool); } /* probably doesn't exist, unless the previous mutex locked us, so we check */ pool = apr_hash_get(ro_connection_pools,cache->cache.name, APR_HASH_KEY_STRING); if(!pool) { /* there where no existing connection pools, create them*/ rv = apr_reslist_create(&pool, 0 /* min */, 10 /* soft max */, 200 /* hard max */, 60*1000000 /*60 seconds, ttl*/, _bdb_reslist_get_connection, /* resource constructor */ _bdb_reslist_free_connection, /* resource destructor */ cache, ctx->process_pool); if(rv != APR_SUCCESS) { ctx->set_error(ctx,500,"failed to create bdb ro connection pool"); #ifdef APR_HAS_THREADS if(ctx->threadlock) apr_thread_mutex_unlock((apr_thread_mutex_t*)ctx->threadlock); #endif return NULL; } apr_hash_set(ro_connection_pools,cache->cache.name,APR_HASH_KEY_STRING,pool); rv = apr_reslist_create(&pool, 0 /* min */, 1 /* soft max */, 1 /* hard max */, 60*1000000 /*60 seconds, ttl*/, _bdb_reslist_get_connection, /* resource constructor */ _bdb_reslist_free_connection, /* resource destructor */ cache, ctx->process_pool); if(rv != APR_SUCCESS) { ctx->set_error(ctx,500,"failed to create bdb rw connection pool"); #ifdef APR_HAS_THREADS if(ctx->threadlock) apr_thread_mutex_unlock((apr_thread_mutex_t*)ctx->threadlock); #endif return NULL; } apr_hash_set(rw_connection_pools,cache->cache.name,APR_HASH_KEY_STRING,pool); } #ifdef APR_HAS_THREADS if(ctx->threadlock) apr_thread_mutex_unlock((apr_thread_mutex_t*)ctx->threadlock); #endif if(readonly) pool = apr_hash_get(ro_connection_pools,cache->cache.name, APR_HASH_KEY_STRING); else pool = apr_hash_get(rw_connection_pools,cache->cache.name, APR_HASH_KEY_STRING); assert(pool); } rv = apr_reslist_acquire(pool, (void **)&benv); if(rv != APR_SUCCESS) { ctx->set_error(ctx,500,"failed to aquire connection to bdb backend: %s", (benv&& benv->errmsg)?benv->errmsg:"unknown error"); return NULL; } benv->readonly = readonly; return benv; } static void _bdb_release_conn(mapcache_context *ctx, mapcache_tile *tile, struct bdb_env *benv) { apr_reslist_t *pool; apr_hash_t *pool_container; if(benv->readonly) { pool_container = ro_connection_pools; } else { pool_container = rw_connection_pools; } pool = apr_hash_get(pool_container,tile->tileset->cache->name, APR_HASH_KEY_STRING); if(GC_HAS_ERROR(ctx)) { apr_reslist_invalidate(pool,(void*)benv); } else { apr_reslist_release(pool, (void*)benv); } } static int _mapcache_cache_bdb_has_tile(mapcache_context *ctx, mapcache_tile *tile) { int ret; DBT key; mapcache_cache_bdb *cache = (mapcache_cache_bdb*)tile->tileset->cache; char *skey = mapcache_util_get_tile_key(ctx,tile,cache->key_template,NULL,NULL); struct bdb_env *benv = _bdb_get_conn(ctx,tile,1); if(GC_HAS_ERROR(ctx)) return MAPCACHE_FALSE; 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,tile,benv); return ret; } static void _mapcache_cache_bdb_delete(mapcache_context *ctx, mapcache_tile *tile) { DBT key; int ret; mapcache_cache_bdb *cache = (mapcache_cache_bdb*)tile->tileset->cache; char *skey = mapcache_util_get_tile_key(ctx,tile,cache->key_template,NULL,NULL); struct bdb_env *benv = _bdb_get_conn(ctx,tile,0); GC_CHECK_ERROR(ctx); 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,tile,benv); } static int _mapcache_cache_bdb_get(mapcache_context *ctx, mapcache_tile *tile) { DBT key,data; int ret; struct bdb_env *benv = _bdb_get_conn(ctx,tile,1); mapcache_cache_bdb *cache = (mapcache_cache_bdb*)tile->tileset->cache; char *skey = mapcache_util_get_tile_key(ctx,tile,cache->key_template,NULL,NULL); if(GC_HAS_ERROR(ctx)) return MAPCACHE_FAILURE; 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,(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,tile,benv); return ret; } static void _mapcache_cache_bdb_set(mapcache_context *ctx, mapcache_tile *tile) { DBT key,data; int ret; apr_time_t now; mapcache_cache_bdb *cache = (mapcache_cache_bdb*)tile->tileset->cache; char *skey = mapcache_util_get_tile_key(ctx,tile,cache->key_template,NULL,NULL); struct bdb_env *benv = _bdb_get_conn(ctx,tile,0); GC_CHECK_ERROR(ctx); 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); } 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,tile,benv); } static void _mapcache_cache_bdb_multiset(mapcache_context *ctx, mapcache_tile *tiles, int ntiles) { DBT key,data; int ret,i; apr_time_t now; mapcache_cache_bdb *cache = (mapcache_cache_bdb*)tiles[0].tileset->cache; struct bdb_env *benv = _bdb_get_conn(ctx,&tiles[0],0); GC_CHECK_ERROR(ctx); now = apr_time_now(); memset(&key, 0, sizeof(DBT)); memset(&data, 0, sizeof(DBT)); for(i=0; ikey_template,NULL,NULL); 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); } 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,&tiles[0],benv); } 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-rel-1-2-1/lib/cache_disk.c000066400000000000000000000654771226152023600170430ustar00rootroot00000000000000/****************************************************************************** * $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_tile *tile, char **path) { *path = apr_pstrcat(ctx->pool, ((mapcache_cache_disk*)tile->tileset->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_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", ((mapcache_cache_disk*)tile->tileset->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_tile *tile, char **path) { mapcache_cache_disk *dcache = (mapcache_cache_disk*)tile->tileset->cache; if(dcache->base_directory) { char *start; _mapcache_cache_disk_base_tile_key(ctx, 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 = dcache->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_tile *tile, char **path) { mapcache_cache_disk *dcache = (mapcache_cache_disk*)tile->tileset->cache; *path = dcache->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_tile *tile, char **path) { mapcache_cache_disk *dcache = (mapcache_cache_disk*)tile->tileset->cache; if(dcache->base_directory) { char *start; _mapcache_cache_disk_base_tile_key(ctx, 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 int _mapcache_cache_disk_has_tile(mapcache_context *ctx, mapcache_tile *tile) { char *filename; apr_finfo_t finfo; int rv; ((mapcache_cache_disk*)tile->tileset->cache)->tile_key(ctx, 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_tile *tile) { apr_status_t ret; char errmsg[120]; char *filename; ((mapcache_cache_disk*)tile->tileset->cache)->tile_key(ctx, 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_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*)tile->tileset->cache)->tile_key(ctx, tile, &filename); if(GC_HAS_ERROR(ctx)) { return MAPCACHE_FAILURE; } 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_tile *tile) { apr_size_t bytes; apr_file_t *f; apr_status_t ret; char errmsg[120]; char *filename, *hackptr1, *hackptr2=NULL; const int creation_retry = ((mapcache_cache_disk*)tile->tileset->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 ((mapcache_cache_disk*)tile->tileset->cache)->tile_key(ctx, 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(((mapcache_cache_disk*)tile->tileset->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,tile,tile->raw_image->data,&blankname); if(apr_file_open(&f, blankname, APR_FOPEN_READ, APR_OS_DEFAULT, ctx->pool) != APR_SUCCESS) { 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", ((mapcache_cache_disk*)tile->tileset->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 */ int isLocked = mapcache_lock_or_wait_for_resource(ctx,blankname); 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,blankname); 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,blankname); 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,blankname); return; } apr_file_close(f); mapcache_unlock_resource(ctx,blankname); #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,"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-rel-1-2-1/lib/cache_memcache.c000066400000000000000000000223661226152023600176410ustar00rootroot00000000000000/****************************************************************************** * $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" static int _mapcache_cache_memcache_has_tile(mapcache_context *ctx, mapcache_tile *tile) { char *key; char *tmpdata; int rv; size_t tmpdatasize; mapcache_cache_memcache *cache = (mapcache_cache_memcache*)tile->tileset->cache; key = mapcache_util_get_tile_key(ctx, tile,NULL," \r\n\t\f\e\a\b","#"); if(GC_HAS_ERROR(ctx)) { return MAPCACHE_FALSE; } rv = apr_memcache_getp(cache->memcache,ctx->pool,key,&tmpdata,&tmpdatasize,NULL); if(rv != APR_SUCCESS) { return MAPCACHE_FALSE; } if(tmpdatasize == 0) { return MAPCACHE_FALSE; } return MAPCACHE_TRUE; } static void _mapcache_cache_memcache_delete(mapcache_context *ctx, mapcache_tile *tile) { char *key; int rv; char errmsg[120]; mapcache_cache_memcache *cache = (mapcache_cache_memcache*)tile->tileset->cache; key = mapcache_util_get_tile_key(ctx, tile,NULL," \r\n\t\f\e\a\b","#"); GC_CHECK_ERROR(ctx); rv = apr_memcache_delete(cache->memcache,key,0); if(rv != APR_SUCCESS && rv!= APR_NOTFOUND) { int code = 500; ctx->set_error(ctx,code,"memcache: failed to delete key %s: %s", key, apr_strerror(rv,errmsg,120)); } } /** * \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_tile *tile) { char *key; int rv; mapcache_cache_memcache *cache = (mapcache_cache_memcache*)tile->tileset->cache; key = mapcache_util_get_tile_key(ctx, tile,NULL," \r\n\t\f\e\a\b","#"); if(GC_HAS_ERROR(ctx)) { return MAPCACHE_FAILURE; } tile->encoded_data = mapcache_buffer_create(0,ctx->pool); rv = apr_memcache_getp(cache->memcache,ctx->pool,key,(char**)&tile->encoded_data->buf,&tile->encoded_data->size,NULL); if(rv != APR_SUCCESS) { return MAPCACHE_CACHE_MISS; } if(tile->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); return MAPCACHE_FAILURE; } /* extract the tile modification time from the end of the data returned */ memcpy( &tile->mtime, &(((char*)tile->encoded_data->buf)[tile->encoded_data->size-sizeof(apr_time_t)]), sizeof(apr_time_t)); ((char*)tile->encoded_data->buf)[tile->encoded_data->size+sizeof(apr_time_t)]='\0'; tile->encoded_data->avail = tile->encoded_data->size; tile->encoded_data->size -= sizeof(apr_time_t); return MAPCACHE_SUCCESS; } /** * \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_tile *tile) { char *key; int rv; /* set expiration to one day if not configured */ int expires = 86400; if(tile->tileset->auto_expire) expires = tile->tileset->auto_expire; mapcache_cache_memcache *cache = (mapcache_cache_memcache*)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); } /* 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,tile->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,tile->encoded_data->buf,tile->encoded_data->size); memcpy(&(data[tile->encoded_data->size]),&now,sizeof(apr_time_t)); rv = apr_memcache_set(cache->memcache,key,data,tile->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); return; } } /** * \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; mapcache_cache_memcache *dcache = (mapcache_cache_memcache*)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,"memcache cache %s has no s configured",cache->name); return; } if(APR_SUCCESS != apr_memcache_create(ctx->pool, servercount, 0, &dcache->memcache)) { ctx->set_error(ctx,400,"cache %s: failed to create memcache backend", cache->name); return; } 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"); const char *host; apr_memcache_server_t *server; apr_port_t port; if(!xhost || !xhost->txt || ! *xhost->txt) { ctx->set_error(ctx,400,"cache %s: with no ",cache->name); return; } else { 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; } port = iport; } if(APR_SUCCESS != apr_memcache_server_create(ctx->pool,host,port,4,5,50,10000,&server)) { ctx->set_error(ctx,400,"cache %s: failed to create server %s:%d",cache->name,host,port); return; } if(APR_SUCCESS != apr_memcache_add_server(dcache->memcache,server)) { ctx->set_error(ctx,400,"cache %s: failed to add server %s:%d",cache->name,host,port); return; } if(APR_SUCCESS != apr_memcache_set(dcache->memcache,"mapcache_test_key","mapcache",8,0,0)) { ctx->set_error(ctx,400,"cache %s: failed to add test key to server %s:%d",cache->name,host,port); return; } } } /** * \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->memcache || dcache->memcache->ntotal==0) { 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-rel-1-2-1/lib/cache_sqlite.c000066400000000000000000001057371226152023600174040ustar00rootroot00000000000000/****************************************************************************** * $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 static apr_hash_t *ro_connection_pools = NULL; static apr_hash_t *rw_connection_pools = NULL; struct sqlite_conn { sqlite3 *handle; int readonly; int nstatements; sqlite3_stmt **prepared_statements; char *errmsg; }; #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 int _sqlite_set_pragmas(apr_pool_t *pool, 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(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) { conn->errmsg = apr_psprintf(pool,"failed to execute pragma statement %s",pragma_stmt); return MAPCACHE_FAILURE; } } } return MAPCACHE_SUCCESS; } static apr_status_t _sqlite_reslist_get_rw_connection(void **conn_, void *params, apr_pool_t *pool) { int ret; int flags; mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*) params; struct sqlite_conn *conn = apr_pcalloc(pool, sizeof (struct sqlite_conn)); *conn_ = conn; flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_CREATE; ret = sqlite3_open_v2(cache->dbfile, &conn->handle, flags, NULL); if (ret != SQLITE_OK) { conn->errmsg = apr_psprintf(pool,"sqlite backend failed to open db %s: %s", cache->dbfile, sqlite3_errmsg(conn->handle)); return APR_EGENERAL; } sqlite3_busy_timeout(conn->handle, 300000); do { ret = sqlite3_exec(conn->handle, 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) { conn->errmsg = apr_psprintf(pool, "sqlite backend failed to create db schema on %s: %s", cache->dbfile, sqlite3_errmsg(conn->handle)); sqlite3_close(conn->handle); return APR_EGENERAL; } conn->readonly = 0; ret = _sqlite_set_pragmas(pool, cache, conn); if(ret != MAPCACHE_SUCCESS) { sqlite3_close(conn->handle); return APR_EGENERAL; } conn->prepared_statements = calloc(cache->n_prepared_statements,sizeof(sqlite3_stmt*)); conn->nstatements = cache->n_prepared_statements; return APR_SUCCESS; } static apr_status_t _sqlite_reslist_get_ro_connection(void **conn_, void *params, apr_pool_t *pool) { int ret; int flags; mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*) params; struct sqlite_conn *conn = apr_pcalloc(pool, sizeof (struct sqlite_conn)); *conn_ = conn; flags = SQLITE_OPEN_READONLY | SQLITE_OPEN_NOMUTEX; ret = sqlite3_open_v2(cache->dbfile, &conn->handle, flags, NULL); if (ret != SQLITE_OK) { return APR_EGENERAL; } sqlite3_busy_timeout(conn->handle, 300000); conn->readonly = 1; ret = _sqlite_set_pragmas(pool,cache, conn); if (ret != MAPCACHE_SUCCESS) { sqlite3_close(conn->handle); return APR_EGENERAL; } conn->prepared_statements = calloc(cache->n_prepared_statements,sizeof(sqlite3_stmt*)); conn->nstatements = cache->n_prepared_statements; return APR_SUCCESS; } static apr_status_t _sqlite_reslist_free_connection(void *conn_, void *params, apr_pool_t *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); return APR_SUCCESS; } static struct sqlite_conn* _sqlite_get_conn(mapcache_context *ctx, mapcache_tile* tile, int readonly) { apr_status_t rv; mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*) tile->tileset->cache; struct sqlite_conn *conn = NULL; apr_reslist_t *pool = NULL; apr_hash_t *pool_container; if (readonly) { pool_container = ro_connection_pools; } else { pool_container = rw_connection_pools; } if(!pool_container || NULL == (pool = apr_hash_get(pool_container,cache->cache.name, APR_HASH_KEY_STRING)) ) { #ifdef APR_HAS_THREADS if(ctx->threadlock) apr_thread_mutex_lock((apr_thread_mutex_t*)ctx->threadlock); #endif if(!ro_connection_pools) { ro_connection_pools = apr_hash_make(ctx->process_pool); rw_connection_pools = apr_hash_make(ctx->process_pool); } /* probably doesn't exist, unless the previous mutex locked us, so we check */ pool = apr_hash_get(ro_connection_pools,cache->cache.name, APR_HASH_KEY_STRING); if(!pool) { /* there where no existing connection pools, create them*/ rv = apr_reslist_create(&pool, 0 /* min */, 10 /* soft max */, 200 /* hard max */, 60*1000000 /*60 seconds, ttl*/, _sqlite_reslist_get_ro_connection, /* resource constructor */ _sqlite_reslist_free_connection, /* resource destructor */ cache, ctx->process_pool); if(rv != APR_SUCCESS) { ctx->set_error(ctx,500,"failed to create bdb ro connection pool"); #ifdef APR_HAS_THREADS if(ctx->threadlock) apr_thread_mutex_unlock((apr_thread_mutex_t*)ctx->threadlock); #endif return NULL; } apr_hash_set(ro_connection_pools,cache->cache.name,APR_HASH_KEY_STRING,pool); rv = apr_reslist_create(&pool, 0 /* min */, 1 /* soft max */, 1 /* hard max */, 60*1000000 /*60 seconds, ttl*/, _sqlite_reslist_get_rw_connection, /* resource constructor */ _sqlite_reslist_free_connection, /* resource destructor */ cache, ctx->process_pool); if(rv != APR_SUCCESS) { ctx->set_error(ctx,500,"failed to create bdb rw connection pool"); #ifdef APR_HAS_THREADS if(ctx->threadlock) apr_thread_mutex_unlock((apr_thread_mutex_t*)ctx->threadlock); #endif return NULL; } apr_hash_set(rw_connection_pools,cache->cache.name,APR_HASH_KEY_STRING,pool); } #ifdef APR_HAS_THREADS if(ctx->threadlock) apr_thread_mutex_unlock((apr_thread_mutex_t*)ctx->threadlock); #endif if(readonly) pool = apr_hash_get(ro_connection_pools,cache->cache.name, APR_HASH_KEY_STRING); else pool = apr_hash_get(rw_connection_pools,cache->cache.name, APR_HASH_KEY_STRING); assert(pool); } rv = apr_reslist_acquire(pool, (void **) &conn); if (rv != APR_SUCCESS) { ctx->set_error(ctx, 500, "failed to aquire connection to sqlite backend: %s", (conn && conn->errmsg)?conn->errmsg:"unknown error"); return NULL; } return conn; } static void _sqlite_release_conn(mapcache_context *ctx, mapcache_tile *tile, struct sqlite_conn *conn) { apr_reslist_t *pool; apr_hash_t *pool_container; if(conn->readonly) { pool_container = ro_connection_pools; } else { pool_container = rw_connection_pools; } pool = apr_hash_get(pool_container,tile->tileset->cache->name, APR_HASH_KEY_STRING); if (GC_HAS_ERROR(ctx)) { apr_reslist_invalidate(pool, (void*) conn); } else { apr_reslist_release(pool, (void*) conn); } } /** * \brief apply appropriate tile properties to the sqlite statement */ static void _bind_sqlite_params(mapcache_context *ctx, void *vstmt, mapcache_tile *tile) { sqlite3_stmt *stmt = vstmt; int paramidx; /* tile->x */ paramidx = sqlite3_bind_parameter_index(stmt, ":x"); if (paramidx) sqlite3_bind_int(stmt, paramidx, tile->x); /* tile->y */ paramidx = sqlite3_bind_parameter_index(stmt, ":y"); if (paramidx) sqlite3_bind_int(stmt, paramidx, tile->y); /* tile->y */ paramidx = sqlite3_bind_parameter_index(stmt, ":z"); if (paramidx) sqlite3_bind_int(stmt, paramidx, tile->z); /* eventual dimensions */ paramidx = sqlite3_bind_parameter_index(stmt, ":dim"); if (paramidx) { if (tile->dimensions) { char *dim = mapcache_util_get_tile_dimkey(ctx, tile, NULL, NULL); sqlite3_bind_text(stmt, paramidx, dim, -1, SQLITE_STATIC); } else { sqlite3_bind_text(stmt, paramidx, "", -1, SQLITE_STATIC); } } /* grid */ paramidx = sqlite3_bind_parameter_index(stmt, ":grid"); if (paramidx) sqlite3_bind_text(stmt, paramidx, tile->grid_link->grid->name, -1, SQLITE_STATIC); /* tileset */ paramidx = sqlite3_bind_parameter_index(stmt, ":tileset"); if (paramidx) sqlite3_bind_text(stmt, paramidx, tile->tileset->name, -1, SQLITE_STATIC); /* tile blob data */ paramidx = sqlite3_bind_parameter_index(stmt, ":data"); if (paramidx) { int written = 0; if(((mapcache_cache_sqlite*)tile->tileset->cache)->detect_blank && tile->grid_link->grid->tile_sx == 256 && tile->grid_link->grid->tile_sy == 256) { if(!tile->raw_image) { tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data); GC_CHECK_ERROR(ctx); } if(mapcache_image_blank_color(tile->raw_image) != MAPCACHE_FALSE) { char *buf = apr_palloc(ctx->pool, 5* sizeof(char)); buf[0] = '#'; memcpy(buf+1,tile->raw_image->data,4); written = 1; sqlite3_bind_blob(stmt, paramidx, buf, 5, SQLITE_STATIC); } } if(!written) { if (!tile->encoded_data) { tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format); GC_CHECK_ERROR(ctx); } if (tile->encoded_data && tile->encoded_data->size) { sqlite3_bind_blob(stmt, paramidx, tile->encoded_data->buf, tile->encoded_data->size, SQLITE_STATIC); } else { sqlite3_bind_text(stmt, paramidx, "", -1, SQLITE_STATIC); } } } } static void _bind_mbtiles_params(mapcache_context *ctx, void *vstmt, 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_tile *tile) { mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*) tile->tileset->cache; struct sqlite_conn *conn = _sqlite_get_conn(ctx, tile, 1); sqlite3_stmt *stmt; int ret; if (GC_HAS_ERROR(ctx)) { if(conn) _sqlite_release_conn(ctx, tile, conn); 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; } 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, 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); _sqlite_release_conn(ctx, tile, conn); return ret; } static void _mapcache_cache_sqlite_delete(mapcache_context *ctx, mapcache_tile *tile) { mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*) tile->tileset->cache; struct sqlite_conn *conn = _sqlite_get_conn(ctx, tile, 0); sqlite3_stmt *stmt = conn->prepared_statements[SQLITE_DEL_TILE_STMT_IDX]; int ret; if (GC_HAS_ERROR(ctx)) { _sqlite_release_conn(ctx, tile, conn); return; } 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, 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); _sqlite_release_conn(ctx, tile, conn); } static void _mapcache_cache_mbtiles_delete(mapcache_context *ctx, mapcache_tile *tile) { mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*) tile->tileset->cache; struct sqlite_conn *conn = _sqlite_get_conn(ctx, tile, 0); sqlite3_stmt *stmt1,*stmt2,*stmt3; int ret; const char *tile_id; size_t tile_id_size; if (GC_HAS_ERROR(ctx)) { _sqlite_release_conn(ctx, tile, conn); return; } 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, 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); _sqlite_release_conn(ctx, tile, conn); return; } } while (ret == SQLITE_BUSY || ret == SQLITE_LOCKED); if (ret == SQLITE_DONE) { /* tile does not exist, ignore */ sqlite3_reset(stmt1); _sqlite_release_conn(ctx, tile, conn); 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, 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); _sqlite_release_conn(ctx, tile, conn); 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); _sqlite_release_conn(ctx, tile, conn); return; } } sqlite3_reset(stmt1); sqlite3_reset(stmt2); sqlite3_reset(stmt3); _sqlite_release_conn(ctx, tile, conn); } static void _single_mbtile_set(mapcache_context *ctx, mapcache_tile *tile, struct sqlite_conn *conn) { sqlite3_stmt *stmt1,*stmt2; mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*)tile->tileset->cache; int ret; if(!tile->raw_image) { tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data); GC_CHECK_ERROR(ctx); } if(mapcache_image_blank_color(tile->raw_image) != MAPCACHE_FALSE) { stmt1 = conn->prepared_statements[MBTILES_SET_EMPTY_TILE_STMT1_IDX]; stmt2 = conn->prepared_statements[MBTILES_SET_EMPTY_TILE_STMT2_IDX]; if(!stmt1) { sqlite3_prepare(conn->handle, "insert or ignore into images(tile_id,tile_data) values (:color,:data);", -1, &conn->prepared_statements[MBTILES_SET_EMPTY_TILE_STMT1_IDX], NULL); sqlite3_prepare(conn->handle, "insert or replace into map(tile_column,tile_row,zoom_level,tile_id) values (:x,:y,:z,:color);", -1, &conn->prepared_statements[MBTILES_SET_EMPTY_TILE_STMT2_IDX], NULL); stmt1 = conn->prepared_statements[MBTILES_SET_EMPTY_TILE_STMT1_IDX]; stmt2 = conn->prepared_statements[MBTILES_SET_EMPTY_TILE_STMT2_IDX]; } cache->bind_stmt(ctx, stmt1, tile); cache->bind_stmt(ctx, stmt2, tile); } else { stmt1 = conn->prepared_statements[MBTILES_SET_TILE_STMT1_IDX]; stmt2 = conn->prepared_statements[MBTILES_SET_TILE_STMT2_IDX]; if(!stmt1) { sqlite3_prepare(conn->handle, "insert or replace into images(tile_id,tile_data) values (:key,:data);", -1, &conn->prepared_statements[MBTILES_SET_TILE_STMT1_IDX], NULL); sqlite3_prepare(conn->handle, "insert or replace into map(tile_column,tile_row,zoom_level,tile_id) values (:x,:y,:z,:key);", -1, &conn->prepared_statements[MBTILES_SET_TILE_STMT2_IDX], NULL); stmt1 = conn->prepared_statements[MBTILES_SET_TILE_STMT1_IDX]; stmt2 = conn->prepared_statements[MBTILES_SET_TILE_STMT2_IDX]; } cache->bind_stmt(ctx, stmt1, tile); cache->bind_stmt(ctx, stmt2, tile); } do { ret = sqlite3_step(stmt1); if (ret != SQLITE_DONE && ret != SQLITE_ROW && ret != SQLITE_BUSY && ret != SQLITE_LOCKED) { ctx->set_error(ctx, 500, "mbtiles backend failed on image set: %s (%d)", sqlite3_errmsg(conn->handle), ret); break; } if (ret == SQLITE_BUSY) { sqlite3_reset(stmt1); } } while (ret == SQLITE_BUSY || ret == SQLITE_LOCKED); if(ret == SQLITE_DONE) { do { ret = sqlite3_step(stmt2); if (ret != SQLITE_DONE && ret != SQLITE_ROW && ret != SQLITE_BUSY && ret != SQLITE_LOCKED) { ctx->set_error(ctx, 500, "mbtiles backend failed on map set: %s (%d)", sqlite3_errmsg(conn->handle), ret); break; } if (ret == SQLITE_BUSY) { sqlite3_reset(stmt2); } } while (ret == SQLITE_BUSY || ret == SQLITE_LOCKED); } sqlite3_reset(stmt1); sqlite3_reset(stmt2); } static int _mapcache_cache_sqlite_get(mapcache_context *ctx, mapcache_tile *tile) { mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*) tile->tileset->cache; struct sqlite_conn *conn; sqlite3_stmt *stmt; int ret; conn = _sqlite_get_conn(ctx, tile, 1); if (GC_HAS_ERROR(ctx)) { if(conn) _sqlite_release_conn(ctx, tile, conn); if(tile->tileset->read_only || !tile->tileset->source) { return MAPCACHE_FAILURE; } else { /* not an error in this case, as the db file may not have been created yet */ ctx->clear_errors(ctx); return MAPCACHE_CACHE_MISS; } } 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, 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); _sqlite_release_conn(ctx, tile, conn); return MAPCACHE_FAILURE; } } while (ret == SQLITE_BUSY || ret == SQLITE_LOCKED); if (ret == SQLITE_DONE) { sqlite3_reset(stmt); _sqlite_release_conn(ctx, tile, conn); 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,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); _sqlite_release_conn(ctx, tile, conn); return MAPCACHE_SUCCESS; } } static void _single_sqlitetile_set(mapcache_context *ctx, mapcache_tile *tile, struct sqlite_conn *conn) { mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*) tile->tileset->cache; 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, 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_tile *tile) { struct sqlite_conn *conn = _sqlite_get_conn(ctx, tile, 0); GC_CHECK_ERROR(ctx); sqlite3_exec(conn->handle, "BEGIN TRANSACTION", 0, 0, 0); _single_sqlitetile_set(ctx,tile,conn); if (GC_HAS_ERROR(ctx)) { sqlite3_exec(conn->handle, "ROLLBACK TRANSACTION", 0, 0, 0); } else { sqlite3_exec(conn->handle, "END TRANSACTION", 0, 0, 0); } _sqlite_release_conn(ctx, tile, conn); } static void _mapcache_cache_sqlite_multi_set(mapcache_context *ctx, mapcache_tile *tiles, int ntiles) { struct sqlite_conn *conn = _sqlite_get_conn(ctx, &tiles[0], 0); int i; GC_CHECK_ERROR(ctx); sqlite3_exec(conn->handle, "BEGIN TRANSACTION", 0, 0, 0); for (i = 0; i < ntiles; i++) { mapcache_tile *tile = &tiles[i]; _single_sqlitetile_set(ctx,tile,conn); if(GC_HAS_ERROR(ctx)) break; } if (GC_HAS_ERROR(ctx)) { sqlite3_exec(conn->handle, "ROLLBACK TRANSACTION", 0, 0, 0); } else { sqlite3_exec(conn->handle, "END TRANSACTION", 0, 0, 0); } _sqlite_release_conn(ctx, &tiles[0], conn); } static void _mapcache_cache_mbtiles_set(mapcache_context *ctx, mapcache_tile *tile) { struct sqlite_conn *conn = _sqlite_get_conn(ctx, tile, 0); GC_CHECK_ERROR(ctx); if(!tile->raw_image) { tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data); if(GC_HAS_ERROR(ctx)) { _sqlite_release_conn(ctx, tile, conn); return; } } sqlite3_exec(conn->handle, "BEGIN TRANSACTION", 0, 0, 0); _single_mbtile_set(ctx,tile,conn); if (GC_HAS_ERROR(ctx)) { sqlite3_exec(conn->handle, "ROLLBACK TRANSACTION", 0, 0, 0); } else { sqlite3_exec(conn->handle, "END TRANSACTION", 0, 0, 0); } _sqlite_release_conn(ctx, tile, conn); } static void _mapcache_cache_mbtiles_multi_set(mapcache_context *ctx, mapcache_tile *tiles, int ntiles) { struct sqlite_conn *conn = NULL; int i; /* decode/encode image data before going into the sqlite write lock */ for (i = 0; i < ntiles; i++) { mapcache_tile *tile = &tiles[i]; if(!tile->raw_image) { tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data); GC_CHECK_ERROR(ctx); } /* only encode to image format if tile is not blank */ if (mapcache_image_blank_color(tile->raw_image) != MAPCACHE_TRUE && !tile->encoded_data) { tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format); GC_CHECK_ERROR(ctx); } } conn = _sqlite_get_conn(ctx, &tiles[0], 0); sqlite3_exec(conn->handle, "BEGIN TRANSACTION", 0, 0, 0); for (i = 0; i < ntiles; i++) { mapcache_tile *tile = &tiles[i]; _single_mbtile_set(ctx,tile,conn); if(GC_HAS_ERROR(ctx)) break; } if (GC_HAS_ERROR(ctx)) { sqlite3_exec(conn->handle, "ROLLBACK TRANSACTION", 0, 0, 0); } else { sqlite3_exec(conn->handle, "END TRANSACTION", 0, 0, 0); } _sqlite_release_conn(ctx, &tiles[0], conn); } static void _mapcache_cache_sqlite_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *cache, mapcache_cfg *config) { ezxml_t cur_node; mapcache_cache_sqlite *dcache; sqlite3_initialize(); sqlite3_config(SQLITE_CONFIG_MULTITHREAD); dcache = (mapcache_cache_sqlite*) cache; 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 "); return; } if ((cur_node = ezxml_child(node, "dbfile")) != NULL) { dcache->dbfile = apr_pstrdup(ctx->pool, cur_node->txt); } dcache->detect_blank = 0; if ((cur_node = ezxml_child(node, "detect_blank")) != NULL) { if(!strcasecmp(cur_node->txt,"true")) { dcache->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) { dcache->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(dcache->pragmas,name,cur_node->txt); cur_node = cur_node->next; } } if (!dcache->dbfile) { ctx->set_error(ctx, 500, "sqlite cache \"%s\" is missing entry", cache->name); return; } } /** * \private \memberof mapcache_cache_sqlite */ static void _mapcache_cache_sqlite_configuration_post_config(mapcache_context *ctx, mapcache_cache *cache, mapcache_cfg *cfg) { } /** * \private \memberof mapcache_cache_sqlite */ static void _mapcache_cache_mbtiles_configuration_post_config(mapcache_context *ctx, mapcache_cache *cache, mapcache_cfg *cfg) { /* check that only one tileset/grid references this cache, as mbtiles does not support multiple tilesets/grids per cache */ 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); } } /** * \brief creates and initializes a mapcache_sqlite_cache */ 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; 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-rel-1-2-1/lib/cache_tiff.c000066400000000000000000000746211226152023600170300ustar00rootroot00000000000000/****************************************************************************** * $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_tile *tile, char **path) { mapcache_cache_tiff *dcache = (mapcache_cache_tiff*)tile->tileset->cache; *path = dcache->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,dcache->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,dcache->div_x_fmt,tile->x/dcache->count_x)); 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,"{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)); /* * 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,dcache->x_fmt,tile->x/dcache->count_x*dcache->count_x)); 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)); 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_y)); if(!*path) { ctx->set_error(ctx,500, "failed to allocate tile key"); } } #ifdef DEBUG static void check_tiff_format(mapcache_context *ctx, mapcache_tile *tile, TIFF *hTIFF, const char *filename) { mapcache_cache_tiff *dcache = (mapcache_cache_tiff*)tile->tileset->cache; 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(dcache->count_x, level->maxx); ntilesy = MAPCACHE_MIN(dcache->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_tile *tile) { char *filename; TIFF *hTIFF; mapcache_cache_tiff *dcache; _mapcache_cache_tiff_tile_key(ctx, tile, &filename); dcache = (mapcache_cache_tiff*)tile->tileset->cache; 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,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(dcache->count_x, level->maxx); ntilesy = MAPCACHE_MIN(dcache->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_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_tile *tile) { char *filename; TIFF *hTIFF = NULL; int rv; mapcache_cache_tiff *dcache; _mapcache_cache_tiff_tile_key(ctx, tile, &filename); dcache = (mapcache_cache_tiff*)tile->tileset->cache; 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,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(dcache->count_x, level->maxx); ntilesy = MAPCACHE_MIN(dcache->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_tile *tile) { #ifdef USE_TIFF_WRITE char *filename; TIFF *hTIFF = NULL; int rv; int create; char errmsg[120]; mapcache_cache_tiff *dcache; 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 */ _mapcache_cache_tiff_tile_key(ctx, tile, &filename); dcache = (mapcache_cache_tiff*)tile->tileset->cache; format = (mapcache_image_format_jpeg*) dcache->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,filename) == 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(dcache->count_x, level->maxx); ntilesy = MAPCACHE_MIN(dcache->count_y, level->maxy); if(create) { #ifdef USE_GEOTIFF double adfPixelScale[3], adfTiePoints[6], bbox[4]; 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 / dcache->count_x)*(dcache->count_x); y = (tile->y / dcache->count_y)*(dcache->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[0]; adfTiePoints[4] = bbox[3]; 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,filename); #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 *cache, mapcache_cfg *config) { ezxml_t cur_node; mapcache_cache_tiff *dcache = (mapcache_cache_tiff*)cache; ezxml_t xcount; ezxml_t ycount; ezxml_t xformat; char * format_name; mapcache_image_format *pformat; if ((cur_node = ezxml_child(node,"template")) != NULL) { char *fmt; dcache->filename_template = apr_pstrdup(ctx->pool,cur_node->txt); fmt = (char*)ezxml_attr(cur_node,"x_fmt"); if(fmt && *fmt) { dcache->x_fmt = apr_pstrdup(ctx->pool,fmt); } fmt = (char*)ezxml_attr(cur_node,"y_fmt"); if(fmt && *fmt) { dcache->y_fmt = apr_pstrdup(ctx->pool,fmt); } fmt = (char*)ezxml_attr(cur_node,"z_fmt"); if(fmt && *fmt) { dcache->z_fmt = apr_pstrdup(ctx->pool,fmt); } fmt = (char*)ezxml_attr(cur_node,"inv_x_fmt"); if(fmt && *fmt) { dcache->inv_x_fmt = apr_pstrdup(ctx->pool,fmt); } fmt = (char*)ezxml_attr(cur_node,"inv_y_fmt"); if(fmt && *fmt) { dcache->inv_y_fmt = apr_pstrdup(ctx->pool,fmt); } fmt = (char*)ezxml_attr(cur_node,"div_x_fmt"); if(fmt && *fmt) { dcache->div_x_fmt = apr_pstrdup(ctx->pool,fmt); } fmt = (char*)ezxml_attr(cur_node,"div_y_fmt"); if(fmt && *fmt) { dcache->div_y_fmt = apr_pstrdup(ctx->pool,fmt); } fmt = (char*)ezxml_attr(cur_node,"inv_div_x_fmt"); if(fmt && *fmt) { dcache->inv_div_x_fmt = apr_pstrdup(ctx->pool,fmt); } fmt = (char*)ezxml_attr(cur_node,"inv_div_y_fmt"); if(fmt && *fmt) { dcache->inv_div_y_fmt = apr_pstrdup(ctx->pool,fmt); } } xcount = ezxml_child(node,"xcount"); if(xcount && xcount->txt && *xcount->txt) { char *endptr; dcache->count_x = (int)strtol(xcount->txt,&endptr,10); if(*endptr != 0) { ctx->set_error(ctx,400,"failed to parse xcount value %s for tiff cache %s", xcount->txt,cache->name); return; } } ycount = ezxml_child(node,"ycount"); if(ycount && ycount->txt && *ycount->txt) { char *endptr; dcache->count_y = (int)strtol(ycount->txt,&endptr,10); if(*endptr != 0) { ctx->set_error(ctx,400,"failed to parse ycount value %s for tiff cache %s", ycount->txt,cache->name); return; } } xformat = ezxml_child(node,"format"); if(xformat && xformat->txt && *xformat->txt) { format_name = xformat->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", cache->name, format_name); return; } if(pformat->type != GC_JPEG) { ctx->set_error(ctx,500,"TIFF cache %s can only reference a JPEG image format", cache->name); return; } dcache->format = (mapcache_image_format_jpeg*)pformat; } /** * \private \memberof mapcache_cache_tiff */ static void _mapcache_cache_tiff_configuration_post_config(mapcache_context *ctx, mapcache_cache *cache, mapcache_cfg *cfg) { mapcache_cache_tiff *dcache = (mapcache_cache_tiff*)cache; /* check all required parameters are configured */ if((!dcache->filename_template || !strlen(dcache->filename_template))) { ctx->set_error(ctx, 400, "tiff cache %s has no template pattern",dcache->cache.name); return; } if(dcache->count_x <= 0 || dcache->count_y <= 0) { ctx->set_error(ctx, 400, "tiff cache %s has invalid count (%d,%d)",dcache->count_x,dcache->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-rel-1-2-1/lib/cache_tokyocabinet.c000066400000000000000000000176231226152023600205720ustar00rootroot00000000000000/****************************************************************************** * $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-rel-1-2-1/lib/configuration.c000066400000000000000000000276261226152023600176270ustar00rootroot00000000000000/****************************************************************************** * $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) { apr_dir_t *lockdir; apr_status_t rv; char errmsg[120]; char *url; mapcache_configuration_parse_xml(ctx,filename,config); GC_CHECK_ERROR(ctx); if(!config->lockdir || !strlen(config->lockdir)) { config->lockdir = apr_pstrdup(ctx->pool, "/tmp"); } rv = apr_dir_open(&lockdir,config->lockdir,ctx->pool); if(rv != APR_SUCCESS) { ctx->set_error(ctx,500, "failed to open lock directory %s: %s" ,config->lockdir,apr_strerror(rv,errmsg,120)); return; } /* only remove lockfiles if we're not in cgi mode */ if(!cgi) { apr_finfo_t finfo; while ((apr_dir_read(&finfo, APR_FINFO_DIRENT|APR_FINFO_TYPE|APR_FINFO_NAME, lockdir)) == APR_SUCCESS) { if(finfo.filetype == APR_REG) { if(!strncmp(finfo.name, MAPCACHE_LOCKFILE_PREFIX, strlen(MAPCACHE_LOCKFILE_PREFIX))) { ctx->log(ctx,MAPCACHE_WARN,"found old lockfile %s/%s, deleting it",config->lockdir, finfo.name); rv = apr_file_remove(apr_psprintf(ctx->pool,"%s/%s",config->lockdir, finfo.name),ctx->pool); if(rv != APR_SUCCESS) { ctx->set_error(ctx,500, "failed to remove lockfile %s: %s",finfo.name,apr_strerror(rv,errmsg,120)); return; } } } } } apr_dir_close(lockdir); /* 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"); /* default retry interval is 1/100th of a second, i.e. 10000 microseconds */ cfg->lock_retry_interval = 10000; 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-rel-1-2-1/lib/configuration_xml.c000066400000000000000000001221121226152023600204710ustar00rootroot00000000000000/****************************************************************************** * $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 *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,"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(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 { 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,"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 { 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; 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; } } 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; } 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,"lock_dir")) != NULL) { config->lockdir = apr_pstrdup(ctx->pool, node->txt); } else { config->lockdir = apr_pstrdup(ctx->pool,"/tmp"); } if((node = ezxml_child(doc,"lock_retry")) != NULL) { char *endptr; config->lock_retry_interval = (unsigned int)strtol(node->txt,&endptr,10); if(*endptr != 0 || config->lock_retry_interval < 0) { ctx->set_error(ctx, 400, "failed to parse lock_retry microseconds \"%s\". Expecting a positive integer", node->txt); return; } } 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-rel-1-2-1/lib/core.c000066400000000000000000000552661226152023600157110ustar00rootroot00000000000000/****************************************************************************** * $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); mapcache_prefetch_tiles(ctx,req_tile->tiles,req_tile->ntiles); if(GC_HAS_ERROR(ctx)) return NULL; /* loop through tiles, and eventually merge them vertically together */ for(i=0; 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->format) { format = req_tile->format; } else { format = req_tile->tiles[0]->tileset->format; if(!format) { format = ctx->config->default_image_format; /* this one is always defined */ } } response->data = format->write(ctx, base, format); if(GC_HAS_ERROR(ctx)) { return NULL; } } else { #ifdef DEBUG if(!is_empty) { ctx->set_error(ctx,500,"BUG: no image data to encode, but tile not marked as empty"); return NULL; } #endif unsigned char empty[5] = {'#',0,0,0,0}; response->data = mapcache_empty_png_decode(ctx,empty,&is_empty); /* is_empty is unchanged and left to 1 */ format = mapcache_configuration_get_image_format(ctx->config,"PNG8"); } } /* compute the content-type */ mapcache_image_format_type t = mapcache_imageio_header_sniff(ctx,response->data); if(t == GC_PNG) apr_table_set(response->headers,"Content-Type","image/png"); else if(t == GC_JPEG) apr_table_set(response->headers,"Content-Type","image/jpeg"); /* compute expiry headers */ if(expires) { apr_time_t now = apr_time_now(); apr_time_t additional = apr_time_from_sec(expires); apr_time_t texpires = now + additional; apr_table_set(response->headers, "Cache-Control",apr_psprintf(ctx->pool, "max-age=%d", expires)); timestr = apr_palloc(ctx->pool, APR_RFC822_DATE_LEN); apr_rfc822_date(timestr, texpires); apr_table_setn(response->headers, "Expires", timestr); } return response; } mapcache_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)); for(i=0; itileset,maps[i]->grid_link, &maps[i]->extent, maps[i]->width, maps[i]->height, &(nmaptiles[i]), &(maptiles[i])); 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,maps[i]->grid_link, &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->getmap_format; /* always defined, defaults to JPEG */ response->data = format->write(ctx,basemap->raw_image,format); if(GC_HAS_ERROR(ctx)) { return NULL; } } else { /* this case happens when we have a forward strategy for a single tileset */ #ifdef DEBUG if(!basemap->encoded_data) { ctx->set_error(ctx,500,"###BUG### core_get_map failed with null encoded_data"); return NULL; } #endif response->data = basemap->encoded_data; } /* compute the content-type */ if(format && format->mime_type) { apr_table_set(response->headers,"Content-Type",format->mime_type); } else { mapcache_image_format_type t = mapcache_imageio_header_sniff(ctx,response->data); if(t == GC_PNG) apr_table_set(response->headers,"Content-Type","image/png"); else if(t == GC_JPEG) apr_table_set(response->headers,"Content-Type","image/jpeg"); } /* compute expiry headers */ if(basemap->expires) { apr_time_t now = apr_time_now(); apr_time_t additional = apr_time_from_sec(basemap->expires); apr_time_t texpires = now + additional; apr_table_set(response->headers, "Cache-Control", apr_psprintf(ctx->pool, "max-age=%d", basemap->expires)); timestr = apr_palloc(ctx->pool, APR_RFC822_DATE_LEN); apr_rfc822_date(timestr, texpires); apr_table_setn(response->headers, "Expires", timestr); } response->mtime = basemap->mtime; return response; } mapcache_http_response *mapcache_core_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 = req_proxy->http; if(req_proxy->pathinfo) { http = mapcache_http_clone(ctx,http); 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); } mapcache_http_do_request_with_params(ctx,http,req_proxy->params,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-rel-1-2-1/lib/dimension.c000066400000000000000000000555531226152023600167450ustar00rootroot00000000000000/****************************************************************************** * $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 #include #ifdef APR_HAS_THREADS #include #endif #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 const char** _mapcache_dimension_intervals_print(mapcache_context *ctx, mapcache_dimension *dim) { mapcache_dimension_intervals *dimension = (mapcache_dimension_intervals*)dim; const char **ret = (const char**)apr_pcalloc(ctx->pool,(dimension->nintervals+1)*sizeof(const char*)); int i; for(i=0; inintervals; i++) { mapcache_interval *interval = &dimension->intervals[i]; ret[i] = apr_psprintf(ctx->pool,"%g/%g/%g",interval->start,interval->end,interval->resolution); } ret[i]=NULL; 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 const char** _mapcache_dimension_regex_print(mapcache_context *ctx, mapcache_dimension *dim) { mapcache_dimension_regex *dimension = (mapcache_dimension_regex*)dim; const char **ret = (const char**)apr_pcalloc(ctx->pool,2*sizeof(const char*)); ret[0]=dimension->regex_string; ret[1]=NULL; 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 const char** _mapcache_dimension_values_print(mapcache_context *ctx, mapcache_dimension *dim) { mapcache_dimension_values *dimension = (mapcache_dimension_values*)dim; const char **ret = (const char**)apr_pcalloc(ctx->pool,(dimension->nvalues+1)*sizeof(const char*)); int i; for(i=0; invalues; i++) { ret[i] = dimension->values[i]; } ret[i]=NULL; 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 static apr_hash_t *time_connection_pools = NULL; struct sqlite_time_conn { sqlite3 *handle; sqlite3_stmt *prepared_statement; char *errmsg; }; static apr_status_t _sqlite_time_reslist_get_ro_connection(void **conn_, void *params, apr_pool_t *pool) { int ret; int flags; mapcache_timedimension_sqlite *dim = (mapcache_timedimension_sqlite*) params; struct sqlite_time_conn *conn = apr_pcalloc(pool, sizeof (struct sqlite_time_conn)); *conn_ = conn; flags = SQLITE_OPEN_READONLY | SQLITE_OPEN_NOMUTEX; ret = sqlite3_open_v2(dim->dbfile, &conn->handle, flags, NULL); if (ret != SQLITE_OK) { return APR_EGENERAL; } sqlite3_busy_timeout(conn->handle, 300000); return APR_SUCCESS; } static apr_status_t _sqlite_time_reslist_free_connection(void *conn_, void *params, apr_pool_t *pool) { struct sqlite_time_conn *conn = (struct sqlite_time_conn*) conn_; if(conn->prepared_statement) { sqlite3_finalize(conn->prepared_statement); } sqlite3_close(conn->handle); return APR_SUCCESS; } static struct sqlite_time_conn* _sqlite_time_get_conn(mapcache_context *ctx, mapcache_timedimension_sqlite *dim) { apr_status_t rv; struct sqlite_time_conn *conn = NULL; apr_reslist_t *pool = NULL; if(!time_connection_pools || NULL == (pool = apr_hash_get(time_connection_pools,dim->timedimension.key, APR_HASH_KEY_STRING)) ) { #ifdef APR_HAS_THREADS if(ctx->threadlock) apr_thread_mutex_lock((apr_thread_mutex_t*)ctx->threadlock); #endif if(!time_connection_pools) { time_connection_pools = apr_hash_make(ctx->process_pool); } /* probably doesn't exist, unless the previous mutex locked us, so we check */ pool = apr_hash_get(time_connection_pools,dim->timedimension.key, APR_HASH_KEY_STRING); if(!pool) { /* there where no existing connection pools, create them*/ rv = apr_reslist_create(&pool, 0 /* min */, 10 /* soft max */, 200 /* hard max */, 60*1000000 /*60 seconds, ttl*/, _sqlite_time_reslist_get_ro_connection, /* resource constructor */ _sqlite_time_reslist_free_connection, /* resource destructor */ dim, ctx->process_pool); if(rv != APR_SUCCESS) { ctx->set_error(ctx,500,"failed to create sqlite time connection pool"); #ifdef APR_HAS_THREADS if(ctx->threadlock) apr_thread_mutex_unlock((apr_thread_mutex_t*)ctx->threadlock); #endif return NULL; } apr_hash_set(time_connection_pools,dim->timedimension.key,APR_HASH_KEY_STRING,pool); } #ifdef APR_HAS_THREADS if(ctx->threadlock) apr_thread_mutex_unlock((apr_thread_mutex_t*)ctx->threadlock); #endif pool = apr_hash_get(time_connection_pools,dim->timedimension.key, APR_HASH_KEY_STRING); assert(pool); } rv = apr_reslist_acquire(pool, (void **) &conn); if (rv != APR_SUCCESS) { ctx->set_error(ctx, 500, "failed to aquire connection to time dimension sqlite backend: %s", (conn && conn->errmsg)?conn->errmsg:"unknown error"); return NULL; } return conn; } static void _sqlite_time_release_conn(mapcache_context *ctx, mapcache_timedimension_sqlite *sdim, struct sqlite_time_conn *conn) { apr_reslist_t *pool; pool = apr_hash_get(time_connection_pools,sdim->timedimension.key, APR_HASH_KEY_STRING); if (GC_HAS_ERROR(ctx)) { apr_reslist_invalidate(pool, (void*) conn); } else { apr_reslist_release(pool, (void*) conn); } } 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; } } } if(extent) { paramidx = sqlite3_bind_parameter_index(stmt, ":minx"); if (paramidx) { ret = sqlite3_bind_double(stmt, paramidx, extent->minx); 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->miny); 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->maxx); 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->maxy); 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; sqlite3_stmt *stmt; apr_array_header_t *time_ids = NULL; struct sqlite_time_conn *conn = _sqlite_time_get_conn(ctx, sdim); if (GC_HAS_ERROR(ctx)) { if(conn) _sqlite_time_release_conn(ctx, sdim, conn); return NULL; } stmt = conn->prepared_statement; if(!stmt) { ret = sqlite3_prepare_v2(conn->handle, sdim->query, -1, &conn->prepared_statement, NULL); if(ret != SQLITE_OK) { ctx->set_error(ctx, 500, "time sqlite backend failed on preparing query: %s", sqlite3_errmsg(conn->handle)); _sqlite_time_release_conn(ctx, sdim, conn); return NULL; } stmt = conn->prepared_statement; } _bind_sqlite_timedimension_params(ctx,stmt,conn->handle,tileset,grid,extent,start,end); if(GC_HAS_ERROR(ctx)) { sqlite3_reset(stmt); _sqlite_time_release_conn(ctx, sdim, conn); return NULL; } time_ids = apr_array_make(ctx->pool,0,sizeof(char*)); 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 timedimension query : %s (%d)", sqlite3_errmsg(conn->handle), ret); sqlite3_reset(stmt); _sqlite_time_release_conn(ctx, sdim, conn); return NULL; } if(ret == SQLITE_ROW) { const char* time_id = (const char*) sqlite3_column_text(stmt, 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(stmt); _sqlite_time_release_conn(ctx, sdim, conn); 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 == '/') { /* we have a second (end) time */ valueptr++; 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 */ } #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-rel-1-2-1/lib/ezxml.c000066400000000000000000001106141226152023600161050ustar00rootroot00000000000000/* 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-rel-1-2-1/lib/services.c000066400000000000000000000056261226152023600165770ustar00rootroot00000000000000/****************************************************************************** * $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-rel-1-2-1/lib/source.c000066400000000000000000000034431226152023600162470ustar00rootroot00000000000000/****************************************************************************** * $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-rel-1-2-1/lib/source_dummy.c000066400000000000000000000067061226152023600174670ustar00rootroot00000000000000/****************************************************************************** * $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-rel-1-2-1/lib/source_gdal.c000066400000000000000000000253261226152023600172420ustar00rootroot00000000000000/****************************************************************************** * $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-rel-1-2-1/lib/source_mapserver.c000066400000000000000000000236141226152023600203350ustar00rootroot00000000000000/****************************************************************************** * $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"); } #endif /* vim: ts=2 sts=2 et sw=2 */ mapcache-rel-1-2-1/lib/source_wms.c000066400000000000000000000215101226152023600171300ustar00rootroot00000000000000/****************************************************************************** * $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; 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); apr_table_setn(params,entry.key,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); mapcache_http_do_request_with_params(ctx,wms->http,params,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_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); mapcache_http_do_request_with_params(ctx,wms->http,params,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-rel-1-2-1/lib/strptime.c000066400000000000000000000242321226152023600166150ustar00rootroot00000000000000/* * Copyright (c) 1999 Kungliga Tekniska Hgskolan * (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-rel-1-2-1/lib/tileset.c000066400000000000000000001003221226152023600164120ustar00rootroot00000000000000/****************************************************************************** * $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) { 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); mapcache_grid_get_closest_level(ctx,grid_link,resolution,&level); /* we don't want to assemble tiles that have already been reassembled from a lower level */ if(grid_link->outofzoom_strategy == MAPCACHE_OUTOFZOOM_REASSEMBLE && level > grid_link->max_cached_zoom) { level = grid_link->max_cached_zoom; } mapcache_grid_get_xy(ctx,grid_link->grid,bbox->minx,bbox->miny,level,&bl_x,&bl_y); mapcache_grid_get_xy(ctx,grid_link->grid,bbox->maxx,bbox->maxy,level,&tr_x,&tr_y); Mx = MAPCACHE_MAX(tr_x,bl_x); My = MAPCACHE_MAX(tr_y,bl_y); mx = MAPCACHE_MIN(tr_x,bl_x); my = MAPCACHE_MIN(tr_y,bl_y); *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, 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->tiles, mt->ntiles); } else { for(i=0; intiles; i++) { mapcache_tile *tile = &(mt->tiles[i]); mt->map.tileset->cache->tile_set(ctx, 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; 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 isLocked,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); 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; } /* the tile does not exist, we must take action before re-asking for it */ /* * 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, mapcache_tileset_metatile_resource_key(ctx,mt)); if(isLocked == MAPCACHE_TRUE) { /* no other thread is doing the rendering, do it ourselves */ #ifdef DEBUG ctx->log(ctx, MAPCACHE_DEBUG, "cache miss: 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); mapcache_unlock_resource(ctx, mapcache_tileset_metatile_resource_key(ctx,mt)); } GC_CHECK_ERROR(ctx); /* the previous step has successfully finished, we can now query the cache to return the tile content */ ret = tile->tileset->cache->tile_get(ctx, 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); 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); /* 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-rel-1-2-1/lib/util.c000066400000000000000000000254101226152023600157220ustar00rootroot00000000000000/****************************************************************************** * $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}; 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 *fmt; va_list args; va_start(args,msg); if(ctx->_errmsg) { fmt=apr_psprintf(ctx->pool,"%s\n%s",ctx->_errmsg,msg); } else { fmt=msg; ctx->_errcode = code; } ctx->_errmsg = apr_pvsprintf(ctx->pool,fmt,args); va_end(args); } void _mapcache_context_clear_error_default(mapcache_context *ctx) { ctx->_errcode = 0; ctx->_errmsg = NULL; if(ctx->exceptions) { apr_table_clear(ctx->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; } 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->process_pool = src->process_pool; } 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-rel-1-2-1/mapcache.xml000066400000000000000000000025461226152023600163230ustar00rootroot00000000000000 /tmp image/png basic http://vmap0.tiles.osgeo.org/wms/vmap0 vmap0 disk WGS84 g PNG 5 5 10 3600 JPEG assemble bilinear JPEG 4096 report /tmp mapcache-rel-1-2-1/mapcache.xml.sample000066400000000000000000001044561226152023600176060ustar00rootroot00000000000000 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 /tmp/foo/ /tmp/ {tileset}-{grid}-{dim}-{z}-{y}-{x}.{ext} 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 true info true mapcache-rel-1-2-1/mod_geocache.doxyfile000066400000000000000000002043141226152023600201770ustar00rootroot00000000000000# 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-rel-1-2-1/nginx/000077500000000000000000000000001226152023600151545ustar00rootroot00000000000000mapcache-rel-1-2-1/nginx/CMakeLists.txt000066400000000000000000000001461226152023600177150ustar00rootroot00000000000000 configure_file ( "${PROJECT_SOURCE_DIR}/nginx/config.in" "${PROJECT_BINARY_DIR}/nginx/config" ) mapcache-rel-1-2-1/nginx/README000066400000000000000000000045261226152023600160430ustar00rootroot00000000000000minimal 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-rel-1-2-1/nginx/config.in000066400000000000000000000005331226152023600167520ustar00rootroot00000000000000ngx_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-rel-1-2-1/nginx/nginx.conf000066400000000000000000000074121226152023600171520ustar00rootroot00000000000000 #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-rel-1-2-1/nginx/ngx_http_mapcache_module.c000066400000000000000000000244461226152023600223530ustar00rootroot00000000000000#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->process_pool = process_pool; 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); ctx->process_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; } 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-rel-1-2-1/nmake.opt000066400000000000000000000233771226152023600156640ustar00rootroot00000000000000 ######################################################################## # 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-rel-1-2-1/packaging/000077500000000000000000000000001226152023600157555ustar00rootroot00000000000000mapcache-rel-1-2-1/packaging/centos/000077500000000000000000000000001226152023600172505ustar00rootroot00000000000000mapcache-rel-1-2-1/packaging/centos/mapcache.spec000066400000000000000000000025721226152023600216730ustar00rootroot00000000000000Name: 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-rel-1-2-1/release-notes.sh000077500000000000000000000005131226152023600171350ustar00rootroot00000000000000#!/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-rel-1-2-1/static/000077500000000000000000000000001226152023600153205ustar00rootroot00000000000000mapcache-rel-1-2-1/static/bench_cached.png000066400000000000000000000556471226152023600204150ustar00rootroot00000000000000PNG  IHDRqgsRGB pHYs  tIME  ʺtEXtCommentCreated with GIMPW IDATxy\T0# . b," *䎖VPTzͬk{LSs駢-""$:+ FB a_z<9s;3~ysΑ"""[:,LDDD `"""01DDD&""bLDD&"""0DDD `"""b1L8;;oܸ744<{l˸YD&"Mrss[naY֍Hd,Ql2===q0UHRXBWWvѭ[7\nll흗WrrrLP(,-- /_Cr%,,Ll̜0a~Ν8&M<ŋ-H$m۶E=ZPPx{׮]ɓ  ˗8;;^7000ڵ믿oիYfC occ#Ɍ|7yŊ#Fhժ՗_~/_=߱cǀ /\Pk|~{ D!s̱}su}ҥ,ԦMm۶-_ɓ' ̟?O>[zѣGAx׻w}UWWWL\f\]]+|߮)RSS xŊ 竷7nx7=<<Μ9믿ى=))I&޽;!!ATرJ:99jٳ dggW򝜜~_վ}kS(ϟ5V=eFq `"HOOdǎd#GTӬY*0888##CWW7..NX^vC .=EUݻWUV_/W*<(]v 1112L?_ooov/?,,LlPrT= `mLJRRix\-ZdaRR833STٕ^,11166VT*Ɨeׯ駟?_~o6m\]]mllF9a7޾m_ңGLVC}D$77wȑC?;Sf'Qqq1ҿ/_..]TZ)sΝ;׷o_cc^z),,ŋ+r̙~Cz葓&4DRL@988dҍnXdD"d|ĉ ũKR1R󣣣'.`jjjooVv~2Vɴb+:UTTdmm} ]]]wssر[cdžkffZv~2 蔔׏1"""y^^^_{}U2H$害/W""jj'W;y掎366>zIJ/гgOvvvRt699ظJQB"%%-KKKc8ࢢr(GFFС\.޽X7z߹;w#G""";6dcc㴴4A>|ʕ{ZYYڵKP)QF9::TjK+*V9n?__BCx Z0oo-Zn@ZZ̙3=zdll,JNDDT>^RGgu .p%\ФMP o/e;_XIJ2DuM`K34Co$'|?ceH;zZ$XI@BAHt)8= Æc=Y4"j0iX xy 7pb׬YqFVLZ0_Ap löxp-aɽ4[N}wذa,K"c F{x2^{>Gqt-A56õz5QU-[E`Vbe?A{xn`ɓ'_xqѢEm۶xYR-_\PBh1DVX{CVSh_|EqqqzstLvرJA*Zi֬Y%_d088g(ج^Rzo/E[,|8so15bJήtcbbRTTex\-ZWRӾRqfffEX_XS05\ HXU!0c1[%|objdee 9rCBBFYutt VZ1@s0G[l-& J2ĉ嶇nuVFFƒ%KLLL$ILLL% W&05Pq81˱޷DoA 9ťK+Zg޼yAAA񑑑+VP*FFFf>~x||3gNVhU/<=j?\Uj(]"W_5սGxt',F9)6*9F⍵ p21>f)*|'B2*z SiP[ͷ\5.9dGE\ @@(Bcx}Fږ "0.pu -a)Fu.nP`08o #jґ~a%\0aF0ggiӦ͛7~7ަM7#5C&qPTZtL?cwqw ^ XXTۺu^p8ĭjg8_q]Yo; I~3A7=b KAFS1ý,`1wcw%cTz?` NBҗ2 ű! ]ևL EwkZM8&bb`5#k+н?tP Ν;/t_*jŊsrrLP(,-- /_Cr%,, do&ɓ'_xqѢEm۶c^Um`уI:?i r>CVE𪙼p_Yp)d)z"<+Z- ÷  4~??ӧO߿ܹsݻw_t͛7 رc͚5d9sXZZ;v9ŕif۶mqqq˗/744| ӧOhh{V^mdd_^TRRL&۽{wBBRܱcGW޹k_?̞5Ӭ/}"/H\v6P`0B1KA _aBKaȷ·} 1cOÆ Sg˧dcǎYi֬Y%wU*CC ]]ݸ8bzڹ/cbbd2YTV'kPxJ85[VhC?JAYz+ >W݊Ը)/31N< aW{E777B!HF^~eA.T*U*WD*gff*B;;;ɟ\XQ666SLٽ{wnn.|?{ۡ݇_jjC/W%YzI>u Hu#~LBlF-lalIAJV%^~СGqqqpppQQ*J}o/z2{dee-_̣Μ9O?ua՚U6l&&CG!2.͝w1UC ^cSwzC/QC<8|t&lJF򋬡 ݋Jύ spphyZ!!!/'N|l?I:Wf7^*{lqcSF<ۂ-XHx,~BX+н_~پ}{BBg}1}7225kM=ϼy󂂂###WX!oHCN,_FpyzLajaaqҥJ6beŷ Nª;;5bOSn:aAKݻuӦM B=jժUFj֬Yǎ+_`ٲeڵdWasYYYVZeee%&M0fy0u kвeKTڥK~A\Cttt׮]RY%Sfҳ& n"y^C1!fч:VwgKM^kB|O^KbwDz٪۸c=1A=_uLu|b*Bw ð-rg*^툎 `Z c#<q NӂIX_}U.]rB4hPttOk֬177:t{4NxK$llö$$}O~ΟzM?_c=GaT&27c<؈1[o.n??1<<ĉ&&& ڵKP*QF*J휄U -u *C%N sK}"|rSYu EoG`~R7IXZ'ׯ_ >ib{JJT*=uwP8h'=D$Y\an;+++V# s >>bXtpff;lmm;uꔟݯ_?OaaajЈƩ*!tlxoT n nF%,@yfCeXf>GpqF***>q℮nbbbQQz33Դ4q#xx z:~o] h9&`:Sw'c1늮^1N~!)))ׯ1bDDDH$OT{ E"r/^5I WWP `F(pk&2Od/_hr՞Mڠy掎 HLL偿D5 Hϗ6h3pj6f雞奯߹s砠 CCógPT˗/С\.wqqQhə9sB7nH*jŊcǎnݺrcccoo\JW>eBaii^-i4 HԆ\.޽XMA&IUqQ5J$cgddǏzs·>}D"tRzzL&;vX%uA*Zi֬Y%U*CCJi΂ւ}ObxMRY\\Rˬŋ׿rsshB\y pt,)&J333+ڒ;t:۩{dɒ%K^;ATB=g)Qqq1,CC2[;rH__9phK7 L%=^٩God2ى'_^GG'44T]ˇ^֭[K,111H$111b{ W-iT[>ć1,QhӦ͘1cΝ{5RS&I$###yGFFXBTL8qW\~ԩS{bdd4k,ooǏǟ9sɓrȑ#n߾vZI]-hKi uIUL0{(rs)򐗇Oa`EТRjFFHЪRh ͛d\dB=U dLcظ/ 0z&w>}KJ8.nn%a$Ɩĭx#9/;;޾6AAQX,TDq122 xDQSӟMōx%qkk {{tccfk@ 9p#83f`\9B75[|ttJƨ B6MVOѲe䚦w"6/ Kzb;s=C}pOe)(?bzdg0u* }%a|,Kƨg·}Onxe,\aJf 6_xxI8:..%c=zhY0 IIHMEBRSd}{}{m[kSStSS}732JWJ翈7-QrKFFٴnժ>Z|vC-Q\-P@_-Z͚Gn4of ##<7aj,n:+N `pC ~OGqT;45X[7R$$<; aGrµtGV@۶03+#(+@ӧ%7]r#3ffI5fk=W ݟ~n5\V/޾do* >~>ksrYrZ7fWqu$FJf˖a֦DA`*pu-~%kY#gwdh>b{dS}:ھ=ڶ}֑Y._6`uW;dĉps 1*TM{w}9Y""0զ o 0m/c@Dx"p+ Oxw_eaڴ[Ż:~~vda^U^d :6h@͛1Z{aN>ӗLl+lݯ… jy/Fq*I, j.ޕrta^{8jړ lz袢jSoxW[P͛9j  ,X~~''ŋSRRRS^.N"k0t@DԔ$lVNNB000:tH.L>󣣣'.`jjjooVv2{l)}ѺuXU!"jC|XSZjuuuǎ/^ܰaCppرceffZv~2HByowϞE~> aaIcXd֭[::/5jTW?~x ?Xlo8;;[[[ի&H$宰/F7Fs4Ww,ߕh>X*&''Vh7@#Vmئ.୷}$&Dk2̮]Vڵkً(((?~|ZZѣG{ˈСC\޽{ϋ...UmGs[ 0}:/KDT ^=˖-ssssss;fiilٲxȑڵ Vq/Ç\w^+++77?gR(AAAJrԨQ*6RC!LL?G"&㤠[&+ٯ,||| kμ4ܺuK &IRSSә3g矷o^.{zz޽{ `Ah-rŻ_-tDD5 ͟XJQ /4SQ~zP\ ;;ك޽9xDDԐNEvWWW###RCz o01ai&844oEWX%!"jO?effZ2."w CJ FbUvoܸm۶k֬q˪w|p|hlJǏ?~ȑǏlrĈ#Gtww卪^tc<-2IR Q-㤨G=rݻw 4bĈ1cƘ1uXCdf""Ҫ.b0`ҥ Ioб#.^K/EDZk8;oѣ8po5Q톂壭*Yn6mbIj6uH=1ɓΪ1q o! <{l 7.U#\bzR OOܽ ==~/j=4xȑ*J*J҂Db``7a:F0^֭Ü9L_":!?;,, ???..kݺur Ż==%!"޳T:n)((ٳFtI4n0'moQ&CJJJ* j7 aRaVKDT49 zӦMtJƍ'N߿?ڎy_""۷wԩ_~zzzzzz۷ogc8fn}DDlddrܹgϦO-[jW^ݣG\޺uz5kz=7b[E}z!===RtW599TSlLE-RB9㊋X)"5VrrrOEټy7|sҤI˗/j ƏvQQ.wXM7&c zw!EzMddd\t… 5_[qqȑ#۵k|CAAAC+y{]$ ׄk $s#Q8 )((ӧ}myyy匣޺%?o߾\.{Qjod|\8$8 pоQ&ӺucN8S&5x/w߅%VQ&㤰ɓ?uuu&M4`TZ׻LdvD{vv ;DD"??>|XP$''n+m8DD :Arʪcǎ&&&|?w y3}DDT4111~+WVX*׻X?b, Q=<.]ܼym…z6mXbvMdAAx փilfAAŅ lllspp`u ^*Ҷ-^Es(hlZOO/??q:0.c5ܷo_9TL?X"A{1f̘'ٕ> ǼyXOl)*|%!"j4GٹS/m>$!2y" 7m4 bxփqiii~~~|xWT.ձ븞, `"666|| /5xph{{1 .]n f *O4KD԰hrpTTԾ}J=|U'p:@` X"F611IJJ*nnn* н;KBDHxڴi7o3c V!NMDo54$~SP(h|kZl٧OݻGFF޹soO݋AC<AsKDh2'M 77GÆ :ɘ DDM!O<ٷo_H۶m[r%\TP^߻w~X"FO<144/xyy)Cűug^3:: Q`++ӧO=zSLuf'v_0%!"j4y&>h?ܳgOV<`. ;BDi4" 0\ك=6nįbN~ȉb(h뉞kf E=o\~oc1wT!9r94" x)"{ e5\2q(@.w̙ QU[=ӧOTugl #/c0Vi0OEYvbtLo:AJT%pnJN 5(?Ga! dg#8F*DDM&y* ǎ-[*DDM&SQm۶[n<Ç===$I||}…RfϞӚ5kz޽jk(De!kw`5:uΜ9#}ӦM5\mNNN?kEiŊbݻW\aÆH\>z袢jk H<}'0f ?DD 9fffeZҞo+WxeoK:99-^X"JO:Urib5/&w""5 MSRRʴ?z~=:tH.L>3FGGaaaUmזOpQ=DDZA3gZn]⏂K.988ҦͭcǎnzǎVTTdjj^,55nB=O?姚lO!J-,,kiӽ9;;[[[ի&&&eH$>/P/WhHA\ߋwO=ڷ秚H6'/\`ɵZI]dcc*W\ jH{ ^ (Ļ0"ZQ?5IeM.u|.H:tݻw?؞Rv4#.*7""tM8qΝ;AɱdRt߾}5\Ǐ;'NW\ٻw] EPPR5jJF{.w: `ٙf&(..N???۷wڵha„ fffRt̙|۷垞wޭv{CQߝ7O] "ҚhpL&~LҡCO?4!!cǎi'|C|s_-a @`a3g`k1"" Z+./Pj%"&2 K<D"quu๠y~𛊩s35; :""ޞ ,2 ` X[#(3քf7EOO/""?d``кukC{c:}##!1}&844oEʚiv/X`ҥ//`VYr/[HirVTTԾ}J=|U֠]UT"7, Q`$KKKuKxx9)OtE#t >C8:MAO6͛|||f̘*kp9 $IDAT&QT[lO}%|||֮]+JO0a6 &ax憄LDe8T*FFF#% Ab_l̯&WV~x͍oR N_ea35؟+}gArssuttca]!GHL.?DDM\&hSRR1P]%W%"b==u@t@?G`odie(hr: ݻwӇP !7zx5/d/^-Zxxx\U!?̿ց7 CDͽzի·~OL@%Ǐѩa`7H[CA=gV;߭ڍo u8x1}a~~JdR;*u Uu18oO/&O"߷o߈>}ٳ'O|".AtfeYj"">t萣ڵkcƌczfb=~?GHCAlGqqqbb"ߡAN zFDD49=|ӧϘ1@EvhnéS3!"b};GWWWWWO>۷og~0tpv1kCD4GѣGMLL[j={#^z)Sз/疈HCAGvȑsι(TCհ'bb-(ѣ7!"j 4666|| /ZU*ϧOkWm1jK. WxyyUu aҍcxֆM==۷o[ZZ*lffq\z|%V+_FA%"gU066Jɦj+ P_GA'"g_[X.wXM7q_]DDP#AyqTTw}ĉQQQYYY ڵKP)QF9::T* ,2u.\4C"F8 Z /!88X޾}{\y]C4^|ЮPTO8Q# ຸO# _g3ܿMXu"F :,_?/@DxÛ=HLLƪLu%0F1}-p X"{tU'Cpp@Rrq{ с1KDԘ11n@DԨ{Zj:- /DDD6np AL_"FDDM!ի<<@,6Qc#G0`ӗc7,7oY"&{t%aňH0Q=`1DDD&""bLDDD `"""01DDD `"""b5*2=~ɓPBV335ZCf:hEWkh]ZRO E6U+H$h2 UUn[zz$-'V+V02BhݺƋgOeh\/ۻ࿼- VC eEhQT@Ƕ-Xu[-ֱۗˈ SmZMa!#XDҖ$% U!'eC6?2䜓{U.z`H%}O{dw7utWw~Z!0Ǎ#r-&иqDDb쑁A$f8)Ku{/xNN VkFC>pPOݿOZ劁9uu=bZcNGmm4;'0bc_FT4!X=Av *\]r0kx> T w"`zd*D ޿oFbfÏMD}L/# 2erο.oxhrAaׇvv0>dN-7I8AsPwPw<:V% .ⱋo-{޲gV#.Q?{˞4˿F7 ߆ ߯333juXXG}1`Tt}1)إ-*{ˮq{~e/?ח&Lב啗SJ DW ,Z, /,Ll%z%+%h'Sj*ٳ ,^{ϧ$|  1%_k# Om-ҽ{TTDf?0ۿi2[/t3gv'0] 0Cle # @ @ `0xJh& `@WffT*Q(@[\nn;8PYY0L\\VEEP```DDDFFT';w.**xq! h& Є>h4r<44}(He2F{jV+H[\\\T*>`QBtG^K4o& X,---9&F40eeeCJUWWOXv)]nnnJJJ^^WZZZcc\.p0\RT갰b/` 0/a @͕ʗ.;6sCh843h4r<44}(He2w[aoooggǭjt[N<=_\\)gϞ]ZZʹ&<%%%JÎsC{[v-G{KTfdd,[믿 ]`͛7Պ wwwWWWax㍒ &`hchڡ=q>iF[oq oCu-Zv޽{sssՊ͛7|UUU?6qqKKKf 3gΌ3[///W*SL BPѼK111jرcz)OOf5OOO1cݻ9=j5P1 _VVGD*.(([NHHPϟ)""?kgΜ{DnbΜ9nhhPZVCC{4ak֯_啖6}_%$$TVV744}||8tT*iƍ- nll\r%?vذm۶9::=zO>)jkk[[[M&846ޞCCh84M}3"ٳgĉ DGG766r<ѯ!t)S}ssҥKBo^^']PP`x"&pkh.pbhRs5Oaj ;EX`0 @ `0`́R~0Xr`[ĸM:l???abqrrow^SLݶmۤI d^3{{{{bbԩSO:%+"(,,?H$|  b^ +V\reƍ<h+'DXΞ=ٳrK deeyzz*ر#==]_ԩSs(,,4ŲM6UTT?55U dӳz{oaVP$&&nذZZZv:>3gLJJ?7oޖ-[ ʔxzz'''}Νap͛]f͚cPޠz3?e2.^?S}y{{2SS]]- [[[ {MfX3f?ӵDtʕN"7n 꿱y_uuub5% @(>>>щvvv 9`+۫CD ,.^je6x޽{ͷm6x/7y<: /\pĉI&ڵ+  `H_ %|||^ugggW]]mt!?} ijwv̮<QUUbܸq}RNϏzwn߾͞lrR f+--=tO?^QQz'|a"u־}8bzǏOIIYn]QQQSSSee0%KRRRkjj6mڤ4uuuuuumٲ<qFss't:ɓի---w1U48u^s΅ >'OdKR778p@bF/t;wtww ...˗/W*_L~Ν%KM(,,g/Rstt>}z~~>{qFdd@ >}徾Tk뿆V"00pժU֭ID%%%x 5 0#PIENDB` mapcache-rel-1-2-1/static/bench_merging.png000066400000000000000000000124771226152023600206300ustar00rootroot00000000000000PNG  IHDR, PLTE@Ai @0`@ԥ**@333MMMfff22U݂d"".Wp͇PErz挽k ܠ ݠݐP@Uk/@@``@@`pͷ|@ KIDATx흋(@uu{N PT޽V&JA>k߫T E |Qݽ ]]e ruk CmC@hD*k~{s( k߻9fA A F4ّ`s O#r9}9<ҿ}-ݦmy]?F~ my}Pv[J4̊p춼} 0#::u^| ぀ ^-4(wo#g[I!Yetqm*˖}xF*za ş Jd6Q.aF \eAL?9VP;I/~:lj5ů}殢D񬀗^k}Ȁ# 6}DȻð G|+`C0!krz;tPHn Y ;*j TOnI|mHKeڝ. tJDKJNa媝N|?!Wx]D@ PPDֱ\Xmj BLLuf0jeQp8S 4l;!FdЛ|I44ӁQ%\1߯ۛ'3r*"[${mw$$]çL>"DttrY1hY)o{+Ջ߅v\2`|xX,"β-b@xDʷ 6<8+#AoHn %kw*LUT~I17$y;QeHKz|/q_lXT٥N߷أ޺%y]XEh8킀" :[*yb}Z~6XaM%=i@./E ZY3%#=גϾQ{;ΟMT>3,׌_w<~gɳ딟nɜp޼}[IG[e˕1 5(|q,RX]ڷ׼k,lKN ޯGIN'[>/up߽nЏx)6̯L6ntW#V!{A`"aNV;T;^:M0UͣM*NXYcw-;:}vPnvKl k#|uIݨoݔQL m( ݗ:XU@S¢wS+%1yi1iOpD _ŴeFWx2&,`8崷%rXϟ)f=׎bǛc#k8A4Ks#%ܖXv.pF~染p]vZv) ܝ5?YU  mX\>m\@ i=,}Lf:oP1 wL- gf W@3r&0g)?;( bB N@l1&1g?{$ d""&F@,Q&2g)?L Xƾgm|I(>`5g*_noخmulA@3ϛ.x?nӺ}bm^7$oBo NXƿI0* ͂MoL `Q@Lœ7Ŀɰ% LGƗ֗}~VGoB|q&;oFn,XQWߔߜX&%T>߬da%'`:VbA@73A#!75aonp4 MN0#ov3MĿQ- ͏i[0jac ~?f> Bd3MĿeP9 H-Aiq{ϰvM0̿a?ѷ̝RqQ}0𝀣ӾYcm2̿A"y6 t<ӵǵoLC/S wo0?X&ϧUk gxkm!RJl`'oDQw6t##il@! *!ilʔLDC[(#RyHexV3'^#Q"`W#i nC? !iiH xYA #O' #luPʨ{GSˠ{'Z ݿA-zKg_X?$=:Uts2`oQ-7U0_ NMX]o:ܢCfΗ`G_~Sr+wU&֗ .fԦ ͋AM~IS^@fG+M髷] 8~QZ%ZDYYˀCqRrJoJ P.xJΆBXbtPy]p.a*@#L}d R⋈BIP"HC?oYJl0DC?(A)IyۀW)fp~Sr:Vˀ><#O=E0OzU = FMàP0 HHvr(~]CS7-D?pܤy5=D?𩜆9<+gC?%?X7JN :ɀvLg(x7F ~pi?_C?T c  -}a=1~q0OFoz ~pK[sI7]D?ѱ o2]~P@z?*eCCI)&䩀Ŵ}Cr]8;7!uo"o~uro~k00%*A4 :o-MàjLDe@[E]0Q09`Yf4aoq |x> -O [~墤2/!?.#&a 7'jկ@[JðAP@g ߼Etu Go&&VF7auԨi@D@aԧR#%RNa>T x<1=#.O#oa'To5}zyS]"O5]ԣinrt-1@h!F_|b)&1gYdk)D>Lc\@쳎eo }s`R@`O@ cblX&Č7'&d1/DQ. ͎fo }kS@[}2] e"jhDط&:ľeQ `H }1b| 21b}@@@4>9 ȐJ# A!o<w/ :6h xt{Z|b:@ M Ц +wݎeyK&@<%(,SS*"Rf3gDE k﫮 ~C@ڛڞo5@{{pr@q8G|]'[N)V8 i 9~Zoh80Oݩ<9"sA[RFYv(EՕSi3v?EUQTh?"IXO fo wlSW kz\y"ZTk!ҐzTᅵ+hԧ 8k;skZ8;t ci$e@5LXMLߋR^f)d!e>#)3+8,`M>N5I>v#8q>Pj]j7'Bmʿ⚣Ѥqy ; Mvis+=E:kOobx6{rR/.X-(3X:ȉͦ:tӜ&mf{&R4j]c$MfCV|Rh v.2<Ǧp'}JEY8Rl?V{UNqGwmfW\sT@hf֓iO*2 m\K\?a*py1p,o%RL d )@# Ca4bC Y޿T 1D3i3yfNs}2NԒڼpx]k)@/y2G{iS]א٬#˩AM?э~Fʼ#CNkwIg&@穝xJ:0.`u+R3,s%=jʀPmk|*raj˾6f4 8տiEO}TK«.]j]iPwZZqX@#TN~*8RkN=Sÿ)Z4>to{WZ\sT@Zp"3֣GopЦ2|E:m;T^& ƻ?k}{Ԕbb!x_mиYm4k_0 Cy@}Tkϼܩ|Lv]~P~tb=U ~qu:x{Jb*oMwQk >H)e h߸IP :|V*sK*\Qд- ?2у%Vb1ҤcߗK&zQTT1 2:aDjW)fTpG[r#W% ͧ}Ŀt\" L%߮ 8̏[L\b!#a  hL&/=˅BNſNH낀XI+v82m?X\@- mTgeOSYX@TD'* a* RXS@T䀀0?A,( Ia* @@r,V02_<7 .x(%xŪA@iٲ'.oӺ>r'W d@J]0 dA di:',# a*BA@"TĂ0%?D|ɿ\!/V'/t P&+Aa*1_\r?Da4?L(3Y@NLHy@NtLg4 A<vA@H1 A|;DfN3a0v5 W9?%~1BLtZaa>9e ˔ip3B9})00^!\pۀ*Q__  Cqkr@i)ϻĀ*EӪ/?(F{\kn.] sW|b:N0j GBiW LD `7"&AV A@ u<[Am" 8H邛?p1jɨe" 8to7 f̽a< 7 !#a^ 8?ٿm6My.NbĻ3!KKpwAHW = n :wK.͋qoɳ4Er[YZ_ O-]~.V'MӞIJi(=a ې$(A7 rs t n^^$\ S ^0Ŀ$4( > # mHRr5LoK~?"|RA2E0Cgpw,LJĿ]ߢ׿yV'qoKXz6\//e~|^p1E0.vPV[3Uެ. mNȗ3LV|ow^Gxf I iO BO "XV@'%?9( bAO >HAЋO) ) #j4ֺ Q5O+M@ܞ :HR|" dO(t'[tbYbp`O2)c h. &i&3Yz P3wڿd!9w a 6',c橸U+qxEtRqwzUߜR r~]p :,C4.]9>$%*t=A}Xatr9 J,ҿڰQ'#C~a<1=l5a= /FǀhvGE0OL.,*#mXk+ 3&#,w[vׄbH;3GIV7 :,wwR~R/`5 ?ҟFtUW1`4LtBfڀՂ= XOoQ_I%x'}?u?_Yȿ:b:NGGDtY,/ n]0A Fe׀slHR"``zL$ `J&agYb̏j>tRlU`:yA% k006Lrզ=i.ƺqT9Xp~mJbsv/)~nmf[} /`s/eCS(HxLaM_ x?1=S5 Ө邟U'=1a#mA.JO'ԯY(MeL[GۋA~o7!OLooCFLDIط%loW ?ۗw$,`j#t6Շ7Q*H~B&`U}b%`~$?I"a,XO=,^'wR>,! e- g$?( <I~L>P+  F }`1L@$ Kta }$?HItt d07!  La*SxbW}@GK 3 B 0:-\Ib:&C)m`B|I~b:&a*SA@ T0\`JklhM/vU~D@ەDYX@ԓ'D='Y2^@XCoQ $^@ԃ V8A@ T0 La*SA@ T0^@ڣ=}X!oioj{'B@ѻ` ̥MO6G' G6DV6mn'6qm]5ms9s; !Wjq|u |1 ?B& \52}AJCyЖ3)& lr[Թi>N=y< \n^L]?mKՒF^sSy #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; #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; apr_time_t age_limit = 0; int seededtilestot=0, seededtiles=0, queuedtilestot=0; struct mctimeval lastlogtime,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_mode; mapcache_iteration_mode iteration_mode = MAPCACHE_ITERATION_UNSET; struct seed_cmd { cmd command; int x; int y; int z; }; #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)"}, #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 { "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)" }, { "quiet", 'q', FALSE, "don't show progress info" }, #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= 1) nworkers = nprocesses; sprintf(msg,"seeding tile %d %d %d",x,y,z); if(lastmsglen) { char erasestring[1024]; int len = MAPCACHE_MIN(1023,lastmsglen); memset(erasestring,' ',len); erasestring[len+1]='\0'; sprintf(erasestring,"\r%%%ds\r",lastmsglen); printf(erasestring," "); } lastmsglen = strlen(msg); printf("%s",msg); fflush(NULL); return; if(queuedtilestot>nworkers) { struct mctimeval now_t; float duration; float totalduration; seededtilestot = queuedtilestot - nworkers; mapcache_gettimeofday(&now_t,NULL); duration = ((now_t.tv_sec-lastlogtime.tv_sec)*1000000+(now_t.tv_usec-lastlogtime.tv_usec))/1000000.0; totalduration = ((now_t.tv_sec-starttime.tv_sec)*1000000+(now_t.tv_usec-starttime.tv_usec))/1000000.0; if(duration>=5) { int Nx, Ny, Ntot, Ncur, ntilessincelast; Nx = (grid_link->grid_limits[z].maxx-grid_link->grid_limits[z].minx)/tileset->metasize_x; Ny = (grid_link->grid_limits[z].maxy-grid_link->grid_limits[z].miny)/tileset->metasize_y; Ntot = Nx*Ny; Ncur = ((y-grid_link->grid_limits[z].miny)/tileset->metasize_y ) * Nx + (x-grid_link->grid_limits[z].minx+1)/tileset->metasize_x; ntilessincelast = seededtilestot-seededtiles; sprintf(msg,"seeding level %d [%d/%d]: %f metatiles/sec (avg since start: %f)",z,Ncur,Ntot,ntilessincelast/duration, seededtilestot/totalduration); lastlogtime=now_t; seededtiles=seededtilestot; } else { return; } } else { sprintf(msg,"seeding level %d",z); } if(lastmsglen) { char erasestring[1024]; int len = MAPCACHE_MIN(1023,lastmsglen); memset(erasestring,' ',len); erasestring[len+1]='\0'; sprintf(erasestring,"\r%%%ds\r",lastmsglen); printf(erasestring," "); } lastmsglen = strlen(msg); printf("%s",msg); fflush(NULL); } cmd examine_tile(mapcache_context *ctx, mapcache_tile *tile) { int action = MAPCACHE_CMD_SKIP; int intersects = -1; int tile_exists = force?0:tileset->cache->tile_exists(ctx,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,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 (tileset_transfer->cache->tile_exists(ctx,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 (tileset_transfer->cache->tile_exists(ctx,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) { queuedtilestot--; } 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); queuedtilestot++; progresslog(tile->x,tile->y,tile->z); } //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) { queuedtilestot--; } break; } 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); queuedtilestot++; progresslog(x,y,z); } //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); int isLocked = mapcache_lock_or_wait_for_resource(&seed_ctx, mapcache_tileset_metatile_resource_key(&seed_ctx,mt)); 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); mapcache_unlock_resource(&seed_ctx, mapcache_tileset_metatile_resource_key(&seed_ctx,mt)); } } 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; tileset_transfer->cache->tile_set(&seed_ctx, subtile); } } else { //CMD_DELETE mapcache_tileset_tile_delete(&seed_ctx,tile,MAPCACHE_TRUE); } if(seed_ctx.get_error(&seed_ctx)) { error_detected++; ctx.log(&ctx,MAPCACHE_INFO,seed_ctx.get_error_message(&seed_ctx)); } } } #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; } 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_threadattr_t *thread_attrs; const char *tileset_name=NULL; const char *tileset_transfer_name=NULL; const char *grid_name = 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); ctx.process_pool = ctx.pool; cfg = mapcache_configuration_create(ctx.pool); ctx.config = cfg; ctx.log= mapcache_context_seeding_log; apr_getopt_init(&opt, ctx.pool, argc, argv); seededtiles=seededtilestot=queuedtilestot=0; mapcache_gettimeofday(&starttime,NULL); lastlogtime=starttime; 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 '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 '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 '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)); } #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(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 (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); } } } 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; i0) { struct mctimeval now_t; float duration; 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 at %g tiles/sec\n",seededtilestot, seededtilestot/duration); } apr_terminate(); if (error_detected > 0) { exit(1); } return 0; } /* vim: ts=2 sts=2 et sw=2 */