【问题标题】:Python: fill a polygon area in an arrayPython:在数组中填充多边形区域
【发布时间】:2012-11-05 13:13:08
【问题描述】:

我有一个光栅图像(Tiff 格式)和一个转换为数组的 shapefile 格式的多边形区域。我希望找到一种优雅的方法来创建一个数组,其中多边形边界内的所有元素都有 1 值,多边形外的所有元素都有值 0。我的最终目标是用从 shapefile 派生的数组屏蔽从图像派生的数组.

我有以下问题,感谢您的帮助:

在使用 np.zeros((ds.RasterYSize, ds.RasterXSize)) 和我的多边形边界的地理空间坐标的像素位置创建一个空数组后,最好的解决方案是什么用 1 填充数组内的多边形?

from osgeo import gdal, gdalnumeric, ogr, osr
import osgeo.gdal
import math
import numpy
import numpy as np

def world2Pixel(geoMatrix, x, y):
    """
    Uses a gdal geomatrix (gdal.GetGeoTransform()) to calculate
    the pixel location of a geospatial coordinate
    (source http://www2.geog.ucl.ac.uk/~plewis/geogg122/vectorMask.html)
    geoMatrix
    [0] = top left x (x Origin)
    [1] = w-e pixel resolution (pixel Width)
    [2] = rotation, 0 if image is "north up"
    [3] = top left y (y Origin)
    [4] = rotation, 0 if image is "north up"
    [5] = n-s pixel resolution (pixel Height)

    """
    ulX = geoMatrix[0]
    ulY = geoMatrix[3]
    xDist = geoMatrix[1]
    yDist = geoMatrix[5]
    rtnX = geoMatrix[2]
    rtnY = geoMatrix[4]
    pixel = np.round((x - ulX) / xDist).astype(np.int)
    line = np.round((ulY - y) / xDist).astype(np.int)
    return (pixel, line)

# Open the image as a read only image
ds = osgeo.gdal.Open(inFile,gdal.GA_ReadOnly)
# Get image georeferencing information.
geoMatrix = ds.GetGeoTransform()
ulX = geoMatrix[0] # top left x (x Origin)
ulY = geoMatrix[3] # top left y (y Origin)
xDist = geoMatrix[1] # w-e pixel resolution (pixel Width)
yDist = geoMatrix[5] # n-s pixel resolution (pixel Height)
rtnX = geoMatrix[2] # rotation, 0 if image is "north up"
rtnY = geoMatrix[4] #rotation, 0 if image is "north up"

# open shapefile (= border of are of interest)
shp = osgeo.ogr.Open(poly)
source_shp = ogr.GetDriverByName("Memory").CopyDataSource(shp, "")
# get the coordinates of the points from the boundary of the shapefile
source_layer = source_shp.GetLayer(0)
feature = source_layer.GetNextFeature()
geometry = feature.GetGeometryRef()
pts = geometry.GetGeometryRef(0)
points = []
for p in range(pts.GetPointCount()):
   points.append((pts.GetX(p), pts.GetY(p)))
pnts = np.array(points).transpose()

print pnts
pnts
array([[  558470.28969598,   559495.31976318,   559548.50931402,
    559362.85560495,   559493.99688721,   558958.22572622,
    558529.58862305,   558575.0174293 ,   558470.28969598],
    [ 6362598.63707171,  6362629.15167236,  6362295.16466266,
    6362022.63453845,  6361763.96246338,  6361635.8559779 ,
    6361707.07684326,  6362279.69352024,  6362598.63707171]])

# calculate the pixel location of a geospatial coordinate (= define the border of my polygon)

pixels, line = world2Pixel(geoMatrix,pnts[0],pnts[1])
pixels
array([17963, 20013, 20119, 19748, 20010, 18939, 18081, 18172, 17963])
line
array([35796, 35734, 36402, 36948, 37465, 37721, 37579, 36433, 35796])

#create an empty array with value zero using 
data = np.zeros((ds.RasterYSize, ds.RasterXSize))

【问题讨论】:

    标签: python arrays numpy fill


    【解决方案1】:

    这本质上是一个point-in-polygon 问题。

    这里有一个小库来解决这个问题。它来自this 页面,经过一些修改使其更具可读性。

    pip.py

    #From http://www.ariel.com.au/a/python-point-int-poly.html
    # Modified by Nick ODell
    from collections import namedtuple
    
    def point_in_polygon(target, poly):
        """x,y is the point to test. poly is a list of tuples comprising the polygon."""
        point = namedtuple("Point", ("x", "y"))
        line = namedtuple("Line", ("p1", "p2"))
        target = point(*target)
    
        inside = False
        # Build list of coordinate pairs
        # First, turn it into named tuples
    
        poly = map(lambda p: point(*p), poly)
    
        # Make two lists, with list2 shifted forward by one and wrapped around
        list1 = poly
        list2 = poly[1:] + [poly[0]]
        poly = map(line, list1, list2)
    
        for l in poly:
            p1 = l.p1
            p2 = l.p2
    
            if p1.y == p2.y:
                # This line is horizontal and thus not relevant.
                continue
            if max(p1.y, p2.y) < target.y <= min(p1.y, p2.y):
                # This line is too high or low
                continue
            if target.x < max(p1.x, p2.x):
                # Ignore this line because it's to the right of our point
                continue
            # Now, the line still might be to the right of our target point, but 
            # still to the right of one of the line endpoints.
            rise = p1.y - p2.y
            run =  p1.x - p2.x
            try:
                slope = rise/float(run)
            except ZeroDivisionError:
                slope = float('inf')
    
            # Find the x-intercept, that is, the place where the line we are
            # testing equals the y value of our target point.
    
            # Pick one of the line points, and figure out what the run between it
            # and the target point is.
            run_to_intercept = target.x - p1.x
            x_intercept = p1.x + run_to_intercept / slope
            if target.x < x_intercept:
                # We almost crossed the line.
                continue
    
            inside = not inside
    
        return inside
    
    if __name__ == "__main__":
        poly = [(2,2), (1,-1), (-1,-1), (-1, 1)]
        print point_in_polygon((1.5, 0), poly)
    

    【讨论】:

    • 谢谢尼克,你知道解决多边形内点问题的函数吗?
    • @Gianni cut-n-paste "point-in-polygon" 把它放到本页右上角那个可爱的小搜索框中,按回车键... ;)
    • :) 我知道,我找到了这个stackoverflow.com/questions/3654289/…
    • img = Image.new('L', (width, height), 0) Python close :(
    【解决方案2】:

    接受的答案对我不起作用。

    我最终使用了 shapely 库。

    sudo pip install shapely
    

    代码:

    import shapely.geometry 
    
    poly = shapely.geometry.Polygon([(2,2), (1,-1), (-1,-1), (-1, 1)])
    point = shapely.geometry.Point(1.5, 0)
    
    point.intersects(poly)
    

    【讨论】:

      猜你喜欢
      • 2016-07-15
      • 2013-07-22
      • 1970-01-01
      • 2015-01-12
      • 1970-01-01
      • 1970-01-01
      • 2021-12-09
      • 1970-01-01
      • 2012-03-02
      相关资源
      最近更新 更多