GeoJSONデータの管理

GeoJson仕様は、地形(ジオメトリ)を表すjsonオブジェクトの構造およびコンテンツを定義します。

GeoJson仕様によれば、JSONオブジェクトがジオメトリ・オブジェクトになるには、typeおよびcoordinatesという2つのフィールドを持つ必要があります。ここで、typeフィールドの値はジオメトリの種類を指定し、coordinatesの値は、要素がジオメトリの形状を定義する配列である必要があります。様々なタイプのジオメトリ・オブジェクトの詳細は、GeoJSONデータの概要に関する項を参照してください。すべての種類のジオメトリが、一連の位置によって指定されます。ただし、行文字列やポリゴンの場合、実際の幾何学的図形は、位置を結ぶ線によって形成されます。GeoJson仕様では、水平軸と垂直軸がそれぞれ経度と緯度である、(フラットな)デカルト座標系の点を結ぶ直線として、2点間の線を定義します。詳細は、線および座標系に関する項を参照してください。

例のとおりに操作する場合は、スクリプトgeojsonschema_loaddata.sqlをダウンロードして、次に示すように実行します。このスクリプトにより、例で使用する表が作成され、表にデータがロードされます。

KVSTOREまたはKVLiteを起動し、SQL.shellを開きます。
java -jar lib/kvstore.jar kvlite -secure-config disable
java -jar lib/sql.jar -helper-hosts localhost:5000 -store kvstore
loadコマンドを使用して、スクリプトを実行します。
load -file geojsonschema_loaddata.sql
Oracle NoSQL Databaseは、JSONオブジェクトをジオメトリとして解釈し、特定の条件を満たすジオメトリを含む行を検索できる多数の関数を実装します。

geo_inside

GeoJSON境界ジオメトリ内のジオメトリを特定します。
boolean geo_inside(any*, any*)
  • 最初のパラメータany*には、任意のジオメトリ・オブジェクトを指定できます。
  • 2番目のパラメータany*はポリゴンである必要があります。

この関数は、最初のパラメータが指すジオメトリが、2番目のパラメータが指すポリゴン内に完全に含まれているかどうかを判断します。

2つのパラメータのいずれかが単一の有効なジオメトリ・オブジェクトを返さず、そのことがコンパイル時に検出された場合は、エラーが発生します。

実行時の動作は次のとおりです。
  • いずれかのパラメータが0個または複数の項目を返した場合はfalseを返します。
  • いずれかのパラメータがNULLを返した場合はNULLを返します。
  • いずれかのパラメータ(実行時)が有効なジオメトリ・オブジェクトではない項目を返した場合は、falseを返します。
  • 2番目のパラメータがポリゴンではないジオメトリ・オブジェクトを返した場合は、falseを返します。
  • 両方のパラメータがそれぞれ1つのジオメトリ・オブジェクトを返し、2番目のジオメトリがポリゴンである場合。
    • 最初のジオメトリが2番目のポリゴンの内部に完全に含まれている場合、つまり、そのすべてのポイントがポリゴンの内部に属している場合は、trueを返します。
    • それ以外の場合はfalseを返します。

ノート:

ポリゴンの内部とは、ポリゴンの境界を定義する線形リング上のポイントを除く、ポリゴン領域内のすべてのポイントです。
例: 北カリフォルニアの自然公園を検索します。
SELECT t.poi.name AS park_name, 
t.poi.address.street AS park_location
FROM PointsOfInterest t
WHERE t.poi.kind = "nature park"
AND geo_inside(t.poi.location,
              { "type" : "polygon",
                "coordinates": [[
                  [-120.1135253906249, 36.99816565700228],
                  [-119.0972900390625, 37.391981943533544],
                  [-119.2840576171875, 37.97451499202459],
                  [-120.2069091796874, 38.035112420612975],
                  [-122.3822021484375, 37.74031329210266],
                  [-122.2283935546875, 37.15156050223665],
                  [-121.5362548828124, 36.85325222344018],
                  [-120.1135253906249, 36.99816565700228]
                ]]
             });
説明:
  • PointsOfInterest表を問い合せて、nature parkの行をフィルタします。
  • geo_inside関数の2番目のパラメータとしてポリゴンを指定します。
  • 指定したポリゴンの座標は、米国カリフォルニア州の北部の座標に対応しています。
  • geo_inside関数は、自然公園の場所が指定された場所のポイント内に完全に含まれている場合にのみ行を返します。
結果:
{"park_name":"portola redwoods state park",
"park_location":"15000 Skyline Blvd"}

geo_intersect

GeoJSONジオメトリと交差するジオメトリを特定します。
boolean geo_intersect(any*, any*)

1番目と2番目のパラメータany*には、任意のジオメトリ・オブジェクトを指定できます。

この関数は、パラメータとして指定された2つのジオメトリに共通するポイントがあるかどうかを判断します。2つのパラメータのいずれかが単一の有効なジオメトリ・オブジェクトを返さず、そのことがコンパイル時に検出された場合は、エラーが発生します。

実行時の動作は次のとおりです。
  • いずれかのパラメータが0個または複数の項目を返した場合はfalseを返します。
  • いずれかのパラメータがNULLを返した場合はNULLを返します。
  • いずれかのパラメータ(実行時)が有効なジオメトリ・オブジェクトではない項目を返した場合は、falseを返します。

両方のパラメータがそれぞれ単一のジオメトリ・オブジェクトを返す場合、2つのジオメトリに共通するポイントがあればtrueを返し、そうでない場合はfalseを返します。

例: テキサスでは、地下水供給へのアクセスの規制を検討しています。帯水層は、水を含む透水層、岩盤の割れ目または未固結の物質からなる地下層です。政府は、帯水層に非常に近い場所に新たな規制を課す必要があります。

帯水層の座標はすでにマップされています。影響を受ける各郡の郡政府に新しい規制の協議に参加するよう通知できるように、その帯水層と交差するテキサス州のすべての郡を識別する必要があります。
SELECT t.poi.county AS County_needs_regulation,
t.poi.contact AS Contact_phone
FROM PointsOfInterest t WHERE
geo_intersect(
    t.poi.location,
    { 

     "type" : "polygon",
      "coordinates": [
          [
            [-97.668457031249, 29.34387539941801],
            [-95.207519531258, 29.19053283229458],
            [-92.900390625653, 30.37287518811801],
            [-94.636230468752, 32.21280106801518],
            [-97.778320312522, 32.45415593941475],
            [-99.799804687541, 31.18460913574325],
            [-97.668457031249, 29.34387539941801]
          ]
        ]
    }
);
説明:
  • 前述の問合せでは、帯水層の場所と交差する場所をフェッチしています。つまり、その場所の座標に帯水層の場所と共通するポイントがある場合です。
  • geo_intersectを使用して、場所の座標に、指定された帯水層の座標と共通するポイントがあるかどうかを確認します。
結果:
{"County_needs_regulation":"Tarrant","Contact_phone":"469 745 5687"}
{"County_needs_regulation":"Kinga","Contact_phone":"469 384 7612"}

geo_distance

2つの地理空間オブジェクト間の距離を決定します。
double geo_distance(any*, any*)

1番目と2番目のパラメータany*には、任意のジオメトリ・オブジェクトを指定できます。

この関数は2つの入力ジオメトリ間の測地距離を返します。返される距離は、1つ目のポイントが1つ目のジオメトリに属し、2つ目のポイントが2つ目のジオメトリに属しているポイントのペアの距離の最小値として定義されます。このような2つのポイントの間の距離は、ポイントを結ぶ測地線の長さです。

測地線の概要

2点間の測地線は、地球の楕円体表面上の2点間に描画できる最短の線です。単純な、ただしより実例的な定義にするために、地球の表面が球面であると想定します。地球上の2点間の測地線は、これらの点に対応する大円の2点間にある劣弧です。つまり、地球の中心と2点で定義された球面と平面の交差で形成される円です。

次の図は、ロサンゼルスとロンドンの間の測地線と直線の違いを示しています。
geodetic-vs-straight-line.jpgの説明が続きます
図geodetic-vs-straight-line.jpgの説明

2つのパラメータのいずれかが単一の有効なジオメトリ・オブジェクトを返さず、そのことがコンパイル時に検出された場合は、エラーが発生します。

実行時の動作は次のとおりです。
  • いずれかのパラメータが0個または複数の項目を返した場合は-1を返します。
  • いずれかのパラメータがNULLを返した場合はNULLを返します。
  • パラメータのいずれかがジオメトリ・オブジェクトでない場合は-1を返します。
それ以外の場合、関数は2つの入力ジオメトリ間の測地距離をメートル単位で返します。

ノート:

結果は距離に基づいて昇順にソートされます(最短距離が最初に表示されます)。
例: 指定の場所から最も近いレストランまでの距離はどれくらいですか。
SELECT 
t.poi.name AS restaurant_name,
t.poi.address.street AS street_name,
geo_distance(
    t.poi.location,
    { 
       "type" : "point",
       "coordinates": [-121.94034576416016,37.2812239247177]
    }
) AS distance_in_meters
FROM PointsOfInterest t
WHERE t.poi.kind = "restaurant" ;
説明:
  • PointsOfInterest表を問い合せて、restaurantの行をフィルタします。
  • 正しい場所のポイントを指定し、geo_distance関数を使用して距離を決定します。
結果:
{"restaurant_name":"Coach Sports Bar & Grill","street_name":"80 Edward St","distance_in_meters":799.2645323337218}
{"restaurant_name":"Ricos Taco","street_name":"80 East Boulevard St","distance_in_meters":976.5361117138553}
{"restaurant_name":"Effie's Restaurant and Bar","street_name":"80 Woodeard St","distance_in_meters":2891.0508307646282}    

現在の場所から最も近いレストランまでの距離は799 mです。

geo_within_distance

ポイントに近接する地理空間オブジェクトを決定します。
boolean geo_within_distance(any*, any*,double)

1番目と2番目のパラメータany*には、任意のジオメトリ・オブジェクトを指定できます。

この関数は、1番目のジオメトリが2番目のジオメトリからN mの距離内にあるかどうかを判断します。

2つのパラメータのいずれかが単一の有効なジオメトリ・オブジェクトを返さず、そのことがコンパイル時に検出された場合は、エラーが発生します。

実行時の動作は次のとおりです。
  • いずれかのパラメータが0個または複数の項目を返した場合はfalseを返します。
  • 最初の2つのパラメータのいずれかがNULLを返した場合はNULLを返します。
  • 最初の2つのパラメータのいずれかが有効なジオメトリ・オブジェクトではない項目を返した場合は、falseを返します。

最終的に、両方のパラメータがそれぞれ単一のジオメトリ・オブジェクトを返す場合、1番目のジオメトリが2番目のジオメトリからN m以内の距離にあればtrueを返します。Nは3番目のパラメータで返される数値です。それ以外の場合はfalseを返します。2つのジオメトリの間の距離は、1つ目のポイントが1つ目のジオメトリに属し、2つ目のポイントが2つ目のジオメトリに属しているポイントのペアの距離の最小値として定義されます。Nが負の数の場合は、0に設定されます。

例: 市役所はこの先5 km以内にありますか。距離はどれだけですか。
SELECT t.poi.address.street AS city_hall_address,
geo_distance(
    t.poi.location,
    { 
        "type" : "point",
        "coordinates" : [-120.653828125,38.85682013474361]
    }

) AS distance_in_meters
FROM PointsOfInterest t
WHERE t.poi.kind = "city hall" AND
geo_within_distance( 
    t.poi.location,
    { 
        "type" : "point",
        "coordinates" : [-120.653828125,38.85682013474361]
    },
    5000
);
説明:
  • PointsOfInterest表を問い合せて、city hallの行をフィルタします。
  • geo_within_distance関数を使用して、指定した場所から5 km (5000m)以内の市役所をフィルタします。
  • また、geo_distance関数を使用して、その場所と市役所の間の実際の距離をフェッチします。
結果:
{"city_hall_address":"70 North 1st street","distance_in_meters":1736.0144040331768}

市役所は現在の場所から1736 m(1.73 km)です。

geo_near

ポイントに近接する地理空間オブジェクトを決定します。
boolean geo_near(any*, any*, double)

1番目と2番目のパラメータany*には、任意のジオメトリ・オブジェクトを指定できます。

この関数は、1番目のジオメトリが2番目のジオメトリからN mの距離内にあるかどうかを判断します。

2つのパラメータのいずれかが単一の有効なジオメトリ・オブジェクトを返さず、そのことがコンパイル時に検出された場合は、エラーが発生します。

実行時の動作は次のとおりです。
  • いずれかのパラメータが0個または複数の項目を返した場合はfalseを返します。
  • 最初の2つのパラメータのいずれかがNULLを返した場合はNULLを返します。
  • 最初の2つのパラメータのいずれかが有効なジオメトリ・オブジェクトではない項目を返した場合は、falseを返します。
最終的に、最初の2つのパラメータの両方がそれぞれ単一のジオメトリ・オブジェクトを返す場合、1番目のジオメトリが2番目のジオメトリからN m以内の距離にあればtrueを返します。Nは3番目のパラメータで返される数値です。それ以外の場合はfalseを返します。

ノート:

geo_nearは、内部でgeo_within_distanceに変換され、2つのジオメトリ間の距離に(暗黙的な) ORDER BYが指定されます。ただし、問合せに(明示的な) ORDER BYがすでに存在する場合、距離による順序付けは実行されません。geo_near関数はWHERE句にのみ使用できます。この場合、最上位レベルの述語(ORまたはNOT演算子の下にネストされていない)にする必要があります。
例1: 指定した場所の3km以内に病院はありますか。
SELECT 
t.poi.name AS hospital_name,
t.poi.address.street AS hospital_address
FROM PointsOfInterest t
WHERE t.poi.kind = "hospital" 
AND
geo_near( 
    t.poi.location,
    {"type" : "point",
     "coordinates" : [-122.03493933105469,37.32949164059004]  
    },
    3000
);
説明:
  • PointsOfInterest表を問い合せて、hospitalの行をフィルタします。
  • geo_near関数を使用して、指定された場所の3000m以内の病院をフィルタします。
結果:
{"hospital_name":"St. Marthas hospital","hospital_address":"18000 West Blvd"}
{"hospital_name":"Memorial hospital","hospital_address":"10500 South St"}
例2: 指定の場所から先の1マイル以内にあるガソリン・スタンドまでどれくらいの距離がありますか。
SELECT 
t.poi.address.street AS gas_station_address,
geo_distance(
    t.poi.location,
    { 
        "type" : "point",
        "coordinates" : [-121.90768646240233,37.292081740702365] 
    }
) AS distance_in_meters
FROM PointsOfInterest t
WHERE t.poi.kind = "gas station" AND
geo_near( 
    t.poi.location,
    { 
        "type" : "point",
        "coordinates" : [-121.90768646240233,37.292081740702365]
    },
    1600
);
説明:
  • PointsOfInterest表を問い合せて、gas stationの行をフィルタします。
  • geo_near関数を使用して、指定された場所の1マイル(1600m)以内のガソリン・スタンドをフィルタします。
  • また、geo_distance関数を使用して、その場所とガソリン・スタンドの間の実際の距離をフェッチします。
結果:
{"gas_station_address":"33 North Avenue","distance_in_meters":886.7004173859665}

次の1マイル以内にある最も近いガソリン・スタンドまでの実際の距離は886mです。

geo_is_geometry

地理空間オブジェクトを検証します。
boolean geo_is_geometry(any*)

パラメータany*には、任意のジオメトリ・オブジェクトを指定できます。

この関数は、指定された入力が有効なジオメトリ・オブジェクトであるかどうかを判断します。
  • パラメータが0個または複数の項目を返した場合はfalseを返します。
  • パラメータがNULLを返した場合はNULLを返します。
  • 入力が単一の有効なジオメトリ・オブジェクトである場合はtrueを返します。それ以外の場合は、falseです。
例: city hallを指す場所が有効なジオメトリ・オブジェクトかどうかを判断します。
SELECT geo_is_geometry(t.poi.location) AS city_hall
FROM PointsOfInterest t
WHERE t.poi.kind = "city hall" 

説明: 関数geo_is_geometryを使用して、指定された場所が有効なジオメトリ・オブジェクトであるかどうかを判断します。

結果:
{ "city_hall" : true}