Add optional names to GeoSpace layers with get_layer(name) accessor - #339
Add optional names to GeoSpace layers with get_layer(name) accessor#339Tejasv-Singh wants to merge 1 commit into
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Team Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #339 +/- ##
==========================================
+ Coverage 78.32% 79.18% +0.86%
==========================================
Files 10 10
Lines 1024 1052 +28
Branches 168 176 +8
==========================================
+ Hits 802 833 +31
+ Misses 181 180 -1
+ Partials 41 39 -2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Both failing checks are pre-existing on main. Two upstream releases landed at once. build (ubuntu, 3.11) : mesa dropped Python 3.11 at 3.4.0, so on 3.11 pip can only Test GIS examples : affine 3.0.1 added a PendingDeprecationWarning on Neither is reachable from this PR, which only touches Happy to open follow-ups: dropping 3.11 from the CI matrix to match mesa's floor, and |
Description
Closes #324.
Adds optional name-based registration and retrieval of layers in
GeoSpace, so multi-layer models no longer need index-based workarounds to reach a specific layer.Usage
Previously the only way to reach a specific layer was by position, which is what the GIS examples do today:
That pattern appears in
UrbanGrowth,Rainfall, andPopulation.What changed
name: str | None = Noneparameter added to:RasterBase.__init__,RasterLayer.__init__,ImageLayer.__init__RasterLayer.from_file,ImageLayer.from_fileGeoSpace.add_layer(layer, name=None)GeoSpace.get_layer(name), which returns the registered layer or raisesKeyErrorlisting the available names.GeoSpace.to_crs(), in both theinplace=Trueandinplace=Falsepaths.GeoSpace._name_for_layer(layer)reverse lookup, used by the upcoming raster portrayal work.Why
GeoSpace.layersis a plain list with no name-based lookup, so any model with more than one layer has to track indices or hand-write an accessor property. The three GIS examples above each do this independently.This also unblocks a name-keyed raster portrayal API in a follow-up PR.
Backward compatibility
GeoSpace.layersstill returns a plainlist[ImageLayer | RasterLayer | gpd.GeoDataFrame], so indexing,len(), and iteration are unchanged.space.add_layer(layer)with no name behaves exactly as before and registers nothing.Nonedefault, so positional calls are unaffected.Design notes
GeoDataFrame layers are never mutated. The registry lives on
GeoSpace._layer_names. Vector layers are rawGeoDataFrameobjects, and stamping.nameon one would be routed by pandasNDFrame.__setattr__toself["name"] = value, silently overwriting a column named"name". That column is common in GIS data, so onlyRasterBaseinstances get the attribute set.Registration is explicit. Only layers added with an explicit
name=enter the registry. An auto-derived name would collide for two files sharing a basename, turning a previously workingadd_layercall into aValueError.from_filestays subclass-safe. The name is assigned after construction rather than forwarded intocls(...). Forwarding it would break anyRasterLayersubclass that overrides__init__with the existing signature and no**kwargs.Validation happens before mutation. Duplicate-name and duplicate-layer checks run before the in-place CRS conversion, so a rejected
add_layerleaves the caller's layer untouched rather than reprojecting it on the way to raising.Reverse lookup is type-guarded.
_name_for_layerfalls back togetattr(layer, "name", None), but aGeoDataFramewith a"name"column returns aSeriesfrom that attribute access, so the result is only used when it is astr.to_crs(inplace=False)builds new layer objects, so names are mapped across to the new instances rather than copied by reference.Tests
12 new tests in
tests/test_named_layers.py, covering the add/get round trip, unknown and duplicate names, preserved list semantics, name survival through bothto_crspaths, aGeoDataFramecarrying a"name"column, aRasterLayersubclass with the pre-existing__init__signature loading throughfrom_file, and rejection of the same layer object under a second name.Suite: 92 passed, 1 skipped (the skip is pre-existing and unrelated).