Skip to content

Commit

Permalink
feat: Add support for GPX map uploads (#1017)
Browse files Browse the repository at this point in the history
* feat: add GPX parser
* test: add GPX tests
  • Loading branch information
paulschreiber authored Nov 29, 2023
1 parent 5cfb2e1 commit 40c17fb
Show file tree
Hide file tree
Showing 4 changed files with 233 additions and 0 deletions.
16 changes: 16 additions & 0 deletions terraso_backend/apps/core/gis/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
logger = structlog.get_logger(__name__)

supported_drivers["KML"] = "rw"
supported_drivers["GPX"] = "rw"


def is_geojson_file_extension(file):
Expand Down Expand Up @@ -56,6 +57,10 @@ def is_kmz_file_extension(file):
return file.name.endswith(".kmz")


def is_gpx_file_extension(file):
return file.name.endswith(".gpx")


def parse_kml_file(file):
gdf = gpd.read_file(file, driver="KML")
return json.loads(gdf.to_json())
Expand Down Expand Up @@ -106,6 +111,11 @@ def parse_shapefile(file):
return json.loads(gdf_transformed.to_json())


def parse_gpx_file(file):
gdf = gpd.read_file(file, driver="GPX")
return json.loads(gdf.to_json())


def parse_file_to_geojson(file):
if is_shape_file_extension(file):
try:
Expand All @@ -131,5 +141,11 @@ def parse_file_to_geojson(file):
except Exception as e:
logger.error("Error parsing geojson file", error=e)
raise ValidationError("invalid_geojson_file")
elif is_gpx_file_extension(file):
try:
return parse_gpx_file(file)
except Exception as e:
logger.error("Error parsing gpx file", error=e)
raise ValidationError("invalid_gpx_file")
else:
raise ValidationError("invalid_file_type")
186 changes: 186 additions & 0 deletions terraso_backend/tests/core/gis/test_parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,36 @@
</Document>
</kml>"""

GPX_CONTENT = """<?xml version="1.0" standalone="yes"?>
<gpx xmlns="http://www.topografix.com/GPX/1/1" version="1.1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
<wpt lat="45.52" lon="-122.681944">
<ele>0</ele>
<name><![CDATA[Portland]]></name>
<cmt><![CDATA[Waypoint no: 1]]></cmt>
<desc><![CDATA[This is waypoint no: 1]]></desc>
</wpt>
<wpt lat="-22.908333" lon="-43.196389">
<ele>0</ele>
<name><![CDATA[Rio de Janeiro]]></name>
<cmt><![CDATA[Waypoint no: 2]]></cmt>
<desc><![CDATA[This is waypoint no: 2]]></desc>
</wpt>
<wpt lat="41.01224" lon="28.976018">
<ele>0</ele>
<name><![CDATA[Istanbul]]></name>
<cmt><![CDATA[Waypoint no: 3]]></cmt>
<desc><![CDATA[This is waypoint no: 3]]></desc>
</wpt>
<wpt lat="64.133333" lon="-21.933333">
<ele>0</ele>
<name><![CDATA[Reykjavik]]></name>
<cmt><![CDATA[Waypoint no: 4]]></cmt>
<desc><![CDATA[This is waypoint no: 4]]></desc>
</wpt>
</gpx>"""

KML_GEOJSON = {
"type": "FeatureCollection",
"features": [
Expand Down Expand Up @@ -115,6 +145,132 @@
],
}

GPX_GEOJSON = {
"type": "FeatureCollection",
"features": [
{
"id": "0",
"type": "Feature",
"properties": {
"ele": 0.0,
"time": None,
"magvar": None,
"geoidheight": None,
"name": "Portland",
"cmt": "Waypoint no: 1",
"desc": "This is waypoint no: 1",
"src": None,
"link1_href": None,
"link1_text": None,
"link1_type": None,
"link2_href": None,
"link2_text": None,
"link2_type": None,
"sym": None,
"type": None,
"fix": None,
"sat": None,
"hdop": None,
"vdop": None,
"pdop": None,
"ageofdgpsdata": None,
"dgpsid": None,
},
"geometry": {"type": "Point", "coordinates": [-122.681944, 45.52]},
},
{
"id": "1",
"type": "Feature",
"properties": {
"ele": 0.0,
"time": None,
"magvar": None,
"geoidheight": None,
"name": "Rio de Janeiro",
"cmt": "Waypoint no: 2",
"desc": "This is waypoint no: 2",
"src": None,
"link1_href": None,
"link1_text": None,
"link1_type": None,
"link2_href": None,
"link2_text": None,
"link2_type": None,
"sym": None,
"type": None,
"fix": None,
"sat": None,
"hdop": None,
"vdop": None,
"pdop": None,
"ageofdgpsdata": None,
"dgpsid": None,
},
"geometry": {"type": "Point", "coordinates": [-43.196389, -22.908333]},
},
{
"id": "2",
"type": "Feature",
"properties": {
"ele": 0.0,
"time": None,
"magvar": None,
"geoidheight": None,
"name": "Istanbul",
"cmt": "Waypoint no: 3",
"desc": "This is waypoint no: 3",
"src": None,
"link1_href": None,
"link1_text": None,
"link1_type": None,
"link2_href": None,
"link2_text": None,
"link2_type": None,
"sym": None,
"type": None,
"fix": None,
"sat": None,
"hdop": None,
"vdop": None,
"pdop": None,
"ageofdgpsdata": None,
"dgpsid": None,
},
"geometry": {"type": "Point", "coordinates": [28.976018, 41.01224]},
},
{
"id": "3",
"type": "Feature",
"properties": {
"ele": 0.0,
"time": None,
"magvar": None,
"geoidheight": None,
"name": "Reykjavik",
"cmt": "Waypoint no: 4",
"desc": "This is waypoint no: 4",
"src": None,
"link1_href": None,
"link1_text": None,
"link1_type": None,
"link2_href": None,
"link2_text": None,
"link2_type": None,
"sym": None,
"type": None,
"fix": None,
"sat": None,
"hdop": None,
"vdop": None,
"pdop": None,
"ageofdgpsdata": None,
"dgpsid": None,
},
"geometry": {"type": "Point", "coordinates": [-21.933333, 64.133333]},
},
],
}


@pytest.fixture
def shapefile_zip(request):
Expand Down Expand Up @@ -181,3 +337,33 @@ def test_parse_kml_file(kml_file):

# Assert that the output of the parse_kml_file function is as expected
assert kml_json == KML_GEOJSON


@pytest.fixture
def gpx_file(request):
gpx_contents, file_extension = request.param
# Create a temporary file
with tempfile.NamedTemporaryFile(mode="w", suffix=f".{file_extension}", delete=False) as f:
# Write the GPX content to the file
f.write(gpx_contents)

# Return the file path
yield f.name

# Clean up: delete the temporary file
os.unlink(f.name)


@pytest.mark.parametrize(
"gpx_file",
[
(GPX_CONTENT, "gpx"),
],
indirect=True,
)
def test_parse_gpx_file(gpx_file):
with open(gpx_file, "rb") as file:
gpx_json = parse_file_to_geojson(file)

# Assert that the output of the parse_gpx_file function is as expected
assert gpx_json == GPX_GEOJSON
15 changes: 15 additions & 0 deletions terraso_backend/tests/graphql/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,21 @@ def data_entry_kml(users, groups):
)


@pytest.fixture
def data_entry_gpx(users, groups):
creator = users[0]
creator_group = groups[0]
creator_group.members.add(creator)
return mixer.blend(
DataEntry,
created_by=creator,
size=100,
groups=creator_group,
entry_type=DataEntry.ENTRY_TYPE_FILE,
resource_type="gpx",
)


@pytest.fixture
def data_entry_shapefile(users, groups):
creator = users[0]
Expand Down
16 changes: 16 additions & 0 deletions terraso_backend/tests/shared_data/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,19 @@ def visualization_config_kml(user):
),
created_by=user,
)


@pytest.fixture
def visualization_config_gpx(user):
return mixer.blend(
VisualizationConfig,
size=1,
data_entry=mixer.blend(
DataEntry,
size=1,
url=f"{settings.DATA_ENTRY_FILE_BASE_URL}/{user.id}/test_data.gpx",
created_by=user,
resource_type="gpx",
),
created_by=user,
)

0 comments on commit 40c17fb

Please sign in to comment.