Esempio n. 1
0
int main(int argc, const char **argv) {
    /* initialize apr_getopt_t */
    apr_getopt_t *opt;
    const char *configfile=NULL;
#ifndef USE_FORK
    apr_thread_t **threads;
    apr_threadattr_t *thread_attrs;
#endif
    const char *tileset_name=NULL;
    const char *tileset_transfer_name=NULL;
    const char *grid_name = NULL;
    int *zooms = NULL;//[2];
    double *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;

#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;
#ifndef USE_FORK
    apr_thread_mutex_create((apr_thread_mutex_t**)&ctx.threadlock,APR_THREAD_MUTEX_DEFAULT,ctx.pool);
#else
    ctx.threadlock = NULL;
#endif
    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 '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);
                break;
            case 'e':
                if ( MAPCACHE_SUCCESS != mapcache_util_extract_double_list(&ctx, (char*)optarg, ",", &extent, &n) ||
                        n != 4 || extent[0] >= extent[2] || extent[1] >= extent[3] ) {
                    return usage(argv[0], "failed to parse extent, expecting comma separated 4 doubles");
                }
                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) {
         return usage(argv[0],"no features in provided ogr parameters, cannot continue");
      }


      initGEOS(notice, log_and_exit);
      clippers = (const GEOSPreparedGeometry**)malloc(nClippers*sizeof(GEOSPreparedGeometry*));


      OGRFeatureH hFeature;
      GEOSWKTReader *geoswktreader = GEOSWKTReader_create();
      OGR_L_ResetReading(layer);
      extent = apr_pcalloc(ctx.pool,4*sizeof(double));
      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[0] = ogr_extent.MinX;
            extent[1] = ogr_extent.MinY;
            extent[2] = ogr_extent.MaxX;
            extent[3] = ogr_extent.MaxY;
         } else {
            extent[0] = MAPCACHE_MIN(ogr_extent.MinX, extent[0]);
            extent[1] = MAPCACHE_MIN(ogr_extent.MinY, extent[1]);
            extent[2] = MAPCACHE_MAX(ogr_extent.MaxX, extent[2]);
            extent[3] = MAPCACHE_MAX(ogr_extent.MaxY, extent[3]);
         }

         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( ! grid_name ) {
           grid_link = APR_ARRAY_IDX(tileset->grid_links,0,mapcache_grid_link*);
        } else {
Esempio n. 2
0
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 <grid>");
    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 <extent>-180 -90 180 90</extent>",
                     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 <size>256 256</size>",
                     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 <resolutions>1 2 4 8 16 32</resolutions>",
                     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 <srs> 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 <extent> 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 <size> tag.", grid->name);
    return;
  }
  if(!grid->nlevels) {
    ctx->set_error(ctx, 400, "grid \"%s\" has no resolutions configured."
                   " You must add a <resolutions> tag.", grid->name);
    return;
  }
  mapcache_configuration_add_grid(config,grid,name);
}
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;
}