Merge commit 'cd064305fc8cf7fee1806e495d9016a5ce1e5d7f' into harfbuzz

Conflicts:
	src/cairo_renderer.cpp
This commit is contained in:
Hermann Kraus 2013-03-16 12:44:01 +01:00
commit 0c62b481f6
48 changed files with 386 additions and 104 deletions

13
.travis.yml Normal file
View file

@ -0,0 +1,13 @@
language: cpp
matrix:
include:
- compiler: clang
env: CXX_SCONS="-Qunused-arguments -fcolor-diagnostics" WARNING_CXXFLAGS="-Wno-unused-function -Wno-uninitialized -Wno-array-bounds -Wno-parentheses -Wno-char-subscripts -Wno-internal-linkage-in-inline"
before_install:
- echo 'yes' | sudo add-apt-repository ppa:mapnik/boost
- sudo apt-get update -qq
- sudo apt-get install -qq libboost-dev libboost-filesystem-dev libboost-program-options-dev libboost-python-dev libboost-regex-dev libboost-system-dev libboost-thread-dev python-nose libicu-dev libpng-dev libjpeg-dev libtiff-dev libz-dev libfreetype6-dev libxml2-dev libproj-dev libpq-dev libgdal-dev libcairomm-1.0-dev python-cairo-dev libsqlite3-dev
script: scons configure JOBS=2 FAST=True CXX="$CXX $CXX_SCONS" WARNING_CXXFLAGS=$WARNING_CXXFLAGS && sudo make install && make test || echo "Overall Test Exit Code: $?"

View file

@ -8,6 +8,10 @@ For a complete change history, see the git log.
## Future
- Added ability to pass a pre-created collision detector to the cairo renderer (#1444)
- Tolerance parameter is now supported for querying datasources at a given point (#503/#1499)
- Improved detection of newlines in CSV files - now more robust in the face of mixed newline types (#1497)
- Allow style level compositing operations to work outside of featureset extents across tiled requests (#1477)

View file

@ -1,12 +1,14 @@
```
_/ _/ _/ _/
_/_/ _/_/ _/_/_/ _/_/_/ _/_/_/ _/ _/
_/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/_/
_/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/
_/ _/ _/_/_/ _/_/_/ _/ _/ _/ _/ _/
_/
_/
```
_/ _/ _/ _/
_/_/ _/_/ _/_/_/ _/_/_/ _/_/_/ _/ _/
_/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/_/
_/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/
_/ _/ _/_/_/ _/_/_/ _/ _/ _/ _/ _/
_/
_/
```
[![Build Status](https://secure.travis-ci.org/mapnik/mapnik.png)](http://travis-ci.org/mapnik/mapnik)
# What is Mapnik?

View file

@ -172,7 +172,7 @@ void export_datasource()
.def("bind",&datasource::bind)
.def("fields",&fields)
.def("field_types",&field_types)
.def("features_at_point",&datasource::features_at_point)
.def("features_at_point",&datasource::features_at_point, (arg("coord"),arg("tolerance")=0))
.def("params",&datasource::params,return_value_policy<copy_const_reference>(),
"The configuration parameters of the data source. "
"These vary depending on the type of data source.")

View file

@ -199,6 +199,56 @@ void render6(const mapnik::Map& map, PycairoContext* context)
ren.apply();
}
void render_with_detector2(
const mapnik::Map& map,
PycairoContext* context,
boost::shared_ptr<mapnik::label_collision_detector4> detector)
{
python_unblock_auto_block b;
Cairo::RefPtr<Cairo::Context> c(new Cairo::Context(context->ctx));
mapnik::cairo_renderer<Cairo::Context> ren(map,c,detector);
ren.apply();
}
void render_with_detector3(
const mapnik::Map& map,
PycairoContext* context,
boost::shared_ptr<mapnik::label_collision_detector4> detector,
double scale_factor = 1.0,
unsigned offset_x = 0u,
unsigned offset_y = 0u)
{
python_unblock_auto_block b;
Cairo::RefPtr<Cairo::Context> c(new Cairo::Context(context->ctx));
mapnik::cairo_renderer<Cairo::Context> ren(map,c,detector,scale_factor,offset_x,offset_y);
ren.apply();
}
void render_with_detector4(
const mapnik::Map& map,
PycairoSurface* surface,
boost::shared_ptr<mapnik::label_collision_detector4> detector)
{
python_unblock_auto_block b;
Cairo::RefPtr<Cairo::Surface> s(new Cairo::Surface(surface->surface));
mapnik::cairo_renderer<Cairo::Surface> ren(map,s,detector);
ren.apply();
}
void render_with_detector5(
const mapnik::Map& map,
PycairoSurface* surface,
boost::shared_ptr<mapnik::label_collision_detector4> detector,
double scale_factor = 1.0,
unsigned offset_x = 0u,
unsigned offset_y = 0u)
{
python_unblock_auto_block b;
Cairo::RefPtr<Cairo::Surface> s(new Cairo::Surface(surface->surface));
mapnik::cairo_renderer<Cairo::Surface> ren(map,s,detector,scale_factor,offset_x,offset_y);
ren.apply();
}
#endif
@ -572,6 +622,65 @@ BOOST_PYTHON_MODULE(_mapnik)
">>> render(m,context)\n"
"\n"
);
def("render_with_detector", &render_with_detector2,
"\n"
"Render Map to Cairo Context using a pre-constructed detector.\n"
"\n"
"Usage:\n"
">>> from mapnik import Map, LabelCollisionDetector, render_with_detector, load_map\n"
">>> from cairo import SVGSurface, Context\n"
">>> surface = SVGSurface('image.svg', m.width, m.height)\n"
">>> ctx = Context(surface)\n"
">>> m = Map(256,256)\n"
">>> load_map(m,'mapfile.xml')\n"
">>> detector = LabelCollisionDetector(m)\n"
">>> render_with_detector(m, ctx, detector)\n"
);
def("render_with_detector", &render_with_detector3,
"\n"
"Render Map to Cairo Context using a pre-constructed detector, scale and offsets.\n"
"\n"
"Usage:\n"
">>> from mapnik import Map, LabelCollisionDetector, render_with_detector, load_map\n"
">>> from cairo import SVGSurface, Context\n"
">>> surface = SVGSurface('image.svg', m.width, m.height)\n"
">>> ctx = Context(surface)\n"
">>> m = Map(256,256)\n"
">>> load_map(m,'mapfile.xml')\n"
">>> detector = LabelCollisionDetector(m)\n"
">>> render_with_detector(m, ctx, detector, 1, 1, 1)\n"
);
def("render_with_detector", &render_with_detector4,
"\n"
"Render Map to Cairo Surface using a pre-constructed detector.\n"
"\n"
"Usage:\n"
">>> from mapnik import Map, LabelCollisionDetector, render_with_detector, load_map\n"
">>> from cairo import SVGSurface, Context\n"
">>> surface = SVGSurface('image.svg', m.width, m.height)\n"
">>> m = Map(256,256)\n"
">>> load_map(m,'mapfile.xml')\n"
">>> detector = LabelCollisionDetector(m)\n"
">>> render_with_detector(m, surface, detector)\n"
);
def("render_with_detector", &render_with_detector5,
"\n"
"Render Map to Cairo Surface using a pre-constructed detector, scale and offsets.\n"
"\n"
"Usage:\n"
">>> from mapnik import Map, LabelCollisionDetector, render_with_detector, load_map\n"
">>> from cairo import SVGSurface, Context\n"
">>> surface = SVGSurface('image.svg', m.width, m.height)\n"
">>> m = Map(256,256)\n"
">>> load_map(m,'mapfile.xml')\n"
">>> detector = LabelCollisionDetector(m)\n"
">>> render_with_detector(m, surface, detector, 1, 1, 1)\n"
);
#endif
def("scale_denominator", &scale_denominator,

View file

@ -143,7 +143,7 @@ private:
boost::shared_ptr<label_collision_detector4> detector_;
boost::scoped_ptr<rasterizer> ras_ptr;
box2d<double> query_extent_;
void setup(Map const &m);
void setup(Map const& m);
};
}

View file

@ -74,6 +74,7 @@ class MAPNIK_DECL cairo_renderer_base : private boost::noncopyable
{
protected:
cairo_renderer_base(Map const& m, Cairo::RefPtr<Cairo::Context> const& context, double scale_factor=1.0, unsigned offset_x=0, unsigned offset_y=0);
cairo_renderer_base(Map const& m, Cairo::RefPtr<Cairo::Context> const& context, boost::shared_ptr<label_collision_detector4> detector, double scale_factor=1.0, unsigned offset_x=0, unsigned offset_y=0);
public:
~cairo_renderer_base();
void start_map_processing(Map const& map);
@ -137,7 +138,7 @@ protected:
boost::shared_ptr<freetype_engine> font_engine_;
face_manager<freetype_engine> font_manager_;
cairo_face_manager face_manager_;
label_collision_detector4 detector_;
boost::shared_ptr<label_collision_detector4> detector_;
box2d<double> query_extent_;
};
@ -148,6 +149,7 @@ class MAPNIK_DECL cairo_renderer : public feature_style_processor<cairo_renderer
public:
typedef cairo_renderer_base processor_impl_type;
cairo_renderer(Map const& m, Cairo::RefPtr<T> const& surface, double scale_factor=1.0, unsigned offset_x=0, unsigned offset_y=0);
cairo_renderer(Map const& m, Cairo::RefPtr<T> const& surface, boost::shared_ptr<label_collision_detector4> detector, double scale_factor=1.0, unsigned offset_x=0, unsigned offset_y=0);
void end_map_processing(Map const& map);
};
}

View file

@ -115,7 +115,7 @@ public:
virtual void bind() const {}
virtual featureset_ptr features(query const& q) const = 0;
virtual featureset_ptr features_at_point(coord2d const& pt) const = 0;
virtual featureset_ptr features_at_point(coord2d const& pt, double tol = 0) const = 0;
virtual box2d<double> envelope() const = 0;
virtual boost::optional<geometry_t> get_geometry_type() const = 0;
virtual layer_descriptor get_descriptor() const = 0;

View file

@ -41,7 +41,7 @@ public:
void push(feature_ptr feature);
datasource::datasource_t type() const;
featureset_ptr features(const query& q) const;
featureset_ptr features_at_point(coord2d const& pt) const;
featureset_ptr features_at_point(coord2d const& pt, double tol = 0) const;
box2d<double> envelope() const;
boost::optional<geometry_t> get_geometry_type() const;
layer_descriptor get_descriptor() const;

View file

@ -33,11 +33,17 @@ struct raster
box2d<double> ext_;
image_data_32 data_;
bool premultiplied_alpha_;
raster(box2d<double> const& ext, unsigned width, unsigned height, bool premultiplied_alpha = false)
: ext_(ext),
data_(width,height),
premultiplied_alpha_(premultiplied_alpha)
{}
raster(box2d<double> const& ext,image_data_32 const& data, bool premultiplied_alpha = false)
: ext_(ext),
data_(data),
premultiplied_alpha_(premultiplied_alpha)
{}
};
}

View file

@ -959,7 +959,7 @@ mapnik::featureset_ptr csv_datasource::features(mapnik::query const& q) const
return boost::make_shared<mapnik::memory_featureset>(q.get_bbox(),features_);
}
mapnik::featureset_ptr csv_datasource::features_at_point(mapnik::coord2d const& pt) const
mapnik::featureset_ptr csv_datasource::features_at_point(mapnik::coord2d const& pt, double tol) const
{
if (!is_bound_) bind();

View file

@ -47,7 +47,7 @@ public:
mapnik::datasource::datasource_t type() const;
static const char * name();
mapnik::featureset_ptr features(mapnik::query const& q) const;
mapnik::featureset_ptr features_at_point(mapnik::coord2d const& pt) const;
mapnik::featureset_ptr features_at_point(mapnik::coord2d const& pt, double tol = 0) const;
mapnik::box2d<double> envelope() const;
boost::optional<mapnik::datasource::geometry_t> get_geometry_type() const;
mapnik::layer_descriptor get_descriptor() const;

View file

@ -246,7 +246,7 @@ featureset_ptr gdal_datasource::features(query const& q) const
nodata_value_));
}
featureset_ptr gdal_datasource::features_at_point(coord2d const& pt) const
featureset_ptr gdal_datasource::features_at_point(coord2d const& pt, double tol) const
{
if (! is_bound_) bind();

View file

@ -50,7 +50,7 @@ public:
mapnik::datasource::datasource_t type() const;
static const char * name();
mapnik::featureset_ptr features(mapnik::query const& q) const;
mapnik::featureset_ptr features_at_point(mapnik::coord2d const& pt) const;
mapnik::featureset_ptr features_at_point(mapnik::coord2d const& pt, double tol = 0) const;
mapnik::box2d<double> envelope() const;
boost::optional<mapnik::datasource::geometry_t> get_geometry_type() const;
mapnik::layer_descriptor get_descriptor() const;

View file

@ -217,7 +217,7 @@ mapnik::featureset_ptr geojson_datasource::features(mapnik::query const& q) cons
}
// FIXME
mapnik::featureset_ptr geojson_datasource::features_at_point(mapnik::coord2d const& pt) const
mapnik::featureset_ptr geojson_datasource::features_at_point(mapnik::coord2d const& pt, double tol) const
{
if (!is_bound_) bind();
throw mapnik::datasource_exception("GeoJSON Plugin: features_at_point is not supported yet");

View file

@ -60,7 +60,7 @@ public:
mapnik::datasource::datasource_t type() const;
static const char * name();
mapnik::featureset_ptr features(mapnik::query const& q) const;
mapnik::featureset_ptr features_at_point(mapnik::coord2d const& pt) const;
mapnik::featureset_ptr features_at_point(mapnik::coord2d const& pt, double tol = 0) const;
mapnik::box2d<double> envelope() const;
mapnik::layer_descriptor get_descriptor() const;
boost::optional<mapnik::datasource::geometry_t> get_geometry_type() const;

View file

@ -315,7 +315,7 @@ featureset_ptr geos_datasource::features(query const& q) const
desc_.get_encoding());
}
featureset_ptr geos_datasource::features_at_point(coord2d const& pt) const
featureset_ptr geos_datasource::features_at_point(coord2d const& pt, double tol) const
{
if (! is_bound_) bind();

View file

@ -50,7 +50,7 @@ public:
mapnik::datasource::datasource_t type() const;
static const char * name();
mapnik::featureset_ptr features(mapnik::query const& q) const;
mapnik::featureset_ptr features_at_point(mapnik::coord2d const& pt) const;
mapnik::featureset_ptr features_at_point(mapnik::coord2d const& pt, double tol = 0) const;
mapnik::box2d<double> envelope() const;
boost::optional<mapnik::datasource::geometry_t> get_geometry_type() const;
mapnik::layer_descriptor get_descriptor() const;

View file

@ -166,7 +166,7 @@ featureset_ptr kismet_datasource::features(query const& q) const
// return featureset_ptr();
}
featureset_ptr kismet_datasource::features_at_point(coord2d const& pt) const
featureset_ptr kismet_datasource::features_at_point(coord2d const& pt, double tol) const
{
if (! is_bound_) bind();

View file

@ -52,7 +52,7 @@ public:
datasource::datasource_t type() const;
static const char * name();
mapnik::featureset_ptr features(mapnik::query const& q) const;
mapnik::featureset_ptr features_at_point(mapnik::coord2d const& pt) const;
mapnik::featureset_ptr features_at_point(mapnik::coord2d const& pt, double tol = 0) const;
mapnik::box2d<double> envelope() const;
boost::optional<mapnik::datasource::geometry_t> get_geometry_type() const;
mapnik::layer_descriptor get_descriptor() const;

View file

@ -590,7 +590,7 @@ featureset_ptr occi_datasource::features(query const& q) const
row_prefetch_);
}
featureset_ptr occi_datasource::features_at_point(coord2d const& pt) const
featureset_ptr occi_datasource::features_at_point(coord2d const& pt, double tol) const
{
if (! is_bound_) bind();

View file

@ -51,7 +51,7 @@ public:
mapnik::datasource::datasource_t type() const;
static const char * name();
mapnik::featureset_ptr features(mapnik::query const& q) const;
mapnik::featureset_ptr features_at_point(mapnik::coord2d const& pt) const;
mapnik::featureset_ptr features_at_point(mapnik::coord2d const& pt, double tol = 0) const;
mapnik::box2d<double> envelope() const;
boost::optional<mapnik::datasource::geometry_t> get_geometry_type() const;
mapnik::layer_descriptor get_descriptor() const;

View file

@ -519,7 +519,7 @@ featureset_ptr ogr_datasource::features(query const& q) const
return featureset_ptr();
}
featureset_ptr ogr_datasource::features_at_point(coord2d const& pt) const
featureset_ptr ogr_datasource::features_at_point(coord2d const& pt, double tol) const
{
if (!is_bound_) bind();

View file

@ -52,7 +52,7 @@ public:
mapnik::datasource::datasource_t type() const;
static const char * name();
mapnik::featureset_ptr features(mapnik::query const& q) const;
mapnik::featureset_ptr features_at_point(mapnik::coord2d const& pt) const;
mapnik::featureset_ptr features_at_point(mapnik::coord2d const& pt, double tol = 0) const;
mapnik::box2d<double> envelope() const;
boost::optional<mapnik::datasource::geometry_t> get_geometry_type() const;
mapnik::layer_descriptor get_descriptor() const;

View file

@ -158,7 +158,7 @@ featureset_ptr osm_datasource::features(const query& q) const
desc_.get_encoding());
}
featureset_ptr osm_datasource::features_at_point(coord2d const& pt) const
featureset_ptr osm_datasource::features_at_point(coord2d const& pt, double tol) const
{
if (!is_bound_) bind();

View file

@ -58,7 +58,7 @@ public:
mapnik::datasource::datasource_t type() const;
static const char * name();
featureset_ptr features(const query& q) const;
featureset_ptr features_at_point(coord2d const& pt) const;
featureset_ptr features_at_point(coord2d const& pt, double tol = 0) const;
box2d<double> envelope() const;
boost::optional<mapnik::datasource::geometry_t> get_geometry_type() const;
layer_descriptor get_descriptor() const;

View file

@ -709,7 +709,7 @@ featureset_ptr postgis_datasource::features(const query& q) const
return featureset_ptr();
}
featureset_ptr postgis_datasource::features_at_point(coord2d const& pt) const
featureset_ptr postgis_datasource::features_at_point(coord2d const& pt, double tol) const
{
if (! is_bound_)
{
@ -778,7 +778,7 @@ featureset_ptr postgis_datasource::features_at_point(coord2d const& pt) const
}
}
box2d<double> box(pt.x, pt.y, pt.x, pt.y);
box2d<double> box(pt.x - tol, pt.y - tol, pt.x + tol, pt.y + tol);
std::string table_with_bbox = populate_tokens(table_, FMAX, box, 0, 0);
s << " FROM " << table_with_bbox;

View file

@ -63,7 +63,7 @@ public:
mapnik::datasource::datasource_t type() const;
static const char * name();
featureset_ptr features(const query& q) const;
featureset_ptr features_at_point(coord2d const& pt) const;
featureset_ptr features_at_point(coord2d const& pt, double tol = 0) const;
mapnik::box2d<double> envelope() const;
boost::optional<mapnik::datasource::geometry_t> get_geometry_type() const;
layer_descriptor get_descriptor() const;

View file

@ -189,7 +189,7 @@ mapnik::featureset_ptr python_datasource::features(mapnik::query const& q) const
return mapnik::featureset_ptr();
}
mapnik::featureset_ptr python_datasource::features_at_point(mapnik::coord2d const& pt) const
mapnik::featureset_ptr python_datasource::features_at_point(mapnik::coord2d const& pt, double tol) const
{
using namespace boost::python;

View file

@ -29,7 +29,7 @@ public:
// mandatory: function to query features by point (coord2d)
// not used by rendering, but available to calling applications
mapnik::featureset_ptr features_at_point(mapnik::coord2d const& pt) const;
mapnik::featureset_ptr features_at_point(mapnik::coord2d const& pt, double tol = 0) const;
// mandatory: return the box2d of the datasource
// called during rendering to determine if the layer should be processed

View file

@ -220,7 +220,7 @@ featureset_ptr raster_datasource::features(query const& q) const
}
}
featureset_ptr raster_datasource::features_at_point(coord2d const&) const
featureset_ptr raster_datasource::features_at_point(coord2d const&, double tol) const
{
MAPNIK_LOG_WARN(raster) << "raster_datasource: feature_at_point not supported";

View file

@ -49,7 +49,7 @@ public:
datasource::datasource_t type() const;
static const char * name();
mapnik::featureset_ptr features(const mapnik::query& q) const;
mapnik::featureset_ptr features_at_point(mapnik::coord2d const& pt) const;
mapnik::featureset_ptr features_at_point(mapnik::coord2d const& pt, double tol = 0) const;
mapnik::box2d<double> envelope() const;
boost::optional<mapnik::datasource::geometry_t> get_geometry_type() const;
mapnik::layer_descriptor get_descriptor() const;

View file

@ -201,7 +201,7 @@ featureset_ptr rasterlite_datasource::features(query const& q) const
return boost::make_shared<rasterlite_featureset>(open_dataset(), gq);
}
featureset_ptr rasterlite_datasource::features_at_point(coord2d const& pt) const
featureset_ptr rasterlite_datasource::features_at_point(coord2d const& pt, double tol) const
{
if (!is_bound_) bind();

View file

@ -50,7 +50,7 @@ public:
mapnik::datasource::datasource_t type() const;
static const char * name();
mapnik::featureset_ptr features(mapnik::query const& q) const;
mapnik::featureset_ptr features_at_point(mapnik::coord2d const& pt) const;
mapnik::featureset_ptr features_at_point(mapnik::coord2d const& pt, double tol = 0) const;
mapnik::box2d<double> envelope() const;
boost::optional<mapnik::datasource::geometry_t> get_geometry_type() const;
mapnik::layer_descriptor get_descriptor() const;

View file

@ -286,7 +286,7 @@ featureset_ptr shape_datasource::features(const query& q) const
}
}
featureset_ptr shape_datasource::features_at_point(coord2d const& pt) const
featureset_ptr shape_datasource::features_at_point(coord2d const& pt, double tol) const
{
if (!is_bound_) bind();

View file

@ -57,7 +57,7 @@ public:
datasource::datasource_t type() const;
static const char * name();
featureset_ptr features(const query& q) const;
featureset_ptr features_at_point(coord2d const& pt) const;
featureset_ptr features_at_point(coord2d const& pt, double tol = 0) const;
box2d<double> envelope() const;
boost::optional<mapnik::datasource::geometry_t> get_geometry_type() const;
layer_descriptor get_descriptor() const;

View file

@ -632,7 +632,7 @@ featureset_ptr sqlite_datasource::features(query const& q) const
return featureset_ptr();
}
featureset_ptr sqlite_datasource::features_at_point(coord2d const& pt) const
featureset_ptr sqlite_datasource::features_at_point(coord2d const& pt, double tol) const
{
if (! is_bound_) bind();

View file

@ -53,7 +53,7 @@ public:
datasource::datasource_t type() const;
static const char * name();
mapnik::featureset_ptr features(mapnik::query const& q) const;
mapnik::featureset_ptr features_at_point(mapnik::coord2d const& pt) const;
mapnik::featureset_ptr features_at_point(mapnik::coord2d const& pt, double tol = 0) const;
mapnik::box2d<double> envelope() const;
boost::optional<mapnik::datasource::geometry_t> get_geometry_type() const;
mapnik::layer_descriptor get_descriptor() const;

View file

@ -82,7 +82,7 @@ mapnik::featureset_ptr hello_datasource::features(mapnik::query const& q) const
return mapnik::featureset_ptr();
}
mapnik::featureset_ptr hello_datasource::features_at_point(mapnik::coord2d const& pt) const
mapnik::featureset_ptr hello_datasource::features_at_point(mapnik::coord2d const& pt, double tol) const
{
if (!is_bound_) bind();

View file

@ -39,7 +39,7 @@ public:
// mandatory: function to query features by point (coord2d)
// not used by rendering, but available to calling applications
mapnik::featureset_ptr features_at_point(mapnik::coord2d const& pt) const;
mapnik::featureset_ptr features_at_point(mapnik::coord2d const& pt, double tol = 0) const;
// mandatory: return the box2d of the datasource
// called during rendering to determine if the layer should be processed

View file

@ -38,6 +38,10 @@
// stl
#include <cmath>
// agg
#include "agg_rendering_buffer.h"
#include "agg_pixfmt_rgba.h"
namespace mapnik {
@ -66,10 +70,22 @@ void agg_renderer<T>::process(raster_symbolizer const& sym,
int raster_height = end_y - start_y;
if (raster_width > 0 && raster_height > 0)
{
image_data_32 target_data(raster_width,raster_height);
raster target(target_ext, target_data);
raster target(target_ext, raster_width,raster_height);
scaling_method_e scaling_method = sym.get_scaling_method();
double filter_radius = sym.calculate_filter_factor();
bool premultiply_source = !source->premultiplied_alpha_;
boost::optional<bool> is_premultiplied = sym.premultiplied();
if (is_premultiplied)
{
if (*is_premultiplied) premultiply_source = false;
else premultiply_source = true;
}
if (premultiply_source)
{
agg::rendering_buffer buffer(source->data_.getBytes(),source->data_.width(),source->data_.height(),source->data_.width() * 4);
agg::pixfmt_rgba32 pixf(buffer);
pixf.premultiply();
}
if (!prj_trans.equal())
{
double offset_x = ext.minx() - start_x;
@ -98,20 +114,9 @@ void agg_renderer<T>::process(raster_symbolizer const& sym,
filter_radius);
}
}
// handle whether to premultiply the source
// data before compositing
// first, default to what the data reports
bool premultiply_source = !source->premultiplied_alpha_;
// the, allow the user to override
boost::optional<bool> is_premultiplied = sym.premultiplied();
if (is_premultiplied)
{
if (*is_premultiplied) premultiply_source = false;
else premultiply_source = true;
}
composite(current_buffer_->data(), target.data_,
sym.comp_op(), sym.get_opacity(),
start_x, start_y, premultiply_source);
start_x, start_y, false);
}
}
}

View file

@ -57,6 +57,8 @@
#include "agg_conv_clip_polyline.h"
#include "agg_conv_clip_polygon.h"
#include "agg_conv_smooth_poly1.h"
#include "agg_rendering_buffer.h"
#include "agg_pixfmt_rgba.h"
// markers
#include "agg_path_storage.h"
@ -760,7 +762,29 @@ cairo_renderer_base::cairo_renderer_base(Map const& m,
font_engine_(boost::make_shared<freetype_engine>()),
font_manager_(*font_engine_),
face_manager_(font_engine_),
detector_(box2d<double>(-m.buffer_size() ,-m.buffer_size() , m.width() + m.buffer_size() ,m.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())))
{
MAPNIK_LOG_DEBUG(cairo_renderer) << "cairo_renderer_base: Scale=" << m.scale();
}
cairo_renderer_base::cairo_renderer_base(Map const& m,
Cairo::RefPtr<Cairo::Context> const& context,
boost::shared_ptr<label_collision_detector4> detector,
double scale_factor,
unsigned offset_x,
unsigned offset_y)
: m_(m),
context_(context),
width_(m.width()),
height_(m.height()),
scale_factor_(scale_factor),
t_(m.width(),m.height(),m.get_current_extent(),offset_x,offset_y),
font_engine_(boost::make_shared<freetype_engine>()),
font_manager_(*font_engine_),
face_manager_(font_engine_),
detector_(detector)
{
MAPNIK_LOG_DEBUG(cairo_renderer) << "cairo_renderer_base: Scale=" << m.scale();
}
@ -775,6 +799,16 @@ cairo_renderer<Cairo::Surface>::cairo_renderer(Map const& m, Cairo::RefPtr<Cairo
: feature_style_processor<cairo_renderer>(m,scale_factor),
cairo_renderer_base(m,Cairo::Context::create(surface),scale_factor,offset_x,offset_y) {}
template <>
cairo_renderer<Cairo::Context>::cairo_renderer(Map const& m, Cairo::RefPtr<Cairo::Context> const& context, boost::shared_ptr<label_collision_detector4> detector, double scale_factor, unsigned offset_x, unsigned offset_y)
: feature_style_processor<cairo_renderer>(m,scale_factor),
cairo_renderer_base(m,context,detector,scale_factor,offset_x,offset_y) {}
template <>
cairo_renderer<Cairo::Surface>::cairo_renderer(Map const& m, Cairo::RefPtr<Cairo::Surface> const& surface, boost::shared_ptr<label_collision_detector4> detector, double scale_factor, unsigned offset_x, unsigned offset_y)
: feature_style_processor<cairo_renderer>(m,scale_factor),
cairo_renderer_base(m,Cairo::Context::create(surface),detector,scale_factor,offset_x,offset_y) {}
cairo_renderer_base::~cairo_renderer_base() {}
void cairo_renderer_base::start_map_processing(Map const& map)
@ -820,7 +854,7 @@ void cairo_renderer_base::start_layer_processing(layer const& lay, box2d<double>
if (lay.clear_label_cache())
{
detector_.clear();
detector_->clear();
}
query_extent_ = query_extent;
}
@ -1212,12 +1246,12 @@ void cairo_renderer_base::process(point_symbolizer const& sym,
label_ext *= tr;
label_ext *= agg::trans_affine_translation(x,y);
if (sym.get_allow_overlap() ||
detector_.has_placement(label_ext))
detector_->has_placement(label_ext))
{
render_marker(pixel_position(x,y),**marker, tr, sym.get_opacity());
if (!sym.get_ignore_placement())
detector_.insert(label_ext);
detector_->insert(label_ext);
}
}
}
@ -1231,7 +1265,7 @@ void cairo_renderer_base::process(shield_symbolizer const& sym,
sym, feature, prj_trans,
width_, height_,
scale_factor_,
t_, font_manager_, detector_, query_extent_);
t_, font_manager_, *detector_, query_extent_);
cairo_context context(context_);
context.set_operator(sym.comp_op());
@ -1422,10 +1456,22 @@ void cairo_renderer_base::process(raster_symbolizer const& sym,
int raster_height = end_y - start_y;
if (raster_width > 0 && raster_height > 0)
{
image_data_32 target_data(raster_width,raster_height);
raster target(target_ext, target_data);
raster target(target_ext, raster_width,raster_height);
scaling_method_e scaling_method = sym.get_scaling_method();
double filter_radius = sym.calculate_filter_factor();
bool premultiply_source = !source->premultiplied_alpha_;
boost::optional<bool> is_premultiplied = sym.premultiplied();
if (is_premultiplied)
{
if (*is_premultiplied) premultiply_source = false;
else premultiply_source = true;
}
if (premultiply_source)
{
agg::rendering_buffer buffer(source->data_.getBytes(),source->data_.width(),source->data_.height(),source->data_.width() * 4);
agg::pixfmt_rgba32 pixf(buffer);
pixf.premultiply();
}
if (!prj_trans.equal())
{
double offset_x = ext.minx() - start_x;
@ -1683,7 +1729,7 @@ void cairo_renderer_base::process(markers_symbolizer const& sym,
box2d<double> bbox = marker_ellipse.bounding_box();
dispatch_type dispatch(context, marker_ellipse, result?attributes:(*stock_vector_marker)->attributes(),
detector_, sym, bbox, marker_tr, scale_factor_);
*detector_, sym, bbox, marker_tr, scale_factor_);
vertex_converter<box2d<double>, dispatch_type, markers_symbolizer,
CoordTransform, proj_transform, agg::trans_affine, conv_types>
converter(query_extent_, dispatch, sym, t_, prj_trans, marker_tr, scale_factor_);
@ -1711,7 +1757,7 @@ void cairo_renderer_base::process(markers_symbolizer const& sym,
bool result = push_explicit_style( (*stock_vector_marker)->attributes(), attributes, sym);
dispatch_type dispatch(context, **stock_vector_marker, result?attributes:(*stock_vector_marker)->attributes(),
detector_, sym, bbox, tr, scale_factor_);
*detector_, sym, bbox, tr, scale_factor_);
vertex_converter<box2d<double>, dispatch_type, markers_symbolizer,
CoordTransform, proj_transform, agg::trans_affine, conv_types>
converter(query_extent_, dispatch, sym, t_, prj_trans, tr, scale_factor_);
@ -1743,7 +1789,7 @@ void cairo_renderer_base::process(markers_symbolizer const& sym,
if ( marker )
{
dispatch_type dispatch(context, *marker,
detector_, sym, bbox, tr, scale_factor_);
*detector_, sym, bbox, tr, scale_factor_);
vertex_converter<box2d<double>, dispatch_type, markers_symbolizer,
CoordTransform, proj_transform, agg::trans_affine, conv_types>
@ -1779,7 +1825,7 @@ void cairo_renderer_base::process(text_symbolizer const& sym,
sym, feature, prj_trans,
width_, height_,
scale_factor_,
t_, font_manager_, detector_, query_extent_);
t_, font_manager_, *detector_, query_extent_);
cairo_context context(context_);
context.set_operator(sym.comp_op());

View file

@ -263,12 +263,12 @@ void scale_image_agg(Image & target,
double filter_radius,
double ratio)
{
// TODO - should all types here be *_pre ?
// "the image filters should work namely in the premultiplied color space"
// http://old.nabble.com/Re:--AGG--Basic-image-transformations-p1110665.html
typedef agg::pixfmt_rgba32 pixfmt;
// "Yes, you need to use premultiplied images only. Only in this case the simple weighted averaging works correctly in the image fitering."
// http://permalink.gmane.org/gmane.comp.graphics.agg/3443
typedef agg::pixfmt_rgba32_pre pixfmt_pre;
typedef agg::renderer_base<pixfmt_pre> renderer_base;
typedef agg::renderer_base<pixfmt_pre> renderer_base_pre;
// define some stuff we'll use soon
agg::rasterizer_scanline_aa<> ras;
@ -278,15 +278,15 @@ void scale_image_agg(Image & target,
// initialize source AGG buffer
agg::rendering_buffer rbuf_src((unsigned char*)source.getBytes(), source.width(), source.height(), source.width() * 4);
pixfmt pixf_src(rbuf_src);
typedef agg::image_accessor_clone<pixfmt> img_src_type;
pixfmt_pre pixf_src(rbuf_src);
typedef agg::image_accessor_clone<pixfmt_pre> img_src_type;
img_src_type img_src(pixf_src);
// initialize destination AGG buffer (with transparency)
agg::rendering_buffer rbuf_dst((unsigned char*)target.getBytes(), target.width(), target.height(), target.width() * 4);
pixfmt_pre pixf_dst(rbuf_dst);
renderer_base rb_dst(pixf_dst);
rb_dst.clear(agg::rgba(0, 0, 0, 0));
renderer_base_pre rb_dst_pre(pixf_dst);
rb_dst_pre.clear(agg::rgba(0, 0, 0, 0));
// create a scaling matrix
agg::trans_affine img_mtx;
@ -311,7 +311,7 @@ void scale_image_agg(Image & target,
{
typedef agg::span_image_filter_rgba_nn<img_src_type, interpolator_type> span_gen_type;
span_gen_type sg(img_src, interpolator);
agg::render_scanlines_aa(ras, sl, rb_dst, sa, sg);
agg::render_scanlines_aa(ras, sl, rb_dst_pre, sa, sg);
return;
}
case SCALING_BILINEAR:
@ -348,9 +348,21 @@ void scale_image_agg(Image & target,
case SCALING_BLACKMAN:
filter.calculate(agg::image_filter_blackman(filter_radius), true); break;
}
// details on various resampling considerations
// http://old.nabble.com/Re%3A-Newbie---texture-p5057255.html
// high quality resampler
//typedef agg::span_image_resample_rgba_affine<img_src_type> span_gen_type;
// faster, lower quality
//typedef agg::span_image_filter_rgba<img_src_type,interpolator_type> span_gen_type;
// local, modified agg::span_image_resample_rgba_affine
// not convinced we need this
// https://github.com/mapnik/mapnik/issues/1489
typedef mapnik::span_image_resample_rgba_affine<img_src_type> span_gen_type;
span_gen_type sg(img_src, interpolator, filter);
agg::render_scanlines_aa(ras, sl, rb_dst, sa, sg);
agg::render_scanlines_aa(ras, sl, rb_dst_pre, sa, sg);
}
template void scale_image_agg<image_data_32> (image_data_32& target,const image_data_32& source, scaling_method_e scaling_method, double scale_factor, double x_off_f, double y_off_f, double filter_radius, double ratio);

View file

@ -548,24 +548,24 @@ featureset_ptr Map::query_point(unsigned index, double x, double y) const
{
throw std::runtime_error("query_point: could not project x,y into layer srs");
}
// TODO - pass tolerance to features_at_point as well
featureset_ptr fs = ds->features_at_point(mapnik::coord2d(x,y));
// calculate default tolerance
mapnik::box2d<double> map_ex = current_extent_;
if (maximum_extent_)
{
map_ex.clip(*maximum_extent_);
}
if (!prj_trans.backward(map_ex,PROJ_ENVELOPE_POINTS))
{
std::ostringstream s;
s << "query_point: could not project map extent '" << map_ex
<< "' into layer srs for tolerance calculation";
throw std::runtime_error(s.str());
}
double tol = (map_ex.maxx() - map_ex.minx()) / width_ * 3;
featureset_ptr fs = ds->features_at_point(mapnik::coord2d(x,y), tol);
MAPNIK_LOG_DEBUG(map) << "map: Query at point tol=" << tol << "(" << x << "," << y << ")";
if (fs)
{
mapnik::box2d<double> map_ex = current_extent_;
if (maximum_extent_)
{
map_ex.clip(*maximum_extent_);
}
if (!prj_trans.backward(map_ex,PROJ_ENVELOPE_POINTS))
{
std::ostringstream s;
s << "query_point: could not project map extent '" << map_ex
<< "' into layer srs for tolerance calculation";
throw std::runtime_error(s.str());
}
double tol = (map_ex.maxx() - map_ex.minx()) / width_ * 3;
MAPNIK_LOG_DEBUG(map) << "map: Query at point tol=" << tol << "(" << x << "," << y << ")";
return boost::make_shared<filter_featureset<hit_test_filter> >(fs,
hit_test_filter(x,y,tol));
}

View file

@ -86,7 +86,7 @@ featureset_ptr memory_datasource::features(const query& q) const
}
featureset_ptr memory_datasource::features_at_point(coord2d const& pt) const
featureset_ptr memory_datasource::features_at_point(coord2d const& pt, double tol) const
{
box2d<double> box = box2d<double>(pt.x, pt.y, pt.x, pt.y);

View file

@ -0,0 +1,43 @@
#!/usr/bin/env python
from nose.tools import *
from utilities import execution_path
import os, mapnik
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_query_tolerance():
srs = '+init=epsg:4326'
lyr = mapnik.Layer('test')
lyr.datasource = mapnik.Shapefile(file='../data/shp/arrows.shp')
lyr.srs = srs
_width = 256
_map = mapnik.Map(_width,_width, srs)
_map.layers.append(lyr)
# zoom determines tolerance
_map.zoom_all()
_map_env = _map.envelope()
tol = (_map_env.maxx - _map_env.minx) / _width * 3
# 0.046875 for arrows.shp and zoom_all
assert tol == 0.046875
# check point really exists
x, y = 2.0, 4.0
features = _map.query_point(0,x,y).features
assert len(features) == 1
# check inside tolerance limit
x = 2.0 + tol * 0.9
features = _map.query_point(0,x,y).features
assert len(features) == 1
# check outside tolerance limit
x = 2.0 + tol * 1.1
features = _map.query_point(0,x,y).features
assert len(features) == 0

View file

@ -103,6 +103,22 @@ def test_load_save_map():
if not 'Could not create datasource' in str(e):
raise RuntimeError(str(e))
def pixel2rgba(pixel):
alpha = (pixel >> 24) & 0xff
red = pixel & 0xff
green = (pixel >> 8) & 0xff
blue = (pixel >> 16) & 0xff
return 'rgba(%s,%s,%s,%s)' % (red,green,blue,alpha)
def get_unique_colors(im):
pixels = []
for x in range(im.width()):
for y in range(im.height()):
pixel = im.get_pixel(x,y)
if pixel not in pixels:
pixels.append(pixel)
return map(pixel2rgba,pixels)
def test_raster_with_alpha_blends_correctly_with_background():
WIDTH = 500
HEIGHT = 500
@ -135,7 +151,7 @@ def test_raster_with_alpha_blends_correctly_with_background():
mapnik.render(map, mim)
imdata = mim.tostring()
# All white is expected
assert contains_word('\xff\xff\xff\xff', imdata)
eq_(contains_word('\xff\xff\xff\xff', imdata),True,'Image expected to contain true white, instead found %s' % get_unique_colors(mim))
def test_raster_warping():
lyrSrs = "+init=epsg:32630"

View file

@ -8,6 +8,8 @@ import sys
import os.path
from compare import compare, summary, fail
visual_output_dir = "/tmp/mapnik-visual-images"
defaults = {
'sizes': [(500, 100)]
}
@ -72,21 +74,25 @@ files = [
]
def render(filename, width, height, bbox, quiet=False):
if not quiet:
print "\"%s\" with size %dx%d ..." % (filename, width, height),
m = mapnik.Map(width, height)
expected = os.path.join(dirname, "images", '%s-%d-reference.png' % (filename, width))
actual = os.path.join("/tmp/mapnik-visual-images", '%s-%d-agg.png' % (filename, width))
actual = '%s-%d' % (filename, width)
try:
mapnik.load_map(m, os.path.join(dirname, "styles", "%s.xml" % filename), False)
if bbox is not None:
m.zoom_to_box(bbox)
else:
m.zoom_all()
if not os.path.exists('/tmp/mapnik-visual-images'):
os.makedirs('/tmp/mapnik-visual-images')
mapnik.render_to_file(m, actual)
diff = compare(actual, expected)
except Exception, e:
sys.stderr.write(e.message + '\n')
fail(actual,expected,str(e.message))
return
actual_agg = os.path.join(visual_output_dir, '%s-agg.png' % actual)
if not quiet:
print "\"%s\" with size %dx%d with agg..." % (filename, width, height),
try:
mapnik.render_to_file(m, actual_agg)
diff = compare(actual_agg, expected)
if not quiet:
if diff > 0:
print '\x1b[31m✘\x1b[0m (\x1b[34m%u different pixels\x1b[0m)' % diff
@ -94,7 +100,22 @@ def render(filename, width, height, bbox, quiet=False):
print '\x1b[32m✓\x1b[0m'
except Exception, e:
sys.stderr.write(e.message + '\n')
fail(actual,expected,str(e.message))
fail(actual_agg,expected,str(e.message))
if 'tiff' in actual:
actual_cairo = os.path.join(visual_output_dir, '%s-cairo.png' % actual)
if not quiet:
print "\"%s\" with size %dx%d with cairo..." % (filename, width, height),
try:
mapnik.render_to_file(m, actual_cairo,'ARGB32')
diff = compare(actual_cairo, expected)
if not quiet:
if diff > 0:
print '\x1b[31m✘\x1b[0m (\x1b[34m%u different pixels\x1b[0m)' % diff
else:
print '\x1b[32m✓\x1b[0m'
except Exception, e:
sys.stderr.write(e.message + '\n')
fail(actual_cairo,expected,str(e.message))
return m
if __name__ == "__main__":
@ -119,6 +140,9 @@ if __name__ == "__main__":
for name in sys.argv[1:]:
active.append({"name": name})
if not os.path.exists(visual_output_dir):
os.makedirs(visual_output_dir)
if 'osm' in mapnik.DatasourceCache.plugin_names():
for f in active:
config = dict(defaults)