From 8c93d9803a7d7566ab7d77e8c2d4e8a286c782f8 Mon Sep 17 00:00:00 2001 From: Warren Snipes Date: Tue, 9 Jun 2026 15:45:41 -0400 Subject: [PATCH] Add support for importing objects --- README.md | 4 +- mappedin_mvf_importer.py | 7 ++- mvf_parser_v3.py | 133 +++++++++++++++++++++++++++++++-------- 3 files changed, 115 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 690b4a3..d574c08 100644 --- a/README.md +++ b/README.md @@ -14,13 +14,14 @@ This plugin provides seamless integration between Mappedin's indoor mapping plat ### 🏗️ **Smart Layer Organization** - **Floor-based Grouping**: Automatically organizes layers by floor levels -- **Layer Categorization**: Separates doors, windows, walls, connections, spaces, and locations +- **Layer Categorization**: Separates doors, windows, walls, objects, connections, spaces, and locations - **Multi-floor Visibility**: Auto-hides upper floors, shows ground floor by default ### 🎨 **Intelligent Styling** - **Doors**: White lines (1.4 width) with proper categorization - **Windows**: Blue lines (1.2 width) for clear identification - **Walls**: Dark grey lines for structural elements +- **Objects**: Tan polygon, line, or point layers for desks, furniture, and fixtures - **Connections**: Green arrow markers (size 4) for stairs/elevators only - **Locations**: Styled point markers with labeling @@ -86,6 +87,7 @@ The plugin creates a comprehensive layer structure: │ ├── 🚪 Level 1 - Doors (white lines) │ ├── 🪟 Level 1 - Windows (blue lines) │ ├── 🧱 Level 1 - Walls (grey lines) +│ ├── 🪑 Level 1 - Objects (tan polygons/lines/points) │ ├── ⬆️ Level 1 - Connections (green arrows) │ └── 🏠 Level 1 - Spaces (polygons) ├── 📁 Level 2 Group (hidden by default) diff --git a/mappedin_mvf_importer.py b/mappedin_mvf_importer.py index be92947..2d21fae 100644 --- a/mappedin_mvf_importer.py +++ b/mappedin_mvf_importer.py @@ -312,6 +312,7 @@ def _organize_and_add_layers(self, parser, layers_data): "doors": [], "windows": [], "walls": [], + "objects": [], "points": [], "lines": [], "spaces": [], @@ -326,6 +327,8 @@ def _organize_and_add_layers(self, parser, layers_data): floor_layers[floor_key]["windows"].append(layer_info) elif "Walls" in layer_name: floor_layers[floor_key]["walls"].append(layer_info) + elif "Objects" in layer_name or "Object " in layer_name: + floor_layers[floor_key]["objects"].append(layer_info) elif "Connections" in layer_name: floor_layers[floor_key]["points"].append(layer_info) elif "Lines" in layer_name or "Doors" in layer_name: @@ -357,6 +360,7 @@ def _organize_and_add_layers(self, parser, layers_data): + len(floor_data["doors"]) + len(floor_data["windows"]) + len(floor_data["walls"]) + + len(floor_data["objects"]) + len(floor_data["points"]) + len(floor_data["lines"]) + len(floor_data["spaces"]) @@ -371,12 +375,13 @@ def _organize_and_add_layers(self, parser, layers_data): floor_groups_created.append((floor_key, group)) layers_added = 0 - # Add layers in specific order: Locations, Doors, Windows, Walls, Connections, Lines, Spaces + # Add layers in specific order: Locations, Doors, Windows, Walls, Objects, Connections, Lines, Spaces layer_order = [ ("locations", floor_data["locations"]), ("doors", floor_data["doors"]), ("windows", floor_data["windows"]), ("walls", floor_data["walls"]), + ("objects", floor_data["objects"]), ("connections", floor_data["points"]), ("lines", floor_data["lines"]), ("spaces", floor_data["spaces"]), diff --git a/mvf_parser_v3.py b/mvf_parser_v3.py index c219b06..a24b526 100644 --- a/mvf_parser_v3.py +++ b/mvf_parser_v3.py @@ -224,16 +224,16 @@ def _process_geometry( if not geometry_data["features"]: return [] - # Group geometry by type AND by kind (doors, windows, walls, etc.) + # Group geometry by type AND by kind (doors, windows, walls, objects, etc.) polygons = [] lines = [] points = [] doors = [] windows = [] walls = [] - - # Counters for debugging - objects_skipped = 0 + object_polygons = [] + object_lines = [] + object_points = [] # Get kinds data for this floor floor_kinds = self.kinds.get(floor_id, {}) @@ -278,10 +278,16 @@ def _process_geometry( geom_type = geometry.type() - # Skip objects entirely - they clutter the visualization + # Extract map objects (desks, furniture, fixtures, etc.) to their own + # layers instead of mixing them with spaces. kind_lower = geom_kind.lower() if "object" in kind_lower: - objects_skipped += 1 + if geom_type == QgsWkbTypes.PolygonGeometry: + object_polygons.append(feature_data) + elif geom_type == QgsWkbTypes.LineGeometry: + object_lines.append(feature_data) + elif geom_type == QgsWkbTypes.PointGeometry: + object_points.append(feature_data) continue # Categorize by kind first, then by geometry type @@ -308,24 +314,12 @@ def _process_geometry( else: # Fallback to geometry type for other features (but only specific types for connections) if geom_type == QgsWkbTypes.PolygonGeometry: - # Only add non-object polygons - if "object" not in kind_lower: - polygons.append(feature_data) - else: - if len(polygons) < 3: # Debug what we're filtering - # DEBUG: print(f" -> SKIPPED POLYGON OBJECT: {geom_id} (kind: {geom_kind})") - pass + polygons.append(feature_data) elif geom_type == QgsWkbTypes.LineGeometry: - # Only add non-object lines - if "object" not in kind_lower: - lines.append(feature_data) - if len(lines) <= 3: # Debug first few lines - # DEBUG: print(f" -> Added to LINES: {geom_id} (kind: {geom_kind})") - pass - else: - if len(lines) < 3: # Debug what we're filtering - # DEBUG: print(f" -> SKIPPED LINE OBJECT: {geom_id} (kind: {geom_kind})") - pass + lines.append(feature_data) + if len(lines) <= 3: # Debug first few lines + # DEBUG: print(f" -> Added to LINES: {geom_id} (kind: {geom_kind})") + pass elif geom_type == QgsWkbTypes.PointGeometry: # Skip door navigation points (API MVF creates -p1, -p2 points for doors) is_door_navigation_point = "-p1" in geom_id or "-p2" in geom_id @@ -359,10 +353,6 @@ def _process_geometry( else: pass - # Debug summary - count processed features - # total_processed = len(doors) + len(windows) + len(walls) + len(polygons) + len(lines) + len(points) - # DEBUG: print(f"Processing summary: {total_features} total -> {objects_skipped} objects skipped -> {processed_features} features processed") - layers = [] floor_name = self._get_floor_name(floor_id) @@ -414,6 +404,39 @@ def _process_geometry( pass # DEBUG: print(f"Skipping empty Walls layer for {floor_name}") + if object_polygons: + layers.append( + { + "name": f"{floor_name} - Objects", + "type": "polygon", + "features": object_polygons, + "fields": self._get_geometry_fields(), + "style_type": "object_polygon", + } + ) + + if object_lines: + layers.append( + { + "name": f"{floor_name} - Object Lines", + "type": "linestring", + "features": object_lines, + "fields": self._get_geometry_fields(), + "style_type": "object_line", + } + ) + + if object_points: + layers.append( + { + "name": f"{floor_name} - Object Points", + "type": "point", + "features": object_points, + "fields": self._get_geometry_fields(), + "style_type": "object_point", + } + ) + # Only create layers if they have actual features if polygons: layers.append( @@ -676,6 +699,53 @@ def _configure_space_layer_styling(self, layer: QgsVectorLayer): except Exception: pass + def _configure_object_polygon_layer_styling(self, layer: QgsVectorLayer): + """Styling for map object polygons such as desks and furniture.""" + try: + symbol = QgsFillSymbol.createSimple( + { + "color": "#C8A45D", + "outline_color": "#5C4A2A", + "outline_width": "0.25", + "style": "solid", + } + ) + layer.setRenderer(QgsSingleSymbolRenderer(symbol)) + layer.setOpacity(0.9) + except Exception: + pass + + def _configure_object_line_layer_styling(self, layer: QgsVectorLayer): + """Styling for map object line geometry.""" + try: + symbol = QgsLineSymbol.createSimple( + { + "color": "#8A6F3D", + "width": "0.5", + "capstyle": "round", + "joinstyle": "round", + } + ) + layer.setRenderer(QgsSingleSymbolRenderer(symbol)) + except Exception: + pass + + def _configure_object_point_layer_styling(self, layer: QgsVectorLayer): + """Styling for map object point geometry.""" + try: + symbol = QgsMarkerSymbol.createSimple( + { + "name": "square", + "color": "#C8A45D", + "outline_color": "#5C4A2A", + "outline_width": "0.25", + "size": "2.5", + } + ) + layer.setRenderer(QgsSingleSymbolRenderer(symbol)) + except Exception: + pass + def _configure_door_layer_styling(self, layer: QgsVectorLayer): """White styling for door layers.""" try: @@ -1090,6 +1160,15 @@ def create_qgis_layer(self, layer_info: Dict[str, Any]) -> Optional[QgsVectorLay elif style_type == "line_doors": self._configure_line_doors_layer_styling(layer) layer.triggerRepaint() + elif style_type == "object_polygon": + self._configure_object_polygon_layer_styling(layer) + layer.triggerRepaint() + elif style_type == "object_line": + self._configure_object_line_layer_styling(layer) + layer.triggerRepaint() + elif style_type == "object_point": + self._configure_object_point_layer_styling(layer) + layer.triggerRepaint() elif "Connections" in layer_name: self._configure_connections_layer_styling(layer) layer.triggerRepaint()