Patch from David Eastcott :

1.  Modified Text Symbolizer

  a) corrected line fragment centering (for 2nd and subsequent lines, when line breaks occur).

  b) adjusted vertical alignment calculation so that:
      i)      middle -> has the center of the text line(s) at the point origin
      ii)     bottom -> has the text line(s) below the point origin
      iii)    top -> has the text line(s) above the point origin

  c) added new text_symbolizer attribute: 'wrap_before', value range: true/false, default == false

     allows line breaks at first wrap_char before wrap_width as an alternative to the original
     which was to create the line break at the first wrap_char after wrap_width

  d) added new text_symbolizer attribute: 'horizontal_alignment', value range: left/middle/right, default == middle
      i)      left -> has all text line(s) to left of the point origin
      ii)     middle -> has all text line(s) centered on the the point origin
      iii)    right -> has all text line(s) to the right of the point origin

      NOTE:   dx, dy position adjustments are applied after alignments and before Justify.

  e) added new text_symbolizer attribute: 'justify_alignment', value range: left/middle/right, default == middle
      i)      left -> after alignments, has all text line(s) are left justified (left to right reading)
      ii)     middle -> after alignments, has all text line(s) center justified
      iii)    right -> after alignments, has all text line(s) right justified (right to left reading)

  f) added new text_symbolizer attribute:  'opacity', value range: 0.0 thru 1.0; 1.0 == fully opaque

  g) modified positioning to compensate for both line_spacing and character_spacing, to ensure proper
     centering of the text envelope.  Also ensure that centering occurs correctly even if no wrapping
     occurs.  Line spacing is uniform and consistent and compensates for errors between text_size and
     the actual size (ci.height is inconsistent, depending on case and character); fixes issue with
     multi-line text where some lines have a slight gap and others are compressed together.


2.  Modified shield_symbolizer

  a) added the attributes:
      i)      allow_overlap
      ii)     vertical_alignment
      iii)    horizontal_alignment
      iv)     justify_alignment
      v)      wrap_width
      vi)     wrap_character
      vii)    wrap_before
      viii)   text_convert
      ix)     line_spacing
      x)      character_spacing
      xi)     opacity

  b)  added new shield_symbolizer attribute: 'unlock_image', value range: true/false, default == false
      i)  false == image and text placement behaviour same as before
      ii) true == image placement independant of text, image is always centered at geometry point, text placed per attributes,
                  dx/dy only affect text.

      Allows user to create point markers with text, but both the text and image rendering collision detection are done
      as a pair (they come and go together - solves problem if using point_symbolizer and text_symbolizers where one or the
      other are omitted due to overlaps, but not both)

  c)  extended choices for the attribute 'placement' to include vertex; effect is limited to the shield_symbolizer

      Allows an attempted placement at every vertex available, gives additional shield placement volume when using line geometry

  d)  ensured that the text placement was not updating the detector unless a shield image was actually placed.

  e)  added new shield_symbolizer attribute: 'no_text', value range: true/false, default = false

      When set true, the text for the feature is ignored ('space' subsituted) so that pure graphic symbols can be used
      and no text is rendered over top of them.
This commit is contained in:
Artem Pavlenko 2009-10-19 13:52:53 +00:00
parent df7bad25f9
commit a3a5859466
11 changed files with 1106 additions and 726 deletions

View file

@ -1,5 +1,5 @@
/*****************************************************************************
*
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2006 Artem Pavlenko
@ -60,7 +60,7 @@ extern "C"
namespace mapnik
{
class font_face;
typedef boost::shared_ptr<font_face> face_ptr;
class MAPNIK_DECL font_glyph : private boost::noncopyable
@ -120,10 +120,10 @@ namespace mapnik
{
if (! FT_Set_Pixel_Sizes( face_, 0, size ))
return true;
return false;
}
~font_face()
{
#ifdef MAPNIK_DEBUG
@ -173,32 +173,32 @@ namespace mapnik
FT_Matrix matrix;
FT_Vector pen;
FT_Error error;
pen.x = 0;
pen.y = 0;
FT_BBox glyph_bbox;
FT_BBox glyph_bbox;
FT_Glyph image;
glyph_ptr glyph = get_glyph(c);
FT_Face face = glyph->get_face()->get_face();
matrix.xx = (FT_Fixed)( 1 * 0x10000L );
matrix.xy = (FT_Fixed)( 0 * 0x10000L );
matrix.yx = (FT_Fixed)( 0 * 0x10000L );
matrix.xx = (FT_Fixed)( 1 * 0x10000L );
matrix.xy = (FT_Fixed)( 0 * 0x10000L );
matrix.yx = (FT_Fixed)( 0 * 0x10000L );
matrix.yy = (FT_Fixed)( 1 * 0x10000L );
FT_Set_Transform(face, &matrix, &pen);
error = FT_Load_Glyph (face, glyph->get_index(), FT_LOAD_NO_HINTING);
error = FT_Load_Glyph (face, glyph->get_index(), FT_LOAD_NO_HINTING);
if ( error )
return dimension_t(0, 0);
error = FT_Get_Glyph(face->glyph, &image);
if ( error )
return dimension_t(0, 0);
FT_Glyph_Get_CBox(image, ft_glyph_bbox_pixels, &glyph_bbox);
FT_Glyph_Get_CBox(image, ft_glyph_bbox_pixels, &glyph_bbox);
FT_Done_Glyph(image);
unsigned tempx = face->glyph->advance.x >> 6;
@ -208,7 +208,7 @@ namespace mapnik
return dimension_t(tempx, tempy);
}
void get_string_info(string_info & info)
{
unsigned width = 0;
@ -217,17 +217,17 @@ namespace mapnik
UnicodeString const& ustr = info.get_string();
const UChar * text = ustr.getBuffer();
UBiDi * bidi = ubidi_openSized(ustr.length(),0,&err);
if (U_SUCCESS(err))
{
ubidi_setPara(bidi,text,ustr.length(), UBIDI_DEFAULT_LTR,0,&err);
if (U_SUCCESS(err))
{
int32_t count = ubidi_countRuns(bidi,&err);
int32_t logicalStart;
int32_t length;
for (int32_t i=0; i< count;++i)
{
if (UBIDI_LTR == ubidi_getVisualRun(bidi,i,&logicalStart,&length))
@ -238,13 +238,13 @@ namespace mapnik
info.add_info(ch, char_dim.first, char_dim.second);
width += char_dim.first;
height = char_dim.second > height ? char_dim.second : height;
} while (--length > 0);
}
else
{
logicalStart += length;
int32_t j=0,i=length;
UnicodeString arabic;
UChar * buf = arabic.getBuffer(length);
@ -252,7 +252,7 @@ namespace mapnik
UChar ch = text[--logicalStart];
buf[j++] = ch;
} while (--i > 0);
arabic.releaseBuffer(length);
if ( *arabic.getBuffer() >= 0x0600 && *arabic.getBuffer() <= 0x06ff)
{
@ -261,11 +261,11 @@ namespace mapnik
U_SHAPE_LETTERS_SHAPE|U_SHAPE_LENGTH_FIXED_SPACES_NEAR|
U_SHAPE_TEXT_DIRECTION_VISUAL_LTR
,&err);
shaped.releaseBuffer(arabic.length());
if (U_SUCCESS(err))
{
{
for (int j=0;j<shaped.length();++j)
{
dimension_t char_dim = character_dimensions(shaped[j]);
@ -289,7 +289,7 @@ namespace mapnik
}
ubidi_close(bidi);
}
info.set_dimensions(width, height);
}
@ -303,7 +303,7 @@ namespace mapnik
private:
std::vector<face_ptr> faces_;
};
typedef boost::shared_ptr<font_face_set> face_set_ptr;
class MAPNIK_DECL freetype_engine // : public mapnik::singleton<freetype_engine,mapnik::CreateStatic>,
@ -320,18 +320,18 @@ namespace mapnik
FT_Library library_;
static boost::mutex mutex_;
static std::map<std::string,std::string> name2file_;
};
};
template <typename T>
class MAPNIK_DECL face_manager : private boost::noncopyable
{
typedef T font_engine_type;
typedef std::map<std::string,face_ptr> faces;
public:
face_manager(T & engine)
: engine_(engine) {}
face_ptr get_face(std::string const& name)
{
typename faces::iterator itr;
@ -395,44 +395,50 @@ namespace mapnik
text_renderer (pixmap_type & pixmap, face_set_ptr faces)
: pixmap_(pixmap),
faces_(faces),
fill_(0,0,0),
fill_(0,0,0),
halo_fill_(255,255,255),
halo_radius_(0) {}
halo_radius_(0),
opacity_(1.0) {}
void set_pixel_size(unsigned size)
{
faces_->set_pixel_sizes(size);
}
void set_fill(mapnik::color const& fill)
{
fill_=fill;
}
void set_halo_fill(mapnik::color const& halo)
{
halo_fill_=halo;
}
void set_halo_radius( int radius=1)
{
halo_radius_=radius;
}
void set_opacity( double opacity=1.0)
{
opacity_=opacity;
}
Envelope<double> prepare_glyphs(text_path *path)
{
//clear glyphs
glyphs_.clear();
FT_Matrix matrix;
FT_Vector pen;
FT_Error error;
FT_BBox bbox;
FT_BBox bbox;
bbox.xMin = bbox.yMin = 32000; // Initialize these so we can tell if we
bbox.xMax = bbox.yMax = -32000; // properly grew the bbox later
for (int i = 0; i < path->num_nodes(); i++)
for (int i = 0; i < path->num_nodes(); i++)
{
int c;
double x, y, angle;
@ -441,27 +447,27 @@ namespace mapnik
#ifdef MAPNIK_DEBUG
// TODO Enable when we have support for setting verbosity
//std::clog << "prepare_glyphs: " << c << "," << x <<
//std::clog << "prepare_glyphs: " << c << "," << x <<
// "," << y << "," << angle << std::endl;
#endif
FT_BBox glyph_bbox;
FT_BBox glyph_bbox;
FT_Glyph image;
pen.x = int(x * 64);
pen.y = int(y * 64);
glyph_ptr glyph = faces_->get_glyph(unsigned(c));
FT_Face face = glyph->get_face()->get_face();
matrix.xx = (FT_Fixed)( cos( angle ) * 0x10000L );
matrix.xy = (FT_Fixed)(-sin( angle ) * 0x10000L );
matrix.yx = (FT_Fixed)( sin( angle ) * 0x10000L );
matrix.xx = (FT_Fixed)( cos( angle ) * 0x10000L );
matrix.xy = (FT_Fixed)(-sin( angle ) * 0x10000L );
matrix.yx = (FT_Fixed)( sin( angle ) * 0x10000L );
matrix.yy = (FT_Fixed)( cos( angle ) * 0x10000L );
FT_Set_Transform(face, &matrix, &pen);
error = FT_Load_Glyph(face, glyph->get_index(), FT_LOAD_NO_HINTING);
error = FT_Load_Glyph(face, glyph->get_index(), FT_LOAD_NO_HINTING);
if ( error )
continue;
@ -469,23 +475,23 @@ namespace mapnik
if ( error )
continue;
FT_Glyph_Get_CBox(image,ft_glyph_bbox_pixels, &glyph_bbox);
if (glyph_bbox.xMin < bbox.xMin)
bbox.xMin = glyph_bbox.xMin;
if (glyph_bbox.yMin < bbox.yMin)
bbox.yMin = glyph_bbox.yMin;
if (glyph_bbox.xMax > bbox.xMax)
bbox.xMax = glyph_bbox.xMax;
if (glyph_bbox.yMax > bbox.yMax)
FT_Glyph_Get_CBox(image,ft_glyph_bbox_pixels, &glyph_bbox);
if (glyph_bbox.xMin < bbox.xMin)
bbox.xMin = glyph_bbox.xMin;
if (glyph_bbox.yMin < bbox.yMin)
bbox.yMin = glyph_bbox.yMin;
if (glyph_bbox.xMax > bbox.xMax)
bbox.xMax = glyph_bbox.xMax;
if (glyph_bbox.yMax > bbox.yMax)
bbox.yMax = glyph_bbox.yMax;
// Check if we properly grew the bbox
if ( bbox.xMin > bbox.xMax )
{
bbox.xMin = 0;
bbox.yMin = 0;
bbox.xMax = 0;
bbox.yMax = 0;
bbox.xMin = 0;
bbox.yMin = 0;
bbox.xMax = 0;
bbox.yMax = 0;
}
// take ownership of the glyph
@ -494,23 +500,23 @@ namespace mapnik
return Envelope<double>(bbox.xMin, bbox.yMin, bbox.xMax, bbox.yMax);
}
void render(double x0, double y0)
{
FT_Error error;
FT_Vector start;
unsigned height = pixmap_.height();
start.x = static_cast<FT_Pos>(x0 * (1 << 6));
start.x = static_cast<FT_Pos>(x0 * (1 << 6));
start.y = static_cast<FT_Pos>((height - y0) * (1 << 6));
// now render transformed glyphs
typename glyphs_t::iterator pos;
//make sure we've got reasonable values.
if (halo_radius_ > 0 && halo_radius_ < 256)
{
//render halo
//render halo
for ( pos = glyphs_.begin(); pos != glyphs_.end();++pos)
{
FT_Glyph_Transform(pos->image,0,&start);
@ -520,11 +526,11 @@ namespace mapnik
{
FT_BitmapGlyph bit = (FT_BitmapGlyph)pos->image;
render_halo(&bit->bitmap, halo_fill_.rgba(),
render_halo(&bit->bitmap, halo_fill_.rgba(),
bit->left,
height - bit->top,halo_radius_);
}
}
}
}
//render actual text
for ( pos = glyphs_.begin(); pos != glyphs_.end();++pos)
@ -537,15 +543,15 @@ namespace mapnik
{
FT_BitmapGlyph bit = (FT_BitmapGlyph)pos->image;
render_bitmap(&bit->bitmap, fill_.rgba(),
render_bitmap(&bit->bitmap, fill_.rgba(),
bit->left,
height - bit->top);
}
}
}
}
private:
void render_halo(FT_Bitmap *bitmap,unsigned rgba,int x,int y,int radius)
{
int x_max=x+bitmap->width;
@ -561,7 +567,7 @@ namespace mapnik
{
for (int n=-halo_radius_; n <=halo_radius_; ++n)
for (int m=-halo_radius_;m <= halo_radius_; ++m)
pixmap_.blendPixel(i+m,j+n,rgba,gray);
pixmap_.blendPixel2(i+m,j+n,rgba,gray,opacity_);
}
}
}
@ -580,12 +586,12 @@ namespace mapnik
int gray=bitmap->buffer[q*bitmap->width+p];
if (gray)
{
pixmap_.blendPixel(i,j,rgba,gray);
pixmap_.blendPixel2(i,j,rgba,gray,opacity_);
}
}
}
}
pixmap_type & pixmap_;
face_set_ptr faces_;
mapnik::color fill_;
@ -594,7 +600,8 @@ namespace mapnik
unsigned text_ratio_;
unsigned wrap_width_;
glyphs_t glyphs_;
};
double opacity_;
};
}
#endif // FONT_ENGINE_FREETYPE_HPP

View file

@ -1,5 +1,5 @@
/*****************************************************************************
*
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2006 Artem Pavlenko
@ -42,7 +42,7 @@
namespace mapnik
{
struct Multiply
{
inline static void mergeRGB(unsigned const &r0, unsigned const &g0, unsigned const &b0,
@ -132,7 +132,7 @@ namespace mapnik
if (b1>255) b1=255;
}
};
class MAPNIK_DECL Image32
{
private:
@ -148,31 +148,31 @@ namespace mapnik
#endif
~Image32();
void setBackground(color const& background);
const color& getBackground() const;
const color& getBackground() const;
const ImageData32& data() const;
inline ImageData32& data()
inline ImageData32& data()
{
return data_;
}
inline const unsigned char* raw_data() const
{
return data_.getBytes();
}
inline unsigned char* raw_data()
{
return data_.getBytes();
}
inline image_view<ImageData32> get_view(unsigned x,unsigned y, unsigned w,unsigned h)
{
return image_view<ImageData32>(x,y,w,h,data_);
}
private:
inline bool checkBounds(unsigned x, unsigned y) const
{
return (x < width_ && y < height_);
@ -187,24 +187,29 @@ namespace mapnik
}
}
inline void blendPixel(int x,int y,unsigned int rgba1,int t)
{
blendPixel2(x,y,rgba1,t,1.0); // do not change opacity
}
inline void blendPixel2(int x,int y,unsigned int rgba1,int t,double opacity)
{
if (checkBounds(x,y))
{
unsigned rgba0 = data_(x,y);
unsigned a1 = (rgba1 >> 24) & 0xff; //t;
unsigned rgba0 = data_(x,y);
unsigned a1 = (int)(((rgba1 >> 24) & 0xff) * opacity) & 0xff; // adjust for desired opacity
a1 = (t*a1) / 255;
if (a1 == 0) return;
unsigned r1 = rgba1 & 0xff;
unsigned g1 = (rgba1 >> 8 ) & 0xff;
unsigned b1 = (rgba1 >> 16) & 0xff;
unsigned a0 = (rgba0 >> 24) & 0xff;
unsigned r0 = (rgba0 & 0xff) * a0;
unsigned g0 = ((rgba0 >> 8 ) & 0xff) * a0;
unsigned b0 = ((rgba0 >> 16) & 0xff) * a0;
a0 = ((a1 + a0) << 8) - a0*a1;
r0 = ((((r1 << 8) - r0) * a1 + (r0 << 8)) / a0);
g0 = ((((g1 << 8) - g0) * a1 + (g0 << 8)) / a0);
b0 = ((((b1 << 8) - b0) * a1 + (b0 << 8)) / a0);
@ -217,7 +222,7 @@ namespace mapnik
{
return width_;
}
inline unsigned height() const
{
return height_;
@ -225,36 +230,36 @@ namespace mapnik
inline void set_rectangle(int x0,int y0,ImageData32 const& data)
{
Envelope<int> ext0(0,0,width_,height_);
Envelope<int> ext0(0,0,width_,height_);
Envelope<int> ext1(x0,y0,x0+data.width(),y0+data.height());
if (ext0.intersects(ext1))
{
{
Envelope<int> box = ext0.intersect(ext1);
for (int y = box.miny(); y < box.maxy(); ++y)
{
unsigned int* row_to = data_.getRow(y);
unsigned int const * row_from = data.getRow(y-y0);
for (int x = box.minx(); x < box.maxx(); ++x)
{
if (row_from[x-x0] & 0xff000000)
{
row_to[x] = row_from[x-x0];
}
}
}
}
}
}
}
inline void set_rectangle_alpha(int x0,int y0,const ImageData32& data)
{
Envelope<int> ext0(0,0,width_,height_);
Envelope<int> ext0(0,0,width_,height_);
Envelope<int> ext1(x0,y0,x0 + data.width(),y0 + data.height());
if (ext0.intersects(ext1))
{
Envelope<int> box = ext0.intersect(ext1);
{
Envelope<int> box = ext0.intersect(ext1);
for (int y = box.miny(); y < box.maxy(); ++y)
{
unsigned int* row_to = data_.getRow(y);
@ -263,20 +268,20 @@ namespace mapnik
{
unsigned rgba0 = row_to[x];
unsigned rgba1 = row_from[x-x0];
unsigned a1 = (rgba1 >> 24) & 0xff;
if (a1 == 0) continue;
unsigned r1 = rgba1 & 0xff;
unsigned g1 = (rgba1 >> 8 ) & 0xff;
unsigned b1 = (rgba1 >> 16) & 0xff;
unsigned a0 = (rgba0 >> 24) & 0xff;
unsigned r0 = (rgba0 & 0xff) * a0;
unsigned g0 = ((rgba0 >> 8 ) & 0xff) * a0;
unsigned b0 = ((rgba0 >> 16) & 0xff) * a0;
a0 = ((a1 + a0) << 8) - a0*a1;
r0 = ((((r1 << 8) - r0) * a1 + (r0 << 8)) / a0);
g0 = ((((g1 << 8) - g0) * a1 + (g0 << 8)) / a0);
b0 = ((((b1 << 8) - b0) * a1 + (b0 << 8)) / a0);
@ -286,15 +291,15 @@ namespace mapnik
}
}
}
inline void set_rectangle_alpha2(ImageData32 const& data, unsigned x0, unsigned y0, float opacity)
{
Envelope<int> ext0(0,0,width_,height_);
Envelope<int> ext0(0,0,width_,height_);
Envelope<int> ext1(x0,y0,x0 + data.width(),y0 + data.height());
if (ext0.intersects(ext1))
{
Envelope<int> box = ext0.intersect(ext1);
{
Envelope<int> box = ext0.intersect(ext1);
for (int y = box.miny(); y < box.maxy(); ++y)
{
unsigned int* row_to = data_.getRow(y);
@ -308,24 +313,24 @@ namespace mapnik
unsigned r1 = rgba1 & 0xff;
unsigned g1 = (rgba1 >> 8 ) & 0xff;
unsigned b1 = (rgba1 >> 16) & 0xff;
unsigned a0 = (rgba0 >> 24) & 0xff;
unsigned r0 = rgba0 & 0xff ;
unsigned g0 = (rgba0 >> 8 ) & 0xff;
unsigned b0 = (rgba0 >> 16) & 0xff;
unsigned a = (a1 * 255 + (255 - a1) * a0 + 127)/255;
r0 = (r1*a1 + (((255 - a1) * a0 + 127)/255) * r0 + 127)/a;
g0 = (g1*a1 + (((255 - a1) * a0 + 127)/255) * g0 + 127)/a;
b0 = (b1*a1 + (((255 - a1) * a0 + 127)/255) * b0 + 127)/a;
row_to[x] = (a << 24)| (b0 << 16) | (g0 << 8) | (r0) ;
}
}
}
}
template <typename MergeMethod>
inline void merge_rectangle(ImageData32 const& data, unsigned x0, unsigned y0, float opacity)
{

View file

@ -1,5 +1,5 @@
/*****************************************************************************
*
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2006 Artem Pavlenko
@ -38,26 +38,27 @@
namespace mapnik
{
typedef text_path placement_element;
struct placement : boost::noncopyable
{
{
placement(string_info & info_, shield_symbolizer const& sym, bool has_dimensions_= false);
placement(string_info & info_, text_symbolizer const& sym);
~placement();
string_info & info;
position displacement_;
label_placement_e label_placement;
std::queue< Envelope<double> > envelopes;
//output
boost::ptr_vector<placement_element> placements;
int wrap_width;
bool wrap_before; // wraps text at wrap_char immediately before current word
unsigned char wrap_char;
int text_ratio;
@ -71,34 +72,35 @@ namespace mapnik
bool has_dimensions;
bool allow_overlap;
std::pair<double, double> dimensions;
int text_size;
};
template <typename DetectorT>
class placement_finder : boost::noncopyable
{
public:
placement_finder(DetectorT & detector);
//Try place a single label at the given point
void find_point_placement(placement & p, double pos_x, double pos_y, vertical_alignment_e = MIDDLE, unsigned line_spacing=0, unsigned character_spacing=0);
void find_point_placement(placement & p, double pos_x, double pos_y, vertical_alignment_e = MIDDLE, unsigned line_spacing=0, unsigned character_spacing=0, horizontal_alignment_e = H_MIDDLE, justify_alignment_e = J_MIDDLE);
//Iterate over the given path, placing point labels with respect to label_spacing
template <typename T>
void find_point_placements(placement & p, T & path);
//Iterate over the given path, placing line-following labels with respect to label_spacing
template <typename T>
void find_line_placements(placement & p, T & path);
void update_detector(placement & p);
void clear();
private:
///Helpers for find_line_placement
///Returns a possible placement on the given line, does not test for collisions
//index: index of the node the current line ends on
//distance: distance along the given index that the placement should start at, this includes the offset,
@ -107,33 +109,33 @@ namespace mapnik
// otherwise it will autodetect the orientation.
// If >= 50% of the characters end up upside down, it will be retried the other way.
// RETURN: 1/-1 depending which way up the string ends up being.
std::auto_ptr<placement_element> get_placement_offset(placement & p,
const std::vector<vertex2d> & path_positions,
const std::vector<double> & path_distances,
std::auto_ptr<placement_element> get_placement_offset(placement & p,
const std::vector<vertex2d> & path_positions,
const std::vector<double> & path_distances,
int & orientation, unsigned index, double distance);
///Tests wether the given placement_element be placed without a collision
// Returns true if it can
// NOTE: This edits p.envelopes so it can be used afterwards (you must clear it otherwise)
bool test_placement(placement & p, const std::auto_ptr<placement_element> & current_placement, const int & orientation);
///Does a line-circle intersect calculation
// NOTE: Follow the strict pre conditions
// Pre Conditions: x1,y1 is within radius distance of cx,cy. x2,y2 is outside radius distance of cx,cy
// This means there is exactly one intersect point
// Result is returned in ix, iy
void find_line_circle_intersection(
const double &cx, const double &cy, const double &radius,
const double &x1, const double &y1, const double &x2, const double &y2,
const double &cx, const double &cy, const double &radius,
const double &x1, const double &y1, const double &x2, const double &y2,
double &ix, double &iy);
///General Internals
DetectorT & detector_;
Envelope<double> const& dimensions_;
};
};
}
#endif

View file

@ -1,5 +1,5 @@
/*****************************************************************************
*
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2006 Artem Pavlenko
@ -25,30 +25,40 @@
#ifndef SHIELD_SYMBOLIZER_HPP
#define SHIELD_SYMBOLIZER_HPP
#include <mapnik/graphics.hpp>
#include <mapnik/graphics.hpp>
#include <mapnik/text_symbolizer.hpp>
#include <mapnik/symbolizer.hpp>
#include <boost/shared_ptr.hpp>
namespace mapnik
{
namespace mapnik
{
struct MAPNIK_DECL shield_symbolizer : public text_symbolizer,
public symbolizer_with_image
{
{
shield_symbolizer(std::string const& name,
std::string const& face_name,
unsigned size,
color const& fill,
color const& fill,
std::string const& file,
std::string const& type,
unsigned width,unsigned height);
shield_symbolizer(std::string const& name,
unsigned size,
color const& fill,
color const& fill,
std::string const& file,
std::string const& type,
unsigned width,unsigned height);
bool get_unlock_image() const; // image is not locked to the text placement
void set_unlock_image(bool unlock_image);
bool get_no_text() const; // do no render text
void set_no_text(bool unlock_image);
private:
bool unlock_image_;
bool no_text_;
};
}

View file

@ -1,5 +1,5 @@
/*****************************************************************************
*
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2006 Artem Pavlenko
@ -40,11 +40,12 @@ namespace mapnik
enum label_placement_enum {
POINT_PLACEMENT,
LINE_PLACEMENT,
VERTEX_PLACEMENT,
label_placement_enum_MAX
};
DEFINE_ENUM( label_placement_e, label_placement_enum );
enum vertical_alignment
{
TOP = 0,
@ -55,6 +56,26 @@ namespace mapnik
DEFINE_ENUM( vertical_alignment_e, vertical_alignment );
enum horizontal_alignment
{
H_LEFT = 0,
H_MIDDLE,
H_RIGHT,
horizontal_alignment_MAX
};
DEFINE_ENUM( horizontal_alignment_e, horizontal_alignment );
enum justify_alignment
{
J_LEFT = 0,
J_MIDDLE,
J_RIGHT,
justify_alignment_MAX
};
DEFINE_ENUM( justify_alignment_e, justify_alignment );
enum text_convert
{
NONE = 0,
@ -66,12 +87,12 @@ namespace mapnik
DEFINE_ENUM( text_convert_e, text_convert );
typedef boost::tuple<double,double> position;
struct MAPNIK_DECL text_symbolizer
{
text_symbolizer(std::string const& name,std::string const& face_name,
unsigned size, color const& fill);
text_symbolizer(std::string const& name, unsigned size, color const& fill);
{
text_symbolizer(std::string const& name,std::string const& face_name,
unsigned size, color const& fill);
text_symbolizer(std::string const& name, unsigned size, color const& fill);
text_symbolizer(text_symbolizer const& rhs);
text_symbolizer& operator=(text_symbolizer const& rhs);
std::string const& get_name() const;
@ -114,8 +135,8 @@ namespace mapnik
label_placement_e get_label_placement() const;
void set_vertical_alignment(vertical_alignment_e valign);
vertical_alignment_e get_vertical_alignment() const;
void set_anchor(double x, double y);
position const& get_anchor() const;
void set_anchor(double x, double y);
position const& get_anchor() const;
void set_displacement(double x, double y);
position const& get_displacement() const;
void set_avoid_edges(bool avoid);
@ -124,6 +145,15 @@ namespace mapnik
double get_minimum_distance() const;
void set_allow_overlap(bool overlap);
bool get_allow_overlap() const;
void set_opacity(double opacity);
double get_opacity() const;
bool get_wrap_before() const; // wrap text at wrap_char immediately before current work
void set_wrap_before(bool wrap_before);
void set_horizontal_alignment(horizontal_alignment_e valign);
horizontal_alignment_e get_horizontal_alignment() const;
void set_justify_alignment(justify_alignment_e valign);
justify_alignment_e get_justify_alignment() const;
private:
std::string name_;
std::string face_name_;
@ -149,6 +179,10 @@ namespace mapnik
bool avoid_edges_;
double minimum_distance_;
bool overlap_;
double opacity_;
bool wrap_before_;
horizontal_alignment_e halign_;
justify_alignment_e jalign_;
};
}

View file

@ -1,5 +1,5 @@
/*****************************************************************************
*
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2006 Artem Pavlenko
@ -74,14 +74,14 @@
#include <iostream>
#endif
namespace mapnik
namespace mapnik
{
class pattern_source : private boost::noncopyable
{
public:
pattern_source(ImageData32 const& pattern)
: pattern_(pattern) {}
unsigned int width() const
{
return pattern_.width();
@ -93,17 +93,17 @@ namespace mapnik
agg::rgba8 pixel(int x, int y) const
{
unsigned c = pattern_(x,y);
return agg::rgba8(c & 0xff,
(c >> 8) & 0xff,
return agg::rgba8(c & 0xff,
(c >> 8) & 0xff,
(c >> 16) & 0xff,
(c >> 24) & 0xff);
}
private:
ImageData32 const& pattern_;
};
struct rasterizer : agg::rasterizer_scanline_aa<>, boost::noncopyable {};
template <typename T>
agg_renderer<T>::agg_renderer(Map const& m, T & pixmap, unsigned offset_x, unsigned offset_y)
: feature_style_processor<agg_renderer>(m),
@ -122,7 +122,7 @@ namespace mapnik
std::clog << "scale=" << m.scale() << "\n";
#endif
}
template <typename T>
agg_renderer<T>::~agg_renderer() {}
@ -130,7 +130,7 @@ namespace mapnik
void agg_renderer<T>::start_map_processing(Map const& map)
{
#ifdef MAPNIK_DEBUG
std::clog << "start map processing bbox="
std::clog << "start map processing bbox="
<< map.getCurrentExtent() << "\n";
#endif
ras_ptr->clip_box(0,0,width_,height_);
@ -143,20 +143,20 @@ namespace mapnik
std::clog << "end map processing\n";
#endif
}
template <typename T>
void agg_renderer<T>::start_layer_processing(Layer const& lay)
{
#ifdef MAPNIK_DEBUG
std::clog << "start layer processing : " << lay.name() << "\n";
std::clog << "datasource = " << lay.datasource().get() << "\n";
#endif
#endif
if (lay.clear_label_cache())
{
detector_.clear();
}
}
template <typename T>
void agg_renderer<T>::end_layer_processing(Layer const&)
{
@ -164,35 +164,35 @@ namespace mapnik
std::clog << "end layer processing\n";
#endif
}
template <typename T>
template <typename T>
void agg_renderer<T>::process(polygon_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
typedef coord_transform2<CoordTransform,geometry2d> path_type;
typedef agg::renderer_base<agg::pixfmt_rgba32_plain> ren_base;
typedef agg::renderer_base<agg::pixfmt_rgba32_plain> ren_base;
typedef agg::renderer_scanline_aa_solid<ren_base> renderer;
color const& fill_ = sym.get_fill();
agg::scanline_u8 sl;
agg::rendering_buffer buf(pixmap_.raw_data(),width_,height_, width_ * 4);
agg::pixfmt_rgba32_plain pixf(buf);
ren_base renb(pixf);
ren_base renb(pixf);
unsigned r=fill_.red();
unsigned g=fill_.green();
unsigned b=fill_.blue();
unsigned a=fill_.alpha();
renderer ren(renb);
ras_ptr->reset();
for (unsigned i=0;i<feature.num_geometries();++i)
{
geometry2d const& geom=feature.get_geometry(i);
if (geom.num_points() > 2)
if (geom.num_points() > 2)
{
path_type path(t_,geom,prj_trans);
ras_ptr->add_path(path);
@ -201,45 +201,45 @@ namespace mapnik
ren.color(agg::rgba8(r, g, b, int(a * sym.get_opacity())));
agg::render_scanlines(*ras_ptr, sl, ren);
}
typedef boost::tuple<double,double,double,double> segment_t;
bool y_order(segment_t const& first,segment_t const& second)
{
double miny0 = std::min(first.get<1>(),first.get<3>());
double miny1 = std::min(second.get<1>(),second.get<3>());
double miny1 = std::min(second.get<1>(),second.get<3>());
return miny0 > miny1;
}
template <typename T>
template <typename T>
void agg_renderer<T>::process(building_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
typedef coord_transform2<CoordTransform,geometry2d> path_type;
typedef coord_transform3<CoordTransform,geometry2d> path_type_roof;
typedef agg::renderer_base<agg::pixfmt_rgba32_plain> ren_base;
typedef agg::renderer_base<agg::pixfmt_rgba32_plain> ren_base;
typedef agg::renderer_scanline_aa_solid<ren_base> renderer;
agg::rendering_buffer buf(pixmap_.raw_data(),width_,height_, width_ * 4);
agg::pixfmt_rgba32_plain pixf(buf);
ren_base renb(pixf);
color const& fill_ = sym.get_fill();
unsigned r=fill_.red();
unsigned g=fill_.green();
unsigned b=fill_.blue();
unsigned a=fill_.alpha();
renderer ren(renb);
renderer ren(renb);
agg::scanline_u8 sl;
ras_ptr->reset();
double height = 0.7071 * sym.height(); // height in meters
for (unsigned i=0;i<feature.num_geometries();++i)
{
geometry2d const& geom = feature.get_geometry(i);
if (geom.num_points() > 2)
{
if (geom.num_points() > 2)
{
boost::scoped_ptr<geometry2d> frame(new line_string_impl);
boost::scoped_ptr<geometry2d> roof(new polygon_impl);
std::deque<segment_t> face_segments;
@ -262,7 +262,7 @@ namespace mapnik
{
face_segments.push_back(segment_t(x0,y0,x,y));
}
x0 = x;
x0 = x;
y0 = y;
}
std::sort(face_segments.begin(),face_segments.end(), y_order);
@ -274,17 +274,17 @@ namespace mapnik
faces->line_to(itr->get<2>(),itr->get<3>());
faces->line_to(itr->get<2>(),itr->get<3>() + height);
faces->line_to(itr->get<0>(),itr->get<1>() + height);
path_type faces_path (t_,*faces,prj_trans);
ras_ptr->add_path(faces_path);
ren.color(agg::rgba8(int(r*0.8), int(g*0.8), int(b*0.8), int(a * sym.get_opacity())));
agg::render_scanlines(*ras_ptr, sl, ren);
ras_ptr->reset();
frame->move_to(itr->get<0>(),itr->get<1>());
frame->line_to(itr->get<0>(),itr->get<1>()+height);
frame->line_to(itr->get<0>(),itr->get<1>()+height);
}
geom.rewind(0);
for (unsigned j=0;j<geom.num_points();++j)
{
@ -300,14 +300,14 @@ namespace mapnik
frame->line_to(x,y+height);
roof->line_to(x,y+height);
}
}
path_type path(t_,*frame,prj_trans);
}
path_type path(t_,*frame,prj_trans);
agg::conv_stroke<path_type> stroke(path);
ras_ptr->add_path(stroke);
ren.color(agg::rgba8(128, 128, 128, int(255 * sym.get_opacity())));
agg::render_scanlines(*ras_ptr, sl, ren);
ras_ptr->reset();
path_type roof_path (t_,*roof,prj_trans);
ras_ptr->add_path(roof_path);
ren.color(agg::rgba8(r, g, b, int(a * sym.get_opacity())));
@ -320,17 +320,17 @@ namespace mapnik
void agg_renderer<T>::process(line_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
typedef agg::renderer_base<agg::pixfmt_rgba32_plain> ren_base;
{
typedef agg::renderer_base<agg::pixfmt_rgba32_plain> ren_base;
typedef coord_transform2<CoordTransform,geometry2d> path_type;
typedef agg::renderer_outline_aa<ren_base> renderer_oaa;
typedef agg::rasterizer_outline_aa<renderer_oaa> rasterizer_outline_aa;
typedef agg::renderer_scanline_aa_solid<ren_base> renderer;
agg::rendering_buffer buf(pixmap_.raw_data(),width_,height_, width_ * 4);
agg::pixfmt_rgba32_plain pixf(buf);
ren_base renb(pixf);
agg::rendering_buffer buf(pixmap_.raw_data(),width_,height_, width_ * 4);
agg::pixfmt_rgba32_plain pixf(buf);
ren_base renb(pixf);
mapnik::stroke const& stroke_ = sym.get_stroke();
color const& col = stroke_.get_color();
unsigned r=col.red();
@ -340,72 +340,72 @@ namespace mapnik
renderer ren(renb);
ras_ptr->reset();
agg::scanline_p8 sl;
for (unsigned i=0;i<feature.num_geometries();++i)
{
geometry2d const& geom = feature.get_geometry(i);
if (geom.num_points() > 1)
{
path_type path(t_,geom,prj_trans);
if (stroke_.has_dash())
{
{
agg::conv_dash<path_type> dash(path);
dash_array const& d = stroke_.get_dash_array();
dash_array::const_iterator itr = d.begin();
dash_array::const_iterator end = d.end();
for (;itr != end;++itr)
{
dash.add_dash(itr->first, itr->second);
dash.add_dash(itr->first, itr->second);
}
agg::conv_stroke<agg::conv_dash<path_type > > stroke(dash);
line_join_e join=stroke_.get_line_join();
if ( join == MITER_JOIN)
stroke.generator().line_join(agg::miter_join);
else if( join == MITER_REVERT_JOIN)
else if( join == MITER_REVERT_JOIN)
stroke.generator().line_join(agg::miter_join);
else if( join == ROUND_JOIN)
else if( join == ROUND_JOIN)
stroke.generator().line_join(agg::round_join);
else
stroke.generator().line_join(agg::bevel_join);
line_cap_e cap=stroke_.get_line_cap();
if (cap == BUTT_CAP)
if (cap == BUTT_CAP)
stroke.generator().line_cap(agg::butt_cap);
else if (cap == SQUARE_CAP)
stroke.generator().line_cap(agg::square_cap);
else
else
stroke.generator().line_cap(agg::round_cap);
stroke.generator().miter_limit(4.0);
stroke.generator().width(stroke_.get_width());
ras_ptr->add_path(stroke);
}
else
else
{
agg::conv_stroke<path_type> stroke(path);
line_join_e join=stroke_.get_line_join();
if ( join == MITER_JOIN)
stroke.generator().line_join(agg::miter_join);
else if( join == MITER_REVERT_JOIN)
else if( join == MITER_REVERT_JOIN)
stroke.generator().line_join(agg::miter_join);
else if( join == ROUND_JOIN)
else if( join == ROUND_JOIN)
stroke.generator().line_join(agg::round_join);
else
stroke.generator().line_join(agg::bevel_join);
line_cap_e cap=stroke_.get_line_cap();
if (cap == BUTT_CAP)
if (cap == BUTT_CAP)
stroke.generator().line_cap(agg::butt_cap);
else if (cap == SQUARE_CAP)
stroke.generator().line_cap(agg::square_cap);
else
else
stroke.generator().line_cap(agg::round_cap);
stroke.generator().miter_limit(4.0);
stroke.generator().width(stroke_.get_width());
ras_ptr->add_path(stroke);
@ -415,7 +415,7 @@ namespace mapnik
ren.color(agg::rgba8(r, g, b, int(a*stroke_.get_opacity())));
agg::render_scanlines(*ras_ptr, sl, ren);
}
template <typename T>
void agg_renderer<T>::process(point_symbolizer const& sym,
Feature const& feature,
@ -430,7 +430,7 @@ namespace mapnik
for (unsigned i=0;i<feature.num_geometries();++i)
{
geometry2d const& geom = feature.get_geometry(i);
geom.label_position(&x,&y);
prj_trans.backward(x,y,z);
t_.forward(&x,&y);
@ -442,23 +442,37 @@ namespace mapnik
floor(y - 0.5 * h),
ceil (x + 0.5 * w),
ceil (y + 0.5 * h));
if (sym.get_allow_overlap() ||
if (sym.get_allow_overlap() ||
detector_.has_placement(label_ext))
{
{
pixmap_.set_rectangle_alpha2(*data,px,py,sym.get_opacity());
detector_.insert(label_ext);
}
}
}
}
template <typename T>
void agg_renderer<T>::process(shield_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
typedef coord_transform2<CoordTransform,geometry2d> path_type;
UnicodeString text = feature[sym.get_name()].to_unicode();
UnicodeString text;
if( sym.get_no_text() )
text = UnicodeString( " " ); // use 'space' as the text to render
else
text = feature[sym.get_name()].to_unicode(); // use text from feature to render
if ( sym.get_text_convert() == TOUPPER)
{
text = text.toUpper();
}
else if ( sym.get_text_convert() == TOLOWER)
{
text = text.toLower();
}
boost::shared_ptr<ImageData32> const& data = sym.get_image();
if (text.length() > 0 && data)
{
@ -468,7 +482,7 @@ namespace mapnik
{
faces = font_manager_.get_face_set(sym.get_fontset());
}
else
else
{
faces = font_manager_.get_face_set(sym.get_face_name());
}
@ -476,83 +490,110 @@ namespace mapnik
if (faces->size() > 0)
{
text_renderer<T> ren(pixmap_, faces);
ren.set_pixel_size(sym.get_text_size());
ren.set_fill(sym.get_fill());
ren.set_halo_fill(sym.get_halo_fill());
ren.set_halo_radius(sym.get_halo_radius());
placement_finder<label_collision_detector4> finder(detector_);
string_info info(text);
faces->get_string_info(info);
int w = data->width();
int h = data->height();
unsigned num_geom = feature.num_geometries();
for (unsigned i=0;i<num_geom;++i)
{
geometry2d const& geom = feature.get_geometry(i);
if (geom.num_points() > 0 )
{
geometry2d const& geom = feature.get_geometry(i);
if (geom.num_points() > 0 )
{
path_type path(t_,geom,prj_trans);
if (sym.get_label_placement() == POINT_PLACEMENT)
{
double label_x;
double label_y;
double z=0.0;
placement text_placement(info, sym, false);
text_placement.avoid_edges = sym.get_avoid_edges();
geom.label_position(&label_x, &label_y);
prj_trans.backward(label_x,label_y, z);
t_.forward(&label_x,&label_y);
finder.find_point_placement(text_placement,label_x,label_y);
for (unsigned int ii = 0; ii < text_placement.placements.size(); ++ ii)
{
double x = text_placement.placements[ii].starting_x;
double y = text_placement.placements[ii].starting_y;
// remove displacement from image label
position pos = sym.get_displacement();
double lx = x - boost::get<0>(pos);
double ly = y - boost::get<1>(pos);
int px=int(lx - (0.5 * w)) ;
int py=int(ly - (0.5 * h)) ;
Envelope<double> label_ext (floor(lx - 0.5 * w), floor(ly - 0.5 * h), ceil (lx + 0.5 * w), ceil (ly + 0.5 * h));
if ( detector_.has_placement(label_ext))
{
pixmap_.set_rectangle_alpha(px,py,*data);
Envelope<double> dim = ren.prepare_glyphs(&text_placement.placements[ii]);
ren.render(x,y);
detector_.insert(label_ext);
label_placement_enum how_placed = sym.get_label_placement();
if (how_placed == POINT_PLACEMENT || how_placed == VERTEX_PLACEMENT)
{
// for every vertex, try and place a shield/text
geom.rewind(0);
for( unsigned jj = 0; jj < geom.num_points(); jj++ )
{
double label_x;
double label_y;
double z=0.0;
placement text_placement(info, sym, false);
text_placement.avoid_edges = sym.get_avoid_edges();
text_placement.allow_overlap = sym.get_allow_overlap();
if( how_placed == VERTEX_PLACEMENT )
geom.vertex(&label_x,&label_y); // by vertex
else
geom.label_position(&label_x, &label_y); // by middle of line or by point
prj_trans.backward(label_x,label_y, z);
t_.forward(&label_x,&label_y);
finder.find_point_placement( text_placement,label_x,label_y,sym.get_vertical_alignment(),sym.get_line_spacing(),
sym.get_character_spacing(),sym.get_horizontal_alignment(),sym.get_justify_alignment() );
// check to see if image overlaps anything too, there is only ever 1 placement found for points and verticies
if( text_placement.placements.size() > 0)
{
double x = text_placement.placements[0].starting_x;
double y = text_placement.placements[0].starting_y;
int px;
int py;
Envelope<double> label_ext;
if( !sym.get_unlock_image() )
{ // center image at text center position
// remove displacement from image label
position pos = sym.get_displacement();
double lx = x - boost::get<0>(pos);
double ly = y - boost::get<1>(pos);
px=int(floor(lx - (0.5 * w))) ;
py=int(floor(ly - (0.5 * h))) ;
label_ext.init( floor(lx - 0.5 * w), floor(ly - 0.5 * h), ceil (lx + 0.5 * w), ceil (ly + 0.5 * h) );
}
else
{ // center image at reference location
px=int(floor(label_x - 0.5 * w));
py=int(floor(label_y - 0.5 * h));
label_ext.init( floor(label_x - 0.5 * w), floor(label_y - 0.5 * h), ceil (label_x + 0.5 * w), ceil (label_y + 0.5 * h));
}
if ( sym.get_allow_overlap() || detector_.has_placement(label_ext) )
{
//pixmap_.set_rectangle_alpha(px,py,*data);
pixmap_.set_rectangle_alpha2(*data,px,py,sym.get_opacity());
Envelope<double> dim = ren.prepare_glyphs(&text_placement.placements[0]);
ren.render(x,y);
detector_.insert(label_ext);
finder.update_detector(text_placement);
}
}
}
finder.update_detector(text_placement);
}
else if (geom.num_points() > 1 && sym.get_label_placement() == LINE_PLACEMENT)
else if (geom.num_points() > 1 && sym.get_label_placement() == LINE_PLACEMENT)
{
placement text_placement(info, sym, true);
text_placement.avoid_edges = sym.get_avoid_edges();
finder.find_point_placements<path_type>(text_placement,path);
for (unsigned int ii = 0; ii < text_placement.placements.size(); ++ ii)
{
int w = data->width();
int h = data->height();
int h = data->height();
double x = text_placement.placements[ii].starting_x;
double y = text_placement.placements[ii].starting_y;
int px=int(x - (w/2));
int py=int(y - (h/2));
pixmap_.set_rectangle_alpha(px,py,*data);
Envelope<double> dim = ren.prepare_glyphs(&text_placement.placements[ii]);
ren.render(x,y);
}
@ -563,7 +604,7 @@ namespace mapnik
}
}
}
template <typename T>
void agg_renderer<T>::process(line_pattern_symbolizer const& sym,
Feature const& feature,
@ -574,68 +615,68 @@ namespace mapnik
typedef agg::renderer_base<agg::pixfmt_rgba32_plain> renderer_base;
typedef agg::renderer_outline_image<renderer_base, pattern_type> renderer_type;
typedef agg::rasterizer_outline_aa<renderer_type> rasterizer_type;
agg::rendering_buffer buf(pixmap_.raw_data(),width_,height_, width_ * 4);
agg::pixfmt_rgba32_plain pixf(buf);
ImageData32 pat = * sym.get_image();
renderer_base ren_base(pixf);
agg::pattern_filter_bilinear_rgba8 filter;
renderer_base ren_base(pixf);
agg::pattern_filter_bilinear_rgba8 filter;
pattern_source source(pat);
pattern_type pattern (filter,source);
renderer_type ren(ren_base, pattern);
ren.clip_box(0,0,width_,height_);
rasterizer_type ras(ren);
for (unsigned i=0;i<feature.num_geometries();++i)
{
geometry2d const& geom = feature.get_geometry(i);
if (geom.num_points() > 1)
{
path_type path(t_,geom,prj_trans);
ras.add_path(path);
}
ras.add_path(path);
}
}
}
template <typename T>
void agg_renderer<T>::process(polygon_pattern_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
typedef coord_transform2<CoordTransform,geometry2d> path_type;
typedef agg::renderer_base<agg::pixfmt_rgba32_plain> ren_base;
typedef agg::renderer_base<agg::pixfmt_rgba32_plain> ren_base;
typedef agg::wrap_mode_repeat wrap_x_type;
typedef agg::wrap_mode_repeat wrap_y_type;
typedef agg::pixfmt_alpha_blend_rgba<agg::blender_rgba32,
agg::row_accessor<agg::int8u>, agg::pixel32_type> rendering_buffer;
typedef agg::image_accessor_wrap<rendering_buffer,
typedef agg::image_accessor_wrap<rendering_buffer,
wrap_x_type,
wrap_y_type> img_source_type;
typedef agg::span_pattern_rgba<img_source_type> span_gen_type;
typedef agg::renderer_scanline_aa<ren_base,
typedef agg::renderer_scanline_aa<ren_base,
agg::span_allocator<agg::rgba8>,
span_gen_type> renderer_type;
span_gen_type> renderer_type;
agg::rendering_buffer buf(pixmap_.raw_data(),width_,height_, width_ * 4);
agg::pixfmt_rgba32_plain pixf(buf);
ren_base renb(pixf);
agg::scanline_u8 sl;
ras_ptr->reset();
ImageData32 const& pattern = * sym.get_image();
unsigned w=pattern.width();
unsigned h=pattern.height();
agg::row_accessor<agg::int8u> pattern_rbuf((agg::int8u*)pattern.getBytes(),w,h,w*4);
agg::row_accessor<agg::int8u> pattern_rbuf((agg::int8u*)pattern.getBytes(),w,h,w*4);
agg::span_allocator<agg::rgba8> sa;
agg::pixfmt_alpha_blend_rgba<agg::blender_rgba32,
agg::row_accessor<agg::int8u>, agg::pixel32_type> pixf_pattern(pattern_rbuf);
img_source_type img_src(pixf_pattern);
double x0=0,y0=0;
unsigned num_geometries = feature.num_geometries();
if (num_geometries>0)
@ -645,18 +686,18 @@ namespace mapnik
}
unsigned offset_x = unsigned(width_-x0);
unsigned offset_y = unsigned(height_-y0);
span_gen_type sg(img_src, offset_x, offset_y);
span_gen_type sg(img_src, offset_x, offset_y);
renderer_type rp(renb,sa, sg);
for (unsigned i=0;i<num_geometries;++i)
{
geometry2d const& geom = feature.get_geometry(i);
if (geom.num_points() > 2)
{
path_type path(t_,geom,prj_trans);
ras_ptr->add_path(path);
{
path_type path(t_,geom,prj_trans);
ras_ptr->add_path(path);
}
}
agg::render_scanlines(*ras_ptr, sl, rp);
agg::render_scanlines(*ras_ptr, sl, rp);
}
template <typename T>
@ -671,7 +712,7 @@ namespace mapnik
ImageData32 target(int(ceil(ext.width())),int(ceil(ext.height())));
int start_x = int(ext.minx()+0.5);
int start_y = int(ext.miny()+0.5);
if (sym.get_scaling() == "fast") {
scale_image<ImageData32>(target,raster->data_);
} else if (sym.get_scaling() == "bilinear"){
@ -714,39 +755,39 @@ namespace mapnik
// TODO: other modes? (add,diff,sub,...)
}
}
template <typename T>
void agg_renderer<T>::process(markers_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
typedef coord_transform2<CoordTransform,geometry2d> path_type;
typedef agg::renderer_base<agg::pixfmt_rgba32_plain> ren_base;
typedef agg::renderer_base<agg::pixfmt_rgba32_plain> ren_base;
typedef agg::renderer_scanline_aa_solid<ren_base> renderer;
arrow arrow_;
ras_ptr->reset();
agg::scanline_u8 sl;
agg::rendering_buffer buf(pixmap_.raw_data(),width_,height_, width_ * 4);
agg::pixfmt_rgba32_plain pixf(buf);
ren_base renb(pixf);
ren_base renb(pixf);
unsigned r = 0;// fill_.red();
unsigned g = 0; //fill_.green();
unsigned b = 255; //fill_.blue();
unsigned a = 255; //fill_.alpha();
renderer ren(renb);
renderer ren(renb);
for (unsigned i=0;i<feature.num_geometries();++i)
{
geometry2d const& geom=feature.get_geometry(i);
if (geom.num_points() > 1)
if (geom.num_points() > 1)
{
path_type path(t_,geom,prj_trans);
agg::conv_dash <path_type> dash(path);
dash.add_dash(20.0,200.0);
markers_converter<agg::conv_dash<path_type>,
arrow,
markers_converter<agg::conv_dash<path_type>,
arrow,
label_collision_detector4>
marker(dash, arrow_, detector_);
ras_ptr->add_path(marker);
@ -755,14 +796,14 @@ namespace mapnik
ren.color(agg::rgba8(r, g, b, a));
agg::render_scanlines(*ras_ptr, sl, ren);
}
template <typename T>
void agg_renderer<T>::process(text_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
typedef coord_transform2<CoordTransform,geometry2d> path_type;
UnicodeString text = feature[sym.get_name()].to_unicode();
if ( sym.get_text_convert() == TOUPPER)
{
@ -783,7 +824,7 @@ namespace mapnik
{
faces = font_manager_.get_face_set(sym.get_fontset());
}
else
else
{
faces = font_manager_.get_face_set(sym.get_face_name());
}
@ -795,35 +836,37 @@ namespace mapnik
ren.set_fill(fill);
ren.set_halo_fill(sym.get_halo_fill());
ren.set_halo_radius(sym.get_halo_radius());
ren.set_opacity(sym.get_opacity());
placement_finder<label_collision_detector4> finder(detector_);
string_info info(text);
faces->get_string_info(info);
unsigned num_geom = feature.num_geometries();
for (unsigned i=0;i<num_geom;++i)
{
geometry2d const& geom = feature.get_geometry(i);
if (geom.num_points() > 0) // don't bother with empty geometries
if (geom.num_points() > 0) // don't bother with empty geometries
{
path_type path(t_,geom,prj_trans);
placement text_placement(info,sym);
placement text_placement(info,sym);
text_placement.avoid_edges = sym.get_avoid_edges();
if (sym.get_label_placement() == POINT_PLACEMENT)
if (sym.get_label_placement() == POINT_PLACEMENT)
{
double label_x, label_y, z=0.0;
geom.label_position(&label_x, &label_y);
prj_trans.backward(label_x,label_y, z);
t_.forward(&label_x,&label_y);
finder.find_point_placement(text_placement,label_x,label_y,sym.get_vertical_alignment(),sym.get_line_spacing(),sym.get_character_spacing());
finder.find_point_placement(text_placement,label_x,label_y,sym.get_vertical_alignment(),sym.get_line_spacing(),
sym.get_character_spacing(),sym.get_horizontal_alignment(),sym.get_justify_alignment());
finder.update_detector(text_placement);
}
else if ( geom.num_points() > 1 && sym.get_label_placement() == LINE_PLACEMENT)
{
finder.find_line_placements<path_type>(text_placement,path);
}
for (unsigned int ii = 0; ii < text_placement.placements.size(); ++ii)
{
double x = text_placement.placements[ii].starting_x;
@ -832,7 +875,7 @@ namespace mapnik
ren.render(x,y);
}
}
}
}
}
else
{

View file

@ -1,5 +1,5 @@
/*****************************************************************************
*
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2006 Artem Pavlenko
@ -55,28 +55,28 @@ using boost::property_tree::ptree;
using std::cerr;
using std::endl;
namespace mapnik
namespace mapnik
{
using boost::optional;
class map_parser : boost::noncopyable {
public:
map_parser( bool strict, std::string const& filename = "" ) :
map_parser( bool strict, std::string const& filename = "" ) :
strict_( strict ),
filename_( filename ),
relative_to_xml_(true),
font_manager_(font_engine_) {}
void parse_map( Map & map, ptree const & sty);
private:
void parse_style( Map & map, ptree const & sty);
void parse_layer( Map & map, ptree const & lay);
void parse_fontset(Map & map, ptree const & fset);
void parse_font(FontSet & fset, ptree const & f);
void parse_rule( feature_type_style & style, ptree const & r);
void parse_point_symbolizer( rule_type & rule, ptree const & sym);
void parse_line_pattern_symbolizer( rule_type & rule, ptree const & sym);
void parse_polygon_pattern_symbolizer( rule_type & rule, ptree const & sym);
@ -87,11 +87,11 @@ namespace mapnik
void parse_building_symbolizer( rule_type & rule, ptree const & sym );
void parse_raster_symbolizer( rule_type & rule, ptree const & sym );
void parse_markers_symbolizer( rule_type & rule, ptree const & sym );
void ensure_font_face( const std::string & face_name );
std::string ensure_relative_to_xml( boost::optional<std::string> opt_path );
bool strict_;
std::string filename_;
bool relative_to_xml_;
@ -127,7 +127,7 @@ namespace mapnik
#ifdef HAVE_LIBXML2
read_xml2_string(str, pt);
#else
try
try
{
std::istringstream s(str);
read_xml(s,pt);
@ -218,7 +218,7 @@ namespace mapnik
std::string name = get_attr<string>(param, "name");
std::string value = get_own<string>( param,
"datasource parameter");
params[name] = value;
params[name] = value;
}
else if( paramIter->first != "<xmlattr>" &&
paramIter->first != "<xmlcomment>" )
@ -255,7 +255,7 @@ namespace mapnik
ptree::const_iterator ruleIter = sty.begin();
ptree::const_iterator endRule = sty.end();
for (; ruleIter!=endRule; ++ruleIter)
for (; ruleIter!=endRule; ++ruleIter)
{
ptree::value_type const& rule_tag = *ruleIter;
if (rule_tag.first == "Rule")
@ -291,7 +291,7 @@ namespace mapnik
ptree::const_iterator itr = fset.begin();
ptree::const_iterator end = fset.end();
for (; itr != end; ++itr)
for (; itr != end; ++itr)
{
ptree::value_type const& font_tag = *itr;
@ -308,8 +308,8 @@ namespace mapnik
}
map.insert_fontset(name, fontset);
// XXX Hack because map object isn't accessible by text_symbolizer
// XXX Hack because map object isn't accessible by text_symbolizer
// when it's parsed
fontsets_.insert(pair<std::string, FontSet>(name, fontset));
} catch (const config_error & ex) {
@ -338,7 +338,7 @@ namespace mapnik
try
{
name = get_attr(lay, "name", string("Unnamed"));
// XXX if no projection is given inherit from map? [DS]
std::string srs = get_attr(lay, "srs", map.srs());
@ -379,7 +379,7 @@ namespace mapnik
{
lyr.setQueryable( * queryable );
}
optional<boolean> clear_cache =
get_opt_attr<boolean>(lay, "clear_label_cache");
if (clear_cache)
@ -409,8 +409,8 @@ namespace mapnik
std::map<std::string,parameters>::const_iterator base_itr = datasource_templates_.find(*base);
if (base_itr!=datasource_templates_.end())
params = base_itr->second;
}
}
ptree::const_iterator paramIter = child.second.begin();
ptree::const_iterator endParam = child.second.end();
for (; paramIter != endParam; ++paramIter)
@ -422,7 +422,7 @@ namespace mapnik
std::string name = get_attr<string>(param, "name");
std::string value = get_own<string>( param,
"datasource parameter");
params[name] = value;
params[name] = value;
}
else if( paramIter->first != "<xmlattr>" &&
paramIter->first != "<xmlcomment>" )
@ -432,15 +432,15 @@ namespace mapnik
paramIter->first + "'");
}
}
if ( relative_to_xml_ ) {
boost::optional<std::string> base_param = params.get<std::string>("base");
boost::optional<std::string> file_param = params.get<std::string>("file");
if (base_param){
params["base"] = ensure_relative_to_xml(base_param);
}
else if (file_param){
params["file"] = ensure_relative_to_xml(file_param);
}
@ -450,27 +450,27 @@ namespace mapnik
std::clog << "\nFound relative paths in xml, leaving unchanged...\n";
}
#endif
//now we are ready to create datasource
try
//now we are ready to create datasource
try
{
boost::shared_ptr<datasource> ds =
datasource_cache::instance()->create(params);
lyr.set_datasource(ds);
}
// catch problem at datasource registration
catch (const mapnik::config_error & ex )
{
throw config_error( ex.what() );
}
// catch problem at the datasource creation
catch (const mapnik::datasource_exception & ex )
{
throw config_error( ex.what() );
}
catch (...)
{
//throw config_error("exception...");
@ -512,27 +512,27 @@ namespace mapnik
// can we use encoding defined for XML document for filter expressions?
rule.set_filter(create_filter(*filter_expr,"utf8"));
}
optional<std::string> else_filter =
optional<std::string> else_filter =
get_opt_child<string>(r, "ElseFilter");
if (else_filter)
{
rule.set_else(true);
}
optional<double> min_scale =
optional<double> min_scale =
get_opt_child<double>(r, "MinScaleDenominator");
if (min_scale)
{
rule.set_min_scale(*min_scale);
}
optional<double> max_scale =
optional<double> max_scale =
get_opt_child<double>(r, "MaxScaleDenominator");
if (max_scale)
if (max_scale)
{
rule.set_max_scale(*max_scale);
}
rule.set_max_scale(*max_scale);
}
ptree::const_iterator symIter = r.begin();
ptree::const_iterator endSym = r.end();
@ -544,7 +544,7 @@ namespace mapnik
if ( sym.first == "PointSymbolizer")
{
parse_point_symbolizer( rule, sym.second );
}
}
else if ( sym.first == "LinePatternSymbolizer")
{
parse_line_pattern_symbolizer( rule, sym.second );
@ -564,24 +564,24 @@ namespace mapnik
else if ( sym.first == "LineSymbolizer")
{
parse_line_symbolizer( rule, sym.second );
}
}
else if ( sym.first == "PolygonSymbolizer")
{
parse_polygon_symbolizer( rule, sym.second );
}
}
else if ( sym.first == "BuildingSymbolizer")
{
parse_building_symbolizer( rule, sym.second );
}
}
else if ( sym.first == "RasterSymbolizer")
{
parse_raster_symbolizer( rule, sym.second );
}
}
else if ( sym.first == "MarkersSymbolizer")
{
rule.append(markers_symbolizer());
}
}
else if ( sym.first != "MinScaleDenominator" &&
sym.first != "MaxScaleDenominator" &&
sym.first != "Filter" &&
@ -592,7 +592,7 @@ namespace mapnik
throw config_error(std::string("Unknown symbolizer '") +
sym.first + "'");
}
}
}
style.add_rule(rule);
@ -609,18 +609,18 @@ namespace mapnik
void map_parser::parse_point_symbolizer( rule_type & rule, ptree const & sym )
{
try
try
{
optional<std::string> file = get_opt_attr<string>(sym, "file");
optional<std::string> base = get_opt_attr<string>(sym, "base");
optional<std::string> type = get_opt_attr<string>(sym, "type");
optional<boolean> allow_overlap =
optional<std::string> type = get_opt_attr<string>(sym, "type");
optional<boolean> allow_overlap =
get_opt_attr<boolean>(sym, "allow_overlap");
optional<float> opacity =
optional<float> opacity =
get_opt_attr<float>(sym, "opacity");
optional<unsigned> width = get_opt_attr<unsigned>(sym, "width");
optional<unsigned> height = get_opt_attr<unsigned>(sym, "height");
optional<unsigned> width = get_opt_attr<unsigned>(sym, "width");
optional<unsigned> height = get_opt_attr<unsigned>(sym, "height");
if (file && type && width && height)
{
@ -634,9 +634,9 @@ namespace mapnik
*file = itr->second + "/" + *file;
}
}
if ( relative_to_xml_ )
{
{
*file = ensure_relative_to_xml(file);
}
#ifdef MAPNIK_DEBUG
@ -644,7 +644,7 @@ namespace mapnik
std::clog << "\nFound relative paths in xml, leaving unchanged...\n";
}
#endif
point_symbolizer symbol(*file,*type,*width,*height);
if (allow_overlap)
{
@ -653,7 +653,7 @@ namespace mapnik
if (opacity)
{
symbol.set_opacity( * opacity );
}
}
rule.append(symbol);
}
catch (ImageReaderException const & ex )
@ -681,12 +681,12 @@ namespace mapnik
if ( ! height ) os << "height ";
throw config_error( os.str() );
}
else
else
{
rule.append(point_symbolizer());
}
}
catch (const config_error & ex)
catch (const config_error & ex)
{
ex.append_context("in PointSymbolizer");
throw;
@ -695,11 +695,11 @@ namespace mapnik
void map_parser::parse_line_pattern_symbolizer( rule_type & rule, ptree const & sym )
{
try
try
{
std::string file = get_attr<string>(sym, "file");
optional<std::string> base = get_opt_attr<string>(sym, "base");
std::string type = get_attr<string>(sym, "type");
std::string type = get_attr<string>(sym, "type");
unsigned width = get_attr<unsigned>(sym, "width");
unsigned height = get_attr<unsigned>(sym, "height");
@ -714,7 +714,7 @@ namespace mapnik
}
}
if ( relative_to_xml_ )
{
{
file = ensure_relative_to_xml(file);
}
#ifdef MAPNIK_DEBUG
@ -722,7 +722,7 @@ namespace mapnik
std::clog << "\nFound relative paths in xml, leaving unchanged...\n";
}
#endif
line_pattern_symbolizer symbol(file,type,width,height);
rule.append(symbol);
@ -741,7 +741,7 @@ namespace mapnik
}
}
}
catch (const config_error & ex)
catch (const config_error & ex)
{
ex.append_context("in LinePatternSymbolizer");
throw;
@ -751,11 +751,11 @@ namespace mapnik
void map_parser::parse_polygon_pattern_symbolizer( rule_type & rule,
ptree const & sym )
{
try
try
{
std::string file = get_attr<string>(sym, "file");
optional<std::string> base = get_opt_attr<string>(sym, "base");
std::string type = get_attr<string>(sym, "type");
std::string type = get_attr<string>(sym, "type");
unsigned width = get_attr<unsigned>(sym, "width");
unsigned height = get_attr<unsigned>(sym, "height");
@ -770,7 +770,7 @@ namespace mapnik
}
}
if ( relative_to_xml_ )
{
{
file = ensure_relative_to_xml(file);
}
#ifdef MAPNIK_DEBUG
@ -796,7 +796,7 @@ namespace mapnik
}
}
}
catch (const config_error & ex)
catch (const config_error & ex)
{
ex.append_context("in PolygonPatternSymbolizer");
throw;
@ -807,20 +807,20 @@ namespace mapnik
{
try
{
std::string name = get_attr<string>(sym, "name");
std::string name = get_attr<string>(sym, "name");
optional<std::string> face_name =
get_opt_attr<std::string>(sym, "face_name");
optional<std::string> fontset_name =
get_opt_attr<std::string>(sym, "fontset_name");
unsigned size = get_attr(sym, "size", 10U);
color c = get_attr(sym, "fill", color(0,0,0));
text_symbolizer text_symbol = text_symbolizer(name, size, c);
text_symbolizer text_symbol = text_symbolizer(name, size, c);
if (fontset_name && face_name)
{
throw config_error(std::string("Can't have both face_name and fontset_name"));
@ -830,7 +830,7 @@ namespace mapnik
std::map<std::string,FontSet>::const_iterator itr = fontsets_.find(*fontset_name);
if (itr != fontsets_.end())
{
text_symbol.set_fontset(itr->second);
text_symbol.set_fontset(itr->second);
}
else
{
@ -857,17 +857,17 @@ namespace mapnik
label_placement_e placement =
get_attr<label_placement_e>(sym, "placement", POINT_PLACEMENT);
text_symbol.set_label_placement( placement );
// vertical alignment
// vertical alignment
vertical_alignment_e valign = get_attr<vertical_alignment_e>(sym, "vertical_alignment", BOTTOM);
text_symbol.set_vertical_alignment(valign);
text_symbol.set_vertical_alignment(valign);
// halo fill and radius
optional<color> halo_fill = get_opt_attr<color>(sym, "halo_fill");
if (halo_fill)
{
text_symbol.set_halo_fill( * halo_fill );
}
optional<unsigned> halo_radius =
optional<unsigned> halo_radius =
get_opt_attr<unsigned>(sym, "halo_radius");
if (halo_radius)
{
@ -875,18 +875,29 @@ namespace mapnik
}
// text ratio and wrap width
optional<unsigned> text_ratio =
optional<unsigned> text_ratio =
get_opt_attr<unsigned>(sym, "text_ratio");
if (text_ratio)
{
text_symbol.set_text_ratio(*text_ratio);
}
optional<unsigned> wrap_width =
optional<unsigned> wrap_width =
get_opt_attr<unsigned>(sym, "wrap_width");
if (wrap_width)
{
text_symbol.set_wrap_width(*wrap_width);
}
optional<boolean> wrap_before =
get_opt_attr<boolean>(sym, "wrap_before");
if (wrap_before)
{
text_symbol.set_wrap_before(*wrap_before);
}
// character used to break long strings
optional<std::string> wrap_char =
optional<std::string> wrap_char =
get_opt_attr<std::string>(sym, "wrap_character");
if (wrap_char && (*wrap_char).size() > 0)
{
@ -935,25 +946,41 @@ namespace mapnik
text_symbol.set_avoid_edges( * avoid_edges);
}
// allow_overlap
optional<boolean> allow_overlap =
// allow_overlap
optional<boolean> allow_overlap =
get_opt_attr<boolean>(sym, "allow_overlap");
if (allow_overlap)
{
text_symbol.set_allow_overlap( * allow_overlap );
}
// opacity
optional<double> opacity =
get_opt_attr<double>(sym, "opacity");
if (opacity)
{
text_symbol.set_opacity( * opacity );
}
// max_char_angle_delta
optional<double> max_char_angle_delta =
optional<double> max_char_angle_delta =
get_opt_attr<double>(sym, "max_char_angle_delta");
if (max_char_angle_delta)
{
text_symbol.set_max_char_angle_delta( * max_char_angle_delta);
}
// horizontal alignment
horizontal_alignment_e halign = get_attr<horizontal_alignment_e>(sym, "horizontal_alignment", H_MIDDLE);
text_symbol.set_horizontal_alignment(halign);
// justify alignment
justify_alignment_e jalign = get_attr<justify_alignment_e>(sym, "justify_alignment", J_MIDDLE);
text_symbol.set_justify_alignment(jalign);
rule.append(text_symbol);
}
catch (const config_error & ex)
catch (const config_error & ex)
{
ex.append_context("in TextSymbolizer");
throw;
@ -991,9 +1018,9 @@ namespace mapnik
image_file = itr->second + "/" + image_file;
}
}
if ( relative_to_xml_ )
{
{
image_file = ensure_relative_to_xml(image_file);
}
#ifdef MAPNIK_DEBUG
@ -1001,7 +1028,7 @@ namespace mapnik
std::clog << "\nFound relative paths in xml, leaving unchanged...\n";
}
#endif
shield_symbolizer shield_symbol(name,size,fill,
image_file,type,width,height);
@ -1014,7 +1041,7 @@ namespace mapnik
std::map<std::string,FontSet>::const_iterator itr = fontsets_.find(*fontset_name);
if (itr != fontsets_.end())
{
shield_symbol.set_fontset(itr->second);
shield_symbol.set_fontset(itr->second);
}
else
{
@ -1037,7 +1064,7 @@ namespace mapnik
double dx = get_attr(sym, "dx", 0.0);
double dy = get_attr(sym, "dy", 0.0);
shield_symbol.set_displacement(dx,dy);
label_placement_e placement =
get_attr<label_placement_e>(sym, "placement", POINT_PLACEMENT);
shield_symbol.set_label_placement( placement );
@ -1049,35 +1076,120 @@ namespace mapnik
{
shield_symbol.set_avoid_edges( *avoid_edges);
}
// halo fill and radius
optional<color> halo_fill = get_opt_attr<color>(sym, "halo_fill");
if (halo_fill)
{
shield_symbol.set_halo_fill( * halo_fill );
}
optional<unsigned> halo_radius =
optional<unsigned> halo_radius =
get_opt_attr<unsigned>(sym, "halo_radius");
if (halo_radius)
{
shield_symbol.set_halo_radius(*halo_radius);
}
// minimum distance between labels
optional<unsigned> min_distance =
optional<unsigned> min_distance =
get_opt_attr<unsigned>(sym, "min_distance");
if (min_distance)
{
shield_symbol.set_minimum_distance(*min_distance);
}
// spacing between repeated labels on lines
optional<unsigned> spacing = get_opt_attr<unsigned>(sym, "spacing");
if (spacing)
{
shield_symbol.set_label_spacing(*spacing);
}
// allow_overlap
optional<boolean> allow_overlap =
get_opt_attr<boolean>(sym, "allow_overlap");
if (allow_overlap)
{
shield_symbol.set_allow_overlap( * allow_overlap );
}
// vertical alignment
vertical_alignment_e valign = get_attr<vertical_alignment_e>(sym, "vertical_alignment", BOTTOM);
shield_symbol.set_vertical_alignment(valign);
// horizontal alignment
horizontal_alignment_e halign = get_attr<horizontal_alignment_e>(sym, "horizontal_alignment", H_MIDDLE);
shield_symbol.set_horizontal_alignment(halign);
// justify alignment
justify_alignment_e jalign = get_attr<justify_alignment_e>(sym, "justify_alignment", J_MIDDLE);
shield_symbol.set_justify_alignment(jalign);
optional<unsigned> wrap_width =
get_opt_attr<unsigned>(sym, "wrap_width");
if (wrap_width)
{
shield_symbol.set_wrap_width(*wrap_width);
}
optional<boolean> wrap_before =
get_opt_attr<boolean>(sym, "wrap_before");
if (wrap_before)
{
shield_symbol.set_wrap_before(*wrap_before);
}
// character used to break long strings
optional<std::string> wrap_char =
get_opt_attr<std::string>(sym, "wrap_character");
if (wrap_char && (*wrap_char).size() > 0)
{
shield_symbol.set_wrap_char((*wrap_char)[0]);
}
// text conversion before rendering
text_convert_e tconvert =
get_attr<text_convert_e>(sym, "text_convert", NONE);
shield_symbol.set_text_convert(tconvert);
// spacing between text lines
optional<unsigned> line_spacing = get_opt_attr<unsigned>(sym, "line_spacing");
if (line_spacing)
{
shield_symbol.set_line_spacing(*line_spacing);
}
// spacing between characters in text
optional<unsigned> character_spacing = get_opt_attr<unsigned>(sym, "character_spacing");
if (character_spacing)
{
shield_symbol.set_character_spacing(*character_spacing);
}
// opacity
optional<double> opacity =
get_opt_attr<double>(sym, "opacity");
if (opacity)
{
shield_symbol.set_opacity( * opacity );
}
// unlock_image
optional<boolean> unlock_image =
get_opt_attr<boolean>(sym, "unlock_image");
if (unlock_image)
{
shield_symbol.set_unlock_image( * unlock_image );
}
// no text
optional<boolean> no_text =
get_opt_attr<boolean>(sym, "no_text");
if (no_text)
{
shield_symbol.set_no_text( * no_text );
}
rule.append(shield_symbol);
}
catch (ImageReaderException const & ex )
@ -1093,9 +1205,9 @@ namespace mapnik
clog << "### WARNING: " << msg << endl;
}
}
}
catch (const config_error & ex)
catch (const config_error & ex)
{
ex.append_context("in ShieldSymbolizer");
throw;
@ -1195,7 +1307,7 @@ namespace mapnik
}
rule.append(line_symbolizer(strk));
}
catch (const config_error & ex)
catch (const config_error & ex)
{
ex.append_context("in LineSymbolizer");
throw;
@ -1246,7 +1358,7 @@ namespace mapnik
rule.append(poly_sym);
}
catch (const config_error & ex)
catch (const config_error & ex)
{
ex.append_context("in PolygonSymbolizer");
throw;
@ -1301,12 +1413,12 @@ namespace mapnik
}
rule.append(building_sym);
}
catch (const config_error & ex)
catch (const config_error & ex)
{
ex.append_context("in BuildingSymbolizer");
throw;
}
}
}
void map_parser::parse_raster_symbolizer( rule_type & rule, ptree const & sym )
{
@ -1368,7 +1480,7 @@ namespace mapnik
face_name + "'");
}
}
std::string map_parser::ensure_relative_to_xml( boost::optional<std::string> opt_path )
{
boost::filesystem::path xml_path = filename_;

View file

@ -1,5 +1,5 @@
/*****************************************************************************
*
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2006 Artem Pavlenko
@ -49,49 +49,54 @@
namespace mapnik
{
placement::placement(string_info & info_,
placement::placement(string_info & info_,
shield_symbolizer const& sym, bool has_dimensions_)
: info(info_),
: info(info_),
displacement_(sym.get_displacement()),
label_placement(sym.get_label_placement()),
wrap_width(sym.get_wrap_width()),
text_ratio(sym.get_text_ratio()),
label_spacing(sym.get_label_spacing()),
label_position_tolerance(sym.get_label_position_tolerance()),
force_odd_labels(sym.get_force_odd_labels()),
label_placement(sym.get_label_placement()),
wrap_width(sym.get_wrap_width()),
wrap_before(sym.get_wrap_before()),
wrap_char(sym.get_wrap_char()),
text_ratio(sym.get_text_ratio()),
label_spacing(sym.get_label_spacing()),
label_position_tolerance(sym.get_label_position_tolerance()),
force_odd_labels(sym.get_force_odd_labels()),
max_char_angle_delta(sym.get_max_char_angle_delta()),
minimum_distance(sym.get_minimum_distance()),
avoid_edges(sym.get_avoid_edges()),
has_dimensions(has_dimensions_),
has_dimensions(has_dimensions_),
allow_overlap(false),
dimensions(std::make_pair(sym.get_image()->width(),
sym.get_image()->height()))
sym.get_image()->height())),
text_size(sym.get_text_size())
{
}
placement::placement(string_info & info_,
text_symbolizer const& sym)
: info(info_),
: info(info_),
displacement_(sym.get_displacement()),
label_placement(sym.get_label_placement()),
wrap_width(sym.get_wrap_width()),
label_placement(sym.get_label_placement()),
wrap_width(sym.get_wrap_width()),
wrap_before(sym.get_wrap_before()),
wrap_char(sym.get_wrap_char()),
text_ratio(sym.get_text_ratio()),
label_spacing(sym.get_label_spacing()),
label_position_tolerance(sym.get_label_position_tolerance()),
force_odd_labels(sym.get_force_odd_labels()),
text_ratio(sym.get_text_ratio()),
label_spacing(sym.get_label_spacing()),
label_position_tolerance(sym.get_label_position_tolerance()),
force_odd_labels(sym.get_force_odd_labels()),
max_char_angle_delta(sym.get_max_char_angle_delta()),
minimum_distance(sym.get_minimum_distance()),
avoid_edges(sym.get_avoid_edges()),
has_dimensions(false),
allow_overlap(sym.get_allow_overlap()),
dimensions()
dimensions(),
text_size(sym.get_text_size())
{
}
placement::~placement()
{
}
template<typename T>
std::pair<double, double> get_position_at_distance(double target_distance, T & shape_path)
{
@ -115,10 +120,10 @@ namespace mapnik
{
double dx = x2-x1;
double dy = y2-y1;
double segment_length = std::sqrt(dx*dx + dy*dy);
distance +=segment_length;
if (distance > target_distance)
{
x = x2 - dx * (distance - target_distance)/segment_length;
@ -131,13 +136,13 @@ namespace mapnik
}
return std::pair<double, double>(x, y);
}
template<typename T>
double get_total_distance(T & shape_path)
{
return agg::path_length(shape_path);
}
template <typename DetectorT>
placement_finder<DetectorT>::placement_finder(DetectorT & detector)
: detector_(detector),
@ -158,7 +163,7 @@ namespace mapnik
double total_distance = get_total_distance<T>(shape_path);
shape_path.rewind(0);
if (distance == 0) //Point data, not a line
{
double x, y;
@ -166,20 +171,20 @@ namespace mapnik
find_point_placement(p, x, y);
return;
}
int num_labels = 1;
if (p.label_spacing > 0)
num_labels = static_cast<int> (floor(total_distance / p.label_spacing));
if (p.force_odd_labels && num_labels%2 == 0)
num_labels--;
if (num_labels <= 0)
num_labels = 1;
double distance = 0.0; // distance from last label
double spacing = total_distance / num_labels;
double target_distance = spacing / 2; // first label should be placed at half the spacing
while (!agg::is_stop(cmd = shape_path.vertex(&new_x,&new_y))) //For each node in the shape
{
@ -187,201 +192,230 @@ namespace mapnik
{
first = false;
}
else
else
{
//Add the length of this segment to the total we have saved up
double segment_length = std::sqrt(std::pow(old_x-new_x,2) + std::pow(old_y-new_y,2)); //Pythagoras
distance += segment_length;
//While we have enough distance to place text in
while (distance > target_distance)
{
//Try place at the specified place
double new_weight = (segment_length - (distance - target_distance))/segment_length;
find_point_placement(p, old_x + (new_x-old_x)*new_weight, old_y + (new_y-old_y)*new_weight);
distance -= target_distance; //Consume the spacing gap we have used up
target_distance = spacing; //Need to reset the target_distance as it is spacing/2 for the first label.
}
}
old_x = new_x;
old_y = new_y;
}
}
template <typename DetectorT>
void placement_finder<DetectorT>::find_point_placement(placement & p,
double label_x,
void placement_finder<DetectorT>::find_point_placement(placement & p,
double label_x,
double label_y,
vertical_alignment_e valign,
unsigned line_spacing,
unsigned character_spacing)
unsigned character_spacing,
horizontal_alignment_e halign,
justify_alignment_e jalign)
{
double x, y;
std::auto_ptr<placement_element> current_placement(new placement_element);
std::pair<double, double> string_dimensions = p.info.get_dimensions();
double string_width = string_dimensions.first;
double string_width = string_dimensions.first + (character_spacing *(p.info.num_characters()-1));
double string_height = string_dimensions.second;
// use height of tallest character in the string for the 'line' spacing to obtain consistent line spacing
double max_character_height = string_height; // height of the tallest character in the string
// check if we need to wrap the string
double wrap_at = string_width + 1;
double wrap_at = string_width + 1.0;
if (p.wrap_width && string_width > p.wrap_width)
{
if (p.text_ratio)
for (int i = 1; ((wrap_at = string_width/i)/(string_height*i)) > p.text_ratio && (string_width/i) > p.wrap_width; ++i);
for (double i = 1.0; ((wrap_at = string_width/i)/(string_height*i)) > p.text_ratio && (string_width/i) > p.wrap_width; i += 1.0);
else
wrap_at = p.wrap_width;
}
// work out where our line breaks need to be
// work out where our line breaks need to be and the resultant width to the 'wrapped' string
std::vector<int> line_breaks;
std::vector<double> line_widths;
std::vector<double> line_heights;
if (wrap_at < string_width && p.info.num_characters() > 0)
{
int last_wrap_char = 0;
string_width = 0;
string_height = 0;
double line_width = 0;
double line_height = 0;
double word_width = 0;
double word_height = 0;
int last_wrap_char_width = 0;
string_width = 0.0;
string_height = 0.0;
double line_width = 0.0;
double word_width = 0.0;
for (unsigned int ii = 0; ii < p.info.num_characters(); ii++)
{
character_info ci;
ci = p.info.at(ii);
unsigned cwidth = ci.width + character_spacing;
double cwidth = ci.width + character_spacing;
unsigned c = ci.character;
word_width += cwidth;
word_height = word_height > (ci.height + line_spacing) ? word_height : (ci.height + line_spacing);
if (c == p.wrap_char)
{
last_wrap_char = ii;
last_wrap_char_width = cwidth;
line_width += word_width;
line_height = line_height > word_height ? line_height : word_height;
word_width = 0;
word_height = 0;
word_width = 0.0;
}
if (line_width > 0 && line_width > wrap_at)
// wrap text at first wrap_char after (default) the wrap width or immediately before the current word
if (line_width > 0 && (((line_width - character_spacing) > wrap_at && !p.wrap_before) ||
((line_width + word_width - character_spacing) > wrap_at && p.wrap_before)) )
{
// Remove width of breaking space character since it is not rendered
line_width -= cwidth;
// Remove width of breaking space character since it is not rendered and the character_spacing for the last character on the line
line_width -= (last_wrap_char_width + character_spacing);
string_width = string_width > line_width ? string_width : line_width;
string_height += line_height;
string_height += max_character_height;
line_breaks.push_back(last_wrap_char);
line_widths.push_back(line_width);
line_heights.push_back(line_height);
ii = last_wrap_char;
line_width = 0;
line_height = 0;
word_width = 0;
word_height = 0;
line_width = 0.0;
word_width = 0.0;
}
}
line_width += word_width;
line_height = line_height > word_height ? line_height : word_height;
line_width += (word_width - character_spacing); // remove character_spacing from last character on the line
string_width = string_width > line_width ? string_width : line_width;
string_height += line_height;
line_breaks.push_back(p.info.num_characters() + 1);
string_height += max_character_height;
line_breaks.push_back(p.info.num_characters());
line_widths.push_back(line_width);
line_heights.push_back(line_height);
}
if (line_breaks.size() == 0)
{
line_breaks.push_back(p.info.num_characters() + 1);
line_breaks.push_back(p.info.num_characters());
line_widths.push_back(string_width);
line_heights.push_back(string_height);
}
p.info.set_dimensions(string_width, string_height);
unsigned int line_number = 0;
unsigned int index_to_wrap_at = line_breaks[line_number];
double line_width = line_widths[line_number];
double line_height = line_heights[line_number];
current_placement->starting_x = label_x;
if (valign == BOTTOM)
{
current_placement->starting_y = label_y;
}
else if (valign == MIDDLE)
{
current_placement->starting_y = label_y - 0.5 * (line_heights.size() - 1) * line_height ;
}
else // TOP
{
current_placement->starting_y = label_y - line_heights.size() * line_height;
}
current_placement->starting_x += boost::tuples::get<0>(p.displacement_);
current_placement->starting_y += boost::tuples::get<1>(p.displacement_);
int total_lines = line_breaks.size();
x = -line_width/2.0;
y = -line_height/2.0;
p.info.set_dimensions( string_width, (string_height + (line_spacing * (total_lines-1))) );
// if needed, adjust for desired vertical alignment
current_placement->starting_y = label_y; // no adjustment, default is MIDDLE
if (valign == TOP)
current_placement->starting_y -= 0.5 * (string_height + (line_spacing * (total_lines-1))); // move center up by 1/2 the total height
else if (valign == BOTTOM)
current_placement->starting_y += 0.5 * (string_height + (line_spacing * (total_lines-1))); // move center down by the 1/2 the total height
// correct placement for error, but BOTTOM does not need to be adjusted
// (text rendering is at text_size, but line placement is by line_height (max_character_height),
// and the rendering adds the extra space below the characters)
if (valign == TOP )
current_placement->starting_y -= (p.text_size - max_character_height); // move up by the error
else if (valign == MIDDLE)
current_placement->starting_y -= ((p.text_size - max_character_height) / 2.0); // move up by 1/2 the error
// set horizontal position to middle of text
current_placement->starting_x = label_x; // no adjustment, default is MIDDLE
if (halign == H_LEFT)
current_placement->starting_x -= 0.5 * string_width; // move center left by 1/2 the string width
else if (halign == H_RIGHT)
current_placement->starting_x += 0.5 * string_width; // move center right by 1/2 the string width
// adjust text envelope position by user's x-y displacement (dx, dy)
current_placement->starting_x += boost::tuples::get<0>(p.displacement_);
current_placement->starting_y += boost::tuples::get<1>(p.displacement_);
// presets for first line
unsigned int line_number = 0;
unsigned int index_to_wrap_at = line_breaks[0];
double line_width = line_widths[0];
// set for upper left corner of text envelope for the first line, bottom left of first character
x = -(line_width / 2.0);
y = (0.5 * (string_height + (line_spacing * (total_lines-1)))) - max_character_height;
// if needed, adjust for desired justification (J_MIDDLE is the default)
if( jalign == J_LEFT )
x = -(string_width / 2.0);
else if (jalign == J_RIGHT)
x = (string_width / 2.0) - line_width;
// save each character rendering position and build envelope as go thru loop
for (unsigned i = 0; i < p.info.num_characters(); i++)
{
character_info ci;
ci = p.info.at(i);
unsigned cwidth = ci.width + character_spacing;
double cwidth = ci.width + character_spacing;
unsigned c = ci.character;
if (i == index_to_wrap_at)
{
index_to_wrap_at = line_breaks[++line_number];
line_width = line_widths[line_number];
line_height = line_heights[line_number];
y -= line_height;
x = -line_width/2.0;
y -= (max_character_height + line_spacing); // move position down to line start
// reset to begining of line position
x = ((jalign == J_LEFT)? -(string_width / 2.0): ((jalign == J_RIGHT)? ((string_width /2.0) - line_width): -(line_width / 2.0)));
continue;
}
else
{
// place the character relative to the center of the string envelope
current_placement->add_node(c, x, y, 0.0);
// compute the Bounding Box for each character and test for:
// overlap, minimum distance or edge avoidance - exit if condition occurs
Envelope<double> e;
if (p.has_dimensions)
{
e.init(current_placement->starting_x - (p.dimensions.first/2.0),
current_placement->starting_y - (p.dimensions.second/2.0),
current_placement->starting_x + (p.dimensions.first/2.0),
e.init(current_placement->starting_x - (p.dimensions.first/2.0), // Top Left
current_placement->starting_y - (p.dimensions.second/2.0),
current_placement->starting_x + (p.dimensions.first/2.0), // Bottom Right
current_placement->starting_y + (p.dimensions.second/2.0));
}
else
{
e.init(current_placement->starting_x + x,
current_placement->starting_y - y,
current_placement->starting_x + x + ci.width,
current_placement->starting_y - y - ci.height);
e.init(current_placement->starting_x + x, // Bottom Left
current_placement->starting_y - y,
current_placement->starting_x + x + ci.width, // Top Right
current_placement->starting_y - y - max_character_height);
}
if (!dimensions_.intersects(e) ||
(!p.allow_overlap && !detector_.has_point_placement(e,
p.minimum_distance)))
if (!dimensions_.intersects(e) ||
(!p.allow_overlap && !detector_.has_point_placement(e,p.minimum_distance)))
{
return;
}
if (p.avoid_edges && !dimensions_.contains(e)) return;
p.envelopes.push(e);
}
x += cwidth;
x += cwidth; // move position to next character
}
p.placements.push_back(current_placement.release());
//update_detector(p);
}
template <typename DetectorT>
template <typename PathT>
void placement_finder<DetectorT>::find_line_placements(placement & p, PathT & shape_path)
@ -392,14 +426,14 @@ p.minimum_distance)))
double old_x = 0.0;
double old_y = 0.0;
bool first = true;
//Pre-Cache all the path_positions and path_distances
//This stops the PathT from having to do multiple re-projections if we need to reposition ourself
// and lets us know how many points are in the shape.
std::vector<vertex2d> path_positions;
std::vector<double> path_distances; // distance from node x-1 to node x
double total_distance = 0;
shape_path.rewind(0);
while (!agg::is_stop(cmd = shape_path.vertex(&new_x,&new_y))) //For each node in the shape
{
@ -422,32 +456,32 @@ p.minimum_distance)))
}
//Now path_positions is full and total_distance is correct
//shape_path shouldn't be used from here
double distance = 0.0;
std::pair<double, double> string_dimensions = p.info.get_dimensions();
double string_width = string_dimensions.first;
double displacement = boost::tuples::get<1>(p.displacement_); // displace by dy
//Calculate a target_distance that will place the labels centered evenly rather than offset from the start of the linestring
if (total_distance < string_width) //Can't place any strings
return;
//If there is no spacing then just do one label, otherwise calculate how many there should be
int num_labels = 1;
if (p.label_spacing > 0)
num_labels = static_cast<int> (floor(total_distance / (p.label_spacing + string_width)));
if (p.force_odd_labels && num_labels%2 == 0)
num_labels--;
if (num_labels <= 0)
num_labels = 1;
//Now we know how many labels we are going to place, calculate the spacing so that they will get placed evenly
double spacing = total_distance / num_labels;
double target_distance = (spacing - string_width) / 2; // first label should be placed at half the spacing
//Calculate or read out the tolerance
double tolerance_delta, tolerance;
if (p.label_position_tolerance > 0)
@ -473,12 +507,12 @@ p.minimum_distance)))
{
first = false;
}
else
else
{
//Add the length of this segment to the total we have saved up
double segment_length = path_distances[index];
distance += segment_length;
//While we have enough distance to place text in
while (distance > target_distance)
{
@ -489,11 +523,11 @@ p.minimum_distance)))
//Record details for the start of the string placement
int orientation = 0;
std::auto_ptr<placement_element> current_placement = get_placement_offset(p, path_positions, path_distances, orientation, index, segment_length - (distance - target_distance) + (diff*dir));
//We were unable to place here
if (current_placement.get() == NULL)
continue;
//Apply displacement
//NOTE: The text is centered on the line in get_placement_offset, so we are offsetting from there
if (displacement != 0)
@ -506,7 +540,7 @@ p.minimum_distance)))
anglesum += current_placement->nodes_[i].angle;
}
anglesum /= current_placement->nodes_.size(); //Now it is angle average
//Offset all the characters by this angle
for (unsigned i = 0; i < current_placement->nodes_.size(); i++)
{
@ -514,14 +548,14 @@ p.minimum_distance)))
current_placement->nodes_[i].y += displacement*sin(anglesum+M_PI/2);
}
}
bool status = test_placement(p, current_placement, orientation);
if (status) //We have successfully placed one
{
p.placements.push_back(current_placement.release());
update_detector(p);
//Totally break out of the loops
diff = tolerance;
break;
@ -532,23 +566,23 @@ p.minimum_distance)))
while (!p.envelopes.empty())
p.envelopes.pop();
}
//Don't need to loop twice when diff = 0
if (diff == 0)
break;
}
}
distance -= target_distance; //Consume the spacing gap we have used up
target_distance = spacing; //Need to reset the target_distance as it is spacing/2 for the first label.
}
}
old_x = new_x;
old_y = new_y;
}
}
template <typename DetectorT>
std::auto_ptr<placement_element> placement_finder<DetectorT>::get_placement_offset(placement & p, const std::vector<vertex2d> &path_positions, const std::vector<double> &path_distances, int &orientation, unsigned index, double distance)
{
@ -560,7 +594,7 @@ p.minimum_distance)))
}
if (index <= 1 && distance < 0) //We've gone off the start, fail out
return std::auto_ptr<placement_element>(NULL);
//Same thing, checking if we go off the end
while (index < path_distances.size() && distance > path_distances[index])
{
@ -569,46 +603,46 @@ p.minimum_distance)))
}
if (index >= path_distances.size())
return std::auto_ptr<placement_element>(NULL);
//Keep track of the initial index,distance incase we need to re-call get_placement_offset
const unsigned initial_index = index;
const double initial_distance = distance;
std::auto_ptr<placement_element> current_placement(new placement_element);
double string_height = p.info.get_dimensions().second;
double old_x = path_positions[index-1].x;
double old_y = path_positions[index-1].y;
double new_x = path_positions[index].x;
double new_y = path_positions[index].y;
double dx = new_x - old_x;
double dy = new_y - old_y;
double segment_length = path_distances[index];
if (segment_length == 0) {
// Not allowed to place across on 0 length segments or discontinuities
return std::auto_ptr<placement_element>(NULL);
}
current_placement->starting_x = old_x + dx*distance/segment_length;
current_placement->starting_y = old_y + dy*distance/segment_length;
double angle = atan2(-dy, dx);
bool orientation_forced = (orientation != 0); //Wether the orientation was set by the caller
if (!orientation_forced)
orientation = (angle > 0.55*M_PI || angle < -0.45*M_PI) ? -1 : 1;
unsigned upside_down_char_count = 0; //Count of characters that are placed upside down.
for (unsigned i = 0; i < p.info.num_characters(); ++i)
{
character_info ci;
unsigned c;
double last_character_angle = angle;
// grab the next character according to the orientation
ci = orientation > 0 ? p.info.at(i) : p.info.at(p.info.num_characters() - i - 1);
c = ci.character;
@ -623,12 +657,12 @@ p.minimum_distance)))
//Coordinates this character ends at, calculated below
double end_x = 0;
double end_y = 0;
if (segment_length - distance >= ci.width)
if (segment_length - distance >= ci.width)
{
//if the distance remaining in this segment is enough, we just go further along the segment
distance += ci.width;
end_x = old_x + dx*distance/segment_length;
end_y = old_y + dy*distance/segment_length;
}
@ -650,24 +684,24 @@ p.minimum_distance)))
new_y = path_positions[index].y;
dx = new_x - old_x;
dy = new_y - old_y;
segment_length = path_distances[index];
}
while (std::sqrt(std::pow(start_x - new_x, 2) + std::pow(start_y - new_y, 2)) < ci.width); //Distance from start_ to new_
//Calculate the position to place the end of the character on
find_line_circle_intersection(
start_x, start_y, ci.width,
old_x, old_y, new_x, new_y,
start_x, start_y, ci.width,
old_x, old_y, new_x, new_y,
end_x, end_y); //results are stored in end_x, end_y
//Need to calculate distance on the new segment
distance = std::sqrt(std::pow(old_x - end_x, 2) + std::pow(old_y - end_y, 2));
}
//Calculate angle from the start of the character to the end based on start_/end_ position
angle = atan2(start_y-end_y, end_x-start_x);
//Test last_character_angle vs angle
// since our rendering angle has changed then check against our
// max allowable angle change.
@ -677,22 +711,22 @@ p.minimum_distance)))
angle_delta -= 2*M_PI;
while (angle_delta < -M_PI)
angle_delta += 2*M_PI;
if (p.max_char_angle_delta > 0 &&
if (p.max_char_angle_delta > 0 &&
fabs(angle_delta) > p.max_char_angle_delta*(M_PI/180))
{
//std::clog << "FAIL: Too Bendy!" << std::endl;
return std::auto_ptr<placement_element>(NULL);
}
double render_angle = angle;
double render_x = start_x;
double render_y = start_y;
//Center the text on the line
render_x -= (((double)string_height/2.0) - 1.0)*cos(render_angle+M_PI/2);
render_y += (((double)string_height/2.0) - 1.0)*sin(render_angle+M_PI/2);
if (orientation < 0)
{
// rotate in place
@ -700,21 +734,21 @@ p.minimum_distance)))
render_y -= ci.width*sin(render_angle) + (string_height-2)*cos(render_angle);
render_angle += M_PI;
}
current_placement->add_node(c,render_x - current_placement->starting_x,
-render_y + current_placement->starting_y,
current_placement->add_node(c,render_x - current_placement->starting_x,
-render_y + current_placement->starting_y,
render_angle);
//Normalise to 0 <= angle < 2PI
while (render_angle >= 2*M_PI)
render_angle -= 2*M_PI;
while (render_angle < 0)
render_angle += 2*M_PI;
if (render_angle > M_PI/2 && render_angle < 1.5*M_PI)
upside_down_char_count++;
}
//If we placed too many characters upside down
//If we placed too many characters upside down
if (upside_down_char_count >= p.info.num_characters()/2.0)
{
//if we auto-detected the orientation then retry with the opposite orientation
@ -730,15 +764,15 @@ p.minimum_distance)))
return std::auto_ptr<placement_element>(NULL);
}
}
return current_placement;
}
template <typename DetectorT>
bool placement_finder<DetectorT>::test_placement(placement & p, const std::auto_ptr<placement_element> & current_placement, const int & orientation)
{
std::pair<double, double> string_dimensions = p.info.get_dimensions();
double string_height = string_dimensions.second;
@ -769,15 +803,15 @@ p.minimum_distance)))
else
{
// put four corners of the letter into envelope
e.init(x, y, x + ci.width*cos(angle),
e.init(x, y, x + ci.width*cos(angle),
y - ci.width*sin(angle));
e.expand_to_include(x - ci.height*sin(angle),
e.expand_to_include(x - ci.height*sin(angle),
y - ci.height*cos(angle));
e.expand_to_include(x + (ci.width*cos(angle) - ci.height*sin(angle)),
e.expand_to_include(x + (ci.width*cos(angle) - ci.height*sin(angle)),
y - (ci.width*sin(angle) + ci.height*cos(angle)));
}
if (!dimensions_.intersects(e) ||
if (!dimensions_.intersects(e) ||
!detector_.has_placement(e, p.info.get_string(), p.minimum_distance))
{
//std::clog << "No Intersects:" << !dimensions_.intersects(e) << ": " << e << " @ " << dimensions_ << std::endl;
@ -785,7 +819,7 @@ p.minimum_distance)))
status = false;
break;
}
if (p.avoid_edges && !dimensions_.contains(e))
{
//std::clog << "Fail avoid edges" << std::endl;
@ -795,25 +829,25 @@ p.minimum_distance)))
p.envelopes.push(e);
}
current_placement->rewind();
return status;
}
template <typename DetectorT>
void placement_finder<DetectorT>::find_line_circle_intersection(
const double &cx, const double &cy, const double &radius,
const double &x1, const double &y1, const double &x2, const double &y2,
const double &cx, const double &cy, const double &radius,
const double &x1, const double &y1, const double &x2, const double &y2,
double &ix, double &iy)
{
double dx = x2 - x1;
double dy = y2 - y1;
double A = dx * dx + dy * dy;
double B = 2 * (dx * (x1 - cx) + dy * (y1 - cy));
double C = (x1 - cx) * (x1 - cx) + (y1 - cy) * (y1 - cy) - radius * radius;
double det = B * B - 4 * A * C;
if (A <= 0.0000001 || det < 0)
{
@ -833,17 +867,17 @@ p.minimum_distance)))
else
{
//Two solutions.
//Always use the 1st one
//We only really have one solution here, as we know the line segment will start in the circle and end outside
double t = (-B + std::sqrt(det)) / (2 * A);
ix = x1 + t * dx;
iy = y1 + t * dy;
//t = (-B - std::sqrt(det)) / (2 * A);
//ix = x1 + t * dx;
//iy = y1 + t * dy;
return;
}
}
@ -864,12 +898,12 @@ p.minimum_distance)))
{
detector_.clear();
}
typedef coord_transform2<CoordTransform,geometry2d> PathType;
typedef label_collision_detector4 DetectorType;
template class placement_finder<DetectorType>;
template void placement_finder<DetectorType>::find_point_placements<PathType> (placement&, PathType & );
template void placement_finder<DetectorType>::find_line_placements<PathType> (placement&, PathType & );
} // namespace

View file

@ -1,5 +1,5 @@
/*****************************************************************************
*
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2006 Artem Pavlenko
@ -36,7 +36,7 @@
// stl
#include <iostream>
namespace mapnik
namespace mapnik
{
using boost::property_tree::ptree;
using boost::optional;
@ -44,7 +44,7 @@ namespace mapnik
class serialize_symbolizer : public boost::static_visitor<>
{
public:
serialize_symbolizer( ptree & r , bool explicit_defaults):
serialize_symbolizer( ptree & r , bool explicit_defaults):
rule_(r),
explicit_defaults_(explicit_defaults) {}
@ -65,23 +65,23 @@ namespace mapnik
if ( strk.get_color() != dfl.get_color() || explicit_defaults_ )
{
set_css( sym_node, "stroke", strk.get_color() );
set_css( sym_node, "stroke", strk.get_color() );
}
if ( strk.get_width() != dfl.get_width() || explicit_defaults_ )
{
set_css( sym_node, "stroke-width", strk.get_width() );
set_css( sym_node, "stroke-width", strk.get_width() );
}
if ( strk.get_opacity() != dfl.get_opacity() || explicit_defaults_ )
{
set_css( sym_node, "stroke-opacity", strk.get_opacity() );
set_css( sym_node, "stroke-opacity", strk.get_opacity() );
}
if ( strk.get_line_join() != dfl.get_line_join() || explicit_defaults_ )
{
set_css( sym_node, "stroke-linejoin", strk.get_line_join() );
set_css( sym_node, "stroke-linejoin", strk.get_line_join() );
}
if ( strk.get_line_cap() != dfl.get_line_cap() || explicit_defaults_ )
{
set_css( sym_node, "stroke-linecap", strk.get_line_cap() );
set_css( sym_node, "stroke-linecap", strk.get_line_cap() );
}
if ( ! strk.get_dash_array().empty() )
{
@ -91,7 +91,7 @@ namespace mapnik
os << dashes[i].first << ", " << dashes[i].second;
if ( i + 1 < dashes.size() ) os << ", ";
}
set_css( sym_node, "stroke-dasharray", os.str() );
set_css( sym_node, "stroke-dasharray", os.str() );
}
}
@ -112,11 +112,11 @@ namespace mapnik
if ( sym.get_fill() != dfl.get_fill() || explicit_defaults_ )
{
set_css( sym_node, "fill", sym.get_fill() );
set_css( sym_node, "fill", sym.get_fill() );
}
if ( sym.get_opacity() != dfl.get_opacity() || explicit_defaults_ )
{
set_css( sym_node, "fill-opacity", sym.get_opacity() );
set_css( sym_node, "fill-opacity", sym.get_opacity() );
}
}
@ -157,6 +157,20 @@ namespace mapnik
add_font_attributes( sym_node, sym);
add_image_attributes( sym_node, sym);
// pseudo-default-construct a shield_symbolizer. It is used
// to avoid printing of attributes with default values without
// repeating the default values here.
// maybe add a real, explicit default-ctor?
shield_symbolizer sym_dfl("<no default>", "<no default>", 0, color(0,0,0), "<no default>", "<no default>", 0, 0 );
if (sym.get_unlock_image() != sym_dfl.get_unlock_image() || explicit_defaults_ )
{
set_attr( sym_node, "unlock_image", sym.get_unlock_image() );
}
if (sym.get_no_text() != sym_dfl.get_no_text() || explicit_defaults_ )
{
set_attr( sym_node, "no_text", sym.get_no_text() );
}
}
void operator () ( const text_symbolizer & sym )
@ -177,15 +191,15 @@ namespace mapnik
if ( sym.get_fill() != dfl.get_fill() || explicit_defaults_ )
{
set_css( sym_node, "fill", sym.get_fill() );
set_css( sym_node, "fill", sym.get_fill() );
}
if ( sym.get_opacity() != dfl.get_opacity() || explicit_defaults_ )
{
set_css( sym_node, "fill-opacity", sym.get_opacity() );
set_css( sym_node, "fill-opacity", sym.get_opacity() );
}
if ( sym.height() != dfl.height() || explicit_defaults_ )
{
set_css( sym_node, "height", sym.height() );
set_css( sym_node, "height", sym.height() );
}
}
@ -221,22 +235,22 @@ namespace mapnik
{
const std::string & name = sym.get_name();
if ( ! name.empty() ) {
set_attr( node, "name", name );
set_attr( node, "name", name );
}
const std::string & face_name = sym.get_face_name();
if ( ! face_name.empty() ) {
set_attr( node, "face_name", face_name );
set_attr( node, "face_name", face_name );
}
const std::string & fontset_name = sym.get_fontset().get_name();
if ( ! fontset_name.empty() ) {
set_attr( node, "fontset_name", fontset_name );
}
set_attr( node, "size", sym.get_text_size() );
set_attr( node, "fill", sym.get_fill() );
set_attr( node, "size", sym.get_text_size() );
set_attr( node, "fill", sym.get_fill() );
// pseudo-default-construct a text_symbolizer. It is used
// to avoid printing ofattributes with default values without
// to avoid printing ofattributes with default values without
// repeating the default values here.
// maybe add a real, explicit default-ctor?
text_symbolizer dfl("<no default>", "<no default>",
@ -245,39 +259,43 @@ namespace mapnik
position displacement = sym.get_displacement();
if ( displacement.get<0>() != dfl.get_displacement().get<0>() || explicit_defaults_ )
{
set_attr( node, "dx", displacement.get<0>() );
set_attr( node, "dx", displacement.get<0>() );
}
if ( displacement.get<1>() != dfl.get_displacement().get<1>() || explicit_defaults_ )
{
set_attr( node, "dy", displacement.get<1>() );
set_attr( node, "dy", displacement.get<1>() );
}
if (sym.get_label_placement() != dfl.get_label_placement() || explicit_defaults_ )
{
set_attr( node, "placement", sym.get_label_placement() );
set_attr( node, "placement", sym.get_label_placement() );
}
if (sym.get_vertical_alignment() != dfl.get_vertical_alignment() || explicit_defaults_ )
{
set_attr( node, "vertical_alignment", sym.get_vertical_alignment() );
set_attr( node, "vertical_alignment", sym.get_vertical_alignment() );
}
if (sym.get_halo_radius() != dfl.get_halo_radius() || explicit_defaults_ )
{
set_attr( node, "halo_radius", sym.get_halo_radius() );
set_attr( node, "halo_radius", sym.get_halo_radius() );
}
const color & c = sym.get_halo_fill();
if ( c != dfl.get_halo_fill() || explicit_defaults_ )
{
set_attr( node, "halo_fill", c );
set_attr( node, "halo_fill", c );
}
if (sym.get_text_ratio() != dfl.get_text_ratio() || explicit_defaults_ )
{
set_attr( node, "text_ratio", sym.get_text_ratio() );
set_attr( node, "text_ratio", sym.get_text_ratio() );
}
if (sym.get_wrap_width() != dfl.get_wrap_width() || explicit_defaults_ )
{
set_attr( node, "wrap_width", sym.get_wrap_width() );
set_attr( node, "wrap_width", sym.get_wrap_width() );
}
if (sym.get_wrap_before() != dfl.get_wrap_before() || explicit_defaults_ )
{
set_attr( node, "wrap_before", sym.get_wrap_before() );
}
if (sym.get_wrap_char() != dfl.get_wrap_char() || explicit_defaults_ )
{
@ -289,27 +307,39 @@ namespace mapnik
}
if (sym.get_line_spacing() != dfl.get_line_spacing() || explicit_defaults_ )
{
set_attr( node, "line_spacing", sym.get_line_spacing() );
set_attr( node, "line_spacing", sym.get_line_spacing() );
}
if (sym.get_character_spacing() != dfl.get_character_spacing() || explicit_defaults_ )
{
set_attr( node, "character_spacing", sym.get_character_spacing() );
set_attr( node, "character_spacing", sym.get_character_spacing() );
}
if (sym.get_label_spacing() != dfl.get_label_spacing() || explicit_defaults_ )
{
set_attr( node, "spacing", sym.get_label_spacing() );
set_attr( node, "spacing", sym.get_label_spacing() );
}
if (sym.get_minimum_distance() != dfl.get_minimum_distance() || explicit_defaults_ )
{
set_attr( node, "min_distance", sym.get_minimum_distance() );
set_attr( node, "min_distance", sym.get_minimum_distance() );
}
if (sym.get_allow_overlap() != dfl.get_allow_overlap() || explicit_defaults_ )
{
set_attr( node, "allow_overlap", sym.get_allow_overlap() );
set_attr( node, "allow_overlap", sym.get_allow_overlap() );
}
if (sym.get_avoid_edges() != dfl.get_avoid_edges() || explicit_defaults_ )
{
set_attr( node, "avoid_edges", sym.get_avoid_edges() );
set_attr( node, "avoid_edges", sym.get_avoid_edges() );
}
if (sym.get_opacity() != dfl.get_opacity() || explicit_defaults_ )
{
set_attr( node, "opacity", sym.get_opacity() );
}
if (sym.get_horizontal_alignment() != dfl.get_horizontal_alignment() || explicit_defaults_ )
{
set_attr( node, "horizontal_alignment", sym.get_horizontal_alignment() );
}
if (sym.get_justify_alignment() != dfl.get_justify_alignment() || explicit_defaults_ )
{
set_attr( node, "justify_alignment", sym.get_justify_alignment() );
}
}
ptree & rule_;
@ -320,7 +350,7 @@ namespace mapnik
{
ptree & rule_node = style_node.push_back(
ptree::value_type("Rule", ptree() ))->second;
rule_type dfl;
if ( rule.get_name() != dfl.get_name() )
{
@ -372,7 +402,7 @@ namespace mapnik
{
const feature_type_style & style = style_it->second;
const std::string & name = style_it->first;
ptree & style_node = map_node.push_back(
ptree::value_type("Style", ptree()))->second;
@ -382,7 +412,7 @@ namespace mapnik
rules::const_iterator end = style.get_rules().end();
for (; it != end; ++it)
{
serialize_rule( style_node, * it , explicit_defaults);
serialize_rule( style_node, * it , explicit_defaults);
}
}
@ -418,15 +448,15 @@ namespace mapnik
for (; it != end; ++it)
{
boost::property_tree::ptree & param_node = datasource_node.push_back(
boost::property_tree::ptree::value_type("Parameter",
boost::property_tree::ptree::value_type("Parameter",
boost::property_tree::ptree()))->second;
param_node.put("<xmlattr>.name", it->first );
param_node.put_own( it->second );
}
}
void serialize_layer( ptree & map_node, const Layer & layer )
void serialize_layer( ptree & map_node, const Layer & layer )
{
ptree & layer_node = map_node.push_back(
ptree::value_type("Layer", ptree()))->second;
@ -449,7 +479,7 @@ namespace mapnik
{
set_attr( layer_node, "srs", layer.srs() );
}
set_attr/*<bool>*/( layer_node, "status", layer.isActive() );
set_attr/*<bool>*/( layer_node, "clear_label_cache", layer.clear_label_cache() );
@ -467,7 +497,7 @@ namespace mapnik
{
set_attr( layer_node, "queryable", layer.isQueryable() );
}
std::vector<std::string> const& style_names = layer.styles();
for (unsigned i = 0; i < style_names.size(); ++i)
{
@ -490,11 +520,11 @@ namespace mapnik
ptree & map_node = pt.push_back(ptree::value_type("Map", ptree() ))->second;
set_attr( map_node, "srs", map.srs() );
optional<color> c = map.background();
if ( c )
{
set_attr( map_node, "bgcolor", * c );
set_attr( map_node, "bgcolor", * c );
}
{
@ -517,9 +547,9 @@ namespace mapnik
for (unsigned i = 0; i < layers.size(); ++i )
{
serialize_layer( map_node, layers[i] );
}
}
}
void save_map(Map const & map, std::string const& filename, bool explicit_defaults)
{
ptree pt;

View file

@ -1,5 +1,5 @@
/*****************************************************************************
*
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2006 Artem Pavlenko
@ -38,25 +38,49 @@ namespace mapnik
std::string const& name,
std::string const& face_name,
unsigned size,
color const& fill,
color const& fill,
std::string const& file,
std::string const& type,
unsigned width,unsigned height)
: text_symbolizer(name, face_name, size, fill),
symbolizer_with_image( file, type, width, height )
symbolizer_with_image( file, type, width, height ),
unlock_image_(false),
no_text_(false)
{
}
shield_symbolizer::shield_symbolizer(
std::string const& name,
unsigned size,
color const& fill,
color const& fill,
std::string const& file,
std::string const& type,
unsigned width,unsigned height)
: text_symbolizer(name, size, fill),
symbolizer_with_image( file, type, width, height )
symbolizer_with_image( file, type, width, height ),
unlock_image_(false),
no_text_(false)
{
}
void shield_symbolizer::set_unlock_image(bool unlock_image)
{
unlock_image_ = unlock_image;
}
bool shield_symbolizer::get_unlock_image() const
{
return unlock_image_;
}
void shield_symbolizer::set_no_text(bool no_text)
{
no_text_ = no_text;
}
bool shield_symbolizer::get_no_text() const
{
return no_text_;
}
}

View file

@ -1,5 +1,5 @@
/*****************************************************************************
*
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2006 Artem Pavlenko
@ -32,6 +32,7 @@
static const char * label_placement_strings[] = {
"point",
"line",
"vertex",
""
};
@ -48,6 +49,26 @@ static const char * vertical_alignment_strings[] = {
IMPLEMENT_ENUM( mapnik::vertical_alignment_e, vertical_alignment_strings );
static const char * horizontal_alignment_strings[] = {
"left",
"middle",
"right",
""
};
IMPLEMENT_ENUM( mapnik::horizontal_alignment_e, horizontal_alignment_strings );
static const char * justify_alignment_strings[] = {
"left",
"middle",
"right",
""
};
IMPLEMENT_ENUM( mapnik::justify_alignment_e, justify_alignment_strings );
static const char * text_convert_strings[] = {
"none",
"toupper",
@ -85,7 +106,12 @@ namespace mapnik
displacement_(0.0,0.0),
avoid_edges_(false),
minimum_distance_(0.0),
overlap_(false) {}
overlap_(false),
opacity_(1.0),
wrap_before_(false),
halign_(H_MIDDLE),
jalign_(J_MIDDLE) {}
text_symbolizer::text_symbolizer(std::string const& name, unsigned size, color const& fill)
: name_(name),
//face_name_(""),
@ -110,7 +136,12 @@ namespace mapnik
displacement_(0.0,0.0),
avoid_edges_(false),
minimum_distance_(0.0),
overlap_(false) {}
overlap_(false),
opacity_(1.0),
wrap_before_(false),
halign_(H_MIDDLE),
jalign_(J_MIDDLE) {}
text_symbolizer::text_symbolizer(text_symbolizer const& rhs)
: name_(rhs.name_),
face_name_(rhs.face_name_),
@ -135,8 +166,12 @@ namespace mapnik
displacement_(rhs.displacement_),
avoid_edges_(rhs.avoid_edges_),
minimum_distance_(rhs.minimum_distance_),
overlap_(rhs.overlap_) {}
overlap_(rhs.overlap_),
opacity_(rhs.opacity_),
wrap_before_(rhs.wrap_before_),
halign_(rhs.halign_),
jalign_(rhs.jalign_) {}
text_symbolizer& text_symbolizer::operator=(text_symbolizer const& other)
{
if (this == &other)
@ -161,28 +196,32 @@ namespace mapnik
label_p_ = other.label_p_;
valign_ = other.valign_;
anchor_ = other.anchor_;
displacement_ = other.displacement_;
displacement_ = other.displacement_;
avoid_edges_ = other.avoid_edges_;
minimum_distance_ = other.minimum_distance_;
overlap_ = other.overlap_;
opacity_ = other.opacity_;
wrap_before_ = other.wrap_before_;
halign_ = other.halign_;
jalign_ = other.jalign_;
return *this;
}
}
std::string const& text_symbolizer::get_name() const
{
return name_;
}
void text_symbolizer::set_name(std::string name)
{
name_ = name;
}
std::string const& text_symbolizer::get_face_name() const
{
return face_name_;
}
void text_symbolizer::set_face_name(std::string face_name)
{
face_name_ = face_name;
@ -203,7 +242,7 @@ namespace mapnik
return text_ratio_;
}
void text_symbolizer::set_text_ratio(unsigned ratio)
void text_symbolizer::set_text_ratio(unsigned ratio)
{
text_ratio_ = ratio;
}
@ -213,10 +252,20 @@ namespace mapnik
return wrap_width_;
}
void text_symbolizer::set_wrap_width(unsigned width)
void text_symbolizer::set_wrap_width(unsigned width)
{
wrap_width_ = width;
}
}
bool text_symbolizer::get_wrap_before() const
{
return wrap_before_;
}
void text_symbolizer::set_wrap_before(bool wrap_before)
{
wrap_before_ = wrap_before;
}
unsigned char text_symbolizer::get_wrap_char() const
{
@ -228,16 +277,16 @@ namespace mapnik
return std::string(1, wrap_char_);
}
void text_symbolizer::set_wrap_char(unsigned char character)
void text_symbolizer::set_wrap_char(unsigned char character)
{
wrap_char_ = character;
}
}
void text_symbolizer::set_wrap_char_from_string(std::string character)
void text_symbolizer::set_wrap_char_from_string(std::string character)
{
wrap_char_ = (character)[0];
}
text_convert_e text_symbolizer::get_text_convert() const
{
return text_convert_;
@ -253,7 +302,7 @@ namespace mapnik
return line_spacing_;
}
void text_symbolizer::set_line_spacing(unsigned spacing)
void text_symbolizer::set_line_spacing(unsigned spacing)
{
line_spacing_ = spacing;
}
@ -263,7 +312,7 @@ namespace mapnik
return character_spacing_;
}
void text_symbolizer::set_character_spacing(unsigned spacing)
void text_symbolizer::set_character_spacing(unsigned spacing)
{
character_spacing_ = spacing;
}
@ -273,7 +322,7 @@ namespace mapnik
return label_spacing_;
}
void text_symbolizer::set_label_spacing(unsigned spacing)
void text_symbolizer::set_label_spacing(unsigned spacing)
{
label_spacing_ = spacing;
}
@ -293,7 +342,7 @@ namespace mapnik
return force_odd_labels_;
}
void text_symbolizer::set_force_odd_labels(bool force)
void text_symbolizer::set_force_odd_labels(bool force)
{
force_odd_labels_ = force;
}
@ -303,7 +352,7 @@ namespace mapnik
return max_char_angle_delta_;
}
void text_symbolizer::set_max_char_angle_delta(double angle)
void text_symbolizer::set_max_char_angle_delta(double angle)
{
max_char_angle_delta_ = angle;
}
@ -322,12 +371,12 @@ namespace mapnik
{
fill_ = fill;
}
color const& text_symbolizer::get_fill() const
{
return fill_;
}
void text_symbolizer::set_halo_fill(color const& fill)
{
halo_fill_ = fill;
@ -337,22 +386,22 @@ namespace mapnik
{
return halo_fill_;
}
void text_symbolizer::set_halo_radius(unsigned radius)
{
halo_radius_ = radius;
}
unsigned text_symbolizer::get_halo_radius() const
{
return halo_radius_;
}
void text_symbolizer::set_label_placement(label_placement_e label_p)
{
label_p_ = label_p;
}
label_placement_e text_symbolizer::get_label_placement() const
{
return label_p_;
@ -362,37 +411,37 @@ namespace mapnik
{
valign_ = valign;
}
vertical_alignment_e text_symbolizer::get_vertical_alignment() const
{
return valign_;
}
void text_symbolizer::set_anchor(double x, double y)
{
anchor_ = boost::make_tuple(x,y);
}
position const& text_symbolizer::get_anchor() const
{
return anchor_;
}
void text_symbolizer::set_displacement(double x, double y)
{
displacement_ = boost::make_tuple(x,y);
}
position const& text_symbolizer::get_displacement() const
{
return displacement_;
}
bool text_symbolizer::get_avoid_edges() const
{
return avoid_edges_;
}
void text_symbolizer::set_avoid_edges(bool avoid)
{
avoid_edges_ = avoid;
@ -402,7 +451,7 @@ namespace mapnik
{
return minimum_distance_;
}
void text_symbolizer::set_minimum_distance(double distance)
{
minimum_distance_ = distance;
@ -412,9 +461,39 @@ namespace mapnik
{
overlap_ = overlap;
}
bool text_symbolizer::get_allow_overlap() const
{
return overlap_;
}
void text_symbolizer::set_opacity(double opacity)
{
opacity_ = opacity;
}
double text_symbolizer::get_opacity() const
{
return opacity_;
}
void text_symbolizer::set_horizontal_alignment(horizontal_alignment_e halign)
{
halign_ = halign;
}
horizontal_alignment_e text_symbolizer::get_horizontal_alignment() const
{
return halign_;
}
void text_symbolizer::set_justify_alignment(justify_alignment_e jalign)
{
jalign_ = jalign;
}
justify_alignment_e text_symbolizer::get_justify_alignment() const
{
return jalign_;
}
}