support nodata for paletted images and allow user to set nodata on-the-fly - closes #1160 and #1161 - refs #688 and refs #730 and refs #50 and refs #1018
This commit is contained in:
parent
a7e150a593
commit
46b16c917e
11 changed files with 196 additions and 16 deletions
|
@ -9,6 +9,10 @@ For a complete change history, see the SVN log.
|
||||||
|
|
||||||
## Mapnik 2.1.0
|
## Mapnik 2.1.0
|
||||||
|
|
||||||
|
- GDAL: allow setting nodata value on the fly (will override value if nodata is set in data) (#1161)
|
||||||
|
|
||||||
|
- GDAL: respect nodata for paletted/colormapped images (#1160)
|
||||||
|
|
||||||
- PostGIS: the primary key, for tables containing one, is now auto-detected allowing for globally unique feature id values (#804)
|
- PostGIS: the primary key, for tables containing one, is now auto-detected allowing for globally unique feature id values (#804)
|
||||||
|
|
||||||
- Fix Markers rendering so that ellipse height/width units are pixels (previously were unintentially radii)
|
- Fix Markers rendering so that ellipse height/width units are pixels (previously were unintentially radii)
|
||||||
|
|
|
@ -78,7 +78,8 @@ inline GDALDataset* gdal_datasource::open_dataset() const
|
||||||
gdal_datasource::gdal_datasource(parameters const& params, bool bind)
|
gdal_datasource::gdal_datasource(parameters const& params, bool bind)
|
||||||
: datasource(params),
|
: datasource(params),
|
||||||
desc_(*params.get<std::string>("type"), "utf-8"),
|
desc_(*params.get<std::string>("type"), "utf-8"),
|
||||||
filter_factor_(*params_.get<double>("filter_factor", 0.0))
|
filter_factor_(*params_.get<double>("filter_factor", 0.0)),
|
||||||
|
nodata_value_(params_.get<double>("nodata"))
|
||||||
{
|
{
|
||||||
#ifdef MAPNIK_DEBUG
|
#ifdef MAPNIK_DEBUG
|
||||||
std::clog << "GDAL Plugin: Initializing..." << std::endl;
|
std::clog << "GDAL Plugin: Initializing..." << std::endl;
|
||||||
|
@ -242,7 +243,8 @@ featureset_ptr gdal_datasource::features(query const& q) const
|
||||||
nbands_,
|
nbands_,
|
||||||
dx_,
|
dx_,
|
||||||
dy_,
|
dy_,
|
||||||
filter_factor_));
|
filter_factor_,
|
||||||
|
nodata_value_));
|
||||||
}
|
}
|
||||||
|
|
||||||
featureset_ptr gdal_datasource::features_at_point(coord2d const& pt) const
|
featureset_ptr gdal_datasource::features_at_point(coord2d const& pt) const
|
||||||
|
@ -261,5 +263,6 @@ featureset_ptr gdal_datasource::features_at_point(coord2d const& pt) const
|
||||||
nbands_,
|
nbands_,
|
||||||
dx_,
|
dx_,
|
||||||
dy_,
|
dy_,
|
||||||
filter_factor_));
|
filter_factor_,
|
||||||
|
nodata_value_));
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,7 @@ private:
|
||||||
mutable int nbands_;
|
mutable int nbands_;
|
||||||
mutable bool shared_dataset_;
|
mutable bool shared_dataset_;
|
||||||
double filter_factor_;
|
double filter_factor_;
|
||||||
|
boost::optional<double> nodata_value_;
|
||||||
inline GDALDataset* open_dataset() const;
|
inline GDALDataset* open_dataset() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,8 @@ gdal_featureset::gdal_featureset(GDALDataset& dataset,
|
||||||
int nbands,
|
int nbands,
|
||||||
double dx,
|
double dx,
|
||||||
double dy,
|
double dy,
|
||||||
double filter_factor)
|
double filter_factor,
|
||||||
|
boost::optional<double> const& nodata)
|
||||||
: dataset_(dataset),
|
: dataset_(dataset),
|
||||||
ctx_(boost::make_shared<mapnik::context_type>()),
|
ctx_(boost::make_shared<mapnik::context_type>()),
|
||||||
band_(band),
|
band_(band),
|
||||||
|
@ -65,6 +66,7 @@ gdal_featureset::gdal_featureset(GDALDataset& dataset,
|
||||||
dy_(dy),
|
dy_(dy),
|
||||||
nbands_(nbands),
|
nbands_(nbands),
|
||||||
filter_factor_(filter_factor),
|
filter_factor_(filter_factor),
|
||||||
|
nodata_value_(nodata),
|
||||||
first_(true)
|
first_(true)
|
||||||
{
|
{
|
||||||
ctx_->push("value");
|
ctx_->push("value");
|
||||||
|
@ -241,8 +243,17 @@ feature_ptr gdal_featureset::get_feature(mapnik::query const& q)
|
||||||
|
|
||||||
float* imageData = (float*)image.getBytes();
|
float* imageData = (float*)image.getBytes();
|
||||||
GDALRasterBand * band = dataset_.GetRasterBand(band_);
|
GDALRasterBand * band = dataset_.GetRasterBand(band_);
|
||||||
int hasNoData;
|
int hasNoData(0);
|
||||||
double nodata = band->GetNoDataValue(&hasNoData);
|
double nodata(0);
|
||||||
|
if (nodata_value_)
|
||||||
|
{
|
||||||
|
hasNoData = 1;
|
||||||
|
nodata = *nodata_value_;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nodata = band->GetNoDataValue(&hasNoData);
|
||||||
|
}
|
||||||
band->RasterIO(GF_Read, x_off, y_off, width, height,
|
band->RasterIO(GF_Read, x_off, y_off, width, height,
|
||||||
imageData, image.width(), image.height(),
|
imageData, image.width(), image.height(),
|
||||||
GDT_Float32, 0, 0);
|
GDT_Float32, 0, 0);
|
||||||
|
@ -340,8 +351,21 @@ feature_ptr gdal_featureset::get_feature(mapnik::query const& q)
|
||||||
#ifdef MAPNIK_DEBUG
|
#ifdef MAPNIK_DEBUG
|
||||||
std::clog << "GDAL Plugin: processing rgb bands..." << std::endl;
|
std::clog << "GDAL Plugin: processing rgb bands..." << std::endl;
|
||||||
#endif
|
#endif
|
||||||
int hasNoData;
|
int hasNoData(0);
|
||||||
float nodata = red->GetNoDataValue(&hasNoData);
|
double nodata(0);
|
||||||
|
if (nodata_value_)
|
||||||
|
{
|
||||||
|
hasNoData = 1;
|
||||||
|
nodata = *nodata_value_;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nodata = red->GetNoDataValue(&hasNoData);
|
||||||
|
}
|
||||||
|
if (hasNoData)
|
||||||
|
{
|
||||||
|
feature->put("NODATA",nodata);
|
||||||
|
}
|
||||||
GDALColorTable *color_table = red->GetColorTable();
|
GDALColorTable *color_table = red->GetColorTable();
|
||||||
|
|
||||||
if (! alpha && hasNoData && ! color_table)
|
if (! alpha && hasNoData && ! color_table)
|
||||||
|
@ -380,12 +404,25 @@ feature_ptr gdal_featureset::get_feature(mapnik::query const& q)
|
||||||
#ifdef MAPNIK_DEBUG
|
#ifdef MAPNIK_DEBUG
|
||||||
std::clog << "GDAL Plugin: processing gray band..." << std::endl;
|
std::clog << "GDAL Plugin: processing gray band..." << std::endl;
|
||||||
#endif
|
#endif
|
||||||
int hasNoData;
|
int hasNoData(0);
|
||||||
float nodata = grey->GetNoDataValue(&hasNoData);
|
double nodata(0);
|
||||||
|
if (nodata_value_)
|
||||||
|
{
|
||||||
|
hasNoData = 1;
|
||||||
|
nodata = *nodata_value_;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nodata = grey->GetNoDataValue(&hasNoData);
|
||||||
|
}
|
||||||
GDALColorTable* color_table = grey->GetColorTable();
|
GDALColorTable* color_table = grey->GetColorTable();
|
||||||
|
|
||||||
if (hasNoData && ! color_table)
|
if (hasNoData && ! color_table)
|
||||||
{
|
{
|
||||||
|
if (hasNoData)
|
||||||
|
{
|
||||||
|
feature->put("NODATA",nodata);
|
||||||
|
}
|
||||||
#ifdef MAPNIK_DEBUG
|
#ifdef MAPNIK_DEBUG
|
||||||
std::clog << "\tno data value for layer: " << nodata << std::endl;
|
std::clog << "\tno data value for layer: " << nodata << std::endl;
|
||||||
#endif
|
#endif
|
||||||
|
@ -422,23 +459,41 @@ feature_ptr gdal_featureset::get_feature(mapnik::query const& q)
|
||||||
#ifdef MAPNIK_DEBUG
|
#ifdef MAPNIK_DEBUG
|
||||||
std::clog << "GDAL Plugin: Loading colour table..." << std::endl;
|
std::clog << "GDAL Plugin: Loading colour table..." << std::endl;
|
||||||
#endif
|
#endif
|
||||||
|
unsigned nodata_value = static_cast<unsigned>(nodata);
|
||||||
|
if (hasNoData)
|
||||||
|
{
|
||||||
|
feature->put("NODATA",static_cast<int>(nodata_value));
|
||||||
|
}
|
||||||
for (unsigned y = 0; y < image.height(); ++y)
|
for (unsigned y = 0; y < image.height(); ++y)
|
||||||
{
|
{
|
||||||
unsigned int* row = image.getRow(y);
|
unsigned int* row = image.getRow(y);
|
||||||
for (unsigned x = 0; x < image.width(); ++x)
|
for (unsigned x = 0; x < image.width(); ++x)
|
||||||
{
|
{
|
||||||
unsigned value = row[x] & 0xff;
|
unsigned value = row[x] & 0xff;
|
||||||
|
if (hasNoData && (value == nodata_value))
|
||||||
|
{
|
||||||
|
// make no data fully alpha
|
||||||
|
row[x] = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
const GDALColorEntry *ce = color_table->GetColorEntry(value);
|
const GDALColorEntry *ce = color_table->GetColorEntry(value);
|
||||||
if (ce )
|
if (ce)
|
||||||
{
|
{
|
||||||
// TODO - big endian support
|
// TODO - big endian support
|
||||||
row[x] = (ce->c4 << 24)| (ce->c3 << 16) | (ce->c2 << 8) | (ce->c1) ;
|
row[x] = (ce->c4 << 24)| (ce->c3 << 16) | (ce->c2 << 8) | (ce->c1) ;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// make lacking color entry fully alpha
|
||||||
|
// note - gdal_translate makes black
|
||||||
|
row[x] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (alpha)
|
if (alpha)
|
||||||
{
|
{
|
||||||
#ifdef MAPNIK_DEBUG
|
#ifdef MAPNIK_DEBUG
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
|
|
||||||
// boost
|
// boost
|
||||||
#include <boost/variant.hpp>
|
#include <boost/variant.hpp>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
|
||||||
class GDALDataset;
|
class GDALDataset;
|
||||||
class GDALRasterBand;
|
class GDALRasterBand;
|
||||||
|
@ -47,7 +48,8 @@ public:
|
||||||
int nbands,
|
int nbands,
|
||||||
double dx,
|
double dx,
|
||||||
double dy,
|
double dy,
|
||||||
double filter_factor);
|
double filter_factor,
|
||||||
|
boost::optional<double> const& nodata);
|
||||||
virtual ~gdal_featureset();
|
virtual ~gdal_featureset();
|
||||||
mapnik::feature_ptr next();
|
mapnik::feature_ptr next();
|
||||||
private:
|
private:
|
||||||
|
@ -67,6 +69,7 @@ private:
|
||||||
double dy_;
|
double dy_;
|
||||||
int nbands_;
|
int nbands_;
|
||||||
double filter_factor_;
|
double filter_factor_;
|
||||||
|
boost::optional<double> nodata_value_;
|
||||||
bool first_;
|
bool first_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
16
tests/data/good_maps/tiff_colortable.xml
Normal file
16
tests/data/good_maps/tiff_colortable.xml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Map srs="+init=epsg:32630" background-color="rgb(255,255,255)">
|
||||||
|
<Style name="2011_5km_vrt_nodata_style">
|
||||||
|
<Rule>
|
||||||
|
<RasterSymbolizer/>
|
||||||
|
</Rule>
|
||||||
|
</Style>
|
||||||
|
<Layer name="2011_5km_vrt_nodata" srs="+init=epsg:32630">
|
||||||
|
<StyleName>2011_5km_vrt_nodata_style</StyleName>
|
||||||
|
<Datasource>
|
||||||
|
<Parameter name="file">../raster/dataraster.tif</Parameter>
|
||||||
|
<Parameter name="nodata">20</Parameter>
|
||||||
|
<Parameter name="type">gdal</Parameter>
|
||||||
|
</Datasource>
|
||||||
|
</Layer>
|
||||||
|
</Map>
|
15
tests/data/good_maps/vrt_colortable.xml
Normal file
15
tests/data/good_maps/vrt_colortable.xml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Map srs="+init=epsg:32630" background-color="rgb(255,255,255)">
|
||||||
|
<Style name="2011_5km_vrt_nodata_style">
|
||||||
|
<Rule>
|
||||||
|
<RasterSymbolizer/>
|
||||||
|
</Rule>
|
||||||
|
</Style>
|
||||||
|
<Layer name="2011_5km_vrt_nodata" srs="+init=epsg:32630">
|
||||||
|
<StyleName>2011_5km_vrt_nodata_style</StyleName>
|
||||||
|
<Datasource>
|
||||||
|
<Parameter name="file">../raster/dataraster.vrt</Parameter>
|
||||||
|
<Parameter name="type">gdal</Parameter>
|
||||||
|
</Datasource>
|
||||||
|
</Layer>
|
||||||
|
</Map>
|
45
tests/data/raster/dataraster.vrt
Normal file
45
tests/data/raster/dataraster.vrt
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
<VRTDataset rasterXSize="2283" rasterYSize="1913">
|
||||||
|
<SRS>PROJCS["WGS 84 / UTM zone 30N",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433],AUTHORITY["EPSG","4326"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-3],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AUTHORITY["EPSG","32630"]]</SRS>
|
||||||
|
<GeoTransform> -1.4637000000000000e+04, 5.0000000000000000e+02, 0.0000000000000000e+00, 4.8596780000000000e+06, 0.0000000000000000e+00, -5.0000000000000000e+02</GeoTransform>
|
||||||
|
<VRTRasterBand dataType="Int16" band="1">
|
||||||
|
<NoDataValue>-9.99000000000000E+02</NoDataValue>
|
||||||
|
<HideNoDataValue>0</HideNoDataValue>
|
||||||
|
<ColorInterp>Palette</ColorInterp>
|
||||||
|
<ColorTable>
|
||||||
|
<Entry c1="0" c2="0" c3="0" c4="0"/>
|
||||||
|
<Entry c1="0" c2="0" c3="255"/>
|
||||||
|
<Entry c1="0" c2="255" c3="0"/>
|
||||||
|
<Entry c1="255" c2="0" c3="0"/>
|
||||||
|
<Entry c1="128" c2="128" c3="128"/>
|
||||||
|
<Entry c1="128" c2="128" c3="128"/>
|
||||||
|
<Entry c1="128" c2="128" c3="128"/>
|
||||||
|
<Entry c1="128" c2="128" c3="128"/>
|
||||||
|
<Entry c1="128" c2="128" c3="128"/>
|
||||||
|
<Entry c1="128" c2="128" c3="128"/>
|
||||||
|
<Entry c1="128" c2="128" c3="128"/>
|
||||||
|
<Entry c1="128" c2="128" c3="128"/>
|
||||||
|
<Entry c1="128" c2="128" c3="128"/>
|
||||||
|
<Entry c1="128" c2="128" c3="128"/>
|
||||||
|
<Entry c1="128" c2="128" c3="128"/>
|
||||||
|
<Entry c1="128" c2="128" c3="128"/>
|
||||||
|
<Entry c1="128" c2="128" c3="128"/>
|
||||||
|
<Entry c1="128" c2="128" c3="128"/>
|
||||||
|
<Entry c1="128" c2="128" c3="128"/>
|
||||||
|
<Entry c1="128" c2="128" c3="128"/>
|
||||||
|
<Entry c1="128" c2="128" c3="128"/>
|
||||||
|
<Entry c1="128" c2="128" c3="128"/>
|
||||||
|
<Entry c1="128" c2="128" c3="128"/>
|
||||||
|
<Entry c1="0" c2="0" c3="255"/>
|
||||||
|
<Entry c1="0" c2="255" c3="0"/>
|
||||||
|
<Entry c1="255" c2="0" c3="0"/>
|
||||||
|
</ColorTable>
|
||||||
|
<ComplexSource>
|
||||||
|
<SourceFilename relativeToVRT="1">dataraster.tif</SourceFilename>
|
||||||
|
<SourceBand>1</SourceBand>
|
||||||
|
<SourceProperties RasterXSize="2283" RasterYSize="1913" DataType="Int16" BlockXSize="256" BlockYSize="256" />
|
||||||
|
<SrcRect xOff="0" yOff="0" xSize="2283" ySize="1913" />
|
||||||
|
<DstRect xOff="0" yOff="0" xSize="2283" ySize="1913" />
|
||||||
|
<NODATA>-999</NODATA>
|
||||||
|
</ComplexSource>
|
||||||
|
</VRTRasterBand>
|
||||||
|
</VRTDataset>
|
BIN
tests/python_tests/images/support/tif_colortable.png
Normal file
BIN
tests/python_tests/images/support/tif_colortable.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
BIN
tests/python_tests/images/support/vrt_colortable.png
Normal file
BIN
tests/python_tests/images/support/vrt_colortable.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.1 KiB |
38
tests/python_tests/raster_colormapped_test.py
Normal file
38
tests/python_tests/raster_colormapped_test.py
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
#coding=utf8
|
||||||
|
import os
|
||||||
|
import mapnik
|
||||||
|
from utilities import execution_path
|
||||||
|
from nose.tools import *
|
||||||
|
|
||||||
|
def setup():
|
||||||
|
# All of the paths used are relative, if we run the tests
|
||||||
|
# from another directory we need to chdir()
|
||||||
|
os.chdir(execution_path('.'))
|
||||||
|
|
||||||
|
def test_vrt_rendering():
|
||||||
|
m = mapnik.Map(512,512)
|
||||||
|
mapnik.load_map(m,'../data/good_maps/vrt_colortable.xml')
|
||||||
|
m.zoom_all()
|
||||||
|
im = mapnik.Image(512,512)
|
||||||
|
mapnik.render(m,im)
|
||||||
|
actual = '/tmp/vrt_colortable.png'
|
||||||
|
expected = 'images/support/vrt_colortable.png'
|
||||||
|
im.save(actual)
|
||||||
|
expected_im = mapnik.Image.open(expected)
|
||||||
|
eq_(im.tostring(),expected_im.tostring(), 'failed comparing actual (%s) and expected(%s)' % (actual,'tests/python_tests/'+ expected))
|
||||||
|
|
||||||
|
def test_tif_rendering_nodata():
|
||||||
|
m = mapnik.Map(512,512)
|
||||||
|
mapnik.load_map(m,'../data/good_maps/tiff_colortable.xml')
|
||||||
|
m.zoom_all()
|
||||||
|
im = mapnik.Image(512,512)
|
||||||
|
mapnik.render(m,im)
|
||||||
|
actual = '/tmp/tif_colortable.png'
|
||||||
|
expected = 'images/support/tif_colortable.png'
|
||||||
|
im.save(actual)
|
||||||
|
expected_im = mapnik.Image.open(expected)
|
||||||
|
eq_(im.tostring(),expected_im.tostring(), 'failed comparing actual (%s) and expected(%s)' % (actual,'tests/python_tests/'+ expected))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
setup()
|
||||||
|
[eval(run)() for run in dir() if 'test_' in run]
|
Loading…
Reference in a new issue