Importer
BRAILS++ is a modular Python framework for creating workflows that create data for SimCenter applications. To avoid hard-coding every possible case, BRAILS++ includes a utility class that can return a class object based on its name — the Importer class.
The Importer
class has a single method, get_class()
, which takes a class name as input and returns the corresponding Python class, allowing you to instantiate objects dynamically.
The following example demonstrates a typical Python usage scenario:
1# Written: fmk 4/23
2# License: BSD-2
3
4import argparse
5
6from brails.types.asset_inventory import AssetInventory
7from brails.types.region_boundary import RegionBoundary
8from brails.scrapers.osm_footprint_scraper.osm_footprint_scraper import OSM_FootprintScraper
9from brails.scrapers.ms_footprint_scraper.ms_footprint_handler import MS_FootprintScraper
10from brails.scrapers.usa_footprint_scraper.usa_footprint_scraper import USA_FootprintScraper
11
12def main():
13
14 # Create the argument parser
15 parser = argparse.ArgumentParser(description="Demonstrate Importer.")
16 parser.add_argument('scraper', type=str, help="Location")
17 parser.add_argument('location', type=str, help="Location")
18
19 # Parse the arguments
20 args = parser.parse_args()
21
22 # create a RegionBoundary
23 region_boundary_object = RegionBoundary({"type": "locationName", "data": args.location})
24
25 scraper_type = args.scraper
26 if scraper_type == "OSM_FootprintScraper":
27 scraper = OSM_FootprintScraper({"length": "ft"})
28 elif scraper_type == "MS_FootprintScraper":
29 scraper = MS_FootprintScraper({"length": "ft"})
30 elif scraper_type == "USA_FootprintScraper":
31 scraper = USA_FootprintScraper({"length": "ft"})
32 else:
33 print(f"Unknown Scraper Type: {scraper_type}")
34
35 inventory = scraper.get_footprints(region_boundary_object)
36
37 print(f"num assets found: {len(inventory.inventory)} for {args.location} using {args.scraper}")
38
39 small_inventory = inventory.get_random_sample(4, 200)
40 small_inventory.print_info()
41
42# Run the main function if this script is executed directly
43if __name__ == "__main__":
44 main()
The following example shows it re-written using the Importer
class.
1# Written: fmk 09/24
2# License: BSD-2
3
4"""
5importer.py
6================
7
8This is a simple example to demonstrate the Importer class in BRAILS++
9
10"""
11
12import argparse
13import sys
14
15# the following line is not neeeded if brails is imported from pypi
16# .. it is included here as it allows us to test the code on a nightly basis
17sys.path.insert(1, "../../")
18
19
20from brails.utils.importer import Importer
21
22
23def main():
24 # Create the argument parser
25 parser = argparse.ArgumentParser(description="Demonstrate Importer.")
26 parser.add_argument('scraper', type=str, help="Footprint Scraper")
27 parser.add_argument('location', type=str, help="Location")
28
29 # Parse the arguments
30 args = parser.parse_args()
31
32 importer = Importer()
33
34 region_boundary_class = importer.get_class("RegionBoundary")
35 region_boundary_object = region_boundary_class({"type": "locationName", "data": args.location})
36 scraper_class = importer.get_class(args.scraper)
37 scraper = scraper_class({"length": "ft"})
38 inventory = scraper.get_footprints(region_boundary_object)
39 print(f"num assets found: {len(inventory.inventory)} for {args.location} using {args.scraper}")
40
41
42# Run the main function if this script is executed directly
43if __name__ == "__main__":
44 main()
45
To run, for example, the importer.py script for Berkeley, CA, you would enter the following command in a terminal window:
python3 importer.py USA_FootprintScraper "Berkeley, CA"
and the application would produce:
1
2Searching for Berkeley, CA...
3Found Berkeley, Alameda County, California, United States
4
5Meshing the defined area...
6
7Meshing complete. Split Berkeley into 76 cells
8
9Found a total of 28404 building footprints in Berkeley
10num assets found: 28404 for Berkeley, CA using USA_FootprintScraper
11AssetInventory
12Inventory stored in: dict
13Key: 1494 Asset:
14 Coordinates: [[-122.251349037992, 37.8918864620566], [-122.251516845084, 37.8918428029327], [-122.251549765644, 37.8919222669851], [-122.251381955857, 37.891965925353], [-122.251349037992, 37.8918864620566]]
15 Features: {'type': 'Building', 'buildingheight': 17.4, 'fpAreas': 1554}
16Key: 6666 Asset:
17 Coordinates: [[-122.249638045616, 37.8518085710136], [-122.249704555083, 37.8517353439222], [-122.249817801199, 37.8518000117672], [-122.249751291732, 37.8518732380851], [-122.249638045616, 37.8518085710136]]
18 Features: {'type': 'Building', 'buildingheight': 21.6, 'fpAreas': 1326}
19Key: 24068 Asset:
20 Coordinates: [[-122.286804056442, 37.8794971468902], [-122.286829349408, 37.8794214649434], [-122.286967954066, 37.8794505676668], [-122.286942660203, 37.8795262510017], [-122.286804056442, 37.8794971468902]]
21 Features: {'type': 'Building', 'buildingheight': 20.4, 'fpAreas': 1181}
22Key: 23236 Asset:
23 Coordinates: [[-122.260463864989, 37.8783452637543], [-122.260597977173, 37.8783233546667], [-122.260614329206, 37.8783862371837], [-122.260480216124, 37.8784081469617], [-122.260463864989, 37.8783452637543]]
24 Features: {'type': 'Building', 'buildingheight': 25.6, 'fpAreas': 925}
Note
The purpose of the
Importer
class is to enable the development of applications for building workflows. For example, imagine creating an application that parses the following JSON input file. By using theImporter
class, the application can be written without lengthyif-else
statements that switch on the type. This is possible in BRAILS++ because all the classes that perform work inherit fromabc.ABC
(Abstract Base Class), which is part of Python’sabc
module.
1{
2 "_comment": "Building workflow #1 — Demonstrates integration of image filters, processors, and BRAILS++ prediction modules.",
3 "workflows": [
4 {
5 "type": "buildingInventory",
6 "location": {
7 "classType": "boundingBox",
8 "appData": {
9 "coordinates": "-122.2,55.6,-122.56,55.7"
10 }
11 },
12 "footprintSource": {
13 "classType": "OvertureMaps",
14 "appData": {
15 "LengthUnit": "m"
16 }
17 },
18 "augmentFiles": [
19 {
20 "classType": "basic",
21 "appData": {
22 "fileName": "assessor.csv",
23 "colMapping": "mapAssessor.json"
24 }
25 },
26 {
27 "classType": "basic",
28 "appData": {
29 "fileName": "myData.csv",
30 "colMapping": "None"
31 }
32 }
33 ],
34 "satelliteImageSource": {
35 "classType": "Google",
36 "appData": {
37 "gcpKey": "YOUR-GCP-KEY"
38 }
39 },
40 "streetImageSource": {
41 "classType": "Google",
42 "appData": {
43 "gcpKey": "YOUR-GCP-KEY"
44 }
45 },
46 "streetFilters": [
47 {
48 "classType": "VanishingPoint",
49 "appData": {}
50 }
51 ]
52 }
53 ]
54}
An additional benefit of this approach is that when developers add new subclasses, they do not need to search the codebase to locate and update all the if-else statements required for their classes to function. The process is fully automated.
When constructing a
Importer
object, the code automatically scans all directories within the BRAILS++ codebase to identify every available class. This means that developers can integrate their new code without making any modifications to the existingImporter
class.Most of the provided examples will make use of the
Importer
class, both to illustrate its functionality and to serve as a means of testing its behavior.