コード例 #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")
コード例 #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
コード例 #3
0
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
コード例 #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
コード例 #5
0
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)
コード例 #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)
コード例 #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
コード例 #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)
コード例 #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)
コード例 #10
0
ファイル: test_io.py プロジェクト: martinfleis/pygeos
 def geom_type(self):
     idx = pygeos.get_type_id(self.g)
     return [
         "None",
         "Point",
         "LineString",
         "LinearRing",
         "Polygon",
         "MultiPoint",
         "MultiLineString",
         "MultiPolygon",
         "GeometryCollection",
     ][idx]
コード例 #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)
コード例 #12
0
ファイル: _vectorized.py プロジェクト: so02e/geopandas
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))
コード例 #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
コード例 #14
0
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}
コード例 #15
0
ファイル: test_creation.py プロジェクト: brendan-ward/pygeos
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
コード例 #16
0
ファイル: test_creation.py プロジェクト: brendan-ward/pygeos
 def type_id(self):
     return pygeos.get_type_id(self)
コード例 #17
0
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)
コード例 #18
0
    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')
コード例 #19
0
ファイル: test_geometry.py プロジェクト: brendan-ward/pygeos
def test_get_exterior_ring():
    actual = pygeos.get_exterior_ring([polygon, polygon_with_hole])
    assert (pygeos.get_type_id(actual) == 2).all()
コード例 #20
0
ファイル: test_geometry.py プロジェクト: brendan-ward/pygeos
def test_get_type_id():
    actual = pygeos.get_type_id(all_types).tolist()
    assert actual == [0, 1, 2, 3, 4, 5, 6, 7, 7]
コード例 #21
0
    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')
コード例 #22
0
    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')
コード例 #23
0
        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
コード例 #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============================")
コード例 #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
コード例 #26
0
# 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",
)
コード例 #27
0
ファイル: _vectorized.py プロジェクト: so02e/geopandas
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)
コード例 #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),
コード例 #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)