ホーム 記事一覧 ブック DH週間トピックス 検索 このサイトについて
English
Omeka Sのモジュールアップデート情報(2025-03-27)

Omeka Sのモジュールアップデート情報(2025-03-27)

概要 Omeka Sの運用において、モジュールのアップデートが必要になったものを紹介します。 IIIF Server https://omeka.org/s/modules/IiifServer/ 2024年2月にリリースされた3.6.18を使用していましたが、IIIFマニフェストファイル生成時に、サムネイル画像がおかしくなる不具合が確認されました。 2025年3月時点で最新の3.6.24に更新したところ、本不具合が解消しました。なお、このアップデートには、Commonモジュールの更新も必要でしたので、参考になりましたら幸いです。 https://omeka.org/s/modules/Common/ Google Analytics https://github.com/Libnamic/Omeka-S-GoogleAnalytics 2023年頃から使用していましたが、PHPのあるバージョンからwarningが表示されるようになりました。 また本モジュールの更新も、2023年から行われていませんでした。 そこで以下の記事でも紹介したように、Analytics Snippetモジュールを使用したほうがよさそうでした。 以下で更新履歴が確認できますが、こちらは最新のリリースが2025/1になっています。 https://omeka.org/s/modules/AnalyticsSnippet/ まとめ Omeka Sの運用にあたり、参考になりましたら幸いです。

Mirador 4プラグイン開発:任意の角度で画像を回転するプラグインで、角度の初期値を設定できるようにしました。

Mirador 4プラグイン開発:任意の角度で画像を回転するプラグインで、角度の初期値を設定できるようにしました。

概要 任意の角度で画像を回転するMirador 4プラグインで、角度の初期値を設定できるようにしました。 リポジトリは以下です。 https://github.com/nakamura196/mirador-rotation-plugin デモページは以下です。角度および矩形を初期設定とともに、画像を回転させることができます。 https://nakamura196.github.io/mirador-rotation-plugin/ 背景 以下の記事で、本プラグインについて説明しています。 一方、課題として、角度の初期値を与えることができませんでした。 これに対して、以下の記事で紹介したように、Mirador 4の標準機能として、角度の初期値を与えることができるようでした。 合わせて、以下の「mirador-image-tools」プラグインについて、webpackからViteに変更されていたので、この変更を「mirador-rotation-plugin」にも反映することにしました。 https://github.com/ProjectMirador/mirador-image-tools GitHub Pagesでの公開 GitHub Pagesでの公開にあたり、「mirador-image-tools」のvite.config.jsを以下のように変更しています。これで、npm run build:demoにより、GitHub Pagesで公開するためのディレクトリを作成することができるようになりました。 https://github.com/nakamura196/mirador-rotation-plugin/blob/main/vite.config.js import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import fs from 'fs/promises'; import path from 'node:path'; import { fileURLToPath } from 'url'; import { globSync } from 'glob'; import pkg from './package.json'; /** * Vite configuration */ export default defineConfig({ base: process.env.GITHUB_PAGES ? (process.env.BASE_PATH || '/mirador-rotation-plugin/') : '/', ...( process.env.GITHUB_PAGES ? { build: { outDir: 'dist', emptyOutDir: true, rollupOptions: { external: ['__tests__/*', '__mocks__/*'], input: fileURLToPath(new URL('./demo/src/index.html', import.meta.url)), }, sourcemap: true, }, } : { build: { lib: { entry: './src/index.js', fileName: (format) => (format === 'umd' ? 'mirador-rotation.js' : 'mirador-rotation.es.js'), formats: ['es', 'umd'], name: 'MiradorDlPlugin', }, rollupOptions: { external: [...Object.keys(pkg.peerDependencies || {}), '__tests__/*', '__mocks__/*'], output: { assetFileNames: 'mirador-rotation.[ext]', globals: { react: 'React', 'react-dom': 'ReactDOM', }, }, }, sourcemap: true, }, } ), esbuild: { exclude: [], // Matches .js and .jsx in __tests__ and .jsx in src include: [/__tests__\/.*\.(js|jsx)$/, /src\/.*\.jsx?$/], loader: 'jsx', }, optimizeDeps: { esbuildOptions: { plugins: [ { name: 'load-js-files-as-jsx', // TODO: rename all our files to .jsx ... setup(build) { build.onLoad({ filter: /(src|__tests__)\/.*\.js$/ }, async (args) => ({ contents: await fs.readFile(args.path, 'utf8'), loader: 'jsx', })); }, }, ], }, }, plugins: [ react(), // カスタムプラグインを追加してディレクトリ構造を修正 { name: 'fix-output-structure', closeBundle: async () => { if (process.env.GITHUB_PAGES) { const distDir = path.resolve('dist'); const demoSrcDir = path.resolve(distDir, 'demo', 'src'); // demo/src/ディレクトリが存在するか確認 try { const demoSrcStats = await fs.stat(demoSrcDir); if (demoSrcStats.isDirectory()) { console.log('Moving files from demo/src to root directory...'); // demo/src内のファイルリストを取得 const files = await fs.readdir(demoSrcDir); // 各ファイルをルートディレクトリに移動 for (const file of files) { const srcPath = path.join(demoSrcDir, file); const destPath = path.join(distDir, file); const stats = await fs.stat(srcPath); if (stats.isFile()) { await fs.copyFile(srcPath, destPath); console.log(`Copied: ${srcPath} -> ${destPath}`); } } console.log('Files moved successfully.'); // demo/src階層を削除(オプション) // await fs.rm(demoSrcDir, { recursive: true, force: true }); // await fs.rm(path.resolve(distDir, 'demo'), { recursive: true, force: true }); // console.log('Removed original directory structure.'); } } catch (err) { if (err.code !== 'ENOENT') { console.error('Error processing output files:', err); } } } } } ], resolve: { alias: { '@tests/': fileURLToPath(new URL('./__tests__', import.meta.url)), }, }, server: { open: '/demo/src/index.html', port: '4446', }, }); まとめ Mirador 4のプラグイン開発にあたり、参考になりましたら幸いです。 ...

Mirador 4で、初期読み込み時に、画像の回転や範囲指定を行う

Mirador 4で、初期読み込み時に、画像の回転や範囲指定を行う

概要 Mirador 4で、初期読み込み時に、画像の回転や範囲指定を行う方法を紹介します。 背景 2025年3月現在、Mirador 4の開発が進められています。alpha版を以下で確認することができます。 https://github.com/ProjectMirador/mirador/releases おそらくMirador 4からの機能かと思いますが、以下のFAQで初期設定の方法が記述されています。 https://github.com/ProjectMirador/mirador/wiki/Frequently-Asked-Questions#q-how-do-i-change-the-view-of-an-image-to-zoom-to-a-certain-area 具体的には、以下のように、initialViewerConfigを用いることで、初期設定ができました。 https://github.com/ProjectMirador/mirador/blob/main/__tests__/integration/mirador/mirador-configs/initial-viewer-config.js export default { id: 'mirador', windows: [{ canvasId: 'https://iiif.harvardartmuseums.org/manifests/object/299843/canvas/canvas-47174892', initialViewerConfig: { thumbnailNavigationPosition: 'far-bottom', x: 934, y: 782, // you need to specify zoom for this to look good zoom: 0.0007, }, manifestId: 'https://iiif.harvardartmuseums.org/manifests/object/299843', }], }; 応用 上記を応用して、以下のような初期設定を行ってみました。 <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="theme-color" content="#000000"> <title>Mirador - Table of contents</title> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500"> </head> <body> <div id="mirador" style="position: absolute; top: 0; bottom: 0; left: 0; right: 0;"></div> <script type="module"> import Mirador from '../../../src'; // import config from './mirador-configs/initial-viewer-config.js'; const xywh = '9554.0,8213.0,1000,1000'; const spl = xywh.split(','); // Box to zoom to const boxToZoom = { height: Number(spl[3]), width: Number(spl[2]), x: Number(spl[0]), y: Number(spl[1]) }; const zoomCenter = { x: boxToZoom.x + boxToZoom.width / 2, y: boxToZoom.y + boxToZoom.height / 2 }; const config = { id: 'mirador', windows: [{ canvasId: "https://iiif.dl.itc.u-tokyo.ac.jp/repo/iiif/187cc82d-11e6-9912-9dd4-b4cca9b10970/canvas/p2", initialViewerConfig: { rotation: 180, x: zoomCenter.x, y: zoomCenter.y, zoom: 1 / Math.max(boxToZoom.width, boxToZoom.height) }, // manifestId: 'https://purl.stanford.edu/sn904cj3429/iiif/manifest', manifestId: "https://iiif.dl.itc.u-tokyo.ac.jp/repo/iiif/187cc82d-11e6-9912-9dd4-b4cca9b10970/manifest", }], }; Mirador.viewer(config); </script> </body> </html> これにより、わかりにくいですが、以下のように、180度回転しつつ、xywhにフォーカスした形で初期読み込みできました。 ...

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の利用にあたり、参考になりましたら幸いです。

IIIF georeference extensionの可視化ツールの改修

IIIF georeference extensionの可視化ツールの改修

概要 IIIF georeference extensionの可視化ツールの改修を行いましたので備忘録です。 以下で公開しているツールです。 https://github.com/nakamura196/iiif_geo 以下のように、現代地図と画像の並列表示機能を提供します。 改修内容 IIIF georeference extensionに基づくデータ作成を支援するツールとして、Allmapsがあります。 https://allmaps.org/ 以下で使い方を紹介しています。 今回の改修では、上記ツールで作成されるデータを読み込めるようにしました。 https://annotations.allmaps.org/images/2e1d3f991aad6cb4 以下が表示例です。 https://nakamura196.github.io/iiif_geo/ja?u=https://annotations.allmaps.org/images/2e1d3f991aad6cb4 まとめ IIIF georeference extensionの応用にあたり、参考になりましたら幸いです。

Miradorで画像を表示し、CETEIceanでテキストを表示するサンプルアプリ

Miradorで画像を表示し、CETEIceanでテキストを表示するサンプルアプリ

概要 TEI/XMLファイルを読み込み、Miradorで画像を表示し、CETEIceanでテキストを表示するサンプルアプリを作成しました。以下のURLからお試しいただけます。 デモサイト https://nakamura196.github.io/ceteicean-mirador/ 背景 これまでにも、同様の機能を提供するアプリケーションを開発してきました。 Next.js を使用した実装例 XSLT を使用した実装例 今回は、HTMLとプレーンなJavaScriptのみを使用して実装する方法をご紹介します。 対象データ 以下の校異源氏物語テキストDBを対象とします。 https://kouigenjimonogatari.github.io/ 実装方法 ソースコードは以下のリポジトリで公開しています。 https://github.com/nakamura196/ceteicean-mirador 実装のポイント 1. CETEIcean の behaviors を利用した pb タグの処理 以下のコードでは、CETEIcean の behaviors を利用して pb タグのクリック時の挙動を定義しています。 ct.addBehaviors({ tei: { // 不要な要素を非表示 graphic: () => document.createDocumentFragment(), figure: () => document.createDocumentFragment(), // pbタグの処理 pb: function (el) { let pb = document.createElement("tei-pb"); // 属性を引き継ぐ if (el.hasAttribute("n")) { pb.setAttribute("n", el.getAttribute("n")); } // corresp属性からzoneIdを取得 const corresp = el.getAttribute("corresp"); if (corresp) { const zoneId = corresp.replace('#', ''); pb.setAttribute("data-zone-id", zoneId); } // ページ番号を表示 const pageNum = el.getAttribute("n") || ""; pb.textContent = `[Page ${pageNum}]`; // クリックイベントを追加 pb.addEventListener("click", function() { const zoneId = this.getAttribute("data-zone-id"); if (zoneId) { const zoneElement = document.querySelector(`tei-zone[id="${zoneId}"]`); if (zoneElement) { const surfaceElement = zoneElement.closest("tei-surface"); if (surfaceElement && surfaceElement.hasAttribute("sameAs")) { goToPage(surfaceElement.getAttribute("sameAs")); } } } }); return pb; } }, }); 2. Mirador でのページ遷移処理 pb タグをクリックした際に、TEI/XML ファイルから Canvas の URI を取得し、Mirador のページ遷移を実行します。 ...

ピラミッドTIFFの作成において、ImageMagickがうまく動作しないケースがある?

ピラミッドTIFFの作成において、ImageMagickがうまく動作しないケースがある?

概要 IIIFの画像配信に向けたピラミッドTIFFの作成において、ImageMagickがうまく動作しないケースがあり、調査してみました。 参考 以下のようなページで、変換方法が説明されています。 https://samvera.github.io/serverless-iiif/docs/source-images#creating-tiled-tiffs Using the VIPS command line # For a 3-channel source image vips tiffsave source_image.tif output_image.tif --tile --pyramid --compression jpeg --tile-width 256 --tile-height 256 # For a source image with an alpha channel vips extract_band source_image.tif temp_image.v 0 --n 3 \ && vips tiffsave temp_image.v output_image.tif --tile --pyramid --compression jpeg --tile-width 256 --tile-height 256 \ && rm temp_image.v Using ImageMagick convert source_image.tif -alpha off \ -define tiff:tile-geometry=256x256 \ -define tiff:generate-pyramids=true \ -compress jpeg \ 'ptif:output_image.tif' 対象データ 以下の画像を使用させていただきます。 ...

mdx.jpのオブジェクトストレージとIIP Image(IIIF Image Server)を使ってIIIF画像を配信する

mdx.jpのオブジェクトストレージとIIP Image(IIIF Image Server)を使ってIIIF画像を配信する

概要 mdx.jpのオブジェクトストレージとIIP Image(IIIF Image Server)を使ってIIIF画像を配信する試行の備忘録です。 以下の記事の続きです。 Docker版IIP Image 以下で、IIPImage serverのDocker Imageが公開されていましたので、こちらを使います。 https://hub.docker.com/r/iipsrv/iipsrv 以下の記事などを参考に、Dockerをインストールします。 https://qiita.com/Marron-chan/items/570c7c7baaae3b4d6b11 実行 前回の記事に倣い、以下のようにmdx.jpのオブジェクトストレージをマウントします。 s3fs satoru196 ~/s3mount -o passwd_file=~/.passwd-s3fs -o url=https://s3ds.mdx.jp -o use_path_request_style -o allow_other 注意点として、前回の記事から、-o allow_otherを追加しています。これを追加しないと、次のコンテナ起動時に以下のエラーが発生しました。 docker: Error response from daemon: error while creating mount source path '~/s3mount/iip/images': mkdir ~/s3mount: file exists Run 'docker run --help' for more information -o allow_otherオプションを追加した上で、以下を実行します。無事に起動しました。 docker run -it -p 9000:9000 -p 8080:80 -v ~/s3mount/iip/images/:/images --rm iipsrv/iipsrv <-----------------------------------> Thu Mar 6 22:35:43 2025 IIPImage Server. Version 1.2 *** Ruven Pillay <ruven@users.sourceforge.net> *** Verbosity level set to 5 Running in standalone mode on socket: 0.0.0.0:9000 with backlog: 2048 Setting maximum image cache size to 10MB Setting filesystem prefix to '/images/' Setting filesystem suffix to '' Setting default JPEG quality to 75 Setting default PNG compression level to 1 Setting default WebP compression level to 50 Setting maximum CVT size to 5000 Setting HTTP Cache-Control header to 'max-age=86400' Setting 3D file sequence name pattern to '_pyr_' Setting default IIIF Image API version to 3 Setting Allow Upscaling to true Setting ICC profile embedding to true Setting up JPEG2000 support via OpenJPEG Setting image processing engine to CPU processor OpenMP enabled for parallelized image processing with 2 threads Setting URI mapping to iiif=>IIIF. Supported protocol: IIIF Memcached support enabled. Connected to servers: 'localhost' with timeout 86400 Initialisation Complete. <-----------------------------------> そして、今回の設定では、オブジェクトストレージの/satoru196/iip/imagesにtiled multi-resolution pyramid TIFFファイルを格納し、以下のようなURLでアクセスできることを確認します。 ...

s3fs を使用してmdx.jpのオブジェクトストレージをファイルシステムのようにマウントする方法

s3fs を使用してmdx.jpのオブジェクトストレージをファイルシステムのようにマウントする方法

概要 s3fs を使用してmdx.jpのオブジェクトストレージをファイルシステムのようにマウントする機会がありましたので、備忘録です。 1. 事前準備 Ubuntu を対象とします。 ✅ s3fs のインストール sudo apt update sudo apt install s3fs ✅ 認証情報の設定 mdx.jpのオブジェクトストレージの アクセスキー と シークレットキー を ~/.passwd-s3fs に保存。 echo “ACCESS_KEY:SECRET_KEY” > ~/.passwd-s3fs chmod 600 ~/.passwd-s3fs # セキュリティのため権限変更 2. S3 ストレージをローカルにマウント ✅ マウントポイントを作成 mkdir ~/s3mount ✅ s3fs でマウント s3fs your-bucket /s3mount -o passwd_file=/.passwd-s3fs -o url=https://s3ds.mdx.jp -o use_path_request_style オプションの説明 • -o passwd_file=~/.passwd-s3fs → 認証情報を指定 • -o url=https://s3ds.mdx.jp → オブジェクトストレージのエンドポイント • -o use_path_request_style → MinIO や Ceph のような “パススタイル” の S3 互換ストレージで必要 ...

Mirador3のFirebase連携annotationsプラグインにおいて、メールアドレスによる登録を可能にしました。

Mirador3のFirebase連携annotationsプラグインにおいて、メールアドレスによる登録を可能にしました。

概要 Mirador3のFirebase連携annotationsプラグインを開発しています。 こちらについて、これまではGoogleアカウントによるログイン機能のみを提供していましたが、メールアドレスによるログイン機能を追加しました。 機能紹介 以下、ログインボタンを押した場合です。 メールアドレスによるログインの場合、アカウントの新規作成が可能です。 ログイン後、ユーザに関する情報を表示するようにしました。 アイコンをクリックすると、ログアウトボタンが表示されます。 まとめ IIIFを用いたアノテーションの作成と共有において、参考になりましたら幸いです。

vttファイルからTEI/XMLを作成する

vttファイルからTEI/XMLを作成する

概要 vttファイルからTEI/XMLファイルを作成する方法の備忘録です。 さらに、IIIFマニフェストから、vttファイルおよびTEI/XMLファイルにアクセスできるようにしてみます。結果、以下のように、TEI/XMLファイルがSeeAlsoに関連づけられ、また「Annotations」タブから、vttファイルの内容にアクセスできます。 https://clover-iiif-demo.vercel.app/?manifest=https://movie-tei-demo.vercel.app/data/sdcommons_npl-02FT0102974177/sdcommons_npl-02FT0102974177_vtt.json 参考 以下の「The Ethiopian Language Archive」における取り組みを参考にしました。特に、TEI/XMLの構造化方法が特に参考になりました。 https://dev.jael.info/documentation/ 例 以下で作成したvttファイルを対象とします。 具体的には、以下の『県政ニュース 第1巻』(県立長野図書館)を使用します。 https://www.ro-da.jp/shinshu-dcommons/library/02FT0102974177 TEI/XMLの作成 作成したTEI/XMLファイルの例は以下です。 https://movie-tei-demo.vercel.app/data/sdcommons_npl-02FT0102974177/sdcommons_npl-02FT0102974177.xml 具体的には以下です。 <?xml-model href="http://www.tei-c.org/release/xml/tei/custom/schema/relaxng/tei_all.rng" type="application/xml" schematypens="http://relaxng.org/ns/structure/1.0"?> <?xml-model href="http://www.tei-c.org/release/xml/tei/custom/schema/relaxng/tei_all.rng" type="application/xml" schematypens="http://purl.oclc.org/dsdl/schematron"?> <teiCorpus xmlns="http://www.tei-c.org/ns/1.0"> <teiHeader> <fileDesc> <titleStmt> <title>県政ニュース 第1巻</title> </titleStmt> <publicationStmt> <distributor>中村覚</distributor> <availability> <licence target="http://creativecommons.org/licenses/by/4.0/">http://creativecommons.org/licenses/by/4.0/</licence> </availability> </publicationStmt> <notesStmt> <note>昭和30年に長野県が制作した記録映像。次の8タイトルを収録する。「地方選挙終る」、「地方選挙後初の県議会開かる」、「三十年度を賄う県のお台所」、「すすむ土木建設」、「明るく正しく健やかに」、「幕をとじた善光寺の御開帳」、「勇ましい水防訓練」、「お国じまん民謡大会」</note> </notesStmt> <sourceDesc> <biblStruct> <monogr> <title>県政ニュース 第1巻</title> <availability> <licence target="https://creativecommons.org/publicdomain/zero/1.0/">cc0</licence> </availability> <imprint> <publisher>信州デジタルコモンズ 県立長野図書館所蔵資料</publisher> </imprint> </monogr> <ref target="https://www.ro-da.jp/shinshu-dcommons/library/02FT0102974177">信州デジタルコモンズ 県立長野図書館所蔵資料</ref> </biblStruct> </sourceDesc> </fileDesc> </teiHeader> <TEI> <teiHeader> <fileDesc> <titleStmt> <title>県政ニュース 第1巻</title> </titleStmt> <publicationStmt> <p /> </publicationStmt> <notesStmt> <note /> </notesStmt> <sourceDesc> <p /> </sourceDesc> </fileDesc> <revisionDesc> <change when="2025-02-18"> 作成 </change> </revisionDesc> </teiHeader> <text> <body> <timeline unit="ms"> <when absolute="00:00:00.000" xml:id="t1" /> <when absolute="00:00:25.500" xml:id="t2" /> <when absolute="00:00:38.500" xml:id="t3" /> <when absolute="00:00:50.500" xml:id="t4" /> <when absolute="00:00:55.500" xml:id="t5" /> <when absolute="00:01:03.500" xml:id="t6" /> <when absolute="00:01:08.500" xml:id="t7" /> <when absolute="00:01:18.500" xml:id="t8" /> <when absolute="00:01:23.500" xml:id="t9" /> <when absolute="00:01:33.500" xml:id="t10" /> ... </timeline> <annotationBlock xml:id="ab1"> <u start="#t1" end="#t2">♪♪♪</u> </annotationBlock> <annotationBlock xml:id="ab2"> <u start="#t2" end="#t3">今年は選挙の当たり年。2月の総選挙に引き続いて、4月の県市町村と八木早の選挙で、長野県116万有権者の関心は非常な高まりようです。</u> </annotationBlock> <annotationBlock xml:id="ab3"> <u start="#t3" end="#t4">男女青年や婦人層はもちろんのこと、この老人も今年88を迎えたとはいえ、その慎重な投票ぶりが老いの表に一徹さを伺わせています。</u> </annotationBlock> <annotationBlock xml:id="ab4"> <u start="#t4" end="#t5">♪〜</u> </annotationBlock> <annotationBlock xml:id="ab5"> <u start="#t5" end="#t6">かくて県下における投票率、全国の上位を占める立派な成績を収めました。</u> </annotationBlock> <annotationBlock xml:id="ab6"> <u start="#t6" end="#t7">♪ ♪</u> </annotationBlock> <annotationBlock xml:id="ab7"> <u start="#t7" end="#t8">その日午後8時 きっかり、県下一斉に即日開票が行われました。</u> </annotationBlock> <annotationBlock xml:id="ab8"> <u start="#t8" end="#t9">その結果、長野県知事には、前知事の林寅氏が当選。</u> </annotationBlock> <annotationBlock xml:id="ab9"> <u start="#t9" end="#t10">またこれと同時に、県議会議員61名の当選も決定しました。</u> </annotationBlock> ... </body> </text> </TEI> </teiCorpus> IIIFマニフェストファイルの作成 上述したTEI/XMLファイルをseeAlsoに持つIIIFマニフェストファイルを作成しました。 ...

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; まとめ 不完全な点もあるかと思いますが、参考になりましたら幸いです。

Mirador 3の mirador-annotations プラグインで、付与したアノテーションをダウンロードする

Mirador 3の mirador-annotations プラグインで、付与したアノテーションをダウンロードする

概要 Mirador 3の mirador-annotations プラグインで、付与したアノテーションをダウンロードするための設定に関する備忘録です。 https://mirador-annotations.vercel.app/ 背景 以下の記事で、アノテーションをGoogleのFirestoreに登録する方法を紹介しました。 ここで登録したアノテーションをダウンロードするにあたり、mirador-annotationsプラグインでダウンロードオプションが提供されていたので、その方法について紹介します。 方法 以下がデモページのソースコードになりますが、exportLocalStorageAnnotationsというオプションをtrueにすることで、ダウンロードアイコンが表示されました。 import mirador from 'mirador/dist/es/src/index'; import annotationPlugins from '../../src'; import LocalStorageAdapter from '../../src/LocalStorageAdapter'; import AnnototAdapter from '../../src/AnnototAdapter'; const endpointUrl = 'http://127.0.0.1:3000/annotations'; const config = { annotation: { adapter: (canvasId) => new LocalStorageAdapter(`localStorage://?canvasId=${canvasId}`), // adapter: (canvasId) => new AnnototAdapter(canvasId, endpointUrl), exportLocalStorageAnnotations: false, // display annotation JSON export button }, id: 'demo', window: { defaultSideBarPanel: 'annotations', sideBarOpenByDefault: true, }, windows: [{ loadedManifest: 'https://iiif.harvardartmuseums.org/manifests/object/299843', }], }; mirador.viewer(config, [...annotationPlugins]); ダウンロードによって得られるJSONファイルの例は以下です。canvas毎にダウンロードできます。 { "id": "https://dl.ndl.go.jp/api/iiif/3437686/canvas/1/annotations", "items": [ { "body": { "type": "TextualBody", "value": "<p>Cを変更</p>" }, "type": "Annotation", "motivation": "commenting", "target": { "selector": [ { "type": "FragmentSelector", "value": "xywh=5314,1983,195,206" }, { "type": "SvgSelector", "value": "<svg xmlns='http://www.w3.org/2000/svg'><path xmlns=\"http://www.w3.org/2000/svg\" d=\"M5314.21383,2086.96256c0,-56.89385 43.74418,-103.01543 97.70536,-103.01543c53.96118,0 97.70536,46.12158 97.70536,103.01543c0,56.89385 -43.74418,103.01543 -97.70536,103.01543c-53.96118,0 -97.70536,-46.12158 -97.70536,-103.01543z\" data-paper-data=\"{"state":null}\" fill=\"none\" fill-rule=\"nonzero\" stroke=\"#00bfff\" stroke-width=\"3\" stroke-linecap=\"butt\" stroke-linejoin=\"miter\" stroke-miterlimit=\"10\" stroke-dasharray=\"\" stroke-dashoffset=\"0\" font-family=\"none\" font-weight=\"none\" font-size=\"none\" text-anchor=\"none\" style=\"mix-blend-mode: normal\"/></svg>" } ], "source": { "id": "https://dl.ndl.go.jp/api/iiif/3437686/canvas/1", "type": "Canvas", "partOf": { "id": "https://dl.ndl.go.jp/api/iiif/3437686/manifest.json", "type": "Manifest" } } }, "canvasId": "https://dl.ndl.go.jp/api/iiif/3437686/canvas/1", "created": { "seconds": 1739524276, "nanoseconds": 522000000 }, "id": "exlXeaswdsiuhcFiLWSl", "userName": "中村覚", "manifestId": "https://dl.ndl.go.jp/api/iiif/3437686/manifest.json", "modified": { "seconds": 1739526046, "nanoseconds": 354000000 } }, { "motivation": "commenting", "id": "rx2JMkwtwuQDgbtAIOoO", "created": { "seconds": 1739524259, "nanoseconds": 611000000 }, "manifestId": "https://dl.ndl.go.jp/api/iiif/3437686/manifest.json", "body": { "type": "TextualBody", "value": "<p>校異源氏物語</p>" }, "userName": "中村覚", "canvasId": "https://dl.ndl.go.jp/api/iiif/3437686/canvas/1", "target": { "selector": [ { "type": "FragmentSelector", "value": "xywh=478,307,748,3424" }, { "value": "<svg xmlns='http://www.w3.org/2000/svg'><path xmlns=\"http://www.w3.org/2000/svg\" d=\"M478.38073,3732.45418v-3424.85175h748.39353v3424.85175z\" data-paper-data=\"{"state":null}\" fill=\"none\" fill-rule=\"nonzero\" stroke=\"#00bfff\" stroke-width=\"3\" stroke-linecap=\"butt\" stroke-linejoin=\"miter\" stroke-miterlimit=\"10\" stroke-dasharray=\"\" stroke-dashoffset=\"0\" font-family=\"none\" font-weight=\"none\" font-size=\"none\" text-anchor=\"none\" style=\"mix-blend-mode: normal\"/></svg>", "type": "SvgSelector" } ], "source": { "partOf": { "id": "https://dl.ndl.go.jp/api/iiif/3437686/manifest.json", "type": "Manifest" }, "type": "Canvas", "id": "https://dl.ndl.go.jp/api/iiif/3437686/canvas/1" } }, "modified": { "seconds": 1739524259, "nanoseconds": 611000000 }, "type": "Annotation" } ], "type": "AnnotationPage" } まとめ Mirador 3の mirador-annotations プラグインの利用にあたり、参考になりましたら幸いです。 ...

Mirador 3の mirador-annotations プラグイン向けに、Firestore用のアダプタを開発しました。

Mirador 3の mirador-annotations プラグイン向けに、Firestore用のアダプタを開発しました。

概要 Mirador 3の mirador-annotations プラグイン向けに、Firestore用のアダプタを開発したので備忘録です。以下でお試しいただけます。 https://mirador-annotations.vercel.app/ 背景 Mirador 3の mirador-annotations プラグインは、デフォルトではローカルストレージにアノテーションが保存されます。 一方、以下の記事で紹介したように、アダプタを変更することで、ローカルストレージではない場所にアノテーションを保存することもできます。 そこで、今回はGoogleのFirestoreに保存するためのアダプタを開発しました。 使い方 ユーザごとにアノテーションを保存できる仕組みとしました。 そのため、まず以下のボタンから、ログインを行います。 ログイン後、自分が付与したアノテーションが表示されます。 アノテーションの登録方法に違いはありません。 ログアウトすると、アノテーションが非表示となります。 リポジトリ ソースコードは以下で公開しています。 https://github.com/nakamura196/mirador-annotations 特に、以下が今回開発したアダプタです。 https://github.com/nakamura196/mirador-annotations/blob/master/src/FirestoreAnnotationAdapter.js また、以下がログインを行うためのボタンです。 https://github.com/nakamura196/mirador-annotations/blob/master/src/GoogleAuthButton.js 開発される際には、.env.exampleを参考に、FirebaseのAPIキーなどを.envに記入します。 https://github.com/nakamura196/mirador-annotations/blob/master/.env.example まとめ 本アプリを使用することで、ユーザごとに、IIIF画像に対するアノテーションを簡単に管理できるようになると思います。 なお、冒頭でご紹介した本アプリのデモ環境に登録されたデータは任意のタイミングで削除する可能性があるのでご注意ください。

IIIFマニフェストファイルからOCR結果を含むTEI_XMLファイルを作成するプログラム

IIIFマニフェストファイルからOCR結果を含むTEI_XMLファイルを作成するプログラム

概要 IIIFマニフェストファイルからOCR結果を含むTEI_XMLファイルを作成するプログラムを作成しました。このプログラムの使用方法について説明します。 仕組み IIIFマニフェストファイルのURLを指定して、NDL古典籍OCR-LiteによるOCR結果を含むTEI/XMLファイルを作成します。 https://github.com/ndl-lab/ndlkotenocr-lite 使い方 以下のノートブックにアクセスしてください。 https://colab.research.google.com/github/nakamura196/000_tools/blob/main/IIIFマニフェストファイルからTEI_XMLファイルを作成するプログラム.ipynb そして、一つ目の再生ボタンを押します。 完了したら、「実行」という部分のmanifest_urlとoutput_dirの値を更新して、セルを実行します。 output_dirに、OCR結果を含むTEI/XMLファイルが出力されます。 出力例 以下のように、ページおよび行ごとのOCR結果を含むファイルが作成されます。 <?xml-model href="http://www.tei-c.org/release/xml/tei/custom/schema/relaxng/tei_all.rng" type="application/xml" schematypens="http://relaxng.org/ns/structure/1.0"?> <?xml-model href="http://www.tei-c.org/release/xml/tei/custom/schema/relaxng/tei_all.rng" type="application/xml" schematypens="http://purl.oclc.org/dsdl/schematron"?> <TEI xmlns="http://www.tei-c.org/ns/1.0"> <teiHeader> <fileDesc> <titleStmt> <title>OCR結果: https://iiif.dl.itc.u-tokyo.ac.jp/repo/iiif/0f11a3ed-18c2-7322-6340-19ed3f0d966e/manifest</title> </titleStmt> <publicationStmt> <publisher>NDL古典OCR</publisher> <date>2025-01-29</date> </publicationStmt> <sourceDesc> <bibl> <ptr target="https://iiif.dl.itc.u-tokyo.ac.jp/repo/iiif/0f11a3ed-18c2-7322-6340-19ed3f0d966e/manifest"/> </bibl> </sourceDesc> </fileDesc> </teiHeader> <text> <body> <ab n="1" type="page" facs="https://iiif.dl.itc.u-tokyo.ac.jp/iiif/soto_ogai_202310/A05_4/002/A05_4_002_0001.tif/full/full/0/default.jpg"> <lb/> <seg type="本文" n="1" corresp="#zone-1">国外</seg> <lb/> <seg type="本文" n="3" corresp="#zone-3">空せみ二</seg> <lb/> <seg type="本文" n="4" corresp="#zone-4">ゆふかほ</seg> </ab> <ab n="2" type="page" facs="https://iiif.dl.itc.u-tokyo.ac.jp/iiif/soto_ogai_202310/A05_4/002/A05_4_002_0002.tif/full/full/0/default.jpg"> <lb/> <seg type="本文" n="1" corresp="#zone-1">あつたい</seg> <lb/> <seg type="本文" n="2" corresp="#zone-2">・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・</seg> <lb/> <seg type="本文" n="3" corresp="#zone-3">中川ノ中川宿にて之源氏十六才</seg> <lb/> <seg type="本文" n="4" corresp="#zone-4">住ぬ</seg> <lb/> <seg type="本文" n="5" corresp="#zone-5">ねられ給はぬまゝに。われはかく人に</seg> <lb/> <seg type="本文" n="6" corresp="#zone-6">にくまれてもならはぬを。こよひなん</seg> <lb/> <seg type="本文" n="7" corresp="#zone-7">はじめてうしと世を思ひ知ぬれば</seg> <lb/> <seg type="本文" n="8" corresp="#zone-8">はつかしうてながらふまじくこそ思ひ</seg> <lb/> <seg type="本文" n="9" corresp="#zone-9">なりぬれなどの給へば。なみたをさへ</seg> <lb/> <seg type="本文" n="10" corresp="#zone-10">こぼしてふしたり。いどらうたしと</seg> <lb/> <seg type="本文" n="11" corresp="#zone-11">おぼすてさぐりのほそくちいさき</seg> <lb/> <seg type="本文" n="12" corresp="#zone-12">ほどがみのいとながからざりしけ</seg> <lb/> まとめ 不完全な点もあるかと思いますが、参考になりましたら幸いです。 ...

Omeka SのImage ServerでのCORS対応

Omeka SのImage ServerでのCORS対応

概要 Omeka SのImage ServerでのCORS対応に関する備忘録です。 背景 以下の記事で、Omeka SのIIIF ServerモジュールでのCORSエラーへの対応方法を紹介しました。 上記の設定により、IIIFマニフェストファイルはダウンロードできるようになりましたが、以下のように、画像がダウンロードできなくなるケースがありました。 Access to image at 'https://xxx/iiif/2/8455/full/86,/0/default.jpg' from origin 'https://uv-v4.netlify.app' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed. この症状への対処方法について紹介します。 方法 複数の箇所にaccess-control-allow-originが記載されていることが原因です。 上記の記事の対応により、サイト全体に対してCORSの設定を行いましたので、Image Serverモジュールにおける設定が適用されないように修正します。なお、このような対応が必要なものは特定のバージョンのモジュールで、最新のモジュールなどを使用する場合には不要かもしれません。 具体的には、以下のファイルに記載されています。 https://github.com/Daniel-KM/Omeka-S-module-ImageServer/blob/master/src/Controller/ImageController.php 以下のように記載されており、条件によって適用の可否が設定されていますが、使用しているバージョンや他のモジュールとの組み合わせにより、この条件が適用されず、複数のaccess-control-allow-originが設定されてしまうことが原因かと思われます。 $headers->addHeaderLine('Access-Control-Allow-Origin', '*'); 上記のような記載をコメントアウトすることにより、'Access-Control-Allow-Origin' header contains multiple values '*, *'というエラーを解消することができました。 まとめ 同様の事象の遭遇することは稀かもしれませんが、Omeka Sを用いたIIIF画像配信にあたり、参考になりましたら幸いです。

AppSheetを使ってデジタルコレクションを構築する

AppSheetを使ってデジタルコレクションを構築する

概要 AppSheetを使ってデジタルコレクションを試作してみましたので、備忘録です。 対象データ 以下の記事と同じデータを利用しています。 具体的には、『東京帝國大學本部構内及農學部建物鳥瞰圖』(東京大学農学生命科学図書館所蔵)をサンプルデータとして使用します。 https://iiif.dl.itc.u-tokyo.ac.jp/repo/s/agriculture/document/187cc82d-11e6-9912-9dd4-b4cca9b10970 成果物 以下のURLからアクセスいただけます。 https://www.appsheet.com/start/092a3ff4-1074-4e27-bbd1-f4820da77511 画像の一覧機能や、 地図へのマッピング機能、 簡単な集計機能を提供します。 作成方法 アプリの初期設定 アプリを作成します。 データソースとして、Google Sheetsを選択します。ちなみに、「AppSheet Database」を使用した場合、Free Planではデプロイできませんでした。 結果、デフォルトで地図表示、およびデータの一覧を表示するページ(View)を作成してくれました。 データ 「データ」タブに移動して、設定を行います。 例えば、使用しない項目には、「SHOW?」列のチェックを外します。また、「KEY?」と「LABEL?」に対して、「identifier」と「title」を割り当てました。 次に、「DISPLAY NAME」で、アプリ上で表示する名称を入力します。 さらに、検索に使用したい項目にのみ、「SEARCH?」列にチェックを入れます。 加えて、今回はデータの閲覧のみを行うため、「Read-Only」の設定を行います。 Views(マップ) Views(マップ)はデフォルトのまま使用しました。 Views(main) ここでは、View nameを「Browse」に変更して、View typeとして「card」を選択して、Positionには「next」を与えました。Positionについては、メニューにおいて「Browse」が「Map」の左にくるようになります。 さらに、レイアウトについて、それぞれの要素に割り当てるカラムを設定します。例えば以下では、画像を表示する列として、「_image_url」を指定しています。 結果、一覧画面が以下のように表示されます。 Views(chart) 以下のように、新規にViewを追加します。 そして、「Chart columns」に、ここではカテゴリが格納されたカラムを設定しました。結果、画面右部のように表示が変わります。 その他 その他、アプリの見た目を変更します。 設定 > Theme & Brand から、例えば、ヘッダーにロゴなどが表示されるように変更します。 また、今回の初期設定では「Map」Viewがはじめに開いてしまうので、「Browse」が表示されるように、「Views」の設定を変更します。 Preview ここまでのところでプロトタイプはほぼ完成です。Previewモードで確認することができます。デフォルトで、各レコードの詳細画面も表示されます。 ...

Pythonを使ってOmeka Sにメディアをアップロードする方法

Pythonを使ってOmeka Sにメディアをアップロードする方法

概要 Pythonを使ってOmeka Sにメディアをアップロードする方法の備忘録です。 準備 環境変数を用意します。 OMEKA_S_BASE_URL=https://dev.omeka.org/omeka-s-sandbox # 例 OMEKA_S_KEY_IDENTITY= OMEKA_S_KEY_CREDENTIAL= 初期化します。 import requests from dotenv import load_dotenv import os def __init__(self): load_dotenv(verbose=True, override=True) OMEKA_S_BASE_URL = os.environ.get("OMEKA_S_BASE_URL") self.omeka_s_base_url = OMEKA_S_BASE_URL self.items_url = f"{OMEKA_S_BASE_URL}/api/items" self.media_url = f"{OMEKA_S_BASE_URL}/api/media" self.params = { "key_identity": os.environ.get("OMEKA_S_KEY_IDENTITY"), "key_credential": os.environ.get("OMEKA_S_KEY_CREDENTIAL") } ローカルファイルをアップロードする def upload_media(self, path, item_id): files = {} payload = {} file_data = { 'o:ingester': 'upload', 'file_index': '0', 'o:source': path.name, 'o:item': {'o:id': item_id} } payload.update(file_data) params = self.params files = [ ('data', (None, json.dumps(payload), 'application/json')), ('file[0]', (path.name, open(path, 'rb'), 'image')) ] media_response = requests.post( self.media_url, params=params, files=files ) # レスポンスを確認 if media_response.status_code == 200: return media_response.json()["o:id"] else: return None IIIF画像をアップロードする 以下のようなIIIF画像のURLを指定して登録します。 https://dl.ndl.go.jp/api/iiif/1288277/R0000030/info.json def upload_media(self, iiif_url, item_id): payload = { 'o:ingester': 'iiif', 'file_index': '0', 'o:source': iiif_url, 'o:item': {'o:id': item_id}, } media_response = requests.post( self.media_url, params=self.params, headers={'Content-Type': 'application/json'}, data=json.dumps(payload) ) # レスポンスを確認 if media_response.status_code == 200: return media_response.json()["o:id"] else: return None まとめ Omeka Sへの画像登録にあたり、参考になりましたら幸いです。 ...

aleph-r3fを試す

aleph-r3fを試す

概要 以下の記事で、Aleph 3D viewerを紹介しました。 その後調べた結果、以下のリポジトリの存在も知りました。 https://github.com/aleph-viewer/aleph-r3f 以下のように説明されており、react-three-fiberとshadcn/uiを使用している点に違いがあるようでした。 Aleph is a 3D object viewer and annotation/measurement tool built with react-three-fiber and shadcn/ui 以下のように、アノテーション付与機能なども改良されているようでした。 今回の記事でも、菊池市デジタルアーカイブで公開されている「石淵家地球儀」の3Dデータを使用します。 https://adeac.jp/kikuchi-city/catalog/e0001 使い方 以下で閲覧いただけます。 https://iiif-aleph-r3f.vercel.app/ アノテーションタブで、アノテーションの付与を行うことができました。 アノテーションデータのインポート/エクスポートを行うことができ、JSON形式でのエクスポート結果は以下でした。 [ { "position": { "x": -0.06690392681702004, "y": 0.6256817352784154, "z": -0.7424544387001097 }, "normal": { "x": -0.11627753958254597, "y": 0.6430031011979032, "z": -0.7569851687044529 }, "cameraPosition": { "x": -0.15922188799592055, "y": 1.1767071158114843, "z": -1.4378842144444104 }, "cameraTarget": { "x": -0.0023649930953979492, "y": -0.0009789466857910165, "z": -0.011684000492095947 }, "rotation": { "isEuler": true, "_x": 0, "_y": 0, "_z": 0, "_order": "XYZ" }, "label": "大西洋", "description": "初めてのアノテーション" } ] カスタマイズ 「石淵家地球儀」を表示するにあたり、以下のようにソースコードを編集する必要がありました。 import './App.css'; import { useEffect, useRef } from 'react'; ... const [{ src }, _setLevaControls] = useControls(() => ({ src: { options: { // 'Measurement Cube': { // url: 'https://cdn.glitch.global/afd88411-0206-477e-b65f-3d1f201de994/measurement_cube.glb?v=1710500461208', // label: 'Measurement Cube', // }, 石淵家地球儀: 'https://sukilam.aws.ldas.jp/files/original/253efdf34478459954ae04f6b3befa5f3822ed59.glb', 'Flight Helmet': 'https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Assets/main/Models/FlightHelmet/glTF/FlightHelmet.gltf', ... また、Vercelへのデプロイにあたり、以下のように、tailwind.config.jsを修正する必要がありました。 ...

Aleph 3D viewerを試す

Aleph 3D viewerを試す

概要 3D object viewerの一つであるAlephを試してみましたので、備忘録です。 https://github.com/aleph-viewer/aleph 菊池市デジタルアーカイブで公開されている「石淵家地球儀」の3Dデータを使用しています。 https://adeac.jp/kikuchi-city/catalog/e0001 背景 IIIF対応の3Dビューアを調査する過程で、以下の記事を見つけました。 https://pro.europeana.eu/post/iiif-for-3d-making-web-interoperability-multi-dimensional こちらで紹介されているビューアの一つとして、Alephを知りました。 使い方 GitHubリポジトリをForkして、Vercelにデプロイしました。 https://aleph-coral.vercel.app/ 初期表示は以下です。 画面左部の入力フォームにあるglbファイルへのURLを変更することで、指定した3Dモデルが表示されました。 まとめ 3Dビューアの調査にあたり、参考になりましたら幸いです。