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"):
|