• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

brick / geo / 17456208570

04 Sep 2025 07:10AM UTC coverage: 50.432%. Remained the same
17456208570

push

github

BenMorel
Use @extends and @implements instead of @template-* variants

For consistency with the rest of the project.

1867 of 3702 relevant lines covered (50.43%)

1140.21 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.98
/src/LineString.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Brick\Geo;
6

7
use ArrayIterator;
8
use Brick\Geo\Attribute\NoProxy;
9
use Brick\Geo\Exception\CoordinateSystemException;
10
use Brick\Geo\Exception\EmptyGeometryException;
11
use Brick\Geo\Exception\InvalidGeometryException;
12
use Brick\Geo\Exception\NoSuchGeometryException;
13
use Brick\Geo\Projector\Projector;
14
use Countable;
15
use IteratorAggregate;
16
use Override;
17

18
use function array_map;
19
use function array_reduce;
20
use function array_values;
21
use function count;
22
use function max;
23
use function min;
24

25
/**
26
 * A LineString is a Curve with linear interpolation between Points.
27
 *
28
 * Each consecutive pair of Points defines a line segment.
29
 *
30
 * @implements IteratorAggregate<int<0, max>, Point>
31
 *
32
 * @final
33
 */
34
class LineString extends Curve implements Countable, IteratorAggregate
35
{
36
    /**
37
     * The Points that compose this LineString.
38
     *
39
     * An empty LineString contains no points.
40
     * A non-empty LineString contains a minimum of 2 points.
41
     *
42
     * @var list<Point>
43
     */
44
    protected array $points = [];
45

46
    /**
47
     * A LineString must be composed of 2 points or more, or 0 points for an empty LineString.
48
     * A LineString with exactly 1 point is not allowed.
49
     *
50
     * The coordinate system of each of the points must match the one of the LineString.
51
     *
52
     * @param CoordinateSystem $cs        The coordinate system of the LineString.
53
     * @param Point            ...$points The points that compose the LineString.
54
     *
55
     * @throws InvalidGeometryException  If only one point was given.
56
     * @throws CoordinateSystemException If different coordinate systems are used.
57
     */
58
    public function __construct(CoordinateSystem $cs, Point ...$points)
59
    {
60
        parent::__construct($cs, ! $points);
14,687✔
61

62
        if (! $points) {
14,687✔
63
            return;
1,052✔
64
        }
65

66
        CoordinateSystem::check($this, ...$points);
13,650✔
67

68
        if (count($points) < 2) {
13,650✔
69
            throw new InvalidGeometryException('A LineString must be composed of at least 2 points.');
8✔
70
        }
71

72
        $this->points = array_values($points);
13,642✔
73
    }
74

75
    /**
76
     * Creates a non-empty LineString composed of the given points.
77
     *
78
     * @param Point $point1    The first point.
79
     * @param Point ...$pointN The subsequent points.
80
     *
81
     * @throws InvalidGeometryException  If only one point was given.
82
     * @throws CoordinateSystemException If the points use different coordinate systems.
83
     */
84
    public static function of(Point $point1, Point ...$pointN): LineString
85
    {
86
        return new LineString($point1->coordinateSystem(), $point1, ...$pointN);
×
87
    }
88

89
    /**
90
     * Creates a rectangle out of two 2D corner points.
91
     *
92
     * The result is a linear ring (closed and simple).
93
     *
94
     * @throws EmptyGeometryException    If any of the points is empty.
95
     * @throws CoordinateSystemException If the points use different coordinate systems, or are not 2D.
96
     *
97
     * @psalm-suppress PossiblyNullArgument
98
     */
99
    public static function rectangle(Point $a, Point $b): LineString
100
    {
101
        $cs = $a->coordinateSystem();
56✔
102

103
        if (! $cs->isEqualTo($b->coordinateSystem())) {
56✔
104
            throw CoordinateSystemException::dimensionalityMix($cs, $b->coordinateSystem());
32✔
105
        }
106

107
        if ($cs->coordinateDimension() !== 2) {
24✔
108
            throw new CoordinateSystemException(__METHOD__ . ' expects 2D points.');
×
109
        }
110

111
        if ($a->isEmpty() || $b->isEmpty()) {
24✔
112
            throw new EmptyGeometryException('Points cannot be empty.');
×
113
        }
114

115
        $x1 = min($a->x(), $b->x());
24✔
116
        $x2 = max($a->x(), $b->x());
24✔
117

118
        $y1 = min($a->y(), $b->y());
24✔
119
        $y2 = max($a->y(), $b->y());
24✔
120

121
        $p1 = new Point($cs, $x1, $y1);
24✔
122
        $p2 = new Point($cs, $x2, $y1);
24✔
123
        $p3 = new Point($cs, $x2, $y2);
24✔
124
        $p4 = new Point($cs, $x1, $y2);
24✔
125

126
        return new LineString($cs, $p1, $p2, $p3, $p4, $p1);
24✔
127
    }
128

129
    #[Override]
130
    public function startPoint(): Point
131
    {
132
        if (count($this->points) === 0) {
648✔
133
            throw new EmptyGeometryException('The LineString is empty and has no start point.');
32✔
134
        }
135

136
        return $this->points[0];
616✔
137
    }
138

139
    #[Override]
140
    public function endPoint(): Point
141
    {
142
        $count = count($this->points);
1,064✔
143

144
        if ($count === 0) {
1,064✔
145
            throw new EmptyGeometryException('The LineString is empty and has no end point.');
32✔
146
        }
147

148
        return $this->points[$count - 1];
1,032✔
149
    }
150

151
    /**
152
     * Returns the number of Points in this LineString.
153
     */
154
    public function numPoints(): int
155
    {
156
        return count($this->points);
1,464✔
157
    }
158

159
    /**
160
     * Returns the specified Point N in this LineString.
161
     *
162
     * @param int $n The point number, 1-based.
163
     *
164
     * @throws NoSuchGeometryException If there is no Point at this index.
165
     */
166
    public function pointN(int $n): Point
167
    {
168
        if (! isset($this->points[$n - 1])) {
176✔
169
            throw new NoSuchGeometryException('There is no Point in this LineString at index ' . $n);
64✔
170
        }
171

172
        return $this->points[$n - 1];
112✔
173
    }
174

175
    /**
176
     * Returns the points that compose this LineString.
177
     *
178
     * @return list<Point>
179
     */
180
    public function points(): array
181
    {
182
        return $this->points;
×
183
    }
184

185
    #[NoProxy, Override]
186
    public function geometryType(): string
187
    {
188
        return 'LineString';
1,938✔
189
    }
190

191
    #[NoProxy, Override]
192
    public function geometryTypeBinary(): int
193
    {
194
        return Geometry::LINESTRING;
2,295✔
195
    }
196

197
    #[Override]
198
    public function getBoundingBox(): BoundingBox
199
    {
200
        return array_reduce(
8✔
201
            $this->points,
8✔
202
            fn (BoundingBox $boundingBox, Point $point) => $boundingBox->extendedWithPoint($point),
8✔
203
            BoundingBox::new(),
8✔
204
        );
8✔
205
    }
206

207
    /**
208
     * @return list<list<float>>
209
     */
210
    #[Override]
211
    public function toArray(): array
212
    {
213
        return array_map(
2,512✔
214
            fn (Point $point) => $point->toArray(),
2,512✔
215
            $this->points,
2,512✔
216
        );
2,512✔
217
    }
218

219
    #[Override]
220
    public function project(Projector $projector): LineString
221
    {
222
        return new LineString(
176✔
223
            $projector->getTargetCoordinateSystem($this->coordinateSystem),
176✔
224
            ...array_map(
176✔
225
                fn (Point $point) => $point->project($projector),
176✔
226
                $this->points,
176✔
227
            ),
176✔
228
        );
176✔
229
    }
230

231
    /**
232
     * Returns the number of points in this LineString.
233
     */
234
    #[Override]
235
    public function count(): int
236
    {
237
        return count($this->points);
4,144✔
238
    }
239

240
    /**
241
     * Returns an iterator for the points in this LineString.
242
     *
243
     * @return ArrayIterator<int<0, max>, Point>
244
     */
245
    #[Override]
246
    public function getIterator(): ArrayIterator
247
    {
248
        return new ArrayIterator($this->points);
9,929✔
249
    }
250

251
    /**
252
     * Returns a copy of this LineString, with the given points added.
253
     */
254
    public function withAddedPoints(Point ...$points): LineString
255
    {
256
        return new LineString($this->coordinateSystem, ...$this->points, ...$points);
40✔
257
    }
258
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc