Compare commits
36 commits
master
...
rotated_re
Author | SHA1 | Date | |
---|---|---|---|
|
1388af9a57 | ||
|
d75400b983 | ||
|
ebbaf49896 | ||
|
2ceaa110cb | ||
|
6c54adc389 | ||
|
eca58770d9 | ||
|
89c3e2f6b9 | ||
|
edbad2664b | ||
|
bac031e6f6 | ||
|
9cbca0d2da | ||
|
3e74f68b26 | ||
|
7158c19f80 | ||
|
8e40711ca4 | ||
|
73e0ac6a77 | ||
|
ad43a98f43 | ||
|
fec6d11560 | ||
|
9c7186e49e | ||
|
69236137e5 | ||
|
d893718343 | ||
|
b228da7bc3 | ||
|
1a95f1753e | ||
|
40b963f9ad | ||
|
793a2f9ffb | ||
|
e5f1379fea | ||
|
cb832c0964 | ||
|
f1feedc54c | ||
|
989af2ea42 | ||
|
11e58275e9 | ||
|
f4ec97489d | ||
|
908f03bc19 | ||
|
35ff68a7ec | ||
|
6bb95d2c85 | ||
|
82a8735ef8 | ||
|
ce33e2d3cc | ||
|
f208717070 | ||
|
e0e46eb3a8 |
18 changed files with 489 additions and 182 deletions
|
@ -27,6 +27,7 @@ Import('env')
|
|||
base = './mapnik/'
|
||||
subdirs = [
|
||||
'',
|
||||
'csv',
|
||||
'svg',
|
||||
'wkt',
|
||||
'cairo',
|
||||
|
|
105
include/mapnik/csv/csv_grammar.hpp
Normal file
105
include/mapnik/csv/csv_grammar.hpp
Normal file
|
@ -0,0 +1,105 @@
|
|||
/*****************************************************************************
|
||||
*
|
||||
* This file is part of Mapnik (c++ mapping toolkit)
|
||||
*
|
||||
* Copyright (C) 2014 Artem Pavlenko
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef MAPNIK_CVS_GRAMMAR_HPP
|
||||
#define MAPNIK_CVS_GRAMMAR_HPP
|
||||
|
||||
//#define BOOST_SPIRIT_DEBUG
|
||||
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
|
||||
namespace mapnik {
|
||||
|
||||
namespace qi = boost::spirit::qi;
|
||||
using column = std::string;
|
||||
using columns = std::vector<column>;
|
||||
using csv_line = columns;
|
||||
using csv_data = std::vector<csv_line>;
|
||||
|
||||
template <typename Iterator>
|
||||
struct csv_line_grammar : qi::grammar<Iterator, csv_line(std::string const&), qi::blank_type>
|
||||
{
|
||||
csv_line_grammar() : csv_line_grammar::base_type(line)
|
||||
{
|
||||
using namespace qi;
|
||||
qi::_a_type _a;
|
||||
qi::_r1_type _r1;
|
||||
qi::lit_type lit;
|
||||
//qi::eol_type eol;
|
||||
qi::_val_type _val;
|
||||
qi::_1_type _1;
|
||||
qi::char_type char_;
|
||||
qi::eps_type eps;
|
||||
qi::omit_type omit;
|
||||
unesc_char.add
|
||||
("\\a", '\a')
|
||||
("\\b", '\b')
|
||||
("\\f", '\f')
|
||||
("\\n", '\n')
|
||||
("\\r", '\r')
|
||||
("\\t", '\t')
|
||||
("\\v", '\v')
|
||||
("\\\\",'\\')
|
||||
("\\\'", '\'')
|
||||
("\\\"", '\"')
|
||||
("\"\"", '\"') // double quote
|
||||
;
|
||||
|
||||
line = column(_r1) % char_(_r1)
|
||||
;
|
||||
column = quoted | *(char_ - (lit(_r1) /*| eol*/))
|
||||
;
|
||||
text = *(unesc_char | (char_ - char_(_r1)))
|
||||
;
|
||||
quoted = omit[char_("\"'")[_a = _1]] >> text(_a)[_val = _1] >> -lit(_a)
|
||||
;
|
||||
BOOST_SPIRIT_DEBUG_NODES((line)(column)(quoted));
|
||||
}
|
||||
private:
|
||||
qi::rule<Iterator, csv_line(std::string const&), qi::blank_type> line;
|
||||
qi::rule<Iterator, column(std::string const&)> column; // no-skip
|
||||
qi::rule<Iterator, std::string(char)> text;
|
||||
qi::rule<Iterator, qi::locals<char>, std::string()> quoted;
|
||||
qi::symbols<char const, char const> unesc_char;
|
||||
};
|
||||
|
||||
template <typename Iterator>
|
||||
struct csv_file_grammar : qi::grammar<Iterator, csv_data(std::string const&), qi::blank_type>
|
||||
{
|
||||
csv_file_grammar() : csv_file_grammar::base_type(start)
|
||||
{
|
||||
using namespace qi;
|
||||
qi::eol_type eol;
|
||||
qi::_r1_type _r1;
|
||||
start = -line(_r1) % eol
|
||||
;
|
||||
BOOST_SPIRIT_DEBUG_NODES((start));
|
||||
}
|
||||
private:
|
||||
qi::rule<Iterator, csv_data(std::string const&), qi::blank_type> start;
|
||||
csv_line_grammar<Iterator> line;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif // MAPNIK_CVS_GRAMMAR_HPP
|
|
@ -27,12 +27,14 @@
|
|||
#include <mapnik/quad_tree.hpp>
|
||||
#include <mapnik/util/noncopyable.hpp>
|
||||
#include <mapnik/value_types.hpp>
|
||||
|
||||
#include <mapnik/rotated_rectangle_collision.hpp>
|
||||
// icu
|
||||
#include <unicode/unistr.h>
|
||||
|
||||
// stl
|
||||
#include <vector>
|
||||
// boost
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
namespace mapnik
|
||||
{
|
||||
|
@ -131,11 +133,18 @@ class label_collision_detector4 : util::noncopyable
|
|||
public:
|
||||
struct label
|
||||
{
|
||||
label(box2d<double> const& b) : box(b), text() {}
|
||||
label(box2d<double> const& b, mapnik::value_unicode_string const& t) : box(b), text(t) {}
|
||||
label(box2d<double> const& b, double a)
|
||||
: box(b),
|
||||
angle(a),
|
||||
text()
|
||||
{}
|
||||
|
||||
label(box2d<double> const& b, double a, mapnik::value_unicode_string const& t)
|
||||
: box(b), angle(a), text(t) {}
|
||||
|
||||
box2d<double> box;
|
||||
mapnik::value_unicode_string text;
|
||||
double angle = 0.0;
|
||||
boost::optional<mapnik::value_unicode_string const&> text;
|
||||
};
|
||||
|
||||
private:
|
||||
|
@ -181,10 +190,30 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
bool has_placement(box2d<double> const& box, double margin, double angle)
|
||||
{
|
||||
tree_t::query_iterator itr = tree_.query_in_box(box);
|
||||
tree_t::query_iterator end = tree_.query_end();
|
||||
|
||||
auto rect_a = rotate(box, angle);
|
||||
|
||||
for (;itr != end; ++itr)
|
||||
{
|
||||
auto const& lab = itr->get();
|
||||
auto rect_b = rotate(lab.box, lab.angle);
|
||||
if (intersects(rect_a, rect_b))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool has_placement(box2d<double> const& box, double margin, mapnik::value_unicode_string const& text, double repeat_distance)
|
||||
{
|
||||
// Don't bother with any of the repeat checking unless the repeat distance is greater than the margin
|
||||
if (repeat_distance <= margin) {
|
||||
if (repeat_distance <= margin)
|
||||
{
|
||||
return has_placement(box, margin);
|
||||
}
|
||||
|
||||
|
@ -201,7 +230,7 @@ public:
|
|||
|
||||
for ( ;itr != end; ++itr)
|
||||
{
|
||||
if (itr->get().box.intersects(margin_box) || (text == itr->get().text && itr->get().box.intersects(repeat_box)))
|
||||
if (itr->get().box.intersects(margin_box) || (itr->get().text && text == *itr->get().text && itr->get().box.intersects(repeat_box)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -210,14 +239,14 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
void insert(box2d<double> const& box)
|
||||
void insert(box2d<double> const& box, double angle = 0.0)
|
||||
{
|
||||
tree_.insert(label(box), box);
|
||||
tree_.insert(label(box, angle), box);
|
||||
}
|
||||
|
||||
void insert(box2d<double> const& box, mapnik::value_unicode_string const& text)
|
||||
void insert(box2d<double> const& box, mapnik::value_unicode_string const& text, double angle = 0.0)
|
||||
{
|
||||
tree_.insert(label(box, text), box);
|
||||
tree_.insert(label(box, angle, text), box);
|
||||
}
|
||||
|
||||
void clear()
|
||||
|
|
|
@ -184,7 +184,7 @@ private:
|
|||
nodes_.push_back(std::make_unique<node>(ext[i]));
|
||||
n->children_[i]=nodes_.back().get();
|
||||
}
|
||||
do_insert_data(data,box,n->children_[i],depth);
|
||||
do_insert_data(data,box, n->children_[i],depth);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
105
include/mapnik/rotated_rectangle_collision.hpp
Normal file
105
include/mapnik/rotated_rectangle_collision.hpp
Normal file
|
@ -0,0 +1,105 @@
|
|||
/*****************************************************************************
|
||||
*
|
||||
* This file is part of Mapnik (c++ mapping toolkit)
|
||||
*
|
||||
* Copyright (C) 2015 Artem Pavlenko
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef MAPNIK_ROTATED_RECTANGLE_COLLISION_HPP
|
||||
#define MAPNIK_ROTATED_RECTANGLE_COLLISION_HPP
|
||||
|
||||
#include <mapnik/box2d.hpp>
|
||||
#include <mapnik/geometry.hpp>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
|
||||
namespace mapnik {
|
||||
|
||||
template <typename T>
|
||||
using rotated_rectangle = std::array<geometry::point<T> ,4>;
|
||||
|
||||
template <typename T>
|
||||
rotated_rectangle<T> rotate(mapnik::box2d<T> const& box, double angle)
|
||||
{
|
||||
rotated_rectangle<T> rect;
|
||||
auto c = box.center();
|
||||
auto lox = box.minx() - c.x;
|
||||
auto loy = box.miny() - c.y;
|
||||
auto hix = box.maxx() - c.x;
|
||||
auto hiy = box.maxy() - c.y;
|
||||
double cos_a = cos(angle);
|
||||
double sin_a = sin(angle);
|
||||
|
||||
rect[0].x = c.x + lox * cos_a - loy * sin_a;
|
||||
rect[0].y = c.y + lox * sin_a + loy * cos_a;
|
||||
rect[1].x = c.x + lox * cos_a - hiy * sin_a;
|
||||
rect[1].y = c.y + lox * sin_a + hiy * cos_a;
|
||||
rect[2].x = c.x + hix * cos_a - hiy * sin_a;
|
||||
rect[2].y = c.y + hix * sin_a + hiy * cos_a;
|
||||
rect[3].x = c.x + hix * cos_a - loy * sin_a;
|
||||
rect[3].y = c.y + hix * sin_a + loy * cos_a;
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
box2d<T> bounding_box(rotated_rectangle<T> const& r)
|
||||
{
|
||||
box2d<T> box(r[0].x, r[0].x, r[0].y, r[0].y);
|
||||
box.expand_to_include(r[1].x, r[1].y);
|
||||
box.expand_to_include(r[2].x, r[2].y);
|
||||
box.expand_to_include(r[3].x, r[3].y);
|
||||
return box;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool intersects(rotated_rectangle<T> const& r0, rotated_rectangle<T> const& r1)
|
||||
{
|
||||
for (rotated_rectangle<T> const& r : { std::cref(r0), std::cref(r1) })
|
||||
{
|
||||
for (std::size_t i = 0; i < 2; ++i)
|
||||
{
|
||||
auto nx = r[i + 1].y - r[i].y;
|
||||
auto ny = r[i].x - r[i + 1].x;
|
||||
auto min_a = std::numeric_limits<T>::max();
|
||||
auto max_a = -min_a;
|
||||
for(std::size_t j = 0; j < 4; ++j)
|
||||
{
|
||||
auto projected = nx * r0[j].x + ny * r0[j].y;
|
||||
if( projected < min_a ) min_a = projected;
|
||||
if( projected > max_a ) max_a = projected;
|
||||
}
|
||||
|
||||
auto min_b = std::numeric_limits<T>::max();
|
||||
auto max_b = -min_b;
|
||||
for(std::size_t j = 0; j < 4; ++j)
|
||||
{
|
||||
auto projected = nx * r1[j].x + ny * r1[j].y;
|
||||
if( projected < min_b ) min_b = projected;
|
||||
if( projected > max_b ) max_b = projected;
|
||||
}
|
||||
if ( max_a < min_b || max_b < min_a ) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif //MAPNIK_ROTATED_RECTANGLE_COLLISION_HPP
|
|
@ -77,11 +77,11 @@ struct glyph_info : util::noncopyable
|
|||
double scale_multiplier;
|
||||
pixel_position offset;
|
||||
|
||||
double ymin() const { return unscaled_ymin * 64.0 * scale_multiplier; }
|
||||
double ymax() const { return unscaled_ymax * 64.0 * scale_multiplier; }
|
||||
double height() const { return ymax() - ymin(); };
|
||||
double advance() const { return unscaled_advance * scale_multiplier; };
|
||||
double line_height() const { return unscaled_line_height * scale_multiplier; };
|
||||
inline double ymin() const { return unscaled_ymin * 64.0 * scale_multiplier; }
|
||||
inline double ymax() const { return unscaled_ymax * 64.0 * scale_multiplier; }
|
||||
inline double height() const { return ymax() - ymin(); };
|
||||
inline double advance() const { return unscaled_advance * scale_multiplier; };
|
||||
inline double line_height() const { return unscaled_line_height * scale_multiplier; };
|
||||
};
|
||||
|
||||
} //ns mapnik
|
||||
|
|
|
@ -97,7 +97,8 @@ static void shape_text(text_line & line,
|
|||
{
|
||||
++pos;
|
||||
hb_buffer_clear_contents(buffer.get());
|
||||
hb_buffer_add_utf16(buffer.get(), uchar_to_utf16(text.getBuffer()), text.length(), text_item.start, static_cast<int>(text_item.end - text_item.start));
|
||||
hb_buffer_add_utf16(buffer.get(), uchar_to_utf16(text.getBuffer()), text.length(), text_item.start,
|
||||
static_cast<int>(text_item.end - text_item.start));
|
||||
hb_buffer_set_direction(buffer.get(), (text_item.dir == UBIDI_RTL)?HB_DIRECTION_RTL:HB_DIRECTION_LTR);
|
||||
hb_buffer_set_script(buffer.get(), _icu_script_to_script(text_item.script));
|
||||
hb_font_t *font(hb_ft_font_create(face->get_face(), nullptr));
|
||||
|
@ -125,27 +126,33 @@ static void shape_text(text_line & line,
|
|||
continue;
|
||||
}
|
||||
|
||||
double max_glyph_height = 0;
|
||||
for (unsigned i=0; i<num_glyphs; ++i)
|
||||
double ymin = 0.0;
|
||||
double ymax = 0.0;
|
||||
double units_per_EM = face->get_face()->units_per_EM;
|
||||
double scale_multiplier = (units_per_EM > 0) ? (size / units_per_EM) : 1.0 ;
|
||||
|
||||
for (unsigned i = 0; i < num_glyphs; ++i)
|
||||
{
|
||||
auto const& gpos = positions[i];
|
||||
auto const& glyph = glyphs[i];
|
||||
unsigned char_index = glyph.cluster;
|
||||
glyph_info g(glyph.codepoint,char_index,text_item.format_);
|
||||
|
||||
if (face->glyph_dimensions(g))
|
||||
{
|
||||
g.face = face;
|
||||
g.scale_multiplier = size / face->get_face()->units_per_EM;
|
||||
g.scale_multiplier = scale_multiplier;//size / face->get_face()->units_per_EM;
|
||||
//Overwrite default advance with better value provided by HarfBuzz
|
||||
g.unscaled_advance = gpos.x_advance;
|
||||
g.offset.set(gpos.x_offset * g.scale_multiplier, gpos.y_offset * g.scale_multiplier);
|
||||
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);
|
||||
ymin = std::min(g.ymin(), ymin);
|
||||
ymax = std::max(g.ymax(), ymax);
|
||||
}
|
||||
}
|
||||
line.update_max_char_height(max_glyph_height);
|
||||
std::cerr << "Harfbuzz shaper: update y min/max " << ymin << "," << ymax << std::endl;
|
||||
line.set_y_minmax(ymin, ymax);
|
||||
break; //When we reach this point the current font had all glyphs.
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,7 +90,9 @@ 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;
|
||||
double ymin = 0.0;
|
||||
double ymax = 0.0;
|
||||
double scale_multiplier = size / face->get_face()->units_per_EM;
|
||||
if (U_SUCCESS(err) && (num_chars == length))
|
||||
{
|
||||
unsigned char_index = 0;
|
||||
|
@ -109,16 +111,17 @@ static void shape_text(text_line & line,
|
|||
if (face->glyph_dimensions(g))
|
||||
{
|
||||
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;
|
||||
g.scale_multiplier = scale_multiplier;
|
||||
width_map[char_index++] += g.advance();
|
||||
line.add_glyph(std::move(g), scale_factor);
|
||||
ymin = std::min(g.ymin(), ymin);
|
||||
ymax = std::max(g.ymax(), ymax);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!shaped_status) continue;
|
||||
line.update_max_char_height(max_glyph_height);
|
||||
std::cerr << "ICU shaper: update y min/max " << ymin << "," << ymax << std::endl;
|
||||
line.set_y_minmax(ymin, ymax);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -88,7 +88,8 @@ public:
|
|||
inline double height() const { return height_; }
|
||||
// Width of the longest line (in pixels).
|
||||
inline double width() const { return width_ ; }
|
||||
|
||||
inline double ymin() const { return ymin_; }
|
||||
inline double ymax() const { return ymax_; }
|
||||
// Line iterator.
|
||||
inline const_iterator begin() const { return lines_.begin(); }
|
||||
inline const_iterator end() const { return lines_.end(); }
|
||||
|
@ -145,6 +146,8 @@ private:
|
|||
std::map<unsigned, double> width_map_;
|
||||
double width_;
|
||||
double height_;
|
||||
double ymin_;
|
||||
double ymax_;
|
||||
unsigned glyphs_count_;
|
||||
|
||||
// output
|
||||
|
|
|
@ -63,12 +63,15 @@ public:
|
|||
// Real line height. For first line: max_char_height(), for all others: line_height().
|
||||
double height() const;
|
||||
|
||||
// Height of the tallest glyph in this line.
|
||||
double max_char_height() const { return max_char_height_; }
|
||||
|
||||
// Called for each font/style to update the maximum height of this line.
|
||||
void update_max_char_height(double max_char_height);
|
||||
inline double ymin() const { return ymin_; }
|
||||
inline double ymax() const { return ymax_; }
|
||||
|
||||
// Called for each font/style to update the max/min y of this line.
|
||||
inline void set_y_minmax(double ymin, double ymax)
|
||||
{
|
||||
ymin_ = ymin;
|
||||
ymax_ = ymax;
|
||||
}
|
||||
// Line height including line spacing.
|
||||
double line_height() const { return line_height_; }
|
||||
|
||||
|
@ -84,12 +87,13 @@ public:
|
|||
// Number of glyphs.
|
||||
unsigned size() const;
|
||||
|
||||
unsigned space_count() const { return space_count_; }
|
||||
inline unsigned space_count() const { return space_count_; }
|
||||
|
||||
private:
|
||||
glyph_vector glyphs_;
|
||||
double line_height_; // Includes line spacing (returned by freetype)
|
||||
double max_char_height_; // Max height of any glyphs in line - calculated by shaper
|
||||
double ymin_;
|
||||
double ymax_;
|
||||
double width_;
|
||||
double glyphs_width_;
|
||||
unsigned first_char_;
|
||||
|
|
|
@ -24,8 +24,8 @@
|
|||
#include "csv_utils.hpp"
|
||||
|
||||
// boost
|
||||
#include <boost/tokenizer.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
|
||||
// mapnik
|
||||
#include <mapnik/debug.hpp>
|
||||
|
@ -43,11 +43,10 @@
|
|||
#include <mapnik/util/trim.hpp>
|
||||
#include <mapnik/util/geometry_to_ds_type.hpp>
|
||||
#include <mapnik/value_types.hpp>
|
||||
|
||||
#include <mapnik/csv/csv_grammar.hpp>
|
||||
// stl
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
@ -57,13 +56,30 @@ using mapnik::parameters;
|
|||
|
||||
DATASOURCE_PLUGIN(csv_datasource)
|
||||
|
||||
namespace mapnik {
|
||||
|
||||
static const csv_line_grammar<char const*> line_g;
|
||||
|
||||
csv_line parse_line(std::string & line_str, std::string const& separator)
|
||||
{
|
||||
csv_line values;
|
||||
auto start = line_str.c_str();
|
||||
auto end = start + line_str.length();
|
||||
boost::spirit::standard::blank_type blank;
|
||||
if (!boost::spirit::qi::phrase_parse(start, end, (line_g)(boost::phoenix::cref(separator)), blank, values))
|
||||
{
|
||||
throw std::runtime_error("Failed to parse CSV line:\n" + line_str);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
csv_datasource::csv_datasource(parameters const& params)
|
||||
: datasource(params),
|
||||
desc_(csv_datasource::name(), *params.get<std::string>("encoding", "utf-8")),
|
||||
extent_(),
|
||||
filename_(),
|
||||
inline_string_(),
|
||||
file_length_(0),
|
||||
row_limit_(*params.get<mapnik::value_integer>("row_limit", 0)),
|
||||
features_(),
|
||||
escape_(*params.get<std::string>("escape", "")),
|
||||
|
@ -123,7 +139,7 @@ csv_datasource::csv_datasource(parameters const& params)
|
|||
if (!inline_string_.empty())
|
||||
{
|
||||
std::istringstream in(inline_string_);
|
||||
parse_csv(in,escape_, separator_, quote_);
|
||||
parse_csv(in, escape_, separator_, quote_);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -136,7 +152,7 @@ csv_datasource::csv_datasource(parameters const& params)
|
|||
{
|
||||
throw mapnik::datasource_exception("CSV Plugin: could not open: '" + filename_ + "'");
|
||||
}
|
||||
parse_csv(in,escape_, separator_, quote_);
|
||||
parse_csv(in, escape_, separator_, quote_);
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
|
@ -144,36 +160,59 @@ csv_datasource::csv_datasource(parameters const& params)
|
|||
|
||||
csv_datasource::~csv_datasource() { }
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
void csv_datasource::parse_csv(T & stream,
|
||||
std::string const& escape,
|
||||
std::string const& separator,
|
||||
std::string const& quote)
|
||||
std::size_t file_length(T & stream)
|
||||
{
|
||||
stream.seekg(0, std::ios::end);
|
||||
file_length_ = stream.tellg();
|
||||
return stream.tellg();
|
||||
}
|
||||
|
||||
if (filesize_max_ > 0)
|
||||
std::string detect_separator(std::string const& str)
|
||||
{
|
||||
std::string separator = ","; // default
|
||||
int num_commas = std::count(str.begin(), str.end(), ',');
|
||||
// detect tabs
|
||||
int num_tabs = std::count(str.begin(), str.end(), '\t');
|
||||
if (num_tabs > 0)
|
||||
{
|
||||
double file_mb = static_cast<double>(file_length_)/1048576;
|
||||
|
||||
// throw if this is an unreasonably large file to read into memory
|
||||
if (file_mb > filesize_max_)
|
||||
if (num_tabs > num_commas)
|
||||
{
|
||||
std::ostringstream s;
|
||||
s << "CSV Plugin: csv file is greater than ";
|
||||
s << filesize_max_ << "MB - you should use a more efficient data format like sqlite, postgis or a shapefile to render this data (set 'filesize_max=0' to disable this restriction if you have lots of memory)";
|
||||
throw mapnik::datasource_exception(s.str());
|
||||
separator = "\t";
|
||||
|
||||
MAPNIK_LOG_DEBUG(csv) << "csv_datasource: auto detected tab separator";
|
||||
}
|
||||
}
|
||||
else // pipes
|
||||
{
|
||||
int num_pipes = std::count(str.begin(), str.end(), '|');
|
||||
if (num_pipes > num_commas)
|
||||
{
|
||||
separator = "|";
|
||||
|
||||
// set back to start
|
||||
stream.seekg(0, std::ios::beg);
|
||||
MAPNIK_LOG_DEBUG(csv) << "csv_datasource: auto detected '|' separator";
|
||||
}
|
||||
else // semicolons
|
||||
{
|
||||
int num_semicolons = std::count(str.begin(), str.end(), ';');
|
||||
if (num_semicolons > num_commas)
|
||||
{
|
||||
separator = ";";
|
||||
MAPNIK_LOG_DEBUG(csv) << "csv_datasource: auto detected ';' separator";
|
||||
}
|
||||
}
|
||||
}
|
||||
return separator;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::tuple<char,bool> autodect_newline(T & stream, std::size_t file_length)
|
||||
{
|
||||
// autodetect newlines
|
||||
char newline = '\n';
|
||||
bool has_newline = false;
|
||||
for (unsigned lidx = 0; lidx < file_length_ && lidx < 4000; lidx++)
|
||||
for (std::size_t lidx = 0; lidx < file_length && lidx < 4000; ++lidx)
|
||||
{
|
||||
char c = static_cast<char>(stream.get());
|
||||
if (c == '\r')
|
||||
|
@ -188,60 +227,57 @@ void csv_datasource::parse_csv(T & stream,
|
|||
break;
|
||||
}
|
||||
}
|
||||
return std::make_tuple(newline,has_newline);
|
||||
}
|
||||
|
||||
} // ns detail
|
||||
|
||||
|
||||
|
||||
template <typename T>
|
||||
void csv_datasource::parse_csv(T & stream,
|
||||
std::string const& escape,
|
||||
std::string const& separator,
|
||||
std::string const& quote)
|
||||
{
|
||||
auto file_length = detail::file_length(stream);
|
||||
if (filesize_max_ > 0)
|
||||
{
|
||||
double file_mb = static_cast<double>(file_length)/1048576;
|
||||
|
||||
// throw if this is an unreasonably large file to read into memory
|
||||
if (file_mb > filesize_max_)
|
||||
{
|
||||
std::ostringstream s;
|
||||
s << "CSV Plugin: csv file is greater than ";
|
||||
s << filesize_max_ << "MB - you should use a more efficient data format like sqlite, postgis or a shapefile to render this data (set 'filesize_max=0' to disable this restriction if you have lots of memory)";
|
||||
throw mapnik::datasource_exception(s.str());
|
||||
}
|
||||
}
|
||||
|
||||
// set back to start
|
||||
stream.seekg(0, std::ios::beg);
|
||||
char newline;
|
||||
bool has_newline;
|
||||
std::tie(newline, has_newline) = detail::autodect_newline(stream, file_length);
|
||||
// set back to start
|
||||
stream.seekg(0, std::ios::beg);
|
||||
|
||||
// get first line
|
||||
std::string csv_line;
|
||||
std::getline(stream,csv_line,newline);
|
||||
std::getline(stream,csv_line,stream.widen(newline));
|
||||
|
||||
// if user has not passed a separator manually
|
||||
// then attempt to detect by reading first line
|
||||
std::string sep = mapnik::util::trim_copy(separator);
|
||||
if (sep.empty())
|
||||
{
|
||||
// default to ','
|
||||
sep = ",";
|
||||
int num_commas = std::count(csv_line.begin(), csv_line.end(), ',');
|
||||
// detect tabs
|
||||
int num_tabs = std::count(csv_line.begin(), csv_line.end(), '\t');
|
||||
if (num_tabs > 0)
|
||||
{
|
||||
if (num_tabs > num_commas)
|
||||
{
|
||||
sep = "\t";
|
||||
|
||||
MAPNIK_LOG_DEBUG(csv) << "csv_datasource: auto detected tab separator";
|
||||
}
|
||||
}
|
||||
else // pipes
|
||||
{
|
||||
int num_pipes = std::count(csv_line.begin(), csv_line.end(), '|');
|
||||
if (num_pipes > num_commas)
|
||||
{
|
||||
sep = "|";
|
||||
|
||||
MAPNIK_LOG_DEBUG(csv) << "csv_datasource: auto detected '|' separator";
|
||||
}
|
||||
else // semicolons
|
||||
{
|
||||
int num_semicolons = std::count(csv_line.begin(), csv_line.end(), ';');
|
||||
if (num_semicolons > num_commas)
|
||||
{
|
||||
sep = ";";
|
||||
|
||||
MAPNIK_LOG_DEBUG(csv) << "csv_datasource: auto detected ';' separator";
|
||||
}
|
||||
}
|
||||
}
|
||||
sep = detail::detect_separator(csv_line);
|
||||
}
|
||||
|
||||
// set back to start
|
||||
stream.seekg(0, std::ios::beg);
|
||||
|
||||
using escape_type = boost::escaped_list_separator<char>;
|
||||
|
||||
std::string esc = mapnik::util::trim_copy(escape);
|
||||
if (esc.empty()) esc = "\\";
|
||||
|
||||
|
@ -251,21 +287,6 @@ void csv_datasource::parse_csv(T & stream,
|
|||
MAPNIK_LOG_DEBUG(csv) << "csv_datasource: csv grammar: sep: '" << sep
|
||||
<< "' quo: '" << quo << "' esc: '" << esc << "'";
|
||||
|
||||
boost::escaped_list_separator<char> grammer;
|
||||
try
|
||||
{
|
||||
// grammer = boost::escaped_list_separator<char>('\\', ',', '\"');
|
||||
grammer = boost::escaped_list_separator<char>(esc, sep, quo);
|
||||
}
|
||||
catch(std::exception const& ex)
|
||||
{
|
||||
std::string s("CSV Plugin: ");
|
||||
s += ex.what();
|
||||
throw mapnik::datasource_exception(s);
|
||||
}
|
||||
|
||||
using Tokenizer = boost::tokenizer< escape_type >;
|
||||
|
||||
int line_number = 1;
|
||||
bool has_wkt_field = false;
|
||||
bool has_json_field = false;
|
||||
|
@ -278,12 +299,11 @@ void csv_datasource::parse_csv(T & stream,
|
|||
|
||||
if (!manual_headers_.empty())
|
||||
{
|
||||
Tokenizer tok(manual_headers_, grammer);
|
||||
Tokenizer::iterator beg = tok.begin();
|
||||
unsigned idx = 0;
|
||||
for (; beg != tok.end(); ++beg)
|
||||
auto headers = mapnik::parse_line(manual_headers_, sep);
|
||||
for (auto const& header : headers)
|
||||
{
|
||||
std::string val = mapnik::util::trim_copy(*beg);
|
||||
std::string val = mapnik::util::trim_copy(header);
|
||||
std::string lower_val = val;
|
||||
std::transform(lower_val.begin(), lower_val.end(), lower_val.begin(), ::tolower);
|
||||
if (lower_val == "wkt"
|
||||
|
@ -319,18 +339,14 @@ void csv_datasource::parse_csv(T & stream,
|
|||
}
|
||||
else // parse first line as headers
|
||||
{
|
||||
while (std::getline(stream,csv_line,newline))
|
||||
while (std::getline(stream,csv_line,stream.widen(newline)))
|
||||
{
|
||||
try
|
||||
{
|
||||
Tokenizer tok(csv_line, grammer);
|
||||
Tokenizer::iterator beg = tok.begin();
|
||||
std::string val;
|
||||
if (beg != tok.end())
|
||||
val = mapnik::util::trim_copy(*beg);
|
||||
|
||||
auto headers = mapnik::parse_line(csv_line, sep);
|
||||
// skip blank lines
|
||||
if (val.empty())
|
||||
std::string val;
|
||||
if (headers.size() > 0 && headers[0].empty())
|
||||
{
|
||||
// do nothing
|
||||
++line_number;
|
||||
|
@ -338,10 +354,10 @@ void csv_datasource::parse_csv(T & stream,
|
|||
else
|
||||
{
|
||||
int idx = -1;
|
||||
for (; beg != tok.end(); ++beg)
|
||||
for (auto const& header : headers)
|
||||
{
|
||||
++idx;
|
||||
val = mapnik::util::trim_copy(*beg);
|
||||
val = mapnik::util::trim_copy(header);
|
||||
if (val.empty())
|
||||
{
|
||||
if (strict_)
|
||||
|
@ -434,7 +450,7 @@ void csv_datasource::parse_csv(T & stream,
|
|||
is_first_row = true;
|
||||
}
|
||||
}
|
||||
while (std::getline(stream,csv_line,newline) || is_first_row)
|
||||
while (std::getline(stream,csv_line, stream.widen(newline)) || is_first_row)
|
||||
{
|
||||
is_first_row = false;
|
||||
if ((row_limit_ > 0) && (line_number > row_limit_))
|
||||
|
@ -459,17 +475,8 @@ void csv_datasource::parse_csv(T & stream,
|
|||
|
||||
try
|
||||
{
|
||||
// special handling for varieties of quoting that we will enounter with json
|
||||
// TODO - test with custom "quo" option
|
||||
if (has_json_field && (quo == "\"") && (std::count(csv_line.begin(), csv_line.end(), '"') >= 6))
|
||||
{
|
||||
csv_utils::fix_json_quoting(csv_line);
|
||||
}
|
||||
|
||||
Tokenizer tok(csv_line, grammer);
|
||||
Tokenizer::iterator beg = tok.begin();
|
||||
|
||||
unsigned num_fields = std::distance(beg,tok.end());
|
||||
auto values = mapnik::parse_line(csv_line, sep);
|
||||
unsigned num_fields = values.size();
|
||||
if (num_fields > num_headers)
|
||||
{
|
||||
std::ostringstream s;
|
||||
|
@ -494,8 +501,10 @@ void csv_datasource::parse_csv(T & stream,
|
|||
}
|
||||
}
|
||||
|
||||
auto beg = values.begin();
|
||||
auto end = values.end();
|
||||
// NOTE: we use ++feature_count here because feature id's should start at 1;
|
||||
mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx_,++feature_count));
|
||||
mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx_, ++feature_count));
|
||||
double x = 0;
|
||||
double y = 0;
|
||||
bool parsed_x = false;
|
||||
|
@ -508,7 +517,7 @@ void csv_datasource::parse_csv(T & stream,
|
|||
std::string fld_name(headers_.at(i));
|
||||
collected.push_back(fld_name);
|
||||
std::string value;
|
||||
if (beg == tok.end()) // there are more headers than column values for this row
|
||||
if (beg == end) // there are more headers than column values for this row
|
||||
{
|
||||
// add an empty string here to represent a missing value
|
||||
// not using null type here since nulls are not a csv thing
|
||||
|
@ -959,7 +968,7 @@ boost::optional<mapnik::datasource_geometry_t> csv_datasource::get_geometry_type
|
|||
|
||||
mapnik::featureset_ptr csv_datasource::features(mapnik::query const& q) const
|
||||
{
|
||||
const std::set<std::string>& attribute_names = q.property_names();
|
||||
std::set<std::string> const& attribute_names = q.property_names();
|
||||
std::set<std::string>::const_iterator pos = attribute_names.begin();
|
||||
while (pos != attribute_names.end())
|
||||
{
|
||||
|
|
|
@ -64,7 +64,6 @@ private:
|
|||
mapnik::box2d<double> extent_;
|
||||
std::string filename_;
|
||||
std::string inline_string_;
|
||||
unsigned file_length_;
|
||||
mapnik::value_integer row_limit_;
|
||||
std::deque<mapnik::feature_ptr> features_;
|
||||
std::string escape_;
|
||||
|
|
|
@ -32,7 +32,8 @@
|
|||
#include <mapnik/transform_path_adapter.hpp>
|
||||
#include <mapnik/agg_helpers.hpp>
|
||||
#include <mapnik/util/is_clockwise.hpp>
|
||||
|
||||
#include <mapnik/rotated_rectangle_collision.hpp>
|
||||
#include <mapnik/path.hpp>
|
||||
// agg
|
||||
#include "agg_basics.h"
|
||||
#include "agg_rendering_buffer.h"
|
||||
|
@ -52,6 +53,7 @@ void draw_rect(T &pixmap, box2d<double> const& box)
|
|||
int x1 = static_cast<int>(box.maxx());
|
||||
int y0 = static_cast<int>(box.miny());
|
||||
int y1 = static_cast<int>(box.maxy());
|
||||
|
||||
unsigned color1 = 0xff0000ff;
|
||||
for (int x=x0; x<x1; x++)
|
||||
{
|
||||
|
@ -65,6 +67,39 @@ void draw_rect(T &pixmap, box2d<double> const& box)
|
|||
}
|
||||
}
|
||||
|
||||
template <typename T, typename R>
|
||||
void draw_rotated_rect(T & pixmap, R & ras, box2d<double> const& box, double angle)
|
||||
{
|
||||
using color_type = agg::rgba8;
|
||||
using order_type = agg::order_rgba;
|
||||
using blender_type = agg::comp_op_adaptor_rgba_pre<color_type, order_type>; // comp blender
|
||||
using pixfmt_comp_type = agg::pixfmt_custom_blend_rgba<blender_type, agg::rendering_buffer>;
|
||||
using renderer_base = agg::renderer_base<pixfmt_comp_type>;
|
||||
using renderer_type = agg::renderer_scanline_aa_solid<renderer_base>;
|
||||
|
||||
agg::rendering_buffer buf(pixmap.bytes(),pixmap.width(),pixmap.height(), pixmap.row_size());
|
||||
pixfmt_comp_type pixf(buf);
|
||||
pixf.comp_op(agg::comp_op_src_over);
|
||||
renderer_base renb(pixf);
|
||||
renderer_type ren(renb);
|
||||
ren.color(agg::rgba8_pre(255, 0, 0, 255));
|
||||
agg::scanline_u8 sl;
|
||||
ras.filling_rule(agg::fill_non_zero);
|
||||
auto rotated_box = rotate(box, angle);
|
||||
path_type p(path_type::LineString);
|
||||
p.move_to(rotated_box[0].x, rotated_box[0].y);
|
||||
p.line_to(rotated_box[1].x, rotated_box[1].y);
|
||||
p.line_to(rotated_box[2].x, rotated_box[2].y);
|
||||
p.line_to(rotated_box[3].x, rotated_box[3].y);
|
||||
p.line_to(rotated_box[0].x, rotated_box[0].y);
|
||||
vertex_adapter va(p);
|
||||
agg::conv_stroke<vertex_adapter> stroke(va);
|
||||
stroke.width(1.0);
|
||||
ras.add_path(stroke);
|
||||
agg::render_scanlines(ras, sl, ren);
|
||||
ras.reset();
|
||||
|
||||
}
|
||||
template <typename Pixmap>
|
||||
struct apply_vertex_mode
|
||||
{
|
||||
|
@ -208,8 +243,8 @@ struct render_ring_visitor {
|
|||
|
||||
template <typename T0, typename T1>
|
||||
void agg_renderer<T0,T1>::process(debug_symbolizer const& sym,
|
||||
mapnik::feature_impl & feature,
|
||||
proj_transform const& prj_trans)
|
||||
mapnik::feature_impl & feature,
|
||||
proj_transform const& prj_trans)
|
||||
{
|
||||
|
||||
debug_symbolizer_mode_enum mode = get<debug_symbolizer_mode_enum>(sym, keys::mode, feature, common_.vars_, DEBUG_SYM_MODE_COLLISION);
|
||||
|
@ -232,7 +267,9 @@ void agg_renderer<T0,T1>::process(debug_symbolizer const& sym,
|
|||
{
|
||||
for (auto const& n : *common_.detector_)
|
||||
{
|
||||
draw_rect(pixmap_, n.get().box);
|
||||
auto const& label = n.get();
|
||||
std::cerr << "label.box=>" << label.box << std::endl;
|
||||
draw_rotated_rect(pixmap_, *ras_ptr, label.box, label.angle);
|
||||
}
|
||||
}
|
||||
else if (mode == DEBUG_SYM_MODE_VERTEX)
|
||||
|
|
|
@ -64,11 +64,14 @@ bool font_face::glyph_dimensions(glyph_info & glyph) const
|
|||
}
|
||||
FT_BBox glyph_bbox;
|
||||
FT_Glyph_Get_CBox(image, FT_GLYPH_BBOX_TRUNCATE, &glyph_bbox);
|
||||
FT_Done_Glyph(image);
|
||||
|
||||
glyph.unscaled_ymin = glyph_bbox.yMin;
|
||||
glyph.unscaled_ymax = glyph_bbox.yMax;
|
||||
glyph.unscaled_advance = face_->glyph->advance.x;
|
||||
glyph.unscaled_line_height = face_->size->metrics.height;
|
||||
// https://github.com/mapnik/mapnik/issues/2928
|
||||
std::cerr << "yMin=" << glyph_bbox.yMin << " yMax=" << glyph_bbox.yMax << std::endl;
|
||||
FT_Done_Glyph(image);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -119,17 +119,17 @@ 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_unique<glyph_positions>();
|
||||
std::vector<box2d<double> > bboxes;
|
||||
std::vector<std::tuple<box2d<double>,double> > labels;
|
||||
|
||||
glyphs->reserve(layouts_.glyphs_count());
|
||||
bboxes.reserve(layouts_.size());
|
||||
labels.reserve(layouts_.size());
|
||||
|
||||
bool base_point_set = false;
|
||||
for (auto const& layout_ptr : layouts_)
|
||||
{
|
||||
text_layout const& layout = *layout_ptr;
|
||||
rotation const& orientation = layout.orientation();
|
||||
|
||||
double angle = (orientation.sin > 0 )? -acos(orientation.cos) : acos(orientation.cos);
|
||||
// Find text origin.
|
||||
pixel_position layout_center = pos + layout.displacement();
|
||||
|
||||
|
@ -140,12 +140,15 @@ bool placement_finder::find_point_placement(pixel_position const& pos)
|
|||
}
|
||||
|
||||
box2d<double> bbox = layout.bounds();
|
||||
bbox.re_center(layout_center.x, layout_center.y);
|
||||
std::cerr << "before recenter -->" << bbox << std::endl;
|
||||
auto box_center_y = bbox.center().y;
|
||||
bbox.re_center(layout_center.x, layout_center.y + box_center_y);
|
||||
|
||||
/* For point placements it is faster to just check the bounding box. */
|
||||
if (collision(bbox, layouts_.text(), false)) return false;
|
||||
// TODO
|
||||
double margin = (text_props_->margin != 0 ? text_props_->margin : text_props_->minimum_distance) * scale_factor_;
|
||||
if (!text_props_->allow_overlap && !detector_.has_placement(bbox, margin, angle)) return false;
|
||||
|
||||
if (layout.num_lines()) bboxes.push_back(std::move(bbox));
|
||||
if (layout.num_lines()) labels.push_back(std::make_tuple(bbox,angle));
|
||||
|
||||
pixel_position layout_offset = layout_center - glyphs->get_base_point();
|
||||
layout_offset.y = -layout_offset.y;
|
||||
|
@ -184,8 +187,9 @@ bool placement_finder::find_point_placement(pixel_position const& pos)
|
|||
|
||||
box2d<double> label_box;
|
||||
bool first = true;
|
||||
for (box2d<double> const& box : bboxes)
|
||||
for (auto const& label : labels)
|
||||
{
|
||||
auto const& box = std::get<0>(label);
|
||||
if (first)
|
||||
{
|
||||
label_box = box;
|
||||
|
@ -195,7 +199,8 @@ bool placement_finder::find_point_placement(pixel_position const& pos)
|
|||
{
|
||||
label_box.expand_to_include(box);
|
||||
}
|
||||
detector_.insert(box, layouts_.text());
|
||||
std::cerr << box << std::endl;
|
||||
detector_.insert(box, layouts_.text(), std::get<1>(label));
|
||||
}
|
||||
// do not render text off the canvas
|
||||
if (extent_.intersects(label_box))
|
||||
|
@ -303,7 +308,7 @@ bool placement_finder::single_line_placement(vertex_cache &pp, text_upright_e or
|
|||
|
||||
pixel_position pos = off_pp.current_position() + cluster_offset;
|
||||
// Center the text on the line
|
||||
double char_height = line.max_char_height();
|
||||
double char_height = line.ymax() - line.ymin(); //line.max_char_height();
|
||||
pos.y = -pos.y - char_height/2.0*rot.cos;
|
||||
pos.x = pos.x + char_height/2.0*rot.sin;
|
||||
|
||||
|
@ -386,7 +391,7 @@ double placement_finder::get_spacing(double path_length, double layout_width) co
|
|||
return path_length / num_labels;
|
||||
}
|
||||
|
||||
bool placement_finder::collision(const box2d<double> &box, const value_unicode_string &repeat_key, bool line_placement) const
|
||||
bool placement_finder::collision(box2d<double> const& box, value_unicode_string const& repeat_key, bool line_placement) const
|
||||
{
|
||||
double margin, repeat_distance;
|
||||
if (line_placement)
|
||||
|
@ -410,6 +415,7 @@ bool placement_finder::collision(const box2d<double> &box, const value_unicode_s
|
|||
(repeat_key.length() > 0 && !detector_.has_placement(box, margin, repeat_key, repeat_distance))));
|
||||
}
|
||||
|
||||
|
||||
void placement_finder::set_marker(marker_info_ptr m, box2d<double> box, bool marker_unlocked, pixel_position const& marker_displacement)
|
||||
{
|
||||
marker_ = m;
|
||||
|
|
|
@ -37,20 +37,15 @@ namespace mapnik
|
|||
{
|
||||
|
||||
// Output is centered around (0,0)
|
||||
static void rotated_box2d(box2d<double> & box, rotation const& rot, pixel_position const& center, double width, double height)
|
||||
void rotated_box2d(box2d<double> & box, rotation const& rot, pixel_position const& center, double width, double height, double vertical_displacement)
|
||||
{
|
||||
double half_width, half_height;
|
||||
if (rot.sin == 0 && rot.cos == 1.)
|
||||
{
|
||||
half_width = width / 2.;
|
||||
half_height = height / 2.;
|
||||
}
|
||||
else
|
||||
{
|
||||
half_width = (width * rot.cos + height * rot.sin) /2.;
|
||||
half_height = (width * rot.sin + height * rot.cos) /2.;
|
||||
}
|
||||
box.init(center.x - half_width, center.y - half_height, center.x + half_width, center.y + half_height);
|
||||
half_width = 0.5 * width ;
|
||||
half_height = 0.5 * height;
|
||||
box.init(center.x - half_width,
|
||||
center.y - half_height - vertical_displacement,
|
||||
center.x + half_width,
|
||||
center.y + half_height - vertical_displacement);
|
||||
}
|
||||
|
||||
pixel_position evaluate_displacement(double dx, double dy, directions_e dir)
|
||||
|
@ -107,6 +102,8 @@ text_layout::text_layout(face_manager_freetype & font_manager,
|
|||
width_map_(),
|
||||
width_(0.0),
|
||||
height_(0.0),
|
||||
ymin_(0.0),
|
||||
ymax_(0.0),
|
||||
glyphs_count_(0),
|
||||
lines_(),
|
||||
layout_properties_(layout_defaults),
|
||||
|
@ -201,7 +198,7 @@ void text_layout::layout()
|
|||
displacement_ = scale_factor_ * displacement_ + alignment_offset();
|
||||
if (rotate_displacement_) displacement_ = displacement_.rotate(!orientation_);
|
||||
// Find layout bounds, expanded for rotation
|
||||
rotated_box2d(bounds_, orientation_, displacement_, width_, height_);
|
||||
rotated_box2d(bounds_, orientation_, displacement_, width_, height_, ymin_);
|
||||
}
|
||||
|
||||
// In the Unicode string characters are always stored in logical order.
|
||||
|
@ -414,6 +411,8 @@ void text_layout::add_line(text_line && line)
|
|||
line.set_first_line(true);
|
||||
}
|
||||
height_ += line.height();
|
||||
ymin_ = std::min(ymin_, line.ymin());
|
||||
ymax_ = std::max(ymax_, line.ymax());
|
||||
glyphs_count_ += line.size();
|
||||
width_ = std::max(width_, line.width());
|
||||
lines_.emplace_back(std::move(line));
|
||||
|
|
|
@ -29,7 +29,8 @@ namespace mapnik {
|
|||
text_line::text_line(unsigned first_char, unsigned last_char)
|
||||
: glyphs_(),
|
||||
line_height_(0.0),
|
||||
max_char_height_(0.0),
|
||||
ymin_(0.0),
|
||||
ymax_(0.0),
|
||||
width_(0.0),
|
||||
glyphs_width_(0.0),
|
||||
first_char_(first_char),
|
||||
|
@ -41,7 +42,8 @@ text_line::text_line(unsigned first_char, unsigned last_char)
|
|||
text_line::text_line(text_line && rhs)
|
||||
: glyphs_(std::move(rhs.glyphs_)),
|
||||
line_height_(std::move(rhs.line_height_)),
|
||||
max_char_height_(std::move(rhs.max_char_height_)),
|
||||
ymin_(std::move(rhs.ymin_)),
|
||||
ymax_(std::move(rhs.ymax_)),
|
||||
width_(std::move(rhs.width_)),
|
||||
glyphs_width_(std::move(rhs.glyphs_width_)),
|
||||
first_char_(std::move(rhs.first_char_)),
|
||||
|
@ -86,15 +88,10 @@ text_line::const_iterator text_line::end() const
|
|||
|
||||
double text_line::height() const
|
||||
{
|
||||
if (first_line_) return max_char_height_;
|
||||
if (first_line_) return ymax_ - ymin_;
|
||||
return line_height_;
|
||||
}
|
||||
|
||||
void text_line::update_max_char_height(double max_char_height)
|
||||
{
|
||||
max_char_height_ = std::max(max_char_height_, max_char_height);
|
||||
}
|
||||
|
||||
void text_line::set_first_line(bool first_line)
|
||||
{
|
||||
first_line_ = first_line;
|
||||
|
|
|
@ -38,7 +38,7 @@ mapnik::datasource_ptr get_csv_ds(std::string const &file_name, bool strict = tr
|
|||
params["strict"] = mapnik::value_bool(strict);
|
||||
auto ds = mapnik::datasource_cache::instance().create(params);
|
||||
// require a non-null pointer returned
|
||||
REQUIRE(bool(ds));
|
||||
REQUIRE(ds != nullptr);
|
||||
return ds;
|
||||
}
|
||||
|
||||
|
@ -295,7 +295,7 @@ TEST_CASE("csv") {
|
|||
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}
|
||||
|
@ -318,7 +318,7 @@ TEST_CASE("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);
|
||||
|
@ -533,7 +533,7 @@ TEST_CASE("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);
|
||||
|
|
Loading…
Reference in a new issue