further gdal nodata handling improvements - refs #2023

This commit is contained in:
Dane Springmeyer 2014-01-17 19:13:53 -08:00
parent ee7ca60442
commit 8faf5511f1
5 changed files with 75 additions and 43 deletions

View file

@ -14,6 +14,12 @@ Released ...
Summary: TODO Summary: TODO
- GDAL plugin: Added back support for user driven `nodata` on rgb(a) images (#2023)
- GDAL plugin: Allowed nodata to override alpha band if set on rgba images (#2023)
- GDAL plugin: Added `nodata_tolerance` option to set nearby pixels transparent (has similar effect to the `nearblack` program) (#2023)
- Added support for web fonts: .woff format (#2113) - Added support for web fonts: .woff format (#2113)
- Added missing support for `geometry-transform` in `line-pattern` and `polygon-pattern` symbolizers (#2065) - Added missing support for `geometry-transform` in `line-pattern` and `polygon-pattern` symbolizers (#2065)

View file

@ -77,7 +77,8 @@ inline GDALDataset* gdal_datasource::open_dataset() const
gdal_datasource::gdal_datasource(parameters const& params) gdal_datasource::gdal_datasource(parameters const& params)
: datasource(params), : datasource(params),
desc_(*params.get<std::string>("type"), "utf-8"), desc_(*params.get<std::string>("type"), "utf-8"),
nodata_value_(params.get<double>("nodata")) nodata_value_(params.get<double>("nodata")),
nodata_tolerance_(*params.get<double>("nodata_tolerance",1e-12))
{ {
MAPNIK_LOG_DEBUG(gdal) << "gdal_datasource: Initializing..."; MAPNIK_LOG_DEBUG(gdal) << "gdal_datasource: Initializing...";
@ -101,7 +102,7 @@ gdal_datasource::gdal_datasource(parameters const& params)
} }
shared_dataset_ = *params.get<mapnik::boolean>("shared", false); shared_dataset_ = *params.get<mapnik::boolean>("shared", false);
band_ = *params.get<int>("band", -1); band_ = *params.get<mapnik::value_integer>("band", -1);
GDALDataset *dataset = open_dataset(); GDALDataset *dataset = open_dataset();
@ -132,17 +133,29 @@ gdal_datasource::gdal_datasource(parameters const& params)
tr[3] = extent_.maxy(); tr[3] = extent_.maxy();
tr[4] = 0; tr[4] = 0;
tr[5] = -extent_.height() / (double)height_; tr[5] = -extent_.height() / (double)height_;
MAPNIK_LOG_DEBUG(gdal) << "gdal_datasource extent override gives Geotransform="
<< tr[0] << "," << tr[1] << ","
<< tr[2] << "," << tr[3] << ","
<< tr[4] << "," << tr[5];
} }
else else
{ {
dataset->GetGeoTransform(tr); if (dataset->GetGeoTransform(tr) != CPLE_None)
{
MAPNIK_LOG_DEBUG(gdal) << "gdal_datasource GetGeotransform failure gives="
<< tr[0] << "," << tr[1] << ","
<< tr[2] << "," << tr[3] << ","
<< tr[4] << "," << tr[5];
}
else
{
MAPNIK_LOG_DEBUG(gdal) << "gdal_datasource Geotransform="
<< tr[0] << "," << tr[1] << ","
<< tr[2] << "," << tr[3] << ","
<< tr[4] << "," << tr[5];
}
} }
MAPNIK_LOG_DEBUG(gdal) << "gdal_datasource Geotransform="
<< tr[0] << "," << tr[1] << ","
<< tr[2] << "," << tr[3] << ","
<< tr[4] << "," << tr[5];
// TODO - We should throw for true non-north up images, but the check // TODO - We should throw for true non-north up images, but the check
// below is clearly too restrictive. // below is clearly too restrictive.
// https://github.com/mapnik/mapnik/issues/970 // https://github.com/mapnik/mapnik/issues/970
@ -228,7 +241,8 @@ featureset_ptr gdal_datasource::features(query const& q) const
nbands_, nbands_,
dx_, dx_,
dy_, dy_,
nodata_value_)); nodata_value_,
nodata_tolerance_));
} }
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
@ -249,5 +263,6 @@ featureset_ptr gdal_datasource::features_at_point(coord2d const& pt, double tol)
nbands_, nbands_,
dx_, dx_,
dy_, dy_,
nodata_value_)); nodata_value_,
nodata_tolerance_));
} }

View file

@ -67,6 +67,7 @@ private:
int nbands_; int nbands_;
bool shared_dataset_; bool shared_dataset_;
boost::optional<double> nodata_value_; boost::optional<double> nodata_value_;
double nodata_tolerance_;
}; };
#endif // GDAL_DATASOURCE_HPP #endif // GDAL_DATASOURCE_HPP

View file

@ -32,7 +32,7 @@
// boost // boost
#include <boost/format.hpp> #include <boost/format.hpp>
#include <boost/make_shared.hpp> #include <boost/make_shared.hpp>
#include <boost/variant/apply_visitor.hpp>
// stl // stl
#include <cmath> #include <cmath>
@ -61,7 +61,8 @@ gdal_featureset::gdal_featureset(GDALDataset& dataset,
int nbands, int nbands,
double dx, double dx,
double dy, double dy,
boost::optional<double> const& nodata) boost::optional<double> const& nodata,
double nodata_tolerance)
: dataset_(dataset), : dataset_(dataset),
ctx_(boost::make_shared<mapnik::context_type>()), ctx_(boost::make_shared<mapnik::context_type>()),
band_(band), band_(band),
@ -73,6 +74,7 @@ gdal_featureset::gdal_featureset(GDALDataset& dataset,
dy_(dy), dy_(dy),
nbands_(nbands), nbands_(nbands),
nodata_value_(nodata), nodata_value_(nodata),
nodata_tolerance_(nodata_tolerance),
first_(true) first_(true)
{ {
ctx_->push("nodata"); ctx_->push("nodata");
@ -90,23 +92,8 @@ feature_ptr gdal_featureset::next()
if (first_) if (first_)
{ {
first_ = false; first_ = false;
MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: Next feature in Dataset=" << &dataset_; MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: Next feature in Dataset=" << &dataset_;
return boost::apply_visitor(query_dispatch(*this), gquery_);
query *q = boost::get<query>(&gquery_);
if (q)
{
return get_feature(*q);
}
else
{
coord2d *p = boost::get<coord2d>(&gquery_);
if (p)
{
return get_feature_at_point(*p);
}
}
// should never reach here
} }
return feature_ptr(); return feature_ptr();
} }
@ -201,9 +188,9 @@ feature_ptr gdal_featureset::get_feature(mapnik::query const& q)
int im_width = int(width_res * intersect.width() + 0.5); int im_width = int(width_res * intersect.width() + 0.5);
int im_height = int(height_res * intersect.height() + 0.5); int im_height = int(height_res * intersect.height() + 0.5);
double sym_downsample_factor = q.get_filter_factor(); double filter_factor = q.get_filter_factor();
im_width = int(im_width * sym_downsample_factor + 0.5); im_width = int(im_width * filter_factor + 0.5);
im_height = int(im_height * sym_downsample_factor + 0.5); im_height = int(im_height * filter_factor + 0.5);
// case where we need to avoid upsampling so that the // case where we need to avoid upsampling so that the
// image can be later scaled within raster_symbolizer // image can be later scaled within raster_symbolizer
@ -303,13 +290,13 @@ feature_ptr gdal_featureset::get_feature(mapnik::query const& q)
MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: Processing rgb bands..."; MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: Processing rgb bands...";
raster_nodata = red->GetNoDataValue(&raster_has_nodata); raster_nodata = red->GetNoDataValue(&raster_has_nodata);
GDALColorTable *color_table = red->GetColorTable(); GDALColorTable *color_table = red->GetColorTable();
if (!alpha && raster_has_nodata && !color_table) bool has_nodata = nodata_value_ || raster_has_nodata;
if (has_nodata && !color_table)
{ {
double apply_nodata = nodata_value_ ? *nodata_value_ : raster_nodata;
// read the data in and create an alpha channel from the nodata values // read the data in and create an alpha channel from the nodata values
// NOTE: we intentionally ignore user supplied nodata value since it only // TODO - we assume here the nodata value for the red band applies to all bands
// works for grayscale images or a single band (as a double) // more details about this at http://trac.osgeo.org/gdal/ticket/2734
// TODO - we assume here the nodata value for the red band applies to all band
// but that may not always be the case: http://trac.osgeo.org/gdal/ticket/2734
float* imageData = (float*)image.getBytes(); float* imageData = (float*)image.getBytes();
red->RasterIO(GF_Read, x_off, y_off, width, height, red->RasterIO(GF_Read, x_off, y_off, width, height,
imageData, image.width(), image.height(), imageData, image.width(), image.height(),
@ -317,7 +304,7 @@ feature_ptr gdal_featureset::get_feature(mapnik::query const& q)
int len = image.width() * image.height(); int len = image.width() * image.height();
for (int i = 0; i < len; ++i) for (int i = 0; i < len; ++i)
{ {
if (std::fabs(raster_nodata - imageData[i]) < 1e-12) if (std::fabs(apply_nodata - imageData[i]) < nodata_tolerance_)
{ {
*reinterpret_cast<unsigned *>(&imageData[i]) = 0; *reinterpret_cast<unsigned *>(&imageData[i]) = 0;
} }
@ -352,7 +339,7 @@ feature_ptr gdal_featureset::get_feature(mapnik::query const& q)
int len = image.width() * image.height(); int len = image.width() * image.height();
for (int i = 0; i < len; ++i) for (int i = 0; i < len; ++i)
{ {
if (std::fabs(apply_nodata - imageData[i]) < 1e-12) if (std::fabs(apply_nodata - imageData[i]) < nodata_tolerance_)
{ {
*reinterpret_cast<unsigned *>(&imageData[i]) = 0; *reinterpret_cast<unsigned *>(&imageData[i]) = 0;
} }
@ -396,12 +383,15 @@ feature_ptr gdal_featureset::get_feature(mapnik::query const& q)
if (alpha) if (alpha)
{ {
MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: processing alpha band..."; MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: processing alpha band...";
if (raster_has_nodata) if (!raster_has_nodata)
{ {
MAPNIK_LOG_ERROR(gdal) << "warning: alpha channel being used instead of nodata value"; alpha->RasterIO(GF_Read, x_off, y_off, width, height, image.getBytes() + 3,
image.width(), image.height(), GDT_Byte, 4, 4 * image.width());
}
else
{
MAPNIK_LOG_ERROR(gdal) << "warning: nodata value (" << raster_nodata << ") used to set transparency instead of alpha band";
} }
alpha->RasterIO(GF_Read, x_off, y_off, width, height, image.getBytes() + 3,
image.width(), image.height(), GDT_Byte, 4, 4 * image.width());
} }
} }
// set nodata value to be used in raster colorizer // set nodata value to be used in raster colorizer

View file

@ -39,6 +39,24 @@ typedef boost::variant<mapnik::query, mapnik::coord2d> gdal_query;
class gdal_featureset : public mapnik::Featureset class gdal_featureset : public mapnik::Featureset
{ {
struct query_dispatch : public boost::static_visitor<mapnik::feature_ptr>
{
query_dispatch( gdal_featureset & featureset)
: featureset_(featureset) {}
mapnik::feature_ptr operator() (mapnik::query const& q) const
{
return featureset_.get_feature(q);
}
mapnik::feature_ptr operator() (mapnik::coord2d const& p) const
{
return featureset_.get_feature_at_point(p);
}
gdal_featureset & featureset_;
};
public: public:
gdal_featureset(GDALDataset& dataset, gdal_featureset(GDALDataset& dataset,
int band, int band,
@ -49,7 +67,8 @@ public:
int nbands, int nbands,
double dx, double dx,
double dy, double dy,
boost::optional<double> const& nodata); boost::optional<double> const& nodata,
double nodata_tolerance);
virtual ~gdal_featureset(); virtual ~gdal_featureset();
mapnik::feature_ptr next(); mapnik::feature_ptr next();
@ -72,6 +91,7 @@ private:
double dy_; double dy_;
int nbands_; int nbands_;
boost::optional<double> nodata_value_; boost::optional<double> nodata_value_;
double nodata_tolerance_;
bool first_; bool first_;
}; };