Created
May 25, 2011 22:32
-
-
Save springmeyer/992150 to your computer and use it in GitHub Desktop.
line offsets in mapnik
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Index: bindings/python/mapnik_stroke.cpp | |
=================================================================== | |
--- bindings/python/mapnik_stroke.cpp (revision 2900) | |
+++ bindings/python/mapnik_stroke.cpp (working copy) | |
@@ -68,14 +68,15 @@ | |
dashes, | |
s.get_line_cap(), | |
s.get_line_join(), | |
- s.get_gamma()); | |
+ s.get_gamma(), | |
+ s.get_offset()); | |
} | |
static void | |
setstate (stroke& s, boost::python::tuple state) | |
{ | |
using namespace boost::python; | |
- if (len(state) != 5) | |
+ if (len(state) != 6) | |
{ | |
PyErr_SetObject(PyExc_ValueError, | |
("expected 5-item tuple in call to __setstate__; got %s" | |
@@ -99,6 +100,7 @@ | |
s.set_line_cap(extract<line_cap_e>(state[2])); | |
s.set_line_join(extract<line_join_e>(state[3])); | |
s.set_gamma(extract<double>(state[4])); | |
+ s.set_offset(extract<double>(state[5])); | |
} | |
@@ -159,6 +161,10 @@ | |
&stroke::get_line_join, | |
&stroke::set_line_join, | |
"Returns the line join mode of this stroke.\n") | |
+ .add_property("offset", | |
+ &stroke::get_offset, | |
+ &stroke::set_offset, | |
+ "Gets or sets the offset of this stroke will be rendered with.\n") | |
// todo consider providing a single get/set property | |
.def("add_dash",&stroke::add_dash, | |
(arg("length"),arg("gap")), | |
Index: include/mapnik/ctrans.hpp | |
=================================================================== | |
--- include/mapnik/ctrans.hpp (revision 2900) | |
+++ include/mapnik/ctrans.hpp (working copy) | |
@@ -1,5 +1,5 @@ | |
/***************************************************************************** | |
- * | |
+ * | |
* This file is part of Mapnik (c++ mapping toolkit) | |
* | |
* Copyright (C) 2006 Artem Pavlenko | |
@@ -25,52 +25,60 @@ | |
#ifndef CTRANS_HPP | |
#define CTRANS_HPP | |
-#include <algorithm> | |
- | |
+// mapnik | |
#include <mapnik/box2d.hpp> | |
#include <mapnik/vertex.hpp> | |
#include <mapnik/coord_array.hpp> | |
#include <mapnik/proj_transform.hpp> | |
+// boost | |
+#include <boost/math/constants/constants.hpp> | |
+ | |
+// stl | |
+#include <algorithm> | |
+ | |
+const double pi = boost::math::constants::pi<double>(); | |
+const double pi_by_2 = pi/2.0; | |
+ | |
namespace mapnik { | |
typedef coord_array<coord2d> CoordinateArray; | |
- | |
+ | |
template <typename Transform,typename Geometry> | |
struct MAPNIK_DECL coord_transform | |
{ | |
coord_transform(Transform const& t, Geometry& geom) | |
: t_(t), geom_(geom) {} | |
- | |
+ | |
unsigned vertex(double *x , double *y) const | |
{ | |
unsigned command = geom_.vertex(x,y); | |
t_.forward(x,y); | |
return command; | |
} | |
- | |
+ | |
void rewind (unsigned pos) | |
{ | |
geom_.rewind(pos); | |
} | |
- | |
+ | |
private: | |
Transform const& t_; | |
Geometry& geom_; | |
}; | |
template <typename Transform,typename Geometry> | |
-struct MAPNIK_DECL coord_transform2 | |
+struct MAPNIK_DECL _coord_transform2 | |
{ | |
typedef std::size_t size_type; | |
typedef typename Geometry::value_type value_type; | |
- coord_transform2(Transform const& t, | |
- Geometry const& geom, | |
+ _coord_transform2(Transform const& t, | |
+ Geometry const& geom, | |
proj_transform const& prj_trans) | |
- : t_(t), | |
- geom_(geom), | |
+ : t_(t), | |
+ geom_(geom), | |
prj_trans_(prj_trans) {} | |
- | |
+ | |
unsigned vertex(double * x , double * y) const | |
{ | |
unsigned command(SEG_MOVETO); | |
@@ -102,7 +110,7 @@ | |
t_.forward(x,y); | |
return command; | |
}*/ | |
- | |
+ | |
void rewind (unsigned pos) | |
{ | |
geom_.rewind(pos); | |
@@ -112,26 +120,26 @@ | |
{ | |
return geom_; | |
} | |
- | |
+ | |
private: | |
Transform const& t_; | |
Geometry const& geom_; | |
proj_transform const& prj_trans_; | |
}; | |
- | |
+ | |
template <typename Transform,typename Geometry> | |
struct MAPNIK_DECL coord_transform3 | |
{ | |
- coord_transform3(Transform const& t, | |
- Geometry const& geom, | |
+ coord_transform3(Transform const& t, | |
+ Geometry const& geom, | |
proj_transform const& prj_trans, | |
int dx, int dy) | |
- : t_(t), | |
- geom_(geom), | |
+ : t_(t), | |
+ geom_(geom), | |
prj_trans_(prj_trans), | |
dx_(dx), dy_(dy) {} | |
- | |
+ | |
unsigned vertex(double * x , double * y) const | |
{ | |
unsigned command = geom_.vertex(x,y); | |
@@ -142,12 +150,12 @@ | |
*y+=dy_; | |
return command; | |
} | |
- | |
+ | |
void rewind (unsigned pos) | |
{ | |
geom_.rewind(pos); | |
} | |
- | |
+ | |
private: | |
Transform const& t_; | |
Geometry const& geom_; | |
@@ -155,7 +163,226 @@ | |
int dx_; | |
int dy_; | |
}; | |
+ | |
+ | |
+template <typename Transform,typename Geometry> | |
+struct MAPNIK_DECL coord_transform2 | |
+{ | |
+ typedef std::size_t size_type; | |
+ typedef typename Geometry::value_type value_type; | |
+ | |
+ coord_transform2(Transform const& t, | |
+ Geometry const& geom, | |
+ proj_transform const& prj_trans ) | |
+ : t_(t), | |
+ geom_(geom), | |
+ prj_trans_(prj_trans), | |
+ offset_(0.0), | |
+ threshold_(10), | |
+ m_status(initial) {} | |
+ enum status | |
+ { | |
+ initial, | |
+ start, | |
+ first, | |
+ process, | |
+ last_vertex, | |
+ angle_joint, | |
+ end | |
+ }; | |
+ | |
+ double get_offset() const | |
+ { | |
+ return offset_; | |
+ } | |
+ | |
+ void set_offset(double offset) | |
+ { | |
+ offset_ = offset; | |
+ } | |
+ | |
+ unsigned int get_threshold() const | |
+ { | |
+ return threshold_; | |
+ } | |
+ | |
+ void set_threshold(unsigned int t) | |
+ { | |
+ threshold_ = t; | |
+ } | |
+ | |
+ unsigned vertex(double * x , double * y) | |
+ { | |
+ double z=0; | |
+ | |
+ if (offset_==0.0) | |
+ { | |
+ unsigned command = geom_.vertex(x,y); | |
+ prj_trans_.backward(*x,*y,z); | |
+ t_.forward(x,y); | |
+ return command; | |
+ } | |
+ else | |
+ { | |
+ while(true){ | |
+ switch(m_status) | |
+ { | |
+ case end: | |
+ return SEG_END; | |
+ break; | |
+ case initial: | |
+ m_pre_cmd = geom_.vertex(x,y); | |
+ prj_trans_.backward(*x,*y,z); | |
+ t_.forward(x,y); | |
+ m_pre_x = *x; | |
+ m_pre_y = *y; | |
+ //m_status = (m_pre_cmd!=SEG_END)?start:end; // | |
+ case start: | |
+ m_cur_cmd = geom_.vertex(&m_cur_x, &m_cur_y); | |
+ prj_trans_.backward(m_cur_x,m_cur_y,z); | |
+ t_.forward(&m_cur_x,&m_cur_y); | |
+ case first: | |
+ angle_a = atan2((m_pre_y-m_cur_y),(m_pre_x-m_cur_x)); | |
+ dx_pre = cos(angle_a + pi_by_2); | |
+ dy_pre = sin(angle_a + pi_by_2); | |
+ #ifdef MAPNIK_DEBUG | |
+ std::clog << "offsetting line by: " << offset_ << "\n"; | |
+ std::clog << "initial dx=" << (dx_pre * offset_) << " dy=" << (dy_pre * offset_) << "\n"; | |
+ #endif | |
+ *x = m_pre_x + (dx_pre * offset_); | |
+ *y = m_pre_y + (dy_pre * offset_); | |
+ m_status = process; | |
+ return SEG_MOVETO; | |
+ case process: | |
+ switch(m_cur_cmd) | |
+ { | |
+ case SEG_LINETO: | |
+ m_next_cmd = geom_.vertex(&m_next_x, &m_next_y); | |
+ prj_trans_.backward(m_next_x,m_next_y,z); | |
+ t_.forward(&m_next_x,&m_next_y); | |
+ switch(m_next_cmd) | |
+ { | |
+ case SEG_LINETO: | |
+ m_status = angle_joint; | |
+ break; | |
+ default: | |
+ m_status = last_vertex; | |
+ break; | |
+ } | |
+ break; | |
+ case SEG_END: | |
+ m_status = end; | |
+ return SEG_END; | |
+ } | |
+ break; | |
+ case last_vertex: | |
+ dx_curr = cos(angle_a + pi_by_2); | |
+ dy_curr = sin(angle_a + pi_by_2); | |
+ *x = m_cur_x + (dx_curr * offset_); | |
+ *y = m_cur_y + (dy_curr * offset_); | |
+ m_status = end; | |
+ return m_cur_cmd; | |
+ case angle_joint: | |
+ angle_b = atan2((m_cur_y-m_next_y),(m_cur_x-m_next_x)); | |
+ h = tan((angle_b - angle_a)/2.0); | |
+ | |
+ if (fabs(h) < threshold_) | |
+ { | |
+ dx_curr = cos(angle_a + pi_by_2); | |
+ dy_curr = sin(angle_a + pi_by_2); | |
+ *x = m_cur_x + (dx_curr * offset_) - h * (dy_curr * offset_); | |
+ *y = m_cur_y + (dy_curr * offset_) + h * (dx_curr * offset_); | |
+ //*x = m_cur_x + (dx_curr - h * dy_curr) * offset_; | |
+ //*y = m_cur_y + (dy_curr + h * dx_curr) * offset_; | |
+ } | |
+ else // skip sharp spikes | |
+ { | |
+ | |
+ #ifdef MAPNIK_DEBUG | |
+ dx_curr = cos(angle_a + pi_by_2); | |
+ dy_curr = sin(angle_a + pi_by_2); | |
+ sin_curve = dx_curr*dy_pre-dy_curr*dx_pre; | |
+ std::clog << "angle a: " << angle_a << "\n"; | |
+ std::clog << "angle b: " << angle_b << "\n"; | |
+ std::clog << "h: " << h << "\n"; | |
+ std::clog << "sin_curve: " << sin_curve << "\n"; | |
+ #endif | |
+ m_status = process; | |
+ break; | |
+ } | |
+ | |
+ // alternate sharp spike fix, does not work... | |
+ | |
+ /* | |
+ sin_curve = dx_curr*dy_pre-dy_curr*dx_pre; | |
+ cos_curve = -dx_pre*dx_curr-dy_pre*dy_curr; | |
+ | |
+ #ifdef MAPNIK_DEBUG | |
+ std::clog << "sin_curve value: " << sin_curve << "\n"; | |
+ #endif | |
+ if(sin_curve > -0.3 && sin_curve < 0.3) { | |
+ angle_b = atan2((m_cur_y-m_next_y),(m_cur_x-m_next_x)); | |
+ h = tan((angle_b - angle_a)/2.0); | |
+ *x = m_cur_x + (dx_curr * offset_) - h * (dy_curr * offset_); | |
+ *y = m_cur_y + (dy_curr * offset_) + h * (dx_curr * offset_); | |
+ } else { | |
+ if (angle_b - angle_a > 0) | |
+ h = -1.0*(1.0+cos_curve)/sin_curve; | |
+ else | |
+ h = (1.0+cos_curve)/sin_curve; | |
+ *x = m_cur_x + (dx_curr + base_shift*dy_curr)*offset_; | |
+ *y = m_cur_y + (dy_curr - base_shift*dx_curr)*offset_; | |
+ } | |
+ */ | |
+ | |
+ m_pre_x = *x; | |
+ m_pre_x = *y; | |
+ m_cur_x = m_next_x; | |
+ m_cur_y = m_next_y; | |
+ angle_a = angle_b; | |
+ m_pre_cmd = m_cur_cmd; | |
+ m_cur_cmd = m_next_cmd; | |
+ m_status = process; | |
+ return m_pre_cmd; | |
+ } | |
+ } | |
+ } | |
+ } | |
+ | |
+ void rewind (unsigned pos) | |
+ { | |
+ geom_.rewind(pos); | |
+ m_status = initial; | |
+ } | |
+ | |
+ private: | |
+ Transform const& t_; | |
+ Geometry const& geom_; | |
+ proj_transform const& prj_trans_; | |
+ int offset_; | |
+ unsigned int threshold_; | |
+ status m_status; | |
+ double dx_pre; | |
+ double dy_pre; | |
+ double dx_curr; | |
+ double dy_curr; | |
+ double sin_curve; | |
+ double cos_curve; | |
+ double angle_a; | |
+ double angle_b; | |
+ double h; | |
+ unsigned m_pre_cmd; | |
+ double m_pre_x; | |
+ double m_pre_y; | |
+ unsigned m_cur_cmd; | |
+ double m_cur_x; | |
+ double m_cur_y; | |
+ unsigned m_next_cmd; | |
+ double m_next_x; | |
+ double m_next_y; | |
+ }; | |
+ | |
class CoordTransform | |
{ | |
private: | |
@@ -174,45 +401,45 @@ | |
sx_ = (double(width_))/extent_.width(); | |
sy_ = (double(height_))/extent_.height(); | |
} | |
- | |
+ | |
inline int width() const | |
{ | |
return width_; | |
} | |
- | |
+ | |
inline int height() const | |
{ | |
return height_; | |
} | |
- | |
+ | |
inline double scale_x() const | |
{ | |
return sx_; | |
} | |
- | |
+ | |
inline double scale_y() const | |
{ | |
return sy_; | |
} | |
- | |
+ | |
inline void forward(double * x, double * y) const | |
{ | |
*x = (*x - extent_.minx()) * sx_ - offset_x_; | |
*y = (extent_.maxy() - *y) * sy_ - offset_y_; | |
} | |
- | |
+ | |
inline void backward(double * x, double * y) const | |
{ | |
*x = extent_.minx() + (*x + offset_x_)/sx_; | |
*y = extent_.maxy() - (*y + offset_y_)/sy_; | |
} | |
- | |
+ | |
inline coord2d& forward(coord2d& c) const | |
{ | |
forward(&c.x,&c.y); | |
return c; | |
} | |
- | |
+ | |
inline coord2d& backward(coord2d& c) const | |
{ | |
backward(&c.x,&c.y); | |
@@ -277,7 +504,7 @@ | |
} | |
return coords; | |
} | |
- | |
+ | |
inline CoordinateArray& backward(CoordinateArray& coords) const | |
{ | |
for (unsigned i=0;i<coords.size();++i) | |
Index: src/agg/process_line_symbolizer.cpp | |
=================================================================== | |
--- src/agg/process_line_symbolizer.cpp (revision 2900) | |
+++ src/agg/process_line_symbolizer.cpp (working copy) | |
@@ -77,6 +77,7 @@ | |
if (geom.num_points() > 1) | |
{ | |
path_type path(t_,geom,prj_trans); | |
+ path.set_offset(stroke_.get_offset()); | |
if (stroke_.has_dash()) | |
{ | |
Index: include/mapnik/stroke.hpp | |
=================================================================== | |
--- include/mapnik/stroke.hpp (revision 2900) | |
+++ include/mapnik/stroke.hpp (working copy) | |
@@ -70,6 +70,7 @@ | |
double gamma_; | |
dash_array dash_; | |
double dash_offset_; | |
+ double offset_; | |
public: | |
explicit stroke(); | |
stroke(color const& c, double width=1.0); | |
@@ -101,6 +102,9 @@ | |
double dash_offset() const; | |
dash_array const& get_dash_array() const; | |
+ | |
+ void set_offset(double opacity); | |
+ double get_offset() const; | |
private: | |
void swap(const stroke& other) throw(); | |
Index: src/stroke.cpp | |
=================================================================== | |
--- src/stroke.cpp (revision 2900) | |
+++ src/stroke.cpp (working copy) | |
@@ -55,7 +55,8 @@ | |
line_join_(MITER_JOIN), | |
gamma_(1.0), | |
dash_(), | |
- dash_offset_(0) {} | |
+ dash_offset_(0), | |
+ offset_(0.0) {} | |
stroke::stroke(color const& c, double width) | |
: c_(c), | |
@@ -65,7 +66,8 @@ | |
line_join_(MITER_JOIN), | |
gamma_(1.0), | |
dash_(), | |
- dash_offset_(0.0) {} | |
+ dash_offset_(0.0), | |
+ offset_(0.0) {} | |
stroke::stroke(stroke const& other) | |
: c_(other.c_), | |
@@ -75,7 +77,8 @@ | |
line_join_(other.line_join_), | |
gamma_(other.gamma_), | |
dash_(other.dash_), | |
- dash_offset_(other.dash_offset_) {} | |
+ dash_offset_(other.dash_offset_), | |
+ offset_(other.offset_) {} | |
stroke & stroke::operator=(const stroke& rhs) | |
{ | |
@@ -98,6 +101,7 @@ | |
{ | |
return width_; | |
} | |
+ | |
void stroke::set_width(double w) | |
{ | |
width_=w; | |
@@ -170,6 +174,16 @@ | |
return dash_; | |
} | |
+double stroke::get_offset() const | |
+{ | |
+ return offset_; | |
+} | |
+ | |
+void stroke::set_offset(double offset) | |
+{ | |
+ offset_=offset; | |
+} | |
+ | |
void stroke::swap(const stroke& other) throw() | |
{ | |
c_=other.c_; | |
@@ -180,5 +194,7 @@ | |
gamma_=other.gamma_; | |
dash_ = other.dash_; | |
dash_offset_ = other.dash_offset_; | |
+ offset_ = other.offset_; | |
} | |
+ | |
} | |
Index: src/load_map.cpp | |
=================================================================== | |
--- src/load_map.cpp (revision 2909) | |
+++ src/load_map.cpp (working copy) | |
@@ -947,7 +947,7 @@ | |
<< "width,height,placement,marker-type," | |
<< "stroke,stroke-width,stroke-opacity,stroke-linejoin," | |
<< "stroke-linecap,stroke-dash-offset,stroke-dasharray," | |
- // note: stroke-gamma intentionally left off here as markers do not support them | |
+ // note: stroke-gamma and stroke-offset intentionally left off here as markers do not support them | |
<< "meta-writer,meta-output"; | |
ensure_attrs(sym, "MarkersSymbolizer", s.str()); | |
@@ -1692,6 +1692,10 @@ | |
optional<double> gamma = get_opt_attr<double>(sym, "stroke-gamma"); | |
if (gamma) strk.set_gamma(*gamma); | |
+ // stroke-offset | |
+ optional<double> offset = get_opt_attr<double>(sym, "stroke-offset"); | |
+ if (offset) strk.set_offset(*offset); | |
+ | |
// stroke-dash-offset | |
optional<double> dash_offset = get_opt_attr<double>(sym, "stroke-dash-offset"); | |
if (dash_offset) strk.set_dash_offset(*dash_offset); | |
@@ -1741,7 +1745,7 @@ | |
{ | |
std::stringstream s; | |
s << "stroke,stroke-width,stroke-opacity,stroke-linejoin," | |
- << "stroke-linecap,stroke-gamma,stroke-dash-offset,stroke-dasharray," | |
+ << "stroke-linecap,stroke-gamma,stroke-offset,stroke-dash-offset,stroke-dasharray," | |
<< "meta-writer,meta-output"; | |
ensure_attrs(sym, "LineSymbolizer", s.str()); | |
Index: src/save_map.cpp | |
=================================================================== | |
--- src/save_map.cpp (revision 2909) | |
+++ src/save_map.cpp (working copy) | |
@@ -584,6 +584,10 @@ | |
{ | |
set_attr( node, "stroke-gamma", strk.get_gamma()); | |
} | |
+ if ( strk.get_offset() != dfl.get_offset() || explicit_defaults_ ) | |
+ { | |
+ set_attr( node, "stroke-offset", strk.get_offset()); | |
+ } | |
if ( strk.dash_offset() != dfl.dash_offset() || explicit_defaults_ ) | |
{ | |
set_attr( node, "stroke-dash-offset", strk.dash_offset()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment