セマンティックウェブ時代に向けた、RDF/Linked Open Data等の最新情報ブログ LOD Diary

2017
03/14

一歩進んだIIIFマニフェストの利用

IIIF

神崎 正英

メタデータの記述

資料を利用しやすい形で提供するためには、表示ツールの画像処理用だけでなく、画像の内容を人間の利用者に伝える情報が不可欠です。最小限の情報は各リソースに付与するlabelで得られますが、より詳しい説明文や利用のためのライセンスなどもぜひ知りたいところでしょう。

前回の資料オブジェクト全体に関する情報でも紹介したように、IIIFマニフェストでは、こうした記述、関連付けのためのプロパティをいくつか定義しています。この中で、汎用的なメタデータの記述に使えるmetadataプロパティの記述方法を少し詳しく見ておきましょう。

metadataは、仕様でプロパティとして定義されていない、表示のための情報項目を記述するものです。そのために情報の項目名を示すlabelと項目内容のvalueを対にし、一連の情報を配列として提示します。

例1

"metadata": [
 {
  "label": "Author",
  "value": "歌川広重"
 },
 {
  "label": "Published",
  "value": "嘉永年間"
 }
]...

著者を表すプロパティがあらかじめ定義されていれば、"Author": "歌川広重"と書けるところです。しかしツールに表示を求める(画像処理情報ではない)メタデータ項目は限定的にする必要があるので、代わりに何にでも使えるmetadataを用意しているわけです。

labelは文字列のみですが、valueはHTMLのマークアップを含めることができます(処理系はこの点に注意しなければなりません)。またmetadataはマニフェスト全体だけでなく、個々のカンバスなど他のリソースにも与えることができます。

※JSON-LDの機能を利用すれば、metadataを使わなくてもDublin Coreなどの語彙で必要なメタデータを記述できますが、IIIFのツールがそれを表示してくれるとは限りません。これはあくまで、ツールを通じて人間の利用者が理解できるようにするためのメタデータ記述なのです。

多言語対応

metadataに限らず、人が読むための情報は利用者によって望ましい言語が異なります。項目名や内容を多言語で提供するために、IIIFマニフェストではJSON-LDの言語情報付き文字列表現(以下「タグ付き文字列」)を利用しています。これはプロパティ値を構造化して@languageで言語コードを示し、本来のプロパティ値を@valueとするものです。

例2

"metadata": [
 {
  "label": [
   {"@value": "Author", "@language": "en"},
   {"@value": "作者", "@language": "ja"}
  ],
  "value": [
   {"@value": "Utagawa Hiroshige", "@language": "en"},
   {"@value": "歌川広重", "@language": "ja"}
  ]
 },
 ...
}...

value@valueの2種類があって混乱しそうですが、少なくともIIIFマニフェスト(2.1)においては、@valueはこのタグ付き文字列でしか用いられません。

タグ付き文字列は、ほかにlabeldescriptionattributionにも利用できます。せっかくの資源を国際的にも活用できるようにするため、ぜひ採用を検討したいところです。

図=言語切替による別言語説明の表示
図1:多言語対応メタデータがあれば、ツールは利用者の優先言語で表示した上で、切替手段を提供したりできる。

複数値と多言語

マニフェストのlabeldescriptionなども含めた表示用メタデータ項目は、複数値を持っても構いません。たとえばattributionとして権利/帰属を記述する対象が複数というケースは十分考えられます。JSONでは同じプロパティを反復できないので、この場合は値を配列とします。

例3

"attribution": [
 "デジタル画像は国立国会図書館デジタルライブラリより",
 "IIIFマニフェストは国デコImage Wall版にラベルや説明などを追加編集"
],
...

ここで、この複数プロパティ値をそれぞれ多言語で記述する必要が生じた場合はどうすればよいでしょうか。プロパティ値ごとの対応関係を保持するならば、入れ子の配列が必要です。

例4

"attribution": [
 [
  {"@value": "Digital images are part of National Diet...", "@language": "en"},
  {"@value": "デジタル画像は国立国会図書館デジタルライブラリより", "@language": "ja"}
 ],
 [
  {"@value": "The IIIF manifest was originally generated by....", "@language": "en"},
  {"@value": "IIIFマニフェストは国デコImage Wall版にラベルや...", "@language": "ja"}
 ]
],
...

この記述はJSON-LDとしては構わないのですが、IIIFでは採用されていません(ほとんどのビューアでエラーになります)。複数値を多言語化する場合は、タグ付き文字列オブジェクトを単純に列挙します。

例5

"attribution": [
 {"@value": "Digital images are part of National Diet...", "@language": "en"},
 {"@value": "デジタル画像は国立国会図書館デジタルライブラリより", "@language": "ja"},
 {"@value": "The IIIF manifest was originally generated by....", "@language": "en"},
 {"@value": "IIIFマニフェストは国デコImage Wall版にラベルや...", "@language": "ja"}
],
...

タグ付き文字列と単純文字列を混在させることも可能です。IIIFでの処理手順は、この場合「タグ付き文字列に適切な言語が見当たらなければ単純文字列を表示する」と定めています。

複数値を多言語化しているときは、この混在には注意が必要です。混在させられたプロパティ値は、本来グループ化すべきものがまとめられておらず、言語情報による区別しかありません。そのため値によって多言語だったりそうでなかったりすると、(優先言語処理の関係で)タグ付き文字列か単純文字列かどちらか一方しか表示されなくなってしまうのです。

なお、metadataプロパティの場合は項目名ごとにオブジェクトとして分かれているので、ある項目が多言語で別項目が単純文字列などと混在しても支障はありません。

レンジによる構造化

IIIFマニフェストには、画像などのカンバスを順番に列挙するシーケンス(Sequence)の他に、カンバスの論理的な順序やグループなどを構造化するレンジ(Range)が用意されています。シーケンスは全てのカンバスをフラットに並べた配列であるのに対し、レンジは階層構造をもたせたり、一部のカンバスだけを対象にして用いることが可能です。

レンジの役割とプロパティ

たとえば書物のデジタル画像の場合、その中の章や節といった内容の構造を示す手段が必要です。シーケンスは基本的に画像(カンバス)に対応する物理構造の情報を持ちますが、ページの途中で章が変わるなど、内容の構造をそこに多重化するのは問題が多いため、別途レンジという形で表現します。

レンジはシーケンスと同じくマニフェストの最上位に置かれ、structuresプロパティで記述されます。プロパティ値はすべてのレンジを要素とする配列です。この要素は章節のような入れ子構造の表現にも用いられますが、JSONオブジェクトにおいてはフラットに並びます。

各レンジは次のプロパティを持ちます。

プロパティ名値の内容
@idレンジのID。一意に識別できるURI
@typeレンジを示す型で、値はsc:Range
labelレンジの表示名(階層目次の項目名)
viewingHintレンジの階層位置を示すヒント
メンバーレンジに含まれるメンバーを示す(下記参照)

メンバーを表すプロパティは次の3種類です。

プロパティ名値の内容
rangesレンジの子となるレンジのIDの配列(入れ子階層)
canvasesレンジの子となるカンバスのIDの配列
membersレンジの子となるレンジ/カンバスを記述するオブジェクトの配列

rangesの各要素は、structuresプロパティ値として並ぶ別のレンジを参照します。これによって、JSONを入れ子にすることなく階層構造を表現しています。レンジがcanvasesを持てば、その各要素が階層の終点ということになります。一つのレンジがrangescanvasesの両方を持つことも可能な点に注意してください。

レンジの記述

上記のプロパティを用いて、簡単な目次構造を記述してみましょう。次の例に用いる資料は、中世の詩を集めた写本のデジタル画像です。羊皮紙を綴じ合わせる順序が間違っていたため、写本の148ページで終わる章の次は169ページから始まります。

図=本来は148の次は169
図2:p.149〜168は誤って挿入されたという研究結果だが、デジタル画像は写本の綴じ合わせ通りこれらも連番で提供されている。

例を単純化するため、1つ目の章は最後の詩だけを、次の章は章全体のみを目次にします。

例6

"structures": [
 {
  "@type": "sc:Range",
  "@id": "http://example.org/iif/cb/range/0",
  "label": "ブラヌス写本",
  "viewingHint": "top",
  "ranges": [
   "http://example.org/iif/cb/range/1",
   "http://example.org/iif/cb/range/3"
  ]
 },
 {
  "@type": "sc:Range",
  "@id": "http://example.org/iif/cb/range/1",
  "label": "恋の歌編",
  "ranges": ["http://example.org/iif/cb/range/2"]
 },
 {
  "@type": "sc:Range",
  "@id": "http://example.org/iif/cb/range/2",
  "label": "CB186 フローラよ",
  "canvases": ["http://example.org/iif/cb/canvas/p148"]
 },
 {
  "@type": "sc:Range",
  "@id": "http://example.org/iif/cb/range/3",
  "label": "酒の歌編",
  "canvases": ["http://example.org/iif/cb/canvas/p169"]
 }
]...

レンジの2と3が、写本画像としては連続しない2つのページ(カンバス)を指すことで、論理的な章の構成を示しています。

viewingHintの値をtopとして加えると、そのレンジが階層構造の出発点であることを示します。論理的なルートとして機能するものなので、ビューアによってはこの表示を飛ばして次の階層から目次を示すものもあります(たぶんその方が自然です)。

図=Range→Canvas
図3:レンジは並列にならび、カンバスもしくは別のレンジを参照する。右はこの構造をUniversal Viewerで表示したもの。書籍の目次のように、最上位と個別ページ(カンバス)は略し、章節(レンジ)のみが表示されている。

membersプロパティは表示API仕様の2.1から加わったもので、rangescanvasesの両方の役割をまとめ、さらに情報量を増やしています。そのためmembersプロパティの要素はURI文字列ではなくJSONオブジェクトです。

対象のURIを@idに記述し、レンジなのかカンバスなのかは@typeプロパティで示します。どちらの場合もlabelが必須です。上例の「恋の歌編」の部分をmembersに書き直してみましょう。

例7

 {
  "@type": "sc:Range",
  "@id": "http://example.org/iif/cb/range/1",
  "label": "恋の歌編",
  "members": [
   {
    "@type": "sc:Range",
    "label": "CB186",
    "@id": "http://example.org/iif/cb/range/2"
   }
  ]
 },
 {
  "@type": "sc:Range",
  "@id": "http://example.org/iif/cb/range/2",
  "label": "CB186 フローラよ",
  "members": [
   {
    "@type": "sc:Canvas",
    "label": "写本148ページ",
    "@id": "http://example.org/iif/cb/canvas/p148"
   }
  ]
 }...

この例でも分かるように、membersの要素としてのレンジと、その参照先(structuresの要素)のレンジの両方がラベルを持つことになります(どちらも必須)。どう使い分けて欲しいのか、仕様にも示されておらず不明です。

また、仕様書の当該セクションの最後で、バージョン3.0では、rangescanvasesを非推奨にしてmembersに一本化するとうたっていますが、現状ではmembersを正しく処理できないツールが多いので、当面はrangescanvasesの方が安心できそうです。

カンバスの部分の参照

章や節がページの途中で切り替わる場合、その境目を指すcanvasesの要素は、厳密に言えばページ(カンバス)単位ではなく、その中で当該章/節にあたる部分を示すべきということになるでしょう。こうした用途を念頭に、レンジにおいてはカンバスURIにメディアフラグメントを加えた識別子を利用できることになっています。

上の例で「CB186 フローラよ」は148ページの下部、座標(x,y,w,h)でいえば137,343,585,603ピクセルの範囲で、同じページのその上には「CB185 ああつらい」の後半が書かれています。フラグメントを使えば、レンジ2は次のように記述できます。

例8

{
 "@type": "sc:Range",
 "@id": "http://example.org/iif/cb/range/2",
 "label": "CB186 フローラよ",
 "canvases": [
  "http://example.org/iif/cb/canvas/p148#xywh=137,343,585,603"
 ]
}...

新聞の飛び地記事のような、本文の続きが別のページに置かれている構造もこの方法で表現できるわけです。これを利用して、ページを開いた時に該当部分をハイライト表示させることも可能でしょう。

図=ページ中のフラグメントで指定された範囲をハイライト表示
図4:試作ツールで、カンバスのフラグメントで指定された範囲をハイライトし、目次項目を表示してみたもの。

ただ残念ながら多くのツールはこのフラグメント指定に対応しておらず、かえってエラーを生じる場合もあるので、すぐに利用するのは難しいかもしれません。

コレクションの表現

資料はしばしば複数のオブジェクトがグループを構成します。書籍の上下巻や全集の各巻などは、それぞれの書籍オブジェクトとして独立した存在でありながら、グループとしての扱いも必要です。

これに対応する形で、IIIFで個別資料に対応するマニフェストをグループ化する方法がコレクションです。コレクションは、マニフェストとよく似た構造のJSONオブジェクトで、同じように@context@id、メタデータを持ちます。@typeの値はsc:Collectionで、sequencesの代わりにmanifestsプロパティを持ち、その値としてグループ化するマニフェストを列挙します。

国立国語研究所が日本語史研究資料として公開しているデジタル画像は、多くが上下巻などのグループになっています。この中から、徒然草(寛文七年版)上下巻のマニフェストをコレクションとして記述してみましょう。

例9

{
 "@context": "http://iiif.io/api/presentation/2/context.json",
 "@id": "http://example.org/iiif/collection/ninjaldl",
 "@type": "sc:Collection",
 "label": "徒然草(寛文七年版)",
 "manifests": [
  {
   "@id": "http://dglb01.ninjal.ac.jp/ninjaldl/turezure/001/manifest.json",
   "@type": "sc:Manifest",
   "label": "上巻"
  },
  {
   "@id": "http://dglb01.ninjal.ac.jp/ninjaldl/turezure/002/manifest.json",
   "@type": "sc:Manifest",
   "label": "下巻"
  }
 ]
}

manifestsの値となる配列要素は、@typesc:Manifestであることからも分かるようにマニフェストそのものなのですが、ここにsequencesを埋め込むことはなく、@idのURIにリンクする形です(レンジのmembersの値がカンバスであるときと同様です)。

コレクションの入れ子

コレクションはmanifestsによってマニフェストをグループ化するだけでなく、別のコレクションをcollectionsプロパティでまとめることもできます。

たとえば徒然草関連のデジタル画像は、人文学オープンデータ共同利用センターの日本古典籍データセットでも多数公開されています。この中から「徒然草寿命院抄」に関する3つの資料をグループ化し、さらに日本語史研究資料と合わせた親コレクションを作ってみます。子コレクションには@contextは不要です。

例10

{
 "@context": "http://iiif.io/api/presentation/2/context.json",
 "@id": "http://example.org/iif/collection/tsurezure",
 "@type": "sc:Collection",
 "label": "徒然草および関連書",
 "collections": [
  {
   "@id": "http://example.org/iif/collection/ninjald",
   "@type": "sc:Collection",
   "label": "徒然草(寛文七年版)",
   "manifests": [
    {
     "@id": "http://dglb01.ninjal.ac.jp/ninjaldl/turezure/001/manifest.json",
     "@type": "sc:Manifest",
     "label": "上巻"
    },
    ...
   ]
  },
  {
   "@id": "http://example.org/iif/collection/pmjt",
   "@type": "sc:Collection",
   "label": "徒然草寿命院抄",
   "manifests": [
    {
     "@id": "http://codh.rois.ac.jp/pmjt/book/200015714/manifest.json",
     "@type": "sc:Manifest",
     "label": "徒然草寿命院抄(写) 2冊〔89-76-1〜2〕"
    },
    ...
   ]
  }
 ]
}

コレクションが階層構造を持てるのは、レンジの場合とよく似ています。実際、Universal Viewerに入れ子コレクションのマニフェストを与えると、レンジによる目次とそっくりの形で表示されます(ただしコレクションの場合はJSONの記述も入れ子になるところが異なります)。

図=Universal Viewerはコレクションを階層目次として表示する
図5:コレクションはJSONも入れ子。UVのコレクション表示は、レンジの階層目次と同様に機能する。ただし子コレクションの@idからJSONを取得しようとするので、独立したファイルも合わせて用意する必要がある。

レンジと同様、manifestscollectionsを合わせたmembersも表現API仕様2.1からは使えるようになり、やはり3.0では前者を非推奨にするとしています。コレクションの場合は、もともとプロパティの配列要素がURI文字列ではなくJSONオブジェクトなので、membersも概ね問題なく使えそうです。

ページTOPへ