Merge branch 'master' into rotated_rectangles
This commit is contained in:
commit
ebbaf49896
52 changed files with 1330 additions and 850 deletions
107
CHANGELOG.md
107
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
|
||||
Released: July 7th, 2015
|
||||
|
||||
(Packaged from e6891a0)
|
||||
|
||||
#### Summary
|
||||
|
||||
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 <https://github.com/mapnik/python-mapnik> 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)
|
||||
|
||||
- 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
|
||||
|
||||
Released ...
|
||||
|
||||
(Packaged from ...)
|
||||
|
||||
Summary: TODO
|
||||
|
||||
- PostGIS: Added support for rendering 3D and 4D geometries (previously silently skipped) (#44)
|
||||
|
||||
- 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: TODO
|
|||
|
||||
- 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: TODO
|
|||
|
||||
- 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
|
||||
|
|
16
INSTALL.md
16
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:
|
||||
|
@ -178,4 +184,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.
|
||||
|
|
|
@ -29,19 +29,7 @@ namespace benchmark {
|
|||
image_rgba8 const& dest = util::get<image_rgba8>(desc_any);
|
||||
image_rgba8 const& src = util::get<image_rgba8>(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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 &
|
||||
|
|
4
deps/agg/include/agg_color_gray.h
vendored
4
deps/agg/include/agg_color_gray.h
vendored
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,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()
|
||||
|
@ -288,31 +299,51 @@ 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<vertex2d> points;
|
||||
std::vector<vertex2d> close_points;
|
||||
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)
|
||||
{
|
||||
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));
|
||||
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;
|
||||
}
|
||||
v1.x = v0.x;
|
||||
v1.y = v0.y;
|
||||
v1.cmd = v0.cmd;
|
||||
else
|
||||
{
|
||||
close_points.push_back(v1);
|
||||
}
|
||||
}
|
||||
else if (v0.cmd == SEG_MOVETO)
|
||||
{
|
||||
start = v0;
|
||||
}
|
||||
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++];
|
||||
|
@ -326,17 +357,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<double>::epsilon() ? 0 : x;
|
||||
y = std::abs(y) < std::numeric_limits<double>::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 +388,28 @@ private:
|
|||
}
|
||||
else
|
||||
{
|
||||
joint_angle = explement_reflex_angle(angle_b - angle_a);
|
||||
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;
|
||||
}
|
||||
|
||||
double half_turns = half_turn_segments_ * std::fabs(joint_angle);
|
||||
int bulge_steps = 0;
|
||||
|
||||
if (offset_ < 0.0)
|
||||
{
|
||||
if (joint_angle > 0.0)
|
||||
{
|
||||
joint_angle = joint_angle - 2 * M_PI;
|
||||
}
|
||||
else
|
||||
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.
|
||||
double half_turns = half_turn_segments_ * std::fabs(curve_angle);
|
||||
bulge_steps = 1 + static_cast<int>(std::floor(half_turns / M_PI));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (joint_angle < 0.0)
|
||||
{
|
||||
joint_angle = joint_angle + 2 * M_PI;
|
||||
}
|
||||
else
|
||||
{
|
||||
bulge_steps = 1 + static_cast<int>(std::floor(half_turns / M_PI));
|
||||
}
|
||||
}
|
||||
|
||||
if (bulge_steps == 0)
|
||||
{
|
||||
displace2(v1, angle_a, angle_b);
|
||||
|
@ -415,18 +452,15 @@ 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<double>::epsilon() ? 0.0 : x;
|
||||
y = std::abs(y) < std::numeric_limits<double>::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++;
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
@ -445,35 +479,40 @@ 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);
|
||||
// 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)
|
||||
{
|
||||
joint_angle = 2 * M_PI - joint_angle;
|
||||
}
|
||||
|
||||
int bulge_steps = 0;
|
||||
|
||||
if (offset_ < 0.0)
|
||||
{
|
||||
if (joint_angle > 0.0)
|
||||
{
|
||||
joint_angle = joint_angle - 2 * M_PI;
|
||||
}
|
||||
else
|
||||
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.
|
||||
double half_turns = half_turn_segments_ * std::fabs(curve_angle);
|
||||
bulge_steps = 1 + static_cast<int>(std::floor(half_turns / M_PI));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (joint_angle < 0.0)
|
||||
{
|
||||
joint_angle = joint_angle + 2 * M_PI;
|
||||
}
|
||||
else
|
||||
{
|
||||
bulge_steps = 1 + static_cast<int>(std::floor(half_turns / M_PI));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MAPNIK_LOG
|
||||
if (bulge_steps == 0)
|
||||
|
@ -516,10 +555,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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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<clip_poly_tag>();
|
||||
else if (type == geometry::geometry_types::LineString)
|
||||
else if (type == geometry::geometry_types::LineString || type == geometry::geometry_types::MultiLineString)
|
||||
converter.template set<clip_line_tag>();
|
||||
}
|
||||
|
||||
|
@ -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<clip_poly_tag>();
|
||||
else if (type == geometry::geometry_types::LineString)
|
||||
else if (type == geometry::geometry_types::LineString || type == geometry::geometry_types::MultiLineString)
|
||||
converter.template set<clip_line_tag>();
|
||||
}
|
||||
converter.template set<transform_tag>(); //always transform
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<double> const & bbox() const;
|
||||
private:
|
||||
std::vector<glyph_position> data_;
|
||||
pixel_position base_point_;
|
||||
|
@ -90,7 +89,7 @@ private:
|
|||
pixel_position marker_pos_;
|
||||
box2d<double> bbox_;
|
||||
};
|
||||
using glyph_positions_ptr = std::shared_ptr<glyph_positions>;
|
||||
using glyph_positions_ptr = std::unique_ptr<glyph_positions>;
|
||||
|
||||
using placements_list = std::list<glyph_positions_ptr>;
|
||||
}
|
||||
|
|
|
@ -28,7 +28,9 @@
|
|||
#include <mapnik/text/text_line.hpp>
|
||||
#include <mapnik/text/face.hpp>
|
||||
#include <mapnik/text/font_feature_settings.hpp>
|
||||
#include <mapnik/text/itemizer.hpp>
|
||||
#include <mapnik/safe_cast.hpp>
|
||||
#include <mapnik/font_engine_freetype.hpp>
|
||||
|
||||
// stl
|
||||
#include <list>
|
||||
|
@ -38,6 +40,9 @@
|
|||
#include <harfbuzz/hb.h>
|
||||
#include <harfbuzz/hb-ft.h>
|
||||
|
||||
// icu
|
||||
#include <unicode/uscript.h>
|
||||
|
||||
namespace mapnik
|
||||
{
|
||||
|
||||
|
|
|
@ -27,6 +27,10 @@
|
|||
#include <mapnik/text/text_properties.hpp>
|
||||
#include <mapnik/text/text_line.hpp>
|
||||
#include <mapnik/text/face.hpp>
|
||||
#include <mapnik/text/font_feature_settings.hpp>
|
||||
#include <mapnik/text/itemizer.hpp>
|
||||
#include <mapnik/safe_cast.hpp>
|
||||
#include <mapnik/font_engine_freetype.hpp>
|
||||
#include <mapnik/debug.hpp>
|
||||
|
||||
// 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<std::size_t>(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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <mapnik/text/evaluated_format_properties_ptr.hpp>
|
||||
#include <mapnik/value_types.hpp>
|
||||
#include <mapnik/util/noncopyable.hpp>
|
||||
#include <mapnik/config.hpp>
|
||||
|
||||
// stl
|
||||
#include <string>
|
||||
|
@ -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();
|
||||
|
|
|
@ -75,7 +75,7 @@ private:
|
|||
// Checks for collision.
|
||||
bool collision(box2d<double> 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;
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
#ifndef __SCRPTRUN_H
|
||||
#define __SCRPTRUN_H
|
||||
|
||||
#include "unicode/utypes.h"
|
||||
#include "unicode/uobject.h"
|
||||
#include "unicode/uscript.h"
|
||||
#include <unicode/utypes.h>
|
||||
#include <unicode/uobject.h>
|
||||
#include <unicode/uscript.h>
|
||||
|
||||
struct ScriptRecord
|
||||
{
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
//stl
|
||||
#include <vector>
|
||||
#include <mapnik/util/noncopyable.hpp>
|
||||
#include <mapnik/config.hpp>
|
||||
|
||||
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<glyph_info>;
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
|
||||
// stl
|
||||
#include <functional>
|
||||
#include <cassert>
|
||||
|
||||
// icu
|
||||
#include <unicode/unistr.h>
|
||||
|
@ -52,7 +51,6 @@ struct value_hasher
|
|||
|
||||
std::size_t operator() (value_unicode_string const& val) const
|
||||
{
|
||||
assert(val.hashCode() > 0);
|
||||
return static_cast<std::size_t>(val.hashCode());
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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::context_type>();
|
||||
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))
|
||||
|
|
|
@ -120,7 +120,7 @@ struct thunk_renderer<image_rgba8>
|
|||
render_offset_placements(
|
||||
thunk.placements_,
|
||||
offset_,
|
||||
[&] (glyph_positions_ptr glyphs)
|
||||
[&] (glyph_positions_ptr const& glyphs)
|
||||
{
|
||||
marker_info_ptr mark = glyphs->get_marker();
|
||||
if (mark)
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <mapnik/vertex_processor.hpp>
|
||||
#include <mapnik/renderer_common/clipping_extent.hpp>
|
||||
#include <mapnik/renderer_common/apply_vertex_converter.hpp>
|
||||
#include <mapnik/geometry_type.hpp>
|
||||
// agg
|
||||
#include "agg_basics.h"
|
||||
#include "agg_rendering_buffer.h"
|
||||
|
@ -164,12 +165,19 @@ void agg_renderer<T0,T1>::process(line_symbolizer const& sym,
|
|||
rasterizer_type ras(ren);
|
||||
set_join_caps_aa(sym, ras, feature, common_.vars_);
|
||||
|
||||
using vertex_converter_type = vertex_converter<clip_line_tag, transform_tag,
|
||||
using vertex_converter_type = vertex_converter<clip_line_tag, clip_poly_tag, transform_tag,
|
||||
affine_transform_tag,
|
||||
simplify_tag, smooth_tag,
|
||||
offset_transform_tag>;
|
||||
vertex_converter_type converter(clip_box,sym,common_.t_,prj_trans,tr,feature,common_.vars_,common_.scale_factor_);
|
||||
if (clip) converter.set<clip_line_tag>(); // 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<clip_poly_tag>();
|
||||
else if (type == geometry::geometry_types::LineString || type == geometry::geometry_types::MultiLineString)
|
||||
converter.template set<clip_line_tag>();
|
||||
}
|
||||
converter.set<transform_tag>(); // always transform
|
||||
if (std::fabs(offset) > 0.0) converter.set<offset_transform_tag>(); // parallel offset
|
||||
converter.set<affine_transform_tag>(); // optional affine transform
|
||||
|
@ -183,14 +191,20 @@ void agg_renderer<T0,T1>::process(line_symbolizer const& sym,
|
|||
}
|
||||
else
|
||||
{
|
||||
using vertex_converter_type = vertex_converter<clip_line_tag, transform_tag,
|
||||
using vertex_converter_type = vertex_converter<clip_line_tag, clip_poly_tag, transform_tag,
|
||||
affine_transform_tag,
|
||||
simplify_tag, smooth_tag,
|
||||
offset_transform_tag,
|
||||
dash_tag, stroke_tag>;
|
||||
vertex_converter_type converter(clip_box, sym,common_.t_,prj_trans,tr,feature,common_.vars_,common_.scale_factor_);
|
||||
|
||||
if (clip) converter.set<clip_line_tag>(); // 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<clip_poly_tag>();
|
||||
else if (type == geometry::geometry_types::LineString || type == geometry::geometry_types::MultiLineString)
|
||||
converter.template set<clip_line_tag>();
|
||||
}
|
||||
converter.set<transform_tag>(); // always transform
|
||||
if (std::fabs(offset) > 0.0) converter.set<offset_transform_tag>(); // parallel offset
|
||||
converter.set<affine_transform_tag>(); // optional affine transform
|
||||
|
|
|
@ -60,7 +60,7 @@ void agg_renderer<T0,T1>::process(shield_symbolizer const& sym,
|
|||
double opacity = get<double>(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)
|
||||
|
|
|
@ -67,7 +67,7 @@ void agg_renderer<T0,T1>::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);
|
||||
}
|
||||
|
|
|
@ -28,8 +28,8 @@
|
|||
#include <mapnik/text/face.hpp>
|
||||
|
||||
#include <cairo-ft.h>
|
||||
#include <vector>
|
||||
|
||||
#include <valarray>
|
||||
namespace mapnik {
|
||||
|
||||
cairo_face::cairo_face(std::shared_ptr<font_library> 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<double> 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<double> 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<int>(d.size()), 0/*offset*/);
|
||||
check_object_status_and_throw_exception(*this);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <mapnik/vertex_converters.hpp>
|
||||
#include <mapnik/vertex_processor.hpp>
|
||||
#include <mapnik/renderer_common/apply_vertex_converter.hpp>
|
||||
#include <mapnik/geometry_type.hpp>
|
||||
|
||||
namespace mapnik
|
||||
{
|
||||
|
@ -82,6 +83,7 @@ void cairo_renderer<T>::process(line_symbolizer const& sym,
|
|||
clipping_extent.pad(padding);
|
||||
}
|
||||
using vertex_converter_type = vertex_converter<clip_line_tag,
|
||||
clip_poly_tag,
|
||||
transform_tag,
|
||||
affine_transform_tag,
|
||||
simplify_tag, smooth_tag,
|
||||
|
@ -89,7 +91,14 @@ void cairo_renderer<T>::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<clip_line_tag>(); // 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<clip_poly_tag>();
|
||||
else if (type == geometry::geometry_types::LineString || type == geometry::geometry_types::MultiLineString)
|
||||
converter.template set<clip_line_tag>();
|
||||
}
|
||||
converter.set<transform_tag>(); // always transform
|
||||
if (std::fabs(offset) > 0.0) converter.set<offset_transform_tag>(); // parallel offset
|
||||
converter.set<affine_transform_tag>(); // optional affine transform
|
||||
|
|
|
@ -55,7 +55,7 @@ void cairo_renderer<T>::process(shield_symbolizer const& sym,
|
|||
double opacity = get<double>(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<T>::process(text_symbolizer const& sym,
|
|||
composite_mode_e halo_comp_op = get<composite_mode_e>(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_);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <mapnik/vertex_converters.hpp>
|
||||
#include <mapnik/vertex_processor.hpp>
|
||||
#include <mapnik/renderer_common/apply_vertex_converter.hpp>
|
||||
#include <mapnik/geometry_type.hpp>
|
||||
// agg
|
||||
#include "agg_rasterizer_scanline_aa.h"
|
||||
#include "agg_renderer_scanline.h"
|
||||
|
@ -89,14 +90,21 @@ void grid_renderer<T>::process(line_symbolizer const& sym,
|
|||
padding *= common_.scale_factor_;
|
||||
clipping_extent.pad(padding);
|
||||
}
|
||||
using vertex_converter_type = vertex_converter<clip_line_tag, transform_tag,
|
||||
using vertex_converter_type = vertex_converter<clip_line_tag, clip_poly_tag, transform_tag,
|
||||
affine_transform_tag,
|
||||
simplify_tag, smooth_tag,
|
||||
offset_transform_tag,
|
||||
dash_tag, stroke_tag>;
|
||||
|
||||
vertex_converter_type converter(clipping_extent,sym,common_.t_,prj_trans,tr,feature,common_.vars_,common_.scale_factor_);
|
||||
if (clip) converter.set<clip_line_tag>(); // 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<clip_poly_tag>();
|
||||
else if (type == geometry::geometry_types::LineString || type == geometry::geometry_types::MultiLineString)
|
||||
converter.template set<clip_line_tag>();
|
||||
}
|
||||
converter.set<transform_tag>(); // always transform
|
||||
if (std::fabs(offset) > 0.0) converter.set<offset_transform_tag>(); // parallel offset
|
||||
converter.set<affine_transform_tag>(); // optional affine transform
|
||||
|
|
|
@ -64,7 +64,7 @@ void grid_renderer<T>::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)
|
||||
|
|
|
@ -66,7 +66,7 @@ void grid_renderer<T>::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;
|
||||
|
|
|
@ -368,6 +368,13 @@ template MAPNIK_DECL void save_to_file<image_view_any> (image_view_any const&,
|
|||
std::string const&,
|
||||
rgba_palette const& palette);
|
||||
|
||||
template MAPNIK_DECL std::string save_to_string<image_view_rgba8> (image_view_rgba8 const&,
|
||||
std::string const&);
|
||||
|
||||
template MAPNIK_DECL std::string save_to_string<image_view_rgba8> (image_view_rgba8 const&,
|
||||
std::string const&,
|
||||
rgba_palette const& palette);
|
||||
|
||||
template MAPNIK_DECL std::string save_to_string<image_view_any> (image_view_any const&,
|
||||
std::string const&);
|
||||
|
||||
|
|
|
@ -183,11 +183,27 @@ 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;
|
||||
|
||||
box2d<double> label_box;
|
||||
bool first = true;
|
||||
for (auto const& label : labels)
|
||||
{
|
||||
detector_.insert(std::get<0>(label), layouts_.text(), std::get<1>(label));
|
||||
auto const& box = std::get<0>(label);
|
||||
if (first)
|
||||
{
|
||||
label_box = box;
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
label_box.expand_to_include(box);
|
||||
}
|
||||
detector_.insert(box, layouts_.text(), std::get<1>(label));
|
||||
}
|
||||
// do not render text off the canvas
|
||||
if (extent_.intersects(label_box))
|
||||
{
|
||||
placements_.push_back(std::move(glyphs));
|
||||
}
|
||||
placements_.push_back(glyphs);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -201,7 +217,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>();
|
||||
glyph_positions_ptr glyphs = std::make_unique<glyph_positions>();
|
||||
std::vector<box2d<double> > bboxes;
|
||||
glyphs->reserve(layouts_.glyphs_count());
|
||||
bboxes.reserve(layouts_.glyphs_count());
|
||||
|
@ -214,7 +230,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;
|
||||
|
@ -233,7 +250,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();
|
||||
|
@ -301,7 +318,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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -326,11 +343,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<double> label_box;
|
||||
bool first = true;
|
||||
for (box2d<double> 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(std::move(glyphs));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -369,9 +401,7 @@ bool placement_finder::collision(box2d<double> const& box, value_unicode_string
|
|||
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)))
|
||||
|
@ -393,7 +423,7 @@ void placement_finder::set_marker(marker_info_ptr m, box2d<double> 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<double> bbox = marker_box_;
|
||||
|
|
|
@ -94,11 +94,10 @@ void composite_bitmap(T & pixmap, FT_Bitmap *bitmap, unsigned rgba, int x, int y
|
|||
{
|
||||
int x_max = x + bitmap->width;
|
||||
int y_max = y + bitmap->rows;
|
||||
int i,p,j,q;
|
||||
|
||||
for (i=x,p=0;i<x_max;++i,++p)
|
||||
for (int i = x, p = 0; i < x_max; ++i, ++p)
|
||||
{
|
||||
for (j=y,q=0;j<y_max;++j,++q)
|
||||
for (int j = y, q = 0; j < y_max; ++j, ++q)
|
||||
{
|
||||
unsigned gray=bitmap->buffer[q*bitmap->width+p];
|
||||
if (gray)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit a6838f99a4c4be37989cea6718442cf4b53bd616
|
||||
Subproject commit 2823c6a0ba9e642869537d8e5c1a1ca5fd3de18a
|
|
@ -1 +1 @@
|
|||
Subproject commit 7080866ca1b3523fead1a25c017bd87082806eb4
|
||||
Subproject commit 7de3183129a08c835a3205f5d51c85e59494ee33
|
|
@ -10,7 +10,7 @@
|
|||
#include <mapnik/expression.hpp>
|
||||
#include <mapnik/expression_evaluator.hpp>
|
||||
#include <mapnik/debug.hpp>
|
||||
|
||||
#include <mapnik/util/fs.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/range/iterator_range_core.hpp>
|
||||
#include <boost/format.hpp>
|
||||
|
@ -157,9 +157,15 @@ void require_geometry(mapnik::feature_ptr feature,
|
|||
}
|
||||
} // anonymous namespace
|
||||
|
||||
const bool registered = mapnik::datasource_cache::instance().register_datasources("./plugins/input/");
|
||||
static const std::string csv_plugin("./plugins/input/csv.input");
|
||||
|
||||
const bool registered = mapnik::datasource_cache::instance().register_datasources(csv_plugin);
|
||||
|
||||
TEST_CASE("csv") {
|
||||
|
||||
if (mapnik::util::exists(csv_plugin))
|
||||
{
|
||||
|
||||
REQUIRE(registered);
|
||||
|
||||
// make the tests silent since we intentially test error conditions that are noisy
|
||||
|
@ -672,4 +678,5 @@ TEST_CASE("csv") {
|
|||
} // END SECTION
|
||||
|
||||
mapnik::logger::instance().set_severity(severity);
|
||||
}
|
||||
} // END TEST CASE
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <mapnik/color.hpp>
|
||||
#include <mapnik/datasource.hpp>
|
||||
#include <mapnik/datasource_cache.hpp>
|
||||
#include <mapnik/util/fs.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
@ -19,6 +20,9 @@ SECTION("layers") {
|
|||
{
|
||||
mapnik::Map m0(100,100);
|
||||
mapnik::Map m2(200,100);
|
||||
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";
|
||||
|
@ -69,6 +73,7 @@ SECTION("layers") {
|
|||
|
||||
REQUIRE( (m0 == m2) );
|
||||
}
|
||||
}
|
||||
catch (std::exception const & ex)
|
||||
{
|
||||
std::clog << ex.what() << "\n";
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <mapnik/datasource_cache.hpp>
|
||||
#include <mapnik/agg_renderer.hpp>
|
||||
#include <mapnik/expression.hpp>
|
||||
#include <mapnik/util/fs.hpp>
|
||||
|
||||
TEST_CASE("image") {
|
||||
|
||||
|
@ -17,7 +18,10 @@ SECTION("painting") {
|
|||
|
||||
try
|
||||
{
|
||||
datasource_cache::instance().register_datasources("plugins/input/csv.input");
|
||||
std::string csv_plugin("./plugins/input/csv.input");
|
||||
if (mapnik::util::exists(csv_plugin))
|
||||
{
|
||||
datasource_cache::instance().register_datasources(csv_plugin);
|
||||
|
||||
Map m(256, 256);
|
||||
|
||||
|
@ -59,6 +63,7 @@ SECTION("painting") {
|
|||
|
||||
REQUIRE(image.painted() == true);
|
||||
}
|
||||
}
|
||||
catch (std::exception const & ex)
|
||||
{
|
||||
std::clog << ex.what() << std::endl;
|
||||
|
|
24
test/unit/text/shaping.cpp
Normal file
24
test/unit/text/shaping.cpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#include "catch.hpp"
|
||||
#include <mapnik/text/icu_shaper.hpp>
|
||||
#include <mapnik/text/harfbuzz_shaper.hpp>
|
||||
#include <mapnik/text/font_library.hpp>
|
||||
|
||||
TEST_CASE("shapers compile") {
|
||||
|
||||
mapnik::text_line line(0,0);
|
||||
mapnik::text_itemizer itemizer;
|
||||
std::map<unsigned,double> 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);
|
||||
}
|
|
@ -27,14 +27,22 @@ struct fake_path
|
|||
: fake_path(l.begin(), l.size()) {
|
||||
}
|
||||
|
||||
fake_path(std::vector<double> const &v)
|
||||
: fake_path(v.begin(), v.size()) {
|
||||
fake_path(std::vector<double> const &v, bool make_invalid = false)
|
||||
: fake_path(v.begin(), v.size(), make_invalid) {
|
||||
}
|
||||
|
||||
template <typename Itr>
|
||||
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,11 +78,37 @@ 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<fake_path> 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<double> v_path = {1, 1, 1, 2};
|
||||
fake_path path(v_path, true);
|
||||
mapnik::offset_converter<fake_path> 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};
|
||||
mapnik::offset_converter<fake_path> 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 +150,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<fake_path> 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 +200,7 @@ void test_offset_curve(double const &offset) {
|
|||
|
||||
fake_path path(pos), off_path(off_pos);
|
||||
mapnik::offset_converter<fake_path> 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 +256,7 @@ void test_s_shaped_curve(double const &offset) {
|
|||
|
||||
fake_path path(pos), off_path(off_pos);
|
||||
mapnik::offset_converter<fake_path> 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);
|
||||
|
@ -263,6 +297,41 @@ void test_s_shaped_curve(double const &offset) {
|
|||
|
||||
TEST_CASE("offset converter") {
|
||||
|
||||
SECTION("null segment") {
|
||||
try {
|
||||
|
||||
std::vector<double> 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<double> 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 {
|
||||
|
||||
|
|
|
@ -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 <memory>
|
||||
|
||||
// mapnik
|
||||
#include <mapnik/image_util.hpp>
|
||||
#include <mapnik/image_reader.hpp>
|
||||
|
||||
namespace visual_tests
|
||||
{
|
||||
|
||||
template <typename Image>
|
||||
unsigned compare_images(Image const & actual, std::string const & reference)
|
||||
{
|
||||
std::unique_ptr<mapnik::image_reader> 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<Image>(ref_image_any);
|
||||
|
||||
return mapnik::compare(actual, reference_image, 0, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -26,6 +26,7 @@
|
|||
// stl
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
|
||||
// boost
|
||||
#include <boost/filesystem.hpp>
|
||||
|
@ -35,21 +36,23 @@ 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
|
||||
{
|
||||
config() : status(true),
|
||||
scales({ 1.0, 2.0 }),
|
||||
sizes({ { 500, 100 } }) { }
|
||||
sizes({ { 500, 100 } }),
|
||||
tiles({ { 1, 1 } }) { }
|
||||
|
||||
bool status;
|
||||
std::vector<double> scales;
|
||||
std::vector<map_size> sizes;
|
||||
std::vector<map_size> tiles;
|
||||
};
|
||||
|
||||
enum result_state : std::uint8_t
|
||||
|
@ -66,11 +69,13 @@ 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;
|
||||
std::string error_message;
|
||||
unsigned diff;
|
||||
std::chrono::high_resolution_clock::duration duration;
|
||||
};
|
||||
|
||||
using result_list = std::vector<result>;
|
||||
|
|
|
@ -23,15 +23,16 @@
|
|||
#ifndef RENDERER_HPP
|
||||
#define RENDERER_HPP
|
||||
|
||||
#include "compare_images.hpp"
|
||||
|
||||
// stl
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
|
||||
// mapnik
|
||||
#include <mapnik/map.hpp>
|
||||
#include <mapnik/image_util.hpp>
|
||||
#include <mapnik/image_reader.hpp>
|
||||
#include <mapnik/agg_renderer.hpp>
|
||||
#if defined(GRID_RENDERER)
|
||||
#include <mapnik/grid/grid_renderer.hpp>
|
||||
|
@ -56,10 +57,20 @@ 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
|
||||
{
|
||||
return compare_images(actual, reference.string());
|
||||
std::unique_ptr<mapnik::image_reader> 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<ImageType>(ref_image_any);
|
||||
|
||||
return mapnik::compare(actual, reference_image, 0, true);
|
||||
}
|
||||
|
||||
void save(image_type const & image, boost::filesystem::path const& path) const
|
||||
|
@ -106,6 +117,7 @@ struct svg_renderer : renderer_base<std::string>
|
|||
{
|
||||
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
|
||||
{
|
||||
|
@ -125,7 +137,7 @@ struct svg_renderer : renderer_base<std::string>
|
|||
}
|
||||
std::string expected(std::istreambuf_iterator<char>(stream.rdbuf()),(std::istreambuf_iterator<char>()));
|
||||
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
|
||||
|
@ -191,19 +203,76 @@ struct grid_renderer : renderer_base<mapnik::image_rgba8>
|
|||
};
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
void set_rectangle(T const & src, T & dst, std::size_t x, std::size_t y)
|
||||
{
|
||||
mapnik::box2d<int> ext0(0, 0, dst.width(), dst.height());
|
||||
mapnik::box2d<int> ext1(x, y, x + src.width(), y + src.height());
|
||||
|
||||
if (ext0.intersects(ext1))
|
||||
{
|
||||
mapnik::box2d<int> box = ext0.intersect(ext1);
|
||||
for (std::size_t pix_y = box.miny(); pix_y < static_cast<std::size_t>(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<std::size_t>(box.maxx()); ++pix_x)
|
||||
{
|
||||
row_to[pix_x] = row_from[pix_x - x];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Renderer>
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
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);
|
||||
return ren.render(map, scale_factor);
|
||||
}
|
||||
|
||||
image_type render(mapnik::Map & map, double scale_factor, map_size const & tiles) const
|
||||
{
|
||||
mapnik::box2d<double> 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++)
|
||||
{
|
||||
mapnik::box2d<double> 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 image;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
|
@ -211,14 +280,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);
|
||||
|
@ -235,16 +305,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();
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <iomanip>
|
||||
#include <fstream>
|
||||
#include <numeric>
|
||||
#include <map>
|
||||
|
||||
#include "report.hpp"
|
||||
|
||||
|
@ -32,8 +33,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)
|
||||
{
|
||||
|
@ -51,6 +56,11 @@ void console_report::report(result const & r)
|
|||
break;
|
||||
}
|
||||
|
||||
if (show_duration)
|
||||
{
|
||||
s << " (" << std::chrono::duration_cast<std::chrono::milliseconds>(r.duration).count() << " milliseconds)";
|
||||
}
|
||||
|
||||
s << std::endl;
|
||||
}
|
||||
|
||||
|
@ -61,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<std::string, high_resolution_clock::duration>;
|
||||
duration_map_type durations;
|
||||
|
||||
for (auto const & r : results)
|
||||
{
|
||||
switch (r.state)
|
||||
|
@ -70,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<milliseconds>(duration.second).count()
|
||||
<< " milliseconds" << std::endl;
|
||||
total += duration.second;
|
||||
}
|
||||
s << "total: \t" << duration_cast<milliseconds>(total).count() << " milliseconds" << std::endl;
|
||||
}
|
||||
|
||||
return fail + error;
|
||||
}
|
||||
|
||||
|
|
|
@ -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(bool _show_duration) : console_report(_show_duration)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,18 @@
|
|||
|
||||
#include "cleanup.hpp" // run_cleanup()
|
||||
|
||||
#ifdef MAPNIK_LOG
|
||||
using log_levels_map = std::map<std::string, mapnik::logger::severity_type>;
|
||||
|
||||
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;
|
||||
|
@ -41,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<std::size_t>()->default_value(1), "number of iterations for benchmarking")
|
||||
("jobs,j", po::value<std::size_t>()->default_value(1), "number of parallel threads")
|
||||
("styles-dir", po::value<std::string>()->default_value("test/data-visual/styles"), "directory with styles")
|
||||
("images-dir", po::value<std::string>()->default_value("test/data-visual/images"), "directory with reference images")
|
||||
|
@ -49,6 +63,11 @@ int main(int argc, char** argv)
|
|||
("styles", po::value<std::vector<std::string>>(), "selected styles to test")
|
||||
("fonts", po::value<std::string>()->default_value("fonts"), "font search path")
|
||||
("plugins", po::value<std::string>()->default_value("plugins/input"), "input plugins search path")
|
||||
#ifdef MAPNIK_LOG
|
||||
("log", po::value<std::string>()->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 +82,20 @@ int main(int argc, char** argv)
|
|||
return 1;
|
||||
}
|
||||
|
||||
#ifdef MAPNIK_LOG
|
||||
std::string log_level(vm["log"].as<std::string>());
|
||||
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<std::string>(), true);
|
||||
mapnik::datasource_cache::instance().register_datasources(vm["plugins"].as<std::string>());
|
||||
|
||||
|
@ -77,8 +110,10 @@ int main(int argc, char** argv)
|
|||
output_dir,
|
||||
vm["images-dir"].as<std::string>(),
|
||||
vm.count("overwrite"),
|
||||
vm["iterations"].as<std::size_t>(),
|
||||
vm["jobs"].as<std::size_t>());
|
||||
report_type report = vm.count("verbose") ? report_type((console_report())) : report_type((console_short_report()));
|
||||
bool show_duration = vm.count("duration");
|
||||
report_type report(vm.count("verbose") ? report_type((console_report(show_duration))) : report_type((console_short_report(show_duration))));
|
||||
result_list results;
|
||||
|
||||
try
|
||||
|
|
|
@ -34,32 +34,97 @@ 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,
|
||||
std::size_t iterations)
|
||||
: name_(name),
|
||||
map_(map),
|
||||
tiles_(tiles),
|
||||
scale_factor_(scale_factor),
|
||||
results_(results),
|
||||
report_(report),
|
||||
iterations_(iterations)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
result operator()(T const & renderer) const
|
||||
template <typename T, typename std::enable_if<T::renderer_type::support_tiles>::type* = nullptr>
|
||||
void operator()(T const & renderer)
|
||||
{
|
||||
return renderer.test(name_, map_, scale_factor_);
|
||||
test(renderer);
|
||||
}
|
||||
|
||||
template <typename T, typename std::enable_if<!T::renderer_type::support_tiles>::type* = nullptr>
|
||||
void operator()(T const & renderer)
|
||||
{
|
||||
if (tiles_.width == 1 && tiles_.height == 1)
|
||||
{
|
||||
test(renderer);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
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 <typename T, typename std::enable_if<T::renderer_type::support_tiles>::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 <typename T, typename std::enable_if<!T::renderer_type::support_tiles>::type* = nullptr>
|
||||
typename T::image_type render(T const & renderer)
|
||||
{
|
||||
return renderer.render(map_, scale_factor_);
|
||||
}
|
||||
|
||||
std::string const & name_;
|
||||
mapnik::Map const & map_;
|
||||
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<agg_renderer>(output_dir_, reference_dir_, overwrite)
|
||||
#if defined(HAVE_CAIRO)
|
||||
,renderer<cairo_renderer>(output_dir_, reference_dir_, overwrite)
|
||||
|
@ -173,12 +238,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 +256,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<mapnik::value_integer> status = params.get<mapnik::value_integer>("status", cfg.status);
|
||||
|
||||
|
@ -208,32 +273,52 @@ result_list runner::test_one(runner::path_type const& style_path, config cfg, re
|
|||
parse_map_sizes(*sizes, cfg.sizes);
|
||||
}
|
||||
|
||||
std::string name(style_path.stem().string());
|
||||
boost::optional<std::string> tiles = params.get<std::string>("tiles");
|
||||
|
||||
for (map_size const & size : cfg.sizes)
|
||||
if (tiles)
|
||||
{
|
||||
m.resize(size.width, size.height);
|
||||
cfg.tiles.clear();
|
||||
parse_map_sizes(*tiles, cfg.tiles);
|
||||
}
|
||||
|
||||
boost::optional<std::string> bbox_string = params.get<std::string>("bbox");
|
||||
mapnik::box2d<double> box;
|
||||
|
||||
if (bbox_string)
|
||||
{
|
||||
mapnik::box2d<double> bbox;
|
||||
bbox.from_string(*bbox_string);
|
||||
m.zoom_to_box(bbox);
|
||||
box.from_string(*bbox_string);
|
||||
}
|
||||
|
||||
std::string name(style_path.stem().string());
|
||||
|
||||
for (auto const & size : cfg.sizes)
|
||||
{
|
||||
for (auto const & scale_factor : cfg.scales)
|
||||
{
|
||||
for (auto const & tiles_count : cfg.tiles)
|
||||
{
|
||||
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
|
||||
{
|
||||
m.zoom_all();
|
||||
map.zoom_all();
|
||||
}
|
||||
mapnik::util::apply_visitor(renderer_visitor(name, map, tiles_count, scale_factor, results, report, iterations_), ren);
|
||||
}
|
||||
|
||||
for (double const & scale_factor : cfg.scales)
|
||||
{
|
||||
for(auto const& ren : renderers_)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<renderer_type::types>::value];
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue