mapcache_http_response *mapcache_core_get_tile(mapcache_context *ctx, mapcache_request_get_tile *req_tile) { int expires = 0; mapcache_http_response *response; int i,is_empty=1 /* response image is initially empty */; char *timestr; mapcache_image *base=NULL; mapcache_image_format *format = NULL; #ifdef DEBUG if(req_tile->ntiles ==0) { ctx->set_error(ctx,500,"BUG: get_tile called with 0 tiles"); return NULL; } #endif response = mapcache_http_response_create(ctx->pool); mapcache_prefetch_tiles(ctx,req_tile->tiles,req_tile->ntiles); if(GC_HAS_ERROR(ctx)) return NULL; /* loop through tiles, and eventually merge them vertically together */ for(i=0; i<req_tile->ntiles; i++) { mapcache_tile *tile = req_tile->tiles[i]; /* shortcut */ if(tile->mtime && (tile->mtime < response->mtime || response->mtime == 0)) response->mtime = tile->mtime; if(tile->expires && (tile->expires < expires || expires == 0)) { expires = tile->expires; } if(tile->nodata) { /* treat the special case where the cache explicitely stated that the tile was empty, and we don't have any vertical merging to do */ if(tile->encoded_data && req_tile->ntiles == 1) { response->data = tile->encoded_data; /* we don't touch is_empty, as we have access to the encoded empty image, but the resulting tile is empty */ } continue; } /* treat the most common case: - we have a single tile request (i.e. isempty is true) - the cache returned the encoded image */ if(is_empty && tile->encoded_data) { response->data = tile->encoded_data; /* just in case we also have the raw image data available, keep a ref to it if we need to merge another tile ontop of it*/ if(tile->raw_image) { base = tile->raw_image; } is_empty = 0; /* we now know we might need to do some vertical merging */ continue; } /* if we're here, either * - we need to merge the current tile onto the previous one(s), or * - we only have the tile's raw data available */ if(!is_empty) { /* we have an existing tile, so we know we need to merge the current one into it */ if(!base) { /* the existing tile has not been decoded yet, but we need the access to the raw pixels*/ base = mapcache_imageio_decode(ctx, response->data); if(!base) return NULL; } response->data = NULL; /* the encoded data is now obsolete, as we will be merging the current tile */ /* we need to access the current tile's pixel data */ if(!tile->raw_image) { tile->raw_image = mapcache_imageio_decode(ctx,tile->encoded_data); if(!tile->raw_image) return NULL; } mapcache_image_merge(ctx, base, tile->raw_image); } else { /* we don't need to merge onto an existing tile and don't have access to the tile's encoded data. * * we don't encode the tile's raw image data just yet because we might need to merge another one on top * of it later. */ base = tile->raw_image; is_empty = 0; } } if(!response->data) { /* we need to encode the raw image data*/ if(base) { if(req_tile->format) { format = req_tile->format; } else { format = req_tile->tiles[0]->tileset->format; if(!format) { format = ctx->config->default_image_format; /* this one is always defined */ } } response->data = format->write(ctx, base, format); if(GC_HAS_ERROR(ctx)) { return NULL; } } else { #ifdef DEBUG if(!is_empty) { ctx->set_error(ctx,500,"BUG: no image data to encode, but tile not marked as empty"); return NULL; } #endif unsigned char empty[5] = {'#',0,0,0,0}; response->data = mapcache_empty_png_decode(ctx,empty,&is_empty); /* is_empty is unchanged and left to 1 */ format = mapcache_configuration_get_image_format(ctx->config,"PNG8"); } } /* compute the content-type */ mapcache_image_format_type t = mapcache_imageio_header_sniff(ctx,response->data); if(t == GC_PNG) apr_table_set(response->headers,"Content-Type","image/png"); else if(t == GC_JPEG) apr_table_set(response->headers,"Content-Type","image/jpeg"); /* compute expiry headers */ if(expires) { apr_time_t now = apr_time_now(); apr_time_t additional = apr_time_from_sec(expires); apr_time_t texpires = now + additional; apr_table_set(response->headers, "Cache-Control",apr_psprintf(ctx->pool, "max-age=%d", expires)); timestr = apr_palloc(ctx->pool, APR_RFC822_DATE_LEN); apr_rfc822_date(timestr, texpires); apr_table_setn(response->headers, "Expires", timestr); } return response; }
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 <format>"); return; } name = apr_pstrdup(ctx->pool, name); if(!type || !strlen(type)) { ctx->set_error(ctx, 400, "mandatory attribute \"type\" not found in <format>"); return; } if(!strcmp(type,"PNG")) { int colors = -1; mapcache_compression_type compression = MAPCACHE_COMPRESSION_DEFAULT; if ((cur_node = ezxml_child(node,"compression")) != NULL) { if(!strcmp(cur_node->txt, "fast")) { compression = MAPCACHE_COMPRESSION_FAST; } else if(!strcmp(cur_node->txt, "best")) { compression = MAPCACHE_COMPRESSION_BEST; } else if(!strcmp(cur_node->txt, "none")) { compression = MAPCACHE_COMPRESSION_DISABLE; } else { ctx->set_error(ctx, 400, "unknown compression type %s for format \"%s\"", cur_node->txt, name); return; } } if ((cur_node = ezxml_child(node,"colors")) != NULL) { char *endptr; colors = (int)strtol(cur_node->txt,&endptr,10); if(*endptr != 0 || colors < 2 || colors > 256) { ctx->set_error(ctx, 400, "failed to parse colors \"%s\" for format \"%s\"" "(expecting an integer between 2 and 256 " "eg <colors>256</colors>", 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 <quality>90</quality>", 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; unsigned int alpha_cutoff=255; 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; } if ((cur_node = ezxml_child(node,"alpha_cutoff")) != NULL) { alpha_cutoff = atoi(cur_node->txt); } format = mapcache_imageio_create_mixed_format(ctx->pool,name,transparent, opaque, alpha_cutoff); } else { ctx->set_error(ctx, 400, "unknown format type %s for format \"%s\"", type, name); return; } 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 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 <mapcache>", 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 <mapcache>",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,"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 <service> type %s",type); } if(GC_HAS_ERROR(ctx)) goto cleanup; } } } else if ((node = ezxml_child(doc,"services")) != NULL) { ctx->log(ctx,MAPCACHE_WARN,"<services> tag is deprecated, use <service type=\"wms\" enabled=\"true|false\">"); parseServices(ctx, node, config); } else { ctx->set_error(ctx, 400, "no <services> configured"); } if(GC_HAS_ERROR(ctx)) goto cleanup; 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,"<errors>: report_img not implemented"); goto cleanup; } else { ctx->set_error(ctx,400,"<errors>: 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 <log_level> \"%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 <auto_reload> \"%s\". Expecting true or false",node->txt); return; } } cleanup: ezxml_free(doc); return; }
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,MAPCACHE_OPTIMIZE_YES), "JPEG"); mapcache_configuration_add_image_format(cfg, mapcache_imageio_create_mixed_format(pool,"mixed", mapcache_configuration_get_image_format(cfg,"PNG"), mapcache_configuration_get_image_format(cfg,"JPEG"), 255), "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; i<grid->nlevels; 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; i<grid->nlevels; 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; i<grid->nlevels; i++) { mapcache_grid_level *level = (mapcache_grid_level*)apr_pcalloc(pool,sizeof(mapcache_grid_level)); level->resolution = google_resolutions[i]; unitheight = grid->tile_sy * level->resolution; unitwidth = grid->tile_sx * level->resolution; level->maxy = ceil((grid->extent.maxy-grid->extent.miny - 0.01* unitheight)/unitheight); level->maxx = ceil((grid->extent.maxx-grid->extent.minx - 0.01* unitwidth)/unitwidth); grid->levels[i] = level; } mapcache_configuration_add_grid(cfg,grid,"g"); cfg->loglevel = MAPCACHE_WARN; cfg->autoreload = 0; return cfg; }
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 <tileset>"); 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,"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 <wgs84bbox>-180 -90 180 90</wgs84bbox>", value,nvalues,values[0],values[1],values[2],values[3]); return; } tileset->wgs84bbox[0] = values[0]; tileset->wgs84bbox[1] = values[1]; tileset->wgs84bbox[2] = values[2]; tileset->wgs84bbox[3] = values[3]; havewgs84bbox = 1; } for(cur_node = ezxml_child(node,"grid"); cur_node; cur_node = cur_node->next) { int i; mapcache_grid *grid; mapcache_grid_link *gridlink; char *restrictedExtent = NULL, *sTolerance = NULL; double *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 = apr_pcalloc(ctx->pool,grid->nlevels*sizeof(int*)); for(i=0;i<grid->nlevels;i++) { gridlink->grid_limits[i] = apr_pcalloc(ctx->pool,4*sizeof(int)); } restrictedExtent = (char*)ezxml_attr(cur_node,"restricted_extent"); if(restrictedExtent) { int nvalues; restrictedExtent = apr_pstrdup(ctx->pool,restrictedExtent); if(MAPCACHE_SUCCESS != mapcache_util_extract_double_list(ctx, restrictedExtent, NULL, &gridlink->restricted_extent, &nvalues) || nvalues != 4) { ctx->set_error(ctx, 400, "failed to parse extent array %s." "(expecting 4 space separated numbers, " "eg <grid restricted_extent=\"-180 -90 180 90\">foo</grid>", restrictedExtent); return; } 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; } /* compute wgs84 bbox if it wasn't supplied already */ if(!havewgs84bbox && !strcasecmp(grid->srs,"EPSG:4326")) { tileset->wgs84bbox[0] = extent[0]; tileset->wgs84bbox[1] = extent[1]; tileset->wgs84bbox[2] = extent[2]; tileset->wgs84bbox[3] = extent[3]; } 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,"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 <metatile>5 5</metatile>", 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 <expires>3600</expires>", 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 <auto_expire>3600</auto_expire>", 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 <metabuffer>1</metabuffer>", 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; }