446 lines
12 KiB
C++
Executable File
446 lines
12 KiB
C++
Executable File
|
|
|
|
|
|
#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 <iomanip>
|
|
|
|
namespace LtsCoordTrans
|
|
{
|
|
|
|
static long s_conversion_counter_MGRS_Forward = 0;
|
|
static long s_conversion_counter_MGRS_Reverse = 0;
|
|
|
|
static long s_conversion_counter_WGS84_Forward = 0;
|
|
static long s_conversion_counter_WGS84_Reverse = 0;
|
|
|
|
static long s_conversion_counter_UTMUPS_Forward = 0;
|
|
static long s_conversion_counter_UTMUPS_Reverse = 0;
|
|
|
|
|
|
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))
|
|
{
|
|
|
|
const char *ptr = mgrs.c_str();
|
|
|
|
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)
|
|
{
|
|
|
|
lon *= rad2deg();
|
|
lat *= rad2deg();
|
|
|
|
UtmCoordinate ret;
|
|
try
|
|
{
|
|
|
|
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;
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
|
|
LtsCoordTrans::UtmCoordinate basePoint;
|
|
{
|
|
|
|
const QPointF centerRad = rect_LatLon_radiant.center();
|
|
QString mgrs = LtsCoordTrans::mgrsFromGeographic(centerRad.x(), centerRad.y(), -1);
|
|
mgrs += QStringLiteral("00000000000000");
|
|
|
|
|
|
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;
|
|
|
|
|
|
double lonMin, lonMax, latMin, latMmax;
|
|
rect.normalized().getCoords(&lonMin, &latMin, &lonMax, &latMmax);
|
|
|
|
|
|
bool withInSingleUtmSquare = true;
|
|
{
|
|
const UtmCoordinate upperRight(UtmCoordinate::fromGeographic(lonMax, latMmax));
|
|
const UtmCoordinate lowerLeft(UtmCoordinate::fromGeographic(lonMin, latMin));
|
|
withInSingleUtmSquare = upperRight.zone == lowerLeft.zone;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
LtsCoordTrans::UtmCoordinate basePoint;
|
|
{
|
|
|
|
QPointF centerRad = rect.center();
|
|
QString mgrs = LtsCoordTrans::mgrsFromGeographic(centerRad.x(), centerRad.y(), -1);
|
|
mgrs += QStringLiteral("00000000000000");
|
|
|
|
|
|
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);
|
|
const double maxY(upperRight.y - lowerLeft.y);
|
|
|
|
|
|
|
|
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);
|
|
|
|
const UtmCoordinate utm(UtmCoordinate::fromGeographic(lowerLeft.geographic(xOfs, yOfs)));
|
|
const QString mgrs(utm.mgrs(0));
|
|
|
|
if (!tmpMgrsSquares.contains(mgrs))
|
|
{
|
|
tmpMgrsSquares += mgrs;
|
|
ret += utm;
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
QStringList getMgrsSurroundingSquares(const QRectF &geoRect, const QSize &offsetMeter)
|
|
{
|
|
|
|
const QVector<UtmCoordinate> mainUtms = getMgrsSquaresInternal(geoRect);
|
|
QStringList mainSquares;
|
|
mainSquares.reserve(mainUtms.count());
|
|
for (const auto &utm : mainUtms)
|
|
mainSquares.append(utm.mgrs(0));
|
|
|
|
|
|
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));
|
|
|
|
|
|
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)
|
|
{
|
|
|
|
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)
|
|
{
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
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; }
|
|
|
|
}
|