-
-
Save taviroquai/858b9519becf2703687045a04fb87421 to your computer and use it in GitHub Desktop.
<?php | |
/** | |
* Parse GeoPackageBinaryHeader | |
* | |
* References | |
* | |
* http://www.geopackage.org/spec/#gpb_spec | |
* | |
* https://en.wikipedia.org/wiki/Well-known_text | |
* http://php.net/manual/en/function.unpack.php | |
* http://ngageoint.github.io/geopackage-js/ (NodeJS + SQL.js Demo) | |
* | |
* @param string $filename | |
*/ | |
protected function parseGeoPackageGeometry($filename) | |
{ | |
// Default values | |
$header = [ | |
'magic' => '', | |
'version' => 0, | |
'flags' => 0, | |
'srs_id' => 0, | |
'envelope' => [] | |
]; | |
$wkb = ''; | |
// Open binary | |
$h = fopen($filename, 'rb'); | |
if (!$h) { | |
throw new \Exception('Could not open stream (geometry data)'); | |
} | |
// Get stream stats | |
$fstat = fstat($h); | |
$total = $fstat['size']; | |
$read = 0; | |
// Parse header | |
$bytes = unpack('A2magic/c1version/c1flags', fread($h, 4)); | |
$read += 4; | |
$header['magic'] = $bytes['magic']; | |
$header['version'] = $bytes['version']; | |
$header['flags'] = $bytes['flags']; | |
$header['envelop_flag'] = ($header['flags'] >> 1) & 7; | |
$header['byte_order'] = $header['flags'] & 1; | |
// Parse SRID | |
$unpack_op = $header['byte_order'] ? 'V' : 'N'; | |
$bytes = array_values(unpack($unpack_op, fread($h, 4))); | |
$read += 4; | |
$header['srs_id'] = $bytes[0]; | |
switch ($header['envelop_flag']) { | |
case 1: // 32 bytes envelop | |
$unpack_op = $header['byte_order'] ? 'f*' : 'f*'; | |
$bytes = array_values(unpack('f*', fread($h, 32))); | |
$header['envelope'] = [ | |
'minx' => $bytes[0], | |
'miny' => $bytes[1], | |
'maxx' => $bytes[2], | |
'maxy' => $bytes[3], | |
'minz' => false, | |
'maxz' => false, | |
'minm' => false, | |
'maxm' => false | |
]; | |
$read += 32; | |
break; | |
case 2: // 48 bytes envelop | |
$unpack_op = $header['byte_order'] ? 'f*' : 'f*'; | |
$bytes = array_values(unpack('f*', fread($h, 48))); | |
$header['envelope'] = [ | |
'minx' => $bytes[0], | |
'miny' => $bytes[1], | |
'maxx' => $bytes[2], | |
'maxy' => $bytes[3], | |
'minz' => $bytes[4], | |
'maxz' => $bytes[5], | |
'minm' => false, | |
'maxm' => false | |
]; | |
$read += 48; | |
break; | |
case 3: // 48 bytes envelop | |
$unpack_op = $header['byte_order'] ? 'f*' : 'f*'; | |
$bytes = array_values(unpack('f*', fread($h, 48))); | |
$header['envelope'] = [ | |
'minx' => $bytes[0], | |
'miny' => $bytes[1], | |
'maxx' => $bytes[2], | |
'maxy' => $bytes[3], | |
'minz' => false, | |
'maxz' => false, | |
'minm' => $bytes[4], | |
'maxm' => $bytes[5] | |
]; | |
$read += 48; | |
break; | |
default: ;// 0 envelop | |
} | |
// Get WKB from bytes left | |
$wkb = fread($h, $total - $read); | |
// Close handler | |
fclose($h); | |
return [$header, $wkb]; | |
} |
Appears you are reading floats for minx miny etc, but all of those are doubles (8 bytes).
For reference, here's our implementations in various languages:
https://github.com/ngageoint/geopackage-js/blob/master/lib/geom/geometryData.js
https://github.com/ngageoint/geopackage-ios/blob/master/geopackage-ios/geom/GPKGGeometryData.m
https://github.com/ngageoint/geopackage-core-java/blob/master/src/main/java/mil/nga/geopackage/geom/GeoPackageGeometryData.java
This is great, parsing the geod blobs nicely from the https://gadm.org/ data. Thank you.
The only change I needed to make was to replace $filename
with $filestream
, leaving it up to the caller to option the stream. In my case the stream is created from a string that is selected directly from the GeoPackage SQLite tables. No files are involved.
When passing the WKB binary to wkx.js using Buffer, it does not recognize the geometry and throws an error.
NOTES:
Steps to reproduce: