/***************************************************************************** * * This file is part of Mapnik (c++ mapping toolkit) * * Copyright (C) 2017 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 #include #include #include #include #include #include #include #include #include #include #pragma GCC diagnostic push #include #include #include #pragma GCC diagnostic pop #pragma GCC diagnostic push #include #include "agg_rendering_buffer.h" #include "agg_pixfmt_rgba.h" #pragma GCC diagnostic pop namespace mapnik { marker_cache::marker_cache() : known_svg_prefix_("shape://"), known_image_prefix_("image://") { insert_svg("ellipse", "" "" "" ""); insert_svg("arrow", "" "" "" ""); marker_cache_.emplace("image://square",std::make_shared(mapnik::marker_rgba8())); } marker_cache::~marker_cache() {} void marker_cache::clear() { #ifdef MAPNIK_THREADSAFE std::lock_guard lock(mutex_); #endif auto itr = marker_cache_.begin(); while(itr != marker_cache_.end()) { if (!is_uri(itr->first)) { marker_cache_.erase(itr++); } else { ++itr; } } } bool marker_cache::is_svg_uri(std::string const& path) { return boost::algorithm::starts_with(path,known_svg_prefix_); } bool marker_cache::is_image_uri(std::string const& path) { return boost::algorithm::starts_with(path,known_image_prefix_); } bool marker_cache::insert_svg(std::string const& name, std::string const& svg_string) { std::string key = known_svg_prefix_ + name; auto itr = svg_cache_.find(key); if (itr == svg_cache_.end()) { return svg_cache_.emplace(key,svg_string).second; } return false; } bool marker_cache::insert_marker(std::string const& uri, mapnik::marker && path) { #ifdef MAPNIK_THREADSAFE std::lock_guard lock(mutex_); #endif return marker_cache_.emplace(uri,std::make_shared(std::move(path))).second; } namespace detail { struct visitor_create_marker { marker operator() (image_rgba8 & data) const { mapnik::premultiply_alpha(data); return mapnik::marker(mapnik::marker_rgba8(data)); } marker operator() (image_null &) const { throw std::runtime_error("Can not make marker from null image data type"); } template marker operator() (T &) const { throw std::runtime_error("Can not make marker from this data type"); } }; } // end detail ns std::shared_ptr marker_cache::find(std::string const& uri, bool update_cache, bool strict) { if (uri.empty()) { return std::make_shared(mapnik::marker_null()); } #ifdef MAPNIK_THREADSAFE std::lock_guard lock(mutex_); #endif auto itr = marker_cache_.find(uri); if (itr != marker_cache_.end()) { return itr->second; } try { // if uri references a built-in marker if (is_svg_uri(uri)) { auto mark_itr = svg_cache_.find(uri); if (mark_itr == svg_cache_.end()) { MAPNIK_LOG_ERROR(marker_cache) << "Marker does not exist: " << uri; return std::make_shared(mapnik::marker_null()); } std::string known_svg_string = mark_itr->second; using namespace mapnik::svg; svg_path_ptr marker_path(std::make_shared()); vertex_stl_adapter 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, strict); p.parse_from_string(known_svg_string); if (!strict) { for (auto const& msg : p.err_handler().error_messages()) { MAPNIK_LOG_ERROR(marker_cache) << "SVG PARSING ERROR:\"" << msg << "\""; } } //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()); if (update_cache) { auto emplace_result = marker_cache_.emplace(uri,std::make_shared(mapnik::marker_svg(marker_path))); return emplace_result.first->second; } else { return std::make_shared(mapnik::marker_svg(marker_path)); } } // otherwise assume file-based else { if (!mapnik::util::exists(uri)) { MAPNIK_LOG_ERROR(marker_cache) << "Marker does not exist: " << uri; return std::make_shared(mapnik::marker_null()); } if (is_svg(uri)) { using namespace mapnik::svg; svg_path_ptr marker_path(std::make_shared()); vertex_stl_adapter 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, strict); p.parse(uri); if (!strict) { for (auto const& msg : p.err_handler().error_messages()) { MAPNIK_LOG_ERROR(marker_cache) << "SVG PARSING ERROR:\"" << msg << "\""; } } //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()); if (update_cache) { auto emplace_result = marker_cache_.emplace(uri,std::make_shared(mapnik::marker_svg(marker_path))); return emplace_result.first->second; } else { return std::make_shared(mapnik::marker_svg(marker_path)); } } else { // TODO - support reading images from string std::unique_ptr reader(mapnik::get_image_reader(uri)); if (reader.get()) { unsigned width = reader->width(); unsigned height = reader->height(); BOOST_ASSERT(width > 0 && height > 0); image_any im = reader->read(0,0,width,height); if (update_cache) { auto emplace_result = marker_cache_.emplace(uri, std::make_shared( util::apply_visitor(detail::visitor_create_marker(), im) )); return emplace_result.first->second; } else { return std::make_shared( util::apply_visitor(detail::visitor_create_marker(), im) ); } } else { MAPNIK_LOG_ERROR(marker_cache) << "could not initialize reader for: '" << uri << "'"; return std::make_shared(mapnik::marker_null()); } } } } catch (std::exception const& ex) { MAPNIK_LOG_ERROR(marker_cache) << "Exception caught while loading: '" << uri << "' (" << ex.what() << ")"; } return std::make_shared(mapnik::marker_null()); } }