Retrieves LiDAR data points from the database based on the specified filter.
156{
157
158 std::shared_lock<std::shared_mutex> lkReadLock(m_muQueryMutex);
159
160
161 std::chrono::time_point<std::chrono::high_resolution_clock> tmStartTime = std::chrono::high_resolution_clock::now();
162
163 if (!m_bIsDBOpen)
164 {
165 LOG_ERROR(logging::g_qSharedLogger, "Database is not open.");
166 return {};
167 }
168
169
170 std::vector<std::string> vClauses;
171 std::vector<std::function<void(sqlite3_stmt*, int&)>> vBinders;
172 int nParamIndex = 1;
173
174
175 vClauses.emplace_back("idx.min_x BETWEEN ? AND ?");
176 vBinders.emplace_back(
177 [&](sqlite3_stmt* sqlSTMT, int& nIndex)
178 {
179 sqlite3_bind_double(sqlSTMT, nIndex++, stPointFilter.dEasting - stPointFilter.dRadius);
180 sqlite3_bind_double(sqlSTMT, nIndex++, stPointFilter.dEasting + stPointFilter.dRadius);
181 });
182 vClauses.emplace_back("idx.min_y BETWEEN ? AND ?");
183 vBinders.emplace_back(
184 [&](sqlite3_stmt* sqlSTMT, int& nIndex)
185 {
186 sqlite3_bind_double(sqlSTMT, nIndex++, stPointFilter.dNorthing - stPointFilter.dRadius);
187 sqlite3_bind_double(sqlSTMT, nIndex++, stPointFilter.dNorthing + stPointFilter.dRadius);
188 });
189
190
191
192 if (stPointFilter.szClassification && !stPointFilter.szClassification->empty())
193 {
194 vClauses.emplace_back("c.label = ?");
195 vBinders.emplace_back([&](sqlite3_stmt* sqlSTMT, int& nIndex)
196 { sqlite3_bind_text(sqlSTMT, nIndex++, stPointFilter.szClassification->c_str(), -1, SQLITE_STATIC); });
197 }
198
199
200 this->
AddRangeFilter(vClauses, vBinders,
"p.normal_x", stPointFilter.dNormalX);
201 this->
AddRangeFilter(vClauses, vBinders,
"p.normal_y", stPointFilter.dNormalY);
202 this->
AddRangeFilter(vClauses, vBinders,
"p.normal_z", stPointFilter.dNormalZ);
203 this->
AddRangeFilter(vClauses, vBinders,
"p.slope", stPointFilter.dSlope);
204 this->
AddRangeFilter(vClauses, vBinders,
"p.rough", stPointFilter.dRoughness);
205 this->
AddRangeFilter(vClauses, vBinders,
"p.curvature", stPointFilter.dCurvature);
206 this->
AddRangeFilter(vClauses, vBinders,
"p.trav_score", stPointFilter.dTraversalScore);
207
208
209 std::ostringstream stdOSS;
210
211
212
213
214 stdOSS << "SELECT p.id, p.easting, p.northing, p.altitude, z.label, c.label,"
215 << " p.normal_x, p.normal_y, p.normal_z, p.slope, p.rough, p.curvature, p.trav_score"
216 << " FROM ProcessedLiDARPoints_idx AS idx"
217 << " JOIN ProcessedLiDARPoints AS p ON p.id = idx.id"
218 << " LEFT JOIN Zones AS z ON p.zone_id = z.id"
219 << " LEFT JOIN Classifications AS c ON p.class_code = c.code"
220 << " WHERE ";
221
222
223 for (size_t siIter = 0; siIter < vClauses.size(); ++siIter)
224 {
225 if (siIter > 0)
226 stdOSS << " AND ";
227 stdOSS << vClauses[siIter];
228 }
229
230
231 const std::string szSQLQuery = stdOSS.str();
232
233
234 sqlite3_stmt* sqlSTMT = nullptr;
235 int nRC = sqlite3_prepare_v2(m_pSQLDatabase, szSQLQuery.c_str(), -1, &sqlSTMT, nullptr);
236 if (nRC != SQLITE_OK)
237 {
238 LOG_ERROR(logging::g_qSharedLogger, "Failed to prepare SQL: {}", sqlite3_errmsg(m_pSQLDatabase));
239 return {};
240 }
241
242
243 for (std::function<void(sqlite3_stmt*, int&)>& binder : vBinders)
244 {
245 binder(sqlSTMT, nParamIndex);
246 }
247
248
249 std::vector<PointRow> vResults;
250 while ((nRC = sqlite3_step(sqlSTMT)) == SQLITE_ROW)
251 {
252 PointRow stRow;
253 stRow.nID = sqlite3_column_int(sqlSTMT, 0);
254 stRow.dEasting = sqlite3_column_double(sqlSTMT, 1);
255 stRow.dNorthing = sqlite3_column_double(sqlSTMT, 2);
256 stRow.dAltitude = sqlite3_column_double(sqlSTMT, 3);
257
258
259
260 const char* pszZone = reinterpret_cast<const char*>(sqlite3_column_text(sqlSTMT, 4));
261 stRow.szZone = pszZone ? pszZone : "Unknown";
262
263
264 const char* pszClass = reinterpret_cast<const char*>(sqlite3_column_text(sqlSTMT, 5));
265 stRow.szClassification = pszClass ? pszClass : "Unclassified";
266
267
268 stRow.dNormalX = sqlite3_column_double(sqlSTMT, 6);
269 stRow.dNormalY = sqlite3_column_double(sqlSTMT, 7);
270 stRow.dNormalZ = sqlite3_column_double(sqlSTMT, 8);
271 stRow.dSlope = sqlite3_column_double(sqlSTMT, 9);
272 stRow.dRoughness = sqlite3_column_double(sqlSTMT, 10);
273 stRow.dCurvature = sqlite3_column_double(sqlSTMT, 11);
274 stRow.dTraversalScore = sqlite3_column_double(sqlSTMT, 12);
275
276 vResults.push_back(stRow);
277 }
278
279
280 if ((nRC = sqlite3_finalize(sqlSTMT)) != SQLITE_OK)
281 {
282 LOG_ERROR(logging::g_qSharedLogger, "Failed to finalize statement: {}", sqlite3_errmsg(m_pSQLDatabase));
283 return {};
284 }
285
286
287 std::chrono::time_point<std::chrono::high_resolution_clock> tmEndTime = std::chrono::high_resolution_clock::now();
288 double dQueryTime = std::chrono::duration<double>(tmEndTime - tmStartTime).count();
289
290 if (dQueryTime > 0.2)
291 {
292 LOG_WARNING(logging::g_qSharedLogger, "Query took {:.2f} seconds to execute.", dQueryTime);
293 }
294 else
295 {
296 LOG_DEBUG(logging::g_qSharedLogger, "Query took {} seconds to execute.", dQueryTime);
297 }
298
299 if (vResults.empty())
300 {
301 LOG_WARNING(logging::g_qSharedLogger, "Query returned no results.");
302 }
303
304 return vResults;
305}
void AddRangeFilter(std::vector< std::string > &vClauses, std::vector< std::function< void(sqlite3_stmt *, int &)> > &vBinders, const char *pColumn, const std::optional< PointFilter::Range< T > > &stdOptRange)
Adds a range filter to the SQL query clauses and binders.
Definition LiDARHandler.cpp:337