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 139
|
/* 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 <osmium/geom/mercator_projection.hpp>
#include "reprojection.hpp"
/** must match expire.tiles.c */
#define EARTH_CIRCUMFERENCE 40075016.68
namespace {
void latlon2merc(double *lat, double *lon)
{
if (*lat > 89.99) {
*lat = 89.99;
} else if (*lat < -89.99) {
*lat = -89.99;
}
using namespace osmium::geom;
auto coord = lonlat_to_mercator(Coordinates(*lon, *lat));
*lon = coord.x;
*lat = coord.y;
}
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
{
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);
}
|