Skip to content

Model base

QGIS CPLUS plugin models.

Activity dataclass

Activity(uuid, name, description, path='', layer_type=LayerType.UNDEFINED, user_defined=False, pathways=list(), priority_layers=list(), layer_styles=dict(), style_pixel_value=-1)

Bases: LayerModelComponent

Contains information about an activity used in a scenario. If the layer has been set then it will not be possible to add NCS pathways unless the layer is cleared. Priority will be given to the layer property.

__post_init__

__post_init__()

Pre-checks on initialization.

Source code in src/cplus_plugin/models/base.py
def __post_init__(self):
    """Pre-checks on initialization."""
    super().__post_init__()

    # Ensure there are no duplicate pathways.
    uuids = [str(p.uuid) for p in self.pathways]

    if len(set(uuids)) != len(uuids):
        msg = "Duplicate pathways found in activity"
        raise ValueError(f"{msg} {self.name}.")

    # Reset pathways if layer has also been set.
    if self.to_map_layer() is not None and len(self.pathways) > 0:
        self.pathways = []

activity_layer_style_info

activity_layer_style_info()

Returns the color ramp properties for styling the activity layer resulting from a scenario run.

Returns:

Type Description
dict

Color ramp properties for the activity styling or an empty dictionary if there was no definition found in the root style.

Source code in src/cplus_plugin/models/base.py
def activity_layer_style_info(self) -> dict:
    """Returns the color ramp properties for styling the activity
    layer resulting from a scenario run.

    :returns: Color ramp properties for the activity styling or an
    empty dictionary if there was no definition found in the root
    style.
    :rtype: dict
    """
    if (
        len(self.layer_styles) == 0
        or ACTIVITY_LAYER_STYLE_ATTRIBUTE not in self.layer_styles
    ):
        return dict()

    return self.layer_styles[ACTIVITY_LAYER_STYLE_ATTRIBUTE]

add_ncs_pathway

add_ncs_pathway(ncs)

Adds an NCS pathway object to the collection.

Parameters:

Name Type Description Default
ncs NcsPathway

NCS pathway to be added to the activity.

required

Returns:

Type Description
bool

True if the NCS pathway was successfully added, else False if there was an existing NCS pathway object with a similar UUID or the layer property had already been set.

Source code in src/cplus_plugin/models/base.py
def add_ncs_pathway(self, ncs: NcsPathway) -> bool:
    """Adds an NCS pathway object to the collection.

    :param ncs: NCS pathway to be added to the activity.
    :type ncs: NcsPathway

    :returns: True if the NCS pathway was successfully added, else False
    if there was an existing NCS pathway object with a similar UUID or
    the layer property had already been set.
    """

    if not ncs.is_valid():
        return False

    if self.contains_pathway(str(ncs.uuid)):
        return False

    self.pathways.append(ncs)

    return True

clear_layer

clear_layer()

Removes a reference to the layer URI defined in the path attribute.

Source code in src/cplus_plugin/models/base.py
def clear_layer(self):
    """Removes a reference to the layer URI defined in the path attribute."""
    self.path = ""

color_ramp

color_ramp()

Create a color ramp for styling the activity layer resulting from a scenario run.

Returns:

Type Description
QgsColorRamp

A color ramp for styling the activity layer or None if there was no definition found.

Source code in src/cplus_plugin/models/base.py
def color_ramp(self) -> typing.Union[QgsColorRamp, None]:
    """Create a color ramp for styling the activity layer resulting
    from a scenario run.

    :returns: A color ramp for styling the activity layer or None
    if there was no definition found.
    :rtype: QgsColorRamp
    """
    model_layer_info = self.activity_layer_style_info()
    if len(model_layer_info) == 0:
        return None

    ramp_info = model_layer_info.get(COLOR_RAMP_PROPERTIES_ATTRIBUTE, None)
    if ramp_info is None or len(ramp_info) == 0:
        return None

    ramp_type = model_layer_info.get(COLOR_RAMP_TYPE_ATTRIBUTE, None)
    if ramp_type is None:
        return None

    # New ramp types will need to be added here manually
    if ramp_type == QgsColorBrewerColorRamp.typeString():
        return QgsColorBrewerColorRamp.create(ramp_info)
    elif ramp_type == QgsCptCityColorRamp.typeString():
        return QgsCptCityColorRamp.create(ramp_info)
    elif ramp_type == QgsGradientColorRamp.typeString():
        return QgsGradientColorRamp.create(ramp_info)
    elif ramp_type == QgsLimitedRandomColorRamp.typeString():
        return QgsLimitedRandomColorRamp.create(ramp_info)
    elif ramp_type == QgsPresetSchemeColorRamp.typeString():
        return QgsPresetSchemeColorRamp.create(ramp_info)
    elif ramp_type == QgsRandomColorRamp.typeString():
        return QgsRandomColorRamp()

    return None

contains_pathway

contains_pathway(pathway_uuid)

Checks if there is an NCS pathway matching the given UUID.

Parameters:

Name Type Description Default
pathway_uuid str

UUID to search for in the collection.

required

Returns:

Type Description
bool

True if there is a matching NCS pathway, else False.

Source code in src/cplus_plugin/models/base.py
def contains_pathway(self, pathway_uuid: str) -> bool:
    """Checks if there is an NCS pathway matching the given UUID.

    :param pathway_uuid: UUID to search for in the collection.
    :type pathway_uuid: str

    :returns: True if there is a matching NCS pathway, else False.
    :rtype: bool
    """
    ncs_pathway = self.pathway_by_uuid(pathway_uuid)
    if ncs_pathway is None:
        return False

    return True

from_dict classmethod

from_dict(activity_dict)

Create an Activity object from Activity dict.

Source code in src/cplus_plugin/models/base.py
@classmethod
def from_dict(cls, activity_dict: typing.Dict):
    """Create an Activity object from Activity dict."""
    pathways = []
    for pathway in activity_dict["pathways"]:
        del pathway["layer_uuid"]
        del pathway["carbon_uuids"]
        pathways.append(NcsPathway(**pathway))
    activity_dict["pathways"] = pathways
    return Activity(**activity_dict)

is_pwls_valid

is_pwls_valid()

Checks if the priority layers are valid.

Returns:

Type Description
bool

True if all priority layers are valid, else False if even one is invalid. If there are no priority layers defined, it will always return True.

Source code in src/cplus_plugin/models/base.py
def is_pwls_valid(self) -> bool:
    """Checks if the priority layers are valid.

    :returns: True if all priority layers are valid, else False if
    even one is invalid. If there are no priority layers defined, it will
    always return True.
    :rtype: bool
    """
    is_valid = True
    for cl in self.pw_layers():
        if not cl.isValid():
            is_valid = False
            break

    return is_valid

is_valid

is_valid()

Includes an additional check to assert if NCS pathways have been specified if the layer has not been set or is not valid.

Does not check for validity of individual NCS pathways in the collection.

Source code in src/cplus_plugin/models/base.py
def is_valid(self) -> bool:
    """Includes an additional check to assert if NCS pathways have
    been specified if the layer has not been set or is not valid.

    Does not check for validity of individual NCS pathways in the
    collection.
    """
    if self.to_map_layer() is not None:
        return super().is_valid()
    else:
        if len(self.pathways) == 0:
            return False

        if not self.is_pwls_valid():
            return False

        return True

pathway_by_uuid

pathway_by_uuid(pathway_uuid)

Returns an NCS pathway matching the given UUID.

Parameters:

Name Type Description Default
pathway_uuid str

UUID for the NCS pathway to retrieve.

required

Returns:

Type Description
NcsPathway

NCS pathway object matching the given UUID else None if not found.

Source code in src/cplus_plugin/models/base.py
def pathway_by_uuid(self, pathway_uuid: str) -> typing.Union[NcsPathway, None]:
    """Returns an NCS pathway matching the given UUID.

    :param pathway_uuid: UUID for the NCS pathway to retrieve.
    :type pathway_uuid: str

    :returns: NCS pathway object matching the given UUID else None if
    not found.
    :rtype: NcsPathway
    """
    pathways = [p for p in self.pathways if str(p.uuid) == pathway_uuid]

    if len(pathways) == 0:
        return None

    return pathways[0]

pw_layers

pw_layers()

Returns the list of priority weighting layers defined under the :py:attr:~priority_layers attribute.

Returns:

Type Description
list

Priority layers for the implementation or an empty list if the path is not defined.

Source code in src/cplus_plugin/models/base.py
def pw_layers(self) -> typing.List[QgsRasterLayer]:
    """Returns the list of priority weighting layers defined under
    the :py:attr:`~priority_layers` attribute.

    :returns: Priority layers for the implementation or an empty list
    if the path is not defined.
    :rtype: list
    """
    return [
        QgsRasterLayer(layer.get("path"))
        for layer in self.priority_layers
        if layer.get("path")
    ]

remove_ncs_pathway

remove_ncs_pathway(pathway_uuid)

Removes the NCS pathway with a matching UUID from the collection.

Parameters:

Name Type Description Default
pathway_uuid str

UUID for the NCS pathway to be removed.

required

Returns:

Type Description
bool

True if the NCS pathway object was successfully removed, else False if there is no object matching the given UUID.

Source code in src/cplus_plugin/models/base.py
def remove_ncs_pathway(self, pathway_uuid: str) -> bool:
    """Removes the NCS pathway with a matching UUID from the collection.

    :param pathway_uuid: UUID for the NCS pathway to be removed.
    :type pathway_uuid: str

    :returns: True if the NCS pathway object was successfully removed,
     else False if there is no object matching the given UUID.
    :rtype: bool
    """
    idxs = [i for i, p in enumerate(self.pathways) if str(p.uuid) == pathway_uuid]

    if len(idxs) == 0:
        return False

    rem_idx = idxs[0]
    _ = self.pathways.pop(rem_idx)

    return True

scenario_fill_symbol

scenario_fill_symbol()

Creates a fill symbol for the activity in the scenario.

Returns:

Type Description
QgsFillSymbol

Fill symbol for the activity in the scenario or None if there was no definition found.

Source code in src/cplus_plugin/models/base.py
def scenario_fill_symbol(self) -> typing.Union[QgsFillSymbol, None]:
    """Creates a fill symbol for the activity in the scenario.

    :returns: Fill symbol for the activity in the scenario
    or None if there was no definition found.
    :rtype: QgsFillSymbol
    """
    scenario_style_info = self.scenario_layer_style_info()
    if len(scenario_style_info) == 0:
        return None

    return QgsFillSymbol.createSimple(scenario_style_info)

scenario_layer_style_info

scenario_layer_style_info()

Returns the fill symbol properties for styling the activity layer in the final scenario result.

Returns:

Type Description
dict

Fill symbol properties for the activity layer styling in the scenario layer or an empty dictionary if there was no definition found in the root style.

Source code in src/cplus_plugin/models/base.py
def scenario_layer_style_info(self) -> dict:
    """Returns the fill symbol properties for styling the activity
    layer in the final scenario result.

    :returns: Fill symbol properties for the activity layer
    styling in the scenario layer or an empty dictionary if there was
    no definition found in the root style.
    :rtype: dict
    """
    if (
        len(self.layer_styles) == 0
        or ACTIVITY_SCENARIO_STYLE_ATTRIBUTE not in self.layer_styles
    ):
        return dict()

    return self.layer_styles[ACTIVITY_SCENARIO_STYLE_ATTRIBUTE]

BaseModelComponent dataclass

BaseModelComponent(uuid, name, description)

Base class for common model item properties.

__eq__

__eq__(other)

Test equality of object with another BaseModelComponent object using the attributes.

Parameters:

Name Type Description Default
other BaseModelComponent

BaseModelComponent object to compare with this object.

required

Returns:

Type Description
bool

True if the all the attribute values match, else False.

Source code in src/cplus_plugin/models/base.py
def __eq__(self, other: "BaseModelComponent") -> bool:
    """Test equality of object with another BaseModelComponent
    object using the attributes.

    :param other: BaseModelComponent object to compare with this object.
    :type other: BaseModelComponent

    :returns: True if the all the attribute values match, else False.
    :rtype: bool
    """
    if self.uuid != other.uuid:
        return False

    if self.name != other.name:
        return False

    if self.description != other.description:
        return False

    return True

LayerModelComponent dataclass

LayerModelComponent(uuid, name, description, path='', layer_type=LayerType.UNDEFINED, user_defined=False)

Bases: BaseModelComponent

Base class for model components that support a map layer.

layer_uuid property

layer_uuid

Return Layer UUID for default layer.

Default layer's path will start with 'cplus://'.

Returns:

Type Description
str

Server Layer UUID

__eq__

__eq__(other)

Uses BaseModelComponent equality test rather than what the dataclass default implementation will provide.

Source code in src/cplus_plugin/models/base.py
def __eq__(self, other) -> bool:
    """Uses BaseModelComponent equality test rather than
    what the dataclass default implementation will provide.
    """
    if self.layer_uuid:
        return self.layer_uuid == other.layer_uuid
    return super().__eq__(other)

__post_init__

__post_init__()

Try to set the layer and layer type properties.

Source code in src/cplus_plugin/models/base.py
def __post_init__(self):
    """Try to set the layer and layer type properties."""
    if self.layer_uuid:
        return
    self.update_layer_type()

is_default_layer

is_default_layer()

Check if layer is a default layer

Returns:

Type Description
bool

True if layer comes from server API

Source code in src/cplus_plugin/models/base.py
def is_default_layer(self) -> bool:
    """Check if layer is a default layer

    :return: True if layer comes from server API
    :rtype: bool
    """
    return self.layer_uuid is not None

is_valid

is_valid()

Checks if the corresponding map layer is valid.

Returns:

Type Description
bool

True if the map layer is valid, else False if map layer is invalid or of None type.

Source code in src/cplus_plugin/models/base.py
def is_valid(self) -> bool:
    """Checks if the corresponding map layer is valid.

    :returns: True if the map layer is valid, else False if map layer is
    invalid or of None type.
    :rtype: bool
    """
    if self.layer_uuid:
        return True
    layer = self.to_map_layer()
    if layer is None:
        return False

    return layer.isValid()

to_map_layer

to_map_layer()

Constructs a map layer from the specified path.

It will first check if the layer property has been set else try to construct the layer from the path else return None.

Returns:

Type Description
QgsMapLayer

Map layer corresponding to the set layer property or specified path.

Source code in src/cplus_plugin/models/base.py
def to_map_layer(self) -> typing.Union[QgsMapLayer, None]:
    """Constructs a map layer from the specified path.

    It will first check if the layer property has been set
    else try to construct the layer from the path else return
    None.

    :returns: Map layer corresponding to the set layer
    property or specified path.
    :rtype: QgsMapLayer
    """
    if not os.path.exists(self.path):
        return None

    layer = None
    if self.layer_type == LayerType.RASTER:
        layer = QgsRasterLayer(self.path, self.name)

    elif self.layer_type == LayerType.VECTOR:
        layer = QgsVectorLayer(self.path, self.name)

    return layer

update_layer_type

update_layer_type()

Update the layer type if either the layer or path properties have been set.

Source code in src/cplus_plugin/models/base.py
def update_layer_type(self):
    """Update the layer type if either the layer or
    path properties have been set.
    """
    layer = self.to_map_layer()
    if layer is None:
        return

    if not layer.isValid():
        return

    if isinstance(layer, QgsRasterLayer):
        self.layer_type = LayerType.RASTER

    elif isinstance(layer, QgsVectorLayer):
        self.layer_type = LayerType.VECTOR

LayerType

Bases: IntEnum

QGIS spatial layer type.

ModelComponentType

Bases: Enum

Type of model component i.e. NCS pathway or activity.

from_string staticmethod

from_string(str_enum)

Creates an enum from the corresponding string equivalent.

Parameters:

Name Type Description Default
str_enum str

String representing the model component type.

required

Returns:

Type Description
ModelComponentType

Component type enum corresponding to the given string else unknown if not found.

Source code in src/cplus_plugin/models/base.py
@staticmethod
def from_string(str_enum: str) -> "ModelComponentType":
    """Creates an enum from the corresponding string equivalent.

    :param str_enum: String representing the model component type.
    :type str_enum: str

    :returns: Component type enum corresponding to the given
    string else unknown if not found.
    :rtype: ModelComponentType
    """
    if str_enum.lower() == "ncs_pathway":
        return ModelComponentType.NCS_PATHWAY
    elif str_enum.lower() == "activity":
        return ModelComponentType.ACTIVITY

    return ModelComponentType.UNKNOWN

NcsPathway dataclass

NcsPathway(uuid, name, description, path='', layer_type=LayerType.UNDEFINED, user_defined=False, carbon_paths=list())

Bases: LayerModelComponent

Contains information about an NCS pathway layer.

__eq__

__eq__(other)

Test equality of NcsPathway object with another NcsPathway object using the attributes.

Excludes testing the map layer for equality.

Parameters:

Name Type Description Default
other NcsPathway

NcsPathway object to compare with this object.

required

Returns:

Type Description
bool

True if all the attribute values match, else False.

Source code in src/cplus_plugin/models/base.py
def __eq__(self, other: "NcsPathway") -> bool:
    """Test equality of NcsPathway object with another
    NcsPathway object using the attributes.

    Excludes testing the map layer for equality.

    :param other: NcsPathway object to compare with this object.
    :type other: NcsPathway

    :returns: True if all the attribute values match, else False.
    :rtype: bool
    """
    base_equality = super().__eq__(other)
    if not base_equality:
        return False

    if self.path != other.path:
        return False

    if self.layer_type != other.layer_type:
        return False

    if self.user_defined != other.user_defined:
        return False

    return True

add_carbon_path

add_carbon_path(carbon_path, is_default_layer=False)

Add a carbon layer path.

Checks if the path has already been defined or if it exists in the file system.

Returns:

Type Description
bool

True if the carbon layer path was successfully added, else False if the path has already been defined or does not exist in the file system.

Source code in src/cplus_plugin/models/base.py
def add_carbon_path(self, carbon_path: str, is_default_layer: bool = False) -> bool:
    """Add a carbon layer path.

    Checks if the path has already been defined or if it exists
    in the file system.

    :returns: True if the carbon layer path was successfully
    added, else False if the path has already been defined
    or does not exist in the file system.
    :rtype: bool
    """
    if carbon_path in self.carbon_paths:
        return False

    if not is_default_layer:
        if not os.path.exists(carbon_path):
            return False

    self.carbon_paths.append(carbon_path)

    return True

carbon_layers

carbon_layers()

Returns the list of carbon layers whose path is defined under the :py:attr:~carbon_paths attribute.

The caller should check the validity of the layers or use :py:meth:~is_carbon_valid function.

Returns:

Type Description
list

Carbon layers for the NCS pathway or an empty list if the path is not defined.

Source code in src/cplus_plugin/models/base.py
def carbon_layers(self) -> typing.List[QgsRasterLayer]:
    """Returns the list of carbon layers whose path is defined under
    the :py:attr:`~carbon_paths` attribute.

    The caller should check the validity of the layers or use
    :py:meth:`~is_carbon_valid` function.

    :returns: Carbon layers for the NCS pathway or an empty list
    if the path is not defined.
    :rtype: list
    """
    return [
        QgsRasterLayer(carbon_path)
        for carbon_path in self.carbon_paths
        if not carbon_path.startswith("cplus://")
    ]

is_carbon_valid

is_carbon_valid()

Checks if the carbon layers are valid.

Returns:

Type Description
bool

True if all carbon layers are valid, else False if even one is invalid. If there are no carbon layers defined, it will always return True.

Source code in src/cplus_plugin/models/base.py
def is_carbon_valid(self) -> bool:
    """Checks if the carbon layers are valid.

    :returns: True if all carbon layers are valid, else False if
    even one is invalid. If there are no carbon layers defined, it will
    always return True.
    :rtype: bool
    """
    is_valid = True
    for cl in self.carbon_layers():
        if not cl.isValid():
            is_valid = False
            break

    return is_valid

is_valid

is_valid()

Additional check to include validity of carbon layers.

Source code in src/cplus_plugin/models/base.py
def is_valid(self) -> bool:
    """Additional check to include validity of carbon layers."""
    valid = super().is_valid()
    if not valid:
        return False

    carbon_valid = self.is_carbon_valid()
    if not carbon_valid:
        return False

    return True

PRIORITY_GROUP

Bases: Enum

Represents priority groups types

PriorityLayer dataclass

PriorityLayer(uuid, name, description, groups, selected=False, path='', type=PriorityLayerType.DEFAULT)

Bases: BaseModelComponent

Base class for model components storing priority weighting layers.

layer_uuid property

layer_uuid

Return Layer UUID for default layer.

Default layer's path will start with 'cplus://'.

Returns:

Type Description
str

Server Layer UUID

__eq__

__eq__(other)

Uses BaseModelComponent equality test rather than what the dataclass default implementation will provide.

Source code in src/cplus_plugin/models/base.py
def __eq__(self, other) -> bool:
    """Uses BaseModelComponent equality test rather than
    what the dataclass default implementation will provide.
    """
    if self.layer_uuid:
        return self.layer_uuid == other.layer_uuid
    return super().__eq__(other)

is_default_layer

is_default_layer()

Check if layer is a default layer

Returns:

Type Description
bool

True if layer comes from server API

Source code in src/cplus_plugin/models/base.py
def is_default_layer(self) -> bool:
    """Check if layer is a default layer

    :return: True if layer comes from server API
    :rtype: bool
    """
    return self.layer_uuid is not None

PriorityLayerType

Bases: IntEnum

Type of priority weighting layer.

Scenario dataclass

Scenario(uuid, name, description, extent, activities, weighted_activities, priority_layer_groups, state=ScenarioState.IDLE, server_uuid=None)

Bases: BaseModelComponent

Object for the handling workflow scenario information.

ScenarioResult dataclass

ScenarioResult(scenario, created_date=datetime.datetime.now(), analysis_output=None, output_layer_name='', scenario_directory='')

Scenario result details.

ScenarioState

Bases: Enum

Defines scenario analysis process states

SpatialExtent dataclass

SpatialExtent(bbox)

Extent object that stores the coordinates of the area of interest


Last update: October 2, 2024
Back to top