diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index d0cddf893d..9d702a84c9 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -41,7 +41,9 @@ jobs: build_openapi_json: name: Build OpenAPI runs-on: ubuntu-latest - container: ghcr.io/hotosm/fmtm/backend:ci-main + container: + image: ghcr.io/hotosm/fmtm/backend:ci-main + options: --user root steps: - name: Checkout repository @@ -60,13 +62,15 @@ jobs: - name: Build OpenAPi JSON run: | - cd src/backend - python ../../scripts/gen_openapi_json.py -o ../../docs/openapi.json + chmod -R 777 . + gosu appuser python scripts/gen_openapi_json.py -o docs/openapi.json publish_docs: name: Publish Docs runs-on: ubuntu-latest - container: ghcr.io/hotosm/fmtm/backend:ci-main + container: + image: ghcr.io/hotosm/fmtm/backend:ci-main + options: --user root needs: [build_doxygen, build_openapi_json] steps: @@ -84,7 +88,12 @@ jobs: restore-keys: | doc-build- + - name: Install Git + run: | + apt-get update + apt-get install -y git --no-install-recommends + - name: Publish run: | - cd src/backend - mkdocs gh-deploy --config-file=../../mkdocs.yml --force + chmod -R 777 . + gosu appuser mkdocs gh-deploy --force diff --git a/INSTALL.md b/INSTALL.md index 9041efded7..32fdc6bbc7 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,4 +1,4 @@ -> NOTE: This is an installation guide to quickly get the fmtm app up and running. For a detailed guide on how to install the fmtm app using different methods and contributing, checkout the [docs](./docs) +> NOTE: This is an installation guide to quickly get the fmtm app up and running. For a detailed guide on how to install the fmtm app using different methods and contributing, checkout the [dev docs](https://hotosm.github.io/fmtm/dev/Setup/) # Table of Contents diff --git a/README.md b/README.md index 3196bf7773..14f157a93a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![](images/hot_logo.png) +![](https://github.com/hotosm/fmtm/blob/main/images/hot_logo.png?raw=true) [![All Contributors](https://img.shields.io/github/all-contributors/hotosm/fmtm?color=ee8449&style=flat-square)](#contributors-) diff --git a/docker-compose.yml b/docker-compose.yml index bfa8e6a91d..1f6af27798 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -38,7 +38,7 @@ services: - POSTGRES_PASSWORD=${FMTM_DB_PASSWORD:-fmtm} - POSTGRES_DB=${FMTM_DB_NAME:-fmtm} ports: - - "5433:5432" + - "5438:5432" networks: - fmtm-dev restart: unless-stopped diff --git a/docs/About.md b/docs/About.md index 94e48abfe6..6c338df1a1 100644 --- a/docs/About.md +++ b/docs/About.md @@ -1,4 +1,4 @@ -![](images/hot_logo.png) +![](https://github.com/hotosm/fmtm/blob/main/images/hot_logo.png?raw=true) # Field Mapping Tasking Manager (FMTM) diff --git a/docs/dev/Backend.md b/docs/dev/Backend.md index 14d3885d69..da2eddaacc 100644 --- a/docs/dev/Backend.md +++ b/docs/dev/Backend.md @@ -139,16 +139,7 @@ Example launch.json config for vscode: Creating a new release during development may not always be feasible. -**Via Dockerfile** - -- The debug stages in the backend Dockerfile install the latest osm-fieldwork repo `main` branch. -- To re-build, run: `docker compose build api --no-cache`. - -> Note: this is useful to debug functionality not yet released in a stable version on PyPi. - -**Via Bind-Mount** - -- Alternatively, a development version of osm-fieldwork can be mounted into the FMTM container. +- A development version of osm-fieldwork can be mounted into the FMTM container via bind mount. - Clone the osm-fieldwork repo to the same root directory as FMTM. - Uncomment the line in docker-compose.yml diff --git a/docs/swagger/swagger-initializer.js b/docs/swagger/swagger-initializer.js index 6c52836b1a..f6c9118ab1 100644 --- a/docs/swagger/swagger-initializer.js +++ b/docs/swagger/swagger-initializer.js @@ -3,7 +3,7 @@ window.onload = function () { // the following lines will be replaced by docker/configurator, when it runs in a docker-container window.ui = SwaggerUIBundle({ - url: "https://hotosm.gitlab-pages.wsl.ch/fmtm/openapi.json", + url: "https://hotosm.github.io/fmtm/openapi.json", dom_id: "#swagger-ui", deepLinking: true, presets: [SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset], diff --git a/src/backend/Dockerfile b/src/backend/Dockerfile index b15aa3068a..a97a291fd4 100644 --- a/src/backend/Dockerfile +++ b/src/backend/Dockerfile @@ -79,6 +79,7 @@ ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ PYTHONFAULTHANDLER=1 \ PATH="/home/appuser/.local/bin:$PATH" \ + PYTHONPATH="/opt" \ PYTHON_LIB="/home/appuser/.local/lib/python$PYTHON_IMG_TAG/site-packages" \ SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt \ REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt \ diff --git a/src/backend/app/central/central_crud.py b/src/backend/app/central/central_crud.py index 84953e9e38..737142e4f6 100644 --- a/src/backend/app/central/central_crud.py +++ b/src/backend/app/central/central_crud.py @@ -283,10 +283,11 @@ def delete_odk_xform( return result -def list_odk_xforms(project_id: int, odk_central: project_schemas.ODKCentral = None): +# def list_odk_xforms(project_id: int, odk_central: project_schemas.ODKCentral = None): +def list_odk_xforms(project_id: int, odk_central: project_schemas.ODKCentral = None, metadata:bool = False): """List all XForms in an ODK Central project.""" project = get_odk_project(odk_central) - xforms = project.listForms(project_id) + xforms = project.listForms(project_id, metadata) # FIXME: make sure it's a valid project id return xforms diff --git a/src/backend/app/config.py b/src/backend/app/config.py index c58199ea9f..56daa0f82a 100644 --- a/src/backend/app/config.py +++ b/src/backend/app/config.py @@ -20,7 +20,7 @@ from functools import lru_cache from typing import Any, Optional, Union -from pydantic import AnyUrl, Extra, FieldValidationInfo, PostgresDsn, field_validator +from pydantic import Extra, FieldValidationInfo, PostgresDsn, field_validator from pydantic_settings import BaseSettings, SettingsConfigDict @@ -35,13 +35,13 @@ class Settings(BaseSettings): FRONTEND_MAIN_URL: Optional[str] FRONTEND_MAP_URL: Optional[str] - EXTRA_CORS_ORIGINS: Optional[Union[str, list[AnyUrl]]] = [] + EXTRA_CORS_ORIGINS: Optional[Union[str, list[str]]] = [] @field_validator("EXTRA_CORS_ORIGINS", mode="before") @classmethod def assemble_cors_origins( cls, - val: Union[str, list[AnyUrl]], + val: Union[str, list[str]], info: FieldValidationInfo, ) -> Union[list[str], str]: """Build and validate CORS origins list. @@ -53,8 +53,8 @@ def assemble_cors_origins( # Build default origins from env vars url_scheme = info.data.get("URL_SCHEME") - main_url = info.data.get("URL_SCHEME") - map_url = info.data.get("URL_SCHEME") + main_url = info.data.get("FRONTEND_MAIN_URL") + map_url = info.data.get("FRONTEND_MAP_URL") if url_scheme and main_url and map_url: default_origins = [ f"{url_scheme}://{main_url}", @@ -99,16 +99,16 @@ def assemble_db_connection(cls, v: Optional[str], info: FieldValidationInfo) -> # Convert Url type to string return str(pg_url) - ODK_CENTRAL_URL: Optional[AnyUrl] - ODK_CENTRAL_USER: Optional[str] - ODK_CENTRAL_PASSWD: Optional[str] + ODK_CENTRAL_URL: Optional[str] = "" + ODK_CENTRAL_USER: Optional[str] = "" + ODK_CENTRAL_PASSWD: Optional[str] = "" OSM_CLIENT_ID: str OSM_CLIENT_SECRET: str OSM_SECRET_KEY: str - OSM_URL: AnyUrl = "https://www.openstreetmap.org" + OSM_URL: str = "https://www.openstreetmap.org" OSM_SCOPE: str = "read_prefs" - OSM_LOGIN_REDIRECT_URI: AnyUrl = "http://127.0.0.1:8080/osmauth/" + OSM_LOGIN_REDIRECT_URI: str = "http://127.0.0.1:8080/osmauth/" SENTRY_DSN: Optional[str] = None diff --git a/src/backend/app/projects/project_crud.py b/src/backend/app/projects/project_crud.py index a302311686..3aa7a0db13 100644 --- a/src/backend/app/projects/project_crud.py +++ b/src/backend/app/projects/project_crud.py @@ -701,15 +701,15 @@ def process_polygon(db:Session, project_id:uuid.UUID, boundary_data:str, no_of_b db.commit() else: # Remove the polygons outside of the project AOI using a parameterized query - query = f""" + query = text(f""" DELETE FROM ways_poly WHERE NOT ST_Within(ST_Centroid(ways_poly.geom), (SELECT geom FROM project_aoi WHERE project_id = '{project_id}')); - """ + """) result = db.execute(query) db.commit() with open('app/db/split_algorithm.sql', 'r') as sql_file: query = sql_file.read() - result = db.execute(query, params={'num_buildings': no_of_buildings}) + result = db.execute(text(query), params={'num_buildings': no_of_buildings}) result = result.fetchall() db.query(db_models.DbBuildings).delete() db.query(db_models.DbOsmLines).delete() @@ -1549,7 +1549,7 @@ def get_task_geometry(db: Session, project_id: int): async def get_project_features_geojson(db:Session, project_id:int): # Get the geojson of those features for this task. - query = f"""SELECT jsonb_build_object( + query = text(f"""SELECT jsonb_build_object( 'type', 'FeatureCollection', 'features', jsonb_agg(feature) ) @@ -1563,7 +1563,7 @@ async def get_project_features_geojson(db:Session, project_id:int): FROM features WHERE project_id={project_id} ) features; - """ + """) result = db.execute(query) features = result.fetchone()[0] @@ -2088,7 +2088,7 @@ async def update_project_form( # Get the features for this task. # Postgis query to filter task inside this task outline and of this project # Update those features and set task_id - query = f"""UPDATE features + query = text(f"""UPDATE features SET task_id={task} WHERE id in ( @@ -2096,12 +2096,12 @@ async def update_project_form( FROM features WHERE project_id={project_id} and ST_Intersects(geometry, '{task_obj.outline}'::Geometry) - )""" + )""") result = db.execute(query) # Get the geojson of those features for this task. - query = f"""SELECT jsonb_build_object( + query = text(f"""SELECT jsonb_build_object( 'type', 'FeatureCollection', 'features', jsonb_agg(feature) ) @@ -2114,7 +2114,7 @@ async def update_project_form( ) AS feature FROM features WHERE project_id={project_id} and task_id={task} - ) features;""" + ) features;""") result = db.execute(query) features = result.fetchone()[0] @@ -2154,7 +2154,7 @@ async def update_odk_credentials( async def get_extracted_data_from_db(db: Session, project_id: int, outfile: str): """Get the geojson of those features for this project.""" - query = f"""SELECT jsonb_build_object( + query = text(f"""SELECT jsonb_build_object( 'type', 'FeatureCollection', 'features', jsonb_agg(feature) ) @@ -2167,7 +2167,7 @@ async def get_extracted_data_from_db(db: Session, project_id: int, outfile: str) ) AS feature FROM features WHERE project_id={project_id} - ) features;""" + ) features;""") result = db.execute(query) features = result.fetchone()[0] @@ -2208,7 +2208,7 @@ def get_project_tiles( db.commit() # Project Outline - query = f"""SELECT jsonb_build_object( + query = text(f"""SELECT jsonb_build_object( 'type', 'FeatureCollection', 'features', jsonb_agg(feature) ) @@ -2220,7 +2220,7 @@ def get_project_tiles( ) AS feature FROM projects WHERE id={project_id} - ) features;""" + ) features;""") result = db.execute(query) features = result.fetchone()[0] @@ -2233,7 +2233,7 @@ def get_project_tiles( jsonfile.truncate(0) dump(features, jsonfile) - basemap = basemapper.BaseMapper(boundary_file, base, source) + basemap = basemapper.BaseMapper(boundary_file, base, source, False) outf = basemapper.DataFile(outfile, basemap.getFormat()) suffix = os.path.splitext(outfile)[1] if suffix == ".mbtiles": diff --git a/src/backend/app/projects/project_routes.py b/src/backend/app/projects/project_routes.py index e04d95d87a..0351ac0c69 100644 --- a/src/backend/app/projects/project_routes.py +++ b/src/backend/app/projects/project_routes.py @@ -1024,6 +1024,7 @@ async def download_task_boundary_osm( response = Response(content=content, media_type="application/xml") return response +from sqlalchemy.sql import text @router.get("/centroid/") async def project_centroid( @@ -1040,11 +1041,11 @@ async def project_centroid( List[Tuple[int, str]]: A list of tuples containing the task ID and the centroid as a string. """ - query = f"""SELECT id, ARRAY_AGG(ARRAY[ST_X(ST_Centroid(outline)), ST_Y(ST_Centroid(outline))]) AS centroid + query = text(f"""SELECT id, ARRAY_AGG(ARRAY[ST_X(ST_Centroid(outline)), ST_Y(ST_Centroid(outline))]) AS centroid FROM projects WHERE {f"id={project_id}" if project_id else "1=1"} - GROUP BY id;""" + GROUP BY id;""") result = db.execute(query) - result = result.fetchall() - return result \ No newline at end of file + result_dict_list = [{"id": row[0], "centroid": row[1]} for row in result.fetchall()] + return result_dict_list \ No newline at end of file diff --git a/src/backend/app/projects/project_schemas.py b/src/backend/app/projects/project_schemas.py index eef1611df8..77b60c662d 100644 --- a/src/backend/app/projects/project_schemas.py +++ b/src/backend/app/projects/project_schemas.py @@ -50,7 +50,7 @@ class BETAProjectUpload(BaseModel): xform_title: Union[str, None] odk_central: ODKCentral hashtags: Union[List[str], None] - organisation_id: Optional[int] + organisation_id: Optional[int] = None # city: str # country: str @@ -58,25 +58,25 @@ class BETAProjectUpload(BaseModel): class Feature(BaseModel): id: int project_id: int - task_id: Optional[int] - geometry: Optional[Feature] + task_id: Optional[int] = None + geometry: Optional[Feature] = None class ProjectSummary(BaseModel): id: int = -1 priority: ProjectPriority = ProjectPriority.MEDIUM priority_str: str = priority.name - title: Optional[str] - location_str: Optional[str] - description: Optional[str] - total_tasks: Optional[int] - tasks_mapped: Optional[int] - num_contributors: Optional[int] - tasks_validated: Optional[int] - tasks_bad: Optional[int] - hashtags: Optional[List[str]] - organisation_id: Optional[int] - organisation_logo: Optional[str] + title: Optional[str] = None + location_str: Optional[str] = None + description: Optional[str] = None + total_tasks: Optional[int] = None + tasks_mapped: Optional[int] = None + num_contributors: Optional[int] = None + tasks_validated: Optional[int] = None + tasks_bad: Optional[int] = None + hashtags: Optional[List[str]] = None + organisation_id: Optional[int] = None + organisation_logo: Optional[str] = None class ProjectBase(BaseModel): @@ -88,9 +88,9 @@ class ProjectBase(BaseModel): # location_str: str # outline_geojson: Optional[Feature] project_tasks: Optional[List[tasks_schemas.Task]] - xform_title: Optional[str] - hashtags: Optional[List[str]] - organisation_id: Optional[int] + xform_title: Optional[str] = None + hashtags: Optional[List[str]] = None + organisation_id: Optional[int] = None class ProjectOut(ProjectBase): diff --git a/src/backend/app/submission/submission_crud.py b/src/backend/app/submission/submission_crud.py index be619eafaf..baebf17927 100644 --- a/src/backend/app/submission/submission_crud.py +++ b/src/backend/app/submission/submission_crud.py @@ -394,8 +394,6 @@ def extract_files(zip_file_path): zip_file.extractall(extract_dir) return [os.path.join(extract_dir, f) for f in zip_file.namelist()] - # Set up logging configuration - logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(threadName)s] %(message)s") with concurrent.futures.ThreadPoolExecutor() as executor: task_list = [x.id for x in project_tasks] diff --git a/src/backend/app/tasks/tasks_crud.py b/src/backend/app/tasks/tasks_crud.py index a1cc26c9e3..21a9d48afb 100644 --- a/src/backend/app/tasks/tasks_crud.py +++ b/src/backend/app/tasks/tasks_crud.py @@ -43,7 +43,7 @@ async def get_task_count_in_project(db: Session, project_id: int): - query = f"""select count(*) from tasks where project_id = {project_id}""" + query = text(f"""select count(*) from tasks where project_id = {project_id}""") result = db.execute(query) return result.fetchone()[0] diff --git a/src/backend/app/tasks/tasks_routes.py b/src/backend/app/tasks/tasks_routes.py index ccc9f0170c..ee1a9dd20e 100644 --- a/src/backend/app/tasks/tasks_routes.py +++ b/src/backend/app/tasks/tasks_routes.py @@ -165,8 +165,6 @@ async def task_features_count( db: Session = Depends(database.get_db), ): - task_list = tasks_crud.get_task_lists(db, project_id) - # Get the project object. project = project_crud.get_project(db, project_id) @@ -177,28 +175,22 @@ async def task_features_count( odk_central_password = project.odk_central_password, ) - def process_task(task): - feature_count_query = f""" - select count(*) from features where project_id = {project_id} and task_id = {task} - """ + odk_details = central_crud.list_odk_xforms(project.odkid, odk_credentials, True) + + # Assemble the final data list + data = [] + for x in odk_details: + feature_count_query = text(f""" + select count(*) from features where project_id = {project_id} and task_id = {x['xmlFormId']} + """) result = db.execute(feature_count_query) feature_count = result.fetchone() - submission_list = central_crud.list_task_submissions( - project.odkid, task, odk_credentials) - - # form_details = central_crud.get_form_full_details(project.odkid, task, odk_credentials) - return { - "task_id": task, - "feature_count": feature_count["count"], - # 'submission_count': form_details['submissions'], - "submission_count": len(submission_list) - if isinstance(submission_list, list) - else 0, - } - - loop = asyncio.get_event_loop() - tasks = [loop.run_in_executor(None, process_task, task) for task in task_list] - processed_results = await asyncio.gather(*tasks) + data.append({ + 'task_id': x['xmlFormId'], + 'submission_count': x['submissions'], + 'last_submission': x['lastSubmission'], + 'feature_count': feature_count[0] + }) - return processed_results + return data \ No newline at end of file diff --git a/src/backend/pdm.lock b/src/backend/pdm.lock index f532a5773b..1cf09df4ee 100644 --- a/src/backend/pdm.lock +++ b/src/backend/pdm.lock @@ -6,7 +6,7 @@ groups = ["default", "debug", "dev", "docs", "test"] cross_platform = true static_urls = false lock_version = "4.3" -content_hash = "sha256:3f7fd08e99e73be9235d910c57f431e25365e4f1aec74a1e451804bea1f2b03c" +content_hash = "sha256:936d2eafa20f60dde566582c436480c3185c66facdc1d777c9986cd509f82460" [[package]] name = "annotated-types" @@ -54,14 +54,14 @@ files = [ [[package]] name = "asttokens" -version = "2.2.1" +version = "2.4.0" summary = "Annotate AST trees with source code positions" dependencies = [ - "six", + "six>=1.12.0", ] files = [ - {file = "asttokens-2.2.1-py2.py3-none-any.whl", hash = "sha256:6b0ac9e93fb0335014d382b8fa9b3afa7df546984258005da0b9e7095b3deb1c"}, - {file = "asttokens-2.2.1.tar.gz", hash = "sha256:4622110b2a6f30b77e1473affaa97e711bc2f07d3f10848420ff1898edbe94f3"}, + {file = "asttokens-2.4.0-py2.py3-none-any.whl", hash = "sha256:cf8fc9e61a86461aa9fb161a14a0841a03c405fa829ac6b202670b3495d2ce69"}, + {file = "asttokens-2.4.0.tar.gz", hash = "sha256:2e0171b991b2c959acc6c49318049236844a5da1d65ba2672c4880c1c894834e"}, ] [[package]] @@ -83,19 +83,6 @@ files = [ {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, ] -[[package]] -name = "beautifulsoup4" -version = "4.12.2" -requires_python = ">=3.6.0" -summary = "Screen-scraping library" -dependencies = [ - "soupsieve>1.2", -] -files = [ - {file = "beautifulsoup4-4.12.2-py3-none-any.whl", hash = "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a"}, - {file = "beautifulsoup4-4.12.2.tar.gz", hash = "sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da"}, -] - [[package]] name = "black" version = "23.7.0" @@ -219,7 +206,7 @@ files = [ [[package]] name = "commitizen" -version = "3.7.0" +version = "3.8.0" requires_python = ">=3.7,<4.0" summary = "Python commitizen client tool" dependencies = [ @@ -236,18 +223,8 @@ dependencies = [ "tomlkit<1.0.0,>=0.5.3", ] files = [ - {file = "commitizen-3.7.0-py3-none-any.whl", hash = "sha256:473e703f4d3cfa14250ee197a7a47acb02c064d590f351eb94338385427e53e3"}, - {file = "commitizen-3.7.0.tar.gz", hash = "sha256:c2c83817981f539f0c92a5f16a5d82e41954fdc886ea651b2f5a07f078c8bbaf"}, -] - -[[package]] -name = "cssselect" -version = "1.2.0" -requires_python = ">=3.7" -summary = "cssselect parses CSS3 Selectors and translates them to XPath 1.0" -files = [ - {file = "cssselect-1.2.0-py2.py3-none-any.whl", hash = "sha256:da1885f0c10b60c03ed5eccbb6b68d6eff248d91976fcde348f395d54c9fd35e"}, - {file = "cssselect-1.2.0.tar.gz", hash = "sha256:666b19839cfaddb9ce9d36bfe4c969132c647b92fc9088c4e23f786b30f1b3dc"}, + {file = "commitizen-3.8.0-py3-none-any.whl", hash = "sha256:8ef33205c0feb85bb32127bb588b68ff63ddd2bc1e073198087acaeae5dc32cc"}, + {file = "commitizen-3.8.0.tar.gz", hash = "sha256:2307befb5477a173598f8217ad7bfeebb66b92e900c7606a523287fc9545b631"}, ] [[package]] @@ -457,15 +434,15 @@ files = [ [[package]] name = "griffe" -version = "0.35.2" +version = "0.36.1" requires_python = ">=3.8" summary = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." dependencies = [ "colorama>=0.4", ] files = [ - {file = "griffe-0.35.2-py3-none-any.whl", hash = "sha256:9650d6d0369c22f29f2c1bec9548ddc7f448f8ca38698a5799f92f736824e749"}, - {file = "griffe-0.35.2.tar.gz", hash = "sha256:84ecfe3df17454993b8dd485201566609ac6706a2eb22e3f402da2a39f9f6b5f"}, + {file = "griffe-0.36.1-py3-none-any.whl", hash = "sha256:859b653fcde0a0af0e841a0109bac2b63a2f683132ae1ec8dae5fa81e94617a0"}, + {file = "griffe-0.36.1.tar.gz", hash = "sha256:11df63f1c85f605c73e4485de70ec13784049695d228241b0b582364a20c0536"}, ] [[package]] @@ -605,16 +582,6 @@ files = [ {file = "ipython-8.15.0.tar.gz", hash = "sha256:2baeb5be6949eeebf532150f81746f8333e2ccce02de1c7eedde3f23ed5e9f1e"}, ] -[[package]] -name = "itsdangerous" -version = "2.1.2" -requires_python = ">=3.7" -summary = "Safely pass data to untrusted environments and back." -files = [ - {file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"}, - {file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"}, -] - [[package]] name = "jedi" version = "0.19.0" @@ -700,7 +667,7 @@ files = [ [[package]] name = "loguru" -version = "0.7.0" +version = "0.7.1" requires_python = ">=3.5" summary = "Python logging made (stupidly) simple" dependencies = [ @@ -708,49 +675,8 @@ dependencies = [ "win32-setctime>=1.0.0; sys_platform == \"win32\"", ] files = [ - {file = "loguru-0.7.0-py3-none-any.whl", hash = "sha256:b93aa30099fa6860d4727f1b81f8718e965bb96253fa190fab2077aaad6d15d3"}, - {file = "loguru-0.7.0.tar.gz", hash = "sha256:1612053ced6ae84d7959dd7d5e431a0532642237ec21f7fd83ac73fe539e03e1"}, -] - -[[package]] -name = "lxml" -version = "4.9.3" -requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" -summary = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." -files = [ - {file = "lxml-4.9.3-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:b86164d2cff4d3aaa1f04a14685cbc072efd0b4f99ca5708b2ad1b9b5988a991"}, - {file = "lxml-4.9.3-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:42871176e7896d5d45138f6d28751053c711ed4d48d8e30b498da155af39aebd"}, - {file = "lxml-4.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:ae8b9c6deb1e634ba4f1930eb67ef6e6bf6a44b6eb5ad605642b2d6d5ed9ce3c"}, - {file = "lxml-4.9.3-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:411007c0d88188d9f621b11d252cce90c4a2d1a49db6c068e3c16422f306eab8"}, - {file = "lxml-4.9.3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:cd47b4a0d41d2afa3e58e5bf1f62069255aa2fd6ff5ee41604418ca925911d76"}, - {file = "lxml-4.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0e2cb47860da1f7e9a5256254b74ae331687b9672dfa780eed355c4c9c3dbd23"}, - {file = "lxml-4.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1247694b26342a7bf47c02e513d32225ededd18045264d40758abeb3c838a51f"}, - {file = "lxml-4.9.3-cp310-cp310-win32.whl", hash = "sha256:cdb650fc86227eba20de1a29d4b2c1bfe139dc75a0669270033cb2ea3d391b85"}, - {file = "lxml-4.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:97047f0d25cd4bcae81f9ec9dc290ca3e15927c192df17331b53bebe0e3ff96d"}, - {file = "lxml-4.9.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:1f447ea5429b54f9582d4b955f5f1985f278ce5cf169f72eea8afd9502973dd5"}, - {file = "lxml-4.9.3-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:57d6ba0ca2b0c462f339640d22882acc711de224d769edf29962b09f77129cbf"}, - {file = "lxml-4.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:9767e79108424fb6c3edf8f81e6730666a50feb01a328f4a016464a5893f835a"}, - {file = "lxml-4.9.3-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:71c52db65e4b56b8ddc5bb89fb2e66c558ed9d1a74a45ceb7dcb20c191c3df2f"}, - {file = "lxml-4.9.3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d73d8ecf8ecf10a3bd007f2192725a34bd62898e8da27eb9d32a58084f93962b"}, - {file = "lxml-4.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0a3d3487f07c1d7f150894c238299934a2a074ef590b583103a45002035be120"}, - {file = "lxml-4.9.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e28c51fa0ce5674be9f560c6761c1b441631901993f76700b1b30ca6c8378d6"}, - {file = "lxml-4.9.3-cp311-cp311-win32.whl", hash = "sha256:0bfd0767c5c1de2551a120673b72e5d4b628737cb05414f03c3277bf9bed3305"}, - {file = "lxml-4.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:25f32acefac14ef7bd53e4218fe93b804ef6f6b92ffdb4322bb6d49d94cad2bc"}, - {file = "lxml-4.9.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6689a3d7fd13dc687e9102a27e98ef33730ac4fe37795d5036d18b4d527abd35"}, - {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:f6bdac493b949141b733c5345b6ba8f87a226029cbabc7e9e121a413e49441e0"}, - {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:05186a0f1346ae12553d66df1cfce6f251589fea3ad3da4f3ef4e34b2d58c6a3"}, - {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c2006f5c8d28dee289f7020f721354362fa304acbaaf9745751ac4006650254b"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-macosx_11_0_x86_64.whl", hash = "sha256:5c245b783db29c4e4fbbbfc9c5a78be496c9fea25517f90606aa1f6b2b3d5f7b"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:4fb960a632a49f2f089d522f70496640fdf1218f1243889da3822e0a9f5f3ba7"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:50670615eaf97227d5dc60de2dc99fb134a7130d310d783314e7724bf163f75d"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:9719fe17307a9e814580af1f5c6e05ca593b12fb7e44fe62450a5384dbf61b4b"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:3331bece23c9ee066e0fb3f96c61322b9e0f54d775fccefff4c38ca488de283a"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-macosx_11_0_x86_64.whl", hash = "sha256:ed667f49b11360951e201453fc3967344d0d0263aa415e1619e85ae7fd17b4e0"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8b77946fd508cbf0fccd8e400a7f71d4ac0e1595812e66025bac475a8e811694"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e4da8ca0c0c0aea88fd46be8e44bd49716772358d648cce45fe387f7b92374a7"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fe4bda6bd4340caa6e5cf95e73f8fea5c4bfc55763dd42f1b50a94c1b4a2fbd4"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:f3df3db1d336b9356dd3112eae5f5c2b8b377f3bc826848567f10bfddfee77e9"}, - {file = "lxml-4.9.3.tar.gz", hash = "sha256:48628bd53a426c9eb9bc066a923acaa0878d1e86129fd5359aee99285f4eed9c"}, + {file = "loguru-0.7.1-py3-none-any.whl", hash = "sha256:046bf970cb3cad77a28d607cbf042ac25a407db987a1e801c7f7e692469982f9"}, + {file = "loguru-0.7.1.tar.gz", hash = "sha256:7ba2a7d81b79a412b0ded69bd921e012335e80fd39937a633570f273a343579e"}, ] [[package]] @@ -763,16 +689,6 @@ files = [ {file = "Markdown-3.4.4.tar.gz", hash = "sha256:225c6123522495d4119a90b3a3ba31a1e87a70369e03f14799ea9c0d7183a3d6"}, ] -[[package]] -name = "markdown2" -version = "2.4.10" -requires_python = ">=3.5, <4" -summary = "A fast and complete Python implementation of Markdown" -files = [ - {file = "markdown2-2.4.10-py2.py3-none-any.whl", hash = "sha256:e6105800483783831f5dc54f827aa5b44eb137ecef5a70293d8ecfbb4109ecc6"}, - {file = "markdown2-2.4.10.tar.gz", hash = "sha256:cdba126d90dc3aef6f4070ac342f974d63f415678959329cc7909f96cc235d72"}, -] - [[package]] name = "markupsafe" version = "2.1.3" @@ -889,27 +805,25 @@ files = [ [[package]] name = "mkdocs-material" -version = "9.2.6" +version = "9.2.8" requires_python = ">=3.7" summary = "Documentation that simply works" dependencies = [ - "babel>=2.10.3", - "colorama>=0.4", - "jinja2>=3.0", - "lxml>=4.6", - "markdown>=3.2", - "mkdocs-material-extensions>=1.1", - "mkdocs>=1.5.2", - "paginate>=0.5.6", - "pygments>=2.14", - "pymdown-extensions>=9.9.1", - "readtime>=2.0", - "regex>=2022.4.24", - "requests>=2.26", + "babel~=2.12", + "colorama~=0.4", + "jinja2~=3.1", + "markdown~=3.4", + "mkdocs-material-extensions~=1.1", + "mkdocs~=1.5", + "paginate~=0.5", + "pygments~=2.16", + "pymdown-extensions~=10.3", + "regex~=2023.8", + "requests~=2.31", ] files = [ - {file = "mkdocs_material-9.2.6-py3-none-any.whl", hash = "sha256:84bc7e79c1d0bae65a77123efd5ef74731b8c3671601c7962c5db8dba50a65ad"}, - {file = "mkdocs_material-9.2.6.tar.gz", hash = "sha256:3806c58dd112e7b9677225e2021035ddbe3220fbd29d9dc812aa7e01f70b5e0a"}, + {file = "mkdocs_material-9.2.8-py3-none-any.whl", hash = "sha256:6bc8524f8047a4f060d6ab0925b9d7cb61b3b5e6d5ca8a8e8085f8bfdeca1b71"}, + {file = "mkdocs_material-9.2.8.tar.gz", hash = "sha256:ec839dc5eaf42d8525acd1d6420fd0a0583671a4f98a9b3ff7897ae8628dbc2d"}, ] [[package]] @@ -924,8 +838,8 @@ files = [ [[package]] name = "mkdocstrings" -version = "0.22.0" -requires_python = ">=3.7" +version = "0.23.0" +requires_python = ">=3.8" summary = "Automatic documentation from sources, for MkDocs." dependencies = [ "Jinja2>=2.11.1", @@ -936,13 +850,13 @@ dependencies = [ "pymdown-extensions>=6.3", ] files = [ - {file = "mkdocstrings-0.22.0-py3-none-any.whl", hash = "sha256:2d4095d461554ff6a778fdabdca3c00c468c2f1459d469f7a7f622a2b23212ba"}, - {file = "mkdocstrings-0.22.0.tar.gz", hash = "sha256:82a33b94150ebb3d4b5c73bab4598c3e21468c79ec072eff6931c8f3bfc38256"}, + {file = "mkdocstrings-0.23.0-py3-none-any.whl", hash = "sha256:051fa4014dfcd9ed90254ae91de2dbb4f24e166347dae7be9a997fe16316c65e"}, + {file = "mkdocstrings-0.23.0.tar.gz", hash = "sha256:d9c6a37ffbe7c14a7a54ef1258c70b8d394e6a33a1c80832bce40b9567138d1c"}, ] [[package]] name = "mkdocstrings-python" -version = "1.6.0" +version = "1.6.2" requires_python = ">=3.8" summary = "A Python handler for mkdocstrings." dependencies = [ @@ -950,8 +864,8 @@ dependencies = [ "mkdocstrings>=0.20", ] files = [ - {file = "mkdocstrings_python-1.6.0-py3-none-any.whl", hash = "sha256:06f116112b335114372f2554b1bf61b709c74ab72605010e1605c1086932dffe"}, - {file = "mkdocstrings_python-1.6.0.tar.gz", hash = "sha256:6164ccaa6e488abc2a8fbccdfd1f21948c2c344d3f347847783a5d1c6fa2bfbf"}, + {file = "mkdocstrings_python-1.6.2-py3-none-any.whl", hash = "sha256:cf560df975faf712808e44c1c2e52b8267f17bc89c8b23e7b9bfe679561adf4d"}, + {file = "mkdocstrings_python-1.6.2.tar.gz", hash = "sha256:edf0f81899bee8024971bf2c18b6fc17f66085992f01c72840a3ee0ee42113fb"}, ] [[package]] @@ -1003,16 +917,6 @@ files = [ {file = "numpy-1.25.2.tar.gz", hash = "sha256:fd608e19c8d7c55021dffd43bfe5492fab8cc105cc8986f813f8c3c048b38760"}, ] -[[package]] -name = "oauthlib" -version = "3.2.2" -requires_python = ">=3.6" -summary = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" -files = [ - {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"}, - {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"}, -] - [[package]] name = "openpyxl" version = "3.0.9" @@ -1028,7 +932,7 @@ files = [ [[package]] name = "osm-fieldwork" -version = "0.3.6rc0" +version = "0.3.6rc1" requires_python = ">=3.10" summary = "Processing field data from OpenDataKit to OpenStreetMap format." dependencies = [ @@ -1055,20 +959,16 @@ dependencies = [ "xmltodict>=0.13.0", ] files = [ - {file = "osm-fieldwork-0.3.6rc0.tar.gz", hash = "sha256:198bf2663f74db7b72b12cacf4177c1631a4527e0ba7ab4a05d2c1db6897f82c"}, - {file = "osm_fieldwork-0.3.6rc0-py3-none-any.whl", hash = "sha256:17cc5cbfc6d5d54953e46632955a00b4133c0acafe87bfd50e2a52caaafd41b5"}, + {file = "osm-fieldwork-0.3.6rc1.tar.gz", hash = "sha256:e3d2ad2e4ebc88055ee4529cc724af44f860ff9b638a2e618c306c80a0bba7e1"}, + {file = "osm_fieldwork-0.3.6rc1-py3-none-any.whl", hash = "sha256:def14e0244cec02df5e2936371f09c8202a53a5e1e737ecba89ee70f3fa8ea11"}, ] [[package]] name = "osm-login-python" -version = "0.0.4" -git = "https://github.com/spwoodcock/osm-login-python.git" -revision = "2687688715b7053f90ec17e93ef64865830cf6e8" +version = "1.0.0" summary = "Use OSM Token exchange with OAuth2.0 for python projects" -dependencies = [ - "itsdangerous~=2.1.2", - "pydantic~=2.3.0", - "requests-oauthlib~=1.3.1", +files = [ + {file = "osm-login-python-1.0.0.tar.gz", hash = "sha256:5a3fc6622567950f8431460b4ba821379a8b5fe01ce0c7adcce79229ee71d1a5"}, ] [[package]] @@ -1189,7 +1089,7 @@ files = [ [[package]] name = "pre-commit" -version = "3.3.3" +version = "3.4.0" requires_python = ">=3.8" summary = "A framework for managing and maintaining multi-language pre-commit hooks." dependencies = [ @@ -1200,8 +1100,8 @@ dependencies = [ "virtualenv>=20.10.0", ] files = [ - {file = "pre_commit-3.3.3-py2.py3-none-any.whl", hash = "sha256:10badb65d6a38caff29703362271d7dca483d01da88f9d7e05d0b97171c136cb"}, - {file = "pre_commit-3.3.3.tar.gz", hash = "sha256:a2256f489cd913d575c145132ae196fe335da32d91a8294b7afe6622335dd023"}, + {file = "pre_commit-3.4.0-py2.py3-none-any.whl", hash = "sha256:96d529a951f8b677f730a7212442027e8ba53f9b04d217c4c67dc56c393ad945"}, + {file = "pre_commit-3.4.0.tar.gz", hash = "sha256:6bbd5129a64cad4c0dfaeeb12cd8f7ea7e15b77028d985341478c8af3c759522"}, ] [[package]] @@ -1391,16 +1291,16 @@ files = [ [[package]] name = "pymdown-extensions" -version = "10.2.1" -requires_python = ">=3.7" +version = "10.3" +requires_python = ">=3.8" summary = "Extension pack for Python Markdown." dependencies = [ "markdown>=3.2", "pyyaml", ] files = [ - {file = "pymdown_extensions-10.2.1-py3-none-any.whl", hash = "sha256:bded105eb8d93f88f2f821f00108cb70cef1269db6a40128c09c5f48bfc60ea4"}, - {file = "pymdown_extensions-10.2.1.tar.gz", hash = "sha256:d0c534b4a5725a4be7ccef25d65a4c97dba58b54ad7c813babf0eb5ba9c81591"}, + {file = "pymdown_extensions-10.3-py3-none-any.whl", hash = "sha256:77a82c621c58a83efc49a389159181d570e370fff9f810d3a4766a75fc678b66"}, + {file = "pymdown_extensions-10.3.tar.gz", hash = "sha256:94a0d8a03246712b64698af223848fd80aaf1ae4c4be29c8c61939b0467b5722"}, ] [[package]] @@ -1412,19 +1312,6 @@ files = [ {file = "pypng-0.20220715.0.tar.gz", hash = "sha256:739c433ba96f078315de54c0db975aee537cbc3e1d0ae4ed9aab0ca1e427e2c1"}, ] -[[package]] -name = "pyquery" -version = "2.0.0" -summary = "A jquery-like library for python" -dependencies = [ - "cssselect>=1.2.0", - "lxml>=2.1", -] -files = [ - {file = "pyquery-2.0.0-py3-none-any.whl", hash = "sha256:8dfc9b4b7c5f877d619bbae74b1898d5743f6ca248cfd5d72b504dd614da312f"}, - {file = "pyquery-2.0.0.tar.gz", hash = "sha256:963e8d4e90262ff6d8dec072ea97285dc374a2f69cad7776f4082abcf6a1d8ae"}, -] - [[package]] name = "pysmartdl" version = "1.3.4" @@ -1436,7 +1323,7 @@ files = [ [[package]] name = "pytest" -version = "7.4.0" +version = "7.4.1" requires_python = ">=3.7" summary = "pytest: simple powerful testing with Python" dependencies = [ @@ -1448,8 +1335,8 @@ dependencies = [ "tomli>=1.0.0; python_version < \"3.11\"", ] files = [ - {file = "pytest-7.4.0-py3-none-any.whl", hash = "sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32"}, - {file = "pytest-7.4.0.tar.gz", hash = "sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a"}, + {file = "pytest-7.4.1-py3-none-any.whl", hash = "sha256:460c9a59b14e27c602eb5ece2e47bec99dc5fc5f6513cf924a7d03a578991b1f"}, + {file = "pytest-7.4.1.tar.gz", hash = "sha256:2f2301e797521b23e4d2585a0a3d7b5e50fdddaaf7e7d6773ea26ddb17c213ab"}, ] [[package]] @@ -1487,11 +1374,11 @@ files = [ [[package]] name = "pytz" -version = "2023.3" +version = "2023.3.post1" summary = "World timezone definitions, modern and historical" files = [ - {file = "pytz-2023.3-py2.py3-none-any.whl", hash = "sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb"}, - {file = "pytz-2023.3.tar.gz", hash = "sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588"}, + {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"}, + {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"}, ] [[package]] @@ -1631,19 +1518,6 @@ files = [ {file = "rapidfuzz-3.2.0.tar.gz", hash = "sha256:448d031d9960fea7826d42bd4284156fc68d3b55a6946eb34ca5c6acf960577b"}, ] -[[package]] -name = "readtime" -version = "3.0.0" -summary = "Calculates the time some text takes the average human to read, based on Medium's read time forumula" -dependencies = [ - "beautifulsoup4>=4.0.1", - "markdown2>=2.4.3", - "pyquery>=1.2", -] -files = [ - {file = "readtime-3.0.0.tar.gz", hash = "sha256:76c5a0d773ad49858c53b42ba3a942f62fbe20cc8c6f07875797ac7dc30963a9"}, -] - [[package]] name = "regex" version = "2023.8.8" @@ -1698,20 +1572,6 @@ files = [ {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, ] -[[package]] -name = "requests-oauthlib" -version = "1.3.1" -requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -summary = "OAuthlib authentication support for Requests." -dependencies = [ - "oauthlib>=3.0.0", - "requests>=2.0.0", -] -files = [ - {file = "requests-oauthlib-1.3.1.tar.gz", hash = "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a"}, - {file = "requests_oauthlib-1.3.1-py2.py3-none-any.whl", hash = "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5"}, -] - [[package]] name = "segno" version = "1.5.2" @@ -1737,12 +1597,12 @@ files = [ [[package]] name = "setuptools" -version = "68.1.2" +version = "68.2.0" requires_python = ">=3.8" summary = "Easily download, build, install, upgrade, and uninstall Python packages" files = [ - {file = "setuptools-68.1.2-py3-none-any.whl", hash = "sha256:3d8083eed2d13afc9426f227b24fd1659489ec107c0e86cec2ffdde5c92e790b"}, - {file = "setuptools-68.1.2.tar.gz", hash = "sha256:3d4dfa6d95f1b101d695a6160a7626e15583af71a5f52176efa5d39a054d475d"}, + {file = "setuptools-68.2.0-py3-none-any.whl", hash = "sha256:af3d5949030c3f493f550876b2fd1dd5ec66689c4ee5d5344f009746f71fd5a8"}, + {file = "setuptools-68.2.0.tar.gz", hash = "sha256:00478ca80aeebeecb2f288d3206b0de568df5cd2b8fada1209843cc9a8d88a48"}, ] [[package]] @@ -1792,16 +1652,6 @@ files = [ {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, ] -[[package]] -name = "soupsieve" -version = "2.4.1" -requires_python = ">=3.7" -summary = "A modern CSS selector implementation for Beautiful Soup." -files = [ - {file = "soupsieve-2.4.1-py3-none-any.whl", hash = "sha256:1c1bfee6819544a3447586c889157365a27e10d88cde3ad3da0cf0ddf646feb8"}, - {file = "soupsieve-2.4.1.tar.gz", hash = "sha256:89d12b2d5dfcd2c9e8c22326da9d9aa9cb3dfab0a83a024f05704076ee8d35ea"}, -] - [[package]] name = "sqlalchemy" version = "2.0.20" diff --git a/src/backend/pyproject.toml b/src/backend/pyproject.toml index f8cf183c55..8fe3720f80 100644 --- a/src/backend/pyproject.toml +++ b/src/backend/pyproject.toml @@ -39,11 +39,11 @@ dependencies = [ "xmltodict==0.13.0", "SQLAlchemy-Utils==0.41.1", "segno==1.5.2", - "osm-fieldwork==0.3.6rc0", + "osm-fieldwork==0.3.6rc1", "sentry-sdk==1.30.0", "py-cpuinfo==9.0.0", "loguru>=0.7.0", - "osm-login-python @ git+https://github.com/spwoodcock/osm-login-python.git", + "osm-login-python>=1.0.0", ] requires-python = ">=3.10,<3.12" readme = "../../README.md" diff --git a/src/frontend/fmtm_openlayer_map/package-lock.json b/src/frontend/fmtm_openlayer_map/package-lock.json index f1695f14e9..0225019885 100644 --- a/src/frontend/fmtm_openlayer_map/package-lock.json +++ b/src/frontend/fmtm_openlayer_map/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "css-minimizer-webpack-plugin": "^5.0.0", "env-cmd": "^10.1.0", - "ol": "^7.2.2", + "ol": "^7.5.2", "ol-layerswitcher": "^4.1.1", "react": "^17.0.2", "react-dom": "^17.0.2", @@ -5805,13 +5805,13 @@ "dev": true }, "node_modules/ol": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/ol/-/ol-7.2.2.tgz", - "integrity": "sha512-eqJ1hhVQQ3Ap4OhYq9DRu5pz9RMpLhmoTauDoIqpn7logVi1AJE+lXjEHrPrTSuZYjtFbMgqr07sxoLNR65nrw==", + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/ol/-/ol-7.5.2.tgz", + "integrity": "sha512-HJbb3CxXrksM6ct367LsP3N+uh+iBBMdP3DeGGipdV9YAYTP0vTJzqGnoqQ6C2IW4qf8krw9yuyQbc9fjOIaOQ==", "dependencies": { "earcut": "^2.2.3", "geotiff": "^2.0.7", - "ol-mapbox-style": "^9.2.0", + "ol-mapbox-style": "^10.1.0", "pbf": "3.2.1", "rbush": "^3.0.1" }, @@ -5829,12 +5829,13 @@ } }, "node_modules/ol-mapbox-style": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/ol-mapbox-style/-/ol-mapbox-style-9.5.0.tgz", - "integrity": "sha512-ArYzeuZ4dOYZ6UnhXK30kNBdouZay/r6LzLsFGnvJ86lxY6ShJu2FtcKCFGCOZoJZYyoLCStHYOHcRrVLDaJ0Q==", + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/ol-mapbox-style/-/ol-mapbox-style-10.7.0.tgz", + "integrity": "sha512-S/UdYBuOjrotcR95Iq9AejGYbifKeZE85D9VtH11ryJLQPTZXZSW1J5bIXcr4AlAH6tyjPPHTK34AdkwB32Myw==", "dependencies": { "@mapbox/mapbox-gl-style-spec": "^13.23.1", - "mapbox-to-css-font": "^2.4.1" + "mapbox-to-css-font": "^2.4.1", + "ol": "^7.3.0" } }, "node_modules/on-finished": { @@ -12919,13 +12920,13 @@ "dev": true }, "ol": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/ol/-/ol-7.2.2.tgz", - "integrity": "sha512-eqJ1hhVQQ3Ap4OhYq9DRu5pz9RMpLhmoTauDoIqpn7logVi1AJE+lXjEHrPrTSuZYjtFbMgqr07sxoLNR65nrw==", + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/ol/-/ol-7.5.2.tgz", + "integrity": "sha512-HJbb3CxXrksM6ct367LsP3N+uh+iBBMdP3DeGGipdV9YAYTP0vTJzqGnoqQ6C2IW4qf8krw9yuyQbc9fjOIaOQ==", "requires": { "earcut": "^2.2.3", "geotiff": "^2.0.7", - "ol-mapbox-style": "^9.2.0", + "ol-mapbox-style": "^10.1.0", "pbf": "3.2.1", "rbush": "^3.0.1" } @@ -12937,12 +12938,13 @@ "requires": {} }, "ol-mapbox-style": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/ol-mapbox-style/-/ol-mapbox-style-9.5.0.tgz", - "integrity": "sha512-ArYzeuZ4dOYZ6UnhXK30kNBdouZay/r6LzLsFGnvJ86lxY6ShJu2FtcKCFGCOZoJZYyoLCStHYOHcRrVLDaJ0Q==", + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/ol-mapbox-style/-/ol-mapbox-style-10.7.0.tgz", + "integrity": "sha512-S/UdYBuOjrotcR95Iq9AejGYbifKeZE85D9VtH11ryJLQPTZXZSW1J5bIXcr4AlAH6tyjPPHTK34AdkwB32Myw==", "requires": { "@mapbox/mapbox-gl-style-spec": "^13.23.1", - "mapbox-to-css-font": "^2.4.1" + "mapbox-to-css-font": "^2.4.1", + "ol": "^7.3.0" } }, "on-finished": { diff --git a/src/frontend/fmtm_openlayer_map/package.json b/src/frontend/fmtm_openlayer_map/package.json index 6832ae967f..44da05d3cc 100755 --- a/src/frontend/fmtm_openlayer_map/package.json +++ b/src/frontend/fmtm_openlayer_map/package.json @@ -39,7 +39,7 @@ "dependencies": { "css-minimizer-webpack-plugin": "^5.0.0", "env-cmd": "^10.1.0", - "ol": "^7.2.2", + "ol": "^7.5.2", "ol-layerswitcher": "^4.1.1", "react": "^17.0.2", "react-dom": "^17.0.2", diff --git a/src/frontend/main/package-lock.json b/src/frontend/main/package-lock.json index ab8a510bb8..28d52958d2 100644 --- a/src/frontend/main/package-lock.json +++ b/src/frontend/main/package-lock.json @@ -60,6 +60,8 @@ "eslint-plugin-react": "^7.33.0", "html-webpack-plugin": "^5.3.2", "jest-environment-jsdom": "^29.6.1", + "ol": "^8.0.0", + "ol-layerswitcher": "^4.1.1", "postcss": "^8.4.28", "postcss-loader": "^4.1.0", "prettier": "^3.0.0", @@ -3217,6 +3219,12 @@ "node": ">= 8" } }, + "node_modules/@petamoriken/float16": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/@petamoriken/float16/-/float16-3.8.3.tgz", + "integrity": "sha512-an2OZ7/6er9Jja8EDUvU/tmtGIutdlb6LwXOwgjzoCjDRAsUd8sRZMBjoPEy78Xa9iOp+Kglk2CHgVwZuZbWbw==", + "dev": true + }, "node_modules/@pkgr/utils": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", @@ -7125,6 +7133,12 @@ "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", "dev": true }, + "node_modules/earcut": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", + "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==", + "dev": true + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -8544,6 +8558,24 @@ "node": ">=6.9.0" } }, + "node_modules/geotiff": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/geotiff/-/geotiff-2.0.7.tgz", + "integrity": "sha512-FKvFTNowMU5K6lHYY2f83d4lS2rsCNdpUC28AX61x9ZzzqPNaWFElWv93xj0eJFaNyOYA63ic5OzJ88dHpoA5Q==", + "dev": true, + "dependencies": { + "@petamoriken/float16": "^3.4.7", + "lerc": "^3.0.0", + "pako": "^2.0.4", + "parse-headers": "^2.0.2", + "quick-lru": "^6.1.1", + "web-worker": "^1.2.0", + "xml-utils": "^1.0.2" + }, + "engines": { + "node": ">=10.19" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -9095,6 +9127,26 @@ "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==", "dev": true }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/ignore": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", @@ -11868,6 +11920,12 @@ "shell-quote": "^1.7.3" } }, + "node_modules/lerc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lerc/-/lerc-3.0.0.tgz", + "integrity": "sha512-Rm4J/WaHhRa93nCN2mwWDZFoRVF18G1f47C+kvQWyHGEZxFpTUi73p7lMVSAndyxGt6lJ2/CFbOcf9ra5p8aww==", + "dev": true + }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -12543,6 +12601,31 @@ "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", "dev": true }, + "node_modules/ol": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/ol/-/ol-8.0.0.tgz", + "integrity": "sha512-tWDVFlt4ylZJeByD/Q5vSWPxEMoHy1tyyWT+LFDlGmGl6p9kmxtCfGAIupwUGrc5ifWsy+gDbg6QGd1Qw0y3HA==", + "dev": true, + "dependencies": { + "earcut": "^2.2.3", + "geotiff": "^2.0.7", + "pbf": "3.2.1", + "rbush": "^3.0.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/openlayers" + } + }, + "node_modules/ol-layerswitcher": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ol-layerswitcher/-/ol-layerswitcher-4.1.1.tgz", + "integrity": "sha512-uqIqZCr/23GoOIOl2T1iPzcVBPweJZgkVHTrn8DLNCa3cCuX+aJzz0DhkA+qJjm5NqXO5uCvB7zpk9vWtRWmsQ==", + "dev": true, + "peerDependencies": { + "ol": ">=5.0.0" + } + }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -12674,6 +12757,12 @@ "node": ">=6" } }, + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", + "dev": true + }, "node_modules/param-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", @@ -12695,6 +12784,12 @@ "node": ">=6" } }, + "node_modules/parse-headers": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.5.tgz", + "integrity": "sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA==", + "dev": true + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -12798,6 +12893,19 @@ "node": ">=8" } }, + "node_modules/pbf": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz", + "integrity": "sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==", + "dev": true, + "dependencies": { + "ieee754": "^1.1.12", + "resolve-protobuf-schema": "^2.1.0" + }, + "bin": { + "pbf": "bin/pbf" + } + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -13629,6 +13737,12 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/protocol-buffers-schema": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", + "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==", + "dev": true + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -13725,6 +13839,24 @@ } ] }, + "node_modules/quick-lru": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.2.tgz", + "integrity": "sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/quickselect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", + "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==", + "dev": true + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -13766,6 +13898,15 @@ "node": ">= 0.8" } }, + "node_modules/rbush": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/rbush/-/rbush-3.0.1.tgz", + "integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==", + "dev": true, + "dependencies": { + "quickselect": "^2.0.0" + } + }, "node_modules/react": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", @@ -14168,6 +14309,15 @@ "node": ">=4" } }, + "node_modules/resolve-protobuf-schema": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", + "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", + "dev": true, + "dependencies": { + "protocol-buffers-schema": "^3.3.1" + } + }, "node_modules/resolve.exports": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", @@ -15883,6 +16033,12 @@ "minimalistic-assert": "^1.0.0" } }, + "node_modules/web-worker": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.2.0.tgz", + "integrity": "sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==", + "dev": true + }, "node_modules/webidl-conversions": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", @@ -16849,6 +17005,12 @@ "node": ">=12" } }, + "node_modules/xml-utils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/xml-utils/-/xml-utils-1.7.0.tgz", + "integrity": "sha512-bWB489+RQQclC7A9OW8e5BzbT8Tu//jtAOvkYwewFr+Q9T9KDGvfzC1lp0pYPEQPEoPQLDkmxkepSC/2gIAZGw==", + "dev": true + }, "node_modules/xmlchars": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", @@ -19070,6 +19232,12 @@ "fastq": "^1.6.0" } }, + "@petamoriken/float16": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/@petamoriken/float16/-/float16-3.8.3.tgz", + "integrity": "sha512-an2OZ7/6er9Jja8EDUvU/tmtGIutdlb6LwXOwgjzoCjDRAsUd8sRZMBjoPEy78Xa9iOp+Kglk2CHgVwZuZbWbw==", + "dev": true + }, "@pkgr/utils": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", @@ -21972,6 +22140,12 @@ "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", "dev": true }, + "earcut": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", + "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==", + "dev": true + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -23032,6 +23206,21 @@ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" }, + "geotiff": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/geotiff/-/geotiff-2.0.7.tgz", + "integrity": "sha512-FKvFTNowMU5K6lHYY2f83d4lS2rsCNdpUC28AX61x9ZzzqPNaWFElWv93xj0eJFaNyOYA63ic5OzJ88dHpoA5Q==", + "dev": true, + "requires": { + "@petamoriken/float16": "^3.4.7", + "lerc": "^3.0.0", + "pako": "^2.0.4", + "parse-headers": "^2.0.2", + "quick-lru": "^6.1.1", + "web-worker": "^1.2.0", + "xml-utils": "^1.0.2" + } + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -23439,6 +23628,12 @@ "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==", "dev": true }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true + }, "ignore": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", @@ -25395,6 +25590,12 @@ "shell-quote": "^1.7.3" } }, + "lerc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lerc/-/lerc-3.0.0.tgz", + "integrity": "sha512-Rm4J/WaHhRa93nCN2mwWDZFoRVF18G1f47C+kvQWyHGEZxFpTUi73p7lMVSAndyxGt6lJ2/CFbOcf9ra5p8aww==", + "dev": true + }, "leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -25895,6 +26096,25 @@ "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", "dev": true }, + "ol": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/ol/-/ol-8.0.0.tgz", + "integrity": "sha512-tWDVFlt4ylZJeByD/Q5vSWPxEMoHy1tyyWT+LFDlGmGl6p9kmxtCfGAIupwUGrc5ifWsy+gDbg6QGd1Qw0y3HA==", + "dev": true, + "requires": { + "earcut": "^2.2.3", + "geotiff": "^2.0.7", + "pbf": "3.2.1", + "rbush": "^3.0.1" + } + }, + "ol-layerswitcher": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ol-layerswitcher/-/ol-layerswitcher-4.1.1.tgz", + "integrity": "sha512-uqIqZCr/23GoOIOl2T1iPzcVBPweJZgkVHTrn8DLNCa3cCuX+aJzz0DhkA+qJjm5NqXO5uCvB7zpk9vWtRWmsQ==", + "dev": true, + "requires": {} + }, "on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -25987,6 +26207,12 @@ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" }, + "pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", + "dev": true + }, "param-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", @@ -26005,6 +26231,12 @@ "callsites": "^3.0.0" } }, + "parse-headers": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.5.tgz", + "integrity": "sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA==", + "dev": true + }, "parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -26080,6 +26312,16 @@ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" }, + "pbf": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz", + "integrity": "sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==", + "dev": true, + "requires": { + "ieee754": "^1.1.12", + "resolve-protobuf-schema": "^2.1.0" + } + }, "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -26599,6 +26841,12 @@ } } }, + "protocol-buffers-schema": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", + "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==", + "dev": true + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -26658,6 +26906,18 @@ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" }, + "quick-lru": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.2.tgz", + "integrity": "sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ==", + "dev": true + }, + "quickselect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", + "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==", + "dev": true + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -26692,6 +26952,15 @@ } } }, + "rbush": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/rbush/-/rbush-3.0.1.tgz", + "integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==", + "dev": true, + "requires": { + "quickselect": "^2.0.0" + } + }, "react": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", @@ -26983,6 +27252,15 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" }, + "resolve-protobuf-schema": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", + "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", + "dev": true, + "requires": { + "protocol-buffers-schema": "^3.3.1" + } + }, "resolve.exports": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", @@ -28249,6 +28527,12 @@ "minimalistic-assert": "^1.0.0" } }, + "web-worker": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.2.0.tgz", + "integrity": "sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==", + "dev": true + }, "webidl-conversions": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", @@ -28979,6 +29263,12 @@ "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", "dev": true }, + "xml-utils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/xml-utils/-/xml-utils-1.7.0.tgz", + "integrity": "sha512-bWB489+RQQclC7A9OW8e5BzbT8Tu//jtAOvkYwewFr+Q9T9KDGvfzC1lp0pYPEQPEoPQLDkmxkepSC/2gIAZGw==", + "dev": true + }, "xmlchars": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", diff --git a/src/frontend/main/package.json b/src/frontend/main/package.json index aa4cb0cbd8..842192f871 100755 --- a/src/frontend/main/package.json +++ b/src/frontend/main/package.json @@ -32,6 +32,8 @@ "eslint-plugin-react": "^7.33.0", "html-webpack-plugin": "^5.3.2", "jest-environment-jsdom": "^29.6.1", + "ol": "^8.0.0", + "ol-layerswitcher": "^4.1.1", "postcss": "^8.4.28", "postcss-loader": "^4.1.0", "prettier": "^3.0.0", diff --git a/src/frontend/main/src/api/CreateProjectService.ts b/src/frontend/main/src/api/CreateProjectService.ts index 229a82d378..abb97a01fa 100755 --- a/src/frontend/main/src/api/CreateProjectService.ts +++ b/src/frontend/main/src/api/CreateProjectService.ts @@ -323,15 +323,21 @@ const GetIndividualProjectDetails: Function = (url: string, payload: any) => { }; }; -const TaskSplittingPreviewService: Function = (url: string, fileUpload: any, no_of_buildings: string) => { +const TaskSplittingPreviewService: Function = ( + url: string, + fileUpload: any, + no_of_buildings: string, + isCustomDataExtract: boolean, +) => { return async (dispatch) => { dispatch(CreateProjectActions.GetTaskSplittingPreviewLoading(true)); - const getTaskSplittingGeojson = async (url, fileUpload) => { + const getTaskSplittingGeojson = async (url, fileUpload, isCustomDataExtract) => { try { const taskSplittingFileFormData = new FormData(); taskSplittingFileFormData.append('upload', fileUpload); taskSplittingFileFormData.append('no_of_buildings', no_of_buildings); + taskSplittingFileFormData.append('has_data_extracts', isCustomDataExtract); const getTaskSplittingResponse = await axios.post(url, taskSplittingFileFormData); const resp: OrganisationListModel = getTaskSplittingResponse.data; @@ -344,7 +350,7 @@ const TaskSplittingPreviewService: Function = (url: string, fileUpload: any, no_ } }; - await getTaskSplittingGeojson(url, fileUpload); + await getTaskSplittingGeojson(url, fileUpload, isCustomDataExtract); }; }; const PatchProjectDetails: Function = (url: string, payload: any) => { diff --git a/src/frontend/main/src/api/HomeService.ts b/src/frontend/main/src/api/HomeService.ts index c1372a5f92..cfb6bf82ae 100755 --- a/src/frontend/main/src/api/HomeService.ts +++ b/src/frontend/main/src/api/HomeService.ts @@ -1,6 +1,7 @@ import axios from 'axios'; import { HomeActions } from '../store/slices/HomeSlice'; import { HomeProjectCardModel } from '../models/home/homeModel'; +import environment from '../environment'; export const HomeSummaryService: Function = (url: string) => { return async (dispatch) => { @@ -9,22 +10,17 @@ export const HomeSummaryService: Function = (url: string) => { const fetchHomeSummaries = async (url) => { try { const fetchHomeData = await axios.get(url); - const resp: HomeProjectCardModel = fetchHomeData.data; - // let resp = new Array(10).fill({ - // id: 1234, - // priority: "MEDIUM", - // title: "Naivasha", - // location_str: "Lake Naivasha, Kenya", - // description: "HOT demo", - // total_tasks: 320, - // tasks_mapped: 0, - // tasks_validated: 0, - // contributors: 0, - // tasks_bad: 0, - // priority_str: 'HIGH', - // num_contributors: 12 - // }) - dispatch(HomeActions.SetHomeProjectSummary(resp)); + const resp: any = fetchHomeData.data; + const fetchProjectCentroid = await axios.get(`${environment.baseApiUrl}/projects/centroid/`); + const projectCentroidResp: any = fetchProjectCentroid.data; + const addedProjectCentroidOnSummary = resp.map((project) => { + const findProjectId = projectCentroidResp.find((payload) => payload.id === project.id); + if (findProjectId) { + return { ...project, centroid: findProjectId.centroid }; + } + return project; + }); + dispatch(HomeActions.SetHomeProjectSummary(addedProjectCentroidOnSummary)); dispatch(HomeActions.HomeProjectLoading(false)); } catch (error) { dispatch(HomeActions.HomeProjectLoading(false)); @@ -34,3 +30,22 @@ export const HomeSummaryService: Function = (url: string) => { await fetchHomeSummaries(url); }; }; +export const ProjectCentroidService: Function = (url: string) => { + return async (dispatch) => { + dispatch(HomeActions.SetProjectCentroidLoading(true)); + + const fetchProjectCentroid = async (url) => { + try { + const fetchHomeData = await axios.get(url); + const resp: HomeProjectCardModel = fetchHomeData.data; + + dispatch(HomeActions.SetProjectCentroid(resp)); + dispatch(HomeActions.SetProjectCentroidLoading(false)); + } catch (error) { + dispatch(HomeActions.SetProjectCentroidLoading(false)); + } + }; + + await fetchProjectCentroid(url); + }; +}; diff --git a/src/frontend/main/src/assets/images/red_marker.png b/src/frontend/main/src/assets/images/red_marker.png new file mode 100644 index 0000000000..927dc0d0e8 Binary files /dev/null and b/src/frontend/main/src/assets/images/red_marker.png differ diff --git a/src/frontend/main/src/components/MapComponent/OpenLayersComponent/LayerSwitcher/index.js b/src/frontend/main/src/components/MapComponent/OpenLayersComponent/LayerSwitcher/index.js new file mode 100644 index 0000000000..23dab94e14 --- /dev/null +++ b/src/frontend/main/src/components/MapComponent/OpenLayersComponent/LayerSwitcher/index.js @@ -0,0 +1,154 @@ +/* eslint-disable react/prop-types */ +/* eslint-disable no-unused-vars */ +import 'ol-layerswitcher/dist/ol-layerswitcher.css'; +// import "../../node_modules/ol-layerswitcher/dist/ol-layerswitcher.css"; +import LayerGroup from 'ol/layer/Group'; +import LayerTile from 'ol/layer/Tile'; +import SourceOSM from 'ol/source/OSM'; +import LayerSwitcher from 'ol-layerswitcher'; +import React,{ useEffect } from 'react'; + +import { XYZ } from 'ol/source'; + +// const mapboxOutdoors = new MapboxVector({ +// styleUrl: 'mapbox://styles/geovation/ckpicg3of094w17nyqyd2ziie', +// accessToken: 'pk.eyJ1IjoiZ2VvdmF0aW9uIiwiYSI6ImNrcGljOXBwbTBoc3oyb3BjMGsxYW9wZ2EifQ.euYtUXb6HJGLHkj4Wi3gjA', +// }); +const osm = (visible) => + new LayerTile({ + title: 'OSM', + type: 'base', + visible: visible === 'osm', + source: new SourceOSM(), + }); +const none = (visible) => + new LayerTile({ + title: 'None', + type: 'base', + visible: visible === 'none', + source: null, + }); +const bingMaps = (visible) => + new LayerTile({ + title: 'Satellite', + type: 'base', + visible: visible === 'satellite', + source: new XYZ({ + url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', + }), + // source: new BingMaps({ + // key: 'AoTlmaazzog43ImdKts9HVztFzUI4PEOT0lmo2V4q7f20rfVorJGAgDREKmfQAgd', + // imagerySet: 'Aerial', + // // use maxZoom 19 to see stretched tiles instead of the BingMaps + // // "no photos at this zoom level" tiles + // maxZoom: 19, + // crossOrigin: 'Anonymous', + // }), + }); +const mapboxMap = (visible) => + new LayerTile({ + title: 'Mapbox Light', + type: 'base', + visible: visible === 'mapbox', + source: new XYZ({ + attributions: + 'Tiles © ArcGIS', + url: 'https://api.mapbox.com/styles/v1/nishon-naxa/ckgkuy7y08rpi19qk46sces9c/tiles/256/{z}/{x}/{y}@2x?access_token=pk.eyJ1IjoibmlzaG9uLW5heGEiLCJhIjoiY2xhYnhwbzN0MDUxYTN1bWhvcWxocWlpaSJ9.0FarR4aPxb7F9BHP31msww', + layer: 'topoMap', + maxZoom: 19, + crossOrigin: 'Anonymous', + }), + }); +const mapboxOutdoors = (visible) => + new LayerTile({ + title: 'Mapbox Outdoors', + type: 'base', + visible: visible === 'outdoors', + source: new XYZ({ + attributions: + 'Tiles © ArcGIS', + // url: + // 'https://api.mapbox.com/styles/v1/geovation/ckpicg3of094w17nyqyd2ziie/tiles/256/{z}/{x}/{y}@2x?access_token=pk.eyJ1IjoiZ2VvdmF0aW9uIiwiYSI6ImNrcGljOXBwbTBoc3oyb3BjMGsxYW9wZ2EifQ.euYtUXb6HJGLHkj4Wi3gjA', + url: 'https://api.mapbox.com/styles/v1/naxa-np/cl50pm1l5001814qpncu8s4ib/tiles/256/{z}/{x}/{y}@2x?access_token=pk.eyJ1IjoibmF4YS1ucCIsImEiOiJja2E5bGp0ZDQwdHE4MnJxdnhmcGxsdGpuIn0.kB42E50iZFlFPcQiqQMClw', + layer: 'topoMap', + maxZoom: 19, + crossOrigin: 'Anonymous', + }), + }); + +const topoMap = (visible = false) => + new LayerTile({ + title: 'Topo Map', + type: 'base', + visible, + source: new XYZ({ + attributions: + 'Tiles © ArcGIS', + url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}', + layer: 'topoMap', + maxZoom: 19, + crossOrigin: 'Anonymous', + }), + }); + +const monochrome = (visible = false) => + new LayerTile({ + title: 'Monochrome', + type: 'base', + visible, + source: new XYZ({ + attributions: + 'Tiles © ArcGIS', + url: 'https://api.mapbox.com/styles/v1/geovation/ckqxdp7rd0t5d17lfuxm259c7/tiles/256/{z}/{x}/{y}@2x?access_token=pk.eyJ1IjoiZ2VvdmF0aW9uIiwiYSI6ImNrcGljOXBwbTBoc3oyb3BjMGsxYW9wZ2EifQ.euYtUXb6HJGLHkj4Wi3gjA', + layer: 'topomap', + maxZoom: 19, + crossOrigin: 'Anonymous', + }), + }); + +const monochromeMidNight = (visible = false) => + new LayerTile({ + title: 'Monochrome Midnight', + type: 'base', + visible, + source: new XYZ({ + attributions: + 'Tiles © ArcGIS', + url: 'https://api.mapbox.com/styles/v1/geovation/ckqxdsqu93r0417mcbdc102nb/tiles/256/{z}/{x}/{y}@2x?access_token=pk.eyJ1IjoiZ2VvdmF0aW9uIiwiYSI6ImNrcGljOXBwbTBoc3oyb3BjMGsxYW9wZ2EifQ.euYtUXb6HJGLHkj4Wi3gjA', + layer: 'topomap', + maxZoom: 19, + crossOrigin: 'Anonymous', + }), + }); + + +const LayerSwitcherControl = ({ map, visible = 'osm' }) => { + useEffect(() => { + if (!map) return; + + const baseMaps = new LayerGroup({ + title: 'Base maps', + layers: [bingMaps(visible), osm(visible), mapboxMap(visible), mapboxOutdoors(visible), none(visible)], + }); + + const layerSwitcher = new LayerSwitcher({ + reverse: true, + groupSelectStyle: 'group', + }); + map.addLayer(baseMaps); + map.addControl(layerSwitcher); + // eslint-disable-next-line consistent-return + return () => { + map.removeLayer(baseMaps); + }; + }, [map, visible]); + + return null; +}; + +export default LayerSwitcherControl; diff --git a/src/frontend/main/src/components/MapComponent/OpenLayersComponent/Layers/VectorLayer.js b/src/frontend/main/src/components/MapComponent/OpenLayersComponent/Layers/VectorLayer.js new file mode 100644 index 0000000000..6ad35c59d5 --- /dev/null +++ b/src/frontend/main/src/components/MapComponent/OpenLayersComponent/Layers/VectorLayer.js @@ -0,0 +1,261 @@ +/* eslint-disable no-console */ +/* eslint-disable consistent-return */ +/* eslint-disable react/forbid-prop-types */ +import React, { useEffect, useState } from 'react'; +import PropTypes from 'prop-types'; +import { get } from 'ol/proj'; +import Style from 'ol/style/Style'; +import Stroke from 'ol/style/Stroke'; +import GeoJSON from 'ol/format/GeoJSON'; +import { Vector as VectorSource } from 'ol/source'; +import OLVectorLayer from 'ol/layer/Vector'; +import { defaultStyles, getStyles } from '../helpers/styleUtils'; +import { isExtentValid } from '../helpers/layerUtils'; +import { Draw, Modify, Select, defaults as defaultInteractions } from 'ol/interaction.js'; + +const selectElement = 'singleselect'; + +const selectedCountry = new Style({ + stroke: new Stroke({ + color: '#008099', + width: 3, + }), + // fill: new Fill({ + // color: 'rgba(200,20,20,0.4)', + // }), +}); +let selection = {}; +const layerViewProperties = { + padding: [50, 50, 50, 50], + duration: 900, + constrainResolution: true, +}; + +const VectorLayer = ({ + map, + geojson, + style, + zIndex, + zoomToLayer = false, + visibleOnMap = true, + properties, + viewProperties, + hoverEffect, + mapOnClick, + setStyle, + onModify, + onDraw, +}) => { + const [vectorLayer, setVectorLayer] = useState(null); + + useEffect(() => () => map && vectorLayer && map.removeLayer(vectorLayer), [map, vectorLayer]); + + // Modify Feature + useEffect(() => { + if (!map) return; + if (!vectorLayer) return; + if (!onModify) return; + const select = new Select({ + wrapX: false, + }); + const vectorLayerSource = vectorLayer.getSource(); + const modify = new Modify({ + // features: select.getFeatures(), + source: vectorLayerSource, + }); + modify.on('modifyend', function (e) { + var geoJSONFormat = new GeoJSON(); + + var geoJSONString = geoJSONFormat.writeFeatures(vectorLayer.getSource().getFeatures(), { + dataProjection: 'EPSG:4326', + featureProjection: 'EPSG:3857', + }); + + onModify(geoJSONString); + }); + map.addInteraction(modify); + map.addInteraction(select); + + return () => { + // map.removeInteraction(defaultInteractions().extend([select, modify])) + }; + }, [map, vectorLayer, onModify]); + // Modify Feature + useEffect(() => { + if (!map) return; + // if(!vectorLayer) return; + if (!onDraw) return; + const source = new VectorSource({ wrapX: false }); + + const vector = new OLVectorLayer({ + source: source, + }); + const draw = new Draw({ + source: source, + type: 'Polygon', + }); + draw.on('drawend', function (e) { + const feature = e.feature; + const geojsonFormat = new GeoJSON(); + const newGeojson = geojsonFormat.writeFeature(feature, { + dataProjection: 'EPSG:4326', + featureProjection: 'EPSG:3857', + }); + + // Call your function here with the GeoJSON as an argument + onDraw(newGeojson); + // var geoJSONFormat = new GeoJSON(); + + // var geoJSONString = geoJSONFormat.writeFeatures(vectorLayer.getSource().getFeatures(),{ dataProjection: 'EPSG:4326', featureProjection: 'EPSG:3857'}); + // console.log(geoJSONString,'geojsonString'); + // onDraw(geoJSONString); + }); + map.addInteraction(draw); + + return () => { + map.removeInteraction(draw); + }; + }, [map, vectorLayer, onDraw]); + + useEffect(() => { + if (!map) return; + if (!geojson) return; + + const vectorLyr = new OLVectorLayer({ + source: new VectorSource({ + features: new GeoJSON().readFeatures(geojson, { + featureProjection: get('EPSG:3857'), + }), + }), + declutter: true, + }); + map.on('click', (evt) => { + var pixel = evt.pixel; + const feature = map.forEachFeatureAtPixel(pixel, function (feature, layer) { + if (layer === vectorLyr) { + return feature; + } + }); + + // Perform an action if a feature is found + if (feature) { + // Do something with the feature + console.log('Clicked feature:', feature.getProperties()); + // dispatch() + mapOnClick(feature.getProperties()); + } + }); + setVectorLayer(vectorLyr); + }, [map, geojson]); + + useEffect(() => { + if (!map || !vectorLayer) return; + if (visibleOnMap) { + map.addLayer(vectorLayer); + } else { + map.removeLayer(vectorLayer); + } + }, [map, vectorLayer, visibleOnMap]); + + useEffect(() => { + if (!map || !vectorLayer || !visibleOnMap || !setStyle) return; + vectorLayer.setStyle(setStyle); + }, [map, setStyle, vectorLayer, visibleOnMap]); + + useEffect(() => { + if (!vectorLayer || !style.visibleOnMap) return; + vectorLayer.setStyle((feature, resolution) => getStyles({ style, feature, resolution })); + }, [vectorLayer, style]); + + useEffect(() => { + if (!vectorLayer) return; + vectorLayer.setZIndex(zIndex); + }, [vectorLayer, zIndex]); + + useEffect(() => { + if (!map || !vectorLayer || !zoomToLayer) return; + const extent = vectorLayer.getSource().getExtent(); + if (!isExtentValid(extent)) return; + map.getView().fit(extent, viewProperties); + }, [map, vectorLayer, zoomToLayer]); + + // set properties to features for identifying popup + useEffect(() => { + if (!vectorLayer || !properties) return; + const features = vectorLayer.getSource().getFeatures(); + features.forEach((feat) => { + feat.setProperties(properties); + }); + }, [vectorLayer, properties]); + + // style on hover + useEffect(() => { + if (!map) return null; + if (!vectorLayer) return null; + if (!hoverEffect) return null; + const selectionLayer = new OLVectorLayer({ + map, + renderMode: 'vector', + source: vectorLayer.getSource(), + // eslint-disable-next-line consistent-return + style: (feature) => { + if (feature.getId() in selection) { + return selectedCountry; + } + // return stylex; + }, + }); + function pointerMovefn(event) { + vectorLayer.getFeatures(event.pixel).then((features) => { + console.log(selection, 'selection'); + if (!features.length) { + selection = {}; + hoverEffect(undefined, vectorLayer); + + selectionLayer.changed(); + return; + } + const feature = features[0]; + if (!feature) { + return; + } + const fid = feature.getId(); + if (selectElement.startsWith('singleselect')) { + selection = {}; + } + // add selected feature to lookup + selection[fid] = feature; + hoverEffect(selection[fid]); + + selectionLayer.changed(); + }); + } + map.on('pointermove', pointerMovefn); + return () => { + map.un('pointermove', pointerMovefn); + }; + }, [vectorLayer]); + return null; +}; + +VectorLayer.defaultProps = { + zIndex: 0, + style: { ...defaultStyles }, + zoomToLayer: false, + viewProperties: layerViewProperties, + mapOnClick: () => {}, + onModify: null, +}; + +VectorLayer.propTypes = { + geojson: PropTypes.object.isRequired, + style: PropTypes.object, + zIndex: PropTypes.number, + zoomToLayer: PropTypes.bool, + viewProperties: PropTypes.object, + mapOnClick: PropTypes.func, + onModify: PropTypes.func, + // Context: PropTypes.object.isRequired, +}; + +export default VectorLayer; diff --git a/src/frontend/main/src/components/MapComponent/OpenLayersComponent/Layers/VectorTileLayer.js b/src/frontend/main/src/components/MapComponent/OpenLayersComponent/Layers/VectorTileLayer.js new file mode 100644 index 0000000000..bd4afdeaa4 --- /dev/null +++ b/src/frontend/main/src/components/MapComponent/OpenLayersComponent/Layers/VectorTileLayer.js @@ -0,0 +1,215 @@ +import React,{ useEffect, useMemo } from 'react'; +// import * as olExtent from 'ol/extent'; +import VectorTile from 'ol/layer/VectorTile'; +import MVT from 'ol/format/MVT'; +import VectorTileSource from 'ol/source/VectorTile'; +import { transformExtent } from 'ol/proj'; +import Stroke from 'ol/style/Stroke'; +import Style from 'ol/style/Style'; +import { getStyles, defaultStyles } from '../helpers/styleUtils'; +import { isExtentValid } from '../helpers/layerUtils'; + +const selectElement = 'singleselect'; + +const selectedCountry = new Style({ + stroke: new Stroke({ + color: 'rgba(255,255,255,0.8)', + width: 3, + }), + // fill: new Fill({ + // color: 'rgba(200,20,20,0.4)', + // }), +}); +let selection = {}; +const VectorTileLayer = ({ + map, + url, + style = { ...defaultStyles }, + zIndex = 1, + visibleOnMap = true, + authToken, + setStyle, + zoomToLayer = false, + bbox = null, + hoverEffect, + // properties, +}) => { + const vectorTileLayer = useMemo( + () => + new VectorTile({ + renderMode: 'hybrid', + // declutter: true, + }), + [], + ); + + vectorTileLayer.setProperties({ name: 'site' }); + + // add source to layer + useEffect(() => { + if (!map) return; + + const requestHeader = new Headers(); + if (authToken) { + requestHeader.append('Authorization', `Token ${authToken}`); + } + + const vectorTileSource = new VectorTileSource({ + // format: new MVT({ featureClass: Feature }), + format: new MVT({ idProperty: 'id' }), + maxZoom: 19, + url, + transition: 0, + + tileLoadFunction: (tile, vtUrl) => { + tile.setLoader((extent, resolution, projection) => { + fetch(vtUrl, { + headers: requestHeader, + }).then((response) => { + response.arrayBuffer().then((data) => { + const format = tile.getFormat(); + const features = format.readFeatures(data, { + extent, + featureProjection: projection, + }); + tile.setFeatures(features); + }); + }); + }); + }, + }); + vectorTileLayer.setSource(vectorTileSource); + }, [map, url, authToken, vectorTileLayer]); + + // add layer to map + useEffect(() => { + if (!map) return; + if (visibleOnMap) { + map.addLayer(vectorTileLayer); + } else { + map.removeLayer(vectorTileLayer); + } + }, [map, visibleOnMap, vectorTileLayer]); + + // // set style + useEffect(() => { + if (!map || !visibleOnMap || !setStyle) return; + vectorTileLayer.setStyle(setStyle); + }, [map, setStyle, vectorTileLayer, visibleOnMap]); + + // set style + useEffect(() => { + if (!map || !visibleOnMap || setStyle) return; + vectorTileLayer.setStyle((feature, resolution) => getStyles({ style, feature, resolution })); + }, [map, style, vectorTileLayer, visibleOnMap, setStyle]); + + // set z-index + useEffect(() => { + if (!map) return; + vectorTileLayer.setZIndex(zIndex); + }, [map, zIndex, vectorTileLayer]); + + // // set properties to features for identifying popup + // useEffect(() => { + // if (!vectorTileLayer || !properties) return; + // vectorTileLayer.getSource().on('tileloadend', (evt) => { + // // const z = evt.tile.getTileCoord()[0]; + // const features = evt.tile.getFeatures(); + // features.forEach((feat) => { + // feat.setProperties(properties); + // }); + // }); + // // // console.log(vectorTileLayer.getSource(), 'sourcex'); + // // const features = vectorTileLayer.getSource().getFeatures(); + // // features.forEach((feat) => { + // // feat.setProperties(properties); + // // }); + // }, [vectorTileLayer, properties]); + + // useEffect(() => { + // const featuresForZ = []; + // vectorTileLayer.getSource().on('tileloadend', evt => { + // const z = evt.tile.getTileCoord()[0]; + // const feature = evt.tile.getFeatures(); + // if (!Array.isArray(featuresForZ[z])) { + // featuresForZ[z] = []; + // } + // featuresForZ[z] = featuresForZ[z].concat(feature); + // }); + // setFeatures(featuresForZ); + // }, []); + + // useEffect(() => { + // if (!map) return; + // const extent = olExtent.createEmpty(); + // if (isExtentValid(extent)) { + // map.getView().fit(extent, { + // padding: [50, 50, 50, 50], + // duration: 500, + // constrainResolution: true, + // }); + // } + // }, [map]); + + // style on hover + useEffect(() => { + if (!map) return null; + if (!hoverEffect) return null; + const selectionLayer = new VectorTile({ + map, + renderMode: 'vector', + source: vectorTileLayer.getSource(), + // eslint-disable-next-line consistent-return + style: (feature) => { + if (feature.getId() in selection) { + return selectedCountry; + } + // return stylex; + }, + }); + function pointerMovefn(event) { + vectorTileLayer.getFeatures(event.pixel).then((features) => { + if (!features.length) { + selection = {}; + selectionLayer.changed(); + return; + } + const feature = features[0]; + if (!feature) { + return; + } + const fid = feature.getId(); + if (selectElement.startsWith('singleselect')) { + selection = {}; + } + // add selected feature to lookup + selection[fid] = feature; + + selectionLayer.changed(); + }); + } + map.on('pointermove', pointerMovefn); + return () => { + map.un('pointermove', pointerMovefn); + }; + }, [vectorTileLayer]); + + // zoom to layer + useEffect(() => { + if (!map || !vectorTileLayer || !zoomToLayer || !bbox) return; + const transformedExtent = transformExtent(bbox, 'EPSG:4326', 'EPSG:3857'); + if (!isExtentValid(transformedExtent)) return; + map.getView().fit(transformedExtent, { + padding: [50, 50, 50, 50], + duration: 900, + constrainResolution: true, + }); + }, [map, vectorTileLayer, zoomToLayer, bbox]); + + // cleanup + useEffect(() => () => map && map.removeLayer(vectorTileLayer), [map, vectorTileLayer]); + + return null; +}; + +export default VectorTileLayer; diff --git a/src/frontend/main/src/components/MapComponent/OpenLayersComponent/Layers/index.js b/src/frontend/main/src/components/MapComponent/OpenLayersComponent/Layers/index.js new file mode 100644 index 0000000000..8292e0e2a4 --- /dev/null +++ b/src/frontend/main/src/components/MapComponent/OpenLayersComponent/Layers/index.js @@ -0,0 +1,3 @@ +export { default as VectorTileLayer } from './VectorTileLayer'; + +export { default as VectorLayer } from './VectorLayer'; diff --git a/src/frontend/main/src/components/MapComponent/OpenLayersComponent/MapContainer/index.jsx b/src/frontend/main/src/components/MapComponent/OpenLayersComponent/MapContainer/index.jsx new file mode 100644 index 0000000000..b7abafa404 --- /dev/null +++ b/src/frontend/main/src/components/MapComponent/OpenLayersComponent/MapContainer/index.jsx @@ -0,0 +1,37 @@ +/* eslint-disable no-nested-ternary */ +/* eslint-disable react/jsx-props-no-spreading */ +/* eslint-disable react/jsx-no-useless-fragment */ +import React from 'react'; +import PropTypes from 'prop-types'; +import '../map.scss'; + +const { Children, cloneElement, forwardRef } = React; + +const MapContainer = forwardRef(({ children, mapInstance, ...rest }, ref) => { + const childrenCount = Children.count(children); + const props = { + map: mapInstance, + }; + return ( +
+ {childrenCount < 1 ? ( + <> + ) : childrenCount > 1 ? ( + Children.map(children, (child) => (child ? cloneElement(child, { ...props }) : <>)) + ) : ( + cloneElement(children, { ...props }) + )} +
+ ); +}); + +MapContainer.defaultProps = { + mapInstance: null, +}; + +MapContainer.propTypes = { + children: PropTypes.node.isRequired, + mapInstance: PropTypes.oneOfType([PropTypes.object, PropTypes.number]), +}; + +export default MapContainer; diff --git a/src/frontend/main/src/components/MapComponent/OpenLayersComponent/Popup/index.jsx b/src/frontend/main/src/components/MapComponent/OpenLayersComponent/Popup/index.jsx new file mode 100644 index 0000000000..9457978187 --- /dev/null +++ b/src/frontend/main/src/components/MapComponent/OpenLayersComponent/Popup/index.jsx @@ -0,0 +1,72 @@ +/* eslint-disable react/prop-types */ +/* eslint-disable jsx-a11y/anchor-has-content */ +/* eslint-disable func-names */ +/* eslint-disable jsx-a11y/control-has-associated-label */ +import React, { useEffect } from 'react'; +import Overlay from 'ol/Overlay'; +import './popup.scss'; + +const Popup = ({ map, except }) => { + useEffect(() => { + if (!map) return; + + const container = document.getElementById('popup'); + const content = document.getElementById('popup-content'); + const closer = document.getElementById('popup-closer'); + + const overlay = new Overlay({ + element: container, + autoPan: true, + autoPanAnimation: { + duration: 250, + }, + }); + + closer.onclick = function () { + overlay.setPosition(undefined); + closer.blur(); + return false; + }; + + map.on('singleclick', (evt) => { + const { coordinate } = evt; + const features = map.getFeaturesAtPixel(evt.pixel); + if (features.length < 1) { + overlay.setPosition(undefined); + closer.blur(); + content.innerHTML = ''; + return; + } + const properties = features[0].getProperties(); + const { layerId } = properties; + if (layerId === except) { + overlay.setPosition(undefined); + closer.blur(); + return; + } + content.innerHTML = ` + ${Object.keys(properties).reduce( + (str, key) => + `${str} + + + + `, + '', + )} +
${key}${properties[key]} +
`; + overlay.setPosition(coordinate); + map.addOverlay(overlay); + }); + }, [map]); + + return ( +