-
Notifications
You must be signed in to change notification settings - Fork 55
Spatial decoding #698
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Spatial decoding #698
Changes from 20 commits
115e341
745d785
a313da3
6f8bdf9
4ef5206
37db0b1
d01fdbc
f25ede9
9e9d50a
4e195f8
bb4b3e4
b94dada
7f7dfbb
773df87
f6bd630
cc972d2
1b9b095
9a0e285
5d2c0c3
44cc3f3
7849c6c
2aed9dc
4ed19c6
ee7fbc1
2648ef9
a0b963a
cfe9b84
a1a52f6
0275563
c349c08
96e8a5e
09be1c0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2002,4 +2002,156 @@ public function getSupportForSpatialAxisOrder(): bool | |
| { | ||
| return false; | ||
| } | ||
|
|
||
| public function decodePoint(string $wkb): array | ||
| { | ||
| if (str_starts_with(strtoupper($wkb), 'POINT(')) { | ||
| $start = strpos($wkb, '(') + 1; | ||
| $end = strrpos($wkb, ')'); | ||
| $inside = substr($wkb, $start, $end - $start); | ||
|
|
||
| $coords = explode(' ', trim($inside)); | ||
| return [(float)$coords[0], (float)$coords[1]]; | ||
| } | ||
|
|
||
| $bin = hex2bin($wkb); | ||
|
|
||
| $isLE = ord($bin[0]) === 1; | ||
| $type = unpack($isLE ? 'V' : 'N', substr($bin, 1, 4))[1]; | ||
| $offset = 5 + (($type & 0x20000000) ? 4 : 0); | ||
|
|
||
| $fmt = $isLE ? 'e' : 'E'; // little vs big endian double | ||
| $x = unpack($fmt, substr($bin, $offset, 8))[1]; | ||
| $y = unpack($fmt, substr($bin, $offset + 8, 8))[1]; | ||
|
|
||
| return [(float)$x, (float)$y]; | ||
| } | ||
|
|
||
| public function decodeLinestring(mixed $wkb): array | ||
| { | ||
| if (str_starts_with(strtoupper($wkb), 'LINESTRING(')) { | ||
| $start = strpos($wkb, '(') + 1; | ||
| $end = strrpos($wkb, ')'); | ||
| $inside = substr($wkb, $start, $end - $start); | ||
|
|
||
| $points = explode(',', $inside); | ||
| return array_map(function ($point) { | ||
| $coords = explode(' ', trim($point)); | ||
| return [(float)$coords[0], (float)$coords[1]]; | ||
| }, $points); | ||
| } | ||
|
|
||
| if (ctype_xdigit($wkb)) { | ||
| $wkb = hex2bin($wkb); | ||
| } | ||
|
|
||
| if (strlen($wkb) < 9) { | ||
| throw new DatabaseException("WKB too short to be a valid geometry"); | ||
| } | ||
|
|
||
| $byteOrder = ord($wkb[0]); | ||
| if ($byteOrder === 0) { | ||
| throw new DatabaseException("Big-endian WKB not supported"); | ||
| } elseif ($byteOrder !== 1) { | ||
| throw new DatabaseException("Invalid byte order in WKB"); | ||
| } | ||
|
|
||
| // Type + SRID flag | ||
| $typeField = unpack('V', substr($wkb, 1, 4))[1]; | ||
| $geomType = $typeField & 0xFF; | ||
| $hasSRID = ($typeField & 0x20000000) !== 0; | ||
|
|
||
| if ($geomType !== 2) { // 2 = LINESTRING | ||
| throw new DatabaseException("Not a LINESTRING geometry type, got {$geomType}"); | ||
| } | ||
|
|
||
| $offset = 5; | ||
| if ($hasSRID) { | ||
| $offset += 4; | ||
| } | ||
|
|
||
| $numPoints = unpack('V', substr($wkb, $offset, 4))[1]; | ||
| $offset += 4; | ||
|
|
||
| $points = []; | ||
| for ($i = 0; $i < $numPoints; $i++) { | ||
| $x = unpack('e', substr($wkb, $offset, 8))[1]; | ||
| $offset += 8; | ||
| $y = unpack('e', substr($wkb, $offset, 8))[1]; | ||
| $offset += 8; | ||
| $points[] = [(float)$x, (float)$y]; | ||
| } | ||
|
|
||
| return $points; | ||
| } | ||
|
Comment on lines
+2038
to
+2146
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add proper type hints and improve error handling The method needs comprehensive type specifications and validation. +/**
+ * @param mixed $wkb WKT or WKB hex string
+ * @return array<int, array{0: float, 1: float}>
+ * @throws DatabaseException
+ */
public function decodeLinestring(mixed $wkb): array
{
+ if (!is_string($wkb)) {
+ throw new DatabaseException('WKB must be a string');
+ }
+
if (str_starts_with(strtoupper($wkb), 'LINESTRING(')) {
$start = strpos($wkb, '(') + 1;
$end = strrpos($wkb, ')');
$inside = substr($wkb, $start, $end - $start);
$points = explode(',', $inside);
return array_map(function ($point) {
$coords = explode(' ', trim($point));
+ if (count($coords) !== 2 || !is_numeric($coords[0]) || !is_numeric($coords[1])) {
+ throw new DatabaseException('Invalid point in LINESTRING');
+ }
return [(float)$coords[0], (float)$coords[1]];
}, $points);
}
- var_dump($wkb);
if (ctype_xdigit($wkb)) {
$wkb = hex2bin($wkb);
+ if ($wkb === false) {
+ throw new DatabaseException('Invalid hex WKB');
+ }
}
if (strlen($wkb) < 9) {
throw new DatabaseException("WKB too short to be a valid geometry");
}
$byteOrder = ord($wkb[0]);
if ($byteOrder === 0) {
throw new DatabaseException("Big-endian WKB not supported");
} elseif ($byteOrder !== 1) {
throw new DatabaseException("Invalid byte order in WKB");
}
// Type + SRID flag
- $typeField = unpack('V', substr($wkb, 1, 4))[1];
+ $typeFieldArr = unpack('V', substr($wkb, 1, 4));
+ if ($typeFieldArr === false || !isset($typeFieldArr[1])) {
+ throw new DatabaseException('Failed to decode LINESTRING type');
+ }
+ $typeField = $typeFieldArr[1];
$geomType = $typeField & 0xFF;
$hasSRID = ($typeField & 0x20000000) !== 0;
if ($geomType !== 2) { // 2 = LINESTRING
throw new DatabaseException("Not a LINESTRING geometry type, got {$geomType}");
}
$offset = 5;
if ($hasSRID) {
$offset += 4;
}
- $numPoints = unpack('V', substr($wkb, $offset, 4))[1];
+ $numPointsArr = unpack('V', substr($wkb, $offset, 4));
+ if ($numPointsArr === false || !isset($numPointsArr[1])) {
+ throw new DatabaseException('Failed to decode LINESTRING point count');
+ }
+ $numPoints = $numPointsArr[1];
$offset += 4;
$points = [];
for ($i = 0; $i < $numPoints; $i++) {
- $x = unpack('e', substr($wkb, $offset, 8))[1]; $offset += 8;
- $y = unpack('e', substr($wkb, $offset, 8))[1]; $offset += 8;
+ $xArr = unpack('e', substr($wkb, $offset, 8));
+ $yArr = unpack('e', substr($wkb, $offset + 8, 8));
+ if ($xArr === false || $yArr === false || !isset($xArr[1], $yArr[1])) {
+ throw new DatabaseException('Failed to decode LINESTRING point');
+ }
+ $x = $xArr[1];
+ $y = $yArr[1];
$points[] = [(float)$x, (float)$y];
+ $offset += 16;
}
return $points;
}🧰 Tools🪛 GitHub Actions: CodeQL[error] 2038-2038: Method Utopia\Database\Adapter\Postgres::decodeLinestring() return type has no value type specified in iterable type array. [error] 2070-2070: Cannot access offset 1 on array|false. [error] 2083-2083: Cannot access offset 1 on array|false. [error] 2088-2088: Cannot access offset 1 on array|false. [error] 2089-2089: Cannot access offset 1 on array|false. 🤖 Prompt for AI Agents |
||
|
|
||
| public function decodePolygon(string $wkb): array | ||
| { | ||
| // POLYGON((x1,y1),(x2,y2)) | ||
| if (str_starts_with($wkb, 'POLYGON((')) { | ||
| $start = strpos($wkb, '((') + 2; | ||
| $end = strrpos($wkb, '))'); | ||
| $inside = substr($wkb, $start, $end - $start); | ||
|
|
||
| $rings = explode('),(', $inside); | ||
| return array_map(function ($ring) { | ||
| $points = explode(',', $ring); | ||
| return array_map(function ($point) { | ||
| $coords = explode(' ', trim($point)); | ||
| return [(float)$coords[0], (float)$coords[1]]; | ||
| }, $points); | ||
| }, $rings); | ||
| } | ||
|
|
||
| // Convert hex string to binary if needed | ||
| if (preg_match('/^[0-9a-fA-F]+$/', $wkb)) { | ||
| $wkb = hex2bin($wkb); | ||
| if ($wkb === false) { | ||
| throw new \RuntimeException("Invalid hex WKB"); | ||
| } | ||
| } | ||
|
|
||
| if (strlen($wkb) < 9) { | ||
| throw new \RuntimeException("WKB too short"); | ||
| } | ||
|
|
||
| $byteOrder = ord($wkb[0]); | ||
| $isLE = $byteOrder === 1; // assume little-endian | ||
| $uInt32 = 'V'; // little-endian 32-bit unsigned | ||
| $uDouble = 'd'; // little-endian double | ||
|
|
||
| $typeInt = unpack($uInt32, substr($wkb, 1, 4))[1]; | ||
| $hasSrid = ($typeInt & 0x20000000) !== 0; | ||
| $geomType = $typeInt & 0xFF; | ||
|
|
||
| if ($geomType !== 3) { // 3 = POLYGON | ||
| throw new \RuntimeException("Not a POLYGON geometry type, got {$geomType}"); | ||
| } | ||
|
|
||
| $offset = 5; | ||
| if ($hasSrid) { | ||
| $offset += 4; | ||
| } | ||
|
|
||
| // Number of rings | ||
| $numRings = unpack($uInt32, substr($wkb, $offset, 4))[1]; | ||
| $offset += 4; | ||
|
|
||
| $rings = []; | ||
| for ($r = 0; $r < $numRings; $r++) { | ||
| $numPoints = unpack($uInt32, substr($wkb, $offset, 4))[1]; | ||
| $offset += 4; | ||
| $points = []; | ||
| for ($i = 0; $i < $numPoints; $i++) { | ||
| $x = unpack($uDouble, substr($wkb, $offset, 8))[1]; | ||
| $y = unpack($uDouble, substr($wkb, $offset + 8, 8))[1]; | ||
| $points[] = [(float)$x, (float)$y]; | ||
| $offset += 16; | ||
| } | ||
| $rings[] = $points; | ||
| } | ||
|
|
||
| return $rings; // array of rings, each ring is array of [x,y] | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.