ホーム 記事一覧 ブック DH週間トピックス 検索 このサイトについて
English
Archivematica における非DCメタデータの登録検証 ── source-metadata.csv を使ったEADの組み込み

Archivematica における非DCメタデータの登録検証 ── source-metadata.csv を使ったEADの組み込み

Archivematica では、Dublin Core(DC)以外のメタデータスキーマもAIPのMETS.xmlに組み込むことができます。本ガイドでは、source-metadata.csv を使って EAD や MODS などの非DCメタデータをTransferに含め、AIPに正しく格納されるかをAPI経由で検証します。 目次 背景と目的 source-metadata.csv の仕組み XML Validation 機能 検証1: MODS単独でのメタデータ登録 検証2: EAD + MODS の同時登録 METS.xml における非DCメタデータの格納形式 検証3: Reingest によるメタデータ追加 まとめ 背景と目的 Archivematica の標準的な Transfer では、metadata/metadata.csv に記述した Dublin Core メタデータが METS.xml に <dmdSec> として格納されます。しかし、実際のデジタルアーカイブ運用では、以下のようなユースケースで DC 以外のメタデータスキーマを扱う必要があります。 EAD(Encoded Archival Description) : アーカイブズの階層記述で広く使われる標準 MODS(Metadata Object Description Schema) : 図書館資料の詳細記述に使われるスキーマ LIDO : 博物館・美術館資料の記述標準 MARC21 : 図書館の目録データフォーマット Archivematica は source-metadata.csv というCSVファイルを通じて、任意の XML メタデータを Transfer に紐付け、AIP の METS.xml に <dmdSec> として格納する機能を提供しています。本ガイドでは、この機能を API 経由で実際に検証します。 ...

AtoM REST APIによるデジタルアーカイブ構築の検証

AtoM REST APIによるデジタルアーカイブ構築の検証

はじめに AtoM (Access to Memory) は、アーカイブ機関向けのオープンソースWebアプリケーションです。世界中の図書館・文書館・博物館で、資料記述の管理に利用されています。 AtoMの操作は通常Web UIから行いますが、REST APIを使えば外部システムとの連携やバッチ処理が可能になります。本記事では、現実的な業務シナリオ に沿ってAPIを一通り試し、Web UIでの反映も確認していきます。 APIプラグインの開発経緯や実装の詳細は、別記事 AtoMのREST APIを拡張するプラグインを開発した話 をご覧ください。 利用するAPI arRestApiPlugin (AtoM標準): 資料記述(Information Object)のCRUD arExtendedApiPlugin (独自開発): 所蔵機関・典拠レコード・受入記録・タクソノミー・機能記述・デジタルオブジェクトの操作 全28エンドポイントの一覧は開発記事を参照してください。 事前準備 # AtoMのURL(環境に合わせて変更) export ATOM_URL="http://localhost:63001" # APIキー(Admin > Settings > Global > API key で設定) export API_KEY="your-api-key-here" APIキーはAtoMの管理画面(Settings > Global)で設定・確認できます。 業務シナリオ:「橋本市立図書館デジタルアーカイブの構築」 ストーリー 橋本市立図書館が、郷土史研究家の山田花子氏から寄贈された橋本市の古写真コレクション(明治〜昭和期)のデジタルアーカイブを構築する。 以下の手順で、アーカイブの構築に必要な一連の作業をAPIで実行していきます。 Step 業務内容 API 0 初期状態を確認する GET /api/summary 1 所蔵機関を登録する POST /api/repositories 2 所蔵機関の情報を確認する GET /api/repositories/:slug 3 連絡先情報を追加する PUT /api/repositories/:slug 4 寄贈者を登録する POST /api/actors 5 寄贈者の情報を確認する GET /api/actors/:slug 6 受入記録を作成する POST /api/accessions 7 処理ステータスを更新する PUT /api/accessions/:slug 8 タクソノミー(分類語彙)を確認する GET /api/taxonomies 9 主題分類を追加する POST /api/taxonomies/:id/terms 10 分類名を修正する PUT /api/taxonomies/terms/:id 11 資料記述を作成する POST /api/informationobjects 12 デジタル画像を添付する POST /api/informationobjects/:slug/digitalobject 13 最終状態を確認する GET /api/summary シナリオ完了後、「追加機能の紹介」セクションで以下も解説します: ...

AtoMのREST APIを拡張するプラグインを開発した話

AtoMのREST APIを拡張するプラグインを開発した話

はじめに AtoM (Access to Memory) は、アーカイブ機関向けのオープンソースWebアプリケーションです。ISAD(G)、ISAAR(CPF)、ISDFなどの国際標準に準拠した記述管理機能を提供しており、世界中の図書館・文書館・博物館で利用されています。 AtoMには arRestApiPlugin という標準のREST APIプラグインが同梱されていますが、以下の制約があります: 情報オブジェクト(資料記述)のCRUD が中心で、カバー範囲が限定的 所蔵機関(Repository) 、典拠レコード(Actor) 、受入記録(Accession) のAPIがない タクソノミー (分類語彙)の操作APIがない デジタルオブジェクト のアップロードAPIが実用的でない 機能記述(Function) のAPIがない これでは、外部システムとの連携やバッチ処理による大量登録といった業務ニーズに応えられません。 本記事では、これらの課題を解決するために開発した arExtendedApiPlugin の実装について解説します。 このプラグインを使って実際に業務シナリオを実行した記事もあります:APIで構築する図書館デジタルアーカイブ — AtoM業務シナリオ実践ガイド arExtendedApiPlugin の概要 エンドポイント一覧(全28エンドポイント) リソース メソッド エンドポイント 説明 サマリー GET /api/summary 各エンティティの件数 所蔵機関 GET /api/repositories 一覧(検索・ページネーション) GET /api/repositories/:slug 詳細取得 POST /api/repositories 新規作成 PUT /api/repositories/:slug 更新 DELETE /api/repositories/:slug 削除 POST /api/repositories/:slug/logo ロゴアップロード DELETE /api/repositories/:slug/logo ロゴ削除 典拠レコード GET /api/actors 一覧 GET /api/actors/:slug 詳細取得 POST /api/actors 新規作成 PUT /api/actors/:slug 更新 DELETE /api/actors/:slug 削除 受入記録 GET /api/accessions 一覧 GET /api/accessions/:slug 詳細取得 POST /api/accessions 新規作成 PUT /api/accessions/:slug 更新 DELETE /api/accessions/:slug 削除 タクソノミー GET /api/taxonomies 全タクソノミー一覧 POST /api/taxonomies/:id/terms 用語追加 PUT /api/taxonomies/terms/:id 用語更新 DELETE /api/taxonomies/terms/:id 用語削除 機能記述 GET /api/functions 一覧 GET /api/functions/:slug 詳細取得 POST /api/functions 新規作成 PUT /api/functions/:slug 更新 DELETE /api/functions/:slug 削除 デジタルオブジェクト POST /api/informationobjects/:slug/digitalobject アップロード Note : 資料記述(Information Object)のCRUDは、既存の arRestApiPlugin が提供する POST/GET /api/informationobjects を使用します。 ...

AlfrescoをDockerで起動し、REST APIでレコード管理のライフサイクルを体験する

AlfrescoをDockerで起動し、REST APIでレコード管理のライフサイクルを体験する

概要 本記事では、Alfresco Governance Services Community Edition(以下AGS)の最新版(25.3.0)をDockerで起動し、REST APIを使ってレコード管理の一連のライフサイクルを体験します。 具体的には、以下の業務シナリオを想定します。 シナリオ: 契約書管理 業務部門が契約書を作成・登録する レコード管理者がレコードとして宣言し、ファイルプランに分類する 保持スケジュール(Retention Schedule)を設定する 契約終了後、カットオフ(現用→非現用)を実行する 保持期間(3年)の経過後、廃棄する 訴訟対応が発生した場合、ホールド(凍結)により廃棄を停止する 以下の前回の記事をベースに、最新版での構築手順とAPIの使い方を紹介します。 https://zenn.dev/nakamura196/articles/8da7161ff3df30 環境 acs-deployment: v10.2.0(2026年2月リリース) Alfresco Governance Repository Community: 25.3.0 Alfresco Governance Share Community: 25.3.0 Alfresco Search Services: 2.0.17 Traefik: 3.6 PostgreSQL: 16.5 セットアップ リポジトリのクローン git clone https://github.com/Alfresco/acs-deployment cd acs-deployment git checkout v10.2.0 cd docker-compose compose fileの作成 community-compose.yamlをベースに、Governance Services用のcompose fileを作成します。変更点は以下の3つです。 1. イメージの差し替え サービス 変更前 変更後 alfresco alfresco/alfresco-content-repository-community:25.3.0 alfresco/alfresco-governance-repository-community:25.3.0 share alfresco/alfresco-share:25.3.0 alfresco/alfresco-governance-share-community:25.3.0 2. 認証チケットのタイムアウト対策(後述) ...

IIIFマニフェストを用いたテキスト比較ツールの開発

IIIFマニフェストを用いたテキスト比較ツールの開発

はじめに 古典籍のデジタル化が進む中、異なる写本や校訂本のテキストを比較・分析するニーズが高まっています。本稿では、IIIF(International Image Interoperability Framework)マニフェストを活用し、2つの資料の画像とテキストを並べて比較できるWebアプリケーション「Text Comparison Tool」を紹介します。 デモサイト : https://iiif-text.vercel.app/ 背景と課題 デジタルアーカイブで公開されている古典籍には、IIIFマニフェストにテキストアノテーションが付与されているものがあります。しかし、2つの資料のテキストを並べて比較する手軽なツールは多くありません。 例えば、ある作品の校訂本と写本を比較する場合、以下のような作業が必要です: 画像を並べて目視で比較する テキストの差異を一文字ずつ確認する どの程度類似しているかを定量的に把握する これらを1つのツールで実現することを目指しました。 3つの比較モード 本ツールは、3つのモードで資料を比較できます。 1. 画像比較 OpenSeadragonを用いた高精細画像ビューアで、2つの資料の画像を左右に並べて表示します。ズーム・パン・回転に対応し、ページ送りも可能です。 2. テキスト差分(Diff) IIIFマニフェストに含まれるテキストアノテーションを抽出し、文字単位での差分をハイライト表示します。追加箇所は緑色、削除箇所は赤色の取り消し線で表示されます。 3. 編集距離(Levenshtein Distance) レーベンシュタイン距離に基づき、行単位でのテキスト類似度を算出します。結果はネットワークグラフとして可視化され、類似度の高い行同士がエッジで結ばれます。閾値スライダーにより、表示するエッジの最低類似度を調整できます。 技術スタック カテゴリ 技術 フレームワーク Next.js(App Router / Static Export) 言語 TypeScript スタイリング Tailwind CSS v4 UIコンポーネント Radix UI 画像ビューア OpenSeadragon ネットワーク可視化 vis-network 状態管理 Zustand 国際化 next-intl(日本語 / English) 差分検出 diff アーキテクチャ データフロー IIIFマニフェストURL ↓ fetchManifest() — マニフェスト取得・パース ↓ ComparisonValue — 画像URL群、テキスト配列、メタデータ ↓ Zustand Store — アプリケーション状態として保持 ↓ 各比較コンポーネントが消費・描画 fetchManifest()関数がIIIF Presentation API v3のマニフェストをパースし、各キャンバスの画像URLとテキストアノテーションを抽出します。抽出されたデータはZustandストアに格納され、各コンポーネントがリアクティブに参照します。 ...

Drupal の GitHub Webhook モジュールを改善しました。

Drupal の GitHub Webhook モジュールを改善しました。

Drupal の管理画面から GitHub Actions をトリガーするカスタムモジュール「GitHub Webhook」を改善しました。 https://github.com/nakamura196/Drupal-module-github_webhook 元は複数リポジトリ対応の基本的なモジュールでしたが、UI のタブ分離、権限の細分化、ワークフローステータス表示、自動トリガーなどの機能を追加しています。 改善前のモジュール 元のモジュールは、以下のような構成でした。 ファイル数 : 5ファイル(info.yml、routing.yml、links.menu.yml、permissions.yml、SettingsForm.php) 対応バージョン : Drupal 10 のみ リポジトリ : 複数対応済み(AJAX で動的追加・削除) 画面 : 設定とトリガーが同一画面(アコーディオン2つ) 権限 : access github webhook settings の1権限のみ(設定もトリガーも同じ権限) トークン管理 : パスワードフィールドに #default_value を設定(HTML ソースに平文で出力される) HTTP クライアント : new \GuzzleHttp\Client() を直接インスタンス化 例外クラス : use 文なしで catch ブロックに記述(名前空間の解決が不正) // 改善前: トークンが #default_value に設定されていた $form['settings']['github_token'] = [ '#type' => 'password', '#title' => $this->t('GitHub Token'), '#default_value' => $config->get('github_token'), // HTML に平文出力される ]; // 改善前: Guzzle クライアントを直接 new していた $client = new \GuzzleHttp\Client(); 変更の全体像 改善前後のファイル構成の比較です。* は変更、+ は新規追加を示します。 ...

Netlify CLIを使って不要なサイトを一括削除する

Netlify CLIを使って不要なサイトを一括削除する

大量のNetlifyサイトが溜まってきたので、CLIを使って一括削除した際の手順をまとめます。 背景 開発やテストで作成したNetlifyサイトが41個まで増えていました。現在使用しているサイトは数個だけだったため、古いサイトをまとめて削除することにしました。 環境 macOS Node.js netlify-cli v23.15.1 手順 1. Netlify CLIのインストール npm install -g netlify-cli 2. ログイン netlify login ブラウザが開き、Netlifyの認証画面が表示されます。認証を許可するとCLIにトークンが保存されます。 3. サイト一覧の取得 netlify sites:list JSON形式で取得する場合は --json オプションを付けます。 netlify sites:list --json Python等で整形して確認すると見やすくなります。 netlify sites:list --json | python3 -c " import json, sys sites = json.load(sys.stdin) print(f'合計: {len(sites)} サイト\n') for i, s in enumerate(sites): name = s.get('name', 'N/A') url = s.get('url', 'N/A') updated = s.get('updated_at', 'N/A')[:10] site_id = s.get('id', 'N/A') print(f'{i+1:3d}. {name}') print(f' URL: {url}') print(f' 更新日: {updated} ID: {site_id}') " 4. サイトの削除 個別に削除する場合: netlify sites:delete --force <site-id> --force を付けないと確認プロンプトが表示されます。 5. シェルスクリプトで一括削除 削除対象のサイトIDと名前を配列に入れて、ループで一括削除できます。 #!/bin/bash sites=( "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx:site-name-1" "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy:site-name-2" "zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz:site-name-3" ) deleted=0 failed=0 for entry in "${sites[@]}"; do id="${entry%%:*}" name="${entry##*:}" echo -n "削除中: $name ... " if netlify sites:delete --force "$id" 2>&1; then echo "OK" ((deleted++)) else echo "FAILED" ((failed++)) fi done echo "" echo "=== 完了 ===" echo "削除成功: $deleted" echo "失敗: $failed" APIを直接使う方法 CLIを使わずにREST APIを直接呼ぶことも可能です。 ...

Drupal 10 の管理画面からモジュール更新とコアアップデートを行う

Drupal 10 の管理画面からモジュール更新とコアアップデートを行う

共用サーバー上の Drupal 10.6.1 で、Backup and Migrate によるバックアップと Automatic Updates によるモジュール・コアの自動更新を設定した手順をまとめる。 現状確認 管理画面の「レポート > サイトの状態」を確認すると、3つの警告が出ていた。 Drupal コアの更新状況:期限切れ(バージョン 10.6.3 が入手可能) PHP APCu available caching:メモリ使用量が75%超え モジュールとテーマの更新状況:期限切れ 「レポート > 利用可能なアップデート」を見ると、以下のモジュールに更新があった。 Consumers 8.x-1.22 Geofield 8.x-1.66 Geofield Map 11.1.1 Leaflet 10.3.11 Backup and Migrate のインストール 更新作業の前に、まずバックアップ手段を用意する。 composer.phar require 'drupal/backup_migrate:^5.1' vendor/bin/drush en backup_migrate vendor/bin/drush cr インストール後、「管理 > 環境設定 > 開発 > バックアップと移動」からクイックバックアップが実行できるようになる。バックアップ元に「デフォルト Drupal データベース」、バックアップ保存先に「ダウンロード」を選択して「今すぐバックアップ」を押すと、データベースのバックアップファイルがダウンロードされる。 Automatic Updates のインストール Automatic Updates モジュールを使うと、管理画面からモジュールやコアのアップデートが行える。 composer.phar require 'drupal/automatic_updates:^3.1' vendor/bin/drush en automatic_updates vendor/bin/drush en automatic_updates_extensions Composer パスの設定 有効化直後、Automatic Updates の画面にエラーが表示された。 ...

Mirador 4 で外部マニフェストのウィンドウタイトルだけを差し替える

Mirador 4 で外部マニフェストのウィンドウタイトルだけを差し替える

背景 Mirador は IIIF 対応の画像ビューアで、複数の IIIF マニフェストを並べて比較閲覧できる。複数機関が公開するマニフェストを一画面に並べて表示する際、各ウィンドウのタイトルにはマニフェストの label がそのまま表示される。 しかし、自プロジェクト独自の名称をウィンドウタイトルとして表示したいケースがある。例えば、マニフェストの label が個別の冊次情報を含む長い文字列であるのに対し、資料群を示す短い名称で表示したい場合などである。 制約:マニフェストの中身は変えてはいけない 他機関が公開している IIIF マニフェストを読み込んで表示する以上、その中身を改変して表示することは避けたい。fetch のインターセプトや Mirador 内部状態の書き換えでマニフェスト JSON の label を差し替える方法もあるが、これは実質的にマニフェストの改変にあたる。 変更すべきは Mirador が画面上に描画したウィンドウのタイトル表示(DOM)だけ であり、マニフェストのデータ自体はオリジナルのまま保持したい。 試したアプローチと結果 1. Mirador.actions.receiveManifest による内部状態の書き換え // Mirador の store を監視し、マニフェスト読み込み後に label を書き換え store.subscribe(function () { var state = store.getState(); if (manifests[manifestId] && manifests[manifestId].json && !overridden[manifestId]) { overridden[manifestId] = true; var updatedJson = JSON.parse(JSON.stringify(manifests[manifestId].json)); updatedJson.label = customTitle; store.dispatch(Mirador.actions.receiveManifest(manifestId, updatedJson)); } }); 結果:動作しない。 unpkg から配信される Mirador 4 の UMD ビルドでは Mirador.actions が undefined であり、この API は利用できなかった。 ...

Next.js 15 で発生する `localStorage.getItem is not a function` エラーの原因と対処法

Next.js 15 で発生する `localStorage.getItem is not a function` エラーの原因と対処法

Node.js 25 + Next.js 15 で発生する localStorage.getItem is not a function エラーの原因と対処法 はじめに Next.js 15 のプロジェクトで npm run dev を実行したところ、以下のエラーが発生して開発サーバーが正常に動作しなくなりました。 ⨯ [TypeError: localStorage.getItem is not a function] { digest: '2892703879' } [TypeError: localStorage.getItem is not a function] ⨯ [TypeError: localStorage.getItem is not a function] { page: '/ja' } (node:2405) Warning: `--localstorage-file` was provided without a valid path コード上で localStorage を直接呼び出している箇所はなく、原因の特定に時間がかかりました。本記事では、このエラーの根本原因と対処法を解説します。 環境 Node.js : v25.2.1 Next.js : 15.3.8 next-intl : 4.3.5 OS : macOS (Darwin 25.2.0) エラーの根本原因 このエラーは Node.js 25 で導入された Web Storage API と Next.js 15 の組み合わせで発生します。 ...

SPARQL クライアントを Apache Jena Fuseki に対応させるときにハマった 3 つのこと

SPARQL クライアントを Apache Jena Fuseki に対応させるときにハマった 3 つのこと

Virtuoso / Dydra 向けに作られた SPARQL Explorer「Snorql」を Apache Jena Fuseki でも動くようにしました。SPARQL は W3C 標準ですが、エンドポイント実装ごとの挙動差は意外と大きいです。Fuseki 対応で直面した 3 つの問題と、その解決方法を記録します。 開発環境 Docker で Fuseki を起動し、ローカルで検証しました。 # docker-compose.yml services: fuseki: image: stain/jena-fuseki container_name: fuseki ports: - "3030:3030" environment: - ADMIN_PASSWORD=admin - FUSEKI_DATASET_1=test volumes: - fuseki-data:/fuseki volumes: fuseki-data: docker compose up -d # テストデータ投入 curl -X POST 'http://localhost:3030/test/data' \ -H 'Content-Type: text/turtle' \ --data-binary @testdata.ttl 1. DESCRIBE のレスポンス形式が違う 症状 Fuseki に DESCRIBE クエリを投げると、結果が画面に表示されません。コンソールには JSON パースエラーが出ていました。 ...

Snorql — 複数の SPARQL エンドポイントを手軽に探索できるブラウザ UI を公開しました

Snorql — 複数の SPARQL エンドポイントを手軽に探索できるブラウザ UI を公開しました

Snorql — A Browser-Based UI for Exploring Multiple SPARQL Endpoints https://nakamura196.github.io/snorql/ はじめに / Introduction SPARQL エンドポイントを手軽に試せるツールが欲しい ── そう思ったことはありませんか? Have you ever wanted a quick, easy way to try out SPARQL endpoints? SPARQL は Linked Open Data (LOD) を検索するための標準クエリ言語ですが、エンドポイントごとに UI が異なったり、そもそも UI が用意されていなかったりします。そこで、1 つの統一的な UI から複数のエンドポイントを切り替えて使える ツールとして Snorql を公開しました。 SPARQL is the standard query language for searching Linked Open Data (LOD), but each endpoint often has a different UI — or none at all. To solve this, I published Snorql, a tool that lets you switch between multiple endpoints from a single, unified UI. ...

Mirador ビューア埋め込み設定

Mirador ビューア埋め込み設定

IIIF画像の表示に Mirador ビューアを使用する方法について説明します。 参考実装 埋め込み方式は、Stanford University Libraries の Stanford Digital Repository を参考にしています。書誌情報の上部にビューアを埋め込み、メタデータと画像を同一ページで閲覧できるようにしています。 ファイル構成 apps/web/ ├── public/mirador/ │ └── index.html # Mirador ビューア本体 ├── src/components/item/ │ └── MiradorViewer.tsx # 埋め込みコンポーネント └── .env.local # 環境変数設定 URLパラメータ /mirador/index.html は以下のURLパラメータを受け付けます: パラメータ 説明 例 manifest IIIFマニフェストURL(必須)。セミコロン区切りで複数指定可能 https://example.com/iiif/manifest.json embed 埋め込みモード(trueで閉じるボタンと左側メニューを非表示) true theme テーマ(dark または light) dark lang 言語コード(デフォルト: ja) ja, en canvas 初期表示するキャンバスID - annotationState アノテーション表示モード(trueでサイドバー開く) true 使用例 /mirador/index.html?manifest=https://example.com/iiif/manifest.json&embed=true&theme=dark&lang=ja Mirador設定詳細 index.html の構成 DOCTYPE html> html lang="ja"> head> meta charset="utf-8" /> meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /> title>Miradortitle> 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 src="https://unpkg.com/mirador@latest/dist/mirador.min.js">script> script> // 設定スクリプト script> body> html> URLパラメータの解析 var vars = {}; var param = location.search.substring(1).split("&"); for (var i = 0; i param.length; i++) { var keySearch = param[i].search(/=/); var key = ""; if (keySearch != -1) key = param[i].slice(0, keySearch); var val = param[i].slice(param[i].indexOf("=", 0) + 1); if (key != "") vars[key] = decodeURI(val); } ウィンドウ設定 var windows = []; if (vars["manifest"]) { var manifests = vars["manifest"]; var array = manifests.split(";"); // セミコロンで複数マニフェストを分割 for (var i = 0; i array.length; i++) { var manifest = decodeURIComponent(array[i]); var obj = { manifestId: manifest, thumbnailNavigationPosition: "far-right", // サムネイルを右端に表示 }; if (vars["canvas"]) { obj.canvasId = vars["canvas"]; // 初期表示キャンバス } windows.push(obj); } } ウィンドウ動作設定 var windowSettings = { allowClose: true, // 閉じるボタン表示 allowFullscreen: true, // 全画面ボタン表示 } // アノテーションモード if (vars["annotationState"]) { windowSettings.highlightAllAnnotations = true; windowSettings.sideBarOpen = true; windowSettings.defaultSideBarPanel = 'annotations'; } // 埋め込みモード: UIを簡素化 if (vars["embed"] === "true") { windowSettings.allowClose = false; windowSettings.allowMaximize = false; } ワークスペースコントロールパネル 左側のメニュー(マニフェスト追加、ワークスペース管理など)の表示制御: ...

Omeka SのIIIF Serverモジュールで、PLYファイルがIIIFマニフェストのitemsに出力されない問題の調査

Omeka SのIIIF Serverモジュールで、PLYファイルがIIIFマニフェストのitemsに出力されない問題の調査

概要 Omeka SのIIIF Serverモジュールで、PLYファイルがIIIFマニフェストのitemsとして出力されないが、GLBファイルは正常に出力される問題を調査しました。 前提条件:Omeka Sの設定 デフォルトでは、PLYファイルはOmeka Sにアップロードできません。以下の設定が必要です。 PLYファイルアップロード時のエラー デフォルト設定では、PLYファイルのメディアタイプ(application/octet-stream)と拡張子(.ply)が許可されていないため、アップロードエラーが発生します。 設定の追加 管理画面の「設定」→「セキュリティ」で以下を追加してください: 許可されるメディアタイプ : application/octet-stream を追加 許可されるファイル拡張子 : ply を追加 原因 PLYファイルの処理コードがモジュールに実装されていませんでした。 GLBファイルには明示的な拡張子チェックと型変換のコードが存在しますが、PLYファイルには同様のコードが存在しませんでした。 技術的詳細 GLBファイルの処理(修正前から存在) TraitMedia.php (format()メソッド) if ($mediaType === 'application/octet-stream') { $extension = strtolower(pathinfo((string) $this->resource->source(), PATHINFO_EXTENSION)); if ($extension === 'glb') { return 'model/gltf-binary'; } } IiifTypeOfMedia.php if ($mediaType === 'application/octet-stream') { $extension = strtolower(pathinfo((string) $media->source(), PATHINFO_EXTENSION)); if ($extension === 'glb') { return $mediaIiifTypes[$mediaId] = 'Model'; } } IIIFマニフェストのitems生成フロー メディアタイプの判定 (TraitMediaInfo.php) ...

3D Gaussian Splatting Viewer の開発 - Spark.jsを使ったブラウザ実装

3D Gaussian Splatting Viewer の開発 - Spark.jsを使ったブラウザ実装

3D Gaussian Splatting(3DGS)ファイルをブラウザで閲覧できるビューアを開発しました。本記事では、3DGSの概要と、通常のPLY Viewerでは表示できない理由、そして専用ビューアの実装について解説します。 デモ: https://3dtiles-viewer.vercel.app/3dgs-viewer.html?manifest=https://3dtiles-viewer.vercel.app/iiif/ply/manifest.json なぜ通常のPLY Viewerでは3DGSを表示できないのか 3DGSファイルは拡張子が.plyであるため、一見すると通常のPLYファイルと同じように扱えそうに見えます。しかし、Three.jsのPLYLoaderで読み込んでも正しく表示されません。 通常のPLYファイルと3DGS PLYファイルの違い 通常のPLY(メッシュ/点群): element vertex 1000 property float x property float y property float z property uchar red property uchar green property uchar blue element face 500 property list uchar int vertex_indices 3DGS用PLY: element vertex 142000 property float x property float y property float z property float f_dc_0 # 球面調和関数(色) property float f_dc_1 property float f_dc_2 property float opacity # 不透明度 property float scale_0 # スケール(楕円体サイズ) property float scale_1 property float scale_2 property float rot_0 # 回転(クォータニオン) property float rot_1 property float rot_2 property float rot_3 PLYLoaderで読み込むとどうなるか Three.jsのPLYLoaderは3DGS用のプロパティを理解できません。読み込むと以下のような問題が発生します: ...

3D点群データの世界:PLY、ガウシアンスプラッティング、Potreeの関係を整理する

3D点群データの世界:PLY、ガウシアンスプラッティング、Potreeの関係を整理する

! 本記事はAI(Claude)との対話をもとに構成・執筆しています。内容の正確性には注意を払っていますが、誤りや不正確な記述が含まれている可能性があります。お気づきの点があれば、コメントでご指摘いただけると助かります。 3D点群データを扱う場面が増えています。文化財のデジタルアーカイブ、都市の3Dスキャン、自動運転のためのLiDAR計測など、応用分野は広がる一方です。しかし、PLY、LAS、ガウシアンスプラッティング、Potreeといった関連する用語が多く、それぞれの位置づけや関係性が掴みにくいと感じる方も多いのではないでしょうか。 本記事では、これらの技術やフォーマットがどのようにつながっているのかを整理してみます。 点群(Point Cloud)とは 点群とは、3D空間中の点の集合によって物体や空間の形状を表現するデータ形式です。各点は最低限の位置情報(x, y, z)を持ち、それに加えて色(r, g, b)、法線ベクトル、反射強度などの属性を付与できます。 点群の取得方法は主に以下のようなものがあります。 LiDAR(Light Detection and Ranging) :レーザー光を照射し、反射光の往復時間から距離を計測します。航空測量、自動運転、地形調査などで広く利用され、1回のスキャンで数億〜数十億点のデータが得られることもあります。 SfM(Structure from Motion) :複数の写真から3D構造を復元する手法です。カメラ位置の推定と三角測量を組み合わせて密な点群を生成します。比較的安価な機材で実施でき、文化財のデジタル記録などで活用が広がっています。 3Dスキャナ :構造光やレーザーを用いて対象物の表面形状を直接計測します。建築物の現況調査や製造業での品質管理に用いられます。 取得方法によって点群の密度、精度、付随する属性が異なり、それが後段のファイル形式選択にも影響します。 PLY(Polygon File Format) 汎用的な3Dデータの器 PLYはスタンフォード大学で3Dスキャンデータの保存用に開発されたフォーマットです。正式名称は「Polygon File Format」で、もともとはポリゴン(メッシュ)データの保存を主な目的としていました。 PLYの最大の特徴は汎用性 にあります。ヘッダ部に属性の定義を記述し、データ部に頂点ごとの値を格納するシンプルな構造で、ASCII形式とバイナリ形式の両方をサポートしています。そして重要なのは、PLYは点群専用の形式ではない ということです。同じ .ply という拡張子であっても、中身はまったく異なるデータを格納できます。 PLYが格納できる3種類のデータ PLYファイルは、ヘッダの内容によって大きく3種類のデータを表現します。 ① メッシュ(Mesh) 頂点(vertex)に加えて面(face)の情報を持ちます。面は通常、三角形の頂点インデックスのリストとして記述されます。3Dスキャナの出力やCGモデルの保存に用いられます。 ply format ascii 1.0 element vertex 678866 property float x property float y property float z element face 1357728 ← faceがある → メッシュ property list uchar int vertex_indices end_header このようなファイルは135万以上の三角形で構成されたメッシュデータであり、ビューアで開くと滑らかな表面として描画されます。 ...

KAKEN OpenSearch API の使い方

KAKEN OpenSearch API の使い方

科研費データベース(KAKEN)の情報をプログラムから取得する方法を解説します。 1. はじめに KAKENは、国立情報学研究所(NII)が提供する科学研究費助成事業データベースです。OpenSearch APIを利用することで、研究課題の情報をプログラムから取得できます。 2. 事前準備:アプリケーションIDの取得 KAKEN APIを利用するには、CiNiiでアプリケーションIDを取得する必要があります。 CiNii API利用登録にアクセス 必要事項を入力して登録申請 承認後、メールでApplication ID(appid)が届く 注意 : 登録から承認まで時間がかかる場合があります。 3. APIエンドポイント 研究課題をさがす https://kaken.nii.ac.jp/opensearch/ 研究者をさがす https://nrid.nii.ac.jp/opensearch/ 4. 主要パラメータ(研究課題をさがす) パラメータ 説明 必須 例 appid アプリケーションID ○ 82RKpPlZiIjbqKwFDO3D qb 研究課題番号で検索 △ 19K20626 kw フリーワード検索 △ IIIF qa 研究課題名で検索 △ デジタルアーカイブ qg 研究者の姓名で検索 △ 中村覚 qm 研究者番号で検索 △ 80802743 format レスポンス形式 - xml(デフォルト: html5) rw 1ページの件数 - 20, 50, 100, 200, 500 lang 言語 - ja, en △: いずれか1つ以上が必要 ...

Three.js + React Three Fiber で GLTF モデルのテクスチャが荒く表示される問題と解決策

Three.js + React Three Fiber で GLTF モデルのテクスチャが荒く表示される問題と解決策

概要 React Three Fiber を使用して GLTF モデルを表示した際、テクスチャがぼやけて見える、または荒く表示される問題に遭遇しました。本記事では、その原因と解決策を解説します。 症状 GLTF モデルのテクスチャがぼやけて表示される 他の 3D ビューアでは正常に表示される同じモデルが、自作のビューアでは荒く見える dpr(デバイスピクセル比)や antialias を設定しても改善しない 原因 Three.js r152 以降、デフォルトの出力カラースペースが変更されました。 GLTF モデルのテクスチャは通常 sRGB 色空間 で保存されています。しかし、Three.js のデフォルト設定では出力が Linear 色空間 になっているため、以下の問題が発生します: テクスチャの補間処理が Linear 空間で行われる ミップマップの生成が正しい色空間で行われない 結果として、テクスチャがぼやけたり、色がおかしくなる 解決策 Canvas の onCreated コールバックで outputColorSpace を設定します。 Before(問題のあるコード) import { Canvas } from '@react-three/fiber'; <Canvas camera={{ position: [5, 5, 5], fov: 50 }} dpr={[1, 2]} gl={{ antialias: true }} > <Scene /> </Canvas> After(修正後のコード) import { Canvas } from '@react-three/fiber'; import * as THREE from 'three'; <Canvas camera={{ position: [5, 5, 5], fov: 50 }} dpr={Math.min(window.devicePixelRatio, 2)} gl={{ antialias: true, preserveDrawingBuffer: true, powerPreference: 'high-performance', toneMapping: THREE.ACESFilmicToneMapping, toneMappingExposure: 1, }} onCreated={({ gl }) => { gl.outputColorSpace = THREE.SRGBColorSpace; }} > <Scene /> </Canvas> 各設定の説明 outputColorSpace: THREE.SRGBColorSpace 最も重要な設定 。出力カラースペースを sRGB に設定することで、テクスチャが正しく表示されます。 ...

Nuxt Content + trailingSlash設定で静的ファイルへのリンクが404になる問題と解決策

Nuxt Content + trailingSlash設定で静的ファイルへのリンクが404になる問題と解決策

概要 Nuxt 3/4 + Nuxt Content の環境で trailingSlash: "append" を設定している場合、コンテンツ内のPDFや画像などの静的ファイルへのリンクが404エラーになることがあります。 発生条件 以下の条件がすべて揃った場合に発生します: Nuxt 3/4 + Nuxt Content を使用 nuxt.config.ts で trailingSlash: "append" を設定 Markdownやコンテンツ内に静的ファイル(PDF、画像など)へのリンクがある 問題の詳細 症状 コンテンツ内で以下のようなリンクを記述した場合: <a href="/uploads/document.pdf">資料をダウンロード</a> 生成されるHTMLでは、リンクが以下のように変換されてしまいます: /uploads/document.pdf/ 末尾に / が追加されるため、静的ファイルにアクセスできず404エラーになります。 原因 Nuxt Content では、Markdown内の <a> タグは ProseA コンポーネントに変換されます。 デフォルトの ProseA コンポーネント(@nuxtjs/mdc パッケージ内)は以下のような実装になっています: <template> <NuxtLink :href="props.href" :target="props.target"> <slot /> </NuxtLink> </template> NuxtLink を使用しているため、nuxt.config.ts の trailingSlash 設定の影響を受けます。 // nuxt.config.ts export default defineNuxtConfig({ experimental: { defaults: { nuxtLink: { trailingSlash: "append", // この設定が原因 }, }, }, }); 解決策 カスタムの ProseA コンポーネントを作成し、静的ファイルへのリンクの場合は通常の <a> タグを使用するようにします。 ...

IIIF Georeference ViewerへのLinked Places Format対応

IIIF Georeference ViewerへのLinked Places Format対応

概要 IIIF Georeference Viewerにおいて、地理空間データの相互運用性を向上させるため、Linked Places Format (LPF) に準拠したデータ構造をサポートしました。本記事では、LPFの概要と実装の詳細について説明します。 Linked Places Format (LPF) とは Linked Places Format は、Pelagios Network が策定した地名辞典データの相互運用フォーマットです。GeoJSONを拡張し、Linked Data (JSON-LD) の概念を取り入れることで、異なるデータセット間での場所情報の共有・連携を可能にします。 LPFの特徴 JSON-LD互換 : @id や @context を使用したセマンティックWeb対応 GeoJSON拡張 : 標準的なGeoJSON構造を維持しつつ、メタデータを追加 リンク機能 : 外部データセットへの参照を links 配列で表現 時間情報 : when プロパティによる時間的な情報の記述 公式仕様 GitHub: https://github.com/LinkedPasts/linked-places-format JSON-LD Context: https://raw.githubusercontent.com/LinkedPasts/linked-places/master/linkedplaces-context-v1.1.jsonld 従来のフォーマットとの比較 従来のフォーマット(metadata オブジェクト) { "type": "Feature", "properties": { "resourceCoords": [6690, 7517] }, "geometry": { "type": "Point", "coordinates": [139.7623182, 35.7151233] }, "metadata": { "id": "http://example.org/place/123", "label": "電気実験室", "tags": ["工学部"], "url": "https://maps.app.goo.gl/dJdXXQEA8dWSptgt8", "xywh": "5936,6344,976,1384" } } 従来のフォーマットでは、metadata オブジェクト内に全てのメタデータを格納していました。これはシンプルですが、以下の課題がありました: 標準的なフォーマットではないため、他のツールとの相互運用性が低い Linked Dataとしての活用が困難 外部リソースへのリンクの種類(同一、類似など)を表現できない 新しい推奨フォーマット(LPF準拠) { "type": "Feature", "@id": "https://example.org/places/denki-jikkenshitsu", "properties": { "resourceCoords": [6690, 7517], "title": "電気実験室", "tags": ["工学部"], "xywh": "5936,6344,976,1384" }, "links": [ { "type": "primaryTopicOf", "identifier": "https://maps.app.goo.gl/dJdXXQEA8dWSptgt8" }, { "type": "closeMatch", "identifier": "http://www.wikidata.org/entity/Q123456" } ], "geometry": { "type": "Point", "coordinates": [139.7623182, 35.7151233] } } フォーマット設計の詳細 @id の配置場所 GeoJSON標準 (RFC 7946) とLinked Places Formatでは、識別子の配置場所が異なります: ...