ラベル マップ の投稿を表示しています。 すべての投稿を表示
ラベル マップ の投稿を表示しています。 すべての投稿を表示

2025年11月26日水曜日

LeafletのQuick Start GuideをAPEXアプリケーションに実装してみる

2Dのマップ表示の用途で人気の高いLeafletによるマップを、Oracle APEXのアプリケーションに組み込んでみます。組み込む機能はLeafletのQuick Start Guideで紹介されている、以下を対象とします。
  • ラスタタイルの表示(国土地理院タイルを使用します)
  • マーカーの表示/非表示、およびポップアップの表示/非表示
  • サークルの表示/非表示、およびポップアップの表示/非表示
  • ポリゴンの表示/非表示、およびポップアップの表示/非表示
  • スタンドアロンのポップアップ表示/非表示
  • マップ上のクリックによるポップアップの表示
上記を実装したAPEXアプリケーションは、以下のように動作します。渋谷のスクランブル交差点を中心に地図を表示し、マーカー、サークルおよびポリゴン(道玄坂)などを表示しています。


Leaflet Quick Start Guideに沿って、APEXアプリケーションの組み込みを実施します。

空のアプリケーションを作成します。名前Leafletとします。


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


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

ホーム・ページに、Quick Start GuideのPreparing your pageに記載されている設定を加えます。

Oracle APEXには、ページにロードするJavaScriptファイルを指定するプロパティとして、JavaScriptファイルURLCSSファイルを指定するプロパティとして、CSSファイルURLがあります。

LeafletのQuick Start Guideでは、ロードするCSSファイルおよびJavaScriptファイルに属性integritycrossoriginの指定が含まれています。これらは、APEXのプロパティでは設定できません。また、必ずCSSファイルをJavaScriptファイルの前にロードするように指示されています。こちらについては、ページ・プロパティのCSSファイルURL(link要素)は、必ずJavaScriptファイルURL(script要素)よりも先にHTMLとして出力されます。

integritycrossoriginの設定を残すために、ページ・プロパティHTMLヘッダーに、Quick Start Guideに記載されている内容をそのまま転記します。
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
    integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
    crossorigin=""/>
<!-- Make sure you put this AFTER Leaflet's CSS -->
 <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
     integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
     crossorigin=""></script>
CSSインラインでマップの高さを指定します。Quick Start Guideでは180pxとなっていますが、それよりも高い640pxを設定します。

#map { height: 640px; }


Leafletを初期化するdiv要素を含むリージョンを作成します。

識別名前Mapタイプ静的コンテンツソースHTMLコードに以下を記述します。

<div id="map"></div>


以上で、Preparing your pageに記載されている内容をホーム・ページに設定できました。

Leafletの初期化から、マーカーの表示/非表示、サークルの表示/非表示など、JavaScriptによる実装はすべて、静的アプリケーション・ファイルに記述します。以下のファイルを、静的アプリケーション・ファイルとして保存します。名前はapp.jsとします。



LeafletのQuick Start Guideでは、マーカー、サークルおよびポリゴンを表示するコードは紹介されていますが、それを非表示にするコードはありません。上記のapp.jsには、非表示にするコードも追加しています。

マーカーなどの表示/非表示はAPEXアクションとして定義し、ページ上のボタンとカスタム属性data-actionによって紐付けています。

ページ・ロード時に静的アプケーション・ファイルapp.jsがロードされるように、ページ・プロパティJavaScriptファイルURLに以下を設定します。

#APP_FILES#app#MIN#.js


静的アプリケーション・ファイルに記述したAPEXアクションを呼び出すボタンを、ページ上に作成します。

ボタンを一列で配置するために、静的コンテンツのリージョンを作成します。外観テンプレートButtons Containerを選択します。


APEXアクションを呼び出すボタンを作成します。

ボタンの詳細カスタム属性に記述するdata-actionで、APEXアクションに紐付けます。

data-action="TOGGLE_MARKER"

ボタンのクリックでページの送信などが動作しないように、動作アクション動的アクションで定義を設定します。

識別ラベルは、data-actionで紐づけられたAPEXアクションのlabel属性の値に置き換わります。そのため、ページでの設定値は画面に表示されません。


同様にTOGGLE_CIRCLETOGGLE_POLYGONTOGGLE_MARKER_POPUPTOGGLE_CIRCLE_POPUPTOGGLE_POLYGON_POPUPTOGGLE_STANALONE_POPUPのボタンを作成します。

これらのボタンは、TOGGLE_MARKERと横一列に並べるため、レイアウト新規行の開始オフにします。それ以外はdata-actionに、それぞれに紐づけるAPEXアクション名を設定します。


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

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

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

2025年11月10日月曜日

Stamen Map TilesをOracle APEXのマップ・リージョンの背景マップにする

米国San FranciscoのStamen Design社が開発したStamen Map Tilesを、Oracle APEXのマップ・リージョンの背景マップにしてみます。Stamen Map Tilesは現在Stadia Maps社のプラットフォームより配布されているため、Stamen Map Tilesを背景マップとして使用するには、Stadia Maps社にユーザー登録する必要があります。非商用用途に限定したFreeプランが提供されています。

Stadia Maps社から配布されているStamen Map Tilesを使用するにあたって、以下のサイトに記載されているFAQを参照します。

Stamen Design × Stadia Maps
https://stadiamaps.com/stamen/

設定については以下のページで説明されています。

Migration Guide for Stamen Map Tile Users
https://docs.stadiamaps.com/guides/migrating-from-stamen-map-tiles/

作成したAPEXアプリケーションのマップは、以下のように表示されます。マップ上に表示しているのは東京都オープンデータカタログサイトにあるTOKYO WALKING MAPより、水元・柴又エリア~寅さんの柴又帝釈天と水元公園へのみち~としてダウンロードできるKMLファイルを、データベースにロードして表示しています。


macOS上のpodmanで実行しているOracle APEXの環境で作業します。構築手順については、こちらの記事「podmanを使ってOracle Database FreeとOracle REST Data Servicesをコンテナとして実行する」で紹介しています。

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

水元・柴又エリア~寅さんの柴又帝釈天と水元公園へのみち~を開き、ZIPファイルをダウンロードします。


ファイルは以下の名前でダウンロードされます。

131229_Mizumoto_and_Shibamata_Area_-_Tora-sans_Road_to_Shibamata_Taishakuten_and_Mizumoto_Park.zip

これはKMLファイルをZIP圧縮したファイルなので、KMZファイルとして扱うことができます。ファイル名をwalking.kmzに変更します。

以降の作業では、上記のファイルをwalking.kmzとして扱います。

GDALを使用してwalking.kmzをOracle Databaseにロードします。GDALを組み込んだコンテナの作成手順については、こちらの記事「GDALのogr2ogrを使ってShapefileをOracle DatabaseのSDO_GEOMETRY列にロードする」を参照してください。今回の作業を行なうにあたって、LIBKMLのドライバの組み込むようにDockerfileを更新しています。

GDALを組み込んだコンテナを実行します。

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

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

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$ 


以下のogrinfoコマンドを実行し、KMZファイルに含まれるフォルダを一覧します。

ogrinfo -ro walking.kmz

今回、データベースにロードするのは無題のレイヤです。

bash-5.1$ ogrinfo -ro walking.kmz 

INFO: Open of `walking.kmz'

      using driver `LIBKML' successful.

1: 無題のレイヤ

2: 都立公園/Tokyo Metropolitan Parks and Gardens

3: 海上公園/Marine Parks

bash-5.1$ 


以下のogr2ogrコマンドを実行し、KMZファイルに含まれる散歩ルートをデータベースにロードします。
ogr2ogr -f OCI \
"OCI:wksp_apexdev/<パスワード>@host.containers.internal/freepdb1" \
./walking.kmz "無題のレイヤ" \
-nln TOKYO_WALKING_ROUTES \
-lco GEOMETRY_NAME=GEOM -lco SRID=4326 -nlt PROMOTE_TO_MULTI

bash-5.1$ ogr2ogr -f OCI \

"OCI:wksp_apexdev/********@host.containers.internal/freepdb1" \

./walking.kmz "無題のレイヤ" \

-nln TOKYO_WALKING_ROUTES \

-lco GEOMETRY_NAME=GEOM -lco SRID=4326 -nlt PROMOTE_TO_MULTI

bash-5.1$ 


エラーが発生しなければ、KMZ(KML)フォーマットで記載された散歩ルートはデータベースにロードされています。

APEXアプリケーションを作成し、マップ・リージョンで散歩ルートを表示します。

最初にSQLコマンドより、表TOKYO_WALKING_ROUTESにロードされたデータを確認します。以下のSELECT文を実行します。

select * from tokyo_walking_routes


データを確認すると、以下の3つの特殊な"Name"のデータが見つかります。これ以外は、観光地です。また、コース/Routeはポイントではなく行(LineString)です。

スタート/Start,ゴール/Goal,コース/Route

これらは、レイヤを分けて表示することにします。

APEXアプリケーションを作成します。

アプリケーション作成ウィザードを開き、デフォルトで作成されているホーム・ページを削除して、代わりにマップのページを追加します。

アプリケーションの名前TOKYO WALKING MAPとします。


マップのページ名散歩にはogr2ogrコマンドで作成したTOKYO_WALKING_ROUTESを選択します。ポイントを選択し、ジオメトリ列GEOMツールチップ列Nameを指定します。


以上でアプリケーションを作成します。

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


作成済みのレイヤ散歩から、列Nameの値がスタート/Startゴール/Goalコース/Routeのデータを除きます。これらは別のレイヤとして表示します。

ソースWHERE句に以下を記述します。

"Name" not in ('スタート/Start','ゴール/Goal','コース/Route')

情報ウィンドウタイトル列Name本体列descriptionを設定します。


レイヤ散歩を重複させ、スタート位置を表示するレイヤに変更します。

名前スタートソースWHERE句に以下を設定します。

"Name" = 'スタート/Start'

外観塗りつぶしの色を赤(#ff0000)にします。


ゴールを表示するレイヤを作成します。

名前ゴールソースWHERE句に以下を設定します。

"Name" = 'ゴール/Goal'

外観塗りつぶしの色を緑(#008000)にします。


コースを表示するレイヤを作成します。

名前コースレイヤー・タイプにします。ソースWHERE句に以下を設定します。

"Name" = 'コース/Route'

外観のストローク・スタイルに点線を選択し、ストロークの色を黒(#101010)にします。


以上で、水元・柴又エリアの散歩コースがマップ上に表示されるようになりました。


次にマップ背景を変更します。

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


作成を開始します。


Standia Mapsにユーザー登録をして、APIキーを生成済みとします。

名前Stamen Terrainとし、タイプラスターXYZタイル・レイヤーを選択します。APIキーにStandia Mapsで取得したAPIキーを設定し、ラスター・タイル(XYZ) URLとして以下を記述します。

https://tiles.stadiamaps.com/tiles/stamen_terrain/{z}/{x}/{y}.png?api_key={api_key}

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


マップ背景が作成されます。属性に以下のコピーライト情報を記述します。
&copy; <a href="https://stadiamaps.com/" target="_blank">Stadia Maps</a> <a href="https://stamen.com/" target="_blank">&copy; Stamen Design</a>
&copy; <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a>
&copy; <a href="https://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>
以上で変更の適用をクリックします。


以上でマップ背景が作成されました。


マップ・リージョンの属性を開き、マップ背景共有コンポーネントを選択します。標準として先ほど作成したマップ背景Stamen Terrainを設定します。


以上でAPEXアプリケーションは完成です。アプリケーションを実行すると、以下のマップが表示されます。


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

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

2025年10月15日水曜日

東京都デジタルツインのLasデータをデータベースにロードし等高線を生成してマップ上に表示する #JoelKallmanDay

東京都デジタルサービス局による東京都デジタルツイン実現プロジェクトで公開している島しょ地域点群データより、伊豆諸島の神津島の西にある恩馳島(おんばせじま)の1つのメッシュに含まれるLasデータから等高線を生成し、APEXアプリケーションのマップ上に表示します。

点群データはデータ量が多く、手元で動かせるOracle Database 23ai Freeでは、一本の等高線を生成するだけで何時間もかかります。そのため、作業の目的は等高線を生成することではなく、Oracle Databaseで点群データを扱う手順の確認です。
  1. LasファイルからCSVファイルを生成しました。
  2. CSVファイルをフラットな点群データとしてデータベースにアップロードしました。
  3. フラットな点群データをOracle Spatialの点群データ型であるSDO_PC型へ変換しました。
  4. SDO_PC型のデータから等高線(SDO_GEOMETRY型)を生成しました。
  5. 生成した等高線をマップ上に表示しました。
  6. オープンデータのDXF形式の等高線をGDALのogr2ogrを使って表示しました。
作成したAPEXアプリケーションのマップ表示は以下になります。IDが09QC6295のメッシュを対象として作業を実施しています。標高0mから60mまで、10m間隔で等高線を生成し、表示しています。


APEXアプリケーションは、等高線を保持している表からほぼ自動で生成できます。そのため、APEXアプリケーションの作成よりは、それまでの準備が実際の作業になります。


Lasファイルの取得



東京都デジタルツイン実現プロジェクトの島しょ地域点群データより、オリジナル(DSM)及びグラウンドデータ(DEM)を開きます。


等高線を生成する対象のメッシュを選択してダウンロードします。


選択したメッシュの名前(本記事の作業では09QC6295.zip)が付いたZIPファイルが、手元にダウンロードされます。それを解凍すると.lasファイルになります。

unzip 09QC6295.zip

onbase % unzip 09QC6295.zip 

Archive:  09QC6295.zip

  inflating: 09QC6295.las            

onbase % 



LasからCSVファイルへの変換



本記事では作業をmacOS上で実施します。データベースにLasデータをロードするには、CSV形式に変換する必要があります。CSVへの変換は、PDALまたはLAStoolsで実施できます。本記事では、LAStoolsにOpen source toolsとして含まれているlas2lasおよびlas2txtを使用することにしました。LAStoolsにはOpen source tools、Free tools、Closed source toolsと、それぞれ使用条件の異なるソフトウェアが含まれています。そのため、使用するツールごとに事前にライセンスを確認します。Open source toolsについてはLGPL-2.1のようです。

ARM版macOSに対応したLAStoolsのバイナリが見つけれらなかったため、GitHubからOpen source toolsのリポジトリをクローンし、ビルドしました。

作業ディレクトリ以下にLAStoolsのリポジトリをクローンします。

git clone https://github.com/LAStools/LAStools.git

onbase % git clone https://github.com/LAStools/LAStools.git

Cloning into 'LAStools'...

remote: Enumerating objects: 11261, done.

remote: Counting objects: 100% (1841/1841), done.

remote: Compressing objects: 100% (413/413), done.

remote: Total 11261 (delta 1585), reused 1463 (delta 1428), pack-reused 9420 (from 2)

Receiving objects: 100% (11261/11261), 21.22 MiB | 6.80 MiB/s, done.

Resolving deltas: 100% (8580/8580), done.

onbase % 


リポジトリをクローンするとディレクトリLAStoolsが作成されます。作成されたディレクトリLAStoolsへ移動します。

cd LAStools

onbase % cd LAStools 

LAStools % ls

bin data lastools.dsw LICENSE.txt

CHANGES.txt example_batch_scripts LAStools.sln readme_logo.jpg

CMakeLists.txt HALL_OF_SHAME.txt LASzip README.md

COPYING.txt LASlib license.html src

LAStools %


バイナリのビルドにはcmakeを使用します。macOSに、Xcodeのコマンドライン・ツール(clang含む)やcmake(brew install cmake)をあらかじめインストールしておく必要があります。

以下のコマンドを実行し、LAStoolsのバイナリを作成します。make実行時に発生したエラーを回避するため、cmakeでCコンパイラに与えるフラグとして-Wno-error -Wno-non-pod-varargs -Wno-deprecated-declarationsを追加しています。
mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_CXX_FLAGS="-Wno-non-pod-varargs -Wno-deprecated-declarations" \
..
make -j"$(sysctl -n hw.ncpu)"

LAStools % mkdir build && cd build

build % cmake -DCMAKE_BUILD_TYPE=Release \

-DCMAKE_CXX_FLAGS="-Wno-non-pod-varargs -Wno-deprecated-declarations" \

..

-- The CXX compiler identification is AppleClang 17.0.0.17000319

-- The C compiler identification is AppleClang 17.0.0.17000319

-- Detecting CXX compiler ABI info

-- Detecting CXX compiler ABI info - done

-- Check for working CXX compiler: /usr/bin/c++ - skipped

-- Detecting CXX compile features

-- Detecting CXX compile features - done

-- Detecting C compiler ABI info

-- Detecting C compiler ABI info - done

-- Check for working C compiler: /usr/bin/cc - skipped

-- Detecting C compile features

-- Detecting C compile features - done

-- Configuring done (0.4s)

-- Generating done (0.0s)

-- Build files have been written to: /Users/**********/Documents/onbase/LAStools/build

build % make -j"$(sysctl -n hw.ncpu)"

[  1%] Building CXX object LASlib/src/CMakeFiles/LASlib.dir/lasreader.cpp.o

[  3%] Building CXX object LASlib/src/CMakeFiles/LASlib.dir/lasreader_shp.cpp.o

[  3%] Building CXX object LASlib/src/CMakeFiles/LASlib.dir/lasignore.cpp.o

[  4%] Building CXX object LASlib/src/CMakeFiles/LASlib.dir/lasreader_las.cpp.o

[  7%] Building CXX object LASlib/src/CMakeFiles/LASlib.dir/lasreader_bil.cpp.o

[  8%] Building CXX object LASlib/src/CMakeFiles/LASlib.dir/laswriter.cpp.o

[  8%] Building CXX object LASlib/src/CMakeFiles/LASlib.dir/lasreader_asc.cpp.o

[  8%] Building CXX object LASlib/src/CMakeFiles/LASlib.dir/lasreader_qfit.cpp.o

[  9%] Building CXX object LASlib/src/CMakeFiles/LASlib.dir/lasreader_bin.cpp.o

[ 10%] Building CXX object LASlib/src/CMakeFiles/LASlib.dir/lasreadermerged.cpp.o

[ 12%] Building CXX object LASlib/src/CMakeFiles/LASlib.dir/lasreader_dtm.cpp.o

[ 13%] Building CXX object LASlib/src/CMakeFiles/LASlib.dir/lasreader_ply.cpp.o

[ 13%] Building CXX object LASlib/src/CMakeFiles/LASlib.dir/lasreaderbuffered.cpp.o

[ 14%] Building CXX object LASlib/src/CMakeFiles/LASlib.dir/lasreaderstored.cpp.o

[ 16%] Building CXX object LASlib/src/CMakeFiles/LASlib.dir/lasreaderpipeon.cpp.o

[ 16%] Building CXX object LASlib/src/CMakeFiles/LASlib.dir/lasreader_txt.cpp.o

In file included from /Users/ynakakoshi/Documents/onbase/LAStools/LASlib/src/lasreader.cpp:31:

In file included from /Users/ynakakoshi/Documents/onbase/LAStools/LASlib/inc/lasreader.hpp:55:

In file included from /Users/ynakakoshi/Documents/onbase/LAStools/LASlib/inc/lasdefinitions.hpp:63:

/Users/ynakakoshi/Documents/onbase/LAStools/LASzip/src/laspoint.hpp:303:9: warning: unknown pragma ignored [-Wunknown-pragmas]

  303 | #pragma warning(push)

      |         ^


[中略]


29 warnings generated.

21 warnings generated.

[100%] Linking CXX executable /Users/ynakakoshi/Documents/onbase/LAStools/bin64/lasinfo64

[100%] Built target lasinfo

build % 


ビルドが正常に完了するとリポジトリをクローンしたディレクトリLAStoolsの下にディレクトリbin64が作成され、そこに一連のLAStoolsのOpen source toolsのバイナリが配置されます。

cd ..
ls bin64

build % cd ..

LAStools % ls bin64

las2las64 lascopcindex64 lasindex64 lasmerge64 laszip64

las2txt64 lasdiff64 lasinfo64 lasprecision64 txt2las64

LAStools %


これらのコマンドを使用するために、環境変数PATHに含めます。

export PATH=$PWD/bin64:$PATH

LAStools % export PATH=$PWD/bin64:$PATH

LAStools % 


ビルドしたLAStoolsを使用して、先ほど解凍したLasファイル09QC6295.lasよりCSVファイルを生成します。

等高線を生成するにあたって地表面だけを対象にします。建物・樹木などは含まないように、LasデータよりClass 1/9のDSM(陸部)データを除きます。las2las64を実行し、Class 2/4、12、22のみを取り出します。

mv 09QC6295.las 09QC6295_all.las
las2las64 -i 09QC6295_all.las -o 09QC6295.las -keep_class 2 4 12 22

onbase % mv 09QC6295.las 09QC6295_all.las

onbase % las2las64 -i 09QC6295_all.las -o 09QC6295.las -keep_class 2 4 12 22

onbase %


las2txt64を実行し、LasデータをCSVに変換します。parseオプションにxyzirnedcaRGBを指定していますが、今回使用するデータは最初のxyzの3列のみです。

las2txt64 -i 09QC6295.las -o 09QC6295.csv -parse xyzirnedcaRGB -sep comma

onbase % las2txt64 -i 09QC6295.las -o 09QC6295.csv -parse xyzirnedcaRGB -sep comma

onbase % ls -l 09QC6295.*

-rw-r--r--  1 ********  staff  161142749 10月 14 12:22 09QC6295.csv

-rw-r--r--  1 ********  staff   81351793 10月 14 12:18 09QC6295.las

-rw-r--r--@ 1 ********  staff   82706254 10月 14 11:23 09QC6295.zip

onbase % 


headコマンドでCSVファイルの先頭データを確認します。ヘッダー行を含まず、parseオプションで指定したデータが、カンマ区切りで列記されています。

head 09QC6295.csv

onbase % head 09QC6295.csv

-69888.740,-200700.000,0.810,11078,1,1,0,1,2,3,41728,43264,40704

-69749.450,-200999.240,0.930,14406,1,1,0,1,2,-11,41984,40192,35328

-69749.630,-200999.050,1.480,6361,1,1,0,1,2,-11,44288,44032,40192

-69749.360,-200999.090,1.320,9665,1,1,0,1,2,-11,45824,44544,38656

-69751.120,-200997.910,0.880,8949,1,1,0,1,2,-11,44544,44544,40704

-69750.600,-200998.300,1.580,3854,1,1,0,1,2,-11,41728,41984,38912

-69750.340,-200998.390,1.590,3962,1,1,0,1,2,-11,43264,43008,39680

-69750.080,-200998.550,1.830,3112,1,1,0,1,2,-11,45568,45824,40960

-69749.830,-200998.710,2.060,3530,1,1,0,1,2,-11,41984,42240,39168

-69749.560,-200998.790,2.010,3812,1,1,0,1,2,-11,40448,41216,38912

onbase % 



CSVをSDO_PC型の点群データとして保存



データベースに表を作成し、フラットな点群データとしてCSVファイルのデータをアップロードします。その後、SDO_PC型の点群データに変換します。CSVのアップロードにはSQLclのLOADコマンドを使用します。

CSVのアップロード先となる表を作成します。名前はLIDAR_POINTSとします。
drop table if exists lidar_points;
create table lidar_points (
  x                     NUMBER,        -- X
  y                     NUMBER,        -- Y
  z                     NUMBER,        -- Z
  intensity             NUMBER,        -- i => Intensity
  return_number         NUMBER,        -- r => Return Number
  number_of_returns     NUMBER,        -- n => Number of Returns
  edge_of_flight_line   NUMBER,        -- e => Flightline Edge
  scan_direction_flag   NUMBER,        -- d => Scan Direction Flag
  classification        NUMBER,        -- c => Classification
  scan_angle_rank       NUMBER,        -- a => Scan Angle Rank
  r                     NUMBER,        -- R => Color red (2 bytes [0-65536])
  g                     NUMBER,        -- G => Color green (2 bytes [0-65536])
  b                     NUMBER         -- B => Color blue (2 bytes [0-65536])
)
nologging;
最終的に生成した等高線はAPEXのマップに表示するため、APEXのワークスペース・スキーマに表を作成し、CSVをアップロードします。

onbase % sql wksp_apexdev@localhost/freepdb1



SQLcl: 火 10月 14 12:34:48 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 lidar_points;


Table LIDAR_POINTSが削除されました。


SQL> create table lidar_points (

  2    x                     NUMBER,        -- X

  3    y                     NUMBER,        -- Y

  4    z                     NUMBER,        -- Z

  5    intensity             NUMBER,        -- i => Intensity

  6    return_number         NUMBER,        -- r => Return Number

  7    number_of_returns     NUMBER,        -- n => Number of Returns

  8    edge_of_flight_line   NUMBER,        -- e => Flightline Edge

  9    scan_direction_flag   NUMBER,        -- d => Scan Direction Flag

 10    classification        NUMBER,        -- c => Classification

 11    scan_angle_rank       NUMBER,        -- a => Scan Angle Rank

 12    r                     NUMBER,        -- R => Color red (2 bytes [0-65536])

 13    g                     NUMBER,        -- G => Color green (2 bytes [0-65536])

 14    b                     NUMBER         -- B => Color blue (2 bytes [0-65536])

 15  )

 16* nologging;


Table LIDAR_POINTSは作成されました。


SQL> 


SQLclのLOADコマンドを実行し、CSVファイルを表LIDAR_POINTSにアップロードします。データ量が多いのでバッチで処理する行を10000まで増やします。また、位置で列をマップするようにオプションを設定します。

set load batch_rows 10000
set loadformat column_names off
load lidar_points 09QC6295.csv

おおよそ240万の点群が表LIDAR_POINTSにロードされます。

SQL> set load batch_rows 10000

SQL> set loadformat column_names off

SQL> load lidar_points 09QC6295.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.LIDAR_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 処理された行数: 2,392,691

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

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

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

SQL> 


座標X、YおよびZの最小値と最大値を確認します。座標参照系は、日本測地系2011/平面直角座標系第9系(EPSG:6677)です。
SELECT
  MIN(x) as min_x, MAX(x) as max_x,
  MIN(y) as min_y, MAX(y) as max_y,
  MIN(z) as min_z, MAX(z) as max_z
FROM lidar_points;

SQL> SELECT

  2    MIN(x) as min_x, MAX(x) as max_x,

  3    MIN(y) as min_y, MAX(y) as max_y,

  4    MIN(z) as min_z, MAX(z) as max_z

  5* FROM lidar_points;


    MIN_X     MAX_X      MIN_Y      MAX_Y     MIN_Z    MAX_Z 

_________ _________ __________ __________ _________ ________ 

   -70000    -69600    -201000    -200700    -12.97    65.08 


SQL> 


Z方向が-12.97mから65.08mの間にデータがあるので、生成できる等高線もこの間になります。

このアップロードしたフラットな点群データを、Oracle SpatialでPoint Cloudを扱うデータ型であるSDO_PC型のデータとして保存します。以下のスクリプトを実行します。


SQL> @create_point_cloud


Table LIDAR_PC_BASEが削除されました。



Table LIDAR_PC_BASEは作成されました。



Table LIDAR_PC_BLKが削除されました。



Table LIDAR_PC_BLKは作成されました。


Point Cloudオブジェクトが初期化されました



PL/SQLプロシージャが正常に完了しました。



Table LIDAR_INPUT_PCが削除されました。



Table LIDAR_INPUT_PCは作成されました。


Point Cloudの作成が完了しました



PL/SQLプロシージャが正常に完了しました。


SQL>


スクリプトの実行結果を確認します。ブロックテーブルである表LIDAR_PC_BLKの内容を表示します。
SELECT
  COUNT(*) as block_count,
  SUM(num_points) as total_points,
  MIN(num_points) as min_points_per_block,
  MAX(num_points) as max_points_per_block,
  AVG(num_points) as avg_points_per_block
FROM lidar_pc_blk;
SDO_PC_PKG.INITを呼び出す際に点群のパーティション化のパラメータ(引数ptn_params)に、blk_capacity=1000を設定しているため、MAX_POINTS_PER_BLOCKは10000になっています。TOTAL_POINTSは表LIDAR_POINTS(および表LIDAR_INPUT_PC)の行数と一致します。

以上より、Point Cloudに指定した点群がロードされていることが確認できます。

SQL> SELECT

  2    COUNT(*) as block_count,

  3    SUM(num_points) as total_points,

  4    MIN(num_points) as min_points_per_block,

  5    MAX(num_points) as max_points_per_block,

  6    AVG(num_points) as avg_points_per_block

  7* FROM lidar_pc_blk;


   BLOCK_COUNT    TOTAL_POINTS    MIN_POINTS_PER_BLOCK    MAX_POINTS_PER_BLOCK                         AVG_POINTS_PER_BLOCK 

______________ _______________ _______________________ _______________________ ____________________________________________ 

           240         2392691                    6345                   10000    9969.545833333333333333333333333333333333 


SQL> 


生成した等高線を保存する表をCONTOUR_LINESとして作成します。
drop table if exists contour_lines purge;
create table contour_lines (
  contour_id number generated by default as identity primary key,
  elevation  number,
  geom_6677  mdsys.sdo_geometry,   -- EPSG:6677
  geom_wgs84 mdsys.sdo_geometry,   -- EPS_G:4326
  geom       mdsys.sdo_geometry    -- sdo_util.remove_duplicate_vertices
);

SQL> drop table if exists contour_lines purge;


Table CONTOUR_LINESが削除されました。


SQL> create table contour_lines (

  2    contour_id number generated by default as identity primary key,

  3    elevation  number,

  4    geom_6677  mdsys.sdo_geometry,   -- EPSG:6677

  5    geom_wgs84 mdsys.sdo_geometry,   -- EPSG:4326

  6    geom       mdsys.sdo_geometry    -- sdo_util.remove_duplicate_vertices

  7* );


Table CONTOUR_LINESは作成されました。


SQL> 


以下のスクリプトを実行し、等高線を生成します。for文で0..6を指定し、forループの内部でl_elevation、つまり等高線の標高に0, 10, 20, 30, 40, 50, 60を順次与えて、一本ずつ等高線を生成しています。SDO_PC_PKG.CREATE_CONTOUR_GEOMETRIESの引数elevationsには複数の値が設定でき、また引数elevations_max、elevations_min、elevations_intervalを指定することにより、等高線を生成する範囲と増分を指定することも可能です。

標高0mから60mまで、7本の等高線を生成するのに1日くらいかかったので、実際に実行するのであれば、リソースの潤沢な環境で実行するか、生成する等高線の本数を減らすことをお勧めします。

上記の処理が完了すると、生成された等高線が表CONTOUR_LINESの列GEOM_6677に、SDO_GEOMETRYのLINESTRINGとして保存されます。

select elevation, sdo_util.to_wktgeometry(geom_6677) from contour_lines;

SQL> select elevation, sdo_util.to_wktgeometry(geom_6677) from contour_lines;


   ELEVATION SDO_UTIL.TO_WKTGEOMETRY(GEOM_6677)                                                  

____________ ___________________________________________________________________________________ 

           0 LINESTRING (-69967.3933570539 -200999.5, -69967.5 -200999.350083891, -69969.5 -2    

          10 LINESTRING (-69893.5 -200905.218388821, -69895.087159423 -200905.5, -69895.5 -20    

          20 LINESTRING (-9893.5 -200913.191513301, -9894.87498406222 -200913.5, -9895.5 -200    

          30 LINESTRING (-9895.5 -200919.186358386, -9895.84202419422 -200919.5, -9897.5 -200    

          40 LINESTRING (-69774.0662795354 -200811.5, -69775.5 -200810.867211666, -69777.5 -2    

          50 LINESTRING (-9725.5 -200809.466071466, -9727.5 -200808.938161045, -9729.5 -20080    

          60 LINESTRING (-9767.5 -200881.000708521, -9769.5 -200880.815372578, -9770.44721335    


7行が選択されました。 


SQL> 


等高線の間隔は一般にメートルを単位とします。そのため、座標参照系は、日本測地系2011/平面直角座標系第9系のまま作業しています。

APEXのマップに等高線を表示するには、参照座標系を世界測地系1984(EPSG:4326)に変換する必要があります。EPSG:4326に変換したデータを保持するための列GEOM_WSG84は作成済みです。列GEOM_6677の値を世界測地系に変換して、GEOM_WSG84に保存します。
update contour_lines set geom_wgs84 =
    SDO_CS.MAKE_2D(
        SDO_CS.TRANSFORM(
            geom_6677,
            4326
       )
    );
commit;

SQL> update contour_lines set geom_wgs84 =

  2      SDO_CS.MAKE_2D(

  3          SDO_CS.TRANSFORM(

  4              geom_6677,

  5              4326

  6         )

  7*     );


7行更新しました。


SQL> commit;


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


SQL> 


世界測地系84に更新されたので、本来であればこれでAPEXのマップに等高線を表示できます。しかし、現状ではデータに問題があります。APEXのマップはMapLibre GL JSをライブラリとして使用しており、SDO_GEOMETRYのデータはGeoJSONに変換してマップ上に表示します。

select sdo_util.to_geojson(geom_wgs84) from contour_lines;

現状ではORA-13199: GeoJSON supports only a straight lineが発生し、等高線のデータをGeoJSONに変換できません。

SQL> select sdo_util.to_geojson(geom_wgs84) from contour_lines;


次のコマンド行の開始中にエラーが発生しました : 1 -

select sdo_util.to_geojson(geom_wgs84) from contour_lines

コマンド行 : 1 列 : 8 でのエラー

エラー・レポート -

SQLエラー: ORA-13199: GeoJSON supports only a straight line

ORA-06512: "MDSYS.SDO_UTIL", 行9150

ORA-06512: "MDSYS.SDO_UTIL", 行9171


https://docs.oracle.com/error-help/db/ora-13199/13199. 00000 -  "%s"

*Cause:    This is an internal error.

*Action:   Contact Oracle Support Services.


More Details :

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

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

SQL> 


データをクリーンアップするため、SDO_UTIL.REMOVE_DUPLICATTE_VERTICESを実行します。
update contour_lines set geom =
    sdo_util.remove_duplicate_vertices(
        geom_wgs84,
        1e-6
    );
commit;

SQL> update contour_lines set geom =

  2      sdo_util.remove_duplicate_vertices(

  3          geom_wgs84,

  4          1e-6

  5*     );


7行更新しました。


SQL> commit;


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


SQL> 


以上で表CONTOUR_LINESの列GEOMに、APEXのマップ上に表示できる等高線のデータが保存されました。


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



アプリケーション作成ウィザードを起動します。アプリケーションの名前等高線とします。

マップのページを追加するため、ページの追加をクリックします。


マップを選択します。


ページ名等高線とします。CONTOUR_LINES形式を選択します。

ジオメトリ列GEOMツールチップ列ELEVATIONを選択します。

以上でページの追加をクリックします。


マップがページとして追加されました。

アプリケーションの作成を実行します。


等高線をマップ上に表示するAPEXアプリケーションが作成されました。


アプリケーションを実行して、マップ上に表示される等高線を確認します。

マップを確認すると、1ケ所は正しく恩馳島の上に等高線が表示されていますが、それ以外に3ケ所、不正な場所に等高線が表示されます。


デフォルトの外観では見にくいため、ストロークの色#000000)、ストロークの幅1に設定します。


島の上に表示されている等高線は問題なさそうです。


海上に表示されている等高線は問題があります。おそらくSDO_PC_PKG.CREATE_CONTOUR_GEOMETRIESの不具合と思われます。


ワークアラウンドを適用します。

等高線は元々以下の範囲に存在する点群のデータから生成されています。従って、等高線も以下の範囲に限定されます。

SQL> SELECT

  2    MIN(x) as min_x, MAX(x) as max_x,

  3    MIN(y) as min_y, MAX(y) as max_y,

  4    MIN(z) as min_z, MAX(z) as max_z

  5* FROM lidar_points;


    MIN_X     MAX_X      MIN_Y      MAX_Y     MIN_Z    MAX_Z 

_________ _________ __________ __________ _________ ________ 

   -70000    -69600    -201000    -200700    -12.97    65.08 


SQL> 


X方向で-70000から-69600、Y方向で-20100から-200700の範囲に含まれる等高線のみを、新たな表CONTOUR_LINES2へ保存します。

以下のスクリプトを実行します。参照系がEPSG:6677のデータである列GEOM_6677の等高線の範囲を限定した後、EPSG:4326への変換とクリーンアップを実施しています。


先ほど作成したマップのレイヤーソース表名CONTOUR_LINESから、ワークアラウンドを適用した表CONTOUR_LINES2に置き換えます。


等高線のデータを点群が存在する領域に限定する(点群が存在する領域以外の等高線は削除する)ことにより、恩馳島上にのみ等高線が表示されるようになりました。



オープンデータの等高線を表示する



東京都デジタルツインの島しょ地域点群データには等高線が含まれています。データの形式はDXFです。


Lasと同様に、恩馳島の1つのメッシュ09QC6295のDXFデータをOracle DatabaseにロードしてAPEXのマップ上に表示してみます。

DXFのデータベースへのロードは、GDALのogr2ogrコマンドで実行できます。GDALのogr2ogrコマンドの実行方法については、記事「GDALのogr2ogrを使ってShapefileをOracle DatabaseのSDO_GEOMETRY列にロードする」で紹介しています。

Oracle Databaseのドライバを組み込んだGDALがインストールされているコンテナを起動します。マウントしている作業ディレクトリに、DXFファイル09qc6295.dxfを配置します。

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

以下のコマンドを実行します。ファイル09qc6295.dxfに記載されている等高線(1m間隔)が、表CONTOUR_LINES_FULLの列GEOMにSDO_GEOMETRY型で保存されます。
ogr2ogr -f OCI -overwrite \
OCI:wksp_apexdev/********@host.containers.internal/freepdb1 \
09qc6295.dxf \
-oo ENCODING=CP932 \
-nln CONTOUR_LINES_FULL -lco GEOMETRY_NAME=GEOM -lco SRID=4326 -lco DIM=2 \
-s_srs EPSG:6677 \
-t_srs EPSG:4326 \
-skipfailures

bash-5.1$ ogr2ogr -f OCI -overwrite \

OCI:wksp_apexdev/oracle@host.containers.internal/freepdb1 \

09qc6295.dxf \

-oo ENCODING=CP932 \

-nln CONTOUR_LINES_FULL -lco GEOMETRY_NAME=GEOM -lco SRID=4326 -lco DIM=2 \

-s_srs EPSG:6677 \

-t_srs EPSG:4326 \

-skipfailures

bash-5.1$ 


ogr2ogrの実行結果を、APEXマップにレイヤーを追加して確認します。

レイヤー名前オープンデータソース表名CONTOUR_LINES_FULL列のマッピングジオメトリ列GEOMを設定します。


ページを実行し、表示するレイヤーをオープンソースに限定します。

DXFをインポートしたデータは、APEXマップで以下のように表示されます。


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

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

追記


Claude DesktopにSQLclのMCPサーバーを組み込み、かなりの部分のSQLおよびPL/SQLのコードを生成しています。Oracle Spatialのファンクションを含んだコードでも、かなり正しく生成します。