ifcfactory 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
ifcfactory/README.md ADDED
@@ -0,0 +1,177 @@
1
+ # ifcfactory
2
+
3
+ A modular geometry system for creating BIM elements with composable geometry creation.
4
+
5
+ ## Overview
6
+
7
+ ifcfactory offers a declarative, composable framework for creating complex BIM geometry.
8
+ It leverages Pydantic models for strong type safety and validation,
9
+ while providing seamless integration with IfcOpenShell for reliable IFC file generation.
10
+
11
+ ## Structure
12
+
13
+ ```
14
+ ifcfactory/
15
+ ├── __init__.py # Main public API
16
+ ├── element.py # High-level BIM elements
17
+ ├── primitives.py # Geometric shapes (Box, Cylinder, Sphere, etc.)
18
+ ├── operations.py # Transformations and Boolean operations
19
+ ├── _internal/ # Internal implementation (not for direct user access)
20
+ │ ├── __init__.py # Internal package marker
21
+ │ ├── primitives_base.py # Base classes and helper functions
22
+ │ ├── material_base.py # Materials and styling
23
+ │ ├── unit_base.py # Unit conversion utilities (pint ↔ IFC)
24
+ │ └── pset_base.py # Property set templates
25
+ ├── examples/
26
+ │ └── example_bimfactory.py
27
+ └── README.md
28
+ ```
29
+
30
+ ## Public API
31
+
32
+ ### High-Level Elements
33
+
34
+ - **`BIMFactoryElement`**: Container for complete BIM elements with IFC integration
35
+
36
+ ### 2D Profiles
37
+
38
+ - **`Rect(width, height)`**: Rectangular profile
39
+ - **`Circle(radius)`**: Circular profile
40
+ - **`Ellipse(semi_axis1, semi_axis2)`**: Elliptical profile
41
+ - **`Polygon(points)`**: Arbitrary polygon from list of 2D points
42
+
43
+ ### 3D Representations
44
+
45
+ - **`Box(width, depth, height)`**: Rectangular box
46
+ - **`Cube(size)`**: Cube with equal dimensions
47
+ - **`Cylinder(radius, height)`**: Circular cylinder
48
+ - **`EllipticalCylinder(semi_axis1, semi_axis2, height)`**: Elliptical cylinder
49
+ - **`NgonCylinder(radius, height, sides)`**: N-sided cylinder
50
+ - **`Sphere(radius, detail=1)`**: Icosphere with configurable detail
51
+ - **`Extrusion(basis, depth)`**: Extrude any 2D profile
52
+ - **`ExtrudedNgonAsMesh(basis, height)`**: N-sided extrusion as mesh
53
+ - **`MeshRepresentation(vertices, faces)`**: Custom mesh geometry
54
+
55
+ ### Operations
56
+
57
+ - **`Translate(vec, item)`**: Move geometry in 3D space
58
+ - **`RotateZ(degrees, item)`**: Rotate around Z-axis
59
+ - **`Transform(matrix, item)`**: Apply 4x4 transformation matrix
60
+ - **`Boolean(operation, children)`**: Boolean operations (union, difference, intersection)
61
+ - **`BooleanOperationTypes`**: Enum for boolean operation types
62
+
63
+ ### Materials & Styling
64
+
65
+ - **`Style`**: Visual styling with colors and transparency
66
+ - **`Material`**: Physical material properties
67
+
68
+ ### Property Sets
69
+
70
+ - **`PropertySetTemplate`**: Template for creating property sets
71
+
72
+ ### Units
73
+
74
+ - Dimension variables: `L` (length), `A` (area), `V` (volume), `M` (mass), `T` (time), `E` (current), `K` (temperature),
75
+ `N` (force), `J` (energy), `P` (power)
76
+ - **`pint_to_ifc`**: Mapping dictionary for unit conversion
77
+ - **`ureg`**: Pint unit registry
78
+ - **`Dim`**: Dimension class
79
+
80
+ ## Key Features
81
+
82
+ - **Type Safety**: Built on Pydantic for runtime type checking and validation
83
+ - **Composable**: Mix and match primitives to create complex geometry
84
+ - **Cacheable**: Built elements are cached to prevent duplication
85
+ - **Unit Aware**: Automatic unit conversion between pint and IFC types
86
+ - **Material Support**: Visual styling and material assignment
87
+ - **Property Sets**: Support for IFC property sets and quantities
88
+ - **API**: Internal implementation hidden in `_internal` module
89
+
90
+ ## Complete Working Example
91
+
92
+ Here's a complete example that creates a simple building with multiple wall types:
93
+
94
+ ```python
95
+ import ifcopenshell
96
+ import ifcopenshell.api
97
+ import ifcopenshell.api.aggregate
98
+ import ifcopenshell.api.context
99
+ import ifcopenshell.api.root
100
+ import ifcopenshell.api.unit
101
+
102
+ from ifcfactory import (
103
+ BIMFactoryElement, Box, Cube, Cylinder, Extrusion, Rect, Sphere
104
+ )
105
+
106
+
107
+ def create_basic_ifc_setup(project_name: str):
108
+ """Create basic IFC model setup using IfcOpenShell API"""
109
+ # Create IFC model
110
+ ifc_model = ifcopenshell.file(schema="IFC4")
111
+
112
+ # Create project
113
+ project_entity = ifcopenshell.api.root.create_entity(ifc_model, ifc_class="IfcProject", name=project_name)
114
+
115
+ # Create units (meters)
116
+ ifcopenshell.api.unit.assign_unit(ifc_model, length={"is_metric": True, "raw": "METERS"})
117
+
118
+ # Create contexts
119
+ model_context = ifcopenshell.api.context.add_context(ifc_model, context_type="Model")
120
+ ifcopenshell.api.context.add_context(
121
+ ifc_model, context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model_context
122
+ )
123
+
124
+ # Create site
125
+ site_entity = ifcopenshell.api.root.create_entity(ifc_model, ifc_class="IfcSite", name="Default Site")
126
+ ifcopenshell.api.aggregate.assign_object(ifc_model, relating_object=project_entity, products=[site_entity])
127
+
128
+ # Create building
129
+ building_entity = ifcopenshell.api.root.create_entity(ifc_model, ifc_class="IfcBuilding", name="Default Building")
130
+ ifcopenshell.api.aggregate.assign_object(ifc_model, relating_object=site_entity, products=[building_entity])
131
+
132
+ return ifc_model, project_entity, site_entity, building_entity
133
+
134
+
135
+ # Create IFC model and setup
136
+ model, proj, site, building = create_basic_ifc_setup("Example Building")
137
+
138
+ # Create complete project structure using BIMFactoryElement
139
+ BIMFactoryElement(
140
+ inst=building,
141
+ children=[
142
+ # Box wall positioned at origin
143
+ BIMFactoryElement(type="IfcWall", name="Box Wall", children=[Box(width=5.0, depth=0.3, height=3.0)]),
144
+ # Cube wall
145
+ BIMFactoryElement(type="IfcWall", name="Cube Wall", children=[Cube(size=4.0)]),
146
+ # Cylinder wall
147
+ BIMFactoryElement(type="IfcWall", name="Cylinder Wall", children=[Cylinder(radius=1.5, height=4.0)]),
148
+ # Extruded slab
149
+ BIMFactoryElement(
150
+ type="IfcSlab",
151
+ name="Extruded Slab",
152
+ children=[Extrusion(basis=Rect(width=5.0, height=2.5), depth=0.3)],
153
+ ),
154
+ # Sphere element
155
+ BIMFactoryElement(
156
+ type="IfcBuildingElementProxy",
157
+ name="Sphere Element",
158
+ children=[Sphere(radius=1.5, detail=2)]
159
+ ),
160
+ ],
161
+ ).build(model)
162
+
163
+ # Save the model
164
+ model.write("example_building.ifc")
165
+ print("Saved: example_building.ifc")
166
+ ```
167
+
168
+ This example demonstrates:
169
+
170
+ - Setting up a proper IFC model with project hierarchy
171
+ - Creating various geometric primitives (Box, Cube, Cylinder, Sphere)
172
+ - Using profile-based extrusions (Rect → Extrusion)
173
+ - Organizing elements in a hierarchical BIM structure
174
+ - Saving the result as an IFC file
175
+
176
+ For more comprehensive examples, see `examples/example_bimfactory.py` which demonstrates all features including
177
+ transformations, boolean operations, materials, and property sets.
ifcfactory/__init__.py ADDED
@@ -0,0 +1,114 @@
1
+ """
2
+ BIM Factory Module
3
+ ==================
4
+
5
+ Copyright (C) 2025 Freie und Hansestadt Hamburg, Landesbetrieb Geoinformation und Vermessung
6
+ BIM-Leitstelle, Ahmed Salem <ahmed.salem@gv.hamburg.de>
7
+
8
+ Developed in collaboration with Thomas Krijnen <mail@thomaskrijnen.com>
9
+
10
+ This library is free software; you can redistribute it and/or
11
+ modify it under the terms of the GNU Lesser General Public
12
+ License as published by the Free Software Foundation; either
13
+ version 2.1 of the License, or (at your option) any later version.
14
+
15
+ This library is distributed in the hope that it will be useful,
16
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
17
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18
+ Lesser General Public License for more details.
19
+
20
+ You should have received a copy of the GNU Lesser General Public
21
+ License along with this library; if not, write to the Free Software
22
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23
+
24
+ Main Components:
25
+ - Base classes and helper functions (_internal/primitives_base.py)
26
+ - Primitive geometry objects (primitives.py)
27
+ - Geometric operations (operations.py)
28
+ - Material and styling definitions (_internal/material_base.py)
29
+ - Unit conversion utilities (_internal/unit_base.py)
30
+ - Property set base classes (_internal/pset_base.py)
31
+ """
32
+
33
+ # Import high-level BIM elements
34
+ from .element import BIMFactoryElement
35
+
36
+ # Import styles and materials
37
+ from ._internal.material_base import Material, Style
38
+
39
+ # Import operations
40
+ from .operations import Boolean, BooleanOperationTypes, RotateZ, Transform, Translate
41
+ from .primitives import (
42
+ Box, # Profile classes; Representation item classes
43
+ Circle,
44
+ Cube,
45
+ Cylinder,
46
+ Ellipse,
47
+ EllipticalCylinder,
48
+ ExtrudedNgonAsMesh,
49
+ Extrusion,
50
+ MeshRepresentation,
51
+ NgonCylinder,
52
+ Polygon,
53
+ Rect,
54
+ Sphere,
55
+ )
56
+
57
+ # Import property set base
58
+ from ._internal.pset_base import PropertySetTemplate
59
+
60
+ # Import units
61
+ from ._internal.unit_base import A, Dim, E, J, L, M, N, P, T, V, K, pint_to_ifc, ureg
62
+
63
+ # Import base classes for advanced usage
64
+ from ._internal.primitives_base import ElementInterface, Primitive, Profile, RepresentationItem
65
+
66
+ __all__ = [
67
+ # Element classes
68
+ "BIMFactoryElement",
69
+ # Profile classes
70
+ "Rect",
71
+ "Circle",
72
+ "Ellipse",
73
+ "Polygon",
74
+ # Representation item classes
75
+ "Extrusion",
76
+ "Box",
77
+ "Cube",
78
+ "ExtrudedNgonAsMesh",
79
+ "NgonCylinder",
80
+ "Cylinder",
81
+ "EllipticalCylinder",
82
+ "Sphere",
83
+ "MeshRepresentation",
84
+ # Operations
85
+ "BooleanOperationTypes",
86
+ "Boolean",
87
+ "Translate",
88
+ "RotateZ",
89
+ "Transform",
90
+ # Styles and materials
91
+ "Style",
92
+ "Material",
93
+ # Property sets
94
+ "PropertySetTemplate",
95
+ # Units
96
+ "ureg",
97
+ "Dim",
98
+ "L",
99
+ "A",
100
+ "V",
101
+ "M",
102
+ "T",
103
+ "E",
104
+ "K",
105
+ "N",
106
+ "J",
107
+ "P",
108
+ "pint_to_ifc",
109
+ # Base classes (for advanced usage)
110
+ "ElementInterface",
111
+ "Primitive",
112
+ "Profile",
113
+ "RepresentationItem",
114
+ ]
@@ -0,0 +1,6 @@
1
+ """
2
+ Internal implementation modules for ifcfactory.
3
+
4
+ This package contains internal implementation details that are not part of the public API.
5
+ These modules should not be imported directly by users.
6
+ """
@@ -0,0 +1,299 @@
1
+ """
2
+ Style and Material Classes
3
+ ==========================
4
+
5
+ Copyright (C) 2025 Freie und Hansestadt Hamburg, Landesbetrieb Geoinformation und Vermessung
6
+ BIM-Leitstelle, Ahmed Salem <ahmed.salem@gv.hamburg.de>
7
+
8
+ Developed in collaboration with Thomas Krijnen <mail@thomaskrijnen.com>
9
+
10
+ This library is free software; you can redistribute it and/or
11
+ modify it under the terms of the GNU Lesser General Public
12
+ License as published by the Free Software Foundation; either
13
+ version 2.1 of the License, or (at your option) any later version.
14
+
15
+ This library is distributed in the hope that it will be useful,
16
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
17
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18
+ Lesser General Public License for more details.
19
+
20
+ You should have received a copy of the GNU Lesser General Public
21
+ License along with this library; if not, write to the Free Software
22
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23
+
24
+ This module contains classes for styling and material definitions
25
+ for IFC elements.
26
+ """
27
+
28
+ from typing import Optional, Tuple
29
+
30
+ import ifcopenshell
31
+ import ifcopenshell.api.material
32
+ import ifcopenshell.api.style
33
+
34
+ from .primitives_base import Primitive, RepresentationItem
35
+
36
+
37
+ class Style(Primitive, RepresentationItem):
38
+ """Styling wrapper that applies color, transparency, and optionally a CAD layer to representation items."""
39
+
40
+ item: RepresentationItem
41
+ rgb: str | Tuple[float, float, float]
42
+ transparency: Optional[float] = None
43
+ cad_layer: Optional[str] = None
44
+
45
+ model_config = {"arbitrary_types_allowed": True}
46
+
47
+ def build(self, model: ifcopenshell.file) -> ifcopenshell.entity_instance:
48
+ """
49
+ Build a styled representation with color, transparency, and optional CAD layer assignment.
50
+
51
+ Args:
52
+ model: The IFC model instance.
53
+
54
+ Returns:
55
+ ifcopenshell.entity_instance: The styled representation.
56
+ """
57
+ inst = self.item.build(model)
58
+ # assign_color_to_representation(model, inst, self.rgb, self.transparency)
59
+
60
+ if self.cad_layer:
61
+ # Use rgb (converted to tuple if needed) for the layer color
62
+ if isinstance(self.rgb, tuple):
63
+ color = self.rgb
64
+ elif isinstance(self.rgb, str):
65
+ color = tuple(float(x.strip()) for x in self.rgb.split(","))
66
+ else:
67
+ color = (1.0, 1.0, 1.0)
68
+ assign_layer_to_representation(model, inst, self.cad_layer, color)
69
+
70
+ assign_color_to_element(model, inst, self.rgb, self.transparency)
71
+
72
+ return inst
73
+
74
+
75
+ class Material(Primitive):
76
+ """
77
+ Material definition for IFC elements.
78
+
79
+ Args:
80
+ name (str): Name of the material.
81
+ category (Optional[str]): Category of the material.
82
+ rgb (Tuple[float, float, float]): Normalized RGB color as floats in [0, 1].
83
+ transparency (Optional[float]): Transparency value (0.0 = opaque, 1.0 = fully transparent).
84
+ """
85
+
86
+ name: str
87
+ category: Optional[str] = None
88
+ rgb: Tuple[float, float, float] # Normalized RGB in [0, 1]
89
+ transparency: Optional[float] = None
90
+ _build_result: Optional[ifcopenshell.entity_instance] = None
91
+
92
+ def build(self, model):
93
+ if res := getattr(self, "_build_result", None):
94
+ # build it only once
95
+ return res
96
+ inst = ifcopenshell.api.material.add_material(
97
+ model,
98
+ name=self.name,
99
+ **({"category": self.category} if model.schema != "IFC2X3" else {}),
100
+ )
101
+
102
+ style = ifcopenshell.api.style.add_style(model)
103
+ ifcopenshell.api.style.add_surface_style(
104
+ model,
105
+ style=style,
106
+ ifc_class=("IfcSurfaceStyleShading" if model.schema != "IFC2X3" else "IfcSurfaceStyleRendering"),
107
+ attributes={
108
+ "SurfaceColour": {
109
+ "Name": None,
110
+ "Red": self.rgb[0],
111
+ "Green": self.rgb[1],
112
+ "Blue": self.rgb[1],
113
+ },
114
+ "Transparency": self.transparency,
115
+ },
116
+ )
117
+ context = [x for x in model.by_type("IfcGeometricRepresentationContext") if x.ContextIdentifier == "Body"][0]
118
+ ifcopenshell.api.style.assign_material_style(model, material=inst, style=style, context=context)
119
+ self._build_result = inst
120
+ return inst
121
+
122
+
123
+ def ifc_normalise_color(rgb_color_str: str) -> list[float]:
124
+ """
125
+ Normalize RGB color string to IFC-compatible values in range [0.1, 1].
126
+
127
+ Args:
128
+ rgb_color_str (str): RGB color string in format "r,g,b" (e.g., "255,0,0").
129
+
130
+ Returns:
131
+ list[float]: Normalized RGB values as a list of three floats.
132
+
133
+ Raises:
134
+ ValueError: If rgb_color_str is not in the expected format.
135
+ """
136
+ rgb_color = rgb_color_str.split(",")
137
+ r, g, b = (float(rgb_color[0]), float(rgb_color[1]), float(rgb_color[2]))
138
+
139
+ # Normalizing RGB values to the range [0.1, 1]
140
+ normalized_rgb = [
141
+ round(r / 255 * (1 - 0.1) + 0.1, 2),
142
+ round(g / 255 * (1 - 0.1) + 0.1, 2),
143
+ round(b / 255 * (1 - 0.1) + 0.1, 2),
144
+ ]
145
+
146
+ return normalized_rgb
147
+
148
+
149
+ def assign_color_to_element(
150
+ model: ifcopenshell.file,
151
+ representation,
152
+ color_rgb: str | tuple[float, ...],
153
+ transparency: float | None,
154
+ ) -> None:
155
+ """
156
+ Assign a color to the IFC element representation or item.
157
+
158
+ Args:
159
+ model (ifcopenshell.file): The IFC model instance.
160
+ representation: The IFC representation or representation item.
161
+ color_rgb (str | tuple[float, ...]): Color as RGB string or tuple.
162
+ transparency (float | None): Transparency value (0.0 to 1.0).
163
+
164
+ Raises:
165
+ TypeError: If representation is not a valid IFC representation type.
166
+ """
167
+ value = ifc_normalise_color(color_rgb) if isinstance(color_rgb, str) else color_rgb
168
+
169
+ # Creating a new style
170
+ style_ifc = ifcopenshell.api.style.add_style(model, name="Style")
171
+
172
+ ifcopenshell.api.style.add_surface_style(
173
+ model,
174
+ style=style_ifc,
175
+ ifc_class="IfcSurfaceStyleShading",
176
+ attributes={
177
+ "SurfaceColour": {
178
+ "Name": None,
179
+ "Red": value[0],
180
+ "Green": value[1],
181
+ "Blue": value[2],
182
+ },
183
+ **({"Transparency": transparency} if transparency is not None else {}),
184
+ },
185
+ )
186
+ if representation.is_a("IfcRepresentation"):
187
+ ifcopenshell.api.style.assign_representation_styles(
188
+ model, shape_representation=representation, styles=[style_ifc]
189
+ )
190
+ elif representation.is_a("IfcRepresentationItem"):
191
+ ifcopenshell.api.style.assign_item_style(model, item=representation, style=style_ifc)
192
+ else:
193
+ raise TypeError(f"Unable to assign style to instance of type {representation.is_a()}")
194
+
195
+
196
+ def assign_layer_to_representation(
197
+ model,
198
+ representation: ifcopenshell.entity_instance,
199
+ layer_name: str,
200
+ color: tuple = (1.0, 1.0, 1.0),
201
+ transparency: float = 0.0,
202
+ ) -> None:
203
+ """
204
+ Assign a layer to the given representation, reusing or creating IfcPresentationLayerWithStyle.
205
+
206
+ Args:
207
+ model: The IFC model instance.
208
+ representation: The representation to assign to the layer.
209
+ layer_name: Name of the layer.
210
+ color: Normalized RGB tuple for the layer style (default: white).
211
+ transparency: Transparency for the layer style (default: 0.0).
212
+ """
213
+ try:
214
+ items_to_assign = []
215
+
216
+ # Assign the representation itself if it's a geometric solid
217
+ if hasattr(representation, "is_a") and representation.is_a() in [
218
+ "IfcExtrudedAreaSolid",
219
+ "IfcTriangulatedFaceSet",
220
+ "IfcPolygonalFaceSet",
221
+ "IfcMappedItem",
222
+ "IfcSweptDiskSolid",
223
+ ]:
224
+ items_to_assign.append(representation)
225
+
226
+ # Add geometry items from the representation if present
227
+ if hasattr(representation, "Items") and representation.Items:
228
+ for item in representation.Items:
229
+ items_to_assign.append(item)
230
+ if hasattr(item, "is_a") and item.is_a("IfcMappedItem"):
231
+ items_to_assign.append(item)
232
+
233
+ # Add the representation itself if it's a mapped representation
234
+ if hasattr(representation, "is_a") and representation.is_a("IfcShapeRepresentation"):
235
+ if getattr(representation, "RepresentationType", "") == "MappedRepresentation":
236
+ items_to_assign.append(representation)
237
+
238
+ # Remove duplicates
239
+ items_to_assign = list(dict.fromkeys(items_to_assign))
240
+
241
+ # Don't create layer if no items to assign (prevents BricsCAD errors)
242
+ if not items_to_assign:
243
+ return
244
+
245
+ # Create surface style for the layer
246
+ layer_color = model.create_entity(
247
+ "IfcColourRgb",
248
+ Name="LayerColor",
249
+ Red=color[0],
250
+ Green=color[1],
251
+ Blue=color[2],
252
+ )
253
+ surface_style_shading = model.create_entity(
254
+ "IfcSurfaceStyleShading",
255
+ SurfaceColour=layer_color,
256
+ Transparency=transparency,
257
+ )
258
+ surface_style = model.create_entity(
259
+ "IfcSurfaceStyle",
260
+ Name="LayerStyle",
261
+ Side="POSITIVE",
262
+ Styles=[surface_style_shading],
263
+ )
264
+
265
+ # Check if layer already exists
266
+ existing_layer = None
267
+ for layer in model.by_type("IfcPresentationLayerWithStyle"):
268
+ if layer.Name == layer_name:
269
+ existing_layer = layer
270
+ break
271
+
272
+ if existing_layer:
273
+ existing_items = list(existing_layer.AssignedItems) if existing_layer.AssignedItems else []
274
+ existing_items.extend(items_to_assign)
275
+ existing_layer.AssignedItems = list(dict.fromkeys(existing_items))
276
+ else:
277
+ model.create_entity(
278
+ "IfcPresentationLayerWithStyle",
279
+ Name=layer_name,
280
+ Description=None,
281
+ AssignedItems=items_to_assign,
282
+ LayerOn=True,
283
+ LayerFrozen=False,
284
+ LayerBlocked=False,
285
+ LayerStyles=[surface_style],
286
+ )
287
+
288
+ # Also assign mapped items to the layer
289
+ if hasattr(representation, "Items") and representation.Items:
290
+ for item in representation.Items:
291
+ if hasattr(item, "is_a") and item.is_a("IfcMappedItem"):
292
+ # Handle mapped items recursively
293
+ if hasattr(item, "MappingSource") and item.MappingSource:
294
+ mapped_rep = item.MappingSource.MappedRepresentation
295
+ if mapped_rep:
296
+ assign_layer_to_representation(model, mapped_rep, layer_name, color, transparency)
297
+
298
+ except Exception as e:
299
+ print(f"Warning: Could not assign layer '{layer_name}' to representation: {e}")