|
23 | 23 |
|
24 | 24 |
|
25 | 25 |
|
| 26 | +# def qgsLayerToGeoDataFrame(layer) -> gpd.GeoDataFrame: |
| 27 | +# if layer is None: |
| 28 | +# return None |
| 29 | +# features = layer.getFeatures() |
| 30 | +# fields = layer.fields() |
| 31 | +# data = {'geometry': []} |
| 32 | +# for f in fields: |
| 33 | +# data[f.name()] = [] |
| 34 | +# for feature in features: |
| 35 | +# geom = feature.geometry() |
| 36 | +# if geom.isEmpty(): |
| 37 | +# continue |
| 38 | +# data['geometry'].append(geom) |
| 39 | +# for f in fields: |
| 40 | +# data[f.name()].append(feature[f.name()]) |
| 41 | +# return gpd.GeoDataFrame(data, crs=layer.crs().authid()) |
| 42 | + |
26 | 43 | def qgsLayerToGeoDataFrame(layer) -> gpd.GeoDataFrame: |
| 44 | + """ |
| 45 | + Convert a QgsVectorLayer to a GeoDataFrame with: |
| 46 | + - Shapely geometries |
| 47 | + - Pandas nullable string dtype for QGIS string fields |
| 48 | + """ |
27 | 49 | if layer is None: |
28 | 50 | return None |
29 | | - features = layer.getFeatures() |
| 51 | + |
30 | 52 | fields = layer.fields() |
31 | | - data = {'geometry': []} |
32 | | - for f in fields: |
33 | | - data[f.name()] = [] |
34 | | - for feature in features: |
35 | | - geom = feature.geometry() |
| 53 | + string_field_names = { |
| 54 | + f.name() for f in fields |
| 55 | + if f.type() in (QVariant.String,) # extend here if you use other text types |
| 56 | + } |
| 57 | + |
| 58 | + rows = [] |
| 59 | + for feat in layer.getFeatures(): |
| 60 | + geom = feat.geometry() |
36 | 61 | if geom.isEmpty(): |
37 | 62 | continue |
38 | | - data['geometry'].append(geom) |
| 63 | + |
| 64 | + row = {"geometry": wkb_loads(geom.asWkb())} |
39 | 65 | for f in fields: |
40 | | - data[f.name()].append(feature[f.name()]) |
41 | | - return gpd.GeoDataFrame(data, crs=layer.crs().authid()) |
| 66 | + val = feat[f.name()] |
| 67 | + # Normalize None in string cols to pandas.NA so StringDtype works cleanly |
| 68 | + if f.name() in string_field_names: |
| 69 | + row[f.name()] = pd.NA if val is None else str(val) |
| 70 | + else: |
| 71 | + row[f.name()] = val |
| 72 | + rows.append(row) |
| 73 | + |
| 74 | + if not rows: |
| 75 | + # Empty GeoDataFrame with correct schema & crs |
| 76 | + gdf = gpd.GeoDataFrame(columns=["geometry"] + [f.name() for f in fields], |
| 77 | + geometry="geometry", |
| 78 | + crs=layer.crs().authid()) |
| 79 | + else: |
| 80 | + gdf = gpd.GeoDataFrame(rows, geometry="geometry", crs=layer.crs().authid()) |
| 81 | + |
| 82 | + # Enforce pandas' nullable string dtype on QGIS string fields |
| 83 | + for name in string_field_names: |
| 84 | + if name in gdf.columns: |
| 85 | + gdf[name] = gdf[name].astype("string") |
| 86 | + |
| 87 | + return gdf |
42 | 88 |
|
43 | 89 |
|
44 | 90 | def qgsLayerToDataFrame(layer, dtm) -> pd.DataFrame: |
|
0 commit comments