diff --git a/SConstruct b/SConstruct index 256508aa7..28d3cb94f 100644 --- a/SConstruct +++ b/SConstruct @@ -1432,7 +1432,7 @@ if not preconfigured: env.Append(CXXFLAGS = gcc_cxx_flags + '-O%s -finline-functions -Wno-inline -Wno-parentheses -Wno-char-subscripts %s' % (env['OPTIMIZATION'],ndebug_flags)) if env['DEBUG_UNDEFINED']: - env.Append(CXXFLAGS = '-fcatch-undefined-behavior') #-ftrapv -fwrapv + env.Append(CXXFLAGS = '-fcatch-undefined-behavior -ftrapv -fwrapv') if 'python' in env['BINDINGS']: if not os.access(env['PYTHON'], os.X_OK): diff --git a/bindings/python/mapnik_shield_symbolizer.cpp b/bindings/python/mapnik_shield_symbolizer.cpp index 5da6bad6a..41eff7536 100644 --- a/bindings/python/mapnik_shield_symbolizer.cpp +++ b/bindings/python/mapnik_shield_symbolizer.cpp @@ -188,6 +188,9 @@ void export_shield_symbolizer() .add_property("minimum_distance", &shield_symbolizer::get_minimum_distance, &shield_symbolizer::set_minimum_distance) + .add_property("minimum_padding", + &shield_symbolizer::get_minimum_padding, + &shield_symbolizer::set_minimum_padding) .add_property("name",&shield_symbolizer::get_name, &shield_symbolizer::set_name) .add_property("opacity", diff --git a/bindings/python/mapnik_text_placement.cpp b/bindings/python/mapnik_text_placement.cpp index c24e99b6b..54ebf2653 100644 --- a/bindings/python/mapnik_text_placement.cpp +++ b/bindings/python/mapnik_text_placement.cpp @@ -357,16 +357,16 @@ void export_text_placement() ; - class_ + class_with_converter ("TextSymbolizerProperties") + .def_readwrite_convert("label_placement", &text_symbolizer_properties::label_placement) + .def_readwrite_convert("horizontal_alignment", &text_symbolizer_properties::halign) + .def_readwrite_convert("justify_alignment", &text_symbolizer_properties::jalign) + .def_readwrite_convert("vertical_alignment", &text_symbolizer_properties::valign) .def_readwrite("orientation", &text_symbolizer_properties::orientation) .add_property("displacement", &get_displacement, &set_displacement) - .def_readwrite("label_placement", &text_symbolizer_properties::label_placement) - .def_readwrite("horizontal_alignment", &text_symbolizer_properties::halign) - .def_readwrite("justify_alignment", &text_symbolizer_properties::jalign) - .def_readwrite("vertical_alignment", &text_symbolizer_properties::valign) .def_readwrite("label_spacing", &text_symbolizer_properties::label_spacing) .def_readwrite("label_position_tolerance", &text_symbolizer_properties::label_position_tolerance) .def_readwrite("avoid_edges", &text_symbolizer_properties::avoid_edges) @@ -483,22 +483,22 @@ void export_text_placement() register_ptr_to_python >(); - class_with_optional, bases, boost::noncopyable> ("FormattingFormat") - .def_readwrite_optional("text_size", &formatting::format_node::text_size) - .def_readwrite_optional("face_name", &formatting::format_node::face_name) - .def_readwrite_optional("character_spacing", &formatting::format_node::character_spacing) - .def_readwrite_optional("line_spacing", &formatting::format_node::line_spacing) - .def_readwrite_optional("text_opacity", &formatting::format_node::text_opacity) - .def_readwrite_optional("wrap_char", &formatting::format_node::wrap_char) - .def_readwrite_optional("wrap_before", &formatting::format_node::wrap_before) - .def_readwrite_optional("text_transform", &formatting::format_node::text_transform) - .def_readwrite_optional("fill", &formatting::format_node::fill) - .def_readwrite_optional("halo_fill", &formatting::format_node::halo_fill) - .def_readwrite_optional("halo_radius", &formatting::format_node::halo_radius) + .def_readwrite_convert("text_size", &formatting::format_node::text_size) + .def_readwrite_convert("face_name", &formatting::format_node::face_name) + .def_readwrite_convert("character_spacing", &formatting::format_node::character_spacing) + .def_readwrite_convert("line_spacing", &formatting::format_node::line_spacing) + .def_readwrite_convert("text_opacity", &formatting::format_node::text_opacity) + .def_readwrite_convert("wrap_char", &formatting::format_node::wrap_char) + .def_readwrite_convert("wrap_before", &formatting::format_node::wrap_before) + .def_readwrite_convert("text_transform", &formatting::format_node::text_transform) + .def_readwrite_convert("fill", &formatting::format_node::fill) + .def_readwrite_convert("halo_fill", &formatting::format_node::halo_fill) + .def_readwrite_convert("halo_radius", &formatting::format_node::halo_radius) .def("apply", &formatting::format_node::apply, &FormatNodeWrap::default_apply) .add_property("child", &formatting::format_node::get_child, diff --git a/bindings/python/python_optional.hpp b/bindings/python/python_optional.hpp index 5e431044d..da3279f2f 100644 --- a/bindings/python/python_optional.hpp +++ b/bindings/python/python_optional.hpp @@ -104,31 +104,31 @@ struct python_optional : public boost::noncopyable See http://osdir.com/ml/python.c++/2003-11/msg00158.html */ -template -class class_with_optional : public boost::python::class_ +template +class class_with_converter : public boost::python::class_ { public: - typedef class_with_optional self; + typedef class_with_converter self; // Construct with the class name, with or without docstring, and default __init__() function - class_with_optional(char const* name, char const* doc = 0) : boost::python::class_(name, doc) { } + class_with_converter(char const* name, char const* doc = 0) : boost::python::class_(name, doc) { } // Construct with class name, no docstring, and an uncallable __init__ function - class_with_optional(char const* name, boost::python::no_init_t y) : boost::python::class_(name, y) { } + class_with_converter(char const* name, boost::python::no_init_t y) : boost::python::class_(name, y) { } // Construct with class name, docstring, and an uncallable __init__ function - class_with_optional(char const* name, char const* doc, boost::python::no_init_t y) : boost::python::class_(name, doc, y) { } + class_with_converter(char const* name, char const* doc, boost::python::no_init_t y) : boost::python::class_(name, doc, y) { } // Construct with class name and init<> function - template class_with_optional(char const* name, boost::python::init_base const& i) + template class_with_converter(char const* name, boost::python::init_base const& i) : boost::python::class_(name, i) { } // Construct with class name, docstring and init<> function template - inline class_with_optional(char const* name, char const* doc, boost::python::init_base const& i) + inline class_with_converter(char const* name, char const* doc, boost::python::init_base const& i) : boost::python::class_(name, doc, i) { } template - self& def_readwrite_optional(char const* name, D const& d, char const* doc=0) + self& def_readwrite_convert(char const* name, D const& d, char const* doc=0) { this->add_property(name, boost::python::make_getter(d, boost::python::return_value_policy()), diff --git a/include/mapnik/hextree.hpp b/include/mapnik/hextree.hpp index 8a94293dd..780c42c56 100644 --- a/include/mapnik/hextree.hpp +++ b/include/mapnik/hextree.hpp @@ -394,8 +394,16 @@ private: // clip extreme alfa values void create_palette_rek(std::vector & palette, node * itr) const { - // actually, ignore ones with < 3 pixels - if (itr->count >= 3) + /* + NOTE: previous code did: + + // actually, ignore ones with < 3 pixels + if (itr->count >= 3) + + But this could lead to memory corruption + */ + + if (itr->count > 0) { unsigned count = itr->count; byte a = byte(itr->alphas/float(count)); @@ -475,8 +483,10 @@ private: } tries = 0; // ignore leaves and also nodes with small mean error and not excessive number of pixels - if (((cur_node->reduce_cost / cur_node->pixel_count + 1) * std::log(double(cur_node->pixel_count))) > 15 - && (cur_node->children_count > 0)) + if (cur_node->pixel_count > 0 && + (cur_node->children_count > 0) && + (((cur_node->reduce_cost / cur_node->pixel_count + 1) * std::log(double(cur_node->pixel_count))) > 15) + ) { colors_--; cur_node->count = 0; diff --git a/include/mapnik/util/conversions.hpp b/include/mapnik/util/conversions.hpp new file mode 100644 index 000000000..51727a10e --- /dev/null +++ b/include/mapnik/util/conversions.hpp @@ -0,0 +1,61 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2012 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 + * + *****************************************************************************/ + +#ifndef MAPNIK_CONVERSIONS_UTIL_HPP +#define MAPNIK_CONVERSIONS_UTIL_HPP + +// mapnik + +// boost +#include + +// stl +#include + +namespace mapnik { namespace conversions { + +using namespace boost::spirit; + +static bool string2int(std::string const& value, int * result) +{ + if (value.empty()) + return false; + std::string::const_iterator str_beg = value.begin(); + std::string::const_iterator str_end = value.end(); + bool r = qi::phrase_parse(str_beg,str_end,qi::int_,ascii::space,*result); + return r && (str_beg == str_end); +} + +static bool string2double(std::string const& value, double * result) +{ + if (value.empty()) + return false; + std::string::const_iterator str_beg = value.begin(); + std::string::const_iterator str_end = value.end(); + bool r = qi::phrase_parse(str_beg,str_end,qi::double_,ascii::space,*result); + return r && (str_beg == str_end); +} + +} +} + +#endif // MAPNIK_CONVERSIONS_UTIL_HPP diff --git a/plugins/input/postgis/postgis_datasource.cpp b/plugins/input/postgis/postgis_datasource.cpp index 679a21861..732fbf647 100644 --- a/plugins/input/postgis/postgis_datasource.cpp +++ b/plugins/input/postgis/postgis_datasource.cpp @@ -28,10 +28,10 @@ #include #include #include +#include // boost #include -#include #include #include #include @@ -50,10 +50,7 @@ DATASOURCE_PLUGIN(postgis_datasource) const std::string postgis_datasource::GEOMETRY_COLUMNS="geometry_columns"; const std::string postgis_datasource::SPATIAL_REF_SYS="spatial_ref_system"; -using boost::lexical_cast; -using boost::bad_lexical_cast; using boost::shared_ptr; - using mapnik::PoolGuard; using mapnik::attribute_descriptor; @@ -174,13 +171,12 @@ void postgis_datasource::bind() const if (srid_ == 0) { - try + const char * srid_c = rs->getValue("srid"); + if (srid_c != NULL) { - srid_ = lexical_cast(rs->getValue("srid")); - } - catch (bad_lexical_cast &ex) - { - std::clog << "Postgis Plugin: SRID=" << rs->getValue("srid") << " " << ex.what() << std::endl; + int result; + if (mapnik::conversions::string2int(srid_c,&result)) + srid_ = result; } } } @@ -205,13 +201,12 @@ void postgis_datasource::bind() const shared_ptr rs = conn->executeQuery(s.str()); if (rs->next()) { - try + const char * srid_c = rs->getValue("srid"); + if (srid_c != NULL) { - srid_ = lexical_cast(rs->getValue("srid")); - } - catch (bad_lexical_cast &ex) - { - std::clog << "Postgis Plugin: SRID=" << rs->getValue("srid") << " " << ex.what() << std::endl; + int result; + if (mapnik::conversions::string2int(srid_c,&result)) + srid_ = result; } } rs->close(); @@ -384,8 +379,9 @@ std::string postgis_datasource::populate_tokens(const std::string& sql) const } if ( boost::algorithm::icontains(sql,scale_denom_token_) ) { - std::string max_denom = lexical_cast(FMAX); - boost::algorithm::replace_all(populated_sql,scale_denom_token_,max_denom); + std::ostringstream ss; + ss << FMAX; + boost::algorithm::replace_all(populated_sql,scale_denom_token_,ss.str()); } return populated_sql; } @@ -397,8 +393,9 @@ std::string postgis_datasource::populate_tokens(const std::string& sql, double s if ( boost::algorithm::icontains(populated_sql,scale_denom_token_) ) { - std::string max_denom = lexical_cast(scale_denom); - boost::algorithm::replace_all(populated_sql,scale_denom_token_,max_denom); + std::ostringstream ss; + ss << scale_denom; + boost::algorithm::replace_all(populated_sql,scale_denom_token_,ss.str()); } if ( boost::algorithm::icontains(populated_sql,bbox_token_) ) @@ -684,19 +681,22 @@ box2d postgis_datasource::envelope() const shared_ptr rs = conn->executeQuery(s.str()); if (rs->next() && !rs->isNull(0)) { - try - { - double lox = lexical_cast(rs->getValue(0)); - double loy = lexical_cast(rs->getValue(1)); - double hix = lexical_cast(rs->getValue(2)); - double hiy = lexical_cast(rs->getValue(3)); - extent_.init(lox,loy,hix,hiy); - extent_initialized_ = true; - } - catch (bad_lexical_cast &ex) - { - std::clog << boost::format("Postgis Plugin: warning: could not determine extent from query: %s\nError was: '%s'\n") % s.str() % ex.what() << std::endl; - } + double lox; + double loy; + double hix; + double hiy; + if (mapnik::conversions::string2double(rs->getValue(0),&lox) && + mapnik::conversions::string2double(rs->getValue(1),&loy) && + mapnik::conversions::string2double(rs->getValue(2),&hix) && + mapnik::conversions::string2double(rs->getValue(3),&hiy)) + { + extent_.init(lox,loy,hix,hiy); + extent_initialized_ = true; + } + else + { + std::clog << boost::format("Postgis Plugin: warning: could not determine extent from query: %s\n") % s.str() << std::endl; + } } rs->close(); } diff --git a/plugins/input/postgis/postgis_datasource.hpp b/plugins/input/postgis/postgis_datasource.hpp index e7f997ed7..846664de1 100644 --- a/plugins/input/postgis/postgis_datasource.hpp +++ b/plugins/input/postgis/postgis_datasource.hpp @@ -31,7 +31,6 @@ #include // boost -#include #include #include "connection_manager.hpp" diff --git a/plugins/input/postgis/postgis_featureset.cpp b/plugins/input/postgis/postgis_featureset.cpp index b90ef4df1..052782f85 100644 --- a/plugins/input/postgis/postgis_featureset.cpp +++ b/plugins/input/postgis/postgis_featureset.cpp @@ -30,17 +30,16 @@ #include #include #include +#include // boost -#include #include +#include // stl #include #include -using boost::lexical_cast; -using boost::bad_lexical_cast; using boost::trim_copy; using mapnik::geometry_type; using mapnik::byte; @@ -162,15 +161,9 @@ feature_ptr postgis_featureset::next() else if (oid == 1700) // numeric { std::string str = mapnik::sql_utils::numeric2string(buf); - try - { - double val = boost::lexical_cast(str); + double val; + if (mapnik::conversions::string2double(str,&val)) feature->put(name,val); - } - catch (boost::bad_lexical_cast & ex) - { - std::clog << ex.what() << "\n"; - } } else { diff --git a/src/image_util.cpp b/src/image_util.cpp index 98851eb15..011166e55 100644 --- a/src/image_util.cpp +++ b/src/image_util.cpp @@ -36,9 +36,7 @@ extern "C" #include #include #include - -// boost -#include +#include // jpeg #if defined(HAVE_JPEG) @@ -73,9 +71,6 @@ extern "C" #include "agg_trans_affine.h" #include "agg_image_filters.h" -using namespace boost::spirit; - - namespace mapnik { @@ -163,36 +158,24 @@ void handle_png_options(std::string const& type, if (*colors < 0) throw ImageWriterException("invalid color parameter: unavailable for true color images"); - std::string const& val = t.substr(2); - std::string::const_iterator str_beg = val.begin(); - std::string::const_iterator str_end = val.end(); - bool r = qi::phrase_parse(str_beg,str_end,qi::int_,ascii::space,*colors); - if (!r || (str_beg != str_end) || *colors < 0 || *colors > 256) - throw ImageWriterException("invalid color parameter: " + val); + if (!mapnik::conversions::string2int(t.substr(2),colors) || *colors < 0 || *colors > 256) + throw ImageWriterException("invalid color parameter: " + t.substr(2)); } else if (boost::algorithm::starts_with(t, "t=")) { if (*colors < 0) throw ImageWriterException("invalid trans_mode parameter: unavailable for true color images"); - std::string const& val = t.substr(2); - std::string::const_iterator str_beg = val.begin(); - std::string::const_iterator str_end = val.end(); - bool r = qi::phrase_parse(str_beg,str_end,qi::int_,ascii::space,*trans_mode); - if (!r || (str_beg != str_end) || *trans_mode < 0 || *trans_mode > 2) - throw ImageWriterException("invalid trans_mode parameter: " + val); + if (!mapnik::conversions::string2int(t.substr(2),trans_mode) || *trans_mode < 0 || *trans_mode > 2) + throw ImageWriterException("invalid trans_mode parameter: " + t.substr(2)); } else if (boost::algorithm::starts_with(t, "g=")) { if (*colors < 0) throw ImageWriterException("invalid gamma parameter: unavailable for true color images"); - std::string const& val = t.substr(2); - std::string::const_iterator str_beg = val.begin(); - std::string::const_iterator str_end = val.end(); - bool r = qi::phrase_parse(str_beg,str_end,qi::double_,ascii::space,*gamma); - if (!r || (str_beg != str_end) || *gamma < 0) + if (!mapnik::conversions::string2double(t.substr(2),gamma) || *gamma < 0) { - throw ImageWriterException("invalid gamma parameter: " + val); + throw ImageWriterException("invalid gamma parameter: " + t.substr(2)); } } else if (boost::algorithm::starts_with(t, "z=")) @@ -203,15 +186,11 @@ void handle_png_options(std::string const& type, #define Z_BEST_COMPRESSION 9 #define Z_DEFAULT_COMPRESSION (-1) */ - std::string const& val = t.substr(2); - std::string::const_iterator str_beg = val.begin(); - std::string::const_iterator str_end = val.end(); - bool r = qi::phrase_parse(str_beg,str_end,qi::int_,ascii::space,*compression); - if (!r || (str_beg != str_end) + if (!mapnik::conversions::string2int(t.substr(2),compression) || *compression < Z_DEFAULT_COMPRESSION || *compression > Z_BEST_COMPRESSION) { - throw ImageWriterException("invalid compression parameter: " + val + " (only -1 through 9 are valid)"); + throw ImageWriterException("invalid compression parameter: " + t.substr(2) + " (only -1 through 9 are valid)"); } } else if (boost::algorithm::starts_with(t, "s=")) @@ -336,14 +315,11 @@ void save_to_stream(T const& image, { int quality = 85; std::string const& val = t.substr(4); - if(!val.empty()) + if (!val.empty()) { - std::string::const_iterator str_beg = val.begin(); - std::string::const_iterator str_end = val.end(); - bool r = qi::phrase_parse(str_beg,str_end,qi::int_,ascii::space,quality); - if (!r || (str_beg != str_end) || quality < 0 || quality > 100) + if (!mapnik::conversions::string2int(val,&quality) || quality < 0 || quality > 100) { - throw ImageWriterException("invalid jpeg quality: " + val); + throw ImageWriterException("invalid jpeg quality: '" + val + "'"); } } save_as_jpeg(stream, quality, image); diff --git a/tests/python_tests/object_test.py b/tests/python_tests/object_test.py index 7981cb16a..6b178dfe5 100644 --- a/tests/python_tests/object_test.py +++ b/tests/python_tests/object_test.py @@ -198,10 +198,11 @@ def test_textsymbolizer_init(): ts = mapnik.TextSymbolizer(mapnik.Expression('[Field_Name]'), 'Font Name', 8, mapnik.Color('black')) # eq_(str(ts.name), str(mapnik2.Expression('[Field_Name]'))) name field is no longer supported - eq_(ts.face_name, 'Font Name') - eq_(ts.text_size, 8) - eq_(ts.fill, mapnik.Color('black')) - eq_(ts.label_placement, mapnik.label_placement.POINT_PLACEMENT) + eq_(ts.format.face_name, 'Font Name') + eq_(ts.format.text_size, 8) + eq_(ts.format.fill, mapnik.Color('black')) + eq_(ts.properties.label_placement, mapnik.label_placement.POINT_PLACEMENT) + eq_(ts.properties.horizontal_alignment, mapnik.horizontal_alignment.AUTO) # Map initialization def test_layer_init():