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 roundAndRemoveInvalidPoints(polygon): coords = [(x, y) for x, y in polygon.exterior.coords] coords = [(round(x, 8), round(y, 8)) for x, y in coords] retPoly = Polygon(coords) if retPoly.is_valid: return retPoly explanation = explain_validity(retPoly) match = re.match('Self-intersection\[(?P<first>-?\d+\.\d+) (?P<sec>-?\d+\.\d+)\]', explanation) if not match: print(explain_validity(retPoly)) assert False errorx, errory = float(match.group(1)), float(match.group(2)) closestD = inf closestP = None for px, py in retPoly.exterior.coords: d = (px - errorx) ** 2 + (py - errory) ** 2 if d < closestD: closestD = d closestP = (px, py) retPoly = Polygon([(x, y) for x, y in list(retPoly.exterior.coords) if (x, y) != closestP]) if retPoly.is_valid: return retPoly print(explain_validity(retPoly)) assert False
def overlapping_entity_pairs(entity_set1_overlaid, entity_set2_overlaid): entity_set1_overlaid = gpd.GeoDataFrame( pd.concat(entity_set1_overlaid, ignore_index=True)) entity_set2_overlaid = gpd.GeoDataFrame( pd.concat(entity_set2_overlaid, ignore_index=True)) all_vertices1 = [] all_vertices2 = [] # Obtain vertices of all entities to be matched. for index, row in entity_set1_overlaid.iterrows(): if row.geometry.geom_type == 'Polygon': all_vertices1.append(list(row.geometry.exterior.coords)) else: all_vertices1.append(list(row.geometry.coords)) all_vertices1 = MultiPoint(sum(all_vertices1, [])) for index, row in entity_set2_overlaid.iterrows(): if row.geometry.geom_type == 'Polygon': all_vertices2.append(list(row.geometry.exterior.coords)) else: all_vertices2.append(list(row.geometry.coords)) all_vertices2 = MultiPoint(sum(all_vertices2, [])) # Compute overlapping area by computing the intersection area of convex_hulls which are generated with all vertices. all_vertices1_convexhull = all_vertices1.convex_hull all_vertices2_convexhull = all_vertices2.convex_hull overlapping_area = all_vertices1_convexhull.intersection( all_vertices2_convexhull) links_gdf = gpd.GeoDataFrame(geometry=[overlapping_area]) links_gdf.to_file("intersection.shp") # Those entities which do not intersect with the overlapping area will be removed. entity_set1_overlaid_copy = entity_set1_overlaid.copy() for index, row in entity_set1_overlaid.iterrows(): if explain_validity(row.geometry) == 'Valid Geometry': if row.geometry.intersect(overlapping_area).is_empty: delete_index = entity_set1_overlaid_copy[ entity_set1_overlaid_copy['FeaID'] == row.FeaID].index.tolist() entity_set1_overlaid_copy.drop( entity_set1_overlaid_copy.index[delete_index], inplace=True) entity_set1_overlaid_copy.index = range( len(entity_set1_overlaid_copy)) entity_set2_overlaid_copy = entity_set2_overlaid.copy() for index, row in entity_set2_overlaid.iterrows(): if explain_validity(row.geometry) == 'Valid Geometry': if row.geometry.intersect(overlapping_area).is_empty: delete_index = entity_set2_overlaid_copy[ entity_set2_overlaid_copy['FeaID'] == row.FeaID].index.tolist() entity_set2_overlaid_copy.drop( entity_set2_overlaid_copy.index[delete_index], inplace=True) entity_set2_overlaid_copy.index = range( len(entity_set2_overlaid_copy)) return entity_set1_overlaid_copy, entity_set2_overlaid_copy
def atr_within(row, radius): geometry_sou = row['sou_feature'] geometry_tar = row['tar_feature'] buf_1 = geometry_sou.buffer(radius) buf_2 = geometry_tar.buffer(radius) if explain_validity(geometry_sou) == 'Valid Geometry' and explain_validity(geometry_tar) == 'Valid Geometry': area = buf_1.intersection(buf_2).area area_ratio = area/(min(buf_1.area, buf_2.area)) return area_ratio
def FixAntiMeridianPolygon(ls): """Detects and fix a 180deg crossing polygon. Args: ls: a linestring as a list of 'lon,lat,alt' strings. Returns: None if not a anti-meridian polygon otherwise the western part linestring. Side effects: If detected anti-meridian, the given linestring is modified to be the easter part. """ coords = [] found_anti_meridian = False for c in ls: xy = c.split(',') coords.append([float(xy[0]), float(xy[1])]) if float(xy[0]) == 180: found_anti_meridian = True lr = LinearRing(coords) polygon = Polygon(lr) # The invalid polygon is the case of Semisopochnoi Island, which # zone crosses the antimeridian. if not polygon.is_valid: print 'POLYGON IS NOT VALID! : %d' % len(ls) explain_validity(polygon) if found_anti_meridian: print 'Polygon spans anti-meridian - Splitting in 2' # To deal with this case, we'll split the zone into two pieces, # one of which is in the eastern hemisphere and one in the # western hemisphere. This is purely a tooling issue to make # the zone easier to manage with other software. new_piece = [] begin_anti_meridian = -1 end_anti_meridian = -1 for i in range(0, len(ls)): xy = ls[i].split(',') if float(xy[0]) == 180: # Note: the '-' is to reverse the sign so shapely sees # the coordinates correctly. new_piece.append('-' + ls[i]) if begin_anti_meridian == -1: begin_anti_meridian = i else: end_anti_meridian = i new_piece.append(new_piece[0]) elif begin_anti_meridian >= 0 and end_anti_meridian == -1: new_piece.append(ls[i]) del ls[begin_anti_meridian + 1:end_anti_meridian] return new_piece return None
def fix_poly(plist): print " " + str(len(plist)) try: shape = Polygon(plist) except ValueError: return [] validity = explain_validity(shape) if validity == "Valid Geometry": return [shape] shape_list = [] while plist != []: #print " " + str(len(plist)) longest_valid = 0 best_choice = None for i in range(len(plist)): # it might be that no matter how long the longest valid polygon is from this point, it can't # beat the current max, if so, just break if (len(plist) - i) < longest_valid: break longest_valid_i = 0 best_choice_i = None for j in range(i + 1, len(plist)): try: temp_shape = Polygon(plist[i:j]) validity = explain_validity(temp_shape) if (validity == "Valid Geometry") and ( (j - i) > longest_valid_i): longest_valid_i = j - i best_choice_i = i, j except ValueError: continue if longest_valid_i > longest_valid: longest_valid = longest_valid_i best_choice = best_choice_i if best_choice is None: break i, j = best_choice shape = Polygon(plist[i:j]) shape_list.append(shape) test_validity = explain_validity(shape) assert test_validity == "Valid Geometry" plist_temp = plist[:i] plist_temp.extend(plist[j:]) plist = plist_temp return shape_list
def fix_poly(plist): print " " + str(len(plist)) try: shape = Polygon(plist) except ValueError: return [] validity = explain_validity(shape) if validity == "Valid Geometry": return [shape] shape_list = [] while plist != []: #print " " + str(len(plist)) longest_valid = 0 best_choice = None for i in range(len(plist)): # it might be that no matter how long the longest valid polygon is from this point, it can't # beat the current max, if so, just break if (len(plist)-i) < longest_valid: break longest_valid_i = 0 best_choice_i = None for j in range(i+1,len(plist)): try: temp_shape = Polygon(plist[i:j]) validity = explain_validity(temp_shape) if (validity == "Valid Geometry") and ((j-i) > longest_valid_i): longest_valid_i = j-i best_choice_i = i,j except ValueError: continue if longest_valid_i > longest_valid: longest_valid = longest_valid_i best_choice = best_choice_i if best_choice is None: break i,j = best_choice shape = Polygon(plist[i:j]) shape_list.append(shape) test_validity = explain_validity(shape) assert test_validity == "Valid Geometry" plist_temp = plist[:i] plist_temp.extend(plist[j:]) plist = plist_temp return shape_list
def minkowski_sum_2(poly1, poly2): ''' Slower minkowski sum algorithm (convex hull operation is a lot slower), but uses blazing fast numpy sums and doesn't require particular polygon orientation''' assert poly1.is_valid, validation.explain_validity(poly1) assert poly2.is_valid, validation.explain_validity(poly2) ext1 = np.array(poly1.boundary)[:-1,:] ext2 = np.array(poly2.boundary)[:-1,:] n_ext2 = ext2.shape[0] n_ext1 = ext1.shape[0] ext_sum = np.repeat(ext1, n_ext2, axis=0) + np.tile(ext2, (n_ext1, 1)) return geom.asPolygon(ext_sum).convex_hull
def _get_reprojected_features(input_file=None, dst_bounds=None, dst_crs=None, validity_check=False): with fiona.open(input_file, 'r') as vector: vector_crs = CRS(vector.crs) # Reproject tile bounding box to source file CRS for filter: if vector_crs == dst_crs: dst_bbox = box(*dst_bounds) else: dst_bbox = reproject_geometry(box(*dst_bounds), src_crs=dst_crs, dst_crs=vector_crs, validity_check=True) for feature in vector.filter(bbox=dst_bbox.bounds): feature_geom = to_shape(feature['geometry']) if not feature_geom.is_valid: feature_geom = feature_geom.buffer(0) # skip feature if geometry cannot be repaired if not feature_geom.is_valid: logger.exception("feature omitted: %s", explain_validity(feature_geom)) continue # only return feature if geometry type stayed the same after # reprojecction geom = clean_geometry_type(feature_geom.intersection(dst_bbox), feature_geom.geom_type) if geom: # Reproject each feature to tile CRS try: geom = reproject_geometry(geom, src_crs=vector_crs, dst_crs=dst_crs, validity_check=validity_check) if validity_check and not geom.is_valid: raise TopologicalError( "reprojected geometry invalid: %s" % (explain_validity(geom))) except TopologicalError: logger.exception("feature omitted: reprojection failed") yield { 'properties': feature['properties'], 'geometry': mapping(geom) } else: logger.exception( "feature omitted: geometry type changed after reprojection" )
def check_valid_shape(self, shape): if not shape.is_valid: validity = explain_validity(shape) print("Shape is not valid. Explanation: %s", validity) return False else: return True
def toPolygon(self, tileXy,imgPath): img = Image.open(imgPath)#打开图片 newImg = Image.new("RGBA",(260,260),(255,255,255)) newImg.paste(img,(2,2)) newImg.save(imgPath) img = io.imread(imgPath) img = color.rgb2gray(img) # 检测所有图形的轮廓 contours = measure.find_contours(img, 0.8) # 绘制轮廓 polygonList = [] for n, contour in enumerate(contours): pointList = [] for xy in contour: pointList.append(self.pixelToLonlat(tileXy,xy[1]-2, xy[0]-2)) if len(pointList) <4: continue p = Polygon(pointList) if not p.is_valid: p = p.buffer(0) assert p.is_valid, \ "Contour %r did not make valid polygon %s because %s" \ % (p, p.wkt, explain_validity(p)) polygonList.append(p) return polygonList
def shape(self): # shapely's idea of "holes" are to subtract everything in the second set # from the first. So let's at least make sure the "first" thing is the # biggest path. paths = self.paths paths.sort(key=lambda point_list: shgeo.Polygon(point_list).area, reverse=True) # Very small holes will cause a shape to be rendered as an outline only # they are too small to be rendered and only confuse the auto_fill algorithm. # So let's ignore them if shgeo.Polygon(paths[0]).area > 5 and shgeo.Polygon( paths[-1]).area < 5: paths = [path for path in paths if shgeo.Polygon(path).area > 3] polygon = shgeo.MultiPolygon([(paths[0], paths[1:])]) # There is a great number of "crossing border" errors on fill shapes # If the polygon fails, we can try to run buffer(0) on the polygon in the # hope it will fix at least some of them if not self.shape_is_valid(polygon): why = explain_validity(polygon) message = re.match(r".+?(?=\[)", why) if message.group(0) == "Self-intersection": buffered = polygon.buffer(0) # if we receive a multipolygon, only use the first one of it if type(buffered) == shgeo.MultiPolygon: buffered = buffered[0] # we do not want to break apart into multiple objects (possibly in the future?!) # best way to distinguish the resulting polygon is to compare the area size of the two # and make sure users will not experience significantly altered shapes without a warning if type(buffered) == shgeo.Polygon and math.isclose( polygon.area, buffered.area, abs_tol=0.5): polygon = shgeo.MultiPolygon([buffered]) return polygon
def pts2feature(pts, nom): poly = [] km = None for pt in pts: lon = dms2d(pt[1], pt[2], pt[3], pt[4]) lat = dms2d(pt[5], pt[6], pt[7], pt[8]) poly.append((lon, lat)) km = pt[9] if km: # calcule un cercle en projection Lambert 93 zone_local = ops.transform( partial( pyproj.transform, pyproj.Proj(init='EPSG:4326'), # EPSG:4326 est WGS 84 pyproj.Proj(init='EPSG:2154')), Point(poly[0])).buffer(float(km.replace(',', '.')) * 1000, resolution=36) zone = ops.transform( partial( pyproj.transform, pyproj.Proj(init='EPSG:2154'), pyproj.Proj(init='EPSG:4326') # EPSG:4326 est WGS 84 ), zone_local) return (Feature(zone, {'nom': nom, 'distance': km})) else: valid = explain_validity(Polygon(poly)) if valid != 'Valid Geometry': print('ERREUR:', nom, valid) exit() if Polygon(poly).area > 0.01 and nom not in ['CSG KOUROU 1']: print('ERREUR:', nom, 'emprise trop importante') exit() return (Feature(Polygon(poly), {'nom': nom}))
def _LoadPolygons(self): with Shapely2ESRI(self.ShapefilePath) as reader: polygons = [] self.TRACKER.startProcess(len(reader)) for polygon in reader.readThrough(): fid = polygon['FID'] if not polygon.is_valid: raise Exception("The polygon at FID=%s is not valid: %s" % (fid, explain_validity(polygon))) try: val = polygon[self.ShapefileFieldIdToLoad] polygon[self.ShapefileFieldIdToLoad] = float(val) except: raise Exception( "Cannot cast '%s' to float in field '%s' for FID=%s" % (val, self.ShapefileFieldIdToLoad, fid)) polygons.append(polygon) self.TRACKER.completeSubtask() self.TRACKER.completeTask() return polygons
def validateGeometry(geom): from shapely.validation import explain_validity if explain_validity(geom) == "Valid Geometry": return True else: return False
def ensure_ok(self, coords): for co in coords: shape = geometry.Polygon(co) if not shape.exterior.is_simple: msg = "La forme du volume n'est pas correcte \nCoordonnees :{co} \nCause :{details}".format( co=co, details=explain_validity(shape)) raise InconsistentGeometricModel(msg, ids=[self.id])
def fix_rings(multipolygon, strict=False): """ This resolves a multipolygon with invalid exterior/interior ring pairing. It does so by first sorting the exteriors by z-order (so that the exteriors contained by the most other exteriors are "higher" & get priority). Then, interiors are assigned to their highest containing exterior ring. This ensures that the "most interior" interior rings are paired with the "most interior" external rings. Requires shapely, may take a bit for shapes with many exteriors/interiors. Parameters --------- multipolygon: a shapely polygon. (should be invalid due to ring ordering) Returns ------- multipolygon: a shapely polygon that should have valid ring ordering. May be invalid due to other reasons. NOTE: This function has undefined behavior for invalid multipolygons. """ from shapely import geometry as geom from shapely.ops import cascaded_union from shapely.validation import explain_validity vexplain = explain_validity(multipolygon) if "hole lies outside shell" not in vexplain.lower(): if strict: from shapely.geos import TopologicalError def tell_user(x): raise TopologicalError(x) else: from warnings import warn as tell_user tell_user("Shape is invalid: \n{}".format(vexplain)) exteriors = [geom.Polygon(part.exterior) for part in multipolygon.geoms] interiors = [ geom.Polygon(interior) for part in multipolygon.geoms for interior in part.interiors ] zorder = [ sum([exterior.contains(other_exterior) for other_exterior in exteriors]) - 1 for exterior in exteriors ] sort_zorder = np.argsort(zorder) zordered_exteriors = np.asarray(exteriors)[sort_zorder] polygons = [[exterior] for exterior in exteriors] for i, exterior in enumerate(zordered_exteriors): owns = [exterior.contains(interior) for interior in interiors] owned_interiors = [ interior for owned, interior in zip(owns, interiors) if owned ] polygons[i] = exterior.difference(cascaded_union(owned_interiors)) interiors = [interior for owned, interior in zip(owns, interiors) if not owned] return geom.MultiPolygon(polygons)
def _get_valid_multipolygon( self, polygon: Union[Polygon, MultiPolygon] ) -> MultiPolygon: """Get a valid multipolygon from the input `polygon` Validates and if applicable creates a multipolygon from the input argument `polygon` Parameters ---------- polygon : Polygon or MultiPolygon The input polygon or multipolygon which might not be topologically valid. Returns ------- MultiPolygon A validated `shapely` `MultiPolygon` entity """ # TODO: Performance bottleneck for valid checks if not polygon.is_valid: polygon = ops.unary_union(polygon) if not polygon.is_valid: polygon = polygon.buffer(0) if not polygon.is_valid: raise ValueError(explain_validity(polygon)) if isinstance(polygon, Polygon): polygon = MultiPolygon([polygon]) return polygon
def show_polygon(polygon): print("Polygon") print(explain_validity(polygon)) x, y = polygon.exterior.xy plt.plot(x, y) plt.axis('equal') plt.show()
def checkGeom(geodataframe): """ Function to check validity of geometry. Returns message from shapely explain_validity if geometry is not 'Valid Geometry' Parameters ---------- geodataframe : TYPE DESCRIPTION. Returns ------- Message. """ for geometry in geodataframe.geometry: if explain_validity(geometry) != 'Valid Geometry': print(explain_validity(geometry))
def ensure_ok(self): shape = self.shape # CAUTION Cache the shape if shape.is_valid: return else : raise InconsistentGeometricModel("Invalid shapely shape : {details}", details=explain_validity(shape), ids=[self.id])
def validate_geometry(geometry: BaseGeometry): if not isinstance(geometry, BaseGeometry): raise ValidationError( 'GeometryField expected a Shapely BaseGeometry child-class.') if not geometry.is_valid: raise ValidationError('Invalid geometry: %s' % validation.explain_validity(geometry))
def _contour_to_poly(contour): poly = Polygon(contour) if not poly.is_valid: poly = poly.buffer(0) assert poly.is_valid, \ "Contour %r did not make valid polygon %s because %s" \ % (contour, poly.wkt, explain_validity(poly)) return poly
def _repair(geom): repaired = geom.buffer(0) if geom.geom_type in ["Polygon", "MultiPolygon"] else geom if repaired.is_valid: return repaired else: raise TopologicalError( "geometry is invalid (%s) and cannot be repaired" % explain_validity(repaired) )
def ensure_ok(self): shape = self.shape # CAUTION Cache the shape if shape.is_valid: return else: msg = "Contour invalide :\nElement : {name} \nCause : {details}".format( name=self.name, details=explain_validity(shape)) raise InconsistentGeometricModel(msg, ids=[self.id])
def importSVGroute(IDT_group): IDT_group_dir = IDT_group['IDT_group_dir'] Tk().withdraw( ) # we don't want a full GUI, so keep the root window from appearing svg_filename = tkFileDialog.askopenfilename(title='Wafer routing filename', defaultextension='svg', initialdir=IDT_group_dir) all_points = SVGT.read_colored_path_from_svg(svg_filename) route = [[], []] for points in all_points['red']: polygon = shapely_geom.Polygon(points.T) polygon_validity = explain_validity(polygon) if polygon_validity == 'Valid Geometry': route[0].append(polygon) else: tkMessageBox.showwarning('Error in svg import', polygon_validity) for points in all_points['blue']: polygon = shapely_geom.Polygon(points.T) polygon_validity = explain_validity(polygon) if polygon_validity == 'Valid Geometry': route[1].append(polygon) else: tkMessageBox.showwarning('Error in svg import', polygon_validity) route[0] = shapely_geom.MultiPolygon(route[0]) route[1] = shapely_geom.MultiPolygon(route[1]) #outbox = route[0].bounds #dx = outbox[2]-outbox[0] #dy = outbox[3]-outbox[1] #x0 = outbox[0]+dx/2 #y0 = outbox[1]+dy/2 x0 = 4000 y0 = 4000 factor = 1e-5 route[0] = shapely_affinity.translate(route[0], xoff=-x0, yoff=-y0) route[0] = shapely_affinity.scale(route[0], xfact=factor, yfact=factor, origin=(0, 0, 0)) route[1] = shapely_affinity.translate(route[1], xoff=-x0, yoff=-y0) route[1] = shapely_affinity.scale(route[1], xfact=factor, yfact=factor, origin=(0, 0, 0)) IDT_group['route'] = route
def genFeature(self, geom, allGeoms, allBounds, errorCounter): try: curShape = asShape(geom) curBounds = curShape.bounds allGeoms.append(curShape) allBounds.append(curBounds) except Exception as e: logging.error(explain_validity(curShape)) errorCounter += 1 return allGeoms, allBounds, errorCounter
def validation_errors(self): if not self.shape_is_valid(self.shape): why = explain_validity(self.shape) message, x, y = re.findall(r".+?(?=\[)|-?\d+(?:\.\d+)?", why) # I Wish this weren't so brittle... if "Hole lies outside shell" in message: yield UnconnectedError((x, y)) else: yield InvalidShapeError((x, y))
def tiles_from_geom(self, geometry, zoom): """ Return all tiles intersecting with input geometry. - geometry: shapely geometry - zoom: zoom level """ try: assert geometry.is_valid except AssertionError: try: clean = geometry.buffer(0.0) assert clean.is_valid assert clean.area > 0 geometry = clean except AssertionError: raise IOError( str( "invalid geometry could not be fixed: '%s'" % explain_validity(geometry) ) ) if geometry.almost_equals(geometry.envelope, ROUND): for tile in self.tiles_from_bbox(geometry, zoom): yield tile elif geometry.geom_type == "Point": lon, lat = list(geometry.coords)[0] tilelon = self.left tilelat = self.top tile_x_size = self.tile_x_size(zoom) tile_y_size = self.tile_y_size(zoom) col = -1 row = -1 while tilelon < lon: tilelon += tile_x_size col += 1 while tilelat > lat: tilelat -= tile_y_size row += 1 yield self.tile(zoom, row, col) elif geometry.geom_type in ( "LineString", "MultiLineString", "Polygon", "MultiPolygon", "MultiPoint", "GeometryCollection" ): prepared_geometry = prep( clip_geometry_to_srs_bounds(geometry, self) ) for tile in self.tiles_from_bbox(geometry, zoom): if prepared_geometry.intersects(tile.bbox()): yield tile elif geometry.is_empty: pass else: raise ValueError("ERROR: no valid geometry: %s" % geometry.type)
def fix_geom(geom): if not geom.is_valid and hasattr(geom, 'buffer'): # Attempt to "fix" invalid geometries. L.info(explain_validity(geom)) # Shapely >= 1.8 includes a make_valid method # which should be used after it is released. For # now use the buffer(0) "hack" that resolves # self-intersections pretty well. # from shapely.validation import make_valid # p = make_valid(p) return geom.buffer(0)
def WayToPoly(wayId, ways, nodes): wayData = ways[wayId] wayNodes = wayData[0] if wayNodes[0] == wayNodes[-1]: #Close polygon tags = wayData[1] pts = [] for nid in wayNodes: if int(nid) not in nodes: print "Warning: missing node", nid continue pts.append(nodes[int(nid)][0]) #Require at least 3 points if len(pts) < 3: return None poly = Polygon(pts) if not poly.is_valid: print "Warning: polygon is not valid" print explain_validity(poly) poly = poly.buffer(0) return poly else: #Unclosed way tags = wayData[1] pts = [] for nid in wayNodes: if int(nid) not in nodes: print "Warning: missing node", nid continue pts.append(nodes[int(nid)][0]) line = LineString(pts) if not line.is_valid: print "Warning: polygon is not valid" print explain_validity(line) line = line.buffer(0) return line return None
def make_line(line_points): """Instantiate a LineString from a list of point pairs, or return an error string""" if len(line_points) < 2: return 'has too few points' line = LineString(line_points) if not line.is_valid: return explain_validity(line) elif line.is_empty: return 'is empty' elif line.bounds[0] < 0 or line.bounds[1] < 0: return 'is negative' return line
def fix_rings(multipolygon, strict=False): """ This resolves a multipolygon with invalid exterior/interior ring pairing. It does so by first sorting the exteriors by z-order (so that the exteriors contained by the most other exteriors are "higher" & get priority). Then, interiors are assigned to their highest containing exterior ring. This ensures that the "most interior" interior rings are paired with the "most interior" external rings. Requires shapely, may take a bit for shapes with many exteriors/interiors. Argument: --------- multipolygon: a shapely polygon. (should be invalid due to ring ordering) Returns: -------- multipolygon: a shapely polygon that should have valid ring ordering. May be invalid due to other reasons. NOTE: This function has undefined behavior for invalid multipolygons. """ from shapely import geometry as geom from shapely.ops import cascaded_union from shapely.validation import explain_validity vexplain = explain_validity(multipolygon) if "hole lies outside shell" not in vexplain.lower(): if strict: from shapely.geos import TopologicalError def tell_user(x): raise TopologicalError(x) else: from warnings import warn as tell_user tell_user('Shape is invalid for a different reason than' ' hole outside of shell: \n{}'.format(vexplain)) exteriors = [geom.Polygon(part.exterior) for part in multipolygon.geoms] interiors = [geom.Polygon(interior) for part in multipolygon.geoms for interior in part.interiors] zorder = [sum([exterior.contains(other_exterior) for other_exterior in exteriors]) - 1 for exterior in exteriors] sort_zorder = np.argsort(zorder) zordered_exteriors = np.asarray(exteriors)[sort_zorder] polygons = [[exterior] for exterior in exteriors] for i, exterior in enumerate(zordered_exteriors): owns = [exterior.contains(interior) for interior in interiors] owned_interiors = [interior for owned,interior in zip(owns, interiors) if owned] polygons[i] = exterior.difference(cascaded_union(owned_interiors)) interiors = [interior for owned, interior in zip(owns, interiors) if not owned] return geom.MultiPolygon(polygons)
def GetOuterPolygons(geo, cornerx, cornery): outerPoly = [] assert geo['type'] == "FeatureCollection" feature = geo['features'][0] geom = feature['geometry'] for poly in geom['coordinates'][1:]: movedPoly = [] for pt in poly: movedPoly.append((pt[0]+cornerx, pt[1]+cornery)) outerPoly.append(movedPoly) print "Outer Polys", len(outerPoly), cornerx, cornery outerPolyShps = [] for poly in outerPoly: newPoly = Polygon(poly) if not newPoly.is_valid: print "Warning: invalid polygon (1)" print explain_validity(newPoly) newPoly = newPoly.buffer(0) outerPolyShps.append(newPoly) return outerPolyShps
def invalid_geometries(df): """Given a GeoDataFrame, returns a list of row indices with invalid geometries. :param df: :class:`geopandas.GeoDataFrame` :rtype: list of int """ invalid = [] for idx, row in df.iterrows(): validity = explain_validity(row.geometry) if validity != "Valid Geometry": invalid.append(idx) return invalid
def GetInnerPolygons(geo, cornerx, cornery): innerPoly = [] for feature in geo['features'][1:]: #print feature['type'] geom = feature['geometry'] movedPoly = [] for pt in geom['coordinates'][0]: movedPoly.append((pt[0]+cornerx, pt[1]+cornery)) innerPoly.append(movedPoly) print "Inner Polys", len(innerPoly), cornerx, cornery innerPolyShps = [] for poly in innerPoly: newPoly = Polygon(poly) if not newPoly.is_valid: print "Warning: invalid polygon (2)" print explain_validity(newPoly) newPoly = newPoly.buffer(0) innerPolyShps.append(newPoly) return innerPolyShps
def GetShapelyPolygons(geo, cornerx, cornery, tc): print tc outerPolys = GetOuterPolygons(geo, cornerx, cornery) innerPolys = GetInnerPolygons(geo, cornerx, cornery) overlaps = [] for oPoly in outerPolys: matches = [] for iNum, iPoly in enumerate(innerPolys): if oPoly.intersects(iPoly): matches.append(iNum) overlaps.append(matches) combined = [] for oPoly, matches in zip(outerPolys, overlaps): newPoly = copy.deepcopy(oPoly) for inInd in matches: newPoly = newPoly.difference(innerPolys[inInd]) if not newPoly.is_valid: print "Warning: invalid polygon (3)" print explain_validity(newPoly) combined.append(newPoly) return combined
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 make_poly(polygon_points): """Instantiate a Polygon from a list of point pairs, or return an error string""" if len(polygon_points) < 4: return 'has too few points' poly = Polygon(polygon_points) if POLY_TOLERANCE: poly = poly.simplify(POLY_TOLERANCE) if not poly.is_valid: return explain_validity(poly) elif poly.is_empty: return 'is empty' elif poly.bounds[0] < 0 or poly.bounds[1] < 0: return 'is negative' return poly
def split_horiz_by_point(polygon, point): """""" assert polygon.geom_type == "Polygon" and point.geom_type == "Point" nx, ny, xx, xy = polygon.bounds if point.x < nx or point.x > xx: return [polygon] lEnv = sg.LineString([(nx, ny), (point.x, xy)]).envelope rEnv = sg.LineString([(point.x, ny), (xx, xy)]).envelope try: return [polygon.intersection(lEnv), polygon.intersection(rEnv)] except Exception as e: print "Geometry error: %s" % validation.explain_validity(polygon) return [polygon.buffer(0)]
def check_bad_geom(geom, osm_id): """ Check if geom is valid """ try: # check if we can parse the geom and determine why is geometry # invalid tst_geom = shapely.wkb.loads(geom.ExportToWkb()) if tst_geom.is_valid: return False else: reason = explain_validity(tst_geom) LOG.error( 'Bad geometry for the feature %s, reason: %s', osm_id, reason ) except: reason = 'BONKERS!' LOG.critical('BONKERS geometry for the feature %s', osm_id) return reason
def _LoadPolygons(self): with Shapely2ESRI(self.ShapefilePath) as reader: polygons = [] self.TRACKER.startProcess(len(reader)) for polygon in reader.readThrough(): fid = polygon['FID'] if not polygon.is_valid: raise Exception("The polygon at FID=%s is not valid: %s" %(fid, explain_validity(polygon))) try: val = polygon[self.ShapefileFieldIdToLoad] polygon[self.ShapefileFieldIdToLoad] = float(val) except: raise Exception("Cannot cast '%s' to float in field '%s' for FID=%s" %(val, self.ShapefileFieldIdToLoad, fid)) polygons.append(polygon) self.TRACKER.completeSubtask() self.TRACKER.completeTask() return polygons
def validateGeometry(self, geom): from shapely.validation import explain_validity if (explain_validity(geom) == 'Valid Geometry'): return True else: return False
vertices_idxs = voronoi.regions[voronoi_region] # ignore infinite cells if -1 in vertices_idxs: continue # add lines between all interior points points = [(tuple(voronoi.vertices[a]), tuple(voronoi.vertices[b])) for a, b in itertools.combinations(vertices_idxs, 2)] ring = MultiLineString(points) polygons = list(polygonize([ring])) if not polygons: log.warning("No polygons detected in line, skipping") continue else: if not polygons[0].is_valid: log.warning("Invalid polygon! %s", explain_validity(polygons[0])) polygons[0] = polygons[0].buffer(0) log.warning("After cleaning validity = %i", polygons[0].is_valid) cluster_polygons.append(polygons[0]) log.info("Merging %i cluster polygons", len(cluster_polygons)) polygon = cascaded_union(cluster_polygons) #import pdb #pdb.set_trace() log.info("Created polygon for cluster %i with area %0.2f", clusteridx, polygon.area) if bounding_polygon is not None: polygon = polygon.intersection(bounding_polygon) log.info("After AND-ing, the area is: %0.2g", polygon.area)
geoms = json.load(data_file) except Exception as e: print(colored("Error in loading evaluation geometries, please check if it is a valid JSON.", "red")) sys.exit(0) allf = iter_evals(geoms['features']) # iterate over the geometry features. for curFeature in allf: shp = 0 featureArea=0 try: # convert the JSON feature in to Shape using Shapely's asShape. shp = asShape(curFeature['geometry']) except Exception as e: # if there is a error in conversion go to the next shape. print(explain_validity(shp)) pass try: assert shp != 0 # get the bounds of the shape bounds = shp.bounds # generate the area of the shape featureArea = myShapesHelper.generateShapeArea(curFeature, units) # generate a random id for the shape fid = random.randint(1, 900000000) # check the areatype areatype = curFeature['properties']['areatype'] if areatype in evalfeatcollection.keys(): # input the shape and details in the collections evalfeatcollection[areatype].append({'id':fid,'shape':shp, 'bounds':bounds,'areatype':areatype,'area':featureArea, 'allocated':False}) # insert the bounds and id into the rtree, the id is used to get the shape later.
def _locate(segmented, offset=None): """Inspired from: https://goo.gl/HYPrR1""" # CV_RETR_EXTERNAL to only get external contours. _, contours, hierarchy = cv2.findContours(segmented.copy(), cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE) # Note: points are represented as (col, row)-tuples apparently transform = identity if offset is not None: col_off, row_off = offset transform = lambda p: affine_transform(p, [1, 0, 0, 1, col_off, row_off]) components = [] if len(contours) > 0: top_index = 0 tops_remaining = True while tops_remaining: exterior = contours[top_index][:, 0, :].tolist() interiors = [] # check if there are childs and process if necessary if hierarchy[0][top_index][2] != -1: sub_index = hierarchy[0][top_index][2] subs_remaining = True while subs_remaining: interiors.append(contours[sub_index][:, 0, :].tolist()) # check if there is another sub contour if hierarchy[0][sub_index][0] != -1: sub_index = hierarchy[0][sub_index][0] else: subs_remaining = False # add component tuple to components only if exterior is a polygon if len(exterior) == 1: components.append(Point(exterior[0])) elif len(exterior) == 2: components.append(LineString(exterior)) elif len(exterior) > 2: polygon = Polygon(exterior, interiors) polygon = transform(polygon) if polygon.is_valid: # some polygons might be invalid components.append(polygon) else: fixed = fix_geometry(polygon) if fixed.is_valid and not fixed.is_empty: components.append(fixed) else: warn("Attempted to fix invalidity '{}' in polygon but failed... " "Output polygon still invalid '{}'".format(explain_validity(polygon), explain_validity(fixed))) # check if there is another top contour if hierarchy[0][top_index][0] != -1: top_index = hierarchy[0][top_index][0] else: tops_remaining = False del contours del hierarchy return components
def test_valid(self): self.assertEqual(explain_validity(Point(0, 0)), 'Valid Geometry')
def test_valid(self): self.failUnlessEqual(explain_validity(Point(0, 0)), 'Valid Geometry')
def validate_geometry(geometry: BaseGeometry): if not isinstance(geometry, BaseGeometry): raise ValidationError('GeometryField expected a Shapely BaseGeometry child-class.') if not geometry.is_valid: raise ValidationError('Invalid geometry: %s' % validation.explain_validity(geometry))
def repair_shapefile(self): try: shpDriver = ogr.GetDriverByName('ESRI Shapefile') print("Drivers are ready") shpdata = self.shapefilePath output = os.path.dirname(shpdata) + os.sep + "rep_" + os.path.basename(shpdata)[4:] if os.path.exists(output): shpDriver.DeleteDataSource(output) ds = shpDriver.CreateDataSource(output) shpSource = shpDriver.Open(shpdata, 0) if shpSource is None: print('Could not open ' + shpdata) sys.exit(1) #exit with an error code layer = shpSource.GetLayer() numFeatures = layer.GetFeatureCount() print('Feature count:' + str(numFeatures)) inputSR = layer.GetSpatialRef() shplayer = ds.CreateLayer(output, inputSR, ogr.wkbPolygon) inFeature = layer.GetNextFeature() for index in range(0, inFeature.GetFieldCount()): #fieldList.append(inFeature.GetFieldDefnRef(index)) shplayer.CreateField(inFeature.GetFieldDefnRef(index)) print('Created field: ' + inFeature.GetFieldDefnRef(index).name) cnt = 0 while inFeature: cnt = cnt + 1 f = ogr.Feature(shplayer.GetLayerDefn()) for index in range(0, f.GetFieldCount()): try: attValue = inFeature.GetField(inFeature.GetFieldDefnRef(index).name) if attValue: f.SetField(f.GetFieldDefnRef(index).name, inFeature.GetField(inFeature.GetFieldDefnRef(index).name)) except: print(inFeature.GetFieldDefnRef(index).name + ' - skipping attribute value adding null') try: geom = loads(inFeature.GetGeometryRef().ExportToWkb()) if geom.is_valid: newGeom = inFeature.geometry() else: print("Bad geom: " + str(inFeature.GetFID())) print(explain_validity(geom)) cleanGeom = geom.buffer(0.0) if cleanGeom.is_valid: print("Geometry is clean") wkbGeom = dumps(cleanGeom) newGeom = ogr.CreateGeometryFromWkb(wkbGeom) else: newGeom = inFeature.geometry() f.SetGeometry(newGeom) if shplayer.CreateFeature(f) != 0: print("Failed to create feature in shapefile.\n") sys.exit(1) f.Destroy() except: print('Skipping null geometry') config.run_error_message('repair geom feature failure null geom..') break inFeature = layer.GetNextFeature() print('Feature count from loop:' + str(cnt)) ds.Destroy() except: config.run_error_message("Repair failure")
coords.append([float(xy[0]), float(xy[1])]) if float(xy[0]) == 180: found_anti_meridian = True lr = LinearRing(coords) if not lr.is_ccw: print 'Reversing non-CCW ring' r = list(reversed(ls)) del(ls[:]) ls.extend(r) polygon = Polygon(lr) # The invalid polygon is the case of Semisopochnoi Island, which # zone crosses the antimeridian. if not polygon.is_valid: print 'POLYGON IS NOT VALID! : %d' % len(ls) explain_validity(polygon) if found_anti_meridian: print 'Polygon spans anti-meridian' # To deal with this case, we'll split the zone into two pieces, # one of which is in the eastern hemisphere and one in the # western hemisphere. This is purely a tooling issue to make # the zone easier to manage with other software. xy = ls[0].split(',') new_piece = [] begin_anti_meridian = -1 end_anti_meridian = -1 for i in range(0, len(ls)): xy = ls[i].split(',') if float(xy[0]) == 180: # Note: the '-' is to reverse the sign so shapely sees # the coordinates correctly.
# -*- coding: utf-8 -*- import os from shapely.geometry import Polygon coords =[(0,0),(0,2),(1,1),(2,2),(2,0),(1,1),(0,0)] p = Polygon(coords) from shapely.validation import explain_validity explain_validity(p) def Test(): assert True