Declarative JSON-based layer and visualization configuration system enabling dynamic deck.gl applications that can be configured without code changes.
Main class for converting JSON configurations into deck.gl objects.
/**
* Converts JSON configurations to deck.gl objects
* Enables declarative, data-driven visualization setup
*/
class JSONConverter {
constructor(configuration?: JSONConfiguration);
/** Convert JSON object to deck.gl objects */
convert(json: any, options?: ConvertOptions): any;
/** Update converter configuration */
setConfiguration(configuration: JSONConfiguration): void;
/** Get current configuration */
getConfiguration(): JSONConfiguration;
}
interface ConvertOptions {
/** Relative path for resolving resources */
relativePath?: string;
/** Custom context for expression evaluation */
context?: any;
/** Enable strict mode */
strict?: boolean;
}Basic Usage:
import { JSONConverter } from '@deck.gl/json';
const jsonConverter = new JSONConverter();
const deckConfig = {
"layers": [
{
"@@type": "ScatterplotLayer",
"id": "scatter",
"data": "https://example.com/data.json",
"getPosition": "@@=properties.coordinates",
"getRadius": 100,
"getFillColor": [255, 0, 0]
}
],
"initialViewState": {
"longitude": -122.4,
"latitude": 37.8,
"zoom": 10
}
};
const deckProps = jsonConverter.convert(deckConfig);
// Use deckProps with Deck constructorConfiguration object defining how JSON should be converted to deck.gl objects.
/**
* Configuration for JSON conversion
* Defines classes, functions, and enums available in JSON
*/
class JSONConfiguration {
constructor(options?: JSONConfigurationOptions);
/** Merge with another configuration */
merge(configuration: JSONConfiguration): JSONConfiguration;
/** Get available classes */
getClasses(): {[key: string]: any};
/** Get available functions */
getFunctions(): {[key: string]: Function};
/** Get available enums */
getEnums(): {[key: string]: any};
}
interface JSONConfigurationOptions {
/** Available classes for @@type */
classes?: {[key: string]: any};
/** Available functions for @@= expressions */
functions?: {[key: string]: Function};
/** Available enums */
enums?: {[key: string]: any};
/** Constants */
constants?: {[key: string]: any};
}Base class for loading remote data and resources.
/**
* Base transport class for loading resources
* Handles data fetching for JSON configurations
*/
abstract class Transport {
constructor(options?: TransportOptions);
/** Load resource by URL */
abstract load(url: string, options?: LoadOptions): Promise<any>;
/** Check if URL is supported */
abstract supports(url: string): boolean;
}
interface TransportOptions {
/** Request timeout in milliseconds */
timeout?: number;
/** Custom headers */
headers?: {[key: string]: string};
/** Credentials mode */
credentials?: 'same-origin' | 'include' | 'omit';
}
interface LoadOptions {
/** Response type */
responseType?: 'json' | 'text' | 'arrayBuffer';
/** Transform response */
transform?: (data: any) => any;
}Define layers using JSON with special syntax:
{
"layers": [
{
"@@type": "ScatterplotLayer",
"id": "points",
"data": "https://api.example.com/points.json",
"getPosition": "@@=coordinates",
"getRadius": "@@=Math.sqrt(population) * 10",
"getFillColor": {
"@@function": "colorScale",
"@@arguments": ["@@=properties.category"]
},
"pickable": true
},
{
"@@type": "GeoJsonLayer",
"id": "polygons",
"data": {
"@@type": "FeatureCollection",
"features": "@@=polygonFeatures"
},
"filled": true,
"getFillColor": [128, 200, 255, 100]
}
]
}JSON configurations support expression evaluation:
{
"getRadius": "@@=Math.max(10, properties.size * 2)",
"getFillColor": "@@=properties.value > 100 ? [255, 0, 0] : [0, 255, 0]",
"data": "@@=context.dataUrl + '?filter=' + encodeURIComponent(filters.category)"
}Call predefined functions with arguments:
{
"getFillColor": {
"@@function": "interpolateColor",
"@@arguments": [
"@@=properties.value",
{"domain": [0, 100], "range": [[255, 0, 0], [0, 255, 0]]}
]
}
}Use conditional expressions for dynamic behavior:
{
"visible": "@@=zoom > 8",
"getRadius": "@@=zoom < 10 ? 5 : 10",
"layers": {
"@@if": "showHeatmap",
"@@then": [
{"@@type": "HeatmapLayer", "id": "heat", "data": "@@=heatmapData"}
],
"@@else": [
{"@@type": "ScatterplotLayer", "id": "scatter", "data": "@@=scatterData"}
]
}
}Create custom configurations with your own classes and functions:
import { JSONConverter, JSONConfiguration } from '@deck.gl/json';
import { ScatterplotLayer, HeatmapLayer } from 'deck.gl';
const customConfig = new JSONConfiguration({
classes: {
ScatterplotLayer,
HeatmapLayer,
CustomLayer: MyCustomLayer
},
functions: {
// Custom color scaling function
colorScale: (value, domain, range) => {
const normalized = (value - domain[0]) / (domain[1] - domain[0]);
return range.map((color, i) =>
color.map((channel, j) =>
range[0][j] + normalized * (range[1][j] - range[0][j])
)
)[0];
},
// Custom data filtering
filterData: (data, predicate) => {
return data.filter(predicate);
}
},
enums: {
COORDINATE_SYSTEM: {
LNGLAT: 1,
CARTESIAN: 0
}
}
});
const converter = new JSONConverter(customConfig);Load and transform data dynamically:
const deckConfig = {
"layers": [
{
"@@type": "ScatterplotLayer",
"id": "dynamic-points",
"data": {
"@@type": "RemoteData",
"url": "https://api.example.com/points",
"transform": "@@=response.features.map(f => ({...f.properties, coordinates: f.geometry.coordinates}))"
},
"getPosition": "@@=coordinates",
"getRadius": 50
}
]
};Use templates for reusable configurations:
{
"templates": {
"defaultScatter": {
"@@type": "ScatterplotLayer",
"radiusMinPixels": 3,
"radiusMaxPixels": 30,
"pickable": true
}
},
"layers": [
{
"@@template": "defaultScatter",
"id": "cities",
"data": "cities.json",
"getPosition": "@@=coordinates",
"getRadius": "@@=population / 1000"
},
{
"@@template": "defaultScatter",
"id": "airports",
"data": "airports.json",
"getPosition": "@@=location",
"getFillColor": [255, 165, 0]
}
]
}Update configurations dynamically:
import { JSONConverter } from '@deck.gl/json';
class DynamicVisualization {
private converter: JSONConverter;
private deck: Deck;
constructor() {
this.converter = new JSONConverter();
this.deck = new Deck({
container: 'map',
controller: true
});
}
updateConfig(newConfig: any) {
const deckProps = this.converter.convert(newConfig, {
context: {
timestamp: Date.now(),
userPreferences: this.getUserPreferences()
}
});
this.deck.setProps(deckProps);
}
private getUserPreferences() {
return {
colorScheme: 'dark',
showLabels: true,
animationSpeed: 1.0
};
}
}Helper functions for JSON configuration:
/**
* Convert JavaScript functions to JSON expressions
*/
function _convertFunctions(obj: any): any;
/**
* Parse expression strings into executable functions
*/
function _parseExpressionString(expression: string): Function;
/**
* Shallow comparison of objects for optimization
*/
function _shallowEqualObjects(obj1: any, obj2: any): boolean;Handle JSON configuration errors:
import { JSONConverter } from '@deck.gl/json';
const converter = new JSONConverter();
try {
const deckProps = converter.convert(jsonConfig, {
strict: true // Enable strict validation
});
} catch (error) {
if (error.message.includes('Unknown class')) {
console.error('Layer type not found:', error.message);
} else if (error.message.includes('Expression error')) {
console.error('Invalid expression:', error.message);
} else {
console.error('Configuration error:', error.message);
}
}// Cache converted configurations
const configCache = new Map();
function getCachedDeckProps(jsonConfig: any) {
const configHash = JSON.stringify(jsonConfig);
if (!configCache.has(configHash)) {
const deckProps = converter.convert(jsonConfig);
configCache.set(configHash, deckProps);
}
return configCache.get(configHash);
}