-
Notifications
You must be signed in to change notification settings - Fork 311
/
s2projections.h
163 lines (141 loc) · 7.25 KB
/
s2projections.h
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: ericv@google.com (Eric Veach)
//
// Defines a few simple map projections. (Clients that need more complex
// projections should use a third-party library such as GeographicLib to
// implement their own projection subtypes.)
#ifndef S2_S2PROJECTIONS_H_
#define S2_S2PROJECTIONS_H_
#include <cmath>
#include "s2/r2.h"
#include "s2/s2latlng.h"
#include "s2/s2point.h"
namespace S2 {
// For the purposes of the S2 library, a projection is a function that maps
// between S2Points and R2Points. It can also define the coordinate wrapping
// behavior along each axis.
class Projection {
public:
virtual ~Projection() = default;
// Converts a point on the sphere to a projected 2D point.
virtual R2Point Project(const S2Point& p) const = 0;
// Converts a projected 2D point to a point on the sphere.
//
// If wrapping is defined for a given axis (see below), then this method
// should accept any real number for the corresponding coordinate.
virtual S2Point Unproject(const R2Point& p) const = 0;
// Convenience function equivalent to Project(ll.ToPoint()), but the
// implementation may be more efficient.
virtual R2Point FromLatLng(const S2LatLng& ll) const = 0;
// Convenience function equivalent to S2LatLng(Unproject(p)), but the
// implementation may be more efficient.
virtual S2LatLng ToLatLng(const R2Point& p) const = 0;
// Returns the point obtained by interpolating the given fraction of the
// distance along the line from A to B. Almost all projections should
// use the default implementation of this method, which simply interpolates
// linearly in R2 space. Fractions < 0 or > 1 result in extrapolation
// instead.
//
// The only reason to override this method is if you want edges to be
// defined as something other than straight lines in the 2D projected
// coordinate system. For example, using a third-party library such as
// GeographicLib you could define edges as geodesics over an ellipsoid model
// of the Earth. (Note that very few data sets define edges this way.)
//
// Also note that there is no reason to define a projection where edges are
// geodesics over the sphere, because this is the native S2 interpretation.
virtual R2Point Interpolate(double f, const R2Point& a, const R2Point& b)
const;
// Defines the coordinate wrapping distance along each axis. If this value
// is non-zero for a given axis, the coordinates are assumed to "wrap" with
// the given period. For example, if wrap_distance.y() == 360 then (x, y)
// and (x, y + 360) should map to the same S2Point.
//
// This information is used to ensure that edges takes the shortest path
// between two given points. For example, if coordinates represent
// (latitude, longitude) pairs in degrees and wrap_distance().y() == 360,
// then the edge (5:179, 5:-179) would be interpreted as spanning 2 degrees
// of longitude rather than 358 degrees.
//
// If a given axis does not wrap, its wrap_distance should be set to zero.
virtual R2Point wrap_distance() const = 0;
// Helper function that wraps the coordinates of B if necessary in order to
// obtain the shortest edge AB. For example, suppose that A = [170, 20],
// B = [-170, 20], and the projection wraps so that [x, y] == [x + 360, y].
// Then this function would return [190, 20] for point B (reducing the edge
// length in the "x" direction from 340 to 20).
R2Point WrapDestination(const R2Point& a, const R2Point& b) const;
};
// PlateCarreeProjection defines the "plate carree" (square plate) projection,
// which converts points on the sphere to (longitude, latitude) pairs.
// Coordinates can be scaled so that they represent radians, degrees, etc, but
// the projection is always centered around (latitude=0, longitude=0).
//
// Note that (x, y) coordinates are backwards compared to the usual (latitude,
// longitude) ordering, in order to match the usual convention for graphs in
// which "x" is horizontal and "y" is vertical.
class PlateCarreeProjection final : public Projection {
public:
// Constructs the plate carree projection where the x coordinates
// (longitude) span [-x_scale, x_scale] and the y coordinates (latitude)
// span [-x_scale/2, x_scale/2]. For example if x_scale==180 then the x
// range is [-180, 180] and the y range is [-90, 90].
//
// By default coordinates are expressed in radians, i.e. the x range is
// [-Pi, Pi] and the y range is [-Pi/2, Pi/2].
explicit PlateCarreeProjection(double x_scale = M_PI);
R2Point Project(const S2Point& p) const override;
S2Point Unproject(const R2Point& p) const override;
R2Point FromLatLng(const S2LatLng& ll) const override;
S2LatLng ToLatLng(const R2Point& p) const override;
R2Point wrap_distance() const override;
private:
double x_wrap_;
double to_radians_; // Multiplier to convert coordinates to radians.
double from_radians_; // Multiplier to convert coordinates from radians.
};
// MercatorProjection defines the spherical Mercator projection. Google Maps
// uses this projection together with WGS84 coordinates, in which case it is
// known as the "Web Mercator" projection (see Wikipedia). This class makes
// no assumptions regarding the coordinate system of its input points, but
// simply applies the spherical Mercator projection to them.
//
// The Mercator projection is finite in width (x) but infinite in height (y).
// "x" corresponds to longitude, and spans a finite range such as [-180, 180]
// (with coordinate wrapping), while "y" is a function of latitude and spans
// an infinite range. (As "y" coordinates get larger, points get closer to
// the north pole but never quite reach it.) The north and south poles have
// infinite "y" values. (Note that this will cause problems if you tessellate
// a Mercator edge where one endpoint is a pole. If you need to do this, clip
// the edge first so that the "y" coordinate is no more than about 5 * max_x.)
class MercatorProjection final : public Projection {
public:
// Constructs a Mercator projection where "x" corresponds to longitude in
// the range [-max_x, max_x] , and "y" corresponds to latitude and can be
// any real number. The horizontal and vertical scales are equal locally.
explicit MercatorProjection(double max_x);
R2Point Project(const S2Point& p) const override;
S2Point Unproject(const R2Point& p) const override;
R2Point FromLatLng(const S2LatLng& ll) const override;
S2LatLng ToLatLng(const R2Point& p) const override;
R2Point wrap_distance() const override;
private:
double x_wrap_;
double to_radians_; // Multiplier to convert coordinates to radians.
double from_radians_; // Multiplier to convert coordinates from radians.
};
} // namespace S2
#endif // S2_S2PROJECTIONS_H_