INSTINCT Code Coverage Report


Directory: src/
File: Nodes/DataLogger/General/KmlLogger.cpp
Date: 2025-02-07 16:54:41
Exec Total Coverage
Lines: 17 101 16.8%
Functions: 5 14 35.7%
Branches: 10 144 6.9%

Line Branch Exec Source
1 // This file is part of INSTINCT, the INS Toolkit for Integrated
2 // Navigation Concepts and Training by the Institute of Navigation of
3 // the University of Stuttgart, Germany.
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
8
9 #include "KmlLogger.hpp"
10
11 #include <imgui.h>
12 #include <implot_internal.h>
13
14 #include "Navigation/Transformations/Units.hpp"
15 #include "Navigation/Geoid/EGM96.hpp"
16 #include "NodeData/NodeData.hpp"
17
18 #include "NodeData/State/Pos.hpp"
19 #include "internal/Node/Pin.hpp"
20 #include "util/Logger.hpp"
21
22 #include <algorithm>
23 #include <iomanip> // std::setprecision
24 #include <memory>
25
26 #include "internal/NodeManager.hpp"
27 namespace nm = NAV::NodeManager;
28 #include "internal/FlowManager.hpp"
29 #include "NodeRegistry.hpp"
30
31 112 NAV::KmlLogger::KmlLogger()
32
4/8
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 112 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 112 times.
✗ Branch 9 not taken.
✓ Branch 14 taken 112 times.
✗ Branch 15 not taken.
112 : Node(typeStatic())
33 {
34 LOG_TRACE("{}: called", name);
35
36 112 _fileType = FileType::ASCII;
37
38 112 _hasConfig = true;
39 112 _guiConfigDefaultWindowSize = { 380, 70 };
40 112 }
41
42 224 NAV::KmlLogger::~KmlLogger()
43 {
44 LOG_TRACE("{}: called", nameId());
45 224 }
46
47 224 std::string NAV::KmlLogger::typeStatic()
48 {
49
1/2
✓ Branch 1 taken 224 times.
✗ Branch 2 not taken.
448 return "KmlLogger";
50 }
51
52 std::string NAV::KmlLogger::type() const
53 {
54 return typeStatic();
55 }
56
57 112 std::string NAV::KmlLogger::category()
58 {
59
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
224 return "Data Logger";
60 }
61
62 void NAV::KmlLogger::guiConfig()
63 {
64 if (FileWriter::guiConfig(".kml", { ".kml" }, size_t(id), nameId()))
65 {
66 flow::ApplyChanges();
67 doDeinitialize();
68 }
69 if (_dynamicInputPins.ShowGuiWidgets(size_t(id), inputPins, this))
70 {
71 flow::ApplyChanges();
72 }
73 }
74
75 [[nodiscard]] json NAV::KmlLogger::save() const
76 {
77 LOG_TRACE("{}: called", nameId());
78
79 json j;
80
81 j["dynamicInputPins"] = _dynamicInputPins;
82 j["FileWriter"] = FileWriter::save();
83
84 return j;
85 }
86
87 void NAV::KmlLogger::restore(json const& j)
88 {
89 LOG_TRACE("{}: called", nameId());
90
91 if (j.contains("dynamicInputPins"))
92 {
93 NAV::gui::widgets::from_json(j.at("dynamicInputPins"), _dynamicInputPins, this);
94 }
95 if (j.contains("FileWriter"))
96 {
97 FileWriter::restore(j.at("FileWriter"));
98 }
99 }
100
101 void NAV::KmlLogger::flush()
102 {
103 // See https://developers.google.com/kml/documentation/kml_tut#paths
104 LOG_DEBUG("{}: Received all data. Writing file now...", nameId());
105 _filestream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
106 "<kml xmlns=\"http://earth.google.com/kml/2.2\">\n"
107 "<Document>\n"
108 "<Style id=\"P0\">\n"
109 " <IconStyle>\n"
110 " <color>ffffffff</color>\n"
111 " <scale>0.3</scale>\n"
112 " <Icon><href>http://maps.google.com/mapfiles/kml/pal2/icon18.png</href></Icon>\n"
113 " </IconStyle>\n"
114 "</Style>\n"
115 "<Style id=\"P2\">\n"
116 " <IconStyle>\n"
117 " <color>ff00aaff</color>\n"
118 " <scale>0.2</scale>\n"
119 " <Icon><href>http://maps.google.com/mapfiles/kml/pal2/icon18.png</href></Icon>\n"
120 " </IconStyle>\n"
121 "</Style>\n";
122
123 ImPlotContext& gp = *ImPlot::GetCurrentContext();
124 int cmap = gp.Style.Colormap; // Current selected colormap
125 int nColors = gp.ColormapData.GetKeyCount(cmap);
126
127 for (size_t i = 0; i < _positionData.size(); i++)
128 {
129 int cidx = static_cast<int>(i) % nColors;
130 ImColor color(gp.ColormapData.GetKeyColor(cmap, cidx));
131 _filestream << "<Style id=\"line" << i << "\">\n"
132 << " <LineStyle>\n"
133 " <color>" // ff00aaff
134 << fmt::format("{:02x}{:02x}{:02x}{:02x}",
135 static_cast<int>(color.Value.w * 255.0F),
136 static_cast<int>(color.Value.z * 255.0F),
137 static_cast<int>(color.Value.y * 255.0F),
138 static_cast<int>(color.Value.x * 255.0F))
139 << "</color>\n"
140 " <width>1</width>\n"
141 " </LineStyle>\n"
142 "</Style>\n";
143 }
144
145 for (size_t i = 0; i < _positionData.size(); i++)
146 {
147 const auto& posData = _positionData.at(i);
148 if (posData.empty()) { continue; }
149
150 if (posData.size() > 1) // Track
151 {
152 _filestream << "<Placemark>\n"
153 << "<name>" << inputPins.at(i).name << " Track</name>\n"
154 << "<styleUrl>#line" << i << "</styleUrl>\n"
155 << "<LineString>\n"
156 << "<altitudeMode>absolute</altitudeMode>\n"
157 "<coordinates>\n";
158 for (const auto& lla : posData)
159 {
160 fmt::print(_filestream, "{:.9f},{:.9f},{:.3f}\n", lla.y(), lla.x(), lla.z());
161 }
162
163 _filestream << "</coordinates>\n"
164 "</LineString>\n"
165 "</Placemark>\n";
166 }
167
168 // Position
169 {
170 if (posData.size() > 1)
171 {
172 _filestream << "<Folder>\n"
173 << "<name>" << inputPins.at(i).name << " Position</name>\n"
174 << "<visibility>0</visibility>";
175 }
176 for (const auto& lla : posData)
177 {
178 _filestream << "<Placemark>\n";
179 if (posData.size() > 1)
180 {
181 _filestream << "<styleUrl>#P2</styleUrl>\n";
182 }
183 else
184 {
185 _filestream << "<name>" << inputPins.at(i).name << " Position</name>\n";
186 _filestream << "<styleUrl>#P0</styleUrl>\n";
187 }
188 _filestream << "<Point>\n"
189 "<extrude>1</extrude>\n"
190 "<altitudeMode>absolute</altitudeMode>\n"
191 << "<coordinates>";
192 fmt::print(_filestream, "{:.9f},{:.9f},{:.3f}", lla.y(), lla.x(), lla.z()); //
193 _filestream << "</coordinates>\n"
194 "</Point>\n"
195 "</Placemark>\n";
196 }
197 if (posData.size() > 1)
198 {
199 _filestream << "</Folder>\n";
200 }
201 }
202 }
203
204 _filestream << "</Document>\n"
205 << "</kml>\n";
206 _filestream.flush();
207 }
208
209 bool NAV::KmlLogger::initialize()
210 {
211 LOG_TRACE("{}: called", nameId());
212
213 if (!FileWriter::initialize())
214 {
215 return false;
216 }
217
218 for (auto& posData : _positionData)
219 {
220 posData.clear();
221 }
222
223 return true;
224 }
225
226 void NAV::KmlLogger::deinitialize()
227 {
228 LOG_TRACE("{}: called", nameId());
229
230 FileWriter::deinitialize();
231 }
232
233 112 void NAV::KmlLogger::pinAddCallback(Node* node)
234 {
235 112 auto* kmlNode = static_cast<KmlLogger*>(node); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast)
236
237
4/8
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
✓ Branch 8 taken 112 times.
✗ Branch 9 not taken.
✓ Branch 12 taken 112 times.
✓ Branch 13 taken 112 times.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
448 nm::CreateInputPin(node, fmt::format("Pin {}", node->inputPins.size() + 1).c_str(), Pin::Type::Flow,
238 { Pos::type() },
239 &KmlLogger::writeObservation);
240
241 112 kmlNode->_positionData.emplace_back();
242 224 }
243
244 void NAV::KmlLogger::pinDeleteCallback(Node* node, size_t pinIdx)
245 {
246 auto* kmlNode = static_cast<KmlLogger*>(node); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast)
247
248 kmlNode->_positionData.erase(std::next(kmlNode->_positionData.begin(), static_cast<int64_t>(pinIdx)));
249
250 nm::DeleteInputPin(node->inputPins.at(pinIdx));
251 }
252
253 void NAV::KmlLogger::writeObservation(NAV::InputPin::NodeDataQueue& queue, size_t pinIdx)
254 {
255 auto obs = std::static_pointer_cast<const Pos>(queue.extract_front());
256 LOG_DATA("{}: [{}] Received data {}", nameId(), obs->insTime.toYMDHMS(GPST), egm96_compute_altitude_offset(obs->latitude(), obs->longitude()));
257
258 _positionData.at(pinIdx).emplace_back(rad2deg(obs->latitude()),
259 rad2deg(obs->longitude()),
260 obs->altitude() - egm96_compute_altitude_offset(obs->latitude(), obs->longitude()));
261 }
262