From bb235fa3168b90e33d394f5902c8af84b5c4186a Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Mon, 16 Oct 2006 13:44:52 +0000 Subject: [PATCH] 1.added projection transformation support based on proj4 (new dependency!!!) Map and Layer objects both have a new parameter 'srs', initialized to "+proj=latlong +datum=WGS84" by default. Basic usage (Python): p = Projection("+proj=merc +datum=WGS84") point = p.forward(Coord(-2,51)) ... 2.reflected arithmetic operators for Envelope/Coord into Python 3.altered return policies for python objects 4.modified build system to require proj4 lib and headers --- SConstruct | 26 ++- agg/SConscript | 2 +- bindings/python/mapnik/__init__.py | 12 +- bindings/python/mapnik_coord.cpp | 45 +++++ bindings/python/mapnik_datasource.cpp | 68 +++++++ bindings/python/mapnik_envelope.cpp | 7 +- bindings/python/mapnik_layer.cpp | 13 +- bindings/python/mapnik_line_symbolizer.cpp | 2 +- bindings/python/mapnik_map.cpp | 8 +- bindings/python/mapnik_polygon_symbolizer.cpp | 2 +- bindings/python/mapnik_projection.cpp | 64 +++++++ bindings/python/mapnik_python.cpp | 46 +---- bindings/python/mapnik_stroke.cpp | 2 +- demo/python/rundemo.py | 2 +- include/mapnik/agg_renderer.hpp | 28 ++- include/mapnik/ctrans.hpp | 32 ++++ include/mapnik/feature_style_processor.hpp | 117 ++++++++---- include/mapnik/layer.hpp | 6 +- include/mapnik/map.hpp | 8 +- include/mapnik/projection.hpp | 174 ++++++++++++++++++ src/agg_renderer.cpp | 53 ++++-- src/layer.cpp | 16 +- src/load_map.cpp | 18 +- src/map.cpp | 86 ++++++--- 24 files changed, 670 insertions(+), 167 deletions(-) create mode 100644 bindings/python/mapnik_coord.cpp create mode 100644 bindings/python/mapnik_datasource.cpp create mode 100644 bindings/python/mapnik_projection.cpp create mode 100644 include/mapnik/projection.hpp diff --git a/SConstruct b/SConstruct index a6446b88c..bf06e18a4 100644 --- a/SConstruct +++ b/SConstruct @@ -45,7 +45,6 @@ opts.Add(PathOption('PROJ_LIBS', 'Search path for PROJ.4 include files', '/usr/l opts.Add(PathOption('PYTHON','Python executable', sys.executable)) opts.Add(ListOption('INPUT_PLUGINS','Input drivers to include','all',['postgis','shape','raster'])) opts.Add(ListOption('BINDINGS','Language bindings to build','all',['python'])) - opts.Add('DEBUG', 'Compile a debug version of mapnik', '') env = Environment(ENV=os.environ, options=opts) @@ -59,14 +58,24 @@ conf = Configure(env) env['CPPPATH'] = ['#agg/include', '#include', '#'] -for path in [env['BOOST_INCLUDES'], env['PNG_INCLUDES'], env['JPEG_INCLUDES'], env['TIFF_INCLUDES'], env['PGSQL_INCLUDES'], env['PROJ_INCLUDES']]: +for path in [env['BOOST_INCLUDES'], + env['PNG_INCLUDES'], + env['JPEG_INCLUDES'], + env['TIFF_INCLUDES'], + env['PGSQL_INCLUDES'], + env['PROJ_INCLUDES']]: if path not in env['CPPPATH']: env['CPPPATH'].append(path) env['LIBPATH'] = ['#agg', '#src'] -for path in [env['BOOST_LIBS'], env['PNG_LIBS'], env['JPEG_LIBS'], env['TIFF_LIBS'], env['PGSQL_LIBS'], env['PROJ_LIBS']]: +for path in [env['BOOST_LIBS'], + env['PNG_LIBS'], + env['JPEG_LIBS'], + env['TIFF_LIBS'], + env['PGSQL_LIBS'], + env['PROJ_LIBS']]: if path not in env['LIBPATH']: env['LIBPATH'].append(path) - + env.ParseConfig(env['FREETYPE_CONFIG'] + ' --libs --cflags') C_LIBSHEADERS = [ @@ -76,8 +85,8 @@ C_LIBSHEADERS = [ ['tiff', 'tiff.h', True], ['z', 'zlib.h', True], ['jpeg', ['stdio.h', 'jpeglib.h'], True], - ['pq', 'libpq-fe.h', False], - ['proj', 'proj_api.h', False] + ['proj', 'proj_api.h', True], + ['pq', 'libpq-fe.h', False] ] BOOST_LIBSHEADERS = [ @@ -130,10 +139,7 @@ if 'python' in env['BINDINGS']: SConscript('bindings/python/SConscript') - if 'proj' in env['LIBS']: - SConscript('bindings/python/pyprojection/SConscript') - env['LIBS'].remove('proj') - + env = conf.Finish() # Setup the c++ args for our own codebase diff --git a/agg/SConscript b/agg/SConscript index 024405919..2dd1d3197 100644 --- a/agg/SConscript +++ b/agg/SConscript @@ -21,4 +21,4 @@ import glob Import('env') -env.StaticLibrary('agg', glob.glob('./src/' + '*.cpp'), LIBS=[], CPPPATH='./include', CXXFLAGS='-O3 -fPIC ') +env.StaticLibrary('agg', glob.glob('./src/' + '*.cpp'), LIBS=[], CPPPATH='./include', CXXFLAGS='-O3 -fPIC -DNDEBUG') diff --git a/bindings/python/mapnik/__init__.py b/bindings/python/mapnik/__init__.py index 5205a804c..7e143b569 100644 --- a/bindings/python/mapnik/__init__.py +++ b/bindings/python/mapnik/__init__.py @@ -55,10 +55,16 @@ class _Coord(Coord,_injector): return 'Coord(%s,%s)' % (self.x, self.y) class _Envelope(Envelope,_injector): - def __repr__(self): - return 'Envelope(%s,%s,%s,%s)' % \ - (self.minx,self.miny,self.maxx,self.maxy) + def __repr__(self): + return 'Envelope(%s,%s,%s,%s)' % \ + (self.minx,self.miny,self.maxx,self.maxy) +class _Projection(Projection,_injector): + def forward(self,pt): + return forward(pt,self) + def inverse(self,pt): + return inverse(pt,self) + def Datasource (**keywords): return CreateDatasource(keywords) diff --git a/bindings/python/mapnik_coord.cpp b/bindings/python/mapnik_coord.cpp new file mode 100644 index 000000000..42cd37ff7 --- /dev/null +++ b/bindings/python/mapnik_coord.cpp @@ -0,0 +1,45 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2006 Artem Pavlenko, Jean-Francois Doyon + * + * 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 + +void export_coord() +{ + using namespace boost::python; + using mapnik::coord; + class_ >("Coord",init()) + .def_readwrite("x", &coord::x) + .def_readwrite("y", &coord::y) + .def(self == self) // __eq__ + .def(self + self) // __add__ + .def(self + float()) + .def(float() + self) + .def(self - self) // __sub__ + .def(self - float()) + .def(self * float()) //__mult__ + .def(float() * self) + .def(self / float()) // __div__ + ; + +} diff --git a/bindings/python/mapnik_datasource.cpp b/bindings/python/mapnik_datasource.cpp new file mode 100644 index 000000000..f60fcd698 --- /dev/null +++ b/bindings/python/mapnik_datasource.cpp @@ -0,0 +1,68 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2006 Artem Pavlenko, Jean-Francois Doyon + * + * 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 +#include + +namespace +{ + //user-friendly wrapper that uses Python dictionary + using namespace boost::python; + boost::shared_ptr create_datasource(const dict& d) + { + mapnik::parameters params; + boost::python::list keys=d.keys(); + for (int i=0; i(keys[i]); + object obj = d[key]; + extract ex(obj); + if (ex.check()) + { + params[key] = ex(); + } + } + + return mapnik::datasource_cache::create(params); + } +} + +void export_datasource() +{ + using namespace boost::python; + using mapnik::datasource; + + class_, + boost::noncopyable>("Datasource",no_init) + .def("envelope",&datasource::envelope, + return_value_policy()) + .def("features",&datasource::features) + .def("params",&datasource::params,return_value_policy(), + "The configuration parameters of the data source. " + "These vary depending on the type of data source.") + ; + + def("CreateDatasource",&create_datasource); +} diff --git a/bindings/python/mapnik_envelope.cpp b/bindings/python/mapnik_envelope.cpp index 41d25892f..4b530d90a 100644 --- a/bindings/python/mapnik_envelope.cpp +++ b/bindings/python/mapnik_envelope.cpp @@ -85,7 +85,12 @@ void export_envelope() .def("intersects",intersects_p1) .def("intersects",intersects_p2) .def("intersects",intersects_p3) - .def(self == self) + .def(self == self) // __eq__ + .def(self + self) // __add__ + .def(self - self) // __sub__ + .def(self * float()) // __mult__ + .def(float() * self) + .def(self / float()) // __div__ .def_pickle(envelope_pickle_suite()) ; } diff --git a/bindings/python/mapnik_layer.cpp b/bindings/python/mapnik_layer.cpp index 20ccb7ee5..606ae3352 100644 --- a/bindings/python/mapnik_layer.cpp +++ b/bindings/python/mapnik_layer.cpp @@ -37,22 +37,27 @@ void export_layer() .def(vector_indexing_suite,true >()) ; - class_("Layer","A map layer.", init()) + class_("Layer", "A map layer.", init >()) .add_property("name", - make_function(&Layer::name, return_value_policy()), + make_function(&Layer::name, return_value_policy()), &Layer::set_name, "Get/Set the name of the layer.") .add_property("title", - make_function(&Layer::title, return_value_policy()), + make_function(&Layer::title, return_value_policy()), &Layer::set_title, "Get/Set the title of the layer.") .add_property("abstract", - make_function(&Layer::abstract,return_value_policy()), + make_function(&Layer::abstract,return_value_policy()), &Layer::set_abstract, "Get/Set the abstract of the layer.") + .add_property("src", + make_function(&Layer::srs,return_value_policy()), + &Layer::set_srs, + "Get/Set the SRS of the layer.") + .add_property("minzoom", &Layer::getMinZoom, &Layer::setMinZoom) diff --git a/bindings/python/mapnik_line_symbolizer.cpp b/bindings/python/mapnik_line_symbolizer.cpp index 8e8feefb8..ae8e73770 100644 --- a/bindings/python/mapnik_line_symbolizer.cpp +++ b/bindings/python/mapnik_line_symbolizer.cpp @@ -38,7 +38,7 @@ void export_line_symbolizer() .def(init()) .add_property("stroke",make_function (&line_symbolizer::get_stroke, - return_value_policy()), + return_value_policy()), &line_symbolizer::set_stroke) ; } diff --git a/bindings/python/mapnik_map.cpp b/bindings/python/mapnik_map.cpp index 3e42f8da0..5cf5dc1c8 100644 --- a/bindings/python/mapnik_map.cpp +++ b/bindings/python/mapnik_map.cpp @@ -40,7 +40,7 @@ struct map_pickle_suite : boost::python::pickle_suite static boost::python::tuple getinitargs(const Map& m) { - return boost::python::make_tuple(m.getWidth(),m.getHeight(),m.srid()); + return boost::python::make_tuple(m.getWidth(),m.getHeight(),m.srs()); } static boost::python::tuple @@ -85,10 +85,11 @@ void export_map() .def(vector_indexing_suite >()) ; - class_("Map","The map object.",init >()) + class_("Map","The map object.",init >()) .add_property("width",&Map::getWidth,"The width of the map image.") .add_property("height",&Map::getHeight,"The height of the map image.") - .add_property("srid",&Map::srid) + .add_property("srs",make_function(&Map::srs,return_value_policy()), + &Map::set_srs,"Spatial reference in proj4 format e.g. \"+proj=latlong +datum=WGS84\"") .add_property("background",make_function (&Map::getBackground,return_value_policy()), &Map::setBackground, "The background color of the map.") @@ -102,6 +103,7 @@ void export_map() .def("zoom_to_box",&Map::zoomToBox, "Set the geographical extent of the map.") .def("pan",&Map::pan) .def("zoom",&Map::zoom) + .def("zoom_all",&Map::zoom_all) .def("pan_and_zoom",&Map::pan_and_zoom) .def("append_style",&Map::insert_style) .def("remove_style",&Map::remove_style) diff --git a/bindings/python/mapnik_polygon_symbolizer.cpp b/bindings/python/mapnik_polygon_symbolizer.cpp index b61f1d95e..bee4c2ffd 100644 --- a/bindings/python/mapnik_polygon_symbolizer.cpp +++ b/bindings/python/mapnik_polygon_symbolizer.cpp @@ -35,7 +35,7 @@ void export_polygon_symbolizer() .def(init("TODO")) .add_property("fill",make_function (&polygon_symbolizer::get_fill, - return_value_policy()), + return_value_policy()), &polygon_symbolizer::set_fill) .add_property("fill_opacity", &polygon_symbolizer::get_opacity, diff --git a/bindings/python/mapnik_projection.cpp b/bindings/python/mapnik_projection.cpp new file mode 100644 index 000000000..301e94650 --- /dev/null +++ b/bindings/python/mapnik_projection.cpp @@ -0,0 +1,64 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2006 Artem Pavlenko, Jean-Francois Doyon + * + * 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::coord2d forward(mapnik::coord2d const& pt, + mapnik::projection const& prj) + { + double x = pt.x; + double y = pt.y; + prj.forward(x,y); + return mapnik::coord2d(x,y); + } + + mapnik::coord2d inverse(mapnik::coord2d const& pt, + mapnik::projection const& prj) + { + double x = pt.x; + double y = pt.y; + prj.inverse(x,y); + return mapnik::coord2d(x,y); + } + +} + +void export_projection () +{ + using namespace boost::python; + using mapnik::projection; + + class_("Projection", init >()) + .def ("forward",&projection::forward) + .def ("inverse",&projection::inverse) + .def ("params", make_function(&projection::params, + return_value_policy())) + ; + + def("forward",&forward); + def("inverse",&inverse); + +} diff --git a/bindings/python/mapnik_python.cpp b/bindings/python/mapnik_python.cpp index 5df24909b..224ca70e7 100644 --- a/bindings/python/mapnik_python.cpp +++ b/bindings/python/mapnik_python.cpp @@ -26,6 +26,7 @@ #include void export_color(); +void export_coord(); void export_layer(); void export_parameters(); void export_envelope(); @@ -37,6 +38,7 @@ void export_filter(); void export_rule(); void export_style(); void export_stroke(); +void export_datasource(); void export_datasource_cache(); void export_point_symbolizer(); void export_line_symbolizer(); @@ -46,6 +48,7 @@ void export_polygon_pattern_symbolizer(); void export_raster_symbolizer(); void export_text_symbolizer(); void export_font_engine(); +void export_projection(); #include #include @@ -70,28 +73,6 @@ void render(const mapnik::Map& map,mapnik::Image32& image) ren.apply(); } -namespace -{ - //user-friendly wrapper that uses Python dictionary - using namespace boost::python; - boost::shared_ptr create_datasource(const dict& d) - { - mapnik::parameters params; - boost::python::list keys=d.keys(); - for (int i=0; i(keys[i]); - object obj = d[key]; - extract ex(obj); - if (ex.check()) - { - params[key] = ex(); - } - } - - return mapnik::datasource_cache::create(params); - } -} BOOST_PYTHON_MODULE(_mapnik) { @@ -109,18 +90,7 @@ BOOST_PYTHON_MODULE(_mapnik) class_("FeatureSet",no_init) ; - class_, - boost::noncopyable>("Datasource",no_init) - .def("envelope",&datasource::envelope, - return_value_policy()) - .def("features",&datasource::features) - .def("params",&datasource::params,return_value_policy(), - "The configuration parameters of the data source. " - "These vary depending on the type of data source.") - ; - - def("CreateDatasource",&create_datasource); - + export_datasource(); export_parameters(); export_color(); export_envelope(); @@ -139,12 +109,8 @@ BOOST_PYTHON_MODULE(_mapnik) export_raster_symbolizer(); export_text_symbolizer(); export_font_engine(); - - class_ >("Coord",init()) - .def_readwrite("x", &coord::x) - .def_readwrite("y", &coord::y) - ; - + export_projection(); + export_coord(); export_map(); def("render_to_file",&render_to_file); diff --git a/bindings/python/mapnik_stroke.cpp b/bindings/python/mapnik_stroke.cpp index 890d4475b..52d574b7e 100644 --- a/bindings/python/mapnik_stroke.cpp +++ b/bindings/python/mapnik_stroke.cpp @@ -45,7 +45,7 @@ void export_stroke () class_("Stroke",init<>()) .def(init()) .add_property("color",make_function - (&stroke::get_color,return_value_policy()), + (&stroke::get_color,return_value_policy()), &stroke::set_color) .add_property("width",&stroke::get_width,&stroke::set_width) .add_property("opacity",&stroke::get_opacity,&stroke::set_opacity) diff --git a/demo/python/rundemo.py b/demo/python/rundemo.py index bc36b14c0..dee114953 100644 --- a/demo/python/rundemo.py +++ b/demo/python/rundemo.py @@ -30,7 +30,7 @@ installed successfully before running this script.\n\n' # Instanciate a map, giving it a width and height. Remember: the word "map" is # reserved in Python! :) -m = Map(800,600) +m = Map(800,600,"+proj=latlong") # Set its background colour. More on colours later ... diff --git a/include/mapnik/agg_renderer.hpp b/include/mapnik/agg_renderer.hpp index 474556fde..0ece2436f 100644 --- a/include/mapnik/agg_renderer.hpp +++ b/include/mapnik/agg_renderer.hpp @@ -45,13 +45,27 @@ namespace mapnik { void end_map_processing(Map const& map); void start_layer_processing(Layer const& lay); void end_layer_processing(Layer const& lay); - void process(point_symbolizer const& sym,Feature const& feature); - void process(line_symbolizer const& sym,Feature const& feature); - void process(line_pattern_symbolizer const& sym,Feature const& feature); - void process(polygon_symbolizer const& sym,Feature const& feature); - void process(polygon_pattern_symbolizer const& sym,Feature const& feature); - void process(raster_symbolizer const& sym,Feature const& feature); - void process(text_symbolizer const& sym,Feature const& feature); + void process(point_symbolizer const& sym, + Feature const& feature, + proj_transform const& prj_trans); + void process(line_symbolizer const& sym, + Feature const& feature, + proj_transform const& prj_trans); + void process(line_pattern_symbolizer const& sym, + Feature const& feature, + proj_transform const& prj_trans); + void process(polygon_symbolizer const& sym, + Feature const& feature, + proj_transform const& prj_trans); + void process(polygon_pattern_symbolizer const& sym, + Feature const& feature, + proj_transform const& prj_trans); + void process(raster_symbolizer const& sym, + Feature const& feature, + proj_transform const& prj_trans); + void process(text_symbolizer const& sym, + Feature const& feature, + proj_transform const& prj_trans); private: T & pixmap_; CoordTransform t_; diff --git a/include/mapnik/ctrans.hpp b/include/mapnik/ctrans.hpp index bfe761999..bf9865e17 100644 --- a/include/mapnik/ctrans.hpp +++ b/include/mapnik/ctrans.hpp @@ -27,6 +27,7 @@ #include #include +#include namespace mapnik { typedef coord_array CoordinateArray; @@ -53,7 +54,38 @@ namespace mapnik { Transform const& t_; Geometry& geom_; }; + + template + struct MAPNIK_DECL coord_transform2 + { + coord_transform2(Transform const& t, + Geometry& geom, + proj_transform const& prj_trans) + : t_(t), + geom_(geom), + prj_trans_(prj_trans) {} + + unsigned vertex(double * x , double * y) const + { + unsigned command = geom_.vertex(x,y); + double z=0; + prj_trans_.backward(*x,*y,z); + t_.forward(x,y); + return command; + } + + void rewind (unsigned pos) + { + geom_.rewind(pos); + } + + private: + Transform const& t_; + Geometry& geom_; + proj_transform const& prj_trans_; + }; + class CoordTransform { private: diff --git a/include/mapnik/feature_style_processor.hpp b/include/mapnik/feature_style_processor.hpp index 2d97c42ee..5cf6ddc85 100644 --- a/include/mapnik/feature_style_processor.hpp +++ b/include/mapnik/feature_style_processor.hpp @@ -36,6 +36,7 @@ #include #include #include +#include namespace mapnik { @@ -44,17 +45,22 @@ namespace mapnik { struct symbol_dispatch : public boost::static_visitor<> { - symbol_dispatch (Processor & output,Feature const& f) - : output_(output),f_(f) {} - + symbol_dispatch (Processor & output, + Feature const& f, + proj_transform const& prj_trans) + : output_(output), + f_(f), + prj_trans_(prj_trans) {} + template void operator () (T const& sym) const { - output_.process(sym,f_); + output_.process(sym,f_,prj_trans_); } - + Processor & output_; Feature const& f_; + proj_transform const& prj_trans_; }; public: feature_style_processor(Map const& m) @@ -63,52 +69,82 @@ namespace mapnik void apply() { boost::progress_timer t; + Processor & p = static_cast(*this); - + p.start_map_processing(m_); - + std::vector::const_iterator itr = m_.layers().begin(); std::vector::const_iterator end = m_.layers().end(); - while (itr != end) - { - if (itr->isVisible(m_.scale()) && - itr->envelope().intersects(m_.getCurrentExtent())) - { - apply_to_layer(*itr,p); - } - ++itr; - } + try + { + projection proj(m_.srs()); // map projection + + while (itr != end) + { + if (itr->isVisible(m_.scale()))// && + //itr->envelope().intersects(m_.getCurrentExtent())) TODO + { + apply_to_layer(*itr, p, proj); + } + ++itr; + } + } + catch (proj_init_error& ex) + { + std::clog << ex.what() << "\n"; + } + p.end_map_processing(m_); } private: - void apply_to_layer(Layer const& lay,Processor & p) + void apply_to_layer(Layer const& lay, Processor & p, projection const& proj0) { p.start_layer_processing(lay); boost::shared_ptr ds=lay.datasource(); if (ds) { - Envelope const& bbox=m_.getCurrentExtent(); + Envelope const& ext=m_.getCurrentExtent(); + + projection proj1(lay.srs()); + proj_transform prj_trans(proj0,proj1); + + double x0 = ext.minx(); + double y0 = ext.miny(); + double z0 = 0.0; + double x1 = ext.maxx(); + double y1 = ext.maxy(); + double z1 = 0.0; + prj_trans.forward(x0,y0,z0); + prj_trans.forward(x1,y1,z1); + Envelope bbox(x0,y0,x1,y1); + std::clog << bbox << "\n"; + double scale = m_.scale(); - + std::vector const& style_names = lay.styles(); std::vector::const_iterator stylesIter = style_names.begin(); - while (stylesIter != style_names.end()) + std::vector::const_iterator stylesEnd = style_names.end(); + + while (stylesIter != stylesEnd) { std::set names; attribute_collector collector(names); std::vector if_rules; std::vector else_rules; - + bool active_rules=false; - + feature_type_style const& style=m_.find_style(*stylesIter++); - + + query q(bbox); //BBOX query + const std::vector& rules=style.get_rules(); std::vector::const_iterator ruleIter=rules.begin(); - - query q(bbox); //BBOX query - while (ruleIter!=rules.end()) + std::vector::const_iterator ruleEnd=rules.end(); + + while (ruleIter!=ruleEnd) { if (ruleIter->active(scale)) { @@ -127,8 +163,10 @@ namespace mapnik ++ruleIter; } std::set::const_iterator namesIter=names.begin(); + std::set::const_iterator namesEnd =names.end(); + // push all property names - while (namesIter!=names.end()) + while (namesIter!=namesEnd) { q.add_property_name(*namesIter); ++namesIter; @@ -143,7 +181,8 @@ namespace mapnik { bool do_else=true; std::vector::const_iterator itr=if_rules.begin(); - while (itr!=if_rules.end()) + std::vector::const_iterator end=if_rules.end(); + while (itr != end) { filter_ptr const& filter=(*itr)->get_filter(); if (filter->pass(*feature)) @@ -151,10 +190,11 @@ namespace mapnik do_else=false; const symbolizers& symbols = (*itr)->get_symbolizers(); symbolizers::const_iterator symIter=symbols.begin(); - while (symIter!=symbols.end()) + symbolizers::const_iterator symEnd =symbols.end(); + while (symIter != symEnd) { boost::apply_visitor - (symbol_dispatch(p,*feature),*symIter++); + (symbol_dispatch(p,*feature,prj_trans),*symIter++); } } ++itr; @@ -164,14 +204,19 @@ namespace mapnik //else filter std::vector::const_iterator itr= else_rules.begin(); - while (itr != else_rules.end()) + std::vector::const_iterator end= + else_rules.end(); + while (itr != end) { const symbolizers& symbols = (*itr)->get_symbolizers(); - symbolizers::const_iterator symIter=symbols.begin(); - while (symIter!=symbols.end()) + symbolizers::const_iterator symIter= symbols.begin(); + symbolizers::const_iterator symEnd = symbols.end(); + + while (symIter!=symEnd) { boost::apply_visitor - (symbol_dispatch(p,*feature),*symIter++); + (symbol_dispatch(p,*feature,prj_trans), + *symIter++); } ++itr; } @@ -180,10 +225,10 @@ namespace mapnik } } } + } p.end_layer_processing(lay); - } - + } Map const& m_; }; } diff --git a/include/mapnik/layer.hpp b/include/mapnik/layer.hpp index f4d16cdfe..fee8959c3 100644 --- a/include/mapnik/layer.hpp +++ b/include/mapnik/layer.hpp @@ -38,6 +38,8 @@ namespace mapnik std::string name_; std::string title_; std::string abstract_; + std::string srs_; + double minZoom_; double maxZoom_; bool active_; @@ -49,7 +51,7 @@ namespace mapnik mutable std::vector > selection_; public: - explicit Layer(std::string const& name); + explicit Layer(std::string const& name, std::string const& srs="+proj=latlong +datum=WGS84"); Layer(Layer const& l); Layer& operator=(Layer const& l); bool operator==(Layer const& other) const; @@ -59,6 +61,8 @@ namespace mapnik const std::string& title() const; void set_abstract(std::string const& abstract); const std::string& abstract() const; + void set_srs(std::string const& srs); + std::string const& srs() const; void add_style(std::string const& stylename); std::vector const& styles() const; void selection_style(const std::string& name); diff --git a/include/mapnik/map.hpp b/include/mapnik/map.hpp index 019c6176d..6050f6223 100644 --- a/include/mapnik/map.hpp +++ b/include/mapnik/map.hpp @@ -36,18 +36,17 @@ namespace mapnik static const unsigned MAX_MAPSIZE=2048; unsigned width_; unsigned height_; - int srid_; + std::string srs_; Color background_; std::map styles_; std::vector layers_; Envelope currentExtent_; public: - typedef std::map::const_iterator style_iterator; Map(); - Map(int width,int height,int srid=-1); + Map(int width, int height, std::string const& srs="+proj=latlong +datum=WGS84"); Map(const Map& rhs); Map& operator=(const Map& rhs); style_iterator begin_styles() const; @@ -67,7 +66,8 @@ namespace mapnik void setWidth(unsigned width); void setHeight(unsigned height); void resize(unsigned width,unsigned height); - int srid() const; + std::string const& srs() const; + void set_srs(std::string const& srs); void setBackground(const Color& c); const Color& getBackground() const; void zoom(double zoom); diff --git a/include/mapnik/projection.hpp b/include/mapnik/projection.hpp new file mode 100644 index 000000000..719fffafb --- /dev/null +++ b/include/mapnik/projection.hpp @@ -0,0 +1,174 @@ + +#ifndef PROJECTION_HPP +#define PROJECTION_HPP + +#include +#include +#include +#include + +#include +#include + +namespace mapnik +{ + class proj_init_error : public std::runtime_error + { + public: + proj_init_error(std::string const& params) + : std::runtime_error("failed to initialize projection with:" + params) {} + }; + + class projection + { + friend class proj_transform; + public: + explicit projection(std::string params = "+proj=latlong +ellps=WGS84") + : params_(params) + { + init(); // + } + + projection(projection const& rhs) + : params_(rhs.params_) + { + init(); // + } + + projection& operator=(projection const& rhs) + { + projection tmp(rhs); + swap(tmp); + return *this; + } + + bool is_initialized() const + { + return proj_ ? true : false; + } + + std::string const& params() const + { + return params_; + } + + void forward(double & x, double &y ) const + { + projUV p; + p.u = x * DEG_TO_RAD; + p.v = y * DEG_TO_RAD; + p = pj_fwd(p,proj_); + x = p.u; + y = p.v; + } + + void inverse(double & x,double & y) const + { + projUV p; + p.u = x; + p.v = y; + p = pj_inv(p,proj_); + x = RAD_TO_DEG * p.u; + y = RAD_TO_DEG * p.v; + } + + ~projection() + { + if (proj_) pj_free(proj_); + } + private: + + void init() + { + proj_=pj_init_plus(params_.c_str()); + if (!proj_) throw proj_init_error(params_); + } + + void swap (projection& rhs) + { + std::swap(params_,rhs.params_); + init (); + } + + private: + std::string params_; + projPJ proj_; + }; + + class proj_transform : private boost::noncopyable + { + public: + proj_transform(projection const& source, + projection const& dest) + : source_(source), + dest_(dest) + { + is_source_latlong_ = pj_is_latlong(source_.proj_); + is_dest_latlong_ = pj_is_latlong(dest_.proj_); + } + + bool forward (double & x, double & y , double & z) const + { + if (is_source_latlong_) + { + x *= DEG_TO_RAD; + y *= DEG_TO_RAD; + } + + if (pj_transform( source_.proj_, dest_.proj_, 1, + 0, &x,&y,&z) != 0) + { + return false; + } + + if (is_dest_latlong_) + { + x *= RAD_TO_DEG; + y *= RAD_TO_DEG; + } + + return true; + } + + bool forward (Envelope & ext) const + { + if (is_source_latlong_) + { + ext = ext.intersect(Envelope(-180,-90,180,90)); + } + // TODO + return true; + } + + bool backward (double & x, double & y , double & z) const + { + if (is_dest_latlong_) + { + x *= DEG_TO_RAD; + y *= DEG_TO_RAD; + } + + if (pj_transform( dest_.proj_, source_.proj_, 1, + 0, &x,&y,&z) != 0) + { + return false; + } + + if (is_source_latlong_) + { + x *= RAD_TO_DEG; + y *= RAD_TO_DEG; + } + + return true; + } + + private: + projection const& source_; + projection const& dest_; + bool is_source_latlong_; + bool is_dest_latlong_; + }; +} + +#endif //PROJECTION_HPP diff --git a/src/agg_renderer.cpp b/src/agg_renderer.cpp index 0825fd1eb..649ab352e 100644 --- a/src/agg_renderer.cpp +++ b/src/agg_renderer.cpp @@ -126,9 +126,11 @@ namespace mapnik } template - void agg_renderer::process(polygon_symbolizer const& sym,Feature const& feature) + void agg_renderer::process(polygon_symbolizer const& sym, + Feature const& feature, + proj_transform const& prj_trans) { - typedef coord_transform path_type; + typedef coord_transform2 path_type; typedef agg::renderer_base ren_base; typedef agg::renderer_scanline_aa_solid renderer; @@ -139,7 +141,7 @@ namespace mapnik { unsigned width = pixmap_.width(); unsigned height = pixmap_.height(); - path_type path(t_,*geom); + path_type path(t_,*geom,prj_trans); agg::row_ptr_cache buf(pixmap_.raw_data(),width,height,width * 4); agg::pixfmt_rgba32 pixf(buf); ren_base renb(pixf); @@ -147,9 +149,8 @@ namespace mapnik unsigned r=fill_.red(); unsigned g=fill_.green(); unsigned b=fill_.blue(); - //unsigned a=fill_.alpha(); renderer ren(renb); - + agg::rasterizer_scanline_aa<> ras; agg::scanline_u8 sl; ras.clip_box(0,0,width,height); @@ -160,10 +161,12 @@ namespace mapnik } template - void agg_renderer::process(line_symbolizer const& sym,Feature const& feature) + void agg_renderer::process(line_symbolizer const& sym, + Feature const& feature, + proj_transform const& prj_trans) { typedef agg::renderer_base ren_base; - typedef coord_transform path_type; + typedef coord_transform2 path_type; typedef agg::renderer_outline_aa renderer_oaa; typedef agg::rasterizer_outline_aa rasterizer_outline_aa; typedef agg::renderer_scanline_aa_solid renderer; @@ -171,7 +174,7 @@ namespace mapnik geometry_ptr const& geom=feature.get_geometry(); if (geom && geom->num_points() > 1) { - path_type path(t_,*geom); + path_type path(t_,*geom,prj_trans); agg::row_ptr_cache buf(pixmap_.raw_data(), pixmap_.width(), pixmap_.height(), @@ -277,17 +280,21 @@ namespace mapnik } template - void agg_renderer::process(point_symbolizer const& sym,Feature const& feature) + void agg_renderer::process(point_symbolizer const& sym, + Feature const& feature, + proj_transform const& prj_trans) { geometry_ptr const& geom=feature.get_geometry(); if (geom) { double x; double y; + double z=0; boost::shared_ptr const& data = sym.get_data(); if ( data ) { geom->label_position(&x,&y); + prj_trans.backward(x,y,z); t_.forward(&x,&y); int w = data->width(); int h = data->height(); @@ -307,9 +314,11 @@ namespace mapnik } template - void agg_renderer::process(line_pattern_symbolizer const& sym,Feature const& feature) + void agg_renderer::process(line_pattern_symbolizer const& sym, + Feature const& feature, + proj_transform const& prj_trans) { - typedef coord_transform path_type; + typedef coord_transform2 path_type; typedef agg::line_image_pattern pattern_type; typedef agg::renderer_base renderer_base; typedef agg::renderer_outline_image renderer_type; @@ -321,7 +330,7 @@ namespace mapnik unsigned width = pixmap_.width(); unsigned height = pixmap_.height(); ImageData32 const& pat = sym.get_pattern(); - path_type path(t_,*geom); + path_type path(t_,*geom,prj_trans); agg::row_ptr_cache buf(pixmap_.raw_data(), width, height,width*4); agg::pixfmt_rgba32 pixf(buf); renderer_base ren_base(pixf); @@ -336,9 +345,11 @@ namespace mapnik } template - void agg_renderer::process(polygon_pattern_symbolizer const& sym,Feature const& feature) + void agg_renderer::process(polygon_pattern_symbolizer const& sym, + Feature const& feature, + proj_transform const& prj_trans) { - typedef coord_transform path_type; + typedef coord_transform2 path_type; typedef agg::renderer_base ren_base; typedef agg::wrap_mode_repeat wrap_x_type; typedef agg::wrap_mode_repeat wrap_y_type; @@ -358,7 +369,7 @@ namespace mapnik unsigned width = pixmap_.width(); unsigned height = pixmap_.height(); - path_type path(t_,*geom); + path_type path(t_,*geom,prj_trans); agg::row_ptr_cache buf(pixmap_.raw_data(),width,height,width * 4); agg::pixfmt_rgba32 pixf(buf); @@ -389,7 +400,9 @@ namespace mapnik } template - void agg_renderer::process(raster_symbolizer const& ,Feature const& feature) + void agg_renderer::process(raster_symbolizer const&, + Feature const& feature, + proj_transform const& prj_trans) { // TODO -- at the moment raster_symbolizer is an empty class // used for type dispatching, but we can have some fancy raster @@ -405,9 +418,11 @@ namespace mapnik } template - void agg_renderer::process(text_symbolizer const& sym ,Feature const& feature) + void agg_renderer::process(text_symbolizer const& sym, + Feature const& feature, + proj_transform const& prj_trans) { - typedef coord_transform path_type; + typedef coord_transform2 path_type; geometry_ptr const& geom=feature.get_geometry(); if (geom) { @@ -416,7 +431,7 @@ namespace mapnik geom->num_points() > 1) { - path_type path(t_,*geom); + path_type path(t_,*geom,prj_trans); double x0,y0,x1,y1; path.vertex(&x0,&y0); path.vertex(&x1,&y1); diff --git a/src/layer.cpp b/src/layer.cpp index 62dc4eca6..d6e5bf019 100644 --- a/src/layer.cpp +++ b/src/layer.cpp @@ -38,10 +38,11 @@ using boost::shared_ptr; namespace mapnik { - Layer::Layer(std::string const& name) + Layer::Layer(std::string const& name, std::string const& srs) : name_(name), title_(""), abstract_(""), + srs_(srs), minZoom_(0), maxZoom_(std::numeric_limits::max()), active_(true), @@ -53,6 +54,7 @@ namespace mapnik : name_(rhs.name_), title_(rhs.title_), abstract_(rhs.abstract_), + srs_(rhs.srs_), minZoom_(rhs.minZoom_), maxZoom_(rhs.maxZoom_), active_(rhs.active_), @@ -119,11 +121,21 @@ namespace mapnik return abstract_; } + void Layer::set_srs(std::string const& srs) + { + srs_ = srs; + } + + std::string const& Layer::srs() const + { + return srs_; + } + void Layer::add_style(std::string const& stylename) { styles_.push_back(stylename); } - + std::vector const& Layer::styles() const { return styles_; diff --git a/src/load_map.cpp b/src/load_map.cpp index 9597f3b61..6458535e7 100644 --- a/src/load_map.cpp +++ b/src/load_map.cpp @@ -47,19 +47,24 @@ namespace mapnik { using boost::property_tree::ptree; ptree pt; - read_xml(filename,pt); boost::optional bgcolor = pt.get_optional("Map..bgcolor"); - if ( bgcolor) + + if (bgcolor) { Color bg = color_factory::from_string(bgcolor->c_str()); map.setBackground(bg); } + 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) { ptree::value_type const& v = *itr; @@ -284,18 +289,19 @@ namespace mapnik else if (v.first == "Layer") { - std::string name = v.second.get(".name",""); - Layer lyr(name); + 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(".status"); + v.second.get_optional(".status"); if (status && *status == "off") { lyr.setActive(false); } - ptree::const_iterator itr2 = v.second.begin(); ptree::const_iterator end2 = v.second.end(); diff --git a/src/map.cpp b/src/map.cpp index c11af6d3f..4b0e47f9e 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -32,17 +33,18 @@ namespace mapnik Map::Map() : width_(400), height_(400), - srid_(-1) {} - Map::Map(int width,int height,int srid) + srs_("+proj=latlong +datum=WGS84") {} + + Map::Map(int width,int height, std::string const& srs) : width_(width), height_(height), - srid_(srid), + srs_(srs), background_(Color(255,255,255)) {} Map::Map(const Map& rhs) : width_(rhs.width_), height_(rhs.height_), - srid_(rhs.srid_), + srs_(rhs.srs_), background_(rhs.background_), styles_(rhs.styles_), layers_(rhs.layers_), @@ -53,12 +55,13 @@ namespace mapnik if (this==&rhs) return *this; width_=rhs.width_; height_=rhs.height_; - srid_=rhs.srid_; + srs_=rhs.srs_; background_=rhs.background_; styles_=rhs.styles_; layers_=rhs.layers_; return *this; } + Map::style_iterator Map::begin_styles() const { return styles_.begin(); @@ -78,10 +81,9 @@ namespace mapnik styles_.erase(name); } - feature_type_style const& Map::find_style(std::string const& name) const + feature_type_style const& Map::find_style(std::string const& name) const { - std::map::const_iterator itr - = styles_.find(name); + std::map::const_iterator itr = styles_.find(name); if (itr!=styles_.end()) return itr->second; static feature_type_style default_style; @@ -118,7 +120,6 @@ namespace mapnik return layers_[index]; } - std::vector const& Map::layers() const { return layers_; @@ -151,6 +152,7 @@ namespace mapnik fixAspectRatio(); } } + void Map::resize(unsigned width,unsigned height) { if (width >= MIN_MAPSIZE && width <= MAX_MAPSIZE && @@ -162,11 +164,16 @@ namespace mapnik } } - int Map::srid() const + std::string const& Map::srs() const { - return srid_; + return srs_; } - + + void Map::set_srs(std::string const& srs) + { + srs_ = srs; + } + void Map::setBackground(const Color& c) { background_=c; @@ -176,7 +183,7 @@ namespace mapnik { return background_; } - + void Map::zoom(double factor) { coord2d center = currentExtent_.center(); @@ -191,23 +198,50 @@ namespace mapnik void Map::zoom_all() { - std::vector::const_iterator itr = layers_.begin(); - Envelope ext; - bool first = true; - while (itr != layers_.end()) + try { - if (first) + projection proj0(srs_); + Envelope ext; + bool first = true; + std::vector::const_iterator itr = layers_.begin(); + std::vector::const_iterator end = layers_.end(); + while (itr != end) { - ext = itr->envelope(); - first = false; + std::string const& layer_srs = itr->srs(); + projection proj1(layer_srs); + proj_transform prj_trans(proj0,proj1); + + Envelope layerExt = itr->envelope(); + double x0 = layerExt.minx(); + double y0 = layerExt.miny(); + double z0 = 0.0; + double x1 = layerExt.maxx(); + double y1 = layerExt.maxy(); + double z1 = 0.0; + prj_trans.backward(x0,y0,z0); + prj_trans.backward(x1,y1,z1); + + Envelope layerExt2(x0,y0,x1,y1); + std::clog << " layer1 - > " << layerExt << "\n"; + std::clog << " layer2 - > " << layerExt2 << "\n"; + + if (first) + { + ext = layerExt2; + first = false; + } + else + { + ext.expand_to_include(layerExt2); + } + ++itr; } - else - { - ext.expand_to_include(itr->envelope()); - } - ++itr; + zoomToBox(ext); + } + catch (proj_init_error & ex) + { + std::clog << ex.what() << '\n'; } - zoomToBox(ext); } void Map::zoomToBox(const Envelope &box)