MapOSMatic is a web frontend for the OCitySMap renderer, which uses the Mapnik map rendering libary, Python and the CairoGraphics library to create decorated single page maps and multi page atlas-like booklets.
Originally this was a pure user centric web GUI, but recently a wish for using the infrastructure with different web frontends did come up.
So this HTTP API provides an alternative way to submit map rendering requests, and to retrieve additional data that may be needed on the way.
Requests and results are submitted as JSON documents, with the optional possibility to attach extra data files, like GPX tracks or Umap user created maps.
This documentation begins with describing the helper calls that provide lists of possible choices for different data fields, followed by the actual map rendering call and how to retrieve rendering status and results, and ends with example programs to access the API from different programming languages.
1. API calls
1.1. Paper Formats
1.1.1. Request URL
https://api.get-map.org/apis/paper_formats
1.1.2. Example result
{ "Best fit": { "height": null, "width": null }, "Din A4": { "height": 297, "width": 210 }, "US letter": { "height": 279, "width": 216 } }
1.2. Layouts
1.2.1. Request URL
https://api.get-map.org/apis/layouts
1.2.2. Example result
{ "multi_page": { "description": "A multi-page layout.", "preview_url": "https://api.get-map.org/media/img/layout/multi_page.png" }, "plain": { "description": "Full-page layout without index.", "preview_url": "https://api.get-map.org/media/img/layout/plain.png" }, "single_page_index_bottom": { "description": "Full-page layout with the index at the bottom.", "preview_url": "https://api.get-map.org/media/img/layout/single_page_index_bottom.png" }, "single_page_index_side": { "description": "Full-page layout with the index on the side.", "preview_url": "https://api.get-map.org/media/img/layout/single_page_index_side.png" } }
1.3. Base Styles
1.3.1. Request URL
https://api.get-map.org/apis/styles
1.3.2. Example output
{ "CartoOSM": { "annotation": "OpenStreetMap Carto standard style", "description": "CartoCSS OSM standard style", "preview_url": "https://api.get-map.org/media/img/style/CartoOSM.png" }, "GermanCartoOSM": { "annotation": "German OSM style based on OSM Carto", "description": "German OSM style", "preview_url": "https://api.get-map.org/media/img/style/GermanCartoOSM.png" } }
1.4. Overlay Styles
1.4.1. Request URL
https://api.get-map.org/apis/overlays
1.4.2. Example output
{ "OpenRailwayMap_Overlay": { "annotation": "OpenRailwayMap overlay", "description": "OpenRailwayMap rail line overlay", "preview_url": "https://api.get-map.org/media/img/style/OpenRailwayMap_Overlay.jpg" }, "Scale_Bar_overlay": { "annotation": "", "description": "Map scale bar" "preview_url": "https://api.get-map.org/media/img/style/Scale_Bar_overlay.jpg" } }
1.5. Job Stati
1.5.1. Request URL
https://api.get-map.org/apis/job_stati/
1.5.2. Example result
{ "0": "Submitted", "1": "In progress", "2": "Done", "3": "Done w/o files", "4": "Cancelled" }
1.6. Submitting a Job
1.6.1. Request URL
POST https://api.get-map.org/apis/jobs/
Expects render job named parameters as a JSON collection.
Possible parameters:
- osmid
-
ID of an object in the osm2pgsql polygon table
- bbox_top
- bbox_bottom
- bbox_left
- bbox_right
-
Render area min. bounding box, may be extended to fit paper format
- title
-
Text to put into the title box (default: "Untitled API Request")
- language
-
locale to use for rendering (default: "en_US.UTF-8")
- layout
-
Map layout to use (default: "plain")
- style
-
Map base style to use (default: "CartoOSM")
- overlays
-
List of overlays to put on top of base style (default: None)
- paper_height
-
Paper height in mm
- paper_width
-
Paper width in mm
- paper_size
-
Predefined paper size to use (default: "Din A4" 210x297mm²)
- orientation
-
Paper orientation: "portrait" or "landscape" when using a predefined paper size (default: "portrait")
- import_url
-
URL pointing to an import file in one of the supported formats
- track_url
-
URL pointing to a GPX track to download and print (deprecated)
- umap_url
-
URL pointing to an Umap export file do download and print (deprecated)
It is also possible import files directly using a multipart/form-data POST request. In this case you have to submit the actual API JSON as a post parameter named "json", and add file fields using whatever field name you like.
Supported import file formats for now are GPX tracks, Umap map exports, and POI files as generated by the "Umgebungsplaene" neighborhood POI web application.
After successfully submitting a request you can either poll the job result API every once in a while until it either returns the "200 OK" status, or a 4xx error code. Or you can redirect a web client to the human readable result page listed in the returned "interactive" job parameter.
1.6.2. Return codes
- 202
-
Accepted when successfully submitted, "Location:" header will contain result URL
- 400
-
Bad request on request validation errors
1.6.3. Example requests
Content-type: application/json { "title": "API Test", "bbox_bottom": 52.00, "bbox_left": 8.52, "bbox_right": 8.50, "bbox_top": 52.02 }
Content-Type: multipart/form-data; boundary=--XXXXXXXX --XXXXXXXX Content-Disposition: form-data; name="json" { "title": "GPX Test", "paper_size": "Din A1" } --XXXXXXXX Content-Disposition: form-data; name="file1"; filename="test.gpx" Content-Type: application/gpx+xml <?xml version="1.0" encoding="UTF-8"?> <gpx version="1.1"> <metadata> <name>GPX test</name> </metadata> <trk> <trkseg> <trkpt lat="52.00" lon="8.50" /> <trkpt lat="52.02" lon="8.52" /> </trkseg> </trk> </gpx> --XXXXXXXX Content-Disposition: form-data; name="file2"; filename="test.umap" Content-Type: application/json { "type": "umap", "properties": { "name": "Umap test" }, "layers": [ { "type": "FeatureCollection", "features": [ { "type": "Feature", "properties": {}, "geometry": { "type": "Point", "coordinates": [ 8.535261, 52.026977 ] } } ], "_umap_options": { "name": "Layer 1" } } ] } --XXXXXXXX--
1.6.4. Example results
HTTP/1.1 202 Accepted [...] Location: /api/jobs/214 Content-Type: text/json { "bbox_bottom": 52.01, "bbox_left": 8.52, "bbox_right": 8.50, "bbox_top": 52.02, "id": 214, "interactive": "https://api.get-map.org/maps/214" "language": "en_US.UTF-8", "layout": "plain", "nonce": "ESubNGmuwYGxPEGM", "paper_height_mm": 297, "paper_width_mm": 210, "status": 0, "styles": "CartoOSM", "title": "API Test" }
HTTP/1.1 400 Bad Request [...] Content-Type: text/json { "error": { "non_field_errors": [ "No bounding box area or OsmID given" ] } }
1.7. Checking Job results
1.7.1. Request Url
https://api.get-map.org/apis/jobs/
1.7.2. Return codes
- 200
-
OK when request has been processed successfully
- 202
-
Accepted when rendering is still in progress, "Location:" header will contain same URL again
- 410
-
Gone when a request had been processed successfuly in the past, but result files have since been purged to free file system space
- 400
-
Bad request when a request failed to render
1.7.3. Example result
HTTP/1.1 200 OK [...] Content-type: application/json { "bbox_bottom": 52.01, "bbox_left": 8.53, "bbox_right": 8.49, "bbox_top": 52.02, "files": { "8bit.png": "https://api.get-map.org/results//000215_2018-09-12_23-14_GPX-test.8bit.png", "jpg": "https://api.get-map.org/results//000215_2018-09-12_23-14_GPX-test.jpg", "pdf": "https://api.get-map.org/results//000215_2018-09-12_23-14_GPX-test.pdf", "png": "https://api.get-map.org/results//000215_2018-09-12_23-14_GPX-test.png", "svgz": "https://api.get-map.org/results//000215_2018-09-12_23-14_GPX-test.svgz" } "id": 215, "language": "en_US.UTF-8", "layout": "plain", "paper_height_mm": 594, "paper_width_mm": 841, "queue_size": 0, "status": 2, "style": "CartoOSM", "title": "GPX test" }
1.8. Cancel a submitted job
1.8.1. Request URL
POST https://api.get-map.org/apis/cancel_job
Expects id and nonce of job to cancel as a JSON collection.
Nonce is only returned when creating a job, so only the creator should be able to cancel a job but nobody else.
Required parameters:
- id
-
ID of job to cancel
- nonce
-
nonce string as returned on job creation call
1.8.2. Return codes
- 204
-
No content when job was cancelled successfully, or already no longer in a wait queue
- 400
-
Bad request when either id or nonce parameter are missing
- 403
-
Forbidden when the given nonce does not match the jobs actual nonce
- 404
-
Not found when no job with the given id exists
2. Example code
2.1. PHP
All examples in this script use the PEAR:HTTP_Request2
class for sending
HTTP requests and receiving server replies.
2.1.1. Retrieve parameter choices
Here we are using the /paper_formats
, /layouts
, styles
and overlays
API calls to retrieve possible choices for all of these parameters, and
their textual description where available.
<?php
require_once 'HTTP/Request2.php';
define('BASE_URL', 'https://api.get-map.org/apis/v1/');
function api_call($url)
{
$request = new HTTP_Request2(BASE_URL . $url);
$request->setMethod(HTTP_Request2::METHOD_GET);
$response = $request->send();
$status = $response->getStatus();
if ($status < 200 || $status > 299) {
echo "invalid HTTP response to '$url': $status\n";
echo $response->getBody();
exit(3);
}
return json_decode($response->getBody());
}
echo "PAPER FORMATS:\n";
$paper_formats = api_call('paper_formats');
foreach ($paper_formats AS $name => $format)
printf("%-10s: %4dx%4d mm²\n", $name, $format->width, $format->height);
echo "\nLAYOUTS\n";
$layouts = api_call('layouts');
foreach ($layouts as $name => $layout)
printf("%-25s: %s\n", $name, $layout->description);
echo "\nSTYLES\n";
$styles = api_call('styles');
foreach ($styles as $name => $style)
printf("%-25s: %s\n", $name, $style->description);
echo "\nOVERLAYS\n";
$overlays = api_call('overlays');
foreach ($overlays as $name => $overlay)
printf("%-25s: %s\n", $name, $overlay->description);
2.1.2. Submitting a simple request
Now we are submitting a simple request, only setting the map title,
and the bounding box of the area to display. We do not check for
rendering to complete, but redirect the web client to the interactive
result URL given as interactive
in the returned JSON data.
This is the same result page as when you submit a map request manually via the standard web user interface, so this approach is suitable for interactive web applications:
<?php
require_once 'HTTP/Request2.php';
define('BASE_URL', 'https://api.get-map.org/apis/v1/');
$data = ['title' => 'PHP Test',
'bbox_bottom' => 52.00,
'bbox_top' => 52.02,
'bbox_left' => 8.50,
'bbox_right' => 8.52
];
$request = new HTTP_Request2(BASE_URL . "jobs");
$request->setMethod(HTTP_Request2::METHOD_POST)
->setHeader('Content-type: application/json; charset=utf-8')
->setBody(json_encode($data));
$response = $request->send();
$status = $response->getStatus();
if ($status < 200 || $status > 299) {
header("Content-type: text/plain");
print_r($response->getBody());
exit;
}
$reply = json_decode($response->getBody());
header("Location: " . $reply->interactive);
2.1.3. Attaching a file
Now, to make things more interesting, we are going to attach a GPX file to our request. As the page title and the necessariy bounding box to fit all track data can be extracted from the file, if it is a valid GPX file, we do not need to pass any other data at all.
<?php
require_once 'HTTP/Request2.php';
define('BASE_URL', 'https://api.get-map.org/apis/v1/');
define('GPX_FILE', 'A1.gpx');
$data = [];
$request = new HTTP_Request2(BASE_URL . "jobs");
$request->setMethod(HTTP_Request2::METHOD_POST)
->addPostParameter('job', json_encode($data))
->addUpload('track', GPX_FILE, basename(GPX_FILE), 'application/gpx+xml');
$response = $request->send();
$status = $response->getStatus();
echo "$status: ".$request->getBody()."\n";
if ($status < 200 || $status > 299) {
header("Content-type: text/plain");
print_r($response->getBody());
exit;
}
$reply = json_decode($response->getBody());
echo "$status: ".$reply->interactive."\n";
2.1.4. Wait for rendering to complete
To have your application check for the rendering status yourself
you need to poll the /jobs
status page, adding the job id
returned by the API. Please wait at least 15 seconds between
status poll requests.
As long as the returned HTTP status is "202 Accepted" the request is either currently being processed, or still waiting in the job queue.
When the job has completed successfully "200 OK" will be returned,
and the files
object in the returned JSON result will contain
a hash of produced file formats, and under what URL each of them
can be retrieved.
In the example we retrieve the PDF version, and start the users preferred PDF viewer to present the result.
<?php
require_once 'HTTP/Request2.php';
define('BASE_URL', 'https://api.get-map.org/apis/v1/');
function api_GET($url)
{
$request = new HTTP_Request2(BASE_URL . $url);
$request->setMethod(HTTP_Request2::METHOD_GET);
$response = $request->send();
$status = $response->getStatus();
if ($status < 200 || $status > 299) {
echo "invalid HTTP response to '$url': $status\n";
echo $response->getBody();
exit(3);
}
return array($status, json_decode($response->getBody()));
}
$data = ['title' => 'PHP Test',
'bbox_bottom' => 52.00,
'bbox_top' => 52.02,
'bbox_left' => 8.50,
'bbox_right' => 8.52
];
$request = new HTTP_Request2(BASE_URL . "jobs");
$request->setMethod(HTTP_Request2::METHOD_POST)
->setHeader('Content-type: application/json; charset=utf-8')
->setBody(json_encode($data));
$response = $request->send();
$status = $response->getStatus();
if ($status < 200 || $status > 299) {
header("Content-type: text/plain");
print_r($response->getBody());
exit;
}
$reply = json_decode($response->getBody());
echo $reply->interactive."\n";
$job_url = "/jobs/" . $reply->id;
echo "waiting ... ";
sleep(15);
list($status, $reply) = api_GET($job_url);
switch ($status) {
case 200:
$done = 1;
break;
case 202:
switch($reply->status) {
case 0:
echo "still in queue\n";
break;
case 1:
echo "now being rendered\n";
break;
default:
echo "unexpected status: ".$reply->status."\n";
break;
}
break;
default:
die("unexpected HTTP status: $status");
break;
}
}
$pdf = basename($reply->files->pdf);
copy($reply->files->pdf, $pdf);
system("xdg-open $pdf");
rm($pdf);
2.2. Python
To be done
2.3. Javascript
To be done, maybe. Due to the "same origin" restrictions this is low on my list …