ホーム 記事一覧 ブック DH週間トピックス 検索 このサイトについて
English
DTS Viewerの更新:ページネーションへの対応

DTS Viewerの更新:ページネーションへの対応

概要 DTS (Distributed Text Services)ビューアについて、ページネーションへの対応を行ったので、備忘録です。 https://dts-viewer.vercel.app/ja/ 背景 DTSで多数のリソースなどを提供する際に、以下のように、viewプロパティを使って、ページネーションに関する情報を提示するようでした。 https://distributed-text-services.github.io/specifications/versions/unstable/#collection-endpoint { "@context": "https://distributed-text-services.github.io/specifications/context/1-alpha1.json", "dtsVersion": "1-alpha", "@id" : "lettres_de_poilus", "@type" : "Collection", "collection": "/api/dts/collection/{?id,page,nav}", "totalParents": 1, "totalChildren": 10000, "title": "Lettres de Poilus", "dublinCore": { "publisher": ["École Nationale des Chartes", "https://viaf.org/viaf/167874585"], "title": [ {"lang": "fr", "value" : "Lettres de Poilus"} ] }, "member": [ "..." ], "view": { "@id": "/api/dts/collection/?id=lettres_de_poilus&page=19", "@type": "Pagination", "first": "/api/dts/collection/?id=lettres_de_poilus&page=1", "previous": "/api/dts/collection/?id=lettres_de_poilus&page=18", "next": "/api/dts/collection/?id=lettres_de_poilus&page=20", "last": "/api/dts/collection/?id=lettres_de_poilus&page=500" } } そこで、DTS Viewerについて、上記のviewプロパティに対応できるように改修しました。 表示例 コレクションがviewプロパティを持つ時、以下のように、ページネーションに関するボタンを表示するようにしました。 まとめ DTS (Distributed Text Services) APIを用いたデジタルテキストのコレクションの配信にあたり、参考になりましたら幸いです。

IIIFマニフェストファイルから画像URLの一覧を含むCSVファイルを作成する

IIIFマニフェストファイルから画像URLの一覧を含むCSVファイルを作成する

概要 IIIFマニフェストファイルから画像URLの一覧を含むCSVファイルを作成するアプリを作成したので備忘録です。 以下からアクセスいただけます。 https://iiif-demo-next.vercel.app/csv-converter 使い方 国立国会図書館所蔵の「校異源氏物語. 巻一」を対象とします。 https://dl.ndl.go.jp/api/iiif/3437686/manifest.json 以下のフォームにマニフェストファイルのURLを入力し、「CSVをダウンロード」ボタンを押します。 結果、以下のように、URLやサイズの情報を含むCSVファイルがダウンロードされます。 url,width,height,canvas https://dl.ndl.go.jp/api/iiif/3437686/R0000001/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/1 https://dl.ndl.go.jp/api/iiif/3437686/R0000002/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/2 https://dl.ndl.go.jp/api/iiif/3437686/R0000003/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/3 https://dl.ndl.go.jp/api/iiif/3437686/R0000004/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/4 https://dl.ndl.go.jp/api/iiif/3437686/R0000005/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/5 https://dl.ndl.go.jp/api/iiif/3437686/R0000006/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/6 https://dl.ndl.go.jp/api/iiif/3437686/R0000007/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/7 https://dl.ndl.go.jp/api/iiif/3437686/R0000008/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/8 https://dl.ndl.go.jp/api/iiif/3437686/R0000009/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/9 https://dl.ndl.go.jp/api/iiif/3437686/R0000010/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/10 https://dl.ndl.go.jp/api/iiif/3437686/R0000011/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/11 https://dl.ndl.go.jp/api/iiif/3437686/R0000012/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/12 https://dl.ndl.go.jp/api/iiif/3437686/R0000013/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/13 https://dl.ndl.go.jp/api/iiif/3437686/R0000014/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/14 https://dl.ndl.go.jp/api/iiif/3437686/R0000015/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/15 https://dl.ndl.go.jp/api/iiif/3437686/R0000016/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/16 https://dl.ndl.go.jp/api/iiif/3437686/R0000017/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/17 https://dl.ndl.go.jp/api/iiif/3437686/R0000018/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/18 https://dl.ndl.go.jp/api/iiif/3437686/R0000019/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/19 https://dl.ndl.go.jp/api/iiif/3437686/R0000020/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/20 https://dl.ndl.go.jp/api/iiif/3437686/R0000021/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/21 https://dl.ndl.go.jp/api/iiif/3437686/R0000022/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/22 https://dl.ndl.go.jp/api/iiif/3437686/R0000023/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/23 https://dl.ndl.go.jp/api/iiif/3437686/R0000024/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/24 https://dl.ndl.go.jp/api/iiif/3437686/R0000025/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/25 https://dl.ndl.go.jp/api/iiif/3437686/R0000026/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/26 https://dl.ndl.go.jp/api/iiif/3437686/R0000027/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/27 https://dl.ndl.go.jp/api/iiif/3437686/R0000028/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/28 https://dl.ndl.go.jp/api/iiif/3437686/R0000029/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/29 https://dl.ndl.go.jp/api/iiif/3437686/R0000030/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/30 https://dl.ndl.go.jp/api/iiif/3437686/R0000031/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/31 https://dl.ndl.go.jp/api/iiif/3437686/R0000032/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/32 https://dl.ndl.go.jp/api/iiif/3437686/R0000033/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/33 https://dl.ndl.go.jp/api/iiif/3437686/R0000034/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/34 https://dl.ndl.go.jp/api/iiif/3437686/R0000035/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/35 https://dl.ndl.go.jp/api/iiif/3437686/R0000036/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/36 https://dl.ndl.go.jp/api/iiif/3437686/R0000037/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/37 https://dl.ndl.go.jp/api/iiif/3437686/R0000038/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/38 https://dl.ndl.go.jp/api/iiif/3437686/R0000039/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/39 https://dl.ndl.go.jp/api/iiif/3437686/R0000040/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/40 https://dl.ndl.go.jp/api/iiif/3437686/R0000041/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/41 https://dl.ndl.go.jp/api/iiif/3437686/R0000042/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/42 https://dl.ndl.go.jp/api/iiif/3437686/R0000043/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/43 https://dl.ndl.go.jp/api/iiif/3437686/R0000044/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/44 https://dl.ndl.go.jp/api/iiif/3437686/R0000045/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/45 https://dl.ndl.go.jp/api/iiif/3437686/R0000046/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/46 https://dl.ndl.go.jp/api/iiif/3437686/R0000047/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/47 https://dl.ndl.go.jp/api/iiif/3437686/R0000048/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/48 https://dl.ndl.go.jp/api/iiif/3437686/R0000049/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/49 https://dl.ndl.go.jp/api/iiif/3437686/R0000050/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/50 https://dl.ndl.go.jp/api/iiif/3437686/R0000051/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/51 https://dl.ndl.go.jp/api/iiif/3437686/R0000052/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/52 https://dl.ndl.go.jp/api/iiif/3437686/R0000053/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/53 https://dl.ndl.go.jp/api/iiif/3437686/R0000054/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/54 https://dl.ndl.go.jp/api/iiif/3437686/R0000055/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/55 https://dl.ndl.go.jp/api/iiif/3437686/R0000056/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/56 https://dl.ndl.go.jp/api/iiif/3437686/R0000057/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/57 https://dl.ndl.go.jp/api/iiif/3437686/R0000058/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/58 https://dl.ndl.go.jp/api/iiif/3437686/R0000059/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/59 https://dl.ndl.go.jp/api/iiif/3437686/R0000060/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/60 https://dl.ndl.go.jp/api/iiif/3437686/R0000061/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/61 https://dl.ndl.go.jp/api/iiif/3437686/R0000062/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/62 https://dl.ndl.go.jp/api/iiif/3437686/R0000063/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/63 https://dl.ndl.go.jp/api/iiif/3437686/R0000064/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/64 https://dl.ndl.go.jp/api/iiif/3437686/R0000065/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/65 https://dl.ndl.go.jp/api/iiif/3437686/R0000066/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/66 https://dl.ndl.go.jp/api/iiif/3437686/R0000067/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/67 https://dl.ndl.go.jp/api/iiif/3437686/R0000068/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/68 https://dl.ndl.go.jp/api/iiif/3437686/R0000069/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/69 https://dl.ndl.go.jp/api/iiif/3437686/R0000070/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/70 https://dl.ndl.go.jp/api/iiif/3437686/R0000071/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/71 https://dl.ndl.go.jp/api/iiif/3437686/R0000072/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/72 https://dl.ndl.go.jp/api/iiif/3437686/R0000073/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/73 https://dl.ndl.go.jp/api/iiif/3437686/R0000074/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/74 https://dl.ndl.go.jp/api/iiif/3437686/R0000075/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/75 https://dl.ndl.go.jp/api/iiif/3437686/R0000076/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/76 https://dl.ndl.go.jp/api/iiif/3437686/R0000077/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/77 https://dl.ndl.go.jp/api/iiif/3437686/R0000078/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/78 https://dl.ndl.go.jp/api/iiif/3437686/R0000079/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/79 https://dl.ndl.go.jp/api/iiif/3437686/R0000080/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/80 https://dl.ndl.go.jp/api/iiif/3437686/R0000081/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/81 https://dl.ndl.go.jp/api/iiif/3437686/R0000082/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/82 https://dl.ndl.go.jp/api/iiif/3437686/R0000083/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/83 https://dl.ndl.go.jp/api/iiif/3437686/R0000084/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/84 https://dl.ndl.go.jp/api/iiif/3437686/R0000085/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/85 https://dl.ndl.go.jp/api/iiif/3437686/R0000086/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/86 https://dl.ndl.go.jp/api/iiif/3437686/R0000087/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/87 https://dl.ndl.go.jp/api/iiif/3437686/R0000088/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/88 https://dl.ndl.go.jp/api/iiif/3437686/R0000089/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/89 https://dl.ndl.go.jp/api/iiif/3437686/R0000090/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/90 https://dl.ndl.go.jp/api/iiif/3437686/R0000091/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/91 https://dl.ndl.go.jp/api/iiif/3437686/R0000092/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/92 https://dl.ndl.go.jp/api/iiif/3437686/R0000093/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/93 https://dl.ndl.go.jp/api/iiif/3437686/R0000094/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/94 https://dl.ndl.go.jp/api/iiif/3437686/R0000095/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/95 https://dl.ndl.go.jp/api/iiif/3437686/R0000096/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/96 https://dl.ndl.go.jp/api/iiif/3437686/R0000097/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/97 https://dl.ndl.go.jp/api/iiif/3437686/R0000098/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/98 https://dl.ndl.go.jp/api/iiif/3437686/R0000099/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/99 https://dl.ndl.go.jp/api/iiif/3437686/R0000100/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/100 https://dl.ndl.go.jp/api/iiif/3437686/R0000101/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/101 https://dl.ndl.go.jp/api/iiif/3437686/R0000102/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/102 https://dl.ndl.go.jp/api/iiif/3437686/R0000103/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/103 https://dl.ndl.go.jp/api/iiif/3437686/R0000104/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/104 https://dl.ndl.go.jp/api/iiif/3437686/R0000105/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/105 https://dl.ndl.go.jp/api/iiif/3437686/R0000106/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/106 https://dl.ndl.go.jp/api/iiif/3437686/R0000107/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/107 https://dl.ndl.go.jp/api/iiif/3437686/R0000108/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/108 https://dl.ndl.go.jp/api/iiif/3437686/R0000109/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/109 https://dl.ndl.go.jp/api/iiif/3437686/R0000110/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/110 https://dl.ndl.go.jp/api/iiif/3437686/R0000111/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/111 https://dl.ndl.go.jp/api/iiif/3437686/R0000112/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/112 https://dl.ndl.go.jp/api/iiif/3437686/R0000113/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/113 https://dl.ndl.go.jp/api/iiif/3437686/R0000114/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/114 https://dl.ndl.go.jp/api/iiif/3437686/R0000115/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/115 https://dl.ndl.go.jp/api/iiif/3437686/R0000116/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/116 https://dl.ndl.go.jp/api/iiif/3437686/R0000117/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/117 https://dl.ndl.go.jp/api/iiif/3437686/R0000118/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/118 https://dl.ndl.go.jp/api/iiif/3437686/R0000119/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/119 https://dl.ndl.go.jp/api/iiif/3437686/R0000120/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/120 https://dl.ndl.go.jp/api/iiif/3437686/R0000121/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/121 https://dl.ndl.go.jp/api/iiif/3437686/R0000122/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/122 https://dl.ndl.go.jp/api/iiif/3437686/R0000123/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/123 https://dl.ndl.go.jp/api/iiif/3437686/R0000124/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/124 https://dl.ndl.go.jp/api/iiif/3437686/R0000125/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/125 https://dl.ndl.go.jp/api/iiif/3437686/R0000126/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/126 https://dl.ndl.go.jp/api/iiif/3437686/R0000127/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/127 https://dl.ndl.go.jp/api/iiif/3437686/R0000128/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/128 https://dl.ndl.go.jp/api/iiif/3437686/R0000129/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/129 https://dl.ndl.go.jp/api/iiif/3437686/R0000130/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/130 https://dl.ndl.go.jp/api/iiif/3437686/R0000131/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/131 https://dl.ndl.go.jp/api/iiif/3437686/R0000132/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/132 https://dl.ndl.go.jp/api/iiif/3437686/R0000133/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/133 https://dl.ndl.go.jp/api/iiif/3437686/R0000134/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/134 https://dl.ndl.go.jp/api/iiif/3437686/R0000135/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/135 https://dl.ndl.go.jp/api/iiif/3437686/R0000136/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/136 https://dl.ndl.go.jp/api/iiif/3437686/R0000137/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/137 https://dl.ndl.go.jp/api/iiif/3437686/R0000138/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/138 https://dl.ndl.go.jp/api/iiif/3437686/R0000139/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/139 https://dl.ndl.go.jp/api/iiif/3437686/R0000140/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/140 https://dl.ndl.go.jp/api/iiif/3437686/R0000141/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/141 https://dl.ndl.go.jp/api/iiif/3437686/R0000142/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/142 https://dl.ndl.go.jp/api/iiif/3437686/R0000143/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/143 https://dl.ndl.go.jp/api/iiif/3437686/R0000144/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/144 https://dl.ndl.go.jp/api/iiif/3437686/R0000145/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/145 https://dl.ndl.go.jp/api/iiif/3437686/R0000146/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/146 https://dl.ndl.go.jp/api/iiif/3437686/R0000147/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/147 https://dl.ndl.go.jp/api/iiif/3437686/R0000148/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/148 https://dl.ndl.go.jp/api/iiif/3437686/R0000149/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/149 https://dl.ndl.go.jp/api/iiif/3437686/R0000150/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/150 https://dl.ndl.go.jp/api/iiif/3437686/R0000151/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/151 https://dl.ndl.go.jp/api/iiif/3437686/R0000152/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/152 https://dl.ndl.go.jp/api/iiif/3437686/R0000153/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/153 https://dl.ndl.go.jp/api/iiif/3437686/R0000154/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/154 https://dl.ndl.go.jp/api/iiif/3437686/R0000155/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/155 https://dl.ndl.go.jp/api/iiif/3437686/R0000156/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/156 https://dl.ndl.go.jp/api/iiif/3437686/R0000157/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/157 https://dl.ndl.go.jp/api/iiif/3437686/R0000158/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/158 https://dl.ndl.go.jp/api/iiif/3437686/R0000159/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/159 https://dl.ndl.go.jp/api/iiif/3437686/R0000160/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/160 https://dl.ndl.go.jp/api/iiif/3437686/R0000161/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/161 https://dl.ndl.go.jp/api/iiif/3437686/R0000162/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/162 https://dl.ndl.go.jp/api/iiif/3437686/R0000163/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/163 https://dl.ndl.go.jp/api/iiif/3437686/R0000164/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/164 https://dl.ndl.go.jp/api/iiif/3437686/R0000165/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/165 https://dl.ndl.go.jp/api/iiif/3437686/R0000166/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/166 https://dl.ndl.go.jp/api/iiif/3437686/R0000167/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/167 https://dl.ndl.go.jp/api/iiif/3437686/R0000168/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/168 https://dl.ndl.go.jp/api/iiif/3437686/R0000169/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/169 https://dl.ndl.go.jp/api/iiif/3437686/R0000170/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/170 https://dl.ndl.go.jp/api/iiif/3437686/R0000171/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/171 https://dl.ndl.go.jp/api/iiif/3437686/R0000172/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/172 https://dl.ndl.go.jp/api/iiif/3437686/R0000173/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/173 https://dl.ndl.go.jp/api/iiif/3437686/R0000174/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/174 https://dl.ndl.go.jp/api/iiif/3437686/R0000175/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/175 https://dl.ndl.go.jp/api/iiif/3437686/R0000176/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/176 https://dl.ndl.go.jp/api/iiif/3437686/R0000177/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/177 https://dl.ndl.go.jp/api/iiif/3437686/R0000178/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/178 https://dl.ndl.go.jp/api/iiif/3437686/R0000179/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/179 https://dl.ndl.go.jp/api/iiif/3437686/R0000180/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/180 https://dl.ndl.go.jp/api/iiif/3437686/R0000181/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/181 https://dl.ndl.go.jp/api/iiif/3437686/R0000182/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/182 https://dl.ndl.go.jp/api/iiif/3437686/R0000183/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/183 https://dl.ndl.go.jp/api/iiif/3437686/R0000184/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/184 https://dl.ndl.go.jp/api/iiif/3437686/R0000185/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/185 https://dl.ndl.go.jp/api/iiif/3437686/R0000186/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/186 https://dl.ndl.go.jp/api/iiif/3437686/R0000187/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/187 https://dl.ndl.go.jp/api/iiif/3437686/R0000188/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/188 https://dl.ndl.go.jp/api/iiif/3437686/R0000189/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/189 https://dl.ndl.go.jp/api/iiif/3437686/R0000190/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/190 https://dl.ndl.go.jp/api/iiif/3437686/R0000191/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/191 https://dl.ndl.go.jp/api/iiif/3437686/R0000192/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/192 https://dl.ndl.go.jp/api/iiif/3437686/R0000193/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/193 https://dl.ndl.go.jp/api/iiif/3437686/R0000194/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/194 https://dl.ndl.go.jp/api/iiif/3437686/R0000195/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/195 https://dl.ndl.go.jp/api/iiif/3437686/R0000196/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/196 https://dl.ndl.go.jp/api/iiif/3437686/R0000197/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/197 https://dl.ndl.go.jp/api/iiif/3437686/R0000198/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/198 https://dl.ndl.go.jp/api/iiif/3437686/R0000199/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/199 https://dl.ndl.go.jp/api/iiif/3437686/R0000200/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/200 https://dl.ndl.go.jp/api/iiif/3437686/R0000201/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/201 https://dl.ndl.go.jp/api/iiif/3437686/R0000202/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/202 https://dl.ndl.go.jp/api/iiif/3437686/R0000203/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/203 https://dl.ndl.go.jp/api/iiif/3437686/R0000204/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/204 https://dl.ndl.go.jp/api/iiif/3437686/R0000205/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/205 https://dl.ndl.go.jp/api/iiif/3437686/R0000206/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/206 https://dl.ndl.go.jp/api/iiif/3437686/R0000207/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/207 https://dl.ndl.go.jp/api/iiif/3437686/R0000208/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/208 https://dl.ndl.go.jp/api/iiif/3437686/R0000209/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/209 https://dl.ndl.go.jp/api/iiif/3437686/R0000210/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/210 https://dl.ndl.go.jp/api/iiif/3437686/R0000211/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/211 https://dl.ndl.go.jp/api/iiif/3437686/R0000212/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/212 https://dl.ndl.go.jp/api/iiif/3437686/R0000213/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/213 https://dl.ndl.go.jp/api/iiif/3437686/R0000214/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/214 https://dl.ndl.go.jp/api/iiif/3437686/R0000215/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/215 https://dl.ndl.go.jp/api/iiif/3437686/R0000216/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/216 https://dl.ndl.go.jp/api/iiif/3437686/R0000217/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/217 https://dl.ndl.go.jp/api/iiif/3437686/R0000218/info.json,6890,4706,https://dl.ndl.go.jp/api/iiif/3437686/canvas/218 メモ 以下の記事で、@iiif/parserというnpmモジュールを紹介しました。 ...

TEI/XMLファイルをS3互換のオブジェクトストレージでホストする

TEI/XMLファイルをS3互換のオブジェクトストレージでホストする

概要 TEI/XMLファイルをS3互換のオブジェクトストレージでホストする機会がありましたので、備忘録です。具体的には、mdx Iのオブジェクトストレージを対象にします。 https://mdx.jp/mdx1/p/about/system 背景 TEI/XMLファイルを読み込み、その内容を可視化するウェブアプリケーション(Next.js)を構築します。この時、ファイル数やサイズが小さい場合は、publicフォルダに格納していましたが、これらが大きくなった場合、別の場所でホストすることを考えました。 場所の選択肢は多々ありますが、今回はS3互換であるmdx Iのオブジェクトストレージを対象にします。 GUIを用いたオブジェクトストレージへのファイルアップロード オブジェクトストレージへTEI/XMLファイルをGUI経由でアップロードする方法も多々あります。その中で、これまではCyberduckを使用する方法や、GakunNin RDMを使用する方法などを紹介しました。 一方、今回の事例では、TEI/XML以外のコンテンツをDrupalで管理していました。そこで、Drupalとオブジェクトストレージを接続し、ユーザはDrupalの操作で完結できるようにしました。 Drupalとオブジェクトストレージの接続 以下のモジュールを使用します。 https://www.drupal.org/project/s3fs インストール後、環境設定のページ/admin/configから、S3 File Systemを選択します。 そして、アクセスキーや秘密鍵を登録し、さらにS3のバケット名を登録します。 またAdvanced Configuration OptionsのCustom Host Settingsにおいて、https://s3ds.mdx.jpを入力します。 これでオブジェクトストレージとの接続設定は完了です。 その後、各コンテンツタイプのフィード設定において、アップロード先として「S3 File System」を選択します。 また、今回はTEI/XMLファイルがアップロード対象となるため、「許可されている拡張子」として、xmlを入力します。 この結果、DrupalのGUIを介してアップロードしたTEI/XMLファイルが、mdx Iのオブジェクトストレージに格納されるようになりました。 (参考)DrupalのJSON:APIを用いたファイルの一括アップロード TEI/XMLの初期登録にあたり、Pythonを用いた一括登録を行いました。JSON:APIを用いたファイルの一括アップロードの方法は、以下の記事などが参考になりました。 https://www.drupal.org/node/3024331 一例ですが、以下のようなスクリプトで実現できました。 import requests import json import os from dotenv import load_dotenv from glob import glob from tqdm import tqdm class ApiClient: def __init__(self): load_dotenv(override=True) # DrupalサイトのURL(例) self.DRUPAL_BASE_URL = os.getenv("DRUPAL_BASE_URL") # エンドポイント(JSON:API) # self.JSONAPI_ENDPOINT = f"{self.DRUPAL_BASE_URL}/jsonapi/node/article" # 認証情報(Basic認証) self.USERNAME = os.getenv("DRUPAL_USERNAME") self.PASSWORD = os.getenv("DRUPAL_PASSWORD") def login(self): # ログインリクエスト login_url = f"{self.DRUPAL_BASE_URL}/user/login?_format=json" login_response = requests.post( login_url, json={"name": self.USERNAME, "pass": self.PASSWORD}, headers={"Content-Type": "application/json"} ) if login_response.status_code == 200: self.session_cookies = login_response.cookies def get_csrf_token(self): # CSRFトークンを取得 csrf_token_response = requests.get( f"{self.DRUPAL_BASE_URL}/session/token", cookies=self.session_cookies # ここでログインセッションを渡す ) if csrf_token_response.status_code == 200: # return csrf_token_response.text # self.csrf_token = csrf_token_response.text self.headers = { "Content-Type": "application/vnd.api+json", "Accept": "application/vnd.api+json", "X-CSRF-Token": csrf_token_response.text, } else: # raise Exception(f"CSRFトークン取得失敗: {csrf_token_response.status_code} {csrf_token_response.text}") self.csrf_token = None def upload_file(self, type, uuid, field, file_path, verbose=False): url = f"{self.DRUPAL_BASE_URL}/jsonapi/node/{type}/{uuid}/{field}" # ファイル名を取得 filename = os.path.basename(file_path) # ファイルをバイナリモードで読み込む with open(file_path, 'rb') as f: file_data = f.read() headers = self.headers.copy() headers['Content-Type'] = 'application/octet-stream' headers['Content-Disposition'] = f'attachment; filename="{filename}"' # ファイルをアップロード response = requests.post(url, headers=headers, cookies=self.session_cookies, data=file_data) if response.status_code == 200: if verbose: print(f"ファイルアップロード成功: {filename}") else: print(f"ファイルアップロード失敗: {response.status_code} {response.text}") すでに対象コンテンツが作成済みで、例えばfield_fileといったフィールドにファイルをアップロードする目的で使用することができます。 ...

Google Cloud Vision APIとGakuNin RDMを用いたTEI/XMLファイル作成アプリの試作

Google Cloud Vision APIとGakuNin RDMを用いたTEI/XMLファイル作成アプリの試作

概要 Google Cloud Vision APIとGakuNin RDMを用いたTEI/XMLファイル作成アプリを試作しましたので備忘録です。 背景 Google Cloud Vision APIを使ってOCR結果を反映したTEI/XMLファイルを作成する環境が必要になりました。そこでバックエンドとしてGakuNin RDMを用いて、ユーザごとにファイルを管理して、OCRを実行可能な環境を試作しました。 使い方 フォルダの作成 以下にアクセスします。 https://ge-manager.vercel.app/ 画面右上から、GakuNin RDMを使ってログインします。 以下のようにプロジェクト一覧が表示されます。 適当な階層まで下り、フォルダの作成ボタンを押します。 ここでは、「sample」というフォルダを作成します。 そして、「GE Manager」のリンクを押します。 以下のようなページに遷移します。 処理の実行 今回は、「e-codices - Virtual Manuscript Library of Switzerland」の「fragm1a」を使用させていただきます。 https://www.e-codices.unifr.ch/loris/gau/gau-Fragment/gau-Fragment_frag001a.jp2/full/full/0/default/jpg 画像のURLを入力して、アップロードボタンを押します。アップロードされると、以下のような画面に変わります。 次に、「OCR実行」ボタンを押します。正しく完了すると、以下のように表示されます。 次に「TEI/XML作成」ボタンを押します。正しく完了すると、以下のようにTEI/XMLとともに表示されます。 Oxygen XML Editorでダウンロードしたファイルを表示した例です。Google Cloud Vision APIによるOCR結果を確認することができます。 GakuNin RDMのファイル 上記のプロセスで作成された各種ファイルは、GakuNin RDMのフォルダにファイルとして保存されます。 参考: URLを介してアクセス可能な画像ファイルを用意する mdx.jpのオブジェクトストレージを利用して、URLを介してアクセス可能な画像ファイルを用意する。 今回はge-editorというバケットを作成し、以下のようなファイルを用意します。 { "Version": "2008-10-17", "Statement": [ { "Sid": "ge-editor", "Effect": "Allow", "Principal": { "DDN": ["*"] }, "Action": ["s3:ListBucket", "s3:GetObject"], "Resource": "ge-editor" } ] } そして、以下を実行することで、上記のバケットにアップロードされたファイルをダウンロード可能にします。 ...

「れきちず x Next.js」サイトにルートの登録機能を追加しました。

「れきちず x Next.js」サイトにルートの登録機能を追加しました。

概要 「れきちず x Next.js」サイトにルートの登録機能を追加しました。以下が表示例です。 参考 「れきちず x Next.js」サイトについては、以下で紹介しています。 この「れきちず」を使ってルートを表示する先行事例として、以下が挙げられます。 https://codh.rois.ac.jp/edomi/route/ 今回は上記の事例を参考に、ルートを作成する機能を追加しました。 使い方 サイトにアクセスします。 https://rekichizu-next.vercel.app/ja/ 「マイルートを管理」をクリックします。 ログインが求められますので、画面右上のボタンからログインします。 「新しいルートを作成」から、ルートを作成します。 以下が編集画面です。 編集アイコンをクリックすると、ルートのタイトルおよび説明を編集できます。 モードを「追加」に設定すると、地図上でクリックした箇所にピンが立ちます。ドラッグ&ドロップで移動させることも可能です。 マーカーはドラッグ&ドロップで順序変更することができます。 インポートとエクスポート エクスポート ルートの一覧画面と編集画面にダウンロードボタンを設置しています。以下のようなJSONファイルがダウンロードされます。 { "type": "FeatureCollection", "features": [ { "type": "Feature", "properties": { "id": "1744639551563", "text": "湯島天神", "where": "神社", "type": "point" }, "geometry": { "type": "Point", "coordinates": [ 139.76809921470056, 35.707702013817155 ] } }, { "type": "Feature", "properties": { "id": "1744639379903", "text": "不忍池", "where": "池", "type": "point" }, "geometry": { "type": "Point", "coordinates": [ 139.77050681034103, 35.71210499496915 ] } }, { "type": "Feature", "properties": { "id": "1744639551563-1744639379903", "path": "[1744639551563] -> [1744639379903]", "type": "line" }, "geometry": { "type": "LineString", "coordinates": [ [ 139.76809921470056, 35.707702013817155 ], [ 139.77050681034103, 35.71210499496915 ] ] } } ] } これをgeojson.ioに貼り付けると、以下のように表示されます。 ...

れきちずをNext.jsで使用する

れきちずをNext.jsで使用する

概要 れきちずをNext.jsで使用する方法を調べてみましたので、備忘録です。 背景 以下の記事で、「れきちず」の使い方を紹介しました。 そして、2025年4月4日に「全国版が公開」されたことを知りました。 https://rekichizu.jp/ そこでNext.jsを用いて作成したアプリケーションへの導入にあたり、その使い方を調べてみました。 デモアプリ 以下のようなアプリケーションを試作しました。 https://rekichizu-next.vercel.app/ja/ 使用方法の調査にあたり、公式サイトで提供されている地図の切り替えや重ね合わせ機能、および検索機能などを再現することを目的としました。この実装にあたり、以下のReactライブラリを使用しました。 https://visgl.github.io/react-maplibre/ 開発メモ 検索機能 検索機能には、GeoLODのAPIを利用させていただきました。なお、「れきちず」の公式サイトでは、専用の検索APIが用いられているようでした。 https://geolod.ex.nii.ac.jp/doc/api/ react-maplibre 本ライブラリを使用して、やりたいことの多くを実現できました。一方、TerrainControlではTerrainのON/OFFと合わせてピッチを変更することが難しい?、useMapではaddLayer/removeLayerが難しい?など、いくつか苦労した点もありました。 まとめ 「れきちず」およびNext.jsを用いたアプリケーション開発にあたり、参考になりましたら幸いです。 「れきちず」の開発に関わる方々に深く感謝いたします。

IIIF画像に対して、多角形のアノテーションを付与するツールを作成しました。

IIIF画像に対して、多角形のアノテーションを付与するツールを作成しました。

概要 IIIF画像に対して、多角形のアノテーションを付与するツールを作成しました。 https://next-fb-anno.vercel.app/ 本記事では、このツールについて説明します。 使い方 以下がトップ画面です。IIIFマニフェストファイルのURLを入力します。「入力例を使用」からもお試しいただけます。『百鬼夜行図』(東京大学総合図書館所蔵)を使用しています。 以下のようなアノテーション登録画面が表示されます。 画面右上のログインボタンからログインできます。 アノテーション付与の方法は、以下の動画を参考にしてください。 https://youtu.be/9RMqaXTaOzE 開発した背景 以下の記事で説明したように、Mirador 3の mirador-annotations プラグイン向けに、Firestore用のアダプタを開発しました。 このmirador-annotations プラグインについて、多角形のアノテーション付与を行いづらいという意見がありました。 そこで、主に多角形のアノテーション付与を支援するために、本ツールを開発しました。また、アノテーション付与を実装するためのライブラリであるAnnotoriousについて、Reactライブラリが公開されていたので、この調査も兼ねて実装しました。 https://annotorious.dev/react/openseadragon-iiif/ さらに、上記の記事で紹介したmirador-annotations プラグインのFirestore用のアダプタを流用することで、同じFirebaseのサービス(AuthenticationとFirestore)を使用するようにしました。 そのため、本ツールの右上のボタンに、Miradorへのリンクを付与しました。 これにより、本ツールで編集を行い、IIIFマニフェストファイルのメタデータを含む、情報の表示にはMiradorを使用する、といった使い方が可能になるかと思います。 多角形のアノテーションを付与するための既存ツール IIIF画像に対してアノテーションを付与する機能を持つ既存ツールは数多く存在します。ここでは、IIIF画像に対して多角形のアノテーションを付与する機能を有するツールと、本ツールとの差分を紹介します。 ここでは、国立国会図書館で公開されている「和泉国絵図」を例とします。 Omeka Classic + IIIF Toolkit 以下の記事でセットアップ方法や使い方を紹介しています。 https://zenn.dev/nakamura196/books/2a0aa162dcd0eb IIIF Toolkitでは、Mirador 2が使用されており、ポリゴンアノテーションが提供されています。 今回にニーズに対しては、多角形アノテーションではなくポリゴンアノテーションである点と、Omeka Classicのセットアップ(サーバの準備や維持)が必要になる点が課題として挙げられます。 Recogito Recogitoでは傾斜したボックス形式のアノテーションを付与することはできましたが、多角形のアノテーション付与はできないようでした。 また、以下のように、pctを用いてIIIF画像にアクセスするようで、画像が表示できないケースが多くありました。 Glycerine: Image Annotation Workbench 本ツールが最も今回のニーズに合致していました。 https://glycerine.io/ おそらく本ツールと同じ「Annotorious(のver.2)」が使用されており、多角形によるアノテーションのほか、複数人による共同作業も可能でした。 唯一の課題として、登録したアノテーションの一括登録機能が提供されていませんでした。この点に対して、今回開発したツールでは、読み込んだIIIFマニフェストファイルに対して、ログインユーザが登録したユーザが付与したアノテーションを一括エクスポートする機能を設けました。 これにより、本ツールで付与したアノテーションを一括エクスポートし、他の可視化ツールで使用する、といった使い方が容易になります。 なお、以下の記事で紹介したように、Mirador 3の mirador-annotations プラグインにも付与したアノテーションをダウンロードする機能が提供されています。しかい、この機能はCanvasごとにダウンロードする仕様となっており、複数ページから構成される場合には、ページごとにダウンロードする必要がありました。 工夫点および開発メモ 本ツールの開発にあたり、工夫した点などを紹介します。 入力するIIIFマニフェストのv2およびv3対応 入力するIIIFマニフェストファイルはv2とv3、どちらでも対応できるようにしました。この実現にあたり、以下の記事で紹介した@iiif/parserを使用しました。 ...

Next.js 15 App Router で Tailwind CSS V4 を使用してダークモードを追加する方法

Next.js 15 App Router で Tailwind CSS V4 を使用してダークモードを追加する方法

概要 Next.js 15 App Router で Tailwind CSS V4 を使用してダークモードを追加する方法です。 以下の記事が参考になりました。 https://sujalvanjare.vercel.app/blog/dark-mode-nextjs15-tailwind-v4 以下のように、ダークモードとライトモードを切り替えることができました。 上記の記事について、ChatGPTによる日本語記事です。 Next.js 15 App RouterプロジェクトでTailwind CSS V4を使用してダークモードとライトモードを実装する方法を学びましょう。このステップバイステップガイドでは、シームレスなテーマスイッチャーを実現するためのTailwind V4とNext.js 15の最新の変更点をカバーしています! 既にNext.jsプロジェクトをお持ちの場合は、ステップ1に進んでください。 ステップ1:新しいNext.jsプロジェクトを作成する App RouterとTailwind CSS v4を搭載したNext.js 15をインストールするには、次の手順に従ってください: 任意の場所にプロジェクトフォルダを作成します。 VS Codeでフォルダを開きます。 ターミナルを開き、次のコマンドを実行します: npmユーザーの場合: npx create-next-app@latest . pnpmユーザーの場合: pnpx create-next-app@latest . インストール中に、次のオプションが表示されます: What is your project named? my-app Would you like to use TypeScript? No / Yes Would you like to use ESLint? No / Yes Would you like to use Tailwind CSS? No / Yes Would you like your code inside a `src/` directory? No / Yes Would you like to use App Router? (recommended) No / Yes Would you like to use Turbopack for `next dev`? No / Yes Would you like to customize the import alias (`@/*` by default)? No / Yes What import alias would you like configured? @/* 矢印キーでオプションを選択し、Enterキーを押します。Tailwind CSSとApp Routerを必ず有効にしてください。 ...

Error: Do not use <img>. Use Image from 'next/image' instead.への対応

Error: Do not use <img>. Use Image from 'next/image' instead.への対応

概要 Error: Do not use . Use Image from ’next/image’ instead.への対応にあたり、以下の記事が参考になりました。 https://stackoverflow.com/questions/68203582/error-do-not-use-img-use-image-from-next-image-instead-next-js-using-ht 上記の記事に基づき、ChatGPTによる回答を以下に掲載します。間違っている点もあるかもしれませんが、参考になりましたら幸いです。 Next.jsのESLintルール変更とフラットコンフィグの設定方法 Next.js 11以降、ESLintの設定がデフォルトで提供されるようになり、@next/next/no-img-element ルールが追加されました。このルールにより、通常の<img>タグの使用が制限され、Next.jsのnext/imageコンポーネントを推奨するようになっています。 しかし、プロジェクトによっては<img>タグを使いたい場合もあるでしょう。そのため、このESLintルールを無効化する方法を紹介します。 1. 従来の設定方法(.eslintrc.js) 以前のNext.jsのESLint設定は、.eslintrc.js に次のように記述することで変更できました。 module.exports = { rules: { "@next/next/no-img-element": "off", }, }; しかし、Next.jsのESLintは「フラットコンフィグ(Flat Configuration)」へ移行しました。そのため、.eslintrc.js ではなく、eslint.config.mjs を使用する必要があります。 2. フラットコンフィグの設定方法(eslint.config.mjs) 現在のNext.jsプロジェクトでESLintのルールを変更するには、eslint.config.mjs ファイルを作成し、以下のように記述します。 import { dirname } from "path"; import { fileURLToPath } from "url"; import { FlatCompat } from "@eslint/eslintrc"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const compat = new FlatCompat({ baseDirectory: __dirname, }); const eslintConfig = [ ...compat.extends("next/core-web-vitals", "next/typescript"), { rules: { "@next/next/no-img-element": "off", }, }, ]; export default eslintConfig; この設定を適用することで、Next.jsのプロジェクトでも通常の<img>タグを使用できるようになります。 3. どの方法を使うべきか? Next.js 11以前のプロジェクト → .eslintrc.js を使用 Next.js 12以降のプロジェクト → eslint.config.mjs を使用 最新のNext.js環境ではフラットコンフィグを利用するのが推奨されているため、新規プロジェクトではeslint.config.mjs を設定するのがベストです。 ...

CETEIceanとXPathを使って特定の要素にスクロールする

CETEIceanとXPathを使って特定の要素にスクロールする

概要 CETEIceanとXPathを使って特定の要素にスクロールする方法を調べたので備忘録です。 デモ 以下のURLからお試しいただけます。 https://next-ceteicean-router.vercel.app/xpath/ ページにアクセス後、スクロールし、以下のように表示されます。 XPathの取得 上記では、以下の「校異源氏物語テキストDB」のXMLファイルを対象にしています。 https://kouigenjimonogatari.github.io/tei/01.xml そして、以下のXPathを指定しています。 /TEI/text[1]/body[1]/p[1]/seg[267] このXPathの取得にあたっては、Oxygen XML Editorを用いて、対象要素を右クリックして、「Copy XPath」から取得することができました。 スクロールの実装 以下で紹介したアプリをベースにします。 GitHub上のソースコードは以下です。 https://github.com/nakamura196/next-ceteicean-router/blob/main/src/components/xpath/Render.tsx 特に以下の部分で、XPathをCETEIceanによって作成される要素名に変換し、scrollIntoViewによってスクロールしています。 // fetchDataの修正 React.useEffect(() => { const rawXpath = "/TEI/text[1]/body[1]/p[1]/seg[267]"; const xpath = rawXpath .replace(/^\//, "") // 先頭のスラッシュを削除 .replace(/([A-Za-z]+)(?=\/|\[|$)/g, "tei-$1") // tei-プレフィックスを追加 .toLowerCase(); if (teiContentRef.current) { const result = document.evaluate( xpath, teiContentRef.current, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null ); const targetElement = result.singleNodeValue as HTMLElement; if (targetElement) { targetElement.scrollIntoView({ behavior: "smooth", block: "center", inline: "center", }); targetElement.style.backgroundColor = "yellow"; } } }, [teiDoc]); まとめ 他にも良い方法があるかもしれませんが、参考になりましたら幸いです。 ...

Next.jsでUniversal Viewerのnpmパッケージを使用する

Next.jsでUniversal Viewerのnpmパッケージを使用する

概要 Next.jsでUniversal Viewerのnpmパッケージを使用する方法の備忘録です。 インストール 以下でインストールします。 npm i universalviewer 実装 useEffectを使うため、クライアントコンポーネントとして実装する必要があるようでした。 またdivタグにuvクラスを与えることで、cssが当たるようになりました。 'use client' import { useEffect } from 'react' import dynamic from 'next/dynamic' interface ViewerProps { manifest: string cv?: number xywh?: string } // コンポーネントの実装 function ViewerComponent({ manifest, cv, xywh }: ViewerProps) { useEffect(() => { // universalviewerをインポートして初期化 const { init } = require('universalviewer') require('universalviewer/dist/esm/index.css') init('uv', { manifest, canvasIndex: cv, xywh }) }, [manifest, cv, xywh]) return ( <div id="uv" className="uv" style={{ width: '100%', height: '60vh' }}></div> ) } // SSRを無効化し、クライアントサイドでのみレンダリングするコンポーネント const Viewer = dynamic(() => Promise.resolve(ViewerComponent), { ssr: false, loading: () => ( <div style={{ width: '100%', height: '60vh', display: 'flex', justifyContent: 'center', alignItems: 'center', background: '#f0f0f0', }} > ビューワーを読み込み中... </div> ), }) export default Viewer 他にも使用可能なオプションがあるかと思いますが、cvで処理ロードするcanvasのインデックス、xywhで表示する矩形を指定することができました。 まとめ Universal Viewerの利用にあたり、参考になりましたら幸いです。

GakuNin RDMのAPIを用いて、連携したストレージのファイルを検索する

GakuNin RDMのAPIを用いて、連携したストレージのファイルを検索する

概要 以下の記事で、GakuNin RDMのAPIを用いたアプリケーション構築について紹介しました。 本記事では、GakuNin RDMのAPIを用いて、連携したストレージのファイルを検索する方法を紹介します。 実装例 次のような形で、検索APIを実装しました。なお、https://rdm.nii.ac.jp/api/v1/search/file/にクライアントから直接アクセスした際には、CORSによるエラーが発生したため、Next.jsのAPI Routesとして実装しています。 import { NextResponse } from "next/server"; import { authOptions } from "@/app/api/auth/[...nextauth]/authOptions"; import { getServerSession } from "next-auth"; export async function GET(req: Request) { const session = await getServerSession(authOptions); // URLからクエリパラメータを取得 const url = new URL(req.url); const query = url.searchParams.get("filter[fulltext]") || ""; const offset = parseInt(url.searchParams.get("page[offset]") || "0", 10); const size = parseInt(url.searchParams.get("page[limit]") || "20", 10); const accessToken = session?.accessToken; const apiUrl = "https://rdm.nii.ac.jp/api/v1/search/file/"; const params = { api_version: { vendor: "grdm", version: 2 }, sort: "created_desc", highlight: "title:30,name:30,user:30,text:124,comments.*:124", elasticsearch_dsl: { query: { filtered: { query: { query_string: { default_field: "_all", fields: [ "_all", "title^4", "description^1.2", "job^1", "school^1", "all_jobs^0.125", "all_schools^0.125", ], query, analyze_wildcard: true, lenient: true, }, }, }, }, from: offset, size, }, }; const res = await fetch(apiUrl, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${accessToken}`, }, body: JSON.stringify(params), }); const data = await res.json(); return NextResponse.json(data); } 利用例 以下のURLからお試しいただけます。(利用にあたっては、GakuNin RDMへのログインが必要です。) ...

LEAF WriterとGakuNin RDMを用いたTEI/XMLファイルの編集環境の試作

LEAF WriterとGakuNin RDMを用いたTEI/XMLファイルの編集環境の試作

概要 LEAF WriterとGakuNin RDMを用いたTEI/XMLファイルの編集環境の試作を行いましたので、備忘録です。 参考 以下の記事で、LEAF WriterをNext.jsから使用する方法を紹介しました。 特に、以下のnpmパッケージを使用しています。 https://www.npmjs.com/package/@cwrc/leafwriter 上記で編集対象とするTEI/XMLファイルの入出力にあたり、GakuNin RDMを使用してみます。GakuNin RDMのAPIをJavaScriptから使用する方法について、以下も参考になりましたら幸いです。 使い方 以下がプロトタイプシステムのURLです。(色々と不具合が含まれる点にご注意ください。) https://rdm-leaf-editor.vercel.app/ UIはClaude 3.7 Sonnetに作成してもらっています。 「サインイン」ボタンを押すと、認証画面に進むので、ログインします。 ログイン後、リダイレクトされ、プロジェクトの一覧が表示されます。 TEI/XMLファイルが含まれるディレクトリまで移動します。ファイル名に「.xml」が含まれる場合、「Leaf Writer」の列に「編集」ボタンが表示されます。 LEAF Writerの編集画面に遷移するので、テキストを編集します。作業が完了したら、画面右上の「保存」ボタンを押します。 GakuNin RDMのUIから確認してみると、バージョンごとに保存されていることが確認できます。 実装 GakuNin RDMからのファイルの取得および更新は以下で行っています。 /** * ファイルの内容を取得する */ export async function fetchFileContent( url: string, accessToken: string ): Promise<string> { const response = await fetch(url, { method: "GET", headers: { Authorization: `Bearer ${accessToken}`, }, }); if (!response.ok) { throw new Error( `ファイルの取得に失敗しました。ステータスコード: ${response.status}` ); } return await response.text(); } /** * ファイルの内容を更新する */ export async function updateFileContent( url: string, content: string, accessToken: string, contentType: string = "application/xml" ): Promise<void> { const blob = new Blob([content], { type: contentType }); const response = await fetch(url, { method: "PUT", headers: { Authorization: `Bearer ${accessToken}`, }, body: blob, }); if (!response.ok) { const errorText = await response.text(); console.error("保存に失敗しました。ステータスコード:", response.status); console.error("レスポンス:", errorText); throw new Error(`保存に失敗しました。ステータスコード: ${response.status}`); } } 上記で使用するURLは以下です。 ...

Next.js + CETEIcean + React TEI Routerを使ったビューア開発

Next.js + CETEIcean + React TEI Routerを使ったビューア開発

概要 Next.js、CETEIcean、React TEI Routerを組み合わせたTEI/XMLビューアの開発についての備忘録です。 背景 CETEIceanは、TEI/XML を HTML5 に変換する JavaScript ライブラリです。 https://github.com/TEIC/CETEIcean そして、React TEI Routerは、CETEIcean をベースに React コンポーネントで TEI/XML を構造化して表示できるライブラリです。以下のように説明されています。 https://github.com/pfefferniels/react-teirouter TEI for React using CETEIcean and routes これらを組み合わせることで、Next.js において TEI/XML をカスタマイズして表示できるビューア を作成しました。 リポジトリ 以下がサンプルリポジトリです。 https://github.com/nakamura196/next-ceteicean-router 実際に動作するデモも用意しています。 https://next-ceteicean-router.vercel.app/ 実装 Next.js のページコンポーネント (page.tsx) CETEIcean を利用して XML を変換し、カスタムコンポーネントで描画します。 import React from "react"; import Render from "@/components/tei"; export default function App() { const xmlContent = `<?xml version="1.0" encoding="UTF-8"?> <TEI xmlns="http://www.tei-c.org/ns/1.0"> <text> <body> <div type="original"> 私の名前は<persName corresp="#id1">田中太郎</persName>です。 </div> <div> <p style="color: green;">こんにちは</p> <p style="color: green;">こんばんは <seg style="color: blue;">xxx</seg> </p> </div> </body> </text> </TEI>`; return <Render xmlContent={xmlContent} />; } TEIレンダリングコンポーネント CETEIcean を使って XML を HTML5 に変換。 TEIRender + TEIRoute を使い、TEI 要素ごとにカスタムコンポーネントを適用。 import { TEIRender, TEIRoute } from "react-teirouter";を使用した上で、要素毎にコンポーネントを用意しています。 ...

Next.js for Drupal の BASE_PATH 問題と修正方法(patch-package活用)

Next.js for Drupal の BASE_PATH 問題と修正方法(patch-package活用)

概要 Next.js for Drupalのv2.0.0が2025/2/11にリリースされました。 https://next-drupal.org/ https://next-drupal.org/blog/next-drupal-2-0 早速試してみたところ、BASE_PATHの取り扱いについて対応が必要だったので、備忘録です。 環境変数 環境変数のサンプルは以下のようになっています。 # See https://next-drupal.org/docs/environment-variables # Required NEXT_PUBLIC_DRUPAL_BASE_URL=https://site.example.com NEXT_IMAGE_DOMAIN=site.example.com # Authentication DRUPAL_CLIENT_ID=Retrieve this from /admin/config/services/consumer DRUPAL_CLIENT_SECRET=Retrieve this from /admin/config/services/consumer # Required for On-demand Revalidation DRUPAL_REVALIDATE_SECRET=Retrieve this from /admin/config/services/next この時、NEXT_PUBLIC_DRUPAL_BASE_URLにhttps://site.example.com/xxxのようなベースパスを含めた形で指定すると、APIのリクエストはhttps://site.example.com/jsonapi/などに送られ、リソースを正しく取得できませんでした。 原因 エラーが発生しているgetResourceCollectionを確認したところ、問い合わせ先のURLを作成するbuildUrl関数において、new URL(path, this.baseUrl);が使用されていました。 ... buildUrl(path, searchParams) { const url = new URL(path, this.baseUrl); const search = ( // Handle DrupalJsonApiParams objects. searchParams && typeof searchParams === "object" && "getQueryObject" in searchParams ? searchParams.getQueryObject() : searchParams ); if (search) { url.search = stringify(search); } return url; } ... async buildEndpoint({ locale = "", path = "", searchParams } = {}) { const localeSegment = locale ? `/${locale}` : ""; if (path && !path.startsWith("/")) { path = `/${path}`; } return this.buildUrl( `${localeSegment}${this.apiPrefix}${path}`, searchParams ).toString(); } ... async getResourceCollection(type, options) { options = { withAuth: this.withAuth, deserialize: true, ...options }; const endpoint = await this.buildEndpoint({ locale: options?.locale !== options?.defaultLocale ? options.locale : void 0, resourceType: type, searchParams: options?.params }); this.debug(`Fetching resource collection of type ${type}.`); const response = await this.fetch(endpoint, { withAuth: options.withAuth, next: options.next, cache: options.cache }); await this.throwIfJsonErrors( response, "Error while fetching resource collection: " ); const json = await response.json(); return options.deserialize ? this.deserialize(json) : json; } ChatGPTに質問したところ、以下の回答が得られました。 ...

Next.jsで多言語対応の静的サイトを構築する

Next.jsで多言語対応の静的サイトを構築する

はじめに この記事は、GPT-4oによって生成された内容です。Next.jsを使用して多言語対応の静的サイトを構築する方法について説明します。特に、メイン言語にはURLプレフィックスを付けず、その他の言語にはプレフィックスを付ける設定に焦点を当てます。GitHub Pagesを使用してデプロイする設定も含まれています。 プロジェクトのセットアップ まず、Next.jsのプロジェクトを作成します。create-next-appを使用してプロジェクトを初期化します。 npx create-next-app@latest next-intl-ssg 必要なパッケージのインストール 多言語対応のために、next-intlをインストールします。 npm install next-intl プロジェクト構成 プロジェクトのディレクトリ構成は以下の通りです。 src/app/[locale]/about/page.tsx src/app/about/page.tsx src/lib/i18n.ts src/components/I18nProvider.tsx src/i18n/ja.json src/i18n/en.json 多言語対応の実装 next-intlを使用して、言語ごとのメッセージを管理します。src/lib/i18n.tsでメッセージを取得する関数を定義しています。 export async function getMessages(locale: string) { return (await import(`@/i18n/${locale}.json`)).default; } I18nProviderコンポーネントを使用して、各ページでメッセージを提供します。 import { ReactNode } from 'react'; import { NextIntlClientProvider } from 'next-intl'; export default function I18nProvider({ children, locale, messages }: { children: ReactNode; locale: string; messages: Record<string, Record<string, string>>; }) { return ( <NextIntlClientProvider locale={locale} messages={messages}> {children} </NextIntlClientProvider> ); } SSGの設定 generateStaticParams関数を使用して、静的ページを生成する際のパラメータを設定します。メイン言語(日本語)にはプレフィックスを付けず、その他の言語にはプレフィックスを付けます。 // src/app/[locale]/about/page.tsx export function generateStaticParams() { return locales.filter(locale => locale !== 'ja').map(locale => ({ locale })); } GitHub Pagesへのデプロイ GitHub Actionsを使用して、GitHub Pagesにデプロイします。.github/workflows/deploy.ymlでデプロイの設定を行います。 name: Deploy to GitHub Pages on: push: branches: [main] workflow_dispatch: permissions: contents: read pages: write id-token: write concurrency: group: "pages" cancel-in-progress: false jobs: build: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Node uses: actions/setup-node@v4 with: node-version: "20" cache: "npm" - name: Install dependencies run: npm ci - name: Build run: npm run build - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: path: ./out deploy: environment: github-pages runs-on: ubuntu-latest needs: build steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 リンク GitHubリポジトリ: next-intl-ssg GitHub Pages: next-intl-ssg まとめ このブログでは、Next.jsを使用して多言語対応の静的サイトを構築し、メイン言語にはプレフィックスを付けず、その他の言語にはプレフィックスを付ける方法を紹介しました。next-intlを活用することで、簡単に多言語対応が可能になります。ぜひ試してみてください。 ...

clover-iiifをNext.jsで使用する

clover-iiifをNext.jsで使用する

概要 clover-iiifをNext.jsで使用するサンプルリポジトリを作成したので、備忘録です。 https://clover-iiif-demo.vercel.app/ 背景 clover-iiifは以下のように説明されています。 https://github.com/samvera-labs/clover-iiif Extensible IIIF front-end toolkit and Manifest viewer. Accessible. Composable. Open Source. (日本語訳)拡張可能な IIIF フロントエンドツールキットとマニフェストビューア。 これをNext.jsで使用します。 データ 「校異源氏物語(国立国会図書館所蔵)」をサンプルデータとして使用します。 https://dl.ndl.go.jp/pid/3437686 リポジトリ 以下で公開しています。 https://github.com/nakamura196/clover-iiif-demo 以下を参考にしました。 https://samvera-labs.github.io/clover-iiif/docs/composing クライアントサイドでの実行にあたり、以下のような工夫が必要でした。 "use client"; import React, { Suspense } from "react"; import dynamic from "next/dynamic"; import { useSearchParams } from "next/navigation"; // Viewerコンポーネントを動的にインポート(SSRを無効化) const Viewer = dynamic( () => import("@samvera/clover-iiif/viewer"), { ssr: false } ); const WorkContent = () => { const searchParams = useSearchParams(); const manifestId = searchParams.get('manifest') || "https://dl.ndl.go.jp/api/iiif/3437686/manifest.json"; return ( <article> <Viewer iiifContent={manifestId} /> </article> ); }; const Work = () => { return ( <Suspense fallback={<div>Loading...</div>}> <WorkContent /> </Suspense> ); }; export default Work; まとめ 不完全な点もあるかと思いますが、参考になりましたら幸いです。

Next 15(React 19 を使用)で、@react-three/fiberとdreiを使う

Next 15(React 19 を使用)で、@react-three/fiberとdreiを使う

概要 Next 15(React 19 を使用)で、@react-three/fiberを使う際、以下のように説明されています。 R3F v8 is not compatible with React 19 or Next 15, which uses React 19. Use the R3F v9 RC instead which can be installed with @react-three/fiber@rc. (日本語訳)R3F v8 は React 19 や Next 15(React 19 を使用)と互換性がありません。代わりに R3F v9 RC を使用してください。インストールするには @react-three/fiber@rc を使用してください。 一方、マウス操作を行うために以下を追加しましたが、@react-three/fiber@rcとは相性が悪いようでした。 https://www.npmjs.com/package/@react-three/drei この問題に対する対処法の備忘録です。 方法 本記事執筆時点(2025-02-06)の情報です。 まず、@react-three/fiber@rcではなく、@react-three/fiber@alphaをインストールする必要がありました。(@betaでもよいかもしれません。) そして、dreiについては、@react-three/drei@10.0.0-rc.1をインストールしました。結果、以下のような組み合わせでは、無事にインストールすることができました。 "@react-three/drei": "^10.0.0-rc.1", "@react-three/fiber": "^9.0.0-alpha.8", まとめ 考慮が不十分な点があるかもしれませんが、参考になりましたら幸いです。

NextAuth.jsを使ってDrupalのOAuthを利用する

NextAuth.jsを使ってDrupalのOAuthを利用する

概要 NextAuth.jsを使ってDrupalのOAuthを利用する方法に関する備忘録です。 挙動 Next.jsで作成したアプリにアクセスして、「Sign in」ボタンを押します。 Drupalにログインしていない場合には、ログイン画面に遷移します。 ログイン済みの場合、「許可」するかのボタンが表示されるので、許可します。 ログイン情報が表示されます。 Drupal側の準備 モジュールのインストール 以下のモジュールをインストールします。 https://www.drupal.org/project/simple_oauth 本記事執筆時点の最新の以下をインストールしました。 composer require 'drupal/simple_oauth:^6.0@beta' トークンを暗号化するための鍵の生成 鍵のペアを生成し、セキュリティのためにドキュメントルートの外に保存します。 openssl genrsa -out private.key 2048 openssl rsa -in private.key -pubout > public.key 鍵のパスを設定 鍵のパスを管理画面で設定します: /admin/config/people/simple_oauth Amazon LightsailでDrupalを動作させている場合、以下のように、ユーザを変更する必要がありました。 sudo chown daemon:daemon private.key Clients /admin/config/services/consumerにアクセスします。 default_consumerがすでに作成されているため、こちらを編集します。 New Secretに値を入力します。 Grant typesでAuthorization Codeを選択します。 Redirect URIsに以下を入力します。 http://localhost:3000/api/auth/callback/drupal https://oauth.pstmn.io/v1/callback (Postmanでのチェック用) Access token expiration timeに値を入力します。 Postmanでの動作確認 以下のように入力します。Drupalがhttps://drupal.example.orgにセットアップされていると過程します。 認可 URL: https://drupal.example.org/oauth/authorize アクセストークン URL: https://drupal.example.org/oauth/token クライアント ID: Drupalで設定した値 クライアントシークレット: Drupalで設定した値 ...

Azure OpenAI Assistants APIを用いたアプリをGradioとNext.jsで作成する

Azure OpenAI Assistants APIを用いたアプリをGradioとNext.jsで作成する

概要 Azure OpenAI Assistants APIを用いたアプリをGradioとNext.jsで作成したので、備忘録です。 対象データ Zennで公開している記事を対象にしました。まず以下により、一括ダウンロードしました。 import requests from bs4 import BeautifulSoup import os from tqdm import tqdm page = 1 urls = [] while 1: url = f"https://zenn.dev/api/articles?username=nakamura196&page={page}" response = requests.get(url) data = response.json() articles = data['articles'] if len(articles) == 0: break for article in articles: urls.append("https://zenn.dev" + article['path']) page += 1 for url in tqdm(urls): text_opath = f"data/text/{url.split('/')[-1]}.txt" if os.path.exists(text_opath): continue response = requests.get(url) soup = BeautifulSoup(response.text, "html.parser") html = soup.find(class_="znc") txt = html.get_text() os.makedirs(os.path.dirname(text_opath), exist_ok=True) with open(text_opath, "w") as f: f.write(txt) ベクトルストアへの登録 以下のようなコードにより、データファイルをアップロードします。 import os from dotenv import load_dotenv from openai import AzureOpenAI from glob import glob from tqdm import tqdm load_dotenv(override=True) client = AzureOpenAI( azure_endpoint = os.getenv("AZURE_OPENAI_ENDPOINT_ZENN"), api_key=os.getenv("AZURE_OPENAI_API_KEY_ZENN"), api_version="2024-05-01-preview" ) # ベクトルストアの作成または取得 is_create_vector_store = True vector_store_name = "Vector Store" if is_create_vector_store: vector_store = client.beta.vector_stores.create(name=vector_store_name) # Create a vector store caled "Financial Statements" vector_stores = client.beta.vector_stores.list() for vector_store in vector_stores: if vector_store.name == vector_store_name: vector_store_id = vector_store.id break # 登録済みデータファイルの取得 response = client.files.list(purpose="assistants") items = response.data filenames = [] for item in items: filename = item.filename filenames.append(filename) filenames.sort() # アップロード ## 定数設定 BATCH_SIZE = 100 vector_store_id = "vs_UELnIBkcROD3o4XKX2CcpVjo" ## ファイル一覧取得とソート files = glob("./data/text/*.txt") files.sort() ## アップロード済みファイルを確認済みと仮定 file_streams = [] for file in tqdm(files): filename = os.path.basename(file) if filename in filenames: # アップロード済みのファイルをスキップ continue ## ファイルをストリームとして開く file_streams.append(open(file, "rb")) ## バッチサイズに達したらアップロード処理 if len(file_streams) == BATCH_SIZE: try: client.beta.vector_stores.file_batches.upload_and_poll( vector_store_id=vector_store_id, files=file_streams ) except Exception as e: print(f"Error processing batch: {e}") finally: file_streams = [] # ストリームリセット ## 残りのファイルを処理 if file_streams: try: client.beta.vector_stores.file_batches.upload_and_poll( vector_store_id=vector_store_id, files=file_streams ) except Exception as e: print(f"Error processing remaining files: {e}") アシスタント プレイグラウンド 「アシスタント プレイグラウンド」を用いて、挙動を確認します。 ...