Skip to content
This repository has been archived by the owner on Apr 6, 2023. It is now read-only.

Trim grid to roads and measure completeness #276

Open
wants to merge 50 commits into
base: develop
Choose a base branch
from

Conversation

RaymondDashWu
Copy link
Contributor

@RaymondDashWu RaymondDashWu commented Jul 12, 2022

Changelog

  • Task grid can now be queried based on whether they contain roads or not
  • New API call is made at projects/<project_id>/tasks to determine road completion %
  • Added helper function to convert tiles to bbox
  • test grid for mapillary and overpass integration added
  • Mapillary tile API call to see what roads have images

Potential issues

  • M1 Macs seemingly don't pass all the tests?

@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jul 12, 2022
Copy link
Contributor

@zlavergne zlavergne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know you're still working on this but I added a few comments that I think can get cleaned up now. I'll review more later

:param grid_dto: the dto containing
:return: geojson.FeatureCollection trimmed task grid
"""
trimmed_grid = GridService.trim_grid_to_aoi(grid_dto)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this function needs to additionally trim to the AOI. I see them as independent operations and that's why we have the independent functions. If we want to stack functionality, we can just stack the calls from the API. So from the API, we call trim_grid_to_aoi() and use the output of that as the input for trim_grid_to_roads() :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough. Done. Added a little todo for myself to do a pass over the comments and docstring later.

backend/services/grid/grid_service.py Outdated Show resolved Hide resolved
backend/services/grid/grid_service.py Outdated Show resolved Hide resolved
backend/services/grid/grid_service.py Show resolved Hide resolved
backend/services/grid/grid_service.py Outdated Show resolved Hide resolved
@RaymondDashWu RaymondDashWu changed the title Trim grid to roads Trim grid to roads and measure completeness Jul 20, 2022
backend/models/postgis/task.py Outdated Show resolved Hide resolved
backend/services/utils/bbox_to_tile.py Outdated Show resolved Hide resolved
tests/backend/unit/services/grid/test_grid_service.py Outdated Show resolved Hide resolved
@@ -432,3 +432,83 @@ def post(self):
error_msg = f"IntersectingTiles GET API - unhandled error: {str(e)}"
current_app.logger.critical(error_msg)
return {"error": error_msg, "SubCode": "InternalServerError"}, 500


class ProjectActionsIntersectingRoadsAPI(Resource):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My thinking was to add this functionality into the ProjectActionsIntersectingTilesAPI using some additional query params. For instance, add a bool param for trimToAOI, trimToRoads, filterWithWater. Then call each service in series depending on the value of these bools. I would do an if for each bool then nest a try:catch within so we can easily say at which stage it failed.

The idea here is to provide a single endpoint to call when the user clicks "Trim" and the levels of trimming just depends on the checkboxes they've selected.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah ok. I just followed the previous examples from the previous tasking manager code. Surprisingly, none of the endpoints seem to use query params (request.args). So you want something like:

projects/actions/intersecting-roads/ (or /intersecting-tiles) => projects/actions/trim?method=[METHODS]

It might require a bit of refactoring on the frontend side as well. Currently, the trim to AOI only corresponds to a single toggle and endpoint (below).

image

backend/models/postgis/task.py Outdated Show resolved Hide resolved
lat_lon_arr = re.findall(
r'"lat":\s+(-?\d+\.\d+),\s+"lon":\s+(-?\d+\.\d+)', overpass_resp.text
)
url = "https://tiles.mapillary.com/maps/vtp/mly1_public/2/{}/{}/{}?access_token={}".format(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this would also be better as an env var.

Additionally, let's use more explicit var names instead of url twice :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you do that? Something like this in the .env:

MAPILLARY_TILE_API_URL=https://tiles.mapillary.com/maps/vtp/mly1_public/2/{}/{}/{}?access_token={}

Where you can fill in the {} with variables? Or are you talking about something like:

url = "{}/{}/{}/{}?access_token={}".format(
                os.getenv("MAPILLARY_TILE_API_URL"),z, x, y, os.getenv("MAPILLARY_ACCESS_TOKEN")
            )

backend/models/postgis/task.py Outdated Show resolved Hide resolved
backend/services/grid/grid_service.py Outdated Show resolved Hide resolved
Comment on lines 904 to 939
if mapillary_roads:
tasks = [
geojson.loads(task.geojson)["coordinates"][0][0]
for task in project_tasks
]
overarching_bbox = MultiPoint(
[(x, y) for task in tasks for x, y in task]
).bounds
x, y, z = bbox_to_tile(overarching_bbox)
if z > 14:
x, y, z = GridService._get_parent_tile(x, y, z)
# if z < 14:
# child_tiles = GridService._get_child_tile(x, y, z) # TODO Refactor so that it gives all 4 tiles
# x, y, z = child_tiles[0] # arbitrarily pick the first one
base_url = "https://overpass-api.de/api/interpreter?data="
url = (
base_url
+ '[out:json][timeout:25];(way["highway"]{bbox};);out geom;'.format(
bbox=(
# Overpass is lon/lat
overarching_bbox[1],
overarching_bbox[0],
overarching_bbox[3],
overarching_bbox[2],
)
)
)
overpass_resp = requests.get(url)
lat_lon_arr = re.findall(
r'"lat":\s+(-?\d+\.\d+),\s+"lon":\s+(-?\d+\.\d+)', overpass_resp.text
)
url = "https://tiles.mapillary.com/maps/vtp/mly1_public/2/{}/{}/{}?access_token={}".format(
z, x, y, os.getenv("MAPILLARY_ACCESS_TOKEN")
)
resp = requests.get(url)
overarching_tile = mapbox_vector_tile.decode(resp.content)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's technically a possibility here to get a not defined error since overarching_tile is defined within the conditional and used in a lower conditional but it's not declared outside the conditionals.

My suggestion is to move the logic in this conditional into the one at L956. It doesn't look like there's actually a need to execute this before what happens below. Additionally, it would be good to move the logic from these conditionals into a function just to reduce the cyclical complexity of this function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Putting it into L956 would cause a lot of repeated work. That would be fetching all this data for each task since L944 is wrapped in a for task in project_tasks: I'm trying to understand the overarching_tile case you're describing. They're both wrapped up in the same conditional of if mapillary_roads: so I don't think you can have them be mutually exclusive? Let me know if I'm missing something.

backend/services/grid/grid_service.py Outdated Show resolved Hide resolved
backend/services/grid/grid_service.py Outdated Show resolved Hide resolved
backend/services/utils/bbox_to_tile.py Outdated Show resolved Hide resolved
backend/services/utils/tile_to_bbox.py Outdated Show resolved Hide resolved
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Component: Backend
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants