71 std::string
const& h1,
75 std::string
const& h2,
92 if( ! h1.empty() && ! h2.empty() ) {
93 throw std::invalid_argument(
"Invalid coordinate: Has two hemisphere directives [NESW]." );
94 }
else if( ! h1.empty() ) {
96 }
else if( ! h2.empty() ) {
101 assert( h.size() < 2 );
105 if( h.size() == 1 ) {
106 if( tolower( h[0] ) == neg_h ) {
108 }
else if( tolower( h[0] ) != pos_h ) {
110 throw std::invalid_argument(
111 "Invalid coordinate: Latitude hemisphere directive not in [NS]."
114 throw std::invalid_argument(
115 "Invalid coordinate: Longitude hemisphere directive not in [EW]."
123 throw std::invalid_argument(
"Invalid coordinate: No degrees." );
125 double const dd = stod(d);
126 if( dd < d_min || dd > d_max ) {
127 throw std::invalid_argument(
128 "Invalid coordinate: Degrees outside of range [ " +
std::to_string( d_min ) +
", " +
135 if( h.size() == 1 && ( d[0] ==
'+' || d[0] ==
'-' )) {
136 if( ( sgn < 0 && dd > 0.0 ) || ( sgn > 0 && dd < 0.0 ) ) {
137 throw std::invalid_argument(
138 "Invalid coordinate: Hemisphere does not match sign of the degrees."
141 if( sgn < 0 && dd < 0.0 ) {
151 if( std::count( d.begin(), d.end(),
'.') == 1 ) {
152 throw std::invalid_argument(
153 "Invalid coordinate: Degrees cannot have decimal part if Minutes are provided."
157 if( md < 0.0 || md > 60.0 ) {
158 throw std::invalid_argument(
159 "Invalid coordinate: Minutes outside of range [ 0.0, 60.0 ]."
168 if( std::count( m.begin(), m.end(),
'.') == 1 ) {
169 throw std::invalid_argument(
170 "Invalid coordinate: Minutes cannot have decimal part if Seconds are provided."
174 if( sd < 0.0 || sd > 60.0 ) {
175 throw std::invalid_argument(
176 "Invalid coordinate: Seconds outside of range [ 0.0, 60.0 ]."
183 v =
static_cast<double>( sgn ) * -1.0 * ( ( -1.0 * dd ) + md / 60.0 + sd / 3600.0 );
185 v =
static_cast<double>( sgn ) * ( dd + md / 60.0 + sd / 3600.0 );
189 if( v < d_min || v > d_max ) {
190 throw std::invalid_argument(
191 "Invalid coordinate: Degrees outside of range [ " +
std::to_string( d_min ) +
", " +
208 std::vector<std::pair<std::string, std::string>> list = {
210 {
"\xE2\x80\xB2\xE2\x80\xB2",
"s" },
211 {
"\xE2\x80\xB2",
"m" },
212 {
"\xE2\x80\xB3",
"s" },
216 auto res = coordinates;
217 for(
auto const& bad : list ) {
219 while( pos < res.size() ) {
222 pos = res.find( bad.first, pos );
223 if( pos == std::string::npos ) {
228 res = res.replace( pos, bad.first.size(), bad.second );
234 if( two_components && std::count( res.begin(), res.end(),
',') == 1 ) {
235 auto const pos = res.find(
',' );
236 assert( pos < res.size() );
241 for(
size_t i = 0; i < res.size(); ++i ) {
242 if( res[i] ==
',' ) {
262 static std::string
const single_expr =
264 "((?:[\\+-]?[0-9]*[\\.][0-9]+)|(?:[\\+-]?[0-9]+))"
266 "(?:(?:[\\s]*[d:][\\s]*)|[\\s]+)"
268 "((?:[0-9]*[\\.][0-9]+)|(?:[0-9]+))"
270 "(?:(?:[\\s]*['m:][\\s]*)|[\\s]+)"
272 "((?:[0-9]*[\\.][0-9]+)|(?:[0-9]+))"
273 "(?:(?:[\\s]*(?:[\"s:]|(?:''))[\\s]*)|[\\s]*)"
280 static std::string
const double_expr =
281 "^[\\s]*" + single_expr +
"(?:(?:[\\s]*[/][\\s]*)|[\\s]+)" + single_expr +
"[\\s]*$"
283 static std::regex pattern( double_expr );
288 if( ! std::regex_search( sanitized, matches, pattern )) {
289 throw std::invalid_argument(
"Invalid coordinate format." );
299 matches[1].str(), matches[2].str(), matches[3].str(), matches[4].str(), matches[5].str(),
303 matches[6].str(), matches[7].str(), matches[8].str(), matches[9].str(), matches[10].str(),
306 assert( - 90.0 <= lat && lat <= 90.0 );
307 assert( -180.0 <= lon && lon <= 180.0 );
322 auto to_rad = [](
double d ){
323 return PI * d / 180.0;
327 auto const th1 = to_rad( c1.
latitude() );
328 auto const ph1 = to_rad( c1.
longitude() );
329 auto const th2 = to_rad( c2.
latitude() );
330 auto const ph2 = to_rad( c2.
longitude() );
333 auto const dx = cos( ph1 - ph2 ) * cos( th1 ) - cos( th2 );
334 auto const dy = sin( ph1 - ph2 ) * cos( th1 );
335 auto const dz = sin( th1 ) - sin( th2 );
337 return asin( sqrt( dx * dx + dy * dy + dz * dz ) / 2.0 ) * 2.0 *
EARTH_MEAN_RADIUS;