453 lines
14 KiB
C++
Executable File
453 lines
14 KiB
C++
Executable File
/**
|
|
* @RDE_HEADER@
|
|
* Provide some coordinate translation functions on top of GeographicLib
|
|
* $Id$
|
|
* $HeadURL$
|
|
* $Author$
|
|
*
|
|
* Created by Christian Ehrlicher, SLSE, 2016-08-31
|
|
*/
|
|
|
|
#include "lts_coord_trans.h"
|
|
|
|
#include <GeographicLib/Geocentric.hpp>
|
|
#include <GeographicLib/UTMUPS.hpp>
|
|
#include <GeographicLib/MGRS.hpp>
|
|
#include <QPointF>
|
|
#include <QRectF>
|
|
#include <QString>
|
|
#include <QStringList>
|
|
|
|
// #include <QDebug>
|
|
#include <iomanip> // setprecision
|
|
|
|
namespace LtsCoordTrans
|
|
{
|
|
|
|
static long s_conversion_counter_MGRS_Forward = 0; // counts conversion
|
|
static long s_conversion_counter_MGRS_Reverse = 0; // counts conversion
|
|
|
|
static long s_conversion_counter_WGS84_Forward = 0; // counts conversion
|
|
static long s_conversion_counter_WGS84_Reverse = 0; // counts conversion
|
|
|
|
static long s_conversion_counter_UTMUPS_Forward = 0; // counts conversion
|
|
static long s_conversion_counter_UTMUPS_Reverse = 0; // counts conversion
|
|
|
|
|
|
inline void incrementCounter(long &counterRef)
|
|
{
|
|
#ifdef LTS_DEBUG
|
|
++counterRef;
|
|
#endif
|
|
}
|
|
|
|
|
|
static inline std::string cleanupMgrsString(const QString &in)
|
|
{
|
|
const QByteArray ba(in.toLatin1());
|
|
std::string ret;
|
|
ret.reserve(ba.size());
|
|
for (const char c : ba)
|
|
{
|
|
if (c != ' ')
|
|
ret += c;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
QString UtmCoordinate::mgrs(int prec, bool withSpaces) const
|
|
{
|
|
if (prec < 0)
|
|
prec = 0;
|
|
if (prec > 11)
|
|
prec = 11;
|
|
|
|
std::string mgrs;
|
|
try
|
|
{
|
|
GeographicLib::MGRS::Forward(zone, northp, x, y, prec, mgrs);
|
|
incrementCounter(s_conversion_counter_MGRS_Forward);
|
|
}
|
|
catch (const std::exception &e)
|
|
{
|
|
fprintf(stderr, "Error while calculating mgrs from utm %d, %9.6f, %9.6f: %s\n", zone, x, y, e.what());
|
|
mgrs.clear();
|
|
}
|
|
|
|
QString ret;
|
|
if (withSpaces && mgrs.size() == static_cast<size_t>(5 + 2 * prec))
|
|
{
|
|
// 32UMD1234512345 -> 32U MD 12345 12345
|
|
const char *ptr = mgrs.c_str();
|
|
// for utm zones < 10, GeiographicLib appends a zero -> 08
|
|
ret = QString::fromLatin1(ptr, 3) + QLatin1Char(' ') +
|
|
QString::fromLatin1(ptr + 3, 2);
|
|
if (prec > 0)
|
|
{
|
|
ret += QLatin1Char(' ') + QString::fromLatin1(ptr + 5, prec) +
|
|
QLatin1Char(' ') + QString::fromLatin1(ptr + 5 + prec, prec);
|
|
}
|
|
}
|
|
else
|
|
ret = QString::fromLatin1(mgrs.c_str(), mgrs.size());
|
|
return ret;
|
|
}
|
|
|
|
QPointF UtmCoordinate::toPoint() const
|
|
{
|
|
return {x,y};
|
|
}
|
|
|
|
QPointF UtmCoordinate::geographic(double offsetX, double offsetY) const
|
|
{
|
|
QPointF ret;
|
|
try
|
|
{
|
|
GeographicLib::UTMUPS::Reverse(zone, northp, x + offsetX, y + offsetY, ret.ry(), ret.rx());
|
|
incrementCounter(s_conversion_counter_UTMUPS_Reverse);
|
|
}
|
|
catch (const std::exception &e)
|
|
{
|
|
fprintf(stderr, "Error while calculating geographic from utm %d, %9.6f, %9.6f: %s\n", zone, x + offsetX, y + offsetY, e.what());
|
|
ret = QPointF();
|
|
}
|
|
return ret * deg2rad();
|
|
}
|
|
|
|
QPointF UtmCoordinate::geographic(const QPointF &offset) const
|
|
{
|
|
return geographic(offset.x(), offset.y());
|
|
}
|
|
|
|
QPointF UtmCoordinate::relativeTo(const UtmCoordinate &other) const
|
|
{
|
|
if (zone == other.zone)
|
|
return QPointF(x - other.x, y - other.y);
|
|
|
|
UtmCoordinate otherInMyZone = fromGeographic(other.geographic(), zone);
|
|
return QPointF(x - otherInMyZone.x, y - otherInMyZone.y);
|
|
}
|
|
|
|
UtmCoordinate UtmCoordinate::fromGeographic(double lon, double lat, int setZone)
|
|
{
|
|
// radian -> degree
|
|
lon *= rad2deg();
|
|
lat *= rad2deg();
|
|
|
|
UtmCoordinate ret;
|
|
try
|
|
{
|
|
// lat/lon -> utm
|
|
GeographicLib::UTMUPS::Forward(lat, lon, ret.zone, ret.northp, ret.x, ret.y, setZone);
|
|
incrementCounter(s_conversion_counter_UTMUPS_Forward);
|
|
}
|
|
catch (const std::exception &e)
|
|
{
|
|
fprintf(stderr, "Error while calculating mgrs base point from %9.6f, %9.6f: %s\n", lat, lon, e.what());
|
|
ret = UtmCoordinate();
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
UtmCoordinate UtmCoordinate::fromGeographic(const QPointF &lonLat, int setZone)
|
|
{
|
|
return fromGeographic(lonLat.x(), lonLat.y(), setZone);
|
|
}
|
|
|
|
UtmCoordinate UtmCoordinate::fromMgrs(const QString &mgrs)
|
|
{
|
|
UtmCoordinate utm;
|
|
try
|
|
{
|
|
int prec;
|
|
// centerp must be true (GeographicLib::MGRS::Forward calculates with this!)
|
|
GeographicLib::MGRS::Reverse(cleanupMgrsString(mgrs), utm.zone, utm.northp, utm.x, utm.y, prec, true);
|
|
incrementCounter(s_conversion_counter_MGRS_Reverse);
|
|
|
|
}
|
|
catch (const std::exception &e)
|
|
{
|
|
fprintf(stderr, "Error while converting mgrs coordinate %s to utm: %s\n", qPrintable(mgrs), e.what());
|
|
utm = UtmCoordinate();
|
|
}
|
|
return utm;
|
|
}
|
|
|
|
QString mgrsFromGeographic(double lon, double lat, int prec, bool withSpaces)
|
|
{
|
|
return UtmCoordinate::fromGeographic(lon, lat, -1).mgrs(prec, withSpaces);
|
|
}
|
|
|
|
QPointF geographicFromMgrs(const QString &mgrs)
|
|
{
|
|
return UtmCoordinate::fromMgrs(mgrs).geographic();
|
|
}
|
|
|
|
QVector<UtmCoordinate> &staticDefaultSquaresHelper()
|
|
{
|
|
static QVector<UtmCoordinate> s_defaultMgrsSquares;
|
|
return s_defaultMgrsSquares;
|
|
}
|
|
UtmCoordinate &staticPreferedMgrsSquareHelper()
|
|
{
|
|
static UtmCoordinate s_preferedMgrsSquare;
|
|
return s_preferedMgrsSquare;
|
|
}
|
|
QVector<UtmCoordinate> &staticMgrsSquaresWithOffsetsHelper()
|
|
{
|
|
static QVector<UtmCoordinate> s_mgrsSquaresWithOffsets;
|
|
return s_mgrsSquaresWithOffsets;
|
|
}
|
|
|
|
|
|
QRectF addMarginToArea(const QRectF &rect_LatLon_radiant, const QSize &marginMeter)
|
|
{
|
|
double lonMin, lonMax, latMin, latMmax;
|
|
rect_LatLon_radiant.normalized().getCoords(&lonMin, &latMin, &lonMax, &latMmax);
|
|
|
|
// In that case upperRight and lowerLeft have to be converted to an UtmCoordinate
|
|
// using the utm zone of the basepoint.
|
|
// so caluclate the basepoint of the training area:
|
|
// see also basePointOfTrainingArea at Lts/Proxies/TrainingAreaBase/tab_training_area_helper.h
|
|
LtsCoordTrans::UtmCoordinate basePoint;
|
|
{
|
|
// calc tetra base point from training area center
|
|
const QPointF centerRad = rect_LatLon_radiant.center();
|
|
QString mgrs = LtsCoordTrans::mgrsFromGeographic(centerRad.x(), centerRad.y(), -1);
|
|
mgrs += QStringLiteral("00000000000000");
|
|
|
|
// now we've the correct tetra base point
|
|
basePoint = LtsCoordTrans::UtmCoordinate::fromMgrs(mgrs);
|
|
}
|
|
|
|
const UtmCoordinate lowerLeft(UtmCoordinate::fromGeographic(lonMin, latMin, basePoint.zone));
|
|
const UtmCoordinate upperRight(UtmCoordinate::fromGeographic(lonMax, latMmax, basePoint.zone));
|
|
|
|
int offsetX = marginMeter.width();
|
|
int offsetY = marginMeter.height();
|
|
|
|
const QPointF topLeft(lowerLeft.geographic(-offsetX, -offsetY));
|
|
const QPointF bottomRight(upperRight.geographic(offsetX, offsetY));
|
|
|
|
QRectF res = QRectF(topLeft, bottomRight);
|
|
return res;
|
|
}
|
|
|
|
|
|
QVector<UtmCoordinate> getMgrsSquares(const QRectF &rect, bool setAsDefaultSquares, bool setAsPreferedSquare, bool setAsSquareWithMargin)
|
|
{
|
|
QVector<UtmCoordinate> ret = getMgrsSquaresInternal(rect);
|
|
|
|
if (setAsDefaultSquares)
|
|
staticDefaultSquaresHelper() = ret;
|
|
|
|
if (setAsPreferedSquare)
|
|
staticPreferedMgrsSquareHelper() = LtsCoordTrans::UtmCoordinate::fromGeographic(rect.center());
|
|
|
|
if (setAsSquareWithMargin)
|
|
staticMgrsSquaresWithOffsetsHelper() = ret;
|
|
|
|
|
|
return ret;
|
|
}
|
|
|
|
QVector<UtmCoordinate> getMgrsSquaresInternal(const QRectF &rect)
|
|
{
|
|
QVector<UtmCoordinate> ret;
|
|
QVector<QString> tmpMgrsSquares;
|
|
// calculate mgrs squares
|
|
// 10km - Schritte - hoffentlich sind wir nicht in kleineren Zonen unterwegs
|
|
double lonMin, lonMax, latMin, latMmax;
|
|
rect.normalized().getCoords(&lonMin, &latMin, &lonMax, &latMmax);
|
|
|
|
// maybe the training area is NOT within a single UTM square.
|
|
bool withInSingleUtmSquare = true;
|
|
{
|
|
const UtmCoordinate upperRight(UtmCoordinate::fromGeographic(lonMax, latMmax));
|
|
const UtmCoordinate lowerLeft(UtmCoordinate::fromGeographic(lonMin, latMin));
|
|
withInSingleUtmSquare = upperRight.zone == lowerLeft.zone;
|
|
}
|
|
|
|
// In that case upperRight and lowerLeft have to be converted to an UtmCoordinate
|
|
// using the utm zone of the basepoint.
|
|
// so caluclate the basepoint of the training area:
|
|
// see also basePointOfTrainingArea at Lts/Proxies/TrainingAreaBase/tab_training_area_helper.h
|
|
LtsCoordTrans::UtmCoordinate basePoint;
|
|
{
|
|
// calc tetra base point from training area center
|
|
QPointF centerRad = rect.center();
|
|
QString mgrs = LtsCoordTrans::mgrsFromGeographic(centerRad.x(), centerRad.y(), -1);
|
|
mgrs += QStringLiteral("00000000000000");
|
|
|
|
// now we've the correct tetra base point
|
|
basePoint = LtsCoordTrans::UtmCoordinate::fromMgrs(mgrs);
|
|
}
|
|
|
|
const UtmCoordinate upperRight(UtmCoordinate::fromGeographic(lonMax, latMmax, basePoint.zone));
|
|
const UtmCoordinate lowerLeft(UtmCoordinate::fromGeographic(lonMin, latMin, basePoint.zone));
|
|
|
|
const double maxX(upperRight.x - lowerLeft.x); // meter
|
|
const double maxY(upperRight.y - lowerLeft.y); // meter
|
|
|
|
// +10000 um auch den Rest rechts und oben zu berücksichtigen
|
|
// allerdings dann genau den Rand und auf keinen Fall darüber hinaus
|
|
const int increment = withInSingleUtmSquare ? 10000 : 1000;
|
|
for (double x = 0; x <= maxX + increment; x += increment)
|
|
{
|
|
const double xOfs = std::min(x, maxX);
|
|
for (double y = 0; y <= maxY + increment; y += increment)
|
|
{
|
|
const double yOfs = std::min(y, maxY);
|
|
// convert to utm coordinate on mgrs intersection point
|
|
const UtmCoordinate utm(UtmCoordinate::fromGeographic(lowerLeft.geographic(xOfs, yOfs)));
|
|
const QString mgrs(utm.mgrs(0));
|
|
// qDebug("mgrs: %s, %s, %lf, %lf", qPrintable(mgrs), qPrintable(utm.mgrs(5)), xOfs, yOfs);
|
|
if (!tmpMgrsSquares.contains(mgrs))
|
|
{
|
|
tmpMgrsSquares += mgrs;
|
|
ret += utm;
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
QStringList getMgrsSurroundingSquares(const QRectF &geoRect, const QSize &offsetMeter)
|
|
{
|
|
// get main squares
|
|
const QVector<UtmCoordinate> mainUtms = getMgrsSquaresInternal(geoRect);
|
|
QStringList mainSquares;
|
|
mainSquares.reserve(mainUtms.count());
|
|
for (const auto &utm : mainUtms)
|
|
mainSquares.append(utm.mgrs(0));
|
|
|
|
// get mainsquare with surrounding squares rect by main square center pos and offsets
|
|
const QRectF rectWithOffset = addMarginToArea(geoRect,offsetMeter);
|
|
|
|
QVector<UtmCoordinate> mainUtmsWithOffset = getMgrsSquaresInternal(rectWithOffset);
|
|
QStringList mainSquaresWithOffset;
|
|
mainSquaresWithOffset.reserve(mainUtmsWithOffset.count());
|
|
for (const auto &utm : qAsConst(mainUtmsWithOffset))
|
|
mainSquaresWithOffset.append(utm.mgrs(0));
|
|
|
|
// get only surrounding squares
|
|
QStringList surroundingSquares;
|
|
surroundingSquares.reserve(mainSquaresWithOffset.count());
|
|
for (const auto & mgrs0 : mainSquaresWithOffset)
|
|
{
|
|
if (!mainSquares.contains(mgrs0))
|
|
surroundingSquares.append(mgrs0);
|
|
}
|
|
|
|
return surroundingSquares;
|
|
}
|
|
|
|
QVector<UtmCoordinate> defaultMgrsSquareWithOffsets()
|
|
{
|
|
return staticMgrsSquaresWithOffsetsHelper();
|
|
}
|
|
|
|
|
|
void setDefaultMgrsSquares(const QVector<QString> &squares)
|
|
{
|
|
QVector<UtmCoordinate> coords;
|
|
coords.reserve(squares.size());
|
|
for (const QString &s : squares)
|
|
coords += UtmCoordinate::fromMgrs(s + QStringLiteral("00000000000000"));
|
|
staticDefaultSquaresHelper() = coords;
|
|
}
|
|
|
|
QVector<UtmCoordinate> defaultMgrsSquares()
|
|
{
|
|
return staticDefaultSquaresHelper();
|
|
}
|
|
|
|
UtmCoordinate preferedMgrsSquare()
|
|
{
|
|
return staticPreferedMgrsSquareHelper();
|
|
}
|
|
|
|
|
|
bool lonLatAltToGeocentric(double lon, double lat, double altitude, double &xOut, double &yOut, double &zOut)
|
|
{
|
|
// radian -> degree
|
|
lon *= rad2deg();
|
|
lat *= rad2deg();
|
|
|
|
try
|
|
{
|
|
GeographicLib::Geocentric::WGS84().Forward(lat, lon, altitude, xOut, yOut, zOut);
|
|
incrementCounter(s_conversion_counter_WGS84_Forward);
|
|
}
|
|
catch (...)
|
|
{
|
|
xOut = 0;
|
|
yOut = 0;
|
|
zOut = 0;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool geocentric2LonLatAlt(double x, double y, double z,
|
|
double &lon, double &lat, double &altitude)
|
|
{
|
|
// Achtung: LatLon statt LonLat
|
|
// Geocentric -> LonLat
|
|
try
|
|
{
|
|
GeographicLib::Geocentric::WGS84().Reverse(x, y, z, lat, lon, altitude);
|
|
incrementCounter(s_conversion_counter_WGS84_Reverse);
|
|
}
|
|
catch (...)
|
|
{
|
|
lon = 0;
|
|
lat = 0;
|
|
altitude = 0;
|
|
return false;
|
|
}
|
|
// degree -> radian
|
|
lon *= deg2rad();
|
|
lat *= deg2rad();
|
|
return true;
|
|
}
|
|
|
|
double distance(const QPointF &p1, const QPointF &p2)
|
|
{
|
|
return distance(p1.x(), p1.y(), p2.x(), p2.y());
|
|
}
|
|
|
|
double squareDistance(const QPointF &p1, const QPointF &p2)
|
|
{
|
|
return squareDistance(p1.x(), p1.y(), p2.x(), p2.y());
|
|
}
|
|
|
|
std::ostream &operator<< (std::ostream &stream,
|
|
const UtmCoordinate &coord)
|
|
{
|
|
|
|
stream << "UtmCoordinate(" << std::setprecision(8) << coord.x
|
|
<< "#" << std::setprecision(8) << coord.y
|
|
<< " zone=" << coord.zone << ")";
|
|
return stream;
|
|
}
|
|
|
|
void dumpConversionCounter()
|
|
{
|
|
fprintf(stderr, "LtsCoordTrans: s_conversion_counter_MGRS_Forward : - mgrs : %ld\n" , s_conversion_counter_MGRS_Forward);
|
|
fprintf(stderr, "LtsCoordTrans: s_conversion_counter_MGRS_Reverse - fromMgrs : %ld\n" , s_conversion_counter_MGRS_Reverse);
|
|
fprintf(stderr, "LtsCoordTrans: s_conversion_counter_WGS84_Forward - lonLatAltToGeocentric : %ld\n" , s_conversion_counter_WGS84_Forward);
|
|
fprintf(stderr, "LtsCoordTrans: s_conversion_counter_WGS84_Reverse - geocentric2LonLatAlt : %ld\n" , s_conversion_counter_WGS84_Reverse);
|
|
fprintf(stderr, "LtsCoordTrans: s_conversion_counter_UTMUPS_Forward - fromGeographic : %ld\n" , s_conversion_counter_UTMUPS_Forward);
|
|
fprintf(stderr, "LtsCoordTrans: s_conversion_counter_UTMUPS_Reverse - geographic : %ld\n" , s_conversion_counter_UTMUPS_Reverse);
|
|
}
|
|
|
|
long getConversionCounter_MGRS_Forward() { return s_conversion_counter_MGRS_Forward; }
|
|
long getConversionCounter_MGRS_Reverse() { return s_conversion_counter_MGRS_Reverse; }
|
|
long getConversionCounter_WGS84_Forward() { return s_conversion_counter_WGS84_Forward; }
|
|
long getConversionCounter_WGS84_Reverse() { return s_conversion_counter_WGS84_Reverse; }
|
|
long getConversionCounter_UTMUPS_Forward() { return s_conversion_counter_UTMUPS_Forward; }
|
|
long getConversionCounter_UTMUPS_Reverse() { return s_conversion_counter_UTMUPS_Reverse; }
|
|
|
|
} // namespace LtsCoordTrans
|