I was looking for a way of extracting elevation values from a DEM and decided to use numpy for this purpose. The idea was to use a polyline, extract points every x m (interval is the raster cell size) and obtain the elevation for each point.
At first I tried using the “GetCellValue_management” tool, but when you do that a 100.000 times, it is pretty slow. When you convert a raster to numpy, the numpy array does not know the coordinates of each element of the array. To solve that you have to establish that relationship.
What I did was:
This method turned out to be over 170x faster than using the repetitive use of the tool “GetCellValue_management” and still does not require a license level higher than ArcGIS for Desktop Basic and there is no need for the Spatial Analyst (or any other extension) either.
#------------------------------------------------------------------------------- # Name: dem_numpy.py # Purpose: extract dem values along a given line using numpy # # Author: xbakker # # Created: 23/12/2014 #------------------------------------------------------------------------------- import arcpy def main(): import datetime start_time = datetime.datetime.now() # inputs dem = r"D:\Xander\Numpy\fgdb\test.gdb\DEM" fc = r"D:\Xander\Numpy\fgdb\test.gdb\Line" dist_from = 25000 dist_to = 125000 # get polyline and extract a part line = getFirstPolyline(fc) subline = line.segmentAlongLine(dist_from, dist_to, False) # 10.3 required cellsize = getRasterCellSize(dem) # get extent from line and adapt extent to raster ext1 = getExtentFromPolyline(subline) ext2 = adaptExtentUsingRaster(ext1, dem, cellsize) # create numpy array np = createNumpyArrayUsingExtent(dem, ext2, cellsize) # loop through points and extract values from raster d = 0 pnts = getListOfPointsFromPolyline(subline, cellsize) for pnt in pnts: row, col = getRowColExtentFromXY(ext2, pnt, cellsize) z = np.item(row,col) print "{0}\t{1}\t{2}\t{3}\t{4}\t{5}".format(d, col, row, pnt.X, pnt.Y, z) d += cellsize end_time = datetime.datetime.now() delta = end_time - start_time print "Elapsed: {0} seconds".format(delta.total_seconds()) # Elapsed: 4.519 seconds def getListOfPointsFromPolyline(line, interval): pnts = [] d = 0 max_len = line.length while d < max_len: pnt = line.positionAlongLine(d, False) pnts.append(pnt.firstPoint) d += interval pnts.append(line.lastPoint) return pnts def getRowColExtentFromXY(ext, pnt, cellsize): # r, c start at 0 from upper left col = int(((pnt.X - ext.XMin) - ((pnt.X - ext.XMin) % cellsize)) / cellsize) row = int(((ext.YMax - pnt.Y) - ((ext.YMax - pnt.Y) % cellsize)) / cellsize) return row, col def createNumpyArrayUsingExtent(raster, ext, cellsize): lowerLeft = arcpy.Point(ext.XMin, ext.YMin) ncols = int(ext.width / cellsize) nrows = int(ext.height / cellsize) return arcpy.RasterToNumPyArray(raster, lowerLeft, ncols, nrows) def adaptExtentUsingRaster(ext, raster, cellsize): ras_ext = arcpy.Describe(raster).extent xmin = ext.XMin - ((ext.XMin - ras_ext.XMin) % cellsize) ymin = ext.YMin - ((ext.YMin - ras_ext.YMin) % cellsize) xmax = ext.XMax + ((ras_ext.XMax - ext.XMax) % cellsize) ymax = ext.YMax + ((ras_ext.YMax - ext.YMax) % cellsize) return arcpy.Extent(xmin, ymin, xmax, ymax) def getRasterCellSize(raster): desc = arcpy.Describe(raster) return (desc.meanCellHeight + desc.meanCellWidth) / 2 def getExtentFromPolyline(line): return line.extent def getExtentFromFC(fc): return arcpy.Describe(fc).extent def getFirstPolyline(fc): # return first feature geometry return arcpy.da.SearchCursor(fc, ("SHAPE@",)).next()[0] if __name__ == '__main__': main()
Please note that the code assumes that the polyline is completely within the raster extent and there is no handling of no data values in the raster.
Nice Xander !!! fix def spelling line 32 and 66....Arrat... to ...Array..
There is lots you can do with geometry objects and numpy without licenses ie your defs beginning lines 49,60 and 72 Good job...We should start Numpy Snippets ...
Lead the way, and I'll follow! ... or do we first need to get some python editor with numpy and arcpy support on the iPad?
edit: thanks for the catch!
I will get back on the PC tonight...during the interim, I do have a python implementation for the iThingy but no external modules are available
Same here on my Android devices... would be cool though...
ID = [ 0,1,2,3,4,5,6,7,8,9]
Ys = [9,7,6,3,4,1,5,8,2,0]
Xs =[0, 2, 1, 3, 5, 4, 7, 6, 9, 8]
XY_array = np.array(zip(ID,Xs,Ys), dtype= [('ID','<i4'),('X','<f8'),('Y','<f8')])
print XY_array
array([(0, 0.0, 9.0), (1, 2.0, 7.0), (2, 1.0, 6.0), (3, 3.0, 3.0),
(4, 5.0, 4.0), (5, 4.0, 1.0), (6, 7.0, 5.0), (7, 6.0, 8.0),
(8, 9.0, 2.0), (9, 8.0, 0.0)],
dtype=[('ID', '<i4'), ('X', '<f8'), ('Y', '<f8')])
Just bought Pythonista from the iStore for $6.99, comes with numpy, matplotlib and a few others I haven't looked at yet ( Pythonista )
No syntax highlighting on the iThingy however....baby steps I suppose