2025年9月19日金曜日

NSIDCからダウンロードできる北極と南極の海氷データを地図上に表示する

NSIDC - National Snow and Ice Data Centerからダウンロードできる北極と南極の海氷のデータから、地図上に海氷を表示するAPEXアプリケーションを作成します。

海氷の表示に使用するデータは、Sea Ice Index, Version 4です。

作成したAPEXアプリケーションは以下のように動作します。


APEXに標準で添付されているマップ・リージョンは、MapLibreをライブラリとして使っています。MapLibreは標準では極座標マップをサポートしていない(プラグインを組み込むとできるという情報はありますが、未確認です)ので、今回はOpenLayersで描画することにしました。

Web画面に描画する部分はほとんどClaudeに作ってもらいました。Claudeが作ってくれたアプリは、以下のような画面です。JavaScriptが<script>タグに書かれているHTMLファイルです。


地図上に描画するたびに海氷の日次データをNSIDCからダウンロードするのも良くないので、一度ダウンロードしたデータはデータベースに保存し、その後のアクセスではデータベースに保存されているデータを使うようにします。また、ブラウザから直接NSIDCにアクセスしてファイルをダウンロードしようとすると、CORSの制限にかかります。APEXアプリケーションの場合、データベースからデータをダウンロードする限り、CORSの制限はかかりません。

海氷データを保存する表をNOAA_G02135として作成します。


極と日付を指定してNSIDCから海氷データを取得するためのパッケージをNOAA_G02134_PKGとして作成します。

パッケージ定義部です。


パッケージ本体です。


これらのオブジェクトをデータベースに作成しておきます。

空のAPEXアプリケーションをNSIDC Sea Ice Indexとして作成します。デフォルトで作成されるホーム・ページに、すべての機能を実装します。

ホーム・ページのJavaScriptファイルURLに、使用するJavaScriptのライブラリを設定します。静的アプリケーション・ファイルのopenlayers_geotiff_polar.jsは、後ほど作成します。
https://cdn.jsdelivr.net/npm/proj4@2.19.10/dist/proj4.min.js
https://cdn.jsdelivr.net/npm/ol-proj@1.1.0/dist/js/proj4.min.js
https://cdn.jsdelivr.net/npm/geotiff@2.1.3/dist-browser/geotiff.js
https://cdn.jsdelivr.net/npm/ol@10.6.1/dist/ol.js
#APP_FILES#openlayers_geotiff_polar#MIN#.js
ページ・ロード時に実行に以下を記述します。ファンクションinitializeMapはopenlayers_geotiff_polar.jsで定義しています。

initializeMap();

CSSファイルURLに、OpenLayersが必要とするCSSファイルを設定します。
https://cdn.jsdelivr.net/npm/ol@10.6.1/ol.min.css
CSSインラインに、地図の表示を調整するスタイルを定義します。
html,body,#map{margin:0;height:100%}
#map {
  background-color: #1e3a5f; /* 深い海の青 */
}


地図を操作するボタンや表示する海氷の日付を決めるページ・アイテムは、テンプレートItem Container静的コンテンツのリージョンに配置します。

ボタンをクリックしたときの処理はAPEXアクションとして実装します。このリージョンをapex.actions.createContextを実行してAPEXアクションを保持する対象にするため、静的IDとしてCONTROLSを設定します。


ボタンNORTHSOUTHICE動作アクション動的アクションで定義に設定し、詳細カスタム属性data-action="ボタン名"を設定します。

data-actionの定義によって呼び出されるAPEXアクションは、静的アプリケーション・ファイルopenlayers_geotiff_polar.jsに記述しています。


表示する海氷の日付はページ・アイテムP1_OBSERVATION_DATEで選択します。タイプは日付ピッカーです。


OpenLayersによる地図を表示するリージョンを作成します。タイプ静的コンテンツです。

ソースHTMLコードとして以下を記述します。

<div id="map"></div>

OpenLayersを初期化するにあたって、このDIV要素の大きさが決まっている必要があります。外観テンプレート・オプションBody Height640pxを設定しておきます。


データベースから海氷のGeoTIFFデータを取り出すために、Ajaxコールバックとしてプロセスを作成します。プロセスの名前GET_BLOBとします。

ソースPL/SQLコードとして以下を記述します。



最後に静的アプリケーション・ファイルとしてopenlayers_geotiff_polar.jsを作成します。


openlayers_geotiff_polar.jsの内容は以下です。

これでアプリケーションを実行すると、記事の先頭にあるGIF動画のように動作します。

作成したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/openlayers-nsidc-noaa-sea-ice-index.zip

今回の記事は以上になります。

Oracle APEXのアプリケーション作成の参考になれば幸いです。

2025年9月18日木曜日

p5.jsのコードを実行するAPEXアプリケーションを作成する

ブラウザ上で手軽に図形描画やアニメーションを実現できるJavaScriptライブラリとして、p5.jsがあります。p5.jsを使って図形を描くときはJavaScriptでコードを書きます。

本記事ではp5.jsのJavaScriptのコードを、REPL(Read-Eval-Print Loop)風に実行するAPEXアプリケーションを作成します。JavaScriptコードの入力には、Ronny Weissさん作成のリージョン・プラグインAPEX-Monaco-Editor-and-Diff-Viewerを使用します。

作成したAPEXアプリケーションは以下のように動作します。


以下よりAPEXアプリケーションの作成手順を紹介します。

最初にクイックSQLの以下のモデルより表P5_SCRIPTSを作成します。この表に図形やアニメーションを描画するJavaScriptのコードを保存します。
# pk: identity
p5_scripts
    title  vc80 /nn
    script clob;

空のAPEXアプリケーションを作成します。名前p5.jsとします。


図形を描画するコードの入力はホーム・ページで行い、そのコードを実行して図形やアニメーションを描画するのは、非モーダル・ダイアログで行うことにします。

図形を描画するページを作成します。ページの作成をクリックします。


空白ページを選択します。


新規に作成するページ番号2名前Sketchとします。このページは非モーダル・ダイアログにする予定ですが、ページ・モードの選択肢に非モーダル・ダイアログはありません。形式が一番近いモーダル・ダイアログを選択し、ページ作成後にモードを変更します。

ページの作成をクリックします。


新規にページが作成されます。

外観ページ・モード非モーダル・ダイアログに変更します。ダイアログ800高さ800としています。この値は便宜的なものなので、それぞれ、使い勝手のよい値を設定します。必須ではありませんが、チェーンオフにしています。


JavaScriptファイルURLに以下を設定します。p5.jsのライブラリをAPEXのページにロードします。

https://cdn.jsdelivr.net/npm/p5@2.0.5/lib/p5.min.js


スクリプトが保存されている表P5_SCRIPTSの列IDの値を保持するページ・アイテムを、P2_IDとして作成します。ページ・アイテムのタイプ非表示です。


P5_SCRIPTSに保存されているJavaScriptのコードを保持するページ・アイテムを、P2_CODEとして作成します。こちらもタイプ非表示です。

セッション・ステートデータ型CLOBストレージリクエストごと(メモリーのみ)を選択します。


ダイアログが開いたときに、P2_IDの値を主キーとして表P5_SCRIPTSよりJavaScriptコード(列SCRIPTの値)を取り出し、P2_CODEに設定するプロセスを作成します。

一般的にはフォームを作成して、そのソースを元にフォームの初期化プロセスを実行することにより、フォームに紐づくページ・アイテムに値を設定します。今回は初期化するページ・アイテムはP2_CODEのみなので、フォームの作成は省略しています。

プロセスのソースPL/SQLコードに以下を記述します。

select script into :P2_CODE from p5_scripts where id = :P2_ID;


JavaScriptのコードを実行し、p5.jsによる図形やアニメーションを描画するリージョンを作成します。

名前Sketchタイプ静的コンテンツを選択します。

ソースHTMLコードに以下を記述します。


外観テンプレートに装飾の少ないBlank with Attributes (No Grid)を選択します。


以上で、図形やアニメーションを描画する非モーダル・ダイアログのページができました。

続いて、コードを記述する機能をホーム・ページに実装します。

APEX-Monaco-Editor-and-Diff-Viewerのページより、region_type_plugin_rw_apex_vs_monaco_editor.sqlをダウンロードします。


共有コンポーネントプラグインを開きます。


プラグインの一覧画面よりインポートをクリックします。


インポートするファイルとして、先ほどダウンロードしたregion_type_plugin_rw_apex_vs_monaco_editor.sqlを選択します。プラグインよりインポート画面を開いているため、ファイル・タイププラグインが選択済みです。

へ進みます。


プラグインをインストールします。へ進みます。


プラグインのインストールを実行します。インストールするアプリケーションには、インポート処理を開始したアプリケーションが設定されています。


Monaco EditorのJavaScriptライブラリの参照先となるCDNのURLを確認します。Monaco Editorの最新版は0.53.0のようですが、0.53.0に置き換えるとプラグインが動作しないので、このBase URLは変更しない方が良いでしょう。

https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.51.0

変更の適用をクリックします。


以上でプラグインのインストールは完了です。

ホーム・ページにコード・エディタを組み込みます。

非モーダル・ダイアログを開くボタンOPEN_SKETCHを作成します。

ボタン名OPEN_SKETCHラベルOpen Sketchとします。非モーダル・ダイアログのページを開くために、動作アクションこのアプリケーションのページにリダイレクトを選択し、ターゲットページ2を設定します。


一般的にページを開く場合は、遷移先のページに主キーの値を引き継ぎます。今回の実装を例にとると、リンク・ビルダーアイテムの設定名前P2_ID&P1_ID.といった設定を行います。

この設定は行なっていません。&P1_ID.ですが、ボタンのアクションリダイレクトの場合、ホーム・ページが開いた時点で設定されているP1_IDの値で置換されます。ページ上の操作によってP1_IDの値が変更されても、リダイレクト時に渡される値には反映されません。

今回はホーム・ページが表示された後にページ・アイテムP1_IDの値を変更するため、変更されたP1_IDの値でページ・アイテムP2_IDを直接更新することにより、リンクの引数による値の引き渡しを省略しました。


P5_SCRIPTS内で、更新の対象としているスクリプトのIDを保持するページ・アイテムとして、P1_IDを作成します。タイプポップアップLOVLOVタイプSQL問合せを選択し、SQL問合せとして以下を記述します。

select title d, id r from p5_scripts order by r asc

追加値の表示オフにします。


新規に登録するスクリプトのタイトルを入力するページ・アイテムとして、P1_TITLEを作成します。タイプはテキスト・フィールドです。

ラベルTitleセッション・ステートストレージリクエストごと(メモリーのみ)を設定します。


P5_SCRIPTSに新規行を保存するボタンとして、CREATEを作成します。P1_TITLEを列TITLEに保存し、自動採番されたIDP1_IDに戻します。列SCRIPTに保存するスクリプトはスクリプト・エディタから保存します。そのため、ボタンCREATEではスクリプトを保存しません。

動作アクションはデフォルトのページの送信です。


ボタンCREATEをクリックしたときに実行されるプロセスを作成します。名前Createとします。

ソースPL/SQLコードとして以下を記述します。

insert into p5_scripts(title) values(:P1_TITLE) returning id into :P1_ID;

サーバー側の条件ボタン押下時CREATEを設定します。


コード・エディタのリージョンを作成します。

名前Editorとし、タイプにプラグインのAPEX-VS-Monaco-Editorを選択します。プラグインのソースSQL問合せとして以下を記述します。


外観テンプレートに装飾の少ないBlank with Attributesを選択します。


属性を開き、ButtonsSwitch between Single Editor and Diff-Editorのチェックを外します。

Execute on Saveに以下を記述します。
begin
    update p5_scripts set script = :CLOB where id = :P1_ID;
end;

この時点でアプリケーションを実行すると、新規スクリプトの作成ができます。

下段のページ・アイテムにTitleを入力してボタンCreateをクリックすると、表P5_SCRIPTSに1行追加されます。


コード・エディタの保存アイコンをクリックすると、上段のTitleで選択されているスクリプトとして保存されます。

ページ・アイテムP1_IDに動的アクションを作成して、選択したIDのスクリプトがコード・エディタにロードされるようにします。

作成する動的アクションの名前onChange P1_IDとします。タイミングイベントはページ・アイテムのデフォルトである変更です。


ページ・アイテムP1_IDの値が変更されたときにページ・アイテムP2_IDに同じ値を設定するよう、TRUEアクションとしてサーバー側のコードを実行します。

設定PL/SQLコードとして以下を記述します。
begin
    apex_session_state.set_value('P2_ID', :P1_ID);
end;
P1_IDが変更したときに限らず、ページが表示された時点でのP1_IDの値もP2_IDに保存するため、実行初期化時に実行オンにします。


変更されたP1_IDのスクリプトを読み込むため、コード・エディタのリージョンであるEditorを、TRUEアクションでリフレッシュします。


以上でポップアップLOVで選択したスクリプトが、コード・エディタに表示されるようになりました。また、選択されたスクリプトがコード・エディタでの保存先になります。

選択されたスクリプトを表P5_SCRIPTSより削除するボタンとして、DELETEを作成します。

動作アクションページの送信です。削除は注意が必要な処理なので、動作確認の要求オンにします。確認メッセージに「削除しますか?」を記述し、スタイル警告を設定します。


ボタンDELETEをクリックしたときに実行されるプロセスを作成します。名前はDeleteとします。

ソースPL/SQLコードに以下を記述します。

delete from p5_scripts where id = :P1_ID;

サーバー側の条件ボタン押下時DELETEを設定します。


以上で、アプリケーションの動作に関しては実装が完了しました。

少し見栄えを調整します。

静的コンテンツのリージョンとしてSelect Scriptを作成し、外観テンプレートItem Containerを選択します。そのリージョンのItemのスロットにページ・アイテムP1_IDButton EndのスロットにボタンDELETEを配置します。


同様に静的コンテンツのリージョンとしてNew Scriptを作成し、そのリージョンのItemのスロットにページ・アイテムP1_TITLEButton EndのスロットにボタンCREATEを配置します。New ScriptはSelect Scriptの右隣に配置するため、レイアウトの新規行の開始はオフにしします。


以上でアプリケーションは完成です。実行すると記事の先頭のGIF動画のように動作します。

今回作成したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/sample-repl-p5-js.zip

Oracle APEXのアプリケーション作成の参考になれば幸いです。

2025年9月17日水曜日

オラクルのドキュメントをGoogleのNotebookLMに解説してもらう

オラクルに限らないとは思いますが、公式ドキュメントが読みにくいと感じます。GoogleのNotebookLMはベータの頃に使ってみたことはあるのですが、まだまだ、という印象でした。ただ、使ってみたのは1年も前ですし、DeNAの南場さんがNotebookLMを使い倒しているという記事を読んでいたのと、個人でGoogle AI Proも契約しているのでNotebookLMに公式ドキュメントの解説をお願いしました。

オラクルの公式ドキュメントはHelp Center(docs.oracle.com)から探すことができます。NotebookLMは英語のドキュメントでも日本語で解説してくれるので、ノートブックのソースとなるドキュメントとして英語のドキュメントを使用します。

Oracle APEXのドキュメントはカテゴリDatabaseの下にあります。


Release 24.2 Documentationのリンクを開きます。


Oracle APEX 24.2のドキュメントが表示されます。

NotebookLMのソースとしてアップロードするドキュメントは、PDF形式だと扱いやすいです。また、PDF形式のオラクルの公式ドキュメントは、(人が見た感じですが)良い感じでフォーマットされています。

PDFをダウンロードするために、Booksのリンクを開きます。


それぞれのドキュメントのへのリンクの右端に、PDFのアイコンが表示されています。このPDFのアイコンをクリックすると、ドキュメントをPDFとしてダウンロードできます。

試しにOracle APEX App Builder User's Guideをダウンロードします。

oracle-apex-app-builder-users-guide.pdfが手元にダウンロードされます。


GoogleのNotebookLMを開きます。

ノートブックを新規作成をクリックします。


ファイルを選択してアップロードをクリックし、先ほどダウンロードしたoracle-apex-app-builder-users-guide.pdfを選択します。


ファイルを選択すると、ソースのロードが開始します。


数分でロードが完了します。

あとは、ソースに質問をしたり、音声解説を生成したり、動画解説を生成したり、レポートを生成したりできます。


驚いたことに、レポートにしろ音声解説にしろ、すごくクオリティが高いです。さすがに動画解説はスクリーンショットの実物が生成できないため、いまいちでしたが、音声の説明とシナリオについては(すこし大袈裟に褒めるところは気になりますが)、少し手直しすれば使えそうなクオリティです。

最近オラクルからAI Data Platformという製品がリリースされました。ドキュメントはOracle Cloud Infrastructureのカテゴリ以下にあります。


オラクルの製品ドキュメントでは大抵Booksというリンクがあり、そこからPDF形式のドキュメントがダウンロードできますが、AI Data PlatformはなぜかBooksのリンクがありません。その代わりにGuidesがあるので、それをクリックします。


Using Oracle AI Data PlatformをPDF形式でダウンロードします。

using-oracle-ai-data-platform.pdfがダウンロードされます。


後はOracle APEXのドキュメントと同様に、ノートブックを新規作成してPDFをロードします。


色々とすごいのですが、特に音声解説のクオリティは驚異的でした。

音声解説の鉛筆アイコンをクリックすると、解説をカスタマイズできます。


解説の形式として詳細または議論を選択すると、男性と女性の声の2人のホストによる掛け合いになり、聞いていて退屈しません。ソースの量に依存するかもしれませんが、デフォルトで20分程度の音声解説が生成されました。

音声解説の生成には5から10分くらいかかりました。


読みやすいとは言いにくいオラクルの公式ドキュメントですが、書籍に近いPDFのドキュメントがあることで、GoogleのNotebookLMを活用できます。

今回の記事は以上になります。