outdooractive/gis-tools-shapefile
ESRI Shapefile (.shp/.dbf/.shx/.prj) read and write support for Swift, built on top of gis-tools.
Features
- Reads and writes Point, PolyLine, Polygon, MultiPoint, and MultiPatch geometry, including Z and M variants
- Reads
.prjfiles to determine the source projection (EPSG:4326 assumed if absent) - Writes
.prjfiles for EPSG:4326, EPSG:3857 and EPSG:4978 - Attributes are mapped to and from
Feature.propertiesvia the companion.dbffile - Feature ids are mapped to and from a dedicated
idcolumn (configurable) - Writes
.shxindex files and.cpgcode-page files - Multipatch geometries are decomposed into
GeometryCollectionvalues withPolygonparts - Null shapes become features without a geometry and keep their attributes
- Deleted
.dbfrecords are skipped together with their geometry
Requirements
Swift 6.1 or higher. Compiles on iOS (≥ iOS 15), macOS (≥ macOS 15), tvOS (≥ tvOS 15), watchOS (≥ watchOS 7), Linux, Android and Wasm.
Installation with Swift Package Manager
dependencies: [
.package(url: "https://github.com/Outdooractive/gis-tools", from: "3.0.0"),
.package(url: "https://github.com/Outdooractive/gis-tools-shapefile", from: "3.0.0"),
],
targets: [
.target(name: "MyTarget", dependencies: [
.product(name: "GISTools", package: "gis-tools"),
.product(name: "GISToolsShapefile", package: "gis-tools-shapefile"),
]),
]Usage
Reading
import GISTools
import GISToolsShapefile
let url = URL(fileURLWithPath: "/path/to/file") // or ".../file.shp"
// Failable convenience initializer:
if let fc = FeatureCollection(shapefile: url) {
print(fc.features.count)
}
// Or via ShapefileCoder directly, which reports errors:
let fc = try ShapefileCoder.read(from: url)The failable FeatureCollection(shapefile:) initializer returns nil on any error. Use ShapefileCoder.read when you need the underlying error.
read accepts optional idColumn and dbfEncoding parameters. When dbfEncoding is nil, a .cpg file is consulted and UTF-8 is used as the fallback. A column named id is promoted to Feature.id by default; pass idColumn: nil to keep it as a property.
Writing
import GISTools
import GISToolsShapefile
let url = URL(fileURLWithPath: "/path/to/new_file")
try fc.writeShapefile(to: url)
// Or via ShapefileCoder directly:
try ShapefileCoder.write(fc, to: url)The shapefile path is constructed from the base URL by replacing the extension:
| Extension | Contents | |-----------|----------| | .shp | Geometry data | | .dbf | Attribute data | | .shx | Spatial index | | .prj | Projection string | | .cpg | .dbf code page |
Coordinates are written verbatim in the collection's projection. Project the collection first when a specific output CRS is required:
try fc.projected(to: .epsg3857).writeShapefile(to: url)In-memory data
let data = try ShapefileCoder.encode(fc)
let fc = try ShapefileCoder.read(
shp: data.shp,
dbf: data.dbf,
prj: data.prj)Errors
All failures throw ShapefileError (also available under the compatibility name ShapefileCoder.ShapefileCoderError). Truncated files, invalid headers, unsupported shape types and mismatched .shp/.dbf record counts are reported instead of being silently truncated.
Limitations
- Maximum field count: 1024 (library limit)
- Maximum record size: 65531 bytes (library limit)
- Field name length: maximum 10 ASCII characters; longer names are truncated, non-ASCII characters become underscores, and collisions receive a numeric suffix
- Character field values longer than 254 bytes are truncated
- Numeric values support up to 15 decimal places
- A shapefile contains one shape family; mixed geometry types throw
- All features in a file must use the same projection
- GeoJSON
Feature.idvalues are mapped to anidcolumn by default - MultiPatch is read as a
GeometryCollection; triangle strips and fans are decomposed into triangles - Writing a projection without a built-in WKT template (anything other than EPSG:4326, 3857 and 4978) throws
- An invalid
.prjfile throws instead of being ignored - Blank
.dbffields and empty strings read back asnil - Numeric values that do not fit their field are written as asterisks and read back as
nil - Memo (
.dbt/.fpt) field contents are not resolved - AutoCAD Civil 3D and ArcGIS-specific variants are not supported
See the DocC documentation in Sources/GISToolsShapefile/GISToolsShapefile.docc for detailed articles.
Development
swift test
swift test --enable-code-coverage
python3 scripts/coverage_report.py --minimum 90
python3 scripts/check_documentation.pyThe CI workflow runs the tests on macOS and Linux, enforces 90% line coverage on Linux, and builds the DocC catalog with warnings treated as errors.
Contributing
Please create an issue or open a pull request with a fix or enhancement.
License
MIT
Package Metadata
Repository: outdooractive/gis-tools-shapefile
Default branch: main
README: README.md