PHP Shapefile 3.0.0
Finally v3 is here and it can write Shapefiles!
30 August 2019
After a loooong time thinking about it, drafting, starting developing, freezing the process, adding improvements and repeating all of this again and again, PHP Shapefile 3.0.0 is finally out.
And it comes with the most wanted and anticipated feature I have been getting so many emails and requests about: yes, it can write Shapefiles too!
It took more time than expected for many reasons and in the end it turned out as a complete rewrite of the library almost from scratch.
The new library is based on a totally different approach compared to previous versions. Initially it was born as a low level utility that I used in my company to read ESRI Shapefiles and I just needed the best performances possible and no nonsense or fancy features. It then evolved into a public open source project, to which I kept adding features and it became pretty popular, especially after the introduction of PHP 7 support and the removal of outdated dBase and/or third part dependencies to read DBF files. Version 3 trades off some performance (not much, don’t worry! After all, memory and CPU usage is not the bottleneck when reading or writing files on disk) in favour of a real and clean OOP approach from the beginning, getting rid of those structured arrays and exposing some nice Geometry Objects. It goes without saying that this allows an easy and logical way to build and write your Shapefiles now, passing some WKT, some GeoJSON or directly creating your Geometry Objects. Hurray!
What’s new in Version 3.0.0
There are a lot of differences from v2 and it’s almost redundant to list breaking changes since the library has been basically rewritten.
The following list is not exaustive and it just represents the features and changes I could manage to keep track of. For sure there are many other that I forgot and did not make it into the list.
- Complete OOP style refactoring
- Folder structure now reflects namespaces hierarchy
- Namespace and class names case normalized (i.e.:
Shapefileinstead ofShapeFile) - Shapefile writing capabilities
- New
Shapefile\Geometrynamespace withPoint,MultiPoint,Linestring,MultiLinestring,PolygonandMultiPolygonClasses - New
ShapefileReaderandShapefileWriterClasses - New
Shapefile,GeometryandGeometryCollectionabstract Classes - New
ShapefileException::getDetails()method - Custom DBF charset support
- Support for emulated
nullvalues in DBF files - Reading and writing optional DBT files (support for
MEMOfields) - Reading and writing optional CPG files
- PHPDoc style comments
- Default output polygons orientation is now opposite to ESRI Shapefile specs and compliant to OGC Simple Features
- Use of
iconv()instead ofutf8_encode()for charset conversion ShapefileException::getErrorType()method now returns one ofShapefile::ERR_*constant valuesShapefileReader::fetchRecord()method replacesShapeFile::getRecord()and returns aGeometryobject- Different order of elements in bounding boxes associative arrays
- Improved invalid date format detection
- Fixed logical (
bool) not initialized values (null) detection in DBF files - Bitwise constructor flags are replaced by associative array
- New constructor options constants:
Shapefile::OPTION_CPG_ENABLE_FOR_DEFAULT_CHARSETShapefile::OPTION_DBF_CONVERT_TO_UTF8Shapefile::OPTION_DBF_FORCE_ALL_CAPSShapefile::OPTION_DBF_IGNORED_FIELDSShapefile::OPTION_DBF_NULL_PADDING_CHARShapefile::OPTION_DBF_NULLIFY_INVALID_DATESShapefile::OPTION_DBF_RETURN_DATES_AS_OBJECTSShapefile::OPTION_DELETE_EMPTY_FILESShapefile::OPTION_ENFORCE_GEOMETRY_DATA_STRUCTUREShapefile::OPTION_ENFORCE_POLYGON_CLOSED_RINGSShapefile::OPTION_FORCE_MULTIPART_GEOMETRIESShapefile::OPTION_IGNORE_GEOMETRIES_BBOXESShapefile::OPTION_IGNORE_SHAPEFILE_BBOXShapefile::OPTION_INVERT_POLYGONS_ORIENTATIONShapefile::OPTION_OVERWRITE_EXISTING_FILESShapefile::OPTION_SUPPRESS_MShapefile::OPTION_SUPPRESS_Z
- New file types constants:
Shapefile::FILE_SHPShapefile::FILE_SHXShapefile::FILE_DBFShapefile::FILE_DBTShapefile::FILE_PRJShapefile::FILE_CPG
- New shape types constants:
Shapefile::SHAPE_TYPE_NULLShapefile::SHAPE_TYPE_POINTShapefile::SHAPE_TYPE_POLYLINEShapefile::SHAPE_TYPE_POLYGONShapefile::SHAPE_TYPE_MULTIPOINTShapefile::SHAPE_TYPE_POINTZShapefile::SHAPE_TYPE_POLYLINEZShapefile::SHAPE_TYPE_POLYGONZShapefile::SHAPE_TYPE_MULTIPOINTZShapefile::SHAPE_TYPE_POINTMShapefile::SHAPE_TYPE_POLYLINEMShapefile::SHAPE_TYPE_POLYGONMShapefile::SHAPE_TYPE_MULTIPOINTM
- New DBF fields types constants:
Shapefile::DBF_TYPE_CHARShapefile::DBF_TYPE_DATEShapefile::DBF_TYPE_LOGICALShapefile::DBF_TYPE_MEMOShapefile::DBF_TYPE_NUMERICShapefile::DBF_TYPE_FLOAT
- New error types constants:
Shapefile::ERR_UNDEFINEDShapefile::ERR_FILE_MISSINGShapefile::ERR_FILE_EXISTSShapefile::ERR_FILE_INVALID_RESOURCEShapefile::ERR_FILE_OPENShapefile::ERR_FILE_READINGShapefile::ERR_FILE_WRITINGShapefile::ERR_SHP_TYPE_NOT_SUPPORTEDShapefile::ERR_SHP_TYPE_NOT_SETShapefile::ERR_SHP_TYPE_ALREADY_SETShapefile::ERR_SHP_GEOMETRY_TYPE_NOT_COMPATIBLEShapefile::ERR_SHP_MISMATCHED_BBOXShapefile::ERR_SHP_FILE_ALREADY_INITIALIZEDShapefile::ERR_SHP_WRONG_RECORD_TYPEShapefile::ERR_DBF_FILE_NOT_VALIDShapefile::ERR_DBF_MISMATCHED_FILEShapefile::ERR_DBF_EOF_REACHEDShapefile::ERR_DBF_MAX_FIELD_COUNT_REACHEDShapefile::ERR_DBF_FIELD_NAME_NOT_UNIQUEShapefile::ERR_DBF_FIELD_NAME_NOT_VALIDShapefile::ERR_DBF_FIELD_TYPE_NOT_VALIDShapefile::ERR_DBF_FIELD_SIZE_NOT_VALIDShapefile::ERR_DBF_FIELD_DECIMALS_NOT_VALIDShapefile::ERR_DBF_CHARSET_CONVERSIONShapefile::ERR_DBT_EOF_REACHEDShapefile::ERR_GEOM_NOT_EMPTYShapefile::ERR_GEOM_COORD_VALUE_NOT_VALIDShapefile::ERR_GEOM_MISMATCHED_DIMENSIONSShapefile::ERR_GEOM_MISMATCHED_BBOXShapefile::ERR_GEOM_MISSING_FIELDShapefile::ERR_GEOM_POINT_NOT_VALIDShapefile::ERR_GEOM_POLYGON_OPEN_RINGShapefile::ERR_GEOM_POLYGON_AREA_TOO_SMALLShapefile::ERR_GEOM_POLYGON_NOT_VALIDShapefile::ERR_INPUT_RECORD_NOT_FOUNDShapefile::ERR_INPUT_FIELD_NOT_FOUNDShapefile::ERR_INPUT_GEOMETRY_TYPE_NOT_VALIDShapefile::ERR_INPUT_GEOMETRY_INDEX_NOT_VALIDShapefile::ERR_INPUT_ARRAY_NOT_VALIDShapefile::ERR_INPUT_WKT_NOT_VALIDShapefile::ERR_INPUT_GEOJSON_NOT_VALIDShapefile::ERR_INPUT_NUMERIC_VALUE_OVERFLOW
- Removed
init(),setDefaultGeometryFormat(),getDBFFields(),getRecord(),readRecord()inShapefileReaderClass (formerShapeFile) - Removed constructor flags constants:
Shapefile::FLAG_SUPPRESS_MShapefile::FLAG_SUPPRESS_ZShapefile::GEOMETRY_ARRAYShapefile::GEOMETRY_BOTHShapefile::GEOMETRY_GEOJSON_GEOMETRYShapefile::GEOMETRY_GEOJSON_FEATUREShapefile::GEOMETRY_WKT
- Removed numeric error codes
Migrating from v2 to v3
Starting with some good news:
- The library is still compatible with PHP 5.4+ (as odd as it might seem, in 2019 there are many production systems that still rely on a fully security-patched version of PHP 5.4 and it did not feel right to drop the support for them).
- It now exposes some nice and convenient Geometry Objects that behave in a predictable way and are a huge step forward compared to those clumsy structured arrays we had before.
The bad news are that since the whole approach has changed, you will need to update the code that relies on this library. Not big deal actually, breaking changes can be summarized as follows:
- New folder structure
- Constructor bitwise flags replaced by constructor options array
- Namespaces (and Classes) names case normalization
- Different Shapefile reader Class name
- Different name and output format of main Shapefile reader method
- ShapefileException error codes replaced by error types
Here is an example of basic usage in v2 and in v3 to better show the differences:
Reading Shapefiles - Version 2
// Register autoloader
require_once('php-shapefile/src/ShapeFileAutoloader.php');
ShapeFile\ShapeFileAutoloader::register();
// Import classes
use ShapeFile\ShapeFile;
use ShapeFile\ShapeFileException;
try {
// Open shapefile
$ShapeFile = new ShapeFile('/path/to/file.shp', ShapeFile::FLAG_SUPPRESS_Z | ShapeFile::FLAG_SUPPRESS_M);
// Read all the records as WKT
while ($record = $ShapeFile->getRecord(ShapeFile::GEOMETRY_WKT)) {
// Skip the record if marked as "deleted"
if ($record['dbf']['_deleted']) {
continue;
}
// Print Geometry as WKT
print_r($record['shp']);
// Print DBF data
print_r($record['dbf']);
}
} catch (ShapeFileException $e) {
// Print detailed error information
exit(
"Error code" . $e->getCode()
. "\nError Type: " . $e->getErrorType()
. "\nMessage: " . $e->getMessage()
);
}
Reading Shapefiles - Version 3
// Register autoloader
require_once('php-shapefile/src/Shapefile/ShapefileAutoloader.php');
Shapefile\ShapefileAutoloader::register();
// Import classes
use Shapefile\Shapefile;
use Shapefile\ShapefileException;
use Shapefile\ShapefileReader;
try {
// Open Shapefile
$Shapefile = new ShapefileReader('/path/to/file.shp', [
Shapefile::OPTION_SUPPRESS_M => true,
Shapefile::OPTION_SUPPRESS_Z => true,
]);
// Read all the records as WKT
while ($Geometry = $Shapefile->fetchRecord()) {
// Skip the record if marked as "deleted"
if ($Geometry->isDeleted()) {
continue;
}
// Print Geometry as WKT
print_r($Geometry->getWKT());
// Print DBF data
print_r($Geometry->getDataArray());
}
} catch (ShapefileException $e) {
// Print detailed error information
exit(
"Error Type: " . $e->getErrorType()
. "\nMessage: " . $e->getMessage()
. "\nDetails: " . $e->getDetails()
);
}
Download and documentation
Go to the Lab page: PHP Shapefile