Fudgeo
Removing the fear-uncertainty-doubt from GeoPackages. OGC GeoPackage Support via Python and SQLite
Install / Use
/learn @realiii/FudgeoREADME
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
GeoPackageor open an existingGeoPackage - Create new
FeatureClassorTablewith optional overwrite - Create
SpatialReferenceSystemfor aFeatureClass - Build geometry objects from lists of coordinate values
- Work with data in
TableorFeatureClassin a normalSQLitemanner (e.g.SELECT,INSERT,UPDATE,DELETE) - Retrieve fields from a
FeatureClassorTable - Access primary key field of
FeatureClassorTable - Access geometry column name and geometry type for
FeatureClass - Add spatial index on
FeatureClass - Drop
FeatureClassorTable - 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
oracle
339.3kBest practices for using the oracle CLI (prompt + file bundling, engines, sessions, and file attachment patterns).
prose
339.3kOpenProse VM skill pack. Activate on any `prose` command, .prose files, or OpenProse mentions; orchestrates multi-agent workflows.
Command Development
83.9kThis skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
Plugin Structure
83.9kThis skill should be used when the user asks to "create a plugin", "scaffold a plugin", "understand plugin structure", "organize plugin components", "set up plugin.json", "use ${CLAUDE_PLUGIN_ROOT}", "add commands/agents/skills/hooks", "configure auto-discovery", or needs guidance on plugin directory layout, manifest configuration, component organization, file naming conventions, or Claude Code plugin architecture best practices.
