GridDBでのジオメトリ値の使用

はじめに

データは時間的にも空間的にも様々な形式で提供されます。GridDBの時系列データコンテナを使用することで、TQLおよび時間特有のAPI操作とともに、データの時間的側面をカバーすることができます。 GridDBのStandard EditionとAdvanced Editionでは地理空間データもカバーされます。ジオメトリの列とデータ型、TQLによる特殊なジオメトリ演算、C APIとJava APIによるサポートが提供されています。

GridDBでは、ジオメトリ値を作成して5つの一般的なモデルや図形を表現することができます。

POINT

2次元または3次元空間の座標点のことです。

2D and 3D Points

Geometry point = Geometry.valueOf("POINT(1 3.4)");
Geometry threeDPt = Geometry.valueOf("POINT(2 -3 0.43)");

points

LINESTRING

2次元または3次元空間内の1つ以上の相互接続された線の集合をLinestringといいます。 それらは2つ以上の点によって表されます。

POLYGON

2次元または3次元空間の閉領域をPolyogonといいます。 この領域は、開始点またはVERTEXから端点に接続され、開始点に戻る3つ以上の点で構成されます。 外側の領域から除外する内部領域を指定することで、モデルの一部ではないギャップまたはアイランドを持つ表面または領域を持つことができます。

2D Polygon

Geometry polygon = Geometry.valueOf("POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))");

3D Polygon

Geometry threeDSurface = Geometry.valueOf("POLYGON((0 0 0, 1 0 1, 1 1 1, 0 1 0, 0 0 0))");

example-polygons

POLYHEDRALSURFACE

POLYHEDRALSURFACEは、頂点、エッジ、および面で構成されます。 シンプルな四面体、多面体、八面体の例 POLYHEDRALSURFACEは3次元形状にしかなりません。 つまり、エッジを構成するすべての点に3つの座標が含まれているはずです。 多面体の表面は、その面が閉じた多角形の表面で構成されるボリュームを囲むことができるという点で、POLYGONの完全な3次元拡張と考えることができます。

Example

Geometry polySurface = Geometry.valueOf( \ 
"POLYHEDRALSURFACE ( ((0 0 0, 0 1 0, 1 1 0, 1 0 0, 0 0 0))," 
+ "((0 0 0, 0 1 0, 0 1 1, 0 0 1, 0 0 0 ))," 
+ "((0 0 0, 1 0 0, 1 0 1, 0 0 1, 0 0 0))," + 
"((1 1 1, 1 0 1, 0 0 1, 0 1 1, 1 1 1)) ," + 
"((1 1 1, 1 0 1, 1 0 0, 1 1 0, 1 1 1))," + 
"((1 1 1, 1 1 0, 0 1 0, 0 1 1, 1 1 1)) )");

すべての座標セットは、三次元の囲まれた表面の一部を構成する閉じた面を表すため、二重括弧(())で囲む必要があります。 表面をPOLYGONとみなして、表面の側面を構成します。 これらはすべてグループ化され、一緒に接続されて3次元表面オブジェクトを形成します。

Polyhedral Surface

QUADRATICSURFACE

三次元の二次曲面をQuadraticsurfaceといいます。 これらのオブジェクトは、方程式f(X) = <AX,X> + BX + Cによって三次元空間の曲面です。その面方程式を作る係数を表す16個の数字を挿入して面を作成します。
QUADRATICSURFACEは、WKT(Well-Known-Text形式)の入力が3D平面の座標ではなく方程式の係数である点で、他の4つのジオメトリオブジェクトとは異なります。 この方程式は、以下の通りです。

ax2+by2+cz2+2fyz+2gzx+2hxy+2px+2qy+2rz+d = 0

二次形式の行列に変換すると、次のようになります。

a h g p
h b f q
g f c r
p q r d

Example Surface

GSRow* row;
const GSChar* wkt = "QUADRATICSURFACE(( 84 2 3 5 6 7 8 9 10 12 13 14 15 16 17 18 ))";
// (snip)
gsGetNextRow(rowSet,row);
gsSetRowFieldByGeometry(row,4,wkt);
gsPutRow(timeseries,NULL,row,NULL);

二次曲面の例には、平行平面、双曲面、円錐、円柱および球が含まれます。 これらのタイプのサーフェスは、異なる多項式にマッピングされます。 これらの式の中には、特定の係数や係数を持たないものもあります。 たとえば、次のような式にxが存在しない可能性があります。

y2 + z2 - 1

等式にはx’sがないので、xの近くのすべての係数をa,h,pのように0に置き換えます。 QUADRATICSURFACE WKTの行列値を入力するときには、必ず16個の値があり、欠損値が0に置き換えられていなければなりません。

その他の例と二次曲面に関する情報は、このWolfram Alpha pageページにあります。

example-quadratics

Creating Objects with WKT Format

GridDBでこれらのオブジェクトを作成するのは非常に簡単で、開発者としてWKT形式でモデルを構築する方法を知っておくだけでよいでしょう。 WKT(Well-Known-Text)テキスト書式は、マップまたは空間参照系のベクトルジオメトリオブジェクトを表すために使用されるテキストマークアップ言語です。 WKT文字列が正しく解析されると、ジオメトリが作成され、それぞれの列にCollectionまたはTimeseriesに挿入されます。 ジオメトリオブジェクトが作成されない等のエラーを防ぐために、WKT形式には注意を払う必要があります。

General Formatting Rules

ジオメトリ(POINT、LINESTRINGなど)の形状を記述するときは、大文字と小文字を正しく使用していることを確認してください。 さもないとオブジェクトは作成されず、例外が発生する可能性があります。 書式設定が正しいこと、およびかっこの量が正しくないか、適切な間隔を使用しないなどの構文エラーがないことを確認してください。

空のジオメトリオブジェクトを作成する場合は、POINT(EMPTY)ジオメトリを作成します。

Empty LINESTRING and POLYGON

Geometry emptyPoint = Geometry.valueOf("POINT(EMPTY)");

一般的なWKTフォーマットの詳細については、こちらのWikipedia pageを参照してください。

Range Restrictions

いくつかのGISサポート(地理情報システム)データベースでは、ポイントの範囲または値に、NaNINF-INF(非数値、負および正の無限大)などの特殊な値を含めることができます。 正または負の浮動小数点数を含む数値型のみ使用できます。

Forming Closed Surfaces

通常、POINTおよびQUADRATICSURFACEを超えるジオメトリは、接続されているポイントで構成されます。 これらのポイントは、いったん一緒に接続されると、エッジを構成するラインを形成します。 エッジは通常、収束して閉じたサーフェスを形成します。 POINTを除くすべてのジオメトリでは、複数のデータポイントが必要です。

Dimensions of Geometry Values

ジオメトリを作成するポイントは、2次元または3次元の形式でなければなりません。 また、オブジェクトの輪郭を描くために点を作成する場合、寸法は一貫していなければなりません(つまり、2Dの座標は2Dの他のすべての座標を持つ必要があります)。 ポイントはコンマ,で区切られ、x,y,z座標はSPACESで区切られます。

Defining Geometric Columns and Indexes

ジオメトリ列は、静的または動的な方法でコンテナの列スキーマに追加できます。 ジオメトリはGridDBライブラリから直接インポートすることができ、コンテナのクラススキーマの列タイプとして設定できます。 また、SPATIALインデックスフラグを設定することによって、ジオメトリ列にインデックスを設定することもできます。

Java Container Creation

// (snip)
import com.toshiba.mwcloud.gs.Geometry;
import com.toshiba.mwcloud.gs.GeometryOperator;
//(snip)
static class Shape {
	@RowKey String name;
	double perimeter;
	double volume;
	Date creation;
	Geometry geom;

	public String toString(){
		return String.format("Shape: Name = %s, Perimeter = %f, \
                                   Volume = %f, Creation Date = %s, WKT = %s",\ 
                                name,perimeter,volume,creation.toString(),geom.toString());
//(snip)
Collection<String,Shape> geometryCollection = \ 
              gridstore.putCollection("GeometryCollection101",Shape.class);
// (snip)
geometryCollection.createIndex("geom",IndexType.SPATIAL);

GridDBのC APIには、明示的なGSGeometry構造体または列挙型はありません。ContainerInfoおよびColumnInfoオブジェクトを使用して動的スキーマを持つコンテナを作成する場合は、ColumnInfoのタイプGS_TYPE_GEOMETRYに設定します。 構造体バインディングを使用している場合は、ジオメトリとして設定する列の型をconst GSChar*型に設定します。 そこから、構造体バインディングを実行するときに、列の型を「ジオメトリ」に設定します。 カラム値をWKTフォームで設定するだけで、GridDBはそれをデータベース内のバイナリに相当するものに変換します。

C Container Creation

typedef struct {
	const GSChar* name;
	GSTimestamp timestamp;
	double volume;
	double perimeter;
	const GSChar* shape;
} GeometryRow;

GS_STRUCT_BINDING(GeometryRow,
	GS_STRUCT_BINDING_KEY(name, GS_TYPE_STRING)
	GS_STRUCT_BINDING_ELEMENT(timestamp,GS_TYPE_TIMESTAMP)
	GS_STRUCT_BINDING_ELEMENT(volume,GS_TYPE_DOUBLE)
	GS_STRUCT_BINDING_ELEMENT(perimeter,GS_TYPE_DOUBLE)
	GS_STRUCT_BINDING_ELEMENT(shape,GS_TYPE_GEOMETRY));
//(snip)
GSCollection* collection;
gsPutCollection(gridstore,"GeometryCollection311", \
GS_GET_STRUCT_BINDING(GeometryRow),NULL,GS_FALSE,&collection);

gsCreateIndex(collection,"shape",GS_INDEX_FLAG_SPATIAL);

GeometryRow gRow; 
gRow.name = "FirstPoint";
gRow.timestamp = gsCurrentTime();
gRow.volume = 0;

double x, y, z;
sscanf(wkt,"POINT(%lf %lf %lf)",&x,&y,&z);
gRow.perimeter = sqrt((x * x) + (y * y) + (z * z));
gRow.shape = "POINT(1 4.23 4.2)";

gsPutRow(collection,NULL,&gRow,NULL);

Spatial Operations in GridDB’s API

ジオメトリデータ値は、GridDBデータベースに挿入すると、BLOBデータ型とよく似たバイナリ形式で格納されます。 つまり、TQLでは直接等価操作を実行できません。

Example of Invalid TQL

SELECT * WHERE geom = ST_GeomFromText('POINT(2 4 5)') #(Throws Error)

Javaでは、GridDBのAPIを使用してジオメトリの値を直接比較することができます。

Geometry pointToFind = Geometry.valueOf("POINT(0 4)");
// (snip)
while(rowSet.hasNext){
     Row row = rowSet.next();
     Geometry rowPoint = row.getGeometry(4);
     boolean match = rowPoint.equals(pointToFind);
     System.out.println(match);

GridDBのAPIとTQLによって許可される1つの空間操作は、SPATIAL INTERSECTIONといいます。これは、2つのオブジェクト間の共通領域またはボリュームを検索することになります。 3D交差は、2つのオブジェクトが交差する直方体として知られる最小境界ボックスを決定することから成ります。オブジェクトが2次元の場合、z座標の範囲値は(-INF,INF)です。これは、3D次元オブジェクトで2D次元オブジェクトを照合することは、交差の可能性を高め、より包括的なz範囲を有することを意味します。
オブジェクトをPOINTと比較すると、境界ボックスの頂点は点で構成されます。ボックスの頂点はすべて同じ側にあり、長さは0です。POINTと非ポイントオブジェクトを比較すると、GridDBはそのバウンディングボックスにその領域内のポイントが含まれているかどうかをチェックします。
2つのPOLYGONオブジェクトのような点ではない図形を比較する場合、境界ボックスには交差点の空間構造を構成する最小x、y、z座標があります。

Examples of Spatial Intersections
spatial-intersection-thumbnail

Issuing Geometric Queries with GridDB API

Javaでのクエリー(String列、Geometry Geom、GeometryOperator演算子)またはgsQueryByGeometry()を発行することにより、Collectionコンテナに対してTQLを使用せずにすばやく照会を行うことができます。 このクエリはすべての行を検索して取得します
その ‘列’の値はconst GSChar *ジオメトリと交差します。

Java

Geometry intersection = Geometry.valueOf("POLYGON((0 0 0, 2 0 0, 2 2 0, 0 2 0, 0 0 0))");

Query query = geometryCollection.query("geom",intersection, \
 GeometryOperator.INTERSECT);

RowSet rowSet = query.fetch(false);
while(rowSet.hasNext()){
	Shape shape = rowSet.next();
	System.out.println(shape);

C

GSQuery* query;
GSRowSet* rowSet;
GeometryRow geometryRow;

const GSChar* point = "POINT(1 4.56 4.2)";
gsQueryByGeometry(collection,"shape",point,GS_GEOMETRY_OPERATOR_INTERSECT,&query);

gsFetch(query,GS_FALSE,&rowSet);

while(gsHasNextRow(rowSet)){
	gsGetNextRow(rowSet, &fetchedRow);
	GSChar timeStr[GS_TIME_STRING_SIZE_MAX];
	gsFormatTime(fetchedRow.timestamp,timeStr,sizeof(timeStr));

	printf("Name = %s",fetchedRow.name);
	printf(" Timestamp = %s",timeStr);
	printf(" Volume = %.2lf", fetchedRow.volume);
	printf(" Perimeter = %.2lf", fetchedRow.perimeter);
	printf(" Shape = %s\n",fetchedRow.shape);

TQL Queries with Geometry Values

TQLを使用して、指定したい他の条件と一緒に幾何学的な交差点をチェックすることができます。 2つの非2次形状を比較する場合は、GIS関数ST_MBRIntersects(geometry1、geometry2)を使用できます。 ジオメトリを指定して交差を確認する場合は、WKT形式で図形を入力し、TQLのST_GeomFromText(wkt)関数を使用してジオメトリオブジェクトに変換すればよいでしょう。

例えば’geom’カラムの値がPOLYGON((2 2 4、5 2 4、5 2 8、2 2 8、2 2 4))と交差し、かつ値の列が降順で7.5より大きい最大5行を取得したい場合は、次のようにクエリを発行できます。

String tql = "SELECT * WHERE ST_MBRIntersects(geom," +  
"ST_GeomFromText('POLYGON((2 2 4, 5 2 4, 5 2 8, 2 2 8, 2 2 4))'))"
+ " AND value > 7.5 ORDER BY value DESC LIMIT 5";

Query<Row> query = collection.query(tql);

ST_MBRIntersectsについて注意すべき点の1つは、比較のためにQUADRATICSURFACESを使用できないことです。 二次曲面と非二次曲面の交点をチェックしたい場合。 ST_QSFMBRIntersects(二次関数、ジオメトリ)を使用して交差をチェックし、ST_MakeQSF(A00、A10、A20、A01 ...)を使用できます。A00、A10、A20、A01は、<AX,X> + BX + Cの2次方程式を作ります。

Example of Quadratic Surface Query

GSQuery* query;
GSRowSet* rowSet;

gsQuery(timeseries, \
  "select * where STQSFMBRIntersects( \
ST_MakeQSF(84,2,3,5,6,7,8,9,10,12,13,14,15,16,17,18),geom) AND id='row1'",\ 
&query);

gsFetch(query,GS_FALSE,&rowSet);

Best Uses of Spatial Data

幾何学的および空間的データタイプは、工学、科学、建築、地理、IoTなどの分野で非常に有用です。地理情報の分析やIoTアプリケーションでは、センサまたはオブジェクトの緯度、経度、高度を表すために空間データを使用できます。例えば、最後の3つのカテゴリは、POINT(緯度経度高度)の形式で3次元のポイントとして表すことができます。ポイントとの空間的な交差を使用して、特定の場所で行を見つけることができます。

POLYGONを使用して、地理的範囲内にある行を見つけることもできます。例えば、POLYGON(57 110, 59 110, 59 130, 57 130, 57 110)で幾何学的クエリを使用して、57〜59緯度および130〜110緯度の範囲内のすべての行を検索することができます。

線ストリング、ポリゴン、およびサーフェスは、ベクトルまたは3次元オブジェクトを表すために使用できます。空間データを作成、格納、照会、および索引付けできることは、建物や装置が3D設計に基づいているエンジニアリングや建築などの分野で特に役立ちます。

GridDBのユースケースとしては、トラフィックセンサーのコレクションを作成することなども含まれ、具体的には、アクティブであるか、見える車の数、等の一般的なデータを格納し、追跡することができます。 ジオメトリを使用して、地理的位置をPOINTで保存することもできます。交差関数のクエリを使用して、API関数またはTQLのいずれかを使用して位置に基づいてフィルタを見つけることができます。

Traffic Sensor in Java

public class TrafficLight {
	@RowKey
	public  String tag; // Sensor Id of Smart Traffic Light	
	public Geometry point; // Latitude, Longitude, and Elevation of Sensor
	public Date installation; // Time of installation to the Traffic Grid
	public boolean active; // Whether the traffic light sensor has been activated
	public int reports; // Number of reports that the Traffic Light has issued
	public String streets; // Cross streets of Traffic Light
// (snip)

Querying the Traffic Collection in Java

Collection<String,TrafficLight> trafficLightCollection = \ 
  gridstore.putCollection("trafficLights101",TrafficLight.class);
// (snip)
trafficLightCollection.createIndex("point",IndexType.SPATIAL);
// (snip)
// Search for a specific POINT
String ptTql = "SELECT * WHERE ST_MBRIntersects(point," +
                "ST_GeomFromText('POINT(47.33 84.72 0.7)'))";

Query<TrafficLight> ptQuery = trafficLightCollection.query(ptTql);
// (snip)
// Search for Point in a 2D Range using POLYGON
String tql = "SELECT * WHERE ST_MBRIntersects(point," + 
            "ST_GeomFromText('POLYGON((49.00 86.00, 51.00 86.00," +
             "51.00 87.00, 49.00 87.00, 49.00 86.00))'))";

Query<TrafficLight> query = trafficLightCollection.query(tql);

Using GridDB Standard Edition

GridDB Community Editionでは、ジオメトリオブジェクトを作成することはできますが、カラム値として挿入することも、コンテナのスキーマの一部として使用することもできません。 これらの機能はStandard Edition以降で提供されています。 GridDB Standard EditionはAWSで使用できます。 一般的なセットアップと構成の情報は、クイックスタートガイドに記載されています。

GEOMETRYタイプの詳細は、GridDB SEテクニカルリファレンスのこのセクションを参照してください。 Spatial操作の詳細については、APIリファレンスマニュアルを参照してください。 GridDBは、正規表現パターンを使用して、入力フォーマットが有効なWKTかどうかを判断します。 受け入れ可能なパターンは、GridDBのソースコードのこのセクションにあります。

ブログの内容について疑問や質問がある場合は Q&A サイトである Stack Overflow に質問を投稿しましょう。 GridDB 開発者やエンジニアから速やかな回答が得られるようにするためにも "griddb" タグをつけることをお忘れなく。 https://stackoverflow.com/questions/ask?tags=griddb

One Comment

  1. […] GridDB Community Edition 4.1以降のバージョンは、開発者がアプリケーションで時系列分析と地理空間分析の両方を組み合わせることができるGeometryデータタイプを備えています。 以前に GridDBでのジオメトリ値の使用と、ジオメトリデータを活用したアプリケーションのブログで、GridDBのジオメトリ機能について説明しました。 […]

Leave a Reply

Your email address will not be published. Required fields are marked *