def getShp(path_glob, ini, end, r_key=4, r_data=5): dShapes = {} for _shp in iglob(path_glob, recursive=True): logging.info(_shp) #_shp = os.path.realpath(_shp) with shapefile.Reader(_shp) as shp: for sr in shp.shapeRecords(): if sr.shape.points and sr.record and len(sr.record) > 4: natcode = sr.record[r_key] key = natcode[ini:end] if key.isdigit(): vals = dShapes.get(key, []) poli = shape(sr.shape) if isinstance(poli, Polygon): poli = MultiPolygon([poli]) vals.append((poli, sr.record[r_data])) dShapes[key] = vals for key, vals in list(dShapes.items()): nombre = set() poli = [] for p, n in vals: poli.append(p) nombre.add(n) if len(nombre) > 1: logging.info("Clave %s con varios nombres: %s", key, nombre) nombre = nombre.pop() main = None for ps in poli: for p in ps: if main is None or main.area < p.area: main = p if len(poli) == 1: poli = poli[0] else: poli = cascaded_union(poli) dShapes[key] = (poli, nombre) dShapes = {k: v for k, v in sorted(dShapes.items(), key=lambda kv: kv[0])} return dShapes
def calc_gz(GL_FILE='', BASIN_FILE='', VEL_FILE='', region='', dist=0, N=0): #-- read the grounding lines gdf = gpd.read_file(GL_FILE) #-- read the basin file basins = gpd.read_file(BASIN_FILE) idx = basins.index[basins['NAME'] == region] #-- get polygon poly = basins['geometry'][idx[0]] #-- add a 5km buffer to find the corresponding GLs region_poly = poly.buffer(5e3) lines = [] for i in range(len(gdf)): #-- extract geometry to see if it's in region of interest ll = gdf['geometry'][i] if ll.intersects(region_poly): lines.append(ll) #-- merge all lines into linestring lm = ops.linemerge(lines) #-- also create a polygon to represent the GZ with a small buffer (10cm) gz_file = os.path.join(os.path.dirname(GL_FILE), 'GZ_{0}.shp'.format(region)) if os.path.exists(gz_file): print('Reading GZ polygon from file.') gz_df = gpd.read_file(gz_file) gz_poly = [] for i in range(len(gz_df)): gz_poly.append(gz_df['geometry'][i]) gz_poly = MultiPolygon(gz_poly) else: print('Creating GZ polygon.') ep = lm.buffer(1e-1) #-- get the boundary of the polygon containing all lines to make new polygon of just the envelope gz_poly = [] for ip in ep: x, y = ip.exterior.coords.xy gz_poly.append(Polygon(zip(x, y))) #-- save the error polygon #-- first make DataFrame df = {'REGION': [], 'center_x': [], 'center_y': []} out_geo = [] for p in gz_poly: #-- note the width can be calculated from area and perimeter w = (-p.length + np.sqrt(p.length**2 - 16 * p.area)) / 4 l = p.length / 2 - w print(w, l) if (w > 1 and l > 1): df['REGION'].append(region) x, y = p.centroid.coords.xy df['center_x'].append(x[0]) df['center_y'].append(y[0]) out_geo.append(p) out_gdf = gpd.GeoDataFrame(df, geometry=out_geo, crs=gdf.crs) out_gdf.to_file(gz_file, driver='ESRI Shapefile') #-- read velocity field vel_fid = nc.Dataset(VEL_FILE, 'r') x = vel_fid['x'][:] y = vel_fid['y'][:] vx = vel_fid['VX'][:] vy = vel_fid['VY'][:] #-- also read lat and lon # vel_lat = vel_fid['lat'][:] # vel_lon = vel_fid['lon'][:] vel_fid.close() #-- select the points for the calculation of GZ width #-- in order to generate points, we randomly draw a line from the #-- mutliline, and then draw a random distance to go along the line to get #-- a coordinate. We repeat until the specified number of points is reached xlist = np.zeros(N) ylist = np.zeros(N) gz = np.zeros(N) vel_transects = {} perp_transects = {} random.seed(13) for i in range(N): #-- draw a random index for line along multilines ind_line = random.randrange(0, len(lm)) rand_line = lm[ind_line] rand_dist = random.uniform(0, rand_line.length) rand_pt = rand_line.interpolate(rand_dist) xx, yy = rand_pt.coords.xy xlist[i] = float(xx[0]) ylist[i] = float(yy[0]) #-- loop through points and calculate GZ for i, (xi, yi) in enumerate(zip(xlist, ylist)): if i % 100 == 0: print(i) #-- A) velocity based approach #-- get list of distances to get a list of closest points #- For a given coordinate, get the flow angle and then the intersecting line ii = np.argsort(np.abs(x - xi)) jj = np.argsort(np.abs(y - yi)) #-- loop through the first 20 points and get the minimum width, so we dont #-- rely on a single point tmp_trans = {} tmp_dist = np.zeros(25) cc = 0 for k in range(5): for w in range(5): #-- find flow angle ang = np.arctan(vy[jj[w], ii[k]] / vx[jj[w], ii[k]]) #-- Now constuct a line of a given length, centered at the #-- chosen coordinates, with the angle above dx, dy = dist * np.cos(ang), dist * np.sin(ang) tmp_trans[cc] = LineString([[x[ii[k]] - dx, y[jj[w]] - dy], [x[ii[k]], y[jj[w]]], [x[ii[k]] + dx, y[jj[w]] + dy]]) if tmp_trans[cc].intersects(gz_poly): vel_int = tmp_trans[cc].intersection(gz_poly) tmp_dist[cc] = vel_int.length else: print("No intersection. i={0:d}, k={1:d}, w={2:d}".format( i, k, w)) cc += 1 ind_min = np.argmin(tmp_dist) vel_transects[i] = tmp_trans[ind_min] gz[i] = tmp_dist[ind_min] # vel_int = vel_transects[i].intersection(gz_poly) # gz[i] = vel_int.length #-- B) tangent based appriach found_match = False if gz_poly.geom_type == 'Polygon': print("GZ object is polygon.") if vel_transects[i].intersects(gz_poly): x_ext, y_ext = gz_poly.exterior.coords.xy found_match = True elif gz_poly.geom_type == 'MultiPolygon': for sp in gz_poly: if vel_transects[i].intersects(sp): if found_match: print( "More than one intersecting polygon found for point {0:d}" .format(i)) else: #-- get coordinates of the exterior of GZ polygon to be used later x_ext, y_ext = sp.exterior.coords.xy found_match = True else: sys.exit("Exiting. GZ object type: ", gz_poly.geom_type) #-- Check if any of the polygons intersect if not found_match: print("No matches found for point {0:d}".format(i)) #-- move on to the next point and skip rest of iteration continue #-- Calculate GZ without velocity for comparison by constructing a perpendicular #-- line to the gz polygon at each point #-- 1) to get the tangent, get the index of the closest point on the boundary #-- of the gz polython dist2 = (x_ext - xi)**2 + (y_ext - yi)**2 # ind = np.argmin(dist2) ind = np.argsort(dist2) #-- loop through the first few points and get the minimum width, so we dont #-- rely on a single point tmp_trans = {} tmp_dist = np.zeros(10) for k in range(10): #-- 2) calculate the slope of the tangent tangent = (y_ext[ind[k]] - y_ext[ind[k - 1]]) / (x_ext[ind[k]] - x_ext[ind[k - 1]]) #-- 3) calculate slope of perpendicular line slope_ang = np.arctan(-1 / tangent) #-- 4) construct new transect and calculate width dx, dy = dist * np.cos(slope_ang), dist * np.sin(slope_ang) tmp_trans[k] = LineString( [[x_ext[ind[k]] - dx, y_ext[ind[k]] - dy], [x_ext[ind[k]], y_ext[ind[k]]], [x_ext[ind[k]] + dx, y_ext[ind[k]] + dy]]) perp_int = tmp_trans[k].intersection(gz_poly) tmp_dist[k] = perp_int.length ind_min = np.argmin(tmp_dist) perp_transects[i] = tmp_trans[ind_min] gz[i] = tmp_dist[ind_min] #-- write grounding zone widths to file outfile = os.path.join(os.path.dirname(GL_FILE), 'GZ_widths_{0}.csv'.format(region)) outfid = open(outfile, 'w') outfid.write('X (m),Y (m),width (km)\n') for i in range(N): outfid.write('{0:.6f},{1:.6f},{2:.3f}\n'.format( xlist[i], ylist[i], gz[i] / 1e3)) outfid.close() #-- plot a sample of points to check the grounding zones fig = plt.figure(1, figsize=(10, 8)) ax = fig.add_subplot(111) pp = PolygonPatch(poly, alpha=0.3, fc='lawngreen', ec='lawngreen', zorder=1) ax.add_patch(pp) for il in lines: xs, ys = il.coords.xy ax.plot(xs, ys, linewidth=0.4, alpha=0.8, color='k', zorder=2) for i in range(20): ip = random.randrange(0, N) #-- while distance to any of the previous points is less than 20km, #-- keep trying new indices (doesn't apply to 1st point) if i == 0: plot_pts = [Point(xlist[ip], ylist[ip])] else: pt = Point(xlist[ip], ylist[ip]) while (pt.distance(MultiPoint(plot_pts)) < 20e3): ip = random.randrange(0, N) pt = Point(xlist[ip], ylist[ip]) #-- now we can ensure the points aren't overlapping print("minimum distance to previous points: ", pt.distance(MultiPoint(plot_pts))) plot_pts.append(pt) #-- Now plot the transect for the given index lx, ly = vel_transects[ip].coords.xy ax.plot(lx, ly, linewidth=2.0, alpha=1.0, color='pink', zorder=3) if ip in perp_transects.keys(): lx2, ly2 = perp_transects[ip].coords.xy ax.plot(lx2, ly2, linewidth=2.0, alpha=1.0, color='red', zorder=4) ax.text(xlist[ip]+5e3,ylist[ip]+5e3,'{0:.1f}km'.format(gz[ip]/1e3),color='darkred',\ fontsize='small',fontweight='bold',bbox=dict(facecolor='mistyrose', alpha=0.5)) ax.plot([], [], color='pink', label="Velocity-based Intersect") ax.plot([], [], color='red', label="Tangent-based Intersect") ax.get_xaxis().set_ticks([]) ax.get_yaxis().set_ticks([]) ax.set_title("Grounding Zone Width for {0}".format(region)) plt.legend() plt.tight_layout() plt.savefig(outfile.replace('.csv', '.pdf'), format='PDF') plt.close(fig)
vdf = pd.read_csv('Boston-2-vertices_r.csv') edf = pd.read_csv('Boston-2-edges-4am_r.csv') # In[53]: geometry = [] for i, r in edf.iterrows(): s = r['s'] t = r['t'] ps = Point(vdf['lon'][s], vdf['lat'][s]) pt = Point(vdf['lon'][t], vdf['lat'][t]) geometry.append(LineString([ps,pt])) crs = {'proj': 'latlong', 'ellps': 'WGS84', 'datum': 'WGS84', 'no_defs': True} crs="+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs" gdf1 = gpd.GeoDataFrame(edf, geometry=geometry, crs=crs) # In[48]: #%matplotlib inline # fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(8,8), dpi=150) gdf['color'] = 0.0 ne = 11 gdf.loc[ne,'color'] = 100 gdf.plot(column='color', ax=ax, zorder=1)
def calc_gz(GL_FILE='',WIDTH_FILE='',BASIN_FILE='',VEL_FILE='',POINT_FILE='',region='',dist=0,N=0,vel_thr=0): #-- read the grounding lines df_gl = gpd.read_file(GL_FILE) #-- read widths df_w = gpd.read_file(WIDTH_FILE) #-- read the basin file basins = gpd.read_file(BASIN_FILE) idx = basins.index[basins['NAME']==region] #-- get polygon poly = basins['geometry'][idx[0]] #-- add a 5km buffer to find the corresponding GLs region_poly = poly.buffer(5e3) lines = [] dates = [] for i in range(len(df_gl)): #-- extract geometry to see if it's in region of interest ll = df_gl['geometry'][i] if ll.intersects(region_poly): lines.append(ll) dates.append(df_gl['FILENAME'][i].split("_")[2]) #-- get width lines ws = [] for i in range(len(df_w)): ws.append(df_w['geometry'][i]) widths = MultiLineString(ws) #-- merge all lines into linestring lm = ops.linemerge(lines) #-- also create a polygon to represent the GZ with a small buffer (10cm) gz_file = os.path.join(os.path.dirname(GL_FILE),'GZ_{0}.shp'.format(region)) if os.path.exists(gz_file): print('Reading GZ polygon from file.') gz_df = gpd.read_file(gz_file) gz_poly = [] for i in range(len(gz_df)): gz_poly.append(gz_df['geometry'][i]) gz_poly = MultiPolygon(gz_poly) else: print('Creating GZ polygon.') ep = lm.buffer(1e-1) #-- get the boundary of the polygon containing all lines to make new polygon of just the envelope gz_poly = [] for ip in ep: x,y = ip.exterior.coords.xy gz_poly.append(Polygon(zip(x,y))) #-- save the error polygon #-- first make DataFrame df = {'REGION':[],'center_x':[],'center_y':[]} out_geo = [] for p in gz_poly: #-- note the width can be calculated from area and perimeter w = (-p.length+np.sqrt(p.length**2 - 16*p.area))/4 l = p.length/2 - w print(w,l) if (w > 1 and l > 1): df['REGION'].append(region) x,y = p.centroid.coords.xy df['center_x'].append(x[0]) df['center_y'].append(y[0]) out_geo.append(p) out_gdf = gpd.GeoDataFrame(df,geometry=out_geo,crs=df_gl.crs) out_gdf.to_file(gz_file,driver='ESRI Shapefile') #-- read velocity field vel_fid = nc.Dataset(VEL_FILE,'r') x = vel_fid['x'][:] y = vel_fid['y'][:] vx = vel_fid['VX'][:] vy = vel_fid['VY'][:] #-- also read lat and lon # vel_lat = vel_fid['lat'][:] # vel_lon = vel_fid['lon'][:] vel_fid.close() #-- select the points for the calculation of GZ width #-- in order to generate points, we randomly draw a line from the #-- mutliline, and then draw a random distance to go along the line to get #-- a coordinate. We repeat until the specified number of points is reached #-- first we will the array with the given points in POINTS_FILE, and fill #-- the rest randomly. xlist = np.zeros(N) ylist = np.zeros(N) gz = np.zeros(N) date1_list = [None]*N date2_list = [None]*N vel_transects = {} cn_transects = {} #-- read given points df_pts = gpd.read_file(POINT_FILE) #-- reproject to the projection of GL data df_pts = df_pts.to_crs(df_gl.crs) N_given = len(df_pts) print("{0:d} points prescribed out a total of {1:d}".format(N_given,N)) for i in range(N_given): xx,yy = df_pts['geometry'][i].coords.xy xlist[i] = float(xx[0]) ylist[i] = float(yy[0]) random.seed(13) for i in range(N_given,N): #-- draw a random index for line along multilines ind_line = random.randrange(0,len(lm)) rand_line = lm[ind_line] rand_dist = random.uniform(0, rand_line.length) rand_pt = rand_line.interpolate(rand_dist) xx,yy = rand_pt.coords.xy xlist[i] = float(xx[0]) ylist[i] = float(yy[0]) #-- make special polygons that require a different transect length plong = Polygon([[-1175399.2293594137,-1124281.6845298712], [-1166026.3695500833,-1132757.1428680948], [-1194493.9384390623,-1149957.337730963], [-1198332.822509906,-1146467.4431211061]]) #-- loop through points and calculate GZ for i,(xi,yi) in enumerate(zip(xlist,ylist)): if i%100 == 0: print(i) #-- A) velocity based approach #-- get list of distances to get a list of closest points #- For a given coordinate, get the flow angle and then the intersecting line ii = np.argmin(np.abs(x - xi)) jj = np.argmin(np.abs(y - yi)) #-- chech if velocity is above required threshold vel_mag = np.sqrt(vy[jj,ii]**2 + vx[jj,ii]**2) if vel_mag > vel_thr: #-- find flow angle ang = np.arctan(vy[jj,ii]/vx[jj,ii]) #-- Now constuct a line of a given length, centered at the #-- chosen coordinates, with the angle above if Point(xi,yi).within(plong): dx,dy = 25e3*np.cos(ang),25e3*np.sin(ang) else: dx,dy = dist*np.cos(ang),dist*np.sin(ang) vel_transects[i] = LineString([[x[ii]-dx,y[jj]-dy],[x[ii],y[jj]],[x[ii]+dx,y[jj]+dy]]) #-- get intersection length vel_int = vel_transects[i].intersection(gz_poly) gz[i] = vel_int.length #-- get dates pt0 = vel_int.interpolate(0,normalized=True) pt1 = vel_int.interpolate(1,normalized=True) for l in range(len(lines)): if lines[l].distance(pt1) < 0.2: date1_list[i] = dates[l] elif lines[l].distance(pt0) < 0.2: date2_list[i] = dates[l] else: #-- B) retrieve width from QGIS centerline width calculation #-- first get the closest line to the point po = Point(xi,yi) wdist = np.zeros(len(widths)) for wi in range(len(widths)): wdist[wi] = widths[wi].distance(po) ind_w = np.argmin(wdist) cn_transects[i] = widths[ind_w] #-- get length gz[i] = cn_transects[i].length #-- also get the corresponding dates pt0 = cn_transects[i].interpolate(0,normalized=True) pt1 = cn_transects[i].interpolate(1,normalized=True) for l in range(len(lines)): if lines[l].distance(pt1) < 0.2: date1_list[i] = dates[l] elif lines[l].distance(pt0) < 0.2: date2_list[i] = dates[l] #-- write grounding zone widths to file outfile = os.path.join(os.path.dirname(GL_FILE),'GZ_widths-hybrid_{0}.csv'.format(region)) outfid = open(outfile,'w') outfid.write('X (m),Y (m),width (km),date1,date2\n') for i in range(N): outfid.write('{0:.6f},{1:.6f},{2:.3f},{3},{4}\n'.\ format(xlist[i],ylist[i],gz[i]/1e3,date1_list[i],date2_list[i])) outfid.close() #-- plot a sample of points to check the grounding zones fig = plt.figure(1,figsize=(10,8)) ax = fig.add_subplot(111) pp = PolygonPatch(poly,alpha=0.3,fc='lawngreen',ec='lawngreen',zorder=1) ax.add_patch(pp) for il in lines: xs,ys = il.coords.xy ax.plot(xs,ys,linewidth=0.4,alpha=0.8,color='k',zorder=2) for i in range(30): if i < N_given: ip = copy(i) if i == 0: plot_pts = [Point(xlist[ip],ylist[ip])] else: plot_pts.append(Point(xlist[ip],ylist[ip])) else: ip = random.randrange(0,N) #-- while distance to any of the previous points is less than 20km, #-- keep trying new indices (doesn't apply to 1st point) pt = Point(xlist[ip],ylist[ip]) while (pt.distance(MultiPoint(plot_pts)) < 12e3): ip = random.randrange(0,N) pt = Point(xlist[ip],ylist[ip]) #-- now we can ensure the points aren't overlapping print("minimum distance to previous points: ", pt.distance(MultiPoint(plot_pts))) plot_pts.append(pt) #-- Now plot the transect for the given index if ip in vel_transects.keys(): lx,ly = vel_transects[ip].coords.xy ax.plot(lx,ly,linewidth=2.0,alpha=1.0,color='red',zorder=3) elif ip in cn_transects.keys(): lx,ly = cn_transects[ip].coords.xy ax.plot(lx,ly,linewidth=2.0,alpha=1.0,color='darkorange',zorder=3) ax.text(xlist[ip]+5e3,ylist[ip]+5e3,'{0:.1f}km'.format(gz[ip]/1e3),color='darkred',\ fontsize=6,fontweight='bold',bbox=dict(facecolor='mistyrose', alpha=0.5)) # ax.scatter(xlist[ip],ylist[ip],s=10,color='darkorchid',zorder=4,alpha=0.5) ax.plot([],[],color='red',label="Velocity-based Intersect") ax.plot([],[],color='darkorange',label="Centerline-based Intersect") ax.get_xaxis().set_ticks([]) ax.get_yaxis().set_ticks([]) ax.set_title("Grounding Zone Width for {0}".format(region)) plt.legend() plt.tight_layout() plt.savefig(outfile.replace('.csv','.pdf'),format='PDF') plt.close(fig)
gdfVM3 = gpd.overlay(gdfCN5, gdfVM2, how='intersection') gdfVM3.plot() # 高德地图三沙市的岛屿 file = "D:/temp/geojsonutf8/中华人民共和国/海南省/三沙市/460300.json" gdfSS = gpd.read_file(file, encoding="utf-8") gdfSS.plot() # 用于协助选出岛屿,153个 import simplejson as json with open(file, encoding='utf-8') as f: collection = json.load(f, encoding='utf-8') # 转换为规范的MultiPolygon ps = collection['features'][0]["geometry"]["coordinates"] geos = [] for i in range(len(ps)): geos.append(Polygon(ps[i])) geos = MultiPolygon(geos) gdfSS2 = gpd.read_file(file, encoding="utf-8") gdfSS2["geometry"] = [geos] gdfSS2.plot() # 转换为wgs84坐标 gdfSS3 = gdf2wgs(gdfSS2) gdfSS3.plot() # 替换原admin1中的岛屿数据 gdfIslands2 = gdfIslands.copy() geos = gdfSS3["geometry"].to_list()[0] gdfIslands2["geometry"] = [geos] gdfIslands2.plot() gdfCN1 = gdfCN.copy() gdfCN1.drop(gdfCN1[gdfCN1.name == "Paracel Islands"].index, inplace=True) gdfCN1 = pd.concat([gdfCN1, gdfIslands2], axis=0)