def process_layer(orig_layer,output_name,tolerance=0.0, create_polygons=False,close_arc=False, single_feature=True, remove_duplicates=True): """ remove_duplicates: if true, exactly duplicated nodes along a single path will be removed, i.e. the linestring A-B-B-C will become A-B-C. single_feature: only save the biggest feature """ ### The actual geometry processing: ### ### <processing> new_features = merge_lines(orig_layer) if remove_duplicates: print "Checking the merged features for duplicate points" # possibly important here to have the duplicate test more stringent than # the tolerant_merge_lines. # also have to be careful about a string of points closely spaced - don't # want to remove all of them, just enough to keep the minimal spacing above # tolerance. short_tol = 0.5*tolerance for fi in range(len(new_features)): pnts = new_features[fi] valid = ones( len(pnts), 'bool8') # go with a slower but safer loop here - last_valid=0 for i in range(1,len(pnts)): if vector_mag( pnts[last_valid]-pnts[i] ) < short_tol: if i==len(pnts)-1: # special case to avoid moving the last vertex valid[last_valid] = False last_valid = i else: valid[i] = False else: last_valid = i # print "Ring %d: # invalid=%d / %d"%(i,sum(~valid),len(new_features[i])) new_features[fi] = new_features[fi][valid,:] if tolerance > 0.0: new_features = tolerant_merge_lines(new_features,tolerance) ### </processing> ### <output> if create_polygons: geoms = lines_to_polygons(new_features,close_arc=close_arc,single_feature=single_feature) else: # Line output geoms = [shapely.geometry.LineString(pnts) for pnts in new_features] # Write it all out to a shapefile: progress_message("Writing output") wkb2shp.wkb2shp(output_name,geoms, overwrite=True) return output_name
def clean_degenerate_rings(point_lists,degen_shpname='degenerate_rings.shp'): """ Given a list of lists of points - filter out point lists which represent degenerate rings, writing the invalid rings to a shapefile degen_shpname, and returning a list of only the valid rings. Unclosed linestrings are passed through. set degen_shpname to None to disable that output. """ degen_lines = [] valid_lists = [] for i in range(len(point_lists)): point_list = point_lists[i] if all(point_list[0]!=point_list[-1]): valid_lists.append(point_list) else: # closed - check it's area poly = shapely.geometry.Polygon(point_list) try: a = poly.area valid_lists.append(point_list) except ValueError: print "degenerate feature",i# ,point_list degen_line = shapely.geometry.LineString(point_list) degen_lines.append(degen_line) if degen_shpname is not None and len(degen_lines)>0: wkb2shp.wkb2shp(degen_shpname,degen_lines,srs_text='EPSG:26910',overwrite=True) return valid_lists
def clean_degenerate_rings(point_lists, degen_shpname='degenerate_rings.shp'): """ Given a list of lists of points - filter out point lists which represent degenerate rings, writing the invalid rings to a shapefile degen_shpname, and returning a list of only the valid rings. Unclosed linestrings are passed through. set degen_shpname to None to disable that output. """ degen_lines = [] valid_lists = [] for i in range(len(point_lists)): point_list = point_lists[i] if all(point_list[0] != point_list[-1]): valid_lists.append(point_list) else: # closed - check it's area poly = shapely.geometry.Polygon(point_list) try: a = poly.area valid_lists.append(point_list) except ValueError: print "degenerate feature", i # ,point_list degen_line = shapely.geometry.LineString(point_list) degen_lines.append(degen_line) if degen_shpname is not None and len(degen_lines) > 0: wkb2shp.wkb2shp(degen_shpname, degen_lines, srs_text='EPSG:26910', overwrite=True) return valid_lists
# make an island by masking a few triangles island = ((mt.x[mt.triangles].mean(axis=1) - 0.75)**2 + (mt.y[mt.triangles].mean(axis=1) - 0.75)**2) < 0.03 mt.set_mask(island) import pylab pylab.figure() contour = tri.tricontourf(pylab.gca(), mt, point_vals) pylab.axis('equal') from shapely import geometry import wkb2shp geoms = [] vals = [] # tuples of vmin,vmax for colli, coll in enumerate(contour.collections): vmin, vmax = contour.levels[colli:colli + 2] for p in coll.get_paths(): p.simplify_threshold = 0.0 polys = p.to_polygons() geoms.append(geometry.Polygon(polys[0], polys[1:])) vals.append((vmin, vmax)) wkb2shp.wkb2shp("contour-output.shp", geoms, overwrite=True, fields=array(vals, dtype=[('min', float64), ('max', float64)]))
# could also do a transect, but we'd have to pick a specific line up of the stations import wkb2shp from shapely import geometry pc = PolarisCruise() stations = pc.station_locs_utm() geoms = [ geometry.Point(stations[i, 0], stations[i, 1]) for i in range(len(stations)) ] i_iter = iter(range(len(stations))) def field_gen(g): i = i_iter.next() s = pc.station_data[i] return { 'name': s['name'], 'station_no': s['station_no'], 'depth': s['depth'], 'source': 'polaris_cruise' } wkb2shp.wkb2shp( '/home/rusty/classes/research/suntans/runs/polaris_points.shp', geoms, field_gen=field_gen, overwrite=True)
import pylab pylab.figure() contour=tri.tricontourf(pylab.gca(),mt , point_vals) pylab.axis('equal') from shapely import geometry import wkb2shp geoms = [] vals = [] # tuples of vmin,vmax for colli,coll in enumerate(contour.collections): vmin,vmax = contour.levels[colli:colli+2] for p in coll.get_paths(): p.simplify_threshold = 0.0 polys = p.to_polygons() geoms.append( geometry.Polygon(polys[0],polys[1:] ) ) vals.append( (vmin,vmax) ) wkb2shp.wkb2shp("contour-output.shp", geoms, overwrite=True, fields =array( vals, dtype=[('min',float64), ('max',float64)] ))
class Tom(object): scale_shps = None tele_scale_shps = None # NB: this is adjusted by scale_factor before being handed to ApolloniusGraph effective_tele_rate = 1.1 boundary_shp = None plot_interval = None checkpoint_interval = None smooth = 1 resume_checkpoint_fn = None verbosity = 1 dry_run = 0 optimize = None interior_shps = None output_shp = None slide_interior = 1 scale_factor = 1.0 scale_ratio_for_cutoff = 1.0 # These are not currently mutable from the command line # but could be. checkpoint_fn = "checkpoint.pav" plot_fn = "partial-grid.pdf" boundary_poly_shp = "processed-boundary.shp" smoothed_poly_shp = "smoothed-shoreline.shp" linestring_join_tolerance = 1.0 scale_shp_field_name = 'scale' density_map = None # non-customizable instance variables original_boundary_geo = None def __init__(self): self.scale_shps = [] self.tele_scale_shps = [] def usage(self): print "tom.py -h # show this help message " print " -b boundary.shp # boundary shapefile " print " -i interior.shp # interior paving guides " print " --slide-interior # Allow nodes on interior lines to slide [default]" print " --rigid-interior # Force nodes on interior lines to stay put" print " -s scale.shp # scale shapefile " if field.has_apollonius: print " -a telescoping_scale.shp # auto telescoping scale shapefile" print " -t N.NN # telescoping rate - defaults to 1.1" else: print " [DISABLED] -a telescoping_scale.shp" print " -f N.NN # factor for adjusting scale globally" print " -C N.NN # smoothing: min number of cells across a channel" print " -p N # output interval for plots " print " -c N # checkpoint interval " print " -d # disable smoothing " print " -o # enable optimization " print " -r checkpoint.pav # resume from a checkpoint " print " -v N # set verbosity level N" print " -n # ready the shoreline, but don't mesh it" print " -m x1,y1,x2,y2,dx,dy # output raster of scale field" print " -g output.shp # output shapefile of grid" print " boundary.shp: " print " A shapefile containing either lines or a single polygon. If " print " lines, they must join together within a tolerance of 1.0 units" print " scale.shp:" print " A shapefile containing points with a field 'scale', which gives" print " the desired length of the edges in a region." print " If the CGAL-python library is available, multiple shapefiles can " print " be specified, including LineString layers." print " interior.shp:" print " A shapefile containg line segments which will be used to guide the orientation of cells" print " output interval N: " print " Every N steps of the algorithm create a PDF of the grid so far" print " checkpoint interval N:" print " Every N steps of the algorithm make a backup of the grid and all" print " intermediate information" print " resume from checkpoint" print " Loads a previously saved checkpoint file and will continue where it" print " left off." print " verbosity level N:" print " Defaults to 1, which prints step numbers." print " 0: almost silent" print " 1: ~1 line per step" print " 2: ~30 lines per step" print " 3: ~100 lines per step and will try to plot intermediate stages" print " raster of scale field: the given region will be rasterized and output to scale-raster.tif" def run(self, argv): try: opts, rest = getopt.getopt(argv[1:], 'hb:s:a:t:i:c:r:dv:np:om:i:f:g:C:', ['slide-interior', 'rigid-interior']) except getopt.GetoptError, e: print e print "-" * 80 self.usage() exit(1) for opt, val in opts: if opt == '-h': self.usage() exit(1) elif opt == '-s': self.scale_shps.append(val) elif opt == '-a': self.tele_scale_shps.append(val) elif opt == '-t': self.effective_tele_rate = float(val) elif opt == '-f': self.scale_factor = float(val) elif opt == '-b': self.boundary_shp = val elif opt == '-p': self.plot_interval = int(val) elif opt == '-c': self.checkpoint_interval = int(val) elif opt == '-C': self.scale_ratio_for_cutoff = float(val) elif opt == '-r': self.resume_checkpoint_fn = val elif opt == '-d': self.smooth = 0 elif opt == '-v': self.verbosity = int(val) elif opt == '-n': self.dry_run = 1 elif opt == '-o': self.optimize = 1 elif opt == '-m': self.density_map = val elif opt == '-i': if not self.interior_shps: self.interior_shps = [] self.interior_shps.append(val) elif opt == '-g': self.output_shp = val elif opt == '--slide-interior': self.slide_interior = 1 elif opt == '--rigid-interior': self.slide_interior = 0 self.check_parameters() log_fp = open('tom.log', 'wt') log_fp.write("TOM log:\n") log_fp.write(" ".join(argv)) log_fp.close() if not self.resume_checkpoint_fn: bound_args = self.prepare_boundary() density_args = self.prepare_density() args = {} args.update(bound_args) args.update(density_args) args['slide_internal_guides'] = self.slide_interior # Wait until after smoothing to add degenerate interior lines # args.update(self.prepare_interiors()) self.p = paver.Paving(**args) self.p.verbose = self.verbosity self.p.scale_ratio_for_cutoff = self.scale_ratio_for_cutoff if self.smooth: self.p.smooth() # and write out the smoothed shoreline wkb2shp.wkb2shp(self.smoothed_poly_shp, [self.p.poly], overwrite=True) int_args = self.prepare_interiors() if int_args.has_key('degenerates'): for degen in int_args['degenerates']: self.p.clip_and_add_degenerate_ring(degen) else: self.p = paver.Paving.load_complete(self.resume_checkpoint_fn) self.p.verbose = self.verbosity if self.dry_run: print "dry run..." elif self.density_map: f = self.p.density x1, y1, x2, y2, dx, dy = map(float, self.density_map.split(',')) bounds = np.array([[x1, y1], [x2, y2]]) rasterized = f.to_grid(dx=dx, dy=dy, bounds=bounds) rasterized.write_gdal("scale-raster.tif") else: starting_step = self.p.step self.create_grid() if (not os.path.exists('final.pav') ) or self.p.step > starting_step: self.p.write_complete('final.pav') if (not os.path.exists('final.pdf') ) or self.p.step > starting_step: self.plot_intermediate(fn='final.pdf', color_by_step=False) # write grid as shapefile if self.output_shp: print "Writing shapefile with %d features (edgse)" % ( self.p.Nedges()) self.p.write_shp(self.output_shp, only_boundaries=0, overwrite=1) # by reading the suntans grid output back in, we should get boundary edges # marked as 1 - self.p probably doesn't have these markers g = trigrid.TriGrid(suntans_path='.') g.write_shp('trigrid_write.shp', only_boundaries=0, overwrite=1) if self.optimize: self.run_optimization() self.p.write_complete('post-optimize.pav') self.plot_intermediate(fn='post-optimize.pdf')
def process_layer(orig_layer, output_name, tolerance=0.0, create_polygons=False, close_arc=False, single_feature=True, remove_duplicates=True): """ remove_duplicates: if true, exactly duplicated nodes along a single path will be removed, i.e. the linestring A-B-B-C will become A-B-C. single_feature: only save the biggest feature """ ### The actual geometry processing: ### ### <processing> new_features = merge_lines(orig_layer) if remove_duplicates: print "Checking the merged features for duplicate points" # possibly important here to have the duplicate test more stringent than # the tolerant_merge_lines. # also have to be careful about a string of points closely spaced - don't # want to remove all of them, just enough to keep the minimal spacing above # tolerance. short_tol = 0.5 * tolerance for fi in range(len(new_features)): pnts = new_features[fi] valid = ones(len(pnts), 'bool8') # go with a slower but safer loop here - last_valid = 0 for i in range(1, len(pnts)): if vector_mag(pnts[last_valid] - pnts[i]) < short_tol: if i == len(pnts) - 1: # special case to avoid moving the last vertex valid[last_valid] = False last_valid = i else: valid[i] = False else: last_valid = i # print "Ring %d: # invalid=%d / %d"%(i,sum(~valid),len(new_features[i])) new_features[fi] = new_features[fi][valid, :] if tolerance > 0.0: new_features = tolerant_merge_lines(new_features, tolerance) ### </processing> ### <output> if create_polygons: geoms = lines_to_polygons(new_features, close_arc=close_arc, single_feature=single_feature) else: # Line output geoms = [shapely.geometry.LineString(pnts) for pnts in new_features] # Write it all out to a shapefile: progress_message("Writing output") wkb2shp.wkb2shp(output_name, geoms, overwrite=True) return output_name