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

    1. 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 the Importer class, the application can be written without lengthy if-else statements that switch on the type. This is possible in BRAILS++ because all the classes that perform work inherit from abc.ABC (Abstract Base Class), which is part of Python’s abc 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}
  1. 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.

  2. 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 existing Importer class.

  3. 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.