Introduction

This section outlines the general usage of the Python bindings for a Cloud.

Reading and Writing of Clouds uses Numpy arrays so numpy must be installed for the version of Python being used.

All the examples assume the import has been done.

from caris.coverage import *
import numpy

Checking the type of a dataset

You can use caris.coverage.identify() to determine the type of dataset a file contains.

from caris.coverage import *

file_path = r'C:\bathymetry.csar'
type = identify(file_path)

if type == DatasetType.RASTER:
    raster = Raster(file_path)

elif type == DatasetType.CLOUD:
    cloud = Cloud(file_path)

elif type == DatasetType.VRS:
    vrs = VRS(file_path)

Opening a Cloud

cloud = Cloud('source_cloud.csar')

One can specify the named parameter.

cloud = Cloud(filename='source_cloud.csar')

One can also supply a URI

cloud = Cloud(uri='file:///source_cloud.csar')

Alternatively, the caris.open() function can be used:

from caris.coverage import *
import caris

# open file read only
cloud = caris.open(file_name='source_cloud.csar')

# open file read write
cloud = caris.open(file_name='source_cloud.csar', open_mode=caris.OpenMode.READ_WRITE)

Opening from BDB Server

To open a database surface, a URI must be given formatted as:

bdb://username:password@hostname/database/boid

Note

The database name is case sensitive.

Boids (object IDs for database objects) can be found by via caris.bathy.db.DatabaseFeature.id.

In BASE Editor the boid can be found by selecting a surfac, the boid will be shown in the Selection window. The URI for opened rasters or clouds is shown in the Properties window’s “Surface Name” field.

cloud = caris.coverage.Cloud(uri='bdb://dba:sql@example.com/MyDB/02000001')

Listing Bands

The band information is found in the Cloud.band_info property. It is simply a dictionary where the key is the band name and the value is an instance of BandInfo. The band information contains properties such as its type, category, minimum, maximum, no-data-value, etc. See BandInfo for more info.

cloud = Cloud('source_cloud.csar')

for band_name in cloud.band_info:
    print(str(cloud.band_info[band_name]))

Reading from a Cloud

Reading from a point cloud is done in a three step process. The first step is iterating over the cloud’s BlockIterator. This will give us a block of points for each band. Next, we iterate over these bands. Each band will give us a numpy array of points. Finally, we iterate over each point in each numpy array (for each band). That means three possible iterations. Iterate over blocks, iterate over bands and iterate over points in a numpy array..

Here is an example where we decided we wanted to read an entire band before moving on to the next. This will usually be slower than doing the iteration in the order mentioned above, but can make the code simpler to follow if you need to deal with an entire band before dealing with the next one.

cloud = Cloud('source_cloud.csar')

points = {}

# Read the points
for band_name in cloud.band_info:
    points[band_name] = []
    for block in cloud:
        for pt in block[band_name]:
            points[band_name].append(pt)

Querying a Cloud

A filter can be applied when reading from a Cloud with it’s member function Cloud.query. It can be filtered by band and/or a geographical box.

cloud = Cloud('cloud.csar')

# filter by bands
bands = [ 'XYZ', 'Source_Name']
for block in cloud.query(bands = bands):
    xyz_data = block['XYZ']
    source_name_data = block['Source_Name']
    # do something

# filter by box
query_box = ((3.841061, 51.344868), (3.844457, 51.346305))
for block in cloud.query(box = query_box):
    for band in block:
        for pt in block[band]:
            # do something

# filter by both
for block in cloud.query(bands = bands, box = query_box):
    xyz_data = block['XYZ']
    source_name_data = block['Source_Name']
    # do something

Copying a Cloud

The simplest way to copy a cloud is to call it’s member function Cloud.create_copy.

cloud = Cloud('source_cloud.csar')
outputFilename = 'dest_cloud.csar'
cloud.create_copy(outputFileName)

Another way of doing it is opening a Cloud and copying all the required info into an Options instance.

srcPC = Cloud('source_cloud.csar')

opts = Options()
opts.open_type = OpenType.WRITE
opts.position_band_name = srcPC.position_band_name
opts.band_info = srcPC.band_info
opts.extents = srcPC.extents
opts.wkt_cosys = srcPC.wkt_cosys
opts.iterator = lambda: iter(srcPC)

dstPC = Cloud(filename = 'cloud_copy.csar', options=opts)

Creating a Cloud

The caris.coverage.Options.iterator attribute can also be used to create Clouds without copying from an existing Cloud. This attribute is expected to be a function object that returns an iterator whose next() returns a map of band names to a list of it’s points (similar to a BlockIterator).

eg: Options.iterator -> Iterable[dict[str, list[Any]]]

where str is the name of the band, and Any is the point data. Each iteration represents a block of point data.

bandInfo = {} # Define our bands below
bandInfo['Height'] = BandInfo(type = DataType.FLOAT64, tuple_length = 1, name = 'Height', direction = Direction.HEIGHT, units = 'm', category = Category.SCALAR, ndv = -1.0)
bandInfo['Position'] = BandInfo(type = DataType.FLOAT64, tuple_length = 3, name = 'Position', direction = Direction.NAP, units = '', category = Category.SCALAR, ndv = (-1.0, -1.0, 0.0))

opts = Options()
opts.open_type = OpenType.WRITE
opts.position_band_name = 'Position'
opts.band_info = bandInfo
opts.extents = ( (0.0,0.0,0.0), (10.0, 10.0, 10.0) )
opts.wkt_cosys = 'PROJCS["WGS 84 / World Mercator",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,' \
'AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],' \
'UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],' \
'UNIT["metre",1,AUTHORITY["EPSG","9001"]],' \
'PROJECTION["Mercator_1SP"],' \
'PARAMETER["central_meridian",0],' \
'PARAMETER["scale_factor",1],' \
'PARAMETER["false_easting",0],' \
'PARAMETER["false_northing",0],' \
'AUTHORITY["EPSG","3395"],' \
'AXIS["Easting",EAST],AXIS["Northing",NORTH]]'

# Create data for iterator
band1_data = [1.0, 2.0, 3.0, 4.0]
band2_data = [ (0.0, 0.0, 0.0), (5.0, 10.0, 0.0), (10.0, 5.0, 0.0), (10.0, 10.0, 0.0) ]
blocks = [ { 'Height': band1_data, 'Position': band2_data } ]

opts.iterator = lambda: iter(blocks)
pc = Cloud(filename = 'cloud.csar', options=opts)

Updating Cloud data

Cloud data can be updated while iterating through the blocks of data. This can be done by calling BlockIterator.write during iteration. The numpy array that is passed to it must have the appropriate shape and data type for the band and block being updated.

Note

New data cannot be inserted into an existing Cloud and the position band cannot be updated.

opts = Options(open_type = OpenType.WRITE)
cloud = Cloud('cloud_to_update.csar', options = opts)

band_name = 'SomeBand'
bands = [band_name]

query_box = ((3.841061, 51.344868), (3.844457, 51.346305))
itr = cloud.query(bands = bands, box = query_box)

# update blocks
for block in itr:
    array = block[band_name]
    for i in range(0, array.shape[0]):
        array[i] = 1

    itr.write(band_name, array)

Updating bounding polygon

A bounding polygon can be automatically generated for a Cloud. Any valid polygon can be set to be used as the bounding polygon. Used together, a bounding polygon can be replaced with the automatically generated bounding polygon.

opts = Options()
opts.open_type = OpenType.WRITE

cloud = Cloud(file_path, options=opts)
new_bounding_polygon = generate_polygon(cloud)
cloud.bounding_polygon = new_bounding_polygon