Source code for honeybee_radiance.reader
"""A collection of auxiliary functions for working with radiance files and objects."""
import re
import os
# TODO: Add support for comments [#] and commands [!]
[docs]
def parse_from_string(full_string):
"""Separate a Radiance file string into multiple strings for each object.
Args:
full_string: Radiance data as a single string. The string can be multiline.
Returns:
A list of strings. Each string represents a different Radiance primitive
(geometry or modifier). Comments [#] and commands [!] are excluded.
"""
raw_rad_objects = re.findall(
r'^\s*([^0-9].*(\s*[\d.-]+.*)*)',
full_string,
re.MULTILINE)
rad_objects = (' '.join(radiance_object[0].split())
for radiance_object in raw_rad_objects)
filtered_objects = tuple(rad_object for rad_object in rad_objects
if rad_object and rad_object[0] not in ['#', '!'])
return filtered_objects
[docs]
def parse_from_file(file_path):
"""Parse a Radiance file.
This function breaks down the file into a list of radiance objects as separate
strings.
Args:
file_path: Path to Radiance file
Returns:
A list of strings. Each string represents a different Radiance object.
Usage:
.. code-block:: python
rad_obj_strs = parse_from_file('some_file.rad')
"""
assert os.path.isfile(file_path), "Can't find %s." % file_path
with open(file_path, "r") as rad_file:
return parse_from_string(rad_file.read())
[docs]
def string_to_dicts(string):
"""Convert a radiance string to a list of primitive dictionaries.
These primitive dictionaries can be seralized to honeybee-radiance Python
objects using the from_primitive_dict methods on all primitive classes.
If the primitive modifier is not void or the primitive has other dependencies,
the dependency must also be part of the input string and this method will
ensure that the dependent output dictionaries are correctly nested so that
they can be correctly serialized.
Returns:
A list of dictionaries.
"""
def find_object(target, index):
for o_count, other_obj in enumerate(objects[:-(index + 1)]):
if other_obj['identifier'] == target:
return o_count, other_obj
input_objects = parse_from_string(string)
if not input_objects:
raise ValueError(
'{} includes no radiance objects.'.format(string)
)
# break down each object and convert it to a dict
objects = [string_to_dict(inp) for inp in input_objects]
# start from the last material and try to find dependencies if any)
rev_objects = list(reversed(objects))
remove_index = []
for count, obj in enumerate(rev_objects):
if obj['modifier'] != 'void':
# try to find it in objects and add replace it
try:
o_count, other_obj = find_object(obj['modifier'], count)
except TypeError:
# didn't find any
raise ValueError(
'Failed to find "{}" modifier for "{}" in input string'.format(
obj['modifier'], obj['identifier']
)
)
else:
objects[-(count + 1)]['modifier'] = other_obj
remove_index.append(o_count)
if len(obj['values'][0]) != 0:
for value in obj['values'][0]:
if '(' in value or '"' in value:
continue
# search for dependencies
try:
o_count, other_obj = find_object(value, count)
except TypeError:
# didn't find any
pass
else:
objects[-(count + 1)]['dependencies'].append(other_obj)
remove_index.append(o_count)
if remove_index:
return [obj for index, obj in enumerate(objects) if index not in remove_index]
else:
return objects
# pattern one handles whitespaces inside ( )
# pattern two handles whitespaces inside " "
# I assume someone who knows re better than I do can do this in a single run!
split_pattern_one = re.compile(r"\s+(?=[^(\")]*(?:\(|$))")
split_pattern_two = re.compile(r"\s+(?=[^()]*(?:\"\w))")
[docs]
def string_to_dict(string):
"""Get a single Radiance string object as a primitive dictionary."""
data = [
d for dt in re.split(split_pattern_one, string)
for d in re.split(split_pattern_one, str(dt))
]
modifier, primitive_type, identifier = data[:3]
base_data = data[3:]
count_1 = int(base_data[0])
count_2 = int(base_data[count_1 + 1])
count_3 = int(base_data[count_1 + count_2 + 2])
l1 = [] if count_1 == 0 else base_data[1: count_1 + 1]
l2 = [] if count_2 == 0 \
else base_data[count_1 + 2: count_1 + count_2 + 2]
l3 = [] if count_3 == 0 \
else base_data[count_1 + count_2 + 3: count_1 + count_2 + count_3 + 3]
return {
'modifier': modifier,
'type': primitive_type,
'identifier': identifier,
'values': [l1, l2, l3],
'dependencies': []
}
[docs]
def sensor_count_from_file(filepath):
"""Return sensor count of a sensor grid file.
This function returns the sensor count of a sensor grid file. Comments [#] and
empty lines will not be counted.
Args:
filepath: Full path to Radiance pts file.
Returns:
sensor_count
"""
sensor_count = 0
with open(filepath, 'r') as pts_file:
for l in pts_file:
if not l.strip() or l[0] == '#':
pass
else:
sensor_count += 1
return sensor_count