Quickstart

The intended and most convenient way of running Kalithea jobs with Perion is by using the web service ApoPerion. It is (by default) accessible at the localhost at port 5000: 127.0.0.1:5000. ApoPerion is a kind of ReST-API and communicates in JSON. For processing larger amounts of requests or recurrent tasks it might be helpful to write a short script to handle those in- and outputs. An example in Python is given below.

Note

Further on in this guide, the paradigmatic HTTP requests to communicate with ApoPerion are sent via the curl command-line tool. On Windows, it seems to be necessary to use double quotation marks – and only those – with curl. Hence, in strings containing double quotes (e.g. JSON data) they need to be escaped: \".

Creating and Removing Jobs

ApoPerion stores all user-sent information and visibility outputs generated by Kalithea. To keep the information belonging to a certain task together, a unique token needs to be created. Via this token the data can be accessed and updated.

Creating a Job

To generate a token – and with it a new job – send the command new to the service:

$ curl "127.0.0.1:5000/new"
{"token":"b2afc4dc-bf91-4899-8654-edc6f48f47ba"}

It responds with the newly generated token, which is needed for all further operations concerning the job.

Note

It is not necessary to put the URL for the curl command in quotation marks. But it helps later on when it comes to more complex URLs containing special charaters as ? and &.

Removing a Job

If all calculations are done and all data is fetched, make sure to remove the created job with the remove command followed by the token to free the occupied memory.

Warning

Keep in mind that removal of a job cannot be undone! Make sure that all generated data is retrieved before removing a job.

$ curl "127.0.0.1:5000/b2afc4dc-bf91-4899-8654-edc6f48f47ba/remove"
{"status":200}

Uploading Layout and Emitter Information

Note

Instead of uploading model, DEM and emitter information separately, it is possible to give URLs to those files in the run parameters file (see All-in-one Run File). That way, only one input file has to be provided.

Kalithea calculates the visibility based on a 3D model. This model needs to be provided by the user either in form of an STL file (ASCII or binary) or as an area layout given in GeoJSON format. The detailed specifications of the GeoJSON elements that can or need to be used are documented in ABM Layout Specifications.

Repeated upload of an STL file or a GeoJSON layout to the same job (same token) overwrites a possibly already existing one. Since the upload of a layout triggers the creation of an STL file, it is not necessary to provide both. Each job can only have a single 3D model regardless in which format it was provided. If both a GeoJSON layout and a detailed STL model are available, it is advisable to first upload the GeoJSON file and then the STL file. This way, the outer area boundaries of the layout are available to Perion and the detailed 3D model can still be used. Note, however, that it is the user’s responsibility to correctly align the coordinates of the layout and the STL model.

STL files do not store meta data such as the coordinate reference systems (CRS). The coordinates in the STL file should be given in a metric, rectilinear CRS. For later transformation of the obtained results in the target CRS, the city model’s CRS can be specified in the run parameters.

Warning

The user has to make sure, that all uploaded files containing georeferenced data will be stored using the same coordinate reference system. A common reason for not getting the expected results is a mismatch in the object’s positions (e.g. emitters lying outside the 3D model’s boundaries).

STL Upload

To upload an STL model, send the respective data as multipart/form-data (curl -F) with the parameter stl to Host:Port/<token>/stl. If the data exists in a file (which is most likely the case), precede the file name with an @ sign.

$ curl -F stl="@path/to/model.stl" "127.0.0.1:5000/b2afc4dc-bf91-4899-8654-edc6f48f47ba/stl"
{"status":200}

Successful upload is acknowledged with return status 200.

Layout Upload

GeoJSON layout data can be sent to the service via the POST method. The receiving address has the form Host:Port/<token>/layout.

$ curl -X POST -H "Content-Type: application/json" -d "@path/to/file.geojson" \
"127.0.0.1:5000/b2afc4dc-bf91-4899-8654-edc6f48f47ba/layout"
{"status":200}

Perion will create a 3D model from the layout data and save it in STL format. The extrusion height of the model can be controlled with the key "extrusion-height" followed by a float value added to the topmost object in the uploaded GeoJSON data. If the key:value pair "extrusion-at": [x, y] (where x and y are of type float) is added, the centroid of the layout is moved to x,y and the extrusion is performed there. This is helpful to reduce numerical instabilities in the coordinates. In the latter case, the origial position of the layout’s centroid is returned after successful processing.

$ curl -X POST -H "Content-Type: application/json" -d "{\"type\": \"FeatureCollection\", \
\"features\": [...], \"extrusion-height\": 5.2, \"extrusion-at\": [0, 0]}" \
"127.0.0.1:5000/b2afc4dc-bf91-4899-8654-edc6f48f47ba/layout"
{"centroid":[..., ...],"status":200}

The key "target-crs" determines the CRS of the created 3D model (e.g. as EPSG code). Omitting this key keeps the CRS given in the layout’s feature collection. In case of doubt, EPSG 3857 ("target-crs": 3857) should be a good choice here.

Emitter Upload

Note

Emitters only need to be provided if the perceptibility of warning devices is to be calculated. For visibility/audibility fields, it is sufficient to upload a layout or STL model.

Analogously to the layout, JSON or GeoJSON emitter data can be sent to the service via the POST method. The receiving address prototype is Host:Port/<token>/emitter. There are two main ways to format the data. First, a JSON list can be given, which contains a list of ten floats for each emitter. Those ten numbers specify the coordinates (1-3), the direction (4-6), the width and height (7, 8), the opening angle in degree (9) and the itensity/power (10) of the warning device.

The coordinates in this list will be used as given and have to match the 3D model. This is the user’s responsibility.

$ curl -X POST -H "Content-Type: application/json" -d "[[182,8,2,0,1,0,1.2,3.2,72.5,550.0], ...]" \
"127.0.0.1:5000/b2afc4dc-bf91-4899-8654-edc6f48f47ba/emitter"
{"status":200}

Second, a GeoJSON FeatureColletion containing Points can be provided. Each warning device is represented by two points. One for its position and one for its view direction. Dimensions and other properties are retrieved from the Warning Device Data Base using the "name" and "type" fields of the position points as selector. The section Warning devices exemplifies such a feature colletion.

To change the CRS, the key "target-crs" can be given on the topmost level of the GeoJSON data just as for the layout. If the key is omitted, the CRS of the 3D model will be used when available. Otherwise the CRS will be kept as given.

$ curl -X POST -H "Content-Type: application/json" -d "@warning_devices.geojson" \
"127.0.0.1:5000/b2afc4dc-bf91-4899-8654-edc6f48f47ba/emitter"
{"status":200}

Running Calculations

Once the necessary data are successfully uploaded, the simulation can be started sending the run parameters as a JSON object to Host:Port/<token>/run.

$ curl -X POST -H "Content-Type: application/json" -d "{\"general\": ..., \"perion\": ...}" \
"127.0.0.1:5000/b2afc4dc-bf91-4899-8654-edc6f48f47ba/run"
{"status":200}

Some of the run parameters are mandatory, other are optional. The run paramters are passed on to Kalithea except for the (optional) "perion" object. A detailed overview of all available parameters is given here.

Below is a minimal example for a JSON run parameter object. It contains only the mandatory fields. For all other parameters the default settings are used.

{
        "general": {
                "generator": "cpu",
                "type": "visrad"
        },
        "analysis": {
                "cell-width": 1.0,
                "evaluation-height": 2.0,
                "max-visibility": 400.0,
                "angle-resolution": 360.0
        }
}

The calculation itself runs in a separat process. Its status is available via Host:Port/<token>/status.

$ curl "127.0.0.1:5000/b2afc4dc-bf91-4899-8654-edc6f48f47ba/status"
{"kalithea-status":"running","progress":0.0,"stdout":["Creating Scenario"],"stderr":[]}

As soon as Kalithea has ended, the job status changes to "finished". Then, the results can be queried.

All-in-one Run File

To reduce the number of file uploads for one-shot jobs, it is possible to provide URLs for STL, DEM and/or emitter files in an additional "scenario" object in the run parameters. URLs have to be given in the form http://host:port/path/to/file.ext. Indication of the port is optional. The recognized keys in the "scenario" object are "city-model" (STL file), "elevation-model" (DEM file) and "emitter-file" (GeoJSON file).

{
        "general": {
                ...
        },
        "scenario": {
                "city-model": "http://localhost:5000/my_data/mesh.stl",
                "elevation-model": "http://www.some-domain.com/elevations/DEM.xyz",
                "emitter-file": "http://127.0.0.1:5000/project/emitters.geojson"
        }
        "analysis": {
                ...
        }
}

Retrieving Results

All produced outputs of finalized jobs can be requested at Host:Port/<token>/results. For this URL two optional GET arguments are available. The format argument defines the output format of the retrieved data. Allowed values are one of the plain text formats geojson, geojson-grid, geojson-abm, and csv, or one of the output types defined by the user in the "output-types" object of the Kalithea run parameters (i.e., png, vtk, or blob).

The id argument requsts download of a specific data set. It is only meaningful for perceptibility calculations of warning devices. Each warning device is assigned an ID, either automatically (ascending integers starting with 0) or according to the feature IDs in the feature collection defining the device positions and directions (see Warning devices). If a job comprises several perceptibility simulation and no ID is specified, all results are returned.

When using the curl command-line tool, it is advisable to add the -o option to redirect the respons to a file.

Note

When special characters like the question mark (?) or ampersand (&) are part of the URL, it is necessary to write it in double quotes (at least on Windows systems).

$ curl "127.0.0.1:5000/b2afc4dc-bf91-4899-8654-edc6f48f47ba/results?format=geojson" \
-o visibility_field.geojson
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 8602k  100 8602k    0     0   748k      0  0:00:11  0:00:11 --:--:-- 2026k
$ curl "127.0.0.1:5000/b2afc4dc-bf91-4899-8654-edc6f48f47ba/results?format=geojson-abm&id=2" \
-o display2_visibility.geojson
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 2671k  100 2671k    0     0   754k      0  0:00:03  0:00:03 --:--:--  754k

Job Management via Python Script

The following Python snippet exemplifies the communication with the ApoPerion API using the apopyrion module. At the moment, the module is only available from the Docker container (/perion/apopyrion.py).

import asyncio
from apopyrion.client import ApoperionJob, ApoperionJobError

stl_file = "myCityModel.stl"
emitters_file = "myEmitters.json"
run_params_file = "myRunParams.json"
results_file = "myResult.blob"

try:
        with ApoperionJob() as job:
                print("uploading STL...")
                job.assignSTL(stl_file)

                print("uploading emitters...")
                job.assignEmitters(emitters_file)

                print("running job...")
                job.run(run_params_file)
                asyncio.run(job.wait()) # wait() is an asynchronous function

                print("fetching results...")
    job.saveResults(results_file, format="blob")
except ApoperionJobError as err:
        print(err)

When creating an instance of ApoperionJob, the host address (host) and port number (port) can be specified. They default to localhost:5000. Hence, the script can either connect to the local ApoPerion service running in the Docker container or to any other remote ApoPerion instance.

Command Line Interface

Based on the Python API apopyrion, the following script works as a command line interface that manages the communication with the ApoPerion web service.

import argparse
import asyncio
from apopyrion.client import ApoperionJob, ApoperionJobError
from pathlib import Path


parser = argparse.ArgumentParser()
parser.add_argument("params", type=Path, help="Path to a set of run parameters in JSON format")
parser.add_argument("-s", "--stl", type=Path, help="Path to a city model in STL format")
parser.add_argument("-d", "--dem", type=Path, help="Path to a elevation model in XYZ format")
parser.add_argument("-e", "--emitters", type=Path, metavar="ETR", help="Path to a set of emitter specifications in JSON format")
parser.add_argument("-o", "--output", type=Path, metavar="OUT", help="Path to the output file")
parser.add_argument("-i", "--ids", type=int, metavar="ID", nargs="+", help="List of emitter IDs to get output for")
parser.add_argument("-a", "--host", type=str, default="localhost", help="Host name of ApoPerion service (default: localhost)")
parser.add_argument("-p", "--port", type=int, default=5000, help="Port of ApoPerion service (default: 5000)")
args = parser.parse_args()

blob_file = args.output if args.output else args.params.with_suffix(".blob")

try:
        with ApoperionJob(args.host, args.port) as job:
                if args.stl:
                        print("uploading STL...")
                        job.assignSTL(str(args.stl))

                if args.dem:
                        print("uploading DEM...")
                        job.assignDEM(str(args.dem))

                if args.emitters:
                        print("uploading emitters...")
                        job.assignEmitters(str(args.emitters))

                print("running job...")
                job.run(str(args.params))
                asyncio.run(job.wait())

                print("fetching results")
                if args.ids:
                        for i in args.ids:
                                print(f" --> ID={i}")
                                curr_blob_file = blob_file.with_name(blob_file.stem + f"_{i:02}" + blob_file.suffix)
                                job.saveResults(curr_blob_file, format="blob", id=i)
                else:
                        job.saveResults(blob_file, format="blob")
except ApoperionJobError as err:
        print(err)

When saved in a file “apoperion_cli.py”, it can be used like this:

$ python apoperion_cli.py my_params.json -s city-model.stl -o my_result.blob