forked from osm2pgsql-dev/osm2pgsql
-
Notifications
You must be signed in to change notification settings - Fork 0
/
reprojection.cpp
138 lines (108 loc) · 3.99 KB
/
reprojection.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/* reprojection.c
*
* Convert OSM coordinates to another coordinate system for
* the database (usually convert lat/lon to Spherical Mercator
* so Mapnik doesn't have to).
*/
#include "config.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include "reprojection.hpp"
/** must match expire.tiles.c */
#define EARTH_CIRCUMFERENCE 40075016.68
namespace {
void latlon2merc(double *lat, double *lon)
{
if (*lat > 85.07)
*lat = 85.07;
else if (*lat < -85.07)
*lat = -85.07;
using namespace osmium::geom;
*lon = (*lon) * EARTH_CIRCUMFERENCE / 360.0;
*lat = log(tan(PI/4.0 + deg_to_rad(*lat) / 2.0)) * EARTH_CIRCUMFERENCE/(PI*2);
}
class latlon_reprojection_t : public reprojection
{
public:
osmium::geom::Coordinates reproject(osmium::Location loc) const override
{
return osmium::geom::Coordinates(loc.lon_without_check(),
loc.lat_without_check());
}
void target_to_tile(double *lat, double *lon) const override
{
latlon2merc(lat, lon);
}
int target_srs() const override { return PROJ_LATLONG; }
const char *target_desc() const override { return "Latlong"; }
};
class merc_reprojection_t : public reprojection
{
public:
osmium::geom::Coordinates reproject(osmium::Location loc) const override
{
/* The latitude co-ordinate is clipped at slightly larger than the 3857 'world'
* extent of +-85.0511 degrees to ensure that the points appear just outside the
* edge of the map. */
double lat = loc.lat_without_check();
double lon = loc.lon_without_check();
latlon2merc(&lat, &lon);
return osmium::geom::Coordinates(lon, lat);
}
void target_to_tile(double *, double *) const override
{ /* nothing */ }
int target_srs() const override { return PROJ_SPHERE_MERC; }
const char *target_desc() const override { return "Spherical Mercator"; }
};
class generic_reprojection_t : public reprojection
{
public:
generic_reprojection_t(int srs)
: m_target_srs(srs), pj_target(srs), pj_source(PROJ_LATLONG),
pj_tile("+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs")
{}
osmium::geom::Coordinates reproject(osmium::Location loc) const override
{
using namespace osmium::geom;
return transform(pj_source, pj_target,
Coordinates(deg_to_rad(loc.lon_without_check()),
deg_to_rad(loc.lat_without_check())));
}
void target_to_tile(double *lat, double *lon) const override
{
auto c = transform(pj_target, pj_tile, osmium::geom::Coordinates(*lon, *lat));
*lon = c.x;
*lat = c.y;
}
int target_srs() const override { return m_target_srs; }
const char *target_desc() const override { return pj_get_def(pj_target.get(), 0); }
private:
int m_target_srs;
osmium::geom::CRS pj_target;
/** The projection of the source data. Always lat/lon (EPSG:4326). */
osmium::geom::CRS pj_source;
/** The projection used for tiles. Currently this is fixed to be Spherical
* Mercator. You will usually have tiles in the same projection as used
* for PostGIS, but it is theoretically possible to have your PostGIS data
* in, say, lat/lon but still create tiles in Spherical Mercator.
*/
osmium::geom::CRS pj_tile;
};
} // anonymous namespace
reprojection *reprojection::create_projection(int srs)
{
switch (srs)
{
case PROJ_LATLONG: return new latlon_reprojection_t();
case PROJ_SPHERE_MERC: return new merc_reprojection_t();
}
return new generic_reprojection_t(srs);
}
void reprojection::coords_to_tile(double *tilex, double *tiley,
double lon, double lat, int map_width)
{
target_to_tile(&lat, &lon);
*tilex = map_width * (0.5 + lon / EARTH_CIRCUMFERENCE);
*tiley = map_width * (0.5 - lat / EARTH_CIRCUMFERENCE);
}