Add types from numista openapi spec

This commit is contained in:
2023-09-20 14:12:05 +01:00
parent a04d790d1e
commit 5b1003d4d9
20 changed files with 433 additions and 195 deletions
+2 -2
View File
@@ -1,4 +1,4 @@
import { CoinProps } from "@/types/coin";
import { NumistaType } from "@/types/numista";
import { readFile, readdir } from "fs/promises";
import path from "path";
@@ -6,7 +6,7 @@ export const getCountryBanknotes = async (country: string) => {
try {
const banknoteIds = await readdir(path.join("./public/banknotes", country));
const banknotes: CoinProps[] = [];
const banknotes: NumistaType[] = [];
for (const banknoteId of banknoteIds) {
const banknote = await readFile(
+2 -2
View File
@@ -1,4 +1,4 @@
import { CoinProps } from "@/types/coin";
import { NumistaType } from "@/types/numista";
import { readFile, readdir } from "fs/promises";
import path from "path";
@@ -6,7 +6,7 @@ export const getCountryCoins = async (country: string) => {
try {
const coinIds = await readdir(path.join("./public/coins", country));
const coins: CoinProps[] = [];
const coins: NumistaType[] = [];
for (const coinId of coinIds) {
const coin = await readFile(
+86 -39
View File
@@ -1,17 +1,60 @@
import * as THREE from "three";
import { useLoader } from "@react-three/fiber";
import { CoinProps } from "@/types/coin";
import { useMemo } from "react";
import { NumistaType } from "@/types/numista";
import { Dispatch, SetStateAction, useEffect, useMemo, useState } from "react";
export const BanknoteInstance = ({ obverse, reverse }: CoinProps) => {
const obverseTexture: THREE.Texture = useLoader(
THREE.TextureLoader,
obverse.picture,
);
const reverseTexture: THREE.Texture = useLoader(
THREE.TextureLoader,
reverse.picture,
const SafeMaterial = ({
url,
bumpMap,
setWidth,
side,
}: {
url: string;
bumpMap: THREE.Texture;
setWidth?: Dispatch<SetStateAction<number>>;
side: "obverse" | "reverse";
}) => {
const texture: THREE.Texture = useLoader(THREE.TextureLoader, url);
const index = useMemo(() => {
switch (side) {
case "obverse":
return 4;
default:
return 5;
}
}, [side]);
useEffect(() => {
if (!setWidth) {
return;
}
setWidth(() => {
try {
return (2 * texture.source.data.width) / texture.source.data.height;
} catch {
return 4;
}
});
}, [setWidth, texture]);
return (
<meshStandardMaterial
color={"#888"}
map={texture}
bumpMap={bumpMap}
bumpScale={0.1}
metalness={0}
roughness={0.5}
attach={"material-" + index}
/>
);
};
export const BanknoteInstance = ({ obverse, reverse }: NumistaType) => {
const [width, setWidth] = useState(0);
const obverseBumpTexture: THREE.Texture = useLoader(
THREE.TextureLoader,
@@ -26,17 +69,6 @@ export const BanknoteInstance = ({ obverse, reverse }: CoinProps) => {
const reverseBumpTexture = obverseBumpTexture.clone();
reverseBumpTexture.repeat = new THREE.Vector2(-1, 0.25);
const width = useMemo(() => {
try {
return (
(2 * obverseTexture.source.data.width) /
obverseTexture.source.data.height
);
} catch {
return 4;
}
}, [obverseTexture]);
return (
<>
<boxGeometry attach="geometry" args={[width, 2, 0.005]} />
@@ -51,25 +83,40 @@ export const BanknoteInstance = ({ obverse, reverse }: CoinProps) => {
/>
))}
<meshStandardMaterial
color={"#888"}
map={obverseTexture}
bumpMap={obverseBumpTexture}
bumpScale={0.1}
metalness={0}
roughness={0.5}
attach="material-4"
/>
{obverse?.picture ? (
<SafeMaterial
bumpMap={obverseBumpTexture}
url={obverse.picture}
side={"obverse"}
setWidth={setWidth}
/>
) : (
<meshStandardMaterial
color={"#888"}
bumpMap={obverseBumpTexture}
bumpScale={0.1}
metalness={0}
roughness={0.5}
attach="material-4"
/>
)}
<meshStandardMaterial
color={"#888"}
map={reverseTexture}
bumpMap={reverseBumpTexture}
bumpScale={0.1}
metalness={0}
roughness={0.5}
attach="material-5"
/>
{reverse?.picture ? (
<SafeMaterial
bumpMap={reverseBumpTexture}
url={reverse.picture}
side={"reverse"}
/>
) : (
<meshStandardMaterial
color={"#888"}
bumpMap={reverseBumpTexture}
bumpScale={0.1}
metalness={0}
roughness={0.5}
attach="material-5"
/>
)}
</>
);
};
@@ -1,7 +1,7 @@
import { useGLTF, useTexture } from "@react-three/drei";
import { CoinProps } from "@/types/coin";
import { NumistaType } from "@/types/numista";
export function BahamasCents10(coin: CoinProps) {
export function BahamasCents10(coin: NumistaType) {
// @ts-expect-error property `nodes` actually exists
const { nodes } = useGLTF("/models/bahamas-cents-10.gltf");
const texture = useTexture("/api/merge-textures/" + coin.id);
@@ -1,7 +1,7 @@
import { useGLTF, useTexture } from "@react-three/drei";
import { CoinProps } from "@/types/coin";
import { NumistaType } from "@/types/numista";
export function BahamasCents15(coin: CoinProps) {
export function BahamasCents15(coin: NumistaType) {
// @ts-expect-error property `nodes` actually exists
const { nodes } = useGLTF("/models/bahamas-cents-15.gltf");
const texture = useTexture("/api/merge-textures/" + coin.id);
+2 -2
View File
@@ -1,7 +1,7 @@
import { useGLTF, useTexture } from "@react-three/drei";
import { CoinProps } from "@/types/coin";
import { NumistaType } from "@/types/numista";
export function EuroCents20(coin: CoinProps) {
export function EuroCents20(coin: NumistaType) {
// @ts-expect-error property `nodes` actually exists
const { nodes } = useGLTF("/models/euro-cents-20.gltf");
const texture = useTexture("/api/merge-textures/" + coin.id);
+2 -2
View File
@@ -1,7 +1,7 @@
import { useGLTF, useTexture } from "@react-three/drei";
import { CoinProps } from "@/types/coin";
import { NumistaType } from "@/types/numista";
export function TostaoFurado(coin: CoinProps) {
export function TostaoFurado(coin: NumistaType) {
// @ts-expect-error property `nodes` actually exists
const { nodes } = useGLTF("/models/tostao-furado.gltf");
const texture = useTexture("/api/merge-textures/" + coin.id);
+70 -47
View File
@@ -1,28 +1,62 @@
import * as THREE from "three";
import { useLoader } from "@react-three/fiber";
import { CoinProps } from "@/types/coin";
import { NumistaType } from "@/types/numista";
import { useEffect, useMemo } from "react";
import { EuroCents20 } from "./custom/euro-cents-20";
import { BahamasCents15 } from "./custom/bahamas-cents-15";
import { BahamasCents10 } from "./custom/bahamas-cents-10";
import { TostaoFurado } from "./custom/tostao-furado";
const SafeEdgeMaterial = ({ edge }: Pick<CoinProps, "edge">) => {
const edgeTexture: THREE.Texture = useLoader(
THREE.TextureLoader,
edge.picture,
);
edgeTexture.repeat = new THREE.Vector2(4, 1);
edgeTexture.offset = new THREE.Vector2(0.5, 0);
edgeTexture.wrapS = THREE.MirroredRepeatWrapping;
edgeTexture.wrapT = THREE.MirroredRepeatWrapping;
const SafeMaterial = ({
url,
orientation,
side,
}: Pick<NumistaType, "orientation"> & {
url: string;
side: "edge" | "obverse" | "reverse";
}) => {
const texture: THREE.Texture = useLoader(THREE.TextureLoader, url);
const index = useMemo(() => {
switch (side) {
case "edge":
return 0;
case "obverse":
return 1;
default:
return 2;
}
}, [side]);
useEffect(() => {
switch (side) {
case "edge":
texture.repeat = new THREE.Vector2(4, 1);
texture.offset = new THREE.Vector2(0.5, 0);
texture.wrapS = THREE.MirroredRepeatWrapping;
texture.wrapT = THREE.MirroredRepeatWrapping;
break;
case "reverse":
if (orientation === "medal") {
texture.center = new THREE.Vector2(0.5, 0.5);
texture.rotation = Math.PI;
}
break;
default:
break;
}
}, [orientation, texture, side]);
return (
<meshStandardMaterial
map={edgeTexture}
map={texture}
metalness={1}
roughness={0.5}
attach="material-0"
attach={"material-" + index}
/>
);
};
@@ -33,61 +67,49 @@ export const BaseCoin = ({
obverse,
reverse,
edge,
}: CoinProps) => {
}: NumistaType) => {
const sides = useMemo(() => {
//const parsed = Number(/(\d+)/.exec(shape)?.[0]);
//return Math.min(32, Math.max(4, isNaN(parsed) ? 32 : parsed));
return 32;
}, []);
const obverseTexture: THREE.Texture = useLoader(
THREE.TextureLoader,
obverse.picture,
);
const reverseTexture: THREE.Texture = useLoader(
THREE.TextureLoader,
reverse.picture,
);
useEffect(() => {
if (orientation === "medal") {
reverseTexture.center = new THREE.Vector2(0.5, 0.5);
reverseTexture.rotation = Math.PI;
}
}, [orientation, reverseTexture]);
const defaultProps = {
metalness: 1,
roughness: 0.5,
color,
};
return (
<>
<cylinderGeometry attach="geometry" args={[1, 1, 1, sides]} />
{edge?.picture ? (
<SafeEdgeMaterial edge={edge} />
<SafeMaterial url={edge.picture} side={"edge"} />
) : (
<meshStandardMaterial
metalness={1}
roughness={0.5}
color={color}
attach="material-0"
/>
<meshStandardMaterial {...defaultProps} attach="material-0" />
)}
<meshStandardMaterial
metalness={1}
roughness={0.5}
map={obverseTexture}
attach="material-1"
/>
{obverse?.picture ? (
<SafeMaterial url={obverse.picture} side={"obverse"} />
) : (
<meshStandardMaterial {...defaultProps} attach="material-1" />
)}
<meshStandardMaterial
metalness={1}
roughness={0.5}
map={reverseTexture}
attach="material-2"
/>
{reverse?.picture ? (
<SafeMaterial
url={reverse.picture}
side={"reverse"}
orientation={orientation}
/>
) : (
<meshStandardMaterial {...defaultProps} attach="material-2" />
)}
</>
);
};
export const CoinInstance = (coin: CoinProps) => {
export const CoinInstance = (coin: NumistaType) => {
if (
coin.value?.numeric_value === 0.2 &&
coin.value?.currency?.name === "Euro"
@@ -98,6 +120,7 @@ export const CoinInstance = (coin: CoinProps) => {
if (
coin.value?.numeric_value === 25 &&
coin.value?.currency?.name === "Peseta" &&
coin.min_year &&
coin.min_year >= 1990
) {
return <TostaoFurado {...coin} />;
+2 -2
View File
@@ -1,10 +1,10 @@
import { CoinProps } from "@/types/coin";
import { NumistaType } from "@/types/numista";
import { CoinInstance } from "../coin";
import { useEffect, useRef } from "react";
import { InstancedMesh } from "three";
import { useCylinder } from "@react-three/cannon";
export const CoinInstances = (coinProps: CoinProps) => {
export const CoinInstances = (coinProps: NumistaType) => {
const didInit = useRef(false);
const { count = 1, size = 16, thickness = 2, weight = 4 } = coinProps;
+2 -2
View File
@@ -2,14 +2,14 @@ import { Canvas, SpotLightProps } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";
import { useRef } from "react";
import { CoinInstances } from "./coin-instances";
import { CoinProps } from "@/types/coin";
import { NumistaType } from "@/types/numista";
import { PlaneProps, Physics, usePlane } from "@react-three/cannon";
import { Mesh, Vector3 } from "three";
import styles from "./index.module.css";
import { StageLightModel } from "./stage-light-model";
type Props = {
coins: CoinProps[];
coins: NumistaType[];
};
const Plane = (props: PlaneProps) => {
+2 -2
View File
@@ -1,5 +1,5 @@
import styles from "./index.module.css";
import { CoinProps } from "@/types/coin";
import { NumistaType } from "@/types/numista";
import { Canvas } from "@react-three/fiber";
import { useEffect } from "react";
import { InfoBox } from "./info-box";
@@ -9,7 +9,7 @@ import { useShowcaseStore } from "./store";
type Props = {
category: "coins" | "banknotes";
types: CoinProps[];
types: NumistaType[];
};
export const Showcase = (props: Props) => {
+1 -1
View File
@@ -50,7 +50,7 @@ export const InfoBox = () => {
color: "white",
}}
>
<small>{issuer.name}</small>
<small>{issuer?.name}</small>
<h1 style={{ textAlign: "center" }}>{title}</h1>
<p>{min_year !== max_year ? min_year + " - " + max_year : min_year}</p>
<small>{(count ?? 1) + " in collection"}</small>
+2 -2
View File
@@ -4,14 +4,14 @@ import { CoinInstance } from "@/components/coin";
import { Mesh } from "three";
import { BanknoteInstance } from "@/components/banknote";
import { useShowcaseStore } from "./store";
import { CoinProps } from "@/types/coin";
import { NumistaType } from "@/types/numista";
const SpinninType = ({
typeProps,
isSelected,
...props
}: MeshProps & {
typeProps?: CoinProps;
typeProps?: NumistaType;
isSelected?: boolean;
}) => {
const ref = useRef<Mesh>(null!);
+4 -4
View File
@@ -1,9 +1,9 @@
import { create } from "zustand";
import { OrbitControls } from "three-stdlib";
import { CoinProps } from "@/types/coin";
import { NumistaType } from "@/types/numista";
import { MutableRefObject, createRef } from "react";
type Props = { category: "coins" | "banknotes"; types: CoinProps[] };
type Props = { category: "coins" | "banknotes"; types: NumistaType[] };
interface ShowcaseStore {
currentIndex: number;
@@ -15,10 +15,10 @@ interface ShowcaseStore {
orbitControlsRef: MutableRefObject<OrbitControls | null>;
setOrbitControlsRef: (ref: OrbitControls | null) => void;
toggleSpin: () => void;
types: CoinProps[];
types: NumistaType[];
category: Props["category"] | null;
init: (props: Props) => void;
getType: (index?: number) => CoinProps | undefined;
getType: (index?: number) => NumistaType | undefined;
}
export const useShowcaseStore = create<ShowcaseStore>((set, get) => ({
+36
View File
@@ -0,0 +1,36 @@
import { NumistaType } from "@/types/numista";
export const sortTypes = (types: NumistaType[]) =>
types
.sort((a, b) => {
if (a.value?.numeric_value && b.value?.numeric_value) {
return a.value.numeric_value - b.value.numeric_value;
}
return 0;
})
.sort((a, b) => {
if (a.value?.currency?.full_name && b.value?.currency?.full_name) {
return a.value.currency.full_name.localeCompare(
b.value.currency.full_name,
);
}
return 0;
})
.sort((a, b) => {
if (a.issuer && b.issuer) {
return a.issuer.name.localeCompare(b.issuer.name);
}
return 0;
})
.sort((a, b) => {
if (a.max_year && b.max_year) {
return a.max_year - b.max_year;
}
return 0;
})
.sort((a, b) => {
if (a.min_year && b.min_year) {
return a.min_year - b.min_year;
}
return 0;
});
+4 -3
View File
@@ -1,17 +1,18 @@
import Head from "next/head";
import { GetStaticPaths, GetStaticProps, NextPage } from "next";
import { CoinProps } from "@/types/coin";
import { NumistaType } from "@/types/numista";
import { readdir } from "fs/promises";
import { Showcase } from "@/components/showcase";
import { Header } from "@/components/header";
import { getCountryBanknotes } from "@/api/banknotes/get-country-banknotes";
import { getCountryCoins } from "@/api/coins/get-country-coins";
import { sortTypes } from "@/core/utils/numista";
type Props = {
country: string;
coinCount: number;
banknoteCount: number;
banknotes: CoinProps[];
banknotes: NumistaType[];
};
const BanknotesPage: NextPage<Props> = ({ banknotes, ...props }) => (
@@ -53,7 +54,7 @@ export const getStaticProps: GetStaticProps<Props> = async ({ params }) => {
(total, banknote) => total + (banknote.count ?? 1),
0,
),
banknotes: banknotes.sort((a, b) => a.min_year - b.min_year),
banknotes: sortTypes(banknotes),
},
};
};
+4 -3
View File
@@ -1,17 +1,18 @@
import Head from "next/head";
import { GetStaticPaths, GetStaticProps, NextPage } from "next";
import { CoinProps } from "@/types/coin";
import { NumistaType } from "@/types/numista";
import { readdir } from "fs/promises";
import { Showcase } from "@/components/showcase";
import { Header } from "@/components/header";
import { getCountryBanknotes } from "@/api/banknotes/get-country-banknotes";
import { getCountryCoins } from "@/api/coins/get-country-coins";
import { sortTypes } from "@/core/utils/numista";
type Props = {
country: string;
coinCount: number;
banknoteCount: number;
coins: CoinProps[];
coins: NumistaType[];
};
const CoinsPage: NextPage<Props> = ({ coins, ...props }) => (
@@ -53,7 +54,7 @@ export const getStaticProps: GetStaticProps<Props> = async ({ params }) => {
0,
),
coinCount: coins.reduce((total, coin) => total + (coin.count ?? 1), 0),
coins: coins.sort((a, b) => a.min_year - b.min_year),
coins: sortTypes(coins),
},
};
};
+4 -3
View File
@@ -1,17 +1,18 @@
import Head from "next/head";
import { Pile } from "@/components/pile";
import { GetStaticPaths, GetStaticProps, NextPage } from "next";
import { CoinProps } from "@/types/coin";
import { NumistaType } from "@/types/numista";
import { readdir } from "fs/promises";
import { Header } from "@/components/header";
import { getCountryBanknotes } from "@/api/banknotes/get-country-banknotes";
import { getCountryCoins } from "@/api/coins/get-country-coins";
import { sortTypes } from "@/core/utils/numista";
type Props = {
country: string;
coinCount: number;
banknoteCount: number;
coins: CoinProps[];
coins: NumistaType[];
};
const PilePage: NextPage<Props> = ({ coins, ...props }) => (
@@ -53,7 +54,7 @@ export const getStaticProps: GetStaticProps<Props> = async ({ params }) => {
0,
),
coinCount: coins.reduce((total, coin) => total + (coin.count ?? 1), 0),
coins: coins.sort((a, b) => a.min_year - b.min_year),
coins: sortTypes(coins),
},
};
};
-75
View File
@@ -1,75 +0,0 @@
type Face = {
engravers: string[];
description: string;
lettering: string;
lettering_scripts: Array<{
name: string;
}>;
picture: string;
thumbnail: string;
picture_copyright: string;
};
export type CoinProps = {
id: number;
url: string;
title: string;
category: "banknote" | "coin" | "exonumia";
issuer: {
code: string;
name: string;
};
min_year: number;
max_year: number;
type: string;
ruler: Array<{
id: number;
name: string;
wikidata_id: string;
}>;
value: {
text: string;
numeric_value: number;
currency: {
id: number;
name: string;
full_name: string;
};
};
demonetization: {
is_demonetized: boolean;
};
shape: string;
composition: {
text: string;
};
technique: {
text: string;
};
obverse: Face;
reverse: Face;
comments: string;
references: Array<{
catalogue: {
id: number;
code: string;
};
number: string;
}>;
weight: number;
size: number;
thickness: number;
orientation: "medal" | "coin";
edge: {
description: string;
picture: string;
thumbnail: string;
picture_copyright: string;
};
mints: Array<{
id: string;
name: string;
}>;
count?: number;
color?: string;
};
+204
View File
@@ -0,0 +1,204 @@
export type NumistaCategory = "coin" | "banknote" | "exonumia";
export interface NumistaType {
/** @description Unique ID of the type on Numista */
id: number;
/** @description URL to the type on Numista */
url?: string;
/** @description Title of the type */
title: string;
/**
* @description Category
* @enum {string}
*/
category: NumistaCategory;
issuer?: Issuer;
/** @description First year the type was produced (in the Gregorian calendar). */
min_year?: number;
/** @description Last year the type was produced (in the Gregorian calendar). */
max_year?: number;
/** @description Type */
type?: string;
/** @description Face value */
value?: {
/** @description Face value in text format */
text?: string;
/**
* Format: float
* @description Face value as a floating number.
*/
numeric_value?: number;
/** @description If the value is better described as a fraction, this is the numerator of the fraction */
numerator?: number;
/** @description If the value is better described as a fraction, this is the denominator of the fraction */
denominator?: number;
currency?: Currency;
};
/** @description Ruling authorities (emperor, queen, period, etc.) */
ruler?: {
/** @description Unique ID of the ruling authority on Numista */
id: number;
/** @description Name of the ruling authority */
name: string;
/** @description Identifier of the ruling authority at Wikidata, starting with a "Q" */
wikidata_id?: string;
/** @description Dynasty, house, extended period, or any other group of ruling authorities */
group?: {
/** @description Unique ID of the ruling authority group on Numista */
id: number;
/** @description Name of the ruling authority group */
name: string;
};
}[];
/** @description Information about the demonetization of the coin or banknote */
demonetization?: {
/** @description True if the type is demonetized, false if it is not demonetized */
is_demonetized: boolean;
/**
* Format: date
* @description Date of demonetisation (YYYY-MM-DD)
*/
demonetization_date?: string;
};
/** @description Shape */
shape?: string;
/** @description Composition (metallic content) */
composition?: {
/** @description Description of the composition */
text?: string;
};
/** @description Manufacturing technique */
technique?: {
/** @description Description of the technique */
text?: string;
};
/**
* Format: float
* @description Weight in grams
*/
weight?: number;
/**
* Format: float
* @description Size (diameter) in millimeters
*/
size?: number;
/**
* Format: float
* @description Thickness of the coin in millimeters
*/
thickness?: number;
/**
* @description Orientation of the coin ("coin", "medal", "three" (3 o'clock), "nine" (9 o'clock), or "variable")
* @enum {string}
*/
orientation?: "coin" | "medal" | "variable" | "three" | "nine";
obverse?: CoinSide;
reverse?: CoinSide;
edge?: CoinSide;
watermark?: CoinSide;
/** @description Mints where the coin was minted */
mints?: {
/** @description Unique ID of the mint on Numista */
id: number;
/** @description Name of the mint */
name: string;
}[];
/** @description Printers where the banknote was printed */
printers?: {
/** @description Unique ID of the printer on Numista */
id: number;
/** @description Name of the printer */
name: string;
}[];
/** @description For types which are part of a series, the name of the series */
series?: string;
/** @description For commemorated types, short description of the commemorated topic (event, person, etc.) */
commemorated_topic?: string;
/** @description General comments about the type (HTML format) */
comments?: string;
/** @description List of related types */
related_types?: {
/** @description Unique ID of the type on Numista */
id: number;
/** @description Title of the type */
title: string;
/**
* @description Category
* @enum {string}
*/
category?: "coin" | "banknote" | "exonumia";
issuer?: Issuer;
/** @description First year the type was producted (in the Gregorian calendar). */
min_year?: number;
/** @description Last year the type was producted (in the Gregorian calendar). */
max_year?: number;
}[];
/** @description List of tags */
tags?: string[];
/** @description References of the type in other catalogues */
references?: Reference[];
/** @description How many of this type are included in my personal collection */
count?: number;
/** @description Dominant color calculated from the pictures */
color: string;
}
interface Issuer {
/** @description Unique ID of the issuer on Numista */
code: string;
/** @description Name of the issuer */
name: string;
}
interface Currency {
/** @description Unique ID of the currency on Numista */
id: number;
/** @description Name of the currency */
name: string;
/** @description Full name of the currency, including dates */
full_name: string;
}
interface Reference {
/** @description The catalogue in which the reference can be found */
catalogue: {
/** @description ID of the catalogue in Numista */
id: number;
/** @description Code identifying the catalogue */
code: string;
};
/** @description Number of the coin in the catalogue */
number: string;
}
export interface CoinSide {
/** @description Name of the engraver(s) */
engravers?: string[];
/** @description Name of the designer(s) */
designers?: string[];
/** @description Description of the side of the coin */
description?: string;
/** @description Lettering visible on the side of the coin */
lettering?: string;
/** @description Scripts used to write the lettering on the side of the coins */
lettering_scripts?: {
/** @description Name of the script */
name: string;
}[];
/** @description Legend visible on the side of the coin with abbreviations replaced by full words */
unabridged_legend?: string;
/** @description Translation of the lettering visible on the side of the coin */
lettering_translation?: string;
/** @description URL to the picture of the side of the coin */
picture?: string;
/** @description URL to the thumbnail of the picture of the side of the coin */
thumbnail?: string;
/** @description Name of the owner of the picture. Pictures should not be used without consent from their owner. */
picture_copyright?: string;
/** @description URL to the website of the owner of the picture. Pictures should not be used without consent from their owner. */
picture_copyright_url?: string;
/** @description Name of the license of the picture, if the owner of the picture specified a license. */
picture_license_name?: string;
/** @description URL to the license of the picture, if the owner of the picture specified a license. */
picture_license_url?: string;
}