From 87e9c64fc44763b909f9c6b475da7b4dbaea0f61 Mon Sep 17 00:00:00 2001 From: artemp Date: Wed, 4 Mar 2015 14:52:55 +0100 Subject: [PATCH 01/15] topojson grammar - add optional bbox element which is omitted --- include/mapnik/json/topojson_grammar_impl.hpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/include/mapnik/json/topojson_grammar_impl.hpp b/include/mapnik/json/topojson_grammar_impl.hpp index 3d125a630..d939d4be8 100644 --- a/include/mapnik/json/topojson_grammar_impl.hpp +++ b/include/mapnik/json/topojson_grammar_impl.hpp @@ -113,13 +113,15 @@ topojson_grammar::topojson_grammar() ; geometry_collection = lit('{') - >> lit("\"type\"") >> lit(':') >> lit("\"GeometryCollection\"") >> lit(',') - >> lit("\"geometries\"") >> lit(':') >> lit('[') >> -(geometry[push_back(_r1, _1)] % lit(',')) + >> lit("\"type\"") >> lit(':') >> lit("\"GeometryCollection\"") + >> -(lit(',') >> omit[bbox]) + >> lit(',') >> lit("\"geometries\"") >> lit(':') >> lit('[') >> -(geometry[push_back(_r1, _1)] % lit(',')) >> lit(']') >> lit('}') ; point = lit('{') >> lit("\"type\"") >> lit(':') >> lit("\"Point\"") + >> -(lit(',') >> omit[bbox]) >> ((lit(',') >> lit("\"coordinates\"") >> lit(':') >> coordinate) ^ (lit(',') >> properties) /*^ (lit(',') >> omit[id])*/) >> lit('}') @@ -127,6 +129,7 @@ topojson_grammar::topojson_grammar() multi_point = lit('{') >> lit("\"type\"") >> lit(':') >> lit("\"MultiPoint\"") + >> -(lit(',') >> omit[bbox]) >> ((lit(',') >> lit("\"coordinates\"") >> lit(':') >> lit('[') >> -(coordinate % lit(',')) >> lit(']')) ^ (lit(',') >> properties) ^ (lit(',') >> omit[id])) @@ -142,6 +145,7 @@ topojson_grammar::topojson_grammar() multi_linestring = lit('{') >> lit("\"type\"") >> lit(':') >> lit("\"MultiLineString\"") + >> -(lit(',') >> omit[bbox]) >> ((lit(',') >> lit("\"arcs\"") >> lit(':') >> lit('[') >> -((lit('[') >> int_ >> lit(']')) % lit(',')) >> lit(']')) ^ (lit(',') >> properties) ^ (lit(',') >> omit[id])) @@ -150,6 +154,7 @@ topojson_grammar::topojson_grammar() polygon = lit('{') >> lit("\"type\"") >> lit(':') >> lit("\"Polygon\"") + >> -(lit(',') >> omit[bbox]) >> ((lit(',') >> lit("\"arcs\"") >> lit(':') >> lit('[') >> -(ring % lit(',')) >> lit(']')) ^ (lit(',') >> properties) ^ (lit(',') >> omit[id])) @@ -158,6 +163,7 @@ topojson_grammar::topojson_grammar() multi_polygon = lit('{') >> lit("\"type\"") >> lit(':') >> lit("\"MultiPolygon\"") + >> -(lit(',') >> omit[bbox]) >> ((lit(',') >> lit("\"arcs\"") >> lit(':') >> lit('[') >> -((lit('[') >> -(ring % lit(',')) >> lit(']')) % lit(',')) From 7f597233c943c29744b17760c5e995882b6ad227 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 4 Mar 2015 12:38:45 -0800 Subject: [PATCH 02/15] better exception when boost regex cannot initialize ICU support - closes #2722 --- src/expression.cpp | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/expression.cpp b/src/expression.cpp index c8798774b..ffa55ddb1 100644 --- a/src/expression.cpp +++ b/src/expression.cpp @@ -40,18 +40,29 @@ expression_ptr parse_expression(std::string const& str) auto node = std::make_shared(); std::string::const_iterator itr = str.begin(); std::string::const_iterator end = str.end(); - try { - bool r = boost::spirit::qi::phrase_parse(itr, end, g, space, *node); - if (r && itr == end) + bool r = false; + try + { + r = boost::spirit::qi::phrase_parse(itr, end, g, space, *node); + } + catch (std::exception const& ex) + { + if (std::string("boost::spirit::qi::expectation_failure") == std::string(ex.what())) { - return node; + // no need to show "boost::spirit::qi::expectation_failure" which is a std::runtime_error + throw config_error("Failed to parse expression: \"" + str + "\""); } else { - throw config_error("Failed to parse expression: \"" + str + "\""); + // show "Could not initialize ICU resources" from boost::regex which is a std::runtime_error + throw config_error(std::string(ex.what()) + " for expression: \"" + str + "\""); } } - catch (std::exception const&) // boost::spirit::qi::expectation_failure + if (r && itr == end) + { + return node; + } + else { throw config_error("Failed to parse expression: \"" + str + "\""); } From e69bedadff122c37d9113a2c104232355babeb0f Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 4 Mar 2015 16:14:07 -0800 Subject: [PATCH 03/15] add @rouault to authors for gdal plugin contributions - refs #2686 --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index e670af393..75589bfbc 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -52,6 +52,7 @@ Mapnik is written by Artem Pavlenko with contributions from: * Igor Podolskiy * Reid Priedhorsky * Brian Quinion +* Even Rouault * Marcin Rudowski * Sandro Santilli * Christopher Schmidt From d5e3dba3dfcd1babb9e119db67418ed4b3298263 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 4 Mar 2015 16:25:15 -0800 Subject: [PATCH 04/15] scons: cross compilation fixes --- SConstruct | 11 ++++++----- src/build.py | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/SConstruct b/SConstruct index 66e771006..18e95f477 100644 --- a/SConstruct +++ b/SConstruct @@ -1283,10 +1283,11 @@ if not preconfigured: else: env['MISSING_DEPS'].append('libxml2') - if conf.CheckHasDlfcn(): - env.Append(CPPDEFINES = '-DMAPNIK_HAS_DLCFN') - else: - env['SKIPPED_DEPS'].extend(['dlfcn']) + if not env['HOST']: + if conf.CheckHasDlfcn(): + env.Append(CPPDEFINES = '-DMAPNIK_HAS_DLCFN') + else: + env['SKIPPED_DEPS'].extend(['dlfcn']) OPTIONAL_LIBSHEADERS = [] @@ -1340,7 +1341,7 @@ if not preconfigured: conf.prioritize_paths(silent=True) # test for C++11 support, which is required - if not conf.supports_cxx11(): + if not env['HOST'] and not conf.supports_cxx11(): color_print(1,"C++ compiler does not support C++11 standard (-std=c++11), which is required. Please upgrade your compiler to at least g++ 4.7 (ideally 4.8)") Exit(1) diff --git a/src/build.py b/src/build.py index fca14e4b2..5311d6068 100644 --- a/src/build.py +++ b/src/build.py @@ -251,7 +251,7 @@ if env['PLUGIN_LINKING'] == 'static': lib_env.AppendUnique(CPPPATH='../plugins/') for plugin in env['REQUESTED_PLUGINS']: details = env['PLUGINS'][plugin] - if details['lib'] in env['LIBS'] or not details['lib']: + if not details['lib'] or details['lib'] in env['LIBS']: plugin_env = SConscript('../plugins/input/%s/build.py' % plugin) if not plugin_env: print("Notice: no 'plugin_env' variable found for plugin: '%s'" % plugin) From 7c9a18b16b7c022c6aaf78f2a214f0fdf1326fef Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 4 Mar 2015 16:26:12 -0800 Subject: [PATCH 05/15] scons: make harfbuzz min version recommended but not strict --- SConstruct | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SConstruct b/SConstruct index 18e95f477..b4daccc5a 100644 --- a/SConstruct +++ b/SConstruct @@ -1362,7 +1362,7 @@ if not preconfigured: env['MISSING_DEPS'].append(env['ICU_LIB_NAME']) elif libname == 'harfbuzz': if not conf.harfbuzz_version(): - env['MISSING_DEPS'].append('harfbuzz-min-version') + env['SKIPPED_DEPS'].append('harfbuzz-min-version') if env['BIGINT']: env.Append(CPPDEFINES = '-DBIGINT') From 34e02bec388988ad747f7d22a6c9a7d196853bb4 Mon Sep 17 00:00:00 2001 From: Blake Thompson Date: Thu, 5 Mar 2015 09:37:22 -0600 Subject: [PATCH 06/15] A fix for jpeg saving, as it wasn't properly dealing with a colon for saving with quality. --- src/image_util_jpeg.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/image_util_jpeg.cpp b/src/image_util_jpeg.cpp index b6da08f76..6ee8efa10 100644 --- a/src/image_util_jpeg.cpp +++ b/src/image_util_jpeg.cpp @@ -50,7 +50,7 @@ void process_rgba8_jpeg(T const& image, std::string const& t, std::ostream & str { #if defined(HAVE_JPEG) int quality = 85; - std::string val = t.substr(4); + std::string val = t.substr(5); if (!val.empty()) { if (!mapnik::util::string2int(val,quality) || quality < 0 || quality > 100) From ea6677df37175858f95630a50c9e60e06d456782 Mon Sep 17 00:00:00 2001 From: Blake Thompson Date: Thu, 5 Mar 2015 21:51:48 -0600 Subject: [PATCH 07/15] A fix to the jpeg driver making it operate with parameters like the other file formats. --- include/mapnik/image.hpp | 3 ++- include/mapnik/image_any.hpp | 1 + src/image_copy.cpp | 4 ++++ src/image_util_jpeg.cpp | 26 +++++++++++++++++++++----- 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/include/mapnik/image.hpp b/include/mapnik/image.hpp index e9796e3db..b7eed4da1 100644 --- a/include/mapnik/image.hpp +++ b/include/mapnik/image.hpp @@ -340,7 +340,8 @@ enum image_dtype : std::uint8_t image_dtype_gray64, image_dtype_gray64s, image_dtype_gray64f, - image_dtype_null + image_dtype_null, + IMAGE_DTYPE_MAX }; } // end ns diff --git a/include/mapnik/image_any.hpp b/include/mapnik/image_any.hpp index 55e75d1f2..b432227fd 100644 --- a/include/mapnik/image_any.hpp +++ b/include/mapnik/image_any.hpp @@ -306,6 +306,7 @@ inline image_any create_image_any(int width, case image_dtype_null: return image_any(std::move(image_null())); case image_dtype_rgba8: + case IMAGE_DTYPE_MAX: default: return image_any(std::move(image_rgba8(width, height, initialize, premultiplied, painted))); } diff --git a/src/image_copy.cpp b/src/image_copy.cpp index e686576db..312b51dda 100644 --- a/src/image_copy.cpp +++ b/src/image_copy.cpp @@ -340,6 +340,10 @@ MAPNIK_DECL image_any image_copy(image_any const& data, image_dtype type, double return image_any(std::move(image_copy(data, offset, scaling))); case image_dtype_null: throw std::runtime_error("Can not cast a null image"); + case IMAGE_DTYPE_MAX: + default: + throw std::runtime_error("Can not cast unknown type"); + } throw std::runtime_error("Unknown image type passed"); } diff --git a/src/image_util_jpeg.cpp b/src/image_util_jpeg.cpp index 6ee8efa10..61c11245f 100644 --- a/src/image_util_jpeg.cpp +++ b/src/image_util_jpeg.cpp @@ -46,16 +46,32 @@ jpeg_saver::jpeg_saver(std::ostream & stream, std::string const& t): stream_(stream), t_(t) {} template -void process_rgba8_jpeg(T const& image, std::string const& t, std::ostream & stream) +void process_rgba8_jpeg(T const& image, std::string const& type, std::ostream & stream) { #if defined(HAVE_JPEG) int quality = 85; - std::string val = t.substr(5); - if (!val.empty()) + //std::string val = type.substr(4); + if (type != "jpeg") { - if (!mapnik::util::string2int(val,quality) || quality < 0 || quality > 100) + boost::char_separator sep(":"); + boost::tokenizer< boost::char_separator > tokens(type, sep); + for (auto const& t : tokens) { - throw ImageWriterException("invalid jpeg quality: '" + val + "'"); + if (t == "jpeg") + { + continue; + } + else if (boost::algorithm::starts_with(t, "quality=")) + { + std::string val = t.substr(8); + if (!val.empty()) + { + if (!mapnik::util::string2int(val,quality) || quality < 0 || quality > 100) + { + throw ImageWriterException("invalid jpeg quality: '" + val + "'"); + } + } + } } } save_as_jpeg(stream, quality, image); From 051462e00fc9cc0bd68eea3feb7522ed991a6af0 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Fri, 6 Mar 2015 14:01:04 -0800 Subject: [PATCH 08/15] fix postgis error reporting - closes #2725 --- plugins/input/postgis/connection.hpp | 25 ++++++++++++++++--------- tests/python_tests/postgis_test.py | 16 ++++++++++++++-- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/plugins/input/postgis/connection.hpp b/plugins/input/postgis/connection.hpp index 8f3e6bfbb..18009e581 100644 --- a/plugins/input/postgis/connection.hpp +++ b/plugins/input/postgis/connection.hpp @@ -58,7 +58,7 @@ public: { std::string err_msg = "Postgis Plugin: "; err_msg += status(); - err_msg += "\nConnection string: '"; + err_msg += "Connection string: '"; err_msg += connection_str; err_msg += "'\n"; MAPNIK_LOG_DEBUG(postgis) << "postgis_connection: creation failed, closing connection - " << this; @@ -71,7 +71,7 @@ public: if ( ! ok ) { std::string err_msg = "Postgis Plugin: "; err_msg += status(); - err_msg += "\nConnection string: '"; + err_msg += "Connection string: '"; err_msg += connection_str; err_msg += "'\n"; close(); @@ -127,7 +127,7 @@ public: { std::string err_msg = "Postgis Plugin: "; err_msg += status(); - err_msg += "\nin executeQuery Full sql was: '"; + err_msg += "in executeQuery Full sql was: '"; err_msg += sql; err_msg += "'\n"; if ( result ) PQclear(result); @@ -142,12 +142,19 @@ public: std::string status; if (conn_) { - if ( isOK() ) return PQerrorMessage(conn_); - else return "Bad connection"; + char * err_msg = PQerrorMessage(conn_); + if (err_msg == nullptr) + { + status = "Bad connection\n"; + } + else + { + status = std::string(err_msg); + } } else { - status = "Uninitialized connection"; + status = "Uninitialized connection\n"; } return status; } @@ -167,7 +174,7 @@ public: { std::string err_msg = "Postgis Plugin: "; err_msg += status(); - err_msg += "\nin executeAsyncQuery Full sql was: '"; + err_msg += "in executeAsyncQuery Full sql was: '"; err_msg += sql; err_msg += "'\n"; clearAsyncResult(PQgetResult(conn_)); @@ -191,7 +198,7 @@ public: { std::string err_msg = "Postgis Plugin: "; err_msg += status(); - err_msg += "\nin getNextAsyncResult"; + err_msg += "in getNextAsyncResult"; clearAsyncResult(result); // We need to guarde against losing the connection // (i.e db restart) so here we invalidate the full connection @@ -208,7 +215,7 @@ public: { std::string err_msg = "Postgis Plugin: "; err_msg += status(); - err_msg += "\nin getAsyncResult"; + err_msg += "in getAsyncResult"; clearAsyncResult(result); // We need to be guarded against losing the connection // (i.e db restart), we invalidate the full connection diff --git a/tests/python_tests/postgis_test.py b/tests/python_tests/postgis_test.py index d3e3ebe3a..42e3399a0 100644 --- a/tests/python_tests/postgis_test.py +++ b/tests/python_tests/postgis_test.py @@ -283,6 +283,16 @@ if 'postgis' in mapnik.DatasourceCache.plugin_names() \ eq_(meta['encoding'],u'UTF8') eq_(meta['geometry_type'],mapnik.DataGeometryType.Polygon) + def test_bad_connection(): + try: + ds = mapnik.PostGIS(dbname=MAPNIK_TEST_DBNAME, + table='test', + max_size=20, + geometry_field='geom', + user="rolethatdoesnotexist") + except Exception, e: + assert 'role "rolethatdoesnotexist" does not exist' in str(e) + def test_empty_db(): ds = mapnik.PostGIS(dbname=MAPNIK_TEST_DBNAME,table='empty') fs = ds.featureset() @@ -844,7 +854,8 @@ if 'postgis' in mapnik.DatasourceCache.plugin_names() \ fs = ds_bad.featureset() for feature in fs: pass - except RuntimeError: + except RuntimeError, e: + assert 'invalid input syntax for integer' in str(e) failed = True eq_(failed,True) @@ -907,7 +918,8 @@ if 'postgis' in mapnik.DatasourceCache.plugin_names() \ mapnik.render_to_file(map1,'/tmp/mapnik-postgis-test-map1.png', 'png') # Test must fail if error was not raised just above eq_(False,True) - except RuntimeError: + except RuntimeError, e: + assert 'invalid input syntax for integer' in str(e) pass # This used to raise an exception before correction of issue 2042 mapnik.render_to_file(map2,'/tmp/mapnik-postgis-test-map2.png', 'png') From 61abe608ee0189c98d9f8e76f4c8085910132774 Mon Sep 17 00:00:00 2001 From: Blake Thompson Date: Fri, 6 Mar 2015 17:24:23 -0600 Subject: [PATCH 09/15] Added the ability to get_type from an image, fixed possible bugs associated with image copy. --- bindings/python/mapnik_image.cpp | 6 +++++ include/mapnik/image.hpp | 23 +++++------------ include/mapnik/image_any.hpp | 16 ++++++++++++ include/mapnik/image_view.hpp | 6 +++++ include/mapnik/image_view_any.hpp | 14 +++++++++++ include/mapnik/pixel_types.hpp | 42 +++++++++++++++++++++++-------- src/image_copy.cpp | 12 ++++++++- tests/python_tests/image_test.py | 6 +++++ 8 files changed, 96 insertions(+), 29 deletions(-) diff --git a/bindings/python/mapnik_image.cpp b/bindings/python/mapnik_image.cpp index 87c9f2d7b..8c54b6cb3 100644 --- a/bindings/python/mapnik_image.cpp +++ b/bindings/python/mapnik_image.cpp @@ -222,6 +222,11 @@ void set_pixel_int(mapnik::image_any & im, unsigned x, unsigned y, int val) mapnik::set_pixel(im, x, y, val); } +unsigned get_type(mapnik::image_any & im) +{ + return im.get_dtype(); +} + std::shared_ptr open_from_file(std::string const& filename) { boost::optional type = type_from_filename(filename); @@ -441,6 +446,7 @@ void export_image() arg("y"), arg("get_color")=false )) + .def("get_type",&get_type) .def("clear",&clear) //TODO(haoyu) The method name 'tostring' might be confusing since they actually return bytes in Python 3 diff --git a/include/mapnik/image.hpp b/include/mapnik/image.hpp index b7eed4da1..abf856e8c 100644 --- a/include/mapnik/image.hpp +++ b/include/mapnik/image.hpp @@ -124,6 +124,7 @@ class image public: using pixel = T; using pixel_type = typename T::type; + static const image_dtype dtype = T::id; static constexpr std::size_t pixel_size = sizeof(pixel_type); private: detail::image_dimensions dimensions_; @@ -313,6 +314,11 @@ public: { return painted_; } + + inline image_dtype get_dtype() const + { + return dtype; + } }; using image_rgba8 = image; @@ -327,23 +333,6 @@ using image_gray64 = image; using image_gray64s = image; using image_gray64f = image; -enum image_dtype : std::uint8_t -{ - image_dtype_rgba8 = 0, - image_dtype_gray8, - image_dtype_gray8s, - image_dtype_gray16, - image_dtype_gray16s, - image_dtype_gray32, - image_dtype_gray32s, - image_dtype_gray32f, - image_dtype_gray64, - image_dtype_gray64s, - image_dtype_gray64f, - image_dtype_null, - IMAGE_DTYPE_MAX -}; - } // end ns #endif // MAPNIK_IMAGE_DATA_HPP diff --git a/include/mapnik/image_any.hpp b/include/mapnik/image_any.hpp index b432227fd..8b2ce6627 100644 --- a/include/mapnik/image_any.hpp +++ b/include/mapnik/image_any.hpp @@ -31,6 +31,7 @@ namespace mapnik { struct image_null { using pixel_type = uint8_t; + static const image_dtype dtype = image_dtype_null; unsigned char const* getBytes() const { return nullptr; } unsigned char* getBytes() { return nullptr;} unsigned getSize() const { return 0; } @@ -40,6 +41,7 @@ struct image_null bool painted() const { return false; } double get_offset() const { return 0.0; } void set_offset(double) {} + image_dtype get_dtype() const { return dtype; } double get_scaling() const { return 1.0; } void set_scaling(double) {} bool get_premultiplied() const { return false; } @@ -88,6 +90,15 @@ struct get_bytes_visitor } }; +struct get_dtype_visitor +{ + template + image_dtype operator()(T & data) + { + return data.get_dtype(); + } +}; + struct get_bytes_visitor_const { template @@ -263,6 +274,11 @@ struct image_any : image_base return util::apply_visitor(detail::get_scaling_visitor(),*this); } + image_dtype get_dtype() const + { + return util::apply_visitor(detail::get_dtype_visitor(),*this); + } + void set_offset(double val) { util::apply_visitor(detail::set_offset_visitor(val),*this); diff --git a/include/mapnik/image_view.hpp b/include/mapnik/image_view.hpp index 640235ac5..2d2d63650 100644 --- a/include/mapnik/image_view.hpp +++ b/include/mapnik/image_view.hpp @@ -33,6 +33,7 @@ class image_view public: using pixel = typename T::pixel; using pixel_type = typename T::pixel_type; + static const image_dtype dtype = T::dtype; static constexpr std::size_t pixel_size = sizeof(pixel_type); image_view(unsigned x, unsigned y, unsigned width, unsigned height, T const& data) @@ -131,6 +132,11 @@ public: { return data_.get_scaling(); } + + inline image_dtype get_dtype() const + { + return dtype; + } private: unsigned x_; diff --git a/include/mapnik/image_view_any.hpp b/include/mapnik/image_view_any.hpp index 9f47f0ae4..90bb0dbe4 100644 --- a/include/mapnik/image_view_any.hpp +++ b/include/mapnik/image_view_any.hpp @@ -69,6 +69,15 @@ struct get_view_size_visitor } }; +struct get_view_dtype_visitor +{ + template + image_dtype operator()(T const& data) const + { + return data.get_dtype(); + } +}; + struct get_view_row_size_visitor { template @@ -148,6 +157,11 @@ struct image_view_any : image_view_base { return util::apply_visitor(detail::get_view_scaling_visitor(),*this); } + + image_dtype get_dtype() const + { + return util::apply_visitor(detail::get_view_dtype_visitor(),*this); + } }; } diff --git a/include/mapnik/pixel_types.hpp b/include/mapnik/pixel_types.hpp index aef5dca8c..2a36cd443 100644 --- a/include/mapnik/pixel_types.hpp +++ b/include/mapnik/pixel_types.hpp @@ -25,16 +25,36 @@ #include -struct rgba8_t { using type = std::uint32_t; }; -struct gray8_t { using type = std::uint8_t; }; -struct gray8s_t { using type = std::int8_t; }; -struct gray16_t { using type = std::uint16_t; }; -struct gray16s_t { using type = std::int16_t; }; -struct gray32_t { using type = std::uint32_t; }; -struct gray32s_t { using type = std::int32_t; }; -struct gray32f_t { using type = float; }; -struct gray64_t { using type = std::uint64_t; }; -struct gray64s_t { using type = std::int64_t; }; -struct gray64f_t { using type = double; }; +namespace mapnik { +enum image_dtype : std::uint8_t +{ + image_dtype_rgba8 = 0, + image_dtype_gray8, + image_dtype_gray8s, + image_dtype_gray16, + image_dtype_gray16s, + image_dtype_gray32, + image_dtype_gray32s, + image_dtype_gray32f, + image_dtype_gray64, + image_dtype_gray64s, + image_dtype_gray64f, + image_dtype_null, + IMAGE_DTYPE_MAX +}; + +struct rgba8_t { using type = std::uint32_t; static const image_dtype id = image_dtype_rgba8; }; +struct gray8_t { using type = std::uint8_t; static const image_dtype id = image_dtype_gray8; }; +struct gray8s_t { using type = std::int8_t; static const image_dtype id = image_dtype_gray8s; }; +struct gray16_t { using type = std::uint16_t; static const image_dtype id = image_dtype_gray16; }; +struct gray16s_t { using type = std::int16_t; static const image_dtype id = image_dtype_gray16s; }; +struct gray32_t { using type = std::uint32_t; static const image_dtype id = image_dtype_gray32; }; +struct gray32s_t { using type = std::int32_t; static const image_dtype id = image_dtype_gray32s; }; +struct gray32f_t { using type = float; static const image_dtype id = image_dtype_gray32f; }; +struct gray64_t { using type = std::uint64_t; static const image_dtype id = image_dtype_gray64; }; +struct gray64s_t { using type = std::int64_t; static const image_dtype id = image_dtype_gray64s; }; +struct gray64f_t { using type = double; static const image_dtype id = image_dtype_gray64f; }; + +} // end ns #endif // MAPNIK_PIXEL_TYPES_HPP diff --git a/src/image_copy.cpp b/src/image_copy.cpp index 312b51dda..f676cee3d 100644 --- a/src/image_copy.cpp +++ b/src/image_copy.cpp @@ -94,7 +94,17 @@ struct visitor_image_copy_so T0 operator() (T0 const& src) { - return T0(src); + if (offset_ == src.get_offset() && scaling_ == src.get_scaling()) + { + return T0(src); + } + else + { + T0 dst(src); + dst.set_scaling(scaling_); + dst.set_offset(offset_); + return T0(std::move(dst)); + } } template diff --git a/tests/python_tests/image_test.py b/tests/python_tests/image_test.py index d4a473d03..3a1dd9a81 100644 --- a/tests/python_tests/image_test.py +++ b/tests/python_tests/image_test.py @@ -10,6 +10,12 @@ def setup(): # from another directory we need to chdir() os.chdir(execution_path('.')) +def test_type(): + im = mapnik.Image(256, 256) + eq_(im.get_type(), mapnik.ImageType.rgba8) + im = mapnik.Image(256, 256, mapnik.ImageType.gray8) + eq_(im.get_type(), mapnik.ImageType.gray8) + def test_image_premultiply(): im = mapnik.Image(256,256) eq_(im.premultiplied(),False) From 2143dbe7efbfca4f8f4827325ddd3c50ff1806c5 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Fri, 6 Mar 2015 17:08:29 -0800 Subject: [PATCH 10/15] allow postgres tests to pass if postgres is not running: followup to #2725 --- tests/python_tests/load_map_test.py | 4 ++-- tests/python_tests/save_map_test.py | 2 +- tests/visual_tests/test.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/python_tests/load_map_test.py b/tests/python_tests/load_map_test.py index 88152c937..5eb211ed5 100644 --- a/tests/python_tests/load_map_test.py +++ b/tests/python_tests/load_map_test.py @@ -53,7 +53,7 @@ def test_can_parse_xml_with_deprecated_properties(): except RuntimeError, e: # only test datasources that we have installed if not 'Could not create datasource' in str(e) \ - and not 'Bad connection' in str(e): + and not 'could not connect' in str(e): failures.append('Failed to load valid map %s (%s)' % (filename,e)) eq_(len(failures),0,'\n'+'\n'.join(failures)) mapnik.logger.set_severity(default_logging_severity) @@ -73,7 +73,7 @@ def test_good_files(): except RuntimeError, e: # only test datasources that we have installed if not 'Could not create datasource' in str(e) \ - and not 'Bad connection' in str(e): + and not 'could not connect' in str(e): failures.append('Failed to load valid map %s (%s)' % (filename,e)) eq_(len(failures),0,'\n'+'\n'.join(failures)) diff --git a/tests/python_tests/save_map_test.py b/tests/python_tests/save_map_test.py index 1cf20e442..d7c1f0323 100644 --- a/tests/python_tests/save_map_test.py +++ b/tests/python_tests/save_map_test.py @@ -27,7 +27,7 @@ def compare_map(xml): except RuntimeError, e: # only test datasources that we have installed if not 'Could not create datasource' in str(e) \ - and not 'Bad connection' in str(e): + and not 'could not connect' in str(e): raise RuntimeError(str(e)) return (handle, test_map) = tempfile.mkstemp(suffix='.xml', prefix='mapnik-temp-map1-') diff --git a/tests/visual_tests/test.py b/tests/visual_tests/test.py index 23a056800..b8d69fa14 100755 --- a/tests/visual_tests/test.py +++ b/tests/visual_tests/test.py @@ -214,7 +214,7 @@ def render(filename, config, scale_factor, reporting): return except Exception, e: if 'Could not create datasource' in str(e) \ - or 'Bad connection' in str(e): + or 'could not connect' in str(e): return m reporting.other_error(filename, repr(e)) return m From 09afe230fb46a5a4e0626f13bbbecc396cd72de0 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Fri, 6 Mar 2015 17:10:21 -0800 Subject: [PATCH 11/15] add test for #2721 --- tests/data/json/escaped.topojson | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/data/json/escaped.topojson b/tests/data/json/escaped.topojson index ef9ea07aa..2a4ce1982 100644 --- a/tests/data/json/escaped.topojson +++ b/tests/data/json/escaped.topojson @@ -3,6 +3,12 @@ "objects": { "escaped": { "type": "GeometryCollection", + "bbox": [ + -180, + -90, + 180, + 90 + ], "geometries": [ { "type": "Point", From c1e947bff18b320f50e68e12b28344e1c8debc62 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Sat, 7 Mar 2015 12:00:38 -0800 Subject: [PATCH 12/15] fully static geojson parsing grammars --- plugins/input/geojson/geojson_datasource.cpp | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/plugins/input/geojson/geojson_datasource.cpp b/plugins/input/geojson/geojson_datasource.cpp index 0969834e2..3f008a52e 100644 --- a/plugins/input/geojson/geojson_datasource.cpp +++ b/plugins/input/geojson/geojson_datasource.cpp @@ -177,18 +177,19 @@ geojson_datasource::geojson_datasource(parameters const& params) namespace { using base_iterator_type = char const*; -const mapnik::transcoder tr("utf8"); -const mapnik::json::feature_collection_grammar fc_grammar(tr); +const mapnik::transcoder geojson_datasource_static_tr("utf8"); +const mapnik::json::feature_collection_grammar geojson_datasource_static_fc_grammar(geojson_datasource_static_tr); +const mapnik::json::feature_grammar geojson_datasource_static_feature_grammar(geojson_datasource_static_tr); +const mapnik::json::extract_bounding_box_grammar geojson_datasource_static_bbox_grammar; } template void geojson_datasource::initialise_index(Iterator start, Iterator end) { mapnik::json::boxes boxes; - mapnik::json::extract_bounding_box_grammar bbox_grammar; boost::spirit::ascii::space_type space; Iterator itr = start; - if (!boost::spirit::qi::phrase_parse(itr, end, (bbox_grammar)(boost::phoenix::ref(boxes)) , space)) + if (!boost::spirit::qi::phrase_parse(itr, end, (geojson_datasource_static_bbox_grammar)(boost::phoenix::ref(boxes)) , space)) { throw mapnik::datasource_exception("GeoJSON Plugin: could not parse: '" + filename_ + "'"); } @@ -208,10 +209,8 @@ void geojson_datasource::initialise_index(Iterator start, Iterator end) Iterator end = itr + geometry_index.second; mapnik::context_ptr ctx = std::make_shared(); mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx,1)); - static const mapnik::transcoder tr("utf8"); - static const mapnik::json::feature_grammar grammar(tr); boost::spirit::ascii::space_type space; - if (!boost::spirit::qi::phrase_parse(itr, end, (grammar)(boost::phoenix::ref(*feature)), space)) + if (!boost::spirit::qi::phrase_parse(itr, end, (geojson_datasource_static_feature_grammar)(boost::phoenix::ref(*feature)), space)) { throw std::runtime_error("Failed to parse geojson feature"); } @@ -238,7 +237,7 @@ void geojson_datasource::parse_geojson(Iterator start, Iterator end) mapnik::json::default_feature_callback callback(features_); - bool result = boost::spirit::qi::phrase_parse(start, end, (fc_grammar) + bool result = boost::spirit::qi::phrase_parse(start, end, (geojson_datasource_static_fc_grammar) (boost::phoenix::ref(ctx),boost::phoenix::ref(start_id), boost::phoenix::ref(callback)), space); if (!result) @@ -335,12 +334,10 @@ boost::optional geojson_datasource::get_geometry chr_iterator_type start = json.data(); chr_iterator_type end = start + json.size(); - static const mapnik::transcoder tr("utf8"); - static const mapnik::json::feature_grammar grammar(tr); using namespace boost::spirit; ascii::space_type space; mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx,1)); - if (!qi::phrase_parse(start, end, (grammar)(boost::phoenix::ref(*feature)), space)) + if (!qi::phrase_parse(start, end, (geojson_datasource_static_feature_grammar)(boost::phoenix::ref(*feature)), space)) { throw std::runtime_error("Failed to parse geojson feature"); } From acee056710dc376be642efbf2e25a5af189e51fa Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Sun, 8 Mar 2015 12:53:25 -0700 Subject: [PATCH 13/15] add tests validating current (non-ideal) need for manually supplying 'geometry_table' for specific sql query - refs #2718 --- tests/python_tests/postgis_test.py | 36 ++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/python_tests/postgis_test.py b/tests/python_tests/postgis_test.py index 42e3399a0..74adc6fdd 100644 --- a/tests/python_tests/postgis_test.py +++ b/tests/python_tests/postgis_test.py @@ -1170,6 +1170,42 @@ if 'postgis' in mapnik.DatasourceCache.plugin_names() \ eq_(meta.get('key_field'),"gid") eq_(meta['geometry_type'],None) + # currently needs manual `geometry_table` passed + # to avoid misparse of `geometry_table` + # in the future ideally this would not need manual `geometry_table` + # https://github.com/mapnik/mapnik/issues/2718 + # currently `bogus` would be picked automatically for geometry_table + def test_broken_parsing_of_comments(): + ds = mapnik.PostGIS(dbname=MAPNIK_TEST_DBNAME,table=''' + (select * FROM test) AS data + -- select this from bogus''', + geometry_table='test') + fs = ds.featureset() + for id in range(1,5): + eq_(fs.next().id(),id) + + meta = ds.describe() + eq_(meta['srid'],4326) + eq_(meta['geometry_type'],mapnik.DataGeometryType.Collection) + + # same + # to avoid misparse of `geometry_table` + # in the future ideally this would not need manual `geometry_table` + # https://github.com/mapnik/mapnik/issues/2718 + # currently nothing would be picked automatically for geometry_table + def test_broken_parsing_of_comments(): + ds = mapnik.PostGIS(dbname=MAPNIK_TEST_DBNAME,table=''' + (select * FROM test) AS data + -- select this from bogus.''', + geometry_table='test') + fs = ds.featureset() + for id in range(1,5): + eq_(fs.next().id(),id) + + meta = ds.describe() + eq_(meta['srid'],4326) + eq_(meta['geometry_type'],mapnik.DataGeometryType.Collection) + atexit.register(postgis_takedown) From 073e46aad8f53015e201a59942bbc5c5ffbb54f3 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Sun, 8 Mar 2015 12:54:19 -0700 Subject: [PATCH 14/15] avoid querying for srid when geometry_table is empty - this can happen when the table detection fails - refs #2718 - ideally this would not happen but it lessens the impact if it does - amends 4a1f4a9b5ea30b --- plugins/input/postgis/postgis_datasource.cpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/plugins/input/postgis/postgis_datasource.cpp b/plugins/input/postgis/postgis_datasource.cpp index 9e6cba390..570016350 100644 --- a/plugins/input/postgis/postgis_datasource.cpp +++ b/plugins/input/postgis/postgis_datasource.cpp @@ -159,6 +159,10 @@ postgis_datasource::postgis_datasource(parameters const& params) geometry_table_ = geometry_table_.substr(0); } + // NOTE: geometry_table_ how should ideally be a table name, but + // there are known edge cases where this will break down and + // geometry_table_ may even be empty: https://github.com/mapnik/mapnik/issues/2718 + // If we do not know both the geometry_field and the srid // then first attempt to fetch the geometry name from a geometry_columns entry. // This will return no records if we are querying a bogus table returned @@ -166,7 +170,7 @@ postgis_datasource::postgis_datasource(parameters const& params) // the table parameter references a table, view, or subselect not // registered in the geometry columns. geometryColumn_ = geometry_field_; - if (geometryColumn_.empty() || srid_ == 0) + if (!geometry_table_.empty() && (geometryColumn_.empty() || srid_ == 0)) { #ifdef MAPNIK_STATS mapnik::progress_timer __stats2__(std::clog, "postgis_datasource::init(get_srid_and_geometry_column)"); @@ -223,12 +227,20 @@ postgis_datasource::postgis_datasource(parameters const& params) // If we still do not know the srid then we can try to fetch // it from the 'geometry_table_' parameter, which should work even if it is // a subselect as long as we know the geometry_field to query - if (! geometryColumn_.empty() && srid_ <= 0) + if (!geometryColumn_.empty() && srid_ <= 0) { std::ostringstream s; - s << "SELECT ST_SRID(\"" << geometryColumn_ << "\") AS srid FROM " - << populate_tokens(geometry_table_) << " WHERE \"" << geometryColumn_ << "\" IS NOT NULL LIMIT 1;"; + s << "SELECT ST_SRID(\"" << geometryColumn_ << "\") AS srid FROM "; + if (!geometry_table_.empty()) + { + s << geometry_table_; + } + else + { + s << populate_tokens(table_); + } + s << " WHERE \"" << geometryColumn_ << "\" IS NOT NULL LIMIT 1;"; shared_ptr rs = conn->executeQuery(s.str()); if (rs->next()) From ffad24f31d7fb9101f6ca6ef28d3534d26a9212c Mon Sep 17 00:00:00 2001 From: Blake Thompson Date: Sun, 8 Mar 2015 23:28:56 -0500 Subject: [PATCH 15/15] Added bugfix for image_view where if an image of zero width or height created a view, it would cause a segfault because it created a 1,1 size view. --- include/mapnik/image_view.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/mapnik/image_view.hpp b/include/mapnik/image_view.hpp index 2d2d63650..ceb12f4a2 100644 --- a/include/mapnik/image_view.hpp +++ b/include/mapnik/image_view.hpp @@ -43,10 +43,10 @@ public: height_(height), data_(data) { - if (x_ >= data_.width()) x_=data_.width()-1; - if (y_ >= data_.height()) y_=data_.height()-1; - if (x_ + width_ > data_.width()) width_= data_.width() - x_; - if (y_ + height_ > data_.height()) height_= data_.height() - y_; + if (x_ >= data_.width() && data_.width() > 0) x_ = data_.width() - 1; + if (y_ >= data_.height() && data.height() > 0) y_ = data_.height() - 1; + if (x_ + width_ > data_.width()) width_ = data_.width() - x_; + if (y_ + height_ > data_.height()) height_ = data_.height() - y_; } ~image_view() {}