diff --git a/apps/project/graphql/queries.py b/apps/project/graphql/queries.py index a5584c57..a7d922ab 100644 --- a/apps/project/graphql/queries.py +++ b/apps/project/graphql/queries.py @@ -13,7 +13,7 @@ TestValidateAoiObjectsResponse, TestValidateTaskingManagerProjectResponse, ) -from apps.project.models import Organization, Project, ProjectAsset, ProjectTypeEnum +from apps.project.models import Organization, Project, ProjectAsset, ProjectAssetInputTypeEnum, ProjectTypeEnum from project_types.base.project import ValidationException from project_types.validate.project import ValidateProject from utils import fields @@ -47,6 +47,7 @@ def _get_raster_tile_server_type(enum: RasterTileServerNameEnumWithoutCustom): credits=config["credits"], min_zoom=config["min_zoom"], max_zoom=config["max_zoom"], + disabled=config["disabled"], ) def _get_vector_tile_server_type(enum: VectorTileServerNameEnumWithoutCustom): @@ -65,9 +66,8 @@ def _get_vector_tile_server_type(enum: VectorTileServerNameEnumWithoutCustom): raster=[ _get_raster_tile_server_type(RasterTileServerNameEnum.BING), _get_raster_tile_server_type(RasterTileServerNameEnum.MAPBOX), - # NOTE: Disabled because it's not working for 2+ years - # _get_raster_tile_server_type(RasterTileServerNameEnum.MAXAR_STANDARD), - # _get_raster_tile_server_type(RasterTileServerNameEnum.MAXAR_PREMIUM), + _get_raster_tile_server_type(RasterTileServerNameEnum.MAXAR_STANDARD), + _get_raster_tile_server_type(RasterTileServerNameEnum.MAXAR_PREMIUM), _get_raster_tile_server_type(RasterTileServerNameEnum.ESRI), _get_raster_tile_server_type(RasterTileServerNameEnum.ESRI_BETA), ], @@ -93,9 +93,9 @@ def default_custom_options(self, project_type: ProjectTypeEnum) -> list[CustomOp @strawberry.field(extensions=[IsAuthenticated()]) def test_aoi_objects( self, - project_id: strawberry.ID | None, - asset_id: strawberry.ID | None, - ohsome_filter: str | None, + project_id: strawberry.ID, + asset_id: strawberry.ID, + ohsome_filter: str, ) -> TestValidateAoiObjectsResponse: response = TestValidateAoiObjectsResponse( project_id=project_id, @@ -103,19 +103,20 @@ def test_aoi_objects( ohsome_filter=ohsome_filter, ) - if project_id is None: - return response.generate_error("project_id is required to test aoi elements") - - if asset_id is None: - return response.generate_error("asset_id is required to test aoi elements") - - if ohsome_filter is None: - return response.generate_error("ohsome_filter is required to test aoi elements") - try: - object_count = ValidateProject.test_ohsome_objects_from_aoi_asset( - project_id, - asset_id, + aoi_asset = ( + ProjectAsset.usable_objects() + .filter( + id=asset_id, + type=ProjectAsset.Type.INPUT, + input_type=ProjectAssetInputTypeEnum.AOI_GEOMETRY, + project_id=project_id, + ) + .first() + ) + + object_count, _ = ValidateProject.test_ohsome_objects_from_aoi_asset( + aoi_asset, ohsome_filter, ) @@ -129,22 +130,16 @@ def test_aoi_objects( @strawberry.field(extensions=[IsAuthenticated()]) def test_tasking_manager_project( self, - hot_tm_id: fields.PydanticId | None, - ohsome_filter: str | None, + hot_tm_id: fields.PydanticId, + ohsome_filter: str, ) -> TestValidateTaskingManagerProjectResponse: response = TestValidateTaskingManagerProjectResponse( hot_tm_id=hot_tm_id, ohsome_filter=ohsome_filter, ) - if hot_tm_id is None: - return response.generate_error("hot_tm_id is required to test HOT project aoi elements") - - if ohsome_filter is None: - return response.generate_error("ohsome_filter is required to test HOT project aoi elements") - try: - object_count = ValidateProject.test_tasking_manager_project( + object_count, _, _ = ValidateProject.test_tasking_manager_project( hot_tm_id, ohsome_filter, ) diff --git a/apps/project/graphql/types/project_types/base.py b/apps/project/graphql/types/project_types/base.py index 4608dce6..13cf5ffc 100644 --- a/apps/project/graphql/types/project_types/base.py +++ b/apps/project/graphql/types/project_types/base.py @@ -47,6 +47,7 @@ class RasterTileServerType: min_zoom: int | None max_zoom: int | None credits: str + disabled: bool @strawberry.type diff --git a/apps/project/graphql/types/project_types/validate.py b/apps/project/graphql/types/project_types/validate.py index bd3e9a68..5daa9c80 100644 --- a/apps/project/graphql/types/project_types/validate.py +++ b/apps/project/graphql/types/project_types/validate.py @@ -22,6 +22,7 @@ class ValidateTestAoiResponse: object_count: int | None = None ohsome_filter: str | None = None + # TODO(tnagorra): This needs to be generic. Create a base response class. def generate_error(self, message: str = DEFAULT_TEST_RESPONSE_ERROR_MESSAGE): self.ok = False self.error = message diff --git a/apps/project/tests/e2e_create_validate_project_test.py b/apps/project/tests/e2e_create_validate_project_test.py index 356a289b..d3d0fb02 100644 --- a/apps/project/tests/e2e_create_validate_project_test.py +++ b/apps/project/tests/e2e_create_validate_project_test.py @@ -296,7 +296,7 @@ class Mutation: class Query: TEST_AOI_OBJECTS = """ - query TestAoiObjects($assetId: ID, $projectId: ID, $ohsomeFilter: String) { + query TestAoiObjects($assetId: ID!, $projectId: ID!, $ohsomeFilter: String!) { testAoiObjects(assetId: $assetId, projectId: $projectId, ohsomeFilter: $ohsomeFilter) { ok error @@ -309,7 +309,7 @@ class Query: """ TEST_TASKING_MANAGER_PROJECT = """ - query TestTaskingManagerProject($hotTmId: String, $ohsomeFilter: String) { + query TestTaskingManagerProject($hotTmId: String!, $ohsomeFilter: String!) { testTaskingManagerProject(hotTmId: $hotTmId, ohsomeFilter: $ohsomeFilter) { ok error diff --git a/assets b/assets index 41037a51..432bbd5a 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 41037a51ebbb0ba162d60c97adaa818f80598fe4 +Subproject commit 432bbd5a38ac5bc13bb3f8e2f30e36ccfc3cc41c diff --git a/project_types/street/project.py b/project_types/street/project.py index cbc82bcd..d03cc827 100644 --- a/project_types/street/project.py +++ b/project_types/street/project.py @@ -91,9 +91,9 @@ def validate(self) -> Grouping[StreetFeature]: raise Exception("Could not find AOI geometry asset") asset_specific_data = AoiGeometryAssetProperty.model_validate(aoi_asset.asset_type_specifics) - allowed_area = 20 - if asset_specific_data.area > allowed_area: - raise base_project.ValidationException(f"Area for AOI Geometry must be less than {allowed_area} sq. km") + MAX_AOI_AREA = 20 + if asset_specific_data.area > MAX_AOI_AREA: + raise base_project.ValidationException(f"Area for AOI Geometry must be less than {MAX_AOI_AREA} sq. km") with aoi_asset.file.open() as aoi_file: aoi_geojson = json.loads(aoi_file.read()) diff --git a/project_types/tile_map_service/base/project.py b/project_types/tile_map_service/base/project.py index de5b9c89..493d1837 100644 --- a/project_types/tile_map_service/base/project.py +++ b/project_types/tile_map_service/base/project.py @@ -213,9 +213,9 @@ def validate(self): raise Exception("Could not find AOI geometry asset") asset_specific_data = AoiGeometryAssetProperty.model_validate(aoi_asset.asset_type_specifics) - allowed_area = 5 * (4 ** (23 - self.project_type_specifics.zoom_level)) - if asset_specific_data.area > allowed_area: - raise base_project.ValidationException(f"Area for AOI Geometry must be less than {allowed_area} sq. km") + max_aoi_area = 5 * (4 ** (23 - self.project_type_specifics.zoom_level)) + if asset_specific_data.area > max_aoi_area: + raise base_project.ValidationException(f"Area for AOI Geometry must be less than {max_aoi_area} sq. km") extension = Path(aoi_asset.file.name).suffix with tempfile.NamedTemporaryFile(suffix=extension, dir=Config.TEMP_DIR) as temp_file: diff --git a/project_types/validate/project.py b/project_types/validate/project.py index 71f0d27b..0a7cefc1 100644 --- a/project_types/validate/project.py +++ b/project_types/validate/project.py @@ -4,7 +4,6 @@ from typing import Any import requests -import strawberry from django.contrib.gis.geos import GEOSGeometry from django.core.files.base import ContentFile from django.db import models @@ -82,7 +81,7 @@ def check_validate_data(self) -> typing.Self: if self.aoi_geometry is None: raise ValueError("AOI Geometry File is required") if self.ohsome_filter is None: - raise ValueError("Ohsome filter is required for AOI GeoJson File") + raise ValueError("OHSOME filter is required for AOI GeoJson File") return self case ValidateObjectSourceTypeEnum.OBJECT_GEOJSON_URL: if self.object_geojson_url is None: @@ -92,7 +91,7 @@ def check_validate_data(self) -> typing.Self: if self.tasking_manager_project_id is None: raise ValueError("Tasking Manager Project ID is required") if self.ohsome_filter is None: - raise ValueError("Ohsome filter is required") + raise ValueError("OHSOME filter is required") return self @@ -131,57 +130,74 @@ def __init__(self, project: Project): super().__init__(project) @staticmethod - def validate_geojson_aoi(geo_json: dict): # type: ignore[reportMissingTypeArgument] - try: - _, geometry_collection = convert_json_dict_to_geometry_collection(geo_json) - except Exception as e: - raise base_project.ValidationException( - "GeoJSON does not contain a valid feature collection of polygon or multi-polygon", - ) from e - - area_km2 = get_area_of_geometry(geometry_collection) - allowed_area = 20 - - if area_km2 > allowed_area: - raise base_project.ValidationException(f"Area for AOI Geometry must be less than {allowed_area} sq. km") - - return PydanticFeatureCollection.model_validate(geo_json) + def _validate_geojson_aoi_area(area_km2: float) -> float: + MAX_AOI_AREA = 500 + if area_km2 > MAX_AOI_AREA: + raise base_project.ValidationException(f"Area for AOI Geometry must be less than {MAX_AOI_AREA} sq. km") + return area_km2 @staticmethod - def validate_object_count(count: int | None) -> int: + def _validate_object_count(count: int | None) -> int: if count is None or count <= 0: raise base_project.ValidationException( "AOI does not contain objects from selected filter.", ) - allowed_count = 100000 - - if count > allowed_count: + MAX_OBJECTS_COUNT = 100_000 + if count > MAX_OBJECTS_COUNT: raise base_project.ValidationException( - f"AOI contains more than 100,000 objects. -> {count}", + f"AOI contains more than {MAX_OBJECTS_COUNT} objects. -> {count}", ) return count @staticmethod - def test_ohsome_objects_from_aoi_asset(project_id: strawberry.ID, asset_id: strawberry.ID, ohsome_filter: str) -> int: - aoi_asset = ( - ProjectAsset.usable_objects() - .filter( - id=asset_id, - type=ProjectAsset.Type.INPUT, - input_type=ProjectAssetInputTypeEnum.AOI_GEOMETRY, - project_id=project_id, + def test_geojson_url_project(url: str): + logger.info("Fetching object geojson from %s", url) + + # FIXME(frozenhelium): use predefined timeout duration + # FIXME(tnagorra): handle timeout error + response = requests.get(url, timeout=500) + if response.status_code != 200: + raise base_project.ValidationException( + f"Failed to fetch object geojson from {url}", ) - .first() + + logger.info("Successfully fetched object geojson from %s", url) + + try: + geojson = response.json() + except Exception as e: + raise base_project.ValidationException("GeoJSON URL did not respond with valid JSON") from e + + try: + features, geometry_collection = convert_json_dict_to_geometry_collection(geojson) + except Exception as e: + raise base_project.ValidationException( + "GeoJSON URL did not respond with a valid feature collection of polygon or multi-polygon", + ) from e + + return ( + ValidateProject._validate_object_count(len(features)), + features, + geometry_collection, ) + @staticmethod + def test_ohsome_objects_from_aoi_asset( + aoi_asset: ProjectAsset | None, + ohsome_filter: str, + ): if not aoi_asset: - raise Exception("Could not find AOI geometry asset") + # NOTE: This error scenario is not expected. + logger.error("Could not find AOI geometry asset") + raise base_project.ValidationException("Could not find AOI geometry asset") + + asset_specific_data = AoiGeometryAssetProperty.model_validate(aoi_asset.asset_type_specifics) + ValidateProject._validate_geojson_aoi_area(asset_specific_data.area) with aoi_asset.file.open() as aoi_file: aoi_geojson = json.loads(aoi_file.read()) - feature_collection = PydanticFeatureCollection.model_validate(aoi_geojson) try: @@ -192,17 +208,19 @@ def test_ohsome_objects_from_aoi_asset(project_id: strawberry.ID, asset_id: stra except Exception as e: raise base_project.ValidationException("Failed to get object_count from ohsome") from e - return ValidateProject.validate_object_count(object_count) + return ( + ValidateProject._validate_object_count(object_count), + aoi_geojson, + ) @staticmethod def test_tasking_manager_project( hot_tm_id: custom_fields.PydanticId, ohsome_filter: str, - ) -> int: + ): hot_tm_url = f"{Config.HOT_TASKING_MANAGER_PROJECT_API_LINK}projects/{hot_tm_id}/queries/aoi/?as_file=false" logger.info("Fetching AOI geojson on HOT from %s", hot_tm_url) - # FIXME(frozenhelium): duplicated logic from _validate_tasking_manager aoi_result = requests.get(hot_tm_url, timeout=500) if aoi_result.status_code != 200: raise base_project.ValidationException( @@ -232,7 +250,18 @@ def test_tasking_manager_project( ], } - feature_collection = ValidateProject.validate_geojson_aoi(aoi_geojson) + try: + _, geometry_collection = convert_json_dict_to_geometry_collection(aoi_geojson) + except Exception as e: + raise base_project.ValidationException( + "GeoJSON does not contain a valid feature collection of polygon or multi-polygon", + ) from e + + area_km2 = get_area_of_geometry(geometry_collection) + ValidateProject._validate_geojson_aoi_area(area_km2) + + feature_collection = PydanticFeatureCollection.model_validate(aoi_geojson) + try: object_count = get_object_count_from_ohsome( feature_collection.model_dump_json(), @@ -241,7 +270,11 @@ def test_tasking_manager_project( except Exception as e: raise base_project.ValidationException("Failed to get object_count from ohsome") from e - return ValidateProject.validate_object_count(object_count) + return ( + ValidateProject._validate_object_count(object_count), + aoi_geojson, + aoi_result, + ) @typing.override def get_aoi_geometry_asset(self) -> ProjectAsset | None: @@ -257,7 +290,7 @@ def get_aoi_geometry_asset(self) -> ProjectAsset | None: project_id=self.project.pk, ) - def _get_object_geometry_from_ohsome(self, geojson: dict): # type: ignore[reportMissingTypeArgument] + def _get_object_geometry_from_ohsome(self, geojson: dict[typing.Any, typing.Any]): try: feature_collection = PydanticFeatureCollection.model_validate(geojson) except Exception as e: @@ -291,7 +324,7 @@ def _get_object_geometry_from_ohsome(self, geojson: dict): # type: ignore[repor # FIXME(frozenhelium): verify if object count validation is required object_count = len(features) - ValidateProject.validate_object_count(object_count) + ValidateProject._validate_object_count(object_count) return features except Exception as e: @@ -300,27 +333,22 @@ def _get_object_geometry_from_ohsome(self, geojson: dict): # type: ignore[repor ) from e def _validate_aoi_geojson_file(self): - if self.project_type_specifics.object_source.aoi_geometry is None: - raise base_project.ValidationException("AOI Geometry is missing") + aoi_geometry_ref = self.project_type_specifics.object_source.aoi_geometry + aoi_geometry_asset = self.project.aoi_geometry_input_asset + ohsome_filter = self.project_type_specifics.object_source.ohsome_filter - if self.project_type_specifics.object_source.ohsome_filter is None: - raise base_project.ValidationException("Ohsome filter is missing") - - aoi_asset = self.project.aoi_geometry_input_asset - if not aoi_asset: - raise Exception("Could not find AOI geometry asset") - - asset_specific_data = AoiGeometryAssetProperty.model_validate(aoi_asset.asset_type_specifics) + if aoi_geometry_ref is None or aoi_geometry_asset is None: + raise base_project.ValidationException("AOI Geometry is missing") - # FIXME(frozenhelium): reuse validate_aoi_geojson - allowed_area = 20 - if asset_specific_data.area > allowed_area: - raise base_project.ValidationException(f"Area for AOI Geometry must be less than {allowed_area} sq. km") + if ohsome_filter is None: + raise base_project.ValidationException("OHSOME filter is missing") - with aoi_asset.file.open() as aoi_file: - aoi_geojson = json.loads(aoi_file.read()) + _, aoi_geojson = ValidateProject.test_ohsome_objects_from_aoi_asset( + self.project.aoi_geometry_input_asset, + ohsome_filter, + ) - # TODO(tnagorra): Also store intermediate geometries? + # TODO(tnagorra): Store intermediate geometries? return self._get_object_geometry_from_ohsome(aoi_geojson) @@ -329,28 +357,7 @@ def _validate_object_geojson_url(self): if url is None: raise base_project.ValidationException("Object Geojson URL is missing") - logger.info("Fetching object geojson from %s", url) - - # FIXME(frozenhelium): use predefined timeout duration - # FIXME(tnagorra): handle timeout error - response = requests.get(url, timeout=500) - if response.status_code != 200: - raise base_project.ValidationException( - f"Failed to fetch object geojson from {url}", - ) - - logger.info("Successfully fetched object geojson from %s", url) - try: - geojson = response.json() - except Exception as e: - raise base_project.ValidationException("GeoJSON URL did not respond with valid JSON") from e - - try: - features, geometry_collection = convert_json_dict_to_geometry_collection(geojson) - except Exception as e: - raise base_project.ValidationException( - "GeoJSON URL did not respond with a valid feature collection of polygon or multi-polygon", - ) from e + _, features, geometry_collection = ValidateProject.test_geojson_url_project(url) # TODO(tnagorra): Also store intermediate geometries? # TODO(tnagorra): Also create a input geometry? @@ -376,57 +383,25 @@ def _validate_object_geojson_url(self): proj_aoi_geometry.geometry = hull proj_aoi_geometry.total_area = area_km2 proj_aoi_geometry.save() + self.project.total_area = area_km2 self.project.bbox = hull_bbox self.project.centroid = hull_center self.project.save(update_fields=["aoi_geometry", "total_area", "bbox", "centroid"]) - # FIXME(frozenhelium): add validation for object count? - return features def _validate_tasking_manager(self): hot_tm_id = self.project_type_specifics.object_source.tasking_manager_project_id + ohsome_filter = self.project_type_specifics.object_source.ohsome_filter if hot_tm_id is None: raise base_project.ValidationException("HOT Tasking Manager Project ID is missing") - hot_tm_url = f"{Config.HOT_TASKING_MANAGER_PROJECT_API_LINK}projects/{hot_tm_id}/queries/aoi/?as_file=false" - logger.info("Fetching AOI geojson on HOT from %s", hot_tm_url) + if ohsome_filter is None: + raise base_project.ValidationException("OHSOME filter is missing") - # FIXME(frozenhelium): use predefined timeout duration - # FIXME(tnagorra): handle timeout error - aoi_result = requests.get(hot_tm_url, timeout=500) - if aoi_result.status_code != 200: - raise base_project.ValidationException( - f"Failed to fetch AOI GeoJSON from HOT Tasking Manager for tm_id {hot_tm_id}", - ) - - logger.info("Successfully fetched AOI geojson from HOT for tm_id %s", hot_tm_id) - - try: - geometry_dict = aoi_result.json() - except Exception as e: - raise base_project.ValidationException("HOT Tasking Manager did not respond with a valid JSON") from e - - aoi_geojson = { - "type": "FeatureCollection", - "metadata": { - "project_id": self.project.pk, - "hot_tm_project_id": hot_tm_id, - }, - "features": [ - { - "type": "Feature", - "geometry": geometry_dict, - "properties": { - "hot_tm_project_id": hot_tm_id, - }, - }, - ], - } - - # TODO(tnagorra): Add area validation for the AOI + _, aoi_geojson, aoi_result = ValidateProject.test_tasking_manager_project(hot_tm_id, ohsome_filter) # TODO(tnagorra): Also create a input geometry? # TODO(tnagorra): Also store intermediate geometries? @@ -452,6 +427,7 @@ def _validate_tasking_manager(self): proj_aoi_geometry.geometry = geometry proj_aoi_geometry.total_area = area_km2 proj_aoi_geometry.save() + self.project.total_area = area_km2 self.project.bbox = geometry_bbox self.project.centroid = geometry_center diff --git a/schema.graphql b/schema.graphql index 3a800743..c66dc63d 100644 --- a/schema.graphql +++ b/schema.graphql @@ -2053,8 +2053,8 @@ type Query { publicOrganizations(filters: OrganizationFilter, order: OrganizationOrder, pagination: OffsetPaginationInput): OrganizationTypeOffsetPaginated! publicProject(id: ID!): ProjectType! publicProjects(filters: ProjectFilter, order: ProjectOrder, pagination: OffsetPaginationInput): ProjectTypeOffsetPaginated! - testAoiObjects(projectId: ID, assetId: ID, ohsomeFilter: String): TestValidateAoiObjectsResponse! @isAuthenticated - testTaskingManagerProject(hotTmId: String, ohsomeFilter: String): TestValidateTaskingManagerProjectResponse! @isAuthenticated + testAoiObjects(projectId: ID!, assetId: ID!, ohsomeFilter: String!): TestValidateAoiObjectsResponse! @isAuthenticated + testTaskingManagerProject(hotTmId: String!, ohsomeFilter: String!): TestValidateTaskingManagerProjectResponse! @isAuthenticated tileServers: RasterTileServersType! @isAuthenticated tutorial(id: ID!): TutorialType! @isAuthenticated tutorialAsset(id: ID!): TutorialAssetType! @isAuthenticated @@ -2086,6 +2086,7 @@ enum RasterTileServerNameEnum { type RasterTileServerType { credits: String! + disabled: Boolean! label: String! maxZoom: Int minZoom: Int diff --git a/utils/geo/raster_tile_server/config.py b/utils/geo/raster_tile_server/config.py index 436ec709..4810211e 100644 --- a/utils/geo/raster_tile_server/config.py +++ b/utils/geo/raster_tile_server/config.py @@ -53,6 +53,7 @@ class RasterTileServerNormConfig(typing.TypedDict): credits: str min_zoom: int | None max_zoom: int | None + disabled: bool class RasterConfig: @@ -74,6 +75,7 @@ def get_config(name: RasterTileServerNameEnumWithoutCustom) -> RasterTileServerN "credits": "© 2019 Microsoft Corporation, Earthstar Geographics SIO", "min_zoom": 1, "max_zoom": 21, + "disabled": False, } case RasterTileServerNameEnum.MAPBOX: api_key = settings.MAP_IMAGE_MAPBOX_API_KEY @@ -86,6 +88,7 @@ def get_config(name: RasterTileServerNameEnumWithoutCustom) -> RasterTileServerN "credits": "© 2019 MapBox", "min_zoom": 0, "max_zoom": 21, + "disabled": True, } case RasterTileServerNameEnum.MAXAR_PREMIUM: api_key = settings.MAP_IMAGE_MAXAR_PREMIUM_API_KEY @@ -103,6 +106,7 @@ def get_config(name: RasterTileServerNameEnumWithoutCustom) -> RasterTileServerN "credits": "© 2019 Maxar", "min_zoom": 0, "max_zoom": 21, + "disabled": True, } case RasterTileServerNameEnum.MAXAR_STANDARD: api_key = settings.MAP_IMAGE_MAXAR_STANDARD_API_KEY @@ -120,6 +124,7 @@ def get_config(name: RasterTileServerNameEnumWithoutCustom) -> RasterTileServerN "credits": "© 2019 Maxar", "min_zoom": 0, "max_zoom": 21, + "disabled": True, } case RasterTileServerNameEnum.ESRI: url = "https://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}" @@ -131,6 +136,7 @@ def get_config(name: RasterTileServerNameEnumWithoutCustom) -> RasterTileServerN "credits": "© 2019 ESRI", "min_zoom": 0, "max_zoom": 21, + "disabled": False, } case RasterTileServerNameEnum.ESRI_BETA: url = "https://clarity.maptiles.arcgis.com/arcgis/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}" @@ -142,4 +148,5 @@ def get_config(name: RasterTileServerNameEnumWithoutCustom) -> RasterTileServerN "credits": "© 2019 ESRI", "min_zoom": 0, "max_zoom": 21, + "disabled": False, } diff --git a/utils/geo/raster_tile_server/models.py b/utils/geo/raster_tile_server/models.py index 26a4db8e..35414e86 100644 --- a/utils/geo/raster_tile_server/models.py +++ b/utils/geo/raster_tile_server/models.py @@ -40,6 +40,7 @@ def get_config(self) -> RasterTileServerNormConfig: "credits": self.custom.credits, "min_zoom": self.custom.min_zoom, "max_zoom": self.custom.max_zoom, + "disabled": False, } return RasterConfig.get_config(self.name)