diff --git a/bindings/python/mapnik_glyph_symbolizer.cpp b/bindings/python/mapnik_glyph_symbolizer.cpp index 2419f5ca6..233578504 100644 --- a/bindings/python/mapnik_glyph_symbolizer.cpp +++ b/bindings/python/mapnik_glyph_symbolizer.cpp @@ -1,6 +1,7 @@ +#include + #include #include -#include using mapnik::glyph_symbolizer; using mapnik::position; @@ -20,56 +21,74 @@ list get_displacement_list(const glyph_symbolizer& t) void export_glyph_symbolizer() { class_("GlyphSymbolizer", - init()) + init()) .add_property("face_name", - make_function(&glyph_symbolizer::get_face_name,return_value_policy()), - &glyph_symbolizer::set_face_name) + make_function(&glyph_symbolizer::get_face_name, + return_value_policy()), + &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()), - &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::get_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::get_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", make_function(&glyph_symbolizer::get_halo_fill, - return_value_policy()), + return_value_policy()), &glyph_symbolizer::set_halo_fill) .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()), - &glyph_symbolizer::set_value_attr) - .add_property("azimuth_attr", make_function( - &glyph_symbolizer::get_azimuth_attr, - return_value_policy()), - &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" ) ; - ; } diff --git a/include/mapnik/glyph_symbolizer.hpp b/include/mapnik/glyph_symbolizer.hpp index 1aaa1db9c..e2745f06f 100644 --- a/include/mapnik/glyph_symbolizer.hpp +++ b/include/mapnik/glyph_symbolizer.hpp @@ -1,103 +1,54 @@ #ifndef ARROW_SYMBOLIZER_HPP #define ARROW_SYMBOLIZER_HPP +// mapnik #include -#include +#include #include -#include -#include #include +// boost +#include + namespace mapnik { - struct MAPNIK_DECL glyph_symbolizer - { - glyph_symbolizer(std::string const& face_name, std::string const& c) + +typedef boost::tuple position; + +struct MAPNIK_DECL glyph_symbolizer +{ + 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,29 +84,51 @@ namespace mapnik { return halo_fill_; } + void set_halo_radius(unsigned radius) { halo_radius_ = radius; } - + unsigned get_halo_radius() const { return halo_radius_; } - std::string const& get_azimuth_attr() const + expression_ptr get_angle() const { - return azimuth_attr_; + return angle_; } - - void set_azimuth_attr(std::string azimuth_attr) + void set_angle(expression_ptr angle) { - azimuth_attr_ = azimuth_attr; + angle_ = angle; } - std::string const& get_value_attr() const + expression_ptr get_value() const { - return value_attr_; + return value_; + } + void set_value(expression_ptr value) + { + value_ = value; + } + + expression_ptr get_size() const + { + 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 @@ -163,41 +139,27 @@ 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_; - 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_; - }; +private: + std::string face_name_; + 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_; }; +}; // end mapnik namespace + #endif diff --git a/src/glyph_symbolizer.cpp b/src/glyph_symbolizer.cpp index a4217c2e8..191548b1d 100644 --- a/src/glyph_symbolizer.cpp +++ b/src/glyph_symbolizer.cpp @@ -7,63 +7,9 @@ namespace mapnik { 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 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(cdim.first))/2.0; - double cheight = (static_cast(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(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(); - } } diff --git a/tests/python_tests/glyph_symbolizer_test.py b/tests/python_tests/glyph_symbolizer_test.py index eea844faa..a0496b81e 100644 --- a/tests/python_tests/glyph_symbolizer_test.py +++ b/tests/python_tests/glyph_symbolizer_test.py @@ -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