Goby v2
liaison_geodesy.cpp
1 // Copyright 2009-2018 Toby Schneider (http://gobysoft.org/index.wt/people/toby)
2 // GobySoft, LLC (2013-)
3 // Massachusetts Institute of Technology (2007-2014)
4 // Community contributors (see AUTHORS file)
5 //
6 //
7 // This file is part of the Goby Underwater Autonomy Project Libraries
8 // ("The Goby Libraries").
9 //
10 // The Goby Libraries are free software: you can redistribute them and/or modify
11 // them under the terms of the GNU Lesser General Public License as published by
12 // the Free Software Foundation, either version 2.1 of the License, or
13 // (at your option) any later version.
14 //
15 // The Goby Libraries are distributed in the hope that they will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public License
21 // along with Goby. If not, see <http://www.gnu.org/licenses/>.
22 
23 #include <Wt/WBreak>
24 #include <Wt/WComboBox>
25 #include <Wt/WContainerWidget>
26 #include <Wt/WGroupBox>
27 #include <Wt/WLineEdit>
28 #include <Wt/WPushButton>
29 
30 #include <goby/util/as.h>
31 
32 #include "liaison_geodesy.h"
33 
34 using namespace Wt;
35 
36 goby::common::LiaisonGeodesy::LiaisonGeodesy(const protobuf::LiaisonConfig& cfg,
37  Wt::WContainerWidget* parent)
38  : LiaisonContainer(parent), geodesy_config_(cfg.GetExtension(protobuf::geodesy_config)),
39  mode_(DECIMAL_DEGREES), last_lat_(std::numeric_limits<double>::quiet_NaN()),
40  last_lon_(std::numeric_limits<double>::quiet_NaN()),
41  last_x_(std::numeric_limits<double>::quiet_NaN()),
42  last_y_(std::numeric_limits<double>::quiet_NaN())
43 {
44  latlon_format_ = new Wt::WComboBox(this);
45  latlon_format_->addItem("Decimal Degrees");
46  latlon_format_->addItem("Degrees, Decimal Minutes");
47  latlon_format_->addItem("Degrees, Minutes, Seconds");
48 
49  latlon_format_->changed().connect(this, &LiaisonGeodesy::change_mode);
50 
51  WGroupBox* datum_box = new Wt::WGroupBox("Datum", this);
52  new WText("Latitude: ", datum_box);
53  datum_lat_ = new WText(datum_box);
54  new WBreak(datum_box);
55  new WText("Longitude: ", datum_box);
56  datum_lon_ = new WText(datum_box);
57 
58  geodesy_.Initialise(geodesy_config_.lat_origin(), geodesy_config_.lon_origin());
59 
60  WGroupBox* local_to_geo_box = new Wt::WGroupBox("Local UTM to Latitude/Longitude", this);
61 
62  new WText("X (m): ", local_to_geo_box);
63  local_to_geo_x_ = new WLineEdit(local_to_geo_box);
64 
65  new WBreak(local_to_geo_box);
66  new WText("Y (m): ", local_to_geo_box);
67  local_to_geo_y_ = new WLineEdit(local_to_geo_box);
68  new WBreak(local_to_geo_box);
69  local_to_geo_button_ = new WPushButton("Convert", local_to_geo_box);
70  local_to_geo_button_->clicked().connect(this, &LiaisonGeodesy::convert_local_to_geo);
71  local_to_geo_x_->changed().connect(this, &LiaisonGeodesy::convert_local_to_geo);
72  local_to_geo_y_->changed().connect(this, &LiaisonGeodesy::convert_local_to_geo);
73 
74  new WBreak(local_to_geo_box);
75 
76  new WText("Latitude: ", local_to_geo_box);
77  local_to_geo_lat_ = new WText(local_to_geo_box);
78  new WBreak(local_to_geo_box);
79  new WText("Longitude: ", local_to_geo_box);
80  local_to_geo_lon_ = new WText(local_to_geo_box);
81 
82  WGroupBox* geo_to_local_box = new Wt::WGroupBox("Latitude/Longitude to Local UTM", this);
83 
84  WContainerWidget* lat_box = new WContainerWidget(geo_to_local_box);
85  new WText("Latitude: ", lat_box);
86  lat_box->resize(WLength(20, Wt::WLength::FontEm), WLength());
87 
88  geo_to_local_lat_deg_ = new WLineEdit(geo_to_local_box);
89  new WText(std::wstring(1, wchar_t(0x00B0)), geo_to_local_box);
90  geo_to_local_lat_deg_->resize(WLength(5, Wt::WLength::FontEm), WLength());
91  lat_min_box_ = new WContainerWidget(geo_to_local_box);
92  lat_min_box_->setInline(true);
93  geo_to_local_lat_min_ = new WLineEdit(lat_min_box_);
94  new WText("'", lat_min_box_);
95  geo_to_local_lat_min_->resize(WLength(5, Wt::WLength::FontEm), WLength());
96  lat_sec_box_ = new WContainerWidget(geo_to_local_box);
97  lat_sec_box_->setInline(true);
98  geo_to_local_lat_sec_ = new WLineEdit(lat_sec_box_);
99  new WText("\"", lat_sec_box_);
100  geo_to_local_lat_sec_->resize(WLength(5, Wt::WLength::FontEm), WLength());
101  lat_hemi_box_ = new WContainerWidget(geo_to_local_box);
102  lat_hemi_box_->setInline(true);
103  geo_to_local_lat_hemi_ = new Wt::WComboBox(lat_hemi_box_);
104  geo_to_local_lat_hemi_->addItem("N");
105  geo_to_local_lat_hemi_->addItem("S");
106 
107  new WBreak(geo_to_local_box);
108 
109  WContainerWidget* lon_box = new WContainerWidget(geo_to_local_box);
110  new WText("Longitude: ", lon_box);
111  lon_box->resize(WLength(20, Wt::WLength::FontEm), WLength());
112 
113  geo_to_local_lon_deg_ = new WLineEdit(geo_to_local_box);
114  geo_to_local_lon_deg_->resize(WLength(5, Wt::WLength::FontEm), WLength());
115  new WText(std::wstring(1, wchar_t(0x00B0)), geo_to_local_box);
116  lon_min_box_ = new WContainerWidget(geo_to_local_box);
117  lon_min_box_->setInline(true);
118  geo_to_local_lon_min_ = new WLineEdit(lon_min_box_);
119  new WText("'", lon_min_box_);
120  geo_to_local_lon_min_->resize(WLength(5, Wt::WLength::FontEm), WLength());
121  lon_sec_box_ = new WContainerWidget(geo_to_local_box);
122  lon_sec_box_->setInline(true);
123  geo_to_local_lon_sec_ = new WLineEdit(lon_sec_box_);
124  new WText("'", lon_sec_box_);
125  geo_to_local_lon_sec_->resize(WLength(5, Wt::WLength::FontEm), WLength());
126  lon_hemi_box_ = new WContainerWidget(geo_to_local_box);
127  lon_hemi_box_->setInline(true);
128  geo_to_local_lon_hemi_ = new Wt::WComboBox(lon_hemi_box_);
129  geo_to_local_lon_hemi_->addItem("E");
130  geo_to_local_lon_hemi_->addItem("W");
131 
132  new WBreak(geo_to_local_box);
133 
134  geo_to_local_button_ = new WPushButton("Convert", geo_to_local_box);
135  new WBreak(geo_to_local_box);
136 
137  new WText("X (m): ", geo_to_local_box);
138  geo_to_local_x_ = new WText(geo_to_local_box);
139  new WBreak(geo_to_local_box);
140  new WText("Y (m): ", geo_to_local_box);
141  geo_to_local_y_ = new WText(geo_to_local_box);
142 
143  geo_to_local_button_->clicked().connect(this, &LiaisonGeodesy::convert_geo_to_local);
144  geo_to_local_lat_deg_->changed().connect(this, &LiaisonGeodesy::convert_geo_to_local);
145  geo_to_local_lat_min_->changed().connect(this, &LiaisonGeodesy::convert_geo_to_local);
146  geo_to_local_lat_sec_->changed().connect(this, &LiaisonGeodesy::convert_geo_to_local);
147  geo_to_local_lat_hemi_->changed().connect(this, &LiaisonGeodesy::convert_geo_to_local);
148  geo_to_local_lon_deg_->changed().connect(this, &LiaisonGeodesy::convert_geo_to_local);
149  geo_to_local_lon_min_->changed().connect(this, &LiaisonGeodesy::convert_geo_to_local);
150  geo_to_local_lon_sec_->changed().connect(this, &LiaisonGeodesy::convert_geo_to_local);
151  geo_to_local_lon_hemi_->changed().connect(this, &LiaisonGeodesy::convert_geo_to_local);
152 
153  change_mode();
154 
155  set_name("MOOSGeodesy");
156 }
157 
158 void goby::common::LiaisonGeodesy::loop() {}
159 
160 void goby::common::LiaisonGeodesy::convert_local_to_geo()
161 {
162  geodesy_.UTM2LatLong(goby::util::as<double>(boost::trim_copy(local_to_geo_x_->text().narrow())),
163  goby::util::as<double>(boost::trim_copy(local_to_geo_y_->text().narrow())),
164  last_lat_, last_lon_);
165  reformat();
166 }
167 
168 void goby::common::LiaisonGeodesy::convert_geo_to_local()
169 {
170  double lat_deg =
171  goby::util::as<double>(boost::trim_copy(geo_to_local_lat_deg_->text().narrow())),
172  lat_min =
173  goby::util::as<double>(boost::trim_copy(geo_to_local_lat_min_->text().narrow())),
174  lat_sec =
175  goby::util::as<double>(boost::trim_copy(geo_to_local_lat_sec_->text().narrow()));
176 
177  double lon_deg =
178  goby::util::as<double>(boost::trim_copy(geo_to_local_lon_deg_->text().narrow())),
179  lon_min =
180  goby::util::as<double>(boost::trim_copy(geo_to_local_lon_min_->text().narrow())),
181  lon_sec =
182  goby::util::as<double>(boost::trim_copy(geo_to_local_lon_sec_->text().narrow()));
183 
184  double lat = lat_deg;
185  double lon = lon_deg;
186  switch (mode_)
187  {
188  case DEGREES_MINUTES_SECONDS:
189  lat += lat > 0 ? lat_sec / 3600 : -lat_sec / 3600;
190  lon += lon > 0 ? lon_sec / 3600 : -lon_sec / 3600;
191  // fall through intentional
192  case DEGREES_MINUTES:
193  lat += lat > 0 ? lat_min / 60 : -lat_min / 60;
194  if (geo_to_local_lat_hemi_->currentIndex() == 1)
195  lat = -lat;
196  lon += lon > 0 ? lon_min / 60 : -lon_min / 60;
197  if (geo_to_local_lon_hemi_->currentIndex() == 1)
198  lon = -lon;
199  // fall through intentional
200  default:
201  case DECIMAL_DEGREES: break;
202  }
203 
204  geodesy_.LatLong2LocalUTM(lat, lon, last_y_, last_x_);
205 
206  if (!(boost::math::isnan)(last_x_) && !(boost::math::isnan)(last_y_))
207  {
208  std::stringstream x_ss, y_ss;
209  x_ss << std::fixed << std::setprecision(2) << last_x_;
210  y_ss << std::fixed << std::setprecision(2) << last_y_;
211 
212  geo_to_local_x_->setText(x_ss.str());
213  geo_to_local_y_->setText(y_ss.str());
214  }
215  else
216  {
217  geo_to_local_x_->setText("---");
218  geo_to_local_y_->setText("---");
219  }
220 }
221 
222 void goby::common::LiaisonGeodesy::change_mode()
223 {
224  int mode = latlon_format_->currentIndex();
225  mode_ = static_cast<LatLonMode>(mode);
226  reformat();
227 
228  switch (mode_)
229  {
230  default:
231  case DECIMAL_DEGREES:
232  {
233  geo_to_local_lat_deg_->resize(WLength(20, Wt::WLength::FontEm), WLength());
234 
235  geo_to_local_lon_deg_->resize(WLength(20, Wt::WLength::FontEm), WLength());
236 
237  lat_min_box_->hide();
238  lat_hemi_box_->hide();
239  lon_min_box_->hide();
240  lon_hemi_box_->hide();
241  lon_sec_box_->hide();
242  lat_sec_box_->hide();
243  break;
244  }
245  case DEGREES_MINUTES:
246  {
247  geo_to_local_lat_deg_->resize(WLength(5, Wt::WLength::FontEm), WLength());
248 
249  geo_to_local_lon_deg_->resize(WLength(5, Wt::WLength::FontEm), WLength());
250  geo_to_local_lat_min_->resize(WLength(15, Wt::WLength::FontEm), WLength());
251 
252  geo_to_local_lon_min_->resize(WLength(15, Wt::WLength::FontEm), WLength());
253 
254  lat_sec_box_->hide();
255  lon_sec_box_->hide();
256  lat_min_box_->show();
257  lat_hemi_box_->show();
258  lon_min_box_->show();
259  lon_hemi_box_->show();
260  break;
261  }
262  case DEGREES_MINUTES_SECONDS:
263  {
264  geo_to_local_lat_deg_->resize(WLength(5, Wt::WLength::FontEm), WLength());
265 
266  geo_to_local_lon_deg_->resize(WLength(5, Wt::WLength::FontEm), WLength());
267  geo_to_local_lat_min_->resize(WLength(5, Wt::WLength::FontEm), WLength());
268 
269  geo_to_local_lon_min_->resize(WLength(5, Wt::WLength::FontEm), WLength());
270 
271  lat_sec_box_->show();
272  lon_sec_box_->show();
273  lat_min_box_->show();
274  lat_hemi_box_->show();
275  lon_min_box_->show();
276  lon_hemi_box_->show();
277  break;
278  }
279  }
280  convert_geo_to_local();
281 }
282 
283 void goby::common::LiaisonGeodesy::reformat()
284 {
285  datum_lat_->setText(format(geodesy_config_.lat_origin(), mode_, LAT));
286  datum_lon_->setText(format(geodesy_config_.lon_origin(), mode_, LON));
287 
288  if (!(boost::math::isnan)(last_lat_))
289  local_to_geo_lat_->setText(format(last_lat_, mode_, LAT));
290  else
291  local_to_geo_lat_->setText("---");
292 
293  if (!(boost::math::isnan)(last_lon_))
294  local_to_geo_lon_->setText(format(last_lon_, mode_, LON));
295  else
296  local_to_geo_lon_->setText("---");
297 }
298 
299 std::wstring goby::common::LiaisonGeodesy::format(double angle, LatLonMode mode, LatLon type)
300 {
301  char pos = (type == LAT) ? 'N' : 'E';
302  char neg = (type == LAT) ? 'S' : 'W';
303 
304  switch (mode_)
305  {
306  default:
307  case DECIMAL_DEGREES:
308  {
309  std::wstringstream ss;
310  ss << std::fixed << std::setprecision(7) << angle << wchar_t(0x00B0);
311  return ss.str();
312  }
313 
314  case DEGREES_MINUTES:
315  {
316  int deg = std::abs(angle);
317  double min = (std::abs(angle) - deg) * 60;
318  std::wstringstream ss;
319  ss << deg << wchar_t(0x00B0) << " " << std::setprecision(7) << min << "' "
320  << ((angle > 0) ? pos : neg);
321  return ss.str();
322  }
323  case DEGREES_MINUTES_SECONDS:
324  {
325  int deg = std::abs(angle);
326  int min = (std::abs(angle) - deg) * 60;
327  double sec = (std::abs(angle) - deg - min / 60.0) * 3600;
328  std::wstringstream ss;
329  ss << deg << wchar_t(0x00B0) << " " << min << "' " << sec << "\" "
330  << ((angle > 0) ? pos : neg);
331  return ss.str();
332  }
333  }
334 }
STL namespace.