2025年10月2日木曜日

東京都の浸水予想区域図をAPEXのマップに表示する

東京都オープンデータカタログサイト(こちら)を見ると、2025/9/25から2025/10/01の期間でのアクセスランキングの一位が浸水予想区域図でした。このオープンデータに含まれる浸水深をAPEXのマップに表示してみます。

以下が作成したAPEXアプリケーションの表示です。神田川の流域で、練馬区の一部を表示しています。


表示するデータとして「神田川流域浸水予想区域図(改訂)浸水深・地盤高データ」を使用します。ファイルはCSV形式で、shinsui_kandagawa.csvというファイル名でダウンロードされます。


最初に浸水深のデータを保存する表TOKYO_INUNDATION_POINTSを作成します。

以下のCREATE TABLE文を実行します。主キー列がありませんが、これはSQLclのLOADコマンドでデータを投入するためです。
create table tokyo_inundation_points(
    figuire_no    number(5)     not null,
    flood_depth   number(10,6)  not null,
    ground_height number(10,3)  not null,
    latitude      number(12,8)  not null,
    longitude     number(12,8)  not null
);
SQLclでAPEXのワークスペース・スキーマに接続し、上記のDDLを実行します。

SQL> create table tokyo_inundation_points(

  2      figuire_no    number(5)     not null,

  3      flood_depth   number(10,6)  not null,

  4      ground_height number(10,3)  not null,

  5      latitude      number(12,8)  not null,

  6      longitude     number(12,8)  not null,

  7      geom          sdo_geometry  not null

  8* );


Table TOKYO_INUNDATION_POINTSは作成されました。


SQL> 


SQLclのLOADコマンドのオプションを設定します。アップロードするファイルの形式はCSV、ヘッダー行は日本語なのでスキップして、データの位置によって保存される列を決めます。CSVには100万ポイントを超えるデータが含まれているので、batches_per_commitを1000とします。
set loadformat csv
set loadformat skip_rows 1
set loadformat column_names off
set load batches_per_commit 1000

SQL> set loadformat csv

SQL> set loadformat skip_rows 1

SQL> set loadformat column_names off

SQL> set load batches_per_commit 1000

SQL> 


ファイルshinsui_kandagawa.csvを表TOKYO_INUNDATION_POINTSにロードします。

load tokyo_inundation_points shinsui_kandagawa.csv

1行エラーが発生しますが、これはファイルの末尾の空白行(not null制約違反となる)なので無視できます。

SQL> load tokyo_inundation_points shinsui_kandagawa.csv


csv

column_names off

delimiter ,

enclosures ""

double off

encoding UTF8

row_limit off

row_terminator default

skip_rows 1

skip_after_names


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

batch_rows 50

batches_per_commit 1000

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


#ERROR  1099859 を介したバッチ行 1099851 の挿入に失敗しました

#ERROR ORA-01400: ("WKSP_APEXDEV"."TOKYO_INUNDATION_POINTS"."FIGUIRE_NO")にはNULLは挿入できません。


https://docs.oracle.com/error-help/db/ora-01400

#ERROR 行1,099,859データは次のとおりです:

,,,,

#INFO 処理された行数: 1,099,859

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

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

警告: エラーありで処理されました

SQL> 


データの投入は以上で完了です。

これからの作業のために、列LONGITUDELATITUDEのデータからSDO_GEOMETRYのポイント・データを生成し、列GEOMに保存します。その後、列GEOMに空間索引を作成します。

alter table tokyo_inundation_points add (geom sdo_geometry);

SQL> alter table tokyo_inundation_points add (geom sdo_geometry);


Table TOKYO_INUNDATION_POINTSが変更されました。


SQL> 


SDO_GEOMETRYのポイント・データを生成します。APEX_SPATIAL.POINTを呼び出します。

update tokyo_inundation_points set geom = apex_spatial.point(longitude,latitude);
commit;


SQL> update tokyo_inundation_points set geom = apex_spatial.point(longitude,latitude);


1,099,858行更新しました。


SQL> commit;


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


SQL> 


列GEOMに空間索引を作成します。

GEOMのメタデータを登録します。
insert into user_sdo_geom_metadata (table_name, column_name, diminfo, srid)
values ('TOKYO_INUNDATION_POINTS', 'GEOM',
    SDO_DIM_ARRAY(
        SDO_DIM_ELEMENT('LONGITUDE', -180, 180, 0.0001),
        SDO_DIM_ELEMENT('LATITUDE',  -90,  90,  0.0001)
    ),
4326);
commit;
空間索引を作成します。
create index TOKYO_INUNDATION_POINTS_SIDX on TOKYO_INUNDATION_POINTS(GEOM)
indextype is mdsys.spatial_index;

SQL> create index TOKYO_INUNDATION_POINTS_SIDX on TOKYO_INUNDATION_POINTS(GEOM)

  2*   indextype is mdsys.spatial_index;


Index TOKYO_INUNDATION_POINTS_SIDXは作成されました。


SQL> 


以上で浸水深のデータを使用する準備ができました。

Oracle APEXのアプリケーションを作成し、マップ上に浸水深を表示します。

作成するアプリケーションの名前東京都浸水予想区域図とします。


アプリケーションが作成されたら、ページの作成を実行しマップのページを作成します。


マップを選択します。


ページの名前浸水予想区域図とします。データ・ソースSQL SELECT文を入力に以下を記述します。表TOKYO_INUNDATION_POINTSには100万行のデータが保存されていて、条件なしで検索するとマップの表示でエラーが発生します。

今回はファセット検索などの条件を付けたりせずに、最初から表示する領域を練馬区の一部に限定します。
SELECT
    flood_depth,
    geom
FROM tokyo_inundation_points t
WHERE SDO_RELATE(
    t.geom,
    SDO_GEOMETRY(
        2003,  -- 2次元ポリゴン
        4326,  -- WGS84
        NULL,
        SDO_ELEM_INFO_ARRAY(1, 1003, 3),
        SDO_ORDINATE_ARRAY(
            139.64759444, 35.72404722,  -- 左下
            139.67415833, 35.74030833   -- 右上
        )
    ),
    'mask=INSIDE'  -- 矩形内に完全に含まれるもののみ
) = 'TRUE'
AND flood_depth > 0
へ進みます。


マップ・スタイルヒート・マップを選択します。

マップ属性ジオメトリ列タイプジオメトリ列を選び、ジオメトリ列GEOMを設定します。カラー値列FLOOD_DEPTHを設定します。

以上でページの作成を実行します。


マップのページが作成されました。ページを実行して作成されたマップを確認します。


以下のようにマップが表示されます。概ね危険な所がどこかは分かります。


レイヤーの外観スキーム名ダーク・ミントに変更し、水に関連した色で表示されるようにします。


表示色が変更されます。


表示色が濃い部分は概ね河川です。


レイヤーの表示を解除すると、河川を確認できます。


上記はマップ背景として、デフォルトのOpenStreetMapを選択しています。この背景は主に道路や建物が表示されます。あまり地形がわかるような背景ではありません。

地形がわかるような背景を、同じく東京都が公開している東京都デジタルツイン実現プロジェクトの区部点群データから作成してみます。




リソースを開き、ダウンロードするメッシュを選択します。新しいタブとしてアンケート・ページも開きそのタブに移動するので、アンケートに回答して元のタブに移動します。


浸水深を表示するSELECT文の条件とした矩形に一致するメッシュを選択し、ダウンロードします(実際はメッシュを先にダウンロードし、そのメッシュから矩形の座標を取り出してSELECT文に渡しています)。

以下の36のファイルがダウンロードされます。
09KD9568.zip	09KD9660.zip	09KD9680.zip	09LD0508.zip	09LD0610.zip
09KD9569.zip	09KD9661.zip	09KD9681.zip	09LD0509.zip	09LD0611.zip
09KD9578.zip	09KD9662.zip	09KD9682.zip	09LD0518.zip	09LD0612.zip
09KD9579.zip	09KD9663.zip	09KD9683.zip	09LD0519.zip	09LD0613.zip
09KD9588.zip	09KD9670.zip	09KD9690.zip	09LD0600.zip
09KD9589.zip	09KD9671.zip	09KD9691.zip	09LD0601.zip
09KD9598.zip	09KD9672.zip	09KD9692.zip	09LD0602.zip
09KD9599.zip	09KD9673.zip	09KD9693.zip	09LD0603.zip

これらのダウンロードしたZIPファイルを解凍すると、GeoTIFFのファイルが取り出されます。この36枚あるGeoTIFFのファイルを1枚にまとめたのち、ラスター・タイルとして配信できる形式に変換します。

この作業にGDALを使用します。以前の記事「GDALのogr2ogrを使ってShapefileをOracle DatabaseのSDO_GEOMETRY列にロードする」で作成した、GDALを実行できるコンテナ・イメージを使用します。記事の初出時とは違い、更新したDockerfileではGDALユーティリティもビルドしています。

ダウンロードしたZIPファイルのあるディレクトリにgen_tiles.shとして、以下のファイルを作成します。

ZIPを解凍してGeoTIFFファイルを取り出し、gdalbuildvrtで1枚のファイルにまとめたのち、座標参照系を日本測地系2011/平面直角座標系第9系(EPSG:6677)からウェブ・メルカトル図法(EPSG:3857)に変換します。その後、出力形式のPNGに合わせてデータを(Float32から)8bitに変換しています。最後にラスター・タイルを生成しています。

GDALのコンテナを起動します。コンテナ内からZIPファイルにアクセスできるように、作業ディレクトリを/home/oracleにマウントします。

podman run --rm -it -v $PWD:/home/oracle gdal

% podman run --rm -it -v $PWD:/home/oracle gdal

bash-5.1$ 


環境変数を設定します。

export PATH=/usr/local/bin:$PATH
export LD_LIBRARY_PATH=/usr/local/lib64:${LD_LIBRARY_PATH}


bash-5.1$ export PATH=/usr/local/bin:$PATH

bash-5.1$ export LD_LIBRARY_PATH=/usr/local/lib64:${LD_LIBRARY_PATH}

bash-5.1$ 


出力先となるディレクトリtokyo_rasterを作成し、gen_tiles.shを実行します。

mkdir tokyo_raster
sh gen_tiles.sh *.zip

bash-5.1$ mkdir tokyo_raster

bash-5.1$ sh gen_tiles.sh *.zip

Archive:  09KD9568.zip

  inflating: 09KD9568.tif            

Archive:  09KD9569.zip

  inflating: 09KD9569.tif   


[中略]


Archive:  09LD0612.zip

  inflating: 09LD0612.tif            

Archive:  09LD0613.zip

  inflating: 09LD0613.tif            

0.Warning 1: The definition of projected CRS EPSG:6677 got from GeoTIFF keys is not the same as the one from the EPSG registry, which may cause issues during reprojection operations. Set GTIFF_SRS_SOURCE configuration option to EPSG to use official parameters (overriding the ones from GeoTIFF keys), or to GEOKEYS to use custom values from GeoTIFF keys and drop the EPSG code.

.Warning 1: The definition of projected CRS EPSG:6677 got from GeoTIFF keys is not the same as the one from the EPSG registry, which may cause issues during reprojection operations. Set GTIFF_SRS_SOURCE configuration option to EPSG to use official parameters (overriding the ones from GeoTIFF keys), or to GEOKEYS to use custom values from GeoTIFF keys and drop the EPSG code.


[中略]


Warning 1: The definition of projected CRS EPSG:6677 got from GeoTIFF keys is not the same as the one from the EPSG registry, which may cause issues during reprojection operations. Set GTIFF_SRS_SOURCE configuration option to EPSG to use official parameters (overriding the ones from GeoTIFF keys), or to GEOKEYS to use custom values from GeoTIFF keys and drop the EPSG code.

Creating output file that is 9597P x 7237L.

Processing mosaic_6677.vrt [1/1] : 0...10...20...30...40...50...60...70...80...90...100 - done.

Input file size is 9597, 7237

0...10...20...30...40...50...60...70...80...90...100 - done.

0...10...20...30...40...50...60...70...80...90...100 - done in 00:00:58.                 

0...10...20...30...40...50...60...70...80...90...100 - done in 00:00:42.                 

bash-5.1$ 


gen_tiles.shが正常に終了すれば、tokyo_rasterの下にラスター・タイルが作成されています。

コンテナを終了します。

exit

bash-5.1$ exit

exit

% 


作成したラスター・タイルを配布するために、nginxを起動します。以下の内容でdefault.confを作成します。とりあえずラスター・タイルの配布さえできればよいというプロンプトで、ChatGPTに作成してもらっています。特にキャッシュの設定については強めに設定しているため、ラスター・タイルを置き換えても(キャッシュをクリアしないかぎり)反映されない点に注意が必要です。

nginxのコンテナを起動します。HTTPの接続は8080番ポートで待ち受けします。

podman run --rm -p 8080:80 \
-v $PWD/tokyo_raster:/var/www/tiles:ro \
-v $PWD/default.conf:/etc/nginx/conf.d/default.conf:ro \
arm64v8/nginx:stable-alpine


% podman run --rm -p 8080:80 \

-v $PWD/tokyo_raster:/var/www/tiles:ro \

-v $PWD/default.conf:/etc/nginx/conf.d/default.conf:ro \

arm64v8/nginx:stable-alpine

/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration

/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/

/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh

10-listen-on-ipv6-by-default.sh: info: can not modify /etc/nginx/conf.d/default.conf (read-only file system?)

/docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh

/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh

/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh

/docker-entrypoint.sh: Configuration complete; ready for start up

2025/10/02 08:38:25 [notice] 1#1: using the "epoll" event method

2025/10/02 08:38:25 [notice] 1#1: nginx/1.28.0

2025/10/02 08:38:25 [notice] 1#1: built by gcc 14.2.0 (Alpine 14.2.0) 

2025/10/02 08:38:25 [notice] 1#1: OS: Linux 6.11.3-200.fc40.aarch64

2025/10/02 08:38:25 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 524288:524288

2025/10/02 08:38:25 [notice] 1#1: start worker processes

2025/10/02 08:38:25 [notice] 1#1: start worker process 16

2025/10/02 08:38:25 [notice] 1#1: start worker process 17

2025/10/02 08:38:25 [notice] 1#1: start worker process 18

2025/10/02 08:38:25 [notice] 1#1: start worker process 19

2025/10/02 08:38:25 [notice] 1#1: start worker process 20

2025/10/02 08:38:25 [notice] 1#1: start worker process 21

2025/10/02 08:38:25 [notice] 1#1: start worker process 22

2025/10/02 08:38:25 [notice] 1#1: start worker process 23

2025/10/02 08:38:25 [notice] 1#1: start worker process 24

2025/10/02 08:38:25 [notice] 1#1: start worker process 25

2025/10/02 08:38:25 [notice] 1#1: start worker process 26

2025/10/02 08:38:25 [notice] 1#1: start worker process 27

2025/10/02 08:38:25 [notice] 1#1: start worker process 28

2025/10/02 08:38:25 [notice] 1#1: start worker process 29

2025/10/02 08:38:25 [notice] 1#1: start worker process 30

2025/10/02 08:38:25 [notice] 1#1: start worker process 31



以上でラスター・タイルの配信サーバーも起動できました。

APEXアプリケーションに戻り、このラスター・タイルをマップ背景として設定します。

共有コンポーネントマップ背景を開きます。


マップ背景の作成を開始します。


マップ背景の名前Tokyo Digital Twinとします。タイプラスターXYZタイル・レイヤーを選択します。

ラスター・タイル(XYZ)URLに以下を設定します。

http://localhost:8080/tiles/{z}/{x}/{y}.png

以上でマップ背景の作成をクリックします。


マップ背景の編集ページに戻るので、変更の適用をクリックします。


以上でマップ背景Tokyo Digital Twinが作成できました。


マップ浸水予想区域図属性を開き、背景を変更します。

マップ背景共有コンポーネントを選択し、標準Tokyo Digital Twinを設定します。マップの高さ1000ピクセルに広げておきます。

変更を保存し、ページを実行します。


gdal2tilesの-zオプションに15-22を与えているため、ズーム倍率が15から22までの間でラスター・タイルの背景が表示されます。背景が表示されない場合は、ズーム倍率を変更してみてください。

先ほどのマップが、以下のように表示されます。


浸水深のレイヤーを非表示にすると、ラスター・タイルの表示を確認できます。


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

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

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