GDALとPython:特定の値を持つすべてのセルの座標を取得する方法は?


12

私が持っているARC / INFOバイナリグリッドを具体的には、ArcGISのは累積ラスタを流し--- ---と私は特定の値(または値の範囲で)を有する全ての細胞を同定したいと思います。最終的に、これらのセルを表すポイントのシェープファイルが欲しいです。

QGISを使用してhdr.adfを開き、この結果を取得できます。ワークフローは次のとおりです。

  • QGIS>ラスターメニュー>ラスター計算機(すべてのポイントを目標値でマーク)
  • QGIS>ラスターメニュー>ポリゴン化
  • QGIS>ベクトルメニュー>ジオメトリサブメニュー>ポリゴン重心
  • 重心を編集して、不要なポリ重心を削除します(これらの重心= 0)

このアプローチは「仕事をします」が、削除しなければならない2つのファイルを作成し、重心のシェイプファイルから不要なレコードを削除する必要があるため、私には魅力的ではありません(つまり、それらは0です)。

既存の質問は、この主題に近づくが、それは、ArcGISの/ ArcPyに合わせだ、と私はFOSSのスペースに滞在したいと思います。

ラスターのセル値を調べる既存のGDAL / Pythonレシピ/スクリプトがあり、ターゲット値(またはターゲット範囲内の値)が見つかった場合、レコードがシェープファイルに追加されますか?これにより、UIの相互作用が回避されるだけでなく、単一のパスでクリーンな結果が作成されます。

Chris Garrardのプレゼンテーションの1つに対して作業することでショットを撮りましたが、ラスター作業は私の操舵室にありません。また、弱いコードで質問を混乱させたくありません。

正確なデータセットを使用したい場合は、ここに.zipとして配置します


[メモの編集]後世のためにこれを残してください。om_hennersとのコメント交換を参照してください。基本的に、x / y(行/列)の値は反転しました。元の答えには次の行がありました:

(y_index, x_index) = np.nonzero(a == 1000)

このように反転します:

(x_index, y_index) = np.nonzero(a == 1000)

スクリーンショットに示されている問題に初めて遭遇したとき、ジオメトリを誤って実装したかどうか疑問に思い、次の行でx / y座標値を反転させて実験しました。

point.SetPoint(0, x, y)

..なので..

point.SetPoint(0, y, x)

しかし、それはうまくいきませんでした。そして、om_hennersのNumpy式で値を反転させようとは考えていませんでした。どちらの行で値を反転しても同等であると間違って信じていました。私は、本当の問題がに関する考えるx_sizey_size、それぞれの値、30および-30行と列のインデックスは、細胞のための計算点の座標に使用されている場合に適用されます。

[オリジナル編集]

@ om_henners、ogr(invisibleroads.comChris Garrard)を使用してポイントシェープファイルを作成するためのいくつかのレシピと協力して、あなたのソリューションを試していますが、ポイントがライン通過を越えて反映されるかのように表示される問題があります315/135度まで。

水色の点:上記のQGISアプローチによって作成された

紫色のポイント:以下のGDAL / OGR pyコードによって作成

ここに画像の説明を入力してください


[解決済み]

このPythonコードは、@ om_hennersによって提案された完全なソリューションを実装します。私はそれをテストしましたが、動作します。ありがとう!


from osgeo import gdal
import numpy as np
import osgeo.ogr
import osgeo.osr

path = "D:/GIS/greeneCty/Greene_DEM/GreeneDEM30m/flowacc_gree/hdr.adf"
print "\nOpening: " + path + "\n"

r = gdal.Open(path)
band = r.GetRasterBand(1)

(upper_left_x, x_size, x_rotation, upper_left_y, y_rotation, y_size) = r.GetGeoTransform()

a = band.ReadAsArray().astype(np.float)

# This evaluation makes x/y arrays for all cell values in a range.
# I knew how many points I should get for ==1000 and wanted to test it.
(y_index, x_index) = np.nonzero((a > 999) & (a < 1001))

# This evaluation makes x/y arrays for all cells having the fixed value, 1000.
#(y_index, x_index) = np.nonzero(a == 1000)

# DEBUG: take a look at the arrays..
#print repr((y_index, x_index))

# Init the shapefile stuff..
srs = osgeo.osr.SpatialReference()
#srs.ImportFromProj4('+proj=utm +zone=15 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs')
srs.ImportFromWkt(r.GetProjection())

driver = osgeo.ogr.GetDriverByName('ESRI Shapefile')
shapeData = driver.CreateDataSource('D:/GIS/01_tutorials/flow_acc/ogr_pts.shp')

layer = shapeData.CreateLayer('ogr_pts', srs, osgeo.ogr.wkbPoint)
layerDefinition = layer.GetLayerDefn()

# Iterate over the Numpy points..
i = 0
for x_coord in x_index:
    x = x_index[i] * x_size + upper_left_x + (x_size / 2) #add half the cell size
    y = y_index[i] * y_size + upper_left_y + (y_size / 2) #to centre the point

    # DEBUG: take a look at the coords..
    #print "Coords: " + str(x) + ", " + str(y)

    point = osgeo.ogr.Geometry(osgeo.ogr.wkbPoint)
    point.SetPoint(0, x, y)

    feature = osgeo.ogr.Feature(layerDefinition)
    feature.SetGeometry(point)
    feature.SetFID(i)

    layer.CreateFeature(feature)

    i += 1

shapeData.Destroy()

print "done! " + str(i) + " points found!"

1
コードのクイックヒント:(srs.ImportFromWkt(r.GetProjection())既知のプロジェクト文字列から投影を作成する代わりに)ラスター投影をシェープファイル投影として使用できます。
om_henners

あなたのコードは私が探しているすべてを実行しますが、numpyフィルターを書いて値= 1000のみを含めるようにラスターセル値を含まないことを除いて。ラスターセル値を出力?ありがとう!
ブレントエドワーズ

回答:


18

GDALでは、ラスターをnumpy配列としてインポートできます。

from osgeo import gdal
import numpy as np

r = gdal.Open("path/to/raster")
band = r.GetRasterBand(1) #bands start at one
a = band.ReadAsArray().astype(np.float)

numpyを使用すると、ブーラン式に一致する配列のインデックスを取得するのは簡単です:

(y_index, x_index) = np.nonzero(a > threshold)
#To demonstate this compare a.shape to band.XSize and band.YSize

ラスタジオトランスフォームから、左上のx座標とy座標、セルサイズなどの情報を取得できます。

(upper_left_x, x_size, x_rotation, upper_left_y, y_rotation, y_size) = r.GetGeoTransform()

左上のセルはに対応しa[0, 0]ます。Yサイズは常に負になるため、xおよびyインデックスを使用して、インデックスに基づいて各セルの座標を計算できます。

x_coords = x_index * x_size + upper_left_x + (x_size / 2) #add half the cell size
y_coords = y_index * y_size + upper_left_y + (y_size / 2) #to centre the point

ここから、OGRを使用してシェープファイルを作成するのは簡単です。サンプルコードについては、ポイント情報を使用して新しいデータセットを生成する方法について、この質問参照してください。


ねえ、私はこれを実装する小さな問題を抱えています。質問を更新して、使用しているコードと取得しているもののスクリーンショットを含めました。基本的に、.pyコードは、QGISアプローチが生成しているものの鏡像(ポイント配置)を作成しています。私の実装のポイントはラスターの境界外にあるため、問題はコードにあります。:=私はあなたがいくらか光を放つことを望んでいます。ありがとう!
elrobis

それについてすみません-完全に私の悪い。GDALでラスタをインポートすると、行はy方向になり、列はx方向になります。上記のコードを更新しましたが、トリックはインデックスを取得することです(y_index, x_index) = np.nonzero(a > threshold)
om_henners

1
また、念のため、セルサイズの半分を両方向の座標に追加して、セル内のポイントを中央に配置することに注意してください。
om_henners

はい、それが問題でした。(スクリーングラブに示されているように)最初にそのバグに遭遇したとき、ポイントジオメトリを誤って実装したの.shpではないかと考えたため、作成時にx / yをy / xとして反転させようとしました。仕事も、それはどこにも近かった。xの値は数十万、yの値は数百万なので、私はショックを受けませんでした。Numpyの表現でそれらを元に戻そうとは思いませんでした。あなたの助けに感謝します、これはクールです。まさに私が欲しかったもの。:)
elrobis

4

QGISでSexanteツールボックスを使用してみませんか?これは、Model Builder for ArcGISのようなものです。そのようにして、操作をチェーン化し、1つの操作として扱うことができます。間違えなければ、中間ファイルの削除と不要なレコードの削除を自動化できます。


1

データをpostgis(ラスタサポート)にインポートし、そこで関数を使用すると役立つ場合があります。このチュートリアルには、必要な要素が含まれている場合があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.