# Custom Map Styles

The HERE SDK for iOS provides a flexible solution to customize the map by loading custom map schemes.

If you are looking for predefined map styles, see the Quick Start guide for an introduction. The HERE SDK already includes a set of pre-configured map styles such as .normalDay or .satellite.

## Create and Load Custom Map Styles

The most flexible solution to customize the map is to create your own map styles using a configuration file generated with the WYSIWYG HERE Style Editor.

### Note

To access the HERE Style Editor, please contact your HERE representative - as the editor is not yet publicly available. Please note that the resulting map styles are not compatible with the web editor, so only the HERE Style Editor can be used with the HERE SDK.

This HERE SDK release is compatible with the HERE Style Editor 0.60. It is recommended to use the latest style editor and to update existing styles in case of unexpected behavior or errors.

Usually, the HERE Style Editor is updated for each new release. In practice, consider to re-export existing custom map styles with the matching version of the HERE Style Editor. This should be checked whenever you integrate a newer HERE SDK version. Note that some map features like 3D landmarks are only available for specific versions. As a rule of thumb, we recommend to get in touch with your HERE representative to discuss potential style updates for each new release.

As soon as you are satisfied with your custom map style, export it via File -> Export -> Here Rendering Engine Configuration. Please unzip the resulting file. You will find a few JSON files.

Generate a new folder via Xcode's Project navigator. Copy all JSON files into that directory, for example, with drag & drop. Or completely drag & drop a new folder including all contents into your project.

The main style file always ends with *.scene.json.

Load the style into a map scene as follows:

private func loadCustomMapStyle() {
let bundle = Bundle(for: ViewController.self)
let jsonResourceUrl = bundle.url(forResource: "omv-traffic-traffic-normal-night.scene",
withExtension: "json",
subdirectory: "omv")
guard let jsonResourceString = jsonResourceUrl?.path else {
return
}

print(jsonResourceString)

// Load the map scene using the JSON resource.
}

guard mapError == nil else {
print("Error: Map scene not loaded, \(String(describing: mapError))")
return
}
}


In the above snippet, we verify that the *.scene.json file exists at the expected location. You only have to load this file. From there, the HERE SDK will find the other files - as exported from the editor. Make sure that all style files are kept together at the same folder level. *.scene.json is the main file that includes references to the other files.

Using custom map styles can be very effective to differentiate your app from other map applications. In addition, it is possible to change map styles on-the-fly, for example, to shift the user's attention to certain map elements based on the current state of your app.

### Note

You can find a CustomMapStyles example app on GitHub.

Another alternative to customize the map's look is to add your own raster tile service on top of the HERE map styles. This can be your own server where you host tiles that you want to show as an overlay on top of selected areas of the world - or a public tile server such as OpenStreetMap. Fully opaque and transparent map tile overlays are supported. It is also possible to add more than one raster layer to the map at the same time.

### Note

Note that this is a beta release of this feature, so there could be a few bugs and unexpected behaviors. Related APIs may change for new releases without a deprecation process.

To add custom raster layers, you need to create a RasterDataSource. A RasterDataSource represents the source of raster tile data to display. It also allows changing its configuration. With a RasterDataSourceConfiguration you can specify a configuration for the data source, including URL, tiling scheme, storage levels and caching parameters.

Finally, with the MapLayerBuilder you can create a MapLayer to add a renderable map overlay to the map.

• Use MapLayerVisibilityRange to specify at which zoom levels the map layer should become visible.
• Use the MapLayerPriority to specify the draw order of the MapLayer.
• Use the MapContentType to specify the type of data to be shown by the MapLayer.

In case of raster tile images, use MapContentType.rasterImage.

Default map gesture actions such as pinch, rotate and zoom behave in the same way for raster tiles as for HERE vector maps - except for a few differences: For example, raster tiles are loaded as bitmaps and therefore a rotated raster map tile rotates all labels and street names contained together with the tile.

### Note

When loading a map scene with a custom map style or the default map style, the map will be rendered using vector tiles where the map information is represented as vector data consisting of vertices and paths for better scalability and performance. By contrast, raster tiles are regularly-spaced and square, and consist of bitmap images that represent only pixel information. Note that the satellite map style is also raster based.

Create a RasterDataSource as follows:

private func createRasterDataSource(dataSourceName: String) -> RasterDataSource {
// The URL template that is used to download tiles from the device or a backend data source.
let templateUrl = "https://stamen-tiles.a.ssl.fastly.net/toner/{z}/{x}/{y}.png"
// The storage levels available for this data source. Supported range [0, 31].
let storageLevels: [Int32] = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
let rasterProviderConfig = RasterDataSourceConfiguration.Provider(templateUrl: templateUrl,
storageLevels: storageLevels)

// Raster tiles are stored in a separate cache on the device.
let path = "cache/raster/toner"
let maxDiskSizeInBytes: Int64 = 1024 * 1024 * 32
let cacheConfig = RasterDataSourceConfiguration.Cache(path: path,
diskSize: maxDiskSizeInBytes)

// Note that this will make the raster source already known to the passed map view.
return RasterDataSource(context: mapView.mapContext,
configuration: RasterDataSourceConfiguration(name: dataSourceName,
provider: rasterProviderConfig,
cache: cacheConfig))
}


This code uses a tile source from Stamen Design, which can be used under the CC BY 3.0 license. The data itself comes from OpenStreetMap. For more information visit http://maps.stamen.com/#watercolor/12/37.7706/-122.3782.

Note that you can also use other tile sources that follow the OSM standard style format /zoom/x/y.png. The templateURL should look like this:

https://YourRasterTileService.com/{zoom}/{xTile}/{yTile}.png

Here, the zoom value represents the map's current zoom level, and xTile and yTile defines the horizontal and vertical tile numbers.

Once the tile source is created, a MapLayer can be built:

private func createMapLayer(dataSourceName: String) -> MapLayer {
// The layer should be rendered on top of other layers.
let priority = MapLayerPriorityBuilder().renderedLast().build()
// And it should be visible for all zoom levels.
let range = MapLayerVisibilityRange(minimumZoomLevel: 0, maximumZoomLevel: 22 + 1)

let mapLayer: MapLayer

do {
// Build and add the layer to the map.
try mapLayer = MapLayerBuilder()
.forMap(mapView.hereMap) // mandatory parameter
.withName(dataSourceName + "Layer") // mandatory parameter
.withDataSource(named: dataSourceName,
contentType: MapContentType.rasterImage)
.withPriority(priority)
.withVisibilityRange(range)
.build()
return mapLayer
} catch let InstantiationException {
fatalError("MapLayer creation failed Cause: \(InstantiationException)")
}
}


And finally, the MapTile's visibility can be controlled by enabling or disabling the layer. Note that we also need to provide a unique name. Each RasterDataSource can be created only once:

let dataSourceName = "myRasterDataSourceTonerStyle"
rasterDataSourceTonerStyle = createRasterDataSource(dataSourceName: dataSourceName)
rasterMapLayerTonerStyle = createMapLayer(dataSourceName: dataSourceName)

rasterMapLayerTonerStyle.setEnabled(true)


The resulting layer looks like this:

The above screenshot shows that you can easily combine custom raster tiles with other HERE SDK features. For example, you can render several MapMarker instances on top of the tile data from a tile server.

### Note

One of the main advantages of custom raster layers is that you can easily enhance the HERE map styles with a transparent custom tile source on top, for example, to show weather data or any other data you want to see on top of the map. When using an opaque raster tile source, it is recommended to combine this with an empty base map style.

If you do not use an empty base map style, then the underlying map scheme will "shine" through until the raster layer tiles are loaded. The accompanying example app shows how this looks like.

There are certain other parameters that you can adjust: For example, if your app uses multiple raster layers, you can define a load priority when building a layer with the MapLayerBuilder. This allows to specify an integer value: Higher values will lead to load the layer before layers with lower values.