diff --git a/CHANGELOG.md b/CHANGELOG.md index e650903f1..ba5601776 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ For a complete change history, see the git log. ## Future +- Allow style level compositing operations to work outside of featureset extents across tiled requests (#1477) + - Support for encoding `literal` postgres types as strings 69fb17cd3/#1466 - Fixed zoom_all behavior when Map maximum-extent is provided. Previously maximum-extent was used outright but diff --git a/bindings/python/mapnik/__init__.py b/bindings/python/mapnik/__init__.py index c705c7a20..bba3cb6c5 100644 --- a/bindings/python/mapnik/__init__.py +++ b/bindings/python/mapnik/__init__.py @@ -703,8 +703,8 @@ class _TextSymbolizer(TextSymbolizer,_injector): if isinstance(self.properties.format_tree, FormattingText): return self.properties.format_tree.text else: - return None # This text symbolizer is using complex formatting features. - # There is no single expression which could be returned as name + # 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): @@ -712,415 +712,287 @@ class _TextSymbolizer(TextSymbolizer,_injector): @property def text_size(self): - warnings.warn("'text_size' is deprecated, use format.text_size", - DeprecationWarning, 2) return self.format.text_size @text_size.setter def text_size(self, text_size): - warnings.warn("'text_size' is deprecated, use format.text_size", - DeprecationWarning, 2) self.format.text_size = text_size @property def face_name(self): - warnings.warn("'face_name' is deprecated, use format.face_name", - DeprecationWarning, 2) return self.format.face_name @face_name.setter def face_name(self, face_name): - warnings.warn("'face_name' is deprecated, use format.face_name", - DeprecationWarning, 2) self.format.face_name = face_name - @property def fontset(self): - warnings.warn("'fontset' is deprecated, use format.fontset", - DeprecationWarning, 2) return self.format.fontset @fontset.setter def fontset(self, fontset): - warnings.warn("'fontset' is deprecated, use format.fontset", - DeprecationWarning, 2) self.format.fontset = fontset - @property def character_spacing(self): - warnings.warn("'character_spacing' is deprecated, use format.character_spacing", - DeprecationWarning, 2) return self.format.character_spacing @character_spacing.setter def character_spacing(self, character_spacing): - warnings.warn("'character_spacing' is deprecated, use format.character_spacing", - DeprecationWarning, 2) self.format.character_spacing = character_spacing - @property def line_spacing(self): - warnings.warn("'line_spacing' is deprecated, use format.line_spacing", - DeprecationWarning, 2) return self.format.line_spacing @line_spacing.setter def line_spacing(self, line_spacing): - warnings.warn("'line_spacing' is deprecated, use format.line_spacing", - DeprecationWarning, 2) self.format.line_spacing = line_spacing - @property def text_opacity(self): - warnings.warn("'text_opacity' is deprecated, use format.text_opacity", - DeprecationWarning, 2) return self.format.text_opacity @text_opacity.setter def text_opacity(self, text_opacity): - warnings.warn("'text_opacity' is deprecated, use format.text_opacity", - DeprecationWarning, 2) self.format.text_opacity = text_opacity - @property def wrap_char(self): - warnings.warn("'wrap_char' is deprecated, use format.wrap_char", - DeprecationWarning, 2) return self.format.wrap_char @wrap_char.setter def wrap_char(self, wrap_char): - warnings.warn("'wrap_char' is deprecated, use format.wrap_char", - DeprecationWarning, 2) self.format.wrap_char = wrap_char @property def wrap_character(self): - warnings.warn("'wrap_character' is deprecated, use format.wrap_character", - DeprecationWarning, 2) return self.format.wrap_character @wrap_char.setter def wrap_character(self, wrap_character): - warnings.warn("'wrap_char' is deprecated, use format.wrap_character", - DeprecationWarning, 2) self.format.wrap_character = wrap_character @property def wrap_before(self): - warnings.warn("'wrap_before' is deprecated, use format.wrap_before", - DeprecationWarning, 2) - return self.format.wrap_before + return self.properties.wrap_before @wrap_before.setter def wrap_before(self, wrap_before): - warnings.warn("'wrap_before' is deprecated, use format.wrap_before", - DeprecationWarning, 2) - self.format.wrap_before = wrap_before - + self.properties.wrap_before = wrap_before @property def text_transform(self): - warnings.warn("'text_transform' is deprecated, use format.text_transform", - DeprecationWarning, 2) return self.format.text_transform @text_transform.setter def text_transform(self, text_transform): - warnings.warn("'text_transform' is deprecated, use format.text_transform", - DeprecationWarning, 2) self.format.text_transform = text_transform - @property def fill(self): - warnings.warn("'fill' is deprecated, use format.fill", - DeprecationWarning, 2) return self.format.fill @fill.setter def fill(self, fill): - warnings.warn("'fill' is deprecated, use format.fill", - DeprecationWarning, 2) self.format.fill = fill - @property def halo_fill(self): - warnings.warn("'halo_fill' is deprecated, use format.halo_fill", - DeprecationWarning, 2) return self.format.halo_fill @halo_fill.setter def halo_fill(self, halo_fill): - warnings.warn("'halo_fill' is deprecated, use format.halo_fill", - DeprecationWarning, 2) self.format.halo_fill = halo_fill @property def halo_radius(self): - warnings.warn("'halo_radius' is deprecated, use format.halo_radius", - DeprecationWarning, 2) return self.format.halo_radius @halo_radius.setter def halo_radius(self, halo_radius): - warnings.warn("'halo_radius' is deprecated, use format.halo_radius", - DeprecationWarning, 2) self.format.halo_radius = halo_radius @property def label_placement(self): - warnings.warn("'label_placement' is deprecated, use properties.label_placement", - DeprecationWarning, 2) return self.properties.label_placement @label_placement.setter def label_placement(self, label_placement): - warnings.warn("'label_placement' is deprecated, use properties.label_placement", - DeprecationWarning, 2) self.properties.label_placement = label_placement @property def horizontal_alignment(self): - warnings.warn("'horizontal_alignment' is deprecated, use properties.horizontal_alignment", - DeprecationWarning, 2) return self.properties.horizontal_alignment @horizontal_alignment.setter def horizontal_alignment(self, horizontal_alignment): - warnings.warn("'horizontal_alignment' is deprecated, use properties.horizontal_alignment", - DeprecationWarning, 2) self.properties.horizontal_alignment = horizontal_alignment @property def justify_alignment(self): - warnings.warn("'justify_alignment' is deprecated, use properties.justify_alignment", - DeprecationWarning, 2) return self.properties.justify_alignment @justify_alignment.setter def justify_alignment(self, justify_alignment): - warnings.warn("'justify_alignment' is deprecated, use properties.justify_alignment", - DeprecationWarning, 2) self.properties.justify_alignment = justify_alignment @property def vertical_alignment(self): - warnings.warn("'vertical_alignment' is deprecated, use properties.vertical_alignment", - DeprecationWarning, 2) return self.properties.vertical_alignment @vertical_alignment.setter def vertical_alignment(self, vertical_alignment): - warnings.warn("'vertical_alignment' is deprecated, use properties.vertical_alignment", - DeprecationWarning, 2) self.properties.vertical_alignment = vertical_alignment @property def orientation(self): - warnings.warn("'orientation' is deprecated, use properties.orientation", - DeprecationWarning, 2) return self.properties.orientation @orientation.setter def orientation(self, orientation): - warnings.warn("'orientation' is deprecated, use properties.orientation", - DeprecationWarning, 2) self.properties.orientation = orientation @property def displacement(self): - warnings.warn("'displacement' is deprecated, use properties.displacement", - DeprecationWarning, 2) return self.properties.displacement @displacement.setter def displacement(self, displacement): - warnings.warn("'displacement' is deprecated, use properties.displacement", - DeprecationWarning, 2) self.properties.displacement = displacement @property def label_spacing(self): - warnings.warn("'label_spacing' is deprecated, use properties.label_spacing", - DeprecationWarning, 2) return self.properties.label_spacing @label_spacing.setter def label_spacing(self, label_spacing): - warnings.warn("'label_spacing' is deprecated, use properties.label_spacing", - DeprecationWarning, 2) self.properties.label_spacing = label_spacing @property def label_position_tolerance(self): - warnings.warn("'label_position_tolerance' is deprecated, use properties.label_position_tolerance", - DeprecationWarning, 2) return self.properties.label_position_tolerance @label_position_tolerance.setter def label_position_tolerance(self, label_position_tolerance): - warnings.warn("'label_position_tolerance' is deprecated, use properties.label_position_tolerance", - DeprecationWarning, 2) self.properties.label_position_tolerance = label_position_tolerance @property def avoid_edges(self): - warnings.warn("'avoid_edges' is deprecated, use properties.avoid_edges", - DeprecationWarning, 2) return self.properties.avoid_edges @avoid_edges.setter def avoid_edges(self, avoid_edges): - warnings.warn("'avoid_edges' is deprecated, use properties.avoid_edges", - DeprecationWarning, 2) self.properties.avoid_edges = avoid_edges @property def minimum_distance(self): - warnings.warn("'minimum_distance' is deprecated, use properties.minimum_distance", - DeprecationWarning, 2) return self.properties.minimum_distance @minimum_distance.setter def minimum_distance(self, minimum_distance): - warnings.warn("'minimum_distance' is deprecated, use properties.minimum_distance", - DeprecationWarning, 2) self.properties.minimum_distance = minimum_distance @property def minimum_padding(self): - warnings.warn("'minimum_padding' is deprecated, use properties.minimum_padding", - DeprecationWarning, 2) return self.properties.minimum_padding @minimum_padding.setter def minimum_padding(self, minimum_padding): - warnings.warn("'minimum_padding' is deprecated, use properties.minimum_padding", - DeprecationWarning, 2) self.properties.minimum_padding = minimum_padding @property def minimum_path_length(self): - warnings.warn("'minimum_path_length' is deprecated, use properties.minimum_path_length", - DeprecationWarning, 2) return self.properties.minimum_path_length @minimum_path_length.setter def minimum_path_length(self, minimum_path_length): - warnings.warn("'minimum_path_length' is deprecated, use properties.minimum_path_length", - DeprecationWarning, 2) self.properties.minimum_path_length = minimum_path_length @property def maximum_angle_char_delta(self): - warnings.warn("'maximum_angle_char_delta' is deprecated, use properties.maximum_angle_char_delta", - DeprecationWarning, 2) return self.properties.maximum_angle_char_delta @maximum_angle_char_delta.setter def maximum_angle_char_delta(self, maximum_angle_char_delta): - warnings.warn("'maximum_angle_char_delta' is deprecated, use properties.maximum_angle_char_delta", - DeprecationWarning, 2) self.properties.maximum_angle_char_delta = maximum_angle_char_delta @property def force_odd_labels(self): - warnings.warn("'force_odd_labels' is deprecated, use properties.force_odd_labels", - DeprecationWarning, 2) return self.properties.force_odd_labels @force_odd_labels.setter def force_odd_labels(self, force_odd_labels): - warnings.warn("'force_odd_labels' is deprecated, use properties.force_odd_labels", - DeprecationWarning, 2) self.properties.force_odd_labels = force_odd_labels @property def allow_overlap(self): - warnings.warn("'allow_overlap' is deprecated, use properties.allow_overlap", - DeprecationWarning, 2) return self.properties.allow_overlap @allow_overlap.setter def allow_overlap(self, allow_overlap): - warnings.warn("'allow_overlap' is deprecated, use properties.allow_overlap", - DeprecationWarning, 2) self.properties.allow_overlap = allow_overlap @property def text_ratio(self): - warnings.warn("'text_ratio' is deprecated, use properties.text_ratio", - DeprecationWarning, 2) return self.properties.text_ratio @text_ratio.setter def text_ratio(self, text_ratio): - warnings.warn("'text_ratio' is deprecated, use properties.text_ratio", - DeprecationWarning, 2) self.properties.text_ratio = text_ratio @property def wrap_width(self): - warnings.warn("'wrap_width' is deprecated, use properties.wrap_width", - DeprecationWarning, 2) return self.properties.wrap_width @wrap_width.setter def wrap_width(self, wrap_width): - warnings.warn("'wrap_width' is deprecated, use properties.wrap_width", - DeprecationWarning, 2) self.properties.wrap_width = wrap_width diff --git a/bindings/python/mapnik_fontset.cpp b/bindings/python/mapnik_fontset.cpp index f467d816f..fbc94a292 100644 --- a/bindings/python/mapnik_fontset.cpp +++ b/bindings/python/mapnik_fontset.cpp @@ -33,8 +33,13 @@ using mapnik::font_set; void export_fontset () { using namespace boost::python; - class_("FontSet", init<>("default fontset constructor") + class_("FontSet", init("default fontset constructor") ) + .add_property("name", + make_function(&font_set::get_name,return_value_policy()), + &font_set::set_name, + "Get/Set the name of the FontSet.\n" + ) .def("add_face_name",&font_set::add_face_name, (arg("name")), "Add a face-name to the fontset.\n" diff --git a/bindings/python/mapnik_python.cpp b/bindings/python/mapnik_python.cpp index ebe2579b8..dffa358cd 100644 --- a/bindings/python/mapnik_python.cpp +++ b/bindings/python/mapnik_python.cpp @@ -617,6 +617,7 @@ BOOST_PYTHON_MODULE(_mapnik) def("has_pycairo", &has_pycairo, "Get pycairo module status"); python_optional(); + python_optional(); python_optional(); python_optional >(); python_optional(); diff --git a/bindings/python/mapnik_text_placement.cpp b/bindings/python/mapnik_text_placement.cpp index 99952cee8..67b9a241e 100644 --- a/bindings/python/mapnik_text_placement.cpp +++ b/bindings/python/mapnik_text_placement.cpp @@ -402,9 +402,9 @@ void export_text_placement() class_with_converter ("CharProperties") .def_readwrite_convert("text_transform", &char_properties::text_transform) + .def_readwrite_convert("fontset", &char_properties::fontset) .def(init()) //Copy constructor .def_readwrite("face_name", &char_properties::face_name) - .def_readwrite("fontset", &char_properties::fontset) .def_readwrite("text_size", &char_properties::text_size) .def_readwrite("character_spacing", &char_properties::character_spacing) .def_readwrite("line_spacing", &char_properties::line_spacing) diff --git a/include/mapnik/agg_renderer.hpp b/include/mapnik/agg_renderer.hpp index 15ca8c2d5..961b5d16b 100644 --- a/include/mapnik/agg_renderer.hpp +++ b/include/mapnik/agg_renderer.hpp @@ -106,6 +106,9 @@ public: void process(markers_symbolizer const& sym, mapnik::feature_impl & feature, proj_transform const& prj_trans); + void process(debug_symbolizer const& sym, + feature_impl & feature, + proj_transform const& prj_trans); inline bool process(rule::symbolizers const& /*syms*/, mapnik::feature_impl & /*feature*/, diff --git a/include/mapnik/debug_symbolizer.hpp b/include/mapnik/debug_symbolizer.hpp new file mode 100644 index 000000000..d25c5c701 --- /dev/null +++ b/include/mapnik/debug_symbolizer.hpp @@ -0,0 +1,39 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2011 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#ifndef MAPNIK_DEBUG_SYMBOLIZER_HPP +#define MAPNIK_DEBUG_SYMBOLIZER_HPP + +#include +#include + +namespace mapnik +{ + +struct MAPNIK_DECL debug_symbolizer : + public symbolizer_base +{ + debug_symbolizer() : symbolizer_base() {} +}; +} + +#endif // DEBUG_SYMBOLIZER_HPP diff --git a/include/mapnik/feature_style_processor_impl.hpp b/include/mapnik/feature_style_processor_impl.hpp index cb4479e47..1514087ca 100644 --- a/include/mapnik/feature_style_processor_impl.hpp +++ b/include/mapnik/feature_style_processor_impl.hpp @@ -256,6 +256,7 @@ void feature_style_processor::apply_to_layer(layer const& lay, Proces box2d layer_ext = lay.envelope(); bool fw_success = false; + bool early_return = false; // first, try intersection of map extent forward projected into layer srs if (prj_trans.forward(buffered_query_ext, PROJ_ENVELOPE_POINTS) && buffered_query_ext.intersects(layer_ext)) @@ -266,10 +267,7 @@ void feature_style_processor::apply_to_layer(layer const& lay, Proces // if no intersection and projections are also equal, early return else if (prj_trans.equal()) { -#if defined(RENDERING_STATS) - layer_timer.discard(); -#endif - return; + early_return = true; } // next try intersection of layer extent back projected into map srs else if (prj_trans.backward(layer_ext, PROJ_ENVELOPE_POINTS) && buffered_query_ext.intersects(layer_ext)) @@ -278,7 +276,7 @@ void feature_style_processor::apply_to_layer(layer const& lay, Proces // forward project layer extent back into native projection if (! prj_trans.forward(layer_ext, PROJ_ENVELOPE_POINTS)) { - MAPNIK_LOG_DEBUG(feature_style_processor) + MAPNIK_LOG_ERROR(feature_style_processor) << "feature_style_processor: Layer=" << lay.name() << " extent=" << layer_ext << " in map projection " << " did not reproject properly back to layer projection"; @@ -287,6 +285,31 @@ void feature_style_processor::apply_to_layer(layer const& lay, Proces else { // if no intersection then nothing to do for layer + early_return = true; + } + + if (early_return) + { + // check for styles needing compositing operations applied + // https://github.com/mapnik/mapnik/issues/1477 + BOOST_FOREACH(std::string const& style_name, style_names) + { + boost::optional style=m_.find_style(style_name); + if (!style) + { + continue; + } + if (style->comp_op() || style->image_filters().size() > 0) + { + if (style->active(scale_denom)) + { + std::clog << "triggering\n"; + // trigger any needed compositing ops + p.start_style_processing(*style); + p.end_style_processing(*style); + } + } + } #if defined(RENDERING_STATS) layer_timer.discard(); #endif @@ -344,9 +367,8 @@ void feature_style_processor::apply_to_layer(layer const& lay, Proces continue; } - const std::vector& rules=(*style).get_rules(); + std::vector const& rules=(*style).get_rules(); bool active_rules=false; - BOOST_FOREACH(rule const& r, rules) { if (r.active(scale_denom)) @@ -444,22 +466,21 @@ void feature_style_processor::apply_to_layer(layer const& lay, Proces } else if (cache_features) { + memory_datasource cache; featureset_ptr features = ds->features(q); if (features) { // Cache all features into the memory_datasource before rendering. - memory_datasource cache; feature_ptr feature; while ((feature = features->next())) { cache.push(feature); } - - int i = 0; - BOOST_FOREACH (feature_type_style * style, active_styles) - { - render_style(lay, p, style, style_names[i++], - cache.features(q), prj_trans, scale_denom); - } + } + int i = 0; + BOOST_FOREACH (feature_type_style * style, active_styles) + { + render_style(lay, p, style, style_names[i++], + cache.features(q), prj_trans, scale_denom); } } // We only have a single style and no grouping. @@ -468,11 +489,8 @@ void feature_style_processor::apply_to_layer(layer const& lay, Proces int i = 0; BOOST_FOREACH (feature_type_style * style, active_styles) { - featureset_ptr features = ds->features(q); - if (features) { - render_style(lay, p, style, style_names[i++], - features, prj_trans, scale_denom); - } + render_style(lay, p, style, style_names[i++], + ds->features(q), prj_trans, scale_denom); } } } @@ -495,8 +513,12 @@ void feature_style_processor::render_style( proj_transform const& prj_trans, double scale_denom) { - p.start_style_processing(*style); + if (!features) + { + p.end_style_processing(*style); + return; + } #if defined(RENDERING_STATS) std::ostringstream s1; diff --git a/include/mapnik/feature_type_style.hpp b/include/mapnik/feature_type_style.hpp index 45707fbdc..a623849f4 100644 --- a/include/mapnik/feature_type_style.hpp +++ b/include/mapnik/feature_type_style.hpp @@ -72,17 +72,17 @@ public: feature_type_style& operator=(feature_type_style const& rhs); void add_rule(rule const& rule); - rules const& get_rules() const; rule_ptrs const& get_if_rules(double scale_denom); rule_ptrs const& get_else_rules(double scale_denom); rule_ptrs const& get_also_rules(double scale_denom); - rules& get_rules_nonconst(); - void set_filter_mode(filter_mode_e mode); + bool active(double scale_denom) const; + void set_filter_mode(filter_mode_e mode); filter_mode_e get_filter_mode() const; + // filters std::vector const& image_filters() const; std::vector & image_filters(); diff --git a/include/mapnik/font_engine_freetype.hpp b/include/mapnik/font_engine_freetype.hpp index 50852a7e7..ce431428f 100644 --- a/include/mapnik/font_engine_freetype.hpp +++ b/include/mapnik/font_engine_freetype.hpp @@ -333,11 +333,11 @@ public: 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 fset) { - if (fset.size() > 0) + if (fset && fset->size() > 0) { - return get_face_set(fset); + return get_face_set(*fset); } else { diff --git a/include/mapnik/font_set.hpp b/include/mapnik/font_set.hpp index 4d0fd79b4..426d60b06 100644 --- a/include/mapnik/font_set.hpp +++ b/include/mapnik/font_set.hpp @@ -35,7 +35,6 @@ namespace mapnik class MAPNIK_DECL font_set { public: - font_set(); font_set(std::string const& name); font_set(font_set const& rhs); font_set& operator=(font_set const& rhs); diff --git a/include/mapnik/rule.hpp b/include/mapnik/rule.hpp index 0e28f060e..492ed7488 100644 --- a/include/mapnik/rule.hpp +++ b/include/mapnik/rule.hpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -108,6 +109,12 @@ inline bool operator==(markers_symbolizer const& lhs, return (&lhs == &rhs); } +inline bool operator==(debug_symbolizer const& lhs, + debug_symbolizer const& rhs) +{ + return (&lhs == &rhs); +} + typedef boost::variant symbolizer; + markers_symbolizer, + debug_symbolizer> symbolizer; class rule { diff --git a/include/mapnik/text_properties.hpp b/include/mapnik/text_properties.hpp index 9e8c5b055..c816620df 100644 --- a/include/mapnik/text_properties.hpp +++ b/include/mapnik/text_properties.hpp @@ -33,6 +33,7 @@ #include // boost +#include #include namespace mapnik @@ -58,7 +59,7 @@ struct char_properties /** Write object to XML ptree. */ void to_xml(boost::property_tree::ptree &node, bool explicit_defaults, char_properties const& dfl=char_properties()) const; std::string face_name; - font_set fontset; + boost::optional fontset; double text_size; double character_spacing; double line_spacing; //Largest total height (fontsize+line_spacing) per line is chosen diff --git a/include/mapnik/text_symbolizer.hpp b/include/mapnik/text_symbolizer.hpp index 5b9de2aeb..3e8fb06f1 100644 --- a/include/mapnik/text_symbolizer.hpp +++ b/include/mapnik/text_symbolizer.hpp @@ -92,7 +92,7 @@ struct MAPNIK_DECL text_symbolizer : public symbolizer_base void set_text_size(double size); std::string const& get_face_name() const func_deprecated; void set_face_name(std::string face_name); - font_set const& get_fontset() const func_deprecated; + boost::optional const& get_fontset() const func_deprecated; void set_fontset(font_set const& fset); color const& get_fill() const func_deprecated; void set_fill(color const& fill); diff --git a/plugins/input/sqlite/sqlite_datasource.cpp b/plugins/input/sqlite/sqlite_datasource.cpp index 89653bdf9..1b769af53 100644 --- a/plugins/input/sqlite/sqlite_datasource.cpp +++ b/plugins/input/sqlite/sqlite_datasource.cpp @@ -339,10 +339,12 @@ void sqlite_datasource::bind() const else { std::ostringstream s; - s << "Sqlite Plugin: key_field is empty for " - << geometry_field_ - << " and " - << geometry_table_; + s << "Sqlite Plugin: could not generate spatial index" + << " for table '" << geometry_table_ << "'" + << " as no primary key can be detected." + << " You should either declare an INTEGER PRIMARY KEY" + << " or set the 'key_field' option to force a" + << " given field to be used as the primary key"; throw datasource_exception(s.str()); } } diff --git a/src/agg/process_debug_symbolizer.cpp b/src/agg/process_debug_symbolizer.cpp new file mode 100644 index 000000000..5476da0c7 --- /dev/null +++ b/src/agg/process_debug_symbolizer.cpp @@ -0,0 +1,64 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2011 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +// mapnik +#include +#include + +namespace mapnik { + +void draw_rect(image_32 &pixmap, box2d const& box) +{ + double x0 = box.minx(); + double x1 = box.maxx(); + double y0 = box.miny(); + double y1 = box.maxy(); + unsigned color1 = 0xff0000ff; + for (double x=x0; x +void agg_renderer::process(debug_symbolizer const& sym, + mapnik::feature_impl & feature, + proj_transform const& prj_trans) +{ + label_collision_detector4::query_iterator itr = detector_->begin(), end = detector_->end(); + for (;itr!=end; itr++) + { + draw_rect(pixmap_, itr->box); + } +} + +template void agg_renderer::process(debug_symbolizer const&, + mapnik::feature_impl &, + proj_transform const&); +} + diff --git a/src/build.py b/src/build.py index d83db33fd..ac7ecc1c0 100644 --- a/src/build.py +++ b/src/build.py @@ -259,6 +259,7 @@ source += Split( agg/process_raster_symbolizer.cpp agg/process_shield_symbolizer.cpp agg/process_markers_symbolizer.cpp + agg/process_debug_symbolizer.cpp """ ) diff --git a/src/feature_type_style.cpp b/src/feature_type_style.cpp index 2dc820895..ff588af7f 100644 --- a/src/feature_type_style.cpp +++ b/src/feature_type_style.cpp @@ -22,6 +22,9 @@ #include +// boost +#include + namespace mapnik { @@ -89,6 +92,18 @@ rules& feature_type_style::get_rules_nonconst() return rules_; } +bool feature_type_style::active(double scale_denom) const +{ + BOOST_FOREACH(rule const& r, rules_) + { + if (r.active(scale_denom)) + { + return true; + } + } + return false; +} + void feature_type_style::set_filter_mode(filter_mode_e mode) { filter_mode_ = mode; diff --git a/src/font_set.cpp b/src/font_set.cpp index 9fce7b1ef..06daf5b42 100644 --- a/src/font_set.cpp +++ b/src/font_set.cpp @@ -29,8 +29,6 @@ namespace mapnik { -font_set::font_set() - : name_("") {} font_set::font_set(std::string const& name) : name_(name) {} diff --git a/src/image_scaling.cpp b/src/image_scaling.cpp index ab1c00b8b..298961f61 100644 --- a/src/image_scaling.cpp +++ b/src/image_scaling.cpp @@ -264,7 +264,8 @@ void scale_image_agg(Image & target, double ratio) { typedef agg::pixfmt_rgba32 pixfmt; - typedef agg::renderer_base renderer_base; + typedef agg::pixfmt_rgba32_pre pixfmt_pre; + typedef agg::renderer_base renderer_base; // define some stuff we'll use soon agg::rasterizer_scanline_aa<> ras; @@ -280,7 +281,7 @@ void scale_image_agg(Image & target, // initialize destination AGG buffer (with transparency) agg::rendering_buffer rbuf_dst((unsigned char*)target.getBytes(), target.width(), target.height(), target.width() * 4); - pixfmt pixf_dst(rbuf_dst); + pixfmt_pre pixf_dst(rbuf_dst); renderer_base rb_dst(pixf_dst); rb_dst.clear(agg::rgba(0, 0, 0, 0)); diff --git a/src/load_map.cpp b/src/load_map.cpp index a5ebbfb98..dfdbf47c9 100644 --- a/src/load_map.cpp +++ b/src/load_map.cpp @@ -106,6 +106,7 @@ private: void parse_building_symbolizer(rule & rule, xml_node const& sym); void parse_raster_symbolizer(rule & rule, xml_node const& sym); void parse_markers_symbolizer(rule & rule, xml_node const& sym); + void parse_debug_symbolizer(rule & rule, xml_node const& sym); void parse_raster_colorizer(raster_colorizer_ptr const& rc, xml_node const& node); bool parse_stroke(stroke & strk, xml_node const & sym); @@ -806,6 +807,10 @@ void map_parser::parse_rule(feature_type_style & style, xml_node const& r) { parse_markers_symbolizer(rule, *symIter); } + else if (symIter->is("DebugSymbolizer")) + { + parse_debug_symbolizer(rule, *symIter); + } } style.add_rule(rule); @@ -1149,7 +1154,7 @@ void map_parser::parse_text_symbolizer(rule & rule, xml_node const& sym) placement_finder->defaults.from_xml(sym, fontsets_); } if (strict_ && - !placement_finder->defaults.format.fontset.size()) + !placement_finder->defaults.format.fontset->size()) { ensure_font_face(placement_finder->defaults.format.face_name); } @@ -1178,7 +1183,7 @@ void map_parser::parse_shield_symbolizer(rule & rule, xml_node const& sym) } placement_finder->defaults.from_xml(sym, fontsets_); if (strict_ && - !placement_finder->defaults.format.fontset.size()) + !placement_finder->defaults.format.fontset->size()) { ensure_font_face(placement_finder->defaults.format.face_name); } @@ -1508,6 +1513,14 @@ void map_parser::parse_raster_symbolizer(rule & rule, xml_node const & sym) } } +void map_parser::parse_debug_symbolizer(rule & rule, xml_node const & sym) +{ + debug_symbolizer symbol; + + parse_symbolizer_base(symbol, sym); + rule.append(symbol); +} + void map_parser::parse_raster_colorizer(raster_colorizer_ptr const& rc, xml_node const& node) { diff --git a/src/map.cpp b/src/map.cpp index 7e4a3f5f8..5c27fd5f0 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -159,6 +159,10 @@ boost::optional Map::find_style(std::string const& na 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; } diff --git a/src/processed_text.cpp b/src/processed_text.cpp index caaa31679..ab27f375f 100644 --- a/src/processed_text.cpp +++ b/src/processed_text.cpp @@ -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); 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()) { - 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 { - 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 { - 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()) diff --git a/src/text_properties.cpp b/src/text_properties.cpp index 1ec63ac0d..2fabf9deb 100644 --- a/src/text_properties.cpp +++ b/src/text_properties.cpp @@ -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); } } - 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); } - if (face_name.empty() && fontset.get_name().empty()) + if (face_name.empty() && !fontset) { 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 { - std::string const& fontset_name = fontset.get_name(); - std::string const& dfl_fontset_name = dfl.fontset.get_name(); - if (fontset_name != dfl_fontset_name || explicit_defaults) + if (fontset) { - set_attr(node, "fontset-name", fontset_name); + set_attr(node, "fontset-name", fontset->get_name()); } if (face_name != dfl.face_name || explicit_defaults) diff --git a/src/text_symbolizer.cpp b/src/text_symbolizer.cpp index 4f0b1b96e..8aed6297b 100644 --- a/src/text_symbolizer.cpp +++ b/src/text_symbolizer.cpp @@ -173,7 +173,7 @@ void text_symbolizer::set_fontset(font_set const& fontset) placement_options_->defaults.format.fontset = fontset; } -font_set const& text_symbolizer::get_fontset() const +boost::optional const& text_symbolizer::get_fontset() const { return placement_options_->defaults.format.fontset; } diff --git a/src/warp.cpp b/src/warp.cpp index b1c135f26..ee0dc6a05 100644 --- a/src/warp.cpp +++ b/src/warp.cpp @@ -78,6 +78,8 @@ void reproject_and_scale_raster(raster & target, raster const& source, typedef agg::pixfmt_rgba32 pixfmt; typedef pixfmt::color_type color_type; typedef agg::renderer_base renderer_base; + typedef agg::pixfmt_rgba32_pre pixfmt_pre; + typedef agg::renderer_base renderer_base_pre; agg::rasterizer_scanline_aa<> rasterizer; agg::scanline_u8 scanline; @@ -85,8 +87,8 @@ void reproject_and_scale_raster(raster & target, raster const& source, target.data_.width(), target.data_.height(), target.data_.width()*4); - pixfmt pixf(buf); - renderer_base rb(pixf); + pixfmt_pre pixf_pre(buf); + renderer_base_pre rb_pre(pixf_pre); rasterizer.clip_box(0, 0, target.data_.width(), target.data_.height()); agg::rendering_buffer buf_tile( (unsigned char*)source.data_.getData(), @@ -176,13 +178,13 @@ void reproject_and_scale_raster(raster & target, raster const& source, span_gen_type; span_gen_type sg(ia, interpolator); - agg::render_scanlines_aa(rasterizer, scanline, rb, + agg::render_scanlines_aa(rasterizer, scanline, rb_pre, sa, sg); } else { typedef mapnik::span_image_resample_rgba_affine span_gen_type; span_gen_type sg(ia, interpolator, filter); - agg::render_scanlines_aa(rasterizer, scanline, rb, + agg::render_scanlines_aa(rasterizer, scanline, rb_pre, sa, sg); } } diff --git a/tests/python_tests/fontset_test.py b/tests/python_tests/fontset_test.py new file mode 100644 index 000000000..4db8ca73c --- /dev/null +++ b/tests/python_tests/fontset_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python + +from nose.tools import * +from utilities import execution_path + +import os, mapnik + +def setup(): + # All of the paths used are relative, if we run the tests + # from another directory we need to chdir() + os.chdir(execution_path('.')) + +def test_loading_fontset_from_map(): + m = mapnik.Map(256,256) + mapnik.load_map(m,'../data/good_maps/fontset.xml',True) + fs = m.find_fontset('book-fonts') + eq_(len(fs.names),2) + 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__": + setup() + [eval(run)() for run in dir() if 'test_' in run] diff --git a/tests/python_tests/images/support/raster-alpha.png b/tests/python_tests/images/support/raster-alpha.png index a84c88eaf..3c79bb304 100644 Binary files a/tests/python_tests/images/support/raster-alpha.png and b/tests/python_tests/images/support/raster-alpha.png differ diff --git a/tests/python_tests/images/support/raster-nodata-edge.png b/tests/python_tests/images/support/raster-nodata-edge.png index d3430199f..df5fa419e 100644 Binary files a/tests/python_tests/images/support/raster-nodata-edge.png and b/tests/python_tests/images/support/raster-nodata-edge.png differ diff --git a/tests/python_tests/images/support/tif_colortable.png b/tests/python_tests/images/support/tif_colortable.png index 02be3553a..aa055bb45 100644 Binary files a/tests/python_tests/images/support/tif_colortable.png and b/tests/python_tests/images/support/tif_colortable.png differ diff --git a/tests/python_tests/images/support/vrt_colortable.png b/tests/python_tests/images/support/vrt_colortable.png index 3eba18c98..f885c68f8 100644 Binary files a/tests/python_tests/images/support/vrt_colortable.png and b/tests/python_tests/images/support/vrt_colortable.png differ diff --git a/tests/python_tests/object_test.py b/tests/python_tests/object_test.py index 7884322de..372ecde76 100644 --- a/tests/python_tests/object_test.py +++ b/tests/python_tests/object_test.py @@ -155,7 +155,7 @@ def test_shield_symbolizer_init(): # 11c34b1: default transform list is empty, not identity matrix eq_(s.transform, '') - eq_(len(s.fontset.names), 0) + eq_(s.fontset, None) # ShieldSymbolizer missing image file # images paths are now PathExpressions are evaluated at runtime diff --git a/tests/python_tests/test_fontset.py b/tests/python_tests/test_fontset.py deleted file mode 100644 index ec44a103d..000000000 --- a/tests/python_tests/test_fontset.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python - -from nose.tools import * -from utilities import execution_path - -import os, mapnik - -def setup(): - # All of the paths used are relative, if we run the tests - # from another directory we need to chdir() - os.chdir(execution_path('.')) - -def test_loading_fontset_from_map(): - m = mapnik.Map(256,256) - mapnik.load_map(m,'../data/good_maps/fontset.xml',True) - fs = m.find_fontset('book-fonts') - eq_(len(fs.names),2) - eq_(list(fs.names),['DejaVu Sans Book','DejaVu Sans Oblique']) - - -if __name__ == "__main__": - setup() - [eval(run)() for run in dir() if 'test_' in run]