The id
property is without a doubt one of the most prevalent properties found throughout a IIIF manifest. As the API
documentation states, all resource types MUST
have an id
property, and because of this the property is littered across the manifest. In our most basic work
type, simple image, the id
property is used 13 times. The definition of the id
property is simply
the URI that identifies the resource.
At first glance, this seems relatively trivial but deeper inspection of the API docs demonstrates the need to understand
the property with a little more care.
The API documentation specifies some important details about the use of the id
property within an embedded resource.
An embedded resource refers to a resource where the entire JSON representation of a resource is found within another resource.
According to the API docs, the value of id
for an embedded resource MAY
be the URI of the embedding resource
with a unique fragment on the end, but only in the case that the embedded resource is not a Canvas
. The API
documentation goes further to state explicitly that a Canvas
must have its own URI without a fragment.
It's important to understand what is meant by fragment. Section 5.3 of the IIIF Presentation API docs
explains that the canvas must not contain a fragment (a #
followed by further characters), as this would make it
impossible to refer to a segment of the Canvas’s area using the media fragment syntax of #xywh= for spatial regions,
and/or #t= for temporal segments. It also states that canvases MAY
be able to be dereferenced separately from the
Manifest via their URIs as well as being embedded.
A few years ago when we first implemented IIIF, this and other details regarding the id
property were things we
missed or didn't fully understand. To create id
properties for canvases, we concatenated a pattern to the end of
the value of the id
property of the manifest based on the order of the Canvas within the work.
1 2 3 4 5 6 7 8 9 10 11 | public function buildCanvas ($index, $uri, $pid) { $canvasId = $uri . '/canvas/' . $index; $title = $this->xpath->query('titleInfo[not(@type="alternative")]')[0]; $canvas = (object) [ "id" => $canvasId, "type" => 'Canvas', "label" => self::getLanguageArray($title, 'label', 'none'), "thumbnail" => self::buildThumbnail(200, 200) ]; } |
This concatenation results in a URI like below within the id
property of a Canvas:
1 2 3 4 5 6 7 8 9 | { "items": [ { "id": "https://digital.lib.utk.edu/assemble/manifest/rftaart/74/canvas/0", "type": "Canvas", "items": [] } ] } |
While we concatenated a pattern to the end of the manifest's id
property to represent the canvas, this is not a
problem since the concatenated value does not contain a fragment (a #
followed by further characters. The issue
instead relates to other rules regarding the id
property that we didn't fully understand. According to the docs,
the value of the id
must be a string and a URI. The API documentation has these rules about id
property
naming conventions:
The value must be a string, and the value must be an HTTP(S) URI for resources defined in this specification. If the resource is retrievable via HTTP(S), then the URI must be the URI at which it is published.
Additionally, the API docs include other important rules for producers and consumers regarding dereferencing URIs.
The existence of an HTTP(S) URI in the id property does not mean that the URI will always be dereferencable. If the resource with the id property is embedded, it may also be dereferenceable. If the resource is referenced, it must be dereferenceable.
As the docs state, a canvas id
MAY
be dereferenceable. It doesn't have to be, but it MAY
. The
problem is that a request on the URI for our id
on the Canvas returns the entire manifest for the work the canvas
is embedded within.
While this may seem harmless, it is not and can have unintended consequences. Recently, I decided to experiment with adding
cartographic information found within our descriptive metadata to our IIIF manifests. The catalyst for this was the
creation of the Research Computing Group at Saint Louis University's
navPlace viewer. This viewer makes use of GeoJSON
found within a IIIF manifest in a navPlace
property. The navPlace
property is available in an
extension to the IIIF Presentation API. The extension describes the
property as such:
The navPlace property identifies a single or multiple geographic areas pertinent to a resource using a GeoJSON Feature Collection containing one or more Features. These areas should be bounded discrete areas of the map akin to extents. These areas do not imply any level of accuracy, temporality, or state of existence.
The navPlace
property is allowed on IIIF collections, manifests, ranges, and canvases. The flexibility of the
extension and property causes challenges for consuming applications as its not clear where to find the property. This is
further complicated by the fact that the resources that contain embedded navPlace
properties may themsleves be embedded
or referenced. Because of that, navPlace viewer
assumes that it should attempt to dereference all id
properties of resources that navPlace
can be
embedded in order to ensure all navPlace
properties are found.
Adding navPlace
to our manifests caused no problems, but trying to implement navPlace viewer
to make use of that content did. For the first time that we are aware of, a consuming application started to attempt to
dereference our canvases embedded in our manifests. It does this because its not sure whether the canvas is referenced
or embedded. For us, while we treat canvases as embedded resources, each of the id
properties found within a
canvas is derefernceable. According to the API specification, this is okay. The only problem is that it is not
dereferenceable for the canvas but instead the manifest it is embedded within. This is also an issue with our ranges.
This causes an endless loop for the viewer and makes it impossible to serve our manifests.
Since this discovery, we have fixed this dereferencing issue. Now, id
properties are only dereferenceable if they
in fact represent the resource they are embedded within. Without navPlace viewer,
I'm unsure we would have ever realized we had a problem.
The viewer has raised other questions for me. 1. How should a
consuming application know that a resource is embedded or referenced? If the id
is derferenceable, should it be
assumed that it may contain additional properties not found within the current resource? 2. What do other viewers do? Do
Mirador, Universal Viewer, Clover, or IIIF Media Viewer have methods that perform the same level of dereferncing? The
IIIF Book contains a recipe entitled, Simplest Manifest - Image.
The recipe contains the minimal number of properties to be able to show how to serve an image. The recipe embeds all
the resources to ensure that users can understand what must be included. That being said, if you were building a
presentation manifest producer, you could simplify the JSON here to just this as long the canvas is derefenceable:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | { "@context": "http://iiif.io/api/presentation/3/context.json", "id": "https://iiif.io/api/cookbook/recipe/0001-mvm-image/manifest.json", "type": "Manifest", "label": { "en": [ "Image 1" ] }, "items": [ { "id": "https://iiif.io/api/cookbook/recipe/0001-mvm-image/canvas/p1", "type": "Canvas", } ] } |
Finally, if these resources can all be referenced, 3. how long may it take a viewer like navPlace viewer, to parse and render a Collection resource that references thousands of book like objects with many pages and table of contents objects that may be dereferencable, and 4. would it not be better to add a property for specifying whether a resource is embedded or referenced so that the consuming application doesn't have to attempt dereferencing all these resources?
These questions are things that I'm thinking about and discussing with others, but this experience has reminded me how important it is to read and understand a specification when implementing a new application.