modified the old arrow_symbolizer interface on glyph_symbolizer and documented it

This commit is contained in:
Alberto Valverde 2010-03-18 20:04:51 +00:00
parent fc3a1beeef
commit 31c3d20e43
4 changed files with 144 additions and 207 deletions

View file

@ -1,6 +1,7 @@
#include <mapnik/glyph_symbolizer.hpp>
#include <boost/python.hpp> #include <boost/python.hpp>
#include <boost/tuple/tuple.hpp> #include <boost/tuple/tuple.hpp>
#include <mapnik/glyph_symbolizer.hpp>
using mapnik::glyph_symbolizer; using mapnik::glyph_symbolizer;
using mapnik::position; using mapnik::position;
@ -20,56 +21,74 @@ list get_displacement_list(const glyph_symbolizer& t)
void export_glyph_symbolizer() void export_glyph_symbolizer()
{ {
class_<glyph_symbolizer>("GlyphSymbolizer", class_<glyph_symbolizer>("GlyphSymbolizer",
init<std::string const&,std::string const&>()) init<std::string,mapnik::expression_ptr>())
.add_property("face_name", .add_property("face_name",
make_function(&glyph_symbolizer::get_face_name,return_value_policy<copy_const_reference>()), make_function(&glyph_symbolizer::get_face_name,
&glyph_symbolizer::set_face_name) return_value_policy<copy_const_reference>()),
&glyph_symbolizer::set_face_name,
"Get/Set the name of the font face (eg:\"DejaVu Sans "
"Book\") which contains the glyph"
)
.add_property("char", .add_property("char",
make_function(&glyph_symbolizer::get_char,return_value_policy<copy_const_reference>()), &glyph_symbolizer::get_char,
&glyph_symbolizer::set_char) &glyph_symbolizer::set_char,
.add_property("angle_offset", "Get/Set the char expression. The char is the unicode "
&glyph_symbolizer::get_angle_offset, "character indexing the glyph in the font referred by "
&glyph_symbolizer::set_angle_offset) "face_name."
)
.add_property("allow_overlap", .add_property("allow_overlap",
&glyph_symbolizer::get_allow_overlap, &glyph_symbolizer::get_allow_overlap,
&glyph_symbolizer::set_allow_overlap) &glyph_symbolizer::set_allow_overlap,
"Get/Set the flag which controls if glyphs should "
"overlap any symbols previously rendered"
)
.add_property("avoid_edges", .add_property("avoid_edges",
&glyph_symbolizer::get_avoid_edges, &glyph_symbolizer::get_avoid_edges,
&glyph_symbolizer::set_avoid_edges) &glyph_symbolizer::set_avoid_edges,
"Get/Set the flag which controls if glyphs should be "
"partially drawn beside the edge of a tile."
)
.def("get_displacement", get_displacement_list) .def("get_displacement", get_displacement_list)
.def("set_displacement", &glyph_symbolizer::set_displacement) .def("set_displacement", &glyph_symbolizer::set_displacement)
.add_property("halo_fill", .add_property("halo_fill",
make_function(&glyph_symbolizer::get_halo_fill, make_function(&glyph_symbolizer::get_halo_fill,
return_value_policy<copy_const_reference>()), return_value_policy<copy_const_reference>()),
&glyph_symbolizer::set_halo_fill) &glyph_symbolizer::set_halo_fill)
.add_property("halo_radius", .add_property("halo_radius",
&glyph_symbolizer::get_halo_radius, &glyph_symbolizer::get_halo_radius,
&glyph_symbolizer::set_halo_radius) &glyph_symbolizer::set_halo_radius)
.add_property("value_attr", make_function( .add_property("size",
&glyph_symbolizer::get_value_attr, &glyph_symbolizer::get_size,
return_value_policy<copy_const_reference>()), &glyph_symbolizer::set_size,
&glyph_symbolizer::set_value_attr) "Get/Set the size expression used to size the glyph."
.add_property("azimuth_attr", make_function( )
&glyph_symbolizer::get_azimuth_attr, .add_property("angle",
return_value_policy<copy_const_reference>()), &glyph_symbolizer::get_angle,
&glyph_symbolizer::set_azimuth_attr) &glyph_symbolizer::set_angle,
"Get/Set the angle expression used to rotate the glyph "
"along its center."
)
.add_property("value",
&glyph_symbolizer::get_value,
&glyph_symbolizer::set_value,
"Get/set the value expression which will be used to "
"retrieve a a value for the colorizer to use to choose "
"a color."
)
.add_property("color",
&glyph_symbolizer::get_color,
&glyph_symbolizer::set_color,
"Get/Set the color expression used to color the glyph. "
"(See also the 'colorizer' attribute)"
)
.add_property("colorizer", .add_property("colorizer",
&glyph_symbolizer::get_colorizer, &glyph_symbolizer::get_colorizer,
&glyph_symbolizer::set_colorizer, &glyph_symbolizer::set_colorizer,
"Get/Set the RasterColorizer used to color the arrows.\n" "Get/Set the RasterColorizer used to color the glyph "
"\n" "depending on the 'value' expression (which must be "
"Usage:\n" "defined).\n"
"\n" "Only needed if no explicit color is provided"
">>> from mapnik import GlyphSymbolizer, RasterColorizer\n"
">>> sym = GlyphSymbolizer()\n"
">>> sym.colorizer = RasterColorizer()\n"
">>> for value, color in [\n"
"... (0, \"#000000\"),\n"
"... (10, \"#ff0000\"),\n"
"... (40, \"#00ff00\"),\n"
"... ]:\n"
"... sym.colorizer.append_band(value, color)\n"
) )
; ;
;
} }

View file

@ -1,103 +1,54 @@
#ifndef ARROW_SYMBOLIZER_HPP #ifndef ARROW_SYMBOLIZER_HPP
#define ARROW_SYMBOLIZER_HPP #define ARROW_SYMBOLIZER_HPP
// mapnik
#include <mapnik/raster_colorizer.hpp> #include <mapnik/raster_colorizer.hpp>
#include <mapnik/text_symbolizer.hpp> #include <mapnik/expression_node.hpp>
#include <mapnik/text_path.hpp> #include <mapnik/text_path.hpp>
#include <mapnik/unicode.hpp>
#include <mapnik/config_error.hpp>
#include <mapnik/font_engine_freetype.hpp> #include <mapnik/font_engine_freetype.hpp>
// boost
#include <boost/tuple/tuple.hpp>
namespace mapnik namespace mapnik
{ {
struct MAPNIK_DECL glyph_symbolizer
{ typedef boost::tuple<double,double> position;
glyph_symbolizer(std::string const& face_name, std::string const& c)
struct MAPNIK_DECL glyph_symbolizer
{
glyph_symbolizer(std::string face_name, expression_ptr c)
: face_name_(face_name), : face_name_(face_name),
char_(c), char_(c),
angle_offset_(0.0), angle_(),
min_size_(10), value_(),
max_size_(100), size_(),
min_value_(0), color_(),
max_value_(1),
colorizer_(), colorizer_(),
allow_overlap_(false), allow_overlap_(false),
avoid_edges_(false), avoid_edges_(false),
displacement_(0.0, 0.0), displacement_(0.0, 0.0),
halo_fill_(color(255,255,255)), halo_fill_(color(255,255,255)),
halo_radius_(0), halo_radius_(0) {}
azimuth_attr_("azimuth"),
value_attr_("value")
{
if (get_min_size() > get_max_size())
throw config_error("Invalid size range");
}
std::string const& get_face_name() const std::string const& get_face_name() const
{ {
return face_name_; return face_name_;
} }
void set_face_name(std::string face_name) void set_face_name(std::string face_name)
{ {
face_name_ = face_name; face_name_ = face_name;
} }
std::string const& get_char() const expression_ptr get_char() const
{ {
return char_; return char_;
} }
void set_char(expression_ptr c)
void set_char(std::string c)
{ {
char_ = c; char_ = c;
} }
double get_angle_offset() const
{
return angle_offset_;
}
void set_angle_offset(double angle)
{
angle_offset_ = angle;
}
float get_min_value() const
{
return min_value_;
}
void set_min_value(float min_value)
{
min_value_ = min_value;
}
float get_max_value() const
{
return max_value_;
}
void set_max_value(float max_value)
{
max_value_ = max_value;
}
unsigned get_min_size() const
{
return min_size_;
}
void set_min_size(unsigned min_size)
{
min_size_ = min_size;
}
unsigned get_max_size() const
{
return max_size_;
}
void set_max_size(unsigned max_size)
{
max_size_ = max_size;
}
bool get_allow_overlap() const bool get_allow_overlap() const
{ {
return allow_overlap_; return allow_overlap_;
@ -106,6 +57,7 @@ namespace mapnik
{ {
allow_overlap_ = allow_overlap; allow_overlap_ = allow_overlap;
} }
bool get_avoid_edges() const bool get_avoid_edges() const
{ {
return avoid_edges_; return avoid_edges_;
@ -114,6 +66,7 @@ namespace mapnik
{ {
avoid_edges_ = avoid_edges; avoid_edges_ = avoid_edges;
} }
void set_displacement(double x, double y) void set_displacement(double x, double y)
{ {
displacement_ = boost::make_tuple(x,y); displacement_ = boost::make_tuple(x,y);
@ -122,6 +75,7 @@ namespace mapnik
{ {
return displacement_; return displacement_;
} }
void set_halo_fill(color const& fill) void set_halo_fill(color const& fill)
{ {
halo_fill_ = fill; halo_fill_ = fill;
@ -130,6 +84,7 @@ namespace mapnik
{ {
return halo_fill_; return halo_fill_;
} }
void set_halo_radius(unsigned radius) void set_halo_radius(unsigned radius)
{ {
halo_radius_ = radius; halo_radius_ = radius;
@ -140,19 +95,40 @@ namespace mapnik
return halo_radius_; return halo_radius_;
} }
std::string const& get_azimuth_attr() const expression_ptr get_angle() const
{ {
return azimuth_attr_; return angle_;
}
void set_angle(expression_ptr angle)
{
angle_ = angle;
} }
void set_azimuth_attr(std::string azimuth_attr) expression_ptr get_value() const
{ {
azimuth_attr_ = azimuth_attr; return value_;
}
void set_value(expression_ptr value)
{
value_ = value;
} }
std::string const& get_value_attr() const expression_ptr get_size() const
{ {
return value_attr_; return size_;
}
void set_size(expression_ptr size)
{
size_ = size;
}
expression_ptr get_color() const
{
return color_;
}
void set_color(expression_ptr color)
{
color_ = color;
} }
raster_colorizer_ptr get_colorizer() const raster_colorizer_ptr get_colorizer() const
@ -164,40 +140,26 @@ namespace mapnik
colorizer_ = colorizer; colorizer_ = colorizer;
} }
void set_value_attr(std::string value_attr)
{
value_attr_ = value_attr;
}
/*
* helpers
*/
text_path_ptr get_text_path(face_set_ptr const& faces, text_path_ptr get_text_path(face_set_ptr const& faces,
Feature const& feature) const; Feature const& feature) const;
unsigned int get_size(double value) const;
double get_angle(Feature const& feature) const;
double get_value(Feature const& feature) const;
private:
private: std::string face_name_;
std::string face_name_; expression_ptr char_;
std::string char_; expression_ptr angle_;
double angle_offset_; expression_ptr value_;
unsigned min_size_; expression_ptr size_;
unsigned max_size_; expression_ptr color_;
float min_value_; raster_colorizer_ptr colorizer_;
float max_value_; bool allow_overlap_;
raster_colorizer_ptr colorizer_; bool avoid_edges_;
bool allow_overlap_; position displacement_;
bool avoid_edges_; color halo_fill_;
position displacement_; unsigned halo_radius_;
color halo_fill_;
unsigned halo_radius_;
std::string azimuth_attr_;
std::string value_attr_;
};
}; };
}; // end mapnik namespace
#endif #endif

View file

@ -7,63 +7,9 @@
namespace mapnik namespace mapnik
{ {
text_path_ptr glyph_symbolizer::get_text_path(face_set_ptr const& faces, text_path_ptr glyph_symbolizer::get_text_path(face_set_ptr const& faces,
Feature const& feature) const Feature const& feature) const
{ {
// Calculate face rotation angle and box offset adjustments
typedef std::pair<unsigned,unsigned> dimension_t;
// Get string_info with arrow glyph
string_info info(UnicodeString(get_char().c_str()));
faces->get_string_info(info);
if (info.num_characters() != 1)
{
throw config_error("'char' length must be exactly 1");
}
character_info ci = info.at(0);
dimension_t cdim = faces->character_dimensions(ci.character);
double cwidth = (static_cast<double>(cdim.first))/2.0;
double cheight = (static_cast<double>(cdim.second))/2.0;
double angle = get_angle(feature);
double xoff = cwidth*cos(angle) - cheight*sin(angle);
double yoff = cwidth*sin(angle) + cheight*cos(angle);
text_path_ptr path_ptr = text_path_ptr(new text_path()); text_path_ptr path_ptr = text_path_ptr(new text_path());
path_ptr->add_node(ci.character, -xoff, -yoff, angle);
return path_ptr; return path_ptr;
} }
unsigned int glyph_symbolizer::get_size(double value) const
{
double scale = (get_max_size()-get_min_size()) /
(get_max_value()-get_min_value());
double size = get_min_size() + (value - get_min_value())*scale;
if (size > get_max_size())
{
// size too large, clip it
size = get_max_size();
}
return static_cast<unsigned>(floor(size));
}
double glyph_symbolizer::get_angle(Feature const& feature) const
{
// Returns the angle of rotation of the glyph in radians
std::string key = get_azimuth_attr();
double azimuth = feature[key].to_double();
azimuth = std::fmod(azimuth, 360.0);
if (azimuth<0) azimuth += 360.0;
azimuth *= (M_PI/180.0);
double angle = atan2(cos(azimuth), sin(azimuth));
angle += get_angle_offset() * (M_PI/180.0);
angle = std::fmod(angle, 2*M_PI);
if (angle<0) angle += 2*M_PI;
return angle;
}
double glyph_symbolizer::get_value(Feature const& feature) const
{
std::string key = get_value_attr();
return feature[key].to_double();
}
} }

View file

@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
from nose.tools import * from nose.tools import *
from utilities import execution_path, save_data from utilities import execution_path, save_data, Todo
import os, mapnik2 import os, mapnik2
@ -11,7 +11,7 @@ def setup():
os.chdir(execution_path('.')) os.chdir(execution_path('.'))
def test_arrows_symbolizer(): def test_glyph_symbolizer():
srs = '+init=epsg:32630' srs = '+init=epsg:32630'
lyr = mapnik2.Layer('arrows') lyr = mapnik2.Layer('arrows')
lyr.datasource = mapnik2.Shapefile( lyr.datasource = mapnik2.Shapefile(
@ -21,16 +21,11 @@ def test_arrows_symbolizer():
_map = mapnik2.Map(256,256, srs) _map = mapnik2.Map(256,256, srs)
style = mapnik2.Style() style = mapnik2.Style()
rule = mapnik2.Rule() rule = mapnik2.Rule()
rule.filter = mapnik2.Expression("[value] > 0 and [azimuth]>-1") #XXX Need to mention an attribute in the expression sym = mapnik2.GlyphSymbolizer("DejaVu Sans Condensed", mapnik2.Expression("'A'"))
sym = mapnik2.GlyphSymbolizer("DejaVu Sans Condensed", "A")
sym.angle_offset = -90
sym.min_value = 1
sym.max_value = 100
sym.min_size = 1
sym.max_size = 100
sym.allow_overlap = True sym.allow_overlap = True
# Assigning a colorizer to the RasterSymbolizer tells the later sym.angle = mapnik2.Expression("[azimuth]-90")
# that it should use it to colorize the raw data raster sym.value = mapnik2.Expression("[value]")
sym.size = mapnik2.Expression("[value]")
sym.colorizer = mapnik2.RasterColorizer() sym.colorizer = mapnik2.RasterColorizer()
for value, color in [ for value, color in [
( 0, "#0044cc"), ( 0, "#0044cc"),
@ -49,9 +44,24 @@ def test_arrows_symbolizer():
im = mapnik2.Image(_map.width,_map.height) im = mapnik2.Image(_map.width,_map.height)
mapnik2.render(_map, im) mapnik2.render(_map, im)
save_data('test_arrows_symbolizer.png', im.tostring('png')) save_data('test_glyph_symbolizer.png', im.tostring('png'))
imdata = im.tostring() imdata = im.tostring()
assert len(imdata) > 0 assert len(imdata) > 0
raise Todo("Implement the process methods of the agg/cairo renderers for GlyphSymbolizer")
# we have features with 20 as a value so check that they're colored # we have features with 20 as a value so check that they're colored
assert '\xff\xff\xff\x00' in imdata assert '\xff\xff\xff\x00' in imdata
def test_load_save_map():
raise Todo("Implement XML de/serialization for GlyphSymbolizer")
map = mapnik2.Map(256,256)
in_map = "../data/good_maps/glyph_symbolizer.xml"
mapnik2.load_map(map, in_map)
out_map = mapnik2.save_map_to_string(map)
assert 'GlyphSymbolizer' in out_map
assert 'RasterSymbolizer' in out_map
assert 'RasterColorizer' in out_map
assert 'ColorBand' in out_map