Merge pull request #1705 from mapnik/lazy-proj4

Lazy proj4 - make projections faster (closes #1703)
This commit is contained in:
Dane Springmeyer 2013-01-29 00:12:08 -08:00
commit f27312cb9a
18 changed files with 432 additions and 183 deletions

View file

@ -318,6 +318,7 @@ PathVariable.PathAccept),
BoolVariable('TIFF', 'Build Mapnik with TIFF read and write support', 'True'),
PathVariable('TIFF_INCLUDES', 'Search path for libtiff include files', '/usr/include', PathVariable.PathAccept),
PathVariable('TIFF_LIBS', 'Search path for libtiff library files', '/usr/' + LIBDIR_SCHEMA_DEFAULT, PathVariable.PathAccept),
BoolVariable('PROJ', 'Build Mapnik with proj4 support to enable transformations between many different projections', 'True'),
PathVariable('PROJ_INCLUDES', 'Search path for PROJ.4 include files', '/usr/include', PathVariable.PathAccept),
PathVariable('PROJ_LIBS', 'Search path for PROJ.4 library files', '/usr/' + LIBDIR_SCHEMA_DEFAULT, PathVariable.PathAccept),
('PKG_CONFIG_PATH', 'Use this path to point pkg-config to .pc files instead of the PKG_CONFIG_PATH environment setting',''),
@ -1062,7 +1063,7 @@ if not preconfigured:
# Adding the required prerequisite library directories to the include path for
# compiling and the library path for linking, respectively.
for required in ('PROJ', 'ICU', 'SQLITE', 'LTDL'):
for required in ('ICU', 'SQLITE', 'LTDL'):
inc_path = env['%s_INCLUDES' % required]
lib_path = env['%s_LIBS' % required]
env.AppendUnique(CPPPATH = os.path.realpath(inc_path))
@ -1101,6 +1102,16 @@ if not preconfigured:
else:
env['SKIPPED_DEPS'].extend(['jpeg'])
if env['PROJ']:
env.Append(CXXFLAGS = '-DMAPNIK_USE_PROJ4')
LIBSHEADERS.append(['proj', 'proj_api.h', True,'C'])
inc_path = env['%s_INCLUDES' % 'PROJ']
lib_path = env['%s_LIBS' % 'PROJ']
env.AppendUnique(CPPPATH = os.path.realpath(inc_path))
env.AppendUnique(LIBPATH = os.path.realpath(lib_path))
else:
env['SKIPPED_DEPS'].extend(['proj'])
if env['PNG']:
env.Append(CXXFLAGS = '-DHAVE_PNG')
LIBSHEADERS.append(['png', 'png.h', True,'C'])

View file

@ -36,39 +36,43 @@ typedef clock_type::duration dur;
template <typename T>
void benchmark(T test, std::string const& name)
{
bool should_run_test = true;
if (!test_set.empty()) {
should_run_test = test_set.find(test_num) != test_set.end();
}
if (should_run_test) {
if (!test.validate()) {
std::clog << "test did not validate: " << name << "\n";
//throw std::runtime_error(std::string("test did not validate: ") + name);
try {
bool should_run_test = true;
if (!test_set.empty()) {
should_run_test = test_set.find(test_num) != test_set.end();
}
if (dry_run) {
std::clog << test_num << ") " << (test.threads_ ? "threaded -> ": "")
<< name << "\n";
} else {
process_cpu_clock::time_point start;
dur elapsed;
if (test.threads_ > 0) {
boost::thread_group tg;
for (unsigned i=0;i<test.threads_;++i)
{
tg.create_thread(test);
}
start = process_cpu_clock::now();
tg.join_all();
elapsed = process_cpu_clock::now() - start;
} else {
start = process_cpu_clock::now();
test();
elapsed = process_cpu_clock::now() - start;
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);
}
if (dry_run) {
std::clog << test_num << ") " << (test.threads_ ? "threaded -> ": "")
<< name << "\n";
} else {
process_cpu_clock::time_point start;
dur elapsed;
if (test.threads_ > 0) {
boost::thread_group tg;
for (unsigned i=0;i<test.threads_;++i)
{
tg.create_thread(test);
}
start = process_cpu_clock::now();
tg.join_all();
elapsed = process_cpu_clock::now() - start;
} else {
start = process_cpu_clock::now();
test();
elapsed = process_cpu_clock::now() - start;
}
std::clog << test_num << ") " << (test.threads_ ? "threaded -> ": "")
<< name << ": "
<< boost::chrono::duration_cast<milliseconds>(elapsed) << "\n";
}
std::clog << test_num << ") " << (test.threads_ ? "threaded -> ": "")
<< name << ": "
<< boost::chrono::duration_cast<milliseconds>(elapsed) << "\n";
}
} catch (std::exception const& ex) {
std::clog << "test runner did not complete: " << ex.what() << "\n";
}
test_num++;
}
@ -281,23 +285,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;
@ -309,17 +316,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;
}
}
}
@ -397,12 +404,13 @@ 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);
{
// 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,true);
benchmark(runner,"lonlat -> merc coord transformation (epsg)");
}
@ -410,7 +418,7 @@ int main( int argc, char** argv)
test6 runner(100000000,100,
"+init=epsg:3857",
"+init=epsg:4326",
to,from);
to,from,true);
benchmark(runner,"merc -> lonlat coord transformation (epsg)");
}
@ -418,7 +426,7 @@ int main( int argc, char** argv)
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);
from,to,true);
benchmark(runner,"lonlat -> merc coord transformation (literal)");
}
@ -426,7 +434,7 @@ int main( int argc, char** argv)
test6 runner(100000000,100,
"+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,true);
benchmark(runner,"merc -> lonlat coord transformation (literal)");
}

View file

@ -156,7 +156,7 @@ void feature_style_processor<Processor>::apply()
try
{
projection proj(m_.srs());
projection proj(m_.srs(),true);
double scale_denom = mapnik::scale_denominator(m_,proj.is_geographic());
scale_denom *= scale_factor_;
@ -190,7 +190,7 @@ void feature_style_processor<Processor>::apply(mapnik::layer const& lyr, std::se
p.start_map_processing(m_);
try
{
projection proj(m_.srs());
projection proj(m_.srs(),true);
double scale_denom = mapnik::scale_denominator(m_,proj.is_geographic());
scale_denom *= scale_factor_;
@ -234,7 +234,7 @@ void feature_style_processor<Processor>::apply_to_layer(layer const& lay, Proces
progress_timer layer_timer(std::clog, "rendering total for layer: '" + lay.name() + "'");
#endif
projection proj1(lay.srs());
projection proj1(lay.srs(),true);
proj_transform prj_trans(proj0,proj1);
#if defined(RENDERING_STATS)

View file

@ -26,6 +26,7 @@
// mapnik
#include <mapnik/feature.hpp>
#include <mapnik/datasource.hpp>
#include <mapnik/well_known_srs.hpp>
// stl
#include <vector>
@ -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);

View file

@ -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>
@ -105,7 +106,7 @@ public:
* @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.
*

View file

@ -49,12 +49,13 @@ public:
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_;
};
}

View file

@ -24,16 +24,11 @@
#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>
@ -52,7 +47,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();
@ -61,26 +58,22 @@ public:
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)
static boost::mutex mutex_;
#endif
bool defer_proj_init_;
mutable bool is_geographic_;
mutable void * proj_;
mutable void * proj_ctx_;
};
}

View file

@ -0,0 +1,93 @@
/*****************************************************************************
*
* 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 double MAX_LATITUDE = R2D * (2 * std::atan(std::exp(180 * D2R)) - M_PI_by2);
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)
{
for(int i=0; i<point_count; i++) {
if (x[i] > 180) x[i] = 180;
else if (x[i] < -180) x[i] = -180;
if (y[i] > MAX_LATITUDE) y[i] = MAX_LATITUDE;
else if (y[i] < -MAX_LATITUDE) y[i] = -MAX_LATITUDE;
x[i] = x[i] * MAXEXTENTby180;
y[i] = std::log(std::tan((90 + y[i]) * M_PIby360)) * R2D;
y[i] = y[i] * MAXEXTENTby180;
}
return true;
}
static inline bool merc2lonlat(double * x, double * y , int point_count)
{
for(int i=0; i<point_count; i++)
{
if (x[i] > MAXEXTENT) x[i] = MAXEXTENT;
else if (x[i] < -MAXEXTENT) x[i] = -MAXEXTENT;
if (y[i] > MAXEXTENT) y[i] = MAXEXTENT;
else if (y[i] < -MAXEXTENT) y[i] = -MAXEXTENT;
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);
}
return true;
}
}
#endif // MAPNIK_WELL_KNOWN_SRS_HPP

View file

@ -100,6 +100,7 @@ else: # unix, non-macos
source = Split(
"""
well_known_srs.cpp
params.cpp
image_filter_types.cpp
miniz_png.cpp

View file

@ -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_("") {}

View file

@ -26,36 +26,55 @@
#include <mapnik/coord.hpp>
#include <mapnik/utils.hpp>
#ifdef MAPNIK_USE_PROJ4
// proj4
#include <proj_api.h>
#endif
// stl
#include <vector>
static const float MAXEXTENT = 20037508.34;
static const float M_PI_by2 = M_PI / 2;
static const float D2R = M_PI / 180;
static const float R2D = 180 / M_PI;
static const float M_PIby360 = M_PI / 360;
static const float MAXEXTENTby180 = MAXEXTENT/180;
namespace mapnik {
proj_transform::proj_transform(projection const& source,
projection const& dest)
: source_(source),
dest_(dest)
dest_(dest),
is_source_longlat_(false),
is_dest_longlat_(false),
wgs84_to_merc_(false),
merc_to_wgs84_(false)
{
is_source_longlat_ = source_.is_geographic();
is_dest_longlat_ = dest_.is_geographic();
is_source_equal_dest_ = (source_ == dest_);
if (source.params() == "+init=epsg:3857" && dest.params() == "+init=epsg:4326")
if (!is_source_equal_dest_)
{
wgs84_to_merc_ = true;
}
else
{
wgs84_to_merc_ = false;
is_source_longlat_ = source_.is_geographic();
is_dest_longlat_ = dest_.is_geographic();
boost::optional<well_known_srs_e> src_k = source.well_known();
boost::optional<well_known_srs_e> dest_k = dest.well_known();
bool known_trans = false;
if (src_k && dest_k)
{
if (*src_k == WGS_84 && *dest_k == G_MERC)
{
wgs84_to_merc_ = true;
known_trans = true;
}
else if (*src_k == G_MERC && *dest_k == WGS_84)
{
merc_to_wgs84_ = true;
known_trans = true;
}
}
if (!known_trans)
{
#ifdef MAPNIK_USE_PROJ4
source_.init_proj4();
dest_.init_proj4();
#else
throw std::runtime_error(std::string("Cannot initialize proj_transform for given projections without proj4 support (-DMAPNIK_USE_PROJ4): '") + source_.params() + "'->'" + dest_.params() + "'");
#endif
}
}
}
@ -64,7 +83,6 @@ bool proj_transform::equal() const
return is_source_equal_dest_;
}
bool proj_transform::forward (double & x, double & y , double & z) const
{
return forward(&x, &y, &z, 1);
@ -76,20 +94,16 @@ bool proj_transform::forward (double * x, double * y , double * z, int point_cou
if (is_source_equal_dest_)
return true;
if (wgs84_to_merc_) {
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 * atan(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;
if (wgs84_to_merc_)
{
return lonlat2merc(x,y,point_count);
}
else if (merc_to_wgs84_)
{
return merc2lonlat(x,y,point_count);
}
#ifdef MAPNIK_USE_PROJ4
if (is_source_longlat_)
{
int i;
@ -99,7 +113,7 @@ bool proj_transform::forward (double * x, double * y , double * z, int point_cou
}
}
do {
{
#if defined(MAPNIK_THREADSAFE) && PJ_VERSION < 480
mutex::scoped_lock lock(projection::mutex_);
#endif
@ -108,7 +122,7 @@ bool proj_transform::forward (double * x, double * y , double * z, int point_cou
{
return false;
}
} while(false);
}
if (is_dest_longlat_)
{
@ -118,7 +132,7 @@ bool proj_transform::forward (double * x, double * y , double * z, int point_cou
y[i] *= RAD_TO_DEG;
}
}
#endif
return true;
}
@ -127,20 +141,16 @@ bool proj_transform::backward (double * x, double * y , double * z, int point_co
if (is_source_equal_dest_)
return true;
if (wgs84_to_merc_) {
int i;
for(i=0; i<point_count; i++) {
x[i] = x[i] * MAXEXTENTby180;
y[i] = std::log(tan((90 + y[i]) * M_PIby360)) / D2R;
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;
if (wgs84_to_merc_)
{
return merc2lonlat(x,y,point_count);
}
else if (merc_to_wgs84_)
{
return lonlat2merc(x,y,point_count);
}
#ifdef MAPNIK_USE_PROJ4
if (is_dest_longlat_)
{
int i;
@ -170,7 +180,7 @@ bool proj_transform::backward (double * x, double * y , double * z, int point_co
y[i] *= RAD_TO_DEG;
}
}
#endif
return true;
}

View file

@ -24,27 +24,51 @@
#include <mapnik/projection.hpp>
#include <mapnik/utils.hpp>
#include <mapnik/util/trim.hpp>
#include <mapnik/well_known_srs.hpp>
#ifdef MAPNIK_USE_PROJ4
// proj4
#include <proj_api.h>
#if defined(MAPNIK_THREADSAFE) && PJ_VERSION < 480
#include <boost/thread/mutex.hpp>
#warning mapnik is building against < proj 4.8, reprojection will be faster if you use >= 4.8
static boost::mutex mutex_;
#endif
#endif
namespace mapnik {
#if defined(MAPNIK_THREADSAFE) && PJ_VERSION < 480
#warning mapnik is building against < proj 4.8, reprojection will be faster if you use >= 4.8
boost::mutex projection::mutex_;
#endif
projection::projection(std::string const& params)
: params_(params)
projection::projection(std::string const& params, bool defer_proj_init)
: params_(params),
defer_proj_init_(defer_proj_init),
proj_(NULL),
proj_ctx_(NULL)
{
init();
boost::optional<bool> is_known = is_known_geographic(params_);
if (is_known){
is_geographic_ = *is_known;
}
else
{
#ifdef MAPNIK_USE_PROJ4
init_proj4();
#else
throw std::runtime_error(std::string("Cannot initialize projection '") + params_ + " ' without proj4 support (-DMAPNIK_USE_PROJ4)");
#endif
}
if (!defer_proj_init_) init_proj4();
}
projection::projection(projection const& rhs)
: params_(rhs.params_)
: params_(rhs.params_),
defer_proj_init_(rhs.defer_proj_init_),
is_geographic_(rhs.is_geographic_),
proj_(NULL),
proj_ctx_(NULL)
{
init();
if (!rhs.defer_proj_init_) init_proj4();
}
projection& projection::operator=(projection const& rhs)
@ -64,6 +88,31 @@ bool projection::operator!=(const projection& other) const
return !(*this == other);
}
void projection::init_proj4() const
{
#ifdef MAPNIK_USE_PROJ4
if (!proj_)
{
#if PJ_VERSION >= 480
proj_ctx_ = pj_ctx_alloc();
proj_ = pj_init_plus_ctx(proj_ctx_, params_.c_str());
if (!proj_)
{
if (proj_ctx_) pj_ctx_free(proj_ctx_);
throw proj_init_error(params_);
}
#else
#if defined(MAPNIK_THREADSAFE)
mutex::scoped_lock lock(mutex_);
#endif
proj_ = pj_init_plus(params_.c_str());
if (!proj_) throw proj_init_error(params_);
#endif
is_geographic_ = pj_is_latlong(proj_) ? true : false;
}
#endif
}
bool projection::is_initialized() const
{
return proj_ ? true : false;
@ -74,6 +123,11 @@ bool projection::is_geographic() const
return is_geographic_;
}
boost::optional<well_known_srs_e> projection::well_known() const
{
return is_well_known_srs(params_);
}
std::string const& projection::params() const
{
return params_;
@ -81,9 +135,14 @@ std::string const& projection::params() const
void projection::forward(double & x, double &y ) const
{
#if defined(MAPNIK_THREADSAFE) && PJ_VERSION < 480
#ifdef MAPNIK_USE_PROJ4
if (!proj_)
{
throw std::runtime_error("projection::forward not supported unless proj4 is initialized");
}
#if defined(MAPNIK_THREADSAFE) && PJ_VERSION < 480
mutex::scoped_lock lock(mutex_);
#endif
#endif
projUV p;
p.u = x * DEG_TO_RAD;
p.v = y * DEG_TO_RAD;
@ -95,13 +154,22 @@ void projection::forward(double & x, double &y ) const
x *=RAD_TO_DEG;
y *=RAD_TO_DEG;
}
#else
throw std::runtime_error("projection::forward not supported without proj4 support (-DMAPNIK_USE_PROJ4)");
#endif
}
void projection::inverse(double & x,double & y) const
{
#if defined(MAPNIK_THREADSAFE) && PJ_VERSION < 480
#ifdef MAPNIK_USE_PROJ4
if (!proj_)
{
throw std::runtime_error("projection::inverse not supported unless proj4 is initialized");
}
#if defined(MAPNIK_THREADSAFE) && PJ_VERSION < 480
mutex::scoped_lock lock(mutex_);
#endif
#endif
if (is_geographic_)
{
x *=DEG_TO_RAD;
@ -113,50 +181,35 @@ void projection::inverse(double & x,double & y) const
p = pj_inv(p,proj_);
x = RAD_TO_DEG * p.u;
y = RAD_TO_DEG * p.v;
#else
throw std::runtime_error("projection::inverse not supported without proj4 support (-DMAPNIK_USE_PROJ4)");
#endif
}
projection::~projection()
{
#if defined(MAPNIK_THREADSAFE) && PJ_VERSION < 480
mutex::scoped_lock lock(mutex_);
#endif
if (proj_) pj_free(proj_);
#if PJ_VERSION >= 480
if (proj_ctx_) pj_ctx_free(proj_ctx_);
#endif
}
void projection::init()
{
#if defined(MAPNIK_THREADSAFE) && PJ_VERSION < 480
mutex::scoped_lock lock(mutex_);
#endif
#if PJ_VERSION >= 480
proj_ctx_ = pj_ctx_alloc();
proj_ = pj_init_plus_ctx(proj_ctx_, params_.c_str());
if (!proj_)
{
#ifdef MAPNIK_USE_PROJ4
#if defined(MAPNIK_THREADSAFE) && PJ_VERSION < 480
mutex::scoped_lock lock(mutex_);
#endif
if (proj_) pj_free(proj_);
#if PJ_VERSION >= 480
if (proj_ctx_) pj_ctx_free(proj_ctx_);
throw proj_init_error(params_);
}
#else
proj_ = pj_init_plus(params_.c_str());
if (!proj_) throw proj_init_error(params_);
#endif
#endif
is_geographic_ = pj_is_latlong(proj_) ? true : false;
}
std::string projection::expanded() const
{
if (proj_) {
return mapnik::util::trim_copy(pj_get_def( proj_, 0 ));
}
return std::string("");
#ifdef MAPNIK_USE_PROJ4
if (proj_) return mapnik::util::trim_copy(pj_get_def( proj_, 0 ));
#endif
return params_;
}
void projection::swap(projection& rhs)
{
std::swap(params_,rhs.params_);
init();
}
}

82
src/well_known_srs.cpp Normal file
View file

@ -0,0 +1,82 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2011 Artem Pavlenko
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
// mapnik
#include <mapnik/well_known_srs.hpp>
#include <mapnik/util/trim.hpp>
// boost
#include <boost/optional.hpp>
namespace mapnik {
static const char * well_known_srs_strings[] = {
"mapnik-longlat",
"mapnik-gmerc",
""
};
boost::optional<well_known_srs_e> is_well_known_srs(std::string const& srs)
{
if (srs == "+init=epsg:4326" || srs == MAPNIK_LONGLAT_PROJ)
{
return boost::optional<well_known_srs_e>(mapnik::WGS_84);
}
else if (srs == "+init=epsg:3857" || srs == MAPNIK_GMERC_PROJ)
{
return boost::optional<well_known_srs_e>(mapnik::G_MERC);
}
return boost::optional<well_known_srs_e>();
}
boost::optional<bool> is_known_geographic(std::string const& srs)
{
std::string trimmed = util::trim_copy(srs);
if (trimmed == "+init=epsg:3857")
{
return boost::optional<bool>(false);
}
else if (trimmed == "+init=epsg:4326")
{
return boost::optional<bool>(true);
}
else if (srs.find("+proj=") != std::string::npos)
{
if ((srs.find("+proj=longlat") != std::string::npos) ||
(srs.find("+proj=latlong") != std::string::npos) ||
(srs.find("+proj=lonlat") != std::string::npos) ||
(srs.find("+proj=latlon") != std::string::npos)
)
{
return boost::optional<bool>(true);
}
else
{
return boost::optional<bool>(false);
}
}
return boost::optional<bool>();
}
IMPLEMENT_ENUM( well_known_srs_e, well_known_srs_strings )
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 47 KiB

View file

@ -45,16 +45,6 @@ if 'shape' in mapnik.DatasourceCache.plugin_names():
m.zoom_all()
m.query_point(0,9999999999999999,9999999999999999)
# invalid coords for back projecting
@raises(RuntimeError)
def test_map_query_throw6():
m = mapnik.Map(256,256)
mapnik.load_map(m,'../data/good_maps/merc2wgs84_reprojection.xml')
wgs84_bounds = mapnik.Box2d(-180,-90,180,90)
m.maximum_extent = wgs84_bounds
m.zoom_all()
m.query_point(0,-180,-90)
def test_map_query_works1():
m = mapnik.Map(256,256)
mapnik.load_map(m,'../data/good_maps/wgs842merc_reprojection.xml')

View file

@ -128,17 +128,19 @@ def test_render_points():
lr_lonlat = mapnik.Coord(143.40,-38.80)
# render for different projections
projs = {
'google': '+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',
'latlon': '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs',
'merc': '+proj=merc +datum=WGS84 +k=1.0 +units=m +over +no_defs',
'google': '+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',
'utm': '+proj=utm +zone=54 +datum=WGS84'
}
for projdescr in projs.iterkeys():
m = mapnik.Map(1000, 500, projs[projdescr])
m.append_style('places_labels',s)
m.layers.append(lyr)
p = mapnik.Projection(projs[projdescr])
m.zoom_to_box(p.forward(mapnik.Box2d(ul_lonlat,lr_lonlat)))
dest_proj = mapnik.Projection(projs[projdescr])
src_proj = mapnik.Projection('+init=epsg:4326')
tr = mapnik.ProjTransform(src_proj,dest_proj)
m.zoom_to_box(tr.forward(mapnik.Box2d(ul_lonlat,lr_lonlat)))
# Render to SVG so that it can be checked how many points are there with string comparison
svg_file = os.path.join(tempfile.gettempdir(), 'mapnik-render-points-%s.svg' % projdescr)
mapnik.render_to_file(m, svg_file)

View file

@ -1,7 +1,7 @@
#coding=utf8
import os
import mapnik
from utilities import execution_path
from utilities import execution_path, run_all
from nose.tools import *
def setup():
@ -10,7 +10,8 @@ def setup():
os.chdir(execution_path('.'))
if 'shape' in mapnik.DatasourceCache.plugin_names():
@raises(RuntimeError)
#@raises(RuntimeError)
def test_zoom_all_will_fail():
m = mapnik.Map(512,512)
mapnik.load_map(m,'../data/good_maps/wgs842merc_reprojection.xml')
@ -22,13 +23,15 @@ if 'shape' in mapnik.DatasourceCache.plugin_names():
merc_bounds = mapnik.Box2d(-20037508.34,-20037508.34,20037508.34,20037508.34)
m.maximum_extent = merc_bounds
m.zoom_all()
eq_(m.envelope(),merc_bounds)
# note - fixAspectRatio is being called, then re-clipping to maxextent
# which makes this hard to predict
#eq_(m.envelope(),merc_bounds)
m = mapnik.Map(512,512)
mapnik.load_map(m,'../data/good_maps/wgs842merc_reprojection.xml')
merc_bounds = mapnik.Box2d(-20037508.34,-20037508.34,20037508.34,20037508.34)
m.zoom_to_box(merc_bounds)
eq_(m.envelope(),merc_bounds)
#m = mapnik.Map(512,512)
#mapnik.load_map(m,'../data/good_maps/wgs842merc_reprojection.xml')
#merc_bounds = mapnik.Box2d(-20037508.34,-20037508.34,20037508.34,20037508.34)
#m.zoom_to_box(merc_bounds)
#eq_(m.envelope(),merc_bounds)
def test_visual_zoom_all_rendering1():
@ -86,4 +89,4 @@ if 'shape' in mapnik.DatasourceCache.plugin_names():
if __name__ == "__main__":
setup()
[eval(run)() for run in dir() if 'test_' in run]
run_all(eval(x) for x in dir() if x.startswith("test_"))