Overview
Use this guide when you want to show the maps of an IVION site in a third-party application. You need access to an IVION instance with a site that contains maps. Follow this guide to download map tiles and place them in the site coordinate system, aligned with panoramas and point clouds.
These are the floor maps that IVION generates from the scan data, not street-level base maps.
Note:
You download map files with pre-signed URLs from the IVION API, in the same way as point cloud files in the Point Streaming API Usage Guide.
Each map is stored in a single NVR archive that contains all of its tiles. See the NVR File Format Specification.
The streaming APIs require API access, which is included in IVION Professional and IVION Enterprise but not in IVION Core. Without it, API requests fail with HTTP 403 and the message
API not licensed.You can use the C++ IVION Streaming API Sample Application as a reference in addition to this document. It shows maps, panoramas and point clouds together.
All placeholders in this document use curly brackets {}. For example, {instance_url}
For a description of all IVION API endpoints, see the IVION API documentation.
Logging in
Third-party applications log in with a login mandate. The application requests a mandate, the user authorizes it in the IVION web application, and the application exchanges it for an access token and a refresh token. For more information, see the sections Logging In and Refreshing the Access Token in Point Streaming API Usage Guide.
All API requests in this guide require the header X-Authorization: Bearer {access_token}. Downloads with pre-signed URLs do not require it.
Note:
Some IVION instances allow anonymous access. On such instances, you can skip the login: all requests in this guide, including the request for a pre-signed download URL, also work without the
X-Authorizationheader, with the permissions of an anonymous user.
Listing sites
Retrieve the list of sites available to the user through the following endpoint:
Endpoint:
GET {instance_url}/api/sites
X-Authorization: Bearer {access_token}Response Code & Status:
200 OKResponse Body:
[
{
"id": number,
"name": string,
"description": string | null,
...
},
...
]Listing maps
Retrieve the maps of a site through the following endpoint:
Endpoint:
GET {instance_url}/api/site/{site_id}/tiled_maps
X-Authorization: Bearer {access_token}Response Code & Status:
200 OKResponse Body:
[
{
"id": number,
"scs_location": [number, number, number],
"scs_orientation": [number, number, number, number],
"storage_file_path": string,
"type": "TILED_PYRAMID",
"map_size_m": number,
"tile_size_px": number,
"max_depth": number,
"quadtree": string,
"outdoor": boolean,
"floor_id": number | null,
...
},
...
]Attribute | Description |
|---|---|
scs_location | Center of the map (x, y, z) in the site coordinate system, in meters. The z coordinate is the lower height bound ( |
scs_orientation | Orientation of the map in the site coordinate system as a quaternion in the order (w, x, y, z). |
storage_file_path | Storage path of the map's NVR archive, starting with the site ID. |
map_size_m | Edge length, in meters, of the square area covered by the root tile. |
tile_size_px | Edge length, in pixels, of a tile image. All tiles are square. |
max_depth | Deepest level of the tile pyramid. The root tile is at depth 0. |
quadtree | Describes which tiles exist. See the section Decoding the quadtree. |
outdoor | Whether the map shows the outdoor area of the site rather than a floor of a building. |
floor_id | ID of the site model floor the map belongs to. |
Maps can overlap, for example when a site contains several scans of the same floor. Show outdoor maps (outdoor is true) together with the indoor maps. Show indoor maps per floor, or let the user switch individual maps on and off.
Floor names
To label maps, retrieve the site model of the site. It contains buildings with nested floors; match the floor's id with the map's floor_id. The fields scs_z_min and scs_z_max are the height range of a floor in the site coordinate system, in meters.
Endpoint:
GET {instance_url}/api/site/{site_id}/site_model
X-Authorization: Bearer {access_token}Response Code & Status:
200 OKResponse Body:
[
{
"id": number,
"type": "BUILDING",
"name": string,
"children": [
{
"id": number,
"type": "FLOOR",
"name": string,
"scs_z_min": number | null,
"scs_z_max": number | null,
...
},
...
],
...
},
...
]Requesting a pre-signed download URL
Map files are downloaded with pre-signed URLs, in the same way as point cloud files. A pre-signed URL is scoped to a storage prefix and can be reused for all files under that prefix.
Endpoint:
POST {instance_url}/api/site/{site_id}/storage/download/prefix/signed/url
X-Authorization: Bearer {access_token}Request Body:
{
"prefix_in_site": "{prefix_in_site}",
"expiry_in_millis": number
}Response Code & Status:
200 OKResponse Body:
{
"url": string,
"issued_at_millis": number,
"expires_at_millis": number
}The field expiry_in_millis is optional. If you omit it, the instance's default validity is used, which is usually 5 minutes. The instance rejects requests for a longer validity than its maximum.
To download a file, insert the file's storage path into the path of the returned URL, before the query string:
{signed_base_url}/{storage_file_path}{signed_query_string}Note:
The storage path of a file starts with the site ID, for example
{site_id}/{directory}/{file_name}.Monitor
expires_at_millisand request a new URL before it expires. Do not rely on a fixed validity, because it depends on the instance configuration.Cache one signed URL per storage prefix. Avoid requesting several signed URLs for the same prefix in parallel.
Usually, the URL points to a CDN. On instances without a CDN, the URL is a path on the IVION instance. If the URL starts with
/, resolve it against the origin (scheme, host and port) of the instance.Signed URLs support HTTP Range requests. No
X-Authorizationheader is needed.
For a map, use the directory of its storage_file_path without the leading site ID as prefix_in_site. For example, if storage_file_path is {site_id}/{directory}/{file_name}, prefix_in_site is {directory}. The URL of the map's archive is then:
{signed_base_url}/{storage_file_path}{signed_query_string}Reading the NVR archive
All tiles of a map are stored in a single NVR archive. The archive's index is stored at the end of the file, so a client can download any tile with an HTTP Range request after reading the index once. The format is described in the NVR File Format Specification. In short:
Request the last 2048 bytes of the archive with
Range: bytes=-2048.Read the 42-byte footer at the end: number of files
N(uint32, big-endian, at offset 32) and archive type (uint8, at offset 36).Read the index:
Nbig-endianuint32file lengths directly before the footer. If the index does not fit into the first response, request the last4 × N + 42bytes.The first file starts at byte 4. Each following file starts where the previous one ends.
Download a file with
Range: bytes={offset}-{offset + length - 1}. Files with a length of 0 are missing.
The archive type of a map archive is 2. Each file in the archive is the PNG image of one tile. The number of files equals the number of tiles in the quadtree.
Tile pyramid
A map is a pyramid of square tiles. The root tile at depth 0 covers the whole map, a square with an edge length of map_size_m meters. Each tile at depth d has up to four children at depth d + 1, each covering a quarter of the tile. Tiles are identified by depth d and column and row (x, y) within their depth; (0, 0) is the top-left tile, and y increases downwards in the image. The children of tile (d, x, y) are:
Child | Position | Tile |
|---|---|---|
0 | Top-left | (d + 1, 2x, 2y) |
1 | Top-right | (d + 1, 2x + 1, 2y) |
2 | Bottom-left | (d + 1, 2x, 2y + 1) |
3 | Bottom-right | (d + 1, 2x + 1, 2y + 1) |
Only tiles that contain map content exist. The resolution of a tile at depth d is:
meters_per_pixel = map_size_m / (tile_size_px × 2^d)Decoding the quadtree
The quadtree attribute is a string of hexadecimal digits. Each digit is the child mask of one tile: bit 0 (value 1) is set if child 0 exists, bit 1 (value 2) for child 1, bit 2 (value 4) for child 2 and bit 3 (value 8) for child 3. The digits describe the tiles in breadth-first order, starting with the root tile. If the string ends before all tiles are described, the remaining tiles have no children.
The tiles are stored in the NVR archive in the same breadth-first order. Decode the quadtree with a queue to get each tile's file index:
queue = [(0, 0, 0)] # (depth, x, y) of the root tile
next_digit = 0
file_index = 0
while queue is not empty:
(d, x, y) = queue.pop_front()
tile (d, x, y) is stored at file_index
file_index = file_index + 1
mask = hex value of quadtree[next_digit], or 0 if next_digit >= length(quadtree)
next_digit = next_digit + 1
for child in 0..3:
if mask has bit child set:
queue.push_back((d + 1, 2x + (child mod 2), 2y + (child div 2)))An empty quadtree string means that the map contains no tiles.
Positioning map tiles
Map tiles are positioned in the map's local coordinate system: a plane in meters with the origin at the map center, +X pointing to the right of the map image and +Y to its top. The bounds of tile (d, x, y) are:
size = map_size_m / 2^d
min_x = −map_size_m / 2 + x × size
max_x = min_x + size
max_y = map_size_m / 2 − y × size
min_y = max_y − sizeThe top row of the tile image is at max_y. To transform a point (px, py) from map-local coordinates into the site coordinate system, rotate it with the quaternion q = scs_orientation and add scs_location:
site_point = scs_location + q × (px, py, 0) × q⁻¹For most maps, scs_orientation is the identity rotation (1, 0, 0, 0), so the map lies in a horizontal plane at the height scs_location.z.
Site coordinates:
Panorama poses and map poses are given in the site coordinate system, a right-handed metric coordinate system with Z pointing up.
Point clouds are stored in dataset coordinates. To show them together with panoramas and maps, apply the dataset's site transformation, as described in the Applying Alignment section of the Point Streaming API Usage Guide.
Choosing the level of detail
Load the root tile first, then refine: if a tile appears larger on screen than its tile_size_px, load its existing children and draw them instead of the tile. Keep drawing the parent tile until all of its existing children have loaded, so that no gaps appear while tiles stream in. Do not refine beyond max_depth.
Panoramas
To show panoramas on top of a map, see the Panorama Streaming API Usage Guide. Panoramas use the same site coordinate system.
FAQ
What is the IVION Map Streaming API used for?
With it, third-party applications can display the floor maps of an IVION site, aligned with panoramas and point clouds.
Does the Map Streaming API include street maps?
No. Street-level base maps are provided by third-party services and are not part of this API.
Are map tiles in web mercator (z/x/y) coordinates?
No. Map tiles use a local metric coordinate system per map, which is placed in the site coordinate system with the map's scs_location and scs_orientation.
Why does a map have fewer files than a full pyramid would?
Only tiles with map content exist. The quadtree describes which tiles exist and in which order they are stored.