Source code for xgbxml.xgbxml

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


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

from . import schema_dicts
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.gbxml_xsd_functions as gbxml_xsd_functions

import math
from . import render_functions
from .geometry_functions import vector_normalize_3d, vector_multiplication_3d, vector_addition_3d


[docs]def get_parser(version='6.01'): """Returns a lxml.etree.XMLParser containing custom elements for gbXML files. :param version: The gbxml version string. Default is '6.01'. :type version: str :rtype: lxml.etree.XMLParser """ #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() 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. .. note:: The returned object is a subclass of lxml.Element, not an lxml.ElementTree. :rtype: gbXML """ 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): """The default element class for the gbxml parser. """ def __repr__(self): """The repr for the class :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,**kwargs): """Adds a new child element to the element. :param child_nntag: The 'no namespace' tag of the child element. :type child_nntag: str :param kwargs: Attributes to be set for the child element. :returns: The newly created child element. :rtype: (subclass of) gbElement """ return gbxml_functions.add_child_to_gbxml_element( self, child_nntag, self.xsd_schema, **kwargs)
@property def get_attributes(self): """The attributes of the 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(self, self.xsd_schema) @property def id(self): """The id of the element. :raises KeyError: If, on retrieval, the 'id' attribute is not present in the element. :rtype: str """ return self.get_attribute('id') @id.setter def id(self,value): "" self.set_attribute('id',value)
[docs] def get_attribute(self,attribute_name): """Returns the attribute value as a python type. :param attribute_name: The name of the attribute. :param attribute_name: str :raises KeyError: If the attribute is not present in the element. :rtype: bool, str, float """ return gbxml_functions.get_attribute_of_gbxml_element(self, attribute_name, 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 ??: 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 child elements with a specified tag. :param child_nntag: The 'no e coercednamespace' tag of the child elements. :type child_nntag: str :rtype: list ?? """ return gbCollection( *gbxml_functions.get_children_of_gbxml_element( self, child_nntag ) )
[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. Value is coerced to the correct python type if needed. :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 does not exist in the schema. :raises ValueError: If attribute has enumerations, and 'value' does not match one of the enumeration options. :rtype: The (coerced) value assigned to the attribute. """ return gbxml_functions.set_attribute_on_gbxml_element(self, attribute_name, value, self.xsd_schema)
@property def nntag(self): """Returns the tag without the namespace ('no namespace tag') Example: >>> print(gbXML.tag) {http://www.gbxml.org/schema}gbXML >>> print(gbXML.nntag) gbXML :rtype: str """ return xml_functions.nntag(self) @property def ns(self): """The namespace dictionary for xpath calls. :rtype: dict """ return gbxml_functions.ns
[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): """The value of the element text as a python type. :rtype: str, float """ xsd_type=gbxml_xsd_functions.get_xsd_type_of_text_of_xsd_element( self.nntag, self.xsd_schema ) python_type=xml_functions.xsd_type_to_python_type(xsd_type) return python_type(self.text) @value.setter def value(self,value): "" self.text=str(value)
[docs]class gbCollection(collections.abc.Sequence): """ """ def __getattr__(self,key): "" #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): "" 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 Campus(): ""
[docs] def render(self, ax=None, set_lims=True, outline_kwargs=None, surface_kwargs=None): """ """ #print(self) for su in self.Surfaces: try: ax=su.render(ax=ax) except Exception 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. :type coordinates: int, float :returns: The newly creeated Coordinate elements. :rtype: list(Coordinate) """ 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 Opening(): ""
[docs] def get_shell(self): """Returns a Polygon 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)
def render(self, ax=None, set_lims=True, outline_kwargs=None, surface_kwargs=None): "" 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 surface_kwargs is None: surface_kwargs=x else: for k,v in x.items: if not k in surface_kwargs: surface_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, surface_kwargs=surface_kwargs ) return ax
[docs]class PlanarGeometry(): ""
[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 a Polygon of the polyloop child element. :rtype: tuple """ return gbxml_functions.get_shell_of_PlanarGeometry( self, self.xsd_schema )
def render(self, ax=None, set_lims=True, outline_kwargs=None, surface_kwargs=None): "" ax=self.PolyLoop.render( ax=ax, set_lims=set_lims, outline_kwargs=outline_kwargs, surface_kwargs=surface_kwargs ) return ax
[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_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): """ """ return gbxml_functions.get_shell_of_PolyLoop( self, self.xsd_schema )
def render(self, ax=None, set_lims=True, outline_kwargs=None, surface_kwargs=None): "" ax=render_functions.render_polygon_3d( polygon=(self.get_shell(),[]), polygon_triangles=None, ax=ax, set_lims=set_lims, outline_kwargs=outline_kwargs, surface_kwargs=surface_kwargs ) return ax
[docs]class RectangularGeometry(): """ """
[docs] def get_shell(self): """Returns the 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 )
[docs] def get_shell_from_height_and_width(self): """ """ return gbxml_functions.get_shell_from_height_and_width_of_RectangularGeometry( self, self.xsd_schema )
def render(self, ax=None, set_lims=True, outline_kwargs=None, surface_kwargs=None): "" ax=render_functions.render_polygon_3d( polygon=(self.get_shell(),[]), polygon_triangles=None, ax=ax, set_lims=set_lims, outline_kwargs=outline_kwargs, surface_kwargs=surface_kwargs ) return ax
[docs]class Surface(): ""
[docs] def copy_opening(self, opening, tolerance=0.01): """ """ return gbxml_functions.copy_Opening_to_Surface( opening, self, self.xsd_schema, tolerance=tolerance )
[docs] def get_holes(self): """ """ return gbxml_functions.get_holes_of_Surface(self, self.xsd_schema)
[docs] def get_shell(self): """Returns a Polygon 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. """ return gbxml_functions.get_Spaces_of_Surface(self)
[docs] def get_polygon(self): """Returns a Polygon of the outer polyloop of the surface. The following sources are tried in order: - PlanarGeometry - RectangularGeometry/PolyLoop - RectangularGeoemetry... from height and width :rtype: tuple(tuple(float)) """ return gbxml_functions.get_polygon_of_Surface(self, self.xsd_schema)
def render(self, ax=None, set_lims=True, outline_kwargs=None, surface_kwargs=None): "" ax=render_functions.render_polygon_3d( polygon=self.get_polygon(), polygon_triangles=None, ax=ax, set_lims=set_lims, outline_kwargs=outline_kwargs, surface_kwargs=surface_kwargs ) for op in self.Openings: ax=op.render(ax=ax) return ax