SkillAgentSearch skills...

Fudgeo

Removing the fear-uncertainty-doubt from GeoPackages. OGC GeoPackage Support via Python and SQLite

Install / Use

/learn @realiii/Fudgeo

README

fudgeo

fudgeo removes the fear uncertainty doubt from using GeoPackages with Python. fudgeo is a lightweight package for creating OGC GeoPackages, Feature Classes, and Tables. Easily read and write geometries and attributes to Feature Classes and Tables using regular Python objects and SQLite syntax.

For details on OGC GeoPackages, please see the OGC web page.

Installation

fudgeo is available from the Python Package Index.

Python Compatibility

The fudgeo library is compatible with Python 3.10 to 3.14. Developed and tested on macOS and Windows, should be fine on Linux too.

Usage

fudgeo can be used to:

  • Create a new empty GeoPackage or open an existing GeoPackage
  • Create new FeatureClass or Table with optional overwrite
  • Create SpatialReferenceSystem for a FeatureClass
  • Build geometry objects from lists of coordinate values
  • Work with data in Table or FeatureClass in a normal SQLite manner (e.g. SELECT, INSERT, UPDATE, DELETE)
  • Retrieve fields from a FeatureClass or Table
  • Access primary key field of FeatureClass or Table
  • Access geometry column name and geometry type for FeatureClass
  • Add spatial index on FeatureClass
  • Drop FeatureClass or Table
  • Add metadata and schema details

Create an Empty GeoPackage / Open GeoPackage

from fudgeo import GeoPackage

# Creates an empty geopackage
gpkg: GeoPackage = GeoPackage.create('../data/example.gpkg')

# Opens an existing Geopackage (no validation)
gpkg: GeoPackage = GeoPackage('../data/example.gpkg')

GeoPackages are created with three default Spatial References defined automatically, a pair of Spatial References to handle undefined cases, and a WGS 84 entry.

The definition of the WGS84 entry is flexible - meaning that the WKT for WGS84 can be setup per the users liking. As an example, use with Esri's ArcGIS means either using the EPSG WKT or the ESRI WKT. By default, the ESRI WKT is used - However, if EPSG WKT is desired, you may provide a flavor parameter to the create method specifying EPSG.

from fudgeo import GeoPackage

# Creates an empty geopackage using EPSG definitions
gpkg: GeoPackage = GeoPackage.create('../temp/test.gpkg', flavor='EPSG')

Create a Feature Class

Use the create_feature_class method of a GeoPackage to make a new feature class. Feature classes require a name and a Spatial Reference, the name must follow SQLite naming requirements. Each feature class is defined with fid and SHAPE fields, additional fields can be defined during creation. SHAPE is the default geometry column name however it can be specified during feature class creation.

A Feature Class can be created with Z or M (or both) enabled. If either of these options are enabled, the geometry inserted into the Feature Class must include a value for the option specified.

from fudgeo import FeatureClass, Field, GeoPackage, SpatialReferenceSystem
from fudgeo.enumeration import FieldType, ShapeType

SRS_WKT: str = (
    'PROJCS["WGS_1984_UTM_Zone_23N",'
    'GEOGCS["GCS_WGS_1984",'
    'DATUM["D_WGS_1984",'
    'SPHEROID["WGS_1984",6378137.0,298.257223563]],'
    'PRIMEM["Greenwich",0.0],'
    'UNIT["Degree",0.0174532925199433]],'
    'PROJECTION["Transverse_Mercator"],'
    'PARAMETER["False_Easting",500000.0],'
    'PARAMETER["False_Northing",0.0],'
    'PARAMETER["Central_Meridian",-45.0],'
    'PARAMETER["Scale_Factor",0.9996],'
    'PARAMETER["Latitude_Of_Origin",0.0],'
    'UNIT["Meter",1.0]]')

srs: SpatialReferenceSystem = SpatialReferenceSystem(
    name='WGS_1984_UTM_Zone_23N', organization='EPSG',
    org_coord_sys_id=32623, definition=SRS_WKT)
# using alias or comment for a Field will automatically enable the schema extension
fields: tuple[Field, ...] = (
    Field('road_id', FieldType.integer, alias='Road Identifier', comment='Unique identifier for the road'),
    Field('name', FieldType.text, size=100, alias='Road Name', comment='Name of the road'),
    Field('begin_easting', FieldType.double, alias='Begin Easting', comment='Easting in UTM Zone 23N'),
    Field('begin_northing', FieldType.double, alias='Begin Northing', comment='Northing in UTM Zone 23N'),
    Field('end_easting', FieldType.double, alias='End Easting', comment='Easting in UTM Zone 23N'),
    Field('end_northing', FieldType.double, alias='End Northing', comment='Northing in UTM Zone 23N'),
    Field('begin_longitude', FieldType.double, alias='Begin Longitude', comment='Longitude in WGS84'),
    Field('begin_latitude', FieldType.double, alias='Begin Latitude', comment='Latitude in WGS84'),
    Field('end_longitude', FieldType.double, alias='End Longitude', comment='Longitude in WGS84'),
    Field('end_latitude', FieldType.double, alias='End Latitude', comment='Latitude in WGS84'),
    Field('is_one_way', FieldType.boolean, alias='Is One Way', comment='True if road is one way'))

gpkg: GeoPackage = GeoPackage.create('../temp/test.gpkg')
fc: FeatureClass = gpkg.create_feature_class(
    'road_l', srs=srs, fields=fields, shape_type=ShapeType.linestring,
    m_enabled=True, overwrite=True, spatial_index=True)

About Spatial References For GeoPackages

Spatial References in GeoPackages can use any definition from any authority - be that EPSG, ESRI, or another authority. fudgeo imposes no restriction and performs no checks on the definitions provided. Take care to ensure that the definitions are compatible with the platform / software you intend to utilize with the GeoPackage.

Insert Features into a Feature Class (SQL)

Features can be inserted into a Feature Class using SQL.

This example shows the creation of a random point Feature Class and builds upon the code from previous examples. Note that the create Feature Class portion of the code is omitted.

from random import choice, randint
from string import ascii_uppercase, digits

from fudgeo import FeatureClass, GeoPackage
from fudgeo.context import ExecuteMany
from fudgeo.extension.ogr import has_ogr_contents
from fudgeo.geometry import LineStringM

# Generate some random points and attributes
rows: list[tuple[LineStringM, int, str, float, float, float, float, bool]] = []
for i in range(10000):
    name = ''.join(choice(ascii_uppercase + digits) for _ in range(10))
    road_id = randint(0, 1000)
    eastings = [randint(300000, 600000) for _ in range(20)]
    northings = [randint(1, 100000) for _ in range(20)]
    coords = [(x, y, m) for m, (x, y) in enumerate(zip(eastings, northings))]
    road = LineStringM(coords, srs_id=32623)
    rows.append((road, road_id, name, eastings[0], northings[0],
                 eastings[-1], northings[-1], False))

# NOTE Builds from previous examples
gpkg: GeoPackage = GeoPackage('../data/example.gpkg')
sql = """INSERT INTO road_l (SHAPE, road_id, name, begin_easting, begin_northing, 
                             end_easting, end_northing, is_one_way) 
         VALUES (?, ?, ?, ?, ?, ?, ?, ?)"""
fc = FeatureClass(geopackage=gpkg, name='road_l')
with gpkg.connection as conn:
    # NOTE for feature classes with OGC triggers use ExecuteMany  
    if has_ogr_contents(conn):
        with ExecuteMany(conn, table=fc) as executor:
            executor(sql=sql, data=rows)
    else:
        conn.executemany(sql, rows)

Geometry Examples

Review the tests for fudgeo for a comprehensive look into creating geometries, below are some examples showing the simplicity of this package.

from fudgeo.geometry import LineStringZM, Point, Polygon

# Point in WGS 84
pt: Point = Point(x=-119, y=34)

# Line with ZM Values for use with UTM Zone 23N (WGS 84)
coords: list[tuple[float, float, float, float]] = [
    (300000, 1, 10, 0), (300000, 4000000, 20, 1000),
    (700000, 4000000, 30, 2000), (700000, 1, 40, 3000)]
line: LineStringZM = LineStringZM(coords, srs_id=32623)

# list of rings where a ring is simply the list of points it contains.
rings: list[list[tuple[float, float]]] = [
    [(300000, 1), (300000, 4000000), (700000, 4000000), (700000, 1), (300000, 1)]]
poly: Polygon = Polygon(rings, srs_id=32623)

Select Features from GeoPackage

When selecting features from a GeoPackage feature class use SQL or use the helper method select.

For simple geometries (e.g. those without Z or M) this can be done via a basic SELECT statement or the select method.

from fudgeo import FeatureClass, GeoPackage
from fudgeo.geometry import Point

gpkg = GeoPackage(...)

# NOTE for fudgeo version v0.8.0 and above use helper method
fc = FeatureClass(geopackage=gpkg, name='point_fc')
cursor = fc.select(fields=('example_id',), include_geometry=True)
features: list[tuple[Point, int]] = cursor.fetchall()

# NOTE for fudgeo prior to v0.8.0
cursor = gpkg.connection.execute("""SELECT SHAPE, example_id FROM point_fc""")
features: list[tuple[Point, int]] = cursor.fetchall()

When using SQL with extended geometry types (e.g. those with Z and/or M) then ensure SQLite knows how to convert the geopackage stored geometry to a fudgeo geometry by including the converter, this is done like so:

from fudgeo import FeatureClass, GeoPackage
from fudgeo.geometry import LineStringM

gpkg = GeoPackage('../data/example.gpkg')
# NOTE for fudgeo version v0.8.0 and above use helper method
fc = FeatureClass(geopackage=gpkg, name='test')
cursor = fc.select(fields=('road_id',), include_geometry=True)
features: list[tuple[LineStringM, int]] = cursor.fetchall()

# NOTE for fudgeo prior to v0.8.0
cursor = gpkg.connection.execute(
    """SELECT SHAPE "[LineStringM]", road_id FROM test""")
features: list[tuple[LineStringM, int]] = cursor.fetchall()

Extensions

Spatial Index Extension

Spatial Index Extension implementation based on section [F.3. RTree Spatial Indexes](http://www.geopackage.org/spec131/index.html#

Related Skills

View on GitHub
GitHub Stars26
CategoryData
Updated25d ago
Forks3

Languages

Python

Security Score

95/100

Audited on Mar 3, 2026

No findings