ReUseX  0.0.5
3D Point Cloud Processing for Building Reuse
Loading...
Searching...
No Matches
ProjectDB.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2025 Povl Filip Sonne-Frederiksen
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#pragma once
6// Point-cloud aliases (Cloud/CloudN/CloudL and pcl::PointCloud<pcl::PointXYZ>)
7// are part of the public signatures below, so the point-type header stays.
8// The heavier OpenCV and PCL mesh headers are only needed by the .cpp: they
9// are forward-declared here (cv::Mat by value/reference, mesh types via
10// std::shared_ptr) to keep DB clients from transitively compiling them.
11#include "reusex/core/component_record.hpp"
12#include "reusex/core/survey.hpp"
13#include "reusex/types/point_types.hpp"
14
15#include <array>
16#include <cstdint>
17#include <filesystem>
18#include <map>
19#include <memory>
20#include <optional>
21#include <string>
22#include <string_view>
23#include <utility>
24#include <vector>
25
26// Forward declarations for heavy third-party types used only by-value or via
27// shared_ptr in the public API (definitions pulled in by ProjectDB.cpp).
28namespace cv {
29class Mat;
30} // namespace cv
31namespace pcl {
32struct PolygonMesh;
33struct TextureMesh;
34} // namespace pcl
35
36namespace reusex {
37
38// Forward declarations
39namespace core {
40struct MaterialPassport;
41struct MaterialPassportMetadata;
42struct SensorIntrinsics;
43} // namespace core
44
45class ProjectDB {
46 public:
82 explicit ProjectDB(std::filesystem::path dbPath, bool readOnly = false);
83
89 struct OpenOptions {
90 bool read_only = false;
91 bool hardened = false;
92 };
93 ProjectDB(std::filesystem::path dbPath, OpenOptions options);
94
99 static void set_hardened_by_default(bool on) noexcept;
100 static bool hardened_by_default() noexcept;
101
104 enum class Kind {
106 unreadable,
107 corrupt,
108 executable_schema,
109 };
111 std::string detail;
112 bool ok() const noexcept { return kind == Kind::ok; }
113 };
114
119 static IntegrityReport check_integrity(const std::filesystem::path &file);
120
121 bool is_read_only() const noexcept;
126
127 // Non-copyable (RAII resource management)
128 ProjectDB(const ProjectDB &) = delete;
129 ProjectDB &operator=(const ProjectDB &) = delete;
130
131 // Movable
132 ProjectDB(ProjectDB &&) noexcept;
133 ProjectDB &operator=(ProjectDB &&) noexcept;
134
135 // --- Core Database Operations ---
136
137 bool is_open() const noexcept;
138 const std::filesystem::path &path() const noexcept;
139 int schema_version() const;
140 static int latest_schema_version() noexcept;
141
143 struct ProbeResult {
144 bool is_project = false;
145 int schema_version = -1;
146 std::string error;
147 };
148
163 static ProbeResult probe(const std::filesystem::path &path) noexcept;
164
170 public:
171 explicit Transaction(ProjectDB &db);
173 void commit();
174 Transaction(const Transaction &) = delete;
176
177 private:
178 ProjectDB *db_;
179 bool open_ = true;
180 };
181 void validate_schema() const;
182
183 // --- Sensor Frame Operations ---
184
185 void save_sensor_frame(int nodeId, const cv::Mat &colorImage);
186
187 void save_sensor_frame(int nodeId, const cv::Mat &color, const cv::Mat &depth,
188 const cv::Mat &confidence,
189 const std::array<double, 16> &worldPose,
190 const core::SensorIntrinsics &intrinsics,
191 double timestamp = -1.0, int scan_id = -1);
192
198 const std::array<double, 16> &worldPose);
199
200 std::vector<int> sensor_frame_ids() const;
201
204 std::vector<std::pair<int, int>> sensor_frame_ids_with_scan() const;
205 cv::Mat sensor_frame_image(int nodeId) const;
206 cv::Mat sensor_frame_depth(int nodeId) const;
207 cv::Mat sensor_frame_confidence(int nodeId) const;
224 std::array<double, 16> sensor_frame_pose(int nodeId) const;
225
237 bool has_sensor_frame_pose(int nodeId) const;
238
240 bool has_sensor_frame(int nodeId) const;
241
244 double sensor_frame_timestamp(int nodeId) const;
245
248 int nearest_sensor_frame_by_timestamp(double timestamp) const;
249
250 // --- Panoramic Image Operations ---
251
253 int id;
254 std::string filename;
255 double timestamp; // -1.0 if unknown
256 int node_id; // -1 if unmatched
257 // Refined 6-DoF pose from content-based alignment (`rux align 360`). When
258 // has_pose is false the panorama has only its timestamp-matched frame pose.
259 bool has_pose = false;
260 std::array<double, 16> pose = {1, 0, 0, 0, 0, 1, 0, 0,
261 0, 0, 1, 0, 0, 0, 0, 1}; // row-major world
262 std::string pose_source = "timestamp"; // "timestamp" | "aligned"
263 int align_inliers = -1; // RANSAC inliers, -1 if not aligned
264 // Angular RMS of the inlier bearings after resection, in degrees
265 // (PanoramaAlignmentResult::rms_deg). -1 if not aligned.
266 double align_rms = -1.0;
267 };
268
269 void save_panoramic_image(const std::string &filename,
270 const std::vector<uint8_t> &jpeg_data,
271 double timestamp = -1.0, int nodeId = -1);
272
273 cv::Mat panoramic_image(int id) const;
274 cv::Mat panoramic_image(std::string_view filename) const;
275 bool has_panoramic_image(std::string_view filename) const;
277 void delete_panoramic_image(std::string_view filename);
278 std::vector<PanoramicImage> list_panoramic_images() const;
280
282 void save_panorama_pose(int id, const std::array<double, 16> &pose,
283 int inliers, double rms);
284
285 // --- Panorama Segmentation Operations ---
286
287 bool has_panorama_segmentation(int panoId) const;
288 cv::Mat panorama_segmentation(int panoId) const;
289 void save_panorama_segmentation(int panoId, const cv::Mat &labels);
290
291 // --- Segmentation Image Operations ---
292
293 bool has_segmentation_image(int nodeId) const;
294 cv::Mat segmentation_image(int nodeId) const;
295 std::vector<int> segmentation_image_ids() const;
296 void save_segmentation_image(int nodeId, const cv::Mat &labels);
297 void save_segmentation_images(const std::vector<int> &nodeIds,
298 const std::vector<cv::Mat> &labels);
299
300 // --- Glass Confidence Image Operations ---
301 //
302 // Per-frame binary depth-suppress map produced by `rux create annotate
303 // --glass-filter`. Stored as CV_8U PNG: 0 = suppress (glass/mirror), 255 =
304 // trust depth. Consumed by reconstruct_point_clouds when glass_filter is
305 // enabled.
306
307 bool has_glass_confidence_image(int nodeId) const;
308 cv::Mat glass_confidence_image(int nodeId) const;
309 std::vector<int> glass_confidence_image_ids() const;
310 void save_glass_confidence_image(int nodeId, const cv::Mat &confidence);
311
312 // --- Point Cloud Operations ---
313
314 void save_point_cloud(std::string_view name, const Cloud &cloud,
315 std::string_view stage = "",
316 std::string_view paramsJson = "");
317
318 void save_point_cloud(std::string_view name, const CloudN &cloud,
319 std::string_view stage = "",
320 std::string_view paramsJson = "");
321
322 void save_point_cloud(std::string_view name, const CloudL &cloud,
323 std::string_view stage = "",
324 std::string_view paramsJson = "");
325
326 void save_point_cloud(std::string_view name,
327 const pcl::PointCloud<pcl::PointXYZ> &cloud,
328 std::string_view stage = "",
329 std::string_view paramsJson = "");
330
331 CloudPtr point_cloud_xyzrgb(std::string_view name) const;
332 CloudNPtr point_cloud_normal(std::string_view name) const;
333 CloudLPtr point_cloud_label(std::string_view name) const;
334 pcl::PointCloud<pcl::PointXYZ>::Ptr
335 point_cloud_xyz(std::string_view name) const;
336
337 bool has_point_cloud(std::string_view name) const;
338 void delete_point_cloud(std::string_view name);
339 std::vector<std::string> list_point_clouds() const;
340 std::string point_cloud_type(std::string_view name) const;
341
348 std::string point_cloud_storage_order(std::string_view name) const;
349
353 std::pair<std::string, std::string>
354 point_cloud_provenance(std::string_view name) const;
355
357 void save_tile_index(std::string_view name, const std::vector<uint8_t> &blob);
358
361 std::vector<uint8_t> tile_index(std::string_view name) const;
362
365 struct CloudPage {
367 std::string point_type;
368 uint32_t point_step = 0;
369 uint64_t offset = 0;
370 uint64_t count = 0;
371 uint64_t total = 0;
373 std::vector<uint8_t> data;
374 };
375
396 CloudPage point_cloud_page(std::string_view name, uint64_t offset,
397 uint64_t limit) const;
398
399 // --- Label Definitions ---
400
401 void save_label_definitions(std::string_view cloudName,
402 const std::map<int, std::string> &labelMap);
403
404 std::map<int, std::string>
405 label_definitions(std::string_view cloudName) const;
406
407 // --- Instances (stable identity) ---
408
412 uint32_t instance_id = 0;
413 std::string guid;
414 int semantic_class = -1;
415 int point_count = 0;
416 };
417
421 void save_instances(const std::string &cloud_name,
422 const std::vector<InstanceRecord> &records);
423
425 std::vector<InstanceRecord> instances(const std::string &cloud_name) const;
426
431 void add_instance(const std::string &cloud_name,
432 const InstanceRecord &record);
433
436 void set_instance_point_count(const std::string &cloud_name,
437 uint32_t instance_id, int point_count);
438
441 std::string instance_guid(const std::string &cloud_name,
442 uint32_t instance_id) const;
443
444 // --- Instance ↔ Material Links ---
445
452 void set_instance_material(std::string_view cloudName, int instanceId,
453 std::string_view materialGuid);
454
456 std::optional<std::string> instance_material_guid(std::string_view cloudName,
457 int instanceId) const;
458
460 std::map<int, std::string>
461 instance_materials(std::string_view cloudName) const;
462
463 // --- Material Annotations (VLM-derived) ---
464
471 std::string description;
472 std::vector<std::pair<std::string, std::string>>
474 std::string provider_model;
475 std::string raw_json;
476 };
477
484 void save_material_annotation(std::string_view materialGuid,
485 const MaterialAnnotation &annotation);
486
489 std::optional<MaterialAnnotation>
490 material_annotation(std::string_view materialGuid) const;
491
492 // --- Mesh Operations ---
493
494 void save_mesh(std::string_view name, const pcl::PolygonMesh &mesh,
495 std::string_view stage = "", std::string_view paramsJson = "");
496
497 void save_mesh(std::string_view name, const pcl::TextureMesh &mesh,
498 std::string_view stage = "", std::string_view paramsJson = "");
499
500 std::shared_ptr<pcl::PolygonMesh> mesh(std::string_view name) const;
501 std::shared_ptr<pcl::TextureMesh> texture_mesh(std::string_view name) const;
502 bool has_mesh(std::string_view name) const;
503 std::vector<std::string> list_meshes() const;
504 std::string mesh_format(std::string_view name) const;
505
509 };
510 MeshMetadata mesh_metadata(std::string_view name) const;
511
512 std::vector<uint8_t> mesh_data_blob(std::string_view name) const;
513
515 std::string tex_name, format;
516 std::vector<uint8_t> image_data;
518 };
519 std::vector<MeshTextureInfo> mesh_texture_blobs(std::string_view name) const;
520
522 std::string tex_name, format;
524 };
525 std::vector<MeshTextureMetadata>
526 mesh_texture_metadata(std::string_view name) const;
527
528 // --- Gaussian Splat Operations ---
529 //
530 // A trained splat is stored verbatim, as the INRIA `.ply` bytes, chunked
531 // across `gaussian_splat_data` rows the same way point clouds are: a splat
532 // is routinely hundreds of MB and SQLite materializes a whole blob column
533 // on read, so a single-row layout would cost peak memory proportional to
534 // the entire file even to answer "how many Gaussians".
535 //
536 // The bytes are opaque to core — nothing here inflates a splat into a
537 // geometry type. What core *does* own is the guarantee that the bytes are a
538 // splat at all: `save_gaussian_splat()` parses the PLY header
539 // (core/gaussian_splat.hpp) and refuses anything else, so the point-cloud
540 // PLY that `rux export ply` writes is rejected at the door instead of
541 // reaching a renderer that would draw nothing and say nothing.
542
545 std::string name;
546 std::string format;
547 std::uint64_t gaussian_count = 0;
548 int sh_degree = 0;
549 std::uint64_t byte_size = 0;
550 std::string created_at;
551 std::string stage;
552 std::string parameters;
553 };
554
565 void save_gaussian_splat(std::string_view name,
566 const std::vector<uint8_t> &ply,
567 std::string_view stage = "",
568 std::string_view parameters = "");
569
573 bool has_gaussian_splat(std::string_view name) const;
574
577 std::vector<std::string> list_gaussian_splats() const;
578
580 GaussianSplatMetadata gaussian_splat_metadata(std::string_view name) const;
581
585 std::vector<uint8_t> gaussian_splat_blob(std::string_view name) const;
586
589 bool delete_gaussian_splat(std::string_view name);
590
591 // --- Pose Graph ---
592 //
593 // Written by `rux optimize` after convergence; replaced atomically on each
594 // run. The table always reflects the most-recent optimize invocation.
595 // A project that has never been optimized has an empty table.
596 //
597 // Node positions are read from `sensor_frames` (their current world poses),
598 // so there is no separate nodes table.
599
603 int to_node_id = 0;
605 std::string edge_type;
607 double residual = 0.0;
610 double weight = 1.0;
611 };
612
615 void save_pose_graph_edges(const std::vector<PoseGraphEdge> &edges);
616
619 std::vector<PoseGraphEdge> list_pose_graph_edges() const;
620
622 bool has_pose_graph() const;
623
630
638 int delete_pose_graph_edges(int from_node_id, int to_node_id,
639 std::string_view edge_type = "");
640
641 // --- Scans (multi-session import, #129) ---
642
646 struct ScanRecord {
647 int id = 0;
648 std::string source_path;
649 std::string imported_at;
650 int id_offset = 0;
651 std::string provenance_json;
652 };
653
657 ScanRecord create_scan(const std::string &source_path,
658 const std::string &provenance_json = "{}");
659
661 std::vector<ScanRecord> scans() const;
662
663 // --- Building Component Operations ---
664 //
665 // Persistence speaks only core::ComponentRecord (see
666 // reusex/core/component_record.hpp), so core stays free of geometry types
667 // (#227). To store/load a geometry::BuildingComponent, use the mapping and
668 // convenience free functions in reusex/geometry/component_persistence.hpp.
669
679 core::ComponentRecord component_record(std::string_view name) const;
680 bool has_building_component(std::string_view name) const;
681 void delete_building_component(std::string_view name);
682 std::vector<std::string> list_building_components() const;
684 std::vector<std::string>
685 list_building_components(std::string_view type) const;
687
688 // --- Pipeline Log ---
689
690 int log_pipeline_start(std::string_view stage,
691 std::string_view paramsJson = "");
692
695 enum class PipelineOutcome { success, failed, cancelled };
696
697 void log_pipeline_end(int logId, PipelineOutcome outcome,
698 std::string_view errorMsg = "");
699
703 void log_pipeline_end(int logId, bool success,
704 std::string_view errorMsg = "");
705
707 int id;
708 std::string stage;
709 std::string started_at;
710 std::string finished_at; // Empty if still running
711 std::string parameters; // JSON string
712 std::string status; // "running", "success", "failed", "cancelled"
713 std::string error_msg; // Empty if no error
714 };
715
716 std::vector<PipelineLogEntry> pipeline_log(int limit = 0) const;
717
718 // --- Raw table browsing (read-only) ---
719 //
720 // A generic, read-only window onto every table of the file, for a database
721 // viewer (the Qt client's table view, `rux get`-style tooling). Typed
722 // accessors above remain the way to *use* the data; these exist to *show*
723 // what the file contains, including tables no accessor covers.
724 //
725 // Blob cells never carry their bytes: only their size and the first few
726 // bytes (enough to sniff PNG / JPEG / PLY), read by incremental blob I/O,
727 // which touches one page — so paging through `sensor_frames` costs
728 // kilobytes, not the images. (A table WITHOUT ROWID has no blob I/O; its
729 // blob heads come from substr(), which does read the whole value.) Long text
730 // is truncated to `text_limit` bytes with its full size reported.
731
733 struct TableInfo {
734 std::string name;
735 std::int64_t row_count = 0;
736 };
737
739 struct TableColumn {
740 std::string name;
741 std::string declared_type;
742 bool primary_key = false;
743 bool not_null = false;
744 };
745
747 struct TableCell {
748 enum class Kind { null, integer, real, text, blob };
750 std::int64_t integer = 0;
751 double real = 0.0;
752 std::string text;
753 std::uint64_t size = 0;
754 bool truncated = false;
755 };
756
758 std::vector<TableInfo> list_tables() const;
759
762 std::int64_t table_row_count(std::string_view table) const;
763
766 std::vector<TableColumn> table_columns(std::string_view table) const;
767
773 std::vector<std::vector<TableCell>>
774 table_rows(std::string_view table, std::int64_t offset, std::int64_t limit,
775 std::size_t text_limit = 256) const;
776
777 // --- Project Summary ---
778
780 struct CloudInfo {
781 std::string name;
782 std::string type; // "PointXYZRGB", "Normal", "Label", "PointXYZ"
784 size_t width;
785 size_t height;
786 bool organized; // height > 1
787 std::map<int, std::string> labels; // Only for Label clouds
788
798 std::string storage_order;
799 };
800
801 struct MeshInfo {
802 std::string name;
805 };
806
808 struct ScanInfo {
809 int scan_id = 0;
810 std::string source_path;
811 std::string imported_at;
812 int frame_count = 0;
813 };
814
816 int width; // 0 if no frames
817 int height; // 0 if no frames
818 int segmented_count; // frames with segmentation
819 std::vector<ScanInfo> scans; // per-scan breakdown; empty if scans table
820 // absent
821 };
822
824 int total_count = 0;
825 int matched_count = 0; // images linked to a sensor frame
826 };
827
829 int total_count = 0;
830 std::map<std::string, int> count_by_type;
831 };
832
834 std::string id;
835 std::string guid;
837 std::string created_at; // ISO 8601 timestamp
838 std::string version_number; // Semver (e.g., "0.1.1")
839 };
840
841 struct ProjectInfo {
842 std::string id;
843 std::string name;
844 std::string building_address;
845 int year_of_construction = 0; // 0 = not set
846 std::string survey_date;
848 std::string notes;
849 };
850
851 std::filesystem::path path;
853 std::vector<ProjectInfo> projects;
854 std::vector<CloudInfo> clouds;
855 std::vector<MeshInfo> meshes;
858 std::vector<GaussianSplatMetadata> gaussian_splats;
862 std::vector<MaterialInfo> materials;
863 };
864
866
875 std::string id;
876 std::string type; // "text" | "number" | "date" | "boolean" | "select" |
877 // "multiselect"
878 std::string name;
879 std::vector<std::string> options; // populated for "select"/"multiselect"
880 int sort_order = 0;
881 int width = 200; // display column width in pixels (schema v19)
882 };
883
884 // --- Material Property Definition Operations (schema v18) ---
885
890 [[nodiscard]] std::vector<PropertyDefinition>
892
901 std::string add_property_definition(const std::string &name,
902 const std::string &type,
903 const std::vector<std::string> &options,
904 int sort_order, int width = 200);
905
910 void update_property_definition(const std::string &id,
911 const std::string &name,
912 const std::string &type,
913 const std::vector<std::string> &options,
914 int sort_order, int width);
915
920 void delete_property_definition(const std::string &id);
921
927 [[nodiscard]] std::optional<std::pair<std::vector<std::uint8_t>, std::string>>
928 material_thumbnail(const std::string &guid) const;
929
936 void set_material_thumbnail(const std::string &guid,
937 const std::vector<std::uint8_t> &blob,
938 const std::string &mime_type);
939
943 void delete_material_thumbnail(const std::string &guid);
944
945 // --- Material Passport Operations ---
946
947 core::MaterialPassport material_passport(std::string_view documentGuid) const;
948 std::vector<core::MaterialPassport> all_material_passports() const;
949
951 std::string_view projectId);
952
965 std::string_view projectId, std::string_view id);
966
972 void delete_material_passport(std::string_view documentGuid);
973
978 std::vector<std::string> list_passport_guids() const;
979
991 std::map<std::string, std::string>
992 passport_stored_properties(std::string_view documentGuid) const;
993
1001 std::string passport_property_value(std::string_view documentGuid,
1002 std::string_view fieldName) const;
1003
1018 std::optional<int>
1019 passport_linked_node_id(std::string_view documentGuid) const;
1020
1028 passport_metadata(std::string_view documentGuid) const;
1029
1041 void set_passport_metadata_field(std::string_view documentGuid,
1042 std::string_view column,
1043 std::string_view value);
1044
1057 void set_passport_property(std::string_view documentGuid,
1058 std::string_view fieldName,
1059 std::string_view value);
1060
1067 void delete_passport_property(std::string_view documentGuid,
1068 std::string_view fieldName);
1069
1070 // --- Report PDF Storage (schema v20) ---
1071
1077 int64_t id = 0;
1080 std::string created_at;
1081 std::string label; // human-readable tag (e.g. "Ressourcekortlægning")
1082 std::size_t size_bytes = 0;
1084 int version = 0;
1087 std::optional<int> blocking_types;
1088 };
1089
1099 add_report_pdf(const std::vector<std::uint8_t> &pdf,
1100 const std::string &label = "",
1101 std::optional<int> blocking_types = std::nullopt);
1102
1107 [[nodiscard]] std::vector<ReportPdfRecord> list_report_pdfs() const;
1108
1113 [[nodiscard]] std::optional<std::vector<std::uint8_t>>
1114 report_pdf(int64_t id) const;
1115
1116 // --- Resource templates (schema v25) ---
1120 int64_t id = 0;
1121 std::string name;
1122 std::string members_json = "[]";
1123 std::string csv_json = "{}";
1124 std::optional<std::string> seed; // "materialepas" | "screening"
1125 std::string created_at; // ISO 8601 UTC
1126 std::string updated_at;
1127 };
1129 std::optional<std::string> name, members_json, csv_json;
1130 };
1131
1132 [[nodiscard]] std::vector<ResourceTemplateRecord> resource_templates() const;
1133 [[nodiscard]] std::optional<ResourceTemplateRecord>
1134 resource_template(int64_t id) const;
1141 bool delete_resource_template(int64_t id); // false when absent
1142
1143 // --- Survey (Ressourcekortlægning, schema v22) ---
1145 int64_t id = 0;
1146 std::string name;
1147 std::string eak_code;
1148 std::string bim7aa_code;
1149 std::string unit = "stk";
1152 std::optional<double> confidence; // 0..1, AI detection confidence
1153 std::optional<double> mass_t; // tonnes
1154 std::string note;
1155 bool starred = false;
1156 int semantic_class = -1; // the class sync_survey seeded it from
1157 std::string created_at; // ISO 8601 UTC
1158 std::string updated_at;
1159 };
1160
1163 std::optional<std::string> name, eak_code, bim7aa_code, unit, note;
1164 std::optional<core::Treatment> treatment;
1165 // Unchecked storage write — set approval through core::set_review_status,
1166 // which enforces the sample gate.
1167 std::optional<core::ReviewStatus> review_status;
1168 std::optional<std::optional<double>> confidence, mass_t;
1169 std::optional<bool> starred;
1170 };
1172 std::string code; // "RX-001"
1173 int64_t type_id = 0;
1174 std::optional<std::string>
1175 cloud_name; // read-only, derived from instance_guid
1176 std::optional<std::uint32_t>
1177 instance_id; // read-only, derived from instance_guid
1178 std::optional<std::uint32_t> room_id;
1179 std::string room_name;
1180 double quantity = 1.0;
1181 bool starred = false;
1182 std::string note;
1183 std::optional<std::string>
1184 material_guid; // read-only: the part's passport (survey_parts.
1185 // passport_guid, schema v25), else its instance's
1186 // instance_materials link — unless another part
1187 // owns that passport, then empty
1193 std::optional<std::string> instance_guid{};
1194 };
1196 std::optional<int64_t> type_id;
1197 std::optional<double> quantity;
1198 std::optional<bool> starred;
1199 std::optional<std::string> note, room_name;
1200 };
1201
1203 add_survey_type(const SurveyTypeRecord &rec); // id/timestamps ignored
1204 [[nodiscard]] std::vector<SurveyTypeRecord> survey_types() const; // by id
1205 [[nodiscard]] std::optional<SurveyTypeRecord> survey_type(int64_t id) const;
1208 [[nodiscard]] std::vector<SurveyPartRecord> survey_parts() const; // by code
1209 [[nodiscard]] std::optional<SurveyPartRecord>
1210 survey_part(std::string_view code) const;
1212 const SurveyPartPatch &patch);
1216 const std::vector<std::pair<std::string, double>> &code_quantities);
1217 [[nodiscard]] bool has_survey_part_for(std::string_view cloud_name,
1218 std::uint32_t instance_id) const;
1219 [[nodiscard]] int max_survey_part_number() const; // 0 when there are none
1220
1227 std::string ensure_resource_passport(std::string_view code);
1229 void delete_survey_part(std::string_view code);
1233 void delete_survey_type(int64_t id);
1234
1238 void dismiss_instance(std::string_view instance_guid);
1240 [[nodiscard]] bool
1241 is_instance_dismissed(std::string_view instance_guid) const;
1243 [[nodiscard]] std::vector<std::string> dismissed_instances() const;
1246 bool unlink_instance_material(std::string_view cloud_name, int instance_id);
1251 void rename_passport_field(std::string_view old_name,
1252 std::string_view new_name);
1256 [[nodiscard]] bool has_passport_field_values(std::string_view name) const;
1261 std::size_t delete_passport_field_values(std::string_view name);
1263 [[nodiscard]] bool is_passport_linked(std::string_view guid) const;
1264
1266 int64_t id = 0;
1267 std::string code; // "P-01"
1268 std::string title; // "PCB i fugemasse"
1269 std::string what; // what was sampled, where
1272 std::vector<int64_t> type_ids; // linked survey types, ascending
1273 std::string created_at;
1274 std::string updated_at;
1275 };
1277 std::optional<std::string> title, what;
1278 std::optional<core::SampleStage> stage;
1279 std::optional<core::SampleResult> result;
1280 };
1281
1287 add_sample(std::string_view title, std::string_view what,
1288 const std::vector<int64_t> &type_ids = {},
1290 [[nodiscard]] std::vector<SampleRecord> samples() const; // by id
1291 [[nodiscard]] std::optional<SampleRecord> sample(int64_t id) const;
1293 update_sample(int64_t id,
1294 const SamplePatch &patch); // storage only; no stage rules
1295 bool delete_sample(int64_t id); // false when absent
1296 void
1298 const std::vector<int64_t> &type_ids); // replaces the set
1299 [[nodiscard]] std::vector<SampleRecord>
1300 samples_for_type(int64_t type_id) const;
1301
1302 // --- Project Metadata Operations ---
1303
1305 std::string id;
1306 std::string name;
1307 std::string building_address;
1308 int year_of_construction = 0; // 0 = not set
1309 std::string survey_date;
1311 std::string notes;
1312 };
1313
1320 ProjectMetadata get_project_metadata(std::string_view projectId) const;
1321
1328
1333 std::vector<std::string> list_project_ids() const;
1334
1335 private:
1336 class Impl;
1337 std::unique_ptr<Impl> impl_;
1338};
1339} // namespace reusex
Transaction(ProjectDB &db)
throws on a read-only project
Transaction & operator=(const Transaction &)=delete
Transaction(const Transaction &)=delete
double sensor_frame_timestamp(int nodeId) const
Get the timestamp (epoch seconds) of a sensor frame.
void add_survey_part(const SurveyPartRecord &rec)
int log_pipeline_start(std::string_view stage, std::string_view paramsJson="")
std::string point_cloud_type(std::string_view name) const
bool unlink_instance_material(std::string_view cloud_name, int instance_id)
Removes the instance_materials link of (cloud_name, instance_id).
bool has_point_cloud(std::string_view name) const
std::vector< SampleRecord > samples() const
bool delete_gaussian_splat(std::string_view name)
Deleting the metadata row cascades to its data chunks.
std::optional< SurveyPartRecord > survey_part(std::string_view code) const
std::vector< uint8_t > mesh_data_blob(std::string_view name) const
void dismiss_instance(std::string_view instance_guid)
Tombstones (schema v26, survey_dismissed_instances): instance guids whose survey part the user delete...
void add_material_passport(const core::MaterialPassport &passport, std::string_view projectId)
std::vector< ReportPdfRecord > list_report_pdfs() const
List all stored report PDFs, newest first.
bool is_passport_linked(std::string_view guid) const
True when a survey part or an instance link still references guid.
SurveyTypeRecord add_survey_type(const SurveyTypeRecord &rec)
bool has_sensor_frame_pose(int nodeId) const
True when this frame carries a usable stored world pose.
int max_survey_part_number() const
std::map< int, std::string > instance_materials(std::string_view cloudName) const
All instance_id → material_guid links for a cloud.
void save_point_cloud(std::string_view name, const pcl::PointCloud< pcl::PointXYZ > &cloud, std::string_view stage="", std::string_view paramsJson="")
bool has_passport_field_values(std::string_view name) const
True when any passport stores a value under the field name name (a leksikon name_en or a user column'...
void rename_passport_field(std::string_view old_name, std::string_view new_name)
Call inside a Transaction.
std::shared_ptr< pcl::TextureMesh > texture_mesh(std::string_view name) const
std::vector< SurveyPartRecord > survey_parts() const
bool has_segmentation_image(int nodeId) const
void log_pipeline_end(int logId, bool success, std::string_view errorMsg="")
Shim for the two-outcome form: success maps to PipelineOutcome:: success, !success to PipelineOutcome...
std::vector< std::string > list_building_components() const
CloudLPtr point_cloud_label(std::string_view name) const
std::vector< std::string > list_building_components(std::string_view type) const
Names of the components whose stored type discriminator equals type.
int nearest_sensor_frame_by_timestamp(double timestamp) const
Find the sensor frame with the closest timestamp to the given value.
bool has_pose_graph() const
True when the pose graph table exists and has at least one edge.
void set_survey_part_quantities(const std::vector< std::pair< std::string, double > > &code_quantities)
Set several parts' quantities in one transaction — all or nothing.
void delete_survey_part(std::string_view code)
std::array< double, 16 > sensor_frame_pose(int nodeId) const
The stored world pose, verbatim — row-major 4x4, identity when the row is missing,...
ResourceTemplateRecord add_resource_template(const ResourceTemplateRecord &rec)
id/timestamps are ignored.
std::vector< PipelineLogEntry > pipeline_log(int limit=0) const
std::vector< MeshTextureMetadata > mesh_texture_metadata(std::string_view name) const
void add_material_passport(const core::MaterialPassport &passport, std::string_view projectId, std::string_view id)
Add a material passport with a custom row ID.
void save_panoramic_image(const std::string &filename, const std::vector< uint8_t > &jpeg_data, double timestamp=-1.0, int nodeId=-1)
bool has_mesh(std::string_view name) const
std::vector< std::string > list_passport_guids() const
List document GUIDs ordered by created_at.
std::shared_ptr< pcl::PolygonMesh > mesh(std::string_view name) const
void set_passport_metadata_field(std::string_view documentGuid, std::string_view column, std::string_view value)
Set a metadata column on a material passport.
void add_instance(const std::string &cloud_name, const InstanceRecord &record)
Insert one instance row, leaving the cloud's other rows (and their instance_materials links,...
int add_pose_graph_edge(const PoseGraphEdge &edge)
Insert a single edge and return its row id.
void delete_panoramic_image(std::string_view filename)
void save_point_cloud(std::string_view name, const Cloud &cloud, std::string_view stage="", std::string_view paramsJson="")
std::vector< ScanRecord > scans() const
Returns all scan records ordered by id.
std::vector< int > sensor_frame_ids() const
SurveyPartRecord update_survey_part(std::string_view code, const SurveyPartPatch &patch)
bool delete_resource_template(int64_t id)
void save_instances(const std::string &cloud_name, const std::vector< InstanceRecord > &records)
Replace the full set of instance rows for a cloud (transactional).
std::vector< std::string > list_gaussian_splats() const
Empty on a pre-v12 schema, for the same reason as has_gaussian_splat().
std::size_t delete_passport_field_values(std::string_view name)
Call inside a Transaction.
std::vector< SurveyTypeRecord > survey_types() const
ProjectSummary project_summary() const
std::vector< core::MaterialPassport > all_material_passports() const
core::MaterialPassport material_passport(std::string_view documentGuid) const
void save_tile_index(std::string_view name, const std::vector< uint8_t > &blob)
Save a serialized tile index blob for the named cloud.
void save_segmentation_image(int nodeId, const cv::Mat &labels)
int schema_version() const
std::int64_t table_row_count(std::string_view table) const
Rows in table now (COUNT(*)).
void save_segmentation_images(const std::vector< int > &nodeIds, const std::vector< cv::Mat > &labels)
int building_component_count() const
ProjectDB(std::filesystem::path dbPath, bool readOnly=false)
Opens a ReUseX project database and validates its schema.
bool is_open() const noexcept
std::vector< std::string > dismissed_instances() const
Every dismissed instance guid, ascending. Empty before v26.
SurveyTypeRecord update_survey_type(int64_t id, const SurveyTypePatch &patch)
void save_pose_graph_edges(const std::vector< PoseGraphEdge > &edges)
Replace the stored pose graph atomically (delete-then-insert inside one transaction).
void delete_building_component(std::string_view name)
void save_mesh(std::string_view name, const pcl::TextureMesh &mesh, std::string_view stage="", std::string_view paramsJson="")
std::optional< int > passport_linked_node_id(std::string_view documentGuid) const
Return the sensor frame node_id this passport was linked to.
cv::Mat sensor_frame_confidence(int nodeId) const
void delete_point_cloud(std::string_view name)
bool is_instance_dismissed(std::string_view instance_guid) const
False on a read-only open of a pre-v26 project (no table).
GaussianSplatMetadata gaussian_splat_metadata(std::string_view name) const
cv::Mat sensor_frame_image(int nodeId) const
int panoramic_image_count() const
cv::Mat panoramic_image(std::string_view filename) const
std::vector< std::pair< int, int > > sensor_frame_ids_with_scan() const
Returns {node_id, scan_id} pairs ordered by node_id.
std::map< int, std::string > label_definitions(std::string_view cloudName) const
std::vector< InstanceRecord > instances(const std::string &cloud_name) const
All instance rows for a cloud, ordered by instance_id.
bool has_panoramic_image(std::string_view filename) const
cv::Mat segmentation_image(int nodeId) const
std::vector< std::string > list_project_ids() const
List all project IDs in the database.
void save_panorama_segmentation(int panoId, const cv::Mat &labels)
static ProbeResult probe(const std::filesystem::path &path) noexcept
Is path a ReUseX project, and at which schema?
PipelineOutcome
How a pipeline stage run ended.
void save_point_cloud(std::string_view name, const CloudL &cloud, std::string_view stage="", std::string_view paramsJson="")
std::string passport_property_value(std::string_view documentGuid, std::string_view fieldName) const
Get a single passport property value by field name.
std::optional< std::vector< std::uint8_t > > report_pdf(int64_t id) const
Fetch one stored PDF blob by id.
ProjectDB(std::filesystem::path dbPath, OpenOptions options)
std::vector< PanoramicImage > list_panoramic_images() const
core::ComponentRecord component_record(std::string_view name) const
Load the component row with the given name. Throws if absent.
void save_point_cloud(std::string_view name, const CloudN &cloud, std::string_view stage="", std::string_view paramsJson="")
ResourceTemplateRecord update_resource_template(int64_t id, const ResourceTemplatePatch &patch)
SampleRecord add_sample(std::string_view title, std::string_view what, const std::vector< int64_t > &type_ids={}, core::SampleStage stage=core::SampleStage::planlagt)
Register a sample, its type links and its initial stage in one transaction (duplicate type ids collap...
CloudPtr point_cloud_xyzrgb(std::string_view name) const
MeshMetadata mesh_metadata(std::string_view name) const
static int latest_schema_version() noexcept
std::vector< TableInfo > list_tables() const
Every user table, sorted by name, with its row count.
int delete_pose_graph_edges(int from_node_id, int to_node_id, std::string_view edge_type="")
Delete edges matching from_node_id and to_node_id.
std::vector< std::vector< TableCell > > table_rows(std::string_view table, std::int64_t offset, std::int64_t limit, std::size_t text_limit=256) const
Rows [offset, offset + limit) of table in rowid order (or primary-key order for a WITHOUT ROWID table...
void set_instance_point_count(const std::string &cloud_name, uint32_t instance_id, int point_count)
Update one instance row's point_count in place (no delete/re-insert).
void delete_material_thumbnail(const std::string &guid)
Delete a material's thumbnail if present (no-op when absent).
CloudNPtr point_cloud_normal(std::string_view name) const
bool has_sensor_frame(int nodeId) const
void validate_schema() const
std::string ensure_resource_passport(std::string_view code)
Resource storage (schema v25).
SampleRecord update_sample(int64_t id, const SamplePatch &patch)
cv::Mat glass_confidence_image(int nodeId) const
void update_project_metadata(const ProjectMetadata &metadata)
Update project metadata.
std::vector< MeshTextureInfo > mesh_texture_blobs(std::string_view name) const
std::map< std::string, std::string > passport_stored_properties(std::string_view documentGuid) const
Get stored property field_name→string pairs for a passport.
std::vector< ResourceTemplateRecord > resource_templates() const
By id. Empty on a read-only open of a pre-v25 project.
static void set_hardened_by_default(bool on) noexcept
Process-wide default for OpenOptions::hardened, used by the (path, readOnly) constructor — and so by ...
bool has_gaussian_splat(std::string_view name) const
False — not an error — on a project whose schema predates the splat tables, which is what a read-only...
void delete_property_definition(const std::string &id)
Delete a material column definition by id.
std::vector< TableColumn > table_columns(std::string_view table) const
The columns of table, in declaration order.
bool has_building_component(std::string_view name) const
const std::filesystem::path & path() const noexcept
std::vector< std::string > list_point_clouds() const
void save_gaussian_splat(std::string_view name, const std::vector< uint8_t > &ply, std::string_view stage="", std::string_view parameters="")
Store an INRIA-format 3DGS .ply under name, replacing any splat already stored under it (chunks of th...
void log_pipeline_end(int logId, PipelineOutcome outcome, std::string_view errorMsg="")
bool has_survey_part_for(std::string_view cloud_name, std::uint32_t instance_id) const
void delete_panoramic_image(int id)
void set_passport_property(std::string_view documentGuid, std::string_view fieldName, std::string_view value)
Set a single property value by field name (upsert).
std::optional< SampleRecord > sample(int64_t id) const
ScanRecord create_scan(const std::string &source_path, const std::string &provenance_json="{}")
Creates a new scan record.
std::vector< PropertyDefinition > list_property_definitions() const
List all user-defined material column definitions.
std::string mesh_format(std::string_view name) const
void save_mesh(std::string_view name, const pcl::PolygonMesh &mesh, std::string_view stage="", std::string_view paramsJson="")
std::string add_property_definition(const std::string &name, const std::string &type, const std::vector< std::string > &options, int sort_order, int width=200)
Add a new material column definition.
std::optional< std::string > instance_material_guid(std::string_view cloudName, int instanceId) const
Material passport guid linked to an instance, or nullopt if unlinked.
std::vector< uint8_t > gaussian_splat_blob(std::string_view name) const
The stored bytes, reassembled from their chunks — byte-for-byte what save_gaussian_splat() was given.
bool has_glass_confidence_image(int nodeId) const
ReportPdfRecord add_report_pdf(const std::vector< std::uint8_t > &pdf, const std::string &label="", std::optional< int > blocking_types=std::nullopt)
Store a PDF blob and return its record (id, created_at, size).
bool has_panorama_segmentation(int panoId) const
core::SensorIntrinsics sensor_frame_intrinsics(int nodeId) const
cv::Mat panoramic_image(int id) const
std::optional< MaterialAnnotation > material_annotation(std::string_view materialGuid) const
The stored annotation for one material passport, or nullopt when none was saved.
std::vector< SampleRecord > samples_for_type(int64_t type_id) const
core::MaterialPassportMetadata passport_metadata(std::string_view documentGuid) const
Get passport metadata without loading all properties.
std::optional< SurveyTypeRecord > survey_type(int64_t id) const
void set_sample_links(int64_t id, const std::vector< int64_t > &type_ids)
pcl::PointCloud< pcl::PointXYZ >::Ptr point_cloud_xyz(std::string_view name) const
std::optional< std::pair< std::vector< std::uint8_t >, std::string > > material_thumbnail(const std::string &guid) const
Fetch a material's thumbnail blob and its MIME type.
void set_instance_material(std::string_view cloudName, int instanceId, std::string_view materialGuid)
Link an instance (a row in the instances table) to a material passport by its document guid.
std::vector< uint8_t > tile_index(std::string_view name) const
Load the tile index blob, or empty if not set.
void delete_passport_property(std::string_view documentGuid, std::string_view fieldName)
Delete a single property value by field name.
void save_material_annotation(std::string_view materialGuid, const MaterialAnnotation &annotation)
Upsert the VLM annotation for one material passport (keyed on its document GUID).
std::pair< std::string, std::string > point_cloud_provenance(std::string_view name) const
The stage and parameters (JSON, may be empty) the cloud was last saved with — so an in-place edit can...
void update_sensor_frame_pose(int nodeId, const std::array< double, 16 > &worldPose)
Update only the stored world pose (transform) of an existing sensor frame.
void set_material_thumbnail(const std::string &guid, const std::vector< std::uint8_t > &blob, const std::string &mime_type)
Insert or replace a material's thumbnail blob.
cv::Mat panorama_segmentation(int panoId) const
void save_panorama_pose(int id, const std::array< double, 16 > &pose, int inliers, double rms)
Store a content-aligned pose (row-major 4x4 world) for a panorama.
std::vector< int > segmentation_image_ids() const
void save_component_record(const core::ComponentRecord &record)
Insert or replace the component row identified by record.name.
cv::Mat sensor_frame_depth(int nodeId) const
void save_sensor_frame(int nodeId, const cv::Mat &color, const cv::Mat &depth, const cv::Mat &confidence, const std::array< double, 16 > &worldPose, const core::SensorIntrinsics &intrinsics, double timestamp=-1.0, int scan_id=-1)
void save_glass_confidence_image(int nodeId, const cv::Mat &confidence)
std::vector< std::string > list_meshes() const
void save_sensor_frame(int nodeId, const cv::Mat &colorImage)
CloudPage point_cloud_page(std::string_view name, uint64_t offset, uint64_t limit) const
Read one page of a point cloud without materializing the cloud.
bool delete_sample(int64_t id)
void delete_survey_type(int64_t id)
Deletes a survey type; its parts and sample links cascade.
void update_component_record_by_guid(const core::ComponentRecord &record)
Update an existing component's mutable fields (name, type, parent_id, confidence, metadata,...
std::vector< int > glass_confidence_image_ids() const
void update_property_definition(const std::string &id, const std::string &name, const std::string &type, const std::vector< std::string > &options, int sort_order, int width)
Overwrite an existing material column definition.
std::string point_cloud_storage_order(std::string_view name) const
Returns the "storage_order" value from the cloud's parameters JSON, or an empty string when the key i...
std::vector< PoseGraphEdge > list_pose_graph_edges() const
All stored edges, in insertion order.
std::string instance_guid(const std::string &cloud_name, uint32_t instance_id) const
Stable GUID of a single instance.
static bool hardened_by_default() noexcept
static IntegrityReport check_integrity(const std::filesystem::path &file)
Checks a file before a server adopts it, on a hardened read-only connection that runs no migration: P...
std::optional< ResourceTemplateRecord > resource_template(int64_t id) const
ProjectMetadata get_project_metadata(std::string_view projectId) const
Get project metadata by project ID.
void delete_material_passport(std::string_view documentGuid)
Delete a material passport by GUID.
void save_label_definitions(std::string_view cloudName, const std::map< int, std::string > &labelMap)
bool is_read_only() const noexcept
Turn a 2D mask drawn on one sensor frame into a labelled 3D selection and a survey part (Segmentering...
typename CloudN::Ptr CloudNPtr
pcl::PointCloud< PointT > Cloud
typename CloudL::Ptr CloudLPtr
typename Cloud::Ptr CloudPtr
pcl::PointCloud< LabelT > CloudL
pcl::PointCloud< NormalT > CloudN
A contiguous window of one cloud's stored records, still in storage layout — no PCL type has been inf...
uint64_t total
Points in the whole cloud.
uint64_t count
Points actually read.
uint32_t point_step
Bytes per stored record.
std::string point_type
"PointXYZRGB" | "PointXYZ" | "Normal" | "Label".
std::vector< uint8_t > data
count * point_step bytes, exactly as stored.
uint64_t offset
First point index, clamped to total.
One row of the gaussian_splats table, minus the payload.
std::uint64_t byte_size
Size of the stored .ply, in bytes.
std::string format
"ply" (INRIA 3DGS layout) — the only one today.
std::string stage
Pipeline stage that produced it, may be empty.
int sh_degree
Derived from the f_rest_* count.
std::string parameters
JSON provenance, may be empty.
One row of the instances table: a spatially-distinct object within an instance-label cloud,...
int point_count
Number of points in the instance.
std::string guid
Stable identity (UUID-v4-like).
uint32_t instance_id
Label value in the instance cloud (>= 1).
int semantic_class
Semantic class this instance belongs to.
Result of check_integrity().
std::string detail
SQLite's own words; empty when ok.
A free-text description plus an arbitrary, prompt-driven set of key/value attributes for one material...
std::string description
Free-text description of the material.
std::vector< std::pair< std::string, std::string > > attributes
Arbitrary key/value pairs the model returned.
std::string provider_model
Provenance: "<base_url>|<model>".
std::string raw_json
The model's full JSON answer, verbatim.
std::vector< uint8_t > image_data
How a connection is opened.
Definition ProjectDB.hpp:89
std::array< double, 16 > pose
One directed edge stored in pose_graph_edges.
std::string edge_type
"odometry" | "loop_closure" | "panorama"
double residual
0.5 × whitened squared residual after convergence (GTSAM convention).
double weight
Translational information weight (1 / sigma_trans²).
What probe() found, without opening a full ProjectDB.
int schema_version
MAX(schema_version.version); -1 if absent.
bool is_project
sqlite with the ReUseX core tables
std::string error
sqlite / validation message when not a project
std::map< int, std::string > labels
std::string storage_order
How points are ordered in storage.
std::vector< CloudInfo > clouds
std::vector< ProjectInfo > projects
std::vector< MaterialInfo > materials
std::vector< GaussianSplatMetadata > gaussian_splats
Metadata only — the summary never carries a splat's payload, which is routinely hundreds of MB.
std::vector< MeshInfo > meshes
A user-defined column in the Notion-like material editor.
std::vector< std::string > options
Metadata for one stored report PDF version.
std::string created_at
UTC, as sqlite's datetime('now') stores it: "YYYY-MM-DD HH:MM:SS" (no 'T', no zone suffix).
std::optional< int > blocking_types
Survey types that still blocked the report when it was generated; nullopt for versions made before sc...
int version
1-based position in generation order (v1 is the first PDF ever made).
std::optional< std::string > name
std::optional< std::string > csv_json
std::optional< std::string > members_json
One row of templates (resources/templates spec §5.1).
std::optional< std::string > seed
std::optional< std::string > title
std::optional< core::SampleResult > result
std::optional< core::SampleStage > stage
std::optional< std::string > what
std::vector< int64_t > type_ids
One row of the scans table.
std::optional< std::string > room_name
std::optional< int64_t > type_id
std::optional< std::string > note
std::optional< double > quantity
std::optional< std::string > cloud_name
std::optional< std::uint32_t > room_id
std::optional< std::string > instance_guid
Stable link to instances.guid (schema v22); survives rux create instances re-runs because instance id...
std::optional< std::uint32_t > instance_id
std::optional< std::string > material_guid
Sparse update; an engaged optional sets the field.
std::optional< core::Treatment > treatment
std::optional< std::string > name
std::optional< std::string > bim7aa_code
std::optional< core::ReviewStatus > review_status
std::optional< std::string > eak_code
std::optional< std::optional< double > > mass_t
std::optional< std::optional< double > > confidence
std::optional< std::string > note
std::optional< std::string > unit
std::optional< double > mass_t
std::optional< double > confidence
One cell of table_rows().
std::string text
text (possibly truncated), or blob head bytes
bool truncated
text shorter than size
std::uint64_t size
full byte size of text or blob
One column of a table, as PRAGMA table_info reports it.
std::string declared_type
e.g. "INTEGER", "BLOB"; may be empty
One user table and its row count. sqlite_* internals are excluded.
Core-owned persistence contract for a row of the building_components table (#227).
Document-level metadata for the material passport.
A complete material passport for a reused building material.
Lightweight camera intrinsics replacing rtabmap::CameraModel in downstream code.