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 diff --git a/SConstruct b/SConstruct index 66e771006..b4daccc5a 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) @@ -1361,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') 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 e9796e3db..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,22 +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 -}; - } // end ns #endif // MAPNIK_IMAGE_DATA_HPP diff --git a/include/mapnik/image_any.hpp b/include/mapnik/image_any.hpp index 55e75d1f2..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); @@ -306,6 +322,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/include/mapnik/image_view.hpp b/include/mapnik/image_view.hpp index 640235ac5..ceb12f4a2 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) @@ -42,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() {} @@ -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/plugins/input/geojson/geojson_datasource.cpp b/plugins/input/geojson/geojson_datasource.cpp index 89e2b7e88..720f11953 100644 --- a/plugins/input/geojson/geojson_datasource.cpp +++ b/plugins/input/geojson/geojson_datasource.cpp @@ -176,18 +176,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_ + "'"); } @@ -207,10 +208,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"); } @@ -237,7 +236,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) 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/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()) 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) 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 + "\""); } diff --git a/src/image_copy.cpp b/src/image_copy.cpp index e686576db..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 @@ -340,6 +350,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 b6da08f76..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(4); - 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); 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", 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) 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/postgis_test.py b/tests/python_tests/postgis_test.py index d3e3ebe3a..74adc6fdd 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') @@ -1158,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) 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