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.0 f ;
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.0 f ) {
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.0 f , 1.0 f ); }
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
Return early from update() when not visible
Always check isVisible and isPaused at the start of update() to avoid unnecessary processing.
Return empty vectors when nothing to render
Don’t create render passes when the layer has no content. Return empty vectors from buildRenderPasses().
Clean up resources in onRemoved()
Release all GPU resources, cancel pending network requests, and clear references in onRemoved() to prevent memory leaks.
Use render passes efficiently
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.
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.0 f ;
float alpha = 1.0 f ;
bool isVisible = true ;
public:
void update () override {
if ( ! isVisible) return ;
time += 0.016 f ; // ~60 FPS
}
std :: vector < std :: shared_ptr < RenderPassInterface >> buildRenderPasses () override {
if ( ! isVisible || alpha <= 0.0 f ) 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