Exemple #1
0
static void _mapcache_cache_mbtiles_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
{
    mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*)pcache;
    struct sqlite_conn *conn;
    mapcache_pooled_connection *pc = mapcache_sqlite_get_conn(ctx,cache,tile,0);
    if (GC_HAS_ERROR(ctx)) {
        mapcache_sqlite_release_conn(ctx, pc);
        return;
    }
    conn = SQLITE_CONN(pc);
    if(!tile->raw_image) {
        tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data);
        if(GC_HAS_ERROR(ctx)) {
            mapcache_sqlite_release_conn(ctx, pc);
            return;
        }
    }
    sqlite3_exec(conn->handle, "BEGIN TRANSACTION", 0, 0, 0);
    _single_mbtile_set(ctx, cache, tile,conn);
    if (GC_HAS_ERROR(ctx)) {
        sqlite3_exec(conn->handle, "ROLLBACK TRANSACTION", 0, 0, 0);
    } else {
        sqlite3_exec(conn->handle, "END TRANSACTION", 0, 0, 0);
    }
    mapcache_sqlite_release_conn(ctx, pc);
}
Exemple #2
0
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);
  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_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);
}
Exemple #3
0
/**
 * \brief push tile data to memcached
 *
 * writes the content of mapcache_tile::data to the configured memcached instance(s)
 * \returns MAPCACHE_FAILURE if there is no data to write, or if the tile isn't locked
 * \returns MAPCACHE_SUCCESS if the tile has been successfully written
 * \private \memberof mapcache_cache_memcache
 * \sa mapcache_cache::tile_set()
 */
static void _mapcache_cache_memcache_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
{
  char *key;
  int rv;
  /* set no expiration if not configured */
  int expires =0;
  mapcache_buffer *encoded_data = NULL;
  mapcache_cache_memcache *cache = (mapcache_cache_memcache*)pcache;
  mapcache_pooled_connection *pc;
  struct mapcache_memcache_pooled_connection *mpc;
  pc = _mapcache_memcache_get_conn(ctx,cache,tile);
  GC_CHECK_ERROR(ctx);
  mpc = pc->connection;
  key = mapcache_util_get_tile_key(ctx, tile,NULL," \r\n\t\f\e\a\b","#");
  if(GC_HAS_ERROR(ctx)) goto cleanup;
  
  if(tile->tileset->auto_expire)
    expires = tile->tileset->auto_expire;

  if(cache->detect_blank) {
    if(!tile->raw_image) {
      tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data);
      GC_CHECK_ERROR(ctx);
    }
    if(mapcache_image_blank_color(tile->raw_image) != MAPCACHE_FALSE) {
      encoded_data = mapcache_buffer_create(5,ctx->pool);
      ((char*)encoded_data->buf)[0] = '#';
      memcpy(((char*)encoded_data->buf)+1,tile->raw_image->data,4);
      encoded_data->size = 5;
    }
  }
  if(!encoded_data) {
    if(!tile->encoded_data) {
      tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format);
      if(GC_HAS_ERROR(ctx)) goto cleanup;
    }
    encoded_data = tile->encoded_data;
  }

  /* concatenate the current time to the end of the memcache data so we can extract it out
   * when we re-get the tile */
  char *data = calloc(1,encoded_data->size+sizeof(apr_time_t));
  apr_time_t now = apr_time_now();
  apr_pool_cleanup_register(ctx->pool, data, (void*)free, apr_pool_cleanup_null);
  memcpy(data,encoded_data->buf,encoded_data->size);
  memcpy(&(data[encoded_data->size]),&now,sizeof(apr_time_t));

  rv = apr_memcache_set(mpc->memcache,key,data,encoded_data->size+sizeof(apr_time_t),expires,0);
  if(rv != APR_SUCCESS) {
    ctx->set_error(ctx,500,"failed to store tile %d %d %d to memcache cache %s",
                   tile->x,tile->y,tile->z,cache->cache.name);
    goto cleanup;
  }

cleanup:
  _mapcache_memcache_release_conn(ctx,pc);
}
Exemple #4
0
//------------------------------------------------------------------------------
void _mapcache_source_tms_render_map_elevation(mapcache_context *ctx, mapcache_map *map)
{
    mapcache_source_tms *tms;
    int elevationblock;
    int zoom, x, y;
    char* url;
    double dx, dy;

    tms = (mapcache_source_tms*)map->tileset->source;
    elevationblock = map->grid_link->grid->elevationblock;

    _GetTileCoords(map, &zoom, &x, &y, tms->flipy);

    url = apr_psprintf(ctx->pool,"%s/1.0.0/%s/%i/%i/%i.%s", tms->url,tms->layer,zoom,x,y,tms->format);
    tms->http->url = apr_pstrdup(ctx->pool,url);

    map->encoded_data = mapcache_buffer_create(30000,ctx->pool);
    mapcache_http_do_request(ctx, tms->http, map->encoded_data, NULL, NULL);
    GC_CHECK_ERROR(ctx);

    if(!mapcache_imageio_is_valid_format(ctx,map->encoded_data)) {
        char *returned_data = apr_pstrndup(ctx->pool,(char*)map->encoded_data->buf,map->encoded_data->size);
        ctx->set_error(ctx, 502, "tms request for tileset %s returned an unsupported format:\n%s",
                       map->tileset->name, returned_data);
        return;
    }

    map->raw_image = mapcache_imageio_decode(ctx, map->encoded_data);
    map->raw_image->is_elevation = MC_ELEVATION_YES;
    GC_CHECK_ERROR(ctx);

    //map->raw_image->stride = 4 * elevationblock;
    dx = fabs(map->grid_link->grid->extent.maxx-map->grid_link->grid->extent.minx);
    dy = fabs(map->grid_link->grid->extent.maxx-map->grid_link->grid->extent.minx);
    map->raw_image->x0 = map->extent.minx / dx * 2.0;
    map->raw_image->y0 = map->extent.miny / dy * 2.0;
    map->raw_image->x1 = map->extent.maxx / dx * 2.0;
    map->raw_image->y1 = map->extent.maxy / dy * 2.0;

    if (map->raw_image->w != elevationblock || map->raw_image->h != elevationblock)
    {
        ctx->set_error(ctx,500,"Error: size of heightmap from source is not configured propery!");
    }

}
Exemple #5
0
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);
}
Exemple #6
0
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);
}
Exemple #7
0
/**
 * \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);
      }
    }
  }
}
Exemple #8
0
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; i<req_map->nmaps; 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; i<req_map->nmaps; 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->expires<basemap->expires) 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;
}
Exemple #9
0
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;
}
Exemple #10
0
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,first = -1;
  int ntiles_with_data = 0;
  char *timestr;
  mapcache_image *base=NULL,*overlay;
  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
  expires = 0;
  response = mapcache_http_response_create(ctx->pool);


  mapcache_prefetch_tiles(ctx,req_tile->tiles,req_tile->ntiles);
  if(GC_HAS_ERROR(ctx))
    return NULL;

  /* count how many tiles actually contain data */
  for(i=0; i<req_tile->ntiles; i++) {
    /* add 1 if tile->nodata == 0 */
    ntiles_with_data -= req_tile->tiles[i]->nodata - 1;
  }
  if(ntiles_with_data == 0) {
    ctx->set_error(ctx,404,
                   "no tiles containing image data could be retrieved (not in cache, and read-only tileset or no source configured)");
    return NULL;
  }
  /* this loop retrieves the tiles from the caches, and eventually decodes and merges them together
   * if multiple tiles were asked for */
  for(i=0; i<req_tile->ntiles; i++) {
    mapcache_tile *tile = req_tile->tiles[i];
    if(tile->nodata) continue;
    if(first == -1) {
      first = i;
      response->mtime = tile->mtime;
      expires = tile->expires;
      /* if we have multiple tiles to merge, decode the image data */
      if(ntiles_with_data>1) {
        if(!tile->raw_image) {
          tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data);
          if(!tile->raw_image) return NULL;
        }
        base = tile->raw_image;
      }
    } else {
      if(response->mtime < tile->mtime)
        response->mtime = tile->mtime;
      if(tile->expires < expires) {
        expires = tile->expires;
      }
      if(!tile->raw_image) {
        tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data);
        if(!tile->raw_image) return NULL;
      }
      overlay = tile->raw_image;
      mapcache_image_merge(ctx, base, overlay);
      if(GC_HAS_ERROR(ctx)) {
        return NULL;
      }
    }
  }
  format = NULL;

  /* if we had more than one tile, we need to encode the raw image data into a mapcache_buffer */
  if(ntiles_with_data > 1) {
    if(req_tile->format) {
      format = req_tile->format;
    } else {
      format = req_tile->tiles[first]->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 {
    response->data = req_tile->tiles[first]->encoded_data;
    format = req_tile->tiles[first]->tileset->format;
  }

  /* 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(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;
}
Exemple #11
0
/**
 * \brief write tile data to disk
 *
 * writes the content of mapcache_tile::data to disk.
 * \returns MAPCACHE_FAILURE if there is no data to write, or if the tile isn't locked
 * \returns MAPCACHE_SUCCESS if the tile has been successfully written to disk
 * \private \memberof mapcache_cache_disk
 * \sa mapcache_cache::tile_set()
 */
static void _mapcache_cache_disk_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
{
  apr_size_t bytes;
  apr_file_t *f;
  apr_status_t ret;
  char errmsg[120];
  char *filename, *hackptr1, *hackptr2=NULL;
  mapcache_cache_disk *cache = (mapcache_cache_disk*)pcache;
  const int creation_retry = cache->creation_retry;
  int retry_count_create_file = 0;

#ifdef DEBUG
  /* all this should be checked at a higher level */
  if(!tile->encoded_data && !tile->raw_image) {
    ctx->set_error(ctx,500,"attempting to write empty tile to disk");
    return;
  }
  if(!tile->encoded_data && !tile->tileset->format) {
    ctx->set_error(ctx,500,"received a raw tile image for a tileset with no format");
    return;
  }
#endif

  cache->tile_key(ctx, cache, tile, &filename);
  GC_CHECK_ERROR(ctx);

  /* find the location of the last '/' in the string */
  hackptr1 = filename;
  while(*hackptr1) {
    if(*hackptr1 == '/')
      hackptr2 = hackptr1;
    hackptr1++;
  }
  *hackptr2 = '\0';

  if(APR_SUCCESS != (ret = apr_dir_make_recursive(filename,APR_OS_DEFAULT,ctx->pool))) {
    /*
     * apr_dir_make_recursive sometimes sends back this error, although it should not.
     * ignore this one
     */
    if(!APR_STATUS_IS_EEXIST(ret)) {
      ctx->set_error(ctx, 500, "failed to create directory %s: %s",filename, apr_strerror(ret,errmsg,120));
      return;
    }
  }
  *hackptr2 = '/';

  ret = apr_file_remove(filename,ctx->pool);
  if(ret != APR_SUCCESS && !APR_STATUS_IS_ENOENT(ret)) {
    ctx->set_error(ctx, 500,  "failed to remove file %s: %s",filename, apr_strerror(ret,errmsg,120));
  }


#ifdef HAVE_SYMLINK
  if(cache->symlink_blank) {
    if(!tile->raw_image) {
      tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data);
      GC_CHECK_ERROR(ctx);
    }
    if(mapcache_image_blank_color(tile->raw_image) != MAPCACHE_FALSE) {
      char *blankname;
      int retry_count_create_symlink = 0;
      char *blankname_rel = NULL;
      _mapcache_cache_disk_blank_tile_key(ctx,cache,tile,tile->raw_image->data,&blankname);
      if(apr_file_open(&f, blankname, APR_FOPEN_READ, APR_OS_DEFAULT, ctx->pool) != APR_SUCCESS) {
        int isLocked;
        void *lock;
        char *blankdirname;
        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 */
        blankdirname = apr_psprintf(ctx->pool, "%s/%s/%s/blanks",
                                          cache->base_directory,
                                          tile->tileset->name,
                                          tile->grid_link->grid->name);
        if(APR_SUCCESS != (ret = apr_dir_make_recursive(
                                   blankdirname, APR_OS_DEFAULT,ctx->pool))) {
          if(!APR_STATUS_IS_EEXIST(ret)) {
            ctx->set_error(ctx, 500,  "failed to create directory %s for blank tiles",blankdirname, apr_strerror(ret,errmsg,120));
            return;
          }
        }

        /* aquire a lock on the blank file */
        isLocked = mapcache_lock_or_wait_for_resource(ctx,ctx->config->locker,blankname, &lock);

        if(isLocked == MAPCACHE_TRUE) {

          if((ret = apr_file_open(&f, blankname,
                                  APR_FOPEN_CREATE|APR_FOPEN_WRITE|APR_FOPEN_BUFFERED|APR_FOPEN_BINARY,
                                  APR_OS_DEFAULT, ctx->pool)) != APR_SUCCESS) {
            ctx->set_error(ctx, 500,  "failed to create file %s: %s",blankname, apr_strerror(ret,errmsg,120));
            mapcache_unlock_resource(ctx,ctx->config->locker,blankname, lock);
            return; /* we could not create the file */
          }

          bytes = (apr_size_t)tile->encoded_data->size;
          ret = apr_file_write(f,(void*)tile->encoded_data->buf,&bytes);
          if(ret != APR_SUCCESS) {
            ctx->set_error(ctx, 500,  "failed to write data to file %s (wrote %d of %d bytes): %s",blankname, (int)bytes, (int)tile->encoded_data->size, apr_strerror(ret,errmsg,120));
            mapcache_unlock_resource(ctx,ctx->config->locker,blankname, lock);
            return; /* we could not create the file */
          }

          if(bytes != tile->encoded_data->size) {
            ctx->set_error(ctx, 500,  "failed to write image data to %s, wrote %d of %d bytes", blankname, (int)bytes, (int)tile->encoded_data->size);
            mapcache_unlock_resource(ctx,ctx->config->locker,blankname, lock);
            return;
          }
          apr_file_close(f);
          mapcache_unlock_resource(ctx,ctx->config->locker,blankname, lock);
#ifdef DEBUG
          ctx->log(ctx,MAPCACHE_DEBUG,"created blank tile %s",blankname);
#endif
        }
      } else {
        apr_file_close(f);
      }


      /*
       * compute the relative path between tile and blank tile
       */
      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 */
  }

}