`render_shapes` with datashader fails with `KeyError: 'global'` on non-"global" coordinate systems with geopandas 1.2
還沒有人認領這個 Issue。
評估
- 難度
- 2/5
- 預估耗時
- 1-3 小時
- 新手友好度
- 72/100
- Issue 類型
- 缺陷
- 描述清晰度
- 描述清楚
- 活躍度
- 活躍
- 技術堆疊
- python
研究方向
從 spatialdata_plot/pl/render.py 中 _render_shapes 的 datashader 分支開始,那裡用 ShapesModel.parse 建構 transformed_element。在非全域座標系上,使用 geopandas 1.2.0 和 1.1.4 執行 issue 中的 12,000 個 shape 重現腳本。當兩個版本都能渲染、複製而來的 transform 屬性已移除,並且明確設定了 "global" 恆等變換時,即完成。
由索引模型根據 Issue 內容生成。
描述
With geopandas 1.2.0, render_shapes() fails with KeyError: 'global' when the element has more than 10,000 shapes (so the datashader method is used) and the element is not in the "global" coordinate system.
The same code works with geopandas 1.1.4.
Minimal example
import geopandas as gpd
import matplotlib.pyplot as plt
import numpy as np
import shapely
import spatialdata as sd
import spatialdata_plot # noqa: F401
from spatialdata.models import ShapesModel
from spatialdata.transformations import Identity
# More than 10,000 shapes so that render_shapes picks the datashader method
rng = np.random.default_rng(0)
xy = rng.uniform(0, 1000, size=(12_000, 2))
shapes = gpd.GeoDataFrame(geometry=[shapely.box(x, y, x + 2, y + 2) for x, y in xy])
# The element is only in a coordinate system that is not "global"
sdata = sd.SpatialData(
shapes={"squares": ShapesModel.parse(shapes, transformations={"sample": Identity()})}
)
fig, ax = plt.subplots()
sdata.pl.render_shapes("squares").pl.show(ax=ax, coordinate_systems="sample")
With geopandas==1.2.0:
File ".../spatialdata_plot/pl/render.py", line 910, in _render_shapes
plot_width, plot_height, x_ext, y_ext, factor = _get_extent_and_range_for_datashader_canvas(
transformed_element, "global", fig_params
)
File ".../spatialdata_plot/pl/_datashader.py", line 830, in _get_extent_and_range_for_datashader_canvas
extent = _fast_extent(spatial_element, coordinate_system)
File ".../spatialdata_plot/pl/utils.py", line 1678, in _fast_extent
return _element_extent_fast(element, coordinate_system) or get_extent(element, coordinate_system=coordinate_system)
File ".../spatialdata_plot/pl/utils.py", line 1585, in _element_extent_fast
matrix = transformations[coordinate_system].to_affine_matrix(("x", "y"), ("x", "y"))
KeyError: 'global'
With geopandas<1.2 (1.1.4) the plot renders correctly.
With method="matplotlib" it also renders correctly.
Cause
In the datashader path of _render_shapes, the geometry is transformed into the target coordinate system and a new element is made:
transformed_element = ShapesModel.parse(
gpd.GeoDataFrame(
data=shapes.drop("geometry", axis=1),
geometry=transformed_geometry,
)
)
The extent is then computed in a hard-coded "global" coordinate system.
This relies on ShapesModel.parse adding the default {"global": Identity()} transformation.
shapes.drop(...) keeps DataFrame.attrs, which hold the original transform.
geopandas 1.1.4 dropped these attrs in the GeoDataFrame constructor, but geopandas 1.2.0 keeps them:
import geopandas as gpd, shapely
g = gpd.GeoDataFrame({"a": [1]}, geometry=[shapely.Point(0, 0)])
g.attrs["transform"] = {"sample": 1}
print(gpd.GeoDataFrame(data=g.drop("geometry", axis=1), geometry=g["geometry"]).attrs)
# geopandas 1.1.4: {}
# geopandas 1.2.0: {'transform': {'sample': 1}}
So with geopandas 1.2.0 ShapesModel.parse keeps the original transformations (here only "sample"), no "global" transformation is added, and the lookup fails.
Possible fix
The geometry is already in the target coordinate system, so the copied transform attrs can be removed before parsing.
Just passing transformations={"global": Identity()} to ShapesModel.parse is not sufficient. With geopandas 1.2.0 it raises ValueError: Transformations are both specified for the element and also passed as an argument to the parser.
This change makes the example above work with both geopandas 1.1.4 and 1.2.0:
- transformed_element = ShapesModel.parse(
- gpd.GeoDataFrame(
- data=shapes.drop("geometry", axis=1),
- geometry=transformed_geometry,
- )
- )
+ transformed_gdf = gpd.GeoDataFrame(
+ data=shapes.drop("geometry", axis=1),
+ geometry=transformed_geometry,
+ )
+ # geometry is already in the target coordinate system; drop any transform
+ # carried over in attrs (geopandas >= 1.2 propagates DataFrame.attrs)
+ transformed_gdf.attrs.pop("transform", None)
+ transformed_element = ShapesModel.parse(transformed_gdf, transformations={"global": Identity()})
Versions
spatialdata-plot 0.4.2
geopandas 1.2.0
spatialdata 0.8.0
pandas 3.0.6
shapely 2.2.0
datashader 0.19.1
numpy 2.5.3
Python 3.13.5
OS macOS-26.7.1-arm64
- 主要語言
- Python
- 星號
- 86
- 分支
- 22
- 平均合併
- 4 天 5 小時
- 30 天內合併 PR
- 5
環境準備
- 沒有 Dockerfile 或 Docker Compose 檔案
- 沒有 Pull Request 範本
- 閱讀貢獻指南
從這裡開始
- 先讀完整個 Issue,再讀專案的貢獻指南。
- 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
- Fork 儲存庫,在一個分支上完成修改。
- 送出 Pull Request,並在描述裡引用這個 Issue 編號。
scverse/spatialdata-plot 的其他 Issue
-
難度 4/5 3-5 天 新手友好度 48/100
scverse/spatialdata-plot#747 ·
-
難度 5/5 一週以上 新手友好度 45/100
scverse/spatialdata-plot#677 ·
-
enhancement priority: medium utils :wrench:
難度 3/5 1-2 天 新手友好度 68/100
scverse/spatialdata-plot#632 ·
-
難度 4/5 3-5 天 新手友好度 45/100
scverse/spatialdata-plot#534 · 1 則留言 ·
-
難度 4/5 3-5 天 新手友好度 30/100
scverse/spatialdata-plot#476 ·
查看 scverse/spatialdata-plot 的全部 Issue
相似的 Issue
-
難度 1/5 1 小時以內 新手友好度 82/100
esphome/esphome.io#7657 ·
維護者通常 1 天內回覆
-
comp/gateway comp/plugins duplicate P3 type/bug
難度 2/5 1-3 小時 新手友好度 82/100
NousResearch/hermes-agent#137019 ·
維護者通常 1 天內回覆
-
bug
難度 2/5 1-3 小時 新手友好度 72/100
維護者通常 1 天內回覆
-
難度 2/5 1-3 小時 新手友好度 67/100
NVIDIA/structured-data-models#1091 ·
維護者通常 1 天內回覆
-
[work-item] adam-adae-worsening-emergence: conform spec.yaml to the specification style contract (REQ-1280)可能已有人在做 @muse-yamaa-bot 今天認領。 未關閉work-item
難度 1/5 1-3 小時 新手友好度 82/100
維護者通常 1 天內回覆