Changeset 1093


Ignore:
Timestamp:
Mar 16, 2005, 11:22:38 PM (20 years ago)
Author:
ole
Message:

Rewrote inside and outside polygon in terms of new separate_points_by_polygon in order to make outside_polygon as fast as inside_polygon.

Location:
inundation/ga/storm_surge/pyvolution
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • inundation/ga/storm_surge/pyvolution/test_util.py

    r1091 r1093  
    936936        res = outside_polygon( points, polygon )
    937937
    938         assert allclose( res, [3,4] )
     938        assert allclose( res, [3, 4] )
    939939
    940940
     
    955955        #evaluate to True as the point 0.5, 1.0 is outside the unit square
    956956
     957    def test_separate_points_by_polygon(self):
     958        U = [[0,0], [1,0], [1,1], [0,1]] #Unit square   
     959
     960        indices, count = separate_points_by_polygon( [[0.5, 0.5], [1, -0.5], [0.3, 0.2]], U )
     961        assert allclose( indices, [0,2,1] )
     962        assert count == 2
     963       
     964        #One more test of vector formulation returning indices
     965        polygon = [[0,0], [1,0], [0.5,-1], [2, -1], [2,1], [0,1]]
     966        points = [ [0.5, 0.5], [1, -0.5], [1.5, 0], [0.5, 1.5], [0.5, -0.5]]
     967        res, count = separate_points_by_polygon( points, polygon )
     968
     969        assert allclose( res, [0,1,2,4,3] )
     970        assert count == 3
     971
     972
     973        polygon = [[0,0], [1,0], [0.5,-1], [2, -1], [2,1], [0,1]]
     974        points = [ [0.5, 1.4], [0.5, 0.5], [1, -0.5], [1.5, 0], [0.5, 1.5], [0.5, -0.5]]
     975        res, count = separate_points_by_polygon( points, polygon )
     976
     977        assert allclose( res, [1,2,3,5,4,0] )       
     978        assert count == 3
     979       
     980
    957981    def test_populate_polygon(self):
    958982
  • inundation/ga/storm_surge/pyvolution/util.py

    r1091 r1093  
    1 """This modul contains various auxiliary function used by pyvolution.
    2 
    3 It is also a clearing house for function tat may later earn a module
     1"""This module contains various auxiliary function used by pyvolution.
     2
     3It is also a clearing house for functions that may later earn a module
    44of their own.
    55"""
     
    2020        theta = acos(v1)
    2121    except ValueError, e:
    22         print 'WARNING Angle acos(%s) failed: %s' %(str(v1), e)
     22        print 'WARNING (util.py): Angle acos(%s) failed: %s' %(str(v1), e)
    2323
    2424        #FIXME, hack to avoid acos(1.0) Value error
     
    3030        elif (v1+s >  -1.0) and (v1-s < -1.0):
    3131            theta = 3.1415926535897931
    32         print "WARNING, setting theta to ", theta   
     32        print 'WARNING (util.py): angle v1 is %f, setting acos to %f '\
     33              %(v1, theta)   
     34             
    3335    if v2 < 0:
    3436        #Quadrant 3 or 4
     
    912914#POLYGON STUFF
    913915#
    914 #FIXME: ALl these should be put into new module polygon.py
    915 
    916 def inside_polygon(point, polygon, closed = True, verbose = False):
     916#FIXME: All these should be put into new module polygon.py
     917
     918
     919             
     920#Redefine these to use separate_by_polygon which will put all inside indices
     921#in the first part of the indices array and outside indices in the last
     922
     923def inside_polygon(points, polygon, closed = True, verbose = False):
     924    """See separate_points_by_polygon for documentation
     925    """
     926   
     927    from Numeric import array, Float, reshape   
     928   
     929    if verbose: print 'Checking input to inside_polygon'   
     930    try:
     931        points = array(points).astype(Float)       
     932    except:
     933        msg = 'Points could not be converted to Numeric array'
     934        raise msg       
     935       
     936    try:
     937        polygon = array(polygon).astype(Float)             
     938    except:
     939        msg = 'Polygon could not be converted to Numeric array'
     940        raise msg               
     941   
     942   
     943   
     944    if len(points.shape) == 1:
     945        one_point = True
     946        points = reshape(points, (1,2))
     947    else:       
     948        one_point = False
     949
     950    indices, count = separate_points_by_polygon(points, polygon,
     951                                                closed, verbose)     
     952   
     953    if one_point:
     954        return count == 1
     955    else:
     956        return indices[:count]   
     957       
     958def outside_polygon(points, polygon, closed = True, verbose = False):
     959    """See separate_points_by_polygon for documentation
     960    """
     961   
     962    from Numeric import array, Float, reshape   
     963   
     964    if verbose: print 'Checking input to outside_polygon'   
     965    try:
     966        points = array(points).astype(Float)       
     967    except:
     968        msg = 'Points could not be converted to Numeric array'
     969        raise msg       
     970       
     971    try:
     972        polygon = array(polygon).astype(Float)             
     973    except:
     974        msg = 'Polygon could not be converted to Numeric array'
     975        raise msg               
     976   
     977   
     978   
     979    if len(points.shape) == 1:
     980        one_point = True
     981        points = reshape(points, (1,2))
     982    else:       
     983        one_point = False
     984
     985    indices, count = separate_points_by_polygon(points, polygon,
     986                                                closed, verbose)     
     987   
     988    if one_point:
     989        return count != 1
     990    else:
     991        return indices[count:][::-1]  #return reversed   
     992       
     993
     994def separate_points_by_polygon(points, polygon,
     995                               closed = True, verbose = False):
     996    """Determine whether points are inside or outside a polygon
     997   
     998    Input:
     999       points - Tuple of (x, y) coordinates, or list of tuples
     1000       polygon - list of vertices of polygon
     1001       closed - (optional) determine whether points on boundary should be
     1002       regarded as belonging to the polygon (closed = True)
     1003       or not (closed = False)
     1004   
     1005    Outputs:
     1006       indices: array of same length as points with indices of points falling
     1007       inside the polygon listed from the beginning and indices of points
     1008       falling outside listed from the end.
     1009       
     1010       count: count of points falling inside the polygon
     1011       
     1012       The indices of points inside are obtained as indices[:count]
     1013       The indices of points outside are obtained as indices[count:]       
     1014       
     1015       
     1016    Examples:
     1017       U = [[0,0], [1,0], [1,1], [0,1]] #Unit square
     1018       
     1019       separate_points_by_polygon( [[0.5, 0.5], [1, -0.5], [0.3, 0.2]], U)
     1020       will return the indices [0, 2, 1] and count == 2 as only the first
     1021       and the last point are inside the unit square
     1022       
     1023    Remarks:
     1024       The vertices may be listed clockwise or counterclockwise and
     1025       the first point may optionally be repeated.   
     1026       Polygons do not need to be convex.
     1027       Polygons can have holes in them and points inside a hole is
     1028       regarded as being outside the polygon.         
     1029               
     1030    Algorithm is based on work by Darel Finley,
     1031    http://www.alienryderflex.com/polygon/
     1032   
     1033    """   
     1034
     1035    from Numeric import array, Float, reshape, Int, zeros
     1036   
     1037   
     1038    #Input checks
     1039    try:
     1040        points = array(points).astype(Float)       
     1041    except:
     1042        msg = 'Points could not be converted to Numeric array'
     1043        raise msg       
     1044       
     1045    try:
     1046        polygon = array(polygon).astype(Float)             
     1047    except:
     1048        msg = 'Polygon could not be converted to Numeric array'
     1049        raise msg               
     1050
     1051    assert len(polygon.shape) == 2,\
     1052       'Polygon array must be a 2d array of vertices'
     1053
     1054    assert polygon.shape[1] == 2,\
     1055       'Polygon array must have two columns'
     1056
     1057    assert len(points.shape) == 2,\
     1058       'Points array must be a 2d array'
     1059       
     1060    assert points.shape[1] == 2,\
     1061       'Points array must have two columns'
     1062       
     1063    N = polygon.shape[0] #Number of vertices in polygon
     1064    M = points.shape[0]  #Number of points
     1065   
     1066    px = polygon[:,0]
     1067    py = polygon[:,1]   
     1068
     1069    #Used for an optimisation when points are far away from polygon
     1070    minpx = min(px); maxpx = max(px)
     1071    minpy = min(py); maxpy = max(py)   
     1072
     1073
     1074    #Begin main loop
     1075    indices = zeros(M, Int)
     1076   
     1077    inside_index = 0    #Keep track of points inside
     1078    outside_index = M-1 #Keep track of points outside (starting from end)
     1079   
     1080    for k in range(M):
     1081
     1082        if verbose:
     1083            if k %((M+10)/10)==0: print 'Doing %d of %d' %(k, M)
     1084       
     1085        #for each point   
     1086        x = points[k, 0]
     1087        y = points[k, 1]       
     1088
     1089        inside = False
     1090
     1091        if not x > maxpx or x < minpx or y > maxpy or y < minpy:
     1092            #Check polygon
     1093            for i in range(N):
     1094                j = (i+1)%N
     1095               
     1096                #Check for case where point is contained in line segment       
     1097                if point_on_line(x, y, px[i], py[i], px[j], py[j]):
     1098                    if closed:
     1099                        inside = True
     1100                    else:       
     1101                        inside = False
     1102                    break
     1103                else:   
     1104                    #Check if truly inside polygon                 
     1105                    if py[i] < y and py[j] >= y or\
     1106                       py[j] < y and py[i] >= y:
     1107                        if px[i] + (y-py[i])/(py[j]-py[i])*(px[j]-px[i]) < x:
     1108                            inside = not inside
     1109       
     1110        if inside:
     1111            indices[inside_index] = k
     1112            inside_index += 1
     1113        else:
     1114            indices[outside_index] = k
     1115            outside_index -= 1   
     1116           
     1117    return indices, inside_index
     1118
     1119
     1120def separate_points_by_polygon_c(points, polygon,
     1121                                 closed = True, verbose = False):
     1122    """Determine whether points are inside or outside a polygon
     1123
     1124    C-wrapper
     1125    """   
     1126
     1127    from Numeric import array, Float, reshape, zeros, Int
     1128   
     1129
     1130    if verbose: print 'Checking input to separate_points_by_polygon'   
     1131    #Input checks
     1132    try:
     1133        points = array(points).astype(Float)       
     1134    except:
     1135        msg = 'Points could not be converted to Numeric array'
     1136        raise msg       
     1137       
     1138    try:
     1139        polygon = array(polygon).astype(Float)             
     1140    except:
     1141        msg = 'Polygon could not be converted to Numeric array'
     1142        raise msg               
     1143
     1144    assert len(polygon.shape) == 2,\
     1145       'Polygon array must be a 2d array of vertices'
     1146
     1147    assert polygon.shape[1] == 2,\
     1148       'Polygon array must have two columns'
     1149
     1150    assert len(points.shape) == 2,\
     1151       'Points array must be a 2d array'
     1152       
     1153    assert points.shape[1] == 2,\
     1154       'Points array must have two columns'
     1155       
     1156    N = polygon.shape[0] #Number of vertices in polygon
     1157    M = points.shape[0]  #Number of points
     1158
     1159    from util_ext import separate_points_by_polygon
     1160
     1161    if verbose: print 'Allocating array for indices'
     1162
     1163    indices = zeros( M, Int )
     1164
     1165    if verbose: print 'Calling C-version of inside poly'
     1166    count = separate_points_by_polygon(points, polygon, indices,
     1167                                       int(closed), int(verbose))
     1168
     1169    return indices, count
     1170
     1171
     1172
     1173class Polygon_function:
     1174    """Create callable object f: x,y -> z, where a,y,z are vectors and
     1175    where f will return different values depending on whether x,y belongs
     1176    to specified polygons.
     1177   
     1178    To instantiate:
     1179     
     1180       Polygon_function(polygons)
     1181       
     1182    where polygons is a list of tuples of the form
     1183   
     1184      [ (P0, v0), (P1, v1), ...]
     1185     
     1186      with Pi being lists of vertices defining polygons and vi either
     1187      constants or functions of x,y to be applied to points with the polygon.
     1188     
     1189    The function takes an optional argument, default which is the value
     1190    (or function) to used for points not belonging to any polygon.
     1191    For example:
     1192   
     1193       Polygon_function(polygons, default = 0.03)       
     1194     
     1195    If omitted the default value will be 0.0 
     1196   
     1197    Note: If two polygons overlap, the one last in the list takes precedence
     1198
     1199    """
     1200   
     1201    def __init__(self, regions, default = 0.0):
     1202       
     1203        try:
     1204            len(regions)
     1205        except:
     1206            msg = 'Polygon_function takes a list of pairs (polygon, value). Got %s' %polygons           
     1207            raise msg
     1208
     1209
     1210        T = regions[0]                                   
     1211        try:
     1212            a = len(T)
     1213        except:   
     1214            msg = 'Polygon_function takes a list of pairs (polygon, value). Got %s' %polygons           
     1215            raise msg   
     1216                   
     1217        assert a == 2, 'Must have two component each: %s' %T       
     1218
     1219        self.regions = regions             
     1220        self.default = default
     1221         
     1222         
     1223    def __call__(self, x, y):
     1224        from util import inside_polygon
     1225        from Numeric import ones, Float, concatenate, array, reshape, choose
     1226       
     1227        x = array(x).astype(Float)
     1228        y = array(y).astype(Float)     
     1229       
     1230        N = len(x)
     1231        assert len(y) == N
     1232       
     1233        points = concatenate( (reshape(x, (N, 1)),
     1234                               reshape(y, (N, 1))), axis=1 )
     1235
     1236        if callable(self.default):
     1237            z = self.default(x,y)
     1238        else:                   
     1239            z = ones(N, Float) * self.default
     1240           
     1241        for polygon, value in self.regions:
     1242            indices = inside_polygon(points, polygon)
     1243           
     1244            #FIXME: This needs to be vectorised
     1245            if callable(value):
     1246                for i in indices:
     1247                    xx = array([x[i]])
     1248                    yy = array([y[i]])
     1249                    z[i] = value(xx, yy)[0]
     1250            else:   
     1251                for i in indices:
     1252                    z[i] = value
     1253
     1254        return z                 
     1255
     1256def read_polygon(filename):
     1257    """Read points assumed to form a polygon
     1258       There must be exactly two numbers in each line
     1259    """             
     1260   
     1261    #Get polygon
     1262    fid = open(filename)
     1263    lines = fid.readlines()
     1264    fid.close()
     1265    polygon = []
     1266    for line in lines:
     1267        fields = line.split(',')
     1268        polygon.append( [float(fields[0]), float(fields[1])] )
     1269
     1270    return polygon     
     1271   
     1272def populate_polygon(polygon, number_of_points, seed = None):
     1273    """Populate given polygon with uniformly distributed points.
     1274
     1275    Input:
     1276       polygon - list of vertices of polygon
     1277       number_of_points - (optional) number of points
     1278       seed - seed for random number generator (default=None)
     1279       
     1280    Output:
     1281       points - list of points inside polygon
     1282       
     1283    Examples:
     1284       populate_polygon( [[0,0], [1,0], [1,1], [0,1]], 5 )
     1285       will return five randomly selected points inside the unit square
     1286    """
     1287
     1288    from random import uniform, seed
     1289
     1290    seed(seed)
     1291
     1292    points = []
     1293
     1294    #Find outer extent of polygon
     1295    max_x = min_x = polygon[0][0]
     1296    max_y = min_y = polygon[0][1]
     1297    for point in polygon[1:]:
     1298        x = point[0]
     1299        if x > max_x: max_x = x
     1300        if x < min_x: min_x = x           
     1301        y = point[1]
     1302        if y > max_y: max_y = y
     1303        if y < min_y: min_y = y           
     1304
     1305
     1306    while len(points) < number_of_points:     
     1307        x = uniform(min_x, max_x)
     1308        y = uniform(min_y, max_y)       
     1309
     1310        if inside_polygon( [x,y], polygon ):
     1311            points.append([x,y])
     1312
     1313    return points
     1314
     1315####################################################################
     1316#Python versions of function that are also implemented in util_gateway.c
     1317#
     1318
     1319def gradient_python(x0, y0, x1, y1, x2, y2, q0, q1, q2):
     1320    """
     1321    """
     1322   
     1323    det = (y2-y0)*(x1-x0) - (y1-y0)*(x2-x0)           
     1324    a = (y2-y0)*(q1-q0) - (y1-y0)*(q2-q0)
     1325    a /= det
     1326
     1327    b = (x1-x0)*(q2-q0) - (x2-x0)*(q1-q0)
     1328    b /= det           
     1329
     1330    return a, b
     1331
     1332
     1333
     1334##############################################
     1335#Initialise module
     1336
     1337import compile
     1338if compile.can_use_C_extension('util_ext.c'):
     1339    from util_ext import gradient, point_on_line
     1340    separate_points_by_polygon = separate_points_by_polygon_c
     1341else:
     1342    gradient = gradient_python
     1343
     1344
     1345if __name__ == "__main__":
     1346    pass
     1347
     1348   
     1349   
     1350   
     1351   
     1352
     1353#OBSOLETED STUFF   
     1354def inside_polygon_old(point, polygon, closed = True, verbose = False):
     1355    #FIXME Obsoleted
    9171356    """Determine whether points are inside or outside a polygon
    9181357   
     
    10361475             
    10371476
     1477#def remove_from(A, B):
     1478#    """Assume that A
     1479#    """
     1480#    from Numeric import search_sorted##
     1481#
     1482#    ind = search_sorted(A, B)
     1483
     1484   
     1485
     1486def outside_polygon_old(point, polygon, closed = True, verbose = False):
     1487    #OBSOLETED
     1488    """Determine whether points are outside a polygon
     1489   
     1490    Input:
     1491       point - Tuple of (x, y) coordinates, or list of tuples
     1492       polygon - list of vertices of polygon
     1493       closed - (optional) determine whether points on boundary should be
     1494       regarded as belonging to the polygon (closed = True)
     1495       or not (closed = False)
     1496   
     1497    Output:
     1498       If one point is considered, True or False is returned.
     1499       If multiple points are passed in, the function returns indices
     1500       of those points that fall outside the polygon     
     1501       
     1502    Examples:
     1503       U = [[0,0], [1,0], [1,1], [0,1]] #Unit square   
     1504       outside_polygon( [0.5, 0.5], U )
     1505       will evaluate to False as the point 0.5, 0.5 is inside the unit square
     1506
     1507       ouside_polygon( [1.5, 0.5], U )
     1508       will evaluate to True as the point 1.5, 0.5 is outside the unit square
     1509       
     1510       outside_polygon( [[0.5, 0.5], [1, -0.5], [0.3, 0.2]], U )
     1511       will return the indices [1] as only the first and the last point
     1512       is inside the unit square
     1513    """
     1514
     1515    #FIXME: This is too slow
     1516   
     1517    res  = inside_polygon(point, polygon, closed, verbose)
     1518
     1519    if res is True or res is False:
     1520        return not res
     1521
     1522    #Now invert indices
     1523    from Numeric import arrayrange, compress
     1524    outside_indices = arrayrange(len(point))
     1525    for i in res:
     1526        outside_indices = compress(outside_indices != i, outside_indices)
     1527    return outside_indices
    10381528
    10391529def inside_polygon_c(point, polygon, closed = True, verbose = False):
     1530    #FIXME: Obsolete
    10401531    """Determine whether points are inside or outside a polygon
    10411532
     
    10861577
    10871578    if one_point:
    1088         return count == 1
     1579        return count == 1 #Return True if the point was inside
    10891580    else:
    10901581        if verbose: print 'Got %d points' %count
    1091         return indices[:count]
    1092 
    1093 
    1094 #def remove_from(A, B):
    1095 #    """Assume that A
    1096 #    """
    1097 #    from Numeric import search_sorted##
    1098 #
    1099 #    ind = search_sorted(A, B)
    1100 
    1101    
    1102 
    1103 def outside_polygon(point, polygon, closed = True, verbose = False):
    1104     """Determine whether points are outside a polygon
    1105    
    1106     Input:
    1107        point - Tuple of (x, y) coordinates, or list of tuples
    1108        polygon - list of vertices of polygon
    1109        closed - (optional) determine whether points on boundary should be
    1110        regarded as belonging to the polygon (closed = True)
    1111        or not (closed = False)
    1112    
    1113     Output:
    1114        If one point is considered, True or False is returned.
    1115        If multiple points are passed in, the function returns indices
    1116        of those points that fall outside the polygon     
    1117        
    1118     Examples:
    1119        U = [[0,0], [1,0], [1,1], [0,1]] #Unit square   
    1120        outside_polygon( [0.5, 0.5], U )
    1121        will evaluate to False as the point 0.5, 0.5 is inside the unit square
    1122 
    1123        ouside_polygon( [1.5, 0.5], U )
    1124        will evaluate to True as the point 1.5, 0.5 is outside the unit square
    1125        
    1126        outside_polygon( [[0.5, 0.5], [1, -0.5], [0.3, 0.2]], U )
    1127        will return the indices [1] as only the first and the last point
    1128        is inside the unit square
    1129     """
    1130 
    1131     res  = inside_polygon(point, polygon, closed, verbose)
    1132 
    1133     if res is True or res is False:
    1134         return not res
    1135 
    1136     #Now invert indices
    1137     from Numeric import arrayrange, compress
    1138     outside_indices = arrayrange(len(point))
    1139     for i in res:
    1140         outside_indices = compress(outside_indices != i, outside_indices)
    1141     return outside_indices
    1142 
    1143 
    1144 
    1145 class Polygon_function:
    1146     """Create callable object f: x,y -> z, where a,y,z are vectors and
    1147     where f will return different values depending on whether x,y belongs
    1148     to specified polygons.
    1149    
    1150     To instantiate:
    1151      
    1152        Polygon_function(polygons)
    1153        
    1154     where polygons is a list of tuples of the form
    1155    
    1156       [ (P0, v0), (P1, v1), ...]
    1157      
    1158       with Pi being lists of vertices defining polygons and vi either
    1159       constants or functions of x,y to be applied to points with the polygon.
    1160      
    1161     The function takes an optional argument, default which is the value
    1162     (or function) to used for points not belonging to any polygon.
    1163     For example:
    1164    
    1165        Polygon_function(polygons, default = 0.03)       
    1166      
    1167     If omitted the default value will be 0.0 
    1168    
    1169     Note: If two polygons overlap, the one last in the list takes precedence
    1170 
    1171     """
    1172    
    1173     def __init__(self, regions, default = 0.0):
    1174        
    1175         try:
    1176             len(regions)
    1177         except:
    1178             msg = 'Polygon_function takes a list of pairs (polygon, value). Got %s' %polygons           
    1179             raise msg
    1180 
    1181 
    1182         T = regions[0]                                   
    1183         try:
    1184             a = len(T)
    1185         except:   
    1186             msg = 'Polygon_function takes a list of pairs (polygon, value). Got %s' %polygons           
    1187             raise msg   
    1188                    
    1189         assert a == 2, 'Must have two component each: %s' %T       
    1190 
    1191         self.regions = regions             
    1192         self.default = default
    1193          
    1194          
    1195     def __call__(self, x, y):
    1196         from util import inside_polygon
    1197         from Numeric import ones, Float, concatenate, array, reshape, choose
    1198        
    1199         x = array(x).astype(Float)
    1200         y = array(y).astype(Float)     
    1201        
    1202         N = len(x)
    1203         assert len(y) == N
    1204        
    1205         points = concatenate( (reshape(x, (N, 1)),
    1206                                reshape(y, (N, 1))), axis=1 )
    1207 
    1208         if callable(self.default):
    1209             z = self.default(x,y)
    1210         else:                   
    1211             z = ones(N, Float) * self.default
    1212            
    1213         for polygon, value in self.regions:
    1214             indices = inside_polygon(points, polygon)
    1215            
    1216             #FIXME: This needs to be vectorised
    1217             if callable(value):
    1218                 for i in indices:
    1219                     xx = array([x[i]])
    1220                     yy = array([y[i]])
    1221                     z[i] = value(xx, yy)[0]
    1222             else:   
    1223                 for i in indices:
    1224                     z[i] = value
    1225 
    1226         return z                 
    1227 
    1228 def read_polygon(filename):
    1229     """Read points assumed to form a polygon
    1230        There must be exactly two numbers in each line
    1231     """             
    1232    
    1233     #Get polygon
    1234     fid = open(filename)
    1235     lines = fid.readlines()
    1236     fid.close()
    1237     polygon = []
    1238     for line in lines:
    1239         fields = line.split(',')
    1240         polygon.append( [float(fields[0]), float(fields[1])] )
    1241 
    1242     return polygon     
    1243    
    1244 def populate_polygon(polygon, number_of_points, seed = None):
    1245     """Populate given polygon with uniformly distributed points.
    1246 
    1247     Input:
    1248        polygon - list of vertices of polygon
    1249        number_of_points - (optional) number of points
    1250        seed - seed for random number generator (default=None)
    1251        
    1252     Output:
    1253        points - list of points inside polygon
    1254        
    1255     Examples:
    1256        populate_polygon( [[0,0], [1,0], [1,1], [0,1]], 5 )
    1257        will return five randomly selected points inside the unit square
    1258     """
    1259 
    1260     from random import uniform, seed
    1261 
    1262     seed(seed)
    1263 
    1264     points = []
    1265 
    1266     #Find outer extent of polygon
    1267     max_x = min_x = polygon[0][0]
    1268     max_y = min_y = polygon[0][1]
    1269     for point in polygon[1:]:
    1270         x = point[0]
    1271         if x > max_x: max_x = x
    1272         if x < min_x: min_x = x           
    1273         y = point[1]
    1274         if y > max_y: max_y = y
    1275         if y < min_y: min_y = y           
    1276 
    1277 
    1278     while len(points) < number_of_points:     
    1279         x = uniform(min_x, max_x)
    1280         y = uniform(min_y, max_y)       
    1281 
    1282         if inside_polygon( [x,y], polygon ):
    1283             points.append([x,y])
    1284 
    1285     return points
    1286 
    1287 ####################################################################
    1288 #Python versions of function that are also implemented in util_gateway.c
    1289 #
    1290 
    1291 def gradient_python(x0, y0, x1, y1, x2, y2, q0, q1, q2):
    1292     """
    1293     """
    1294    
    1295     det = (y2-y0)*(x1-x0) - (y1-y0)*(x2-x0)           
    1296     a = (y2-y0)*(q1-q0) - (y1-y0)*(q2-q0)
    1297     a /= det
    1298 
    1299     b = (x1-x0)*(q2-q0) - (x2-x0)*(q1-q0)
    1300     b /= det           
    1301 
    1302     return a, b
    1303 
    1304 
    1305 
    1306 ##############################################
    1307 #Initialise module
    1308 
    1309 import compile
    1310 if compile.can_use_C_extension('util_ext.c'):
    1311     from util_ext import gradient, point_on_line
    1312     inside_polygon = inside_polygon_c
    1313 else:
    1314     gradient = gradient_python
    1315 
    1316 
    1317 if __name__ == "__main__":
    1318     pass
    1319 
     1582       
     1583        return indices[:count]   #Return those indices that were inside
     1584
  • inundation/ga/storm_surge/pyvolution/util_ext.c

    r1091 r1093  
    6262
    6363
     64
     65int _separate_points_by_polygon(int M,     // Number of points
     66                                int N,     // Number of polygon vertices
     67                                double* points,
     68                                double* polygon,
     69                                long* indices,  // M-Array for storage indices
     70                                int closed,
     71                                int verbose) {
     72
     73  double minpx, maxpx, minpy, maxpy, x, y, px_i, py_i, px_j, py_j;
     74  int i, j, k, outside_index, inside_index, inside;
     75
     76  //Find min and max of poly used for optimisation when points
     77  //are far away from polygon
     78
     79  minpx = polygon[0]; maxpx = minpx;
     80  minpy = polygon[1]; maxpy = minpy;
     81
     82  for (i=0; i<N; i++) {
     83    px_i = polygon[2*i];
     84    py_i = polygon[2*i + 1];
     85
     86    if (px_i < minpx) minpx = px_i;
     87    if (px_i > maxpx) maxpx = px_i;
     88    if (py_i < minpy) minpy = py_i;
     89    if (py_i > maxpy) maxpy = py_i;
     90  }
     91
     92  //Begin main loop (for each point)
     93  inside_index = 0;    //Keep track of points inside
     94  outside_index = M-1; //Keep track of points outside (starting from end)   
     95  for (k=0; k<M; k++) {
     96    if (verbose){
     97      if (k %((M+10)/10)==0) printf("Doing %d of %d\n", k, M);
     98    }
     99   
     100    x = points[2*k];
     101    y = points[2*k + 1];
     102
     103    inside = 0;
     104
     105    //Optimisation
     106    if ((x > maxpx) || (x < minpx) || (y > maxpy) || (y < minpy)) {
     107      //Nothing
     108    } else {   
     109      //Check polygon
     110      for (i=0; i<N; i++) {
     111        //printf("k,i=%d,%d\n", k, i);
     112        j = (i+1)%N;
     113
     114        px_i = polygon[2*i];
     115        py_i = polygon[2*i+1];
     116        px_j = polygon[2*j];
     117        py_j = polygon[2*j+1];
     118
     119        //Check for case where point is contained in line segment
     120        if (_point_on_line(x, y, px_i, py_i, px_j, py_j)) {
     121          if (closed == 1) {
     122            inside = 1;
     123          } else {
     124            inside = 0;
     125          }
     126          break;
     127        } else {
     128          //Check if truly inside polygon
     129          if ( ((py_i < y) && (py_j >= y)) ||
     130               ((py_j < y) && (py_i >= y)) ) {
     131            if (px_i + (y-py_i)/(py_j-py_i)*(px_j-px_i) < x)
     132              inside = 1-inside;
     133          }
     134        }
     135      }
     136    }
     137    if (inside == 1) {
     138      indices[inside_index] = k;
     139      inside_index += 1;
     140    } else {
     141      indices[outside_index] = k;
     142      outside_index -= 1;   
     143    }
     144  } // End k
     145
     146  return inside_index;
     147}
     148
     149
     150
     151// Gateways to Python
     152PyObject *point_on_line(PyObject *self, PyObject *args) {
     153  //
     154  // point_on_line(x, y, x0, y0, x1, y1)
     155  //
     156
     157  double x, y, x0, y0, x1, y1;
     158  int res;
     159  PyObject *result;
     160
     161  // Convert Python arguments to C
     162  if (!PyArg_ParseTuple(args, "dddddd", &x, &y, &x0, &y0, &x1, &y1))
     163    return NULL;
     164
     165
     166  // Call underlying routine
     167  res = _point_on_line(x, y, x0, y0, x1, y1);
     168
     169  // Return values a and b
     170  result = Py_BuildValue("i", res);
     171  return result;
     172}
     173
     174
     175
     176PyObject *separate_points_by_polygon(PyObject *self, PyObject *args) {
     177  //def separate_points_by_polygon(points, polygon, closed, verbose, one_point):
     178  //  """Determine whether points are inside or outside a polygon
     179  //
     180  //  Input:
     181  //     point - Tuple of (x, y) coordinates, or list of tuples
     182  //     polygon - list of vertices of polygon
     183  //     closed - (optional) determine whether points on boundary should be
     184  //     regarded as belonging to the polygon (closed = True)
     185  //     or not (closed = False)
     186
     187  //
     188  //  Output:
     189  //     indices: array of same length as points with indices of points falling
     190  //     inside the polygon listed from the beginning and indices of points
     191  //     falling outside listed from the end.
     192  //     
     193  //     count: count of points falling inside the polygon
     194  //     
     195  //     The indices of points inside are obtained as indices[:count]
     196  //     The indices of points outside are obtained as indices[count:]       
     197  //
     198  //  Examples:
     199  //     separate_polygon( [[0.5, 0.5], [1, -0.5], [0.3, 0.2]] )
     200  //     will return the indices [0, 2, 1] as only the first and the last point
     201  //     is inside the unit square
     202  //
     203  //  Remarks:
     204  //     The vertices may be listed clockwise or counterclockwise and
     205  //     the first point may optionally be repeated.
     206  //     Polygons do not need to be convex.
     207  //     Polygons can have holes in them and points inside a hole is
     208  //     regarded as being outside the polygon.
     209  //
     210  //
     211  //  Algorithm is based on work by Darel Finley,
     212  //  http://www.alienryderflex.com/polygon/
     213  //
     214  //
     215
     216  PyArrayObject
     217    *points,
     218    *polygon,
     219    *indices;
     220
     221  int closed, verbose; //Flags
     222  int count, M, N;
     223
     224  // Convert Python arguments to C
     225  if (!PyArg_ParseTuple(args, "OOOii",
     226                        &points,
     227                        &polygon,
     228                        &indices,
     229                        &closed,
     230                        &verbose))
     231    return NULL;
     232
     233  M = points -> dimensions[0];   //Number of points
     234  N = polygon -> dimensions[0];  //Number of vertices in polygon
     235
     236  if (verbose) printf("Got %d points and %d polygon vertices\n", M, N);
     237 
     238  //Call underlying routine
     239  count = _separate_points_by_polygon(M, N,
     240                                      (double*) points -> data,
     241                                      (double*) polygon -> data,
     242                                      (long*) indices -> data,
     243                                      closed, verbose);
     244 
     245  //NOTE: return number of points inside..
     246  return Py_BuildValue("i", count);
     247}
     248
     249
     250
     251PyObject *gradient(PyObject *self, PyObject *args) {
     252  //
     253  // a,b = gradient(x0, y0, x1, y1, x2, y2, q0, q1, q2)
     254  //
     255
     256  double x0, y0, x1, y1, x2, y2, q0, q1, q2, a, b;
     257  PyObject *result;
     258
     259  // Convert Python arguments to C
     260  if (!PyArg_ParseTuple(args, "ddddddddd", &x0, &y0, &x1, &y1, &x2, &y2,
     261                        &q0, &q1, &q2))
     262    return NULL;
     263
     264
     265  // Call underlying routine
     266  _gradient(x0, y0, x1, y1, x2, y2,
     267            q0, q1, q2, &a, &b);
     268
     269  // Return values a and b
     270  result = Py_BuildValue("dd", a, b);
     271  return result;
     272}
     273
     274
     275// Method table for python module
     276static struct PyMethodDef MethodTable[] = {
     277  /* The cast of the function is necessary since PyCFunction values
     278   * only take two PyObject* parameters, and rotate() takes
     279   * three.
     280   */
     281
     282  //{"rotate", (PyCFunction)rotate, METH_VARARGS | METH_KEYWORDS, "Print out"},
     283  {"gradient", gradient, METH_VARARGS, "Print out"},
     284  {"point_on_line", point_on_line, METH_VARARGS, "Print out"},
     285  //{"inside_polygon", inside_polygon, METH_VARARGS, "Print out"},
     286  {"separate_points_by_polygon", separate_points_by_polygon,
     287                                 METH_VARARGS, "Print out"},
     288  {NULL, NULL, 0, NULL}   /* sentinel */
     289};
     290
     291
     292
     293// Module initialisation
     294void initutil_ext(void){
     295  Py_InitModule("util_ext", MethodTable);
     296
     297  import_array();     //Necessary for handling of NumPY structures
     298}
     299
     300
     301
     302
     303//OBSOLETE
    64304
    65305int _inside_polygon(int M,           // Number of points
     
    144384
    145385
    146 // Gateways to Python
    147 PyObject *point_on_line(PyObject *self, PyObject *args) {
    148   //
    149   // point_on_line(x, y, x0, y0, x1, y1)
    150   //
    151 
    152   double x, y, x0, y0, x1, y1;
    153   int res;
    154   PyObject *result;
    155 
    156   // Convert Python arguments to C
    157   if (!PyArg_ParseTuple(args, "dddddd", &x, &y, &x0, &y0, &x1, &y1))
    158     return NULL;
    159 
    160 
    161   // Call underlying routine
    162   res = _point_on_line(x, y, x0, y0, x1, y1);
    163 
    164   // Return values a and b
    165   result = Py_BuildValue("i", res);
    166   return result;
    167 }
    168 
    169 
    170 
    171386PyObject *inside_polygon(PyObject *self, PyObject *args) {
    172387  //def inside_polygon(point, polygon, closed, verbose, one_point):
     
    242457  return Py_BuildValue("i", count);
    243458}
    244 
    245 
    246 
    247 PyObject *gradient(PyObject *self, PyObject *args) {
    248   //
    249   // a,b = gradient(x0, y0, x1, y1, x2, y2, q0, q1, q2)
    250   //
    251 
    252   double x0, y0, x1, y1, x2, y2, q0, q1, q2, a, b;
    253   PyObject *result;
    254 
    255   // Convert Python arguments to C
    256   if (!PyArg_ParseTuple(args, "ddddddddd", &x0, &y0, &x1, &y1, &x2, &y2,
    257                         &q0, &q1, &q2))
    258     return NULL;
    259 
    260 
    261   // Call underlying routine
    262   _gradient(x0, y0, x1, y1, x2, y2,
    263             q0, q1, q2, &a, &b);
    264 
    265   // Return values a and b
    266   result = Py_BuildValue("dd", a, b);
    267   return result;
    268 }
    269 
    270 
    271 // Method table for python module
    272 static struct PyMethodDef MethodTable[] = {
    273   /* The cast of the function is necessary since PyCFunction values
    274    * only take two PyObject* parameters, and rotate() takes
    275    * three.
    276    */
    277 
    278   //{"rotate", (PyCFunction)rotate, METH_VARARGS | METH_KEYWORDS, "Print out"},
    279   {"gradient", gradient, METH_VARARGS, "Print out"},
    280   {"point_on_line", point_on_line, METH_VARARGS, "Print out"},
    281   {"inside_polygon", inside_polygon, METH_VARARGS, "Print out"},
    282   {NULL, NULL, 0, NULL}   /* sentinel */
    283 };
    284 
    285 
    286 
    287 // Module initialisation
    288 void initutil_ext(void){
    289   Py_InitModule("util_ext", MethodTable);
    290 
    291   import_array();     //Necessary for handling of NumPY structures
    292 }
Note: See TracChangeset for help on using the changeset viewer.