Skip to main content

Overview

The Open Mobile Maps SDK allows you to create custom layers by implementing the LayerInterface. This enables you to render custom graphics, handle specialized data sources, or create unique visualization types beyond the built-in layer types.

LayerInterface

All layers must implement the LayerInterface:
class LayerInterface {
public:
    virtual ~LayerInterface() = default;
    
    // Lifecycle methods
    virtual void onAdded(const std::shared_ptr<MapInterface>& mapInterface, 
                        int32_t layerIndex) = 0;
    virtual void onRemoved() = 0;
    virtual void update() = 0;
    
    // Rendering
    virtual std::vector<std::shared_ptr<RenderPassInterface>> buildRenderPasses() = 0;
    virtual std::vector<std::shared_ptr<ComputePassInterface>> buildComputePasses() = 0;
    
    // State control
    virtual void pause() = 0;
    virtual void resume() = 0;
    virtual void hide() = 0;
    virtual void show() = 0;
    
    // Visual properties
    virtual void setAlpha(float alpha) = 0;
    virtual float getAlpha() = 0;
    virtual void setScissorRect(const std::optional<RectI>& scissorRect) = 0;
    
    // Advanced features
    virtual void setMaskingObject(const std::shared_ptr<MaskingObjectInterface>& maskingObject) = 0;
    virtual void enableAnimations(bool enabled) = 0;
    virtual LayerReadyState isReadyToRenderOffscreen() = 0;
    virtual void forceReload() = 0;
    virtual void setErrorManager(const std::shared_ptr<ErrorManager>& errorManager) = 0;
    virtual void setPrimaryRenderTarget(const std::shared_ptr<RenderTargetInterface>& target) = 0;
};

Basic Custom Layer Example

Here’s a simple custom layer that renders a colored overlay:
class CustomOverlayLayer : public LayerInterface {
private:
    std::shared_ptr<MapInterface> mapInterface;
    int32_t layerIndex;
    float alpha = 1.0f;
    bool isVisible = true;
    bool isPaused = false;
    
public:
    void onAdded(const std::shared_ptr<MapInterface>& map, int32_t index) override {
        mapInterface = map;
        layerIndex = index;
        // Initialize resources
    }
    
    void onRemoved() override {
        // Clean up resources
        mapInterface = nullptr;
    }
    
    void update() override {
        if (isPaused || !isVisible) return;
        // Update layer state before rendering
    }
    
    std::vector<std::shared_ptr<RenderPassInterface>> buildRenderPasses() override {
        if (!isVisible || alpha <= 0.0f) {
            return {};
        }
        // Build and return render passes
        return {createRenderPass()};
    }
    
    std::vector<std::shared_ptr<ComputePassInterface>> buildComputePasses() override {
        return {}; // No compute passes needed
    }
    
    void pause() override { isPaused = true; }
    void resume() override { isPaused = false; }
    void hide() override { isVisible = false; }
    void show() override { isVisible = true; }
    
    void setAlpha(float a) override { alpha = std::clamp(a, 0.0f, 1.0f); }
    float getAlpha() override { return alpha; }
    
    void setScissorRect(const std::optional<RectI>& rect) override {
        // Implement scissor rectangle clipping if needed
    }
    
    void setMaskingObject(const std::shared_ptr<MaskingObjectInterface>& mask) override {
        // Implement masking if needed
    }
    
    void enableAnimations(bool enabled) override {
        // Enable/disable layer animations
    }
    
    LayerReadyState isReadyToRenderOffscreen() override {
        return LayerReadyState::READY;
    }
    
    void forceReload() override {
        // Implement reload logic
    }
    
    void setErrorManager(const std::shared_ptr<ErrorManager>& manager) override {
        // Store error manager for error reporting
    }
    
    void setPrimaryRenderTarget(const std::shared_ptr<RenderTargetInterface>& target) override {
        // Set render target if needed
    }
};

Layer Lifecycle

Adding a Layer

auto customLayer = std::make_shared<CustomOverlayLayer>();
mapInterface->addLayer(customLayer);

// The layer's onAdded() method is automatically called

Removing a Layer

mapInterface->removeLayer(customLayer);

// The layer's onRemoved() method is automatically called

Update Cycle

The update() method is called every frame before rendering:
void CustomLayer::update() override {
    // Update layer state
    // Fetch new data if needed
    // Prepare objects for rendering
    // Update animations
}
The update() method is called after the camera’s update() method, so you can safely access the current camera state.

Render Passes

Layers render content through RenderPassInterface objects:
class CustomRenderPass : public RenderPassInterface {
public:
    void execute() override {
        // Execute rendering commands
        // Bind shaders
        // Set uniforms
        // Draw geometry
    }
};

std::vector<std::shared_ptr<RenderPassInterface>> CustomLayer::buildRenderPasses() override {
    if (!isVisible) return {};
    
    auto pass = std::make_shared<CustomRenderPass>();
    return {pass};
}
Return an empty vector from buildRenderPasses() when the layer has nothing to render. This avoids unnecessary render pass setup.

Compute Passes

For GPU-accelerated data processing:
std::vector<std::shared_ptr<ComputePassInterface>> CustomLayer::buildComputePasses() override {
    if (!needsCompute) return {};
    
    auto computePass = std::make_shared<CustomComputePass>();
    return {computePass};
}

Layer State Management

Visibility Control

// Hide layer without removing it
layer->hide();

// Show layer again
layer->show();

// Check state in update()
void update() override {
    if (!isVisible) return;
    // ... update logic
}

Pause/Resume

// Pause updates (e.g., when app goes to background)
layer->pause();

// Resume updates
layer->resume();

Alpha Blending

// Animate layer opacity
auto fadeIn = std::make_shared<DoubleAnimation>(
    1000, 0.0, 1.0,
    InterpolatorFunction::EaseOut,
    [layer](double alpha) {
        layer->setAlpha(static_cast<float>(alpha));
    }
);
fadeIn->start();

Advanced Features

Masking

Restrict rendering to specific regions:
void CustomLayer::setMaskingObject(
    const std::shared_ptr<MaskingObjectInterface>& mask
) override {
    this->maskingObject = mask;
    // Use mask in render pass to clip geometry
}

Scissor Rectangle

Clip rendering to a screen rectangle:
void CustomLayer::setScissorRect(const std::optional<RectI>& rect) override {
    scissorRect = rect;
    // Apply scissor test in render pass
}

Error Management

void CustomLayer::setErrorManager(
    const std::shared_ptr<ErrorManager>& manager
) override {
    errorManager = manager;
}

// Report errors during rendering or data loading
void reportError(const std::string& message) {
    if (errorManager) {
        errorManager->logError("CustomLayer", message);
    }
}

Ready State

Indicate when layer is ready for offscreen rendering:
LayerReadyState CustomLayer::isReadyToRenderOffscreen() override {
    if (dataLoading) {
        return LayerReadyState::NOT_READY;
    } else if (dataFailed) {
        return LayerReadyState::ERROR;
    }
    return LayerReadyState::READY;
}

Layer Objects

Many layer types use LayerObjectInterface for individual renderable items:
class CustomLayerObject : public LayerObjectInterface {
public:
    virtual std::vector<std::shared_ptr<RenderPassInterface>> buildRenderPasses() = 0;
    virtual void update() = 0;
    // ... other methods
};
Example usage in a custom layer:
class ObjectBasedLayer : public LayerInterface {
private:
    std::vector<std::shared_ptr<CustomLayerObject>> objects;
    
public:
    void addObject(std::shared_ptr<CustomLayerObject> obj) {
        objects.push_back(obj);
    }
    
    void update() override {
        for (auto& obj : objects) {
            obj->update();
        }
    }
    
    std::vector<std::shared_ptr<RenderPassInterface>> buildRenderPasses() override {
        std::vector<std::shared_ptr<RenderPassInterface>> passes;
        for (auto& obj : objects) {
            auto objPasses = obj->buildRenderPasses();
            passes.insert(passes.end(), objPasses.begin(), objPasses.end());
        }
        return passes;
    }
};

Accessing Map Context

Use the MapInterface to access map state:
void CustomLayer::update() override {
    auto camera = mapInterface->getCamera();
    auto zoom = camera->getZoom();
    auto center = camera->getCenterPosition();
    auto bounds = camera->getVisibleRect();
    
    // Use map state to determine what to render
    updateVisibleObjects(bounds, zoom);
}

Indexed Layers

For layers that need position in the layer stack:
void CustomLayer::onAdded(
    const std::shared_ptr<MapInterface>& map, 
    int32_t index
) override {
    mapInterface = map;
    layerIndex = index;
    
    // Get layer above or below
    if (index > 0) {
        auto layerBelow = mapInterface->getLayer(index - 1);
    }
}

Best Practices

Always check isVisible and isPaused at the start of update() to avoid unnecessary processing.
Don’t create render passes when the layer has no content. Return empty vectors from buildRenderPasses().
Release all GPU resources, cancel pending network requests, and clear references in onRemoved() to prevent memory leaks.
Batch similar rendering operations into single render passes to minimize GPU state changes.
Always apply the layer’s alpha value to your rendered content for consistent behavior with other layers.

Performance Considerations

  • Minimize work in update() - it’s called every frame
  • Cache computed results when possible
  • Use level-of-detail (LOD) based on zoom level
  • Cull objects outside the visible bounds
  • Consider using compute passes for heavy data processing

Example: Animated Grid Layer

class AnimatedGridLayer : public LayerInterface {
private:
    std::shared_ptr<MapInterface> mapInterface;
    float time = 0.0f;
    float alpha = 1.0f;
    bool isVisible = true;
    
public:
    void update() override {
        if (!isVisible) return;
        time += 0.016f; // ~60 FPS
    }
    
    std::vector<std::shared_ptr<RenderPassInterface>> buildRenderPasses() override {
        if (!isVisible || alpha <= 0.0f) return {};
        
        auto camera = mapInterface->getCamera();
        auto bounds = camera->getVisibleRect();
        
        // Create render pass with animated grid
        auto pass = createGridRenderPass(bounds, time, alpha);
        return {pass};
    }
    
    // ... implement other LayerInterface methods
};

Next Steps

Animations

Animate your custom layer properties

Coordinate Conversion

Convert coordinates for rendering

Build docs developers (and LLMs) love