vendor/gasparesganga/php-shapefile/src/Shapefile/Shapefile.php line 760

Open in your IDE?
  1. <?php
  2. /**
  3.  * PHP Shapefile - PHP library to read and write ESRI Shapefiles, compatible with WKT and GeoJSON
  4.  *
  5.  * @package Shapefile
  6.  * @author  Gaspare Sganga
  7.  * @version 3.4.0
  8.  * @license MIT
  9.  * @link    https://gasparesganga.com/labs/php-shapefile/
  10.  */
  11. namespace Shapefile;
  12. /**
  13.  * Abstract base class for ShapefileReader and ShapefileWriter.
  14.  * It provides some common public methods to both of them and exposes package-wide constants.
  15.  *
  16.  * Efforts have been made all throughout the library to keep it compatible with an audience
  17.  * as broader as possible and some "stylistic tradeoffs" here and there were necessary to support PHP 5.4.
  18.  */
  19. abstract class Shapefile
  20. {
  21.     /////////////////////////////// PUBLIC CONSTANTS ///////////////////////////////
  22.     /** Actions */
  23.     const ACTION_IGNORE     0;
  24.     const ACTION_CHECK      1;
  25.     const ACTION_FORCE      2;
  26.     
  27.     /** DBF fields types */
  28.     const DBF_TYPE_CHAR     'C';
  29.     const DBF_TYPE_DATE     'D';
  30.     const DBF_TYPE_LOGICAL  'L';
  31.     const DBF_TYPE_MEMO     'M';
  32.     const DBF_TYPE_NUMERIC  'N';
  33.     const DBF_TYPE_FLOAT    'F';
  34.     
  35.     /** File types */
  36.     const FILE_SHP  'shp';
  37.     const FILE_SHX  'shx';
  38.     const FILE_DBF  'dbf';
  39.     const FILE_DBT  'dbt';
  40.     const FILE_PRJ  'prj';
  41.     const FILE_CPG  'cpg';
  42.     
  43.     /** Return formats  */
  44.     const FORMAT_INT 0;
  45.     const FORMAT_STR 1;
  46.     
  47.     /** File modes */
  48.     const MODE_PRESERVE     0;
  49.     const MODE_OVERWRITE    1;
  50.     const MODE_APPEND       2;
  51.     
  52.     /** Polygon orientations */
  53.     const ORIENTATION_CLOCKWISE         0;
  54.     const ORIENTATION_COUNTERCLOCKWISE  1;
  55.     const ORIENTATION_UNCHANGED         2;
  56.     
  57.     /** Shape types */
  58.     const SHAPE_TYPE_NULL           0;
  59.     const SHAPE_TYPE_POINT          1;
  60.     const SHAPE_TYPE_POLYLINE       3;
  61.     const SHAPE_TYPE_POLYGON        5;
  62.     const SHAPE_TYPE_MULTIPOINT     8;
  63.     const SHAPE_TYPE_POINTZ         11;
  64.     const SHAPE_TYPE_POLYLINEZ      13;
  65.     const SHAPE_TYPE_POLYGONZ       15;
  66.     const SHAPE_TYPE_MULTIPOINTZ    18;
  67.     const SHAPE_TYPE_POINTM         21;
  68.     const SHAPE_TYPE_POLYLINEM      23;
  69.     const SHAPE_TYPE_POLYGONM       25;
  70.     const SHAPE_TYPE_MULTIPOINTM    28;
  71.     
  72.     /** Misc */
  73.     const EOF       0;
  74.     const UNDEFINED null;
  75.     const UNKNOWN   = -1;
  76.     
  77.     
  78.     
  79.     /////////////////////////////// OPTIONS ///////////////////////////////
  80.     /**
  81.      * Number of records to keep into buffer before writing them.
  82.      * Use a value equal or less than 0 to keep all records into buffer and write them at once.
  83.      * ShapefileWriter
  84.      * @var int
  85.      */
  86.     const OPTION_BUFFERED_RECORDS 'OPTION_BUFFERED_RECORDS';
  87.     const OPTION_BUFFERED_RECORDS_DEFAULT 10;
  88.     
  89.     /**
  90.      * Converts from input charset to UTF-8 all strings read from DBF files.
  91.      * ShapefileWriter
  92.      * @var bool
  93.      */
  94.     const OPTION_CPG_ENABLE_FOR_DEFAULT_CHARSET 'OPTION_CPG_ENABLE_FOR_DEFAULT_CHARSET';
  95.     const OPTION_CPG_ENABLE_FOR_DEFAULT_CHARSET_DEFAULT false;
  96.     
  97.     /**
  98.      * Allows a maximum field size of 255 bytes instead of 254 bytes in DBF files.
  99.      * ShapefileReader and ShapefileWriter
  100.      * @var bool
  101.      */
  102.     const OPTION_DBF_ALLOW_FIELD_SIZE_255 'OPTION_DBF_ALLOW_FIELD_SIZE_255';
  103.     const OPTION_DBF_ALLOW_FIELD_SIZE_255_DEFAULT false;
  104.     
  105.     /**
  106.      * Converts from input charset to UTF-8 all strings read from DBF files.
  107.      * ShapefileReader
  108.      * @var bool
  109.      */
  110.     const OPTION_DBF_CONVERT_TO_UTF8 'OPTION_DBF_CONVERT_TO_UTF8';
  111.     const OPTION_DBF_CONVERT_TO_UTF8_DEFAULT true;
  112.     
  113.     /**
  114.      * Forces all capitals field names in DBF files.
  115.      * ShapefileReader and ShapefileWriter
  116.      * @var bool
  117.      */
  118.     const OPTION_DBF_FORCE_ALL_CAPS 'OPTION_DBF_FORCE_ALL_CAPS';
  119.     const OPTION_DBF_FORCE_ALL_CAPS_DEFAULT true;
  120.     
  121.     /**
  122.      * Ignored fields in DBF file.
  123.      * An array of fields to ignore when reading the DBF file.
  124.      * ShapefileReader
  125.      * @var array|null
  126.      */
  127.     const OPTION_DBF_IGNORED_FIELDS 'OPTION_DBF_IGNORED_FIELDS';
  128.     const OPTION_DBF_IGNORED_FIELDS_DEFAULT null;
  129.     
  130.     /**
  131.      * Defines a null padding character to represent null values in DBF files.
  132.      * ShapefileReader and ShapefileWriter
  133.      * @var string|null
  134.      */
  135.     const OPTION_DBF_NULL_PADDING_CHAR 'OPTION_DBF_NULL_PADDING_CHAR';
  136.     const OPTION_DBF_NULL_PADDING_CHAR_DEFAULT null;
  137.     
  138.     /**
  139.      * Returns a null value for invalid dates when reading DBF files and nullify invalid dates when writing them.
  140.      * ShapefileReader and ShapefileWriter
  141.      * @var bool
  142.      */
  143.     const OPTION_DBF_NULLIFY_INVALID_DATES 'OPTION_DBF_NULLIFY_INVALID_DATES';
  144.     const OPTION_DBF_NULLIFY_INVALID_DATES_DEFAULT true;
  145.     
  146.     /**
  147.      * Returns dates as DateTime objects instead of ISO strings (YYYY-MM-DD).
  148.      * ShapefileReader
  149.      * @var bool
  150.      */
  151.     const OPTION_DBF_RETURN_DATES_AS_OBJECTS 'OPTION_DBF_RETURN_DATES_AS_OBJECTS';
  152.     const OPTION_DBF_RETURN_DATES_AS_OBJECTS_DEFAULT false;
  153.     
  154.     /**
  155.      * Deletes empty files after closing them (only if they were passed as resource handles).
  156.      * ShapefileWriter
  157.      * @var bool
  158.      */
  159.     const OPTION_DELETE_EMPTY_FILES 'OPTION_DELETE_EMPTY_FILES';
  160.     const OPTION_DELETE_EMPTY_FILES_DEFAULT true;
  161.     
  162.     /**
  163.      * Enforces Geometries to have all data fields defined in Shapefile.
  164.      * ShapefileWriter
  165.      * @var bool
  166.      */
  167.     const OPTION_ENFORCE_GEOMETRY_DATA_STRUCTURE 'OPTION_ENFORCE_GEOMETRY_DATA_STRUCTURE';
  168.     const OPTION_ENFORCE_GEOMETRY_DATA_STRUCTURE_DEFAULT true;
  169.     
  170.     /**
  171.      * Defines behaviour with existing files with the same name.
  172.      * Possible values:
  173.      *    MODE_PRESERVE  : Throws Shapefile::ERR_FILE_EXISTS
  174.      *    MODE_OVERWRITE : Overwrites existing files
  175.      *    MODE_APPEND    : Appends new records to existing files
  176.      * ShapefileWriter
  177.      * @var int
  178.      */
  179.     const OPTION_EXISTING_FILES_MODE 'OPTION_EXISTING_FILES_MODE';
  180.     const OPTION_EXISTING_FILES_MODE_DEFAULT self::MODE_PRESERVE;
  181.     
  182.     /**
  183.      * Reads all Polyline and Polygon Geometries as Multi.
  184.      * ShapefileReader
  185.      * @var bool
  186.      */
  187.     const OPTION_FORCE_MULTIPART_GEOMETRIES 'OPTION_FORCE_MULTIPART_GEOMETRIES';
  188.     const OPTION_FORCE_MULTIPART_GEOMETRIES_DEFAULT false;
  189.     
  190.     /**
  191.      * Ignores DBF file (useful to recover corrupted Shapefiles).
  192.      * Data will not be available for geometries.
  193.      * ShapefileReader
  194.      * @var bool
  195.      */
  196.     const OPTION_IGNORE_FILE_DBF 'OPTION_IGNORE_FILE_DBF';
  197.     const OPTION_IGNORE_FILE_DBF_DEFAULT false;
  198.     
  199.     /**
  200.      * Ignores SHX file (useful to recover corrupted Shapefiles).
  201.      * This might not always work as it relies on SHP record headers content lengths
  202.      * and assumes there are no unused bytes between records in SHP file.
  203.      * Random access to specific records will not be possible.
  204.      * ShapefileReader
  205.      * @var bool
  206.      */
  207.     const OPTION_IGNORE_FILE_SHX 'OPTION_IGNORE_FILE_SHX';
  208.     const OPTION_IGNORE_FILE_SHX_DEFAULT false;
  209.     
  210.     /**
  211.      * Ignores Geometries bounding box found in Shapefile.
  212.      * ShapefileReader
  213.      * @var bool
  214.      */
  215.     const OPTION_IGNORE_GEOMETRIES_BBOXES 'OPTION_IGNORE_GEOMETRIES_BBOXES';
  216.     const OPTION_IGNORE_GEOMETRIES_BBOXES_DEFAULT false;
  217.     
  218.     /**
  219.      * Ignores bounding box found in Shapefile.
  220.      * ShapefileReader
  221.      * @var bool
  222.      */
  223.     const OPTION_IGNORE_SHAPEFILE_BBOX 'OPTION_IGNORE_SHAPEFILE_BBOX';
  224.     const OPTION_IGNORE_SHAPEFILE_BBOX_DEFAULT false;
  225.     
  226.     /**
  227.      * Defines action to perform on Polygons rings.
  228.      * They should be closed but some software do not enforce that, creating uncompliant Shapefiles.
  229.      * Possible values:
  230.      *    Shapefile::ACTION_IGNORE : No action taken
  231.      *    Shapefile::ACTION_CHECK  : Checks for open rings and eventually throws Shapefile::ERR_GEOM_POLYGON_OPEN_RING
  232.      *    Shapefile::ACTION_FORCE  : Forces all rings to be closed in Polygons
  233.      * ShapefileReader
  234.      * @var int
  235.      */
  236.     const OPTION_POLYGON_CLOSED_RINGS_ACTION 'OPTION_POLYGON_CLOSED_RINGS_ACTION';
  237.     const OPTION_POLYGON_CLOSED_RINGS_ACTION_DEFAULT self::ACTION_CHECK;
  238.     
  239.     /**
  240.      * Allows Polygons orientation to be either clockwise or counterclockwise when reading Shapefiles.
  241.      * Set to false to enforce strict ESRI Shapefile specs (clockwise outer rings and counterclockwise inner ones)
  242.      * and raise a Shapefile::ERR_GEOM_POLYGON_WRONG_ORIENTATION error for uncompliant Shapefiles.
  243.      * ShapefileReader
  244.      * @var bool
  245.      */
  246.     const OPTION_POLYGON_ORIENTATION_READING_AUTOSENSE 'OPTION_POLYGON_ORIENTATION_READING_AUTOSENSE';
  247.     const OPTION_POLYGON_ORIENTATION_READING_AUTOSENSE_DEFAULT true;
  248.     
  249.     /**
  250.      * Forces a specific orientation for Polygons after reading them.
  251.      * ESRI Shapefile specs establish clockwise orientation for outer rings and counterclockwise for inner ones,
  252.      * GeoJSON require the opposite (counterclockwise outer rings and clockwise inner ones)
  253.      * and Simple Features used to be the same as GeoJSON but is currently allowing both.
  254.      * Possible values:
  255.      *    Shapefile::ORIENTATION_CLOCKWISE        : Forces clockwise outer ring and counterclockwise inner rings
  256.      *    Shapefile::ORIENTATION_COUNTERCLOCKWISE : Forces counterclockwise outer ring and clockwise inner rings
  257.      *    Shapefile::ORIENTATION_UNCHANGED        : Preserves original Shapefile orientation depending on file
  258.      * ShapefileReader
  259.      * @var int
  260.      */
  261.     const OPTION_POLYGON_OUTPUT_ORIENTATION 'OPTION_POLYGON_OUTPUT_ORIENTATION';
  262.     const OPTION_POLYGON_OUTPUT_ORIENTATION_DEFAULT self::ORIENTATION_COUNTERCLOCKWISE;
  263.     
  264.     /**
  265.      * Suppresses M dimension.
  266.      * ShapefileReader and ShapefileWriter
  267.      * @var bool
  268.      */
  269.     const OPTION_SUPPRESS_M 'OPTION_SUPPRESS_M';
  270.     const OPTION_SUPPRESS_M_DEFAULT false;
  271.     
  272.     /**
  273.      * Suppresses Z dimension.
  274.      * ShapefileReader and ShapefileWriter
  275.      * @var bool
  276.      */
  277.     const OPTION_SUPPRESS_Z 'OPTION_SUPPRESS_Z';
  278.     const OPTION_SUPPRESS_Z_DEFAULT false;
  279.     
  280.     
  281.     
  282.     /////////////////////////////// ERRORS ///////////////////////////////
  283.     const ERR_UNDEFINED 'ERR_UNDEFINED';
  284.     const ERR_UNDEFINED_MESSAGE "Undefined error.";
  285.     
  286.     const ERR_FILE_MISSING 'ERR_FILE_MISSING';
  287.     const ERR_FILE_MISSING_MESSAGE "A required file is missing";
  288.     
  289.     const ERR_FILE_EXISTS 'ERR_FILE_EXISTS';
  290.     const ERR_FILE_EXISTS_MESSAGE "Check if the file exists and is readable and/or writable";
  291.     
  292.     const ERR_FILE_INVALID_RESOURCE 'ERR_FILE_INVALID_RESOURCE';
  293.     const ERR_FILE_INVALID_RESOURCE_MESSAGE "File pointer resource not valid";
  294.     
  295.     const ERR_FILE_OPEN 'ERR_FILE_OPEN';
  296.     const ERR_FILE_OPEN_MESSAGE "Unable to open file";
  297.     
  298.     const ERR_FILE_READING 'ERR_FILE_READING';
  299.     const ERR_FILE_READING_MESSAGE "Error during binary file reading";
  300.     
  301.     const ERR_FILE_WRITING 'ERR_FILE_WRITING';
  302.     const ERR_FILE_WRITING_MESSAGE "Error during binary file writing";
  303.     
  304.     const ERR_SHP_TYPE_NOT_SUPPORTED 'ERR_SHP_TYPE_NOT_SUPPORTED';
  305.     const ERR_SHP_TYPE_NOT_SUPPORTED_MESSAGE "Shape type not supported";
  306.     
  307.     const ERR_SHP_TYPE_NOT_SET 'ERR_SHP_TYPE_NOT_SET';
  308.     const ERR_SHP_TYPE_NOT_SET_MESSAGE "Shape type not set";
  309.     
  310.     const ERR_SHP_TYPE_ALREADY_SET 'ERR_SHP_TYPE_ALREADY_SET';
  311.     const ERR_SHP_TYPE_ALREADY_SET_MESSAGE "Shape type has already been set";
  312.     
  313.     const ERR_SHP_GEOMETRY_TYPE_NOT_COMPATIBLE 'ERR_SHP_GEOMETRY_TYPE_NOT_COMPATIBLE';
  314.     const ERR_SHP_GEOMETRY_TYPE_NOT_COMPATIBLE_MESSAGE "Geometry type must be compatible with Shapefile shape type";
  315.     
  316.     const ERR_SHP_MISMATCHED_BBOX 'ERR_SHP_MISMATCHED_BBOX';
  317.     const ERR_SHP_MISMATCHED_BBOX_MESSAGE "Bounding box must have the same dimensions as the Shapefile (2D, 3D or 4D)";
  318.     
  319.     const ERR_SHP_FILE_ALREADY_INITIALIZED 'ERR_SHP_FILE_ALREADY_INITIALIZED';
  320.     const ERR_SHP_FILE_ALREADY_INITIALIZED_MESSAGE "Cannot change Shapefile definition after it has been initialized with data";
  321.     
  322.     const ERR_SHP_WRONG_RECORD_TYPE 'ERR_SHP_WRONG_RECORD_TYPE';
  323.     const ERR_SHP_WRONG_RECORD_TYPE_MESSAGE "Wrong record shape type";
  324.     
  325.     const ERR_DBF_FILE_NOT_VALID 'ERR_DBF_FILE_NOT_VALID';
  326.     const ERR_DBF_FILE_NOT_VALID_MESSAGE "DBF file doesn't seem to be a valid dBase III or dBase IV format";
  327.     
  328.     const ERR_DBF_MISMATCHED_FILE 'ERR_DBF_MISMATCHED_FILE';
  329.     const ERR_DBF_MISMATCHED_FILE_MESSAGE "Mismatched DBF file. Number of records not corresponding to the SHP file";
  330.     
  331.     const ERR_DBF_EOF_REACHED 'ERR_DBF_EOF_REACHED';
  332.     const ERR_DBF_EOF_REACHED_MESSAGE "End of DBF file reached. Number of records not corresponding to the SHP file";
  333.     
  334.     const ERR_DBF_MAX_FIELD_COUNT_REACHED 'ERR_DBF_MAX_FIELD_COUNT_REACHED';
  335.     const ERR_DBF_MAX_FIELD_COUNT_REACHED_MESSAGE "Cannot add other fields, maximum number of fields in a DBF file reached";
  336.     
  337.     const ERR_DBF_FIELD_NAME_NOT_VALID 'ERR_DBF_FIELD_NAME_NOT_VALID';
  338.     const ERR_DBF_FIELD_NAME_NOT_VALID_MESSAGE "Too many field names conflicting";
  339.     
  340.     const ERR_DBF_FIELD_TYPE_NOT_VALID 'ERR_DBF_FIELD_TYPE_NOT_VALID';
  341.     const ERR_DBF_FIELD_TYPE_NOT_VALID_MESSAGE "Field type must be CHAR, DATE, LOGICAL, MEMO or NUMERIC";
  342.     
  343.     const ERR_DBF_FIELD_SIZE_NOT_VALID 'ERR_DBF_FIELD_SIZE_NOT_VALID';
  344.     const ERR_DBF_FIELD_SIZE_NOT_VALID_MESSAGE "Field size incorrect according to its type";
  345.     
  346.     const ERR_DBF_FIELD_DECIMALS_NOT_VALID 'ERR_DBF_FIELD_DECIMALS_NOT_VALID';
  347.     const ERR_DBF_FIELD_DECIMALS_NOT_VALID_MESSAGE "Field decimals incorrect according to its type";
  348.     
  349.     const ERR_DBF_CHARSET_CONVERSION 'ERR_DBF_CHARSET_CONVERSION';
  350.     const ERR_DBF_CHARSET_CONVERSION_MESSAGE "Error during conversion from provided DBF input charset to UTF-8";
  351.     
  352.     const ERR_DBT_EOF_REACHED 'ERR_DBT_EOF_REACHED';
  353.     const ERR_DBT_EOF_REACHED_MESSAGE "End of DBT file reached. File might be corrupted";
  354.     
  355.     const ERR_GEOM_NOT_EMPTY 'ERR_GEOM_NOT_EMPTY';
  356.     const ERR_GEOM_NOT_EMPTY_MESSAGE "Cannot reinitialize non-empty Geometry";
  357.     
  358.     const ERR_GEOM_COORD_VALUE_NOT_VALID 'ERR_GEOM_COORD_VALUE_NOT_VALID';
  359.     const ERR_GEOM_COORD_VALUE_NOT_VALID_MESSAGE "Invalid coordinate value";
  360.     
  361.     const ERR_GEOM_MISMATCHED_DIMENSIONS 'ERR_GEOM_MISMATCHED_DIMENSIONS';
  362.     const ERR_GEOM_MISMATCHED_DIMENSIONS_MESSAGE "All geometries in a collection must have the same dimensions (2D, 3D or 4D)";
  363.     
  364.     const ERR_GEOM_MISMATCHED_BBOX 'ERR_GEOM_MISMATCHED_BBOX';
  365.     const ERR_GEOM_MISMATCHED_BBOX_MESSAGE "Bounding box must have the same dimensions as the Geometry (2D, 3D or 4D)";
  366.     
  367.     const ERR_GEOM_MISSING_FIELD 'ERR_GEOM_MISSING_FIELD';
  368.     const ERR_GEOM_MISSING_FIELD_MESSAGE "Geometry is missing a field defined in the Shapefile";
  369.     
  370.     const ERR_GEOM_POINT_NOT_VALID 'ERR_GEOM_POINT_NOT_VALID';
  371.     const ERR_GEOM_POINT_NOT_VALID_MESSAGE "A Point can be either EMPTY or al least 2D";
  372.     
  373.     const ERR_GEOM_POLYGON_OPEN_RING 'ERR_GEOM_POLYGON_OPEN_RING';
  374.     const ERR_GEOM_POLYGON_OPEN_RING_MESSAGE "Polygons cannot contain open rings";
  375.     
  376.     const ERR_GEOM_POLYGON_WRONG_ORIENTATION 'ERR_GEOM_POLYGON_WRONG_ORIENTATION';
  377.     const ERR_GEOM_POLYGON_WRONG_ORIENTATION_MESSAGE "Polygon orientation not compliant with Shapefile specifications";
  378.     
  379.     const ERR_GEOM_RING_AREA_TOO_SMALL 'ERR_GEOM_RING_AREA_TOO_SMALL';
  380.     const ERR_GEOM_RING_AREA_TOO_SMALL_MESSAGE "Ring area too small. Cannot determine ring orientation";
  381.     
  382.     const ERR_GEOM_RING_NOT_ENOUGH_VERTICES 'ERR_GEOM_RING_NOT_ENOUGH_VERTICES';
  383.     const ERR_GEOM_RING_NOT_ENOUGH_VERTICES_MESSAGE "Not enough vertices. Cannot determine ring orientation";
  384.     
  385.     const ERR_INPUT_RANDOM_ACCESS_UNAVAILABLE 'ERR_INPUT_RANDOM_ACCESS_UNAVAILABLE';
  386.     const ERR_INPUT_RANDOM_ACCESS_UNAVAILABLE_MESSAGE "Cannot change current record without a valid SHX file";
  387.     
  388.     const ERR_INPUT_RECORD_NOT_FOUND 'ERR_INPUT_RECORD_NOT_FOUND';
  389.     const ERR_INPUT_RECORD_NOT_FOUND_MESSAGE "Record index not found (check the total number of records in the SHP file)";
  390.     
  391.     const ERR_INPUT_FIELD_NOT_FOUND 'ERR_INPUT_FIELD_NOT_FOUND';
  392.     const ERR_INPUT_FIELD_NOT_FOUND_MESSAGE "Field not found";
  393.     
  394.     const ERR_INPUT_GEOMETRY_TYPE_NOT_VALID 'ERR_INPUT_GEOMETRY_TYPE_NOT_VALID';
  395.     const ERR_INPUT_GEOMETRY_TYPE_NOT_VALID_MESSAGE "Geometry type not valid. Must be of specified type";
  396.     
  397.     const ERR_INPUT_GEOMETRY_INDEX_NOT_VALID 'ERR_INPUT_GEOMETRY_INDEX_NOT_VALID';
  398.     const ERR_INPUT_GEOMETRY_INDEX_NOT_VALID_MESSAGE "Geometry index not valid (check the total number of geometries in the collection)";
  399.     
  400.     const ERR_INPUT_ARRAY_NOT_VALID 'ERR_INPUT_ARRAY_NOT_VALID';
  401.     const ERR_INPUT_ARRAY_NOT_VALID_MESSAGE "Array not valid";
  402.     
  403.     const ERR_INPUT_WKT_NOT_VALID 'ERR_INPUT_WKT_NOT_VALID';
  404.     const ERR_INPUT_WKT_NOT_VALID_MESSAGE "WKT not valid";
  405.     
  406.     const ERR_INPUT_GEOJSON_NOT_VALID 'ERR_INPUT_GEOJSON_NOT_VALID';
  407.     const ERR_INPUT_GEOJSON_NOT_VALID_MESSAGE "GeoJSON not valid";
  408.     
  409.     const ERR_INPUT_NUMERIC_VALUE_OVERFLOW 'ERR_INPUT_NUMERIC_VALUE_OVERFLOW';
  410.     const ERR_INPUT_NUMERIC_VALUE_OVERFLOW_MESSAGE "Integer value overflows field size definition";
  411.     
  412.     
  413.         
  414.     /////////////////////////////// DEPRECATED CONSTANTS ///////////////////////////////
  415.     /**
  416.      * @deprecated  This option was deprecated with v3.3.0 and will disappear in the next releases.
  417.      *              Use OPTION_POLYGON_CLOSED_RINGS_ACTION instead.
  418.      */
  419.     const OPTION_ENFORCE_POLYGON_CLOSED_RINGS 'OPTION_ENFORCE_POLYGON_CLOSED_RINGS';
  420.     
  421.     /**
  422.      * @deprecated  This option was deprecated with v3.3.0 and will disappear in the next releases.
  423.      *              Use OPTION_POLYGON_OUTPUT_ORIENTATION instead.
  424.      */
  425.     const OPTION_INVERT_POLYGONS_ORIENTATION 'OPTION_INVERT_POLYGONS_ORIENTATION';
  426.     
  427.     /**
  428.      * @deprecated  This constant was deprecated with v3.3.0 and will disappear in the next releases.
  429.      *              Use ERR_GEOM_RING_AREA_TOO_SMALL instead.
  430.      */
  431.     const ERR_GEOM_POLYGON_AREA_TOO_SMALL 'ERR_GEOM_RING_AREA_TOO_SMALL';
  432.     
  433.     /**
  434.      * @deprecated  This constant was deprecated with v3.3.0 and will disappear in the next releases.
  435.      *              Use ERR_GEOM_POLYGON_WRONG_ORIENTATION instead.
  436.      */
  437.     const ERR_GEOM_POLYGON_NOT_VALID 'ERR_GEOM_POLYGON_WRONG_ORIENTATION';
  438.     
  439.     
  440.     
  441.     /////////////////////////////// INTERNAL CONSTANTS ///////////////////////////////
  442.     /** SHP files constants */
  443.     const SHP_FILE_CODE         9994;
  444.     const SHP_HEADER_SIZE       100;
  445.     const SHP_NO_DATA_THRESHOLD = -1e38;
  446.     const SHP_NO_DATA_VALUE     = -1e40;
  447.     const SHP_REC_HEADER_SIZE   8;
  448.     const SHP_VERSION           1000;
  449.     /** SHX files constants */
  450.     const SHX_HEADER_SIZE       100;
  451.     const SHX_RECORD_SIZE       8;
  452.     /** DBF files constants */
  453.     const DBF_BLANK             0x20;
  454.     const DBF_DEFAULT_CHARSET   'ISO-8859-1';
  455.     const DBF_DELETED_MARKER    0x2a;
  456.     const DBF_EOF_MARKER        0x1a;
  457.     const DBF_FIELD_TERMINATOR  0x0d;
  458.     const DBF_MAX_FIELD_COUNT   255;
  459.     const DBF_VALUE_MASK_FALSE  'FfNn0';
  460.     const DBF_VALUE_MASK_TRUE   'TtYy1';
  461.     const DBF_VALUE_FALSE       'F';
  462.     const DBF_VALUE_NULL        '?';
  463.     const DBF_VALUE_TRUE        'T';
  464.     const DBF_VERSION           0x03;
  465.     const DBF_VERSION_WITH_DBT  0x83;
  466.     /** DBT files constants */
  467.     const DBT_BLOCK_SIZE        512;
  468.     const DBT_FIELD_TERMINATOR  0x1a;
  469.     
  470.     /** Shape types text description */
  471.     public static $shape_types = [
  472.         self::SHAPE_TYPE_NULL           => 'Null Shape',
  473.         self::SHAPE_TYPE_POINT          => 'Point',
  474.         self::SHAPE_TYPE_POLYLINE       => 'PolyLine',
  475.         self::SHAPE_TYPE_POLYGON        => 'Polygon',
  476.         self::SHAPE_TYPE_MULTIPOINT     => 'MultiPoint',
  477.         self::SHAPE_TYPE_POINTZ         => 'PointZ',
  478.         self::SHAPE_TYPE_POLYLINEZ      => 'PolyLineZ',
  479.         self::SHAPE_TYPE_POLYGONZ       => 'PolygonZ',
  480.         self::SHAPE_TYPE_MULTIPOINTZ    => 'MultiPointZ',
  481.         self::SHAPE_TYPE_POINTM         => 'PointM',
  482.         self::SHAPE_TYPE_POLYLINEM      => 'PolyLineM',
  483.         self::SHAPE_TYPE_POLYGONM       => 'PolygonM',
  484.         self::SHAPE_TYPE_MULTIPOINTM    => 'MultiPointM',
  485.     ];
  486.     
  487.     
  488.     
  489.     /////////////////////////////// PRIVATE VARIABLES ///////////////////////////////
  490.     /**
  491.      * @var int|null    Shapefile type.
  492.      */
  493.     private $shape_type null;
  494.     
  495.     /**
  496.      * @var array|null      Custom bounding box set with setCustomBoundingBox() method.
  497.      */
  498.     private $custom_bounding_box null;
  499.     
  500.     /**
  501.      * @var array|null      Computed bounding box.
  502.      */
  503.     private $computed_bounding_box null;
  504.     
  505.     /**
  506.      * @var string|null     PRJ well-known-text.
  507.      */
  508.     private $prj null;
  509.     
  510.     /**
  511.      * @var string|null     DBF charset.
  512.      */
  513.     private $charset null;
  514.     
  515.     /**
  516.      * @var array   Fields definition.
  517.      *              Every field is represented by an array with the following structure:
  518.      *              [
  519.      *                  "type"      => string
  520.      *                  "size"      => int
  521.      *                  "decimals"  => int
  522.      *              ]
  523.      */
  524.     private $fields = [];
  525.     
  526.     /**
  527.      * @var array   Array of file pointer resource handles.
  528.      */
  529.     private $files = [];
  530.     
  531.     /**
  532.      * @var array   Array of canonicalized absolute pathnames of open files.
  533.      *              It will be populated only if files are NOT passed as stream resources.
  534.      */
  535.     private $filenames = [];
  536.     
  537.     /**
  538.      * @var array   Options.
  539.      */
  540.     private $options = [];
  541.     
  542.     /**
  543.      * @var int     Total number of records.
  544.      */
  545.     private $tot_records;
  546.     
  547.     
  548.     /**
  549.      * @var bool|null   Flag to store whether the machine is big endian or not.
  550.      */
  551.     private $flag_big_endian_machine null;
  552.     
  553.     /**
  554.      * @var bool    Flag representing whether the Shapefile has been initialized with any Geometry or not.
  555.      */
  556.     private $flag_initialized false;
  557.     
  558.     
  559.     
  560.     /////////////////////////////// PUBLIC ///////////////////////////////
  561.     /**
  562.      * Checks if Shapefile is of type Z.
  563.      *
  564.      * @return  bool
  565.      */
  566.     public function isZ()
  567.     {
  568.         $shape_type $this->getShapeType(Shapefile::FORMAT_INT);
  569.         return $shape_type 10 && $shape_type 20;
  570.     }
  571.     
  572.     /**
  573.      * Checks if Shapefile is of type M.
  574.      *
  575.      * @return  bool
  576.      */
  577.     public function isM()
  578.     {
  579.         return $this->getShapeType(Shapefile::FORMAT_INT) > 10;
  580.     }
  581.     
  582.     
  583.     /**
  584.      * Gets shape type either as integer or string.
  585.      *
  586.      * @param   int     $format     Optional desired output format.
  587.      *                              It can be on of the following:
  588.      *                              - Shapefile::FORMAT_INT [default]
  589.      *                              - Shapefile::FORMAT_STR
  590.      *
  591.      * @return  int|string
  592.      */
  593.     public function getShapeType($format Shapefile::FORMAT_INT)
  594.     {
  595.         if ($this->shape_type === null) {
  596.             throw new ShapefileException(Shapefile::ERR_SHP_TYPE_NOT_SET);
  597.         }
  598.         if ($format == Shapefile::FORMAT_STR) {
  599.             return Shapefile::$shape_types[$this->shape_type];
  600.         } else {
  601.             return $this->shape_type;
  602.         }
  603.     }
  604.     
  605.     
  606.     /**
  607.      * Gets Shapefile bounding box.
  608.      *
  609.      * @return  array
  610.      */
  611.     public function getBoundingBox()
  612.     {
  613.         return $this->custom_bounding_box ?: $this->computed_bounding_box;
  614.     }
  615.     
  616.     
  617.     /**
  618.      * Gets PRJ well-known-text.
  619.      *
  620.      * @return  string
  621.      */
  622.     public function getPRJ()
  623.     {
  624.         return $this->prj;
  625.     }
  626.     
  627.     
  628.     /**
  629.      * Gets DBF charset.
  630.      *
  631.      * @return  string
  632.      */
  633.     public function getCharset()
  634.     {
  635.         return $this->charset ?: Shapefile::DBF_DEFAULT_CHARSET;
  636.     }
  637.     
  638.     /**
  639.      * Sets or resets DBF charset.
  640.      *
  641.      * @param   mixed   $charset    Name of the charset.
  642.      *                              Pass a falsy value (eg. false or "") to reset it to default.
  643.      *
  644.      * @return  self    Returns $this to provide a fluent interface.
  645.      */
  646.     public function setCharset($charset)
  647.     {
  648.         $this->charset $charset ?: Shapefile::DBF_DEFAULT_CHARSET;
  649.         return $this;
  650.     }
  651.     
  652.     
  653.     /**
  654.      * Gets all fields names.
  655.      *
  656.      * @return  array
  657.      */
  658.     public function getFieldsNames()
  659.     {
  660.         return array_keys($this->fields);
  661.     }
  662.     
  663.     /**
  664.      * Gets all fields definitions.
  665.      *
  666.      * @return  array
  667.      */
  668.     public function getFields()
  669.     {
  670.         return $this->fields;
  671.     }
  672.     
  673.     /**
  674.      * Gets a field type.
  675.      *
  676.      * @param   string  $name   Name of the field.
  677.      *
  678.      * @return  string
  679.      */
  680.     public function getFieldType($name)
  681.     {
  682.         return $this->getField($name)['type'];
  683.     }
  684.     
  685.     /**
  686.      * Gets a field size.
  687.      *
  688.      * @param   string  $name   Name of the field.
  689.      *
  690.      * @return  int
  691.      */
  692.     public function getFieldSize($name)
  693.     {
  694.         return $this->getField($name)['size'];
  695.     }
  696.     
  697.     /**
  698.      * Gets a field decimals.
  699.      *
  700.      * @param   string  $name   Name of the field.
  701.      *
  702.      * @return  int
  703.      */
  704.     public function getFieldDecimals($name)
  705.     {
  706.         return $this->getField($name)['decimals'];
  707.     }
  708.     
  709.     /**
  710.      * Gets a complete field definition.
  711.      *
  712.      * The returned array contains the following elements:
  713.      *  [
  714.      *      "type"      => string
  715.      *      "size"      => int
  716.      *      "decimals"  => int
  717.      *  ]
  718.      *
  719.      * @param   string  $name   Name of the field.
  720.      *
  721.      * @return  array
  722.      */
  723.     public function getField($name)
  724.     {
  725.         $name $this->normalizeDBFFieldNameCase($name);
  726.         if (!isset($this->fields[$name])) {
  727.             throw new ShapefileException(Shapefile::ERR_INPUT_FIELD_NOT_FOUND$name);
  728.         }
  729.         return $this->fields[$name];
  730.     }
  731.     
  732.     
  733.     /**
  734.      * Gets total number of records in SHP and DBF files.
  735.      *
  736.      * @return  int
  737.      */
  738.     public function getTotRecords()
  739.     {
  740.         return $this->tot_records;
  741.     }
  742.     
  743.     
  744.     
  745.     /////////////////////////////// PROTECTED ///////////////////////////////
  746.     /**
  747.      * Opens file pointer resource handles to specified files with binary read or write access.
  748.      *
  749.      * (Filenames are mapped here because files are closed in destructors and working directory may be different!)
  750.      *
  751.      * @param   string|array    $files          Path to SHP file / Array of paths / Array of resource handles of individual files.
  752.      * @param   bool            $write_access   Access type: false = read; true = write.
  753.      * @param   array           $ignored_files  Optional map of files to ignore [filetype => bool].
  754.      *
  755.      * @return  self    Returns $this to provide a fluent interface.
  756.      */
  757.     protected function openFiles($files$write_access$ignored_files = [])
  758.     {
  759.         // Create $files array from single string (SHP filename)
  760.         if (is_string($files)) {
  761.             $basename = (substr($files, -4) == '.' Shapefile::FILE_SHP) ? substr($files0, -4) : $files;
  762.             $files = [
  763.                 Shapefile::FILE_SHP => $basename '.' Shapefile::FILE_SHP,
  764.                 Shapefile::FILE_SHX => $basename '.' Shapefile::FILE_SHX,
  765.                 Shapefile::FILE_DBF => $basename '.' Shapefile::FILE_DBF,
  766.                 Shapefile::FILE_DBT => $basename '.' Shapefile::FILE_DBT,
  767.                 Shapefile::FILE_PRJ => $basename '.' Shapefile::FILE_PRJ,
  768.                 Shapefile::FILE_CPG => $basename '.' Shapefile::FILE_CPG,
  769.             ];
  770.         }
  771.         
  772.         // Ignored files
  773.         $ignored_files $ignored_files + [
  774.             Shapefile::FILE_SHX => false,
  775.             Shapefile::FILE_DBF => false,
  776.         ];
  777.         
  778.         // Make sure required files are specified
  779.         foreach (
  780.             [
  781.                 Shapefile::FILE_SHP,
  782.                 Shapefile::FILE_SHX,
  783.                 Shapefile::FILE_DBF,
  784.             ] as $type
  785.         ) {
  786.             if (!is_array($files) || (!isset($files[$type]) && (!isset($ignored_files[$type]) || !$ignored_files[$type]))) {
  787.                 throw new ShapefileException(Shapefile::ERR_FILE_MISSINGstrtoupper($type));
  788.             }
  789.         }
  790.         
  791.         
  792.         $mode $write_access 'c+b' 'rb';
  793.         if ($files === array_filter($files'is_resource')) {
  794.             // Resource handles
  795.             foreach ($files as $type => $file) {
  796.                 if (!isset($ignored_files[$type]) || !$ignored_files[$type]) {
  797.                     $file_mode stream_get_meta_data($file)['mode'];
  798.                     if (
  799.                             get_resource_type($file) != 'stream'
  800.                         ||  (!$write_access && !in_array($file_mode, ['rb''r+b''w+b''x+b''c+b']))
  801.                         ||  ($write_access && !in_array($file_mode, ['r+b''wb''w+b''xb''x+b''cb''c+b']))
  802.                     ) {
  803.                         throw new ShapefileException(Shapefile::ERR_FILE_INVALID_RESOURCEstrtoupper($type));
  804.                     }
  805.                     $this->files[$type] = $file;
  806.                 }
  807.             }
  808.             $this->filenames = [];
  809.         } else {
  810.             // Filenames
  811.             foreach (
  812.                 [
  813.                     Shapefile::FILE_SHP => true,
  814.                     Shapefile::FILE_SHX => !$ignored_files[Shapefile::FILE_SHX],
  815.                     Shapefile::FILE_DBF => !$ignored_files[Shapefile::FILE_DBF],
  816.                     Shapefile::FILE_DBT => false,
  817.                     Shapefile::FILE_PRJ => false,
  818.                     Shapefile::FILE_CPG => false,
  819.                 ] as $type => $required
  820.             ) {
  821.                 if (isset($files[$type]) && (!isset($ignored_files[$type]) || !$ignored_files[$type])) {
  822.                     if (
  823.                             (!$write_access && is_string($files[$type]) && is_readable($files[$type]) && is_file($files[$type]))
  824.                         ||  ($write_access && is_string($files[$type]) && is_writable(dirname($files[$type])) && (!file_exists($files[$type]) || ($this->getOption(Shapefile::OPTION_EXISTING_FILES_MODE) != Shapefile::MODE_PRESERVE && is_readable($files[$type]) && is_file($files[$type]))))
  825.                     ) {
  826.                         $handle fopen($files[$type], $mode);
  827.                         if ($handle === false) {
  828.                             throw new ShapefileException(Shapefile::ERR_FILE_OPEN$files[$type]);
  829.                         }
  830.                         $this->files[$type]     = $handle;
  831.                         $this->filenames[$type] = realpath(stream_get_meta_data($handle)['uri']);
  832.                     } elseif ($required) {
  833.                         throw new ShapefileException(Shapefile::ERR_FILE_EXISTS$files[$type]);
  834.                     }
  835.                 }
  836.             }
  837.         }
  838.         foreach (array_keys($this->files) as $file_type) {
  839.             $this->setFilePointer($file_type0);
  840.         }
  841.         
  842.         return $this;
  843.     }
  844.     
  845.     /**
  846.      * Closes all open resource handles.
  847.      *
  848.      * @return  self    Returns $this to provide a fluent interface.
  849.      */
  850.     protected function closeFiles()
  851.     {
  852.         if (count($this->filenames) > 0) {
  853.             foreach ($this->files as $handle) {
  854.                 fclose($handle);
  855.             }
  856.         }
  857.         return $this;
  858.     }
  859.     
  860.     /**
  861.      * Truncates an open resource handle to a given length.
  862.      *
  863.      * @param   string  $file_type  File type.
  864.      * @param   int     $size       Optional size to truncate to.
  865.      *
  866.      * @return  self    Returns $this to provide a fluent interface.
  867.      */
  868.     protected function fileTruncate($file_type$size 0)
  869.     {
  870.         ftruncate($this->files[$file_type], $size);
  871.         return $this;
  872.     }
  873.     
  874.     /**
  875.      * Checks if file type has been opened.
  876.      *
  877.      * @param   string  $file_type  File type.
  878.      *
  879.      * @return  bool
  880.      */
  881.     protected function isFileOpen($file_type)
  882.     {
  883.         return isset($this->files[$file_type]);
  884.     }
  885.     
  886.     /**
  887.      * Gets an array of the open resource handles.
  888.      *
  889.      * @return  array
  890.      */
  891.     protected function getFiles()
  892.     {
  893.         return $this->files;
  894.     }
  895.     
  896.     /**
  897.      * Gets an array of canonicalized absolute pathnames if files were NOT passed as stream resources, or an empty array if they were.
  898.      *
  899.      * @return  array
  900.      */
  901.     protected function getFilenames()
  902.     {
  903.         return $this->filenames;
  904.     }
  905.     
  906.     /**
  907.      * Gets size of an open a resource handle.
  908.      *
  909.      * @param   string  $file_type  File type (member of $this->files array).
  910.      *
  911.      * @return  int
  912.      */
  913.     protected function getFileSize($file_type)
  914.     {
  915.         return fstat($this->files[$file_type])['size'];
  916.     }
  917.     
  918.     /**
  919.      * Gets current pointer position of a resource handle.
  920.      *
  921.      * @param   string  $file_type  File type (member of $this->files array).
  922.      *
  923.      * @return  int
  924.      */
  925.     protected function getFilePointer($file_type)
  926.     {
  927.         return ftell($this->files[$file_type]);
  928.     }
  929.     
  930.     /**
  931.      * Sets the pointer position of a resource handle to specified value.
  932.      *
  933.      * @param   string  $file_type  File type (member of $this->files array).
  934.      * @param   int     $position   The position to set the pointer to.
  935.      *
  936.      * @return  self    Returns $this to provide a fluent interface.
  937.      */
  938.     protected function setFilePointer($file_type$position)
  939.     {
  940.         fseek($this->files[$file_type], $positionSEEK_SET);
  941.         return $this;
  942.     }
  943.     
  944.     /**
  945.      * Resets the pointer position of a resource handle to its end.
  946.      *
  947.      * @param   string  $file_type  File type (member of $this->files array).
  948.      *
  949.      * @return  self    Returns $this to provide a fluent interface.
  950.      */
  951.     protected function resetFilePointer($file_type)
  952.     {
  953.         fseek($this->files[$file_type], 0SEEK_END);
  954.         return $this;
  955.     }
  956.     
  957.     /**
  958.      * Increase the pointer position of a resource handle of specified value.
  959.      *
  960.      * @param   string  $file_type  File type (member of $this->files array).
  961.      * @param   int     $offset     The offset to move the pointer for.
  962.      *
  963.      * @return  self    Returns $this to provide a fluent interface.
  964.      */
  965.     protected function setFileOffset($file_type$offset)
  966.     {
  967.         fseek($this->files[$file_type], $offsetSEEK_CUR);
  968.         return $this;
  969.     }
  970.     
  971.     /**
  972.      * Reads data from an open resource handle.
  973.      *
  974.      * @param   string  $file_type      File type.
  975.      * @param   int     $length         Number of bytes to read.
  976.      *
  977.      * @return  string
  978.      */
  979.     protected function readData($file_type$length)
  980.     {
  981.         $ret = @fread($this->files[$file_type], $length);
  982.         if ($ret === false) {
  983.             throw new ShapefileException(Shapefile::ERR_FILE_READING);
  984.         }
  985.         return $ret;
  986.     }
  987.     
  988.     /**
  989.      * Writes binary string packed data to an open resource handle.
  990.      *
  991.      * @param   string  $file_type      File type.
  992.      * @param   string  $data           Binary string packed data to write.
  993.      *
  994.      * @return  self    Returns $this to provide a fluent interface.
  995.      */
  996.     protected function writeData($file_type$data)
  997.     {
  998.         if (@fwrite($this->files[$file_type], $data) === false) {
  999.             throw new ShapefileException(Shapefile::ERR_FILE_WRITING);
  1000.         }
  1001.         return $this;
  1002.     }
  1003.     
  1004.     /**
  1005.      * Checks if machine is big endian.
  1006.      *
  1007.      * @return  bool
  1008.      */
  1009.     protected function isBigEndianMachine()
  1010.     {
  1011.         if ($this->flag_big_endian_machine === null) {
  1012.             $this->flag_big_endian_machine current(unpack('v'pack('S'0xff))) !== 0xff;
  1013.         }
  1014.         return $this->flag_big_endian_machine;
  1015.     }
  1016.     
  1017.     
  1018.     /**
  1019.      * Initializes options with default and user-provided values.
  1020.      *
  1021.      * @param   array   $options_list   Array of options to initialize.
  1022.      * @param   array   $user_values    User-provided options values.
  1023.      *
  1024.      * @return  self    Returns $this to provide a fluent interface.
  1025.      */
  1026.     protected function initOptions($options_list$user_values)
  1027.     {
  1028.         // Make sure compulsory options used in this abstract class are defined
  1029.         $options_list array_unique(array_merge($options_list, [
  1030.             Shapefile::OPTION_DBF_ALLOW_FIELD_SIZE_255,
  1031.             Shapefile::OPTION_DBF_FORCE_ALL_CAPS,
  1032.             Shapefile::OPTION_SUPPRESS_M,
  1033.             Shapefile::OPTION_SUPPRESS_Z,
  1034.         ]));
  1035.         
  1036.         // Defaults
  1037.         $defaults = [];
  1038.         foreach ($options_list as $option) {
  1039.             $defaults[$option] = constant('Shapefile\Shapefile::' $option '_DEFAULT');
  1040.         }
  1041.         
  1042.         // Filter custom options
  1043.         $user_values array_intersect_key(array_change_key_case($user_valuesCASE_UPPER), $defaults);
  1044.         
  1045.         // Initialize option array
  1046.         $this->options $user_values $defaults;
  1047.         
  1048.         // Use only the first character of OPTION_DBF_NULL_PADDING_CHAR if it's set and is not false or empty
  1049.         $k Shapefile::OPTION_DBF_NULL_PADDING_CHAR;
  1050.         if (array_key_exists($k$this->options)) {
  1051.             $this->options[$k] = ($this->options[$k] === false || $this->options[$k] === null || $this->options[$k] === '') ? null substr($this->options[$k], 01);
  1052.         }
  1053.         
  1054.         // Parse OPTION_DBF_IGNORED_FIELDS
  1055.         $k Shapefile::OPTION_DBF_IGNORED_FIELDS;
  1056.         if (array_key_exists($k$this->options)) {
  1057.             $this->options[$k] = is_array($this->options[$k]) ? array_map([$this'normalizeDBFFieldNameCase'], $this->options[$k]) : [];
  1058.         }
  1059.         
  1060.         return $this;
  1061.     }
  1062.     
  1063.     /**
  1064.      * Gets option value.
  1065.      *
  1066.      * @param   string  $option     Name of the option.
  1067.      *
  1068.      * @return  string
  1069.      */
  1070.     protected function getOption($option)
  1071.     {
  1072.         return $this->options[$option];
  1073.     }
  1074.     
  1075.     
  1076.     /**
  1077.      * Sets shape type.
  1078.      * It can be called just once for an instance of the class.
  1079.      *
  1080.      * @param   int     $type   Shape type. It can be on of the following:
  1081.      *                          - Shapefile::SHAPE_TYPE_NULL
  1082.      *                          - Shapefile::SHAPE_TYPE_POINT
  1083.      *                          - Shapefile::SHAPE_TYPE_POLYLINE
  1084.      *                          - Shapefile::SHAPE_TYPE_POLYGON
  1085.      *                          - Shapefile::SHAPE_TYPE_MULTIPOINT
  1086.      *                          - Shapefile::SHAPE_TYPE_POINTZ
  1087.      *                          - Shapefile::SHAPE_TYPE_POLYLINEZ
  1088.      *                          - Shapefile::SHAPE_TYPE_POLYGONZ
  1089.      *                          - Shapefile::SHAPE_TYPE_MULTIPOINTZ
  1090.      *                          - Shapefile::SHAPE_TYPE_POINTM
  1091.      *                          - Shapefile::SHAPE_TYPE_POLYLINEM
  1092.      *                          - Shapefile::SHAPE_TYPE_POLYGONM
  1093.      *                          - Shapefile::SHAPE_TYPE_MULTIPOINTM
  1094.      *
  1095.      * @return  self    Returns $this to provide a fluent interface.
  1096.      */
  1097.     protected function setShapeType($type)
  1098.     {
  1099.         if ($this->shape_type !== null) {
  1100.             throw new ShapefileException(Shapefile::ERR_SHP_TYPE_ALREADY_SET);
  1101.         }
  1102.         if (!isset(Shapefile::$shape_types[$type])) {
  1103.             throw new ShapefileException(Shapefile::ERR_SHP_TYPE_NOT_SUPPORTED$type);
  1104.         }
  1105.         $this->shape_type $type;
  1106.         return $this;
  1107.     }
  1108.     
  1109.     /**
  1110.      * Gets Shapefile base type, regardless of Z and M dimensions.
  1111.      *
  1112.      * @return  int
  1113.      */
  1114.     protected function getBasetype()
  1115.     {
  1116.         return $this->getShapeType(Shapefile::FORMAT_INT) % 10;
  1117.     }
  1118.     
  1119.     
  1120.     /**
  1121.      * Overwrites computed bounding box for the Shapefile.
  1122.      * No check is carried out except a formal compliance of dimensions.
  1123.      *
  1124.      * @param   array   $bounding_box   Associative array with the xmin, xmax, ymin, ymax and optional zmin, zmax, mmin, mmax values.
  1125.      *
  1126.      * @return  self    Returns $this to provide a fluent interface.
  1127.      */
  1128.     protected function overwriteComputedBoundingBox($bounding_box)
  1129.     {
  1130.         if ($bounding_box) {
  1131.             $this->computed_bounding_box $this->sanitizeBoundingBox($bounding_box);
  1132.         }
  1133.         return $this;
  1134.     }
  1135.     
  1136.     /**
  1137.      * Sets a custom bounding box for the Shapefile.
  1138.      * No check is carried out except a formal compliance of dimensions.
  1139.      *
  1140.      * @param   array   $bounding_box   Associative array with the xmin, xmax, ymin, ymax and optional zmin, zmax, mmin, mmax values.
  1141.      *
  1142.      * @return  self    Returns $this to provide a fluent interface.
  1143.      */
  1144.     protected function setCustomBoundingBox($bounding_box)
  1145.     {
  1146.         $this->custom_bounding_box $this->sanitizeBoundingBox($bounding_box);
  1147.         return $this;
  1148.     }
  1149.     
  1150.     /**
  1151.      * Resets custom bounding box for the Shapefile.
  1152.      * It will cause getBoundingBox() method to return a normally computed bbox instead of a custom one.
  1153.      *
  1154.      * @return  self    Returns $this to provide a fluent interface.
  1155.      */
  1156.     protected function resetCustomBoundingBox()
  1157.     {
  1158.         $this->custom_bounding_box null;
  1159.         return $this;
  1160.     }
  1161.     
  1162.     
  1163.     /**
  1164.      * Sets PRJ well-known-text.
  1165.      *
  1166.      * @param   string  $prj    PRJ well-known-text.
  1167.      *                          Pass a falsy value (eg. false or "") to delete it.
  1168.      *
  1169.      * @return  self    Returns $this to provide a fluent interface.
  1170.      */
  1171.     protected function setPRJ($prj)
  1172.     {
  1173.         $this->prj $prj ?: null;
  1174.         return $this;
  1175.     }
  1176.     
  1177.     
  1178.     /**
  1179.      * Sets current total number of records.
  1180.      *
  1181.      * @param   int     $tot_records    Total number of records currently in the files.
  1182.      *
  1183.      * @return  self    Returns $this to provide a fluent interface.
  1184.      */
  1185.     protected function setTotRecords($tot_records)
  1186.     {
  1187.         $this->tot_records $tot_records;
  1188.         return $this;
  1189.     }
  1190.     
  1191.     /**
  1192.      * Gets the state of the initialized flag.
  1193.      *
  1194.      * @return  bool
  1195.      */
  1196.     protected function isInitialized()
  1197.     {
  1198.         return $this->flag_initialized;
  1199.     }
  1200.     
  1201.     /**
  1202.      * Sets the state of the initialized flag.
  1203.      *
  1204.      * @param   bool    $value
  1205.      *
  1206.      * @return  self    Returns $this to provide a fluent interface.
  1207.      */
  1208.     protected function setFlagInitialized($value)
  1209.     {
  1210.         $this->flag_initialized $value;
  1211.         return $this;
  1212.     }
  1213.     
  1214.     
  1215.     /**
  1216.      * Adds a field to the shapefile definition.
  1217.      * Returns the effective field name after eventual sanitization.
  1218.      *
  1219.      * @param   string  $name               Name of the field. Invalid names will be sanitized
  1220.      *                                      (maximum 10 characters, only letters, numbers and underscores are allowed).
  1221.      * @param   string  $type               Type of the field. It can be on of the following:
  1222.      *                                      - Shapefile::DBF_TYPE_CHAR
  1223.      *                                      - Shapefile::DBF_TYPE_DATE
  1224.      *                                      - Shapefile::DBF_TYPE_LOGICAL
  1225.      *                                      - Shapefile::DBF_TYPE_MEMO
  1226.      *                                      - Shapefile::DBF_TYPE_NUMERIC
  1227.      *                                      - Shapefile::DBF_TYPE_FLOAT
  1228.      * @param   int     $size               Lenght of the field, depending on the type.
  1229.      * @param   int     $decimals           Optional number of decimal digits for numeric type.
  1230.      *
  1231.      * @return  string
  1232.      */
  1233.     protected function addField($name$type$size$decimals)
  1234.     {
  1235.         // Check init
  1236.         if ($this->isInitialized()) {
  1237.             throw new ShapefileException(Shapefile::ERR_SHP_FILE_ALREADY_INITIALIZED);
  1238.         }
  1239.         // Check filed count
  1240.         if (count($this->fields) >= Shapefile::DBF_MAX_FIELD_COUNT) {
  1241.             throw new ShapefileException(Shapefile::ERR_DBF_MAX_FIELD_COUNT_REACHEDShapefile::DBF_MAX_FIELD_COUNT);
  1242.         }
  1243.         
  1244.         // Sanitize name and normalize case
  1245.         $name $this->normalizeDBFFieldNameCase($this->sanitizeDBFFieldName($name));
  1246.         
  1247.         // Check type
  1248.         if (
  1249.                 $type !== Shapefile::DBF_TYPE_CHAR
  1250.             &&  $type !== Shapefile::DBF_TYPE_DATE
  1251.             &&  $type !== Shapefile::DBF_TYPE_LOGICAL
  1252.             &&  $type !== Shapefile::DBF_TYPE_MEMO
  1253.             &&  $type !== Shapefile::DBF_TYPE_NUMERIC
  1254.             &&  $type !== Shapefile::DBF_TYPE_FLOAT
  1255.         ) {
  1256.             throw new ShapefileException(Shapefile::ERR_DBF_FIELD_TYPE_NOT_VALID$type);
  1257.         }
  1258.         
  1259.         // Check size
  1260.         $size       intval($size);
  1261.         $max_size   $this->getOption(Shapefile::OPTION_DBF_ALLOW_FIELD_SIZE_255) ? 255 254;
  1262.         if (
  1263.                 ($size 1)
  1264.             ||  ($type == Shapefile::DBF_TYPE_CHAR && $size $max_size)
  1265.             ||  ($type == Shapefile::DBF_TYPE_DATE && $size !== 8)
  1266.             ||  ($type == Shapefile::DBF_TYPE_LOGICAL && $size !== 1)
  1267.             ||  ($type == Shapefile::DBF_TYPE_MEMO && $size !== 10)
  1268.             ||  ($type == Shapefile::DBF_TYPE_NUMERIC && $size $max_size)
  1269.             ||  ($type == Shapefile::DBF_TYPE_FLOAT && $size $max_size)
  1270.         ) {
  1271.             throw new ShapefileException(Shapefile::ERR_DBF_FIELD_SIZE_NOT_VALID$size);
  1272.         }
  1273.         
  1274.         // Minimal decimal formal check
  1275.         $decimals intval($decimals);
  1276.         if (
  1277.                 ($type != Shapefile::DBF_TYPE_NUMERIC && $type != Shapefile::DBF_TYPE_FLOAT && $decimals !== 0)
  1278.             ||  ($type == Shapefile::DBF_TYPE_FLOAT && $decimals === 0)
  1279.             ||  ($decimals 0)
  1280.             ||  ($decimals && $size <= $decimals)
  1281.         ) {
  1282.             throw new ShapefileException(Shapefile::ERR_DBF_FIELD_DECIMALS_NOT_VALID$type ' - ' $decimals);
  1283.         }
  1284.         
  1285.         // Add field
  1286.         $this->fields[$name] = [
  1287.             'type'      => $type,
  1288.             'size'      => $size,
  1289.             'decimals'  => $decimals,
  1290.         ];
  1291.         
  1292.         return $name;
  1293.     }
  1294.     
  1295.     
  1296.     /**
  1297.      * Normalize field name case according to OPTION_DBF_FORCE_ALL_CAPS status.
  1298.      *
  1299.      * @param   string  $input      Field name to be case-normalized.
  1300.      *
  1301.      * @return  string
  1302.      */
  1303.     protected function normalizeDBFFieldNameCase($input)
  1304.     {
  1305.         return $this->getOption(Shapefile::OPTION_DBF_FORCE_ALL_CAPS) ? strtoupper($input) : $input;
  1306.     }
  1307.     
  1308.     
  1309.     /**
  1310.      * Pairs a Geometry with the Shapefile.
  1311.      * It enforces the Geometry type and computes Shapefile bounding box.
  1312.      * After that the Shapefile will be considered as "initialized" and no changes will be allowd to its structure.
  1313.      *
  1314.      * @param   \Shapefile\Geometry\Geometry    $Geometry   Geometry to pair with.
  1315.      *
  1316.      * @return  self    Returns $this to provide a fluent interface.
  1317.      */
  1318.     protected function pairGeometry(Geometry\Geometry $Geometry)
  1319.     {
  1320.         // Geometry type
  1321.         if (
  1322.                 $this->getBasetype() !== $Geometry->getSHPBasetype()
  1323.             ||  (!$Geometry->isEmpty() && $Geometry->isZ() !== $this->isZ() && !$this->getOption(Shapefile::OPTION_SUPPRESS_Z))
  1324.             ||  (!$Geometry->isEmpty() && $Geometry->isM() !== $this->isM() && !$this->getOption(Shapefile::OPTION_SUPPRESS_M))
  1325.         ) {
  1326.             throw new ShapefileException(Shapefile::ERR_SHP_GEOMETRY_TYPE_NOT_COMPATIBLE$this->getShapeType(Shapefile::FORMAT_INT) . ' - ' $this->getShapeType(Shapefile::FORMAT_STR));
  1327.         }
  1328.         
  1329.         // Bounding box
  1330.         $bbox $Geometry->getBoundingBox();
  1331.         if (!$this->computed_bounding_box && $bbox) {
  1332.             if ($this->getOption(Shapefile::OPTION_SUPPRESS_Z)) {
  1333.                 unset($bbox['zmin'], $bbox['zmax']);
  1334.             }
  1335.             if ($this->getOption(Shapefile::OPTION_SUPPRESS_M)) {
  1336.                 unset($bbox['mmin'], $bbox['mmax']);
  1337.             }
  1338.             $this->computed_bounding_box $bbox;
  1339.         } elseif ($bbox) {
  1340.             if ($bbox['xmin'] < $this->computed_bounding_box['xmin']) {
  1341.                 $this->computed_bounding_box['xmin'] = $bbox['xmin'];
  1342.             }
  1343.             if ($bbox['xmax'] > $this->computed_bounding_box['xmax']) {
  1344.                 $this->computed_bounding_box['xmax'] = $bbox['xmax'];
  1345.             }
  1346.             if ($bbox['ymin'] < $this->computed_bounding_box['ymin']) {
  1347.                 $this->computed_bounding_box['ymin'] = $bbox['ymin'];
  1348.             }
  1349.             if ($bbox['ymax'] > $this->computed_bounding_box['ymax']) {
  1350.                 $this->computed_bounding_box['ymax'] = $bbox['ymax'];
  1351.             }
  1352.             if ($this->isZ() && !$this->getOption(Shapefile::OPTION_SUPPRESS_Z)) {
  1353.                 if ($bbox['zmin'] < $this->computed_bounding_box['zmin']) {
  1354.                     $this->computed_bounding_box['zmin'] = $bbox['zmin'];
  1355.                 }
  1356.                 if ($bbox['zmax'] > $this->computed_bounding_box['zmax']) {
  1357.                     $this->computed_bounding_box['zmax'] = $bbox['zmax'];
  1358.                 }
  1359.             }
  1360.             if ($this->isM() && !$this->getOption(Shapefile::OPTION_SUPPRESS_M)) {
  1361.                 if ($this->computed_bounding_box['mmin'] === false || $bbox['mmin'] < $this->computed_bounding_box['mmin']) {
  1362.                     $this->computed_bounding_box['mmin'] = $bbox['mmin'];
  1363.                 }
  1364.                 if ($this->computed_bounding_box['mmax'] === false || $bbox['mmax'] > $this->computed_bounding_box['mmax']) {
  1365.                     $this->computed_bounding_box['mmax'] = $bbox['mmax'];
  1366.                 }
  1367.             }
  1368.         }
  1369.         
  1370.         // Mark Shapefile as initialized
  1371.         $this->setFlagInitialized(true);
  1372.         
  1373.         return $this;
  1374.     }
  1375.     
  1376.     
  1377.     
  1378.     /////////////////////////////// PRIVATE ///////////////////////////////
  1379.     /**
  1380.      * Checks formal compliance of a bounding box dimensions.
  1381.      *
  1382.      * @param   array   $bounding_box   Associative array with the xmin, xmax, ymin, ymax and optional zmin, zmax, mmin, mmax values.
  1383.      */
  1384.     private function sanitizeBoundingBox($bounding_box)
  1385.     {
  1386.         $bounding_box array_intersect_key($bounding_boxarray_flip(['xmin''xmax''ymin''ymax''zmin''zmax''mmin''mmax']));
  1387.         if ($this->getOption(Shapefile::OPTION_SUPPRESS_Z)) {
  1388.             unset($bounding_box['zmin'], $bounding_box['zmax']);
  1389.         }
  1390.         if ($this->getOption(Shapefile::OPTION_SUPPRESS_M)) {
  1391.             unset($bounding_box['mmin'], $bounding_box['mmax']);
  1392.         }
  1393.         
  1394.         if (
  1395.             !isset($bounding_box['xmin'], $bounding_box['xmax'], $bounding_box['ymin'], $bounding_box['ymax'])
  1396.             || (
  1397.                 ($this->isZ() && !$this->getOption(Shapefile::OPTION_SUPPRESS_Z) && !isset($bounding_box['zmin'], $bounding_box['zmax']))
  1398.                 || (!$this->isZ() && (isset($bounding_box['zmin']) || isset($bounding_box['zmax'])))
  1399.             )
  1400.             || (
  1401.                 ($this->isM() && !$this->getOption(Shapefile::OPTION_SUPPRESS_M) && !isset($bounding_box['mmin'], $bounding_box['mmax']))
  1402.                 || (!$this->isM() && (isset($bounding_box['mmin']) || isset($bounding_box['mmax'])))
  1403.             )
  1404.         ) {
  1405.             throw new ShapefileException(Shapefile::ERR_SHP_MISMATCHED_BBOX);
  1406.         }
  1407.         
  1408.         return $bounding_box;
  1409.     }
  1410.     
  1411.     
  1412.     /**
  1413.      * Returns a valid name for a DBF field.
  1414.      *
  1415.      * Only letters, numbers and underscores are allowed, everything else is converted to underscores.
  1416.      * Field names get truncated to 10 characters and conflicting ones are truncated to 8 characters adding a number from 1 to 99.
  1417.      *
  1418.      * @param   string  $input      Raw name to be sanitized.
  1419.      *
  1420.      * @return  string
  1421.      */
  1422.     private function sanitizeDBFFieldName($input)
  1423.     {
  1424.         if ($input === '') {
  1425.             return $input;
  1426.         }
  1427.         
  1428.         $ret        substr(preg_replace('/[^a-zA-Z0-9]/''_'$input), 010);
  1429.         $fieldnames array_fill_keys(array_keys(array_change_key_case($this->fieldsCASE_UPPER)), true);
  1430.         if (isset($fieldnames[strtoupper($ret)])) {
  1431.             $ret substr($ret08) . '_1';
  1432.             while (isset($fieldnames[strtoupper($ret)])) {
  1433.                 $n intval(trim(substr($ret, -2), '_')) + 1;
  1434.                 if ($n 99) {
  1435.                     throw new ShapefileException(Shapefile::ERR_DBF_FIELD_NAME_NOT_VALID$input);
  1436.                 }
  1437.                 $ret substr($ret0, -2) . str_pad($n2'_'STR_PAD_LEFT);
  1438.             }
  1439.         }
  1440.         return $ret;
  1441.     }
  1442. }