shapekit

A comprehensive Dart library for reading and writing ESRI Shapefiles, with GeoPackage support for feature data workflows.

pub package Buy Me A Coffee

Features

  • Complete Shapefile Support - Read and write .shp, .shx, .dbf, and .prj files
  • 12 Geometry Types - Point, PointM, PointZ, Polyline, PolylineM, PolylineZ, Polygon, PolygonM, PolygonZ, MultiPoint, MultiPointM, MultiPointZ
  • GeoPackage Support - Read feature tables, inspect metadata, stream typed features, and write feature data
  • Streaming Shapefile Reads - Read large shapefiles incrementally with ShapefileStreamReader
  • Attribute Support - Full dBASE III+ (.dbf) file support for feature attributes
  • Projection Support - Read projection information from .prj files
  • Korean Text Support - CP949 encoding for Korean text in attributes
  • UTF-8 Support - Modern UTF-8 encoding support
  • Type-Safe - Strongly typed geometry classes with immutable data structures
  • Clean Architecture - Well-organized codebase following clean architecture principles

Installation

Add this to your package's pubspec.yaml file:

dependencies:
  shapekit: ^0.3.2

Then run:

dart pub get

Quick Start

Reading a Shapefile

import 'package:shapekit/shapekit.dart';

void main() {
  final shapefile = Shapefile();

  try {
    shapefile.read('path/to/file.shp');
    print('Loaded ${shapefile.records.length} records');

    // Access geometry
    for (final record in shapefile.records) {
      if (record is Point) {
        print('Point: ${record.x}, ${record.y}');
      } else if (record is Polyline) {
        print('Polyline with ${record.numPoints} points');
      } else if (record is Polygon) {
        print('Polygon with ${record.numParts} parts');
      }
    }

    // Access attributes (if .dbf file exists)
    for (int i = 0; i < shapefile.attributeRecords.length; i++) {
      print('Record $i attributes: ${shapefile.attributeRecords[i]}');
    }

    // Access projection EPSG code (if .prj file exists)
    if (shapefile.epsgCode != null) {
      print('Projection EPSG: ${shapefile.epsgCode}');
    }
  } on ShapefileException catch (e) {
    print('Error reading shapefile: ${e.message}');
  }
}

Writing a Shapefile

import 'package:shapekit/shapekit.dart';

void main() {
  final shapefile = Shapefile();

  // Create point records
  final records = [
    Point(126.9780, 37.5665), // Seoul
    Point(129.0756, 35.1796), // Busan
  ];

  // Create attribute fields
  final fields = [
    DbaseField.fieldC('NAME', 50),
    DbaseField.fieldN('POPULATION', 10),
  ];

  // Create attribute records
  final attributes = [
    ['Seoul', 9776000],
    ['Busan', 3413000],
  ];

  // Write shapefile
  shapefile.writeComplete(
    'cities.shp',
    ShapeType.shapePOINT,
    records,
    minX: 126.9780,
    minY: 35.1796,
    maxX: 129.0756,
    maxY: 37.5665,
    attributeFields: fields,
    attributeRecords: attributes,
  );

  print('Shapefile created successfully!');
}

Reading a GeoPackage

import 'package:shapekit/shapekit.dart';

Future<void> main() async {
  final gpkg = GpkgReader.open('path/to/file.gpkg');

  try {
    final tables = gpkg.listFeatureTables();
    print('Feature tables: $tables');

    await for (final batch in gpkg.queryFeatures(
      table: tables.first,
      bounds: const Envelope(-180, -90, 180, 90),
      loadAttributes: true,
    )) {
      for (final feature in batch.features) {
        print('${feature.fid}: ${feature.geometry.type}');
      }
    }
  } finally {
    gpkg.close();
  }
}

Streaming a Shapefile

import 'package:shapekit/shapekit.dart';

Future<void> main() async {
  final reader = ShapefileStreamReader.open('path/to/file.shp', isUtf8: true);

  await for (final feature in reader.features()) {
    print('Feature ${feature.index}: ${feature.geometry.type}');
  }
}

Supported Geometry Types

Geometry Type Class Description
Point Point Single point (X, Y)
PointM PointM Point with measure value (X, Y, M)
PointZ PointZ Point with Z and M values (X, Y, Z, M)
Polyline Polyline Line or multi-line (parts, points)
PolylineM PolylineM Polyline with optional M values
PolylineZ PolylineZ Polyline with Z and optional M values
Polygon Polygon Polygon or multi-polygon (parts, points)
PolygonM PolygonM Polygon with optional M values
PolygonZ PolygonZ Polygon with Z and optional M values
MultiPoint MultiPoint Collection of points
MultiPointM MultiPointM MultiPoint with optional M values
MultiPointZ MultiPointZ MultiPoint with Z and optional M values
MultiPatch MultiPatch 3D surface ❌ not yet implemented

Text Encoding

The library supports multiple text encodings for attribute data:

// UTF-8 encoding (default, recommended)
final shapefile = Shapefile(isUtf8: true);

// CP949 encoding (for Korean legacy data)
final shapefile = Shapefile(isCp949: true);

// ASCII encoding (when both flags are false)
final shapefile = Shapefile();

Attribute Field Types

When working with dBASE attributes, use these field types:

// Character field (text)
DbaseField.fieldC('NAME', 50)  // name, max length

// Date field
DbaseField.fieldD('DATE')  // name only

// Logical field (boolean)
DbaseField.fieldL('ACTIVE')  // name only

// Numeric field (integer)
DbaseField.fieldN('COUNT', 10)  // name, total digits

// Numeric field (floating point)
DbaseField.fieldNF('AREA', 20, 8)  // name, total digits, decimal places

Limitations

  • No MultiPatch support - MultiPatch geometry type is not yet implemented
  • No coordinate transformation - The library reads projection information but does not transform coordinates
  • Synchronous I/O - File operations are synchronous (blocking)

Error Handling

The library uses typed exceptions for error handling:

try {
  final shapefile = Shapefile();
  shapefile.read('data.shp');
} on FileNotFoundException catch (e) {
  print('File not found: ${e.path}');
} on InvalidHeaderException catch (e) {
  print('Invalid shapefile header: ${e.message}');
} on CorruptedDataException catch (e) {
  print('Corrupted data: ${e.details}');
} on ShapefileException catch (e) {
  print('Shapefile error: $e');
}

Exception Types:

  • FileNotFoundException - File not found or cannot be accessed
  • InvalidHeaderException - Invalid file header
  • InvalidFormatException - Invalid file format
  • CorruptedDataException - Corrupted record data
  • UnsupportedTypeException - Unsupported geometry type
  • ShapefileIOException - File I/O error
  • GpkgException - GeoPackage read/write/query error

Support

If you find this library helpful, consider supporting its development! ☕️

Buy Me A Coffee

Your support helps me:

  • 🐛 Fix bugs faster
  • ✨ Add new features and projections
  • 📚 Improve documentation
  • 🚀 Build more GIS tools for the community

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the MIT License - see the LICENSE file for details.

References

Acknowledgments

This library is based on the shapefile-kr repository by @michael-kim-korea.

Libraries

shapekit
A comprehensive Dart library for reading and writing ESRI Shapefiles and GeoPackage files with unified domain entities.