UNPKG

@allmaps/annotation

Version:

Functions for generating and parsing IIIF georeference annotations

174 lines (173 loc) 5.74 kB
import { GeoreferencedMap1Schema, GeoreferencedMap2Schema, GeoreferencedMaps1Schema, GeoreferencedMaps2Schema } from './schemas/georeferenced-map.js'; import { isGeoreferencedMapsBeforeParse, isGeoreferencedMap2BeforeParse } from './before-parse.js'; import { isGeoreferencedMap2 } from './guards.js'; function generateSvgSelector(georeferencedMap) { let width; let height; let resourceMask; if (isGeoreferencedMap2(georeferencedMap)) { width = georeferencedMap.resource.width; height = georeferencedMap.resource.height; resourceMask = georeferencedMap.resourceMask; } else { width = georeferencedMap.image.width; height = georeferencedMap.image.height; resourceMask = georeferencedMap.pixelMask; } let svg = `<svg>`; if (width && height) { svg = `<svg width="${width}" height="${height}">`; } return { type: 'SvgSelector', value: `${svg}<polygon points="${resourceMask .map((point) => point.join(',')) .join(' ')}" /></svg>` }; } function generateSource(georeferencedMap) { let id; let type; let width; let height; let partOf; if (isGeoreferencedMap2(georeferencedMap)) { if (georeferencedMap.resource.type === 'Canvas') { // TODO: Don't know why TypeScript complains if I don't do this const source = { id: georeferencedMap.resource.id, type: georeferencedMap.resource.type, height: georeferencedMap.resource.height, width: georeferencedMap.resource.width, partOf: georeferencedMap.resource.partOf }; return source; } else { id = georeferencedMap.resource.id; type = georeferencedMap.resource.type; width = georeferencedMap.resource.width; height = georeferencedMap.resource.height; partOf = georeferencedMap.resource.partOf; } } else { id = georeferencedMap.image.uri; type = georeferencedMap.image.type; width = georeferencedMap.image.width; height = georeferencedMap.image.height; } return { id, type, height, width, partOf }; } function generateDates(georeferencedMap) { if (isGeoreferencedMap2(georeferencedMap)) { return { created: georeferencedMap.created, modified: georeferencedMap.modified }; } } function generateContext() { return [ 'http://iiif.io/api/extension/georef/1/context.json', 'http://iiif.io/api/presentation/3/context.json' ]; } function generateFeature(gcp) { let resourceCoords; let geoCoords; if ('resource' in gcp) { resourceCoords = gcp.resource; geoCoords = gcp.geo; } else { resourceCoords = gcp.image; geoCoords = gcp.world; } return { type: 'Feature', properties: { resourceCoords }, geometry: { type: 'Point', coordinates: geoCoords } }; } function generateGeoreferenceAnnotation(georeferencedMap) { const target = { type: 'SpecificResource', source: generateSource(georeferencedMap), selector: generateSvgSelector(georeferencedMap) }; let resourceCrs; if ('resourceCrs' in georeferencedMap) { resourceCrs = georeferencedMap.resourceCrs; } const body = { type: 'FeatureCollection', transformation: georeferencedMap.transformation, resourceCrs, features: georeferencedMap.gcps.map((gcp) => generateFeature(gcp)) }; return { id: georeferencedMap.id, type: 'Annotation', '@context': generateContext(), ...generateDates(georeferencedMap), motivation: 'georeferencing', target, body }; } /** * Generates a Georeference Annotation from a single Georeferenced Map or * an Annotation Page containing multiple Georeference Annotations from an array of Georeferenced Maps. * @param mapOrMaps - Single Georeferenced Map, or an array of Georeferenced Maps * @returns Georeference Annotation or Annotation Page * @example * ```js * import fs from 'fs' * import { generateAnnotation } from '@allmaps/annotation' * * const map = JSON.parse(fs.readFileSync('./examples/map.example.json')) * const annotation = generateAnnotation(map) * ``` */ export function generateAnnotation(mapOrMaps) { if (isGeoreferencedMapsBeforeParse(mapOrMaps)) { // Seperate .parse for different versions for better Zod errors let parsedGeoreferencedMaps; if (isGeoreferencedMap2BeforeParse(mapOrMaps[0])) { parsedGeoreferencedMaps = GeoreferencedMaps2Schema.parse(mapOrMaps); } else { parsedGeoreferencedMaps = GeoreferencedMaps1Schema.parse(mapOrMaps); } const annotations = parsedGeoreferencedMaps.map((parsedGeoreferencedMap) => generateGeoreferenceAnnotation(parsedGeoreferencedMap)); return { type: 'AnnotationPage', '@context': 'http://www.w3.org/ns/anno.jsonld', items: annotations }; } else { // Seperate .parse for different versions for better Zod errors let parsedGeoreferencedMap; if (isGeoreferencedMap2BeforeParse(mapOrMaps)) { parsedGeoreferencedMap = GeoreferencedMap2Schema.parse(mapOrMaps); } else { parsedGeoreferencedMap = GeoreferencedMap1Schema.parse(mapOrMaps); } return generateGeoreferenceAnnotation(parsedGeoreferencedMap); } }