Compare commits

...

7 commits

Author SHA1 Message Date
Dane Springmeyer
99071d1253 return correct height for svg vector 2013-05-21 19:40:33 -07:00
Dane Springmeyer
8aae463c10 merge with master 2013-05-21 19:35:48 -07:00
Dane Springmeyer
6b33feceab remove build-in image marker - only added for testing 2013-05-08 19:59:12 -07:00
Dane Springmeyer
006aadcb89 marker_cache: support getting objects back out of cache from python and putting new objects for existing keys 2013-05-08 18:18:18 -07:00
Dane Springmeyer
a50051bded threaded test of read/write to marker cache 2013-05-08 11:22:24 -07:00
Dane Springmeyer
8abda7c7ae further work on marker cache api, now from python 2013-05-08 09:25:19 -07:00
Dane Springmeyer
3a44fecf6f first push refactoring marker api 2013-05-06 23:18:45 -07:00
16 changed files with 651 additions and 233 deletions

View file

@ -0,0 +1,99 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2006 Artem Pavlenko, Jean-Francois Doyon
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
#include <mapnik/utils.hpp>
#include <mapnik/marker.hpp>
#include <mapnik/marker_cache.hpp>
#include <mapnik/graphics.hpp>
// boost
#include <boost/make_shared.hpp>
#include <boost/python.hpp>
#include <boost/noncopyable.hpp>
namespace {
bool add_marker_from_image(mapnik::marker_cache & cache, std::string const& uri, mapnik::image_32 const& im)
{
boost::optional<mapnik::image_ptr> imagep(boost::make_shared<mapnik::image_data_32>(im.data()));
return cache.insert_marker(uri,boost::make_shared<mapnik::marker>(imagep),true);
}
bool add_marker_from_svg(mapnik::marker_cache & cache, std::string const& uri, mapnik::svg_storage_type const& svg)
{
mapnik::svg_path_ptr marker_path(boost::make_shared<mapnik::svg_storage_type>(svg));
return cache.insert_marker(uri,boost::make_shared<mapnik::marker>(marker_path),true);
}
boost::python::object get_marker(boost::shared_ptr<mapnik::marker_cache> const& cache, std::string const& uri)
{
mapnik::marker_cache::iterator_type itr = cache->search(uri);
mapnik::marker_cache::iterator_type end = cache->end();
if (itr != end)
{
if (itr->second->is_bitmap())
{
mapnik::image_data_32 const& im = *itr->second->get_bitmap_data()->get();
return boost::python::object(boost::make_shared<mapnik::image_32>(im));
}
return boost::python::object(*(itr->second->get_vector_data()));
}
return boost::python::object();
}
boost::python::list get_keys(boost::shared_ptr<mapnik::marker_cache> const& cache)
{
boost::python::list l;
mapnik::marker_cache::iterator_type itr = cache->begin();
mapnik::marker_cache::iterator_type end = cache->end();
for (;itr != end; ++itr)
{
l.append(itr->first);
}
return l;
}
}
void export_marker_cache()
{
using mapnik::marker_cache;
using mapnik::singleton;
using mapnik::CreateUsingNew;
using namespace boost::python;
class_<singleton<marker_cache,CreateUsingNew>,boost::noncopyable>("Singleton",no_init)
.def("instance",&singleton<marker_cache,CreateUsingNew>::instance,
return_value_policy<reference_existing_object>())
.staticmethod("instance")
;
class_<marker_cache,bases<singleton<marker_cache,CreateUsingNew> >,
boost::noncopyable>("MarkerCache",no_init)
.def("clear",&marker_cache::clear)
.def("remove",&marker_cache::remove)
.def("size",&marker_cache::size)
.def("put",&add_marker_from_image)
.def("put",&add_marker_from_svg)
.def("get",&get_marker)
.def("keys",&get_keys)
;
}

View file

@ -34,6 +34,7 @@ void export_envelope();
void export_query(); void export_query();
void export_geometry(); void export_geometry();
void export_palette(); void export_palette();
void export_svg();
void export_image(); void export_image();
void export_image_view(); void export_image_view();
void export_gamma_method(); void export_gamma_method();
@ -64,6 +65,7 @@ void export_text_placement();
void export_shield_symbolizer(); void export_shield_symbolizer();
void export_debug_symbolizer(); void export_debug_symbolizer();
void export_font_engine(); void export_font_engine();
void export_marker_cache();
void export_projection(); void export_projection();
void export_proj_transform(); void export_proj_transform();
void export_view_transform(); void export_view_transform();
@ -441,6 +443,7 @@ BOOST_PYTHON_MODULE(_mapnik)
export_color(); export_color();
export_envelope(); export_envelope();
export_palette(); export_palette();
export_svg();
export_image(); export_image();
export_image_view(); export_image_view();
export_gamma_method(); export_gamma_method();
@ -466,6 +469,7 @@ BOOST_PYTHON_MODULE(_mapnik)
export_shield_symbolizer(); export_shield_symbolizer();
export_debug_symbolizer(); export_debug_symbolizer();
export_font_engine(); export_font_engine();
export_marker_cache();
export_projection(); export_projection();
export_proj_transform(); export_proj_transform();
export_view_transform(); export_view_transform();

View file

@ -0,0 +1,62 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2006 Artem Pavlenko, Jean-Francois Doyon
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
// boost
#include <boost/python.hpp>
#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
#include <boost/make_shared.hpp>
// mapnik
#include <mapnik/marker.hpp>
namespace {
mapnik::svg_path_ptr open_from_file(std::string const& filename)
{
return mapnik::read_svg_marker(filename);
}
mapnik::svg_path_ptr fromstring(std::string const& svg)
{
return mapnik::read_svg_marker(svg,true);
}
}
void export_svg()
{
using namespace boost::python;
using mapnik::svg_storage_type;
class_<svg_storage_type,boost::shared_ptr<svg_storage_type> >("SVG","This class represents an svg object.",no_init)
.def("width",&svg_storage_type::width)
.def("height",&svg_storage_type::height)
.def("extent",make_function(&svg_storage_type::bounding_box,
return_value_policy<copy_const_reference>()))
.def("open",&open_from_file)
.staticmethod("open")
.def("fromstring",&fromstring)
.staticmethod("fromstring")
;
}

View file

@ -57,6 +57,7 @@ private:
public: public:
image_32(int width,int height); image_32(int width,int height);
image_32(image_32 const& rhs); image_32(image_32 const& rhs);
explicit image_32(image_data_32 const& rhs);
#ifdef HAVE_CAIRO #ifdef HAVE_CAIRO
explicit image_32(cairo_surface_ptr const& surface); explicit image_32(cairo_surface_ptr const& surface);
#endif #endif

View file

@ -62,6 +62,19 @@ struct MAPNIK_DECL image_reader : private mapnik::noncopyable
virtual ~image_reader() {} virtual ~image_reader() {}
}; };
struct image_reader_guard
{
image_reader_guard(image_reader * reader)
: r_(reader) {}
~image_reader_guard()
{
if (r_) delete r_;
}
image_reader * r_;
};
bool register_image_reader(std::string const& type,image_reader* (*)(std::string const&)); bool register_image_reader(std::string const& type,image_reader* (*)(std::string const&));
bool register_image_reader(std::string const& type,image_reader* (*)(char const*, std::size_t)); bool register_image_reader(std::string const& type,image_reader* (*)(char const*, std::size_t));

View file

@ -27,18 +27,13 @@
#include <mapnik/global.hpp> #include <mapnik/global.hpp>
#include <mapnik/image_data.hpp> #include <mapnik/image_data.hpp>
#include <mapnik/svg/svg_path_attributes.hpp> #include <mapnik/svg/svg_path_attributes.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_storage.hpp>
#include <mapnik/noncopyable.hpp> #include <mapnik/noncopyable.hpp>
// agg
#include "agg_path_storage.h"
// boost // boost
#include <boost/unordered_map.hpp>
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <boost/make_shared.hpp>
// stl // stl
#include <cassert> #include <cassert>
@ -51,6 +46,14 @@ 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> svg_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;
class marker;
typedef boost::shared_ptr<marker> marker_ptr;
mapnik::svg_path_ptr read_svg_marker(std::string const& uri, bool from_string=false);
mapnik::image_ptr read_bitmap_marker(std::string const& uri, bool from_string=false);
/** /**
* 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.
@ -58,96 +61,24 @@ typedef boost::shared_ptr<image_data_32> image_ptr;
class marker: private mapnik::noncopyable class marker: private mapnik::noncopyable
{ {
public: public:
marker() marker();
{ marker(boost::optional<mapnik::image_ptr> const& data);
// create default OGC 4x4 black pixel marker(boost::optional<mapnik::svg_path_ptr> const& data);
bitmap_data_ = boost::optional<mapnik::image_ptr>(boost::make_shared<image_data_32>(4,4)); marker(marker const& rhs);
(*bitmap_data_)->set(0xff000000); box2d<double> bounding_box() const;
} double width() const;
double height() const;
marker(boost::optional<mapnik::image_ptr> const& data) bool is_bitmap() const;
: bitmap_data_(data) bool is_vector() const;
{ boost::optional<mapnik::image_ptr> get_bitmap_data() const;
boost::optional<mapnik::svg_path_ptr> get_vector_data() const;
}
marker(boost::optional<mapnik::svg_path_ptr> const& data)
: vector_data_(data)
{
}
marker(marker const& rhs)
: bitmap_data_(rhs.bitmap_data_),
vector_data_(rhs.vector_data_)
{}
box2d<double> bounding_box() const
{
if (is_vector())
{
return (*vector_data_)->bounding_box();
}
if (is_bitmap())
{
double width = (*bitmap_data_)->width();
double height = (*bitmap_data_)->height();
return box2d<double>(0, 0, width, height);
}
return box2d<double>();
}
inline double width() const
{
if (is_bitmap())
{
return (*bitmap_data_)->width();
}
else if (is_vector())
{
return (*vector_data_)->bounding_box().width();
}
return 0;
}
inline double height() const
{
if (is_bitmap())
{
return (*bitmap_data_)->height();
}
else if (is_vector())
{
return (*vector_data_)->bounding_box().height();
}
return 0;
}
inline bool is_bitmap() const
{
return bitmap_data_;
}
inline bool is_vector() const
{
return vector_data_;
}
boost::optional<mapnik::image_ptr> get_bitmap_data() const
{
return bitmap_data_;
}
boost::optional<mapnik::svg_path_ptr> get_vector_data() const
{
return vector_data_;
}
private: private:
boost::optional<mapnik::image_ptr> bitmap_data_; boost::optional<mapnik::image_ptr> bitmap_data_;
boost::optional<mapnik::svg_path_ptr> vector_data_; boost::optional<mapnik::svg_path_ptr> vector_data_;
}; };
} }
#endif // MAPNIK_MARKER_HPP #endif // MAPNIK_MARKER_HPP

View file

@ -24,6 +24,7 @@
#define MAPNIK_MARKER_CACHE_HPP #define MAPNIK_MARKER_CACHE_HPP
// mapnik // mapnik
#include <mapnik/marker.hpp>
#include <mapnik/utils.hpp> #include <mapnik/utils.hpp>
#include <mapnik/config.hpp> #include <mapnik/config.hpp>
#include <mapnik/noncopyable.hpp> #include <mapnik/noncopyable.hpp>
@ -36,11 +37,6 @@
namespace mapnik namespace mapnik
{ {
class marker;
typedef boost::shared_ptr<marker> marker_ptr;
class MAPNIK_DECL marker_cache : class MAPNIK_DECL marker_cache :
public singleton <marker_cache, CreateUsingNew>, public singleton <marker_cache, CreateUsingNew>,
private mapnik::noncopyable private mapnik::noncopyable
@ -49,15 +45,24 @@ class MAPNIK_DECL marker_cache :
private: private:
marker_cache(); marker_cache();
~marker_cache(); ~marker_cache();
bool insert_marker(std::string const& key, marker_ptr path);
boost::unordered_map<std::string,marker_ptr> marker_cache_; boost::unordered_map<std::string,marker_ptr> marker_cache_;
bool insert_svg(std::string const& name, std::string const& svg_string);
boost::unordered_map<std::string,std::string> svg_cache_;
public: public:
typedef boost::unordered_map<std::string, marker_ptr>::const_iterator iterator_type;
typedef boost::unordered_map<std::string, marker_ptr>::size_type size_type;
bool insert_marker(std::string const& key, marker_ptr path, bool override=false);
std::string known_svg_prefix_; std::string known_svg_prefix_;
std::string known_image_prefix_;
void init();
bool is_uri(std::string const& path); bool is_uri(std::string const& path);
boost::optional<marker_ptr> find(std::string const& key, bool update_cache = false); bool is_svg_uri(std::string const& path);
bool is_image_uri(std::string const& path);
boost::optional<marker_ptr> find(std::string const& uri, bool update_cache = false);
iterator_type search(std::string const& uri) const { return marker_cache_.find(uri); }
void clear(); void clear();
bool remove(std::string const& uri);
size_type size() const { return marker_cache_.size(); }
iterator_type begin() const { return marker_cache_.begin(); }
iterator_type end() const { return marker_cache_.end(); }
}; };
} }

View file

@ -24,9 +24,7 @@
#define MAPNIK_SVG_PARSER_HPP #define MAPNIK_SVG_PARSER_HPP
// mapnik // mapnik
#include <mapnik/svg/svg_path_attributes.hpp>
#include <mapnik/svg/svg_converter.hpp> #include <mapnik/svg/svg_converter.hpp>
#include <mapnik/svg/svg_path_adapter.hpp>
#include <mapnik/gradient.hpp> #include <mapnik/gradient.hpp>
#include <mapnik/noncopyable.hpp> #include <mapnik/noncopyable.hpp>

View file

@ -25,19 +25,28 @@
// mapnik // mapnik
#include <mapnik/box2d.hpp> #include <mapnik/box2d.hpp>
#include <mapnik/noncopyable.hpp>
namespace mapnik { namespace mapnik {
namespace svg { namespace svg {
template <typename VertexSource ,typename AttributeSource> template <typename VertexSource,typename AttributeSource>
class svg_storage : mapnik::noncopyable class svg_storage
{ {
public: public:
svg_storage() : svg_storage() :
source_(),
attributes_(),
bounding_box_(),
svg_width_(0), svg_width_(0),
svg_height_(0) {} svg_height_(0) {}
svg_storage(svg_storage const& rhs)
: source_(rhs.source_),
attributes_(rhs.attributes_),
bounding_box_(rhs.bounding_box_),
svg_width_(rhs.svg_width_),
svg_height_(rhs.svg_height_) {}
VertexSource & source() // FIXME!! make const VertexSource & source() // FIXME!! make const
{ {
return source_; return source_;

View file

@ -109,6 +109,7 @@ else: # unix, non-macos
source = Split( source = Split(
""" """
marker.cpp
debug_symbolizer.cpp debug_symbolizer.cpp
request.cpp request.cpp
well_known_srs.cpp well_known_srs.cpp

View file

@ -53,6 +53,12 @@ image_32::image_32(const image_32& rhs)
data_(rhs.data_), data_(rhs.data_),
painted_(rhs.painted_) {} painted_(rhs.painted_) {}
image_32::image_32(const image_data_32& rhs)
:width_(rhs.width()),
height_(rhs.height()),
data_(rhs),
painted_(false) {}
#ifdef HAVE_CAIRO #ifdef HAVE_CAIRO
image_32::image_32(cairo_surface_ptr const& surface) image_32::image_32(cairo_surface_ptr const& surface)
:width_(cairo_image_surface_get_width(&*surface)), :width_(cairo_image_surface_get_width(&*surface)),

168
src/marker.cpp Normal file
View file

@ -0,0 +1,168 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2013 Artem Pavlenko
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
// mapnik
#include <mapnik/marker.hpp>
#include <mapnik/svg/svg_converter.hpp>
#include <mapnik/svg/svg_path_adapter.hpp>
#include <mapnik/svg/svg_parser.hpp>
#include <mapnik/image_util.hpp>
#include <mapnik/image_reader.hpp>
// agg
#include "agg_rendering_buffer.h"
#include "agg_pixfmt_rgba.h"
// boost
#include <boost/make_shared.hpp>
namespace mapnik
{
mapnik::svg_path_ptr read_svg_marker(std::string const& uri, bool from_string)
{
using namespace mapnik::svg;
svg_path_ptr marker_path(boost::make_shared<svg_storage_type>());
vertex_stl_adapter<svg_path_storage> stl_storage(marker_path->source());
svg_path_adapter svg_path(stl_storage);
svg_converter_type svg(svg_path, marker_path->attributes());
svg_parser p(svg);
if (from_string)
{
p.parse_from_string(uri);
}
else
{
p.parse(uri);
}
double lox,loy,hix,hiy;
svg.bounding_rect(&lox, &loy, &hix, &hiy);
marker_path->set_bounding_box(lox,loy,hix,hiy);
marker_path->set_dimensions(svg.width(),svg.height());
return marker_path;
}
mapnik::image_ptr read_bitmap_marker(std::string const& uri, bool from_string)
{
mapnik::image_reader * reader = NULL;
mapnik::image_reader_guard guard(reader);
if (from_string)
{
reader = mapnik::get_image_reader(uri.data(),uri.size());
}
else
{
reader = mapnik::get_image_reader(uri);
}
if (!reader) throw std::runtime_error("could not intialize reader for: '" + uri + "'");
unsigned width = reader->width();
unsigned height = reader->height();
BOOST_ASSERT(width > 0 && height > 0);
mapnik::image_ptr image(boost::make_shared<mapnik::image_data_32>(width,height));
reader->read(0,0,*image);
if (!reader->premultiplied_alpha())
{
agg::rendering_buffer buffer(image->getBytes(),image->width(),image->height(),image->width() * 4);
agg::pixfmt_rgba32 pixf(buffer);
pixf.premultiply();
}
return image;
}
marker::marker()
{
// create default OGC 4x4 black pixel
bitmap_data_ = boost::optional<mapnik::image_ptr>(boost::make_shared<image_data_32>(4,4));
(*bitmap_data_)->set(0xff000000);
}
marker::marker(boost::optional<mapnik::image_ptr> const& data)
: bitmap_data_(data)
{
}
marker::marker(boost::optional<mapnik::svg_path_ptr> const& data)
: vector_data_(data)
{
}
marker::marker(marker const& rhs)
: bitmap_data_(rhs.bitmap_data_),
vector_data_(rhs.vector_data_)
{}
box2d<double> marker::bounding_box() const
{
if (is_vector())
{
return (*vector_data_)->bounding_box();
}
if (is_bitmap())
{
double width = (*bitmap_data_)->width();
double height = (*bitmap_data_)->height();
return box2d<double>(0, 0, width, height);
}
return box2d<double>();
}
double marker::width() const
{
if (is_bitmap())
return (*bitmap_data_)->width();
else if (is_vector())
return (*vector_data_)->bounding_box().width();
return 0;
}
double marker::height() const
{
if (is_bitmap())
return (*bitmap_data_)->height();
else if (is_vector())
return (*vector_data_)->bounding_box().height();
return 0;
}
bool marker::is_bitmap() const
{
return bitmap_data_;
}
bool marker::is_vector() const
{
return vector_data_;
}
boost::optional<mapnik::image_ptr> marker::get_bitmap_data() const
{
return bitmap_data_;
}
boost::optional<mapnik::svg_path_ptr> marker::get_vector_data() const
{
return vector_data_;
}
}

View file

@ -24,13 +24,7 @@
#include <mapnik/debug.hpp> #include <mapnik/debug.hpp>
#include <mapnik/marker.hpp> #include <mapnik/marker.hpp>
#include <mapnik/marker_cache.hpp> #include <mapnik/marker_cache.hpp>
#include <mapnik/svg/svg_parser.hpp>
#include <mapnik/svg/svg_storage.hpp>
#include <mapnik/svg/svg_converter.hpp>
#include <mapnik/svg/svg_path_adapter.hpp>
#include <mapnik/svg/svg_path_attributes.hpp>
#include <mapnik/image_util.hpp> #include <mapnik/image_util.hpp>
#include <mapnik/image_reader.hpp>
// boost // boost
#include <boost/assert.hpp> #include <boost/assert.hpp>
@ -38,36 +32,39 @@
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/make_shared.hpp> #include <boost/make_shared.hpp>
// agg
#include "agg_rendering_buffer.h"
#include "agg_pixfmt_rgba.h"
namespace mapnik namespace mapnik
{ {
marker_cache::marker_cache() marker_cache::marker_cache()
: known_svg_prefix_("shape://") : known_svg_prefix_("shape://"),
known_image_prefix_("image://")
{ {
insert_svg("ellipse", init();
"<?xml version='1.0' standalone='no'?>"
"<svg width='100%' height='100%' version='1.1' xmlns='http://www.w3.org/2000/svg'>"
"<ellipse rx='5' ry='5' fill='#0000FF' stroke='black' stroke-width='.5'/>"
"</svg>");
insert_svg("arrow",
"<?xml version='1.0' standalone='no'?>"
"<svg width='100%' height='100%' version='1.1' xmlns='http://www.w3.org/2000/svg'>"
"<path fill='#0000FF' stroke='black' stroke-width='.5' d='m 31.698405,7.5302648 -8.910967,-6.0263712 0.594993,4.8210971 -18.9822542,0 0,2.4105482 18.9822542,0 -0.594993,4.8210971 z'/>"
"</svg>");
} }
marker_cache::~marker_cache() {} marker_cache::~marker_cache() {}
void marker_cache::init()
{
std::string ellipse =
"<?xml version='1.0' standalone='no'?>"
"<svg width='100%' height='100%' version='1.1' xmlns='http://www.w3.org/2000/svg'>"
"<ellipse rx='5' ry='5' fill='#0000FF' stroke='black' stroke-width='.5'/>"
"</svg>";
marker_cache_.insert(std::make_pair("shape://ellipse",boost::make_shared<marker>(read_svg_marker(ellipse,true))));
std::string arrow =
"<?xml version='1.0' standalone='no'?>"
"<svg width='100%' height='100%' version='1.1' xmlns='http://www.w3.org/2000/svg'>"
"<path fill='#0000FF' stroke='black' stroke-width='.5' d='m 31.698405,7.5302648 -8.910967,-6.0263712 0.594993,4.8210971 -18.9822542,0 0,2.4105482 18.9822542,0 -0.594993,4.8210971 z'/>"
"</svg>";
marker_cache_.insert(std::make_pair("shape://arrow",boost::make_shared<marker>(read_svg_marker(arrow,true))));
}
void marker_cache::clear() void marker_cache::clear()
{ {
#ifdef MAPNIK_THREADSAFE #ifdef MAPNIK_THREADSAFE
mutex::scoped_lock lock(mutex_); mutex::scoped_lock lock(mutex_);
#endif #endif
typedef boost::unordered_map<std::string, marker_ptr>::const_iterator iterator_type;
iterator_type itr = marker_cache_.begin(); iterator_type itr = marker_cache_.begin();
while(itr != marker_cache_.end()) while(itr != marker_cache_.end())
{ {
@ -82,147 +79,103 @@ void marker_cache::clear()
} }
} }
bool marker_cache::is_uri(std::string const& path) bool marker_cache::remove(std::string const& uri)
{
return boost::algorithm::starts_with(path,known_svg_prefix_);
}
bool marker_cache::insert_svg(std::string const& name, std::string const& svg_string)
{
std::string key = known_svg_prefix_ + name;
typedef boost::unordered_map<std::string, std::string>::const_iterator iterator_type;
iterator_type itr = svg_cache_.find(key);
if (itr == svg_cache_.end())
{
return svg_cache_.insert(std::make_pair(key,svg_string)).second;
}
return false;
}
bool marker_cache::insert_marker(std::string const& uri, marker_ptr path)
{ {
#ifdef MAPNIK_THREADSAFE #ifdef MAPNIK_THREADSAFE
mutex::scoped_lock lock(mutex_); mutex::scoped_lock lock(mutex_);
#endif #endif
iterator_type itr = marker_cache_.find(uri);
if (itr != marker_cache_.end())
{
marker_cache_.erase(itr);
return true;
}
return false;
}
bool marker_cache::is_uri(std::string const& uri)
{
return is_svg_uri(uri) || is_image_uri(uri);
}
bool marker_cache::is_svg_uri(std::string const& uri)
{
return boost::algorithm::starts_with(uri,known_svg_prefix_);
}
bool marker_cache::is_image_uri(std::string const& uri)
{
return boost::algorithm::starts_with(uri,known_image_prefix_);
}
bool marker_cache::insert_marker(std::string const& uri, marker_ptr path, bool override)
{
#ifdef MAPNIK_THREADSAFE
mutex::scoped_lock lock(mutex_);
#endif
if (!override)
{
return marker_cache_.insert(std::make_pair(uri,path)).second; return marker_cache_.insert(std::make_pair(uri,path)).second;
}
else
{
typedef boost::unordered_map<std::string, marker_ptr>::iterator non_const_iterator_type;
std::pair<non_const_iterator_type,bool> result = marker_cache_.insert(std::make_pair(uri,path));
if (!result.second)
{
result.first->second = path;
}
return result.second;
}
} }
boost::optional<marker_ptr> marker_cache::find(std::string const& uri, boost::optional<marker_ptr> marker_cache::find(std::string const& uri,
bool update_cache) bool update_cache)
{ {
boost::optional<marker_ptr> result; boost::optional<marker_ptr> result;
if (uri.empty()) if (uri.empty())
{ {
return result; return result;
} }
#ifdef MAPNIK_THREADSAFE #ifdef MAPNIK_THREADSAFE
mutex::scoped_lock lock(mutex_); mutex::scoped_lock lock(mutex_);
#endif #endif
typedef boost::unordered_map<std::string, marker_ptr>::const_iterator iterator_type;
iterator_type itr = marker_cache_.find(uri); iterator_type itr = marker_cache_.find(uri);
if (itr != marker_cache_.end()) if (itr != marker_cache_.end())
{ {
result.reset(itr->second); result.reset(itr->second);
return result; return result;
} }
try try
{ {
// if uri references a built-in marker if (boost::filesystem::exists(boost::filesystem::path(uri)))
if (is_uri(uri))
{ {
boost::unordered_map<std::string, std::string>::const_iterator mark_itr = svg_cache_.find(uri);
if (mark_itr == svg_cache_.end())
{
MAPNIK_LOG_ERROR(marker_cache) << "Marker does not exist: " << uri;
return result;
}
std::string known_svg_string = mark_itr->second;
using namespace mapnik::svg;
svg_path_ptr marker_path(boost::make_shared<svg_storage_type>());
vertex_stl_adapter<svg_path_storage> stl_storage(marker_path->source());
svg_path_adapter svg_path(stl_storage);
svg_converter_type svg(svg_path, marker_path->attributes());
svg_parser p(svg);
p.parse_from_string(known_svg_string);
//svg.arrange_orientations();
double lox,loy,hix,hiy;
svg.bounding_rect(&lox, &loy, &hix, &hiy);
marker_path->set_bounding_box(lox,loy,hix,hiy);
marker_path->set_dimensions(svg.width(),svg.height());
marker_ptr mark(boost::make_shared<marker>(marker_path));
result.reset(mark);
if (update_cache)
{
marker_cache_.insert(std::make_pair(uri,*result));
}
}
// otherwise assume file-based
else
{
boost::filesystem::path path(uri);
if (!exists(path))
{
MAPNIK_LOG_ERROR(marker_cache) << "Marker does not exist: " << uri;
return result;
}
if (is_svg(uri)) if (is_svg(uri))
{ {
using namespace mapnik::svg; result.reset(boost::make_shared<marker>(read_svg_marker(uri)));
svg_path_ptr marker_path(boost::make_shared<svg_storage_type>()); }
vertex_stl_adapter<svg_path_storage> stl_storage(marker_path->source()); else
svg_path_adapter svg_path(stl_storage); {
svg_converter_type svg(svg_path, marker_path->attributes()); result.reset(boost::make_shared<marker>(read_bitmap_marker(uri)));
svg_parser p(svg); }
p.parse(uri); }
//svg.arrange_orientations(); if (result)
double lox,loy,hix,hiy; {
svg.bounding_rect(&lox, &loy, &hix, &hiy);
marker_path->set_bounding_box(lox,loy,hix,hiy);
marker_path->set_dimensions(svg.width(),svg.height());
marker_ptr mark(boost::make_shared<marker>(marker_path));
result.reset(mark);
if (update_cache) if (update_cache)
{ {
marker_cache_.insert(std::make_pair(uri,*result)); marker_cache_.insert(std::make_pair(uri,*result));
} }
return result;
} }
else else
{ {
// TODO - support reading images from string MAPNIK_LOG_ERROR(marker_cache) << "Marker does not exist: " << uri;
std::auto_ptr<mapnik::image_reader> reader(mapnik::get_image_reader(uri)); return result;
if (reader.get())
{
unsigned width = reader->width();
unsigned height = reader->height();
BOOST_ASSERT(width > 0 && height > 0);
mapnik::image_ptr image(boost::make_shared<mapnik::image_data_32>(width,height));
reader->read(0,0,*image);
if (!reader->premultiplied_alpha())
{
agg::rendering_buffer buffer(image->getBytes(),image->width(),image->height(),image->width() * 4);
agg::pixfmt_rgba32 pixf(buffer);
pixf.premultiply();
}
marker_ptr mark(boost::make_shared<marker>(image));
result.reset(mark);
if (update_cache)
{
marker_cache_.insert(std::make_pair(uri,*result));
}
}
else
{
MAPNIK_LOG_ERROR(marker_cache) << "could not intialize reader for: '" << uri << "'";
}
}
} }
} }
catch (std::exception const& ex) catch (std::exception const& ex)
{ {
MAPNIK_LOG_ERROR(marker_cache) << "Exception caught while loading: '" << uri << "' (" << ex.what() << ")"; MAPNIK_LOG_ERROR(marker_cache) << ex.what();
} }
return result; return result;
} }

View file

@ -1060,11 +1060,11 @@ void svg_parser::parse(std::string const& filename)
xmlTextReaderPtr reader = xmlNewTextReaderFilename(filename.c_str()); xmlTextReaderPtr reader = xmlNewTextReaderFilename(filename.c_str());
if (reader == NULL) if (reader == NULL)
{ {
MAPNIK_LOG_ERROR(svg_parser) << "Unable to open '" << filename << "'"; throw std::runtime_error("Unable to open '" + filename + "'");
} }
else if (!parse_reader(*this,reader)) else if (!parse_reader(*this,reader))
{ {
MAPNIK_LOG_ERROR(svg_parser) << "Unable to parse '" << filename << "'"; throw std::runtime_error("Unable to parse '" + filename + "'");
} }
} }
@ -1074,11 +1074,11 @@ void svg_parser::parse_from_string(std::string const& svg)
(XML_PARSE_NOBLANKS | XML_PARSE_NOCDATA | XML_PARSE_NOERROR | XML_PARSE_NOWARNING)); (XML_PARSE_NOBLANKS | XML_PARSE_NOCDATA | XML_PARSE_NOERROR | XML_PARSE_NOWARNING));
if (reader == NULL) if (reader == NULL)
{ {
MAPNIK_LOG_ERROR(svg_parser) << "Unable to parse '" << svg << "'"; throw std::runtime_error("Unable to parse '" + svg + "'");
} }
else if (!parse_reader(*this,reader)) else if (!parse_reader(*this,reader))
{ {
MAPNIK_LOG_ERROR(svg_parser) << "Unable to parse '" << svg << "'"; throw std::runtime_error("Unable to parse '" + svg + "'");
} }
} }

View file

@ -0,0 +1,125 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
from utilities import execution_path
from nose.tools import *
import mapnik
import threading
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_svg_put_to_marker_cache():
cache = mapnik.MarkerCache.instance()
cache.clear()
eq_(cache.size(),2)
svg_file = '../data/svg/octocat.svg'
svg = mapnik.SVG.open(svg_file)
cache.put(svg_file,svg)
eq_(cache.size(),3)
eq_(svg_file in cache.keys(),True)
def test_svg_put_and_clear_marker_cache():
cache = mapnik.MarkerCache.instance()
cache.clear()
eq_(cache.size(),2)
svg_file = '../data/svg/octocat.svg'
svg = mapnik.SVG.open(svg_file)
cache.put(svg_file,svg)
eq_(cache.size(),3)
eq_(svg_file in cache.keys(),True)
cache.clear()
eq_(svg_file in cache.keys(),False)
eq_(cache.size(),2)
def test_svg_put_and_remove_marker_cache():
cache = mapnik.MarkerCache.instance()
cache.clear()
eq_(cache.size(),2)
svg_file = '../data/svg/octocat.svg'
svg = mapnik.SVG.open(svg_file)
cache.put(svg_file,svg)
eq_(cache.size(),3)
eq_(svg_file in cache.keys(),True)
cache.remove(svg_file)
eq_(svg_file in cache.keys(),False)
eq_(cache.size(),2)
def test_image_put_and_clear_marker_cache():
cache = mapnik.MarkerCache.instance()
cache.clear()
eq_(cache.size(),2)
image_file = '../data/images/marker.png'
image = mapnik.Image.open(image_file)
cache.put(image_file,image)
eq_(cache.size(),3)
eq_(image_file in cache.keys(),True)
cache.clear()
eq_(image_file in cache.keys(),False)
eq_(cache.size(),2)
def test_image_put_and_clear_marker_cache():
cache = mapnik.MarkerCache.instance()
cache.clear()
eq_(cache.size(),2)
image_file = '../data/images/marker.png'
image = mapnik.Image.open(image_file)
cache.put(image_file,image)
eq_(cache.size(),3)
eq_(image_file in cache.keys(),True)
eq_(cache.remove(image_file),True)
# removing twice should return False for no successful removal
eq_(cache.remove(image_file),False)
eq_(image_file in cache.keys(),False)
eq_(cache.size(),2)
def test_image_put_and_get_image_in_marker_cache():
cache = mapnik.MarkerCache.instance()
cache.clear()
eq_(cache.size(),2)
image_file = '../data/images/marker.png'
image = mapnik.Image.open(image_file)
cache.put(image_file,image)
new_im = cache.get(image_file)
eq_(image.tostring(),new_im.tostring())
def test_image_put_and_get_svg_in_marker_cache():
cache = mapnik.MarkerCache.instance()
cache.clear()
eq_(cache.size(),2)
image_file = '../data/svg/rect.svg'
image = mapnik.SVG.open(image_file)
cache.put(image_file,image)
new_im = cache.get(image_file)
eq_(image.width(),new_im.width())
def test_marker_cache_override():
cache = mapnik.MarkerCache.instance()
cache.clear()
eq_(cache.size(),2)
image_file = '../data/images/marker.png'
image = mapnik.Image.open(image_file)
cache.put(image_file,image)
alt_im = mapnik.Image(4,4)
result = cache.put(image_file,alt_im)
# putting a item for which a key already exists should return False
eq_(result,False)
alt_im_copy = cache.get(image_file)
eq_(alt_im.tostring(),alt_im_copy.tostring())
def test_threaded_reads_and_writes():
threads = []
for i in range(100):
t = threading.Thread(target=test_image_put_and_clear_marker_cache)
t.start()
threads.append(t)
for t in threads:
t.join()
if __name__ == "__main__":
setup()
[eval(run)() for run in dir() if 'test_' in run]

View file

@ -0,0 +1,43 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
from utilities import execution_path
from nose.tools import *
import mapnik
import threading
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_svg_loading_from_file():
svg_file = '../data/svg/octocat.svg'
svg = mapnik.SVG.open(svg_file)
# TODO - invalid numbers
eq_(svg.width(),0)
eq_(svg.height(),0)
expected = mapnik.Box2d(0.00700000000001,-4.339,378.46,332.606)
actual = svg.extent()
assert_almost_equal(expected.minx,actual.minx, places=7)
assert_almost_equal(expected.miny,actual.miny, places=7)
assert_almost_equal(expected.maxx,actual.maxx, places=7)
assert_almost_equal(expected.maxy,actual.maxy, places=7)
def test_svg_loading_from_string():
svg_file = '../data/svg/octocat.svg'
svg = mapnik.SVG.fromstring(open(svg_file,'rb').read())
# TODO - invalid numbers
eq_(svg.width(),0)
eq_(svg.height(),0)
expected = mapnik.Box2d(0.00700000000001,-4.339,378.46,332.606)
actual = svg.extent()
assert_almost_equal(expected.minx,actual.minx, places=7)
assert_almost_equal(expected.miny,actual.miny, places=7)
assert_almost_equal(expected.maxx,actual.maxx, places=7)
assert_almost_equal(expected.maxy,actual.maxy, places=7)
if __name__ == "__main__":
setup()
[eval(run)() for run in dir() if 'test_' in run]