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 project for street level imagery #980

Merged
merged 56 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
6779347
feat(street): add street project draft fixture
ofr1tz Oct 29, 2024
40cdc66
feat: add new street project
Gigaszi Oct 29, 2024
33bc5ec
feat: add tutorial to street project
Gigaszi Oct 29, 2024
ec59ee0
refactor: move process_mapillary.py to utils
Gigaszi Oct 30, 2024
78f28df
feat: add functions to process mapillary data
Gigaszi Oct 30, 2024
3a7ac21
refactor: removed unused imports and classes
Gigaszi Oct 30, 2024
8e5c55c
feat: add function to convert input geojson to polygon
Gigaszi Oct 30, 2024
dd4e220
tests: add unittests for process_mapillary
Gigaszi Oct 30, 2024
b274af3
fix: wrong indention and patch in test_process_mapillary
Gigaszi Oct 30, 2024
7ec92ee
feat: add filtering and handling of mapillary response
Gigaszi Oct 30, 2024
97e552c
tests: add tests for filtering and handling of mapillary response
Gigaszi Oct 30, 2024
53dfda5
refactor: use mapillary incredentials in variables
Gigaszi Nov 11, 2024
481030c
refactor: reformat file process_mapillary.py
Gigaszi Nov 11, 2024
1187561
feat: add geometry to StreetTask and refactoring
Gigaszi Nov 11, 2024
0bef2e0
tests(street): use simpler geometry and add filter parameters to test…
Gigaszi Nov 11, 2024
789b027
refactor: tests for mapillary processing and street project
Gigaszi Nov 11, 2024
c1f1a79
fix: add missing parameter
Gigaszi Nov 11, 2024
a12a803
tests(street): add integration test for street project
Gigaszi Nov 11, 2024
2fb3bc7
tests(street): add street.json for testing of street project
Gigaszi Nov 11, 2024
14ff237
fix: test after refactoring
Gigaszi Nov 11, 2024
80ecf28
feat: add functions for spatial sampling and tests for it
Gigaszi Nov 11, 2024
984d821
feat: use filtering in street project
Gigaszi Nov 14, 2024
4ab528d
feat: add spatial sampling to street project
Gigaszi Nov 18, 2024
f7fc5f3
feat: add size restriction for too many images
Gigaszi Nov 18, 2024
f11f75e
breaking: change geom column in postgres to allow all geometry types
Gigaszi Nov 18, 2024
98f82c9
feat: save location of image as geometry
Gigaszi Nov 18, 2024
6a1d15e
feat: use spatial filter on downloaded images
Gigaszi Nov 18, 2024
05f1449
fix: adapt mapillary token entry in example env
ofr1tz Nov 28, 2024
263d738
fix: do not return failed_rows and do not use functions on empty df
Gigaszi Nov 28, 2024
5ac3620
Merge branch 'feature/street' of https://github.com/mapswipe/python-m…
Gigaszi Nov 28, 2024
df17d3c
fix: testing for removed funcionality
Gigaszi Nov 28, 2024
9f2ac81
style: code formatting
ofr1tz Nov 28, 2024
8593a3c
fix: fixed tests and removed return of failed rows for download from …
Gigaszi Nov 28, 2024
81d44ed
Merge branch 'feature/street' of https://github.com/mapswipe/python-m…
Gigaszi Nov 28, 2024
9503a7d
fix: tests for removed failing rows
Gigaszi Nov 28, 2024
c0016e2
style: fix flake8 errors and isort
ofr1tz Nov 28, 2024
2f3d523
style: isort
ofr1tz Nov 28, 2024
6ad174f
build: add requirements
ofr1tz Nov 28, 2024
315d239
build: add dummy mapillary key to github workflow
ofr1tz Nov 28, 2024
2b88cf7
fix: use os.getenv instead of os.environ
ofr1tz Nov 28, 2024
24c7eec
test: rename class and correct comments
ofr1tz Nov 28, 2024
7f46e80
fix: remove left overs of failed rows
Gigaszi Dec 3, 2024
652e885
Merge branch 'feature/street' of https://github.com/mapswipe/python-m…
Gigaszi Dec 3, 2024
bfa0818
fix: unittests for tile download
Gigaszi Dec 3, 2024
816b1e6
fix: add condition to raise valueerror if no features are found in aoi
Gigaszi Dec 3, 2024
f287b3a
fix: use mapillary api key secret in github workflow
ofr1tz Dec 3, 2024
020178d
fix: use square brackets with os.environ
ofr1tz Dec 3, 2024
dcaab3d
fix: add mapillary key to docker compose
ofr1tz Dec 3, 2024
8128488
fix: allow tasks with point geom in postgres
ofr1tz Dec 3, 2024
74064f8
fix: change geometry type in tasks table in initdb
Gigaszi Dec 3, 2024
f712340
Merge branch 'feature/street' of https://github.com/mapswipe/python-m…
Gigaszi Dec 3, 2024
729a4be
fix: change to all geometries in tasks table in setup db
Gigaszi Dec 3, 2024
994f398
fix: use lower case in setup db
Gigaszi Dec 3, 2024
57dc509
feat(django): add new project types to project type model
ofr1tz Dec 11, 2024
26c822c
fix: sort imports
ofr1tz Dec 11, 2024
4787dd4
fix: add new project types to schema.graphql
ofr1tz Dec 11, 2024
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
1 change: 1 addition & 0 deletions .github/workflows/actions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ jobs:
POSTGRES_DB: postgres
OSMCHA_API_KEY: ${{ secrets.OSMCHA_API_KEY }}
DJANGO_SECRET_KEY: test-django-secret-key
MAPILLARY_API_KEY: ${{ secrets.MAPILLARY_API_KEY }}
COMPOSE_FILE: ../docker-compose.yaml:../docker-compose-ci.yaml
run: |
docker compose run --rm mapswipe_workers_creation python -m unittest discover --verbose --start-directory tests/unittests/
Expand Down
5 changes: 4 additions & 1 deletion django/apps/existing_database/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ class Type(models.IntegerChoices):
FOOTPRINT = 2, "Validate"
CHANGE_DETECTION = 3, "Compare"
COMPLETENESS = 4, "Completeness"
MEDIA = 5, "Media"
DIGITIZATION = 6, "Digitization"
STREET = 7, "Street"

project_id = models.CharField(primary_key=True, max_length=999)
created = models.DateTimeField(blank=True, null=True)
Expand Down Expand Up @@ -127,7 +130,7 @@ class Task(Model):
project = models.ForeignKey(Project, models.DO_NOTHING, related_name="+")
group_id = models.CharField(max_length=999)
task_id = models.CharField(max_length=999)
geom = gis_models.MultiPolygonField(blank=True, null=True)
geom = gis_models.GeometryField(blank=True, null=True)
# Database uses JSON instead of JSONB (not supported by django)
project_type_specifics = models.TextField(blank=True, null=True)

Expand Down
3 changes: 3 additions & 0 deletions django/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ enum ProjectTypeEnum {
FOOTPRINT
CHANGE_DETECTION
COMPLETENESS
MEDIA
DIGITIZATION
STREET
}

type ProjectTypeSwipeStatsType {
Expand Down
1 change: 1 addition & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ x-mapswipe-workers: &base_mapswipe_workers
SLACK_CHANNEL: '${SLACK_CHANNEL}'
SENTRY_DSN: '${SENTRY_DSN}'
OSMCHA_API_KEY: '${OSMCHA_API_KEY}'
MAPILLARY_API_KEY: '${MAPILLARY_API_KEY}'
depends_on:
- postgres
volumes:
Expand Down
3 changes: 3 additions & 0 deletions example.env
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,6 @@ COMMUNITY_DASHBOARD_GRAPHQL_ENDPOINT=https://api.example.com/graphql/
COMMUNITY_DASHBOARD_SENTRY_DSN=
COMMUNITY_DASHBOARD_SENTRY_TRACES_SAMPLE_RATE=
COMMUNITY_DASHBOARD_MAPSWIPE_WEBSITE=https://mapswipe.org

# Mapillary
MAPILLARY_API_KEY=
5 changes: 5 additions & 0 deletions mapswipe_workers/mapswipe_workers/definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
OSM_API_LINK = "https://www.openstreetmap.org/api/0.6/"
OSMCHA_API_LINK = "https://osmcha.org/api/v1/"
OSMCHA_API_KEY = os.environ["OSMCHA_API_KEY"]
MAPILLARY_API_LINK = "https://tiles.mapillary.com/maps/vtp/mly1_computed_public/2/"
MAPILLARY_API_KEY = os.environ["MAPILLARY_API_KEY"]
ofr1tz marked this conversation as resolved.
Show resolved Hide resolved

# number of geometries for project geometries
MAX_INPUT_GEOMETRIES = 10
Expand Down Expand Up @@ -134,6 +136,7 @@ class ProjectType(Enum):
COMPLETENESS = 4
MEDIA_CLASSIFICATION = 5
DIGITIZATION = 6
STREET = 7

@property
def constructor(self):
Expand All @@ -145,6 +148,7 @@ def constructor(self):
DigitizationProject,
FootprintProject,
MediaClassificationProject,
StreetProject,
)

project_type_classes = {
Expand All @@ -154,6 +158,7 @@ def constructor(self):
4: CompletenessProject,
5: MediaClassificationProject,
6: DigitizationProject,
7: StreetProject,
}
return project_type_classes[self.value]

Expand Down
2 changes: 2 additions & 0 deletions mapswipe_workers/mapswipe_workers/project_types/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from .arbitrary_geometry.footprint.project import FootprintProject
from .arbitrary_geometry.footprint.tutorial import FootprintTutorial
from .media_classification.project import MediaClassificationProject
from .street.project import StreetProject
from .tile_map_service.change_detection.project import ChangeDetectionProject
from .tile_map_service.change_detection.tutorial import ChangeDetectionTutorial
from .tile_map_service.classification.project import ClassificationProject
Expand All @@ -20,4 +21,5 @@
"FootprintProject",
"FootprintTutorial",
"DigitizationProject",
"StreetProject",
]
Empty file.
134 changes: 134 additions & 0 deletions mapswipe_workers/mapswipe_workers/project_types/street/project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import math
from dataclasses import dataclass
from typing import Dict, List

from mapswipe_workers.definitions import logger
from mapswipe_workers.firebase.firebase import Firebase
from mapswipe_workers.firebase_to_postgres.transfer_results import (
results_to_file,
save_results_to_postgres,
truncate_temp_results,
)
from mapswipe_workers.generate_stats.project_stats import (
get_statistics_for_integer_result_project,
)
from mapswipe_workers.project_types.project import BaseGroup, BaseProject, BaseTask
from mapswipe_workers.utils.process_mapillary import get_image_metadata
from mapswipe_workers.utils.validate_input import (
build_multipolygon_from_layer_geometries,
check_if_layer_has_too_many_geometries,
check_if_layer_is_empty,
load_geojson_to_ogr,
multipolygon_to_wkt,
save_geojson_to_file,
)


@dataclass
class StreetGroup(BaseGroup):
# todo: does client use this, or only for the implementation of project creation?
pass


@dataclass
class StreetTask(BaseTask):
geometry: str


class StreetProject(BaseProject):
def __init__(self, project_draft):
super().__init__(project_draft)
self.groups: Dict[str, StreetGroup] = {}
self.tasks: Dict[str, List[StreetTask]] = {}

self.geometry = project_draft["geometry"]

# TODO: validate inputs
ImageMetadata = get_image_metadata(
self.geometry,
is_pano=project_draft.get("isPano", None),
start_time=project_draft.get("startTimestamp", None),
end_time=project_draft.get("endTimestamp", None),
organization_id=project_draft.get("organizationId", None),
sampling_threshold=project_draft.get("samplingThreshold", None),
)

self.imageIds = ImageMetadata["ids"]
self.imageGeometries = ImageMetadata["geometries"]

def save_tasks_to_firebase(self, projectId: str, tasks: dict):
firebase = Firebase()
firebase.save_tasks_to_firebase(projectId, tasks, useCompression=False)

@staticmethod
def results_to_postgres(results: dict, project_id: str, filter_mode: bool):
"""How to move the result data from firebase to postgres."""
results_file, user_group_results_file = results_to_file(results, project_id)
truncate_temp_results()
save_results_to_postgres(results_file, project_id, filter_mode)
return user_group_results_file

@staticmethod
def get_per_project_statistics(project_id, project_info):
"""How to aggregate the project results."""
return get_statistics_for_integer_result_project(
project_id, project_info, generate_hot_tm_geometries=False
)

def validate_geometries(self):
self.inputGeometriesFileName = save_geojson_to_file(
self.projectId, self.geometry
)
layer, datasource = load_geojson_to_ogr(
self.projectId, self.inputGeometriesFileName
)

# check if inputs fit constraints
check_if_layer_is_empty(self.projectId, layer)

multi_polygon, project_area = build_multipolygon_from_layer_geometries(
self.projectId, layer
)

check_if_layer_has_too_many_geometries(self.projectId, multi_polygon)

del datasource
del layer

logger.info(
f"{self.projectId}" f" - validate geometry - " f"input geometry is correct."
)
wkt_geometry = multipolygon_to_wkt(multi_polygon)
return wkt_geometry

def create_groups(self):
self.numberOfGroups = math.ceil(len(self.imageIds) / self.groupSize)
for group_id in range(self.numberOfGroups):
self.groups[f"g{group_id}"] = StreetGroup(
projectId=self.projectId,
groupId=f"g{group_id}",
progress=0,
finishedCount=0,
requiredCount=0,
numberOfTasks=self.groupSize,
)

def create_tasks(self):
if len(self.groups) == 0:
raise ValueError("Groups needs to be created before tasks can be created.")
for group_id, group in self.groups.items():
self.tasks[group_id] = []
for i in range(self.groupSize):
task = StreetTask(
projectId=self.projectId,
groupId=group_id,
geometry=self.imageGeometries.pop(),
taskId=self.imageIds.pop(),
)
self.tasks[group_id].append(task)

# list now empty? if usual group size is not reached
# the actual number of tasks for the group is updated
if not self.imageIds:
group.numberOfTasks = i + 1
break
14 changes: 14 additions & 0 deletions mapswipe_workers/mapswipe_workers/project_types/street/tutorial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from mapswipe_workers.project_types.tutorial import BaseTutorial


class StreetTutorial(BaseTutorial):
"""The subclass for an TMS Grid based Tutorial."""

def save_tutorial(self):
raise NotImplementedError("Currently Street has no Tutorial")

def create_tutorial_groups(self):
raise NotImplementedError("Currently Street has no Tutorial")

def create_tutorial_tasks(self):
raise NotImplementedError("Currently Street has no Tutorial")
Loading
Loading