forked from osm2pgsql-dev/osm2pgsql
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathreprojection-generic-proj6.cpp
More file actions
128 lines (101 loc) · 3.79 KB
/
Copy pathreprojection-generic-proj6.cpp
File metadata and controls
128 lines (101 loc) · 3.79 KB
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
#include "format.hpp"
#include "reprojection.hpp"
#include <osmium/geom/coordinates.hpp>
#include <osmium/geom/util.hpp>
#include <proj.h>
namespace {
/**
* Generic projection using proj library (version 6 and above).
*/
class generic_reprojection_t : public reprojection
{
public:
explicit generic_reprojection_t(int srs)
: m_target_srs(srs), m_context(proj_context_create())
{
assert(m_context);
m_transformation = create_transformation(PROJ_LATLONG, srs);
m_transformation.reset(proj_normalize_for_visualization(
m_context.get(), m_transformation.get()));
if (!m_transformation) {
throw std::runtime_error{
"Invalid projection '{}': {}"_format(srs, errormsg())};
}
m_transformation_tile = create_transformation(PROJ_SPHERE_MERC, srs);
}
osmium::geom::Coordinates reproject(osmium::Location loc) const override
{
return transform(m_transformation.get(),
osmium::geom::Coordinates{loc.lon_without_check(),
loc.lat_without_check()});
}
osmium::geom::Coordinates
target_to_tile(osmium::geom::Coordinates coords) const override
{
return transform(m_transformation_tile.get(), coords);
}
int target_srs() const noexcept override { return m_target_srs; }
char const *target_desc() const noexcept override { return ""; }
private:
struct pj_context_deleter_t
{
void operator()(PJ_CONTEXT *ctx) const noexcept
{
proj_context_destroy(ctx);
}
};
struct pj_deleter_t
{
void operator()(PJ *p) const noexcept { proj_destroy(p); }
};
char const *errormsg() const noexcept
{
return proj_errno_string(proj_context_errno(m_context.get()));
}
std::unique_ptr<PJ, pj_deleter_t> create_transformation(int from,
int to) const
{
std::string const source = "epsg:{}"_format(from);
std::string const target = "epsg:{}"_format(to);
std::unique_ptr<PJ, pj_deleter_t> trans{proj_create_crs_to_crs(
m_context.get(), source.c_str(), target.c_str(), nullptr)};
if (!trans) {
throw std::runtime_error{
"Invalid projection from {} to {}: {}"_format(from, to,
errormsg())};
}
return trans;
}
osmium::geom::Coordinates transform(PJ *transformation,
osmium::geom::Coordinates coords) const
noexcept
{
PJ_COORD c_in;
c_in.lpzt.z = 0.0;
c_in.lpzt.t = HUGE_VAL;
c_in.lpzt.lam = osmium::geom::deg_to_rad(coords.x);
c_in.lpzt.phi = osmium::geom::deg_to_rad(coords.y);
auto const c_out = proj_trans(transformation, PJ_FWD, c_in);
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
return osmium::geom::Coordinates{c_out.xy.x, c_out.xy.y};
}
int m_target_srs;
std::unique_ptr<PJ_CONTEXT, pj_context_deleter_t> m_context;
std::unique_ptr<PJ, pj_deleter_t> m_transformation;
/**
* 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.
*/
std::unique_ptr<PJ, pj_deleter_t> m_transformation_tile;
};
} // anonymous namespace
std::shared_ptr<reprojection> reprojection::make_generic_projection(int srs)
{
return std::make_shared<generic_reprojection_t>(srs);
}
std::string get_proj_version()
{
return "[API 6] {}"_format(proj_info().version);
}