ホーム 記事一覧 ブック DH週間トピックス 検索 このサイトについて
English

最新の記事

校異源氏物語テキストDBのDTS(Distributed Text Services) APIの更新

校異源氏物語テキストDBのDTS(Distributed Text Services) APIの更新

概要 校異源氏物語テキストDBのDTS(Distributed Text Services) APIを更新したので、備忘録です。 背景 DTS(Distributed Text Services) APIは以下で説明されています。 https://distributed-text-services.github.io/specifications/ 以下の記事で、DTS APIの作成について紹介しました。 一方、以下を課題としていました。 今回開発したDTS APIも上記のガイドラインに非対応の箇所がある可能性がある点にご注意ください。 そこで、前回作成したAPIをv1とし、今回はdtsVersionの1-alphaに従ったv2のAPIを作成します。 API 以下がEntry Endpointです。v1とv2の違いは以下です。 v1 https://dts-typescript.vercel.app/api/v1/dts { "navigation": "/api/v1/dts/navigation", "@id": "/api/v1/dts", "@type": "EntryPoint", "collections": "/api/v1/dts/collections", "@context": "dts/EntryPoint.jsonld", "documents": "/api/v1/dts/document" } v2 https://dts-typescript.vercel.app/api/v2/dts { "@context": "https://distributed-text-services.github.io/specifications/context/1-alpha1.json", "dtsVersion": "1-alpha", "@id": "/api/v2/dts", "@type": "EntryPoint", "collection": "/api/v2/dts/collection{?id}", "navigation": "/api/v2/dts/navigation{?resource,ref,down}", "document": "/api/v2/dts/document{?resource,ref}" } 同様に、各種Endpointの記述を変更しています。 ビューアの改修 以下の記事で、DTSのビューア開発について紹介しました。 そして、以下を課題としていましたが、この点に対応できるように改修しました。 Navigation Endpointを使用していますが、現時点で複数階層には非対応です。 例えば、Navigation Endpointは以下のように記述します。 https://dts-typescript.vercel.app/api/v2/dts/navigation?resource=urn:kouigenjimonogatari.1&down=1 { "@context": "https://distributed-text-services.github.io/specifications/context/1-alpha1.json", "dtsVersion": "1-alpha", "@type": "Navigation", "@id": "/api/v2/dts/navigation?resource=urn:kouigenjimonogatari.1&down=1", "resource": { "@id": "urn:kouigenjimonogatari.1", "@type": "Resource", "document": "/api/v2/dts/document?resource=urn:kouigenjimonogatari.1{&ref}", "collection": "/api/v2/dts/collection?id=urn:kouigenjimonogatari.1", "navigation": "/api/v2/dts/navigation?resource=urn:kouigenjimonogatari.1{&ref}", "citationTrees": [ { "@type": "CitationTree", "citeStructure": [ { "@type": "CiteStructure", "citeType": "page", "citeStructure": [ { "@type": "CiteStructure", "citeType": "line" } ] } ] } ] }, "member": [ { "identifier": "5", "@type": "CitableUnit", "level": 1, "parent": null, "citeType": "page" }, { "identifier": "6", "@type": "CitableUnit", "level": 1, "parent": null, "citeType": "page" }, { "identifier": "7", "@type": "CitableUnit", "level": 1, "parent": null, "citeType": "page" }, { "identifier": "8", "@type": "CitableUnit", "level": 1, "parent": null, "citeType": "page" }, ... ] } 特に、CitationTreeを使って、階層を記述します。ビューアがこの情報を処理するように修正することで、以下のように、レベルごとのナビゲーションボタンが表示されるようにしました。 ...

Dockerによるディスク圧迫の調査と対処法【Ubuntu 22.04 運用事例】

Dockerによるディスク圧迫の調査と対処法【Ubuntu 22.04 運用事例】

はじめに 本記事では、Dockerコンテナやイメージによるディスク圧迫が原因でElasticsearchにエラーが発生した事例と、その調査・対処方法について記録します。同様の問題に直面している方の参考になれば幸いです。 🔍 問題の発生 運用中のElasticsearchで以下のエラーが発生しました。 { "error": { "type": "search_phase_execution_exception", "reason": "all shards failed", "phase": "query", ... }, "status": 503 } 初期調査により、インデックスが close 状態になっており、ディスク容量不足が疑われました。 📊 ディスク使用状況の調査 ルートディレクトリの使用量確認 まず、システム全体のディスク使用状況を確認しました。 sudo du -h --max-depth=1 / | sort -hr | head -n 20 実行結果: 60G / 50G /var 4.7G /usr 2.1G /home 1.2G /opt ... /var ディレクトリが50GBと異常に大きいことが判明しました。 /var ディレクトリの詳細調査 sudo du -h --max-depth=1 /var | sort -hr 実行結果: 50G /var 49G /var/lib 342M /var/log 240M /var/cache 128M /var/spool ... /var/lib がほぼ全容量を占めているため、さらに詳細を調査しました。 sudo du -h --max-depth=1 /var/lib | sort -hr 実行結果: ...

IIIF画像に対する多角形アノテーション支援ツールの改修

IIIF画像に対する多角形アノテーション支援ツールの改修

概要 IIIF画像に対する多角形アノテーション支援ツール「IIIF Annotator」の改修を行いました。具体的には、以下の2点に取り組みました。 Image Server未使用のマニフェストファイルへの対応 アノテーション付きIIIFマニフェストファイルのエクスポート機能 TEI/XMLファイルのエクスポート機能 以下、これらの改修について説明します。 背景 以下の記事で、アノテーション付与ツールを新規に作成した理由等を説明しました。 今回追加した機能は他のツールでも提供されている機能群になりますが、利便性向上のため追加実装しました。 Image Server未使用のマニフェストファイルへの対応 以下の記事で紹介しているように、IIIFマニフェストファイルにおいて、Image API(Image Server)を使用しないオプションを取ることができます。デメリットもありますが、Cantaloupe Image Serverなどのソフトウェアを導入せずに使用できる点に利点があります。 IIIF Annotatorについて、これまではImage Serverの使用を前提として実装していましたが、今回の改修により、Image Server未使用のマニフェストファイルも読み込めるようにしました。 具体的には以下のように、serviceの有無に応じて、OpenSeadragonに渡すtileSourcesを調整しました。 const tileSources = canvases .map((canvas: Canvas) => { const annotationPage = canvas.items?.[0]; const annotation = annotationPage?.items?.[0]; if (!annotation) return null; const body = annotation.body as { id: string; service?: { "@id": string }[]; }; if (body.service && body.service.length > 0) { return body.service[0]["@id"] + "/info.json"; } else { return { type: "image", url: body.id, }; } }) .filter( (tileSource: string | { type: string; url: string } | null) => tileSource !== null ); エクスポート機能 アノテーション付きIIIFマニフェストファイル、およびTEI/XMLファイルとしてエクスポートする機能を追加しました。エクスポートボタンを押すと、以下のような選択肢が表示されます。 アノテーション付きIIIFマニフェストファイルの用途としては、ダウンロードしたファイルをMirador等に読み込ませることで、アノテーション結果を確認することができます。 またTEI/XMLファイルについては、Oxygen XML Editorの作者モード等を使って、アノテーション結果を確認することができます。 まとめ IIIFのアノテーション結果の活用にあたり、参考になりましたら幸いです。

DTS (Distributed Text Services)のビューア開発

DTS (Distributed Text Services)のビューア開発

概要 DTS (Distributed Text Services)のビューアを開発したので、備忘録です。 以下のURLからお試しいただけます。 https://dts-viewer.vercel.app/ja/ 背景 DTS (Distributed Text Services)の公式ページは以下です。 https://distributed-text-services.github.io/specifications/ 以下の記事でも取り上げました。 今回、このDTS仕様に一部準拠したビューアを開発しました。 使い方 以下がトップページです。フォームにDTSのURLを入力します。ページ下部で例を提供します。技術的には、Entry pointを使用しています。 コレクションの一覧ページです。Collection Endpointを使用しています。 以下のAPIを例としています。 リンクをたどると、以下のようなリソースの一覧ページに遷移します。 ダウンロードボタンを押すと、TEI/XMLが表示されます。Document Endpointを使用しています。 ナビゲーションボタンを押すと、アクセス可能な部分テキストの一覧が表示されます。Navigation Endpointを使用していますが、現時点で複数階層には非対応です。 リンクをクリックすると、以下のような部分テキストをダウンロードすることができます。 工夫点 公式ページに以下のように記載されています。 The DTS Specification is currently in a public comment period following the 1-alpha release (機械翻訳)DTS仕様は、1-alphaリリースの後、現在パブリックコメント期間中です。 このような背景のため、既存のDTSの記述方法にばらつきがありました。そこで内部でできるだけDTS API (1.0 Draft)に変換し、その結果を可視化するようにしています。 DTS仕様が成熟するにつれ、このような問題は解決されるかと思います。 まとめ DTS仕様は以下のように説明されています。 The Distributed Text Services (DTS) Specification defines an API for working with collections of text as machine-actionable data. ...

Annotorious v2のpolygonツールを使って、polylineを作成する

Annotorious v2のpolygonツールを使って、polylineを作成する

概要 Annotorious v2のpolygonツールを使って、polylineを作成する方法の備忘録です。 背景 Annotorious v2のウェブサイトは以下です。 https://annotorious.github.io/getting-started/ 以下のように、polygonを記述することができます。 一方、同様の方法でpolylineを記述するツールは、以下のプラグインを含めて、提供されていないようでした。 https://github.com/annotorious/annotorious-v2-selector-pack カスタマイズ 以下のような多角形を作成した場合、 以下のようなJSONファイルが作成されます。 { "type": "Annotation", "body": [ { "type": "TextualBody", "value": "polygon", "purpose": "commenting" } ], "target": { "source": "https://www.e-codices.unifr.ch/loris/gau/gau-Fragment/gau-Fragment_frag001a.jp2/full/full/0/default/jpg", "selector": { "type": "SvgSelector", "value": "<svg><polygon points=\"3383.121337890625,1290.137451171875 945.135498046875,1658.426513671875 885.9696655273438,3003.352294921875 2508.54150390625,3348.424072265625 3485.021484375,2724.35791015625 2170.811767578125,2107.6337890625\" /></svg>" } }, "@context": "http://www.w3.org/ns/anno.jsonld", "id": "#c469b1a3-8902-4443-8f54-47df8bb87d7e" } 上記に対して、autoCloseのような変数を用意し、これがfalseの場合、polygonという文字列をpolylineに変更する処理を加えました。 anno.on("createAnnotation", function (selection: any) { if(!autoClose.value) { selection.target.selector.value = selection.target.selector.value.replace("polygon", "polyline"); } ... }); これにより、以下のように、polygonツールをベースとして、polygonとpolylineを使い分けることができます。 TEI/XMLでの記述 TEI/XMLでの多角形の記述例として、path要素を使用することができます。この場合、polygonであれば始点を終点の後に追加することで、多角形を表現することができます。 <facsimile> <surface ulx="0" uly="0" lrx="8176" lry="6132"> <graphic url="https://www.e-codices.unifr.ch/loris/gau/gau-Fragment/gau-Fragment_frag001a.jp2/full/full/0/default/jpg" /> <zone xml:id="layer_000" change="#ch_layer_000" n="layer_01" type="layer"> <zone xml:id="sign_layer_000_0000" change="#ch_sign_layer_000_0000" type="sign"> <!-- polygon --> <path points="1290.137451171875,3383.121337890625 1658.426513671875,945.135498046875 3003.352294921875,885.9696655273438 3348.424072265625,2508.54150390625 2724.35791015625,3485.021484375 2107.6337890625,2170.811767578125 1290.137451171875,3383.121337890625"></path> </zone> <zone xml:id="sign_layer_000_0001" change="#ch_sign_layer_000_0001" type="sign"> <!-- polyline --> <path points="1393.265625,5290.81005859375 1921.02783203125,3869.745849609375 2982.731689453125,3829.64013671875 3428.122802734375,4874.005859375 2683.244384765625,5741.7509765625 2138.024169921875,4582.19775390625"></path> </zone> </zone> </surface> </facsimile> アプリケーションによっては、始点と終点が一致しなくても閉じた図形を描くことがあるかもしれませんが、polygonとpolylineを使い分ける方法として、参考になりましたら幸いです。 ...

Elasticsearch Search UIでの初期ソート順の指定方法

Elasticsearch Search UIでの初期ソート順の指定方法

概要 本記事はAIが作成しました。 ElasticsearchとSearch UIを使って検索インターフェースを構築する際、検索結果のソート順を制御することは一般的な要件です。このガイドでは、Search UI Reactライブラリでソートを設定する方法を説明します。 参考 https://www.elastic.co/docs/reference/search-ui/api-react-search-provider#api-react-search-provider-initial-state 初期状態とソート設定の理解 Search UIライブラリでは、検索の初期状態を指定することができ、ソートの方向とソートするフィールドを含めることができます。これは、ユーザーがページに最初にアクセスしたときに、検索結果が事前に決められた順序で表示されるようにしたい場合に特に役立ちます。 基本的なソート設定の例 Search UI設定でソートを指定する方法は次のとおりです: const config = { // その他の設定オプション... initialState: { sortDirection: 'asc', sortField: 'field_tz_id', }, }; return ( <SearchProvider config={config}> {/* 検索コンポーネント */} </SearchProvider> ); ソート設定オプション initialStateオブジェクトは、ソート関連の2つのプロパティを受け付けます: sortField : ソートしたいElasticsearchインデックス内のフィールド sortDirection : ソートの方向、'asc'(昇順)または'desc'(降順) TypeScriptの型安全性 TypeScriptを使用している場合、ソート方向を定義するときにas constを使用して型安全性を確保できます: const config = { // その他の設定... initialState: { sortDirection: 'asc' as const, // 'asc' | 'desc'として型付け sortField: 'field_tz_id', }, }; これにより、sortDirectionは「asc」または「desc」のみを取り得るようになり、潜在的なエラーを防ぐことができます。 結論 Elasticsearch Search UIで初期ソートを設定する際の参考になりましたら幸いです。

Vercelにデプロイしたexpressについて、vercel.jsonによるcors対応を行う

Vercelにデプロイしたexpressについて、vercel.jsonによるcors対応を行う

概要 Vercelにデプロイしたexpressについて、vercel.jsonによるcors対応を行う方法に関する備忘録です。 背景 以下の記事で紹介したプログラムについて、cors対応を行いました。 以下を参考にしています。 https://vercel.com/guides/how-to-enable-cors 方法 対応方法は以下です。他にも方法があるかと思いますが、headersを加えることで対応することができました。 https://github.com/nakamura196/dts-typescript/commit/4c28f66b2af68950656dcb812f3e941d1b9b5feb { "version": 2, "builds": [ { "src": "src/index.ts", "use": "@vercel/node" } ], "rewrites": [ { "source": "/api/dts(.*)", "destination": "/src/index.ts" } ], "redirects": [ { "source": "/", "destination": "/api/dts", "permanent": true } ], "headers": [ { "source": "/api/(.*)", "headers": [ { "key": "Access-Control-Allow-Credentials", "value": "true" }, { "key": "Access-Control-Allow-Origin", "value": "*" }, { "key": "Access-Control-Allow-Methods", "value": "GET,OPTIONS,PATCH,DELETE,POST,PUT" }, { "key": "Access-Control-Allow-Headers", "value": "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version" } ] } ] } まとめ 参考になりましたら幸いです。

ArchivematicaのPreservation planningにおいて、Normalizationのルールを追加する

ArchivematicaのPreservation planningにおいて、Normalizationのルールを追加する

概要 ArchivematicaのPreservation planningにおいて、Normalizationのルールを追加する方法の備忘録です。 背景 拡張子が.jpgである画像をArchivematicaに投入した際、以下のようにFormatがJPEGのものに対してtifファイルを保存用に作成するルールを用意しているにもかかわらず、tifファイルが作成されないことがありました。 そこで、以下のような履歴の画面から、タスクの内容を確認しました。 結果は以下です。 具体的には以下のような記載になっており、該当するルールが存在しない、ということが記載されています。 File format: Image (Raster): Exchangeable Image File Format (Compressed): EXIF Compressed Image 2.2.1 (big-endian) (fmt/645) Not normalizing 11ecf05d-8fc6-4704-a6e9-4a26ef98f186.jpg - No rule or default rule found to normalize for preservation そこで、fmt/645に対するルールを追加します。 ルールの追加 「Create new rule」のリンクをクリックします。 そして、以下のように入力します。 今回は以下を「The related format」として指定します。 Image (Raster): Exchangeable Image File Format (Compressed): EXIF Compressed Image 2.2.1 (big-endian) (fmt/645) 結果、以下のようにルールが新規に追加され、以降、保存用のtifファイルが生成されるようになりました。 まとめ ArchivematicaのPreservation planningにおけるルール追加の一例を紹介しました。参考になりましたら幸いです。 ...

MDX.jpのオブジェクトストレージに対するIPアドレス制限の実装方法

MDX.jpのオブジェクトストレージに対するIPアドレス制限の実装方法

概要 MDX.jpのオブジェクトストレージに対するIPアドレス制限の実装方法を調べました。以下、動作確認を行なった上で、AIが記事を執筆しました。 はじめに 本記事では、MDX.jpが提供するDDN EXAScaler S3互換オブジェクトストレージサービスにおいて、特定のIPアドレスからのみアクセスを許可する設定方法について解説します。 オブジェクトストレージのセキュリティレイヤー DDN EXAScaler S3互換ストレージには、主に以下の3つのセキュリティレイヤーがあります: アクセスキーとシークレットキー :基本的な認証情報 バケットポリシー :バケットレベルでのアクセス制御 アクセス制御リスト(ACL) :オブジェクトレベルでのアクセス制御 この中で、IPアドレス制限を実装するには「バケットポリシー」を利用します。 バケットポリシーによるIPアドレス制限の設定手順 1. ポリシーJSONファイルの作成 まず、以下のようなJSONファイル(例:mdx.json)を作成します: { "Version": "2008-10-17", "Statement": [ { "Sid": "BucketName", "Effect": "Allow", "Principal": { "DDN": ["*"] }, "Action": [ "s3:ListBucket", "s3:GetObject" ], "Resource": "BucketName", "Condition": { "IpAddress": { "aws:SourceIp": [ "192.168.1.1/32", "203.0.113.0/24" ] } } } ] } ポリシーの主な要素: Version : ポリシー構文のバージョン Sid : ポリシーステートメントの識別子(任意の名前) Effect : 許可または拒否(“Allow"または"Deny”) Principal : このポリシーが適用されるユーザー(DDN EXAScalerでは"DDN"を使用) Action : 許可または拒否するアクション Resource : ポリシーが適用されるリソース(バケット名) Condition : 条件(ここでIPアドレス制限を設定) 2. ポリシーの適用 s3cmdツールを使用して、作成したポリシーをバケットに適用します: ...

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に貼り付けると、以下のように表示されます。 ...

Nuxt i18nのブラウザ言語検出を無効化する方法

Nuxt i18nのブラウザ言語検出を無効化する方法

概要 (ChatGPTによる作成) Nuxt i18nモジュールでは、ユーザーのブラウザ言語を検出して、適切な言語のページにリダイレクトする機能があります。しかし、特定の状況ではこの機能を無効化したい場合もあります。この記事では、detectBrowserLanguage: falseを使用してブラウザ言語検出を完全に無効化する方法について解説します。 https://v8.i18n.nuxtjs.org/guide/browser-language-detection 設定方法 ブラウザの言語検出機能を無効にするには、nuxt.config.jsファイルでdetectBrowserLanguageオプションをfalseに設定します。 export default defineNuxtConfig({ // その他の設定 i18n: { // 言語検出を無効化 detectBrowserLanguage: false }, // その他の設定 }) これが役立つケース 特定のURLで直接アクセスさせたい場合 : 特定のコンテンツをユーザーが直接訪れることを意図している場合、リダイレクトが邪魔になることがあります。 クロールの最適化 : 検索エンジンのクローラーが特定の言語ページに直接アクセスできるようにしたい場合。 一貫性のあるユーザーエクスペリエンス : 例えば、リンクされた言語のページ同士を行き来するユーザーに対して、予想外のリダイレクトを避けたい場合。 まとめ detectBrowserLanguage: falseを利用することで、ユーザー体験やSEO最適化のために、リダイレクトに頼らずにサイトのアクセスを制御できます。適切な状況でこの設定を利用することで、サイトのユーザビリティを向上させることができます。 ブラウザの言語設定に影響されずに、指定した言語ページをユーザーに見てもらいたいときに、この設定を試してみてください。

れきちずを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の多角形アノテーションをTEI/XMLで表現する一例

IIIFの多角形アノテーションをTEI/XMLで表現する一例

概要 IIIFの多角形アノテーションをTEI/XMLで表現する一例について紹介します。 方法 TEI/XMLでは、zoneタグとpoints属性を使用して、多角形のアノテーションを表現することができます。 https://www.tei-c.org/release/doc/tei-p5-doc/en/html/ref-teidata.point.html 例 動作確認のため、以下の記事で紹介したアノテーションツールに、TEI/XML形式でのエクスポート機能を追加しました。 具体的には、以下のようなダウンロード時のオプションを追加しました。 ダウンロード結果として得られるTEI/XMLの例は以下です。ulx, uly, lrx, lryで矩形を記述しつつ、pointsで多角形の情報を記述しています。 <?xml version="1.0" encoding="utf-8"?> <?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> Title </title> </titleStmt> <publicationStmt> <p> Publication Information </p> </publicationStmt> <sourceDesc> <p> Information about the source </p> </sourceDesc> </fileDesc> </teiHeader> <text> <body> <p> Some text here. </p> </body> </text> <facsimile sameAs="https://dl.ndl.go.jp/api/iiif/3437686/manifest.json"> <surface sameAs="https://dl.ndl.go.jp/api/iiif/3437686/canvas/1"> <graphic url="https://dl.ndl.go.jp/api/iiif/3437686/R0000001/full/full/0/default.jpg" sameAs="https://dl.ndl.go.jp/api/iiif/3437686/R0000001"/> <zone ulx="5314" uly="1983" lrx="5509" lry="2189" ana="Cを変更" points="5314,2087 5412,1984 5510,2087 5412,2190 5314,2087 5314,2087"/> <zone ulx="478" uly="307" lrx="1226" lry="3731" ana="校異源氏物語" points="478,3732 478,308 1227,308 1227,3732 478,3732"/> </surface> <surface sameAs="https://dl.ndl.go.jp/api/iiif/3437686/canvas/3"> <graphic url="https://dl.ndl.go.jp/api/iiif/3437686/R0000003/full/full/0/default.jpg" sameAs="https://dl.ndl.go.jp/api/iiif/3437686/R0000003"/> <zone ulx="2197" uly="3044" lrx="2731" lry="3573" ana="サンプル" points="2209,3045 2198,3551 2729,3575 2732,3062 2209,3045"/> <zone ulx="993" uly="3701" lrx="2849" lry="4095" ana="中央公論社蔵版" points="993,4096 993,3702 2849,3702 2849,4096 993,4096"/> </surface> </facsimile> </TEI> 以下は、Oxygen XML Editorで表示した例です。 ...

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 を設定するのがベストです。 ...

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

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]); まとめ 他にも良い方法があるかもしれませんが、参考になりましたら幸いです。 ...

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のプラグイン開発にあたり、参考になりましたら幸いです。 ...