วิธีรับพิกัด XY และค่าเซลล์ของแต่ละพิกเซลในแรสเตอร์โดยใช้ Python


16

ฉันใหม่จริง ๆ กับ Python และฉันต้องการทราบว่ามีวิธีที่รวดเร็วในการรับค่าเซลล์ของพิกเซลแรสเตอร์ทีละพิกเซลและพิกัด (พิกัด XY แผนที่ของศูนย์กลางของแต่ละพิกเซล) โดยใช้ Python ใน ArcGIS 10 หรือไม่

เพื่ออธิบายเพิ่มเติมนี้ฉันต้องได้รับ map X, map Y และค่าเซลล์ของพิกเซลแรกและกำหนดค่าทั้งสามให้กับตัวแปรสามตัวและทำซ้ำขั้นตอนนี้สำหรับส่วนที่เหลือของพิกเซลอื่น ๆ (วนรอบแรสเตอร์ทั้งหมด)


ฉันคิดว่าฉันต้องอธิบายคำถามของฉันให้มากขึ้น ปัญหาคือฉันต้องได้รับตำแหน่ง XY ของพิกเซลของแรสเตอร์แรกและรับค่าเซลล์ของแรสเตอร์อื่น ๆ ที่สอดคล้องกับตำแหน่ง XY นั้น กระบวนการนี้ควรวนซ้ำทุกพิกเซลของแรสเตอร์แรกโดยไม่ต้องสร้างเชพไฟล์จุดกึ่งกลางใด ๆ เนื่องจากมันต้องใช้เวลามากจริงๆเพราะฉันต้องจัดการแรสเตอร์ที่มีเกือบ 8 พันล้านพิกเซล นอกจากนี้ฉันต้องทำสิ่งนี้โดยใช้ Python ใน ArcGIS 10

@ James: ขอบคุณมากสำหรับคำแนะนำของคุณ ใช่สิ่งนี้จะใช้ได้กับแรสเตอร์เดียว แต่ฉันต้องเก็บค่าเซลล์สำหรับแรสเตอร์อื่น ๆ ด้วยเช่นกัน ปัญหาคือหลังจากได้รับพิกัด X และ Y ของพิกเซลแรกของแรสเตอร์แรกฉันต้องได้รับค่าเซลล์ของแรสเตอร์ที่สองที่สอดคล้องกับ X, Y ตำแหน่งของแรสเตอร์แรกแล้วแรสเตอร์ที่สามและอื่น ๆ ดังนั้นฉันคิดว่าเมื่อวนลูปผ่านแรสเตอร์แรกรับตำแหน่ง X และ Y ของพิกเซลและรับค่าเซลล์ของแรสเตอร์อื่นที่สอดคล้องกับตำแหน่งนั้นควรจะทำพร้อมกัน แต่ฉันไม่แน่ใจ สิ่งนี้สามารถทำได้โดยการแปลงแรสเตอร์แรกในรูปแบบไฟล์จุดและดำเนินการแยกหลายค่าเพื่อฟังก์ชั่นจุดใน ArcGIS 10 แต่ฉัน '

@hmfly: ขอบคุณใช่วิธีนี้ (RastertoNumpyarray) จะทำงานถ้าฉันสามารถรับพิกัดของแถวและคอลัมน์ค่าที่รู้จักของอาร์เรย์

@whuber: ฉันไม่ต้องการทำการคำนวณใด ๆ ทั้งหมดที่ฉันต้องทำก็คือเขียนพิกัด XY และค่าของเซลล์ในไฟล์ข้อความและนั่นคือทั้งหมด


บางทีคุณอาจต้องการทำคณิตศาสตร์กับแรสเตอร์ทั้งหมด? เครื่องคิดเลขแรสเตอร์ทำงานพิกเซลต่อพิกเซล
BWill

1
โปรดอธิบายวัตถุประสงค์ของคุณในรายละเอียดเพิ่มเติม
BWill

โดยปกติแล้วการแก้ปัญหาที่มีประสิทธิภาพและเชื่อถือได้จะได้รับจากการใช้งานการทำแผนที่พีชคณิตแทนที่จะวนซ้ำจุด ข้อ จำกัด ในการนำพีชคณิตของ Spatial Analyst มาใช้ทำให้วิธีนี้ไม่สามารถทำงานได้ในทุกกรณี แต่ในสถานการณ์จำนวนมากอย่างน่าประหลาดใจคุณไม่ต้องเขียนรหัสวนซ้ำ คุณต้องคำนวณอะไรบ้าง
whuber

การแก้ไขของคุณอีกครั้ง: แน่นอนว่าเป็นวัตถุประสงค์ที่ถูกต้องตามกฎหมาย รูปแบบอาจถูกกำหนดโดยคุณตามความต้องการของซอฟต์แวร์เพิ่มเติมลงท่อ แต่เมื่อพิจารณาว่าการเขียนสิ่งเขียน 8 พันล้าน (X, Y, ค่า 1, ... , ค่า 3) จะต้องอยู่ระหว่าง 224,000,000,000 ไบต์ (ในไบนารี) และบางที 400 พันล้านไบต์ (ใน ASCII) ซึ่งเป็นชุดข้อมูลที่ค่อนข้างใหญ่ อาจจะคุ้มค่าในการหาแนวทางทางเลือกให้กับสิ่งที่คุณกำลังพยายามทำให้สำเร็จ
whuber

คำตอบ:


11

ตามแนวคิดของ @ Dango ฉันได้สร้างและทดสอบ (บน rasters ขนาดเล็กที่มีขนาดเท่ากันและขนาดเซลล์) รหัสต่อไปนี้:

import arcpy, numpy

inRaster = r"C:\tmp\RastersArray.gdb\InRaster"
inRaster2 = r"C:\tmp\RastersArray.gdb\InRaster2"

##Get properties of the input raster
inRasterDesc = arcpy.Describe(inRaster)

#coordinates of the lower left corner
rasXmin = inRasterDesc.Extent.Xmin
rasYmin = inRasterDesc.Extent.Ymin

# Cell size, raster size
rasMeanCellHeight = inRasterDesc.MeanCellHeight
rasMeanCellWidth = inRasterDesc.MeanCellWidth
rasHeight = inRasterDesc.Height
rasWidth = inRasterDesc.Width

##Calculate coordinates basing on raster properties
#create numpy array of coordinates of cell centroids
def rasCentrX(rasHeight, rasWidth):
    coordX = rasXmin + (0.5*rasMeanCellWidth + rasWidth)
    return coordX
inRasterCoordX = numpy.fromfunction(rasCentrX, (rasHeight,rasWidth)) #numpy array of X coord

def rasCentrY(rasHeight, rasWidth):
    coordY = rasYmin + (0.5*rasMeanCellHeight + rasHeight)
    return coordY
inRasterCoordY = numpy.fromfunction(rasCentrY, (rasHeight,rasWidth)) #numpy array of Y coord

#combine arrays of coordinates (although array for Y is before X, dstack produces [X, Y] pairs)
inRasterCoordinates = numpy.dstack((inRasterCoordY,inRasterCoordX))


##Raster conversion to NumPy Array
#create NumPy array from input rasters 
inRasterArrayTopLeft = arcpy.RasterToNumPyArray(inRaster)
inRasterArrayTopLeft2 = arcpy.RasterToNumPyArray(inRaster2)

#flip array upside down - then lower left corner cells has the same index as cells in coordinates array
inRasterArray = numpy.flipud(inRasterArrayTopLeft)
inRasterArray2 = numpy.flipud(inRasterArrayTopLeft2)


# combine coordinates and value
inRasterFullArray = numpy.dstack((inRasterCoordinates, inRasterArray.T))

#add values from second raster
rasterValuesArray = numpy.dstack((inRasterFullArray, inRasterArray2.T))

ตามรหัส @hmfly คุณสามารถเข้าถึงค่าที่ต้องการ:

(height, width, dim )=rasterValuesArray.shape
for row in range(0,height):
    for col in range(0,width):
        #now you have access to single array of values for one cell location

น่าเสียดายที่มีหนึ่ง 'แต่' - รหัสนี้เหมาะสำหรับอาร์เรย์ NumPy ซึ่งสามารถจัดการได้โดยหน่วยความจำระบบ สำหรับระบบของฉัน (8GB) อาร์เรย์ที่ใหญ่ที่สุดคือประมาณ 9000,9,000

เนื่องจากประสบการณ์ของฉันไม่ให้ฉันให้ความช่วยเหลือเพิ่มเติมคุณสามารถพิจารณาคำแนะนำบางอย่างเกี่ยวกับการจัดการอาร์เรย์ขนาดใหญ่ด้วย: /programming/1053928/python-numpy-very-large-matrices

arcpy.RasterToNumPyArrayเมธอดอนุญาตให้ระบุเซ็ตย่อยของแรสเตอร์ที่แปลงเป็นอาร์เรย์ NumPy ( หน้าช่วยเหลือ ArcGIS10 ) สิ่งที่มีประโยชน์เมื่อทำการ chunking ชุดข้อมูลขนาดใหญ่เป็นเมทริกซ์ย่อย


รหัสของ Marcin นั้นยอดเยี่ยมมาก! ขอบคุณ แต่มันไม่ได้เขียน X, Y ของแรสเตอร์ที่มีความละเอียดเท่ากันของแรสเตอร์ฉันหมายถึง x และ y ที่เพิ่มขึ้น 1 เมตรและไม่ใช่เช่น) 100 เมตร .... คุณมีคำแนะนำให้แก้ไขไหม ขอบคุณมาก

7

หากคุณต้องการรับค่าพิกเซลผ่าน (แถว, คอลัมน์) คุณสามารถเขียนสคริปต์ arcpy ดังนี้:

import arcpy
raster = arcpy.Raster("yourfilepath")
array = arcpy.RasterToNumPyArray(raster)
(height, width)=array.shape
for row in range(0,height):
    for col in range(0,width):
        print str(row)+","+str(col)+":"+str(array.item(row,col))

แต่ถ้าคุณต้องการได้รับพิกัดพิกเซล NumPyArray ไม่สามารถช่วยคุณได้ คุณสามารถแปลงแรสเตอร์ให้ชี้ด้วยเครื่องมือ RasterToPoint จากนั้นคุณสามารถรับพิกัดโดยการยื่น Shape


7

วิธีที่ง่ายที่สุดในการส่งออกค่าพิกัดและค่าเซลล์ไปยังไฟล์ข้อความใน ArcGIS 10 คือฟังก์ชั่นตัวอย่างไม่จำเป็นต้องใช้รหัสและโดยเฉพาะอย่างยิ่งไม่จำเป็นต้องวนซ้ำกันในแต่ละเซลล์ ในArcGIS <= 9.3xเครื่องคิดเลขแรสเตอร์นั้นเคยเป็นเรื่องง่ายเหมือนoutfile.csv = sample(someraster)ที่จะส่งออกไฟล์ข้อความของทุกค่าเซลล์ (ไม่ใช่โมฆะ) และพิกัด (ในรูปแบบ z, x, y) ใน ArcGIS 10 ดูเหมือนว่า "in_location_data argument" Sample(someraster, someraster, outcsvfile)คือตอนนี้บังคับให้คุณต้องใช้ไวยากรณ์

แก้ไข: คุณยังสามารถระบุ rasters หลายรายการ: Sample([someraster, anotherraster, etc], someraster, outcsvfile)หลาย ไม่ว่ามันจะทำงานกับเซลล์ 8 พันล้านเซลล์ฉันก็ไม่ทราบหรอก ...

แก้ไข: หมายเหตุฉันยังไม่ได้ทดสอบสิ่งนี้ใน ArcGIS 10 แต่ใช้ฟังก์ชั่นตัวอย่างเป็นเวลาหลายปีใน <= 9.3 (และเวิร์กสเตชัน)

แก้ไข: ตอนนี้ฉันได้ทดสอบใน ArcGIS 10 แล้วและจะไม่ส่งออกไปยังไฟล์ข้อความ เครื่องมือเปลี่ยนนามสกุลไฟล์เป็น ".dbf" โดยอัตโนมัติ อย่างไรก็ตาม ... รหัสไพ ธ อนต่อไปนี้ทำงานเป็นคำสั่งพีชคณิตแผนที่SOMA และ MOMAยังคงได้รับการสนับสนุนใน ArcGIS 10:

import arcgisscripting
gp=arcgisscripting.create()
gp.multioutputmapalgebra(r'%s=sample(%s)' % (outputcsv,inputraster))

ดีมาก. ขอบคุณที่ชี้นำสิ่งนี้ - ฉันไม่เคยเห็นเครื่องมือนี้มาก่อน แน่นอนมากและเรียบง่ายกว่าโซลูชันของฉัน!
JamesS

6

วิธีหนึ่งในการทำเช่นนี้คือการใช้เครื่องมือRaster_To_Pointตามด้วยเครื่องมือAdd_XY_Coordinates คุณจะจบลงด้วย shapefile ที่แต่ละแถวในตารางแอตทริบิวต์แสดงพิกเซลจากแรสเตอร์ของคุณด้วยคอลัมน์สำหรับX_Coord , Y_Coordและ CELL_VALUE จากนั้นคุณสามารถวนซ้ำตารางนี้โดยใช้เคอร์เซอร์ (หรือส่งออกไปยังบางสิ่งเช่น Excel หากคุณต้องการ)

หากคุณมีเพียงหนึ่งแรสเตอร์ในการประมวลผลมันอาจจะไม่คุ้มค่ากับการเขียนสคริปต์เพียงใช้เครื่องมือจาก ArcToolbox หากคุณจำเป็นต้องทำเช่นนี้สำหรับ rasters หลาย ๆ คุณสามารถลองสิ่งนี้:

[ หมายเหตุ:ฉันไม่มี ArcGIS 10 และไม่คุ้นเคยกับ ArcPy ดังนั้นนี่เป็นเพียงโครงร่างคร่าวๆ มันยังไม่ได้ทดสอบและเกือบจะต้อง tweaking เพื่อให้มันทำงาน]

import arcpy, os
from arcpy import env

# User input
ras_fold = r'path/to/my/data'           # The folder containing the rasters
out_fold = r'path/to/output/shapefiles' # The folder in which to create the shapefiles

# Set the workspace
env.workspace = ras_fold

# Get a list of raster datasets in the raster folder
raster_list = arcpy.ListRasters("*", "All")

# Loop over the rasters
for raster in raster_list:
    # Get the name of the raster dataset without the file extension
    dataset_name = os.path.splitext(raster)[0]

    # Build a path for the output shapefile
    shp_path = os.path.join(out_fold, '%s.shp' % dataset_name)

    # Convert the raster to a point shapefile
    arcpy.RasterToPoint_conversion(raster, shp_path, "VALUE")

    # Add columns to the shapefile containing the X and Y co-ordinates
    arcpy.AddXY_management(shp_path)

จากนั้นคุณสามารถห่วงตาราง shapefile แอตทริบิวต์ที่ใช้ค้นหา Cursorหรือ (อาจจะง่าย) โดยใช้dbfpy สิ่งนี้จะช่วยให้คุณสามารถอ่านข้อมูลจากแรสเตอร์ของคุณ (ตอนนี้เก็บไว้ในตาราง shapefile .dbf) ลงในตัวแปรหลาม

from dbfpy import dbf

# Path to shapefile .dbf
dbf_path = r'path\to\my\dbf_file.dbf'

# Open the dbf file
db = dbf.Dbf(dbf_path)

# Loop over the records
for rec in db:
    cell_no = rec['POINTID'] # Numbered from top left, running left to right along each row
    cell_x = rec['POINT_X']
    cell_y = rec['POINT_Y']
    cell_val = rec['GRID_CODE']

    # Print values
    print cell_no, cell_x, cell_y, cell_val

3

บางทีคุณอาจสร้างไฟล์โลกสำหรับแรสเตอร์และซ่อนแรสเตอร์เป็นอาร์เรย์ที่ไม่มีค่า ถ้าคุณวนลูปมากกว่าอาเรย์คุณจะได้รับค่าเซลล์และหากคุณเพิ่มค่า x, y จากไฟล์โลกคุณจะมีพิกัดสำหรับค่าแต่ละเซลล์ หวังว่าจะเป็นประโยชน์


หากคุณไม่สนใจวิธีเครื่องมือ Raster to Point ที่แนะนำโดย JamesS ฉันจะบอกว่านี่เป็นวิธีที่จะไป
nmpeterson

3

รหัสของ Marcin ทำงานได้ดียกเว้นปัญหาในฟังก์ชั่น rasCentrX และ rasCentrY ทำให้พิกัด ouput ปรากฏที่ความละเอียดต่างกัน (ตามที่ Grazia สังเกต) การแก้ไขของฉันคือการเปลี่ยนแปลง

coordX = rasXmin + (0.5*rasMeanCellWidth + rasWidth)

ถึง

coordX = rasXmin + ((0.5 + rasWidth) * rasMeanCellWidth)

และ

  coordY = rasYmin + (0.5*rasMeanCellHeight + rasHeight)

ถึง

  coordY = rasYmin + ((0.5 + rasHeight) * rasMeanCellHeight)

ฉันใช้รหัสเพื่อแปลง ESRI Grid เป็นไฟล์ CSV สิ่งนี้ทำได้โดยการลบการอ้างอิงถึง inRaster2 จากนั้นใช้ csv.writer เพื่อส่งออกพิกัดและค่า:

out = csv.writer(open(outputCSV,"wb"), delimiter=',', quoting=csv.QUOTE_NONNUMERIC)
out.writerow(['X','Y','Value'])
(height, width, dim )=inRasterFullArray.shape
for row in range(0,height):
    for col in range(0,width):
        out.writerow(inRasterFullArray[row,col])

ฉันยังไม่พบว่ามีการย้ายที่จำเป็น

inRasterFullArray = numpy.dstack((inRasterCoordinates, inRasterArray.T))

แปลงให้เป็น

inRasterFullArray = numpy.dstack((inRasterCoordinates, inRasterArray))

2

น่าเกลียด แต่มีประสิทธิภาพสูง:

  1. สร้างคุณลักษณะจุดใหม่ด้วย 4 คะแนนนอกมุมของแรสเตอร์ที่เป็นปัญหา ตรวจสอบให้แน่ใจในระบบพิกัดเดียวกันกับแรสเตอร์ที่เป็นปัญหา
  2. เพิ่มฟิลด์คู่ 'xcor' และ 'ycor'
  3. คำนวณเรขาคณิตเพื่อรับค่าพิกัดสำหรับฟิลด์เหล่านี้
  4. นักวิเคราะห์เชิงพื้นที่ -> การแก้ไข-> แนวโน้ม -> การถดถอยเชิงเส้น
  5. การตั้งค่าสภาพแวดล้อม: snap raster และขนาดเซลล์ให้เหมือนกับ raster ที่เป็นปัญหา
  6. ดำเนินการแยกต่างหากสำหรับ 'xcor' และ 'ycor'
  7. Out มาพร้อมกับพิกัดเป็นค่าเซลล์ใช้เป็นอินพุตสำหรับสคริปต์

2

วิธีง่ายๆในการใช้แพ็คเกจโอเพนซอร์ส:

import fiona
import rasterio
from pprint import pprint


def raster_point_coords(raster, points):

    # initialize dict to hold data
    pt_data = {}

    with fiona.open(points, 'r') as src:
        for feature in src:
            # create dict entry for each feature
            pt_data[feature['id']] = feature

    with rasterio.open(raster, 'r') as src:
        # read raster into numpy array
        arr = src.read()
        # rasterio always reads into 3d array, this is 2d, so reshape
        arr = arr.reshape(arr.shape[1], arr.shape[2])
        # get affine, i.e. data needed to work between 'image' and 'raster' coords
        a = src.affine

    for key, val in pt_data.items():
        # get coordinates
        x, y = val['geometry']['coordinates'][0], val['geometry']['coordinates'][1]
        # use affine to convert to row, column
        col, row = ~a * (x, y)
        # remember numpy array is indexed array[row, column] ie. y, x
        val['raster_value'] = arr[int(row), int(col)]

    pprint(pt_data) 

if __name__ == '__main__':
    # my Landsat raster
    ras = '/data01/images/sandbox/LT05_040028_B1.tif'
    # my shapefile with two points which overlap raster area
    pts = '/data01/images/sandbox/points.shp'
    # call function
    raster_point_coords(ras, pts)

ฟิโอน่ามีประโยชน์ในขณะที่คุณสามารถเปิดรูปร่างไฟล์ย้ำผ่านคุณสมบัติและ (ตามที่ฉันมี) ผนวกเข้ากับdictวัตถุ แน่นอนว่าฟิโอน่าfeatureนั้นเป็นเหมือนdictกันดังนั้นจึงง่ายต่อการเข้าถึงคุณสมบัติ หากคะแนนของฉันมีคุณสมบัติใด ๆ พวกเขาจะปรากฏใน dict นี้พร้อมกับพิกัด id และอื่น ๆ

Rasterio มีประโยชน์เพราะง่ายต่อการอ่านในแรสเตอร์ในรูปแบบ numpy ชนิดข้อมูลที่เบาและรวดเร็ว นอกจากนี้เรายังสามารถเข้าถึงdictคุณสมบัติแรสเตอร์ซึ่งรวมถึงaffineข้อมูลทั้งหมดที่เราต้องใช้ในการแปลงแรสเตอร์ x, y ให้เป็นแถวอาเรย์และพิกัดโคล ดูคำอธิบายที่ดี @ perrygeo ของที่นี่

เราจบลงด้วยpt_dataประเภทซึ่งมีข้อมูลสำหรับแต่ละจุดและสกัดdict raster_valueเราสามารถเขียน shapefile ได้อย่างง่ายดายด้วยข้อมูลที่แยกออกมาเช่นกันหากเราต้องการ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.