2025年9月4日木曜日

海面水温の変動を可視化するOracle APEXのアプリケーションを作成する

米国海洋大気庁(NOAA)物理科学研究所のサイトからダウンロードできる海面水温のデータをOracle Databaseにロードし、Oracle APEXで可視化するアプリケーションを作成してみます。

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


以下のページから海面水温のデータをダウンロードします。日本の気象庁(Japanese Mateorolgical Center)から提供されているデータセットです。今回は1850/01から2025/08の間の月次平均のデータを使用します。アプリケーションを作った後に気づきましたが、年次のデータの方が季節単位の変動がグラフに現れないので、トレンドを見るのには向いているでしょう。

上記のサイトからsst.mon.mean.ncをダウンロードします。ファイルのフォーマットはNetCDF4という形式です。


NetCDF4という形式は初めて聞きました。NetCDF4をOracle Databaseに直接ロードする方法はないので、一般的なCSVに変換する方法をClaude Sonnet 4に聞いてみました。

色々な方法が提示されましたが、その中でcdo - Climate Data OperatorsというツールでCSVに変換するのが一番簡単そうだったので、それを使うことにします。

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

brew install cdo

以下のコマンド実行例では、cdoはすでにインストール済みとレポートされています。

sst % brew install cdo

==> Downloading https://formulae.brew.sh/api/formula.jws.json

==> Downloading https://formulae.brew.sh/api/cask.jws.json

Warning: cdo 2.5.3 is already installed and up-to-date.

To reinstall 2.5.3, run:

  brew reinstall cdo

sst % 


cdoを使って、NetCDF4の海面水温のデータsst.mon.mean.ncより、日本近海のデータ(経度: 120 - 150の間、緯度: 20 - 50の間)に限定したCSVファイルjapan_region_sst.csvを出力します。

cdo outputtab,date,lon,lat,value -sellonlatbox,120,150,20,50 sst.mon.mean.nc > sst_japan_region.csv

sst % cdo outputtab,date,lon,lat,value -sellonlatbox,120,150,20,50 sst.mon.mean.nc > sst_japan_region.csv

sst %


出力されたCSVファイルの内容を確認します。先頭の列名の行頭に#(コメント)があるのと、区切り文字が空白になっていることが確認できます。

head -5 sst_japan_region.csv

sst % head -5 sst_japan_region.csv 

#      date    lon    lat    value 

 1850-01-01  120.5   49.5    1e+20 

 1850-01-01  121.5   49.5    1e+20 

 1850-01-01  122.5   49.5    1e+20 

 1850-01-01  123.5   49.5    1e+20 

sst % 


今回はデータ・ロードにSQLclloadコマンドを使用します。そのため、列名のコメントは不要です。また、空白区切りでは無くカンマ区切りにする必要があります。

エディタでファイルsst_japan_region.csvを開き、先頭の#を削除します。その後、以下のスクリプトを実行して、空白区切りをカンマ区切りに変換します。
while read date lon lat value
do
echo $date,$lon,$lat,$value
done < sst_japan_region.csv > sst_japan.csv

sst % while read date lon lat value

do

echo $date,$lon,$lat,$value

done < sst_japan_region.csv > sst_japan.csv

sst % 


出力されたファイルsst_japan.csvの内容を確認します。

head -5 sst_japan.csv

SQLclのloadコマンドでロードできるCSVの形式に変わりました。

sst % head -5 sst_japan.csv        

date,lon,lat,value

1850-01-01,120.5,49.5,1e+20

1850-01-01,121.5,49.5,1e+20

1850-01-01,122.5,49.5,1e+20

1850-01-01,123.5,49.5,1e+20

sst % 


今回は1回限りの作業なので、手作業でCSVファイルを変換しました。

CDOではPythonのモジュールとしてpython-cdoが提供されています。Pythonでコーディングするこにより、カスタマイズした出力が可能なようです。また、APEXのデータ・ロード機能であれば、列マッピングやタブ区切りもサポートしているため、このような作業は不要になるでしょう。

Oracle DatabaseにアップロードするCSVファイルをsst_japan.csvとして準備できました。

SQLclを起動して、Autonomous Databaseに作成済みのAPEXのワークスペース・スキーマに接続します。Autonomous Database以外でも同様の作業は可能です。接続先のAPEXワークスペース・スキーマはWKSP_APEXDEVとします。ワークスペース・スキーマが異なる場合は、WKSP_APEXDEVの部分を、使用しているワークスペース・スキーマに置き換えて作業を実施してください。

sql -cloudconfig   ウォレットファイル   APEXワークスペース・スキーマ@接続先TNS名

sst % sql -cloudconfig ../Wallets/Wallet_APEXDEV.zip wksp_apexdev@apexdev_low



SQLcl: 木 9月 04 11:45:52 2025のリリース25.2 Production


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


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

接続先:

Oracle Database 23ai Enterprise Edition Release 23.0.0.0.0 - for Oracle Cloud and Engineered Systems

Version 23.9.0.25.08


SQL> 


海面水温のデータを保持する表SST_DATAを作成します。以下のDDLを実行します。
create table sst_data (
    observation_date    date   not null,
    longitude           number not null,
    latitude            number not null,
    value               number not null
);

SQL> create table sst_data (

  2      observation_date    date   not null,

  3      longitude           number not null,

  4      latitude            number not null,

  5      value               number not null

  6* );


Table SST_DATAは作成されました。


SQL> 


SQLclのloadコマンドのオプションとして、日付フォーマットdate_format)、列マッピングmap_column_names)および一度にロードする行数batch_rows)を設定します。

set load date_format YYYY-MM-DD
set load map_column_names (date=OBSERVATION_DATE,lon=LONGITUDE,lat=LATITUDE,value=VALUE)
set load batch_rows 100000

SQL> set load date_format YYYY-MM-DD

SQL> set load map_column_names (date=OBSERVATION_DATE,lon=LONGITUDE,lat=LATITUDE,value=VALUE)

SQL> set load batch_rows 100000

SQL> 


設定したloadコマンドのオプションを確認します。

show load

SQL> show load

batch_rows 100000

batches_per_commit 10

clean_names transform

column_size rounded

commit on

date_format YYYY-MM-DD

errors 50

map_column_names (date=OBSERVATION_DATE, lon=LONGITUDE, value=VALUE, lat=LATITUDE)

method insert

timestamp_format 

timestamptz_format 

locale

scan_rows 100

truncate off

unknown_columns_fail on

SQL> 


ファイルsst_japan.csvを表SST_DATAにロードします。経過時間を表示するため、timingをonにしています。

set timing on
load sst_data ./sst_japan.csv


おおよそ190万行、54MBのCSVデータが34秒でアップロードできています。

SQL> set timing on

SQL> load sst_data ./sst_japan.csv


csv

column_names on

delimiter ,

enclosures ""

double

encoding UTF8

row_limit off

row_terminator default

skip_rows 0

skip_after_names


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

batch_rows 100000

batches_per_commit 10

clean_names transform

column_size rounded

commit on

date_format YYYY-MM-DD

errors 50

map_column_names (date=OBSERVATION_DATE, lon=LONGITUDE, value=VALUE, lat=LATITUDE)

method insert

timestamp_format 

timestamptz_format 

locale 日本語 日本

scan_rows 100

truncate off

unknown_columns_fail on


#INFO 処理された行数: 1,897,200

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

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

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

合計経過時間: 00:00:34.343

SQL> 


SST_DATAに主キー列IDを追加します。主キーを自動採番にしていますが、SQLclのloadコマンドでは自動採番の主キー列があるとデータをロードできません(NOT NULL列には必ず値を割り当てる必要があります)。そのため、データをアップロード後に主キー列を追加しています。

alter table sst_data add id number generated by default as identity primary key;

SQL> alter table sst_data add id number generated by default as identity primary key;


Table SST_DATAが変更されました。


経過時間: 00:01:15.300

SQL> 


海面水温の計測地点の座標は点として与えられていますが、APEXのマップ・リージョンでは矩形のヒートマップとして表示したいので、これを矩形のジオメトリに変換します。

SST_DATAに、データ型がSDO_GEOMETRYの列SQUAREを追加します。

alter table sst_data add (square sdo_geometry);

SQL> alter table sst_data add (square sdo_geometry);


Table SST_DATAが変更されました。


経過時間: 00:00:00.327

SQL> 


SST_DATAに含まれる計測地点の経度と緯度を中心として、経度および緯度の範囲が1度になる矩形を列SQUAREに保存します。
update sst_data set square = sdo_geometry(
    2003, 4326, null, sdo_elem_info_array(1,1003,3),
    sdo_ordinate_array(
        floor(longitude), floor(latitude),
        ceil(longitude),  ceil(latitude)
    )
);
commit;

SQL> update sst_data set square = sdo_geometry(

  2      2003, 4326, null, sdo_elem_info_array(1,1003,3),

  3      sdo_ordinate_array(

  4          floor(longitude), floor(latitude),

  5          ceil(longitude),  ceil(latitude)

  6      )

  7* );


1,897,200行更新しました。


経過時間: 00:01:39.085

SQL> commit;


コミットが完了しました。


経過時間: 00:00:00.220

SQL> 


以上で、APEXアプリケーションで表示するデータを表SST_DATAに準備できました。

APEXアプリケーションの作成作業に移ります。

アプリケーション・ビルダーより空のアプリケーションを作成します。

名前Sea Surface Temperatureとします。


空のアプリケーションが作成されます。今回はすべての機能をホーム・ページに実装します。

ページ・デザイナホーム・ページを開きます。


APEXのマップ・リージョン上に海面水温を表示するにあたって、表示する計測日を選択するページ・アイテムをP1_OBSERVATION_DATEとして作成します。タイプポップアップLOVラベル計測日とします。

LOVタイプSQL問合せを選択し、SQL問合せとして以下を記述します。
select observation_date d, observation_date r from sst_data 
group by observation_date order by observation_date desc
追加値の表示オフNULL表示値- 計測日を選ぶ -とします。

セッション・ステートストレージセッションごと(永続)とし、ページの再ロード後も前回の値を維持するようにします。


マップ・リージョンを作成します。名前地図タイプマップです。


レイヤー識別名前ヒートマップとします。レイヤー・タイプポリゴンを選択します。

ソースの表名にSST_DATAを指定し、WHERE句に以下を記述します。列VALUEの値は温度なので、極端に高い値は表示から除外します。

observation_date = :P1_OBSERVATION_DATE and value < 100

計測日の選択を変更したときに、動的アクションによってマップを再描画します。そのため、送信するページ・アイテムP1_OBSERVATION_DATEを設定します。

列のマッピングジオメトリ列データ型SDO_GEOMETRYを選択し、ジオメトリ列としてSQUAREを選択します。主キー列IDです。このレイヤーに表示されるのは、ジオメトリ列として設定されている列SQUAREの矩形になります。また、その矩形をクリックしたときに主キー列に設定した列IDの値が、動的アクションに渡されます。


外観カラー・スキームの使用オンにします。

カラー・スキーム相違(英語ではDiverging)、スキーム名温度カラー値列VALUEを選択します。

塗りつぶしの不透明度.5を設定し、ベースの地図が透けて見えるようにします。ストロークの色#101010とします。

ツールチップ拡張フォーマットオンにし、HTML式として以下を記述します。ツールチップに計測地点の緯度、経度、温度の実数を表示します。
経度: &LONGITUDE.<br>
緯度: &LATITUDE.<br>
温度: &VALUE.
凡例は不要なので表示オフにします。


ヒートマップの表示は以上で完了です。

選択した計測日で表示されるように、ページ・アイテムP1_OBSERVATION_DATE動的アクションを作成します。

識別名前onChange P1_OBSERVATION_DATEとします。タイミングイベントはページ・アイテムのデフォルトである変更です。


TRUEアクションとしてリフレッシュを選択し、影響を受ける要素選択タイプリージョンリージョン地図を選択します。


ページを表示するにあたって、データの出典を明示するために、ブレッドクラム・リージョンのソースHTMLコードに以下を記述します。

COBE-SST 2 and Sea Ice data provided by the NOAA PSL, Boulder, Colorado, USA, from their website at https://psl.noaa.gov


以上の設定で計測日を選択すると、その月の日本近海の海面水温が地図上に表示されるようになりました。


次に地図上で計測地点を選択し、その地点での海面水温の変動をチャートで表示します。

地図上で選択された地点を覚えておくためのページ・アイテムとしてP1_IDを作成します。タイプ非表示セッション・ステートストレージリクエストごと(メモリーのみ)を選択し、ページが再ロードされたときは、未設定になるようにします。


選択された地点を表示するレイヤーを地図に追加します。すでに作成されているレイヤーヒートマップ重複させます。

識別名前選択した地点に変更します。ソースWHERE句を以下に変更し、選択されたIDのポリゴンのみを表示対象とします。

id = :P1_ID

送信するページ・アイテムP1_IDを設定します。

外観カラー・スキームの使用オフに変更し、塗りつぶしの色#101010塗りつぶしの不透明度を設定することにより、選択された計測地点が黒一色で表示されます。

ツールチップ凡例はレイヤーヒートマップと同じ設定にします。


マップ・リージョンに動的アクションを作成し、クリックした計測地点のIDをページ・アイテムP1_IDに設定します。

作成した動的アクションの識別名前onClick Map Objectとします。タイミングイベントマップ・オブジェクトがクリックされました[マップ}を選択します。


TRUEアクションとして値の設定を選択します。設定タイプの設定JavaScript式を選び、JavaScript式this.data.idを指定します。this.data.idには、クリックした計測地点に対応する表SST_DATAの列IDの値が設定されています。

影響を受ける要素選択タイプアイテムアイテムとしてP1_IDを指定します。結果としてthis.data.idの値がページ・アイテムP1_IDに保存されます。

設定変更イベントの禁止は必ずオフにします。また、実行初期化時に実行オフにします。


ページ・アイテムP1_IDに動的アクションを作成します。値が設定されたときに地図を再描画することにより、選択された計測地点を黒く表示します。

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


TRUEアクションとしてリフレッシュを選択します。影響を受ける要素選択タイプリージョンリージョンはリフレッシュの対象であるリージョンである地図になります。


以上で、地図上で選択した計測地点が黒く表示されるようになりました。


海面水温の変動を折れ線チャートで表示するリージョンを作成します。

識別名前海面水温タイプチャートを選択します。リージョンのソースタイプSQL問合せを選択し、SQL問合せとして以下を記述します。計測した水温の実測値、1年、10年、30年の移動平均を計算しています。分析関数の部分はClaude Sonnet 4に書いてもらいました。

送信するページ・アイテムP1_IDを指定します。

地図のリージョンの右横に表示するため、レイアウト新規行の開始オフにします。


チャートの属性を設定します。

チャートタイプに折れ線を選択します。設定時間軸タイプ有効ズームとスクロールライブを選択すると、チャート単体で表示の開始時期と終了時期を変更できるようになります。初期ズームオフにします。

レイアウト高さ640ピクセルを設定します。左隣の地図のリージョンも高さが640ピクセルなので、リージョンの高さが一致します。

凡例オンにして、それぞれのシリーズの表示非表示を切り替え可能にします。


yを選択し、小数点2に変更し、小数点2桁までをY軸のメモリに表示します。ベースライン・スケール最小に変更し、Y軸が0ではなくグラフの最小値から始まるようにします。


リージョン・ソースから取得できる計測水温、1年、10年、30年の移動平均をシリーズとして作成します。

シリーズ名前はそれぞれ計測水温移動平均 - 1年移動平均 - 10年移動平均 - 30年とします。ソース位置リージョン・ソース列のマッピングラベルOBSERVATION_DATEはそれぞれ、VALUEMOVING_AVG_1YMOVING_AVG_10YMOVING_AVG_30Yを設定します。


以上で海面水温の変動を表示するチャートは完成です。

ページ・アイテムP1_IDの値が変更されたときに、この海面水温のチャートがリフレッシュされるようにTRUEアクションを追加します。


これで海面水温の変動を可視化するAPEXアプリケーションは完成です。

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

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

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