Skip to main content

Overview

The Tiled2dMapLayerConfig interface allows you to configure custom tiled layers for different map tile services. This is essential when working with non-standard tile pyramids, custom coordinate systems, or specialized map services.

Tiled2dMapLayerConfig Interface

The configuration interface defines how the layer computes visible tiles and loads them:
class Tiled2dMapLayerConfig {
public:
    virtual int32_t getCoordinateSystemIdentifier() = 0;
    virtual std::string getTileUrl(int32_t x, int32_t y, int32_t t, int32_t zoom) = 0;
    virtual std::vector<Tiled2dMapZoomLevelInfo> getZoomLevelInfos() = 0;
    virtual Tiled2dMapZoomInfo getZoomInfo() = 0;
    virtual std::string getLayerName() = 0;
    virtual std::optional<Tiled2dMapVectorSettings> getVectorSettings() = 0;
    virtual std::optional<RectCoord> getBounds() = 0;
}

Creating a Custom Layer Config

Android Example

private val customConfig = object : Tiled2dMapLayerConfig() {
    // Defines the bounds of the layer
    val epsg3857Bounds: RectCoord = RectCoord(
        Coord(CoordinateSystemIdentifiers.EPSG3857(), -20037508.34, 20037508.34, 0.0),
        Coord(CoordinateSystemIdentifiers.EPSG3857(), 20037508.34, -20037508.34, 0.0)
    )

    // Map coordinate system of the layer
    override fun getCoordinateSystemIdentifier(): Int = 
        CoordinateSystemIdentifiers.EPSG3857()

    // Layer's name
    override fun getLayerName(): String = "OSM_Layer"

    // URL pattern to load tiles
    override fun getTileUrl(x: Int, y: Int, t: Int, zoom: Int): String {
        return "https://a.tile.openstreetmap.org/$zoom/$x/$y.png"
    }

    // Origin corner of data in vector tiles (null for raster)
    override fun getVectorSettings(): Tiled2dMapVectorSettings? = null

    // Extent that defines the bounds of this layer
    override fun getBounds(): RectCoord? = null

    // Zoom configuration
    override fun getZoomInfo(): Tiled2dMapZoomInfo {
        return Tiled2dMapZoomInfo(
            zoomLevelScaleFactor = 0.6667f,
            numDrawPreviousLayers = 2,
            numDrawPreviousOrLaterTLayers = 0,
            adaptScaleToScreen = true,
            maskTile = false,
            underzoom = true,
            overzoom = true
        )
    }

    // List of valid zoom levels
    override fun getZoomLevelInfos(): ArrayList<Tiled2dMapZoomLevelInfo> = ArrayList(
        listOf(
            Tiled2dMapZoomLevelInfo(559082264.029, 40075016f, 1, 1, 1, 0, epsg3857Bounds),
            Tiled2dMapZoomLevelInfo(279541132.015, 20037508f, 2, 2, 1, 1, epsg3857Bounds),
            Tiled2dMapZoomLevelInfo(139770566.007, 10018754f, 4, 4, 1, 2, epsg3857Bounds),
            Tiled2dMapZoomLevelInfo(69885283.0036, 5009377.1f, 8, 8, 1, 3, epsg3857Bounds),
            Tiled2dMapZoomLevelInfo(34942641.5018, 2504688.5f, 16, 16, 1, 4, epsg3857Bounds),
            Tiled2dMapZoomLevelInfo(17471320.7509, 1252344.3f, 32, 32, 1, 5, epsg3857Bounds),
            Tiled2dMapZoomLevelInfo(8735660.37545, 626172.1f, 64, 64, 1, 6, epsg3857Bounds),
            Tiled2dMapZoomLevelInfo(4367830.18773, 313086.1f, 128, 128, 1, 7, epsg3857Bounds),
            Tiled2dMapZoomLevelInfo(2183915.09386, 156543f, 256, 256, 1, 8, epsg3857Bounds),
            Tiled2dMapZoomLevelInfo(1091957.54693, 78271.5f, 512, 512, 1, 9, epsg3857Bounds),
            Tiled2dMapZoomLevelInfo(545978.773466, 39135.8f, 1024, 1024, 1, 10, epsg3857Bounds),
            Tiled2dMapZoomLevelInfo(272989.386733, 19567.9f, 2048, 2048, 1, 11, epsg3857Bounds),
            Tiled2dMapZoomLevelInfo(136494.693366, 9783.94f, 4096, 4096, 1, 12, epsg3857Bounds),
            Tiled2dMapZoomLevelInfo(68247.3466832, 4891.97f, 8192, 8192, 1, 13, epsg3857Bounds),
            Tiled2dMapZoomLevelInfo(34123.6733416, 2445.98f, 16384, 16384, 1, 14, epsg3857Bounds),
            Tiled2dMapZoomLevelInfo(17061.8366708, 1222.99f, 32768, 32768, 1, 15, epsg3857Bounds),
            Tiled2dMapZoomLevelInfo(8530.91833540, 611.496f, 65536, 65536, 1, 16, epsg3857Bounds),
            Tiled2dMapZoomLevelInfo(4265.45916770, 305.748f, 131072, 131072, 1, 17, epsg3857Bounds),
            Tiled2dMapZoomLevelInfo(2132.72958385, 152.874f, 262144, 262144, 1, 18, epsg3857Bounds),
            Tiled2dMapZoomLevelInfo(1066.36479193, 76.437f, 524288, 524288, 1, 19, epsg3857Bounds),
            Tiled2dMapZoomLevelInfo(533.18239597, 38.2185f, 1048576, 1048576, 1, 20, epsg3857Bounds)
        )
    )
}

iOS Example

import MapCore

class TiledLayerConfig: MCTiled2dMapLayerConfig {
    // Zoom configuration
    func getZoomInfo() -> MCTiled2dMapZoomInfo {
        MCTiled2dMapZoomInfo(
            zoomLevelScaleFactor: 0.65,
            numDrawPreviousLayers: 1,
            adaptScaleToScreen: true
        )
    }

    // Map coordinate system
    func getCoordinateSystemIdentifier() -> Int32 {
        MCCoordinateSystemIdentifiers.epsg3857()
    }

    // Layer bounds
    func getBounds() -> MCRectCoord {
        let identifer = MCCoordinateSystemIdentifiers.epsg3857()
        let topLeft = MCCoord(
            systemIdentifier: identifer,
            x: -20037508.34,
            y: 20037508.34,
            z: 0.0
        )
        let bottomRight = MCCoord(
            systemIdentifier: identifer,
            x: 20037508.34,
            y: -20037508.34,
            z: 0.0
        )
        return MCRectCoord(
            topLeft: topLeft,
            bottomRight: bottomRight
        )
    }

    // Tile URL pattern
    func getTileUrl(_ x: Int32, y: Int32, zoom: Int32) -> String {
        return "https://example.com/tiles/\(zoom)/\(x)/\(y).png"
    }

    // Layer name
    func getLayerName() -> String {
        "OSM Layer"
    }

    // Zoom level definitions
    func getZoomLevelInfos() -> [MCTiled2dMapZoomLevelInfo] {
        [
            .init(zoom: 559082264.029, tileWidthLayerSystemUnits: 40_075_016, 
                  numTilesX: 1, numTilesY: 1, numTilesT: 1, 
                  zoomLevelIdentifier: 0, bounds: getBounds()),
            .init(zoom: 279541132.015, tileWidthLayerSystemUnits: 20_037_508, 
                  numTilesX: 2, numTilesY: 2, numTilesT: 1, 
                  zoomLevelIdentifier: 1, bounds: getBounds()),
            .init(zoom: 139770566.007, tileWidthLayerSystemUnits: 10_018_754, 
                  numTilesX: 4, numTilesY: 4, numTilesT: 1, 
                  zoomLevelIdentifier: 2, bounds: getBounds()),
            // ... additional zoom levels
            .init(zoom: 533.18239597, tileWidthLayerSystemUnits: 38.2185, 
                  numTilesX: 1_048_576, numTilesY: 1_048_576, numTilesT: 1, 
                  zoomLevelIdentifier: 20, bounds: getBounds()),
        ]
    }
}

Configuration Parameters

Tiled2dMapZoomInfo

Controls how tiles are displayed at different zoom levels:
struct Tiled2dMapZoomInfo {
    float zoomLevelScaleFactor;          // Scale factor applied to zoom
    int32_t numDrawPreviousLayers;        // Number of previous zoom layers to draw
    int32_t numDrawPreviousOrLaterTLayers; // Temporal layers to draw
    bool adaptScaleToScreen;              // Scale according to screen DPI
    bool maskTile;                        // Enable tile masking
    bool underzoom;                       // Draw when zoomed out beyond valid range
    bool overzoom;                        // Draw when zoomed in beyond valid range
}
ParameterTypeDescription
zoomLevelScaleFactorfloatMultiplier applied to the scale (e.g., 0.6667 for slightly larger tiles)
numDrawPreviousLayersint32How many lower-resolution layers to show while loading
numDrawPreviousOrLaterTLayersint32Temporal layers for time-series data
adaptScaleToScreenboolScale tiles based on WMTS ScaleDenominator and screen PPI
maskTileboolEnable masking for tile boundaries
underzoomboolContinue drawing when zoomed out beyond minimum
overzoomboolContinue drawing when zoomed in beyond maximum

Tiled2dMapZoomLevelInfo

Defines each zoom level in the tile pyramid:
struct Tiled2dMapZoomLevelInfo {
    double zoom;                      // Target zoom value
    float tileWidthLayerSystemUnits;  // Tile size in layer coordinates
    int32_t numTilesX;                // Number of tiles horizontally
    int32_t numTilesY;                // Number of tiles vertically
    int32_t numTilesT;                // Number of temporal tiles
    int32_t zoomLevelIdentifier;      // Identifier used in tile URL
    RectCoord bounds;                 // Bounds for this zoom level
}
ParameterTypeDescription
zoomdoubleCamera zoom value that triggers this level
tileWidthLayerSystemUnitsfloatWidth of one tile in layer coordinate units
numTilesXint32Total tiles in X direction at this zoom
numTilesYint32Total tiles in Y direction at this zoom
numTilesTint32Temporal dimension (usually 1)
zoomLevelIdentifierint32Zoom parameter passed to getTileUrl()
boundsRectCoordValid bounds for tiles at this level

Common Use Cases

Custom Tile Service

When using a tile service with non-standard URL patterns:
override fun getTileUrl(x: Int, y: Int, t: Int, zoom: Int): String {
    // Custom tile service with API key
    return "https://tiles.myservice.com/maps/$zoom/$x/$y.png?key=API_KEY"
}

Non-Standard Coordinate System

For regional maps with custom projections:
override fun getCoordinateSystemIdentifier(): Int = 
    CoordinateSystemIdentifiers.EPSG2056()  // Swiss coordinate system

override fun getBounds(): RectCoord {
    return RectCoord(
        Coord(CoordinateSystemIdentifiers.EPSG2056(), 2420000.0, 1350000.0, 0.0),
        Coord(CoordinateSystemIdentifiers.EPSG2056(), 2900000.0, 1030000.0, 0.0)
    )
}

Limited Zoom Range

For specialized maps that only work at certain zoom levels:
override fun getZoomInfo(): Tiled2dMapZoomInfo {
    return Tiled2dMapZoomInfo(
        zoomLevelScaleFactor = 1.0f,
        numDrawPreviousLayers = 0,
        numDrawPreviousOrLaterTLayers = 0,
        adaptScaleToScreen = true,
        maskTile = false,
        underzoom = false,  // Don't draw below min zoom
        overzoom = false    // Don't draw above max zoom
    )
}

WMTS Support

The SDK can automatically create layer configurations from WMTS capabilities:
val resource = WmtsCapabilitiesResource.create(xml)
val layer = resource.createLayer("identifier", dataLoader)
mapView.addLayer(layer.asLayerInterface())

Best Practices

  • Define zoom levels that match your tile service exactly
  • Include appropriate overlap with numDrawPreviousLayers for smooth transitions
  • Use adaptScaleToScreen: true for consistent appearance across devices
  • Consider enabling underzoom and overzoom for better UX at extreme zoom levels
  • Limit numDrawPreviousLayers to reduce overdraw (2-3 is usually sufficient)
  • Set appropriate bounds to avoid loading tiles outside your area of interest
  • Use zoomLevelScaleFactor to fine-tune tile loading behavior
  • Test with different network conditions to ensure smooth tile loading
  • Ensure bounds match your coordinate system
  • Convert coordinates correctly when mixing different projections
  • Test boundary conditions at the edges of your valid area
  • Document which coordinate system your layer uses

Styling

Customize colors, patterns, and visual properties

Touch Handlers

Customize gesture handling and interactions

Build docs developers (and LLMs) love