Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add support for GPX map uploads #1017

Merged
merged 3 commits into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
)