From 0c877e1575477b1bb863112f730a25856bc8fdb1 Mon Sep 17 00:00:00 2001 From: Klaus Basan Date: Wed, 7 Mar 2018 01:31:02 +0100 Subject: [PATCH] Ref T259, Ref T243 coordinate list and improved "find" functions in IGeoObjectList --- cppcheck.supp | 1 + src/blackmisc/geo/coordinategeodetic.cpp | 3 +- src/blackmisc/geo/coordinategeodetic.h | 3 + src/blackmisc/geo/coordinategeodeticlist.cpp | 31 ++++++ src/blackmisc/geo/coordinategeodeticlist.h | 57 ++++++++++ src/blackmisc/geo/geo.h | 1 + src/blackmisc/geo/geoobjectlist.cpp | 108 ++++++++++++++++++- src/blackmisc/geo/geoobjectlist.h | 53 +++++++-- src/blackmisc/geo/registermetadatageo.cpp | 1 + 9 files changed, 244 insertions(+), 14 deletions(-) create mode 100644 src/blackmisc/geo/coordinategeodeticlist.cpp create mode 100644 src/blackmisc/geo/coordinategeodeticlist.h diff --git a/cppcheck.supp b/cppcheck.supp index 24a4a2547..9be048510 100644 --- a/cppcheck.supp +++ b/cppcheck.supp @@ -19,6 +19,7 @@ passedByValue:src/blackmisc/input/actionhotkeylist.h passedByValue:src/blackmisc/network/rawfsdmessagelist.cpp passedByValue:src/blackmisc/logcategorylist.h passedByValue:src/blackmisc/platformset.cpp +passedByValue:src/blackmisc/geo/coordinategeodeticlist.cpp // Shared pointers should be passed by value passedByValue:src/blackmisc/lockfree.h diff --git a/src/blackmisc/geo/coordinategeodetic.cpp b/src/blackmisc/geo/coordinategeodetic.cpp index 0d35fd7a9..23f087c58 100644 --- a/src/blackmisc/geo/coordinategeodetic.cpp +++ b/src/blackmisc/geo/coordinategeodetic.cpp @@ -249,8 +249,7 @@ namespace BlackMisc void CCoordinateGeodetic::setGeodeticHeightToNull() { - static const CAltitude na = CAltitude(0, CAltitude::MeanSeaLevel, CLengthUnit::nullUnit()); - this->setGeodeticHeight(na); + this->setGeodeticHeight(CAltitude::null()); } CCoordinateGeodetic &CCoordinateGeodetic::switchUnit(const CLengthUnit &unit) diff --git a/src/blackmisc/geo/coordinategeodetic.h b/src/blackmisc/geo/coordinategeodetic.h index 4ad562946..2e43ac9ec 100644 --- a/src/blackmisc/geo/coordinategeodetic.h +++ b/src/blackmisc/geo/coordinategeodetic.h @@ -95,6 +95,9 @@ namespace BlackMisc //! Geodetic height null? bool isGeodeticHeightNull() const { return this->geodeticHeight().isNull(); } + //! Geodetic height not null and Aviation::CAltitude::MeanSeaLevel + bool hasMSLGeodeticHeight() const { return !this->geodeticHeight().isNull() && this->geodeticHeight().getReferenceDatum() == Aviation::CAltitude::MeanSeaLevel; } + //! Is null, means vector x, y, z == 0 //! \remark this is a default implementation, concrete implementations of ICoordinateGeodetic might override it virtual bool isNull() const { return this->normalVector().isNull(); } diff --git a/src/blackmisc/geo/coordinategeodeticlist.cpp b/src/blackmisc/geo/coordinategeodeticlist.cpp new file mode 100644 index 000000000..158fa82aa --- /dev/null +++ b/src/blackmisc/geo/coordinategeodeticlist.cpp @@ -0,0 +1,31 @@ +/* Copyright (C) 2018 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "coordinategeodeticlist.h" + +#include +#include +#include + +namespace BlackMisc +{ + namespace Geo + { + CCoordinateGeodeticList::CCoordinateGeodeticList() + { } + + CCoordinateGeodeticList::CCoordinateGeodeticList(const CSequence &other) : + CSequence(other) + { } + + CCoordinateGeodeticList::CCoordinateGeodeticList(std::initializer_list il) : + CSequence(il) + { } + } // namespace +} // namespace diff --git a/src/blackmisc/geo/coordinategeodeticlist.h b/src/blackmisc/geo/coordinategeodeticlist.h new file mode 100644 index 000000000..fec084435 --- /dev/null +++ b/src/blackmisc/geo/coordinategeodeticlist.h @@ -0,0 +1,57 @@ +/* Copyright (C) 2018 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKMISC_GEO_COORDINATEGEODETICLIST_H +#define BLACKMISC_GEO_COORDINATEGEODETICLIST_H + +#include "coordinategeodetic.h" +#include "geoobjectlist.h" +#include "blackmisc/blackmiscexport.h" +#include "blackmisc/collection.h" +#include "blackmisc/json.h" +#include "blackmisc/sequence.h" +#include "blackmisc/variant.h" + +#include +#include +#include + +namespace BlackMisc +{ + namespace Geo + { + //! Value object encapsulating a list of coordinates. + class BLACKMISC_EXPORT CCoordinateGeodeticList : + public CSequence, + public IGeoObjectList, + public Mixin::MetaType, + public Mixin::JsonOperators + { + public: + BLACKMISC_DECLARE_USING_MIXIN_METATYPE(CCoordinateGeodeticList) + + //! Default constructor. + CCoordinateGeodeticList(); + + //! Construct by coordinates + CCoordinateGeodeticList(std::initializer_list coordinates); + + //! Construct from a base class object. + CCoordinateGeodeticList(const CSequence &other); + }; + } //namespace +} // namespace + +Q_DECLARE_METATYPE(BlackMisc::Geo::CCoordinateGeodeticList) +Q_DECLARE_METATYPE(BlackMisc::CCollection) +Q_DECLARE_METATYPE(BlackMisc::CSequence) + +#endif //guard diff --git a/src/blackmisc/geo/geo.h b/src/blackmisc/geo/geo.h index 996e5db0f..ffbcbb98a 100644 --- a/src/blackmisc/geo/geo.h +++ b/src/blackmisc/geo/geo.h @@ -19,6 +19,7 @@ #include "blackmisc/geo/latitude.h" #include "blackmisc/geo/longitude.h" #include "blackmisc/geo/coordinategeodetic.h" +#include "blackmisc/geo/coordinategeodeticlist.h" #include "blackmisc/geo/elevationplane.h" #endif // guard diff --git a/src/blackmisc/geo/geoobjectlist.cpp b/src/blackmisc/geo/geoobjectlist.cpp index 2c68ea8ba..038a13a85 100644 --- a/src/blackmisc/geo/geoobjectlist.cpp +++ b/src/blackmisc/geo/geoobjectlist.cpp @@ -8,14 +8,16 @@ */ #include "blackmisc/geo/geoobjectlist.h" +#include "blackmisc/geo/geo.h" #include "blackmisc/geo/coordinategeodetic.h" #include "blackmisc/aviation/atcstationlist.h" #include "blackmisc/aviation/airportlist.h" #include "blackmisc/aviation/atcstationlist.h" -#include "blackmisc/simulation/simulatedaircraft.h" +#include "blackmisc/aviation/aircraftsituationlist.h" #include "blackmisc/simulation/simulatedaircraftlist.h" #include "blackmisc/simulation/xplane/navdatareference.h" +using namespace BlackMisc::Aviation; using namespace BlackMisc::PhysicalQuantities; namespace BlackMisc @@ -51,6 +53,68 @@ namespace BlackMisc }); } + template + CONTAINER IGeoObjectList::findWithGeodeticMSLHeight() const + { + return this->container().findBy(&OBJ::hasMSLGeodeticHeight, true); + } + + template + bool IGeoObjectList::containsObjectInRange(const ICoordinateGeodetic &coordinate, const CLength &range) const + { + return this->container().containsBy([&](const OBJ & geoObj) + { + const CLength d = coordinate.calculateGreatCircleDistance(geoObj); + return d <= range; + }); + } + + template + typename IGeoObjectList::MinMaxAverageHeight IGeoObjectList::findMinMaxAverageHeight() const + { + MinMaxAverageHeight stats{ CAltitude::null(), CAltitude::null(), CAltitude::null(), 0 }; + int c = 0; + double avgFt = 0; + for (const OBJ &obj : this->container()) + { + if (!obj.hasMSLGeodeticHeight()) { continue; } + const CAltitude alt = obj.geodeticHeight(); + if (std::get<0>(stats).isNull() || std::get<0>(stats) > alt) + { + std::get<0>(stats) = alt; + } + if (std::get<1>(stats).isNull() || std::get<1>(stats) < alt) + { + std::get<1>(stats) = alt; + } + avgFt += alt.value(CLengthUnit::ft()); + } + + std::get<2>(stats) = CAltitude(avgFt / c, CAltitude::MeanSeaLevel, CLengthUnit::ft()); + std::get<3>(stats) = c; + return stats; + } + + template + int IGeoObjectList::removeOutsideRange(const ICoordinateGeodetic &coordinate, const PhysicalQuantities::CLength &range) + { + const int size = this->container().size(); + const CONTAINER copy = this->container().findWithinRange(coordinate, range); + const int d = size - copy.size(); + if (d > 0) { *this = copy; } + return d; + } + + template + int IGeoObjectList::removeWithoutGeodeticHeight() + { + const int size = this->container().size(); + const CONTAINER copy = this->findWithGeodeticMSLHeight(); + const int d = size - copy.size(); + if (d > 0) { *this = copy; } + return d; + } + template CONTAINER IGeoObjectList::findClosest(int number, const ICoordinateGeodetic &coordinate) const { @@ -58,10 +122,45 @@ namespace BlackMisc { return calculateEuclideanDistanceSquared(a, coordinate) < calculateEuclideanDistanceSquared(b, coordinate); }); - closest.truncate(number); + Q_ASSERT_X(closest.size() <= number, Q_FUNC_INFO, "size exceeded"); return closest; } + template + OBJ IGeoObjectList::findClosestWithinRange(const ICoordinateGeodetic &coordinate, const CLength &range) const + { + OBJ closest; + CLength distance = CLength::null(); + for (const OBJ &obj : this->container()) + { + const CLength d = coordinate.calculateGreatCircleDistance(obj); + if (d > range) { continue; } + if (distance.isNull() || distance > d) + { + distance = d; + closest = obj; + } + } + return closest; + } + + template + void IGeoObjectList::sortByEuclideanDistanceSquared(const ICoordinateGeodetic &coordinate) + { + this->container().sort([ & ](const OBJ & a, const OBJ & b) + { + return calculateEuclideanDistanceSquared(a, coordinate) < calculateEuclideanDistanceSquared(b, coordinate); + }); + } + + template + CONTAINER IGeoObjectList::sortedByEuclideanDistanceSquared(const ICoordinateGeodetic &coordinate) + { + CONTAINER copy(this->container()); + copy.sortByEuclideanDistanceSquared(coordinate); + return copy; + } + template void IGeoObjectWithRelativePositionList::calculcateAndUpdateRelativeDistanceAndBearing(const ICoordinateGeodetic &position) { @@ -111,7 +210,7 @@ namespace BlackMisc if (this->container().size() >= number) { return (this->container()); } CONTAINER closest(this->container()); closest.partiallySortByDistanceToOwnAircraft(number); - closest.truncate(number); + Q_ASSERT_X(closest.size() <= number, Q_FUNC_INFO, "size exceeded"); return closest; } @@ -120,9 +219,10 @@ namespace BlackMisc //! \cond PRIVATE template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IGeoObjectList; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IGeoObjectList; + template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IGeoObjectList; + template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IGeoObjectList; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IGeoObjectList; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IGeoObjectList; - template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IGeoObjectWithRelativePositionList; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IGeoObjectWithRelativePositionList; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IGeoObjectWithRelativePositionList; diff --git a/src/blackmisc/geo/geoobjectlist.h b/src/blackmisc/geo/geoobjectlist.h index dc77485c4..0d0a5bea1 100644 --- a/src/blackmisc/geo/geoobjectlist.h +++ b/src/blackmisc/geo/geoobjectlist.h @@ -12,9 +12,13 @@ #ifndef BLACKMISC_GEO_GEOOBJECTLIST_H #define BLACKMISC_GEO_GEOOBJECTLIST_H +#include "blackmisc/aviation/altitude.h" +#include "blackmisc/pq/length.h" #include "blackmisc/blackmiscexport.h" #include "blackmisc/sequence.h" -#include "blackmisc/pq/length.h" + +#include +#include namespace BlackMisc { @@ -24,6 +28,8 @@ namespace BlackMisc class CAtcStationList; class CAirport; class CAirportList; + class CAircraftSituation; + class CAircraftSituationList; } namespace Simulation @@ -41,19 +47,48 @@ namespace BlackMisc namespace Geo { class ICoordinateGeodetic; + class CCoordinateGeodetic; + class CCoordinateGeodeticList; //! List of objects with geo coordinates. template class IGeoObjectList { public: + //! For statistics + using MinMaxAverageHeight = std::tuple; + //! Find 0..n objects within range of given coordinate - //! \param coordinate other position - //! \param range within range of other position - CONTAINER findWithinRange(const BlackMisc::Geo::ICoordinateGeodetic &coordinate, const BlackMisc::PhysicalQuantities::CLength &range) const; + //! \param coordinate other position + //! \param range within range of other position + CONTAINER findWithinRange(const ICoordinateGeodetic &coordinate, const PhysicalQuantities::CLength &range) const; + + //! Elements with geodetic height (only MSL) + CONTAINER findWithGeodeticMSLHeight() const; + + //! Any object in range + bool containsObjectInRange(const ICoordinateGeodetic &coordinate, const PhysicalQuantities::CLength &range) const; + + //! Find min/max/average height + MinMaxAverageHeight findMinMaxAverageHeight() const; + + //! Remove outside range + int removeOutsideRange(const ICoordinateGeodetic &coordinate, const PhysicalQuantities::CLength &range); + + //! Remove if there is no geodetic height + int removeWithoutGeodeticHeight(); //! Find 0..n objects closest to the given coordinate. - CONTAINER findClosest(int number, const BlackMisc::Geo::ICoordinateGeodetic &coordinate) const; + CONTAINER findClosest(int number, const ICoordinateGeodetic &coordinate) const; + + //! Find closest within range to the given coordinate + OBJ findClosestWithinRange(const ICoordinateGeodetic &coordinate, const PhysicalQuantities::CLength &range) const; + + //! Sort by distance + void sortByEuclideanDistanceSquared(const ICoordinateGeodetic &coordinate); + + //! Sorted by distance + CONTAINER sortedByEuclideanDistanceSquared(const ICoordinateGeodetic &coordinate); protected: //! Constructor @@ -69,6 +104,8 @@ namespace BlackMisc //! \cond PRIVATE extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IGeoObjectList; extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IGeoObjectList; + extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IGeoObjectList; + extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IGeoObjectList; extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IGeoObjectList; extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IGeoObjectList; //! \endcond @@ -79,7 +116,7 @@ namespace BlackMisc { public: //! Calculate distances, then sort by range - void sortByRange(const BlackMisc::Geo::ICoordinateGeodetic &position, bool updateValues); + void sortByRange(const ICoordinateGeodetic &position, bool updateValues); //! If distance is already set, just sort void sortByDistanceToOwnAircraft(); @@ -91,10 +128,10 @@ namespace BlackMisc CONTAINER getClosestObjects(int number) const; //! Calculate distances, remove if outside range - void removeIfOutsideRange(const BlackMisc::Geo::ICoordinateGeodetic &position, const BlackMisc::PhysicalQuantities::CLength &maxDistance, bool updateValues); + void removeIfOutsideRange(const ICoordinateGeodetic &position, const PhysicalQuantities::CLength &maxDistance, bool updateValues); //! Calculate distances - void calculcateAndUpdateRelativeDistanceAndBearing(const BlackMisc::Geo::ICoordinateGeodetic &position); + void calculcateAndUpdateRelativeDistanceAndBearing(const ICoordinateGeodetic &position); protected: //! Constructor diff --git a/src/blackmisc/geo/registermetadatageo.cpp b/src/blackmisc/geo/registermetadatageo.cpp index 822f9b566..e7a2d7087 100644 --- a/src/blackmisc/geo/registermetadatageo.cpp +++ b/src/blackmisc/geo/registermetadatageo.cpp @@ -17,6 +17,7 @@ namespace BlackMisc void registerMetadata() { CCoordinateGeodetic::registerMetadata(); + CCoordinateGeodeticList::registerMetadata(); CLatitude::registerMetadata(); CLongitude::registerMetadata(); CElevationPlane::registerMetadata();