米国海洋大気庁(NOAA)物理科学研究所のサイトからダウンロードできる海面水温のデータをOracle Databaseにロードし、Oracle APEXで可視化するアプリケーションを作成してみます。
cdo outputtab,date,lon,lat,value -sellonlatbox,120,150,20,50 sst.mon.mean.nc > sst_japan_region.csvsst % 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 %
今回はデータ・ロードに
SQLclの
loadコマンドを使用します。そのため、列名のコメントは不要です。また、空白区切りでは無くカンマ区切りにする必要があります。
エディタでファイル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、塗りつぶしの不透明度に1を設定することにより、選択された計測地点が黒一色で表示されます。
ツールチップと凡例はレイヤーヒートマップと同じ設定にします。
マップ・リージョンに動的アクションを作成し、クリックした計測地点の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、値はそれぞれ、VALUE、MOVING_AVG_1Y、MOVING_AVG_10Y、MOVING_AVG_30Yを設定します。
以上で海面水温の変動を表示するチャートは完成です。
ページ・アイテムP1_IDの値が変更されたときに、この海面水温のチャートがリフレッシュされるようにTRUEアクションを追加します。
これで海面水温の変動を可視化するAPEXアプリケーションは完成です。
アプリケーションを実行すると記事の先頭のGIF動画のように動作します。
今回作成したAPEXアプリケーションのエクスポートを以下に置きました。https://github.com/ujnak/apexapps/blob/master/exports/sea-surface-temperature.zip
Oracle APEXのアプリケーション作成の参考になれば幸いです。
完