Merge branch 'master' of https://github.com/mapnik/mapnik into statistics

This commit is contained in:
Tom MacWright 2012-02-01 13:58:22 -05:00
commit 55d5a8ae82
6 changed files with 139 additions and 86 deletions

View file

@ -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);
}

View file

@ -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?

View file

@ -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:

View file

@ -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_;

View file

@ -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) ||

View file

@ -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]