2025年10月31日金曜日

3D Gaussian SplatsビューワーをAPEXアプリケーションに組み込む

衛星画像から3Dの都市シーンを生成するSkeyfall-GSというプロジェクトがあります。詳しい内容は理解できませんが、衛星画像と生成AIだけで3Dの都市シーンを生成するという仕組みのようです。最終的に生成するデータはPLY(Polygon File Format)であり、このPLYデータを3D Gaussian Splatsビューワーを使用して、ブラウザに表示しています。

Skyfall-GSでは3D Gaussian SplatsビューワーとしてGaussianSplats3Dを使用しています。今回はこのGaussianSplats3Dビューワーを、APEXアプリケーションに組み込んでみます。

作成したAPEXアプリケーションは以下のように動作します。Oracle Databaseと3Dビューワーを組み合わせる方法の確認のためにアプリケーションを作成していて、実用的な3Dビューワーの作成を目的とはしていません。


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

最初にクイックSQLを使用し、PLYファイルを保存する表SPLAT_FILESを作成します。クイックSQLには以下を記述します。
splat_files
    image file
レビューおよび実行を行い、ガイドに従って最終的に表SPLAT_FILESを作成します。


空のAPEXアプリケーションを作成します。名前3D Gaussian Splats Viewerとします。


アプリケーションが作成されます。表SPLAT_FILESに保存したファイルの一覧表示と、ファイルのアップロードを行なうページを作成します。

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


対話モード・レポートを選択します。


レポートのページの名前Filesとし、フォーム・ページを含めるオンにします。ファイルのダウンロードURLを生成する際にページ番号を含むページ・アイテム名を参照するため、フォーム・ページ番号は必ず3にします。フォーム・ページ名Fileとします。

データ・ソース表/ビューの名前としてSPLAT_FILESを選択し、へ進みます。


主キー列1にID(Number)を選択します。

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


対話モード・レポートとフォームのページが作成されます。

ページ・デザイナで対話モード・レポートのページを開き、列IMAGEBLOB属性を設定します。

MIMEタイプ列IMAGE_MIMETYPEファイル名列IMAGE_FILENAME最終更新列IMAGE_LASTUPD文字セット列IMAGE_CHARSETを設定します。


ページ・デザイナでフォームのページを開き、ページ・アイテムP3_IMAGEストレージを、先ほどのレポート列のBLOB属性と同様に設定します。


以上で簡単なファイル管理を行なうページが作成できました。

このページを使用して表SPLAT_FILESにアップロードするデータを準備します。

Skyfall-GSのページを開き、3DGS PLY Filesのページに移動します。


Google Driversが開くので、興味のあるPLYファイルをいくつかダウンロードします。


APEXアプリケーションを実行し、Filesのページを開きます。

作成ボタンをクリックし、ファイルをアップロードするドロワーを開きます。


Imageに先ほどダウンロードしたPLYファイルを選択し、作成をクリックします。


レポートのページに戻り、アップロードされたファイルが一覧に表示されます。


複数のPLYファイルをダウンロードしている場合、アップロードを繰り返します。


以上で表示するデータの準備ができました。

3Dビューワーはホーム・ページに実装します。ページ・デザイナでホーム・ページを開きます。

表示するファイルを選択するページ・アイテムを作成します。

識別名前P1_FILEタイプ選択リストラベルFileとします。

LOVタイプSQL問合せを選択し、SQL問合せとして以下を記述します。

select image_filename d, image_filename r from splat_files


表SPLAT_FILESからファイルを取り出すURLを保存するページ・アイテムとして、P1_URLを作成します。タイプは本来非表示にすべきですが、デバッグのためテキスト・フィールドを選択します。


選択したファイルを表示するボタンDRAWを作成します。動作アクションページの送信です。外観ホットオンテンプレート・オプションWidthStretchを指定します。


3D Gaussian Splatsビュワーの表示領域となるリージョンを作成します。

識別名前3D Gaussian Splats Viewerタイプ静的コンテンツとします。外観テンプレート・オプションRemove Body Paddingチェックを入れ、リージョンいっぱいに3D画像が表示されるようにします。

ソースHTMLコードに以下を記述します。3D Gaussian Splatsビューワーは、canvas-containerを対象として初期化します。

<div id="canvas-container"></div>


以上で、ページ上のコンポーネントの配置は完了です。

ボタンDRAWをクリックしたときに、ファイル名からダウンロードURLを生成するプロセスを作成します。

識別名前Get URLとし、ソースPL/SQLコードとして以下を記述します。

サーバー側の条件ボタン押下時DRAWを選択します。


3D Gaussian Splatsビューワーの本体の処理と言えるJavaScriptのコードを記述したファイルを、静的アプリケーション・ファイルとして作成します。ファイル名はapp.jsとし、以下の内容を記述します。


ホーム・ページに3Dビューワーを組み込みます。

ページ・プロパティJavaScriptファイルURLに以下を記述し、静的アプリケーション・ファイルのapp.jsを、ページにロードします。

[module]#APP_FILES#app#MIN#.js

CSSインラインに以下を記述し、3Dビューワーの描画領域を設定します。
#canvas-container {
    flex: 1;
    width: 100%;
    height: 800px;
    position: relative;
}
app.jsの中で参照しているthree.jsおよびgaussian-splats-3d.jsのimportmapを設定します。

HTMLヘッダーに以下を記述します。



あまり深く理由については調べていませんが、3D Gaussian Splats Viewerのライブラリが表示するデータをデータベースから読み込むと、CORSに関するエラーが発生します。

そのため、アプリケーション定義セキュリティに含まれる、ブラウザ・セキュリティセクションHTTPレスポンス・ヘッダーに、以下の2つのヘッダーを設定しています。

Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp


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

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

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

2025年10月30日木曜日

テキスト・フィールド、ポップアップLOV、1つ選択、複数選択の変更イベントでAPEXアクションを呼び出す

Oracle APEXのボタン、ラジオ・グループ、チェックボックスおよびチェックボックス・グループといったコンポーネントでは、カスタム属性のdata-actionにAPEXアクション名を指定することにより、クリック(ボタン)や変更(ラジオ・グループ、チェックボックス、チェックボックス・グループ)イベントが発生したときに、APEXアクションとして記述したJavaScriptのコードを実行できます。

この設定により動的アクションを作成せずに、静的アプリケーション・ファイルに記述したJavaScriptのコードを直接呼び出すことができます。動的アクションとしてJavaScriptのコードをページに埋め込むと、別々の場所にコードが書かれているので全体としての見通しが悪くなります。また、ページとして生成されるHTMLにJavaScriptのコードがインラインで記述されることになり、セキュリティ面の懸念もあります(Content-Security-Policyの実装が必要)。

そのため、JavaScriptのコードを実行する場合は、可能な限りdata-actionを設定するように方針を決めることもできます。しかし、残念なことにテキスト・フィールドポップアップLOV1つ選択複数選択日付ピッカーといったページ・アイテムは、data-actionの指定に対応していません。

ワークアラウンドとして、テキスト・フィールド、1つ選択、複数選択、日付ピッカーを対象に、カスタム属性data-actionに指定したAPEXアクションを呼び出すようなワークアラウンドを記述してみました。


このスクリプトを静的アプリケーション・ファイルとして導入したAPEXアプリケーションは、以下のように動作します。


APEXアプリケーションとしては動的アクションは作成せず、すべて詳細カスタム属性data-action="APEXアクション名"を設定しています。


実装のテストに使用したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/data-action-for-page-item.zip

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

2025年10月29日水曜日

日日是Oracle APEXの技術記事をChatGPT Atlasに説明してもらう

OpenAIが2025年10月21日にリリースしたブラウザ、ChatGPT Atlasを使って本ブログの記事を説明してもらいました。

ChatGPTがリリースされたころから、記事の要約や言い換えのようなことはAIがやってくれるようになることが予想されました。なので、AIのソースとして意味のある、実際に実施した手順や動作を確認したコードなどの掲載を重視して記事を書くようにしています。結果として、人が読むのはつらい技術記事になっていると思います。

ChatGPT Atlasでこのブログ記事を開き、以下の質問をしてみました。

「このページに書かれている内容で、どのようなAPEXアプリケーションが作成できるようになりますか。また、どのようなスキルが身につきますか?」


記事の著者としては、ChatGPTは少しおべっかが過ぎてこそばゆい感じはしますが、記事の原文よりもはるかに読みやすい文章を出力してくれます。これからはChatGPT Atlasのような生成AI付きブラウザを使用して、自分なりのプロンプトによる自分向けの解説を読んだ後に、記事の原文にあたる(もしくは原文は読まない)ようにすると、作業効率が上がるし速く理解もできるでしょう。

LiDARスキャナーで取得した点群データをデータベースに保存してthree.jsで表示する

iPhone 12以降のProモデルにはLiDARスキャナーが搭載されています。このLiDARスキャナーを使って取得した点群データをOracle Databaseに保存し、Three.jsを使って表示するAPEXアプリケーションを作成してみます。LiDARスキャンを実行するアプリケーションとして、Niantic Spatial社のScaniverseを使用します。

作成したアプリケーションは以下のように動作します。今回はズーム、回転および中心位置の移動に、ベルギーのUnited Codes社の製品Plug-Ins Proに含まれているUC - Range Sliderを使用しました。


ブラウザでの点群データの表示にthree.jsを使用しています。

最初はxeoglを使おうと考えていたのですが、Claudeによると「xeoglについては、ライブラリが古くなっていてCDNが利用できなくなっている可能性が高いです。xeoglプロジェクトは2017年頃から更新が停止しており、公式サイトやCDNへのアクセスができない状態になっています。」とのことでした。Claudeの回答の「公式サイトやCDNへのアクセスができない」という部分は正しいとは言えませんが、GitHubのリポジトリを確認する範囲では「xeoglプロジェクトは2017年頃から更新が停止しており」の部分は正しそうです。

そして、Claudeから次の提案がされました。「xeoglライブラリが廃止されている可能性があるため、three.jsを使った代替案を作成しましょうか?three.jsは現在も活発にメンテナンスされており、点群表示に最適です。」

Claudeの提案に従って、three.jsを使った点群データのビューワー作成してもらいました。APEXアプリケーションは、Claudeが作成したindex.htmlpointcloud.jsをもとに作成しています。私のthree.jsや3Dレンダリングに関する知見が乏しいため、その部分については、Claudeが生成したコードをほぼそのまま採用しています。

以下より、APEXアプリケーションの作成作業を記述します。

最初にiPhoneのLiDARスキャナーで、物体の点群データを取得します。

スマホでScaniverseを起動し、プラスボタンをクリックして撮影を開始します。


取得したLiDARデータは点群データとしてエクスポートするため、メッシュを選択します。


今回は小さなオブジェクトを選択して、スキャンを実施しています。作成するAPEXアプリケーションにはズーム、中心位置X、Y、Zを変更するスライダーを設置していますが、範囲が固定値で小さなオブジェクト向けになっています。ミディアムや大きなオブジェクトの場合は範囲を調整する必要があるでしょう。


LiDARスキャナーの準備が完了します。赤いボタンをクリックしてスキャンを開始します。


画面に表示されるガイドに従って、対象オブジェクトのスキャンを実行します。完了したら、スキャンを停止します。


スキャンを停止したあとに処理モードを選択します。今回はエリアを選択しました。


以上でスキャンは完了です。データを保存します。


iPhoneに保存されているLiDARデータをOracle Databaseにロードできるように、エクスポートします。

データはエクスポートする前に編集できます。とくにトリミングでは、トップフロントを指定して、周辺の不要なデータを削除できます。


共有をクリックし、モデルのエクスポートを選択します。


 モデルをエクスポートするフォーマットを選択します。今回はPLY(Polygon File Format)を選択します。ジオリファレンス(緯度経度)が必要な場合はLASを選択します。


モデルのエクスポート先を選択します。PLYファイル(後でCSVに変換します)をデータベースにロードするためにSQLclを使用します。そのため、SQLclが実行できるPC上にファイルをエクスポートします。本作業ではAirDropを選択し、Macbookにエクスポートしています。


以上で、LiDARデータの取得は完了です。

続いてエクスポートしたPLYファイルに含まれる点群データを、Oracle Databaseにロードします。

作業はmacOSで実施します。PLYファイルに含まれる点群データをCSV形式に変換するために、PDALを使用します。PDALとSQLclが使用できる環境であれば、macOSでなくても同様に作業できるでしょう。

macOSではHomebrewでPDALをインストールできます。

brew install pdal

使用したPDALのバージョンは2.9.2です。

pdal --version

% pdal --version 

---------------------------------------------------------------------------------------------------------------------------

pdal 2.9.2 (git-version: Release)

---------------------------------------------------------------------------------------------------------------------------


% 


ScaniverseからエクスポートしたファイルがScaniverse 2025-10-28 094508.plyである場合、CSVへ変換するために以下のコマンドを実行します。数値はデフォルトで小数点以下3桁なので、8桁まで増やしています。
pdal translate "Scaniverse 2025-10-28 094508.ply" output.csv \
  --writers.text.order="X,Y,Z,Red,Green,Blue" \
  --writers.text.delimiter=',' \
  --writers.text.precision=8 \
  --writers.text.write_header=false

% pdal translate "Scaniverse 2025-10-28 094508.ply" output.csv \

  --writers.text.order="X,Y,Z,Red,Green,Blue" \

  --writers.text.delimiter=',' \

  --writers.text.precision=8 \

--writers.text.write_header=false

% 


CSVの列はX、Y、Z、R、G、Bの順で並ぶようにし、ヘッダー行は出力しません。変換されたoutput.csvは、以下のようになります。

-0.05524448,0.00925601,0.04967391,130.00000000,90.00000000,66.00000000

-0.05524448,0.00988102,0.04779890,122.00000000,82.00000000,58.00000000

-0.07461950,0.05925608,0.00717390,150.00000000,94.00000000,58.00000000

-0.05461952,0.01050603,0.04779890,106.00000000,86.00000000,70.00000000

-0.07524452,0.06113100,0.00654891,150.00000000,94.00000000,58.00000000

-0.05461952,0.00988102,0.04842392,118.00000000,86.00000000,66.00000000

-0.07461950,0.05988097,0.00717390,146.00000000,90.00000000,54.00000000


このCSV形式のデータを、SQLclを使用してOracle Databaseにロードします。

SQLclでAPEXのワークスペース・スキーマに接続します。最初にロード先となる表をMYAKU_POINTSとして作成します。
drop table if exists myaku_points;
create table myaku_points(
    x number,
    y number,
    z number,
    r number,
    g number,
    b number)
nologging;

% sql wksp_apexdev@localhost/freepdb1



SQLcl: 水 10月 29 13:09:35 2025のリリース25.2 Production


Copyright (c) 1982, 2025, Oracle.  All rights reserved.


パスワード (**********?) ******

接続先:

Oracle Database 23ai Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free

Version 23.9.0.25.07


SQL> drop table if exists myaku_points;


Table MYAKU_POINTSが削除されました。


SQL> create table myaku_points(

  2      x number,

  3      y number,

  4      z number,

  5      r number,

  6      g number,

  7      b number)

  8* nologging;


Table MYAKU_POINTSは作成されました。


SQL> 


loadコマンドを使って、output.csvの内容を表MYAKU_POINTSにロードします。
set load batch_rows 10000
set loadformat column_names off
load myaku_points ./output.csv

SQL> set load batch_rows 10000

SQL> set loadformat column_names off

SQL> load myaku_points ./output.csv


csv

column_names off

delimiter ,

enclosures ""

double off

encoding UTF8

row_limit off

row_terminator default

skip_rows 0

skip_after_names


データを表にロードします WKSP_APEXDEV.MYAKU_POINTS

batch_rows 10000

batches_per_commit 10

clean_names transform

column_size rounded

commit on

date_format 

errors 50

map_column_names off

method insert

timestamp_format 

timestamptz_format 

locale

scan_rows 100

truncate off

unknown_columns_fail on


#INFO 処理された行数: 243,817

#INFO エラーのある行数: 0

#INFO 最後にコミットされたバッチで処理された最後の行: 243,817

成功: エラーなしで処理されました

SQL> exit

Oracle Database 23ai Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free

Version 23.9.0.25.07から切断されました

% 


以上でiPhoneのLiDARスキャナーを使って取得した点群データを、Oracle Databaseに保存できました。

Oracle Databaseに保存した点群データを表示する、APEXアプリケーションを作成します。

空のAPEXアプリケーションを作成します。名前LiDARデータ・ビューワーとします。


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


ページ・アイテムでズーム、回転、中心の移動を実装するため、United Codes社のプラグインUC - Range Sliderを使用します。

United Codes社のPlug-ins Proのページを開きます。


開いたページを下にスクロールし、Our Plug-insのセクションを見つけます。

UC Range Sliderはアイテム・プラグインなのでItemを選択しても見つけられますが、無償で利用できることを確実に確認するため、Free!を選択します。Free!のカテゴリ内に、UC Range Sliderを見つけることができます。

UC Range Sliderをクリックして開きます。

開いたダイアログ上にあるLogin to Downloadをクリックし、プラグインをダウンロードします。Loginとあるように、無償利用できるプラグインでも、United Codes社にユーザー登録する必要があります。


Login to Downloadをクリックすると、ログイン画面が開きます。新規にユーザー登録をする場合は、Need a United Codes Account? Sign-up now.をクリックします。

ユーザー登録の手順の説明は割愛します。


Plug-ins Proの画面にログインすると、ダッシュボードのページが開きます。検索フィールドRange Sliderと入力し、UC Range Sliderのプラグインを見つけます。


UC Range Sliderの概要がダイアログに表示されます。ダイアログ上にDownloadボタンがあります。

概要を読むと、このプラグインがnoUiSliderを使用していることが分かります。また、AuthorにFOSと記載されていますが、これはオーストリアにあったFOEX GmbHを指しています。FOEXは2022年3月にOracle Corporationによって買収されました。Oracle Corporationによる買収に際して、FOEXが開発していたFOEX Plugin FrameworkをUnited Codes社が引き継いでいます。

DownloadをクリックするとUC_Range_Slider_v25.1.zipが手元にダウンロードされます。ファイル名のバージョン番号は、将来変更されるでしょう。


UC Range Sliderのドキュメンテーションへのリンクは以下です。


ファイルUC_Range_Slider_v25.1.zipには、README.mdとSQLファイルが2つ含まれています。
  • app_unitedcodes_free_plugins.sql - サンプル・アプリケーションのエクスポート
  • item_type_plugin_uc_range_slider.sql - プラグインのエクスポート
プラグインのエクスポートはitem_type_plugin_uc_range_slider.sqlなので、このファイルをAPEXアプリケーションにインポートします。

APEXのアプリケーション・ビルダーに戻り、共有コンポーネントプラグインを開きます。


作成済みのプラグインの一覧画面から、インポートをクリックします。


インポートするファイルとしてitem_type_plugin_uc_range_slider.sqlを選択します。ファイル・タイププラグインです。

へ進みます。


ファイルのインポートが完了します。すぐにプラグインをインストールするため、へ進みます。


確認画面が開きます。プラグインのインストールをクリックします。


プラグインの一覧画面に戻ります。一覧にUC - Range Sliderが表示されます。


以上で、スライダーに使用するプラグインのインストールは完了です。

ページ・デザイナでホーム・ページを開き、コンポーネントを配置します。

ボタンやスライダーといった、操作を行うコンポーネントを配置するリージョンを作成します。識別名前Controlsタイプ静的コンテンツとします。

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

このリージョンに配置するコンポーネントから呼び出される処理は、APEXアクションとして静的アプリケーション・ファイルに記述します。APEXアクションのコンテキストをこのリージョンに作成するため、詳細静的IDとしてCONTROLSを設定します。


three.jsが点群を描画するリージョンを作成します。

識別名前Canvasタイプ静的コンテンツとします。ソースHTMLコードに以下を記述します。このDIV要素の子要素として点群を描画するキャンバス要素が作成されます。

<div id="canvas-container"></div>

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


ページ・プロパティCSSインラインに以下を記述し、リージョンのサイズを指定します。
横幅は(親となっている要素と同じ)100%、高さは1000pxとしています。
#canvas-container {
    flex: 1;
    width: 100%;
    height: 1000px;
    position: relative;
}

リージョンControlsに操作を行うコンポーネントを作成します。

データベースから点群データを読み込むボタンGENERATEを作成します。ラベル点群データの読み込みとします。

レイアウトリージョンControlsスロットPreviousを選択し、リージョンControlsの上部にボタンを配置します。外観ホットオンテンプレート・オプションWidthStretchを選択します。

ボタンをクリックしたときの処理は、静的アプリケーション・ファイルにAPEXアクションGENERATEとして記述します。そのため、動作アクション動的アクションで定義を選択し、詳細カスタム属性data-action="GENERATE"を記述します。


ページ・アイテムP1_ZOOMとして、ズームを操作するスライダーを設置します。タイプとしてUC - Range Sliderを選択します。ラベルズームです。

設定タイプNumberHandlesSingle(単一の値 - Doubleは上限と下限の範囲)、Minimum Value0Maximum Value5Number Steps0.01OrientationHorizontalを指定します。

水平のスライダーを使って、0から5の間の値を0.01刻みで設定できます。

OptionsEnable Ticks(スライダーに目盛を表示)、Enable Tooltips(ツールチップとして現在の値を表示)、Connect Handles(スライダーの色付け)にチェックを入れます。

Ticks(スライダーの範囲は0から5なので、0,1,2,3,4,5の6つの値が目盛に表示される)、Range Color#0076dfを設定し、選択された部分を青で表示します。


レイアウトリージョンControlsスロットRegion Bodyを指定します。スライダーをリージョンの左端に配置する場合は、新規行の開始オンにします。すでに存在するスライダーの右隣に配置する場合は、新規行の開始オフにします。

スライダーの場合、外観テンプレートOptional - Above(もしくはRequired - Above)を選択します。Floatingを選択するとラベルがスライダに被るため、見た目が良くありません。

ズームのステップを0.01としているため、ツールチップとして表示される数値も小数点以下2桁まで表示するように設定します。デフォルトでは小数点以下の値は表示されません。

詳細初期化JavaScriptファンクションに以下を記述します。
function(config) {
    config.numberFormat = {
        "decimals": 2
    };
    return config;
}

スライダーによって変更した値をカメラに反映させるために、動的アクションを作成します。

作成したページ・アイテムに動的アクションを作成します。タイミングイベントは標準の変更の代わりに、UC - Range Slider - Change [UC - Range Slider]を選択します。標準のページ・アイテムの変更イベントと同様に、値が変更されたときに発生するイベントになります。


スライダーの値が変更されたとき(より正確には、スライダーを動かしているときではなく、スライダーを止めて値が確定したとき)に実行するTRUEアクションとしてJavaScriptコードの実行を選択し、設定コードに以下を記述します。
apex.actions.findContextById("CONTROLS").invoke("ZOOM", this.browserEvent, this.triggeringElement);
リージョンControlsに作成したAPEXアクションZOOMを呼び出します。


以上でズームのスライダーが設定できました。

この他のスライダーも同様の手順で作成します。

ページ・アイテムP1_ROTATION_Hとして水平回転のスライダーを作成します。

設定Minimum Value0Maximum Value360Number Steps1とします。OptionsEnable TicksEnable Tooltipsにチェックを入れ、Ticks(0, 90, 180, 270, 360の5つが目盛に表示)を設定します。

数値の後ろに°(角度の単位)を追加するため、詳細初期化JavaScriptファンクションに以下を記述します。
function(config) {
    config.numberFormat = {
        "postfix": "°"
    };
    return config;
}
スライダーの値が変更されたときにAPEXアクションROTATION_Hを呼び出すように、動的アクションを作成します。


ページ・アイテムP1_ROTATION_Vとして垂直回転のスライダーを作成します。設定は水平回転のスライダーとほぼ同じですが、Minimum Value-90Maxmum Value90に設定します。値が変更されたときに呼び出されるAPEXアクションはROTATION_Vです。


ページ・アイテムP1_CENTER_Xとして、X軸の中心を移動するスライダーを作成します。ラベル中心 Xとします。

設定Minimum Value-2Maximum Value2Number Steps0.01を設定します。この値は、今回データベースにロードした点群データが見やすくなるように設定した値なので、ロードするデータに合わせて調整が必要かもしれません。ロードしたデータの範囲を確認して動的に変更することも可能ですが、今回はそこまでの実装はしていません。

ツールチップに小数点以下2桁まで表示するため、詳細初期化JavaScriptファンクションズームと同じスクリプトを記述します。値が変更されたときに呼び出されるAPEXアクションはCENTER_Xです。


ページ・アイテムP1_CENTER_Yとして、Y軸の中心を移動するスライダーを作成します。ラベル中心 Yとします。設定はX軸の中心を移動するスライダーとほぼ同じです。値が変更されたときに呼び出されるAPEXアクションはCENTER_Yです。


ページ・アイテムP1_CENTER_Zとして、Z軸の中心を移動するスライダーを作成します。ラベル中心 Zとします。設定はX軸の中心を移動するスライダーとほぼ同じです。値が変更されたときに呼び出されるAPEXアクションはCENTER_Zです。


以上で、点群データの表示をコントロールするボタンとスライダーの配置は完了です。

この時点でAPEXアプリケーションを実行すると、ホーム・ページは以下のように表示されます。


APEXアプリケーションに、実際の処理を実装します。

データベースから点群データを取り出すAjaxコールバックを作成します。

識別名前GET_POINTSソースPL/SQLコードに以下を記述します。


このAjaxコールバックは、ボタンGENERATEをクリックしたときに呼び出されるAPEXアクションGENERATEの中から呼び出されます。

点群データに加えて、X,Y,Z方向の最小値、最大値も属性extentとして返却しているため、これらの値を使ってスライダーの範囲を動的に変える実装も可能でしょう。


点群データの表示を実行するJavaScriptコードを記述した、静的アプリケーション・ファイルを作成します。

ファイル名はpointcloud.jsとします。記述するコードは以下です。



作成した静的アプリケーション・ファイルpointcloud.jsを、ホーム・ページで実行されるように設定します。

three.jsをJavaScriptから参照しやすくなるように、ページ・プロパティHTMLヘッダーにimportmapを定義します。
<script type="importmap">
  {
    "imports": {
      "three": "https://cdn.jsdelivr.net/npm/three@0.180.0/build/three.module.js",
      "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.180.0/examples/jsm/"
    }
  }
</script>

JavaScriptファイルURLに、静的アプリケーション・ファイルpointcloud.jsを参照するように以下を記述します。

[module]#APP_FILES#pointcloud#MIN#.js


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

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

点群データを表示するためにScaniverseで取得したPLYファイルをCSVに変換してデータベースにロードしていますが、その変換やthree.jsでの表示が適切かどうか確認するためにPLYファイルをインポートして表示できるMeshLabを使用しました。

MeshLabによる描画とthree.jsでの描画を比較することにより、APEXアプリケーションの動作を確認できます。


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

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