HERE Maps offers map-rendering services that provide world-wide raster data, such as aerial maps. You can use those as background data in your LuciadCPillar map application.

The HERE Maps data is structured as a quad tree with 21 levels. In short, a quad tree is a multi-leveled tile structure where a tile at a certain level is split up in 2x2 tiles at a more detailed level.

This figure illustrates the tile layout of a quad tree with 4 levels. Each level has the same model bounds. The image on the left shows the root tile at level 0. Each new level has 4 times as many tiles.

quadtree tile structure
Figure 1. Quad tree tile structure

Each of the tiles in this quad tree corresponds to an image that you can download using the HERE Maps service.

This article shows how you can do that with the LuciadCPillar API.

Step 1: Create a model with a quad-tree structure

The code snippets show how the HERE Maps model is created. It uses a quad-tree structure with a single root tile and 21 levels. Images have a resolution of 256 x 256 pixels.

Note that this snippet already mentions the HereMapsDataRetriever class. Step 2: Retrieve the HERE Maps imagery data elaborates on this class.

Program: Create a HERE Maps model
// The reference for the HERE maps tile structure.
auto pseudoMercator = CoordinateReferenceProvider::create("EPSG:3857");
if (!pseudoMercator) {
throw luciad::RuntimeException("Cannot create Pseudo Mercator reference 'EPSG:3857'");
}
// The extent of the tile structure; which is also the extent of the data.
Bounds hereMapsBounds = Bounds(*pseudoMercator, Coordinate{-20037508.34278925, -20037508.34278925}, Coordinate{20037508.34278925, 20037508.34278925});
auto modelMetadata = ModelMetadata::newBuilder().title(toTitle(hereType)).build();
auto hereMapsAttributionProvider = createAttributionProvider(hereType, apiKey);
auto hereMapsDataRetriever = std::make_shared<HereMapsDataRetriever>(hereMapInfo, dpi);
return QuadTreeRasterModelBuilder::newBuilder()
.reference(*pseudoMercator)
.levelCount(21)
.level0ColumnCount(1)
.level0RowCount(1)
.tileWidthPixels(256)
.tileHeightPixels(256)
.bounds(hereMapsBounds)
.modelMetadata(modelMetadata)
.dataRetriever(hereMapsDataRetriever)
.attributionProvider(hereMapsAttributionProvider)
.build();

Step 2: Retrieve the HERE Maps imagery data

The next step is to create an IMultilevelTiledRasterDataRetriever implementation that can return HERE Maps data for each tile. When the raster visualization engine requests data for a certain tile that’s visible on the map, the IMultilevelTiledRasterDataRetriever class is called.

The snippets show how this interface is implemented for HERE Maps.

For each tile for which data is requested, it:

  1. Constructs a HERE Maps URL.

  2. Performs a HTTP GET request to this URL to download the HERE Maps PNG image for the tile.

  3. Passes the encoded PNG data to the IMultilevelTiledRasterDataRetrieverCallback, which decodes the PNG data, and passes it on to the raster visualization engine.

  4. Handles cancellation, possible errors, or missing tile data.

These snippets don’t show details for every operation. For the full source code, see the HereMapsModelFactory class in the data formats sample.

Program: Create a HERE Maps data retriever
class HereMapsDataRetriever : public IMultilevelTiledRasterDataRetriever {
public:
void retrieveTileData(const MultilevelTileCoordinate& tileCoordinate,
const CancellationToken& cancellationToken,
const std::shared_ptr<IMultilevelTiledRasterDataRetrieverCallback>& callback) override {
// Create a HERE Maps url, based on the (fixed) base URL and the tile coordinate for this tile
const std::string tileUrl = getUrl(tileCoordinate);
// Perform a HTTP GET request to retrieve the data from the HERE Maps url
const HttpRequest httpRequest = HttpRequest::newBuilder().uri(tileUrl).build();
const expected<HttpResponse, ErrorInfo> httpResponse = _httpClient->send(httpRequest, cancellationToken);
// Handle cancellation. This happens for example when a layer is removed. In that case, the data is not needed anymore
if (cancellationToken.isCanceled()) {
callback->onCanceled(tileCoordinate);
return;
}
if (httpResponse.has_value()) {
// Read the byte data and add it to a DataEntity
std::optional<DataEntity> content = httpResponse->getBody();
if (content.has_value()) {
// Pass the data to the onDataAvailable callback, which will decode the HERE Maps PNG data
// and pass on the decoded image to the raster painting engine.
callback->onDataAvailable(tileCoordinate, content.value());
} else {
callback->onDataNotAvailable(tileCoordinate);
}
} else {
// Handle errors
const std::string message = httpResponse.get_unexpected().value().getMessage();
callback->onError(tileCoordinate, message);
}
}
};

Step 3: Getting a HERE Maps API key

The next thing you need before connecting to HERE Maps is a HERE API key. On the HERE developer portal, you can create a HERE account and one or more keys.

To create a key for HERE Maps:

  1. Go to https://developer.here.com/.

  2. Log in with your HERE account, or sign up for a new account.

    1. If you sign up for a new account, select a plan that fits your project. The default is the Freemium plan.
      Once you have logged on, you see a Projects page that lists all your projects and the corresponding HERE plan. For new accounts with a Freemium plan, the Projects page shows one project called Freemium and the project creation date.

  3. Select the project from the projects page.

  4. On the Project Details page, you can create keys. In the REST section, click Generate App.
    The portal generates an APP ID.

  5. Click Create API key.
    The portal generates a key. Click the Copy button to copy/paste it in your code.

Note that more terms may apply for deployment. For more information about HERE licensing, see the HERE Terms and Conditions.

Step 4: Add the HERE Maps model to the map

The final step is adding the HERE Maps model to the map:

Program: Add the HERE Maps model to the map
const auto map = _mapObject->getMap();
// Create a model for HereMaps using the API Key
auto hereMapsModel = HereMapsModelFactory::createHereModel(hereType, map->getDpi(), hereMapsApiKey.toStdString());
// Create a layer for the model using the layer builder
auto hereMapsLayer = RasterLayer::newBuilder().model(hereMapsModel).build();
// Add the layer to the map
map->getLayerList()->add(hereMapsLayer);

The tutorial Introduction to styling raster data explains the styling options you have on the raster layer.

Getting the HERE Maps attributions

LuciadCPillar exposes the attributions on a map through MapAttributions. You can get attributions from all layers, or request the attributions of a specific layer. For more information, see How to provide and retrieve attribution data.