はじめに
GridDB SE は、空間/位置情報データをサポートし、開発者の方々に便利に使っていただける製品です。 これまでのブログでは、GridDB SEのいくつかの機能を詳しくご紹介してきました。このブログでは、導入事例に基づいて説明します。
空間/位置情報データのサポートとは、GEOMETRYカラムのサポートと、ライン、シェイプ、3次元オブジェクトなどの多くのタイプの空間データをモデル化して利用できるメソッドを追加することを意味します。 さらに、これらのジオメトリはポイントまたは方程式で作成することができます。 ジオメトリデータを使用するGIS(地理情報システム)の最も一般的なドメインの1つは、地理的アプリケーション用で、たとえばジオロケーション、地図、交通システムなどがあります。
空間クエリの作成
GEOMETRYカラムを使うことにより、フィルタリングのためのさまざまな方法を用いてある特定の場所を検索することができます。 ある例では、使用されるジオメトリは POINTS で ST_MBRIntersects() TQL操作を使用して特定の場所を検索します。 たとえば、交差クエリを使用して、緯度36.48度、緯度73.34度、標高0.6フィートの地点を検索する、というようなことができます。
また、ジオメトリ値を使用して3Dボリュームまたは2D領域内のポイントを検索することもできます。 ここでは、これらのジオメトリには、2次曲面、立方体、3Dオブジェクト、領域、3Dジオメトリ面が含まれます。 GridDBは、TQLの交差関数を使用して、これらの異なるタイプのすべてを一度にすべてフィルタリングします。
特定のジオメトリ的範囲内にカメラ位置があるかどうかを確認するための最も整合性のある方法は、 POLYHEDRALSURFACE を作成し、そのジオメトリをTQLクエリに挿入することです。 まず、 WKT (Well-Known Text)フォームでジオメトリを入力します。 その後ジオメトリ値が選択したGEOMETRYタイプのカラムと交差しているかどうかを確認してください。 ジオメトリテキストは ST_GeomFromText 操作で変換できます。 たとえば、緯度36.48度、経度73.34度、および16フィートの標高に位置するカメラを検索したいとします。 この位置は、 POINT(36.48 73.34 16)
で表すことができます。このようにしてジオメトリが確立されたら、以下のTQLクエリを発行することができます。
SELECT * WHERE ST_MBRIntersects(geometry_column,ST_GeomFromText(‘POINT(36.48 73.34 16)’))
ポイントまたはエリアのフィルタリング
In these applications, the types of geometries used for querying were:
- QUADRATICSURFACE
- POINT
- POLYGON
- LINESTRING
- POLYHEDRALSURFACE
POINT
ジオメトリを使用して個々の位置や点をモデリングする必要があります。
地理的領域または3Dジオメトリ平面は、 POLYGON
ジオメトリを使用してモデル化できます。
一方、ピラミッド、キューブ、その他の多面体などの3Dオブジェクトは、 POLYHEDRALSURFACE
で使用できます。 多面体は、それぞれの辺または面を POLYGON
形式で作成することで形成できます。 (閉じたオブジェクトを形成するためにはすべての側面が接している必要があります)。
より複雑な面または形状は、 QUADRATICSURFACE
WKTまたは ST_MakeQSF()
および ST_QSFMBRIntersects()
TQL関数で作成することができます。
スマートトラフィックカメラ
最初の小さなプログラム StreetLightApplication.java では、データベースのすべての行がストリートカメラを表しています。 このアプリケーションの最初のコンテナはコレクションになります。 @RowKey
はSTRING IDになります。 このコレクションの各行には、各カメラの場所、インストール日、ID、その他の情報が含まれています。
Row Schema (StreetCamera.java)
public class StreetCamera { @RowKey public String cameraId; public Geometry coordinates; public Date installation; public int carsSeen; public int violations; //(snip)
実際に導入する場合、ストリートカメラの管理担当者は、自分のいる位置に応じてカメラを選択することができます。 これは、正確な位置または地理的範囲内または標高範囲内にあるカメラのフィルタリングに変換される可能性があります。 ユーザーは、四角形や境界ボックスとしての視覚化が難しいような、より複雑な空間範囲を作成することもできます。 これらの異なる範囲の照会はすべて、交差ステートメントを使用して実行できます。
コード例
Geometry point = Geometry.valueOf("POINT(40.98 76.43 1.6)"); geometryLogic.getSearchResults(point); geometryLogic.searchZRange(0.8,0.3); geometryLogic.searchArea(40,79,45,85,true); geometryLogic.searchVolume(36,73,0.6,42,77,1.7,true); //(snip) double matrix[] = {0.8,0,0,0,0,0.8,0,0,0,0,0.8,0,0,0,0,-1}; geometryLogic.searchQuadraticSurface(matrix,true);
ヘルパー関数
上のコードからわかるように、 GeometryLogic
というヘルパーオブジェクトによって使用される、あらかじめ定義されたヘルパー関数がいくつかあります。 このオブジェクトは、 GridDBコレクションとそのジオメトリ列の1つを入力として受け取ります。 そこからこのオブジェクトは、そのコンテナに対して空間クエリを発行するために、関数を使用します。
コード例
Collection<String,StreetCamera> streetCollection = gridstore.putCollection("TrafficApplication_101",StreetCamera.class); GeometryLogic geometryLogic = new GeometryLogic(streetCollection,"coordinates");
GridDBでジオメトリクエリを発行するのに便利な GeometryLogic.java で使用されるクラスメソッドを次に示します。 実装と機能の詳細については、後のセクションでご説明します。
- formPoint()
- searchArea()
- searchVolume()
- formSurfaceWkt()
- searchXRange(), searchYRange(), searchZRange()
- searchQuadraticSurface()
- searchPlane()
ポイントの検索
空間データベースに対して行われる最も簡単なクエリは、ポイントに関するものです。 ユーザーは2つの値(2次元)または3つの値(3次元)を持つポイントを検索することができます。この機能を使用する GeometryLogic.java のメソッドは formPoint()
です。
public Geometry formPoint(double x, double y){ String wkt = String.format("POINT(%.9f %.9f)",x,y); Geometry point = Geometry.valueOf(wkt); return point; } public Geometry formPoint(double x, double y, double z){ String wkt = String.format("POINT(%.9f %.9f %.9f)",x,y,z); Geometry point = Geometry.valueOf(wkt); return point; }
3D空間範囲の設定
このアプリケーションでは、ユーザーが x 、 y 、 z の各方向でどの程度までクエリすることができるかについての制限はありません。 GridDBでは INF と -INF の値は使用できないため、 Double.MAX_VALUE
と -1 * Double.MAX_VALUE
はそれぞれデフォルトの最小値と最大値で、その範囲は変更することができます。 たとえば、緯度35.5度以上のカメラを検索する場合などです。 空間範囲の境界ボックスは、最小xが35.5で、デフォルトとして最大x、最小x、最小および最大y、最小および最大z値を持ちます。 前に述べたように、バウンディングボックスは POLYHEDRALSURFACE
です。 そのWKTフォームは以下のようになります。
- 1.79xe308 is the value for Double.MAX_VALUE)
バウンディングボックスの例
POLYHEDRALSURFACE( ((35.5 -1.79e308 -1.79e308,35.5 1.79e308 -1.79e308,1.79e308 1.79e308 -1.79e308,1.79e308 -1.79e308 -1.79e308,35.5 -1.79e308 -1.79e308)), ((35.5 -1.79e308 -1.79e308,35.5 1.79e308 -1.79e308,35.5 1.79e308 1.79e308,35.5 -1.79e308 1.79e308,35.5 -1.79e308 -1.79e308)), ((35.5 -1.79e308 -1.79e308,1.79e308 -1.79e308 -1.79e308,1.79e308 -1.79e308 1.79e308,35.5 -1.79e308 1.79e308,35.5 -1.79e308 -1.79e308)), ((1.79e308 1.79e308 1.79e308,1.79e308 -1.79e308 1.79e308,35.5 -1.79e308 1.79e308,35.5 1.79e308 1.79e308,1.79e308 1.79e308 1.79e308)), ((1.79e308 1.79e308 1.79e308,1.79e308 -1.79e308 1.79e308,1.79e308 -1.79e308 -1.79e308,1.79e308 1.79e308 -1.79e308,1.79e308 1.79e308 1.79e308)), ((1.79e308 1.79e308 1.79e308,1.79e308 1.79e308 -1.79e308,35.5 1.79e308 -1.79e308,35.5 1.79e308 1.79e308,1.79e308 1.79e308 1.79e308)))
多くの点を入力しなくてはならず手間のかかる作業です。 このジオメトリを作成するポイント数は30ですが、実際には6ポイントしか必要ありません。 幸いなことにこのアプリケーションでは、これを簡単にする関数を持つクラスがGeometryLogic.javaの中にあります。
public String formSurfaceWkt(double minX,double minY,double minZ,double maxX,double maxY,double maxZ){ String bottomFace = String.format("((%.9f %.9f %.9f, %.9f %.9f %.9f, %.9f %.9f %.9f, %.9f %.9f %.9f, %.9f %.9f %.9f))",minX,minY,minZ,minX,maxY,minZ,maxX,maxY,minZ,maxX,minY,minZ,minX,minY,minZ); String leftFace = String.format("((%.9f %.9f %.9f, %.9f %.9f %.9f, %.9f %.9f %.9f, %.9f %.9f %.9f, %.9f %.9f %.9f))",minX,minY,minZ,minX,maxY,minZ,minX,maxY,maxZ,minX,minY,maxZ,minX,minY,minZ); String frontFace = String.format("((%.9f %.9f %.9f, %.9f %.9f %.9f, %.9f %.9f %.9f, %.9f %.9f %.9f, %.9f %.9f %.9f))",minX,minY,minZ,maxX,minY,minZ,maxX,minY,maxZ,minX,minY,maxZ,minX,minY,minZ); //(snip) String wkt = \ String.format("POLYHEDRALSURFACE(%s,%s,%s,%s,%s,%s)",bottomFace,leftFace,frontFace,topFace,rightFace,backFace); return wkt; }
軸の範囲のクエリ
場合によっては6ポイントのサーフェスを作成しなければならない場合もあります。 このアプリケーションの GeometryLogic クラスを使用して、x軸、y軸またはz軸に制限値を設定することもできます。
setXRange()
、 setYRange()
、または setZRange()
の各関数は、1つまたは2つのパラメータで空間範囲を作成できます。 1つの数値のみを使用する場合は、 BoundingOption
という名前の列挙型という形式で追加パラメータを指定する必要があります。
BoundingOptions では、値を境界ボックスの最小値または最大値として設定するかどうかを指定するだけです。 BoundingOption.GREATER
は値を範囲の最小値として設定し、 BoundingOption.LESSER
は値を範囲の最大値として設定することを意味します。
たとえば、緯度32度以上の場所にあるストリートカメラのみを選択する場合は、 次の通り実行します。
GeometryLogic geometryLogic = new GeometryLogic(collection,column); geometryLogic.searchXRange(32,BoundingOption.GREATER);
緯度32度のカメラで選択する場合は、 以下を実行してください。
geometryLogic.searchXRange(32,BoundingOption.LESSER);
範囲の最小値と最大値を設定する場合は、2つの数値で関数を呼び出します。緯度 116度と緯度132度の間にある通りのカメラだけを選択したい場合は、 以下を実行します。
geometryLogic.searchYRange(116.0,132.0);
public void searchXRange(double firstX, double secondX) { Geometry box = Geometry.valueOf("LINESTRING(EMPTY)"); if(firstX == secondX){ getSearchResults(box); return; } double min = firstX; double max = secondX; if(firstX > secondX){ min = secondX; max = firstX; } minY = min; maxY = max; box = formBoundingSurface(Dimension.THREE_DIMENSIONAL); getSearchResults(box); setInitialRanges(); }
2次元領域
特定の地理的範囲内にあるカメラをフィルタリングすることもできます。 例えば、都市や通りなどの領域をフィルタリングしたい場合などに使用できます。たとえば、緯度40度から緯度45度の間かつ経度79度から85度の間の都市にあるすべてのカメラを選択するとします。
geometryLogic.searchArea(40,79,45,85,true);
この検索を実行するためには、4つの値からなる交差クエリの POLYGON
シェイプを作成する必要があります。これら4つの値は、その範囲の角を特定するために使用され、それによりすべてのコーナーを接続した閉じた形にすることができます。 これを実装するメソッドは formBoundingSquare()
です。
public String formBoundingSquare(double minX, double minY, \ double maxX, double maxY){ String bottomLeft = String.format("%.9f %.9f",minX,minY); String bottomRight = String.format("%.9f %.9f",maxX,minY); String topLeft = String.format("%.9f %.9f",minX,maxY); String topRight = String.format("%.9f %.9f",maxX,maxY); String wkt = String.format("POLYGON((%s,%s,%s,%s,%s))", bottomLeft,bottomRight,topRight,topLeft,bottomLeft); return wkt; }
ジオメトリ平面に対する探索
探索したいエリアが3次元であったり、四角形ではなかったりする場合もあるでしょう。そのような場合は、三角形を形成し、特定の高さの領域にあるカメラを検索することができます。 以下の例では、高さは0.6になります。
List<Double> planePoints = new ArrayList<Double>(); planePoints.add(38.23); planePoints.add(74.34); planePoints.add(0.6); planePoints.add(43.11); planePoints.add(78.67); planePoints.add(0.6); planePoints.add(40.98); planePoints.add(76.43); planePoints.add(0.6); System.out.println(); geometryLogic.searchPlane(planePoints,Dimension.THREE_DIMENSIONAL,true);
public String formPlane(List<Double> points, Dimension dimension){ String polygon = "POLYGON(("; int stopIndex = 2; if(dimension == Dimension.THREE_DIMENSIONAL){ stopIndex = 3; } double value; //(snip) value = (double) points.get(i); //(snip) return polygon; }
2次問合せ
一般には、確認したい範囲はいくつかのポイントによって作成される場合が多いのですが、時には2次曲線によって範囲が形成されることもあります。そのような場合は、 以前のブログ で述べたように、 16要素の数字の行列が必要です。
以下の関数 makeQSFTql()
はジオメトリ交差TQLクエリを形成します。 その入力は係数の行列を取り込み、それらを使ってすぐに QUADRATICSURFACE
ジオメトリを作成するのに役立ちます。 このジオメトリは、交差をテストするために使用されるものです。
public String formQSFTql(String column,double matrix[],boolean include){ int max = 16; int stopIndex = matrix.length > 16 ? max : matrix.length; String wkt = ""; String exclude = include ? " " : " NOT"; //(snip) wkt += (String.valueOf(matrix[i])); //(snip) } String tql = String.format("%s %s ST_QSFMBRIntersects(ST_MakeQSF(%s),%s)", \ QUERY_PREFIX,exclude,wkt,column); return tql; }
スマートビル
アプリケーションフォルダ内の2つ目のプログラムは、建物の部屋に関して機能します。 すべての部屋が、モニターが必要な機能をそれぞれ有していると考えられます。あるエリアに関して独自の機能を持つ部屋が、他のエリアに関しては他の部屋と関連しているという場合もあります。たとえばホテルの客室では、 複数の客室で同じ配管、電気接続回路などを共有しており、客室間が交差する方法は、建物のレイアウト方法や使用する回路図によってさまざまです。 この機能により、空間データを格納したり、空間クエリを有効に使うことができます。
Row Schema (SmartBuilding.java)
public class SmartBuilding { @RowKey public String buildingId; public Geometry layout; public double volume; public Date installation; public String name; //(snip)
Intersection Queries (BuildingApplication.java)
geometryLogic.searchVolume(0,5,0,5,0,5,true); geometryLogic.searchXRange(-5,-2); geometryLogic.searchArea(10,0,15,12,true); double matrix[] = {0.5,0,0,0,0.5,0,0,0,0.5,0,0,0,-1,0,0,0}; geometryLogic.searchQuadraticSurface(matrix,true);
IoT パイプモニター
その他のアプリケーションとして、パイプに関連するものもあり、メンテナンス担当者によってモニターされる地下のパイプなども含まれます。 パイプのジオメトリは、 LINESTRING
ジオメトリとして表されます。 パイプに関連するデータは、半径とその地面下の高度で表されることが多いでしょう 。行スキーマは PipeMonitor.java
にあります。
Row Schema (PipeMonitor.java)
public class PipeMonitor { @RowKey public String id; // Sensor Tag and Container RowKey public Geometry pipe; // Multi-Part Vector public Date installation; // When the sensor was installed and first connected to GridDB public double radius; // Radius of the pipe public double elevation; // Location in reference to sea level
スキーマが定義されたら、GridDBコレクションを挿入することができるようになります。 そこから、ユーザーは特定のエリア内にあるパイプを検索します。 また、個々のジオメトリまたは標高によってパイプを検索することもできます。
Sample Queries
Collection<String,PipeMonitor> pipeCollection = gridstore.putCollection(name,PipeMonitor.class); Query<PipeMonitor> apiQuery = pipeCollection.query(column,Geometry.valueOf("LINESTRING(1 1 1, 1 0 1, 1 0 0)"),GeometryOperator.INTERSECT); //(snip) String tql = "SELECT * WHERE NOT ST_MBRIntersects(pipe,ST_GeomFromText('LINESTRING(7 2 0, 7 2 4)')) AND elevation > -5.3";
トラフィックカメラのTimeseries
このTimeseriesプログラムでは、ジオメトリ値を使用してカメラの画像の領域を表します。 さらにこのコンテナは、カメラが時間とともに撮影したトラフィックの画像を保持することができます。 この時系列の各行には、画像のタイムスタンプ、画像のURLとそのデータ、画像が表すトラフィックのジオメトリ的領域、画像内の車の量、画像が撮影された高さなどが含まれています。 行スキーマファイルは TrafficCamera.java
です。
Row Schema
public class TrafficCamera { @RowKey public Date timestamp; public String id; public String url; public Geometry area; public int cars; public double elevation; public Blob image;
すべての TimeSeries
行がGridDBに挿入されたら、空間クエリを行うことができます。 ほとんどの場合、ユーザーはこの後に2D領域を作成して、交差する画像の領域を確認するでしょう。
Intersection Queries
String name = "CameraTs121"; TimeSeries<TrafficCamera> timeseries = gridstore.putTimeSeries(name,TrafficCamera.class); String tql = "SELECT * WHERE ST_MBRIntersects(area,ST_GeomFromText('POLYGON((8 8, 9 8, 9 9, 8 9, 8 8))'))"; Query<TrafficCamera> query = timeseries.query(tql);
ソースコード
このブログに使用されているすべてのコードは、以下からダウンロードできます。
geometry-sample.tar.gz
ブログの内容について疑問や質問がある場合は Q&A サイトである Stack Overflow に質問を投稿しましょう。 GridDB 開発者やエンジニアから速やかな回答が得られるようにするためにも "griddb" タグをつけることをお忘れなく。 https://stackoverflow.com/questions/ask?tags=griddb
[…] GridDB Community Edition 4.1以降のバージョンは、開発者がアプリケーションで時系列分析と地理空間分析の両方を組み合わせることができるGeometryデータタイプを備えています。 以前に GridDBでのジオメトリ値の使用と、ジオメトリデータを活用したアプリケーションのブログで、GridDBのジオメトリ機能について説明しました。 […]