def process_centreline(geom): try: line = Centerline(geom) except centerline.exceptions.TooFewRidgesError: line = Centerline(geom, interpolation_distance=0.1) line = remove_short_lines(linemerge(line)) return line.simplify(1, preserve_topology=True)
def test_creating_centerline_using_too_large_interp_dist_raises_runtimeerror( create_polygon): polygon = create_polygon(exterior=[[0, 0], [10, 0], [10, 10], [0, 10]]) with pytest.raises(TooFewRidgesError): Centerline(polygon, 10) centerline = Centerline(polygon, 5) assert isinstance(centerline, Centerline) assert isinstance(centerline, geometry.MultiLineString)
def test_centerline_has_attributes_assigned_to_it(simple_polygon): ATTRIBUTES = {"id": 1, "name": "polygon", "valid": True} centerline = Centerline(simple_polygon, **ATTRIBUTES) assert isinstance(centerline, Centerline) assert isinstance(centerline, geometry.MultiLineString) assert centerline.id == ATTRIBUTES.get("id") assert centerline.name == ATTRIBUTES.get("name") assert centerline.valid == ATTRIBUTES.get("valid")
def gen_centerlines(df, interpolation_distance=0.5): """ """ centerlines = [] for i, row in df.iterrows(): cl = Centerline(row.geometry, interpolation_distance=interpolation_distance) centerlines.append(cl) return centerlines
def extract_centerlines(shapes): shapes = (shape.buffer(0) for shape in shapes) polys = [ poly for poly in shapes if type(poly) == geom.Polygon and type(poly.envelope) == geom.Polygon ] centerlines = [Centerline(p, valid=True) for p in polys] center_geoms = [line.geoms for line in centerlines] center_geom_lines = [geom.MultiLineString(line) for line in center_geoms] center_geom_lines = [ops.linemerge(line) for line in center_geom_lines] return center_geom_lines
def extract_centerlines(edges): ''' Extract centerlines using Centerline python library ''' shapes = vector_trace(edges) shapes = (shape.buffer(0) for shape in shapes) polys = [ poly for poly in shapes if type(poly) == geom.Polygon and type(poly.envelope) == geom.Polygon ] centerlines = [Centerline(p, valid=True) for p in polys] center_geoms = [line.geoms for line in centerlines] center_geom_lines = [geom.MultiLineString(line) for line in center_geoms] center_geom_lines = [ops.linemerge(line) for line in center_geom_lines] center_geom_lines = explode_multilines(center_geom_lines) # prune lines too long/short center_geom_lines = [ line for line in center_geom_lines if max(line.length, geom.Point(line.coords[0]).distance(geom.Point( line.coords[-1]))) > 4 ] return center_geom_lines
def test_centerline_geometry_is_valid(complex_polygon): centerline = Centerline(complex_polygon) assert centerline.is_valid is True
def test_centerline_has_length_greater_than_zero(complex_polygon): centerline = Centerline(complex_polygon) assert centerline.length > 0
def test_creating_centerline_from_geometry_collection_raises_typeerror( geometry_collection): with pytest.raises(InvalidInputTypeError): Centerline(geometry_collection)
def test_creating_centerline_from_multipolygon_returns_centerline( multipolygon): centerline = Centerline(multipolygon) assert isinstance(centerline, Centerline) assert isinstance(centerline, geometry.MultiLineString)
def test_polygon_contains_the_centerline(complex_polygon): centerline = Centerline(complex_polygon) assert complex_polygon.contains(centerline) is True
def test_centerline_does_not_touch_the_polygons_boundary(complex_polygon): centerline = Centerline(complex_polygon) assert centerline.intersects(complex_polygon.boundary) is False
with open('/input/features.geojson', 'r') as f: features = json.load(f)['features'] with open('input/border_density', 'r') as f: border_density = float(f.read()) output_file_path = '/output/centerlines.geojson' centerlines = [] i = 0 print("Total " + str(len(features)) + " Features") for feature in features: s = shape(feature["geometry"]) centerline = Centerline(s, border_density) f = {"type": "Feature", "properties": {}, "geometry": mapping(centerline)} centerlines.append(f) i = i + 1 print("Done " + str(i)) gjson = { "type": "FeatureCollection", "name": "input-clean", "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::4326" } }, "features": centerlines
def test_qhull_error(create_polygon): # https://github.com/fitodic/centerline/issues/24 polygon = create_polygon(exterior=[ (107, 189), (106, 190), (100, 190), (99, 191), (95, 191), (94, 192), (92, 192), (91, 193), (90, 193), (89, 194), (88, 194), (87, 195), (86, 195), (85, 196), (84, 196), (82, 198), (82, 203), (86, 207), (87, 207), (90, 210), (91, 210), (92, 211), (94, 211), (95, 212), (98, 212), (99, 213), (101, 213), (102, 214), (105, 214), (106, 215), (108, 215), (109, 216), (111, 216), (112, 217), (114, 217), (115, 218), (117, 218), (118, 219), (120, 219), (121, 220), (126, 220), (127, 221), (133, 221), (134, 222), (136, 222), (137, 223), (138, 223), (139, 224), (139, 227), (137, 229), (137, 230), (136, 231), (135, 231), (133, 233), (132, 233), (130, 235), (129, 235), (126, 238), (125, 238), (124, 239), (123, 239), (122, 240), (121, 240), (120, 241), (119, 241), (118, 242), (116, 242), (115, 243), (114, 243), (113, 244), (112, 244), (110, 246), (109, 246), (108, 247), (107, 247), (106, 248), (105, 248), (104, 249), (102, 249), (101, 250), (99, 250), (98, 251), (97, 251), (96, 252), (94, 252), (92, 254), (91, 254), (90, 255), (89, 255), (88, 256), (87, 256), (86, 257), (85, 257), (84, 258), (83, 258), (82, 259), (80, 259), (79, 260), (78, 260), (70, 268), (70, 269), (69, 270), (69, 272), (68, 273), (68, 276), (71, 279), (73, 279), (74, 280), (83, 280), (84, 279), (90, 279), (91, 278), (94, 278), (95, 277), (97, 277), (98, 276), (99, 276), (100, 275), (101, 275), (103, 273), (105, 273), (106, 272), (108, 272), (109, 271), (111, 271), (112, 270), (113, 270), (114, 269), (115, 269), (116, 268), (117, 268), (120, 265), (121, 265), (122, 264), (124, 264), (125, 263), (126, 263), (127, 262), (128, 262), (129, 261), (130, 261), (132, 259), (133, 259), (135, 257), (136, 257), (137, 256), (138, 256), (139, 255), (141, 255), (142, 254), (143, 254), (145, 252), (146, 252), (149, 249), (150, 249), (153, 246), (154, 246), (158, 242), (158, 241), (164, 235), (164, 234), (165, 233), (165, 232), (166, 231), (166, 230), (167, 229), (167, 227), (168, 226), (168, 223), (169, 222), (169, 220), (170, 219), (170, 212), (169, 211), (169, 210), (168, 209), (168, 208), (167, 207), (167, 206), (162, 201), (161, 201), (159, 199), (158, 199), (157, 198), (155, 198), (154, 197), (153, 197), (151, 195), (150, 195), (149, 194), (147, 194), (146, 193), (144, 193), (143, 192), (139, 192), (138, 191), (132, 191), (131, 190), (122, 190), (121, 189), ]) attributes = {"id": 1, "name": "polygon", "valid": True} centerline = Centerline(polygon, **attributes) assert isinstance(centerline, Centerline) assert isinstance(centerline, geometry.MultiLineString)
def main(): #-- Read the system arguments listed after the program long_options=['DIR=','FILTER=','CLOBBER'] optlist,arglist = getopt.getopt(sys.argv[1:],'D:F:C',long_options) #-- Set default settings subdir = 'atrous_32init_drop0.2_customLossR727.dir' FILTER = 0. flt_str = '' clobber = False for opt, arg in optlist: if opt in ("-D","--DIR"): subdir = arg elif opt in ("-F","--FILTER"): if arg not in ['NONE','none','None','N','n',0]: FILTER = float(arg) flt_str = '_%.1fkm'%(FILTER/1000) elif opt in ("-C","--CLOBBER"): clobber = True #-- Get list of files pred_dir = os.path.join(ddir,'stitched.dir',subdir) fileList = os.listdir(pred_dir) pred_list = [f for f in fileList if (f.endswith('.tif') and ('mask' not in f))] #-- output directory output_dir = os.path.join(pred_dir,'shapefiles.dir') #-- make directories if they don't exist if not os.path.exists(output_dir): os.mkdir(output_dir) #-- if CLOBBBER is False, we are not overwriting old files, so remove exisiting files from list if not clobber: print('Removing exisitng files.') existingList = os.listdir(output_dir) existing = [f for f in existingList if (f.endswith('.shp') and ('ERR' not in f) and f.startswith('gl_'))] rem_list = [] for p in pred_list: if p.replace('.tif','%s.shp'%flt_str) in existing: #-- save index for removing at the end rem_list.append(p) for p in rem_list: print('Ignoring %s.'%p) pred_list.remove(p) # pred_list = ['gl_069_181218-181224-181224-181230_014095-025166-025166-014270_T110614_T110655.tif'] # pred_list = ['gl_007_180518-180524-180530-180605_021954-011058-022129-011233_T050854_T050855.tif'] print('# of files: ', len(pred_list)) #-- threshold for getting contours and centerlines eps = 0.3 #-- loop through prediction files #-- get contours and save each as a line in shapefile #-- also save training label as line for f in pred_list: #-- read file raster = rasterio.open(os.path.join(pred_dir,f),'r') im = raster.read(1) #-- get transformation matrix trans = raster.transform #-- also read the corresponding mask file mask_file = os.path.join(pred_dir,f.replace('.tif','_mask.tif')) print(mask_file) mask_raster = rasterio.open(mask_file,'r') mask = mask_raster.read(1) mask_raster.close() #-- get contours of prediction #-- close contour ends to make polygons im[np.nonzero(im[:,0] > eps),0] = eps im[np.nonzero(im[:,-1] > eps),-1] = eps im[0,np.nonzero(im[0,:] > eps)] = eps im[-1,np.nonzero(im[-1,:] > eps)] = eps contours = skimage.measure.find_contours(im, eps) #-- make contours into closed polyons to find pinning points #-- also apply noise filter and append to noise list x = {} y = {} noise = [] pols = [None]*len(contours) pol_type = [None]*len(contours) for n,contour in enumerate(contours): #-- convert to coordinates x[n],y[n] = rasterio.transform.xy(trans, contour[:,0], contour[:,1]) pols[n] = Polygon(zip(x[n],y[n])) #-- get elements of mask the contour is on submask = mask[np.round(contour[:, 0]).astype('int'), np.round(contour[:, 1]).astype('int')] #-- if more than half of the elements are from test tile, count contour as test type if np.count_nonzero(submask) > submask.size/2.: pol_type[n] = 'Test' else: pol_type[n] = 'Train' #-- Loop through all the polygons and taking any overlapping areas out #-- of the enclosing polygon and ignore the inside polygon ignore_list = [] for i in range(len(pols)): for j in range(len(pols)): if (i != j) and pols[i].contains(pols[j]): pols[i] = pols[i].difference(pols[j]) ignore_list.append(j) #-- loop through and apply noise filter for n in range(len(contours)): #-- apply filter if (n not in ignore_list) and (len(x[n]) < 2 or LineString(zip(x[n],y[n])).length <= FILTER): noise.append(n) #-- loop through remaining polygons and determine which ones are #-- pinning points based on the width and length of the bounding box pin_list = [] box_ll = [None]*len(contours) box_ww = [None]*len(contours) for n in range(len(contours)): box_ll[n] = pols[n].length box_ww[n] = pols[n].area/box_ll[n] if (n not in noise) and (n not in ignore_list): #-- make bounding box # box = pols[n].minimum_rotated_rectangle # bx,by = box.exterior.coords.xy # #-- get the dimensions of the sides of the box # edge_length = (Point(bx[0],by[0]).distance(Point(bx[1],by[1])), Point(bx[1],by[1]).distance(Point(bx[2],by[2]))) #-- length is the larger dimension # box_ll = max(edge_length) # #-- width is the smaller dimension # box_ww = min(edge_length) #-- if the with is larger than 1/4 of the length, it's a pinning point if box_ww[n] > box_ll[n]/25: pin_list.append(n) #-- find overlap between ignore list nad noise list if len(list(set(noise) & set(ignore_list))) != 0: sys.exit('Overlap not empty: ', list(set(noise) & set(ignore_list))) #-- initialize list of contour linestrings er = [None]*len(contours) cn = [] #[None]*(len(contours)-len(ignore_list)-len(noise)) n = 0 # total center line counter pc = 1 # pinning point counter lc = 1 # line counter er_type = [None]*len(er) cn_type = [] #[None]*len(cn) er_class = [None]*len(er) cn_class = [] #[None]*len(cn) er_lbl = [None]*len(er) cn_lbl = [] #[None]*len(cn) #-- loop through polygons, get centerlines, and save for idx,p in enumerate(pols): er[idx] = [list(a) for a in zip(x[idx],y[idx])] er_type[idx] = pol_type[idx] if idx in noise: er_class[idx] = 'Noise' elif idx in ignore_list: er_class[idx] = 'Inner Contour' else: if idx in pin_list: #-- pinning point. Just get perimeter of polygon xc,yc = pols[idx].exterior.coords.xy cn.append([[list(a) for a in zip(xc,yc)]]) cn_class.append(['Pinning Point']) cn_type.append([pol_type[idx]]) #-- set label cn_lbl.append(['pin%i'%pc]) pc += 1 #- incremenet pinning point counter else: #-- get centerlines attributes = {"id": idx, "name": "polygon", "valid": True} #-- loop over interpolation distances until we can get a single line dis = pols[idx].length/400 #100 try: cl = Centerline(p,interpolation_distance=dis, **attributes) except: print('not enough ridges. Skip') continue else: #-- merge all the lines merged_lines = linemerge(cl) if merged_lines.geom_type == 'LineString': #-- save coordinates of linestring xc,yc = merged_lines.coords.xy cn.append([[list(a) for a in zip(xc,yc)]]) cn_class.append(['Grounding Line']) cn_lbl.append(['line%i'%lc]) cn_type.append([pol_type[idx]]) er_class[idx] = 'GL Uncertainty' #-- set label er_lbl[idx] = 'err%i'%lc lc += 1 #- incremenet line counter else: nml = len(merged_lines) #-- for lines with many bifurcations, the average segment is #-- about 300m, so if # of segments is length/300 or more, ignore. if nml < pols[idx].length/300: coord_list = [] for nn in range(nml): xc,yc = merged_lines[nn].coords.xy coord_list.append([list(a) for a in zip(xc,yc)]) cn.append(coord_list) cn_class.append(['Grounding Line']*nml) cn_lbl.append(['line%i'%lc]*nml) cn_type.append([pol_type[idx]]*nml) er_class[idx] = 'GL Uncertainty' #-- set label er_lbl[idx] = 'err%i'%lc lc += 1 #- incremenet line counter #-- save all linestrings to file #-- make separate files for centerlines and errors # 1) GL file gl_file = os.path.join(output_dir,f.replace('.tif','%s.shp'%flt_str)) w = shapefile.Writer(gl_file) w.field('ID', 'C') w.field('Type','C') w.field('Class','C') #-- loop over contour centerlines for n in range(len(cn)): for nn in range(len(cn[n])): w.line([cn[n][nn]]) w.record(cn_lbl[n][nn], cn_type[n][nn], cn_class[n][nn]) w.close() # create the .prj file prj = open(gl_file.replace('.shp','.prj'), "w") prj.write(raster.crs.to_wkt()) prj.close() # 2) Err File er_file = os.path.join(output_dir,f.replace('.tif','%s_ERR.shp'%flt_str)) w = shapefile.Writer(er_file) w.field('ID', 'C') w.field('Type','C') w.field('Class','C') w.field('Length','C') w.field('Width','C') #-- loop over contours and write them for n in range(len(er)): w.line([er[n]]) w.record(er_lbl[n] , er_type[n], er_class[n], box_ll[n], box_ww[n]) w.close() # create the .prj file prj = open(er_file.replace('.shp','.prj'), "w") prj.write(raster.crs.to_wkt()) prj.close() #-- close input file raster.close()
def test_centerline_is_not_empty(complex_polygon): centerline = Centerline(complex_polygon) assert centerline.is_empty is False
def test_centerline_is_simple(complex_polygon): centerline = Centerline(complex_polygon) assert centerline.is_simple is True
def test_creating_centerline_from_point_raises_typeerror(point): with pytest.raises(InvalidInputTypeError): Centerline(point)
import cv2 import numpy as np import matplotlib.pyplot as plt import gdal from shapely.geometry import Polygon from centerline.geometry import Centerline import math polygon = Polygon([[0, 0], [0, 4], [4, 4], [4, 0]]) attributes = {"id": 1, "name": "polygon", "valid": True} centerline = Centerline(polygon, **attributes) print(centerline.id == 1) x = math.log2(3, 2) """ def nothing(x): pass img_file = r'I:\DVRPC\Fill_gap\StreetView\images\kXg-PRDBdAix6L11lAtONA_-75.675715_40.050323_0_74.77_landcover.png' cv2.namedWindow('image') img = cv2.imread(img_file) cv2.namedWindow('image') cv2.createTrackbar('Er/Di', 'image', 0, 1, nothing) # 创建腐蚀或膨胀选择滚动条,只有两个值 cv2.createTrackbar('size', 'image', 0, 21, nothing)
def test_creating_centerline_from_multilinestring_raises_typeerror( multilinestring): with pytest.raises(InvalidInputTypeError): Centerline(multilinestring)