def eez(countries, geo_crs, country_shapes, EEZ_gpkg, out_logging=False, distance=0.01): """ Creates offshore shapes by - buffer smooth countryshape (=offset country shape) - and differ that with the offshore shape Leads to for instance a 100m non-build coastline """ if out_logging: _logger.info("Stage 2 of 4: Create offshore shapes") # load data df_eez = load_EEZ(countries, geo_crs, EEZ_gpkg) ret_df = df_eez[["name", "geometry"]] # create unique shape if country is described by multiple shapes for c_code in countries: selection = ret_df.name == c_code n_offshore_shapes = selection.sum() if n_offshore_shapes > 1: # when multiple shapes per country, then merge polygons geom = ret_df[selection].geometry.unary_union ret_df.drop(ret_df[selection].index, inplace=True) ret_df = ret_df.append({ "name": c_code, "geometry": geom }, ignore_index=True) ret_df = ret_df.set_index("name")["geometry"].map( lambda x: _simplify_polys(x, minarea=0.001, tolerance=0.0001)) ret_df = ret_df.apply(lambda x: make_valid(x)) country_shapes = country_shapes.apply(lambda x: make_valid(x)) country_shapes_with_buffer = country_shapes.buffer(distance) ret_df_new = ret_df.difference(country_shapes_with_buffer) # repeat to simplify after the buffer correction ret_df_new = ret_df_new.map(lambda x: x if x is None else _simplify_polys( x, minarea=0.001, tolerance=0.0001)) ret_df_new = ret_df_new.apply(lambda x: x if x is None else make_valid(x)) # Drops empty geometry ret_df = ret_df_new.dropna() return ret_df
def make_valid(element, drop_z=True): """ Attempt to make a geometry valid. Returns `None` if the geometry cannot be made valid. Example: .. code-block:: python p | beam.Map(geobeam.fn.make_valid) | beam.Map(geobeam.fn.filter_invalid) """ from shapely.geometry import shape from shapely import validation, wkb props, geom = element shape_geom = shape(geom) if not shape_geom.is_valid: shape_geom = validation.make_valid(shape_geom) if drop_z and shape_geom.has_z: shape_geom = wkb.loads(wkb.dumps(shape_geom, output_dimension=2)) if shape_geom is not None: return (props, shape_geom.__geo_interface__) else: return None
def make_geom_valid(geometry): """ Transform invalid geometries to valid geometries Parameters ---------- geometry: the geometry Returns ------- """ if geometry is None: return geometry geom = shape(geometry) if not geom.is_valid: geom = make_valid(geom) # Keep only polygons and multipolygons if isinstance(geom, GeometryCollection): keep = [] for g in geom.geoms: if isinstance(g, Polygon) or isinstance(g, MultiPolygon): keep.append(g) geom = cascaded_union(keep) if not (isinstance(geom, Polygon) or isinstance(geom, MultiPolygon)): raise ValueError("Geometry not Polygon: {}".format(type(geom))) geometry = mapping(geom) return geometry
def buildMultiPolyFromOffset( cls, multi_offset: any) -> shapely.geometry.MultiPolygon: ''' offset is the direct result of an parallel_offset operation -> can be of various type We filter the degenerated lines ''' polygons = [] for offset in multi_offset: lines_ok = [] if offset.geom_type == 'LineString': if len(list(offset.coords)) <= 2: pass else: lines_ok.append(offset) elif offset.geom_type == 'MultiLineString': for geom in offset.geoms: if geom.geom_type == 'LineString': if len(list(geom.coords)) <= 2: continue lines_ok.append(geom) elif offset.geom_type == 'GeometryCollection': for geom in offset.geoms: if geom.geom_type == 'LineString': if len(list(geom.coords)) <= 2: continue lines_ok.append(geom) for line_ok in lines_ok: polygon = shapely.geometry.Polygon(line_ok) if not polygon.is_valid: polygon = cls.fixSimplePolygon(polygon) #print("linestring -> poly -> fixed :", polygon.is_valid) polygons.append(polygon) multipoly = shapely.geometry.MultiPolygon(polygons) if not multipoly.is_valid: # two polygon which crosses are not valid #cls.MatplotlibDisplay("multipoly", multipoly, force=True) multipoly = make_valid(multipoly) # this makes their intersection(s) on common point(s) print("multipoly VALID ?", multipoly.is_valid) #cls.MatplotlibDisplay("multipoly", multipoly, force=True) # ensure orientation multipoly = ShapelyUtils.orientMultiPolygon(multipoly) print("multipoly VALID ?", multipoly.is_valid) return multipoly
def multiLineToMultiPoly( cls, multiline: shapely.geometry.MultiLineString ) -> shapely.geometry.MultiPolygon: ''' ''' polys = [] for line in multiline.geoms: poly = shapely.geometry.Polygon(line) polys.append(poly) multipoly = shapely.geometry.MultiPolygon(polys) multipoly = make_valid(multipoly) return multipoly
def roi_to_geometry(dict_rois_contours): """ Convert ROI contour data in each image slice to a geometry object :param dict_rois_contours: A dictionary with key-value pair {slice-uid: contour sequence} :return: A dictionary with key-value pair {slice-uid: Geometry object} """ dict_geometry = {} for slice_uid, contour_sequence in dict_rois_contours.items(): # convert contour data to polygon omitting data with less than 3 points polygon_list = [ Polygon(contour_data) for contour_data in contour_sequence if len(contour_data) >= 3 ] result_geometry = make_valid(MultiPolygon(polygon_list)) dict_geometry[slice_uid] = result_geometry return dict_geometry
def fixSimplePolygon( cls, polygon: shapely.geometry.Polygon) -> shapely.geometry.Polygon: ''' ''' valid = make_valid(polygon) if valid.geom_type == 'Polygon': return valid elif valid.geom_type == 'MultiPolygon': # take the largest one! CHECKME largest_area = -1 largest_poly = None for poly in valid.geoms: area = poly.area if area > largest_area: largest_area = area largest_poly = poly return largest_poly elif valid.geom_type == 'GeometryCollection': # shit - FIXME # take the largest Polygon largest_area = -1 largest_poly = None for geom in valid.geoms: if geom.geom_type == 'Polygon': area = geom.area if area > largest_area: largest_area = area largest_poly = geom return largest_poly return None
from shapely.geometry import Polygon from shapely.validation import make_valid from matplotlib import pyplot from descartes.patch import PolygonPatch from figures import SIZE, BLUE, RED, set_limits invalid_poly = Polygon([(0, 0), (0, 2), (1, 1), (2, 2), (2, 0), (1, 1), (0, 0)]) valid_poly = make_valid(invalid_poly) fig = pyplot.figure(1, figsize=SIZE, dpi=90) fig.set_frameon(True) invalid_ax = fig.add_subplot(121) patch = PolygonPatch(invalid_poly, facecolor=BLUE, edgecolor=BLUE, alpha=0.5, zorder=2) invalid_ax.add_patch(patch) set_limits(invalid_ax, -1, 3, -1, 3) valid_ax = fig.add_subplot(122) patch = PolygonPatch(valid_poly[0], facecolor=BLUE, edgecolor=BLUE, alpha=0.5, zorder=2) valid_ax.add_patch(patch) patch = PolygonPatch(valid_poly[1], facecolor=RED, edgecolor=RED, alpha=0.5, zorder=2) valid_ax.add_patch(patch) set_limits(valid_ax, -1, 3, -1, 3)
def test_make_valid_invalid_input(): geom = Polygon([(0, 0), (0, 2), (1, 1), (2, 2), (2, 0), (1, 1), (0, 0)]) valid = make_valid(geom) assert len(valid) == 2 assert all(geom.type == 'Polygon' for geom in valid.geoms)
def test_make_valid_input(): geom = Polygon([(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)]) valid = make_valid(geom) assert id(valid) == id(geom)
logger.info(f"Type: {source['properties']['type']}") # Check geometry if "geometry" in source: geom = shape(source["geometry"]) # type: ignore # Check if geometry is a valid (e.g. no intersection etc.) if not geom.is_valid: # type: ignore try: reason = explain_validity(geom) # type: ignore messages.append( Message( level=MessageLevel.ERROR, message=f"{filename} invalid geometry: {reason}", )) valid_geom = make_valid(geom) # type: ignore valid_geom = eliutils.orient_geometry_rfc7946( valid_geom) # type: ignore valid_geom_json = json.dumps( mapping(valid_geom), sort_keys=False, ensure_ascii=False) # type: ignore messages.append( Message( level=MessageLevel.ERROR, message= f"{filename} please consider using corrected geometry: {valid_geom_json}", )) geom = valid_geom # type: ignore except Exception as e: logger.warning("Geometry check failed: {e}")
def polygon_zonal_areas(gdf, binsize=10, method='polygon', raster_sampling=1): ''' Given a geodataframe containing polygoms, divides the polygons into latitude bands of a user-defined width and returns the area of the polygon within each band ''' if method == 'polygon': bin_areas = [] for bin_edge in np.arange(-90., 90., binsize): polygon = Polygon([(-180, bin_edge), (-180, bin_edge + binsize), (180, bin_edge + binsize), (180, bin_edge), (-180, bin_edge)]) poly_gdf = gpd.GeoDataFrame([1], geometry=[polygon], crs=4326) # Fix geometries that are invalid (e.g. near the poles) # https://gis.stackexchange.com/questions/430384/using-shapely-methods-explain-validity-and-make-valid-on-shapefile gdf.geometry = gdf.apply( lambda row: make_valid(row.geometry) if not row.geometry.is_valid else row.geometry, axis=1) poly_clip = gpd.clip(gdf, poly_gdf) if poly_clip.empty: bin_area = 0 else: bin_area = 0 tmp = gdf2gpml(poly_clip) for f in tmp: if f.get_geometry(): bin_area += f.get_geometry().get_area() bin_areas.append(bin_area * pygplates.Earth.mean_radius_in_kms**2) return bin_areas elif method == 'rasterize': import xarray as xr from rasterio.features import rasterize, Affine #TODO change the raster definition to be pixel-registered dims = (int(180. / raster_sampling) + 1, int(360. / raster_sampling) + 1) transform = Affine(raster_sampling, 0.0, -180. - raster_sampling / 2., 0.0, raster_sampling, -90. - raster_sampling / 2.) geometry_zval_tuples = [(x.geometry, 1) for i, x in gdf.iterrows()] #with rasterio.open(raster_file) as src: # iterate over features to get (geometry, id value) pairs mask = rasterize(geometry_zval_tuples, transform=transform, out_shape=dims) # the first and last columns should match, but may not due to the imposed dateline mask[:, 0] = mask[:, -1] lats = np.arange(-90, 90 + raster_sampling, raster_sampling) bin_areas = raster_zonal_areas(mask, lats, binsize) return bin_areas else: raise ValueError('Unknown value {} for method parameter')
def attach_load( n, load_paths, regions, admin_shapes, countries, scale, ): """ Add load to the network and distributes them according GDP and population. Parameters ---------- n : pypsa network regions : .geojson Contains bus_id of low voltage substations and bus region shapes (voronoi cells) load_paths: paths of the load files admin_shapes : .geojson contains subregional gdp, population and shape data countries : list List of countries that is config input scale : float The scale factor is multiplied with the load (1.3 = 30% more load) Returns ------- n : pypsa network Now attached with load time series """ substation_lv_i = n.buses.index[n.buses["substation_lv"]] regions = ( gpd.read_file(regions).set_index("name").reindex(substation_lv_i) ).dropna( axis="rows") # TODO: check if dropna required here. NaN shapes exist? load_paths = load_paths # Merge load .nc files: https://stackoverflow.com/questions/47226429/join-merge-multiple-netcdf-files-using-xarray gegis_load = xr.open_mfdataset(load_paths, combine="nested") gegis_load = gegis_load.to_dataframe().reset_index().set_index("time") # filter load for analysed countries gegis_load = gegis_load.loc[gegis_load.region_code.isin(countries)] logger.info(f"Load data scaled with scalling factor {scale}.") gegis_load *= scale shapes = gpd.read_file(admin_shapes).set_index("GADM_ID") shapes.loc[:, "geometry"] = shapes["geometry"].apply(lambda x: make_valid(x)) def upsample(cntry, group): """ Distributes load in country according to population and gdp """ l = gegis_load.loc[gegis_load.region_code == cntry]["Electricity demand"] if len(group) == 1: return pd.DataFrame({group.index[0]: l}) else: shapes_cntry = shapes.loc[shapes.country == cntry] transfer = vtransfer.Shapes2Shapes(group, shapes_cntry.geometry, normed=False).T.tocsr() gdp_n = pd.Series(transfer.dot( shapes_cntry["gdp"].fillna(1.0).values), index=group.index) pop_n = pd.Series(transfer.dot( shapes_cntry["pop"].fillna(1.0).values), index=group.index) # relative factors 0.6 and 0.4 have been determined from a linear # regression on the country to EU continent load data # (refer to vresutils.load._upsampling_weights) # TODO: require adjustment for Africa factors = normed(0.6 * normed(gdp_n) + 0.4 * normed(pop_n)) return pd.DataFrame( factors.values * l.values[:, np.newaxis], index=l.index, columns=factors.index, ) load = pd.concat( [ upsample(cntry, group) for cntry, group in regions.geometry.groupby(regions.country) ], axis=1, ) n.madd("Load", substation_lv_i, bus=substation_lv_i, p_set=load)
print(str(len(shapes_pc4))) print(str(len(shapes_traffic))) fields = sf_traffic.fields[1:] field_names = [field[0] for field in fields] pprint.pprint(field_names) c = 0 c_2 = 0 for i_1 in range(len(shapes_traffic)): # print(str(i_1)) bbox_1 = shapes_traffic[i_1].bbox max_area = 0 max_id = -1 total_area = 0 poly_1 = make_valid(Polygon(shapes_traffic[i_1].points)) for i_2 in range(len(shapes_pc4)): bbox_2 = shapes_pc4[i_2].bbox # if Polygon(outline_1).intersects(Polygon(outline_2)): if ((bbox_2[0] - bbox_1[0]) * (bbox_2[2] - bbox_1[0]) < 0 or (bbox_2[0] - bbox_1[2]) * (bbox_2[2] - bbox_1[2]) < 0) and ((bbox_2[1] - bbox_1[1] ) * (bbox_2[3] - bbox_1[1]) < 0 or (bbox_2[1] - bbox_1[3]) * (bbox_2[3] - bbox_1[3]) < 0) or ((bbox_1[0] - bbox_2[0]) * (bbox_1[2] - bbox_2[0]) < 0 or (bbox_2[0] - bbox_2[2]) * (bbox_1[2] - bbox_2[2]) < 0) and ((bbox_1[1] - bbox_2[1] ) * (bbox_1[3] - bbox_2[1]) < 0 or (bbox_1[1] - bbox_2[3]) * (bbox_1[3] - bbox_2[3]) < 0): # print("i1 = " + str(i_1) + " i2 = " + str(i_2)) poly_2 = make_valid(Polygon(shapes_pc4[i_2].points)) if not poly_2.is_valid: poly_2 = make_valid(poly_2) if not poly_1.intersects(poly_2): min_ang = math.pi max_ang = - math.pi pre_ang = 100 * math.pi for pp in shapes_pc4[i_2].points:
img = tf.nn.erosion2d(img, kernel2, [1, 1, 1, 1], [1, 1, 1, 1], 'SAME') img = img[0, :, :, 0] t = threshold cond = tf.less(img, tf.constant(t, shape=imshape, dtype=tf.float16)) imgt = tf.where(cond, tf.zeros(tf.shape(img), dtype=tf.float16), img) imgt = tf.image.convert_image_dtype(imgt, dtype=tf.uint8) imgt = cv2.flip(imgt.numpy(), 0) shapes[f] = getpolygon(imgt) print(time() - start) print(f, t, "valid?", shapes[f].is_valid) ploys = {} for f in shapes.keys(): print("doing", f) ploys[f] = make_valid(shapes[f]) if ploys[f] is not MultiPolygon: print(ploys[f].__repr__(), "is not multipolygon") if isinstance(ploys[f], GeometryCollection): print("is GeometryCollection") tmp = MultiPolygon() for shape in ploys[f]: if isinstance(shape, MultiPolygon) or isinstance( shape, Polygon): tmp = tmp.union(shape) ploys[f] = tmp ''' if shapes[f].is_valid: print(0) ploys[f]=shapes[f] continue