Skip to content

Python

PyPI Open in Colab

The rustgis package runs the RustGIS engine inside Python, headless. It opens data and projects, runs any command and geoprocessing tool, styles layers, renders maps and saves projects that the desktop app opens as they are. There is no app to install and no server to run: the engine is a native module built for Windows, macOS and Linux, for Python 3.12 and later.

pip install rustgis               # pip install "rustgis[geopandas]" for GeoDataFrames

A getting-started notebook walks through what follows; open it in Colab to try it without installing anything.

A first session

import rustgis

s = rustgis.Session(sample=True)                    # the World Atlas sample
s.layer_names()                                     # ['Cities', 'Rivers', 'Countries', …]
buffers = s.run_tool("buffer", input=s["Cities"], distance=100)
s["Countries"].style("graduated", field="pop_est")
s.zoom_to(buffers)
s.to_png("map.png", width=1600, height=900)
s.save("atlas.rgis")

In Jupyter, a session on the last line of a cell shows its map as a picture. For the app itself, use a Map.

Layers

s["Cities"] (or s.layer("Cities")) is a Layer: one layer of the active map. It is kept by its id, so it is still the same layer after a rename, and its properties read the session each time.

cities = s["Cities"]                                # by name (any case) or id
cities.count, cities.type, cities.fields, cities.bounds
"Cities" in s                                       # True
[layer.name for layer in s]                         # every layer, top first

cities.style("proportional", field="pop_max")       # layer.symbology; returns the layer
cities.visible = False
cities.set_transparency(30)
cities.filter("pop_max > 1000000")                  # a definition query; None clears it
cities.rename("Towns")
cities.zoom_to()
cities.remove()                                     # s.undo() brings it back

Selections work by SQL where clause:

n = cities.select("pop_max > 10000000")            # mode="add", "remove" or "subset"
cities.to_geojson(selected=True)                    # only the selected features
cities.clear_selection()

The app in a notebook

rustgis.Map shows the RustGIS app (ribbon, panes, map) in the cell, kept in step with its session: what Python adds or styles appears in the app, and what you do in the app (an edit, a new layer, a pan) is back in the session for Python to read. It needs anywidget, and works in JupyterLab, Notebook, VS Code and Colab.

pip install "rustgis[jupyter]"
m = rustgis.Map(sample=True)                         # height="800px" by default
m                                                   # the app, in the cell

m.add_geojson("sites.geojson", name="Sites")        # appears in the app
m["Sites"].style("unique", field="name")
m.set_center(10, 50, scale=5_000_000)
m.add_tiles("https://tile.openstreetmap.org/{z}/{x}/{y}.png", name="OpenStreetMap")

m.info()["center"]                                  # where the app's map is now
m.save("sites.rgis")                                # with what was done in the app
m.run_in_app("ui.language", code="de")              # a command only the app has

Map has the Session methods (run, run_tool, add_data, m["Sites"], to_png, ...), and what they change, its layers' changes too, shows in the app; m.session is the session itself. Files of up to 2 MB travel with the project, so the app shows them; a larger file is read from its path, which only Python can see, so it draws in to_png but not in the app. The whole project goes across after each change, so changes made on both sides at once don't merge: the later one wins. A change made in the app replaces Python's project, so m.undo() undoes only what Python did since.

The app is the web app, loaded from https://web.rustgis.app, so the notebook needs to be online. Map(url=...) or the RUSTGIS_WEB_URL environment variable points it at another copy (a self-hosted one, or trunk serve in apps/rustgis-web). Settings you change in the widget last until the cell is run again: an embedded app keeps nothing in the browser.

The project, with the data that travels with it, goes to that app through the browser (postMessage); nothing goes to a server. The app runs in your browser, so with the default address your data stays on your machine. Point url= only at a copy of the app you trust.

Commands

Every action in RustGIS is a command with an id and JSON parameters: the same ids the ribbon, the command line and the AI Assistant use. Session.run(id, ...) runs any of them, with the parameters as keyword arguments, a dict, or both. It returns the command's result and raises rustgis.RustGISError when the command fails.

s.commands()                                        # every command, with its parameters
s.tools()                                           # the geoprocessing tools
s.describe("layer.symbology")                       # one command's (or tool's) parameters
s.run("labels.field", layer="Cities", field="name")
s.run("map.goToXY", {"lon": 114.1, "lat": 22.4, "scale": 2_000_000})
s.notices()                                         # what the last commands reported
s.undo()

run_tool runs a geoprocessing tool and returns the layer it made (a list when it made several); layers can be given as Layer objects or by name:

s.describe("buffer")["params"]                      # input, distance, units, dissolve, output
rings = s.run_tool("buffer", input=s["Cities"], distance=50, units="Kilometers")
rings.style("single").set_transparency(50)

The common view commands have shortcuts too:

s.set_center(114.1, 22.4, scale=2_000_000)          # bearing= and pitch= tilt and turn it
s.zoom_to(s["Cities"])                              # or a box: (west, south, east, north)
s.bounds                                            # what the map shows now
s.set_basemap("paper")                              # paper, midnight, ocean, atlas, blank
s.add_tiles("https://tile.openstreetmap.org/{z}/{x}/{y}.png", name="OpenStreetMap")

The GeoLibre and Whitebox tools run as they do in the app, through the geolibre program (see Analysis). rustgis.load_plugins(), or Session(plugins=True), loads the plugins installed for the desktop app.

Data in and out

s = rustgis.Session(data=["parcels.gpkg", "roads.fgb"])
roads = s.add_data("roads.fgb", name="Roads")      # each add_* returns its Layer
sites = s.add_geojson({"type": "FeatureCollection", "features": [...]}, name="Sites")

gdf = s["Cities"].to_geopandas()                    # a GeoDataFrame in EPSG:4326
big = s.add_geopandas(gdf[gdf.pop_max > 5_000_000], name="Big cities")
s["Cities"].to_geojson()                            # a FeatureCollection dict
big.to_file("big-cities.gpkg", crs="EPSG:3857")     # GeoJSON, CSV, FlatGeobuf, GeoParquet, …

add_geojson also takes any object with __geo_interface__ (a Shapely geometry, a GeoSeries).

Every format the app reads works here: GeoJSON, CSV, Shapefile, GeoPackage, GeoParquet, FlatGeobuf, KML/KMZ, GPX, GeoTIFF and the rest of Supported Data Formats. A large file read in place keeps only the features of the last view in memory; run layer.loadAll on it before to_geojson or to_geopandas to get all of them.

From source

The package lives in apps/rustgis-python, built with maturin:

pip install ./apps/rustgis-python                   # or: cd apps/rustgis-python && maturin develop
python -m pytest apps/rustgis-python/tests

Its version is the RustGIS version, and each release publishes wheels to PyPI.