Ejemplo n.º 1
0
    def _rings_to_multi_polygon(self, rings):
        exterior_rings = []
        interior_rings = []
        for ring in rings:
            if ring.is_ccw:
                interior_rings.append(ring)
            else:
                exterior_rings.append(ring)

        polygon_bits = []

        # Turn all the exterior rings into polygon definitions,
        # "slurping up" and interior rings they contain.
        for exterior_ring in exterior_rings:
            polygon = sgeom.Polygon(exterior_ring)
            holes = []
            for interior_ring in interior_rings[:]:
                if polygon.contains(interior_ring):
                    holes.append(interior_ring)
                    interior_rings.remove(interior_ring)
            polygon_bits.append(
                (exterior_ring.coords, [ring.coords for ring in holes]))

        # Any left over "interior" rings need "inverting" with respect
        # to the boundary.
        if interior_rings:
            boundary_poly = sgeom.Polygon(self.boundary)
            x3, y3, x4, y4 = boundary_poly.bounds
            bx = (x4 - x3) * 0.1
            by = (y4 - y3) * 0.1
            x3 -= bx
            y3 -= by
            x4 += bx
            y4 += by
            for ring in interior_rings:
                polygon = sgeom.Polygon(ring)
                x1, y1, x2, y2 = polygon.bounds
                bx = (x2 - x1) * 0.1
                by = (y2 - y1) * 0.1
                x1 -= bx
                y1 -= by
                x2 += bx
                y2 += by
                box = sgeom.box(min(x1, x3), min(y1, y3), max(x2, x4),
                                max(y2, y4))

                # Invert the polygon
                polygon = box.difference(polygon)

                # Intersect the inverted polygon with the boundary
                polygon = boundary_poly.intersection(polygon)

                if not polygon.is_empty:
                    polygon_bits.append(polygon)

        if polygon_bits:
            multi_poly = sgeom.MultiPolygon(polygon_bits)
        else:
            multi_poly = sgeom.MultiPolygon()
        return multi_poly
Ejemplo n.º 2
0
    def shape(self):
        # shapely's idea of "holes" are to subtract everything in the second set
        # from the first. So let's at least make sure the "first" thing is the
        # biggest path.
        paths = self.paths
        paths.sort(key=lambda point_list: shgeo.Polygon(point_list).area,
                   reverse=True)
        # Very small holes will cause a shape to be rendered as an outline only
        # they are too small to be rendered and only confuse the auto_fill algorithm.
        # So let's ignore them
        if shgeo.Polygon(paths[0]).area > 5 and shgeo.Polygon(
                paths[-1]).area < 5:
            paths = [path for path in paths if shgeo.Polygon(path).area > 3]

        polygon = shgeo.MultiPolygon([(paths[0], paths[1:])])

        # There is a great number of "crossing border" errors on fill shapes
        # If the polygon fails, we can try to run buffer(0) on the polygon in the
        # hope it will fix at least some of them
        if not self.shape_is_valid(polygon):
            why = explain_validity(polygon)
            message = re.match(r".+?(?=\[)", why)
            if message.group(0) == "Self-intersection":
                buffered = polygon.buffer(0)
                # if we receive a multipolygon, only use the first one of it
                if type(buffered) == shgeo.MultiPolygon:
                    buffered = buffered[0]
                # we do not want to break apart into multiple objects (possibly in the future?!)
                # best way to distinguish the resulting polygon is to compare the area size of the two
                # and make sure users will not experience significantly altered shapes without a warning
                if type(buffered) == shgeo.Polygon and math.isclose(
                        polygon.area, buffered.area, abs_tol=0.5):
                    polygon = shgeo.MultiPolygon([buffered])

        return polygon
Ejemplo n.º 3
0
def __mask_to_polygons(mask):
    """[summary]

    Args:
        mask ([type]): [description]

    Returns:
        [type]: [description]
    """
    # XXX: maybe this should be merged with __mask_to_polys() defined above
    import numpy as np
    from rasterio import features, Affine
    from shapely import geometry

    all_polygons = []
    for shape, value in features.shapes(mask.astype(np.int16),
                                        mask=(mask > 0),
                                        transform=Affine(1.0, 0, 0, 0, 1.0,
                                                         0)):
        all_polygons.append(geometry.shape(shape))

    all_polygons = geometry.MultiPolygon(all_polygons)
    if not all_polygons.is_valid:
        all_polygons = all_polygons.buffer(0)
        # Sometimes buffer() converts a simple Multipolygon to just a Polygon,
        # need to keep it a Multi throughout
        if all_polygons.type == 'Polygon':
            all_polygons = geometry.MultiPolygon([all_polygons])
    return all_polygons
Ejemplo n.º 4
0
def _create_polygon(shape):
    if not shape.points:
        return sgeom.MultiPolygon()

    # Partition the shapefile rings into outer rings/polygons (clockwise) and
    # inner rings/holes (anti-clockwise).
    parts = list(shape.parts) + [None]
    bounds = zip(parts[:-1], parts[1:])
    outer_polygons_and_holes = []
    inner_polygons = []
    for lower, upper in bounds:
        polygon = sgeom.Polygon(shape.points[slice(lower, upper)])
        if polygon.exterior.is_ccw:
            inner_polygons.append(polygon)
        else:
            outer_polygons_and_holes.append((polygon, []))

    # Find the appropriate outer ring for each inner ring.
    # aka. Group the holes with their containing polygons.
    for inner_polygon in inner_polygons:
        for outer_polygon, holes in outer_polygons_and_holes:
            if outer_polygon.contains(inner_polygon):
                holes.append(inner_polygon.exterior.coords)
                break

    polygon_defns = [(outer_polygon.exterior.coords, holes)
                     for outer_polygon, holes in outer_polygons_and_holes]
    return sgeom.MultiPolygon(polygon_defns)
Ejemplo n.º 5
0
def buffer_shpfile(source, target, env):
    buffer_dist = env['buffer_dist']

    with fiona.open(str(source[1])) as coasts:
        lands = [c for c in coasts if c['properties']['area'] > 10000]
        land = [sgeom.shape(record['geometry']) for record in lands]
        land = sgeom.MultiPolygon(land)

    with fiona.open(str(source[0])) as inshp:
        driver = inshp.driver
        crs = inshp.crs
        schema = inshp.schema

        polys = [sgeom.shape(record['geometry']) for record in inshp]
        multi = sgeom.MultiPolygon(polys)
        dissolved = sops.unary_union(multi)
        clipped = dissolved.intersection(land)
        buffered = clipped.buffer(buffer_dist)  # deg lat/lon

        record = {
            'properties': {
                'BASIN': 'merged'
            },
            'geometry': sgeom.mapping(buffered)
        }

        with fiona.open(str(target[0]),
                        'w',
                        driver=driver,
                        crs=crs,
                        schema=schema) as outshp:
            outshp.write(record)

    return 0
Ejemplo n.º 6
0
 def getMultiPolygon(self, hasZ=False):
     if hasZ:
         return geometry.MultiPolygon([
             geometry.Polygon([(0, 0, 5), (1, 1, 5), (2, 1, 5), (0, 0, 5)]),
             geometry.Polygon([(3, 3, 5), (4, 4, 5), (5, 3, 5), (3, 3, 5)])
         ])
     return geometry.MultiPolygon([
         geometry.Polygon([(0, 0), (1, 1), (2, 1), (0, 0)]),
         geometry.Polygon([(3, 3), (4, 4), (5, 3), (3, 3)])
     ])
Ejemplo n.º 7
0
def shapelyToMultipolygon(anydata):
    if anydata.type == 'MultiPolygon':
        return anydata
    elif anydata.type == 'Polygon':
        if not anydata.is_empty:
            return shapely.geometry.MultiPolygon([anydata])
        else:
            return sgeometry.MultiPolygon()
    else:
        print(anydata.type, 'shapely conversion aborted')
        return sgeometry.MultiPolygon()
Ejemplo n.º 8
0
 def _project_multipolygon(self, geometry, src_crs):
     geoms = []
     for geom in geometry.geoms:
         r = self._project_polygon(geom, src_crs)
         if r:
             geoms.extend(r.geoms)
     if geoms:
         result = sgeom.MultiPolygon(geoms)
     else:
         result = sgeom.MultiPolygon()
     return result
Ejemplo n.º 9
0
def set_terrain_type(api_key, hexagons):
    """
    Function that updates terrain in Tygron. Mainly, it updates terrain from
    land to water and visa versa. In case of water to land, first changes the
    hexagon terrain to water and then adds a building to it which is
    subsequently updated to a specific land use. In case of land to water,
    first removes any building (the land use) from the hexagon and then changes
    the terrain to water.
    """
    print("Updating terrain in Tygron.")
    water = []
    land = []
    new_land_hexagons = []
    for feature in hexagons.features:
        if feature.properties["ghost_hexagon"]:
            continue
        if (feature.properties["water"] and feature.properties["z_changed"]):
            shape = geometry.asShape(feature.geometry)
            water.append(shape)
            if feature.properties["tygron_id"] is not None:
                remove_polygon(api_key, feature.properties["tygron_id"], shape)
        elif (feature.properties["land"] and feature.properties["z_changed"]):
            shape = geometry.asShape(feature.geometry)
            land.append(shape)
            new_land_hexagons.append(feature)
        else:
            continue
    water_multipolygon = geometry.MultiPolygon(water)
    land_multipolygon = geometry.MultiPolygon(land)
    becomes_water = geometry.mapping(water_multipolygon)
    becomes_land = geometry.mapping(land_multipolygon)
    r = update_terrain(api_key, becomes_water, terrain_type="water")
    try:
        pastebin_url = r.json()
        print(pastebin_url)
    except ValueError:
        print("Water terrain updated in Tygron.")
    r = update_terrain(api_key, becomes_land, terrain_type="land")

    new_land_hexagons = FeatureCollection(new_land_hexagons)
    for feature in new_land_hexagons.features:
        if feature.properties["tygron_id"] is None:
            tygron_id = add_standard(api_key)
            feature.properties["tygron_id"] = tygron_id
            set_name(api_key, tygron_id, feature.id)
        shape = geometry.asShape(feature.geometry)
        add_polygon(api_key, feature.properties["tygron_id"], shape)
        set_function(api_key, feature.properties["tygron_id"], 0)
    try:
        pastebin_url = r.json()
        print(pastebin_url)
    except ValueError:
        print("Land terrain updated in Tygron.")
    return
Ejemplo n.º 10
0
def ensure_MultiPolygon(input_geometry):
    if input_geometry.is_empty:
        return geometry.MultiPolygon()
    elif input_geometry.geom_type == 'MultiPolygon':
        return input_geometry
    elif input_geometry.geom_type == 'Polygon':
        return geometry.MultiPolygon([input_geometry])
    elif 'Collection' in input_geometry.geom_type:
        return geometry.MultiPolygon((pol for pol in input_geometry.geoms
                                      if pol.geom_type == 'Polygon'))
    else:
        return geometry.MultiPolygon()
Ejemplo n.º 11
0
def descretize_voronoi_to_polys(vor, bbox):
    points = vor.points
    lines = [
        geometry.LineString(vor.vertices[line]) for line in vor.ridge_vertices
        if -1 not in line
    ]

    result = geometry.MultiPolygon(
        [poly.intersection(bbox) for poly in ops.polygonize(lines)])
    result = geometry.MultiPolygon(
        [p for p in result] +
        [p for p in bbox.difference(ops.unary_union(result))])

    return result
Ejemplo n.º 12
0
def voronoi_polygons(X, margin=0):
    '''
    Returns a set of Voronoi polygons corresponding to a set of points X.

    :param X: Array of points (optional).
              Numpy array, shape = [n, 2].

    :param margin: Minimum margin to extend the outer polygons of the tessellation.
                   Non-negative float.

    :return: Geopandas data frame.
    '''
    assert isinstance(X, np.ndarray), 'Expecting a numpy array.'
    assert X.ndim == 2, 'Expecting a two-dimensional array.'
    assert X.shape[1] == 2, 'Number of columns is different from expected.'
    n_points = X.shape[0]

    c1, c2 = np.sort(X[:, 0]), np.sort(X[:, 1])
    _diffs = np.array(
        [max(margin,
             np.diff(c1).mean()),
         max(margin,
             np.diff(c2).mean())])

    min_c1, min_c2 = X.min(0) - _diffs
    max_c1, max_c2 = X.max(0) + _diffs

    extra_points = np.vstack([
        np.vstack([np.repeat(min_c1, n_points), c2]).T,
        np.vstack([np.repeat(max_c1, n_points), c2]).T,
        np.vstack([c1, np.repeat(min_c2, n_points)]).T,
        np.vstack([c1, np.repeat(max_c2, n_points)]).T
    ])

    _X = np.vstack([X, extra_points])

    # Define polygons geometry based on tessellation
    vor = Voronoi(_X)
    lines = [
        geometry.LineString(vor.vertices[li]) for li in vor.ridge_vertices
        if -1 not in li
    ]
    disord = geometry.MultiPolygon(list(polygonize(lines)))
    ix_order = np.array(
        [[i for i, di in enumerate(disord) if di.contains(geometry.Point(pi))]
         for pi in X]).ravel()

    return geop.GeoDataFrame(
        {'geometry': geometry.MultiPolygon([disord[i] for i in ix_order])})
Ejemplo n.º 13
0
 def make_cone(self):
     polygons = [self.makeErrCircle(lat, lon, radius) \
         for lat, lon, radius in zip(self.interp_lats, self.interp_lons, \
             self.interp_errs)]
     convex_hulls = [sgeometry.MultiPolygon([polygons[i], polygons[i+1]]).convex_hull \
         for i in range(len(polygons)-1)]
     self.cone = sops.cascaded_union(convex_hulls)
Ejemplo n.º 14
0
def __mask_to_polys(mask):
    """[summary]

    Args:
        mask ([type]): [description]

    Returns:
        [type]: [description]
    """
    import numpy as np
    import pandas as pd
    from rasterio import features
    from shapely import ops, geometry

    shapes = features.shapes(mask.astype(np.int16), mask > 0)
    mp = ops.cascaded_union(
        geometry.MultiPolygon(
            [geometry.shape(shape) for shape, value in shapes]))

    if isinstance(mp, geometry.Polygon):
        polygon_gdf = pd.DataFrame({
            'geometry': [mp],
        })
    else:
        polygon_gdf = pd.DataFrame({
            'geometry': [p for p in mp],
        })
    return polygon_gdf
Ejemplo n.º 15
0
def project(coords, p, prj_cen, i):
    #plot_slice(gg)
    #plot_slice(list(l.coords))
    pp = sg.MultiPolygon([sg.Polygon(a) for a in coords])
    minx, miny, maxx, maxy = pp.bounds
    minx1, miny1, maxx1, maxy1 = p.bounds
    cen = (maxx + minx) / 2
    pp = sa.translate(pp, -cen + prj_cen[0], -(miny - maxy1 - 20))
    minx, miny, maxx, maxy = pp.bounds
    if maxy > i + miny:
        l = sg.LineString([(int(minx - 3), i + miny),
                           (int(maxx + 3), i + miny)])
        intrsct = l.intersection(pp)
        if type(intrsct) == sg.multilinestring.MultiLineString:
            for b in intrsct:
                points = list(b.coords)
                lines = [
                    sg.LineString(ln)
                    for ln in list(zip(points, [prj_cen] * 2))
                ]
                ln1, ln2 = [a.intersection(p) for a in lines]
                p1 = list(lines[0].coords)[0]
                p2 = list(ln1.interpolate(1).coords)[0]
                p3 = list(ln2.interpolate(1).coords)[0]
                p4 = list(lines[1].coords)[0]
                p = p.difference(sg.Polygon([p1, p2, p3, p4]))
    return p
Ejemplo n.º 16
0
def setup_shape(key: str):
    """
    This method returns the shape (or shapes) as 'Polygon' or 'MultyPoligon' type
    :param key: country ISO2 code/ISO3 code/name/FIPS code
    :return: the shape as Polygon or Multipolygon
    """

    # importing geojson file
    country_ids = eucountries_py.CountryList(constants_py.Constants.EU_PATH)
    uid = country_ids.get_by_key(key)
    shape_dict = country_ids.get_by_key(uid)

    # list of poligons (only used for MultiPolygon)
    poligons = []
    if shape_dict['type'] == "MultiPolygon":
        for polygon in shape_dict['coordinates']:
            for sub_polygon in polygon:
                pol = geom_shapely.Polygon(sub_polygon)
                poligons.append(pol)
        shape = geom_shapely.MultiPolygon(poligons)
    else:
        if shape_dict['type'] == "Polygon":
            shape = geom_shapely.Polygon(shape_dict['coordinates'][0])
        else:
            raise Exception('Error occurred during setting up the shape')

    return shape
Ejemplo n.º 17
0
    def run(self):

        dataset = DataSet.query.filter_by(name=self.args['dataset']).first()
        volume = Volume.query.filter_by(name=self.args['volume'],
                                        dataset=dataset).first()

        coll_type = self.args['synapse_collection_type']
        coll_name = self.args['synapse_collection_name']
        collection = SynapseCollection(name=coll_name,
                                       volume=volume,
                                       synapse_collection_type=coll_type)

        synapse_file = self.args['synapse_file']
        with open(synapse_file, 'r') as fp:
            syn_anns = json.load(fp)

        syn_anns = syn_anns['area_lists']
        for ann in syn_anns:
            rings = []
            for area in ann['areas']:
                path = np.array(area['global_path'])
                path = np.concatenate((path, area['z'] * np.ones(
                    (path.shape[0], 1))),
                                      axis=1)
                rings.append(geometry.Polygon(path))
            mpoly = geometry.MultiPolygon(rings)
            syn = Synapse(oid=ann['oid'],
                          areas=from_shape(mpoly, srid=SYNAPSE_SRID),
                          collection=collection)
            db.session.add(syn)

        db.session.add(collection)

        db.session.commit()
Ejemplo n.º 18
0
    def shape(self):
        poly_ary = []
        for sub_path in self.paths:
            point_ary = []
            last_pt = None
            for pt in sub_path:
                if (last_pt is not None):
                    vp = (pt[0] - last_pt[0], pt[1] - last_pt[1])
                    dp = math.sqrt(math.pow(vp[0], 2.0) + math.pow(vp[1], 2.0))
                    # dbg.write("dp %s\n" % dp)
                    if (dp > 0.01):
                        # I think too-close points confuse shapely.
                        point_ary.append(pt)
                        last_pt = pt
                else:
                    last_pt = pt
            if point_ary:
                poly_ary.append(point_ary)

        # shapely's idea of "holes" are to subtract everything in the second set
        # from the first. So let's at least make sure the "first" thing is the
        # biggest path.
        # TODO: actually figure out which things are holes and which are shells
        poly_ary.sort(key=lambda point_list: shgeo.Polygon(point_list).area,
                      reverse=True)

        polygon = shgeo.MultiPolygon([(poly_ary[0], poly_ary[1:])])
        # print >> sys.stderr, "polygon valid:", polygon.is_valid
        return polygon
Ejemplo n.º 19
0
def geodjango_to_shapely(geos_obj):
    """ Convert geodjango geometry to shapely for plotting etc
        inputs: x is a sequence of geodjango geometry objects """
    assert HAS_GEODJANGO, "Requires Geodjango"

    geodjango_poly_to_shapely = lambda t: geometry.Polygon(shell=t.coords[0],
                                                           holes=t.coords[1:])

    converters = {
        geos.Point:
        lambda t: geometry.Point(t.coords),
        geos.LineString:
        lambda t: geometry.LineString(t.coords),
        geos.Polygon:
        lambda t: geodjango_poly_to_shapely(t),
        geos.MultiPolygon:
        lambda t: geometry.MultiPolygon(
            [geodjango_poly_to_shapely(x) for x in t])
    }

    if not issubclass(geos_obj.__class__, geos.GEOSGeometry):
        raise TypeError("Require object that inherits from geos.GEOSGeometry")

    return converters[type(geos_obj)](
        geos_obj)  # FIXME: why is PyCharm complaining about this line?!
Ejemplo n.º 20
0
 def add(self, points: List[aecPoint], restart: bool = False) -> bool:
     """
     If restart is True, constructs a new boundary from the delivered list of points.
     If restart is False, combines the current boundary with boundaries defined by
     the delivered points.
     Returns True if successful.
     Returns False if the delivered points do not resolve to a single non-crossing
     polygon and leaves the current boundary unchanged.
     """
     try:
         if restart: boundaries = []
         else: boundaries = [self.__boundary]
         if self.__setBoundary(points):
             boundaries.append(self.__boundary)
             boundaries = shapely.MultiPolygon(boundaries)
             boundary = shapelyOps.unary_union(boundaries)
             if type(boundary) != shapely.polygon.Polygon: return False
             points = [
                 aecPoint(pnt[0], pnt[1])
                 for pnt in list(boundary.exterior.coords)[:-1]
             ]
             return self.__setBoundary(points)
         return False
     except Exception:
         traceback.print_exc()
         return False
Ejemplo n.º 21
0
    def from_shapely(cls, shape, orient=True):
        """
        Build a spatialpandas MultiPolygon object from a shapely shape

        Args:
            shape: A shapely Polygon or MultiPolygon shape
            orient: If True (default), reorder polygon vertices so that outer shells
                    are stored in counter clockwise order and holes are stored in
                    clockwise order.  If False, accept vertices as given. Note that
                    while there is a performance cost associated with this operation
                    some algorithms will not behave properly if the above ordering
                    convention is not followed, so only set orient=False if it is
                    known that this convention is followed in the input data.
        Returns:
            spatialpandas MultiPolygon
        """
        import shapely.geometry as sg
        if orient:
            if isinstance(shape, sg.Polygon):
                shape = sg.polygon.orient(shape)
            elif isinstance(shape, sg.MultiPolygon):
                shape = sg.MultiPolygon(
                    [sg.polygon.orient(poly) for poly in shape])

        shape_parts = cls._shapely_to_coordinates(shape)
        return cls(shape_parts)
Ejemplo n.º 22
0
    def add_boundary_from_gml(self, gml, hucname):
        """
        Creates Shapely polygon objects from GML returned via ArcServer
        """

        polys = []
        namespaces = {'gml': 'http://www.opengis.net/gml/3.2'}
        for polygon in gml:
            vertex_list = polygon.text.split(' ')
            vertex_list = [float(v) for v in vertex_list]
            coords = list(zip(vertex_list[0::2], vertex_list[1::2]))
            polys.append(geometry.Polygon(coords))

        if len(polys) > 1:
            # create multipolygon
            plist = list(itertools.chain.from_iterable(self.polygons.values()))
            shape = geometry.MultiPolygon(plist)
        else:
            shape = polys[0]

        if hucname not in self.polygons.keys():
            self.polygons[hucname] = shape
        else:
            raise Exception(
                f'Huc already exists in WatershedBoundary: {hucname}')
Ejemplo n.º 23
0
def _intersect_multipolygon(shape, tile_bounds, clip_bounds):
    """
    Return the parts of the MultiPolygon shape which overlap the tile_bounds,
    each clipped to the clip_bounds. This can be used to extract only the
    parts of a multipolygon which are actually visible in the tile, while
    keeping those parts which extend beyond the tile clipped to avoid huge
    polygons.
    """

    polys = []
    for poly in shape.geoms:
        if tile_bounds.intersects(poly):
            if not clip_bounds.contains(poly):
                poly = clip_bounds.intersection(poly)

            # the intersection operation can make the resulting polygon
            # invalid. including it in a MultiPolygon would make that
            # invalid too. instead, we skip it, and hope it wasn't too
            # important.
            if not poly.is_valid:
                continue

            if poly.type == 'Polygon':
                polys.append(poly)
            elif poly.type == 'MultiPolygon':
                polys.extend(poly.geoms)

    return geometry.MultiPolygon(polys)
Ejemplo n.º 24
0
def sparsify(union, subset_paths):
    """Helper function to discard polygons from a superset that do not intersect at least one geometry from each subset geojson

    Arguments:
        union {MultiPolygon} -- superset Multipolygon that overlaps the subset GeoJSONs
        subset_paths {List of Paths} -- List of Locations of the small disparate GeoJSONs

    Returns:
        MultiPolygon -- thinned out version of union
    """
    # Function to delete features that don't intersect another file
    if server_config.VERBOSE:
        print("Discarding extraneous polygons")

    remove_polys = []
    for path in subset_paths:
        sub_multi = geojson_to_shapely_multi(path)
        for i, super_poly in enumerate(union):
            if not super_poly.intersects(sub_multi):
                remove_polys += [i]

    thinned_polys = []
    for i, super_poly in enumerate(union):
        if not i in remove_polys:
            thinned_polys += [super_poly]

    sparse = unary_union([sh.shape(p) for p in thinned_polys])
    if isinstance(sparse, sh.Polygon):
        sparse = sh.MultiPolygon([sparse])

    return sparse
Ejemplo n.º 25
0
def regular_polygons(X, radius, n_angles=8):
    assert isinstance(X, np.ndarray), 'Expecting a numpy array.'
    assert X.ndim == 2, 'Expecting a two-dimensional array.'
    assert X.shape[1] == 2, 'Number of columns is different from expected.'

    assert isinstance(n_angles, int), 'n_angles must be an integer.'
    assert n_angles >= 3, 'Angles must be greater than two.'

    vertex = np.pi * np.linspace(0, 2, n_angles + 1)

    if isinstance(radius, float):
        assert radius > 0, 'Radius must be positive.'
        polys = [
            np.vstack([
                xi + radius * np.array([np.cos(t), np.sin(t)]) for t in vertex
            ]) for xi in X
        ]
    else:
        assert isinstance(radius, np.ndarray), 'Expecting a numpy array.'
        assert radius.ndim == 1, 'Expecting a one-dimensional array.'
        assert radius.size == X.shape[
            0], 'Array size is different from expected.'

        polys = [
            np.vstack(
                [xi + ri * np.array([np.cos(t), np.sin(t)]) for t in vertex])
            for xi, ri in zip(X, radius)
        ]

    return geop.GeoDataFrame({
        'geometry':
        geometry.MultiPolygon([geometry.Polygon(pi) for pi in polys])
    })
Ejemplo n.º 26
0
    def shape(self):
        poly_ary = []
        for sub_path in self.paths:
            point_ary = []
            last_pt = None
            for pt in sub_path:
                if (last_pt is not None):
                    vp = (pt[0] - last_pt[0], pt[1] - last_pt[1])
                    dp = math.sqrt(math.pow(vp[0], 2.0) + math.pow(vp[1], 2.0))
                    # dbg.write("dp %s\n" % dp)
                    if (dp > 0.01):
                        # I think too-close points confuse shapely.
                        point_ary.append(pt)
                        last_pt = pt
                else:
                    last_pt = pt
            if len(point_ary) > 2:
                poly_ary.append(point_ary)

        if not poly_ary:
            self.fatal(_("shape %s is so small that it cannot be filled with stitches.  Please make it bigger or delete it.") % self.node.get('id'))

        # shapely's idea of "holes" are to subtract everything in the second set
        # from the first. So let's at least make sure the "first" thing is the
        # biggest path.
        # TODO: actually figure out which things are holes and which are shells
        poly_ary.sort(key=lambda point_list: shgeo.Polygon(point_list).area, reverse=True)

        polygon = shgeo.MultiPolygon([(poly_ary[0], poly_ary[1:])])

        if not polygon.is_valid:
            self.fatal(_("shape is not valid.  This can happen if the border crosses over itself."))

        return polygon
Ejemplo n.º 27
0
def unify(json_paths):
    """Merge multiple GeoJsons with overlapping polygons into a single GeoJSON with "union" applied to all overlapping features

    Arguments:
        json_paths {list of Paths} -- Multi-featured GeoJSONs to be merged

    Returns:
        MultiPolygon -- shapely multipolygon containing all the polygons from all the geojsons dissolved together
    """
    if server_config.VERBOSE:
        print("Combining all model outputs")

    polys = []
    for path in json_paths:
        with open(path) as f:
            features = json.load(f)["features"]
        polys += [
            sh.shape(feat["geometry"]).buffer(0) for feat in features
            if sh.shape(feat["geometry"]).geom_type in
            ["Polygon", "Multipolygon"]
        ]  # Ignore points and lines
    uni = unary_union(polys)
    if isinstance(uni, sh.Polygon):
        uni = sh.MultiPolygon([uni])

    return uni
Ejemplo n.º 28
0
def fix_rings(multipolygon, strict=False):
    """
    This resolves a multipolygon with invalid exterior/interior ring pairing. 

    It does so by first sorting the exteriors by z-order (so that the exteriors 
    contained by the most other exteriors are "higher" & get priority). Then, 
    interiors are assigned to their highest containing exterior ring.

    This ensures that the "most interior" interior rings are paired with the 
    "most interior" external rings.

    Requires shapely, may take a bit for shapes with many exteriors/interiors. 

    Parameters
    ---------
    multipolygon: a shapely polygon. (should be invalid due to ring ordering)

    Returns
    -------
    multipolygon: a shapely polygon that should have valid ring ordering. 
                  May be invalid due to other reasons.

    NOTE: This function has undefined behavior for invalid multipolygons. 
    """
    from shapely import geometry as geom
    from shapely.ops import cascaded_union
    from shapely.validation import explain_validity

    vexplain = explain_validity(multipolygon)
    if "hole lies outside shell" not in vexplain.lower():
        if strict:
            from shapely.geos import TopologicalError

            def tell_user(x):
                raise TopologicalError(x)

        else:
            from warnings import warn as tell_user
        tell_user("Shape is invalid: \n{}".format(vexplain))
    exteriors = [geom.Polygon(part.exterior) for part in multipolygon.geoms]
    interiors = [
        geom.Polygon(interior)
        for part in multipolygon.geoms
        for interior in part.interiors
    ]
    zorder = [
        sum([exterior.contains(other_exterior) for other_exterior in exteriors]) - 1
        for exterior in exteriors
    ]
    sort_zorder = np.argsort(zorder)
    zordered_exteriors = np.asarray(exteriors)[sort_zorder]
    polygons = [[exterior] for exterior in exteriors]
    for i, exterior in enumerate(zordered_exteriors):
        owns = [exterior.contains(interior) for interior in interiors]
        owned_interiors = [
            interior for owned, interior in zip(owns, interiors) if owned
        ]
        polygons[i] = exterior.difference(cascaded_union(owned_interiors))
        interiors = [interior for owned, interior in zip(owns, interiors) if not owned]
    return geom.MultiPolygon(polygons)
Ejemplo n.º 29
0
def st_multipolygon_array(draw, min_size=0, max_size=5, geoseries=False):
    n = draw(st.integers(min_value=min_size, max_value=max_size))
    sg_multipolygons = []
    for _ in range(n):
        xmid = draw(st.floats(-50, 50))
        ymid = draw(st.floats(-50, 50))
        polygon = draw(st_polygon(xmid=xmid, ymid=ymid))
        m = draw(st.integers(min_value=1, max_value=4))

        # populate polygons with m copies of polygon
        polygons = [polygon] * m

        # translate polygons so they don't overlap
        _, _, last_x1, last_y1 = polygons[0].bounds
        for j in range(1, m):
            polygon = scale(polygons[j], 0.8, 0.5)
            poly_x0, poly_y0, poly_x1, poly_y1 = polygon.bounds
            new_polygon = translate(polygon, yoff=last_y1 - poly_y0 + 1)
            _, _, last_x1, last_y1 = new_polygon.bounds
            polygons[j] = new_polygon

        sg_multipolygons.append(sg.MultiPolygon(polygons))

    result = from_shapely(sg_multipolygons)
    if geoseries:
        result = GeoSeries(result)
    return result
Ejemplo n.º 30
0
def test_remove_inner_rings():
    # Apply to single Polygon, with area tolerance smaller than holes
    polygon_removerings_withholes = sh_geom.Polygon(
            shell=[(0, 0), (0, 10), (1, 10), (10, 10), (10, 0), (0,0)], 
            holes=[[(2,2), (2,4), (4,4), (4,2), (2,2)], [(5,5), (5,6), (7,6), (7,5), (5,5)]])
    poly_result = geometry_util.remove_inner_rings(polygon_removerings_withholes, min_area_to_keep=1, crs=None)
    assert isinstance(poly_result, sh_geom.Polygon)
    assert len(poly_result.interiors) == 2

    # Apply to single Polygon, with area tolerance between 
    # smallest hole (= 2m²) and largest (= 4m²)
    poly_result = geometry_util.remove_inner_rings(polygon_removerings_withholes, min_area_to_keep=3, crs=None)
    assert isinstance(poly_result, sh_geom.Polygon)
    assert len(poly_result.interiors) == 1

    # Apply to single polygon and remove all holes
    poly_result = geometry_util.remove_inner_rings(polygon_removerings_withholes, min_area_to_keep=0, crs=None)
    assert isinstance(poly_result, sh_geom.Polygon)
    assert len(poly_result.interiors) == 0
    polygon_removerings_noholes = sh_geom.Polygon(shell=[(100, 100), (100, 110), (110, 110), (110, 100), (100,100)])
    poly_result = geometry_util.remove_inner_rings(polygon_removerings_noholes, min_area_to_keep=0, crs=None)
    assert isinstance(poly_result, sh_geom.Polygon)
    assert len(poly_result.interiors) == 0
     
    # Apply to MultiPolygon, with area tolerance between 
    # smallest hole (= 2m²) and largest (= 4m²)
    multipoly_removerings = sh_geom.MultiPolygon([polygon_removerings_withholes, polygon_removerings_noholes])
    poly_result = geometry_util.remove_inner_rings(multipoly_removerings, min_area_to_keep=3, crs=None)
    assert isinstance(poly_result, sh_geom.MultiPolygon)
    assert len(poly_result.geoms[0].interiors) == 1