summaryrefslogtreecommitdiff
path: root/src/third_party/s2/s2latlng.h
blob: 88f1214b8f35cda0934e3329e3e9474adf470784 (plain)
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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
// Copyright 2005 Google Inc. All Rights Reserved.

#ifndef UTIL_GEOMETRY_S2LATLNG_H__
#define UTIL_GEOMETRY_S2LATLNG_H__

#include <string>
using std::string;

#include <ostream>
#include "base/basictypes.h"
#include "s1angle.h"
#include "s2.h"
#include "util/math/vector2-inl.h"

// This class represents a point on the unit sphere as a pair
// of latitude-longitude coordinates.  Like the rest of the "geometry"
// package, the intent is to represent spherical geometry as a mathematical
// abstraction, so functions that are specifically related to the Earth's
// geometry (e.g. easting/northing conversions) should be put elsewhere.
//
// This class is intended to be copied by value as desired.  It uses
// the default copy constructor and assignment operator.
class S2LatLng {
 public:
  // Constructor.  The latitude and longitude are allowed to be outside
  // the is_valid() range.  However, note that most methods that accept
  // S2LatLngs expect them to be normalized (see Normalized() below).
  inline S2LatLng(S1Angle const& lat, S1Angle const& lng);

  // The default constructor sets the latitude and longitude to zero.  This is
  // mainly useful when declaring arrays, STL containers, etc.
  inline S2LatLng();

  // Convert a direction vector (not necessarily unit length) to an S2LatLng.
  explicit S2LatLng(S2Point const& p);

  // Returns an S2LatLng for which is_valid() will return false.
  inline static S2LatLng Invalid();

  // Convenience functions -- shorter than calling S1Angle::Radians(), etc.
  inline static S2LatLng FromRadians(double lat_radians, double lng_radians);
  inline static S2LatLng FromDegrees(double lat_degrees, double lng_degrees);
  inline static S2LatLng FromE5(int32 lat_e5, int32 lng_e5);
  inline static S2LatLng FromE6(int32 lat_e6, int32 lng_e6);
  inline static S2LatLng FromE7(int32 lat_e7, int32 lng_e7);

  // Convenience functions -- to use when args have been fixed32s in protos.
  //
  // The arguments are static_cast into int32, so very large unsigned values
  // are treated as negative numbers.
  inline static S2LatLng FromUnsignedE6(uint32 lat_e6, uint32 lng_e6);
  inline static S2LatLng FromUnsignedE7(uint32 lat_e7, uint32 lng_e7);

  // Methods to compute the latitude and longitude of a point separately.
  inline static S1Angle Latitude(S2Point const& p);
  inline static S1Angle Longitude(S2Point const& p);

  // Accessor methods.
  S1Angle lat() const { return S1Angle::Radians(coords_[0]); }
  S1Angle lng() const { return S1Angle::Radians(coords_[1]); }
  Vector2_d const& coords() const { return coords_; }

  // Return true if the latitude is between -90 and 90 degrees inclusive
  // and the longitude is between -180 and 180 degrees inclusive.
  inline bool is_valid() const;

  // Clamps the latitude to the range [-90, 90] degrees, and adds or subtracts
  // a multiple of 360 degrees to the longitude if necessary to reduce it to
  // the range [-180, 180].
  S2LatLng Normalized() const;

  // Convert a normalized S2LatLng to the equivalent unit-length vector.
  S2Point ToPoint() const;

  // Return the distance (measured along the surface of the sphere) to the
  // given S2LatLng.  This is mathematically equivalent to:
  //
  //   S1Angle::Radians(ToPoint().Angle(o.ToPoint()))
  //
  // but this implementation is slightly more efficient.  Both S2LatLngs
  // must be normalized.
  S1Angle GetDistance(S2LatLng const& o) const;

  // Simple arithmetic operations for manipulating latitude-longitude pairs.
  // The results are not normalized (see Normalized()).
  friend inline S2LatLng operator+(S2LatLng const& a, S2LatLng const& b);
  friend inline S2LatLng operator-(S2LatLng const& a, S2LatLng const& b);
  friend inline S2LatLng operator*(double m, S2LatLng const& a);
  friend inline S2LatLng operator*(S2LatLng const& a, double m);

  bool operator==(S2LatLng const& o) const { return coords_ == o.coords_; }
  bool operator!=(S2LatLng const& o) const { return coords_ != o.coords_; }
  bool operator<(S2LatLng const& o) const { return coords_ < o.coords_; }
  bool operator>(S2LatLng const& o) const { return coords_ > o.coords_; }
  bool operator<=(S2LatLng const& o) const { return coords_ <= o.coords_; }
  bool operator>=(S2LatLng const& o) const { return coords_ >= o.coords_; }

  bool ApproxEquals(S2LatLng const& o, double max_error = 1e-15) const {
    return coords_.aequal(o.coords_, max_error);
  }

  // Export the latitude and longitude in degrees, separated by a comma.
  // e.g. "94.518000,150.300000"
  string ToStringInDegrees() const;
  void ToStringInDegrees(string* s) const;
  string toString() const { return ToStringInDegrees(); }

 private:
  // Internal constructor.
  inline S2LatLng(Vector2_d const& coords) : coords_(coords) {}

  // This is internal to avoid ambiguity about which units are expected.
  inline S2LatLng(double lat_radians, double lng_radians)
    : coords_(lat_radians, lng_radians) {}

  Vector2_d coords_;
};
DECLARE_POD(S2LatLng);

inline S2LatLng::S2LatLng(S1Angle const& lat, S1Angle const& lng)
    : coords_(lat.radians(), lng.radians()) {}

inline S2LatLng::S2LatLng() : coords_(0, 0) {}

inline S2LatLng S2LatLng::FromRadians(double lat_radians, double lng_radians) {
  return S2LatLng(lat_radians, lng_radians);
}

inline S2LatLng S2LatLng::FromDegrees(double lat_degrees, double lng_degrees) {
  return S2LatLng(S1Angle::Degrees(lat_degrees), S1Angle::Degrees(lng_degrees));
}

inline S2LatLng S2LatLng::FromE5(int32 lat_e5, int32 lng_e5) {
  return S2LatLng(S1Angle::E5(lat_e5), S1Angle::E5(lng_e5));
}

inline S2LatLng S2LatLng::FromE6(int32 lat_e6, int32 lng_e6) {
  return S2LatLng(S1Angle::E6(lat_e6), S1Angle::E6(lng_e6));
}

inline S2LatLng S2LatLng::FromE7(int32 lat_e7, int32 lng_e7) {
  return S2LatLng(S1Angle::E7(lat_e7), S1Angle::E7(lng_e7));
}

inline S2LatLng S2LatLng::FromUnsignedE6(uint32 lat_e6, uint32 lng_e6) {
  return S2LatLng(S1Angle::UnsignedE6(lat_e6), S1Angle::UnsignedE6(lng_e6));
}

inline S2LatLng S2LatLng::FromUnsignedE7(uint32 lat_e7, uint32 lng_e7) {
  return S2LatLng(S1Angle::UnsignedE7(lat_e7), S1Angle::UnsignedE7(lng_e7));
}

inline S2LatLng S2LatLng::Invalid() {
  // These coordinates are outside the bounds allowed by is_valid().
  return S2LatLng(M_PI, 2 * M_PI);
}

inline S1Angle S2LatLng::Latitude(S2Point const& p) {
  // We use atan2 rather than asin because the input vector is not necessarily
  // unit length, and atan2 is much more accurate than asin near the poles.
  return S1Angle::Radians(atan2(p[2], sqrt(p[0]*p[0] + p[1]*p[1])));
}

inline S1Angle S2LatLng::Longitude(S2Point const& p) {
  // Note that atan2(0, 0) is defined to be zero.
  return S1Angle::Radians(atan2(p[1], p[0]));
}

inline bool S2LatLng::is_valid() const {
  return fabs(lat().radians()) <= M_PI_2 && fabs(lng().radians()) <= M_PI;
}

inline S2LatLng operator+(S2LatLng const& a, S2LatLng const& b) {
  return S2LatLng(a.coords_ + b.coords_);
}

inline S2LatLng operator-(S2LatLng const& a, S2LatLng const& b) {
  return S2LatLng(a.coords_ - b.coords_);
}

inline S2LatLng operator*(double m, S2LatLng const& a) {
  return S2LatLng(m * a.coords_);
}

inline S2LatLng operator*(S2LatLng const& a, double m) {
  return S2LatLng(m * a.coords_);
}

ostream& operator<<(ostream& os, S2LatLng const& ll);

#endif  // UTIL_GEOMETRY_S2LATLNG_H__