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

brick / geo / 14057076899

25 Mar 2025 10:14AM UTC coverage: 52.154% (-13.7%) from 65.828%
14057076899

push

github

BenMorel
Wip

0 of 383 new or added lines in 14 files covered. (0.0%)

141 existing lines in 12 files now uncovered.

1634 of 3133 relevant lines covered (52.15%)

396.62 hits per line

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

97.06
/src/Io/Internal/AbstractWkbWriter.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Brick\Geo\Io\Internal;
6

7
use Brick\Geo\CircularString;
8
use Brick\Geo\CompoundCurve;
9
use Brick\Geo\Curve;
10
use Brick\Geo\CurvePolygon;
11
use Brick\Geo\Exception\GeometryIoException;
12
use Brick\Geo\Geometry;
13
use Brick\Geo\GeometryCollection;
14
use Brick\Geo\Io\ByteOrder;
15
use Brick\Geo\LineString;
16
use Brick\Geo\Point;
17
use Brick\Geo\Polygon;
18
use Brick\Geo\PolyhedralSurface;
19

20
/**
21
 * Base class for WkbWriter and EwkbWriter.
22
 *
23
 * @internal
24
 */
25
abstract readonly class AbstractWkbWriter
26
{
27
    private ByteOrder $byteOrder;
28
    private ByteOrder $machineByteOrder;
29

30
    /**
31
     * Whether to support PostGIS-style empty points with NaN coordinates.
32
     * This is not part of the WKB standard.
33
     * This will be disabled by default in WKB, but enabled by default in EWKB.
34
     */
35
    private bool $supportEmptyPointWithNan;
36

37
    /**
38
     * @throws GeometryIoException
39
     */
40
    public function __construct(
41
        ?ByteOrder $byteOrder,
42
        bool $supportEmptyPointWithNan,
43
    ) {
44
        $this->machineByteOrder = WkbTools::getMachineByteOrder();
1,394✔
45
        $this->byteOrder = $byteOrder ?? $this->machineByteOrder;
1,394✔
46
        $this->supportEmptyPointWithNan = $supportEmptyPointWithNan;
1,394✔
47
    }
48

49
    /**
50
     * @param Geometry $geometry The geometry to export as WKB.
51
     *
52
     * @return string The WKB representation of the given geometry.
53
     *
54
     * @throws GeometryIoException If the given geometry cannot be exported as WKB.
55
     */
56
    public function write(Geometry $geometry) : string
57
    {
58
        return $this->doWrite($geometry, true);
1,598✔
59
    }
60

61
    /**
62
     * @param Geometry $geometry The geometry export as WKB write.
63
     * @param bool     $outer    False if the geometry is nested in another geometry, true otherwise.
64
     *
65
     * @return string The WKB representation of the given geometry.
66
     *
67
     * @throws GeometryIoException If the given geometry cannot be exported as WKT.
68
     */
69
    protected function doWrite(Geometry $geometry, bool $outer) : string
70
    {
71
        if ($geometry instanceof Point) {
1,598✔
72
            return $this->writePoint($geometry, $outer);
251✔
73
        }
74

75
        if ($geometry instanceof LineString) {
1,485✔
76
            return $this->writeCurve($geometry, $outer);
436✔
77
        }
78

79
        if ($geometry instanceof CircularString) {
1,324✔
80
            return $this->writeCurve($geometry, $outer);
202✔
81
        }
82

83
        if ($geometry instanceof Polygon) {
1,223✔
84
            return $this->writePolygon($geometry, $outer);
513✔
85
        }
86

87
        if ($geometry instanceof CompoundCurve) {
929✔
88
            return $this->writeComposedGeometry($geometry, $outer);
124✔
89
        }
90

91
        if ($geometry instanceof CurvePolygon) {
829✔
92
            return $this->writeComposedGeometry($geometry, $outer);
98✔
93
        }
94

95
        if ($geometry instanceof GeometryCollection) {
731✔
96
            return $this->writeComposedGeometry($geometry, $outer);
537✔
97
        }
98

99
        if ($geometry instanceof PolyhedralSurface) {
194✔
100
            return $this->writeComposedGeometry($geometry, $outer);
194✔
101
        }
102

UNCOV
103
        throw GeometryIoException::unsupportedGeometryType($geometry->geometryType());
×
104
    }
105

106
    private function packByteOrder() : string
107
    {
108
        return pack('C', $this->byteOrder->value);
1,598✔
109
    }
110

111
    protected function packUnsignedInteger(int $uint) : string
112
    {
113
        return pack(match ($this->byteOrder) {
1,598✔
114
            ByteOrder::BigEndian => 'N',
1,598✔
115
            ByteOrder::LittleEndian => 'V'
1,598✔
116
        }, $uint);
1,598✔
117
    }
118

119
    private function packDouble(float $double) : string
120
    {
121
        $binary = pack('d', $double);
1,000✔
122

123
        if ($this->byteOrder !== $this->machineByteOrder) {
1,000✔
124
            return strrev($binary);
400✔
125
        }
126

127
        return $binary;
600✔
128
    }
129

130
    /**
131
     * @throws GeometryIoException
132
     */
133
    private function packPoint(Point $point) : string
134
    {
135
        if ($point->isEmpty() && ! $this->supportEmptyPointWithNan) {
1,016✔
136
            throw new GeometryIoException(
16✔
137
                'Empty points have no WKB representation. ' .
16✔
138
                'If you want to output empty points with NaN coordinates (PostGIS-style), ' .
16✔
139
                'enable the $supportEmptyPointWithNan option.',
16✔
140
            );
16✔
141
        }
142

143
        $binary = $this->packDouble($point->x() ?? NAN) . $this->packDouble($point->y() ?? NAN);
1,000✔
144

145
        if ($point->coordinateSystem()->hasZ()) {
1,000✔
146
            $binary .= $this->packDouble($point->z() ?? NAN);
415✔
147
        }
148

149
        if ($point->coordinateSystem()->hasM()) {
1,000✔
150
            $binary .= $this->packDouble($point->m() ?? NAN);
400✔
151
        }
152

153
        return $binary;
1,000✔
154
    }
155

156
    /**
157
     * @throws GeometryIoException
158
     */
159
    private function packCurve(Curve $curve) : string
160
    {
161
        if (! $curve instanceof LineString && ! $curve instanceof CircularString) {
942✔
162
            // CompoundCurve is not a list of Points, not sure if WKB supports it!
163
            // For now, let's just not support it ourselves.
UNCOV
164
            throw new GeometryIoException(sprintf('Writing a %s as WKB is not supported.', $curve->geometryType()));
×
165
        }
166

167
        $wkb = $this->packUnsignedInteger($curve->count());
942✔
168

169
        foreach ($curve as $point) {
942✔
170
            $wkb .= $this->packPoint($point);
844✔
171
        }
172

173
        return $wkb;
942✔
174
    }
175

176
    private function writePoint(Point $point, bool $outer) : string
177
    {
178
        $wkb = $this->packByteOrder();
251✔
179
        $wkb.= $this->packHeader($point, $outer);
251✔
180
        $wkb.= $this->packPoint($point);
251✔
181

182
        return $wkb;
235✔
183
    }
184

185
    private function writeCurve(Curve $curve, bool $outer) : string
186
    {
187
        $wkb = $this->packByteOrder();
538✔
188
        $wkb.= $this->packHeader($curve, $outer);
538✔
189
        $wkb.= $this->packCurve($curve);
538✔
190

191
        return $wkb;
538✔
192
    }
193

194
    private function writePolygon(Polygon $polygon, bool $outer) : string
195
    {
196
        $wkb = $this->packByteOrder();
513✔
197
        $wkb.= $this->packHeader($polygon, $outer);
513✔
198
        $wkb.= $this->packUnsignedInteger($polygon->count());
513✔
199

200
        foreach ($polygon as $ring) {
513✔
201
            $wkb .= $this->packCurve($ring);
417✔
202
        }
203

204
        return $wkb;
513✔
205
    }
206

207
    private function writeComposedGeometry(CompoundCurve|CurvePolygon|GeometryCollection|PolyhedralSurface $collection, bool $outer) : string
208
    {
209
        $wkb = $this->packByteOrder();
929✔
210
        $wkb.= $this->packHeader($collection, $outer);
929✔
211
        $wkb.= $this->packUnsignedInteger($collection->count());
929✔
212

213
        foreach ($collection as $geometry) {
929✔
214
            $wkb .= $this->doWrite($geometry, false);
541✔
215
        }
216

217
        return $wkb;
929✔
218
    }
219

220
    abstract protected function packHeader(Geometry $geometry, bool $outer) : string;
221
}
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