Merge branch 'master' into harfbuzz

Conflicts:
	src/build.py
This commit is contained in:
Hermann Kraus 2012-08-03 19:27:09 +02:00
commit ef0aae3733
59 changed files with 1568 additions and 852 deletions

7
.gitignore vendored
View file

@ -43,4 +43,11 @@ demo/c++/demo.tif
demo/c++/demo.jpg demo/c++/demo.jpg
demo/c++/demo.png demo/c++/demo.png
demo/c++/demo256.png demo/c++/demo256.png
demo/viewer/Makefile
demo/viewer/Makefile.Debug
demo/viewer/Makefile.Release
demo/viewer/release/
demo/viewer/ui_about.h
demo/viewer/ui_info.h
demo/viewer/ui_layer_info.h
tests/cpp_tests/*-bin tests/cpp_tests/*-bin

View file

@ -11,6 +11,10 @@ For a complete change history, see the git log.
Not yet released Not yet released
- Added support for overriding fill, stroke, and opacity for svg markers using marker properties
- Added support for setting opacity dynamically on images in polygon pattern and markers symbolizers
- Added support for filtering on a features geometry type, either `point`, `linestring`, 'polygon`, - Added support for filtering on a features geometry type, either `point`, `linestring`, 'polygon`,
or `collection` using the expression keyword of `[mapnik::geometry_type]` (#546) or `collection` using the expression keyword of `[mapnik::geometry_type]` (#546)

View file

@ -91,6 +91,18 @@ struct layer_pickle_suite : boost::python::pickle_suite
std::vector<std::string> & (mapnik::layer::*_styles_)() = &mapnik::layer::styles; std::vector<std::string> & (mapnik::layer::*_styles_)() = &mapnik::layer::styles;
void set_maximum_extent(mapnik::layer & l, boost::optional<mapnik::box2d<double> > const& box)
{
if (box)
{
l.set_maximum_extent(*box);
}
else
{
l.reset_maximum_extent();
}
}
void export_layer() void export_layer()
{ {
using namespace boost::python; using namespace boost::python;
@ -196,6 +208,27 @@ void export_layer()
"<mapnik.Datasource object at 0x65470>\n" "<mapnik.Datasource object at 0x65470>\n"
) )
.add_property("buffer_size",
&layer::buffer_size,
&layer::set_buffer_size,
"Get/Set the size of buffer around layer in pixels.\n"
"\n"
"Usage:\n"
">>> l.buffer_size\n"
"0 # zero by default\n"
">>> l.buffer_size = 2\n"
">>> l.buffer_size\n"
"2\n"
)
.add_property("maximum_extent",make_function
(&layer::maximum_extent,return_value_policy<copy_const_reference>()),
&set_maximum_extent,
"The maximum extent of the map.\n"
"\n"
"Usage:\n"
">>> m.maximum_extent = Box2d(-180,-90,180,90)\n"
)
.add_property("maxzoom", .add_property("maxzoom",
&layer::max_zoom, &layer::max_zoom,
&layer::set_max_zoom, &layer::set_max_zoom,

View file

@ -57,7 +57,7 @@ void export_line_symbolizer()
"Set/get the rasterization method of the line of the point") "Set/get the rasterization method of the line of the point")
.add_property("stroke",make_function .add_property("stroke",make_function
(&line_symbolizer::get_stroke, (&line_symbolizer::get_stroke,
return_value_policy<copy_const_reference>()), return_value_policy<reference_existing_object>()),
&line_symbolizer::set_stroke) &line_symbolizer::set_stroke)
.add_property("smooth", .add_property("smooth",
&line_symbolizer::smooth, &line_symbolizer::smooth,

View file

@ -117,7 +117,7 @@ struct map_pickle_suite : boost::python::pickle_suite
std::vector<layer>& (Map::*layers_nonconst)() = &Map::layers; std::vector<layer>& (Map::*layers_nonconst)() = &Map::layers;
std::vector<layer> const& (Map::*layers_const)() const = &Map::layers; std::vector<layer> const& (Map::*layers_const)() const = &Map::layers;
mapnik::parameters& (Map::*params_nonconst)() = &Map::get_extra_parameters; mapnik::parameters& (Map::*params_nonconst)() = &Map::get_extra_parameters;
boost::optional<mapnik::box2d<double> > const& (Map::*maximum_extent_const)() const = &Map::maximum_extent; //boost::optional<mapnik::box2d<double> > const& (Map::*maximum_extent_const)() const = &Map::maximum_extent;
mapnik::feature_type_style find_style(mapnik::Map const& m, std::string const& name) mapnik::feature_type_style find_style(mapnik::Map const& m, std::string const& name)
{ {
@ -192,7 +192,6 @@ mapnik::Map map_deepcopy(mapnik::Map & m, boost::python::dict memo)
return result; return result;
} }
// TODO - find a simplier way to set optional to uninitialized
void set_maximum_extent(mapnik::Map & m, boost::optional<mapnik::box2d<double> > const& box) void set_maximum_extent(mapnik::Map & m, boost::optional<mapnik::box2d<double> > const& box)
{ {
if (box) if (box)
@ -201,7 +200,7 @@ void set_maximum_extent(mapnik::Map & m, boost::optional<mapnik::box2d<double> >
} }
else else
{ {
m.maximum_extent().reset(); m.reset_maximum_extent();
} }
} }
@ -562,7 +561,7 @@ void export_map()
) )
.add_property("maximum_extent",make_function .add_property("maximum_extent",make_function
(maximum_extent_const,return_value_policy<copy_const_reference>()), (&Map::maximum_extent,return_value_policy<copy_const_reference>()),
&set_maximum_extent, &set_maximum_extent,
"The maximum extent of the map.\n" "The maximum extent of the map.\n"
"\n" "\n"

View file

@ -86,13 +86,13 @@ struct markers_symbolizer_pickle_suite : boost::python::pickle_suite
}; };
void export_markers_symbolizer() void export_markers_symbolizer()
{ {
using namespace boost::python; using namespace boost::python;
mapnik::enumeration_<mapnik::marker_placement_e>("marker_placement") mapnik::enumeration_<mapnik::marker_placement_e>("marker_placement")
.value("POINT_PLACEMENT",mapnik::MARKER_POINT_PLACEMENT) .value("POINT_PLACEMENT",mapnik::MARKER_POINT_PLACEMENT)
.value("INTERIOR_PLACEMENT",mapnik::MARKER_INTERIOR_PLACEMENT)
.value("LINE_PLACEMENT",mapnik::MARKER_LINE_PLACEMENT) .value("LINE_PLACEMENT",mapnik::MARKER_LINE_PLACEMENT)
; ;
@ -115,7 +115,11 @@ void export_markers_symbolizer()
.add_property("opacity", .add_property("opacity",
&markers_symbolizer::get_opacity, &markers_symbolizer::get_opacity,
&markers_symbolizer::set_opacity, &markers_symbolizer::set_opacity,
"Set/get the text opacity") "Set/get the overall opacity")
.add_property("fill_opacity",
&markers_symbolizer::get_fill_opacity,
&markers_symbolizer::set_fill_opacity,
"Set/get the fill opacity")
.add_property("ignore_placement", .add_property("ignore_placement",
&markers_symbolizer::get_ignore_placement, &markers_symbolizer::get_ignore_placement,
&markers_symbolizer::set_ignore_placement) &markers_symbolizer::set_ignore_placement)

View file

@ -608,12 +608,14 @@ BOOST_PYTHON_MODULE(_mapnik)
def("has_cairo", &has_cairo, "Get cairo library status"); def("has_cairo", &has_cairo, "Get cairo library status");
def("has_pycairo", &has_pycairo, "Get pycairo module status"); def("has_pycairo", &has_pycairo, "Get pycairo module status");
python_optional<mapnik::stroke>();
python_optional<mapnik::color>(); python_optional<mapnik::color>();
python_optional<mapnik::box2d<double> >(); python_optional<mapnik::box2d<double> >();
python_optional<mapnik::datasource::geometry_t>(); python_optional<mapnik::datasource::geometry_t>();
python_optional<std::string>(); python_optional<std::string>();
python_optional<unsigned>(); python_optional<unsigned>();
python_optional<double>(); python_optional<double>();
python_optional<float>();
python_optional<bool>(); python_optional<bool>();
python_optional<mapnik::text_transform_e>(); python_optional<mapnik::text_transform_e>();
register_ptr_to_python<mapnik::expression_ptr>(); register_ptr_to_python<mapnik::expression_ptr>();

View file

@ -36,52 +36,59 @@ int main( int argc, char **argv )
using mapnik::datasource_cache; using mapnik::datasource_cache;
using mapnik::freetype_engine; using mapnik::freetype_engine;
QCoreApplication::setOrganizationName("Mapnik"); try
QCoreApplication::setOrganizationDomain("mapnik.org");
QCoreApplication::setApplicationName("Viewer");
QSettings settings("viewer.ini",QSettings::IniFormat);
// register input plug-ins
QString plugins_dir = settings.value("mapnik/plugins_dir",
QVariant("/usr/local/lib/mapnik/input/")).toString();
datasource_cache::instance()->register_datasources(plugins_dir.toStdString());
// register fonts
int count = settings.beginReadArray("mapnik/fonts");
for (int index=0; index < count; ++index)
{ {
settings.setArrayIndex(index); QCoreApplication::setOrganizationName("Mapnik");
QString font_dir = settings.value("dir").toString(); QCoreApplication::setOrganizationDomain("mapnik.org");
freetype_engine::register_fonts(font_dir.toStdString()); QCoreApplication::setApplicationName("Viewer");
} QSettings settings("viewer.ini",QSettings::IniFormat);
settings.endArray();
QApplication app( argc, argv ); // register input plug-ins
MainWindow window; QString plugins_dir = settings.value("mapnik/plugins_dir",
window.show(); QVariant("/usr/local/lib/mapnik/input/")).toString();
if (argc > 1) window.open(argv[1]); datasource_cache::instance()->register_datasources(plugins_dir.toStdString());
if (argc >= 3) // register fonts
{ int count = settings.beginReadArray("mapnik/fonts");
QStringList list = QString(argv[2]).split(","); for (int index=0; index < count; ++index)
if (list.size()==4) {
settings.setArrayIndex(index);
QString font_dir = settings.value("dir").toString();
freetype_engine::register_fonts(font_dir.toStdString());
}
settings.endArray();
QApplication app( argc, argv );
MainWindow window;
window.show();
if (argc > 1) window.open(argv[1]);
if (argc >= 3)
{
QStringList list = QString(argv[2]).split(",");
if (list.size()==4)
{
bool ok;
double x0 = list[0].toDouble(&ok);
double y0 = list[1].toDouble(&ok);
double x1 = list[2].toDouble(&ok);
double y1 = list[3].toDouble(&ok);
if (ok) window.set_default_extent(x0,y0,x1,y1);
}
}
else
{
window.zoom_all();
}
if (argc == 4)
{ {
bool ok; bool ok;
double x0 = list[0].toDouble(&ok); double scaling_factor = QString(argv[3]).toDouble(&ok);
double y0 = list[1].toDouble(&ok); if (ok) window.set_scaling_factor(scaling_factor);
double x1 = list[2].toDouble(&ok);
double y1 = list[3].toDouble(&ok);
if (ok) window.set_default_extent(x0,y0,x1,y1);
} }
return app.exec();
} }
else catch (std::exception const& ex)
{ {
window.zoom_all(); std::cerr << "Could not start viewer: '" << ex.what() << "'\n";
return 1;
} }
if (argc == 4)
{
bool ok;
double scaling_factor = QString(argv[3]).toDouble(&ok);
if (ok) window.set_scaling_factor(scaling_factor);
}
return app.exec();
} }

View file

@ -1015,18 +1015,34 @@ namespace agg
// Dca' = Sa.(Sca.Da + Dca.Sa - Sa.Da)/Sca + Sca.(1 - Da) + Dca.(1 - Sa) // Dca' = Sa.(Sca.Da + Dca.Sa - Sa.Da)/Sca + Sca.(1 - Da) + Dca.(1 - Sa)
// //
// Da' = Sa + Da - Sa.Da // Da' = Sa + Da - Sa.Da
// http://www.w3.org/TR/SVGCompositing/
// if Sca == 0 and Dca == Da
// Dca' = Sa × Da + Sca × (1 - Da) + Dca × (1 - Sa)
// = Sa × Da + Dca × (1 - Sa)
// = Da = Dca
// otherwise if Sca == 0
// Dca' = Sca × (1 - Da) + Dca × (1 - Sa)
// = Dca × (1 - Sa)
// otherwise if Sca > 0
// Dca' = Sa × Da - Sa × Da × min(1, (1 - Dca/Da) × Sa/Sca) + Sca × (1 - Da) + Dca × (1 - Sa)
// = Sa × Da × (1 - min(1, (1 - Dca/Da) × Sa/Sca)) + Sca × (1 - Da) + Dca × (1 - Sa)
// sa * da * (255 - std::min(255, (255 - p[0]/da)*(sa/(sc*sa)) +
static AGG_INLINE void blend_pix(value_type* p, static AGG_INLINE void blend_pix(value_type* p,
unsigned sr, unsigned sg, unsigned sb, unsigned sr, unsigned sg, unsigned sb,
unsigned sa, unsigned cover) unsigned sa, unsigned cover)
{ {
if(cover < 255) if (cover < 255)
{ {
sr = (sr * cover + 255) >> 8; sr = (sr * cover + 255) >> 8;
sg = (sg * cover + 255) >> 8; sg = (sg * cover + 255) >> 8;
sb = (sb * cover + 255) >> 8; sb = (sb * cover + 255) >> 8;
sa = (sa * cover + 255) >> 8; sa = (sa * cover + 255) >> 8;
} }
if(sa)
if (sa)
{ {
calc_type d1a = base_mask - p[Order::A]; calc_type d1a = base_mask - p[Order::A];
calc_type s1a = base_mask - sa; calc_type s1a = base_mask - sa;
@ -1042,15 +1058,15 @@ namespace agg
long_type sbda = sb * da; long_type sbda = sb * da;
long_type sada = sa * da; long_type sada = sa * da;
p[Order::R] = (value_type)(((srda + drsa <= sada) ? if ( sr > 0) p[Order::R] = (value_type)(((srda + drsa <= sada) ?
sr * d1a + dr * s1a : sr * d1a + dr * s1a :
sa * (srda + drsa - sada) / sr + sr * d1a + dr * s1a + base_mask) >> base_shift); sa * (srda + drsa - sada) / sr + sr * d1a + dr * s1a + base_mask) >> base_shift);
p[Order::G] = (value_type)(((sgda + dgsa <= sada) ? if ( sg > 0 ) p[Order::G] = (value_type)(((sgda + dgsa <= sada) ?
sg * d1a + dg * s1a : sg * d1a + dg * s1a :
sa * (sgda + dgsa - sada) / sg + sg * d1a + dg * s1a + base_mask) >> base_shift); sa * (sgda + dgsa - sada) / sg + sg * d1a + dg * s1a + base_mask) >> base_shift);
p[Order::B] = (value_type)(((sbda + dbsa <= sada) ? if ( sb > 0) p[Order::B] = (value_type)(((sbda + dbsa <= sada) ?
sb * d1a + db * s1a : sb * d1a + db * s1a :
sa * (sbda + dbsa - sada) / sb + sb * d1a + db * s1a + base_mask) >> base_shift); sa * (sbda + dbsa - sada) / sb + sb * d1a + db * s1a + base_mask) >> base_shift);
@ -1502,24 +1518,29 @@ namespace agg
unsigned sr, unsigned sg, unsigned sb, unsigned sr, unsigned sg, unsigned sb,
unsigned sa, unsigned cover) unsigned sa, unsigned cover)
{ {
if (cover < 255) calc_type da = (p[Order::A] * sa + 255) >> 8;
{
sr = (sr * cover + 255) >> 8;
sg = (sg * cover + 255) >> 8;
sb = (sb * cover + 255) >> 8;
sa = (sa * cover + 255) >> 8;
}
if (sa > 0)
{
calc_type da = p[Order::A];
int dr = p[Order::R] - sr + 128;
int dg = p[Order::G] - sg + 128;
int db = p[Order::B] - sb + 128;
p[Order::R] = dr < 0 ? 0 : (dr > 255 ? 255 : dr); int dr = p[Order::R] - sr + 128;
p[Order::G] = dg < 0 ? 0 : (dg > 255 ? 255 : dg); int dg = p[Order::G] - sg + 128;
p[Order::B] = db < 0 ? 0 : (db > 255 ? 255 : db); int db = p[Order::B] - sb + 128;
p[Order::A] = (value_type)(sa + da - ((sa * da + base_mask) >> base_shift));
dr = dr < 0 ? 0 : (dr > 255 ? 255 : dr);
dg = dg < 0 ? 0 : (dg > 255 ? 255 : dg);
db = db < 0 ? 0 : (db > 255 ? 255 : db);
p[Order::A] = da;
if (da < 255)
{
p[Order::R] = (dr * da + 255) >> 8;
p[Order::G] = (dg * da + 255) >> 8;
p[Order::B] = (db * da + 255) >> 8;
}
else
{
p[Order::R] = dr;
p[Order::G] = dg;
p[Order::B] = db;
} }
} }
}; };

View file

@ -127,6 +127,7 @@ protected:
double x, double y, double angle = 0.0); double x, double y, double angle = 0.0);
void debug_draw_box(box2d<double> const& extent, void debug_draw_box(box2d<double> const& extent,
double x, double y, double angle = 0.0); double x, double y, double angle = 0.0);
void draw_geo_extent(box2d<double> const& extent,mapnik::color const& color);
private: private:
buffer_type & pixmap_; buffer_type & pixmap_;

View file

@ -249,7 +249,7 @@ public:
struct directive_collector : public boost::static_visitor<> struct directive_collector : public boost::static_visitor<>
{ {
directive_collector(double * filter_factor) directive_collector(double & filter_factor)
: filter_factor_(filter_factor) {} : filter_factor_(filter_factor) {}
template <typename T> template <typename T>
@ -257,10 +257,10 @@ struct directive_collector : public boost::static_visitor<>
void operator () (raster_symbolizer const& sym) void operator () (raster_symbolizer const& sym)
{ {
*filter_factor_ = sym.calculate_filter_factor(); filter_factor_ = sym.calculate_filter_factor();
} }
private: private:
double * filter_factor_; double & filter_factor_;
}; };
} // namespace mapnik } // namespace mapnik

View file

@ -110,7 +110,7 @@ operator << (std::basic_ostream<charT,traits>& out,
std::basic_ostringstream<charT,traits> s; std::basic_ostringstream<charT,traits> s;
s.copyfmt(out); s.copyfmt(out);
s.width(0); s.width(0);
s << "box2d(" << std::setprecision(16) s << "box2d(" << std::fixed << std::setprecision(16)
<< e.minx() << ',' << e.miny() << ',' << e.minx() << ',' << e.miny() << ','
<< e.maxx() << ',' << e.maxy() << ')'; << e.maxx() << ',' << e.maxy() << ')';
out << s.str(); out << s.str();

View file

@ -52,34 +52,25 @@ struct MAPNIK_DECL coord_transform
: t_(0), : t_(0),
geom_(geom), geom_(geom),
prj_trans_(0) {} prj_trans_(0) {}
void set_proj_trans(proj_transform const& prj_trans) void set_proj_trans(proj_transform const& prj_trans)
{ {
prj_trans_ = &prj_trans; prj_trans_ = &prj_trans;
} }
void set_trans(Transform const& t) void set_trans(Transform const& t)
{ {
t_ = &t; t_ = &t;
} }
unsigned vertex(double *x, double *y) const unsigned vertex(double *x, double *y) const
{ {
unsigned command = SEG_MOVETO; unsigned command = geom_.vertex(x, y);
bool ok = false; if ( command != SEG_END)
bool skipped_points = false;
double z = 0;
while (!ok && command != SEG_END)
{ {
command = geom_.vertex(x, y); double z = 0;
ok = prj_trans_->backward(*x, *y, z); if (!prj_trans_->backward(*x, *y, z))
if (!ok) { return SEG_END;
skipped_points = true;
}
}
if (skipped_points && (command == SEG_LINETO))
{
command = SEG_MOVETO;
} }
t_->forward(x, y); t_->forward(x, y);
return command; return command;

View file

@ -241,6 +241,8 @@ double path_length(PathType & path)
return length; return length;
} }
namespace label {
template <typename PathType> template <typename PathType>
bool middle_point(PathType & path, double & x, double & y) bool middle_point(PathType & path, double & x, double & y)
{ {
@ -271,8 +273,6 @@ bool middle_point(PathType & path, double & x, double & y)
return true; return true;
} }
namespace label {
template <typename PathType> template <typename PathType>
bool centroid(PathType & path, double & x, double & y) bool centroid(PathType & path, double & x, double & y)
{ {

View file

@ -118,9 +118,10 @@ private:
CoordTransform t_; CoordTransform t_;
freetype_engine font_engine_; freetype_engine font_engine_;
face_manager<freetype_engine> font_manager_; face_manager<freetype_engine> font_manager_;
label_collision_detector4 detector_; boost::shared_ptr<label_collision_detector4> detector_;
boost::scoped_ptr<grid_rasterizer> ras_ptr; boost::scoped_ptr<grid_rasterizer> ras_ptr;
box2d<double> query_extent_; box2d<double> query_extent_;
void setup(Map const& m);
}; };
} }

View file

@ -43,7 +43,9 @@ namespace mapnik
class MAPNIK_DECL layer class MAPNIK_DECL layer
{ {
public: public:
explicit layer(std::string const& name, std::string const& srs="+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"); layer(std::string const& name,
std::string const& srs="+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs");
layer(layer const& l); layer(layer const& l);
layer& operator=(layer const& l); layer& operator=(layer const& l);
bool operator==(layer const& other) const; bool operator==(layer const& other) const;
@ -57,7 +59,7 @@ public:
* @return the name of the layer. * @return the name of the layer.
*/ */
const std::string& name() const; std::string const& name() const;
/*! /*!
* @brief Set the SRS of the layer. * @brief Set the SRS of the layer.
@ -188,6 +190,11 @@ public:
*/ */
box2d<double> envelope() const; box2d<double> envelope() const;
void set_maximum_extent(box2d<double> const& box);
boost::optional<box2d<double> > const& maximum_extent() const;
void reset_maximum_extent();
void set_buffer_size(int size);
int buffer_size() const;
~layer(); ~layer();
private: private:
void swap(const layer& other); void swap(const layer& other);
@ -204,6 +211,8 @@ private:
std::string group_by_; std::string group_by_;
std::vector<std::string> styles_; std::vector<std::string> styles_;
datasource_ptr ds_; datasource_ptr ds_;
int buffer_size_;
boost::optional<box2d<double> > maximum_extent_;
}; };
} }

View file

@ -332,15 +332,13 @@ public:
/*! \brief Set the map maximum extent. /*! \brief Set the map maximum extent.
* @param box The bounding box for the maximum extent. * @param box The bounding box for the maximum extent.
*/ */
void set_maximum_extent(box2d<double>const& box); void set_maximum_extent(box2d<double> const& box);
/*! \brief Get the map maximum extent as box2d<double> /*! \brief Get the map maximum extent as box2d<double>
*/ */
boost::optional<box2d<double> > const& maximum_extent() const; boost::optional<box2d<double> > const& maximum_extent() const;
/*! \brief Get the non-const map maximum extent as box2d<double> void reset_maximum_extent();
*/
boost::optional<box2d<double> > & maximum_extent();
/*! \brief Get the map base path where paths should be relative to. /*! \brief Get the map base path where paths should be relative to.
*/ */

View file

@ -49,13 +49,13 @@ namespace mapnik
typedef agg::pod_bvector<mapnik::svg::path_attributes> attr_storage; typedef agg::pod_bvector<mapnik::svg::path_attributes> attr_storage;
typedef mapnik::svg::svg_storage<mapnik::svg::svg_path_storage,attr_storage> svg_storage_type; typedef mapnik::svg::svg_storage<mapnik::svg::svg_path_storage,attr_storage> svg_storage_type;
typedef boost::shared_ptr<svg_storage_type> path_ptr; typedef boost::shared_ptr<svg_storage_type> svg_path_ptr;
typedef boost::shared_ptr<image_data_32> image_ptr; typedef boost::shared_ptr<image_data_32> image_ptr;
/** /**
* A class to hold either vector or bitmap marker data. This allows these to be treated equally * A class to hold either vector or bitmap marker data. This allows these to be treated equally
* in the image caches and most of the render paths. * in the image caches and most of the render paths.
*/ */
class marker class marker: private boost::noncopyable
{ {
public: public:
marker() marker()
@ -65,20 +65,21 @@ public:
(*bitmap_data_)->set(0xff000000); (*bitmap_data_)->set(0xff000000);
} }
marker(const boost::optional<mapnik::image_ptr> &data) marker(boost::optional<mapnik::image_ptr> const& data)
: bitmap_data_(data) : bitmap_data_(data)
{ {
} }
marker(const boost::optional<mapnik::path_ptr> &data) marker(boost::optional<mapnik::svg_path_ptr> const& data)
: vector_data_(data) : vector_data_(data)
{ {
} }
marker(const marker& rhs) marker(marker const& rhs)
: bitmap_data_(rhs.bitmap_data_), vector_data_(rhs.vector_data_) : bitmap_data_(rhs.bitmap_data_),
vector_data_(rhs.vector_data_)
{} {}
box2d<double> bounding_box() const box2d<double> bounding_box() const
@ -128,17 +129,14 @@ public:
return bitmap_data_; return bitmap_data_;
} }
boost::optional<mapnik::path_ptr> get_vector_data() const boost::optional<mapnik::svg_path_ptr> get_vector_data() const
{ {
return vector_data_; return vector_data_;
} }
private: private:
marker& operator=(const marker&);
boost::optional<mapnik::image_ptr> bitmap_data_; boost::optional<mapnik::image_ptr> bitmap_data_;
boost::optional<mapnik::path_ptr> vector_data_; boost::optional<mapnik::svg_path_ptr> vector_data_;
}; };

View file

@ -27,39 +27,359 @@
#include <mapnik/markers_symbolizer.hpp> #include <mapnik/markers_symbolizer.hpp>
#include <mapnik/expression_evaluator.hpp> #include <mapnik/expression_evaluator.hpp>
#include <mapnik/svg/svg_path_attributes.hpp> #include <mapnik/svg/svg_path_attributes.hpp>
#include <mapnik/svg/svg_converter.hpp>
#include <mapnik/marker.hpp> // for svg_storage_type
#include <mapnik/svg/svg_storage.hpp>
#include <mapnik/markers_placement.hpp>
// agg
#include "agg_ellipse.h"
#include "agg_basics.h"
#include "agg_renderer_base.h"
#include "agg_renderer_scanline.h"
#include "agg_rendering_buffer.h"
#include "agg_scanline_u.h"
#include "agg_image_filters.h"
#include "agg_trans_bilinear.h"
#include "agg_span_allocator.h"
#include "agg_image_accessors.h"
#include "agg_pixfmt_rgba.h"
#include "agg_span_image_filter_rgba.h"
// boost // boost
#include <boost/optional.hpp> #include <boost/optional.hpp>
namespace mapnik { namespace mapnik {
template <typename BufferType, typename SvgRenderer, typename Rasterizer, typename Detector>
struct vector_markers_rasterizer_dispatch
{
typedef typename SvgRenderer::renderer_base renderer_base;
typedef typename renderer_base::pixfmt_type pixfmt_type;
vector_markers_rasterizer_dispatch(BufferType & image_buffer,
SvgRenderer & svg_renderer,
Rasterizer & ras,
box2d<double> const& bbox,
agg::trans_affine const& marker_trans,
markers_symbolizer const& sym,
Detector & detector,
double scale_factor)
: buf_(image_buffer.raw_data(), image_buffer.width(), image_buffer.height(), image_buffer.width() * 4),
pixf_(buf_),
renb_(pixf_),
svg_renderer_(svg_renderer),
ras_(ras),
bbox_(bbox),
marker_trans_(marker_trans),
sym_(sym),
detector_(detector),
scale_factor_(scale_factor)
{
pixf_.comp_op(static_cast<agg::comp_op_e>(sym_.comp_op()));
}
template <typename T>
void add_path(T & path)
{
marker_placement_e placement_method = sym_.get_marker_placement();
if (placement_method != MARKER_LINE_PLACEMENT)
{
double x,y;
path.rewind(0);
if (placement_method == MARKER_INTERIOR_PLACEMENT)
{
label::interior_position(path, x, y);
}
else
{
label::centroid(path, x, y);
}
agg::trans_affine matrix = marker_trans_;
matrix.translate(x,y);
box2d<double> transformed_bbox = bbox_ * matrix;
if (sym_.get_allow_overlap() ||
detector_.has_placement(transformed_bbox))
{
svg_renderer_.render(ras_, sl_, renb_, matrix, 1, bbox_);
if (!sym_.get_ignore_placement())
detector_.insert(transformed_bbox);
}
}
else
{
markers_placement<T, label_collision_detector4> placement(path, bbox_, marker_trans_, detector_,
sym_.get_spacing() * scale_factor_,
sym_.get_max_error(),
sym_.get_allow_overlap());
double x, y, angle;
while (placement.get_point(x, y, angle))
{
agg::trans_affine matrix = marker_trans_;
matrix.rotate(angle);
matrix.translate(x, y);
svg_renderer_.render(ras_, sl_, renb_, matrix, 1, bbox_);
}
}
}
private:
agg::scanline_u8 sl_;
agg::rendering_buffer buf_;
pixfmt_type pixf_;
renderer_base renb_;
SvgRenderer & svg_renderer_;
Rasterizer & ras_;
box2d<double> const& bbox_;
agg::trans_affine const& marker_trans_;
markers_symbolizer const& sym_;
Detector & detector_;
double scale_factor_;
};
template <typename Rasterizer, typename RendererBuffer>
void render_raster_marker(Rasterizer & ras, RendererBuffer & renb,
agg::scanline_u8 & sl, image_data_32 const& src,
agg::trans_affine const& marker_tr, double opacity)
{
double width = src.width();
double height = src.height();
double p[8];
p[0] = 0; p[1] = 0;
p[2] = width; p[3] = 0;
p[4] = width; p[5] = height;
p[6] = 0; p[7] = height;
marker_tr.transform(&p[0], &p[1]);
marker_tr.transform(&p[2], &p[3]);
marker_tr.transform(&p[4], &p[5]);
marker_tr.transform(&p[6], &p[7]);
ras.move_to_d(p[0],p[1]);
ras.line_to_d(p[2],p[3]);
ras.line_to_d(p[4],p[5]);
ras.line_to_d(p[6],p[7]);
typedef agg::rgba8 color_type;
agg::span_allocator<color_type> sa;
agg::image_filter_bilinear filter_kernel;
agg::image_filter_lut filter(filter_kernel, false);
agg::rendering_buffer marker_buf((unsigned char *)src.getBytes(),
src.width(),
src.height(),
src.width()*4);
agg::pixfmt_rgba32_pre pixf(marker_buf);
typedef agg::image_accessor_clone<agg::pixfmt_rgba32_pre> img_accessor_type;
typedef agg::span_interpolator_linear<agg::trans_affine> interpolator_type;
typedef agg::span_image_filter_rgba_2x2<img_accessor_type,
interpolator_type> span_gen_type;
typedef agg::order_rgba order_type;
typedef agg::pixel32_type pixel_type;
typedef agg::comp_op_adaptor_rgba_pre<color_type, order_type> blender_type; // comp blender
typedef agg::pixfmt_custom_blend_rgba<blender_type, agg::rendering_buffer> pixfmt_comp_type;
typedef agg::renderer_base<pixfmt_comp_type> renderer_base;
typedef agg::renderer_scanline_aa_alpha<renderer_base,
agg::span_allocator<agg::rgba8>,
span_gen_type> renderer_type;
img_accessor_type ia(pixf);
interpolator_type interpolator(agg::trans_affine(p, 0, 0, width, height) );
span_gen_type sg(ia, interpolator, filter);
renderer_type rp(renb,sa, sg, unsigned(opacity*255));
agg::render_scanlines(ras, sl, rp);
}
template <typename BufferType, typename Rasterizer, typename Detector>
struct raster_markers_rasterizer_dispatch
{
typedef agg::rgba8 color_type;
typedef agg::order_rgba order_type;
typedef agg::pixel32_type pixel_type;
typedef agg::comp_op_adaptor_rgba_pre<color_type, order_type> blender_type; // comp blender
typedef agg::pixfmt_custom_blend_rgba<blender_type, agg::rendering_buffer> pixfmt_comp_type;
typedef agg::renderer_base<pixfmt_comp_type> renderer_base;
raster_markers_rasterizer_dispatch(BufferType & image_buffer,
Rasterizer & ras,
image_data_32 const& src,
agg::trans_affine const& marker_trans,
markers_symbolizer const& sym,
Detector & detector,
double scale_factor)
: buf_(image_buffer.raw_data(), image_buffer.width(), image_buffer.height(), image_buffer.width() * 4),
pixf_(buf_),
renb_(pixf_),
ras_(ras),
src_(src),
marker_trans_(marker_trans),
sym_(sym),
detector_(detector),
scale_factor_(scale_factor)
{
pixf_.comp_op(static_cast<agg::comp_op_e>(sym_.comp_op()));
}
template <typename T>
void add_path(T & path)
{
marker_placement_e placement_method = sym_.get_marker_placement();
box2d<double> bbox_(0,0, src_.width(),src_.height());
if (placement_method != MARKER_LINE_PLACEMENT)
{
double x,y;
path.rewind(0);
if (placement_method == MARKER_INTERIOR_PLACEMENT)
{
label::interior_position(path, x, y);
}
else
{
label::centroid(path, x, y);
}
agg::trans_affine matrix = marker_trans_;
matrix.translate(x,y);
box2d<double> transformed_bbox = bbox_ * matrix;
if (sym_.get_allow_overlap() ||
detector_.has_placement(transformed_bbox))
{
float opacity = sym_.get_opacity() ? *sym_.get_opacity() : 1;
render_raster_marker(ras_, renb_, sl_, src_,
matrix, opacity);
if (!sym_.get_ignore_placement())
detector_.insert(transformed_bbox);
}
}
else
{
markers_placement<T, label_collision_detector4> placement(path, bbox_, marker_trans_, detector_,
sym_.get_spacing() * scale_factor_,
sym_.get_max_error(),
sym_.get_allow_overlap());
double x, y, angle;
while (placement.get_point(x, y, angle))
{
agg::trans_affine matrix = marker_trans_;
matrix.rotate(angle);
matrix.translate(x,y);
float opacity = sym_.get_opacity() ? *sym_.get_opacity() : 1;
render_raster_marker(ras_, renb_, sl_, src_,
matrix, opacity);
}
}
}
private:
agg::scanline_u8 sl_;
agg::rendering_buffer buf_;
pixfmt_comp_type pixf_;
renderer_base renb_;
Rasterizer & ras_;
image_data_32 const& src_;
agg::trans_affine const& marker_trans_;
markers_symbolizer const& sym_;
Detector & detector_;
double scale_factor_;
};
template <typename T>
void build_ellipse(T const& sym, mapnik::feature_impl const& feature, svg_storage_type & marker_ellipse, svg::svg_path_adapter & svg_path)
{
expression_ptr const& width_expr = sym.get_width();
expression_ptr const& height_expr = sym.get_height();
double width = 0;
double height = 0;
if (width_expr && height_expr)
{
width = boost::apply_visitor(evaluate<Feature,value_type>(feature), *width_expr).to_double();
height = boost::apply_visitor(evaluate<Feature,value_type>(feature), *height_expr).to_double();
}
else if (width_expr)
{
width = boost::apply_visitor(evaluate<Feature,value_type>(feature), *width_expr).to_double();
height = width;
}
else if (height_expr)
{
height = boost::apply_visitor(evaluate<Feature,value_type>(feature), *height_expr).to_double();
width = height;
}
svg::svg_converter_type styled_svg(svg_path, marker_ellipse.attributes());
styled_svg.push_attr();
styled_svg.begin_path();
agg::ellipse c(0, 0, width/2.0, height/2.0);
styled_svg.storage().concat_path(c);
styled_svg.end_path();
styled_svg.pop_attr();
double lox,loy,hix,hiy;
styled_svg.bounding_rect(&lox, &loy, &hix, &hiy);
marker_ellipse.set_bounding_box(lox,loy,hix,hiy);
}
template <typename Attr> template <typename Attr>
bool push_explicit_style(Attr const& src, Attr & dst, markers_symbolizer const& sym) bool push_explicit_style(Attr const& src, Attr & dst, markers_symbolizer const& sym)
{ {
boost::optional<stroke> const& strk = sym.get_stroke(); boost::optional<stroke> const& strk = sym.get_stroke();
boost::optional<color> const& fill = sym.get_fill(); boost::optional<color> const& fill = sym.get_fill();
if (strk || fill) boost::optional<float> const& opacity = sym.get_opacity();
boost::optional<float> const& fill_opacity = sym.get_fill_opacity();
if (strk || fill || opacity || fill_opacity)
{ {
bool success = false;
for(unsigned i = 0; i < src.size(); ++i) for(unsigned i = 0; i < src.size(); ++i)
{ {
mapnik::svg::path_attributes attr = src[i]; success = true;
dst.push_back(src[i]);
if (strk && attr.stroke_flag) mapnik::svg::path_attributes & attr = dst.last();
if (attr.stroke_flag)
{ {
attr.stroke_width = strk->get_width(); // TODO - stroke attributes need to be boost::optional
color const& s_color = strk->get_color(); // for this to work properly
attr.stroke_color = agg::rgba(s_color.red()/255.0,s_color.green()/255.0, if (strk)
s_color.blue()/255.0,(s_color.alpha()*strk->get_opacity())/255.0); {
attr.stroke_width = strk->get_width();
color const& s_color = strk->get_color();
attr.stroke_color = agg::rgba(s_color.red()/255.0,
s_color.green()/255.0,
s_color.blue()/255.0,
s_color.alpha()/255.0);
}
if (opacity)
{
attr.stroke_opacity = *opacity;
}
else if (strk)
{
attr.stroke_opacity = strk->get_opacity();
}
} }
if (fill && attr.fill_flag) if (attr.fill_flag)
{ {
color const& f_color = *fill; if (fill)
attr.fill_color = agg::rgba(f_color.red()/255.0,f_color.green()/255.0, {
f_color.blue()/255.0,(f_color.alpha()*sym.get_opacity())/255.0); color const& f_color = *fill;
attr.fill_color = agg::rgba(f_color.red()/255.0,
f_color.green()/255.0,
f_color.blue()/255.0,
f_color.alpha()/255.0);
}
if (opacity)
{
attr.fill_opacity = *opacity;
}
else if (fill_opacity)
{
attr.fill_opacity = *fill_opacity;
}
} }
dst.push_back(attr);
} }
return true; return success;
} }
return false; return false;
} }

View file

@ -24,13 +24,26 @@
#define MAPNIK_MARKERS_PLACEMENT_HPP #define MAPNIK_MARKERS_PLACEMENT_HPP
// mapnik // mapnik
#include <mapnik/markers_placement.hpp>
#include <mapnik/geometry.hpp>
#include <mapnik/ctrans.hpp>
#include <mapnik/label_collision_detector.hpp>
#include <mapnik/global.hpp> //round
#include <mapnik/box2d.hpp> #include <mapnik/box2d.hpp>
// boost // boost
#include <boost/utility.hpp> #include <boost/utility.hpp>
// agg // agg
#include "agg_basics.h"
#include "agg_conv_clip_polygon.h"
#include "agg_conv_clip_polyline.h"
#include "agg_trans_affine.h"
#include "agg_conv_transform.h" #include "agg_conv_transform.h"
#include "agg_conv_smooth_poly1.h"
// stl
#include <cmath>
namespace mapnik { namespace mapnik {
@ -47,12 +60,40 @@ public:
* converted to a positive value with similar magnitude, but * converted to a positive value with similar magnitude, but
* choosen to optimize marker placement. 0 = no markers * choosen to optimize marker placement. 0 = no markers
*/ */
markers_placement(Locator &locator, box2d<double> const& size, agg::trans_affine const& tr, Detector &detector, double spacing, double max_error, bool allow_overlap); markers_placement(Locator &locator, box2d<double> const& size, agg::trans_affine const& tr, Detector &detector, double spacing, double max_error, bool allow_overlap)
: locator_(locator),
size_(size),
tr_(tr),
detector_(detector),
max_error_(max_error),
allow_overlap_(allow_overlap)
{
marker_width_ = (size_ * tr_).width();
if (spacing >= 0)
{
spacing_ = spacing;
} else if (spacing < 0)
{
spacing_ = find_optimal_spacing(-spacing);
}
rewind();
}
/** Start again at first marker. /** Start again at first marker.
* \note Returns the same list of markers only works when they were NOT added * \note Returns the same list of markers only works when they were NOT added
* to the detector. * to the detector.
*/ */
void rewind(); void rewind()
{
locator_.rewind(0);
//Get first point
done_ = agg::is_stop(locator_.vertex(&next_x, &next_y)) || spacing_ < marker_width_;
last_x = next_x;
last_y = next_y; // Force request of new segment
error_ = 0;
marker_nr_ = 0;
}
/** Get a point where the marker should be placed. /** Get a point where the marker should be placed.
* Each time this function is called a new point is returned. * Each time this function is called a new point is returned.
* \param x Return value for x position * \param x Return value for x position
@ -61,7 +102,115 @@ public:
* \param add_to_detector Add selected position to detector * \param add_to_detector Add selected position to detector
* \return True if a place is found, false if none is found. * \return True if a place is found, false if none is found.
*/ */
bool get_point(double & x, double & y, double & angle, bool add_to_detector = true); bool get_point(double & x, double & y, double & angle, bool add_to_detector = true)
{
if (done_) return false;
unsigned cmd;
/* This functions starts at the position of the previous marker,
walks along the path, counting how far it has to go in spacing_left.
If one marker can't be placed at the position it should go to it is
moved a bit. The error is compensated for in the next call to this
function.
error > 0: Marker too near to the end of the path.
error = 0: Perfect position.
error < 0: Marker too near to the beginning of the path.
*/
if (marker_nr_++ == 0)
{
//First marker
spacing_left_ = spacing_ / 2;
} else
{
spacing_left_ = spacing_;
}
spacing_left_ -= error_;
error_ = 0;
//Loop exits when a position is found or when no more segments are available
while (true)
{
//Do not place markers too close to the beginning of a segment
if (spacing_left_ < marker_width_/2)
{
set_spacing_left(marker_width_/2); //Only moves forward
}
//Error for this marker is too large. Skip to the next position.
if (abs(error_) > max_error_ * spacing_)
{
if (error_ > spacing_)
{
error_ = 0; //Avoid moving backwards
MAPNIK_LOG_WARN(markers_placement) << "Extremely large error in markers_placement. Please file a bug report.";
}
spacing_left_ += spacing_ - error_;
error_ = 0;
}
double dx = next_x - last_x;
double dy = next_y - last_y;
double segment_length = std::sqrt(dx * dx + dy * dy);
if (segment_length <= spacing_left_)
{
//Segment is to short to place marker. Find next segment
spacing_left_ -= segment_length;
last_x = next_x;
last_y = next_y;
while (agg::is_move_to(cmd = locator_.vertex(&next_x, &next_y)))
{
//Skip over "move" commands
last_x = next_x;
last_y = next_y;
}
if (agg::is_stop(cmd))
{
done_ = true;
return false;
}
continue; //Try again
}
/* At this point we know the following things:
- segment_length > spacing_left
- error is small enough
- at least half a marker fits into this segment
*/
//Check if marker really fits in this segment
if (segment_length < marker_width_)
{
//Segment to short => Skip this segment
set_spacing_left(segment_length + marker_width_/2); //Only moves forward
continue;
} else if (segment_length - spacing_left_ < marker_width_/2)
{
//Segment is long enough, but we are to close to the end
//Note: This function moves backwards. This could lead to an infinite
// loop when another function adds a positive offset. Therefore we
// only move backwards when there is no offset
if (error_ == 0)
{
set_spacing_left(segment_length - marker_width_/2, true);
} else
{
//Skip this segment
set_spacing_left(segment_length + marker_width_/2); //Only moves forward
}
continue; //Force checking of max_error constraint
}
angle = atan2(dy, dx);
x = last_x + dx * (spacing_left_ / segment_length);
y = last_y + dy * (spacing_left_ / segment_length);
box2d<double> box = perform_transform(angle, x, y);
if (!allow_overlap_ && !detector_.has_placement(box))
{
//10.0 is the approxmiate number of positions tried and choosen arbitrarily
set_spacing_left(spacing_left_ + spacing_ * max_error_ / 10.0); //Only moves forward
continue;
}
if (add_to_detector) detector_.insert(box);
last_x = x;
last_y = y;
return true;
}
}
private: private:
Locator &locator_; Locator &locator_;
box2d<double> size_; box2d<double> size_;
@ -82,11 +231,69 @@ private:
unsigned marker_nr_; unsigned marker_nr_;
/** Rotates the size_ box and translates the position. */ /** Rotates the size_ box and translates the position. */
box2d<double> perform_transform(double angle, double dx, double dy); box2d<double> perform_transform(double angle, double dx, double dy)
{
double x1 = size_.minx();
double x2 = size_.maxx();
double y1 = size_.miny();
double y2 = size_.maxy();
agg::trans_affine tr = tr_ * agg::trans_affine_rotation(angle).translate(dx, dy);
double xA = x1, yA = y1, xB = x2, yB = y1, xC = x2, yC = y2, xD = x1, yD = y2;
tr.transform(&xA, &yA);
tr.transform(&xB, &yB);
tr.transform(&xC, &yC);
tr.transform(&xD, &yD);
box2d<double> result(xA, yA, xC, yC);
result.expand_to_include(xB, yB);
result.expand_to_include(xD, yD);
return result;
}
/** Automatically chooses spacing. */ /** Automatically chooses spacing. */
double find_optimal_spacing(double s); double find_optimal_spacing(double s)
{
rewind();
//Calculate total path length
unsigned cmd = agg::path_cmd_move_to;
double length = 0;
while (!agg::is_stop(cmd))
{
double dx = next_x - last_x;
double dy = next_y - last_y;
length += std::sqrt(dx * dx + dy * dy);
last_x = next_x;
last_y = next_y;
while (agg::is_move_to(cmd = locator_.vertex(&next_x, &next_y)))
{
//Skip over "move" commands
last_x = next_x;
last_y = next_y;
}
}
unsigned points = round(length / s);
if (points == 0) return 0.0; //Path to short
return length / points;
}
/** Set spacing_left_, adjusts error_ and performs sanity checks. */ /** Set spacing_left_, adjusts error_ and performs sanity checks. */
void set_spacing_left(double sl, bool allow_negative=false); void set_spacing_left(double sl, bool allow_negative=false)
{
double delta_error = sl - spacing_left_;
if (!allow_negative && delta_error < 0)
{
MAPNIK_LOG_WARN(markers_placement) << "Unexpected negative error in markers_placement. Please file a bug report.";
return;
}
#ifdef MAPNIK_DEBUG
if (delta_error == 0.0)
{
MAPNIK_LOG_WARN(markers_placement) << "Not moving at all in set_spacing_left()! Please file a bug report.";
}
#endif
error_ += delta_error;
spacing_left_ = sl;
}
}; };
} }

View file

@ -39,6 +39,7 @@ namespace mapnik {
// TODO - consider merging with text_symbolizer label_placement_e // TODO - consider merging with text_symbolizer label_placement_e
enum marker_placement_enum { enum marker_placement_enum {
MARKER_POINT_PLACEMENT, MARKER_POINT_PLACEMENT,
MARKER_INTERIOR_PLACEMENT,
MARKER_LINE_PLACEMENT, MARKER_LINE_PLACEMENT,
marker_placement_enum_MAX marker_placement_enum_MAX
}; };
@ -65,8 +66,12 @@ public:
double get_spacing() const; double get_spacing() const;
void set_max_error(double max_error); void set_max_error(double max_error);
double get_max_error() const; double get_max_error() const;
void set_opacity(float opacity);
boost::optional<float> get_opacity() const;
void set_fill(color const& fill); void set_fill(color const& fill);
boost::optional<color> get_fill() const; boost::optional<color> get_fill() const;
void set_fill_opacity(float opacity);
boost::optional<float> get_fill_opacity() const;
void set_stroke(stroke const& stroke); void set_stroke(stroke const& stroke);
boost::optional<stroke> get_stroke() const; boost::optional<stroke> get_stroke() const;
void set_marker_placement(marker_placement_e marker_p); void set_marker_placement(marker_placement_e marker_p);
@ -79,6 +84,8 @@ private:
double spacing_; double spacing_;
double max_error_; double max_error_;
boost::optional<color> fill_; boost::optional<color> fill_;
boost::optional<float> fill_opacity_;
boost::optional<float> opacity_;
boost::optional<stroke> stroke_; boost::optional<stroke> stroke_;
marker_placement_e marker_p_; marker_placement_e marker_p_;
}; };

View file

@ -66,10 +66,10 @@ public:
{ {
throw std::runtime_error("end_path : The path was not begun"); throw std::runtime_error("end_path : The path was not begun");
} }
path_attributes attr = cur_attr(); path_attributes& attr = attributes_[attributes_.size() - 1];
unsigned idx = attributes_[attributes_.size() - 1].index; unsigned idx = attr.index;
attr = cur_attr();
attr.index = idx; attr.index = idx;
attributes_[attributes_.size() - 1] = attr;
} }
void move_to(double x, double y, bool rel=false) // M, m void move_to(double x, double y, bool rel=false) // M, m

View file

@ -102,14 +102,14 @@ private:
template <typename VertexSource, typename AttributeSource, typename ScanlineRenderer, typename PixelFormat> template <typename VertexSource, typename AttributeSource, typename ScanlineRenderer, typename PixelFormat>
class svg_renderer : boost::noncopyable class svg_renderer : boost::noncopyable
{ {
public:
typedef agg::conv_curve<VertexSource> curved_type; typedef agg::conv_curve<VertexSource> curved_type;
typedef agg::conv_stroke<curved_type> curved_stroked_type; typedef agg::conv_stroke<curved_type> curved_stroked_type;
typedef agg::conv_transform<curved_stroked_type> curved_stroked_trans_type; typedef agg::conv_transform<curved_stroked_type> curved_stroked_trans_type;
typedef agg::conv_transform<curved_type> curved_trans_type; typedef agg::conv_transform<curved_type> curved_trans_type;
typedef agg::conv_contour<curved_trans_type> curved_trans_contour_type; typedef agg::conv_contour<curved_trans_type> curved_trans_contour_type;
typedef agg::renderer_base<PixelFormat> renderer_base; typedef agg::renderer_base<PixelFormat> renderer_base;
public:
svg_renderer(VertexSource & source, AttributeSource const& attributes) svg_renderer(VertexSource & source, AttributeSource const& attributes)
: source_(source), : source_(source),
curved_(source_), curved_(source_),

View file

@ -21,12 +21,14 @@
*****************************************************************************/ *****************************************************************************/
// mapnik // mapnik
#include <mapnik/layer.hpp>
#include <mapnik/feature_type_style.hpp>
#include <mapnik/graphics.hpp>
#include <mapnik/agg_renderer.hpp> #include <mapnik/agg_renderer.hpp>
#include <mapnik/agg_rasterizer.hpp> #include <mapnik/agg_rasterizer.hpp>
#include <mapnik/agg_helpers.hpp> #include <mapnik/agg_helpers.hpp>
#include <mapnik/graphics.hpp>
#include <mapnik/debug.hpp>
#include <mapnik/layer.hpp>
#include <mapnik/feature_type_style.hpp>
#include <mapnik/marker.hpp> #include <mapnik/marker.hpp>
#include <mapnik/marker_cache.hpp> #include <mapnik/marker_cache.hpp>
#include <mapnik/unicode.hpp> #include <mapnik/unicode.hpp>
@ -36,6 +38,7 @@
#include <mapnik/svg/svg_converter.hpp> #include <mapnik/svg/svg_converter.hpp>
#include <mapnik/svg/svg_renderer.hpp> #include <mapnik/svg/svg_renderer.hpp>
#include <mapnik/svg/svg_path_adapter.hpp> #include <mapnik/svg/svg_path_adapter.hpp>
#include <mapnik/image_compositing.hpp> #include <mapnik/image_compositing.hpp>
#include <mapnik/image_filter.hpp> #include <mapnik/image_filter.hpp>
#include <mapnik/image_util.hpp> #include <mapnik/image_util.hpp>
@ -156,6 +159,10 @@ void agg_renderer<T>::setup(Map const &m)
} }
} }
agg::rendering_buffer buf(pixmap_.raw_data(),width_,height_, width_ * 4);
agg::pixfmt_rgba32 pixf(buf);
pixf.premultiply();
MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: Scale=" << m.scale(); MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: Scale=" << m.scale();
} }
@ -166,7 +173,6 @@ template <typename T>
void agg_renderer<T>::start_map_processing(Map const& map) void agg_renderer<T>::start_map_processing(Map const& map)
{ {
MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: Start map processing bbox=" << map.get_current_extent(); MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: Start map processing bbox=" << map.get_current_extent();
ras_ptr->clip_box(0,0,width_,height_); ras_ptr->clip_box(0,0,width_,height_);
} }
@ -191,7 +197,24 @@ void agg_renderer<T>::start_layer_processing(layer const& lay, box2d<double> con
{ {
detector_->clear(); detector_->clear();
} }
query_extent_ = query_extent; query_extent_ = query_extent;
int buffer_size = lay.buffer_size();
if (buffer_size != 0 )
{
double padding = buffer_size * (double)(query_extent.width()/pixmap_.width());
double x0 = query_extent_.minx();
double y0 = query_extent_.miny();
double x1 = query_extent_.maxx();
double y1 = query_extent_.maxy();
query_extent_.init(x0 - padding, y0 - padding, x1 + padding , y1 + padding);
}
boost::optional<box2d<double> > const& maximum_extent = lay.maximum_extent();
if (maximum_extent)
{
query_extent_.clip(*maximum_extent);
}
} }
template <typename T> template <typename T>
@ -362,15 +385,18 @@ void agg_renderer<T>::render_marker(pixel_position const& pos, marker const& mar
src.height(), src.height(),
src.width()*4); src.width()*4);
agg::pixfmt_rgba32_pre pixf(marker_buf); agg::pixfmt_rgba32_pre pixf(marker_buf);
typedef agg::image_accessor_clone<agg::pixfmt_rgba32_pre> img_accessor_type; typedef agg::image_accessor_clone<agg::pixfmt_rgba32_pre> img_accessor_type;
typedef agg::span_interpolator_linear<agg::trans_affine> interpolator_type; typedef agg::span_interpolator_linear<agg::trans_affine> interpolator_type;
typedef agg::span_image_filter_rgba_2x2<img_accessor_type, typedef agg::span_image_filter_rgba_2x2<img_accessor_type,
interpolator_type> span_gen_type; interpolator_type> span_gen_type;
typedef agg::renderer_scanline_aa_alpha<renderer_base,
agg::span_allocator<agg::rgba8>,
span_gen_type> renderer_type;
img_accessor_type ia(pixf); img_accessor_type ia(pixf);
interpolator_type interpolator(agg::trans_affine(p, 0, 0, width, height) ); interpolator_type interpolator(agg::trans_affine(p, 0, 0, width, height) );
span_gen_type sg(ia, interpolator, filter); span_gen_type sg(ia, interpolator, filter);
agg::render_scanlines_aa(*ras_ptr, sl, renb, sa, sg); renderer_type rp(renb,sa, sg, unsigned(opacity*255));
agg::render_scanlines(*ras_ptr, sl, rp);
} }
} }
} }
@ -427,6 +453,27 @@ void agg_renderer<T>::debug_draw_box(R& buf, box2d<double> const& box,
agg::render_scanlines(*ras_ptr, sl_line, ren); agg::render_scanlines(*ras_ptr, sl_line, ren);
} }
template <typename T>
void agg_renderer<T>::draw_geo_extent(box2d<double> const& extent, mapnik::color const& color)
{
box2d<double> box = t_.forward(extent);
double x0 = box.minx();
double x1 = box.maxx();
double y0 = box.miny();
double y1 = box.maxy();
unsigned rgba = color.rgba();
for (double x=x0; x<x1; x++)
{
pixmap_.setPixel(x, y0, rgba);
pixmap_.setPixel(x, y1, rgba);
}
for (double y=y0; y<y1; y++)
{
pixmap_.setPixel(x0, y, rgba);
pixmap_.setPixel(x1, y, rgba);
}
}
template class agg_renderer<image_32>; template class agg_renderer<image_32>;
template void agg_renderer<image_32>::debug_draw_box<agg::rendering_buffer>( template void agg_renderer<image_32>::debug_draw_box<agg::rendering_buffer>(
agg::rendering_buffer& buf, agg::rendering_buffer& buf,

View file

@ -82,32 +82,31 @@ void agg_renderer<T>::process(building_symbolizer const& sym,
boost::scoped_ptr<geometry_type> frame(new geometry_type(LineString)); boost::scoped_ptr<geometry_type> frame(new geometry_type(LineString));
boost::scoped_ptr<geometry_type> roof(new geometry_type(Polygon)); boost::scoped_ptr<geometry_type> roof(new geometry_type(Polygon));
std::deque<segment_t> face_segments; std::deque<segment_t> face_segments;
double x0(0); double x0 = 0;
double y0(0); double y0 = 0;
double x,y;
geom.rewind(0); geom.rewind(0);
unsigned cm = geom.vertex(&x0,&y0); for (unsigned cm = geom.vertex(&x, &y); cm != SEG_END;
for (unsigned j=1;j<geom.size();++j) cm = geom.vertex(&x, &y))
{ {
double x(0);
double y(0);
cm = geom.vertex(&x,&y);
if (cm == SEG_MOVETO) if (cm == SEG_MOVETO)
{ {
frame->move_to(x,y); frame->move_to(x,y);
} }
else if (cm == SEG_LINETO) else if (cm == SEG_LINETO || cm == SEG_CLOSE)
{ {
frame->line_to(x,y); frame->line_to(x,y);
face_segments.push_back(segment_t(x0,y0,x,y)); face_segments.push_back(segment_t(x0,y0,x,y));
} }
x0 = x; x0 = x;
y0 = y; y0 = y;
} }
std::sort(face_segments.begin(),face_segments.end(), y_order); std::sort(face_segments.begin(),face_segments.end(), y_order);
std::deque<segment_t>::const_iterator itr=face_segments.begin(); std::deque<segment_t>::const_iterator itr=face_segments.begin();
for (;itr!=face_segments.end();++itr) std::deque<segment_t>::const_iterator end=face_segments.end();
for (; itr!=end; ++itr)
{ {
boost::scoped_ptr<geometry_type> faces(new geometry_type(Polygon)); boost::scoped_ptr<geometry_type> faces(new geometry_type(Polygon));
faces->move_to(itr->get<0>(),itr->get<1>()); faces->move_to(itr->get<0>(),itr->get<1>());
@ -120,22 +119,22 @@ void agg_renderer<T>::process(building_symbolizer const& sym,
ren.color(agg::rgba8(int(r*0.8), int(g*0.8), int(b*0.8), int(a * sym.get_opacity()))); ren.color(agg::rgba8(int(r*0.8), int(g*0.8), int(b*0.8), int(a * sym.get_opacity())));
agg::render_scanlines(*ras_ptr, sl, ren); agg::render_scanlines(*ras_ptr, sl, ren);
ras_ptr->reset(); ras_ptr->reset();
//
frame->move_to(itr->get<0>(),itr->get<1>()); frame->move_to(itr->get<0>(),itr->get<1>());
frame->line_to(itr->get<0>(),itr->get<1>()+height); frame->line_to(itr->get<0>(),itr->get<1>()+height);
} }
geom.rewind(0); geom.rewind(0);
for (unsigned j=0;j<geom.size();++j) for (unsigned cm = geom.vertex(&x, &y); cm != SEG_END;
cm = geom.vertex(&x, &y))
{ {
double x,y;
unsigned cm = geom.vertex(&x,&y);
if (cm == SEG_MOVETO) if (cm == SEG_MOVETO)
{ {
frame->move_to(x,y+height); frame->move_to(x,y+height);
roof->move_to(x,y+height); roof->move_to(x,y+height);
} }
else if (cm == SEG_LINETO) else if (cm == SEG_LINETO || cm == SEG_CLOSE)
{ {
frame->line_to(x,y+height); frame->line_to(x,y+height);
roof->line_to(x,y+height); roof->line_to(x,y+height);
@ -153,6 +152,7 @@ void agg_renderer<T>::process(building_symbolizer const& sym,
ras_ptr->add_path(roof_path); ras_ptr->add_path(roof_path);
ren.color(agg::rgba8(r, g, b, int(a * sym.get_opacity()))); ren.color(agg::rgba8(r, g, b, int(a * sym.get_opacity())));
agg::render_scanlines(*ras_ptr, sl, ren); agg::render_scanlines(*ras_ptr, sl, ren);
} }
} }
} }

View file

@ -56,7 +56,7 @@ void agg_renderer<T>::process(line_pattern_symbolizer const& sym,
{ {
typedef agg::rgba8 color; typedef agg::rgba8 color;
typedef agg::order_rgba order; typedef agg::order_rgba order;
typedef agg::pixel32_type pixel_type; typedef agg::pixel32_type pixel_type;
typedef agg::comp_op_adaptor_rgba_pre<color, order> blender_type; typedef agg::comp_op_adaptor_rgba_pre<color, order> blender_type;
typedef agg::pattern_filter_bilinear_rgba8 pattern_filter_type; typedef agg::pattern_filter_bilinear_rgba8 pattern_filter_type;
typedef agg::line_image_pattern<pattern_filter_type> pattern_type; typedef agg::line_image_pattern<pattern_filter_type> pattern_type;
@ -64,7 +64,7 @@ void agg_renderer<T>::process(line_pattern_symbolizer const& sym,
typedef agg::renderer_base<pixfmt_type> renderer_base; typedef agg::renderer_base<pixfmt_type> renderer_base;
typedef agg::renderer_outline_image<renderer_base, pattern_type> renderer_type; typedef agg::renderer_outline_image<renderer_base, pattern_type> renderer_type;
typedef agg::rasterizer_outline_aa<renderer_type> rasterizer_type; typedef agg::rasterizer_outline_aa<renderer_type> rasterizer_type;
std::string filename = path_processor_type::evaluate( *sym.get_filename(), feature); std::string filename = path_processor_type::evaluate( *sym.get_filename(), feature);
boost::optional<marker_ptr> mark = marker_cache::instance()->find(filename,true); boost::optional<marker_ptr> mark = marker_cache::instance()->find(filename,true);
@ -81,8 +81,6 @@ void agg_renderer<T>::process(line_pattern_symbolizer const& sym,
if (!pat) return; if (!pat) return;
box2d<double> ext = query_extent_ * 1.0;
agg::rendering_buffer buf(current_buffer_->raw_data(),width_,height_, width_ * 4); agg::rendering_buffer buf(current_buffer_->raw_data(),width_,height_, width_ * 4);
pixfmt_type pixf(buf); pixfmt_type pixf(buf);
pixf.comp_op(static_cast<agg::comp_op_e>(sym.comp_op())); pixf.comp_op(static_cast<agg::comp_op_e>(sym.comp_op()));
@ -91,26 +89,40 @@ void agg_renderer<T>::process(line_pattern_symbolizer const& sym,
pattern_source source(*(*pat)); pattern_source source(*(*pat));
pattern_type pattern (filter,source); pattern_type pattern (filter,source);
renderer_type ren(ren_base, pattern); renderer_type ren(ren_base, pattern);
rasterizer_type ras(ren); rasterizer_type ras(ren);
agg::trans_affine tr; agg::trans_affine tr;
evaluate_transform(tr, feature, sym.get_transform()); evaluate_transform(tr, feature, sym.get_transform());
box2d<double> clipping_extent = query_extent_;
if (sym.clip())
{
double padding = (double)(query_extent_.width()/pixmap_.width());
float half_stroke = (*mark)->width()/2.0;
if (half_stroke > 1)
padding *= half_stroke;
double x0 = query_extent_.minx();
double y0 = query_extent_.miny();
double x1 = query_extent_.maxx();
double y1 = query_extent_.maxy();
clipping_extent.init(x0 - padding, y0 - padding, x1 + padding , y1 + padding);
}
typedef boost::mpl::vector<clip_line_tag,transform_tag,smooth_tag> conv_types; typedef boost::mpl::vector<clip_line_tag,transform_tag,smooth_tag> conv_types;
vertex_converter<box2d<double>, rasterizer_type, line_pattern_symbolizer, vertex_converter<box2d<double>, rasterizer_type, line_pattern_symbolizer,
CoordTransform, proj_transform, agg::trans_affine, conv_types> CoordTransform, proj_transform, agg::trans_affine, conv_types>
converter(ext,ras,sym,t_,prj_trans,tr,scale_factor_); converter(clipping_extent,ras,sym,t_,prj_trans,tr,scale_factor_);
if (sym.clip()) converter.set<clip_line_tag>(); //optional clip (default: true) if (sym.clip()) converter.set<clip_line_tag>(); //optional clip (default: true)
converter.set<transform_tag>(); //always transform converter.set<transform_tag>(); //always transform
if (sym.smooth() > 0.0) converter.set<smooth_tag>(); // optional smooth converter if (sym.smooth() > 0.0) converter.set<smooth_tag>(); // optional smooth converter
BOOST_FOREACH(geometry_type & geom, feature.paths()) BOOST_FOREACH(geometry_type & geom, feature.paths())
{ {
if (geom.size() > 1) if (geom.size() > 1)
{ {
converter.apply(geom); converter.apply(geom);
} }
} }
} }

View file

@ -47,7 +47,6 @@
namespace mapnik { namespace mapnik {
template <typename T> template <typename T>
void agg_renderer<T>::process(line_symbolizer const& sym, void agg_renderer<T>::process(line_symbolizer const& sym,
mapnik::feature_impl & feature, mapnik::feature_impl & feature,
@ -60,7 +59,7 @@ void agg_renderer<T>::process(line_symbolizer const& sym,
unsigned g=col.green(); unsigned g=col.green();
unsigned b=col.blue(); unsigned b=col.blue();
unsigned a=col.alpha(); unsigned a=col.alpha();
ras_ptr->reset(); ras_ptr->reset();
set_gamma_method(stroke_, ras_ptr); set_gamma_method(stroke_, ras_ptr);
@ -76,11 +75,28 @@ void agg_renderer<T>::process(line_symbolizer const& sym,
pixfmt_comp_type pixf(buf); pixfmt_comp_type pixf(buf);
pixf.comp_op(static_cast<agg::comp_op_e>(sym.comp_op())); pixf.comp_op(static_cast<agg::comp_op_e>(sym.comp_op()));
renderer_base renb(pixf); renderer_base renb(pixf);
agg::trans_affine tr; agg::trans_affine tr;
evaluate_transform(tr, feature, sym.get_transform()); evaluate_transform(tr, feature, sym.get_transform());
box2d<double> ext = query_extent_ * 1.1; box2d<double> clipping_extent = query_extent_;
if (sym.clip())
{
double padding = (double)(query_extent_.width()/pixmap_.width());
float half_stroke = stroke_.get_width()/2.0;
if (half_stroke > 1)
padding *= half_stroke;
if (fabs(sym.offset()) > 0)
padding *= fabs(sym.offset()) * 1.2;
double x0 = query_extent_.minx();
double y0 = query_extent_.miny();
double x1 = query_extent_.maxx();
double y1 = query_extent_.maxy();
clipping_extent.init(x0 - padding, y0 - padding, x1 + padding , y1 + padding);
// debugging
//box2d<double> inverse(x0 + padding, y0 + padding, x1 - padding , y1 - padding);
//draw_geo_extent(inverse,mapnik::color("red"));
}
if (sym.get_rasterizer() == RASTERIZER_FAST) if (sym.get_rasterizer() == RASTERIZER_FAST)
{ {
@ -94,11 +110,12 @@ void agg_renderer<T>::process(line_symbolizer const& sym,
rasterizer_type ras(ren); rasterizer_type ras(ren);
set_join_caps_aa(stroke_,ras); set_join_caps_aa(stroke_,ras);
typedef boost::mpl::vector<clip_line_tag,transform_tag, offset_transform_tag, affine_transform_tag, smooth_tag, dash_tag, stroke_tag> conv_types; typedef boost::mpl::vector<clip_line_tag, transform_tag,
offset_transform_tag, affine_transform_tag,
smooth_tag, dash_tag, stroke_tag> conv_types;
vertex_converter<box2d<double>, rasterizer_type, line_symbolizer, vertex_converter<box2d<double>, rasterizer_type, line_symbolizer,
CoordTransform, proj_transform, agg::trans_affine, conv_types> CoordTransform, proj_transform, agg::trans_affine, conv_types>
converter(ext,ras,sym,t_,prj_trans,tr,scaled); converter(clipping_extent,ras,sym,t_,prj_trans,tr,scaled);
if (sym.clip()) converter.set<clip_line_tag>(); // optional clip (default: true) if (sym.clip()) converter.set<clip_line_tag>(); // optional clip (default: true)
converter.set<transform_tag>(); // always transform converter.set<transform_tag>(); // always transform
if (fabs(sym.offset()) > 0.0) converter.set<offset_transform_tag>(); // parallel offset if (fabs(sym.offset()) > 0.0) converter.set<offset_transform_tag>(); // parallel offset
@ -117,10 +134,12 @@ void agg_renderer<T>::process(line_symbolizer const& sym,
} }
else else
{ {
typedef boost::mpl::vector<clip_line_tag,transform_tag, offset_transform_tag, affine_transform_tag, smooth_tag, dash_tag, stroke_tag> conv_types; typedef boost::mpl::vector<clip_line_tag, transform_tag, offset_transform_tag,
affine_transform_tag, smooth_tag, dash_tag, stroke_tag> conv_types;
vertex_converter<box2d<double>, rasterizer, line_symbolizer, vertex_converter<box2d<double>, rasterizer, line_symbolizer,
CoordTransform, proj_transform, agg::trans_affine, conv_types> CoordTransform, proj_transform, agg::trans_affine, conv_types>
converter(ext,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_); converter(clipping_extent,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_);
if (sym.clip()) converter.set<clip_line_tag>(); // optional clip (default: true) if (sym.clip()) converter.set<clip_line_tag>(); // optional clip (default: true)
converter.set<transform_tag>(); // always transform converter.set<transform_tag>(); // always transform

View file

@ -21,23 +21,27 @@
*****************************************************************************/ *****************************************************************************/
// mapnik // mapnik
#include <mapnik/debug.hpp>
#include <mapnik/graphics.hpp> #include <mapnik/graphics.hpp>
#include <mapnik/geom_util.hpp>
#include <mapnik/agg_renderer.hpp> #include <mapnik/agg_renderer.hpp>
#include <mapnik/agg_rasterizer.hpp> #include <mapnik/agg_rasterizer.hpp>
#include <mapnik/debug.hpp>
#include <mapnik/geom_util.hpp>
#include <mapnik/expression_evaluator.hpp> #include <mapnik/expression_evaluator.hpp>
#include <mapnik/vertex_converters.hpp> #include <mapnik/vertex_converters.hpp>
#include <mapnik/marker_helpers.hpp>
#include <mapnik/marker.hpp> #include <mapnik/marker.hpp>
#include <mapnik/marker_cache.hpp> #include <mapnik/marker_cache.hpp>
#include <mapnik/marker_helpers.hpp>
#include <mapnik/svg/svg_renderer.hpp> #include <mapnik/svg/svg_renderer.hpp>
#include <mapnik/svg/svg_storage.hpp>
#include <mapnik/svg/svg_path_adapter.hpp> #include <mapnik/svg/svg_path_adapter.hpp>
#include <mapnik/markers_placement.hpp> #include <mapnik/svg/svg_path_attributes.hpp>
#include <mapnik/markers_symbolizer.hpp> #include <mapnik/markers_symbolizer.hpp>
// agg // agg
#include "agg_basics.h" #include "agg_basics.h"
#include "agg_renderer_base.h"
#include "agg_renderer_scanline.h"
#include "agg_rendering_buffer.h" #include "agg_rendering_buffer.h"
#include "agg_pixfmt_rgba.h" #include "agg_pixfmt_rgba.h"
#include "agg_rasterizer_scanline_aa.h" #include "agg_rasterizer_scanline_aa.h"
@ -45,234 +49,13 @@
#include "agg_path_storage.h" #include "agg_path_storage.h"
#include "agg_conv_clip_polyline.h" #include "agg_conv_clip_polyline.h"
#include "agg_conv_transform.h" #include "agg_conv_transform.h"
#include "agg_image_filters.h"
#include "agg_trans_bilinear.h"
#include "agg_span_allocator.h"
#include "agg_image_accessors.h"
#include "agg_span_image_filter_rgba.h"
// boost // boost
#include <boost/optional.hpp> #include <boost/optional.hpp>
namespace mapnik { namespace mapnik {
template <typename BufferType, typename SvgRenderer, typename Rasterizer, typename Detector>
struct vector_markers_rasterizer_dispatch
{
typedef agg::rgba8 color_type;
typedef agg::order_rgba order_type;
typedef agg::pixel32_type pixel_type;
typedef agg::comp_op_adaptor_rgba_pre<color_type, order_type> blender_type; // comp blender
typedef agg::pixfmt_custom_blend_rgba<blender_type, agg::rendering_buffer> pixfmt_comp_type;
typedef agg::renderer_base<pixfmt_comp_type> renderer_base;
typedef agg::renderer_scanline_aa_solid<renderer_base> renderer_type;
vector_markers_rasterizer_dispatch(BufferType & image_buffer,
SvgRenderer & svg_renderer,
Rasterizer & ras,
box2d<double> const& bbox,
agg::trans_affine const& marker_trans,
markers_symbolizer const& sym,
Detector & detector,
double scale_factor)
: buf_(image_buffer.raw_data(), image_buffer.width(), image_buffer.height(), image_buffer.width() * 4),
pixf_(buf_),
renb_(pixf_),
svg_renderer_(svg_renderer),
ras_(ras),
bbox_(bbox),
marker_trans_(marker_trans),
sym_(sym),
detector_(detector),
scale_factor_(scale_factor)
{
pixf_.comp_op(static_cast<agg::comp_op_e>(sym_.comp_op()));
}
template <typename T>
void add_path(T & path)
{
marker_placement_e placement_method = sym_.get_marker_placement();
if (placement_method == MARKER_POINT_PLACEMENT)
{
double x,y;
path.rewind(0);
label::interior_position(path, x, y);
agg::trans_affine matrix = marker_trans_;
matrix.translate(x,y);
box2d<double> transformed_bbox = bbox_ * matrix;
if (sym_.get_allow_overlap() ||
detector_.has_placement(transformed_bbox))
{
svg_renderer_.render(ras_, sl_, renb_, matrix, sym_.get_opacity(), bbox_);
if (!sym_.get_ignore_placement())
detector_.insert(transformed_bbox);
}
}
else
{
markers_placement<T, label_collision_detector4> placement(path, bbox_, marker_trans_, detector_,
sym_.get_spacing() * scale_factor_,
sym_.get_max_error(),
sym_.get_allow_overlap());
double x, y, angle;
while (placement.get_point(x, y, angle))
{
agg::trans_affine matrix = marker_trans_;
matrix.rotate(angle);
matrix.translate(x, y);
svg_renderer_.render(ras_, sl_, renb_, matrix, sym_.get_opacity(), bbox_);
}
}
}
private:
agg::scanline_u8 sl_;
agg::rendering_buffer buf_;
pixfmt_comp_type pixf_;
renderer_base renb_;
SvgRenderer & svg_renderer_;
Rasterizer & ras_;
box2d<double> const& bbox_;
agg::trans_affine const& marker_trans_;
markers_symbolizer const& sym_;
Detector & detector_;
double scale_factor_;
};
template <typename Rasterizer, typename RendererBuffer>
void render_raster_marker(Rasterizer & ras, RendererBuffer & renb,
agg::scanline_u8 & sl, image_data_32 const& src,
agg::trans_affine const& marker_tr, double opacity)
{
double width = src.width();
double height = src.height();
double p[8];
p[0] = 0; p[1] = 0;
p[2] = width; p[3] = 0;
p[4] = width; p[5] = height;
p[6] = 0; p[7] = height;
marker_tr.transform(&p[0], &p[1]);
marker_tr.transform(&p[2], &p[3]);
marker_tr.transform(&p[4], &p[5]);
marker_tr.transform(&p[6], &p[7]);
ras.move_to_d(p[0],p[1]);
ras.line_to_d(p[2],p[3]);
ras.line_to_d(p[4],p[5]);
ras.line_to_d(p[6],p[7]);
typedef agg::rgba8 color_type;
agg::span_allocator<color_type> sa;
agg::image_filter_bilinear filter_kernel;
agg::image_filter_lut filter(filter_kernel, false);
agg::rendering_buffer marker_buf((unsigned char *)src.getBytes(),
src.width(),
src.height(),
src.width()*4);
agg::pixfmt_rgba32_pre pixf(marker_buf);
typedef agg::image_accessor_clone<agg::pixfmt_rgba32_pre> img_accessor_type;
typedef agg::span_interpolator_linear<agg::trans_affine> interpolator_type;
typedef agg::span_image_filter_rgba_2x2<img_accessor_type,
interpolator_type> span_gen_type;
img_accessor_type ia(pixf);
interpolator_type interpolator(agg::trans_affine(p, 0, 0, width, height) );
span_gen_type sg(ia, interpolator, filter);
agg::render_scanlines_aa(ras, sl, renb, sa, sg);
}
template <typename BufferType, typename Rasterizer, typename Detector>
struct raster_markers_rasterizer_dispatch
{
typedef agg::rgba8 color_type;
typedef agg::order_rgba order_type;
typedef agg::pixel32_type pixel_type;
typedef agg::comp_op_adaptor_rgba_pre<color_type, order_type> blender_type; // comp blender
typedef agg::pixfmt_custom_blend_rgba<blender_type, agg::rendering_buffer> pixfmt_comp_type;
typedef agg::renderer_base<pixfmt_comp_type> renderer_base;
typedef agg::renderer_scanline_aa_solid<renderer_base> renderer_type;
raster_markers_rasterizer_dispatch(BufferType & image_buffer,
Rasterizer & ras,
image_data_32 const& src,
agg::trans_affine const& marker_trans,
markers_symbolizer const& sym,
Detector & detector,
double scale_factor)
: buf_(image_buffer.raw_data(), image_buffer.width(), image_buffer.height(), image_buffer.width() * 4),
pixf_(buf_),
renb_(pixf_),
ras_(ras),
src_(src),
marker_trans_(marker_trans),
sym_(sym),
detector_(detector),
scale_factor_(scale_factor)
{
pixf_.comp_op(static_cast<agg::comp_op_e>(sym_.comp_op()));
}
template <typename T>
void add_path(T & path)
{
marker_placement_e placement_method = sym_.get_marker_placement();
box2d<double> bbox_(0,0, src_.width(),src_.height());
if (placement_method == MARKER_POINT_PLACEMENT)
{
double x,y;
path.rewind(0);
label::interior_position(path, x, y);
agg::trans_affine matrix = marker_trans_;
matrix.translate(x,y);
box2d<double> transformed_bbox = bbox_ * matrix;
if (sym_.get_allow_overlap() ||
detector_.has_placement(transformed_bbox))
{
render_raster_marker(ras_, renb_, sl_, src_,
matrix, sym_.get_opacity());
if (!sym_.get_ignore_placement())
detector_.insert(transformed_bbox);
}
}
else
{
markers_placement<T, label_collision_detector4> placement(path, bbox_, marker_trans_, detector_,
sym_.get_spacing() * scale_factor_,
sym_.get_max_error(),
sym_.get_allow_overlap());
double x, y, angle;
while (placement.get_point(x, y, angle))
{
agg::trans_affine matrix = marker_trans_;
matrix.rotate(angle);
matrix.translate(x,y);
render_raster_marker(ras_, renb_, sl_, src_,
matrix, sym_.get_opacity());
}
}
}
private:
agg::scanline_u8 sl_;
agg::rendering_buffer buf_;
pixfmt_comp_type pixf_;
renderer_base renb_;
Rasterizer & ras_;
image_data_32 const& src_;
agg::trans_affine const& marker_trans_;
markers_symbolizer const& sym_;
Detector & detector_;
double scale_factor_;
};
template <typename T> template <typename T>
void agg_renderer<T>::process(markers_symbolizer const& sym, void agg_renderer<T>::process(markers_symbolizer const& sym,
feature_impl & feature, feature_impl & feature,
@ -284,9 +67,8 @@ void agg_renderer<T>::process(markers_symbolizer const& sym,
typedef agg::comp_op_adaptor_rgba_pre<color_type, order_type> blender_type; // comp blender typedef agg::comp_op_adaptor_rgba_pre<color_type, order_type> blender_type; // comp blender
typedef agg::pixfmt_custom_blend_rgba<blender_type, agg::rendering_buffer> pixfmt_comp_type; typedef agg::pixfmt_custom_blend_rgba<blender_type, agg::rendering_buffer> pixfmt_comp_type;
typedef agg::renderer_base<pixfmt_comp_type> renderer_base; typedef agg::renderer_base<pixfmt_comp_type> renderer_base;
typedef agg::renderer_scanline_aa_solid<renderer_base> renderer_type;
typedef label_collision_detector4 detector_type; typedef label_collision_detector4 detector_type;
typedef boost::mpl::vector<clip_line_tag,transform_tag,smooth_tag> conv_types; typedef boost::mpl::vector<clip_line_tag,clip_poly_tag,transform_tag,smooth_tag> conv_types;
std::string filename = path_processor_type::evaluate(*sym.get_filename(), feature); std::string filename = path_processor_type::evaluate(*sym.get_filename(), feature);
@ -297,67 +79,124 @@ void agg_renderer<T>::process(markers_symbolizer const& sym,
{ {
ras_ptr->reset(); ras_ptr->reset();
ras_ptr->gamma(agg::gamma_power()); ras_ptr->gamma(agg::gamma_power());
agg::trans_affine geom_tr; agg::trans_affine geom_tr;
evaluate_transform(geom_tr, feature, sym.get_transform()); evaluate_transform(geom_tr, feature, sym.get_transform());
box2d<double> const& bbox = (*mark)->bounding_box();
agg::trans_affine tr; agg::trans_affine tr;
setup_label_transform(tr, bbox, feature, sym); tr *= agg::trans_affine_scaling(scale_factor_);
tr = agg::trans_affine_scaling(scale_factor_) * tr;
coord2d center = bbox.center();
agg::trans_affine_translation recenter(-center.x, -center.y);
agg::trans_affine marker_trans = recenter * tr;
if ((*mark)->is_vector()) if ((*mark)->is_vector())
{ {
using namespace mapnik::svg; using namespace mapnik::svg;
boost::optional<path_ptr> marker = (*mark)->get_vector_data(); typedef agg::renderer_scanline_aa_solid<renderer_base> renderer_type;
typedef agg::pod_bvector<path_attributes> svg_attribute_type;
vertex_stl_adapter<svg_path_storage> stl_storage((*marker)->source());
svg_path_adapter svg_path(stl_storage);
agg::pod_bvector<path_attributes> attributes;
bool result = push_explicit_style( (*marker)->attributes(), attributes, sym);
typedef svg_renderer<svg_path_adapter, typedef svg_renderer<svg_path_adapter,
agg::pod_bvector<path_attributes>, svg_attribute_type,
renderer_type, renderer_type,
agg::pixfmt_rgba32 > svg_renderer_type; pixfmt_comp_type > svg_renderer_type;
typedef vector_markers_rasterizer_dispatch<buffer_type, svg_renderer_type, rasterizer, detector_type> dispatch_type; typedef vector_markers_rasterizer_dispatch<buffer_type,
svg_renderer_type,
rasterizer,
detector_type > dispatch_type;
boost::optional<svg_path_ptr> const& stock_vector_marker = (*mark)->get_vector_data();
expression_ptr const& width_expr = sym.get_width();
expression_ptr const& height_expr = sym.get_height();
// special case for simple ellipse markers
svg_renderer_type svg_renderer(svg_path, result ? attributes : (*marker)->attributes()); // to allow for full control over rx/ry dimensions
if (filename == "shape://ellipse"
dispatch_type rasterizer_dispatch(*current_buffer_,svg_renderer,*ras_ptr, && (width_expr || height_expr))
bbox, marker_trans, sym, *detector_, scale_factor_);
vertex_converter<box2d<double>, dispatch_type, markers_symbolizer,
CoordTransform, proj_transform, agg::trans_affine, conv_types>
converter(query_extent_* 1.1,rasterizer_dispatch, sym,t_,prj_trans,tr,scale_factor_);
if (sym.clip()) converter.template set<clip_line_tag>(); //optional clip (default: true)
converter.template set<transform_tag>(); //always transform
if (sym.smooth() > 0.0) converter.template set<smooth_tag>(); // optional smooth converter
BOOST_FOREACH(geometry_type & geom, feature.paths())
{ {
converter.apply(geom); svg_storage_type marker_ellipse;
vertex_stl_adapter<svg_path_storage> stl_storage(marker_ellipse.source());
svg_path_adapter svg_path(stl_storage);
build_ellipse(sym, feature, marker_ellipse, svg_path);
svg_attribute_type attributes;
bool result = push_explicit_style( (*stock_vector_marker)->attributes(), attributes, sym);
svg_renderer_type svg_renderer(svg_path, result ? attributes : (*stock_vector_marker)->attributes());
evaluate_transform(tr, feature, sym.get_image_transform());
box2d<double> bbox = marker_ellipse.bounding_box();
coord2d center = bbox.center();
agg::trans_affine_translation recenter(-center.x, -center.y);
agg::trans_affine marker_trans = recenter * tr;
dispatch_type rasterizer_dispatch(*current_buffer_,svg_renderer,*ras_ptr,
bbox, marker_trans, sym, *detector_, scale_factor_);
vertex_converter<box2d<double>, dispatch_type, markers_symbolizer,
CoordTransform, proj_transform, agg::trans_affine, conv_types>
converter(query_extent_, rasterizer_dispatch, sym,t_,prj_trans,tr,scale_factor_);
if (sym.clip() && feature.paths().size() > 0) // optional clip (default: true)
{
eGeomType type = feature.paths()[0].type();
if (type == Polygon)
converter.template set<clip_poly_tag>();
else if (type == LineString)
converter.template set<clip_line_tag>();
// don't clip if type==Point
}
converter.template set<transform_tag>(); //always transform
if (sym.smooth() > 0.0) converter.template set<smooth_tag>(); // optional smooth converter
BOOST_FOREACH(geometry_type & geom, feature.paths())
{
converter.apply(geom);
}
}
else
{
box2d<double> const& bbox = (*mark)->bounding_box();
setup_label_transform(tr, bbox, feature, sym);
coord2d center = bbox.center();
agg::trans_affine_translation recenter(-center.x, -center.y);
agg::trans_affine marker_trans = recenter * tr;
vertex_stl_adapter<svg_path_storage> stl_storage((*stock_vector_marker)->source());
svg_path_adapter svg_path(stl_storage);
svg_attribute_type attributes;
bool result = push_explicit_style( (*stock_vector_marker)->attributes(), attributes, sym);
svg_renderer_type svg_renderer(svg_path, result ? attributes : (*stock_vector_marker)->attributes());
dispatch_type rasterizer_dispatch(*current_buffer_,svg_renderer,*ras_ptr,
bbox, marker_trans, sym, *detector_, scale_factor_);
vertex_converter<box2d<double>, dispatch_type, markers_symbolizer,
CoordTransform, proj_transform, agg::trans_affine, conv_types>
converter(query_extent_, rasterizer_dispatch, sym,t_,prj_trans,tr,scale_factor_);
if (sym.clip() && feature.paths().size() > 0) // optional clip (default: true)
{
eGeomType type = feature.paths()[0].type();
if (type == Polygon)
converter.template set<clip_poly_tag>();
else if (type == LineString)
converter.template set<clip_line_tag>();
// don't clip if type==Point
}
converter.template set<transform_tag>(); //always transform
if (sym.smooth() > 0.0) converter.template set<smooth_tag>(); // optional smooth converter
BOOST_FOREACH(geometry_type & geom, feature.paths())
{
converter.apply(geom);
}
} }
} }
else // raster markers else // raster markers
{ {
box2d<double> const& bbox = (*mark)->bounding_box();
setup_label_transform(tr, bbox, feature, sym);
coord2d center = bbox.center();
agg::trans_affine_translation recenter(-center.x, -center.y);
agg::trans_affine marker_trans = recenter * tr;
boost::optional<mapnik::image_ptr> marker = (*mark)->get_bitmap_data(); boost::optional<mapnik::image_ptr> marker = (*mark)->get_bitmap_data();
typedef raster_markers_rasterizer_dispatch<buffer_type,rasterizer, detector_type> dispatch_type; typedef raster_markers_rasterizer_dispatch<buffer_type,rasterizer, detector_type> dispatch_type;
dispatch_type rasterizer_dispatch(*current_buffer_,*ras_ptr, **marker, dispatch_type rasterizer_dispatch(*current_buffer_,*ras_ptr, **marker,
marker_trans, sym, *detector_, scale_factor_); marker_trans, sym, *detector_, scale_factor_);
vertex_converter<box2d<double>, dispatch_type, markers_symbolizer, vertex_converter<box2d<double>, dispatch_type, markers_symbolizer,
CoordTransform, proj_transform, agg::trans_affine, conv_types> CoordTransform, proj_transform, agg::trans_affine, conv_types>
converter(query_extent_* 1.1, rasterizer_dispatch, sym,t_,prj_trans,tr,scale_factor_); converter(query_extent_, rasterizer_dispatch, sym,t_,prj_trans,tr,scale_factor_);
if (sym.clip()) converter.template set<clip_line_tag>(); //optional clip (default: true) if (sym.clip() && feature.paths().size() > 0) // optional clip (default: true)
{
eGeomType type = feature.paths()[0].type();
if (type == Polygon)
converter.template set<clip_poly_tag>();
else if (type == LineString)
converter.template set<clip_line_tag>();
// don't clip if type==Point
}
converter.template set<transform_tag>(); //always transform converter.template set<transform_tag>(); //always transform
if (sym.smooth() > 0.0) converter.template set<smooth_tag>(); // optional smooth converter if (sym.smooth() > 0.0) converter.template set<smooth_tag>(); // optional smooth converter

View file

@ -23,19 +23,16 @@
// mapnik // mapnik
#include <mapnik/agg_renderer.hpp> #include <mapnik/agg_renderer.hpp>
#include <mapnik/agg_rasterizer.hpp> #include <mapnik/agg_rasterizer.hpp>
#include <mapnik/geom_util.hpp>
#include <mapnik/image_util.hpp> #include <mapnik/image_util.hpp>
#include <mapnik/metawriter.hpp>
#include <mapnik/geom_util.hpp>
#include <mapnik/point_symbolizer.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/marker.hpp> #include <mapnik/marker.hpp>
#include <mapnik/marker_cache.hpp> #include <mapnik/marker_cache.hpp>
#include <mapnik/expression_evaluator.hpp>
// agg // agg
#include "agg_basics.h" #include "agg_trans_affine.h"
#include "agg_rendering_buffer.h"
#include "agg_pixfmt_rgba.h"
#include "agg_rasterizer_scanline_aa.h"
#include "agg_scanline_u.h"
// stl // stl
#include <string> #include <string>
@ -89,7 +86,6 @@ void agg_renderer<T>::process(point_symbolizer const& sym,
prj_trans.backward(x,y,z); prj_trans.backward(x,y,z);
t_.forward(&x,&y); t_.forward(&x,&y);
label_ext.re_center(x,y); label_ext.re_center(x,y);
if (sym.get_allow_overlap() || if (sym.get_allow_overlap() ||
detector_->has_placement(label_ext)) detector_->has_placement(label_ext))
{ {
@ -106,8 +102,8 @@ void agg_renderer<T>::process(point_symbolizer const& sym,
if (!sym.get_ignore_placement()) if (!sym.get_ignore_placement())
detector_->insert(label_ext); detector_->insert(label_ext);
metawriter_with_properties writer = sym.get_metawriter(); //metawriter_with_properties writer = sym.get_metawriter();
if (writer.first) writer.first->add_box(label_ext, feature, t_, writer.second); //if (writer.first) writer.first->add_box(label_ext, feature, t_, writer.second);
} }
} }
} }

View file

@ -139,11 +139,10 @@ void agg_renderer<T>::process(polygon_pattern_symbolizer const& sym,
agg::trans_affine tr; agg::trans_affine tr;
evaluate_transform(tr, feature, sym.get_transform()); evaluate_transform(tr, feature, sym.get_transform());
box2d<double> inflated_extent = query_extent_ * 1.0;
typedef boost::mpl::vector<clip_poly_tag,transform_tag,smooth_tag> conv_types; typedef boost::mpl::vector<clip_poly_tag,transform_tag,smooth_tag> conv_types;
vertex_converter<box2d<double>, rasterizer, polygon_pattern_symbolizer, vertex_converter<box2d<double>, rasterizer, polygon_pattern_symbolizer,
CoordTransform, proj_transform, agg::trans_affine, conv_types> CoordTransform, proj_transform, agg::trans_affine, conv_types>
converter(inflated_extent,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_); converter(query_extent_,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_);
if (sym.clip()) converter.set<clip_poly_tag>(); //optional clip (default: true) if (sym.clip()) converter.set<clip_poly_tag>(); //optional clip (default: true)
converter.set<transform_tag>(); //always transform converter.set<transform_tag>(); //always transform

View file

@ -48,15 +48,13 @@ void agg_renderer<T>::process(polygon_symbolizer const& sym,
ras_ptr->reset(); ras_ptr->reset();
set_gamma_method(sym,ras_ptr); set_gamma_method(sym,ras_ptr);
box2d<double> inflated_extent = query_extent_ * 1.0;
agg::trans_affine tr; agg::trans_affine tr;
evaluate_transform(tr, feature, sym.get_transform()); evaluate_transform(tr, feature, sym.get_transform());
typedef boost::mpl::vector<clip_poly_tag,transform_tag,affine_transform_tag,smooth_tag> conv_types; typedef boost::mpl::vector<clip_poly_tag,transform_tag,affine_transform_tag,smooth_tag> conv_types;
vertex_converter<box2d<double>, rasterizer, polygon_symbolizer, vertex_converter<box2d<double>, rasterizer, polygon_symbolizer,
CoordTransform, proj_transform, agg::trans_affine, conv_types> CoordTransform, proj_transform, agg::trans_affine, conv_types>
converter(inflated_extent,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_); converter(query_extent_,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_);
if (sym.clip()) converter.set<clip_poly_tag>(); //optional clip (default: true) if (sym.clip()) converter.set<clip_poly_tag>(); //optional clip (default: true)
converter.set<transform_tag>(); //always transform converter.set<transform_tag>(); //always transform
@ -72,7 +70,7 @@ void agg_renderer<T>::process(polygon_symbolizer const& sym,
} }
agg::rendering_buffer buf(current_buffer_->raw_data(),width_,height_, width_ * 4); agg::rendering_buffer buf(current_buffer_->raw_data(),width_,height_, width_ * 4);
color const& fill = sym.get_fill(); color const& fill = sym.get_fill();
unsigned r=fill.red(); unsigned r=fill.red();
unsigned g=fill.green(); unsigned g=fill.green();

View file

@ -171,7 +171,6 @@ source = Split(
json/feature_grammar.cpp json/feature_grammar.cpp
json/feature_collection_parser.cpp json/feature_collection_parser.cpp
json/geojson_generator.cpp json/geojson_generator.cpp
markers_placement.cpp
formatting/base.cpp formatting/base.cpp
formatting/expression.cpp formatting/expression.cpp
formatting/list.cpp formatting/list.cpp

View file

@ -497,7 +497,6 @@ public:
} }
else if (cm == SEG_CLOSE) else if (cm == SEG_CLOSE)
{ {
line_to(x, y);
close_path(); close_path();
} }
} }
@ -882,43 +881,32 @@ void cairo_renderer_base::process(building_symbolizer const& sym,
boost::scoped_ptr<geometry_type> frame(new geometry_type(LineString)); boost::scoped_ptr<geometry_type> frame(new geometry_type(LineString));
boost::scoped_ptr<geometry_type> roof(new geometry_type(Polygon)); boost::scoped_ptr<geometry_type> roof(new geometry_type(Polygon));
std::deque<segment_t> face_segments; std::deque<segment_t> face_segments;
double x0(0); double x0 = 0;
double y0(0); double y0 = 0;
double x, y;
geom.rewind(0); geom.rewind(0);
unsigned cm = geom.vertex(&x0, &y0); for (unsigned cm = geom.vertex(&x, &y); cm != SEG_END;
cm = geom.vertex(&x, &y))
for (unsigned j = 1; j < geom.size(); ++j)
{ {
double x=0;
double y=0;
cm = geom.vertex(&x,&y);
if (cm == SEG_MOVETO) if (cm == SEG_MOVETO)
{ {
frame->move_to(x,y); frame->move_to(x,y);
} }
else if (cm == SEG_LINETO) else if (cm == SEG_LINETO || cm == SEG_CLOSE)
{ {
frame->line_to(x,y); frame->line_to(x,y);
face_segments.push_back(segment_t(x0,y0,x,y));
} }
if (j != 0)
{
face_segments.push_back(segment_t(x0, y0, x, y));
}
x0 = x; x0 = x;
y0 = y; y0 = y;
} }
std::sort(face_segments.begin(), face_segments.end(), y_order); std::sort(face_segments.begin(), face_segments.end(), y_order);
std::deque<segment_t>::const_iterator itr = face_segments.begin(); std::deque<segment_t>::const_iterator itr = face_segments.begin();
for (; itr != face_segments.end(); ++itr) std::deque<segment_t>::const_iterator end=face_segments.end();
for (; itr != end; ++itr)
{ {
boost::scoped_ptr<geometry_type> faces(new geometry_type(Polygon)); boost::scoped_ptr<geometry_type> faces(new geometry_type(Polygon));
faces->move_to(itr->get<0>(), itr->get<1>()); faces->move_to(itr->get<0>(), itr->get<1>());
faces->line_to(itr->get<2>(), itr->get<3>()); faces->line_to(itr->get<2>(), itr->get<3>());
faces->line_to(itr->get<2>(), itr->get<3>() + height); faces->line_to(itr->get<2>(), itr->get<3>() + height);
@ -935,20 +923,18 @@ void cairo_renderer_base::process(building_symbolizer const& sym,
} }
geom.rewind(0); geom.rewind(0);
for (unsigned j = 0; j < geom.size(); ++j) for (unsigned cm = geom.vertex(&x, &y); cm != SEG_END;
cm = geom.vertex(&x, &y))
{ {
double x, y;
unsigned cm = geom.vertex(&x, &y);
if (cm == SEG_MOVETO) if (cm == SEG_MOVETO)
{ {
frame->move_to(x, y + height); frame->move_to(x,y+height);
roof->move_to(x, y + height); roof->move_to(x,y+height);
} }
else if (cm == SEG_LINETO) else if (cm == SEG_LINETO || cm == SEG_CLOSE)
{ {
frame->line_to(x, y + height); frame->line_to(x,y+height);
roof->line_to(x, y + height); roof->line_to(x,y+height);
} }
} }
@ -987,14 +973,21 @@ void cairo_renderer_base::process(line_symbolizer const& sym,
evaluate_transform(tr, feature, sym.get_transform()); evaluate_transform(tr, feature, sym.get_transform());
box2d<double> ext = query_extent_ * 1.1; box2d<double> ext = query_extent_ * 1.1;
typedef boost::mpl::vector<clip_line_tag,transform_tag, offset_transform_tag, affine_transform_tag, smooth_tag> conv_types; typedef boost::mpl::vector<clip_line_tag, clip_poly_tag, transform_tag, offset_transform_tag, affine_transform_tag, smooth_tag> conv_types;
vertex_converter<box2d<double>, cairo_context, line_symbolizer, vertex_converter<box2d<double>, cairo_context, line_symbolizer,
CoordTransform, proj_transform, agg::trans_affine, conv_types> CoordTransform, proj_transform, agg::trans_affine, conv_types>
converter(ext,context,sym,t_,prj_trans,tr,scale_factor_); converter(ext,context,sym,t_,prj_trans,tr,scale_factor_);
if (sym.clip()) converter.set<clip_line_tag>(); // optional clip (default: true) if (sym.clip() && feature.paths().size() > 0) // optional clip (default: true)
{
eGeomType type = feature.paths()[0].type();
if (type == Polygon)
converter.set<clip_poly_tag>();
else if (type == LineString)
converter.set<clip_line_tag>();
// don't clip if type==Point
}
converter.set<transform_tag>(); // always transform converter.set<transform_tag>(); // always transform
if (fabs(sym.offset()) > 0.0) converter.set<offset_transform_tag>(); // parallel offset if (fabs(sym.offset()) > 0.0) converter.set<offset_transform_tag>(); // parallel offset
converter.set<affine_transform_tag>(); // optional affine transform converter.set<affine_transform_tag>(); // optional affine transform
if (sym.smooth() > 0.0) converter.set<smooth_tag>(); // optional smooth converter if (sym.smooth() > 0.0) converter.set<smooth_tag>(); // optional smooth converter
@ -1035,7 +1028,7 @@ void cairo_renderer_base::render_marker(pixel_position const& pos, marker const&
typedef coord_transform<CoordTransform,geometry_type> path_type; typedef coord_transform<CoordTransform,geometry_type> path_type;
agg::trans_affine transform; agg::trans_affine transform;
mapnik::path_ptr vmarker = *marker.get_vector_data(); mapnik::svg_path_ptr vmarker = *marker.get_vector_data();
using namespace mapnik::svg; using namespace mapnik::svg;
agg::pod_bvector<path_attributes> const & attributes_ = vmarker->attributes(); agg::pod_bvector<path_attributes> const & attributes_ = vmarker->attributes();
for(unsigned i = 0; i < attributes_.size(); ++i) for(unsigned i = 0; i < attributes_.size(); ++i)
@ -1411,7 +1404,7 @@ void cairo_renderer_base::process(markers_symbolizer const& sym,
return; return;
} }
boost::optional<path_ptr> marker = (*mark)->get_vector_data(); boost::optional<svg_path_ptr> marker = (*mark)->get_vector_data();
box2d<double> const& bbox = (*marker)->bounding_box(); box2d<double> const& bbox = (*marker)->bounding_box();
double x1 = bbox.minx(); double x1 = bbox.minx();
double y1 = bbox.miny(); double y1 = bbox.miny();
@ -1443,7 +1436,7 @@ void cairo_renderer_base::process(markers_symbolizer const& sym,
if (sym.get_allow_overlap() || if (sym.get_allow_overlap() ||
detector_.has_placement(extent)) detector_.has_placement(extent))
{ {
render_marker(pixel_position(x - 0.5 * w, y - 0.5 * h) ,**mark, tr, sym.get_opacity()); render_marker(pixel_position(x - 0.5 * w, y - 0.5 * h) ,**mark, tr, 1);
if (!sym.get_ignore_placement()) if (!sym.get_ignore_placement())
detector_.insert(extent); detector_.insert(extent);
@ -1462,7 +1455,7 @@ void cairo_renderer_base::process(markers_symbolizer const& sym,
while (placement.get_point(x, y, angle)) while (placement.get_point(x, y, angle))
{ {
agg::trans_affine matrix = recenter * tr * agg::trans_affine_rotation(angle) * agg::trans_affine_translation(x, y); agg::trans_affine matrix = recenter * tr * agg::trans_affine_rotation(angle) * agg::trans_affine_translation(x, y);
render_marker(pixel_position(x - 0.5 * w, y - 0.5 * h), **mark, matrix, sym.get_opacity(),false); render_marker(pixel_position(x - 0.5 * w, y - 0.5 * h), **mark, matrix, 1,false);
} }
} }
context.fill(); context.fill();

View file

@ -102,35 +102,35 @@ struct feature_style_processor<Processor>::symbol_dispatch : public boost::stati
: output_(output), : output_(output),
f_(f), f_(f),
prj_trans_(prj_trans) {} prj_trans_(prj_trans) {}
template <typename T> template <typename T>
void operator () (T const& sym) const void operator () (T const& sym) const
{ {
process_impl<has_process<Processor,T>::value>::process(output_,sym,f_,prj_trans_); process_impl<has_process<Processor,T>::value>::process(output_,sym,f_,prj_trans_);
} }
Processor & output_; Processor & output_;
mapnik::feature_impl & f_; mapnik::feature_impl & f_;
proj_transform const& prj_trans_; proj_transform const& prj_trans_;
}; };
typedef char (&no_tag)[1]; typedef char (&no_tag)[1];
typedef char (&yes_tag)[2]; typedef char (&yes_tag)[2];
template <typename T0, typename T1, void (T0::*)(T1 const&, mapnik::feature_impl &, proj_transform const&) > template <typename T0, typename T1, void (T0::*)(T1 const&, mapnik::feature_impl &, proj_transform const&) >
struct process_memfun_helper {}; struct process_memfun_helper {};
template <typename T0, typename T1> no_tag has_process_helper(...); template <typename T0, typename T1> no_tag has_process_helper(...);
template <typename T0, typename T1> yes_tag has_process_helper(process_memfun_helper<T0, T1, &T0::process>* p); template <typename T0, typename T1> yes_tag has_process_helper(process_memfun_helper<T0, T1, &T0::process>* p);
template<typename T0,typename T1> template<typename T0,typename T1>
struct has_process struct has_process
{ {
typedef typename T0::processor_impl_type processor_impl_type; typedef typename T0::processor_impl_type processor_impl_type;
BOOST_STATIC_CONSTANT(bool BOOST_STATIC_CONSTANT(bool
, value = sizeof(has_process_helper<processor_impl_type,T1>(0)) == sizeof(yes_tag) , value = sizeof(has_process_helper<processor_impl_type,T1>(0)) == sizeof(yes_tag)
); );
}; };
template <typename Processor> template <typename Processor>
@ -321,9 +321,11 @@ void feature_style_processor<Processor>::apply_to_layer(layer const& lay, Proces
// if we've got this far, now prepare the unbuffered extent // if we've got this far, now prepare the unbuffered extent
// which is used as a bbox for clipping geometries // which is used as a bbox for clipping geometries
box2d<double> query_ext = m_.get_current_extent(); // unbuffered box2d<double> query_ext = m_.get_current_extent(); // unbuffered
if (maximum_extent) { if (maximum_extent)
{
query_ext.clip(*maximum_extent); query_ext.clip(*maximum_extent);
} }
box2d<double> layer_ext2 = lay.envelope(); box2d<double> layer_ext2 = lay.envelope();
if (fw_success) if (fw_success)
{ {
@ -351,8 +353,8 @@ void feature_style_processor<Processor>::apply_to_layer(layer const& lay, Proces
query q(layer_ext,res,scale_denom,m_.get_current_extent()); query q(layer_ext,res,scale_denom,m_.get_current_extent());
std::vector<feature_type_style*> active_styles; std::vector<feature_type_style*> active_styles;
attribute_collector collector(names); attribute_collector collector(names);
double filt_factor = 1; double filt_factor = 1.0;
directive_collector d_collector(&filt_factor); directive_collector d_collector(filt_factor);
// iterate through all named styles collecting active styles and attribute names // iterate through all named styles collecting active styles and attribute names
BOOST_FOREACH(std::string const& style_name, style_names) BOOST_FOREACH(std::string const& style_name, style_names)
@ -520,7 +522,7 @@ void feature_style_processor<Processor>::render_style(
{ {
p.start_style_processing(*style); p.start_style_processing(*style);
#if defined(RENDERING_STATS) #if defined(RENDERING_STATS)
std::ostringstream s1; std::ostringstream s1;
s1 << "rendering style for layer: '" << lay.name() s1 << "rendering style for layer: '" << lay.name()
@ -662,4 +664,3 @@ template class feature_style_processor<grid_renderer<grid> >;
template class feature_style_processor<agg_renderer<image_32> >; template class feature_style_processor<agg_renderer<image_32> >;
} }

View file

@ -21,14 +21,15 @@
*****************************************************************************/ *****************************************************************************/
// mapnik // mapnik
#include <mapnik/layer.hpp>
#include <mapnik/feature_type_style.hpp>
#include <mapnik/debug.hpp>
#include <mapnik/grid/grid_rasterizer.hpp> #include <mapnik/grid/grid_rasterizer.hpp>
#include <mapnik/grid/grid_renderer.hpp> #include <mapnik/grid/grid_renderer.hpp>
#include <mapnik/grid/grid_pixfmt.hpp> #include <mapnik/grid/grid_pixfmt.hpp>
#include <mapnik/grid/grid_pixel.hpp> #include <mapnik/grid/grid_pixel.hpp>
#include <mapnik/grid/grid.hpp> #include <mapnik/grid/grid.hpp>
#include <mapnik/debug.hpp>
#include <mapnik/layer.hpp>
#include <mapnik/feature_type_style.hpp>
#include <mapnik/marker.hpp> #include <mapnik/marker.hpp>
#include <mapnik/marker_cache.hpp> #include <mapnik/marker_cache.hpp>
#include <mapnik/unicode.hpp> #include <mapnik/unicode.hpp>
@ -56,13 +57,22 @@ grid_renderer<T>::grid_renderer(Map const& m, T & pixmap, double scale_factor, u
width_(pixmap_.width()), width_(pixmap_.width()),
height_(pixmap_.height()), height_(pixmap_.height()),
scale_factor_(scale_factor), scale_factor_(scale_factor),
// NOTE: can change this to m dims instead of pixmap_ if render-time
// resolution support is dropped from grid_renderer python interface
t_(pixmap_.width(),pixmap_.height(),m.get_current_extent(),offset_x,offset_y), t_(pixmap_.width(),pixmap_.height(),m.get_current_extent(),offset_x,offset_y),
font_engine_(), font_engine_(),
font_manager_(font_engine_), font_manager_(font_engine_),
detector_(box2d<double>(-m.buffer_size(), -m.buffer_size(), pixmap_.width() + m.buffer_size(), pixmap_.height() + m.buffer_size())), detector_(boost::make_shared<label_collision_detector4>(box2d<double>(-m.buffer_size(), -m.buffer_size(), m.width() + m.buffer_size() ,m.height() + m.buffer_size()))),
ras_ptr(new grid_rasterizer) ras_ptr(new grid_rasterizer)
{
setup(m);
}
template <typename T>
void grid_renderer<T>::setup(Map const& m)
{ {
MAPNIK_LOG_DEBUG(grid_renderer) << "grid_renderer: Scale=" << m.scale(); MAPNIK_LOG_DEBUG(grid_renderer) << "grid_renderer: Scale=" << m.scale();
// nothing to do for grids yet on setup
} }
template <typename T> template <typename T>
@ -91,9 +101,25 @@ void grid_renderer<T>::start_layer_processing(layer const& lay, box2d<double> co
if (lay.clear_label_cache()) if (lay.clear_label_cache())
{ {
detector_.clear(); detector_->clear();
} }
query_extent_ = query_extent; query_extent_ = query_extent;
int buffer_size = lay.buffer_size();
if (buffer_size != 0 )
{
double padding = buffer_size * (double)(query_extent.width()/pixmap_.width());
double x0 = query_extent_.minx();
double y0 = query_extent_.miny();
double x1 = query_extent_.maxx();
double y1 = query_extent_.maxy();
query_extent_.init(x0 - padding, y0 - padding, x1 + padding , y1 + padding);
}
boost::optional<box2d<double> > const& maximum_extent = lay.maximum_extent();
if (maximum_extent)
{
query_extent_.clip(*maximum_extent);
}
} }
template <typename T> template <typename T>
@ -144,21 +170,27 @@ void grid_renderer<T>::render_marker(mapnik::feature_impl & feature, unsigned in
else else
{ {
image_data_32 const& data = **marker.get_bitmap_data(); image_data_32 const& data = **marker.get_bitmap_data();
if (step == 1 && scale_factor_ == 1.0) double width = data.width();
double height = data.height();
double cx = 0.5 * width;
double cy = 0.5 * height;
if (step == 1 && (std::fabs(1.0 - scale_factor_) < 0.001 && tr.is_identity()))
{ {
// TODO - support opacity
pixmap_.set_rectangle(feature.id(), data, pixmap_.set_rectangle(feature.id(), data,
boost::math::iround(pos.x), boost::math::iround(pos.x - cx),
boost::math::iround(pos.y)); boost::math::iround(pos.y - cy));
} }
else else
{ {
// TODO - remove support for step != or add support for agg scaling with opacity
double ratio = (1.0/step); double ratio = (1.0/step);
image_data_32 target(ratio * data.width(), ratio * data.height()); image_data_32 target(ratio * data.width(), ratio * data.height());
mapnik::scale_image_agg<image_data_32>(target,data, SCALING_NEAR, mapnik::scale_image_agg<image_data_32>(target,data, SCALING_NEAR,
scale_factor_, 0.0, 0.0, 1.0, ratio); scale_factor_, 0.0, 0.0, 1.0, ratio);
pixmap_.set_rectangle(feature.id(), target, pixmap_.set_rectangle(feature.id(), target,
boost::math::iround(pos.x), boost::math::iround(pos.x - cx),
boost::math::iround(pos.y)); boost::math::iround(pos.y - cy));
} }
} }
pixmap_.add_feature(feature); pixmap_.add_feature(feature);

View file

@ -20,22 +20,55 @@
* *
*****************************************************************************/ *****************************************************************************/
/*
porting notes -->
- grid includes
- detector
- no gamma
- mapnik::pixfmt_gray32
- agg::scanline_bin sl
- grid_rendering_buffer
- agg::renderer_scanline_bin_solid
- clamping:
// - clamp sizes to > 4 pixels of interactivity
if (tr.scale() < 0.5)
{
agg::trans_affine tr2;
tr2 *= agg::trans_affine_scaling(0.5);
tr = tr2;
}
tr *= agg::trans_affine_scaling(scale_factor_*(1.0/pixmap_.get_resolution()));
- svg_renderer.render_id
- only encode feature if placements are found:
if (placed)
{
pixmap_.add_feature(feature);
}
*/
// mapnik // mapnik
#include <mapnik/debug.hpp>
#include <mapnik/grid/grid_rasterizer.hpp> #include <mapnik/grid/grid_rasterizer.hpp>
#include <mapnik/grid/grid_renderer.hpp> #include <mapnik/grid/grid_renderer.hpp>
#include <mapnik/grid/grid_pixfmt.hpp> #include <mapnik/grid/grid_pixfmt.hpp>
#include <mapnik/grid/grid_pixel.hpp> #include <mapnik/grid/grid_pixel.hpp>
#include <mapnik/grid/grid.hpp> #include <mapnik/grid/grid.hpp>
#include <mapnik/debug.hpp>
#include <mapnik/geom_util.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/vertex_converters.hpp>
#include <mapnik/marker.hpp> #include <mapnik/marker.hpp>
#include <mapnik/marker_cache.hpp> #include <mapnik/marker_cache.hpp>
#include <mapnik/marker_helpers.hpp> #include <mapnik/marker_helpers.hpp>
#include <mapnik/markers_symbolizer.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/marker_cache.hpp>
#include <mapnik/svg/svg_renderer.hpp> #include <mapnik/svg/svg_renderer.hpp>
#include <mapnik/svg/svg_storage.hpp>
#include <mapnik/svg/svg_path_adapter.hpp> #include <mapnik/svg/svg_path_adapter.hpp>
#include <mapnik/svg/svg_path_attributes.hpp>
#include <mapnik/markers_placement.hpp> #include <mapnik/markers_placement.hpp>
#include <mapnik/markers_symbolizer.hpp>
// agg // agg
#include "agg_basics.h" #include "agg_basics.h"
@ -46,6 +79,11 @@
#include "agg_path_storage.h" #include "agg_path_storage.h"
#include "agg_conv_clip_polyline.h" #include "agg_conv_clip_polyline.h"
#include "agg_conv_transform.h" #include "agg_conv_transform.h"
#include "agg_image_filters.h"
#include "agg_trans_bilinear.h"
#include "agg_span_allocator.h"
#include "agg_image_accessors.h"
#include "agg_span_image_filter_rgba.h"
// boost // boost
#include <boost/optional.hpp> #include <boost/optional.hpp>
@ -87,7 +125,7 @@ void grid_renderer<T>::process(markers_symbolizer const& sym,
agg::trans_affine geom_tr; agg::trans_affine geom_tr;
evaluate_transform(geom_tr, feature, sym.get_transform()); evaluate_transform(geom_tr, feature, sym.get_transform());
boost::optional<path_ptr> marker = (*mark)->get_vector_data(); boost::optional<svg_path_ptr> marker = (*mark)->get_vector_data();
box2d<double> const& bbox = (*marker)->bounding_box(); box2d<double> const& bbox = (*marker)->bounding_box();
agg::trans_affine tr; agg::trans_affine tr;
@ -137,12 +175,12 @@ void grid_renderer<T>::process(markers_symbolizer const& sym,
box2d<double> transformed_bbox = bbox * matrix; box2d<double> transformed_bbox = bbox * matrix;
if (sym.get_allow_overlap() || if (sym.get_allow_overlap() ||
detector_.has_placement(transformed_bbox)) detector_->has_placement(transformed_bbox))
{ {
placed = true; placed = true;
svg_renderer.render_id(*ras_ptr, sl, renb, feature.id(), matrix, sym.get_opacity(), bbox); svg_renderer.render_id(*ras_ptr, sl, renb, feature.id(), matrix, 1, bbox);
if (!sym.get_ignore_placement()) if (!sym.get_ignore_placement())
detector_.insert(transformed_bbox); detector_->insert(transformed_bbox);
} }
} }
else if (sym.clip()) else if (sym.clip())
@ -155,7 +193,7 @@ void grid_renderer<T>::process(markers_symbolizer const& sym,
clipped.clip_box(query_extent_.minx(),query_extent_.miny(),query_extent_.maxx(),query_extent_.maxy()); clipped.clip_box(query_extent_.minx(),query_extent_.miny(),query_extent_.maxx(),query_extent_.maxy());
path_type path(t_,clipped,prj_trans); path_type path(t_,clipped,prj_trans);
transformed_path_type path_transformed(path,geom_tr); transformed_path_type path_transformed(path,geom_tr);
markers_placement<transformed_path_type, label_collision_detector4> placement(path_transformed, bbox, marker_trans, detector_, markers_placement<transformed_path_type, label_collision_detector4> placement(path_transformed, bbox, marker_trans, *detector_,
sym.get_spacing() * scale_factor_, sym.get_spacing() * scale_factor_,
sym.get_max_error(), sym.get_max_error(),
sym.get_allow_overlap()); sym.get_allow_overlap());
@ -166,7 +204,7 @@ void grid_renderer<T>::process(markers_symbolizer const& sym,
agg::trans_affine matrix = marker_trans; agg::trans_affine matrix = marker_trans;
matrix.rotate(angle); matrix.rotate(angle);
matrix.translate(x, y); matrix.translate(x, y);
svg_renderer.render_id(*ras_ptr, sl, renb, feature.id(), matrix, sym.get_opacity(), bbox); svg_renderer.render_id(*ras_ptr, sl, renb, feature.id(), matrix, 1, bbox);
} }
} }
else else
@ -175,7 +213,7 @@ void grid_renderer<T>::process(markers_symbolizer const& sym,
typedef agg::conv_transform<path_type, agg::trans_affine> transformed_path_type; typedef agg::conv_transform<path_type, agg::trans_affine> transformed_path_type;
path_type path(t_,geom,prj_trans); path_type path(t_,geom,prj_trans);
transformed_path_type path_transformed(path,geom_tr); transformed_path_type path_transformed(path,geom_tr);
markers_placement<transformed_path_type, label_collision_detector4> placement(path_transformed, bbox, marker_trans, detector_, markers_placement<transformed_path_type, label_collision_detector4> placement(path_transformed, bbox, marker_trans, *detector_,
sym.get_spacing() * scale_factor_, sym.get_spacing() * scale_factor_,
sym.get_max_error(), sym.get_max_error(),
sym.get_allow_overlap()); sym.get_allow_overlap());
@ -186,7 +224,7 @@ void grid_renderer<T>::process(markers_symbolizer const& sym,
agg::trans_affine matrix = marker_trans; agg::trans_affine matrix = marker_trans;
matrix.rotate(angle); matrix.rotate(angle);
matrix.translate(x, y); matrix.translate(x, y);
svg_renderer.render_id(*ras_ptr, sl, renb, feature.id(), matrix, sym.get_opacity(), bbox); svg_renderer.render_id(*ras_ptr, sl, renb, feature.id(), matrix, 1, bbox);
} }
} }
} }

View file

@ -26,15 +26,22 @@
#include <mapnik/grid/grid_pixfmt.hpp> #include <mapnik/grid/grid_pixfmt.hpp>
#include <mapnik/grid/grid_pixel.hpp> #include <mapnik/grid/grid_pixel.hpp>
#include <mapnik/grid/grid.hpp> #include <mapnik/grid/grid.hpp>
#include <mapnik/geom_util.hpp> #include <mapnik/geom_util.hpp>
#include <mapnik/point_symbolizer.hpp> #include <mapnik/point_symbolizer.hpp>
#include <mapnik/expression_evaluator.hpp> #include <mapnik/expression_evaluator.hpp>
#include <mapnik/marker.hpp> #include <mapnik/marker.hpp>
#include <mapnik/marker_cache.hpp> #include <mapnik/marker_cache.hpp>
// agg
#include "agg_trans_affine.h"
// stl // stl
#include <string> #include <string>
// boost
#include <boost/make_shared.hpp>
namespace mapnik { namespace mapnik {
template <typename T> template <typename T>
@ -57,14 +64,14 @@ void grid_renderer<T>::process(point_symbolizer const& sym,
if (marker) if (marker)
{ {
box2d<double> const& bbox = (*marker)->bounding_box(); box2d<double> const& bbox = (*marker)->bounding_box();
coord2d const center = bbox.center(); coord2d center = bbox.center();
agg::trans_affine tr; agg::trans_affine tr;
evaluate_transform(tr, feature, sym.get_image_transform()); evaluate_transform(tr, feature, sym.get_image_transform());
tr = agg::trans_affine_scaling(scale_factor_) * tr; tr = agg::trans_affine_scaling(scale_factor_) * tr;
agg::trans_affine_translation const recenter(-center.x, -center.y); agg::trans_affine_translation recenter(-center.x, -center.y);
agg::trans_affine const recenter_tr = recenter * tr; agg::trans_affine recenter_tr = recenter * tr;
box2d<double> label_ext = bbox * recenter_tr; box2d<double> label_ext = bbox * recenter_tr;
for (unsigned i=0; i<feature.num_geometries(); ++i) for (unsigned i=0; i<feature.num_geometries(); ++i)
@ -82,7 +89,7 @@ void grid_renderer<T>::process(point_symbolizer const& sym,
t_.forward(&x,&y); t_.forward(&x,&y);
label_ext.re_center(x,y); label_ext.re_center(x,y);
if (sym.get_allow_overlap() || if (sym.get_allow_overlap() ||
detector_.has_placement(label_ext)) detector_->has_placement(label_ext))
{ {
render_marker(feature, render_marker(feature,
@ -94,7 +101,7 @@ void grid_renderer<T>::process(point_symbolizer const& sym,
sym.comp_op()); sym.comp_op());
if (!sym.get_ignore_placement()) if (!sym.get_ignore_placement())
detector_.insert(label_ext); detector_->insert(label_ext);
} }
} }
} }

View file

@ -47,7 +47,7 @@ void grid_renderer<T>::process(shield_symbolizer const& sym,
sym, feature, prj_trans, sym, feature, prj_trans,
width_, height_, width_, height_,
scale_factor_, scale_factor_,
t_, font_manager_, detector_, t_, font_manager_, *detector_,
query_extent_); query_extent_);
bool placement_found = false; bool placement_found = false;

View file

@ -37,7 +37,7 @@ void grid_renderer<T>::process(text_symbolizer const& sym,
sym, feature, prj_trans, sym, feature, prj_trans,
width_, height_, width_, height_,
scale_factor_ * (1.0/pixmap_.get_resolution()), scale_factor_ * (1.0/pixmap_.get_resolution()),
t_, font_manager_, detector_, t_, font_manager_, *detector_,
query_extent_); query_extent_);
bool placement_found = false; bool placement_found = false;

View file

@ -42,7 +42,8 @@ layer::layer(std::string const& name, std::string const& srs)
clear_label_cache_(false), clear_label_cache_(false),
cache_features_(false), cache_features_(false),
group_by_(""), group_by_(""),
ds_() {} ds_(),
buffer_size_(0) {}
layer::layer(const layer& rhs) layer::layer(const layer& rhs)
: name_(rhs.name_), : name_(rhs.name_),
@ -55,7 +56,9 @@ layer::layer(const layer& rhs)
cache_features_(rhs.cache_features_), cache_features_(rhs.cache_features_),
group_by_(rhs.group_by_), group_by_(rhs.group_by_),
styles_(rhs.styles_), styles_(rhs.styles_),
ds_(rhs.ds_) {} ds_(rhs.ds_),
buffer_size_(rhs.buffer_size_),
maximum_extent_(rhs.maximum_extent_) {}
layer& layer::operator=(const layer& rhs) layer& layer::operator=(const layer& rhs)
{ {
@ -82,6 +85,8 @@ void layer::swap(const layer& rhs)
group_by_ = rhs.group_by_; group_by_ = rhs.group_by_;
styles_=rhs.styles_; styles_=rhs.styles_;
ds_=rhs.ds_; ds_=rhs.ds_;
buffer_size_ = rhs.buffer_size_;
maximum_extent_ = rhs.maximum_extent_;
} }
layer::~layer() {} layer::~layer() {}
@ -176,6 +181,31 @@ void layer::set_datasource(datasource_ptr const& ds)
ds_ = ds; ds_ = ds;
} }
void layer::set_maximum_extent(box2d<double> const& box)
{
maximum_extent_.reset(box);
}
boost::optional<box2d<double> > const& layer::maximum_extent() const
{
return maximum_extent_;
}
void layer::reset_maximum_extent()
{
maximum_extent_.reset();
}
void layer::set_buffer_size(int size)
{
buffer_size_ = size;
}
int layer::buffer_size() const
{
return buffer_size_;
}
box2d<double> layer::envelope() const box2d<double> layer::envelope() const
{ {
if (ds_) return ds_->envelope(); if (ds_) return ds_->envelope();

View file

@ -575,66 +575,95 @@ bool map_parser::parse_font(font_set &fset, xml_node const& f)
return false; return false;
} }
void map_parser::parse_layer(Map & map, xml_node const& lay) void map_parser::parse_layer(Map & map, xml_node const& node)
{ {
std::string name; std::string name;
try try
{ {
name = lay.get_attr("name", std::string("Unnamed")); name = node.get_attr("name", std::string("Unnamed"));
// XXX if no projection is given inherit from map? [DS] // XXX if no projection is given inherit from map? [DS]
std::string srs = lay.get_attr("srs", map.srs()); std::string srs = node.get_attr("srs", map.srs());
layer lyr(name, srs); layer lyr(name, srs);
optional<boolean> status = lay.get_opt_attr<boolean>("status"); optional<boolean> status = node.get_opt_attr<boolean>("status");
if (status) if (status)
{ {
lyr.set_active(* status); lyr.set_active(* status);
} }
optional<double> min_zoom = lay.get_opt_attr<double>("minzoom"); optional<double> min_zoom = node.get_opt_attr<double>("minzoom");
if (min_zoom) if (min_zoom)
{ {
lyr.set_min_zoom(* min_zoom); lyr.set_min_zoom(* min_zoom);
} }
optional<double> max_zoom = lay.get_opt_attr<double>("maxzoom"); optional<double> max_zoom = node.get_opt_attr<double>("maxzoom");
if (max_zoom) if (max_zoom)
{ {
lyr.set_max_zoom(* max_zoom); lyr.set_max_zoom(* max_zoom);
} }
optional<boolean> queryable = lay.get_opt_attr<boolean>("queryable"); optional<boolean> queryable = node.get_opt_attr<boolean>("queryable");
if (queryable) if (queryable)
{ {
lyr.set_queryable(* queryable); lyr.set_queryable(* queryable);
} }
optional<boolean> clear_cache = optional<boolean> clear_cache =
lay.get_opt_attr<boolean>("clear-label-cache"); node.get_opt_attr<boolean>("clear-label-cache");
if (clear_cache) if (clear_cache)
{ {
lyr.set_clear_label_cache(* clear_cache); lyr.set_clear_label_cache(* clear_cache);
} }
optional<boolean> cache_features = optional<boolean> cache_features =
lay.get_opt_attr<boolean>("cache-features"); node.get_opt_attr<boolean>("cache-features");
if (cache_features) if (cache_features)
{ {
lyr.set_cache_features(* cache_features); lyr.set_cache_features(* cache_features);
} }
optional<std::string> group_by = optional<std::string> group_by =
lay.get_opt_attr<std::string>("group-by"); node.get_opt_attr<std::string>("group-by");
if (group_by) if (group_by)
{ {
lyr.set_group_by(* group_by); lyr.set_group_by(* group_by);
} }
xml_node::const_iterator child = lay.begin(); optional<unsigned> buffer_size = node.get_opt_attr<unsigned>("buffer-size");
xml_node::const_iterator end = lay.end(); if (buffer_size)
{
lyr.set_buffer_size(*buffer_size);
}
optional<std::string> maximum_extent = node.get_opt_attr<std::string>("maximum-extent");
if (maximum_extent)
{
box2d<double> box;
if (box.from_string(*maximum_extent))
{
lyr.set_maximum_extent(box);
}
else
{
std::ostringstream s_err;
s_err << "failed to parse 'maximum-extent' in layer " << name;
if (strict_)
{
throw config_error(s_err.str());
}
else
{
MAPNIK_LOG_WARN(load_map) << "map_parser: " << s_err.str();
}
}
}
xml_node::const_iterator child = node.begin();
xml_node::const_iterator end = node.end();
for(; child != end; ++child) for(; child != end; ++child)
{ {
@ -719,7 +748,7 @@ void map_parser::parse_layer(Map & map, xml_node const& lay)
{ {
if (!name.empty()) if (!name.empty())
{ {
ex.append_context(std::string(" encountered during parsing of layer '") + name + "'", lay); ex.append_context(std::string(" encountered during parsing of layer '") + name + "'", node);
} }
throw; throw;
} }
@ -894,7 +923,7 @@ void map_parser::parse_point_symbolizer(rule & rule, xml_node const & sym)
symbol.set_ignore_placement(* ignore_placement); symbol.set_ignore_placement(* ignore_placement);
} }
point_placement_e placement = point_placement_e placement =
sym.get_attr<point_placement_e>("placement", CENTROID_POINT_PLACEMENT); sym.get_attr<point_placement_e>("placement", symbol.get_point_placement());
symbol.set_point_placement(placement); symbol.set_point_placement(placement);
if (file && !file->empty()) if (file && !file->empty())
@ -1018,9 +1047,13 @@ void map_parser::parse_markers_symbolizer(rule & rule, xml_node const& node)
sym.set_filename(expr); sym.set_filename(expr);
} }
// overall opacity - impacts both fill and stroke, like svg
optional<float> opacity = node.get_opt_attr<float>("opacity"); optional<float> opacity = node.get_opt_attr<float>("opacity");
if (opacity) sym.set_opacity(*opacity); if (opacity) sym.set_opacity(*opacity);
optional<float> fill_opacity = node.get_opt_attr<float>("fill-opacity");
if (fill_opacity) sym.set_fill_opacity(*fill_opacity);
optional<std::string> image_transform_wkt = node.get_opt_attr<std::string>("transform"); optional<std::string> image_transform_wkt = node.get_opt_attr<std::string>("transform");
if (image_transform_wkt) if (image_transform_wkt)
{ {
@ -1065,7 +1098,7 @@ void map_parser::parse_markers_symbolizer(rule & rule, xml_node const& node)
sym.set_stroke(strk); sym.set_stroke(strk);
} }
marker_placement_e placement = node.get_attr<marker_placement_e>("placement", MARKER_POINT_PLACEMENT); marker_placement_e placement = node.get_attr<marker_placement_e>("placement",sym.get_marker_placement());
sym.set_marker_placement(placement); sym.set_marker_placement(placement);
parse_symbolizer_base(sym, node); parse_symbolizer_base(sym, node);
rule.append(sym); rule.append(sym);

View file

@ -352,7 +352,7 @@ void Map::set_background_image(std::string const& image_filename)
void Map::set_maximum_extent(box2d<double> const& box) void Map::set_maximum_extent(box2d<double> const& box)
{ {
maximum_extent_ = box; maximum_extent_.reset(box);
} }
boost::optional<box2d<double> > const& Map::maximum_extent() const boost::optional<box2d<double> > const& Map::maximum_extent() const
@ -360,9 +360,9 @@ boost::optional<box2d<double> > const& Map::maximum_extent() const
return maximum_extent_; return maximum_extent_;
} }
boost::optional<box2d<double> > & Map::maximum_extent() void Map::reset_maximum_extent()
{ {
return maximum_extent_; maximum_extent_.reset();
} }
std::string const& Map::base_path() const std::string const& Map::base_path() const

View file

@ -144,7 +144,7 @@ boost::optional<marker_ptr> marker_cache::find(std::string const& uri,
} }
std::string known_svg_string = mark_itr->second; std::string known_svg_string = mark_itr->second;
using namespace mapnik::svg; using namespace mapnik::svg;
path_ptr marker_path(boost::make_shared<svg_storage_type>()); svg_path_ptr marker_path(boost::make_shared<svg_storage_type>());
vertex_stl_adapter<svg_path_storage> stl_storage(marker_path->source()); vertex_stl_adapter<svg_path_storage> stl_storage(marker_path->source());
svg_path_adapter svg_path(stl_storage); svg_path_adapter svg_path(stl_storage);
svg_converter_type svg(svg_path, marker_path->attributes()); svg_converter_type svg(svg_path, marker_path->attributes());
@ -173,7 +173,7 @@ boost::optional<marker_ptr> marker_cache::find(std::string const& uri,
if (is_svg(uri)) if (is_svg(uri))
{ {
using namespace mapnik::svg; using namespace mapnik::svg;
path_ptr marker_path(boost::make_shared<svg_storage_type>()); svg_path_ptr marker_path(boost::make_shared<svg_storage_type>());
vertex_stl_adapter<svg_path_storage> stl_storage(marker_path->source()); vertex_stl_adapter<svg_path_storage> stl_storage(marker_path->source());
svg_path_adapter svg_path(stl_storage); svg_path_adapter svg_path(stl_storage);
svg_converter_type svg(svg_path, marker_path->attributes()); svg_converter_type svg(svg_path, marker_path->attributes());

View file

@ -1,253 +0,0 @@
// mapnik
#include <mapnik/markers_placement.hpp>
#include <mapnik/geometry.hpp>
#include <mapnik/ctrans.hpp>
#include <mapnik/label_collision_detector.hpp>
#include <mapnik/global.hpp> //round
// agg
#include "agg_basics.h"
#include "agg_conv_clip_polyline.h"
#include "agg_trans_affine.h"
#include "agg_conv_transform.h"
#include "agg_conv_smooth_poly1.h"
// stl
#include <cmath>
namespace mapnik
{
template <typename Locator, typename Detector>
markers_placement<Locator, Detector>::markers_placement(
Locator &locator, box2d<double> const& size, agg::trans_affine const& tr, Detector &detector, double spacing, double max_error, bool allow_overlap)
: locator_(locator), size_(size), tr_(tr), detector_(detector), max_error_(max_error), allow_overlap_(allow_overlap)
{
marker_width_ = (size_ * tr_).width();
if (spacing >= 0)
{
spacing_ = spacing;
} else if (spacing < 0)
{
spacing_ = find_optimal_spacing(-spacing);
}
rewind();
}
template <typename Locator, typename Detector>
double markers_placement<Locator, Detector>::find_optimal_spacing(double s)
{
rewind();
//Calculate total path length
unsigned cmd = agg::path_cmd_move_to;
double length = 0;
while (!agg::is_stop(cmd))
{
double dx = next_x - last_x;
double dy = next_y - last_y;
length += std::sqrt(dx * dx + dy * dy);
last_x = next_x;
last_y = next_y;
while (agg::is_move_to(cmd = locator_.vertex(&next_x, &next_y)))
{
//Skip over "move" commands
last_x = next_x;
last_y = next_y;
}
}
unsigned points = round(length / s);
if (points == 0) return 0.0; //Path to short
return length / points;
}
template <typename Locator, typename Detector>
void markers_placement<Locator, Detector>::rewind()
{
locator_.rewind(0);
//Get first point
done_ = agg::is_stop(locator_.vertex(&next_x, &next_y)) || spacing_ < marker_width_;
last_x = next_x;
last_y = next_y; // Force request of new segment
error_ = 0;
marker_nr_ = 0;
}
template <typename Locator, typename Detector>
bool markers_placement<Locator, Detector>::get_point(
double & x, double & y, double & angle, bool add_to_detector)
{
if (done_) return false;
unsigned cmd;
/* This functions starts at the position of the previous marker,
walks along the path, counting how far it has to go in spacing_left.
If one marker can't be placed at the position it should go to it is
moved a bit. The error is compensated for in the next call to this
function.
error > 0: Marker too near to the end of the path.
error = 0: Perfect position.
error < 0: Marker too near to the beginning of the path.
*/
if (marker_nr_++ == 0)
{
//First marker
spacing_left_ = spacing_ / 2;
} else
{
spacing_left_ = spacing_;
}
spacing_left_ -= error_;
error_ = 0;
//Loop exits when a position is found or when no more segments are available
while (true)
{
//Do not place markers too close to the beginning of a segment
if (spacing_left_ < marker_width_/2)
{
set_spacing_left(marker_width_/2); //Only moves forward
}
//Error for this marker is too large. Skip to the next position.
if (abs(error_) > max_error_ * spacing_)
{
if (error_ > spacing_)
{
error_ = 0; //Avoid moving backwards
MAPNIK_LOG_WARN(markers_placement) << "Extremely large error in markers_placement. Please file a bug report.";
}
spacing_left_ += spacing_ - error_;
error_ = 0;
}
double dx = next_x - last_x;
double dy = next_y - last_y;
double segment_length = std::sqrt(dx * dx + dy * dy);
if (segment_length <= spacing_left_)
{
//Segment is to short to place marker. Find next segment
spacing_left_ -= segment_length;
last_x = next_x;
last_y = next_y;
while (agg::is_move_to(cmd = locator_.vertex(&next_x, &next_y)))
{
//Skip over "move" commands
last_x = next_x;
last_y = next_y;
}
if (agg::is_stop(cmd))
{
done_ = true;
return false;
}
continue; //Try again
}
/* At this point we know the following things:
- segment_length > spacing_left
- error is small enough
- at least half a marker fits into this segment
*/
//Check if marker really fits in this segment
if (segment_length < marker_width_)
{
//Segment to short => Skip this segment
set_spacing_left(segment_length + marker_width_/2); //Only moves forward
continue;
} else if (segment_length - spacing_left_ < marker_width_/2)
{
//Segment is long enough, but we are to close to the end
//Note: This function moves backwards. This could lead to an infinite
// loop when another function adds a positive offset. Therefore we
// only move backwards when there is no offset
if (error_ == 0)
{
set_spacing_left(segment_length - marker_width_/2, true);
} else
{
//Skip this segment
set_spacing_left(segment_length + marker_width_/2); //Only moves forward
}
continue; //Force checking of max_error constraint
}
angle = atan2(dy, dx);
x = last_x + dx * (spacing_left_ / segment_length);
y = last_y + dy * (spacing_left_ / segment_length);
box2d<double> box = perform_transform(angle, x, y);
if (!allow_overlap_ && !detector_.has_placement(box))
{
//10.0 is the approxmiate number of positions tried and choosen arbitrarily
set_spacing_left(spacing_left_ + spacing_ * max_error_ / 10.0); //Only moves forward
continue;
}
if (add_to_detector) detector_.insert(box);
last_x = x;
last_y = y;
return true;
}
}
template <typename Locator, typename Detector>
box2d<double> markers_placement<Locator, Detector>::perform_transform(double angle, double dx, double dy)
{
double x1 = size_.minx();
double x2 = size_.maxx();
double y1 = size_.miny();
double y2 = size_.maxy();
agg::trans_affine tr = tr_ * agg::trans_affine_rotation(angle).translate(dx, dy);
double xA = x1, yA = y1, xB = x2, yB = y1, xC = x2, yC = y2, xD = x1, yD = y2;
tr.transform(&xA, &yA);
tr.transform(&xB, &yB);
tr.transform(&xC, &yC);
tr.transform(&xD, &yD);
box2d<double> result(xA, yA, xC, yC);
result.expand_to_include(xB, yB);
result.expand_to_include(xD, yD);
return result;
}
template <typename Locator, typename Detector>
void markers_placement<Locator, Detector>::set_spacing_left(double sl, bool allow_negative)
{
double delta_error = sl - spacing_left_;
if (!allow_negative && delta_error < 0)
{
MAPNIK_LOG_WARN(markers_placement) << "Unexpected negative error in markers_placement. Please file a bug report.";
return;
}
#ifdef MAPNIK_DEBUG
if (delta_error == 0.0)
{
MAPNIK_LOG_WARN(markers_placement) << "Not moving at all in set_spacing_left()! Please file a bug report.";
}
#endif
error_ += delta_error;
spacing_left_ = sl;
}
typedef agg::conv_clip_polyline<geometry_type> clipped_geometry_type;
typedef coord_transform<CoordTransform,geometry_type> path_type;
typedef coord_transform<CoordTransform,clipped_geometry_type> clipped_path_type;
typedef agg::conv_transform<path_type, agg::trans_affine> transformed_path_type;
template class markers_placement<geometry_type, label_collision_detector4>;
template class markers_placement<path_type, label_collision_detector4>;
template class markers_placement<clipped_geometry_type, label_collision_detector4>;
template class markers_placement<transformed_path_type, label_collision_detector4>;
template class markers_placement<clipped_path_type, label_collision_detector4>;
template class markers_placement<agg::conv_transform<clipped_path_type,agg::trans_affine>, label_collision_detector4>;
template class markers_placement<agg::conv_smooth_poly1_curve<clipped_path_type>, label_collision_detector4>;
template class markers_placement<agg::conv_smooth_poly1_curve<geometry_type>, label_collision_detector4>;
template class markers_placement<agg::conv_smooth_poly1_curve<path_type>, label_collision_detector4>;
template class markers_placement<agg::conv_smooth_poly1_curve<clipped_geometry_type>, label_collision_detector4>;
} //ns mapnik

View file

@ -30,6 +30,7 @@ namespace mapnik {
static const char * marker_placement_strings[] = { static const char * marker_placement_strings[] = {
"point", "point",
"interior",
"line", "line",
"" ""
}; };
@ -45,10 +46,7 @@ markers_symbolizer::markers_symbolizer()
allow_overlap_(false), allow_overlap_(false),
spacing_(100.0), spacing_(100.0),
max_error_(0.2), max_error_(0.2),
marker_p_(MARKER_POINT_PLACEMENT) { marker_p_(MARKER_POINT_PLACEMENT) { }
// override the default for clipping in symbolizer base
this->set_clip(false);
}
markers_symbolizer::markers_symbolizer(path_expression_ptr const& filename) markers_symbolizer::markers_symbolizer(path_expression_ptr const& filename)
: symbolizer_with_image(filename), : symbolizer_with_image(filename),
@ -59,10 +57,7 @@ markers_symbolizer::markers_symbolizer(path_expression_ptr const& filename)
allow_overlap_(false), allow_overlap_(false),
spacing_(100.0), spacing_(100.0),
max_error_(0.2), max_error_(0.2),
marker_p_(MARKER_POINT_PLACEMENT) { marker_p_(MARKER_POINT_PLACEMENT) { }
// override the default for clipping in symbolizer base
this->set_clip(false);
}
markers_symbolizer::markers_symbolizer(markers_symbolizer const& rhs) markers_symbolizer::markers_symbolizer(markers_symbolizer const& rhs)
: symbolizer_with_image(rhs), : symbolizer_with_image(rhs),
@ -74,6 +69,8 @@ markers_symbolizer::markers_symbolizer(markers_symbolizer const& rhs)
spacing_(rhs.spacing_), spacing_(rhs.spacing_),
max_error_(rhs.max_error_), max_error_(rhs.max_error_),
fill_(rhs.fill_), fill_(rhs.fill_),
fill_opacity_(rhs.fill_opacity_),
opacity_(rhs.opacity_),
stroke_(rhs.stroke_), stroke_(rhs.stroke_),
marker_p_(rhs.marker_p_) {} marker_p_(rhs.marker_p_) {}
@ -117,6 +114,16 @@ double markers_symbolizer::get_max_error() const
return max_error_; return max_error_;
} }
void markers_symbolizer::set_opacity(float opacity)
{
opacity_ = opacity;
}
boost::optional<float> markers_symbolizer::get_opacity() const
{
return opacity_;
}
void markers_symbolizer::set_fill(color const& fill) void markers_symbolizer::set_fill(color const& fill)
{ {
fill_ = fill; fill_ = fill;
@ -127,6 +134,16 @@ boost::optional<color> markers_symbolizer::get_fill() const
return fill_; return fill_;
} }
void markers_symbolizer::set_fill_opacity(float opacity)
{
fill_opacity_ = opacity;
}
boost::optional<float> markers_symbolizer::get_fill_opacity() const
{
return fill_opacity_;
}
void markers_symbolizer::set_width(expression_ptr const& width) void markers_symbolizer::set_width(expression_ptr const& width)
{ {
width_ = width; width_ = width;

View file

@ -78,6 +78,11 @@ public:
{ {
set_attr( sym_node, "placement", sym.get_point_placement() ); set_attr( sym_node, "placement", sym.get_point_placement() );
} }
if (sym.get_image_transform())
{
std::string tr_str = sym.get_image_transform_string();
set_attr( sym_node, "transform", tr_str );
}
serialize_symbolizer_base(sym_node, sym); serialize_symbolizer_base(sym_node, sym);
} }
@ -218,6 +223,11 @@ public:
{ {
set_attr(sym_node, "shield-dy", displacement.y); set_attr(sym_node, "shield-dy", displacement.y);
} }
if (sym.get_image_transform())
{
std::string tr_str = sym.get_image_transform_string();
set_attr( sym_node, "transform", tr_str );
}
serialize_symbolizer_base(sym_node, sym); serialize_symbolizer_base(sym_node, sym);
} }
@ -249,7 +259,6 @@ public:
{ {
set_attr( sym_node, "height", mapnik::to_expression_string(*sym.height()) ); set_attr( sym_node, "height", mapnik::to_expression_string(*sym.height()) );
} }
serialize_symbolizer_base(sym_node, sym); serialize_symbolizer_base(sym_node, sym);
} }
@ -283,6 +292,10 @@ public:
{ {
set_attr( sym_node, "fill", sym.get_fill() ); set_attr( sym_node, "fill", sym.get_fill() );
} }
if (sym.get_fill_opacity() != dfl.get_fill_opacity() || explicit_defaults_)
{
set_attr( sym_node, "fill-opacity", sym.get_fill_opacity() );
}
if (sym.get_opacity() != dfl.get_opacity() || explicit_defaults_) if (sym.get_opacity() != dfl.get_opacity() || explicit_defaults_)
{ {
set_attr( sym_node, "opacity", sym.get_opacity() ); set_attr( sym_node, "opacity", sym.get_opacity() );
@ -454,7 +467,7 @@ private:
} }
if ( strk.dash_offset() != dfl.dash_offset() || explicit_defaults_ ) if ( strk.dash_offset() != dfl.dash_offset() || explicit_defaults_ )
{ {
set_attr( node, "stroke-dash-offset", strk.dash_offset()); set_attr( node, "stroke-dashoffset", strk.dash_offset());
} }
if ( ! strk.get_dash_array().empty() ) if ( ! strk.get_dash_array().empty() )
{ {
@ -728,6 +741,22 @@ void serialize_layer( ptree & map_node, const layer & layer, bool explicit_defau
set_attr( layer_node, "group-by", layer.group_by() ); set_attr( layer_node, "group-by", layer.group_by() );
} }
int buffer_size = layer.buffer_size();
if ( buffer_size || explicit_defaults)
{
set_attr( layer_node, "buffer-size", buffer_size );
}
optional<box2d<double> > const& maximum_extent = layer.maximum_extent();
if ( maximum_extent)
{
std::ostringstream s;
s << std::setprecision(16)
<< maximum_extent->minx() << "," << maximum_extent->miny() << ","
<< maximum_extent->maxx() << "," << maximum_extent->maxy();
set_attr( layer_node, "maximum-extent", s.str() );
}
std::vector<std::string> const& style_names = layer.styles(); std::vector<std::string> const& style_names = layer.styles();
for (unsigned i = 0; i < style_names.size(); ++i) for (unsigned i = 0; i < style_names.size(); ++i)
{ {
@ -775,7 +804,7 @@ void serialize_map(ptree & pt, Map const & map, bool explicit_defaults)
set_attr( map_node, "background-image", *image_filename ); set_attr( map_node, "background-image", *image_filename );
} }
unsigned buffer_size = map.buffer_size(); int buffer_size = map.buffer_size();
if ( buffer_size || explicit_defaults) if ( buffer_size || explicit_defaults)
{ {
set_attr( map_node, "buffer-size", buffer_size ); set_attr( map_node, "buffer-size", buffer_size );

View file

@ -221,7 +221,11 @@ void text_symbolizer_helper<FaceManagerT, DetectorT>::initialize_points()
} }
else else
{ {
if (how_placed == POINT_PLACEMENT) if (geom.type() == LineString)
{
label::middle_point(geom, label_x,label_y);
}
else if (how_placed == POINT_PLACEMENT)
{ {
label::centroid(geom, label_x, label_y); label::centroid(geom, label_x, label_y);
} }

View file

@ -0,0 +1,66 @@
<Map srs="+init=epsg:4326" background-color="rgba(255,255,255,.5)">
<Style name="ellipse">
<Rule>
<MarkersSymbolizer
width="240"
height="240"
fill="steelblue"
fill-opacity=".7"
stroke="yellow"
stroke-width="16"
stroke-opacity=".3"
/>
<MarkersSymbolizer
width="100"
opacity=".5"
fill="darkorange"
comp-op="multiply"
transform="skewX(50)"
allow-overlap="true"
/>
<MarkersSymbolizer
width="100"
opacity=".5"
fill="darkred"
comp-op="color-burn"
transform="skewX(-50)"
allow-overlap="true"
/>
</Rule>
</Style>
<Layer name="layer" srs="+init=epsg:4326">
<StyleName>ellipse</StyleName>
<Datasource>
<Parameter name="type">csv</Parameter>
<Parameter name="inline">
x,y
2.5,2.5
</Parameter>
</Datasource>
</Layer>
<!-- points to frame data view -->
<Style name="frame">
<Rule>
<PointSymbolizer />
</Rule>
</Style>
<Layer name="frame" srs="+init=epsg:4326">
<StyleName>frame</StyleName>
<Datasource>
<Parameter name="type">csv</Parameter>
<Parameter name="inline">
x,y
0,0
5,0
0,5
5,5
</Parameter>
</Datasource>
</Layer>
</Map>

View file

@ -0,0 +1,66 @@
<Map srs="+init=epsg:4326" background-color="rgba(255,255,255,.5)">
<Style name="ellipse">
<Rule>
<MarkersSymbolizer
width="240"
height="240"
fill="steelblue"
fill-opacity=".7"
stroke="yellow"
stroke-width="16"
stroke-opacity=".3"
/>
<MarkersSymbolizer
width="100"
opacity=".5"
fill="darkorange"
comp-op="grain-extract"
transform="skewX(50)"
allow-overlap="true"
/>
<MarkersSymbolizer
width="100"
opacity=".5"
fill="darkred"
comp-op="color-burn"
transform="skewX(-50)"
allow-overlap="true"
/>
</Rule>
</Style>
<Layer name="layer" srs="+init=epsg:4326">
<StyleName>ellipse</StyleName>
<Datasource>
<Parameter name="type">csv</Parameter>
<Parameter name="inline">
x,y
2.5,2.5
</Parameter>
</Datasource>
</Layer>
<!-- points to frame data view -->
<Style name="frame">
<Rule>
<PointSymbolizer />
</Rule>
</Style>
<Layer name="frame" srs="+init=epsg:4326">
<StyleName>frame</StyleName>
<Datasource>
<Parameter name="type">csv</Parameter>
<Parameter name="inline">
x,y
0,0
5,0
0,5
5,5
</Parameter>
</Datasource>
</Layer>
</Map>

View file

@ -3,7 +3,14 @@
<Style name="1"> <Style name="1">
<Rule> <Rule>
<LineSymbolizer stroke-width=".2" stroke="grey"/> <LineSymbolizer stroke-width=".2" stroke="grey"/>
<MarkersSymbolizer stroke="green" stroke-width="1.3" fill="yellow"/> <MarkersSymbolizer
file="shape://arrow"
transform="scale(.8,.5)"
stroke="green"
stroke-width="1.3"
fill="yellow"
placement="line"
/>
</Rule> </Rule>
</Style> </Style>

View file

@ -2,8 +2,15 @@
<Map background-color="white" srs="+init=epsg:4326" minimum-version="0.7.2"> <Map background-color="white" srs="+init=epsg:4326" minimum-version="0.7.2">
<Style name="1"> <Style name="1">
<Rule> <Rule>
<LineSymbolizer stroke-width=".2" stroke="grey"/> <LineSymbolizer offset="-2" stroke-width="2" stroke="red"/>
<MarkersSymbolizer file="../svg/crosshair16x16.svg"/> <LineSymbolizer stroke-width="2" stroke="orange"/>
<LineSymbolizer offset="2" stroke-width="2" stroke="yellow"/>
<LineSymbolizer offset="4" stroke-width="2" stroke="green"/>
<MarkersSymbolizer
file="../svg/octocat.svg"
transform="scale(.13)"
placement="line"
/>
</Rule> </Rule>
</Style> </Style>

View file

@ -0,0 +1,52 @@
<Map srs="+init=epsg:4326" background-color="steelblue">
<!-- test case for https://github.com/mapnik/mapnik/issues/1354 -->
<Style name="labels">
<Rule>
<TextSymbolizer
face-name="DejaVu Sans Book"
size="22"
halo-radius="10"
placement="point"
allow-overlap="false">
'this text halos should not overlap vertically'
</TextSymbolizer>
</Rule>
</Style>
<Layer name="points" srs="+init=epsg:4326">
<StyleName>labels</StyleName>
<Datasource>
<Parameter name="type">csv</Parameter>
<Parameter name="inline">
x,y
2,2
2,2.3
</Parameter>
</Datasource>
</Layer>
<!-- points to frame data view -->
<Style name="frame">
<Rule>
<PointSymbolizer />
</Rule>
</Style>
<Layer name="frame" srs="+init=epsg:4326">
<StyleName>frame</StyleName>
<Datasource>
<Parameter name="type">csv</Parameter>
<Parameter name="inline">
x,y
0,0
5,0
0,5
5,5
</Parameter>
</Datasource>
</Layer>
</Map>

View file

@ -0,0 +1,17 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-0.2 -1 379 334">
<path id="puddle" fill="#9CDAF1" d="m296.94 295.43c0 20.533-47.56 37.176-106.22 37.176-58.67 0-106.23-16.643-106.23-37.176s47.558-37.18 106.23-37.18c58.66 0 106.22 16.65 106.22 37.18z"/>
<g id="shadow-legs" fill="#7DBBE6">
<path d="m161.85 331.22v-26.5c0-3.422-.619-6.284-1.653-8.701 6.853 5.322 7.316 18.695 7.316 18.695v17.004c6.166.481 12.534.773 19.053.861l-.172-16.92c-.944-23.13-20.769-25.961-20.769-25.961-7.245-1.645-7.137 1.991-6.409 4.34-7.108-12.122-26.158-10.556-26.158-10.556-6.611 2.357-.475 6.607-.475 6.607 10.387 3.775 11.33 15.105 11.33 15.105v23.622c5.72.98 11.71 1.79 17.94 2.4z"/>
<path d="m245.4 283.48s-19.053-1.566-26.16 10.559c.728-2.35.839-5.989-6.408-4.343 0 0-19.824 2.832-20.768 25.961l-.174 16.946c6.509-.025 12.876-.254 19.054-.671v-17.219s.465-13.373 7.316-18.695c-1.034 2.417-1.653 5.278-1.653 8.701v26.775c6.214-.544 12.211-1.279 17.937-2.188v-24.113s.944-11.33 11.33-15.105c0-.01 6.13-4.26-.48-6.62z"/>
</g>
<path id="cat" d="m378.18 141.32l.28-1.389c-31.162-6.231-63.141-6.294-82.487-5.49 3.178-11.451 4.134-24.627 4.134-39.32 0-21.073-7.917-37.931-20.77-50.759 2.246-7.25 5.246-23.351-2.996-43.963 0 0-14.541-4.617-47.431 17.396-12.884-3.22-26.596-4.81-40.328-4.81-15.109 0-30.376 1.924-44.615 5.83-33.94-23.154-48.923-18.411-48.923-18.411-9.78 24.457-3.733 42.566-1.896 47.063-11.495 12.406-18.513 28.243-18.513 47.659 0 14.658 1.669 27.808 5.745 39.237-19.511-.71-50.323-.437-80.373 5.572l.276 1.389c30.231-6.046 61.237-6.256 80.629-5.522.898 2.366 1.899 4.661 3.021 6.879-19.177.618-51.922 3.062-83.303 11.915l.387 1.36c31.629-8.918 64.658-11.301 83.649-11.882 11.458 21.358 34.048 35.152 74.236 39.484-5.704 3.833-11.523 10.349-13.881 21.374-7.773 3.718-32.379 12.793-47.142-12.599 0 0-8.264-15.109-24.082-16.292 0 0-15.344-.235-1.059 9.562 0 0 10.267 4.838 17.351 23.019 0 0 9.241 31.01 53.835 21.061v32.032s-.943 11.33-11.33 15.105c0 0-6.137 4.249.475 6.606 0 0 28.792 2.361 28.792-21.238v-34.929s-1.142-13.852 5.663-18.667v57.371s-.47 13.688-7.551 18.881c0 0-4.723 8.494 5.663 6.137 0 0 19.824-2.832 20.769-25.961l.449-58.06h4.765l.453 58.06c.943 23.129 20.768 25.961 20.768 25.961 10.383 2.357 5.663-6.137 5.663-6.137-7.08-5.193-7.551-18.881-7.551-18.881v-56.876c6.801 5.296 5.663 18.171 5.663 18.171v34.929c0 23.6 28.793 21.238 28.793 21.238 6.606-2.357.474-6.606.474-6.606-10.386-3.775-11.33-15.105-11.33-15.105v-45.786c0-17.854-7.518-27.309-14.87-32.3 42.859-4.25 63.426-18.089 72.903-39.591 18.773.516 52.557 2.803 84.873 11.919l.384-1.36c-32.131-9.063-65.692-11.408-84.655-11.96.898-2.172 1.682-4.431 2.378-6.755 19.25-.80 51.38-.79 82.66 5.46z"/>
<path id="face" fill="#F4CBB2" d="m258.19 94.132c9.231 8.363 14.631 18.462 14.631 29.343 0 50.804-37.872 52.181-84.585 52.181-46.721 0-84.589-7.035-84.589-52.181 0-10.809 5.324-20.845 14.441-29.174 15.208-13.881 40.946-6.531 70.147-6.531 29.07-.004 54.72-7.429 69.95 6.357z"/>
<path id="eyes" fill="#FFF" d="m160.1 126.06 c0 13.994-7.88 25.336-17.6 25.336-9.72 0-17.6-11.342-17.6-25.336 0-13.992 7.88-25.33 17.6-25.33 9.72.01 17.6 11.34 17.6 25.33z m94.43 0 c0 13.994-7.88 25.336-17.6 25.336-9.72 0-17.6-11.342-17.6-25.336 0-13.992 7.88-25.33 17.6-25.33 9.72.01 17.6 11.34 17.6 25.33z"/>
<g fill="#AD5C51">
<path id="pupils" d="m154.46 126.38 c0 9.328-5.26 16.887-11.734 16.887s-11.733-7.559-11.733-16.887c0-9.331 5.255-16.894 11.733-16.894 6.47 0 11.73 7.56 11.73 16.89z m94.42 0 c0 9.328-5.26 16.887-11.734 16.887s-11.733-7.559-11.733-16.887c0-9.331 5.255-16.894 11.733-16.894 6.47 0 11.73 7.56 11.73 16.89z"/>
<circle id="nose" cx="188.5" cy="148.56" r="4.401"/>
<path id="mouth" d="m178.23 159.69c-.26-.738.128-1.545.861-1.805.737-.26 1.546.128 1.805.861 1.134 3.198 4.167 5.346 7.551 5.346s6.417-2.147 7.551-5.346c.26-.738 1.067-1.121 1.805-.861s1.121 1.067.862 1.805c-1.529 4.324-5.639 7.229-10.218 7.229s-8.68-2.89-10.21-7.22z"/>
</g>
<path id="octo" fill="#C3E4D8" d="m80.641 179.82 c0 1.174-1.376 2.122-3.07 2.122-1.693 0-3.07-.948-3.07-2.122 0-1.175 1.377-2.127 3.07-2.127 1.694 0 3.07.95 3.07 2.13z m8.5 4.72 c0 1.174-1.376 2.122-3.07 2.122-1.693 0-3.07-.948-3.07-2.122 0-1.175 1.377-2.127 3.07-2.127 1.694 0 3.07.95 3.07 2.13z m5.193 6.14 c0 1.174-1.376 2.122-3.07 2.122-1.693 0-3.07-.948-3.07-2.122 0-1.175 1.377-2.127 3.07-2.127 1.694 0 3.07.95 3.07 2.13z m4.72 7.08 c0 1.174-1.376 2.122-3.07 2.122-1.693 0-3.07-.948-3.07-2.122 0-1.175 1.377-2.127 3.07-2.127 1.694 0 3.07.95 3.07 2.13z m5.188 6.61 c0 1.174-1.376 2.122-3.07 2.122-1.693 0-3.07-.948-3.07-2.122 0-1.175 1.377-2.127 3.07-2.127 1.694 0 3.07.95 3.07 2.13z m7.09 5.66 c0 1.174-1.376 2.122-3.07 2.122-1.693 0-3.07-.948-3.07-2.122 0-1.175 1.377-2.127 3.07-2.127 1.694 0 3.07.95 3.07 2.13z m9.91 3.78 c0 1.174-1.376 2.122-3.07 2.122-1.693 0-3.07-.948-3.07-2.122 0-1.175 1.377-2.127 3.07-2.127 1.694 0 3.07.95 3.07 2.13z m9.87 0 c0 1.174-1.376 2.122-3.07 2.122-1.693 0-3.07-.948-3.07-2.122 0-1.175 1.377-2.127 3.07-2.127 1.694 0 3.07.95 3.07 2.13z m10.01 -1.64 c0 1.174-1.376 2.122-3.07 2.122-1.693 0-3.07-.948-3.07-2.122 0-1.175 1.377-2.127 3.07-2.127 1.694 0 3.07.95 3.07 2.13z"/>
<path id="drop" fill="#9CDAF1" d="m69.369 186.12l-3.066 10.683s-.8 3.861 2.84 4.546c3.8-.074 3.486-3.627 3.223-4.781z"/>
</svg>

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

View file

@ -0,0 +1,39 @@
#coding=utf8
import os
import mapnik
from utilities import execution_path
from nose.tools import *
def setup():
# All of the paths used are relative, if we run the tests
# from another directory we need to chdir()
os.chdir(execution_path('.'))
def test_marker_ellipse_render1():
m = mapnik.Map(256,256)
mapnik.load_map(m,'../data/good_maps/marker_ellipse_transform.xml')
m.zoom_all()
im = mapnik.Image(m.width,m.height)
mapnik.render(m,im)
actual = '/tmp/mapnik-marker-ellipse-render1.png'
expected = 'images/support/mapnik-marker-ellipse-render1.png'
im.save(actual)
expected_im = mapnik.Image.open(expected)
eq_(im.tostring(),expected_im.tostring(), 'failed comparing actual (%s) and expected (%s)' % (actual,'tests/python_tests/'+ expected))
#def test_marker_ellipse_render2():
# # currently crashes https://github.com/mapnik/mapnik/issues/1365
# m = mapnik.Map(256,256)
# mapnik.load_map(m,'../data/good_maps/marker_ellipse_transform2.xml')
# m.zoom_all()
# im = mapnik.Image(m.width,m.height)
# mapnik.render(m,im)
# actual = '/tmp/mapnik-marker-ellipse-render2.png'
# expected = 'images/support/mapnik-marker-ellipse-render2.png'
# im.save(actual)
# expected_im = mapnik.Image.open(expected)
# eq_(im.tostring(),expected_im.tostring(), 'failed comparing actual (%s) and expected (%s)' % (actual,'tests/python_tests/'+ expected))
if __name__ == "__main__":
setup()
[eval(run)() for run in dir() if 'test_' in run]

View file

@ -21,6 +21,15 @@ def test_line_symbolizer_init():
s = mapnik.LineSymbolizer() s = mapnik.LineSymbolizer()
eq_(s.rasterizer, mapnik.line_rasterizer.FULL) eq_(s.rasterizer, mapnik.line_rasterizer.FULL)
def test_line_symbolizer_stroke_reference():
l = mapnik.LineSymbolizer(mapnik.Color('green'),0.1)
l.stroke.add_dash(.1,.1)
l.stroke.add_dash(.1,.1)
eq_(l.stroke.get_dashes(), [(.1,.1),(.1,.1)])
eq_(l.stroke.color,mapnik.Color('green'))
eq_(l.stroke.opacity,1.0)
assert_almost_equal(l.stroke.width,0.1)
# ShieldSymbolizer initialization # ShieldSymbolizer initialization
def test_shieldsymbolizer_init(): def test_shieldsymbolizer_init():
s = mapnik.ShieldSymbolizer(mapnik.Expression('[Field Name]'), 'DejaVu Sans Bold', 6, mapnik.Color('#000000'), mapnik.PathExpression('../data/images/dummy.png')) s = mapnik.ShieldSymbolizer(mapnik.Expression('[Field Name]'), 'DejaVu Sans Bold', 6, mapnik.Color('#000000'), mapnik.PathExpression('../data/images/dummy.png'))
@ -126,7 +135,8 @@ def test_pointsymbolizer_init():
def test_markersymbolizer_init(): def test_markersymbolizer_init():
p = mapnik.MarkersSymbolizer() p = mapnik.MarkersSymbolizer()
eq_(p.allow_overlap, False) eq_(p.allow_overlap, False)
eq_(p.opacity,1) eq_(p.opacity,None)
eq_(p.fill_opacity,None)
eq_(p.filename,'shape://ellipse') eq_(p.filename,'shape://ellipse')
eq_(p.placement,mapnik.marker_placement.POINT_PLACEMENT) eq_(p.placement,mapnik.marker_placement.POINT_PLACEMENT)
eq_(p.fill,None) eq_(p.fill,None)
@ -154,9 +164,11 @@ def test_markersymbolizer_init():
p.fill = mapnik.Color('white') p.fill = mapnik.Color('white')
p.allow_overlap = True p.allow_overlap = True
p.opacity = 0.5 p.opacity = 0.5
p.fill_opacity = .01
eq_(p.allow_overlap, True) eq_(p.allow_overlap, True)
eq_(p.opacity, 0.5) eq_(p.opacity, 0.5)
eq_(p.fill_opacity, 0.5)
# PointSymbolizer missing image file # PointSymbolizer missing image file

View file

@ -20,13 +20,13 @@ def setup():
grid_correct_old = {"keys": ["", "North West", "North East", "South West", "South East"], "data": {"South East": {"Name": "South East"}, "North East": {"Name": "North East"}, "North West": {"Name": "North West"}, "South West": {"Name": "South West"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !!! ### ", " !!!!! ##### ", " !!!!! ##### ", " !!! ### ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$$$ %%%% ", " $$$$$ %%%%% ", " $$$$$ %%%%% ", " $$$ %%% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]} grid_correct_old = {"keys": ["", "North West", "North East", "South West", "South East"], "data": {"South East": {"Name": "South East"}, "North East": {"Name": "North East"}, "North West": {"Name": "North West"}, "South West": {"Name": "South West"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !!! ### ", " !!!!! ##### ", " !!!!! ##### ", " !!! ### ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$$$ %%%% ", " $$$$$ %%%%% ", " $$$$$ %%%%% ", " $$$ %%% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]}
# now using svg rendering # now using svg rendering
grid_correct_old2 = {"data": {"North East": {"Name": "North East"}, "North West": {"Name": "North West"}, "South East": {"Name": "South East"}, "South West": {"Name": "South West"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !!! ### ", " !!! ### ", " !!! ### ", " !!! ### ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$$ %%% ", " $$$ %%% ", " $$$ %%% ", " $ % ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "North West", "North East", "South West", "South East"]} grid_correct_old2 = {"data": {"North East": {"Name": "North East"}, "North West": {"Name": "North West"}, "South East": {"Name": "South East"}, "South West": {"Name": "South West"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !!! ### ", " !!! ### ", " !!! ### ", " !!! ### ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$$ %%% ", " $$$ %%% ", " $$$ %%% ", " $$$ %%% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "North West", "North East", "South West", "South East"]}
# previous rendering using agg ellipse directly # previous rendering using agg ellipse directly
grid_correct_new = {"keys": ["", "North West", "North East", "South West", "South East"], "data": {"South East": {"Name": "South East"}, "North East": {"Name": "North East"}, "North West": {"Name": "North West"}, "South West": {"Name": "South West"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !! ## ", " !!! ### ", " !! ## ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$$ %% ", " $$$ %%% ", " $$ %% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]} grid_correct_new = {"data": {"North East": {"Name": "North East"}, "North West": {"Name": "North West"}, "South East": {"Name": "South East"}, "South West": {"Name": "South West"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !! ## ", " !!! ### ", " !! ## ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$ %% ", " $$$ %% ", " $$ %% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "North West", "North East", "South West", "South East"]}
# newer rendering using svg # newer rendering using svg
grid_correct_new2 = {"data": {"North East": {"Name": "North East"}, "North West": {"Name": "North West"}, "South East": {"Name": "South East"}, "South West": {"Name": "South West"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !! ## ", " !!! ## ", " !! ## ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$ %% ", " $$ %% ", " $ % ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "North West", "North East", "South West", "South East"]} grid_correct_new2 = {"data": {"North East": {"Name": "North East"}, "North West": {"Name": "North West"}, "South East": {"Name": "South East"}, "South West": {"Name": "South West"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !! ## ", " !!! ### ", " !! ## ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$ %% ", " $$$ %% ", " $$ %% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "North West", "North East", "South West", "South East"]}
def resolve(grid,row,col): def resolve(grid,row,col):
""" Resolve the attributes for a given pixel in a grid. """ Resolve the attributes for a given pixel in a grid.
@ -108,7 +108,7 @@ def show_grids(name,g1,g2):
val += '\n\t%s\n\t%s' % (g1_file,g2_file) val += '\n\t%s\n\t%s' % (g1_file,g2_file)
return val return val
def test_render_grid(): def test_render_grid_old():
""" test old method """ """ test old method """
width,height = 256,256 width,height = 256,256
m = create_grid_map(width,height) m = create_grid_map(width,height)
@ -117,7 +117,7 @@ def test_render_grid():
lr_lonlat = mapnik.Coord(143.40,-38.80) lr_lonlat = mapnik.Coord(143.40,-38.80)
m.zoom_to_box(mapnik.Box2d(ul_lonlat,lr_lonlat)) m.zoom_to_box(mapnik.Box2d(ul_lonlat,lr_lonlat))
grid = mapnik.render_grid(m,0,key='Name',resolution=4,fields=['Name']) grid = mapnik.render_grid(m,0,key='Name',resolution=4,fields=['Name'])
eq_(grid,grid_correct_old2,show_grids('old',grid,grid_correct_old2)) eq_(grid,grid_correct_old2,show_grids('old-markers',grid,grid_correct_old2))
eq_(resolve(grid,0,0),None) eq_(resolve(grid,0,0),None)
# check every pixel of the nw symbol # check every pixel of the nw symbol
@ -128,7 +128,7 @@ def test_render_grid():
eq_(resolve(grid,23,10),expected) eq_(resolve(grid,23,10),expected)
eq_(resolve(grid,23,11),expected) eq_(resolve(grid,23,11),expected)
def test_render_grid2(): def test_render_grid_new():
""" test old against new""" """ test old against new"""
width,height = 256,256 width,height = 256,256
m = create_grid_map(width,height) m = create_grid_map(width,height)
@ -140,7 +140,7 @@ def test_render_grid2():
grid = mapnik.Grid(m.width,m.height,key='Name') grid = mapnik.Grid(m.width,m.height,key='Name')
mapnik.render_layer(m,grid,layer=0,fields=['Name']) mapnik.render_layer(m,grid,layer=0,fields=['Name'])
utf1 = grid.encode('utf',resolution=4) utf1 = grid.encode('utf',resolution=4)
eq_(utf1,grid_correct_new2,show_grids('new',utf1,grid_correct_new2)) eq_(utf1,grid_correct_new2,show_grids('new-markers',utf1,grid_correct_new2))
# check a full view is the same as a full image # check a full view is the same as a full image
grid_view = grid.view(0,0,width,height) grid_view = grid.view(0,0,width,height)
@ -164,7 +164,7 @@ def test_render_grid2():
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': [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' !! ## ', ' !!! ### ', ' !! ## ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' $$$ %% ', ' $$$ %%% ', ' $$ %% ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']} 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': [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' !! ## ', ' !!! ### ', ' !! ## ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' $$$ %% ', ' $$$ %%% ', ' $$ %% ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']}
grid_feat_id2 = {"data": {"1": {"Name": "South East"}, "2": {"Name": "South West"}, "3": {"Name": "North West"}, "4": {"Name": "North East"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !! ## ", " !!! ## ", " !! ## ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$ %% ", " $$ %% ", " $ % ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "3", "4", "2", "1"]} grid_feat_id2 = {"data": {"1": {"Name": "South East"}, "2": {"Name": "South West"}, "3": {"Name": "North West"}, "4": {"Name": "North East"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !! ## ", " !!! ### ", " !! ## ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$ %% ", " $$$ %% ", " $$ %% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "3", "4", "2", "1"]}
def test_render_grid3(): def test_render_grid3():
""" test using feature id""" """ test using feature id"""
@ -177,7 +177,7 @@ def test_render_grid3():
grid = mapnik.Grid(m.width,m.height,key='__id__') grid = mapnik.Grid(m.width,m.height,key='__id__')
mapnik.render_layer(m,grid,layer=0,fields=['__id__','Name']) mapnik.render_layer(m,grid,layer=0,fields=['__id__','Name'])
utf1 = grid.encode('utf',resolution=4) utf1 = grid.encode('utf',resolution=4)
eq_(utf1,grid_feat_id2,show_grids('id',utf1,grid_feat_id2)) eq_(utf1,grid_feat_id2,show_grids('id-markers',utf1,grid_feat_id2))
# check a full view is the same as a full image # check a full view is the same as a full image
grid_view = grid.view(0,0,width,height) grid_view = grid.view(0,0,width,height)
# for kicks check at full res too # for kicks check at full res too
@ -282,7 +282,7 @@ def test_line_rendering():
eq_(utf1,line_expected,show_grids('line',utf1,line_expected)) eq_(utf1,line_expected,show_grids('line',utf1,line_expected))
#open('test.json','w').write(json.dumps(grid.encode())) #open('test.json','w').write(json.dumps(grid.encode()))
point_expected = {"keys": ["", "3", "4", "2", "1"], "data": {"1": {"Name": "South East"}, "3": {"Name": "North West"}, "2": {"Name": "South West"}, "4": {"Name": "North East"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !!!! #### ", " !!!! #### ", " !!!! #### ", " !!!! #### ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$$$ %%%% ", " $$$$ %%%% ", " $$$$ %%%% ", " $$$$ %%%% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]} point_expected = {"data": {"1": {"Name": "South East"}, "2": {"Name": "South West"}, "3": {"Name": "North West"}, "4": {"Name": "North East"}}, "grid": [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " !!!! #### ", " !!!! #### ", " !!!! #### ", " !!!! #### ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " $$$$ %%%% ", " $$$$ %%%% ", " $$$$ %%%% ", " $$$$ %%%% ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], "keys": ["", "3", "4", "2", "1"]}
def test_point_symbolizer_grid(): def test_point_symbolizer_grid():
width,height = 256,256 width,height = 256,256

View file

@ -155,8 +155,9 @@ int main (int argc,char** argv)
{ {
std::clog << "found width of '" << w << "' and height of '" << h << "'\n"; std::clog << "found width of '" << w << "' and height of '" << h << "'\n";
} }
mapnik::image_32 im(w,h); // 10 pixel buffer to avoid edge clipping of 100% svg's
agg::rendering_buffer buf(im.raw_data(), w, h, w * 4); mapnik::image_32 im(w+10,h+10);
agg::rendering_buffer buf(im.raw_data(), im.width(), im.height(), im.width() * 4);
pixfmt pixf(buf); pixfmt pixf(buf);
renderer_base renb(pixf); renderer_base renb(pixf);
@ -165,7 +166,7 @@ int main (int argc,char** argv)
// center the svg marker on '0,0' // center the svg marker on '0,0'
agg::trans_affine mtx = agg::trans_affine_translation(-c.x,-c.y); agg::trans_affine mtx = agg::trans_affine_translation(-c.x,-c.y);
// render the marker at the center of the marker box // render the marker at the center of the marker box
mtx.translate(0.5 * w, 0.5 * h); mtx.translate(0.5 * im.width(), 0.5 * im.height());
mapnik::svg::vertex_stl_adapter<mapnik::svg::svg_path_storage> stl_storage((*marker.get_vector_data())->source()); mapnik::svg::vertex_stl_adapter<mapnik::svg::svg_path_storage> stl_storage((*marker.get_vector_data())->source());
mapnik::svg::svg_path_adapter svg_path(stl_storage); mapnik::svg::svg_path_adapter svg_path(stl_storage);