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
示例#3
0
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
示例#4
0
    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
示例#5
0
    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
示例#6
0
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
示例#7
0
    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)
示例#9
0
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)
示例#10
0
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)
示例#11
0
        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}")
示例#12
0
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)
示例#14
0
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:
示例#15
0
        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