mirror of
https://github.com/mapnik/mapnik.git
synced 2025-12-08 20:13:09 +00:00
Merge branch 'master' into python-textplacement
This commit is contained in:
commit
3508ec5fb4
@ -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>
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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();
|
||||
|
||||
57
include/mapnik/formatting/registry.hpp
Normal file
57
include/mapnik/formatting/registry.hpp
Normal 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
|
||||
@ -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;
|
||||
|
||||
@ -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_;
|
||||
|
||||
57
include/mapnik/text_placements/registry.hpp
Normal file
57
include/mapnik/text_placements/registry.hpp
Normal 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
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
14
src/build.py
14
src/build.py
@ -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
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
@ -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
70
src/formatting/base.cpp
Normal 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
114
src/formatting/format.cpp
Normal 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
85
src/formatting/list.cpp
Normal 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
|
||||
|
||||
56
src/formatting/registry.cpp
Normal file
56
src/formatting/registry.cpp
Normal 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
99
src/formatting/text.cpp
Normal 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
|
||||
@ -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
53
src/processed_text.cpp
Normal 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
|
||||
@ -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?
|
||||
|
||||
|
||||
48
src/text_placements/base.cpp
Normal file
48
src/text_placements/base.cpp
Normal 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
|
||||
39
src/text_placements/dummy.cpp
Normal file
39
src/text_placements/dummy.cpp
Normal 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
|
||||
85
src/text_placements/list.cpp
Normal file
85
src/text_placements/list.cpp
Normal 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
|
||||
|
||||
51
src/text_placements/registry.cpp
Normal file
51
src/text_placements/registry.cpp
Normal 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
|
||||
168
src/text_placements/simple.cpp
Normal file
168
src/text_placements/simple.cpp
Normal 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
|
||||
@ -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 */
|
||||
@ -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
|
||||
Loading…
x
Reference in New Issue
Block a user