From 75f7cb26bba1439cf256b0b8a466d49aeb43b66c Mon Sep 17 00:00:00 2001 From: Blake Thompson Date: Tue, 9 Jun 2015 09:18:52 -0500 Subject: [PATCH 001/165] Reversed the offsets --- include/mapnik/offset_converter.hpp | 20 +++++++++---------- src/text/placement_finder.cpp | 7 ++++--- test/unit/vertex_adapter/offset_converter.cpp | 8 ++++---- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/include/mapnik/offset_converter.hpp b/include/mapnik/offset_converter.hpp index b32d8170c..723282e91 100644 --- a/include/mapnik/offset_converter.hpp +++ b/include/mapnik/offset_converter.hpp @@ -238,8 +238,8 @@ private: */ static void displace(vertex2d & v, double dx, double dy, double a) { - v.x += dx * std::cos(a) + dy * std::sin(a); - v.y += dx * std::sin(a) - dy * std::cos(a); + v.x += dx * std::cos(a) - dy * std::sin(a); + v.y += dx * std::sin(a) + dy * std::cos(a); } /** @@ -247,8 +247,8 @@ private: */ void displace(vertex2d & v, double a) const { - v.x += offset_ * std::sin(a); - v.y -= offset_ * std::cos(a); + v.x -= offset_ * std::sin(a); + v.y += offset_ * std::cos(a); } /** @@ -256,8 +256,8 @@ private: */ void displace(vertex2d & v, vertex2d const& u, double a) const { - v.x = u.x + offset_ * std::sin(a); - v.y = u.y - offset_ * std::cos(a); + v.x = u.x - offset_ * std::sin(a); + v.y = u.y + offset_ * std::cos(a); v.cmd = u.cmd; } @@ -266,8 +266,8 @@ private: double sa = offset_ * std::sin(a); double ca = offset_ * std::cos(a); double h = std::tan(0.5 * (b - a)); - v.x = v.x + sa + h * ca; - v.y = v.y - ca + h * sa; + v.x = v.x - sa - h * ca; + v.y = v.y + ca - h * sa; } status init_vertices() @@ -343,7 +343,7 @@ private: double half_turns = half_turn_segments_ * std::fabs(joint_angle); int bulge_steps = 0; - if (offset_ < 0.0) + if (offset_ > 0.0) { if (joint_angle > 0.0) { @@ -444,7 +444,7 @@ private: double half_turns = half_turn_segments_ * std::fabs(joint_angle); int bulge_steps = 0; - if (offset_ < 0.0) + if (offset_ > 0.0) { if (joint_angle > 0.0) { diff --git a/src/text/placement_finder.cpp b/src/text/placement_finder.cpp index ff44147d2..32603343a 100644 --- a/src/text/placement_finder.cpp +++ b/src/text/placement_finder.cpp @@ -209,7 +209,8 @@ bool placement_finder::single_line_placement(vertex_cache &pp, text_upright_e or pixel_position align_offset = layout.alignment_offset(); pixel_position const& layout_displacement = layout.displacement(); double sign = (real_orientation == UPRIGHT_LEFT) ? -1 : 1; - double offset = layout_displacement.y + 0.5 * sign * layout.height(); + //double offset = 0 - (layout_displacement.y + 0.5 * sign * layout.height()); + double offset = layout_displacement.y - 0.5 * sign * layout.height(); double adjust_character_spacing = .0; double layout_width = layout.width(); bool adjust = layout.horizontal_alignment() == H_ADJUST; @@ -228,7 +229,7 @@ bool placement_finder::single_line_placement(vertex_cache &pp, text_upright_e or { // Only subtract half the line height here and half at the end because text is automatically // centered on the line - offset -= sign * line.height()/2; + offset += sign * line.height()/2; vertex_cache & off_pp = pp.get_offseted(offset, sign * layout_width); vertex_cache::scoped_state off_state(off_pp); // TODO: Remove this when a clean implementation in vertex_cache::get_offseted is done double line_width = adjust ? (line.glyphs_width() + line.space_count() * adjust_character_spacing) : line.width(); @@ -296,7 +297,7 @@ bool placement_finder::single_line_placement(vertex_cache &pp, text_upright_e or glyphs->emplace_back(glyph, pos, rot); } // See comment above - offset -= sign * line.height()/2; + offset += sign * line.height()/2; } } diff --git a/test/unit/vertex_adapter/offset_converter.cpp b/test/unit/vertex_adapter/offset_converter.cpp index 50740b7eb..38c91b5dc 100644 --- a/test/unit/vertex_adapter/offset_converter.cpp +++ b/test/unit/vertex_adapter/offset_converter.cpp @@ -74,7 +74,7 @@ void test_simple_segment(double const &offset) { fake_path path = {0, 0, 1, 0}, off_path = {0, offset, 1, offset}; mapnik::offset_converter off_path_new(path); - off_path_new.set_offset(-offset); + off_path_new.set_offset(offset); double x0, y0, x1, y1; unsigned cmd0 = off_path_new.vertex(&x0, &y0); @@ -116,7 +116,7 @@ void test_straight_line(double const &offset) { fake_path path = {0, 0, 1, 0, 9, 0, 10, 0}, off_path = {0, offset, 1, offset, 9, offset, 10, offset}; mapnik::offset_converter off_path_new(path); - off_path_new.set_offset(-offset); + off_path_new.set_offset(offset); double x0, y0, x1, y1; unsigned cmd0 = off_path_new.vertex(&x0, &y0); @@ -166,7 +166,7 @@ void test_offset_curve(double const &offset) { fake_path path(pos), off_path(off_pos); mapnik::offset_converter off_path_new(path); - off_path_new.set_offset(-offset); + off_path_new.set_offset(offset); double x0, y0, x1, y1; unsigned cmd0 = off_path_new.vertex(&x0, &y0); @@ -222,7 +222,7 @@ void test_s_shaped_curve(double const &offset) { fake_path path(pos), off_path(off_pos); mapnik::offset_converter off_path_new(path); - off_path_new.set_offset(-offset); + off_path_new.set_offset(offset); double x0, y0, x1, y1; unsigned cmd0 = off_path_new.vertex(&x0, &y0); From d3f37d8f1e6bf2dd7aa4144743cd603f27599b99 Mon Sep 17 00:00:00 2001 From: Blake Thompson Date: Thu, 11 Jun 2015 12:47:00 -0500 Subject: [PATCH 002/165] Updated clipper to be reversed and fixes to the way the algorithm operates. --- include/mapnik/offset_converter.hpp | 144 ++++++++++++++++------------ test/data-visual | 2 +- 2 files changed, 82 insertions(+), 64 deletions(-) diff --git a/include/mapnik/offset_converter.hpp b/include/mapnik/offset_converter.hpp index 4d75d679c..93f45fcf2 100644 --- a/include/mapnik/offset_converter.hpp +++ b/include/mapnik/offset_converter.hpp @@ -266,16 +266,27 @@ private: double sa = offset_ * std::sin(a); double ca = offset_ * std::cos(a); double h = std::tan(0.5 * (b - a)); - if (h > 1.5) + double hsa = h * sa; + double hca = h * ca; + double abs_offset = std::abs(offset_); + if (hsa > 1.0 * abs_offset) { - h = 1.5; + hsa = 1.0 * abs_offset; } - else if (h < -1.5) + else if (hsa < -1.0 * abs_offset) { - h = -1.5; + hsa = -1.0 * abs_offset; } - v.x = v.x - sa - h * ca; - v.y = v.y + ca - h * sa; + if (hca > 1.0 * abs_offset) + { + hca = 1.0 * abs_offset; + } + else if (hca < -1.0 * abs_offset) + { + hca = -1.0 * abs_offset; + } + v.x = v.x - sa - hca; + v.y = v.y + ca - hsa; } status init_vertices() @@ -326,17 +337,28 @@ private: } double angle_a = 0; + // The vector parts from v1 to v0. + double v_x1x0 = 0; + double v_y1y0 = 0; + // The vector parts from v1 to v2; + double v_x1x2 = v2.x - v1.x; + double v_y1y2 = v2.y - v1.y; + if (is_polygon) { - double x = v1.x - close_points[cpt].x; - double y = v1.y - close_points[cpt].y; + v_x1x0 = close_points[cpt].x - v1.x; + v_y1y0 = close_points[cpt].y - v1.y; cpt++; - x = std::abs(x) < std::numeric_limits::epsilon() ? 0 : x; - y = std::abs(y) < std::numeric_limits::epsilon() ? 0 : y; - angle_a = std::atan2(y, x); + angle_a = std::atan2(-v_y1y0, -v_x1x0); } - double angle_b = std::atan2((v2.y - v1.y), (v2.x - v1.x)); + // dot product + double dot; + // determinate + double det; + double angle_b = std::atan2(v_y1y2, v_x1x2); + // Angle between the two vectors double joint_angle; + double curve_angle; if (!is_polygon) { @@ -346,33 +368,28 @@ private: } else { - joint_angle = explement_reflex_angle(angle_b - angle_a); - - double half_turns = half_turn_segments_ * std::fabs(joint_angle); - int bulge_steps = 0; - + dot = v_x1x0 * v_x1x2 + v_y1y0 * v_y1y2; // dot product + det = v_x1x0 * v_y1y2 - v_y1y0 * v_x1x2; // determinant + + joint_angle = std::atan2(det, dot); // atan2(y, x) or atan2(sin, cos) + if (joint_angle < 0) joint_angle = joint_angle + 2 * M_PI; + joint_angle = std::fmod(joint_angle, 2 * M_PI); + if (offset_ > 0.0) { - if (joint_angle > 0.0) - { - joint_angle = joint_angle - 2 * M_PI; - } - else - { - bulge_steps = 1 + static_cast(std::floor(half_turns / M_PI)); - } + joint_angle = 2 * M_PI - joint_angle; } - else + + int bulge_steps = 0; + + if (std::abs(joint_angle) > M_PI) { - if (joint_angle < 0.0) - { - joint_angle = joint_angle + 2 * M_PI; - } - else - { - bulge_steps = 1 + static_cast(std::floor(half_turns / M_PI)); - } + curve_angle = explement_reflex_angle(angle_b - angle_a); + // Bulge steps should be determined by the inverse of the joint angle. + double half_turns = half_turn_segments_ * std::fabs(curve_angle); + bulge_steps = 1 + static_cast(std::floor(half_turns / M_PI)); } + if (bulge_steps == 0) { displace2(v1, angle_a, angle_b); @@ -415,11 +432,8 @@ private: v1.y = start_.y; if (cpt < close_points.size()) { - double x = v1.x - close_points[cpt].x; - double y = v1.y - close_points[cpt].y; - x = std::abs(x) < std::numeric_limits::epsilon() ? 0.0 : x; - y = std::abs(y) < std::numeric_limits::epsilon() ? 0.0 : y; - angle_b = std::atan2(y,x); + v_x1x2 = v1.x - close_points[cpt].x; + v_y1y2 = v1.y - close_points[cpt].y; cpt++; } } @@ -445,34 +459,39 @@ private: v2.x = start_.x; v2.y = start_.y; } - angle_a = angle_b; - angle_b = std::atan2((v2.y - v1.y), (v2.x - v1.x)); - joint_angle = explement_reflex_angle(angle_b - angle_a); - double half_turns = half_turn_segments_ * std::fabs(joint_angle); - int bulge_steps = 0; + // Switch the previous vector's direction as the origin has changed + v_x1x0 = -v_x1x2; + v_y1y0 = -v_y1y2; + // Calculate new angle_a + angle_a = std::atan2(v_y1y2, v_x1x2); + + // Calculate the new vector + v_x1x2 = v2.x - v1.x; + v_y1y2 = v2.y - v1.y; + // Calculate the new angle_b + angle_b = std::atan2(v_y1y2, v_x1x2); + dot = v_x1x0 * v_x1x2 + v_y1y0 * v_y1y2; // dot product + det = v_x1x0 * v_y1y2 - v_y1y0 * v_x1x2; // determinant + + joint_angle = std::atan2(det, dot); // atan2(y, x) or atan2(sin, cos) + if (joint_angle < 0) joint_angle = joint_angle + 2 * M_PI; + joint_angle = std::fmod(joint_angle, 2 * M_PI); + if (offset_ > 0.0) { - if (joint_angle > 0.0) - { - joint_angle = joint_angle - 2 * M_PI; - } - else - { - bulge_steps = 1 + static_cast(std::floor(half_turns / M_PI)); - } + joint_angle = 2 * M_PI - joint_angle; } - else + + int bulge_steps = 0; + + if (std::abs(joint_angle) > M_PI) { - if (joint_angle < 0.0) - { - joint_angle = joint_angle + 2 * M_PI; - } - else - { - bulge_steps = 1 + static_cast(std::floor(half_turns / M_PI)); - } + curve_angle = explement_reflex_angle(angle_b - angle_a); + // Bulge steps should be determined by the inverse of the joint angle. + double half_turns = half_turn_segments_ * std::fabs(curve_angle); + bulge_steps = 1 + static_cast(std::floor(half_turns / M_PI)); } #ifdef MAPNIK_LOG @@ -516,10 +535,9 @@ private: displace(w, v1, angle_a); w.cmd = SEG_LINETO; push_vertex(w); - for (int s = 0; ++s < bulge_steps;) { - displace(w, v1, angle_a + (joint_angle * s) / bulge_steps); + displace(w, v1, angle_a + (curve_angle * s) / bulge_steps); w.cmd = SEG_LINETO; push_vertex(w); } diff --git a/test/data-visual b/test/data-visual index cdb6afca7..70fc341ae 160000 --- a/test/data-visual +++ b/test/data-visual @@ -1 +1 @@ -Subproject commit cdb6afca7b517c1fdda824fcee4abb60ed525331 +Subproject commit 70fc341ae3e70f52bc8c09115d3344692844cf88 From 127279682206e27bb6f7e5bd81ff5fa98824fa8b Mon Sep 17 00:00:00 2001 From: Blake Thompson Date: Tue, 16 Jun 2015 14:21:55 -0500 Subject: [PATCH 003/165] Updated visual tests --- test/data-visual | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/data-visual b/test/data-visual index 272c9fc56..e27fe7e42 160000 --- a/test/data-visual +++ b/test/data-visual @@ -1 +1 @@ -Subproject commit 272c9fc5667ba0d6b349622f787c594fbe29cc83 +Subproject commit e27fe7e42822864ff56f438a834f5f9b699cc457 From fa1950c5d0f14068cc3b4096cec6737ff276be75 Mon Sep 17 00:00:00 2001 From: Blake Thompson Date: Tue, 16 Jun 2015 20:54:24 -0500 Subject: [PATCH 004/165] Updated visual tests --- test/data-visual | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/data-visual b/test/data-visual index e27fe7e42..1ff20dbb0 160000 --- a/test/data-visual +++ b/test/data-visual @@ -1 +1 @@ -Subproject commit e27fe7e42822864ff56f438a834f5f9b699cc457 +Subproject commit 1ff20dbb0550da6ab37374b0525f289150c79652 From c4625e06bc6b8c401eefdf943097539b8c941a69 Mon Sep 17 00:00:00 2001 From: Jiri Drbalek Date: Fri, 12 Jun 2015 15:52:50 +0000 Subject: [PATCH 005/165] visual tests: add command line argument for controlling log level --- test/visual/run.cpp | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/test/visual/run.cpp b/test/visual/run.cpp index 1353bcc42..d44508188 100644 --- a/test/visual/run.cpp +++ b/test/visual/run.cpp @@ -31,6 +31,18 @@ #include "cleanup.hpp" // run_cleanup() +#ifdef MAPNIK_LOG +using log_levels_map = std::map; + +log_levels_map log_levels +{ + { "debug", mapnik::logger::severity_type::debug }, + { "warn", mapnik::logger::severity_type::warn }, + { "error", mapnik::logger::severity_type::error }, + { "none", mapnik::logger::severity_type::none } +}; +#endif + int main(int argc, char** argv) { using namespace visual_tests; @@ -49,6 +61,11 @@ int main(int argc, char** argv) ("styles", po::value>(), "selected styles to test") ("fonts", po::value()->default_value("fonts"), "font search path") ("plugins", po::value()->default_value("plugins/input"), "input plugins search path") +#ifdef MAPNIK_LOG + ("log", po::value()->default_value(std::find_if(log_levels.begin(), log_levels.end(), + [](log_levels_map::value_type const & level) { return level.second == mapnik::logger::get_severity(); } )->first), + "log level (debug, warn, error, none)") +#endif ; po::positional_options_description p; @@ -63,6 +80,20 @@ int main(int argc, char** argv) return 1; } +#ifdef MAPNIK_LOG + std::string log_level(vm["log"].as()); + log_levels_map::const_iterator level_iter = log_levels.find(log_level); + if (level_iter == log_levels.end()) + { + std::cerr << "Error: Unknown log level: " << log_level << std::endl; + return 1; + } + else + { + mapnik::logger::set_severity(level_iter->second); + } +#endif + mapnik::freetype_engine::register_fonts(vm["fonts"].as(), true); mapnik::datasource_cache::instance().register_datasources(vm["plugins"].as()); From 2a2e0d8260a58035f46257baea96cd08057ae6f6 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 30 Jun 2015 00:06:24 -0700 Subject: [PATCH 006/165] update visual test refs [skip ci] - refs #2904 --- test/data | 2 +- test/data-visual | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/data b/test/data index a6838f99a..2823c6a0b 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit a6838f99a4c4be37989cea6718442cf4b53bd616 +Subproject commit 2823c6a0ba9e642869537d8e5c1a1ca5fd3de18a diff --git a/test/data-visual b/test/data-visual index 7080866ca..efb81026f 160000 --- a/test/data-visual +++ b/test/data-visual @@ -1 +1 @@ -Subproject commit 7080866ca1b3523fead1a25c017bd87082806eb4 +Subproject commit efb81026fce780cf2731f02f3fb55f9096c3dff0 From 89f0f53938f8c20777d5e01c1be162a005bf9c6e Mon Sep 17 00:00:00 2001 From: Blake Thompson Date: Tue, 30 Jun 2015 13:02:28 -0500 Subject: [PATCH 007/165] Updated visual tests to be in line with python mapnik --- test/data-visual | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/data-visual b/test/data-visual index 1c4c5bc2d..d694deeca 160000 --- a/test/data-visual +++ b/test/data-visual @@ -1 +1 @@ -Subproject commit 1c4c5bc2d77d86c466087d004fb37cf1bef2f9dd +Subproject commit d694deeca509481d540568b85f079ee35a3fda39 From 25016f1a2f6cab133f166b70ae5f1b81a6894610 Mon Sep 17 00:00:00 2001 From: Blake Thompson Date: Tue, 30 Jun 2015 14:30:02 -0500 Subject: [PATCH 008/165] Updated changelog some #2934 --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49849e340..40cdf34ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,11 +25,11 @@ geometry. - `ShieldSymbolizer` now supports `smooth`, `simplify`, `halo-opacity`, `halo-comp-op`, and `halo-transform` - New GroupSymbolizer for applying multiple symbolizers in a single layout -Released ... +Released: June 30th, 2015 (Packaged from ...) -Summary: TODO +Summary: The 3.0 release is a major milestone for mapnik and includes many changes in a very large number of areas. As we there has not been an official release of mapnik since 2.2 on June 3rd, 2013. This version includes a very large number of internal changes to the way rasters and geometry are processed. It also includes a large number of performance improvements in almost every area of the processing. The python bindings that were bundled with mapnik have now been moved to their own repository and are versioned independently of the mapnik library. - PostGIS: Added support for rendering 3D and 4D geometries (previously silently skipped) (#44) From 9419219b14fb1f59dc31dba900ffede9c5402b19 Mon Sep 17 00:00:00 2001 From: Blake Thompson Date: Tue, 30 Jun 2015 16:28:16 -0500 Subject: [PATCH 009/165] Changed extension to use standard md for markdown. --- INSTALL.md | 2 +- docs/{contributing.markdown => contributing.md} | 0 docs/{design.markdown => design.md} | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename docs/{contributing.markdown => contributing.md} (100%) rename docs/{design.markdown => design.md} (100%) diff --git a/INSTALL.md b/INSTALL.md index 974a2809c..a3d16a6d6 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -178,4 +178,4 @@ tutorials on how to programmatically use Mapnik. ### Contributers -Read docs/contributing.markdown for resources for getting involved with Mapnik development. +Read docs/contributing.md for resources for getting involved with Mapnik development. diff --git a/docs/contributing.markdown b/docs/contributing.md similarity index 100% rename from docs/contributing.markdown rename to docs/contributing.md diff --git a/docs/design.markdown b/docs/design.md similarity index 100% rename from docs/design.markdown rename to docs/design.md From a4753f4c87684b97a8b2af862b3b095ee580c405 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 30 Jun 2015 16:12:22 -0700 Subject: [PATCH 010/165] update CHANGELOG [skip ci] - refs #2934 --- CHANGELOG.md | 125 +++++++++++++++++++++++++++++++++++++++------------ INSTALL.md | 14 ++++-- 2 files changed, 107 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40cdf34ca..4acdcb927 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,42 +8,112 @@ For a complete change history, see the git log. ## 3.0.0 -- Added new and experimental `dot` symbolizer for fast rendering of points -- Improved support for International Text (now uses harfbuzz library for text shaping) -- Uses latest c++11 features for better performance (especially map loading) -- Expressions everywhere: all symbolizer properties can now be data driven expressions (with the exception of `face-name` and `fontset-name` on the `TextSymbolizer`). -- New functions supported in expressions: `exp`, `sin`, `cos`, `tan`, `atan`, `abs`. -- New constants supported in expressions: `PI`, `DEG_TO_RAD`, `RAD_TO_DEG` -- Pattern symbolizers now support SVG input and applying transformations on them dynamically -- Experimental / interface may change: `@variables` can be passed to renderer and evaluated in expressions -- Supports being built with clang++ using `-fvisibility=hidden -flto` for smaller binaries -- Supports being built with Visual Studio 2014 CTP #3 -- Shield icons are now pixel snapped for crisp rendering -- `MarkersSymbolizer` now supports `avoid-edges`, `offset`, `geometry-transform`, `simplify` for `line` placement and two new `placement` options called `vertex-last` and `vertex-first` to place a single marker at the end or beginning of a path. Also `clip` is now respected when rendering markers on a LineString -geometry. -- `TextSymbolizer` now supports `smooth`, `simplify`, `halo-opacity`, `halo-comp-op`, and `halo-transform` -- `ShieldSymbolizer` now supports `smooth`, `simplify`, `halo-opacity`, `halo-comp-op`, and `halo-transform` -- New GroupSymbolizer for applying multiple symbolizers in a single layout - Released: June 30th, 2015 (Packaged from ...) -Summary: The 3.0 release is a major milestone for mapnik and includes many changes in a very large number of areas. As we there has not been an official release of mapnik since 2.2 on June 3rd, 2013. This version includes a very large number of internal changes to the way rasters and geometry are processed. It also includes a large number of performance improvements in almost every area of the processing. The python bindings that were bundled with mapnik have now been moved to their own repository and are versioned independently of the mapnik library. +#### Summary -- PostGIS: Added support for rendering 3D and 4D geometries (previously silently skipped) (#44) +The 3.0 release is a major milestone for Mapnik and includes many performance and design improvements. The is the first release to provide text shaping using the harfbuzz library. This harfbuzz support unlocks improved rendering and layer for many new languages, particularly SE Asian scripts. The internal storage for working with images and geometries has been made more flexible, faster, and strongly typed. The python bindings that were previously bundled with Mapnik have now been moved to and are versioned independently. + +#### Notice + + - Mapnik 3.0.0 requires a compiler capable of `std=c++11`. + - It is highly recommended you use the `clang++` compiler on both OS X and Linux since it has robust c++11 support lower memory requirements. + +##### Major Changes + +- Improved support for International Text (now uses harfbuzz library for text shaping) + +- Uses latest C++11 features for better performance (especially map loading) + +- Expressions everywhere: all symbolizer properties can now be data driven expressions (with the exception of `face-name` and `fontset-name` on the `TextSymbolizer`). + +- Rewritten geometry storage based on `std::vector` (#2739) + - Separate storage of polygon exterior rings and interior rings to allow for more robust clipping of parts. + - Enforces consistent winding order per OGC spec (exterior rings are CCW, interior CW) + - Reduced memory consumption for layers with many points + - Ability to adapt Mapnik geometries to boost::geometry operations (in a zero-copy way) + - Ability to have i/o grammars for json/wkt work on geometries rather than paths for better efficiency and simpler code + +- Added new and experimental `dot` symbolizer for fast rendering of points + +- New functions supported in expressions: `exp`, `sin`, `cos`, `tan`, `atan`, `abs`. + +- New constants supported in expressions: `PI`, `DEG_TO_RAD`, `RAD_TO_DEG` + +- Added support for a variety of different grayscale images: + - `mapnik.imageType.null` + - `mapnik.imageType.rgba8` + - `mapnik.imageType.gray8` + - `mapnik.imageType.gray8s` + - `mapnik.imageType.gray16` + - `mapnik.imageType.gray16s` + - `mapnik.imageType.gray32` + - `mapnik.imageType.gray32s` + - `mapnik.imageType.gray32f` + - `mapnik.imageType.gray64` + - `mapnik.imageType.gray64s` + - `mapnik.imageType.gray64f` + +- Pattern symbolizers now support SVG input and applying transformations on them dynamically + +- Experimental / interface may change: `@variables` can be passed to renderer and evaluated in expressions + +- Supports being built with clang++ using `-fvisibility=hidden -flto` for smaller binaries + +- Supports being built with Visual Studio 2014 CTP #3 + +- Shield icons are now pixel snapped for crisp rendering + +- `MarkersSymbolizer` now supports `avoid-edges`, `offset`, `geometry-transform`, `simplify` for `line` placement and two new `placement` options called `vertex-last` and `vertex-first` to place a single marker at the end or beginning of a path. Also `clip` is now respected when rendering markers on a LineString +geometry. + +- `TextSymbolizer` now supports `smooth`, `simplify`, `halo-opacity`, `halo-comp-op`, and `halo-transform` + +- `ShieldSymbolizer` now supports `smooth`, `simplify`, `halo-opacity`, `halo-comp-op`, and `halo-transform` + +- The `text-transform` property of `TextSymbolizer` now supports `reverse` value to flip direction of text. + +- The `TextSymbolizer` now supports `font-feature-settings` for advanced control over Opentype font rendering (https://developer.mozilla.org/en-US/docs/Web/CSS/font-feature-settings) + +- New GroupSymbolizer for applying multiple symbolizers in a single layout - AGG renderer: fixed geometry offsetting to work after smoothing to produce more consistent results (#2202) - AGG renderer: increased `vertex_dist_epsilon` to ensure nearly coincident points are discarded more readily (#2196) -- GDAL plugin: Added back support for user driven `nodata` on rgb(a) images (#2023) +- GDAL plugin + - Now keeps datasets open for the lifetime of the datasource (rather than per featureset) + - Added back support for user driven `nodata` on rgb(a) images (#2023) + - Allowed nodata to override alpha band if set on rgba images (#2023) + - Added `nodata_tolerance` option to set nearby pixels transparent (has similar effect to the `nearblack` program) (#2023) + - At process exit Mapnik core no longer calls `dlclose` on gdal.input (#2716) -- GDAL plugin: Allowed nodata to override alpha band if set on rgba images (#2023) +- TopoJSON plugin + - Now supporting optional `bbox` property on layer + - Fixed support for reporting correct `feature.id()` + - Now supports `inline` string for passing data from memory + - Faster parsing via static initialization of grammars + - Fix crash on invalid arc index -- GDAL plugin: Added `nodata_tolerance` option to set nearby pixels transparent (has similar effect to the `nearblack` program) (#2023) +- GeoJSON plugin + - Now supporting optional `bbox` property on layer + - Fixed support for reporting correct `feature.id()` + - Now supports `inline` string for passing data from memory + - Faster parsing via static initialization of grammars -- Added support for web fonts: .woff format (#2113) +- SQLite plugin + - Fixed support for handling all column types + +- CSV Plugin + - Added the ability to pass an `extent` in options + +- PostGIS plugin + - Added Async support to - https://github.com/mapnik/mapnik/wiki/PostGIS-Async + - Added support for rendering 3D and 4D geometries (previously silently skipped) (#44) + +- Added support for web fonts: `.woff` format (#2113) - Added missing support for `geometry-transform` in `line-pattern` and `polygon-pattern` symbolizers (#2065) @@ -51,11 +121,9 @@ Summary: The 3.0 release is a major milestone for mapnik and includes many chang - Upgraded unifont to `unifont-6.3.20131020` -- CSV Plugin: added the ability to pass an `extent` in options - - Fixed crash when rendering to cairo context from python (#2031) -- Moved `label-position-tolerance` from unsigned type to double +- Moved `label-position-tolerance` from `unsigned` type to `double` - Added support for more seamless blurring by rendering to a larger internal image to avoid edge effects (#1478) @@ -67,8 +135,6 @@ Summary: The 3.0 release is a major milestone for mapnik and includes many chang - Added `color-to-alpha` `image-filter` to allow for applying alpha in proportion to color similiarity (#2023) -- Added Async support to PostGIS plugin - https://github.com/mapnik/mapnik/wiki/PostGIS-Async - - Fixed alpha handling bug with `comp-op:dst-over` (#1995) - Fixed alpha handling bug with building-fill-opacity (#2011) @@ -133,6 +199,9 @@ are not set. (#1966) - Added `scale-hsla` image-filter that allows scaling colors in HSL color space. RGB is converted to HSL (hue-saturation-lightness) and then each value (and the original alpha value) is stretched based on the specified scaling values. An example syntax is `scale-hsla(0,1,0,1,0,1,0,1)` which means no change because the full range will be kept (0 for lowest, 1 for highest). Other examples are: 1) `scale-hsla(0,0,0,1,0,1,0,1)` which would force all colors to be red in hue in the same way `scale-hsla(1,1,0,1,0,1,0,1)` would, 2) `scale-hsla(0,1,1,1,0,1,0,1)` which would cause all colors to become fully saturated, 3) `scale-hsla(0,1,1,1,0,1,.5,1)` which would force no colors to be any more transparent than half, and 4) `scale-hsla(0,1,1,1,0,1,0,.5)` which would force all colors to be at least half transparent. (#1954) +- The `shapeindex` tool now works correctly with point 3d geometry types + + ## 2.2.0 Released June 3rd, 2013 diff --git a/INSTALL.md b/INSTALL.md index a3d16a6d6..b88abd2a4 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -5,17 +5,23 @@ Mapnik runs on Linux, OS X, Windows, and BSD systems. To configure and build Mapnik do: ```bash - $ ./configure - $ make +./configure +make ``` To trigger parallel compilation you can pass a JOBS value to make: ```bash - $ JOBS=4 make +JOBS=4 make ``` -(Note that compiling Mapnik needs several GBytes of RAM. If you use parallel compilation it needs more.) +Mapnik needs > 2 GB of RAM to build. If you use parallel compilation it needs more. + +If you are on a system with less memory make sure you only build with one JOB: + +```bash +JOBS=1 make +``` To use a Python interpreter that is not named `python` for your build, do something like the following instead: From 1eea7294aeb7eb058b28ef1b5f0a295f936bbaab Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 30 Jun 2015 17:04:48 -0700 Subject: [PATCH 011/165] ensure tests pass with no plugins built --- test/standalone/csv_test.cpp | 916 ++++++++++++----------- test/unit/core/copy_move_test.cpp | 85 ++- test/unit/imaging/image_painted_test.cpp | 83 +- 3 files changed, 550 insertions(+), 534 deletions(-) diff --git a/test/standalone/csv_test.cpp b/test/standalone/csv_test.cpp index 0c1552c6a..9405e6615 100644 --- a/test/standalone/csv_test.cpp +++ b/test/standalone/csv_test.cpp @@ -10,7 +10,7 @@ #include #include #include - +#include #include #include #include @@ -157,519 +157,525 @@ void require_geometry(mapnik::feature_ptr feature, } } // anonymous namespace -const bool registered = mapnik::datasource_cache::instance().register_datasources("./plugins/input/"); - TEST_CASE("csv") { - REQUIRE(registered); - // make the tests silent since we intentially test error conditions that are noisy - auto const severity = mapnik::logger::instance().get_severity(); - mapnik::logger::instance().set_severity(mapnik::logger::none); + std::string csv_plugin("./plugins/input/csv.input"); + if (mapnik::util::exists(csv_plugin)) + { - // check the CSV datasource is loaded - const std::vector plugin_names = - mapnik::datasource_cache::instance().plugin_names(); - const bool have_csv_plugin = - std::find(plugin_names.begin(), plugin_names.end(), "csv") != plugin_names.end(); + const bool registered = mapnik::datasource_cache::instance().register_datasources(csv_plugin); - SECTION("broken files") { - if (have_csv_plugin) { - std::vector broken; - add_csv_files("test/data/csv/fails", broken); - add_csv_files("test/data/csv/warns", broken); - broken.emplace_back("test/data/csv/fails/does_not_exist.csv"); + REQUIRE(registered); - for (auto const &path : broken) { - REQUIRE_THROWS(get_csv_ds(path.native())); - } - } - } // END SECTION + // make the tests silent since we intentially test error conditions that are noisy + auto const severity = mapnik::logger::instance().get_severity(); + mapnik::logger::instance().set_severity(mapnik::logger::none); - SECTION("good files") { - if (have_csv_plugin) { - std::vector good; - add_csv_files("test/data/csv", good); - add_csv_files("test/data/csv/warns", good); + // check the CSV datasource is loaded + const std::vector plugin_names = + mapnik::datasource_cache::instance().plugin_names(); + const bool have_csv_plugin = + std::find(plugin_names.begin(), plugin_names.end(), "csv") != plugin_names.end(); - for (auto const &path : good) { - auto ds = get_csv_ds(path.native(), false); - // require a non-null pointer returned - REQUIRE(bool(ds)); - } - } - } // END SECTION + SECTION("broken files") { + if (have_csv_plugin) { + std::vector broken; + add_csv_files("test/data/csv/fails", broken); + add_csv_files("test/data/csv/warns", broken); + broken.emplace_back("test/data/csv/fails/does_not_exist.csv"); - SECTION("lon/lat detection") { - for (auto const &lon_name : {std::string("lon"), std::string("lng")}) { - auto ds = get_csv_ds((boost::format("test/data/csv/%1%_lat.csv") % lon_name).str()); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {lon_name, "lat"}); - require_field_types(fields, {mapnik::Integer, mapnik::Integer}); + for (auto const &path : broken) { + REQUIRE_THROWS(get_csv_ds(path.native())); + } + } + } // END SECTION - CHECK(ds->get_geometry_type() == mapnik::datasource_geometry_t::Point); + SECTION("good files") { + if (have_csv_plugin) { + std::vector good; + add_csv_files("test/data/csv", good); + add_csv_files("test/data/csv/warns", good); - mapnik::query query(ds->envelope()); - for (auto const &field : fields) { - query.add_property_name(field.get_name()); - } - auto features = ds->features(query); - auto feature = features->next(); + for (auto const &path : good) { + auto ds = get_csv_ds(path.native(), false); + // require a non-null pointer returned + REQUIRE(bool(ds)); + } + } + } // END SECTION - require_attributes(feature, { - attr { lon_name, mapnik::value_integer(0) }, - attr { "lat", mapnik::value_integer(0) } - }); - } - } // END SECTION + SECTION("lon/lat detection") { + for (auto const &lon_name : {std::string("lon"), std::string("lng")}) { + auto ds = get_csv_ds((boost::format("test/data/csv/%1%_lat.csv") % lon_name).str()); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {lon_name, "lat"}); + require_field_types(fields, {mapnik::Integer, mapnik::Integer}); - SECTION("type detection") { - auto ds = get_csv_ds("test/data/csv/nypd.csv"); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"Precinct", "Phone", "Address", "City", "geo_longitude", "geo_latitude", "geo_accuracy"}); - require_field_types(fields, {mapnik::String, mapnik::String, mapnik::String, mapnik::String, mapnik::Double, mapnik::Double, mapnik::String}); + CHECK(ds->get_geometry_type() == mapnik::datasource_geometry_t::Point); - CHECK(ds->get_geometry_type() == mapnik::datasource_geometry_t::Point); - CHECK(count_features(all_features(ds)) == 2); + mapnik::query query(ds->envelope()); + for (auto const &field : fields) { + query.add_property_name(field.get_name()); + } + auto features = ds->features(query); + auto feature = features->next(); - auto feature = all_features(ds)->next(); - require_attributes(feature, { - attr { "City", mapnik::value_unicode_string("New York, NY") } - , attr { "geo_accuracy", mapnik::value_unicode_string("house") } - , attr { "Phone", mapnik::value_unicode_string("(212) 334-0711") } - , attr { "Address", mapnik::value_unicode_string("19 Elizabeth Street") } - , attr { "Precinct", mapnik::value_unicode_string("5th Precinct") } - , attr { "geo_longitude", mapnik::value_integer(-70) } - , attr { "geo_latitude", mapnik::value_integer(40) } - }); - } // END SECTION + require_attributes(feature, { + attr { lon_name, mapnik::value_integer(0) }, + attr { "lat", mapnik::value_integer(0) } + }); + } + } // END SECTION - SECTION("skipping blank rows") { - auto ds = get_csv_ds("test/data/csv/blank_rows.csv"); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"x", "y", "name"}); - require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::String}); + SECTION("type detection") { + auto ds = get_csv_ds("test/data/csv/nypd.csv"); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"Precinct", "Phone", "Address", "City", "geo_longitude", "geo_latitude", "geo_accuracy"}); + require_field_types(fields, {mapnik::String, mapnik::String, mapnik::String, mapnik::String, mapnik::Double, mapnik::Double, mapnik::String}); - CHECK(ds->get_geometry_type() == mapnik::datasource_geometry_t::Point); - CHECK(count_features(all_features(ds)) == 2); - } // END SECTION + CHECK(ds->get_geometry_type() == mapnik::datasource_geometry_t::Point); + CHECK(count_features(all_features(ds)) == 2); - SECTION("empty rows") { - auto ds = get_csv_ds("test/data/csv/empty_rows.csv"); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"x", "y", "text", "date", "integer", "boolean", "float", "time", "datetime", "empty_column"}); - require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::String, mapnik::String, mapnik::Integer, mapnik::Boolean, mapnik::Double, mapnik::String, mapnik::String, mapnik::String}); + auto feature = all_features(ds)->next(); + require_attributes(feature, { + attr { "City", mapnik::value_unicode_string("New York, NY") } + , attr { "geo_accuracy", mapnik::value_unicode_string("house") } + , attr { "Phone", mapnik::value_unicode_string("(212) 334-0711") } + , attr { "Address", mapnik::value_unicode_string("19 Elizabeth Street") } + , attr { "Precinct", mapnik::value_unicode_string("5th Precinct") } + , attr { "geo_longitude", mapnik::value_integer(-70) } + , attr { "geo_latitude", mapnik::value_integer(40) } + }); + } // END SECTION - CHECK(ds->get_geometry_type() == mapnik::datasource_geometry_t::Point); - CHECK(count_features(all_features(ds)) == 4); + SECTION("skipping blank rows") { + auto ds = get_csv_ds("test/data/csv/blank_rows.csv"); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"x", "y", "name"}); + require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::String}); - auto featureset = all_features(ds); - auto feature = featureset->next(); - require_attributes(feature, { - attr { "x", mapnik::value_integer(0) } - , attr { "empty_column", mapnik::value_unicode_string("") } - , attr { "text", mapnik::value_unicode_string("a b") } - , attr { "float", mapnik::value_double(1.0) } - , attr { "datetime", mapnik::value_unicode_string("1971-01-01T04:14:00") } - , attr { "y", mapnik::value_integer(0) } - , attr { "boolean", mapnik::value_bool(true) } - , attr { "time", mapnik::value_unicode_string("04:14:00") } - , attr { "date", mapnik::value_unicode_string("1971-01-01") } - , attr { "integer", mapnik::value_integer(40) } - }); + CHECK(ds->get_geometry_type() == mapnik::datasource_geometry_t::Point); + CHECK(count_features(all_features(ds)) == 2); + } // END SECTION - while (bool(feature = featureset->next())) { - CHECK(feature->size() == 10); - CHECK(feature->get("empty_column") == mapnik::value_unicode_string("")); - } - } // END SECTION + SECTION("empty rows") { + auto ds = get_csv_ds("test/data/csv/empty_rows.csv"); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"x", "y", "text", "date", "integer", "boolean", "float", "time", "datetime", "empty_column"}); + require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::String, mapnik::String, mapnik::Integer, mapnik::Boolean, mapnik::Double, mapnik::String, mapnik::String, mapnik::String}); - SECTION("slashes") { - auto ds = get_csv_ds("test/data/csv/has_attributes_with_slashes.csv"); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"x", "y", "name"}); - // NOTE: y column is integer, even though a double value is used below in the test? - require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::String}); - - auto featureset = all_features(ds); - require_attributes(featureset->next(), { - attr{"x", 0} - , attr{"y", 0} - , attr{"name", mapnik::value_unicode_string("a/a") } }); - require_attributes(featureset->next(), { - attr{"x", 1} - , attr{"y", 4} - , attr{"name", mapnik::value_unicode_string("b/b") } }); - require_attributes(featureset->next(), { - attr{"x", 10} - , attr{"y", 2.5} - , attr{"name", mapnik::value_unicode_string("c/c") } }); - } // END SECTION + CHECK(ds->get_geometry_type() == mapnik::datasource_geometry_t::Point); + CHECK(count_features(all_features(ds)) == 4); - SECTION("wkt field") { - using mapnik::geometry::geometry_types; + auto featureset = all_features(ds); + auto feature = featureset->next(); + require_attributes(feature, { + attr { "x", mapnik::value_integer(0) } + , attr { "empty_column", mapnik::value_unicode_string("") } + , attr { "text", mapnik::value_unicode_string("a b") } + , attr { "float", mapnik::value_double(1.0) } + , attr { "datetime", mapnik::value_unicode_string("1971-01-01T04:14:00") } + , attr { "y", mapnik::value_integer(0) } + , attr { "boolean", mapnik::value_bool(true) } + , attr { "time", mapnik::value_unicode_string("04:14:00") } + , attr { "date", mapnik::value_unicode_string("1971-01-01") } + , attr { "integer", mapnik::value_integer(40) } + }); - auto ds = get_csv_ds("test/data/csv/wkt.csv"); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"type"}); - require_field_types(fields, {mapnik::String}); - - auto featureset = all_features(ds); - require_geometry(featureset->next(), 1, geometry_types::Point); - require_geometry(featureset->next(), 1, geometry_types::LineString); - require_geometry(featureset->next(), 1, geometry_types::Polygon); - require_geometry(featureset->next(), 1, geometry_types::Polygon); - require_geometry(featureset->next(), 4, geometry_types::MultiPoint); - require_geometry(featureset->next(), 2, geometry_types::MultiLineString); - require_geometry(featureset->next(), 2, geometry_types::MultiPolygon); - require_geometry(featureset->next(), 2, geometry_types::MultiPolygon); - } // END SECTION + while (bool(feature = featureset->next())) { + CHECK(feature->size() == 10); + CHECK(feature->get("empty_column") == mapnik::value_unicode_string("")); + } + } // END SECTION - SECTION("handling of missing header") { - // TODO: does this mean 'missing_header.csv' should be in the warnings - // subdirectory, since it doesn't work in strict mode? - auto ds = get_csv_ds("test/data/csv/missing_header.csv", false); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"one", "two", "x", "y", "_4", "aftermissing"}); - auto feature = all_features(ds)->next(); - REQUIRE(feature); - REQUIRE(feature->has_key("_4")); - CHECK(feature->get("_4") == mapnik::value_unicode_string("missing")); - } // END SECTION + SECTION("slashes") { + auto ds = get_csv_ds("test/data/csv/has_attributes_with_slashes.csv"); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"x", "y", "name"}); + // NOTE: y column is integer, even though a double value is used below in the test? + require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::String}); + + auto featureset = all_features(ds); + require_attributes(featureset->next(), { + attr{"x", 0} + , attr{"y", 0} + , attr{"name", mapnik::value_unicode_string("a/a") } }); + require_attributes(featureset->next(), { + attr{"x", 1} + , attr{"y", 4} + , attr{"name", mapnik::value_unicode_string("b/b") } }); + require_attributes(featureset->next(), { + attr{"x", 10} + , attr{"y", 2.5} + , attr{"name", mapnik::value_unicode_string("c/c") } }); + } // END SECTION - SECTION("handling of headers that are numbers") { - auto ds = get_csv_ds("test/data/csv/numbers_for_headers.csv"); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"x", "y", "1990", "1991", "1992"}); - auto feature = all_features(ds)->next(); - require_attributes(feature, { - attr{"x", 0} - , attr{"y", 0} - , attr{"1990", 1} - , attr{"1991", 2} - , attr{"1992", 3} - }); - auto expression = mapnik::parse_expression("[1991]=2"); - REQUIRE(bool(expression)); - auto value = mapnik::util::apply_visitor( - mapnik::evaluate( - *feature, mapnik::attributes()), *expression); - CHECK(value == true); - } // END SECTION + SECTION("wkt field") { + using mapnik::geometry::geometry_types; - SECTION("quoted numbers") { - using ustring = mapnik::value_unicode_string; + auto ds = get_csv_ds("test/data/csv/wkt.csv"); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"type"}); + require_field_types(fields, {mapnik::String}); + + auto featureset = all_features(ds); + require_geometry(featureset->next(), 1, geometry_types::Point); + require_geometry(featureset->next(), 1, geometry_types::LineString); + require_geometry(featureset->next(), 1, geometry_types::Polygon); + require_geometry(featureset->next(), 1, geometry_types::Polygon); + require_geometry(featureset->next(), 4, geometry_types::MultiPoint); + require_geometry(featureset->next(), 2, geometry_types::MultiLineString); + require_geometry(featureset->next(), 2, geometry_types::MultiPolygon); + require_geometry(featureset->next(), 2, geometry_types::MultiPolygon); + } // END SECTION - auto ds = get_csv_ds("test/data/csv/quoted_numbers.csv"); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"x", "y", "label"}); - auto featureset = all_features(ds); + SECTION("handling of missing header") { + // TODO: does this mean 'missing_header.csv' should be in the warnings + // subdirectory, since it doesn't work in strict mode? + auto ds = get_csv_ds("test/data/csv/missing_header.csv", false); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"one", "two", "x", "y", "_4", "aftermissing"}); + auto feature = all_features(ds)->next(); + REQUIRE(feature); + REQUIRE(feature->has_key("_4")); + CHECK(feature->get("_4") == mapnik::value_unicode_string("missing")); + } // END SECTION - require_attributes(featureset->next(), { - attr{"x", 0}, attr{"y", 0}, attr{"label", ustring("0,0") } }); - require_attributes(featureset->next(), { - attr{"x", 5}, attr{"y", 5}, attr{"label", ustring("5,5") } }); - require_attributes(featureset->next(), { - attr{"x", 0}, attr{"y", 5}, attr{"label", ustring("0,5") } }); - require_attributes(featureset->next(), { - attr{"x", 5}, attr{"y", 0}, attr{"label", ustring("5,0") } }); - require_attributes(featureset->next(), { - attr{"x", 2.5}, attr{"y", 2.5}, attr{"label", ustring("2.5,2.5") } }); + SECTION("handling of headers that are numbers") { + auto ds = get_csv_ds("test/data/csv/numbers_for_headers.csv"); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"x", "y", "1990", "1991", "1992"}); + auto feature = all_features(ds)->next(); + require_attributes(feature, { + attr{"x", 0} + , attr{"y", 0} + , attr{"1990", 1} + , attr{"1991", 2} + , attr{"1992", 3} + }); + auto expression = mapnik::parse_expression("[1991]=2"); + REQUIRE(bool(expression)); + auto value = mapnik::util::apply_visitor( + mapnik::evaluate( + *feature, mapnik::attributes()), *expression); + CHECK(value == true); + } // END SECTION - } // END SECTION + SECTION("quoted numbers") { + using ustring = mapnik::value_unicode_string; - SECTION("reading newlines") { - for (auto const &platform : {std::string("windows"), std::string("mac")}) { - std::string file_name = (boost::format("test/data/csv/%1%_newlines.csv") % platform).str(); - auto ds = get_csv_ds(file_name); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"x", "y", "z"}); - require_attributes(all_features(ds)->next(), { - attr{"x", 1}, attr{"y", 10}, attr{"z", 9999.9999} }); - } - } // END SECTION + auto ds = get_csv_ds("test/data/csv/quoted_numbers.csv"); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"x", "y", "label"}); + auto featureset = all_features(ds); - SECTION("mixed newlines") { - using ustring = mapnik::value_unicode_string; + require_attributes(featureset->next(), { + attr{"x", 0}, attr{"y", 0}, attr{"label", ustring("0,0") } }); + require_attributes(featureset->next(), { + attr{"x", 5}, attr{"y", 5}, attr{"label", ustring("5,5") } }); + require_attributes(featureset->next(), { + attr{"x", 0}, attr{"y", 5}, attr{"label", ustring("0,5") } }); + require_attributes(featureset->next(), { + attr{"x", 5}, attr{"y", 0}, attr{"label", ustring("5,0") } }); + require_attributes(featureset->next(), { + attr{"x", 2.5}, attr{"y", 2.5}, attr{"label", ustring("2.5,2.5") } }); - for (auto const &file : { - std::string("test/data/csv/mac_newlines_with_unix_inline.csv") - , std::string("test/data/csv/mac_newlines_with_unix_inline_escaped.csv") - , std::string("test/data/csv/windows_newlines_with_unix_inline.csv") - , std::string("test/data/csv/windows_newlines_with_unix_inline_escaped.csv") + } // END SECTION + + SECTION("reading newlines") { + for (auto const &platform : {std::string("windows"), std::string("mac")}) { + std::string file_name = (boost::format("test/data/csv/%1%_newlines.csv") % platform).str(); + auto ds = get_csv_ds(file_name); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"x", "y", "z"}); + require_attributes(all_features(ds)->next(), { + attr{"x", 1}, attr{"y", 10}, attr{"z", 9999.9999} }); + } + } // END SECTION + + SECTION("mixed newlines") { + using ustring = mapnik::value_unicode_string; + + for (auto const &file : { + std::string("test/data/csv/mac_newlines_with_unix_inline.csv") + , std::string("test/data/csv/mac_newlines_with_unix_inline_escaped.csv") + , std::string("test/data/csv/windows_newlines_with_unix_inline.csv") + , std::string("test/data/csv/windows_newlines_with_unix_inline_escaped.csv") + }) { + auto ds = get_csv_ds(file); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"x", "y", "line"}); + require_attributes(all_features(ds)->next(), { + attr{"x", 0}, attr{"y", 0} + , attr{"line", ustring("many\n lines\n of text\n with unix newlines")} }); + } + } // END SECTION + + SECTION("tabs") { + auto ds = get_csv_ds("test/data/csv/tabs_in_csv.csv"); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"x", "y", "z"}); + require_attributes(all_features(ds)->next(), { + attr{"x", -122}, attr{"y", 48}, attr{"z", 0} }); + } // END SECTION + + SECTION("separators") { + using ustring = mapnik::value_unicode_string; + + for (auto const &file : { + std::string("test/data/csv/pipe_delimiters.csv") + , std::string("test/data/csv/semicolon_delimiters.csv") + }) { + auto ds = get_csv_ds(file); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"x", "y", "z"}); + require_attributes(all_features(ds)->next(), { + attr{"x", 0}, attr{"y", 0}, attr{"z", ustring("hello")} }); + } + } // END SECTION + + SECTION("null and bool keywords are empty strings") { + using ustring = mapnik::value_unicode_string; + + auto ds = get_csv_ds("test/data/csv/nulls_and_booleans_as_strings.csv"); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"x", "y", "null", "boolean"}); + require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::String, mapnik::Boolean}); + + auto featureset = all_features(ds); + require_attributes(featureset->next(), { + attr{"x", 0}, attr{"y", 0}, attr{"null", ustring("null")}, attr{"boolean", true}}); + require_attributes(featureset->next(), { + attr{"x", 0}, attr{"y", 0}, attr{"null", ustring("")}, attr{"boolean", false}}); + } // END SECTION + + SECTION("nonexistent query fields throw") { + auto ds = get_csv_ds("test/data/csv/lon_lat.csv"); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"lon", "lat"}); + require_field_types(fields, {mapnik::Integer, mapnik::Integer}); + + mapnik::query query(ds->envelope()); + for (auto const &field : fields) { + query.add_property_name(field.get_name()); + } + // also add an invalid one, triggering throw + query.add_property_name("bogus"); + + REQUIRE_THROWS(ds->features(query)); + } // END SECTION + + SECTION("leading zeros mean strings") { + using ustring = mapnik::value_unicode_string; + + auto ds = get_csv_ds("test/data/csv/leading_zeros.csv"); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"x", "y", "fips"}); + require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::String}); + + auto featureset = all_features(ds); + require_attributes(featureset->next(), { + attr{"x", 0}, attr{"y", 0}, attr{"fips", ustring("001")}}); + require_attributes(featureset->next(), { + attr{"x", 0}, attr{"y", 0}, attr{"fips", ustring("003")}}); + require_attributes(featureset->next(), { + attr{"x", 0}, attr{"y", 0}, attr{"fips", ustring("005")}}); + } // END SECTION + + SECTION("advanced geometry detection") { + using row = std::pair; + + for (row r : { + row{"point", mapnik::datasource_geometry_t::Point} + , row{"poly", mapnik::datasource_geometry_t::Polygon} + , row{"multi_poly", mapnik::datasource_geometry_t::Polygon} + , row{"line", mapnik::datasource_geometry_t::LineString} }) { - auto ds = get_csv_ds(file); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"x", "y", "line"}); - require_attributes(all_features(ds)->next(), { - attr{"x", 0}, attr{"y", 0} - , attr{"line", ustring("many\n lines\n of text\n with unix newlines")} }); - } - } // END SECTION + std::string file_name = (boost::format("test/data/csv/%1%_wkt.csv") % r.first).str(); + auto ds = get_csv_ds(file_name); + CHECK(ds->get_geometry_type() == r.second); + } + } // END SECTION - SECTION("tabs") { - auto ds = get_csv_ds("test/data/csv/tabs_in_csv.csv"); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"x", "y", "z"}); - require_attributes(all_features(ds)->next(), { - attr{"x", -122}, attr{"y", 48}, attr{"z", 0} }); - } // END SECTION + SECTION("creation of CSV from in-memory strings") { + using ustring = mapnik::value_unicode_string; - SECTION("separators") { - using ustring = mapnik::value_unicode_string; + for (auto const &name : {std::string("Winthrop, WA"), std::string(u8"Qu\u00e9bec")}) { + std::string csv_string = + (boost::format( + "wkt,Name\n" + "\"POINT (120.15 48.47)\",\"%1%\"\n" + ) % name).str(); - for (auto const &file : { - std::string("test/data/csv/pipe_delimiters.csv") - , std::string("test/data/csv/semicolon_delimiters.csv") - }) { - auto ds = get_csv_ds(file); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"x", "y", "z"}); - require_attributes(all_features(ds)->next(), { - attr{"x", 0}, attr{"y", 0}, attr{"z", ustring("hello")} }); - } - } // END SECTION + mapnik::parameters params; + params["type"] = std::string("csv"); + params["inline"] = csv_string; + auto ds = mapnik::datasource_cache::instance().create(params); + REQUIRE(bool(ds)); - SECTION("null and bool keywords are empty strings") { - using ustring = mapnik::value_unicode_string; + auto feature = all_features(ds)->next(); + REQUIRE(bool(feature)); + REQUIRE(feature->has_key("Name")); + CHECK(feature->get("Name") == ustring(name.c_str())); + } + } // END SECTION - auto ds = get_csv_ds("test/data/csv/nulls_and_booleans_as_strings.csv"); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"x", "y", "null", "boolean"}); - require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::String, mapnik::Boolean}); + SECTION("geojson quoting") { + using mapnik::geometry::geometry_types; - auto featureset = all_features(ds); - require_attributes(featureset->next(), { - attr{"x", 0}, attr{"y", 0}, attr{"null", ustring("null")}, attr{"boolean", true}}); - require_attributes(featureset->next(), { - attr{"x", 0}, attr{"y", 0}, attr{"null", ustring("")}, attr{"boolean", false}}); - } // END SECTION + for (auto const &file : { + std::string("test/data/csv/geojson_double_quote_escape.csv") + , std::string("test/data/csv/geojson_single_quote.csv") + , std::string("test/data/csv/geojson_2x_double_quote_filebakery_style.csv") + }) { + auto ds = get_csv_ds(file); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"type"}); + require_field_types(fields, {mapnik::String}); + + auto featureset = all_features(ds); + require_geometry(featureset->next(), 1, geometry_types::Point); + require_geometry(featureset->next(), 1, geometry_types::LineString); + require_geometry(featureset->next(), 1, geometry_types::Polygon); + require_geometry(featureset->next(), 1, geometry_types::Polygon); + require_geometry(featureset->next(), 4, geometry_types::MultiPoint); + require_geometry(featureset->next(), 2, geometry_types::MultiLineString); + require_geometry(featureset->next(), 2, geometry_types::MultiPolygon); + require_geometry(featureset->next(), 2, geometry_types::MultiPolygon); + } + } // END SECTION - SECTION("nonexistent query fields throw") { - auto ds = get_csv_ds("test/data/csv/lon_lat.csv"); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"lon", "lat"}); - require_field_types(fields, {mapnik::Integer, mapnik::Integer}); + SECTION("blank undelimited rows are still parsed") { + using ustring = mapnik::value_unicode_string; - mapnik::query query(ds->envelope()); - for (auto const &field : fields) { - query.add_property_name(field.get_name()); - } - // also add an invalid one, triggering throw - query.add_property_name("bogus"); + // TODO: does this mean this CSV file should be in the warnings + // subdirectory, since it doesn't work in strict mode? + auto ds = get_csv_ds("test/data/csv/more_headers_than_column_values.csv", false); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"x", "y", "one", "two", "three"}); + require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::String, mapnik::String, mapnik::String}); - REQUIRE_THROWS(ds->features(query)); - } // END SECTION + require_attributes(all_features(ds)->next(), { + attr{"x", 0}, attr{"y", 0}, attr{"one", ustring("")}, attr{"two", ustring("")}, attr{"three", ustring("")} }); + } // END SECTION - SECTION("leading zeros mean strings") { - using ustring = mapnik::value_unicode_string; + SECTION("fewer headers than rows throws") { + REQUIRE_THROWS(get_csv_ds("test/data/csv/more_column_values_than_headers.csv")); + } // END SECTION - auto ds = get_csv_ds("test/data/csv/leading_zeros.csv"); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"x", "y", "fips"}); - require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::String}); + SECTION("feature ID only incremented for valid rows") { + auto ds = get_csv_ds("test/data/csv/warns/feature_id_counting.csv", false); + auto fs = all_features(ds); - auto featureset = all_features(ds); - require_attributes(featureset->next(), { - attr{"x", 0}, attr{"y", 0}, attr{"fips", ustring("001")}}); - require_attributes(featureset->next(), { - attr{"x", 0}, attr{"y", 0}, attr{"fips", ustring("003")}}); - require_attributes(featureset->next(), { - attr{"x", 0}, attr{"y", 0}, attr{"fips", ustring("005")}}); - } // END SECTION + // first + auto feature = fs->next(); + REQUIRE(bool(feature)); + CHECK(feature->id() == 1); - SECTION("advanced geometry detection") { - using row = std::pair; + // second, should have skipped bogus one + feature = fs->next(); + REQUIRE(bool(feature)); + CHECK(feature->id() == 2); - for (row r : { - row{"point", mapnik::datasource_geometry_t::Point} - , row{"poly", mapnik::datasource_geometry_t::Polygon} - , row{"multi_poly", mapnik::datasource_geometry_t::Polygon} - , row{"line", mapnik::datasource_geometry_t::LineString} - }) { - std::string file_name = (boost::format("test/data/csv/%1%_wkt.csv") % r.first).str(); - auto ds = get_csv_ds(file_name); - CHECK(ds->get_geometry_type() == r.second); - } - } // END SECTION + feature = fs->next(); + CHECK(!feature); + } // END SECTION - SECTION("creation of CSV from in-memory strings") { - using ustring = mapnik::value_unicode_string; + SECTION("dynamically defining headers") { + using ustring = mapnik::value_unicode_string; + using row = std::pair; - for (auto const &name : {std::string("Winthrop, WA"), std::string(u8"Qu\u00e9bec")}) { - std::string csv_string = - (boost::format( - "wkt,Name\n" - "\"POINT (120.15 48.47)\",\"%1%\"\n" - ) % name).str(); + for (auto const &r : { + row{"test/data/csv/fails/needs_headers_two_lines.csv", 2} + , row{"test/data/csv/fails/needs_headers_one_line.csv", 1} + , row{"test/data/csv/fails/needs_headers_one_line_no_newline.csv", 1} + }) { + mapnik::parameters params; + params["type"] = std::string("csv"); + params["file"] = r.first; + params["headers"] = "x,y,name"; + auto ds = mapnik::datasource_cache::instance().create(params); + REQUIRE(bool(ds)); - mapnik::parameters params; - params["type"] = std::string("csv"); - params["inline"] = csv_string; - auto ds = mapnik::datasource_cache::instance().create(params); - REQUIRE(bool(ds)); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"x", "y", "name"}); + require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::String}); + require_attributes(all_features(ds)->next(), { + attr{"x", 0}, attr{"y", 0}, attr{"name", ustring("data_name")} }); + REQUIRE(count_features(all_features(ds)) == r.second); + } + } // END SECTION - auto feature = all_features(ds)->next(); - REQUIRE(bool(feature)); - REQUIRE(feature->has_key("Name")); - CHECK(feature->get("Name") == ustring(name.c_str())); - } - } // END SECTION + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wlong-long" + SECTION("64bit int fields work") { + auto ds = get_csv_ds("test/data/csv/64bit_int.csv"); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"x", "y", "bigint"}); + require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::Integer}); - SECTION("geojson quoting") { - using mapnik::geometry::geometry_types; + auto fs = all_features(ds); + auto feature = fs->next(); + require_attributes(feature, { + attr{"x", 0}, attr{"y", 0}, attr{"bigint", 2147483648} }); - for (auto const &file : { - std::string("test/data/csv/geojson_double_quote_escape.csv") - , std::string("test/data/csv/geojson_single_quote.csv") - , std::string("test/data/csv/geojson_2x_double_quote_filebakery_style.csv") - }) { - auto ds = get_csv_ds(file); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"type"}); - require_field_types(fields, {mapnik::String}); - - auto featureset = all_features(ds); - require_geometry(featureset->next(), 1, geometry_types::Point); - require_geometry(featureset->next(), 1, geometry_types::LineString); - require_geometry(featureset->next(), 1, geometry_types::Polygon); - require_geometry(featureset->next(), 1, geometry_types::Polygon); - require_geometry(featureset->next(), 4, geometry_types::MultiPoint); - require_geometry(featureset->next(), 2, geometry_types::MultiLineString); - require_geometry(featureset->next(), 2, geometry_types::MultiPolygon); - require_geometry(featureset->next(), 2, geometry_types::MultiPolygon); - } - } // END SECTION + feature = fs->next(); + require_attributes(feature, { + attr{"x", 0}, attr{"y", 0}, attr{"bigint", 9223372036854775807ll} }); + require_attributes(feature, { + attr{"x", 0}, attr{"y", 0}, attr{"bigint", 0x7FFFFFFFFFFFFFFFll} }); + } // END SECTION + #pragma GCC diagnostic pop - SECTION("blank undelimited rows are still parsed") { - using ustring = mapnik::value_unicode_string; + SECTION("various number types") { + auto ds = get_csv_ds("test/data/csv/number_types.csv"); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {"x", "y", "floats"}); + require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::Double}); - // TODO: does this mean this CSV file should be in the warnings - // subdirectory, since it doesn't work in strict mode? - auto ds = get_csv_ds("test/data/csv/more_headers_than_column_values.csv", false); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"x", "y", "one", "two", "three"}); - require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::String, mapnik::String, mapnik::String}); + auto fs = all_features(ds); + for (double d : { .0, +.0, 1e-06, -1e-06, 0.000001, 1.234e+16, 1.234e+16 }) { + auto feature = fs->next(); + REQUIRE(bool(feature)); + CHECK(feature->get("floats").get() == Approx(d)); + } + } // END SECTION - require_attributes(all_features(ds)->next(), { - attr{"x", 0}, attr{"y", 0}, attr{"one", ustring("")}, attr{"two", ustring("")}, attr{"three", ustring("")} }); - } // END SECTION + SECTION("manually supplied extent") { + std::string csv_string("wkt,Name\n"); + mapnik::parameters params; + params["type"] = std::string("csv"); + params["inline"] = csv_string; + params["extent"] = "-180,-90,180,90"; + auto ds = mapnik::datasource_cache::instance().create(params); + REQUIRE(bool(ds)); - SECTION("fewer headers than rows throws") { - REQUIRE_THROWS(get_csv_ds("test/data/csv/more_column_values_than_headers.csv")); - } // END SECTION + auto box = ds->envelope(); + CHECK(box.minx() == -180); + CHECK(box.miny() == -90); + CHECK(box.maxx() == 180); + CHECK(box.maxy() == 90); + } // END SECTION - SECTION("feature ID only incremented for valid rows") { - auto ds = get_csv_ds("test/data/csv/warns/feature_id_counting.csv", false); - auto fs = all_features(ds); + SECTION("inline geojson") { + std::string csv_string = "geojson\n'{\"coordinates\":[-92.22568,38.59553],\"type\":\"Point\"}'"; + mapnik::parameters params; + params["type"] = std::string("csv"); + params["inline"] = csv_string; + auto ds = mapnik::datasource_cache::instance().create(params); + REQUIRE(bool(ds)); - // first - auto feature = fs->next(); - REQUIRE(bool(feature)); - CHECK(feature->id() == 1); + auto fields = ds->get_descriptor().get_descriptors(); + require_field_names(fields, {}); - // second, should have skipped bogus one - feature = fs->next(); - REQUIRE(bool(feature)); - CHECK(feature->id() == 2); + // TODO: this originally had the following comment: + // - re-enable after https://github.com/mapnik/mapnik/issues/2319 is fixed + // but that seems to have been merged and tested separately? + auto fs = all_features(ds); + auto feat = fs->next(); + CHECK(feature_count(feat->get_geometry()) == 1); + } // END SECTION - feature = fs->next(); - CHECK(!feature); - } // END SECTION - - SECTION("dynamically defining headers") { - using ustring = mapnik::value_unicode_string; - using row = std::pair; - - for (auto const &r : { - row{"test/data/csv/fails/needs_headers_two_lines.csv", 2} - , row{"test/data/csv/fails/needs_headers_one_line.csv", 1} - , row{"test/data/csv/fails/needs_headers_one_line_no_newline.csv", 1} - }) { - mapnik::parameters params; - params["type"] = std::string("csv"); - params["file"] = r.first; - params["headers"] = "x,y,name"; - auto ds = mapnik::datasource_cache::instance().create(params); - REQUIRE(bool(ds)); - - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"x", "y", "name"}); - require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::String}); - require_attributes(all_features(ds)->next(), { - attr{"x", 0}, attr{"y", 0}, attr{"name", ustring("data_name")} }); - REQUIRE(count_features(all_features(ds)) == r.second); - } - } // END SECTION - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wlong-long" - SECTION("64bit int fields work") { - auto ds = get_csv_ds("test/data/csv/64bit_int.csv"); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"x", "y", "bigint"}); - require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::Integer}); - - auto fs = all_features(ds); - auto feature = fs->next(); - require_attributes(feature, { - attr{"x", 0}, attr{"y", 0}, attr{"bigint", 2147483648} }); - - feature = fs->next(); - require_attributes(feature, { - attr{"x", 0}, attr{"y", 0}, attr{"bigint", 9223372036854775807ll} }); - require_attributes(feature, { - attr{"x", 0}, attr{"y", 0}, attr{"bigint", 0x7FFFFFFFFFFFFFFFll} }); - } // END SECTION -#pragma GCC diagnostic pop - - SECTION("various number types") { - auto ds = get_csv_ds("test/data/csv/number_types.csv"); - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {"x", "y", "floats"}); - require_field_types(fields, {mapnik::Integer, mapnik::Integer, mapnik::Double}); - - auto fs = all_features(ds); - for (double d : { .0, +.0, 1e-06, -1e-06, 0.000001, 1.234e+16, 1.234e+16 }) { - auto feature = fs->next(); - REQUIRE(bool(feature)); - CHECK(feature->get("floats").get() == Approx(d)); - } - } // END SECTION - - SECTION("manually supplied extent") { - std::string csv_string("wkt,Name\n"); - mapnik::parameters params; - params["type"] = std::string("csv"); - params["inline"] = csv_string; - params["extent"] = "-180,-90,180,90"; - auto ds = mapnik::datasource_cache::instance().create(params); - REQUIRE(bool(ds)); - - auto box = ds->envelope(); - CHECK(box.minx() == -180); - CHECK(box.miny() == -90); - CHECK(box.maxx() == 180); - CHECK(box.maxy() == 90); - } // END SECTION - - SECTION("inline geojson") { - std::string csv_string = "geojson\n'{\"coordinates\":[-92.22568,38.59553],\"type\":\"Point\"}'"; - mapnik::parameters params; - params["type"] = std::string("csv"); - params["inline"] = csv_string; - auto ds = mapnik::datasource_cache::instance().create(params); - REQUIRE(bool(ds)); - - auto fields = ds->get_descriptor().get_descriptors(); - require_field_names(fields, {}); - - // TODO: this originally had the following comment: - // - re-enable after https://github.com/mapnik/mapnik/issues/2319 is fixed - // but that seems to have been merged and tested separately? - auto fs = all_features(ds); - auto feat = fs->next(); - CHECK(feature_count(feat->get_geometry()) == 1); - } // END SECTION - - mapnik::logger::instance().set_severity(severity); + mapnik::logger::instance().set_severity(severity); + } } // END TEST CASE diff --git a/test/unit/core/copy_move_test.cpp b/test/unit/core/copy_move_test.cpp index 3b38ae76c..690537920 100644 --- a/test/unit/core/copy_move_test.cpp +++ b/test/unit/core/copy_move_test.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -19,55 +20,59 @@ SECTION("layers") { { mapnik::Map m0(100,100); mapnik::Map m2(200,100); - mapnik::datasource_cache::instance().register_datasources("plugins/input/shape.input"); - mapnik::parameters p; - p["type"]="shape"; - p["file"]="demo/data/boundaries"; - p["encoding"]="latin1"; - auto ds0 = mapnik::datasource_cache::instance().create(p); + std::string shape_plugin("./plugins/input/shape.input"); + if (mapnik::util::exists(shape_plugin)) + { + mapnik::datasource_cache::instance().register_datasources("plugins/input/shape.input"); + mapnik::parameters p; + p["type"]="shape"; + p["file"]="demo/data/boundaries"; + p["encoding"]="latin1"; + auto ds0 = mapnik::datasource_cache::instance().create(p); - auto ds1 = ds0; // shared ptr copy - REQUIRE( (ds1 == ds0) ); - REQUIRE( !(*ds1 != *ds0) ); - REQUIRE( (ds1.get() == ds0.get()) ); - ds1 = mapnik::datasource_cache::instance().create(p); // new with the same parameters - REQUIRE( (ds1 != ds0) ); - REQUIRE( (*ds1 == *ds0) ); - auto ds2 = std::move(ds1); - REQUIRE( (ds2 != ds0) ); - REQUIRE( (*ds2 == *ds0) ); + auto ds1 = ds0; // shared ptr copy + REQUIRE( (ds1 == ds0) ); + REQUIRE( !(*ds1 != *ds0) ); + REQUIRE( (ds1.get() == ds0.get()) ); + ds1 = mapnik::datasource_cache::instance().create(p); // new with the same parameters + REQUIRE( (ds1 != ds0) ); + REQUIRE( (*ds1 == *ds0) ); + auto ds2 = std::move(ds1); + REQUIRE( (ds2 != ds0) ); + REQUIRE( (*ds2 == *ds0) ); - // mapnik::layer - mapnik::layer l0("test-layer"); - l0.set_datasource(ds0); + // mapnik::layer + mapnik::layer l0("test-layer"); + l0.set_datasource(ds0); - mapnik::layer l1 = l0; // copy assignment - REQUIRE( (l1 == l0) ); - mapnik::layer l2(l0); // copy ctor - REQUIRE( (l2 == l0) ); - mapnik::layer l3(mapnik::layer("test-layer")); // move ctor - l3.set_datasource(ds2); + mapnik::layer l1 = l0; // copy assignment + REQUIRE( (l1 == l0) ); + mapnik::layer l2(l0); // copy ctor + REQUIRE( (l2 == l0) ); + mapnik::layer l3(mapnik::layer("test-layer")); // move ctor + l3.set_datasource(ds2); - REQUIRE( (l3 == l0) ); - mapnik::layer l4 = std::move(l3); - REQUIRE( (l4 == l0) ); // move assignment + REQUIRE( (l3 == l0) ); + mapnik::layer l4 = std::move(l3); + REQUIRE( (l4 == l0) ); // move assignment - m0.add_layer(l4); - m0.set_background(mapnik::color("skyblue")); - m2.set_background(mapnik::color("skyblue")); + m0.add_layer(l4); + m0.set_background(mapnik::color("skyblue")); + m2.set_background(mapnik::color("skyblue")); - auto m1 = m0; //copy + auto m1 = m0; //copy - REQUIRE( (m0 == m1) ); - REQUIRE( (m0 != m2) ); + REQUIRE( (m0 == m1) ); + REQUIRE( (m0 != m2) ); - m2 = m1; // copy - REQUIRE( (m2 == m1) ); - m2 = std::move(m1); - REQUIRE( (m2 == m0) ); - REQUIRE( (m1 != m0) ); + m2 = m1; // copy + REQUIRE( (m2 == m1) ); + m2 = std::move(m1); + REQUIRE( (m2 == m0) ); + REQUIRE( (m1 != m0) ); - REQUIRE( (m0 == m2) ); + REQUIRE( (m0 == m2) ); + } } catch (std::exception const & ex) { diff --git a/test/unit/imaging/image_painted_test.cpp b/test/unit/imaging/image_painted_test.cpp index b567d5e6a..a1fbbc979 100644 --- a/test/unit/imaging/image_painted_test.cpp +++ b/test/unit/imaging/image_painted_test.cpp @@ -8,6 +8,7 @@ #include #include #include +#include TEST_CASE("image") { @@ -17,47 +18,51 @@ SECTION("painting") { try { - datasource_cache::instance().register_datasources("plugins/input/csv.input"); - - Map m(256, 256); - - feature_type_style lines_style; + std::string csv_plugin("./plugins/input/csv.input"); + if (mapnik::util::exists(csv_plugin)) { - rule r; - line_symbolizer line_sym; - r.append(std::move(line_sym)); - lines_style.add_rule(std::move(r)); + datasource_cache::instance().register_datasources(csv_plugin); + + Map m(256, 256); + + feature_type_style lines_style; + { + rule r; + line_symbolizer line_sym; + r.append(std::move(line_sym)); + lines_style.add_rule(std::move(r)); + } + m.insert_style("lines", std::move(lines_style)); + + feature_type_style markers_style; + { + rule r; + r.set_filter(parse_expression("False")); + markers_symbolizer mark_sym; + r.append(std::move(mark_sym)); + markers_style.add_rule(std::move(r)); + } + m.insert_style("markers", std::move(markers_style)); + + parameters p; + p["type"] = "csv"; + p["separator"] = "|"; + p["inline"] = "wkt\nLINESTRING(-10 0, 0 20, 10 0, 15 5)"; + + layer lyr("layer"); + lyr.set_datasource(datasource_cache::instance().create(p)); + lyr.add_style("lines"); + lyr.add_style("markers"); + m.add_layer(lyr); + + m.zoom_all(); + + image_rgba8 image(m.width(), m.height()); + agg_renderer ren(m, image); + ren.apply(); + + REQUIRE(image.painted() == true); } - m.insert_style("lines", std::move(lines_style)); - - feature_type_style markers_style; - { - rule r; - r.set_filter(parse_expression("False")); - markers_symbolizer mark_sym; - r.append(std::move(mark_sym)); - markers_style.add_rule(std::move(r)); - } - m.insert_style("markers", std::move(markers_style)); - - parameters p; - p["type"] = "csv"; - p["separator"] = "|"; - p["inline"] = "wkt\nLINESTRING(-10 0, 0 20, 10 0, 15 5)"; - - layer lyr("layer"); - lyr.set_datasource(datasource_cache::instance().create(p)); - lyr.add_style("lines"); - lyr.add_style("markers"); - m.add_layer(lyr); - - m.zoom_all(); - - image_rgba8 image(m.width(), m.height()); - agg_renderer ren(m, image); - ren.apply(); - - REQUIRE(image.painted() == true); } catch (std::exception const & ex) { From 01dbf53a62154bf5d096ec0fcedd66db5719ba69 Mon Sep 17 00:00:00 2001 From: Blake Thompson Date: Tue, 30 Jun 2015 19:30:11 -0500 Subject: [PATCH 012/165] setting up for mapnik v3.0.0 release --- include/mapnik/version.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/mapnik/version.hpp b/include/mapnik/version.hpp index 6ed43f574..47a200e8f 100644 --- a/include/mapnik/version.hpp +++ b/include/mapnik/version.hpp @@ -23,7 +23,7 @@ #ifndef MAPNIK_VERSION_HPP #define MAPNIK_VERSION_HPP -#define MAPNIK_VERSION_IS_RELEASE 0 +#define MAPNIK_VERSION_IS_RELEASE 1 #define MAPNIK_MAJOR_VERSION 3 #define MAPNIK_MINOR_VERSION 0 From 9a54d720cdc59ea6203e9f128bbdf7634f756221 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 30 Jun 2015 17:52:08 -0700 Subject: [PATCH 013/165] add symbol for save_to_string - closes #2938 - refs #2939 --- src/image_util.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/image_util.cpp b/src/image_util.cpp index 63d544088..7f86fdb3c 100644 --- a/src/image_util.cpp +++ b/src/image_util.cpp @@ -368,6 +368,13 @@ template MAPNIK_DECL void save_to_file (image_view_any const&, std::string const&, rgba_palette const& palette); +template MAPNIK_DECL std::string save_to_string (image_view_rgba8 const&, + std::string const&); + +template MAPNIK_DECL std::string save_to_string (image_view_rgba8 const&, + std::string const&, + rgba_palette const& palette); + template MAPNIK_DECL std::string save_to_string (image_view_any const&, std::string const&); From c7693d9765a277b5c37a5562653cfc79c4f2cc52 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 30 Jun 2015 18:47:36 -0700 Subject: [PATCH 014/165] fix odd inf loop in catch.hpp --- test/standalone/csv_test.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/standalone/csv_test.cpp b/test/standalone/csv_test.cpp index 9405e6615..2023f67f4 100644 --- a/test/standalone/csv_test.cpp +++ b/test/standalone/csv_test.cpp @@ -157,14 +157,15 @@ void require_geometry(mapnik::feature_ptr feature, } } // anonymous namespace +static const std::string csv_plugin("./plugins/input/csv.input"); + +const bool registered = mapnik::datasource_cache::instance().register_datasources(csv_plugin); + TEST_CASE("csv") { - std::string csv_plugin("./plugins/input/csv.input"); if (mapnik::util::exists(csv_plugin)) { - const bool registered = mapnik::datasource_cache::instance().register_datasources(csv_plugin); - REQUIRE(registered); // make the tests silent since we intentially test error conditions that are noisy From 770d93b7cc1178d705a23997a087e884fc74374b Mon Sep 17 00:00:00 2001 From: Blake Thompson Date: Tue, 30 Jun 2015 22:23:44 -0500 Subject: [PATCH 015/165] Fix situation where offset_converter might start off with an SEG_END from the vertex, therefore starting invalid processing. Tests added to confirm fix. Ref #2937 --- include/mapnik/offset_converter.hpp | 5 ++ test/unit/vertex_adapter/offset_converter.cpp | 75 ++++++++++++++++++- 2 files changed, 77 insertions(+), 3 deletions(-) diff --git a/include/mapnik/offset_converter.hpp b/include/mapnik/offset_converter.hpp index b1f2476c8..2badce8b5 100644 --- a/include/mapnik/offset_converter.hpp +++ b/include/mapnik/offset_converter.hpp @@ -310,6 +310,11 @@ private: v1.cmd = v0.cmd; // PUSH INITIAL points.push_back(vertex2d(v0.x, v0.y, v0.cmd)); + if (v0.cmd == SEG_END) // not enough vertices in source + { + return status_ = process; + } + while ((v0.cmd = geom_.vertex(&v0.x, &v0.y)) != SEG_END) { points.push_back(vertex2d(v0.x, v0.y, v0.cmd)); diff --git a/test/unit/vertex_adapter/offset_converter.cpp b/test/unit/vertex_adapter/offset_converter.cpp index 38c91b5dc..b4b7d73c7 100644 --- a/test/unit/vertex_adapter/offset_converter.cpp +++ b/test/unit/vertex_adapter/offset_converter.cpp @@ -27,14 +27,22 @@ struct fake_path : fake_path(l.begin(), l.size()) { } - fake_path(std::vector const &v) - : fake_path(v.begin(), v.size()) { + fake_path(std::vector const &v, bool make_invalid = false) + : fake_path(v.begin(), v.size(), make_invalid) { } template - fake_path(Itr itr, size_t sz) { + fake_path(Itr itr, size_t sz, bool make_invalid = false) { size_t num_coords = sz >> 1; + if (make_invalid) + { + num_coords++; + } vertices_.reserve(num_coords); + if (make_invalid) + { + vertices_.push_back(std::make_tuple(0,0,mapnik::SEG_END)); + } for (size_t i = 0; i < num_coords; ++i) { double x = *itr++; @@ -70,6 +78,32 @@ double dist(double x0, double y0, double x1, double y1) return std::sqrt(dx*dx + dy*dy); } +void test_null_segment(double const &offset) +{ + fake_path path = {}; + mapnik::offset_converter off_path_new(path); + off_path_new.set_offset(offset); + double x0 = 0; + double y0 = 0; + REQUIRE(off_path_new.vertex(&x0, &y0) == mapnik::SEG_END); + REQUIRE(off_path_new.vertex(&x0, &y0) == mapnik::SEG_END); + REQUIRE(off_path_new.vertex(&x0, &y0) == mapnik::SEG_END); +} + +void test_invalid_segment(double const &offset) +{ + std::vector v_path = {1, 1, 1, 2}; + fake_path path(v_path, true); + mapnik::offset_converter off_path_new(path); + off_path_new.set_offset(offset); + double x0 = 0; + double y0 = 0; + REQUIRE(off_path_new.vertex(&x0, &y0) == mapnik::SEG_END); + REQUIRE(off_path_new.vertex(&x0, &y0) == mapnik::SEG_END); + REQUIRE(off_path_new.vertex(&x0, &y0) == mapnik::SEG_END); +} + + void test_simple_segment(double const &offset) { fake_path path = {0, 0, 1, 0}, off_path = {0, offset, 1, offset}; @@ -263,6 +297,41 @@ void test_s_shaped_curve(double const &offset) { TEST_CASE("offset converter") { +SECTION("null segment") { + try { + + std::vector offsets = { 1, -1 }; + for (double offset : offsets) { + // test simple straight line segment - should be easy to + // find the correspondance here. + offset_test::test_null_segment(offset); + } + } + catch (std::exception const& ex) + { + std::cerr << ex.what() << "\n"; + REQUIRE(false); + } +} + +SECTION("invalid segment") { + try { + + std::vector offsets = { 1, -1 }; + for (double offset : offsets) { + // test simple straight line segment - should be easy to + // find the correspondance here. + offset_test::test_invalid_segment(offset); + } + } + catch (std::exception const& ex) + { + std::cerr << ex.what() << "\n"; + REQUIRE(false); + } +} + + SECTION("simple segment") { try { From 9704e3eec5b8d01832c29293db76f68bb3a5ad4c Mon Sep 17 00:00:00 2001 From: Jiri Drbalek Date: Wed, 1 Jul 2015 12:12:21 +0000 Subject: [PATCH 016/165] fix integer overflow (-fsanitize=integer) --- test/visual/renderer.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/visual/renderer.hpp b/test/visual/renderer.hpp index f097dac2b..e66343ef9 100644 --- a/test/visual/renderer.hpp +++ b/test/visual/renderer.hpp @@ -125,7 +125,7 @@ struct svg_renderer : renderer_base } std::string expected(std::istreambuf_iterator(stream.rdbuf()),(std::istreambuf_iterator())); stream.close(); - return std::fabs(actual.size() - expected.size()); + return std::max(actual.size(), expected.size()) - std::min(actual.size(), expected.size()); } void save(image_type const & image, boost::filesystem::path const& path) const From a42e93a2598071947d8339640c64fc49b2c34963 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 1 Jul 2015 09:58:37 -0700 Subject: [PATCH 017/165] remove unneeded assert - refs #2943 --- include/mapnik/value_hash.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/include/mapnik/value_hash.hpp b/include/mapnik/value_hash.hpp index e0aec264e..da2621269 100644 --- a/include/mapnik/value_hash.hpp +++ b/include/mapnik/value_hash.hpp @@ -52,7 +52,6 @@ struct value_hasher std::size_t operator() (value_unicode_string const& val) const { - assert(val.hashCode() > 0); return static_cast(val.hashCode()); } From 66a67dff5ccf207c5667c418cff59821109dae7f Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 1 Jul 2015 10:00:00 -0700 Subject: [PATCH 018/165] remove unused header --- include/mapnik/value_hash.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/include/mapnik/value_hash.hpp b/include/mapnik/value_hash.hpp index da2621269..4c12bbe97 100644 --- a/include/mapnik/value_hash.hpp +++ b/include/mapnik/value_hash.hpp @@ -29,7 +29,6 @@ // stl #include -#include // icu #include From 4f6c286f9232e28ee9b9a56bd270f26997c33910 Mon Sep 17 00:00:00 2001 From: artemp Date: Thu, 2 Jul 2015 10:14:14 +0200 Subject: [PATCH 019/165] style --- src/text/renderer.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/text/renderer.cpp b/src/text/renderer.cpp index 05a460631..227c35dbe 100644 --- a/src/text/renderer.cpp +++ b/src/text/renderer.cpp @@ -92,13 +92,12 @@ void text_renderer::prepare_glyphs(glyph_positions const& positions) template void composite_bitmap(T & pixmap, FT_Bitmap *bitmap, unsigned rgba, int x, int y, double opacity, composite_mode_e comp_op) { - int x_max=x+bitmap->width; - int y_max=y+bitmap->rows; - int i,p,j,q; + int x_max = x + bitmap->width; + int y_max = y + bitmap->rows; - for (i=x,p=0;ibuffer[q*bitmap->width+p]; if (gray) From 9cb43bf8e7ffad03c360eb2b546c2ccae4db426a Mon Sep 17 00:00:00 2001 From: Jiri Drbalek Date: Thu, 2 Jul 2015 15:10:27 +0000 Subject: [PATCH 020/165] benchmark: remove duplicate code --- benchmark/compare_images.hpp | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/benchmark/compare_images.hpp b/benchmark/compare_images.hpp index 8fc6b1986..fb0e4ac95 100644 --- a/benchmark/compare_images.hpp +++ b/benchmark/compare_images.hpp @@ -29,19 +29,7 @@ namespace benchmark { image_rgba8 const& dest = util::get(desc_any); image_rgba8 const& src = util::get(src_any); - unsigned int width = src.width(); - unsigned int height = src.height(); - if ((width != dest.width()) || height != dest.height()) return false; - for (unsigned int y = 0; y < height; ++y) - { - const unsigned int* row_from = src.get_row(y); - const unsigned int* row_to = dest.get_row(y); - for (unsigned int x = 0; x < width; ++x) - { - if (row_from[x] != row_to[x]) return false; - } - } - return true; + return compare(dest, src, 0, true) == 0; } } From aff4fa5da6c30d76d1cdf3ba14c65aa8b9eb647f Mon Sep 17 00:00:00 2001 From: Jiri Drbalek Date: Mon, 15 Jun 2015 12:34:20 +0000 Subject: [PATCH 021/165] visual tests: prefer size_t --- test/visual/config.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/visual/config.hpp b/test/visual/config.hpp index 8353d4b2e..5f4c77192 100644 --- a/test/visual/config.hpp +++ b/test/visual/config.hpp @@ -35,10 +35,10 @@ namespace visual_tests struct map_size { - map_size(int _width, int _height) : width(_width), height(_height) { } + map_size(std::size_t _width, std::size_t _height) : width(_width), height(_height) { } map_size() { } - unsigned width = 0; - unsigned height = 0; + std::size_t width = 0; + std::size_t height = 0; }; struct config From 449ccf324369a75f54ccba4f9937c282199f4e77 Mon Sep 17 00:00:00 2001 From: Jiri Drbalek Date: Mon, 15 Jun 2015 15:00:44 +0000 Subject: [PATCH 022/165] visual tests: drop compare_images.hpp --- test/visual/compare_images.hpp | 53 ---------------------------------- test/visual/renderer.hpp | 16 ++++++++-- 2 files changed, 13 insertions(+), 56 deletions(-) delete mode 100644 test/visual/compare_images.hpp diff --git a/test/visual/compare_images.hpp b/test/visual/compare_images.hpp deleted file mode 100644 index d227d7864..000000000 --- a/test/visual/compare_images.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 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 COMPARE_IMAGES_HPP -#define COMPARE_IMAGES_HPP - -// stl -#include - -// mapnik -#include -#include - -namespace visual_tests -{ - -template -unsigned compare_images(Image const & actual, std::string const & reference) -{ - std::unique_ptr reader(mapnik::get_image_reader(reference, "png")); - if (!reader.get()) - { - throw mapnik::image_reader_exception("Failed to load: " + reference); - } - - mapnik::image_any ref_image_any = reader->read(0, 0, reader->width(), reader->height()); - Image const & reference_image = mapnik::util::get(ref_image_any); - - return mapnik::compare(actual, reference_image, 0, true); -} - -} - -#endif diff --git a/test/visual/renderer.hpp b/test/visual/renderer.hpp index f097dac2b..e1d0d1eb1 100644 --- a/test/visual/renderer.hpp +++ b/test/visual/renderer.hpp @@ -23,15 +23,16 @@ #ifndef RENDERER_HPP #define RENDERER_HPP -#include "compare_images.hpp" - // stl #include #include #include +#include // mapnik #include +#include +#include #include #if defined(GRID_RENDERER) #include @@ -59,7 +60,16 @@ struct renderer_base unsigned compare(image_type const & actual, boost::filesystem::path const& reference) const { - return compare_images(actual, reference.string()); + std::unique_ptr reader(mapnik::get_image_reader(reference.string(), "png")); + if (!reader.get()) + { + throw mapnik::image_reader_exception("Failed to load: " + reference.string()); + } + + mapnik::image_any ref_image_any = reader->read(0, 0, reader->width(), reader->height()); + ImageType const & reference_image = mapnik::util::get(ref_image_any); + + return mapnik::compare(actual, reference_image, 0, true); } void save(image_type const & image, boost::filesystem::path const& path) const From cb2717bd043a9c0b9c03a21252dced38a3ddb018 Mon Sep 17 00:00:00 2001 From: Jiri Drbalek Date: Tue, 30 Jun 2015 12:25:35 +0000 Subject: [PATCH 023/165] visual tests: add support for tiles --- test/visual/config.hpp | 5 +- test/visual/renderer.hpp | 91 +++++++++++++++++++++++++++---- test/visual/report.cpp | 8 ++- test/visual/runner.cpp | 114 ++++++++++++++++++++++++++++----------- 4 files changed, 174 insertions(+), 44 deletions(-) diff --git a/test/visual/config.hpp b/test/visual/config.hpp index 5f4c77192..88daa8e98 100644 --- a/test/visual/config.hpp +++ b/test/visual/config.hpp @@ -45,11 +45,13 @@ struct config { config() : status(true), scales({ 1.0, 2.0 }), - sizes({ { 500, 100 } }) { } + sizes({ { 500, 100 } }), + tiles({ { 1, 1 } }) { } bool status; std::vector scales; std::vector sizes; + std::vector tiles; }; enum result_state : std::uint8_t @@ -66,6 +68,7 @@ struct result result_state state; std::string renderer_name; map_size size; + map_size tiles; double scale_factor; boost::filesystem::path actual_image_path; boost::filesystem::path reference_image_path; diff --git a/test/visual/renderer.hpp b/test/visual/renderer.hpp index e1d0d1eb1..296d86ed0 100644 --- a/test/visual/renderer.hpp +++ b/test/visual/renderer.hpp @@ -57,6 +57,7 @@ struct renderer_base using image_type = ImageType; static constexpr const char * ext = ".png"; + static constexpr const bool support_tiles = true; unsigned compare(image_type const & actual, boost::filesystem::path const& reference) const { @@ -116,6 +117,7 @@ struct svg_renderer : renderer_base { static constexpr const char * name = "svg"; static constexpr const char * ext = ".svg"; + static constexpr const bool support_tiles = false; image_type render(mapnik::Map const & map, double scale_factor) const { @@ -201,10 +203,35 @@ struct grid_renderer : renderer_base }; #endif +template +void set_rectangle(T const & src, T & dst, std::size_t x, std::size_t y) +{ + mapnik::box2d ext0(0, 0, dst.width(), dst.height()); + mapnik::box2d ext1(x, y, x + src.width(), y + src.height()); + + if (ext0.intersects(ext1)) + { + mapnik::box2d box = ext0.intersect(ext1); + for (std::size_t pix_y = box.miny(); pix_y < static_cast(box.maxy()); ++pix_y) + { + typename T::pixel_type * row_to = dst.get_row(pix_y); + typename T::pixel_type const * row_from = src.get_row(pix_y - y); + + for (std::size_t pix_x = box.minx(); pix_x < static_cast(box.maxx()); ++pix_x) + { + row_to[pix_x] = row_from[pix_x - x]; + } + } + } +} + template class renderer { public: + using renderer_type = Renderer; + using image_type = typename Renderer::image_type; + renderer(boost::filesystem::path const & _output_dir, boost::filesystem::path const & _reference_dir, bool _overwrite) : ren(), output_dir(_output_dir), reference_dir(_reference_dir), overwrite(_overwrite) { @@ -212,8 +239,42 @@ public: result test(std::string const & name, mapnik::Map const & map, double scale_factor) const { - typename Renderer::image_type image(ren.render(map, scale_factor)); - boost::filesystem::path reference = reference_dir / image_file_name(name, map.width(), map.height(), scale_factor, true, Renderer::ext); + image_type image(ren.render(map, scale_factor)); + return report(image, name, { map.width(), map.height() }, { 1, 1 }, scale_factor); + } + + result test_tiles(std::string const & name, mapnik::Map & map, map_size const & tiles, double scale_factor) const + { + image_type image(map.width(), map.height()); + mapnik::box2d box = map.get_current_extent(); + map.resize(image.width() / tiles.width, image.height() / tiles.height); + double tile_box_width = box.width() / tiles.width; + double tile_box_height = box.height() / tiles.height; + + for (std::size_t tile_y = 0; tile_y < tiles.height; tile_y++) + { + for (std::size_t tile_x = 0; tile_x < tiles.width; tile_x++) + { + mapnik::box2d tile_box( + box.minx() + tile_x * tile_box_width, + box.miny() + tile_y * tile_box_height, + box.minx() + (tile_x + 1) * tile_box_width, + box.miny() + (tile_y + 1) * tile_box_height); + map.zoom_to_box(tile_box); + image_type tile(ren.render(map, scale_factor)); + set_rectangle(tile, image, tile_x * tile.width(), (tiles.height - 1 - tile_y) * tile.height()); + } + } + return report(image, name, { image.width(), image.height() }, tiles, scale_factor); + } + + result report(image_type const & image, + std::string const & name, + map_size const & size, + map_size const & tiles, + double scale_factor) const + { + boost::filesystem::path reference = reference_dir / image_file_name(name, size, tiles, scale_factor, true); bool reference_exists = boost::filesystem::exists(reference); result res; @@ -221,14 +282,15 @@ public: res.name = name; res.renderer_name = Renderer::name; res.scale_factor = scale_factor; - res.size = map_size(map.width(), map.height()); + res.size = size; + res.tiles = tiles; res.reference_image_path = reference; res.diff = reference_exists ? ren.compare(image, reference) : 0; if (res.diff) { boost::filesystem::create_directories(output_dir); - boost::filesystem::path path = output_dir / image_file_name(name, map.width(), map.height(), scale_factor, false, Renderer::ext); + boost::filesystem::path path = output_dir / image_file_name(name, size, tiles, scale_factor, false); res.actual_image_path = path; res.state = STATE_FAIL; ren.save(image, path); @@ -245,16 +307,23 @@ public: private: std::string image_file_name(std::string const & test_name, - double width, - double height, + map_size const & size, + map_size const & tiles, double scale_factor, - bool reference, - std::string const& ext) const + bool reference) const { std::stringstream s; - s << test_name << '-' << width << '-' << height << '-' - << std::fixed << std::setprecision(1) << scale_factor - << '-' << Renderer::name << (reference ? "-reference" : "") << ext; + s << test_name << '-' << size.width << '-' << size.height << '-'; + if (tiles.width > 1 || tiles.height > 1) + { + s << tiles.width << 'x' << tiles.height << '-'; + } + s << std::fixed << std::setprecision(1) << scale_factor << '-' << Renderer::name; + if (reference) + { + s << "-reference"; + } + s << Renderer::ext; return s.str(); } diff --git a/test/visual/report.cpp b/test/visual/report.cpp index aeca371a0..f60d5da77 100644 --- a/test/visual/report.cpp +++ b/test/visual/report.cpp @@ -32,8 +32,12 @@ namespace visual_tests void console_report::report(result const & r) { - s << '"' << r.name << '-' << r.size.width << '-' << r.size.height << '-' << std::fixed << - std::setprecision(1) << r.scale_factor << "\" with " << r.renderer_name << "... "; + s << '"' << r.name << '-' << r.size.width << '-' << r.size.height; + if (r.tiles.width > 1 || r.tiles.height > 1) + { + s << '-' << r.tiles.width << 'x' << r.tiles.height; + } + s << '-' << std::fixed << std::setprecision(1) << r.scale_factor << "\" with " << r.renderer_name << "... "; switch (r.state) { diff --git a/test/visual/runner.cpp b/test/visual/runner.cpp index f2562c197..f9454f046 100644 --- a/test/visual/runner.cpp +++ b/test/visual/runner.cpp @@ -34,21 +34,55 @@ namespace visual_tests class renderer_visitor { public: - renderer_visitor(std::string const & name, mapnik::Map const & map, double scale_factor) - : name_(name), map_(map), scale_factor_(scale_factor) + renderer_visitor(std::string const & name, + mapnik::Map & map, + map_size const & tiles, + double scale_factor, + result_list & results, + report_type & report) + : name_(name), + map_(map), + tiles_(tiles), + scale_factor_(scale_factor), + results_(results), + report_(report) { } - template - result operator()(T const & renderer) const + template ::type* = nullptr> + void operator()(T const & renderer) { - return renderer.test(name_, map_, scale_factor_); + result r; + if (tiles_.width == 1 && tiles_.height == 1) + { + r = renderer.test(name_, map_, scale_factor_); + } + else + { + r = renderer.test_tiles(name_, map_, tiles_, scale_factor_); + } + mapnik::util::apply_visitor(report_visitor(r), report_); + results_.push_back(std::move(r)); + } + + template ::type* = nullptr> + void operator()(T const & renderer) + { + if (tiles_.width == 1 && tiles_.height == 1) + { + result r = renderer.test(name_, map_, scale_factor_); + mapnik::util::apply_visitor(report_visitor(r), report_); + results_.push_back(std::move(r)); + } } private: std::string const & name_; - mapnik::Map const & map_; + mapnik::Map & map_; + map_size const & tiles_; double scale_factor_; + result_list & results_; + report_type & report_; }; runner::runner(runner::path_type const & styles_dir, @@ -173,12 +207,12 @@ result_list runner::test_range(files_iterator begin, files_iterator end, std::re result_list runner::test_one(runner::path_type const& style_path, config cfg, report_type & report) const { - mapnik::Map m(cfg.sizes.front().width, cfg.sizes.front().height); + mapnik::Map map(cfg.sizes.front().width, cfg.sizes.front().height); result_list results; try { - mapnik::load_map(m, style_path.string(), true); + mapnik::load_map(map, style_path.string(), true); } catch (std::exception const& ex) { @@ -191,7 +225,7 @@ result_list runner::test_one(runner::path_type const& style_path, config cfg, re throw; } - mapnik::parameters const & params = m.get_extra_parameters(); + mapnik::parameters const & params = map.get_extra_parameters(); boost::optional status = params.get("status", cfg.status); @@ -208,32 +242,52 @@ result_list runner::test_one(runner::path_type const& style_path, config cfg, re parse_map_sizes(*sizes, cfg.sizes); } + boost::optional tiles = params.get("tiles"); + + if (tiles) + { + cfg.tiles.clear(); + parse_map_sizes(*tiles, cfg.tiles); + } + + boost::optional bbox_string = params.get("bbox"); + mapnik::box2d box; + + if (bbox_string) + { + box.from_string(*bbox_string); + } + std::string name(style_path.stem().string()); - for (map_size const & size : cfg.sizes) + for (auto const & size : cfg.sizes) { - m.resize(size.width, size.height); - - boost::optional bbox_string = params.get("bbox"); - - if (bbox_string) + for (auto const & scale_factor : cfg.scales) { - mapnik::box2d bbox; - bbox.from_string(*bbox_string); - m.zoom_to_box(bbox); - } - else - { - m.zoom_all(); - } - - for (double const & scale_factor : cfg.scales) - { - for(auto const& ren : renderers_) + for (auto const & tiles_count : cfg.tiles) { - result r = mapnik::util::apply_visitor(renderer_visitor(name, m, scale_factor), ren); - results.emplace_back(r); - mapnik::util::apply_visitor(report_visitor(r), report); + if (!tiles_count.width || !tiles_count.height) + { + throw std::runtime_error("Cannot render zero tiles."); + } + if (size.width % tiles_count.width || size.height % tiles_count.height) + { + throw std::runtime_error("Tile size is not an integer."); + } + + for (auto const & ren : renderers_) + { + map.resize(size.width, size.height); + if (box.valid()) + { + map.zoom_to_box(box); + } + else + { + map.zoom_all(); + } + mapnik::util::apply_visitor(renderer_visitor(name, map, tiles_count, scale_factor, results, report), ren); + } } } } From a5594bd44572a60e0a47ff09003b944ba44ce42e Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Thu, 2 Jul 2015 09:57:12 -0700 Subject: [PATCH 024/165] fix scan-build 'Value stored to 'a_' is never read' warning --- deps/agg/include/agg_color_gray.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deps/agg/include/agg_color_gray.h b/deps/agg/include/agg_color_gray.h index f2cd0ff9c..bf300b8f1 100644 --- a/deps/agg/include/agg_color_gray.h +++ b/deps/agg/include/agg_color_gray.h @@ -652,8 +652,8 @@ struct gray16 //-------------------------------------------------------------------- self_type& opacity(double a_) { - if (a_ < 0) a_ = 0; - else if(a_ > 1) a_ = 1; + if (a_ < 0) a = 0; + else if(a_ > 1) a = 1; else a = (value_type)uround(a_ * double(base_mask)); return *this; } From 39eab41ecd27b60a10f7b131827e0702a477c385 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Thu, 2 Jul 2015 10:19:22 -0700 Subject: [PATCH 025/165] use C++11 in cairo_context::set_dash - refs #2948 --- src/cairo/cairo_context.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/cairo/cairo_context.cpp b/src/cairo/cairo_context.cpp index 1a20e90f0..c8ba13c8a 100644 --- a/src/cairo/cairo_context.cpp +++ b/src/cairo/cairo_context.cpp @@ -28,8 +28,8 @@ #include #include +#include -#include namespace mapnik { cairo_face::cairo_face(std::shared_ptr const& library, face_ptr const& face) @@ -227,17 +227,13 @@ void cairo_context::set_line_width(double width) void cairo_context::set_dash(dash_array const& dashes, double scale_factor) { - std::valarray d(dashes.size() * 2); - dash_array::const_iterator itr = dashes.begin(); - dash_array::const_iterator end = dashes.end(); - std::size_t index = 0; - - for (; itr != end; ++itr) + std::vector d; + d.reserve(dashes.size() * 2); + for (auto const& dash : dashes) { - d[index++] = itr->first * scale_factor; - d[index++] = itr->second * scale_factor; + d.emplace_back(dash.first * scale_factor); + d.emplace_back(dash.second * scale_factor); } - cairo_set_dash(cairo_.get() , &d[0], static_cast(d.size()), 0/*offset*/); check_object_status_and_throw_exception(*this); } From 8fe19a7b4517e2ceda1402b1bd2dfd01c713b80d Mon Sep 17 00:00:00 2001 From: Jiri Drbalek Date: Fri, 3 Jul 2015 13:22:23 +0000 Subject: [PATCH 026/165] fix rendering text on tile's borders --- src/text/placement_finder.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/text/placement_finder.cpp b/src/text/placement_finder.cpp index ec2add037..6d81aa516 100644 --- a/src/text/placement_finder.cpp +++ b/src/text/placement_finder.cpp @@ -369,9 +369,7 @@ bool placement_finder::collision(const box2d &box, const value_unicode_s margin = (text_props_->margin != 0 ? text_props_->margin : text_props_->minimum_distance) * scale_factor_; repeat_distance = text_props_->repeat_distance * scale_factor_; } - return !detector_.extent().intersects(box) - || - (text_props_->avoid_edges && !extent_.contains(box)) + return (text_props_->avoid_edges && !extent_.contains(box)) || (text_props_->minimum_padding > 0 && !extent_.contains(box + (scale_factor_ * text_props_->minimum_padding))) From 56678b8aad89a61cecbb0d26f8f775ffc6b80238 Mon Sep 17 00:00:00 2001 From: artemp Date: Mon, 6 Jul 2015 12:47:45 +0200 Subject: [PATCH 027/165] geojson.input - fix bug typo --- plugins/input/geojson/geojson_datasource.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/input/geojson/geojson_datasource.cpp b/plugins/input/geojson/geojson_datasource.cpp index 739ef733e..e1ebb4751 100644 --- a/plugins/input/geojson/geojson_datasource.cpp +++ b/plugins/input/geojson/geojson_datasource.cpp @@ -218,7 +218,7 @@ void geojson_datasource::initialise_index(Iterator start, Iterator end) // parse first feature to extract attributes schema. // NOTE: this doesn't yield correct answer for geoJSON in general, just an indication Iterator itr2 = start + geometry_index.first; - Iterator end2 = itr + geometry_index.second; + Iterator end2 = itr2 + geometry_index.second; mapnik::context_ptr ctx = std::make_shared(); mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx,1)); if (!boost::spirit::qi::phrase_parse(itr2, end2, (geojson_datasource_static_feature_grammar)(boost::phoenix::ref(*feature)), space)) From c233364abc7d67bf60e57196eca5e0852b5ed53d Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 6 Jul 2015 12:43:22 -0700 Subject: [PATCH 028/165] use latest libpng@1.6.17 --- bootstrap.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootstrap.sh b/bootstrap.sh index 695613d64..d68daa802 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -61,7 +61,7 @@ function install_mason_deps() { install harfbuzz 0.9.40 libharfbuzz & install jpeg_turbo 1.4.0 libjpeg & install libxml2 2.9.2 libxml2 & - install libpng 1.6.16 libpng & + install libpng 1.6.17 libpng & install webp 0.4.2 libwebp & install icu 54.1 & install proj 4.8.0 libproj & From d5c11af01945b415710a7f577c3948a3acc8380a Mon Sep 17 00:00:00 2001 From: Blake Thompson Date: Mon, 6 Jul 2015 15:30:54 -0500 Subject: [PATCH 029/165] Fixed issues with multi-line paths in offset_converter, line offsets were following the polygon offset logic when they shouldn't. Closes #2942 --- include/mapnik/offset_converter.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/mapnik/offset_converter.hpp b/include/mapnik/offset_converter.hpp index b1f2476c8..857cea5bd 100644 --- a/include/mapnik/offset_converter.hpp +++ b/include/mapnik/offset_converter.hpp @@ -436,11 +436,11 @@ private: v_y1y2 = v1.y - close_points[cpt].y; cpt++; } + start_v2.x = v2.x; + start_v2.y = v2.y; } - start_v2.x = v2.x; - start_v2.y = v2.y; } - if (v2.cmd == SEG_MOVETO) + if (is_polygon && v2.cmd == SEG_MOVETO) { start_.x = v2.x; start_.y = v2.y; From 852a7e432aee3e90924a8dab77f483df8d5e38ac Mon Sep 17 00:00:00 2001 From: Blake Thompson Date: Mon, 6 Jul 2015 16:12:04 -0500 Subject: [PATCH 030/165] Updated tests to cover case found in #2942 --- test/data-visual | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/data-visual b/test/data-visual index d694deeca..f2e2ae094 160000 --- a/test/data-visual +++ b/test/data-visual @@ -1 +1 @@ -Subproject commit d694deeca509481d540568b85f079ee35a3fda39 +Subproject commit f2e2ae0949dfd074db973838ce1db365e4e70ab6 From ff720d6231e1de064c98e6c816e1f87566d4acd3 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 6 Jul 2015 15:40:34 -0700 Subject: [PATCH 031/165] support clipping in markerssymbolizer on multi-types - refs #2951 --- .../mapnik/renderer_common/process_markers_symbolizer.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/mapnik/renderer_common/process_markers_symbolizer.hpp b/include/mapnik/renderer_common/process_markers_symbolizer.hpp index 9d0852675..16e18ecf2 100644 --- a/include/mapnik/renderer_common/process_markers_symbolizer.hpp +++ b/include/mapnik/renderer_common/process_markers_symbolizer.hpp @@ -118,9 +118,9 @@ struct render_marker_symbolizer_visitor if (clip) // optional clip (default: true) { geometry::geometry_types type = geometry::geometry_type(feature_.get_geometry()); - if (type == geometry::geometry_types::Polygon) + if (type == geometry::geometry_types::Polygon || type == geometry::geometry_types::MultiPolygon) converter.template set(); - else if (type == geometry::geometry_types::LineString) + else if (type == geometry::geometry_types::LineString || type == geometry::geometry_types::MultiLineString) converter.template set(); } @@ -223,9 +223,9 @@ struct render_marker_symbolizer_visitor if (clip) // optional clip (default: true) { geometry::geometry_types type = geometry::geometry_type(feature_.get_geometry()); - if (type == geometry::geometry_types::Polygon) + if (type == geometry::geometry_types::Polygon || type == geometry::geometry_types::MultiPolygon) converter.template set(); - else if (type == geometry::geometry_types::LineString) + else if (type == geometry::geometry_types::LineString || type == geometry::geometry_types::MultiLineString) converter.template set(); } converter.template set(); //always transform From 418855118ac3315cbf873135b602e685d8155a6d Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 6 Jul 2015 17:40:47 -0700 Subject: [PATCH 032/165] remove un-implemented method on glyph_positions --- include/mapnik/text/glyph_positions.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/include/mapnik/text/glyph_positions.hpp b/include/mapnik/text/glyph_positions.hpp index 554e40667..c192037fd 100644 --- a/include/mapnik/text/glyph_positions.hpp +++ b/include/mapnik/text/glyph_positions.hpp @@ -82,7 +82,6 @@ public: void set_marker(marker_info_ptr marker, pixel_position const& marker_pos); marker_info_ptr get_marker() const; pixel_position const& marker_pos() const; - box2d const & bbox() const; private: std::vector data_; pixel_position base_point_; From 9f729506290d813a5ddad3066557f12ba0716774 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 6 Jul 2015 18:14:56 -0700 Subject: [PATCH 033/165] update visual tests --- test/data-visual | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/data-visual b/test/data-visual index f2e2ae094..92c331e07 160000 --- a/test/data-visual +++ b/test/data-visual @@ -1 +1 @@ -Subproject commit f2e2ae0949dfd074db973838ce1db365e4e70ab6 +Subproject commit 92c331e0775ae8babc2bc36976b8ba041c07f23d From e65f93f5f25195222266096931514a66d9fbeb3e Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 6 Jul 2015 18:23:34 -0700 Subject: [PATCH 034/165] update test images after #2949 #2929 --- test/data-visual | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/data-visual b/test/data-visual index 92c331e07..d7dc9041f 160000 --- a/test/data-visual +++ b/test/data-visual @@ -1 +1 @@ -Subproject commit 92c331e0775ae8babc2bc36976b8ba041c07f23d +Subproject commit d7dc9041ffc19c3f1e0762f6a1313639f35068ed From 3f91186a74a78f0111121b4527b0f6f78960159b Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 6 Jul 2015 18:28:37 -0700 Subject: [PATCH 035/165] avoid creating placements for off canvas placements - refs #2929 --- src/text/placement_finder.cpp | 38 +++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/src/text/placement_finder.cpp b/src/text/placement_finder.cpp index 6d81aa516..3e7b69bfc 100644 --- a/src/text/placement_finder.cpp +++ b/src/text/placement_finder.cpp @@ -182,11 +182,26 @@ bool placement_finder::find_point_placement(pixel_position const& pos) // add_marker first checks for collision and then updates the detector. if (has_marker_ && !add_marker(glyphs, pos)) return false; - for (box2d const& bbox : bboxes) + box2d label_box; + bool first = true; + for (box2d const& box : bboxes) { - detector_.insert(bbox, layouts_.text()); + if (first) + { + label_box = box; + first = false; + } + else + { + label_box.expand_to_include(box); + } + detector_.insert(box, layouts_.text()); + } + // do not render text off the canvas + if (extent_.intersects(label_box)) + { + placements_.push_back(glyphs); } - placements_.push_back(glyphs); return true; } @@ -326,11 +341,26 @@ bool placement_finder::single_line_placement(vertex_cache &pp, text_upright_e or return single_line_placement(pp, real_orientation == UPRIGHT_RIGHT ? UPRIGHT_LEFT : UPRIGHT_RIGHT); } + box2d label_box; + bool first = true; for (box2d const& box : bboxes) { + if (first) + { + label_box = box; + first = false; + } + else + { + label_box.expand_to_include(box); + } detector_.insert(box, layouts_.text()); } - placements_.push_back(glyphs); + // do not render text off the canvas + if (extent_.intersects(label_box)) + { + placements_.push_back(glyphs); + } return true; } From 3ed7e347cb7f43c7685c282fae6a9069d978dc14 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 6 Jul 2015 18:47:47 -0700 Subject: [PATCH 036/165] use unique_ptr instead of shared_ptr for glyph_positions_ptr - refs #2516 --- .../renderer_common/process_group_symbolizer.hpp | 2 +- include/mapnik/text/glyph_positions.hpp | 2 +- include/mapnik/text/placement_finder.hpp | 2 +- src/agg/process_group_symbolizer.cpp | 2 +- src/agg/process_shield_symbolizer.cpp | 2 +- src/agg/process_text_symbolizer.cpp | 2 +- src/cairo/process_group_symbolizer.cpp | 2 +- src/cairo/process_text_symbolizer.cpp | 4 ++-- src/grid/process_group_symbolizer.cpp | 2 +- src/grid/process_shield_symbolizer.cpp | 2 +- src/grid/process_text_symbolizer.cpp | 2 +- src/text/placement_finder.cpp | 10 +++++----- 12 files changed, 17 insertions(+), 17 deletions(-) diff --git a/include/mapnik/renderer_common/process_group_symbolizer.hpp b/include/mapnik/renderer_common/process_group_symbolizer.hpp index 006d1a9f3..d593b8ead 100644 --- a/include/mapnik/renderer_common/process_group_symbolizer.hpp +++ b/include/mapnik/renderer_common/process_group_symbolizer.hpp @@ -211,7 +211,7 @@ void render_offset_placements(placements_list const& placements, pixel_position const& offset, F render_text) { - for (glyph_positions_ptr glyphs : placements) + for (auto const& glyphs : placements) { // move the glyphs to the correct offset pixel_position base_point = glyphs->get_base_point(); diff --git a/include/mapnik/text/glyph_positions.hpp b/include/mapnik/text/glyph_positions.hpp index c192037fd..604de8c7c 100644 --- a/include/mapnik/text/glyph_positions.hpp +++ b/include/mapnik/text/glyph_positions.hpp @@ -89,7 +89,7 @@ private: pixel_position marker_pos_; box2d bbox_; }; -using glyph_positions_ptr = std::shared_ptr; +using glyph_positions_ptr = std::unique_ptr; using placements_list = std::list; } diff --git a/include/mapnik/text/placement_finder.hpp b/include/mapnik/text/placement_finder.hpp index f3ffd512a..f2bd36db3 100644 --- a/include/mapnik/text/placement_finder.hpp +++ b/include/mapnik/text/placement_finder.hpp @@ -75,7 +75,7 @@ private: // Checks for collision. bool collision(box2d const& box, const value_unicode_string &repeat_key, bool line_placement) const; // Adds marker to glyph_positions and to collision detector. Returns false if there is a collision. - bool add_marker(glyph_positions_ptr glyphs, pixel_position const& pos) const; + bool add_marker(glyph_positions_ptr & glyphs, pixel_position const& pos) const; // Maps upright==auto, left-only and right-only to left,right to simplify processing. // angle = angle of at start of line (to estimate best option for upright==auto) text_upright_e simplify_upright(text_upright_e upright, double angle) const; diff --git a/src/agg/process_group_symbolizer.cpp b/src/agg/process_group_symbolizer.cpp index 836e13533..68c54f2c3 100644 --- a/src/agg/process_group_symbolizer.cpp +++ b/src/agg/process_group_symbolizer.cpp @@ -120,7 +120,7 @@ struct thunk_renderer render_offset_placements( thunk.placements_, offset_, - [&] (glyph_positions_ptr glyphs) + [&] (glyph_positions_ptr const& glyphs) { marker_info_ptr mark = glyphs->get_marker(); if (mark) diff --git a/src/agg/process_shield_symbolizer.cpp b/src/agg/process_shield_symbolizer.cpp index e8d1cc5c5..67fe8de0d 100644 --- a/src/agg/process_shield_symbolizer.cpp +++ b/src/agg/process_shield_symbolizer.cpp @@ -60,7 +60,7 @@ void agg_renderer::process(shield_symbolizer const& sym, double opacity = get(sym,keys::opacity, feature, common_.vars_, 1.0); placements_list const& placements = helper.get(); - for (glyph_positions_ptr glyphs : placements) + for (auto const& glyphs : placements) { marker_info_ptr mark = glyphs->get_marker(); if (mark) diff --git a/src/agg/process_text_symbolizer.cpp b/src/agg/process_text_symbolizer.cpp index c30566d61..078a7abaa 100644 --- a/src/agg/process_text_symbolizer.cpp +++ b/src/agg/process_text_symbolizer.cpp @@ -67,7 +67,7 @@ void agg_renderer::process(text_symbolizer const& sym, } placements_list const& placements = helper.get(); - for (glyph_positions_ptr glyphs : placements) + for (auto const& glyphs : placements) { ren.render(*glyphs); } diff --git a/src/cairo/process_group_symbolizer.cpp b/src/cairo/process_group_symbolizer.cpp index 79338664d..d32afcf67 100644 --- a/src/cairo/process_group_symbolizer.cpp +++ b/src/cairo/process_group_symbolizer.cpp @@ -95,7 +95,7 @@ struct thunk_renderer render_offset_placements( thunk.placements_, offset_, - [&] (glyph_positions_ptr glyphs) + [&] (glyph_positions_ptr const& glyphs) { marker_info_ptr mark = glyphs->get_marker(); if (mark) diff --git a/src/cairo/process_text_symbolizer.cpp b/src/cairo/process_text_symbolizer.cpp index 176b6c635..06b56cb2d 100644 --- a/src/cairo/process_text_symbolizer.cpp +++ b/src/cairo/process_text_symbolizer.cpp @@ -55,7 +55,7 @@ void cairo_renderer::process(shield_symbolizer const& sym, double opacity = get(sym,keys::opacity,feature, common_.vars_, 1.0); placements_list const &placements = helper.get(); - for (glyph_positions_ptr glyphs : placements) + for (auto const& glyphs : placements) { marker_info_ptr mark = glyphs->get_marker(); if (mark) { @@ -93,7 +93,7 @@ void cairo_renderer::process(text_symbolizer const& sym, composite_mode_e halo_comp_op = get(sym, keys::halo_comp_op, feature, common_.vars_, src_over); placements_list const& placements = helper.get(); - for (glyph_positions_ptr glyphs : placements) + for (auto const& glyphs : placements) { context_.add_text(*glyphs, face_manager_, comp_op, halo_comp_op, common_.scale_factor_); } diff --git a/src/grid/process_group_symbolizer.cpp b/src/grid/process_group_symbolizer.cpp index 25c360027..a672f6b8f 100644 --- a/src/grid/process_group_symbolizer.cpp +++ b/src/grid/process_group_symbolizer.cpp @@ -129,7 +129,7 @@ struct thunk_renderer render_offset_placements( thunk.placements_, offset_, - [&] (glyph_positions_ptr glyphs) + [&] (glyph_positions_ptr const& glyphs) { marker_info_ptr mark = glyphs->get_marker(); if (mark) diff --git a/src/grid/process_shield_symbolizer.cpp b/src/grid/process_shield_symbolizer.cpp index ae4ffa2d1..781989384 100644 --- a/src/grid/process_shield_symbolizer.cpp +++ b/src/grid/process_shield_symbolizer.cpp @@ -64,7 +64,7 @@ void grid_renderer::process(shield_symbolizer const& sym, placements_list const& placements = helper.get(); value_integer feature_id = feature.id(); - for (glyph_positions_ptr glyphs : placements) + for (auto const& glyphs : placements) { marker_info_ptr mark = glyphs->get_marker(); if (mark) diff --git a/src/grid/process_text_symbolizer.cpp b/src/grid/process_text_symbolizer.cpp index a615a5db6..3df4ad24f 100644 --- a/src/grid/process_text_symbolizer.cpp +++ b/src/grid/process_text_symbolizer.cpp @@ -66,7 +66,7 @@ void grid_renderer::process(text_symbolizer const& sym, placements_list const& placements = helper.get(); value_integer feature_id = feature.id(); - for (glyph_positions_ptr glyphs : placements) + for (auto const& glyphs : placements) { ren.render(*glyphs, feature_id); placement_found = true; diff --git a/src/text/placement_finder.cpp b/src/text/placement_finder.cpp index 3e7b69bfc..7c09e56f2 100644 --- a/src/text/placement_finder.cpp +++ b/src/text/placement_finder.cpp @@ -118,7 +118,7 @@ text_upright_e placement_finder::simplify_upright(text_upright_e upright, double bool placement_finder::find_point_placement(pixel_position const& pos) { - glyph_positions_ptr glyphs = std::make_shared(); + glyph_positions_ptr glyphs = std::make_unique(); std::vector > bboxes; glyphs->reserve(layouts_.glyphs_count()); @@ -200,7 +200,7 @@ bool placement_finder::find_point_placement(pixel_position const& pos) // do not render text off the canvas if (extent_.intersects(label_box)) { - placements_.push_back(glyphs); + placements_.push_back(std::move(glyphs)); } return true; @@ -215,7 +215,7 @@ bool placement_finder::single_line_placement(vertex_cache &pp, text_upright_e or vertex_cache::scoped_state begin(pp); text_upright_e real_orientation = simplify_upright(orientation, pp.angle()); - glyph_positions_ptr glyphs = std::make_shared(); + glyph_positions_ptr glyphs = std::make_unique(); std::vector > bboxes; glyphs->reserve(layouts_.glyphs_count()); bboxes.reserve(layouts_.glyphs_count()); @@ -359,7 +359,7 @@ bool placement_finder::single_line_placement(vertex_cache &pp, text_upright_e or // do not render text off the canvas if (extent_.intersects(label_box)) { - placements_.push_back(glyphs); + placements_.push_back(std::move(glyphs)); } return true; @@ -420,7 +420,7 @@ void placement_finder::set_marker(marker_info_ptr m, box2d box, bool mar } -bool placement_finder::add_marker(glyph_positions_ptr glyphs, pixel_position const& pos) const +bool placement_finder::add_marker(glyph_positions_ptr & glyphs, pixel_position const& pos) const { pixel_position real_pos = (marker_unlocked_ ? pos : glyphs->get_base_point()) + marker_displacement_; box2d bbox = marker_box_; From a91d73779f6cb7249a37d7c75766f84d4560eea5 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 6 Jul 2015 19:27:05 -0700 Subject: [PATCH 037/165] revert the functionality of 944f34b - refs #2937 --- src/vertex_cache.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/vertex_cache.cpp b/src/vertex_cache.cpp index 071b8d08b..c0d575892 100644 --- a/src/vertex_cache.cpp +++ b/src/vertex_cache.cpp @@ -155,8 +155,14 @@ vertex_cache & vertex_cache::get_offseted(double offset, double region_width) // find the point on the offset line closest to the current position, // which we'll use to make the offset line aligned to this one. - double seek = offseted_line->position_closest_to(current_position_); + // TODO: `position_closest_to` is disable since it is too expensive: + // https://github.com/mapnik/mapnik/issues/2937 + //double seek = offseted_line->position_closest_to(current_position_); + double seek = (position_ + region_width/2.0) * offseted_line->length() / length() - region_width/2.0; + if (seek < 0) seek = 0; + if (seek > offseted_line->length()) seek = offseted_line->length(); offseted_line->move(seek); + return *offseted_line; } From d13543c359b243e61cb1740306f72e418e53a1d4 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 7 Jul 2015 00:39:30 -0700 Subject: [PATCH 038/165] update visual tests to https://github.com/mapnik/test-data-visual/commit/a83ea35fec924a5a749e591909f4fd20a9d989c1 --- test/data-visual | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/data-visual b/test/data-visual index d7dc9041f..a83ea35fe 160000 --- a/test/data-visual +++ b/test/data-visual @@ -1 +1 @@ -Subproject commit d7dc9041ffc19c3f1e0762f6a1313639f35068ed +Subproject commit a83ea35fec924a5a749e591909f4fd20a9d989c1 From 56fe702bac0a9be9fe989a42d30b0a6dbbdd0932 Mon Sep 17 00:00:00 2001 From: artemp Date: Tue, 7 Jul 2015 14:19:51 +0200 Subject: [PATCH 039/165] agg line_symbolizer - dispatch to appropriate clipper based on geometry_type --- src/agg/process_line_symbolizer.cpp | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/agg/process_line_symbolizer.cpp b/src/agg/process_line_symbolizer.cpp index e2004e921..2eb476f95 100644 --- a/src/agg/process_line_symbolizer.cpp +++ b/src/agg/process_line_symbolizer.cpp @@ -31,6 +31,7 @@ #include #include #include +#include // agg #include "agg_basics.h" #include "agg_rendering_buffer.h" @@ -164,12 +165,19 @@ void agg_renderer::process(line_symbolizer const& sym, rasterizer_type ras(ren); set_join_caps_aa(sym, ras, feature, common_.vars_); - using vertex_converter_type = vertex_converter; vertex_converter_type converter(clip_box,sym,common_.t_,prj_trans,tr,feature,common_.vars_,common_.scale_factor_); - if (clip) converter.set(); // optional clip (default: true) + if (clip) + { + geometry::geometry_types type = geometry::geometry_type(feature.get_geometry()); + if (type == geometry::geometry_types::Polygon) + converter.template set(); + else if (type == geometry::geometry_types::LineString) + converter.template set(); + } converter.set(); // always transform if (std::fabs(offset) > 0.0) converter.set(); // parallel offset converter.set(); // optional affine transform @@ -183,14 +191,20 @@ void agg_renderer::process(line_symbolizer const& sym, } else { - using vertex_converter_type = vertex_converter; vertex_converter_type converter(clip_box, sym,common_.t_,prj_trans,tr,feature,common_.vars_,common_.scale_factor_); - - if (clip) converter.set(); // optional clip (default: true) + if (clip) + { + geometry::geometry_types type = geometry::geometry_type(feature.get_geometry()); + if (type == geometry::geometry_types::Polygon) + converter.template set(); + else if (type == geometry::geometry_types::LineString) + converter.template set(); + } converter.set(); // always transform if (std::fabs(offset) > 0.0) converter.set(); // parallel offset converter.set(); // optional affine transform From bb815dacf807560e3382027b571d4f0fca6c7872 Mon Sep 17 00:00:00 2001 From: artemp Date: Tue, 7 Jul 2015 14:20:54 +0200 Subject: [PATCH 040/165] offset_converter - skip duplicate SEG_CLOSE (after LINETO (startx,starty)) commands to normalise input --- include/mapnik/offset_converter.hpp | 35 ++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/include/mapnik/offset_converter.hpp b/include/mapnik/offset_converter.hpp index 8f3a6f1f3..674562137 100644 --- a/include/mapnik/offset_converter.hpp +++ b/include/mapnik/offset_converter.hpp @@ -53,7 +53,7 @@ struct offset_converter , pre_first_(vertex2d::no_init) , pre_(vertex2d::no_init) , cur_(vertex2d::no_init) - {} + {} enum status { @@ -299,6 +299,7 @@ private: vertex2d v1(vertex2d::no_init); vertex2d v2(vertex2d::no_init); vertex2d w(vertex2d::no_init); + vertex2d start(vertex2d::no_init); vertex2d start_v2(vertex2d::no_init); std::vector points; std::vector close_points; @@ -314,21 +315,35 @@ private: { return status_ = process; } - + while ((v0.cmd = geom_.vertex(&v0.x, &v0.y)) != SEG_END) { - points.push_back(vertex2d(v0.x, v0.y, v0.cmd)); if (v0.cmd == SEG_CLOSE) { is_polygon = true; close_points.push_back(vertex2d(v1.x, v1.y, v1.cmd)); + if (points.size() > 0) + { + auto & prev = points.back(); + if (prev.x == start.x && prev.y == start.y) + { + prev.cmd = SEG_CLOSE; // account for dupes (line_to(move_to) + close_path) in agg poly clipper + continue; + } + } + } + else if (v0.cmd == SEG_MOVETO) + { + start = v0; } v1.x = v0.x; v1.y = v0.y; v1.cmd = v0.cmd; + points.push_back(vertex2d(v0.x, v0.y, v0.cmd)); } // Push SEG_END points.push_back(vertex2d(v0.x, v0.y, v0.cmd)); + std::size_t i = 0; v1 = points[i++]; v2 = points[i++]; @@ -375,11 +390,11 @@ private: { dot = v_x1x0 * v_x1x2 + v_y1y0 * v_y1y2; // dot product det = v_x1x0 * v_y1y2 - v_y1y0 * v_x1x2; // determinant - + joint_angle = std::atan2(det, dot); // atan2(y, x) or atan2(sin, cos) if (joint_angle < 0) joint_angle = joint_angle + 2 * M_PI; joint_angle = std::fmod(joint_angle, 2 * M_PI); - + if (offset_ > 0.0) { joint_angle = 2 * M_PI - joint_angle; @@ -390,7 +405,7 @@ private: if (std::abs(joint_angle) > M_PI) { curve_angle = explement_reflex_angle(angle_b - angle_a); - // Bulge steps should be determined by the inverse of the joint angle. + // Bulge steps should be determined by the inverse of the joint angle. double half_turns = half_turn_segments_ * std::fabs(curve_angle); bulge_steps = 1 + static_cast(std::floor(half_turns / M_PI)); } @@ -470,7 +485,7 @@ private: v_y1y0 = -v_y1y2; // Calculate new angle_a angle_a = std::atan2(v_y1y2, v_x1x2); - + // Calculate the new vector v_x1x2 = v2.x - v1.x; v_y1y2 = v2.y - v1.y; @@ -479,11 +494,11 @@ private: dot = v_x1x0 * v_x1x2 + v_y1y0 * v_y1y2; // dot product det = v_x1x0 * v_y1y2 - v_y1y0 * v_x1x2; // determinant - + joint_angle = std::atan2(det, dot); // atan2(y, x) or atan2(sin, cos) if (joint_angle < 0) joint_angle = joint_angle + 2 * M_PI; joint_angle = std::fmod(joint_angle, 2 * M_PI); - + if (offset_ > 0.0) { joint_angle = 2 * M_PI - joint_angle; @@ -494,7 +509,7 @@ private: if (std::abs(joint_angle) > M_PI) { curve_angle = explement_reflex_angle(angle_b - angle_a); - // Bulge steps should be determined by the inverse of the joint angle. + // Bulge steps should be determined by the inverse of the joint angle. double half_turns = half_turn_segments_ * std::fabs(curve_angle); bulge_steps = 1 + static_cast(std::floor(half_turns / M_PI)); } From 1fa6cc73db21bbc6a8e330f1cafcf234f7df5751 Mon Sep 17 00:00:00 2001 From: Jiri Drbalek Date: Tue, 7 Jul 2015 12:35:28 +0000 Subject: [PATCH 041/165] visual tests: add time measurement --- test/visual/config.hpp | 2 ++ test/visual/renderer.hpp | 12 ++++---- test/visual/report.cpp | 5 ++++ test/visual/report.hpp | 5 ++-- test/visual/run.cpp | 7 ++++- test/visual/runner.cpp | 65 +++++++++++++++++++++++++++++----------- test/visual/runner.hpp | 2 ++ 7 files changed, 71 insertions(+), 27 deletions(-) diff --git a/test/visual/config.hpp b/test/visual/config.hpp index 88daa8e98..0dac1d983 100644 --- a/test/visual/config.hpp +++ b/test/visual/config.hpp @@ -26,6 +26,7 @@ // stl #include #include +#include // boost #include @@ -74,6 +75,7 @@ struct result boost::filesystem::path reference_image_path; std::string error_message; unsigned diff; + std::chrono::high_resolution_clock::duration duration; }; using result_list = std::vector; diff --git a/test/visual/renderer.hpp b/test/visual/renderer.hpp index 33811cfbd..6ad979165 100644 --- a/test/visual/renderer.hpp +++ b/test/visual/renderer.hpp @@ -237,20 +237,18 @@ public: { } - result test(std::string const & name, mapnik::Map const & map, double scale_factor) const + image_type render(mapnik::Map const & map, double scale_factor) const { - image_type image(ren.render(map, scale_factor)); - return report(image, name, { map.width(), map.height() }, { 1, 1 }, scale_factor); + return ren.render(map, scale_factor); } - result test_tiles(std::string const & name, mapnik::Map & map, map_size const & tiles, double scale_factor) const + image_type render(mapnik::Map & map, double scale_factor, map_size const & tiles) const { - image_type image(map.width(), map.height()); mapnik::box2d box = map.get_current_extent(); + image_type image(map.width(), map.height()); map.resize(image.width() / tiles.width, image.height() / tiles.height); double tile_box_width = box.width() / tiles.width; double tile_box_height = box.height() / tiles.height; - for (std::size_t tile_y = 0; tile_y < tiles.height; tile_y++) { for (std::size_t tile_x = 0; tile_x < tiles.width; tile_x++) @@ -265,7 +263,7 @@ public: set_rectangle(tile, image, tile_x * tile.width(), (tiles.height - 1 - tile_y) * tile.height()); } } - return report(image, name, { image.width(), image.height() }, tiles, scale_factor); + return image; } result report(image_type const & image, diff --git a/test/visual/report.cpp b/test/visual/report.cpp index f60d5da77..105ba06d0 100644 --- a/test/visual/report.cpp +++ b/test/visual/report.cpp @@ -55,6 +55,11 @@ void console_report::report(result const & r) break; } + if (show_duration) + { + s << " (" << std::chrono::duration_cast(r.duration).count() << " milliseconds)"; + } + s << std::endl; } diff --git a/test/visual/report.hpp b/test/visual/report.hpp index 3b6146ec5..1a7a3c4e7 100644 --- a/test/visual/report.hpp +++ b/test/visual/report.hpp @@ -36,7 +36,7 @@ namespace visual_tests class console_report { public: - console_report() : s(std::clog) + console_report(bool _show_duration) : s(std::clog), show_duration(_show_duration) { } @@ -49,12 +49,13 @@ public: protected: std::ostream & s; + bool show_duration; }; class console_short_report : public console_report { public: - console_short_report() : console_report() + console_short_report() : console_report(false) { } diff --git a/test/visual/run.cpp b/test/visual/run.cpp index d44508188..444486905 100644 --- a/test/visual/run.cpp +++ b/test/visual/run.cpp @@ -53,6 +53,8 @@ int main(int argc, char** argv) ("help,h", "produce usage message") ("verbose,v", "verbose output") ("overwrite,o", "overwrite reference image") + ("duration,d", "output rendering duration") + ("iterations,i", po::value()->default_value(1), "number of iterations for benchmarking") ("jobs,j", po::value()->default_value(1), "number of parallel threads") ("styles-dir", po::value()->default_value("test/data-visual/styles"), "directory with styles") ("images-dir", po::value()->default_value("test/data-visual/images"), "directory with reference images") @@ -108,8 +110,11 @@ int main(int argc, char** argv) output_dir, vm["images-dir"].as(), vm.count("overwrite"), + vm["iterations"].as(), vm["jobs"].as()); - report_type report = vm.count("verbose") ? report_type((console_report())) : report_type((console_short_report())); + bool show_duration = vm.count("duration"); + bool verbose = vm.count("verbose") | show_duration; + report_type report(verbose ? report_type((console_report(show_duration))) : report_type((console_short_report()))); result_list results; try diff --git a/test/visual/runner.cpp b/test/visual/runner.cpp index f9454f046..45dc36656 100644 --- a/test/visual/runner.cpp +++ b/test/visual/runner.cpp @@ -39,30 +39,22 @@ public: map_size const & tiles, double scale_factor, result_list & results, - report_type & report) + report_type & report, + std::size_t iterations) : name_(name), map_(map), tiles_(tiles), scale_factor_(scale_factor), results_(results), - report_(report) + report_(report), + iterations_(iterations) { } template ::type* = nullptr> void operator()(T const & renderer) { - result r; - if (tiles_.width == 1 && tiles_.height == 1) - { - r = renderer.test(name_, map_, scale_factor_); - } - else - { - r = renderer.test_tiles(name_, map_, tiles_, scale_factor_); - } - mapnik::util::apply_visitor(report_visitor(r), report_); - results_.push_back(std::move(r)); + test(renderer); } template ::type* = nullptr> @@ -70,30 +62,69 @@ public: { if (tiles_.width == 1 && tiles_.height == 1) { - result r = renderer.test(name_, map_, scale_factor_); - mapnik::util::apply_visitor(report_visitor(r), report_); - results_.push_back(std::move(r)); + test(renderer); } } private: + template + void test(T const & renderer) + { + map_size size { map_.width(), map_.height() }; + std::chrono::high_resolution_clock::time_point start(std::chrono::high_resolution_clock::now()); + for (std::size_t i = iterations_ ; i > 0; i--) + { + typename T::image_type image(render(renderer)); + if (i == 1) + { + std::chrono::high_resolution_clock::time_point end(std::chrono::high_resolution_clock::now()); + result r(renderer.report(image, name_, size, tiles_, scale_factor_)); + r.duration = end - start; + mapnik::util::apply_visitor(report_visitor(r), report_); + results_.push_back(std::move(r)); + } + } + } + + template ::type* = nullptr> + typename T::image_type render(T const & renderer) + { + if (tiles_.width == 1 && tiles_.height == 1) + { + return renderer.render(map_, scale_factor_); + } + else + { + return renderer.render(map_, scale_factor_, tiles_); + } + } + + template ::type* = nullptr> + typename T::image_type render(T const & renderer) + { + return renderer.render(map_, scale_factor_); + } + std::string const & name_; mapnik::Map & map_; map_size const & tiles_; double scale_factor_; result_list & results_; report_type & report_; + std::size_t iterations_; }; runner::runner(runner::path_type const & styles_dir, runner::path_type const & output_dir, runner::path_type const & reference_dir, bool overwrite, + std::size_t iterations, std::size_t jobs) : styles_dir_(styles_dir), output_dir_(output_dir), reference_dir_(reference_dir), jobs_(jobs), + iterations_(iterations), renderers_{ renderer(output_dir_, reference_dir_, overwrite) #if defined(HAVE_CAIRO) ,renderer(output_dir_, reference_dir_, overwrite) @@ -286,7 +317,7 @@ result_list runner::test_one(runner::path_type const& style_path, config cfg, re { map.zoom_all(); } - mapnik::util::apply_visitor(renderer_visitor(name, map, tiles_count, scale_factor, results, report), ren); + mapnik::util::apply_visitor(renderer_visitor(name, map, tiles_count, scale_factor, results, report, iterations_), ren); } } } diff --git a/test/visual/runner.hpp b/test/visual/runner.hpp index 77949e46e..a4c91baef 100644 --- a/test/visual/runner.hpp +++ b/test/visual/runner.hpp @@ -54,6 +54,7 @@ public: path_type const & output_dir, path_type const & reference_dir, bool overwrite, + std::size_t iterations, std::size_t jobs); result_list test_all(report_type & report) const; @@ -70,6 +71,7 @@ private: const path_type output_dir_; const path_type reference_dir_; const std::size_t jobs_; + const std::size_t iterations_; const renderer_type renderers_[boost::mpl::size::value]; }; From 2fd1525269b696f99c4ce925c97123049187a245 Mon Sep 17 00:00:00 2001 From: artemp Date: Tue, 7 Jul 2015 15:02:13 +0200 Subject: [PATCH 042/165] remove redundant check --- include/mapnik/offset_converter.hpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/include/mapnik/offset_converter.hpp b/include/mapnik/offset_converter.hpp index 674562137..4a1fabf3b 100644 --- a/include/mapnik/offset_converter.hpp +++ b/include/mapnik/offset_converter.hpp @@ -322,14 +322,11 @@ private: { is_polygon = true; close_points.push_back(vertex2d(v1.x, v1.y, v1.cmd)); - if (points.size() > 0) + auto & prev = points.back(); + if (prev.x == start.x && prev.y == start.y) { - auto & prev = points.back(); - if (prev.x == start.x && prev.y == start.y) - { - prev.cmd = SEG_CLOSE; // account for dupes (line_to(move_to) + close_path) in agg poly clipper - continue; - } + prev.cmd = SEG_CLOSE; // account for dupes (line_to(move_to) + close_path) in agg poly clipper + continue; } } else if (v0.cmd == SEG_MOVETO) From 0a484bf784c1ca17c5d20a50c9ab131dfc70968b Mon Sep 17 00:00:00 2001 From: artemp Date: Tue, 7 Jul 2015 18:37:27 +0200 Subject: [PATCH 043/165] handle Multi-geoms + update cairo and grid renderers update --- src/agg/process_line_symbolizer.cpp | 8 ++++---- src/cairo/process_line_symbolizer.cpp | 11 ++++++++++- src/grid/process_line_symbolizer.cpp | 12 ++++++++++-- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/agg/process_line_symbolizer.cpp b/src/agg/process_line_symbolizer.cpp index 2eb476f95..b3a01c7e8 100644 --- a/src/agg/process_line_symbolizer.cpp +++ b/src/agg/process_line_symbolizer.cpp @@ -173,9 +173,9 @@ void agg_renderer::process(line_symbolizer const& sym, if (clip) { geometry::geometry_types type = geometry::geometry_type(feature.get_geometry()); - if (type == geometry::geometry_types::Polygon) + if (type == geometry::geometry_types::Polygon || type == geometry::geometry_types::MultiPolygon) converter.template set(); - else if (type == geometry::geometry_types::LineString) + else if (type == geometry::geometry_types::LineString || type == geometry::geometry_types::MultiLineString) converter.template set(); } converter.set(); // always transform @@ -200,9 +200,9 @@ void agg_renderer::process(line_symbolizer const& sym, if (clip) { geometry::geometry_types type = geometry::geometry_type(feature.get_geometry()); - if (type == geometry::geometry_types::Polygon) + if (type == geometry::geometry_types::Polygon || type == geometry::geometry_types::MultiPolygon) converter.template set(); - else if (type == geometry::geometry_types::LineString) + else if (type == geometry::geometry_types::LineString || type == geometry::geometry_types::MultiLineString) converter.template set(); } converter.set(); // always transform diff --git a/src/cairo/process_line_symbolizer.cpp b/src/cairo/process_line_symbolizer.cpp index 1510547e4..ed64d5c01 100644 --- a/src/cairo/process_line_symbolizer.cpp +++ b/src/cairo/process_line_symbolizer.cpp @@ -29,6 +29,7 @@ #include #include #include +#include namespace mapnik { @@ -82,6 +83,7 @@ void cairo_renderer::process(line_symbolizer const& sym, clipping_extent.pad(padding); } using vertex_converter_type = vertex_converter::process(line_symbolizer const& sym, vertex_converter_type converter(clipping_extent,sym,common_.t_,prj_trans,tr,feature,common_.vars_,common_.scale_factor_); - if (clip) converter.set(); // optional clip (default: true) + if (clip) + { + geometry::geometry_types type = geometry::geometry_type(feature.get_geometry()); + if (type == geometry::geometry_types::Polygon || type == geometry::geometry_types::MultiPolygon) + converter.template set(); + else if (type == geometry::geometry_types::LineString || type == geometry::geometry_types::MultiLineString) + converter.template set(); + } converter.set(); // always transform if (std::fabs(offset) > 0.0) converter.set(); // parallel offset converter.set(); // optional affine transform diff --git a/src/grid/process_line_symbolizer.cpp b/src/grid/process_line_symbolizer.cpp index 0dc6f3f6a..c569f6b2c 100644 --- a/src/grid/process_line_symbolizer.cpp +++ b/src/grid/process_line_symbolizer.cpp @@ -31,6 +31,7 @@ #include #include #include +#include // agg #include "agg_rasterizer_scanline_aa.h" #include "agg_renderer_scanline.h" @@ -89,14 +90,21 @@ void grid_renderer::process(line_symbolizer const& sym, padding *= common_.scale_factor_; clipping_extent.pad(padding); } - using vertex_converter_type = vertex_converter; vertex_converter_type converter(clipping_extent,sym,common_.t_,prj_trans,tr,feature,common_.vars_,common_.scale_factor_); - if (clip) converter.set(); // optional clip (default: true) + if (clip) + { + geometry::geometry_types type = geometry::geometry_type(feature.get_geometry()); + if (type == geometry::geometry_types::Polygon || type == geometry::geometry_types::MultiPolygon) + converter.template set(); + else if (type == geometry::geometry_types::LineString || type == geometry::geometry_types::MultiLineString) + converter.template set(); + } converter.set(); // always transform if (std::fabs(offset) > 0.0) converter.set(); // parallel offset converter.set(); // optional affine transform From 73cae70a41b7625da285d5fb95f657f2f9a0ba03 Mon Sep 17 00:00:00 2001 From: artemp Date: Tue, 7 Jul 2015 18:38:25 +0200 Subject: [PATCH 044/165] correctly populate `close_points` when skipping duplicate (coincident vertices) --- include/mapnik/offset_converter.hpp | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/include/mapnik/offset_converter.hpp b/include/mapnik/offset_converter.hpp index 4a1fabf3b..51d43e347 100644 --- a/include/mapnik/offset_converter.hpp +++ b/include/mapnik/offset_converter.hpp @@ -306,41 +306,44 @@ private: bool is_polygon = false; std::size_t cpt = 0; v0.cmd = geom_.vertex(&v0.x, &v0.y); - v1.x = v0.x; - v1.y = v0.y; - v1.cmd = v0.cmd; + v1 = v0; // PUSH INITIAL - points.push_back(vertex2d(v0.x, v0.y, v0.cmd)); + points.push_back(v0); if (v0.cmd == SEG_END) // not enough vertices in source { return status_ = process; } - + start = v0; while ((v0.cmd = geom_.vertex(&v0.x, &v0.y)) != SEG_END) { if (v0.cmd == SEG_CLOSE) { is_polygon = true; - close_points.push_back(vertex2d(v1.x, v1.y, v1.cmd)); auto & prev = points.back(); if (prev.x == start.x && prev.y == start.y) { + prev.x = v0.x; // hack + prev.y = v0.y; prev.cmd = SEG_CLOSE; // account for dupes (line_to(move_to) + close_path) in agg poly clipper + std::size_t size = points.size(); + if (size > 1) close_points.push_back(points[size - 2]); + else close_points.push_back(prev); continue; } + else + { + close_points.push_back(v1); + } } else if (v0.cmd == SEG_MOVETO) { start = v0; } - v1.x = v0.x; - v1.y = v0.y; - v1.cmd = v0.cmd; - points.push_back(vertex2d(v0.x, v0.y, v0.cmd)); + v1 = v0; + points.push_back(v0); } // Push SEG_END - points.push_back(vertex2d(v0.x, v0.y, v0.cmd)); - + points.push_back(vertex2d(v0.x,v0.y,SEG_END)); std::size_t i = 0; v1 = points[i++]; v2 = points[i++]; From b1e4f0ea2393602b5556338468ff3b9ad140a069 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 7 Jul 2015 11:57:52 -0700 Subject: [PATCH 045/165] update visual tests after #2955 --- test/data-visual | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/data-visual b/test/data-visual index a83ea35fe..a4038f4b0 160000 --- a/test/data-visual +++ b/test/data-visual @@ -1 +1 @@ -Subproject commit a83ea35fec924a5a749e591909f4fd20a9d989c1 +Subproject commit a4038f4b09901ebff4d58fa8593f5df1a78c1abe From 8511865f60b35ec99109b3221df0b7c73ba3ffc7 Mon Sep 17 00:00:00 2001 From: Blake Thompson Date: Tue, 7 Jul 2015 14:41:34 -0500 Subject: [PATCH 046/165] Updated visual tests for python --- test/data-visual | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/data-visual b/test/data-visual index a4038f4b0..7de318312 160000 --- a/test/data-visual +++ b/test/data-visual @@ -1 +1 @@ -Subproject commit a4038f4b09901ebff4d58fa8593f5df1a78c1abe +Subproject commit 7de3183129a08c835a3205f5d51c85e59494ee33 From e6891a03e66a2e371bdb78161cf0d35d5d960ac1 Mon Sep 17 00:00:00 2001 From: Blake Thompson Date: Tue, 7 Jul 2015 14:42:22 -0500 Subject: [PATCH 047/165] Changed release date of CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4acdcb927..5270e0392 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ For a complete change history, see the git log. ## 3.0.0 -Released: June 30th, 2015 +Released: July 7th, 2015 (Packaged from ...) From 1c56214bbbcf3dc5d3e9bed1810d8279ecfad9ec Mon Sep 17 00:00:00 2001 From: Blake Thompson Date: Tue, 7 Jul 2015 14:50:42 -0500 Subject: [PATCH 048/165] update CHANGELOG for mapnik v3.0.0 release --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5270e0392..2cdd960b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ For a complete change history, see the git log. Released: July 7th, 2015 -(Packaged from ...) +(Packaged from e6891a0) #### Summary From d144f97f4e5c667b86c16ee4bfc0113d00afa81e Mon Sep 17 00:00:00 2001 From: Paul Ramsey Date: Wed, 8 Jul 2015 08:29:53 -0700 Subject: [PATCH 049/165] Render NODATA as transparent and clip highbit data ala GDAL for greyscale outputs. Addresses #2661 --- plugins/input/pgraster/pgraster_wkb_reader.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/plugins/input/pgraster/pgraster_wkb_reader.cpp b/plugins/input/pgraster/pgraster_wkb_reader.cpp index 0320fe106..87f71c093 100644 --- a/plugins/input/pgraster/pgraster_wkb_reader.cpp +++ b/plugins/input/pgraster/pgraster_wkb_reader.cpp @@ -271,18 +271,30 @@ mapnik::raster_ptr read_grayscale_band(mapnik::box2d const& bbox, int val; + int nodataval; uint8_t * data = image.bytes(); int ps = 4; // sizeof(image::pixel_type) int off; - val = reader(); // nodata value, need to read anyway + nodataval = reader(); // nodata value, need to read anyway for (int y=0; y 255 ) val = 255; + // Calculate pixel offset off = y * width * ps + x * ps; - // Pixel space is RGBA + // Pixel space is RGBA, fill all w/ same value for Grey data[off+0] = val; data[off+1] = val; data[off+2] = val; + // Set the alpha channel for transparent nodata values + // Nodata handling is *manual* at the driver level + if ( hasnodata && val == nodataval ) { + data[off+3] = 0x00; // transparent + } else { + data[off+3] = 0xFF; // opaque + } } } mapnik::raster_ptr raster = std::make_shared(bbox, image, 1.0); From ced3d35023b148f57388be9f9b71e0a1efe18676 Mon Sep 17 00:00:00 2001 From: Jiri Drbalek Date: Wed, 8 Jul 2015 20:01:23 +0000 Subject: [PATCH 050/165] visual tests: report total time spent by renderers --- test/visual/report.cpp | 30 ++++++++++++++++++++++++++++++ test/visual/report.hpp | 2 +- test/visual/run.cpp | 3 +-- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/test/visual/report.cpp b/test/visual/report.cpp index 105ba06d0..deaf5b9c5 100644 --- a/test/visual/report.cpp +++ b/test/visual/report.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include "report.hpp" @@ -70,6 +71,10 @@ unsigned console_report::summary(result_list const & results) unsigned fail = 0; unsigned overwrite = 0; + using namespace std::chrono; + using duration_map_type = std::map; + duration_map_type durations; + for (auto const & r : results) { switch (r.state) @@ -79,12 +84,37 @@ unsigned console_report::summary(result_list const & results) case STATE_ERROR: error++; break; case STATE_OVERWRITE: overwrite++; break; } + + if (show_duration) + { + duration_map_type::iterator duration = durations.find(r.renderer_name); + if (duration == durations.end()) + { + durations.emplace(r.renderer_name, r.duration); + } + else + { + duration->second += r.duration; + } + } } s << std::endl; s << "Visual rendering: " << fail << " failed / " << ok << " passed / " << overwrite << " overwritten / " << error << " errors" << std::endl; + if (show_duration) + { + high_resolution_clock::duration total(0); + for (auto const & duration : durations) + { + s << duration.first << ": \t" << duration_cast(duration.second).count() + << " milliseconds" << std::endl; + total += duration.second; + } + s << "total: \t" << duration_cast(total).count() << " milliseconds" << std::endl; + } + return fail + error; } diff --git a/test/visual/report.hpp b/test/visual/report.hpp index 1a7a3c4e7..837ed5e95 100644 --- a/test/visual/report.hpp +++ b/test/visual/report.hpp @@ -55,7 +55,7 @@ protected: class console_short_report : public console_report { public: - console_short_report() : console_report(false) + console_short_report(bool _show_duration) : console_report(_show_duration) { } diff --git a/test/visual/run.cpp b/test/visual/run.cpp index 444486905..62ca73b0e 100644 --- a/test/visual/run.cpp +++ b/test/visual/run.cpp @@ -113,8 +113,7 @@ int main(int argc, char** argv) vm["iterations"].as(), vm["jobs"].as()); bool show_duration = vm.count("duration"); - bool verbose = vm.count("verbose") | show_duration; - report_type report(verbose ? report_type((console_report(show_duration))) : report_type((console_short_report()))); + report_type report(vm.count("verbose") ? report_type((console_report(show_duration))) : report_type((console_short_report(show_duration)))); result_list results; try From 784bd8d19428d69fd44a376cbd20d339f29ca01f Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 8 Jul 2015 17:24:05 -0700 Subject: [PATCH 051/165] use correct include type in scrptrun.hpp --- include/mapnik/text/scrptrun.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/mapnik/text/scrptrun.hpp b/include/mapnik/text/scrptrun.hpp index e4c607f6c..67942b0a8 100644 --- a/include/mapnik/text/scrptrun.hpp +++ b/include/mapnik/text/scrptrun.hpp @@ -17,9 +17,9 @@ #ifndef __SCRPTRUN_H #define __SCRPTRUN_H -#include "unicode/utypes.h" -#include "unicode/uobject.h" -#include "unicode/uscript.h" +#include +#include +#include struct ScriptRecord { From a6a4f1eb2eb4ac0ab5d7d5cd0d5fd38d4dbc0658 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 8 Jul 2015 23:27:41 -0700 Subject: [PATCH 052/165] fix compile of dormant icu_shaper --- include/mapnik/text/icu_shaper.hpp | 35 +++++++++++++++++------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/include/mapnik/text/icu_shaper.hpp b/include/mapnik/text/icu_shaper.hpp index dedeafd81..bcd822b27 100644 --- a/include/mapnik/text/icu_shaper.hpp +++ b/include/mapnik/text/icu_shaper.hpp @@ -27,6 +27,10 @@ #include #include #include +#include +#include +#include +#include #include // stl @@ -59,12 +63,11 @@ static void shape_text(text_line & line, UErrorCode err = U_ZERO_ERROR; mapnik::value_unicode_string shaped; mapnik::value_unicode_string reordered; - unsigned char_index = 0; for (auto const& text_item : list) { - face_set_ptr face_set = font_manager.get_face_set(text_item.format->face_name, text_item.format->fontset); - double size = text_item.format->text_size * scale_factor; + face_set_ptr face_set = font_manager.get_face_set(text_item.format_->face_name, text_item.format_->fontset); + double size = text_item.format_->text_size * scale_factor; face_set->set_unscaled_character_sizes(); for (auto const& face : *face_set) { @@ -87,33 +90,35 @@ static void shape_text(text_line & line, std::size_t num_chars = static_cast(num_char); shaped.releaseBuffer(length); bool shaped_status = true; + double max_glyph_height = 0; if (U_SUCCESS(err) && (num_chars == length)) { + unsigned char_index = 0; U_NAMESPACE_QUALIFIER StringCharacterIterator iter(shaped); for (iter.setToStart(); iter.hasNext();) { UChar ch = iter.nextPostInc(); - glyph_info tmp; - tmp.glyph_index = FT_Get_Char_Index(face->get_face(), ch); - tmp.offset.clear(); - if (tmp.glyph_index == 0) + auto codepoint = FT_Get_Char_Index(face->get_face(), ch); + glyph_info g(codepoint,char_index,text_item.format_); + //g.offset.clear(); + if (g.glyph_index == 0) { shaped_status = false; break; } - if (face->glyph_dimensions(tmp)) + if (face->glyph_dimensions(g)) { - tmp.char_index = char_index; - tmp.face = face; - tmp.format = text_item.format; - tmp.scale_multiplier = size / face->get_face()->units_per_EM; - width_map[char_index++] += tmp.advance(); - line.add_glyph(tmp, scale_factor); + g.face = face; + g.scale_multiplier = size / face->get_face()->units_per_EM; + double tmp_height = g.height(); + if (tmp_height > max_glyph_height) max_glyph_height = tmp_height; + width_map[char_index++] += g.advance(); + line.add_glyph(std::move(g), scale_factor); } } } if (!shaped_status) continue; - line.update_max_char_height(face->get_char_height(size)); + line.update_max_char_height(max_glyph_height); return; } } From 153051d020e20a45dc7b8b5daa0558c0488b8d62 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 8 Jul 2015 23:28:38 -0700 Subject: [PATCH 053/165] add test to ensure icu/harfbuzz shapers can be compiled --- include/mapnik/text/face.hpp | 2 +- include/mapnik/text/harfbuzz_shaper.hpp | 5 +++++ include/mapnik/text/itemizer.hpp | 5 +++-- include/mapnik/text/text_line.hpp | 3 ++- test/unit/text/shaping.cpp | 24 ++++++++++++++++++++++++ 5 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 test/unit/text/shaping.cpp diff --git a/include/mapnik/text/face.hpp b/include/mapnik/text/face.hpp index 9c50bebcc..60a37e692 100644 --- a/include/mapnik/text/face.hpp +++ b/include/mapnik/text/face.hpp @@ -44,7 +44,7 @@ extern "C" namespace mapnik { -class font_face : util::noncopyable +class MAPNIK_DECL font_face : util::noncopyable { public: font_face(FT_Face face); diff --git a/include/mapnik/text/harfbuzz_shaper.hpp b/include/mapnik/text/harfbuzz_shaper.hpp index 8c4b8309f..ac436a166 100644 --- a/include/mapnik/text/harfbuzz_shaper.hpp +++ b/include/mapnik/text/harfbuzz_shaper.hpp @@ -28,7 +28,9 @@ #include #include #include +#include #include +#include // stl #include @@ -38,6 +40,9 @@ #include #include +// icu +#include + namespace mapnik { diff --git a/include/mapnik/text/itemizer.hpp b/include/mapnik/text/itemizer.hpp index fe036ad24..4f11bba57 100644 --- a/include/mapnik/text/itemizer.hpp +++ b/include/mapnik/text/itemizer.hpp @@ -27,6 +27,7 @@ #include #include #include +#include // stl #include @@ -41,7 +42,7 @@ namespace mapnik { -struct text_item : util::noncopyable +struct MAPNIK_DECL text_item : util::noncopyable { text_item(unsigned s, unsigned e, @@ -71,7 +72,7 @@ struct text_item : util::noncopyable // - format // - script (http://en.wikipedia.org/wiki/Scripts_in_Unicode) -class text_itemizer +class MAPNIK_DECL text_itemizer { public: text_itemizer(); diff --git a/include/mapnik/text/text_line.hpp b/include/mapnik/text/text_line.hpp index 1bd02db66..a00fb79cf 100644 --- a/include/mapnik/text/text_line.hpp +++ b/include/mapnik/text/text_line.hpp @@ -25,6 +25,7 @@ //stl #include #include +#include namespace mapnik { @@ -35,7 +36,7 @@ struct glyph_info; // It can be used for rendering but no text processing (like line breaking) // should be done! -class text_line : util::noncopyable +class MAPNIK_DECL text_line : util::noncopyable { public: using glyph_vector = std::vector; diff --git a/test/unit/text/shaping.cpp b/test/unit/text/shaping.cpp new file mode 100644 index 000000000..2e2701b8f --- /dev/null +++ b/test/unit/text/shaping.cpp @@ -0,0 +1,24 @@ +#include "catch.hpp" +#include +#include +#include + +TEST_CASE("shapers compile") { + + mapnik::text_line line(0,0); + mapnik::text_itemizer itemizer; + std::map width_map; + double scale_factor = 1; + mapnik::font_library fl; + mapnik::freetype_engine::font_file_mapping_type font_file_mapping; + mapnik::freetype_engine::font_memory_cache_type font_memory_cache; + mapnik::face_manager fm(fl,font_file_mapping,font_memory_cache); + mapnik::harfbuzz_shaper::shape_text(line,itemizer, + width_map, + fm, + scale_factor); + mapnik::icu_shaper::shape_text(line,itemizer, + width_map, + fm, + scale_factor); +} \ No newline at end of file From 6325bf21cdb91638d423d19ee2c2dc0fd89e4d94 Mon Sep 17 00:00:00 2001 From: artemp Date: Fri, 10 Jul 2015 09:44:00 +0200 Subject: [PATCH 054/165] geojson : correct parsing feature boundaries ref #2964 --- include/mapnik/json/extract_bounding_box_grammar_impl.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/mapnik/json/extract_bounding_box_grammar_impl.hpp b/include/mapnik/json/extract_bounding_box_grammar_impl.hpp index c6fd321ba..c497669a2 100644 --- a/include/mapnik/json/extract_bounding_box_grammar_impl.hpp +++ b/include/mapnik/json/extract_bounding_box_grammar_impl.hpp @@ -78,6 +78,8 @@ extract_bounding_box_grammar::extract_bounding_box_gramm | coords[_b = _1] | + json.string_ + | char_))][push_box(_r1, _r2, _b, _1)] ; From 16e6db3281c5ed64f89deb6c456cf398d9e3b73e Mon Sep 17 00:00:00 2001 From: artemp Date: Fri, 10 Jul 2015 11:05:48 +0200 Subject: [PATCH 055/165] add initial geojson unit test --- test/unit/datasource/geojson.cpp | 80 ++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 test/unit/datasource/geojson.cpp diff --git a/test/unit/datasource/geojson.cpp b/test/unit/datasource/geojson.cpp new file mode 100644 index 000000000..380450cb3 --- /dev/null +++ b/test/unit/datasource/geojson.cpp @@ -0,0 +1,80 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2015 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 + * + *****************************************************************************/ + +#include "catch.hpp" + +#include +#include +#include +#include + + +static const std::string geojson_plugin("./plugins/input/geojson.input"); +const bool registered = mapnik::datasource_cache::instance().register_datasources(geojson_plugin); + +TEST_CASE("geojson") { + +SECTION("json feature cache-feature=\"true\"") +{ + // check the GeoJSON datasource can be loaded + const std::vector plugin_names = + mapnik::datasource_cache::instance().plugin_names(); + const bool have_csv_plugin = + std::find(plugin_names.begin(), plugin_names.end(), "geojson") != plugin_names.end(); + // Create datasource + mapnik::parameters params; + params["type"] = "geojson"; + params["file"] = "./test/data/json/feature.json"; + params["cache-features"] = false; + auto ds = mapnik::datasource_cache::instance().create(params); + REQUIRE(bool(ds)); + auto fields = ds->get_descriptor().get_descriptors(); + mapnik::query query(ds->envelope()); + for (auto const &field : fields) + { + query.add_property_name(field.get_name()); + } + auto features = ds->features(query); + auto feature = features->next(); + REQUIRE(feature != nullptr); +} + +SECTION("json feature cache-feature=\"false\"") +{ + mapnik::parameters params; + params["type"] = "geojson"; + params["file"] = "./test/data/json/feature.json"; + params["cache-features"] = false; + auto ds = mapnik::datasource_cache::instance().create(params); + REQUIRE(bool(ds)); + auto fields = ds->get_descriptor().get_descriptors(); + mapnik::query query(ds->envelope()); + for (auto const &field : fields) + { + query.add_property_name(field.get_name()); + } + auto features = ds->features(query); + auto feature = features->next(); + REQUIRE(feature != nullptr); +} + +} From 8ab51254315b1de22e623e512782d8ff5b11a177 Mon Sep 17 00:00:00 2001 From: artemp Date: Fri, 10 Jul 2015 11:19:13 +0200 Subject: [PATCH 056/165] update submodules --- .gitmodules | 1 + test/data | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 8ca2e9845..d0ae1390c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,7 @@ [submodule "test/data"] path = test/data url = https://github.com/mapnik/test-data.git + branch = master [submodule "test/data-visual"] path = test/data-visual url = https://github.com/mapnik/test-data-visual.git diff --git a/test/data b/test/data index 2823c6a0b..bc51f1d98 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit 2823c6a0ba9e642869537d8e5c1a1ca5fd3de18a +Subproject commit bc51f1d9890b4dc5887864b00b1217c3ab904b4a From b54b2753a009629f7340324a418efbee7e44b82a Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Fri, 10 Jul 2015 12:10:46 -0700 Subject: [PATCH 057/165] fixup geojson test --- test/unit/datasource/geojson.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/test/unit/datasource/geojson.cpp b/test/unit/datasource/geojson.cpp index 380450cb3..0af5148e8 100644 --- a/test/unit/datasource/geojson.cpp +++ b/test/unit/datasource/geojson.cpp @@ -35,16 +35,11 @@ TEST_CASE("geojson") { SECTION("json feature cache-feature=\"true\"") { - // check the GeoJSON datasource can be loaded - const std::vector plugin_names = - mapnik::datasource_cache::instance().plugin_names(); - const bool have_csv_plugin = - std::find(plugin_names.begin(), plugin_names.end(), "geojson") != plugin_names.end(); // Create datasource mapnik::parameters params; params["type"] = "geojson"; params["file"] = "./test/data/json/feature.json"; - params["cache-features"] = false; + params["cache-features"] = true; auto ds = mapnik::datasource_cache::instance().create(params); REQUIRE(bool(ds)); auto fields = ds->get_descriptor().get_descriptors(); From 0145476ff72bceeab65f9302ad9117a29b885294 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Fri, 10 Jul 2015 12:34:18 -0700 Subject: [PATCH 058/165] only run geojson unit test if plugin was built --- test/unit/datasource/geojson.cpp | 82 ++++++++++++++++---------------- 1 file changed, 42 insertions(+), 40 deletions(-) diff --git a/test/unit/datasource/geojson.cpp b/test/unit/datasource/geojson.cpp index 0af5148e8..ae49b47e4 100644 --- a/test/unit/datasource/geojson.cpp +++ b/test/unit/datasource/geojson.cpp @@ -26,50 +26,52 @@ #include #include #include - - -static const std::string geojson_plugin("./plugins/input/geojson.input"); -const bool registered = mapnik::datasource_cache::instance().register_datasources(geojson_plugin); +#include TEST_CASE("geojson") { -SECTION("json feature cache-feature=\"true\"") -{ - // Create datasource - mapnik::parameters params; - params["type"] = "geojson"; - params["file"] = "./test/data/json/feature.json"; - params["cache-features"] = true; - auto ds = mapnik::datasource_cache::instance().create(params); - REQUIRE(bool(ds)); - auto fields = ds->get_descriptor().get_descriptors(); - mapnik::query query(ds->envelope()); - for (auto const &field : fields) + std::string geojson_plugin("./plugins/input/geojson.input"); + if (mapnik::util::exists(geojson_plugin)) { - query.add_property_name(field.get_name()); - } - auto features = ds->features(query); - auto feature = features->next(); - REQUIRE(feature != nullptr); -} + mapnik::datasource_cache::instance().register_datasources("plugins/input/geojson.input"); + SECTION("json feature cache-feature=\"true\"") + { + // Create datasource + mapnik::parameters params; + params["type"] = "geojson"; + params["file"] = "./test/data/json/feature.json"; + params["cache-features"] = true; + auto ds = mapnik::datasource_cache::instance().create(params); + REQUIRE(bool(ds)); + auto fields = ds->get_descriptor().get_descriptors(); + mapnik::query query(ds->envelope()); + for (auto const &field : fields) + { + query.add_property_name(field.get_name()); + } + auto features = ds->features(query); + auto feature = features->next(); + REQUIRE(feature != nullptr); + } -SECTION("json feature cache-feature=\"false\"") -{ - mapnik::parameters params; - params["type"] = "geojson"; - params["file"] = "./test/data/json/feature.json"; - params["cache-features"] = false; - auto ds = mapnik::datasource_cache::instance().create(params); - REQUIRE(bool(ds)); - auto fields = ds->get_descriptor().get_descriptors(); - mapnik::query query(ds->envelope()); - for (auto const &field : fields) - { - query.add_property_name(field.get_name()); - } - auto features = ds->features(query); - auto feature = features->next(); - REQUIRE(feature != nullptr); -} + SECTION("json feature cache-feature=\"false\"") + { + mapnik::parameters params; + params["type"] = "geojson"; + params["file"] = "./test/data/json/feature.json"; + params["cache-features"] = false; + auto ds = mapnik::datasource_cache::instance().create(params); + REQUIRE(bool(ds)); + auto fields = ds->get_descriptor().get_descriptors(); + mapnik::query query(ds->envelope()); + for (auto const &field : fields) + { + query.add_property_name(field.get_name()); + } + auto features = ds->features(query); + auto feature = features->next(); + REQUIRE(feature != nullptr); + } + } } From 15af190c7799f172daf119d462aaafb70a0f10a4 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Fri, 10 Jul 2015 12:56:10 -0700 Subject: [PATCH 059/165] add #2964 to changelog [skip ci] --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2cdd960b0..fd987f58c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ Developers: Please commit along with changes. For a complete change history, see the git log. +## Future + +- Fixed parsing of GeoJSON when properties contained `{}` (#2964) + ## 3.0.0 Released: July 7th, 2015 From c2295755f6d6020374918031accb397b2cfe7eb1 Mon Sep 17 00:00:00 2001 From: artemp Date: Mon, 13 Jul 2015 13:48:34 +0200 Subject: [PATCH 060/165] fix typo --- src/color_factory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/color_factory.cpp b/src/color_factory.cpp index d13a89562..dc54e89e5 100644 --- a/src/color_factory.cpp +++ b/src/color_factory.cpp @@ -45,7 +45,7 @@ color parse_color(std::string const& str) } else { - throw config_error("Failed to a parse color: \"" + str + "\""); + throw config_error("Failed to parse color: \"" + str + "\""); } } From 6d6cb15b4547539152c488fb7bfe7f9506c48d9a Mon Sep 17 00:00:00 2001 From: artemp Date: Tue, 14 Jul 2015 11:53:50 +0200 Subject: [PATCH 061/165] skewX/skewY - add input validation ref https://github.com/mapbox/mapbox-studio/issues/1409 * convert input angle into -90,90 range * clamp to -89,89 range --- include/mapnik/transform_processor.hpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/include/mapnik/transform_processor.hpp b/include/mapnik/transform_processor.hpp index fd45ecc38..5bd25ee3b 100644 --- a/include/mapnik/transform_processor.hpp +++ b/include/mapnik/transform_processor.hpp @@ -31,6 +31,10 @@ #include // agg #include +// boost +#include +// stl +#include namespace mapnik { @@ -149,13 +153,15 @@ struct transform_processor void operator() (skewX_node const& node) { - double angle = deg2rad(eval(node.angle_)); + auto degrees = std::fmod(eval(node.angle_),90.0); + auto angle = deg2rad(boost::algorithm::clamp(degrees, -89.0, 89.0)); transform_.multiply(agg::trans_affine_skewing(angle, 0.0)); } void operator() (skewY_node const& node) { - double angle = deg2rad(eval(node.angle_)); + auto degrees = std::fmod(eval(node.angle_),90.0); + auto angle = deg2rad(boost::algorithm::clamp(degrees, -89.0, 89.0)); transform_.multiply(agg::trans_affine_skewing(0.0, angle)); } From a46af7621685de1fabd44b7b5a030bfec8988c2e Mon Sep 17 00:00:00 2001 From: artemp Date: Tue, 14 Jul 2015 19:45:50 +0200 Subject: [PATCH 062/165] initial commit - make parse and parse_from_string return boolean ,collect error messages --- include/mapnik/svg/svg_parser.hpp | 7 ++- include/mapnik/svg/svg_parser_exception.hpp | 54 ++++++++++++++++++++ src/marker_cache.cpp | 10 +++- src/svg/svg_parser.cpp | 56 +++++++++++++-------- 4 files changed, 103 insertions(+), 24 deletions(-) create mode 100644 include/mapnik/svg/svg_parser_exception.hpp diff --git a/include/mapnik/svg/svg_parser.hpp b/include/mapnik/svg/svg_parser.hpp index ef9eafc06..63caacd8b 100644 --- a/include/mapnik/svg/svg_parser.hpp +++ b/include/mapnik/svg/svg_parser.hpp @@ -38,15 +38,18 @@ namespace mapnik { namespace svg { class MAPNIK_DECL svg_parser : private util::noncopyable { + using error_message_container = std::vector ; public: explicit svg_parser(svg_converter_type & path); ~svg_parser(); - void parse(std::string const& filename); - void parse_from_string(std::string const& svg); + error_message_container const& error_messages() const; + bool parse(std::string const& filename); + bool parse_from_string(std::string const& svg); svg_converter_type & path_; bool is_defs_; std::map gradient_map_; std::pair temporary_gradient_; + error_message_container error_messages_; }; }} diff --git a/include/mapnik/svg/svg_parser_exception.hpp b/include/mapnik/svg/svg_parser_exception.hpp new file mode 100644 index 000000000..89139da88 --- /dev/null +++ b/include/mapnik/svg/svg_parser_exception.hpp @@ -0,0 +1,54 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2015 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_SVG_PARSER_EXCEPTION_HPP +#define MAPNIK_SVG_PARSER_EXCEPTION_HPP + +// mapnik +#include +#include + +// stl +#include + +namespace mapnik { namespace svg { + +class MAPNIK_DECL svg_parser_exception : public std::exception +{ +public: + svg_parser_exception(std::string const& message) + : message_(message) {} + + ~svg_parser_exception() throw() {} + + virtual const char* what() const throw() + { + return message_.c_str(); + } +private: + std::string message_; +}; + +}} + + +#endif // MAPNIK_SVG_PARSER_EXCEPTION_HPP diff --git a/src/marker_cache.cpp b/src/marker_cache.cpp index 3f8f9396b..6dbd6f5cb 100644 --- a/src/marker_cache.cpp +++ b/src/marker_cache.cpp @@ -175,7 +175,15 @@ std::shared_ptr marker_cache::find(std::string const& uri, 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); + + if (!p.parse_from_string(known_svg_string)) + { + for (auto const& msg : p.error_messages()) + { + std::cerr << "SVG PARSING ERROR:\"" << msg << "\"" << std::endl; + } + return std::make_shared(mapnik::marker_null()); + } //svg.arrange_orientations(); double lox,loy,hix,hiy; svg.bounding_rect(&lox, &loy, &hix, &hiy); diff --git a/src/svg/svg_parser.cpp b/src/svg/svg_parser.cpp index 1ef103c3b..c1f6ad49a 100644 --- a/src/svg/svg_parser.cpp +++ b/src/svg/svg_parser.cpp @@ -26,7 +26,7 @@ #include #include #include - +#include #include "agg_ellipse.h" #include "agg_rounded_rect.h" #include "agg_span_gradient.h" @@ -99,7 +99,8 @@ struct key_value_sequence_ordered qi::rule key, value; }; -agg::rgba8 parse_color(const char* str) +template +agg::rgba8 parse_color(T & error_messages, const char* str) { mapnik::color c(100,100,100); try @@ -108,7 +109,7 @@ agg::rgba8 parse_color(const char* str) } catch (mapnik::config_error const& ex) { - MAPNIK_LOG_ERROR(svg_parser) << ex.what(); + error_messages.emplace_back(ex.what()); } return agg::rgba8(c.red(), c.green(), c.blue(), c.alpha()); } @@ -330,12 +331,14 @@ void parse_attr(svg_parser & parser, const xmlChar * name, const xmlChar * value } else { - MAPNIK_LOG_ERROR(svg_parser) << "Failed to find gradient fill: " << id; + std::stringstream ss; + ss << "Failed to find gradient fill: " << id; + parser.error_messages_.push_back(ss.str()); } } else { - parser.path_.fill(parse_color((const char*) value)); + parser.path_.fill(parse_color(parser.error_messages_, (const char*) value)); } } else if (xmlStrEqual(name, BAD_CAST "fill-opacity")) @@ -367,12 +370,14 @@ void parse_attr(svg_parser & parser, const xmlChar * name, const xmlChar * value } else { - MAPNIK_LOG_ERROR(svg_parser) << "Failed to find gradient fill: " << id; + std::stringstream ss; + ss << "Failed to find gradient fill: " << id; + parser.error_messages_.push_back(ss.str()); } } else { - parser.path_.stroke(parse_color((const char*) value)); + parser.path_.stroke(parse_color(parser.error_messages_, (const char*) value)); } } else if (xmlStrEqual(name, BAD_CAST "stroke-width")) @@ -519,7 +524,6 @@ void parse_path(svg_parser & parser, xmlTextReaderPtr reader) void parse_polygon(svg_parser & parser, xmlTextReaderPtr reader) { xmlChar *value; - value = xmlTextReaderGetAttribute(reader, BAD_CAST "points"); if (value) { @@ -815,7 +819,7 @@ void parse_gradient_stop(svg_parser & parser, xmlTextReaderPtr reader) } catch (mapnik::config_error const& ex) { - MAPNIK_LOG_ERROR(svg_parser) << ex.what(); + parser.error_messages_.emplace_back(ex.what()); } } else if (kv.first == "stop-opacity") @@ -835,7 +839,7 @@ void parse_gradient_stop(svg_parser & parser, xmlTextReaderPtr reader) } catch (mapnik::config_error const& ex) { - MAPNIK_LOG_ERROR(svg_parser) << ex.what(); + parser.error_messages_.emplace_back(ex.what()); } xmlFree(value); } @@ -893,7 +897,9 @@ bool parse_common_gradient(svg_parser & parser, xmlTextReaderPtr reader) } else { - MAPNIK_LOG_ERROR(svg_parser) << "Failed to find linked gradient " << linkid; + std::stringstream ss; + ss << "Failed to find linked gradient " << linkid; + parser.error_messages_.push_back(ss.str()); } } xmlFree(value); @@ -1062,32 +1068,40 @@ svg_parser::svg_parser(svg_converter(svg.size()),nullptr,nullptr, (XML_PARSE_NOBLANKS | XML_PARSE_NOCDATA | XML_PARSE_NOERROR | XML_PARSE_NOWARNING)); - if (reader == nullptr) + if (reader == nullptr ||!parse_reader(*this,reader)) { - MAPNIK_LOG_ERROR(svg_parser) << "Unable to parse '" << svg << "'"; - } - else if (!parse_reader(*this,reader)) - { - MAPNIK_LOG_ERROR(svg_parser) << "Unable to parse '" << svg << "'"; + std::stringstream ss; + ss << "Unable to parse '" << svg << "'"; + error_messages_.push_back(ss.str()); } + return error_messages_.empty() ? true : false; } +svg_parser::error_message_container const& svg_parser::error_messages() const +{ + return error_messages_; +} }} From ec68559d3d9568788435e63f7d9837eb5540d442 Mon Sep 17 00:00:00 2001 From: artemp Date: Tue, 14 Jul 2015 19:54:41 +0200 Subject: [PATCH 063/165] fix stderr --- src/marker_cache.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/marker_cache.cpp b/src/marker_cache.cpp index 6dbd6f5cb..7b15e087a 100644 --- a/src/marker_cache.cpp +++ b/src/marker_cache.cpp @@ -215,7 +215,16 @@ std::shared_ptr marker_cache::find(std::string const& uri, svg_path_adapter svg_path(stl_storage); svg_converter_type svg(svg_path, marker_path->attributes()); svg_parser p(svg); - p.parse(uri); + + + if (!p.parse(uri)) + { + for (auto const& msg : p.error_messages()) + { + std::cerr << "SVG PARSING ERROR:\"" << msg << "\"" << std::endl; + } + return std::make_shared(mapnik::marker_null()); + } //svg.arrange_orientations(); double lox,loy,hix,hiy; svg.bounding_rect(&lox, &loy, &hix, &hiy); From 4cb2dcace2f400288f0829bafddffc2ecba44245 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 14 Jul 2015 13:29:17 -0700 Subject: [PATCH 064/165] add fix for skew (6d6cb15) to changelog [skip-ci] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd987f58c..bfad888ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ For a complete change history, see the git log. ## Future - Fixed parsing of GeoJSON when properties contained `{}` (#2964) +- Fixed potential hang due to invalid use of `line-geometry-transform` (6d6cb15) ## 3.0.0 From 8a9b46edfd95371564c8e455bed4ba1d128f9636 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 14 Jul 2015 13:34:50 -0700 Subject: [PATCH 065/165] bring in new regression tests for 6d6cb15 --- test/data-visual | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/data-visual b/test/data-visual index 7de318312..80c744348 160000 --- a/test/data-visual +++ b/test/data-visual @@ -1 +1 @@ -Subproject commit 7de3183129a08c835a3205f5d51c85e59494ee33 +Subproject commit 80c744348de8cb03a519e451129a243610e55f52 From 2b8abc4bfac6616f4027bbc4e2a504b63594515b Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 14 Jul 2015 19:19:39 -0700 Subject: [PATCH 066/165] fix #2975 - remove usage of boost/algorithm/clamp.hpp --- include/mapnik/transform_processor.hpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/include/mapnik/transform_processor.hpp b/include/mapnik/transform_processor.hpp index 5bd25ee3b..bec6bff3b 100644 --- a/include/mapnik/transform_processor.hpp +++ b/include/mapnik/transform_processor.hpp @@ -31,8 +31,7 @@ #include // agg #include -// boost -#include + // stl #include @@ -154,14 +153,18 @@ struct transform_processor void operator() (skewX_node const& node) { auto degrees = std::fmod(eval(node.angle_),90.0); - auto angle = deg2rad(boost::algorithm::clamp(degrees, -89.0, 89.0)); + if (degrees < -89.0) degrees = -89.0; + else if (degrees > 89.0) degrees = 89.0; + auto angle = deg2rad(degrees); transform_.multiply(agg::trans_affine_skewing(angle, 0.0)); } void operator() (skewY_node const& node) { auto degrees = std::fmod(eval(node.angle_),90.0); - auto angle = deg2rad(boost::algorithm::clamp(degrees, -89.0, 89.0)); + if (degrees < -89.0) degrees = -89.0; + else if (degrees > 89.0) degrees = 89.0; + auto angle = deg2rad(degrees); transform_.multiply(agg::trans_affine_skewing(0.0, angle)); } From 0a126a3765754a995a795c44d64e13d29140b624 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 14 Jul 2015 22:54:32 -0700 Subject: [PATCH 067/165] attemp to fix #2971 --- include/mapnik/text/glyph_positions.hpp | 3 +-- src/agg/process_group_symbolizer.cpp | 1 + src/agg/process_shield_symbolizer.cpp | 1 + src/agg/process_text_symbolizer.cpp | 1 + src/cairo/process_group_symbolizer.cpp | 1 + src/cairo/process_text_symbolizer.cpp | 1 + src/grid/process_group_symbolizer.cpp | 1 + src/grid/process_shield_symbolizer.cpp | 1 + src/grid/process_text_symbolizer.cpp | 1 + 9 files changed, 9 insertions(+), 2 deletions(-) diff --git a/include/mapnik/text/glyph_positions.hpp b/include/mapnik/text/glyph_positions.hpp index 604de8c7c..e956edda8 100644 --- a/include/mapnik/text/glyph_positions.hpp +++ b/include/mapnik/text/glyph_positions.hpp @@ -26,6 +26,7 @@ #include #include #include +#include // agg #include "agg_trans_affine.h" @@ -37,8 +38,6 @@ namespace mapnik { -struct glyph_info; - struct glyph_position { glyph_position(glyph_info const& _glyph, pixel_position const& _pos, rotation const& _rot) diff --git a/src/agg/process_group_symbolizer.cpp b/src/agg/process_group_symbolizer.cpp index 68c54f2c3..f2c8adf48 100644 --- a/src/agg/process_group_symbolizer.cpp +++ b/src/agg/process_group_symbolizer.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include diff --git a/src/agg/process_shield_symbolizer.cpp b/src/agg/process_shield_symbolizer.cpp index 67fe8de0d..3d28eff60 100644 --- a/src/agg/process_shield_symbolizer.cpp +++ b/src/agg/process_shield_symbolizer.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include namespace mapnik { diff --git a/src/agg/process_text_symbolizer.cpp b/src/agg/process_text_symbolizer.cpp index 078a7abaa..b3794bcf5 100644 --- a/src/agg/process_text_symbolizer.cpp +++ b/src/agg/process_text_symbolizer.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include namespace mapnik { diff --git a/src/cairo/process_group_symbolizer.cpp b/src/cairo/process_group_symbolizer.cpp index d32afcf67..a2dc4abbe 100644 --- a/src/cairo/process_group_symbolizer.cpp +++ b/src/cairo/process_group_symbolizer.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include diff --git a/src/cairo/process_text_symbolizer.cpp b/src/cairo/process_text_symbolizer.cpp index 06b56cb2d..2d34e213c 100644 --- a/src/cairo/process_text_symbolizer.cpp +++ b/src/cairo/process_text_symbolizer.cpp @@ -27,6 +27,7 @@ #include #include #include +#include namespace mapnik { diff --git a/src/grid/process_group_symbolizer.cpp b/src/grid/process_group_symbolizer.cpp index a672f6b8f..f73118969 100644 --- a/src/grid/process_group_symbolizer.cpp +++ b/src/grid/process_group_symbolizer.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include diff --git a/src/grid/process_shield_symbolizer.cpp b/src/grid/process_shield_symbolizer.cpp index 781989384..50bc8209f 100644 --- a/src/grid/process_shield_symbolizer.cpp +++ b/src/grid/process_shield_symbolizer.cpp @@ -31,6 +31,7 @@ #include #include #include +#include // agg #include "agg_trans_affine.h" diff --git a/src/grid/process_text_symbolizer.cpp b/src/grid/process_text_symbolizer.cpp index 3df4ad24f..7fe0ff377 100644 --- a/src/grid/process_text_symbolizer.cpp +++ b/src/grid/process_text_symbolizer.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include namespace mapnik { From dfe73b799536aef546ebf8fa213d5bb268123483 Mon Sep 17 00:00:00 2001 From: artemp Date: Wed, 15 Jul 2015 11:26:45 +0200 Subject: [PATCH 068/165] mapnik c++ style --- include/mapnik/grid/grid_pixfmt.hpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/include/mapnik/grid/grid_pixfmt.hpp b/include/mapnik/grid/grid_pixfmt.hpp index a26a936c9..fbd6f5173 100644 --- a/include/mapnik/grid/grid_pixfmt.hpp +++ b/include/mapnik/grid/grid_pixfmt.hpp @@ -38,7 +38,7 @@ namespace mapnik { //============================================================blender_gray -template struct blender_gray +template struct blender_gray { using color_type = ColorT; using value_type = typename color_type::value_type; @@ -55,7 +55,7 @@ template struct blender_gray //=====================================================apply_gamma_dir_gray -template class apply_gamma_dir_gray +template class apply_gamma_dir_gray { public: using value_type = typename ColorT::value_type; @@ -74,7 +74,7 @@ private: //=====================================================apply_gamma_inv_gray -template class apply_gamma_inv_gray +template class apply_gamma_inv_gray { public: using value_type = typename ColorT::value_type; @@ -93,7 +93,7 @@ private: //=================================================pixfmt_alpha_blend_gray -template +template class pixfmt_alpha_blend_gray { public: @@ -160,7 +160,7 @@ public: void attach(rbuf_type& rb) { m_rbuf = &rb; } //-------------------------------------------------------------------- - template + template bool attach(PixFmt& pixf, int x1, int y1, int x2, int y2) { agg::rect_i r(x1, y1, x2, y2); @@ -538,10 +538,10 @@ public: } //-------------------------------------------------------------------- - template void for_each_pixel(Function f) + template + void for_each_pixel(Function f) { - unsigned y; - for(y = 0; y < height(); ++y) + for(unsigned y = 0; y < height(); ++y) { row_data r = m_rbuf->row(y); if(r.ptr) @@ -562,19 +562,19 @@ public: } //-------------------------------------------------------------------- - template void apply_gamma_dir(const GammaLut& g) + template void apply_gamma_dir(const GammaLut& g) { for_each_pixel(apply_gamma_dir_gray(g)); } //-------------------------------------------------------------------- - template void apply_gamma_inv(const GammaLut& g) + template void apply_gamma_inv(const GammaLut& g) { for_each_pixel(apply_gamma_inv_gray(g)); } //-------------------------------------------------------------------- - template + template void copy_from(const RenBuf2& from, int xdst, int ydst, int xsrc, int ysrc, @@ -590,7 +590,7 @@ public: } //-------------------------------------------------------------------- - template + template void blend_from_color(const SrcPixelFormatRenderer& from, const color_type& color, int xdst, int ydst, @@ -617,7 +617,7 @@ public: } //-------------------------------------------------------------------- - template + template void blend_from_lut(const SrcPixelFormatRenderer& from, const color_type* color_lut, int xdst, int ydst, From eded32d27d73b24a475f2d19a7656ebf3183be0c Mon Sep 17 00:00:00 2001 From: artemp Date: Wed, 15 Jul 2015 11:30:05 +0200 Subject: [PATCH 069/165] formattin --- include/mapnik/text/renderer.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/mapnik/text/renderer.hpp b/include/mapnik/text/renderer.hpp index a0b521d30..1b6bac112 100644 --- a/include/mapnik/text/renderer.hpp +++ b/include/mapnik/text/renderer.hpp @@ -56,8 +56,8 @@ public: text_renderer (halo_rasterizer_e rasterizer, composite_mode_e comp_op = src_over, composite_mode_e halo_comp_op = src_over, - double scale_factor=1.0, - stroker_ptr stroker=stroker_ptr()); + double scale_factor = 1.0, + stroker_ptr stroker = stroker_ptr()); void set_transform(agg::trans_affine const& transform); void set_halo_transform(agg::trans_affine const& halo_transform); protected: From 1733db502243326e39f06f27d0e75c0a9b752080 Mon Sep 17 00:00:00 2001 From: artemp Date: Wed, 15 Jul 2015 17:46:27 +0200 Subject: [PATCH 070/165] c++ - don't rely on implicit int to bool conversions --- src/text/face.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/text/face.cpp b/src/text/face.cpp index 10afa65ae..79f35d8fe 100644 --- a/src/text/face.cpp +++ b/src/text/face.cpp @@ -36,12 +36,12 @@ font_face::font_face(FT_Face face) bool font_face::set_character_sizes(double size) { - return !FT_Set_Char_Size(face_,0,(FT_F26Dot6)(size * (1<<6)),0,0); + return (FT_Set_Char_Size(face_,0,(FT_F26Dot6)(size * (1<<6)),0,0) == 0); } bool font_face::set_unscaled_character_sizes() { - return !FT_Set_Char_Size(face_,0,face_->units_per_EM,0,0); + return (FT_Set_Char_Size(face_,0,face_->units_per_EM,0,0) == 0); } bool font_face::glyph_dimensions(glyph_info & glyph) const From af0369d812c33c8d9c1433099df865f8c5bf74ad Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 15 Jul 2015 12:59:42 -0700 Subject: [PATCH 071/165] add no-op rendering benchmark - refs #2976 --- benchmark/build.py | 1 + benchmark/test_noop_rendering.cpp | 53 +++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 benchmark/test_noop_rendering.cpp diff --git a/benchmark/build.py b/benchmark/build.py index 8b2049e24..800a9abb5 100644 --- a/benchmark/build.py +++ b/benchmark/build.py @@ -46,6 +46,7 @@ benchmarks = [ "test_offset_converter.cpp", "test_marker_cache.cpp", "test_quad_tree.cpp", + "test_noop_rendering.cpp", # "test_numeric_cast_vs_static_cast.cpp", ] for cpp_test in benchmarks: diff --git a/benchmark/test_noop_rendering.cpp b/benchmark/test_noop_rendering.cpp new file mode 100644 index 000000000..03d2e675c --- /dev/null +++ b/benchmark/test_noop_rendering.cpp @@ -0,0 +1,53 @@ +#include "bench_framework.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +class test : public benchmark::test_case +{ +public: + test(mapnik::parameters const& params) + : test_case(params) {} + + bool validate() const + { + return true; + } + bool operator()() const + { + mapnik::Map m(256,256,"+init=epsg:3857"); + + mapnik::parameters params; + params["type"]="memory"; + auto ds = std::make_shared(params); + // add whitespace to trigger phony "reprojection" + mapnik::layer lay("layer",m.srs() + " "); + lay.set_datasource(ds); + lay.add_style("style"); + m.add_layer(lay); + // dummy style to ensure that layer is processed + m.insert_style("style",mapnik::feature_type_style()); + // dummy bbox, but "valid" because minx and miny are less + // with an invalid bbox then layer.visible() returns false + // and the initial rendering setup is not run + m.zoom_to_box(mapnik::box2d(-1,-1,0,0)); + for (unsigned i=0;i ren(m,im); + ren.apply(); + } + return true; + } +}; + +BENCHMARK(test,"rendering with reprojection") From c6643943975b9a2b085bf69a42a189b07f746b44 Mon Sep 17 00:00:00 2001 From: artemp Date: Thu, 16 Jul 2015 12:17:21 +0200 Subject: [PATCH 072/165] remove duplicate operator= --- include/mapnik/image_null.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/include/mapnik/image_null.hpp b/include/mapnik/image_null.hpp index c318c766c..67dbbd358 100644 --- a/include/mapnik/image_null.hpp +++ b/include/mapnik/image_null.hpp @@ -50,7 +50,6 @@ public: image(image const&) {} image(image &&) noexcept {} image& operator=(image) { return *this; } - imageconst& operator=(image const& rhs) const { return rhs; } bool operator==(image const&) const { return true; } bool operator<(image const&) const { return false; } From d2cfd0f0ea7af69a0d1e41577e81aca4f33ebc86 Mon Sep 17 00:00:00 2001 From: artemp Date: Thu, 16 Jul 2015 12:58:32 +0200 Subject: [PATCH 073/165] log all errors without throwing --- src/svg/svg_parser.cpp | 63 ++++++++++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/src/svg/svg_parser.cpp b/src/svg/svg_parser.cpp index c1f6ad49a..80c40a9ed 100644 --- a/src/svg/svg_parser.cpp +++ b/src/svg/svg_parser.cpp @@ -501,18 +501,17 @@ void parse_path(svg_parser & parser, xmlTextReaderPtr reader) if (!mapnik::svg::parse_path((const char*) value, parser.path_)) { - xmlFree(value); xmlChar *id_value; id_value = xmlTextReaderGetAttribute(reader, BAD_CAST "id"); if (id_value) { std::string id_string((const char *) id_value); xmlFree(id_value); - throw std::runtime_error(std::string("unable to parse invalid svg with id '") + id_string + "'"); + parser.error_messages_.push_back(std::string("unable to parse invalid svg with id '") + id_string + "'"); } else { - throw std::runtime_error("unable to parse invalid svg "); + parser.error_messages_.push_back(std::string("unable to parse invalid svg ")); } } parser.path_.end_path(); @@ -530,8 +529,7 @@ void parse_polygon(svg_parser & parser, xmlTextReaderPtr reader) parser.path_.begin_path(); if (!mapnik::svg::parse_points((const char*) value, parser.path_)) { - xmlFree(value); - throw std::runtime_error("Failed to parse "); + parser.error_messages_.push_back(std::string("Failed to parse ")); } parser.path_.close_subpath(); parser.path_.end_path(); @@ -549,10 +547,8 @@ void parse_polyline(svg_parser & parser, xmlTextReaderPtr reader) parser.path_.begin_path(); if (!mapnik::svg::parse_points((const char*) value, parser.path_)) { - xmlFree(value); - throw std::runtime_error("Failed to parse "); + parser.error_messages_.push_back(std::string("Failed to parse ")); } - parser.path_.end_path(); xmlFree(value); } @@ -632,9 +628,15 @@ void parse_circle(svg_parser & parser, xmlTextReaderPtr reader) if(r != 0.0) { - if(r < 0.0) throw std::runtime_error("parse_circle: Invalid radius"); - agg::ellipse c(cx, cy, r, r); - parser.path_.storage().concat_path(c); + if (r < 0.0) + { + parser.error_messages_.emplace_back("parse_circle: Invalid radius"); + } + else + { + agg::ellipse c(cx, cy, r, r); + parser.path_.storage().concat_path(c); + } } parser.path_.end_path(); @@ -678,12 +680,21 @@ void parse_ellipse(svg_parser & parser, xmlTextReaderPtr reader) parser.path_.begin_path(); - if(rx != 0.0 && ry != 0.0) + if (rx != 0.0 && ry != 0.0) { - if(rx < 0.0) throw std::runtime_error("parse_ellipse: Invalid rx"); - if(ry < 0.0) throw std::runtime_error("parse_ellipse: Invalid ry"); - agg::ellipse c(cx, cy, rx, ry); - parser.path_.storage().concat_path(c); + if (rx < 0.0) + { + parser.error_messages_.emplace_back("parse_ellipse: Invalid rx"); + } + else if (ry < 0.0) + { + parser.error_messages_.emplace_back("parse_ellipse: Invalid ry"); + } + else + { + agg::ellipse c(cx, cy, rx, ry); + parser.path_.storage().concat_path(c); + } } parser.path_.end_path(); @@ -754,10 +765,22 @@ void parse_rect(svg_parser & parser, xmlTextReaderPtr reader) if(w != 0.0 && h != 0.0) { - if(w < 0.0) throw std::runtime_error("parse_rect: Invalid width"); - if(h < 0.0) throw std::runtime_error("parse_rect: Invalid height"); - if(rx < 0.0) throw std::runtime_error("parse_rect: Invalid rx"); - if(ry < 0.0) throw std::runtime_error("parse_rect: Invalid ry"); + if(w < 0.0) + { + parser.error_messages_.emplace_back("parse_rect: Invalid width"); + } + else if(h < 0.0) + { + parser.error_messages_.emplace_back("parse_rect: Invalid height"); + } + else if(rx < 0.0) + { + parser.error_messages_.emplace_back("parse_rect: Invalid rx"); + } + else if(ry < 0.0) + { + parser.error_messages_.emplace_back("parse_rect: Invalid ry"); + } parser.path_.begin_path(); if(rounded) From c166d9acb84609983f66de0b9e0aac89e87862b3 Mon Sep 17 00:00:00 2001 From: artemp Date: Thu, 16 Jul 2015 13:06:52 +0200 Subject: [PATCH 074/165] reduce code duplication --- utils/svg2png/svg2png.cpp | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/utils/svg2png/svg2png.cpp b/utils/svg2png/svg2png.cpp index 7e3aca532..5c40ec210 100644 --- a/utils/svg2png/svg2png.cpp +++ b/utils/svg2png/svg2png.cpp @@ -61,18 +61,6 @@ struct main_marker_visitor verbose_(verbose), auto_open_(auto_open) {} - void operator() (mapnik::marker_null const&) - { - std::clog << "svg2png error: '" << svg_name_ << "' is not a valid vector!\n"; - return_value_ = -1; - } - - void operator() (mapnik::marker_rgba8 const&) - { - std::clog << "svg2png error: '" << svg_name_ << "' is not a valid vector!\n"; - return_value_ = -1; - } - void operator() (mapnik::marker_svg const& marker) { using pixfmt = agg::pixfmt_rgba32_pre; @@ -130,6 +118,14 @@ struct main_marker_visitor } + // default + template + void operator() (T const&) + { + std::clog << "svg2png error: '" << svg_name_ << "' is not a valid vector!\n"; + return_value_ = -1; + } + private: std::string & svg_name_; int & return_value_; From 62a58937a48f9bf5191656c7ef26125d51cb457f Mon Sep 17 00:00:00 2001 From: artemp Date: Thu, 16 Jul 2015 16:55:41 +0200 Subject: [PATCH 075/165] try to never fail (almost) --- src/marker_cache.cpp | 4 ++-- src/svg/svg_parser.cpp | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/marker_cache.cpp b/src/marker_cache.cpp index 7b15e087a..e0f9b8f8e 100644 --- a/src/marker_cache.cpp +++ b/src/marker_cache.cpp @@ -182,7 +182,7 @@ std::shared_ptr marker_cache::find(std::string const& uri, { std::cerr << "SVG PARSING ERROR:\"" << msg << "\"" << std::endl; } - return std::make_shared(mapnik::marker_null()); + //return std::make_shared(mapnik::marker_null()); } //svg.arrange_orientations(); double lox,loy,hix,hiy; @@ -223,7 +223,7 @@ std::shared_ptr marker_cache::find(std::string const& uri, { std::cerr << "SVG PARSING ERROR:\"" << msg << "\"" << std::endl; } - return std::make_shared(mapnik::marker_null()); + //return std::make_shared(mapnik::marker_null()); } //svg.arrange_orientations(); double lox,loy,hix,hiy; diff --git a/src/svg/svg_parser.cpp b/src/svg/svg_parser.cpp index 80c40a9ed..cc5335922 100644 --- a/src/svg/svg_parser.cpp +++ b/src/svg/svg_parser.cpp @@ -923,6 +923,8 @@ bool parse_common_gradient(svg_parser & parser, xmlTextReaderPtr reader) std::stringstream ss; ss << "Failed to find linked gradient " << linkid; parser.error_messages_.push_back(ss.str()); + xmlFree(value); + return false; } } xmlFree(value); @@ -968,9 +970,9 @@ bool parse_common_gradient(svg_parser & parser, xmlTextReaderPtr reader) */ void parse_radial_gradient(svg_parser & parser, xmlTextReaderPtr reader) { - if (!parse_common_gradient(parser,reader)) - return; - + //if (!parse_common_gradient(parser,reader)) + // return; + parse_common_gradient(parser, reader); xmlChar *value; double cx = 0.5; double cy = 0.5; From cd2c76689dafcd979bf7b7314407226ede447a47 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Thu, 16 Jul 2015 11:20:50 -0700 Subject: [PATCH 076/165] Add a plugin policy per #2978 --- docs/contributing.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/contributing.md b/docs/contributing.md index e7f327977..bb486a6db 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -44,6 +44,20 @@ We host our code on github.com/mapnik and encourage anyone interested to fork th If you just have a question about the code, or a feature you want to discuss then feel free to create a new issue at github.com/mapnik-support. +## Plugins + +Mapnik has a plugin interface for reading various geodata formats. Plugins are both viable inside of Mapnik core and also able to be used outside of Mapnik. + +Plugins should be developed outside of core except in rare cases when most of the following are met: + + - The plugin has no external dependencies or dependencies are easily installed + - The plugin has excellent unit tests + - The plugin has a maintainer willing to support the plugin over time + - Setup and testing of the plugin is easy on travis.ci (see .travis.yml) + + +Therefore plugins that depend on proprietary, unmaintained, or difficult to test third-party dependencies are not viable for Mapnik core. However they are still likely very valuable for the Mapnik community, so get in touch via https://github.com/mapnik/mapnik-support if we can help you develop your plugin outside of core. + ## Code Philosophy From 2048be7b30e72727be739b5f00c9d0cbe648324f Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Thu, 16 Jul 2015 11:46:10 -0700 Subject: [PATCH 077/165] move occi, osm, rasterlite to non-core repo https://github.com/mapnik/non-core-plugins/commit/84b0bfecb36bea2e7a61c5bc21609cef2685058a - refs #2980 --- SConstruct | 7 - plugins/input/occi/README | 12 - plugins/input/occi/build.py | 74 -- plugins/input/occi/occi_datasource.cpp | 641 ------------------ plugins/input/occi/occi_datasource.hpp | 95 --- plugins/input/occi/occi_featureset.cpp | 516 -------------- plugins/input/occi/occi_featureset.hpp | 77 --- plugins/input/occi/occi_types.cpp | 91 --- plugins/input/occi/occi_types.hpp | 265 -------- plugins/input/occi/spatial_classesh.h | 147 ---- plugins/input/occi/spatial_classesm.cpp | 11 - plugins/input/occi/spatial_classesm.h | 14 - plugins/input/occi/spatial_classeso.cpp | 312 --------- plugins/input/occi/spatial_types.typ | 4 - plugins/input/osm/basiccurl.cpp | 75 -- plugins/input/osm/basiccurl.h | 40 -- plugins/input/osm/build.py | 69 -- plugins/input/osm/dataset_deliverer.cpp | 66 -- plugins/input/osm/dataset_deliverer.h | 47 -- plugins/input/osm/osm.cpp | 230 ------- plugins/input/osm/osm.h | 136 ---- plugins/input/osm/osm_datasource.cpp | 159 ----- plugins/input/osm/osm_datasource.hpp | 73 -- plugins/input/osm/osm_featureset.cpp | 132 ---- plugins/input/osm/osm_featureset.hpp | 72 -- plugins/input/osm/osmparser.cpp | 150 ---- plugins/input/osm/osmparser.h | 54 -- plugins/input/osm/osmtagtypes.h | 50 -- plugins/input/rasterlite/build.py | 71 -- .../rasterlite/rasterlite_datasource.cpp | 191 ------ .../rasterlite/rasterlite_datasource.hpp | 66 -- .../rasterlite/rasterlite_featureset.cpp | 136 ---- .../rasterlite/rasterlite_featureset.hpp | 71 -- .../input/rasterlite/rasterlite_include.hpp | 31 - 34 files changed, 4185 deletions(-) delete mode 100644 plugins/input/occi/README delete mode 100644 plugins/input/occi/build.py delete mode 100644 plugins/input/occi/occi_datasource.cpp delete mode 100644 plugins/input/occi/occi_datasource.hpp delete mode 100644 plugins/input/occi/occi_featureset.cpp delete mode 100644 plugins/input/occi/occi_featureset.hpp delete mode 100644 plugins/input/occi/occi_types.cpp delete mode 100644 plugins/input/occi/occi_types.hpp delete mode 100644 plugins/input/occi/spatial_classesh.h delete mode 100644 plugins/input/occi/spatial_classesm.cpp delete mode 100644 plugins/input/occi/spatial_classesm.h delete mode 100644 plugins/input/occi/spatial_classeso.cpp delete mode 100644 plugins/input/occi/spatial_types.typ delete mode 100755 plugins/input/osm/basiccurl.cpp delete mode 100755 plugins/input/osm/basiccurl.h delete mode 100644 plugins/input/osm/build.py delete mode 100644 plugins/input/osm/dataset_deliverer.cpp delete mode 100644 plugins/input/osm/dataset_deliverer.h delete mode 100644 plugins/input/osm/osm.cpp delete mode 100644 plugins/input/osm/osm.h delete mode 100644 plugins/input/osm/osm_datasource.cpp delete mode 100644 plugins/input/osm/osm_datasource.hpp delete mode 100644 plugins/input/osm/osm_featureset.cpp delete mode 100644 plugins/input/osm/osm_featureset.hpp delete mode 100644 plugins/input/osm/osmparser.cpp delete mode 100644 plugins/input/osm/osmparser.h delete mode 100644 plugins/input/osm/osmtagtypes.h delete mode 100644 plugins/input/rasterlite/build.py delete mode 100644 plugins/input/rasterlite/rasterlite_datasource.cpp delete mode 100644 plugins/input/rasterlite/rasterlite_datasource.hpp delete mode 100644 plugins/input/rasterlite/rasterlite_featureset.cpp delete mode 100644 plugins/input/rasterlite/rasterlite_featureset.hpp delete mode 100644 plugins/input/rasterlite/rasterlite_include.hpp diff --git a/SConstruct b/SConstruct index f4e910b22..488431df4 100644 --- a/SConstruct +++ b/SConstruct @@ -110,14 +110,7 @@ PLUGINS = { # plugins with external dependencies 'pgraster': {'default':True,'path':None,'inc':'libpq-fe.h','lib':'pq','lang':'C'}, 'gdal': {'default':True,'path':None,'inc':'gdal_priv.h','lib':'gdal','lang':'C++'}, 'ogr': {'default':True,'path':None,'inc':'ogrsf_frmts.h','lib':'gdal','lang':'C++'}, - # configured with custom paths, hence 'path': PREFIX/INCLUDES/LIBS - 'occi': {'default':False,'path':'OCCI','inc':'occi.h','lib':'clntsh','lang':'C++'}, 'sqlite': {'default':True,'path':'SQLITE','inc':'sqlite3.h','lib':'sqlite3','lang':'C'}, - 'rasterlite': {'default':False,'path':'RASTERLITE','inc':['sqlite3.h','rasterlite.h'],'lib':'rasterlite','lang':'C'}, - - # todo: osm plugin does also depend on libxml2 (but there is a separate check for that) - 'osm': {'default':False,'path':None,'inc':None,'lib':None,'lang':'C'}, - # plugins without external dependencies requiring CheckLibWithHeader... 'shape': {'default':True,'path':None,'inc':None,'lib':None,'lang':'C++'}, 'csv': {'default':True,'path':None,'inc':None,'lib':None,'lang':'C++'}, diff --git a/plugins/input/occi/README b/plugins/input/occi/README deleted file mode 100644 index a36411c88..000000000 --- a/plugins/input/occi/README +++ /dev/null @@ -1,12 +0,0 @@ -# -# To regenerate C++ class declarations & implementations for the Spatial -# object types of your database you should execute OTT in your server ! -# - -echo "TYPE MDSYS.SDO_POINT_TYPE AS SDOPointType" > spatial_types.typ -echo "TYPE MDSYS.SDO_GEOMETRY AS SDOGeometry" >> spatial_types.typ - -ott userid=scott/tiger attraccess=private intype=spatial_types.typ code=cpp \ - cppfile=spatial_classeso.cpp hfile=spatial_classesh.h mapfile=spatial_classesm.cpp \ - mapfunc=RegisterClasses - diff --git a/plugins/input/occi/build.py b/plugins/input/occi/build.py deleted file mode 100644 index e0aeb7b4b..000000000 --- a/plugins/input/occi/build.py +++ /dev/null @@ -1,74 +0,0 @@ -# -# This file is part of Mapnik (c++ mapping toolkit) -# -# Copyright (C) 2015 Artem Pavlenko -# -# Mapnik 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 -# -# - -Import ('plugin_base') -Import ('env') - -PLUGIN_NAME = 'occi' - -plugin_env = plugin_base.Clone() - -plugin_sources = Split( - """ - %(PLUGIN_NAME)s_types.cpp - %(PLUGIN_NAME)s_datasource.cpp - %(PLUGIN_NAME)s_featureset.cpp - spatial_classesm.cpp - spatial_classeso.cpp - """ % locals() -) - -libraries = [ 'clntsh', 'occi' ] -libraries.append('boost_system%s' % env['BOOST_APPEND']) -libraries.append(env['ICU_LIB_NAME']) - -if env['PLUGIN_LINKING'] == 'shared': - libraries.append(env['MAPNIK_NAME']) - - # libocci.dylib, at least for 11.2 links to libstdc++ - # so we defer symbol resolution to runtime in order to - # dodge linking errors like - # Undefined symbols for architecture x86_64: - # "std::string::_Rep::_M_destroy(std::allocator const&)", referenced from: - # RegisterClasses(oracle::occi::Environment*) in spatial_classesm.os - - if env['PLATFORM'] == 'Darwin': - plugin_env.Append(LINKFLAGS='-undefined dynamic_lookup') - - TARGET = plugin_env.SharedLibrary('../%s' % PLUGIN_NAME, - SHLIBPREFIX='', - SHLIBSUFFIX='.input', - source=plugin_sources, - LIBS=libraries) - - # if the plugin links to libmapnik ensure it is built first - Depends(TARGET, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME'])) - - if 'uninstall' not in COMMAND_LINE_TARGETS: - env.Install(env['MAPNIK_INPUT_PLUGINS_DEST'], TARGET) - env.Alias('install', env['MAPNIK_INPUT_PLUGINS_DEST']) - -plugin_obj = { - 'LIBS': libraries, - 'SOURCES': plugin_sources, -} - -Return('plugin_obj') diff --git a/plugins/input/occi/occi_datasource.cpp b/plugins/input/occi/occi_datasource.cpp deleted file mode 100644 index 1c2809239..000000000 --- a/plugins/input/occi/occi_datasource.cpp +++ /dev/null @@ -1,641 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 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 - * - *****************************************************************************/ - -#include "occi_datasource.hpp" -#include "occi_featureset.hpp" - -// mapnik -#include -#include -#include -#include -#include - -// boost -#include -#include - -// stl -#include -#include -#include -#include -#include - -using mapnik::datasource; -using mapnik::parameters; -using mapnik::query; -using mapnik::featureset_ptr; -using mapnik::layer_descriptor; -using mapnik::attribute_descriptor; -using mapnik::datasource_exception; -using mapnik::box2d; -using mapnik::coord2d; - -using oracle::occi::Environment; -using oracle::occi::Connection; -using oracle::occi::Statement; -using oracle::occi::ResultSet; -using oracle::occi::MetaData; -using oracle::occi::SQLException; -using oracle::occi::Type; -using oracle::occi::StatelessConnectionPool; - -const double occi_datasource::FMAX = std::numeric_limits::max(); -const std::string occi_datasource::METADATA_TABLE = "USER_SDO_GEOM_METADATA"; - -DATASOURCE_PLUGIN(occi_datasource) - -occi_datasource::occi_datasource(parameters const& params) - : datasource (params), - type_(datasource::Vector), - fields_(*params.get("fields", "*")), - geometry_field_(*params.get("geometry_field", "")), - srid_initialized_(false), - extent_initialized_(false), - bbox_token_("!bbox!"), - scale_denom_token_("!scale_denominator!"), - pixel_width_token_("!pixel_width!"), - pixel_height_token_("!pixel_height!"), - desc_(occi_datasource::name(), *params.get("encoding", "utf-8")), - use_wkb_(*params.get("use_wkb", false)), - row_limit_(*params.get("row_limit", 0)), - row_prefetch_(*params.get("row_prefetch", 100)), - pool_(0), - conn_(0) -{ -#ifdef MAPNIK_STATS - mapnik::progress_timer __stats__(std::clog, "occi_datasource::init"); -#endif - - if (! params.get("user")) throw datasource_exception("OCCI Plugin: no specified"); - if (! params.get("password")) throw datasource_exception("OCCI Plugin: no specified"); - if (! params.get("host")) throw datasource_exception("OCCI Plugin: no string specified"); - - boost::optional table = params.get("table"); - if (! table) - { - throw datasource_exception("OCCI Plugin: no parameter specified"); - } - else - { - table_ = *table; - } - estimate_extent_ = *params.get("estimate_extent",false); - use_spatial_index_ = *params.get("use_spatial_index",true); - use_connection_pool_ = *params.get("use_connection_pool",true); - - boost::optional ext = params.get("extent"); - if (ext) extent_initialized_ = extent_.from_string(*ext); - - boost::optional srid = params.get("srid"); - if (srid) - { - srid_ = *srid; - srid_initialized_ = true; - } - - // connect to environment - if (use_connection_pool_) - { - try - { - pool_ = occi_environment::instance().create_pool( - *params.get("user"), - *params.get("password"), - *params.get("host"), - *params.get("max_size", 5), - *params.get("initial_size", 1), - 1); - } - catch (SQLException& ex) - { - throw datasource_exception("OCCI Plugin: " + ex.getMessage()); - } - } - else - { - try - { - conn_ = occi_environment::instance().create_connection( - *params.get("user"), - *params.get("password"), - *params.get("host")); - } - catch (SQLException& ex) - { - throw datasource_exception("OCCI Plugin: " + ex.getMessage()); - } - } - - // extract real table name - table_name_ = mapnik::sql_utils::table_from_sql(table_); - - // get SRID and/or GEOMETRY_FIELD from metadata table only if we need to - if (! srid_initialized_ || geometry_field_ == "") - { -#ifdef MAPNIK_STATS - mapnik::progress_timer __stats__(std::clog, "occi_datasource::get_srid_and_geometry_field"); -#endif - - std::ostringstream s; - s << "SELECT srid, column_name FROM " << METADATA_TABLE << " WHERE"; - s << " LOWER(table_name) = LOWER('" << table_name_ << "')"; - - if (geometry_field_ != "") - { - s << " AND LOWER(column_name) = LOWER('" << geometry_field_ << "')"; - } - - MAPNIK_LOG_DEBUG(occi) << "occi_datasource: " << s.str(); - - try - { - occi_connection_ptr conn; - if (use_connection_pool_) conn.set_pool(pool_); - else conn.set_connection(conn_, false); - - ResultSet* rs = conn.execute_query(s.str()); - if (rs && rs->next ()) - { - if (! srid_initialized_) - { - srid_ = rs->getInt(1); - srid_initialized_ = true; - } - - if (geometry_field_ == "") - { - geometry_field_ = rs->getString(2); - } - } - } - catch (SQLException& ex) - { - throw datasource_exception("OCCI Plugin: " + ex.getMessage()); - } - } - - // get columns description - { -#ifdef MAPNIK_STATS - mapnik::progress_timer __stats__(std::clog, "occi_datasource::get_column_description"); -#endif - - std::ostringstream s; - s << "SELECT " << fields_ << " FROM (" << table_name_ << ") WHERE ROWNUM < 1"; - - MAPNIK_LOG_DEBUG(occi) << "occi_datasource: " << s.str(); - - try - { - occi_connection_ptr conn; - if (use_connection_pool_) conn.set_pool(pool_); - else conn.set_connection(conn_, false); - - ResultSet* rs = conn.execute_query(s.str()); - if (rs) - { - std::vector listOfColumns = rs->getColumnListMetaData(); - - for (unsigned int i = 0; i < listOfColumns.size(); ++i) - { - MetaData columnObj = listOfColumns[i]; - - std::string fld_name = columnObj.getString(MetaData::ATTR_NAME); - int type_oid = columnObj.getInt(MetaData::ATTR_DATA_TYPE); - - /* - int type_code = columnObj.getInt(MetaData::ATTR_TYPECODE); - if (type_code == OCCI_TYPECODE_OBJECT) - { - desc_.add_descriptor(attribute_descriptor(fld_name,mapnik::Object)); - continue; - } - */ - - switch (type_oid) - { - case oracle::occi::OCCIBOOL: - desc_.add_descriptor(attribute_descriptor(fld_name,mapnik::Boolean)); - break; - case oracle::occi::OCCIINT: - case oracle::occi::OCCIUNSIGNED_INT: - desc_.add_descriptor(attribute_descriptor(fld_name,mapnik::Integer)); - break; - case oracle::occi::OCCIFLOAT: - case oracle::occi::OCCIBFLOAT: - case oracle::occi::OCCIDOUBLE: - case oracle::occi::OCCIBDOUBLE: - case oracle::occi::OCCINUMBER: - case oracle::occi::OCCI_SQLT_NUM: - desc_.add_descriptor(attribute_descriptor(fld_name,mapnik::Double)); - break; - case oracle::occi::OCCICHAR: - case oracle::occi::OCCISTRING: - case oracle::occi::OCCI_SQLT_AFC: - case oracle::occi::OCCI_SQLT_AVC: - case oracle::occi::OCCI_SQLT_CHR: - case oracle::occi::OCCI_SQLT_LNG: - case oracle::occi::OCCI_SQLT_LVC: - case oracle::occi::OCCI_SQLT_STR: - case oracle::occi::OCCI_SQLT_VCS: - case oracle::occi::OCCI_SQLT_VNU: - case oracle::occi::OCCI_SQLT_VBI: - case oracle::occi::OCCI_SQLT_VST: - case oracle::occi::OCCIROWID: - case oracle::occi::OCCI_SQLT_RDD: - case oracle::occi::OCCI_SQLT_RID: - case oracle::occi::OCCIDATE: - case oracle::occi::OCCI_SQLT_DAT: - case oracle::occi::OCCI_SQLT_DATE: - case oracle::occi::OCCI_SQLT_TIME: - case oracle::occi::OCCI_SQLT_TIME_TZ: - case oracle::occi::OCCITIMESTAMP: - case oracle::occi::OCCI_SQLT_TIMESTAMP: - case oracle::occi::OCCI_SQLT_TIMESTAMP_LTZ: - case oracle::occi::OCCI_SQLT_TIMESTAMP_TZ: - desc_.add_descriptor(attribute_descriptor(fld_name,mapnik::String)); - break; - case oracle::occi::OCCIINTERVALDS: - case oracle::occi::OCCIINTERVALYM: - case oracle::occi::OCCI_SQLT_INTERVAL_YM: - case oracle::occi::OCCI_SQLT_INTERVAL_DS: - case oracle::occi::OCCIANYDATA: - case oracle::occi::OCCIBLOB: - case oracle::occi::OCCIBFILE: - case oracle::occi::OCCIBYTES: - case oracle::occi::OCCICLOB: - case oracle::occi::OCCIVECTOR: - case oracle::occi::OCCIMETADATA: - case oracle::occi::OCCIPOBJECT: - case oracle::occi::OCCIREF: - case oracle::occi::OCCIREFANY: - case oracle::occi::OCCISTREAM: - case oracle::occi::OCCICURSOR: - case oracle::occi::OCCI_SQLT_FILE: - case oracle::occi::OCCI_SQLT_CFILE: - case oracle::occi::OCCI_SQLT_REF: - case oracle::occi::OCCI_SQLT_CLOB: - case oracle::occi::OCCI_SQLT_BLOB: - case oracle::occi::OCCI_SQLT_RSET: - MAPNIK_LOG_WARN(occi) << "occi_datasource: Unsupported datatype " - << occi_enums::resolve_datatype(type_oid) - << " (type_oid=" << type_oid << ")"; - break; - default: - MAPNIK_LOG_WARN(occi) << "occi_datasource: Unknown datatype " - << "(type_oid=" << type_oid << ")"; - break; - } - } - } - } - catch (SQLException& ex) - { - throw datasource_exception(ex.getMessage()); - } - } -} - -occi_datasource::~occi_datasource() -{ - if (use_connection_pool_) - { - if (pool_ != 0) - { - occi_environment::instance().destroy_pool(pool_); - } - } - else - { - if (conn_ != 0) - { - occi_environment::instance().destroy_connection(conn_); - } - } -} - -const char * occi_datasource::name() -{ - return "occi"; -} - -mapnik::datasource::datasource_t occi_datasource::type() const -{ - return type_; -} - -box2d occi_datasource::envelope() const -{ - if (extent_initialized_) return extent_; - - double lox = 0.0, loy = 0.0, hix = 0.0, hiy = 0.0; - - - if (estimate_extent_) - { -#ifdef MAPNIK_STATS - mapnik::progress_timer __stats__(std::clog, "occi_datasource::envelope(estimate_extent)"); -#endif - - std::ostringstream s; - s << "SELECT MIN(c.x), MIN(c.y), MAX(c.x), MAX(c.y) FROM "; - s << " (SELECT SDO_AGGR_MBR(" << geometry_field_ << ") shape FROM " << table_ << ") a, "; - s << " TABLE(SDO_UTIL.GETVERTICES(a.shape)) c"; - - MAPNIK_LOG_DEBUG(occi) << "occi_datasource: " << s.str(); - - try - { - occi_connection_ptr conn; - if (use_connection_pool_) conn.set_pool(pool_); - else conn.set_connection(conn_, false); - - ResultSet* rs = conn.execute_query(s.str()); - if (rs && rs->next()) - { - lox = rs->getDouble(1); - loy = rs->getDouble(2); - hix = rs->getDouble(3); - hiy = rs->getDouble(4); - extent_.init(lox, loy, hix, hiy); - extent_initialized_ = true; - } - } - catch (SQLException& ex) - { - throw datasource_exception("OCCI Plugin: " + ex.getMessage()); - } - } - else if (use_spatial_index_) - { -#ifdef MAPNIK_STATS - mapnik::progress_timer __stats__(std::clog, "occi_datasource::envelope(use_spatial_index)"); -#endif - - std::ostringstream s; - s << "SELECT dim.sdo_lb, dim.sdo_ub FROM "; - s << METADATA_TABLE << " m, TABLE(m.diminfo) dim "; - s << " WHERE LOWER(m.table_name) = LOWER('" << table_name_ << "') AND dim.sdo_dimname = 'X'"; - s << " UNION "; - s << "SELECT dim.sdo_lb, dim.sdo_ub FROM "; - s << METADATA_TABLE << " m, TABLE(m.diminfo) dim "; - s << " WHERE LOWER(m.table_name) = LOWER('" << table_name_ << "') AND dim.sdo_dimname = 'Y'"; - - MAPNIK_LOG_DEBUG(occi) << "occi_datasource: " << s.str(); - - try - { - occi_connection_ptr conn; - if (use_connection_pool_) conn.set_pool(pool_); - else conn.set_connection(conn_, false); - - ResultSet* rs = conn.execute_query(s.str()); - if (rs) - { - if (rs->next()) - { - lox = rs->getDouble(1); - hix = rs->getDouble(2); - } - - if (rs->next()) - { - loy = rs->getDouble(1); - hiy = rs->getDouble(2); - } - extent_.init(lox, loy, hix, hiy); - extent_initialized_ = true; - } - } - catch (SQLException& ex) - { - throw datasource_exception("OCCI Plugin: " + ex.getMessage()); - } - } - - if (! extent_initialized_) - { - throw datasource_exception("OCCI Plugin: unable to determine the extent of a table"); - } - - return extent_; -} - - -boost::optional occi_datasource::get_geometry_type() const -{ - return boost::optional(); -} - -layer_descriptor occi_datasource::get_descriptor() const -{ - return desc_; -} - -std::string occi_datasource::sql_bbox(box2d const& env) const -{ - std::ostringstream b; - b << std::setprecision(16); - b << "MDSYS.SDO_GEOMETRY(" << SDO_GTYPE_2DPOLYGON << "," << srid_ << ",NULL,"; - b << " MDSYS.SDO_ELEM_INFO_ARRAY(1," << SDO_ETYPE_POLYGON << "," << SDO_INTERPRETATION_RECTANGLE << "),"; - b << " MDSYS.SDO_ORDINATE_ARRAY("; - b << env.minx() << "," << env.miny() << ", "; - b << env.maxx() << "," << env.maxy() << "))"; - return b.str(); -} - -std::string occi_datasource::populate_tokens(std::string const& sql, double scale_denom, box2d const& env, double pixel_width, double pixel_height) const -{ - std::string populated_sql = sql; - - if (boost::algorithm::icontains(populated_sql, scale_denom_token_)) - { - std::ostringstream ss; - ss << scale_denom; - boost::algorithm::replace_all(populated_sql, scale_denom_token_, ss.str()); - } - - if (boost::algorithm::icontains(sql, pixel_width_token_)) - { - std::ostringstream ss; - ss << pixel_width; - boost::algorithm::replace_all(populated_sql, pixel_width_token_, ss.str()); - } - - if (boost::algorithm::icontains(sql, pixel_height_token_)) - { - std::ostringstream ss; - ss << pixel_height; - boost::algorithm::replace_all(populated_sql, pixel_height_token_, ss.str()); - } - - if (boost::algorithm::icontains(populated_sql, bbox_token_)) - { - boost::algorithm::replace_all(populated_sql, bbox_token_, sql_bbox(env)); - } - - return populated_sql; -} - -featureset_ptr occi_datasource::features(query const& q) const -{ -#ifdef MAPNIK_STATS - mapnik::progress_timer __stats__(std::clog, "occi_datasource::features"); -#endif - - box2d const& box = q.get_bbox(); - const double px_gw = 1.0 / std::get<0>(q.resolution()); - const double px_gh = 1.0 / std::get<1>(q.resolution()); - const double scale_denom = q.scale_denominator(); - - std::ostringstream s; - s << "SELECT "; - if (use_wkb_) - { - s << "SDO_UTIL.TO_WKBGEOMETRY(" << geometry_field_ << ")"; - } - else - { - s << geometry_field_; - } - std::set const& props = q.property_names(); - std::set::const_iterator pos = props.begin(); - std::set::const_iterator end = props.end(); - mapnik::context_ptr ctx = std::make_shared(); - for (; pos != end; ++pos) - { - s << ", " << *pos; - ctx->push(*pos); - } - - std::string query = populate_tokens(table_, scale_denom, box, px_gw, px_gh); - - if (use_spatial_index_) - { - std::ostringstream spatial_sql; - spatial_sql << " WHERE SDO_FILTER("; - spatial_sql << geometry_field_ << "," << sql_bbox(box); - spatial_sql << ", 'querytype = WINDOW') = 'TRUE'"; - - if (boost::algorithm::ifind_first(query, "WHERE")) - { - boost::algorithm::ireplace_first(query, "WHERE", spatial_sql.str() + " AND "); - } - else if (boost::algorithm::ifind_first(query, table_name_)) - { - boost::algorithm::ireplace_first(query, table_name_, table_name_ + " " + spatial_sql.str()); - } - else - { - MAPNIK_LOG_WARN(occi) << "occi_datasource: cannot determine where to add the spatial filter declaration"; - } - } - - s << " FROM " << query; - - if (row_limit_ > 0) - { - s << " WHERE ROWNUM < " << row_limit_; - } - - MAPNIK_LOG_DEBUG(occi) << "occi_datasource: " << s.str(); - - return std::make_shared(pool_, - conn_, - ctx, - s.str(), - desc_.get_encoding(), - use_connection_pool_, - use_wkb_, - row_prefetch_); -} - -featureset_ptr occi_datasource::features_at_point(coord2d const& pt, double tol) const -{ -#ifdef MAPNIK_STATS - mapnik::progress_timer __stats__(std::clog, "occi_datasource::features_at_point"); -#endif - - std::ostringstream s; - s << "SELECT "; - if (use_wkb_) - { - s << "SDO_UTIL.TO_WKBGEOMETRY(" << geometry_field_ << ")"; - } - else - { - s << geometry_field_; - } - std::vector::const_iterator itr = desc_.get_descriptors().begin(); - std::vector::const_iterator end = desc_.get_descriptors().end(); - mapnik::context_ptr ctx = std::make_shared(); - while (itr != end) - { - s << ", " << itr->get_name(); - ctx->push(itr->get_name()); - ++itr; - } - - box2d box(pt.x - tol, pt.y - tol, pt.x + tol, pt.y + tol); - std::string query = populate_tokens(table_, FMAX, box, 0, 0); - - if (use_spatial_index_) - { - std::ostringstream spatial_sql; - spatial_sql << " WHERE SDO_FILTER("; - spatial_sql << geometry_field_ << "," << sql_bbox(box); - spatial_sql << ", 'querytype = WINDOW') = 'TRUE'"; - - if (boost::algorithm::ifind_first(query, "WHERE")) - { - boost::algorithm::ireplace_first(query, "WHERE", spatial_sql.str() + " AND "); - } - else if (boost::algorithm::ifind_first(query, table_name_)) - { - boost::algorithm::ireplace_first(query, table_name_, table_name_ + " " + spatial_sql.str()); - } - else - { - MAPNIK_LOG_WARN(occi) << "occi_datasource: Cannot determine where to add the spatial filter declaration"; - } - } - - s << " FROM " << query; - - if (row_limit_ > 0) - { - s << " WHERE ROWNUM < " << row_limit_; - } - - MAPNIK_LOG_DEBUG(occi) << "occi_datasource: " << s.str(); - - return std::make_shared(pool_, - conn_, - ctx, - s.str(), - desc_.get_encoding(), - use_connection_pool_, - use_wkb_, - row_prefetch_); -} diff --git a/plugins/input/occi/occi_datasource.hpp b/plugins/input/occi/occi_datasource.hpp deleted file mode 100644 index 0affc339e..000000000 --- a/plugins/input/occi/occi_datasource.hpp +++ /dev/null @@ -1,95 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 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 OCCI_DATASOURCE_HPP -#define OCCI_DATASOURCE_HPP - -// mapnik -#include -#include -#include -#include -#include -#include -#include -#include - -// boost -#include -#include - -// stl -#include -#include - -// oci -#include "occi_types.hpp" - -class occi_datasource : public mapnik::datasource -{ -public: - occi_datasource(mapnik::parameters const& params); - virtual ~occi_datasource (); - mapnik::datasource::datasource_t type() const; - static const char * name(); - mapnik::featureset_ptr features(mapnik::query const& q) const; - mapnik::featureset_ptr features_at_point(mapnik::coord2d const& pt, double tol = 0) const; - mapnik::box2d envelope() const; - boost::optional get_geometry_type() const; - mapnik::layer_descriptor get_descriptor() const; - -private: - std::string sql_bbox(mapnik::box2d const& env) const; - std::string populate_tokens(std::string const& sql, - double scale_denom, - mapnik::box2d const& env, - double pixel_width, - double pixel_height) const; - - static const std::string METADATA_TABLE; - static const double FMAX; - - mapnik::datasource::datasource_t type_; - std::string table_; - std::string table_name_; - std::string fields_; - std::string geometry_field_; - int srid_; - bool srid_initialized_; - mutable bool extent_initialized_; - mutable mapnik::box2d extent_; - const std::string bbox_token_; - const std::string scale_denom_token_; - const std::string pixel_width_token_; - const std::string pixel_height_token_; - mapnik::layer_descriptor desc_; - bool use_wkb_; - mapnik::value_integer row_limit_; - int row_prefetch_; - oracle::occi::StatelessConnectionPool* pool_; - oracle::occi::Connection* conn_; - bool use_connection_pool_; - bool use_spatial_index_; - bool estimate_extent_; -}; - -#endif // OCCI_DATASOURCE_HPP diff --git a/plugins/input/occi/occi_featureset.cpp b/plugins/input/occi/occi_featureset.cpp deleted file mode 100644 index 0a0b942eb..000000000 --- a/plugins/input/occi/occi_featureset.cpp +++ /dev/null @@ -1,516 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 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 - -// ogr -#include "occi_featureset.hpp" - -using mapnik::query; -using mapnik::box2d; -using mapnik::feature_ptr; -using mapnik::geometry_type; -using mapnik::geometry_utils; -using mapnik::transcoder; -using mapnik::datasource_exception; -using mapnik::feature_factory; - -using oracle::occi::Connection; -using oracle::occi::Statement; -using oracle::occi::ResultSet; -using oracle::occi::StatelessConnectionPool; -using oracle::occi::MetaData; -using oracle::occi::SQLException; -using oracle::occi::Type; -using oracle::occi::Number; -using oracle::occi::Blob; - -occi_featureset::occi_featureset(StatelessConnectionPool* pool, - Connection* conn, - mapnik::context_ptr const& ctx, - std::string const& sqlstring, - std::string const& encoding, - bool use_connection_pool, - bool use_wkb, - unsigned prefetch_rows) - : rs_(nullptr), - tr_(new transcoder(encoding)), - feature_id_(1), - ctx_(ctx), - use_wkb_(use_wkb) -{ - if (use_connection_pool) - { - conn_.set_pool(pool); - } - else - { - conn_.set_connection(conn, false); - } - - try - { - rs_ = conn_.execute_query(sqlstring, prefetch_rows); - } - catch (SQLException &ex) - { - MAPNIK_LOG_ERROR(occi) << "OCCI Plugin: error processing " << sqlstring << " : " << ex.getMessage(); - - rs_ = nullptr; - } -} - -occi_featureset::~occi_featureset() -{ -} - -feature_ptr occi_featureset::next() -{ - while (rs_ != nullptr && rs_->next() == oracle::occi::ResultSet::DATA_AVAILABLE) - { - feature_ptr feature(feature_factory::create(ctx_, feature_id_)); - - if (use_wkb_) - { - Blob blob = rs_->getBlob(1); - blob.open(oracle::occi::OCCI_LOB_READONLY); - - unsigned int size = blob.length(); - if (buffer_.size() < size) - { - buffer_.resize(size); - } - - oracle::occi::Stream* instream = blob.getStream(1, 0); - instream->readBuffer(buffer_.data(), size); - blob.closeStream(instream); - blob.close(); - - if (! geometry_utils::from_wkb(feature->paths(), buffer_.data(), size)) - { - continue; - } - } - else - { - const std::unique_ptr geom(dynamic_cast(rs_->getObject(1))); - if (geom.get()) - { - convert_geometry(geom.get(), feature); - } - else - { - continue; - } - } - - std::vector listOfColumns = rs_->getColumnListMetaData(); - - for (unsigned int i = 1; i < listOfColumns.size(); ++i) - { - MetaData columnObj = listOfColumns[i]; - - std::string fld_name = columnObj.getString(MetaData::ATTR_NAME); - int type_oid = columnObj.getInt(MetaData::ATTR_DATA_TYPE); - - /* - int type_code = columnObj.getInt(MetaData::ATTR_TYPECODE); - if (type_code == OCCI_TYPECODE_OBJECT) - { - continue; - } - */ - - switch (type_oid) - { - case oracle::occi::OCCIBOOL: - feature->put(fld_name, (rs_->getInt(i + 1) != 0)); - break; - case oracle::occi::OCCIINT: - case oracle::occi::OCCIUNSIGNED_INT: - feature->put(fld_name, static_cast(rs_->getInt(i + 1))); - break; - case oracle::occi::OCCIFLOAT: - case oracle::occi::OCCIBFLOAT: - feature->put(fld_name, (double)rs_->getFloat(i + 1)); - break; - case oracle::occi::OCCIDOUBLE: - case oracle::occi::OCCIBDOUBLE: - case oracle::occi::OCCINUMBER: - case oracle::occi::OCCI_SQLT_NUM: - feature->put(fld_name, rs_->getDouble(i + 1)); - break; - case oracle::occi::OCCICHAR: - case oracle::occi::OCCISTRING: - case oracle::occi::OCCI_SQLT_AFC: - case oracle::occi::OCCI_SQLT_AVC: - case oracle::occi::OCCI_SQLT_CHR: - case oracle::occi::OCCI_SQLT_LNG: - case oracle::occi::OCCI_SQLT_LVC: - case oracle::occi::OCCI_SQLT_STR: - case oracle::occi::OCCI_SQLT_VCS: - case oracle::occi::OCCI_SQLT_VNU: - case oracle::occi::OCCI_SQLT_VBI: - case oracle::occi::OCCI_SQLT_VST: - case oracle::occi::OCCIROWID: - case oracle::occi::OCCI_SQLT_RDD: - case oracle::occi::OCCI_SQLT_RID: - case oracle::occi::OCCIDATE: - case oracle::occi::OCCI_SQLT_DAT: - case oracle::occi::OCCI_SQLT_DATE: - case oracle::occi::OCCI_SQLT_TIME: - case oracle::occi::OCCI_SQLT_TIME_TZ: - case oracle::occi::OCCITIMESTAMP: - case oracle::occi::OCCI_SQLT_TIMESTAMP: - case oracle::occi::OCCI_SQLT_TIMESTAMP_LTZ: - case oracle::occi::OCCI_SQLT_TIMESTAMP_TZ: - feature->put(fld_name, static_cast(tr_->transcode(rs_->getString(i + 1).c_str()))); - break; - case oracle::occi::OCCIINTERVALDS: - case oracle::occi::OCCIINTERVALYM: - case oracle::occi::OCCI_SQLT_INTERVAL_YM: - case oracle::occi::OCCI_SQLT_INTERVAL_DS: - case oracle::occi::OCCIANYDATA: - case oracle::occi::OCCIBLOB: - case oracle::occi::OCCIBFILE: - case oracle::occi::OCCIBYTES: - case oracle::occi::OCCICLOB: - case oracle::occi::OCCIVECTOR: - case oracle::occi::OCCIMETADATA: - case oracle::occi::OCCIPOBJECT: - case oracle::occi::OCCIREF: - case oracle::occi::OCCIREFANY: - case oracle::occi::OCCISTREAM: - case oracle::occi::OCCICURSOR: - case oracle::occi::OCCI_SQLT_FILE: - case oracle::occi::OCCI_SQLT_CFILE: - case oracle::occi::OCCI_SQLT_REF: - case oracle::occi::OCCI_SQLT_CLOB: - case oracle::occi::OCCI_SQLT_BLOB: - case oracle::occi::OCCI_SQLT_RSET: - { - MAPNIK_LOG_WARN(occi) << "occi_featureset: Unsupported datatype " - << occi_enums::resolve_datatype(type_oid) - << " (type_oid=" << type_oid << ")"; - break; - } - default: // shouldn't get here - { - MAPNIK_LOG_WARN(occi) << "occi_featureset: Unknown datatype " - << "(type_oid=" << type_oid << ")"; - break; - } - } - } - - ++feature_id_; - - return feature; - } - - return feature_ptr(); -} - - -void occi_featureset::convert_geometry(SDOGeometry* geom, feature_ptr feature) -{ - int gtype = (int)geom->getSdo_gtype(); - int dimensions = gtype / 1000; - int lrsvalue = (gtype - dimensions * 1000) / 100; - int geomtype = (gtype - dimensions * 1000 - lrsvalue * 100); - - const std::vector& elem_info = geom->getSdo_elem_info(); - const std::vector& ordinates = geom->getSdo_ordinates(); - const int ordinates_size = (int)ordinates.size(); - - switch (geomtype) - { - case SDO_GTYPE_POINT: - { - SDOPointType* sdopoint = geom->getSdo_point(); - if (sdopoint && ! sdopoint->isNull()) - { - std::unique_ptr point = std::make_unique(mapnik::geometry::geometry_types::Point); - point->move_to(sdopoint->getX(), sdopoint->getY()); - feature->add_geometry(point.release()); - } - } - break; - case SDO_GTYPE_LINE: - { - if (ordinates_size >= dimensions) - { - const bool is_single_geom = true; - const bool is_point_type = false; - convert_ordinates(feature, - mapnik::geometry::geometry_types::LineString, - elem_info, - ordinates, - dimensions, - is_single_geom, - is_point_type); - } - } - break; - case SDO_GTYPE_POLYGON: - { - if (ordinates_size >= dimensions) - { - const bool is_single_geom = true; - const bool is_point_type = false; - convert_ordinates(feature, - mapnik::geometry::geometry_types::Polygon, - elem_info, - ordinates, - dimensions, - is_single_geom, - is_point_type); - } - } - break; - case SDO_GTYPE_MULTIPOINT: - { - if (ordinates_size >= dimensions) - { - const bool is_single_geom = false; - const bool is_point_type = true; - convert_ordinates(feature, - mapnik::geometry::geometry_types::Point, - elem_info, - ordinates, - dimensions, - is_single_geom, - is_point_type); - } - } - break; - case SDO_GTYPE_MULTILINE: - { - if (ordinates_size >= dimensions) - { - const bool is_single_geom = false; - const bool is_point_type = false; - - convert_ordinates(feature, - mapnik::geometry::geometry_types::LineString, - elem_info, - ordinates, - dimensions, - is_single_geom, - is_point_type); - } - } - break; - case SDO_GTYPE_MULTIPOLYGON: - { - if (ordinates_size >= dimensions) - { - const bool is_single_geom = false; - const bool is_point_type = false; - - convert_ordinates(feature, - mapnik::geometry::geometry_types::Polygon, - elem_info, - ordinates, - dimensions, - is_single_geom, - is_point_type); - } - - } - break; - case SDO_GTYPE_COLLECTION: - { - if (ordinates_size >= dimensions) - { - const bool is_single_geom = false; - const bool is_point_type = false; - - convert_ordinates(feature, - mapnik::geometry::geometry_types::Polygon, - elem_info, - ordinates, - dimensions, - is_single_geom, - is_point_type); - } - } - break; - case SDO_GTYPE_UNKNOWN: - default: - { - MAPNIK_LOG_WARN(occi) << "occi_featureset: Unknown oracle enum " - << occi_enums::resolve_gtype(geomtype) - << "(gtype=" << gtype << ")"; - } - break; - } -} - -void occi_featureset::convert_ordinates(mapnik::feature_ptr feature, - const mapnik::geometry_type::types& geom_type, - const std::vector& elem_info, - const std::vector& ordinates, - const int dimensions, - const bool is_single_geom, - const bool is_point_geom) -{ - const int elem_size = elem_info.size(); - const int ord_size = ordinates.size(); - - if (elem_size >= 0) - { - int offset = elem_info[0]; - int etype = elem_info[1]; - int interp = elem_info[2]; - - if (! is_single_geom && elem_size > SDO_ELEM_INFO_SIZE) - { - geometry_type* geom = new geometry_type(geom_type); - - for (int i = SDO_ELEM_INFO_SIZE; i < elem_size; i+=3) - { - int next_offset = elem_info[i]; - int next_etype = elem_info[i + 1]; - int next_interp = elem_info[i + 2]; - bool is_linear_element = true; - bool is_unknown_etype = false; - mapnik::geometry_type::types gtype = mapnik::geometry::geometry_types::Point; - - switch (etype) - { - case SDO_ETYPE_POINT: - if (interp == SDO_INTERPRETATION_POINT) {} - if (interp > SDO_INTERPRETATION_POINT) {} - gtype = mapnik::geometry::geometry_types::Point; - break; - - case SDO_ETYPE_LINESTRING: - if (interp == SDO_INTERPRETATION_STRAIGHT) {} - if (interp == SDO_INTERPRETATION_CIRCULAR) {} - gtype = mapnik::geometry::geometry_types::LineString; - break; - - case SDO_ETYPE_POLYGON: - case SDO_ETYPE_POLYGON_INTERIOR: - if (interp == SDO_INTERPRETATION_STRAIGHT) {} - if (interp == SDO_INTERPRETATION_CIRCULAR) {} - if (interp == SDO_INTERPRETATION_RECTANGLE) {} - if (interp == SDO_INTERPRETATION_CIRCLE) {} - gtype = mapnik::geometry::geometry_types::Polygon; - break; - - case SDO_ETYPE_COMPOUND_LINESTRING: - case SDO_ETYPE_COMPOUND_POLYGON: - case SDO_ETYPE_COMPOUND_POLYGON_INTERIOR: - // interp = next ETYPE to consider - is_linear_element = false; - gtype = mapnik::geometry::geometry_types::Polygon; - break; - - case SDO_ETYPE_UNKNOWN: // unknown - default: - is_unknown_etype = true; - break; - } - - if (is_unknown_etype) - { - break; - } - - if (is_linear_element) - { - if (geom) - { - feature->add_geometry(geom); - } - - geom = new geometry_type(gtype); - fill_geometry_type(geom, - offset - 1, - next_offset - 1, - ordinates, - dimensions, - is_point_geom); - } - - offset = next_offset; - etype = next_etype; - interp = next_interp; - } - - if (geom) - { - feature->add_geometry(geom); - geom = 0; - } - } - else - { - geometry_type * geom = new geometry_type(geom_type); - fill_geometry_type(geom, - offset - 1, - ord_size, - ordinates, - dimensions, - is_point_geom); - - feature->add_geometry(geom); - } - } -} - -void occi_featureset::fill_geometry_type(geometry_type* geom, - const int real_offset, - const int next_offset, - const std::vector& ordinates, - const int dimensions, - const bool is_point_geom) -{ - geom->move_to((double) ordinates[real_offset], (double) ordinates[real_offset + 1]); - - if (is_point_geom) - { - for (int p = real_offset + dimensions; p < next_offset; p += dimensions) - { - geom->move_to((double) ordinates[p], (double) ordinates[p + 1]); - } - } - else - { - for (int p = real_offset + dimensions; p < next_offset; p += dimensions) - { - geom->line_to((double) ordinates[p], (double) ordinates[p + 1]); - } - } -} diff --git a/plugins/input/occi/occi_featureset.hpp b/plugins/input/occi/occi_featureset.hpp deleted file mode 100644 index 13445dd96..000000000 --- a/plugins/input/occi/occi_featureset.hpp +++ /dev/null @@ -1,77 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 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 OCCI_FEATURESET_HPP -#define OCCI_FEATURESET_HPP - -// mapnik -#include -#include -#include -// stl -#include - -// oci -#include "occi_types.hpp" - -#include - -class occi_featureset : public mapnik::Featureset -{ -public: - occi_featureset(oracle::occi::StatelessConnectionPool* pool, - oracle::occi::Connection* conn, - mapnik::context_ptr const& ctx, - std::string const& sqlstring, - std::string const& encoding, - bool use_connection_pool, - bool use_wkb, - unsigned prefetch_rows); - virtual ~occi_featureset(); - mapnik::feature_ptr next(); - -private: - void convert_geometry (SDOGeometry* geom, mapnik::feature_ptr feature); - void convert_ordinates (mapnik::feature_ptr feature, - const mapnik::geometry_type::types& geom_type, - const std::vector& elem_info, - const std::vector& ordinates, - const int dimensions, - const bool is_single_geom, - const bool is_point_geom); - void fill_geometry_type (mapnik::geometry_type* geom, - const int real_offset, - const int next_offset, - const std::vector& ordinates, - const int dimensions, - const bool is_point_geom); - - occi_connection_ptr conn_; - oracle::occi::ResultSet* rs_; - const std::unique_ptr tr_; - mapnik::value_integer feature_id_; - mapnik::context_ptr ctx_; - bool use_wkb_; - std::vector buffer_; -}; - -#endif // OCCI_FEATURESET_HPP diff --git a/plugins/input/occi/occi_types.cpp b/plugins/input/occi/occi_types.cpp deleted file mode 100644 index 439670680..000000000 --- a/plugins/input/occi/occi_types.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 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 - * - *****************************************************************************/ - -#include "occi_types.hpp" - -std::string occi_enums::resolve_gtype(int gtype) -{ - switch (gtype) - { - case SDO_GTYPE_UNKNOWN: return "SDO_GTYPE_UNKNOWN"; - case SDO_GTYPE_POINT: return "SDO_GTYPE_POINT"; - case SDO_GTYPE_LINE: return "SDO_GTYPE_LINE"; - case SDO_GTYPE_POLYGON: return "SDO_GTYPE_POLYGON"; - case SDO_GTYPE_MULTIPOINT: return "SDO_GTYPE_MULTIPOINT"; - case SDO_GTYPE_MULTILINE: return "SDO_GTYPE_MULTILINE"; - case SDO_GTYPE_MULTIPOLYGON: return "SDO_GTYPE_MULTIPOLYGON"; - case SDO_GTYPE_COLLECTION: return "SDO_GTYPE_COLLECTION"; - default: return ""; - } -} - -std::string occi_enums::resolve_etype(int etype) -{ - switch (etype) - { - case SDO_ETYPE_UNKNOWN: return "SDO_ETYPE_UNKNOWN"; - case SDO_ETYPE_POINT: return "SDO_ETYPE_POINT"; - case SDO_ETYPE_LINESTRING: return "SDO_ETYPE_LINESTRING"; - case SDO_ETYPE_POLYGON: return "SDO_ETYPE_POLYGON"; - case SDO_ETYPE_POLYGON_INTERIOR: return "SDO_ETYPE_POLYGON_INTERIOR"; - case SDO_ETYPE_COMPOUND_LINESTRING: return "SDO_ETYPE_COMPOUND_LINESTRING"; - case SDO_ETYPE_COMPOUND_POLYGON: return "SDO_ETYPE_COMPOUND_POLYGON"; - case SDO_ETYPE_COMPOUND_POLYGON_INTERIOR: return "SDO_ETYPE_COMPOUND_POLYGON_INTERIOR"; - default: return ""; - } -} - -std::string occi_enums::resolve_datatype(int type_id) -{ - switch (type_id) - { - case oracle::occi::OCCIINT: return "OCCIINT"; - case oracle::occi::OCCIUNSIGNED_INT: return "OCCIUNSIGNED_INT"; - case oracle::occi::OCCIFLOAT: return "OCCIFLOAT"; - case oracle::occi::OCCIBFLOAT: return "OCCIBFLOAT"; - case oracle::occi::OCCIDOUBLE: return "OCCIDOUBLE"; - case oracle::occi::OCCIBDOUBLE: return "OCCIBDOUBLE"; - case oracle::occi::OCCINUMBER: return "OCCINUMBER"; - case oracle::occi::OCCI_SQLT_NUM: return "OCCI_SQLT_NUM"; - case oracle::occi::OCCICHAR: return "OCCICHAR"; - case oracle::occi::OCCISTRING: return "OCCISTRING"; - case oracle::occi::OCCI_SQLT_AFC: return "OCCI_SQLT_AFC"; - case oracle::occi::OCCI_SQLT_AVC: return "OCCI_SQLT_AVC"; - case oracle::occi::OCCI_SQLT_CHR: return "OCCI_SQLT_CHR"; - case oracle::occi::OCCI_SQLT_LVC: return "OCCI_SQLT_LVC"; - case oracle::occi::OCCI_SQLT_LNG: return "OCCI_SQLT_LNG"; - case oracle::occi::OCCI_SQLT_STR: return "OCCI_SQLT_STR"; - case oracle::occi::OCCI_SQLT_VCS: return "OCCI_SQLT_VCS"; - case oracle::occi::OCCI_SQLT_VNU: return "OCCI_SQLT_VNU"; - case oracle::occi::OCCI_SQLT_VBI: return "OCCI_SQLT_VBI"; - case oracle::occi::OCCI_SQLT_VST: return "OCCI_SQLT_VST"; - case oracle::occi::OCCI_SQLT_RDD: return "OCCI_SQLT_RDD"; - case oracle::occi::OCCIDATE: return "OCCIDATE"; - case oracle::occi::OCCITIMESTAMP: return "OCCITIMESTAMP"; - case oracle::occi::OCCI_SQLT_DAT: return "OCCI_SQLT_DAT"; - case oracle::occi::OCCI_SQLT_TIMESTAMP: return "OCCI_SQLT_TIMESTAMP"; - case oracle::occi::OCCI_SQLT_TIMESTAMP_LTZ: return "OCCI_SQLT_TIMESTAMP_LTZ"; - case oracle::occi::OCCI_SQLT_TIMESTAMP_TZ: return "OCCI_SQLT_TIMESTAMP_TZ"; - case oracle::occi::OCCIPOBJECT: return "OCCIPOBJECT"; - default: return ""; - } -} diff --git a/plugins/input/occi/occi_types.hpp b/plugins/input/occi/occi_types.hpp deleted file mode 100644 index cc8cd41fa..000000000 --- a/plugins/input/occi/occi_types.hpp +++ /dev/null @@ -1,265 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 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 OCCI_TYPES_HPP -#define OCCI_TYPES_HPP - -// mapnik -#include -#include - -// occi -#include - -// ott generated SDOGeometry classes -#include "spatial_classesh.h" -#include "spatial_classesm.h" - -// check for oracle support -#if OCCI_MAJOR_VERSION >= 10 && OCCI_MINOR_VERSION >= 1 -// We have at least ORACLE 10g >= 10.2.0.X -#else -#error Only ORACLE 10g >= 10.2.0.X is supported ! -#endif - -// geometry types definitions -enum -{ - SDO_GTYPE_UNKNOWN = 0, - SDO_GTYPE_POINT = 1, - SDO_GTYPE_LINE = 2, - SDO_GTYPE_POLYGON = 3, - SDO_GTYPE_COLLECTION = 4, - SDO_GTYPE_MULTIPOINT = 5, - SDO_GTYPE_MULTILINE = 6, - SDO_GTYPE_MULTIPOLYGON = 7, - - SDO_GTYPE_2DPOINT = 2001, - SDO_GTYPE_2DLINE = 2002, - SDO_GTYPE_2DPOLYGON = 2003, - SDO_GTYPE_2DMULTIPOINT = 2005, - SDO_GTYPE_2DMULTILINE = 2006, - SDO_GTYPE_2DMULTIPOLYGON = 2007, - - SDO_ELEM_INFO_SIZE = 3, - - SDO_ETYPE_UNKNOWN = 0, - SDO_ETYPE_POINT = 1, - SDO_ETYPE_LINESTRING = 2, - SDO_ETYPE_POLYGON = 1003, - SDO_ETYPE_POLYGON_INTERIOR = 2003, - SDO_ETYPE_COMPOUND_LINESTRING = 4, - SDO_ETYPE_COMPOUND_POLYGON = 1005, - SDO_ETYPE_COMPOUND_POLYGON_INTERIOR = 2005, - - SDO_INTERPRETATION_POINT = 1, - SDO_INTERPRETATION_RECTANGLE = 3, - SDO_INTERPRETATION_CIRCLE = 4, - SDO_INTERPRETATION_STRAIGHT = 1, - SDO_INTERPRETATION_CIRCULAR = 2 -}; - -class occi_environment : public mapnik::singleton -{ - friend class mapnik::CreateStatic; - -public: - - oracle::occi::Environment* get_environment() - { - return env_; - } - - oracle::occi::Connection* create_connection( - const std::string& user, - const std::string& password, - const std::string& host) - { - MAPNIK_LOG_DEBUG(occi) << "occi_environment: create_connection"; - - return env_->createConnection(user, password, host); - } - - void destroy_connection(oracle::occi::Connection* conn) - { - env_->terminateConnection(conn); - } - - oracle::occi::StatelessConnectionPool* create_pool( - const std::string& user, - const std::string& password, - const std::string& host, - int max_size, - int initial_size, - int incr_size) - { - MAPNIK_LOG_DEBUG(occi) << "occi_environment: create_pool"; - - return env_->createStatelessConnectionPool( - user, - password, - host, - max_size, - initial_size, - incr_size, - oracle::occi::StatelessConnectionPool::HOMOGENEOUS); - } - - void destroy_pool(oracle::occi::StatelessConnectionPool* pool) - { - env_->terminateStatelessConnectionPool( - pool, - oracle::occi::StatelessConnectionPool::SPD_FORCE); - } - -private: - - occi_environment() - : env_(0) - { - MAPNIK_LOG_DEBUG(occi) << "occi_environment: constructor"; - - env_ = oracle::occi::Environment::createEnvironment( - (oracle::occi::Environment::Mode)(oracle::occi::Environment::OBJECT - | oracle::occi::Environment::THREADED_MUTEXED)); - RegisterClasses(env_); - } - - ~occi_environment() - { - MAPNIK_LOG_DEBUG(occi) << "occi_environment: destructor"; - - oracle::occi::Environment::terminateEnvironment(env_); - env_ = 0; - } - - oracle::occi::Environment* env_; -}; - - -class occi_connection_ptr -{ -public: - explicit occi_connection_ptr() - : pool_(0), - conn_(0), - stmt_(0), - rs_(0), - owns_connection_(false) - { - } - - ~occi_connection_ptr() - { - close_query(true); - } - - void set_pool(oracle::occi::StatelessConnectionPool* pool) - { - close_query(true); - - pool_ = pool; - conn_ = pool_->getConnection(); - owns_connection_ = true; - } - - void set_connection(oracle::occi::Connection* conn, bool owns_connection) - { - close_query(true); - - pool_ = 0; - conn_ = conn; - owns_connection_ = owns_connection; - } - - oracle::occi::ResultSet* execute_query(std::string const& s, const unsigned prefetch = 0) - { - close_query(false); - - MAPNIK_LOG_DEBUG(occi) << "occi_connection_ptr: " << s; - - stmt_ = conn_->createStatement(s); - - if (prefetch > 0) - { - stmt_->setPrefetchMemorySize(0); - stmt_->setPrefetchRowCount(prefetch); - } - - rs_ = stmt_->executeQuery(); - - return rs_; - } - -private: - void close_query(const bool release_connection) - { - if (conn_) - { - if (stmt_) - { - if (rs_) - { - stmt_->closeResultSet(rs_); - rs_ = 0; - } - - conn_->terminateStatement(stmt_); - stmt_ = 0; - } - - if (release_connection) - { - if (pool_) - { - pool_->releaseConnection(conn_); - } - else - { - if (owns_connection_) - { - occi_environment::instance().destroy_connection(conn_); - } - } - - conn_ = 0; - } - } - } - - oracle::occi::StatelessConnectionPool* pool_; - oracle::occi::Connection* conn_; - oracle::occi::Statement* stmt_; - oracle::occi::ResultSet* rs_; - bool owns_connection_; -}; - -class occi_enums -{ -public: - - static std::string resolve_gtype(int gtype); - static std::string resolve_etype(int etype); - static std::string resolve_datatype(int type_id); -}; - -#endif // OCCI_TYPES_HPP diff --git a/plugins/input/occi/spatial_classesh.h b/plugins/input/occi/spatial_classesh.h deleted file mode 100644 index 9543b4ef0..000000000 --- a/plugins/input/occi/spatial_classesh.h +++ /dev/null @@ -1,147 +0,0 @@ -#ifndef SPATIAL_CLASSESH_ORACLE -# define SPATIAL_CLASSESH_ORACLE - -#ifndef OCCI_ORACLE -# include -#endif - -class SDOPointType; -class SDOGeometry; - -/************************************************************/ -// generated declarations for the SDO_POINT_TYPE object type. -/************************************************************/ - -class SDOPointType : public oracle::occi::PObject { - -private: - - oracle::occi::Number X; - oracle::occi::Number Y; - oracle::occi::Number Z; - -public: - - oracle::occi::Number getX() const; - - void setX(const oracle::occi::Number &value); - - oracle::occi::Number getY() const; - - void setY(const oracle::occi::Number &value); - - oracle::occi::Number getZ() const; - - void setZ(const oracle::occi::Number &value); - - void *operator new(size_t size); - - void *operator new(size_t size, const oracle::occi::Connection * sess, - const OCCI_STD_NAMESPACE::string& table); - - void *operator new(size_t, void *ctxOCCI_); - - void *operator new(size_t size, const oracle::occi::Connection *sess, - const OCCI_STD_NAMESPACE::string &tableName, - const OCCI_STD_NAMESPACE::string &typeName, - const OCCI_STD_NAMESPACE::string &tableSchema, - const OCCI_STD_NAMESPACE::string &typeSchema); - - OCCI_STD_NAMESPACE::string getSQLTypeName() const; - - void getSQLTypeName(oracle::occi::Environment *env, void **schemaName, - unsigned int &schemaNameLen, void **typeName, - unsigned int &typeNameLen) const; - - SDOPointType(); - - SDOPointType(void *ctxOCCI_) : oracle::occi::PObject (ctxOCCI_) { } - - static void *readSQL(void *ctxOCCI_); - - virtual void readSQL(oracle::occi::AnyData& streamOCCI_); - - static void writeSQL(void *objOCCI_, void *ctxOCCI_); - - virtual void writeSQL(oracle::occi::AnyData& streamOCCI_); - - ~SDOPointType(); - -}; - -/************************************************************/ -// generated declarations for the SDO_GEOMETRY object type. -/************************************************************/ - -class SDOGeometry : public oracle::occi::PObject { - -private: - - oracle::occi::Number SDO_GTYPE; - oracle::occi::Number SDO_SRID; - SDOPointType * SDO_POINT; - OCCI_STD_NAMESPACE::vector< oracle::occi::Number > SDO_ELEM_INFO; - OCCI_STD_NAMESPACE::vector< oracle::occi::Number > SDO_ORDINATES; - -public: - - oracle::occi::Number getSdo_gtype() const; - - void setSdo_gtype(const oracle::occi::Number &value); - - oracle::occi::Number getSdo_srid() const; - - void setSdo_srid(const oracle::occi::Number &value); - - SDOPointType * getSdo_point() const; - - void setSdo_point(SDOPointType * value); - - OCCI_STD_NAMESPACE::vector< oracle::occi::Number >& getSdo_elem_info(); - - const OCCI_STD_NAMESPACE::vector< oracle::occi::Number >& getSdo_elem_info() const; - - void setSdo_elem_info(const OCCI_STD_NAMESPACE::vector< oracle::occi::Number > &value); - - OCCI_STD_NAMESPACE::vector< oracle::occi::Number >& getSdo_ordinates(); - - const OCCI_STD_NAMESPACE::vector< oracle::occi::Number >& getSdo_ordinates() const; - - void setSdo_ordinates(const OCCI_STD_NAMESPACE::vector< oracle::occi::Number > &value); - - void *operator new(size_t size); - - void *operator new(size_t size, const oracle::occi::Connection * sess, - const OCCI_STD_NAMESPACE::string& table); - - void *operator new(size_t, void *ctxOCCI_); - - void *operator new(size_t size, const oracle::occi::Connection *sess, - const OCCI_STD_NAMESPACE::string &tableName, - const OCCI_STD_NAMESPACE::string &typeName, - const OCCI_STD_NAMESPACE::string &tableSchema, - const OCCI_STD_NAMESPACE::string &typeSchema); - - OCCI_STD_NAMESPACE::string getSQLTypeName() const; - - void getSQLTypeName(oracle::occi::Environment *env, void **schemaName, - unsigned int &schemaNameLen, void **typeName, - unsigned int &typeNameLen) const; - - SDOGeometry(); - - SDOGeometry(void *ctxOCCI_) : oracle::occi::PObject (ctxOCCI_) { } - - static void *readSQL(void *ctxOCCI_); - - virtual void readSQL(oracle::occi::AnyData& streamOCCI_); - - static void writeSQL(void *objOCCI_, void *ctxOCCI_); - - virtual void writeSQL(oracle::occi::AnyData& streamOCCI_); - - ~SDOGeometry(); - -}; - -#endif diff --git a/plugins/input/occi/spatial_classesm.cpp b/plugins/input/occi/spatial_classesm.cpp deleted file mode 100644 index aa4ea865c..000000000 --- a/plugins/input/occi/spatial_classesm.cpp +++ /dev/null @@ -1,11 +0,0 @@ - -#ifndef SPATIAL_CLASSESM_ORACLE -# include "spatial_classesm.h" -#endif - -void RegisterClasses(oracle::occi::Environment* envOCCI_) -{ - oracle::occi::Map *mapOCCI_ = envOCCI_->getMap(); - mapOCCI_->put("MDSYS.SDO_POINT_TYPE", &SDOPointType::readSQL, &SDOPointType::writeSQL); - mapOCCI_->put("MDSYS.SDO_GEOMETRY", &SDOGeometry::readSQL, &SDOGeometry::writeSQL); -} diff --git a/plugins/input/occi/spatial_classesm.h b/plugins/input/occi/spatial_classesm.h deleted file mode 100644 index 78ca1c4e5..000000000 --- a/plugins/input/occi/spatial_classesm.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef SPATIAL_CLASSESM_ORACLE -# define SPATIAL_CLASSESM_ORACLE - -#ifndef OCCI_ORACLE -# include -#endif - -#ifndef SPATIAL_CLASSESH_ORACLE -# include "spatial_classesh.h" -#endif - -void RegisterClasses(oracle::occi::Environment* envOCCI_); - -#endif diff --git a/plugins/input/occi/spatial_classeso.cpp b/plugins/input/occi/spatial_classeso.cpp deleted file mode 100644 index cbc704656..000000000 --- a/plugins/input/occi/spatial_classeso.cpp +++ /dev/null @@ -1,312 +0,0 @@ -#ifndef SPATIAL_CLASSESH_ORACLE -# include "spatial_classesh.h" -#endif - - -/*****************************************************************/ -// generated method implementations for the SDO_POINT_TYPE object type. -/*****************************************************************/ - -oracle::occi::Number SDOPointType::getX() const -{ - return X; -} - -void SDOPointType::setX(const oracle::occi::Number &value) -{ - X = value; -} - -oracle::occi::Number SDOPointType::getY() const -{ - return Y; -} - -void SDOPointType::setY(const oracle::occi::Number &value) -{ - Y = value; -} - -oracle::occi::Number SDOPointType::getZ() const -{ - return Z; -} - -void SDOPointType::setZ(const oracle::occi::Number &value) -{ - Z = value; -} - -void *SDOPointType::operator new(size_t size) -{ - return oracle::occi::PObject::operator new(size); -} - -void *SDOPointType::operator new(size_t size, const oracle::occi::Connection * sess, - const OCCI_STD_NAMESPACE::string& table) -{ - return oracle::occi::PObject::operator new(size, sess, table, - (char *) "MDSYS.SDO_POINT_TYPE"); -} - -void *SDOPointType::operator new(size_t size, void *ctxOCCI_) -{ - return oracle::occi::PObject::operator new(size, ctxOCCI_); -} - -void *SDOPointType::operator new(size_t size, - const oracle::occi::Connection *sess, - const OCCI_STD_NAMESPACE::string &tableName, - const OCCI_STD_NAMESPACE::string &typeName, - const OCCI_STD_NAMESPACE::string &tableSchema, - const OCCI_STD_NAMESPACE::string &typeSchema) -{ - return oracle::occi::PObject::operator new(size, sess, tableName, - typeName, tableSchema, typeSchema); -} - -OCCI_STD_NAMESPACE::string SDOPointType::getSQLTypeName() const -{ - return OCCI_STD_NAMESPACE::string("MDSYS.SDO_POINT_TYPE"); -} - -void SDOPointType::getSQLTypeName(oracle::occi::Environment *env, void **schemaName, - unsigned int &schemaNameLen, void **typeName, unsigned int &typeNameLen) const -{ - PObject::getSQLTypeName(env, &SDOPointType::readSQL, schemaName, - schemaNameLen, typeName, typeNameLen); -} - -SDOPointType::SDOPointType() -{ -} - -void *SDOPointType::readSQL(void *ctxOCCI_) -{ - SDOPointType *objOCCI_ = new(ctxOCCI_) SDOPointType(ctxOCCI_); - oracle::occi::AnyData streamOCCI_(ctxOCCI_); - - try - { - if (streamOCCI_.isNull()) - objOCCI_->setNull(); - else - objOCCI_->readSQL(streamOCCI_); - } - catch (oracle::occi::SQLException& excep) - { - delete objOCCI_; - excep.setErrorCtx(ctxOCCI_); - return (void *)nullptr; - } - return (void *)objOCCI_; -} - -void SDOPointType::readSQL(oracle::occi::AnyData& streamOCCI_) -{ - X = streamOCCI_.getNumber(); - Y = streamOCCI_.getNumber(); - Z = streamOCCI_.getNumber(); -} - -void SDOPointType::writeSQL(void *objectOCCI_, void *ctxOCCI_) -{ - SDOPointType *objOCCI_ = (SDOPointType *) objectOCCI_; - oracle::occi::AnyData streamOCCI_(ctxOCCI_); - - try - { - if (objOCCI_->isNull()) - streamOCCI_.setNull(); - else - objOCCI_->writeSQL(streamOCCI_); - } - catch (oracle::occi::SQLException& excep) - { - excep.setErrorCtx(ctxOCCI_); - } - return; -} - -void SDOPointType::writeSQL(oracle::occi::AnyData& streamOCCI_) -{ - streamOCCI_.setNumber(X); - streamOCCI_.setNumber(Y); - streamOCCI_.setNumber(Z); -} - -SDOPointType::~SDOPointType() -{ -} - -/*****************************************************************/ -// generated method implementations for the SDO_GEOMETRY object type. -/*****************************************************************/ - -oracle::occi::Number SDOGeometry::getSdo_gtype() const -{ - return SDO_GTYPE; -} - -void SDOGeometry::setSdo_gtype(const oracle::occi::Number &value) -{ - SDO_GTYPE = value; -} - -oracle::occi::Number SDOGeometry::getSdo_srid() const -{ - return SDO_SRID; -} - -void SDOGeometry::setSdo_srid(const oracle::occi::Number &value) -{ - SDO_SRID = value; -} - -SDOPointType * SDOGeometry::getSdo_point() const -{ - return SDO_POINT; -} - -void SDOGeometry::setSdo_point(SDOPointType * value) -{ - SDO_POINT = value; -} - -OCCI_STD_NAMESPACE::vector< oracle::occi::Number >& SDOGeometry::getSdo_elem_info() -{ - return SDO_ELEM_INFO; -} - -const OCCI_STD_NAMESPACE::vector< oracle::occi::Number >& SDOGeometry::getSdo_elem_info() const -{ - return SDO_ELEM_INFO; -} - -void SDOGeometry::setSdo_elem_info(const OCCI_STD_NAMESPACE::vector< oracle::occi::Number > &value) -{ - SDO_ELEM_INFO = value; -} - -OCCI_STD_NAMESPACE::vector< oracle::occi::Number >& SDOGeometry::getSdo_ordinates() -{ - return SDO_ORDINATES; -} - -const OCCI_STD_NAMESPACE::vector< oracle::occi::Number >& SDOGeometry::getSdo_ordinates() const -{ - return SDO_ORDINATES; -} - -void SDOGeometry::setSdo_ordinates(const OCCI_STD_NAMESPACE::vector< oracle::occi::Number > &value) -{ - SDO_ORDINATES = value; -} - -void *SDOGeometry::operator new(size_t size) -{ - return oracle::occi::PObject::operator new(size); -} - -void *SDOGeometry::operator new(size_t size, const oracle::occi::Connection * sess, - const OCCI_STD_NAMESPACE::string& table) -{ - return oracle::occi::PObject::operator new(size, sess, table, - (char *) "MDSYS.SDO_GEOMETRY"); -} - -void *SDOGeometry::operator new(size_t size, void *ctxOCCI_) -{ - return oracle::occi::PObject::operator new(size, ctxOCCI_); -} - -void *SDOGeometry::operator new(size_t size, - const oracle::occi::Connection *sess, - const OCCI_STD_NAMESPACE::string &tableName, - const OCCI_STD_NAMESPACE::string &typeName, - const OCCI_STD_NAMESPACE::string &tableSchema, - const OCCI_STD_NAMESPACE::string &typeSchema) -{ - return oracle::occi::PObject::operator new(size, sess, tableName, - typeName, tableSchema, typeSchema); -} - -OCCI_STD_NAMESPACE::string SDOGeometry::getSQLTypeName() const -{ - return OCCI_STD_NAMESPACE::string("MDSYS.SDO_GEOMETRY"); -} - -void SDOGeometry::getSQLTypeName(oracle::occi::Environment *env, void **schemaName, - unsigned int &schemaNameLen, void **typeName, unsigned int &typeNameLen) const -{ - PObject::getSQLTypeName(env, &SDOGeometry::readSQL, schemaName, - schemaNameLen, typeName, typeNameLen); -} - -SDOGeometry::SDOGeometry() -{ - SDO_POINT = (SDOPointType *) 0; -} - -void *SDOGeometry::readSQL(void *ctxOCCI_) -{ - SDOGeometry *objOCCI_ = new(ctxOCCI_) SDOGeometry(ctxOCCI_); - oracle::occi::AnyData streamOCCI_(ctxOCCI_); - - try - { - if (streamOCCI_.isNull()) - objOCCI_->setNull(); - else - objOCCI_->readSQL(streamOCCI_); - } - catch (oracle::occi::SQLException& excep) - { - delete objOCCI_; - excep.setErrorCtx(ctxOCCI_); - return (void *)nullptr; - } - return (void *)objOCCI_; -} - -void SDOGeometry::readSQL(oracle::occi::AnyData& streamOCCI_) -{ - SDO_GTYPE = streamOCCI_.getNumber(); - SDO_SRID = streamOCCI_.getNumber(); - SDO_POINT = (SDOPointType *) streamOCCI_.getObject(&SDOPointType::readSQL); - oracle::occi::getVector(streamOCCI_, SDO_ELEM_INFO); - oracle::occi::getVector(streamOCCI_, SDO_ORDINATES); -} - -void SDOGeometry::writeSQL(void *objectOCCI_, void *ctxOCCI_) -{ - SDOGeometry *objOCCI_ = (SDOGeometry *) objectOCCI_; - oracle::occi::AnyData streamOCCI_(ctxOCCI_); - - try - { - if (objOCCI_->isNull()) - streamOCCI_.setNull(); - else - objOCCI_->writeSQL(streamOCCI_); - } - catch (oracle::occi::SQLException& excep) - { - excep.setErrorCtx(ctxOCCI_); - } - return; -} - -void SDOGeometry::writeSQL(oracle::occi::AnyData& streamOCCI_) -{ - streamOCCI_.setNumber(SDO_GTYPE); - streamOCCI_.setNumber(SDO_SRID); - streamOCCI_.setObject(SDO_POINT); - oracle::occi::setVector(streamOCCI_, SDO_ELEM_INFO); - oracle::occi::setVector(streamOCCI_, SDO_ORDINATES); -} - -SDOGeometry::~SDOGeometry() -{ - delete SDO_POINT; -} diff --git a/plugins/input/occi/spatial_types.typ b/plugins/input/occi/spatial_types.typ deleted file mode 100644 index 5c9c0a72e..000000000 --- a/plugins/input/occi/spatial_types.typ +++ /dev/null @@ -1,4 +0,0 @@ -TYPE MDSYS.SDO_POINT_TYPE AS SDOPointType -TYPE MDSYS.SDO_GEOMETRY AS SDOGeometry - - diff --git a/plugins/input/osm/basiccurl.cpp b/plugins/input/osm/basiccurl.cpp deleted file mode 100755 index fd8361453..000000000 --- a/plugins/input/osm/basiccurl.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 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 - * - *****************************************************************************/ - -#include "basiccurl.h" - -#include -#include - -CURL_LOAD_DATA* grab_http_response(const char* url) -{ - CURL_LOAD_DATA* data; - - CURL* curl = curl_easy_init(); - - if(curl) - { - data = do_grab(curl, url); - curl_easy_cleanup(curl); - return data; - } - return nullptr; -} - -CURL_LOAD_DATA* do_grab(CURL* curl,const char* url) -{ - CURL_LOAD_DATA* data = (CURL_LOAD_DATA*)malloc(sizeof(CURL_LOAD_DATA)); - data->data = nullptr; - data->nbytes = 0; - - curl_easy_setopt(curl, CURLOPT_URL, url); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, response_callback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, data); - - CURLcode res = curl_easy_perform(curl); - if (res !=0) { - std::clog << "error grabbing data\n"; - } - - return data; -} - -size_t response_callback(void* ptr, size_t size, size_t nmemb, void* d) -{ - size_t rsize = size * nmemb; - CURL_LOAD_DATA* data = (CURL_LOAD_DATA*)d; - - // fprintf(stderr,"rsize is %d\n", rsize); - - data->data = (char*)realloc(data->data, (data->nbytes + rsize) * sizeof(char)); - std::memcpy(&(data->data[data->nbytes]), ptr, rsize); - data->nbytes += rsize; - - // fprintf(stderr,"data->nbytes is %d\n", data->nbytes); - - return rsize; -} diff --git a/plugins/input/osm/basiccurl.h b/plugins/input/osm/basiccurl.h deleted file mode 100755 index 7ab529783..000000000 --- a/plugins/input/osm/basiccurl.h +++ /dev/null @@ -1,40 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2011 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 BASICCURL_H -#define BASICCURL_H - -#include -#include -#include - -typedef struct -{ - char *data; - int nbytes; -} CURL_LOAD_DATA; - -CURL_LOAD_DATA *grab_http_response(const char *url); -CURL_LOAD_DATA *do_grab(CURL *curl, const char *url); -size_t response_callback(void *ptr ,size_t size, size_t nmemb, void *data); - -#endif // BASICCURL_H diff --git a/plugins/input/osm/build.py b/plugins/input/osm/build.py deleted file mode 100644 index 061fc859c..000000000 --- a/plugins/input/osm/build.py +++ /dev/null @@ -1,69 +0,0 @@ -# -# This file is part of Mapnik (c++ mapping toolkit) -# -# Copyright (C) 2015 Artem Pavlenko -# -# Mapnik 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 -# -# - -Import ('plugin_base') -Import ('env') -from copy import copy - -PLUGIN_NAME = 'osm' - -plugin_env = plugin_base.Clone() - -plugin_sources = Split( - """ - %(PLUGIN_NAME)s.cpp - %(PLUGIN_NAME)s_datasource.cpp - %(PLUGIN_NAME)s_featureset.cpp - osmparser.cpp - dataset_deliverer.cpp - """ % locals() -) - -plugin_env['LIBS'] = [] -plugin_env.Append(LIBS='xml2') - -# Link Library to Dependencies -libraries = copy(plugin_env['LIBS']) -libraries.append(env['ICU_LIB_NAME']) -libraries.append('boost_system%s' % env['BOOST_APPEND']) - -if env['PLUGIN_LINKING'] == 'shared': - libraries.append(env['MAPNIK_NAME']) - - TARGET = plugin_env.SharedLibrary('../%s' % PLUGIN_NAME, - SHLIBPREFIX='', - SHLIBSUFFIX='.input', - source=plugin_sources, - LIBS=libraries) - - # if the plugin links to libmapnik ensure it is built first - Depends(TARGET, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME'])) - - if 'uninstall' not in COMMAND_LINE_TARGETS: - env.Install(env['MAPNIK_INPUT_PLUGINS_DEST'], TARGET) - env.Alias('install', env['MAPNIK_INPUT_PLUGINS_DEST']) - -plugin_obj = { - 'LIBS': libraries, - 'SOURCES': plugin_sources, -} - -Return('plugin_obj') diff --git a/plugins/input/osm/dataset_deliverer.cpp b/plugins/input/osm/dataset_deliverer.cpp deleted file mode 100644 index 6d7fe31d9..000000000 --- a/plugins/input/osm/dataset_deliverer.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 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 - -// std -#include - -#include "dataset_deliverer.h" - -osm_dataset * dataset_deliverer::dataset = nullptr; -std::string dataset_deliverer::last_bbox = ""; -std::string dataset_deliverer::last_filename = ""; - -osm_dataset* dataset_deliverer::load_from_file(const string& file, const string& parser) -{ - // Only actually load from file if we haven't done so already - if (dataset == nullptr) - { - if (!mapnik::util::exists(file)) - { - throw mapnik::datasource_exception("OSM Plugin: '" + file + "' does not exist"); - } - - dataset = new osm_dataset; - if (dataset->load(file.c_str(), parser) == false) - { - return nullptr; - } - - atexit(dataset_deliverer::release); - last_filename = file; - } - else if(file != last_filename) - { - dataset = new osm_dataset; - if (dataset->load(file.c_str(), parser) == false) - { - return nullptr; - } - last_filename = file; - } - return dataset; -} diff --git a/plugins/input/osm/dataset_deliverer.h b/plugins/input/osm/dataset_deliverer.h deleted file mode 100644 index dde269b65..000000000 --- a/plugins/input/osm/dataset_deliverer.h +++ /dev/null @@ -1,47 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2011 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 DATASET_DELIVERER_H -#define DATASET_DELIVERER_H - -#include "osm.h" -#include - -using namespace std; - -class dataset_deliverer -{ -private: - static osm_dataset* dataset; - static std::string last_bbox; - static std::string last_filename; - -public: - static osm_dataset *load_from_file(const string&, const string&); - - static void release() - { - delete dataset; - } -}; - -#endif // DATASET_DELIVERER_H diff --git a/plugins/input/osm/osm.cpp b/plugins/input/osm/osm.cpp deleted file mode 100644 index 76d3bac18..000000000 --- a/plugins/input/osm/osm.cpp +++ /dev/null @@ -1,230 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 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 - * - *****************************************************************************/ - -#include "osm.h" -#include "osmparser.h" - -#include - -#include -#include -#include -#include -#include -#include - -polygon_types osm_way::ptypes; - -bool osm_dataset::load(const char* filename,std::string const& parser) -{ - if (parser == "libxml2") - { - return osmparser::parse(this, filename); - } - return false; -} - -osm_dataset::~osm_dataset() -{ - clear(); -} - -void osm_dataset::clear() -{ - MAPNIK_LOG_DEBUG(osm) << "osm_dataset: Clear"; - - MAPNIK_LOG_DEBUG(osm) << "osm_dataset: -- Deleting ways"; - for (unsigned int count = 0; count < ways.size(); ++count) - { - delete ways[count]; - ways[count] = nullptr; - } - ways.clear(); - - MAPNIK_LOG_DEBUG(osm) << "osm_dataset: -- Deleting nodes"; - for (unsigned int count = 0; count < nodes.size(); ++count) - { - delete nodes[count]; - nodes[count] = nullptr; - } - nodes.clear(); - - MAPNIK_LOG_DEBUG(osm) << "osm_dataset: Clear done"; -} - -std::string osm_dataset::to_string() -{ - std::string result; - - for (unsigned int count = 0; count < nodes.size(); ++count) - { - result += nodes[count]->to_string(); - } - - for (unsigned int count = 0; count < ways.size(); ++count) - { - result += ways[count]->to_string(); - } - - return result; -} - -bounds osm_dataset::get_bounds() -{ - bounds b (-180, -90, 180, 90); - for (unsigned int count = 0; count < nodes.size(); ++count) - { - if(nodes[count]->lon > b.w) b.w = nodes[count]->lon; - if(nodes[count]->lon < b.e) b.e = nodes[count]->lon; - if(nodes[count]->lat > b.s) b.s = nodes[count]->lat; - if(nodes[count]->lat < b.n) b.n = nodes[count]->lat; - } - return b; -} - -osm_node* osm_dataset::next_node() -{ - if (node_i != nodes.end()) - { - return *(node_i++); - } - return nullptr; -} - -osm_way* osm_dataset::next_way() -{ - if (way_i != ways.end()) - { - return *(way_i++); - } - return nullptr; -} - -osm_item* osm_dataset::next_item() -{ - osm_item* item = nullptr; - if (next_item_mode == Node) - { - item = next_node(); - if (item == nullptr) - { - next_item_mode = Way; - rewind_ways(); - item = next_way(); - } - } - else - { - item = next_way(); - } - return item; -} - -std::set osm_dataset::get_keys() -{ - std::set keys; - for (unsigned int count = 0; count < nodes.size(); ++count) - { - for (std::map::iterator i = nodes[count]->keyvals.begin(); - i != nodes[count]->keyvals.end(); i++) - { - keys.insert(i->first); - } - } - - for (unsigned int count = 0; count < ways.size(); ++count) - { - for (std::map::iterator i = ways[count]->keyvals.begin(); - i != ways[count]->keyvals.end(); i++) - { - keys.insert(i->first); - } - } - return keys; -} - - -std::string osm_item::to_string() -{ - std::ostringstream strm; - strm << "id=" << id << std::endl << "Keyvals: " << std::endl; - - for (std::map::iterator i = keyvals.begin(); - i != keyvals.end(); i++) - { - strm << "Key " << i->first << " Value " << i->second << std::endl; - } - - return strm.str(); -} - -std::string osm_node::to_string() -{ - std::ostringstream strm; - strm << "Node: " << osm_item::to_string() << " lat=" << lat <<" lon=" <id << " "; - } - } - - strm << std::endl; - return strm.str(); -} - -bounds osm_way::get_bounds() -{ - bounds b (-180, -90, 180, 90); - - for (unsigned int count = 0; count < nodes.size(); ++count) - { - if(nodes[count]->lon > b.w) b.w = nodes[count]->lon; - if(nodes[count]->lon < b.e) b.e = nodes[count]->lon; - if(nodes[count]->lat > b.s) b.s = nodes[count]->lat; - if(nodes[count]->lat < b.n) b.n = nodes[count]->lat; - } - return b; -} - -bool osm_way::is_polygon() -{ - for (unsigned int count = 0; count < ptypes.ptypes.size(); ++count) - { - if (keyvals.find(ptypes.ptypes[count].first) != keyvals.end() && - (ptypes.ptypes[count].second.empty() || keyvals[ptypes.ptypes[count].first] == ptypes.ptypes[count].second)) - { - return true; - } - } - - return false; -} diff --git a/plugins/input/osm/osm.h b/plugins/input/osm/osm.h deleted file mode 100644 index 86a48a9af..000000000 --- a/plugins/input/osm/osm.h +++ /dev/null @@ -1,136 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2011 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 OSM_H -#define OSM_H - -#include -#include -#include -#include -#include -#include - -struct bounds -{ - double w, s, e, n; - bounds() { w = -180; s = -90; e = 180; n = 90; } - bounds(double w_, double s_, double e_, double n_) - { - this->w = w_; - this->s = s_; - this->e = e_; - this->n = n_; - } -}; - -class polygon_types -{ -public: - std::vector > ptypes; - - polygon_types() - { - ptypes.push_back(std::pair("water", "")); - ptypes.push_back(std::pair("aeroway", "")); - ptypes.push_back(std::pair("building", "")); - ptypes.push_back(std::pair("natural", "wood")); - ptypes.push_back(std::pair("natural", "water")); - ptypes.push_back(std::pair("natural", "heath")); - ptypes.push_back(std::pair("natural", "marsh")); - ptypes.push_back(std::pair("military", "danger_area")); - ptypes.push_back(std::pair("landuse", "forest")); - ptypes.push_back(std::pair("landuse", "industrial")); - ptypes.push_back(std::pair("leisure", "park")); - ptypes.push_back(std::pair("area", "yes")); - } -}; - -struct osm_item -{ - mapnik::value_integer id; - std::map keyvals; - virtual std::string to_string(); - virtual ~osm_item() {} -}; - -struct osm_node : public osm_item -{ - double lat, lon; - std::string to_string(); -}; - -struct osm_way : public osm_item -{ - std::vector nodes; - std::string to_string(); - bounds get_bounds(); - bool is_polygon(); - static polygon_types ptypes; -}; - -class osm_dataset -{ -public: - osm_dataset() - { - node_i = nodes.begin(); - way_i = ways.begin(); - next_item_mode = Node; - } - - osm_dataset(const char* name) - { - node_i = nodes.begin(); - way_i = ways.begin(); - next_item_mode = Node; - load(name); - } - - ~osm_dataset(); - - bool load(const char* name, std::string const& parser = "libxml2"); - void clear(); - void add_node(osm_node* n) { nodes.push_back(n); } - void add_way(osm_way* w) { ways.push_back(w); } - std::string to_string(); - bounds get_bounds(); - std::set get_keys(); - void rewind_nodes() { node_i = nodes.begin(); } - void rewind_ways() { way_i = ways.begin(); } - void rewind() { rewind_nodes(); rewind_ways(); next_item_mode = Node; } - osm_node * next_node(); - osm_way * next_way(); - osm_item * next_item(); - bool current_item_is_node() { return next_item_mode == Node; } - bool current_item_is_way() { return next_item_mode == Way; } - -private: - int next_item_mode; - enum { Node, Way }; - std::vector::iterator node_i; - std::vector::iterator way_i; - std::vector nodes; - std::vector ways; -}; - -#endif // OSM_H diff --git a/plugins/input/osm/osm_datasource.cpp b/plugins/input/osm/osm_datasource.cpp deleted file mode 100644 index 4bbe1bfde..000000000 --- a/plugins/input/osm/osm_datasource.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 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 - * - *****************************************************************************/ - -// stl -#include -#include - -// mapnik -#include -#include -#include -#include - -// boost - -#include "osm_datasource.hpp" -#include "osm_featureset.hpp" -#include "dataset_deliverer.h" -#include "osmtagtypes.h" -#include "osmparser.h" - -using mapnik::String; -using mapnik::Double; -using mapnik::Integer; -using mapnik::datasource_exception; -using mapnik::filter_in_box; -using mapnik::filter_at_point; -using mapnik::attribute_descriptor; - -DATASOURCE_PLUGIN(osm_datasource) - -osm_datasource::osm_datasource(const parameters& params) - : datasource (params), - extent_(), - type_(datasource::Vector), - desc_(osm_datasource::name(), *params.get("encoding", "utf-8")) -{ - osm_data_ = nullptr; - std::string osm_filename = *params.get("file", ""); - std::string parser = *params.get("parser", "libxml2"); - std::string url = *params.get("url", ""); - std::string bbox = *params.get("bbox", ""); - - // load the data - if (url != "" && bbox != "") - { - throw datasource_exception("Error loading from URL is no longer supported (removed in >= Mapnik 2.3.x"); - } - else if (osm_filename != "") - { - // if we supplied a filename, load from file - if ((osm_data_ = dataset_deliverer::load_from_file(osm_filename, parser)) == nullptr) - { - std::string s("OSM Plugin: Error loading from file '"); - s += osm_filename + "'"; - throw datasource_exception(s); - } - } - else - { - throw datasource_exception("OSM Plugin: Neither 'file' nor 'url' and 'bbox' specified"); - } - - - osm_tag_types tagtypes; - tagtypes.add_type("maxspeed", mapnik::Integer); - tagtypes.add_type("z_order", mapnik::Integer); - - osm_data_->rewind(); - - // Need code to get the attributes of all the data - std::set keys = osm_data_->get_keys(); - - // Add the attributes to the datasource descriptor - assume they are - // all of type String - for (auto const& key : keys) - { - desc_.add_descriptor(attribute_descriptor(key, tagtypes.get_type(key))); - } - // Get the bounds of the data and set extent_ accordingly - bounds b = osm_data_->get_bounds(); - extent_ = box2d(b.w,b.s,b.e,b.n); -} - -osm_datasource::~osm_datasource() -{ - // Do not do as is now static variable and cleaned up at exit - //delete osm_data_; -} - -const char * osm_datasource::name() -{ - return "osm"; -} - -mapnik::datasource::datasource_t osm_datasource::type() const -{ - return type_; -} - -layer_descriptor osm_datasource::get_descriptor() const -{ - return desc_; -} - -featureset_ptr osm_datasource::features(const query& q) const -{ - filter_in_box filter(q.get_bbox()); - // so we need to filter osm features by bbox here... - - return std::make_shared >(filter, - osm_data_, - q.property_names(), - desc_.get_encoding()); -} - -featureset_ptr osm_datasource::features_at_point(coord2d const& pt, double tol) const -{ - filter_at_point filter(pt); - // collect all attribute names - std::set names; - for (auto const& elem : desc_.get_descriptors()) - { - names.insert(elem.get_name()); - } - return std::make_shared >(filter, - osm_data_, - names, - desc_.get_encoding()); -} - -box2d osm_datasource::envelope() const -{ - return extent_; -} - -boost::optional osm_datasource::get_geometry_type() const -{ - return boost::optional(mapnik::datasource_geometry_t::Collection); -} diff --git a/plugins/input/osm/osm_datasource.hpp b/plugins/input/osm/osm_datasource.hpp deleted file mode 100644 index 1bd8bb387..000000000 --- a/plugins/input/osm/osm_datasource.hpp +++ /dev/null @@ -1,73 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 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 OSM_DATASOURCE_HPP -#define OSM_DATASOURCE_HPP - -// mapnik -#include -#include -#include -#include -#include -#include -#include - -// boost -#include -#include - -// stl -#include -#include - -#include "osm.h" - -using mapnik::datasource; -using mapnik::parameters; -using mapnik::query; -using mapnik::featureset_ptr; -using mapnik::layer_descriptor; -using mapnik::coord2d; -using mapnik::box2d; - -class osm_datasource : public datasource -{ -public: - osm_datasource(const parameters& params); - virtual ~osm_datasource(); - mapnik::datasource::datasource_t type() const; - static const char * name(); - featureset_ptr features(const query& q) const; - featureset_ptr features_at_point(coord2d const& pt, double tol = 0) const; - box2d envelope() const; - boost::optional get_geometry_type() const; - layer_descriptor get_descriptor() const; - -private: - box2d extent_; - osm_dataset* osm_data_; - mapnik::datasource::datasource_t type_; - layer_descriptor desc_; -}; - -#endif // OSM_DATASOURCE_HPP diff --git a/plugins/input/osm/osm_featureset.cpp b/plugins/input/osm/osm_featureset.cpp deleted file mode 100644 index 46e796681..000000000 --- a/plugins/input/osm/osm_featureset.cpp +++ /dev/null @@ -1,132 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 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 "osm_featureset.hpp" - -using mapnik::feature_ptr; -using mapnik::feature_factory; - -template -osm_featureset::osm_featureset(const filterT& filter, - osm_dataset* dataset, - const std::set& - attribute_names, - std::string const& encoding) - : filter_(filter), - query_ext_(), - tr_(new transcoder(encoding)), - dataset_ (dataset), - attribute_names_ (attribute_names), - ctx_(std::make_shared()) -{ - dataset_->rewind(); -} - -template -feature_ptr osm_featureset::next() -{ - feature_ptr feature; - - osm_item* cur_item = dataset_->next_item(); - if (!cur_item) return feature_ptr(); - if (dataset_->current_item_is_node()) - { - feature = feature_factory::create(ctx_, cur_item->id); - double lat = static_cast(cur_item)->lat; - double lon = static_cast(cur_item)->lon; - feature->set_geometry(mapnik::geometry::point(lon,lat)); - } - else if (dataset_->current_item_is_way()) - { - // Loop until we find a feature which passes the filter - while (cur_item) - { - bounds b = static_cast(cur_item)->get_bounds(); - if (filter_.pass(box2d(b.w, b.s, b.e, b.n)) - && - static_cast(cur_item)->nodes.size()) break; - cur_item = dataset_->next_item(); - } - - if (!cur_item) return feature_ptr(); - feature = feature_factory::create(ctx_, cur_item->id); - if (static_cast(cur_item)->is_polygon()) - { - mapnik::geometry::linear_ring ring; - for (unsigned int count = 0; - count < static_cast(cur_item)->nodes.size(); - count++) - { - ring.add_coord(static_cast(cur_item)->nodes[count]->lon, - static_cast(cur_item)->nodes[count]->lat); - } - mapnik::geometry::polygon geom; - geom.set_exterior_ring(std::move(ring)); - mapnik::geometry::correct(geom); - feature->set_geometry(std::move(geom)); - } - else - { - mapnik::geometry::line_string geom; - for (unsigned int count = 0; - count < static_cast(cur_item)->nodes.size(); - count++) - { - geom.add_coord(static_cast(cur_item)->nodes[count]->lon, - static_cast(cur_item)->nodes[count]->lat); - } - feature->set_geometry(std::move(geom)); - } - } - else - { - MAPNIK_LOG_ERROR(osm_featureset) << "Current item is neither node nor way.\n"; - } - - std::set::const_iterator itr = attribute_names_.begin(); - std::set::const_iterator end = attribute_names_.end(); - std::map::iterator end_keyvals = cur_item->keyvals.end(); - for (; itr != end; itr++) - { - std::map::iterator i = cur_item->keyvals.find(*itr); - if (i != end_keyvals) - { - feature->put_new(i->first, tr_->transcode(i->second.c_str())); - } - } - return feature; -} - -template -osm_featureset::~osm_featureset() {} - -template class osm_featureset; -template class osm_featureset; diff --git a/plugins/input/osm/osm_featureset.hpp b/plugins/input/osm/osm_featureset.hpp deleted file mode 100644 index 7eed2e636..000000000 --- a/plugins/input/osm/osm_featureset.hpp +++ /dev/null @@ -1,72 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 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 OSM_FS_HH -#define OSM_FS_HH - -// stl -#include - -// boost - - -// mapnik -#include -#include -#include -#include -#include - -#include "osm.h" - -using mapnik::Featureset; -using mapnik::box2d; -using mapnik::feature_ptr; -using mapnik::transcoder; - -template -class osm_featureset : public Featureset -{ -public: - osm_featureset(const filterT& filter, - osm_dataset* dataset, - const std::set& attribute_names, - std::string const& encoding); - virtual ~osm_featureset(); - feature_ptr next(); - -private: - filterT filter_; - box2d query_ext_; - const std::unique_ptr tr_; - std::vector attr_ids_; - mutable box2d feature_ext_; - mutable int total_geom_size; - osm_dataset *dataset_; - std::set attribute_names_; - mapnik::context_ptr ctx_; - - osm_featureset(const osm_featureset&); - const osm_featureset& operator=(const osm_featureset&); -}; - -#endif // OSM_FS_HH diff --git a/plugins/input/osm/osmparser.cpp b/plugins/input/osm/osmparser.cpp deleted file mode 100644 index b6c3d9bc2..000000000 --- a/plugins/input/osm/osmparser.cpp +++ /dev/null @@ -1,150 +0,0 @@ -// much of this is based on osm2pgsql - -#include "osmparser.h" -#include "osm.h" -#include -#include -#include - -osm_item* osmparser::cur_item=nullptr; -mapnik::value_integer osmparser::curID=0; -bool osmparser::in_node=false, osmparser::in_way=false; -osm_dataset* osmparser::components=nullptr; -std::string osmparser::error=""; -std::map osmparser::tmp_node_store=std::map(); - -void osmparser::processNode(xmlTextReaderPtr reader) -{ - xmlChar *name = xmlTextReaderName(reader); - if(name==nullptr) - name=xmlStrdup(BAD_CAST "--"); - - switch(xmlTextReaderNodeType(reader)) - { - case XML_READER_TYPE_ELEMENT: - startElement(reader,name); - break; - - case XML_READER_TYPE_END_ELEMENT: - endElement(name); - } - xmlFree(name); -} - -void osmparser::startElement(xmlTextReaderPtr reader, const xmlChar *name) -{ - std::string tags; - xmlChar *xid, *xlat, *xlon, *xk, *xv; - - if(xmlStrEqual(name,BAD_CAST "node")) - { - curID = 0; - in_node = true; - osm_node *node=new osm_node; - xlat=xmlTextReaderGetAttribute(reader,BAD_CAST "lat"); - xlon=xmlTextReaderGetAttribute(reader,BAD_CAST "lon"); - xid=xmlTextReaderGetAttribute(reader,BAD_CAST "id"); - assert(xlat); - assert(xlon); - assert(xid); - node->lat=atof((char*)xlat); - node->lon=atof((char*)xlon); - mapnik::util::string2int((char *)xid, node->id); - cur_item = node; - tmp_node_store[node->id] = node; - xmlFree(xid); - xmlFree(xlon); - xmlFree(xlat); - } - else if (xmlStrEqual(name,BAD_CAST "way")) - { - curID=0; - in_way = true; - osm_way *way=new osm_way; - xid=xmlTextReaderGetAttribute(reader,BAD_CAST "id"); - assert(xid); - mapnik::util::string2int((char *)xid, way->id); - cur_item = way; - xmlFree(xid); - } - else if (xmlStrEqual(name,BAD_CAST "nd")) - { - xid=xmlTextReaderGetAttribute(reader,BAD_CAST "ref"); - assert(xid); - mapnik::value_integer ndid; - mapnik::util::string2int((char *)xid, ndid); - if(tmp_node_store.find(ndid)!=tmp_node_store.end()) - { - (static_cast(cur_item))->nodes.push_back - (tmp_node_store[ndid]); - } - xmlFree(xid); - } - else if (xmlStrEqual(name,BAD_CAST "tag")) - { - std::string key="", value=""; - xk = xmlTextReaderGetAttribute(reader,BAD_CAST "k"); - xv = xmlTextReaderGetAttribute(reader,BAD_CAST "v"); - assert(xk); - assert(xv); - cur_item->keyvals[(char*)xk] = (char*)xv; - xmlFree(xk); - xmlFree(xv); - } - if (xmlTextReaderIsEmptyElement(reader)) - { - // Fake endElement for empty nodes - endElement(name); - } -} - -void osmparser::endElement(const xmlChar* name) -{ - if(xmlStrEqual(name,BAD_CAST "node")) - { - in_node = false; - components->add_node(static_cast(cur_item)); - } - else if(xmlStrEqual(name,BAD_CAST "way")) - { - in_way = false; - components->add_way(static_cast(cur_item)); - } -} - -bool osmparser::parse(osm_dataset *ds, const char* filename) -{ - components=ds; - xmlTextReaderPtr reader = xmlNewTextReaderFilename(filename); - int ret=do_parse(reader); - xmlFreeTextReader(reader); - return (ret==0) ? true:false; -} - -bool osmparser::parse(osm_dataset *ds,char* data, int nbytes) -{ - // from cocoasamurai.blogspot.com/2008/10/getting-some-xml-love-with- - // libxml2.html, converted from Objective-C to straight C - - components=ds; - xmlTextReaderPtr reader = xmlReaderForMemory(data,nbytes,nullptr,nullptr,0); - int ret=do_parse(reader); - xmlFreeTextReader(reader); - return (ret==0) ? true:false; -} - - -int osmparser::do_parse(xmlTextReaderPtr reader) -{ - int ret=-1; - if(reader!=nullptr) - { - ret = xmlTextReaderRead(reader); - while(ret==1) - { - processNode(reader); - ret=xmlTextReaderRead(reader); - } - } - return ret; -} diff --git a/plugins/input/osm/osmparser.h b/plugins/input/osm/osmparser.h deleted file mode 100644 index 9f2aeacd8..000000000 --- a/plugins/input/osm/osmparser.h +++ /dev/null @@ -1,54 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2011 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 OSMPARSER_H -#define OSMPARSER_H - -#include -#include -#include -#include -#include -#include "osm.h" -#include - -class osmparser -{ -public: - static void processNode(xmlTextReaderPtr reader); - static void startElement(xmlTextReaderPtr reader, const xmlChar* name); - static void endElement(const xmlChar* name); - static bool parse(osm_dataset* ds, const char* filename); - static bool parse(osm_dataset* ds, char* data, int nbytes); - -private: - static osm_item *cur_item; - static mapnik::value_integer curID; - static bool in_node, in_way; - static osm_dataset* components; - static std::string error; - static std::map tmp_node_store; - - static int do_parse(xmlTextReaderPtr); -}; - -#endif // OSMPARSER_H diff --git a/plugins/input/osm/osmtagtypes.h b/plugins/input/osm/osmtagtypes.h deleted file mode 100644 index 191514757..000000000 --- a/plugins/input/osm/osmtagtypes.h +++ /dev/null @@ -1,50 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2011 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 OSMTAGTYPES_H -#define OSMTAGTYPES_H - -// osmtagtypes.h -// for finding the types of particular tags - -// mapnik -#include - -class osm_tag_types -{ -public: - void add_type(std::string tag, mapnik::eAttributeType type) - { - types[tag] = type; - } - - mapnik::eAttributeType get_type(std::string tag) - { - std::map::iterator i = types.find(tag); - return (i == types.end()) ? mapnik::String : i->second; - } - -private: - std::map types; -}; - -#endif // OSMTAGTYPES_H diff --git a/plugins/input/rasterlite/build.py b/plugins/input/rasterlite/build.py deleted file mode 100644 index 779a48908..000000000 --- a/plugins/input/rasterlite/build.py +++ /dev/null @@ -1,71 +0,0 @@ -# -# This file is part of Mapnik (c++ mapping toolkit) -# -# Copyright (C) 2015 Artem Pavlenko -# -# Mapnik 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 -# -# - -Import ('plugin_base') -Import ('env') - -PLUGIN_NAME = 'rasterlite' - -plugin_env = plugin_base.Clone() - -plugin_sources = Split( - """ - %(PLUGIN_NAME)s_datasource.cpp - %(PLUGIN_NAME)s_featureset.cpp - """ % locals() -) - -# Link Library to Dependencies -libraries = [env['PLUGINS']['rasterlite']['lib']] -libraries.append(env['ICU_LIB_NAME']) -libraries.append('boost_system%s' % env['BOOST_APPEND']) - -if env['RUNTIME_LINK'] == 'static': - libraries.append('geotiff') - libraries.append('spatialite') - libraries.append('sqlite3') - libraries.append('geos_c') - libraries.append('geos') - libraries.append('proj') - libraries.append('z') - -if env['PLUGIN_LINKING'] == 'shared': - libraries.append(env['MAPNIK_NAME']) - - TARGET = plugin_env.SharedLibrary('../%s' % PLUGIN_NAME, - SHLIBPREFIX='', - SHLIBSUFFIX='.input', - source=plugin_sources, - LIBS=libraries) - - # if the plugin links to libmapnik ensure it is built first - Depends(TARGET, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME'])) - - if 'uninstall' not in COMMAND_LINE_TARGETS: - env.Install(env['MAPNIK_INPUT_PLUGINS_DEST'], TARGET) - env.Alias('install', env['MAPNIK_INPUT_PLUGINS_DEST']) - -plugin_obj = { - 'LIBS': libraries, - 'SOURCES': plugin_sources, -} - -Return('plugin_obj') diff --git a/plugins/input/rasterlite/rasterlite_datasource.cpp b/plugins/input/rasterlite/rasterlite_datasource.cpp deleted file mode 100644 index 026101865..000000000 --- a/plugins/input/rasterlite/rasterlite_datasource.cpp +++ /dev/null @@ -1,191 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 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 - * - *****************************************************************************/ - -#include "rasterlite_datasource.hpp" -#include "rasterlite_featureset.hpp" - -// boost - -// mapnik -#include -#include -#include -#include - -using mapnik::datasource; -using mapnik::parameters; - -DATASOURCE_PLUGIN(rasterlite_datasource) - -using mapnik::box2d; -using mapnik::coord2d; -using mapnik::query; -using mapnik::featureset_ptr; -using mapnik::layer_descriptor; -using mapnik::datasource_exception; - - -/* - * Opens a GDALDataset and returns a pointer to it. - * Caller is responsible for calling GDALClose on it - */ -inline void* rasterlite_datasource::open_dataset() const -{ - void* dataset = rasterliteOpen (dataset_name_.c_str(), table_name_.c_str()); - - if (! dataset) - { - throw datasource_exception("Rasterlite Plugin: Error opening dataset"); - } - - if (rasterliteIsError (dataset)) - { - std::string error (rasterliteGetLastError(dataset)); - - rasterliteClose (dataset); - - throw datasource_exception(error); - } - - return dataset; -} - - -rasterlite_datasource::rasterlite_datasource(parameters const& params) - : datasource(params), - desc_(rasterlite_datasource::name(),"utf-8") -{ - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_datasource: Initializing..."; - - boost::optional file = params.get("file"); - if (!file) throw datasource_exception("missing parameter"); - - boost::optional table = params.get("table"); - if (!table) throw datasource_exception("missing
parameter"); - - table_name_ = *table; - - boost::optional base = params.get("base"); - if (base) - dataset_name_ = *base + "/" + *file; - else - dataset_name_ = *file; - - if (!mapnik::util::exists(dataset_name_)) throw datasource_exception(dataset_name_ + " does not exist"); - - void *dataset = open_dataset(); - - double x0, y0, x1, y1; - if (rasterliteGetExtent (dataset, &x0, &y0, &x1, &y1) != RASTERLITE_OK) - { - std::string error (rasterliteGetLastError(dataset)); - - rasterliteClose (dataset); - - throw datasource_exception(error); - } - - extent_.init(x0,y0,x1,y1); - -#ifdef MAPNIK_LOG - int srid, auth_srid; - const char *auth_name; - const char *ref_sys_name; - const char *proj4text; - - int tile_count; - double pixel_x_size, pixel_y_size; - int levels = rasterliteGetLevels (dataset); - - if (rasterliteGetSrid(dataset, &srid, &auth_name, &auth_srid, &ref_sys_name, &proj4text) != RASTERLITE_OK) - { - std::string error (rasterliteGetLastError(dataset)); - - rasterliteClose (dataset); - - throw datasource_exception(error); - } - - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_datasource: Data Source=" << rasterliteGetTablePrefix(dataset); - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_datasource: SRID=" << srid; - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_datasource: Authority=" << auth_name; - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_datasource: AuthSRID=" << auth_srid; - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_datasource: RefSys Name=" << ref_sys_name; - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_datasource: Proj4Text=" << proj4text; - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_datasource: Extent=" << x0 << "," << y0 << " " << x1 << "," << y1 << ")"; - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_datasource: Levels=" << levels; - - for (int i = 0; i < levels; i++) - { - if (rasterliteGetResolution(dataset, i, &pixel_x_size, &pixel_y_size, &tile_count) == RASTERLITE_OK) - { - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_datasource: Level=" << i - << " x=" << pixel_x_size - << " y=" << pixel_y_size - << " tiles=" << tile_count; - } - } -#endif - - rasterliteClose(dataset); -} - -rasterlite_datasource::~rasterlite_datasource() -{ -} - -const char * rasterlite_datasource::name() -{ - return "rasterlite"; -} - -mapnik::datasource::datasource_t rasterlite_datasource::type() const -{ - return datasource::Raster; -} - -box2d rasterlite_datasource::envelope() const -{ - return extent_; -} - -boost::optional rasterlite_datasource::get_geometry_type() const -{ - return boost::optional(); -} - -layer_descriptor rasterlite_datasource::get_descriptor() const -{ - return desc_; -} - -featureset_ptr rasterlite_datasource::features(query const& q) const -{ - rasterlite_query gq = q; - return std::make_shared(open_dataset(), gq); -} - -featureset_ptr rasterlite_datasource::features_at_point(coord2d const& pt, double tol) const -{ - rasterlite_query gq = pt; - return std::make_shared(open_dataset(), gq); -} diff --git a/plugins/input/rasterlite/rasterlite_datasource.hpp b/plugins/input/rasterlite/rasterlite_datasource.hpp deleted file mode 100644 index dd304465b..000000000 --- a/plugins/input/rasterlite/rasterlite_datasource.hpp +++ /dev/null @@ -1,66 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 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 RASTERLITE_DATASOURCE_HPP -#define RASTERLITE_DATASOURCE_HPP - -// mapnik -#include -#include -#include -#include -#include -#include -#include - -// boost -#include -#include - -// stl -#include -#include - -#include "rasterlite_include.hpp" - -class rasterlite_datasource : public mapnik::datasource -{ -public: - rasterlite_datasource(mapnik::parameters const& params); - virtual ~rasterlite_datasource (); - mapnik::datasource::datasource_t type() const; - static const char * name(); - mapnik::featureset_ptr features(mapnik::query const& q) const; - mapnik::featureset_ptr features_at_point(mapnik::coord2d const& pt, double tol = 0) const; - mapnik::box2d envelope() const; - boost::optional get_geometry_type() const; - mapnik::layer_descriptor get_descriptor() const; - -private: - void* open_dataset() const; - mapnik::box2d extent_; - std::string dataset_name_; - std::string table_name_; - mapnik::layer_descriptor desc_; -}; - -#endif // RASTERLITE_DATASOURCE_HPP diff --git a/plugins/input/rasterlite/rasterlite_featureset.cpp b/plugins/input/rasterlite/rasterlite_featureset.cpp deleted file mode 100644 index 270a87e53..000000000 --- a/plugins/input/rasterlite/rasterlite_featureset.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 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 - * - *****************************************************************************/ - -#include "rasterlite_featureset.hpp" - -// mapnik -#include -#include -#include -#include -#include -#include -#include - -#include - -using mapnik::coord2d; -using mapnik::box2d; -using mapnik::feature_ptr; -using mapnik::query; -using mapnik::feature_factory; - - -rasterlite_featureset::rasterlite_featureset(void* dataset, - rasterlite_query q) - : dataset_(dataset), - gquery_(q), - first_(true), - ctx_(std::make_shared()) -{ - rasterliteSetBackgroundColor(dataset_, 255, 0, 255); - rasterliteSetTransparentColor(dataset_, 255, 0, 255); -} - -rasterlite_featureset::~rasterlite_featureset() -{ - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_featureset: Closing"; - - rasterliteClose(dataset_); -} - -feature_ptr rasterlite_featureset::next() -{ - if (first_) - { - first_ = false; - MAPNIK_LOG_DEBUG(gdal) << "rasterlite_featureset: Next feature in Dataset=" << &dataset_; - return mapnik::util::apply_visitor(query_dispatch(*this), gquery_); - } - return feature_ptr(); -} - -feature_ptr rasterlite_featureset::get_feature(mapnik::query const& q) -{ - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_featureset: Running get_feature"; - - feature_ptr feature(feature_factory::create(ctx_,1)); - - double x0, y0, x1, y1; - rasterliteGetExtent (dataset_, &x0, &y0, &x1, &y1); - - box2d raster_extent(x0, y0, x1, y1); - box2d intersect = raster_extent.intersect(q.get_bbox()); - - const int width = static_cast(std::get<0>(q.resolution()) * intersect.width() + 0.5); - const int height = static_cast(std::get<0>(q.resolution()) * intersect.height() + 0.5); - - const double pixel_size = (intersect.width() >= intersect.height()) ? - (intersect.width() / (double) width) : (intersect.height() / (double) height); - - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_featureset: Raster extent=" << raster_extent; - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_featureset: View extent=" << q.get_bbox(); - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_featureset: Intersect extent=" << intersect; - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_featureset: Query resolution=" << std::get<0>(q.resolution()) << "," << std::get<1>(q.resolution()); - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_featureset: Size=" << width << " " << height; - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_featureset: Pixel Size=" << pixel_size; - - if (width > 0 && height > 0) - { - int size = 0; - void* raster = 0; - - if (rasterliteGetRawImageByRect(dataset_, - intersect.minx(), - intersect.miny(), - intersect.maxx(), - intersect.maxy(), - pixel_size, - width, - height, - GAIA_RGBA_ARRAY, - &raster, - &size) == RASTERLITE_OK) - { - if (size > 0) - { - mapnik::image_rgba8 image(width,height); - unsigned char* raster_data = static_cast(raster); - std::memcpy(image.bytes(), raster_data, size); - feature->set_raster(std::make_shared(intersect, std::move(image), 1.0)); - MAPNIK_LOG_DEBUG(rasterlite) << "rasterlite_featureset: Done"; - } - else - { - MAPNIK_LOG_ERROR(rasterlite) << "Rasterlite Plugin: Error " << rasterliteGetLastError (dataset_); - } - } - - return feature; - } - return feature_ptr(); -} - -feature_ptr rasterlite_featureset::get_feature_at_point(mapnik::coord2d const& pt) -{ - return feature_ptr(); -} diff --git a/plugins/input/rasterlite/rasterlite_featureset.hpp b/plugins/input/rasterlite/rasterlite_featureset.hpp deleted file mode 100644 index cb251c86a..000000000 --- a/plugins/input/rasterlite/rasterlite_featureset.hpp +++ /dev/null @@ -1,71 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 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 RASTERLITE_FEATURESET_HPP -#define RASTERLITE_FEATURESET_HPP - -// mapnik -#include -#include -#include -#include - -#include "rasterlite_include.hpp" - -using rasterlite_query = mapnik::util::variant; - -class rasterlite_featureset : public mapnik::Featureset -{ - struct query_dispatch - { - query_dispatch( rasterlite_featureset & featureset) - : featureset_(featureset) {} - - mapnik::feature_ptr operator() (mapnik::query const& q) const - { - return featureset_.get_feature(q); - } - - mapnik::feature_ptr operator() (mapnik::coord2d const& p) const - { - return featureset_.get_feature_at_point(p); - } - - rasterlite_featureset & featureset_; - }; - -public: - rasterlite_featureset(void* dataset, - rasterlite_query q); - virtual ~rasterlite_featureset(); - mapnik::feature_ptr next(); - -private: - mapnik::feature_ptr get_feature(mapnik::query const& q); - mapnik::feature_ptr get_feature_at_point(mapnik::coord2d const& p); - void* dataset_; - rasterlite_query gquery_; - bool first_; - mapnik::context_ptr ctx_; -}; - -#endif // RASTERLITE_FEATURESET_HPP diff --git a/plugins/input/rasterlite/rasterlite_include.hpp b/plugins/input/rasterlite/rasterlite_include.hpp deleted file mode 100644 index b6a9dd274..000000000 --- a/plugins/input/rasterlite/rasterlite_include.hpp +++ /dev/null @@ -1,31 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2015 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 RASTERLITE_INCLUDE_HPP -#define RASTERLITE_INCLUDE_HPP - -extern "C" { -#include -#include -} - -#endif // RASTERLITE_INCLUDE_HPP From c4d8af702daed3f63ed7490d489c4834157b4232 Mon Sep 17 00:00:00 2001 From: artemp Date: Fri, 17 Jul 2015 09:42:05 +0200 Subject: [PATCH 078/165] don't return early from parse_linear_gradient --- src/svg/svg_parser.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/svg/svg_parser.cpp b/src/svg/svg_parser.cpp index cc5335922..e3d8f7e4b 100644 --- a/src/svg/svg_parser.cpp +++ b/src/svg/svg_parser.cpp @@ -1035,9 +1035,9 @@ void parse_radial_gradient(svg_parser & parser, xmlTextReaderPtr reader) void parse_linear_gradient(svg_parser & parser, xmlTextReaderPtr reader) { - if (!parse_common_gradient(parser,reader)) - return; - + //if (!parse_common_gradient(parser,reader)) + // return; + parse_common_gradient(parser,reader); xmlChar *value; double x1 = 0.0; double x2 = 1.0; From 5423f4c5bf4ca3d87b23eb8dc52cc29189510294 Mon Sep 17 00:00:00 2001 From: artemp Date: Fri, 17 Jul 2015 10:58:36 +0200 Subject: [PATCH 079/165] relax grammar rules to allow arbitary ordering - currently we're looking for `features` skipping anything else ref: https://github.com/mapnik/mapnik/issues/2983 --- include/mapnik/json/extract_bounding_box_grammar_impl.hpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/include/mapnik/json/extract_bounding_box_grammar_impl.hpp b/include/mapnik/json/extract_bounding_box_grammar_impl.hpp index c497669a2..fc08584fe 100644 --- a/include/mapnik/json/extract_bounding_box_grammar_impl.hpp +++ b/include/mapnik/json/extract_bounding_box_grammar_impl.hpp @@ -63,10 +63,9 @@ extract_bounding_box_grammar::extract_bounding_box_gramm start = features(_r1) ; - features = iter_pos[_a = _1] >> -(lit('{') >> -lit("\"type\"") - >> lit(':') >> lit("\"FeatureCollection\"") - >> *(lit(',') >> (json.key_value - lit("\"features\""))) - >> lit(',') >> lit("\"features\"") + features = iter_pos[_a = _1] >> -(lit('{') + >> *((json.key_value - lit("\"features\"")) >> lit(',')) + >> lit("\"features\"") >> lit(':')) >> lit('[') >> (feature(_r1,_a) % lit(',')) >> lit(']') ; From bb16cf90ff09488e79af18a8005f965f498af767 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Fri, 17 Jul 2015 07:49:16 -0700 Subject: [PATCH 080/165] make text_itemizer noncopyable --- include/mapnik/text/itemizer.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/mapnik/text/itemizer.hpp b/include/mapnik/text/itemizer.hpp index 4f11bba57..b8589448d 100644 --- a/include/mapnik/text/itemizer.hpp +++ b/include/mapnik/text/itemizer.hpp @@ -72,7 +72,7 @@ struct MAPNIK_DECL text_item : util::noncopyable // - format // - script (http://en.wikipedia.org/wiki/Scripts_in_Unicode) -class MAPNIK_DECL text_itemizer +class MAPNIK_DECL text_itemizer : util::noncopyable { public: text_itemizer(); From 25af931f58c9c33b1bbb4180b434674ad6631f69 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Fri, 17 Jul 2015 07:49:41 -0700 Subject: [PATCH 081/165] don't forward declare glyph_info in text_line.hpp --- include/mapnik/text/text_line.hpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/mapnik/text/text_line.hpp b/include/mapnik/text/text_line.hpp index a00fb79cf..cf47026df 100644 --- a/include/mapnik/text/text_line.hpp +++ b/include/mapnik/text/text_line.hpp @@ -26,12 +26,11 @@ #include #include #include +#include namespace mapnik { -struct glyph_info; - // This class stores all glyphs of a line in left to right order. // It can be used for rendering but no text processing (like line breaking) // should be done! @@ -98,6 +97,6 @@ private: unsigned space_count_; }; -} //namespace mapnik +} // namespace mapnik #endif // MAPNIK_TEXT_LINE_HPP From c63e71acacb7515c84770e23636dde76fc93a687 Mon Sep 17 00:00:00 2001 From: artemp Date: Fri, 17 Jul 2015 17:43:04 +0200 Subject: [PATCH 082/165] +unit test: feature_collections with extra properties --- test/unit/datasource/geojson.cpp | 44 ++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/test/unit/datasource/geojson.cpp b/test/unit/datasource/geojson.cpp index ae49b47e4..4fe4f0792 100644 --- a/test/unit/datasource/geojson.cpp +++ b/test/unit/datasource/geojson.cpp @@ -73,5 +73,49 @@ TEST_CASE("geojson") { REQUIRE(feature != nullptr); } + mapnik::datasource_cache::instance().register_datasources("plugins/input/geojson.input"); + SECTION("json extra properties cache-feature=\"true\"") + { + // Create datasource + mapnik::parameters params; + params["type"] = "geojson"; + params["file"] = "./test/data/json/feature_collection_extra_properties.json"; + params["cache-features"] = true; + auto ds = mapnik::datasource_cache::instance().create(params); + REQUIRE(bool(ds)); + auto fields = ds->get_descriptor().get_descriptors(); + mapnik::query query(ds->envelope()); + for (auto const &field : fields) + { + query.add_property_name(field.get_name()); + } + auto features = ds->features(query); + auto feature = features->next(); + REQUIRE(feature != nullptr); + REQUIRE(feature->envelope() == mapnik::box2d(123,456,123,456)); + } + + mapnik::datasource_cache::instance().register_datasources("plugins/input/geojson.input"); + SECTION("json extra properties cache-feature=\"false\"") + { + // Create datasource + mapnik::parameters params; + params["type"] = "geojson"; + params["file"] = "./test/data/json/feature_collection_extra_properties.json"; + params["cache-features"] = false; + auto ds = mapnik::datasource_cache::instance().create(params); + REQUIRE(bool(ds)); + auto fields = ds->get_descriptor().get_descriptors(); + mapnik::query query(ds->envelope()); + for (auto const &field : fields) + { + query.add_property_name(field.get_name()); + } + auto features = ds->features(query); + auto feature = features->next(); + REQUIRE(feature != nullptr); + REQUIRE(feature->envelope() == mapnik::box2d(123,456,123,456)); + } + } } From a8da31b765c183b069e3e00e3edf6265459c9734 Mon Sep 17 00:00:00 2001 From: artemp Date: Fri, 17 Jul 2015 19:49:28 +0200 Subject: [PATCH 083/165] make '%' sign optional ( was 0 or N) --- src/svg/svg_parser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/svg/svg_parser.cpp b/src/svg/svg_parser.cpp index 1ef103c3b..edec3c93d 100644 --- a/src/svg/svg_parser.cpp +++ b/src/svg/svg_parser.cpp @@ -136,7 +136,7 @@ double parse_double_optional_percent(const char* str, bool &percent) double val = 0.0; char unit='\0'; - parse(str, str + std::strlen(str),double_[ref(val)=_1] >> *char_('%')[ref(unit)=_1]); + parse(str, str + std::strlen(str),double_[ref(val)=_1] >> -char_('%')[ref(unit)=_1]); if (unit =='%') { percent = true; From bb8b8356182201c7ba4f82356988f61054ad47e9 Mon Sep 17 00:00:00 2001 From: artemp Date: Fri, 17 Jul 2015 20:00:14 +0200 Subject: [PATCH 084/165] calculate percentage value inside parser, reduce local vars --- src/svg/svg_parser.cpp | 13 ++----------- test/data | 2 +- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/src/svg/svg_parser.cpp b/src/svg/svg_parser.cpp index edec3c93d..ec895256b 100644 --- a/src/svg/svg_parser.cpp +++ b/src/svg/svg_parser.cpp @@ -135,17 +135,8 @@ double parse_double_optional_percent(const char* str, bool &percent) qi::char_type char_; double val = 0.0; - char unit='\0'; - parse(str, str + std::strlen(str),double_[ref(val)=_1] >> -char_('%')[ref(unit)=_1]); - if (unit =='%') - { - percent = true; - val/=100.0; - } - else - { - percent = false; - } + parse(str, str + std::strlen(str),double_[ref(val)=_1, ref(percent) = false] + >> -char_('%')[ref(val)/100.0, ref(percent) = true]); return val; } diff --git a/test/data b/test/data index bc51f1d98..fa2bac087 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit bc51f1d9890b4dc5887864b00b1217c3ab904b4a +Subproject commit fa2bac0878b329362ad8647674b5496dd5e60f46 From 9ab1935a368e5f7ae92f9ab144bbe8a29231620e Mon Sep 17 00:00:00 2001 From: artemp Date: Fri, 17 Jul 2015 20:16:21 +0200 Subject: [PATCH 085/165] add boolean flag for tracking when to re-calculate extent (ref #2985) --- include/mapnik/memory_datasource.hpp | 1 + src/memory_datasource.cpp | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/include/mapnik/memory_datasource.hpp b/include/mapnik/memory_datasource.hpp index a6ab9188c..cd2b61130 100644 --- a/include/mapnik/memory_datasource.hpp +++ b/include/mapnik/memory_datasource.hpp @@ -56,6 +56,7 @@ private: datasource::datasource_t type_; bool bbox_check_; mutable box2d extent_; + mutable bool dirty_extent_ = true; }; } diff --git a/src/memory_datasource.cpp b/src/memory_datasource.cpp index c30f6745f..f7bd61d4e 100644 --- a/src/memory_datasource.cpp +++ b/src/memory_datasource.cpp @@ -82,6 +82,7 @@ void memory_datasource::push(feature_ptr feature) // TODO - collect attribute descriptors? //desc_.add_descriptor(attribute_descriptor(fld_name,mapnik::Integer)); features_.push_back(feature); + dirty_extent_ = true; } datasource::datasource_t memory_datasource::type() const @@ -110,10 +111,11 @@ void memory_datasource::set_envelope(box2d const& box) box2d memory_datasource::envelope() const { - if (!extent_.valid()) + if (!extent_.valid() || dirty_extent_) { accumulate_extent func(extent_); std::for_each(features_.begin(),features_.end(),func); + dirty_extent_ = false; } return extent_; } From faa2c193597b0e3fc537fd6bbd4316a3d97f5807 Mon Sep 17 00:00:00 2001 From: artemp Date: Mon, 20 Jul 2015 12:05:26 +0200 Subject: [PATCH 086/165] make visitor return `status` + don't mutating `svg_name_` --- utils/svg2png/svg2png.cpp | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/utils/svg2png/svg2png.cpp b/utils/svg2png/svg2png.cpp index 7e3aca532..0dc8efca0 100644 --- a/utils/svg2png/svg2png.cpp +++ b/utils/svg2png/svg2png.cpp @@ -52,28 +52,26 @@ struct main_marker_visitor { - main_marker_visitor(std::string & svg_name, - int & return_value, + main_marker_visitor(std::string const& svg_name, bool verbose, bool auto_open) : svg_name_(svg_name), - return_value_(return_value), verbose_(verbose), auto_open_(auto_open) {} - void operator() (mapnik::marker_null const&) + int operator() (mapnik::marker_null const&) { std::clog << "svg2png error: '" << svg_name_ << "' is not a valid vector!\n"; - return_value_ = -1; + return -1; } - void operator() (mapnik::marker_rgba8 const&) + int operator() (mapnik::marker_rgba8 const&) { std::clog << "svg2png error: '" << svg_name_ << "' is not a valid vector!\n"; - return_value_ = -1; + return -1; } - void operator() (mapnik::marker_svg const& marker) + int operator() (mapnik::marker_svg const& marker) { using pixfmt = agg::pixfmt_rgba32_pre; using renderer_base = agg::renderer_base; @@ -111,28 +109,29 @@ struct main_marker_visitor svg_renderer_this.render(ras_ptr, sl, renb, mtx, opacity, bbox); - boost::algorithm::ireplace_last(svg_name_,".svg",".png"); + std::string png_name(svg_name_); + boost::algorithm::ireplace_last(png_name,".svg",".png"); demultiply_alpha(im); - mapnik::save_to_file(im,svg_name_,"png"); + mapnik::save_to_file(im,png_name,"png"); + int status = 0; if (auto_open_) { std::ostringstream s; #ifdef DARWIN - s << "open " << svg_name_; + s << "open " << png_name; #else - s << "xdg-open " << svg_name_; + s << "xdg-open " << png_name; #endif int ret = system(s.str().c_str()); if (ret != 0) - return_value_ = ret; + status = ret; } std::clog << "rendered to: " << svg_name_ << "\n"; - + return status; } private: - std::string & svg_name_; - int & return_value_; + std::string const& svg_name_; bool verbose_; bool auto_open_; }; @@ -143,7 +142,7 @@ int main (int argc,char** argv) bool verbose = false; bool auto_open = false; - int return_value = 0; + int status = 0; std::vector svg_files; mapnik::logger::instance().set_severity(mapnik::logger::error); @@ -214,8 +213,8 @@ int main (int argc,char** argv) } std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); - main_marker_visitor visitor(svg_name, return_value, verbose, auto_open); - mapnik::util::apply_visitor(visitor, *marker); + main_marker_visitor visitor(svg_name, verbose, auto_open); + status = mapnik::util::apply_visitor(visitor, *marker); } } catch (...) @@ -229,5 +228,5 @@ int main (int argc,char** argv) // to make sure valgrind output is clean // http://xmlsoft.org/xmlmem.html xmlCleanupParser(); - return return_value; + return status; } From f58a13825f41b4867d19e8c6b59005ba624c3bee Mon Sep 17 00:00:00 2001 From: artemp Date: Mon, 20 Jul 2015 14:51:24 +0200 Subject: [PATCH 087/165] correct std::clog message --- utils/svg2png/svg2png.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/svg2png/svg2png.cpp b/utils/svg2png/svg2png.cpp index 0dc8efca0..21041b280 100644 --- a/utils/svg2png/svg2png.cpp +++ b/utils/svg2png/svg2png.cpp @@ -126,7 +126,7 @@ struct main_marker_visitor if (ret != 0) status = ret; } - std::clog << "rendered to: " << svg_name_ << "\n"; + std::clog << "rendered to: " << png_name << "\n"; return status; } From 9da6b9b38966df2a27dd44b7db48cc77d9096212 Mon Sep 17 00:00:00 2001 From: artemp Date: Mon, 20 Jul 2015 15:41:04 +0200 Subject: [PATCH 088/165] format --- include/mapnik/svg/svg_path_adapter.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/mapnik/svg/svg_path_adapter.hpp b/include/mapnik/svg/svg_path_adapter.hpp index 957717166..beb8222cb 100644 --- a/include/mapnik/svg/svg_path_adapter.hpp +++ b/include/mapnik/svg/svg_path_adapter.hpp @@ -45,7 +45,7 @@ using namespace agg; template class path_adapter : util::noncopyable { public: - using container_type = VertexContainer ; + using container_type = VertexContainer; using self_type = path_adapter; //-------------------------------------------------------------------- From 6a2d0b9ebfb2d9238ee95e2934e327213b4ee607 Mon Sep 17 00:00:00 2001 From: artemp Date: Mon, 20 Jul 2015 15:41:35 +0200 Subject: [PATCH 089/165] started adding svg parsing tests to increase test coverage (work-in-progress) --- test/data | 2 +- test/unit/svg/svg_parser_test.cpp | 172 ++++++++++++++++++++++++++++++ 2 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 test/unit/svg/svg_parser_test.cpp diff --git a/test/data b/test/data index fa2bac087..a04e0bb1f 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit fa2bac0878b329362ad8647674b5496dd5e60f46 +Subproject commit a04e0bb1fc7e6eef0bd9cc67dc0c8c04fd3f8c52 diff --git a/test/unit/svg/svg_parser_test.cpp b/test/unit/svg/svg_parser_test.cpp new file mode 100644 index 000000000..0cfd3e56b --- /dev/null +++ b/test/unit/svg/svg_parser_test.cpp @@ -0,0 +1,172 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2015 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 + * + *****************************************************************************/ + +#include "catch.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include // for xmlInitParser(), xmlCleanupParser() +#include + +namespace detail { + +template +struct vertex_equal +{ + template + bool operator() (T const& lhs, T const& rhs) const + { + static const double eps = 1.0 / std::pow(10,N); + return (std::fabs(std::get<0>(lhs) - std::get<0>(rhs)) < eps) + && (std::fabs(std::get<1>(lhs) - std::get<1>(rhs)) < eps) + && std::get<2>(lhs) == std::get<2>(rhs); + } +}; +} + +TEST_CASE("SVG parser") { + + xmlInitParser(); + SECTION("SVG ") + { + // + std::string svg_name("./test/data/svg/rect.svg"); + std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); + REQUIRE(marker); + REQUIRE(marker->is()); + mapnik::marker_svg const& svg = mapnik::util::get(*marker); + auto bbox = svg.bounding_box(); + REQUIRE(bbox == mapnik::box2d(0, 0, 20, 15)); + auto storage = svg.get_data(); + REQUIRE(storage); + mapnik::svg::vertex_stl_adapter stl_storage(storage->source()); + mapnik::svg::svg_path_adapter path(stl_storage); + double x,y; + unsigned cmd; + std::vector> vec; + while ((cmd = path.vertex(&x,&y)) != mapnik::SEG_END) + { + vec.emplace_back(x, y, cmd); + //std::cerr << x << "," << y << " cmd=" << cmd << std::endl; + } + std::vector> expected = { {0, 0, 1}, {20, 0, 2}, {20, 15, 2}, {0, 15, 2}, {0, 0, 79} }; + REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin())); + } + SECTION("SVG rounded ") + { + // + std::string svg_name("./test/data/svg/rounded_rect.svg"); + std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); + REQUIRE(marker); + REQUIRE(marker->is()); + mapnik::marker_svg const& svg = mapnik::util::get(*marker); + auto bbox = svg.bounding_box(); + REQUIRE(bbox == mapnik::box2d(0, 0, 20, 15)); + auto storage = svg.get_data(); + REQUIRE(storage); + mapnik::svg::vertex_stl_adapter stl_storage(storage->source()); + mapnik::svg::svg_path_adapter path(stl_storage); + double x,y; + unsigned cmd; + std::vector> vec; + + while ((cmd = path.vertex(&x,&y)) != mapnik::SEG_END) + { + vec.emplace_back(x, y, cmd); + } + + std::vector> expected = {{0, 5,1},{0.481856, 2.85842,2},{1.83455, 1.12961,2},{3.79736, 0.146789,2},{5, 0,2},{15, 0,2},{17.1416, 0.481856,2},{18.8704, 1.83455,2},{19.8532, 3.79736,2},{20, 5,2},{20, 10,2},{19.5181, 12.1416,2},{18.1654, 13.8704,2},{16.2026, 14.8532,2},{15, 15,2},{5, 15,2},{2.85842, 14.5181,2},{1.12961, 13.1654,2},{0.146789, 11.2026,2},{0, 10,2},{0, 10,95}}; + + REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin(),detail::vertex_equal<3>())); + } + + SECTION("SVG ") + { + // + std::string svg_name("./test/data/svg/polyline.svg"); + std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); + REQUIRE(marker); + REQUIRE(marker->is()); + mapnik::marker_svg const& svg = mapnik::util::get(*marker); + auto bbox = svg.bounding_box(); + REQUIRE(bbox == mapnik::box2d(1.0,1.0,1199.0,399.0)); + auto storage = svg.get_data(); + REQUIRE(storage); + mapnik::svg::vertex_stl_adapter stl_storage(storage->source()); + mapnik::svg::svg_path_adapter path(stl_storage); + double x,y; + unsigned cmd; + std::vector> vec; + std::size_t num_vertices = path.total_vertices(); + for (std::size_t i = 0; i < num_vertices; ++i) + { + cmd = path.vertex(&x,&y); + vec.emplace_back(x, y, cmd); + } + + std::vector> expected = {{1, 1, 1},{1199, 1, 2},{1199, 399, 2},{1, 399, 2},{1, 1, 79},{0, 0, 0},{50, 375, 1},{150, 375, 2},{150, 325, 2},{250, 325, 2},{250, 375, 2},{350, 375, 2},{350, 250, 2},{450, 250, 2},{450, 375, 2},{550, 375, 2},{550, 175, 2},{650, 175, 2},{650, 375, 2},{750, 375, 2},{750, 100, 2},{850, 100, 2},{850, 375, 2},{950, 375, 2},{950, 25, 2},{1050, 25, 2},{1050, 375, 2},{1150, 375, 2}}; + + REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin())); + } + + SECTION("SVG ") + { + // + std::string svg_name("./test/data/svg/polygon.svg"); + std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); + REQUIRE(marker); + REQUIRE(marker->is()); + mapnik::marker_svg const& svg = mapnik::util::get(*marker); + auto bbox = svg.bounding_box(); + REQUIRE(bbox == mapnik::box2d(1.0,1.0,1199.0,399.0)); + auto storage = svg.get_data(); + REQUIRE(storage); + mapnik::svg::vertex_stl_adapter stl_storage(storage->source()); + mapnik::svg::svg_path_adapter path(stl_storage); + double x,y; + unsigned cmd; + std::vector> vec; + std::size_t num_vertices = path.total_vertices(); + //std::cerr << "Num vertices = " << num_vertices << std::endl; + //std::cerr << "{"; + for (std::size_t i = 0; i < num_vertices; ++i) + { + cmd = path.vertex(&x,&y); + vec.emplace_back(x, y, cmd); + //if (vec.size() > 1) std::cerr << ","; + //std::cerr << std::setprecision(6) << "{" << x << ", " << y << ", " << cmd << "}"; + } + //std::cerr << "}" << std::endl; + + std::vector> expected = {{1, 1, 1},{1199, 1, 2},{1199, 399, 2},{1, 399, 2},{1, 1, 79},{0, 0, 0},{350, 75, 1},{379, 161, 2},{469, 161, 2},{397, 215, 2},{423, 301, 2},{350, 250, 2},{277, 301, 2},{303, 215, 2},{231, 161, 2},{321, 161, 2},{350, 75, 79},{0, 0, 0},{850, 75, 1},{958, 137.5, 2},{958, 262.5, 2},{850, 325, 2},{742, 262.6, 2},{742, 137.5, 2},{850, 75, 79}}; + + REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin())); + } + + xmlCleanupParser(); +} From fb155a57cde10b96bcce15b046c06e1738749b7e Mon Sep 17 00:00:00 2001 From: artemp Date: Mon, 20 Jul 2015 17:16:35 +0200 Subject: [PATCH 090/165] support compilers that can't construct tuple from initializer list --- test/unit/svg/svg_parser_test.cpp | 83 +++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 4 deletions(-) diff --git a/test/unit/svg/svg_parser_test.cpp b/test/unit/svg/svg_parser_test.cpp index 0cfd3e56b..a25c9183b 100644 --- a/test/unit/svg/svg_parser_test.cpp +++ b/test/unit/svg/svg_parser_test.cpp @@ -74,7 +74,11 @@ TEST_CASE("SVG parser") { vec.emplace_back(x, y, cmd); //std::cerr << x << "," << y << " cmd=" << cmd << std::endl; } - std::vector> expected = { {0, 0, 1}, {20, 0, 2}, {20, 15, 2}, {0, 15, 2}, {0, 0, 79} }; + std::vector> expected = { std::make_tuple(0, 0, 1), + std::make_tuple(20, 0, 2), + std::make_tuple(20, 15, 2), + std::make_tuple(0, 15, 2), + std::make_tuple(0, 0, 79) }; REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin())); } SECTION("SVG rounded ") @@ -100,7 +104,27 @@ TEST_CASE("SVG parser") { vec.emplace_back(x, y, cmd); } - std::vector> expected = {{0, 5,1},{0.481856, 2.85842,2},{1.83455, 1.12961,2},{3.79736, 0.146789,2},{5, 0,2},{15, 0,2},{17.1416, 0.481856,2},{18.8704, 1.83455,2},{19.8532, 3.79736,2},{20, 5,2},{20, 10,2},{19.5181, 12.1416,2},{18.1654, 13.8704,2},{16.2026, 14.8532,2},{15, 15,2},{5, 15,2},{2.85842, 14.5181,2},{1.12961, 13.1654,2},{0.146789, 11.2026,2},{0, 10,2},{0, 10,95}}; + std::vector> expected = {std::make_tuple(0, 5,1), + std::make_tuple(0.481856, 2.85842,2), + std::make_tuple(1.83455, 1.12961,2), + std::make_tuple(3.79736, 0.146789,2), + std::make_tuple(5, 0,2), + std::make_tuple(15, 0,2), + std::make_tuple(17.1416, 0.481856,2), + std::make_tuple(18.8704, 1.83455,2), + std::make_tuple(19.8532, 3.79736,2), + std::make_tuple(20, 5,2), + std::make_tuple(20, 10,2), + std::make_tuple(19.5181, 12.1416,2), + std::make_tuple(18.1654, 13.8704,2), + std::make_tuple(16.2026, 14.8532,2), + std::make_tuple(15, 15,2), + std::make_tuple(5, 15,2), + std::make_tuple(2.85842, 14.5181,2), + std::make_tuple(1.12961, 13.1654,2), + std::make_tuple(0.146789, 11.2026,2), + std::make_tuple(0, 10,2), + std::make_tuple(0, 10,95)}; REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin(),detail::vertex_equal<3>())); } @@ -129,7 +153,34 @@ TEST_CASE("SVG parser") { vec.emplace_back(x, y, cmd); } - std::vector> expected = {{1, 1, 1},{1199, 1, 2},{1199, 399, 2},{1, 399, 2},{1, 1, 79},{0, 0, 0},{50, 375, 1},{150, 375, 2},{150, 325, 2},{250, 325, 2},{250, 375, 2},{350, 375, 2},{350, 250, 2},{450, 250, 2},{450, 375, 2},{550, 375, 2},{550, 175, 2},{650, 175, 2},{650, 375, 2},{750, 375, 2},{750, 100, 2},{850, 100, 2},{850, 375, 2},{950, 375, 2},{950, 25, 2},{1050, 25, 2},{1050, 375, 2},{1150, 375, 2}}; + std::vector> expected = {std::make_tuple(1, 1, 1), + std::make_tuple(1199, 1, 2), + std::make_tuple(1199, 399, 2), + std::make_tuple(1, 399, 2), + std::make_tuple(1, 1, 79), + std::make_tuple(0, 0, 0), + std::make_tuple(50, 375, 1), + std::make_tuple(150, 375, 2), + std::make_tuple(150, 325, 2), + std::make_tuple(250, 325, 2), + std::make_tuple(250, 375, 2), + std::make_tuple(350, 375, 2), + std::make_tuple(350, 250, 2), + std::make_tuple(450, 250, 2), + std::make_tuple(450, 375, 2), + std::make_tuple(550, 375, 2), + std::make_tuple(550, 175, 2), + std::make_tuple(650, 175, 2), + std::make_tuple(650, 375, 2), + std::make_tuple(750, 375, 2), + std::make_tuple(750, 100, 2), + std::make_tuple(850, 100, 2), + std::make_tuple(850, 375, 2), + std::make_tuple(950, 375, 2), + std::make_tuple(950, 25, 2), + std::make_tuple(1050, 25, 2), + std::make_tuple(1050, 375, 2), + std::make_tuple(1150, 375, 2)}; REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin())); } @@ -163,7 +214,31 @@ TEST_CASE("SVG parser") { } //std::cerr << "}" << std::endl; - std::vector> expected = {{1, 1, 1},{1199, 1, 2},{1199, 399, 2},{1, 399, 2},{1, 1, 79},{0, 0, 0},{350, 75, 1},{379, 161, 2},{469, 161, 2},{397, 215, 2},{423, 301, 2},{350, 250, 2},{277, 301, 2},{303, 215, 2},{231, 161, 2},{321, 161, 2},{350, 75, 79},{0, 0, 0},{850, 75, 1},{958, 137.5, 2},{958, 262.5, 2},{850, 325, 2},{742, 262.6, 2},{742, 137.5, 2},{850, 75, 79}}; + std::vector> expected = {std::make_tuple(1, 1, 1), + std::make_tuple(1199, 1, 2), + std::make_tuple(1199, 399, 2), + std::make_tuple(1, 399, 2), + std::make_tuple(1, 1, 79), + std::make_tuple(0, 0, 0), + std::make_tuple(350, 75, 1), + std::make_tuple(379, 161, 2), + std::make_tuple(469, 161, 2), + std::make_tuple(397, 215, 2), + std::make_tuple(423, 301, 2), + std::make_tuple(350, 250, 2), + std::make_tuple(277, 301, 2), + std::make_tuple(303, 215, 2), + std::make_tuple(231, 161, 2), + std::make_tuple(321, 161, 2), + std::make_tuple(350, 75, 79), + std::make_tuple(0, 0, 0), + std::make_tuple(850, 75, 1), + std::make_tuple(958, 137.5, 2), + std::make_tuple(958, 262.5, 2), + std::make_tuple(850, 325, 2), + std::make_tuple(742, 262.6, 2), + std::make_tuple(742, 137.5, 2), + std::make_tuple(850, 75, 79)}; REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin())); } From 43dcf35a93138b5a072930a8677da87fbda543b4 Mon Sep 17 00:00:00 2001 From: Jiri Drbalek Date: Mon, 20 Jul 2015 15:37:05 +0000 Subject: [PATCH 091/165] do not store out of extent collision boxes (fixes #2963) --- include/mapnik/label_collision_detector.hpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/include/mapnik/label_collision_detector.hpp b/include/mapnik/label_collision_detector.hpp index 6dce9ff2c..74a76be7f 100644 --- a/include/mapnik/label_collision_detector.hpp +++ b/include/mapnik/label_collision_detector.hpp @@ -212,12 +212,18 @@ public: void insert(box2d const& box) { - tree_.insert(label(box), box); + if (tree_.extent().intersects(box)) + { + tree_.insert(label(box), box); + } } void insert(box2d const& box, mapnik::value_unicode_string const& text) { - tree_.insert(label(box, text), box); + if (tree_.extent().intersects(box)) + { + tree_.insert(label(box, text), box); + } } void clear() From 0625b90067d4000d86d0b1fba83d9038410fbff4 Mon Sep 17 00:00:00 2001 From: artemp Date: Mon, 20 Jul 2015 17:44:47 +0200 Subject: [PATCH 092/165] SVG unit test - add test (http://www.w3.org/TR/SVGTiny12/shapes.html#LineElement) --- test/unit/svg/svg_parser_test.cpp | 58 ++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/test/unit/svg/svg_parser_test.cpp b/test/unit/svg/svg_parser_test.cpp index a25c9183b..0c2752a29 100644 --- a/test/unit/svg/svg_parser_test.cpp +++ b/test/unit/svg/svg_parser_test.cpp @@ -129,6 +129,59 @@ TEST_CASE("SVG parser") { REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin(),detail::vertex_equal<3>())); } + SECTION("SVG ") + { + // + std::string svg_name("./test/data/svg/line.svg"); + std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); + REQUIRE(marker); + REQUIRE(marker->is()); + mapnik::marker_svg const& svg = mapnik::util::get(*marker); + auto bbox = svg.bounding_box(); + REQUIRE(bbox == mapnik::box2d(1.0,1.0,1199.0,399.0)); + auto storage = svg.get_data(); + REQUIRE(storage); + mapnik::svg::vertex_stl_adapter stl_storage(storage->source()); + mapnik::svg::svg_path_adapter path(stl_storage); + double x,y; + unsigned cmd; + std::vector> vec; + std::size_t num_vertices = path.total_vertices(); + //std::cerr << "Num vertices = " << num_vertices << std::endl; + //std::cerr << "{"; + for (std::size_t i = 0; i < num_vertices; ++i) + { + cmd = path.vertex(&x,&y); + vec.emplace_back(x, y, cmd); + //if (vec.size() > 1) std::cerr << ","; + //std::cerr << std::setprecision(6) << "std::make_tuple(" << x << ", " << y << ", " << cmd << ")"; + } + //std::cerr << "}" << std::endl; + + std::vector> expected = {std::make_tuple(1, 1, 1), + std::make_tuple(1199, 1, 2), + std::make_tuple(1199, 399, 2), + std::make_tuple(1, 399, 2), + std::make_tuple(1, 1, 79), + std::make_tuple(0, 0, 0), + std::make_tuple(100, 300, 1), + std::make_tuple(300, 100, 2), + std::make_tuple(0, 0, 0), + std::make_tuple(300, 300, 1), + std::make_tuple(500, 100, 2), + std::make_tuple(0, 0, 0), + std::make_tuple(500, 300, 1), + std::make_tuple(700, 100, 2), + std::make_tuple(0, 0, 0), + std::make_tuple(700, 300, 1), + std::make_tuple(900, 100, 2), + std::make_tuple(0, 0, 0), + std::make_tuple(900, 300, 1), + std::make_tuple(1100, 100, 2)}; + + REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin())); + } + SECTION("SVG ") { // @@ -203,16 +256,11 @@ TEST_CASE("SVG parser") { unsigned cmd; std::vector> vec; std::size_t num_vertices = path.total_vertices(); - //std::cerr << "Num vertices = " << num_vertices << std::endl; - //std::cerr << "{"; for (std::size_t i = 0; i < num_vertices; ++i) { cmd = path.vertex(&x,&y); vec.emplace_back(x, y, cmd); - //if (vec.size() > 1) std::cerr << ","; - //std::cerr << std::setprecision(6) << "{" << x << ", " << y << ", " << cmd << "}"; } - //std::cerr << "}" << std::endl; std::vector> expected = {std::make_tuple(1, 1, 1), std::make_tuple(1199, 1, 2), From 27fb4a9e007659794d4286c9dc45bb5683815b4c Mon Sep 17 00:00:00 2001 From: artemp Date: Mon, 20 Jul 2015 20:36:31 +0200 Subject: [PATCH 093/165] update tests --- test/data | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/data b/test/data index a04e0bb1f..07e9a9c7c 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit a04e0bb1fc7e6eef0bd9cc67dc0c8c04fd3f8c52 +Subproject commit 07e9a9c7c3e9365d6b0d9023d77dfe897887af1c From 55a98550697ff20807fe90961a028ff1a7611f11 Mon Sep 17 00:00:00 2001 From: artemp Date: Tue, 21 Jul 2015 10:25:19 +0200 Subject: [PATCH 094/165] add `xml:id` support ( ref #2989 ) --- src/svg/svg_parser.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/svg/svg_parser.cpp b/src/svg/svg_parser.cpp index 009c5c790..d04177d09 100644 --- a/src/svg/svg_parser.cpp +++ b/src/svg/svg_parser.cpp @@ -493,7 +493,8 @@ void parse_path(svg_parser & parser, xmlTextReaderPtr reader) if (!mapnik::svg::parse_path((const char*) value, parser.path_)) { xmlChar *id_value; - id_value = xmlTextReaderGetAttribute(reader, BAD_CAST "id"); + id_value = xmlTextReaderGetAttribute(reader, BAD_CAST "xml:id"); + if (!id_value) id_value = xmlTextReaderGetAttribute(reader, BAD_CAST "id"); if (id_value) { std::string id_string((const char *) id_value); @@ -883,7 +884,9 @@ bool parse_common_gradient(svg_parser & parser, xmlTextReaderPtr reader) xmlChar *value; std::string id; - value = xmlTextReaderGetAttribute(reader, BAD_CAST "id"); + value = xmlTextReaderGetAttribute(reader, BAD_CAST "xml:id"); + if (!value) value = xmlTextReaderGetAttribute(reader, BAD_CAST "id"); + if (value) { // start a new gradient From 61a2ef7ebb74b5bb1161dbbfed297e6f95b62cbf Mon Sep 17 00:00:00 2001 From: artemp Date: Tue, 21 Jul 2015 10:29:19 +0200 Subject: [PATCH 095/165] update test data --- test/data | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/data b/test/data index 07e9a9c7c..bcaa7f226 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit 07e9a9c7c3e9365d6b0d9023d77dfe897887af1c +Subproject commit bcaa7f2260630aaac1dc4c75db0cb877e1b6ba95 From f19ddf81ba2492f4a38e3c98b39da257f033119d Mon Sep 17 00:00:00 2001 From: artemp Date: Tue, 21 Jul 2015 10:35:21 +0200 Subject: [PATCH 096/165] SVG - add gradient test (ref #2989) --- test/unit/svg/svg_parser_test.cpp | 39 +++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/test/unit/svg/svg_parser_test.cpp b/test/unit/svg/svg_parser_test.cpp index 0c2752a29..6bd37bede 100644 --- a/test/unit/svg/svg_parser_test.cpp +++ b/test/unit/svg/svg_parser_test.cpp @@ -291,5 +291,44 @@ TEST_CASE("SVG parser") { REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin())); } + SECTION("SVG ") + { + // + std::string svg_name("./test/data/svg/gradient.svg"); + std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); + REQUIRE(marker); + REQUIRE(marker->is()); + mapnik::marker_svg const& svg = mapnik::util::get(*marker); + auto bbox = svg.bounding_box(); + REQUIRE(bbox == mapnik::box2d(1.0,1.0,799.0,399.0)); + auto storage = svg.get_data(); + REQUIRE(storage); + mapnik::svg::vertex_stl_adapter stl_storage(storage->source()); + mapnik::svg::svg_path_adapter path(stl_storage); + double x,y; + unsigned cmd; + std::vector> vec; + std::size_t num_vertices = path.total_vertices(); + + for (std::size_t i = 0; i < num_vertices; ++i) + { + cmd = path.vertex(&x,&y); + vec.emplace_back(x, y, cmd); + } + std::vector> expected = {std::make_tuple(1, 1, 1), + std::make_tuple(799, 1, 2), + std::make_tuple(799, 399, 2), + std::make_tuple(1, 399, 2), + std::make_tuple(1, 1, 79), + std::make_tuple(0, 0, 0), + std::make_tuple(100, 100, 1), + std::make_tuple(700, 100, 2), + std::make_tuple(700, 300, 2), + std::make_tuple(100, 300, 2), + std::make_tuple(100, 100, 79)}; + REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin())); + } + + xmlCleanupParser(); } From d7cf0d5bcaa5601c1aa265cec9f41ffe0b91156d Mon Sep 17 00:00:00 2001 From: artemp Date: Tue, 21 Jul 2015 11:30:04 +0200 Subject: [PATCH 097/165] add parse_id_from_url method --- src/svg/svg_parser.cpp | 16 ++++++++++++---- test/data | 2 +- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/svg/svg_parser.cpp b/src/svg/svg_parser.cpp index d04177d09..701e8fa25 100644 --- a/src/svg/svg_parser.cpp +++ b/src/svg/svg_parser.cpp @@ -149,6 +149,16 @@ bool parse_style (const char* str, pairs_type & v) return phrase_parse(str, str + std::strlen(str), kv_parser, skip_type(), v); } +bool parse_id_from_url (char const* str, std::string & id) +{ + using namespace boost::spirit::qi; + using skip_type = boost::spirit::ascii::space_type; + qi::_1_type _1; + qi::char_type char_; + qi::lit_type lit; + return phrase_parse(str, str + std::strlen(str), lit("url") > "(" > "#" > *(char_ - lit(')'))[boost::phoenix::ref(id) += _1] > ")" , skip_type()); +} + bool parse_reader(svg_parser & parser, xmlTextReaderPtr reader) { int ret = xmlTextReaderRead(reader); @@ -306,16 +316,14 @@ void parse_attr(svg_parser & parser, const xmlChar * name, const xmlChar * value } else if (xmlStrEqual(name, BAD_CAST "fill")) { + std::string id; if (xmlStrEqual(value, BAD_CAST "none")) { parser.path_.fill_none(); } - else if (boost::starts_with((const char*)value, "url(#")) + else if (parse_id_from_url((const char*)value, id)) { // see if we have a known gradient fill - std::string id = std::string((const char*)&value[5]); - // get rid of the trailing ) - id.erase(id.end()-1); if (parser.gradient_map_.count(id) > 0) { parser.path_.add_fill_gradient(parser.gradient_map_[id]); diff --git a/test/data b/test/data index bcaa7f226..d1795652a 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit bcaa7f2260630aaac1dc4c75db0cb877e1b6ba95 +Subproject commit d1795652ab1e9fce645498b94a33ca660d3650f4 From d1b39f5841fbae70576123dc199fba2bcbe358df Mon Sep 17 00:00:00 2001 From: artemp Date: Tue, 21 Jul 2015 11:45:11 +0200 Subject: [PATCH 098/165] handle color parsing in one place --- src/svg/svg_parser.cpp | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/src/svg/svg_parser.cpp b/src/svg/svg_parser.cpp index 701e8fa25..4f00a5768 100644 --- a/src/svg/svg_parser.cpp +++ b/src/svg/svg_parser.cpp @@ -100,7 +100,7 @@ struct key_value_sequence_ordered }; template -agg::rgba8 parse_color(T & error_messages, const char* str) +mapnik::color parse_color(T & error_messages, const char* str) { mapnik::color c(100,100,100); try @@ -111,6 +111,13 @@ agg::rgba8 parse_color(T & error_messages, const char* str) { error_messages.emplace_back(ex.what()); } + return c; +} + +template +agg::rgba8 parse_color_agg(T & error_messages, const char* str) +{ + auto c = parse_color(error_messages, str); return agg::rgba8(c.red(), c.green(), c.blue(), c.alpha()); } @@ -337,7 +344,7 @@ void parse_attr(svg_parser & parser, const xmlChar * name, const xmlChar * value } else { - parser.path_.fill(parse_color(parser.error_messages_, (const char*) value)); + parser.path_.fill(parse_color_agg(parser.error_messages_, (const char*) value)); } } else if (xmlStrEqual(name, BAD_CAST "fill-opacity")) @@ -376,7 +383,7 @@ void parse_attr(svg_parser & parser, const xmlChar * name, const xmlChar * value } else { - parser.path_.stroke(parse_color(parser.error_messages_, (const char*) value)); + parser.path_.stroke(parse_color_agg(parser.error_messages_, (const char*) value)); } } else if (xmlStrEqual(name, BAD_CAST "stroke-width")) @@ -836,14 +843,7 @@ void parse_gradient_stop(svg_parser & parser, xmlTextReaderPtr reader) { if (kv.first == "stop-color") { - try - { - stop_color = mapnik::parse_color(kv.second.c_str()); - } - catch (mapnik::config_error const& ex) - { - parser.error_messages_.emplace_back(ex.what()); - } + stop_color = parse_color(parser.error_messages_, kv.second.c_str()); } else if (kv.first == "stop-opacity") { @@ -856,14 +856,7 @@ void parse_gradient_stop(svg_parser & parser, xmlTextReaderPtr reader) value = xmlTextReaderGetAttribute(reader, BAD_CAST "stop-color"); if (value) { - try - { - stop_color = mapnik::parse_color((const char *) value); - } - catch (mapnik::config_error const& ex) - { - parser.error_messages_.emplace_back(ex.what()); - } + stop_color = parse_color(parser.error_messages_,(const char *) value); xmlFree(value); } From 43b3770a953ec2833f04a79a1967a0e1b53a3d21 Mon Sep 17 00:00:00 2001 From: artemp Date: Tue, 21 Jul 2015 14:12:47 +0200 Subject: [PATCH 099/165] make gradient movable --- include/mapnik/gradient.hpp | 5 +++-- src/gradient.cpp | 34 ++++++++++++++++++++++++---------- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/include/mapnik/gradient.hpp b/include/mapnik/gradient.hpp index eb70be4c2..c7fafd81d 100644 --- a/include/mapnik/gradient.hpp +++ b/include/mapnik/gradient.hpp @@ -79,7 +79,8 @@ class MAPNIK_DECL gradient public: gradient(); gradient(gradient const& other); - gradient& operator=(const gradient& rhs); + gradient(gradient && other); + gradient& operator=(gradient rhs); void set_gradient_type(gradient_e grad); gradient_e get_gradient_type() const; @@ -100,7 +101,7 @@ public: void get_control_points(double &x1, double &y1, double &x2, double &y2) const; private: - void swap(const gradient& other) throw(); + void swap(gradient& other) throw(); }; } diff --git a/src/gradient.cpp b/src/gradient.cpp index ddf665870..a10838597 100644 --- a/src/gradient.cpp +++ b/src/gradient.cpp @@ -66,10 +66,20 @@ gradient::gradient(gradient const& other) units_(other.units_), gradient_type_(other.gradient_type_) {} -gradient & gradient::operator=(const gradient& rhs) +gradient::gradient(gradient && other) + : transform_(std::move(other.transform_)), + x1_(std::move(other.x1_)), + y1_(std::move(other.y1_)), + x2_(std::move(other.x2_)), + y2_(std::move(other.y2_)), + r_(std::move(other.r_)), + stops_(std::move(other.stops_)), + units_(std::move(other.units_)), + gradient_type_(std::move(other.gradient_type_)) {} + +gradient & gradient::operator=(gradient rhs) { - gradient tmp(rhs); - swap(tmp); + swap(rhs); return *this; } @@ -108,7 +118,7 @@ void gradient::add_stop(double offset,mapnik::color const& c) bool gradient::has_stop() const { - return ! stops_.empty(); + return !stops_.empty(); } stop_array const& gradient::get_stop_array() const @@ -116,13 +126,17 @@ stop_array const& gradient::get_stop_array() const return stops_; } -void gradient::swap(const gradient& other) throw() +void gradient::swap(gradient& other) throw() { - gradient_type_=other.gradient_type_; - stops_=other.stops_; - units_=other.units_; - transform_=other.transform_; - other.get_control_points(x1_,y1_,x2_,y2_,r_); + std::swap(gradient_type_, other.gradient_type_); + std::swap(stops_, other.stops_); + std::swap(units_, other.units_); + std::swap(transform_, other.transform_); + std::swap(x1_, other.x1_); + std::swap(y1_, other.y1_); + std::swap(x2_, other.x2_); + std::swap(y2_, other.y2_); + std::swap(r_, other.r_); } void gradient::set_control_points(double x1, double y1, double x2, double y2, double r) From 8faff70620f5bada088cec13252cd3a9f3385636 Mon Sep 17 00:00:00 2001 From: Jiri Drbalek Date: Tue, 21 Jul 2015 12:54:16 +0000 Subject: [PATCH 100/165] fix text-minimum-path-length --- src/text/symbolizer_helpers.cpp | 34 +++++---------------------------- 1 file changed, 5 insertions(+), 29 deletions(-) diff --git a/src/text/symbolizer_helpers.cpp b/src/text/symbolizer_helpers.cpp index 99c7c0356..64fdd7847 100644 --- a/src/text/symbolizer_helpers.cpp +++ b/src/text/symbolizer_helpers.cpp @@ -79,11 +79,10 @@ struct split_multi_geometries { using container_type = T; split_multi_geometries(container_type & cont, view_transform const& t, - proj_transform const& prj_trans, double minimum_path_length) + proj_transform const& prj_trans) : cont_(cont), t_(t), - prj_trans_(prj_trans), - minimum_path_length_(minimum_path_length) {} + prj_trans_(prj_trans) { } void operator() (geometry::geometry_empty const&) const {} void operator() (geometry::multi_point const& multi_pt) const @@ -95,18 +94,7 @@ struct split_multi_geometries } void operator() (geometry::line_string const& line) const { - if (minimum_path_length_ > 0) - { - box2d bbox = t_.forward(geometry::envelope(line), prj_trans_); - if (bbox.width() >= minimum_path_length_) - { - cont_.push_back(base_symbolizer_helper::geometry_cref(std::cref(line))); - } - } - else - { - cont_.push_back(base_symbolizer_helper::geometry_cref(std::cref(line))); - } + cont_.push_back(base_symbolizer_helper::geometry_cref(std::cref(line))); } void operator() (geometry::multi_line_string const& multi_line) const @@ -119,18 +107,7 @@ struct split_multi_geometries void operator() (geometry::polygon const& poly) const { - if (minimum_path_length_ > 0) - { - box2d bbox = t_.forward(geometry::envelope(poly), prj_trans_); - if (bbox.width() >= minimum_path_length_) - { - cont_.push_back(base_symbolizer_helper::geometry_cref(std::cref(poly))); - } - } - else - { - cont_.push_back(base_symbolizer_helper::geometry_cref(std::cref(poly))); - } + cont_.push_back(base_symbolizer_helper::geometry_cref(std::cref(poly))); } void operator() (geometry::multi_polygon const& multi_poly) const @@ -158,7 +135,6 @@ struct split_multi_geometries container_type & cont_; view_transform const& t_; proj_transform const& prj_trans_; - double minimum_path_length_; }; } // ns detail @@ -210,7 +186,7 @@ void base_symbolizer_helper::initialize_geometries() const double minimum_path_length = text_props_->minimum_path_length; auto const& geom = feature_.get_geometry(); util::apply_visitor(detail::split_multi_geometries - (geometries_to_process_, t_, prj_trans_, minimum_path_length ), geom); + (geometries_to_process_, t_, prj_trans_), geom); if (!geometries_to_process_.empty()) { auto type = geometry::geometry_type(geom); From 9af33130637962e5608131209677cd79acb6a9e0 Mon Sep 17 00:00:00 2001 From: artemp Date: Tue, 21 Jul 2015 14:56:23 +0200 Subject: [PATCH 101/165] use `parse_id_from_url` for stroke --- src/svg/svg_parser.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/svg/svg_parser.cpp b/src/svg/svg_parser.cpp index 4f00a5768..25a9b6682 100644 --- a/src/svg/svg_parser.cpp +++ b/src/svg/svg_parser.cpp @@ -360,16 +360,14 @@ void parse_attr(svg_parser & parser, const xmlChar * name, const xmlChar * value } else if (xmlStrEqual(name, BAD_CAST "stroke")) { + std::string id; if (xmlStrEqual(value, BAD_CAST "none")) { parser.path_.stroke_none(); } - else if (boost::starts_with((const char*)value, "url(#")) + else if (parse_id_from_url((const char*)value, id)) { // see if we have a known gradient fill - std::string id = std::string((const char*)&value[5]); - // get rid of the trailing ) - id.erase(id.end()-1); if (parser.gradient_map_.count(id) > 0) { parser.path_.add_stroke_gradient(parser.gradient_map_[id]); From 804115089b1f243fcb5649af94974143ef9d1d43 Mon Sep 17 00:00:00 2001 From: artemp Date: Tue, 21 Jul 2015 20:48:43 +0200 Subject: [PATCH 102/165] clamp rx/ry to valid range (<= 0.5*width/0.5*height) SVG spec : ".. If 'rx' is greater than half of the width of the rectangle, then the user agent must process the 'rect' element with the effective value for 'rx' as half of the width of the rectangle. If 'ry' is greater than half of the height of the rectangle, then the user agent must process the 'rect' element with the effective value for 'ry' as half of the height of the rectangle.." --- src/svg/svg_parser.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/svg/svg_parser.cpp b/src/svg/svg_parser.cpp index 25a9b6682..72a4baebc 100644 --- a/src/svg/svg_parser.cpp +++ b/src/svg/svg_parser.cpp @@ -708,6 +708,7 @@ void parse_ellipse(svg_parser & parser, xmlTextReaderPtr reader) void parse_rect(svg_parser & parser, xmlTextReaderPtr reader) { + // http://www.w3.org/TR/SVGTiny12/shapes.html#RectElement xmlChar *value; double x = 0.0; double y = 0.0; @@ -748,6 +749,7 @@ void parse_rect(svg_parser & parser, xmlTextReaderPtr reader) if (value) { rx = parse_double((const char*)value); + if ( rx > 0.5 * w ) rx = 0.5 * w; xmlFree(value); } else rounded = false; @@ -756,6 +758,7 @@ void parse_rect(svg_parser & parser, xmlTextReaderPtr reader) if (value) { ry = parse_double((const char*)value); + if ( ry > 0.5 * h ) ry = 0.5 * h; if (!rounded) { rx = ry; From 34a1d1315a8d272326e3ba1d0e8d87bf45b5941d Mon Sep 17 00:00:00 2001 From: artemp Date: Tue, 21 Jul 2015 20:48:43 +0200 Subject: [PATCH 103/165] clamp rx/ry to valid range (<= 0.5*width/0.5*height) SVG spec : ".. If 'rx' is greater than half of the width of the rectangle, then the user agent must process the 'rect' element with the effective value for 'rx' as half of the width of the rectangle. If 'ry' is greater than half of the height of the rectangle, then the user agent must process the 'rect' element with the effective value for 'ry' as half of the height of the rectangle.." --- src/svg/svg_parser.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/svg/svg_parser.cpp b/src/svg/svg_parser.cpp index ec895256b..a96fb9a89 100644 --- a/src/svg/svg_parser.cpp +++ b/src/svg/svg_parser.cpp @@ -679,6 +679,7 @@ void parse_ellipse(svg_parser & parser, xmlTextReaderPtr reader) void parse_rect(svg_parser & parser, xmlTextReaderPtr reader) { + // http://www.w3.org/TR/SVGTiny12/shapes.html#RectElement xmlChar *value; double x = 0.0; double y = 0.0; @@ -719,6 +720,7 @@ void parse_rect(svg_parser & parser, xmlTextReaderPtr reader) if (value) { rx = parse_double((const char*)value); + if ( rx > 0.5 * w ) rx = 0.5 * w; xmlFree(value); } else rounded = false; @@ -727,6 +729,7 @@ void parse_rect(svg_parser & parser, xmlTextReaderPtr reader) if (value) { ry = parse_double((const char*)value); + if ( ry > 0.5 * h ) ry = 0.5 * h; if (!rounded) { rx = ry; From 9eadd48d7b588d01f6bd115bd305332d68437f15 Mon Sep 17 00:00:00 2001 From: artemp Date: Wed, 22 Jul 2015 11:52:18 +0200 Subject: [PATCH 104/165] add tests for no-existing svg file + bogus color in `fill` and `stroke` --- src/svg/svg_parser.cpp | 1 + test/data | 2 +- test/unit/svg/svg_parser_test.cpp | 44 ++++++++++++++++++++++++++++++- 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/svg/svg_parser.cpp b/src/svg/svg_parser.cpp index 72a4baebc..6fc51674d 100644 --- a/src/svg/svg_parser.cpp +++ b/src/svg/svg_parser.cpp @@ -109,6 +109,7 @@ mapnik::color parse_color(T & error_messages, const char* str) } catch (mapnik::config_error const& ex) { + error_messages.emplace_back(ex.what()); } return c; diff --git a/test/data b/test/data index d1795652a..41793d29b 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit d1795652ab1e9fce645498b94a33ca660d3650f4 +Subproject commit 41793d29ba9068e68c7ec46da3e4d5e1c2f75b1a diff --git a/test/unit/svg/svg_parser_test.cpp b/test/unit/svg/svg_parser_test.cpp index 6bd37bede..8ff4013bd 100644 --- a/test/unit/svg/svg_parser_test.cpp +++ b/test/unit/svg/svg_parser_test.cpp @@ -27,11 +27,21 @@ #include #include #include + +//#include +//#include +//#include + +#include +#include +#include #include -#include #include + #include // for xmlInitParser(), xmlCleanupParser() #include +#include +#include namespace detail { @@ -52,6 +62,38 @@ struct vertex_equal TEST_CASE("SVG parser") { xmlInitParser(); + SECTION("SVG i/o") + { + std::string svg_name("FAIL"); + std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); + REQUIRE(marker); + REQUIRE(marker->is()); + } + + SECTION("SVG parser color ") + { + + std::string svg_name("./test/data/svg/color_fail.svg"); + std::ifstream in(svg_name.c_str()); + std::string svg_str((std::istreambuf_iterator(in)), + std::istreambuf_iterator()); + + using namespace mapnik::svg; + mapnik::svg_storage_type path; + vertex_stl_adapter stl_storage(path.source()); + svg_path_adapter svg_path(stl_storage); + svg_converter_type svg(svg_path, path.attributes()); + svg_parser p(svg); + + if (!p.parse_from_string(svg_str)) + { + for (auto const& msg : p.error_messages()) + { + REQUIRE(msg == "Failed to parse color: \"fail\""); + } + } + } + SECTION("SVG ") { // From 6ca438be50b873ebda519a9e25ad3157f576e110 Mon Sep 17 00:00:00 2001 From: artemp Date: Wed, 22 Jul 2015 11:58:48 +0200 Subject: [PATCH 105/165] test data -> track master --- .gitmodules | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitmodules b/.gitmodules index d0ae1390c..281ddbda3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -5,3 +5,4 @@ [submodule "test/data-visual"] path = test/data-visual url = https://github.com/mapnik/test-data-visual.git + branch = master From fddc4e0312bf10f544c46e9982f025298061d749 Mon Sep 17 00:00:00 2001 From: artemp Date: Wed, 22 Jul 2015 13:21:17 +0200 Subject: [PATCH 106/165] update tests --- test/data | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/data b/test/data index 41793d29b..86c5f815a 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit 41793d29ba9068e68c7ec46da3e4d5e1c2f75b1a +Subproject commit 86c5f815a772ccdd30fba98d8caed1954435c2ac From 7aa30a211e9593bdf45cf271ce0a6497660b842f Mon Sep 17 00:00:00 2001 From: artemp Date: Wed, 22 Jul 2015 13:29:04 +0200 Subject: [PATCH 107/165] SGV tests: - update expected path data --- test/unit/svg/svg_parser_test.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/test/unit/svg/svg_parser_test.cpp b/test/unit/svg/svg_parser_test.cpp index 8ff4013bd..bc8f65665 100644 --- a/test/unit/svg/svg_parser_test.cpp +++ b/test/unit/svg/svg_parser_test.cpp @@ -342,7 +342,7 @@ TEST_CASE("SVG parser") { REQUIRE(marker->is()); mapnik::marker_svg const& svg = mapnik::util::get(*marker); auto bbox = svg.bounding_box(); - REQUIRE(bbox == mapnik::box2d(1.0,1.0,799.0,399.0)); + REQUIRE(bbox == mapnik::box2d(1.0,1.0,799.0,599.0)); auto storage = svg.get_data(); REQUIRE(storage); mapnik::svg::vertex_stl_adapter stl_storage(storage->source()); @@ -357,17 +357,25 @@ TEST_CASE("SVG parser") { cmd = path.vertex(&x,&y); vec.emplace_back(x, y, cmd); } + std::vector> expected = {std::make_tuple(1, 1, 1), std::make_tuple(799, 1, 2), - std::make_tuple(799, 399, 2), - std::make_tuple(1, 399, 2), + std::make_tuple(799, 599, 2), + std::make_tuple(1, 599, 2), std::make_tuple(1, 1, 79), std::make_tuple(0, 0, 0), std::make_tuple(100, 100, 1), std::make_tuple(700, 100, 2), std::make_tuple(700, 300, 2), std::make_tuple(100, 300, 2), - std::make_tuple(100, 100, 79)}; + std::make_tuple(100, 100, 79), + std::make_tuple(0, 0, 0), + std::make_tuple(100, 320, 1), + std::make_tuple(700, 320, 2), + std::make_tuple(700, 520, 2), + std::make_tuple(100, 520, 2), + std::make_tuple(100, 320, 79)}; + REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin())); } From 626cb9f47c38f561e53618677fda2420643a8ffb Mon Sep 17 00:00:00 2001 From: artemp Date: Wed, 22 Jul 2015 15:28:55 +0200 Subject: [PATCH 108/165] add display=none test --- test/data | 2 +- test/unit/svg/svg_parser_test.cpp | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/test/data b/test/data index 86c5f815a..867d26dbf 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit 86c5f815a772ccdd30fba98d8caed1954435c2ac +Subproject commit 867d26dbfc536e58abcb25d4db5726a921cccf79 diff --git a/test/unit/svg/svg_parser_test.cpp b/test/unit/svg/svg_parser_test.cpp index bc8f65665..d87cf40ef 100644 --- a/test/unit/svg/svg_parser_test.cpp +++ b/test/unit/svg/svg_parser_test.cpp @@ -94,6 +94,24 @@ TEST_CASE("SVG parser") { } } + SECTION("SVG parser display=none") + { + + std::string svg_name("./test/data/svg/invisible.svg"); + std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); + REQUIRE(marker); + REQUIRE(marker->is()); + mapnik::marker_svg const& svg = mapnik::util::get(*marker); + auto bbox = svg.bounding_box(); + REQUIRE(bbox == mapnik::box2d(0, 0, 1, 1)); + auto storage = svg.get_data(); + REQUIRE(storage); + mapnik::svg::vertex_stl_adapter stl_storage(storage->source()); + mapnik::svg::svg_path_adapter path(stl_storage); + double x,y; + REQUIRE(path.vertex(&x,&y) == mapnik::SEG_END); + } + SECTION("SVG ") { // From 158e4b7f8747304678861b7097de00e925a7cd52 Mon Sep 17 00:00:00 2001 From: artemp Date: Wed, 22 Jul 2015 16:34:28 +0200 Subject: [PATCH 109/165] add missing gradient definition test --- test/data | 2 +- test/unit/svg/svg_parser_test.cpp | 45 +++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/test/data b/test/data index 867d26dbf..626a2f6fb 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit 867d26dbfc536e58abcb25d4db5726a921cccf79 +Subproject commit 626a2f6fb9f952e429915d5481b26b4bc0f49504 diff --git a/test/unit/svg/svg_parser_test.cpp b/test/unit/svg/svg_parser_test.cpp index d87cf40ef..15eb66737 100644 --- a/test/unit/svg/svg_parser_test.cpp +++ b/test/unit/svg/svg_parser_test.cpp @@ -396,6 +396,51 @@ TEST_CASE("SVG parser") { REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin())); } + SECTION("SVG missing def") + { + // + std::string svg_name("./test/data/svg/gradient-nodef.svg"); + std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); + REQUIRE(marker); + REQUIRE(marker->is()); + mapnik::marker_svg const& svg = mapnik::util::get(*marker); + auto bbox = svg.bounding_box(); + REQUIRE(bbox == mapnik::box2d(1.0,1.0,799.0,599.0)); + auto storage = svg.get_data(); + REQUIRE(storage); + mapnik::svg::vertex_stl_adapter stl_storage(storage->source()); + mapnik::svg::svg_path_adapter path(stl_storage); + double x,y; + unsigned cmd; + std::vector> vec; + std::size_t num_vertices = path.total_vertices(); + + for (std::size_t i = 0; i < num_vertices; ++i) + { + cmd = path.vertex(&x,&y); + vec.emplace_back(x, y, cmd); + } + + std::vector> expected = {std::make_tuple(1, 1, 1), + std::make_tuple(799, 1, 2), + std::make_tuple(799, 599, 2), + std::make_tuple(1, 599, 2), + std::make_tuple(1, 1, 79), + std::make_tuple(0, 0, 0), + std::make_tuple(100, 100, 1), + std::make_tuple(700, 100, 2), + std::make_tuple(700, 300, 2), + std::make_tuple(100, 300, 2), + std::make_tuple(100, 100, 79), + std::make_tuple(0, 0, 0), + std::make_tuple(100, 320, 1), + std::make_tuple(700, 320, 2), + std::make_tuple(700, 520, 2), + std::make_tuple(100, 520, 2), + std::make_tuple(100, 320, 79)}; + + REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin())); + } xmlCleanupParser(); From 4b9b1b54016880b305331be206690ca0047a5729 Mon Sep 17 00:00:00 2001 From: artemp Date: Wed, 22 Jul 2015 16:38:45 +0200 Subject: [PATCH 110/165] fix err message --- src/svg/svg_parser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/svg/svg_parser.cpp b/src/svg/svg_parser.cpp index 6fc51674d..808306f7a 100644 --- a/src/svg/svg_parser.cpp +++ b/src/svg/svg_parser.cpp @@ -376,7 +376,7 @@ void parse_attr(svg_parser & parser, const xmlChar * name, const xmlChar * value else { std::stringstream ss; - ss << "Failed to find gradient fill: " << id; + ss << "Failed to find gradient stroke: " << id; parser.error_messages_.push_back(ss.str()); } } From 6a1a25efbf6bb04daed147f3e8d37a04cd444946 Mon Sep 17 00:00:00 2001 From: artemp Date: Thu, 23 Jul 2015 11:14:13 +0200 Subject: [PATCH 111/165] remove duplicate `if else` --- src/svg/svg_parser.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/svg/svg_parser.cpp b/src/svg/svg_parser.cpp index 808306f7a..55ee50f94 100644 --- a/src/svg/svg_parser.cpp +++ b/src/svg/svg_parser.cpp @@ -393,10 +393,6 @@ void parse_attr(svg_parser & parser, const xmlChar * name, const xmlChar * value { parser.path_.stroke_opacity(parse_double((const char*)value)); } - else if(xmlStrEqual(name,BAD_CAST "stroke-width")) - { - parser.path_.stroke_width(parse_double((const char*) value)); - } else if(xmlStrEqual(name,BAD_CAST "stroke-linecap")) { if(xmlStrEqual(value,BAD_CAST "butt")) From ce2eaa02eecf08935d0bd2719418a5b5e3b92351 Mon Sep 17 00:00:00 2001 From: artemp Date: Thu, 23 Jul 2015 12:58:14 +0200 Subject: [PATCH 112/165] add operator==() --- include/mapnik/gradient.hpp | 2 +- src/gradient.cpp | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/include/mapnik/gradient.hpp b/include/mapnik/gradient.hpp index c7fafd81d..f8eff6e7d 100644 --- a/include/mapnik/gradient.hpp +++ b/include/mapnik/gradient.hpp @@ -81,7 +81,7 @@ public: gradient(gradient const& other); gradient(gradient && other); gradient& operator=(gradient rhs); - + bool operator==(gradient const& other) const; void set_gradient_type(gradient_e grad); gradient_e get_gradient_type() const; diff --git a/src/gradient.cpp b/src/gradient.cpp index a10838597..037cf8fc7 100644 --- a/src/gradient.cpp +++ b/src/gradient.cpp @@ -83,6 +83,19 @@ gradient & gradient::operator=(gradient rhs) return *this; } +bool gradient::operator==(gradient const& other) const +{ + return transform_ == other.transform_ && + x1_ == other.x1_ && + y1_ == other.y1_ && + x2_ == other.x2_ && + y2_ == other.y2_ && + r_ == other.r_ && + std::equal(stops_.begin(),stops_.end(), other.stops_.begin()), + units_ == other.units_ && + gradient_type_ == other.gradient_type_; +} + void gradient::set_gradient_type(gradient_e grad) { gradient_type_=grad; From e496e24deb539ec54ade8e0f01375e533d768731 Mon Sep 17 00:00:00 2001 From: artemp Date: Thu, 23 Jul 2015 12:58:38 +0200 Subject: [PATCH 113/165] add "Gradients apply to leaf nodes" test and update test data --- test/data | 2 +- test/unit/svg/svg_parser_test.cpp | 49 +++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/test/data b/test/data index 626a2f6fb..7ba42dbac 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit 626a2f6fb9f952e429915d5481b26b4bc0f49504 +Subproject commit 7ba42dbacd1d6a2f9552201b2c5fb8f9d5ae6885 diff --git a/test/unit/svg/svg_parser_test.cpp b/test/unit/svg/svg_parser_test.cpp index 15eb66737..9855a0054 100644 --- a/test/unit/svg/svg_parser_test.cpp +++ b/test/unit/svg/svg_parser_test.cpp @@ -442,6 +442,55 @@ TEST_CASE("SVG parser") { REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin())); } + SECTION("SVG missing def") + { + // + std::string svg_name("./test/data/svg/gradient-inherit.svg"); + std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); + REQUIRE(marker); + REQUIRE(marker->is()); + mapnik::marker_svg const& svg = mapnik::util::get(*marker); + auto bbox = svg.bounding_box(); + REQUIRE(bbox == mapnik::box2d(1.0,1.0,699.0,199.0)); + auto storage = svg.get_data(); + REQUIRE(storage); + mapnik::svg::vertex_stl_adapter stl_storage(storage->source()); + + auto const& attrs = storage->attributes(); + + REQUIRE( attrs[1].fill_gradient == attrs[2].fill_gradient); + + mapnik::svg::svg_path_adapter path(stl_storage); + double x,y; + unsigned cmd; + std::vector> vec; + std::size_t num_vertices = path.total_vertices(); + for (std::size_t i = 0; i < num_vertices; ++i) + { + cmd = path.vertex(&x,&y); + vec.emplace_back(x, y, cmd); + } + + std::vector> expected = {std::make_tuple(1, 1, 1), + std::make_tuple(699, 1, 2), + std::make_tuple(699, 199, 2), + std::make_tuple(1, 199, 2), + std::make_tuple(1, 1, 79), + std::make_tuple(0, 0, 0), + std::make_tuple(100, 50, 1), + std::make_tuple(300, 50, 2), + std::make_tuple(300, 150, 2), + std::make_tuple(100, 150, 2), + std::make_tuple(100, 50, 79), + std::make_tuple(0, 0, 0), + std::make_tuple(400, 50, 1), + std::make_tuple(600, 50, 2), + std::make_tuple(600, 150, 2), + std::make_tuple(400, 150, 2), + std::make_tuple(400, 50, 79)}; + + REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin())); + } xmlCleanupParser(); } From 49d4beec5994a4a4d7dfc92404d65040c3c06709 Mon Sep 17 00:00:00 2001 From: artemp Date: Thu, 23 Jul 2015 15:16:08 +0200 Subject: [PATCH 114/165] fix typo --- test/unit/svg/svg_parser_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/svg/svg_parser_test.cpp b/test/unit/svg/svg_parser_test.cpp index 9855a0054..3949e954d 100644 --- a/test/unit/svg/svg_parser_test.cpp +++ b/test/unit/svg/svg_parser_test.cpp @@ -442,7 +442,7 @@ TEST_CASE("SVG parser") { REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin())); } - SECTION("SVG missing def") + SECTION("SVG missing inheritance") { // std::string svg_name("./test/data/svg/gradient-inherit.svg"); From 2e7d75f02254f3779dfd6891817d2c2156f15fab Mon Sep 17 00:00:00 2001 From: artemp Date: Fri, 24 Jul 2015 11:05:26 +0200 Subject: [PATCH 115/165] track errors parsing double values --- src/svg/svg_parser.cpp | 98 +++++++++++++++++++++++------------------- 1 file changed, 53 insertions(+), 45 deletions(-) diff --git a/src/svg/svg_parser.cpp b/src/svg/svg_parser.cpp index 55ee50f94..dbd1fa505 100644 --- a/src/svg/svg_parser.cpp +++ b/src/svg/svg_parser.cpp @@ -122,20 +122,25 @@ agg::rgba8 parse_color_agg(T & error_messages, const char* str) return agg::rgba8(c.red(), c.green(), c.blue(), c.alpha()); } -double parse_double(const char* str) +template +double parse_double(T & error_messages, const char* str) { using namespace boost::spirit::qi; qi::double_type double_; double val = 0.0; - parse(str, str + std::strlen(str),double_,val); + if (!parse(str, str + std::strlen(str),double_,val)) + { + error_messages.emplace_back("Failed to parse double: \"" + std::string(str) + "\""); + } return val; } -/* - * parse a double that might end with a % - * if it does then set the ref bool true and divide the result by 100 - */ -double parse_double_optional_percent(const char* str, bool &percent) + +// parse a double that might end with a % +// if it does then set the ref bool true and divide the result by 100 + +template +double parse_double_optional_percent(T & error_messages, const char* str, bool &percent) { using namespace boost::spirit::qi; using boost::phoenix::ref; @@ -144,8 +149,11 @@ double parse_double_optional_percent(const char* str, bool &percent) qi::char_type char_; double val = 0.0; - parse(str, str + std::strlen(str),double_[ref(val)=_1, ref(percent) = false] - >> -char_('%')[ref(val)/100.0, ref(percent) = true]); + if (!parse(str, str + std::strlen(str),double_[ref(val)=_1, ref(percent) = false] + >> -char_('%')[ref(val)/100.0, ref(percent) = true])) + { + error_messages.emplace_back("Failed to parse double (optional %) from " + std::string(str)); + } return val; } @@ -350,7 +358,7 @@ void parse_attr(svg_parser & parser, const xmlChar * name, const xmlChar * value } else if (xmlStrEqual(name, BAD_CAST "fill-opacity")) { - parser.path_.fill_opacity(parse_double((const char*) value)); + parser.path_.fill_opacity(parse_double(parser.error_messages_, (const char*) value)); } else if (xmlStrEqual(name, BAD_CAST "fill-rule")) { @@ -387,11 +395,11 @@ void parse_attr(svg_parser & parser, const xmlChar * name, const xmlChar * value } else if (xmlStrEqual(name, BAD_CAST "stroke-width")) { - parser.path_.stroke_width(parse_double((const char*)value)); + parser.path_.stroke_width(parse_double(parser.error_messages_, (const char*)value)); } else if (xmlStrEqual(name, BAD_CAST "stroke-opacity")) { - parser.path_.stroke_opacity(parse_double((const char*)value)); + parser.path_.stroke_opacity(parse_double(parser.error_messages_, (const char*)value)); } else if(xmlStrEqual(name,BAD_CAST "stroke-linecap")) { @@ -413,12 +421,12 @@ void parse_attr(svg_parser & parser, const xmlChar * name, const xmlChar * value } else if(xmlStrEqual(name,BAD_CAST "stroke-miterlimit")) { - parser.path_.miter_limit(parse_double((const char*)value)); + parser.path_.miter_limit(parse_double(parser.error_messages_, (const char*)value)); } else if(xmlStrEqual(name, BAD_CAST "opacity")) { - double opacity = parse_double((const char*)value); + double opacity = parse_double(parser.error_messages_, (const char*)value); parser.path_.opacity(opacity); } else if (xmlStrEqual(name, BAD_CAST "visibility")) @@ -471,14 +479,14 @@ void parse_dimensions(svg_parser & parser, xmlTextReaderPtr reader) value = xmlTextReaderGetAttribute(reader, BAD_CAST "width"); if (value) { - width = parse_double((const char*)value); + width = parse_double(parser.error_messages_, (const char*)value); xmlFree(value); } xmlChar *value2; value2 = xmlTextReaderGetAttribute(reader, BAD_CAST "width"); if (value2) { - height = parse_double((const char*)value2); + height = parse_double(parser.error_messages_, (const char*)value2); xmlFree(value2); } parser.path_.set_dimensions(width,height); @@ -567,28 +575,28 @@ void parse_line(svg_parser & parser, xmlTextReaderPtr reader) value = xmlTextReaderGetAttribute(reader, BAD_CAST "x1"); if (value) { - x1 = parse_double((const char*)value); + x1 = parse_double(parser.error_messages_, (const char*)value); xmlFree(value); } value = xmlTextReaderGetAttribute(reader, BAD_CAST "y1"); if (value) { - y1 = parse_double((const char*)value); + y1 = parse_double(parser.error_messages_, (const char*)value); xmlFree(value); } value = xmlTextReaderGetAttribute(reader, BAD_CAST "x2"); if (value) { - x2 = parse_double((const char*)value); + x2 = parse_double(parser.error_messages_,(const char*)value); xmlFree(value); } value = xmlTextReaderGetAttribute(reader, BAD_CAST "y2"); if (value) { - y2 = parse_double((const char*)value); + y2 = parse_double(parser.error_messages_, (const char*)value); xmlFree(value); } @@ -608,21 +616,21 @@ void parse_circle(svg_parser & parser, xmlTextReaderPtr reader) value = xmlTextReaderGetAttribute(reader, BAD_CAST "cx"); if (value) { - cx = parse_double((const char*)value); + cx = parse_double(parser.error_messages_,(const char*)value); xmlFree(value); } value = xmlTextReaderGetAttribute(reader, BAD_CAST "cy"); if (value) { - cy = parse_double((const char*)value); + cy = parse_double(parser.error_messages_,(const char*)value); xmlFree(value); } value = xmlTextReaderGetAttribute(reader, BAD_CAST "r"); if (value) { - r = parse_double((const char*)value); + r = parse_double(parser.error_messages_,(const char*)value); xmlFree(value); } @@ -655,28 +663,28 @@ void parse_ellipse(svg_parser & parser, xmlTextReaderPtr reader) value = xmlTextReaderGetAttribute(reader, BAD_CAST "cx"); if (value) { - cx = parse_double((const char*)value); + cx = parse_double(parser.error_messages_,(const char*)value); xmlFree(value); } value = xmlTextReaderGetAttribute(reader, BAD_CAST "cy"); if (value) { - cy = parse_double((const char*)value); + cy = parse_double(parser.error_messages_,(const char*)value); xmlFree(value); } value = xmlTextReaderGetAttribute(reader, BAD_CAST "rx"); if (value) { - rx = parse_double((const char*)value); + rx = parse_double(parser.error_messages_,(const char*)value); xmlFree(value); } value = xmlTextReaderGetAttribute(reader, BAD_CAST "ry"); if (value) { - ry = parse_double((const char*)value); + ry = parse_double(parser.error_messages_,(const char*)value); xmlFree(value); } @@ -717,27 +725,27 @@ void parse_rect(svg_parser & parser, xmlTextReaderPtr reader) value = xmlTextReaderGetAttribute(reader, BAD_CAST "x"); if (value) { - x = parse_double((const char*)value); + x = parse_double(parser.error_messages_,(const char*)value); xmlFree(value); } value = xmlTextReaderGetAttribute(reader, BAD_CAST "y"); if (value) { - y = parse_double((const char*)value); + y = parse_double(parser.error_messages_,(const char*)value); xmlFree(value); } value = xmlTextReaderGetAttribute(reader, BAD_CAST "width"); if (value) { - w = parse_double((const char*)value); + w = parse_double(parser.error_messages_,(const char*)value); xmlFree(value); } value = xmlTextReaderGetAttribute(reader, BAD_CAST "height"); if (value) { - h = parse_double((const char*)value); + h = parse_double(parser.error_messages_,(const char*)value); xmlFree(value); } @@ -745,7 +753,7 @@ void parse_rect(svg_parser & parser, xmlTextReaderPtr reader) value = xmlTextReaderGetAttribute(reader, BAD_CAST "rx"); if (value) { - rx = parse_double((const char*)value); + rx = parse_double(parser.error_messages_,(const char*)value); if ( rx > 0.5 * w ) rx = 0.5 * w; xmlFree(value); } @@ -754,7 +762,7 @@ void parse_rect(svg_parser & parser, xmlTextReaderPtr reader) value = xmlTextReaderGetAttribute(reader, BAD_CAST "ry"); if (value) { - ry = parse_double((const char*)value); + ry = parse_double(parser.error_messages_,(const char*)value); if ( ry > 0.5 * h ) ry = 0.5 * h; if (!rounded) { @@ -825,7 +833,7 @@ void parse_gradient_stop(svg_parser & parser, xmlTextReaderPtr reader) value = xmlTextReaderGetAttribute(reader, BAD_CAST "offset"); if (value) { - offset = parse_double((const char*)value); + offset = parse_double(parser.error_messages_,(const char*)value); xmlFree(value); } @@ -845,7 +853,7 @@ void parse_gradient_stop(svg_parser & parser, xmlTextReaderPtr reader) } else if (kv.first == "stop-opacity") { - opacity = parse_double(kv.second.c_str()); + opacity = parse_double(parser.error_messages_,kv.second.c_str()); } } xmlFree(value); @@ -861,7 +869,7 @@ void parse_gradient_stop(svg_parser & parser, xmlTextReaderPtr reader) value = xmlTextReaderGetAttribute(reader, BAD_CAST "stop-opacity"); if (value) { - opacity = parse_double((const char *) value); + opacity = parse_double(parser.error_messages_,(const char *) value); xmlFree(value); } @@ -977,21 +985,21 @@ void parse_radial_gradient(svg_parser & parser, xmlTextReaderPtr reader) value = xmlTextReaderGetAttribute(reader, BAD_CAST "cx"); if (value) { - cx = parse_double_optional_percent((const char*)value, has_percent); + cx = parse_double_optional_percent(parser.error_messages_,(const char*)value, has_percent); xmlFree(value); } value = xmlTextReaderGetAttribute(reader, BAD_CAST "cy"); if (value) { - cy = parse_double_optional_percent((const char*)value, has_percent); + cy = parse_double_optional_percent(parser.error_messages_,(const char*)value, has_percent); xmlFree(value); } value = xmlTextReaderGetAttribute(reader, BAD_CAST "fx"); if (value) { - fx = parse_double_optional_percent((const char*)value, has_percent); + fx = parse_double_optional_percent(parser.error_messages_,(const char*)value, has_percent); xmlFree(value); } else @@ -1000,7 +1008,7 @@ void parse_radial_gradient(svg_parser & parser, xmlTextReaderPtr reader) value = xmlTextReaderGetAttribute(reader, BAD_CAST "fy"); if (value) { - fy = parse_double_optional_percent((const char*)value, has_percent); + fy = parse_double_optional_percent(parser.error_messages_,(const char*)value, has_percent); xmlFree(value); } else @@ -1009,7 +1017,7 @@ void parse_radial_gradient(svg_parser & parser, xmlTextReaderPtr reader) value = xmlTextReaderGetAttribute(reader, BAD_CAST "r"); if (value) { - r = parse_double_optional_percent((const char*)value, has_percent); + r = parse_double_optional_percent(parser.error_messages_,(const char*)value, has_percent); xmlFree(value); } // this logic for detecting %'s will not support mixed coordinates. @@ -1041,28 +1049,28 @@ void parse_linear_gradient(svg_parser & parser, xmlTextReaderPtr reader) value = xmlTextReaderGetAttribute(reader, BAD_CAST "x1"); if (value) { - x1 = parse_double_optional_percent((const char*)value, has_percent); + x1 = parse_double_optional_percent(parser.error_messages_,(const char*)value, has_percent); xmlFree(value); } value = xmlTextReaderGetAttribute(reader, BAD_CAST "x2"); if (value) { - x2 = parse_double_optional_percent((const char*)value, has_percent); + x2 = parse_double_optional_percent(parser.error_messages_,(const char*)value, has_percent); xmlFree(value); } value = xmlTextReaderGetAttribute(reader, BAD_CAST "y1"); if (value) { - y1 = parse_double_optional_percent((const char*)value, has_percent); + y1 = parse_double_optional_percent(parser.error_messages_,(const char*)value, has_percent); xmlFree(value); } value = xmlTextReaderGetAttribute(reader, BAD_CAST "y2"); if (value) { - y2 = parse_double_optional_percent((const char*)value, has_percent); + y2 = parse_double_optional_percent(parser.error_messages_,(const char*)value, has_percent); xmlFree(value); } // this logic for detecting %'s will not support mixed coordinates. From ec591c1ffa7168c841c3f455c57fd6a553daf688 Mon Sep 17 00:00:00 2001 From: artemp Date: Fri, 24 Jul 2015 11:05:58 +0200 Subject: [PATCH 116/165] add bogus stroke-width test --- test/data | 2 +- test/unit/svg/svg_parser_test.cpp | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/test/data b/test/data index 7ba42dbac..503856319 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit 7ba42dbacd1d6a2f9552201b2c5fb8f9d5ae6885 +Subproject commit 5038563194e75b95af155a337ce48f6478534022 diff --git a/test/unit/svg/svg_parser_test.cpp b/test/unit/svg/svg_parser_test.cpp index 3949e954d..2378deb55 100644 --- a/test/unit/svg/svg_parser_test.cpp +++ b/test/unit/svg/svg_parser_test.cpp @@ -87,10 +87,11 @@ TEST_CASE("SVG parser") { if (!p.parse_from_string(svg_str)) { - for (auto const& msg : p.error_messages()) - { - REQUIRE(msg == "Failed to parse color: \"fail\""); - } + auto const& errors = p.error_messages(); + REQUIRE(errors.size() == 3); + REQUIRE(errors[0] == "Failed to parse color: \"fail\""); + REQUIRE(errors[1] == "Failed to parse double: \"fail\""); + REQUIRE(errors[2] == "Failed to parse color: \"fail\""); } } @@ -457,8 +458,8 @@ TEST_CASE("SVG parser") { mapnik::svg::vertex_stl_adapter stl_storage(storage->source()); auto const& attrs = storage->attributes(); - - REQUIRE( attrs[1].fill_gradient == attrs[2].fill_gradient); + REQUIRE(attrs.size() == 3 ); + REQUIRE(attrs[1].fill_gradient == attrs[2].fill_gradient); mapnik::svg::svg_path_adapter path(stl_storage); double x,y; From ba8d51e0294d6c72cd510eeead29a02f21e505f0 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 27 Jul 2015 14:57:57 -0700 Subject: [PATCH 117/165] include fixups --- include/mapnik/text/placement_finder.hpp | 1 - include/mapnik/text/placement_finder_impl.hpp | 18 +++++++----------- include/mapnik/text/symbolizer_helpers.hpp | 5 ++++- src/agg/process_line_pattern_symbolizer.cpp | 1 - src/agg/process_markers_symbolizer.cpp | 1 - src/group/group_symbolizer_helper.cpp | 3 --- src/text/placement_finder.cpp | 4 +--- src/text/symbolizer_helpers.cpp | 2 -- 8 files changed, 12 insertions(+), 23 deletions(-) diff --git a/include/mapnik/text/placement_finder.hpp b/include/mapnik/text/placement_finder.hpp index f2bd36db3..0460b2c52 100644 --- a/include/mapnik/text/placement_finder.hpp +++ b/include/mapnik/text/placement_finder.hpp @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include diff --git a/include/mapnik/text/placement_finder_impl.hpp b/include/mapnik/text/placement_finder_impl.hpp index 739238924..1b8f84147 100644 --- a/include/mapnik/text/placement_finder_impl.hpp +++ b/include/mapnik/text/placement_finder_impl.hpp @@ -19,23 +19,19 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ -//mapnik -#include -#include -#include -#include + +// mapnik +//#include +//#include #include #include #include -#include +//#include #include #include +#include -// agg -#include "agg_conv_clip_polyline.h" - -// stl -#include +#include namespace mapnik { diff --git a/include/mapnik/text/symbolizer_helpers.hpp b/include/mapnik/text/symbolizer_helpers.hpp index 714d566ba..8a44bddfc 100644 --- a/include/mapnik/text/symbolizer_helpers.hpp +++ b/include/mapnik/text/symbolizer_helpers.hpp @@ -22,10 +22,13 @@ #ifndef SYMBOLIZER_HELPERS_HPP #define SYMBOLIZER_HELPERS_HPP -//mapnik +// mapnik #include +#include #include #include +#include +#include namespace mapnik { diff --git a/src/agg/process_line_pattern_symbolizer.cpp b/src/agg/process_line_pattern_symbolizer.cpp index 7cdd16648..a6be6b154 100644 --- a/src/agg/process_line_pattern_symbolizer.cpp +++ b/src/agg/process_line_pattern_symbolizer.cpp @@ -50,7 +50,6 @@ #include "agg_span_allocator.h" #include "agg_span_pattern_rgba.h" #include "agg_renderer_outline_image.h" -#include "agg_conv_clip_polyline.h" namespace mapnik { diff --git a/src/agg/process_markers_symbolizer.cpp b/src/agg/process_markers_symbolizer.cpp index ab06ed30c..cb9428260 100644 --- a/src/agg/process_markers_symbolizer.cpp +++ b/src/agg/process_markers_symbolizer.cpp @@ -50,7 +50,6 @@ #include "agg_rasterizer_scanline_aa.h" #include "agg_scanline_u.h" #include "agg_path_storage.h" -#include "agg_conv_clip_polyline.h" #include "agg_conv_transform.h" diff --git a/src/group/group_symbolizer_helper.cpp b/src/group/group_symbolizer_helper.cpp index a8e453a69..2ba70ff9b 100644 --- a/src/group/group_symbolizer_helper.cpp +++ b/src/group/group_symbolizer_helper.cpp @@ -34,9 +34,6 @@ #include #include -//agg -#include "agg_conv_clip_polyline.h" - namespace mapnik { namespace detail { template diff --git a/src/text/placement_finder.cpp b/src/text/placement_finder.cpp index 7c09e56f2..53dda7da7 100644 --- a/src/text/placement_finder.cpp +++ b/src/text/placement_finder.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -32,9 +33,6 @@ #include #include -// agg -#include "agg_conv_clip_polyline.h" - // stl #include diff --git a/src/text/symbolizer_helpers.cpp b/src/text/symbolizer_helpers.cpp index 99c7c0356..66212f88b 100644 --- a/src/text/symbolizer_helpers.cpp +++ b/src/text/symbolizer_helpers.cpp @@ -41,8 +41,6 @@ #include #include -//agg -#include "agg_conv_clip_polyline.h" namespace mapnik { namespace detail { From e82de0109dcd4f48ab9a81e831c257a615ecea30 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 27 Jul 2015 15:35:00 -0700 Subject: [PATCH 118/165] centralize plugin registration for unit tests --- test/unit/core/copy_move_test.cpp | 1 - test/unit/core/exceptions_test.cpp | 2 -- test/unit/datasource/geojson.cpp | 7 ++++--- test/unit/imaging/image_painted_test.cpp | 2 -- test/unit/run.cpp | 4 ++++ 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/unit/core/copy_move_test.cpp b/test/unit/core/copy_move_test.cpp index 690537920..a219178a9 100644 --- a/test/unit/core/copy_move_test.cpp +++ b/test/unit/core/copy_move_test.cpp @@ -23,7 +23,6 @@ SECTION("layers") { std::string shape_plugin("./plugins/input/shape.input"); if (mapnik::util::exists(shape_plugin)) { - mapnik::datasource_cache::instance().register_datasources("plugins/input/shape.input"); mapnik::parameters p; p["type"]="shape"; p["file"]="demo/data/boundaries"; diff --git a/test/unit/core/exceptions_test.cpp b/test/unit/core/exceptions_test.cpp index 332460082..46abcce55 100644 --- a/test/unit/core/exceptions_test.cpp +++ b/test/unit/core/exceptions_test.cpp @@ -62,7 +62,6 @@ SECTION("handling") { std::string csv_plugin("./plugins/input/csv.input"); if (mapnik::util::exists(csv_plugin)) { try { - mapnik::datasource_cache::instance().register_datasource(csv_plugin); mapnik::parameters p; p["type"]="csv"; p["inline"]="x,y\n0,0"; @@ -88,7 +87,6 @@ SECTION("handling") { std::string shape_plugin("./plugins/input/shape.input"); if (mapnik::util::exists(shape_plugin)) { try { - mapnik::datasource_cache::instance().register_datasource(shape_plugin); mapnik::parameters p2; p2["type"]="shape"; p2["file"]="foo"; diff --git a/test/unit/datasource/geojson.cpp b/test/unit/datasource/geojson.cpp index 4fe4f0792..9bcacca95 100644 --- a/test/unit/datasource/geojson.cpp +++ b/test/unit/datasource/geojson.cpp @@ -33,7 +33,6 @@ TEST_CASE("geojson") { std::string geojson_plugin("./plugins/input/geojson.input"); if (mapnik::util::exists(geojson_plugin)) { - mapnik::datasource_cache::instance().register_datasources("plugins/input/geojson.input"); SECTION("json feature cache-feature=\"true\"") { // Create datasource @@ -50,6 +49,7 @@ TEST_CASE("geojson") { query.add_property_name(field.get_name()); } auto features = ds->features(query); + REQUIRE(features != nullptr); auto feature = features->next(); REQUIRE(feature != nullptr); } @@ -69,11 +69,11 @@ TEST_CASE("geojson") { query.add_property_name(field.get_name()); } auto features = ds->features(query); + REQUIRE(features != nullptr); auto feature = features->next(); REQUIRE(feature != nullptr); } - mapnik::datasource_cache::instance().register_datasources("plugins/input/geojson.input"); SECTION("json extra properties cache-feature=\"true\"") { // Create datasource @@ -90,12 +90,12 @@ TEST_CASE("geojson") { query.add_property_name(field.get_name()); } auto features = ds->features(query); + REQUIRE(features != nullptr); auto feature = features->next(); REQUIRE(feature != nullptr); REQUIRE(feature->envelope() == mapnik::box2d(123,456,123,456)); } - mapnik::datasource_cache::instance().register_datasources("plugins/input/geojson.input"); SECTION("json extra properties cache-feature=\"false\"") { // Create datasource @@ -112,6 +112,7 @@ TEST_CASE("geojson") { query.add_property_name(field.get_name()); } auto features = ds->features(query); + REQUIRE(features != nullptr); auto feature = features->next(); REQUIRE(feature != nullptr); REQUIRE(feature->envelope() == mapnik::box2d(123,456,123,456)); diff --git a/test/unit/imaging/image_painted_test.cpp b/test/unit/imaging/image_painted_test.cpp index a1fbbc979..5d0b403f7 100644 --- a/test/unit/imaging/image_painted_test.cpp +++ b/test/unit/imaging/image_painted_test.cpp @@ -21,8 +21,6 @@ SECTION("painting") { std::string csv_plugin("./plugins/input/csv.input"); if (mapnik::util::exists(csv_plugin)) { - datasource_cache::instance().register_datasources(csv_plugin); - Map m(256, 256); feature_type_style lines_style; diff --git a/test/unit/run.cpp b/test/unit/run.cpp index 15ad0991a..8ae4ddc73 100644 --- a/test/unit/run.cpp +++ b/test/unit/run.cpp @@ -1,10 +1,14 @@ #define CATCH_CONFIG_RUNNER #include "catch.hpp" +#include + #include "cleanup.hpp" // run_cleanup() int main (int argc, char* const argv[]) { + mapnik::datasource_cache::instance().register_datasources("plugins/input/"); + int result = Catch::Session().run( argc, argv ); testing::run_cleanup(); From 8efaacd923bb145e2730359aed267c617d4adf1e Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 27 Jul 2015 15:53:09 -0700 Subject: [PATCH 119/165] add recent work to changelog #2991, #2985, #2983 [skip ci] --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bfad888ba..3501e433e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ For a complete change history, see the git log. ## Future +- Fixed handling of out of range `rx` and `ry` in SVG `rect` (#2991) +- Fixed reporting of envelope from `mapnik::memory_datasource` when new features are added (#2985) +- Fixed parsing of GeoJSON when unknown properties encountered at `FeatureCollection` level (#2983) - Fixed parsing of GeoJSON when properties contained `{}` (#2964) - Fixed potential hang due to invalid use of `line-geometry-transform` (6d6cb15) From 8ebb9db43193074d2b7927bf69c872a5d4c6dcd9 Mon Sep 17 00:00:00 2001 From: artemp Date: Tue, 21 Jul 2015 10:25:19 +0200 Subject: [PATCH 120/165] add `xml:id` support ( ref #2989 ) --- src/svg/svg_parser.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/svg/svg_parser.cpp b/src/svg/svg_parser.cpp index a96fb9a89..548ccd294 100644 --- a/src/svg/svg_parser.cpp +++ b/src/svg/svg_parser.cpp @@ -489,7 +489,8 @@ void parse_path(svg_parser & parser, xmlTextReaderPtr reader) { xmlFree(value); xmlChar *id_value; - id_value = xmlTextReaderGetAttribute(reader, BAD_CAST "id"); + id_value = xmlTextReaderGetAttribute(reader, BAD_CAST "xml:id"); + if (!id_value) id_value = xmlTextReaderGetAttribute(reader, BAD_CAST "id"); if (id_value) { std::string id_string((const char *) id_value); @@ -859,7 +860,9 @@ bool parse_common_gradient(svg_parser & parser, xmlTextReaderPtr reader) xmlChar *value; std::string id; - value = xmlTextReaderGetAttribute(reader, BAD_CAST "id"); + value = xmlTextReaderGetAttribute(reader, BAD_CAST "xml:id"); + if (!value) value = xmlTextReaderGetAttribute(reader, BAD_CAST "id"); + if (value) { // start a new gradient From 79bfe8b2f04d336720e7740e4801ddccebc59823 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 27 Jul 2015 15:56:14 -0700 Subject: [PATCH 121/165] Add #2989 to changelog [skip ci] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3501e433e..f04b8619c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ For a complete change history, see the git log. ## Future +- Fixed handling of `xml:id` in SVG parsing (#2989) - Fixed handling of out of range `rx` and `ry` in SVG `rect` (#2991) - Fixed reporting of envelope from `mapnik::memory_datasource` when new features are added (#2985) - Fixed parsing of GeoJSON when unknown properties encountered at `FeatureCollection` level (#2983) From 6f61c0e6c452f9a409317085d154a4a9dd9153e2 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 27 Jul 2015 16:21:31 -0700 Subject: [PATCH 122/165] fix building both postgis and pgraster plugins together - closes #2986 --- SConstruct | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/SConstruct b/SConstruct index 488431df4..9f3c0f4a9 100644 --- a/SConstruct +++ b/SConstruct @@ -1897,6 +1897,8 @@ if not HELP_REQUESTED: # Build the requested and able-to-be-compiled input plug-ins GDAL_BUILT = False OGR_BUILT = False + POSTGIS_BUILT = False + PGRASTER_BUILT = False for plugin in env['PLUGINS']: if env['PLUGIN_LINKING'] == 'static' or plugin not in env['REQUESTED_PLUGINS']: if os.path.exists('plugins/input/%s.input' % plugin): @@ -1906,11 +1908,17 @@ if not HELP_REQUESTED: if details['lib'] in env['LIBS']: if env['PLUGIN_LINKING'] == 'shared': SConscript('plugins/input/%s/build.py' % plugin) + # hack to avoid breaking on plugins with the same dep if plugin == 'ogr': OGR_BUILT = True if plugin == 'gdal': GDAL_BUILT = True + if plugin == 'postgis': POSTGIS_BUILT = True + if plugin == 'pgraster': PGRASTER_BUILT = True if plugin == 'ogr' or plugin == 'gdal': if GDAL_BUILT and OGR_BUILT: env['LIBS'].remove(details['lib']) + elif plugin == 'postgis' or plugin == 'pgraster': + if POSTGIS_BUILT and PGRASTER_BUILT: + env['LIBS'].remove(details['lib']) else: env['LIBS'].remove(details['lib']) elif not details['lib']: From a5db12123112adde1fca3bb7db79bdbb6eadf663 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 27 Jul 2015 16:45:22 -0700 Subject: [PATCH 123/165] followup to #2990 [skip ci] --- src/text/symbolizer_helpers.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/text/symbolizer_helpers.cpp b/src/text/symbolizer_helpers.cpp index 3570d80de..860359af6 100644 --- a/src/text/symbolizer_helpers.cpp +++ b/src/text/symbolizer_helpers.cpp @@ -181,7 +181,6 @@ struct largest_bbox_first void base_symbolizer_helper::initialize_geometries() const { - double minimum_path_length = text_props_->minimum_path_length; auto const& geom = feature_.get_geometry(); util::apply_visitor(detail::split_multi_geometries (geometries_to_process_, t_, prj_trans_), geom); From c7384e781c0db2e26cd406ddc0029f94a415c8f1 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 27 Jul 2015 17:08:01 -0700 Subject: [PATCH 124/165] be explicit about pointing at master of test-data-visual --- .gitmodules | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitmodules b/.gitmodules index d0ae1390c..d2185cdfa 100644 --- a/.gitmodules +++ b/.gitmodules @@ -5,3 +5,4 @@ [submodule "test/data-visual"] path = test/data-visual url = https://github.com/mapnik/test-data-visual.git + branch = master From c50f72764fb451315b30e0bb0b359693799280b0 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 27 Jul 2015 17:09:46 -0700 Subject: [PATCH 125/165] update tests + add #2990 to changelog --- CHANGELOG.md | 1 + test/data-visual | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f04b8619c..a6e34c837 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ For a complete change history, see the git log. ## Future +- Fixed rendering behavior for `text-minimum-path-length` which regressed in 3.0.0 (#2990) - Fixed handling of `xml:id` in SVG parsing (#2989) - Fixed handling of out of range `rx` and `ry` in SVG `rect` (#2991) - Fixed reporting of envelope from `mapnik::memory_datasource` when new features are added (#2985) diff --git a/test/data-visual b/test/data-visual index 80c744348..31a2dd518 160000 --- a/test/data-visual +++ b/test/data-visual @@ -1 +1 @@ -Subproject commit 80c744348de8cb03a519e451129a243610e55f52 +Subproject commit 31a2dd518ab3776e8cfe6cbbf75258f57c44b36e From 0e8887171dfa8ac7a7d9ef2dd7a8df177110b4a7 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 27 Jul 2015 17:26:59 -0700 Subject: [PATCH 126/165] update visual tests after #2988 - https://github.com/mapnik/test-data-visual/commit/e3d79ed493485afbb5c79cd90913c8db488561b6 --- test/data-visual | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/data-visual b/test/data-visual index 31a2dd518..e3d79ed49 160000 --- a/test/data-visual +++ b/test/data-visual @@ -1 +1 @@ -Subproject commit 31a2dd518ab3776e8cfe6cbbf75258f57c44b36e +Subproject commit e3d79ed493485afbb5c79cd90913c8db488561b6 From 47c3139371906ec7ea0778420a735f60625f703c Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 27 Jul 2015 17:29:18 -0700 Subject: [PATCH 127/165] Add #2963 to changelog [skip ci] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6e34c837..dcf9251f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ For a complete change history, see the git log. ## Future +- Fixed text placement performance after #2949 (#2963) - Fixed rendering behavior for `text-minimum-path-length` which regressed in 3.0.0 (#2990) - Fixed handling of `xml:id` in SVG parsing (#2989) - Fixed handling of out of range `rx` and `ry` in SVG `rect` (#2991) From 411486eaf3b54ece9fc353fc4940300b73e9e50d Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 27 Jul 2015 17:38:36 -0700 Subject: [PATCH 128/165] ignore warnings for karma --- include/mapnik/json/geometry_generator_grammar_impl.hpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/mapnik/json/geometry_generator_grammar_impl.hpp b/include/mapnik/json/geometry_generator_grammar_impl.hpp index c6968cfc0..f94498065 100644 --- a/include/mapnik/json/geometry_generator_grammar_impl.hpp +++ b/include/mapnik/json/geometry_generator_grammar_impl.hpp @@ -23,13 +23,21 @@ // mapnik #include #include +#include // boost +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#pragma GCC diagnostic ignored "-Wunused-local-typedef" +#pragma GCC diagnostic ignored "-Wshadow" +#pragma GCC diagnostic ignored "-Wsign-conversion" +#pragma GCC diagnostic ignored "-Wconversion" #include #include #include #include #include +#pragma GCC diagnostic pop namespace mapnik { namespace json { From 4e2890bb1844ecf40ca22cc2867dc69df7a5f593 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 27 Jul 2015 17:41:10 -0700 Subject: [PATCH 129/165] iwyu for spirit_transform_attribute.hpp - closes #2993 --- include/mapnik/util/spirit_transform_attribute.hpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/include/mapnik/util/spirit_transform_attribute.hpp b/include/mapnik/util/spirit_transform_attribute.hpp index 1c0bdac54..e924fd822 100644 --- a/include/mapnik/util/spirit_transform_attribute.hpp +++ b/include/mapnik/util/spirit_transform_attribute.hpp @@ -24,6 +24,20 @@ #define MAPNIK_UTIL_SPIRIT_TRANSFORM_ATTRIBUTE_HPP #include +#include + +#include +#include + +// boost +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#pragma GCC diagnostic ignored "-Wunused-local-typedef" +#pragma GCC diagnostic ignored "-Wshadow" +#pragma GCC diagnostic ignored "-Wsign-conversion" +#pragma GCC diagnostic ignored "-Wconversion" +#include +#pragma GCC diagnostic pop namespace boost { namespace spirit { namespace traits { From 28f6f4d63bdd997018d041c67d5cea44e2a62b9e Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 27 Jul 2015 18:30:26 -0700 Subject: [PATCH 130/165] setting up for mapnik v3.0.1 release --- include/mapnik/version.hpp | 4 ++-- localize.sh | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/mapnik/version.hpp b/include/mapnik/version.hpp index 47a200e8f..cbba22f2c 100644 --- a/include/mapnik/version.hpp +++ b/include/mapnik/version.hpp @@ -27,9 +27,9 @@ #define MAPNIK_MAJOR_VERSION 3 #define MAPNIK_MINOR_VERSION 0 -#define MAPNIK_PATCH_VERSION 0 +#define MAPNIK_PATCH_VERSION 1 -// translates to 300000 +// translates to 300001 #define MAPNIK_VERSION (MAPNIK_MAJOR_VERSION*100000) + (MAPNIK_MINOR_VERSION*100) + (MAPNIK_PATCH_VERSION) #ifndef MAPNIK_STRINGIFY diff --git a/localize.sh b/localize.sh index ee62e5987..31c6a6ea5 100755 --- a/localize.sh +++ b/localize.sh @@ -8,6 +8,7 @@ else fi export PATH=$(pwd)/utils/nik2img/:${PATH} +export PATH=$(pwd)/utils/mapnik-config/:${PATH} # mapnik-settings.env is an optional file to store # environment variables that should be used before From 3f26e7a4b051143311c017a8a41bb45bc2510575 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 27 Jul 2015 18:42:09 -0700 Subject: [PATCH 131/165] update CHANGELOG for mapnik v3.0.1 release [skip ci] --- CHANGELOG.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dcf9251f5..a8d10ff78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,15 @@ Developers: Please commit along with changes. For a complete change history, see the git log. -## Future +## 3.0.1 + +Released: July 27th, 2015 + +(Packaged from 28f6f4d) + +#### Summary + +The 3.0.1 fixes a few bugs in geojson parsing, svg parsing, and rendering. It also avoids a potential hang when using `line-geometry-transform` and includes a speedup for text rendering compared to v3.0.0. It is fully back compatibility with v3.0.0 and everyone is encouraged to upgrade. - Fixed text placement performance after #2949 (#2963) - Fixed rendering behavior for `text-minimum-path-length` which regressed in 3.0.0 (#2990) From 8424843eed49710e21759637e7a574cbc8e34690 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 27 Jul 2015 19:04:28 -0700 Subject: [PATCH 132/165] make note of plugin move - refs #2980 [skip ci] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8d10ff78..763444947 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ The 3.0.1 fixes a few bugs in geojson parsing, svg parsing, and rendering. It al - Fixed parsing of GeoJSON when unknown properties encountered at `FeatureCollection` level (#2983) - Fixed parsing of GeoJSON when properties contained `{}` (#2964) - Fixed potential hang due to invalid use of `line-geometry-transform` (6d6cb15) +- Moved unmaintained plugins out of core: `osm`, `occi`, and `rasterlite` (#2980) ## 3.0.0 From c8370485da83678373d747e114c96f4c0e8af7ac Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 27 Jul 2015 19:05:18 -0700 Subject: [PATCH 133/165] use latest testdata [skip ci] --- test/data | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/data b/test/data index 07e9a9c7c..503856319 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit 07e9a9c7c3e9365d6b0d9023d77dfe897887af1c +Subproject commit 5038563194e75b95af155a337ce48f6478534022 From dfc6a41a27029e587ad8a1dc67175e473d6a530c Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 27 Jul 2015 19:17:31 -0700 Subject: [PATCH 134/165] wip: add make release target [skip ci] --- Makefile | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Makefile b/Makefile index ef02f71f7..3ff06a59c 100755 --- a/Makefile +++ b/Makefile @@ -12,6 +12,25 @@ all: mapnik install: $(PYTHON) scons/scons.py -j$(JOBS) --config=cache --implicit-cache --max-drift=1 install +release: + export MAPNIK_VERSION=$(shell ./utils/mapnik-config/mapnik-config --version) && \ + export TARBALL_NAME="mapnik-v$${MAPNIK_VERSION}" && \ + cd /tmp/ && \ + rm -rf $${TARBALL_NAME} && \ + git clone --depth 1 --branch v$${MAPNIK_VERSION} git@github.com:mapnik/mapnik.git $${TARBALL_NAME} && \ + cd $${TARBALL_NAME} && \ + git checkout "tags/v$${MAPNIK_VERSION}" && \ + git submodule update --depth 1 --init && \ + rm -rf test/data/.git && \ + rm -rf test/data/.gitignore && \ + rm -rf test/data-visual/.git && \ + rm -rf test/data-visual/.gitignore && \ + rm -rf .git && \ + rm -rf .gitignore && \ + cd ../ && \ + tar cjf $${TARBALL_NAME}.tar.bz2 $${TARBALL_NAME}/ && \ + aws s3 cp --acl public-read $${TARBALL_NAME}.tar.bz2 s3://mapnik/dist/v$${MAPNIK_VERSION}/ + python: if [ ! -d ./bindings/python ]; then git clone git@github.com:mapnik/python-mapnik.git --recursive ./bindings/python; else (cd bindings/python && git pull && git submodule update --init); fi; make From a78a89564b41accbaccb402735b0adf0aaf59c3b Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 27 Jul 2015 20:27:21 -0700 Subject: [PATCH 135/165] back to development of > Mapnik 3.0.1 [skip ci] --- include/mapnik/version.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/mapnik/version.hpp b/include/mapnik/version.hpp index cbba22f2c..a0a7b9d8b 100644 --- a/include/mapnik/version.hpp +++ b/include/mapnik/version.hpp @@ -23,7 +23,7 @@ #ifndef MAPNIK_VERSION_HPP #define MAPNIK_VERSION_HPP -#define MAPNIK_VERSION_IS_RELEASE 1 +#define MAPNIK_VERSION_IS_RELEASE 0 #define MAPNIK_MAJOR_VERSION 3 #define MAPNIK_MINOR_VERSION 0 From 4ee453ada0aebe51c39334b1e53570c7e6b581e3 Mon Sep 17 00:00:00 2001 From: artemp Date: Tue, 28 Jul 2015 10:30:24 +0200 Subject: [PATCH 136/165] update test data --- test/data-visual | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/data-visual b/test/data-visual index e3d79ed49..80c744348 160000 --- a/test/data-visual +++ b/test/data-visual @@ -1 +1 @@ -Subproject commit e3d79ed493485afbb5c79cd90913c8db488561b6 +Subproject commit 80c744348de8cb03a519e451129a243610e55f52 From 515da3f9a87b581a5e2abdbb4e038821a2919852 Mon Sep 17 00:00:00 2001 From: artemp Date: Tue, 28 Jul 2015 16:33:39 +0200 Subject: [PATCH 137/165] rapidxml based svg parser - initial impl --- src/svg/svg_parser.cpp | 854 ++++++++++++++++++----------------------- 1 file changed, 375 insertions(+), 479 deletions(-) diff --git a/src/svg/svg_parser.cpp b/src/svg/svg_parser.cpp index dbd1fa505..fd9733c89 100644 --- a/src/svg/svg_parser.cpp +++ b/src/svg/svg_parser.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include "agg_ellipse.h" #include "agg_rounded_rect.h" #include "agg_span_gradient.h" @@ -41,42 +42,40 @@ #include #include #include +// rapidxml +#include #pragma GCC diagnostic pop #include #include #include #include - -// xml2 -#include - +#include namespace mapnik { namespace svg { -bool parse_reader(svg_parser & parser,xmlTextReaderPtr reader); -void process_node(svg_parser & parser,xmlTextReaderPtr reader); -void start_element(svg_parser & parser,xmlTextReaderPtr reader); -void end_element(svg_parser & parser,xmlTextReaderPtr reader); -void parse_path(svg_parser & parser,xmlTextReaderPtr reader); -void parse_dimensions(svg_parser & parser,xmlTextReaderPtr reader); -void parse_polygon(svg_parser & parser,xmlTextReaderPtr reader); -void parse_polyline(svg_parser & parser,xmlTextReaderPtr reader); -void parse_line(svg_parser & parser,xmlTextReaderPtr reader); -void parse_rect(svg_parser & parser,xmlTextReaderPtr reader); -void parse_circle(svg_parser & parser,xmlTextReaderPtr reader); -void parse_ellipse(svg_parser & parser,xmlTextReaderPtr reader); -void parse_linear_gradient(svg_parser & parser,xmlTextReaderPtr reader); -void parse_radial_gradient(svg_parser & parser,xmlTextReaderPtr reader); -bool parse_common_gradient(svg_parser & parser,xmlTextReaderPtr reader); -void parse_gradient_stop(svg_parser & parser,xmlTextReaderPtr reader); -void parse_attr(svg_parser & parser,xmlTextReaderPtr reader); -void parse_attr(svg_parser & parser,const xmlChar * name, const xmlChar * value ); +namespace rapidxml = boost::property_tree::detail::rapidxml; + +bool traverse_tree(svg_parser & parser,rapidxml::xml_node const* node); +void end_element(svg_parser & parser,rapidxml::xml_node const* node); +void parse_path(svg_parser & parser,rapidxml::xml_node const* node); +void parse_dimensions(svg_parser & parser,rapidxml::xml_node const* node); +void parse_polygon(svg_parser & parser,rapidxml::xml_node const* node); +void parse_polyline(svg_parser & parser,rapidxml::xml_node const* node); +void parse_line(svg_parser & parser,rapidxml::xml_node const* node); +void parse_rect(svg_parser & parser,rapidxml::xml_node const* node); +void parse_circle(svg_parser & parser,rapidxml::xml_node const* node); +void parse_ellipse(svg_parser & parser,rapidxml::xml_node const* node); +void parse_linear_gradient(svg_parser & parser,rapidxml::xml_node const* node); +void parse_radial_gradient(svg_parser & parser,rapidxml::xml_node const* node); +bool parse_common_gradient(svg_parser & parser,rapidxml::xml_node const* node); +void parse_gradient_stop(svg_parser & parser,rapidxml::xml_node const* node); +void parse_attr(svg_parser & parser,rapidxml::xml_node const* node); +void parse_attr(svg_parser & parser,char const * name, char const* value); + using color_lookup_type = std::vector >; - namespace qi = boost::spirit::qi; - using pairs_type = std::vector >; template @@ -172,172 +171,157 @@ bool parse_id_from_url (char const* str, std::string & id) qi::_1_type _1; qi::char_type char_; qi::lit_type lit; - return phrase_parse(str, str + std::strlen(str), lit("url") > "(" > "#" > *(char_ - lit(')'))[boost::phoenix::ref(id) += _1] > ")" , skip_type()); + return phrase_parse(str, str + std::strlen(str), + lit("url") > "(" > "#" > *(char_ - lit(')'))[boost::phoenix::ref(id) += _1] > ")", + skip_type()); } -bool parse_reader(svg_parser & parser, xmlTextReaderPtr reader) +bool traverse_tree(svg_parser & parser, rapidxml::xml_node const* node) { - int ret = xmlTextReaderRead(reader); - try { - while (ret == 1) + auto const* name = node->name(); + switch (node->type()) + { + case rapidxml::node_element: + { + if (std::strcmp(name, "defs") == 0) { - process_node(parser,reader); - ret = xmlTextReaderRead(reader); + parser.is_defs_ = true; } + // the gradient tags *should* be in defs, but illustrator seems not to put them in there so + // accept them anywhere + else if (std::strcmp(name, "linearGradient") == 0) + { + parse_linear_gradient(parser, node); + } + else if (std::strcmp(name, "radialGradient") == 0) + { + parse_radial_gradient(parser, node); + } + else if (std::strcmp(name, "stop") == 0) + { + parse_gradient_stop(parser, node); + } + + if (!parser.is_defs_) // FIXME + { + if (std::strcmp(name, "g") == 0) + { + parser.path_.push_attr(); + parse_attr(parser, node); + } + else + { + parser.path_.push_attr(); + parse_attr(parser, node); + if (parser.path_.display()) + { + if (std::strcmp(name, "path") == 0) + { + parse_path(parser, node); + } + else if (std::strcmp("polygon", name) == 0) + { + parse_polygon(parser, node); + } + else if (std::strcmp("polyline", name) == 0) + { + parse_polyline(parser, node); + } + else if (std::strcmp(name, "line") == 0) + { + parse_line(parser, node); + } + else if (std::strcmp(name, "rect") == 0) + { + parse_rect(parser, node); + } + else if (std::strcmp(name, "circle") == 0) + { + parse_circle(parser, node); + } + else if (std::strcmp(name, "ellipse") == 0) + { + parse_ellipse(parser, node); + } + else if (std::strcmp(name, "svg") == 0) + { + parse_dimensions(parser, node); + } + else + { + //std::cerr << "unprocessed node <--[" << node->name() << "]\n"; + } + } + parser.path_.pop_attr(); + } + } + + for (auto const* child = node->first_node(); + child; child = child->next_sibling()) + { + traverse_tree(parser, child); + } + + end_element(parser, node); } - catch (std::exception const& ex) + break; + + // Data nodes + case rapidxml::node_data: + case rapidxml::node_cdata: { - xmlFreeTextReader(reader); - throw ex; +#if 0 + if (node->value_size() > 0) // Don't add empty text nodes + { + // parsed text values should have leading and trailing + // whitespace trimmed. + //std::string trimmed = node->value(); + //mapnik::util::trim(trimmed); + std::cerr << "CDATA:" << node->value() << std::endl; + } +#endif } - xmlFreeTextReader(reader); - if (ret != 0) - { - // parsing failure - return false; + break; + default: + break; } return true; } -void start_element(svg_parser & parser, xmlTextReaderPtr reader) +void end_element(svg_parser & parser, rapidxml::xml_node const* node) { - const xmlChar *name; - name = xmlTextReaderConstName(reader); - - if (xmlStrEqual(name, BAD_CAST "defs")) - { - if (xmlTextReaderIsEmptyElement(reader) == 0) - parser.is_defs_ = true; - } - // the gradient tags *should* be in defs, but illustrator seems not to put them in there so - // accept them anywhere - else if (xmlStrEqual(name, BAD_CAST "linearGradient")) - { - parse_linear_gradient(parser,reader); - } - else if (xmlStrEqual(name, BAD_CAST "radialGradient")) - { - parse_radial_gradient(parser,reader); - } - else if (xmlStrEqual(name, BAD_CAST "stop")) - { - parse_gradient_stop(parser,reader); - } - if ( !parser.is_defs_ ) - { - - if (xmlStrEqual(name, BAD_CAST "g")) - { - if (xmlTextReaderIsEmptyElement(reader) == 0) - { - parser.path_.push_attr(); - parse_attr(parser,reader); - } - } - else - { - parser.path_.push_attr(); - parse_attr(parser,reader); - if (parser.path_.display()) - { - if (xmlStrEqual(name, BAD_CAST "path")) - { - parse_path(parser,reader); - } - else if (xmlStrEqual(name, BAD_CAST "polygon") ) - { - parse_polygon(parser,reader); - } - else if (xmlStrEqual(name, BAD_CAST "polyline")) - { - parse_polyline(parser,reader); - } - else if (xmlStrEqual(name, BAD_CAST "line")) - { - parse_line(parser,reader); - } - else if (xmlStrEqual(name, BAD_CAST "rect")) - { - parse_rect(parser,reader); - } - else if (xmlStrEqual(name, BAD_CAST "circle")) - { - parse_circle(parser,reader); - } - else if (xmlStrEqual(name, BAD_CAST "ellipse")) - { - parse_ellipse(parser,reader); - } - else if (xmlStrEqual(name, BAD_CAST "svg")) - { - parse_dimensions(parser,reader); - } -#ifdef MAPNIK_LOG - else if (!xmlStrEqual(name, BAD_CAST "svg")) - { - MAPNIK_LOG_WARN(svg_parser) << "svg_parser: Unhandled svg element=" << name; - } -#endif - } - parser.path_.pop_attr(); - } - } -} - -void end_element(svg_parser & parser, xmlTextReaderPtr reader) -{ - const xmlChar *name; - name = xmlTextReaderConstName(reader); - - - if (!parser.is_defs_ && xmlStrEqual(name, BAD_CAST "g")) + auto const* name = node->name(); + if (!parser.is_defs_ && std::strcmp(name, "g") == 0) { parser.path_.pop_attr(); } - else if (xmlStrEqual(name, BAD_CAST "defs")) + else if (std::strcmp(name, "defs") == 0) { parser.is_defs_ = false; } - else if ((xmlStrEqual(name, BAD_CAST "linearGradient")) || (xmlStrEqual(name, BAD_CAST "radialGradient"))) + else if (std::strcmp(name, "linearGradient") == 0 || std::strcmp(name, "radialGradient") == 0) { parser.gradient_map_[parser.temporary_gradient_.first] = parser.temporary_gradient_.second; } - } -void process_node(svg_parser & parser, xmlTextReaderPtr reader) +void parse_attr(svg_parser & parser, char const* name, char const* value ) { - int node_type = xmlTextReaderNodeType(reader); - switch (node_type) - { - case 1: //start element - start_element(parser,reader); - break; - case 15:// end element - end_element(parser,reader); - break; - default: - break; - } -} - -void parse_attr(svg_parser & parser, const xmlChar * name, const xmlChar * value ) -{ - if (xmlStrEqual(name, BAD_CAST "transform")) + if (std::strcmp(name, "transform") == 0) { agg::trans_affine tr; - mapnik::svg::parse_svg_transform((const char*) value,tr); + mapnik::svg::parse_svg_transform(value,tr); parser.path_.transform().premultiply(tr); } - else if (xmlStrEqual(name, BAD_CAST "fill")) + else if (std::strcmp(name, "fill") == 0) { std::string id; - if (xmlStrEqual(value, BAD_CAST "none")) + if (std::strcmp(value, "none") == 0) { parser.path_.fill_none(); } - else if (parse_id_from_url((const char*)value, id)) + else if (parse_id_from_url(value, id)) { // see if we have a known gradient fill if (parser.gradient_map_.count(id) > 0) @@ -356,25 +340,25 @@ void parse_attr(svg_parser & parser, const xmlChar * name, const xmlChar * value parser.path_.fill(parse_color_agg(parser.error_messages_, (const char*) value)); } } - else if (xmlStrEqual(name, BAD_CAST "fill-opacity")) + else if (std::strcmp(name,"fill-opacity") == 0) { parser.path_.fill_opacity(parse_double(parser.error_messages_, (const char*) value)); } - else if (xmlStrEqual(name, BAD_CAST "fill-rule")) + else if (std::strcmp(name, "fill-rule") == 0) { - if (xmlStrEqual(value, BAD_CAST "evenodd")) + if (std::strcmp(value, "evenodd") == 0) { parser.path_.even_odd(true); } } - else if (xmlStrEqual(name, BAD_CAST "stroke")) + else if (std::strcmp(name, "stroke") == 0) { std::string id; - if (xmlStrEqual(value, BAD_CAST "none")) + if (std::strcmp(value, "none") == 0) { parser.path_.stroke_none(); } - else if (parse_id_from_url((const char*)value, id)) + else if (parse_id_from_url(value, id)) { // see if we have a known gradient fill if (parser.gradient_map_.count(id) > 0) @@ -390,134 +374,115 @@ void parse_attr(svg_parser & parser, const xmlChar * name, const xmlChar * value } else { - parser.path_.stroke(parse_color_agg(parser.error_messages_, (const char*) value)); + parser.path_.stroke(parse_color_agg(parser.error_messages_, value)); } } - else if (xmlStrEqual(name, BAD_CAST "stroke-width")) + else if (std::strcmp(name, "stroke-width") == 0) { - parser.path_.stroke_width(parse_double(parser.error_messages_, (const char*)value)); + parser.path_.stroke_width(parse_double(parser.error_messages_, value)); } - else if (xmlStrEqual(name, BAD_CAST "stroke-opacity")) + else if (std::strcmp(name, "stroke-opacity") == 0) { - parser.path_.stroke_opacity(parse_double(parser.error_messages_, (const char*)value)); + parser.path_.stroke_opacity(parse_double(parser.error_messages_, value)); } - else if(xmlStrEqual(name,BAD_CAST "stroke-linecap")) + else if(std::strcmp(name, "stroke-linecap") == 0) { - if(xmlStrEqual(value,BAD_CAST "butt")) + if(std::strcmp(value, "butt") == 0) parser.path_.line_cap(agg::butt_cap); - else if(xmlStrEqual(value,BAD_CAST "round")) + else if(std::strcmp(value, "round") == 0) parser.path_.line_cap(agg::round_cap); - else if(xmlStrEqual(value,BAD_CAST "square")) + else if(std::strcmp(value, "square") == 0) parser.path_.line_cap(agg::square_cap); } - else if(xmlStrEqual(name,BAD_CAST "stroke-linejoin")) + else if(std::strcmp(name, "stroke-linejoin") == 0) { - if(xmlStrEqual(value,BAD_CAST "miter")) + if(std::strcmp(value, "miter") == 0) parser.path_.line_join(agg::miter_join); - else if(xmlStrEqual(value,BAD_CAST "round")) + else if(std::strcmp(value, "round") == 0) parser.path_.line_join(agg::round_join); - else if(xmlStrEqual(value,BAD_CAST "bevel")) + else if(std::strcmp(value, "bevel") == 0) parser.path_.line_join(agg::bevel_join); } - else if(xmlStrEqual(name,BAD_CAST "stroke-miterlimit")) + else if(std::strcmp(name, "stroke-miterlimit") == 0) { parser.path_.miter_limit(parse_double(parser.error_messages_, (const char*)value)); } - else if(xmlStrEqual(name, BAD_CAST "opacity")) + else if(std::strcmp(name, "opacity") == 0) { double opacity = parse_double(parser.error_messages_, (const char*)value); parser.path_.opacity(opacity); } - else if (xmlStrEqual(name, BAD_CAST "visibility")) + else if (std::strcmp(name, "visibility") == 0) { - parser.path_.visibility(!xmlStrEqual(value, BAD_CAST "hidden")); + parser.path_.visibility(!std::strcmp(value, "hidden")); } - else if (xmlStrEqual(name, BAD_CAST "display") && xmlStrEqual(value, BAD_CAST "none")) + else if (std::strcmp(name, "display") == 0 && std::strcmp(value, "none") == 0) { parser.path_.display(false); } } -void parse_attr(svg_parser & parser, xmlTextReaderPtr reader) +void parse_attr(svg_parser & parser, rapidxml::xml_node const* node) { - const xmlChar *name, *value; - - if (xmlTextReaderMoveToFirstAttribute(reader) == 1) + for (rapidxml::xml_attribute *attr = node->first_attribute(); + attr; attr = attr->next_attribute()) { - do + auto const* name = attr->name(); + if (std::strcmp(name, "style") == 0) { - name = xmlTextReaderConstName(reader); - value = xmlTextReaderConstValue(reader); - - if (xmlStrEqual(name, BAD_CAST "style")) + using cont_type = std::vector >; + using value_type = cont_type::value_type; + cont_type vec; + parse_style(attr->value(), vec); + for (value_type kv : vec ) { - using cont_type = std::vector >; - using value_type = cont_type::value_type; - cont_type vec; - parse_style((const char*)value, vec); - for (value_type kv : vec ) - { - parse_attr(parser,BAD_CAST kv.first.c_str(),BAD_CAST kv.second.c_str()); - } + parse_attr(parser, kv.first.c_str(), kv.second.c_str()); } - else - { - parse_attr(parser,name,value); - } - } while(xmlTextReaderMoveToNextAttribute(reader) == 1); - } - xmlTextReaderMoveToElement(reader); -} - -void parse_dimensions(svg_parser & parser, xmlTextReaderPtr reader) -{ - xmlChar *value; - double width = 0; - double height = 0; - value = xmlTextReaderGetAttribute(reader, BAD_CAST "width"); - if (value) - { - width = parse_double(parser.error_messages_, (const char*)value); - xmlFree(value); - } - xmlChar *value2; - value2 = xmlTextReaderGetAttribute(reader, BAD_CAST "width"); - if (value2) - { - height = parse_double(parser.error_messages_, (const char*)value2); - xmlFree(value2); - } - parser.path_.set_dimensions(width,height); - -} -void parse_path(svg_parser & parser, xmlTextReaderPtr reader) -{ - xmlChar *value; - - value = xmlTextReaderGetAttribute(reader, BAD_CAST "d"); - if (value) - { - // d="" (empty paths) are valid - if (std::strlen((const char*)value) < 1) - { - xmlFree(value); } else + { + parse_attr(parser,name, attr->value()); + } + } +} + +void parse_dimensions(svg_parser & parser, rapidxml::xml_node const * node) +{ + double width = 0; + double height = 0; + auto const* width_attr = node->first_attribute("width"); + if (width_attr) + { + width = parse_double(parser.error_messages_, width_attr->value()); + } + auto const* height_attr = node->first_attribute("height"); + if (height_attr) + { + height = parse_double(parser.error_messages_, height_attr->value()); + } + parser.path_.set_dimensions(width, height); +} + +void parse_path(svg_parser & parser, rapidxml::xml_node const* node) +{ + auto const* attr = node->first_attribute("d"); + if (attr != nullptr) + { + auto const* value = attr->value(); + if (std::strlen(value) > 0) { parser.path_.begin_path(); - if (!mapnik::svg::parse_path((const char*) value, parser.path_)) + if (!mapnik::svg::parse_path(value, parser.path_)) { - xmlChar *id_value; - id_value = xmlTextReaderGetAttribute(reader, BAD_CAST "xml:id"); - if (!id_value) id_value = xmlTextReaderGetAttribute(reader, BAD_CAST "id"); - if (id_value) + auto const* id_attr = node->first_attribute("xml:id"); + if (id_attr != nullptr) id_attr = node->first_attribute("id"); + if (id_attr) { - std::string id_string((const char *) id_value); - xmlFree(id_value); - parser.error_messages_.push_back(std::string("unable to parse invalid svg with id '") + id_string + "'"); + parser.error_messages_.push_back(std::string("unable to parse invalid svg with id '") + + id_attr->value() + "'"); } else { @@ -525,117 +490,89 @@ void parse_path(svg_parser & parser, xmlTextReaderPtr reader) } } parser.path_.end_path(); - xmlFree(value); } } } -void parse_polygon(svg_parser & parser, xmlTextReaderPtr reader) +void parse_polygon(svg_parser & parser, rapidxml::xml_node const* node) { - xmlChar *value; - value = xmlTextReaderGetAttribute(reader, BAD_CAST "points"); - if (value) + auto const* attr = node->first_attribute("points"); + if (attr != nullptr) { parser.path_.begin_path(); - if (!mapnik::svg::parse_points((const char*) value, parser.path_)) + if (!mapnik::svg::parse_points(attr->value(), parser.path_)) { - parser.error_messages_.push_back(std::string("Failed to parse ")); + parser.error_messages_.push_back(std::string("Failed to parse 'points'")); } parser.path_.close_subpath(); parser.path_.end_path(); - xmlFree(value); } } -void parse_polyline(svg_parser & parser, xmlTextReaderPtr reader) +void parse_polyline(svg_parser & parser, rapidxml::xml_node const* node) { - xmlChar *value; - - value = xmlTextReaderGetAttribute(reader, BAD_CAST "points"); - if (value) + auto const* attr = node->first_attribute("points"); + if (attr != nullptr) { parser.path_.begin_path(); - if (!mapnik::svg::parse_points((const char*) value, parser.path_)) + if (!mapnik::svg::parse_points(attr->value(), parser.path_)) { - parser.error_messages_.push_back(std::string("Failed to parse ")); + parser.error_messages_.push_back(std::string("Failed to parse 'points'")); } parser.path_.end_path(); - xmlFree(value); } } -void parse_line(svg_parser & parser, xmlTextReaderPtr reader) +void parse_line(svg_parser & parser, rapidxml::xml_node const* node) { - xmlChar *value; double x1 = 0.0; double y1 = 0.0; double x2 = 0.0; double y2 = 0.0; - value = xmlTextReaderGetAttribute(reader, BAD_CAST "x1"); - if (value) - { - x1 = parse_double(parser.error_messages_, (const char*)value); - xmlFree(value); - } + auto const* x1_attr = node->first_attribute("x1"); + if (x1_attr) x1 = parse_double(parser.error_messages_, x1_attr->value()); - value = xmlTextReaderGetAttribute(reader, BAD_CAST "y1"); - if (value) - { - y1 = parse_double(parser.error_messages_, (const char*)value); - xmlFree(value); - } + auto const* y1_attr = node->first_attribute("y1"); + if (y1_attr) y1 = parse_double(parser.error_messages_, y1_attr->value()); - value = xmlTextReaderGetAttribute(reader, BAD_CAST "x2"); - if (value) - { - x2 = parse_double(parser.error_messages_,(const char*)value); - xmlFree(value); - } + auto const* x2_attr = node->first_attribute("x2"); + if (x2_attr) x2 = parse_double(parser.error_messages_, x2_attr->value()); - value = xmlTextReaderGetAttribute(reader, BAD_CAST "y2"); - if (value) - { - y2 = parse_double(parser.error_messages_, (const char*)value); - xmlFree(value); - } + auto const* y2_attr = node->first_attribute("y2"); + if (y2_attr) y2 = parse_double(parser.error_messages_, y2_attr->value()); parser.path_.begin_path(); parser.path_.move_to(x1, y1); parser.path_.line_to(x2, y2); parser.path_.end_path(); - } -void parse_circle(svg_parser & parser, xmlTextReaderPtr reader) +void parse_circle(svg_parser & parser, rapidxml::xml_node const* node) { - xmlChar *value; double cx = 0.0; double cy = 0.0; double r = 0.0; - value = xmlTextReaderGetAttribute(reader, BAD_CAST "cx"); - if (value) + + auto const* cx_attr = node->first_attribute("cx"); + if (cx_attr != nullptr) { - cx = parse_double(parser.error_messages_,(const char*)value); - xmlFree(value); + cx = parse_double(parser.error_messages_, cx_attr->value()); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "cy"); - if (value) + auto const* cy_attr = node->first_attribute("cy"); + if (cy_attr != nullptr) { - cy = parse_double(parser.error_messages_,(const char*)value); - xmlFree(value); + cy = parse_double(parser.error_messages_, cy_attr->value()); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "r"); - if (value) + auto const* r_attr = node->first_attribute("r"); + if (r_attr != nullptr) { - r = parse_double(parser.error_messages_,(const char*)value); - xmlFree(value); + r = parse_double(parser.error_messages_, r_attr->value()); } parser.path_.begin_path(); - if(r != 0.0) { if (r < 0.0) @@ -648,48 +585,41 @@ void parse_circle(svg_parser & parser, xmlTextReaderPtr reader) parser.path_.storage().concat_path(c); } } - parser.path_.end_path(); } -void parse_ellipse(svg_parser & parser, xmlTextReaderPtr reader) +void parse_ellipse(svg_parser & parser, rapidxml::xml_node const * node) { - xmlChar *value; double cx = 0.0; double cy = 0.0; double rx = 0.0; double ry = 0.0; - value = xmlTextReaderGetAttribute(reader, BAD_CAST "cx"); - if (value) + auto const* cx_attr = node->first_attribute("cx"); + if (cx_attr != nullptr) { - cx = parse_double(parser.error_messages_,(const char*)value); - xmlFree(value); + cx = parse_double(parser.error_messages_, cx_attr->value()); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "cy"); - if (value) + auto const* cy_attr = node->first_attribute("cy"); + if (cy_attr) { - cy = parse_double(parser.error_messages_,(const char*)value); - xmlFree(value); + cy = parse_double(parser.error_messages_, cy_attr->value()); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "rx"); - if (value) + auto const* rx_attr = node->first_attribute("rx"); + if (rx_attr != nullptr) { - rx = parse_double(parser.error_messages_,(const char*)value); - xmlFree(value); + rx = parse_double(parser.error_messages_, rx_attr->value()); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "ry"); - if (value) + auto const* ry_attr = node->first_attribute("ry"); + if (ry_attr != nullptr) { - ry = parse_double(parser.error_messages_,(const char*)value); - xmlFree(value); + ry = parse_double(parser.error_messages_, ry_attr->value()); } parser.path_.begin_path(); - if (rx != 0.0 && ry != 0.0) { if (rx < 0.0) @@ -706,15 +636,12 @@ void parse_ellipse(svg_parser & parser, xmlTextReaderPtr reader) parser.path_.storage().concat_path(c); } } - parser.path_.end_path(); - } -void parse_rect(svg_parser & parser, xmlTextReaderPtr reader) +void parse_rect(svg_parser & parser, rapidxml::xml_node const* node) { // http://www.w3.org/TR/SVGTiny12/shapes.html#RectElement - xmlChar *value; double x = 0.0; double y = 0.0; double w = 0.0; @@ -722,61 +649,55 @@ void parse_rect(svg_parser & parser, xmlTextReaderPtr reader) double rx = 0.0; double ry = 0.0; - value = xmlTextReaderGetAttribute(reader, BAD_CAST "x"); - if (value) + auto const* x_attr = node->first_attribute("x"); + if (x_attr != nullptr) { - x = parse_double(parser.error_messages_,(const char*)value); - xmlFree(value); + x = parse_double(parser.error_messages_, x_attr->value()); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "y"); - if (value) + auto const* y_attr = node->first_attribute("y"); + if (y_attr != nullptr) { - y = parse_double(parser.error_messages_,(const char*)value); - xmlFree(value); + y = parse_double(parser.error_messages_, y_attr->value()); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "width"); - if (value) + auto const* width_attr = node->first_attribute("width"); + if (width_attr != nullptr) { - w = parse_double(parser.error_messages_,(const char*)value); - xmlFree(value); + w = parse_double(parser.error_messages_, width_attr->value()); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "height"); - if (value) + auto const* height_attr = node->first_attribute("height"); + if (height_attr) { - h = parse_double(parser.error_messages_,(const char*)value); - xmlFree(value); + h = parse_double(parser.error_messages_, height_attr->value()); } bool rounded = true; - value = xmlTextReaderGetAttribute(reader, BAD_CAST "rx"); - if (value) + auto const* rx_attr = node->first_attribute("rx"); + if (rx_attr != nullptr) { - rx = parse_double(parser.error_messages_,(const char*)value); + rx = parse_double(parser.error_messages_, rx_attr->value()); if ( rx > 0.5 * w ) rx = 0.5 * w; - xmlFree(value); } else rounded = false; - value = xmlTextReaderGetAttribute(reader, BAD_CAST "ry"); - if (value) + auto const* ry_attr = node->first_attribute("ry"); + if (ry_attr != nullptr) { - ry = parse_double(parser.error_messages_,(const char*)value); + ry = parse_double(parser.error_messages_, ry_attr->value()); if ( ry > 0.5 * h ) ry = 0.5 * h; if (!rounded) { rx = ry; rounded = true; } - xmlFree(value); } else if (rounded) { ry = rx; } - if(w != 0.0 && h != 0.0) + if (w != 0.0 && h != 0.0) { if(w < 0.0) { @@ -794,6 +715,7 @@ void parse_rect(svg_parser & parser, xmlTextReaderPtr reader) { parser.error_messages_.emplace_back("parse_rect: Invalid ry"); } + parser.path_.begin_path(); if(rounded) @@ -815,35 +737,25 @@ void parse_rect(svg_parser & parser, xmlTextReaderPtr reader) } } - -/* - * -*/ -void parse_gradient_stop(svg_parser & parser, xmlTextReaderPtr reader) +void parse_gradient_stop(svg_parser & parser, rapidxml::xml_node const* node) { - xmlChar *value; - double offset = 0.0; mapnik::color stop_color; double opacity = 1.0; - value = xmlTextReaderGetAttribute(reader, BAD_CAST "offset"); - if (value) + auto * attr = node->first_attribute("offset"); + if (attr != nullptr) { - offset = parse_double(parser.error_messages_,(const char*)value); - xmlFree(value); + offset = parse_double(parser.error_messages_,attr->value()); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "style"); - if (value) + attr = node->first_attribute("style"); + if (attr != nullptr) { using cont_type = std::vector >; using value_type = cont_type::value_type; cont_type vec; - parse_style((const char*)value, vec); + parse_style(attr->value(), vec); for (value_type kv : vec ) { @@ -856,51 +768,34 @@ void parse_gradient_stop(svg_parser & parser, xmlTextReaderPtr reader) opacity = parse_double(parser.error_messages_,kv.second.c_str()); } } - xmlFree(value); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "stop-color"); - if (value) + attr = node->first_attribute("stop-color"); + if (attr != nullptr) { - stop_color = parse_color(parser.error_messages_,(const char *) value); - xmlFree(value); + stop_color = parse_color(parser.error_messages_, attr->value()); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "stop-opacity"); - if (value) + attr = node->first_attribute("stop-opacity"); + if (attr != nullptr) { - opacity = parse_double(parser.error_messages_,(const char *) value); - xmlFree(value); + opacity = parse_double(parser.error_messages_, attr->value()); } - stop_color.set_alpha(static_cast(opacity * 255)); parser.temporary_gradient_.second.add_stop(offset, stop_color); - - /* - MAPNIK_LOG_DEBUG(svg_parser) << "\tFound Stop: " << offset << " " - << (unsigned)stop_color.red() << " " - << (unsigned)stop_color.green() << " " - << (unsigned)stop_color.blue() << " " - << (unsigned)stop_color.alpha(); - */ } -bool parse_common_gradient(svg_parser & parser, xmlTextReaderPtr reader) +bool parse_common_gradient(svg_parser & parser, rapidxml::xml_node const* node) { - xmlChar *value; - std::string id; - value = xmlTextReaderGetAttribute(reader, BAD_CAST "xml:id"); - if (!value) value = xmlTextReaderGetAttribute(reader, BAD_CAST "id"); + auto * attr = node->first_attribute("xml:id"); + if (attr == nullptr) attr = node->first_attribute("id"); - if (value) + if (attr != nullptr) { // start a new gradient - gradient new_grad; - id = std::string((const char *) value); - parser.temporary_gradient_ = std::make_pair(id, new_grad); - xmlFree(value); + parser.temporary_gradient_ = std::make_pair(std::string(attr->value()), gradient()); } else { @@ -909,12 +804,13 @@ bool parse_common_gradient(svg_parser & parser, xmlTextReaderPtr reader) } // check if we should inherit from another tag - value = xmlTextReaderGetAttribute(reader, BAD_CAST "xlink:href"); - if (value) + attr = node->first_attribute("xlink:href"); + if (attr != nullptr) { - if (value[0] == '#') + auto const* value = attr->value(); + if (std::strlen(value) > 1 && value[0] == '#') { - std::string linkid = (const char *) &value[1]; + std::string linkid(&value[1]); // FIXME !!! if (parser.gradient_map_.count(linkid)) { parser.temporary_gradient_.second = parser.gradient_map_[linkid]; @@ -924,17 +820,15 @@ bool parse_common_gradient(svg_parser & parser, xmlTextReaderPtr reader) std::stringstream ss; ss << "Failed to find linked gradient " << linkid; parser.error_messages_.push_back(ss.str()); - xmlFree(value); return false; } } - xmlFree(value); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "gradientUnits"); - if (value) + attr = node->first_attribute("gradientUnits"); + if (attr != nullptr) { - if (xmlStrEqual(value, BAD_CAST "userSpaceOnUse")) + if (std::strcmp(attr->value(), "userSpaceOnUse") == 0) { parser.temporary_gradient_.second.set_units(USER_SPACE_ON_USE); } @@ -942,39 +836,22 @@ bool parse_common_gradient(svg_parser & parser, xmlTextReaderPtr reader) { parser.temporary_gradient_.second.set_units(OBJECT_BOUNDING_BOX); } - xmlFree(value); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "gradientTransform"); - if (value) + attr = node->first_attribute("gradientTransform"); + if (attr != nullptr) { agg::trans_affine tr; - mapnik::svg::parse_svg_transform((const char*) value,tr); + mapnik::svg::parse_svg_transform(attr->value(),tr); parser.temporary_gradient_.second.set_transform(tr); - xmlFree(value); } - return true; } -/** - * -*/ -void parse_radial_gradient(svg_parser & parser, xmlTextReaderPtr reader) +void parse_radial_gradient(svg_parser & parser, rapidxml::xml_node const* node) { - //if (!parse_common_gradient(parser,reader)) - // return; - parse_common_gradient(parser, reader); - xmlChar *value; + parse_common_gradient(parser, node); + double cx = 0.5; double cy = 0.5; double fx = 0.0; @@ -982,43 +859,38 @@ void parse_radial_gradient(svg_parser & parser, xmlTextReaderPtr reader) double r = 0.5; bool has_percent=true; - value = xmlTextReaderGetAttribute(reader, BAD_CAST "cx"); - if (value) + auto * attr = node->first_attribute("cx"); + if (attr != nullptr) { - cx = parse_double_optional_percent(parser.error_messages_,(const char*)value, has_percent); - xmlFree(value); + cx = parse_double_optional_percent(parser.error_messages_, attr->value(), has_percent); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "cy"); - if (value) + attr = node->first_attribute("cy"); + if (attr != nullptr) { - cy = parse_double_optional_percent(parser.error_messages_,(const char*)value, has_percent); - xmlFree(value); + cy = parse_double_optional_percent(parser.error_messages_, attr->value(), has_percent); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "fx"); - if (value) + attr = node->first_attribute( "fx"); + if (attr != nullptr) { - fx = parse_double_optional_percent(parser.error_messages_,(const char*)value, has_percent); - xmlFree(value); + fx = parse_double_optional_percent(parser.error_messages_,attr->value(), has_percent); } else fx = cx; - value = xmlTextReaderGetAttribute(reader, BAD_CAST "fy"); - if (value) + attr = node->first_attribute("fy"); + if (attr != nullptr) { - fy = parse_double_optional_percent(parser.error_messages_,(const char*)value, has_percent); - xmlFree(value); + fy = parse_double_optional_percent(parser.error_messages_, attr->value(), has_percent); } else fy = cy; - value = xmlTextReaderGetAttribute(reader, BAD_CAST "r"); - if (value) + attr = node->first_attribute("r"); + if (attr != nullptr) { - r = parse_double_optional_percent(parser.error_messages_,(const char*)value, has_percent); - xmlFree(value); + r = parse_double_optional_percent(parser.error_messages_, attr->value(), has_percent); } // this logic for detecting %'s will not support mixed coordinates. if (has_percent && parser.temporary_gradient_.second.get_units() == USER_SPACE_ON_USE) @@ -1034,44 +906,38 @@ void parse_radial_gradient(svg_parser & parser, xmlTextReaderPtr reader) //MAPNIK_LOG_DEBUG(svg_parser) << "Found Radial Gradient: " << " " << cx << " " << cy << " " << fx << " " << fy << " " << r; } -void parse_linear_gradient(svg_parser & parser, xmlTextReaderPtr reader) +void parse_linear_gradient(svg_parser & parser, rapidxml::xml_node const* node) { - //if (!parse_common_gradient(parser,reader)) - // return; - parse_common_gradient(parser,reader); - xmlChar *value; + parse_common_gradient(parser, node); + double x1 = 0.0; double x2 = 1.0; double y1 = 0.0; double y2 = 1.0; bool has_percent=true; - value = xmlTextReaderGetAttribute(reader, BAD_CAST "x1"); - if (value) + auto const* x1_attr = node->first_attribute("x1"); + if (x1_attr != nullptr) { - x1 = parse_double_optional_percent(parser.error_messages_,(const char*)value, has_percent); - xmlFree(value); + x1 = parse_double_optional_percent(parser.error_messages_, x1_attr->value(), has_percent); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "x2"); - if (value) + auto const* x2_attr = node->first_attribute("x2"); + if (x2_attr != nullptr) { - x2 = parse_double_optional_percent(parser.error_messages_,(const char*)value, has_percent); - xmlFree(value); + x2 = parse_double_optional_percent(parser.error_messages_, x2_attr->value(), has_percent); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "y1"); - if (value) + auto const* y1_attr = node->first_attribute("y1"); + if (y1_attr != nullptr) { - y1 = parse_double_optional_percent(parser.error_messages_,(const char*)value, has_percent); - xmlFree(value); + y1 = parse_double_optional_percent(parser.error_messages_, y1_attr->value(), has_percent); } - value = xmlTextReaderGetAttribute(reader, BAD_CAST "y2"); - if (value) + auto const* y2_attr = node->first_attribute("y2"); + if (y2_attr != nullptr) { - y2 = parse_double_optional_percent(parser.error_messages_,(const char*)value, has_percent); - xmlFree(value); + y2 = parse_double_optional_percent(parser.error_messages_, y2_attr->value(), has_percent); } // this logic for detecting %'s will not support mixed coordinates. if (has_percent && parser.temporary_gradient_.second.get_units() == USER_SPACE_ON_USE) @@ -1083,8 +949,6 @@ void parse_linear_gradient(svg_parser & parser, xmlTextReaderPtr reader) parser.temporary_gradient_.second.set_control_points(x1,y1,x2,y2); // add this here in case we have no end tag, will be replaced if we do parser.gradient_map_[parser.temporary_gradient_.first] = parser.temporary_gradient_.second; - - //MAPNIK_LOG_DEBUG(svg_parser) << "Found Linear Gradient: " << "(" << x1 << " " << y1 << "),(" << x2 << " " << y2 << ")"; } svg_parser::svg_parser(svg_converter stream(filename.c_str()); + if (!stream) { std::stringstream ss; ss << "Unable to open '" << filename << "'"; error_messages_.push_back(ss.str()); + return false; } - else if (!parse_reader(*this,reader)) + + stream.unsetf(std::ios::skipws); + std::vector buffer(std::istreambuf_iterator(stream.rdbuf()), + std::istreambuf_iterator()); + buffer.push_back(0); + + const int flags = rapidxml::parse_trim_whitespace | rapidxml::parse_validate_closing_tags; + rapidxml::xml_document<> doc; + try + { + doc.parse(buffer.data()); + } + catch (rapidxml::parse_error const& ex) { std::stringstream ss; - ss << "Unable to parse '" << filename << "'"; + ss << "svg_parser::parse - Unable to parse '" << filename << "'"; error_messages_.push_back(ss.str()); + return false; + } + + for (rapidxml::xml_node *child = doc.first_node(); + child; child = child->next_sibling()) + { + traverse_tree(*this, child); } return error_messages_.empty() ? true : false; } bool svg_parser::parse_from_string(std::string const& svg) { - xmlTextReaderPtr reader = xmlReaderForMemory(svg.c_str(),safe_cast(svg.size()),nullptr,nullptr, - (XML_PARSE_NOBLANKS | XML_PARSE_NOCDATA | XML_PARSE_NOERROR | XML_PARSE_NOWARNING)); - if (reader == nullptr ||!parse_reader(*this,reader)) + const int flags = rapidxml::parse_trim_whitespace | rapidxml::parse_validate_closing_tags; + rapidxml::xml_document<> doc; + std::vector buffer(svg.begin(), svg.end()); + buffer.push_back(0); + try + { + doc.parse(buffer.data()); + } + catch (rapidxml::parse_error const& ex) { std::stringstream ss; ss << "Unable to parse '" << svg << "'"; error_messages_.push_back(ss.str()); + return false; + } + for (rapidxml::xml_node *child = doc.first_node(); + child; child = child->next_sibling()) + { + traverse_tree(*this, child); } return error_messages_.empty() ? true : false; } From 0b728e676c6ae1b0d020370b4de5528a5fe68680 Mon Sep 17 00:00:00 2001 From: artemp Date: Tue, 28 Jul 2015 16:35:50 +0200 Subject: [PATCH 138/165] update visual test data --- test/data-visual | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/data-visual b/test/data-visual index 80c744348..e3d79ed49 160000 --- a/test/data-visual +++ b/test/data-visual @@ -1 +1 @@ -Subproject commit 80c744348de8cb03a519e451129a243610e55f52 +Subproject commit e3d79ed493485afbb5c79cd90913c8db488561b6 From e9284ce62ae75486b8fe4111ce974349a590f981 Mon Sep 17 00:00:00 2001 From: artemp Date: Tue, 28 Jul 2015 16:50:06 +0200 Subject: [PATCH 139/165] c++ tidy --- src/svg/svg_parser.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/svg/svg_parser.cpp b/src/svg/svg_parser.cpp index fd9733c89..7fcaf1415 100644 --- a/src/svg/svg_parser.cpp +++ b/src/svg/svg_parser.cpp @@ -156,7 +156,7 @@ double parse_double_optional_percent(T & error_messages, const char* str, bool & return val; } -bool parse_style (const char* str, pairs_type & v) +bool parse_style (char const* str, pairs_type & v) { using namespace boost::spirit::qi; using skip_type = boost::spirit::ascii::space_type; @@ -337,12 +337,12 @@ void parse_attr(svg_parser & parser, char const* name, char const* value ) } else { - parser.path_.fill(parse_color_agg(parser.error_messages_, (const char*) value)); + parser.path_.fill(parse_color_agg(parser.error_messages_, value)); } } else if (std::strcmp(name,"fill-opacity") == 0) { - parser.path_.fill_opacity(parse_double(parser.error_messages_, (const char*) value)); + parser.path_.fill_opacity(parse_double(parser.error_messages_, value)); } else if (std::strcmp(name, "fill-rule") == 0) { @@ -405,12 +405,12 @@ void parse_attr(svg_parser & parser, char const* name, char const* value ) } else if(std::strcmp(name, "stroke-miterlimit") == 0) { - parser.path_.miter_limit(parse_double(parser.error_messages_, (const char*)value)); + parser.path_.miter_limit(parse_double(parser.error_messages_,value)); } else if(std::strcmp(name, "opacity") == 0) { - double opacity = parse_double(parser.error_messages_, (const char*)value); + double opacity = parse_double(parser.error_messages_, value); parser.path_.opacity(opacity); } else if (std::strcmp(name, "visibility") == 0) @@ -426,7 +426,7 @@ void parse_attr(svg_parser & parser, char const* name, char const* value ) void parse_attr(svg_parser & parser, rapidxml::xml_node const* node) { - for (rapidxml::xml_attribute *attr = node->first_attribute(); + for (rapidxml::xml_attribute const* attr = node->first_attribute(); attr; attr = attr->next_attribute()) { auto const* name = attr->name(); @@ -448,7 +448,7 @@ void parse_attr(svg_parser & parser, rapidxml::xml_node const* node) } } -void parse_dimensions(svg_parser & parser, rapidxml::xml_node const * node) +void parse_dimensions(svg_parser & parser, rapidxml::xml_node const* node) { double width = 0; double height = 0; From a8f5ddff73ce701788ab0913e6202f21d3c7c910 Mon Sep 17 00:00:00 2001 From: artemp Date: Tue, 28 Jul 2015 19:37:55 +0200 Subject: [PATCH 140/165] handle empty element + fix visibity parsing --- src/svg/svg_parser.cpp | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/svg/svg_parser.cpp b/src/svg/svg_parser.cpp index 7fcaf1415..8f0b04e31 100644 --- a/src/svg/svg_parser.cpp +++ b/src/svg/svg_parser.cpp @@ -185,7 +185,10 @@ bool traverse_tree(svg_parser & parser, rapidxml::xml_node const* node) { if (std::strcmp(name, "defs") == 0) { - parser.is_defs_ = true; + if (node->first_node() != nullptr) + { + parser.is_defs_ = true; + } } // the gradient tags *should* be in defs, but illustrator seems not to put them in there so // accept them anywhere @@ -206,8 +209,11 @@ bool traverse_tree(svg_parser & parser, rapidxml::xml_node const* node) { if (std::strcmp(name, "g") == 0) { - parser.path_.push_attr(); - parse_attr(parser, node); + if (node->first_node() != nullptr) + { + parser.path_.push_attr(); + parse_attr(parser, node); + } } else { @@ -294,11 +300,17 @@ void end_element(svg_parser & parser, rapidxml::xml_node const* node) auto const* name = node->name(); if (!parser.is_defs_ && std::strcmp(name, "g") == 0) { - parser.path_.pop_attr(); + if (node->first_node() != nullptr) + { + parser.path_.pop_attr(); + } } else if (std::strcmp(name, "defs") == 0) { - parser.is_defs_ = false; + if (node->first_node() != nullptr) + { + parser.is_defs_ = false; + } } else if (std::strcmp(name, "linearGradient") == 0 || std::strcmp(name, "radialGradient") == 0) { @@ -415,7 +427,7 @@ void parse_attr(svg_parser & parser, char const* name, char const* value ) } else if (std::strcmp(name, "visibility") == 0) { - parser.path_.visibility(!std::strcmp(value, "hidden")); + parser.path_.visibility(std::strcmp(value, "hidden") != 0); } else if (std::strcmp(name, "display") == 0 && std::strcmp(value, "none") == 0) { From 9531adbf56860919fe173b49f1d6313b1be7fda2 Mon Sep 17 00:00:00 2001 From: artemp Date: Wed, 29 Jul 2015 11:16:51 +0200 Subject: [PATCH 141/165] re-use attribute pointer and reduce number of local vars --- src/svg/svg_parser.cpp | 108 ++++++++++++++++++++--------------------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/src/svg/svg_parser.cpp b/src/svg/svg_parser.cpp index 8f0b04e31..f136a35cf 100644 --- a/src/svg/svg_parser.cpp +++ b/src/svg/svg_parser.cpp @@ -271,12 +271,12 @@ bool traverse_tree(svg_parser & parser, rapidxml::xml_node const* node) end_element(parser, node); } break; - +#if 0 // // Data nodes case rapidxml::node_data: case rapidxml::node_cdata: { -#if 0 + if (node->value_size() > 0) // Don't add empty text nodes { // parsed text values should have leading and trailing @@ -285,9 +285,9 @@ bool traverse_tree(svg_parser & parser, rapidxml::xml_node const* node) //mapnik::util::trim(trimmed); std::cerr << "CDATA:" << node->value() << std::endl; } -#endif } break; +#endif default: break; } @@ -566,22 +566,22 @@ void parse_circle(svg_parser & parser, rapidxml::xml_node const* node) double cy = 0.0; double r = 0.0; - auto const* cx_attr = node->first_attribute("cx"); - if (cx_attr != nullptr) + auto * attr = node->first_attribute("cx"); + if (attr != nullptr) { - cx = parse_double(parser.error_messages_, cx_attr->value()); + cx = parse_double(parser.error_messages_, attr->value()); } - auto const* cy_attr = node->first_attribute("cy"); - if (cy_attr != nullptr) + attr = node->first_attribute("cy"); + if (attr != nullptr) { - cy = parse_double(parser.error_messages_, cy_attr->value()); + cy = parse_double(parser.error_messages_, attr->value()); } - auto const* r_attr = node->first_attribute("r"); - if (r_attr != nullptr) + attr = node->first_attribute("r"); + if (attr != nullptr) { - r = parse_double(parser.error_messages_, r_attr->value()); + r = parse_double(parser.error_messages_, attr->value()); } parser.path_.begin_path(); @@ -607,28 +607,28 @@ void parse_ellipse(svg_parser & parser, rapidxml::xml_node const * node) double rx = 0.0; double ry = 0.0; - auto const* cx_attr = node->first_attribute("cx"); - if (cx_attr != nullptr) + auto * attr = node->first_attribute("cx"); + if (attr != nullptr) { - cx = parse_double(parser.error_messages_, cx_attr->value()); + cx = parse_double(parser.error_messages_, attr->value()); } - auto const* cy_attr = node->first_attribute("cy"); - if (cy_attr) + attr = node->first_attribute("cy"); + if (attr) { - cy = parse_double(parser.error_messages_, cy_attr->value()); + cy = parse_double(parser.error_messages_, attr->value()); } - auto const* rx_attr = node->first_attribute("rx"); - if (rx_attr != nullptr) + attr = node->first_attribute("rx"); + if (attr != nullptr) { - rx = parse_double(parser.error_messages_, rx_attr->value()); + rx = parse_double(parser.error_messages_, attr->value()); } - auto const* ry_attr = node->first_attribute("ry"); - if (ry_attr != nullptr) + attr = node->first_attribute("ry"); + if (attr != nullptr) { - ry = parse_double(parser.error_messages_, ry_attr->value()); + ry = parse_double(parser.error_messages_, attr->value()); } parser.path_.begin_path(); @@ -661,42 +661,42 @@ void parse_rect(svg_parser & parser, rapidxml::xml_node const* node) double rx = 0.0; double ry = 0.0; - auto const* x_attr = node->first_attribute("x"); - if (x_attr != nullptr) + auto * attr = node->first_attribute("x"); + if (attr != nullptr) { - x = parse_double(parser.error_messages_, x_attr->value()); + x = parse_double(parser.error_messages_, attr->value()); } - auto const* y_attr = node->first_attribute("y"); - if (y_attr != nullptr) + attr = node->first_attribute("y"); + if (attr != nullptr) { - y = parse_double(parser.error_messages_, y_attr->value()); + y = parse_double(parser.error_messages_, attr->value()); } - auto const* width_attr = node->first_attribute("width"); - if (width_attr != nullptr) + attr = node->first_attribute("width"); + if (attr != nullptr) { - w = parse_double(parser.error_messages_, width_attr->value()); + w = parse_double(parser.error_messages_, attr->value()); } - auto const* height_attr = node->first_attribute("height"); - if (height_attr) + attr = node->first_attribute("height"); + if (attr) { - h = parse_double(parser.error_messages_, height_attr->value()); + h = parse_double(parser.error_messages_, attr->value()); } bool rounded = true; - auto const* rx_attr = node->first_attribute("rx"); - if (rx_attr != nullptr) + attr = node->first_attribute("rx"); + if (attr != nullptr) { - rx = parse_double(parser.error_messages_, rx_attr->value()); + rx = parse_double(parser.error_messages_, attr->value()); if ( rx > 0.5 * w ) rx = 0.5 * w; } else rounded = false; - auto const* ry_attr = node->first_attribute("ry"); - if (ry_attr != nullptr) + attr = node->first_attribute("ry"); + if (attr != nullptr) { - ry = parse_double(parser.error_messages_, ry_attr->value()); + ry = parse_double(parser.error_messages_, attr->value()); if ( ry > 0.5 * h ) ry = 0.5 * h; if (!rounded) { @@ -928,28 +928,28 @@ void parse_linear_gradient(svg_parser & parser, rapidxml::xml_node const* double y2 = 1.0; bool has_percent=true; - auto const* x1_attr = node->first_attribute("x1"); - if (x1_attr != nullptr) + auto * attr = node->first_attribute("x1"); + if (attr != nullptr) { - x1 = parse_double_optional_percent(parser.error_messages_, x1_attr->value(), has_percent); + x1 = parse_double_optional_percent(parser.error_messages_, attr->value(), has_percent); } - auto const* x2_attr = node->first_attribute("x2"); - if (x2_attr != nullptr) + attr = node->first_attribute("x2"); + if (attr != nullptr) { - x2 = parse_double_optional_percent(parser.error_messages_, x2_attr->value(), has_percent); + x2 = parse_double_optional_percent(parser.error_messages_, attr->value(), has_percent); } - auto const* y1_attr = node->first_attribute("y1"); - if (y1_attr != nullptr) + attr = node->first_attribute("y1"); + if (attr != nullptr) { - y1 = parse_double_optional_percent(parser.error_messages_, y1_attr->value(), has_percent); + y1 = parse_double_optional_percent(parser.error_messages_, attr->value(), has_percent); } - auto const* y2_attr = node->first_attribute("y2"); - if (y2_attr != nullptr) + attr = node->first_attribute("y2"); + if (attr != nullptr) { - y2 = parse_double_optional_percent(parser.error_messages_, y2_attr->value(), has_percent); + y2 = parse_double_optional_percent(parser.error_messages_, attr->value(), has_percent); } // this logic for detecting %'s will not support mixed coordinates. if (has_percent && parser.temporary_gradient_.second.get_units() == USER_SPACE_ON_USE) From 4cf6ce1866963fc7396ec5939af2fbd43f32431a Mon Sep 17 00:00:00 2001 From: artemp Date: Wed, 29 Jul 2015 13:01:51 +0200 Subject: [PATCH 142/165] correct error handling --- src/svg/svg_parser.cpp | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/src/svg/svg_parser.cpp b/src/svg/svg_parser.cpp index f136a35cf..1d5a4410c 100644 --- a/src/svg/svg_parser.cpp +++ b/src/svg/svg_parser.cpp @@ -631,9 +631,9 @@ void parse_ellipse(svg_parser & parser, rapidxml::xml_node const * node) ry = parse_double(parser.error_messages_, attr->value()); } - parser.path_.begin_path(); if (rx != 0.0 && ry != 0.0) { + if (rx < 0.0) { parser.error_messages_.emplace_back("parse_ellipse: Invalid rx"); @@ -644,11 +644,12 @@ void parse_ellipse(svg_parser & parser, rapidxml::xml_node const * node) } else { + parser.path_.begin_path(); agg::ellipse c(cx, cy, rx, ry); parser.path_.storage().concat_path(c); + parser.path_.end_path(); } } - parser.path_.end_path(); } void parse_rect(svg_parser & parser, rapidxml::xml_node const* node) @@ -727,25 +728,27 @@ void parse_rect(svg_parser & parser, rapidxml::xml_node const* node) { parser.error_messages_.emplace_back("parse_rect: Invalid ry"); } - - parser.path_.begin_path(); - - if(rounded) - { - agg::rounded_rect r; - r.rect(x,y,x+w,y+h); - r.radius(rx,ry); - parser.path_.storage().concat_path(r); - } else { - parser.path_.move_to(x, y); - parser.path_.line_to(x + w, y); - parser.path_.line_to(x + w, y + h); - parser.path_.line_to(x, y + h); - parser.path_.close_subpath(); + parser.path_.begin_path(); + + if(rounded) + { + agg::rounded_rect r; + r.rect(x,y,x+w,y+h); + r.radius(rx,ry); + parser.path_.storage().concat_path(r); + } + else + { + parser.path_.move_to(x, y); + parser.path_.line_to(x + w, y); + parser.path_.line_to(x + w, y + h); + parser.path_.line_to(x, y + h); + parser.path_.close_subpath(); + } + parser.path_.end_path(); } - parser.path_.end_path(); } } From d05c13d8491025ebd7b49ac2182fcde3e8eb87b6 Mon Sep 17 00:00:00 2001 From: artemp Date: Wed, 29 Jul 2015 15:42:53 +0200 Subject: [PATCH 143/165] unit test - cope with incorrectly encoded geometries --- test/data | 2 +- test/unit/svg/svg_parser_test.cpp | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/test/data b/test/data index 503856319..e0058873d 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit 5038563194e75b95af155a337ce48f6478534022 +Subproject commit e0058873df6a16b1eeef15c3ae0be09d3c0d700b diff --git a/test/unit/svg/svg_parser_test.cpp b/test/unit/svg/svg_parser_test.cpp index 2378deb55..16748a3ad 100644 --- a/test/unit/svg/svg_parser_test.cpp +++ b/test/unit/svg/svg_parser_test.cpp @@ -95,6 +95,27 @@ TEST_CASE("SVG parser") { } } + SECTION("SVG - cope with erroneous geometries") + { + std::string svg_name("./test/data/svg/errors.svg"); + std::ifstream in(svg_name.c_str()); + std::string svg_str((std::istreambuf_iterator(in)), + std::istreambuf_iterator()); + + using namespace mapnik::svg; + mapnik::svg_storage_type path; + vertex_stl_adapter stl_storage(path.source()); + svg_path_adapter svg_path(stl_storage); + svg_converter_type svg(svg_path, path.attributes()); + svg_parser p(svg); + + if (!p.parse_from_string(svg_str)) + { + auto const& errors = p.error_messages(); + REQUIRE(errors.size() == 10); + } + } + SECTION("SVG parser display=none") { From 5b287ab79841da3a25cb5a62cbc72d3311fe5ddf Mon Sep 17 00:00:00 2001 From: artemp Date: Wed, 29 Jul 2015 16:09:33 +0200 Subject: [PATCH 144/165] catch std::exception to get better stderr --- utils/svg2png/svg2png.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/utils/svg2png/svg2png.cpp b/utils/svg2png/svg2png.cpp index 18fbf8fe3..87b8f0e8c 100644 --- a/utils/svg2png/svg2png.cpp +++ b/utils/svg2png/svg2png.cpp @@ -213,6 +213,12 @@ int main (int argc,char** argv) status = mapnik::util::apply_visitor(visitor, *marker); } } + catch (std::exception const& ex) + { + std::clog << "Exception caught:" << ex.what() << std::endl; + xmlCleanupParser(); + return -1; + } catch (...) { std::clog << "Exception of unknown type!" << std::endl; From f3870cc8fe632357405f0c57e77c36608a01e518 Mon Sep 17 00:00:00 2001 From: artemp Date: Wed, 29 Jul 2015 16:09:51 +0200 Subject: [PATCH 145/165] update test --- test/data | 2 +- test/unit/svg/svg_parser_test.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/data b/test/data index e0058873d..88573f2c3 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit e0058873df6a16b1eeef15c3ae0be09d3c0d700b +Subproject commit 88573f2c329dd4f4ff1f95e9265a8104d5e8f6fa diff --git a/test/unit/svg/svg_parser_test.cpp b/test/unit/svg/svg_parser_test.cpp index 16748a3ad..dfd05a041 100644 --- a/test/unit/svg/svg_parser_test.cpp +++ b/test/unit/svg/svg_parser_test.cpp @@ -112,7 +112,7 @@ TEST_CASE("SVG parser") { if (!p.parse_from_string(svg_str)) { auto const& errors = p.error_messages(); - REQUIRE(errors.size() == 10); + REQUIRE(errors.size() == 12); } } From af27af4af28a8eb60aabd9b6bb99ccddb898266d Mon Sep 17 00:00:00 2001 From: jakepruitt Date: Wed, 29 Jul 2015 22:26:28 -0400 Subject: [PATCH 146/165] Improving svg test coverage - refs #3005 --- test/data | 2 +- test/unit/svg/svg_parser_test.cpp | 183 +++++++++++++++++++++++++++++- 2 files changed, 183 insertions(+), 2 deletions(-) diff --git a/test/data b/test/data index 88573f2c3..5b1fbae49 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit 88573f2c329dd4f4ff1f95e9265a8104d5e8f6fa +Subproject commit 5b1fbae495f27c75c922e1b1fb8ca02fc236a04f diff --git a/test/unit/svg/svg_parser_test.cpp b/test/unit/svg/svg_parser_test.cpp index dfd05a041..5f5772de3 100644 --- a/test/unit/svg/svg_parser_test.cpp +++ b/test/unit/svg/svg_parser_test.cpp @@ -112,7 +112,43 @@ TEST_CASE("SVG parser") { if (!p.parse_from_string(svg_str)) { auto const& errors = p.error_messages(); - REQUIRE(errors.size() == 12); + REQUIRE(errors.size() == 13); + REQUIRE(errors[0] == "parse_rect: Invalid width"); + REQUIRE(errors[1] == "Failed to parse double: \"FAIL\""); + REQUIRE(errors[2] == "parse_rect: Invalid height"); + REQUIRE(errors[3] == "parse_rect: Invalid rx"); + REQUIRE(errors[4] == "parse_rect: Invalid ry"); + REQUIRE(errors[5] == "unable to parse invalid svg "); + REQUIRE(errors[6] == "unable to parse invalid svg with id 'fail-path'"); + REQUIRE(errors[7] == "parse_circle: Invalid radius"); + REQUIRE(errors[8] == "Failed to parse 'points'"); + REQUIRE(errors[9] == "Failed to parse 'points'"); + REQUIRE(errors[10] == "parse_ellipse: Invalid rx"); + REQUIRE(errors[11] == "parse_ellipse: Invalid ry"); + REQUIRE(errors[12] == "parse_rect: Invalid height"); + } + } + + SECTION("SVG parser double % ") + { + + std::string svg_name("./test/data/svg/gradient-radial-error.svg"); + std::ifstream in(svg_name.c_str()); + std::string svg_str((std::istreambuf_iterator(in)), + std::istreambuf_iterator()); + + using namespace mapnik::svg; + mapnik::svg_storage_type path; + vertex_stl_adapter stl_storage(path.source()); + svg_path_adapter svg_path(stl_storage); + svg_converter_type svg(svg_path, path.attributes()); + svg_parser p(svg); + + if (!p.parse_from_string(svg_str)) + { + auto const& errors = p.error_messages(); + REQUIRE(errors.size() == 1); + REQUIRE(errors[0] == "Failed to parse double (optional %) from FAIL"); } } @@ -134,6 +170,38 @@ TEST_CASE("SVG parser") { REQUIRE(path.vertex(&x,&y) == mapnik::SEG_END); } + SECTION("SVG parser stroke-linecap=square") + { + + std::string svg_name("./test/data/svg/stroke-linecap-square.svg"); + std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); + REQUIRE(marker); + REQUIRE(marker->is()); + mapnik::marker_svg const& svg = mapnik::util::get(*marker); + auto bbox = svg.bounding_box(); + REQUIRE(bbox == mapnik::box2d(5, 60, 220, 60)); + auto storage = svg.get_data(); + REQUIRE(storage); + mapnik::svg::vertex_stl_adapter stl_storage(storage->source()); + mapnik::svg::svg_path_adapter path(stl_storage); + + auto const& attrs = storage->attributes(); + agg::line_cap_e expected_cap(agg::square_cap); + REQUIRE(attrs.size() == 1 ); + REQUIRE(attrs[0].line_cap == expected_cap); + + double x,y; + unsigned cmd; + std::vector> vec; + while ((cmd = path.vertex(&x,&y)) != mapnik::SEG_END) + { + vec.emplace_back(x, y, cmd); + } + std::vector> expected = { std::make_tuple(5, 60, 1), + std::make_tuple(220, 60, 2) }; + REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin())); + } + SECTION("SVG ") { // @@ -211,6 +279,71 @@ TEST_CASE("SVG parser") { REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin(),detail::vertex_equal<3>())); } + SECTION("SVG rounded s missing rx or ry") + { + std::string svg_name("./test/data/svg/rounded_rect-missing-one-radius.svg"); + std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); + REQUIRE(marker); + REQUIRE(marker->is()); + mapnik::marker_svg const& svg = mapnik::util::get(*marker); + auto bbox = svg.bounding_box(); + REQUIRE(bbox == mapnik::box2d(0, 0, 20, 15)); + auto storage = svg.get_data(); + REQUIRE(storage); + mapnik::svg::vertex_stl_adapter stl_storage(storage->source()); + mapnik::svg::svg_path_adapter path(stl_storage); + double x,y; + unsigned cmd; + std::vector> vec; + + while ((cmd = path.vertex(&x,&y)) != mapnik::SEG_END) + { + vec.emplace_back(x, y, cmd); + } + std::vector> expected = {std::make_tuple(0, 5,1), + std::make_tuple(0.481856, 2.85842,2), + std::make_tuple(1.83455, 1.12961,2), + std::make_tuple(3.79736, 0.146789,2), + std::make_tuple(5, 0,2), + std::make_tuple(15, 0,2), + std::make_tuple(17.1416, 0.481856,2), + std::make_tuple(18.8704, 1.83455,2), + std::make_tuple(19.8532, 3.79736,2), + std::make_tuple(20, 5,2), + std::make_tuple(20, 10,2), + std::make_tuple(19.5181, 12.1416,2), + std::make_tuple(18.1654, 13.8704,2), + std::make_tuple(16.2026, 14.8532,2), + std::make_tuple(15, 15,2), + std::make_tuple(5, 15,2), + std::make_tuple(2.85842, 14.5181,2), + std::make_tuple(1.12961, 13.1654,2), + std::make_tuple(0.146789, 11.2026,2), + std::make_tuple(0, 10,2), + std::make_tuple(0, 10,95)}; + + REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin(),detail::vertex_equal<3>())); + } + + SECTION("SVG beveled ") + { + std::string svg_name("./test/data/svg/stroke-linejoin-bevel.svg"); + std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); + REQUIRE(marker); + REQUIRE(marker->is()); + mapnik::marker_svg const& svg = mapnik::util::get(*marker); + auto bbox = svg.bounding_box(); + REQUIRE(bbox == mapnik::box2d(10, 10, 30, 25)); + auto storage = svg.get_data(); + REQUIRE(storage); + mapnik::svg::vertex_stl_adapter stl_storage(storage->source()); + + auto const& attrs = storage->attributes(); + agg::line_join_e expected_join(agg::bevel_join); + REQUIRE(attrs.size() == 1 ); + REQUIRE(attrs[0].line_join == expected_join); + } + SECTION("SVG ") { // @@ -418,6 +551,7 @@ TEST_CASE("SVG parser") { REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin())); } + SECTION("SVG missing def") { // @@ -464,6 +598,30 @@ TEST_CASE("SVG parser") { REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin())); } + SECTION("SVG missing id") + { + + std::string svg_name("./test/data/svg/gradient-no-id.svg"); + std::ifstream in(svg_name.c_str()); + std::string svg_str((std::istreambuf_iterator(in)), + std::istreambuf_iterator()); + + using namespace mapnik::svg; + mapnik::svg_storage_type path; + vertex_stl_adapter stl_storage(path.source()); + svg_path_adapter svg_path(stl_storage); + svg_converter_type svg(svg_path, path.attributes()); + svg_parser p(svg); + + if (!p.parse_from_string(svg_str)) + { + auto const& errors = p.error_messages(); + REQUIRE(errors.size() == 2); + REQUIRE(errors[0] == "Failed to find gradient fill: MyGradient"); + REQUIRE(errors[1] == "Failed to find gradient stroke: MyGradient"); + } + } + SECTION("SVG missing inheritance") { // @@ -514,5 +672,28 @@ TEST_CASE("SVG parser") { REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin())); } + SECTION("SVG with transformations") + { + // + std::string svg_name("./test/data/svg/gradient-transform.svg"); + std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); + REQUIRE(marker); + REQUIRE(marker->is()); + mapnik::marker_svg const& svg = mapnik::util::get(*marker); + auto bbox = svg.bounding_box(); + REQUIRE(bbox == mapnik::box2d(1.0,1.0,799.0,599.0)); + auto storage = svg.get_data(); + REQUIRE(storage); + + auto const& attrs = storage->attributes(); + REQUIRE(attrs.size() == 3 ); + REQUIRE(attrs[1].fill_gradient == attrs[2].fill_gradient); + REQUIRE(attrs[1].fill_gradient.get_gradient_type() == mapnik::RADIAL); + agg::trans_affine transform; + transform *= agg::trans_affine_translation(240,155); + REQUIRE(attrs[1].fill_gradient.get_transform() == transform); + } + + xmlCleanupParser(); } From b4bc16751c2eb41a1bca25cc225f887a5ad63d7d Mon Sep 17 00:00:00 2001 From: jakepruitt Date: Wed, 29 Jul 2015 22:37:30 -0400 Subject: [PATCH 147/165] Adjusted error message logic for logging path ids --- src/svg/svg_parser.cpp | 2 +- test/data | 2 +- test/unit/svg/svg_parser_test.cpp | 15 ++++++++------- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/svg/svg_parser.cpp b/src/svg/svg_parser.cpp index 1d5a4410c..293235268 100644 --- a/src/svg/svg_parser.cpp +++ b/src/svg/svg_parser.cpp @@ -490,7 +490,7 @@ void parse_path(svg_parser & parser, rapidxml::xml_node const* node) if (!mapnik::svg::parse_path(value, parser.path_)) { auto const* id_attr = node->first_attribute("xml:id"); - if (id_attr != nullptr) id_attr = node->first_attribute("id"); + if (id_attr == nullptr) id_attr = node->first_attribute("id"); if (id_attr) { parser.error_messages_.push_back(std::string("unable to parse invalid svg with id '") diff --git a/test/data b/test/data index 5b1fbae49..c7994e102 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit 5b1fbae495f27c75c922e1b1fb8ca02fc236a04f +Subproject commit c7994e1027943c19f88a2bb65a4c745b05f32dd5 diff --git a/test/unit/svg/svg_parser_test.cpp b/test/unit/svg/svg_parser_test.cpp index 5f5772de3..f73783488 100644 --- a/test/unit/svg/svg_parser_test.cpp +++ b/test/unit/svg/svg_parser_test.cpp @@ -112,7 +112,7 @@ TEST_CASE("SVG parser") { if (!p.parse_from_string(svg_str)) { auto const& errors = p.error_messages(); - REQUIRE(errors.size() == 13); + REQUIRE(errors.size() == 14); REQUIRE(errors[0] == "parse_rect: Invalid width"); REQUIRE(errors[1] == "Failed to parse double: \"FAIL\""); REQUIRE(errors[2] == "parse_rect: Invalid height"); @@ -120,12 +120,13 @@ TEST_CASE("SVG parser") { REQUIRE(errors[4] == "parse_rect: Invalid ry"); REQUIRE(errors[5] == "unable to parse invalid svg "); REQUIRE(errors[6] == "unable to parse invalid svg with id 'fail-path'"); - REQUIRE(errors[7] == "parse_circle: Invalid radius"); - REQUIRE(errors[8] == "Failed to parse 'points'"); - REQUIRE(errors[9] == "Failed to parse 'points'"); - REQUIRE(errors[10] == "parse_ellipse: Invalid rx"); - REQUIRE(errors[11] == "parse_ellipse: Invalid ry"); - REQUIRE(errors[12] == "parse_rect: Invalid height"); + REQUIRE(errors[7] == "unable to parse invalid svg with id 'fail-path'"); + REQUIRE(errors[8] == "parse_circle: Invalid radius"); + REQUIRE(errors[9] == "Failed to parse 'points'"); + REQUIRE(errors[10] == "Failed to parse 'points'"); + REQUIRE(errors[11] == "parse_ellipse: Invalid rx"); + REQUIRE(errors[12] == "parse_ellipse: Invalid ry"); + REQUIRE(errors[13] == "parse_rect: Invalid height"); } } From 930893a877afd78b06ab56adf802fa8d34ebdb63 Mon Sep 17 00:00:00 2001 From: artemp Date: Wed, 29 Jul 2015 16:49:23 +0200 Subject: [PATCH 148/165] benchmark - correct params --- benchmark/run | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmark/run b/benchmark/run index b17f82ad3..f91853995 100755 --- a/benchmark/run +++ b/benchmark/run @@ -55,5 +55,5 @@ run test_offset_converter 10 1000 --threads 1 ./benchmark/out/test_quad_tree \ - --iterations 10000 \ + --iterations 1000 \ --threads 10 From e5c4dbf1c05fa4e403a4443bed547c751d42841c Mon Sep 17 00:00:00 2001 From: artemp Date: Wed, 29 Jul 2015 18:12:21 +0200 Subject: [PATCH 149/165] format --- src/svg/svg_parser.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/svg/svg_parser.cpp b/src/svg/svg_parser.cpp index 293235268..72db15370 100644 --- a/src/svg/svg_parser.cpp +++ b/src/svg/svg_parser.cpp @@ -866,7 +866,6 @@ bool parse_common_gradient(svg_parser & parser, rapidxml::xml_node const* void parse_radial_gradient(svg_parser & parser, rapidxml::xml_node const* node) { parse_common_gradient(parser, node); - double cx = 0.5; double cy = 0.5; double fx = 0.0; @@ -886,7 +885,7 @@ void parse_radial_gradient(svg_parser & parser, rapidxml::xml_node const* cy = parse_double_optional_percent(parser.error_messages_, attr->value(), has_percent); } - attr = node->first_attribute( "fx"); + attr = node->first_attribute("fx"); if (attr != nullptr) { fx = parse_double_optional_percent(parser.error_messages_,attr->value(), has_percent); From f75921c7ca3f48725993047b3170ae73de845b0c Mon Sep 17 00:00:00 2001 From: artemp Date: Thu, 30 Jul 2015 10:57:51 +0200 Subject: [PATCH 150/165] update test + data --- test/unit/svg/svg_parser_test.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test/unit/svg/svg_parser_test.cpp b/test/unit/svg/svg_parser_test.cpp index f73783488..ec7566a1f 100644 --- a/test/unit/svg/svg_parser_test.cpp +++ b/test/unit/svg/svg_parser_test.cpp @@ -363,17 +363,11 @@ TEST_CASE("SVG parser") { unsigned cmd; std::vector> vec; std::size_t num_vertices = path.total_vertices(); - //std::cerr << "Num vertices = " << num_vertices << std::endl; - //std::cerr << "{"; for (std::size_t i = 0; i < num_vertices; ++i) { cmd = path.vertex(&x,&y); vec.emplace_back(x, y, cmd); - //if (vec.size() > 1) std::cerr << ","; - //std::cerr << std::setprecision(6) << "std::make_tuple(" << x << ", " << y << ", " << cmd << ")"; } - //std::cerr << "}" << std::endl; - std::vector> expected = {std::make_tuple(1, 1, 1), std::make_tuple(1199, 1, 2), std::make_tuple(1199, 399, 2), From 9d43f69cf6aeeb5f72f7b1c1e924544bdaa5711c Mon Sep 17 00:00:00 2001 From: artemp Date: Thu, 30 Jul 2015 11:02:08 +0200 Subject: [PATCH 151/165] const correctness --- src/svg/svg_parser.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/svg/svg_parser.cpp b/src/svg/svg_parser.cpp index 72db15370..f57451e44 100644 --- a/src/svg/svg_parser.cpp +++ b/src/svg/svg_parser.cpp @@ -1002,7 +1002,7 @@ bool svg_parser::parse(std::string const& filename) return false; } - for (rapidxml::xml_node *child = doc.first_node(); + for (rapidxml::xml_node const* child = doc.first_node(); child; child = child->next_sibling()) { traverse_tree(*this, child); @@ -1027,7 +1027,7 @@ bool svg_parser::parse_from_string(std::string const& svg) error_messages_.push_back(ss.str()); return false; } - for (rapidxml::xml_node *child = doc.first_node(); + for (rapidxml::xml_node const* child = doc.first_node(); child; child = child->next_sibling()) { traverse_tree(*this, child); From f8fd60dc6962d939c47a4729f341702a1bedb29d Mon Sep 17 00:00:00 2001 From: artemp Date: Thu, 30 Jul 2015 13:13:53 +0200 Subject: [PATCH 152/165] remove libxml includes and init calls --- test/cleanup.hpp | 8 ++++++-- test/unit/svg/svg_parser_test.cpp | 10 ---------- utils/svg2png/svg2png.cpp | 11 ----------- 3 files changed, 6 insertions(+), 23 deletions(-) diff --git a/test/cleanup.hpp b/test/cleanup.hpp index 264a6b559..b30ab215a 100644 --- a/test/cleanup.hpp +++ b/test/cleanup.hpp @@ -1,9 +1,11 @@ #ifndef TEST_MEMORY_CLEANUP #define TEST_MEMORY_CLEANUP +#if defined(HAS_XML2) #include #include #include +#endif #if defined(HAVE_CAIRO) #include @@ -21,6 +23,7 @@ inline void run_cleanup() // only call this once, on exit // to make sure valgrind output is clean // http://xmlsoft.org/xmlmem.html +#if defined(HAS_XML2) xmlCleanupCharEncodingHandlers(); xmlCleanupEncodingAliases(); xmlCleanupGlobals(); @@ -29,6 +32,7 @@ inline void run_cleanup() xmlCleanupInputCallbacks(); xmlCleanupOutputCallbacks(); xmlCleanupMemory(); +#endif #if defined(HAVE_CAIRO) // http://cairographics.org/manual/cairo-Error-handling.html#cairo-debug-reset-static-data @@ -45,9 +49,9 @@ inline void run_cleanup() #endif // https://trac.osgeo.org/proj/wiki/ProjAPI#EnvironmentFunctions pj_deallocate_grids(); -#endif +#endif } } -#endif \ No newline at end of file +#endif diff --git a/test/unit/svg/svg_parser_test.cpp b/test/unit/svg/svg_parser_test.cpp index ec7566a1f..d7e1b9ca4 100644 --- a/test/unit/svg/svg_parser_test.cpp +++ b/test/unit/svg/svg_parser_test.cpp @@ -27,18 +27,12 @@ #include #include #include - -//#include -//#include -//#include - #include #include #include #include #include -#include // for xmlInitParser(), xmlCleanupParser() #include #include #include @@ -61,7 +55,6 @@ struct vertex_equal TEST_CASE("SVG parser") { - xmlInitParser(); SECTION("SVG i/o") { std::string svg_name("FAIL"); @@ -688,7 +681,4 @@ TEST_CASE("SVG parser") { transform *= agg::trans_affine_translation(240,155); REQUIRE(attrs[1].fill_gradient.get_transform() == transform); } - - - xmlCleanupParser(); } diff --git a/utils/svg2png/svg2png.cpp b/utils/svg2png/svg2png.cpp index 87b8f0e8c..3e00663bb 100644 --- a/utils/svg2png/svg2png.cpp +++ b/utils/svg2png/svg2png.cpp @@ -48,8 +48,6 @@ #include "agg_pixfmt_rgba.h" #include "agg_scanline_u.h" -#include // for xmlInitParser(), xmlCleanupParser() - struct main_marker_visitor { main_marker_visitor(std::string const& svg_name, @@ -198,8 +196,6 @@ int main (int argc,char** argv) return 0; } - xmlInitParser(); - while (itr != svg_files.end()) { std::string svg_name (*itr++); @@ -216,19 +212,12 @@ int main (int argc,char** argv) catch (std::exception const& ex) { std::clog << "Exception caught:" << ex.what() << std::endl; - xmlCleanupParser(); return -1; } catch (...) { std::clog << "Exception of unknown type!" << std::endl; - xmlCleanupParser(); return -1; } - - // only call this once, on exit - // to make sure valgrind output is clean - // http://xmlsoft.org/xmlmem.html - xmlCleanupParser(); return status; } From ffcacf3509f491b402d0602ae99902d91f3bf5c1 Mon Sep 17 00:00:00 2001 From: artemp Date: Thu, 30 Jul 2015 13:14:37 +0200 Subject: [PATCH 153/165] make libxml2 optional dependency (XMLPARSER=libxml2) --- SConstruct | 25 +++++++++++++------------ src/build.py | 2 +- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/SConstruct b/SConstruct index 9f3c0f4a9..a09b63aa6 100644 --- a/SConstruct +++ b/SConstruct @@ -1266,18 +1266,19 @@ if not preconfigured: # libxml2 should be optional but is currently not # https://github.com/mapnik/mapnik/issues/913 - if env.get('XML2_LIBS') or env.get('XML2_INCLUDES'): - REQUIRED_LIBSHEADERS.insert(0,['libxml2','libxml/parser.h',True,'C']) - if env.get('XML2_INCLUDES'): - inc_path = env['XML2_INCLUDES'] - env.AppendUnique(CPPPATH = fix_path(inc_path)) - if env.get('XML2_LIBS'): - lib_path = env['XML2_LIBS'] - env.AppendUnique(LIBPATH = fix_path(lib_path)) - elif conf.parse_config('XML2_CONFIG',checks='--cflags'): - env['HAS_LIBXML2'] = True - else: - env['MISSING_DEPS'].append('libxml2') + if env.get('XMLPARSER') and env['XMLPARSER'] == 'libxml2': + if env.get('XML2_LIBS') or env.get('XML2_INCLUDES'): + OPTIONAL_LIBSHEADERS.insert(0,['libxml2','libxml/parser.h',True,'C']) + if env.get('XML2_INCLUDES'): + inc_path = env['XML2_INCLUDES'] + env.AppendUnique(CPPPATH = fix_path(inc_path)) + if env.get('XML2_LIBS'): + lib_path = env['XML2_LIBS'] + env.AppendUnique(LIBPATH = fix_path(lib_path)) + elif conf.parse_config('XML2_CONFIG',checks='--cflags'): + env['HAS_LIBXML2'] = True + else: + env['MISSING_DEPS'].append('libxml2') if not env['HOST']: if conf.CheckHasDlfcn(): diff --git a/src/build.py b/src/build.py index 520a34710..3d9e3c4c9 100644 --- a/src/build.py +++ b/src/build.py @@ -84,7 +84,7 @@ if '-DHAVE_WEBP' in env['CPPDEFINES']: lib_env['LIBS'].append('webp') enabled_imaging_libraries.append('webp_reader.cpp') -lib_env['LIBS'].append('xml2') +if '-DHAVE_LIBXML2' in env['CPPDEFINES'] : lib_env['LIBS'].append('xml2') if '-DBOOST_REGEX_HAS_ICU' in env['CPPDEFINES']: lib_env['LIBS'].append('icui18n') From 362a2ee0b1f1a365992814d1dd13b7d13a85f101 Mon Sep 17 00:00:00 2001 From: artemp Date: Thu, 30 Jul 2015 14:32:18 +0200 Subject: [PATCH 154/165] use std::ptrdiff_t and remove static_cast's - ref #3006 --- include/mapnik/image_filter.hpp | 64 ++++++++++++++++----------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/include/mapnik/image_filter.hpp b/include/mapnik/image_filter.hpp index 37c332786..8301adefd 100644 --- a/include/mapnik/image_filter.hpp +++ b/include/mapnik/image_filter.hpp @@ -254,10 +254,10 @@ void apply_convolution_3x3(Src const& src_view, Dst & dst_view, Filter const& fi typename Src::x_iterator dst_it = dst_view.row_begin(0); // top row - for (std::size_t x = 0 ; x < static_cast(src_view.width()); ++x) + for (std::ptrdiff_t x = 0 ; x < src_view.width(); ++x) { (*dst_it)[3] = src_loc[loc11][3]; // Dst.a = Src.a - for (std::size_t i = 0; i < 3; ++i) + for (std::ptrdiff_t i = 0; i < 3; ++i) { bits32f p[9]; @@ -275,7 +275,7 @@ void apply_convolution_3x3(Src const& src_view, Dst & dst_view, Filter const& fi p[6] = src_loc[loc02][i]; } - if ( x == static_cast(src_view.width())-1) + if ( x == (src_view.width())-1) { p[5] = p[4]; p[8] = p[7]; @@ -296,15 +296,15 @@ void apply_convolution_3x3(Src const& src_view, Dst & dst_view, Filter const& fi ++dst_it; } // carrige-return - src_loc += point2(-static_cast(src_view.width()),1); + src_loc += point2(-src_view.width(),1); // 1... height-1 rows - for (std::size_t y = 1; y(src_view.height())-1; ++y) + for (std::ptrdiff_t y = 1; y < src_view.height()-1; ++y) { - for (std::size_t x = 0; x < static_cast(src_view.width()); ++x) + for (std::ptrdiff_t x = 0; x < src_view.width(); ++x) { (*dst_it)[3] = src_loc[loc11][3]; // Dst.a = Src.a - for (std::size_t i = 0; i < 3; ++i) + for (std::ptrdiff_t i = 0; i < 3; ++i) { bits32f p[9]; @@ -325,7 +325,7 @@ void apply_convolution_3x3(Src const& src_view, Dst & dst_view, Filter const& fi p[6] = src_loc[loc02][i]; } - if ( x == static_cast(src_view.width()) - 1) + if ( x == (src_view.width()) - 1) { p[2] = p[1]; p[5] = p[4]; @@ -343,15 +343,15 @@ void apply_convolution_3x3(Src const& src_view, Dst & dst_view, Filter const& fi ++src_loc.x(); } // carrige-return - src_loc += point2(-static_cast(src_view.width()),1); + src_loc += point2(-src_view.width(),1); } // bottom row - //src_loc = src_view.xy_at(0,static_cast(src_view.height())-1); - for (std::size_t x = 0 ; x < static_cast(src_view.width()); ++x) + //src_loc = src_view.xy_at(0,src_view.height()-1); + for (std::ptrdiff_t x = 0 ; x < src_view.width(); ++x) { (*dst_it)[3] = src_loc[loc11][3]; // Dst.a = Src.a - for (std::size_t i = 0; i < 3; ++i) + for (std::ptrdiff_t i = 0; i < 3; ++i) { bits32f p[9]; @@ -369,7 +369,7 @@ void apply_convolution_3x3(Src const& src_view, Dst & dst_view, Filter const& fi p[3] = src_loc[loc01][i]; } - if ( x == static_cast(src_view.width())-1) + if ( x == (src_view.width())-1) { p[2] = p[1]; p[5] = p[4]; @@ -431,10 +431,10 @@ void apply_filter(Src & src, color_to_alpha const& op) double cr = static_cast(op.color.red())/255.0; double cg = static_cast(op.color.green())/255.0; double cb = static_cast(op.color.blue())/255.0; - for (std::size_t y=0; y(src_view.height()); ++y) + for (std::ptrdiff_t y = 0; y < src_view.height(); ++y) { rgba8_view_t::x_iterator src_it = src_view.row_begin(static_cast(y)); - for (std::size_t x=0; x(src_view.width()); ++x) + for (std::ptrdiff_t x = 0; x < src_view.width(); ++x) { uint8_t & r = get_color(src_it[x], red_t()); uint8_t & g = get_color(src_it[x], green_t()); @@ -485,17 +485,17 @@ template void apply_filter(Src & src, colorize_alpha const& op) { using namespace boost::gil; - std::size_t size = op.size(); + std::ptrdiff_t size = op.size(); if (op.size() == 1) { // no interpolation if only one stop mapnik::filter::color_stop const& stop = op[0]; mapnik::color const& c = stop.color; rgba8_view_t src_view = rgba8_view(src); - for (std::size_t y=0; y(src_view.height()); ++y) + for (std::ptrdiff_t y = 0; y < src_view.height(); ++y) { rgba8_view_t::x_iterator src_it = src_view.row_begin(static_cast(y)); - for (std::size_t x=0; x(src_view.width()); ++x) + for (std::ptrdiff_t x = 0; x < src_view.width(); ++x) { uint8_t & r = get_color(src_it[x], red_t()); uint8_t & g = get_color(src_it[x], green_t()); @@ -533,10 +533,10 @@ void apply_filter(Src & src, colorize_alpha const& op) if (grad_lut.build_lut()) { rgba8_view_t src_view = rgba8_view(src); - for (std::size_t y=0; y(src_view.height()); ++y) + for (std::ptrdiff_t y = 0; y < src_view.height(); ++y) { rgba8_view_t::x_iterator src_it = src_view.row_begin(static_cast(y)); - for (std::size_t x=0; x(src_view.width()); ++x) + for (std::ptrdiff_t x = 0; x < src_view.width(); ++x) { uint8_t & r = get_color(src_it[x], red_t()); uint8_t & g = get_color(src_it[x], green_t()); @@ -598,10 +598,10 @@ void apply_filter(Src & src, scale_hsla const& transform) if (tinting || set_alpha) { rgba8_view_t src_view = rgba8_view(src); - for (std::size_t y=0; y(src_view.height()); ++y) + for (std::ptrdiff_t y = 0; y < src_view.height(); ++y) { rgba8_view_t::x_iterator src_it = src_view.row_begin(static_cast(y)); - for (std::size_t x=0; x(src_view.width()); ++x) + for (std::ptrdiff_t x = 0; x < src_view.width(); ++x) { uint8_t & r = get_color(src_it[x], red_t()); uint8_t & g = get_color(src_it[x], green_t()); @@ -681,10 +681,10 @@ void apply_filter(Src & src, gray const& /*op*/) rgba8_view_t src_view = rgba8_view(src); - for (std::size_t y=0; y(src_view.height()); ++y) + for (std::ptrdiff_t y = 0; y < src_view.height(); ++y) { rgba8_view_t::x_iterator src_it = src_view.row_begin(static_cast(y)); - for (std::size_t x=0; x(src_view.width()); ++x) + for (std::ptrdiff_t x = 0; x < src_view.width(); ++x) { // formula taken from boost/gil/color_convert.hpp:rgb_to_luminance uint8_t & r = get_color(src_it[x], red_t()); @@ -699,7 +699,7 @@ void apply_filter(Src & src, gray const& /*op*/) template void x_gradient_impl(Src const& src_view, Dst const& dst_view) { - for (std::size_t y=0; y(src_view.height()); ++y) + for (std::ptrdiff_t y = 0; y < src_view.height(); ++y) { typename Src::x_iterator src_it = src_view.row_begin(static_cast(y)); typename Dst::x_iterator dst_it = dst_view.row_begin(static_cast(y)); @@ -708,13 +708,13 @@ void x_gradient_impl(Src const& src_view, Dst const& dst_view) dst_it[0][1] = 128 + (src_it[0][1] - src_it[1][1]) / 2; dst_it[0][2] = 128 + (src_it[0][2] - src_it[1][2]) / 2; - dst_it[dst_view.width()-1][0] = 128 + (src_it[static_cast(src_view.width())-2][0] - src_it[static_cast(src_view.width())-1][0]) / 2; - dst_it[dst_view.width()-1][1] = 128 + (src_it[static_cast(src_view.width())-2][1] - src_it[static_cast(src_view.width())-1][1]) / 2; - dst_it[dst_view.width()-1][2] = 128 + (src_it[static_cast(src_view.width())-2][2] - src_it[static_cast(src_view.width())-1][2]) / 2; + dst_it[dst_view.width()-1][0] = 128 + (src_it[(src_view.width())-2][0] - src_it[(src_view.width())-1][0]) / 2; + dst_it[dst_view.width()-1][1] = 128 + (src_it[(src_view.width())-2][1] - src_it[(src_view.width())-1][1]) / 2; + dst_it[dst_view.width()-1][2] = 128 + (src_it[(src_view.width())-2][2] - src_it[(src_view.width())-1][2]) / 2; - dst_it[0][3] = dst_it[static_cast(src_view.width())-1][3] = 255; + dst_it[0][3] = dst_it[(src_view.width())-1][3] = 255; - for (std::size_t x=1; x(src_view.width())-1; ++x) + for (std::ptrdiff_t x = 1; x < src_view.width()-1; ++x) { dst_it[x][0] = 128 + (src_it[x-1][0] - src_it[x+1][0]) / 2; dst_it[x][1] = 128 + (src_it[x-1][1] - src_it[x+1][1]) / 2; @@ -746,10 +746,10 @@ void apply_filter(Src & src, invert const& /*op*/) rgba8_view_t src_view = rgba8_view(src); - for (std::size_t y=0; y(src_view.height()); ++y) + for (std::ptrdiff_t y = 0; y < src_view.height(); ++y) { rgba8_view_t::x_iterator src_it = src_view.row_begin(static_cast(y)); - for (std::size_t x=0; x(src_view.width()); ++x) + for (std::ptrdiff_t x = 0; x < src_view.width(); ++x) { // we only work with premultiplied source, // thus all color values must be <= alpha From 56712c2435cc1c4c15be140f673d6c3edcab90e1 Mon Sep 17 00:00:00 2001 From: artemp Date: Thu, 30 Jul 2015 14:34:01 +0200 Subject: [PATCH 155/165] fix XMLPARSER=libxml2 logic --- src/build.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/build.py b/src/build.py index 3d9e3c4c9..bdc343803 100644 --- a/src/build.py +++ b/src/build.py @@ -84,7 +84,8 @@ if '-DHAVE_WEBP' in env['CPPDEFINES']: lib_env['LIBS'].append('webp') enabled_imaging_libraries.append('webp_reader.cpp') -if '-DHAVE_LIBXML2' in env['CPPDEFINES'] : lib_env['LIBS'].append('xml2') +if env['XMLPARSER'] == 'libxml2' and env['HAS_LIBXML2']: + lib_env['LIBS'].append('xml2') if '-DBOOST_REGEX_HAS_ICU' in env['CPPDEFINES']: lib_env['LIBS'].append('icui18n') From 9f4db6c8554dc5e2733e0bcf0bc43a370a740f7a Mon Sep 17 00:00:00 2001 From: artemp Date: Thu, 30 Jul 2015 18:30:22 +0200 Subject: [PATCH 156/165] use correct define -> HAVE_LIBXML2 --- test/cleanup.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/cleanup.hpp b/test/cleanup.hpp index b30ab215a..f175e72b4 100644 --- a/test/cleanup.hpp +++ b/test/cleanup.hpp @@ -1,7 +1,7 @@ #ifndef TEST_MEMORY_CLEANUP #define TEST_MEMORY_CLEANUP -#if defined(HAS_XML2) +#if defined(HAVE_LIBXML2) #include #include #include @@ -23,7 +23,7 @@ inline void run_cleanup() // only call this once, on exit // to make sure valgrind output is clean // http://xmlsoft.org/xmlmem.html -#if defined(HAS_XML2) +#if defined(HAVE_LIBXML2) xmlCleanupCharEncodingHandlers(); xmlCleanupEncodingAliases(); xmlCleanupGlobals(); From 8982938270e8a94bc055468435acc9bf955720a4 Mon Sep 17 00:00:00 2001 From: jakepruitt Date: Thu, 30 Jul 2015 12:48:23 -0400 Subject: [PATCH 157/165] Adding test for gradient xlink:href - refs #3005 --- test/data | 2 +- test/unit/svg/svg_parser_test.cpp | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/test/data b/test/data index c7994e102..28be586d7 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit c7994e1027943c19f88a2bb65a4c745b05f32dd5 +Subproject commit 28be586d71b24fe1bcdf70bc9ba37dc4f63860fc diff --git a/test/unit/svg/svg_parser_test.cpp b/test/unit/svg/svg_parser_test.cpp index d7e1b9ca4..55ba5a376 100644 --- a/test/unit/svg/svg_parser_test.cpp +++ b/test/unit/svg/svg_parser_test.cpp @@ -681,4 +681,22 @@ TEST_CASE("SVG parser") { transform *= agg::trans_affine_translation(240,155); REQUIRE(attrs[1].fill_gradient.get_transform() == transform); } + SECTION("SVG with xlink:href") + { + std::string svg_name("./test/data/svg/gradient-xhref.svg"); + std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); + REQUIRE(marker); + REQUIRE(marker->is()); + mapnik::marker_svg const& svg = mapnik::util::get(*marker); + auto bbox = svg.bounding_box(); + REQUIRE(bbox == mapnik::box2d(20,20,460,230)); + auto storage = svg.get_data(); + REQUIRE(storage); + + auto const& attrs = storage->attributes(); + REQUIRE(attrs.size() == 2 ); + REQUIRE(attrs[0].fill_gradient.get_gradient_type() == mapnik::LINEAR); + REQUIRE(attrs[1].fill_gradient.get_gradient_type() == mapnik::LINEAR); + REQUIRE(attrs[1].fill_gradient.has_stop()); + } } From 2af5a75dd7430faf24493b584033d190a2f4375b Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Thu, 30 Jul 2015 11:22:23 -0700 Subject: [PATCH 158/165] default to ptree xml parser (rapidxml inside boost) instead of libxml2 --- SConstruct | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SConstruct b/SConstruct index a09b63aa6..359801da3 100644 --- a/SConstruct +++ b/SConstruct @@ -395,7 +395,7 @@ opts.AddVariables( BoolVariable('FULL_LIB_PATH', 'Embed the full and absolute path to libmapnik when linking ("install_name" on OS X/rpath on Linux)', 'True'), BoolVariable('ENABLE_SONAME', 'Embed a soname in libmapnik on Linux', 'True'), EnumVariable('THREADING','Set threading support','multi', ['multi','single']), - EnumVariable('XMLPARSER','Set xml parser','libxml2', ['libxml2','ptree']), + EnumVariable('XMLPARSER','Set xml parser','ptree', ['libxml2','ptree']), BoolVariable('DEMO', 'Compile demo c++ application', 'True'), BoolVariable('PGSQL2SQLITE', 'Compile and install a utility to convert postgres tables to sqlite', 'False'), BoolVariable('SHAPEINDEX', 'Compile and install a utility to generate shapefile indexes in the custom format (.index) Mapnik supports', 'True'), From 261b2f51853406d01bde9a5999a3eb0ec7e3743e Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Thu, 30 Jul 2015 11:24:38 -0700 Subject: [PATCH 159/165] drop libxml2 as a dep in bootstrap for now (todo - selectively re-enable #3008) --- bootstrap.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/bootstrap.sh b/bootstrap.sh index d68daa802..5d38b16eb 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -60,7 +60,6 @@ function install_mason_deps() { install freetype 2.5.5 libfreetype & install harfbuzz 0.9.40 libharfbuzz & install jpeg_turbo 1.4.0 libjpeg & - install libxml2 2.9.2 libxml2 & install libpng 1.6.17 libpng & install webp 0.4.2 libwebp & install icu 54.1 & @@ -117,8 +116,6 @@ PG_INCLUDES = '${MASON_LINKED_REL}/include' PG_LIBS = '${MASON_LINKED_REL}/lib' FREETYPE_INCLUDES = '${MASON_LINKED_REL}/include/freetype2' FREETYPE_LIBS = '${MASON_LINKED_REL}/lib' -XML2_INCLUDES = '${MASON_LINKED_REL}/include/libxml2' -XML2_LIBS = '${MASON_LINKED_REL}/lib' SVG_RENDERER = True CAIRO_INCLUDES = '${MASON_LINKED_REL}/include' CAIRO_LIBS = '${MASON_LINKED_REL}/lib' From 9549a1cc0159fb8fdb3624f21523e039acc8587e Mon Sep 17 00:00:00 2001 From: jakepruitt Date: Thu, 30 Jul 2015 16:40:34 -0400 Subject: [PATCH 160/165] Adding tests for radial gradients with percentages --- test/data | 2 +- test/unit/svg/svg_parser_test.cpp | 45 +++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/test/data b/test/data index 28be586d7..3b714b97d 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit 28be586d71b24fe1bcdf70bc9ba37dc4f63860fc +Subproject commit 3b714b97dcde432b7f3507cab0fe254e6af647af diff --git a/test/unit/svg/svg_parser_test.cpp b/test/unit/svg/svg_parser_test.cpp index 55ba5a376..551682ce3 100644 --- a/test/unit/svg/svg_parser_test.cpp +++ b/test/unit/svg/svg_parser_test.cpp @@ -63,6 +63,25 @@ TEST_CASE("SVG parser") { REQUIRE(marker->is()); } + SECTION("SVG syntax") + { + std::string svg_name("./test/data/svg/invalid.svg"); + std::ifstream in(svg_name.c_str()); + std::string svg_str((std::istreambuf_iterator(in)), + std::istreambuf_iterator()); + + using namespace mapnik::svg; + mapnik::svg_storage_type path; + vertex_stl_adapter stl_storage(path.source()); + svg_path_adapter svg_path(stl_storage); + svg_converter_type svg(svg_path, path.attributes()); + svg_parser p(svg); + + if (!p.parse_from_string(svg_str)) + { + } + } + SECTION("SVG parser color ") { @@ -681,6 +700,7 @@ TEST_CASE("SVG parser") { transform *= agg::trans_affine_translation(240,155); REQUIRE(attrs[1].fill_gradient.get_transform() == transform); } + SECTION("SVG with xlink:href") { std::string svg_name("./test/data/svg/gradient-xhref.svg"); @@ -699,4 +719,29 @@ TEST_CASE("SVG parser") { REQUIRE(attrs[1].fill_gradient.get_gradient_type() == mapnik::LINEAR); REQUIRE(attrs[1].fill_gradient.has_stop()); } + + SECTION("SVG with radial percents") + { + std::string svg_name("./test/data/svg/gradient-radial-percents.svg"); + std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); + REQUIRE(marker); + REQUIRE(marker->is()); + mapnik::marker_svg const& svg = mapnik::util::get(*marker); + auto bbox = svg.bounding_box(); + REQUIRE(bbox == mapnik::box2d(0,0,200,200)); + auto storage = svg.get_data(); + REQUIRE(storage); + + double x1, x2, y1, y2, r; + auto const& attrs = storage->attributes(); + REQUIRE(attrs.size() == 1 ); + REQUIRE(attrs[0].fill_gradient.get_gradient_type() == mapnik::RADIAL); + REQUIRE(attrs[0].fill_gradient.has_stop()); + attrs[0].fill_gradient.get_control_points(x1, y1, x2, y2, r); + REQUIRE(x1 == 0); + REQUIRE(y1 == 25); + REQUIRE(x2 == 10); + REQUIRE(y2 == 10); + REQUIRE(r == 75); + } } From 6f6af87a4313eb604b154d31d859fb7944bd6f9b Mon Sep 17 00:00:00 2001 From: jakepruitt Date: Thu, 30 Jul 2015 17:05:19 -0400 Subject: [PATCH 161/165] Write invalid parse_string test --- test/data | 2 +- test/unit/svg/svg_parser_test.cpp | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/test/data b/test/data index 3b714b97d..6e4ff44ff 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit 3b714b97dcde432b7f3507cab0fe254e6af647af +Subproject commit 6e4ff44fffb28d907b109d13a350f889839940c9 diff --git a/test/unit/svg/svg_parser_test.cpp b/test/unit/svg/svg_parser_test.cpp index 551682ce3..8e50aa056 100644 --- a/test/unit/svg/svg_parser_test.cpp +++ b/test/unit/svg/svg_parser_test.cpp @@ -79,6 +79,9 @@ TEST_CASE("SVG parser") { if (!p.parse_from_string(svg_str)) { + auto const& errors = p.error_messages(); + REQUIRE(errors.size() == 1); + REQUIRE(errors[0] == "Unable to parse '\n\n'"); } } From 1f7b961901ab28b45844ba22ba89225e011537d5 Mon Sep 17 00:00:00 2001 From: jakepruitt Date: Thu, 30 Jul 2015 17:09:26 -0400 Subject: [PATCH 162/165] Update test data --- test/data | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/data b/test/data index 6e4ff44ff..98464c2d9 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit 6e4ff44fffb28d907b109d13a350f889839940c9 +Subproject commit 98464c2d9cbe29cf0b881652abbd9bb40deb71b5 From 0cc524aa1c263288cf62aeab652652f9286fd81a Mon Sep 17 00:00:00 2001 From: jakepruitt Date: Thu, 30 Jul 2015 17:35:30 -0400 Subject: [PATCH 163/165] Adding percentage for linear gradient --- test/data | 2 +- test/unit/svg/svg_parser_test.cpp | 40 ++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/test/data b/test/data index 98464c2d9..584ecdb67 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit 98464c2d9cbe29cf0b881652abbd9bb40deb71b5 +Subproject commit 584ecdb679eb2172186f63e90d3826574781d4c8 diff --git a/test/unit/svg/svg_parser_test.cpp b/test/unit/svg/svg_parser_test.cpp index 8e50aa056..ab71bc550 100644 --- a/test/unit/svg/svg_parser_test.cpp +++ b/test/unit/svg/svg_parser_test.cpp @@ -63,7 +63,26 @@ TEST_CASE("SVG parser") { REQUIRE(marker->is()); } - SECTION("SVG syntax") + SECTION("SVG::parse i/o") + { + std::string svg_name("FAIL"); + + using namespace mapnik::svg; + mapnik::svg_storage_type path; + vertex_stl_adapter stl_storage(path.source()); + svg_path_adapter svg_path(stl_storage); + svg_converter_type svg(svg_path, path.attributes()); + svg_parser p(svg); + + if (!p.parse(svg_name)) + { + auto const& errors = p.error_messages(); + REQUIRE(errors.size() == 1); + REQUIRE(errors[0] == "Unable to open 'FAIL'"); + } + } + + SECTION("SVG::parse_from_string syntax error") { std::string svg_name("./test/data/svg/invalid.svg"); std::ifstream in(svg_name.c_str()); @@ -85,6 +104,25 @@ TEST_CASE("SVG parser") { } } + SECTION("SVG::parse_from_string syntax error") + { + std::string svg_name("./test/data/svg/invalid.svg"); + + using namespace mapnik::svg; + mapnik::svg_storage_type path; + vertex_stl_adapter stl_storage(path.source()); + svg_path_adapter svg_path(stl_storage); + svg_converter_type svg(svg_path, path.attributes()); + svg_parser p(svg); + + if (!p.parse(svg_name)) + { + auto const& errors = p.error_messages(); + REQUIRE(errors.size() == 1); + REQUIRE(errors[0] == "svg_parser::parse - Unable to parse './test/data/svg/invalid.svg'"); + } + } + SECTION("SVG parser color ") { From 3a4d57492fe68d3afe4ce2b02344dd5c0a78e1ed Mon Sep 17 00:00:00 2001 From: artemp Date: Fri, 31 Jul 2015 10:44:42 +0200 Subject: [PATCH 164/165] make parser strict - return marker_null() on SVG parser failures redirect stderr to MAPNIK_LOG --- src/marker_cache.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/marker_cache.cpp b/src/marker_cache.cpp index e0f9b8f8e..330f4d094 100644 --- a/src/marker_cache.cpp +++ b/src/marker_cache.cpp @@ -180,9 +180,9 @@ std::shared_ptr marker_cache::find(std::string const& uri, { for (auto const& msg : p.error_messages()) { - std::cerr << "SVG PARSING ERROR:\"" << msg << "\"" << std::endl; + MAPNIK_LOG_ERROR(marker_cache) << "SVG PARSING ERROR:\"" << msg << "\""; } - //return std::make_shared(mapnik::marker_null()); + return std::make_shared(mapnik::marker_null()); } //svg.arrange_orientations(); double lox,loy,hix,hiy; @@ -221,9 +221,9 @@ std::shared_ptr marker_cache::find(std::string const& uri, { for (auto const& msg : p.error_messages()) { - std::cerr << "SVG PARSING ERROR:\"" << msg << "\"" << std::endl; + MAPNIK_LOG_ERROR(marker_cache) << "SVG PARSING ERROR:\"" << msg << "\""; } - //return std::make_shared(mapnik::marker_null()); + return std::make_shared(mapnik::marker_null()); } //svg.arrange_orientations(); double lox,loy,hix,hiy; From 5b04764757605ea03e18d5a3019269a47fd411a0 Mon Sep 17 00:00:00 2001 From: artemp Date: Fri, 31 Jul 2015 10:46:15 +0200 Subject: [PATCH 165/165] SVG - update tests to work with marker_cache's strict policy --- test/data | 2 +- test/unit/svg/svg_parser_test.cpp | 52 +++++++------------------------ 2 files changed, 12 insertions(+), 42 deletions(-) diff --git a/test/data b/test/data index 584ecdb67..d0a23b2a5 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit 584ecdb679eb2172186f63e90d3826574781d4c8 +Subproject commit d0a23b2a512d2ea83f08a9c1dc50e9b9b4a08dd5 diff --git a/test/unit/svg/svg_parser_test.cpp b/test/unit/svg/svg_parser_test.cpp index ab71bc550..a390bb4ad 100644 --- a/test/unit/svg/svg_parser_test.cpp +++ b/test/unit/svg/svg_parser_test.cpp @@ -602,48 +602,18 @@ TEST_CASE("SVG parser") { SECTION("SVG missing def") { - // std::string svg_name("./test/data/svg/gradient-nodef.svg"); - std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); - REQUIRE(marker); - REQUIRE(marker->is()); - mapnik::marker_svg const& svg = mapnik::util::get(*marker); - auto bbox = svg.bounding_box(); - REQUIRE(bbox == mapnik::box2d(1.0,1.0,799.0,599.0)); - auto storage = svg.get_data(); - REQUIRE(storage); - mapnik::svg::vertex_stl_adapter stl_storage(storage->source()); - mapnik::svg::svg_path_adapter path(stl_storage); - double x,y; - unsigned cmd; - std::vector> vec; - std::size_t num_vertices = path.total_vertices(); - - for (std::size_t i = 0; i < num_vertices; ++i) - { - cmd = path.vertex(&x,&y); - vec.emplace_back(x, y, cmd); - } - - std::vector> expected = {std::make_tuple(1, 1, 1), - std::make_tuple(799, 1, 2), - std::make_tuple(799, 599, 2), - std::make_tuple(1, 599, 2), - std::make_tuple(1, 1, 79), - std::make_tuple(0, 0, 0), - std::make_tuple(100, 100, 1), - std::make_tuple(700, 100, 2), - std::make_tuple(700, 300, 2), - std::make_tuple(100, 300, 2), - std::make_tuple(100, 100, 79), - std::make_tuple(0, 0, 0), - std::make_tuple(100, 320, 1), - std::make_tuple(700, 320, 2), - std::make_tuple(700, 520, 2), - std::make_tuple(100, 520, 2), - std::make_tuple(100, 320, 79)}; - - REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin())); + using namespace mapnik::svg; + mapnik::svg_storage_type path; + vertex_stl_adapter stl_storage(path.source()); + svg_path_adapter svg_path(stl_storage); + svg_converter_type svg(svg_path, path.attributes()); + svg_parser p(svg); + REQUIRE(!p.parse(svg_name)); + auto const& errors = p.error_messages(); + REQUIRE(errors.size() == 2); + REQUIRE(errors[0] == "Failed to find gradient fill: MyGradient"); + REQUIRE(errors[1] == "Failed to find gradient stroke: MyGradient"); } SECTION("SVG missing id")