/** * @RDE_HEADER@ * Provide some coordinate translation functions on top of GeographicLib * $Id$ * $HeadURL$ * $Author$ * * Created by Christian Ehrlicher, SLSE, 2016-08-31 */ #pragma once #include #include #include #include class QPointF; class QRectF; class QSize; class QString; namespace LtsCoordTrans { /** * What do we want to achieve: * - internally our coordinates are represented as geographic coordinates * (lat/lon, wgs84, radian) * - convert them to a user-readable format which is (currently) MGRS * - convert to/from a metric offset relative to a base point from/to * geographic coordinates (e.g. for equipment in training area, for calculations) * - calculate a base point from a given geogrphic coordinate (base point * should be on an mgrs intersection (e.g. 32UPD 0 0) * * a nice view of utm and mgrs square could be found here: * https://mappingsupport.com/p2/coordinates-mgrs-gissurfer-maps.html */ // some helper functions inline Q_CONSTEXPR double pi() { return M_PI; } inline Q_CONSTEXPR double deg2rad() { return pi() / 180.; } inline Q_CONSTEXPR double rad2deg() { return 1. / deg2rad(); } struct LTSCOORDTRANS_EXPORT UtmCoordinate { int zone = -1; bool northp = true; double x = 0.; // in meter double y = 0.; // in meter UtmCoordinate() = default; UtmCoordinate(int zone_, double x_, double y_, bool northp_ = true) : zone(zone_) , northp(northp_) , x(x_) , y(y_) {} /** * @brief Checks if the UtmCoordinate is properly initialized (just checks the zone) * @return if UtmCoordinate is valid */ bool isValid() const { return zone > 0 && zone <= 60; } /** * @brief Convert the utm coordinate into a mgrs string (prettified - with spaces) * @param prec precision to use (default: 5) * @param withSpaces Insert spaces between mgrs parts * @return mgrs string (empty if conversion did not work) */ QString mgrs(int prec = 5, bool withSpaces = true) const; QPointF toPoint() const; /** * @brief Convert the utm coordinate to geographic coordinate, add offset if desired * @param offsetX the possible x offset (east) to the utm coordinate * @param offsetY the possible y offset (north) to the utm coorinate * @return */ QPointF geographic(double offsetX = 0., double offsetY = 0.) const; /** * @brief overload of QPointF geographic(double, double) const */ QPointF geographic(const QPointF &offset) const; /** * @brief Get relative Position of coordinate to other coordinate * @param other Coordinate to get the offset to. * @return */ QPointF relativeTo(const UtmCoordinate &other) const; /** * @brief convert a geographic coordinate into a utm coordinate * @param lon longitude in radian * @param lat latitude in radian * @param setZone override default zone (needed e.g. to be sure that every utm coord in an vector gets the same utm zone) * @return BasePointInformation for conversions from/to relative coordinates */ static UtmCoordinate fromGeographic(double lon, double lat, int setZone = -1); /** * @brief overload of UtmCoordinate fromGeographic(double, double, bool) */ static UtmCoordinate fromGeographic(const QPointF &lonLat, int setZone = -1); /** * @brief convert a mgrs coordinate into a utm coordinate * @param mgrs coordinate to convert */ static UtmCoordinate fromMgrs(const QString &mgrs); }; /** * @brief convert lon/lat coordinates to mgrs. Shortcut for UtmCoordinate::fromGeographic().mgrs() * @param lon longitude in radian * @param lat latitude in radian * @param prec the precision for the mgrs string * @param withSpaces Insert spaces between mgrs parts * @return mgrs as one string with spaces */ LTSCOORDTRANS_EXPORT QString mgrsFromGeographic(double lon, double lat, int prec = 5, bool withSpaces = true); /** * @brief convert a mgrs string into geographic coordinates. Shortcut for UtmCoordinate::fromMgrs().geographic() * @param mgrs coordinate to convert * @return geographic coordinate (invalid QPointF on conversion errror) */ LTSCOORDTRANS_EXPORT QPointF geographicFromMgrs(const QString &mgrs); /** * @brief addMarginToArea add a margin in meter to a north _orientated_ area * @param rect_LatLon_radiant rect top be increased * @param marginMeter margin to be added * @return increase Area */ LTSCOORDTRANS_EXPORT QRectF addMarginToArea(const QRectF &rect_LatLon_radiant, const QSize &marginMeter); /** * Return all MGRS squares covered by the current exercise area * @param rect the rectangle to retriebe the mgrs squares for (in geographic coordinates) * @param setAsDefaultSquares if true, then set the calculated squares as default squares returned by defaultMgrsSquares() * @param setAsPreferedSquare if true, then set the center square as prefered square returned by preferedMgrsSquare() * @return all current available mgrs squares */ LTSCOORDTRANS_EXPORT QVector getMgrsSquares(const QRectF &rect, bool setAsDefaultSquares, bool setAsPreferedSquare, bool setAsSquareWithMargin = false); /** * @brief getMgrsSquaresInternal * @param rect * @return */ QVector getMgrsSquaresInternal(const QRectF &rect); /** * @brief getMgrsSurroundingSquares * @param geoRect * @param offsetMeter * @return */ QStringList getMgrsSurroundingSquares(const QRectF &geoRect, const QSize &offsetMeter); /** * @brief defaultMgrsSquareWithOffsets * @return */ LTSCOORDTRANS_EXPORT QVector defaultMgrsSquareWithOffsets(); /** * @brief Set default MGRS squares to use if getMgrsSquares() is not possible */ LTSCOORDTRANS_EXPORT void setDefaultMgrsSquares(const QVector &squares); /** * @brief Return previously set default squares (e.g. for LtsCoordWidget) * @return all available mgrs squares for the rect set with getMgrsSquares() */ LTSCOORDTRANS_EXPORT QVector defaultMgrsSquares(); /** * @brief Return previously set prefered square (e.g. for LtsCoordWidget) * @return prefered mgrs square (center) of the rect set with getMgrsSquares() */ LTSCOORDTRANS_EXPORT UtmCoordinate preferedMgrsSquare(); /** * @brief Convert lon/lat/altitude to geocentric coordinates, output in x,y,z * @param lon longitude in radian * @param lat latitude in radian * @param altitude in meter above ground * @param xOut geocentric x-coordinate * @param yOut geocentric y-coordinate * @param zOut geocentric z-coordinate * @return true on success */ LTSCOORDTRANS_EXPORT bool lonLatAltToGeocentric(double lon, double lat, double altitude, double &xOut, double &yOut, double &zOut); /** * @brief Convert geocentric coordinates to lon/lat/altitude output in lon,lat,altiude * @param x geocentric x-coordinate * @param y geocentric y-coordinate * @param z geocentric z-coordinate * @param lon longitude in radian * @param lat latitude in radian * @param altitude in meter above ground * @return true on success */ LTSCOORDTRANS_EXPORT bool geocentric2LonLatAlt(double x, double y, double z, double &lon, double &lat, double &altitude); /** * Overload of double squareDistance(const double lon1, const double lat1, const double lon2, const double lat2) */ inline double squareDistance(const UtmCoordinate &p1, const UtmCoordinate &p2) { if (p1.zone != p2.zone) { Q_ASSERT(false); return std::nan(""); } return std::pow(p1.x - p2.x, 2) + std::pow(p1.y - p2.y, 2); } /** * Overload of double squareDistance(const double lon1, const double lat1, const double lon2, const double lat2) */ LTSCOORDTRANS_EXPORT double squareDistance(const QPointF &p1, const QPointF &p2); /** * @brief Calculate the square distance between two geographicPoints in meter * @param lon1 longitude of the first point * @param lat1 latitude of the first point * @param lon2 longitude of the second point * @param lat2 latitude of the second point * @return distance between the two points in meter */ inline double squareDistance(const double lon1, const double lat1, const double lon2, const double lat2) { const auto p1(UtmCoordinate::fromGeographic(lon1, lat1)); const auto p2(UtmCoordinate::fromGeographic(lon2, lat2, p1.zone)); return squareDistance(p1, p2); } /** * Overload of double squareDistance(const double lon1, const double lat1, const double lon2, const double lat2) */ inline double squareDistance(const UtmCoordinate &p1, const double lon2, const double lat2) { const auto p2(UtmCoordinate::fromGeographic(lon2, lat2, p1.zone)); return squareDistance(p1, p2); } /** * Overload of double distance(const double lon1, const double lat1, const double lon2, const double lat2) */ inline double distance(const UtmCoordinate &p1, const UtmCoordinate &p2) { if (p1.zone != p2.zone) { Q_ASSERT(false); return std::nan(""); } return sqrt(squareDistance(p1, p2)); } /** * Overload of double distance(const double lon1, const double lat1, const double lon2, const double lat2) */ LTSCOORDTRANS_EXPORT double distance(const QPointF &p1, const QPointF &p2); /** * @brief Calculate the distance between two geographicPoints in meter * @param lon1 longitude of the first point * @param lat1 latitude of the first point * @param lon2 longitude of the second point * @param lat2 latitude of the second point * @return distance between the two points in meter */ inline double distance(const double lon1, const double lat1, const double lon2, const double lat2) { return sqrt(squareDistance(lon1, lat1, lon2, lat2)); } inline double distance(const UtmCoordinate &p1, const double lon2, const double lat2) { return sqrt(squareDistance(p1, lon2, lat2)); } // for debugging purpose: LTSCOORDTRANS_EXPORT void dumpConversionCounter(); LTSCOORDTRANS_EXPORT long getConversionCounter_MGRS_Forward(); LTSCOORDTRANS_EXPORT long getConversionCounter_MGRS_Reverse(); LTSCOORDTRANS_EXPORT long getConversionCounter_WGS84_Forward(); LTSCOORDTRANS_EXPORT long getConversionCounter_WGS84_Reverse(); LTSCOORDTRANS_EXPORT long getConversionCounter_UTMUPS_Forward(); LTSCOORDTRANS_EXPORT long getConversionCounter_UTMUPS_Reverse(); LTSCOORDTRANS_EXPORT std::ostream &operator<< (std::ostream &stream, const UtmCoordinate &coord); // // class UtmCoordinate3D // /*! * \brief The class UtmCoordinate3D add just a zValue to its baseClass UtmCoordinate */ class LTSCOORDTRANS_EXPORT UtmCoordinate3D : public LtsCoordTrans::UtmCoordinate { using BaseClass = LtsCoordTrans::UtmCoordinate; public: UtmCoordinate3D() { reset(); } explicit UtmCoordinate3D(const LtsCoordTrans::UtmCoordinate &utm2d, double _zValue = std::nan("")) :BaseClass() { zone = utm2d.zone; northp = utm2d.northp; x = utm2d.x; y = utm2d.y; zValue = _zValue; } void reset() { zone = -1; northp = true; // To be consistent with LtsCoordinate: // For elevation a value of '0' should be valid. // So an invalid zValue has to be different: choose NaN // To be consistent to default constructor of Core::GeoPosition, the (invalid) default value for lon, lat is '0': x = 0.; // in meter y = 0.; // in meter zValue = std::nan(""); } double z() const { return zValue; } protected: double zValue{std::nan("")}; }; } // namespace LtsCoordTrans