HERE iOS SDK Developer's Guide

Custom Raster Tiles

You can use the HERE iOS SDK to enhance maps with the custom raster tiles API — NMAMapTileLayer.

Custom raster tiles are tile images that you can add to a map for enhancing the map with extra information over a large geographical area. If the application is set to display custom raster tiles, then users see them whenever they view a designated geographical area at a specified zoom level or range of zoom levels.

You can provide tile images in two ways:
  1. Store custom raster tile images on a remote server and return URLs via the NMAMapTileLayerDataSource mapTileLayer:urlForTileAtX:y:zoomLevel: protocol method.
  2. Provide raw bitmap data using the NMAMapTileLayerDataSource mapTileLayer:requestDataForTileAtX:y:zoomLevel: protocol method.

Dividing a Map and Using Tile Coordinates

NMAMapTileLayer uses a scheme that divides the world map into tiles, specified by x, y and zoom level coordinates. This coordinate system is used by the NMAMapTileLayerDataSource protocol when it requests tiles.

At each zoom level, it is expected that the world map is rendered on (2 zoomlevel )2 tiles:
  • at level 3: 8 x 8 = 64 tiles
  • at level 4: 16 x 16 = 256 tiles
  • continuing on until zoom level 20

For example, at zoom level 2, the world map would be divided up as follows:

Figure 1. World Map at Zoom Level 2

The x and y parameters indicate which tile is being requested for the given zoom level.

You need to provide enough tile images to cover all the zoom levels you are supporting within a geographical area. You can restrict custom tile rendering to a specific NMAGeoBoundingBox using the boundingBox property of NMAMapTileLayer. You can restrict the zoom level using showAtZoomLevel: and related methods.

Supplying Tiles from a Web Server

The steps for providing custom tiles from a web server to a map view are as follows:
  1. Host the appropriate number of tiles on a server according to the zoom levels and NMAGeoBoundingBox you are supporting. The tiles must be in either PNG or JPG format and should be sized at 256 x 256 or 512 x 512 pixels.
    Note: As long as your tiles conform to 256 by 256 or 512 by 512 pixels, the NMAMapView can scale the image to fit the current map resolution. For example, if the useHighResolutionMap property is set to YES (512 by 512 pixels) but your tiles are 256 by 256 pixels, the NMAMapView enlarges your image tiles.
  2. Create an object that derives from NMAMapTileLayerDataSource and implement the mapTileLayer:urlForTileAtX:y:zoomLevel:method to return a URL pointing to the specified tile on your server.
  3. Create an NMAMapTileLayer object and set its properties to correspond with the tile data source server. At a minimum, set the boundingBox and zoomLevel properties to reflect the tiles hosted on your server. Set the dataSource property.
  4. Add the NMAMapTileLayer object to the NMAMapView by calling the addMapTileLayer: method.

The following code snippet shows a class that renders the Queen Elizabeth Olympic Park in London. The park is displayed as a tile layer that is added to an NMAMapView, and the raster tiles are served from the HERE server. To use this class, call [OlympicParkTileLayer addOlympicParkTileLayerToMapView:myMapView].


@interface OlympicParkTileLayer : NMAMapTileLayer <NMAMapTileLayerDataSource>
@end

@implementation OlympicParkTileLayer

+(void)addOlympicParkTileLayerToMapView:(NMAMapView*)mapView
{
  OlympicParkTileLayer *tileLayer = [OlympicParkTileLayer new];
  [mapView addMapTileLayer:tileLayer];
  [mapView setGeoCenter:tileLayer.boundingBox.center
      zoomLevel:14.0
      withAnimation:NMAMapAnimationNone ];
}

-(id)init
{
  if (self = [super init]) {
    // Set the data source
    self.dataSource = self;

    // Limit the tiles to the bounding box supported by the server
    NMAGeoBoundingBox *olympicParkBoundingBox =
    [NMAGeoBoundingBox geoBoundingBoxWithTopLeft:[NMAGeoCoordinates 
      geoCoordinatesWithLatitude:51.557000 longitude:-0.042772]
      bottomRight:[NMAGeoCoordinates geoCoordinatesWithLatitude:51.525941 
       longitude: 0.028296]];
    self.boundingBox = olympicParkBoundingBox;

    // Enable caching
    self.cacheTimeToLive = 60 * 60 * 24;  // 24 hours
    self.cacheSizeLimit = 1024 * 1024 * 64; // 64MB
    [self setCacheEnabled:YES withIdentifier:@"OlympicParkTileLayer"];
  }
  return self;
}

-(NSString *)mapTileLayer:(NMAMapTileLayer *)mapTileLayer
      urlForTileAtX:(NSUInteger)x
            y:(NSUInteger)y
        zoomLevel:(NSUInteger)zoomLevel
{
  // Return a URL for the specified tile
  // This tile source is hosted by HERE Global B.V. and may be removed at any time
  return [NSString stringWithFormat:
    @"http://api.maps.example.org/maptiles/olympic_park/normal.day/%d/%d/%d.png",
    zoomLevel,
    y,
    x ];
}

@end

Supplying Tiles as Bitmaps

You can choose to supply tiles as bitmaps if your app uses bundled static tiles, dynamically-generated tiles, or if the server that you are using for tile images requires authentication. In the third case, since NMAMapTileLayer only uses simple HTTP GET requests to retrieve tile images, it is up to you to provide code that handles authentication and downloads the tiles. Once the tiles have been downloaded, you can use them as local bitmaps with the NMAMapTileLayer class.

The steps for providing custom tiles as local bitmaps to a map view are as follows:
  1. Create an object that derives from NMAMapTileLayerDataSource and implements the mapTileLayer:requestDataForTileAtX:y:zoomLevel: method.
  2. Create an NMAMapTileLayer object and set its properties to correspond with the tile data source. Set the dataSource property.
  3. Add the NMAMapTileLayer object to the NMAMapView by calling the addMapTileLayer: method.

Caching Tiles

Tiles can be cached to the disk to improve performance and reduce data traffic in the URL fetching case.

When you enable caching, you must provide a cache identifier. This identifier must be unique for each NMAMapTileLayer used within your application. Since the cache persists across app sessions, it is important to use the same identifier across sessions (by defining a constant, for example).

You can optionally limit the cache size and time-to-live for each cached tile. The cache can be cleared at any time by calling [NMAMapTileLayer clearCache]. To be sure the cache is completely cleared first remove the NMAMapTileLayer from the map view before calling [NMAMapTileLayer clearCache].

The following code enables disk caching with a 128MB maximum size and a tile time-to-live of 7 days:

NMAMapTileLayer *tileLayer = [[NMAMapTileLayer alloc]init];
tileLayer.dataSource = self; // Assuming self is a valid data source
[tileLayer setCacheEnabled:YES withIdentifier:@"MyUniqueTileCacheIdenfifier"];
tileLayer.cacheTimeToLive = 60 * 60 * 24 * 7; // 7 days
tileLayer.cacheSizeLimit = 1024 * 1024 * 128; // 128 MB
[mapView addMapTileLayer:tileLayer]; // NOTE: add to map view after setting tile properties
      

Performance Tips

  1. IMPORTANT: Set NMAMapTileLayer properties before adding the tile layer to the map view. Most properties ignore attempts to set them after being added to the view.
  2. Ensure the properties you set on the tile layer match the data you are supplying via the NMAMapTileLayerDataSource protocol.
  3. Do not block NMAMapTileLayerDataSource methods for extended periods of time. For example, if it takes a while to generate tiles on the fly move the processing to a separate GCD queue.
  4. If requesting a specific tile is constantly failing consider implementing mapTileLayer:hasTileAtX:y:zoomLevel:, returning NO.
  5. Use the provided disk caching mechanism.