Merge pull request #3872 from mapnik/gdal_overview_fix_v3.0.x

GDAL Driver Overview Fix and Memory Reduction
This commit is contained in:
Blake Thompson 2018-03-30 15:44:07 -05:00 committed by GitHub
commit d4f95642ca
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 170 additions and 30 deletions

View file

@ -74,7 +74,7 @@ public:
static constexpr image_dtype dtype = T::id; static constexpr image_dtype dtype = T::id;
static constexpr std::size_t pixel_size = sizeof(pixel_type); static constexpr std::size_t pixel_size = sizeof(pixel_type);
private: private:
detail::image_dimensions<65535> dimensions_; detail::image_dimensions<4294836225> dimensions_;
detail::buffer buffer_; detail::buffer buffer_;
double offset_; double offset_;
double scaling_; double scaling_;

View file

@ -39,8 +39,10 @@ image_dimensions<max_size>::image_dimensions(int width, int height)
: width_(width), : width_(width),
height_(height) height_(height)
{ {
if (width < 0 || static_cast<std::size_t>(width) > max_size) throw std::runtime_error("Invalid width for image dimensions requested"); int64_t area = (int64_t)width * (int64_t)height;
if (height < 0 || static_cast<std::size_t>(height) > max_size) throw std::runtime_error("Invalid height for image dimensions requested"); if (width < 0) throw std::runtime_error("Invalid width for image dimensions requested");
if (height < 0) throw std::runtime_error("Invalid height for image dimensions requested");
if (area > max_size) throw std::runtime_error("Image area too large based on image dimensions");
} }
template <std::size_t max_size> template <std::size_t max_size>

View file

@ -83,6 +83,13 @@ gdal_datasource::gdal_datasource(parameters const& params)
shared_dataset_ = *params.get<mapnik::boolean_type>("shared", false); shared_dataset_ = *params.get<mapnik::boolean_type>("shared", false);
band_ = *params.get<mapnik::value_integer>("band", -1); band_ = *params.get<mapnik::value_integer>("band", -1);
// Maximum memory limitation for image will be simply based on the maximum
// area we allow for an image. The true memory footprint therefore will vary based
// on the type of imagery that exists. This is not the maximum size of an image
// on disk but rather the maximum size we will load into mapnik from GDAL.
// max_im_area based on 50 mb limit for RGBA
max_image_area_ = *params.get<mapnik::value_integer>("max_image_area", (50*1024*1024) / 4);
#if GDAL_VERSION_NUM >= 1600 #if GDAL_VERSION_NUM >= 1600
if (shared_dataset_) if (shared_dataset_)
{ {
@ -235,7 +242,8 @@ featureset_ptr gdal_datasource::features(query const& q) const
dx_, dx_,
dy_, dy_,
nodata_value_, nodata_value_,
nodata_tolerance_); nodata_tolerance_,
max_image_area_);
} }
featureset_ptr gdal_datasource::features_at_point(coord2d const& pt, double tol) const featureset_ptr gdal_datasource::features_at_point(coord2d const& pt, double tol) const
@ -254,5 +262,6 @@ featureset_ptr gdal_datasource::features_at_point(coord2d const& pt, double tol)
dx_, dx_,
dy_, dy_,
nodata_value_, nodata_value_,
nodata_tolerance_); nodata_tolerance_,
max_image_area_);
} }

View file

@ -68,6 +68,7 @@ private:
bool shared_dataset_; bool shared_dataset_;
boost::optional<double> nodata_value_; boost::optional<double> nodata_value_;
double nodata_tolerance_; double nodata_tolerance_;
int64_t max_image_area_;
}; };
#endif // GDAL_DATASOURCE_HPP #endif // GDAL_DATASOURCE_HPP

View file

@ -89,7 +89,8 @@ gdal_featureset::gdal_featureset(GDALDataset& dataset,
double dx, double dx,
double dy, double dy,
boost::optional<double> const& nodata, boost::optional<double> const& nodata,
double nodata_tolerance) double nodata_tolerance,
int64_t max_image_area)
: dataset_(dataset), : dataset_(dataset),
ctx_(std::make_shared<mapnik::context_type>()), ctx_(std::make_shared<mapnik::context_type>()),
band_(band), band_(band),
@ -102,6 +103,7 @@ gdal_featureset::gdal_featureset(GDALDataset& dataset,
nbands_(nbands), nbands_(nbands),
nodata_value_(nodata), nodata_value_(nodata),
nodata_tolerance_(nodata_tolerance), nodata_tolerance_(nodata_tolerance),
max_image_area_(max_image_area),
first_(true) first_(true)
{ {
ctx_->push("nodata"); ctx_->push("nodata");
@ -155,6 +157,8 @@ feature_ptr gdal_featureset::get_feature(mapnik::query const& q)
//size of resized output pixel in source image domain //size of resized output pixel in source image domain
double margin_x = 1.0 / (std::fabs(dx_) * std::get<0>(q.resolution())); double margin_x = 1.0 / (std::fabs(dx_) * std::get<0>(q.resolution()));
double margin_y = 1.0 / (std::fabs(dy_) * std::get<1>(q.resolution())); double margin_y = 1.0 / (std::fabs(dy_) * std::get<1>(q.resolution()));
MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: margin_x=" << margin_x;
MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: margin_y=" << margin_y;
if (margin_x < 1) if (margin_x < 1)
{ {
margin_x = 1.0; margin_x = 1.0;
@ -169,6 +173,10 @@ feature_ptr gdal_featureset::get_feature(mapnik::query const& q)
int y_off = rint(box.miny() - margin_y); int y_off = rint(box.miny() - margin_y);
int end_x = rint(box.maxx() + margin_x); int end_x = rint(box.maxx() + margin_x);
int end_y = rint(box.maxy() + margin_y); int end_y = rint(box.maxy() + margin_y);
MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: x_off=" << x_off;
MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: y_off=" << y_off;
MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: end_x=" << end_x;
MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: end_y=" << end_y;
//clip to available data //clip to available data
if (x_off < 0) if (x_off < 0)
@ -187,21 +195,134 @@ feature_ptr gdal_featureset::get_feature(mapnik::query const& q)
{ {
end_y = raster_height_; end_y = raster_height_;
} }
// width and height of the portion of the source image we are requesting
int width = end_x - x_off; int width = end_x - x_off;
int height = end_y - y_off; int height = end_y - y_off;
// In many cases we want GDAL to simply return the exact image so we
// can handle resampling internally in mapnik. In other cases such as
// when overviews exist or when the image allocated might be too large
// we want to utilize some resampling in GDAL instead.
int im_height = height;
int im_width = width;
double im_offset_x = x_off;
double im_offset_y = y_off;
int current_width = (int)raster_width_;
int current_height = (int)raster_height_;
// loop through overviews -- snap up in resolution to closest overview if necessary
// we find an image size that most resembles the resolution of our output image.
double width_res = std::get<0>(q.resolution());
double height_res = std::get<1>(q.resolution());
int res_adjusted_raster_width = static_cast<int>(std::floor(((double)raster_width_ * width_res) + .5));
int res_adjusted_raster_height = static_cast<int>(std::floor(((double)raster_height_ * height_res) + .5));
if (band_ > 0 && band_ < nbands_)
{
GDALRasterBand * band = dataset_.GetRasterBand(band_);
int band_overviews = band->GetOverviewCount();
if (band_overviews > 0)
{
for (int b = 0; b < band_overviews; b++)
{
GDALRasterBand * overview = band->GetOverview(b);
int overview_width = overview->GetXSize();
int overview_height = overview->GetYSize();
if ((overview_width < current_width || overview_height < current_height) &&
res_adjusted_raster_width <= overview_width &&
res_adjusted_raster_height <= overview_height)
{
current_width = overview_width;
current_height = overview_height;
}
}
}
}
else
{
for (int i = 0; i < nbands_; ++i)
{
GDALRasterBand * band = dataset_.GetRasterBand(i + 1);
int band_overviews = band->GetOverviewCount();
if (band_overviews > 0)
{
for (int b = 0; b < band_overviews; b++)
{
GDALRasterBand * overview = band->GetOverview(b);
int overview_width = overview->GetXSize();
int overview_height = overview->GetYSize();
if ((overview_width < current_width || overview_height < current_height) &&
res_adjusted_raster_width <= overview_width &&
res_adjusted_raster_height <= overview_height)
{
current_width = overview_width;
current_height = overview_height;
}
}
}
}
}
if (current_width != (int)raster_width_ || current_height != (int)raster_height_)
{
if (current_width != (int)raster_width_)
{
double ratio = (double)current_width / (double)raster_width_;
int adjusted_width = static_cast<int>(std::floor((ratio * im_width) + 0.5));
double adjusted_ratio = (double)adjusted_width / (double)im_width;
im_offset_x = adjusted_ratio * im_offset_x;
im_width = adjusted_width;
}
if (current_height != (int)raster_height_)
{
double ratio = (double)current_height / (double)raster_height_;
int adjusted_height = static_cast<int>(std::floor((ratio * im_height) + 0.5));
double adjusted_ratio = (double)adjusted_height / (double)im_height;
im_offset_y = adjusted_ratio * im_offset_y;
im_height = adjusted_height;
}
}
int64_t im_area = (int64_t)im_width * (int64_t)im_height;
if (im_area > max_image_area_)
{
int adjusted_width = static_cast<int>(std::round(std::sqrt(max_image_area_ * ((double)im_width / (double)im_height))));
int adjusted_height = static_cast<int>(std::round(std::sqrt(max_image_area_ * ((double)im_height / (double)im_width))));
if (adjusted_width < 1)
{
adjusted_width = 1;
}
if (adjusted_height < 1)
{
adjusted_height = 1;
}
double ratio_x = (double)adjusted_width / (double)im_width;
double ratio_y = (double)adjusted_height / (double)im_height;
im_offset_x = ratio_x * im_offset_x;
im_offset_y = ratio_y * im_offset_y;
im_width = adjusted_width;
im_height = adjusted_height;
current_width = static_cast<int>(std::floor((ratio_x * current_width) + 0.5));
current_height = static_cast<int>(std::floor((ratio_y * current_height) + 0.5));
}
//calculate actual box2d of returned raster //calculate actual box2d of returned raster
box2d<double> feature_raster_extent(x_off, y_off, x_off + width, y_off + height); view_transform t2(current_width, current_height, raster_extent_, 0, 0);
feature_raster_extent = t.backward(feature_raster_extent); box2d<double> feature_raster_extent(im_offset_x, im_offset_y, im_offset_x + im_width, im_offset_y + im_height);
MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: Feature Raster extent=" << feature_raster_extent;
feature_raster_extent = t2.backward(feature_raster_extent);
MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: Raster extent=" << raster_extent_; MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: Raster extent=" << raster_extent_;
MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: Feature Raster extent=" << feature_raster_extent;
MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: View extent=" << intersect; MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: View extent=" << intersect;
MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: Query resolution=" << std::get<0>(q.resolution()) << "," << std::get<1>(q.resolution()); MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: Query resolution=" << std::get<0>(q.resolution()) << "," << std::get<1>(q.resolution());
MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: StartX=" << x_off << " StartY=" << y_off << " Width=" << width << " Height=" << height; MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: StartX=" << x_off << " StartY=" << y_off << " Width=" << width << " Height=" << height;
MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: IM StartX=" << im_offset_x << " StartY=" << im_offset_y << " Width=" << im_width << " Height=" << im_height;
if (width > 0 && height > 0) if (width > 0 && height > 0)
{ {
MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: Image Size=(" << width << "," << height << ")";
MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: Requested Image Size=(" << width << "," << height << ")";
MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: Image Size=(" << im_width << "," << im_height << ")";
MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: Reading band=" << band_; MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: Reading band=" << band_;
if (band_ > 0) // we are querying a single band if (band_ > 0) // we are querying a single band
{ {
@ -217,7 +338,7 @@ feature_ptr gdal_featureset::get_feature(mapnik::query const& q)
{ {
case GDT_Byte: case GDT_Byte:
{ {
mapnik::image_gray8 image(width, height); mapnik::image_gray8 image(im_width, im_height);
image.set(std::numeric_limits<std::uint8_t>::max()); image.set(std::numeric_limits<std::uint8_t>::max());
raster_nodata = band->GetNoDataValue(&raster_has_nodata); raster_nodata = band->GetNoDataValue(&raster_has_nodata);
raster_io_error = band->RasterIO(GF_Read, x_off, y_off, width, height, raster_io_error = band->RasterIO(GF_Read, x_off, y_off, width, height,
@ -237,7 +358,7 @@ feature_ptr gdal_featureset::get_feature(mapnik::query const& q)
case GDT_Float64: case GDT_Float64:
case GDT_Float32: case GDT_Float32:
{ {
mapnik::image_gray32f image(width, height); mapnik::image_gray32f image(im_width, im_height);
image.set(std::numeric_limits<float>::max()); image.set(std::numeric_limits<float>::max());
raster_nodata = band->GetNoDataValue(&raster_has_nodata); raster_nodata = band->GetNoDataValue(&raster_has_nodata);
raster_io_error = band->RasterIO(GF_Read, x_off, y_off, width, height, raster_io_error = band->RasterIO(GF_Read, x_off, y_off, width, height,
@ -256,7 +377,7 @@ feature_ptr gdal_featureset::get_feature(mapnik::query const& q)
} }
case GDT_UInt16: case GDT_UInt16:
{ {
mapnik::image_gray16 image(width, height); mapnik::image_gray16 image(im_width, im_height);
image.set(std::numeric_limits<std::uint16_t>::max()); image.set(std::numeric_limits<std::uint16_t>::max());
raster_nodata = band->GetNoDataValue(&raster_has_nodata); raster_nodata = band->GetNoDataValue(&raster_has_nodata);
raster_io_error = band->RasterIO(GF_Read, x_off, y_off, width, height, raster_io_error = band->RasterIO(GF_Read, x_off, y_off, width, height,
@ -276,7 +397,7 @@ feature_ptr gdal_featureset::get_feature(mapnik::query const& q)
default: default:
case GDT_Int16: case GDT_Int16:
{ {
mapnik::image_gray16s image(width, height); mapnik::image_gray16s image(im_width, im_height);
image.set(std::numeric_limits<std::int16_t>::max()); image.set(std::numeric_limits<std::int16_t>::max());
raster_nodata = band->GetNoDataValue(&raster_has_nodata); raster_nodata = band->GetNoDataValue(&raster_has_nodata);
raster_io_error = band->RasterIO(GF_Read, x_off, y_off, width, height, raster_io_error = band->RasterIO(GF_Read, x_off, y_off, width, height,
@ -297,7 +418,7 @@ feature_ptr gdal_featureset::get_feature(mapnik::query const& q)
} }
else // working with all bands else // working with all bands
{ {
mapnik::image_rgba8 image(width, height); mapnik::image_rgba8 image(im_width, im_height);
image.set(std::numeric_limits<std::uint32_t>::max()); image.set(std::numeric_limits<std::uint32_t>::max());
for (int i = 0; i < nbands_; ++i) for (int i = 0; i < nbands_; ++i)
{ {

View file

@ -66,7 +66,8 @@ public:
double dx, double dx,
double dy, double dy,
boost::optional<double> const& nodata, boost::optional<double> const& nodata,
double nodata_tolerance); double nodata_tolerance,
int64_t max_image_area);
virtual ~gdal_featureset(); virtual ~gdal_featureset();
mapnik::feature_ptr next(); mapnik::feature_ptr next();
@ -85,6 +86,7 @@ private:
int nbands_; int nbands_;
boost::optional<double> nodata_value_; boost::optional<double> nodata_value_;
double nodata_tolerance_; double nodata_tolerance_;
int64_t max_image_area_;
bool first_; bool first_;
}; };

View file

@ -84,7 +84,7 @@ void buffer::swap(buffer & rhs)
std::swap(owns_, rhs.owns_); std::swap(owns_, rhs.owns_);
} }
template struct MAPNIK_DECL image_dimensions<65535>; template struct MAPNIK_DECL image_dimensions<4294836225>;
} // end ns detail } // end ns detail

View file

@ -129,20 +129,25 @@ void raster_colorizer::colorize(image_rgba8 & out, T const& in,
{ {
using image_type = T; using image_type = T;
using pixel_type = typename image_type::pixel_type; using pixel_type = typename image_type::pixel_type;
// TODO: assuming in/out have the same width/height for now
std::uint32_t * out_data = out.data(); const std::size_t width = std::min(in.width(), out.width());
pixel_type const* in_data = in.data(); const std::size_t height = std::min(in.height(), out.height());
int len = out.width() * out.height();
for (int i=0; i<len; ++i) for (std::size_t y = 0; y < height; ++y)
{ {
pixel_type value = in_data[i]; pixel_type const * in_row = in.get_row(y);
if (nodata && (std::fabs(value - *nodata) < epsilon_)) image_rgba8::pixel_type * out_row = out.get_row(y);
for (std::size_t x = 0; x < width; ++x)
{ {
out_data[i] = 0; // rgba(0,0,0,0) pixel_type val = in_row[x];
if (nodata && (std::fabs(val - *nodata) < epsilon_))
{
out_row[x] = 0; // rgba(0,0,0,0)
} }
else else
{ {
out_data[i] = get_color(value); out_row[x] = get_color(val);
}
} }
} }
} }

@ -1 +1 @@
Subproject commit da98ee10eb7ac8cd6d3b1441c3470dc5d3748c6e Subproject commit 18f610c211efa95c9d689e350d7ebe635d9c0b36

@ -1 +1 @@
Subproject commit 9e0887293e7f6195e1eecc6fed13ecb64e300259 Subproject commit 6ece97e8d43915527b4bd83507a9cd9ea9ef910e