Connor Sutton
commited on
Commit
·
a69a345
1
Parent(s):
6286500
fixed reversed lon/lat with KML uploads
Browse files
geospatial-data-converter/kml_tricks.py
CHANGED
@@ -5,6 +5,15 @@ import bs4
|
|
5 |
import geopandas as gpd
|
6 |
import lxml # nosec
|
7 |
import pandas as pd
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
|
9 |
|
10 |
def parse_descriptions_to_geodf(geodf: gpd.GeoDataFrame) -> gpd.GeoDataFrame:
|
@@ -41,6 +50,48 @@ def parse_descriptions_to_geodf(geodf: gpd.GeoDataFrame) -> gpd.GeoDataFrame:
|
|
41 |
return result_geodf
|
42 |
|
43 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
44 |
def load_kmz_as_geodf(file_path: str) -> gpd.GeoDataFrame:
|
45 |
"""Loads a KMZ file into a GeoPandas DataFrame, assuming the KMZ contains one KML file"""
|
46 |
|
@@ -67,7 +118,6 @@ def load_kmz_as_geodf(file_path: str) -> gpd.GeoDataFrame:
|
|
67 |
|
68 |
def load_ge_file(file_path: str) -> gpd.GeoDataFrame:
|
69 |
"""Loads a KML or KMZ file and parses its descriptions into a GeoDataFrame"""
|
70 |
-
|
71 |
if file_path.endswith(".kml"):
|
72 |
return parse_descriptions_to_geodf(
|
73 |
gpd.read_file(file_path, driver="KML", engine="pyogrio"),
|
@@ -134,7 +184,6 @@ def extract_kml_code_from_file(file_path: str) -> str:
|
|
134 |
|
135 |
def extract_data_from_ge_file(file_path: str) -> gpd.GeoDataFrame:
|
136 |
"""Extracts data from a Google Earth file (KML or KMZ) into a GeoDataFrame using SimpleData tags, excluding embedded tables in feature descriptions"""
|
137 |
-
|
138 |
data_df = extract_data_from_kml_code(extract_kml_code_from_file(file_path))
|
139 |
|
140 |
if file_path.endswith(".kmz"):
|
@@ -147,7 +196,7 @@ def extract_data_from_ge_file(file_path: str) -> gpd.GeoDataFrame:
|
|
147 |
geometry=ge_file_gdf["geometry"],
|
148 |
crs=ge_file_gdf.crs,
|
149 |
)
|
150 |
-
|
151 |
return geo_df
|
152 |
|
153 |
|
|
|
5 |
import geopandas as gpd
|
6 |
import lxml # nosec
|
7 |
import pandas as pd
|
8 |
+
from shapely.geometry import (
|
9 |
+
Point,
|
10 |
+
LineString,
|
11 |
+
Polygon,
|
12 |
+
MultiPoint,
|
13 |
+
MultiLineString,
|
14 |
+
MultiPolygon,
|
15 |
+
LinearRing,
|
16 |
+
)
|
17 |
|
18 |
|
19 |
def parse_descriptions_to_geodf(geodf: gpd.GeoDataFrame) -> gpd.GeoDataFrame:
|
|
|
50 |
return result_geodf
|
51 |
|
52 |
|
53 |
+
def swap_coordinates(geometry):
|
54 |
+
"""
|
55 |
+
Swap the latitude and longitude of Shapely Point, LineString, Polygon,
|
56 |
+
MultiPoint, MultiLineString, MultiPolygon, or LinearRing geometry.
|
57 |
+
|
58 |
+
Parameters:
|
59 |
+
- geometry: Shapely geometry (Point, LineString, Polygon, MultiPoint,
|
60 |
+
MultiLineString, MultiPolygon, or LinearRing)
|
61 |
+
|
62 |
+
Returns:
|
63 |
+
- Shapely geometry with swapped coordinates
|
64 |
+
"""
|
65 |
+
|
66 |
+
def swap_coords(coords):
|
67 |
+
return [(coord[1], coord[0]) for coord in coords]
|
68 |
+
|
69 |
+
if isinstance(geometry, Point):
|
70 |
+
return Point([geometry.y, geometry.x])
|
71 |
+
elif isinstance(geometry, MultiPoint):
|
72 |
+
return MultiPoint(
|
73 |
+
[Point(swap_coords(point.coords)) for point in geometry.geoms],
|
74 |
+
)
|
75 |
+
elif isinstance(geometry, LineString):
|
76 |
+
return LineString(swap_coords(geometry.coords))
|
77 |
+
elif isinstance(geometry, MultiLineString):
|
78 |
+
return MultiLineString(
|
79 |
+
[LineString(swap_coords(line.coords)) for line in geometry.geoms],
|
80 |
+
)
|
81 |
+
elif isinstance(geometry, Polygon):
|
82 |
+
exterior_coords = swap_coords(geometry.exterior.coords)
|
83 |
+
interior_coords = [
|
84 |
+
swap_coords(interior.coords) for interior in geometry.interiors
|
85 |
+
]
|
86 |
+
return Polygon(exterior_coords, interior_coords)
|
87 |
+
elif isinstance(geometry, MultiPolygon):
|
88 |
+
return MultiPolygon([swap_coordinates(poly) for poly in geometry.geoms])
|
89 |
+
elif isinstance(geometry, LinearRing):
|
90 |
+
return LinearRing(swap_coords(geometry.coords))
|
91 |
+
else:
|
92 |
+
raise ValueError("Unsupported geometry type")
|
93 |
+
|
94 |
+
|
95 |
def load_kmz_as_geodf(file_path: str) -> gpd.GeoDataFrame:
|
96 |
"""Loads a KMZ file into a GeoPandas DataFrame, assuming the KMZ contains one KML file"""
|
97 |
|
|
|
118 |
|
119 |
def load_ge_file(file_path: str) -> gpd.GeoDataFrame:
|
120 |
"""Loads a KML or KMZ file and parses its descriptions into a GeoDataFrame"""
|
|
|
121 |
if file_path.endswith(".kml"):
|
122 |
return parse_descriptions_to_geodf(
|
123 |
gpd.read_file(file_path, driver="KML", engine="pyogrio"),
|
|
|
184 |
|
185 |
def extract_data_from_ge_file(file_path: str) -> gpd.GeoDataFrame:
|
186 |
"""Extracts data from a Google Earth file (KML or KMZ) into a GeoDataFrame using SimpleData tags, excluding embedded tables in feature descriptions"""
|
|
|
187 |
data_df = extract_data_from_kml_code(extract_kml_code_from_file(file_path))
|
188 |
|
189 |
if file_path.endswith(".kmz"):
|
|
|
196 |
geometry=ge_file_gdf["geometry"],
|
197 |
crs=ge_file_gdf.crs,
|
198 |
)
|
199 |
+
geo_df["geometry"] = geo_df["geometry"].apply(swap_coordinates)
|
200 |
return geo_df
|
201 |
|
202 |
|
geospatial-data-converter/utils.py
CHANGED
@@ -3,7 +3,6 @@ import os
|
|
3 |
import zipfile
|
4 |
from tempfile import TemporaryDirectory
|
5 |
from typing import BinaryIO
|
6 |
-
|
7 |
import geopandas as gpd
|
8 |
|
9 |
from kml_tricks import load_ge_data
|
@@ -65,6 +64,9 @@ def convert(gdf: gpd.GeoDataFrame, output_name: str, output_format: str) -> byte
|
|
65 |
if output_format == "CSV":
|
66 |
gdf.to_csv(out_path)
|
67 |
else:
|
|
|
|
|
|
|
68 |
gdf.to_file(out_path, driver=output_format, engine="pyogrio")
|
69 |
|
70 |
if output_format in ("ESRI Shapefile", "OpenFileGDB"):
|
|
|
3 |
import zipfile
|
4 |
from tempfile import TemporaryDirectory
|
5 |
from typing import BinaryIO
|
|
|
6 |
import geopandas as gpd
|
7 |
|
8 |
from kml_tricks import load_ge_data
|
|
|
64 |
if output_format == "CSV":
|
65 |
gdf.to_csv(out_path)
|
66 |
else:
|
67 |
+
# if output_format == 'KML': # need to longitude and latitude columns
|
68 |
+
# # gdf = gdf.apply(lambda row: swap_coordinates(row), axis=1)
|
69 |
+
# gdf['geometry'] = gdf['geometry'].apply(swap_coordinates)
|
70 |
gdf.to_file(out_path, driver=output_format, engine="pyogrio")
|
71 |
|
72 |
if output_format in ("ESRI Shapefile", "OpenFileGDB"):
|