Sparse Block Models

Important:   Sparse block models are primarily used by Maptek Evolution. Support for it may be incomplete in other applications.

In a sparse block model, all of the blocks are the same size, similar to a dense block model, however unlike a dense block model a sparse model can contain ‘holes’ in the three dimensional grid structure where there are no blocks. Block properties are not stored for these holes, which allows for more efficiently storing data that contains a large amount of holes.

Creating a sparse block model

When a SparseBlockModel is created, it is empty and contains no blocks. Before the model can be saved, blocks must be added to the model. This is done by assigning to the SparseBlockModel.block_indices property. The SparseBlockModel.block_indices property contains one row for each existent block in the model. Each row is of the form:

[slice, row, column]

which indicates that the sparse block model contains a block in the specified slice, row and column.

This is demonstrated by the following script:

from mapteksdk.project import Project
from mapteksdk.data import SparseBlockModel
from mapteksdk.operations import open_new_view

if __name__ == "__main__":
    with Project() as project:
        with project.new("block models/new_sparse_model", SparseBlockModel(
                row_count=3, slice_count=3, col_count=3,
                row_res=1.5, col_res=1.25, slice_res=1.75
                )) as model:
            model.block_indices = [
                [0, 0, 0], [2, 0, 0], [0, 2, 0],
                [0, 0, 2], [2, 2, 0], [0, 2, 2],
                [2, 0, 2], [2, 2, 2], [1, 1, 1]
            ]
            # Nine block indices means there are nine blocks so nine block
            # colours are required.
            model.block_colours = [
                [255, 165, 0, 255], [255, 165, 0, 255], [255, 165, 0, 255],
                [255, 165, 0, 255], [255, 165, 0, 255], [255, 165, 0, 255],
                [255, 165, 0, 255], [255, 165, 0, 255], [255, 0, 0, 255]
            ]
        open_new_view([model.id])
		

This creates the following block model:

Note
  • The constructor parameters are the same for dense, sparse and subblocked block models.

  • The block count of a sparse block model is defined by the length of the block indices array.

  • If the above script used the same constructor parameters to create a dense block model, then the block model would contain row_count * col_count * slice_count = 3 * 3 * 3 = 27 blocks. The sparse block model instead contains nine blocks, with the remaining sixteen blocks not stored.

  • Because the sparse block model above only contains nine blocks, only nine colours are required to colour every block, instead of the 27 colours that would be required to colour an equivalent dense block model.

  • Blocks added to SparseBlockModel.block_indices that are outside of the model (i.e. their slice, row, or column are greater than or equal to the slice, row or column count) are silently removed when the block model is closed.

Adding blocks to an existing sparse block model

Adding blocks to an existing SparseBlockModel can be done using numpy.row_stack(), as demonstrated by the following examples:

Note:  This example assumes you have already run the example in Creating a sparse block model. Make sure to run that script first.

from mapteksdk.project import Project
from mapteksdk.data import SparseBlockModel
import numpy as np

def append_blocks_to_sparse_model(
        model: SparseBlockModel, new_indices: np.ndarray):
    """Append blocks to a SparseBlockModel.

    Parameters
    ----------
    model
        Open sparse block model to append blocks to.
    new_indices
        Numpy array of shape (N, 3) containing the block indices to add to
        the model.

    Raises
    ------
    ReadOnlyError
        If model was opened with Project.read().
    ObjectClosedError
        If model is closed.
    ValueError
        If new_indices is the wrong shape or does not contain floats.
    """
    model.block_indices = np.row_stack((
        model.block_indices,
        new_indices
    ))

if __name__ == "__main__":
    with Project() as project:
        with project.edit("block models/new_sparse_model", SparseBlockModel
                ) as edit_model:
            new_block_indices = [[1, 1, 0], [1, 1, 2]]
            append_blocks_to_sparse_model(edit_model, new_block_indices)

This adds two blocks to the sparse block model, displayed in green in the following screenshot:

Note
  • The row_stack() function added the new blocks at the end of the SparseBlockModel.block_indices array. This ensures that the existing blocks are still coloured the same as before the new blocks were added. New blocks should always be added to the end of the array.

  • The new blocks are coloured green because that is the default colour. They could be coloured differently by assigning different colours into indices nine and ten of the block colours array after the new blocks have been added.

  • Unlike a DenseBlockModel, the order of the blocks in a SparseBlockModel is not related to the topology of the model. Adjacent blocks are often not stored adjacent to each other.

  • Running the above script multiple times will add duplicate blocks to the model—two blocks that have the same block indices. Caution should be used to avoid adding duplicate blocks when adding new blocks into the model.

Removing blocks from an existing sparse block model

Blocks can be removed from a SparseBlockModel through the SparseBlockModel.remove_block() function, as demonstrated by the following script:

Note:  This example assumes you have already run both the example scripts presented in Creating a sparse block model and Adding blocks to an existing sparse block model. Be sure to run those first.

from mapteksdk.project import Project
from mapteksdk.data import SparseBlockModel

if __name__ == "__main__":
    with Project() as project:
    with project.edit("block models/new_sparse_model", SparseBlockModel
            ) as edit_model:
        edit_model.remove_block(3)

This results in the following block model:

Note

Checking for duplicate blocks in a sparse block model

Sparse block models can contain duplicate blocks—two blocks that exist in the same row, column and slice and completely overlap with each other. These duplicate blocks can cause difficult to diagnose glitches in algorithms. For performance reasons, the Python SDK does not check for duplicate blocks. The following script allows the user to pick a SparseBlockModel and the script reports any duplicate blocks the model contains.

from mapteksdk.project import Project
from mapteksdk.data import SparseBlockModel
from mapteksdk.operations import object_pick, write_report
import numpy as np

def check_for_duplicate_blocks(sparse_model: SparseBlockModel) -> bool:
    """Check if a SparseBlockModel contains duplicate blocks.

    Parameters
    ----------
    sparse_model
        The open sparse block model to check for duplicate blocks.

    Returns
    -------
    bool
        True if the sparse block model contains duplicate blocks.
        False if it does not.
    """
    # If the number of unique block indices is equal to the block count,
    # then there are no duplicate blocks.
    return np.unique(
        sparse_model.block_indices, axis=0).shape[0] != model.block_count

if __name__ == "__main__":
    with Project() as project:
        block_model_id = object_pick(
            object_types=SparseBlockModel,
            label="Pick a sparse block model to check for duplicate blocks."
        )

    title = f"{block_model_id.path} Duplicate Blocks"

    with project.edit(block_model_id, SparseBlockModel) as model:
        has_duplicate_blocks = check_for_duplicate_blocks(model)

    write_report(
        title,
        str(has_duplicate_blocks)
    )
Note
  • Checking for duplicate blocks in very large block models can take a prohibitively long time.

  • The above script could be modified to use SubblockedBlockModel.remove_block() to remove the duplicate blocks from the model.