Spaces:
Running
Running
Guilherme Silberfarb Costa commited on
Commit ·
02cb858
1
Parent(s): 4970cc0
Use pyshp fallback for portable shapefiles
Browse files
backend/app/core/shapefile_runtime.py
CHANGED
|
@@ -30,17 +30,52 @@ def _build_transformer(source_crs: Any, target_crs: str | None):
|
|
| 30 |
return None
|
| 31 |
|
| 32 |
|
| 33 |
-
def
|
| 34 |
-
import
|
| 35 |
from shapely.geometry import shape
|
| 36 |
from shapely.ops import transform as shapely_transform
|
| 37 |
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
|
| 42 |
-
|
| 43 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
transformer = _build_transformer(source.crs_wkt or source.crs, target_crs)
|
| 45 |
for feature in source:
|
| 46 |
properties = dict(feature.get("properties") or {})
|
|
@@ -50,8 +85,35 @@ def load_vector_dataframe(shapefile_path: str | Path, *, target_crs: str | None
|
|
| 50 |
geometry = shape(geometry_raw)
|
| 51 |
if transformer is not None:
|
| 52 |
geometry = shapely_transform(transformer.transform, geometry)
|
| 53 |
-
properties
|
| 54 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
|
| 56 |
return pd.DataFrame(rows)
|
| 57 |
|
|
@@ -63,9 +125,7 @@ def load_vector_geojson(
|
|
| 63 |
property_fields: tuple[str, ...] | list[str] | None = None,
|
| 64 |
simplify_tolerance: float = 0.0,
|
| 65 |
) -> dict[str, Any]:
|
| 66 |
-
import
|
| 67 |
-
from shapely.geometry import mapping, shape
|
| 68 |
-
from shapely.ops import transform as shapely_transform
|
| 69 |
|
| 70 |
path = Path(shapefile_path).expanduser().resolve()
|
| 71 |
if not path.exists():
|
|
@@ -77,31 +137,24 @@ def load_vector_geojson(
|
|
| 77 |
}
|
| 78 |
|
| 79 |
wanted_fields = tuple(str(field).strip() for field in (property_fields or ()) if str(field).strip())
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
if
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
feature_collection["features"].append(
|
| 100 |
-
{
|
| 101 |
-
"type": "Feature",
|
| 102 |
-
"properties": properties,
|
| 103 |
-
"geometry": mapping(geometry),
|
| 104 |
-
}
|
| 105 |
-
)
|
| 106 |
|
| 107 |
return feature_collection
|
|
|
|
| 30 |
return None
|
| 31 |
|
| 32 |
|
| 33 |
+
def _iter_features_pyshp(shapefile_path: Path, *, target_crs: str | None) -> list[tuple[dict[str, Any], Any]]:
|
| 34 |
+
import shapefile
|
| 35 |
from shapely.geometry import shape
|
| 36 |
from shapely.ops import transform as shapely_transform
|
| 37 |
|
| 38 |
+
reader = shapefile.Reader(str(shapefile_path))
|
| 39 |
+
try:
|
| 40 |
+
field_names = [str(field[0]) for field in reader.fields[1:]]
|
| 41 |
+
source_crs = None
|
| 42 |
+
prj_path = shapefile_path.with_suffix(".prj")
|
| 43 |
+
if prj_path.exists():
|
| 44 |
+
try:
|
| 45 |
+
source_crs = prj_path.read_text(encoding="utf-8", errors="replace")
|
| 46 |
+
except Exception:
|
| 47 |
+
source_crs = prj_path.read_text(errors="replace")
|
| 48 |
+
transformer = _build_transformer(source_crs, target_crs)
|
| 49 |
+
|
| 50 |
+
features: list[tuple[dict[str, Any], Any]] = []
|
| 51 |
+
for shape_record in reader.iterShapeRecords():
|
| 52 |
+
values = list(shape_record.record)
|
| 53 |
+
properties = {
|
| 54 |
+
field_name: values[index] if index < len(values) else None
|
| 55 |
+
for index, field_name in enumerate(field_names)
|
| 56 |
+
}
|
| 57 |
+
geometry_raw = getattr(shape_record.shape, "__geo_interface__", None)
|
| 58 |
+
geometry = None
|
| 59 |
+
if geometry_raw and geometry_raw.get("coordinates"):
|
| 60 |
+
geometry = shape(geometry_raw)
|
| 61 |
+
if transformer is not None:
|
| 62 |
+
geometry = shapely_transform(transformer.transform, geometry)
|
| 63 |
+
features.append((properties, geometry))
|
| 64 |
+
return features
|
| 65 |
+
finally:
|
| 66 |
+
try:
|
| 67 |
+
reader.close()
|
| 68 |
+
except Exception:
|
| 69 |
+
pass
|
| 70 |
|
| 71 |
+
|
| 72 |
+
def _iter_features_fiona(shapefile_path: Path, *, target_crs: str | None) -> list[tuple[dict[str, Any], Any]]:
|
| 73 |
+
import fiona
|
| 74 |
+
from shapely.geometry import shape
|
| 75 |
+
from shapely.ops import transform as shapely_transform
|
| 76 |
+
|
| 77 |
+
features: list[tuple[dict[str, Any], Any]] = []
|
| 78 |
+
with fiona.open(shapefile_path) as source:
|
| 79 |
transformer = _build_transformer(source.crs_wkt or source.crs, target_crs)
|
| 80 |
for feature in source:
|
| 81 |
properties = dict(feature.get("properties") or {})
|
|
|
|
| 85 |
geometry = shape(geometry_raw)
|
| 86 |
if transformer is not None:
|
| 87 |
geometry = shapely_transform(transformer.transform, geometry)
|
| 88 |
+
features.append((properties, geometry))
|
| 89 |
+
return features
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
def _load_features(shapefile_path: Path, *, target_crs: str | None = "EPSG:4326") -> list[tuple[dict[str, Any], Any]]:
|
| 93 |
+
pyshp_error = None
|
| 94 |
+
try:
|
| 95 |
+
return _iter_features_pyshp(shapefile_path, target_crs=target_crs)
|
| 96 |
+
except Exception as exc:
|
| 97 |
+
pyshp_error = exc
|
| 98 |
+
|
| 99 |
+
try:
|
| 100 |
+
return _iter_features_fiona(shapefile_path, target_crs=target_crs)
|
| 101 |
+
except Exception:
|
| 102 |
+
if pyshp_error is not None:
|
| 103 |
+
raise pyshp_error
|
| 104 |
+
raise
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
def load_vector_dataframe(shapefile_path: str | Path, *, target_crs: str | None = "EPSG:4326") -> pd.DataFrame:
|
| 108 |
+
path = Path(shapefile_path).expanduser().resolve()
|
| 109 |
+
if not path.exists():
|
| 110 |
+
raise FileNotFoundError(f"Shapefile nao encontrado: {path}")
|
| 111 |
+
|
| 112 |
+
rows: list[dict[str, Any]] = []
|
| 113 |
+
for properties, geometry in _load_features(path, target_crs=target_crs):
|
| 114 |
+
row = dict(properties)
|
| 115 |
+
row["geometry"] = geometry
|
| 116 |
+
rows.append(row)
|
| 117 |
|
| 118 |
return pd.DataFrame(rows)
|
| 119 |
|
|
|
|
| 125 |
property_fields: tuple[str, ...] | list[str] | None = None,
|
| 126 |
simplify_tolerance: float = 0.0,
|
| 127 |
) -> dict[str, Any]:
|
| 128 |
+
from shapely.geometry import mapping
|
|
|
|
|
|
|
| 129 |
|
| 130 |
path = Path(shapefile_path).expanduser().resolve()
|
| 131 |
if not path.exists():
|
|
|
|
| 137 |
}
|
| 138 |
|
| 139 |
wanted_fields = tuple(str(field).strip() for field in (property_fields or ()) if str(field).strip())
|
| 140 |
+
for properties_raw, geometry in _load_features(path, target_crs=target_crs):
|
| 141 |
+
if geometry is None:
|
| 142 |
+
continue
|
| 143 |
+
|
| 144 |
+
if simplify_tolerance:
|
| 145 |
+
geometry = geometry.simplify(float(simplify_tolerance), preserve_topology=True)
|
| 146 |
+
|
| 147 |
+
properties = (
|
| 148 |
+
{field: properties_raw.get(field) for field in wanted_fields if field in properties_raw}
|
| 149 |
+
if wanted_fields
|
| 150 |
+
else dict(properties_raw)
|
| 151 |
+
)
|
| 152 |
+
feature_collection["features"].append(
|
| 153 |
+
{
|
| 154 |
+
"type": "Feature",
|
| 155 |
+
"properties": properties,
|
| 156 |
+
"geometry": mapping(geometry),
|
| 157 |
+
}
|
| 158 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 159 |
|
| 160 |
return feature_collection
|
backend/requirements.txt
CHANGED
|
@@ -12,6 +12,7 @@ folium
|
|
| 12 |
branca
|
| 13 |
joblib
|
| 14 |
openpyxl
|
|
|
|
| 15 |
geopandas
|
| 16 |
fiona
|
| 17 |
gradio>=4.0
|
|
|
|
| 12 |
branca
|
| 13 |
joblib
|
| 14 |
openpyxl
|
| 15 |
+
pyshp
|
| 16 |
geopandas
|
| 17 |
fiona
|
| 18 |
gradio>=4.0
|
build/windows/mesa_frame_portable.spec
CHANGED
|
@@ -31,6 +31,7 @@ hiddenimports += collect_submodules("app")
|
|
| 31 |
hiddenimports += collect_submodules("fiona")
|
| 32 |
hiddenimports += collect_submodules("pyproj")
|
| 33 |
hiddenimports += collect_submodules("shapely")
|
|
|
|
| 34 |
hiddenimports += [
|
| 35 |
"uvicorn.loops.auto",
|
| 36 |
"uvicorn.protocols.http.auto",
|
|
|
|
| 31 |
hiddenimports += collect_submodules("fiona")
|
| 32 |
hiddenimports += collect_submodules("pyproj")
|
| 33 |
hiddenimports += collect_submodules("shapely")
|
| 34 |
+
hiddenimports += collect_submodules("shapefile")
|
| 35 |
hiddenimports += [
|
| 36 |
"uvicorn.loops.auto",
|
| 37 |
"uvicorn.protocols.http.auto",
|