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/tuple/tuple.hpp>
#include <mapnik/glyph_symbolizer.hpp>
using mapnik::glyph_symbolizer;
using mapnik::position;
@ -20,22 +21,33 @@ list get_displacement_list(const glyph_symbolizer& t)
void export_glyph_symbolizer()
{
class_<glyph_symbolizer>("GlyphSymbolizer",
init<std::string const&,std::string const&>())
init<std::string,mapnik::expression_ptr>())
.add_property("face_name",
make_function(&glyph_symbolizer::get_face_name,return_value_policy<copy_const_reference>()),
&glyph_symbolizer::set_face_name)
make_function(&glyph_symbolizer::get_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",
make_function(&glyph_symbolizer::get_char,return_value_policy<copy_const_reference>()),
&glyph_symbolizer::set_char)
.add_property("angle_offset",
&glyph_symbolizer::get_angle_offset,
&glyph_symbolizer::set_angle_offset)
&glyph_symbolizer::get_char,
&glyph_symbolizer::set_char,
"Get/Set the char expression. The char is the unicode "
"character indexing the glyph in the font referred by "
"face_name."
)
.add_property("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",
&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("set_displacement", &glyph_symbolizer::set_displacement)
.add_property("halo_fill",
@ -45,31 +57,38 @@ void export_glyph_symbolizer()
.add_property("halo_radius",
&glyph_symbolizer::get_halo_radius,
&glyph_symbolizer::set_halo_radius)
.add_property("value_attr", make_function(
&glyph_symbolizer::get_value_attr,
return_value_policy<copy_const_reference>()),
&glyph_symbolizer::set_value_attr)
.add_property("azimuth_attr", make_function(
&glyph_symbolizer::get_azimuth_attr,
return_value_policy<copy_const_reference>()),
&glyph_symbolizer::set_azimuth_attr)
.add_property("size",
&glyph_symbolizer::get_size,
&glyph_symbolizer::set_size,
"Get/Set the size expression used to size the glyph."
)
.add_property("angle",
&glyph_symbolizer::get_angle,
&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",
&glyph_symbolizer::get_colorizer,
&glyph_symbolizer::set_colorizer,
"Get/Set the RasterColorizer used to color the arrows.\n"
"\n"
"Usage:\n"
"\n"
">>> 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"
"Get/Set the RasterColorizer used to color the glyph "
"depending on the 'value' expression (which must be "
"defined).\n"
"Only needed if no explicit color is provided"
)
;
;
}

View file

@ -1,103 +1,54 @@
#ifndef ARROW_SYMBOLIZER_HPP
#define ARROW_SYMBOLIZER_HPP
// mapnik
#include <mapnik/raster_colorizer.hpp>
#include <mapnik/text_symbolizer.hpp>
#include <mapnik/expression_node.hpp>
#include <mapnik/text_path.hpp>
#include <mapnik/unicode.hpp>
#include <mapnik/config_error.hpp>
#include <mapnik/font_engine_freetype.hpp>
// boost
#include <boost/tuple/tuple.hpp>
namespace mapnik
{
typedef boost::tuple<double,double> position;
struct MAPNIK_DECL glyph_symbolizer
{
glyph_symbolizer(std::string const& face_name, std::string const& c)
glyph_symbolizer(std::string face_name, expression_ptr c)
: face_name_(face_name),
char_(c),
angle_offset_(0.0),
min_size_(10),
max_size_(100),
min_value_(0),
max_value_(1),
angle_(),
value_(),
size_(),
color_(),
colorizer_(),
allow_overlap_(false),
avoid_edges_(false),
displacement_(0.0, 0.0),
halo_fill_(color(255,255,255)),
halo_radius_(0),
azimuth_attr_("azimuth"),
value_attr_("value")
{
if (get_min_size() > get_max_size())
throw config_error("Invalid size range");
}
halo_radius_(0) {}
std::string const& get_face_name() const
{
return face_name_;
}
void set_face_name(std::string face_name)
{
face_name_ = face_name;
}
std::string const& get_char() const
expression_ptr get_char() const
{
return char_;
}
void set_char(std::string c)
void set_char(expression_ptr 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
{
return allow_overlap_;
@ -106,6 +57,7 @@ namespace mapnik
{
allow_overlap_ = allow_overlap;
}
bool get_avoid_edges() const
{
return avoid_edges_;
@ -114,6 +66,7 @@ namespace mapnik
{
avoid_edges_ = avoid_edges;
}
void set_displacement(double x, double y)
{
displacement_ = boost::make_tuple(x,y);
@ -122,6 +75,7 @@ namespace mapnik
{
return displacement_;
}
void set_halo_fill(color const& fill)
{
halo_fill_ = fill;
@ -130,6 +84,7 @@ namespace mapnik
{
return halo_fill_;
}
void set_halo_radius(unsigned radius)
{
halo_radius_ = radius;
@ -140,19 +95,40 @@ namespace mapnik
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
@ -164,40 +140,26 @@ namespace mapnik
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,
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:
std::string face_name_;
std::string char_;
double angle_offset_;
unsigned min_size_;
unsigned max_size_;
float min_value_;
float max_value_;
expression_ptr char_;
expression_ptr angle_;
expression_ptr value_;
expression_ptr size_;
expression_ptr color_;
raster_colorizer_ptr colorizer_;
bool allow_overlap_;
bool avoid_edges_;
position displacement_;
color halo_fill_;
unsigned halo_radius_;
std::string azimuth_attr_;
std::string value_attr_;
};
};
}; // end mapnik namespace
#endif

View file

@ -9,61 +9,7 @@ namespace mapnik
text_path_ptr glyph_symbolizer::get_text_path(face_set_ptr const& faces,
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());
path_ptr->add_node(ci.character, -xoff, -yoff, angle);
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
from nose.tools import *
from utilities import execution_path, save_data
from utilities import execution_path, save_data, Todo
import os, mapnik2
@ -11,7 +11,7 @@ def setup():
os.chdir(execution_path('.'))
def test_arrows_symbolizer():
def test_glyph_symbolizer():
srs = '+init=epsg:32630'
lyr = mapnik2.Layer('arrows')
lyr.datasource = mapnik2.Shapefile(
@ -21,16 +21,11 @@ def test_arrows_symbolizer():
_map = mapnik2.Map(256,256, srs)
style = mapnik2.Style()
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", "A")
sym.angle_offset = -90
sym.min_value = 1
sym.max_value = 100
sym.min_size = 1
sym.max_size = 100
sym = mapnik2.GlyphSymbolizer("DejaVu Sans Condensed", mapnik2.Expression("'A'"))
sym.allow_overlap = True
# Assigning a colorizer to the RasterSymbolizer tells the later
# that it should use it to colorize the raw data raster
sym.angle = mapnik2.Expression("[azimuth]-90")
sym.value = mapnik2.Expression("[value]")
sym.size = mapnik2.Expression("[value]")
sym.colorizer = mapnik2.RasterColorizer()
for value, color in [
( 0, "#0044cc"),
@ -49,9 +44,24 @@ def test_arrows_symbolizer():
im = mapnik2.Image(_map.width,_map.height)
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()
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
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