Introduction

This section outlines the general usage of the BDB Server module.

Connect to a BDB Server

The following will create a simple connection to a running BDB Server (Node Manager):

1
2
3
4
5
6
7
from caris.bathy.db import *

user = 'dba'
passwd = 'sql'
host = 'localhost'

nm = NodeManager(user, passwd, host)

Retrieve a list of databases

The following will retrieve and display a list of databases registered on the server, and the state of each database (started/stopped):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from caris.bathy.db import *

user = 'dba'
passwd = 'sql'
host = 'localhost'

nm = NodeManager(user, passwd, host)

for db in nm.databases:
    print(db + ' (' + str(nm.get_database_state(db)) + ')')

Query a database

A query is applied to a running database and returns a list of object IDs which meet that query criteria. These object IDs can be used to retrieve the associated database object to perform actions on it.

Simple query

The most basic database query retrieves all objects of a chosen type from a running database:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
from caris.bathy.db import *

user = 'dba'
passwd = 'sql'
host = 'localhost'

nm = NodeManager(user, passwd, host)
db = nm.get_database('Global_DB')

query = ObjectQuery(QueryType.SURFACE)
boidList = db.query_objects(query)

for boid in boidList:
    surface = db.get_surface(boid)
    print(surface.id)# print surface id

The QueryType can be SURFACE, PRODUCT_SURFACE or SURVEY, each type returning a different set of objects (surfaces, product surfaces and surveys respectively).

Query by attribute

A query condition can be added to filter for attributes with specific values:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from caris.bathy.db import *

user = 'dba'
passwd = 'sql'
host = 'localhost'

nm = NodeManager(user, passwd, host)
db = nm.get_database('Global_DB')

condition = QueryCondition()
condition.acronym = 'OBJNAM'
condition.operation = ConditionType.EQUAL_TO
condition.value = 'my_surface'

rule = QueryRule(RuleType.INCLUDE, RuleLogic.AND)
rule.add_condition(condition)

query = ObjectQuery(QueryType.SURFACE)
query.add_rule(rule)

boidList = db.query_objects(query)

for boid in boidList:
    surface = db.get_surface(boid)
    print(surface.id) # print surface id

Multiple conditions can be added to a rule, and multiple rules can be added to a query.

Query by polygon

A Database can also be queried by polygon:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from caris.bathy.db import *

user = 'dba'
passwd = 'sql'
host = 'localhost'

nm = NodeManager(user, passwd, host)
db = nm.get_database('Global_DB')

polygon = 'POLYGON((-004.153110200 50.333355300,
-004.147552500 50.333200600,
-004.147310900 50.326781500,
-004.153714300 50.327400300,
-004.153110200 50.333355300))'

query = ObjectQuery(QueryType.SURFACE)
query.geometry = polygon

boidList = db.query_objects(query)

for boid in boidList:
    surface = db.get_surface(boid)
    print(surface.id) # print surface id

Note

Any object which intersects the query geometry will be returned, not just objects that fall within the geometry.

Described geometry must be defined in the Well-known text markup language (http://en.wikipedia.org/wiki/Well-known_text), and the object type must be a (closed) Polygon. The coordinates must be provided in Geographic longitude, latitude pairs on the WGS 84 ellipsoid. The Attribute and Polygon queries can be combined into a complex query.

Journal query

The Database contains a journaling system that records information about changes made to the data. This system can tell us when data was created, modified, or deleted. In addition to recording what data was modified, it also records who made the change and when they did it.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
from caris.bathy.db import *
from datetime import *

user = 'dba'
passwd = 'sql'
host = 'localhost'

nm = NodeManager(user, passwd, host)
db = nm.get_database('Global_DB')

operationTypes = [OperationType.CREATE, OperationType.EDIT]
modifiedBeforeToday = db.query_journal(operationTypes, Operator.LESS, datetime.utcnow())

for entry in modifiedBeforeToday:
    print(entry.identifier, str(entry.operation), str(entry.time), entry.userName)

Metadata

Metadata on objects in the database can be retrieved, added, modified and removed, providing the login credentials used to connect to the server have permission for these actions. Below is an example of the command used to retrieve metadata:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
from caris.bathy.db import *

user = 'dba'
passwd = 'sql'
host = 'localhost'

nm = NodeManager(user, passwd, host)
db = nm.get_database('Global_DB')

query = ObjectQuery(QueryType.SURFACE)
boidList = db.query_objects(query)

for boid in boidList:
    surface = db.get_surface(boid)
    metadata = surface.get_metadata()
    for attribute in metadata:
        print(attribute)

Metadata from a Surface object is returned as an attribute list. Attribute values can be read, assigned or replaced within this list, and uploaded to the database:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
...


surface = db.get_surface(boid)
metadata = surface.get_metadata()

# retrieve metadata
print(metadata['OBJNAM'])

# display the object name
metadata['INFORM'] = 'Test'

# set an attribute value
metadata['DUNITS'] = 'metres'

surface.set_metadata(metadata) # upload the new metadata


...

Note

For enumerated lists, these attributes can be described by either list number or list text. In the example above, DUNITS has a list value of 1, which corresponds to the description, metres. When retrieving metadata, the attribute will report a value of metres. When assigning a value to DUNITS, either 1 or metres can be used.

1
2
3
4
5
6
7
8
9
...


#these statements are equivalent
metadata['DUNITS'] = 'metres'
metadata['DUNITS'] = '1'


...

Metadata formatted as ISO 19115-compliant XML can also be retrieved:

1
2
3
4
5
6
7
8
...


surface = db.get_surface(boid)
print(surface.iso_metadata)


...

Working with data

For each surface and product surface object in a database, a CSAR file can be downloaded containing the stored bathymetry, in either grid or point cloud format. This holds true regardless of the original source format when the data was loaded into the Server as the data is converted and stored in the CSAR format.

Download a Surface

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from caris.bathy.db import *

user = 'dba'
passwd = 'sql'
host = 'localhost'

nm = NodeManager(user, passwd, host)
db = nm.get_database('Global_DB')

surface = db.get_surface(3) # BOID 02000003
surface.download_bathymetry('C:/my_surface.csar')

Replace a Surface

The data stored in a Surface object can be replaced at any time. Any change to the stored bathymetry will cause an automatic update to the boundary that represents the surface object, ensuring it coinsides with the current bathymetric data.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from caris.bathy.db import *

user = 'dba'
passwd = 'sql'
host = 'localhost'

nm = NodeManager(user, passwd, host)
db = nm.get_database('Global_DB')

surface = db.get_surface(3) # BOID 02000003
surface.upload_bathymetry('C:/my_surface.csar')

Upload a new Surface

The upload surface operation resembles a replace bathymetry command, with an additional step of creating the database object and setting the default metadata.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
from caris.bathy.db import *

user = 'dba'
passwd = 'sql'
host = 'localhost'

nm = NodeManager(user, passwd, host)
db = nm.get_database('Global_DB')

dummy_polygon = 'POLYGON((0 0,0 1,1 1,1 0,0 0))'

surface = db.add_surface(dummy_polygon)
surface.upload_bathymetry('C:/my_surface.csar')

metadata = surface.get_metadata()
metadata['OBJNAM'] = 'my_surface'
surface.set_metadata(metadata)

The first step is to create a new database object to store the bathymetry. To add a new object, geometry must be provided for this object when it is created. If the upload fails or no upload is performed, this geometry will be representative of the object in the database. In this instance, a simple 1-pixel polygon is used temporarily. A better practice is to use a minimum bounding rectangle (MBR), which is the behaviour found in BASE Editor when uploading a new surface object.

After creating the new Database object, CSAR (or other supported format) data can be uploaded to the object. Once complete, metadata for this object can optionally be set.

Note

Through the Python interface, only the system attributes of an object are defined. All other attributes, including OBJNAM, must be defined by the user when a new object is created. The standard practice for defining OBJNAM in BASE Editor is to use the filename without the file extension, as shown in the above example.

Working with Attachments

A Surface object can have any number of attachments, which are stored in the file system on the BDB Server node location (potentially separate from the back-end database, PostgreSQL or Oracle). Attachments can be accessed directly from a surface object returned from a database.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from caris.bathy.db import *

user = 'dba'
passwd = 'sql'
host = 'localhost'

nm = NodeManager(user, passwd, host)
db = nm.get_database('Global_DB')

surface = db.get_surface(3) # BOID 02000003

if surface.attachments:
    for attachment in surface.attachments:
        print(attachment.name + ' ' + attachment.size)

Attachments can be downloaded and uploaded in the same manner as bathymetry.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from caris.bathy.db import *

user = 'dba'
passwd = 'sql'
host = 'localhost'

nm = NodeManager(user, passwd, host)
db = nm.get_database('Global_DB')

surface = db.get_surface(3) # BOID 02000003
surface.upload_attachment('C:/my_document.pdf')
surface.download_attachment('my_document.pdf', 'D:/')

Examining the Catalog

The Database's Catalog can be retrieved and examined to view objects, attributes, expected values, etc.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
from caris.bathy.db import *

user = 'dba'
passwd = 'sql'
host = 'localhost'

nm = NodeManager(user, passwd, host)
db = nm.get_database('Global_DB')

catalog = db.get_catalog()

for object in catalog.get_objects():

    for attribute in object.get_attributes():
        print(attribute.name)

        for value in attribute.get_expected_values():
            print("{0} - {1}".format(value.identifier, value.description))

Error handling

Any errors returned from BDB are passed to Python as RunTimeError exceptions, which can be intercepted using Python syntax. The returned exception will contain the error code and description from BDB. In the following example, this error number and description is replaced with a simplified description of the problem when reported to the user:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from caris.bathy.db import *

user = 'dba'
passwd = 'sql'
host = 'localhost'

a_server = None

try:
    #connect to node
    a_server = NodeManager(user, passwd, host)
    
except RuntimeError as e:
    if "Error code = 30" in str(e):
        sys.exit("Error: Login information incorrect or account disabled.")
    else:
        sys.exit(str(e))
        
db = None

try:
    db = a_server.get_database('Global_DB')
    
except RuntimeError as e:
    if "Error code = 21" in str(e):
        sys.exit("Error: Database does not exist")
    elif "Error code = 29" in str(e):
        sys.exit("Error: Database not started.")
    else:
        sys.exit(str(e))