The Layer & Map Configuration feature provided MMGIS with a comprehensive system for defining, styling, and managing geospatial layers across multiple rendering engines (Leaflet for 2D maps and Cesium/Litho for 3D globes). This system enabled mission operators to configure complex layer hierarchies, apply sophisticated styling rules, manage visibility controls, and render legends dynamically based on layer properties and symbology.
The implemented layer configuration system supported:
- Multi-Layer Type Support: Vector (GeoJSON), raster/tile, image, model (3D), velocity fields, video, header (grouping), and data layers
- Hierarchical Organization: Nested layer groups with parent-child relationships and header-based organization
- Dynamic Styling: Property-based styling, gradient interpolation, discrete value mapping, and geologic pattern fills
- Visibility Management: Per-layer on/off toggles, opacity controls, z-index ordering, and visibility cutoff by zoom level
- Legend Generation: Automatic legend creation from CSV files or JSON configuration with support for continuous gradients, discrete symbols, and image-based legends
- Data Source Configuration: Flexible URL patterns, tile server integration, COG (Cloud Optimized GeoTIFF) support, and time-enabled data sources
- Cross-Engine Synchronization: Coordinated layer state between 2D (Leaflet) and 3D (Cesium/Litho) rendering engines
The layer configuration system was implemented through the Layers_ module (C:\Users\tsoliman\Documents\Projects\MMGIS\src\essence\Basics\Layers_\Layers_.js), which maintained several key data structures:
layers: {
data: {}, // Layer configurations by UUID
dataFlat: [], // Flattened array of all layers
layer: {}, // Leaflet/rendering layer instances
attachments: {}, // Sublayers (models, labels, pairings, etc.)
on: {}, // Layer visibility state (true/false)
opacity: {}, // Layer opacity values (0-1)
filters: {}, // Applied filter effects
nameToUUID: {}, // Name to UUID lookup
refreshIntervals: {} // Auto-refresh timers
}The system implemented support for the following layer types:
- vector: GeoJSON-based point, line, and polygon features with property-driven styling
- tile: XYZ tile layers (TMS, WMS formats) with optional DEM overlays
- image: Single georeferenced images with COG support and pixel value transformations
- model: 3D models (glTF/GLB) with position, rotation, and scale configuration
- velocity: Vector field visualization (streamlines, particles)
- video: Georeferenced video overlays
- vectortile: Mapbox Vector Tile (MVT) format support
- data: Pure data layers without visual representation
- header: Non-rendering organizational groups for layer hierarchies
Each layer was configured through a JSON object with the following structure:
{
"name": "layer-uuid",
"display_name": "Human Readable Name",
"type": "vector|tile|image|model|...",
"url": "path/to/data",
"visibility": true|false,
"opacity": 0.0-1.0,
"initialOpacity": 0.0-1.0,
"minZoom": 0,
"maxNativeZoom": 20,
"visibilitycutoff": 0,
"style": {
"color": "#RRGGBB",
"weight": 2,
"opacity": 1.0,
"fillColor": "#RRGGBB",
"fillOpacity": 0.5,
"radius": 8,
"colorProp": "property_name",
"weightProp": "property_name",
"fillColorProp": "property_name",
"fillOpacityProp": "property_name",
"radiusProp": "property_name",
"opacityProp": "property_name"
},
"legend": "path/to/legend.csv",
"variables": {
"legendOrientation": "vertical|horizontal",
"hideLegendLayerName": true|false
},
"time": {
"type": "global|local|none",
"start": "2020-01-01T00:00:00Z",
"end": "2020-12-31T23:59:59Z",
"startProp": "start_time",
"endProp": "end_time"
}
}The system implemented property-based styling through the prop: prefix convention:
// Configuration example
"style": {
"fillColor": "prop:temperature", // Color from 'temperature' property
"radius": "prop:magnitude" // Size from 'magnitude' property
}The LayerConstructors.js module processed these property references during feature rendering.
The implementation supported automatic styling based on legend configurations. When a legend defined property-value-to-color mappings with styleMatching: true, the rendering system automatically applied those styles to features:
Business Decision: Legend-based styling took priority over configured styles but not over feature-level properties.style overrides. This priority hierarchy balanced configurability with feature-specific customization needs.
Supported legend-based styling modes:
- Discrete Matching: Exact value matches for categorical data
- Continuous Interpolation: Gradient interpolation for numeric properties using RGB color space
- Multi-Stop Gradients: Complex color ramps with multiple color stops
Implementation in LayerConstructors.js (lines 230-393):
// Continuous interpolation example
if (shouldUseContinuous) {
const fillColorStops = numericEntries
.filter(entry => entry.color)
.map(entry => ({
position: (entry.numericValue - minValue) / (maxValue - minValue),
color: entry.color
}))
const interpolatedFillColor = interpolateMultipleColors(
fillColorStops,
featureValue,
minValue,
maxValue
)
}The system included specialized support for FGDC geologic pattern symbols through the LayerGeologic module, enabling standard geological map symbologies for polygon features.
The Legend Tool (C:\Users\tsoliman\Documents\Projects\MMGIS\src\essence\Tools\Legend\LegendTool.js) provided dynamic legend generation and rendering.
Legends were configured through two methods:
- CSV Files: External CSV files with columns for shape, color, strokecolor, value
- JSON Arrays: Inline legend arrays in layer configuration under
_legendproperty
Legend CSV format:
shape,color,strokecolor,value,propertyName,propertyValue,styleMatching,hideFromLegend
circle,#FF0000,#000000,High Temperature,temperature,30,true,false
square,#00FF00,#000000,Medium Temperature,temperature,20,true,false
rect,#0000FF,#000000,Low Temperature,temperature,10,true,false
continuous,#FF0000,,Max,temperature,40,true,false
continuous,#0000FF,,Min,temperature,0,true,falseSupported shape types:
- Primitive Shapes:
circle,square,rect- Rendered as colored divs - Image Markers: File paths to PNG/JPG/SVG images - Rendered as background images
- MDI Icons: Material Design Icon names (e.g.,
mdi-home) - Rendered using icon fonts - Continuous Gradients:
continuous- Rendered as color gradients with tick marks - Discrete Bands:
discreet- Rendered as stepped color bands
Implemented features in the Legend Tool:
- Orientation Support: Vertical (default) or horizontal legend layouts
- Dynamic Tick Marks: Positioned markers on continuous gradients for value reference
- Interactive Tooltips: Hover-based value display with interpolated continuous values
- Units Extraction: Automatic unit detection and display for consistent labeling
- Image Legends: Direct rendering of WMS GetLegendGraphic or static legend images
- Responsive Label Density: Automatic label reduction in horizontal mode to prevent overlap
- Layer Headers: Optional display of header layer names in legend hierarchy
Business Decision: The legend display order followed the layer hierarchy with optional header inclusion. Legends only appeared for visible layers, automatically updating on layer toggle events. This provided users with a current view of active symbology without cluttering the legend panel with inactive layers.
The layer toggle system was implemented through toggleLayer() and toggleLayerHelper() functions with the following behavior:
- Leaflet Layer Management: Added/removed layers from the map using
addLayer()/removeLayer() - Globe/Litho Synchronization: Coordinated 3D layer visibility through
Globe_.litho.addLayer()/removeLayer() - Sublayer Coordination: Managed attached sublayers (models, labels, uncertainty ellipses)
- Event Broadcasting: Notified subscribed tools via
_onLayerToggleSubscriptions - State Persistence: Maintained toggle state in
L_.layers.on[layerName]
The system maintained layer rendering order through z-index values:
// Z-index calculation (higher index = on top)
const zIndex = L_._layersOrdered.length + 1 - L_._layersOrdered.indexOf(layerName)
layer.setZIndex(zIndex)The _layersOrdered array preserved the configuration-defined layer order, ensuring consistent stacking.
Opacity management was implemented through setLayerOpacity() (lines 1671-1748):
- Range: 0.0 (transparent) to 1.0 (opaque)
- Fill Opacity Preservation: Separate tracking of
initialFillOpacityto maintain fill transparency ratios - Sublayer Propagation: Opacity changes cascaded to associated sublayers with appropriate modifiers
- Dual-Engine Updates: Synchronized opacity between Leaflet and Globe/Litho renderers
- Highlight Preservation: Active feature highlights were reapplied after opacity changes
The visibilitycutoff property controlled zoom-level-based layer visibility:
- Positive Values: Minimum zoom level (layer hidden when zoomed out beyond this level)
- Negative Values: Maximum zoom level (layer hidden when zoomed in beyond this level)
Business Decision: This approach allowed performance optimization by hiding detailed layers at inappropriate zoom levels while maintaining configuration simplicity with a single numeric parameter rather than separate min/max properties.
The system supported multiple URL patterns:
- Relative Paths: Resolved relative to mission directory (
L_.missionPath) - Absolute URLs: Full HTTP/HTTPS URLs for external resources
- COG Prefix:
COG:prefix routed tiles through tile server for Cloud Optimized GeoTIFF processing - Template Variables:
{z}/{x}/{y}for tile coordinates,{starttime}/{endtime}for temporal data
URL resolution logic in getUrl() (lines 247-276):
let nextUrl = url
if (nextUrl.startsWith('COG:')) {
nextUrl = nextUrl.slice(4)
wasCOG = true
}
if (!F_.isUrlAbsolute(nextUrl)) {
nextUrl = L_.missionPath + nextUrl
}
if (type === 'tile' && (layerData.throughTileServer || wasCOG)) {
// Route through tile server
nextUrl = `../../${nextUrl}` // or `/${nextUrl}` in Docker
}The throughTileServer flag enabled server-side tile processing:
- COG Support: On-the-fly tile generation from Cloud Optimized GeoTIFFs
- Format Transformation: Conversion between tile formats (TMS, WMS)
- DEM Processing: Digital elevation model tile serving with seam correction
Time configuration enabled temporal data filtering:
{
"time": {
"type": "global", // Controlled by TimeControl tool
"start": "2020-01-01T00:00:00Z",
"end": "2020-12-31T23:59:59Z",
"format": "YYYY-MM-DDTHH:mm:ss[Z]"
}
}For vector layers with type: "local", the system filtered features based on startProp and endProp timestamp properties.
The Layers Tool (C:\Users\tsoliman\Documents\Projects\MMGIS\src\essence\Tools\Layers\LayersTool.js) provided the user interface for layer management:
- Hierarchical Display: Tree-style presentation of layer groups and sublayers
- Toggle Controls: Checkboxes for layer visibility
- Opacity Sliders: Per-layer opacity adjustment (0-100%)
- Layer Actions: Download, filter, metadata viewing, time controls
- Search/Filter: Layer list filtering by name or properties
- Collapsible Groups: Expandable/collapsible header layers
Business Decision: The tool configuration included an expanded variable to control whether layer groups defaulted to expanded or collapsed state on load. Mission operators could set this based on the complexity of their layer hierarchies and typical user workflows.
The attachments system (L_.layers.attachments[layerName]) supported auxiliary layer elements:
- models: 3D models attached to vector features (e.g., spacecraft models at trajectory points)
- labels: Text labels for features with custom positioning
- pairings: Connecting lines between related features
- uncertainty_ellipses: Error ellipses for position uncertainty visualization
- image_overlays: Image overlays associated with features
Sublayer management functions:
toggleSublayer(): Show/hide specific sublayerssetSublayerOpacity(): Adjust sublayer transparencysyncSublayerData(): Synchronize sublayer data with parent layer changes
The system implemented COG support for efficient remote raster access:
- Lazy Loading: Only requested tiles were fetched from COG files
- Pixel Value Transformations: Custom functions for converting raw pixel values to colors
- Scale Generation: Automatic generation of color scales for COG layers
- Legend Integration: Dynamic legend creation from COG value ranges
Layer visual filters were supported through setLayerFilter():
- Brightness: Lightness adjustment (-1.0 to 1.0)
- Contrast: Contrast adjustment (0.0 to 2.0)
- Saturation: Color saturation (0.0 to 2.0)
- Blend Modes: Overlay, color, multiply blending
Filter state was tracked in L_.layers.filters[layerName] and synchronized across both rendering engines.
Layers could be configured with automatic refresh intervals:
{
"variables": {
"refreshInterval": 30000 // Milliseconds
}
}The system maintained refresh timers in L_.layers.refreshIntervals[layerName] and automatically reloaded layer data at the specified interval.
The layer creation process followed this pipeline:
- Configuration Parsing:
parseConfig()processed layer JSON and built data structures - Layer Ordering: Layers were added to
_layersOrderedin configuration order - Layer Creation:
Map_.makeLayer()instantiated Leaflet layer objects based on type - Style Application:
LayerConstructors.constructVectorLayer()applied styling rules - Legend Processing: CSV legends were loaded and parsed into
_legendarrays - Globe Synchronization: Matching layers were created in Globe/Litho engine
- Event Binding: Click handlers, hover behaviors, and popup bindings were attached
- Visibility Application: Initial visibility state was applied via
addVisible()
The dual rendering engine architecture required careful state synchronization:
// Example from toggleLayerHelper()
if (on) {
// Turn off
L_.Map_.rmNotNull(L_.layers.layer[s.name]) // Remove from Leaflet
L_.Globe_.litho.removeLayer(s.name) // Remove from Globe
} else {
// Turn on
L_.Map_.map.addLayer(L_.layers.layer[s.name]) // Add to Leaflet
L_.Globe_.litho.addLayer('vector', {...}) // Add to Globe
}State properties synchronized across engines:
- Visibility (on/off)
- Opacity
- Layer order (z-index / order array)
- Filter effects
- Time ranges
Implemented performance optimizations:
- Lazy Layer Creation: Layers were created on first toggle rather than at initialization
- Z-Index Batching:
orderedBringToFront()batch-updated z-indexes to minimize reflows - Debounced Opacity Updates: Opacity slider changes were debounced to reduce render cycles
- Feature Pooling: Reused Leaflet feature objects when reloading layers
- Visibility Culling: Layers outside
visibilitycutoffranges were not rendered
{
"name": "rover-traverses",
"display_name": "Rover Traverses",
"type": "vector",
"url": "Layers/Rover/traverses.geojson",
"visibility": true,
"opacity": 0.8,
"style": {
"color": "prop:sol",
"weight": "prop:confidence",
"fillColor": "#FFD700",
"fillOpacity": 0.3,
"radius": 6
},
"legend": "Layers/Rover/traverses_legend.csv",
"variables": {
"legendOrientation": "horizontal",
"useKeyAsName": "sol"
},
"time": {
"type": "local",
"startProp": "start_sol",
"endProp": "end_sol"
}
}{
"name": "mars-ctx-mosaic",
"display_name": "Mars CTX Mosaic",
"type": "tile",
"url": "Layers/CTX/{z}/{x}/{y}.png",
"demtileurl": "Layers/MOLA/{z}/{x}/{y}.png",
"visibility": false,
"opacity": 1.0,
"minZoom": 5,
"maxNativeZoom": 15,
"visibilitycutoff": 8,
"tileformat": "tms",
"throughTileServer": false
}{
"name": "msl-model",
"display_name": "Mars Science Laboratory",
"type": "model",
"url": "Models/MSL/msl.glb",
"visibility": true,
"initialOpacity": 1.0,
"position": {
"longitude": 137.4417,
"latitude": -4.5895,
"elevation": 100
},
"scale": 10,
"rotation": {
"x": 0,
"y": 45,
"z": 0
}
}{
"name": "orbital-imagery",
"display_name": "Orbital Imagery",
"type": "header",
"visibility": true,
"sublayers": [
{
"name": "hirise-dtm",
"display_name": "HiRISE DTM",
"type": "tile",
"url": "Layers/HiRISE/DTM/{z}/{x}/{y}.png",
"visibility": false,
"opacity": 0.7
},
{
"name": "ctx-mosaic",
"display_name": "CTX Mosaic",
"type": "tile",
"url": "Layers/CTX/{z}/{x}/{y}.png",
"visibility": true,
"opacity": 1.0
}
]
}The layer configuration system integrated with multiple tools:
- Legend Tool: Consumed
_legendarrays for dynamic legend rendering - Layers Tool: Provided UI for layer management and configuration viewing
- Info Tool: Accessed layer properties for feature information display
- Measure Tool: Used DEM layers for elevation profiling
- Draw Tool: Interacted with vector layers for feature editing
- Identifier Tool: Queried raster layer pixel values based on configuration
The system exposed several API methods for programmatic layer control:
// Toggle layer visibility
L_.toggleLayer(layerObj)
// Set layer opacity (0.0 - 1.0)
L_.setLayerOpacity(layerName, opacity)
// Get layer opacity
L_.getLayerOpacity(layerName)
// Set layer filter effects
L_.setLayerFilter(layerName, filter, value)
// Reload layer data
L_.reloadLayer(layerName)
// Get layer by name/UUID
L_.asLayerUUID(nameOrUUID)
// Subscribe to layer toggle events
L_.subscribeOnLayerToggle(fid, callback)Layer configurations could be updated dynamically through WebSocket messages, enabling real-time layer updates and collaborative configuration changes.
- Memory Constraints: Large vector datasets (>10,000 features) could impact performance on resource-limited devices
- Legend Size Limits: Legends with >50 discrete entries experienced rendering performance degradation
- Z-Index Range: Maximum of ~10,000 layers due to CSS z-index limitations
- Globe-Map Feature Parity: Not all Leaflet layer types had equivalent Globe/Litho implementations
- UUID Uniqueness: Layer names (UUIDs) must be unique across the entire configuration
- URL Length Limits: Very long tile URL templates could exceed browser URL length limits
- Property Name Collisions: Property-based styling required non-conflicting property names
- Legend File Size: Large legend CSV files (>1MB) caused initialization delays
Business Decision: These constraints were documented in the configuration guide but not enforced programmatically. Mission operators were expected to follow best practices to avoid these issues.
The layer configuration system was tested through:
- Unit Tests: Individual functions for layer parsing, styling, and state management
- Integration Tests: Layer creation pipeline, engine synchronization, and tool integration
- Visual Tests: Screenshot-based testing of layer rendering and legend display
- Performance Tests: Load testing with large layer counts and complex hierarchies
- Cross-Browser Tests: Compatibility testing across Chrome, Firefox, Safari, and Edge
- Leaflet: 2D map rendering engine
- Cesium/Litho: 3D globe rendering engine (custom fork)
- D3.js: Legend rendering and DOM manipulation
- jQuery: DOM traversal and event handling
- Turf.js: Spatial analysis for legend-based styling
- Layers_: Core layer management module
- LayerConstructors: Layer creation and styling pipeline
- Map_: Leaflet map wrapper
- Globe_: Cesium/Litho globe wrapper
- Formulae_: Utility functions for parsing and calculations
- ToolController_: Tool lifecycle management
Layer configuration inputs were validated for:
- Valid JSON structure
- Allowed layer types
- URL format validation
- Property name sanitization
Legend labels and layer names were sanitized before rendering to prevent XSS attacks:
// Example from annotation creation
`${feature.properties.name.replace(/[<>;{}]/g, '')}`Layer visibility could be restricted based on user permissions, though this was handled at the configuration level rather than within the layer system itself.
Based on the implemented system, potential future enhancements could include:
- Layer Presets: Saved layer visibility/opacity configurations for quick switching
- Dynamic Symbology: Real-time legend updates based on feature data changes
- Advanced Filtering: SQL-like queries for complex feature filtering
- Layer Analytics: Statistics and metrics on layer usage and performance
- Version Control: Track configuration changes over time with rollback capability
- Cloud Storage: Direct integration with cloud storage for layer data (S3, GCS)
- Streaming Layers: WebSocket-based real-time data streaming for live layers
This specification represents the implemented state of the Layer & Map Configuration feature in the MMGIS system.