1 // ----------------------------------------------------------------------------
2 // coordinate.cxx -- Handling of longitude and latitude.
3 //
4 // Copyright (C) 2012
5 // Remi Chateauneu, F4ECW
6 //
7 // This file is part of fldigi.
8 //
9 // Fldigi is free software: you can redistribute it and/or modify
10 // it under the terms of the GNU General Public License as published by
11 // the Free Software Foundation, either version 3 of the License, or
12 // (at your option) any later version.
13 //
14 // Fldigi is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with fldigi. If not, see <http://www.gnu.org/licenses/>.
21 // ----------------------------------------------------------------------------
22
23 #include <stdio.h>
24
25 #include <math.h>
26 #include <string.h>
27 #include <stdexcept>
28 #include <iomanip>
29 #include <sstream>
30
31 #include "config.h"
32 #include "coordinate.h"
33 #include "locator.h"
34
Check(void) const35 void CoordinateT::Check(void) const
36 {
37 if( m_is_lon ) {
38 if( ( m_angle >= -180.0 ) && ( m_angle <= 180.0 ) ) return ;
39 } else {
40 if( ( m_angle >= -90.0 ) && ( m_angle <= 90.0 ) ) return ;
41 }
42 std::stringstream strm ;
43 strm << "Invalid m_angle=" << m_angle << " m_is_lon=" << m_is_lon ;
44 throw std::runtime_error(strm.str());
45 }
46
CoordinateT(double degrees,bool is_lon)47 CoordinateT::CoordinateT( double degrees, bool is_lon )
48 : m_angle( fmod(degrees, 360.0 ) ), m_is_lon(is_lon)
49 {
50 if( m_angle > 180.0 ) m_angle -= 360.0 ;
51 Check();
52 };
53
54 // Longitude East and Latitude North are positive.
Init(char direction,double angle_degrees)55 void CoordinateT::Init( char direction, double angle_degrees )
56 {
57 m_angle = angle_degrees ;
58 switch( direction )
59 {
60 case 'W':
61 case 'w':
62 m_angle = -m_angle ;
63 case 'E':
64 case 'e':
65 if( ( angle_degrees < -180 ) || ( angle_degrees > 180 ) )
66 throw std::runtime_error("Invalid longitude degree");
67 m_is_lon = true ;
68 break ;
69 case 'S':
70 case 's':
71 m_angle = -m_angle ;
72 case 'N':
73 case 'n':
74 if( ( angle_degrees < -90 ) || ( angle_degrees > 90 ) )
75 throw std::runtime_error("Invalid latitude degree");
76 m_is_lon = false ;
77 break ;
78 default:
79 throw std::runtime_error("Invalid direction");
80 }
81 Check();
82 }
83
CoordinateT(char direction,double angle_degrees)84 CoordinateT::CoordinateT( char direction, double angle_degrees ) {
85 Init( direction, angle_degrees );
86 }
87
CoordinateT(char direction,int degree,int minute,int second)88 CoordinateT::CoordinateT( char direction, int degree, int minute, int second )
89 {
90 // std::cout << "ctor d=" << direction << " " << degree << " " << minute << " " << second << "\n";
91 if( ( degree < 0 ) || ( degree > 180 ) )
92 throw std::runtime_error("Invalid degree");
93
94 if( ( minute < 0 ) || ( minute >= 60 ) )
95 throw std::runtime_error("Invalid minute");
96
97 if( ( second < 0 ) || ( second >= 60 ) )
98 throw std::runtime_error("Invalid second");
99
100 double angle_degrees = (double)degree + (double)minute / 60.0 + (double)second / 3600.0 ;
101 Init( direction, angle_degrees );
102 }
103
104 // Specific for reading from the file of navtex or wmo stations.
105 // Navtex: "57 06 N"
106 // Wmo : "69-36N", "013-27E", "009-25E" ou floating-point degrees: "12.34 E".
107 // Station Latitude or Latitude :DD-MM-SSH where DD is degrees, MM is minutes, SS is seconds
108 // and H is N for northern hemisphere or S for southern hemisphere or
109 // E for eastern hemisphere or W for western hemisphere.
110 // The seconds value is omitted for those stations where the seconds value is unknown.
operator >>(std::istream & istrm,CoordinateT & ref)111 std::istream & operator>>( std::istream & istrm, CoordinateT & ref )
112 {
113 if( ! istrm ) return istrm ;
114
115 std::stringstream sstrm ;
116
117 char direction ;
118 while( true ) {
119 // istrm >> direction ;
120 direction = (char)istrm.get();
121 if( ! istrm ) return istrm ;
122 switch( direction ) {
123 case 'e':
124 case 'E':
125 case 'w':
126 case 'W':
127 case 's':
128 case 'S':
129 case 'n':
130 case 'N':
131 break;
132 case '0' ... '9' :
133 case '.' :
134 case '-' :
135 case '+' :
136 case ' ' :
137 case '\t' :
138 sstrm << direction ;
139 continue;
140 default:
141 istrm.setstate(std::ios::eofbit);
142 return istrm ;
143 }
144 break;
145 }
146 // TODO: Check that the direction is what we expect.
147
148 std::string tmpstr = sstrm.str();
149 // std::cout << "READ:" << tmpstr << ":" << direction << "\n";
150
151 const char * tmpPtr = tmpstr.c_str();
152 int i_degree, i_minute, i_second ;
153 if( ( 3 == sscanf( tmpPtr, "%d-%d-%d", &i_degree, &i_minute, &i_second ) )
154 || ( 3 == sscanf( tmpPtr, "%d %d %d", &i_degree, &i_minute, &i_second ) ) ) {
155 ref = CoordinateT( direction, i_degree, i_minute, i_second );
156 return istrm;
157 }
158
159 if( ( 2 == sscanf( tmpPtr, "%d-%d", &i_degree, &i_minute ) )
160 || ( 2 == sscanf( tmpPtr, "%d %d", &i_degree, &i_minute ) ) ) {
161 ref = CoordinateT( direction, i_degree, i_minute, 0 );
162 return istrm;
163 }
164
165 double d_degree ;
166 if( 1 == sscanf( tmpPtr, "%lf", &d_degree ) ) {
167 ref = CoordinateT( direction, d_degree );
168 return istrm;
169 }
170
171 istrm.setstate(std::ios::eofbit);
172 return istrm ;
173 }
174
operator <<(std::ostream & ostrm,const CoordinateT & ref)175 std::ostream & operator<<( std::ostream & ostrm, const CoordinateT & ref )
176 {
177 bool sign = ref.m_angle > 0 ;
178 double ang = sign ? ref.m_angle : -ref.m_angle;
179
180 ostrm << std::setfill('0') << std::setw( ref.m_is_lon ? 3 : 2 ) << (int)ang << "°"
181 << std::setfill('0') << std::setw(2) << ( (int)( 0.5 + ang * 60.0 ) % 60 ) << "'"
182 << std::setfill('0') << std::setw(2) << (int)fmod( ang * 3600.0, 60 ) << "''"
183 << " ";
184 ostrm << ( ref.m_is_lon ? sign ? 'E' : 'W' : sign ? 'N' : 'S' );
185 return ostrm;
186 }
187
Pair(const CoordinateT & coo1,const CoordinateT & coo2)188 CoordinateT::Pair::Pair( const CoordinateT & coo1, const CoordinateT & coo2 )
189 : m_lon( coo1.is_lon() ? coo1 : coo2 )
190 , m_lat( coo2.is_lon() ? coo1 : coo2 )
191 {
192 if( ! ( coo1.is_lon() ^ coo2.is_lon() ) )
193 {
194 throw std::runtime_error("Internal inconsistency");
195 }
196 }
197
Pair(double lon,double lat)198 CoordinateT::Pair::Pair( double lon, double lat )
199 : m_lon( CoordinateT( lon, true ) )
200 , m_lat( CoordinateT( lat, false ) ) {}
201
Pair(const std::string & locator)202 CoordinateT::Pair::Pair( const std::string & locator )
203 {
204 double lon, lat ;
205 int res = QRB::locator2longlat( &lon, &lat, locator.c_str() );
206 if( res != QRB::QRB_OK ) {
207 throw std::runtime_error("Cannot decode Maidenhead locator:" + locator );
208 };
209 m_lon = CoordinateT( lon, true );
210 m_lat = CoordinateT( lat, false );
211 }
212
distance(const Pair & a) const213 double CoordinateT::Pair::distance( const Pair & a ) const
214 {
215 double dist, azimuth ;
216 int res = QRB::qrb(
217 longitude().angle(), latitude().angle(),
218 a.longitude().angle(), a.latitude().angle(),
219 &dist, &azimuth );
220 if( res != QRB::QRB_OK) {
221 std::stringstream sstrm ;
222 sstrm << "Bad qrb result:" << *this << " <-> " << a ;
223 throw std::runtime_error(sstrm.str());
224 }
225 return dist ;
226 }
227
locator(void) const228 std::string CoordinateT::Pair::locator(void) const
229 {
230 char buf[64];
231 int ret = QRB::longlat2locator(
232 longitude().angle(),
233 latitude().angle(),
234 buf,
235 3 );
236
237 if( ret == QRB::QRB_OK ) {
238 return buf ;
239 }
240 return std::string();
241 }
242
operator <<(std::ostream & ostrm,const CoordinateT::Pair & ref)243 std::ostream & operator<<( std::ostream & ostrm, const CoordinateT::Pair & ref )
244 {
245 ostrm << ref.latitude() << "/" << ref.longitude();
246 return ostrm;
247 }
248
operator >>(std::istream & istrm,CoordinateT::Pair & ref)249 std::istream & operator>>( std::istream & istrm, CoordinateT::Pair & ref )
250 {
251 istrm >> ref.latitude() >> ref.longitude();
252 return istrm;
253 }
254
255
256