Skip to content

Commit

Permalink
refactor projection interface: 1. cleanup custom lonlat<->merc code. …
Browse files Browse the repository at this point in the history
…2. centralize proj4 strings into constants, 3. tweak projection benchmarking to allocate objects in loop so we can test that specifically - refs #1703
  • Loading branch information
Dane Springmeyer committed Jan 28, 2013
1 parent 95b29f8 commit f8b1520
Show file tree
Hide file tree
Showing 11 changed files with 326 additions and 114 deletions.
64 changes: 38 additions & 26 deletions benchmark/run.cpp
Expand Up @@ -40,7 +40,7 @@ void benchmark(T test, std::string const& name)
if (!test_set.empty()) {
should_run_test = test_set.find(test_num) != test_set.end();
}
if (should_run_test) {
if (should_run_test || dry_run) {
if (!test.validate()) {
std::clog << "test did not validate: " << name << "\n";
//throw std::runtime_error(std::string("test did not validate: ") + name);
Expand Down Expand Up @@ -282,23 +282,26 @@ struct test6
std::string dest_;
mapnik::box2d<double> from_;
mapnik::box2d<double> to_;
bool defer_proj4_init_;
explicit test6(unsigned iterations,
unsigned threads,
std::string const& src,
std::string const& dest,
mapnik::box2d<double> from,
mapnik::box2d<double> to) :
mapnik::box2d<double> to,
bool defer_proj) :
iter_(iterations),
threads_(threads),
src_(src),
dest_(dest),
from_(from),
to_(to) {}
to_(to),
defer_proj4_init_(defer_proj) {}

bool validate()
{
mapnik::projection src(src_);
mapnik::projection dest(dest_);
mapnik::projection src(src_,defer_proj4_init_);
mapnik::projection dest(dest_,defer_proj4_init_);
mapnik::proj_transform tr(src,dest);
mapnik::box2d<double> bbox = from_;
if (!tr.forward(bbox)) return false;
Expand All @@ -310,17 +313,17 @@ struct test6
}
void operator()()
{
mapnik::projection src(src_);
mapnik::projection dest(dest_);
mapnik::proj_transform tr(src,dest);
unsigned count=0;
for (int i=-180;i<180;i=++i)
{
for (int j=-85;j<85;++j)
{
mapnik::box2d<double> box(i,j,i,j);
if (!tr.forward(box)) throw std::runtime_error("could not transform coords");
++count;
mapnik::projection src(src_,defer_proj4_init_);
mapnik::projection dest(dest_,defer_proj4_init_);
mapnik::proj_transform tr(src,dest);
mapnik::box2d<double> box(i,j,i,j);
if (!tr.forward(box)) throw std::runtime_error("could not transform coords");
++count;
}
}
}
Expand Down Expand Up @@ -398,38 +401,47 @@ int main( int argc, char** argv)

mapnik::box2d<double> from(-180,-80,180,80);
mapnik::box2d<double> to(-20037508.3427892476,-15538711.0963092316,20037508.3427892476,15538711.0963092316);

{
test6 runner(1,5,
"+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs",
"+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
from,to,false);
benchmark(runner,"lonlat -> merc coord transformation with proj4 init (literal)");
}

{
test6 runner(1,5,
"+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs",
"+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
from,to,true);
benchmark(runner,"lonlat -> merc coord transformation with lazy proj4 init (literal)");
}

/*{
// echo -180 -60 | cs2cs -f "%.10f" +init=epsg:4326 +to +init=epsg:3857
test6 runner(100000000,100,
"+init=epsg:4326",
"+init=epsg:3857",
from,to);
from,to,false);
benchmark(runner,"lonlat -> merc coord transformation (epsg)");
}
{
test6 runner(100000000,100,
"+init=epsg:3857",
"+init=epsg:4326",
to,from);
to,from,false);
benchmark(runner,"merc -> lonlat coord transformation (epsg)");
}

{
test6 runner(100000000,100,
"+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs",
"+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
from,to);
benchmark(runner,"lonlat -> merc coord transformation (literal)");
}
}*/

{
test6 runner(100000000,100,
/*{
test6 runner(10,2,
"+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
"+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs",
to,from);
to,from,false);
benchmark(runner,"merc -> lonlat coord transformation (literal)");
}
}*/

std::cout << "...benchmark done\n";
return 0;
Expand Down
3 changes: 2 additions & 1 deletion include/mapnik/layer.hpp
Expand Up @@ -26,6 +26,7 @@
// mapnik
#include <mapnik/feature.hpp>
#include <mapnik/datasource.hpp>
#include <mapnik/well_known_srs.hpp>

// stl
#include <vector>
Expand All @@ -44,7 +45,7 @@ class MAPNIK_DECL layer
{
public:
layer(std::string const& name,
std::string const& srs="+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs");
std::string const& srs=MAPNIK_LONGLAT_PROJ);

layer(layer const& l);
layer& operator=(layer const& rhs);
Expand Down
3 changes: 2 additions & 1 deletion include/mapnik/map.hpp
Expand Up @@ -30,6 +30,7 @@
#include <mapnik/datasource.hpp> // for featureset_ptr
#include <mapnik/layer.hpp>
#include <mapnik/params.hpp>
#include <mapnik/well_known_srs.hpp>

// boost
#include <boost/optional/optional.hpp>
Expand Down Expand Up @@ -105,7 +106,7 @@ class MAPNIK_DECL Map
* @param height Initial map height.
* @param srs Initial map projection.
*/
Map(int width, int height, std::string const& srs="+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs");
Map(int width, int height, std::string const& srs=MAPNIK_LONGLAT_PROJ);

/*! \brief Copy Constructur.
*
Expand Down
5 changes: 3 additions & 2 deletions include/mapnik/proj_transform.hpp
Expand Up @@ -49,12 +49,13 @@ class MAPNIK_DECL proj_transform : private mapnik::noncopyable
mapnik::projection const& dest() const;

private:
projection const source_;
projection const dest_;
projection const& source_;
projection const& dest_;
bool is_source_longlat_;
bool is_dest_longlat_;
bool is_source_equal_dest_;
bool wgs84_to_merc_;
bool merc_to_wgs84_;
};
}

Expand Down
27 changes: 14 additions & 13 deletions include/mapnik/projection.hpp
Expand Up @@ -24,16 +24,14 @@
#define MAPNIK_PROJECTION_HPP

// mapnik
#include <mapnik/box2d.hpp>

// proj4
#include <proj_api.h>
#include <mapnik/config.hpp>
#include <mapnik/well_known_srs.hpp>

// boost
#if defined(MAPNIK_THREADSAFE) && PJ_VERSION < 480
#include <boost/thread/mutex.hpp>
#endif
#include <boost/utility.hpp>
#include <boost/optional.hpp>

// stl
#include <string>
Expand All @@ -52,7 +50,9 @@ class MAPNIK_DECL projection
{
friend class proj_transform;
public:
explicit projection(std::string const& params = "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs");

explicit projection(std::string const& params = MAPNIK_LONGLAT_PROJ,
bool defer_proj_init = false);
projection(projection const& rhs);
~projection();

Expand All @@ -61,24 +61,25 @@ class MAPNIK_DECL projection
bool operator!=(const projection& other) const;
bool is_initialized() const;
bool is_geographic() const;
boost::optional<well_known_srs_e> well_known() const;
std::string const& params() const;

void forward(double & x, double &y ) const;
void forward(double & x, double & y) const;
void inverse(double & x,double & y) const;

std::string expanded() const;
void init_proj4() const;

private:
void init();
void swap (projection& rhs);

private:
std::string params_;
projPJ proj_;
bool is_geographic_;
#if PJ_VERSION >= 480
projCtx proj_ctx_;
#elif defined(MAPNIK_THREADSAFE)
bool defer_proj_init_;
mutable bool is_geographic_;
mutable void * proj_;
mutable void * proj_ctx_;
#if defined(MAPNIK_THREADSAFE) && PJ_VERSION < 480
static boost::mutex mutex_;
#endif
};
Expand Down
94 changes: 94 additions & 0 deletions include/mapnik/well_known_srs.hpp
@@ -0,0 +1,94 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2013 Artem Pavlenko
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/

#ifndef MAPNIK_WELL_KNOWN_SRS_HPP
#define MAPNIK_WELL_KNOWN_SRS_HPP

// mapnik
#include <mapnik/enumeration.hpp>

// boost
#include <boost/optional.hpp>

// stl
#include <cmath>

namespace mapnik {

enum well_known_srs_enum {
WGS_84,
G_MERC,
well_known_srs_enum_MAX
};

DEFINE_ENUM( well_known_srs_e, well_known_srs_enum );

static const double EARTH_RADIUS = 6378137.0;
static const double EARTH_DIAMETER = EARTH_RADIUS * 2.0;
static const double EARTH_CIRCUMFERENCE = EARTH_DIAMETER * M_PI;
static const double MAXEXTENT = EARTH_CIRCUMFERENCE / 2.0;
static const double M_PI_by2 = M_PI / 2;
static const double D2R = M_PI / 180;
static const double R2D = 180 / M_PI;
static const double M_PIby360 = M_PI / 360;
static const double MAXEXTENTby180 = MAXEXTENT / 180;
static const std::string MAPNIK_LONGLAT_PROJ = "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs";
static const std::string MAPNIK_GMERC_PROJ = "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over";

boost::optional<well_known_srs_e> is_well_known_srs(std::string const& srs);

boost::optional<bool> is_known_geographic(std::string const& srs);

static inline bool lonlat2merc(double * x, double * y , int point_count)
{
int i;
for(i=0; i<point_count; i++) {
x[i] = x[i] * MAXEXTENTby180;
y[i] = std::log(std::tan((90 + y[i]) * M_PIby360)) * R2D;
y[i] = y[i] * MAXEXTENTby180;
if (x[i] > MAXEXTENT) x[i] = MAXEXTENT;
if (x[i] < -MAXEXTENT) x[i] = -MAXEXTENT;
if (y[i] > MAXEXTENT) y[i] = MAXEXTENT;
if (y[i] < -MAXEXTENT) y[i] = -MAXEXTENT;
}
return true;
}

static inline bool merc2lonlat(double * x, double * y , int point_count)
{
int i;
for(i=0; i<point_count; i++)
{
x[i] = (x[i] / MAXEXTENT) * 180;
y[i] = (y[i] / MAXEXTENT) * 180;
y[i] = R2D * (2 * std::atan(std::exp(y[i] * D2R)) - M_PI_by2);
if (x[i] > 180) x[i] = 180;
if (x[i] < -180) x[i] = -180;
if (y[i] > 85.0511) y[i] = 85.0511;
if (y[i] < -85.0511) y[i] = -85.0511;
}
return true;
}

}

#endif // MAPNIK_WELL_KNOWN_SRS_HPP
1 change: 1 addition & 0 deletions src/build.py
Expand Up @@ -100,6 +100,7 @@ def ldconfig(*args,**kwargs):

source = Split(
"""
well_known_srs.cpp
params.cpp
image_filter_types.cpp
miniz_png.cpp
Expand Down
2 changes: 1 addition & 1 deletion src/map.cpp
Expand Up @@ -62,7 +62,7 @@ IMPLEMENT_ENUM( aspect_fix_mode_e, aspect_fix_mode_strings )
Map::Map()
: width_(400),
height_(400),
srs_("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"),
srs_(MAPNIK_LONGLAT_PROJ),
buffer_size_(0),
aspectFixMode_(GROW_BBOX),
base_path_("") {}
Expand Down

0 comments on commit f8b1520

Please sign in to comment.