diff --git a/_includes/links.md b/_includes/links.md index 0f7620a31..421f8cac5 100644 --- a/_includes/links.md +++ b/_includes/links.md @@ -78,6 +78,7 @@ [0434]: {{ site.cookbook_url | absolute_url }}/recipe/0434-choice-av/ "Multiple Choice of Audio Formats in a Single View (Canvas)" [0489]: {{ site.cookbook_url | absolute_url }}/recipe/0489-multimedia-canvas/ "Rendering Multiple Media Types on a Time-Based Canvas" +[0540]: {{ site.cookbook_url | absolute_url }}/recipe/0540-link-for-opening-multiple-canvases/ "Sharing a link for opening two or more Canvases" [cookbook-process]: {{site.cookbook_url | absolute_url }}/recipe diff --git a/index.md b/index.md index c868e6fb7..326933b15 100644 --- a/index.md +++ b/index.md @@ -137,11 +137,13 @@ _(leading on to segmentation examples later)_ * [Linking to Structured Metadata][0053] (8) -## Sharing IIIF content +## Sharing IIIF content Recipes using [Content State API](https://iiif.io/api/content-state/1.0/) * [Loading a manifest with a viewer using a link][0466] * [Open a specific region of a canvas in a viewer][0485] +* [Sharing a link for opening two or more Canvases][0540] + ## Technical diff --git a/recipe/0540-link-for-opening-multiple-canvases/annotation.json b/recipe/0540-link-for-opening-multiple-canvases/annotation.json new file mode 100644 index 000000000..3a08f4b10 --- /dev/null +++ b/recipe/0540-link-for-opening-multiple-canvases/annotation.json @@ -0,0 +1,28 @@ +{ + "@context": "http://iiif.io/api/presentation/3/context.json", + "id": "{{ id.url }}", + "type": "Annotation", + "motivation": ["contentState"], + "target": [ + { + "id": "{{ id.path }}/canvas/2", + "type": "Canvas", + "partOf": [ + { + "id": "{{ id.path }}/manifest-2.json", + "type": "Manifest" + } + ] + }, + { + "id": "{{ id.path }}/canvas/p2", + "type": "Canvas", + "partOf": [ + { + "id": "{{ id.path }}/manifest.json", + "type": "Manifest" + } + ] + } + ] + } \ No newline at end of file diff --git a/recipe/0540-link-for-opening-multiple-canvases/canvases_sides_by_side.jpeg b/recipe/0540-link-for-opening-multiple-canvases/canvases_sides_by_side.jpeg new file mode 100644 index 000000000..de2d3f6e4 Binary files /dev/null and b/recipe/0540-link-for-opening-multiple-canvases/canvases_sides_by_side.jpeg differ diff --git a/recipe/0540-link-for-opening-multiple-canvases/index.md b/recipe/0540-link-for-opening-multiple-canvases/index.md new file mode 100644 index 000000000..dab1c864f --- /dev/null +++ b/recipe/0540-link-for-opening-multiple-canvases/index.md @@ -0,0 +1,57 @@ +--- +title: Sharing a link for opening two or more Canvases +id: 540 +layout: recipe +tags: [annotation, content-state] +summary: "Allows users to use Content State API to open two canvases at the same time." +viewers: +topic: + - content-state +--- + +## Use Case + +I want to share a link to a colleague showing two similar works from two different collections. I want to compare pages from two different manuscripts and share a link to open both on the same view. I want to save my current workspace (open Canvases in the current viewer) for later use. I want to open my current workspace with another viewer. + +![Illustration of a possible way of visualizing two Canvases from different IIIF objects.](canvases_sides_by_side.jpeg) + +## Implementation Notes + +Some viewers already implement custom formats for exporting the current workspace for sharing or later use. The Content State API could be used for the same purpose, adding the advantage of direct loading of the workspace using a crafted link with the `iiif-content` query parameter. The [multiple targets for a comparison view section](https://iiif.io/api/content-state/1.0/#53-multiple-targets-for-a-comparison-view) describes a method for targetting two Canvases at the same time; each Canvas could be from a different Manifest. + +For this purpose, we create an Annotation with `motivation` set to `["contentState"]`. +The value of the `target` property of the Annotation is a list containing the `id` of the Canvases and a `partOf` property with the `id` of the Manifests they belong to. + +We can hence encode the Annotation as explained in the [Content State encoding guidelines](https://iiif.io/api/content-state/1.0/#6-content-state-encoding), and then pass the encoded value, using the `iiif-content` query parameter of the viewer landing page: + +``` +https://example.org/viewer?iiif-content=JTdCJTIyJTQwY29udGV4dCUyMiUzQSUyMmh0dHAlM0ElMkYlMkZpaWlmLmlvJTJGYXBpJTJGcHJlc2VudGF0aW9uJTJGMyUyRmNvbnRleHQuanNvbiUyMiUyQyUyMmlkJTIyJTNBJTIyaHR0cHMlM0ElMkYlMkZwcmV2aWV3LmlpaWYuaW8lMkZjb29rYm9vayUyRjA1NDAtbGluay1mb3Itb3BlbmluZy1tdWx0aXBsZS1jYW52YXNlcyUyRnJlY2lwZSUyRjA1NDAtbGluay1mb3Itb3BlbmluZy1tdWx0aXBsZS1jYW52YXNlcyUyRmFubm90YXRpb24uanNvbiUyMiUyQyUyMnR5cGUlMjIlM0ElMjJBbm5vdGF0aW9uJTIyJTJDJTIybW90aXZhdGlvbiUyMiUzQSU1QiUyMmNvbnRlbnRTdGF0ZSUyMiU1RCUyQyUyMnRhcmdldCUyMiUzQSU1QiU3QiUyMmlkJTIyJTNBJTIyaHR0cHMlM0ElMkYlMkZpaWlmLmlvJTJGYXBpJTJGY29va2Jvb2slMkZyZWNpcGUlMkYwMzE4LW5hdlBsYWNlLW5hdkRhdGUlMkZjYW52YXMlMkYyJTIyJTJDJTIydHlwZSUyMiUzQSUyMkNhbnZhcyUyMiUyQyUyMnBhcnRPZiUyMiUzQSU1QiU3QiUyMmlkJTIyJTNBJTIyaHR0cHMlM0ElMkYlMkZpaWlmLmlvJTJGYXBpJTJGY29va2Jvb2slMkZyZWNpcGUlMkYwMzE4LW5hdlBsYWNlLW5hdkRhdGUlMkZtYW5pZmVzdC0yLmpzb24lMjIlMkMlMjJ0eXBlJTIyJTNBJTIyTWFuaWZlc3QlMjIlN0QlNUQlN0QlMkMlN0IlMjJpZCUyMiUzQSUyMmh0dHBzJTNBJTJGJTJGcHJldmlldy5paWlmLmlvJTJGY29va2Jvb2slMkYwNTQwLWxpbmstZm9yLW9wZW5pbmctbXVsdGlwbGUtY2FudmFzZXMlMkZyZWNpcGUlMkYwNTQwLWxpbmstZm9yLW9wZW5pbmctbXVsdGlwbGUtY2FudmFzZXMlMkZjYW52YXMlMkZwMiUyMiUyQyUyMnR5cGUlMjIlM0ElMjJDYW52YXMlMjIlMkMlMjJwYXJ0T2YlMjIlM0ElNUIlN0IlMjJpZCUyMiUzQSUyMmh0dHBzJTNBJTJGJTJGcHJldmlldy5paWlmLmlvJTJGY29va2Jvb2slMkYwNTQwLWxpbmstZm9yLW9wZW5pbmctbXVsdGlwbGUtY2FudmFzZXMlMkZyZWNpcGUlMkYwNTQwLWxpbmstZm9yLW9wZW5pbmctbXVsdGlwbGUtY2FudmFzZXMlMkZtYW5pZmVzdC5qc29uJTIyJTJDJTIydHlwZSUyMiUzQSUyMk1hbmlmZXN0JTIyJTdEJTVEJTdEJTVEJTdE +```` + +## Restrictions + +The Content State API does not define how the viewer should show the two Canvases. It only mentions: + +“This data structure can be used by clients to load the resource required, present a particular part of the resource to the user.” [https://iiif.io/api/content-state/1.0/#content-state](https://iiif.io/api/content-state/1.0/#content-state) + +Viewers may show the Canvases side by side or decide to use different approaches (e.g. opening two browser tabs). +## Example +In this example we want to compare two paintings of the Colosseum from two different Manifests. +We can notice that the Colosseum painting of the first Manifest is in the second Canvas. + +The Annotation will target the `id` of the two Canvases we want to compare and contain a reference to the two Manifests as shown in the example: + +{% include content-state-viewers.html iiif-content="annotation.json" viewers="" %} + +{% include jsonviewer.html src="annotation.json" %} + +The expected result should show the two Canvases of the two Manifest depicting the Colosseum. + +## Related Recipes + +* [Open a specific region of a canvas in a viewer][0485] shows how open a region of interest of a Canvas. +* [Link for loading a manifest][0466] another example of Content State API. + +{% include acronyms.md %} +{% include links.md %} + diff --git a/recipe/0540-link-for-opening-multiple-canvases/manifest-2.json b/recipe/0540-link-for-opening-multiple-canvases/manifest-2.json new file mode 100644 index 000000000..cef189574 --- /dev/null +++ b/recipe/0540-link-for-opening-multiple-canvases/manifest-2.json @@ -0,0 +1,51 @@ +{ + "@context": "http://iiif.io/api/presentation/3/context.json", + "id": "{{ id.url }}/manifest-2.json", + "type": "Manifest", + "label": { + "en": [ + "The Colosseum" + ] + }, + "items": [ + { + "id": "{{ id.path }}/canvas/2", + "type": "Canvas", + "height": 1529, + "width": 2048, + "label": { + "en": [ + "The Colosseum" + ] + }, + "items": [ + { + "id": "{{ id.path }}/anno-page/2", + "type": "AnnotationPage", + "items": [ + { + "id": "{{ id.path }}/anno/2", + "type": "Annotation", + "motivation": "painting", + "body": { + "id": "https://iiif.io/api/image/3.0/example/reference/71b9228e087f15c75b628214cd9f647d-The_Colosseum/full/max/0/default.jpg", + "type": "Image", + "format": "image/jpeg", + "height": 1529, + "width": 2048, + "service": [ + { + "id": "https://iiif.io/api/image/3.0/example/reference/71b9228e087f15c75b628214cd9f647d-The_Colosseum", + "profile": "level1", + "type": "ImageService3" + } + ] + }, + "target": "{{ id.path }}/canvas/2" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/recipe/0540-link-for-opening-multiple-canvases/manifest.json b/recipe/0540-link-for-opening-multiple-canvases/manifest.json new file mode 100644 index 000000000..ae04eb99d --- /dev/null +++ b/recipe/0540-link-for-opening-multiple-canvases/manifest.json @@ -0,0 +1,76 @@ +{ + "@context": "http://iiif.io/api/presentation/3/context.json", + "id": "{{ id.url }}/manifest.json", + "type": "Manifest", + "label": { + "en": [ + "Two monuments in Rome" + ] + }, + "items": [ + { + "id": "{{ id.path }}/canvas/p1", + "type": "Canvas", + "label": { + "en": [ + "The Temple of Vesta Rome" + ] + }, + "height": 1464, + "width": 2048, + "items": [ + { + "id": "{{ id.path }}/page/p1/1", + "type": "AnnotationPage", + "items": [ + { + "id": "{{ id.path }}/annotation/p0001-image", + "type": "Annotation", + "motivation": "painting", + "body": { + "id": "https://fixtures.iiif.io/images/Yale/ycba/The_Temple_of_Vesta_Rome.jpg", + "type": "Image", + "format": "image/jpeg", + "height": 1464, + "width": 2048 + }, + "target": "{{ id.path }}/canvas/p1" + } + ] + } + ] + }, + { + "id": "{{ id.path }}/canvas/p2", + "type": "Canvas", + "label": { + "en": [ + "The Colosseum" + ] + }, + "height": 1302, + "width": 2048, + "items": [ + { + "id": "{{ id.path }}/page/p2/1", + "type": "AnnotationPage", + "items": [ + { + "id": "{{ id.path }}/annotation/p0002-image", + "type": "Annotation", + "motivation": "painting", + "body": { + "id": "https://fixtures.iiif.io/images/Yale/ycba/The_Colosseum_Rome2.jpg", + "type": "Image", + "format": "image/jpeg", + "height": 1302, + "width": 2048 + }, + "target": "{{ id.path }}/canvas/p2" + } + ] + } + ] + } + ] + } \ No newline at end of file