From 75f7cb26bba1439cf256b0b8a466d49aeb43b66c Mon Sep 17 00:00:00 2001 From: Blake Thompson Date: Tue, 9 Jun 2015 09:18:52 -0500 Subject: [PATCH 01/52] 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 02/52] 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 03/52] 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 04/52] 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 05/52] 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 06/52] 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 07/52] 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 08/52] 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 09/52] 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 10/52] 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 11/52] 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 12/52] 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 13/52] 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 14/52] 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 15/52] 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 16/52] 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 17/52] 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 18/52] 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 19/52] 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 20/52] 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 21/52] 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 22/52] 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 23/52] 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 24/52] 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 25/52] 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 26/52] 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 27/52] 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 28/52] 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 29/52] 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 30/52] 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 31/52] 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 32/52] 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 33/52] 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 34/52] 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 35/52] 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 36/52] 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 37/52] 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 38/52] 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 39/52] 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 40/52] 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 41/52] 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 42/52] 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 43/52] 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 44/52] 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 45/52] 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 46/52] 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 47/52] 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 48/52] 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 ced3d35023b148f57388be9f9b71e0a1efe18676 Mon Sep 17 00:00:00 2001 From: Jiri Drbalek Date: Wed, 8 Jul 2015 20:01:23 +0000 Subject: [PATCH 49/52] 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 50/52] 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 51/52] 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 52/52] 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