Compare commits

...

54 commits

Author SHA1 Message Date
Dane Springmeyer
e06e50a61f add travis config for 2.1.x branch 2013-11-17 15:41:14 -08:00
Dane Springmeyer
2dce39fa8f fix osx/libc++/boost-1.55 compile error - refs #1973 2013-11-17 15:30:25 -08:00
Dane Springmeyer
adb07a7407 Merge pull request #1938 from strk/2.1.x-layername-in-errormsg
Include layer name in error message from renderer (#1924)
2013-07-16 09:15:37 -07:00
Sandro Santilli
7fe38520b7 Include layer name in error message from renderer (#1924) 2013-07-04 12:53:58 +02:00
Dane Springmeyer
3e70226a69 also translate std::out_of_bounds => IndexError otherwise new std::exception => runtime catch will override - refs #1816 2013-04-17 15:29:32 -07:00
Dane Springmeyer
ab3525cfc4 add missing exception translator for datasource exceptions by catching std::exception - closes #1816 2013-04-17 13:44:35 -07:00
Dane Springmeyer
855ff06159 Merge pull request #1815 from strk/2.1.x-too-many-float-digits
Use 15 significant digits for float to string conversion
2013-04-17 09:49:54 -07:00
Sandro Santilli
c930807b9f Use 15 significant digits for float to string conversion
Fixes printing of numbers like 67.65 or 67.35. Closes #1811
2013-04-17 12:40:15 +02:00
Sandro Santilli
bb0133e929 Exit with failure if any CPP test fails (see #1811) 2013-04-16 18:34:50 +02:00
Dane Springmeyer
96414f9262 avoid std::vector declare within mapnik namespace 2013-04-12 19:27:37 -07:00
Dane Springmeyer
c6fd387469 postgis: avoid potential crash if connection is not initialized 2013-04-02 11:24:38 -07:00
Daniel C. Casimiro
42d7263cab cherry-pick fix from #1787 into 2.1.x 2013-04-02 11:13:02 -07:00
Dane Springmeyer
88f8d9951c Merge pull request #1709 from strk/2.1.x-out_of_pool_slots
Fix postgresql connection leaks on error
2013-04-02 11:11:21 -07:00
Dane Springmeyer
7f0c745366 fix access to mapnik::query::property_names in python - closes #1762 2013-03-14 15:34:45 -07:00
Dane Springmeyer
098f9de2aa fix linking of python.input to python lib on linux 2013-03-06 14:04:17 -08:00
Dane Springmeyer
ae28268ba8 fix serialization of agg-stack-blur 2013-02-27 11:33:14 -05:00
Dane Springmeyer
cf70b9959a fix compile of json feature_collection grammar when building against >= boost 1.52 - refs #1658 and #1716 2013-02-10 07:24:35 -08:00
Sandro Santilli
3f51e17737 Fix postgresql connection leaks on error 2013-01-31 12:03:30 +01:00
Dane Springmeyer
d8128a04f8 scons: make python includes a list and sensitive to platform specific directories - closes #1691 and fixes mapnik/mapnik-packaging#66 2013-01-14 16:10:49 -08:00
Dane Springmeyer
abaca39738 comment failing to_string precision test - refs #1676 2013-01-02 18:09:38 -08:00
artemp
e99f8be047 fixup cherry pick of ae69ee9a24 to 2.1.x from master 2013-01-02 18:08:30 -08:00
Dane Springmeyer
66aaa4f70c Merge branch '2.1.x' of github.com:mapnik/mapnik into 2.1.x 2012-12-18 18:00:23 -08:00
Dane Springmeyer
8b649f7555 fix compile error due to ambiguous 'str' variable 2012-12-18 18:00:06 -08:00
Dane Springmeyer
3f54120354 python plugin: catch and report exceptions, closes #1422 2012-12-18 17:15:36 -08:00
Dane Springmeyer
9d209cf959 Merge pull request #1650 from strk/2.1.x-looser-simplification-factor
Loosen simplification factor to 1/20 of pixel (2.1.x branch) - closes #1639
2012-12-18 11:57:05 -08:00
Artem Pavlenko
2fb39d23b4 Merge pull request #1631 from strk/2.1.x-float-labels
Fix float data conversion to string (backward compatibility)
2012-12-18 04:16:06 -08:00
Sandro Santilli
17dd295a1d Implement scientific notation for double-to-string
Also fixes tests for 1e5 expecting fixed precision rather than
scientific notation (stringstream gives scientific notation indeed)

The only still failing test now is the one having less than 16
significant digits of precision, due to the boost bug:
https://svn.boost.org/trac/boost/ticket/7785
2012-12-17 20:47:42 +01:00
Sandro Santilli
c921a67fd4 Loosen simplification factor to 1/20 of pixel 2012-12-14 10:00:37 +01:00
Dane Springmeyer
41a650e568 Merge pull request #1636 from strk/2.1.x-marker-placement-fixes
2.1.x marker placement fixes
2012-12-10 10:37:01 -08:00
Dane Springmeyer
664e05b2ca Fix line placement of markers on points (#1548)
REFS #1604,#1350,#1607,#1548,#1625
2012-12-10 17:09:28 +01:00
Sandro Santilli
33dcd5a459 Add visual test for point placement marker on lines (#1604) 2012-12-10 16:25:42 +01:00
Dane Springmeyer
3e938cbfaa Use middle_point placement for marker POINT placement on lines
REFS #1604, #1350, #1607 and #1625
2012-12-10 16:05:03 +01:00
Sandro Santilli
19522ac509 Fix float data conversion to string
Fixes precision digits, closing #430
Also avoids forcing a trailing '.0', closing #1627
2012-12-06 11:59:28 +01:00
Dane Springmeyer
93f58b1ef3 backport support for postgis literal type to 2.1.x - refs #1464 - closes #1626 2012-12-05 20:07:49 -08:00
Dane Springmeyer
3d7575c282 fix potential compile due to error problematic defines that did not get properly cleaned up in a986aedd05 - refs #1400 2012-11-30 11:27:34 -08:00
Dane Springmeyer
f99190cbdd fix marker-multi-policy test - closes #1613 and closes #1612 2012-11-29 18:28:05 -08:00
Dane Springmeyer
3ec3a7cd66 Merge pull request #1610 from strk/2.1.x-whole-centroid
Fix "whole" multi centroid computation (#1606)
2012-11-29 18:04:13 -08:00
Sandro Santilli
e9cb647423 Add test for whole centroid computation (#1606) 2012-11-29 17:10:52 +01:00
Sandro Santilli
9cccf2ba77 Fix "whole" multi centroid computation (#1606)
Still lacks a testcase
2012-11-29 11:41:24 +01:00
Dane Springmeyer
9728f346f4 fix method name - refs #1562 2012-11-28 20:55:05 -08:00
Dane Springmeyer
499c6aca70 add #1562 backport to CHANGELOG 2012-11-28 20:23:58 -08:00
Dane Springmeyer
13a437232d add serialization of line_symbolizer offset - closes #1562 2012-11-28 20:22:17 -08:00
Dane Springmeyer
0b2f2f8ee0 Merge pull request #1587 from strk/2.1.x-multi_policy
Add marker-multi-policy parameter (backport to 2.1.x from master) - refs #1573, #1555 and #1586
2012-11-21 14:51:38 -08:00
Sandro Santilli
4a0933e34a Add marker-multi-policy parameter
This is to support user-configurable rendering behavior for
multi-geometries when using either point or interior placement
See #1573 and #1555
2012-11-21 23:05:43 +01:00
Colin Rundel
219c5f5809 Added set_name to font_set class 2012-09-26 16:12:14 -07:00
Hermann Kraus
924f3fb19c Raise Exception instead of returning None. 2012-09-26 14:52:49 -07:00
Hermann Kraus
29a0d6133b Reenable "name" tests.
Refs #1482.
2012-09-26 14:46:19 -07:00
Dane Springmeyer
36a5e0d857 scons: do not invoke any install or uninstall targets if using SYSTEM_FONTS - closes #1481 2012-09-26 14:23:11 -07:00
Dane Springmeyer
e221168e4e after #1483 fontset is boost optional so check appropriately in load_map (solves assert) 2012-09-26 14:21:06 -07:00
Dane Springmeyer
78790e6692 fix conflicts from cherry-picking 8cfb40a 2012-09-26 14:21:03 -07:00
Dane Springmeyer
62b74002cb Add a test (currently failing) ensuring that named fontsets created in python are propertly serialized 2012-09-26 14:19:22 -07:00
Hermann Kraus
00a4ab0528 Add Changelog entry for DebugSymbolizer. 2012-09-15 01:56:51 +02:00
Hermann Kraus
f27578d41c Add TextSymbolizer.name in python bindings.
Refs #1482.
2012-09-14 02:00:45 +02:00
Dane Springmeyer
d617403b33 now working on mapnik v2.1.1-pre 2012-08-23 15:22:50 -07:00
73 changed files with 1008 additions and 241 deletions

20
.travis.yml Normal file
View file

@ -0,0 +1,20 @@
language: cpp
compiler:
- clang
- gcc
before_install:
- echo 'yes' | sudo add-apt-repository ppa:mapnik/boost
- sudo apt-get update -qq
- sudo apt-get install -qq make libboost-dev libboost-filesystem-dev libboost-program-options-dev libboost-python-dev libboost-regex-dev libboost-system-dev libboost-thread-dev python-nose libicu-dev libpng-dev libjpeg-dev libtiff-dev libwebp-dev libz-dev libfreetype6-dev libxml2-dev libproj-dev libcairo-dev python-cairo-dev libsqlite3-dev postgresql-server-dev*
script:
- ./configure CXX=${CXX} CC=${CC} DEMO=False BINDINGS='python' CAIRO=False FAST=True && JOBS=2 make
- make test-local
notifications:
irc:
channels:
- "irc.freenode.org#mapnik"
use_notice: true

View file

@ -7,6 +7,17 @@ Developers: Please commit along with changes.
For a complete change history, see the git log. For a complete change history, see the git log.
## Unreleased
- Added serialization of `line-offset` to save_map (#1562)
- Added support for controlling rendering behavior of markers on multi-geometries `marker-multi-policy` (#1555,#1573)
- Add DebugSymbolizer (#1366)
- Added support for literal types in PostGIS Plugin (#1464)
## Mapnik 2.1.0 ## Mapnik 2.1.0
Released Aug 23, 2012 Released Aug 23, 2012

View file

@ -25,7 +25,7 @@ test:
@python tests/visual_tests/test.py -q || true @python tests/visual_tests/test.py -q || true
@echo "*** Running C++ tests..." @echo "*** Running C++ tests..."
@for FILE in tests/cpp_tests/*-bin; do \ @for FILE in tests/cpp_tests/*-bin; do \
$${FILE}; \ $${FILE} || exit 1;\
done done
@echo "*** Running python tests..." @echo "*** Running python tests..."
@python tests/run_tests.py -q @python tests/run_tests.py -q

View file

@ -350,7 +350,7 @@ opts.AddVariables(
# Other variables # Other variables
BoolVariable('SHAPE_MEMORY_MAPPED_FILE', 'Utilize memory-mapped files in Shapefile Plugin (higher memory usage, better performance)', 'True'), BoolVariable('SHAPE_MEMORY_MAPPED_FILE', 'Utilize memory-mapped files in Shapefile Plugin (higher memory usage, better performance)', 'True'),
('SYSTEM_FONTS','Provide location for python bindings to register fonts (if given aborts installation of bundled DejaVu fonts)',''), ('SYSTEM_FONTS','Provide location for python bindings to register fonts (if provided then the bundled DejaVu fonts are not installed)',''),
('LIB_DIR_NAME','Name to use for the subfolder beside libmapnik where fonts and plugins are installed','mapnik'), ('LIB_DIR_NAME','Name to use for the subfolder beside libmapnik where fonts and plugins are installed','mapnik'),
PathVariable('PYTHON','Full path to Python executable used to build bindings', sys.executable), PathVariable('PYTHON','Full path to Python executable used to build bindings', sys.executable),
BoolVariable('FRAMEWORK_PYTHON', 'Link against Framework Python on Mac OS X', 'True'), BoolVariable('FRAMEWORK_PYTHON', 'Link against Framework Python on Mac OS X', 'True'),
@ -962,6 +962,7 @@ if not preconfigured:
env['PLUGINS'] = PLUGINS env['PLUGINS'] = PLUGINS
env['EXTRA_FREETYPE_LIBS'] = [] env['EXTRA_FREETYPE_LIBS'] = []
env['SQLITE_LINKFLAGS'] = [] env['SQLITE_LINKFLAGS'] = []
env['PYTHON_INCLUDES'] = []
# previously a leading / was expected for LIB_DIR_NAME # previously a leading / was expected for LIB_DIR_NAME
# now strip it to ensure expected behavior # now strip it to ensure expected behavior
if env['LIB_DIR_NAME'].startswith(os.path.sep): if env['LIB_DIR_NAME'].startswith(os.path.sep):
@ -1355,7 +1356,14 @@ if not preconfigured:
py_includes = '''%s -c "from distutils.sysconfig import get_python_inc; print(get_python_inc())"''' % env['PYTHON'] py_includes = '''%s -c "from distutils.sysconfig import get_python_inc; print(get_python_inc())"''' % env['PYTHON']
else: else:
py_includes = '''%s -c "from distutils.sysconfig import get_python_inc; print get_python_inc()"''' % env['PYTHON'] py_includes = '''%s -c "from distutils.sysconfig import get_python_inc; print get_python_inc()"''' % env['PYTHON']
env['PYTHON_INCLUDES'] = call(py_includes) env['PYTHON_INCLUDES'].append(call(py_includes))
# also append platform specific includes
if py3:
py_plat_includes = '''%s -c "from distutils.sysconfig import get_python_inc; print(get_python_inc(plat_specific=True))"''' % env['PYTHON']
else:
py_plat_includes = '''%s -c "from distutils.sysconfig import get_python_inc; print get_python_inc(plat_specific=True)"''' % env['PYTHON']
env['PYTHON_INCLUDES'].append(call(py_plat_includes))
# Note: we use the plat_specific argument here to make sure to respect the arch-specific site-packages location # Note: we use the plat_specific argument here to make sure to respect the arch-specific site-packages location
if py3: if py3:
@ -1366,7 +1374,7 @@ if not preconfigured:
else: else:
env['PYTHON_SYS_PREFIX'] = os.popen('''%s -c "import sys; print sys.prefix"''' % env['PYTHON']).read().strip() env['PYTHON_SYS_PREFIX'] = os.popen('''%s -c "import sys; print sys.prefix"''' % env['PYTHON']).read().strip()
env['PYTHON_VERSION'] = os.popen('''%s -c "import sys; print sys.version"''' % env['PYTHON']).read()[0:3] env['PYTHON_VERSION'] = os.popen('''%s -c "import sys; print sys.version"''' % env['PYTHON']).read()[0:3]
env['PYTHON_INCLUDES'] = env['PYTHON_SYS_PREFIX'] + '/include/python' + env['PYTHON_VERSION'] env['PYTHON_INCLUDES'] = [env['PYTHON_SYS_PREFIX'] + '/include/python' + env['PYTHON_VERSION']]
env['PYTHON_SITE_PACKAGES'] = env['DESTDIR'] + os.path.sep + env['PYTHON_SYS_PREFIX'] + os.path.sep + env['LIBDIR_SCHEMA'] + '/python' + env['PYTHON_VERSION'] + '/site-packages/' env['PYTHON_SITE_PACKAGES'] = env['DESTDIR'] + os.path.sep + env['PYTHON_SYS_PREFIX'] + os.path.sep + env['LIBDIR_SCHEMA'] + '/python' + env['PYTHON_VERSION'] + '/site-packages/'
# if user-requested custom prefix fall back to manual concatenation for building subdirectories # if user-requested custom prefix fall back to manual concatenation for building subdirectories
@ -1444,7 +1452,7 @@ if not preconfigured:
# fetch the mapnik version header in order to set the # fetch the mapnik version header in order to set the
# ABI version used to build libmapnik.so on linux in src/build.py # ABI version used to build libmapnik.so on linux in src/build.py
abi = conf.GetMapnikLibVersion() abi = conf.GetMapnikLibVersion()
abi_fallback = "2.1.0" abi_fallback = "2.1.1-pre"
if not abi: if not abi:
color_print(1,'Problem encountered parsing mapnik version, falling back to %s' % abi_fallback) color_print(1,'Problem encountered parsing mapnik version, falling back to %s' % abi_fallback)
abi = abi_fallback abi = abi_fallback
@ -1532,7 +1540,8 @@ if not preconfigured:
# as they are later set in the python build.py # as they are later set in the python build.py
# ugly hack needed until we have env specific conf # ugly hack needed until we have env specific conf
backup = env.Clone().Dictionary() backup = env.Clone().Dictionary()
env.AppendUnique(CPPPATH = os.path.realpath(env['PYTHON_INCLUDES'])) for pyinc in env['PYTHON_INCLUDES']:
env.AppendUnique(CPPPATH = os.path.realpath(pyinc))
if not conf.CheckHeader(header='Python.h',language='C'): if not conf.CheckHeader(header='Python.h',language='C'):
color_print(1,'Could not find required header files for the Python language (version %s)' % env['PYTHON_VERSION']) color_print(1,'Could not find required header files for the Python language (version %s)' % env['PYTHON_VERSION'])

View file

@ -698,6 +698,18 @@ class PythonDatasource(object):
return itertools.imap(make_it, features, itertools.count(1)) return itertools.imap(make_it, features, itertools.count(1))
class _TextSymbolizer(TextSymbolizer,_injector): class _TextSymbolizer(TextSymbolizer,_injector):
@property
def name(self):
if isinstance(self.properties.format_tree, FormattingText):
return self.properties.format_tree.text
else:
# There is no single expression which could be returned as name
raise RuntimeError("TextSymbolizer uses complex formatting features, but old compatibility interface is used to access it. Use self.properties.format_tree instead.")
@name.setter
def name(self, name):
self.properties.format_tree = FormattingText(name)
@property @property
def text_size(self): def text_size(self):
warnings.warn("'text_size' is deprecated, use format.text_size", warnings.warn("'text_size' is deprecated, use format.text_size",

View file

@ -33,8 +33,13 @@ using mapnik::font_set;
void export_fontset () void export_fontset ()
{ {
using namespace boost::python; using namespace boost::python;
class_<font_set>("FontSet", init<>("default fontset constructor") class_<font_set>("FontSet", init<std::string const&>("default fontset constructor")
) )
.add_property("name",
make_function(&font_set::get_name,return_value_policy<copy_const_reference>()),
&font_set::set_name,
"Get/Set the name of the FontSet.\n"
)
.def("add_face_name",&font_set::add_face_name, .def("add_face_name",&font_set::add_face_name,
(arg("name")), (arg("name")),
"Add a face-name to the fontset.\n" "Add a face-name to the fontset.\n"

View file

@ -89,6 +89,12 @@ void export_markers_symbolizer()
.value("LINE_PLACEMENT",mapnik::MARKER_LINE_PLACEMENT) .value("LINE_PLACEMENT",mapnik::MARKER_LINE_PLACEMENT)
; ;
mapnik::enumeration_<mapnik::marker_multi_policy_e>("marker_multi_policy")
.value("EACH",mapnik::MARKER_EACH_MULTI)
.value("WHOLE",mapnik::MARKER_WHOLE_MULTI)
.value("LARGEST",mapnik::MARKER_LARGEST_MULTI)
;
class_<markers_symbolizer>("MarkersSymbolizer", class_<markers_symbolizer>("MarkersSymbolizer",
init<>("Default Markers Symbolizer - circle")) init<>("Default Markers Symbolizer - circle"))
.def (init<mapnik::path_expression_ptr>("<path expression ptr>")) .def (init<mapnik::path_expression_ptr>("<path expression ptr>"))
@ -143,6 +149,10 @@ void export_markers_symbolizer()
&markers_symbolizer::get_marker_placement, &markers_symbolizer::get_marker_placement,
&markers_symbolizer::set_marker_placement, &markers_symbolizer::set_marker_placement,
"Set/get the marker placement") "Set/get the marker placement")
.add_property("multi_policy",
&markers_symbolizer::get_marker_multi_policy,
&markers_symbolizer::set_marker_multi_policy,
"Set/get the marker multi geometry rendering policy")
.add_property("comp_op", .add_property("comp_op",
&markers_symbolizer::comp_op, &markers_symbolizer::comp_op,
&markers_symbolizer::set_comp_op, &markers_symbolizer::set_comp_op,

View file

@ -295,6 +295,16 @@ void runtime_error_translator(std::runtime_error const & ex)
PyErr_SetString(PyExc_RuntimeError, ex.what()); PyErr_SetString(PyExc_RuntimeError, ex.what());
} }
void out_of_range_error_translator(std::out_of_range const & ex)
{
PyErr_SetString(PyExc_IndexError, ex.what());
}
void standard_error_translator(std::exception const & ex)
{
PyErr_SetString(PyExc_RuntimeError, ex.what());
}
unsigned mapnik_version() unsigned mapnik_version()
{ {
return MAPNIK_VERSION; return MAPNIK_VERSION;
@ -364,6 +374,8 @@ BOOST_PYTHON_MODULE(_mapnik)
using mapnik::save_map_to_string; using mapnik::save_map_to_string;
using mapnik::render_grid; using mapnik::render_grid;
register_exception_translator<std::exception>(&standard_error_translator);
register_exception_translator<std::out_of_range>(&out_of_range_error_translator);
register_exception_translator<mapnik::config_error>(&config_error_translator); register_exception_translator<mapnik::config_error>(&config_error_translator);
register_exception_translator<mapnik::value_error>(&value_error_translator); register_exception_translator<mapnik::value_error>(&value_error_translator);
register_exception_translator<std::runtime_error>(&runtime_error_translator); register_exception_translator<std::runtime_error>(&runtime_error_translator);
@ -617,6 +629,7 @@ BOOST_PYTHON_MODULE(_mapnik)
def("has_pycairo", &has_pycairo, "Get pycairo module status"); def("has_pycairo", &has_pycairo, "Get pycairo module status");
python_optional<mapnik::stroke>(); python_optional<mapnik::stroke>();
python_optional<mapnik::font_set>();
python_optional<mapnik::color>(); python_optional<mapnik::color>();
python_optional<mapnik::box2d<double> >(); python_optional<mapnik::box2d<double> >();
python_optional<mapnik::composite_mode_e>(); python_optional<mapnik::composite_mode_e>();

View file

@ -22,11 +22,15 @@
// boost // boost
#include <boost/python.hpp> #include <boost/python.hpp>
#include <boost/foreach.hpp>
// mapnik // mapnik
#include <mapnik/query.hpp> #include <mapnik/query.hpp>
#include <mapnik/box2d.hpp> #include <mapnik/box2d.hpp>
#include <string>
#include <set>
using mapnik::query; using mapnik::query;
using mapnik::box2d; using mapnik::box2d;
@ -46,11 +50,30 @@ struct resolution_to_tuple
} }
}; };
struct names_to_list
{
static PyObject* convert(std::set<std::string> const& names)
{
boost::python::list l;
BOOST_FOREACH( std::string const& name, names )
{
l.append(name);
}
return python::incref(l.ptr());
}
static PyTypeObject const* get_pytype()
{
return &PyList_Type;
}
};
void export_query() void export_query()
{ {
using namespace boost::python; using namespace boost::python;
to_python_converter<query::resolution_type, resolution_to_tuple> (); to_python_converter<query::resolution_type, resolution_to_tuple> ();
to_python_converter<std::set<std::string>, names_to_list> ();
class_<query>("Query", "a spatial query data object", class_<query>("Query", "a spatial query data object",
init<box2d<double>,query::resolution_type const&,double>() ) init<box2d<double>,query::resolution_type const&,double>() )

View file

@ -193,7 +193,11 @@ struct ListNodeWrap: formatting::list_node, wrapper<formatting::list_node>
ListNodeWrap(object l) : formatting::list_node(), wrapper<formatting::list_node>() ListNodeWrap(object l) : formatting::list_node(), wrapper<formatting::list_node>()
{ {
stl_input_iterator<formatting::node_ptr> begin(l), end; stl_input_iterator<formatting::node_ptr> begin(l), end;
children_.insert(children_.end(), begin, end); while (begin != end)
{
children_.push_back(*begin);
++begin;
}
} }
/* TODO: Add constructor taking variable number of arguments. /* TODO: Add constructor taking variable number of arguments.
@ -402,9 +406,9 @@ void export_text_placement()
class_with_converter<char_properties> class_with_converter<char_properties>
("CharProperties") ("CharProperties")
.def_readwrite_convert("text_transform", &char_properties::text_transform) .def_readwrite_convert("text_transform", &char_properties::text_transform)
.def_readwrite_convert("fontset", &char_properties::fontset)
.def(init<char_properties const&>()) //Copy constructor .def(init<char_properties const&>()) //Copy constructor
.def_readwrite("face_name", &char_properties::face_name) .def_readwrite("face_name", &char_properties::face_name)
.def_readwrite("fontset", &char_properties::fontset)
.def_readwrite("text_size", &char_properties::text_size) .def_readwrite("text_size", &char_properties::text_size)
.def_readwrite("character_spacing", &char_properties::character_spacing) .def_readwrite("character_spacing", &char_properties::character_spacing)
.def_readwrite("line_spacing", &char_properties::line_spacing) .def_readwrite("line_spacing", &char_properties::line_spacing)

View file

@ -29,6 +29,7 @@ namespace agg
void rewind(unsigned) {} void rewind(unsigned) {}
unsigned vertex(double*, double*) { return path_cmd_stop; } unsigned vertex(double*, double*) { return path_cmd_stop; }
unsigned type() const { return 0; }
}; };
@ -64,6 +65,7 @@ namespace agg
} }
unsigned vertex(double* x, double* y); unsigned vertex(double* x, double* y);
unsigned type() const { return m_source->type(); }
private: private:
// Prohibit copying // Prohibit copying

View file

@ -33,6 +33,7 @@ namespace agg
void rewind(unsigned path_id); void rewind(unsigned path_id);
unsigned vertex(double* x, double* y); unsigned vertex(double* x, double* y);
unsigned type() const { return m_source->type(); }
private: private:
conv_adaptor_vpgen(const conv_adaptor_vpgen<VertexSource, VPGen>&); conv_adaptor_vpgen(const conv_adaptor_vpgen<VertexSource, VPGen>&);

View file

@ -51,6 +51,7 @@ namespace agg
double y1() const { return base_type::vpgen().y1(); } double y1() const { return base_type::vpgen().y1(); }
double x2() const { return base_type::vpgen().x2(); } double x2() const { return base_type::vpgen().x2(); }
double y2() const { return base_type::vpgen().y2(); } double y2() const { return base_type::vpgen().y2(); }
unsigned type() const { return base_type::type(); }
private: private:
conv_clip_polygon(const conv_clip_polygon<VertexSource>&); conv_clip_polygon(const conv_clip_polygon<VertexSource>&);

View file

@ -51,6 +51,7 @@ namespace agg
double y1() const { return base_type::vpgen().y1(); } double y1() const { return base_type::vpgen().y1(); }
double x2() const { return base_type::vpgen().x2(); } double x2() const { return base_type::vpgen().x2(); }
double y2() const { return base_type::vpgen().y2(); } double y2() const { return base_type::vpgen().y2(); }
unsigned type() const { return base_type::type(); }
private: private:
conv_clip_polyline(const conv_clip_polyline<VertexSource>&); conv_clip_polyline(const conv_clip_polyline<VertexSource>&);

View file

@ -42,6 +42,7 @@ namespace agg
void smooth_value(double v) { base_type::generator().smooth_value(v); } void smooth_value(double v) { base_type::generator().smooth_value(v); }
double smooth_value() const { return base_type::generator().smooth_value(); } double smooth_value() const { return base_type::generator().smooth_value(); }
unsigned type() const { return base_type::type(); }
private: private:
conv_smooth_poly1(const conv_smooth_poly1<VertexSource>&); conv_smooth_poly1(const conv_smooth_poly1<VertexSource>&);
@ -64,6 +65,7 @@ namespace agg
void smooth_value(double v) { m_smooth.generator().smooth_value(v); } void smooth_value(double v) { m_smooth.generator().smooth_value(v); }
double smooth_value() const { return m_smooth.generator().smooth_value(); } double smooth_value() const { return m_smooth.generator().smooth_value(); }
unsigned type() const { return m_smooth.type(); }
private: private:
conv_smooth_poly1_curve(const conv_smooth_poly1_curve<VertexSource>&); conv_smooth_poly1_curve(const conv_smooth_poly1_curve<VertexSource>&);

View file

@ -22,15 +22,12 @@ import glob
Import('env') Import('env')
# grab all the deja vu fonts if not env['SYSTEM_FONTS']:
includes = glob.glob('*/*/*.ttf') # grab all the deja vu fonts
includes = glob.glob('*/*/*.ttf')
# grab single unifont ttf (available at http://unifoundry.com/unifont.html) # grab single unifont ttf (available at http://unifoundry.com/unifont.html)
includes.extend(glob.glob('unifont*.ttf')) includes.extend(glob.glob('unifont*.ttf'))
target_path = env['MAPNIK_FONTS_DEST']
target_path = env['MAPNIK_FONTS_DEST'] if 'uninstall' not in COMMAND_LINE_TARGETS:
env.Alias(target='install', source=env.Install(target_path, includes))
if 'uninstall' not in COMMAND_LINE_TARGETS and not env['SYSTEM_FONTS']: env['create_uninstall_target'](env, target_path)
env.Alias(target='install', source=env.Install(target_path, includes))
env['create_uninstall_target'](env, target_path)

View file

@ -81,6 +81,11 @@ struct MAPNIK_DECL coord_transform
geom_.rewind(pos); geom_.rewind(pos);
} }
unsigned type() const
{
return static_cast<unsigned>(geom_.type());
}
Geometry const& geom() const Geometry const& geom() const
{ {
return geom_; return geom_;

View file

@ -333,11 +333,11 @@ public:
return face_set; return face_set;
} }
face_set_ptr get_face_set(std::string const& name, font_set const& fset) face_set_ptr get_face_set(std::string const& name, boost::optional<font_set> fset)
{ {
if (fset.size() > 0) if (fset && fset->size() > 0)
{ {
return get_face_set(fset); return get_face_set(*fset);
} }
else else
{ {

View file

@ -35,11 +35,11 @@ namespace mapnik
class MAPNIK_DECL font_set class MAPNIK_DECL font_set
{ {
public: public:
font_set();
font_set(std::string const& name); font_set(std::string const& name);
font_set(font_set const& rhs); font_set(font_set const& rhs);
font_set& operator=(font_set const& rhs); font_set& operator=(font_set const& rhs);
unsigned size() const; unsigned size() const;
void set_name(std::string const& name);
std::string const& get_name() const; std::string const& get_name() const;
void add_face_name(std::string); void add_face_name(std::string);
std::vector<std::string> const& get_face_names() const; std::vector<std::string> const& get_face_names() const;

View file

@ -329,6 +329,73 @@ bool centroid(PathType & path, double & x, double & y)
return true; return true;
} }
// Compute centroid over a set of paths
template <typename Iter>
bool centroid_geoms(Iter start, Iter end, double & x, double & y)
{
double x0 = 0.0;
double y0 = 0.0;
double x1 = 0.0;
double y1 = 0.0;
double start_x = x0;
double start_y = y0;
double atmp = 0.0;
double xtmp = 0.0;
double ytmp = 0.0;
unsigned count = 0;
while (start!=end)
{
typename Iter::value_type & path = *start++;
path.rewind(0);
unsigned command = path.vertex(&x0, &y0);
if (command == SEG_END) continue;
if ( ! count++ ) {
start_x = x0;
start_y = y0;
}
while (SEG_END != (command = path.vertex(&x1, &y1)))
{
double dx0 = x0 - start_x;
double dy0 = y0 - start_y;
double dx1 = x1 - start_x;
double dy1 = y1 - start_y;
double ai = dx0 * dy1 - dx1 * dy0;
atmp += ai;
xtmp += (dx1 + dx0) * ai;
ytmp += (dy1 + dy0) * ai;
x0 = x1;
y0 = y1;
++count;
}
}
if (count == 0) return false;
if (count <= 2) {
x = (start_x + x0) * 0.5;
y = (start_y + y0) * 0.5;
return true;
}
if (atmp != 0)
{
x = (xtmp/(3*atmp)) + start_x;
y = (ytmp/(3*atmp)) + start_y;
}
else
{
x = x0;
y = y0;
}
return true;
}
template <typename PathType> template <typename PathType>
bool hit_test(PathType & path, double x, double y, double tol) bool hit_test(PathType & path, double x, double y, double tol)
{ {

View file

@ -78,11 +78,17 @@ struct raster_markers_rasterizer_dispatch_grid
{ {
marker_placement_e placement_method = sym_.get_marker_placement(); marker_placement_e placement_method = sym_.get_marker_placement();
box2d<double> bbox_(0,0, src_.width(),src_.height()); box2d<double> bbox_(0,0, src_.width(),src_.height());
if (placement_method != MARKER_LINE_PLACEMENT) if (placement_method != MARKER_LINE_PLACEMENT ||
path.type() == Point)
{ {
double x = 0; double x = 0;
double y = 0; double y = 0;
if (placement_method == MARKER_INTERIOR_PLACEMENT) if (path.type() == LineString)
{
if (!label::middle_point(path, x, y))
return;
}
else if (placement_method == MARKER_INTERIOR_PLACEMENT)
{ {
if (!label::interior_position(path, x, y)) if (!label::interior_position(path, x, y))
return; return;
@ -209,11 +215,17 @@ struct vector_markers_rasterizer_dispatch_grid
void add_path(T & path) void add_path(T & path)
{ {
marker_placement_e placement_method = sym_.get_marker_placement(); marker_placement_e placement_method = sym_.get_marker_placement();
if (placement_method != MARKER_LINE_PLACEMENT) if (placement_method != MARKER_LINE_PLACEMENT ||
path.type() == Point)
{ {
double x = 0; double x = 0;
double y = 0; double y = 0;
if (placement_method == MARKER_INTERIOR_PLACEMENT) if (path.type() == LineString)
{
if (!label::middle_point(path, x, y))
return;
}
else if (placement_method == MARKER_INTERIOR_PLACEMENT)
{ {
if (!label::interior_position(path, x, y)) if (!label::interior_position(path, x, y))
return; return;

View file

@ -75,7 +75,7 @@ inline std::ostream& operator<< (std::ostream& os, gray)
inline std::ostream& operator<< (std::ostream& os, agg_stack_blur const& filter) inline std::ostream& operator<< (std::ostream& os, agg_stack_blur const& filter)
{ {
os << "agg-stack-blur:" << filter.rx << ',' << filter.ry; os << "agg-stack-blur(" << filter.rx << ',' << filter.ry << ')';
return os; return os;
} }

View file

@ -91,7 +91,7 @@ struct feature_collection_grammar :
> lit(']') > lit(']')
; ;
feature = eps[_a = construct<feature_ptr>(new_<feature_impl>(ctx_,generate_id_()))] feature = eps[_a = phoenix::construct<mapnik::feature_ptr>(new_<mapnik::feature_impl>(ctx_,generate_id_()))]
>> feature_g(*_a)[push_back(_r1,_a)] >> feature_g(*_a)[push_back(_r1,_a)]
; ;

View file

@ -87,11 +87,17 @@ struct vector_markers_rasterizer_dispatch
{ {
marker_placement_e placement_method = sym_.get_marker_placement(); marker_placement_e placement_method = sym_.get_marker_placement();
if (placement_method != MARKER_LINE_PLACEMENT) if (placement_method != MARKER_LINE_PLACEMENT ||
path.type() == Point)
{ {
double x = 0; double x = 0;
double y = 0; double y = 0;
if (placement_method == MARKER_INTERIOR_PLACEMENT) if (path.type() == LineString)
{
if (!label::middle_point(path, x, y))
return;
}
else if (placement_method == MARKER_INTERIOR_PLACEMENT)
{ {
if (!label::interior_position(path, x, y)) if (!label::interior_position(path, x, y))
return; return;
@ -183,11 +189,17 @@ struct raster_markers_rasterizer_dispatch
marker_placement_e placement_method = sym_.get_marker_placement(); marker_placement_e placement_method = sym_.get_marker_placement();
box2d<double> bbox_(0,0, src_.width(),src_.height()); box2d<double> bbox_(0,0, src_.width(),src_.height());
if (placement_method != MARKER_LINE_PLACEMENT) if (placement_method != MARKER_LINE_PLACEMENT ||
path.type() == Point)
{ {
double x = 0; double x = 0;
double y = 0; double y = 0;
if (placement_method == MARKER_INTERIOR_PLACEMENT) if (path.type() == LineString)
{
if (!label::middle_point(path, x, y))
return;
}
else if (placement_method == MARKER_INTERIOR_PLACEMENT)
{ {
if (!label::interior_position(path, x, y)) if (!label::interior_position(path, x, y))
return; return;
@ -398,6 +410,68 @@ void setup_transform_scaling(agg::trans_affine & tr, box2d<double> const& bbox,
} }
} }
// Apply markers to a feature with multiple geometries
template <typename Converter>
void apply_markers_multi(feature_impl & feature, Converter& converter, markers_symbolizer const& sym)
{
std::size_t geom_count = feature.paths().size();
if (geom_count == 1)
{
converter.apply(feature.paths()[0]);
}
else if (geom_count > 1)
{
marker_multi_policy_e multi_policy = sym.get_marker_multi_policy();
marker_placement_e placement = sym.get_marker_placement();
if (placement == MARKER_POINT_PLACEMENT &&
multi_policy == MARKER_WHOLE_MULTI)
{
double x, y;
if (label::centroid_geoms(feature.paths().begin(), feature.paths().end(), x, y))
{
geometry_type pt(Point);
pt.move_to(x, y);
// unset any clipping since we're now dealing with a point
converter.template unset<clip_poly_tag>();
converter.apply(pt);
}
}
else if ((placement == MARKER_POINT_PLACEMENT || placement == MARKER_INTERIOR_PLACEMENT) &&
multi_policy == MARKER_LARGEST_MULTI)
{
// Only apply to path with largest envelope area
// TODO: consider using true area for polygon types
double maxarea = 0;
geometry_type* largest = 0;
BOOST_FOREACH(geometry_type & geom, feature.paths())
{
const box2d<double>& env = geom.envelope();
double area = env.width() * env.height();
if (area > maxarea)
{
maxarea = area;
largest = &geom;
}
}
if (largest)
{
converter.apply(*largest);
}
}
else
{
if (multi_policy != MARKER_EACH_MULTI && placement != MARKER_POINT_PLACEMENT)
{
MAPNIK_LOG_WARN(marker_symbolizer) << "marker_multi_policy != 'each' has no effect with marker_placement != 'point'";
}
BOOST_FOREACH(geometry_type & path, feature.paths())
{
converter.apply(path);
}
}
}
}
} }
#endif //MAPNIK_MARKER_HELPERS_HPP #endif //MAPNIK_MARKER_HELPERS_HPP

View file

@ -46,6 +46,15 @@ enum marker_placement_enum {
DEFINE_ENUM( marker_placement_e, marker_placement_enum ); DEFINE_ENUM( marker_placement_e, marker_placement_enum );
enum marker_multi_policy_enum {
MARKER_EACH_MULTI, // each component in a multi gets its marker
MARKER_WHOLE_MULTI, // consider all components of a multi as a whole
MARKER_LARGEST_MULTI, // only the largest component of a multi gets a marker
marker_multi_policy_enum_MAX
};
DEFINE_ENUM( marker_multi_policy_e, marker_multi_policy_enum );
struct MAPNIK_DECL markers_symbolizer : struct MAPNIK_DECL markers_symbolizer :
public symbolizer_with_image, public symbolizer_base public symbolizer_with_image, public symbolizer_base
{ {
@ -74,6 +83,8 @@ public:
boost::optional<stroke> get_stroke() const; boost::optional<stroke> get_stroke() const;
void set_marker_placement(marker_placement_e marker_p); void set_marker_placement(marker_placement_e marker_p);
marker_placement_e get_marker_placement() const; marker_placement_e get_marker_placement() const;
void set_marker_multi_policy(marker_multi_policy_e marker_p);
marker_multi_policy_e get_marker_multi_policy() const;
private: private:
expression_ptr width_; expression_ptr width_;
expression_ptr height_; expression_ptr height_;
@ -86,6 +97,7 @@ private:
boost::optional<float> opacity_; boost::optional<float> opacity_;
boost::optional<stroke> stroke_; boost::optional<stroke> stroke_;
marker_placement_e marker_p_; marker_placement_e marker_p_;
marker_multi_policy_e marker_mp_;
}; };
} }

View file

@ -33,9 +33,7 @@
namespace mapnik namespace mapnik
{ {
using std::pair; typedef std::vector<std::pair<double,double> > dash_array;
using std::vector;
typedef vector<pair<double,double> > dash_array;
// if you add new tokens, don't forget to add them to the corresponding // if you add new tokens, don't forget to add them to the corresponding
// string array in the cpp file. // string array in the cpp file.

View file

@ -33,6 +33,7 @@
#include <map> #include <map>
// boost // boost
#include <boost/optional.hpp>
#include <boost/property_tree/ptree.hpp> #include <boost/property_tree/ptree.hpp>
namespace mapnik namespace mapnik
@ -58,7 +59,7 @@ struct char_properties
/** Write object to XML ptree. */ /** Write object to XML ptree. */
void to_xml(boost::property_tree::ptree &node, bool explicit_defaults, char_properties const& dfl=char_properties()) const; void to_xml(boost::property_tree::ptree &node, bool explicit_defaults, char_properties const& dfl=char_properties()) const;
std::string face_name; std::string face_name;
font_set fontset; boost::optional<font_set> fontset;
float text_size; float text_size;
double character_spacing; double character_spacing;
double line_spacing; //Largest total height (fontsize+line_spacing) per line is chosen double line_spacing; //Largest total height (fontsize+line_spacing) per line is chosen

View file

@ -92,7 +92,7 @@ struct MAPNIK_DECL text_symbolizer : public symbolizer_base
void set_text_size(float size); void set_text_size(float size);
std::string const& get_face_name() const func_deprecated; std::string const& get_face_name() const func_deprecated;
void set_face_name(std::string face_name); void set_face_name(std::string face_name);
font_set const& get_fontset() const func_deprecated; boost::optional<font_set> const& get_fontset() const func_deprecated;
void set_fontset(font_set const& fset); void set_fontset(font_set const& fset);
color const& get_fill() const func_deprecated; color const& get_fill() const func_deprecated;
void set_fill(color const& fill); void set_fill(color const& fill);

View file

@ -38,6 +38,7 @@
#if BOOST_VERSION >= 104500 #if BOOST_VERSION >= 104500
#include <boost/config/warning_disable.hpp> #include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/karma.hpp> #include <boost/spirit/include/karma.hpp>
#include <boost/math/special_functions/trunc.hpp> // trunc to avoid needing C++11
#else #else
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#endif #endif
@ -67,8 +68,80 @@ template <typename T>
struct double_policy : boost::spirit::karma::real_policies<T> struct double_policy : boost::spirit::karma::real_policies<T>
{ {
typedef boost::spirit::karma::real_policies<T> base_type; typedef boost::spirit::karma::real_policies<T> base_type;
static int floatfield(T n) { return base_type::fmtflags::fixed; }
static unsigned precision(T n) { return 16 ;} static int floatfield(T n) {
using namespace boost::spirit; // for traits
if (traits::test_zero(n))
return base_type::fmtflags::fixed;
T abs_n = traits::get_absolute_value(n);
return (abs_n >= 1e16 || abs_n < 1e-4)
? base_type::fmtflags::scientific : base_type::fmtflags::fixed;
}
static unsigned precision(T n) {
if ( n == 0.0 ) return 0;
using namespace boost::spirit; // for traits
return static_cast<unsigned>(14 - boost::math::trunc(log10(traits::get_absolute_value(n))));
}
template <typename OutputIterator>
static bool dot(OutputIterator& sink, T n, unsigned precision) {
if (n == 0.0) return true; // avoid trailing zeroes
return base_type::dot(sink, n, precision);
}
template <typename OutputIterator>
static bool fraction_part (OutputIterator& sink, T n
, unsigned precision_, unsigned precision)
{
// NOTE: copied from karma only to avoid trailing zeroes
// (maybe a bug ?)
// allow for ADL to find the correct overload for floor and log10
using namespace std;
using namespace boost::spirit; // for traits
using namespace boost::spirit::karma; // for char_inserter
using namespace boost; // for remove_const
if ( traits::test_zero(n) ) return true; // this part added to karma
// The following is equivalent to:
// generate(sink, right_align(precision, '0')[ulong], n);
// but it's spelled out to avoid inter-modular dependencies.
typename remove_const<T>::type digits =
(traits::test_zero(n) ? 0 : floor(log10(n))) + 1;
bool r = true;
for (/**/; r && digits < precision_; digits = digits + 1)
r = char_inserter<>::call(sink, '0');
if (precision && r)
r = int_inserter<10>::call(sink, n);
return r;
}
template <typename CharEncoding, typename Tag, typename OutputIterator>
static bool exponent (OutputIterator& sink, long n)
{
// NOTE: copied from karma to force sign in exponent
const bool force_sign = true;
using namespace boost::spirit; // for traits
using namespace boost::spirit::karma; // for char_inserter, sign_inserter
long abs_n = traits::get_absolute_value(n);
bool r = char_inserter<CharEncoding, Tag>::call(sink, 'e') &&
sign_inserter::call(sink, traits::test_zero(n)
, traits::test_negative(n), force_sign);
// the C99 Standard requires at least two digits in the exponent
if (r && abs_n < 10)
r = char_inserter<CharEncoding, Tag>::call(sink, '0');
return r && int_inserter<10>::call(sink, abs_n);
}
}; };
@ -77,7 +150,7 @@ template <>
inline bool to_string(std::string & str, double value) inline bool to_string(std::string & str, double value)
{ {
namespace karma = boost::spirit::karma; namespace karma = boost::spirit::karma;
typedef boost::spirit::karma::real_generator<double, double_policy<double> > double_type; typedef karma::real_generator<double, double_policy<double> > double_type;
std::back_insert_iterator<std::string> sink(str); std::back_insert_iterator<std::string> sink(str);
return karma::generate(sink, double_type(), value); return karma::generate(sink, double_type(), value);
} }

View file

@ -23,13 +23,13 @@
#ifndef MAPNIK_VERSION_HPP #ifndef MAPNIK_VERSION_HPP
#define MAPNIK_VERSION_HPP #define MAPNIK_VERSION_HPP
#define MAPNIK_VERSION_IS_RELEASE 1 #define MAPNIK_VERSION_IS_RELEASE 0
#define MAPNIK_MAJOR_VERSION 2 #define MAPNIK_MAJOR_VERSION 2
#define MAPNIK_MINOR_VERSION 1 #define MAPNIK_MINOR_VERSION 1
#define MAPNIK_PATCH_VERSION 0 #define MAPNIK_PATCH_VERSION 1
// translates to 200100 // translates to 200101
#define MAPNIK_VERSION (MAPNIK_MAJOR_VERSION*100000) + (MAPNIK_MINOR_VERSION*100) + (MAPNIK_PATCH_VERSION) #define MAPNIK_VERSION (MAPNIK_MAJOR_VERSION*100000) + (MAPNIK_MINOR_VERSION*100) + (MAPNIK_PATCH_VERSION)
#ifndef MAPNIK_STRINGIFY #ifndef MAPNIK_STRINGIFY

View file

@ -347,6 +347,16 @@ struct vertex_converter : private boost::noncopyable
disp_.vec_[index]=1; disp_.vec_[index]=1;
} }
template <typename Conv>
void unset()
{
typedef typename boost::mpl::find<conv_types,Conv>::type iter;
typedef typename boost::mpl::end<conv_types>::type end;
std::size_t index = boost::mpl::distance<iter,end>::value - 1;
if (index < disp_.vec_.size())
disp_.vec_[index]=0;
}
detail::dispatcher<args_type,conv_types> disp_; detail::dispatcher<args_type,conv_types> disp_;
}; };

View file

@ -53,7 +53,7 @@ public:
{ {
std::ostringstream s; std::ostringstream s;
s << "Postgis Plugin: "; s << "Postgis Plugin: ";
if (conn_ ) if (conn_)
{ {
std::string msg = PQerrorMessage(conn_); std::string msg = PQerrorMessage(conn_);
if (! msg.empty()) if (! msg.empty())
@ -64,13 +64,13 @@ public:
{ {
s << "unable to connect to postgres server"; s << "unable to connect to postgres server";
} }
PQfinish(conn_);
} }
else else
{ {
s << "unable to connect to postgres server"; s << "unable to connect to postgres server";
} }
s << "\n" << connection_str; s << "\n" << connection_str;
throw mapnik::datasource_exception(s.str()); throw mapnik::datasource_exception(s.str());
} }
} }
@ -152,7 +152,7 @@ public:
bool isOK() const bool isOK() const
{ {
return (PQstatus(conn_) != CONNECTION_BAD); return (!closed_) && (PQstatus(conn_) != CONNECTION_BAD);
} }
void close() void close()

View file

@ -127,11 +127,12 @@ void postgis_datasource::bind() const
if (pool) if (pool)
{ {
shared_ptr<Connection> conn = pool->borrowObject(); shared_ptr<Connection> conn = pool->borrowObject();
if (conn && conn->isOK()) if (!conn) return;
{
PoolGuard<shared_ptr<Connection>,
shared_ptr< Pool<Connection,ConnectionCreator> > > guard(conn, pool);
PoolGuard<shared_ptr<Connection>,
shared_ptr< Pool<Connection,ConnectionCreator> > > guard(conn, pool);
if (conn->isOK())
{
desc_.set_encoding(conn->client_encoding()); desc_.set_encoding(conn->client_encoding());
if (geometry_table_.empty()) if (geometry_table_.empty())
@ -391,6 +392,7 @@ void postgis_datasource::bind() const
case 1042: // bpchar case 1042: // bpchar
case 1043: // varchar case 1043: // varchar
case 25: // text case 25: // text
case 705: // literal
desc_.add_descriptor(attribute_descriptor(fld_name, mapnik::String)); desc_.add_descriptor(attribute_descriptor(fld_name, mapnik::String));
break; break;
default: // should not get here default: // should not get here
@ -434,6 +436,7 @@ postgis_datasource::~postgis_datasource()
if (conn) if (conn)
{ {
conn->close(); conn->close();
pool->returnObject(conn);
} }
} }
} }
@ -654,7 +657,10 @@ featureset_ptr postgis_datasource::features(const query& q) const
s << "\"" << geometryColumn_ << "\""; s << "\"" << geometryColumn_ << "\"";
if (simplify_geometries_) { if (simplify_geometries_) {
const double tolerance = std::min(px_gw, px_gh) / 2.0; // 1/20 of pixel seems to be a good compromise to avoid
// drop of collapsed polygons.
// See https://github.com/mapnik/mapnik/issues/1639
const double tolerance = std::min(px_gw, px_gh) / 20.0;
s << ", " << tolerance << ")"; s << ", " << tolerance << ")";
} }
@ -702,7 +708,17 @@ featureset_ptr postgis_datasource::features(const query& q) const
} }
else else
{ {
throw mapnik::datasource_exception("Postgis Plugin: bad connection"); std::string err_msg = "Postgis Plugin:";
if (conn)
{
err_msg += " Bad connection";
pool->returnObject(conn);
}
else
{
err_msg += " Null connection";
}
throw mapnik::datasource_exception(err_msg);
} }
} }
@ -725,10 +741,11 @@ featureset_ptr postgis_datasource::features_at_point(coord2d const& pt) const
if (pool) if (pool)
{ {
shared_ptr<Connection> conn = pool->borrowObject(); shared_ptr<Connection> conn = pool->borrowObject();
if (conn && conn->isOK()) if (!conn) return featureset_ptr();
{ PoolGuard<shared_ptr<Connection>, shared_ptr< Pool<Connection,ConnectionCreator> > > guard(conn, pool);
PoolGuard<shared_ptr<Connection>, shared_ptr< Pool<Connection,ConnectionCreator> > > guard(conn, pool);
if (conn->isOK())
{
if (geometryColumn_.empty()) if (geometryColumn_.empty())
{ {
std::ostringstream s_error; std::ostringstream s_error;
@ -815,10 +832,10 @@ box2d<double> postgis_datasource::envelope() const
if (pool) if (pool)
{ {
shared_ptr<Connection> conn = pool->borrowObject(); shared_ptr<Connection> conn = pool->borrowObject();
if (conn && conn->isOK()) if (!conn) return extent_;
PoolGuard<shared_ptr<Connection>, shared_ptr< Pool<Connection,ConnectionCreator> > > guard(conn, pool);
if (conn->isOK())
{ {
PoolGuard<shared_ptr<Connection>, shared_ptr< Pool<Connection,ConnectionCreator> > > guard(conn, pool);
std::ostringstream s; std::ostringstream s;
boost::optional<mapnik::boolean> estimate_extent = boost::optional<mapnik::boolean> estimate_extent =
@ -916,10 +933,10 @@ boost::optional<mapnik::datasource::geometry_t> postgis_datasource::get_geometry
if (pool) if (pool)
{ {
shared_ptr<Connection> conn = pool->borrowObject(); shared_ptr<Connection> conn = pool->borrowObject();
if (conn && conn->isOK()) if (!conn) return result;
PoolGuard<shared_ptr<Connection>, shared_ptr< Pool<Connection,ConnectionCreator> > > guard(conn, pool);
if (conn->isOK())
{ {
PoolGuard<shared_ptr<Connection>, shared_ptr< Pool<Connection,ConnectionCreator> > > guard(conn, pool);
std::ostringstream s; std::ostringstream s;
std::string g_type; std::string g_type;

View file

@ -174,6 +174,7 @@ feature_ptr postgis_featureset::next()
case 25: //text case 25: //text
case 1043: //varchar case 1043: //varchar
case 705: //literal
{ {
feature->put(name, tr_->transcode(buf)); feature->put(name, tr_->transcode(buf));
break; break;

View file

@ -14,7 +14,8 @@ plugin_env = plugin_base.Clone()
plugin_sources = Split( plugin_sources = Split(
""" """
%(PLUGIN_NAME)s_datasource.cpp %(PLUGIN_NAME)s_datasource.cpp
%(PLUGIN_NAME)s_featureset.cpp %(PLUGIN_NAME)s_featureset.cpp
%(PLUGIN_NAME)s_utils.cpp
""" % locals() """ % locals()
) )
@ -25,12 +26,7 @@ libraries = ['mapnik',env['BOOST_PYTHON_LIB'],boost_system,env['ICU_LIB_NAME']]
# python plugin is used by a app in python using mapnik's python bindings # python plugin is used by a app in python using mapnik's python bindings
# we explicitly link to libpython here so that this plugin # we explicitly link to libpython here so that this plugin
# can be used from a pure C++ calling application or a different binding language # can be used from a pure C++ calling application or a different binding language
python_link_flag = '-lpython%s' % env['PYTHON_VERSION'] if env['PLATFORM'] == 'Darwin' and env['FRAMEWORK_PYTHON']:
if env['PLATFORM'] == 'Darwin':
if env['PYTHON_DYNAMIC_LOOKUP']:
python_link_flag = '-undefined dynamic_lookup'
elif env['FRAMEWORK_PYTHON']:
if env['FRAMEWORK_SEARCH_PATH']: if env['FRAMEWORK_SEARCH_PATH']:
python_link_flag = '-F%s -framework Python -Z' % env['FRAMEWORK_SEARCH_PATH'] python_link_flag = '-F%s -framework Python -Z' % env['FRAMEWORK_SEARCH_PATH']
else: else:
@ -41,6 +37,11 @@ if env['PLATFORM'] == 'Darwin':
python_link_flag = '-F/System/Library/Frameworks/ -framework Python -Z' python_link_flag = '-F/System/Library/Frameworks/ -framework Python -Z'
else: else:
python_link_flag = '-F/ -framework Python' python_link_flag = '-F/ -framework Python'
else:
# on linux the linkflags end up to early in the compile flags to work correctly
python_link_flag = ''
# so instead add to libraries
libraries.append('python%s' % env['PYTHON_VERSION'])
if env['CUSTOM_LDFLAGS']: if env['CUSTOM_LDFLAGS']:
linkflags = '%s %s' % (env['CUSTOM_LDFLAGS'], python_link_flag) linkflags = '%s %s' % (env['CUSTOM_LDFLAGS'], python_link_flag)

View file

@ -62,147 +62,170 @@ mapnik::layer_descriptor python_datasource::get_descriptor() const
void python_datasource::bind() const void python_datasource::bind() const
{ {
using namespace boost; using namespace boost;
using namespace boost::python;
if (is_bound_) return; if (is_bound_) return;
// if no factory callable is defined, bind is a nop // if no factory callable is defined, bind is a nop
if (factory_.empty()) return; if (factory_.empty()) return;
// split factory at ':' to parse out module and callable try
std::vector<std::string> factory_split;
split(factory_split, factory_, is_any_of(":"));
if ((factory_split.size() < 1) || (factory_split.size() > 2))
{
// FIMXE: is this appropriate error reporting?
std::cerr << "python: factory string must be of the form '[module:]callable' when parsing \""
<< factory_ << '"' << std::endl;
return;
}
// extract the module and the callable
str module_name("__main__"), callable_name;
if (factory_split.size() == 1)
{
callable_name = str(factory_split[0]);
}
else
{
module_name = str(factory_split[0]);
callable_name = str(factory_split[1]);
}
{ {
// split factory at ':' to parse out module and callable
std::vector<std::string> factory_split;
split(factory_split, factory_, is_any_of(":"));
if ((factory_split.size() < 1) || (factory_split.size() > 2))
{
throw mapnik::datasource_exception(
std::string("python: factory string must be of the form '[module:]callable' when parsing \"")
+ factory_ + '"');
}
// extract the module and the callable
boost::python::str module_name("__main__"), callable_name;
if (factory_split.size() == 1)
{
callable_name = boost::python::str(factory_split[0]);
}
else
{
module_name = boost::python::str(factory_split[0]);
callable_name = boost::python::str(factory_split[1]);
}
ensure_gil lock; ensure_gil lock;
// import the main module from Python (in case we're embedding the // import the main module from Python (in case we're embedding the
// interpreter directly) and also import the callable. // interpreter directly) and also import the callable.
object main_module = import("__main__"); boost::python::object main_module = boost::python::import("__main__");
object callable_module = import(module_name); boost::python::object callable_module = boost::python::import(module_name);
object callable = callable_module.attr(callable_name); boost::python::object callable = callable_module.attr(callable_name);
// prepare the arguments // prepare the arguments
dict kwargs; boost::python::dict kwargs;
typedef std::map<std::string, std::string>::value_type kv_type; typedef std::map<std::string, std::string>::value_type kv_type;
BOOST_FOREACH(const kv_type& kv, kwargs_) BOOST_FOREACH(const kv_type& kv, kwargs_)
{ {
kwargs[str(kv.first)] = str(kv.second); kwargs[boost::python::str(kv.first)] = boost::python::str(kv.second);
} }
// get our wrapped data source // get our wrapped data source
datasource_ = callable(*boost::python::make_tuple(), **kwargs); datasource_ = callable(*boost::python::make_tuple(), **kwargs);
} }
catch ( boost::python::error_already_set )
{
throw mapnik::datasource_exception(extractException());
}
is_bound_ = true; is_bound_ = true;
} }
mapnik::datasource::datasource_t python_datasource::type() const mapnik::datasource::datasource_t python_datasource::type() const
{ {
using namespace boost::python;
typedef boost::optional<mapnik::datasource::geometry_t> return_type; typedef boost::optional<mapnik::datasource::geometry_t> return_type;
if (!is_bound_) bind(); if (!is_bound_) bind();
ensure_gil lock; try
{
ensure_gil lock;
boost::python::object data_type = datasource_.attr("data_type");
long data_type_integer = boost::python::extract<long>(data_type);
return mapnik::datasource::datasource_t(data_type_integer);
}
catch ( boost::python::error_already_set )
{
throw mapnik::datasource_exception(extractException());
}
object data_type = datasource_.attr("data_type");
long data_type_integer = extract<long>(data_type);
return mapnik::datasource::datasource_t(data_type_integer);
} }
mapnik::box2d<double> python_datasource::envelope() const mapnik::box2d<double> python_datasource::envelope() const
{ {
using namespace boost::python;
if (!is_bound_) bind(); if (!is_bound_) bind();
ensure_gil lock; try
return extract<mapnik::box2d<double> >(datasource_.attr("envelope")); {
ensure_gil lock;
return boost::python::extract<mapnik::box2d<double> >(datasource_.attr("envelope"));
}
catch ( boost::python::error_already_set )
{
throw mapnik::datasource_exception(extractException());
}
} }
boost::optional<mapnik::datasource::geometry_t> python_datasource::get_geometry_type() const boost::optional<mapnik::datasource::geometry_t> python_datasource::get_geometry_type() const
{ {
using namespace boost::python;
typedef boost::optional<mapnik::datasource::geometry_t> return_type; typedef boost::optional<mapnik::datasource::geometry_t> return_type;
if (!is_bound_) bind(); if (!is_bound_) bind();
ensure_gil lock; try
{
// if the datasource object has no geometry_type attribute, return a 'none' value ensure_gil lock;
if (!PyObject_HasAttrString(datasource_.ptr(), "geometry_type")) // if the datasource object has no geometry_type attribute, return a 'none' value
return return_type(); if (!PyObject_HasAttrString(datasource_.ptr(), "geometry_type"))
{
object py_geometry_type = datasource_.attr("geometry_type"); return return_type();
}
// if the attribute value is 'None', return a 'none' value boost::python::object py_geometry_type = datasource_.attr("geometry_type");
if (py_geometry_type.ptr() == object().ptr()) // if the attribute value is 'None', return a 'none' value
return return_type(); if (py_geometry_type.ptr() == boost::python::object().ptr())
{
long geom_type_integer = extract<long>(py_geometry_type); return return_type();
return mapnik::datasource::geometry_t(geom_type_integer); }
long geom_type_integer = boost::python::extract<long>(py_geometry_type);
return mapnik::datasource::geometry_t(geom_type_integer);
}
catch ( boost::python::error_already_set )
{
throw mapnik::datasource_exception(extractException());
}
} }
mapnik::featureset_ptr python_datasource::features(mapnik::query const& q) const mapnik::featureset_ptr python_datasource::features(mapnik::query const& q) const
{ {
using namespace boost::python;
if (!is_bound_) bind(); if (!is_bound_) bind();
// if the query box intersects our world extent then query for features try
if (envelope().intersects(q.get_bbox()))
{ {
ensure_gil lock; // if the query box intersects our world extent then query for features
if (envelope().intersects(q.get_bbox()))
object features(datasource_.attr("features")(q)); {
ensure_gil lock;
// if 'None' was returned, return an empty feature set boost::python::object features(datasource_.attr("features")(q));
if(features.ptr() == object().ptr()) // if 'None' was returned, return an empty feature set
return mapnik::featureset_ptr(); if(features.ptr() == boost::python::object().ptr())
{
return boost::make_shared<python_featureset>(features); return mapnik::featureset_ptr();
}
return boost::make_shared<python_featureset>(features);
}
// otherwise return an empty featureset pointer
return mapnik::featureset_ptr();
}
catch ( boost::python::error_already_set )
{
throw mapnik::datasource_exception(extractException());
} }
// otherwise return an empty featureset pointer
return mapnik::featureset_ptr();
} }
mapnik::featureset_ptr python_datasource::features_at_point(mapnik::coord2d const& pt) const mapnik::featureset_ptr python_datasource::features_at_point(mapnik::coord2d const& pt) const
{ {
using namespace boost::python;
if (!is_bound_) bind(); if (!is_bound_) bind();
ensure_gil lock; try
{
ensure_gil lock;
boost::python::object features(datasource_.attr("features_at_point")(pt));
// if we returned none, return an empty set
if(features.ptr() == boost::python::object().ptr())
{
return mapnik::featureset_ptr();
}
// otherwise, return a feature set which can iterate over the iterator
return boost::make_shared<python_featureset>(features);
}
catch ( boost::python::error_already_set )
{
throw mapnik::datasource_exception(extractException());
}
object features(datasource_.attr("features_at_point")(pt)); }
// if we returned none, return an empty set
if(features.ptr() == object().ptr())
return mapnik::featureset_ptr();
// otherwise, return a feature set which can iterate over the iterator
return boost::make_shared<python_featureset>(features);
}

View file

@ -0,0 +1,23 @@
#include "python_utils.hpp"
std::string extractException()
{
using namespace boost::python;
PyObject *exc,*val,*tb;
PyErr_Fetch(&exc,&val,&tb);
PyErr_NormalizeException(&exc,&val,&tb);
handle<> hexc(exc),hval(allow_null(val)),htb(allow_null(tb));
if(!hval)
{
return extract<std::string>(str(hexc));
}
else
{
object traceback(import("traceback"));
object format_exception(traceback.attr("format_exception"));
object formatted_list(format_exception(hexc,hval,htb));
object formatted(str("").join(formatted_list));
return extract<std::string>(formatted);
}
}

View file

@ -13,4 +13,6 @@ class ensure_gil
PyGILState_STATE gil_state_; PyGILState_STATE gil_state_;
}; };
std::string extractException();
#endif // PYTHON_UTILS_HPP #endif // PYTHON_UTILS_HPP

View file

@ -26,6 +26,7 @@
#include <mapnik/agg_rasterizer.hpp> #include <mapnik/agg_rasterizer.hpp>
#include <mapnik/debug.hpp> #include <mapnik/debug.hpp>
#include <mapnik/feature.hpp>
#include <mapnik/geom_util.hpp> #include <mapnik/geom_util.hpp>
#include <mapnik/expression_evaluator.hpp> #include <mapnik/expression_evaluator.hpp>
#include <mapnik/vertex_converters.hpp> #include <mapnik/vertex_converters.hpp>
@ -136,10 +137,7 @@ void agg_renderer<T>::process(markers_symbolizer const& sym,
} }
converter.template set<transform_tag>(); //always transform converter.template set<transform_tag>(); //always transform
if (sym.smooth() > 0.0) converter.template set<smooth_tag>(); // optional smooth converter if (sym.smooth() > 0.0) converter.template set<smooth_tag>(); // optional smooth converter
BOOST_FOREACH(geometry_type & geom, feature.paths()) apply_markers_multi(feature, converter, sym);
{
converter.apply(geom);
}
} }
else else
{ {
@ -172,10 +170,7 @@ void agg_renderer<T>::process(markers_symbolizer const& sym,
} }
converter.template set<transform_tag>(); //always transform converter.template set<transform_tag>(); //always transform
if (sym.smooth() > 0.0) converter.template set<smooth_tag>(); // optional smooth converter if (sym.smooth() > 0.0) converter.template set<smooth_tag>(); // optional smooth converter
BOOST_FOREACH(geometry_type & geom, feature.paths()) apply_markers_multi(feature, converter, sym);
{
converter.apply(geom);
}
} }
} }
else // raster markers else // raster markers
@ -207,11 +202,7 @@ void agg_renderer<T>::process(markers_symbolizer const& sym,
} }
converter.template set<transform_tag>(); //always transform converter.template set<transform_tag>(); //always transform
if (sym.smooth() > 0.0) converter.template set<smooth_tag>(); // optional smooth converter if (sym.smooth() > 0.0) converter.template set<smooth_tag>(); // optional smooth converter
apply_markers_multi(feature, converter, sym);
BOOST_FOREACH(geometry_type & geom, feature.paths())
{
converter.apply(geom);
}
} }
} }
} }

View file

@ -1494,11 +1494,17 @@ struct markers_dispatch
{ {
marker_placement_e placement_method = sym_.get_marker_placement(); marker_placement_e placement_method = sym_.get_marker_placement();
if (placement_method != MARKER_LINE_PLACEMENT) if (placement_method != MARKER_LINE_PLACEMENT ||
path.type() == Point)
{ {
double x = 0; double x = 0;
double y = 0; double y = 0;
if (placement_method == MARKER_INTERIOR_PLACEMENT) if (path.type() == LineString)
{
if (!label::middle_point(path, x, y))
return;
}
else if (placement_method == MARKER_INTERIOR_PLACEMENT)
{ {
if (!label::interior_position(path, x, y)) if (!label::interior_position(path, x, y))
return; return;
@ -1577,11 +1583,17 @@ struct markers_dispatch_2
{ {
marker_placement_e placement_method = sym_.get_marker_placement(); marker_placement_e placement_method = sym_.get_marker_placement();
if (placement_method != MARKER_LINE_PLACEMENT) if (placement_method != MARKER_LINE_PLACEMENT ||
path.type() == Point)
{ {
double x = 0; double x = 0;
double y = 0; double y = 0;
if (placement_method == MARKER_INTERIOR_PLACEMENT) if (path.type() == LineString)
{
if (!label::middle_point(path, x, y))
return;
}
else if (placement_method == MARKER_INTERIOR_PLACEMENT)
{ {
if (!label::interior_position(path, x, y)) if (!label::interior_position(path, x, y))
return; return;
@ -1706,10 +1718,7 @@ void cairo_renderer_base::process(markers_symbolizer const& sym,
} }
converter.set<transform_tag>(); //always transform converter.set<transform_tag>(); //always transform
if (sym.smooth() > 0.0) converter.set<smooth_tag>(); // optional smooth converter if (sym.smooth() > 0.0) converter.set<smooth_tag>(); // optional smooth converter
BOOST_FOREACH(geometry_type & geom, feature.paths()) apply_markers_multi(feature, converter, sym);
{
converter.apply(geom);
}
} }
else else
{ {
@ -1734,10 +1743,7 @@ void cairo_renderer_base::process(markers_symbolizer const& sym,
} }
converter.set<transform_tag>(); //always transform converter.set<transform_tag>(); //always transform
if (sym.smooth() > 0.0) converter.set<smooth_tag>(); // optional smooth converter if (sym.smooth() > 0.0) converter.set<smooth_tag>(); // optional smooth converter
BOOST_FOREACH(geometry_type & geom, feature.paths()) apply_markers_multi(feature, converter, sym);
{
converter.apply(geom);
}
} }
} }
else // raster markers else // raster markers
@ -1767,10 +1773,7 @@ void cairo_renderer_base::process(markers_symbolizer const& sym,
} }
converter.set<transform_tag>(); //always transform converter.set<transform_tag>(); //always transform
if (sym.smooth() > 0.0) converter.set<smooth_tag>(); // optional smooth converter if (sym.smooth() > 0.0) converter.set<smooth_tag>(); // optional smooth converter
BOOST_FOREACH(geometry_type & geom, feature.paths()) apply_markers_multi(feature, converter, sym);
{
converter.apply(geom);
}
} }
} }
} }

View file

@ -60,14 +60,10 @@ logger::severity_type logger::severity_level_ =
#if MAPNIK_DEFAULT_LOG_SEVERITY == 0 #if MAPNIK_DEFAULT_LOG_SEVERITY == 0
logger::debug logger::debug
#elif MAPNIK_DEFAULT_LOG_SEVERITY == 1 #elif MAPNIK_DEFAULT_LOG_SEVERITY == 1
logger::info
#elif MAPNIK_DEFAULT_LOG_SEVERITY == 2
logger::warn logger::warn
#elif MAPNIK_DEFAULT_LOG_SEVERITY == 3 #elif MAPNIK_DEFAULT_LOG_SEVERITY == 2
logger::error logger::error
#elif MAPNIK_DEFAULT_LOG_SEVERITY == 4 #elif MAPNIK_DEFAULT_LOG_SEVERITY == 3
logger::fatal
#elif MAPNIK_DEFAULT_LOG_SEVERITY == 5
logger::none logger::none
#else #else
#error "Wrong default log severity level specified!" #error "Wrong default log severity level specified!"

View file

@ -207,6 +207,7 @@ void feature_style_processor<Processor>::apply_to_layer(layer const& lay, Proces
double scale_denom, double scale_denom,
std::set<std::string>& names) std::set<std::string>& names)
{ {
try {
std::vector<std::string> const& style_names = lay.styles(); std::vector<std::string> const& style_names = lay.styles();
unsigned int num_styles = style_names.size(); unsigned int num_styles = style_names.size();
@ -477,6 +478,15 @@ void feature_style_processor<Processor>::apply_to_layer(layer const& lay, Proces
#endif #endif
p.end_layer_processing(lay); p.end_layer_processing(lay);
} catch (std::exception const& e) {
// Include layer name in exception
// see https://github.com/mapnik/mapnik/issues/1924
std::ostringstream m;
m << lay.name() << ": " << e.what();
throw std::runtime_error(m.str().c_str());
}
} }

View file

@ -29,8 +29,6 @@
namespace mapnik namespace mapnik
{ {
font_set::font_set()
: name_("") {}
font_set::font_set(std::string const& name) font_set::font_set(std::string const& name)
: name_(name) {} : name_(name) {}
@ -61,6 +59,11 @@ void font_set::add_face_name(std::string face_name)
face_names_.push_back(face_name); face_names_.push_back(face_name);
} }
void font_set::set_name(std::string const& name)
{
name_ = name;
}
std::string const& font_set::get_name() const std::string const& font_set::get_name() const
{ {
return name_; return name_;

View file

@ -163,10 +163,7 @@ void grid_renderer<T>::process(markers_symbolizer const& sym,
} }
converter.template set<transform_tag>(); //always transform converter.template set<transform_tag>(); //always transform
if (sym.smooth() > 0.0) converter.template set<smooth_tag>(); // optional smooth converter if (sym.smooth() > 0.0) converter.template set<smooth_tag>(); // optional smooth converter
BOOST_FOREACH(geometry_type & geom, feature.paths()) apply_markers_multi(feature, converter, sym);
{
converter.apply(geom);
}
} }
else else
{ {
@ -208,10 +205,7 @@ void grid_renderer<T>::process(markers_symbolizer const& sym,
} }
converter.template set<transform_tag>(); //always transform converter.template set<transform_tag>(); //always transform
if (sym.smooth() > 0.0) converter.template set<smooth_tag>(); // optional smooth converter if (sym.smooth() > 0.0) converter.template set<smooth_tag>(); // optional smooth converter
BOOST_FOREACH(geometry_type & geom, feature.paths()) apply_markers_multi(feature, converter, sym);
{
converter.apply(geom);
}
} }
} }
else // raster markers else // raster markers
@ -256,10 +250,7 @@ void grid_renderer<T>::process(markers_symbolizer const& sym,
} }
converter.template set<transform_tag>(); //always transform converter.template set<transform_tag>(); //always transform
if (sym.smooth() > 0.0) converter.template set<smooth_tag>(); // optional smooth converter if (sym.smooth() > 0.0) converter.template set<smooth_tag>(); // optional smooth converter
BOOST_FOREACH(geometry_type & geom, feature.paths()) apply_markers_multi(feature, converter, sym);
{
converter.apply(geom);
}
} }
} }
} }

View file

@ -20,12 +20,17 @@
* *
*****************************************************************************/ *****************************************************************************/
// TODO https://github.com/mapnik/mapnik/issues/1658
#include <boost/version.hpp>
#if BOOST_VERSION >= 105200
#define BOOST_SPIRIT_USE_PHOENIX_V3
#endif
// mapnik // mapnik
#include <mapnik/json/feature_collection_parser.hpp> #include <mapnik/json/feature_collection_parser.hpp>
#include <mapnik/json/feature_collection_grammar.hpp> #include <mapnik/json/feature_collection_grammar.hpp>
// boost // boost
#include <boost/version.hpp>
#include <boost/spirit/include/qi.hpp> #include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/support_multi_pass.hpp> #include <boost/spirit/include/support_multi_pass.hpp>

View file

@ -514,7 +514,7 @@ void map_parser::parse_fontset(Map & map, xml_node const& fset)
// 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 // when it's parsed
fontsets_.insert(pair<std::string, font_set>(name, fontset)); fontsets_.insert(std::pair<std::string, font_set>(name, fontset));
} }
catch (const config_error & ex) catch (const config_error & ex)
{ {
@ -1027,6 +1027,10 @@ void map_parser::parse_markers_symbolizer(rule & rule, xml_node const& node)
marker_placement_e placement = node.get_attr<marker_placement_e>("placement",sym.get_marker_placement()); marker_placement_e placement = node.get_attr<marker_placement_e>("placement",sym.get_marker_placement());
sym.set_marker_placement(placement); sym.set_marker_placement(placement);
marker_multi_policy_e mpolicy = node.get_attr<marker_multi_policy_e>("multi-policy",sym.get_marker_multi_policy());
sym.set_marker_multi_policy(mpolicy);
parse_symbolizer_base(sym, node); parse_symbolizer_base(sym, node);
rule.append(sym); rule.append(sym);
} }
@ -1148,7 +1152,7 @@ void map_parser::parse_text_symbolizer(rule & rule, xml_node const& sym)
placement_finder->defaults.from_xml(sym, fontsets_); placement_finder->defaults.from_xml(sym, fontsets_);
} }
if (strict_ && if (strict_ &&
!placement_finder->defaults.format.fontset.size()) !placement_finder->defaults.format.fontset)
{ {
ensure_font_face(placement_finder->defaults.format.face_name); ensure_font_face(placement_finder->defaults.format.face_name);
} }
@ -1177,7 +1181,7 @@ void map_parser::parse_shield_symbolizer(rule & rule, xml_node const& sym)
} }
placement_finder->defaults.from_xml(sym, fontsets_); placement_finder->defaults.from_xml(sym, fontsets_);
if (strict_ && if (strict_ &&
!placement_finder->defaults.format.fontset.size()) !placement_finder->defaults.format.fontset)
{ {
ensure_font_face(placement_finder->defaults.format.face_name); ensure_font_face(placement_finder->defaults.format.face_name);
} }

View file

@ -159,6 +159,10 @@ boost::optional<feature_type_style const&> Map::find_style(std::string const& na
bool Map::insert_fontset(std::string const& name, font_set const& fontset) bool Map::insert_fontset(std::string const& name, font_set const& fontset)
{ {
if (fontset.get_name() != name)
{
throw mapnik::config_error("Fontset name must match the name used to reference it on the map");
}
return fontsets_.insert(make_pair(name, fontset)).second; return fontsets_.insert(make_pair(name, fontset)).second;
} }

View file

@ -37,6 +37,15 @@ static const char * marker_placement_strings[] = {
IMPLEMENT_ENUM( marker_placement_e, marker_placement_strings ) IMPLEMENT_ENUM( marker_placement_e, marker_placement_strings )
static const char * marker_multi_policy_strings[] = {
"each",
"whole",
"largest",
""
};
IMPLEMENT_ENUM( marker_multi_policy_e, marker_multi_policy_strings )
markers_symbolizer::markers_symbolizer() markers_symbolizer::markers_symbolizer()
: symbolizer_with_image(parse_path("shape://ellipse")), : symbolizer_with_image(parse_path("shape://ellipse")),
symbolizer_base(), symbolizer_base(),
@ -46,7 +55,10 @@ markers_symbolizer::markers_symbolizer()
allow_overlap_(false), allow_overlap_(false),
spacing_(100.0), spacing_(100.0),
max_error_(0.2), max_error_(0.2),
marker_p_(MARKER_POINT_PLACEMENT) { } marker_p_(MARKER_POINT_PLACEMENT),
// TODO: consider defaulting to MARKER_WHOLE_MULTI,
// for backward compatibility with 2.0.0
marker_mp_(MARKER_EACH_MULTI) { }
markers_symbolizer::markers_symbolizer(path_expression_ptr const& filename) markers_symbolizer::markers_symbolizer(path_expression_ptr const& filename)
: symbolizer_with_image(filename), : symbolizer_with_image(filename),
@ -57,7 +69,10 @@ markers_symbolizer::markers_symbolizer(path_expression_ptr const& filename)
allow_overlap_(false), allow_overlap_(false),
spacing_(100.0), spacing_(100.0),
max_error_(0.2), max_error_(0.2),
marker_p_(MARKER_POINT_PLACEMENT) { } marker_p_(MARKER_POINT_PLACEMENT),
// TODO: consider defaulting to MARKER_WHOLE_MULTI,
// for backward compatibility with 2.0.0
marker_mp_(MARKER_EACH_MULTI) { }
markers_symbolizer::markers_symbolizer(markers_symbolizer const& rhs) markers_symbolizer::markers_symbolizer(markers_symbolizer const& rhs)
: symbolizer_with_image(rhs), : symbolizer_with_image(rhs),
@ -71,7 +86,8 @@ markers_symbolizer::markers_symbolizer(markers_symbolizer const& rhs)
fill_(rhs.fill_), fill_(rhs.fill_),
fill_opacity_(rhs.fill_opacity_), fill_opacity_(rhs.fill_opacity_),
stroke_(rhs.stroke_), stroke_(rhs.stroke_),
marker_p_(rhs.marker_p_) {} marker_p_(rhs.marker_p_),
marker_mp_(rhs.marker_mp_) {}
void markers_symbolizer::set_ignore_placement(bool ignore_placement) void markers_symbolizer::set_ignore_placement(bool ignore_placement)
{ {
@ -173,4 +189,14 @@ marker_placement_e markers_symbolizer::get_marker_placement() const
return marker_p_; return marker_p_;
} }
void markers_symbolizer::set_marker_multi_policy(marker_multi_policy_e marker_mp)
{
marker_mp_ = marker_mp;
}
marker_multi_policy_e markers_symbolizer::get_marker_multi_policy() const
{
return marker_mp_;
}
} }

View file

@ -65,22 +65,22 @@ string_info &processed_text::get_string_info()
face_set_ptr faces = font_manager_.get_face_set(p.face_name, p.fontset); face_set_ptr faces = font_manager_.get_face_set(p.face_name, p.fontset);
if (faces->size() == 0) if (faces->size() == 0)
{ {
if (!p.fontset.get_name().empty()) if (p.fontset && !p.fontset->get_name().empty())
{ {
if (p.fontset.size()) if (p.fontset->size())
{ {
if (!p.face_name.empty()) if (!p.face_name.empty())
{ {
throw config_error("Unable to find specified font face '" + p.face_name + "' in font set: '" + p.fontset.get_name() + "'"); throw config_error("Unable to find specified font face '" + p.face_name + "' in font set: '" + p.fontset->get_name() + "'");
} }
else else
{ {
throw config_error("No valid font face could be loaded for font set: '" + p.fontset.get_name() + "'"); throw config_error("No valid font face could be loaded for font set: '" + p.fontset->get_name() + "'");
} }
} }
else else
{ {
throw config_error("Font set '" + p.fontset.get_name() + "' does not contain any Font face-name entries"); throw config_error("Font set '" + p.fontset->get_name() + "' does not contain any Font face-name entries");
} }
} }
else if (!p.face_name.empty()) else if (!p.face_name.empty())

View file

@ -98,6 +98,10 @@ public:
{ {
set_attr( sym_node, "rasterizer", sym.get_rasterizer() ); set_attr( sym_node, "rasterizer", sym.get_rasterizer() );
} }
if ( sym.offset() != dfl.offset() || explicit_defaults_ )
{
set_attr( sym_node, "offset", sym.offset() );
}
serialize_symbolizer_base(sym_node, sym); serialize_symbolizer_base(sym_node, sym);
} }
@ -311,6 +315,10 @@ public:
{ {
set_attr( sym_node, "placement", sym.get_marker_placement() ); set_attr( sym_node, "placement", sym.get_marker_placement() );
} }
if ( sym.get_marker_multi_policy() != dfl.get_marker_multi_policy() || explicit_defaults_ )
{
set_attr( sym_node, "multi-policy", sym.get_marker_multi_policy() );
}
if (sym.get_image_transform()) if (sym.get_image_transform())
{ {
std::string tr_str = sym.get_image_transform_string(); std::string tr_str = sym.get_image_transform_string();

View file

@ -293,11 +293,11 @@ void char_properties::from_xml(xml_node const& sym, fontset_map const& fontsets)
throw config_error("Unable to find any fontset named '" + *fontset_name_ + "'", sym); throw config_error("Unable to find any fontset named '" + *fontset_name_ + "'", sym);
} }
} }
if (!face_name.empty() && !fontset.get_name().empty()) if (!face_name.empty() && fontset)
{ {
throw config_error("Can't have both face-name and fontset-name", sym); throw config_error("Can't have both face-name and fontset-name", sym);
} }
if (face_name.empty() && fontset.get_name().empty()) if (face_name.empty() && !fontset)
{ {
throw config_error("Must have face-name or fontset-name", sym); throw config_error("Must have face-name or fontset-name", sym);
} }
@ -305,11 +305,9 @@ void char_properties::from_xml(xml_node const& sym, fontset_map const& fontsets)
void char_properties::to_xml(boost::property_tree::ptree &node, bool explicit_defaults, char_properties const &dfl) const void char_properties::to_xml(boost::property_tree::ptree &node, bool explicit_defaults, char_properties const &dfl) const
{ {
std::string const& fontset_name = fontset.get_name(); if (fontset)
std::string const& dfl_fontset_name = dfl.fontset.get_name();
if (fontset_name != dfl_fontset_name || explicit_defaults)
{ {
set_attr(node, "fontset-name", fontset_name); set_attr(node, "fontset-name", fontset->get_name());
} }
if (face_name != dfl.face_name || explicit_defaults) if (face_name != dfl.face_name || explicit_defaults)

View file

@ -173,7 +173,7 @@ void text_symbolizer::set_fontset(font_set const& fontset)
placement_options_->defaults.format.fontset = fontset; placement_options_->defaults.format.fontset = fontset;
} }
font_set const& text_symbolizer::get_fontset() const boost::optional<font_set> const& text_symbolizer::get_fontset() const
{ {
return placement_options_->defaults.format.fontset; return placement_options_->defaults.format.fontset;
} }

View file

@ -486,6 +486,7 @@ compile_get_attr(std::string);
compile_get_attr(filter_mode_e); compile_get_attr(filter_mode_e);
compile_get_attr(point_placement_e); compile_get_attr(point_placement_e);
compile_get_attr(marker_placement_e); compile_get_attr(marker_placement_e);
compile_get_attr(marker_multi_policy_e);
compile_get_attr(pattern_alignment_e); compile_get_attr(pattern_alignment_e);
compile_get_attr(line_rasterizer_e); compile_get_attr(line_rasterizer_e);
compile_get_attr(colorizer_mode); compile_get_attr(colorizer_mode);

View file

@ -0,0 +1,133 @@
#include <boost/version.hpp>
#include <mapnik/util/conversions.hpp>
#include <boost/detail/lightweight_test.hpp>
#include <iostream>
int main( int, char*[] )
{
using mapnik::util::to_string;
try
{
std::string out;
// Test double
to_string(out, double(0));
BOOST_TEST_EQ( out, "0" );
out.clear();
to_string(out, double(1));
BOOST_TEST_EQ( out, "1" );
out.clear();
to_string(out, double(-1));
BOOST_TEST_EQ( out, "-1" );
out.clear();
to_string(out, double(0.1));
BOOST_TEST_EQ( out, "0.1" );
out.clear();
to_string(out, double(-0.1));
BOOST_TEST_EQ( out, "-0.1" );
out.clear();
to_string(out, double(0.123));
BOOST_TEST_EQ( out, "0.123" );
out.clear();
to_string(out, double(-0.123));
BOOST_TEST_EQ( out, "-0.123" );
out.clear();
to_string(out, double(1e-06));
BOOST_TEST_EQ( out, "1e-06" );
out.clear();
to_string(out, double(-1e-06));
BOOST_TEST_EQ( out, "-1e-06" );
out.clear();
to_string(out, double(1e-05));
BOOST_TEST_EQ( out, "1e-05" );
out.clear();
to_string(out, double(-1e-05));
BOOST_TEST_EQ( out, "-1e-05" );
out.clear();
to_string(out, double(0.0001));
BOOST_TEST_EQ( out, "0.0001" );
out.clear();
to_string(out, double(-0.0001));
BOOST_TEST_EQ( out, "-0.0001" );
out.clear();
to_string(out, double(0.0001234567890123456));
// TODO: https://github.com/mapnik/mapnik/issues/1676
//BOOST_TEST_EQ( out, "0.0001234567890123456" );
out.clear();
to_string(out, double(-0.0001234567890123456));
// TODO: https://github.com/mapnik/mapnik/issues/1676
//BOOST_TEST_EQ( out, "-0.0001234567890123456" );
out.clear();
to_string(out, double(1000000000000000));
BOOST_TEST_EQ( out, "1000000000000000" );
out.clear();
to_string(out, double(-1000000000000000));
BOOST_TEST_EQ( out, "-1000000000000000" );
out.clear();
to_string(out, double(10000000000000.1));
BOOST_TEST_EQ( out, "10000000000000.1" );
out.clear();
to_string(out, double(1.00001));
BOOST_TEST_EQ( out, "1.00001" );
out.clear();
to_string(out, double(67.65));
BOOST_TEST_EQ( out, "67.65" );
out.clear();
to_string(out, double(67.35));
BOOST_TEST_EQ( out, "67.35" );
out.clear();
to_string(out, double(1234000000000000));
BOOST_TEST_EQ( out, "1234000000000000" );
out.clear();
to_string(out, double(1.234e+16));
BOOST_TEST_EQ( out, "1.234e+16" );
out.clear();
to_string(out, double(-1.234e+16));
BOOST_TEST_EQ( out, "-1.234e+16" );
out.clear();
// Test int
to_string(out, int(2));
BOOST_TEST_EQ( out, "2" );
out.clear();
}
catch (std::exception const & ex)
{
std::clog << "C++ type conversions problem: " << ex.what() << "\n";
BOOST_TEST(false);
}
if (!::boost::detail::test_errors()) {
std::clog << "C++ type conversions: \x1b[1;32m✓ \x1b[0m\n";
#if BOOST_VERSION >= 104600
::boost::detail::report_errors_remind().called_report_errors_function = true;
#endif
} else {
return ::boost::report_errors();
}
}

View file

@ -31,6 +31,9 @@ int main( int, char*[] )
BOOST_TEST( x == 25 ); BOOST_TEST( x == 25 );
BOOST_TEST( y == 25 ); BOOST_TEST( y == 25 );
// TODO - centroid and interior should be equal but they appear not to be (check largest)
// MULTIPOLYGON(((-52 40,-60 32,-68 40,-60 48,-52 40)),((-60 50,-80 30,-100 49.9999999999999,-80.0000000000001 70,-60 50)),((-52 60,-60 52,-68 60,-60 68,-52 60)))
if (!::boost::detail::test_errors()) { if (!::boost::detail::test_errors()) {
std::clog << "C++ label algorithms: \x1b[1;32m✓ \x1b[0m\n"; std::clog << "C++ label algorithms: \x1b[1;32m✓ \x1b[0m\n";
#if BOOST_VERSION >= 104600 #if BOOST_VERSION >= 104600

View file

@ -20,9 +20,9 @@ map_ = '''<Map>
<![CDATA[ <![CDATA[
([region] >= 0) ([region] >= 0)
and and
([region] <= 50) ([region] <= 50)
]]> ]]>
@ -49,7 +49,7 @@ map_ = '''<Map>
</Style> </Style>
</Map>''' </Map>'''
def test_filter_init(): def test_filter_init():
m = mapnik.Map(1,1) m = mapnik.Map(1,1)
mapnik.load_map_from_string(m,map_) mapnik.load_map_from_string(m,map_)
filters = [] filters = []
@ -74,7 +74,7 @@ def test_filter_init():
0) 0)
and and
([region] ([region]
<= <=
50) 50)
''')) '''))
@ -169,13 +169,17 @@ def test_float_precision():
context = mapnik.Context() context = mapnik.Context()
context.push('num') context.push('num')
f = mapnik.Feature(context,0) f = mapnik.Feature(context,0)
f["num"] = 1.0000 f["num1"] = 1.0000
eq_(f["num"],1.0000) f["num2"] = 1.0001
expr = mapnik.Expression("[num] = 1.0000") eq_(f["num1"],1.0000)
eq_(f["num2"],1.0001)
expr = mapnik.Expression("[num1] = 1.0000")
eq_(expr.evaluate(f),True) eq_(expr.evaluate(f),True)
expr = mapnik.Expression("[num].match('.*0$')") expr = mapnik.Expression("[num1].match('1')")
eq_(expr.evaluate(f),True) eq_(expr.evaluate(f),True)
expr = mapnik.Expression("[num].match('.*0$')") expr = mapnik.Expression("[num2] = 1.0001")
eq_(expr.evaluate(f),True)
expr = mapnik.Expression("[num2].match('1.0001')")
eq_(expr.evaluate(f),True) eq_(expr.evaluate(f),True)
def test_string_matching_on_precision(): def test_string_matching_on_precision():

View file

@ -14,7 +14,7 @@ def setup():
wkts = [ wkts = [
[1,"POINT(30 10)"], [1,"POINT(30 10)"],
[1,"POINT(30.0 10.0)"], [1,"POINT(30 10)"],
[1,"POINT(30.1 10.1)"], [1,"POINT(30.1 10.1)"],
[1,"LINESTRING(30 10,10 30,40 40)"], [1,"LINESTRING(30 10,10 30,40 40)"],
[1,"POLYGON((30 10,10 20,20 40,40 40,30 10))"], [1,"POLYGON((30 10,10 20,20 40,40 40,30 10))"],
@ -61,7 +61,7 @@ wkbs = [
] ]
geojson = [ geojson = [
[1,'{"type":"Point","coordinates":[30.0,10.0]}'], [1,'{"type":"Point","coordinates":[30,10]}'],
[1,'{"type":"Point","coordinates":[30.0,10.0]}'], [1,'{"type":"Point","coordinates":[30.0,10.0]}'],
[1,'{"type":"Point","coordinates":[30.1,10.1]}'], [1,'{"type":"Point","coordinates":[30.1,10.1]}'],
[1,'{"type":"LineString","coordinates":[[30.0,10.0],[10.0,30.0],[40.0,40.0]]}'], [1,'{"type":"LineString","coordinates":[[30.0,10.0],[10.0,30.0],[40.0,40.0]]}'],

View file

@ -97,7 +97,7 @@ def test_text_symbolizer():
# old args required method # old args required method
ts = mapnik.TextSymbolizer(mapnik.Expression('[Field_Name]'), 'Font Name', 8, mapnik.Color('black')) ts = mapnik.TextSymbolizer(mapnik.Expression('[Field_Name]'), 'Font Name', 8, mapnik.Color('black'))
# eq_(str(ts.name), str(mapnik2.Expression('[Field_Name]'))) name field is no longer supported eq_(str(ts.name), str(mapnik.Expression('[Field_Name]')))
eq_(ts.format.face_name, 'Font Name') eq_(ts.format.face_name, 'Font Name')
eq_(ts.format.text_size, 8) eq_(ts.format.text_size, 8)
eq_(ts.format.fill, mapnik.Color('black')) eq_(ts.format.fill, mapnik.Color('black'))
@ -112,7 +112,7 @@ def test_shield_symbolizer_init():
eq_(s.allow_overlap, False) eq_(s.allow_overlap, False)
eq_(s.avoid_edges, False) eq_(s.avoid_edges, False)
eq_(s.character_spacing,0) eq_(s.character_spacing,0)
#eq_(str(s.name), str(mapnik2.Expression('[Field Name]'))) name field is no longer supported eq_(str(s.name), str(mapnik.Expression('[Field Name]')))
eq_(s.face_name, 'DejaVu Sans Bold') eq_(s.face_name, 'DejaVu Sans Bold')
eq_(s.allow_overlap, False) eq_(s.allow_overlap, False)
eq_(s.fill, mapnik.Color('#000000')) eq_(s.fill, mapnik.Color('#000000'))
@ -155,7 +155,7 @@ def test_shield_symbolizer_init():
# 11c34b1: default transform list is empty, not identity matrix # 11c34b1: default transform list is empty, not identity matrix
eq_(s.transform, '') eq_(s.transform, '')
eq_(len(s.fontset.names), 0) eq_(s.fontset, None)
# ShieldSymbolizer missing image file # ShieldSymbolizer missing image file
# images paths are now PathExpressions are evaluated at runtime # images paths are now PathExpressions are evaluated at runtime
@ -170,11 +170,11 @@ def test_shield_symbolizer_modify():
def check_transform(expr, expect_str=None): def check_transform(expr, expect_str=None):
s.transform = expr s.transform = expr
eq_(s.transform, expr if expect_str is None else expect_str) eq_(s.transform, expr if expect_str is None else expect_str)
check_transform("matrix(1 2 3 4 5 6)", "matrix(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)") check_transform("matrix(1 2 3 4 5 6)", "matrix(1, 2, 3, 4, 5, 6)")
check_transform("matrix(1, 2, 3, 4, 5, 6 +7)", "matrix(1, 2, 3, 4, 5, (6+7))") check_transform("matrix(1, 2, 3, 4, 5, 6 +7)", "matrix(1, 2, 3, 4, 5, (6+7))")
check_transform("rotate([a])") check_transform("rotate([a])")
check_transform("rotate([a] -2)", "rotate(([a]-2))") check_transform("rotate([a] -2)", "rotate(([a]-2))")
check_transform("rotate([a] -2 -3)", "rotate([a], -2.0, -3.0)") check_transform("rotate([a] -2 -3)", "rotate([a], -2, -3)")
check_transform("rotate([a] -2 -3 -4)", "rotate(((([a]-2)-3)-4))") check_transform("rotate([a] -2 -3 -4)", "rotate(((([a]-2)-3)-4))")
check_transform("rotate([a] -2, 3, 4)", "rotate(([a]-2), 3, 4)") check_transform("rotate([a] -2, 3, 4)", "rotate(([a]-2), 3, 4)")
check_transform("translate([tx]) rotate([a])") check_transform("translate([tx]) rotate([a])")
@ -209,6 +209,7 @@ def test_markers_symbolizer():
eq_(p.fill_opacity,None) eq_(p.fill_opacity,None)
eq_(p.filename,'shape://ellipse') eq_(p.filename,'shape://ellipse')
eq_(p.placement,mapnik.marker_placement.POINT_PLACEMENT) eq_(p.placement,mapnik.marker_placement.POINT_PLACEMENT)
eq_(p.multi_policy,mapnik.marker_multi_policy.EACH)
eq_(p.fill,None) eq_(p.fill,None)
eq_(p.ignore_placement,False) eq_(p.ignore_placement,False)
eq_(p.spacing,100) eq_(p.spacing,100)
@ -218,7 +219,7 @@ def test_markers_symbolizer():
eq_(p.transform,'') eq_(p.transform,'')
eq_(p.clip,True) eq_(p.clip,True)
eq_(p.comp_op,mapnik.CompositeOp.src_over) eq_(p.comp_op,mapnik.CompositeOp.src_over)
p.width = mapnik.Expression('12') p.width = mapnik.Expression('12')
p.height = mapnik.Expression('12') p.height = mapnik.Expression('12')
@ -239,10 +240,14 @@ def test_markers_symbolizer():
p.allow_overlap = True p.allow_overlap = True
p.opacity = 0.5 p.opacity = 0.5
p.fill_opacity = 0.5 p.fill_opacity = 0.5
p.placement = mapnik.marker_placement.LINE_PLACEMENT
p.multi_policy = mapnik.marker_multi_policy.WHOLE
eq_(p.allow_overlap, True) eq_(p.allow_overlap, True)
eq_(p.opacity, 0.5) eq_(p.opacity, 0.5)
eq_(p.fill_opacity, 0.5) eq_(p.fill_opacity, 0.5)
eq_(p.multi_policy,mapnik.marker_multi_policy.WHOLE)
eq_(p.placement,mapnik.marker_placement.LINE_PLACEMENT)
#https://github.com/mapnik/mapnik/issues/1285 #https://github.com/mapnik/mapnik/issues/1285
#https://github.com/mapnik/mapnik/issues/1427 #https://github.com/mapnik/mapnik/issues/1427

View file

@ -17,6 +17,10 @@ def test_query_init():
r = query.resolution r = query.resolution
assert_almost_equal(r[0], 1.0, places=7) assert_almost_equal(r[0], 1.0, places=7)
assert_almost_equal(r[1], 1.0, places=7) assert_almost_equal(r[1], 1.0, places=7)
# https://github.com/mapnik/mapnik/issues/1762
eq_(query.property_names,[])
query.add_property_name('migurski')
eq_(query.property_names,['migurski'])
# Converting *from* tuples *to* resolutions is not yet supported # Converting *from* tuples *to* resolutions is not yet supported
@raises(TypeError) @raises(TypeError)

View file

@ -17,6 +17,25 @@ def test_loading_fontset_from_map():
eq_(len(fs.names),2) eq_(len(fs.names),2)
eq_(list(fs.names),['DejaVu Sans Book','DejaVu Sans Oblique']) eq_(list(fs.names),['DejaVu Sans Book','DejaVu Sans Oblique'])
def test_loading_fontset_from_python():
m = mapnik.Map(256,256)
fset = mapnik.FontSet('foo')
fset.add_face_name('Comic Sans')
fset.add_face_name('Papyrus')
eq_(fset.name,'foo')
fset.name = 'my-set'
eq_(fset.name,'my-set')
m.append_fontset('my-set', fset)
sty = mapnik.Style()
rule = mapnik.Rule()
tsym = mapnik.TextSymbolizer()
eq_(tsym.fontset,None)
tsym.fontset = fset
rule.symbols.append(tsym)
sty.rules.append(rule)
m.append_style('Style',sty)
serialized_map = mapnik.save_map_to_string(m)
eq_('fontset-name="my-set"' in serialized_map,True)
if __name__ == "__main__": if __name__ == "__main__":
setup() setup()

View file

@ -5,7 +5,7 @@ import sys
try: try:
import nose import nose
except ImportError: except ImportError:
sys.stderr.write("Unable to run python tests: the third party 'nose' module is required\nTo install 'nose' do:\n\tsudo pip install nose (or on debian systems: apt-get install python-nose\n") sys.stderr.write("Unable to run python tests: the third party 'nose' module is required\nTo install 'nose' do:\n\tsudo pip install nose (or on debian systems: apt-get install python-nose)\n")
sys.exit(1) sys.exit(1)
from python_tests.utilities import TodoPlugin from python_tests.utilities import TodoPlugin

View file

@ -0,0 +1,3 @@
i|wkt
1|MULTIPOLYGON(((90 40,50 0,10 40,50 80,90 40)),((190 40,150 0,110 40,150 80,190 40)),((190 140,150 100,110 140,150 180,190 140)))
2|MULTIPOLYGON(((48 130,40 122,32 130,40 138,48 130)),((40 140,20 120,0 140,20 160,40 140)),((48 150,40 142,32 150,40 158,48 150)))
1 i wkt
2 1 MULTIPOLYGON(((90 40,50 0,10 40,50 80,90 40)),((190 40,150 0,110 40,150 80,190 40)),((190 140,150 100,110 140,150 180,190 140)))
3 2 MULTIPOLYGON(((48 130,40 122,32 130,40 138,48 130)),((40 140,20 120,0 140,20 160,40 140)),((48 150,40 142,32 150,40 158,48 150)))

View file

@ -0,0 +1,2 @@
i|wkt
1|LINESTRING(-10 0, 0 20, 10 0, 15 5)
1 i wkt
2 1 LINESTRING(-10 0, 0 20, 10 0, 15 5)

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View file

@ -0,0 +1,36 @@
<Map maximum-extent="0,0,190,180">
<Style name="each">
<Rule>
<Filter>[i] = 1</Filter>
<MarkersSymbolizer placement="point" fill="red" width="30" allow-overlap="true"/>
</Rule>
</Style>
<Style name="whole">
<Rule>
<Filter>[i] = 1</Filter>
<MarkersSymbolizer placement="point" multi-policy="whole" fill="yellow" width="20" allow-overlap="true"/>
</Rule>
</Style>
<Style name="largest">
<Rule>
<Filter>[i] = 2</Filter>
<MarkersSymbolizer placement="point" multi-policy="largest" fill="blue" width="10" allow-overlap="true"/>
</Rule>
</Style>
<Style name="boundary">
<Rule>
<LineSymbolizer />
</Rule>
</Style>
<Layer name="multi">
<StyleName>boundary</StyleName>
<StyleName>each</StyleName>
<StyleName>whole</StyleName>
<StyleName>largest</StyleName>
<Datasource>
<Parameter name="type">csv</Parameter>
<Parameter name="file">../data/marker-multi-policy.csv</Parameter>
<Parameter name="separator">|</Parameter>
</Datasource>
</Layer>
</Map>

View file

@ -0,0 +1,21 @@
<Map>
<Style name="line" filter-mode="first" >
<Rule>
<LineSymbolizer stroke="#000000" />
</Rule>
</Style>
<Style name="point-placement" filter-mode="first" >
<Rule>
<MarkersSymbolizer placement="point" marker-type="ellipse" fill="blue" />
</Rule>
</Style>
<Layer name="carto_tests">
<StyleName>line</StyleName>
<StyleName>point-placement</StyleName>
<Datasource>
<Parameter name="type">csv</Parameter>
<Parameter name="file">../data/marker-on-line.csv</Parameter>
<Parameter name="separator">|</Parameter>
</Datasource>
</Layer>
</Map>

View file

@ -0,0 +1,36 @@
<!DOCTYPE Map>
<Map background-color="white" srs="+init=epsg:4326" minimum-version="0.7.2">
<Style name="1">
<Rule>
<Filter>[id]=1</Filter>
<MarkersSymbolizer fill="darkgreen" opacity=".7" width="15" height="10" stroke="green" stroke-width="7" stroke-opacity=".2" placement="line" marker-type="ellipse"/>
</Rule>
<Rule>
<Filter>[id]=2</Filter>
<MarkersSymbolizer fill="darkorange" opacity=".7" width="20" height="10" stroke="orange" stroke-width="7" stroke-opacity=".2" placement="line" marker-type="ellipse"/>
</Rule>
<Rule>
<Filter>[id]=3</Filter>
<MarkersSymbolizer fill="darkred" opacity=".7" width="20" height="10" stroke="orange" stroke-width="7" stroke-opacity=".2" placement="line" marker-type="ellipse"/>
</Rule>
</Style>
<Layer name="point" srs="+init=epsg:4326">
<StyleName>1</StyleName>
<Datasource>
<Parameter name="type">csv</Parameter>
<Parameter name="inline">
x,y,id
0,0,1
5,0,1
5,5,1
0,5,1
2.5,2.5,2
2.5,3,3
2.5,2,3
3,2.5,3
2,2.5,3
</Parameter>
</Datasource>
</Layer>
</Map>

View file

@ -0,0 +1,21 @@
<Map maximum-extent="736908, 4390316, 2060771, 5942346">
<Style name="whole">
<Rule>
<MarkersSymbolizer placement="point" multi-policy="whole" fill="yellow" width="20" allow-overlap="true"/>
</Rule>
</Style>
<Style name="boundary">
<Rule>
<LineSymbolizer />
</Rule>
</Style>
<Layer name="multi">
<StyleName>boundary</StyleName>
<StyleName>whole</StyleName>
<Datasource>
<Parameter name="type">csv</Parameter>
<Parameter name="file">../data/whole-centroid.csv</Parameter>
<Parameter name="separator">|</Parameter>
</Datasource>
</Layer>
</Map>

View file

@ -27,6 +27,14 @@ files = [
{'name': "lines-2", 'sizes': sizes_few_square}, {'name': "lines-2", 'sizes': sizes_few_square},
{'name': "lines-3", 'sizes': sizes_few_square}, {'name': "lines-3", 'sizes': sizes_few_square},
{'name': "lines-shield", 'sizes': sizes_few_square}, {'name': "lines-shield", 'sizes': sizes_few_square},
{'name': "marker-multi-policy", 'sizes':[(600,400)],
'bbox': mapnik.Box2d(0, 0, 190, 180)},
{'name': "marker-on-line", 'sizes':[(600,400)],
'bbox': mapnik.Box2d(-10, 0, 15, 20)},
{'name': "marker_line_placement_on_points", 'sizes':[(500,100)],
'bbox': mapnik.Box2d(0, 0, 5, 5)},
{'name': "whole-centroid", 'sizes':[(600,400)],
'bbox': mapnik.Box2d(736908, 4390316, 2060771, 5942346)},
{'name': "simple-E"}, {'name': "simple-E"},
{'name': "simple-NE"}, {'name': "simple-NE"},
{'name': "simple-NW"}, {'name': "simple-NW"},