Terminal-native GIS tool for vector data
Project description
geo-tool
Terminal-native GIS tool for vector data. No QGIS, no GUI, no bloat.
Inspect, convert, reproject, filter, and visualize geospatial datasets entirely from the command line. Renders maps as Unicode braille characters directly in your terminal.
Supported Formats
| Format | Read | Write |
|---|---|---|
| Shapefile | Yes | Yes |
| GeoPackage | Yes | Yes |
| GeoJSON | Yes | Yes |
| FlatGeobuf | Yes | Yes |
| TopoJSON | Yes | No |
Installation
Requires Python 3.12+ and uv.
git clone <repo-url>
cd "GIS Terminal"
uv sync
This installs geo-tool as a CLI tool in the project's virtual environment. Run it with:
# Activate the venv first
# Linux/macOS:
source .venv/bin/activate
# Windows:
.venv\Scripts\activate
# Then use geo-tool directly
geo-tool --help
Or run without activating:
# Linux/macOS:
.venv/bin/geo-tool --help
# Windows:
.venv\Scripts\geo-tool.exe --help
Dependencies
Four runtime dependencies, all pulling from GDAL/OGR, PROJ, and GEOS under the hood:
- click -- CLI framework
- fiona -- vector I/O (GDAL/OGR bindings with streaming support)
- shapely -- geometry operations (GEOS bindings)
- pyproj -- CRS transformations (PROJ bindings)
Commands
geo-tool info -- Inspect metadata
geo-tool info countries.shp
File: countries.shp
Driver: ESRI Shapefile
CRS: EPSG:4326
Geometry: Polygon
Features: 177
Extent: -180.000000, -90.000000, 180.000000, 83.645130
Fields:
NAME str
POP_EST int64
GDP_MD float
ISO_A3 str
Machine-readable output for scripting:
geo-tool --json info countries.shp
{
"path": "countries.shp",
"driver": "ESRI Shapefile",
"crs": "EPSG:4326",
"geometry_type": "Polygon",
"feature_count": 177,
"bounds": [-180.0, -90.0, 180.0, 83.64513],
"fields": [
{"name": "NAME", "type": "str"},
{"name": "POP_EST", "type": "int64"}
]
}
For multi-layer formats (GeoPackage):
geo-tool info data.gpkg --layer boundaries
geo-tool convert -- Format conversion
Convert between any supported writable formats. The output format is detected from the file extension.
# Shapefile to GeoPackage
geo-tool convert input.shp output.gpkg
# GeoJSON to FlatGeobuf
geo-tool convert input.geojson output.fgb
# Convert and reproject in one step
geo-tool convert input.shp output.geojson --crs EPSG:4326
geo-tool reproject -- Change coordinate reference system
# WGS84 to Web Mercator
geo-tool reproject input.geojson output.geojson --crs EPSG:3857
# To a UTM zone
geo-tool reproject input.shp output.gpkg --crs EPSG:32633
The --crs flag accepts any string that pyproj understands: EPSG:XXXX, WKT, or PROJ strings.
geo-tool filter -- Spatial and attribute filtering
Filter by bounding box (west, south, east, north):
geo-tool filter countries.gpkg --bbox -10,35,30,60
Filter by attribute expression (OGR SQL syntax):
geo-tool filter countries.gpkg --where "POP_EST > 50000000"
Combine both:
geo-tool filter countries.gpkg --bbox -10,35,30,60 --where "POP_EST > 10000000"
Write results to a file instead of stdout:
geo-tool filter countries.gpkg --bbox 0,0,10,10 -o subset.geojson
When no -o is given, GeoJSON is written to stdout -- pipe it wherever you want:
geo-tool filter data.gpkg --where "type = 'highway'" | jq '.features | length'
geo-tool view -- Terminal map preview
Renders geometries as Unicode braille characters directly in your terminal.
geo-tool view countries.shp
Interactive controls:
| Key | Action |
|---|---|
+ |
Zoom in |
- |
Zoom out |
| Arrows | Pan |
r |
Reset view |
q |
Quit |
For non-interactive (pipe-friendly) output:
geo-tool view countries.shp --static
Restrict the initial view to a bounding box:
geo-tool view countries.shp --bbox -20,30,45,72
How it works: Each terminal character cell is a 2x4 braille dot pattern (Unicode block U+2800). This gives 8x the pixel resolution of regular characters. An 80-column terminal renders at 160x84 effective pixels. A 200-column terminal reaches 400x192 pixels -- enough to recognize coastlines, boundaries, and road networks.
Geometries are rasterized using Bresenham's line algorithm for lines and polygon outlines. Points are drawn as small crosses. For large datasets (>10,000 features), the renderer samples features and displays a notice.
geo-tool serve -- Browser map
Opens the dataset on a Leaflet map in your browser. No external server or build step required.
geo-tool serve countries.gpkg
Serving countries.gpkg at http://localhost:8080
Press Ctrl+C to stop.
Options:
# Custom port
geo-tool serve data.geojson --port 3000
# Don't auto-open the browser
geo-tool serve data.geojson --no-open
The server uses Python's built-in http.server with Leaflet loaded from CDN. Features are spatially filtered on each map pan/zoom -- only the visible extent is sent to the browser (max 5,000 features per request). Click any feature to see its attributes in a popup.
Global Options
These go before the subcommand:
geo-tool --json info data.shp # JSON output
geo-tool -q convert in.shp out.gpkg # Suppress status messages
geo-tool --version # Show version
| Flag | Effect |
|---|---|
--json |
Machine-readable JSON output |
-q/--quiet |
Suppress non-essential stderr output |
-v/--verbose |
Debug-level logging |
--version |
Print version and exit |
Piping and Scripting
geo-tool is designed to be Unix-pipe friendly. Combine commands:
# Get the CRS of a file
geo-tool --json info data.shp | python -m json.tool
# Filter then inspect the result
geo-tool filter big.gpkg --bbox 0,0,10,10 -o subset.geojson && geo-tool info subset.geojson
# Count features matching a query
geo-tool --json filter data.gpkg --where "pop > 1000000" | python -c "
import json, sys
fc = json.load(sys.stdin)
print(len(fc['features']))
"
When stdout is not a TTY (i.e., output is piped), geo-tool view automatically uses static mode.
Project Structure
src/geo/
cli.py Entry point, Click command group
formats.py File extension to OGR driver mapping
output.py Human-readable and JSON formatters
errors.py Exception hierarchy
commands/
info.py geo-tool info
convert.py geo-tool convert
reproject.py geo-tool reproject
filter.py geo-tool filter
view.py geo-tool view
serve.py geo-tool serve
core/
reader.py Dataset opening via Fiona
writer.py Dataset writing via Fiona
inspector.py Metadata extraction
transform.py CRS reprojection via pyproj
query.py Spatial/attribute filtering via OGR
render/
canvas.py Boolean pixel grid
braille.py Pixel-to-braille encoding
viewport.py Geographic-to-pixel coordinate transform
rasterizer.py Bresenham lines, geometry rasterization
display.py Interactive terminal loop
server/
app.py HTTP server startup
handler.py Request routing and feature API
templates.py Leaflet HTML template
Design Decisions
- Click over Typer -- explicit argument handling, no type-hint magic
- Fiona over pyogrio -- streaming iteration instead of bulk memory load
- Braille over block characters -- 8x pixel resolution per character cell
- Leaflet over MapLibre -- 40KB from CDN, zero build step, native GeoJSON support
- No rich/tabulate -- the tool has ~3 tables total;
str.ljust()is enough always_xy=Truein pyproj -- prevents silent lon/lat axis swap that corrupts datasrc/package layout -- PyPA standard, prevents accidental local imports
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file gis_terminal-0.2.0.tar.gz.
File metadata
- Download URL: gis_terminal-0.2.0.tar.gz
- Upload date:
- Size: 6.3 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7dbe3cc2021df9247f22601cbba909c9b61e6735bd9b6c2a930afc0d515a9f84
|
|
| MD5 |
075939a0e474e223cc48f0449647a141
|
|
| BLAKE2b-256 |
e1bbbb2c7758a35e066f3526196f0d2beb49e786637f512b5e451bf4c4280782
|
File details
Details for the file gis_terminal-0.2.0-py3-none-any.whl.
File metadata
- Download URL: gis_terminal-0.2.0-py3-none-any.whl
- Upload date:
- Size: 23.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
32f8e4b7ae7ef7e96de149873a1da002a6fb2c99b4b2857c53eea6eef403a007
|
|
| MD5 |
06c7034c69c0988255fd762717d71cb6
|
|
| BLAKE2b-256 |
6a3d46122faa97da1ca4327844519e8bf1405092d15462f54ab3c027cd6da2e5
|