Implementing a Kanzi Maps backend

You can implement your own Kanzi Maps backend.

When implementing a Kanzi Maps backend, you must implement:

  • A way to register the backend. See Registering a backend.

  • These main components:

    Component

    Example name

    Inherits from

    Backend

    MyOwnBackend

    MapBackend

    Tiling scheme

    MyOwnTilingScheme

    TilingScheme

    Tile fetcher

    MyOwnTileFetcher

    TileFetcher

    Tile source

    MyOwnTileSource

    TileSource

To make your backend support more functionality, you can implement these optional components:

Component

Example name

Inherits from

Texture reference

MyOwnTextureReference

TextureReference

Texture tile

MyOwnTextureTile

TextureTile

Router

MyOwnRouter

Router

Geocoder

MyOwnGeocoder

Geocoder

Registering a backend

Current Kanzi Maps backends expose a function that you can call to register the backend. For MyOwnBackend this function can look like this:

void registerMyOwnBackend()
{
    kanzi::MapBackendFactory::get().registerBackend("myown",
        [](MapDomainSharedPtr mapDomain) -> std::shared_ptr<kanzi::MapBackend>
    {
        return std::make_shared<kanzi::MyOwnBackend>(mapDomain);
    });
}

After a call to registerMyOwnBackend, Kanzi Maps can instantiate the new backend with the name “myown”.

Implementing a backend

MyOwnBackend acts as an orchestrator or manager between the other components. Whenever Kanzi Maps requests from the backend a subcomponent, such as the tile fetcher or the router, the backend must return the same instance of that component on every request.

Implementing a tiling scheme

The purpose of a tiling scheme is to:

  • Divide the world into tiles at different zoom levels.

  • Map WGS84 coordinates to target projection and the other way around.

These are some of the things to consider when you implement a tiling scheme:

  • Retiling data.

    It is usually good to select the tiling to match the tiling used by the source data. Avoid retiling data on the fly, when possible. If the source data does not follow any tiling, you cannot avoid retiling.

  • Target projection.

    When you implement an xyz tiling scheme, you must choose the target projection. To minimize the amount of CPU time use, match the source data projection as closely as possible, as long as that source data uses a projection that roughly approximates meters. An example of such projection is Web Mercator, for which the map backend library provides conversion routines. See geom::WebMercator. Another option is to perform runtime conversion from source projection while creating VectorTiles.

  • Encoding tile addresses.

    The world divided into tiles at 32 zoom levels fits into 64 bits allocated for the TileAddress. For example, if zoom level takes 5 bits, it leaves space for 2^(64-5)=2^59 tiles per zoom level. Covering an area of 40000 km by 40000 km with tiles results in 0.003 square meters per tile.

  • The projection used by MyOwnTilingScheme is directly used for rendering, that is, the returned coordinates are considered as the world coordinates.

Implementing a tile fetcher

Tile fetcher defines:

  • The available map layers

  • The geometry and metadata that the features on the layers can contain

When designing the tile fetcher, you need to decide how to:

  • Map TileFilter into TileSource.

  • Define the pipeline for fetching a tile from a given TileSource with a given TileAddress.

  • Map source geometry to that supported by geom::GeometryCollectionBase:

    2D point, 2D or 3D line string, concave or convex 2D or 3D polygon with optional holes, 2D triangles, 3D mesh with normals, texture coordinates, and a texture, or 3D strip geometry.

  • Map feature-specific information into key-value metadata of the feature.

  • Map potential raster or texture data into TextureTile. See Implementing texturing.

The data source is often a REST API that you access over HTTP or HTTPS. The Kanzi Maps backend library provides a rudimentary HTTPClient API that you can use to implement access to such APIs.

Kanzi Maps does not provide built-in support for authentication and similar concepts. Your backend implementation must handle them internally.

The pipeline defined by a tile fetcher often has these stages:

  1. Fetch data from the server, disk, or in-memory cache.

    Handle this stage in the IO context.

  2. Process and package the data into VectorTile or TextureTile instances, and the instances into parts of a Tile.

    Handle this stage in the COMPUTE context.

Renderers can then append further COMPUTE or MAIN context tasks to produce Kanzi meshes, textures, and nodes.

Make sure that you set these metadata keys that the Kanzi Maps plugin uses:

Key

Type

Description

no_clip_to_tile

Boolean

Whether to disable clipping geometry to tile boundaries.

extrude

Boolean

Whether to extrude geometry when supported.

min_height

Number

Sets the floor level of extruded structures. Make this compatible with the target projection in meters.

height

Number

Sets the ceiling level of extruded structures. Make this compatible with the target projection in meters.

Implementing texturing

Texturing relies on these classes:

TextureTile

Container for texture or raster data for a given TileAddress.

If several tiles share texture data, MyOwnTileFetcher and MyOwnTextureTile need to implement sharing internally.

TextureReference

Opaque reference to TextureTile. Enables getting the texture data for a specific texture.

MyOwnTextureReference can store the information needed to return an instance of MapTexture from MyOwnTextureTile given a MyOwnTextureReference.

geom::Mesh

Owns TextureReference, which Kanzi Maps converts to a Kanzi Mesh and Texture.

Kanzi Maps supports the PNG and raw RGBA8888 texture formats. TextureTile and TextureReference do not support tile atlases. Each texture must either be its own Kanzi texture, or you need to implement the texture atlas on the fly after retrieving the texture data from the texture tile.

Implementing a router

The main purpose of the router is to take a list of waypoints, convert them to a request, and fill in a collection of Route instances.

The routing data source is often a REST API that you access over HTTP or HTTPS. The Kanzi Maps backend library provides a rudimentary HTTPClient API that you can use to implement access to such APIs.

Kanzi Maps does not provide built-in support for authentication and similar concepts. Your backend implementation must handle them internally.

The data structure of a Route instance closely follows that used by OSRM responses. If your router is OSRM-based, you can use the OSRM JSON parser which is built-in to the map backend library.

For other routing services, you must convert the route information to a format that a Route can represent:

  1. Split the data into Legs between Waypoints.

  2. Split the Legs into Steps between Maneuvers.

  3. Encode maneuvers and turn-by-turn instructions into Maneuvers.

You need to provide the Route geometry as a geom::LineString, if requested, and at the requested quality level. Kanzi Maps uses this geometry to render the route on top of the map, which is why the geometry needs to match the road geometry of the map tiles with high enough accuracy.

Implementing a geocoder

The Kanzi Maps API supports only reverse geocoding.

The geocoder must return a pipeline for fetching a collection of GeoFeatures when given WGS84 latitude-longitude coordinates and a map feature type filter.

If the reverse geocoding service supports the type filter, you can use that filter to limit results.

You can use the HTTPClient API provided by the Kanzi Maps backend library to access a REST API through which you get the data.

Sometimes geocoding results are provided in GeoJSON format. The Kanzi Maps backend library comes with a partial GeoJSON parser implementation. This parser directly outputs GeoFeature instances. Order the results by relevance, more specific results first. Make the geocoder return only results that intersect a given coordinate. For example, a place of interest comes before a postal code, and a postal code comes before a country.

The Geocoder needs to be able to provide this information for each feature.

  • Unique ID.

  • Type. For example, country, post code, or place of interest.

  • Human-readable text.

  • (Optional) Address.

  • Subtype. For example, a place of interest can be a cafe, restaurant, hotel, and so on.

  • Center of the feature in WGS84 latitude-longitude coordinates.

  • Requested center, that is, the WGS84 center of the request.

See also

Kanzi Maps backend