Skip to content

Commit

Permalink
fix: inform user if GeoJSON CRS is not EPSG:4326 (#919)
Browse files Browse the repository at this point in the history
* fix: conversion of crs of geojson to epsg:4326

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* build: relock backend deps after pyproj + pmtiles

* feat: Updated function to check the coordinate system of uploaded GeoJSON file

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* removed conversion function

---------

Co-authored-by: Sujanadh <[email protected]>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: spwoodcock <[email protected]>
  • Loading branch information
4 people authored Oct 19, 2023
1 parent eff5a11 commit a4a9427
Show file tree
Hide file tree
Showing 4 changed files with 562 additions and 473 deletions.
74 changes: 72 additions & 2 deletions src/backend/app/projects/project_crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import geojson
import numpy as np
import pkg_resources
import pyproj
import requests
import segno
import shapely.wkb as wkblib
Expand All @@ -54,6 +55,7 @@
mapping,
shape,
)
from shapely.ops import transform
from sqlalchemy import and_, column, func, inspect, select, table, text
from sqlalchemy.dialects.postgresql import insert
from sqlalchemy.orm import Session
Expand Down Expand Up @@ -632,7 +634,7 @@ def get_osm_extracts(boundary: str):


async def split_into_tasks(
db: Session, boundary: str, no_of_buildings: int, has_data_extracts: bool
db: Session, outline: str, no_of_buildings: int, has_data_extracts: bool
):
"""Splits a project into tasks.
Expand All @@ -645,7 +647,6 @@ async def split_into_tasks(
Any: A GeoJSON object containing the tasks for the specified project.
"""
project_id = uuid.uuid4()
outline = json.loads(boundary)
all_results = []
boundary_data = []
result = []
Expand Down Expand Up @@ -1791,6 +1792,7 @@ def get_outline_from_geojson_file_in_zip(
with zip.open(filename) as file:
data = file.read()
json_dump = json.loads(data)
check_crs(json_dump) # Validatiing Coordinate Reference System
feature_collection = geojson.FeatureCollection(json_dump)
feature = feature_collection["features"][feature_index]
geom = feature["geometry"]
Expand Down Expand Up @@ -2711,3 +2713,71 @@ def update_project_location_info(
longitude, latitude = geometry.x, geometry.y
address = get_address_from_lat_lon(latitude, longitude)
db_project.location_str = address if address is not None else ""


def convert_geojson_to_epsg4326(input_geojson):
source_crs = pyproj.CRS(
input_geojson.get("crs", {}).get("properties", {}).get("name", "EPSG:4326")
)
transformer = pyproj.Transformer.from_crs(source_crs, "EPSG:4326", always_xy=True)

# Convert the coordinates to EPSG:4326
transformed_features = []
for feature in input_geojson.get("features", []):
geom = shape(feature.get("geometry", {}))
transformed_geom = transform(transformer.transform, geom)
transformed_feature = {
"type": "Feature",
"geometry": transformed_geom.__geo_interface__,
"properties": feature.get("properties", {}),
}
transformed_features.append(transformed_feature)

# Create a new GeoJSON with EPSG:4326
output_geojson = {"type": "FeatureCollection", "features": transformed_features}

return output_geojson


def check_crs(input_geojson: dict):
log.debug("validating coordinate reference system")

def is_valid_crs(crs_name):
valid_crs_list = [
"urn:ogc:def:crs:OGC:1.3:CRS84",
"urn:ogc:def:crs:EPSG::4326",
"WGS 84",
]
return crs_name in valid_crs_list

def is_valid_coordinate(coord):
return -180 <= coord[0] <= 180 and -90 <= coord[1] <= 90

error_message = "ERROR: Unsupported coordinate system, it is recommended to use a GeoJSON file in WGS84(EPSG 4326) standard."
if "crs" in input_geojson:
crs = input_geojson["crs"]["properties"]["name"]
if not is_valid_crs(crs):
log.error(error_message)
raise HTTPException(status_code=400, detail=error_message)
return

if input_geojson["type"] == "FeatureCollection":
coordinates = input_geojson["features"][0]["geometry"]["coordinates"]
elif input_geojson["type"] == "Feature":
coordinates = input_geojson["geometry"]["coordinates"]
geometry_type = (
input_geojson["features"][0]["geometry"]["type"]
if input_geojson["type"] == "FeatureCollection"
else input_geojson["geometry"]["type"]
)

if geometry_type == "MultiPolygon":
first_coordinate = coordinates[0][0][
0
] # Get the first coordinate from the first point
else:
first_coordinate = coordinates[0][0]

if not is_valid_coordinate(first_coordinate):
log.error(error_message)
raise HTTPException(status_code=400, detail=error_message)
22 changes: 21 additions & 1 deletion src/backend/app/projects/project_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
from ..models.enums import TILES_SOURCE
from ..tasks import tasks_crud
from . import project_crud, project_schemas, utils
from .project_crud import check_crs

router = APIRouter(
prefix="/projects",
Expand Down Expand Up @@ -368,6 +369,9 @@ async def upload_multi_project_boundary(
content = await upload.read()
boundary = json.loads(content)

# Validatiing Coordinate Reference System
check_crs(boundary)

log.debug("Creating tasks for each polygon in project")
result = project_crud.update_multi_polygon_project_boundary(
db, project_id, boundary
Expand Down Expand Up @@ -409,9 +413,13 @@ async def task_split(
# read entire file
await upload.seek(0)
content = await upload.read()
boundary = json.loads(content)

# Validatiing Coordinate Reference System
check_crs(boundary)

result = await project_crud.split_into_tasks(
db, content, no_of_buildings, has_data_extracts
db, boundary, no_of_buildings, has_data_extracts
)

return result
Expand Down Expand Up @@ -447,6 +455,9 @@ async def upload_project_boundary(
content = await upload.read()
boundary = json.loads(content)

# Validatiing Coordinate Reference System
check_crs(boundary)

# update project boundary and dimension
result = project_crud.update_project_boundary(db, project_id, boundary, dimension)
if not result:
Expand Down Expand Up @@ -483,6 +494,9 @@ async def edit_project_boundary(
content = await upload.read()
boundary = json.loads(content)

# Validatiing Coordinate Reference System
check_crs(boundary)

result = project_crud.update_project_boundary(db, project_id, boundary, dimension)
if not result:
raise HTTPException(
Expand Down Expand Up @@ -760,6 +774,9 @@ async def preview_tasks(upload: UploadFile = File(...), dimension: int = Form(50
content = await upload.read()
boundary = json.loads(content)

# Validatiing Coordinate Reference System
check_crs(boundary)

result = await project_crud.preview_tasks(boundary, dimension)
return result

Expand Down Expand Up @@ -793,6 +810,9 @@ async def add_features(
content = await upload.read()
features = json.loads(content)

# Validatiing Coordinate Reference System
check_crs(features)

# generate a unique task ID using uuid
background_task_id = uuid.uuid4()

Expand Down
Loading

0 comments on commit a4a9427

Please sign in to comment.