SLProject  4.2.000
A platform independent 3D computer graphics framework for desktop OS, Android, iOS and online in web browsers
SLDeviceLocation.cpp
Go to the documentation of this file.
1 /**
2  * \file SLDeviceLocation.cpp
3  * \authors Marcus Hudritsch
4  * \date July 2014
5  * \authors Marcus Hudritsch
6  * \copyright http://opensource.org/licenses/GPL-3.0
7  * \remarks Please use clangformat to format the code. See more code style on
8  * https://github.com/cpvrlab/SLProject4/wiki/SLProject-Coding-Style
9  */
10 
11 #include "SL.h"
12 #include <SLAlgo.h>
13 #include <SLDeviceLocation.h>
14 #include <SLImporter.h>
15 #include <spa.h>
16 
17 //-----------------------------------------------------------------------------
19 {
20  _isUsed = false;
21  _isFirstSensorValue = false;
22  _locLatLonAlt.set(0, 0, 0);
23  _locECEF.set(0, 0, 0);
24  _locENU.set(0, 0, 0);
25  _locAccuracyM = 0.0f;
26  _locMaxDistanceM = 1000.0f;
27  _defaultLatLonAlt.set(0, 0, 0);
28  _defaultENU.set(0, 0, 0);
29  _originLatLonAlt.set(0, 0, 0);
30  _originENU.set(0, 0, 0);
31  _offsetENU.set(0, 0, 0);
32  _originAccuracyM = FLT_MAX;
33  _originSolarZenith = 45.0f;
34  _originSolarAzimuth = 0.0f;
35  _originSolarSunrise = 0.0f;
36  _originSolarSunset = 0.0f;
37  _wRecef.identity();
38  _hasOrigin = false;
39  _useOriginAltitude = true;
40  _improveOrigin = true;
41  _improveTimeSEC = 10.0f;
42  _sunLightNode = nullptr;
43  _altDemM = 0.0f;
44  _altGpsM = 0.0f;
45  _cameraHeightM = 1.6f;
47  _nameLocations.clear();
49 }
50 //-----------------------------------------------------------------------------
51 // Setter for hasOrigin flag.
53 {
54  if (!hasOrigin)
55  {
57  _originAccuracyM = FLT_MAX;
58  }
60 }
61 //-----------------------------------------------------------------------------
62 //! Event handler for mobile device location update.
63 /*! Global event handler for device GPS location with longitude and latitude in
64  degrees and altitude in meters. This location uses the World Geodetic System
65  1984 (WGS84). The accuracy in meters is a radius in which the location is with
66  a probability of 68% (2 sigma). The altitude in m is the most inaccurate
67  information. The option _useOriginAltitude allows to overwrite the current
68  altitude with the origins altitude. If a geoTiff is available the altitude is
69  is take from it.
70  /param latDEG Latitude (vertical) on WGS84 geoid in degrees
71  /param lonDEG Longitude (horizontal) on WGS84 geoid in degrees
72  /param altM Altitude over WGS84 geoid in meters
73  /param accuracyM Accuracy in meters is a radius
74 */
76  SLdouble lonDEG,
77  SLdouble altM,
78  SLfloat accuracyM)
79 {
80  // Use altitude either from DEM (best), origin (static) or GPS (worst)
81  _altGpsM = (float)altM;
82  float altToUse = (float)altM;
83  if (geoTiffIsAvailableAndValid() && posIsOnGeoTiff(latDEG, lonDEG))
84  {
86  lonDEG);
87  altToUse = _altDemM + _cameraHeightM;
88  }
89  else
90  {
91  altToUse = _useOriginAltitude ? (float)_originLatLonAlt.alt : _altGpsM;
92  }
93 
94  // Init origin if it is not set yet or if the origin should be improved
95  if (!_hasOrigin || _improveOrigin)
96  {
97  // The first sensor value can appear after a few seconds.
99  {
101  _isFirstSensorValue = false;
102  }
103 
104  // Only improve if accuracy is higher and the improve time has not elapsed
105  if (accuracyM < _originAccuracyM ||
107  {
108  _originAccuracyM = accuracyM;
109  originLatLonAlt(latDEG, lonDEG, altToUse);
110  defaultLatLonAlt(latDEG, lonDEG, altToUse);
111  }
112  }
113 
114  _locLatLonAlt.set(latDEG, lonDEG, altToUse);
115 
116  _locAccuracyM = accuracyM;
117 
118  // Convert to cartesian ECEF coordinates
120 
121  // Transform to local east-north-up frame
123 }
124 //-----------------------------------------------------------------------------
125 //! Origin coordinate setter in WGS84 Lat-Lon in degrees, minutes and seconds
126 /* Swisstopo coordinates at https://map.geo.admin.ch in degrees, minutes and
127  * seconds are preciser than their decimal degrees.
128  */
130  int minutesLat,
131  double secondsLat,
132  int degreesLon,
133  int minutesLon,
134  double secondsLon,
135  double altitudeM)
136 {
137  SLVec3d originWGS84Decimal = SLAlgo::geoDegMinSec2Decimal(degreesLat,
138  minutesLat,
139  secondsLat,
140  degreesLon,
141  minutesLon,
142  secondsLon,
143  altitudeM);
144  originLatLonAlt(originWGS84Decimal.lat,
145  originWGS84Decimal.lon,
146  originWGS84Decimal.alt);
147 }
148 //-----------------------------------------------------------------------------
149 //! Set global origin in latitude, longitude and altitude at the ground level.
150 /*! The calculated values can be used for global camera positioning via GPS
151  sensor. The origin is the zero point of the model. The origin should be defined
152  in the model on the ground.
153  /param latDEG Latitude (vertical) on WGS84 geoid in decimal degrees
154  /param lonDEG Longitude (horizontal) on WGS84 geoid in decimal degrees
155  /param altM Altitude over WGS84 geoid in meters
156 */
158  SLdouble lonDEG,
159  SLdouble altM)
160 {
161  _originLatLonAlt = SLVec3d(latDEG, lonDEG, altM);
162  SLVec3d originECEF;
163  originECEF.latlonAlt2ecef(_originLatLonAlt);
164 
165  // calculation of ECEF to world (scene) rotation matrix
166  // definition of rotation matrix for ECEF to world frame rotation:
167  // world frame (scene) w.r.t. ENU frame
168  double phiRad = latDEG * Utils::DEG2RAD; // phi == latitude
169  double lamRad = lonDEG * Utils::DEG2RAD; // lambda == longitude
170  double sinPhi = sin(phiRad);
171  double cosPhi = cos(phiRad);
172  double sinLam = sin(lamRad);
173  double cosLam = cos(lamRad);
174 
175  SLMat3d enuRecef(-sinLam,
176  cosLam,
177  0,
178  -cosLam * sinPhi,
179  -sinLam * sinPhi,
180  cosPhi,
181  cosLam * cosPhi,
182  sinLam * cosPhi,
183  sinPhi);
184 
185  // ENU frame w.r.t. world frame (scene)
186  SLMat3d wRenu; // same as before
187  wRenu.rotation(-90, 1, 0, 0);
188 
189  // ECEF w.r.t. world frame (scene)
190  _wRecef = wRenu * enuRecef;
191  _originENU = _wRecef * originECEF;
192 
193  // Indicate that origin is set. Otherwise it would be reset on each update
194  _hasOrigin = true;
195 
197  std::time(nullptr));
198 }
199 //-----------------------------------------------------------------------------
200 //! Default coordinate setter in WGS84 Lat-Lon in degrees, minutes and seconds
201 /* Swisstopo coordinates at https://map.geo.admin.ch in degrees, minutes and
202  * seconds are preciser than their decimal degrees.
203  */
205  int minutesLat,
206  double secondsLat,
207  int degreesLon,
208  int minutesLon,
209  double secondsLon,
210  double altitudeM)
211 {
212  SLVec3d defaultWGS84Decimal = SLAlgo::geoDegMinSec2Decimal(degreesLat,
213  minutesLat,
214  secondsLat,
215  degreesLon,
216  minutesLon,
217  secondsLon,
218  altitudeM);
219  defaultLatLonAlt(defaultWGS84Decimal.lat,
220  defaultWGS84Decimal.lon,
221  defaultWGS84Decimal.alt);
222 }
223 //-----------------------------------------------------------------------------
224 //! Sets the default location in latitude, longitude and altitude.
225 /*! It must be called after setting the origin. If no origin is set with it
226  will be automatically set in onLocationLatLonAlt. The default location is used by
227  the camera in SLCamera::setView if the current distance between _locENU and
228  _originENU is greater than _locMaxDistanceM. Witch means that you are in real
229  not near the location.
230  /param latDEG Latitude (vertical) on WGS84 geoid in degrees
231  /param lonDEG Longitude (horizontal) on WGS84 geoid in degrees
232  /param altM Altitude over WGS84 geoid in meters
233  */
235  SLdouble lonDEG,
236  SLdouble altM)
237 {
238  _defaultLatLonAlt.set(latDEG,
239  lonDEG,
242 
243  // Convert to cartesian ECEF coordinates
244  SLVec3d defaultECEF;
245  defaultECEF.latlonAlt2ecef(_defaultLatLonAlt);
246 
247  // Transform to local east-north-up frame
248  _defaultENU = _wRecef * defaultECEF;
249 }
250 //-----------------------------------------------------------------------------
251 //! Setter that turns on the device rotation sensor
253 {
254  if (!_isUsed && use)
255  _isFirstSensorValue = true;
256 
257  _isUsed = use;
258 }
259 //-----------------------------------------------------------------------------
260 //! Calculates the solar angles at origin at local time
261 /*! Calculates the zenith and azimuth angle in deg. of the sun at the origin at
262  the local time using the Solar Position Algorithm from:
263  https://midcdmz.nrel.gov/spa/ that is part of libsl_external.
264 */
266  std::time_t time)
267 {
268  // leave default angles if origin has not been set
269  if (!_hasOrigin) return false;
270 
271  // transform time
272  tm ut{}, lt{};
273  memcpy(&ut, std::gmtime(&time), sizeof(tm));
274  memcpy(&lt, std::localtime(&time), sizeof(tm));
275 
276  ut.tm_year += 1900;
277  lt.tm_year += 1900;
278  ut.tm_mon++;
279  lt.tm_mon++;
280 
281  SL_LOG_DEBUG("Universal time : %02d.%02d.%02d %02d:%02d:%02d",
282  ut.tm_mday,
283  ut.tm_mon,
284  ut.tm_year,
285  ut.tm_hour,
286  ut.tm_min,
287  ut.tm_sec);
288  SL_LOG_DEBUG("Local time : %02d.%02d.%02d %02d:%02d:%02d",
289  lt.tm_mday,
290  lt.tm_mon,
291  lt.tm_year,
292  lt.tm_hour,
293  lt.tm_min,
294  lt.tm_sec);
295  SL_LOG_DEBUG("Timezone : %d", lt.tm_hour - ut.tm_hour);
296 
297  spa_data spa; // declare the SPA structure
298  SLint result;
299 
300  // enter required input values into SPA structure
301  spa.year = lt.tm_year;
302  spa.month = lt.tm_mon;
303  spa.day = lt.tm_mday;
304  spa.hour = lt.tm_hour;
305  spa.minute = lt.tm_min;
306  spa.second = lt.tm_sec;
307  spa.timezone = lt.tm_hour - ut.tm_hour;
308  spa.delta_ut1 = 0;
309  spa.delta_t = 0;
310  spa.longitude = locationLatLonAlt.lon;
311  spa.latitude = locationLatLonAlt.lat;
312  spa.elevation = locationLatLonAlt.alt;
313  // http://systemdesign.ch/wiki/Barometrische_Hoehenformel
314  spa.pressure = 1013.25 * pow((1.0 - 0.0065 * locationLatLonAlt.alt / 288.15), 5.255);
315  spa.temperature = 15.0;
316  spa.slope = 0;
317  spa.azm_rotation = 0;
318  spa.atmos_refract = 0.5667;
319  spa.function = SPA_ALL;
320 
321  /////////////////////////////
322  result = spa_calculate(&spa);
323  /////////////////////////////
324 
325  if (result == 0) // check for SPA errors
326  {
327  _originSolarZenith = (SLfloat)spa.zenith;
328  _originSolarAzimuth = (SLfloat)spa.azimuth;
329  _originSolarSunrise = (SLfloat)spa.sunrise;
330  _originSolarSunset = (SLfloat)spa.sunset;
331 
333  SLfloat SRm = (SLfloat)(60.0f * (SRh - (int)(SRh)));
334  SLfloat SRs = (SLfloat)(60.0 * (SRm - floor(SRm)));
336  SLfloat SSm = (SLfloat)(60.0f * (SSh - (int)(SSh)));
337  SLfloat SSs = (SLfloat)(60.0f * (SSm - floor(SSm)));
338 
339  SL_LOG_DEBUG("Zenith : %.6f degrees", _originSolarZenith);
340  SL_LOG_DEBUG("Azimuth : %.6f degrees", _originSolarAzimuth);
341  SL_LOG_DEBUG("Sunrise : %02d:%02d:%02d Local Time", (int)(SRh), (int)SRm, (int)SRs);
342  SL_LOG_DEBUG("Sunset : %02d:%02d:%02d Local Time", (int)(SSh), (int)SSm, (int)SSs);
343  }
344  else
345  SL_LOG_DEBUG("SPA Error Code: %d", result);
346 
347  if (_sunLightNode)
348  {
349  // The azimuth is from north eastwards
351 
352  // The zenith angle is from up downwards
354  }
355 
356  return (result == 0);
357 }
358 //------------------------------------------------------------------------------
359 //! Converter method: the transferred wgs84 coordinate is converted to ENU frame and returned (does not change SLDeviceLocation)
361 {
364 
365  // Convert to cartesian ECEF coordinates
368 
369  // Transform to local east-north-up frame
371 
372  return locENU;
373 }
374 //------------------------------------------------------------------------------
375 //! Loads a GeoTiff DEM (Digital Elevation Model) Image
376 /* Loads a GeoTiff DEM (Digital Elevation Model) Image that must be in WGS84
377  coordinates. For more info see CVImageGeoTiff.
378  If the 32-bit image file and its JSON info file gets successfully loaded,
379  we can set the altitudes from the _originLatLonAlt and _defaultLatLonAlt by the DEM.
380  */
381 void SLDeviceLocation::loadGeoTiff(const SLstring& geoTiffFile)
382 {
383  try
384  {
385  assert(!_defaultLatLonAlt.isZero() &&
387  "Set first defaultLatLonAlt and originLatLonAlt before you add a GeoTiff.");
388 
389  _demGeoTiff.loadGeoTiff(geoTiffFile);
390 
391  // Check that default and origin location is withing the GeoTiff extends
393  {
394  // Overwrite the altitudes of origin
399  altOriginM);
400 
401  // Overwrite the altitudes of default with the additional camera height
406  altDefaultM + _cameraHeightM);
407  }
408  else
409  {
410  string msg = "SLDeviceLocation::loadGeoTiff: Either the geotiff file ";
411  msg += "could not be loaded or the origin or default position lies ";
412  msg += "not within the extends of the geotiff file.";
413  throw std::runtime_error(msg.c_str());
414  }
415  }
416  catch (std::exception& e)
417  {
418  SL_WARN_MSG(e.what());
419  }
420  catch (...)
421  {
422  SL_WARN_MSG("SLDeviceLocation::loadGeoTiff: Unknown exception catched.");
423  }
424 }
425 //-----------------------------------------------------------------------------
426 /* Returns true if a geoTiff files is loaded and the origin and default
427  positions are within the extends of the image.
428 */
430 {
431  return (!_demGeoTiff.cvMat().empty() &&
440 }
441 //-----------------------------------------------------------------------------
442 //! Return true if the current GPS location is within the GeoTiff boundaries
444 {
445  return (!_demGeoTiff.empty() &&
446  latDEG < _demGeoTiff.upperLeftLatLonAlt()[0] &&
447  latDEG > _demGeoTiff.lowerRightLatLonAlt()[0] &&
448  lonDEG > _demGeoTiff.upperLeftLatLonAlt()[1] &&
449  lonDEG < _demGeoTiff.lowerRightLatLonAlt()[1]);
450 }
451 //------------------------------------------------------------------------------
452 //! Returns the device location offset mode as string
454 {
455  switch (_offsetMode)
456  {
457  case LOM_none: return "None";
458  case LOM_twoFingerY: return "TwoFingerY";
459  default: return "Unknown";
460  }
461 }
462 //-----------------------------------------------------------------------------
float SLfloat
Definition: SL.h:173
#define SL_LOG_DEBUG(...)
Definition: SL.h:237
#define SL_WARN_MSG(message)
Definition: SL.h:241
double SLdouble
Definition: SL.h:174
bool SLbool
Definition: SL.h:175
string SLstring
Definition: SL.h:158
int SLint
Definition: SL.h:170
Container for general algorithm functions.
Mobile device location class declaration.
@ LOM_none
@ LOM_twoFingerY
SLVec3< double > SLVec3d
Definition: SLVec3.h:323
CVVec3d lowerRightLatLonAlt() const
void loadGeoTiff(const string &filename)
Loads a GEOTiff file into the OpenCV image matrix.
float getAltitudeAtLatLon(double lat, double lon) const
Returns the altitude in m at the given position in WGS84 latitude-longitude.
CVVec3d upperLeftLatLonAlt() const
CVMat cvMat() const
Definition: CVImage.h:122
bool empty() const
Definition: CVImage.h:124
void start()
Definition: HighResTimer.h:35
float elapsedTimeInSec()
Definition: HighResTimer.h:37
SLfloat _originSolarAzimuth
Azimuth angle of the sun in deg. (eastward from north) at origin at local time.
SLfloat _originSolarSunset
Sunset local time at origin.
void loadGeoTiff(const SLstring &geoTiffFile)
Loads a GeoTiff DEM (Digital Elevation Model) Image.
SLVec3d locLatLonAlt() const
bool posIsOnGeoTiff(SLdouble latDEG, SLdouble lonDEG) const
Return true if the current GPS location is within the GeoTiff boundaries.
SLbool isUsed() const
SLstring offsetModeStr() const
Returns the device location offset mode as string.
SLbool calculateSolarAngles(SLVec3d locationLatLonAlt, std::time_t time)
Calculates the solar angles at origin at local time.
SLVec3d _locLatLonAlt
Earth location in latitudeDEG, longitudeDEG & AltitudeM on WGS84 geoid.
SLfloat _originAccuracyM
Accuracy radius of origin point.
SLVLocation _nameLocations
Vector of fix locations for default view points.
SLVec3d _defaultLatLonAlt
Default location of scene in LatLonAlt.
SLVec3d convertLatLonAlt2ENU(SLVec3d locLatLonAlt) const
Converter method: the transferred wgs84 coordinate is converted to ENU frame and returned (does not c...
SLbool hasOrigin() const
SLbool _isFirstSensorValue
Flag for the first sensor values.
SLint _activeNamedLocation
Index of the active named location as defaultENU;.
SLVec3d originLatLonAlt() const
SLLocOffsetMode _offsetMode
Location offset mode.
SLfloat _locMaxDistanceM
Max. allowed distance from origin. If higher it is ignored.
SLVec3d _locECEF
Cartesian location in ECEF.
CVImageGeoTiff _demGeoTiff
Digital Elevation Model from a Geo Tiff image.
SLVec3d _defaultENU
Default location in ENU frame used if real location is too far away from origin.
HighResTimer _improveTimer
Timer to measure the improve time.
SLNode * _sunLightNode
Pointer to directional light node to be changed if solar angles are calculated.
SLVec3d _offsetENU
Offset vector in ENU frame.
bool geoTiffIsAvailableAndValid() const
SLfloat _locAccuracyM
Horizontal accuracy radius in m with 68% probability.
SLVec3d _originLatLonAlt
Global origin location of scene in LatLonAlt.
void onLocationLatLonAlt(SLdouble latDEG, SLdouble lonDEG, SLdouble altM, SLfloat accuracyM)
Event handler for mobile device location update.
SLbool _useOriginAltitude
Flag if global reference altitude should be used.
SLVec3d locECEF() const
SLfloat _originSolarZenith
Zenith angle of the sun in deg. (from up dir.) at origin at local time.
SLMat3d _wRecef
ECEF frame to world frame rotation: rotates a point defined in ecef.
SLVec3d _locENU
Cartesian location in ENU frame.
SLbool _hasOrigin
Flag if this scene has a global reference location.
SLfloat _altDemM
Altitude in m from Digital Elevation Model.
SLbool _improveOrigin
Flag if origin should be improved over time & accuracy.
SLVec3d defaultLatLonAlt() const
SLfloat _improveTimeSEC
Max. time in seconds for the origin improvement.
SLfloat _altGpsM
Altitude in m from GPS.
SLbool _isUsed
Flag if the devices GPS Sensor is used.
SLfloat _cameraHeightM
Height from ground to the mobile camera in m.
SLVec3d locENU() const
SLVec3d _originENU
Origin location in ENU frame.
SLfloat _originSolarSunrise
Sunrise local time at origin.
void rotation(const T angleDEG, const SLVec3< T > &axis)
Sets the rotation components
Definition: SLMat3.h:392
void identity()
Definition: SLMat3.h:329
void rotation(const SLQuat4f &rot, SLTransformSpace relativeTo=TS_parent)
Definition: SLNode.cpp:846
void rotate(const SLQuat4f &rot, SLTransformSpace relativeTo=TS_object)
Definition: SLNode.cpp:945
void latlonAlt2ecef(const SLVec3 &latDegLonDegAltM)
Latitude Longitude Altitude (LatLonAlt) to Earth Centered Earth Fixed (ecef) using the WGS84 model.
Definition: SLVec3.h:269
T y
Definition: SLVec3.h:43
T lat
Definition: SLVec3.h:45
static SLVec3 AXISY
Definition: SLVec3.h:298
T alt
Definition: SLVec3.h:45
T x
Definition: SLVec3.h:43
static SLVec3 AXISX
Definition: SLVec3.h:297
void set(const T X, const T Y, const T Z)
Definition: SLVec3.h:59
SLbool isZero()
Definition: SLVec3.h:165
T z
Definition: SLVec3.h:43
T lon
Definition: SLVec3.h:45
T geoDegMinSec2Decimal(int degrees, int minutes, T seconds)
convert geodetic datum defined in degrees, minutes and seconds to decimal
Definition: SLAlgo.cpp:45
static const float DEG2RAD
Definition: Utils.h:239
T floor(T a)
Definition: Utils.h:246