initial skeleton of glyph_symbolizer (coarsely ported from arrow_symbolizer). Mapnik compiles but symbolizer does nothing useful yet and is going to be heavily modified to make use of expressions to be general enough to be usable for purposes other than rendering arrows representing vector direction/intensity

This commit is contained in:
Alberto Valverde 2010-03-18 20:04:35 +00:00
parent 53876d14a0
commit fc3a1beeef
19 changed files with 493 additions and 5 deletions

View file

@ -0,0 +1,75 @@
#include <boost/python.hpp>
#include <boost/tuple/tuple.hpp>
#include <mapnik/glyph_symbolizer.hpp>
using mapnik::glyph_symbolizer;
using mapnik::position;
using namespace boost::python;
list get_displacement_list(const glyph_symbolizer& t)
{
position pos = t.get_displacement();
double dx = boost::get<0>(pos);
double dy = boost::get<1>(pos);
boost::python::list disp;
disp.append(dx);
disp.append(dy);
return disp;
}
void export_glyph_symbolizer()
{
class_<glyph_symbolizer>("GlyphSymbolizer",
init<std::string const&,std::string const&>())
.add_property("face_name",
make_function(&glyph_symbolizer::get_face_name,return_value_policy<copy_const_reference>()),
&glyph_symbolizer::set_face_name)
.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)
.add_property("allow_overlap",
&glyph_symbolizer::get_allow_overlap,
&glyph_symbolizer::set_allow_overlap)
.add_property("avoid_edges",
&glyph_symbolizer::get_avoid_edges,
&glyph_symbolizer::set_avoid_edges)
.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<copy_const_reference>()),
&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<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("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"
)
;
;
}

View file

@ -60,6 +60,7 @@ void export_projection();
void export_proj_transform();
void export_view_transform();
void export_raster_colorizer();
void export_glyph_symbolizer();
#include <mapnik/version.hpp>
#include <mapnik/map.hpp>
@ -333,6 +334,7 @@ BOOST_PYTHON_MODULE(_mapnik2)
export_coord();
export_map();
export_raster_colorizer();
export_glyph_symbolizer();
def("render_to_file",&render_to_file1,
"\n"

View file

@ -44,6 +44,7 @@ using mapnik::shield_symbolizer;
using mapnik::text_symbolizer;
using mapnik::building_symbolizer;
using mapnik::markers_symbolizer;
using mapnik::glyph_symbolizer;
using mapnik::symbolizer;
using mapnik::to_expression_string;
@ -161,6 +162,7 @@ void export_rule()
implicitly_convertible<raster_symbolizer,symbolizer>();
implicitly_convertible<shield_symbolizer,symbolizer>();
implicitly_convertible<text_symbolizer,symbolizer>();
implicitly_convertible<glyph_symbolizer,symbolizer>();
class_<rule_type::symbolizers>("Symbolizers",init<>("TODO"))
.def(vector_indexing_suite<rule_type::symbolizers>())

View file

@ -39,6 +39,7 @@ using mapnik::shield_symbolizer;
using mapnik::text_symbolizer;
using mapnik::building_symbolizer;
using mapnik::markers_symbolizer;
using mapnik::glyph_symbolizer;
struct get_symbolizer_type : public boost::static_visitor<std::string>
{
@ -95,6 +96,11 @@ struct get_symbolizer_type : public boost::static_visitor<std::string>
return "markers";
}
std::string operator () ( const glyph_symbolizer & sym )
{
return "glyph";
}
};
std::string get_symbol_type(const symbolizer& symbol)

View file

@ -83,6 +83,9 @@ namespace mapnik {
void process(markers_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans);
void process(glyph_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans);
private:
T & pixmap_;
unsigned width_;

View file

@ -104,6 +104,9 @@ namespace mapnik {
void process(markers_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans);
void process(glyph_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans);
protected:
Map const& m_;
Cairo::RefPtr<Cairo::Context> context_;

View file

@ -0,0 +1,203 @@
#ifndef ARROW_SYMBOLIZER_HPP
#define ARROW_SYMBOLIZER_HPP
#include <mapnik/raster_colorizer.hpp>
#include <mapnik/text_symbolizer.hpp>
#include <mapnik/text_path.hpp>
#include <mapnik/unicode.hpp>
#include <mapnik/config_error.hpp>
#include <mapnik/font_engine_freetype.hpp>
namespace mapnik
{
struct MAPNIK_DECL glyph_symbolizer
{
glyph_symbolizer(std::string const& face_name, std::string const& c)
: face_name_(face_name),
char_(c),
angle_offset_(0.0),
min_size_(10),
max_size_(100),
min_value_(0),
max_value_(1),
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");
}
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
{
return char_;
}
void set_char(std::string 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_;
}
void set_allow_overlap(bool allow_overlap)
{
allow_overlap_ = allow_overlap;
}
bool get_avoid_edges() const
{
return avoid_edges_;
}
void set_avoid_edges(bool avoid_edges)
{
avoid_edges_ = avoid_edges;
}
void set_displacement(double x, double y)
{
displacement_ = boost::make_tuple(x,y);
}
position const& get_displacement() const
{
return displacement_;
}
void set_halo_fill(color const& fill)
{
halo_fill_ = fill;
}
color const& get_halo_fill() const
{
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
{
return azimuth_attr_;
}
void set_azimuth_attr(std::string azimuth_attr)
{
azimuth_attr_ = azimuth_attr;
}
std::string const& get_value_attr() const
{
return value_attr_;
}
raster_colorizer_ptr get_colorizer() const
{
return colorizer_;
}
void set_colorizer(raster_colorizer_ptr const& 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,
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_;
};
};
#endif

View file

@ -32,6 +32,7 @@
#include <mapnik/shield_symbolizer.hpp>
#include <mapnik/text_symbolizer.hpp>
#include <mapnik/markers_symbolizer.hpp>
#include <mapnik/glyph_symbolizer.hpp>
#include <mapnik/feature.hpp>
#include <mapnik/expression_grammar.hpp>
@ -101,6 +102,12 @@ namespace mapnik
{
return (&lhs == &rhs);
}
inline bool operator==(glyph_symbolizer const& lhs,
glyph_symbolizer const& rhs)
{
return (&lhs == &rhs);
}
typedef boost::variant<point_symbolizer,
line_symbolizer,
line_pattern_symbolizer,
@ -110,7 +117,8 @@ namespace mapnik
shield_symbolizer,
text_symbolizer,
building_symbolizer,
markers_symbolizer> symbolizer;
markers_symbolizer,
glyph_symbolizer> symbolizer;

View file

@ -27,6 +27,7 @@
#define __TEXT_PATH_H__
#include <boost/utility.hpp>
#include <boost/shared_ptr.hpp>
#include <unicode/unistr.h>
namespace mapnik
@ -164,6 +165,8 @@ namespace mapnik
nodes_.clear();
}
};
typedef boost::shared_ptr<text_path> text_path_ptr;
}
#endif

View file

@ -624,6 +624,37 @@ struct to_expression_string : public boost::static_visitor<std::string>
return ss.str();
}
};
struct to_double : public boost::static_visitor<double>
{
double operator() (double val) const
{
return val;
}
double operator() (std::string const& val) const
{
std::istringstream stream(val);
double t;
stream >> t;
return t;
}
double operator() (UnicodeString const& val) const
{
std::string utf8;
to_utf8(val,utf8);
std::istringstream stream(utf8);
double t;
stream >> t;
return t;
}
double operator() (value_null const& val) const
{
boost::ignore_unused_variable_warning(val);
return 0.0;
}
};
}
class value
@ -696,6 +727,12 @@ public:
{
return boost::apply_visitor(impl::to_unicode(),base_);
}
double to_double() const
{
return boost::apply_visitor(impl::to_double(),base_);
}
};
inline const value operator+(value const& p1,value const& p2)

View file

@ -109,6 +109,7 @@ source = Split(
symbolizer.cpp
arrow.cpp
unicode.cpp
glyph_symbolizer.cpp
"""
)

View file

@ -945,5 +945,13 @@ void agg_renderer<T>::process(text_symbolizer const& sym,
}
}
}
template <typename T>
void agg_renderer<T>::process(glyph_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
}
template class agg_renderer<image_32>;
}

View file

@ -1041,6 +1041,12 @@ void cairo_renderer_base::process(markers_symbolizer const& sym,
{
}
void cairo_renderer_base::process(glyph_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
}
void cairo_renderer_base::process(text_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)

69
src/glyph_symbolizer.cpp Normal file
View file

@ -0,0 +1,69 @@
#include <mapnik/glyph_symbolizer.hpp>
#include <mapnik/value.hpp>
#include <boost/lexical_cast.hpp>
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

@ -256,6 +256,11 @@ namespace mapnik
{
// FIXME!!!!!
}
void operator () ( glyph_symbolizer const& )
{
// FIXME!!!!!
}
private:
serialize_symbolizer();
void add_image_attributes(ptree & node, const symbolizer_with_image & sym)

BIN
tests/data/shp/arrows.dbf Normal file

Binary file not shown.

BIN
tests/data/shp/arrows.shp Normal file

Binary file not shown.

BIN
tests/data/shp/arrows.shx Normal file

Binary file not shown.

View file

@ -0,0 +1,57 @@
#!/usr/bin/env python
from nose.tools import *
from utilities import execution_path, save_data
import os, mapnik2
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_arrows_symbolizer():
srs = '+init=epsg:32630'
lyr = mapnik2.Layer('arrows')
lyr.datasource = mapnik2.Shapefile(
file = '../data/shp/arrows.shp',
)
lyr.srs = srs
_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.allow_overlap = True
# Assigning a colorizer to the RasterSymbolizer tells the later
# that it should use it to colorize the raw data raster
sym.colorizer = mapnik2.RasterColorizer()
for value, color in [
( 0, "#0044cc"),
( 10, "#00cc00"),
( 20, "#ffff00"),
( 30, "#ff7f00"),
( 40, "#ff0000"),
]:
sym.colorizer.append_band(value, mapnik2.Color(color))
rule.symbols.append(sym)
style.rules.append(rule)
_map.append_style('foo', style)
lyr.styles.append('foo')
_map.layers.append(lyr)
_map.zoom_to_box(mapnik2.Box2d(0,0,8,8))
im = mapnik2.Image(_map.width,_map.height)
mapnik2.render(_map, im)
save_data('test_arrows_symbolizer.png', im.tostring('png'))
imdata = im.tostring()
assert len(imdata) > 0
# we have features with 20 as a value so check that they're colored
assert '\xff\xff\xff\x00' in imdata