Python 中带有 shapefile 的 GTiff 掩码以及 gdal、ogr 等

问题描述 投票:0回答:1

好的,经过一番摆弄,我从第二个注释行中的站点超链接中调整了一个脚本。该脚本的目的是使用具有多个多边形(每个多边形都有一个“名称”记录)的 shapefile 以 GTiff 格式剪辑/遮罩大型栅格(即无法适合 32 位 Python 2.7.5 应用程序)并保存将剪切的栅格放入“clip”子目录中,其中每个蒙版网格均以每个多边形的“名称”命名。 与原始脚本一样,它假设 GTiff 和 shapefile 位于同一投影中并且正确重叠,并且每秒处理约 100 个蒙版。 但是,我已将该脚本修改为 1)使用浮点值高程网格,2)仅将较大网格的窗口加载到当前多边形边界的内存中(即减少内存负载),2)导出GTiff 具有正确的(即未移动的)地理位置和价值。

但是,我遇到了每个蒙版网格都有一个我称之为“右侧阴影”的问题。也就是说,对于多边形中的每一条垂直线,其中该线的右侧位于给定多边形之外,遮罩网格将包括该多边形侧右侧的一个栅格单元。

因此,我的问题是,我做错了什么,导致蒙版网格出现右阴影?

我将尝试弄清楚如何发布示例 shapefile 和 tif 以便其他人可以重现。下面的代码还包含整数值卫星图像的注释行(例如,来自 geospatialpython.com 的原始代码)。

# RasterClipper.py - clip a geospatial image using a shapefile
# http://geospatialpython.com/2011/02/clip-raster-using-shapefile.html
# http://gis.stackexchange.com/questions/57005/python-gdal-write-new-raster-using-projection-from-old

import os, sys, time, Tkinter as Tk, tkFileDialog
import operator
from osgeo import gdal, gdalnumeric, ogr, osr
import Image, ImageDraw

def SelectFile(req = 'Please select a file:', ft='txt'):
    """ Customizable file-selection dialogue window, returns list() = [full path, root path, and filename]. """
    try:    # Try to select a csv dataset
        foptions = dict(filetypes=[(ft+' file','*.'+ft)], defaultextension='.'+ft)
        root = Tk.Tk(); root.withdraw(); fname = tkFileDialog.askopenfilename(title=req, **foptions); root.destroy()
        return [fname]+list(os.path.split(fname))
    except: print "Error: {0}".format(sys.exc_info()[1]); time.sleep(5);  sys.exit()

def rnd(v, N): return int(round(v/float(N))*N)
def rnd2(v): return int(round(v))

# Raster image to clip
rname = SelectFile('Please select your TIF DEM:',ft='tif')
raster = rname[2]
print 'DEM:', raster
os.chdir(rname[1])

# Polygon shapefile used to clip
shp = SelectFile('Please select your shapefile of catchments (requires Name field):',ft='shp')[2]
print 'shp:', shp

qs = raw_input('Do you want to stretch the image? (y/n)')
qs = True if qs == 'y' else False

# Name of base clip raster file(s)
if not os.path.exists('./clip/'):   os.mkdir('./clip/')
output = "/clip/clip"

# This function will convert the rasterized clipper shapefile
# to a mask for use within GDAL.
def imageToArray(i):
    """
    Converts a Python Imaging Library array to a
    gdalnumeric image.
    """
    a=gdalnumeric.fromstring(i.tostring(),'b')
    a.shape=i.im.size[1], i.im.size[0]
    return a

def arrayToImage(a):
    """
    Converts a gdalnumeric array to a
    Python Imaging Library Image.
    """
    i=Image.fromstring('L',(a.shape[1],a.shape[0]), (a.astype('b')).tostring())
    return i

def world2Pixel(geoMatrix, x, y, N= 5, r=True):
    """
    Uses a gdal geomatrix (gdal.GetGeoTransform()) to calculate
    the pixel location of a geospatial coordinate
    """
    ulX = geoMatrix[0]
    ulY = geoMatrix[3]
    xDist = geoMatrix[1]
    yDist = geoMatrix[5]
    rtnX = geoMatrix[2]
    rtnY = geoMatrix[4]
    if r:
        pixel = int(round(x - ulX) / xDist)
        line = int(round(ulY - y) / xDist)
    else:
        pixel = int(rnd(x - ulX, N) / xDist)
        line = int(rnd(ulY - y, N) / xDist)
    return (pixel, line)

def histogram(a, bins=range(0,256)):
    """
    Histogram function for multi-dimensional array.
    a = array
    bins = range of numbers to match
    """
    fa = a.flat
    n = gdalnumeric.searchsorted(gdalnumeric.sort(fa), bins)
    n = gdalnumeric.concatenate([n, [len(fa)]])
    hist = n[1:]-n[:-1]
    return hist

def stretch(a):
    """
    Performs a histogram stretch on a gdalnumeric array image.
    """
    hist = histogram(a)
    im = arrayToImage(a)
    lut = []
    for b in range(0, len(hist), 256):
        # step size
        step = reduce(operator.add, hist[b:b+256]) / 255
        # create equalization lookup table
        n = 0
        for i in range(256):
            lut.append(n / step)
            n = n + hist[i+b]
    im = im.point(lut)
    return imageToArray(im)

# Also load as a gdal image to get geotransform
# (world file) info
srcImage = gdal.Open(raster)
geoTrans_src = srcImage.GetGeoTransform()
#print geoTrans_src
pxs = int(geoTrans_src[1])
srcband = srcImage.GetRasterBand(1)
ndv = -9999.0
#ndv = 0

# Create an OGR layer from a boundary shapefile
shapef = ogr.Open(shp)
lyr = shapef.GetLayer()
minXl, maxXl, minYl, maxYl = lyr.GetExtent()
ulXl, ulYl = world2Pixel(geoTrans_src, minXl, maxYl)
lrXl, lrYl = world2Pixel(geoTrans_src, maxXl, minYl)
#poly = lyr.GetNextFeature()
for poly in lyr:
    pnm = poly.GetField("Name")

    # Convert the layer extent to image pixel coordinates
    geom = poly.GetGeometryRef()
    #print geom.GetEnvelope()
    minX, maxX, minY, maxY = geom.GetEnvelope()

    geoTrans = geoTrans_src
    ulX, ulY = world2Pixel(geoTrans, minX, maxY)
    lrX, lrY = world2Pixel(geoTrans, maxX, minY)

    # Calculate the pixel size of the new image
    pxWidth = int(lrX - ulX)
    pxHeight = int(lrY - ulY)

    # Load the source data as a gdalnumeric array
    #srcArray = gdalnumeric.LoadFile(raster)
    clip = gdalnumeric.BandReadAsArray(srcband, xoff=ulX, yoff=ulY, win_xsize=pxWidth, win_ysize=pxHeight)
    #clip = srcArray[:, ulY:lrY, ulX:lrX]

    # Create a new geomatrix for the image
    geoTrans = list(geoTrans)
    geoTrans[0] = minX
    geoTrans[3] = maxY

    # Map points to pixels for drawing the
    # boundary on a blank 8-bit,
    # black and white, mask image.
    points = []
    pixels = []
    #geom = poly.GetGeometryRef()
    pts = geom.GetGeometryRef(0)
    for p in range(pts.GetPointCount()):
        points.append((pts.GetX(p), pts.GetY(p)))
    for p in points:
        pixels.append(world2Pixel(geoTrans, p[0], p[1]))
    rasterPoly = Image.new("L", (pxWidth, pxHeight), 1)
    rasterize = ImageDraw.Draw(rasterPoly)
    rasterize.polygon(pixels, 0)
    mask = imageToArray(rasterPoly)

    # Clip the image using the mask
    #clip = gdalnumeric.choose(mask, (clip, 0)).astype(gdalnumeric.uint8)
    clip = gdalnumeric.choose(mask, (clip, ndv)).astype(gdalnumeric.numpy.float)

    # This image has 3 bands so we stretch each one to make them
    # visually brighter
    #for i in range(3):
    #    clip[i,:,:] = stretch(clip[i,:,:])
    if qs:  clip[:,:] = stretch(clip[:,:])

    # Save ndvi as tiff
    outputi = rname[1]+output+'_'+pnm+'.tif'
    #gdalnumeric.SaveArray(clip, outputi, format="GTiff", prototype=srcImage)
    driver = gdal.GetDriverByName('GTiff')
    DataSet = driver.Create(outputi, pxWidth, pxHeight, 1, gdal.GDT_Float64)
    #DataSet = driver.Create(outputi, pxWidth, pxHeight, 1, gdal.GDT_Int32)
    DataSet.SetGeoTransform(geoTrans)
    Projection = osr.SpatialReference()
    Projection.ImportFromWkt(srcImage.GetProjectionRef())
    DataSet.SetProjection(Projection.ExportToWkt())
    # Write the array
    DataSet.GetRasterBand(1).WriteArray(clip)
    DataSet.GetRasterBand(1).SetNoDataValue(ndv)

    # Save ndvi as an 8-bit jpeg for an easy, quick preview
    #clip = clip.astype(gdalnumeric.uint8)
    #gdalnumeric.SaveArray(clip, rname[1]+outputi+'.jpg', format="JPEG")
    #print '\t\tSaved:', outputi, '-.tif, -.jpg'
    print 'Saved:', outputi
    del mask, clip, geom
    del driver, DataSet

del shapef, srcImage, srcband
python masking gdal geotiff ogr
1个回答
15
投票

此功能已合并到 gdal 命令行实用程序中。鉴于您的情况,我看不出您有任何理由想在 Python 中自己执行此操作。

您可以使用 OGR 循环遍历几何图形,并为每个几何图形调用

gdalwarp
并使用适当的参数。

import ogr
import subprocess

inraster = 'NE1_HR_LC_SR_W_DR\NE1_HR_LC_SR_W_DR.tif'
inshape = '110m_cultural\ne_110m_admin_0_countries_lakes.shp'

ds = ogr.Open(inshape)
lyr = ds.GetLayer(0)

lyr.ResetReading()
ft = lyr.GetNextFeature()

while ft:
    
    country_name = ft.GetFieldAsString('admin')
    
    outraster = inraster.replace('.tif', '_%s.tif' % country_name.replace(' ', '_'))    
    subprocess.call(['gdalwarp', inraster, outraster, '-cutline', inshape, 
                     '-crop_to_cutline', '-cwhere', "'admin'='%s'" % country_name])
    
    ft = lyr.GetNextFeature()
    
ds = None

我在上面的示例中使用了来自 Natural Earth 的一些示例数据,对于巴西,剪裁如下:

enter image description here

如果您只想将图像裁剪到多边形区域并且不遮盖外部的任何内容,您可以转换形状文件,使其包含多边形的包络线。或者简单地松开形状文件并使用

gdal_translate
调用
-projwin
来指定感兴趣的区域。

© www.soinside.com 2019 - 2024. All rights reserved.