Merge branch 'master' of https://github.com/mapnik/mapnik into statistics
This commit is contained in:
commit
55d5a8ae82
6 changed files with 139 additions and 86 deletions
|
@ -44,8 +44,17 @@ tuple get_displacement(text_symbolizer_properties const& t)
|
|||
|
||||
void set_displacement(text_symbolizer_properties &t, boost::python::tuple arg)
|
||||
{
|
||||
if (len(arg) != 2)
|
||||
{
|
||||
PyErr_SetObject(PyExc_ValueError,
|
||||
("expected 2-item tuple in call to set_displacement; got %s"
|
||||
% arg).ptr()
|
||||
);
|
||||
throw_error_already_set();
|
||||
}
|
||||
|
||||
double x = extract<double>(arg[0]);
|
||||
double y = extract<double>(arg[0]);
|
||||
double y = extract<double>(arg[1]);
|
||||
t.displacement = std::make_pair(x, y);
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
#include <mapnik/grid/grid_util.hpp>
|
||||
#include <mapnik/grid/grid_view.hpp>
|
||||
#include <mapnik/value_error.hpp>
|
||||
#include <mapnik/feature.hpp>
|
||||
#include <mapnik/feature_kv_iterator.hpp>
|
||||
#include "mapnik_value_converter.hpp"
|
||||
|
||||
|
||||
|
@ -213,42 +215,54 @@ static void write_features(T const& grid_type,
|
|||
bool include_key = (attributes.find(key) != attributes.end());
|
||||
for (; feat_itr != feat_end; ++feat_itr)
|
||||
{
|
||||
std::map<std::string,mapnik::value> const& props = feat_itr->second;
|
||||
std::map<std::string,mapnik::value>::const_iterator const& itr = props.find(key);
|
||||
if (itr != props.end())
|
||||
mapnik::Feature const* feature = feat_itr->second;
|
||||
boost::optional<std::string> join_value;
|
||||
if (key == grid_type.key_name())
|
||||
{
|
||||
typename T::lookup_type const& join_value = itr->second.to_string();
|
||||
std::stringstream s;
|
||||
s << feature->id();
|
||||
join_value = s.str();
|
||||
}
|
||||
else if (feature->has_key(key))
|
||||
{
|
||||
join_value = feature->get(key).to_string();
|
||||
}
|
||||
|
||||
if (join_value)
|
||||
{
|
||||
// only serialize features visible in the grid
|
||||
if(std::find(key_order.begin(), key_order.end(), join_value) != key_order.end()) {
|
||||
if(std::find(key_order.begin(), key_order.end(), *join_value) != key_order.end()) {
|
||||
boost::python::dict feat;
|
||||
std::map<std::string,mapnik::value>::const_iterator it = props.begin();
|
||||
std::map<std::string,mapnik::value>::const_iterator end = props.end();
|
||||
bool found = false;
|
||||
for (; it != end; ++it)
|
||||
if (key == grid_type.key_name())
|
||||
{
|
||||
std::string const& key_name = it->first;
|
||||
// drop key unless requested
|
||||
if (include_key) {
|
||||
found = true;
|
||||
//TODO - add __id__ as data key?
|
||||
//feat[key] = *join_value;
|
||||
}
|
||||
}
|
||||
|
||||
feature_kv_iterator itr = feature->begin();
|
||||
feature_kv_iterator end = feature->end();
|
||||
for ( ;itr!=end; ++itr)
|
||||
{
|
||||
std::string const& key_name = boost::get<0>(*itr);
|
||||
if (key_name == key) {
|
||||
// drop key unless requested
|
||||
if (include_key) {
|
||||
found = true;
|
||||
feat[it->first] = boost::python::object(
|
||||
boost::python::handle<>(
|
||||
boost::apply_visitor(
|
||||
boost::python::value_converter(),
|
||||
it->second.base())));
|
||||
feat[key_name] = boost::get<1>(*itr);
|
||||
}
|
||||
}
|
||||
else if ( (attributes.find(key_name) != attributes.end()) )
|
||||
{
|
||||
found = true;
|
||||
feat[it->first] = boost::python::object(
|
||||
boost::python::handle<>(
|
||||
boost::apply_visitor(
|
||||
boost::python::value_converter(),
|
||||
it->second.base())));
|
||||
feat[key_name] = boost::get<1>(*itr);
|
||||
}
|
||||
}
|
||||
|
||||
if (found)
|
||||
{
|
||||
feature_data[feat_itr->first] = feat;
|
||||
|
@ -358,7 +372,7 @@ static void render_layer_for_grid(const mapnik::Map& map,
|
|||
std::string const& key = grid.get_key();
|
||||
|
||||
// if key is special __id__ keyword
|
||||
if (key == grid.id_name_)
|
||||
if (key == grid.key_name())
|
||||
{
|
||||
// TODO - should feature.id() be a first class attribute?
|
||||
|
||||
|
@ -425,7 +439,7 @@ static boost::python::dict render_grid(const mapnik::Map& map,
|
|||
std::set<std::string> attributes = grid.property_names();
|
||||
|
||||
// if key is special __id__ keyword
|
||||
if (key == grid.id_name_)
|
||||
if (key == grid.key_name())
|
||||
{
|
||||
// TODO - should feature.id() be a first class attribute?
|
||||
|
||||
|
|
|
@ -55,9 +55,7 @@ public:
|
|||
// mapping between pixel id and key
|
||||
typedef std::map<value_type, lookup_type> feature_key_type;
|
||||
typedef std::map<lookup_type, value_type> key_type;
|
||||
typedef std::map<std::string, mapnik::value> feature_properties_type;
|
||||
// note: feature_type is not the same as a mapnik::Feature as it lacks a geometry
|
||||
typedef std::map<std::string, feature_properties_type > feature_type;
|
||||
typedef std::map<std::string, const mapnik::Feature * > feature_type;
|
||||
|
||||
private:
|
||||
unsigned width_;
|
||||
|
@ -68,11 +66,11 @@ private:
|
|||
data_type data_;
|
||||
std::set<std::string> names_;
|
||||
unsigned int resolution_;
|
||||
std::string id_name_;
|
||||
bool painted_;
|
||||
|
||||
public:
|
||||
|
||||
std::string id_name_;
|
||||
|
||||
hit_grid(int width, int height, std::string const& key, unsigned int resolution)
|
||||
:width_(width),
|
||||
|
@ -80,7 +78,9 @@ public:
|
|||
key_(key),
|
||||
data_(width,height),
|
||||
resolution_(resolution),
|
||||
id_name_("__id__") {
|
||||
id_name_("__id__"),
|
||||
painted_(false)
|
||||
{
|
||||
// this only works if each datasource's
|
||||
// feature count starts at 1
|
||||
f_keys_[0] = "";
|
||||
|
@ -92,7 +92,9 @@ public:
|
|||
key_(rhs.key_),
|
||||
data_(rhs.data_),
|
||||
resolution_(rhs.resolution_),
|
||||
id_name_("__id__") {
|
||||
id_name_("__id__"),
|
||||
painted_(rhs.painted_)
|
||||
{
|
||||
f_keys_[0] = "";
|
||||
}
|
||||
|
||||
|
@ -108,30 +110,28 @@ public:
|
|||
return painted_;
|
||||
}
|
||||
|
||||
inline std::string const& key_name() const
|
||||
{
|
||||
return id_name_;
|
||||
}
|
||||
|
||||
inline void add_feature(mapnik::Feature const& feature)
|
||||
{
|
||||
|
||||
// copies feature props
|
||||
// FIXME
|
||||
/*
|
||||
std::map<std::string,value> fprops = feature.props();
|
||||
lookup_type lookup_value;
|
||||
// NOTE: currently lookup keys must be strings,
|
||||
// but this should be revisited
|
||||
boost::optional<lookup_type> lookup_value;
|
||||
if (key_ == id_name_)
|
||||
{
|
||||
// TODO - this will break if lookup_type is not a string
|
||||
std::stringstream s;
|
||||
s << feature.id();
|
||||
lookup_value = s.str();
|
||||
// add this as a proper feature so filtering works later on
|
||||
fprops[id_name_] = feature.id();
|
||||
//fprops[id_name_] = tr_->transcode(lookup_value));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::map<std::string,value>::const_iterator const& itr = fprops.find(key_);
|
||||
if (itr != fprops.end())
|
||||
if (feature.has_key(key_))
|
||||
{
|
||||
lookup_value = itr->second.to_string();
|
||||
lookup_value = feature.get(key_).to_string();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -139,23 +139,21 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
// what good is an empty lookup key?
|
||||
if (!lookup_value.empty())
|
||||
if (lookup_value)
|
||||
{
|
||||
// TODO - consider shortcutting f_keys if feature_id == lookup_value
|
||||
// create a mapping between the pixel id and the feature key
|
||||
f_keys_.insert(std::make_pair(feature.id(),lookup_value));
|
||||
f_keys_.insert(std::make_pair(feature.id(),*lookup_value));
|
||||
// if extra fields have been supplied, push them into grid memory
|
||||
if (!names_.empty()) {
|
||||
// TODO - add ability to push WKT/WKB of geometry into grid storage
|
||||
features_.insert(std::make_pair(lookup_value,fprops));
|
||||
if (!names_.empty())
|
||||
{
|
||||
features_.insert(std::make_pair(*lookup_value,const_cast<mapnik::Feature*>(&feature)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::clog << "### Warning: key '" << key_ << "' was blank for " << feature << "\n";
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
inline void add_property_name(std::string const& name)
|
||||
|
@ -198,18 +196,6 @@ public:
|
|||
key_ = key;
|
||||
}
|
||||
|
||||
// compatibility
|
||||
inline const std::string& get_join_field() const
|
||||
{
|
||||
return key_;
|
||||
}
|
||||
|
||||
// compatibility
|
||||
inline unsigned int get_step() const
|
||||
{
|
||||
return resolution_;
|
||||
}
|
||||
|
||||
inline unsigned int get_resolution() const
|
||||
{
|
||||
return resolution_;
|
||||
|
@ -248,9 +234,8 @@ public:
|
|||
inline mapnik::grid_view get_view(unsigned x, unsigned y, unsigned w, unsigned h)
|
||||
{
|
||||
return mapnik::grid_view(x,y,w,h,
|
||||
data_,key_,resolution_,names_,f_keys_,features_);
|
||||
data_,key_,id_name_,resolution_,names_,f_keys_,features_);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <mapnik/box2d.hpp>
|
||||
#include <mapnik/global.hpp>
|
||||
#include <mapnik/value.hpp>
|
||||
#include <mapnik/feature.hpp>
|
||||
|
||||
// boost
|
||||
#include <boost/cstdint.hpp>
|
||||
|
@ -49,16 +50,17 @@ class hit_grid_view
|
|||
public:
|
||||
typedef T data_type;
|
||||
typedef typename T::pixel_type value_type;
|
||||
typedef typename T::pixel_type pixel_type;
|
||||
typedef std::string lookup_type;
|
||||
typedef std::map<value_type, lookup_type> feature_key_type;
|
||||
typedef std::map<lookup_type, value_type> key_type;
|
||||
typedef std::map<std::string, mapnik::value> feature_properties_type;
|
||||
typedef std::map<std::string, feature_properties_type > feature_type;
|
||||
typedef std::map<std::string, const mapnik::Feature * > feature_type;
|
||||
|
||||
hit_grid_view(unsigned x, unsigned y,
|
||||
unsigned width, unsigned height,
|
||||
T const& data,
|
||||
std::string const& key,
|
||||
std::string const& id_name,
|
||||
unsigned resolution,
|
||||
std::set<std::string> const& names,
|
||||
feature_key_type const& f_keys,
|
||||
|
@ -71,6 +73,7 @@ public:
|
|||
data_(data),
|
||||
key_(key),
|
||||
resolution_(resolution),
|
||||
id_name_(id_name),
|
||||
names_(names),
|
||||
f_keys_(f_keys),
|
||||
features_(features)
|
||||
|
@ -92,6 +95,7 @@ public:
|
|||
data_(rhs.data_),
|
||||
key_(rhs.key_),
|
||||
resolution_(rhs.resolution_),
|
||||
id_name_(rhs.id_name_),
|
||||
names_(rhs.names_),
|
||||
f_keys_(rhs.f_keys_),
|
||||
features_(rhs.features_)
|
||||
|
@ -107,6 +111,7 @@ public:
|
|||
data_ = rhs.data_;
|
||||
key_ = rhs.key_;
|
||||
resolution_ = rhs.resolution_;
|
||||
id_name_ = rhs.id_name_;
|
||||
names_ = rhs.names_;
|
||||
f_keys_ = rhs.f_keys_;
|
||||
features_ = rhs.features_;
|
||||
|
@ -131,6 +136,11 @@ public:
|
|||
{
|
||||
return height_;
|
||||
}
|
||||
|
||||
inline std::string const& key_name() const
|
||||
{
|
||||
return id_name_;
|
||||
}
|
||||
|
||||
inline const value_type* getRow(unsigned row) const
|
||||
{
|
||||
|
@ -185,6 +195,7 @@ private:
|
|||
T const& data_;
|
||||
std::string const& key_;
|
||||
unsigned int resolution_;
|
||||
std::string const& id_name_;
|
||||
std::set<std::string> const& names_;
|
||||
feature_key_type const& f_keys_;
|
||||
feature_type const& features_;
|
||||
|
|
|
@ -787,22 +787,22 @@ std::auto_ptr<text_path> placement_finder<DetectorT>::get_placement_offset(const
|
|||
}
|
||||
|
||||
double render_angle = angle;
|
||||
//double cosa = fast_cos(angle); TODO: do we need them?
|
||||
//double sina = fast_sin(angle);
|
||||
double cosa = fast_cos(angle);
|
||||
double sina = fast_sin(angle);
|
||||
|
||||
double render_x = start_x;
|
||||
double render_y = start_y;
|
||||
|
||||
//Center the text on the line
|
||||
double char_height = ci.avg_height;
|
||||
render_x -= char_height/2.0*cos(render_angle+M_PI/2);
|
||||
render_y += char_height/2.0*sin(render_angle+M_PI/2);
|
||||
render_x += char_height/2.0*sina;
|
||||
render_y += char_height/2.0*cosa;
|
||||
|
||||
if (orientation < 0)
|
||||
{
|
||||
// rotate in place
|
||||
render_x += ci.width*cos(render_angle) - (char_height-2)*sin(render_angle);
|
||||
render_y -= ci.width*sin(render_angle) + (char_height-2)*cos(render_angle);
|
||||
render_x += ci.width*cosa - (char_height-2)*sina;
|
||||
render_y -= ci.width*sina + (char_height-2)*cosa;
|
||||
render_angle += M_PI;
|
||||
}
|
||||
current_placement->add_node(c,render_x - current_placement->starting_x,
|
||||
|
@ -854,13 +854,19 @@ bool placement_finder<DetectorT>::test_placement(const std::auto_ptr<text_path>
|
|||
current_placement->vertex(&c, &x, &y, &angle, &properties);
|
||||
x = current_placement->starting_x + x;
|
||||
y = current_placement->starting_y - y;
|
||||
|
||||
double sina = fast_sin(angle);
|
||||
double cosa = fast_cos(angle);
|
||||
if (orientation < 0)
|
||||
{
|
||||
// rotate in place
|
||||
/* TODO: What's the meaning of -2? */
|
||||
x += ci.width*cos(angle) - (string_height_-2)*sin(angle);
|
||||
y -= ci.width*sin(angle) + (string_height_-2)*cos(angle);
|
||||
x += ci.width*cosa - (string_height_-2)*sina;
|
||||
y -= ci.width*sina + (string_height_-2)*cosa;
|
||||
angle += M_PI;
|
||||
//sin(x+PI) = -sin(x)
|
||||
sina = -sina;
|
||||
cosa = -cosa;
|
||||
}
|
||||
|
||||
box2d<double> e;
|
||||
|
@ -871,12 +877,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*cos(angle),
|
||||
y - ci.width*sin(angle));
|
||||
e.expand_to_include(x - ci.height()*sin(angle),
|
||||
y - ci.height()*cos(angle));
|
||||
e.expand_to_include(x + (ci.width*cos(angle) - ci.height()*sin(angle)),
|
||||
y - (ci.width*sin(angle) + ci.height()*cos(angle)));
|
||||
e.init(x, y, x + ci.width*cosa,
|
||||
y - ci.width*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));
|
||||
}
|
||||
|
||||
if (!detector_.extent().intersects(e) ||
|
||||
|
|
|
@ -61,18 +61,9 @@ def create_grid_map(width,height):
|
|||
ds.add_feature(f)
|
||||
s = mapnik.Style()
|
||||
r = mapnik.Rule()
|
||||
#symb = mapnik.PointSymbolizer()
|
||||
symb = mapnik.MarkersSymbolizer()
|
||||
symb.allow_overlap = True
|
||||
r.symbols.append(symb)
|
||||
label = mapnik.TextSymbolizer(mapnik.Expression('[Name]'),
|
||||
'DejaVu Sans Book',
|
||||
10,
|
||||
mapnik.Color('black')
|
||||
)
|
||||
label.allow_overlap = True
|
||||
label.displacement = (0,-10)
|
||||
#r.symbols.append(label)
|
||||
|
||||
s.rules.append(r)
|
||||
lyr = mapnik.Layer('Places')
|
||||
|
@ -160,6 +151,43 @@ def test_render_grid2():
|
|||
eq_(resolve(utf5,25,46),{"Name": "North East"})
|
||||
eq_(resolve(utf5,38,10),{"Name": "South West"})
|
||||
eq_(resolve(utf5,38,46),{"Name": "South East"})
|
||||
|
||||
|
||||
grid_feat_id = {'keys': ['', '3', '4', '2', '1'], 'data': {'1': {'Name': 'South East'}, '3': {'Name': u'North West'}, '2': {'Name': 'South West'}, '4': {'Name': 'North East'}}, 'grid': [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' !! ## ', ' !!! ### ', ' !! ## ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' $$$ %% ', ' $$$ %%% ', ' $$ %% ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']}
|
||||
|
||||
def test_render_grid3():
|
||||
""" test using feature id"""
|
||||
width,height = 256,256
|
||||
m = create_grid_map(width,height)
|
||||
ul_lonlat = mapnik.Coord(142.30,-38.20)
|
||||
lr_lonlat = mapnik.Coord(143.40,-38.80)
|
||||
m.zoom_to_box(mapnik.Box2d(ul_lonlat,lr_lonlat))
|
||||
|
||||
grid = mapnik.Grid(m.width,m.height,key='__id__')
|
||||
mapnik.render_layer(m,grid,layer=0,fields=['__id__','Name'])
|
||||
utf1 = grid.encode('utf',resolution=4)
|
||||
eq_(utf1['keys'],grid_feat_id['keys'])
|
||||
eq_(utf1['grid'],grid_feat_id['grid'])
|
||||
eq_(utf1['data'],grid_feat_id['data'])
|
||||
eq_(utf1,grid_feat_id)
|
||||
# check a full view is the same as a full image
|
||||
grid_view = grid.view(0,0,width,height)
|
||||
# for kicks check at full res too
|
||||
utf3 = grid.encode('utf',resolution=1)
|
||||
utf4 = grid_view.encode('utf',resolution=1)
|
||||
eq_(utf3['grid'],utf4['grid'])
|
||||
eq_(utf3['keys'],utf4['keys'])
|
||||
eq_(utf3['data'],utf4['data'])
|
||||
|
||||
eq_(resolve(utf4,0,0),None)
|
||||
|
||||
# resolve some center points in the
|
||||
# resampled view
|
||||
utf5 = grid_view.encode('utf',resolution=4)
|
||||
eq_(resolve(utf5,25,10),{"Name": "North West"})
|
||||
eq_(resolve(utf5,25,46),{"Name": "North East"})
|
||||
eq_(resolve(utf5,38,10),{"Name": "South West"})
|
||||
eq_(resolve(utf5,38,46),{"Name": "South East"})
|
||||
|
||||
if __name__ == "__main__":
|
||||
[eval(run)() for run in dir() if 'test_' in run]
|
||||
|
|
Loading…
Add table
Reference in a new issue