Skip to main content

geocsv

PyPI version Python version support License: MIT Code style: Ruff

Turn any spreadsheet or CSV of places into a map-ready GeoJSON, GeoPackage, KML or GPX file, without GDAL.

📦 PyPI • 💻 GitHub • 🐛 Issues

You have a spreadsheet export with a column called Breitengrad, or enlem;boylam with decimal commas, or 40°26′46″N 79°58′56″W strings, or Plus Codes, or MGRS references. geocsv finds the coordinates, tells you how sure it is and why, reports the rows that are broken, and writes a file that QGIS, ArcGIS, Google Earth, a GPS or a web map can open. It is pure Python with no required dependencies.

📦 Installation

pip install geocsv

# optional extras
pip install "geocsv[pyproj]"     # reproject x/y or EWKT from any EPSG code
pip install "geocsv[shapefile]"  # write ESRI Shapefiles (via pyshp)

Python 3.9+ on Windows, macOS or Linux.

⚡ Quickstart

geocsv inspect places.csv                 # what was detected, and which rows are wrong
geocsv convert places.csv -o places.gpkg  # GeoPackage (also .geojson .geojsonl .kml .gpx .shp)
geocsv convert places.csv -o places.geojson --lat "Y koord" --lon "X koord"
import geocsv

table = geocsv.read("places.csv")
print(table.detection.describe())
# latlon: lat='Breitengrad', lon='Längengrad', decimal (confidence 0.99)
print(table.report())

table.to_gpkg("places.gpkg")
table.to_geojson("places.geojson")
fc = table.to_geojson()          # or get the FeatureCollection as a dict
table.to_kml("places.kml")
table.to_gpx("places.gpx")

geocsv.detect("places.csv")      # detection only, with .reasons and .alternatives

🔎 What it reads

CSV dialects (standard library only)

  • encodings: UTF-8, UTF-8 with BOM, UTF-16 (Excel "Unicode text"), and the Windows code pages cp1252 / latin-1 / cp1254 (Turkish letters such as ğ ı ş are told apart from Western European text)
  • delimiters , ; tab | (sniffed from the data, quoted fields respected) and Excel's sep=; first line
  • decimal commas (40,7128), header or no header (detected), ragged rows

Geometry encodings, found automatically:

kind example notes
latlon lat / lon columns header names in many languages: lat, latitude, latitud, Breitengrad, enlem, широта, lng, long, Längengrad, boylam, долгота, … plus value ranges when names are missing
latlon (DMS) 40°26′46″N, N40 26.767, 40d26m46s degrees, minutes, seconds, any symbols or letters
pair "41.0082, 28.9784", 40°26′46″N 79°58′56″W one column; lat/lon order inferred from ranges, hemisphere letters or a lonlat header
wkt POINT (29 41), SRID=3857;POLYGON ((…)) all simple-feature types, Z/M/ZM, EWKT
geojson {"type": "Point", "coordinates": [29, 41]} geometry or Feature objects
geohash sxk97cn4
pluscode 8GFJWVM5+9V Open Location Code; short codes with plus_code_reference
maidenhead KM69kw 2-10 characters
mgrs 35T PF 66370 41552 WGS 84, pure-Python Krüger series
utm 35T 666371 4541552 zone + band or hemisphere
xy easting / northing columns any EPSG code via the pyproj extra

Every detection has a confidence and human-readable reasons:

$ geocsv detect stations.csv
latlon: lat='lon', lon='lat', decimal (confidence 0.99)
  - header 'lat' looks like latitude but its values only fit longitude; the columns are swapped
  - 100% of values are coordinates; 100% are within latitude/longitude range
  - the two columns are adjacent

Override any decision: lat=/lon=, wkt=, column= + kind=, x=/y= + epsg=, order="lonlat", encoding=, delimiter=, header= (CLI: --lat, --lon, --wkt, --column, --kind, --x, --y, --epsg, --order, --encoding, --delimiter, --header/--no-header). Columns can be named or numbered from 1.

🩺 Validation report

$ geocsv inspect issues.csv
Geometry   latlon: lat='latitude', lon='longitude', decimal (confidence 0.87)
Valid      8 of 13 rows  bbox=(0.000000, 0.000000, 37.874600, 41.008200)

issue           severity   rows  lines
--------------  --------  -----  --------------------
missing         error         2  3, 4
invalid         error         1  5
out_of_range    error         1  6
swapped         error         1  9
likely_swapped  warning       1  13
null_island     warning       1  7
duplicate       warning       1  8
ragged          warning       1  14
code severity meaning
missing error empty geometry cell(s)
invalid error the value does not parse
empty error WKT/GeoJSON EMPTY geometry
out_of_range error outside ±90 / ±180
swapped error latitude beyond ±90 but would fit if swapped (fix_swapped=True repairs it)
crs error EWKT in another CRS and pyproj is not installed
likely_swapped warning in range, but far closer to the rest of the table when swapped
swapped_fixed warning swapped back (by fix_swapped=True or hemisphere letters)
null_island warning exactly (0, 0)
duplicate warning same geometry as an earlier row
ragged warning row length differs from the header

Line numbers are physical lines in the file, as your editor shows them. table.issues, table.summary() and geocsv inspect --json give the same data in machine-readable form; geocsv convert --strict exits with status 1 when any row has an error.

🗺️ What it writes

format method notes
GeoJSON to_geojson(path=None) RFC 7946 FeatureCollection; returns a dict without a path
GeoJSONSeq to_geojsonseq(path) one Feature per line (.geojsonl, .ndjson)
GeoPackage to_gpkg(path, layer=None) pure sqlite3; OGC GeoPackage 1.4 tables, StandardGeoPackageBinary + ISO WKB, any geometry type, Z
KML to_kml(path=None) Placemarks with ExtendedData, holes, MultiGeometry
GPX to_gpx(path=None) points as waypoints, lines as tracks
Shapefile to_shapefile(path) needs geocsv[shapefile]; one geometry family
any to_file(path, driver=None) picks the format from the extension

Attributes keep their types: integer, real or text (codes like "00123" stay text). A name-like column (name, title, isim, nombre, …) labels KML placemarks and GPX waypoints. The GeoPackage writer is tested against sqlite3, GDAL's ogrinfo, GDAL's validate_gpkg.py conformance checker and GeoPandas.

🧭 Coordinate helpers

The decoders are public and dependency-free:

from geocsv import dms, geohash, pluscode, maidenhead, utm

dms.parse_dms_pair("40°26′46″N 79°58′56″W")   # (40.4461, -79.9822)
dms.format_dms(40.446111, "lat")             # '40°26′46.0″N'
geohash.decode("u4pruydqqvj")                # (57.64911, 10.40744)
pluscode.decode("849VCWC8+R9")               # (37.4220625, -122.0840625)
pluscode.recover_nearest("CWC8+R9", 37.4, -122.0)
maidenhead.decode("FN31pr")                  # (41.7292, -72.7083)
utm.mgrs_to_latlon("31U DQ 48251 11932")     # (48.8582, 2.2945)
utm.latlon_to_mgrs(48.8582, 2.2945)          # '31U DQ 48251 11932'
utm.utm_to_latlon(31, "N", 448251, 5411932)

UTM/MGRS use Krüger's series to sixth order; they agree with PROJ to under a millimetre.

🛠️ Development

git clone https://github.com/YusufEminoglu/geocsv_sdk.git
cd geocsv_sdk
pip install -e ".[dev,pyproj,shapefile]"
python -m pytest --cov=geocsv
python -m ruff check . && python -m ruff format --check src tests && python -m mypy src

The tests include hand-made fixture CSVs, reference values for every decoder, a seeded synthetic corpus of 400 tables that measures detection accuracy, and GDAL-based checks of the GeoPackage output that run when GDAL is installed.

📄 License

MIT © Yusuf Eminoğlu

Metadata

Release files for geocsv 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for geocsv 0.1.0
File Size Uploaded Provenance Actions
geocsv-0.1.0.tar.gz 81.4 kB With provenance Details for geocsv-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for geocsv 0.1.0
File Interpreter ABI Platform Actions
geocsv-0.1.0-py3-none-any.whl Python 3 none any Details for geocsv-0.1.0-py3-none-any.whl

Total release size: 141.5 kB

Release files / geocsv-0.1.0.tar.gz

Download URL geocsv-0.1.0.tar.gz
Size 81.4 kB
Tags Source
SHA-256 checksum
How to use checksums
f332db5a64f78b5f8c71a6cd17df780cb3f3f569ad811144b1f03a5f94f8ba0d
BLAKE2b-256 checksum
How to use checksums
352b5f85a38352637e3c42446f41cbd3a8c05b3023cabbbbdd914cbb05bd4543
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 8, 2026.

Transparency log

Release files / geocsv-0.1.0-py3-none-any.whl

Download URL geocsv-0.1.0-py3-none-any.whl
Size 60.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
28d08032cc36bcef190659bb74e0fd6f761311dc9435a7ed015f1fc6da5a9de5
BLAKE2b-256 checksum
How to use checksums
1384a1437c964ae1d6bbb67b7db0f1165027470c8a7010a28edca2d4e3a42bfa
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 8, 2026.

Transparency log

Security for geocsv 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for geocsv-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 8, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page