INSTINCT Code Coverage Report


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