aiiinotate 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +61 -0
  3. package/cli/import.js +142 -0
  4. package/cli/index.js +26 -0
  5. package/cli/io.js +105 -0
  6. package/cli/migrate.js +123 -0
  7. package/cli/mongoClient.js +11 -0
  8. package/docs/architecture.md +88 -0
  9. package/docs/db.md +38 -0
  10. package/docs/dev_iiif_compatibility.md +43 -0
  11. package/docs/endpoints.md +48 -0
  12. package/docs/progress.md +159 -0
  13. package/docs/specifications/0_w3c_open_annotations.md +332 -0
  14. package/docs/specifications/1_w3c_web_annotations.md +577 -0
  15. package/docs/specifications/2_iiif_apis.md +396 -0
  16. package/docs/specifications/3_iiif_annotations.md +103 -0
  17. package/docs/specifications/4_search_api.md +135 -0
  18. package/docs/specifications/5_sas.md +119 -0
  19. package/docs/specifications/6_mirador.md +119 -0
  20. package/docs/specifications/7_aikon.md +137 -0
  21. package/docs/specifications/include/presentation_2.0.webp +0 -0
  22. package/docs/specifications/include/presentation_2.0_white.png +0 -0
  23. package/docs/specifications/include/presentation_3.0.png +0 -0
  24. package/docs/specifications/include/presentation_3.0_resize.png +0 -0
  25. package/eslint.config.js +27 -0
  26. package/migrations/baseConfig.js +56 -0
  27. package/migrations/manageIndex.js +55 -0
  28. package/migrations/migrate-mongo-config-main.js +8 -0
  29. package/migrations/migrate-mongo-config-test.js +8 -0
  30. package/migrations/migrationScripts/20250825185706-collections.js +41 -0
  31. package/migrations/migrationScripts/20250826194832-annotations2-canvas-index.js +31 -0
  32. package/migrations/migrationScripts/20250904080710-annotations2-schema.js +42 -0
  33. package/migrations/migrationScripts/20251002141951-manifest2-schema.js +43 -0
  34. package/migrations/migrationScripts/20251006212110-manifest-unique-index.js +29 -0
  35. package/migrations/migrationScripts/20251028115614-annotations2-id-index.js +27 -0
  36. package/migrations/migrationTemplate.js +25 -0
  37. package/package.json +78 -0
  38. package/run.sh +70 -0
  39. package/scripts/_migrations.sh +79 -0
  40. package/scripts/_setup.js +31 -0
  41. package/scripts/setup_mongodb.sh +61 -0
  42. package/scripts/setup_mongodb_migrate.sh +17 -0
  43. package/scripts/setup_node.sh +15 -0
  44. package/scripts/utils.sh +192 -0
  45. package/setup.sh +20 -0
  46. package/src/app.js +113 -0
  47. package/src/config/.env.template +22 -0
  48. package/src/data/annotations/annotations2.js +419 -0
  49. package/src/data/annotations/annotations3.js +32 -0
  50. package/src/data/annotations/routes.js +271 -0
  51. package/src/data/annotations/routes.test.js +180 -0
  52. package/src/data/collectionAbstract.js +270 -0
  53. package/src/data/index.js +29 -0
  54. package/src/data/manifests/manifests2.js +305 -0
  55. package/src/data/manifests/manifests2.test.js +53 -0
  56. package/src/data/manifests/manifests3.js +23 -0
  57. package/src/data/manifests/routes.js +95 -0
  58. package/src/data/manifests/routes.test.js +69 -0
  59. package/src/data/routes.js +141 -0
  60. package/src/data/routes.test.js +117 -0
  61. package/src/data/utils/iiif2Utils.js +196 -0
  62. package/src/data/utils/iiif2Utils.test.js +98 -0
  63. package/src/data/utils/iiif3Utils.js +0 -0
  64. package/src/data/utils/iiifUtils.js +18 -0
  65. package/src/data/utils/routeUtils.js +109 -0
  66. package/src/data/utils/testUtils.js +253 -0
  67. package/src/data/utils/utils.js +231 -0
  68. package/src/db/index.js +48 -0
  69. package/src/fileServer/annotations.js +39 -0
  70. package/src/fileServer/data/annotationList_aikon_wit9_man11_anno165_all.jsonld +827 -0
  71. package/src/fileServer/data/annotationList_vhs_wit250_man250_anno250_all.jsonld +37514 -0
  72. package/src/fileServer/data/annotationList_vhs_wit253_man253_anno253_all.jsonld +20111 -0
  73. package/src/fileServer/data/annotations2Invalid.jsonld +39 -0
  74. package/src/fileServer/data/annotations2Valid.jsonld +39 -0
  75. package/src/fileServer/data/bnf_invalid_manifest.json +2806 -0
  76. package/src/fileServer/data/bnf_valid_manifest.json +2817 -0
  77. package/src/fileServer/data/vhs_wit253_man253_anno253_anno-24.json +1 -0
  78. package/src/fileServer/index.js +64 -0
  79. package/src/fileServer/manifests.js +14 -0
  80. package/src/fileServer/utils.js +35 -0
  81. package/src/schemas/index.js +20 -0
  82. package/src/schemas/schemasBase.js +47 -0
  83. package/src/schemas/schemasPresentation2.js +417 -0
  84. package/src/schemas/schemasPresentation3.js +57 -0
  85. package/src/schemas/schemasResolver.js +71 -0
  86. package/src/schemas/schemasRoutes.js +277 -0
  87. package/src/server.js +22 -0
  88. package/src/types.js +93 -0
@@ -0,0 +1,48 @@
1
+ # Endpoints
2
+
3
+ ---
4
+
5
+ ## URL prefixes
6
+
7
+ URL anatomy is a mix of [SAS endpoints](./specifications/4_sas.md) and IIIF specifications. In turn, we define the following prefixes:
8
+
9
+ - `data`: for all IIIF URIs: URIs of annotations and annotation lists
10
+ - `annotations`: operations on annotations
11
+ - `manifests`: operations on manifests
12
+ - `search-api`: endpoint to access the IIIF search API
13
+
14
+ In turn, URL anatomy is:
15
+
16
+ ```
17
+ {host}/{prefix}/{iiif_version}/{slug}
18
+ ```
19
+
20
+ Where:
21
+ - `host`: the host of your app
22
+ - `prefix`: `data | annotations | manifests`
23
+ - `iiif_version`:
24
+ - if `prefix` is `search-api`, `1 | 2`: IIIF Search API version used
25
+ - otherwise, `2 | 3`, the IIIF Presentation API version your data is in
26
+ - `slug`: the rest of the qurty URI
27
+
28
+ There is an extra URL prefix: `schemas`. It is only used internally (not accessible to clients or accessible through HTTP) to define the IDs of all JsonSchemas, so we won't talk about it here.
29
+
30
+ ---
31
+
32
+ ## IIIF URIs
33
+
34
+ IIIF URIs in the Presentation 2.1 API are:
35
+
36
+ ```
37
+ Collection {scheme}://{host}/{prefix}/collection/{name}
38
+ Manifest {scheme}://{host}/{prefix}/{identifier}/manifest
39
+ Sequence {scheme}://{host}/{prefix}/{identifier}/sequence/{name}
40
+ Canvas {scheme}://{host}/{prefix}/{identifier}/canvas/{name}
41
+ Annotation (incl images) {scheme}://{host}/{prefix}/{identifier}/annotation/{name}
42
+ AnnotationList {scheme}://{host}/{prefix}/{identifier}/list/{name}
43
+ Range {scheme}://{host}/{prefix}/{identifier}/range/{name}
44
+ Layer {scheme}://{host}/{prefix}/{identifier}/layer/{name}
45
+ Content {scheme}://{host}/{prefix}/{identifier}/res/{name}.{format}
46
+ ```
47
+
48
+
@@ -0,0 +1,159 @@
1
+ # Dev progress
2
+
3
+ We mostly talk about which routes are done here
4
+
5
+ ---
6
+
7
+ ## Done
8
+
9
+ ### Routes
10
+
11
+ Routes are only implemented with IIIF Presentation API 2.x, not with the 3.0 version.
12
+
13
+ #### Generic routes
14
+
15
+ - `GET /search-api/:iiifSearchVersion/manifests/:manifestShortId/search`: search API
16
+
17
+ #### Annotations routes
18
+
19
+ - `GET /annotations/:iiifPresentationVersion/search`: get all annotations for a canvas URI
20
+ - `POST /annotations/:iiifPresentationVersion/create`: create 1 annotation
21
+ - `POST /annotations/:iiifPresentationVersion/createMany`: create several annotations
22
+ - `POST /annotations/:iiifPresentationVersion/update`: update 1 annotation
23
+ - `DELETE /annotations/:iiifPresentationVersion/delete`: delete annotations, either by their `@id`, trget canvas URI (`on.full`), or their `on.manifestShortId`
24
+
25
+ => all create/update/delete annotation routes are done !
26
+
27
+ #### Manifests routes
28
+
29
+ - `POST /manifests/:iiifPresentationVersion/create`: create a single manifest, either by including the manifest in the body or its URI
30
+ - `DELETE /manifests/:iiifPresentationVersion/delete`: delete a single manifest
31
+ - `GET /manifests/:iiifPresentationVersion`: return an index of all manifests as a collection
32
+
33
+ ### Non-routes
34
+
35
+ - `manifests2`: `insert`, `insertMany` internal behaviours
36
+ - fetching and inserting manifests related to an annotation when using inserting annotations.
37
+
38
+ ---
39
+
40
+ ## Notes
41
+
42
+ ### Uniqueness
43
+
44
+ As of writing (09.10.25), there are no uniqueness constraints on annotations. There is only a uniqueness constraint on collection `manifest2` on field `manifest2.@id` (the ID of a manifest).
45
+
46
+ Ideally, we would want to avoid having duplicate annotations in the database. This is more complicated in practice: at least for `annotions2`, an annotation's `@id` field is re-generated and a random part (an UUID) is generated at insert time. This means that, when trying to store the same annotation (with the same `@id`), the `@id` is changed, and so a different value is inserted.
47
+
48
+ This means that we can't have a uniqueness constraint on `@id` or `id` fields of annotations. Another option would be to have a uniqueness constraint on annotation targets (no 2 annotations can annotate the same region), but this behaviour seems brittle in practice, so it's not yet implemented.
49
+
50
+ ### Concurrency
51
+
52
+ For clients, concurrency/parrallelization (i.e., with JS `Promise.all()`) on insert/update should be avoided because it can cause a data race: several processes can attempt to write the same thing in parrallel.
53
+
54
+ For example, when inserting annotations, the manifests related to each annotation are inserted in parrallel. Since this is a side effect, 2 processes may unknowingly try to insert the same manifest in the database, which causes a uniqueness constraint to fail. This error can be hard to debug, so it's best to avoid concurrency at write time.
55
+
56
+ ---
57
+
58
+ ## Dev quirks
59
+
60
+ Sometimes, node/fastify can be weird. When scratching your head at dev errors, look here:)
61
+
62
+ ### Error swallowing at app build
63
+
64
+ Errors that happen when a plugin is being registered will cause the app's startup to fail, without necessarily throwing an error.
65
+
66
+ This is especially true for test cases that build the fastify app:
67
+
68
+ ```
69
+ test
70
+ => build fastify app
71
+ => build step fails silently
72
+ => test fails
73
+ ```
74
+
75
+ #### Troubleshooting
76
+
77
+ - normal behaviour: when a runtime error happens, the failing test will log the error on the console
78
+ - what this error looks like:
79
+ - tests fail **very quickly**,
80
+ - without throwing an error, seemingly without even running the test suite
81
+ - the proces doesn't exit, although all tests have failed
82
+ - when `npm run test` fails like this, run `npm run start`. See if normal startup throws an error
83
+ - NOTE: normal startup not throwing does NOT mean that the build step necessarily worked
84
+
85
+ #### Possible help/solutions
86
+
87
+ - find a way to stisfyingly use `try...catch` at plugin registration.
88
+ - look into these issues: [2694](https://github.com/fastify/fastify/issues/2694)
89
+ - in particular, it may be an issue specific to async plugins ?
90
+
91
+ ### Route response schema definition
92
+
93
+ For some reason, route schema definition is much less flexible for responses than for queries.
94
+
95
+ #### The problem
96
+
97
+ **Query schemas**: In queries (`schema.params` or `schema.querystring`), fastify is very permissive. You can use unresolved schemas (with `$ref`), save entire schemas as JsonSchemas ...
98
+
99
+ **Response schemas**: Response schemas have more constraints.
100
+ - **response schemas are defined at route level** and cannot be stored as full JsonSchemas:
101
+ ```js
102
+ // this will be an invalid response schema: it is trying to store a complete response schema as a JsonSchema
103
+ fastify.addSchema({
104
+ $id: makeSchemaUri("routeResponsePost"),
105
+ properties: {
106
+ 200: { ... },
107
+ 500: { ... },
108
+ }
109
+ })
110
+
111
+ // this is a fixed version: an object containing schemas, not a full JsonSchema, to be used inside a Route definition
112
+ schema: {
113
+ response: {
114
+ 200: { ... },
115
+ 500: { ... }
116
+ }
117
+ }
118
+ // if you want to reuse a schema, store it as a JS object and import it.
119
+ ```
120
+ - **unresolved response schemas (`$ref`) are forbidden**. You cannot use `$ref`. In the app, use `fastify.schemasResolver` to resolve the schema to a plain `JsonSchema` without `$ref`.
121
+
122
+ #### The fix
123
+
124
+ In short, here's **how to use shared schemas in responses**:
125
+ 1. Define payload schemas for different response cases:
126
+ ```js
127
+ // in case of a POST success
128
+ fastify.addSchema({
129
+ $id: makeSchemaUri("routeResponseInsert"),
130
+ type: "object",
131
+ // ...properties
132
+ });
133
+
134
+ // in case of a POST error
135
+ fastify.addSchema({
136
+ $id: makeSchemaUri("routeResponseError"),
137
+ type: "object",
138
+ // ...properties
139
+ });
140
+ ```
141
+ 2.
142
+ 2. Resolve schemas and use in responses
143
+ ```js
144
+ const routeResponseInsert = fastify.getSchema("...");
145
+ const routeResponseError = fastify.getSchema("...");
146
+ fastify.post(
147
+ "/annotations/:iiifPresentationVersion/create",
148
+ {
149
+ schema: {
150
+ body: routeAnnotations2Or3Schema,
151
+ response: {
152
+ 200: routeResponseInsert,
153
+ 500: routeResponseError
154
+ }
155
+ }
156
+ },
157
+ async (req, rep) => {}
158
+ )
159
+ ```
@@ -0,0 +1,332 @@
1
+ # Open annotations data model
2
+
3
+ [https://web.archive.org/web/20220119101135/http://www.openannotation.org/spec/core](https://web.archive.org/web/20220119101135/http://www.openannotation.org/spec/core)
4
+
5
+ [https://web.archive.org/web/20221225112722/http://www.openannotation.org/spec/core/core.html](https://web.archive.org/web/20221225112722/http://www.openannotation.org/spec/core/core.html)
6
+
7
+ [https://www.w3.org/TR/annotation-vocab/](https://www.w3.org/TR/annotation-vocab/)
8
+
9
+
10
+ The IIIF 2.x standard relies on the W3C Open annotations (OA) standard. Established in 2013, it has since been deprecated in favour of the W3C's Web annotations model, described in the next section. To make things fun, the site is not longer available and must be accessed through the wayback machine.
11
+
12
+ ---
13
+
14
+ ## In general
15
+
16
+ - an Annotation is **a set of interconnected resources**, made mostly of a `Body` and a `Target`
17
+ - the data model is **implementation independant** and can be implemented in JSON, RDF-XML, Turtle...
18
+ - the standard extamples are in RDF, while IIIF uses JSON-ld. In turn, keys vary from the IIIF standard. When there are differences, I indicate both what is in the OA examples, and what is used in IIIF.
19
+
20
+ ---
21
+
22
+ ## Vocabulary
23
+
24
+ - `Annotation`: a set of interconnected resources, with at its core a `Body` and a `Target`, where the `Body` is about the `Target`.
25
+ - `Target`: the object being annotated
26
+ - `Body`: the annotation's contents
27
+ - `Motivation`: the motivation further describes the relationship between `Body` and `Target`
28
+
29
+ ---
30
+
31
+ ## Namespaces
32
+
33
+ <table>
34
+ <tbody><tr><th>Prefix</th><th>Namespace</th><th>Description</th></tr>
35
+ <tr><td>oa</td><td>http://www.w3.org/ns/oa#</td> <td>The Open Annotation ontology</td></tr>
36
+
37
+ <tr><td>cnt</td><td>http://www.w3.org/2011/content#</td><td><a href="https://web.archive.org/web/20220119101135/http://www.w3.org/TR/Content-in-RDF10/">Representing Content in RDF</a></td></tr>
38
+ <tr><td>dc</td><td>http://purl.org/dc/elements/1.1/</td><td><a href="https://web.archive.org/web/20220119101135/http://dublincore.org/documents/dces/">Dublin Core Elements</a></td></tr>
39
+ <tr><td>dcterms</td><td>http://purl.org/dc/terms/</td><td><a href="https://web.archive.org/web/20220119101135/http://dublincore.org/documents/dcmi-terms/">Dublin Core Terms</a></td></tr>
40
+ <tr><td>dctypes</td><td>http://purl.org/dc/dcmitype/</td><td><a href="https://web.archive.org/web/20220119101135/http://dublincore.org/documents/dcmi-type-vocabulary/">Dublin Core Type Vocabulary</a></td></tr>
41
+ <tr><td>foaf</td><td>http://xmlns.com/foaf/0.1/</td><td><a href="https://web.archive.org/web/20220119101135/http://xmlns.com/foaf/spec/">Friend-of-a-Friend Vocabulary</a></td></tr>
42
+ <tr><td>prov</td><td>http://www.w3.org/ns/prov#</td><td><a href="https://web.archive.org/web/20220119101135/http://www.w3.org/TR/prov-o/">Provenance Ontology</a></td></tr>
43
+ <tr><td>rdf</td><td>http://www.w3.org/1999/02/22-rdf-syntax-ns#</td><td><a href="https://web.archive.org/web/20220119101135/http://www.w3.org/TR/rdf-syntax-grammar/">RDF</a></td></tr>
44
+ <tr><td>rdfs</td><td>http://www.w3.org/2000/01/rdf-schema#</td><td><a href="https://web.archive.org/web/20220119101135/http://www.w3.org/TR/rdf-schema/">RDF Schema</a></td></tr>
45
+ <tr><td>skos</td><td>http://www.w3.org/2004/02/skos/core#</td><td><a href="https://web.archive.org/web/20220119101135/http://www.w3.org/TR/skos-reference/">Simple Knowledge Organization System</a></td></tr>
46
+ <tr><td>trig</td><td>http://www.w3.org/2004/03/trix/rdfg-1/</td><td><a href="https://web.archive.org/web/20220119101135/http://www.w3.org/2004/03/trix/">TriG Named Graphs</a></td></tr>
47
+
48
+ </tbody></table>
49
+
50
+ ---
51
+
52
+ ## Annotations
53
+
54
+ An annotation has 1+ `Targets` and 0+ `Bodies`
55
+ - IIIF `@id`: `URI`
56
+ - is used to identify with an URI the `Annotation`, `Body` and `Target`
57
+ - in Turtle, there is no specific key because the standard is different.
58
+ - `a` (IIIF `@type`): `oa:Annotation`
59
+ - this indicates that the resource is an `Annotation`
60
+ - `oa:hasBody` (IIIF `resource`): `Body | Body[]`
61
+ - the relationship between `Annotation` and `Body`
62
+ - the body MAY be omitted if there is no content to annotate
63
+ - `oa:hasTarget` (IIIF `on`): `Target | Target[]`
64
+ - the relationship between `Annotation` and `Target`
65
+ - `oa:Motivation` (IIIF `motivation`): `string[]` (in IIIF, `oa:commenting | sc:painting` are frequent)
66
+ - the motivation, or role of the annotation
67
+ - `all allowed values are: `oa:bookmarking | oa:classifying | oa:commenting | oa:describing | oa:editing | oa:highlighting | oa:identifying | oa:linking | oa:moderating | oa:questionning | oa:replying | oa:tagging`
68
+ - in IIIF, `sc:painting` is also allowed to indicate a painting annotation. The most useful values are `sc:painting | oa:commenting`.
69
+
70
+ ```js
71
+ // basic structure
72
+ {
73
+ "@id": "<URI>",
74
+ "@type": "oa:Annotation",
75
+ "motivation": "sc:painting" || "oa:commenting", // or an array of values
76
+ "resource": {
77
+ "@id": "<URI?>",
78
+ "@type": "<dctype>",
79
+ // other keys
80
+ },
81
+ "on": "<URI>" || SpecificResource // `on` can be either specified as the URI to a canvas, or as a SpecificResource
82
+ }
83
+ ```
84
+ ### Bodies and targets
85
+
86
+ - `a` (IIIF `@type`): `dctypes:Dataset | dctypes:Image | dctypes:MovingImage | dctypes:Sound | dcTypes:Text`
87
+ - SHOULD be used to describe the type of a `Body` or `Target`
88
+ - IIIF `@id`: `URI`
89
+
90
+ Special cases:
91
+ - an annotation may have **no `Body`**
92
+ - an annotation may have **multiple `Bodies` or `Targets`**.
93
+ - in that case, **each body is related to each target individually**. If we have 2 bodies and 2 targets, we have the following situation:
94
+ ```
95
+ body1 <-> target1
96
+ body1 <-> target2
97
+ body2 <-> target1
98
+ body2 <-> target2
99
+ ```
100
+ - you may have 1 `Target` and N `Bodies` (several annotations apply to a single body), or N `Target` and 1 `Bodies` (1 annotation applies to several images) or an N to N relationship.
101
+
102
+ ### Embedded textual body (ETB)
103
+
104
+ ETBs are used to embed textual content directly in an annotation instead of referencing it by an `id`.
105
+ - `a` (IIIF `@type`): `cnt:ContentAsText | dctypes:Text`
106
+ - MUST be used to indicate that this is an ETB. MAY contain both values described above.
107
+ - `cnt:chars` (IIIF `chars`): `string`
108
+ - MUST Be used to contain the contents of the ETB.
109
+ - `dc:format` (SAS `format`): `mimetype`
110
+ - SHOULD be used to describe the mimetype of the ETB
111
+ - for example, it can distinguish plain text form HTML.
112
+ - `dc:language`: `string`
113
+ - the language the ETB is in
114
+ - language codes SHOULD follow [RFC 3066](https://www.ietf.org/rfc/rfc3066.txt)
115
+
116
+ ```js
117
+ {
118
+ "@type" : "dctypes:Text",
119
+ "format" : "text/html",
120
+ "chars" : "<p>This is en embedded textual content from SAS</p>",
121
+ }
122
+ ```
123
+
124
+ ### Tags
125
+
126
+ A tag is a specific type of `Body`: a keyword or label used to annotate a `Target`. There are 2 types of tags: "normal" and "semantic" tags
127
+ - `a` (IIIF `@type`): `oa:Tag | oa:SemanticTag`
128
+ - `oa:Tag` describes a non-semantic tag. In that case, the tag functions like an ETB
129
+ - `oa:SemanticTag` describes a semantic tag. Instead of containing the contents of the tag, the `Body` references an external URI.
130
+ - `oa:Motivation` (IIIF `motivation`): `oa:tagging`
131
+ - `oa:tagging` should be added to the `oa:Motivation` of the annotation to indicate it's a tag
132
+
133
+ ### Fragment URIs
134
+
135
+ Fragment URIs can be used to target part of a resource.
136
+ - fragments ARE NOT COMPATIBLE with `SpecificResources`. Either use one or the other.
137
+
138
+ ```js
139
+ {
140
+ "@id": "http://example.com/image.jpg#xywh=1,1,1,1"
141
+ }
142
+ ```
143
+
144
+ ---
145
+
146
+ ## Specific Resources
147
+
148
+ [https://web.archive.org/web/20221225112722/http://www.openannotation.org/spec/core/specific.html](https://web.archive.org/web/20221225112722/http://www.openannotation.org/spec/core/specific.html)
149
+
150
+ `SpecificResources` are an extension of the core data model to reference part of a resource with more precision than Fragment URIs. They can be used either in `Bodies` or `Targets` (though in IIIF they are used mostly in `Targets`).
151
+
152
+ ```js
153
+ // basic IIIF structure for a SpecificResource
154
+ {
155
+ "@id": "<annotationId>",
156
+ "@type": "oa:SpecificResource",
157
+ "full": {
158
+ // link to the full image
159
+ },
160
+ "selector": {
161
+ "@type": "<selectorType>",
162
+ "value": "<selectorValue>"
163
+ },
164
+ "within": {
165
+ // extra context, such as the IIIF manifest in which to include the annotation
166
+ },
167
+ }
168
+ ```
169
+
170
+ ### Basic units
171
+
172
+ 3 conceptual units are present in the Specific Resource model:
173
+ - `Source resource`: the complete target , i.e. an entire image that we want to select a section of.
174
+ - `SpecificResource`: the segment of the resource described by the `Specifier`
175
+ - `Specifiers`: describes how to determine the aspects of the `Source` that constitute the Specific Resource.
176
+
177
+ ### Specifiers
178
+
179
+ There are 3 types of specifiers:
180
+ - `oa:State`: specifies the state of a `Body` or a `Target` applicable to an `Annotation` in order to retrieve the proper representation of the resource. For example,
181
+ - the timestamp at which a `Body` was created (if there are several versions of the same resource with different timestamps)
182
+ - `oa:Selector`: specifies the segment of a resource relevant to the `Annotation`: text range, image section, SVG selector...
183
+ - `oa:Style`: a description of how to style an `Annotation`
184
+
185
+ If `State` and `Selector` are present in a annotation, the `State` will be processed before `Selector` to be sure that we have the proper state.
186
+
187
+ Keys:
188
+ - `a` (IIIF `@type`) `oa:SpecificResource`: the specific resource
189
+ - `oa:hasSource` (SAS `full`): the `Source Resource` being specified by the `Specific Resource`.
190
+ - MUST be used: the `Specific Resource` is defined relatively to the `Source Resource`, and so the renderer will fetch the `Source` before targeting the `Specific resource` part.
191
+
192
+ I'll only present selectors, not `states` or `styles` that aren't used in IIIF.
193
+
194
+ ### Selectors
195
+
196
+ A `Selector` is a `Specifier` which describes how to determine the segment of interest from within the retrieved representation of the Source resource. There MUST be only 1 `Selector` per specifier, otherwise use the [multiplicity module](https://web.archive.org/web/20221225112718/http://www.openannotation.org/spec/core/multiplicity.html).
197
+
198
+ - `oa:hasSource` (IIIF `full`): the `Source resource`
199
+ - `oa:hasSelector` (IIIF `selector`): the `Selector`'s content
200
+
201
+ There are selectors other than those presented below: `RangeSelector`, `TextPositionSelector`, `TextQuoteSelector`... but they are out of scope with IIIF so I won't talk about them
202
+
203
+ #### FragmentSelector
204
+
205
+ - `a` (IIIF `@type`): `oa:FragmentSelector`
206
+ - `rdf:value` (IIIF `value`): `string'
207
+ - `dcterms:conformsTo`: the specification that defines the syntax of the fragment.
208
+ - for IIIF, targets can only be targeted using IIIF-specific `ImageApiSelector` or W3C media fragments (`xhwh=int,int,int,int`).
209
+ - SHOULD be used
210
+ - SHOULD be one of:
211
+
212
+ <table>
213
+ <tbody><tr><th>Fragment Specification</th><th>Description</th></tr>
214
+ <tr><td>http://tools.ietf.org/rfc/rfc3236</td><td><a href="https://web.archive.org/web/20221225112722/http://tools.ietf.org/rfc/rfc3236">XHTML, and HTML</a>. Example: #namedSection </td></tr>
215
+ <tr><td>http://tools.ietf.org/rfc/rfc3778</td><td><a href="https://web.archive.org/web/20221225112722/http://tools.ietf.org/rfc/rfc3778">PDF</a>. Example: #page=10&amp;viewrect=50,50,640,480</td></tr>
216
+ <tr><td>http://tools.ietf.org/rfc/rfc5147</td><td><a href="https://web.archive.org/web/20221225112722/http://tools.ietf.org/rfc/rfc5147">Plain Text</a>. Example: #char=0,10</td></tr>
217
+ <tr><td>http://tools.ietf.org/rfc/rfc3023</td><td><a href="https://web.archive.org/web/20221225112722/http://tools.ietf.org/rfc/rfc3023">XML</a>. Example: #xpointer(/a/b/c) </td></tr>
218
+ <tr><td>http://www.ietf.org/rfc/rfc3870</td><td><a href="https://web.archive.org/web/20221225112722/http://www.ietf.org/rfc/rfc3870">RDF/XML</a>. Example: #namedResource </td></tr>
219
+ <tr><td>http://www.w3.org/TR/media-frags/</td><td><a href="https://web.archive.org/web/20221225112722/http://www.w3.org/TR/media-frags/">W3C Media Fragments</a>. Example: #xywh=50,50,640,480</td></tr>
220
+ <tr><td>http://www.w3.org/TR/SVG/</td><td><a href="https://web.archive.org/web/20221225112722/http://www.w3.org/TR/SVG/">SVG</a>. Example: #svgView(viewBox(50,50,640,480))</td></tr>
221
+ </tbody></table>
222
+
223
+ ```js
224
+ {
225
+ // ...
226
+ selector: {
227
+ "@type": "oa:FragmentSelector",
228
+ "value": "xywh=int,int,int,int" // so here we use the W3C media fragments selector
229
+ }
230
+
231
+ }
232
+ ```
233
+
234
+ #### SVG selectors
235
+
236
+ `SvgSelectors` define an area through an SVG.
237
+ - the `SvgSelector`'s value MUST be a valid and complete SVG document
238
+ - the SVG SHOULD be a single shape: `path|rect|circle|ellipse|polyline|polygone|g`
239
+ - dimensions in the SVG MUST be relative to the `Source resource`.
240
+ - For example, given an image which is 600 pixels by 400 pixels, and the desired section is a circle of 100 pixel radius at the center of the image, then the SVG element would be: `<circle cx="300" cy="200" r="100"/>`
241
+
242
+
243
+ ### Examples
244
+
245
+
246
+ `Specific resource` structure in IIIF 2.x.
247
+ ```js
248
+ {
249
+ "@type" : "oa:SpecificResource",
250
+ "within" : {
251
+ "@id" : "https://aikon.enpc.fr/aikon/iiif/v2/wit9_man11_anno165/manifest.json",
252
+ "@type" : "sc:Manifest"
253
+ },
254
+ "selector" : {
255
+ "@type" : "oa:FragmentSelector",
256
+ "value" : "xywh=0,31,1865,1670"
257
+ },
258
+ "full" : "https://aikon.enpc.fr/aikon/iiif/v2/wit9_man11_anno165/canvas/c16.json"
259
+ }
260
+ ```
261
+
262
+ `Specific resource` that uses the IIIF `ImageApiSelector` extension
263
+ ```js
264
+ {
265
+ // `@id` uses the IIIF Image api to describe the fragment
266
+ "@id": "http://www.example.org/iiif/book1-page1/50,50,1250,1850/full/0/default.jpg",
267
+ "@type": "oa:SpecificResource",
268
+
269
+ // `full` describes the full image.
270
+ "full": {
271
+ "@id": "http://example.org/iiif/book1-page1/full/full/0/default.jpg",
272
+ "@type": "dctypes:Image",
273
+ "service": {
274
+ "@context": "http://iiif.io/api/image/2/context.json",
275
+ "@id": "http://example.org/iiif/book1-page1",
276
+ "profile": "http://iiif.io/api/image/2/level2.json"
277
+ }
278
+ },
279
+
280
+ // `selector` describes the region targeted in `@id`
281
+ "selector": {
282
+ "@context": "http://iiif.io/api/annex/openannotation/context.json",
283
+ "@type": "iiif:ImageApiSelector",
284
+ "region": "50,50,1250,1850"
285
+ }
286
+ }
287
+ ```
288
+
289
+ ---
290
+
291
+ ## Multiplicity
292
+
293
+ [https://web.archive.org/web/20221225112718/http://www.openannotation.org/spec/core/multiplicity.html](https://web.archive.org/web/20221225112718/http://www.openannotation.org/spec/core/multiplicity.html)
294
+
295
+ In IIIF, this module is useful if we want to specify more than 1 `SpecificResource`. In OA, the multiplicity module is used to have multiple bodies or targets in an annotation (IE: 3 bodies in 3 languages). Options for multiplicity are:
296
+
297
+ - `oa:Choice`: only one of several bodies/targets will be rendered at once. Done by defining 1 default value and other alternative values
298
+ - `oa:Composite`: when all resources are required for an annotation to function properly.
299
+ - `oa:List`: like a composite, but ordered
300
+
301
+ `Bodies` and `Targets` can use `multiplicity` by setting their `@type` to `oa:Choice | oa:Composite |oa:List`.
302
+
303
+ I have only ever seen `oa:Choice` be used in IIIF so that's the one I'll present.
304
+
305
+ ### `oa:Choice`
306
+
307
+ ```js
308
+ {
309
+ "@type": "oa:Choice",
310
+ "default": /** default choice */,
311
+ "item": /** one or several other available items */
312
+ }
313
+ ```
314
+
315
+ Example: a choice between a `FragmentSelector` and an `SvgSelector`
316
+ ```js
317
+ {
318
+ "@type" : "oa:Choice",
319
+ "default" : {
320
+ "@type" : "oa:FragmentSelector",
321
+ "value" : "xywh=0,31,1865,1670"
322
+ },
323
+ "item" : {
324
+ "@type" : "oa:SvgSelector",
325
+ "value" : "<svg xmlns=\"http://www.w3.org/2000/svg\"><path xmlns='http://www.w3.org/2000/svg' d='M0 31 h 932 v 0 h 932 v 835 v 835 h -932 h -932 v -835Z' id='rectangle_wit9_man11_anno165_c16_5ebd8bc8044b45b58aae2a508ca75eed' data-paper-data='{&quot;strokeWidth&quot;:1,&quot;rotation&quot;:0,&quot;annotation&quot;:null,&quot;nonHoverStrokeColor&quot;:[&quot;Color&quot;,0,1,0],&quot;editable&quot;:true,&quot;deleteIcon&quot;:null,&quot;rotationIcon&quot;:null,&quot;group&quot;:null}' fill-opacity='0' fill='#00ff00' fill-rule='nonzero' stroke='#00ff00' stroke-width='1' stroke-linecap='butt' stroke-linejoin='miter' stroke-miterlimit='10' stroke-dashoffset='0' style='mix-blend-mode: normal'/></svg>"
326
+ }
327
+ }
328
+ ```
329
+
330
+
331
+
332
+