/** * @RDE_HEADER@ * LtsCoordinate * $Id$ * $HeadURL$ * $Author$ * Created by Norbert Thoden, ESVG, 2024-08-27 */ #include "lts_coordinate.h" #include namespace LtsCoordTrans { static long s_conversion_counter = 0; // counts conversion // std::function LtsCoordinate::m_getElevationCB; //------------------------------------------------------------------------------ // class LtsCoordinate //------------------------------------------------------------------------------ void LtsCoordinate::setElevationCallback(const elevationCbFunc &elevationCB) { m_getElevationCB = elevationCB; } bool LtsCoordinate::hasElevationCallback() { return m_getElevationCB != nullptr; } double LtsCoordinate::elevationAt(double defaultElevation, bool *bIsCovered) const { // shall we cache bIsCovered also? // no. If bIsCovered is provided, we explicitly call m_getElevationCB // if not, we benefit from cache (m_elevation); if (!bIsCovered) if (!std::isnan(m_elevation)) return m_elevation; if (m_getElevationCB) m_elevation = m_getElevationCB(m_geo.lon(), m_geo.lat(), defaultElevation, bIsCovered); return m_elevation; } void LtsCoordinate::setZValueAccordingElevation(double defaultElevation) { m_geo.z(elevationAt(defaultElevation)); } double LtsCoordinate::elevationAt(const LtsCoordinate &coordinate, double defaultElevation, bool *bIsCovered) { return coordinate.elevationAt(defaultElevation, bIsCovered); } void LtsCoordinate::replaceZValueByElevationIfValue(double zValueToBeReplaced, double defaultElevation) { // 2024-10: Norbert Thoden // if (qFuzzyCompare(m_geo.z(), zValueToBeReplaced)) did not work with NaN as expected // qFuzzyCompare(std::nan(""), std::nan("")) will return 'false' !!! // so check NaN explicit: if ((std::isnan(zValueToBeReplaced) && std::isnan(m_geo.z())) || qFuzzyCompare(m_geo.z(), zValueToBeReplaced)) setZValueAccordingElevation(defaultElevation); } double LtsCoordinate::angleDegreeUtm2D(const LtsCoordinate &coord1, const LtsCoordinate &coord2) { const LtsCoordTrans::UtmCoordinate &utm1 = coord1.utmCoordinate(); // implicit coord1.zone will be set const LtsCoordTrans::UtmCoordinate &utm2 = coord2.utmCoordinateDiffZone(utm1.zone); const QPointF& offsetUtm = utm2.toPoint() - utm1.toPoint(); QLineF poly(QPointF(), offsetUtm); const double angle = poly.angle(); // QLineF::angle() // 270 // | // | // 180 --------- 0 // | // | // 90 // desired: // 0 // | // | // 270 --------- 90 // | // | // 180 return fmod(angle+90.0, 360); } const UtmCoordinate &LtsCoordinate::utmCoordinateInternal(int zone) const { if (zone == -1) { if (!hasUtm()) convertGeoToUtm(); return m_utm2D; // <- return } utmCoordinateDiffZone(zone); // ensure m_utm2D_differentZone return m_utm2D_differentZone; // <- return } void LtsCoordinate::convertGeoToUtm() const { // geo -> utm int zone = -1; // avoid working with NaN if (std::isnan(m_geo.lon()) || std::isnan(m_geo.lat())) { fprintf(stderr, "Error while fromGeographic, value is NaN\n"); m_utm2D = LtsCoordTrans::UtmCoordinate(); return; } #ifdef LTS_DEBUG ++s_conversion_counter; #endif // LTS_DEBUG m_utm2D = LtsCoordTrans::UtmCoordinate::fromGeographic(m_geo.lon(), m_geo.lat(), zone); Q_ASSERT_X(m_utm2D.zone > 0, "LtsCoordinate", "zone has to be set"); } QString LtsCoordinate::mgrs(int prec, bool withSpaces) const { utmCoordinateInternal(); // ensure m_utm2D QString tmp = m_utm2D.mgrs(prec, withSpaces); return tmp; } QString LtsCoordinate::mgrsWithZ(int prec, bool withSpaces) const { double ele = elevationAt(); QString tmp = mgrs(prec, withSpaces) + QStringLiteral(" z=") + QString::number(ele, 'f', 2); return tmp; } QString LtsCoordinate::toString(bool fullAndExpensive) const { if (fullAndExpensive) { utmCoordinateInternal(); // ensure m_utm2D elevationAt(); // ensure m_elevation } std::ostringstream stream; stream << "LtsCoordinate(m_geo=" << m_geo << ", m_utm2D.x " << m_utm2D.x << ", m_utm2D.y " << m_utm2D.y << ", m_utm2D.zone " << m_utm2D.zone; if (!qFuzzyIsNull(m_utm2D.x) || !qFuzzyIsNull(m_utm2D.y)) stream << ", m_utm2D.mgrs '" << m_utm2D.mgrs().toUtf8().constData() << "'"; stream << ", elevation " << std::fixed << std::setprecision(3) << m_elevation // << std::fixed << std::setprecision(3) -> 3 decimal places << ")"; return QString::fromStdString(stream.str()); } void LtsCoordinate::resetConversionCounter() { s_conversion_counter = 0; } long LtsCoordinate::getConversionCounter() { return s_conversion_counter; } // output operator: std::ostream &operator<<(std::ostream &stream, const LtsCoordinate &value) { stream << value.toString().toStdString(); return stream; } } // namespace LtsCoordTrans