Merge branch 'master' into python-textplacement

This commit is contained in:
Hermann Kraus 2012-02-17 20:54:48 +01:00
commit 3508ec5fb4
26 changed files with 1118 additions and 659 deletions

View File

@ -31,7 +31,6 @@
#include <boost/python/tuple.hpp>
#include <boost/python/to_python_converter.hpp>
#include <boost/python.hpp>
#include <boost/scoped_array.hpp>
// mapnik
#include <mapnik/feature.hpp>

View File

@ -70,8 +70,8 @@ public:
void end_map_processing(Map const& map);
void start_layer_processing(layer const& lay);
void end_layer_processing(layer const& lay);
void render_marker(const int x, const int y, marker &marker, const agg::trans_affine & tr, double opacity);
void render_marker(double x, double y, marker & marker, agg::trans_affine const& tr, double opacity);
void process(point_symbolizer const& sym,
mapnik::feature_ptr const& feature,
proj_transform const& prj_trans);

View File

@ -159,24 +159,26 @@ public:
value_type const& get(context_type::key_type const& key) const
{
context_type::map_type::const_iterator itr = ctx_->mapping_.find(key);
if (itr != ctx_->mapping_.end()
&& itr->second < data_.size())
{
return data_[itr->second];
}
else
{
throw std::out_of_range(std::string("Key does not exist: '") + key + "'");
}
if (itr != ctx_->mapping_.end())
return get(itr->second);
else
throw std::out_of_range(std::string("Key does not exist: '") + key + "'");
}
value_type const& get(std::size_t index) const
{
if (index < data_.size())
return data_[index];
throw std::out_of_range("Index out of range");
}
boost::optional<value_type const&> get_optional(std::size_t index) const
{
if (index < data_.size())
return boost::optional<value_type const&>(data_[index]);
return boost::optional<value_type const&>();
}
std::size_t size() const
{
return data_.size();

View File

@ -0,0 +1,57 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2012 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
*
*****************************************************************************/
#ifndef FORMATTING_REGISTRY_HPP
#define FORMATTING_REGISTRY_HPP
// mapnik
#include <mapnik/utils.hpp>
#include <mapnik/formatting/base.hpp>
// boost
#include <boost/utility.hpp>
// stl
#include <string>
#include <map>
namespace mapnik
{
namespace formatting
{
typedef node_ptr (*from_xml_function_ptr)(boost::property_tree::ptree const& xml);
class registry : public singleton<registry, CreateStatic>,
private boost::noncopyable
{
public:
registry();
~registry() {}
void register_name(std::string name, from_xml_function_ptr ptr, bool overwrite=false);
node_ptr from_xml(std::string name, boost::property_tree::ptree const& xml);
private:
std::map<std::string, from_xml_function_ptr> map_;
};
} //ns formatting
} //ns mapnik
#endif // FORMATTING_REGISTRY_HPP

View File

@ -75,10 +75,19 @@ class hextree : private boost::noncopyable
~node ()
{
for (unsigned i = 0; i < 16; ++i)
if (children_[i] != 0) delete children_[i],children_[i]=0;
{
if (children_[i] != 0)
{
delete children_[i];
children_[i]=0;
}
}
}
bool is_leaf() const { return children_count == 0; }
bool is_leaf() const
{
return (children_count == 0);
}
node * children_[16];
// sum of values for computing mean value using count or pixel_count
double reds;
@ -101,8 +110,10 @@ class hextree : private boost::noncopyable
bool operator() (const node * lhs, const node* rhs) const
{
if (lhs->reduce_cost != rhs->reduce_cost)
return lhs->reduce_cost > rhs->reduce_cost;
return lhs > rhs;
{
return (lhs->reduce_cost > rhs->reduce_cost);
}
return (lhs > rhs);
}
};
@ -126,13 +137,13 @@ class hextree : private boost::noncopyable
enum transparency_mode_t {NO_TRANSPARENCY=0, BINARY_TRANSPARENCY=1, FULL_TRANSPARENCY=2};
unsigned trans_mode_;
inline double gamma(const double &b, const double &g) const
inline double gamma(double b, double g) const
{
return 255 * std::pow(b/255, g);
}
public:
explicit hextree(unsigned max_colors=256, const double &g=2.0)
explicit hextree(unsigned max_colors=256, double g=2.0)
: max_colors_(max_colors),
colors_(0),
has_holes_(false),
@ -142,18 +153,23 @@ public:
setGamma(g);
}
~hextree() { delete root_;}
~hextree()
{
delete root_;
}
void setMaxColors(unsigned max_colors)
{
max_colors_ = max_colors;
}
void setGamma(const double &g)
void setGamma(double g)
{
gamma_ = g;
for (unsigned i=0; i<256; i++)
{
gammaLUT_[i] = gamma(i, 1/gamma_);
}
}
void setTransMode(unsigned t)
@ -201,7 +217,9 @@ public:
if (level == InsertPolicy::MAX_LEVELS)
{
if (cur_node->pixel_count == 1)
{
++colors_;
}
break;
}
@ -222,9 +240,13 @@ public:
byte a = preprocessAlpha(c.a);
unsigned ind=0;
if (a < InsertPolicy::MIN_ALPHA || colors_ == 0)
{
return 0;
}
if (colors_ == 1)
{
return pal_remap_[has_holes_?1:0];
}
rgba_hash_table::iterator it = color_hashmap_.find(c);
if (it == color_hashmap_.end())
@ -253,8 +275,10 @@ public:
db = sorted_pal_[i].b - c.b;
da = sorted_pal_[i].a - a;
// stop criteria based on properties of used sorting
if ((dr+db+dg+da) * (dr+db+dg+da) / 4 > dist)
if (((dr+db+dg+da) * (dr+db+dg+da) / 4 > dist))
{
break;
}
newdist = dr*dr + dg*dg + db*db + da*da;
if (newdist < dist)
{
@ -270,7 +294,9 @@ public:
da = sorted_pal_[i].a - a;
// stop criteria based on properties of used sorting
if ((dr+db+dg+da) * (dr+db+dg+da) / 4 > dist)
{
break;
}
newdist = dr*dr + dg*dg + db*db + da*da;
if (newdist < dist)
{
@ -282,7 +308,9 @@ public:
color_hashmap_[c] = ind;
}
else
{
ind = it->second;
}
return pal_remap_[ind];
}
@ -303,7 +331,7 @@ public:
root_ = new node();
// sort palette for binary searching in quantization
std::sort(sorted_pal_.begin(), sorted_pal_.end(),rgba::mean_sort_cmp());
std::sort(sorted_pal_.begin(), sorted_pal_.end(), rgba::mean_sort_cmp());
// returned palette is rearanged, so that colors with a<255 are at the begining
pal_remap_.resize(sorted_pal_.size());
@ -332,25 +360,34 @@ private:
void print_tree(node *r, int d=0, int id=0) const
{
for (int i=0; i<d; i++)
{
printf("\t");
}
if (r->count>0)
{
printf("%d: (+%d/%d/%.5f) (%d %d %d %d)\n",
id, (int)r->count, (int)r->pixel_count, r->reduce_cost,
(int)round(gamma(r->reds / r->count, gamma_)),
(int)round(gamma(r->greens / r->count, gamma_)),
(int)round(gamma(r->blues / r->count, gamma_)),
(int)(r->alphas / r->count));
}
else
{
printf("%d: (%d/%d/%.5f) (%d %d %d %d)\n", id,
(int)r->count, (int)r->pixel_count, r->reduce_cost,
(int)round(gamma(r->reds / r->pixel_count, gamma_)),
(int)round(gamma(r->greens / r->pixel_count, gamma_)),
(int)round(gamma(r->blues / r->pixel_count, gamma_)),
(int)(r->alphas / r->pixel_count));
for (unsigned idx=0; idx < 16; ++idx) if (r->children_[idx] != 0)
{
print_tree(r->children_[idx], d+1, idx);
}
}
for (unsigned idx=0; idx < 16; ++idx)
{
if (r->children_[idx] != 0)
{
print_tree(r->children_[idx], d+1, idx);
}
}
}
// traverse tree and search for nodes with count!=0, that represent single color.
@ -368,10 +405,13 @@ private:
(byte)round(gamma(itr->greens / count, gamma_)),
(byte)round(gamma(itr->blues / count, gamma_)), a));
}
for (unsigned idx=0; idx < 16; ++idx) if (itr->children_[idx] != 0)
{
create_palette_rek(palette, itr->children_[idx]);
}
for (unsigned idx=0; idx < 16; ++idx)
{
if (itr->children_[idx] != 0)
{
create_palette_rek(palette, itr->children_[idx]);
}
}
}
// assign value to r, representing some penalty for assigning one
@ -381,7 +421,9 @@ private:
//initial small value, so that all nodes have >0 cost
r->reduce_cost = r->pixel_count/1000.0;
if (r->children_count==0)
{
return;
}
// mean color of all pixels in subtree
double mean_r = r->reds / r->pixel_count;
double mean_g = r->greens / r->pixel_count;
@ -421,20 +463,20 @@ private:
std::set<node*,node_rev_cmp> colored_leaves_heap;
colored_leaves_heap.insert(root_);
while(!colored_leaves_heap.empty() && colors_ < max_colors_ && tries < 16)
while((!colored_leaves_heap.empty() && (colors_ < max_colors_) && (tries < 16)))
{
// select worst node to remove it from palette and replace with children
node * cur_node = *colored_leaves_heap.begin();
colored_leaves_heap.erase(colored_leaves_heap.begin());
if (cur_node->children_count + colors_ - 1 > max_colors_)
if (((cur_node->children_count + colors_ - 1) > max_colors_))
{
tries++;
continue; // try few times, maybe next will have less children
}
tries=0;
tries = 0;
// ignore leaves and also nodes with small mean error and not excessive number of pixels
if ((cur_node->reduce_cost / cur_node->pixel_count + 1) * std::log(double(cur_node->pixel_count)) > 15
&& cur_node->children_count > 0)
if (((cur_node->reduce_cost / cur_node->pixel_count + 1) * std::log(double(cur_node->pixel_count))) > 15
&& (cur_node->children_count > 0))
{
colors_--;
cur_node->count = 0;

View File

@ -141,10 +141,10 @@ protected:
box2d<double> marker_ext_;
boost::optional<marker_ptr> marker_;
agg::trans_affine transform_;
int marker_w_;
int marker_h_;
int marker_x_;
int marker_y_;
double marker_w_;
double marker_h_;
double marker_x_;
double marker_y_;
// F***ing templates...
// http://womble.decadent.org.uk/c++/template-faq.html#base-lookup
using text_symbolizer_helper<FaceManagerT, DetectorT>::geometries_to_process_;

View File

@ -0,0 +1,57 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2012 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
*
*****************************************************************************/
#ifndef PLACEMENTS_REGISTRY_HPP
#define PLACEMENTS_REGISTRY_HPP
// mapnik
#include <mapnik/utils.hpp>
#include <mapnik/text_placements/base.hpp>
// boost
#include <boost/utility.hpp>
// stl
#include <string>
#include <map>
namespace mapnik
{
namespace placements
{
typedef text_placements_ptr (*from_xml_function_ptr)(boost::property_tree::ptree const& xml);
class registry : public singleton<registry, CreateStatic>,
private boost::noncopyable
{
public:
registry();
~registry() {}
void register_name(std::string name, from_xml_function_ptr ptr, bool overwrite=false);
text_placements_ptr from_xml(std::string name, boost::property_tree::ptree const& xml);
private:
std::map<std::string, from_xml_function_ptr> map_;
};
} //ns placements
} //ns mapnik
#endif // PLACEMENTS_REGISTRY_HPP

View File

@ -498,7 +498,7 @@ void csv_datasource::parse_csv(T& stream,
(
qi::lit("POINT") >> '('
>> double_[ref(x) = _1]
>> double_[ref(y) = _1] >> ')'
>> double_[ref(y) = _1] >> ')'
),
ascii::space);

View File

@ -75,6 +75,7 @@
// boost
#include <boost/utility.hpp>
#include <boost/make_shared.hpp>
#include <boost/math/special_functions/round.hpp>
// stl
#ifdef MAPNIK_DEBUG
@ -223,7 +224,7 @@ void agg_renderer<T>::end_layer_processing(layer const&)
}
template <typename T>
void agg_renderer<T>::render_marker(const int x, const int y, marker &marker, const agg::trans_affine & tr, double opacity)
void agg_renderer<T>::render_marker(double x, double y, marker & marker, agg::trans_affine const& tr, double opacity)
{
if (marker.is_vector())
{
@ -262,7 +263,11 @@ void agg_renderer<T>::render_marker(const int x, const int y, marker &marker, co
}
else
{
pixmap_.set_rectangle_alpha2(**marker.get_bitmap_data(), x, y, opacity);
//TODO: Add subpixel support
pixmap_.set_rectangle_alpha2(**marker.get_bitmap_data(),
boost::math::iround(x),
boost::math::iround(y),
opacity);
}
}

View File

@ -151,8 +151,6 @@ source = Split(
markers_symbolizer.cpp
metawriter.cpp
raster_colorizer.cpp
text_placements.cpp
text_processing.cpp
wkt/wkt_factory.cpp
metawriter_inmem.cpp
metawriter_factory.cpp
@ -165,7 +163,19 @@ source = Split(
warp.cpp
json/feature_collection_parser.cpp
markers_placement.cpp
processed_text.cpp
formatting/base.cpp
formatting/expression.cpp
formatting/list.cpp
formatting/text.cpp
formatting/format.cpp
formatting/registry.cpp
text_placements/registry.cpp
text_placements/base.cpp
text_placements/dummy.cpp
text_placements/list.cpp
text_placements/simple.cpp
text_properties.cpp
"""
)

View File

@ -22,6 +22,7 @@
#include <mapnik/feature_kv_iterator.hpp>
#include <mapnik/feature.hpp>
#include <boost/optional.hpp>
namespace mapnik {
@ -44,7 +45,9 @@ bool feature_kv_iterator::equal( feature_kv_iterator const& other) const
feature_kv_iterator::value_type const& feature_kv_iterator::dereference() const
{
boost::get<0>(kv_) = itr_->first;
boost::get<1>(kv_) = f_.get(itr_->first);
boost::optional<mapnik::value const&> val = f_.get_optional(itr_->second);
if (val) boost::get<1>(kv_) = *val;
else boost::get<1>(kv_) = value_null();
return kv_;
}

70
src/formatting/base.cpp Normal file
View File

@ -0,0 +1,70 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2012 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
*
*****************************************************************************/
// mapnik
#include <mapnik/formatting/base.hpp>
#include <mapnik/formatting/list.hpp>
#include <mapnik/formatting/registry.hpp>
// boost
#include <boost/property_tree/ptree.hpp>
namespace mapnik {
namespace formatting {
void node::to_xml(boost::property_tree::ptree &xml) const
{
//TODO: Should this throw a config_error?
#ifdef MAPNIK_DEBUG
std::cerr << "Error: Trying to write unsupported node type to XML.\n";
#endif
}
node_ptr node::from_xml(boost::property_tree::ptree const& xml)
{
list_node *list = new list_node();
node_ptr list_ptr(list);
boost::property_tree::ptree::const_iterator itr = xml.begin();
boost::property_tree::ptree::const_iterator end = xml.end();
for (; itr != end; ++itr) {
if (itr->first == "<xmlcomment>" || itr->first == "<xmlattr>" || itr->first == "Placement")
{
continue;
}
node_ptr n = registry::instance()->from_xml(itr->first, itr->second);
if (n) list->push_back(n);
}
if (list->get_children().size() == 1) {
return list->get_children()[0];
} else if (list->get_children().size() > 1) {
return list_ptr;
} else {
return node_ptr();
}
}
void node::add_expressions(expression_set &output) const
{
//Do nothing by default
}
} //ns formatting
} //ns mapnik

114
src/formatting/format.cpp Normal file
View File

@ -0,0 +1,114 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2012 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
*
*****************************************************************************/
#include <mapnik/formatting/format.hpp>
#include <mapnik/ptree_helpers.hpp>
namespace mapnik {
using boost::property_tree::ptree;
namespace formatting {
void format_node::to_xml(ptree &xml) const
{
ptree &new_node = xml.push_back(ptree::value_type("Format", ptree()))->second;
if (face_name) set_attr(new_node, "face-name", *face_name);
if (text_size) set_attr(new_node, "size", *text_size);
if (character_spacing) set_attr(new_node, "character-spacing", *character_spacing);
if (line_spacing) set_attr(new_node, "line-spacing", *line_spacing);
if (text_opacity) set_attr(new_node, "opacity", *text_opacity);
if (wrap_before) set_attr(new_node, "wrap-before", *wrap_before);
if (wrap_char) set_attr(new_node, "wrap-character", *wrap_char);
if (text_transform) set_attr(new_node, "text-transform", *text_transform);
if (fill) set_attr(new_node, "fill", *fill);
if (halo_fill) set_attr(new_node, "halo-fill", *halo_fill);
if (halo_radius) set_attr(new_node, "halo-radius", *halo_radius);
if (child_) child_->to_xml(new_node);
}
node_ptr format_node::from_xml(ptree const& xml)
{
format_node *n = new format_node();
node_ptr np(n);
node_ptr child = node::from_xml(xml);
n->set_child(child);
n->face_name = get_opt_attr<std::string>(xml, "face-name");
/*TODO: Fontset is problematic. We don't have the fontsets pointer here... */
n->text_size = get_opt_attr<unsigned>(xml, "size");
n->character_spacing = get_opt_attr<unsigned>(xml, "character-spacing");
n->line_spacing = get_opt_attr<unsigned>(xml, "line-spacing");
n->text_opacity = get_opt_attr<double>(xml, "opactity");
boost::optional<boolean> wrap = get_opt_attr<boolean>(xml, "wrap-before");
if (wrap) n->wrap_before = *wrap;
n->wrap_char = get_opt_attr<unsigned>(xml, "wrap-character");
n->text_transform = get_opt_attr<text_transform_e>(xml, "text-transform");
n->fill = get_opt_attr<color>(xml, "fill");
n->halo_fill = get_opt_attr<color>(xml, "halo-fill");
n->halo_radius = get_opt_attr<double>(xml, "halo-radius");
return np;
}
void format_node::apply(char_properties const& p, const Feature &feature, processed_text &output) const
{
char_properties new_properties = p;
if (face_name) new_properties.face_name = *face_name;
if (text_size) new_properties.text_size = *text_size;
if (character_spacing) new_properties.character_spacing = *character_spacing;
if (line_spacing) new_properties.line_spacing = *line_spacing;
if (text_opacity) new_properties.text_opacity = *text_opacity;
if (wrap_before) new_properties.wrap_before = *wrap_before;
if (wrap_char) new_properties.wrap_char = *wrap_char;
if (text_transform) new_properties.text_transform = *text_transform;
if (fill) new_properties.fill = *fill;
if (halo_fill) new_properties.halo_fill = *halo_fill;
if (halo_radius) new_properties.halo_radius = *halo_radius;
if (child_) {
child_->apply(new_properties, feature, output);
} else {
#ifdef MAPNIK_DEBUG
std::cerr << "Warning: Useless format: No text to format\n";
#endif
}
}
void format_node::set_child(node_ptr child)
{
child_ = child;
}
node_ptr format_node::get_child() const
{
return child_;
}
void format_node::add_expressions(expression_set &output) const
{
if (child_) child_->add_expressions(output);
}
} //ns formatting
} //ns mapnik

85
src/formatting/list.cpp Normal file
View File

@ -0,0 +1,85 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2012 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
*
*****************************************************************************/
#include <mapnik/formatting/list.hpp>
namespace mapnik {
using boost::property_tree::ptree;
namespace formatting {
/************************************************************/
void list_node::to_xml(boost::property_tree::ptree &xml) const
{
std::vector<node_ptr>::const_iterator itr = children_.begin();
std::vector<node_ptr>::const_iterator end = children_.end();
for (;itr != end; itr++)
{
(*itr)->to_xml(xml);
}
}
void list_node::apply(char_properties const& p, Feature const& feature, processed_text &output) const
{
std::vector<node_ptr>::const_iterator itr = children_.begin();
std::vector<node_ptr>::const_iterator end = children_.end();
for (;itr != end; itr++)
{
(*itr)->apply(p, feature, output);
}
}
void list_node::add_expressions(expression_set &output) const
{
std::vector<node_ptr>::const_iterator itr = children_.begin();
std::vector<node_ptr>::const_iterator end = children_.end();
for (;itr != end; itr++)
{
(*itr)->add_expressions(output);
}
}
void list_node::push_back(node_ptr n)
{
children_.push_back(n);
}
void list_node::clear()
{
children_.clear();
}
void list_node::set_children(std::vector<node_ptr> const& children)
{
children_ = children;
}
std::vector<node_ptr> const& list_node::get_children() const
{
return children_;
}
} // ns mapnik
} // ns formatting

View File

@ -0,0 +1,56 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2012 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
*
*****************************************************************************/
// mapnik
#include <mapnik/formatting/registry.hpp>
#include <mapnik/formatting/text.hpp>
#include <mapnik/formatting/format.hpp>
#include <mapnik/formatting/expression.hpp>
namespace mapnik
{
namespace formatting
{
registry::registry()
{
register_name("<xmltext>", &text_node::from_xml);
register_name("Format", &format_node::from_xml);
register_name("ExpressionFormat", &expression_format::from_xml);
}
void registry::register_name(std::string name, from_xml_function_ptr ptr, bool overwrite)
{
if (overwrite) {
map_[name] = ptr;
} else {
map_.insert(make_pair(name, ptr));
}
}
node_ptr registry::from_xml(std::string name, const boost::property_tree::ptree &xml)
{
std::map<std::string, from_xml_function_ptr>::const_iterator itr = map_.find(name);
if (itr == map_.end()) throw config_error("Unknown element '" + name + "'");
return itr->second(xml);
}
} //ns formatting
} //ns mapnik

99
src/formatting/text.cpp Normal file
View File

@ -0,0 +1,99 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2012 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
*
*****************************************************************************/
// mapnik
#include <mapnik/formatting/text.hpp>
#include <mapnik/expression_string.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/feature.hpp>
#include <mapnik/text_properties.hpp>
#include <mapnik/processed_text.hpp>
// boost
#include <boost/algorithm/string.hpp>
namespace mapnik
{
namespace formatting
{
using boost::property_tree::ptree;
void text_node::to_xml(ptree &xml) const
{
ptree &new_node = xml.push_back(ptree::value_type(
"<xmltext>", ptree()))->second;
new_node.put_value(to_expression_string(*text_));
}
node_ptr text_node::from_xml(boost::property_tree::ptree const& xml)
{
std::string data = xml.data();
boost::trim(data);
if (data.empty()) return node_ptr(); //No text
return node_ptr(new text_node(parse_expression(data, "utf8")));
}
void text_node::apply(char_properties const& p, Feature const& feature, processed_text &output) const
{
UnicodeString text_str = boost::apply_visitor(evaluate<Feature,value_type>(feature), *text_).to_unicode();
if (p.text_transform == UPPERCASE)
{
text_str = text_str.toUpper();
}
else if (p.text_transform == LOWERCASE)
{
text_str = text_str.toLower();
}
else if (p.text_transform == CAPITALIZE)
{
text_str = text_str.toTitle(NULL);
}
if (text_str.length() > 0) {
output.push_back(p, text_str);
} else {
#ifdef MAPNIK_DEBUG
std::cerr << "Warning: Empty expression.\n";
#endif
}
}
void text_node::add_expressions(expression_set &output) const
{
if (text_) output.insert(text_);
}
void text_node::set_text(expression_ptr text)
{
text_ = text;
}
expression_ptr text_node::get_text() const
{
return text_;
}
} //ns formatting
} //ns mapnik

View File

@ -713,6 +713,7 @@ std::auto_ptr<text_path> placement_finder<DetectorT>::get_placement_offset(const
{
// grab the next character according to the orientation
char_info const &ci = orientation > 0 ? info_.at(i) : info_.at(info_.num_characters() - i - 1);
double cwidth = ci.width + ci.format->character_spacing;
unsigned c = ci.c;
double last_character_angle = angle;
@ -728,10 +729,10 @@ std::auto_ptr<text_path> placement_finder<DetectorT>::get_placement_offset(const
double end_x = 0;
double end_y = 0;
if (segment_length - distance >= ci.width)
if (segment_length - distance >= cwidth)
{
//if the distance remaining in this segment is enough, we just go further along the segment
distance += ci.width;
distance += cwidth;
end_x = old_x + dx*distance/segment_length;
end_y = old_y + dy*distance/segment_length;
@ -757,11 +758,11 @@ std::auto_ptr<text_path> placement_finder<DetectorT>::get_placement_offset(const
segment_length = path_distances[index];
}
while (std::sqrt(std::pow(start_x - new_x, 2) + std::pow(start_y - new_y, 2)) < ci.width); //Distance from start_ to new_
while (std::sqrt(std::pow(start_x - new_x, 2) + std::pow(start_y - new_y, 2)) < cwidth); //Distance from start_ to new_
//Calculate the position to place the end of the character on
find_line_circle_intersection(
start_x, start_y, ci.width,
start_x, start_y, cwidth,
old_x, old_y, new_x, new_y,
end_x, end_y); //results are stored in end_x, end_y
@ -803,8 +804,8 @@ std::auto_ptr<text_path> placement_finder<DetectorT>::get_placement_offset(const
if (orientation < 0)
{
// rotate in place
render_x += ci.width*cosa - (char_height-2)*sina;
render_y -= ci.width*sina + (char_height-2)*cosa;
render_x += cwidth*cosa - (char_height-2)*sina;
render_y -= cwidth*sina + (char_height-2)*cosa;
render_angle += M_PI;
}
current_placement->add_node(c,render_x - current_placement->starting_x,
@ -850,6 +851,7 @@ bool placement_finder<DetectorT>::test_placement(const std::auto_ptr<text_path>
{
// grab the next character according to the orientation
char_info const& ci = orientation > 0 ? info_.at(i) : info_.at(info_.num_characters() - i - 1);
double cwidth = ci.width + ci.format->character_spacing;
int c;
double x, y, angle;
char_properties *properties;
@ -863,8 +865,8 @@ bool placement_finder<DetectorT>::test_placement(const std::auto_ptr<text_path>
{
// rotate in place
/* TODO: What's the meaning of -2? */
x += ci.width*cosa - (string_height_-2)*sina;
y -= ci.width*sina + (string_height_-2)*cosa;
x += cwidth*cosa - (string_height_-2)*sina;
y -= cwidth*sina + (string_height_-2)*cosa;
angle += M_PI;
//sin(x+PI) = -sin(x)
sina = -sina;
@ -879,12 +881,12 @@ bool placement_finder<DetectorT>::test_placement(const std::auto_ptr<text_path>
else
{
// put four corners of the letter into envelope
e.init(x, y, x + ci.width*cosa,
y - ci.width*sina);
e.init(x, y, x + cwidth*cosa,
y - cwidth*sina);
e.expand_to_include(x - ci.height()*sina,
y - ci.height()*cosa);
e.expand_to_include(x + (ci.width*cosa - ci.height()*sina),
y - (ci.width*sina + ci.height()*cosa));
e.expand_to_include(x + (cwidth*cosa - ci.height()*sina),
y - (cwidth*sina + ci.height()*cosa));
}
if (!detector_.extent().intersects(e) ||

53
src/processed_text.cpp Normal file
View File

@ -0,0 +1,53 @@
#include <mapnik/processed_text.hpp>
namespace mapnik
{
void processed_text::push_back(char_properties const& properties, UnicodeString const& text)
{
expr_list_.push_back(processed_expression(properties, text));
}
processed_text::expression_list::const_iterator processed_text::begin() const
{
return expr_list_.begin();
}
processed_text::expression_list::const_iterator processed_text::end() const
{
return expr_list_.end();
}
processed_text::processed_text(face_manager<freetype_engine> & font_manager, double scale_factor)
: font_manager_(font_manager), scale_factor_(scale_factor)
{
}
void processed_text::clear()
{
info_.clear();
expr_list_.clear();
}
string_info &processed_text::get_string_info()
{
info_.clear(); //if this function is called twice invalid results are returned, so clear string_info first
expression_list::iterator itr = expr_list_.begin();
expression_list::iterator end = expr_list_.end();
for (; itr != end; ++itr)
{
char_properties const &p = itr->p;
face_set_ptr faces = font_manager_.get_face_set(p.face_name, p.fontset);
if (faces->size() <= 0)
{
throw config_error("Unable to find specified font face '" + p.face_name + "'");
}
faces->set_character_sizes(p.text_size * scale_factor_);
faces->get_string_info(info_, itr->str, &(itr->p));
info_.add_text(itr->str);
}
return info_;
}
} //ns mapnik

View File

@ -255,14 +255,14 @@ text_placement_info_ptr shield_symbolizer_helper<FaceManagerT, DetectorT>::get_p
// remove displacement from image label
double lx = x - pos.first;
double ly = y - pos.second;
marker_x_ = int(floor(lx - (0.5 * marker_w_))) + 1;
marker_y_ = int(floor(ly - (0.5 * marker_h_))) + 1;
marker_x_ = lx - 0.5 * marker_w_;
marker_y_ = ly - 0.5 * marker_h_;
marker_ext_.re_center(lx, ly);
}
else
{ // center image at reference location
marker_x_ = int(floor(label_x - 0.5 * marker_w_));
marker_y_ = int(floor(label_y - 0.5 * marker_h_));
marker_x_ = label_x - 0.5 * marker_w_;
marker_y_ = label_y - 0.5 * marker_h_;
marker_ext_.re_center(label_x, label_y);
}
@ -345,8 +345,8 @@ std::pair<int, int> shield_symbolizer_helper<FaceManagerT, DetectorT>::get_marke
double lx = x - pos.first;
double ly = y - pos.second;
int px = int(floor(lx - (0.5*marker_w_))) + 1;
int py = int(floor(ly - (0.5*marker_h_))) + 1;
int px = lx - 0.5*marker_w_;
int py = ly - 0.5*marker_h_;
marker_ext_.re_center(lx, ly);
// detector_->insert(label_ext); //TODO: Is this done by placement_finder?

View File

@ -0,0 +1,48 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2012 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
*
*****************************************************************************/
#include <mapnik/text_placements/base.hpp>
namespace mapnik {
text_placements::text_placements() : defaults()
{
}
void text_placements::add_expressions(expression_set &output)
{
defaults.add_expressions(output);
}
/************************************************************************/
text_placement_info::text_placement_info(text_placements const* parent,
double scale_factor_, dimension_type dim, bool has_dimensions_)
: properties(parent->defaults),
scale_factor(scale_factor_),
has_dimensions(has_dimensions_),
dimensions(dim),
collect_extents(false)
{
}
} //ns mapnik

View File

@ -0,0 +1,39 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2012 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
*
*****************************************************************************/
#include <mapnik/text_placements/dummy.hpp>
namespace mapnik
{
bool text_placement_info_dummy::next()
{
if (state) return false;
state++;
return true;
}
text_placement_info_ptr text_placements_dummy::get_placement_info(
double scale_factor, dimension_type dim, bool has_dimensions) const
{
return text_placement_info_ptr(new text_placement_info_dummy(
this, scale_factor, dim, has_dimensions));
}
} //ns mapnik

View File

@ -0,0 +1,85 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2012 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
*
*****************************************************************************/
#include <mapnik/text_placements/list.hpp>
namespace mapnik
{
bool text_placement_info_list::next()
{
if (state == 0) {
properties = parent_->defaults;
} else {
if (state-1 >= parent_->list_.size()) return false;
properties = parent_->list_[state-1];
}
state++;
return true;
}
text_symbolizer_properties & text_placements_list::add()
{
if (list_.size()) {
text_symbolizer_properties &last = list_.back();
list_.push_back(last); //Preinitialize with old values
} else {
list_.push_back(defaults);
}
return list_.back();
}
text_symbolizer_properties & text_placements_list::get(unsigned i)
{
return list_[i];
}
/***************************************************************************/
text_placement_info_ptr text_placements_list::get_placement_info(
double scale_factor, dimension_type dim, bool has_dimensions) const
{
return text_placement_info_ptr(new text_placement_info_list(this,
scale_factor, dim, has_dimensions));
}
text_placements_list::text_placements_list() : text_placements(), list_(0)
{
}
void text_placements_list::add_expressions(expression_set &output)
{
defaults.add_expressions(output);
std::vector<text_symbolizer_properties>::const_iterator it;
for (it=list_.begin(); it != list_.end(); it++)
{
it->add_expressions(output);
}
}
unsigned text_placements_list::size() const
{
return list_.size();
}
} //ns mapnik

View File

@ -0,0 +1,51 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2012 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
*
*****************************************************************************/
// mapnik
#include <mapnik/text_placements/registry.hpp>
namespace mapnik
{
namespace placements
{
registry::registry()
{
}
void registry::register_name(std::string name, from_xml_function_ptr ptr, bool overwrite)
{
if (overwrite) {
map_[name] = ptr;
} else {
map_.insert(make_pair(name, ptr));
}
}
text_placements_ptr registry::from_xml(std::string name, const boost::property_tree::ptree &xml)
{
std::map<std::string, from_xml_function_ptr>::const_iterator itr = map_.find(name);
if (itr == map_.end()) throw config_error("Unknown placement-type '" + name + "'");
return itr->second(xml);
}
} //ns formatting
} //ns mapnik

View File

@ -0,0 +1,168 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2012 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
*
*****************************************************************************/
// mapnik
#include <mapnik/text_placements/simple.hpp>
// boost
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
namespace mapnik
{
namespace qi = boost::spirit::qi;
namespace phoenix = boost::phoenix;
using phoenix::push_back;
using boost::spirit::ascii::space;
using phoenix::ref;
using qi::_1;
bool text_placement_info_simple::next()
{
while (1) {
if (state > 0)
{
if (state > parent_->text_sizes_.size()) return false;
properties.format.text_size = parent_->text_sizes_[state-1];
}
if (!next_position_only()) {
state++;
position_state = 0;
} else {
break;
}
}
return true;
}
bool text_placement_info_simple::next_position_only()
{
const position &pdisp = parent_->defaults.displacement;
position &displacement = properties.displacement;
if (position_state >= parent_->direction_.size()) return false;
directions_t dir = parent_->direction_[position_state];
switch (dir) {
case EXACT_POSITION:
displacement = pdisp;
break;
case NORTH:
displacement = std::make_pair(0, -abs(pdisp.second));
break;
case EAST:
displacement = std::make_pair(abs(pdisp.first), 0);
break;
case SOUTH:
displacement = std::make_pair(0, abs(pdisp.second));
break;
case WEST:
displacement = std::make_pair(-abs(pdisp.first), 0);
break;
case NORTHEAST:
displacement = std::make_pair(abs(pdisp.first), -abs(pdisp.second));
break;
case SOUTHEAST:
displacement = std::make_pair(abs(pdisp.first), abs(pdisp.second));
break;
case NORTHWEST:
displacement = std::make_pair(-abs(pdisp.first), -abs(pdisp.second));
break;
case SOUTHWEST:
displacement = std::make_pair(-abs(pdisp.first), abs(pdisp.second));
break;
default:
std::cerr << "WARNING: Unknown placement\n";
}
position_state++;
return true;
}
text_placement_info_ptr text_placements_simple::get_placement_info(
double scale_factor, dimension_type dim, bool has_dimensions) const
{
return text_placement_info_ptr(new text_placement_info_simple(this,
scale_factor, dim, has_dimensions));
}
/** Position string: [POS][SIZE]
* [POS] is any combination of
* N, E, S, W, NE, SE, NW, SW, X (exact position) (separated by commas)
* [SIZE] is a list of font sizes, separated by commas. The first font size
* is always the one given in the TextSymbolizer's parameters.
* First all directions are tried, then font size is reduced
* and all directions are tried again. The process ends when a placement is
* found or the last fontsize is tried without success.
* Example: N,S,15,10,8 (tries placement above, then below and if
* that fails it tries the additional font sizes 15, 10 and 8.
*/
void text_placements_simple::set_positions(std::string positions)
{
positions_ = positions;
struct direction_name_ : qi::symbols<char, directions_t>
{
direction_name_()
{
add
("N" , NORTH)
("E" , EAST)
("S" , SOUTH)
("W" , WEST)
("NE", NORTHEAST)
("SE", SOUTHEAST)
("NW", NORTHWEST)
("SW", SOUTHWEST)
("X" , EXACT_POSITION)
;
}
} direction_name;
std::string::iterator first = positions.begin(), last = positions.end();
qi::phrase_parse(first, last,
(direction_name[push_back(phoenix::ref(direction_), _1)] % ',') >> *(',' >> qi::float_[push_back(phoenix::ref(text_sizes_), _1)]),
space
);
if (first != last) {
std::cerr << "WARNING: Could not parse text_placement_simple placement string ('" << positions << "').\n";
}
if (direction_.size() == 0) {
std::cerr << "WARNING: text_placements_simple with no valid placments! ('"<< positions<<"')\n";
}
}
text_placements_simple::text_placements_simple()
{
set_positions("X");
}
text_placements_simple::text_placements_simple(std::string positions)
{
set_positions(positions);
}
std::string text_placements_simple::get_positions()
{
return positions_; //TODO: Build string from data in direction_ and text_sizes_
}
} //ns mapnik

View File

@ -1,343 +0,0 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2011 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
*
*****************************************************************************/
#include <mapnik/formatting/text.hpp>
#include <mapnik/formatting/list.hpp>
#include <mapnik/formatting/format.hpp>
#include <mapnik/formatting/expression.hpp>
#include <mapnik/processed_text.hpp>
#include <mapnik/color.hpp>
#include <mapnik/feature.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/expression_string.hpp>
#include <mapnik/ptree_helpers.hpp>
#include <boost/optional.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/shared_ptr.hpp>
#include <stack>
#include <vector>
namespace mapnik {
using boost::property_tree::ptree;
using boost::optional;
namespace formatting {
void node::to_xml(boost::property_tree::ptree &xml) const
{
//TODO: Should this throw a config_error?
#ifdef MAPNIK_DEBUG
std::cerr << "Error: Trying to write unsupported node type to XML.\n";
#endif
}
node_ptr node::from_xml(boost::property_tree::ptree const& xml)
{
list_node *list = new list_node();
node_ptr list_ptr(list);
ptree::const_iterator itr = xml.begin();
ptree::const_iterator end = xml.end();
for (; itr != end; ++itr) {
node_ptr n;
if (itr->first == "<xmltext>") {
n = text_node::from_xml(itr->second);
} else if (itr->first == "Format") {
n = format_node::from_xml(itr->second);
} else if (itr->first == "ExpressionFormat") {
n = expression_format::from_xml(itr->second);
} else if (itr->first != "<xmlcomment>" && itr->first != "<xmlattr>" && itr->first != "Placement") {
throw config_error("Unknown item " + itr->first);
}
if (n) list->push_back(n);
}
if (list->get_children().size() == 1) {
return list->get_children()[0];
} else if (list->get_children().size() > 1) {
return list_ptr;
} else {
return node_ptr();
}
}
void node::add_expressions(expression_set &output) const
{
//Do nothing by default
}
/************************************************************/
void list_node::to_xml(boost::property_tree::ptree &xml) const
{
std::vector<node_ptr>::const_iterator itr = children_.begin();
std::vector<node_ptr>::const_iterator end = children_.end();
for (;itr != end; itr++)
{
(*itr)->to_xml(xml);
}
}
void list_node::apply(char_properties const& p, Feature const& feature, processed_text &output) const
{
std::vector<node_ptr>::const_iterator itr = children_.begin();
std::vector<node_ptr>::const_iterator end = children_.end();
for (;itr != end; itr++)
{
(*itr)->apply(p, feature, output);
}
}
void list_node::add_expressions(expression_set &output) const
{
std::vector<node_ptr>::const_iterator itr = children_.begin();
std::vector<node_ptr>::const_iterator end = children_.end();
for (;itr != end; itr++)
{
(*itr)->add_expressions(output);
}
}
void list_node::push_back(node_ptr n)
{
children_.push_back(n);
}
void list_node::clear()
{
children_.clear();
}
void list_node::set_children(std::vector<node_ptr> const& children)
{
children_ = children;
}
std::vector<node_ptr> const& list_node::get_children() const
{
return children_;
}
/************************************************************/
void text_node::to_xml(ptree &xml) const
{
ptree &new_node = xml.push_back(ptree::value_type(
"<xmltext>", ptree()))->second;
new_node.put_value(to_expression_string(*text_));
}
node_ptr text_node::from_xml(boost::property_tree::ptree const& xml)
{
std::string data = xml.data();
boost::trim(data);
if (data.empty()) return node_ptr(); //No text
return node_ptr(new text_node(parse_expression(data, "utf8")));
}
void text_node::apply(char_properties const& p, Feature const& feature, processed_text &output) const
{
UnicodeString text_str = boost::apply_visitor(evaluate<Feature,value_type>(feature), *text_).to_unicode();
if (p.text_transform == UPPERCASE)
{
text_str = text_str.toUpper();
}
else if (p.text_transform == LOWERCASE)
{
text_str = text_str.toLower();
}
else if (p.text_transform == CAPITALIZE)
{
text_str = text_str.toTitle(NULL);
}
if (text_str.length() > 0) {
output.push_back(p, text_str);
} else {
#ifdef MAPNIK_DEBUG
std::cerr << "Warning: Empty expression.\n";
#endif
}
}
void text_node::add_expressions(expression_set &output) const
{
if (text_) output.insert(text_);
}
void text_node::set_text(expression_ptr text)
{
text_ = text;
}
expression_ptr text_node::get_text() const
{
return text_;
}
/************************************************************/
void format_node::to_xml(ptree &xml) const
{
ptree &new_node = xml.push_back(ptree::value_type("Format", ptree()))->second;
if (face_name) set_attr(new_node, "face-name", *face_name);
if (text_size) set_attr(new_node, "size", *text_size);
if (character_spacing) set_attr(new_node, "character-spacing", *character_spacing);
if (line_spacing) set_attr(new_node, "line-spacing", *line_spacing);
if (text_opacity) set_attr(new_node, "opacity", *text_opacity);
if (wrap_before) set_attr(new_node, "wrap-before", *wrap_before);
if (wrap_char) set_attr(new_node, "wrap-character", *wrap_char);
if (text_transform) set_attr(new_node, "text-transform", *text_transform);
if (fill) set_attr(new_node, "fill", *fill);
if (halo_fill) set_attr(new_node, "halo-fill", *halo_fill);
if (halo_radius) set_attr(new_node, "halo-radius", *halo_radius);
if (child_) child_->to_xml(new_node);
}
node_ptr format_node::from_xml(ptree const& xml)
{
format_node *n = new format_node();
node_ptr np(n);
node_ptr child = node::from_xml(xml);
n->set_child(child);
n->face_name = get_opt_attr<std::string>(xml, "face-name");
/*TODO: Fontset is problematic. We don't have the fontsets pointer here... */
n->text_size = get_opt_attr<unsigned>(xml, "size");
n->character_spacing = get_opt_attr<unsigned>(xml, "character-spacing");
n->line_spacing = get_opt_attr<unsigned>(xml, "line-spacing");
n->text_opacity = get_opt_attr<double>(xml, "opactity");
boost::optional<boolean> wrap = get_opt_attr<boolean>(xml, "wrap-before");
if (wrap) n->wrap_before = *wrap;
n->wrap_char = get_opt_attr<unsigned>(xml, "wrap-character");
n->text_transform = get_opt_attr<text_transform_e>(xml, "text-transform");
n->fill = get_opt_attr<color>(xml, "fill");
n->halo_fill = get_opt_attr<color>(xml, "halo-fill");
n->halo_radius = get_opt_attr<double>(xml, "halo-radius");
return np;
}
void format_node::apply(char_properties const& p, const Feature &feature, processed_text &output) const
{
char_properties new_properties = p;
if (face_name) new_properties.face_name = *face_name;
if (text_size) new_properties.text_size = *text_size;
if (character_spacing) new_properties.character_spacing = *character_spacing;
if (line_spacing) new_properties.line_spacing = *line_spacing;
if (text_opacity) new_properties.text_opacity = *text_opacity;
if (wrap_before) new_properties.wrap_before = *wrap_before;
if (wrap_char) new_properties.wrap_char = *wrap_char;
if (text_transform) new_properties.text_transform = *text_transform;
if (fill) new_properties.fill = *fill;
if (halo_fill) new_properties.halo_fill = *halo_fill;
if (halo_radius) new_properties.halo_radius = *halo_radius;
if (child_) {
child_->apply(new_properties, feature, output);
} else {
#ifdef MAPNIK_DEBUG
std::cerr << "Warning: Useless format: No text to format\n";
#endif
}
}
void format_node::set_child(node_ptr child)
{
child_ = child;
}
node_ptr format_node::get_child() const
{
return child_;
}
void format_node::add_expressions(expression_set &output) const
{
if (child_) child_->add_expressions(output);
}
} //namespace formatting
/************************************************************/
void processed_text::push_back(char_properties const& properties, UnicodeString const& text)
{
expr_list_.push_back(processed_expression(properties, text));
}
processed_text::expression_list::const_iterator processed_text::begin() const
{
return expr_list_.begin();
}
processed_text::expression_list::const_iterator processed_text::end() const
{
return expr_list_.end();
}
processed_text::processed_text(face_manager<freetype_engine> & font_manager, double scale_factor)
: font_manager_(font_manager), scale_factor_(scale_factor)
{
}
void processed_text::clear()
{
info_.clear();
expr_list_.clear();
}
string_info &processed_text::get_string_info()
{
info_.clear(); //if this function is called twice invalid results are returned, so clear string_info first
expression_list::iterator itr = expr_list_.begin();
expression_list::iterator end = expr_list_.end();
for (; itr != end; ++itr)
{
char_properties const &p = itr->p;
face_set_ptr faces = font_manager_.get_face_set(p.face_name, p.fontset);
if (faces->size() <= 0)
{
throw config_error("Unable to find specified font face '" + p.face_name + "'");
}
faces->set_character_sizes(p.text_size * scale_factor_);
faces->get_string_info(info_, itr->str, &(itr->p));
info_.add_text(itr->str);
}
return info_;
}
} /* namespace */

View File

@ -19,29 +19,15 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
#include <mapnik/text_placements/simple.hpp>
#include <mapnik/text_placements/list.hpp>
#include <mapnik/text_placements/dummy.hpp>
#include <mapnik/expression_string.hpp>
#include <mapnik/ptree_helpers.hpp>
#include <mapnik/formatting/text.hpp>
// mapnik
#include <mapnik/text_properties.hpp>
#include <mapnik/processed_text.hpp>
#include <mapnik/ptree_helpers.hpp>
#include <mapnik/expression_string.hpp>
#include <mapnik/formatting/text.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/optional.hpp>
namespace mapnik {
namespace qi = boost::spirit::qi;
namespace phoenix = boost::phoenix;
using boost::spirit::ascii::space;
using phoenix::push_back;
using phoenix::ref;
using qi::_1;
namespace mapnik
{
using boost::optional;
text_symbolizer_properties::text_symbolizer_properties() :
@ -350,233 +336,4 @@ void char_properties::to_xml(boost::property_tree::ptree &node, bool explicit_de
}
}
/************************************************************************/
text_placements::text_placements() : defaults()
{
}
void text_placements::add_expressions(expression_set &output)
{
defaults.add_expressions(output);
}
/************************************************************************/
text_placement_info::text_placement_info(text_placements const* parent,
double scale_factor_, dimension_type dim, bool has_dimensions_)
: properties(parent->defaults),
scale_factor(scale_factor_),
has_dimensions(has_dimensions_),
dimensions(dim),
collect_extents(false)
{
}
bool text_placement_info_dummy::next()
{
if (state) return false;
state++;
return true;
}
text_placement_info_ptr text_placements_dummy::get_placement_info(
double scale_factor, dimension_type dim, bool has_dimensions) const
{
return text_placement_info_ptr(new text_placement_info_dummy(
this, scale_factor, dim, has_dimensions));
}
/************************************************************************/
bool text_placement_info_simple::next()
{
while (1) {
if (state > 0)
{
if (state > parent_->text_sizes_.size()) return false;
properties.format.text_size = parent_->text_sizes_[state-1];
}
if (!next_position_only()) {
state++;
position_state = 0;
} else {
break;
}
}
return true;
}
bool text_placement_info_simple::next_position_only()
{
const position &pdisp = parent_->defaults.displacement;
position &displacement = properties.displacement;
if (position_state >= parent_->direction_.size()) return false;
directions_t dir = parent_->direction_[position_state];
switch (dir) {
case EXACT_POSITION:
displacement = pdisp;
break;
case NORTH:
displacement = std::make_pair(0, -abs(pdisp.second));
break;
case EAST:
displacement = std::make_pair(abs(pdisp.first), 0);
break;
case SOUTH:
displacement = std::make_pair(0, abs(pdisp.second));
break;
case WEST:
displacement = std::make_pair(-abs(pdisp.first), 0);
break;
case NORTHEAST:
displacement = std::make_pair(abs(pdisp.first), -abs(pdisp.second));
break;
case SOUTHEAST:
displacement = std::make_pair(abs(pdisp.first), abs(pdisp.second));
break;
case NORTHWEST:
displacement = std::make_pair(-abs(pdisp.first), -abs(pdisp.second));
break;
case SOUTHWEST:
displacement = std::make_pair(-abs(pdisp.first), abs(pdisp.second));
break;
default:
std::cerr << "WARNING: Unknown placement\n";
}
position_state++;
return true;
}
text_placement_info_ptr text_placements_simple::get_placement_info(
double scale_factor, dimension_type dim, bool has_dimensions) const
{
return text_placement_info_ptr(new text_placement_info_simple(this,
scale_factor, dim, has_dimensions));
}
/** Position string: [POS][SIZE]
* [POS] is any combination of
* N, E, S, W, NE, SE, NW, SW, X (exact position) (separated by commas)
* [SIZE] is a list of font sizes, separated by commas. The first font size
* is always the one given in the TextSymbolizer's parameters.
* First all directions are tried, then font size is reduced
* and all directions are tried again. The process ends when a placement is
* found or the last fontsize is tried without success.
* Example: N,S,15,10,8 (tries placement above, then below and if
* that fails it tries the additional font sizes 15, 10 and 8.
*/
void text_placements_simple::set_positions(std::string positions)
{
positions_ = positions;
struct direction_name_ : qi::symbols<char, directions_t>
{
direction_name_()
{
add
("N" , NORTH)
("E" , EAST)
("S" , SOUTH)
("W" , WEST)
("NE", NORTHEAST)
("SE", SOUTHEAST)
("NW", NORTHWEST)
("SW", SOUTHWEST)
("X" , EXACT_POSITION)
;
}
} direction_name;
std::string::iterator first = positions.begin(), last = positions.end();
qi::phrase_parse(first, last,
(direction_name[push_back(phoenix::ref(direction_), _1)] % ',') >> *(',' >> qi::float_[push_back(phoenix::ref(text_sizes_), _1)]),
space
);
if (first != last) {
std::cerr << "WARNING: Could not parse text_placement_simple placement string ('" << positions << "').\n";
}
if (direction_.size() == 0) {
std::cerr << "WARNING: text_placements_simple with no valid placments! ('"<< positions<<"')\n";
}
}
text_placements_simple::text_placements_simple()
{
set_positions("X");
}
text_placements_simple::text_placements_simple(std::string positions)
{
set_positions(positions);
}
std::string text_placements_simple::get_positions()
{
return positions_; //TODO: Build string from data in direction_ and text_sizes_
}
/***************************************************************************/
bool text_placement_info_list::next()
{
if (state == 0) {
properties = parent_->defaults;
} else {
if (state-1 >= parent_->list_.size()) return false;
properties = parent_->list_[state-1];
}
state++;
return true;
}
text_symbolizer_properties & text_placements_list::add()
{
if (list_.size()) {
text_symbolizer_properties &last = list_.back();
list_.push_back(last); //Preinitialize with old values
} else {
list_.push_back(defaults);
}
return list_.back();
}
text_symbolizer_properties & text_placements_list::get(unsigned i)
{
return list_[i];
}
/***************************************************************************/
text_placement_info_ptr text_placements_list::get_placement_info(
double scale_factor, dimension_type dim, bool has_dimensions) const
{
return text_placement_info_ptr(new text_placement_info_list(this,
scale_factor, dim, has_dimensions));
}
text_placements_list::text_placements_list() : text_placements(), list_(0)
{
}
void text_placements_list::add_expressions(expression_set &output)
{
defaults.add_expressions(output);
std::vector<text_symbolizer_properties>::const_iterator it;
for (it=list_.begin(); it != list_.end(); it++)
{
it->add_expressions(output);
}
}
unsigned text_placements_list::size() const
{
return list_.size();
}
} //namespace
} //ns mapnik