2006-10-17 19:26:35 +02:00
/*****************************************************************************
2012-02-02 02:53:35 +01:00
*
2006-10-17 19:26:35 +02:00
* This file is part of Mapnik ( c + + mapping toolkit )
*
2011-10-23 15:04:25 +02:00
* Copyright ( C ) 2011 Artem Pavlenko
2006-10-17 19:26:35 +02:00
*
* 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
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2008-02-04 12:54:07 +01:00
// mapnik
2011-10-19 16:29:24 +02:00
# include <mapnik/global.hpp>
2014-07-23 07:40:39 +02:00
# include <mapnik/box2d.hpp>
# include <mapnik/projection.hpp>
2006-10-17 19:26:35 +02:00
# include <mapnik/proj_transform.hpp>
2012-02-02 02:53:35 +01:00
# include <mapnik/coord.hpp>
2008-02-04 12:54:07 +01:00
# include <mapnik/utils.hpp>
2011-06-01 00:38:15 +02:00
2013-01-28 19:45:41 +01:00
# ifdef MAPNIK_USE_PROJ4
2008-02-04 12:54:07 +01:00
// proj4
2007-10-08 19:42:41 +02:00
# include <proj_api.h>
2013-01-28 19:45:41 +01:00
# endif
2007-10-08 19:42:41 +02:00
2011-06-01 00:38:15 +02:00
// stl
# include <vector>
2013-06-03 05:19:33 +02:00
# include <stdexcept>
2011-06-01 00:38:15 +02:00
2006-10-17 19:26:35 +02:00
namespace mapnik {
2012-02-02 02:53:35 +01:00
proj_transform : : proj_transform ( projection const & source ,
2010-06-02 13:03:30 +02:00
projection const & dest )
: source_ ( source ) ,
2013-01-28 07:47:32 +01:00
dest_ ( dest ) ,
is_source_longlat_ ( false ) ,
is_dest_longlat_ ( false ) ,
wgs84_to_merc_ ( false ) ,
merc_to_wgs84_ ( false )
2010-06-02 13:03:30 +02:00
{
2010-07-22 01:05:22 +02:00
is_source_equal_dest_ = ( source_ = = dest_ ) ;
2013-01-28 07:47:32 +01:00
if ( ! is_source_equal_dest_ )
2011-09-12 04:10:58 +02:00
{
2013-01-28 07:47:32 +01:00
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 ( ) ;
2013-01-28 21:10:24 +01:00
bool known_trans = false ;
2013-01-28 07:47:32 +01:00
if ( src_k & & dest_k )
{
2013-01-28 21:10:24 +01:00
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 ;
}
2013-01-28 07:47:32 +01:00
}
2013-01-28 21:10:24 +01:00
if ( ! known_trans )
2013-01-28 07:47:32 +01:00
{
2013-01-28 20:03:07 +01:00
# ifdef MAPNIK_USE_PROJ4
2013-01-28 07:47:32 +01:00
source_ . init_proj4 ( ) ;
dest_ . init_proj4 ( ) ;
2013-01-28 20:03:07 +01:00
# 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
2013-01-28 07:47:32 +01:00
}
2011-09-12 04:10:58 +02:00
}
2010-06-02 13:03:30 +02:00
}
2010-07-22 01:05:22 +02:00
bool proj_transform : : equal ( ) const
{
return is_source_equal_dest_ ;
}
2011-09-13 17:41:39 +02:00
bool proj_transform : : forward ( double & x , double & y , double & z ) const
{
return forward ( & x , & y , & z , 1 ) ;
}
2011-09-12 20:30:34 +02:00
bool proj_transform : : forward ( double * x , double * y , double * z , int point_count ) const
2010-06-02 13:03:30 +02:00
{
2011-09-12 04:10:58 +02:00
2010-07-22 01:05:22 +02:00
if ( is_source_equal_dest_ )
2010-06-02 13:03:30 +02:00
return true ;
2008-12-07 17:23:57 +01:00
2013-01-28 07:47:32 +01:00
if ( wgs84_to_merc_ )
{
return lonlat2merc ( x , y , point_count ) ;
}
else if ( merc_to_wgs84_ )
{
return merc2lonlat ( x , y , point_count ) ;
2011-09-12 04:10:58 +02:00
}
2012-02-02 02:53:35 +01:00
2013-01-28 19:45:41 +01:00
# ifdef MAPNIK_USE_PROJ4
2012-02-02 02:53:35 +01:00
if ( is_source_longlat_ )
2010-06-02 13:03:30 +02:00
{
2011-09-12 20:30:34 +02:00
int i ;
for ( i = 0 ; i < point_count ; i + + ) {
x [ i ] * = DEG_TO_RAD ;
y [ i ] * = DEG_TO_RAD ;
}
2010-06-02 13:03:30 +02:00
}
2010-07-22 01:05:22 +02:00
2013-01-29 09:36:38 +01:00
if ( pj_transform ( source_ . proj_ , dest_ . proj_ , point_count ,
0 , x , y , z ) ! = 0 )
2013-01-28 19:45:41 +01:00
{
2013-01-29 09:36:38 +01:00
return false ;
2013-01-28 19:45:41 +01:00
}
2011-06-01 00:38:15 +02:00
2010-08-10 20:18:31 +02:00
if ( is_dest_longlat_ )
2010-06-02 13:03:30 +02:00
{
2011-09-13 17:41:39 +02:00
int i ;
2011-09-12 20:30:34 +02:00
for ( i = 0 ; i < point_count ; i + + ) {
x [ i ] * = RAD_TO_DEG ;
y [ i ] * = RAD_TO_DEG ;
}
2010-06-02 13:03:30 +02:00
}
2013-01-28 19:45:41 +01:00
# endif
2010-06-02 13:03:30 +02:00
return true ;
2011-09-12 20:30:34 +02:00
}
bool proj_transform : : backward ( double * x , double * y , double * z , int point_count ) const
2010-06-02 13:03:30 +02:00
{
2010-07-22 01:05:22 +02:00
if ( is_source_equal_dest_ )
2006-10-17 19:26:35 +02:00
return true ;
2011-06-01 00:38:15 +02:00
2013-01-28 07:47:32 +01:00
if ( wgs84_to_merc_ )
{
return merc2lonlat ( x , y , point_count ) ;
}
else if ( merc_to_wgs84_ )
{
return lonlat2merc ( x , y , point_count ) ;
2011-09-12 04:10:58 +02:00
}
2013-01-28 19:45:41 +01:00
# ifdef MAPNIK_USE_PROJ4
2010-08-10 20:18:31 +02:00
if ( is_dest_longlat_ )
2010-06-02 13:03:30 +02:00
{
2011-09-12 20:30:34 +02:00
int i ;
for ( i = 0 ; i < point_count ; i + + ) {
x [ i ] * = DEG_TO_RAD ;
y [ i ] * = DEG_TO_RAD ;
}
2009-05-24 08:31:32 +02:00
}
2011-06-01 00:38:15 +02:00
2013-01-29 09:36:38 +01:00
if ( pj_transform ( dest_ . proj_ , source_ . proj_ , point_count ,
0 , x , y , z ) ! = 0 )
2012-09-19 14:51:53 +02:00
{
2013-01-29 09:36:38 +01:00
return false ;
2012-09-19 14:51:53 +02:00
}
2011-06-01 00:38:15 +02:00
2010-08-10 20:18:31 +02:00
if ( is_source_longlat_ )
2009-05-24 08:31:32 +02:00
{
2011-09-12 20:30:34 +02:00
int i ;
for ( i = 0 ; i < point_count ; i + + ) {
x [ i ] * = RAD_TO_DEG ;
y [ i ] * = RAD_TO_DEG ;
}
2009-05-24 08:31:32 +02:00
}
2013-01-28 19:45:41 +01:00
# endif
2010-06-02 13:03:30 +02:00
return true ;
}
2011-09-12 20:30:34 +02:00
bool proj_transform : : backward ( double & x , double & y , double & z ) const
{
return backward ( & x , & y , & z , 1 ) ;
}
2011-04-13 21:40:44 +02:00
bool proj_transform : : forward ( box2d < double > & box ) const
{
if ( is_source_equal_dest_ )
return true ;
2011-06-01 00:38:15 +02:00
2011-04-13 21:40:44 +02:00
double minx = box . minx ( ) ;
double miny = box . miny ( ) ;
double maxx = box . maxx ( ) ;
double maxy = box . maxy ( ) ;
double z = 0.0 ;
2011-04-13 23:03:15 +02:00
if ( ! forward ( minx , miny , z ) )
return false ;
if ( ! forward ( maxx , maxy , z ) )
return false ;
2011-04-13 21:40:44 +02:00
box . init ( minx , miny , maxx , maxy ) ;
2011-04-13 23:03:15 +02:00
return true ;
2012-02-02 02:53:35 +01:00
}
2011-06-01 00:38:15 +02:00
2011-04-13 21:40:44 +02:00
bool proj_transform : : backward ( box2d < double > & box ) const
{
if ( is_source_equal_dest_ )
return true ;
double minx = box . minx ( ) ;
double miny = box . miny ( ) ;
double maxx = box . maxx ( ) ;
double maxy = box . maxy ( ) ;
double z = 0.0 ;
2011-04-13 23:03:15 +02:00
if ( ! backward ( minx , miny , z ) )
return false ;
if ( ! backward ( maxx , maxy , z ) )
return false ;
2011-04-13 21:40:44 +02:00
box . init ( minx , miny , maxx , maxy ) ;
2011-04-13 23:03:15 +02:00
return true ;
2011-04-13 21:40:44 +02:00
}
2011-06-01 00:38:15 +02:00
void envelope_points ( std : : vector < coord < double , 2 > > & coords , box2d < double > & env , int points )
{
double width = env . width ( ) ;
double height = env . height ( ) ;
2012-02-02 02:53:35 +01:00
2011-06-01 00:38:15 +02:00
int steps ;
2012-02-02 02:53:35 +01:00
2011-06-01 00:38:15 +02:00
if ( points < = 4 ) {
steps = 0 ;
} else {
2013-01-17 22:53:48 +01:00
steps = static_cast < int > ( std : : ceil ( ( points - 4 ) / 4.0 ) ) ;
2011-06-01 00:38:15 +02:00
}
2012-02-02 02:53:35 +01:00
2011-06-01 00:38:15 +02:00
steps + = 1 ;
double xstep = width / steps ;
double ystep = height / steps ;
2012-02-02 02:53:35 +01:00
2011-06-01 00:38:15 +02:00
for ( int i = 0 ; i < = steps ; i + + ) {
coords . push_back ( coord < double , 2 > ( env . minx ( ) + i * xstep , env . miny ( ) ) ) ;
coords . push_back ( coord < double , 2 > ( env . minx ( ) + i * xstep , env . maxy ( ) ) ) ;
}
for ( int i = 1 ; i < steps ; i + + ) {
coords . push_back ( coord < double , 2 > ( env . minx ( ) , env . miny ( ) + i * ystep ) ) ;
coords . push_back ( coord < double , 2 > ( env . maxx ( ) , env . miny ( ) + i * ystep ) ) ;
}
}
box2d < double > calculate_bbox ( std : : vector < coord < double , 2 > > & points ) {
std : : vector < coord < double , 2 > > : : iterator it = points . begin ( ) ;
std : : vector < coord < double , 2 > > : : iterator it_end = points . end ( ) ;
2012-02-02 02:53:35 +01:00
2011-06-01 00:38:15 +02:00
box2d < double > env ( * it , * ( + + it ) ) ;
for ( ; it ! = it_end ; + + it ) {
env . expand_to_include ( * it ) ;
}
return env ;
}
/* More robust, but expensive, bbox transform
* in the face of proj4 out of bounds conditions .
* Can result in 20 - > 10 r / s performance hit .
* Alternative is to provide proper clipping box
2012-02-02 02:53:35 +01:00
* in the target srs by setting map ' maximum - extent '
2011-06-01 00:38:15 +02:00
*/
bool proj_transform : : backward ( box2d < double > & env , int points ) const
{
if ( is_source_equal_dest_ )
return true ;
2012-02-02 02:53:35 +01:00
2011-06-01 00:38:15 +02:00
std : : vector < coord < double , 2 > > coords ;
envelope_points ( coords , env , points ) ;
2012-02-02 02:53:35 +01:00
2011-06-01 00:38:15 +02:00
double z ;
for ( std : : vector < coord < double , 2 > > : : iterator it = coords . begin ( ) ; it ! = coords . end ( ) ; + + it ) {
z = 0 ;
if ( ! backward ( it - > x , it - > y , z ) ) {
return false ;
}
}
box2d < double > result = calculate_bbox ( coords ) ;
2012-02-02 02:53:35 +01:00
2011-06-01 00:38:15 +02:00
env . re_center ( result . center ( ) . x , result . center ( ) . y ) ;
env . height ( result . height ( ) ) ;
env . width ( result . width ( ) ) ;
return true ;
}
bool proj_transform : : forward ( box2d < double > & env , int points ) const
{
if ( is_source_equal_dest_ )
return true ;
2012-02-02 02:53:35 +01:00
2011-06-01 00:38:15 +02:00
std : : vector < coord < double , 2 > > coords ;
envelope_points ( coords , env , points ) ;
2012-02-02 02:53:35 +01:00
2011-06-01 00:38:15 +02:00
double z ;
for ( std : : vector < coord < double , 2 > > : : iterator it = coords . begin ( ) ; it ! = coords . end ( ) ; + + it ) {
z = 0 ;
if ( ! forward ( it - > x , it - > y , z ) ) {
return false ;
}
}
box2d < double > result = calculate_bbox ( coords ) ;
2012-02-02 02:53:35 +01:00
2011-06-01 00:38:15 +02:00
env . re_center ( result . center ( ) . x , result . center ( ) . y ) ;
env . height ( result . height ( ) ) ;
env . width ( result . width ( ) ) ;
return true ;
}
2010-06-02 13:03:30 +02:00
mapnik : : projection const & proj_transform : : source ( ) const
{
return source_ ;
}
mapnik : : projection const & proj_transform : : dest ( ) const
{
return dest_ ;
}
2012-02-02 02:53:35 +01:00
2006-10-17 19:26:35 +02:00
}