Skip to content

lazer.config

We process airborne LiDAR data using fairly consistent settings that we establish via configuration files. Many processing configurations are managed via yaml files in the lazer/config/ directory, which are read and mapped in the lazer.config module.


LAStoolsPaths

Stores os-specific paths to LAStools executables.

__init__(self, system='Linux', lastools_directory='/home/cba/src/lastools/bin') special

Creates a config object that stores OS-specific paths to LAStools executables.

Parameters:

Name Type Description Default
system str

the operating system to build paths for. Options include ["Linux", "CentOS", "Windows", "Darwin"].

'Linux'
lastools_directory str

path to the lastools executables.

'/home/cba/src/lastools/bin'
Source code in lazer/config/__init__.py
def __init__(self, system: str = SYSTEM, lastools_directory: str = LTBASE) -> None:
    """Creates a config object that stores OS-specific paths to LAStools executables.

    Args:
        system: the operating system to build paths for.
            Options include ["Linux", "CentOS", "Windows", "Darwin"].
        lastools_directory: path to the lastools executables.
    """
    if system.lower() in ["linux", "centos", "darwin"]:
        self.blast2dem = f"wine {os.path.join(lastools_directory, 'blast2dem.exe')}"
        self.las2dem = f"wine {os.path.join(lastools_directory, 'las2dem.exe')}"
        self.las2las = f"wine {os.path.join(lastools_directory, 'las2las.exe')}"
        self.lasboundary = (
            f"wine {os.path.join(lastools_directory, 'lasboundary.exe')}"
        )
        self.lascanopy = f"wine {os.path.join(lastools_directory, 'lascanopy.exe')}"
        self.lasclassify = (
            f"wine {os.path.join(lastools_directory, 'lasclassify.exe')}"
        )
        self.lasclip = f"wine {os.path.join(lastools_directory, 'lasclip.exe')}"
        self.lasgrid = f"wine {os.path.join(lastools_directory, 'lasgrid.exe')}"
        self.lasground = f"wine {os.path.join(lastools_directory, 'lasground.exe')}"
        self.lasheight = f"wine {os.path.join(lastools_directory, 'lasheight.exe')}"
        self.lasinfo = f"wine {os.path.join(lastools_directory, 'lasinfo.exe')}"
        self.lasindex = f"wine {os.path.join(lastools_directory, 'lasindex.exe')}"
        self.lasmerge = f"wine {os.path.join(lastools_directory, 'lasmerge.exe')}"
        self.lasnoise = f"wine {os.path.join(lastools_directory, 'lasnoise.exe')}"
        self.lastile = f"wine {os.path.join(lastools_directory, 'lastile.exe')}"
        self.laszip = f"wine {os.path.join(lastools_directory, 'laszip.exe')}"

    elif system.lower() == "windows":
        self.blast2dem = "blast2dem.exe"
        self.las2dem = "las2dem.exe"
        self.las2las = "las2las.exe"
        self.lasboundary = "lasboundary.exe"
        self.lascanopy = "lascanopy.exe"
        self.lasclassify = "lasclassify.exe"
        self.lasclip = "lasclip.exe"
        self.lasgrid = "lasgrid.exe"
        self.lasground = "lasground.exe"
        self.lasheight = "lasheight.exe"
        self.lasinfo = "lasinfo.exe"
        self.lasindex = "lasindex.exe"
        self.lasmerge = "lasmerge.exe"
        self.lasnoise = "lasnoise.exe"
        self.lastile = "lastile.exe"
        self.laszip = "laszip.exe"

    else:
        raise KeyError(f"OS not recognized: {system}")

MetricConfig

Stores information for lidar-derived environmental metrics.

Attributes:

Name Type Description
name str

the formatted metric name

units str

the unit of measurement for this unit

odix str

the string to append output raster files if batch processed by lastools

__init__(self, metric) special

Get metadata (units, name, etc.) for a specific lidar metric.

Parameters:

Name Type Description Default
metric str

the lidar metric to get the configuration for. you can retrieve the available metrics with config.list_metrics().

required
Source code in lazer/config/__init__.py
def __init__(self, metric: str):
    """Get metadata (units, name, etc.) for a specific lidar metric.

    Args:
        metric: the lidar metric to get the configuration for.
            you can retrieve the available metrics with `config.list_metrics()`.
    """
    available = list_metrics()
    assert (
        metric in available
    ), f"Invalid metric: {metric}. Valid metrics: {', '.join(available)}"

    self.name = METRICS[metric]["name"]
    self.units = METRICS[metric]["units"]
    self.odix = METRICS[metric]["odix"]

PipelineConfig

Stores critical parameters for chained processing pipelines

__init__(self, config={'data': {'buffer_size': 20, 'tile_size': 600, 'drop_above': 150, 'drop_below': -1, 'nodata': -9999, 'slicer_nodata': 255, 'resolution': 1.0, 'slicer_resolution': 10.0, 'slicer_vertical_resolution': 1.0, 'set_horizontal_srs': True, 'target_precision': 0.01, 'target_elevation_precision': 0.01, 'fill': 5, 'subcircle': 0.4, 'epsg': 'none', 'raster_extension': '.tif', 'raster_format': 'GTiff', 'raster_options': ['BIGTIFF=YES', 'TILED=YES', 'COMPRESS=DEFLATE', 'NUM_THREADS=ALL_CPUS'], 'vector_extension': '.shp', 'vector_format': 'ESRI Shapefile', 'cpu_count': 'none', 'ram_mb': 1000}, 'forestry': {'canopy_density_base': 4, 'ladder_fuel_base': 1.0, 'ladder_fuel_ceiling': 4.0, 'max_tch': 99, 'max_base_height': 40, 'overstory_threshold': 4, 'vertical_layer_threshold': 3, 'planar_threshold': 0.25, 'planar_search_radius': 5, 'min_treetop_height': 5, 'min_istree_height': 2, 'crown_search_radius': 3, 'crown_height_decrease_rate': 0.5, 'crown_height_grouping_rate': 0.5, 'max_crown_diameter': 8}, 'terrain': {'town': False, 'city': False, 'metro': False, 'coarse': False, 'extra_coarse': False, 'extra_fine': False, 'fine': True, 'egm_file': '/data/egm2008.tif'}, 'meta': {'creator': 'Salo Sciences', 'license': 'CC BY-NC-SA-4.0', 'license_reference': 'https://creativecommons.org/licenses/by-nc-sa/4.0/'}}) special

Creates the pipeline configuration options object.

Assumes that there's a one-level nesting of the configuration options, and all second-level attributes will be stored as attributes in the output PipelineConfig object.

I wrote the class this way to make it easy to update the config file with new options without having to update the code to make corresponding changes.

Parameters:

Name Type Description Default
config Union[dict, str]

the configuration options to pass to this object. If it's a string, it assumes a path to a yaml file. If it's a dictionary, it assumes the config was already read.

{'data': {'buffer_size': 20, 'tile_size': 600, 'drop_above': 150, 'drop_below': -1, 'nodata': -9999, 'slicer_nodata': 255, 'resolution': 1.0, 'slicer_resolution': 10.0, 'slicer_vertical_resolution': 1.0, 'set_horizontal_srs': True, 'target_precision': 0.01, 'target_elevation_precision': 0.01, 'fill': 5, 'subcircle': 0.4, 'epsg': 'none', 'raster_extension': '.tif', 'raster_format': 'GTiff', 'raster_options': ['BIGTIFF=YES', 'TILED=YES', 'COMPRESS=DEFLATE', 'NUM_THREADS=ALL_CPUS'], 'vector_extension': '.shp', 'vector_format': 'ESRI Shapefile', 'cpu_count': 'none', 'ram_mb': 1000}, 'forestry': {'canopy_density_base': 4, 'ladder_fuel_base': 1.0, 'ladder_fuel_ceiling': 4.0, 'max_tch': 99, 'max_base_height': 40, 'overstory_threshold': 4, 'vertical_layer_threshold': 3, 'planar_threshold': 0.25, 'planar_search_radius': 5, 'min_treetop_height': 5, 'min_istree_height': 2, 'crown_search_radius': 3, 'crown_height_decrease_rate': 0.5, 'crown_height_grouping_rate': 0.5, 'max_crown_diameter': 8}, 'terrain': {'town': False, 'city': False, 'metro': False, 'coarse': False, 'extra_coarse': False, 'extra_fine': False, 'fine': True, 'egm_file': '/data/egm2008.tif'}, 'meta': {'creator': 'Salo Sciences', 'license': 'CC BY-NC-SA-4.0', 'license_reference': 'https://creativecommons.org/licenses/by-nc-sa/4.0/'}}
Source code in lazer/config/__init__.py
def __init__(self, config: Union[dict, str] = PIPELINE):
    """Creates the pipeline configuration options object.

    Assumes that there's a one-level nesting of the configuration options,
        and all second-level attributes will be stored as attributes in the
        output PipelineConfig object.

    I wrote the class this way to make it easy to update the config file with
        new options without having to update the code to make corresponding changes.

    Args:
        config: the configuration options to pass to this object.
            If it's a string, it assumes a path to a yaml file.
            If it's a dictionary, it assumes the config was already read.
    """
    if type(config) is str:
        with open(config, "r") as handler:
            configuration = yaml.load(handler)
    else:
        configuration = config

    metas = list(configuration.keys())
    for meta in metas:
        for key, value in configuration[meta].items():
            setattr(self, key, value)

list_metrics()

Returns key names for all metrics specified in the config.

Source code in lazer/config/__init__.py
def list_metrics() -> list:
    """Returns key names for all metrics specified in the config."""
    return list(METRICS.keys())
Back to top