简体   繁体   中英

How to iterate through a numpy array and select neighboring cells

I am converting a USGS elevation raster data set to a Numpy array and then trying to select a position in the array at random. From this position I would like to create a method that identifies the eight surrounding cells to see if the elevations of these cells are within one meter of randomly selected cell.

This is where it gets a little more complex...if a neighbor is within one meter then the same method will be called on it and the process repeats until there is no longer cells within a meter of elevation or the number of cells selected reaches a prescribed limit.

If this is unclear hopefully this 2d array example below will make more sense. The bold/italicized cell ( 35 ) was randomly selected, the method was called on it (selecting all eight of its neighbors), and then the method was called on all neighbors until no more cells could be selected (all bold numbers were selected).

33 33 33 37 38 37 43 40

33 33 33 38 38 38 44 40

36 36 36 36 38 39 44 41

35 36 35 35 34 30 40 41

36 36 35 35 34 30 30 41

38 38 35 35 34 30 30 41

I am fairly good at java and know how to write a method to achieve this purpose, however GIS is primarily python based. I am in the process of learning python and have generated some code, but am having major problems adapting python to the GIS scripting interface.

Thanks for any help!

Question continued...

Thanks for the answer Bas Swinckels. I tried to incorporate your code into the code I have written up so far and ended up getting a infinite loop. Below is what I have written up. There are two main steps I need to overcome to make this work. This is an example of my array generated from my raster (-3.40e+38 is the no data value).

>>> 
[[ -3.40282306e+38  -3.40282306e+38  -3.40282306e+38 ...,  -3.40282306e+38
   -3.40282306e+38  -3.40282306e+38]
 [ -3.40282306e+38  -3.40282306e+38  -3.40282306e+38 ...,  -3.40282306e+38
   -3.40282306e+38  -3.40282306e+38]
 [ -3.40282306e+38  -3.40282306e+38  -3.40282306e+38 ...,  -3.40282306e+38
   -3.40282306e+38  -3.40282306e+38]
 ..., 
 [ -3.40282306e+38  -3.40282306e+38  -3.40282306e+38 ...,  -3.40282306e+38
   -3.40282306e+38  -3.40282306e+38]
 [ -3.40282306e+38  -3.40282306e+38  -3.40282306e+38 ...,  -3.40282306e+38
   -3.40282306e+38  -3.40282306e+38]
 [ -3.40282306e+38  -3.40282306e+38  -3.40282306e+38 ...,  -3.40282306e+38
   -3.40282306e+38  -3.40282306e+38]]
The script took 0.457999944687seconds.
>>> 

What I need to do is randomly select a position(cell) within this array and then run the code you generated on this point, let the flood fill algorithm grow until it maxes out like in the example above or until it reaches a prescribed number of cells (the user can set that no flood fill algorithm selection will be over 25 selected cells). Than ideally, new selected cells would be outputted as a single raster maintaining its georefrenced structure.

#import modules
from osgeo import gdal
import numpy as np
import os, sys, time

#start timing
startTime = time.time()

#register all of drivers
gdal.AllRegister()

#get raster
geo = gdal.Open("C:/Users/Harmon_work/Desktop/Python_Scratch/all_fill.img")

#read raster as array
arr = geo.ReadAsArray()
data = geo.ReadAsArray(0, 0, geo.RasterXSize, geo.RasterYSize).astype(np.float)
print data

#get image size
rows = geo.RasterYSize
cols = geo.RasterXSize
bands = geo.RasterCount

#get georefrence info
transform = geo.GetGeoTransform()
xOrgin = transform[0]
yOrgin = transform[3]
pixelWidth = transform[1]
pixelHeight = transform[5]

#get array dimensions
row = data.shape[0]
col = data.shape[1]

#get random position in array
randx = random.randint(1, row)
randy = random.randint(1, col)
print randx, randy

neighbours = [(-1,-1), (-1,0), (-1,1), (0,1), (1,1), (1,0), (1,-1), (0,-1)]
mask = np.zeros_like(data, dtype = bool)

#start coordinate
stack = [(randx,randy)]

while stack:
    x, y = stack.pop()
    mask[x, y] = True
    for dx, dy in neighbours:
        nx, ny = x + dx, y + dy
        if (0 <= nx < data.shape[0] and 0 <= ny < data.shape[1]
            and not mask[nx, ny] and abs(data[nx, ny] - data[x, y]) <= 1):
            stack.append((nx, ny))

for line in mask:
    print ''.join('01'[i] for i in line)

#run time
endTime = time.time()
print 'The script took ' + str(endTime-startTime) + 'seconds.'

Thanks again for your help. Please ask me questions if anything is unclear.

This can be done with an algorithm similar to flood fill , using a stack:

import numpy as np

z = '''33 33 33 37 38 37 43 40
33 33 33 38 38 38 44 40
36 36 36 36 38 39 44 41
35 36 35 35 34 30 40 41
36 36 35 35 34 30 30 41
38 38 35 35 34 30 30 41'''
z = np.array([[int(i) for i in line.split()] for line in z.splitlines()])

neighbours = [(-1,-1), (-1,0), (-1,1), (0,1), (1,1), (1,0), (1,-1), (0,-1)]
mask = np.zeros_like(z, dtype = bool)
stack = [(3,2)] # push start coordinate on stack

while stack:
    x, y = stack.pop()
    mask[x, y] = True
    for dx, dy in neighbours:
        nx, ny = x + dx, y + dy
        if (0 <= nx < z.shape[0] and 0 <= ny < z.shape[1] 
            and not mask[nx, ny] and abs(z[nx, ny] - z[x, y]) <= 1):
            stack.append((nx, ny))

for line in mask:
    print ''.join('01'[i] for i in line)    

Result:

00000000
00000000
11110000
11111000
11111000
00111000

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM