2025年11月7日金曜日

Oracle Spatial Studioの公開されたプロジェクトをAPEXアプリケーションに埋め込む

Oracle Spatial Studioで作成したプロジェクトを公開し、そのプロジェクトをOracle APEXのアプリケーションに埋め込みます。オラクルの以下のドキュメントで紹介されている手順を実施します。

Oracle Spatial Studio Guide, Release 25.1
7.4 Embedding a Published Project in an APEX Application

作成したAPEXアプリケーションは以下のように動作します。地図上には、東京都オープンデータカタログの都市計画決定情報GISデータから、再開発等促進区を定める地区計画(シェープファイル)として公開されているシェープファイルを、データベースに取り込んで表示しています。


macOS上のpodmanで作業環境を構築しています。Oracle Database 26ai Free、Oracle REST Data Services、Oracle Spatial Studioをそれぞれコンテナとして実行します。また、APEXアプリケーションのページにOracle Spatial StudioのWebコンポーネントを埋め込んで操作をするため、CORS(Cross-Origin Resource Sharing)の制約を受けないように、ORDSとSpatial Studioの前段にNginxによるリバース・プロキシを配置します。Oracle Spatial Studioの要件に合わせて、すべてHTTPS(暗号化)で通信します。

以下のような構成になります。


Oracle APEXの環境構築は、こちらの記事「podmanを使ってOracle Database FreeとOracle REST Data Servicesをコンテナとして実行する」で紹介しています。今回の作業にあたって、Nginxのリバース・プロキシをポッドapex内で動作させる必要があったため、コンテナ・ポート:ホスト・ポートのペアに9443:9443を追加しています。作成済みのポッドにコンテナ・ポートは追加できないため、コンテナ・ポートを追加するにはポッドを再作成する必要があります。

Oracle Spatial Studioの環境構築は、こちらの記事「Oracle Spatial Studioのコンテナ・イメージを作成する」で紹介しています。

これらの環境が構築済みであることを前提として、本記事の作業を始めます。


Nginxで使用する自己署名証明書と秘密キーの作成



NginxでのTLS接続を有効にするため、自己署名証明書と秘密キーを生成します。それぞれを保存するファイルの作成に、opensslコマンドを使用します。

openssl -v

% openssl -v

OpenSSL 3.5.2 5 Aug 2025 (Library: OpenSSL 3.5.2 5 Aug 2025)

% 


以下のコマンドにより自己署名証明書をserver.crt、秘密キーをserver.keyに保存します。ブラウザからはホスト名をlocalhostとしてアクセスすることを前提として、証明書のサブジェクトのCN(Common Name)にlocalhostsubjectAltNameにもlocalhostを設定しています。
openssl req -x509 -newkey rsa:4096 -sha256 -days 365 -nodes \
  -keyout ./server.key \
  -out  ./server.crt \
  -subj "/C=JP/ST=Tokyo/L=Chiyoda/O=LocalDev/OU=IT/CN=localhost" \
  -addext "subjectAltName=DNS:localhost,IP:127.0.0.1"

% openssl req -x509 -newkey rsa:4096 -sha256 -days 365 -nodes \

  -keyout ./server.key \

  -out  ./server.crt \

  -subj "/C=JP/ST=Tokyo/L=Chiyoda/O=LocalDev/OU=IT/CN=localhost" \

  -addext "subjectAltName=DNS:localhost,IP:127.0.0.1"

.....+.+.....+....+.....+....+++++++++++++++++++++++++++++++++++++++++++++*.......+...+...+.+...+...+...+.....+....+++++++++++++++++++++++++++++++++++++++++++++*....+..+.............+.....+............+.........+...+.....................+......+.+..............+...+...............+......+......+...+.+............+........+................+...+..+......+...+....+....................+.+..+..................+.+...+..+...+.+......+......+...+.........+........+......................+........+.......+........+...+...................+.....+.......+......+...+......+...+.....................+.....+.........+.+...........+...+.+......+...+.....+...+.............+......+...........+.........+.............+........+.......+..+......+....+...+...........+++++

........+.......+......+...........+......+....+.....+....+..+....+.........+..+...+.........+.+...........+.........+.+..............+++++++++++++++++++++++++++++++++++++++++++++*.+++++++++++++++++++++++++++++++++++++++++++++*...+.+..............+.............+..+......+.+......+..+.+..+.....................................+.....+...+.........+...+..........+...+.....+.......+.......................+...+.+...+........+..........+...+..........................+.............+.....+...+...................+.....+...............+................+...+.....+.......+..+...+....+...............+........+...+...+.........+......+.......+..............+.............+...........+......+..........+.....+.....................+............+......+.......+...+.........+...+........+.......+...+.........+..+.+...+...........+....+..+........................+....+.................+....+...+...+++++

-----

% ls server.*

server.crt server.key

% 


作成されたserver.crtをテキスト形式で表示します。SubjectCNx509 extensionsSubject Alternative Nameとしてlocalhostが設定されていることを確認します。

openssl x509 -in server.crt -text -noout

% openssl x509 -in server.crt -text -noout

Certificate:

    Data:

        Version: 3 (0x2)

        Serial Number:

            52:d4:c5:21:24:02:94:94:74:40:28:98:f9:63:da:cd:b1:8a:ca:5e

        Signature Algorithm: sha256WithRSAEncryption

        Issuer: C=JP, ST=Tokyo, L=Chiyoda, O=LocalDev, OU=IT, CN=localhost

        Validity

            Not Before: Nov  7 00:57:32 2025 GMT

            Not After : Nov  7 00:57:32 2026 GMT

        Subject: C=JP, ST=Tokyo, L=Chiyoda, O=LocalDev, OU=IT, CN=localhost

        Subject Public Key Info:

            Public Key Algorithm: rsaEncryption

                Public-Key: (4096 bit)

                Modulus:

                    00:da:c5:65:cc:f9:64:af:63:01:59:d0:5a:f7:44:

                    fb:aa:dd:81:aa:0d:8c:6b:7e:ea:5e:6a:48:2c:1a:

                    3d:2b:d4:63:89:46:fb:9f:53:a1:2b:af:8a:c2:b6:

                    f9:44:b2:8a:69:41:f4:f5:e2:f2:c0:47:eb:5c:5a:

                    38:64:1d:b3:bc:5e:75:29:17:ae:f0:fd:df:a7:06:

                    a3:11:28:ac:d4:03:e7:d8:0b:4f:6f:f3:99:15:cd:

                    be:dc:33:b9:2d:6d:d7:1f:48:b6:ad:0f:ef:77:24:

                    4f:ef:70:d8:5e:55:27:bf:44:50:b4:c7:f4:39:88:

                    82:69:0c:46:97:84:2e:11:88:93:0c:11:d9:de:04:

                    89:c9:0e:cf:53:1a:1c:7b:1a:54:9e:d5:04:81:84:

                    f0:1a:77:ee:3e:8c:c4:bf:92:4d:bd:18:90:28:c1:

                    79:44:67:3e:23:69:ea:33:77:ff:b5:18:8a:95:56:

                    6d:ae:af:98:8f:27:c7:67:3a:ed:f3:64:d8:90:19:

                    4b:9a:cb:9b:df:0d:0a:7a:a5:2d:6c:b7:9d:bf:24:

                    6c:e4:b0:76:3a:95:2e:16:f2:5c:b7:cb:33:b2:a5:

                    0b:2a:08:e6:ba:04:48:1e:3e:dc:e5:92:d5:55:bf:

                    57:74:ad:15:7d:2a:8c:e9:85:62:26:40:4d:9f:96:

                    9e:e7:23:8f:77:15:65:13:c3:bc:3f:89:8a:56:84:

                    20:04:7a:d1:87:12:61:a2:7c:71:f5:38:cd:2d:e1:

                    83:2c:81:e5:7f:37:a5:18:26:6b:34:b4:dc:b0:7d:

                    84:44:9c:4d:d9:c1:51:e4:99:1b:e2:30:ee:d7:d2:

                    e1:c1:3a:c3:f3:d6:14:c0:d9:05:81:e1:d0:de:87:

                    2d:85:a3:e5:64:08:bd:37:3d:d5:5b:ea:55:7d:af:

                    70:b9:41:0c:b6:be:6a:45:4f:ec:90:28:e5:33:b8:

                    64:f3:9c:7e:af:18:2a:e8:5a:e4:4d:21:7c:d1:86:

                    fe:8d:07:4c:64:87:a1:80:68:ea:49:08:a1:73:b9:

                    83:f6:c9:67:67:89:e6:4a:17:8d:bf:ef:03:89:be:

                    9d:55:ce:e6:c9:37:b8:78:8f:ae:09:0c:0f:cc:25:

                    12:1c:94:6e:2f:fb:a2:01:76:01:bb:3f:b4:d4:cf:

                    3e:11:e9:11:93:27:34:98:56:f9:85:08:65:23:bb:

                    77:93:af:0a:3e:85:ce:c1:37:fa:44:a6:c8:ce:3f:

                    37:7e:dc:64:30:bc:8c:38:c7:e9:c3:d6:f2:7b:43:

                    0a:55:51:13:e9:35:32:d4:b1:58:0b:db:09:87:d1:

                    cb:4b:c3:a2:54:89:5f:35:75:6b:75:e1:cd:2a:f4:

                    19:d6:c7

                Exponent: 65537 (0x10001)

        X509v3 extensions:

            X509v3 Subject Key Identifier: 

                B2:9A:9F:7B:F4:4E:8C:BE:03:C2:5B:05:07:52:A1:0E:E1:A9:2E:36

            X509v3 Authority Key Identifier: 

                B2:9A:9F:7B:F4:4E:8C:BE:03:C2:5B:05:07:52:A1:0E:E1:A9:2E:36

            X509v3 Basic Constraints: critical

                CA:TRUE

            X509v3 Subject Alternative Name: 

                DNS:localhost, IP Address:127.0.0.1

    Signature Algorithm: sha256WithRSAEncryption

    Signature Value:

        22:fd:bb:d3:95:08:04:e6:87:d9:40:34:40:20:f0:43:b3:38:

        ec:50:af:38:c2:e6:97:43:6e:b1:e1:42:30:b9:af:19:06:32:

        bc:df:f3:72:78:cd:d8:6f:f2:60:8f:75:92:25:4e:72:06:70:

        b1:27:28:f0:24:03:60:f9:9e:54:d0:4b:a7:de:06:e7:ab:56:

        eb:aa:a8:c6:af:18:cf:ea:9f:18:ae:b4:4c:f4:34:5e:8e:7e:

        aa:60:ba:4e:33:46:08:85:e9:46:9f:89:16:0e:6f:c4:f0:d1:

        a4:e2:27:34:75:20:ff:64:ac:13:1a:94:b6:8d:1b:9a:16:77:

        6f:3b:bf:aa:3c:c9:17:4b:ab:56:80:66:c2:de:ba:d1:36:25:

        1a:ad:94:b0:cd:67:bd:ff:be:4d:03:4a:fb:bb:32:03:bc:d2:

        1e:48:e9:e0:eb:1c:fe:15:ce:96:de:f9:21:3a:f5:98:d2:89:

        11:ff:7b:16:59:ee:07:ee:8f:0a:21:79:05:13:02:99:53:b6:

        48:f5:51:61:f0:60:70:88:62:7b:cd:5a:62:7f:db:55:db:5a:

        83:53:19:bd:bf:0c:b1:49:46:c3:0b:64:1b:23:53:92:a8:0b:

        d2:6e:88:18:f1:6e:0a:dd:38:11:e8:cb:b8:b5:a7:18:ba:8c:

        04:0f:b7:26:58:51:eb:e7:71:df:81:07:a1:8a:e2:43:d6:db:

        93:78:d7:07:d5:95:3a:5e:60:be:2f:8d:89:ff:86:27:b0:57:

        ac:68:3b:4f:c9:65:17:14:2f:ce:5c:bd:97:a7:74:8a:15:af:

        bf:6c:d8:b1:55:07:42:73:d4:8e:cd:2b:3c:96:9e:ae:56:5f:

        de:91:cc:2f:4a:6f:af:dc:33:3a:f2:b9:c4:bf:01:ac:47:a2:

        b6:43:ee:1e:ab:ed:70:50:5b:c2:02:79:de:5b:c4:b6:5d:38:

        59:50:dd:b4:dc:d1:37:c3:86:fd:03:8f:18:af:23:78:b3:42:

        6b:91:90:67:97:c6:53:67:18:9d:40:e1:f8:bb:51:f0:b9:9d:

        52:d1:a9:87:4b:38:dc:3d:3d:27:c0:f0:41:cf:fe:d2:60:da:

        af:5c:0c:09:84:9b:19:60:30:fe:73:c1:ba:ee:eb:5e:38:8b:

        eb:b6:86:c1:63:86:29:35:18:0b:d3:d6:a0:23:bf:c8:6d:13:

        ee:46:a9:84:6f:a5:d9:0c:fd:3f:f4:40:35:8a:48:ae:e6:cc:

        d6:e5:e5:01:f0:56:9e:d3:4a:af:01:70:e9:78:91:62:d8:6f:

        f8:9d:8e:df:12:df:a1:1e:8e:9a:1a:5a:bf:c8:7d:c2:b4:7d:

        c0:3d:47:88:b5:6a:42:8c

% 



Oracle REST Data ServicesのHTTPS化



コンテナapex-ordsで動作しているOracle REST Data Servicesを、HTTPからHTTPSで通信するように変更します。

コンテナapex-ordsに接続します。

podman exec -it apex-ords sh

podman exec -it apex-ords sh

sh-5.1$ 


ORDSの設定のstandalone.https.port8443を設定します。ORDSはstandalone.https.portが設定されていれば、HTTPSで通信するように構成されます。

ords --config /etc/ords/config config set standalone.https.port 8443

sh-5.1$ ords --config /etc/ords/config config set standalone.https.port 8443


ORDS: Release 25.3 Production on Fri Nov 07 01:07:29 2025


Copyright (c) 2010, 2025, Oracle.


Configuration:

  /etc/ords/config


The global setting named: standalone.https.port was set to: 8443

sh-5.1$ 


コンテナapex-ordsから抜けて、コンテナを再起動します。

podman restart apex-ords

sh-5.1$ exit

exit

% podman restart apex-ords

apex-ords

% 


ORDSはstandalone.https.portを設定後の初回起動時に、HTTPSで通信できるようにディレクトリ/etc/ords/config/global/standalone以下に、自己署名証明書のファイルとしてself-signed.pem、秘密キーのファイルとしてself-signed.keyを生成します。これらはstandalone.https.certおよびstandalone.https.cert.keyが未設定の場合に、デフォルトとして参照されます。

今回の設定ではstandalone.https.portのみを設定していますが、ORDSをHTTPS化する場合は、他のstandard.https.で始まるプロパティも設定すべきです。


ブラウザから、プロトコルとしてhttps、ポート番号8443を指定して、APEXへの接続を確認します。


自己署名証明書による暗号化なので、証明書を信頼してもよいかどうか、ブラウザから確認を要求されます。

詳細情報を表示し、localhostにアクセスします。


APEXのサインイン画面が表示されれば、ORDSのHTTPS化は完了です。


今回はNginxのリバース・プロキシがHTTPS(暗号化)の通信を受け付けて、バックエンドのORDSにHTTPS(暗号化)で通信します。一般的には、リバース・プロキシがHTTPSで受け付けて、バックエンドのサーバーへはHTTP(非暗号化)で通信するように構成することが多いと思います。

このような構成をするために、ORDSには設定security.httpsHeaderCheckがあります。このプロパティにX-Forwarded-Proto: httpsを設定することにより、リバース・プロキシとの間ではHTTPで通信しているにも関わらず、ORDSから呼び出されるRESTサービスやAPEXはHTTPSで通信していると認識します。

ords --config /etc/ords/config config set security.httpsHeaderCheck "X-Forwarded-Proto: https"

しかし、この設定を行った場合、HTTPSの通信はポート番号443で受け付けているとバックエンドに伝えます。結果として、ブラウザからhttps://localhost:9443/(Nginxのリバース・プロキシが接続を受け付けるURL)でアクセスしていても、それがhttps://localhost:443/からのリクエストとして認識されるため、APEXは自サイトにアクセスするためのURLを適切に生成できません。

今回はリバース・プロキシの接続をポート番号9443で受け付けたかった(ローカルのPCではポート番号443を開らけない)ため、security.httpsHeaderCheckは設定せず、バックエンドとの通信はHTTPSで行うようにしています。リバース・プロキシがHTTPSの標準ポート番号である443で接続を受け付ける場合は、security.httpsHeaderCheckを設定して、HTTPS -> HTTPとなるリバース・プロキシを構成できます。


Nginxによるリバース・プロキシの構成



最初に注意ですが、Oracle APEXおよびOracle REST Data Servicesは公式にはリバース・プロキシの使用をサポートしていません。以下は、APEXやORDSに問題なくアクセスするために、リバース・プロキシの存在を隠すための設定です。

Nginxによるリバース・プロキシを構成します。Nginxのコンテナはポッドapexに含めます。

podman run --name nginx --pod apex -d nginx:alpine

% podman run --name nginx --pod apex -d nginx:alpine

9e724da5d19499f25102f48d7bff252063690e8fc798df708eeab217a7a292c9

%


コンテナnginxが作成されたので、接続してリバース・プロキシを構成します。

podman exec -it nginx sh

% podman exec -it nginx sh

/ # 


ディレクトリ/etc/nginx/sslを作成し、その下に先ほど作成した自己署名証明書のファイルserver.crtと秘密キーのファイルserver.keyを配置します。

mkdir /etc/nginx/ssl
cd /etc/nginx/ssl
cat > server.crt
server.crtの内容をペーストしてCtrl+Dを入力
cat > server.key
server.keyの内容をペーストしてCtrl+Dを入力
chmod 400 server*
ls -l server*

/ # mkdir /etc/nginx/ssl

/ # cd /etc/nginx/ssl

/etc/nginx/ssl # cat > server.crt

-----BEGIN CERTIFICATE-----

MIIFwzCCA6ugAwIBAgIUUtTFISQClJR0QCiY+WPazbGKyl4wDQYJKoZIhvcNAQEL


[省略]


Vp7TSq8BcOl4kWLYb/idjt8S36EejpoaWr/IfcK0fcA9R4i1akKM

-----END CERTIFICATE-----

/etc/nginx/ssl # cat > server.key

-----BEGIN PRIVATE KEY-----

MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDaxWXM+WSvYwFZ


[省略]


zwQl/5TGWf/KEODEVkvjtJtqXVD6ZA==

-----END PRIVATE KEY-----

/etc/nginx/ssl # chmod 400 server*

/etc/nginx/ssl # ls -l server*

-r--------    1 root     root          2057 Nov  7 01:53 server.crt

-r--------    1 root     root          3272 Nov  7 01:53 server.key

/etc/nginx/ssl # 


Nginxの設定ファイル/etc/nginx/conf.d/default.confを以下の内容に置き換えます。ORDS+APEXおよびOracle Spatial Studioへのリバース・プロキシを構成しています。

APEXのパラメータIMAGE_PREFIXを設定し、静的リソースについてはCDNを参照するように構成するとロケーション/i/へのリクエストはCDNに向かいます。そのため、/i/の設定は不要になり、リバース・プロキシおよびORDSへのアクセスを減らすことができます。

default.confを入れ替えて、nginxを再起動します。

nginx -s reload

/etc/nginx/conf.d # nginx -s reload

2025/11/07 02:13:55 [notice] 41#41: signal process started

/etc/nginx/conf.d # 


リバース・プロキシ経由でのAPEXへのアクセスを確認します。この場合でも自己署名証明書を使用して暗号化を行なっているため、警告は表示されます。


APEXのサインイン・ページが表示されれば、APEXへのリバース・プロキシは正常です。


リバース・プロキシ経由でのOracle Spatial Studioへのアクセスを確認します。


Oracle Spatial Studioのサインイン画面が表示されれば、Oracle Spatial Studioへのリバース・プロキシは正常です。


以上で、同じURL(https://localhost:9443/)から、Oracle APEXとOracle Spatial Studioの双方にアクセスできる環境が構築できました。


シェープファイルをデータベースにロードする



東京都オープンデータカタログの都市計画決定情報GISデータから再開発等促進区を定める地区計画(シェープファイル)として公開されているシェープファイルをダウンロードし、GDALを使ってデータベースにロードします。

再開発等促進区を定める地区計画のシェープファイルはgis05_saikaihatsuchikukeikaku.zipとしてダウンロードされます。


GDALの実行には、Oracle Databaseにアクセスするドライバを組み込んだコンテナを使用します。GDALのコンテナ・イメージに作成手順は、こちらの記事「GDALのogr2ogrを使ってShapefileをOracle DatabaseのSDO_GEOMETRY列にロードする」で紹介しています。

シェープファイルのロード先となるOracle Databaseには、APEXのワークスペース・スキーマとしてWKSP_APEXDEVが作成済みとします。このスキーマにシェープファイルをロードします。

gis05_saikaihatsuchikukeikaku.zipが配置されているディレクトリで、GDALのコンテナを実行します。

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

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

bash-5.1$ ls gis05*

gis05_saikaihatsuchikukeikaku.zip

bash-5.1$ 


Oracle Databaseのドライバが適切に呼び出せるように、以下の環境変数を設定します。
export LANG=ja_JP.utf8
export NLS_LANG=American_America.AL32UTF8
export PATH=/usr/local/bin:$PATH
export ORACLE_HOME=/usr/lib/oracle/23/client64
export LD_LIBRARY_PATH=${ORACLE_HOME}/lib:/usr/local/lib64:${LD_LIBRARY_PATH}

bash-5.1$ export LANG=ja_JP.utf8

export NLS_LANG=American_America.AL32UTF8

export PATH=/usr/local/bin:$PATH

export ORACLE_HOME=/usr/lib/oracle/23/client64

export LD_LIBRARY_PATH=${ORACLE_HOME}/lib:/usr/local/lib64:${LD_LIBRARY_PATH}

bash-5.1$ 


gis05_saikaihatsuchikukeikaku.zipを解凍します。

unar gis05_saikaihatsuchikukeikaku.zip

bash-5.1$ unar gis05_saikaihatsuchikukeikaku.zip

gis05_saikaihatsuchikukeikaku.zip: Zip

  再開発等促進区を定める地区計画/  (dir)... OK.

  再開発等促進区を定める地区計画/再開発等促進区を定める地区計画_R070331.dbf  (23409 B)... OK.

  再開発等促進区を定める地区計画/再開発等促進区を定める地区計画_R070331.prj  (410 B)... OK.

  再開発等促進区を定める地区計画/再開発等促進区を定める地区計画_R070331.sbn  (1028 B)... OK.

  再開発等促進区を定める地区計画/再開発等促進区を定める地区計画_R070331.sbx  (164 B)... OK.

  再開発等促進区を定める地区計画/再開発等促進区を定める地区計画_R070331.shp  (146476 B)... OK.

  再開発等促進区を定める地区計画/再開発等促進区を定める地区計画_R070331.shx  (876 B)... OK.

Successfully extracted to "./再開発等促進区を定める地区計画".

bash-5.1$ 


取り出されたシェープファイル"再開発等促進区を定める地区計画/再開発等促進区を定める地区計画_R070331.shp"を、ogr2ogrコマンドを実行して、スキーマWKSP_APEXDEVに新規表GIS_SAIKAIHATSUを作成しロードします。
ogr2ogr -f OCI -overwrite OCI:wksp_apexdev/oracle@host.containers.internal/freepdb1 \
"再開発等促進区を定める地区計画/再開発等促進区を定める地区計画_R070331.shp" \
-oo ENCODING=CP932 -nln GIS_SAIKAIHATSU -lco GEOMETRY_NAME=GEOM -lco SRID=4326 \
-lco DIM=2 -nlt PROMOTE_TO_MULTI -s_srs EPSG:2451 -t_srs EPSG:4326 \
-skipfailures

bash-5.1$ ogr2ogr -f OCI -overwrite OCI:wksp_apexdev/oracle@host.containers.internal/freepdb1 \

"再開発等促進区を定める地区計画/再開発等促進区を定める地区計画_R070331.shp" \

-oo ENCODING=CP932 -nln GIS_SAIKAIHATSU -lco GEOMETRY_NAME=GEOM -lco SRID=4326 \

-lco DIM=2 -nlt PROMOTE_TO_MULTI -s_srs EPSG:2451 -t_srs EPSG:4326 \

-skipfailures

bash-5.1$ 


特にエラーが発生しなければ、表GIS_SAIKAIHATSUにデータがロードされています。


Oracle Spatial Studioでのプロジェクト作成



リバース・プロキシ経由でOracle Spatial Studioに接続し、ログインします。



デフォルトの接続として、コンテナで動作しているapex-dbへの接続が作成済みとします。

Oracle Spatial Studioのコンテナはポッドapexに含まれていないため、データベースへの接続先ホストはhost.containers.internal、ポート番号は1521、サービス名はFREEPDB1になります。本記事では、接続するスキーマはAPEXのワークスペース・スキーマとしてWKSP_APEXDEVを想定しているため、WKSP_APEXDEVで接続します。


作成した表GIS_SAIKAIHATSUを元に、データセットを作成します。


データベース表/ビューを選択し、作成をクリックします。


データセットを作成するアイテムの選択としてGIS_SAIKAIHATSUを選び、作成をクリックします。


データセットとしてGIS_SAIKAIHATSUが作成されます。


続いてプロジェクトを作成します。


新規に作成されたプロジェクトに、先ほど作成したデータセットGIS_SAIKAIHATSUを、データセットの追加をクリックして追加します。


GIS_SAIKAIHATSUにチェックを入れ、OKをクリックします。


追加されたデータセットGIS_SAIKAIHATSUを開き、列GEOMをマップ上にドラッグ&ドロップします。


マップ上に再開発促進区に関する区域が表示されます。

プロジェクトを保存します。


保存するプロジェクトの名前tokyo_saikaihatsuとします。保存をクリックします。


プロジェクトがtokyo_saikaihatsuとして保存されます。

保存されたプロジェクトをパブリッシュします。

アクション・メニューを開き、プロジェクトの公開を実行します。


共有をクリックします。


以上で作成したプロジェクトtokyo_saikaihatsuがパブリッシュされました。

プロジェクトのページを開き、パブリッシュされたプロジェクトのプロジェクトIDを確認します。プロジェクトIDは、APEXアプリケーションへのプロジェクトの埋め込み時に使用します。

公開済プロジェクトを選択し、3点メニューを開きURLを選択します。


公開済プロジェクトのURLが表示されます。このURLをコピーしてダイアログを閉じます


URLは以下の形式です。CGI引数のproj_idに指定されている値が、プロジェクトtokyo_saikaihatsuプロジェクトIDになります。

https://localhost:9443/spatialstudio/published.html?proj_id=edc92249bfb9a70092d694056a813215&ui_elements=application_header,layers_list,project_header

以上でAPEXアプリケーションに埋め込み可能な、Oracle Spatial Studioのプロジェクトが作成されました。


アクセス・トークンの生成



APEXアプリケーションより、Oracle Spatial Studioを呼び出す際に使用するアクセス・トークンを生成します。

本作業についての、ドキュメントでの記載箇所は以下になります。

3.3.1.1 Generating Access Tokens Programmatically
https://docs.oracle.com/en/database/oracle/spatial-studio/25.1/spstu/tokens-spatial-studio.html

Oracle Spatial Studioのコンソールより設定を開き、セキュリティアクセス・トークンを開きます。


アクセス・トークンの作成方法には2つの手順があります。
  • 新規トークンの作成を実行し、個別にアクセス・トークンを生成する
  • アクセスジェネレータ・トークンを使って、RESTサービスを呼び出してアクセス・トークンを生成する
今回はより柔軟にアクセス・トークンの生成が可能な、アクセスジェネレータ・トークンを使用します。

アクセスジェネレータ・トークンの表示をクリックします。


ダイアログが開き、表示されたアクセスジェネレータ・トークンをコピーします。コピーしたトークンを記録し、ダイアログを閉じます


Oracle APEXのSQLコマンドより、アクセス・トークンを発行するRESTサービスを呼び出します。

Oracle Spatial StudioのRESTサービスのエンドポイントはhttps://localhost:9443/になります。このエンドポイントは自己署名証明書を使って暗号化されているため、APEX_WEB_SERVICE.MAKE_REST_REQUESTを呼び出す際に、自己署名証明書を信頼する証明書として登録されたウォレットが必要になります。

そのため、Oracle Walletsを作成します。

コンテナapex-dbに接続します。

podman exec -it apex-db sh

% podman exec -it apex-db sh

sh-4.4$ 


Nginxのリバース・プロキシを構成する際に使用したserver.crtを、コンテナ内にコピーします。

cat > server.crt
server.crtの内容をペーストしてCtrl+D

sh-4.4$ cat > server.crt

-----BEGIN CERTIFICATE-----

MIIFwzCCA6ugAwIBAgIUUtTFISQClJR0QCiY+WPazbGKyl4wDQYJKoZIhvcNAQEL


[省略]


Vp7TSq8BcOl4kWLYb/idjt8S36EejpoaWr/IfcK0fcA9R4i1akKM

-----END CERTIFICATE-----

sh-4.4$ 


ディレクトリ/home/oracle/walletsとして、Oracle Walletsを作成します。

orapki wallet create -wallet /home/oracle/wallets -pwd <パスワード> -auto_login

sh-4.4$ orapki wallet create -wallet /home/oracle/wallets -pwd ChangeMe5566 -auto_login

Oracle PKI Tool Release 23.0.0.0.0 - Production

Version 23.0.0.0.0

Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved.


Operation is successfully completed.

sh-4.4$ 


自己署名証明書server.crtを信頼する証明書として、Oracle Walletsに追加します。

orapki wallet add -wallet /home/oracle/wallets -pwd <パスワード> -trusted_cert -cert ./server.crt

sh-4.4$ orapki wallet add -wallet /home/oracle/wallets -pwd ChangeMe5566 -trusted_cert -cert ./server.crt 

Oracle PKI Tool Release 23.0.0.0.0 - Production

Version 23.0.0.0.0

Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved.


Operation is successfully completed.

sh-4.4$ 


以上で、Oracle Walletsは作成できました。

続いてOracle Spatial Studioのアクセスジェネレータ・トークンから、APEXのWeb資格証明を作成します。

ワークスペース・ユーティリティWeb資格証明を開きます。


作成を開始します。


名前Spatial Studio静的IDSPATIAL_STUDIOとします。認証タイプHTTPヘッダーを選択し、資格証明名としてHTTPヘッダー名であるAuthorizationを選択します。

資格証明シークレットとして、Bearerで始めて空白で区切り、Oracle Spatial Studioのアクセスジェネレータ・トークンを記述します。

以上の設定で、作成を実行します。


Oracle Spatial Studioのアクセス・トークンの生成に使用するWeb資格証明として、SPATIAL_STUDIOが作成されました。


SQLコマンドを開き、RESTサービスを呼び出してアクセス・トークンを生成します。アクセス・トークンを生成するエンドポイントは以下です。

https://localhost:9443/spatialstudio/oauth/v1/user/token?name=<トークン名>&validTime=<1-99999または-1、単位は分)&action=<read_onlyまたはread_write>&resourceType=<dataset_streaming, dataset_streaming_refreshing , embedded_published_project, all>

今回は作成するトークンの名前myTokenとして、以下の条件でアクセス・トークンを生成します。

きちんと確認していませんが、APEXのページにOracle Spatial StudioのプロジェクトをWebコンポーネントとして埋め込み、外部のJavaScriptコードからWebコンポーネントの機能を呼び出す場合は、actionとしてread_writeである必要があり、その場合はresouceTypeallにしないとエラーが発生します。

https://localhost:9443/spatialstudio/oauth/v1/user/token?name=myToken&validTime=9999&action=read_write&resourceType=all

以下のスクリプトを実行し、アクセス・トークンを生成します。



生成されたアクセス・トークンをコピーし、保管しておきます。

APEXのSQLコマンドからAPEX_WEB_SERVICE.MAKE_REST_REQUESTを実行する場合、コンテナapex-dbで実行しているOracle DatabaseからHTTPSのリクエストが発行されます。Nginxのリバース・プロキシがポッドapexに含まれていないと、RESTサービスのリクエストのリクエストはhost.containers.internalをエンドポイントのホストとして呼び出す必要があります。ホスト名にhost.containers.internalを指定すると、Nginxに設定した自己署名証明書のサブジェクトやSANに設定しているlocalhostと異なるため、RESTサービスのリクエストが失敗します。そのため、コンテナとして実行されているOracle Databaseから、localhostを宛先としてNginxを呼び出せるように、Nginxをポッドapexに含めています。

PL/SQLのコードよりアクセス・トークンを生成できますが、APEXアプリケーションへの組み込みは割愛します。


APEXアプリケーションの作成



Oracle Spatial Studioのプロジェクトを埋め込んだAPEXアプリケーションを作成します。APEXアプリケーションへの埋め込みは、以下のドキュメントの記述に沿って実施します。

7.4 Embedding a Published Project in an APEX Application
https://docs.oracle.com/en/database/oracle/spatial-studio/25.1/spstu/embedding-published-project-apex-application.html

空のAPEXアプリケーションを作成します。名前東京都再開発とします。


アプリケーションが作成されたら、ページ・デザイナホーム・ページを開きます。


ページ・プロパティJavaScriptファイルURLに以下を記述します。

[require requirejs]https://localhost:9443/spatialstudio/api/v1/embeddable.js?ex=kobinding


Spatial Studioの埋め込み先となるリージョンを作成します。名前Spatial Studioタイプ静的コンテンツとします。

ソースHTMLコードには以下を記述します。project-idには、Spatial Studioの公開済プロジェクトのURLから取り出した値、tokenにはRESTサービスを呼び出して生成した、アクセス・トークンに置き換えます。
<div style="width:100%; height: 400px;">
    <spatial-studio-project 
        id="spatial-studio-project-1"
        server-url="https://localhost:9443/spatialstudio/"
        project-id="edc92249bfb9a70092d694056a813215"
        token="eyJ0eXAiOiJzZ3RlY2hfand0IiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiI3YTYzMGU2ODhkZDQ0Y2YzYjhjYzgxNTQ3NzRhODJkYSIsInN0dWRpb190ZW5hbnRfaWRlbnRpZmllciI6IkdMT0JBTDpAZGF0YWJhc2VAOkBkZWZhdWx0QCIsIm5iZiI6MTc2MjQ5NjA1MCwic2NvcGUiOiJyZXNvdXJjZS5yZWFkX3dyaXRlOnJlc291cmNlLnR5cGUuYWxsIiwiaXNzIjoiT3JhY2xlU3BhdGlhbFN0dWRpbyIsImV4cCI6MTc2MzA5NTk5MCwiaWF0IjoxNzYyNDk2MDUwLCJqdGkiOiIwNjMxYzQ4NS02YjNiLTRiZGQtOWRkYy0xNjMzNTU1OWUzZDEiLCJzdHVkaW9fb2lkIjoiYmUxYWFmNTFhY2RmNDgyYWI3NTVkZTYxMTVmNzg1NDAifQ.qfNoxEto7SyvUijKDbyDYAz5G3hVWhPV047ys9XRqkUCBLkDgwvM62PiATW7UpZ7igbHC64bOomeywQ-2yMgSxQWvx6fdkxbpE9ihkUS5dUq3iJPSRODLJDKa7AWk1BC5ot2j6n1MkvAuqf6qb9sx0VuGc4fwO6sqi9lEe04DhZf_aVsShzHUYHwPrJnsMYL-9DbNfdoW6q9Mi4O9rtUZa5nreXuDiuMFx2SmluLL7jFzerpXPzy20mzw64GPUzs2s6RimH6elrWBSNpBRKLrScxqtSCyKpfiRLJ2UZJ-n4wvQdbkxJcQ4cXvYkvRx-hkdQQqWOBcnbd2Y77PzU-tg"
        project-header="off"
        layers-list="on">
    </spatial-studio-project>
</div>

以上でアプリケーションを実行します。以下のようにAPEXのページにSpatial Studioのプロジェクトが埋め込まれて表示されます。

エラーが発生するとコンソールに表示されるので、JavaScriptコンソールを開いた状態でAPEXアプリケーションを実行することをお勧めします。


プログラムから作成したアクセス・トークンであっても、Oracle Spatial Studioの設定のアクセス・トークンのページに一覧されます。トークンの末尾数文字は表示されるため、カスタム要素spatial-studio-projectに与えたtokenが有効かどうかを確認できます。


ドキュメントには以下のCSSが記載されていますが、ページ・プロパティCSSインラインに設定しても、APEXアプリケーションとOracle Spatial Studioの間で設定の不整合は解消しません。
body {
    --sgtech-bg: var(--rbr-blue);
    --oj-heading-text-color: white;

    --ut-region-header-text-color: white;
    --ut-region-border-color: white;
    --ut-footer-text-color: white;

    max-width: 100%;

    --oj-core-spacing-3x: 0px;
    --ut-region-body-padding-y: 0px;
    --ut-region-body-padding-x: 0px;
}

div.sgtech-home-container {
    max-height: 100%;
}
そのためCSSについては、ドキュメントの記述をそのまま採用はできず、調整が必要です。


APEXのページに埋め込んだOracle Spatial StudioのWebコンポーネントと、APEXの対話グリッドの連携を実装します。

対話グリッドのリージョンを作成します。名前区域ソース表名GIS_SAIKAIHATSUを設定します。


対話グリッドの属性を開きます。

複数行の選択ができるように、編集有効オンにし、実行可能な操作はすべてチェックを外します。

外観最初の行の選択オフページ区切りタイプスクロールヘッダー固定リージョン固定レポートの高さ400ピクセルにします。


対話グリッドを配置したページは、以下のように表示されます。

ここで、JavaScriptコンソールにA polygon layer added to canvas:といったメッセージとともにLayer IDが表示されます。このLayer IDを記録しておきます。


Spatial Studioと対話グリッドを連携させるために、動的アクションを設定します。

主にドキュメントの以下のページを参照します。

7.3 Interactive Usage with the Spatial Studio Web Component
https://docs.oracle.com/en/database/oracle/spatial-studio/25.1/spstu/interactive-usage-spatial-studio-web-component.html

名前Selection Changeタイミングイベント選択の変更[対話グリッド]を選択します。


TRUEアクションとしてJavaScriptコードの実行を選択し、設定コードに以下を記述します。LAYER_IDはJavaScriptコンソールより確認した値で置き換えます。


埋め込まれたSpatial Studioのプロジェクトが表示されている条件で実行するように、クライアント側の条件タイプJavaScript式を選択し、JavaScript式に以下を記述します。

document.querySelector('#spatial-studio-project-1 studio-application')


APEXアプリケーションを実行します。

対話グリッドで選択した区域のみが、埋め込まれたSpatial Studioのプロジェクトに表示されます。

対話グリッドにはすべてを選択するためのチェックボックスがありますが、これはすでに対話グリッドに読み込まれた行だけが対象になります。期待通りに動作させるには、対話グリッドを最終行までスクロールさせ、すべての行を読み込んでおく必要があります。


Layer IDはOracle Spatial Studioからも取得できますが、ここでコピーしたLayer IDはJavaScriptコンソールに表示されるLayer IDとは異なります。JavaScriptのコードに含めるLayer IDは、JavaScriptコンソールに表示される値を使用します。


今度は反対に、Spatialのプロジェクト上で選択した区域を対話グリッドに反映します。

ページ・プロパティJavaScriptページ・ロード時に実行に以下を記述します。Spatial StudioのWebコンポーネントのイベントfeatures_selecetedに、イベント・ハンドラを設定します。



対話グリッドの静的IDとしてTABLEREGIONを設定します。


以上ですべての実装は完了です。

領域で選択するツールなどを使用すると、Spatial StudioのWebコンポーネント上で選択した領域が、対話グリッドの行の選択に反映されることが確認できます。期待通りに動作させるには、対話グリッドを最下部までスクロールさせて、表のデータをすべて読み込んでおく必要があります。


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

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

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