Beispiel #1
0
def test_polygonize_full():
    lines = [
        None,
        pygeos.Geometry("LINESTRING (0 0, 1 1)"),
        pygeos.Geometry("LINESTRING (0 0, 0 1)"),
        pygeos.Geometry("LINESTRING (0 1, 1 1)"),
        pygeos.Geometry("LINESTRING (1 1, 1 0)"),
        None,
        pygeos.Geometry("LINESTRING (1 0, 0 0)"),
        pygeos.Geometry("LINESTRING (5 5, 6 6)"),
        pygeos.Geometry("LINESTRING (1 1, 100 100)"),
        pygeos.Geometry("POINT (0 0)"),
        None,
    ]
    result = pygeos.polygonize_full(lines)
    assert len(result) == 4
    assert all(pygeos.get_type_id(geom) == 7 for geom in result)  # GeometryCollection
    polygons, cuts, dangles, invalid = result
    expected_polygons = pygeos.Geometry(
        "GEOMETRYCOLLECTION (POLYGON ((0 0, 1 1, 1 0, 0 0)), POLYGON ((1 1, 0 0, 0 1, 1 1)))"
    )
    assert polygons == expected_polygons
    assert cuts == pygeos.Geometry("GEOMETRYCOLLECTION EMPTY")
    expected_dangles = pygeos.Geometry(
        "GEOMETRYCOLLECTION (LINESTRING (1 1, 100 100), LINESTRING (5 5, 6 6))"
    )
    assert dangles == expected_dangles
    assert invalid == pygeos.Geometry("GEOMETRYCOLLECTION EMPTY")
Beispiel #2
0
    def _dense_point_array(self, geoms, distance, index):
        """
        geoms - array of pygeos lines
        """
        # interpolate lines to represent them as points for Voronoi
        points = np.empty((0, 2))
        ids = []

        if pygeos.get_type_id(geoms[0]) not in [1, 2, 5]:
            lines = pygeos.boundary(geoms)
        else:
            lines = geoms
        lengths = pygeos.length(lines)
        for ix, line, length in zip(index, lines, lengths):
            if length > distance:  # some polygons might have collapsed
                pts = pygeos.line_interpolate_point(
                    line,
                    np.linspace(0.1,
                                length - 0.1,
                                num=int((length - 0.1) // distance)),
                )  # .1 offset to keep a gap between two segments
                points = np.append(points, pygeos.get_coordinates(pts), axis=0)
                ids += [ix] * len(pts)

        return points, ids
def merge_lines(df, by):
    """Use GEOS line merge to merge MultiLineStrings into LineStrings (where possible).

    This uses aggregate_lines first to aggregate lines to MultiLineStrings.

    WARNING: this can be a bit slow.

    Parameters
    ----------
    df : GeoDataFrame
    by : string or list-like
        field(s) to aggregate by

    Returns
    -------
    GeoDataFrame of LineStrings or MultiLinestrings (if required)
    """
    agg = aggregate_lines(df, by)
    agg["geometry"] = pg.line_merge(agg.geometry.values.data)

    geom_type = pg.get_type_id(agg["geometry"].values.data)
    ix = geom_type == 5
    if ix.sum() > 0:
        agg.loc[~ix, "geometry"] = pg.multilinestrings(
            agg.loc[~ix].geometry.values.data, np.arange((~ix).sum()))

    return agg
Beispiel #4
0
    def transform_geometry(self, geom, rs, max_points=5):
        """Transforms a geometry embedding new points.

        In case geom is (multi)line or (multi)polygon, it adds points collinear to their neighbours, so that an equivalent geometry is generated. The number of extra points depends on the number of vertices in the geometry.

        Arguments:
            geom (pygeos.Geometry): Geometry
            rs (numpy.RandomState): Random State
            max_points (int): Maximum value of extra points.

        Returns:
            (pygeos.Geometry)

        Raises:
            ValueError: When geometry type is not supported.
        """
        type_ = pg.get_type_id(geom)
        if type_ == 1 or type_ == 3:
            # LINESTRING or POLYGON
            vertices = pg.get_coordinates(geom)
            size = min(max_points, math.ceil(len(vertices) / 6))
            vert_ids = rs.randint(1, len(vertices), size)
            vert_ids.sort()
            new = []
            for idx in vert_ids:
                xa, ya = vertices[idx - 1]
                xb, yb = vertices[idx]
                if xa == xb:
                    x = xa
                    y = self._random_float(rs, ya, yb)
                else:
                    x = self._random_float(rs, xa, xb)
                    y = (yb - ya) * (x - xa) / (xb - xa) + ya
                x = _round(x, [xa, xb])
                y = _round(y, [ya, yb])
                new.append((idx, [x, y]))
            offset = 0
            extended = []
            for idx, entry in new:
                extended.extend(vertices[offset:idx])
                extended.append(entry)
                offset = idx
            extended.extend(vertices[offset:])
            extended = np.array(extended)
            result = pg.linestrings(extended) if type_ == 1 else pg.polygons(
                extended)
        elif type_ == 5 or type_ == 6:
            # MULTILINESTRING or MULTIPOLYGON
            parts = pg.get_parts(geom)
            part_idx = rs.randint(0, len(parts))
            parts[part_idx] = self.transform_geometry(parts[part_idx], rs)
            result = pg.multilinestrings(
                parts) if type_ == 5 else pg.multipolygons(parts)
        else:
            raise ValueError(
                'geom should be linestring, polygon, multilinestring, or multipolygon.'
            )

        return result
def cut_line_at_points(line, cut_points, tolerance=1e-6):
    """Cut a pygeos line geometry at points.
    If there are no interior points, the original line will be returned.

    Parameters
    ----------
    line : pygeos Linestring
    cut_points : list-like of pygeos Points
        will be projected onto the line; those interior to the line will be
        used to cut the line in to new segments.
    tolerance : float, optional (default: 1e-6)
        minimum distance from endpoints to consider the points interior
        to the line.

    Returns
    -------
    MultiLineStrings (or LineString, if unchanged)
    """
    if not pg.get_type_id(line) == 1:
        raise ValueError("line is not a single linestring")

    vertices = pg.get_point(line, range(pg.get_num_points(line)))
    offsets = pg.line_locate_point(line, vertices)
    cut_offsets = pg.line_locate_point(line, cut_points)
    # only keep those that are interior to the line and ignore those very close
    # to endpoints or beyond endpoints
    cut_offsets = cut_offsets[(cut_offsets > tolerance)
                              & (cut_offsets < offsets[-1] - tolerance)]

    if len(cut_offsets) == 0:
        # nothing to cut, return original
        return line

    # get coordinates of new vertices from the cut points (interpolated onto the line)
    cut_offsets.sort()

    # add in the last coordinate of the line
    cut_offsets = np.append(cut_offsets, offsets[-1])

    # TODO: convert this to a pygos ufunc
    coords = pg.get_coordinates(line)
    cut_coords = pg.get_coordinates(
        pg.line_interpolate_point(line, cut_offsets))
    lines = []
    orig_ix = 0
    for cut_ix in range(len(cut_offsets)):
        offset = cut_offsets[cut_ix]

        segment = []
        if cut_ix > 0:
            segment = [cut_coords[cut_ix - 1]]
        while offsets[orig_ix] < offset:
            segment.append(coords[orig_ix])
            orig_ix += 1

        segment.append(cut_coords[cut_ix])
        lines.append(pg.linestrings(segment))

    return pg.multilinestrings(lines)
Beispiel #6
0
def test_pickle(geom):
    if pygeos.get_type_id(geom) == 2:
        # Linearrings get converted to linestrings
        expected = pygeos.linestrings(pygeos.get_coordinates(geom))
    else:
        expected = geom
    pickled = pickle.dumps(geom)
    assert pygeos.equals_exact(pickle.loads(pickled), expected)
Beispiel #7
0
def _harmonize_to_multitype(geoseries: gpd.GeoSeries,
                            dest_geometrytype: GeometryType) -> gpd.GeoSeries:

    # Copy geoseries to pygeos array
    geometries_arr = geoseries.array.data.copy()  # type: ignore

    # Set empty geometries to None
    empty_idxs = pg.get_type_id(geometries_arr) == 7
    if empty_idxs.sum():
        geometries_arr[empty_idxs] = None

    # Cast all geometries that are not of the correct multitype yet
    # Remark: all rows need to be retained, so the same indexers exist in the
    # returned geoseries
    if dest_geometrytype is GeometryType.MULTIPOLYGON:
        # Convert polygons to multipolygons
        single_idxs = pg.get_type_id(geometries_arr) == 3
        if single_idxs.sum():
            geometries_arr[single_idxs] = np.apply_along_axis(
                pg.multipolygons,
                arr=(np.expand_dims(geometries_arr[single_idxs], 1)),
                axis=1)
    elif dest_geometrytype is GeometryType.MULTILINESTRING:
        # Convert linestrings to multilinestrings
        single_idxs = pg.get_type_id(geometries_arr) == 1
        if single_idxs.sum():
            geometries_arr[single_idxs] = np.apply_along_axis(
                pg.multilinestrings,
                arr=(np.expand_dims(geometries_arr[single_idxs], 1)),
                axis=1)
    elif dest_geometrytype is GeometryType.MULTIPOINT:
        single_idxs = pg.get_type_id(geometries_arr) == 0
        if single_idxs.sum():
            geometries_arr[single_idxs] = np.apply_along_axis(
                pg.multipoints,
                arr=(np.expand_dims(geometries_arr[single_idxs], 1)),
                axis=1)
    else:
        raise Exception(
            f"Unsupported destination GeometryType: {dest_geometrytype}")

    # Prepare result to return
    geoseries_result = geoseries.copy()
    geoseries_result.array.data = geometries_arr  # type: ignore
    assert isinstance(geoseries_result, gpd.GeoSeries)
    return geoseries_result
Beispiel #8
0
def test_float_arg_array(geometry, func):
    if func is pygeos.offset_curve and pygeos.get_type_id(geometry) not in [1, 2]:
        with pytest.raises(GEOSException, match="only accept linestrings"):
            func([geometry, geometry], 0.0)
        return
    actual = func([geometry, geometry], 0.0)
    assert actual.shape == (2,)
    assert isinstance(actual[0], Geometry)
Beispiel #9
0
def test_get_rings(geom):
    if (pygeos.get_type_id(geom) !=
            pygeos.GeometryType.POLYGON) or pygeos.is_empty(geom):
        rings = pygeos.get_rings(geom)
        assert len(rings) == 0
    else:
        rings = pygeos.get_rings(geom)
        assert len(rings) == 1
        assert rings[0] == pygeos.get_exterior_ring(geom)
Beispiel #10
0
 def geom_type(self):
     idx = pygeos.get_type_id(self.g)
     return [
         "None",
         "Point",
         "LineString",
         "LinearRing",
         "Polygon",
         "MultiPoint",
         "MultiLineString",
         "MultiPolygon",
         "GeometryCollection",
     ][idx]
Beispiel #11
0
def is_type(x, *geometry_types):
    """Return whether geometries in the provided array are of one of the provided types.

    Args:
        x (numpy.ndarray): A 1D ``[geometry]`` array.
        *geometry_types (GeometryType): A geometry type.

    Returns:
        numpy.ndarray: A boolean array of whether geometries are of any of the provided``geometry_types``.

    """
    types = get_type_id(x)
    return sum(types == geometry_type.value
               for geometry_type in geometry_types).astype(bool)
Beispiel #12
0
def _pygeos_to_shapely(geom):
    if geom is None:
        return None

    if compat.PYGEOS_SHAPELY_COMPAT:
        geom = shapely.geos.lgeos.GEOSGeom_clone(geom._ptr)
        return shapely.geometry.base.geom_factory(geom)

    # fallback going through WKB
    if pygeos.is_empty(geom) and pygeos.get_type_id(geom) == 0:
        # empty point does not roundtrip through WKB
        return shapely.wkt.loads("POINT EMPTY")
    else:
        return shapely.wkb.loads(pygeos.to_wkb(geom))
Beispiel #13
0
def test_polygonize():
    lines = [
        pygeos.Geometry("LINESTRING (0 0, 1 1)"),
        pygeos.Geometry("LINESTRING (0 0, 0 1)"),
        pygeos.Geometry("LINESTRING (0 1, 1 1)"),
        pygeos.Geometry("LINESTRING (1 1, 1 0)"),
        pygeos.Geometry("LINESTRING (1 0, 0 0)"),
        pygeos.Geometry("LINESTRING (5 5, 6 6)"),
        pygeos.Geometry("POINT (0 0)"),
        None,
    ]
    result = pygeos.polygonize(lines)
    assert pygeos.get_type_id(result) == 7  # GeometryCollection
    expected = pygeos.Geometry(
        "GEOMETRYCOLLECTION (POLYGON ((0 0, 1 1, 1 0, 0 0)), POLYGON ((1 1, 0 0, 0 1, 1 1)))"
    )
    assert result == expected
def to_dict(geometry):
    """Convert pygeos Geometry object to a dictionary representation.
    Equivalent to structure of GeoJSON.

    Parameters
    ----------
    geometry : pygeos Geometry object (singular)

    Returns
    -------
    dict
        GeoJSON dict representation of geometry
    """
    geometry = pg.normalize(geometry)

    def get_ring_coords(polygon):
        # outer ring must be reversed to be counterclockwise[::-1]
        coords = [pg.get_coordinates(pg.get_exterior_ring(polygon)).tolist()]
        for i in range(pg.get_num_interior_rings(polygon)):
            # inner rings must be reversed to be clockwise[::-1]
            coords.append(
                pg.get_coordinates(pg.get_interior_ring(polygon, i)).tolist())

        return coords

    geom_type = GEOJSON_TYPE[pg.get_type_id(geometry)]
    coords = []

    if geom_type == "MultiPolygon":
        coords = []
        geoms = pg.get_geometry(geometry,
                                range(pg.get_num_geometries(geometry)))
        for geom in geoms:
            coords.append(get_ring_coords(geom))

    elif geom_type == "Polygon":
        coords = get_ring_coords(geometry)

    else:
        raise NotImplementedError("Not built")

    return {"type": geom_type, "coordinates": coords}
Beispiel #15
0
def test_subclasses(with_point_in_registry):
    for point in [Point("POINT (1 1)"), pygeos.points(1, 1)]:
        assert isinstance(point, Point)
        assert pygeos.get_type_id(point) == pygeos.GeometryType.POINT
        assert point.x == 1
Beispiel #16
0
 def type_id(self):
     return pygeos.get_type_id(self)
def union_or_combine(geometries, grid_size=None, op="union"):
    """First does a check for overlap of geometries according to STRtree
    intersects.  If any overlap, then will use union_all on all of them;
    otherwise will return as a multipolygon.

    If only one polygon is present, it will be returned in a MultiPolygon.

    If coverage_union op is provided, geometries must be polygons and
    topologically related or this will produce bad output or fail outright.
    See docs for coverage_union in GEOS.

    Parameters
    ----------
    geometries : ndarray of single part polygons
    grid_size : [type], optional (default: None)
        provided to union_all; otherwise no effect
    op : str, one of {'union', 'coverage_union'}

    Returns
    -------
    MultiPolygon
    """

    if not (pg.get_type_id(geometries) == 3).all():
        print("Inputs to union or combine must be single-part geometries")

    if len(geometries) == 1:
        return pg.multipolygons(geometries)

    tree = pg.STRtree(geometries)
    left, right = tree.query_bulk(geometries, predicate="intersects")
    # drop self intersections
    ix = left != right
    left = left[ix]
    right = right[ix]

    # no intersections, just combine parts
    if len(left) == 0:
        return pg.multipolygons(geometries)

    # find groups of contiguous geometries and union them together individually
    contiguous = np.sort(np.unique(np.concatenate([left, right])))
    discontiguous = np.setdiff1d(np.arange(len(geometries), dtype="uint"),
                                 contiguous)
    groups = find_adjacent_groups(left, right)

    parts = []

    if op == "coverage_union":
        for group in groups:
            parts.extend(
                pg.get_parts(pg.coverage_union_all(geometries[list(group)])))

    else:
        for group in groups:
            parts.extend(
                pg.get_parts(
                    pg.union_all(geometries[list(group)],
                                 grid_size=grid_size)))

    parts.extend(pg.get_parts(geometries[discontiguous]))

    return pg.multipolygons(parts)
    def rotate(self, *angles, origin):
        r"""
        Performs a 2D or 3D rotation on all the coordinates.

        2D
            .. math::

                \begin{bmatrix}
                    x' \\ y' \\ 1
                \end{bmatrix}
                &=
                \begin{bmatrix}
                    cos(a) & -sin(a) & x_{off} \\
                    sin(a) & cos(a)  & y_{off} \\
                    0      & 0       & 1 \\
                \end{bmatrix}
                \begin{bmatrix}
                    x \\ y \\ 1
                \end{bmatrix}
                \\
                x_{off} &= x_{origin} - x_{origin}*cos(a) + y_{origin}*sin(a) \\
                y_{off} &= y_{origin} - x_{origin}*sin(a) - y_{origin}*cos(a)

        3D
            .. math::

                \begin{bmatrix}
                    x' \\ y' \\ z' \\ 1
                \end{bmatrix}
                &=
                \begin{bmatrix}
                    cos(a_z)*cos(a_y) &
                    cos(a_z)*sin(a_y)*sin(a_x) - sin(a_z)*cos(a_x) &
                    cos(a_z)*sin(a_y)*cos(a_x) + sin(a_z)*sin(a_x) &
                    x_{off} \\
                    sin(a_z)*cos(a_y) &
                    sin(a_z)*sin(a_y)*sin(a_x) + cos(a_z)*cos(a_x) &
                    sin(a_z)*sin(a_y)*cos(a_x) - cos(a_z)*sin(a_x) &
                    y_{off} \\
                    -sin(a_y) &
                    cos(a_y)*sin(a_x) &
                    cos(a_y)*cos(a_x) &
                    z_{off} \\
                    0 & 0 & 0 & 1 \\
                \end{bmatrix}
                \begin{bmatrix}
                    x \\ y \\ z \\ 1
                \end{bmatrix}
                \\
                x_{off} &= x_{origin} - (a)*x_{origin} - (b)*y_{origin} - (c)*z_{origin} \\
                y_{off} &= y_{origin} - (d)*x_{origin} - (e)*y_{origin} - (f)*z_{origin} \\
                z_{off} &= z_{origin} - (g)*x_{origin} - (h)*y_{origin} - (i)*z_{origin}

        Args:
            angles (float): 2D rotation angle or X,Y,Z 3D rotation angles in radians.
            origin (pygeos.lib.Geometry or list-like): Origin point for the transformation.

        Returns:
            pandas.Series: Transformed geometries.
        """
        if origin is None:
            origin = (0, 0, 0)
        elif isinstance(origin, pygeos.lib.Geometry):
            if pygeos.get_type_id(origin) != 0:
                raise TypeError('Origin should be a single point geometry')
            origin = np.nan_to_num(pygeos.get_coordinates(origin,
                                                          True)).squeeze()

        if len(angles) == 1:
            x0, y0 = origin[:2]
            ca = cos(angles[0])
            sa = sin(angles[0])
            result = self._obj.array.affine((
                ca,
                -sa,
                sa,
                ca,
                x0 - x0 * ca + y0 * sa,
                y0 - x0 * sa - y0 * ca,
            ))
        elif len(angles) == 3:
            x0, y0, z0 = origin[:3]
            cx, cy, cz = (cos(a) for a in angles)
            sx, sy, sz = (sin(a) for a in angles)
            a = cz * cy
            b = cz * sy * sx - sz * cx
            c = cz * sy * cx + sz * sx
            d = sz * cy
            e = sz * sy * sx + cz * cx
            f = sz * sy * cx - cz * sx
            g = -sy
            h = cy * sx
            i = cy * cx
            result = self._obj.array.affine((
                a,
                b,
                c,
                d,
                e,
                f,
                g,
                h,
                i,
                x0 - a * x0 - b * y0 - c * z0,
                y0 - d * x0 - e * y0 - f * z0,
                z0 - g * x0 - h * y0 - i * z0,
            ))
        else:
            raise ValueError(
                'The rotate transformation requires 1 or 3 angles')

        return pd.Series(result, index=self._obj.index, name='rotate')
Beispiel #19
0
def test_get_exterior_ring():
    actual = pygeos.get_exterior_ring([polygon, polygon_with_hole])
    assert (pygeos.get_type_id(actual) == 2).all()
Beispiel #20
0
def test_get_type_id():
    actual = pygeos.get_type_id(all_types).tolist()
    assert actual == [0, 1, 2, 3, 4, 5, 6, 7, 7]
    def scale(self, x, y, z=None, *, origin=None):
        r"""
        Performs a 2D or 3D scaling on all the coordinates.

        2D
            .. math::

                \begin{bmatrix}
                    x' \\ y' \\ 1
                \end{bmatrix}
                &=
                \begin{bmatrix}
                    x_s & 0 & x_{off} \\
                    0 & y_s & y_{off} \\
                    0 & 0 & 1 \\
                \end{bmatrix}
                \begin{bmatrix}
                    x \\ y \\ 1
                \end{bmatrix}
                \\
                x_{off} &= x_{origin} - x_{origin}*x_s \\
                y_{off} &= y_{origin} - y_{origin}*y_s

        3D
            .. math::

                \begin{bmatrix}
                    x' \\ y' \\ z' \\ 1
                \end{bmatrix}
                &=
                \begin{bmatrix}
                    x_s & 0 & 0 & x_{off} \\
                    0 & y_s & 0 & y_{off} \\
                    0 & 0 & z_s & z_{off} \\
                    0 & 0 & 0 & 1 \\
                \end{bmatrix}
                \begin{bmatrix}
                    x \\ y \\ z \\ 1
                \end{bmatrix}
                \\
                x_{off} &= x_{origin} - x_{origin}*x_s \\
                y_{off} &= y_{origin} - y_{origin}*y_s \\
                z_{off} &= z_{origin} - z_{origin}*z_s

        Args:
            x (float): Scaling value in the X direction.
            y (float): Scaling value in the Y direction.
            z (float, optional): Scaling value in the Z direction; Default **None**.
            origin (pygeos.lib.Geometry or list-like): Origin point for the transformation.

        Returns:
            pandas.Series: Transformed geometries.
        """
        if origin is None:
            origin = (0, 0, 0)
        elif isinstance(origin, pygeos.lib.Geometry):
            if pygeos.get_type_id(origin) != 0:
                raise TypeError('Origin should be a single point geometry')
            origin = np.nan_to_num(pygeos.get_coordinates(origin,
                                                          True)).squeeze()

        if z is None:
            x0, y0 = origin[:2]
            result = self._obj.array.affine((
                x,
                0,
                0,
                y,
                x0 - x * x0,
                y0 - y * y0,
            ))
        else:
            x0, y0, z0 = origin[:3]
            result = self._obj.array.affine((
                x,
                0,
                0,
                0,
                y,
                0,
                0,
                0,
                z,
                x0 - x * x0,
                y0 - y * y0,
                z0 - z * z0,
            ))

        return pd.Series(result, index=self._obj.index, name='scale')
    def skew(self, *angles, origin=None):
        r"""
        Performs a 2D or 3D skew/shear transformation on all the coordinates.

        2D
            .. math::

                \begin{bmatrix}
                    x' \\ y' \\ 1
                \end{bmatrix}
                &=
                \begin{bmatrix}
                    1 & a_{xy} & x_{off} \\
                    a_{yx} & 1 & y_{off} \\
                    0 & 0 & 1 \\
                \end{bmatrix}
                \begin{bmatrix}
                    x \\ y \\ 1
                \end{bmatrix}
                \\
                x_{off} &= -(y_{origin}*a_{xy}) \\
                y_{off} &= -(x_{origin}*a_{yx})

        3D
            .. math::

                \begin{bmatrix}
                    x' \\ y' \\ z' \\ 1
                \end{bmatrix}
                &=
                \begin{bmatrix}
                    1 & a_{xy} & a_{xz} & x_{off} \\
                    a_{yx} & 1 & a_{yz} & y_{off} \\
                    a_{zx} & a_{zy} & 1 & z_{off} \\
                    0 & 0 & 0 & 1 \\
                \end{bmatrix}
                \begin{bmatrix}
                    x \\ y \\ z \\ 1
                \end{bmatrix}
                \\
                x_{off} &= -(y_{origin}*a_{xy} + z_{origin}*a_{xz}) \\
                y_{off} &= -(x_{origin}*a_{yx} + z_{origin}*a_{yz}) \\
                z_{off} &= -(x_{origin}*a_{zx} + y_{origin}*a_{zy})

        Args:
            angles (float): skewing angles (2D: ``[x, y]`` ; 3D: ``[xy, xz, yx, yz, zx, zy]``)
            origin (pygeos.lib.Geometry or list-like): Origin point for the transformation.

        Returns:
            pandas.Series: Transformed geometries.
        """
        if origin is None:
            origin = (0, 0, 0)
        elif isinstance(origin, pygeos.lib.Geometry):
            if pygeos.get_type_id(origin) != 0:
                raise TypeError('Origin should be a single point geometry')
            origin = np.nan_to_num(pygeos.get_coordinates(origin,
                                                          True)).squeeze()

        if len(angles) == 2:
            x0, y0 = origin[:2]
            x, y = (tan(a) for a in angles)
            result = self._obj.array.affine((
                1,
                x,
                y,
                1,
                -(y0 * x),
                -(x0 * y),
            ))
        elif len(angles) == 6:
            x0, y0, z0 = origin[:3]
            xy, xz, yx, yz, zx, zy = (tan(a) for a in angles)
            result = self._obj.array.affine((
                1,
                xy,
                xz,
                yx,
                1,
                yz,
                zx,
                zy,
                1,
                -(y0 * xy + z0 * xz),
                -(x0 * yx + z0 * yz),
                -(x0 * zx + y0 * zy),
            ))
        else:
            raise ValueError('The skew transformation requires 2 or 6 angles')

        return pd.Series(result, index=self._obj.index, name='skew')
        pd.DataFrame(
            sjoin_geometry(
                pd.Series(dams.geometry.values.data, index=dams.index),
                pd.Series(flowlines.geometry.values.data, flowlines.index),
            ).rename("lineID")
        )
        .reset_index()
        .join(dams.geometry, on="damID")
        .join(flowlines.geometry.rename("flowline"), on="lineID")
    ).reset_index(drop=True)
    print(f"Found {len(dams):,} joins in {time() - join_start:.2f}s")

    print("Extracting intersecting flowlines...")
    # Only keep the joins for lines that significantly cross (have a line / multiline and not a point)
    clipped = pg.intersection(dams.geometry.values.data, dams.flowline.values.data)
    t = pg.get_type_id(clipped)
    dams = dams.loc[(t == 1) | (t == 5)].copy()

    # find all joins for lines that start or end at these dams
    j = find_joins(
        joins,
        dams.lineID.unique(),
        downstream_col="downstream_id",
        upstream_col="upstream_id",
    )

    def find_upstreams(ids):
        if len(ids) == 1:
            return ids

        # multiple segments, find the dowstream ones
Beispiel #24
0
                {"break_geometry": breaks.take(left)}, index=df.index.take(right)
            )
            grouped = pairs.groupby(level=0).break_geometry.apply(
                lambda g: pg.multipolygons(g.values.data)
            )
            df.loc[grouped.index, "geometry"] = pg.difference(
                df.loc[grouped.index].geometry.values.data, grouped.values
            )

            df = explode(df).reset_index(drop=True)

    # make sure all polygons are valid
    ix = ~pg.is_valid(df.geometry.values.data)
    if ix.sum():
        print(f"Repairing {ix.sum()} invalid waterbodies")
        df.loc[ix, "geometry"] = pg.make_valid(df.loc[ix].geometry.values.data)
        df = explode(explode(df))
        df = df.loc[pg.get_type_id(df.geometry.values.data) == 3].reset_index()

    # assign a new unique wbID
    df["wbID"] = df.index.values.astype("uint32") + 1 + int(huc2) * 1000000
    df["km2"] = pg.area(df.geometry.values.data) / 1e6

    df.to_feather(huc2_dir / "waterbodies.feather")
    write_dataframe(df, huc2_dir / "waterbodies.gpkg")

    print("--------------------")
    print(f"HUC2: {huc2} done in {time() - huc2_start:.0f}s\n\n")

print(f"Done in {time() - start:.2f}s\n============================")
Beispiel #25
0
def test_from_wkb_point_empty(wkb, expected_type):
    geom = pygeos.from_wkb(wkb)
    # POINT (nan nan) transforms to an empty point
    # Note that the dimensionality (2D/3D) is GEOS-version dependent
    assert pygeos.is_empty(geom)
    assert pygeos.get_type_id(geom) == expected_type
# set the CRS, it is same as 5070 but not recognized properly
df = df.set_crs(DATA_CRS)

# drop BOEM lease block groups
df = df.loc[df.Agg_Src != "USGS_PADUS2_0Marine_BOEM_Block_Dissolve"].drop(
    columns=["Agg_Src"])

tree = pg.STRtree(df.geometry.values.data)
ix = tree.query(bnd_df.geometry.values.data[0], predicate="intersects")
df = df.iloc[ix].copy()

print("making valid...")
df["geometry"] = pg.make_valid(df.geometry.values.data)

df = explode(df).reset_index()
# there are some geometry errors after cleaning up above, keep only polys
df = df.loc[pg.get_type_id(df.geometry.values.data) == 3].copy()

print("Writing files")
df.to_feather(out_dir / "ownership.feather")
write_dataframe(df, data_dir / "boundaries/ownership.gpkg", driver="GPKG")

# Write for tiles
print("Writing GeoJSON for tiles")
write_dataframe(
    df[["geometry", "Own_Type", "GAP_Sts"]].to_crs(GEO_CRS),
    tile_dir / "ownership.geojson",
    driver="GeoJSONSeq",
)
Beispiel #27
0
def geom_type(data):
    if compat.USE_PYGEOS:
        res = pygeos.get_type_id(data)
        return geometry_type_values[np.searchsorted(geometry_type_ids, res)]
    else:
        return _unary_op("geom_type", data, null_value=None)
Beispiel #28
0
import numpy as np
import pytest
import pygeos
from pygeos import Geometry
from pygeos.decorators import UnsupportedGEOSOperation

from .common import point, all_types, polygon, multi_polygon

# fixed-precision operations raise GEOS exceptions on mixed dimension geometry collections
all_single_types = [g for g in all_types if not pygeos.get_type_id(g) == 7]

SET_OPERATIONS = (
    pygeos.difference,
    pygeos.intersection,
    pygeos.symmetric_difference,
    pygeos.union,
    # pygeos.coverage_union is tested seperately
)

REDUCE_SET_OPERATIONS = (
    (pygeos.intersection_all, pygeos.intersection),
    (pygeos.symmetric_difference_all, pygeos.symmetric_difference),
    (pygeos.union_all, pygeos.union),
    # (pygeos.coverage_union_all, pygeos.coverage_union) is tested seperately
)

# operations that support fixed precision
REDUCE_SET_OPERATIONS_PREC = ((pygeos.union_all, pygeos.union), )

reduce_test_data = [
    pygeos.box(0, 0, 5, 5),
Beispiel #29
0
def test_get_type_id():
    assert pygeos.get_type_id(all_types).tolist()[:-1] == list(range(8))
    def _pandas(cls, column, **kwargs):

        column_shape_format = kwargs.get("column_shape_format")
        place = kwargs.get("place")
        geocoder = kwargs.get("geocoder")
        geocoder_config = kwargs.get("geocoder_config")
        min_value = kwargs.get("min_value")
        max_value = kwargs.get("max_value")
        strict_min = kwargs.get("strict_min")
        strict_max = kwargs.get("strict_max")
        units = kwargs.get("units")

        if min_value is None and max_value is None:
            raise ValueError("min_value and max_value cannot both be None")
        if min_value is not None and max_value is not None and min_value > max_value:
            raise ValueError("min_value cannot be greater than max_value")

        if geocoder not in ["nominatim", "pickpoint", "openmapquest"]:
            raise NotImplementedError(
                "The geocoder is not implemented for this method.")

        # find the reference shape with the geocoder.
        if geocoder is not None:
            try:
                # Specify the default parameters for Nominatim and run query. User is responsible for config and query params otherwise.
                query_params = dict(exactly_one=True, geometry="wkt")
                location = cls.geocode(geocoder, geocoder_config, place,
                                       query_params)
            except:
                raise Exception(
                    "Geocoding configuration and query failed to produce a valid result."
                )
        else:
            raise Exception(
                "A valid geocoder must be provided for this method. See GeoPy for reference."
            )

        # Load the column into a pygeos Geometry vector from numpy array (Series not supported).
        if column_shape_format == "wkt":
            shape_test = geos.from_wkt(column.to_numpy(), on_invalid="ignore")
        elif column_shape_format == "wkb":
            shape_test = geos.from_wkb(column.to_numpy(), on_invalid="ignore")
        elif column_shape_format == "lonlat":
            shape_df = pd.DataFrame(column.to_list(), columns=("lon", "lat"))
            shape_test = geos.points(shape_df.lon, y=shape_df.lat)
        elif column_shape_format == "latlon":
            shape_df = pd.DataFrame(column.to_list(), columns=("lat", "lon"))
            shape_test = geos.points(shape_df.lon, y=shape_df.lat)
        else:
            raise NotImplementedError(
                "Column values shape format not implemented.")

        # verify that all shapes are points and if not, convert to centroid point.
        points_test = pd.Series(shape_test)
        if not points_test.apply(lambda x: geos.get_type_id(x) == 0).all():
            points_test = points_test.map(geos.centroid)

        # convert the geos point to a geopy point.
        points_test = points_test.apply(
            lambda x: lonlat(geos.get_x(x), geos.get_y(x)))

        if location is None:
            raise Exception("Geocoding failed to return a result.")
        else:
            point_ref = lonlat(location.longitude, location.latitude)

        # calculate the distance between the points using geopy
        if units in [
                "km", "kilometers", "kilometres", "kilometer", "kilometre"
        ]:
            column_dist = points_test.apply(
                lambda p: distance(p, point_ref).km)
        elif units in ["m", "meters", "metres", "meter", "metre"]:
            column_dist = points_test.apply(lambda p: distance(p, point_ref).m)
        elif units in ["mi", "miles", "mile"]:
            column_dist = points_test.apply(
                lambda p: distance(p, point_ref).mi)
        elif units in ["ft", "feet", "foot"]:
            column_dist = points_test.apply(
                lambda p: distance(p, point_ref).ft)
        else:
            raise NotImplementedError(
                "Unit conversion has not yet been implemented. Please use one of km, m, mi, ft"
            )

        # Evaluate the between statement (from column_values_between.py)
        if min_value is None:
            if strict_max:
                return column_dist < max_value
            else:
                return column_dist <= max_value

        elif max_value is None:
            if strict_min:
                return min_value < column_dist
            else:
                return min_value <= column_dist

        else:
            if strict_min and strict_max:
                return (min_value < column_dist) & (column_dist < max_value)
            elif strict_min:
                return (min_value < column_dist) & (column_dist <= max_value)
            elif strict_max:
                return (min_value <= column_dist) & (column_dist < max_value)
            else:
                return (min_value <= column_dist) & (column_dist <= max_value)