UNPKG

@allmaps/annotation

Version:

Functions for generating and parsing IIIF georeference annotations

115 lines (114 loc) 3.73 kB
import { z } from 'zod'; export function oneOrMany(schema) { return z .union([z.array(schema), schema]) .transform((value) => (Array.isArray(value) ? value : [value])); } export function parseIfValid(schema) { return z.unknown().transform((value) => { const result = schema.safeParse(value); if (result.success) { return result.data; } }); } // Copied from presentation.3.ts in @allmaps/iiif-parser const SingleValueSchema = z.union([z.string(), z.number(), z.boolean()]); export const LanguageValueSchema = z.record(z.string(), SingleValueSchema.array()); export const PointSchema = z.tuple([z.number(), z.number()]); export const PointGeometrySchema = z.object({ type: z.literal('Point'), coordinates: PointSchema }); export const ResourceMaskSchema = PointSchema.array().min(3); const ImageServices = [ 'ImageService1', 'ImageService2', 'ImageService3' ]; const ResourceTypes = [...ImageServices, ...['Canvas']]; export const ImageServiceSchema = z.enum(ImageServices); export const ResourceTypeSchema = z.enum(ResourceTypes); export const PartOfItemSchema = z.object({ id: z.string().url(), type: z.string(), label: LanguageValueSchema.optional(), get partOf() { return z.array(PartOfItemSchema).optional(); } }); export const PartOfSchema = oneOrMany(PartOfItemSchema); export const HomepageItemSchema = z.object({ id: z.string().url(), type: z.string().optional(), label: LanguageValueSchema.optional(), format: z.string().optional(), language: z.union([z.string(), z.array(z.string())]).optional() }); const OptionalHomepageItemSchema = z.union([ HomepageItemSchema, z.unknown().transform(() => undefined) ]); export const HomepageSchema = oneOrMany(OptionalHomepageItemSchema).transform((values) => values.filter((value) => value !== undefined)); export const ProviderItemSchema = z.object({ id: z.string().url().optional(), label: LanguageValueSchema.optional(), homepage: HomepageSchema.transform((values) => values.length > 0 ? values : undefined).optional() }); export const ProviderSchema = oneOrMany(ProviderItemSchema); const ValidTransformationSchema = z.object({ type: z.enum([ 'helmert', 'polynomial', 'thinPlateSpline', 'projective', 'straight', 'linear' ]), options: z.looseObject({}).optional() }); function parseInvalidTransformation(val) { // Also allow values like: // - thin-plate-spline // - thinplatespline // - polynomial1 const valLowerCase = val.toLowerCase(); if (valLowerCase === 'thinplatespline' || valLowerCase === 'thin-plate-spline') { return { type: 'thinPlateSpline' }; } else if (valLowerCase === 'polynomial1') { return { type: 'polynomial', options: { order: 1 } }; } else if (valLowerCase === 'polynomial2') { return { type: 'polynomial', options: { order: 2 } }; } } export const TransformationSchema = parseIfValid(ValidTransformationSchema).or(z.unknown().transform((val) => { if (typeof val === 'string') { return parseInvalidTransformation(val); } if (val && typeof val === 'object' && 'type' in val && typeof val.type === 'string') { return parseInvalidTransformation(val.type); } })); export const ProjectionSchema = z.object({ id: z.string().url().optional(), name: z.string().optional(), definition: z.union([z.string(), z.unknown()]) }); export const ContextSchema = z.union([ z.string().url().array(), z.string().url() ]);