Created
May 8, 2016 17:58
-
-
Save taviroquai/858b9519becf2703687045a04fb87421 to your computer and use it in GitHub Desktop.
PHP parse GeoPackage
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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]; | |
} |
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.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Appears you are reading floats for minx miny etc, but all of those are doubles (8 bytes).