# Comentar si se usa CPU config = tf.ConfigProto() config.gpu_options.allow_growth = True config.gpu_options.per_process_gpu_memory_fraction = 0.9 tf.keras.backend.set_session(tf.Session(config=config)) # comentar hasta aquí region_name = "20015" fp = 'C:/Users/ASUS/Desktop/ARAUCO/arauco/shapeFiles/20015/20015.shp' data = gpd.read_file(fp) caminos = data[data.TIPOUSO == "FCAM"] caminos = caminos["geometry"] caminos = gpd.GeoSeries(caminos) start = time.time() lengthComp = 0 parametros = { "1.1": 3, #"1.52": 9, #"1.8": 15 } canchasList = [] caminitos = [] sensProp = 1.1 lengthComp = 3
class TestDataFrameOps(TestCase): df = pd.DataFrame({ 'A': [1, 2, 3, 4, 5, 6], 'B': ['a', 'b', 'c', 'x', 'y', 'z'], 'C': [False, False, True, False, True, True], 'D': [0.4, 0.5, 0.3, 0.3, 0.1, 0.4] }) gdf = gpd.GeoDataFrame({ 'A': [1, 2, 3, 4, 5, 6], 'B': ['a', 'b', 'c', 'x', 'y', 'z'], 'C': [False, False, True, False, True, True], 'D': [0.4, 0.5, 0.3, 0.3, 0.1, 0.4], 'geometry': gpd.GeoSeries([ shapely.wkt.loads('POINT(10 10)'), shapely.wkt.loads('POINT(10 20)'), shapely.wkt.loads('POINT(10 30)'), shapely.wkt.loads('POINT(20 30)'), shapely.wkt.loads('POINT(20 20)'), shapely.wkt.loads('POINT(20 10)'), ]) }) def test_data_frame_min(self): df2 = data_frame_min(TestDataFrameOps.df, 'D') self.assertIsInstance(df2, pd.DataFrame) self.assertEqual(len(df2), 1) self.assertEqual(list(df2.columns), ['A', 'B', 'C', 'D']) self.assertEqual(df2.iloc[0, 0], 5) self.assertEqual(df2.iloc[0, 1], 'y') self.assertEqual(df2.iloc[0, 2], True) self.assertEqual(df2.iloc[0, 3], 0.1) def test_data_frame_max(self): df2 = data_frame_max(TestDataFrameOps.df, 'D') self.assertIsInstance(df2, pd.DataFrame) self.assertEqual(len(df2), 1) self.assertEqual(list(df2.columns), ['A', 'B', 'C', 'D']) self.assertEqual(df2.iloc[0, 0], 2) self.assertEqual(df2.iloc[0, 1], 'b') self.assertEqual(df2.iloc[0, 2], False) self.assertEqual(df2.iloc[0, 3], 0.5) def test_data_frame_query(self): df2 = data_frame_query(TestDataFrameOps.df, "D >= 0.4 and B != 'b'") self.assertIsInstance(df2, pd.DataFrame) self.assertEqual(len(df2), 2) self.assertEqual(list(df2.columns), ['A', 'B', 'C', 'D']) self.assertEqual(df2.iloc[0, 0], 1) self.assertEqual(df2.iloc[0, 1], 'a') self.assertEqual(df2.iloc[0, 2], False) self.assertEqual(df2.iloc[0, 3], 0.4) self.assertEqual(df2.iloc[1, 0], 6) self.assertEqual(df2.iloc[1, 1], 'z') self.assertEqual(df2.iloc[1, 2], True) self.assertEqual(df2.iloc[1, 3], 0.4) def test_data_frame_query_with_geom(self): df2 = data_frame_query(TestDataFrameOps.gdf, "not C and @almost_equals('10,10')") self.assertIsInstance(df2, gpd.GeoDataFrame) self.assertEqual(len(df2), 1) df2 = data_frame_query(TestDataFrameOps.gdf, "not C and @contains('10,10')") self.assertIsInstance(df2, gpd.GeoDataFrame) self.assertEqual(len(df2), 1) df2 = data_frame_query(TestDataFrameOps.gdf, "not C and @crosses('10,10')") self.assertIsInstance(df2, gpd.GeoDataFrame) self.assertEqual(len(df2), 0) df2 = data_frame_query(TestDataFrameOps.gdf, "not C and @disjoint('10,10')") self.assertIsInstance(df2, gpd.GeoDataFrame) self.assertEqual(len(df2), 2) df2 = data_frame_query(TestDataFrameOps.gdf, "not C and @intersects('19, 9, 21, 31')") self.assertIsInstance(df2, gpd.GeoDataFrame) self.assertEqual(len(df2), 1) df2 = data_frame_query(TestDataFrameOps.gdf, "not C and @touches('10, 10, 20, 30')") self.assertIsInstance(df2, gpd.GeoDataFrame) self.assertEqual(len(df2), 3) df2 = data_frame_query(TestDataFrameOps.gdf, "@within('19, 9, 21, 31')") self.assertIsInstance(df2, gpd.GeoDataFrame) self.assertEqual(len(df2), 3) self.assertEqual(list(df2.columns), ['A', 'B', 'C', 'D', 'geometry']) self.assertEqual(df2.iloc[0, 0], 4) self.assertEqual(df2.iloc[1, 0], 5) self.assertEqual(df2.iloc[2, 0], 6) self.assertEqual(df2.iloc[0, 1], 'x') self.assertEqual(df2.iloc[1, 1], 'y') self.assertEqual(df2.iloc[2, 1], 'z') self.assertEqual(df2.iloc[0, 2], False) self.assertEqual(df2.iloc[1, 2], True) self.assertEqual(df2.iloc[2, 2], True) self.assertEqual(df2.iloc[0, 3], 0.3) self.assertEqual(df2.iloc[1, 3], 0.1) self.assertEqual(df2.iloc[2, 3], 0.4) df2 = data_frame_query(TestDataFrameOps.gdf, "not C and @within('19, 9, 21, 31')") self.assertIsInstance(df2, gpd.GeoDataFrame) self.assertEqual(len(df2), 1) self.assertEqual(list(df2.columns), ['A', 'B', 'C', 'D', 'geometry']) self.assertEqual(df2.iloc[0, 0], 4) self.assertEqual(df2.iloc[0, 1], 'x') self.assertEqual(df2.iloc[0, 2], False) self.assertEqual(df2.iloc[0, 3], 0.3) df2 = data_frame_query( TestDataFrameOps.gdf, "not C and geometry.within(@from_wkt('19, 9, 21, 31'))") self.assertIsInstance(df2, gpd.GeoDataFrame) self.assertEqual(len(df2), 1) self.assertEqual(list(df2.columns), ['A', 'B', 'C', 'D', 'geometry']) self.assertEqual(df2.iloc[0, 0], 4) self.assertEqual(df2.iloc[0, 1], 'x') self.assertEqual(df2.iloc[0, 2], False) self.assertEqual(df2.iloc[0, 3], 0.3)
pts_in_this_poly = [] # Now loop over all points with index j. for j, pt in pts.iterrows(): if poly.geometry.contains(pt.geometry): # Then it's a hit! Add it to the list, # and drop it so we have less hunting. pts_in_this_poly.append(pt.geometry) pts = pts.drop([j]) # We could do all sorts, like grab a property of the # points, but let's just append the number of them. pts_in_polys.append(len(pts_in_this_poly)) # Add the number of points for each poly to the dataframe. polygons['number of points'] = gpd.GeoSeries(pts_in_polys) ############# import geopandas as gpd import pandas as pd polys = grid.copy() polys['PolyID'] = polys.index points2 = points.copy() dfsjoin = gpd.sjoin(polys, points2) #Spatial join Points to polygons dfpivot = pd.pivot_table(dfsjoin, index='PolyID', columns='Food', aggfunc={'Food': len}) dfpivot.columns = dfpivot.columns.droplevel()
def compute_screen_line_counts(feed: "Feed", screen_lines: gpd.GeoDataFrame, dates: List[str]) -> pd.DataFrame: """ Find all the Feed trips active on the given YYYYMMDD dates whose shapes intersect the given GeoDataFrame of screen lines, that is, of straight WGS84 LineStrings. Compute the intersection times and directions for each trip. Return a DataFrame with the columns - ``'date'`` - ``'trip_id'`` - ``'route_id'`` - ``'route_short_name'`` - ``'shape_id'``: shape ID of the trip - ``'screen_line_id'``: ID of the screen line as specified in ``screen_lines`` or as assigned after the fact. - ``'crossing_distance'``: distance along the trip shape of the screen line intersection ``'crossing_time'``: time that the trip's vehicle crosses the scren line; one trip could cross multiple times - ``'crossing_direction'``: 1 or -1; 1 indicates trip travel from the left side to the right side of the screen line; -1 indicates trip travel in the opposite direction Notes: - Assume the Feed's stop times DataFrame has an accurate ``shape_dist_traveled`` column. - Assume that trips travel in the same direction as their shapes, an assumption that is part of the GTFS. - Assume that the screen line is straight and simple. - Probably does not give correct results for trips with self-intersecting shapes. - The algorithm works as follows 1. Find the trip shapes that intersect the screen lines. 2. For each such shape and screen line, compute the intersection points, the distance of the point along the shape, and the orientation of the screen line relative to the shape. 3. For each given date, restrict to trips active on the date and interpolate a stop time for the intersection point using the ``shape_dist_traveled`` column. 4. Use that interpolated time as the crossing time of the trip vehicle. """ dates = feed.subset_dates(dates) if not dates: return pd.DataFrame() # Get shapes as GeoDataFrame shapes_g = feed.geometrize_shapes(use_utm=True) # Convert screen lines to UTM crs = shapes_g.crs screen_lines = screen_lines.to_crs(crs) # Create screen line IDs if necessary n = screen_lines.shape[0] if "screen_line_id" not in screen_lines.columns: screen_lines["screen_line_id"] = hp.make_ids(n, "sl") # Make a vector in the direction of each screen line to calculate crossing orientation. # Does not work in case of a bent screen line. p1 = screen_lines.geometry.map(lambda x: np.array(x.coords[0])) p2 = screen_lines.geometry.map(lambda x: np.array(x.coords[-1])) screen_lines["screen_line_vector"] = p2 - p1 # Get intersection points of shapes and screen lines g0 = ( # Only keep shapes that intersect screen lines to reduce computations gpd.sjoin(shapes_g, screen_lines.filter( ["screen_line_id", "geometry"])).merge(screen_lines, on="screen_line_id").assign( geometry_x=lambda x: gpd.GeoSeries(x.geometry_x, crs=crs) ).set_geometry("geometry_x") # Compute intersection points .assign(int_point=lambda x: x.geometry_x.intersection( gpd.GeoSeries(x.geometry_y, crs=crs)))) # Unpack multipoint intersections to yield a new GeoDataFrame records = [] for row in g0.itertuples(index=False): if isinstance(row.int_point, sg.Point): intersections = [row.int_point] else: intersections = row.int_point for int_point in intersections: record = { "shape_id": row.shape_id, "screen_line_id": row.screen_line_id, "geometry": row.geometry_x, "int_point": int_point, "screen_line_vector": row.screen_line_vector, } records.append(record) g = gpd.GeoDataFrame.from_records(records) g.crs = crs # Get distance (in meters) of each intersection point along shape g["crossing_dist"] = g.apply(lambda x: x.geometry.project(x.int_point), axis=1) # Build a tiny vector along each shape p2 = g.apply(lambda x: x.geometry.interpolate(x.crossing_dist + 1), axis=1).map(lambda x: np.array(x.coords[0])) p1 = g.int_point.map(lambda x: np.array(x.coords[0])) g["shape_vector"] = p2 - p1 # Compute crossing direction by taking the vector cross product of # the shape vector and the screen line vector det = g.apply( lambda x: np.linalg.det( np.array([x.shape_vector, x.screen_line_vector])), axis=1, ) g["crossing_direction"] = det.map(lambda x: 1 if x >= 0 else -1) # Convert to feed distance units converter = hp.get_convert_dist("m", feed.dist_units) g["crossing_dist"] = g["crossing_dist"].map(converter) # Summarize work so far into a lookup table h = ( g.filter([ "shape_id", "screen_line_id", "crossing_direction", "crossing_dist" ]).set_index("shape_id").sort_values( ["shape_id", "crossing_dist"]) # Need this sorting for interpolation to work ) # Get stop times of trips whose shapes lie in h st = ( feed.trips.loc[lambda x: x.shape_id.isin(h.index)] # Merge in route short names and stop times .merge(feed.routes[["route_id", "route_short_name"]]).merge(feed.stop_times) # Keep only non-NaN departure times .loc[lambda x: x.departure_time.notna()] # Convert to seconds past midnight .assign(departure_time=lambda x: x.departure_time.map( hp.timestr_to_seconds))) # Compute crossing times by date records = [] ta = feed.compute_trip_activity(dates) for date in dates: # Subset to trips active on date and merge with g ids = ta.loc[lambda x: x[date] == 1, "trip_id"] f = st.loc[lambda x: x.trip_id.isin(ids)].sort_values([ "trip_id", "shape_dist_traveled" ]) # Need this sorting for interpolation to work # Get crossing time for each trip for tid, group in f.groupby("trip_id"): sid = group["shape_id"].iat[0] rid = group["route_id"].iat[0] rsn = group["route_short_name"].iat[0] dists = group["shape_dist_traveled"].values times = group["departure_time"].values crossing_dists = h.loc[[sid], "crossing_dist"].values crossing_times = np.interp(crossing_dists, dists, times) for i, row in enumerate(h.loc[[sid]].itertuples(index=False)): record = { "date": date, "trip_id": tid, "route_id": group.route_id.iat[0], "route_short_name": group.route_short_name.iat[0], "shape_id": group.shape_id.iat[0], "screen_line_id": row.screen_line_id, "crossing_direction": row.crossing_direction, "crossing_distance": row.crossing_dist, "crossing_time": crossing_times[i], } records.append(record) result = pd.DataFrame.from_records(records).assign( crossing_time=lambda x: x.crossing_time.map( lambda x: hp.timestr_to_seconds(x, inverse=True))) return result
def do_your_thang(img_dir, out_path, path_t, saved_model, w, Ovr, f, timeline): truths = gpd.read_file(path_t) crs = truths.crs # print('\nCascading truths for analysis...') truths = gpd.GeoSeries(cascaded_union(truths['geometry'])) truths = gpd.GeoDataFrame(geometry=truths, crs=crs) total = get_number(img_dir, '*tif') count = 1 for pic in glob.glob(img_dir + '/*.tif'): if timeline: print('########## \ Timeline Image: %s / %s \ ##########' % (count, total)) fn = get_name(pic) out_dir = out_path + '/' + fn ### Build subfolders if os.path.isdir(out_dir) is False: os.makedirs(out_dir) os.makedirs(out_dir + '/tiles') os.makedirs(out_dir + '/predictions') os.makedirs(out_dir + '/map') os.makedirs(out_dir + '/metrics') tiles_dir = out_dir + '/tiles' pred_dir = out_dir + '/predictions' map_dir = out_dir + '/map' met_dir = out_dir + '/metrics' ### Split mosic into tiles print('Splitting image: %s...' % fn) with suppress_stdout(): ### suppress the long output Split.split_image(input=pic, output_dir=tiles_dir, patch_w=w, patch_h=w, adj_overlay_x=Ovr, adj_overlay_y=Ovr, out_format=f) os.remove('split_image_info.txt') ### Remove tiles that don't intersect ground truths & Re-number Filter.remove(tiles_dir, truths, overlap_only=True) ### convert to .JPEG Convert.to_jpg(tiles_dir) ### create & save predictions UNet_Predict.deploy_model(saved_model, tiles_dir, pred_dir) ### create map from prediction tiles if Map.build_map(tiles_dir, pred_dir, map_dir, fn, truths): ### calculate performance metrics (and save True Posities for timeline) Metrics.run_metrics(truths, map_dir, pic, fn, met_dir, timeline) count += 1 top_folder = out_path + '/Maps' ### create topfolder to consolidate prediction/timeline output if os.path.isdir(top_folder) is False: os.makedirs(top_folder) ### copy output maps to top folder for file in glob.glob(out_path + '/**/map/*cascaded_map*'): shutil.copy(file, top_folder) return
def get_perimeter(): """Creates a perimeter polygon from points""" d_array = get_geometry_data('Perimeter') aoi = Polygon([tuple(p) for p in d_array]) return gpd.GeoDataFrame(geometry=gpd.GeoSeries(aoi))
def to_feature_collection(shape): """ Create a GeoJSON FeatureCollection object for the given shapely.geometry :shape:. """ collection = geopandas.GeoSeries([shape]).__geo_interface__ return dict(collection)
def plot_dataframe(df, column=None, cmap=None, color=None, ax=None, cax=None, categorical=False, legend=False, scheme=None, k=5, vmin=None, vmax=None, markersize=None, figsize=None, legend_kwds=None, categories=None, classification_kwds=None, missing_kwds=None, aspect="auto", **style_kwds): """ Plot a GeoDataFrame. Generate a plot of a GeoDataFrame with matplotlib. If a column is specified, the plot coloring will be based on values in that column. Parameters ---------- df : GeoDataFrame The GeoDataFrame to be plotted. Currently Polygon, MultiPolygon, LineString, MultiLineString and Point geometries can be plotted. column : str, np.array, pd.Series (default None) The name of the dataframe column, np.array, or pd.Series to be plotted. If np.array or pd.Series are used then it must have same length as dataframe. Values are used to color the plot. Ignored if `color` is also set. cmap : str (default None) The name of a colormap recognized by matplotlib. color : str (default None) If specified, all objects will be colored uniformly. ax : matplotlib.pyplot.Artist (default None) axes on which to draw the plot cax : matplotlib.pyplot Artist (default None) axes on which to draw the legend in case of color map. categorical : bool (default False) If False, cmap will reflect numerical values of the column being plotted. For non-numerical columns, this will be set to True. legend : bool (default False) Plot a legend. Ignored if no `column` is given, or if `color` is given. scheme : str (default None) Name of a choropleth classification scheme (requires mapclassify). A mapclassify.MapClassifier object will be used under the hood. Supported are all schemes provided by mapclassify (e.g. 'BoxPlot', 'EqualInterval', 'FisherJenks', 'FisherJenksSampled', 'HeadTailBreaks', 'JenksCaspall', 'JenksCaspallForced', 'JenksCaspallSampled', 'MaxP', 'MaximumBreaks', 'NaturalBreaks', 'Quantiles', 'Percentiles', 'StdMean', 'UserDefined'). Arguments can be passed in classification_kwds. k : int (default 5) Number of classes (ignored if scheme is None) vmin : None or float (default None) Minimum value of cmap. If None, the minimum data value in the column to be plotted is used. vmax : None or float (default None) Maximum value of cmap. If None, the maximum data value in the column to be plotted is used. markersize : str or float or sequence (default None) Only applies to point geometries within a frame. If a str, will use the values in the column of the frame specified by markersize to set the size of markers. Otherwise can be a value to apply to all points, or a sequence of the same length as the number of points. figsize : tuple of integers (default None) Size of the resulting matplotlib.figure.Figure. If the argument axes is given explicitly, figsize is ignored. legend_kwds : dict (default None) Keyword arguments to pass to matplotlib.pyplot.legend() or matplotlib.pyplot.colorbar(). Additional accepted keywords when `scheme` is specified: fmt : string A formatting specification for the bin edges of the classes in the legend. For example, to have no decimals: ``{"fmt": "{:.0f}"}``. labels : list-like A list of legend labels to override the auto-generated labels. Needs to have the same number of elements as the number of classes (`k`). categories : list-like Ordered list-like object of categories to be used for categorical plot. classification_kwds : dict (default None) Keyword arguments to pass to mapclassify missing_kwds : dict (default None) Keyword arguments specifying color options (as style_kwds) to be passed on to geometries with missing values in addition to or overwriting other style kwds. If None, geometries with missing values are not plotted. aspect : 'auto', 'equal', None or float (default 'auto') Set aspect of axis. If 'auto', the default aspect for map plots is 'equal'; if however data are not projected (coordinates are long/lat), the aspect is by default set to 1/cos(df_y * pi/180) with df_y the y coordinate of the middle of the GeoDataFrame (the mean of the y range of bounding box) so that a long/lat square appears square in the middle of the plot. This implies an Equirectangular projection. If None, the aspect of `ax` won't be changed. It can also be set manually (float) as the ratio of y-unit to x-unit. **style_kwds : dict Style options to be passed on to the actual plot function, such as ``edgecolor``, ``facecolor``, ``linewidth``, ``markersize``, ``alpha``. Returns ------- ax : matplotlib axes instance """ if "colormap" in style_kwds: warnings.warn( "'colormap' is deprecated, please use 'cmap' instead " "(for consistency with matplotlib)", FutureWarning, ) cmap = style_kwds.pop("colormap") if "axes" in style_kwds: warnings.warn( "'axes' is deprecated, please use 'ax' instead " "(for consistency with pandas)", FutureWarning, ) ax = style_kwds.pop("axes") if column is not None and color is not None: warnings.warn( "Only specify one of 'column' or 'color'. Using 'color'.", UserWarning) column = None try: import matplotlib.pyplot as plt except ImportError: raise ImportError( "The matplotlib package is required for plotting in geopandas. " "You can install it using 'conda install -c conda-forge matplotlib' or " "'pip install matplotlib'.") if ax is None: if cax is not None: raise ValueError("'ax' can not be None if 'cax' is not.") fig, ax = plt.subplots(figsize=figsize) if aspect == "auto": if df.crs and df.crs.is_geographic: bounds = df.total_bounds y_coord = np.mean([bounds[1], bounds[3]]) ax.set_aspect(1 / np.cos(y_coord * np.pi / 180)) # formula ported from R package sp # https://github.com/edzer/sp/blob/master/R/mapasp.R else: ax.set_aspect("equal") elif aspect is not None: ax.set_aspect(aspect) # GH 1555 # if legend_kwds set, copy so we don't update it in place if legend_kwds is not None: legend_kwds = legend_kwds.copy() if df.empty: warnings.warn( "The GeoDataFrame you are attempting to plot is " "empty. Nothing has been displayed.", UserWarning, ) return ax if isinstance(markersize, str): markersize = df[markersize].values if column is None: return plot_series(df.geometry, cmap=cmap, color=color, ax=ax, figsize=figsize, markersize=markersize, aspect=aspect, **style_kwds) # To accept pd.Series and np.arrays as column if isinstance(column, (np.ndarray, pd.Series)): if column.shape[0] != df.shape[0]: raise ValueError( "The dataframe and given column have different number of rows." ) else: values = column # Make sure index of a Series matches index of df if isinstance(values, pd.Series): values = values.reindex(df.index) else: values = df[column] if pd.api.types.is_categorical_dtype(values.dtype): if categories is not None: raise ValueError( "Cannot specify 'categories' when column has categorical dtype" ) categorical = True elif values.dtype is np.dtype("O") or categories: categorical = True nan_idx = np.asarray(pd.isna(values), dtype="bool") # Define `values` as a Series if categorical: if cmap is None: cmap = "tab10" cat = pd.Categorical(values, categories=categories) categories = list(cat.categories) # values missing in the Categorical but not in original values missing = list(np.unique(values[~nan_idx & cat.isna()])) if missing: raise ValueError( "Column contains values not listed in categories. " "Missing categories: {}.".format(missing)) values = cat.codes[~nan_idx] vmin = 0 if vmin is None else vmin vmax = len(categories) - 1 if vmax is None else vmax if scheme is not None: if classification_kwds is None: classification_kwds = {} if "k" not in classification_kwds: classification_kwds["k"] = k binning = _mapclassify_choro(values[~nan_idx], scheme, **classification_kwds) # set categorical to True for creating the legend categorical = True if legend_kwds is not None and "labels" in legend_kwds: if len(legend_kwds["labels"]) != binning.k: raise ValueError("Number of labels must match number of bins, " "received {} labels for {} bins".format( len(legend_kwds["labels"]), binning.k)) else: categories = list(legend_kwds.pop("labels")) else: fmt = "{:.2f}" if legend_kwds is not None and "fmt" in legend_kwds: fmt = legend_kwds.pop("fmt") categories = binning.get_legend_classes(fmt) values = np.array(binning.yb) # fill values with placeholder where were NaNs originally to map them properly # (after removing them in categorical or scheme) if categorical: for n in np.where(nan_idx)[0]: values = np.insert(values, n, values[0]) mn = values[~np.isnan(values)].min() if vmin is None else vmin mx = values[~np.isnan(values)].max() if vmax is None else vmax # decompose GeometryCollections geoms, multiindex = _flatten_multi_geoms(df.geometry, prefix="Geom") values = np.take(values, multiindex, axis=0) nan_idx = np.take(nan_idx, multiindex, axis=0) expl_series = geopandas.GeoSeries(geoms) geom_types = expl_series.type poly_idx = np.asarray((geom_types == "Polygon") | (geom_types == "MultiPolygon")) line_idx = np.asarray((geom_types == "LineString") | (geom_types == "MultiLineString") | (geom_types == "LinearRing")) point_idx = np.asarray((geom_types == "Point") | (geom_types == "MultiPoint")) # plot all Polygons and all MultiPolygon components in the same collection polys = expl_series[poly_idx & np.invert(nan_idx)] subset = values[poly_idx & np.invert(nan_idx)] if not polys.empty: _plot_polygon_collection(ax, polys, subset, vmin=mn, vmax=mx, cmap=cmap, **style_kwds) # plot all LineStrings and MultiLineString components in same collection lines = expl_series[line_idx & np.invert(nan_idx)] subset = values[line_idx & np.invert(nan_idx)] if not lines.empty: _plot_linestring_collection(ax, lines, subset, vmin=mn, vmax=mx, cmap=cmap, **style_kwds) # plot all Points in the same collection points = expl_series[point_idx & np.invert(nan_idx)] subset = values[point_idx & np.invert(nan_idx)] if not points.empty: if isinstance(markersize, np.ndarray): markersize = np.take(markersize, multiindex, axis=0) markersize = markersize[point_idx & np.invert(nan_idx)] _plot_point_collection(ax, points, subset, vmin=mn, vmax=mx, markersize=markersize, cmap=cmap, **style_kwds) if missing_kwds is not None and not expl_series[nan_idx].empty: if color: if "color" not in missing_kwds: missing_kwds["color"] = color merged_kwds = style_kwds.copy() merged_kwds.update(missing_kwds) plot_series(expl_series[nan_idx], ax=ax, **merged_kwds) if legend and not color: if legend_kwds is None: legend_kwds = {} if "fmt" in legend_kwds: legend_kwds.pop("fmt") from matplotlib.lines import Line2D from matplotlib.colors import Normalize from matplotlib import cm norm = style_kwds.get("norm", None) if not norm: norm = Normalize(vmin=mn, vmax=mx) n_cmap = cm.ScalarMappable(norm=norm, cmap=cmap) if categorical: patches = [] for value, cat in enumerate(categories): patches.append( Line2D( [0], [0], linestyle="none", marker="o", alpha=style_kwds.get("alpha", 1), markersize=10, markerfacecolor=n_cmap.to_rgba(value), markeredgewidth=0, )) if missing_kwds is not None: if "color" in merged_kwds: merged_kwds["facecolor"] = merged_kwds["color"] patches.append( Line2D( [0], [0], linestyle="none", marker="o", alpha=merged_kwds.get("alpha", 1), markersize=10, markerfacecolor=merged_kwds.get("facecolor", None), markeredgecolor=merged_kwds.get("edgecolor", None), markeredgewidth=merged_kwds.get( "linewidth", 1 if merged_kwds.get("edgecolor", False) else 0), )) categories.append(merged_kwds.get("label", "NaN")) legend_kwds.setdefault("numpoints", 1) legend_kwds.setdefault("loc", "best") ax.legend(patches, categories, **legend_kwds) else: if cax is not None: legend_kwds.setdefault("cax", cax) else: legend_kwds.setdefault("ax", ax) n_cmap.set_array([]) ax.get_figure().colorbar(n_cmap, **legend_kwds) plt.draw() return ax
def complete_unique_geoms(): # output unique geometries and sum of all # project locations associated with that geometry unique_geo_df = gpd.GeoDataFrame() if active_data.size > 0: unique_active_data = active_data.loc[ active_data.geom_val != "None"].copy(deep=True) if active_data.size > 0 and unique_active_data.size > 0: # creating geodataframe geo_df = gpd.GeoDataFrame() # location id geo_df["project_location_id"] = unique_active_data["project_location_id"] geo_df["project_location_id"].fillna(unique_active_data["project_id"], inplace=True) geo_df["project_location_id"] = geo_df["project_location_id"].astype(str) # assuming even split of total project dollars is "max" dollars # that project location could receive geo_df["dollars"] = unique_active_data["adjusted_val"] # geometry for each project location geo_df["geometry"] = gpd.GeoSeries(unique_active_data["geom_val"]) # # write full to geojson # full_geo_json = geo_df.to_json() # full_geo_file = open(dir_working + "/full.geojson", "w") # json.dump(json.loads(full_geo_json), full_geo_file, indent=4) # full_geo_file.close() # string version of geometry used to determine duplicates geo_df["str_geo_hash"] = geo_df["geometry"].astype(str).apply( lambda z: str_sha1_hash(z)) # create and set unique index geo_df['index'] = range(0, len(geo_df)) geo_df = geo_df.set_index('index') # group project locations by geometry using str_geo_hash field # and for each unique geometry get the sum of dollars for # all project locations with that geometry sum_unique = geo_df.groupby(by='str_geo_hash')['dollars'].sum() # get count of locations for each unique geom geo_df['ones'] = 1 #(pd.Series(np.ones(len(geo_df)))).values sum_count = geo_df.groupby(by='str_geo_hash')['ones'].sum() # create list of project location ids for unique geoms cat_plids = geo_df.groupby(by='str_geo_hash')['project_location_id'].apply( lambda z: '|'.join(list(z))) # temporary dataframe with # unique geometry # location_count # dollar sums # which can be used to merge with original geo_df dataframe tmp_geo_df = gpd.GeoDataFrame() tmp_geo_df['unique_dollars'] = sum_unique tmp_geo_df['location_count'] = sum_count tmp_geo_df['project_location_ids'] = cat_plids tmp_geo_df['str_geo_hash'] = tmp_geo_df.index # merge geo_df with tmp_geo_df new_geo_df = geo_df.merge(tmp_geo_df, how='inner', on="str_geo_hash") # drops duplicate rows new_geo_df.drop_duplicates(subset="str_geo_hash", inplace=True) # gets rid of str_geo_hash column new_geo_df.drop('str_geo_hash', axis=1, inplace=True) # create final output geodataframe with index, unique_dollars # and unique geometry # unique_geo_df = gpd.GeoDataFrame() unique_geo_df["geometry"] = gpd.GeoSeries(new_geo_df["geometry"]) unique_geo_df["unique_dollars"] = new_geo_df["unique_dollars"] unique_geo_df["location_count"] = new_geo_df["location_count"] unique_geo_df["project_location_ids"] = new_geo_df["project_location_ids"] # unique_geo_df['index'] = range(len(unique_geo_df)) # write unique to geojson unique_geo_json = unique_geo_df.to_json() unique_geo_file = open(dir_working + "/unique.geojson", "w") json.dump(json.loads(unique_geo_json), unique_geo_file, indent=4) unique_geo_file.close()
from rasterio.plot import show from rasterio.mask import mask from shapely.geometry import box import geopandas as gpd from fiona.crs import from_epsg import os import matplotlib.pyplot as plt import gdal import glob # Openf file with the roofs for a single block src = fiona.open("blockRoofs.shp") pol = MultiPolygon([shape(building["geometry"]) for building in src]) rasterfiles = glob.glob('/media/HD/raster/*[!.aux.xml,!.dbf,!.py,!.csv,!.qpj]') gdf = gpd.GeoSeries(pol) basePath = '/media/HD/blockRaster/' l = len(rasterfiles) print("Start cropping") i = 0 for raster in rasterfiles: i += 1 print(f'Completed at {i*100/l:.2}%\r', end="") data = rasterio.open(raster) clippedImg, clippedTrans = mask(dataset=data, shapes=gdf, crop=True) out_meta = data.meta.copy() out_meta.update({ "driver": "GTiff", "height": clippedImg.shape[1],
def plot_series(s, cmap=None, color=None, ax=None, figsize=None, aspect="auto", **style_kwds): """ Plot a GeoSeries. Generate a plot of a GeoSeries geometry with matplotlib. Parameters ---------- s : Series The GeoSeries to be plotted. Currently Polygon, MultiPolygon, LineString, MultiLineString and Point geometries can be plotted. cmap : str (default None) The name of a colormap recognized by matplotlib. Any colormap will work, but categorical colormaps are generally recommended. Examples of useful discrete colormaps include: tab10, tab20, Accent, Dark2, Paired, Pastel1, Set1, Set2 color : str (default None) If specified, all objects will be colored uniformly. ax : matplotlib.pyplot.Artist (default None) axes on which to draw the plot figsize : pair of floats (default None) Size of the resulting matplotlib.figure.Figure. If the argument ax is given explicitly, figsize is ignored. aspect : 'auto', 'equal', None or float (default 'auto') Set aspect of axis. If 'auto', the default aspect for map plots is 'equal'; if however data are not projected (coordinates are long/lat), the aspect is by default set to 1/cos(s_y * pi/180) with s_y the y coordinate of the middle of the GeoSeries (the mean of the y range of bounding box) so that a long/lat square appears square in the middle of the plot. This implies an Equirectangular projection. If None, the aspect of `ax` won't be changed. It can also be set manually (float) as the ratio of y-unit to x-unit. **style_kwds : dict Color options to be passed on to the actual plot function, such as ``edgecolor``, ``facecolor``, ``linewidth``, ``markersize``, ``alpha``. Returns ------- ax : matplotlib axes instance """ if "colormap" in style_kwds: warnings.warn( "'colormap' is deprecated, please use 'cmap' instead " "(for consistency with matplotlib)", FutureWarning, ) cmap = style_kwds.pop("colormap") if "axes" in style_kwds: warnings.warn( "'axes' is deprecated, please use 'ax' instead " "(for consistency with pandas)", FutureWarning, ) ax = style_kwds.pop("axes") try: import matplotlib.pyplot as plt except ImportError: raise ImportError( "The matplotlib package is required for plotting in geopandas. " "You can install it using 'conda install -c conda-forge matplotlib' or " "'pip install matplotlib'.") if ax is None: fig, ax = plt.subplots(figsize=figsize) if aspect == "auto": if s.crs and s.crs.is_geographic: bounds = s.total_bounds y_coord = np.mean([bounds[1], bounds[3]]) ax.set_aspect(1 / np.cos(y_coord * np.pi / 180)) # formula ported from R package sp # https://github.com/edzer/sp/blob/master/R/mapasp.R else: ax.set_aspect("equal") elif aspect is not None: ax.set_aspect(aspect) if s.empty: warnings.warn( "The GeoSeries you are attempting to plot is " "empty. Nothing has been displayed.", UserWarning, ) return ax if s.is_empty.all(): warnings.warn( "The GeoSeries you are attempting to plot is " "composed of empty geometries. Nothing has been displayed.", UserWarning, ) return ax # if cmap is specified, create range of colors based on cmap values = None if cmap is not None: values = np.arange(len(s)) if hasattr(cmap, "N"): values = values % cmap.N style_kwds["vmin"] = style_kwds.get("vmin", values.min()) style_kwds["vmax"] = style_kwds.get("vmax", values.max()) # decompose GeometryCollections geoms, multiindex = _flatten_multi_geoms(s.geometry, prefix="Geom") values = np.take(values, multiindex, axis=0) if cmap else None expl_series = geopandas.GeoSeries(geoms) geom_types = expl_series.type poly_idx = np.asarray((geom_types == "Polygon") | (geom_types == "MultiPolygon")) line_idx = np.asarray((geom_types == "LineString") | (geom_types == "MultiLineString") | (geom_types == "LinearRing")) point_idx = np.asarray((geom_types == "Point") | (geom_types == "MultiPoint")) # plot all Polygons and all MultiPolygon components in the same collection polys = expl_series[poly_idx] if not polys.empty: # color overrides both face and edgecolor. As we want people to be # able to use edgecolor as well, pass color to facecolor facecolor = style_kwds.pop("facecolor", None) if color is not None: facecolor = color values_ = values[poly_idx] if cmap else None _plot_polygon_collection(ax, polys, values_, facecolor=facecolor, cmap=cmap, **style_kwds) # plot all LineStrings and MultiLineString components in same collection lines = expl_series[line_idx] if not lines.empty: values_ = values[line_idx] if cmap else None _plot_linestring_collection(ax, lines, values_, color=color, cmap=cmap, **style_kwds) # plot all Points in the same collection points = expl_series[point_idx] if not points.empty: values_ = values[point_idx] if cmap else None _plot_point_collection(ax, points, values_, color=color, cmap=cmap, **style_kwds) plt.draw() return ax
def decomposeSite(deims_site, admin_zones, zone_id, zone_name, debug=False): """Decompose a site by administrative zones. deims_site: site to decompose (gpd.GeoDataFrame) admin_zones: admin zones/regions to break deims_site into (gpd.GeoDataFrame) zone_id: column name of the zone ID in admin_zones (str) zone_name: column name of the zone name in admin_zones (str) debug: whether or not to plot the results for visual checking (bool) Returns the resulting GDF. """ # convert CRS of dataset to match admin zones # this will be the CRS of the output GDF deims_site = deims_site.to_crs(admin_zones.crs) # check which zones intersect the LTSER site ltser_zones = gpd.overlay(deims_site,admin_zones,how='intersection') # add original zones for area comparison, setting correct geometry # this is necessary to align the comparison geometry correctly: # comparing straight away with admin_zones.geometry.area doesn't # align the rows correctly ltser_zones = pd.merge(ltser_zones,admin_zones,on=zone_id) ltser_zones = ltser_zones.set_geometry('geometry_x') # add intersection area/zone area as new column # full_areas definition necessary because pd.merge only allows for one geoseries full_areas = gpd.GeoSeries(ltser_zones['geometry_y']) ltser_zones['intersection_ratio'] = ltser_zones.geometry.area/full_areas.area # construct GDF of cropped zones + ratio of area intersection gdf_out = gpd.GeoDataFrame( { 'zone_id': ltser_zones[zone_id].astype('string'), 'zone_name': ltser_zones[zone_name+'_x'].astype('string'), 'geometry': ltser_zones['geometry_x'], 'area_ratio': ltser_zones['intersection_ratio'] }, crs = ltser_zones.crs ) # optional visual check of intersection adds full geometry of zones if debug: # add and set full geometry gdf_out['debug_geometry'] = ltser_zones['geometry_y'] gdf_out = gdf_out.set_geometry('debug_geometry') # plot overlap - no need to return object since plots directly to (presumably) stdout fig, ax = plt.subplots(figsize = (10,10)) ax.set_axis_off() ax.set_title('Zones (blue) intersecting LTSER site (red)') gdf_out.plot(ax=ax) deims_site.boundary.plot(color='r',ax=ax) # drop debug_geometry column so output is identical to non-debug gdf_out.drop(columns='debug_geometry',inplace=True) gdf_out = gdf_out.set_geometry('geometry') return gdf_out else: return gdf_out
def calculate_risk(): shapefile = 'iho/iho.shp' oceans = gpd.read_file(shapefile) #list of webpages of the weather stations weather_stations = [ 'https://www.infoclimat.fr/observations-meteo/temps-reel/kemi/02864.html', 'https://www.infoclimat.fr/observations-meteo/temps-reel/helsinki-malmi/02975.html', 'https://www.infoclimat.fr/observations-meteo/temps-reel/bagaskar/02984.html', 'https://www.infoclimat.fr/observations-meteo/temps-reel/market/02993.html', 'https://www.infoclimat.fr/observations-meteo/temps-reel/nyhamn/02980.html', 'https://www.infoclimat.fr/observations-meteo/temps-reel/oulu/02875.html', 'https://www.infoclimat.fr/observations-meteo/temps-reel/pori/02952.html' ] #list of zone coordinates station_coordinates = gpd.GeoSeries([ Polygon([(66, 24.58), (65.355, 24.58), (65.355, 25.37, 64.63, 25.37)]), Polygon([(64.63, 23.6), (64.025, 23.6), (64.025, 23.15), (63.575, 23.15)]), Polygon([(63.575, 21.07), (63.24, 21.07), (63.24, 21.77, 62.26, 21.77)]), Polygon([(62.26, 21.8), (50, 21.8), (60.13, 19), (60.13, 19.935)]), Polygon([(59.97, 19.935), (59.97, 21.12), (60.52, 21.12), (60.52, 22.61)]), Polygon([(59.77, 22.61), (59.77, 23.485), (59.93, 23.485), (59.93, 24.535)]), Polygon([(60.25, 24.535), (60.25, 26.01), (60.37, 26.01), (60.37, 27)]) ]) df1 = gpd.GeoDataFrame({'geometry': station_coordinates}) results = [] #to put the zones coordinates item = 0 res = {} ocean = gpd.read_file(shapefile) df2 = gpd.GeoDataFrame({'geometry': station_coordinates}) for i in weather_stations: r = requests.get(i) try: soup = BeautifulSoup(r.text, 'html.parser') temp_max = soup.find_all(class_='displayer')[0].get_text() regex = re.search("[[\s]*([\S]*])", temp_max) if regex != None: temp_max = regex.group(1) else: temp_max = 0 temp_min = soup.find_all(class_='displayer')[1].get_text() regex = re.search("[\s]*([\S]*)", temp_min) regex = re.search("([0-9])", regex.group(1)) if regex != None: temp_min = regex.group(1) else: temp_min = 0 temp_min = int(temp_min) try: rain = soup.find_all(class_='displayer')[2].get_text() except: rain = 0 if (temp_min > 10) and (temp_max > 15): risk = 5.8 * temp_max + rain * 2 if risk < 96: color = "green" #in more than 50% of cases, in those conditions there will be a bloom elif risk < 104: color = "yellow" #in more than 80% of cases, in those conditions there will be a bloom else: color = "red" #in more than 95%... else: color = "blue" except: color = "blue" res_intersection = gpd.overlay(df1, df2, how='intersection') results.append(res_intersection) ocean = gpd.GeoDataFrame({'geometry': results}) item = item + 1 risk = 0 color = "blue" return ocean
def RequestData(self, request, inInfo, outInfo): import xarray as xr import numpy as np import geopandas as gpd import pandas as pd from shapely.geometry import box, Point, LineString from shapely.affinity import translate from vtk import vtkPolyData, vtkAppendPolyData, vtkCompositeDataSet, vtkMultiBlockDataSet import math import time # check mandatory fields if self._tablename is None or self._xcol is None or self._ycol is None or self._zcol is None: return 1 t0 = time.time() #df = pd.read_csv(self._tablename, low_memory=False) df = pd.read_csv(self._tablename, encoding=self._tableencoding, low_memory=False) print("len(df)", len(df)) if (len(df)) == 0: return # calculate coordinates xs = df[self._xcol] ys = df[self._ycol] zs = df[self._zcol] # create point geom = gpd.GeoSeries(map(Point, zip(xs, ys, zs))) # create line segment when possible if self._xcol2 is not None or self._ycol2 is not None or self._zcol2 is not None: # we can define only one (changed) column for 2nd point self._xcol2 = self._xcol2 if self._xcol2 is not None else self._xcol self._ycol2 = self._ycol2 if self._ycol2 is not None else self._ycol self._zcol2 = self._zcol2 if self._zcol2 is not None else self._zcol xs2 = df[self._xcol2] ys2 = df[self._ycol2] zs2 = df[self._zcol2] # create line segment geom2 = gpd.GeoSeries(map(Point, zip(xs2, ys2, zs2))) geom = gpd.GeoSeries(map(LineString, zip(geom, geom2))) # create geodataframe to build output df = gpd.GeoDataFrame(df, geometry=geom) # group to multiblock if self._tablecol is not None: df = df.sort_values(self._tablecol).set_index(self._tablecol) vtk_blocks = _NCubeGeometryOnTopography(df, None) if len(vtk_blocks) > 0: print("vtk_blocks", len(vtk_blocks)) mb = vtkMultiBlockDataSet.GetData(outInfo, 0) mb.SetNumberOfBlocks(len(vtk_blocks)) rowidx = 0 for (label, polyData) in vtk_blocks: #print (rowidx, label) mb.SetBlock(rowidx, polyData) mb.GetMetaData(rowidx).Set(vtkCompositeDataSet.NAME(), label) rowidx += 1 t1 = time.time() print("t1-t0", t1 - t0) return 1
census_us_county_gdf = census_us_county_gdf[census_us_county_gdf.STATEFP != "02"] census_us_county_gdf = census_us_county_gdf[ ["STATEFP", "COUNTYFP", "GEOID", "NAME", "geometry"] ] # 15005, Kalawao County has 86 people is combined with 15009, # Maui County population 167,417 in the eleciton results # to match the election results, we combine them here under Maui poly = shp.ops.cascaded_union( census_us_county_gdf[census_us_county_gdf.GEOID.isin(["15005", "15009"])][ "geometry" ] ) census_us_county_gdf.loc[ (census_us_county_gdf.GEOID == "15009"), "geometry" ] = gpd.GeoSeries(poly) census_us_county_gdf = census_us_county_gdf.drop( census_us_county_gdf[census_us_county_gdf.GEOID == "15005"].index ) ak_shapefiles_path = pkg_resources.resource_filename('op_verification', "data/county_shapefiles/2013-HD-ProclamationPlan") alaska_districts = gpd.read_file(ak_shapefiles_path) alaska_districts["STATEFP"] = "02" alaska_districts["COUNTYFP"] = alaska_districts["District_N"].apply( lambda x: x.zfill(3) ) alaska_districts["GEOID"] = alaska_districts["STATEFP"].map(str) + alaska_districts[ "COUNTYFP" ].map(str) alaska_districts["NAME"] = alaska_districts["District_N"].apply( lambda x: "district " + x
def network_false_nodes(gdf): """ Check topology of street network and eliminate nodes of degree 2 by joining affected edges. Parameters ---------- gdf : GeoDataFrame GeoDataFrame containg edge representation of street network. Returns ------- gdf : GeoDataFrame """ streets = gdf.copy().explode() streets.reset_index(inplace=True) sindex = streets.sindex false_points = [] print("Identifying false points...") for idx, row in tqdm(streets.iterrows(), total=streets.shape[0]): line = row["geometry"] l_coords = list(line.coords) # network_w = network.drop(idx, axis=0)['geometry'] # ensure that it wont intersect itself start = Point(l_coords[0]) end = Point(l_coords[-1]) # find out whether ends of the line are connected or not possible_first_index = list(sindex.intersection(start.bounds)) possible_first_matches = streets.iloc[possible_first_index] possible_first_matches_clean = possible_first_matches.drop(idx, axis=0) real_first_matches = possible_first_matches_clean[ possible_first_matches_clean.intersects(start)] possible_second_index = list(sindex.intersection(end.bounds)) possible_second_matches = streets.iloc[possible_second_index] possible_second_matches_clean = possible_second_matches.drop(idx, axis=0) real_second_matches = possible_second_matches_clean[ possible_second_matches_clean.intersects(end)] if len(real_first_matches) == 1: false_points.append(start) if len(real_second_matches) == 1: false_points.append(end) false_xy = [] for p in false_points: false_xy.append([p.x, p.y]) false_xy_unique = [list(x) for x in set(tuple(x) for x in false_xy)] false_unique = [] for p in false_xy_unique: false_unique.append(Point(p[0], p[1])) geoms = streets.geometry print("Merging segments...") for point in tqdm(false_unique): matches = list(geoms[geoms.intersects(point)].index) idx = max(geoms.index) + 1 try: multiline = geoms[matches[0]].union(geoms[matches[1]]) linestring = shapely.ops.linemerge(multiline) geoms = geoms.append(gpd.GeoSeries(linestring, index=[idx])) geoms = geoms.drop(matches) except IndexError: import warnings warnings.warn( "An exception during merging occured. Lines at point [{x}, {y}] were not merged." .format(x=point.x, y=point.y)) geoms_gdf = gpd.GeoDataFrame(geometry=geoms) geoms_gdf.crs = streets.crs streets = geoms_gdf.explode().reset_index(drop=True) return streets
def get_reference_sl(metadata, settings): """ Allows the user to manually digitize a reference shoreline that is used seed the shoreline detection algorithm. The reference shoreline helps to detect the outliers, making the shoreline detection more robust. KV WRL 2018 Arguments: ----------- metadata: dict contains all the information about the satellite images that were downloaded settings: dict with the following keys 'inputs': dict input parameters (sitename, filepath, polygon, dates, sat_list) 'cloud_thresh': float value between 0 and 1 indicating the maximum cloud fraction in the cropped image that is accepted 'cloud_mask_issue': boolean True if there is an issue with the cloud mask and sand pixels are erroneously being masked on the images 'output_epsg': int output spatial reference system as EPSG code Returns: ----------- reference_shoreline: np.array coordinates of the reference shoreline that was manually digitized. This is also saved as a .pkl and .geojson file. """ sitename = settings['inputs']['sitename'] filepath_data = settings['inputs']['filepath'] pts_coords = [] # check if reference shoreline already exists in the corresponding folder filepath = os.path.join(filepath_data, sitename) filename = sitename + '_reference_shoreline.pkl' # if it exist, load it and return it if filename in os.listdir(filepath): print('Reference shoreline already exists and was loaded') with open( os.path.join(filepath, sitename + '_reference_shoreline.pkl'), 'rb') as f: refsl = pickle.load(f) return refsl # otherwise get the user to manually digitise a shoreline on S2, L8 or L5 images (no L7 because of scan line error) else: # first try to use S2 images (10m res for manually digitizing the reference shoreline) if 'S2' in metadata.keys(): satname = 'S2' filepath = SDS_tools.get_filepath(settings['inputs'], satname) filenames = metadata[satname]['filenames'] # if no S2 images, try L8 (15m res in the RGB with pansharpening) elif not 'S2' in metadata.keys() and 'L8' in metadata.keys(): satname = 'L8' filepath = SDS_tools.get_filepath(settings['inputs'], satname) filenames = metadata[satname]['filenames'] # if no S2 images and no L8, use L5 images (L7 images have black diagonal bands making it # hard to manually digitize a shoreline) elif not 'S2' in metadata.keys() and not 'L8' in metadata.keys( ) and 'L5' in metadata.keys(): satname = 'L5' filepath = SDS_tools.get_filepath(settings['inputs'], satname) filenames = metadata[satname]['filenames'] else: raise Exception( 'You cannot digitize the shoreline on L7 images (because of gaps in the images), add another L8, S2 or L5 to your dataset.' ) # create figure fig, ax = plt.subplots(1, 1, figsize=[18, 9], tight_layout=True) mng = plt.get_current_fig_manager() mng.window.showMaximized() # loop trhough the images for i in range(len(filenames)): # read image fn = SDS_tools.get_filenames(filenames[i], filepath, satname) im_ms, georef, cloud_mask, im_extra, im_QA, im_nodata = preprocess_single( fn, satname, settings['cloud_mask_issue']) # calculate cloud cover cloud_cover = np.divide( sum(sum(cloud_mask.astype(int))), (cloud_mask.shape[0] * cloud_mask.shape[1])) # skip image if cloud cover is above threshold if cloud_cover > settings['cloud_thresh']: continue # rescale image intensity for display purposes im_RGB = rescale_image_intensity(im_ms[:, :, [2, 1, 0]], cloud_mask, 99.9) # plot the image RGB on a figure ax.axis('off') ax.imshow(im_RGB) # decide if the image if good enough for digitizing the shoreline ax.set_title( 'Press <right arrow> if image is clear enough to digitize the shoreline.\n' + 'If the image is cloudy press <left arrow> to get another image', fontsize=14) # set a key event to accept/reject the detections (see https://stackoverflow.com/a/15033071) # this variable needs to be immuatable so we can access it after the keypress event skip_image = False key_event = {} def press(event): # store what key was pressed in the dictionary key_event['pressed'] = event.key # let the user press a key, right arrow to keep the image, left arrow to skip it # to break the loop the user can press 'escape' while True: btn_keep = plt.text(1.1, 0.9, 'keep ⇨', size=12, ha="right", va="top", transform=ax.transAxes, bbox=dict(boxstyle="square", ec='k', fc='w')) btn_skip = plt.text(-0.1, 0.9, '⇦ skip', size=12, ha="left", va="top", transform=ax.transAxes, bbox=dict(boxstyle="square", ec='k', fc='w')) btn_esc = plt.text(0.5, 0, '<esc> to quit', size=12, ha="center", va="top", transform=ax.transAxes, bbox=dict(boxstyle="square", ec='k', fc='w')) plt.draw() fig.canvas.mpl_connect('key_press_event', press) plt.waitforbuttonpress() # after button is pressed, remove the buttons btn_skip.remove() btn_keep.remove() btn_esc.remove() # keep/skip image according to the pressed key, 'escape' to break the loop if key_event.get('pressed') == 'right': skip_image = False break elif key_event.get('pressed') == 'left': skip_image = True break elif key_event.get('pressed') == 'escape': plt.close() raise StopIteration( 'User cancelled checking shoreline detection') else: plt.waitforbuttonpress() if skip_image: ax.clear() continue else: # create two new buttons add_button = plt.text(0, 0.9, 'add', size=16, ha="left", va="top", transform=plt.gca().transAxes, bbox=dict(boxstyle="square", ec='k', fc='w')) end_button = plt.text(1, 0.9, 'end', size=16, ha="right", va="top", transform=plt.gca().transAxes, bbox=dict(boxstyle="square", ec='k', fc='w')) # add multiple reference shorelines (until user clicks on <end> button) pts_sl = np.expand_dims(np.array([np.nan, np.nan]), axis=0) geoms = [] while 1: add_button.set_visible(False) end_button.set_visible(False) # update title (instructions) ax.set_title( 'Click points along the shoreline (enough points to capture the beach curvature).\n' + 'Start at one end of the beach.\n' + 'When finished digitizing, click <ENTER>', fontsize=14) plt.draw() # let user click on the shoreline pts = ginput(n=50000, timeout=1e9, show_clicks=True) pts_pix = np.array(pts) # convert pixel coordinates to world coordinates pts_world = SDS_tools.convert_pix2world( pts_pix[:, [1, 0]], georef) # interpolate between points clicked by the user (1m resolution) pts_world_interp = np.expand_dims(np.array( [np.nan, np.nan]), axis=0) for k in range(len(pts_world) - 1): pt_dist = np.linalg.norm(pts_world[k, :] - pts_world[k + 1, :]) xvals = np.arange(0, pt_dist) yvals = np.zeros(len(xvals)) pt_coords = np.zeros((len(xvals), 2)) pt_coords[:, 0] = xvals pt_coords[:, 1] = yvals phi = 0 deltax = pts_world[k + 1, 0] - pts_world[k, 0] deltay = pts_world[k + 1, 1] - pts_world[k, 1] phi = np.pi / 2 - np.math.atan2(deltax, deltay) tf = transform.EuclideanTransform( rotation=phi, translation=pts_world[k, :]) pts_world_interp = np.append(pts_world_interp, tf(pt_coords), axis=0) pts_world_interp = np.delete(pts_world_interp, 0, axis=0) # save as geometry (to create .geojson file later) geoms.append(geometry.LineString(pts_world_interp)) # convert to pixel coordinates and plot pts_pix_interp = SDS_tools.convert_world2pix( pts_world_interp, georef) pts_sl = np.append(pts_sl, pts_world_interp, axis=0) ax.plot(pts_pix_interp[:, 0], pts_pix_interp[:, 1], 'r--') ax.plot(pts_pix_interp[0, 0], pts_pix_interp[0, 1], 'ko') ax.plot(pts_pix_interp[-1, 0], pts_pix_interp[-1, 1], 'ko') # update title and buttons add_button.set_visible(True) end_button.set_visible(True) ax.set_title( 'click on <add> to digitize another shoreline or on <end> to finish and save the shoreline(s)', fontsize=14) plt.draw() # let the user click again (<add> another shoreline or <end>) pt_input = ginput(n=1, timeout=1e9, show_clicks=False) pt_input = np.array(pt_input) # if user clicks on <end>, save the points and break the loop if pt_input[0][0] > im_ms.shape[1] / 2: add_button.set_visible(False) end_button.set_visible(False) plt.title('Reference shoreline saved as ' + sitename + '_reference_shoreline.pkl and ' + sitename + '_reference_shoreline.geojson') plt.draw() ginput(n=1, timeout=3, show_clicks=False) plt.close() break pts_sl = np.delete(pts_sl, 0, axis=0) # convert world image coordinates to user-defined coordinate system image_epsg = metadata[satname]['epsg'][i] pts_coords = SDS_tools.convert_epsg(pts_sl, image_epsg, settings['output_epsg']) # save the reference shoreline as .pkl filepath = os.path.join(filepath_data, sitename) with open( os.path.join(filepath, sitename + '_reference_shoreline.pkl'), 'wb') as f: pickle.dump(pts_coords, f) # also store as .geojson in case user wants to drag-and-drop on GIS for verification for k, line in enumerate(geoms): gdf = gpd.GeoDataFrame(geometry=gpd.GeoSeries(line)) gdf.index = [k] gdf.loc[k, 'name'] = 'reference shoreline ' + str(k + 1) # store into geodataframe if k == 0: gdf_all = gdf else: gdf_all = gdf_all.append(gdf) gdf_all.crs = {'init': 'epsg:' + str(image_epsg)} # convert from image_epsg to user-defined coordinate system gdf_all = gdf_all.to_crs( {'init': 'epsg:' + str(settings['output_epsg'])}) # save as geojson gdf_all.to_file(os.path.join( filepath, sitename + '_reference_shoreline.geojson'), driver='GeoJSON', encoding='utf-8') print('Reference shoreline has been saved in ' + filepath) break # check if a shoreline was digitised if len(pts_coords) == 0: raise Exception( 'No cloud free images are available to digitise the reference shoreline,' + 'download more images and try again') return pts_coords
CHappyhill """ import os if not os.path.exists('data'): os.makedirs('data') if not os.path.exists('output'): os.makedirs('output') import matplotlib.pyplot as plt # #import os #os.getcwd() from shapely.geometry import Point import geopandas as gpd wageningenCampus = Point([173994.1578792833, 444133.60329471016]) print(type(wageningenCampus)) gs = gpd.GeoSeries([wageningenCampus]) print(type(gs), len(gs)) from shapely.wkt import loads WKTstring = 'POINT(173994.1578792833 444133.6032947102)' gs = gpd.GeoSeries([loads(WKTstring)]) gs.crs = "EPSG:28992" # specify manually the projection gsBuffer = gs.buffer(100) print(gs.geometry) print(gsBuffer.geometry) import pandas as pd data = { 'name': ['a', 'b', 'c'], 'x': [173994.1578792833, 173974.1578792833, 173910.1578792833], 'y': [444135.6032947102, 444186.6032947102, 444111.6032947102]
Tests that raw data inputs function correctly. """ import sys sys.path.insert(0, '../') import geoplot as gplt import unittest import geopandas as gpd import matplotlib.pyplot as plt from shapely.geometry import Point, LineString import numpy as np import pandas as pd # Point-type DataFrame input. list_gaussian_points = gplt.utils.gaussian_points(n=4) series_gaussian_points = gpd.GeoSeries(list_gaussian_points) dataframe_gaussian_points = gpd.GeoDataFrame( geometry=series_gaussian_points).assign(hue_var=[1, 2, 3, 4]) # Polygon-type DataFrame input. list_gaussian_polys = gplt.utils.gaussian_polygons( gplt.utils.gaussian_points(n=1000), n=2).append( gplt.utils.gaussian_multi_polygons(gplt.utils.gaussian_points(n=1000), n=2)) series_gaussian_polys = gpd.GeoSeries(list_gaussian_polys) dataframe_gaussian_polys = gpd.GeoDataFrame( geometry=series_gaussian_polys).assign(hue_var=[1, 2, 3, 4]) # Hue input. list_hue_values = [1, 2, 3, 4] series_hue_values = pd.Series(list_hue_values)
def func(data, x, y, z): return geopandas.GeoSeries( geopandas.points_from_xy(data[x], data[y], data[z] if z in df.columns else None), index=data.index, )
def street_blocks_to_parking_spots(): """ Read in a shapefile containing the RPP blocks and save out a GeoJSON containing one point for every possible parking space on each block. """ rpp = gpd.read_file( 'input/Residential_Parking_Permit_Blocks-shp/Residential_Parking_Permit_Blocks.shp' ) rpp = drop_duplicate_geometries(rpp, print_counts=True) # rpp = rpp.to_crs(maryland_crs) # Rename block sides rpp['block_side'] = rpp['BLK_SIDE'].map({ 'AO': 'address only', 'B': 'both sides', 'V': 'even side', 'O': 'odd side', 'S': 'single building' }) parking_dict = {} street_width = 10 # meters street_width_radians = 0.00003 for idx, street in tqdm(rpp.iterrows(), total=len(rpp)): if street['OBJECTID'] == 1973107: # Multiple line segments on this street # rpp[rpp['OBJECTID'] == 1973107] continue # if idx > 10: # break # if street['OBJECTID'] not in [ # 1971375 # California # , 1972138 # 19th # , 1969724 # Mintwood Place # ]: continue try: street_points = cut_street_into_points(street) except Exception as e: bad_objectid = street['OBJECTID'] print(f'OBJECTID {bad_objectid} causes error: {e}') continue start_coord = street['geometry'].coords[0] start_lon = start_coord[0] start_lat = start_coord[1] end_coord = street['geometry'].coords[-1] end_lon = end_coord[0] end_lat = end_coord[1] geodesic = pyproj.Geod(ellps='WGS84') street_heading, _, _ = geodesic.inv(start_lon, start_lat, end_lon, end_lat) heading_right = (street_heading + 90) offset_right_x = math.sin( math.radians(heading_right)) * street_width_radians offset_right_y = math.cos( math.radians(heading_right)) * street_width_radians heading_left = (street_heading - 90) offset_left_x = math.sin( math.radians(heading_left)) * street_width_radians offset_left_y = math.cos( math.radians(heading_left)) * street_width_radians street_series = gpd.GeoSeries(street_points) # todo: determine which is side is even and odd based off of heading if street['block_side'] == 'both sides': street_series_odd = street_series.translate(xoff=offset_right_x, yoff=offset_right_y) street_series_even = street_series.translate(xoff=offset_left_x, yoff=offset_left_y) street_series_combined = pd.concat( [street_series_odd, street_series_even], ignore_index=True) elif street['block_side'] == 'even side': street_series_combined = street_series.translate( xoff=offset_left_x, yoff=offset_left_y) elif street['block_side'] == 'odd side': street_series_combined = street_series.translate( xoff=offset_right_x, yoff=offset_right_y) else: continue temp_df = pd.DataFrame() temp_df['geometry'] = street_series_combined temp_df['source_street_objectid'] = street['OBJECTID'] temp_df['block_side'] = street['block_side'] parking_dict[street['OBJECTID']] = temp_df df_dict = {} for d in parking_dict: df_dict[d] = pd.DataFrame(parking_dict[d], columns=['geometry']) parking_df = pd.concat(df_dict).reset_index() parking_df.rename(columns={'level_0': 'source_street_objectid'}, inplace=True) parking_df.drop('level_1', axis=1, inplace=True) parking_df = parking_df.reset_index() parking_df['parking_spot_id'] = parking_df['index'] + 1 parking_df.drop('index', axis=1, inplace=True) parking_gdf = gpd.GeoDataFrame(parking_df) parking_gdf_output = parking_gdf #.to_crs(output_crs) parking_gdf_output.to_file('output/parking_spots.geojson', driver='GeoJSON') print('Number of parking spots: {:,}'.format(len(parking_gdf)))
match3=gpd.GeoDataFrame(match3, geometry='geometry') match3=match3.to_crs('epsg:4326') match3.plot() os.chdir(r'D:\부동산 빅데이터 분석 스터디\상권 프로젝트 수정') import folium lat = 37.55 long = 127 m = folium.Map([lat,long],zoom_start=11, title='Competitive') for _, r in match3.iterrows(): # simplify를 쓰나 안쓰나 차이는 별로 없으나 geopandas 원문에서 쓰라고 하니 써주자 sim_geo = gpd.GeoSeries(r['geometry']).simplify(tolerance=0.001) geo_j = sim_geo.to_json() geo_j = folium.GeoJson(data=geo_j, style_function = lambda x: {'fillColor': 'blue'}) # folium.Popup(r['법정동명']).add_to(geo_j) geo_j.add_to(m) m.save('competitive.html') # 최종 결과 파일 저장 match3.to_csv(r'D:\부동산 빅데이터 분석 스터디\상권 프로젝트 수정\최종 매칭.csv', encoding='cp949', sep = ',')
def test_AreaTablesAreaInterpolate(self): polys1 = gpd.GeoSeries([ Polygon([(0, 0), (10, 0), (10, 5), (0, 5)]), Polygon([(0, 5), (0, 10), (10, 10), (10, 5)]) ]) polys2 = gpd.GeoSeries([ Polygon([(0, 0), (5, 0), (5, 7), (0, 7)]), Polygon([(5, 0), (5, 10), (10, 10), (10, 0)]), Polygon([(0, 7), (0, 10), (5, 10), (5, 7)]) ]) df1 = gpd.GeoDataFrame({'geometry': polys1}) df2 = gpd.GeoDataFrame({'geometry': polys2}) df1['population'] = [500, 200] df1['pci'] = [75, 100] df1['income'] = df1['population'] * df1['pci'] res_union = gpd.overlay(df1, df2, how='union') result_area = area_tables(df1, res_union) result_area_binning = area_tables_binning(df1, res_union) np.testing.assert_almost_equal(result_area[0], np.array([[25., 25., 0., 0., 0.], [0., 0., 10., 15., 25.]]), decimal=3) np.testing.assert_almost_equal(result_area[1], np.array([[1., 0., 0., 0., 0.], [0., 0., 1., 0., 0.], [0., 1., 0., 0., 0.], [0., 0., 0., 0., 1.], [0., 0., 0., 1., 0.]]), decimal=3) np.testing.assert_almost_equal(result_area_binning.toarray(), np.array([[25., 0., 25., 0., 0.], [0., 10., 0., 25., 15.]]), decimal=3) result_inte = area_interpolate( df1, res_union, extensive_variables=['population', 'income'], intensive_variables=['pci']) result_inte_binning = area_interpolate_binning( df1, res_union, extensive_variables=['population', 'income'], intensive_variables=['pci']) np.testing.assert_almost_equal(result_inte[0], np.array([[250., 18750.], [40., 4000.], [250., 18750.], [100., 10000.], [60., 6000.]]), decimal=3) np.testing.assert_almost_equal(result_inte[1], np.array([[75.], [100.], [75.], [100.], [100.]]), decimal=3) np.testing.assert_almost_equal(result_inte_binning[0], np.array([[250., 18750.], [39.99999762, 3999.99976158], [250., 18750.], [100., 10000.], [59.99999642, 5999.99964237]]), decimal=3) np.testing.assert_almost_equal(result_inte_binning[1], np.array([[75.], [100.], [75.], [100.], [100.]]), decimal=3)
def consolidate_intersections( G, tolerance=10, rebuild_graph=True, dead_ends=False, update_edge_lengths=True ): """ Consolidate intersections comprising clusters of nearby nodes. Merging nodes and return either their centroids or a rebuilt graph with consolidated intersections and reconnected edge geometries. The tolerance argument should be adjusted to approximately match street design standards in the specific street network, and you should always use a projected graph to work in meaningful and consistent units like meters. Divided roads are often represented by separate centerline edges. The intersection of two divided roads thus creates 4 nodes, representing where each edge intersects a perpendicular edge. These 4 nodes represent a single intersection in the real world. This function consolidates them up by buffering them to an arbitrary distance, merging overlapping buffers, and taking their centroid. For best results, the tolerance argument should be adjusted to approximately match street design standards in the specific street network. Parameters ---------- G : networkx.MultiDiGraph a projected graph tolerance : float nodes within this distance (in graph's geometry's units) will be dissolved into a single intersection rebuild_graph : bool if True, use consolidate_intersections_rebuild_graph to consolidate the intersections and rebuild the graph, then return as networkx.MultiDiGraph. if False, just return the consolidated intersection points as a geopandas.GeoSeries (faster than rebuilding graph) dead_ends : bool if False, discard dead-end nodes to return only street-intersection points update_edge_lengths : bool just passed to consolidate_intersections_rebuild_graph. if True, update the length attribute of edges reconnected to a new merged node; if False, just retain the original edge length. Returns ------- networkx.MultiDiGraph or geopandas.GeoSeries if rebuild_graph=True, returns MultiDiGraph with consolidated intersections and reconnected edge geometries. if rebuild_graph=False, returns GeoSeries of shapely Points representing the centroids of street intersections """ # if dead_ends is False, discard dead-end nodes to retain only intersections if not dead_ends: if "streets_per_node" in G.graph: streets_per_node = G.graph["streets_per_node"] else: streets_per_node = utils_graph.count_streets_per_node(G) dead_end_nodes = [node for node, count in streets_per_node.items() if count <= 1] G = G.copy() G.remove_nodes_from(dead_end_nodes) if rebuild_graph: return _consolidate_intersections_rebuild_graph( G=G, tolerance=tolerance, update_edge_lengths=update_edge_lengths ) else: # create a GeoDataFrame of nodes, buffer to passed-in distance, merge overlaps gdf_nodes = utils_graph.graph_to_gdfs(G, edges=False) buffered_nodes = gdf_nodes.buffer(tolerance).unary_union if isinstance(buffered_nodes, Polygon): # if only a single node results, make iterable to convert to GeoSeries buffered_nodes = [buffered_nodes] # get the centroids of the merged intersection polygons unified_intersections = gpd.GeoSeries(list(buffered_nodes)) intersection_centroids = unified_intersections.centroid return intersection_centroids
rGrid = makePlotCube( iris.coord_systems.RotatedGeogCS(90.0, 180.0, 0), 0.05, 110 - 40 * 0.5, 110 + 40 * 0.5, 30 - 15 * 0.5, 30 + 15 * 0.5, ) # Convert the field to an in=1, out=0 mask lats = rGrid.coord(axis='Y').points lons = rGrid.coord(axis='X').points [lon2d, lat2d] = numpy.meshgrid(lons, lats) lon2 = lon2d.reshape(-1) # to 1d for iteration lat2 = lat2d.reshape(-1) mask = [] for lat, lon in zip(lat2, lon2): this_point = geopandas.GeoSeries([Point(lon, lat)], crs=yangtze.crs, index=yangtze.index) res = yangtze.geometry.contains(this_point) mask.append(res.values[0]) mask = numpy.array(mask).reshape(lon2d.shape) mask = mask * 1 rGrid.data = mask iris.save(rGrid, "mask.plot.nc") #pickle.dump(rGrid,open('mask.plot.pkl','wb'))
data = [('Nevada', geometry_nevada), ('Colorado', geometry_colorado), ('Wyoming', geometry_wyoming)] cursor.executemany('insert into TestStates values (:state, :obj)', data) # We now have test geometries in Oracle Spatial (SDO_GEOMETRY) and will next # bring them back into Python to analyze with GeoPandas. GeoPandas is able to # consume geometries in the Well Known Text (WKT) and Well Known Binary (WKB) # formats. Oracle database includes utility functions to return SDO_GEOMETRY as # both WKT and WKB. Therefore we use that utility function in the query below # to provide results in a format readily consumable by GeoPandas. These utility # functions were introduced in Oracle 10g. We use WKB here; however the same # process applies for WKT. cursor.execute(""" SELECT state, sdo_util.to_wkbgeometry(geometry) FROM TestStates""") gdf = gpd.GeoDataFrame(cursor.fetchall(), columns=['state', 'wkbgeometry']) # create GeoSeries to replace the WKB geometry column gdf['geometry'] = gpd.GeoSeries(gdf['wkbgeometry'].apply(lambda x: loads(x))) del gdf['wkbgeometry'] # display the GeoDataFrame print() print(gdf) # perform a basic GeoPandas operation (unary_union) # to combine the 3 adjacent states into 1 geometry print() print("GeoPandas combining the 3 geometries into a single geometry...") print(gdf.unary_union)
#Subset to bounding box of basin demdata = rio.open('Mads_1.tif') show((demdata, 1), cmap='terrain') print(demdata.crs, basin.crs) # print(basin.boundary.to_json()) basin_coords = getFeatures(basin) minx, miny, maxx, maxy = basin.total_bounds p1 = geometry.Point(minx, miny) p2 = geometry.Point(minx, maxy) p3 = geometry.Point(maxx, maxy) p4 = geometry.Point(maxx, miny) pointlist = [p1, p2, p3, p4, p1] clipBnd = geometry.Polygon(pointlist) clip = gpd.GeoSeries(clipBnd) out_img, out_transform = mask(dataset=demdata, shapes=clip, crop=True) out_meta = demdata.meta out_meta.update({ "driver": "GTiff", "height": out_img.shape[1], "width": out_img.shape[2], "transform": out_transform }) out_raster = rio.open('Mads_clip_1.tif', "w", **out_meta) out_raster.write(out_img) out_raster.close() demdata_clip = rio.open('Mads_clip_1.tif') with rio.open('Mads_clip_1.tif') as dataset1:
def remove_false_nodes(gdf): """ Clean topology of existing LineString geometry by removal of nodes of degree 2. Parameters ---------- gdf : GeoDataFrame, GeoSeries, array of pygeos geometries (Multi)LineString data of street network Returns ------- gdf : GeoDataFrame, GeoSeries See also -------- momepy.extend_lines momepy.close_gaps """ if isinstance(gdf, (gpd.GeoDataFrame, gpd.GeoSeries)): # explode to avoid MultiLineStrings # double reset index due to the bug in GeoPandas explode df = gdf.reset_index(drop=True).explode().reset_index(drop=True) # get underlying pygeos geometry geom = df.geometry.values.data else: geom = gdf # extract array of coordinates and number per geometry coords = pygeos.get_coordinates(geom) indices = pygeos.get_num_coordinates(geom) # generate a list of start and end coordinates and create point geometries edges = [0] i = 0 for ind in indices: ix = i + ind edges.append(ix - 1) edges.append(ix) i = ix edges = edges[:-1] points = pygeos.points(np.unique(coords[edges], axis=0)) # query LineString geometry to identify points intersecting 2 geometries tree = pygeos.STRtree(geom) inp, res = tree.query_bulk(points, predicate="intersects") unique, counts = np.unique(inp, return_counts=True) merge = res[np.isin(inp, unique[counts == 2])] if len(merge) > 0: # filter duplications and create a dictionary with indication of components to # be merged together dups = [item for item, count in collections.Counter(merge).items() if count > 1] split = np.split(merge, len(merge) / 2) components = {} for i, a in enumerate(split): if a[0] in dups or a[1] in dups: if a[0] in components.keys(): i = components[a[0]] elif a[1] in components.keys(): i = components[a[1]] components[a[0]] = i components[a[1]] = i # iterate through components and create new geometries new = [] for c in set(components.values()): keys = [] for item in components.items(): if item[1] == c: keys.append(item[0]) new.append(pygeos.line_merge(pygeos.union_all(geom[keys]))) # remove incorrect geometries and append fixed versions df = df.drop(merge) final = gpd.GeoSeries(new).explode().reset_index(drop=True) if isinstance(gdf, gpd.GeoDataFrame): return df.append( gpd.GeoDataFrame({df.geometry.name: final}, geometry=df.geometry.name), ignore_index=True, ) return df.append(final, ignore_index=True)
def plot_local_autocorrelation(moran_loc, gdf, attribute, p=0.05, region_column=None, mask=None, mask_color='#636363', quadrant=None, legend=True, scheme='Quantiles', cmap='YlGnBu', figsize=(15, 4), scatter_kwds=None, fitline_kwds=None): ''' Produce three-plot visualisation of Moran Scatteprlot, LISA cluster and Choropleth maps, with Local Moran region and quadrant masking Parameters ---------- moran_loc : esda.moran.Moran_Local instance Values of Moran's Local Autocorrelation Statistic gdf : geopandas dataframe The Dataframe containing information to plot the two maps. attribute : str Column name of attribute which should be depicted in Choropleth map. p : float, optional The p-value threshold for significance. Points and polygons will be colored by significance. Default = 0.05. region_column: string, optional Column name containing mask region of interest. Default = None mask: str, optional Identifier or name of the region to highlight. Default = None mask_color: str, optional Color of mask. Default = '#636363' quadrant : int, optional Quadrant 1-4 in scatterplot masking values in LISA cluster and Choropleth maps. Default = None figsize: tuple, optional W, h of figure. Default = (15,4) legend: boolean, optional If True, legend for maps will be depicted. Default = True scheme: str, optional Name of PySAL classifier to be used. Default = 'Quantiles' cmap: str, optional Name of matplotlib colormap used for plotting the Choropleth. Default = 'YlGnBU' scatter_kwds : keyword arguments, optional Keywords used for creating and designing the scatter points. Default =None. fitline_kwds : keyword arguments, optional Keywords used for creating and designing the moran fitline in the scatterplot. Default =None. Returns ------- fig : Matplotlib figure instance Moran Scatterplot, LISA cluster map and Choropleth. axs : list of Matplotlib axes Lisat of Matplotlib axes plotted. Examples -------- Imports >>> import matplotlib.pyplot as plt >>> import libpysal.api as lp >>> from libpysal import examples >>> import geopandas as gpd >>> from esda.moran import Moran_Local >>> from splot.esda import plot_local_autocorrelation Data preparation and analysis >>> link = examples.get_path('Guerry.shp') >>> gdf = gpd.read_file(link) >>> y = gdf['Donatns'].values >>> w = lp.Queen.from_dataframe(gdf) >>> w.transform = 'r' >>> moran_loc = Moran_Local(y, w) Plotting with quadrant mask and region mask >>> fig = plot_local_autocorrelation(moran_loc, gdf, 'HOVAL', p=0.05, ... region_column='POLYID', ... mask=['1', '2', '3'], quadrant=1) >>> plt.show() ''' fig, axs = plt.subplots(1, 3, figsize=figsize, subplot_kw={'aspect': 'equal'}) # Moran Scatterplot if isinstance (moran_loc, Moran_Local): moran_loc_scatterplot(moran_loc, p=p, ax=axs[0], scatter_kwds=scatter_kwds, fitline_kwds=fitline_kwds) else: moran_loc_bv_scatterplot(moran_loc, p=p, ax=axs[0], scatter_kwds=scatter_kwds, fitline_kwds=fitline_kwds) axs[0].set_aspect('auto') # Lisa cluster map # TODO: Fix legend_kwds: display boxes instead of points lisa_cluster(moran_loc, gdf, p=p, ax=axs[1], legend=legend, legend_kwds={'loc': 'upper left', 'bbox_to_anchor': (0.92, 1.05)}) axs[1].set_aspect('equal') # Choropleth for attribute gdf.plot(column=attribute, scheme=scheme, cmap=cmap, legend=legend, legend_kwds={'loc': 'upper left', 'bbox_to_anchor': (0.92, 1.05)}, ax=axs[2], alpha=1) axs[2].set_axis_off() axs[2].set_aspect('equal') # MASKING QUADRANT VALUES if quadrant is not None: # Quadrant masking in Scatterplot mask_angles = {1: 0, 2: 90, 3: 180, 4: 270} # rectangle angles # We don't want to change the axis data limits, so use the current ones xmin, xmax = axs[0].get_xlim() ymin, ymax = axs[0].get_ylim() # We are rotating, so we start from 0 degrees and # figured out the right dimensions for the rectangles for other angles mask_width = {1: abs(xmax), 2: abs(ymax), 3: abs(xmin), 4: abs(ymin)} mask_height = {1: abs(ymax), 2: abs(xmin), 3: abs(ymin), 4: abs(xmax)} axs[0].add_patch(patches.Rectangle((0, 0), width=mask_width[quadrant], height=mask_height[quadrant], angle=mask_angles[quadrant], color='grey', zorder=-1, alpha=0.8)) # quadrant selection in maps non_quadrant = ~(moran_loc.q == quadrant) mask_quadrant = gdf[non_quadrant] df_quadrant = gdf.iloc[~non_quadrant] union2 = df_quadrant.unary_union.boundary # LISA Cluster mask and cluster boundary mask_quadrant.plot(column=attribute, scheme=scheme, color='white', ax=axs[1], alpha=0.7, zorder=1) gpd.GeoSeries([union2]).plot(linewidth=2, ax=axs[1], color='darkgrey') # CHOROPLETH MASK mask_quadrant.plot(column=attribute, scheme=scheme, color='white', ax=axs[2], alpha=0.7, zorder=1) gpd.GeoSeries([union2]).plot(linewidth=2, ax=axs[2], color='darkgrey') # REGION MASKING if region_column is not None: # masking inside axs[0] or Moran Scatterplot ix = gdf[region_column].isin(mask) df_mask = gdf[ix] x_mask = moran_loc.z[ix] y_mask = lp.lag_spatial(moran_loc.w, moran_loc.z)[ix] axs[0].plot(x_mask, y_mask, color=mask_color, marker='o', markersize=14, alpha=.8, linestyle="None", zorder=-1) # masking inside axs[1] or Lisa cluster map union = df_mask.unary_union.boundary gpd.GeoSeries([union]).plot(linewidth=2, ax=axs[1], color=mask_color) # masking inside axs[2] or Chloropleth gpd.GeoSeries([union]).plot(linewidth=2, ax=axs[2], color=mask_color) return fig, axs
def extract_raster_features(gdf, raster_path, pixel_values=None, nodata=255, n_jobs=-1, collapse_values=False): """Generate a geodataframe from raster data by polygonizing contiguous pixels with the same value using rasterio's features module. Parameters ---------- gdf : geopandas.GeoDataFrame geodataframe defining the area of interest. The input raster will be clipped to the extent of the geodataframe raster_path : str path to raster file, such as downloaded from <https://lcviewer.vito.be/download> pixel_values : list-like, optional subset of pixel values to extract, by default None. If None, this function may generate a very large geodataframe nodata : int, optional pixel value denoting "no data" in input raster n_jobs : int [Optional. Default=-1] Number of processes to run in parallel. If -1, this is set to the number of CPUs available collapse_values : bool, optional If True, multiple values passed to the pixel_values argument are treated as a single type. I.e. polygons will be generated from any contiguous collection of values from pixel_types, instead of unique polygons generated for each value This can dramatically reduce the complexity of the resulting geodataframe a fewer polygons are required to represent the study area. Returns ------- geopandas.GeoDataFrame geodataframe whose rows are the zones extracted by the rasterio.features module. The geometry of each zone is the boundary of a contiguous group of pixels with the same value; the `value` column contains the pixel value of each zone. """ if n_jobs == -1: n_jobs = multiprocessing.cpu_count() with rio.open(raster_path) as src: raster_crs = src.crs.data gdf = gdf.to_crs(raster_crs) geomask = [gdf.unary_union.__geo_interface__] out_image, out_transform = mask( src, geomask, nodata=nodata, crop=True) # clip to AoI using a vector layer if pixel_values: if collapse_values: out_image = np.where( np.isin(out_image, pixel_values), pixel_values[0], out_image) # replace values to generate fewer polys pixel_values = np.isin( out_image, pixel_values) # only include requested pixels shapes = list( features.shapes( out_image, mask=pixel_values, transform=out_transform)) # convert regions to polygons res = list(zip(*shapes)) geoms = pd.Series(res[0], name="geometry").astype(str) pieces = _chunk_dfs(geoms, n_jobs) geoms = pd.concat( Parallel(n_jobs=n_jobs)(delayed(_apply_parser)(i) for i in pieces)) geoms = gpd.GeoSeries(geoms).buffer( 0) # we sometimes get self-intersecting rings vals = pd.Series(res[1], name="value") gdf = gpd.GeoDataFrame(vals, geometry=geoms, crs=raster_crs) if collapse_values: gdf = gdf.drop(columns=["value" ]) # values col is misleading in this case return gdf