File: linestring.py

package info (click to toggle)
python-shapely 2.1.1-2
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 2,564 kB
  • sloc: python: 18,650; ansic: 6,615; makefile: 88; sh: 62
file content (211 lines) | stat: -rw-r--r-- 7,599 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
"""Line strings and related utilities."""

import numpy as np

import shapely
from shapely.decorators import deprecate_positional
from shapely.geometry.base import JOIN_STYLE, BaseGeometry
from shapely.geometry.point import Point

__all__ = ["LineString"]


class LineString(BaseGeometry):
    """A geometry type composed of one or more line segments.

    A LineString is a one-dimensional feature and has a non-zero length but
    zero area. It may approximate a curve and need not be straight. A LineString may
    be closed.

    Parameters
    ----------
    coordinates : sequence
        A sequence of (x, y, [,z]) numeric coordinate pairs or triples, or
        an array-like with shape (N, 2) or (N, 3).
        Also can be a sequence of Point objects, or combination of both.

    Examples
    --------
    Create a LineString with two segments

    >>> from shapely import LineString
    >>> a = LineString([[0, 0], [1, 0], [1, 1]])
    >>> a.length
    2.0

    """

    __slots__ = []

    def __new__(self, coordinates=None):
        """Create a new LineString geometry."""
        if coordinates is None:
            # empty geometry
            # TODO better constructor
            return shapely.from_wkt("LINESTRING EMPTY")
        elif isinstance(coordinates, LineString):
            if type(coordinates) is LineString:
                # return original objects since geometries are immutable
                return coordinates
            else:
                # LinearRing
                # TODO convert LinearRing to LineString more directly
                coordinates = coordinates.coords
        else:
            if hasattr(coordinates, "__array__"):
                coordinates = np.asarray(coordinates)
            if isinstance(coordinates, np.ndarray) and np.issubdtype(
                coordinates.dtype, np.number
            ):
                pass
            else:
                # check coordinates on points
                def _coords(o):
                    if isinstance(o, Point):
                        return o.coords[0]
                    else:
                        return [float(c) for c in o]

                coordinates = [_coords(o) for o in coordinates]

        if len(coordinates) == 0:
            # empty geometry
            # TODO better constructor + should shapely.linestrings handle this?
            return shapely.from_wkt("LINESTRING EMPTY")

        geom = shapely.linestrings(coordinates)
        if not isinstance(geom, LineString):
            raise ValueError("Invalid values passed to LineString constructor")
        return geom

    @property
    def __geo_interface__(self):
        """Return a GeoJSON-like mapping of the LineString geometry."""
        return {"type": "LineString", "coordinates": tuple(self.coords)}

    def svg(self, scale_factor=1.0, stroke_color=None, opacity=None):
        """Return SVG polyline element for the LineString geometry.

        Parameters
        ----------
        scale_factor : float
            Multiplication factor for the SVG stroke-width.  Default is 1.
        stroke_color : str, optional
            Hex string for stroke color. Default is to use "#66cc99" if
            geometry is valid, and "#ff3333" if invalid.
        opacity : float
            Float number between 0 and 1 for color opacity. Default value is 0.8

        """
        if self.is_empty:
            return "<g />"
        if stroke_color is None:
            stroke_color = "#66cc99" if self.is_valid else "#ff3333"
        if opacity is None:
            opacity = 0.8
        pnt_format = " ".join(["{},{}".format(*c) for c in self.coords])
        return (
            f'<polyline fill="none" stroke="{stroke_color}" '
            f'stroke-width="{2.0 * scale_factor}" '
            f'points="{pnt_format}" opacity="{opacity}" />'
        )

    @property
    def xy(self):
        """Separate arrays of X and Y coordinate values.

        Examples
        --------
        >>> from shapely import LineString
        >>> x, y = LineString([(0, 0), (1, 1)]).xy
        >>> list(x)
        [0.0, 1.0]
        >>> list(y)
        [0.0, 1.0]

        """
        return self.coords.xy

    # Note: future plan is to change this signature over a few releases:
    # shapely 2.0:
    #   offset_curve(self, distance, quad_segs=16, ...)
    # shapely 2.1: shows deprecation warning about positional 'quad_segs', etc.
    #   same signature as 2.0
    # shapely 2.2(?): enforce keyword-only arguments after 'distance'
    #   offset_curve(self, distance, *, quad_segs=16, ...)

    @deprecate_positional(
        ["quad_segs", "join_style", "mitre_limit"], category=DeprecationWarning
    )
    def offset_curve(
        self,
        distance,
        quad_segs=16,
        join_style=JOIN_STYLE.round,
        mitre_limit=5.0,
    ):
        """Return a (Multi)LineString at a distance from the object.

        The side, left or right, is determined by the sign of the `distance`
        parameter (negative for right side offset, positive for left side
        offset). The resolution of the buffer around each vertex of the object
        increases by increasing the `quad_segs` keyword parameter.

        The join style is for outside corners between line segments. Accepted
        values are JOIN_STYLE.round (1), JOIN_STYLE.mitre (2), and
        JOIN_STYLE.bevel (3).

        The mitre ratio limit is used for very sharp corners. It is the ratio
        of the distance from the corner to the end of the mitred offset corner.
        When two line segments meet at a sharp angle, a miter join will extend
        far beyond the original geometry. To prevent unreasonable geometry, the
        mitre limit allows controlling the maximum length of the join corner.
        Corners with a ratio which exceed the limit will be beveled.

        Note: the behaviour regarding orientation of the resulting line
        depends on the GEOS version. With GEOS < 3.11, the line retains the
        same direction for a left offset (positive distance) or has reverse
        direction for a right offset (negative distance), and this behaviour
        was documented as such in previous Shapely versions. Starting with
        GEOS 3.11, the function tries to preserve the orientation of the
        original line.
        """
        if mitre_limit == 0.0:
            raise ValueError("Cannot compute offset from zero-length line segment")
        elif not np.isfinite(distance):
            raise ValueError("offset_curve distance must be finite")
        return shapely.offset_curve(
            self,
            distance,
            quad_segs=quad_segs,
            join_style=join_style,
            mitre_limit=mitre_limit,
        )

    def parallel_offset(
        self,
        distance,
        side="right",
        resolution=16,
        join_style=JOIN_STYLE.round,
        mitre_limit=5.0,
    ):
        """Alternative method to :meth:`offset_curve` method.

        Older alternative method to the :meth:`offset_curve` method, but uses
        ``resolution`` instead of ``quad_segs`` and a ``side`` keyword
        ('left' or 'right') instead of sign of the distance. This method is
        kept for backwards compatibility for now, but is is recommended to
        use :meth:`offset_curve` instead.
        """
        if side == "right":
            distance *= -1
        return self.offset_curve(
            distance,
            quad_segs=resolution,
            join_style=join_style,
            mitre_limit=mitre_limit,
        )


shapely.lib.registry[1] = LineString