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;
std::string sanitize_geo_coordinate(std::string const &coordinates, bool two_components)
Replace non-ascii symbols used in geographic coordinates by their ascii equivalents.
static double convert_single_geo_coordinate_(std::string const &h1, std::string const &d, std::string const &m, std::string const &s, std::string const &h2, GeoCoordinateComponent component)
Local helper function that takes parts of the regex matches and converts them to double.
Geographical coordinates in degrees.
Container namespace for all symbols of genesis in order to keep them separate when used as a library...
std::ostream & operator<<(std::ostream &os, const Matrix< signed char > &matrix)
Template specialization for signed char, in order to print nicely.
double geo_distance(GeoCoordinate const &c1, GeoCoordinate const &c2)
Calculate the distance (in km) between two points on Earth.
constexpr double PI
Make the world go round.
GeoCoordinateComponent
Local helper enum that indicates which component of a coordinate we are dealing with.
double latitude() const
Latitude, in range [ -90.0, 90.0 ].
GeoCoordinate convert_geo_coordinate(std::string const &latitude, std::string const &longitude)
Parse strings of geographic coordinates.
Provides some commonly used string utility functions.
std::shared_ptr< BaseOutputTarget > to_string(std::string &target_string)
Obtain an output target for writing to a string.
constexpr double EARTH_MEAN_RADIUS
Earth is not flat!
double longitude() const
Longitude, in range [ -180.0, 180.0 ].