diff --git a/SConstruct b/SConstruct index 723762390..9639b5652 100644 --- a/SConstruct +++ b/SConstruct @@ -27,12 +27,13 @@ if platform.uname()[4] == 'x86_64': else: LIBDIR_SCHEMA='lib' -opts = Options() +opts = Options('config.py') opts.Add('PREFIX', 'The install path "prefix"', '/usr/local') opts.Add(PathOption('BOOST_INCLUDES', 'Search path for boost include files', '/usr/include')) opts.Add(PathOption('BOOST_LIBS', 'Search path for boost library files', '/usr/' + LIBDIR_SCHEMA)) opts.Add('BOOST_TOOLKIT','Specify boost toolkit e.g. gcc41.','',False) opts.Add(('FREETYPE_CONFIG', 'The path to the freetype-config executable.', 'freetype-config')) +opts.Add(('XML2_CONFIG', 'The path to the xml2-config executable.', 'xml2-config')) opts.Add(PathOption('FRIBIDI_INCLUDES', 'Search path for fribidi include files', '/usr/include')) opts.Add(PathOption('FRIBIDI_LIBS','Search path for fribidi include files','/usr/' + LIBDIR_SCHEMA)) opts.Add(PathOption('PNG_INCLUDES', 'Search path for libpng include files', '/usr/include')) @@ -56,7 +57,7 @@ opts.Add(BoolOption('DEBUG', 'Compile a debug version of mapnik', 'False')) opts.Add('DESTDIR', 'The root directory to install into. Useful mainly for binary package building', '/') opts.Add(BoolOption('BIDI', 'BIDI support', 'False')) opts.Add(EnumOption('THREADING','Set threading support','multi', ['multi','single'])) -opts.Add(EnumOption('XMLPARSER','Set xml parser ','tinyxml', ['tinyxml','spirit'])) +opts.Add(EnumOption('XMLPARSER','Set xml parser ','tinyxml', ['tinyxml','spirit','libxml2'])) env = Environment(ENV=os.environ, options=opts) @@ -105,6 +106,9 @@ if env['BIDI']: if env['XMLPARSER'] == 'tinyxml': env.Append(CXXFLAGS = '-DBOOST_PROPERTY_TREE_XML_PARSER_TINYXML -DTIXML_USE_STL') +elif env['XMLPARSER'] == 'libxml2': + env.ParseConfig(env['XML2_CONFIG'] + ' --libs --cflags') + env.Append(CXXFLAGS = '-DHAVE_LIBXML2'); C_LIBSHEADERS = [ ['m', 'math.h', True], @@ -224,10 +228,12 @@ env = conf.Finish() if env['PLATFORM'] == 'Darwin': pthread = '' else: pthread = '-pthread' +common_cxx_flags = '-ansi -Wall %s -ftemplate-depth-100 -D%s ' % (pthread, env['PLATFORM'].upper()); + if env['DEBUG']: - env.Append(CXXFLAGS = '-ansi -Wall %s -ftemplate-depth-100 -O0 -fno-inline -g -DDEBUG -DMAPNIK_DEBUG -D%s ' % (pthread, env['PLATFORM'].upper())) + env.Append(CXXFLAGS = common_cxx_flags + '-O0 -fno-inline -g -DDEBUG -DMAPNIK_DEBUG') else: - env.Append(CXXFLAGS = '-ansi -Wall %s -ftemplate-depth-100 -O2 -finline-functions -Wno-inline -DNDEBUG -D%s' % (pthread,env['PLATFORM'].upper())) + env.Append(CXXFLAGS = common_cxx_flags + '-O2 -finline-functions -Wno-inline -DNDEBUG') # Install some free default fonts diff --git a/bindings/python/mapnik/__init__.py b/bindings/python/mapnik/__init__.py index 2732b8e30..42548d55d 100644 --- a/bindings/python/mapnik/__init__.py +++ b/bindings/python/mapnik/__init__.py @@ -90,7 +90,10 @@ DatasourceCache.instance().register_datasources('%s' % inputpluginspath) from mapnik import FontEngine from glob import glob fonts = glob('%s/*.ttf' % fontscollectionpath) -map(FontEngine.instance().register_font, fonts) +if len( fonts ) == 0: + print "### WARNING: No ttf files found in '%s'." % fontscollectionpath +else: + map(FontEngine.instance().register_font, fonts) #set dlopen flags back to the original setdlopenflags(flags) diff --git a/bindings/python/mapnik_python.cpp b/bindings/python/mapnik_python.cpp index 6eb30983c..528e8ea7a 100644 --- a/bindings/python/mapnik_python.cpp +++ b/bindings/python/mapnik_python.cpp @@ -24,6 +24,7 @@ #include #include #include +#include void export_color(); void export_coord(); @@ -58,6 +59,7 @@ void export_projection(); #include #include #include +#include #include @@ -95,14 +97,21 @@ double scale_denominator(mapnik::Map const &map, bool geographic) return mapnik::scale_denominator(map, geographic); } +void translator(mapnik::config_error const & ex) { + PyErr_SetString(PyExc_UserWarning, ex.what()); +} + +BOOST_PYTHON_FUNCTION_OVERLOADS(load_map_overloads, load_map, 2, 3); BOOST_PYTHON_MODULE(_mapnik) { - using namespace boost::python; + using namespace boost::python; + using mapnik::load_map; using mapnik::save_map; - + + register_exception_translator(translator); export_query(); export_feature(); export_featureset(); @@ -137,8 +146,8 @@ BOOST_PYTHON_MODULE(_mapnik) def("render",&render2); def("scale_denominator", &scale_denominator); - def("load_map",&load_map,"load Map object from XML"); - def("save_map",&load_map,"sace Map object to XML"); + def("load_map", & load_map, load_map_overloads()); + def("save_map", & save_map, "save Map object to XML"); using mapnik::symbolizer; class_("Symbolizer",no_init) diff --git a/bindings/python/mapnik_stroke.cpp b/bindings/python/mapnik_stroke.cpp index 52d574b7e..bcdf340d5 100644 --- a/bindings/python/mapnik_stroke.cpp +++ b/bindings/python/mapnik_stroke.cpp @@ -30,12 +30,12 @@ void export_stroke () using namespace mapnik; using namespace boost::python; - enum_("line_cap") + enum_("line_cap") .value("BUTT_CAP",BUTT_CAP) .value("SQUARE_CAP",SQUARE_CAP) .value("ROUND_CAP",ROUND_CAP) ; - enum_("line_join") + enum_("line_join") .value("MITER_JOIN",MITER_JOIN) .value("MITER_REVERT_JOIN",MITER_REVERT_JOIN) .value("ROUND_JOIN",ROUND_JOIN) diff --git a/bindings/python/mapnik_text_symbolizer.cpp b/bindings/python/mapnik_text_symbolizer.cpp index db73e8a9a..574505ce7 100644 --- a/bindings/python/mapnik_text_symbolizer.cpp +++ b/bindings/python/mapnik_text_symbolizer.cpp @@ -32,9 +32,9 @@ void export_text_symbolizer() using mapnik::text_symbolizer; using mapnik::Color; - enum_("label_placement") - .value("LINE_PLACEMENT",mapnik::line_placement) - .value("POINT_PLACEMENT",mapnik::point_placement) + enum_("label_placement") + .value("LINE_PLACEMENT",mapnik::LINE_PLACEMENT) + .value("POINT_PLACEMENT",mapnik::POINT_PLACEMENT) ; class_("TextSymbolizer", diff --git a/include/mapnik/color.hpp b/include/mapnik/color.hpp index 4bef0e314..d40e4c6bc 100644 --- a/include/mapnik/color.hpp +++ b/include/mapnik/color.hpp @@ -104,6 +104,11 @@ namespace mapnik { return abgr_ == other.abgr_; } + inline bool operator!=(Color const& other) const + { + return abgr_ != other.abgr_; + } + inline std::string to_string() const { std::stringstream ss; diff --git a/include/mapnik/color_factory.hpp b/include/mapnik/color_factory.hpp index 5bcf63001..c0eb8c18b 100644 --- a/include/mapnik/color_factory.hpp +++ b/include/mapnik/color_factory.hpp @@ -27,6 +27,7 @@ #include #include +#include using namespace boost::spirit; @@ -40,8 +41,11 @@ namespace mapnik { actions a(color); css_color_grammar > grammar(a); parse_info<> info = parse(css_color, grammar, space_p); - if (info.full) return color; - return Color(0,0,0); + if ( ! info.full) { + throw config_error(std::string("Failed to parse color value: ") + + "Expected a color, but got '" + css_color + "'"); + } + return color; } private: color_factory(); diff --git a/include/mapnik/config_error.hpp b/include/mapnik/config_error.hpp new file mode 100644 index 000000000..fcb5e7c52 --- /dev/null +++ b/include/mapnik/config_error.hpp @@ -0,0 +1,47 @@ +#ifndef MAPNIK_CONFIG_ERROR_INCLUDED +#define MAPNIK_CONFIG_ERROR_INCLUDED + +#include +#include + +namespace mapnik { + + class config_error : public std::exception + { + public: + config_error() {} + + config_error( const std::string & what ) : + what_( what ) + { + } + virtual ~config_error() throw() {}; + + virtual const char * what() const throw() + { + std::ostringstream os; + os << what_; + if ( ! context_.empty() ) + { + os << std::endl << context_; + } + os << "."; + return os.str().c_str(); + } + + void append_context(const std::string & ctx) const + { + if ( ! context_.empty() ) + { + context_ += " "; + } + context_ += ctx; + } + + protected: + std::string what_; + mutable std::string context_; + }; +} + +#endif // MAPNIK_CONFIG_ERROR_INCLUDED diff --git a/include/mapnik/datasource.hpp b/include/mapnik/datasource.hpp index be84dcfbb..3e33f7745 100644 --- a/include/mapnik/datasource.hpp +++ b/include/mapnik/datasource.hpp @@ -52,9 +52,9 @@ namespace mapnik { class MAPNIK_DECL datasource_exception : public std::exception { private: - const std::string message_; + std::string message_; public: - datasource_exception(const std::string& message=std::string()) + datasource_exception(const std::string& message=std::string("no reason")) :message_(message) {} ~datasource_exception() throw() {} diff --git a/include/mapnik/enumeration.hpp b/include/mapnik/enumeration.hpp new file mode 100644 index 000000000..c61c9b96d --- /dev/null +++ b/include/mapnik/enumeration.hpp @@ -0,0 +1,291 @@ +#ifndef MAPNIK_ENUMERATION_INCLUDED +#define MAPNIK_ENUMERATION_INCLUDED + +#include +#include +#include + +namespace mapnik { + +class illegal_enum_value : public std::exception +{ + public: + illegal_enum_value() {} + + illegal_enum_value( const std::string & what ) : + what_( what ) + { + } + virtual ~illegal_enum_value() throw() {}; + + virtual const char * what() const throw() + { + return what_.c_str(); + } + + protected: + std::string what_; +}; + + +/** Slim wrapper for enumerations. It creates a new type from a native enum and + * a char pointer array. It almost exactly behaves like a native enumeration + * type. It supports string conversion through stream operators. This is usefull + * for debugging, serialization/deserialization and also helps with implementing + * language bindings. The two convinient macros DEFINE_ENUM() and IMPLEMENT_ENUM() + * are provided to help with instanciation. + * + * @par Limitations: + * - The enum must start at zero. + * - The enum must be consecutive. + * - The enum must be terminated with a special token consisting of the enum's + * name plus "_MAX". + * - The corresponding char pointer array must be terminated with an empty string. + * - The names must only consist of characters and digits (a-z, A-Z, 0-9), + * underscores (_) and dashes (-). + * + * + * @warning At the moment the verify() method is called during static initialization. + * It quits the application with exit code 1 if any error is detected. The other solution + * i thought of is to do the checks at compile time (using boost::mpl). + * + * @par Example: + * The following code goes into the header file: + * @code + * enum fruit_enum { + * APPLE, + * CHERRY, + * BANANA, + * PASSION_FRUIT, + * fruit_enum_MAX + * }; + * + * static const char * fruit_strings[] = { + * "apple", + * "cherry", + * "banana", + * "passion_fruit", + * "" + * }; + * + * DEFINE_ENUM( fruit, fruit_enum); + * @endcode + * In the corresponding cpp file do: + * @code + * IMPLEMENT_ENUM( fruit, fruit_strings ); + * @endcode + * And here is how to use the resulting type Fruit + * @code + * + * int + * main(int argc, char * argv[]) { + * fruit f(APPLE); + * switch ( f ) { + * case BANANA: + * case APPLE: + * cerr << "No thanks. I hate " << f << "s" << endl; + * break; + * default: + * cerr << "Hmmm ... yummy " << f << endl; + * break; + * } + * + * f = CHERRY; + * + * fruit_enum native_enum = f; + * + * f.from_string("passion_fruit"); + * + * for (unsigned i = 0; i < fruit::MAX; ++i) { + * cerr << i << " = " << fruit::get_string(i) << endl; + * } + * + * f.from_string("elephant"); // throws illegal_enum_value + * + * return 0; + * } + * @endcode + */ +template +class enumeration { + public: + typedef ENUM Native; + enumeration() {}; + enumeration( ENUM v ) : value_(v) {} + enumeration( const enumeration & other ) : value_(other.value_) {} + + /** Assignment operator for native enum values. */ + void operator=(ENUM v) + { + value_ = v; + } + + /** Assignment operator. */ + void operator=(const enumeration & other) + { + value_ = other.value_; + } + + /** Conversion operator for native enum values. */ + operator ENUM() const + { + return value_; + } + + enum Max + { + MAX = THE_MAX + }; + ENUM max() const + { + return THE_MAX; + } + /** Converts @p str to an enum. + * @throw illegal_enum_value @p str is not a legal identifier. + * */ + void from_string(const std::string & str) + { + for (unsigned i = 0; i < THE_MAX; ++i) + { + if (str == our_strings_[i]) + { + value_ = static_cast(i); + return; + } + } + throw illegal_enum_value(std::string("Illegal enumeration value '") + + str + "' for enum " + our_name_); + } + + /** Parses the input stream @p is for a word consisting of characters and + * digits (a-z, A-Z, 0-9) and underscores (_). + * The failbit of the stream is set if the word is not a valid identifier. + */ + std::istream & parse(std::istream & is) + { + std::string word; + char c; + + while ( is.peek() != std::char_traits< char >::eof()) + { + is >> c; + if ( isspace(c) && word.empty() ) + { + continue; + } + if ( isalnum(c) || (c == '_') || c == '-' ) + { + word += c; + } + else + { + is.unget(); + break; + } + } + + try + { + from_string( word ); + } + catch (const illegal_enum_value & ex) + { + is.setstate(std::ios::failbit); + } + + return is; + } + + /** Returns the current value as a string identifier. */ + std::string as_string() const + { + return our_strings_[value_]; + } + + /** Prints the string identifier to the output stream @p os. */ + std::ostream & print(std::ostream & os = std::cerr) const + { + return os << our_strings_[value_]; + } + + /** Static helper function to iterate over valid identifiers. */ + static const char * get_string(unsigned i) + { + return our_strings_[i]; + } + + /** Performs some simple checks and quits the application if + * any error is detected. Tries to print helpful error messages. + */ + static bool verify(const char * filename, unsigned line_no) + { + for (unsigned i = 0; i < THE_MAX; ++i) + { + if (our_strings_[i] == 0 ) + { + std::cerr << "### FATAL: Not enough strings for enum " + << our_name_ << " defined in file '" << filename + << "' at line " << line_no << std::endl; + exit(1); + } + } + if ( std::string("") != our_strings_[THE_MAX]) + { + std::cerr << "### FATAL: The string array for enum " << our_name_ + << " defined in file '" << filename << "' at line " << line_no + << " has too many items or is not terminated with an " + << "empty string." << std::endl; + exit(1); + } + return true; + } + static const char * get_name() + { + return our_name_; + } + private: + ENUM value_; + static const char ** our_strings_ ; + static const char * our_name_ ; + static bool our_verified_flag_; +}; + +/** ostream operator for enumeration + * @relates mapnik::enumeration + */ +template +std::ostream & +operator<<(std::ostream & os, const mapnik::enumeration & e) +{ + e.print( os ); + return os; +} + +/** istream operator for enumeration + * @relates mapnik::enumeration + */ +template +std::istream & +operator>>(std::istream & is, mapnik::enumeration & e) +{ + e.parse( is ); + return is; +} + +} // end of namespace + +/** Helper macro. Creates a typedef. + * @relates mapnik::enumeration + */ +#define DEFINE_ENUM( name, e) \ + typedef mapnik::enumeration name; + +/** Helper macro. Runs the verify() method during static initialization. + * @relates mapnik::enumeration + */ +#define IMPLEMENT_ENUM( name, strings ) \ + template <> const char ** name ::our_strings_ = strings; \ + template <> const char * name ::our_name_ = #name; \ + template <> bool name ::our_verified_flag_( name ::verify(__FILE__, __LINE__)); + +#endif // MAPNIK_ENUMERATION_INCLUDED diff --git a/include/mapnik/filter_factory.hpp b/include/mapnik/filter_factory.hpp index db94286c4..ff8351c8b 100644 --- a/include/mapnik/filter_factory.hpp +++ b/include/mapnik/filter_factory.hpp @@ -25,6 +25,7 @@ #ifndef FILTER_FACTORY_HPP #define FILTER_FACTORY_HPP +#include #include namespace mapnik @@ -41,14 +42,23 @@ namespace mapnik stack > > exps; filter_grammar grammar(filters,exps); char const *text = str.c_str(); - parse_info<> info = parse(text,text+strlen(text),grammar,space_p); - if (info.full && !filters.empty()) + parse_info<> info = parse(text, grammar, space_p); + if ( ! info.full) { + std::ostringstream os; + os << "Failed to parse filter expression:" << std::endl + << str << std::endl + << "Parsing aborted at '" << info.stop << "'"; + + throw config_error( os.str() ); + } + + if ( ! filters.empty()) { return filters.top(); } else { - clog << "failed at :" << info.stop << "\n"; + // XXX: do we ever get here? [DS] return filter_ptr(new none_filter()); } } diff --git a/include/mapnik/filter_parser.hpp b/include/mapnik/filter_parser.hpp index ca5f86383..3aaff8c35 100644 --- a/include/mapnik/filter_parser.hpp +++ b/include/mapnik/filter_parser.hpp @@ -29,6 +29,9 @@ #include #include // boost + +//#define BOOST_SPIRIT_DEBUG + #include #include #include @@ -404,7 +407,7 @@ namespace mapnik [compose_filter >(self.filters,self.exprs)] | (L"<=" >> expression) [compose_filter >(self.filters,self.exprs)] - | regex ); + /* | regex */); equation = relation >> *( ( L'=' >> relation) [compose_filter >(self.filters,self.exprs)] @@ -417,7 +420,26 @@ namespace mapnik or_expr = and_expr >> *(L"or" >> and_expr)[compose_or_filter(self.filters)]; - filter_statement = or_expr; + filter_statement = or_expr >> *(space_p) >> end_p; + +#ifdef BOOST_SPIRIT_DEBUG + BOOST_SPIRIT_DEBUG_RULE( factor ); + BOOST_SPIRIT_DEBUG_RULE( term ); + BOOST_SPIRIT_DEBUG_RULE( expression ); + BOOST_SPIRIT_DEBUG_RULE( relation ); + BOOST_SPIRIT_DEBUG_RULE( equation ); + BOOST_SPIRIT_DEBUG_RULE( not_expr ); + BOOST_SPIRIT_DEBUG_RULE( and_expr ); + BOOST_SPIRIT_DEBUG_RULE( or_expr ); + + BOOST_SPIRIT_DEBUG_RULE( filter_statement ); + BOOST_SPIRIT_DEBUG_RULE( literal ); + BOOST_SPIRIT_DEBUG_RULE( number ); + BOOST_SPIRIT_DEBUG_RULE( string_ ); + BOOST_SPIRIT_DEBUG_RULE( property ); + BOOST_SPIRIT_DEBUG_RULE( function ); + BOOST_SPIRIT_DEBUG_RULE( regex ); +#endif } boost::spirit::rule const& start() const @@ -444,6 +466,8 @@ namespace mapnik symbols func1_op; symbols func2_op; symbols spatial_op; + + }; stack > >& filters; stack > >& exprs; diff --git a/include/mapnik/libxml2_loader.hpp b/include/mapnik/libxml2_loader.hpp new file mode 100644 index 000000000..f2a7f1533 --- /dev/null +++ b/include/mapnik/libxml2_loader.hpp @@ -0,0 +1,9 @@ +#ifndef LOAD_MAP_XML2_INCLUDED +#define LOAD_MAP_XML2_INCLUDED + +namespace mapnik +{ + void read_xml2( std::string const & filename, boost::property_tree::ptree & pt); +} + +#endif // LOAD_MAP_XML2_INCLUDED diff --git a/include/mapnik/line_pattern_symbolizer.hpp b/include/mapnik/line_pattern_symbolizer.hpp index e6a69f308..bb1266a60 100644 --- a/include/mapnik/line_pattern_symbolizer.hpp +++ b/include/mapnik/line_pattern_symbolizer.hpp @@ -26,19 +26,17 @@ #include #include +#include namespace mapnik { - struct MAPNIK_DECL line_pattern_symbolizer + struct MAPNIK_DECL line_pattern_symbolizer : + public symbolizer_with_image { line_pattern_symbolizer(std::string const& file, std::string const& type, unsigned width,unsigned height); - line_pattern_symbolizer(line_pattern_symbolizer const& rhs); - ImageData32 const& get_pattern() const; - private: - boost::shared_ptr pattern_; }; } diff --git a/include/mapnik/load_map.hpp b/include/mapnik/load_map.hpp index d93c9878d..79c8367fb 100644 --- a/include/mapnik/load_map.hpp +++ b/include/mapnik/load_map.hpp @@ -29,7 +29,7 @@ namespace mapnik { - MAPNIK_DECL void load_map(Map & map, std::string const& filename); + MAPNIK_DECL void load_map(Map & map, std::string const& filename, bool strict = false); } #endif // LOAD_MAP_HPP diff --git a/include/mapnik/map.hpp b/include/mapnik/map.hpp index 8a9cac30b..ff5a0f686 100644 --- a/include/mapnik/map.hpp +++ b/include/mapnik/map.hpp @@ -199,7 +199,7 @@ namespace mapnik * @param c Background color. */ void set_background(const Color& c); - + /*! \brief Get the map background color * @return Background color as boost::optional * object diff --git a/include/mapnik/point_symbolizer.hpp b/include/mapnik/point_symbolizer.hpp index dabe5cf82..a45a6cfb1 100644 --- a/include/mapnik/point_symbolizer.hpp +++ b/include/mapnik/point_symbolizer.hpp @@ -26,23 +26,22 @@ #include #include +#include namespace mapnik { - struct MAPNIK_DECL point_symbolizer + struct MAPNIK_DECL point_symbolizer : + public symbolizer_with_image { explicit point_symbolizer(); point_symbolizer(std::string const& file, std::string const& type, unsigned width,unsigned height); point_symbolizer(point_symbolizer const& rhs); - void set_data (boost::shared_ptr symbol); - boost::shared_ptr const& get_data() const; void set_allow_overlap(bool overlap); bool get_allow_overlap() const; private: - boost::shared_ptr symbol_; bool overlap_; }; } diff --git a/include/mapnik/polygon_pattern_symbolizer.hpp b/include/mapnik/polygon_pattern_symbolizer.hpp index 9a6729875..c998b7630 100644 --- a/include/mapnik/polygon_pattern_symbolizer.hpp +++ b/include/mapnik/polygon_pattern_symbolizer.hpp @@ -26,10 +26,12 @@ #include #include +#include namespace mapnik { - struct MAPNIK_DECL polygon_pattern_symbolizer + struct MAPNIK_DECL polygon_pattern_symbolizer : + public symbolizer_with_image { polygon_pattern_symbolizer(std::string const& file, @@ -37,10 +39,6 @@ namespace mapnik unsigned width,unsigned height); polygon_pattern_symbolizer(polygon_pattern_symbolizer const& rhs); - - ImageData32 const& get_pattern() const; - private: - boost::shared_ptr pattern_; }; } diff --git a/include/mapnik/ptree_helpers.hpp b/include/mapnik/ptree_helpers.hpp new file mode 100644 index 000000000..f75496a5a --- /dev/null +++ b/include/mapnik/ptree_helpers.hpp @@ -0,0 +1,338 @@ +#ifndef MAPNIK_CONFIG_HELPERS_INCLUDED +#define MAPNIK_CONFIG_HELPERS_INCLUDED + +#include +#include + +#include +#include +#include + +#include +#include +#include + +namespace mapnik { + + template + T get(const boost::property_tree::ptree & node, const std::string & name, bool is_attribute, + const T & default_value); + template + T get(const boost::property_tree::ptree & node, const std::string & name, bool is_attribute); + template + T get_own(const boost::property_tree::ptree & node, const std::string & name); + template + boost::optional get_optional(const boost::property_tree::ptree & node, const std::string & name, + bool is_attribute); + + template + boost::optional get_opt_attr( const boost::property_tree::ptree & node, + const std::string & name) + { + return get_optional( node, name, true); + } + + template + boost::optional get_opt_child( const boost::property_tree::ptree & node, + const std::string & name) + { + return get_optional( node, name, false); + } + + template + T get_attr( const boost::property_tree::ptree & node, const std::string & name, + const T & default_value ) + { + return get( node, name, true, default_value); + } + + template + T get_attr( const boost::property_tree::ptree & node, const std::string & name ) + { + return get( node, name, true ); + } + + template + T get_css( const boost::property_tree::ptree & node, const std::string & name ) + { + return get_own( node, std::string("CSS parameter '") + name + "'"); + } + + /** Stream input operator for Color values */ + template + std::basic_istream & + operator >> ( std::basic_istream & s, mapnik::Color & c ) + { + std::string word; + s >> word; + if ( s ) + { + try + { + c = mapnik::color_factory::from_string( word.c_str() ); + } + catch (...) + { + s.setstate( std::ios::failbit ); + } + } + return s; + } + + template + std::basic_ostream & + operator << ( std::basic_ostream & s, const mapnik::Color & c ) + { + std::string hex_string( c.to_hex_string() ); + s << hex_string; + return s; + } + + /** Helper for class bool */ + class boolean { + public: + boolean() {} + boolean(bool b) : b_(b) {} + boolean(const boolean & b) : b_(b.b_) {} + + operator bool() const + { + return b_; + } + boolean & operator = (const boolean & other) + { + b_ = other.b_; + return * this; + } + boolean & operator = (bool other) + { + b_ = other; + return * this; + } + private: + bool b_; + }; + + /** Special stream input operator for boolean values */ + template + std::basic_istream & + operator >> ( std::basic_istream & s, boolean & b ) + { + std::string word; + s >> word; + if ( s ) + { + if ( word == "true" || word == "yes" || word == "on" || + word == "1") + { + b = true; + } + else if ( word == "false" || word == "no" || word == "off" || + word == "0") + { + b = false; + } + else + { + s.setstate( std::ios::failbit ); + } + } + return s; + } + + template + std::basic_ostream & + operator << ( std::basic_ostream & s, const boolean & b ) + { + s << ( b ? "true" : "false" ); + return s; + } + + template + void set_attr(boost::property_tree::ptree & pt, const std::string & name, const T & v) + { + pt.put("." + name, v); + } + + /* + template <> + void set_attr(boost::property_tree::ptree & pt, const std::string & name, const bool & v) + { + pt.put("." + name, boolean(v)); + } + */ + + class boolean; + + template + void set_css(boost::property_tree::ptree & pt, const std::string & name, const T & v) + { + boost::property_tree::ptree & css_node = pt.push_back( + boost::property_tree::ptree::value_type("CssParameter", + boost::property_tree::ptree()))->second; + css_node.put(".name", name ); + css_node.put_own( v ); + } + + template + struct name_trait + { + static const char * name() + { + return ""; + } + // missing name_trait for type ... + // if you get here you are probably using a new type + // in the XML file. Just add a name trait for the new + // type below. + BOOST_STATIC_ASSERT( sizeof(T) == 0 ); + }; + +#define DEFINE_NAME_TRAIT_WITH_NAME( type, type_name ) \ + template <> \ + struct name_trait \ + { \ + static const char * name() { return "type " type_name; } \ + }; + +#define DEFINE_NAME_TRAIT( type ) \ + DEFINE_NAME_TRAIT_WITH_NAME( type, #type ); + + DEFINE_NAME_TRAIT( double ); + DEFINE_NAME_TRAIT( float ); + DEFINE_NAME_TRAIT( unsigned ); + DEFINE_NAME_TRAIT( boolean ); + DEFINE_NAME_TRAIT_WITH_NAME( int, "integer" ); + DEFINE_NAME_TRAIT_WITH_NAME( std::string, "string" ); + DEFINE_NAME_TRAIT_WITH_NAME( Color, "color" ); + + template + struct name_trait< enumeration > + { + typedef enumeration Enum; + + static const char * name() + { + std::string value_list("one of ["); + for (unsigned i = 0; i < Enum::MAX; ++i) + { + value_list += Enum::get_string( i ); + if ( i + 1 < Enum::MAX ) value_list += ", "; + } + value_list += "]"; + + return value_list.c_str(); + } + }; + + template + T get(const boost::property_tree::ptree & node, const std::string & name, bool is_attribute, + const T & default_value) + { + boost::optional str; + if (is_attribute) + { + str = node.get_optional( std::string(".") + name ); + } + else + { + str = node.get_optional(name ); + } + + if ( str ) { + try + { + return boost::lexical_cast( * str ); + } + catch (const boost::bad_lexical_cast & ex) + { + throw config_error(string("Failed to parse ") + + (is_attribute ? "attribute" : "child node") + " '" + + name + "'. Expected " + name_trait::name() + + " but got '" + *str + "'"); + } + } else { + return default_value; + } + } + + template + T get(const boost::property_tree::ptree & node, const std::string & name, bool is_attribute) + { + boost::optional str; + if (is_attribute) + { + str = node.get_optional( std::string(".") + name); + } + else + { + str = node.get_optional(name); + } + + if ( ! str ) { + throw config_error(string("Required ") + + (is_attribute ? "attribute " : "child node ") + + "'" + name + "' is missing"); + } + try + { + return boost::lexical_cast( *str ); + } + catch (const boost::bad_lexical_cast & ex) + { + throw config_error(string("Failed to parse ") + + (is_attribute ? "attribute" : "child node") + " '" + + name + "'. Expected " + name_trait::name() + + " but got '" + *str + "'"); + } + } + + template + T get_own(const boost::property_tree::ptree & node, const std::string & name) + { + try + { + return node.get_own(); + } + catch (...) + { + throw config_error(string("Failed to parse ") + + name + ". Expected " + name_trait::name() + + " but got '" + node.data() + "'"); + } + } + + template + boost::optional get_optional(const boost::property_tree::ptree & node, const std::string & name, + bool is_attribute) + { + boost::optional str; + if (is_attribute) + { + str = node.get_optional( std::string(".") + name); + } + else + { + str = node.get_optional(name); + } + + boost::optional result; + if ( str ) { + try + { + result = boost::lexical_cast( *str ); + } + catch (const boost::bad_lexical_cast & ex) + { + throw config_error(string("Failed to parse ") + + (is_attribute ? "attribute" : "child node") + " '" + + name + "'. Expected " + name_trait::name() + + " but got '" + *str + "'"); + } + + } + return result; + } +} // end of namespace mapnik + +#endif // MAPNIK_CONFIG_HELPERS_INCLUDED diff --git a/include/mapnik/shield_symbolizer.hpp b/include/mapnik/shield_symbolizer.hpp index 891fca32c..fa4e94c20 100644 --- a/include/mapnik/shield_symbolizer.hpp +++ b/include/mapnik/shield_symbolizer.hpp @@ -25,13 +25,16 @@ #ifndef SHIELD_SYMBOLIZER_HPP #define SHIELD_SYMBOLIZER_HPP -#include #include #include +#include + +#include namespace mapnik { - struct MAPNIK_DECL shield_symbolizer : public text_symbolizer + struct MAPNIK_DECL shield_symbolizer : public text_symbolizer, + public symbolizer_with_image { shield_symbolizer(std::string const& name, std::string const& face_name, @@ -41,13 +44,6 @@ namespace mapnik std::string const& type, unsigned width,unsigned height); - - void set_background_image(boost::shared_ptr); - boost::shared_ptr const& get_background_image() const; - - private: - boost::shared_ptr background_image_; - }; } diff --git a/include/mapnik/stroke.hpp b/include/mapnik/stroke.hpp index 1eb1f1280..4e7e62eda 100644 --- a/include/mapnik/stroke.hpp +++ b/include/mapnik/stroke.hpp @@ -27,6 +27,7 @@ #include // mapnik #include +#include namespace mapnik { @@ -34,20 +35,30 @@ namespace mapnik using std::vector; typedef vector > dash_array; - enum line_cap_e + // if you add new tokens, don't forget to add them to the corresponding + // string array in the cpp file too. + enum line_cap_enum { BUTT_CAP, SQUARE_CAP, - ROUND_CAP + ROUND_CAP, + line_cap_enum_MAX }; + + DEFINE_ENUM( line_cap_e, line_cap_enum ); - enum line_join_e + // if you add new tokens, don't forget to add them to the corresponding + // string array in the cpp file too. + enum line_join_enum { MITER_JOIN, MITER_REVERT_JOIN, ROUND_JOIN, - BEVEL_JOIN + BEVEL_JOIN, + line_join_enum_MAX }; + + DEFINE_ENUM( line_join_e, line_join_enum ); class stroke { @@ -58,112 +69,34 @@ namespace mapnik line_join_e line_join_; dash_array dash_; public: - explicit stroke() - : c_(0,0,0), - width_(1.0), - opacity_(1.0), - line_cap_(BUTT_CAP), - line_join_(MITER_JOIN), - dash_() {} - - stroke(Color const& c, float width=1.0) - : c_(c), - width_(width), - opacity_(1.0), - line_cap_(BUTT_CAP), - line_join_(MITER_JOIN), - dash_() {} + explicit stroke(); + stroke(Color const& c, float width=1.0); + stroke(stroke const& other); + stroke& operator=(const stroke& rhs); - stroke(stroke const& other) - : c_(other.c_), - width_(other.width_), - opacity_(other.opacity_), - line_cap_(other.line_cap_), - line_join_(other.line_join_), - dash_(other.dash_) {} - - stroke& operator=(const stroke& rhs) - { - stroke tmp(rhs); - swap(tmp); - return *this; - } + void set_color(const Color& c); + + Color const& get_color() const; + + float get_width() const; + void set_width(float w); + void set_opacity(float opacity); - void set_color(const Color& c) - { - c_=c; - } + float get_opacity() const; - Color const& get_color() const - { - return c_; - } + void set_line_cap(line_cap_e line_cap); + line_cap_e get_line_cap() const; - float get_width() const - { - return width_; - } - void set_width(float w) - { - width_=w; - } - - void set_opacity(float opacity) - { - if (opacity > 1.0) opacity_=1.0; - else if (opacity < 0.0) opacity_=0.0; - else opacity_=opacity; - } - - float get_opacity() const - { - return opacity_; - } + void set_line_join(line_join_e line_join); + line_join_e get_line_join() const; - void set_line_cap(line_cap_e line_cap) - { - line_cap_=line_cap; - } - - line_cap_e get_line_cap() const - { - return line_cap_; - } + void add_dash(float dash,float gap); + bool has_dash() const; - void set_line_join(line_join_e line_join) - { - line_join_=line_join; - } - - line_join_e get_line_join() const - { - return line_join_; - } - - void add_dash(float dash,float gap) - { - dash_.push_back(std::make_pair(dash,gap)); - } - bool has_dash() const - { - return dash_.size()>0 ? true : false ; - } - - dash_array const& get_dash_array() const - { - return dash_; - } + dash_array const& get_dash_array() const; private: - void swap(const stroke& other) throw() - { - c_=other.c_; - width_=other.width_; - opacity_=other.opacity_; - line_cap_=other.line_cap_; - line_join_=other.line_join_; - dash_ = other.dash_; - } + void swap(const stroke& other) throw(); }; } diff --git a/include/mapnik/symbolizer.hpp b/include/mapnik/symbolizer.hpp index c81e872e7..c5659dc5d 100644 --- a/include/mapnik/symbolizer.hpp +++ b/include/mapnik/symbolizer.hpp @@ -24,8 +24,31 @@ #ifndef SYMBOLIZER_HPP #define SYMBOLIZER_HPP +#include +#include + namespace mapnik { + + class symbolizer_with_image { + public: + boost::shared_ptr get_image() const; + const std::string & get_filename() const; + void set_image( boost::shared_ptr symbol); + + virtual ~symbolizer_with_image() {}; + protected: + symbolizer_with_image(boost::shared_ptr img); + symbolizer_with_image(std::string const& file, + std::string const& type, + unsigned width,unsigned height); + + symbolizer_with_image(symbolizer_with_image const& rhs); + + boost::shared_ptr image_; + std::string image_filename_; + + }; } #endif //SYMBOLIZER_HPP diff --git a/include/mapnik/text_symbolizer.hpp b/include/mapnik/text_symbolizer.hpp index 47816342e..c35e660e9 100644 --- a/include/mapnik/text_symbolizer.hpp +++ b/include/mapnik/text_symbolizer.hpp @@ -30,22 +30,25 @@ #include #include // mapnik +#include #include #include namespace mapnik { - enum label_placement_e { - point_placement=1, - line_placement=2 + enum label_placement_enum { + POINT_PLACEMENT, + LINE_PLACEMENT, + label_placement_enum_MAX }; + DEFINE_ENUM( label_placement_e, label_placement_enum ); typedef boost::tuple position; struct MAPNIK_DECL text_symbolizer { text_symbolizer(std::string const& name,std::string const& face_name, - unsigned size,Color const& fill); + unsigned size, Color const& fill); text_symbolizer(text_symbolizer const& rhs); text_symbolizer& operator=(text_symbolizer const& rhs); std::string const& get_name() const; diff --git a/plugins/input/postgis/connection.hpp b/plugins/input/postgis/connection.hpp index 12e111d57..c0fafb42a 100644 --- a/plugins/input/postgis/connection.hpp +++ b/plugins/input/postgis/connection.hpp @@ -25,6 +25,8 @@ #ifndef CONNECTION_HPP #define CONNECTION_HPP +#include + extern "C" { #include "libpq-fe.h" @@ -41,10 +43,18 @@ class Connection Connection(std::string const& connection_str) { conn_=PQconnectdb(connection_str.c_str()); - if (PQstatus(conn_) == CONNECTION_BAD) + if (PQstatus(conn_) != CONNECTION_OK) { - std::clog << "connection ["<< connection_str<< "] failed\n" - << PQerrorMessage(conn_)<< std::endl; + std::string s("PSQL error"); + if (conn_ ) + { + std::string msg = PQerrorMessage( conn_ ); + if ( ! msg.empty() ) + { + s += ":\n" + msg.substr( 0, msg.size() - 1 ); + } + } + throw mapnik::datasource_exception( s ); } } diff --git a/plugins/input/postgis/connection_manager.hpp b/plugins/input/postgis/connection_manager.hpp index ee3e7ad31..bd1ade801 100644 --- a/plugins/input/postgis/connection_manager.hpp +++ b/plugins/input/postgis/connection_manager.hpp @@ -57,7 +57,7 @@ public: T* operator()() const { - return new T(connection_string()); + return new T(connection_string()); } inline std::string id() const diff --git a/src/SConscript b/src/SConscript index 3678793e0..eeb9f5344 100644 --- a/src/SConscript +++ b/src/SConscript @@ -46,8 +46,8 @@ source = Split( image_util.cpp layer.cpp line_pattern_symbolizer.cpp - load_map.cpp map.cpp + load_map.cpp memory.cpp params.cpp placement_finder.cpp @@ -65,15 +65,25 @@ source = Split( distance.cpp scale_denominator.cpp memory_datasource.cpp + stroke.cpp + symbolizer.cpp """ ) -source += Split( - """ - ../tinyxml/tinystr.cpp - ../tinyxml/tinyxml.cpp - ../tinyxml/tinyxmlerror.cpp - ../tinyxml/tinyxmlparser.cpp - """) + +if env['XMLPARSER'] == 'tinyxml': + source += Split( + """ + ../tinyxml/tinystr.cpp + ../tinyxml/tinyxml.cpp + ../tinyxml/tinyxmlerror.cpp + ../tinyxml/tinyxmlparser.cpp + """) +elif env['XMLPARSER'] == 'libxml2': + source += Split( + """ + libxml2_loader.cpp + """) + mapnik = env.SharedLibrary('mapnik', source, LIBS=libraries, LINKFLAGS=linkflags) diff --git a/src/agg_renderer.cpp b/src/agg_renderer.cpp index 9a22defe1..8d180ecd8 100644 --- a/src/agg_renderer.cpp +++ b/src/agg_renderer.cpp @@ -218,7 +218,8 @@ namespace mapnik boost::scoped_ptr roof(new polygon_impl); std::deque face_segments; - double x0,y0; + double x0(0); + double y0(0); for (unsigned j=0;j const& data = sym.get_data(); + boost::shared_ptr const& data = sym.get_image(); if ( data ) { for (unsigned i=0;i const& data = sym.get_background_image(); + boost::shared_ptr const& data = sym.get_image(); if (text.length() > 0 && data) { face_ptr face = font_manager_.get_face(sym.get_face_name()); @@ -487,7 +488,7 @@ namespace mapnik typedef agg::renderer_outline_image renderer_type; typedef agg::rasterizer_outline_aa rasterizer_type; - ImageData32 const& pat = sym.get_pattern(); + ImageData32 pat = * sym.get_image(); renderer_base ren_base(pixf_); agg::pattern_filter_bilinear_rgba8 filter; pattern_source source(pat); @@ -532,7 +533,7 @@ namespace mapnik ren_base renb(pixf_); agg::scanline_u8 sl; - ImageData32 const& pattern = sym.get_pattern(); + ImageData32 const& pattern = * sym.get_image(); unsigned w=pattern.width(); unsigned h=pattern.height(); agg::row_accessor pattern_rbuf((agg::int8u*)pattern.getBytes(),w,h,w*4); diff --git a/src/datasource_cache.cpp b/src/datasource_cache.cpp index 8b5bfd454..514949b3b 100644 --- a/src/datasource_cache.cpp +++ b/src/datasource_cache.cpp @@ -30,6 +30,7 @@ #include // mapnik #include +#include // ltdl #include @@ -46,7 +47,7 @@ namespace mapnik datasource_cache::datasource_cache() { - if (lt_dlinit()) throw; + if (lt_dlinit()) throw std::runtime_error("lt_dlinit() failed"); } datasource_cache::~datasource_cache() @@ -57,49 +58,43 @@ namespace mapnik std::map > datasource_cache::plugins_; bool datasource_cache::registered_=false; - datasource_ptr datasource_cache::create(const parameters& params) + datasource_ptr datasource_cache::create(const parameters& params) { - datasource_ptr ds; - try - { - boost::optional type = params.get("type"); - if (type) - { - map >::iterator itr=plugins_.find(*type); - if (itr!=plugins_.end()) - { - if (itr->second->handle()) - { - create_ds* create_datasource = - (create_ds*) lt_dlsym(itr->second->handle(), "create"); - if (!create_datasource) - { - std::clog << "Cannot load symbols: " << lt_dlerror() << std::endl; - } - else - { - ds=datasource_ptr(create_datasource(params), datasource_deleter()); - } - } - else - { - std::clog << "Cannot load library: " << lt_dlerror() << std::endl; - } - } - } + boost::optional type = params.get("type"); + if ( ! type) + { + throw config_error(string("Could not create datasource. Required ") + + "parameter 'type' is missing"); + } + + datasource_ptr ds; + map >::iterator itr=plugins_.find(*type); + if ( itr == plugins_.end() ) + { + throw config_error(string("Could not create datasource. No plugin ") + + "found for type '" + * type + "'"); + } + if ( ! itr->second->handle()) + { + throw std::runtime_error(string("Cannot load library: ") + + lt_dlerror()); + } + + create_ds* create_datasource = + (create_ds*) lt_dlsym(itr->second->handle(), "create"); + + if ( ! create_datasource) + { + throw std::runtime_error(string("Cannot load symbols: ") + + lt_dlerror()); + } + + ds=datasource_ptr(create_datasource(params), datasource_deleter()); + #ifdef MAPNIK_DEBUG - std::clog<<"datasource="< +using std::cerr; +using std::endl; + namespace mapnik { freetype_engine::freetype_engine() @@ -30,7 +33,7 @@ namespace mapnik FT_Error error = FT_Init_FreeType( &library_ ); if (error) { - throw std::runtime_error("can not load FreeType2 library"); + throw std::runtime_error("Failed to initialize FreeType2 library"); } } @@ -43,6 +46,7 @@ namespace mapnik { mutex::scoped_lock lock(mapnik::singleton::mutex_); + //cerr << "freetype_engine::register_font() '" << file_name << "'" << endl; FT_Face face; FT_Error error = FT_New_Face (library_,file_name.c_str(),0,&face); if ( !error ) diff --git a/src/libxml2_loader.cpp b/src/libxml2_loader.cpp new file mode 100644 index 000000000..a0aac5c45 --- /dev/null +++ b/src/libxml2_loader.cpp @@ -0,0 +1,180 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2007 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 + * + *****************************************************************************/ + +#include +#include +#include + +#include +#include +#include + +#include + +#include + +using boost::property_tree::ptree; +using namespace std; + +namespace mapnik +{ + class libxml2_loader : boost::noncopyable + { + public: + libxml2_loader() : + ctx_( 0 ) + { + LIBXML_TEST_VERSION; + } + + ~libxml2_loader() + { + if (ctx_ && ctx_->myDoc) + { + xmlFreeDoc( ctx_->myDoc ); + } + if (ctx_) + { + xmlFreeParserCtxt(ctx_); + } + } + + void load( const std::string & filename, ptree & pt ) + { + boost::filesystem::path path(filename); + if ( ! boost::filesystem::exists( path ) ) { + throw config_error(string("Could not load map file '") + + filename + "': File does not exist"); + } + ctx_ = xmlCreateFileParserCtxt( filename.c_str() ); + + if ( ! ctx_ ) + { + throw std::runtime_error("Failed to create parser context."); + } + + ctx_->replaceEntities = true; + ctx_->keepBlanks = false; + xmlCtxtUseOptions( ctx_, XML_PARSE_NOERROR | XML_PARSE_NOENT | XML_PARSE_NOBLANKS | + XML_PARSE_DTDLOAD); + xmlParseDocument( ctx_ ); + + if ( ! ctx_->wellFormed ) + { + xmlError * error = xmlCtxtGetLastError( ctx_ ); + std::ostringstream os; + os << "XML document not well formed"; + if (error) + { + os << ": " << std::endl << error->message; + // remove CR + std::string msg = os.str().substr(0, os.str().size() - 1); + config_error ex( msg ); + + os.str(""); + os << "in file '" << error->file << "' at line " + << error->line; + + ex.append_context( os.str() ); + + throw ex; + } + } + + /* + if ( ! ctx->valid ) + { + std::clog << "### ERROR: Failed to validate DTD." + << std::endl; + } + */ + + xmlNode * root( 0 ); + root = xmlDocGetRootElement( ctx_->myDoc ); + if ( ! root ) { + throw config_error("XML document is empty."); + } + + populate_tree( root, pt ); + + } + + private: + void append_attributes( xmlAttr * attributes, ptree & pt) + { + if (attributes) + { + ptree::iterator it = pt.push_back( ptree::value_type( "", ptree() )); + ptree & attr_list = it->second; + xmlAttr * cur_attr = attributes; + for (; cur_attr; cur_attr = cur_attr->next ) + { + ptree::iterator it = attr_list.push_back( + ptree::value_type( (char*)cur_attr->name, ptree() )); + it->second.put_own( (char*) cur_attr->children->content ); + } + } + } + + void populate_tree( xmlNode * node, ptree & pt ) + { + xmlNode * cur_node = node; + + for (; cur_node; cur_node = cur_node->next ) + { + switch (cur_node->type) + { + case XML_ELEMENT_NODE: + { + ptree::iterator it = pt.push_back( ptree::value_type( + (char*)cur_node->name, ptree() )); + append_attributes( cur_node->properties, it->second); + populate_tree( cur_node->children, it->second ); + } + break; + case XML_TEXT_NODE: + pt.put_own( (char*) cur_node->content ); + break; + case XML_COMMENT_NODE: + { + ptree::iterator it = pt.push_back( + ptree::value_type( "", ptree() )); + it->second.put_own( (char*) cur_node->content ); + } + break; + default: + break; + + } + } + } + + xmlParserCtxtPtr ctx_; + }; + + void read_xml2( std::string const & filename, boost::property_tree::ptree & pt) + { + libxml2_loader loader; + loader.load( filename, pt ); + } + +} // end of namespace mapnik diff --git a/src/line_pattern_symbolizer.cpp b/src/line_pattern_symbolizer.cpp index 3dd216705..209c7be80 100644 --- a/src/line_pattern_symbolizer.cpp +++ b/src/line_pattern_symbolizer.cpp @@ -33,25 +33,10 @@ namespace mapnik line_pattern_symbolizer::line_pattern_symbolizer(std::string const& file, std::string const& type, unsigned width,unsigned height) - : pattern_(new ImageData32(width,height)) - { - try - { - std::auto_ptr reader(get_image_reader(type,file)); - if (reader.get()) - reader->read(0,0,*pattern_); - } - catch (...) - { - std::clog << "exception caught..." << std::endl; - } - } + : symbolizer_with_image( file, type, width, height ) + { } line_pattern_symbolizer::line_pattern_symbolizer(line_pattern_symbolizer const& rhs) - : pattern_(rhs.pattern_) {} + : symbolizer_with_image(rhs) {} - ImageData32 const& line_pattern_symbolizer::get_pattern() const - { - return *pattern_; - } } diff --git a/src/load_map.cpp b/src/load_map.cpp index a6eb66025..abec43f4b 100644 --- a/src/load_map.cpp +++ b/src/load_map.cpp @@ -28,523 +28,783 @@ #include #include #include +#include // mapnik +#include #include #include #include #include #include +#include +#include +#include #include using boost::lexical_cast; using boost::bad_lexical_cast; using boost::tokenizer; +using boost::property_tree::ptree; + +using std::cerr; +using std::endl; namespace mapnik { - void load_map(Map & map, std::string const& filename) + using boost::optional; + + class map_parser { + public: + map_parser( bool strict ) : strict_( strict ) {}; + + void parse_map( Map & map, ptree const & sty); + private: + void parse_style( Map & map, ptree const & sty); + void parse_layer( Map & map, ptree const & lay); + + void parse_rule( feature_type_style & style, ptree const & r); + + void parse_point_symbolizer( rule_type & rule, ptree const & sym); + void parse_line_pattern_symbolizer( rule_type & rule, ptree const & sym); + void parse_polygon_pattern_symbolizer( rule_type & rule, ptree const & sym); + void parse_text_symbolizer( rule_type & rule, ptree const & sym); + void parse_shield_symbolizer( rule_type & rule, ptree const & sym); + void parse_line_symbolizer( rule_type & rule, ptree const & sym); + void parse_polygon_symbolizer( rule_type & rule, ptree const & sym); + void parse_building_symbolizer( rule_type & rule, ptree const & sym ); + + void ensure_font_face( const text_symbolizer & text_symbol ); + + bool strict_; + face_manager font_manager_; + }; + + void load_map(Map & map, std::string const& filename, bool strict) { - using boost::property_tree::ptree; ptree pt; - read_xml(filename,pt); - - boost::optional bgcolor = - pt.get_optional("Map..bgcolor"); - - if (bgcolor) +#ifdef HAVE_LIBXML2 + read_xml2(filename, pt); +#else + try { - Color bg = color_factory::from_string(bgcolor->c_str()); - map.set_background(bg); + read_xml(filename, pt); } - - std::string srs = pt.get("Map..srs", - "+proj=latlong +datum=WGS84"); - map.set_srs(srs); - - ptree::const_iterator itr = pt.get_child("Map").begin(); - ptree::const_iterator end = pt.get_child("Map").end(); - - for (; itr != end; ++itr) + catch (const boost::property_tree::xml_parser_error & ex) { - ptree::value_type const& v = *itr; - - if (v.first == "Style") + throw config_error( ex.what() ); + } +#endif + + map_parser parser( strict ); + parser.parse_map(map, pt); + } + + void map_parser::parse_map( Map & map, ptree const & pt ) + { + try + { + ptree const & map_node = pt.get_child("Map"); + + try { - std::string name = v.second.get(".name"); - feature_type_style style; - - ptree::const_iterator ruleIter = v.second.begin(); - ptree::const_iterator endRule = v.second.end(); - - for (; ruleIter!=endRule; ++ruleIter) - { - ptree::value_type const& rule_tag = *ruleIter; - if (rule_tag.first == "Rule") - { - std::string name = - rule_tag.second.get(".name",""); - std::string title = - rule_tag.second.get(".title",""); - rule_type rule(name,title); - - boost::optional filter_expr = - rule_tag.second.get_optional("Filter"); - - if (filter_expr) - { - rule.set_filter(create_filter(*filter_expr)); - } - boost::optional else_filter = - rule_tag.second.get_optional("ElseFilter"); - if (else_filter) - { - rule.set_else(true); - } - - boost::optional min_scale = - rule_tag.second.get_optional("MinScaleDenominator"); - if (min_scale) - { - rule.set_min_scale(*min_scale); - } - - boost::optional max_scale = - rule_tag.second.get_optional("MaxScaleDenominator"); - if (max_scale) - { - rule.set_max_scale(*max_scale); - } - - ptree::const_iterator symIter = rule_tag.second.begin(); - ptree::const_iterator endSym = rule_tag.second.end(); - - for( ;symIter != endSym; ++symIter) - { - ptree::value_type const& sym = *symIter; - - if ( sym.first == "PointSymbolizer") - { - boost::optional file = - sym.second.get_optional(".file"); - boost::optional type = - sym.second.get_optional(".type"); - boost::optional allow_overlap = - sym.second.get_optional(".allow_overlap"); - - boost::optional width = - sym.second.get_optional(".width"); - boost::optional height = - sym.second.get_optional(".height"); - - if (file && type && width && height) - { - point_symbolizer symbol(*file,*type,*width,*height); - if (allow_overlap && (*allow_overlap == "yes" || *allow_overlap == "true")) - { - symbol.set_allow_overlap(true); // default is 'false' - } - rule.append(symbol); - - } - else - { - rule.append(point_symbolizer()); - } - - } - else if ( sym.first == "LinePatternSymbolizer") - { - std::string file = - sym.second.get(".file"); - std::string type = - sym.second.get(".type"); - unsigned width = - sym.second.get(".width"); - unsigned height = - sym.second.get(".height"); - - rule.append(line_pattern_symbolizer(file,type,width,height)); - - } - else if ( sym.first == "PolygonPatternSymbolizer") - { - std::string file = - sym.second.get(".file"); - std::string type = - sym.second.get(".type"); - unsigned width = - sym.second.get(".width"); - unsigned height = - sym.second.get(".height"); - rule.append(polygon_pattern_symbolizer(file,type,width,height)); - } - else if ( sym.first == "TextSymbolizer") - { - std::string name = - sym.second.get(".name"); - std::string face_name = - sym.second.get(".face_name"); - unsigned size = - sym.second.get(".size",10); - std::string color_str = - sym.second.get(".fill","black"); - Color c = color_factory::from_string(color_str.c_str()); - - text_symbolizer text_symbol(name,face_name, size,c); - - std::string placement_str = - sym.second.get(".placement","point"); - - // displacement - - int dx = sym.second.get(".dx",0); - int dy = sym.second.get(".dy",0); - text_symbol.set_displacement(dx,dy); - - if (placement_str == "line") - { - text_symbol.set_label_placement(line_placement); - } - - // halo fill and radius - boost::optional halo_fill = - sym.second.get_optional(".halo_fill"); - - if (halo_fill) - { - text_symbol.set_halo_fill - (color_factory::from_string(halo_fill->c_str())); - } - boost::optional halo_radius = - sym.second.get_optional(".halo_radius"); - if (halo_radius) - { - text_symbol.set_halo_radius(*halo_radius); - } - - // text ratio and wrap width - boost::optional text_ratio = - sym.second.get_optional(".text_ratio"); - - if (text_ratio) - { - text_symbol.set_text_ratio(*text_ratio); - } - - boost::optional wrap_width = - sym.second.get_optional(".wrap_width"); - if (wrap_width) - { - text_symbol.set_wrap_width(*wrap_width); - } - - // spacing between repeated labels on lines - boost::optional spacing = - sym.second.get_optional(".spacing"); - if (spacing) - { - text_symbol.set_label_spacing(*spacing); - } - - // minimum distance between labels - boost::optional min_distance = - sym.second.get_optional(".min_distance"); - if (min_distance) - { - text_symbol.set_minimum_distance(*min_distance); - } - - // allow_overlap - boost::optional allow_overlap = - sym.second.get_optional(".allow_overlap"); - if (allow_overlap && (*allow_overlap == "yes" || *allow_overlap == "true")) - { - text_symbol.set_allow_overlap(true); // default is 'false' - } - - rule.append(text_symbol); - } - else if ( sym.first == "ShieldSymbolizer") - { - std::string name = - sym.second.get(".name"); - std::string face_name = - sym.second.get(".face_name"); - unsigned size = - sym.second.get(".size",10); - std::string color_str = - sym.second.get(".fill","black"); - Color fill = color_factory::from_string(color_str.c_str()); - - std::string image_file = - sym.second.get(".file"); - std::string type = - sym.second.get(".type"); - unsigned width = - sym.second.get(".width"); - unsigned height = - sym.second.get(".height"); - - shield_symbolizer shield_symbol(name,face_name,size,fill, - image_file,type,width,height); - - // minimum distance between labels - boost::optional min_distance = - sym.second.get_optional(".min_distance"); - if (min_distance) - { - shield_symbol.set_minimum_distance(*min_distance); - } - rule.append(shield_symbol); - } - else if ( sym.first == "LineSymbolizer") - { - stroke strk; - ptree::const_iterator cssIter = sym.second.begin(); - ptree::const_iterator endCss = sym.second.end(); - - for(; cssIter != endCss; ++cssIter) - { - ptree::value_type const& css = * cssIter; - std::string css_name = - css.second.get(".name"); - std::string data = css.second.data(); - if (css_name == "stroke") - { - Color c = color_factory::from_string(css.second.data().c_str()); - strk.set_color(c); - } - else if (css_name == "stroke-width") - { - try - { - float width = lexical_cast(data); - strk.set_width(width); - } - catch (bad_lexical_cast & ex) - { - std::clog << ex.what() << "\n"; - } - } - else if (css_name == "stroke-opacity") - { - try - { - float opacity = lexical_cast(data); - strk.set_opacity(opacity); - } - catch (bad_lexical_cast & ex) - { - std::clog << ex.what() << "\n"; - } - } - else if (css_name == "stroke-linejoin") - { - if ("miter" == data) - { - strk.set_line_join(mapnik::MITER_JOIN); - } - else if ("round" == data) - { - strk.set_line_join(mapnik::ROUND_JOIN); - } - else if ("bevel" == data) - { - strk.set_line_join(mapnik::BEVEL_JOIN); - } - } - else if (css_name == "stroke-linecap") - { - if ("round" == data) - { - strk.set_line_cap(mapnik::ROUND_CAP); - } - else if ("butt" == data) - { - strk.set_line_cap(mapnik::BUTT_CAP); - } - else if ("square" == data) - { - strk.set_line_cap(mapnik::SQUARE_CAP); - } - } - else if (css_name == "stroke-dasharray") - { - tokenizer<> tok (data); - std::vector dash_array; - for (tokenizer<>::iterator itr = tok.begin(); itr != tok.end(); ++itr) - { - try - { - float f = boost::lexical_cast(*itr); - dash_array.push_back(f); - } - catch ( boost::bad_lexical_cast & ex) - { - std::clog << ex.what() << "\n"; - } - } - if (dash_array.size()) - { - size_t size = dash_array.size(); - if ( size % 2) - { - for (size_t i=0; i < size ;++i) - { - dash_array.push_back(dash_array[i]); - } - } - std::vector::const_iterator pos = dash_array.begin(); - while (pos != dash_array.end()) - { - strk.add_dash(*pos,*(pos + 1)); - pos +=2; - } - } - } - } - rule.append(line_symbolizer(strk)); - } - else if ( sym.first == "PolygonSymbolizer") - { - polygon_symbolizer poly_sym; - - ptree::const_iterator cssIter = sym.second.begin(); - ptree::const_iterator endCss = sym.second.end(); - - for(; cssIter != endCss; ++cssIter) - { - ptree::value_type const& css = * cssIter; - - std::string css_name = - css.second.get(".name"); - std::string data = css.second.data(); - if (css_name == "fill") - { - Color c = color_factory::from_string(css.second.data().c_str()); - poly_sym.set_fill(c); - } - else if (css_name == "fill-opacity") - { - try - { - float opacity = lexical_cast(data); - poly_sym.set_opacity(opacity); - } - catch (bad_lexical_cast & ex) - { - std::clog << ex.what() << "\n"; - } - } - } - rule.append(poly_sym); - } - else if ( sym.first == "BuildingSymbolizer") - { - building_symbolizer building_sym; - - ptree::const_iterator cssIter = sym.second.begin(); - ptree::const_iterator endCss = sym.second.end(); - - for(; cssIter != endCss; ++cssIter) - { - ptree::value_type const& css = * cssIter; - - std::string css_name = - css.second.get(".name"); - std::string data = css.second.data(); - if (css_name == "fill") - { - Color c = color_factory::from_string(css.second.data().c_str()); - building_sym.set_fill(c); - } - else if (css_name == "fill-opacity") - { - try - { - float opacity = lexical_cast(data); - building_sym.set_opacity(opacity); - } - catch (bad_lexical_cast & ex) - { - std::clog << ex.what() << "\n"; - } - } - } - rule.append(building_sym); - } - else if ( sym.first == "RasterSymbolizer") - { - rule.append(raster_symbolizer()); - } - } - - style.add_rule(rule); - } + optional bgcolor = get_opt_attr(map_node, "bgcolor"); + if (bgcolor) { + map.set_background( * bgcolor ); } - - map.insert_style(name, style); - + + map.set_srs( get_attr(map_node, "srs", map.srs() )); } - else if (v.first == "Layer") + catch (const config_error & ex) { - - std::string name = v.second.get(".name","Unnamed"); - std::string srs = v.second.get(".srs","+proj=latlong +datum=WGS84"); - - Layer lyr(name, srs); - - boost::optional status = - v.second.get_optional(".status"); - - if (status && *status == "off") + ex.append_context("in node Map"); + throw; + } + + ptree::const_iterator itr = map_node.begin(); + ptree::const_iterator end = map_node.end(); + + for (; itr != end; ++itr) + { + ptree::value_type const& v = *itr; + + if (v.first == "Style") { - lyr.setActive(false); + parse_style( map, v.second ); } - - boost::optional clear_cache = v.second.get_optional(".clear_label_cache"); - if (clear_cache && (*clear_cache == "yes" || *clear_cache == "true")) + else if (v.first == "Layer") { - lyr.set_clear_label_cache(true); + + parse_layer(map, v.second ); } - - - ptree::const_iterator itr2 = v.second.begin(); - ptree::const_iterator end2 = v.second.end(); - - for(; itr2 != end2; ++itr2) + else if (v.first != "" && + v.first != "") { - ptree::value_type const& child = *itr2; - - if (child.first == "StyleName") + throw config_error(std::string("Unknown child node in 'Map'. ") + + "Expected 'Style' or 'Layer' but got '" + v.first + "'"); + } + } + } + catch (const boost::property_tree::ptree_bad_path & ex) + { + throw config_error("Not a map file. Node 'Map' not found."); + } + } + + void map_parser::parse_style( Map & map, ptree const & sty ) + { + string name(""); + try + { + name = get_attr(sty, "name"); + feature_type_style style; + + ptree::const_iterator ruleIter = sty.begin(); + ptree::const_iterator endRule = sty.end(); + + for (; ruleIter!=endRule; ++ruleIter) + { + ptree::value_type const& rule_tag = *ruleIter; + if (rule_tag.first == "Rule") + { + parse_rule( style, rule_tag.second ); + } + else if (rule_tag.first != "" && + rule_tag.first != "" ) + { + throw config_error(std::string("Unknown child node in 'Style'.") + + "Expected 'Rule' but got '" + rule_tag.first + "'"); + } + } + + map.insert_style(name, style); + + } catch (const config_error & ex) { + if ( ! name.empty() ) { + ex.append_context(string("in style '") + name + "'"); + } + throw; + } + } + + void map_parser::parse_layer( Map & map, ptree const & lay ) + { + std::string name; + try + { + name = get_attr(lay, "name", string("Unnamed")); + // XXX if no projection is given inherit from map? [DS] + std::string srs = get_attr(lay, "srs", map.srs()); + + Layer lyr(name, srs); + + optional status = get_opt_attr(lay, "status"); + if (status) + { + lyr.setActive( * status ); + } + + optional clear_cache = + get_opt_attr(lay, "clear_label_cache"); + if (clear_cache) + { + lyr.set_clear_label_cache( * clear_cache ); + } + + + ptree::const_iterator itr2 = lay.begin(); + ptree::const_iterator end2 = lay.end(); + + for(; itr2 != end2; ++itr2) + { + ptree::value_type const& child = *itr2; + + if (child.first == "StyleName") + { + // TODO check references [DS] + lyr.add_style(child.second.data()); + } + else if (child.first == "Datasource") + { + parameters params; + ptree::const_iterator paramIter = child.second.begin(); + ptree::const_iterator endParam = child.second.end(); + for (; paramIter != endParam; ++paramIter) { - lyr.add_style(child.second.data()); - } - else if (child.first == "Datasource") - { - parameters params; - ptree::const_iterator paramIter = child.second.begin(); - ptree::const_iterator endParam = child.second.end(); - for (; paramIter != endParam; ++paramIter) + ptree const& param = paramIter->second; + + if (paramIter->first == "Parameter") { - ptree::value_type const& param_tag=*paramIter; - - if (param_tag.first == "Parameter") - { - std::string name = param_tag.second.get(".name"); - std::string value = param_tag.second.data(); - params[name] = value; - } + std::string name = get_attr(param, "name"); + std::string value = get_own( param, + "datasource parameter"); + params[name] = value; } - //now we're ready to create datasource - boost::shared_ptr ds = datasource_cache::instance()->create(params); + else + { + throw config_error(std::string("Unknown child node in ") + + "'Datasource'. Expected 'Parameter' but got '" + + paramIter->first + "'"); + } + } + //now we're ready to create datasource + try + { + boost::shared_ptr ds = + datasource_cache::instance()->create(params); lyr.set_datasource(ds); } + catch (const mapnik::datasource_exception & ex ) + { + throw config_error( ex.what() ); + } } - map.addLayer(lyr); + else if (child.first != "" && + child.first != "") + { + throw config_error(std::string("Unknown child node in 'Layer'. ") + + "Expected 'StyleName' or 'Datasource' but got '" + + child.first + "'"); + } + } + + map.addLayer(lyr); + + } catch (const config_error & ex) { + if ( ! name.empty() ) { + ex.append_context(string("in layer '") + name + "'"); + } + throw; + } + } + + void map_parser::parse_rule( feature_type_style & style, ptree const & r ) + { + std::string name; + try + { + name = get_attr( r, "name", string()); + std::string title = get_attr( r, "title", string()); + + rule_type rule(name,title); + + optional filter_expr = + get_opt_child( r, "Filter"); + if (filter_expr) + { + rule.set_filter(create_filter(*filter_expr)); + } + + optional else_filter = + get_opt_child(r, "ElseFilter"); + if (else_filter) + { + rule.set_else(true); + } + + optional min_scale = + get_opt_child(r, "MinScaleDenominator"); + if (min_scale) + { + rule.set_min_scale(*min_scale); + } + + optional max_scale = + get_opt_child(r, "MaxScaleDenominator"); + if (max_scale) + { + rule.set_max_scale(*max_scale); + } + + ptree::const_iterator symIter = r.begin(); + ptree::const_iterator endSym = r.end(); + + for( ;symIter != endSym; ++symIter) + { + ptree::value_type const& sym = *symIter; + + if ( sym.first == "PointSymbolizer") + { + parse_point_symbolizer( rule, sym.second ); + } + else if ( sym.first == "LinePatternSymbolizer") + { + parse_line_pattern_symbolizer( rule, sym.second ); + } + else if ( sym.first == "PolygonPatternSymbolizer") + { + parse_polygon_pattern_symbolizer( rule, sym.second ); + } + else if ( sym.first == "TextSymbolizer") + { + parse_text_symbolizer( rule, sym.second ); + } + else if ( sym.first == "ShieldSymbolizer") + { + parse_shield_symbolizer( rule, sym.second ); + } + else if ( sym.first == "LineSymbolizer") + { + parse_line_symbolizer( rule, sym.second ); + } + else if ( sym.first == "PolygonSymbolizer") + { + parse_polygon_symbolizer( rule, sym.second ); + } + else if ( sym.first == "BuildingSymbolizer") + { + parse_building_symbolizer( rule, sym.second ); + } + else if ( sym.first == "RasterSymbolizer") + { + rule.append(raster_symbolizer()); + } + else if ( sym.first != "MinScaleDenominator" && + sym.first != "MaxScaleDenominator" && + sym.first != "Filter" && + sym.first != "ElseFilter" && + sym.first != "" && + sym.first != "" ) + { + throw config_error(std::string("Unknown symbolizer '") + + sym.first + "'"); + } + } + + style.add_rule(rule); + + } + catch (const config_error & ex) + { + if ( ! name.empty() ) + { + ex.append_context(string("in rule '") + name + "'"); + } + throw; + } + } + + void map_parser::parse_point_symbolizer( rule_type & rule, ptree const & sym ) + { + try + { + optional file = get_opt_attr(sym, "file"); + optional type = get_opt_attr(sym, "type"); + optional allow_overlap = + get_opt_attr(sym, "allow_overlap"); + + optional width = get_opt_attr(sym, "width"); + optional height = get_opt_attr(sym, "height"); + + if (file && type && width && height) + { + try + { + point_symbolizer symbol(*file,*type,*width,*height); + if (allow_overlap) + { + symbol.set_allow_overlap( * allow_overlap ); + } + rule.append(symbol); + } + catch (ImageReaderException const & ex ) + { + string msg("Failed to load image file '" + * file + + "': " + ex.what()); + if (strict_) + { + throw config_error(msg); + } + else + { + clog << "### WARNING: " << msg << endl; + } + } + + } + else if (file || type || width || height) + { + std::ostringstream os; + os << "Missing required attributes: "; + if ( ! file ) os << "file "; + if ( ! type ) os << "type "; + if ( ! width ) os << "width "; + if ( ! height ) os << "height "; + throw config_error( os.str() ); + } + else + { + rule.append(point_symbolizer()); } } - } -} + catch (const config_error & ex) + { + ex.append_context("in PointSymbolizer"); + throw; + } + } + + void map_parser::parse_line_pattern_symbolizer( rule_type & rule, ptree const & sym ) + { + try + { + std::string file = get_attr(sym, "file"); + std::string type = get_attr(sym, "type"); + unsigned width = get_attr(sym, "width"); + unsigned height = get_attr(sym, "height"); + + try + { + rule.append(line_pattern_symbolizer(file,type,width,height)); + } + catch (ImageReaderException const & ex ) + { + string msg("Failed to load image file '" + file + + "': " + ex.what()); + if (strict_) + { + throw config_error(msg); + } + else + { + clog << "### WARNING: " << msg << endl; + } + } + } + catch (const config_error & ex) + { + ex.append_context("in LinePatternSymbolizer"); + throw; + } + } + + void map_parser::parse_polygon_pattern_symbolizer( rule_type & rule, + ptree const & sym ) + { + try + { + std::string file = get_attr(sym, "file"); + std::string type = get_attr(sym, "type"); + unsigned width = get_attr(sym, "width"); + unsigned height = get_attr(sym, "height"); + + try + { + rule.append(polygon_pattern_symbolizer(file,type,width,height)); + } + catch (ImageReaderException const & ex ) + { + string msg("Failed to load image file '" + file + + "': " + ex.what()); + if (strict_) + { + throw config_error(msg); + } + else + { + clog << "### WARNING: " << msg << endl; + } + } + } + catch (const config_error & ex) + { + ex.append_context("in PolygonPatternSymbolizer"); + throw; + } + } + + void map_parser::parse_text_symbolizer( rule_type & rule, ptree const & sym ) + { + try + { + std::string name = get_attr(sym, "name"); + std::string face_name = get_attr(sym, "face_name"); + unsigned size = get_attr(sym, "size", 10U ); + + Color c = get_attr(sym, "fill", Color(0,0,0)); + + text_symbolizer text_symbol(name, face_name, size, c); + + int dx = get_attr(sym, "dx", 0); + int dy = get_attr(sym, "dy", 0); + text_symbol.set_displacement(dx,dy); + + label_placement_e placement = + get_attr(sym, "placement", POINT_PLACEMENT); + text_symbol.set_label_placement( placement ); + + // halo fill and radius + optional halo_fill = get_opt_attr(sym, "halo_fill"); + if (halo_fill) + { + text_symbol.set_halo_fill( * halo_fill ); + } + optional halo_radius = + get_opt_attr(sym, "halo_radius"); + if (halo_radius) + { + text_symbol.set_halo_radius(*halo_radius); + } + + // text ratio and wrap width + optional text_ratio = + get_opt_attr(sym, "text_ratio"); + + optional wrap_width = + get_opt_attr(sym, "wrap_width"); + if (wrap_width) + { + text_symbol.set_wrap_width(*wrap_width); + } + + // spacing between repeated labels on lines + optional spacing = get_opt_attr(sym, "spacing"); + if (spacing) + { + text_symbol.set_label_spacing(*spacing); + } + + // minimum distance between labels + optional min_distance = + get_opt_attr(sym, "min_distance"); + if (min_distance) + { + text_symbol.set_minimum_distance(*min_distance); + } + + // allow_overlap + optional allow_overlap = + get_opt_attr(sym, "allow_overlap"); + if (allow_overlap) + { + text_symbol.set_allow_overlap( * allow_overlap ); + } + + if ( strict_ ) + { + ensure_font_face( text_symbol ); + } + + rule.append(text_symbol); + } + catch (const config_error & ex) + { + ex.append_context("in TextSymbolizer"); + throw; + } + } + + void map_parser::parse_shield_symbolizer( rule_type & rule, ptree const & sym ) + { + try + { + std::string name = get_attr(sym, "name"); + std::string face_name = get_attr(sym, "face_name"); + unsigned size = get_attr(sym, "size", 10U); + Color fill = get_attr(sym, "fill", Color(0,0,0)); + + std::string image_file = get_attr(sym, "file"); + std::string type = get_attr(sym, "type"); + unsigned width = get_attr(sym, "width"); + unsigned height = get_attr(sym, "height"); + + try + { + shield_symbolizer shield_symbol(name,face_name,size,fill, + image_file,type,width,height); + + // minimum distance between labels + optional min_distance = + get_opt_attr(sym, "min_distance"); + if (min_distance) + { + shield_symbol.set_minimum_distance(*min_distance); + } + rule.append(shield_symbol); + } + catch (ImageReaderException const & ex ) + { + string msg("Failed to load image file '" + image_file + + "': " + ex.what()); + if (strict_) + { + throw config_error(msg); + } + else + { + clog << "### WARNING: " << msg << endl; + } + } + + } + catch (const config_error & ex) + { + ex.append_context("in ShieldSymbolizer"); + throw; + } + } + + void map_parser::parse_line_symbolizer( rule_type & rule, ptree const & sym ) + { + try + { + stroke strk; + ptree::const_iterator cssIter = sym.begin(); + ptree::const_iterator endCss = sym.end(); + + for(; cssIter != endCss; ++cssIter) + { + ptree const & css = cssIter->second; + std::string css_name = get_attr(css, "name"); + if (css_name == "stroke") + { + Color c = get_css(css, css_name); + strk.set_color(c); + } + else if (css_name == "stroke-width") + { + float width = get_css(css, css_name); + strk.set_width(width); + } + else if (css_name == "stroke-opacity") + { + float opacity = get_css(css, css_name); + strk.set_opacity(opacity); + } + else if (css_name == "stroke-linejoin") + { + line_join_e line_join = get_css(css, css_name); + strk.set_line_join( line_join ); + } + else if (css_name == "stroke-linecap") + { + line_cap_e line_cap = get_css(css, css_name); + strk.set_line_cap( line_cap ); + } + else if (css_name == "stroke-dasharray") + { + tokenizer<> tok ( css.data() ); + std::vector dash_array; + tokenizer<>::iterator itr = tok.begin(); + for (; itr != tok.end(); ++itr) + { + try + { + float f = boost::lexical_cast(*itr); + dash_array.push_back(f); + } + catch ( boost::bad_lexical_cast & ex) + { + throw config_error(std::string("Failed to parse CSS ") + + "parameter '" + css_name + "'. Expected a " + + "list of floats but got '" + css.data() + "'"); + } + } + if (dash_array.size()) + { + size_t size = dash_array.size(); + if ( size % 2) + { + for (size_t i=0; i < size ;++i) + { + dash_array.push_back(dash_array[i]); + } + } + std::vector::const_iterator pos = dash_array.begin(); + while (pos != dash_array.end()) + { + strk.add_dash(*pos,*(pos + 1)); + pos +=2; + } + } + } + } + rule.append(line_symbolizer(strk)); + } + catch (const config_error & ex) + { + ex.append_context("in LineSymbolizer"); + throw; + } + } + + + void map_parser::parse_polygon_symbolizer( rule_type & rule, ptree const & sym ) + { + try + { + polygon_symbolizer poly_sym; + + ptree::const_iterator cssIter = sym.begin(); + ptree::const_iterator endCss = sym.end(); + + for(; cssIter != endCss; ++cssIter) + { + ptree const & css = cssIter->second; + std::string css_name = get_attr(css, "name"); + if (css_name == "fill") + { + Color c = get_css(css, css_name); + poly_sym.set_fill(c); + } + else if (css_name == "fill-opacity") + { + float opacity = get_css(css, css_name); + poly_sym.set_opacity(opacity); + } + } + rule.append(poly_sym); + + } + catch (const config_error & ex) + { + ex.append_context("in PolygonSymbolizer"); + throw; + } + } + + + void map_parser::parse_building_symbolizer( rule_type & rule, ptree const & sym ) + { + try { + building_symbolizer building_sym; + + ptree::const_iterator cssIter = sym.begin(); + ptree::const_iterator endCss = sym.end(); + + for(; cssIter != endCss; ++cssIter) + { + ptree const& css = cssIter->second; + + std::string css_name = get_attr(css, "name"); + std::string data = css.data(); + if (css_name == "fill") + { + Color c = get_css(css, css_name); + building_sym.set_fill(c); + } + else if (css_name == "fill-opacity") + { + float opacity = get_css(css, css_name); + building_sym.set_opacity(opacity); + } + } + rule.append(building_sym); + } + catch (const config_error & ex) + { + ex.append_context("in BuildingSymbolizer"); + throw; + } + } + + void map_parser::ensure_font_face( const text_symbolizer & text_symbol ) + { + if ( ! font_manager_.get_face( text_symbol.get_face_name() ) ) + { + throw config_error("Failed to find font face '" + + text_symbol.get_face_name() + "'"); + } + } +} // end of namespace mapnik diff --git a/src/placement_finder.cpp b/src/placement_finder.cpp index 6d9c1243d..9c4c5848c 100644 --- a/src/placement_finder.cpp +++ b/src/placement_finder.cpp @@ -70,8 +70,8 @@ namespace mapnik minimum_distance(sym.get_minimum_distance()), avoid_edges(sym.get_avoid_edges()), has_dimensions(true), - dimensions(std::make_pair(sym.get_background_image()->width(), - sym.get_background_image()->height())) + dimensions(std::make_pair(sym.get_image()->width(), + sym.get_image()->height())) { } @@ -205,16 +205,16 @@ namespace mapnik double distance = p->get_total_distance(); - if (p->label_placement == line_placement && string_width > distance) + if (p->label_placement == LINE_PLACEMENT && string_width > distance) { //Empty! return ideal_label_distances; } int num_labels = 0; - if (p->label_spacing && p->label_placement == line_placement) + if (p->label_spacing && p->label_placement == LINE_PLACEMENT) num_labels = static_cast (floor(distance / (p->label_spacing + string_width))); - else if (p->label_spacing && p->label_placement == point_placement) + else if (p->label_spacing && p->label_placement == POINT_PLACEMENT) num_labels = static_cast (floor(distance / p->label_spacing)); if (p->force_odd_labels && num_labels%2 == 0) @@ -225,7 +225,7 @@ namespace mapnik double ideal_spacing = distance/num_labels; double middle; //try draw text centered - if (p->label_placement == line_placement) + if (p->label_placement == LINE_PLACEMENT) middle = (distance / 2.0) - (string_width/2.0); else // (p->label_placement == point_placement) middle = distance / 2.0; @@ -289,9 +289,9 @@ namespace mapnik } p->clear_envelopes(); // check position +- delta for valid placement - if ((p->label_placement == line_placement && + if ((p->label_placement == LINE_PLACEMENT && build_path_follow(p, *itr + (i*s)) ) || - (p->label_placement == point_placement && + (p->label_placement == POINT_PLACEMENT && build_path_horizontal(p, *itr + (i*s))) ) { update_detector(p); diff --git a/src/png_reader.cpp b/src/png_reader.cpp index 1044e8d0d..c1390666a 100644 --- a/src/png_reader.cpp +++ b/src/png_reader.cpp @@ -68,15 +68,7 @@ namespace mapnik bit_depth_(0), color_type_(0) { - try - { - init(); - } - catch (const ImageReaderException& e) - { - std::clog << e.what() << '\n'; - throw; - } + init(); } PngReader::~PngReader() {} diff --git a/src/point_symbolizer.cpp b/src/point_symbolizer.cpp index e5e9cd84d..5b1c6167b 100644 --- a/src/point_symbolizer.cpp +++ b/src/point_symbolizer.cpp @@ -33,48 +33,25 @@ namespace mapnik { point_symbolizer::point_symbolizer() - : symbol_(new ImageData32(4,4)), + : symbolizer_with_image(boost::shared_ptr(new ImageData32(4,4))), overlap_(false) { //default point symbol is black 4x4px square - symbol_->set(0xff000000); + image_->set(0xff000000); } point_symbolizer::point_symbolizer(std::string const& file, std::string const& type, unsigned width,unsigned height) - : symbol_(new ImageData32(width,height)), + : symbolizer_with_image(file, type, width, height), overlap_(false) - { - try - { - boost::scoped_ptr reader(get_image_reader(type,file)); - if (reader.get()) - { - reader->read(0,0,*symbol_); - } - } - catch (...) - { - std::clog<<"exception caught..." << std::endl; - } - } + { } point_symbolizer::point_symbolizer(point_symbolizer const& rhs) - : symbol_(rhs.symbol_), + : symbolizer_with_image(rhs), overlap_(rhs.overlap_) {} - void point_symbolizer::set_data( boost::shared_ptr symbol) - { - symbol_ = symbol; - } - - boost::shared_ptr const& point_symbolizer::get_data() const - { - return symbol_; - } - void point_symbolizer::set_allow_overlap(bool overlap) { overlap_ = overlap; diff --git a/src/polygon_pattern_symbolizer.cpp b/src/polygon_pattern_symbolizer.cpp index 7c03612b3..3ab5d8c2e 100644 --- a/src/polygon_pattern_symbolizer.cpp +++ b/src/polygon_pattern_symbolizer.cpp @@ -31,24 +31,11 @@ namespace mapnik polygon_pattern_symbolizer::polygon_pattern_symbolizer(std::string const& file, std::string const& type, unsigned width,unsigned height) - : pattern_(new ImageData32(width,height)) + : symbolizer_with_image( file, type, width, height ) { - try - { - std::auto_ptr reader(get_image_reader(type,file)); - if (reader.get()) - reader->read(0,0,*pattern_); - } - catch (...) - { - std::clog << "exception caught...\n"; - } } polygon_pattern_symbolizer::polygon_pattern_symbolizer(polygon_pattern_symbolizer const& rhs) - : pattern_(rhs.pattern_) {} + : symbolizer_with_image(rhs) {} - ImageData32 const& polygon_pattern_symbolizer::get_pattern() const - { - return *pattern_; - } } + diff --git a/src/save_map.cpp b/src/save_map.cpp index 7302d944e..527bbe7e2 100644 --- a/src/save_map.cpp +++ b/src/save_map.cpp @@ -26,18 +26,394 @@ #include #include #include +#include #include #include // mapnik #include +#include namespace mapnik { - void save_map(Map & map, std::string const& filename) + using boost::property_tree::ptree; + using boost::optional; + + std::string guess_type( const std::string & filename ) + { + std::string::size_type idx = filename.find_last_of("."); + if ( idx != std::string::npos ) { + return filename.substr( idx + 1 ); + } + return ""; + } + class serialize_symbolizer : public boost::static_visitor<> + { + public: + serialize_symbolizer( ptree & r ) : rule_(r) {} + + void operator () ( const point_symbolizer & sym ) + { + ptree & sym_node = rule_.push_back( + ptree::value_type("PointSymbolizer", ptree()))->second; + + add_image_attributes( sym_node, sym ); + } + + void operator () ( const line_symbolizer & sym ) + { + ptree & sym_node = rule_.push_back( + ptree::value_type("LineSymbolizer", ptree()))->second; + const stroke & strk = sym.get_stroke(); + stroke dfl = stroke(); + + if ( strk.get_color() != dfl.get_color() ) + { + set_css( sym_node, "stroke", strk.get_color() ); + } + if ( strk.get_width() != dfl.get_width() ) + { + set_css( sym_node, "stroke-width", strk.get_width() ); + } + if ( strk.get_opacity() != dfl.get_opacity() ) + { + set_css( sym_node, "stroke-opacity", strk.get_opacity() ); + } + if ( strk.get_line_join() != dfl.get_line_join() ) + { + set_css( sym_node, "stroke-linejoin", strk.get_line_join() ); + } + if ( strk.get_line_cap() != dfl.get_line_cap() ) + { + set_css( sym_node, "stroke-linecap", strk.get_line_cap() ); + } + if ( ! strk.get_dash_array().empty() ) + { + std::ostringstream os; + const dash_array & dashes = strk.get_dash_array(); + for (unsigned i = 0; i < dashes.size(); ++i) { + os << dashes[i].first << ", " << dashes[i].second; + if ( i + 1 < dashes.size() ) os << ", "; + } + set_css( sym_node, "stroke-dasharray", os.str() ); + } + } + + void operator () ( const line_pattern_symbolizer & sym ) + { + ptree & sym_node = rule_.push_back( + ptree::value_type("LinePatternSymbolizer", + ptree()))->second; + + add_image_attributes( sym_node, sym ); + } + + void operator () ( const polygon_symbolizer & sym ) + { + ptree & sym_node = rule_.push_back( + ptree::value_type("PolygonSymbolizer", ptree()))->second; + polygon_symbolizer dfl; + + if ( sym.get_fill() != dfl.get_fill() ) + { + set_css( sym_node, "fill", sym.get_fill() ); + } + if ( sym.get_opacity() != dfl.get_opacity() ) + { + set_css( sym_node, "opacity", sym.get_opacity() ); + } + } + + void operator () ( const polygon_pattern_symbolizer & sym ) + { + ptree & sym_node = rule_.push_back( + ptree::value_type("PolygonPatternSymbolizer", + ptree()))->second; + + add_image_attributes( sym_node, sym ); + } + + void operator () ( const raster_symbolizer & sym ) + { + rule_.push_back( + ptree::value_type("RasterSymbolizer", ptree())); + } + + void operator () ( const shield_symbolizer & sym ) + { + ptree & sym_node = rule_.push_back( + ptree::value_type("ShieldSymbolizer", + ptree()))->second; + + add_font_attributes( sym_node, sym); + add_image_attributes( sym_node, sym); + } + + void operator () ( const text_symbolizer & sym ) + { + ptree & sym_node = rule_.push_back( + ptree::value_type("TextSymbolizer", + ptree()))->second; + + add_font_attributes( sym_node, sym); + + } + + void operator () ( const building_symbolizer & sym ) + { + ptree & sym_node = rule_.push_back( + ptree::value_type("BuildingSymbolizer", ptree()))->second; + building_symbolizer dfl; + + if ( sym.get_fill() != dfl.get_fill() ) + { + set_css( sym_node, "fill", sym.get_fill() ); + } + if ( sym.get_opacity() != dfl.get_opacity() ) + { + set_css( sym_node, "fill-opacity", sym.get_opacity() ); + } + } + private: + serialize_symbolizer(); + void add_image_attributes(ptree & node, const symbolizer_with_image & sym) + { + const std::string & filename = sym.get_filename(); + if ( ! filename.empty() ) { + set_attr( node, "file", filename ); + set_attr( node, "type", guess_type( filename ) ); + + boost::shared_ptr img = sym.get_image(); + if ( img ) + { + if ( img->width() > 0) + { + set_attr( node, "width", img->width() ); + } + if ( img->height() > 0) + { + set_attr( node, "height", img->height() ); + } + } + + } + } + void add_font_attributes(ptree & node, const text_symbolizer & sym) + { + const std::string & name = sym.get_name(); + if ( ! name.empty() ) { + set_attr( node, "name", name ); + } + const std::string & face_name = sym.get_face_name(); + if ( ! face_name.empty() ) { + set_attr( node, "face_name", face_name ); + } + + set_attr( node, "size", sym.get_text_size() ); + set_attr( node, "fill", sym.get_fill() ); + + // pseudo-default-construct a text_symbolizer. It is used + // to avoid printing ofattributes with default values without + // repeating the default values here. + // maybe add a real, explicit default-ctor? + text_symbolizer dfl("", "", + 0, Color(0,0,0) ); + + position displacement = sym.get_displacement(); + if ( displacement.get<0>() != dfl.get_displacement().get<0>() ) + { + set_attr( node, "dx", displacement.get<0>() ); + } + if ( displacement.get<1>() != dfl.get_displacement().get<1>() ) + { + set_attr( node, "dy", displacement.get<1>() ); + } + + if (sym.get_label_placement() != dfl.get_label_placement() ) + { + set_attr( node, "placement", sym.get_label_placement() ); + } + if (sym.get_halo_radius() != dfl.get_halo_radius()) + { + set_attr( node, "halo_radius", sym.get_halo_radius() ); + } + const Color & c = sym.get_halo_fill(); + if ( c != dfl.get_halo_fill() ) + { + set_attr( node, "halo_fill", c ); + } + if (sym.get_text_ratio() != dfl.get_text_ratio() ) + { + set_attr( node, "text_ratio", sym.get_text_ratio() ); + } + if (sym.get_wrap_width() != dfl.get_wrap_width()) + { + set_attr( node, "wrap_width", sym.get_wrap_width() ); + } + if (sym.get_label_spacing() != dfl.get_label_spacing()) + { + set_attr( node, "spacing", sym.get_label_spacing() ); + } + if (sym.get_minimum_distance() != dfl.get_minimum_distance()) + { + set_attr( node, "min_distance", sym.get_minimum_distance() ); + } + if (sym.get_allow_overlap() != dfl.get_allow_overlap() ) + { + set_attr( node, "allow_overlap", sym.get_allow_overlap() ); + } + } + ptree & rule_; + }; + + void serialize_rule( ptree & style_node, const rule_type & rule) + { + ptree & rule_node = style_node.push_back( + ptree::value_type("Rule", ptree() ))->second; + + rule_type dfl; + if ( rule.get_name() != dfl.get_name() ) + { + set_attr(rule_node, "name", rule.get_name()); + } + if ( rule.get_title() != dfl.get_title() ) + { + set_attr(rule_node, "title", rule.get_title()); + } + + if ( rule.has_else_filter() ) + { + rule_node.push_back( ptree::value_type( + "ElseFilter", ptree())); + } + else + { + // filters are not comparable, so compare strings for now + std::string filter = rule.get_filter()->to_string(); + std::string default_filter = dfl.get_filter()->to_string(); + if ( filter != default_filter) + { + rule_node.push_back( ptree::value_type( + "Filter", ptree()))->second.put_own( filter ); + } + } + + if (rule.get_min_scale() != dfl.get_min_scale()) + { + ptree & min_scale = rule_node.push_back( ptree::value_type( + "MinScaleDenominator", ptree()))->second; + min_scale.put_own( rule.get_min_scale() ); + } + + if (rule.get_max_scale() != dfl.get_max_scale() ) + { + ptree & max_scale = rule_node.push_back( ptree::value_type( + "MaxScaleDenominator", ptree()))->second; + max_scale.put_own( rule.get_max_scale() ); + } + + symbolizers::const_iterator begin = rule.get_symbolizers().begin(); + symbolizers::const_iterator end = rule.get_symbolizers().end(); + serialize_symbolizer serializer( rule_node ); + std::for_each( begin, end , boost::apply_visitor( serializer )); + } + + void serialize_style( ptree & map_node, Map::const_style_iterator style_it ) + { + const feature_type_style & style = style_it->second; + const std::string & name = style_it->first; + + ptree & style_node = map_node.push_back( + ptree::value_type("Style", ptree()))->second; + + set_attr(style_node, "name", name); + + rules::const_iterator it = style.get_rules().begin(); + rules::const_iterator end = style.get_rules().end(); + for (; it != end; ++it) + { + serialize_rule( style_node, * it ); + } + + } + + void serialize_datasource( ptree & layer_node, datasource_ptr datasource) + { + ptree & datasource_node = layer_node.push_back( + ptree::value_type("Datasource", ptree()))->second; + + parameters::const_iterator it = datasource->params().begin(); + parameters::const_iterator end = datasource->params().end(); + for (; it != end; ++it) + { + boost::property_tree::ptree & param_node = datasource_node.push_back( + boost::property_tree::ptree::value_type("Parameter", + boost::property_tree::ptree()))->second; + param_node.put(".name", it->first ); + param_node.put_own( it->second ); + + } + } + + void serialize_layer( ptree & map_node, const Layer & layer ) + { + ptree & layer_node = map_node.push_back( + ptree::value_type("Layer", ptree()))->second; + if ( layer.name() != "" ) + { + set_attr( layer_node, "name", layer.name() ); + } + if ( layer.srs() != "" ) + { + set_attr( layer_node, "srs", layer.srs() ); + } + set_attr/**/( layer_node, "status", layer.isActive() ); + set_attr/**/( layer_node, "clear_label_cache", layer.clear_label_cache() ); + + std::vector const& style_names = layer.styles(); + for (unsigned i = 0; i < style_names.size(); ++i) + { + boost::property_tree::ptree & style_node = layer_node.push_back( + boost::property_tree::ptree::value_type("StyleName", + boost::property_tree::ptree()))->second; + style_node.put_own( style_names[i] ); + } + + datasource_ptr datasource = layer.datasource(); + if ( datasource ) + { + serialize_datasource( layer_node, datasource ); + } + } + + void save_map(Map const & map, std::string const& filename) { - using boost::property_tree::ptree; ptree pt; - // TODO + + ptree & map_node = pt.push_back(ptree::value_type("Map", ptree() ))->second; + + set_attr( map_node, "srs", map.srs() ); + + optional c = map.background(); + if ( c ) + { + set_attr( map_node, "bgcolor", * c ); + } + + Map::const_style_iterator it = map.styles().begin(); + Map::const_style_iterator end = map.styles().end(); + for (; it != end; ++it) + { + serialize_style( map_node, it); + } + + std::vector const & layers = map.layers(); + for (unsigned i = 0; i < layers.size(); ++i ) + { + serialize_layer( map_node, layers[i] ); + } + + write_xml(filename,pt); } + } diff --git a/src/shield_symbolizer.cpp b/src/shield_symbolizer.cpp index 1096ff1e8..6c2bae2d5 100644 --- a/src/shield_symbolizer.cpp +++ b/src/shield_symbolizer.cpp @@ -43,30 +43,8 @@ namespace mapnik std::string const& type, unsigned width,unsigned height) : text_symbolizer(name, face_name, size, fill), - background_image_(new ImageData32(width, height)) + symbolizer_with_image( file, type, width, height ) { - try - { - boost::scoped_ptr reader(get_image_reader(type,file)); - if (reader.get()) - { - reader->read(0,0,*background_image_); - } - } - catch (...) - { - std::clog << "exception caught..." << std::endl; - } - } - - void shield_symbolizer::set_background_image(boost::shared_ptr background_image) - { - background_image_ = background_image; - } - - boost::shared_ptr const& shield_symbolizer::get_background_image() const - { - return background_image_; } } diff --git a/src/stroke.cpp b/src/stroke.cpp new file mode 100644 index 000000000..eff9a782f --- /dev/null +++ b/src/stroke.cpp @@ -0,0 +1,153 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2006 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 + * + *****************************************************************************/ + +#include + +static const char * line_cap_strings[] = { + "butt", + "square", + "round", + "" +}; + +IMPLEMENT_ENUM( mapnik::line_cap_e, line_cap_strings ); + +static const char * line_join_strings[] = { + "miter", + "miter_revert", + "round", + "bevel", + "" +}; + +IMPLEMENT_ENUM( mapnik::line_join_e, line_join_strings ); + +namespace mapnik +{ + + + stroke::stroke() + : c_(0,0,0), + width_(1.0), + opacity_(1.0), + line_cap_(BUTT_CAP), + line_join_(MITER_JOIN), + dash_() {} + + stroke::stroke(Color const& c, float width) + : c_(c), + width_(width), + opacity_(1.0), + line_cap_(BUTT_CAP), + line_join_(MITER_JOIN), + dash_() {} + + stroke::stroke(stroke const& other) + : c_(other.c_), + width_(other.width_), + opacity_(other.opacity_), + line_cap_(other.line_cap_), + line_join_(other.line_join_), + dash_(other.dash_) {} + + stroke & stroke::operator=(const stroke& rhs) + { + stroke tmp(rhs); + swap(tmp); + return *this; + } + + void stroke::set_color(const Color& c) + { + c_=c; + } + + Color const& stroke::get_color() const + { + return c_; + } + + float stroke::get_width() const + { + return width_; + } + void stroke::set_width(float w) + { + width_=w; + } + + void stroke::set_opacity(float opacity) + { + if (opacity > 1.0) opacity_=1.0; + else if (opacity < 0.0) opacity_=0.0; + else opacity_=opacity; + } + + float stroke::get_opacity() const + { + return opacity_; + } + + void stroke::set_line_cap(line_cap_e line_cap) + { + line_cap_=line_cap; + } + + line_cap_e stroke::get_line_cap() const + { + return line_cap_; + } + + void stroke::set_line_join(line_join_e line_join) + { + line_join_=line_join; + } + + line_join_e stroke::get_line_join() const + { + return line_join_; + } + + void stroke::add_dash(float dash,float gap) + { + dash_.push_back(std::make_pair(dash,gap)); + } + bool stroke::has_dash() const + { + return ! dash_.empty(); + } + + dash_array const& stroke::get_dash_array() const + { + return dash_; + } + + void stroke::swap(const stroke& other) throw() + { + c_=other.c_; + width_=other.width_; + opacity_=other.opacity_; + line_cap_=other.line_cap_; + line_join_=other.line_join_; + dash_ = other.dash_; + } +} diff --git a/src/symbolizer.cpp b/src/symbolizer.cpp new file mode 100644 index 000000000..c99f1f9c7 --- /dev/null +++ b/src/symbolizer.cpp @@ -0,0 +1,66 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2006 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 + * + *****************************************************************************/ +//$Id$ + +#include + +#include + +#include + +namespace mapnik { + + symbolizer_with_image::symbolizer_with_image(boost::shared_ptr img) : + image_( img ) {} + + symbolizer_with_image::symbolizer_with_image(std::string const& file, + std::string const& type, unsigned width,unsigned height) + : image_(new ImageData32(width,height)), + image_filename_( file ) + { + std::auto_ptr reader(get_image_reader(type,file)); + if (reader.get()) + reader->read(0,0,*image_); + } + + + symbolizer_with_image::symbolizer_with_image( symbolizer_with_image const& rhs) + : image_(rhs.image_), image_filename_(rhs.image_filename_) {} + + + boost::shared_ptr symbolizer_with_image::get_image() const + { + return image_; + } + void symbolizer_with_image::set_image(boost::shared_ptr symbol) + { + image_ = symbol; + } + const std::string & symbolizer_with_image::get_filename() const + { + return image_filename_; + } + +} // end of namespace mapnik + + + diff --git a/src/text_symbolizer.cpp b/src/text_symbolizer.cpp index 1b0f6977f..3f6a6e5d6 100644 --- a/src/text_symbolizer.cpp +++ b/src/text_symbolizer.cpp @@ -29,6 +29,14 @@ //mapnik #include +static const char * label_placement_strings[] = { + "point", + "line", + "" +}; + +IMPLEMENT_ENUM( mapnik::label_placement_e, label_placement_strings ); + namespace mapnik { text_symbolizer::text_symbolizer(std::string const& name, std::string const& face_name, unsigned size,Color const& fill) @@ -44,7 +52,7 @@ namespace mapnik fill_(fill), halo_fill_(Color(255,255,255)), halo_radius_(0), - label_p_(point_placement), + label_p_(POINT_PLACEMENT), anchor_(0.0,0.5), displacement_(0.0,0.0), avoid_edges_(false), diff --git a/src/tiff_reader.cpp b/src/tiff_reader.cpp index 7290ef9ab..ced3f4615 100644 --- a/src/tiff_reader.cpp +++ b/src/tiff_reader.cpp @@ -88,21 +88,14 @@ namespace mapnik tile_width_(0), tile_height_(0) { - try - { - init(); - } - catch (ImageReaderException& ex) - { - std::clog<