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