Source code for xgbxml.xgbxml

# -*- coding: utf-8 -*-


import importlib.resources as pkg_resources
import importlib
from lxml import etree

from . import schemas

import collections.abc

import inspect

from copy import copy

import xgbxml.gbxml_functions as gbxml_functions
import xgbxml.xml_functions as xml_functions
import xgbxml.xsd_functions as xsd_functions
import xgbxml.gbxml_xsd_functions as gbxml_xsd_functions

from . import render_functions
from . import geometry_functions 


[docs] def get_parser(version='6.01'): """Returns a lxml.etree.XMLParser containing custom elements for gbXML files. :param version: The `gbxml version string <https://www.gbxml.org/schema_doc/6.01/GreenBuildingXML_Ver6.01.html#Link27C>`_. Default is '6.01'. :type version: str :rtype: lxml.etree.XMLParser .. note:: Using this parser means that when lxml is used to read in a gbXML file, the different elements (`gbXML`, `Campus`, `Building` etc.) are instantiated with custom classes. This means that the lxml/xgbxml elements have additional features, which are specifically designed for working with gbXML files. For example, the `gbXML` element has additional properties such as :code:`version` and :code:`temperatureUnit` for direct access to the XML attributes. It also has additional methods, such as :code:`add_Campus`, for creating new child elements. The *xgbxml* parser provides three types of additional properties and methods: 1. Properties and methods inherited from the :py:class:`~xgbxml.xgbxml.gbElement` class. 2. Properties and methods that are automatically generated from the gbXML schema file. For example, this method generates the :code:`version` property and the :code:`add_Campus` method for the gbXML element. 3. Properties and methods which are custom written for the element. This is bespoke code written for a particular element to provide additional functionality. An example is the :py:func:`~xgbxml.xgbxml.Surface.get_shell` method of the :py:class:`~xgbxml.xgbxml.Surface` class. .. rubric:: Code Example .. code-block:: python from lxml import etree import xgbxml parser=xgbxml.get_parser() # default is gbXML version 6.01 tree=etree.parse('my_gbxml_file.xml', parser) gbxml=tree.getroot() """ #create lookup lookup = etree.ElementNamespaceClassLookup() #set namespace namespace = lookup.get_namespace('http://www.gbxml.org/schema') #set default element namespace[None]=gbElement # load xsd_schema xsd_schema_text=pkg_resources.read_text(schemas, 'GreenBuildingXML_Ver%s.xsd' % version) xsd_schema=etree.fromstring(xsd_schema_text.encode())#.getroot() # load schema_dict # schema_text = pkg_resources.read_text(schema_dicts, # 'schema_dict_%s.json' % version.replace('.','_')) # schema_dict=json.loads(schema_text) # load autogenerated gbElements_X_XX module auto_gbElement_module = importlib.import_module('.auto.gbElements_%s' % version.replace('.','_'), 'xgbxml') #print(auto_gbElement_module) # loop through classes in module for k,v in auto_gbElement_module.__dict__.items(): if not k.startswith('__'): element_name=k[:-5].replace('-','_') #print(element_name) base_classes=[gbElement,v] # add custom written element class in gbxml directory if it exists try: kls=globals()[element_name] base_classes.append(kls) except KeyError: pass # add class for element into namespace namespace[element_name.replace('_','-')]=\ type(element_name, tuple(base_classes), dict( #_class_schema_dict=schema_dict, _xsd_schema=xsd_schema ) ) parser = etree.XMLParser(remove_blank_text=True) parser.set_element_class_lookup(lookup) return parser
[docs] def create_gbXML(id=None, engine=None, temperatureUnit='C', lengthUnit='Meters', areaUnit='SquareMeters', volumeUnit='CubicMeters', useSIUnitsForResults=True, version='6.01', SurfaceReferenceLocation=None ): """Returns a root `gbXML` element for a new, blank gbXML file. The keyword arguments for this function set the XML attributes of the newly created `gbXML` element. `See the gbXML schema for details of these attributes. <https://www.gbxml.org/schema_doc/6.01/GreenBuildingXML_Ver6.01.html#Link105>`_. :rtype: xgbxml.xgbxml.gbXML (subclass of lxml.etree._Element) .. note:: The returned object is a subclass of lxml.Element, not an lxml.ElementTree. To access the ElementTree of the returned gbXML element, the lxml method :code:`getroottree()` can be used. This is needed to save the gbXML file using the ElementTree :code:`write()` method. .. rubric:: Code Example .. code-block:: python import xgbxml gbxml=xgbxml.create_gbXML() tree=gbxml.getroottree() tree.write('new_gbxml_file.xml') """ xml='<gbXML version="%s" xmlns="http://www.gbxml.org/schema"></gbXML>' % version parser=get_parser(version) root=etree.fromstring(xml,parser) if not id is None: root.id=id if not engine is None: root.engine=engine root.temperatureUnit=temperatureUnit root.lengthUnit=lengthUnit root.areaUnit=areaUnit root.volumeUnit=volumeUnit root.useSIUnitsForResults=useSIUnitsForResults if not SurfaceReferenceLocation is None: root.SurfaceReferenceLocation=SurfaceReferenceLocation return root
[docs] class gbElement(etree.ElementBase): """A base class which is inherited by all xgbxml element instances. When using either :py:func:`~xgbxml.xgbxml.get_parser` or :py:func:`~xgbxml.xgbxml.create_gbXML`, all elements created will have the properties and methods available in this class. """ def _introspect(self): "" print('self.__class__',self.__class__) print('self.__class__.__bases__',self.__class__.__bases__) print(self.__class__.__bases__[1]) print([x for x in dir(self.__class__.__bases__[1]) if not x.startswith('_')]) import inspect for name,obj in inspect.getmembers(self.__class__.__bases__[1]): print(name, type(obj)) if inspect.isfunction(obj): print(inspect.signature(obj))
[docs] def __repr__(self): """The repr for the class. Determines how the instance is displayed when printed. :returns: A different value is returned depending on if this is the gbElement class or a subclass, and/or if the element has an 'id' attribute. :rtype: str """ try: id_=self.get_attribute('id') id_st=' (id="%s")' % id_ except KeyError: id_st='' if self.__class__.__name__=='gbElement': return '<%s %s%s>' % (self.__class__.__name__, self.nntag, id_st) else: return '<%s%s>' % (self.__class__.__name__, id_st)
[docs] def add_child(self, child_nntag, value=None, **kwargs): """Adds a new child element to the element. :param child_nntag: The 'no namespace' tag of the child element (i.e. "Campus") :type child_nntag: str :param value: The value for the element. Optional. :type value: str, float, bool etc. :param kwargs: Attributes to be set for the child element. Optional. :raises KeyError: If the child name does not exist in the schema. :raises: Other error may be raised if the optional value or attributes are not specified correctly. :returns: The newly created child element. :rtype: (subclass of) gbElement """ return gbxml_functions.add_child_to_gbxml_element( gbxml_element=self, child_nntag=child_nntag, xsd_schema=self.xsd_schema, value=value, **kwargs)
[docs] def get_attribute(self,attribute_name): """Returns the attribute value of the element. :param attribute_name: The name of the attribute. :param attribute_name: str :raises KeyError: If the attribute is not present in the element. :returns: The text value of the attribute converted to the python type of the attribute. :rtype: bool, str or float etc. """ return gbxml_functions.get_attribute_of_gbxml_element( gbxml_element=self, attribute_name=attribute_name, xsd_schema=self.xsd_schema )
@property def get_attributes(self): """Returns the attributes of the element. :param gbxml_element: A gbXML element. :type gbxml_element: lxml.etree._Element :param xsd_schema: The root node of a gbXML schema. :type xsd_schema: lxml.etree._Element :returns: A dictionary of attributes where the attribute values have been converted to the correct python types according to the schema. :rtype: dict """ return gbxml_functions.get_attributes_of_gbxml_element( gbxml_element=self, xsd_schema=self.xsd_schema )
[docs] def get_child(self, child_nntag, child_id=None): """Returns a child element with specified tag. If child_id is not supplied, then the first child element found is returned. :param child_nntag: The 'no namespace' tag of the child element. :type child_nntag: str :param child_id: Optional, the 'id' attribute of the child element. :type child_id: str :raises KeyError: If the child element is not present. :rtype: (subclass of) gbElement """ return gbxml_functions.get_child_of_gbxml_element( self, child_nntag, child_id=child_id)
[docs] def get_children(self,child_nntag): """Returns all child element with specified tag. :param child_nntag: The 'no namespace' tag of the child element (i.e. "Campus") :type child_nntag: str :rtype: gbCollection """ return gbCollection( *gbxml_functions.get_children_of_gbxml_element( self, child_nntag ) )
@property def id(self): """Returns / sets the 'id' attribute of the element. :param value: When setting, the value of the id attribute :type value: str :raises KeyError: When returning, if the 'id' attribute is not present in the element. :raises KeyError: When setting, if attribute name does not exist in the schema. :raises TypeError: When setting, if the attribute value is of a type that does not match the schema. :rtype: str (when returning) """ return self.get_attribute('id') @id.setter def id(self,value): """Sets the 'id' attribute of the element. """ self.set_attribute('id',value) @property def nntag(self): """Returns the tag without the namespace ('no namespace tag') :rtype: str .. rubric:: Code Example .. code-block:: python >>> print(gbxml.tag) {http://www.gbxml.org/schema}gbXML >>> print(gbxml.nntag) gbXML """ return xml_functions.nntag(self) @property def ns(self): """The namespace dictionary for xpath calls. :rtype: dict """ return gbxml_functions.ns
[docs] def set_attribute(self,attribute_name,value): """Sets an attribute value of the element. Attribute will be created if it does not already exist. Attribute value is modified if attribute does already exist. :param attribute_name: The name of the attribute. :param attribute_name: str :param value: The new value for the attribute. :type value: bool, str, float :raises KeyError: If attribute name does not exist in the schema. :raises ValueError: If attribute has enumerations, and 'value' does not match one of the enumeration options. :raises TypeError: If the attribute value is of a type that does not match the schema. """ return gbxml_functions.set_attribute_on_gbxml_element(self, attribute_name, value, self.xsd_schema)
[docs] def tostring(self): """Returns a string of the xml of the element. :rtype: str """ return etree.tostring(copy(self), pretty_print=True).decode()
@property def xsd_schema(self): "" return self._xsd_schema @property def value(self): """Returns / set the value of the gbXML element. This is stored in the text value of the XML element. :param value: When setting, the value for the element. :type value: str, float, bool etc. :raises TypeError: When setting, if value is of a type that does not match the schema. :returns: When returning, a value which is converted from the element text node. :rtype: str, int, float or book etc. """ xsd_type=gbxml_xsd_functions.get_xsd_type_of_text_of_xsd_element( self.nntag, self.xsd_schema ) python_type=xsd_functions.xsd_type_to_python_type(xsd_type) return python_type(self.text) @value.setter def value(self,value): """Sets the value of the element. """ gbxml_functions.set_value_of_gbxml_element( gbxml_element=self, value=value, xsd_schema=self.xsd_schema )
[docs] class gbCollection(collections.abc.Sequence): """This class is a collection of xgbxml elements. It acts like a list, but is immutable so cannot be changed or updated. However the items in the list can be changed. Instances of this class occur when a list of xgbxml elements is returned using the :py:func:`get_children` method or a method that is based on :py:func:`get_children`. For example, the code below returns a :code:`gbCollection` instance of Building elements: .. code-block:: python from lxml import etree import xgbxml parser=xgbxml.get_parser() # default is gbXML version 6.01 tree=etree.parse('gbXMLStandard.xml', parser) gbxml=tree.getroot() buildings=gbxml.Campus.Buildings print(buildings) # prints "gbCollection(<Building (id="aim0013")>)" print(type(buildings)) # prints "<class 'xgbxml.xgbxml.gbCollection'>" """
[docs] def __getattr__(self,key): """This method catches any unknown attribute calls on a gbCollection instance. This allows gbCollection instances to propogate methods and property calls onto the elements stored in the collection. :param key: The key passes to __getattr__ :param key: str This can be used to access all child elements of the elements in a gbCollection. For example, the code below shows a quick access approach to query all the openings in a gbXML file. .. code-block:: python from lxml import etree import xgbxml parser=xgbxml.get_parser() # default is gbXML version 6.01 tree=etree.parse('gbXMLStandard.xml', parser) gbxml=tree.getroot() openings=gbxml.Campus.Surfaces.Openings print(len(openings)) # prints "138" print(type(openings)) # prints "<class 'xgbxml.xgbxml.gbCollection'>" This can also be used to access the properties of the elements of a gbCollection. For example, the code below shows a quick-access approach to listing all the openingTypes attributes of the openings in a gbXL file. .. code-block:: python from lxml import etree import xgbxml parser=xgbxml.get_parser() # default is gbXML version 6.01 tree=etree.parse('gbXMLStandard.xml', parser) gbxml=tree.getroot() opening_types=gbxml.Campus.Surfaces.Openings.openingType print(len(opening_types)) # prints "138" print(type(opening_types)) # prints "<class 'tuple'>" print(opening_types) # prints "('NonSlidingDoor', 'NonSlidingDoor', 'NonSlidingDoor', ...)" The approach also works with method calls. For example, the code below creates a list of the areas of all openings in a gbXML file. .. code-block:: python from lxml import etree import xgbxml parser=xgbxml.get_parser() # default is gbXML version 6.01 tree=etree.parse('gbXMLStandard.xml', parser) gbxml=tree.getroot() surface_areas=gbxml.Campus.Surfaces.Openings.get_area() print(len(surface_areas)) # prints "138" print(type(surface_areas)) # prints "<class 'tuple'>" print(surface_areas) # prints "(21.0, 21.000000056466668, 21.0, ...)" """ #print('__getattr__', key) result=[] for x in self: y=getattr(x,key) if isinstance(y,gbCollection): result.extend(y) else: result.append(y) if len(result)==0: return [] elif isinstance(result[0],gbElement): # if result is a collection of elements return gbCollection(*result) elif inspect.ismethod(result[0]): # if result is a collection of methods def boundmethods(*args,**kwargs): y=[x(*args,**kwargs) for x in result] if isinstance(y[0],gbElement): return gbCollection(*y) else: return tuple(y) return boundmethods else: return tuple(result)
def __getitem__(self,index): """ :param index: The index value passed to __getitem__ :type index: int """ if isinstance(index, slice): indices = range(*index.indices(len(self._items))) return gbCollection(*[self._items[i] for i in indices]) else: return self._items[index] def __init__(self,*items): "" self._items=tuple(items) def __len__(self): "" return len(self._items) def __repr__(self): "" return '%s(%s)' % (self.__class__.__name__, ', '.join([str(c) for c in self]))
[docs] class Building(): ""
[docs] def get_gaps_in_surfaces( self ): """Identifies any gaps in the surfaces of the Building. To be used for spotting errors in the geometry - such as small surfaces not being exported from REVIT. See https://forums.autodesk.com/t5/revit-api-forum/gbxml-from-adjacent-conceptual-mass-adjacent-space-missing-small/m-p/12232100. This functions looks at each Space in the Building and determines if the edges of the Surfaces adjacent to the Space match with each other. Where they do not match, this may indicate a gap in the Surfaces. :returns: A list of dictionaries. Each dictionary contains the shell (exterior) of missing surface polygon and a list of the adjacent Spaces. For example: .. code-block:: python [ { 'space_ids': ['aim2197'], 'shell': [ (72.2287629, -0.3141381, 0.0), (72.2287629, -0.4999998, 0.0), (72.0986211, -0.4999998, 0.0), (72.2287629, -0.3141381, 0.0) ] }, { 'space_ids': ['aim2553', 'aim7413'], 'shell': [ (80.2291667, 14.5625, 10.0), (80.0208333, 14.5625, 10.0), (80.0208333, 16.020833, 10.0), (80.2291667, 16.020833, 10.0), (80.2291667, 14.5625, 10.0) ] } ] :rtype: list """ result = gbxml_functions.get_gaps_in_Surfaces_of_Building( self, self.xsd_schema ) return result
[docs] class Campus(): ""
[docs] def render(self, ax=None, set_lims=True, surface_outline_kwargs=None, surface_fill_kwargs=None, opening_outline_kwargs=None, opening_fill_kwargs=None): """Renders the Campus in 3D using matplotlib. :param ax: A matplotlib 3D Axes instance. Optional, if not supplied then an axis is created and returned. :type ax: matplotlib.axes._subplots.Axes3DSubplot :param set_lims: If True, then the x, y and z axis limits are set automatically based on the geometry being rendered. :type set_lims: bool :param surface_outline_kwargs: matplotlib keywork arguments for formatting the outlines of surfaces (passed to ax.plot method). :param surface_fill_kwargs: matplotlib keywork arguments for formatting the fill of surfaces (passed to Poly3DCollection method). :param opening_outline_kwargs: matplotlib keywork arguments for formatting the outlines of openings (passed to ax.plot method). :param opening_fill_kwargs: matplotlib keywork arguments for formatting the fill of openings (passed to Poly3DCollection method). :returns: The axis instance. :rtype: matplotlib.axes._subplots.Axes3DSubplot """ #print(self) for su in self.Surfaces: #try: ax=su.render(ax=ax, set_lims=set_lims, surface_outline_kwargs=surface_outline_kwargs, surface_fill_kwargs=surface_fill_kwargs, opening_outline_kwargs=opening_outline_kwargs, opening_fill_kwargs=opening_fill_kwargs ) #except TypeError as err: # print(su) # print(type(err)) return ax
[docs] class CartesianPoint(): ""
[docs] def create_Coordinates(self,*coordinates): """Creates Coordinate child elements and sets their value. :param coordinates: The values of the x,y,(z) coordinates as an argument list. For example: ((0,0,0),(1,0,0),(0,1,0)) :type coordinates: tuple otr list etc. :returns: The newly creeated Coordinate elements. :rtype: gbCollection """ return gbCollection( *gbxml_functions.add_Coordinates_to_CartesianPoint( gbxml_element=self, xsd_schema=self.xsd_schema, *coordinates ) )
[docs] def get_coordinates(self): """Returns the values of the Coordinate child elements. :rtype: tuple(float) """ return gbxml_functions.get_Coordinate_values_from_CartesianPoint( self, self.xsd_schema )
[docs] class ClosedShell(): "" # def get_gaps( # self # ): # """ # """ # return gbxml_functions.get_gaps_in_ClosedShell( # self, # self.xsd_schema # )
[docs] def render(self, ax=None, set_lims=True, outline_kwargs=None, fill_kwargs=None): """Renders the PolyLoops of the ClosedShell in 3D using matplotlib. :param ax: A matplotlib 3D Axes instance. Optional, if not supplied then an axis is created and returned. :type ax: matplotlib.axes._subplots.Axes3DSubplot :param set_lims: If True, then the x, y and z axis limits are set automatically based on the geometry being rendered. :type set_lims: bool :param outline_kwargs: matplotlib keywork arguments for formatting the outlines of surfaces (passed to ax.plot method). :param fill_kwargs: matplotlib keywork arguments for formatting the fill of surfaces (passed to Poly3DCollection method). :returns: The axis instance. :rtype: matplotlib.axes._subplots.Axes3DSubplot """ #print(self) for pl in self.PolyLoops: #try: ax=pl.render(ax=ax, set_lims=set_lims, outline_kwargs=outline_kwargs, fill_kwargs=fill_kwargs ) #except TypeError as err: # print(su) # print(type(err)) return ax
[docs] class Opening(): ""
[docs] def get_area(self): """Calculates the area of the opening. :returns: The area of the Opening. :rtype: float """ shell=self.get_shell() holes=[] return geometry_functions.polygon_area_3d(shell,holes)
[docs] def get_shell(self): """Returns the shell of the outer polyloop of the opening. The following sources are tried in order: - PlanarGeometry - RectangularGeometry/PolyLoop - RectangularGeoemetry... from height and width :rtype: tuple(tuple(float)) """ return gbxml_functions.get_shell_of_Opening(self, self.xsd_schema)
[docs] def render(self, ax=None, set_lims=True, outline_kwargs=None, fill_kwargs=None): """ Renders the Opening in 3D using matplotlib. :param ax: A matplotlib 3D Axes instance. Optional, if not supplied then an axis is created and returned. :type ax: matplotlib.axes._subplots.Axes3DSubplot :param set_lims: If True, then the x, y and z axis limits are set automatically based on the geometry being rendered. :type set_lims: bool :param outline_kwargs: matplotlib keywork arguments for formatting the outlines of the openings (passed to ax.plot method). :param surface_kwargs: matplotlib keywork arguments for formatting the surface rendering (passed to Poly3DCollection method). :returns: The axis instance. :rtype: matplotlib.axes._subplots.Axes3DSubplot """ x=dict(color='green') if outline_kwargs is None: outline_kwargs=x else: for k,v in x.items: if not k in outline_kwargs: outline_kwargs[k]=v x=dict(color='green') if fill_kwargs is None: fill_kwargs=x else: for k,v in x.items: if not k in fill_kwargs: fill_kwargs[k]=v ax=render_functions.render_polygon_3d( polygon=(self.get_shell(),[]), polygon_triangles=None, ax=ax, set_lims=set_lims, outline_kwargs=outline_kwargs, fill_kwargs=fill_kwargs ) return ax
[docs] class PlanarGeometry(): ""
[docs] def get_area(self): """Returns the area of the polygon described by the PlanarGeometry. :rtype: float """ shell=self.get_shell() holes=[] return geometry_functions.polygon_area_3d(shell,holes)
[docs] def get_coordinates(self): """Returns the coordinates of the polyloop child element. :returns: Point_coordinates where each point_coordinate is a tuple of the (x,y,(z)) coordinates of a CartesianPoint. :rtype: tuple(tuple(float)) """ return gbxml_functions.get_coordinate_values_from_PlanarGeometry( self, self.xsd_schema )
[docs] def get_shell(self): """Returns the shell of the polyloop child element. :rtype: tuple(tuple(float)) """ return gbxml_functions.get_shell_of_PlanarGeometry( self, self.xsd_schema )
[docs] def render(self, ax=None, set_lims=True, outline_kwargs=None, fill_kwargs=None): """Renders the PlanarGeometry in 3D using matplotlib. :param ax: A matplotlib 3D Axes instance. Optional, if not supplied then an axis is created and returned. :type ax: matplotlib.axes._subplots.Axes3DSubplot :param set_lims: If True, then the x, y and z axis limits are set automatically based on the geometry being rendered. :type set_lims: bool :param outline_kwargs: matplotlib keywork arguments for formatting the outlines (passed to ax.plot method). :param fill_kwargs: matplotlib keywork arguments for formatting the fill (passed to Poly3DCollection method). :returns: The axis instance. :rtype: matplotlib.axes._subplots.Axes3DSubplot """ ax=self.PolyLoop.render( ax=ax, set_lims=set_lims, outline_kwargs=outline_kwargs, fill_kwargs=fill_kwargs ) return ax
[docs] def set_shell( self, shell ): """Creates new coordinate points for the PlanarGeometry. :param shell: The exterior points of a polygon (first and lost point of shell are the same). :type shell: tuple(tuple(float)) """ gbxml_functions.set_shell_of_PlanarGeometry( gbxml_planar_geometry=self, shell=shell, xsd_schema=self.xsd_schema)
[docs] class PolyLoop(): ""
[docs] def create_CartesianPoints(self,*points_coordinates): """Creates CartesianPoint child elements with Coordinate subelements. :param points_coordinates: An argument list of tuple where each tuple is the (x,y,(z)) coordinates of a CartesianPoint. :type points_coordinates: tuple :returns: The newly creeated CartesianPoint elements. :rtype: list(CartesianPoints) """ for point_coordinates in points_coordinates: self.add_CartesianPoint().create_Coordinates(*point_coordinates) return self.CartesianPoints
[docs] def get_area(self): """ :returns: The area of the PolyLoop :rtype: float """ shell=self.get_shell() holes=[] return geometry_functions.polygon_area_3d(shell,holes)
[docs] def get_coordinates(self): """Returns the coordinates of the CartesianPoint child elements. :returns: Point_coordinates where each point_coordinate is a tuple of the (x,y,(z)) coordinates of a CartesianPoint. :rtype: tuple(tuple(float)) """ return gbxml_functions.get_coordinate_values_from_PolyLoop( self, self.xsd_schema )
[docs] def get_shell(self): """Returns the shell of the Polyloop. :rtype: tuple(tuple(float)) """ return gbxml_functions.get_shell_of_PolyLoop( self, self.xsd_schema )
[docs] def render(self, ax=None, set_lims=True, outline_kwargs=None, fill_kwargs=None): """Renders the PolyLoop in 3D using matplotlib. :param ax: A matplotlib 3D Axes instance. Optional, if not supplied then an axis is created and returned. :type ax: matplotlib.axes._subplots.Axes3DSubplot :param set_lims: If True, then the x, y and z axis limits are set automatically based on the geometry being rendered. :type set_lims: bool :param outline_kwargs: matplotlib keywork arguments for formatting the outlines (passed to ax.plot method). :param fill_kwargs: matplotlib keywork arguments for formatting the fill (passed to Poly3DCollection method). :returns: The axis instance. :rtype: matplotlib.axes._subplots.Axes3DSubplot """ ax=render_functions.render_polygon_3d( polygon=(self.get_shell(),[]), polygon_triangles=None, ax=ax, set_lims=set_lims, outline_kwargs=outline_kwargs, fill_kwargs=fill_kwargs ) return ax
[docs] class RectangularGeometry(): """ """
[docs] def get_shell(self): """Returns the shell coordinates of the rectangular geometry. The following sources are tried in order: - RectangularGeometry/PolyLoop - RectangularGeoemetry... from height and width :rtype: tuple(tuple(float)) """ return gbxml_functions.get_shell_of_RectangularGeometry( self, self.xsd_schema )
# def get_shell_from_height_and_width(self): # """Returns the shell coordinates of the rectangular geometry. # :rtype: tuple(tuple(float)) # """ # return gbxml_functions.get_shell_from_height_and_width_of_RectangularGeometry( # self, # self.xsd_schema # )
[docs] def render(self, ax=None, set_lims=True, outline_kwargs=None, fill_kwargs=None): """Renders the RectangularGeometry in 3D using matplotlib. :param ax: A matplotlib 3D Axes instance. Optional, if not supplied then an axis is created and returned. :type ax: matplotlib.axes._subplots.Axes3DSubplot :param set_lims: If True, then the x, y and z axis limits are set automatically based on the geometry being rendered. :type set_lims: bool :param outline_kwargs: matplotlib keywork arguments for formatting the outlines (passed to ax.plot method). :param fill_kwargs: matplotlib keywork arguments for formatting the fill (passed to Poly3DCollection method). :returns: The axis instance. :rtype: matplotlib.axes._subplots.Axes3DSubplot """ ax=render_functions.render_polygon_3d( polygon=(self.get_shell(),[]), polygon_triangles=None, ax=ax, set_lims=set_lims, outline_kwargs=outline_kwargs, fill_kwargs=fill_kwargs ) return ax
[docs] class Space(): # def get_gaps_in_surfaces( # self # ): # """ # """ # result = gbxml_functions.get_gaps_in_Surfaces_of_Space( # self, # self.xsd_schema # ) # return result
[docs] def get_surfaces( self ): """Returns all Surfaces adjacent to the Space. :rtype: gbCollection(Surfaces) """ result = gbxml_functions.get_Surfaces_of_Space( self, self.xsd_schema ) return gbCollection(*result)
[docs] def render(self, ax=None, set_lims=True, surface_outline_kwargs=None, surface_fill_kwargs=None, opening_outline_kwargs=None, opening_fill_kwargs=None): """Renders the Surfaces of the Space in 3D using matplotlib. :param ax: A matplotlib 3D Axes instance. Optional, if not supplied then an axis is created and returned. :type ax: matplotlib.axes._subplots.Axes3DSubplot :param set_lims: If True, then the x, y and z axis limits are set automatically based on the geometry being rendered. :type set_lims: bool :param surface_outline_kwargs: matplotlib keywork arguments for formatting the outlines of surfaces (passed to ax.plot method). :param surface_fill_kwargs: matplotlib keywork arguments for formatting the fill of surfaces (passed to Poly3DCollection method). :param opening_outline_kwargs: matplotlib keywork arguments for formatting the outlines of openings (passed to ax.plot method). :param opening_fill_kwargs: matplotlib keywork arguments for formatting the fill of openings (passed to Poly3DCollection method). :returns: The axis instance. :rtype: matplotlib.axes._subplots.Axes3DSubplot """ #print(self) for su in self.get_surfaces(): #try: ax=su.render(ax=ax, set_lims=set_lims, surface_outline_kwargs=surface_outline_kwargs, surface_fill_kwargs=surface_fill_kwargs, opening_outline_kwargs=opening_outline_kwargs, opening_fill_kwargs=opening_fill_kwargs ) #except TypeError as err: # print(su) # print(type(err)) return ax
[docs] class Surface(): ""
[docs] def copy_opening(self, opening, tolerance=0.01): """Makes a copy of an Opening and places it on the Surface. :param opening: The opening to be copied. :type opening: xgbxml.xgbxml.Opening :param tolerance: The distance which an opening can be 'snapped' to a surface in meters. :type tolerance: fload :returns: The newly created Opening. :rtype: xgbxml.xgbxml.Opening """ return gbxml_functions.copy_Opening_to_Surface( opening, self, self.xsd_schema, tolerance=tolerance )
[docs] def get_area(self): """Calculates the area of the surface (does not include the area of any openings) :returns: The area of the Surface (the shell area minus the Opening areas) :rtype: float """ shell=self.get_shell() holes=self.get_holes() return geometry_functions.polygon_area_3d(shell,holes)
[docs] def get_holes(self): """Returns the coordinates of the holes of the surface. :rtype: list(tuple(tuple(float))) """ return gbxml_functions.get_holes_of_Surface(self, self.xsd_schema)
[docs] def get_shell(self): """Returns the shell coordinates of the outer polyloop of the opening. The following sources are tried in order: - PlanarGeometry - RectangularGeometry/PolyLoop - RectangularGeoemetry... from height and width :rtype: tuple(tuple(float)) """ return gbxml_functions.get_shell_of_Surface(self, self.xsd_schema)
[docs] def get_Spaces(self): """Returns the space elements adjacent to the surface. :returns: The Spaces as given by the AdjacentSpaceId elements. :rtype: gbCollection """ return gbCollection( *gbxml_functions.get_Spaces_of_Surface(self) )
[docs] def get_polygon(self): """Returns the polygon coordinates of the outer polyloop of the surface. The following sources are tried in order: - PlanarGeometry - RectangularGeometry/PolyLoop - RectangularGeoemetry... from height and width :returns: (shell_coordinates, list of hole coordinates) :rtype: ( tuple(tuple(float)), list(tuple(tuple(float))) ) """ return gbxml_functions.get_polygon_of_Surface(self, self.xsd_schema)
[docs] def render(self, ax=None, set_lims=True, surface_outline_kwargs=None, surface_fill_kwargs=None, opening_outline_kwargs=None, opening_fill_kwargs=None): """Renders the Surface in 3D using matplotlib. :param ax: A matplotlib 3D Axes instance. Optional, if not supplied then an axis is created and returned. :type ax: matplotlib.axes._subplots.Axes3DSubplot :param set_lims: If True, then the x, y and z axis limits are set automatically based on the geometry being rendered. :type set_lims: bool :param surface_outline_kwargs: matplotlib keywork arguments for formatting the outlines of surfaces (passed to ax.plot method). :param surface_fill_kwargs: matplotlib keywork arguments for formatting the fill of surfaces (passed to Poly3DCollection method). :param opening_outline_kwargs: matplotlib keywork arguments for formatting the outlines of openings (passed to ax.plot method). :param opening_fill_kwargs: matplotlib keywork arguments for formatting the fill of openings (passed to Poly3DCollection method). :returns: The axis instance. :rtype: matplotlib.axes._subplots.Axes3DSubplot """ ax=render_functions.render_polygon_3d( polygon=self.get_polygon(), polygon_triangles=None, ax=ax, set_lims=set_lims, outline_kwargs=surface_outline_kwargs, fill_kwargs=surface_fill_kwargs ) for op in self.Openings: ax=op.render(ax=ax, set_lims=set_lims, outline_kwargs=opening_outline_kwargs, fill_kwargs=opening_fill_kwargs) return ax