diff --git a/src/blenderbim/Makefile b/src/blenderbim/Makefile index 11db78bcf7..cb843d9afb 100644 --- a/src/blenderbim/Makefile +++ b/src/blenderbim/Makefile @@ -134,6 +134,8 @@ endif # Provides IFC5D functionality cp -r dist/working/IfcOpenShell-0.7.0/src/ifc5d/ifc5d dist/blenderbim/libs/site/packages/ rm -rf dist/working + # Provides IFCCityJSON functionality + cp -r dist/working/IfcOpenShell-0.7.0/src/ifccityjson dist/blenderbim/libs/site/packages/ # Provides Mustache templating in construction documentation mkdir dist/working @@ -259,6 +261,14 @@ endif mkdir dist/working cd dist/working && wget $(HPPFCL_URL) cd dist/working && tar -xf hpp-fcl* + + # Required by IFCCityJSON + mkdir dist/working + cd dist/working && python -m venv env + cd dist/working && . env/bin/activate && pip install cjio + cp -r dist/working/env/Lib/site-packages dist/blenderbim/libs/site/packages/ + rm -rf dist/working + ifeq ($(PLATFORM), linux) cp -r dist/working/lib/$(PYLIBDIR)/site-packages/hppfcl dist/blenderbim/libs/site/packages/ cp -r dist/working/lib/*.so* dist/blenderbim/libs/ diff --git a/src/blenderbim/blenderbim/bim/__init__.py b/src/blenderbim/blenderbim/bim/__init__.py index 356632626c..f829cf0ac6 100644 --- a/src/blenderbim/blenderbim/bim/__init__.py +++ b/src/blenderbim/blenderbim/bim/__init__.py @@ -63,6 +63,7 @@ modules = { "bimtester": None, "diff": None, "patch": None, + "gis": None, "covetool": None, "augin": None, "debug": None, diff --git a/src/blenderbim/blenderbim/bim/module/gis/__init__.py b/src/blenderbim/blenderbim/bim/module/gis/__init__.py new file mode 100644 index 0000000000..e894239cd4 --- /dev/null +++ b/src/blenderbim/blenderbim/bim/module/gis/__init__.py @@ -0,0 +1,17 @@ +import bpy +from . import prop +from . import operator +from . import ui + +classes = ( + prop.BIMCityJsonProperties, + operator.BIM_OT_cityjson2ifc, + operator.BIM_OT_find_cityjson_lod, + ui.BIM_PT_cityjson_converter, +) + +def register(): + bpy.types.Scene.ifccityjson_properties = bpy.props.PointerProperty(type=prop.BIMCityJsonProperties) + +def unregister(): + del bpy.types.Scene.ifccityjson_properties \ No newline at end of file diff --git a/src/blenderbim/blenderbim/bim/module/gis/operator.py b/src/blenderbim/blenderbim/bim/module/gis/operator.py new file mode 100644 index 0000000000..7cbc2eb1bf --- /dev/null +++ b/src/blenderbim/blenderbim/bim/module/gis/operator.py @@ -0,0 +1,47 @@ +import bpy +from bpy.types import Operator +from cjio import cityjson +from ifccityjson.cityjson2ifc.cityjson2ifc import Cityjson2ifc + +class BIM_OT_cityjson2ifc(Operator): + bl_idname = "bim.convert_cityjson2ifc" + bl_label = "Convert CityJSON to IFC" + bl_context = "scene" + + def execute(self, context): + props = context.scene.ifccityjson_properties + city_model = cityjson.load(props.input, transform=False) + data = { + "file_destination": props.output, + "lod": props.lod, + "split": props.split_lod, + } + if props.name is not "": + data["name_attribute"] = props.name + + converter = Cityjson2ifc() + converter.configuration(**data) + converter.convert(city_model) + if props.load_after_convert: + bpy.ops.bim.load_project(filepath=props.output) + return {'FINISHED'} + + +class BIM_OT_find_cityjson_lod(Operator): + bl_idname = "bim.find_cityjson_lod" + bl_label = "Find LODs in CityJSON file" + bl_context = "scene" + + def execute(self, context): + props = context.scene.ifccityjson_properties + city_model = cityjson.load(props.input) + lods = set() + for obj_id, obj in city_model.get_cityobjects().items(): + for geometry in obj.geometry: + lod = str(geometry.lod) + if lod not in lods: + lods.add(lod) + props.lods.add() + props.lods[-1].name = lod + props.is_lod_found = True + return {'FINISHED'} diff --git a/src/blenderbim/blenderbim/bim/module/gis/prop.py b/src/blenderbim/blenderbim/bim/module/gis/prop.py new file mode 100644 index 0000000000..f9671dfa65 --- /dev/null +++ b/src/blenderbim/blenderbim/bim/module/gis/prop.py @@ -0,0 +1,16 @@ +from bpy.types import PropertyGroup +from bpy.props import StringProperty, BoolProperty, EnumProperty, CollectionProperty +from blenderbim.bim.prop import StrProperty + +class BIMCityJsonProperties(PropertyGroup): + def get_lods(self, context): + return [(item.name, "LOD" + item.name, "Level of Detail " + item.name) for item in self.lods] + + input: StringProperty(name="cj_input", default="", subtype='FILE_PATH') + output: StringProperty(name="ifc_output", default="", subtype='FILE_PATH') + name: StringProperty(name="identifier", default="") + split_lod: BoolProperty(name="split_lod", default=True) + lods: CollectionProperty(name="lods", type=StrProperty) + lod: EnumProperty(name="lod", description="", items=get_lods, options={'ANIMATABLE'}, default=None) + is_lod_found: BoolProperty(name="is_lod_found", default=False) + load_after_convert: BoolProperty(name="load_after_convert", default=True) diff --git a/src/blenderbim/blenderbim/bim/module/gis/ui.py b/src/blenderbim/blenderbim/bim/module/gis/ui.py new file mode 100644 index 0000000000..3d099ee6db --- /dev/null +++ b/src/blenderbim/blenderbim/bim/module/gis/ui.py @@ -0,0 +1,32 @@ +from bpy.types import Panel + +class BIM_PT_cityjson_converter(Panel): + bl_label = "IFC CityJSON converter" + bl_idname = "BIM_PT_ifccityjson" + bl_options = {"DEFAULT_CLOSED"} + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "scene" + + def draw(self, context): + layout = self.layout + scene = context.scene + props = scene.ifccityjson_properties + + row = layout.row(align=True) + row.prop(props, "input") + row = layout.row(align=True) + row.operator("bim.find_cityjson_lod") + row = layout.row(align=True) + row.prop(props, "output") + row = layout.row(align=True) + row.prop(props, "name") + row = layout.row(align=True) + row.prop(props, "split_lod") + row.prop(props, "load_after_convert") + row = layout.row(align=True) + row.prop(props, "lod") + if not props.is_lod_found: + row.enabled = False + row = layout.row(align=True) + row.operator("bim.convert_cityjson2ifc") diff --git a/src/ifccityjson/__init__.py b/src/ifccityjson/__init__.py new file mode 100644 index 0000000000..82ea5b3f38 --- /dev/null +++ b/src/ifccityjson/__init__.py @@ -0,0 +1,23 @@ + +# ifccityjson - Python CityJSON to IFC converter +# Copyright (C) 2021 Laurens J.N. Oostwegel +# +# This file is part of ifccityjson. +# +# ifccityjson is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# ifccityjson is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with ifccityjson. If not, see . + +from .cityjson2ifc import * + +# Export version number +__version__ = "0.0.1" \ No newline at end of file diff --git a/src/ifccityjson/cityjson2ifc/__init__.py b/src/ifccityjson/cityjson2ifc/__init__.py new file mode 100644 index 0000000000..ccae140c39 --- /dev/null +++ b/src/ifccityjson/cityjson2ifc/__init__.py @@ -0,0 +1,20 @@ + +# ifccityjson - Python CityJSON to IFC converter +# Copyright (C) 2021 Laurens J.N. Oostwegel +# +# This file is part of ifccityjson. +# +# ifccityjson is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# ifccityjson is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with ifccityjson. If not, see . + +from .cityjson2ifc import * \ No newline at end of file diff --git a/src/ifccityjson/cityjson2ifc.py b/src/ifccityjson/cityjson2ifc/cityjson2ifc.py similarity index 98% rename from src/ifccityjson/cityjson2ifc.py rename to src/ifccityjson/cityjson2ifc/cityjson2ifc.py index bdb10c8466..d9128e074c 100644 --- a/src/ifccityjson/cityjson2ifc.py +++ b/src/ifccityjson/cityjson2ifc/cityjson2ifc.py @@ -15,14 +15,13 @@ # # You should have received a copy of the GNU Lesser General Public License # along with ifccityjson. If not, see . -import os -import copy +import os import ifcopenshell import ifcopenshell.api -from geometry import GeometryIO from datetime import datetime +from .geometry import GeometryIO JSON_TO_IFC = { "Building": ["IfcBuilding"], @@ -145,8 +144,8 @@ class Cityjson2ifc: def create_representation_sub_contexts(self): self.IFC_representation_sub_contexts = {} - for lod in self.city_model.j["metadata"]["presentLoDs"]: - self.IFC_representation_sub_contexts[str(lod)] = self.create_representation_sub_context(lod) + # for lod in self.city_model.j["metadata"]["presentLoDs"]: + # self.IFC_representation_sub_contexts[str(lod)] = self.create_representation_sub_context(lod) def create_representation_sub_context(self, lod): # TODO in ifcopenshell.api.context.add_context add support for UserDefinedTargetView diff --git a/src/ifccityjson/geometry.py b/src/ifccityjson/cityjson2ifc/geometry.py similarity index 91% rename from src/ifccityjson/geometry.py rename to src/ifccityjson/cityjson2ifc/geometry.py index e278f6edfe..227dd99e9e 100644 --- a/src/ifccityjson/geometry.py +++ b/src/ifccityjson/cityjson2ifc/geometry.py @@ -29,7 +29,7 @@ class GeometryIO: def build_vertices(self, IFC_model, vertices): for vertex in vertices: - self.build_vertex(self, IFC_model, vertex) + self.build_vertex(IFC_model, vertex) def build_vertex(self, IFC_model, vertex): if self.scale: @@ -158,12 +158,11 @@ class GeometryIO: if len(face) == 1: return IFC_model.create_entity("IfcFace", Bounds=[outerbound]) - return IFC_model.create_entity("IfcFace", [outerbound]) - # interior face BUGS - # innerbounds = [] - # for interior_face in face[1:]: - # for vertex in interior_face: - # vertices.append(self.get_vertex(IFC_model, vertex)) - # polyloop = IFC_model.create_entity("IfcPolyLoop", Polygon=vertices) - # innerbounds.append(IFC_model.create_entity("IfcFaceBound", Bound=polyloop, Orientation=False)) - # return IFC_model.create_entity("IfcFace", Bounds=[outerbound] + innerbounds) + innerbounds = [] + for interior_face in face[1:]: + vertices = [] + for vertex in interior_face: + vertices.append(self.get_vertex(IFC_model, vertex)) + polyloop = IFC_model.create_entity("IfcPolyLoop", Polygon=vertices) + innerbounds.append(IFC_model.create_entity("IfcFaceBound", Bound=polyloop, Orientation=False)) + return IFC_model.create_entity("IfcFace", Bounds=[outerbound] + innerbounds) diff --git a/src/ifccityjson/ifccityjson.py b/src/ifccityjson/ifccityjson.py index f567560d99..562c2dc7e0 100644 --- a/src/ifccityjson/ifccityjson.py +++ b/src/ifccityjson/ifccityjson.py @@ -1,4 +1,3 @@ - # ifccityjson - Python CityJSON to IFC converter # Copyright (C) 2021 Laurens J.N. Oostwegel # @@ -16,12 +15,12 @@ # # You should have received a copy of the GNU Lesser General Public License # along with ifccityjson. If not, see . + import argparse from cjio import cityjson -from cityjson2ifc import Cityjson2ifc +from cityjson2ifc.cityjson2ifc import Cityjson2ifc -# Press the green button in the gutter to run the script. -if __name__ == '__main__': +def cmdline(): # Example: # python ifccityjson.py -i example/3DBAG_example.json -o example/output.ifc -n identificatie # python ifccityjson.py -i example/geometries.json -o example/geometry_output.ifc @@ -50,3 +49,6 @@ if __name__ == '__main__': converter = Cityjson2ifc() converter.configuration(**data) converter.convert(city_model) + +if __name__ == '__main__': + cmdline() \ No newline at end of file