def make_valid_polygon(geom, precision=2): if geom.is_valid == True or isinstance(geom, (Polygon, MultiPolygon)) == False: return geom if isinstance(geom, Polygon): area = geom.area buff_geom = geom.buffer(0) buff_area = buff_geom.area if round(area, precision) == round(buff_area, precision): return buff_geom else: exterior = geom.exterior interiors = geom.interiors exterior = exterior.intersection(exterior) interiors = [MultiPolygon(polygonize(i.intersection(i)))[0].exterior for i in interiors] result = MultiPolygon([Polygon(i.exterior, interiors) for i in polygonize(exterior)]) return result elif isinstance(geom, MultiPolygon): result = [make_valid_polygon(poly) for poly in geom] result = MultiPolygon(flatten([[i for i in j] if isinstance(j, MultiPolygon) else j for j in result])) if len(result) == 1: return result[0] else: return result
def merge_vor(vor1, vor2): ax = plt.subplot(1, 1, 1) lines1 = [ geometry.LineString(vor1.vertices[line]) for line in vor1.ridge_vertices if -1 not in line ] lines2 = [ geometry.LineString(vor2.vertices[line]) for line in vor2.ridge_vertices if -1 not in line ] for coords in vor1.points: ax.scatter(coords[0], coords[1]) for coords in vor2.points: ax.scatter(coords[0], coords[1]) #points1 = vor1.points.tolist() #for x,y in points1: # ax.scatter(x,y) #points2 = vor2.points.tolist() #for x,y in points2: # ax.scatter(x,y) shape1 = ops.unary_union(list(ops.polygonize(lines1))) x1, y1 = shape1.exterior.xy ax.plot(x1, y1) shape2 = ops.unary_union(list(ops.polygonize(lines2))) x2, y2 = shape2.exterior.xy ax.plot(x2, y2) plt.show()
def doPolygonize(): blocks = polygonize(lines) writeBlocks(blocks, args[0] + '-blocks.geojson') blocks = polygonize(lines) bounds = Polygon([ [minlng, minlat], [minlng, maxlat], [maxlng, maxlat], [maxlng, minlat], [minlng, minlat] ]) # Geometry transform function based on pyproj.transform project = partial( pyproj.transform, pyproj.Proj(init='EPSG:3785'), pyproj.Proj(init='EPSG:4326')) print bounds print transform(project, bounds) print 'finding holes' for index, block in enumerate(blocks): if index % 1000 == 0: print "diff'd %s" % (index) if not block.is_valid: print explain_validity(block) print transform(project, block) else: bounds = bounds.difference(block) print bounds
def pfr_lines(self, inp, core): c = QuadContourGenerator.from_rectilinear(core.psi_data.R[0], core.psi_data.Z[:, 0], core.psi_data.psi_norm) contours = c.contour(0.999) if len(contours) == 1: # then we're definitely dealing with a surface inside the seperatrix raise ValueError("Did not find PFR flux surface. Stopping.") else: # we need to find the surface that is contained within the private flux region for j, contour in enumerate(contours): # if the contour is entirely below the x-point and contour extends to both the left and the # right of the x-point horizontally, then the contour is almost certainly the desired PFR contour if np.amax(contour[:, 1]) < core.pts.xpt[1] and \ np.amin(contour[:, 0]) < core.pts.xpt[0] < np.amax(contour[:, 0]): # then it's a probably a pfr flux surface, might need to add additional checks later # make sure the line goes from inboard to outboard if contour[-1, 0] < contour[0, 0]: contour = np.flipud(contour) # find cut points cut_pt_ib = LineString(contour).intersection(inp.wall_line)[0] cut_pt_ob = LineString(contour).intersection(inp.wall_line)[1] dist1 = LineString(contour).project(cut_pt_ib, normalized=True) cutline_temp = cut(LineString(contour), dist1)[1] # reverse line point order so we can reliably find the second intersection point cutline_temp_rev = LineString(np.flipud(np.asarray(cutline_temp.coords))) dist2 = cutline_temp_rev.project(cut_pt_ob, normalized=True) cutline_final_rev = cut(cutline_temp_rev, dist2)[1] # reverse again for final pfr flux line pfr_flux_line = LineString(np.flipud(np.asarray(cutline_final_rev.coords))) # add pfr_line intersection points on inboard side # for some reason, union freaks out when I try to do inboard and outboard # at the same time. union = inp.wall_line.union(cut(LineString(contour), 0.5)[0]) result = [geom for geom in polygonize(union)][0] inp.wall_line = LineString(result.exterior.coords) # add pfr line intersection points on outboard side union = inp.wall_line.union(cut(LineString(contour), 0.5)[1]) result = [geom for geom in polygonize(union)][0] inp.wall_line = LineString(result.exterior.coords) # cut out pfr section of wall line wall_pts = np.asarray(inp.wall_line.xy).T wall_start_pos = np.where((wall_pts == cut_pt_ob).all(axis=1))[0][0] wall_line_rolled = LineString(np.roll(wall_pts, -wall_start_pos, axis=0)) wall_line_cut_pfr = cut(wall_line_rolled, wall_line_rolled.project(cut_pt_ib, normalized=True))[0] # create LineString with pfr line and section of wall line self.pfr_line = linemerge((pfr_flux_line, wall_line_cut_pfr)) break
def create_union(in_ply1, in_ply2, result_geojson): """ Create union polygon :param in_ply1: first input shapely polygon :param in_ply2: second input shapely polygon :param result_geojson: output geojson file including full file path :return: shapely MultiPolygon """ # union the polygon outer linestrings together outer_bndry = in_ply1.boundary.union(in_ply2.boundary) # rebuild linestrings into polygons output_poly_list = polygonize(outer_bndry) out_geojson = dict(type='FeatureCollection', features=[]) # generate geojson file output for (index_num, ply) in enumerate(output_poly_list): feature = dict(type='Feature', properties=dict(id=index_num)) feature['geometry'] = ply.__geo_interface__ out_geojson['features'].append(feature) # create geojson file on disk json.dump(out_geojson, open(result_geojson, 'w')) # create shapely MultiPolygon ply_list = [] for fp in polygonize(outer_bndry): ply_list.append(fp) out_multi_ply = MultiPolygon(ply_list) return out_multi_ply
def print_cid_count(node_array_x, node_array_y): gridx = np.linspace(300000, 800000, 5) gridy = np.linspace(3700000, 5500000, 5) grid, _, _ = np.histogram2d(node_array_x, node_array_y, bins=[gridx, gridy]) # plotting # plt.figure(figsize=(9, 7), dpi=90, facecolor='w', edgecolor='k') # plt.plot(node_array_x, node_array_y, 'ro') # plt.grid(True) # plt.figure(figsize=(9, 7), dpi=90, facecolor='w', edgecolor='k') # plt.pcolormesh(gridx, gridy, grid) # plt.plot(node_array_x, node_array_y, 'ro') # plt.colorbar() # plt.show() points = gpd.GeoDataFrame({"x":node_array_x,"y":node_array_y}) points['geometry'] = points.apply(lambda p: Point(p.x, p.y), axis=1) # print(points.head(2)) # np mesh-grid to shapely polygons hlines = [((x1, yi), (x2, yi)) for x1, x2 in list(zip(gridx[:-1], gridx[1:])) for yi in gridy] vlines = [((xi, y1), (xi, y2)) for y1, y2 in zip(gridy[:-1], gridy[1:]) for xi in gridx] polys = list(polygonize(MultiLineString(hlines + vlines))) grids = list(polygonize(MultiLineString(hlines + vlines))) cid = [i for i in range(len(grids))] grid = gpd.GeoDataFrame({"cid":cid,"geometry":polys}) # print(grid.head(2)) # number of points in polygons pointInPolys = sjoin(points, grid, how='left') print(pointInPolys.groupby(['cid']).size().reset_index(name='count'))
def sol_lines(self, inp, R, Z, core): #find value of psi at outside of what we're going to call the SOL self.sol_lines = [] self.sol_lines_cut = [] sol_width_obmp = 0.02 psi_pts = np.linspace(1, inp.sollines_psi_max, inp.num_sollines + 1, endpoint=True)[1:] for i, v in enumerate(psi_pts): num_lines = int( len(cntr.Cntr(R, Z, core.psi_norm_raw).trace(v)) / 2) if num_lines == 1: #then we're definitely dealing with a surface inside the seperatrix x, y = draw_line(R, Z, core.psi_norm_raw, v, 0) self.sol_lines.append(LineString(np.column_stack((x, y)))) else: #TODO: pass for line in self.sol_lines: #find intersection points with the wall int_pts = line.intersection(inp.wall_line) #cut line at intersection points cut_line = cut(line, line.project(int_pts[0], normalized=True))[1] cut_line = cut(cut_line, cut_line.project(int_pts[1], normalized=True))[0] self.sol_lines_cut.append(cut_line) #add wall intersection points from divertor legs and sol lines to wall_line. #This is necessary to prevent thousands of tiny triangles from forming if the #end of the flux line isn't exactly on top of the wall line. #add inboard seperatrix strike point union = inp.wall_line.union(core.ib_div_line) result = [geom for geom in polygonize(union)][0] inp.wall_line = LineString(result.exterior.coords) #add outboard seperatrix strike point union = inp.wall_line.union(core.ob_div_line) result = [geom for geom in polygonize(union)][0] inp.wall_line = LineString(result.exterior.coords) #add sol line intersection points on inboard side #for some reason, union freaks out when I try to do inboard and outboard #at the same time. for num, line in enumerate(self.sol_lines): union = inp.wall_line.union(cut(line, 0.5)[0]) result = [geom for geom in polygonize(union)][0] inp.wall_line = LineString(result.exterior.coords) #add sol line intersection points on outboard side for num, line in enumerate(self.sol_lines): union = inp.wall_line.union(cut(line, 0.5)[1]) result = [geom for geom in polygonize(union)][0] inp.wall_line = LineString(result.exterior.coords)
def st_polygon(draw, n=10, num_holes=None, xmid=0, ymid=0): # Handle defaults if num_holes is None: num_holes = draw(st.integers(0, 4)) # Build outer shell tries = 50 poly = None while tries > 0: tries -= 1 points = (np.random.rand(n, 2) - 0.5) * 100 points[:, 0] = points[:, 0] + xmid points[:, 1] = points[:, 1] + ymid vor = Voronoi(points) mls = sg.MultiLineString([ vor.vertices[s] for s in vor.ridge_vertices if all(np.array(s) >= 0) ]) poly = cascaded_union(list(polygonize(mls))) poly = poly.intersection(sg.box(-50, -50, 50, 50)) if isinstance(poly, sg.Polygon) and not poly.is_empty: break if not isinstance(poly, sg.Polygon): raise ValueError("Failed to construct polygon") # Build holes remaining_holes = num_holes tries = 50 while remaining_holes > 0 and tries > 0: tries -= 1 points = (np.random.rand(n, 2) - 0.5) * 50 points[:, 0] = points[:, 0] + xmid points[:, 1] = points[:, 1] + ymid vor = Voronoi(points) mls = sg.MultiLineString([ vor.vertices[s] for s in vor.ridge_vertices if all(np.array(s) >= 0) ]) hole_components = [p for p in polygonize(mls) if poly.contains(p)] if hole_components: hole = cascaded_union( [p for p in polygonize(mls) if poly.contains(p)]) if isinstance(hole, sg.MultiPolygon): hole = hole[0] new_poly = poly.difference(hole) if isinstance(new_poly, sg.Polygon): poly = new_poly remaining_holes -= 1 return poly
def circle_group_area(radiuses, positions): circles = [] for i in range(len(radiuses)): circles.append( point.Point(positions[i][0], positions[i][1]).buffer(radiuses[i])) union = ops.unary_union(circles) result = [geom for geom in ops.polygonize(union)] completeareas = [list(ops.polygonize(g.exterior))[0].area for g in result] max_index = np.argmax(completeareas) result_area = result[max_index].area return result_area
def alpha_shape(self, points, alpha): """ Compute the alpha shape (concave hull) of a set of points. @param points: Iterable container of points. @param alpha: alpha value to influence the gooeyness of the border. Smaller numbers don't fall inward as much as larger numbers. Too large, and you lose everything! """ if len(points) < 4: # When you have a triangle, there is no sense in computing an alpha # shape. return geometry.MultiPoint(list(points)).convex_hull def add_edge(edges, edge_points, coords, i, j): """Add a line between the i-th and j-th points, if not in the list already""" if (i, j) in edges or (j, i) in edges: # already added return edges.add((i, j)) edge_points.append(coords[[i, j]]) coords = np.array([point.coords[0] for point in points]) tri = Delaunay(coords) edges = set() edge_points = [] # loop over triangles: # ia, ib, ic = indices of corner points of the triangle for ia, ib, ic in tri.vertices: pa = coords[ia] pb = coords[ib] pc = coords[ic] # Lengths of sides of triangle a = sqrt((pa[0] - pb[0])**2 + (pa[1] - pb[1])**2) b = sqrt((pb[0] - pc[0])**2 + (pb[1] - pc[1])**2) c = sqrt((pc[0] - pa[0])**2 + (pc[1] - pa[1])**2) # Semiperimeter of triangle s = (a + b + c) / 2.0 # Area of triangle by Heron's formula area = sqrt(s * (s - a) * (s - b) * (s - c)) try: circum_r = a * b * c / (4.0 * area) except (ZeroDivisionError): circum_r = 0 # Here's the radius filter. # print circum_r if circum_r < 1.0 / alpha: add_edge(edges, edge_points, coords, ia, ib) add_edge(edges, edge_points, coords, ib, ic) add_edge(edges, edge_points, coords, ic, ia) m = geometry.MultiLineString(edge_points) triangles = list(polygonize(m)) return cascaded_union(triangles), edge_points
def _try_build_poly(self, points): poly = Polygon(points) # Everything is OK, points are valid polygon if poly.is_valid: return poly # Polygon contains small bowties, we can fix them (https://stackoverflow.com/questions/13062334/polygon-intersection-error-in-shapely-shapely-geos-topologicalerror-the-opera) fixed_poly = poly.buffer(0) if not fixed_poly.geom_type == 'MultiPolygon' and self._area_close( fixed_poly, poly): return fixed_poly # Trying to build multiple polygons from self intersected polygon line_non_simple = LineString(points) mls = unary_union(line_non_simple) polygons = list(polygonize(mls)) multi_poly = MultiPolygon(polygons) if len(polygons) and multi_poly.is_valid: return multi_poly # Trying to draw convex hull convex_hull = poly.convex_hull if self._area_close(convex_hull, poly): return convex_hull # We are failing to build polygon, this shall be reported via error log raise ValueError(f'Fail to build polygon from {points}')
def __init__(self, way_map, start, end): """ Args: way_map(WayMap): the map the trip takes place on start(2-tuple): starting point end(2-tuple): destination """ self.way_map = way_map self.start = start self.end = end try: self.list_of_nodes = astar_path(self.way_map.graph, self.start, self.end) except NetworkXNoPath: self.list_of_nodes = [] self.linestring = LineString(self.list_of_nodes) self.straightline_length = Path.straightline_distance(self.start, self.end) self.straightline = LineString([self.start, self.end]) self.deviation_factor = (self.linestring.length / self.straightline_length) self.obstacles = [] for polygon in list(polygonize([self.linestring, self.straightline])): self.obstacles.append(Obstacle(self.way_map, self.start, self.end, polygon))
def difference(line1, line2, close_ends=False): """ Create polygons from two LineString objects. Parameters: line1 (LineString) : a line representing the initial condition. line2 (LineString) : a line representing the final condition. close_ends (bool) : option to close open line ends with vertical line segments. Returns: intersections (Point array) : the intersections between the LineString objects. polygons (Polygon array) : the polygons between the lines. signs (int array) : contains values of +1 or -1 to identify polygons as cut or fill. """ if close_ends==True: line1, line2 = close(line1, line2) intersections = line1.intersection(line2) segs1 = cut_by_points([line1], intersections) segs2 = cut_by_points([line2], intersections) polygons = polygonize([segs1, segs2]) signs = sign(linemerge(segs1), linemerge(segs2)) # can't pass the polygonize generator to my class so convert the polygons into an array polygontxt = [] areas = [] for i, poly in enumerate(polygons): polygontxt.append(poly) areas.append(poly.area*signs[i]) cutfill = pnd.Series(asarray(areas), name='area') return intersections, polygontxt, cutfill
def alpha_shape(points, alpha): def add_edge(points, i, j): if (i, j) in edges or (j, i) in edges: return edges.add((i, j)) edge_points.append((points[i], points[j])) tri = DelaunayTri(points) edges = set() edge_points = [] for i1, i2, i3 in tri.vertices: x1, y1 = tri.points[i1] x2, y2 = tri.points[i2] x3, y3 = tri.points[i3] a = hypot(x1 - x2, y1 - y2) b = hypot(x2 - x3, y2 - y3) c = hypot(x3 - x1, y3 - y1) s = (a + b + c) / 2.0 area = sqrt(s * (s - a) * (s - b) * (s - c)) radius = a * b * c / (4 * area) if radius < 1.0 / alpha: add_edge(tri.points, i1, i2) add_edge(tri.points, i2, i3) add_edge(tri.points, i3, i1) shape = cascaded_union(list(polygonize(MultiLineString(edge_points)))) return shape
def voronoi_diagram(points, plot=False, size=None, method='box'): multi = shapely.geometry.multipoint.MultiPoint(points) if method == 'box': g = shapely.geometry.box(*multi.bounds) elif method == 'convex_hull': g = multi.convex_hull size = size if size else pow(g.area, 0.5) buffer = g.buffer(size) points_and_bound = points + [ shapely.geometry.point.Point(c) for c in buffer.boundary.coords ][:-1] vor = Voronoi([list(g.coords)[0] for g in points_and_bound]) lines = [ shapely.geometry.LineString(vor.vertices[line]) for line in vor.ridge_vertices if -1 not in line ] polygons = [ poly.intersection(g.buffer(size / 10)) for poly in polygonize(lines) ] if plot: voronoi_plot_2d(vor) ridges = pd.DataFrame(vor.ridge_points, columns=['a', 'b']) ridges = ridges[(ridges['a'] < len(points)) & (ridges['b'] < len(points))] ridges['geometry'] = ridges.apply(lambda r: shapely.geometry.LineString( [points[r['a']], points[r['b']]]), axis=1) return polygons, ridges[['a', 'b', 'geometry']].values.tolist()
def polygon_shapes_from_voronoi_lines(poly_lines, geo_shape=None, shapes_from_diff_with_min_area=None): """ Form shapely Polygons objects from a list of shapely LineString objects in `poly_lines` by using [`polygonize`](http://toblerity.org/shapely/manual.html#shapely.ops.polygonize). If `geo_shape` is not None, then the intersection between any generated polygon and `geo_shape` is taken in case they overlap (i.e. the Voronoi regions at the border are "cut" to the `geo_shape` polygon that represents the geographic area holding the Voronoi regions). Setting `shapes_from_diff_with_min_area` fixes rare errors where the Voronoi shapes do not fully cover `geo_shape`. Set this to a small number that indicates the minimum valid area of "fill up" Voronoi region shapes. Returns a list of shapely Polygons objects. """ # generate shapely Polygon objects from the LineStrings of the Voronoi shapes in `poly_lines` poly_shapes = [] for p in polygonize(poly_lines): if geo_shape is not None and not geo_shape.contains(p): # if `geo_shape` contains polygon `p`, p = p.intersection(geo_shape) # intersect it with `geo_shape` (i.e. "cut" it) if not p.is_empty: poly_shapes.append(p) if geo_shape is not None and shapes_from_diff_with_min_area is not None: # fix rare cases where the generated polygons of the Voronoi regions don't fully cover `geo_shape` vor_polys_union = cascaded_union(poly_shapes) # union of Voronoi regions diff = np.array(geo_shape.difference(vor_polys_union), dtype=object) # "gaps" diff_areas = np.array([p.area for p in diff]) # areas of "gaps" # use only those "gaps" bigger than `shapes_from_diff_with_min_area` because very tiny areas are generated # at the borders due to floating point errors poly_shapes.extend(diff[diff_areas >= shapes_from_diff_with_min_area]) return poly_shapes
def SplitDomainByFault(self): # Extend fault line to remove the hanging point # hanging point is not intersect with other fault and boundary #https://gis.stackexchange.com/questions/232771/splitting-polygon-by-linestring-in-geodjango #https://gis.stackexchange.com/questions/283352/most-efficient-way-to-split-a-polygon-with-lines-c-api #Extend Fault lines BoundaryLine_Splitted,FaultLine_Extend, NewIntersectPts=self.extendFaultLines() #Find the 2D sub domain results = polygonize(MultiLineString(BoundaryLine_Splitted+FaultLine_Extend)) self.SplitPolygons=Shapely2List_MultiPolygon(results) for i,p in enumerate(self.SplitPolygons):#Remove unnecessary points in polygon self.SplitPolygons[i]=simplify_Polygon(p) for i,poly in enumerate(self.SplitPolygons):#Convert float to int poly=list(reversed(poly)) #Reverse to anti-clock wise order for j,node in enumerate(poly): self.SplitPolygons[i][j]=(int(node[0]),int(node[1])) print('[FaultProcess] Domain is splitted as %d polygons.'%(len(self.SplitPolygons))) #debug #self.plotLines(BoundaryLine_Splitted,FaultLine_Extend,NewIntersectPts) self.plotSplittedDomain()
def isNonAeroDynamic(index, cordpt, globaldata, wallpoints): main_pointx,main_pointy = getPoint(index, globaldata) cordptx = float(cordpt.split(",")[0]) cordpty = float(cordpt.split(",")[1]) line = shapely.geometry.LineString([[main_pointx, main_pointy], [cordptx, cordpty]]) responselist = [] for item in wallpoints: polygonpts = [] for item2 in item: polygonpts.append([float(item2.split(",")[0]), float(item2.split(",")[1])]) polygontocheck = shapely.geometry.Polygon(polygonpts) merged = linemerge([polygontocheck.boundary, line]) borders = unary_union(merged) polygons = polygonize(borders) i = 0 for p in polygons: i = i + 1 if i == 1: responselist.append(False) else: responselist.append(True) if True in responselist: return True else: return False
def alpha_shape(x, y, alpha): coords = np.c_[x, y] tri = scipy.spatial.Delaunay(coords) # edges = [] edge_coords = [] for ia, ib, ic in tri.vertices: ab = math.sqrt((coords[ia,0]-coords[ib,0])**2 + (coords[ia,1]-coords[ib,1])**2) bc = math.sqrt((coords[ib,0]-coords[ic,0])**2 + (coords[ib,1]-coords[ic,1])**2) ac = math.sqrt((coords[ia,0]-coords[ic,0])**2 + (coords[ia,1]-coords[ic,1])**2) semiperim = 0.5*(ab+bc+ac) area = math.sqrt(semiperim*(semiperim-ab)*(semiperim-bc)*(semiperim-ac)) radius = 0.25*ab*bc*ac/area if radius < 1.0/alpha: # edges.append((tuple(coords[ia]), tuple(coords[ib]))) # edges.append((tuple(coords[ib]), tuple(coords[ic]))) # edges.append((tuple(coords[ia]), tuple(coords[ic]))) edge_coords.append(coords[[ia, ib]]) edge_coords.append(coords[[ib, ic]]) edge_coords.append(coords[[ia, ic]]) # edges = set(edges) m = MultiLineString(edge_coords) return cascaded_union(list(polygonize(m))), edge_coords
def create_grids(shape, meters): ''' purpose # create grids within a shape inputs # shape: the shape that forms the exterior of grids # meters: the width and heigh of each grid outputs pd.DataFrame(grids): a dataframe with all the grids ''' x_grids = np.arange(shape.bounds[0] - meters, shape.bounds[2] + meters, meters) y_grids = np.arange(shape.bounds[1] - meters, shape.bounds[3] + meters, meters) x_lines = [((x1, yi), (x2, yi)) for x1, x2 in zip(x_grids[:-1], x_grids[1:]) for yi in y_grids] y_lines = [((xi, y1), (xi, y2)) for y1, y2 in zip(y_grids[:-1], y_grids[1:]) for xi in x_grids] grids = list(polygonize(MultiLineString(x_lines + y_lines))) return (pd.DataFrame.from_dict({'shape': grids}))
def alpha_shape(points, alpha): from shapely.ops import cascaded_union, polygonize from scipy.spatial import Delaunay import numpy as np import math import shapely.geometry as geometry """ Compute the alpha shape (concave hull) of a set of points. @param points: Iterable container of points. @param alpha: alpha value to influence the gooeyness of the border. Smaller numbers don't fall inward as much as larger numbers. Too large, and you lose everything! """ if len(points) < 4: # When you have a triangle, there is no sense # in computing an alpha shape. return geometry.MultiPoint(list(points)).convex_hull def add_edge(edges, edge_points, coords, i, j): """ Add a line between the i-th and j-th points, if not in the list already """ if (i, j) in edges or (j, i) in edges: # already added return edges.add( (i, j) ) edge_points.append(coords[ [i, j] ]) coords = np.array([point.coords[0] for point in points]) tri = Delaunay(coords) edges = set() edge_points = [] # loop over triangles: # ia, ib, ic = indices of corner points of the # triangle for ia, ib, ic in tri.vertices: pa = coords[ia] pb = coords[ib] pc = coords[ic] # Lengths of sides of triangle a = math.sqrt((pa[0]-pb[0])**2 + (pa[1]-pb[1])**2) b = math.sqrt((pb[0]-pc[0])**2 + (pb[1]-pc[1])**2) c = math.sqrt((pc[0]-pa[0])**2 + (pc[1]-pa[1])**2) # Semiperimeter of triangle s = (a + b + c)/2.0 # Area of triangle by Heron's formula area = math.sqrt(s*(s-a)*(s-b)*(s-c)) circum_r = a*b*c/(4.0*area) # Here's the radius filter. #print circum_r if circum_r < 1.0/alpha: add_edge(edges, edge_points, coords, ia, ib) add_edge(edges, edge_points, coords, ib, ic) add_edge(edges, edge_points, coords, ic, ia) m = geometry.MultiLineString(edge_points) triangles = list(polygonize(m)) return cascaded_union(triangles), edge_points
def create_voronoi_gdf(coordinates, input_gdf): """Create a voronoi gdf given a list of coordinates and an input image. The exterior of the floorplan image will be used as an outer limit for the geodataframe. Args: coordinates (list): List of tuples containing points. input_gdf (gpd.GeoDataFrame): original b&w input image gdf. Returns: Geodataframe with voronoi areas, clipped to the input image. """ BUFFER_SIZE = 20 edge_coordinates = create_exterior_points(input_gdf.buffer(BUFFER_SIZE, join_style=2), number_points_along_line=10) vor = Voronoi(coordinates + edge_coordinates) lines = [ LineString(vor.vertices[line]) for line in vor.ridge_vertices if -1 not in line ] all_polygons = list(ops.polygonize(lines)) areas = gpd.GeoDataFrame(geometry=all_polygons) clipped_gdf = gpd.clip(areas, input_gdf, keep_geom_type=True) return clipped_gdf
def alpha_shape(points, alpha): # No alpha shape for a triangle. if len(points) < 4: return geometry.MultiPoint(list(points)).convex_hull coords = np.array([point.coords[0] for point in points]) tri = Delaunay(coords) triangles = coords[tri.vertices] a = ((triangles[:, 0, 0] - triangles[:, 1, 0])**2 + (triangles[:, 0, 1] - triangles[:, 1, 1])**2)**0.5 b = ((triangles[:, 1, 0] - triangles[:, 2, 0])**2 + (triangles[:, 1, 1] - triangles[:, 2, 1])**2)**0.5 c = ((triangles[:, 2, 0] - triangles[:, 0, 0])**2 + (triangles[:, 2, 1] - triangles[:, 0, 1])**2)**0.5 s = (a + b + c) / 2.0 areas = (s * (s - a) * (s - b) * (s - c))**0.5 circums = a * b * c / (4.0 * areas) filtered = triangles[circums < (1.0 / alpha)] edge1 = filtered[:, (0, 1)] edge2 = filtered[:, (1, 2)] edge3 = filtered[:, (2, 0)] edge_points = np.unique(np.concatenate((edge1, edge2, edge3)), axis=0).tolist() m = geometry.MultiLineString(edge_points) triangles = list(polygonize(m)) return cascaded_union(triangles), edge_points
def save(datasource, filename): """ Save a Datasource instance to a named OGR datasource. """ ext = splitext(filename)[1] out_driver = ogr.GetDriverByName(drivers.get(ext)) out_source = out_driver.CreateDataSource(filename) if out_source is None: raise Exception('Failed creation of %s - is there one already?' % filename) out_layer = out_source.CreateLayer('default', datasource.srs, ogr.wkbMultiPolygon) for field in datasource.fields: field_defn = ogr.FieldDefn(field.name, field.type) field_defn.SetWidth(field.width) out_layer.CreateField(field_defn) for i in datasource._indexes(): segments = datasource.db.execute( """SELECT x1, y1, x2, y2 FROM segments WHERE (src1_id = ? OR src2_id = ?) AND removed = 0""", (i, i)) lines = [ datasource.memo_line(x1, y1, x2, y2) for (x1, y1, x2, y2) in segments ] try: poly = polygonize(lines).next() except StopIteration: lost_area = datasource.shapes[i].area lost_portion = lost_area / (datasource.tolerance**2) if lost_portion < 4: # It's just small. print >> stderr, 'Skipped small feature #%(i)d' % locals() continue # This is a bug we don't understand yet. raise Exception( 'Failed to get a meaningful polygon out of large feature #%(i)d' % locals()) feat = ogr.Feature(out_layer.GetLayerDefn()) for (j, field) in enumerate(datasource.fields): feat.SetField(field.name, datasource.values[i][j]) geom = ogr.CreateGeometryFromWkb(dumps(poly)) feat.SetGeometry(geom) out_layer.CreateFeature(feat)
def segment(self, intersect=True): """ Segments the input detection object with the Voronoi segmentation algorithm. :param intersect: If true, intersects with the bounding box of the height model. """ from scipy.spatial import Voronoi from shapely.geometry import LineString from shapely.ops import polygonize vor = Voronoi(self._centered_coords) lines = [ LineString(vor.vertices[line]) for line in vor.ridge_vertices if -1 not in line ] poly_generator = polygonize(lines) series = gpd.GeoSeries(poly_generator) series = self.translate(series) series.crs = self.detection_base.height_model.crs if intersect: series = series.intersection( self.detection_base.height_model._bounding_box_poly) return series
def side_based_procedure(terrain, image): line = terrain.center_line.coords con = terrain.poly.boundary.coords mer = linemerge([con, line]) uni = unary_union(mer) left_side, right_side = polygonize(uni) draw = ImageDraw.Draw(image) # draw.polygon(convert_to_map(v[0].boundary.coords), fill="#ff0000") # draw.polygon(convert_to_map(v[1].boundary.coords), fill="#00ff00") center_l = helpers.points_located_on_center_line( terrain.center_line, max(1, 1 / TIME_CONSUMPTION)) left_points = helpers.uniformly_distribute_points(8 / TIME_CONSUMPTION, terrain.poly) left_points += random_points_in_polygon(int(3 * TIME_CONSUMPTION), left_side) right_points = helpers.uniformly_distribute_points(8 / TIME_CONSUMPTION, terrain.poly) right_points += random_points_in_polygon(int(3 * TIME_CONSUMPTION), right_side) image = put_rotated_sprites_onto_image(image, False, left_points, "mountains_side", left_side, center_l) image = put_rotated_sprites_onto_image(image, True, right_points, "mountains_side", right_side, center_l) if center_l: image = put_rotated_sprites_onto_image(image, False, center_l, "mountains", terrain.poly, center_l) return image
def _floor2obsts(self,tin,floor):# {{{ ''' For a roomX we create a roomX_ghost, we move it by self.walls_width, which must match the width of hvents. Then we create walls via logical operations. Finally doors cut the openings in walls. ''' if self.fire_model=='FDS': return [] walls=[] for i in self.s.query("SELECT * FROM {} WHERE floor=? AND type_pri='COMPA' ORDER BY name".format(tin), (floor,)): walls.append((i['x0']+self.walls_width , i['y0'] , i['x0']+i['width'] , i['y0']+self.walls_width) ) walls.append((i['x0']+i['width'] , i['y0'] , i['x0']+i['width']+self.walls_width , i['y0']+i['depth']+self.walls_width) ) walls.append((i['x0']+self.walls_width , i['y0']+i['depth'] , i['x0']+i['width'] , i['y0']+i['depth']+self.walls_width) ) walls.append((i['x0'] , i['y0'] , i['x0']+self.walls_width , i['y0']+i['depth']+self.walls_width) ) walls_polygons=([box(ii[0],ii[1],ii[2],ii[3]) for ii in set(walls)]) doors_polygons=[] for i in self.s.query("SELECT * FROM {} WHERE floor=? AND type_tri='DOOR' ORDER BY name".format(tin), (floor,)): doors_polygons.append(box(i['x0'], i['y0'], i['x0']+i['width'], i['y0']+i['depth'])) obsts=[] for wall in walls_polygons: for door in doors_polygons: wall=wall.difference(door) if isinstance(wall, MultiPolygon): for i in polygonize(wall): obsts.append(i) elif isinstance(wall, Polygon): obsts.append(wall) return obsts
def voronoi(points, save=False): """ 生成德劳内三角网和泰森多边形 :param points: 要生成德劳内三角网和泰森多边形的点 :param save: 默认不保存本地文件 :return: """ pointlength = len(points) array = np.array(points) # 泰森多边形 vor = Voronoi(array, furthest_site=False, incremental=True, qhull_options=None) if save: # 泰森多边形的顶点 vertices = vor.vertices with open('voronoi_vertices.txt', 'w', encoding='utf-8') as f: for index, v in enumerate(vertices): f.write( str(index) + '\t' + 'POINT(' + str(v[0]) + ' ' + str(v[1]) + ')' + '\n') # 泰森多边形的面,-1代表无穷索引 regions = vor.regions with open('voronoi_regions.txt', 'w', encoding='utf-8') as f: for index, r in enumerate(regions): if len(r) == 0: continue if -1 in r: continue angulars = [] for id in r: angulars.append(vertices[id]) angulars.append(vertices[r[0]]) polygon = Polygon(angulars) f.write(str(index) + '\t' + str(polygon.wkt) + '\n') # 德劳内三角形的边,用原始的点数量 vorOriginal = Voronoi(array[0:pointlength], furthest_site=False, incremental=True, qhull_options=None) ridge_points = vorOriginal.ridge_points polylines = [] for ridge in ridge_points: polyline = LineString([points[ridge[0]], points[ridge[1]]]) polylines.append(polyline) # 德劳内三角形构面 delaunays = polygonize(polylines) with open(r'voronoi_delaunays.txt', 'w', encoding='utf-8') as f: for index, p in enumerate(delaunays): f.write(str(index) + '\t' + str(p.wkt) + '\n') fig = voronoi_plot_2d(vor) plt.gca().xaxis.set_major_locator(plt.NullLocator()) plt.gca().yaxis.set_major_locator(plt.NullLocator()) plt.subplots_adjust(top=1, bottom=0, right=1, left=0, hspace=0, wspace=0)
def convert_linestring_to_polygon(geom): """Convert (Multi)LineString to (Multi)Polygon Use shapely.ops.polygonize to transform (Multi)LineString into Multi(Polygon). The function merges all (Multi)Polygons into one geometry. Args: geom: Multi(LineString) geometry Returns: merged_geom: Multi(Polygon) geometry """ # Check that geometry is (Multi)LineString assert isinstance(geom, (LineString, MultiLineString)),\ "geometry is not (Multi)LineString" # Use shapely.ops.polygonize to transform # (Multi)LineString into (Multi)Polygon(s) # Note that the output is a generator yielding # (Multi)Polygon geometries polygonized_geom = polygonize(geom) # Merge the Multi(Polygon) geometry(ies) merged_geom = unary_union(list(polygonized_geom)) return merged_geom
def alpha_shape(points, alpha): """ Compute the alpha shape (concave hull) of a set of points. @param points: Iterable container of points. @param alpha: alpha value to influence the gooeyness of the border. Smaller numbers don't fall inward as much as larger numbers. Too large, and you lose everything! """ if len(points) < 4: # When you have a triangle, there is no sense # in computing an alpha shape. return geometry.MultiPoint(list(points)).convex_hull coords = np.array([point.coords[0] for point in points]) tri = Delaunay(coords) triangles = coords[tri.vertices] a = ((triangles[:,0,0] - triangles[:,1,0]) ** 2 + (triangles[:,0,1] - triangles[:,1,1]) ** 2) ** 0.5 b = ((triangles[:,1,0] - triangles[:,2,0]) ** 2 + (triangles[:,1,1] - triangles[:,2,1]) ** 2) ** 0.5 c = ((triangles[:,2,0] - triangles[:,0,0]) ** 2 + (triangles[:,2,1] - triangles[:,0,1]) ** 2) ** 0.5 s = ( a + b + c ) / 2.0 areas = (s*(s-a)*(s-b)*(s-c)) ** 0.5 circums = a * b * c / (4.0 * areas) filtered = triangles[circums < (1.0 / alpha)] edge1 = filtered[:,(0,1)] edge2 = filtered[:,(1,2)] edge3 = filtered[:,(2,0)] edge_points = np.unique(np.concatenate((edge1,edge2,edge3)), axis = 0).tolist() m = geometry.MultiLineString(edge_points) triangles = list(polygonize(m)) return cascaded_union(triangles), edge_points
def calculate_iou(predicted, gt): if not checkPolygon(predicted, gt): assert 1 == 0, 'gt or predicted cannot form polygon' return 0 gt_x = [i[0] for i in gt] gt_y = [i[1] for i in gt] predicted = Polygon(predicted) gt = Polygon(gt) assert predicted.is_valid, 'prediction is not valid' + str(predicted) if gt.is_valid: iou = predicted.intersection(gt).area / predicted.union(gt).area else: # original data ls = LineString(np.c_[gt_x, gt_y]) # closed, non-simple lr = LineString(ls.coords[:] + ls.coords[0:1]) assert lr.is_simple is False, 'lr is simple' mls = unary_union(lr) #mls.geom_type # MultiLineString' gt = MultiPolygon(list(polygonize(mls))) intersection = 0 union = predicted for gt_polygons in gt: intersection += predicted.intersection(gt_polygons).area union = union.union(gt_polygons) iou = intersection / union.area return iou
def polygon_from_points(points): num_points = len(points) if num_points == 0: return Point() if num_points == 1: return Point(points[0]) if num_points == 2: return LineString(points) polygon = Polygon(points) if polygon.is_valid: return polygon raw_edges = polygon_edges(polygon) split_edges = split_at_intersections(raw_edges) deduplicated_edges = remove_duplicates(split_edges) polygons = list(polygonize(deduplicated_edges)) if len(polygons) > 0: return largest_polygon(polygons) return Polygon()
def get_boundingpolygon(self): """ TODO: Implement ncell bbox order of lines creation (assumes 0,0 is x) -----3----- | | 4 2 | | x----1----- """ if self._ndim == 2: # CGRID nx,ny = self._xarray.shape one = MultiLineString([((self._xarray[i][0],self._yarray[i][0]),(self._xarray[i+1][0],self._yarray[i+1][0])) for i in range(nx-1)]) two = MultiLineString([((self._xarray[nx-1][j],self._yarray[nx-1][j]),(self._xarray[nx-1][j+1],self._yarray[nx-1][j+1])) for j in range(ny-1)]) three = MultiLineString([((self._xarray[i][ny-1],self._yarray[i][ny-1]),(self._xarray[i-1][ny-1],self._yarray[i-1][ny-1])) for i in reversed(list(range(1,nx)))]) four = MultiLineString([((self._xarray[0][j],self._yarray[0][j]),(self._xarray[0][j-1],self._yarray[0][j-1])) for j in reversed(list(range(1,ny)))]) m = one.union(two).union(three).union(four) else: # RGRID nx,ny = self._xarray.shape[0], self._yarray.shape[0] one = LineString([(self._xarray[i], self._yarray[0]) for i in range(nx)]) two = LineString([(self._xarray[-1], self._yarray[i]) for i in range(ny)]) three = LineString([(self._xarray[i], self._yarray[-1]) for i in reversed(list(range(nx)))]) four = LineString([(self._xarray[0], self._yarray[i]) for i in reversed(list(range(ny)))]) m = MultiLineString([one,two,three,four]) polygons = list(polygonize(m)) # -- polygonize returns a list of polygons, including interior features, the largest in area "should" be the full feature assert len(polygons) > 0, "Could not determine a polygon" polygon = sorted(polygons, key=lambda x: x.area)[-1] return polygon
def merge_vor(vor, com_points, lim): points_coord = [] #region_indices = [] lines = [] for p in com_points: if(int(p) < lim): points_coord.append(vor.points[int(p)]) #region_indices.append(vor.point_region[int(p)]) #r = vor.point_region[int(p)] region = vor.regions[vor.point_region[int(p)]] for i in range(0, len(region)-1): for j in range(1, len(region)): v1 = region[i] v2 = region[j] if(v1 == -1 or v2 == -1): continue try: line = geometry.LineString(vor.vertices[[v1, v2]]) except: try: line = geometry.LineString(vor.vertices[[v2, v1]]) except: continue lines.append(line) print(len(lines)) shape = ops.unary_union(list(ops.polygonize(lines))) return points_coord, shape
def procesaLineaInterna(featuresExternas, featuresInternas, featuresCentroide, featureDefn): #print "Procedemos a procesar las lineas internas" centroides = [] for centroide in featuresCentroide: #obtenemos la altura y el rotulo del estilo de cada centroide for n in centroide.GetStyleString().split(','): if n.startswith('s'): altura = float(n.replace('s:', '').replace('g', '')) elif n.startswith('t'): rotulo = n.split('"')[1] punto = centroide.GetGeometryRef() x = punto.GetX() y = punto.GetY() longitudRotulo = len(rotulo) factor = 0.15 * (altura * 3.3333) desfaseX = longitudRotulo * factor - 0.05 punto.SetPoint(point = 0, x = x + desfaseX, y = y - 0.20) centroides.append((rotulo, punto)) featuresProceso = featuresExternas + featuresInternas outFeature = [] if len(featuresProceso) > 1: geometry_out = None for inFeature in featuresProceso: geometry_in = inFeature.GetGeometryRef() if geometry_out is None: geometry_out = geometry_in geometry_out = ogr.ForceToMultiLineString(geometry_out) else: geometry_out = geometry_out.Union(geometry_in) lineasInternasShapely = loads(geometry_out.ExportToWkt()) polygonsShapely = polygonize(lineasInternasShapely) polygonGeom = [] for polygon in polygonsShapely: polygonGeom.append(ogr.CreateGeometryFromWkt(dumps(polygon))) for pol in polygonGeom: for cen in centroides: if pol.Contains(cen[1]): feature = ogr.Feature(featureDefn) feature.SetGeometry(pol) feature.SetField('rotulo', cen[0]) outFeature.append(feature.Clone()) feature.Destroy() else: feature = ogr.Feature(featureDefn) geometryPoly = ogr.BuildPolygonFromEdges(ogr.ForceToMultiLineString(featuresProceso[0].GetGeometryRef()), dfTolerance = 0) feature.SetGeometry(geometryPoly) feature.SetField('rotulo', centroides[0][0]) outFeature.append(feature.Clone()) feature.Destroy() return outFeature
def _cont_to_polys(self, cont_lats, cont_lons): polys = [] start_idx = 0 splits = [] while True: try: split_idx = cont_lats[start_idx:].index(99.99) splits.append(start_idx + split_idx) start_idx += split_idx + 1 except ValueError: break splits = [ -1 ] + splits + [ len(cont_lats) + 1 ] poly_lats = [ cont_lats[splits[i] + 1:splits[i + 1]] for i in xrange(len(splits) - 1) ] poly_lons = [ cont_lons[splits[i] + 1:splits[i + 1]] for i in xrange(len(splits) - 1) ] # Intersect with the US boundary shape file. for plat, plon in zip(poly_lats, poly_lons): cont = LineString(zip(plon, plat)) if plat[0] != plat[-1] or plon[0] != plon[-1]: # If the line is not a closed contour, then it intersects with the edge of the US. Extend # the ends a little bit to make sure it's outside the edge. dln = np.diff(plon) dlt = np.diff(plat) pre = [ (plon[0] - 0.5 * dln[0], plat[0] - 0.5 * dlt[0]) ] post = [ (plon[-1] + 0.5 * dln[-1], plat[-1] + 0.5 * dlt[-1]) ] cont.coords = pre + list(cont.coords) + post # polygonize() will split the country into two parts: one inside the outlook and one outside. # Construct test_ln that is to the right of (inside) the contour and keep only the polygon # that contains the line test_ln = cont.parallel_offset(0.05, 'right') for poly in polygonize(self._conus.boundary.union(cont)): if (poly.crosses(test_ln) or poly.contains(test_ln)) and self._conus.contains(poly.buffer(-0.01)): polys.append(poly) # Sort the polygons by area so we intersect the big ones with the big ones first. polys.sort(key=lambda p: p.area, reverse=True) # If any polygons intersect, replace them with their intersection. intsct_polys = [] while len(polys) > 0: intsct_poly = polys.pop(0) pops = [] for idx, poly in enumerate(polys): if intsct_poly.intersects(poly): intsct_poly = intsct_poly.intersection(poly) pops.append(idx) for pop_idx in pops[::-1]: polys.pop(pop_idx) intsct_polys.append(intsct_poly) return intsct_polys
def get_convex_hull(points): """ Find the convex hull of points, return as Shapely polygon """ log.info("Finding convex hull of %i points", len(points)) hull = ConvexHull(points) log.info("Found convex hull with %i facets", len(hull.simplices)) hull_edges = [] for simplex in hull.simplices: hull_edges.append( zip(points[simplex, 0], points[simplex, 1])) polygon = list(polygonize(hull_edges)) assert(len(polygon) == 1) return polygon[0]
def get_shapes(filelike): shapes = [] node_cache = {} way_cache = {} for thing in iter_osm_file(filelike): if type(thing) == Node: pt = (thing.lon, thing.lat) shape = Point(pt) node_cache[thing.id] = pt if thing.tags: shapes.append((thing, shape)) elif type(thing) == Way: points = [] for nd in thing.nds: node_loc = node_cache.get(nd) if node_loc: points.append(node_loc) else: raise Exception("Way %s references node %s which is not parsed yet." % (thing.id, nd)) if way_is_polygon(thing): shape = Polygon(points) else: shape = LineString(points) way_cache[thing.id] = points if any(thing.tags): # Only include tagged things at this point. Otherwise, # the shapes that are part of multipolygon relations # will be included twice. shapes.append((thing, shape)) elif type(thing) == Relation: if any([t.key == 'type' and t.value == 'multipolygon' for t in thing.tags]): parts = [] for member in thing.members: if member.type == 'way': shape = way_cache.get(member.ref) if not shape: raise Exception("Relation %s references way %s which is not parsed yet." % (thing.id, member.ref)) parts.append(shape) # Polygonize will return all the polygons created, so the # inner parts of the multipolygons will be returned twice # we only want the first one shapes.append((thing, next(polygonize(parts)))) return shapes
def test_polygonize(self): lines = [ LineString(((0, 0), (1, 1))), LineString(((0, 0), (0, 1))), LineString(((0, 1), (1, 1))), LineString(((1, 1), (1, 0))), LineString(((1, 0), (0, 0))), LineString(((5, 5), (6, 6))), Point(0, 0), ] result = list(polygonize(lines)) self.assertTrue(all([isinstance(x, Polygon) for x in result]))
def save(datasource, filename): """ Save a Datasource instance to a named OGR datasource. """ ext = splitext(filename)[1] out_driver = ogr.GetDriverByName(drivers.get(ext)) out_source = out_driver.CreateDataSource(filename) if out_source is None: raise Exception('Failed creation of %s - is there one already?' % filename) out_layer = out_source.CreateLayer('default', datasource.srs, ogr.wkbMultiPolygon) for field in datasource.fields: field_defn = ogr.FieldDefn(field.name, field.type) field_defn.SetWidth(field.width) out_layer.CreateField(field_defn) for i in datasource._indexes(): segments = datasource.db.execute("""SELECT x1, y1, x2, y2 FROM segments WHERE (src1_id = ? OR src2_id = ?) AND removed = 0""", (i, i)) lines = [datasource.memo_line(x1, y1, x2, y2) for (x1, y1, x2, y2) in segments] try: poly = polygonize(lines).next() except StopIteration: lost_area = datasource.shapes[i].area lost_portion = lost_area / (datasource.tolerance ** 2) if lost_portion < 4: # It's just small. print >> stderr, 'Skipped small feature #%(i)d' % locals() continue # This is a bug we don't understand yet. raise Exception('Failed to get a meaningful polygon out of large feature #%(i)d' % locals()) feat = ogr.Feature(out_layer.GetLayerDefn()) for (j, field) in enumerate(datasource.fields): feat.SetField(field.name, datasource.values[i][j]) geom = ogr.CreateGeometryFromWkb(dumps(poly)) feat.SetGeometry(geom) out_layer.CreateFeature(feat)
def intersect(a, b): ap, bp = [orient_offset_polygon(p) for p in [a,b]] overlap = ap.intersection(bp) if overlap.is_empty: return [] if not hasattr(overlap, 'exterior'): return [Poly(p.exterior.coords, p.centroid.coords[0], 0) for p in polygonize(overlap)] return [Poly(overlap.exterior.coords, overlap.centroid.coords[0], 0)]
def processAlgorithm(self, progress): try: from shapely.ops import polygonize from shapely.geometry import Point, MultiLineString except ImportError: raise GeoAlgorithmExecutionException( 'Polygonize algorithm requires shapely module!') vlayer = dataobjects.getObjectFromUri( self.getParameterValue(self.INPUT)) output = self.getOutputFromName(self.OUTPUT) vprovider = vlayer.dataProvider() if self.getParameterValue(self.FIELDS): fields = vprovider.fields() else: fields = QgsFields() if self.getParameterValue(self.GEOMETRY): fieldsCount = fields.count() fields.append(QgsField('area', QVariant.Double, 'double', 16, 2)) fields.append(QgsField('perimeter', QVariant.Double, 'double', 16, 2)) allLinesList = [] features = vector.features(vlayer) current = 0 total = 40.0 / float(len(features)) for inFeat in features: inGeom = inFeat.geometry() if inGeom.isMultipart(): allLinesList.extend(inGeom.asMultiPolyline()) else: allLinesList.append(inGeom.asPolyline()) current += 1 progress.setPercentage(int(current * total)) progress.setPercentage(40) allLines = MultiLineString(allLinesList) allLines = allLines.union(Point(0, 0)) progress.setPercentage(45) polygons = list(polygonize([allLines])) progress.setPercentage(50) writer = output.getVectorWriter(fields, QGis.WKBPolygon, vlayer.crs()) outFeat = QgsFeature() current = 0 total = 50.0 / float(len(polygons)) for polygon in polygons: outFeat.setGeometry(QgsGeometry.fromWkt(polygon.wkt)) if self.getParameterValue(self.GEOMETRY): outFeat.setAttributes([None] * fieldsCount + [polygon.area, polygon.length]) writer.addFeature(outFeat) current += 1 progress.setPercentage(50 + int(current * total)) del writer
def processAlgorithm(self, progress): vlayer = dataobjects.getObjectFromUri(self.getParameterValue(self.INPUT)) output = self.getOutputFromName(self.OUTPUT) vprovider = vlayer.dataProvider() if self.getParameterValue(self.FIELDS): fields = vprovider.fields() else: fields = QgsFields() if self.getParameterValue(self.GEOMETRY): fieldsCount = fields.count() fields.append(QgsField('area', QVariant.Double, 'double', 16, 2)) fields.append(QgsField('perimeter', QVariant.Double, 'double', 16, 2)) allLinesList = [] features = vector.features(vlayer) progress.setInfo(self.tr('Processing lines...')) total = 40.0 / len(features) for current, inFeat in enumerate(features): inGeom = inFeat.geometry() if inGeom.isMultipart(): allLinesList.extend(inGeom.asMultiPolyline()) else: allLinesList.append(inGeom.asPolyline()) progress.setPercentage(int(current * total)) progress.setPercentage(40) allLines = MultiLineString(allLinesList) progress.setInfo(self.tr('Noding lines...')) allLines = unary_union(allLines) progress.setPercentage(45) progress.setInfo(self.tr('Polygonizing...')) polygons = list(polygonize([allLines])) if not polygons: raise GeoAlgorithmExecutionException(self.tr('No polygons were created!')) progress.setPercentage(50) progress.setInfo('Saving polygons...') writer = output.getVectorWriter(fields, QgsWkbTypes.Polygon, vlayer.crs()) outFeat = QgsFeature() total = 50.0 / len(polygons) for current, polygon in enumerate(polygons): outFeat.setGeometry(QgsGeometry.fromWkt(polygon.wkt)) if self.getParameterValue(self.GEOMETRY): outFeat.setAttributes([None] * fieldsCount + [polygon.area, polygon.length]) writer.addFeature(outFeat) progress.setPercentage(50 + int(current * total)) del writer
def create_union(in_ply1, in_ply2): """ Create union polygon :param in_ply1: first input polygon :param in_ply2: second input polygon :return: shapely polgon """ # union the polygon outer linestrings together outer_bndry = in_ply1.boundary.union(in_ply2.boundary) # rebuild linestrings into polygons output_poly = polygonize(outer_bndry) # pprint(list(polygonize(outer_bndry))) return output_poly
def _cont_to_polys(self, cont_lats, cont_lons, cont_val): """ Take the lat/lon contours, split them into their different segments, and create polygons out of them. Contours that stretch from border to border will end up covering large sections of the country. That's okay; we'll take care of that later. """ polys = {} start_idx = 0 splits = [] while True: try: split_idx = cont_lats[start_idx:].index(99.99) splits.append(start_idx + split_idx) start_idx += split_idx + 1 except ValueError: break splits = [ -1 ] + splits + [ len(cont_lats) + 1 ] poly_lats = [ cont_lats[splits[i] + 1:splits[i + 1]] for i in xrange(len(splits) - 1) ] poly_lons = [ cont_lons[splits[i] + 1:splits[i + 1]] for i in xrange(len(splits) - 1) ] # Intersect with the US boundary shape file. for plat, plon in zip(poly_lats, poly_lons): cont = LineString(zip(plon, plat)) if plat[0] != plat[-1] or plon[0] != plon[-1]: # If the line is not a closed contour, then it intersects with the edge of the US. Extend # the ends a little bit to make sure it's outside the edge. dln = np.diff(plon) dlt = np.diff(plat) pre = [ (plon[0] - 0.03 * dln[0], plat[0] - 0.03 * dlt[0]) ] post = [ (plon[-1] + 0.03 * dln[-1], plat[-1] + 0.03 * dlt[-1]) ] cont.coords = pre + list(cont.coords) + post # polygonize() will split the country into two parts: one inside the outlook and one outside. # Construct test_ln that is to the right of (inside) the contour and keep only the polygon # that contains the line test_ln = cont.parallel_offset(0.05, 'right') polys[cont] = [] for poly in polygonize(self._conus.boundary.union(cont)): if (poly.crosses(test_ln) or poly.contains(test_ln)) and self._conus.contains(poly.buffer(-0.01)): polys[cont].append(poly) return polys
def union(self): '''new method using Union function''' global polyCount inFeat = QgsFeature() progress = 0. self.emit(SIGNAL('progress'), 0) self.parent.layer = getMapLayerByName(self.ui.cmbLayer.currentText()) provider = self.parent.layer.dataProvider() #user can't toggle edit mode of line layer while polygonizing, plugin automatically turn it off QObject.connect(self.parent.layer,SIGNAL("editingStarted()"), self.parent.startEditing) allAttrs = provider.attributeIndexes() provider.select(allAttrs) provider.select() step = 45. / self.parent.layer.featureCount() allLinesList = [] allLinesListExtend = allLinesList.extend allLinesListAppend = allLinesList.append while provider.nextFeature(inFeat): geom = inFeat.geometry() if geom.isMultipart(): allLinesListExtend(geom.asMultiPolyline() ) else: allLinesListAppend(geom.asPolyline()) progress += step self.emit(SIGNAL('progress'), progress) allLines = MultiLineString(allLinesList) allLines = allLines.union(Point(0,0)) polygons = list(polygonize([allLines])) polyCount = len(polygons) #if no polygons where created then exit from thread if polyCount == 0: QObject.disconnect(self.parent.polygonizeThread,SIGNAL("finished()"), self.parent.threadFinished) self.emit(SIGNAL('noPolygons')) return else: if self.ui.cbOutput.isChecked(): self.saveAsFile(polygons, progress) else: self.saveInMemory(polygons, progress)
def _geometric_process(self): if len(self._entries) != 1: merged_union = linemerge(unary_union(self._entries)) else: merged_union = MultiLineString(self._entries) self._edges = [ Edge(tuple(g.coords)) for g in merged_union.geoms ] edges = map(lambda e: e._geom, self._edges) if self._bnode: xyset = set([edge.coords[i] for edge in edges for i in (0,-1)]) self._nodes = map(lambda xy: Node(xy),xyset) if self._bface: self._faces = [Face(poly.exterior,poly.interiors) for poly in polygonize(edges)]
def _topological_process(self): # initialisation des arcs (géométrie calculée, autres attributs à None) init_None = dict.fromkeys(('start_node','end_node','left_face','right_face'),None) if len(self._entries) != 1: merged_union = linemerge(unary_union(self._entries)) else: merged_union = MultiLineString(self._entries) self._edges = [ Edge(tuple(g.coords),**init_None) for g in merged_union.geoms ] del merged_union # liste des géométries Shapely des Edge (arcs du graphe) et index spatial # (facilité d'écriture par la suite + fait une seule fois) edges = map(lambda e: e._geom,self._edges) edge_si = geometry_spatial_index(edges) # construction des noeuds, mise à jour des noeuds départ et fin des arcs self._nodes, already_done = list(), dict() for edge in self._edges: for i,attname in ((0,'_start_node'),(-1,'_end_node')): xy = edge._geom.coords[i] inode = already_done.get(xy,None) if inode is None: already_done[xy] = inode = len(self._nodes) self._nodes.append(Node(xy)) setattr(edge,attname,inode) del already_done # création des faces du graphe planaire self._faces = list() for polygon in polygonize(edges): new_face = Face(polygon.exterior,polygon.interiors,extring=None,intrings=list()) self._faces.append(new_face) # liste des géométries Shapely des Face (faces du graphe et index spatial) # (facilité d'écriture par la suite + fait une seule fois) faces = map(lambda pgf: pgf._geom,self._faces) face_si = geometry_spatial_index(faces) self._process_rings(edges,faces,edge_si,face_si)
def polygonizeFeatures(features, fields=None): lineList = [] for inFeat in features: inGeom = inFeat.geometry() if inGeom is None: pass elif inGeom.isMultipart(): lineList.extend(inGeom.asMultiPolyline()) else: lineList.append(inGeom.asPolyline()) allLines = MultiLineString(lineList) allLines = unary_union(allLines) polygons = list(polygonize([allLines])) outList = [] for polygon in polygons: outFeat = QgsFeature(fields) outFeat.setGeometry(QgsGeometry.fromWkt(polygon.wkt)) outList.append(outFeat) return outList
def clipped_polygon(azimuth,polygon,line1,line2): merged_line = linemerge([line1, line2]) merged = linemerge([polygon.boundary, merged_line]) borders = unary_union(merged) polygons = polygonize(borders) polygon_list = list(polygons) if len(polygon_list)>1: polygon1 = polygon_list[0] polygon2 = polygon_list[1] polygon1_area = polygon_area_calculator(polygon=polygon1) polygon2_area=polygon_area_calculator(polygon=polygon2) polygon_areas = (polygon1_area, polygon2_area) polygon_dict = {polygon1_area:polygon1, polygon2_area:polygon2} if azimuth >= 180: result = polygon_dict[max(polygon_areas)] elif azimuth == 0 or azimuth == 360: result = polygon_dict[max(polygon_areas)] else: result = polygon_dict[min(polygon_areas)] return result else: return polygon
def fix_invalid_polygon(p): if not p.is_valid: p_buffer_0 = p.buffer(0) # If the polygon cross itself the .buffer(0) operation # will return only half of the polygon (the other half # may be considered negative) # This is not what we want, so we check resulting area # to detect this didn't happen: if p_buffer_0.area >= p.area*0.99: p = p_buffer_0 else: # Compute the list of all segments of the polygon, add missing points # at the position they cross each others, and use # polygonize() tool function to rebuild a MultiPolygon assert(len(p.interiors) == 0) coords = p.exterior.coords segments = [ LineString([coords[i], coords[i+1]]) for i in xrange(len(coords)-1)] crosses = [set() for i in xrange(len(segments))] for i in xrange(len(segments)): for j in xrange(i): if segments[i].crosses(segments[j]): intersection = segments[i].intersection(segments[j]) assert(type(intersection)== ShapelyPoint) intersection = intersection.coords[0] crosses[i].add(intersection) crosses[j].add(intersection) result_segments = [] for i in xrange(len(segments)): if crosses[i]: points = list(crosses[i]) points.sort(key = lambda c : ShapelyPoint(segments[i].coords[0]).distance(ShapelyPoint(c))) points = [segments[i].coords[0]] + points + [segments[i].coords[1]] for j in xrange(len(points) - 1): result_segments.append((points[j], points[j+1])) else: result_segments.append((segments[i].coords[0], segments[i].coords[1])) p = MultiPolygon(list(polygonize(result_segments))) return p
def tesselation(regionlocs): """Function to create a tesselation from the regionlocs. Parameters ---------- regionlocs: array_like the spatial locations that define each region considered. Returns ------- polygons: shapely.Polygon the polygon object which contains the information to define it as a polygon. """ vor = Voronoi(regionlocs) lines = [] for line in vor.ridge_vertices: if -1 not in line: lines.append(shapely.geometry.LineString(vor.vertices[line])) pols = ops.polygonize(lines) polygons = [poly for poly in pols] return polygons
def Polygonize(self): if self.ui.cmbLayer.currentText() == "": QMessageBox.critical(self, "Polygonizer", "Select line layer first!") elif getMapLayerByName(self.ui.cmbLayer.currentText()).dataProvider().featureCount() == 0: QMessageBox.critical(self, "Polygonizer", "Selected layer has no lines!") elif self.ui.eOutput.text() == "": QMessageBox.critical(self, "Polygonizer", "Choose output file!") else: sys.setcheckinterval(10000) setValue = self.ui.pbProgress.setValue SetWidgetsEnabled(self.ui, False) setValue(0) self.t1 = time() if self.ui.rbNew.isChecked(): inFeat = QgsFeature() outFeat = QgsFeature() layer = QgsVectorLayer() layer = getMapLayerByName(self.ui.cmbLayer.currentText()) progress = 0.0 provider = layer.dataProvider() allAttrs = provider.attributeIndexes() provider.select(allAttrs) fields = provider.fields() provider.select() step = 30.0 / layer.featureCount() allLinesList = [] allLinesListExtend = allLinesList.extend allLinesListAppend = allLinesList.append while provider.nextFeature(inFeat): geom = inFeat.geometry() if geom.isMultipart(): allLinesListExtend(geom.asMultiPolyline()) else: allLinesListAppend(geom.asPolyline()) progress += step setValue(progress) allLines = MultiLineString(allLinesList) allLines = allLines.union(Point(0, 0)) polygons = list(polygonize([allLines])) self.polyCount = len(polygons) if self.polyCount == 0: QMessageBox.critical(self, "Polygonizer", "Sorry, I don't see any polygon!") SetWidgetsEnabled(self.ui, True) setValue(0) return else: step = 65.0 / self.polyCount # addFeature = writer.addFeature setGeometry = outFeat.setGeometry # ok setAttributeMap = outFeat.setAttributeMap # ok if self.ui.cbGeometry.isChecked(): fields[len(fields)] = QgsField("area", QVariant.Double, "double", 16, 2) fields[len(fields)] = QgsField("perimeter", QVariant.Double, "double", 16, 2) nrArea = len(fields) - 2 nrPerimeter = len(fields) - 1 writer = QgsVectorFileWriter( self.ui.eOutput.text(), provider.encoding(), fields, QGis.WKBPolygon, layer.srs() ) for polygon in polygons: # QInputDialog.getText( self, "m", "e", QLineEdit.Normal, str(fields) ) setGeometry(QgsGeometry.fromWkt(polygon.wkt)) setAttributeMap({nrArea: polygon.area, nrPerimeter: polygon.length}) writer.addFeature(outFeat) progress += step setValue(progress) setValue(100) del writer else: writer = QgsVectorFileWriter( self.ui.eOutput.text(), provider.encoding(), fields, QGis.WKBPolygon, layer.srs() ) for polygon in polygons: setGeometry(QgsGeometry.fromWkt(polygon.wkt)) writer.addFeature(outFeat) progress += step setValue(progress) setValue(100) del writer else: inFeat = QgsFeature() inFeatB = QgsFeature() outFeat = QgsFeature() layer = QgsVectorLayer() layer = self.getMapLayerByName(self.ui.cmbLayer.currentText()) progress = 0.0 provider = layer.dataProvider() allAttrs = provider.attributeIndexes() provider.select(allAttrs) fields = provider.fields() new_path = self.ui.eOutput.text() if new_path.contains("\\"): out_name = new_path.right((new_path.length() - new_path.lastIndexOf("\\")) - 1) else: out_name = new_path.right((new_path.length() - new_path.lastIndexOf("/")) - 1) if out_name.endsWith(".shp"): out_name = out_name.left(out_name.length() - 4) step = 20.0 / float(provider.featureCount()) # to single lines and without duplicate lines provider.select() lines = [] while provider.nextFeature(inFeat): inGeom = inFeat.geometry() if inFeat.geometry().isMultipart(): for line in inFeat.geometry().asMultiPolyline(): self.splitline(line, lines) else: self.splitline(inFeat.geometry().asPolyline(), lines) progress += step setValue(progress) # QMessageBox.critical(self.iface.mainWindow(), "d", str(len(lines))) single_lines = QgsVectorLayer("LineString", "single", "memory") single_provider = single_lines.dataProvider() step = 20.0 / float(len(lines)) for line in lines: outFeat.setGeometry(QgsGeometry.fromPolyline(line)) single_provider.addFeatures([outFeat]) single_lines.updateExtents() progress += step setValue(progress) # intersections index = createIndex(single_provider) lines = [] single_provider.select() step = 50.0 / float(single_provider.featureCount()) while single_provider.nextFeature(inFeat): pointList = [] inGeom = inFeat.geometry() lineList = index.intersects(inGeom.boundingBox()) if len(lineList) > 0: for i in lineList: single_provider.featureAtId(int(i), inFeatB, True, allAttrs) tmpGeom = QgsGeometry(inFeatB.geometry()) if inGeom.intersects(tmpGeom): pointGeom = inGeom.intersection(tmpGeom) if pointGeom.type() == QGis.Point: if pointGeom.asPoint() not in pointList: pointList.append(pointGeom.asPoint()) linePoints = [] linePoints = inGeom.asPolyline() s = [i for i in pointList + linePoints if i not in linePoints] if len(s) > 1: l = sortPoints(linePoints[0], s) else: l = s tempLine = [] tempLine.append(linePoints[0]) tempLine.extend(l) tempLine.append(linePoints[1]) countSubLines = len(tempLine) - 1 for p in range(countSubLines): lines.append([tempLine[p], tempLine[p + 1]]) progress += step setValue(progress) del single_lines del single_provider # create polygons polygons = list(polygonize(lines)) self.polyCount = 0 self.polyCount = len(polygons) # QMessageBox.critical(self.iface.mainWindow(), "d", str(fields)) setValue(95) setGeometry = outFeat.setGeometry # ok setAttributeMap = outFeat.setAttributeMap # ok if self.ui.cbGeometry.isChecked(): fields[len(fields)] = QgsField("area", QVariant.Double, "double", 16, 2) fields[len(fields)] = QgsField("perimeter", QVariant.Double, "double", 16, 2) nrArea = len(fields) - 2 nrPerimeter = len(fields) - 1 writer = QgsVectorFileWriter(new_path, provider.encoding(), fields, QGis.WKBPolygon, layer.srs()) for polygon in polygons: # QInputDialog.getText( self.parent, "m", "e", QLineEdit.Normal, str(polygon)) setGeometry(QgsGeometry.fromWkt(polygon.wkt)) setAttributeMap({nrArea: polygon.area, nrPerimeter: polygon.length}) writer.addFeature(outFeat) progress += step setValue(progress) del writer setValue(100) else: writer = QgsVectorFileWriter(new_path, provider.encoding(), fields, QGis.WKBPolygon, layer.srs()) for polygon in polygons: # QInputDialog.getText( self.parent, "m", "e", QLineEdit.Normal, str(polygon)) setGeometry(QgsGeometry.fromWkt(polygon.wkt)) writer.addFeature(outFeat) progress += step setValue(progress) del writer setValue(100) # koniec self.t2 = time() # self.ui.pbProgress.setValue(0) SetWidgetsEnabled(self.ui, True) msg = QMessageBox.question( self, "Polygonizer", "Polygonization finished in %03.2f seconds. \n %d polygons were crested. \n Load created layer?" % ((self.t2 - self.t1), self.polyCount), QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes, ) if msg == QMessageBox.Yes: new_path = self.ui.eOutput.text() if new_path.contains("\\"): out_name = new_path.right((new_path.length() - new_path.lastIndexOf("\\")) - 1) else: out_name = new_path.right((new_path.length() - new_path.lastIndexOf("/")) - 1) if out_name.endsWith(".shp"): out_name = out_name.left(out_name.length() - 4) self.iface.addVectorLayer(self.ui.eOutput.text(), out_name, "ogr") self.close()
##################################### # second plot # display sample intersection # ################################### ax = fig.add_subplot(122) # convert circle polygon to linestring of circle boundary cirle_as_line = polygon.boundary # combine new boundary lines with the input set of lines result_union_lines = cirle_as_line.union(line) # re-create polygons from unioned lines new_polygons = polygonize(result_union_lines) # stores the final split up polygons new_cut_ply = [] # identify which new polygon we want to keep for poly in new_polygons: # check if new poly is inside original otherwise ignore it if poly.centroid.within(polygon): # center_pt = poly.centroid # ax.plot(center_pt.x, center_pt.y, 'o', color='#999999') print "creating new split polygon" patch3 = PolygonPatch(poly, fc='purple', alpha=0.5, zorder=2) ax.add_patch(patch3) # add only polygons that overlap original for export
def track(self, original_img, filtered_img, prev_data): n_objects = self.nObjectsSpinBox.value() n_k_means = self.nKmeansSpinBox.value() non_zero_pos = np.transpose(np.nonzero(filtered_img.T)) contours = [None for i in range(n_objects)] # FIXME: 真っ黒な画像が入力されたときのためアドホックに対処. if self.gmm is None: gmm = GroupTrackerGMM( n_components=n_objects, covariance_type='full', n_iter=1000, init_params='wc', params='wc' ) gmm.set_likelihood_diff_threshold( self.likelihoodDiffThresholdSpinBox.value() ) else: gmm = self.gmm try: gmm._fit(non_zero_pos, n_k_means=n_k_means) self.gmm = gmm res = self.gmm.means_ predict = self.gmm.predict(non_zero_pos) skeletons = [] for i in range(n_objects): p_data = non_zero_pos[predict == i] ch_vtx_list = skeletonize2D.get_concave_hull( p_data.astype(np.int32) ) ch_poly = unary_union(list( polygonize(geometry.MultiLineString(ch_vtx_list)) )) if ch_poly.type == 'Polygon': ch = np.array(ch_poly.exterior.coords) elif ch_poly.type == 'MultiPolygon': try: new_poly = cascaded_union( ch_poly.buffer(10).buffer(-10) ) if new_poly.type == 'MultiPolygon': new_poly = new_poly[np.argmax([ poly.area for poly in new_poly ])] ch = np.array(new_poly.exterior.coords) except: ch = contours[i] contours[i] = ch edges, vertices = skeletonize2D.get_skeleton_from_polygon( ch.astype(np.float64) ) G = nx.Graph() G.add_weighted_edges_from(edges) init_nodes = list(filter( lambda n: G.degree(n) == 1, G.nodes_iter() )) longest_path_list = get_longest_paths(init_nodes, G) optimal_path = skeletonize2D.find_optimal_path( longest_path_list, vertices ) skeletons.append(vertices[optimal_path[1:-1], :]) # tck, u = splprep([unique[path, 0], unique[path, 1]]) # t = 0.0 # t_list = [] # while t<1: # t_list.append(t) # t += 1.0/ np.linalg.norm(splev(t, tck, der=1)) # # interpolated = splev(t_list,tck) # skeletons.append(np.array([ # [x,y] for x,y in zip(*interpolated) # ])) # plt.plot(unique[path, 0], unique[path, 1]) # plt.scatter(interpolated[0], interpolated[1]) # plt.show() except Exception as e: if self.gmm is None: res = np.full((n_objects, 2), np.nan) skeletons = np.full((n_objects, 3, 2), np.nan) contours = np.full((n_objects, 3, 2), np.nan) else: if prev_data['ignore_error']: res = prev_data['position'] skeletons = prev_data['path'] contours = prev_data['polygon'] else: raise RuntimeError( '{}\n' 'Please check "Ignore mis-detection error" on ' 'if you want to ignore tracking errors.'.format(e) ) return {'position': res, 'path': skeletons, 'polygon': contours}
def _overlay_old(df1, df2, how, use_sindex=True, **kwargs): """Perform spatial overlay between two polygons. Currently only supports data GeoDataFrames with polygons. Implements several methods that are all effectively subsets of the union. Parameters ---------- df1 : GeoDataFrame with MultiPolygon or Polygon geometry column df2 : GeoDataFrame with MultiPolygon or Polygon geometry column how : string Method of spatial overlay: 'intersection', 'union', 'identity', 'symmetric_difference' or 'difference'. use_sindex : boolean, default True Use the spatial index to speed up operation if available. Returns ------- df : GeoDataFrame GeoDataFrame with new set of polygons and attributes resulting from the overlay """ allowed_hows = [ 'intersection', 'union', 'identity', 'symmetric_difference', 'difference', # aka erase ] if how not in allowed_hows: raise ValueError("`how` was \"%s\" but is expected to be in %s" % \ (how, allowed_hows)) if isinstance(df1, GeoSeries) or isinstance(df2, GeoSeries): raise NotImplementedError("overlay currently only implemented for GeoDataFrames") # Collect the interior and exterior rings rings1 = _extract_rings(df1) rings2 = _extract_rings(df2) mls1 = MultiLineString(rings1) mls2 = MultiLineString(rings2) # Union and polygonize mm = unary_union([mls1, mls2]) newpolys = polygonize(mm) # determine spatial relationship collection = [] for fid, newpoly in enumerate(newpolys): cent = newpoly.representative_point() # Test intersection with original polys # FIXME there should be a higher-level abstraction to search by bounds # and fall back in the case of no index? if use_sindex and df1.sindex is not None: candidates1 = [x.object for x in df1.sindex.intersection(newpoly.bounds, objects=True)] else: candidates1 = [i for i, x in df1.iterrows()] if use_sindex and df2.sindex is not None: candidates2 = [x.object for x in df2.sindex.intersection(newpoly.bounds, objects=True)] else: candidates2 = [i for i, x in df2.iterrows()] df1_hit = False df2_hit = False prop1 = None prop2 = None for cand_id in candidates1: cand = df1.loc[cand_id] if cent.intersects(cand[df1.geometry.name]): df1_hit = True prop1 = cand break # Take the first hit for cand_id in candidates2: cand = df2.loc[cand_id] if cent.intersects(cand[df2.geometry.name]): df2_hit = True prop2 = cand break # Take the first hit # determine spatial relationship based on type of overlay hit = False if how == "intersection" and (df1_hit and df2_hit): hit = True elif how == "union" and (df1_hit or df2_hit): hit = True elif how == "identity" and df1_hit: hit = True elif how == "symmetric_difference" and not (df1_hit and df2_hit): hit = True elif how == "difference" and (df1_hit and not df2_hit): hit = True if not hit: continue # gather properties if prop1 is None: prop1 = pd.Series(dict.fromkeys(df1.columns, None)) if prop2 is None: prop2 = pd.Series(dict.fromkeys(df2.columns, None)) # Concat but don't retain the original geometries out_series = pd.concat([prop1.drop(df1._geometry_column_name), prop2.drop(df2._geometry_column_name)]) out_series.index = _uniquify(out_series.index) # Create a geoseries and add it to the collection out_series['geometry'] = newpoly collection.append(out_series) # Return geodataframe with new indices return GeoDataFrame(collection, index=range(len(collection)))
def get_concave_hull(points, cut): """ Find the concave hull for a set of points """ max_x, max_y = np.max(points, axis=0) min_x, min_y = np.min(points, axis=0) log.info("Found %i nodes, bounded in x by (%i, %i)" " and y by (%i, %i)", len(points), min_x, max_x, min_y, max_y) size = max(max_x - min_x, max_y - min_y) tri = Delaunay(points) log.info("Found %i Delaunay triangles", len(tri.vertices)) edges = Counter() def add_edge(i, j): """Count the occurences of each edge. """ edges[(i, j) if i > j else (j, i)] += 1 # loop over triangles: # ia, ib, ic = indices of corner points of the triangle for ia, ib, ic in tri.vertices: pa = points[ia] pb = points[ib] pc = points[ic] # Lengths of sides of triangle a = math.sqrt((pa[0]-pb[0])**2 + (pa[1]-pb[1])**2) b = math.sqrt((pb[0]-pc[0])**2 + (pb[1]-pc[1])**2) c = math.sqrt((pc[0]-pa[0])**2 + (pc[1]-pa[1])**2) # Semiperimeter of triangle s = (a + b + c)/2.0 argument = s*(s-a)*(s-b)*(s-c) if argument < 0: continue # Area of triangle by Heron's formula area = math.sqrt(argument) if area <= 0: continue circum_r = a*b*c/(4.0*area) # Here's the radius filter. if circum_r / size < 1.0 / cut: add_edge(ia, ib) add_edge(ib, ic) add_edge(ic, ia) log.info("After filter, %i edges remain", len(edges)) if not edges: log.warning("No edges remain, concave hull is not defined!") return None # Exterior edges are only owned by one triangle. exterior_edges = [ points[list(edge)] for edge, count in edges.iteritems() if count == 1] m = MultiLineString(exterior_edges) log.info("Polygonizing") triangles = list(polygonize(m)) log.info("Unionizing") polygon = cascaded_union(triangles) best_polygon = polygon best_area = polygon.area if isinstance(polygon, MultiPolygon): best_area = 0 log.info("Multi polygons detected") for ip, subpoly in enumerate(polygon): #log.info("Poly %i - area: %f", ip, subpoly.area) if subpoly.area > best_area: best_polygon = subpoly best_area = subpoly.area log.info("Found main polygon with area: %f", best_area) return best_polygon