apply major refactor of RasterColorizer by ben moores from https://github.com/BenMoores/mapnik-trunk/wiki/RasterColorizer - closes #523

This commit is contained in:
Dane Springmeyer 2011-05-04 00:20:17 +00:00
parent 2c275729b1
commit d05b0c539f
14 changed files with 821 additions and 383 deletions

View file

@ -27,38 +27,40 @@
using mapnik::raster_colorizer;
using mapnik::raster_colorizer_ptr;
using mapnik::color_band;
using mapnik::color_bands;
using mapnik::colorizer_stop;
using mapnik::colorizer_stops;
using mapnik::colorizer_mode_enum;
using mapnik::color;
using mapnik::COLORIZER_INHERIT;
using mapnik::COLORIZER_LINEAR;
using mapnik::COLORIZER_DISCRETE;
using mapnik::COLORIZER_EXACT;
namespace {
void append_band1(raster_colorizer_ptr & rc, color_band b)
void add_stop(raster_colorizer_ptr & rc, colorizer_stop & stop)
{
rc->append_band(b);
rc->add_stop(stop);
}
void append_band2(raster_colorizer_ptr & rc, color_band b, unsigned m)
{
rc->append_band(b, m);
void add_stop2(raster_colorizer_ptr & rc, float v) {
colorizer_stop stop(v, rc->get_default_mode(), rc->get_default_color());
rc->add_stop(stop);
}
void append_band3(raster_colorizer_ptr & rc, float v, color c)
{
rc->append_band(v, c);
void add_stop3(raster_colorizer_ptr &rc, float v, color c) {
colorizer_stop stop(v, rc->get_default_mode(), c);
rc->add_stop(stop);
}
void append_band4(raster_colorizer_ptr & rc, float v, color c, unsigned m)
{
rc->append_band(v, c, m);
void add_stop4(raster_colorizer_ptr &rc, float v, colorizer_mode_enum m) {
colorizer_stop stop(v, m, rc->get_default_color());
rc->add_stop(stop);
}
void append_band5(raster_colorizer_ptr & rc, float v, float vm, color c, unsigned m)
{
rc->append_band(v, vm, c, m);
void add_stop5(raster_colorizer_ptr &rc, float v, colorizer_mode_enum m, color c) {
colorizer_stop stop(v, m, c);
rc->add_stop(stop);
}
void append_band6(raster_colorizer_ptr & rc, float v, float vm, color c)
colorizer_stops const& get_stops(raster_colorizer_ptr & rc)
{
rc->append_band(v, vm, c);
}
color_bands const& get_color_bands(raster_colorizer_ptr & rc)
{
return rc->get_color_bands();
return rc->get_stops();
}
}
@ -66,121 +68,135 @@ void export_raster_colorizer()
{
using namespace boost::python;
class_<raster_colorizer,raster_colorizer_ptr>("RasterColorizer", init<>("Default ctor."))
.add_property("bands",make_function
(get_color_bands,
return_value_policy<reference_existing_object>()))
.def("append_band", append_band1,
(arg("color_band")),
"Append a color band to the raster colorizer.\n"
"\n"
"Usage:\n"
">>> colorizer = mapnik.ColorBand()\n"
">>> color = mapnik.Color(\"#0044cc\")\n"
">>> color_band = mapnik.ColorBand(3, color)\n"
">>> colorizer.append_band(color_band)\n"
class_<raster_colorizer,raster_colorizer_ptr>("RasterColorizer",
"A Raster Colorizer object.",
init<colorizer_mode_enum, color>(args("default_mode","default_color"))
)
.def("append_band", append_band2,
(arg("color_band"), arg("midpoints")),
"Append a color band to the raster colorizer with midpoints "
"lineally interpolated color bands between this one and the "
"previous one.\n"
"\n"
"Usage:\n"
">>> colorizer = mapnik.ColorBand()\n"
">>> color = mapnik.Color(\"#0044cc\")\n"
">>> color_band = mapnik.ColorBand(3, color)\n"
">>> colorizer.append_band(color_band, 1)\n"
)
.def("append_band", append_band3,
(arg("value"), arg("color")),
"Create and append a color band to color the range "
"[value, next_val) where next_val is the next band's color or "
"inifinity if there is no next band.\n"
"Usage:\n"
">>> colorizer = mapnik.RasterColorizer()\n"
">>> color = mapnik.Color(\"#0044cc\")\n"
">>> colorizer.append_band(30, color)\n"
)
.def("append_band", append_band4,
(arg("value"), arg("color"), arg("midpoints")),
"Create and append a color band to the raster colorizer with "
"midpoints lineally interpolated color bands between this one and "
"the previous one.\n"
"color will be applied to all values in the "
"range [value, next_val) where next_val is the next band's color "
"or infinity if there is no next band\n"
.def(init<>())
.add_property("default_color",
make_function(&raster_colorizer::get_default_color, return_value_policy<reference_existing_object>()),
&raster_colorizer::set_default_color,
"The default color for stops added without a color (mapnik.Color).\n")
.add_property("default_mode",
&raster_colorizer::get_default_mode_enum,
&raster_colorizer::set_default_mode_enum,
"The default mode (mapnik.ColorizerMode).\n"
"\n"
"If a stop is added without a mode, then it will inherit this default mode\n")
.add_property("stops",
make_function(get_stops,return_value_policy<reference_existing_object>()),
"The list of stops this RasterColorizer contains\n")
.add_property("epsilon",
&raster_colorizer::get_epsilon,
&raster_colorizer::set_epsilon,
"Comparison epsilon value for exact mode\n"
"\n"
"When comparing values in exact mode, values need only be within epsilon to match.\n")
.def("add_stop", add_stop,
(arg("ColorizerStop")),
"Add a colorizer stop to the raster colorizer.\n"
"\n"
"Usage:\n"
">>> colorizer = mapnik.RasterColorizer()\n"
">>> color = mapnik.Color(\"#0044cc\")\n"
">>> colorizer.append_band(30, color, 4)\n"
">>> stop = mapnik.ColorizerStop(3, mapnik.COLORIZER_INHERIT, color)\n"
">>> colorizer.add_stop(stop)\n"
)
.def("append_band", append_band5,
(arg("value"), arg("value_max"), arg("color"), arg("midpoints")),
"Create and append a color band to the raster colorizer with "
"midpoints lineally interpolated color bands between this one and "
"the previous one.\n"
"color will be applied to all values in the "
"range [value, next_val) where next_val is the next band's color "
"or value_max if there is no next band\n"
"\n"
.def("add_stop", add_stop2,
(arg("value")),
"Add a colorizer stop to the raster colorizer, using the default mode and color.\n"
"\n"
"Usage:\n"
">>> colorizer = mapnik.RasterColorizer()\n"
">>> color = mapnik.Color(\"#0044cc\")\n"
">>> colorizer.append_band(30, 40, color, 4)\n"
">>> default_color = mapnik.Color(\"#0044cc\")\n"
">>> colorizer = mapnik.RasterColorizer(mapnik2.COLORIZER_LINEAR, default_color)\n"
">>> colorizer.add_stop(100)\n"
)
.def("append_band", append_band6,
(arg("value"), arg("value_max"), arg("color")),
"Create and append a color band to color the range "
"[value, next_val) where next_val is the next band's color or "
"value_max if there is no next band.\n"
.def("add_stop", add_stop3,
(arg("value")),
"Add a colorizer stop to the raster colorizer, using the default mode.\n"
"\n"
"Usage:\n"
">>> colorizer = mapnik.RasterColorizer()\n"
">>> color = mapnik.Color(\"#0044cc\")\n"
">>> colorizer.append_band(30, 40, color)\n"
">>> default_color = mapnik.Color(\"#0044cc\")\n"
">>> colorizer = mapnik.RasterColorizer(mapnik2.COLORIZER_LINEAR, default_color)\n"
">>> colorizer.add_stop(100, mapnik.Color(\"#123456\"))\n"
)
.def("add_stop", add_stop4,
(arg("value")),
"Add a colorizer stop to the raster colorizer, using the default color.\n"
"\n"
"Usage:\n"
">>> default_color = mapnik.Color(\"#0044cc\")\n"
">>> colorizer = mapnik.RasterColorizer(mapnik2.COLORIZER_LINEAR, default_color)\n"
">>> colorizer.add_stop(100, mapnik2.COLORIZER_EXACT)\n"
)
.def("add_stop", add_stop5,
(arg("value")),
"Add a colorizer stop to the raster colorizer.\n"
"\n"
"Usage:\n"
">>> default_color = mapnik.Color(\"#0044cc\")\n"
">>> colorizer = mapnik.RasterColorizer(mapnik2.COLORIZER_LINEAR, default_color)\n"
">>> colorizer.add_stop(100, mapnik.COLORIZER_DISCRETE, mapnik.Color(\"#112233\"))\n"
)
.def("get_color", &raster_colorizer::get_color,
"Get the color assigned to a certain value in raster data.\n"
"By default, returns Color(\"transparent\")\n"
"\n"
"Usage:\n"
">>> colorizer = mapnik.RasterColorizer()\n"
">>> color = mapnik.Color(\"#0044cc\")\n"
">>> colorizer.append_band(30, 40, color)\n"
">>> colorizer.get_color(35)\n"
"Color('#0044cc')\n"
">>> colorizer.add_stop(0, mapnik2.COLORIZER_DISCRETE, mapnik.Color(\"#000000\"))\n"
">>> colorizer.add_stop(100, mapnik2.COLORIZER_DISCRETE, mapnik.Color(\"#0E0A06\"))\n"
">>> colorizer.get_color(50)\n"
"Color('#070503')\n"
)
;
class_<color_bands>("ColorBands",
"A RasterColorizer's collection of ordered color bands.\n"
class_<colorizer_stops>("ColorizerStops",
"A RasterColorizer's collection of ordered color stops.\n"
"This class is not meant to be instantiated from python. However, "
"it can be accessed at a RasterColorizer's \"bands\" attribute for "
"it can be accessed at a RasterColorizer's \"stops\" attribute for "
"introspection purposes",
no_init)
.def(vector_indexing_suite<color_bands>())
.def(vector_indexing_suite<colorizer_stops>())
;
enum_<colorizer_mode_enum>("ColorizerMode")
.value("COLORIZER_INHERIT", COLORIZER_INHERIT)
.value("COLORIZER_LINEAR", COLORIZER_LINEAR)
.value("COLORIZER_DISCRETE", COLORIZER_DISCRETE)
.value("COLORIZER_EXACT", COLORIZER_EXACT)
.export_values()
;
class_<color_band>("ColorBand",init<float,color const&>(
"A Color Band object.\n"
"Create with a value and color\n"
class_<colorizer_stop>("ColorizerStop",init<float, colorizer_mode_enum, color const&>(
"A Colorizer Stop object.\n"
"Create with a value, ColorizerMode, and Color\n"
"\n"
"Usage:"
">>> color = mapnik.Color(\"#fff000\")\n"
">>> color_band = mapnik.ColorBand(4, color)\n"
">>> stop= mapnik.ColorizerStop(42.42, mapnik.COLORIZER_LINEAR, color)\n"
))
.add_property("color", make_function
(&color_band::get_color,
return_value_policy<reference_existing_object>()))
.add_property("value", &color_band::get_value)
.add_property("max_value", &color_band::get_max_value)
.add_property("color",
make_function(&colorizer_stop::get_color, return_value_policy<reference_existing_object>()),
&colorizer_stop::set_color,
"The stop color (mapnik.Color).\n")
.add_property("value",
&colorizer_stop::get_value,
&colorizer_stop::set_value,
"The stop value.\n")
.add_property("mode",
&colorizer_stop::get_mode_enum,
&colorizer_stop::set_mode_enum,
"The stop mode (mapnik.ColorizerMode).\n"
"\n"
"If this is COLORIZER_INHERIT then it will inherit the default mode\n"
" from the RasterColorizer it is added to.\n")
.def(self == self)
.def("__str__",&color_band::to_string)
.def("__str__",&colorizer_stop::to_string)
;
}

View file

@ -22,6 +22,20 @@
*****************************************************************************/
//$Id$
/** \brief Raster Colouriser
*
* This class allows GDAL raster bands to be colourised. It only works with single
* band GDAL rasters, not greyscale, alpha, or rgb (due to the GDAL featureset loading
* single channel GDAL rasters as FLOAT32, and the others as BYTE, and not having a method
* of figuring out which).
*
* Every input value is translated to an output value. The output value is determined by
* what 'stop' the input value is in. Each stop covers the range of input values from its
* 'value' parameter, up to the 'value' parameter of the next stop.
*
*/
#ifndef RASTER_COLORIZER_HPP
#define RASTER_COLORIZER_HPP
@ -29,193 +43,171 @@
#include <mapnik/config_error.hpp>
#include <mapnik/color.hpp>
#include <mapnik/feature.hpp>
#include <mapnik/enumeration.hpp>
#include <vector>
namespace mapnik
{
struct MAPNIK_DECL color_band
//! \brief Enumerates the modes of interpolation
enum colorizer_mode_enum
{
float value_;
float max_value_;
color color_;
unsigned midpoints_;
bool is_interpolated_;
color_band(float value, color c)
: value_(value),
max_value_(value),
color_(c),
midpoints_(0),
is_interpolated_(false) {}
color_band(float value, float max_value, color c)
: value_(value),
max_value_(max_value),
color_(c),
midpoints_(0),
is_interpolated_(false) {}
bool is_interpolated() const
{
return is_interpolated_;
}
unsigned get_midpoints() const
{
return midpoints_;
}
float get_value() const
{
return value_;
}
float get_max_value() const
{
return max_value_;
}
const color& get_color() const
{
return color_;
}
bool operator==(color_band const& other) const
{
return value_ == other.value_ && color_ == other.color_ && max_value_ == other.max_value_;
}
std::string to_string() const
{
std::stringstream ss;
ss << color_.to_string() << " " << value_ << " " << max_value_;
return ss.str();
}
};
COLORIZER_INHERIT = 0, //!< The stop inherits the mode from the colorizer
COLORIZER_LINEAR = 1, //!< Linear interpolation between colors
COLORIZER_DISCRETE = 2, //!< Single color for stop
COLORIZER_EXACT = 3, //!< Only the exact value specified for the stop gets translated, others use the default
colorizer_mode_enum_MAX
};
typedef std::vector<color_band> color_bands;
struct MAPNIK_DECL raster_colorizer
{
explicit raster_colorizer()
: colors_() {}
raster_colorizer(const raster_colorizer &ps)
: colors_(ps.colors_) {}
raster_colorizer(color_bands &colors)
: colors_(colors) {}
const color_bands& get_color_bands() const
{
return colors_;
}
void append_band (color_band band)
{
if (colors_.size() > 0 && colors_.back().value_ > band.value_) {
#ifdef MAPNIK_DEBUG
std::clog << "prev.v=" << colors_.back().value_ << ". band.v=" << band.value_ << "\n";
#endif
throw config_error(
"Bands must be appended in ascending value order"
);
}
colors_.push_back(band);
if (colors_.size() > 0 && colors_.back().value_ == colors_.back().max_value_)
colors_.back().max_value_ = band.value_;
}
void append_band (color_band band, unsigned midpoints)
{
band.midpoints_ = midpoints;
if (colors_.size() > 0 && midpoints > 0) {
color_band lo = colors_.back();
color_band const &hi = band;
int steps = midpoints+1;
float dv = (hi.value_ - lo.value_)/steps;
float da = (float(hi.color_.alpha()) - lo.color_.alpha())/steps;
float dr = (float(hi.color_.red()) - lo.color_.red())/steps;
float dg = (float(hi.color_.green()) - lo.color_.green())/steps;
float db = (float(hi.color_.blue()) - lo.color_.blue())/steps;
#ifdef MAPNIK_DEBUG
std::clog << "lo.v=" << lo.value_ << ", hi.v=" << hi.value_ << ", dv="<<dv<<"\n";
#endif
// interpolate intermediate values and colors
int j;
for (j=1; j<steps; j++) {
color_band b(
lo.get_value() + dv*j,
color(int(float(lo.color_.red()) + dr*j),
int(float(lo.color_.green()) + dg*j),
int(float(lo.color_.blue()) + db*j),
int(float(lo.color_.alpha()) + da*j)
)
);
b.is_interpolated_ = true;
append_band(b);
}
}
append_band(band);
}
void append_band (float value, color c)
{
append_band(color_band(value, c));
}
void append_band (float value, float max_value, color c)
{
append_band(color_band(value, max_value, c));
}
void append_band (float value, color c, unsigned midpoints)
{
append_band(color_band(value, c), midpoints);
}
void append_band (float value, float max_value, color c, unsigned midpoints)
{
append_band(color_band(value, max_value, c), midpoints);
}
DEFINE_ENUM( colorizer_mode, colorizer_mode_enum );
//! \brief Structure to represent a stop position.
class colorizer_stop {
public:
//! \brief Constructor
//!
//! \param[in] value The stop value
//! \param[in] mode The stop mode
//! \param[in] color The stop color
colorizer_stop(const float value = 0, const colorizer_mode mode = COLORIZER_INHERIT, const color& _color = color(0,0,0,0) );
//! \brief Copy constructor
colorizer_stop(const colorizer_stop& stop);
//! \brief Destructor
~colorizer_stop();
/* rgba =
* if cs[pos].value <= value < cs[pos+1].value: cs[pos].color
* otherwise: transparent
* where 0 <= pos < length(bands)-1
* Last band is special, its value represents the upper bound and its
* color will only be used if the value matches its value exactly.
*/
color get_color(float value) const {
int pos=-1, last=(int)colors_.size()-1, lo=0, hi=last;
while (lo<=hi) {
pos = (lo+hi)/2;
if (colors_[pos].value_<value) {
lo = pos+1;
} else if (colors_[pos].value_>value) {
hi = pos-1;
} else {
lo = pos+1;
break;
}
}
lo--;
if ((0 <= lo && lo < last) ||
(lo==last && (colors_[last].value_==value || value<colors_[last].max_value_)))
return colors_[lo].color_;
else
return color(0,0,0,0);
}
//! \brief Set the stop value
//! \param[in] value The stop value
inline void set_value(const float value) { value_ = value; };
//! \brief Get the stop value
//! \return The stop value
inline float get_value(void) const {return value_; };
//! \brief Set the stop mode
//! \param[in] mode The stop mode
inline void set_mode(const colorizer_mode mode) { mode_ = mode; };
inline void set_mode_enum(const colorizer_mode_enum mode) { set_mode(mode); };
//! \brief Get the stop mode
//! \return The stop mode
inline colorizer_mode get_mode(void) const { return mode_; };
inline colorizer_mode_enum get_mode_enum(void) const { return get_mode(); };
//! \brief set the stop color
//! \param[in] the stop color
inline void set_color(const color& _color) { color_ = _color; };
//! \brief get the stop color
//! \return The stop color
inline const color& get_color(void) const {return color_; };
void colorize(raster_ptr const& raster) const
{
float *rasterData = reinterpret_cast<float*>(raster->data_.getBytes());
unsigned *imageData = raster->data_.getData();
unsigned i;
for (i=0; i<raster->data_.width()*raster->data_.height(); i++)
{
imageData[i] = get_color(rasterData[i]).rgba();
}
}
//! \brief Equality operator
//! \return True if equal, false otherwise
bool operator==(colorizer_stop const& other) const;
//! \brief Print the stop to a string
//! \return A string representing this stop.
std::string to_string() const;
private:
color_bands colors_;
float value_; //!< The stop value
colorizer_mode mode_; //!< The stop mode
color color_; //!< The stop color
};
typedef std::vector<colorizer_stop> colorizer_stops;
//! \brief Class representing the raster colorizer
class raster_colorizer {
public:
//! \brief Constructor
raster_colorizer(colorizer_mode mode = COLORIZER_LINEAR, const color& _color = color(0,0,0,0));
//! \brief Destructor
~raster_colorizer();
//! \brief Set the default mode
//!
//! This can not be set as INHERIT, if you do, LINEAR will be used instead.
//! \param[in] mode The default mode
void set_default_mode(const colorizer_mode mode) { default_mode_ = (mode == COLORIZER_INHERIT) ? COLORIZER_LINEAR:(colorizer_mode_enum)mode; };
void set_default_mode_enum(const colorizer_mode_enum mode) { set_default_mode(mode); };
//! \brief Get the default mode
//! \return The default mode
colorizer_mode get_default_mode(void) const {return default_mode_; };
colorizer_mode_enum get_default_mode_enum(void) const {return get_default_mode(); };
//! \brief Set the default color
//! \param[in] color The default color
void set_default_color(const color& color) { default_color_ = color; };
//! \brief Get the default color
//! \return The default color
const color& get_default_color(void) const {return default_color_; };
//! \brief Add a stop
//!
//! \param[in] stop The stop to add
//! \return True if added, false if error
bool add_stop(const colorizer_stop & stop);
//! \brief Get the list of stops
//! \return The list of stops
const colorizer_stops& get_stops(void) const {return stops_; };
//! \brief Colorize a raster
//!
//! \param[in, out] raster A raster stored in float32 single channel format, which gets colorized in place.
//! \param[in] properties belonging to the feature, used to find 'NODATA' information if available
void colorize(raster_ptr const& raster,const std::map<std::string,value> &Props) const;
//! \brief Perform the translation of input to output
//!
//! \param[in] value Input value
//! \return color associated with the value
color get_color(float value) const;
//! \brief Set the epsilon value for exact mode
//! \param[in] e The epsilon value
inline void set_epsilon(const float e) { if(e > 0) epsilon_ = e; };
//! \brief Get the epsilon value for exact mode
//! \return The epsilon value
inline float get_epsilon(void) const { return epsilon_; };
private:
colorizer_stops stops_; //!< The vector of stops
colorizer_mode default_mode_; //!< The default mode inherited by stops
color default_color_; //!< The default color
float epsilon_; //!< The epsilon value for exact mode
};
typedef boost::shared_ptr<raster_colorizer> raster_colorizer_ptr;
} // mapnik namespace
#endif //RASTER_COLORIZER_HPP

View file

@ -194,13 +194,18 @@ feature_ptr gdal_featureset::get_feature(mapnik::query const& q)
{
if (band_ > nbands_)
throw datasource_exception((boost::format("GDAL Plugin: '%d' is an invalid band, dataset only has '%d' bands\n") % band_ % nbands_).str());
float *imageData = (float*)image.getBytes();
GDALRasterBand * band = dataset_.GetRasterBand(band_);
int hasNoData;
double nodata = band->GetNoDataValue(&hasNoData);
band->RasterIO(GF_Read, x_off, y_off, width, height,
imageData, image.width(), image.height(),
GDT_Float32, 0, 0);
feature->set_raster(mapnik::raster_ptr(new mapnik::raster(intersect,image)));
if (hasNoData)
feature->props()["NODATA"]=nodata;
}
else // working with all bands

View file

@ -139,6 +139,7 @@ source = Split(
glyph_symbolizer.cpp
markers_symbolizer.cpp
metawriter.cpp
raster_colorizer.cpp
text_placements.cpp
wkt/wkt_factory.cpp
metawriter_inmem.cpp

View file

@ -43,7 +43,7 @@ void agg_renderer<T>::process(raster_symbolizer const& sym,
// If there's a colorizer defined, use it to color the raster in-place
raster_colorizer_ptr colorizer = sym.get_colorizer();
if (colorizer)
colorizer->colorize(raster);
colorizer->colorize(raster,feature.props());
box2d<double> ext=t_.forward(raster->ext_);

View file

@ -1384,7 +1384,7 @@ void cairo_renderer_base::process(raster_symbolizer const& sym,
// If there's a colorizer defined, use it to color the raster in-place
raster_colorizer_ptr colorizer = sym.get_colorizer();
if (colorizer)
colorizer->colorize(raster);
colorizer->colorize(raster,feature.props());
box2d<double> ext = t_.forward(raster->ext_);
int start_x = (int)ext.minx();

View file

@ -1979,37 +1979,82 @@ void map_parser::parse_raster_colorizer(raster_colorizer_ptr const& rc,
{
try
{
ptree::const_iterator cbIter = node.begin();
ptree::const_iterator endCb = node.end();
// mode
colorizer_mode default_mode =
get_attr<colorizer_mode>(node, "default-mode", COLORIZER_LINEAR);
if(default_mode == COLORIZER_INHERIT) {
throw config_error("RasterColorizer mode must not be INHERIT. ");
}
rc->set_default_mode( default_mode );
for(; cbIter != endCb; ++cbIter)
// default colour
optional<color> default_color = get_opt_attr<color>(node, "default-color");
if (default_color)
{
ptree::value_type const& cb_tag = *cbIter;
ptree const & cb = cbIter->second;
rc->set_default_color( *default_color );
}
if (cb_tag.first == "ColorBand")
{
ensure_attrs(cb, "ColorBand", "value,color,midpoints,max-value,label");
std::string value_s = get_attr<string>(cb, "value");
float value;
std::stringstream(value_s) >> value;
optional<color> c = get_opt_attr<color>(cb, "color");
if (!c) {
throw config_error("missing color");
}
unsigned midpoints = get_attr(cb, "midpoints", 0);
optional<float> max_value = get_opt_attr<float>(cb, "max-value");
if (max_value) {
rc->append_band(value, *max_value, *c, midpoints);
} else {
rc->append_band(value, *c, midpoints);
}
// epsilon
optional<float> eps = get_opt_attr<float>(node, "epsilon");
if (eps)
{
if(*eps < 0) {
throw config_error("RasterColorizer epsilon must be > 0. ");
}
else if (cb_tag.first != "<xmlcomment>" &&
cb_tag.first != "<xmlattr>" )
rc->set_epsilon( *eps );
}
ptree::const_iterator stopIter = node.begin();
ptree::const_iterator endStop = node.end();
float maximumValue = -std::numeric_limits<float>::max();
for(; stopIter != endStop; ++stopIter)
{
ptree::value_type const& stop_tag = *stopIter;
ptree const & stop = stopIter->second;
if (stop_tag.first == "stop")
{
// colour is optional.
optional<color> stopcolor = get_opt_attr<color>(stop, "color");
if (!stopcolor) {
*stopcolor = *default_color;
}
// mode default to INHERIT
colorizer_mode mode =
get_attr<colorizer_mode>(stop, "mode", COLORIZER_INHERIT);
// value is required, and it must be bigger than the previous
optional<float> value =
get_opt_attr<float>(stop, "value");
if(!value) {
throw config_error("stop tag missing value");
}
if(value < maximumValue) {
throw config_error("stop tag values must be in ascending order");
}
maximumValue = *value;
//append the stop
colorizer_stop tmpStop;
tmpStop.set_color(*stopcolor);
tmpStop.set_mode(mode);
tmpStop.set_value(*value);
rc->add_stop(tmpStop);
}
else if (stop_tag.first != "<xmlcomment>" &&
stop_tag.first != "<xmlattr>" )
{
throw config_error(std::string("Unknown child node. ") +
"Expected 'ColorBand' but got '" + cb_tag.first + "'");
"Expected 'stop' but got '" + stop_tag.first + "'");
}
}
}

256
src/raster_colorizer.cpp Normal file
View file

@ -0,0 +1,256 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2006 Artem Pavlenko
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
//$Id: $
#include <mapnik/raster_colorizer.hpp>
#include <limits>
#include <arpa/inet.h>
using namespace std;
using namespace mapnik;
//! \brief Strings for the colorizer_mode enumeration
static const char *colorizer_mode_strings[] = {
"inherit",
"linear",
"discrete",
"exact",
""
};
IMPLEMENT_ENUM( colorizer_mode, colorizer_mode_strings );
colorizer_stop::colorizer_stop(const float value/* = 0*/, const colorizer_mode mode/* = COLORIZER_INHERIT*/, const color& _color/* = color(0,0,0,0)*/ )
: value_(value)
, mode_(mode)
, color_(_color)
{
}
colorizer_stop::colorizer_stop(const colorizer_stop& stop)
: value_(stop.value_)
, mode_(stop.mode_)
, color_(stop.color_)
{
}
colorizer_stop::~colorizer_stop()
{
}
bool colorizer_stop::operator==(colorizer_stop const& other) const
{
return (value_ == other.value_) &&
(color_ == other.color_) &&
(mode_ == other.mode_);
}
std::string colorizer_stop::to_string() const
{
std::stringstream ss;
ss << color_.to_string() << " " << value_ << " " << mode_.as_string();
return ss.str();
}
raster_colorizer::raster_colorizer(colorizer_mode mode/* = COLORIZER_LINEAR*/, const color& _color/* = color(0,0,0,0)*/)
: default_mode_(mode)
, default_color_(_color)
, epsilon_(numeric_limits<float>::epsilon())
{
}
raster_colorizer::~raster_colorizer()
{
}
bool raster_colorizer::add_stop(const colorizer_stop & stop) {
//make sure stops are added in order of value
if(stops_.size()) {
if(stop.get_value() <= stops_.back().get_value()) {
return false;
}
}
stops_.push_back(stop);
return true;
}
void raster_colorizer::colorize(raster_ptr const& raster,const std::map<std::string,value> &Props) const
{
unsigned *imageData = raster->data_.getData();
int len = raster->data_.width() * raster->data_.height();
bool hasNoData = false;
float noDataValue = 0;
if (Props.count("NODATA")>0)
{
hasNoData = true;
noDataValue = Props.at("NODATA").to_double();
}
for (int i=0; i<len; ++i)
{
// the GDAL plugin reads single bands as floats
float value = *reinterpret_cast<float *> (&imageData[i]);
if (hasNoData && noDataValue == value)
imageData[i] = color(0,0,0,0).rgba();
else
imageData[i] = get_color(value).rgba();
}
}
inline float interpolate(float start,float end, float fraction)
{
return fraction * (end - start) + start;
}
color raster_colorizer::get_color(float value) const {
int stopCount = stops_.size();
//use default color if no stops
if(stopCount == 0) {
return default_color_;
}
//1 - Find the stop that the value is in
int stopIdx = -1;
bool foundStopIdx = false;
for(int i=0; i<stopCount; i++) {
if(value < stops_[i].get_value()) {
stopIdx = i-1;
foundStopIdx = true;
break;
}
}
if(!foundStopIdx) {
stopIdx = stopCount-1;
}
//2 - Find the next stop
int nextStopIdx = stopIdx + 1;
if(nextStopIdx >= stopCount) { //there is no next stop
nextStopIdx = stopCount - 1;
}
//3 - Work out the mode
colorizer_mode stopMode;
if( stopIdx == -1 ) { //before the first stop
stopMode = default_mode_;
}
else {
stopMode = stops_[stopIdx].get_mode();
if(stopMode == COLORIZER_INHERIT) {
stopMode = default_mode_;
}
}
//4 - Calculate the colour
color stopColor;
color nextStopColor;
float stopValue = 0;
float nextStopValue = 0;
color outputColor = get_default_color();
if(stopIdx == -1) {
stopColor = default_color_;
nextStopColor = stops_[nextStopIdx].get_color();
stopValue = value;
nextStopValue = stops_[nextStopIdx].get_value();
}
else {
stopColor = stops_[stopIdx].get_color();
nextStopColor = stops_[nextStopIdx].get_color();
stopValue = stops_[stopIdx].get_value();
nextStopValue = stops_[nextStopIdx].get_value();
}
switch(stopMode) {
case COLORIZER_LINEAR:
{
//deal with this separately so we don't have to worry about div0
if(nextStopValue == stopValue) {
outputColor = stopColor;
}
else {
float fraction = (value - stopValue) / (nextStopValue - stopValue);
float r = interpolate(stopColor.red(), nextStopColor.red(),fraction);
float g = interpolate(stopColor.green(), nextStopColor.green(),fraction);
float b = interpolate(stopColor.blue(), nextStopColor.blue(),fraction);
float a = interpolate(stopColor.alpha(), nextStopColor.alpha(),fraction);
outputColor.set_red(r);
outputColor.set_green(g);
outputColor.set_blue(b);
outputColor.set_alpha(a);
}
}
break;
case COLORIZER_DISCRETE:
outputColor = stopColor;
break;
case COLORIZER_EXACT:
default:
//approximately equal (within epsilon)
if(fabs(value - stopValue) < epsilon_) {
outputColor = stopColor;
}
else {
outputColor = default_color_;
}
break;
}
/*
std::clog << "get_color: " << value << "\n";
std::clog << "\tstopIdx: " << stopIdx << "\n";
std::clog << "\tnextStopIdx: " << nextStopIdx << "\n";
std::clog << "\tstopValue: " << stopValue << "\n";
std::clog << "\tnextStopValue: " << nextStopValue << "\n";
std::clog << "\tstopColor: " << stopColor.to_string() << "\n";
std::clog << "\tnextStopColor: " << nextStopColor.to_string() << "\n";
std::clog << "\tstopMode: " << stopMode.as_string() << "\n";
std::clog << "\toutputColor: " << outputColor.to_string() << "\n";
*/
return outputColor;
}

View file

@ -43,30 +43,6 @@ namespace mapnik
using boost::property_tree::ptree;
using boost::optional;
void serialize_raster_colorizer(ptree & sym_node,
raster_colorizer_ptr const& colorizer,
bool explicit_defaults)
{
ptree & col_node = sym_node.push_back(
ptree::value_type("RasterColorizer", ptree() ))->second;
unsigned i;
color_bands const &cb = colorizer->get_color_bands();
for (i=0; i<cb.size(); i++) {
if (!cb[i].is_interpolated()) {
ptree & band_node = col_node.push_back(
ptree::value_type("ColorBand", ptree())
)->second;
set_attr(band_node, "value", cb[i].get_value());
if (cb[i].get_value() != cb[i].get_max_value())
set_attr(band_node, "max-value", cb[i].get_max_value());
set_attr(band_node, "midpoints", cb[i].get_midpoints());
optional<color> c = cb[i].get_color();
if (c) set_attr(band_node, "color", * c);
}
}
}
class serialize_symbolizer : public boost::static_visitor<>
{
@ -409,6 +385,29 @@ public:
private:
serialize_symbolizer();
void serialize_raster_colorizer(ptree & sym_node,
raster_colorizer_ptr const& colorizer,
bool explicit_defaults)
{
ptree & col_node = sym_node.push_back(
ptree::value_type("RasterColorizer", ptree() ))->second;
set_attr(col_node, "default-mode", colorizer->get_default_mode());
set_attr(col_node, "default-color", colorizer->get_default_color());
set_attr(col_node, "epsilon", colorizer->get_epsilon());
unsigned i;
colorizer_stops const &stops = colorizer->get_stops();
for (i=0; i<stops.size(); i++) {
ptree &stop_node = col_node.push_back( ptree::value_type("stop", ptree()) )->second;
set_attr(stop_node, "value", stops[i].get_value());
set_attr(stop_node, "color", stops[i].get_color());
set_attr(stop_node, "mode", stops[i].get_mode().as_string());
}
}
void add_image_attributes(ptree & node, const symbolizer_with_image & sym)
{
std::string const& filename = path_processor_type::to_string( *sym.get_filename());

View file

@ -2,12 +2,12 @@
<Style name="arrows">
<Rule>
<GlyphSymbolizer size="10" char="'&#237;'" value="[value]" angle="[azimuth]+90" face-name="DejaVu Sans Condensed" allow-overlap="1" avoid-edges="0" halo-fill="rgba(0%,0%,0%,.1)" halo-radius="1" angle-mode="azimuth">
<RasterColorizer>
<ColorBand value="0" color="#0044cc"/>
<ColorBand value="10" color="#00cc00"/>
<ColorBand value="20" color="#ffff00"/>
<RasterColorizer default_mode="discrete" default_color="#000000">
<stop value="0" color="#0044cc"/>
<stop value="10" color="#00cc00"/>
<stop value="20" color="#ffff00"/>
</RasterColorizer>
</GlyphSymbolizer>
</Rule>
</Style>
</Map>
</Map>

View file

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<Map background-color="white" srs="+init=epsg:32630">
<!-- blue yellow orange indigo green red transparent black white -->
<Style name="DISCRETE RAINBOW">
<Rule>
<RasterSymbolizer>
<RasterColorizer default-mode="discrete" default-color="white" >
<stop color="red" value = "0" />
<stop color="orange" value = "5" />
<stop color="yellow" value = "10" />
<stop color="green" value = "15" />
<stop color="blue" value = "20" />
<stop color="indigo" value = "25" />
</RasterColorizer>
</RasterSymbolizer>
</Rule>
</Style>
<Style name="LINEAR BLEND">
<Rule>
<RasterSymbolizer>
<RasterColorizer default-mode="discrete" default-color="white" >
<stop color="#222222" value = "0" mode = "linear" />
<stop color="#EEEEEE" value = "25" />
</RasterColorizer>
</RasterSymbolizer>
</Rule>
</Style>
<Style name="BANDED">
<Rule>
<RasterSymbolizer>
<RasterColorizer default-mode="discrete" default-color="white" >
<stop color="red" value = "0" mode = "exact" />
<stop color="orange" value = "2" mode = "exact" />
<stop color="yellow" value = "4" mode = "exact" />
<stop color="green" value = "6" mode = "exact" />
<stop color="blue" value = "8" mode = "exact" />
<stop color="indigo" value = "10" mode = "exact" />
<stop color="violet" value = "12" mode = "exact" />
</RasterColorizer>
</RasterSymbolizer>
</Rule>
</Style>
<Style name="MIXED">
<Rule>
<RasterSymbolizer>
<RasterColorizer default-mode="discrete" default-color="white" >
<stop color="red" value = "0" />
<stop color="orange" value = "5" />
<stop color="yellow" value = "10" mode = "exact" />
<stop color="green" value = "13" mode = "exact" />
<stop color="#222222" value = "15" mode = "linear" />
<stop color="#EEEEEE" value = "25" />
</RasterColorizer>
</RasterSymbolizer>
</Rule>
</Style>
<Layer name="dataraster" srs="+init=epsg:32630">
<StyleName>DISCRETE RAINBOW</StyleName>
<Datasource>
<Parameter name="file">../raster/dataraster.tif</Parameter>
<Parameter name="type">gdal</Parameter>
<Parameter name="band">1</Parameter>
</Datasource>
</Layer>
</Map>

View file

@ -3,9 +3,9 @@
<Style name="two-value">
<Rule>
<RasterSymbolizer opacity="1">
<RasterColorizer>
<ColorBand value="0" color="yellow" label="&lt;0"/>
<ColorBand value="26" color="purple" midpoints="1"/>
<RasterColorizer default_mode="discrete" default_color="#000000">
<stop value="0" color="yellow"></stop>
<stop value="26" color="purple"></stop>
</RasterColorizer>
</RasterSymbolizer>
</Rule>
@ -14,9 +14,9 @@
<Style name="ramped">
<Rule>
<RasterSymbolizer>
<RasterColorizer>
<ColorBand value="0" color="red" label="&lt;0"/>
<ColorBand value="26" color="green" midpoints="100"/>
<RasterColorizer default_mode="linear" default_color="#000000">
<stop value="0" color="red"></stop>
<stop value="26" color="green"></stop>
</RasterColorizer>
</RasterSymbolizer>
</Rule>
@ -32,4 +32,4 @@
</Datasource>
</Layer>
</Map>
</Map>

View file

@ -1,46 +1,95 @@
import mapnik2
from nose.tools import *
def test_get_color():
colorizer = mapnik2.RasterColorizer()
# Setup the color bands. band[N].color will apply to all
# values 'v' if band[N].value <= v < band[N+1].color
# If no color is found then "transparent" will be assigned
bands = [(value, mapnik2.Color(color)) for value, color in [
( 0, "#0044cc"),
( 10, "#00cc00"),
( 20, "#ffff00"),
( 30, "#ff7f00"),
( 40, "#ff0000"),
( 50, "#ff007f"),
( 60, "#ff00ff"),
( 70, "#cc00cc"),
( 80, "#990099"),
( 90, "#660066"),
( 200, "#ffffff"),
]]
for value, color in bands:
colorizer.append_band(value, color)
def test_gen_map():
mapxmlfile = '../data/good_maps/raster_colorizer.xml'
mapxmloutputfile = 'raster_colorizer_test_save.xml'
outputfile = 'raster_colorizer_test.png'
eq_(colorizer.get_color(-1), mapnik2.Color("transparent"))
eq_(colorizer.get_color(0), bands[0][1])
eq_(colorizer.get_color(5), bands[0][1])
eq_(colorizer.get_color(10), bands[1][1])
# last value is used if it matches exactly
eq_(colorizer.get_color(200), bands[-1][1])
# values greater than the last value are mapped to "transparent"
eq_(colorizer.get_color(201), mapnik2.Color("transparent"))
m = mapnik2.Map(800, 600)
mapnik2.load_map(m, mapxmlfile)
mapnik2.save_map(m, mapxmloutputfile)
m.zoom_all()
mapnik2.render_to_file(m, outputfile)
#test discrete colorizer mode
def test_get_color_discrete():
#setup
colorizer = mapnik2.RasterColorizer();
colorizer.default_color = mapnik2.Color(0,0,0,0);
colorizer.default_mode = mapnik2.COLORIZER_DISCRETE;
colorizer.add_stop(10, mapnik2.Color(100,100,100,100));
colorizer.add_stop(20, mapnik2.Color(200,200,200,200));
#should be default colour
eq_(colorizer.get_color(-50), mapnik2.Color(0,0,0,0));
eq_(colorizer.get_color(0), mapnik2.Color(0,0,0,0));
#now in stop 1
eq_(colorizer.get_color(10), mapnik2.Color(100,100,100,100));
eq_(colorizer.get_color(19), mapnik2.Color(100,100,100,100));
#now in stop 2
eq_(colorizer.get_color(20), mapnik2.Color(200,200,200,200));
eq_(colorizer.get_color(1000), mapnik2.Color(200,200,200,200));
#test exact colorizer mode
def test_get_color_exact():
#setup
colorizer = mapnik2.RasterColorizer();
colorizer.default_color = mapnik2.Color(0,0,0,0);
colorizer.default_mode = mapnik2.COLORIZER_EXACT;
colorizer.add_stop(10, mapnik2.Color(100,100,100,100));
colorizer.add_stop(20, mapnik2.Color(200,200,200,200));
#should be default colour
eq_(colorizer.get_color(-50), mapnik2.Color(0,0,0,0));
eq_(colorizer.get_color(11), mapnik2.Color(0,0,0,0));
eq_(colorizer.get_color(20.001), mapnik2.Color(0,0,0,0));
#should be stop 1
eq_(colorizer.get_color(10), mapnik2.Color(100,100,100,100));
#should be stop 2
eq_(colorizer.get_color(20), mapnik2.Color(200,200,200,200));
#test linear colorizer mode
def test_get_color_linear():
#setup
colorizer = mapnik2.RasterColorizer();
colorizer.default_color = mapnik2.Color(0,0,0,0);
colorizer.default_mode = mapnik2.COLORIZER_LINEAR;
colorizer.add_stop(10, mapnik2.Color(100,100,100,100));
colorizer.add_stop(20, mapnik2.Color(200,200,200,200));
#should be default colour
eq_(colorizer.get_color(-50), mapnik2.Color(0,0,0,0));
eq_(colorizer.get_color(9.9), mapnik2.Color(0,0,0,0));
#should be stop 1
eq_(colorizer.get_color(10), mapnik2.Color(100,100,100,100));
#should be stop 2
eq_(colorizer.get_color(20), mapnik2.Color(200,200,200,200));
#half way between stops 1 and 2
eq_(colorizer.get_color(15), mapnik2.Color(150,150,150,150));
#after stop 2
eq_(colorizer.get_color(100), mapnik2.Color(200,200,200,200));
def test_get_color_with_max_value():
colorizer = mapnik2.RasterColorizer()
c1 = mapnik2.Color("#0044cc")
colorizer.append_band(0, c1)
c2 = mapnik2.Color("#0055dd")
colorizer.append_band(1, 2, c2)
eq_(colorizer.get_color(-1), mapnik2.Color("transparent"))
eq_(colorizer.get_color(0), c1)
eq_(colorizer.get_color(0.5), c1)
eq_(colorizer.get_color(1), c2)
eq_(colorizer.get_color(1.5), c2)
eq_(colorizer.get_color(2), mapnik2.Color("transparent"))

View file

@ -25,7 +25,8 @@ def test_dataraster_coloring():
sym = mapnik2.RasterSymbolizer()
# Assigning a colorizer to the RasterSymbolizer tells the later
# that it should use it to colorize the raw data raster
sym.colorizer = mapnik2.RasterColorizer()
sym.colorizer = mapnik2.RasterColorizer(mapnik2.COLORIZER_DISCRETE, mapnik2.Color("transparent"))
for value, color in [
( 0, "#0044cc"),
( 10, "#00cc00"),
@ -39,7 +40,7 @@ def test_dataraster_coloring():
( 90, "#660066"),
( 200, "transparent"),
]:
sym.colorizer.append_band(value, mapnik2.Color(color))
sym.colorizer.add_stop(value, mapnik2.Color(color))
rule.symbols.append(sym)
style.rules.append(rule)
_map.append_style('foo', style)
@ -92,4 +93,4 @@ def test_load_save_map():
out_map = mapnik2.save_map_to_string(map)
assert 'RasterSymbolizer' in out_map
assert 'RasterColorizer' in out_map
assert 'ColorBand' in out_map
assert 'stop' in out_map