Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/schemas/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@
"apache-arrow": ">=15.0.0",
"global": "^4.3.0",
"keymirror": "^0.1.1",
"lodash": "4.17.21"
"lodash": "4.17.21",
"zod": "^3.24.2"
},
"nyc": {
"sourceMap": false,
Expand Down
83 changes: 83 additions & 0 deletions src/schemas/src/color.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// SPDX-License-Identifier: MIT
// Copyright contributors to the kepler.gl project

import {z} from 'zod';

const RGBColorComponent = z.number().min(0).max(255);

export const RGBColorSchema = z.union([
z.tuple([RGBColorComponent, RGBColorComponent, RGBColorComponent]),
z.tuple([RGBColorComponent, RGBColorComponent, RGBColorComponent, RGBColorComponent])
]);
export type RGBColorSchema = z.infer<typeof RGBColorSchema>;

export const HexColorSchema = z.coerce.string();
export type HexColorSchema = z.infer<typeof HexColorSchema>;

export const ColorMapSchema = z.array(
z.tuple([z.union([z.string(), z.number(), z.null(), z.array(z.coerce.string())]), HexColorSchema])
);
export type ColorMapSchema = z.infer<typeof ColorMapSchema>;

export const ColorLegendsSchema = z.record(z.string(), z.string());
export type ColorLegendsSchema = z.infer<typeof ColorLegendsSchema>;

export enum ColorRangeType {
sequential = 'sequential',
qualitative = 'qualitative',
diverging = 'diverging',
cyclical = 'cyclical',
custom = 'custom',
ordinal = 'ordinal',
customOrdinal = 'customOrdinal'
}

export const ColorRangeTypeZod = z.preprocess((type: any) => {
if (type === 'singlehue') {
type = ColorRangeType.sequential;
}
return type;
}, z.nativeEnum(ColorRangeType).default(ColorRangeType.sequential));
export type ColorRangeTypeZod = z.infer<typeof ColorRangeTypeZod>;

const COLOR_RANGE_TYPES = Object.values(ColorRangeType).join(', ');
const COLOR_RANGE_TYPE_DESC = `The type of the color range. Must be one of: ${COLOR_RANGE_TYPES}`;

export const ColorRangeSchema = z.preprocess(
(colorRange: any) => {
let next = colorRange;
if (colorRange.colorMap === null) {
next = {...next};
delete next.colorMap;
}
return next;
},
z.object({
name: z.string().describe('The name of the color range.').default('Unnamed'),
type: ColorRangeTypeZod.describe(COLOR_RANGE_TYPE_DESC),
category: z.string().default('Unnamed'),
colors: z.array(HexColorSchema),
reversed: z.boolean().optional(),
colorMap: ColorMapSchema.optional(),
colorLegends: ColorLegendsSchema.optional()
})
);
export type ColorRangeSchema = z.infer<typeof ColorRangeSchema>;

export const DEFAULT_COLOR_RANGE: ColorRangeSchema = {
name: 'Uber Viz Qualitative',
type: ColorRangeType.qualitative,
category: 'Uber',
colors: [
'#12939A',
'#DDB27C',
'#88572C',
'#FF991F',
'#F15C17',
'#223F9A',
'#DA70BF',
'#125C77',
'#4DC19C',
'#776E57'
]
};
44 changes: 44 additions & 0 deletions src/schemas/src/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// SPDX-License-Identifier: MIT
// Copyright contributors to the kepler.gl project

import {z} from 'zod';

const TIME_INTERVALS_KEYS = [
'year',
'month',
'week',
'day',
'hour',
'minute',
'second',
'millisecond'
] as const;

export const TIME_INTERVAL_REGEX = new RegExp(`^([0-9]+)-(${TIME_INTERVALS_KEYS.join('|')})$`);

export const TIME_INTERVAL_DESC =
`Should be in the form (number)-(interval), where interval is one of: ` +
`${TIME_INTERVALS_KEYS.join(', ')}, e.g 1-day, 2-week, 3-month, 4-year`;

export const TimeIntervalSchema = z.string().regex(TIME_INTERVAL_REGEX);

export enum AggregationKey {
COUNT = 'COUNT',
SUM = 'SUM',
MEAN = 'MEAN',
MAX = 'MAX',
MIN = 'MIN',
DEVIATION = 'DEVIATION',
VARIANCE = 'VARIANCE',
MEDIAN = 'MEDIAN',
P05 = 'P05',
P25 = 'P25',
P50 = 'P50',
P75 = 'P75',
P95 = 'P95',
MODE = 'MODE',
UNIQUE = 'UNIQUE',
MERGE = 'MERGE'
}
export const AggregationKeySchema = z.nativeEnum(AggregationKey);
export type AggregationKeySchema = z.infer<typeof AggregationKeySchema>;
54 changes: 54 additions & 0 deletions src/schemas/src/layers/3d-layer-schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: MIT
// Copyright contributors to the kepler.gl project

import {z} from 'zod';

import {
BaseLayerSchema,
LayerType,
BaseLayerConfigSchema,
OpacitySchema,
VisualChannelFieldSchema,
ColorScale
} from './common';
import {ColorRangeSchema} from '../color';

export const _3DLayerSchema = BaseLayerSchema.extend({
type: z.literal(LayerType.enum['3D']),
config: BaseLayerConfigSchema.extend({
columns: z.object({
lat: z.string(),
lng: z.string(),
altitude: z.string().nullable().optional()
}),
visConfig: z
.object({
opacity: OpacitySchema,
colorRange: ColorRangeSchema.optional(),
sizeScale: z.number().gte(0).lte(1000).default(10),
angleX: z.number().gte(0).lte(360).default(0),
angleY: z.number().gte(0).lte(360).default(0),
angleZ: z.number().gte(0).lte(360).default(0),
scenegraph: z.string().default('default-model'),
scenegraphColorEnabled: z.boolean().default(false),
scenegraphColor: z
.array(z.union([z.number(), z.number(), z.number()]))
.min(3)
.max(3)
.optional()
.nullable(),
scenegraphCustomModelUrl: z.string().default('')
})

.optional()
}),
visualChannels: z.object({
colorField: VisualChannelFieldSchema.optional().nullable(),
colorScale: ColorScale.optional().describe('Scale is based on colorField type.').optional(),
sizeField: VisualChannelFieldSchema.optional().nullable(),
sizeScale: z
.enum(['point', 'linear', 'sqrt', 'log'])
.describe('Scale is based on sizeField type.')
.optional()
})
});
65 changes: 65 additions & 0 deletions src/schemas/src/layers/arc-layer-schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// SPDX-License-Identifier: MIT
// Copyright contributors to the kepler.gl project

import {z} from 'zod';

import {
BaseLayerConfigSchema,
OpacitySchema,
BaseLayerSchema,
LayerType,
VisualChannelFieldSchema,
ColorScale
} from './common';
import {ColorRangeSchema, RGBColorSchema} from '../color';

const BaseArcLayerConfigSchema = BaseLayerConfigSchema.extend({
visConfig: z
.object({
opacity: OpacitySchema,
thickness: z.number().gte(0).lte(100).default(2),
colorRange: ColorRangeSchema.optional(),
sizeRange: z.array(z.number().gte(0).lte(200)).default([0, 10]),
targetColor: z
.array(z.union([z.number(), z.number(), z.number()]))
.min(3)
.max(3)
.nullable()
.optional()
})
.optional(),
highlightColor: RGBColorSchema.optional().nullable().describe('Highlight color')
});

export const ArcLayerSchema = BaseLayerSchema.extend({
type: z.literal(LayerType.enum.arc),
config: z.union([
BaseArcLayerConfigSchema.extend({
columnMode: z.literal('points').default('points').describe('Column mode'),
columns: z.object({
lat0: z.string(),
lng0: z.string(),
lat1: z.string(),
lng1: z.string()
})
}),
BaseArcLayerConfigSchema.extend({
columnMode: z.literal('neighbors').describe('Column mode'),
columns: z.object({
lat: z.string(),
lng: z.string(),
neighbors: z.string()
})
})
]),
visualChannels: z.object({
colorField: VisualChannelFieldSchema.optional().nullable(),
colorScale: ColorScale.optional().describe('Scale is based on colorField type.').optional(),
sizeField: VisualChannelFieldSchema.optional().nullable(),
sizeScale: z
.enum(['point', 'linear', 'sqrt', 'log'])
.describe('Scale is based on sizeField type.')
.optional()
})
});
export type ArcLayerSchema = z.infer<typeof ArcLayerSchema>;
38 changes: 38 additions & 0 deletions src/schemas/src/layers/cluster-layer-schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// SPDX-License-Identifier: MIT
// Copyright contributors to the kepler.gl project

import {z} from 'zod';

import {
BaseLayerSchema,
LayerType,
BaseLayerConfigSchema,
OpacitySchema,
AggregationModeSchema,
VisualChannelFieldSchema,
ColorScale
} from './common';
import {ColorRangeSchema} from '../color';

export const ClusterLayerSchema = BaseLayerSchema.extend({
type: z.literal(LayerType.enum.cluster),
config: BaseLayerConfigSchema.extend({
columns: z.object({lat: z.string(), lng: z.string()}),
visConfig: z
.object({
opacity: OpacitySchema,
clusterRadius: z.number().gte(1).lte(500).default(40),
colorRange: ColorRangeSchema.optional(),
radiusRange: z.array(z.number().gte(0).lte(150)).default([1, 40]),
colorAggregation: AggregationModeSchema.optional()
})

.optional()
}),
visualChannels: z.object({
colorField: VisualChannelFieldSchema.optional().nullable(),
colorScale: ColorScale.optional().describe('Scale is based on colorField type.').optional()
})
});

export type ClusterLayerSchema = z.infer<typeof ClusterLayerSchema>;
Loading
Loading