Basic Usage
This page gives some common basic usage examples. For more detailed information, see the documentation for the SDKs and the REST reference.
Setup
If you have an existing Python environment set up, you can add fathom-global-client-sdk
to your existing
requirements.txt file and install as normal.
To create a new virtualenv to use the Fathom Python module, proceed with the following steps.
# ensure you have the right requirements
python3 -m ensurepip
# Go to your project directory
cd /path/to/working/dir/
# Create a virtualenv in the 'venv' folder and activate it
python3 -m venv venv
source venv/bin/activate
# Install the package from pypi into the virtualenv
pip3 install fathom-global-client-sdk
The suggested way to interact with the Fathom API on the command line is using curl.
Authentication
Authentication tokens are required for API requests.
The SDK performs the obtaining of tokens for the user when creating a Client object. Just use the access credentials supplied by Fathom.
from fathom.sdk.v2 import Client
client = Client(
"client_id",
"client_secret",
)
# Future requests will be authenticated
To access data from the REST API endpoints you will need to authorize requests using a JWT token in the Authorization
header.
To be able to do this, you must obtain a token from our authorization server using your supplied client_id
and client_secret
.
curl --request POST \
--url https://api.fathom.global/v2/auth/token \
--header 'content-type: application/json' \
--data '{"client_id":"CLIENT_ID","client_secret":"CLIENT_SECRET"}'
You should obtain a response like:
{
"access_token": "access_token",
"expire_secs": 86400
}
When you have the Auth0 JWT token you will need to pass it as a header with every request to any of the Fathom API endpoints using the pattern:
Authorization: Bearer ACCESS_TOKEN
Data queries
Key Values
There are some integer values that are used to represent special cases in the data. These are:
Value | Title | Examples |
---|---|---|
-32768 | No Data | A point in the ocean or an inland point on a coastal layer |
-32767 | Permanent Water | A point just offshore or in a river |
Project ID metadata
In some cases you may require Fathom API queries to be billed as part of an internal project. The SDK allows you to attach a project ID as part of a query. These project IDs are stored in Fathom's usage database and will be used to itemise usage for billing purposes.
Methods to fetch data take an optional project_id
keyword argument that will pass the project ID along:
project_id = "large-engineering-project"
client.geo.get_points(([pt]), layer_ids, project_id)
Add the project id in a "metadata"
dictionary in the request body:
curl --location --request POST 'https://api.fathom.global/v2/points:data' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer ACCESS_TOKEN' \
--data-raw '{
"metadata": {"project_id": "large-engineering-project"},
"points": [
...
],
"layer_ids": {
...
}
}'
Point queries
To obtain values at specified points:
pts = [
point(lat=51.448765, lng=-2.601340),
point(lat=39.628574, lng=-105.1162333)
]
layer_ids = [
"US-1_3ARCSEC-00_OFFSET-1in1000-FLUVIAL-DEFENDED-DEPTH-2020-PERCENTILE50-v2.0.0",
"GLOBAL-1ARCSEC-NW_OFFSET-1in1000-FLUVIAL-UNDEFENDED-DEPTH-2020-PERCENTILE50-v3.0",
"GLOBAL-1ARCSEC-NW_OFFSET-1in100-FLUVIAL-UNDEFENDED-DEPTH-2020-PERCENTILE50-v3.0"
]
data = client.geo.get_points(pts, layer_ids)
To obtain values at specified points:
Request:
curl --location --request POST 'https://api.fathom.global/v2/points:data' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer ACCESS_TOKEN' \
--data-raw '{
"points": [
{
"longitude": -2.601340,
"latitude": 51.448765
},
{
"longitude": -105.1162333,
"latitude": 39.628574
}
],
"layer_ids": [
"US-1_3ARCSEC-00_OFFSET-1in1000-FLUVIAL-DEFENDED-DEPTH-2020-PERCENTILE50-v2.0.0",
"GLOBAL-1ARCSEC-NW_OFFSET-1in1000-FLUVIAL-UNDEFENDED-DEPTH-2020-PERCENTILE50-v3.0",
"GLOBAL-1ARCSEC-NW_OFFSET-1in100-FLUVIAL-UNDEFENDED-DEPTH-2020-PERCENTILE50-v3.0"
]
}'
Expected response:
{
"results": {
"US-1_3ARCSEC-00_OFFSET-1in1000-FLUVIAL-DEFENDED-DEPTH-2020-PERCENTILE50-v2.0.0": {
"values": [
{
"query_point": {
"longitude": -2.60134,
"latitude": 51.448765
},
"val": -32768
},
{
"query_point": {
"longitude": -105.1162333,
"latitude": 39.628574
},
"val": 0
}
]
},
"GLOBAL-1ARCSEC-NW_OFFSET-1in1000-FLUVIAL-UNDEFENDED-DEPTH-2020-PERCENTILE50-v3.0": {
...
}
}
}
Polygon queries
- Polygons may not contain holes.
- Polygons can either be simple_polygon objects or shapefiles.
- The polygon's points must be specified in counterclockwise order.
- The first and last points must be the same.
- Consecutive duplicate points are not supported.
- Polygons that cross the antimeridian are not supported. To retrieve data for this area, create separate polygons on either side of the antimeridian
To obtain data within an area defined by a polygon object:
layer_ids = [
"GLOBAL-1ARCSEC-NW_OFFSET-1in1000-FLUVIAL-UNDEFENDED-DEPTH-2020-PERCENTILE50-v3.0",
"US-1_3ARCSEC-00_OFFSET-1in1000-FLUVIAL-DEFENDED-DEPTH-2020-PERCENTILE50-v2.0.0"
]
poly = polygon(
[
point(51.45, 0),
point(51.55, 0),
point(51.55, -0.1),
point(51.45, -0.1),
point(51.45, 0),
]
)
results = client.geo.get_polygon(poly, layer_ids).results
# we return one tif per layer, this example saves the tif for each layer using the layer ID as the filename
for layer_id, data in results.items():
f = open(layer_id+".tif", "wb")
f.write(data.geo_tiff)
f.close()
Request:
curl --location --request POST 'https://api.fathom.global/v2/polygon:data' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer ACCESS_TOKEN' \
--data-raw '{
"layer_ids": [
"GLOBAL-1ARCSEC-NW_OFFSET-1in1000-PLUVIAL-DEFENDED-DEPTH-2020-PERCENTILE50-v3.0"
],
"polygon": {
"points": [
{
"latitude": 51.45,
"longitude": 0
},
{
"latitude": 51.55,
"longitude": 0
},
{
"latitude": 51.55,
"longitude": -0.1
},
{
"latitude": 51.45,
"longitude": -0.1
},
{
"latitude": 51.45,
"longitude": 0
}
]
}
}'
Response:
{
"results": {
"GLOBAL-1ARCSEC-NW_OFFSET-1in100-PLUVIAL-DEFENDED-DEPTH-2020-PERCENTILE50-v3.0": {
"geo_tiff": "SUkqAAgAAAASAA..."
}
}
}
The geo_tiff
element is a raw GeoTIFF file encoded as a base64-URL-encoded string. To write this to a file, first decode the base64 string then save the result to a file with a .tiff
or .tif
extension. An example for doing this in python is as follows:
encoded_string = response['results']['GLOBAL-1ARCSEC-NW_OFFSET-1in100-PLUVIAL-DEFENDED-DEPTH-2020-PERCENTILE50-v3.0']['geo_tiff']
encoded_bytes = encoded_string.encode('ascii')
decoded = base64.decodebytes(encoded_bytes)
with open(output_file_path, 'wb') as output_file:
output_file.write(decoded)
Polygon stats queries
Allows fetching statistical data about a polygonal area. We currently return:
- Mean
- Max
- Min
- Standard Deviation
Polygons submitted to the polygon stats endpoint must follow the same rules as polygon queries.
poly = polygon(
[
point(52.41, -1.52),
point(52.4, -1.52),
point(52.4, -1.5),
point(52.41, -1.5),
point(52.41, -1.52),
]
)
layer_ids = [
"US-1_3ARCSEC-00_OFFSET-1in1000-FLUVIAL-DEFENDED-DEPTH-2020-PERCENTILE50-v2.0.0",
"GLOBAL-1ARCSEC-NW_OFFSET-1in1000-FLUVIAL-UNDEFENDED-DEPTH-2020-PERCENTILE50-v3.0",
"GLOBAL-1ARCSEC-NW_OFFSET-1in100-FLUVIAL-UNDEFENDED-DEPTH-2020-PERCENTILE50-v3.0"
]
data = client.geo.get_polygon_stats(poly, layer_ids)
Request:
curl --location --request POST 'https://api.fathom.global/v2/polygon:stats' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer ACCESS_TOKEN' \
--data-raw '{
"polygon": {
"points": [
{
"latitude": 51.45,
"longitude": 0
},
{
"latitude": 51.55,
"longitude": 0
},
{
"latitude": 51.55,
"longitude": -0.1
},
{
"latitude": 51.45,
"longitude": -0.1
},
{
"latitude": 51.45,
"longitude": 0
}
]
},
"layer_ids": [
"US-1_3ARCSEC-00_OFFSET-1in1000-FLUVIAL-DEFENDED-DEPTH-2020-PERCENTILE50-v2.0.0",
"GLOBAL-1ARCSEC-NW_OFFSET-1in1000-FLUVIAL-UNDEFENDED-DEPTH-2020-PERCENTILE50-v3.0",
"GLOBAL-1ARCSEC-NW_OFFSET-1in100-FLUVIAL-UNDEFENDED-DEPTH-2020-PERCENTILE50-v3.0"
]
}'
Expected response:
{
"results": {
"US-1_3ARCSEC-00_OFFSET-1in1000-FLUVIAL-DEFENDED-DEPTH-2020-PERCENTILE50-v2.0.0": {
"mean": 5,
"max": 10,
"min": 1,
"stdDev": 2
},
"GLOBAL-1ARCSEC-NW_OFFSET-1in1000-FLUVIAL-UNDEFENDED-DEPTH-2020-PERCENTILE50-v3.0": {
...
}
}
}
GeoJSON queries
The GeoJSON endpoints as documented in the REST reference and the Python SDK reference only accepts a subset of GeoJSON. In short:
- Only 'Point', 'MultiPoint', and 'Polygon' are accepted
- 'LineString', 'MultiPolygon', and 'MultiLineString' are not accepted
- Inputs may be contained in a 'FeatureCollection' or 'Feature', but not 'GeometryCollection'
- 'Polygon' geometries must follow the same rules as the rules for other endpoints
A non-exhaustive set of examples of valid inputs:
{
"type": "Point",
"coordinates": [-1.1081944, 50.8162643]
}
{
"type": "MultiPoint",
"coordinates": [
[-1.1081944, 50.8162643],
[-1.1071944, 50.8163643]
]
}
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-1.1081944, 50.8162643]
},
"properties": {
"name": "Dinagat Islands"
}
}
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[100.0, 0.0],
[101.0, 0.0],
[101.0, 1.0],
[100.0, 1.0],
[100.0, 0.0]
]
]
}
}
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-1.1081944, 50.8162643]
},
"properties": {
"name": "Point 1"
}
},
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-1.1071944, 50.8163643]
},
"properties": {
"name": "Point 2"
}
}
]
}
Note
If a FeatureCollection of Point or MultiPoint (or a combination) is given, the results from the API will be flattened into one response object. For the above 'FeatureCollection of Point' example, this will result in:
{
"results": {
"FLOOD_MAP-...": {
"values": [
{
"query_point": {
"longitude": -1.1081944,
"latitude": 50.8162643
},
"val": 12
},
{
"query_point": {
"longitude": -1.1071944,
"latitude": 50.8163643
},
"val": 56
}
]
}
}
}
Some examples of invalid inputs:
Failure
LineString geometries are not allowed.
{
"type": "Feature",
"geometry": {
"type": "LineString",
"coordinates": [
[102.0, 0.0],
[103.0, 1.0],
[104.0, 0.0],
[105.0, 1.0]
]
}
}
Failure
Polygons with multiple rings are not allowed.
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[100.0, 0.0],
[101.0, 0.0],
[101.0, 1.0],
[100.0, 1.0],
[100.0, 0.0]
],
[
[100.0, 0.0],
[101.0, 0.0],
[101.0, 1.0],
[100.0, 1.0],
[100.0, 0.0]
]
]
}
}
Failure
Only one polygon per request is allowed.
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[100.0, 0.0],
[101.0, 0.0],
[101.0, 1.0],
[100.0, 1.0],
[100.0, 0.0]
]
]
}
},
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[100.0, 0.0],
[101.0, 0.0],
[101.0, 1.0],
[100.0, 1.0],
[100.0, 0.0]
]
]
}
}
]
}
Vector file queries
The Python SDK supports reading polygons from a variety of vector files (see the SDK documentation for more information.)