OpenGeodeWeb-Back 6.16.1__tar.gz → 6.17.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/PKG-INFO +3 -1
  2. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/pyproject.toml +3 -1
  3. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/requirements.txt +4 -0
  4. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/OpenGeodeWeb_Back.egg-info/PKG-INFO +3 -1
  5. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/OpenGeodeWeb_Back.egg-info/SOURCES.txt +35 -2
  6. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/OpenGeodeWeb_Back.egg-info/requires.txt +2 -0
  7. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/app.py +12 -0
  8. opengeodeweb_back-6.17.0/src/opengeodeweb_back/basemap/basemap_mesh.py +93 -0
  9. opengeodeweb_back-6.17.0/src/opengeodeweb_back/basemap/basemap_providers.py +100 -0
  10. opengeodeweb_back-6.17.0/src/opengeodeweb_back/basemap/basemap_tiles.py +312 -0
  11. opengeodeweb_back-6.17.0/src/opengeodeweb_back/crs/crs_alignment.py +139 -0
  12. opengeodeweb_back-6.17.0/src/opengeodeweb_back/crs/crs_assignment.py +117 -0
  13. opengeodeweb_back-6.17.0/src/opengeodeweb_back/crs/crs_functions.py +342 -0
  14. opengeodeweb_back-6.17.0/src/opengeodeweb_back/geode_objects/crs_coordinates.py +61 -0
  15. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_objects/geode_brep.py +85 -0
  16. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_objects/geode_edged_curve2d.py +17 -0
  17. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_objects/geode_edged_curve3d.py +20 -0
  18. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_objects/geode_point_set2d.py +17 -0
  19. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_objects/geode_point_set3d.py +20 -0
  20. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_objects/geode_section.py +61 -0
  21. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_objects/geode_solid_mesh3d.py +20 -0
  22. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_objects/geode_surface_mesh2d.py +17 -0
  23. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_objects/geode_surface_mesh3d.py +20 -0
  24. opengeodeweb_back-6.17.0/src/opengeodeweb_back/routes/__init__.py +0 -0
  25. opengeodeweb_back-6.17.0/src/opengeodeweb_back/routes/basemap/__init__.py +0 -0
  26. opengeodeweb_back-6.17.0/src/opengeodeweb_back/routes/basemap/blueprint_basemap.py +136 -0
  27. opengeodeweb_back-6.17.0/src/opengeodeweb_back/routes/basemap/schemas/__init__.py +2 -0
  28. opengeodeweb_back-6.17.0/src/opengeodeweb_back/routes/basemap/schemas/build.json +204 -0
  29. opengeodeweb_back-6.17.0/src/opengeodeweb_back/routes/basemap/schemas/build.py +110 -0
  30. opengeodeweb_back-6.17.0/src/opengeodeweb_back/routes/basemap/schemas/delete.json +30 -0
  31. opengeodeweb_back-6.17.0/src/opengeodeweb_back/routes/basemap/schemas/delete.py +30 -0
  32. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/blueprint_routes.py +45 -21
  33. opengeodeweb_back-6.17.0/src/opengeodeweb_back/routes/create/__init__.py +0 -0
  34. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/create/blueprint_create.py +22 -0
  35. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/create/schemas/__init__.py +1 -0
  36. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/create/schemas/edged_curve.json +21 -1
  37. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/create/schemas/edged_curve.py +11 -0
  38. opengeodeweb_back-6.17.0/src/opengeodeweb_back/routes/create/schemas/horizon_stack.json +192 -0
  39. opengeodeweb_back-6.17.0/src/opengeodeweb_back/routes/create/schemas/horizon_stack.py +82 -0
  40. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/create/schemas/point_set.json +21 -1
  41. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/create/schemas/point_set.py +11 -0
  42. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/create/schemas/polygonal_surface.json +21 -1
  43. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/create/schemas/polygonal_surface.py +11 -0
  44. opengeodeweb_back-6.17.0/src/opengeodeweb_back/routes/crs/__init__.py +0 -0
  45. opengeodeweb_back-6.17.0/src/opengeodeweb_back/routes/crs/blueprint_crs.py +142 -0
  46. opengeodeweb_back-6.17.0/src/opengeodeweb_back/routes/crs/schemas/__init__.py +5 -0
  47. opengeodeweb_back-6.17.0/src/opengeodeweb_back/routes/crs/schemas/active.json +50 -0
  48. opengeodeweb_back-6.17.0/src/opengeodeweb_back/routes/crs/schemas/active.py +42 -0
  49. opengeodeweb_back-6.17.0/src/opengeodeweb_back/routes/crs/schemas/align.json +158 -0
  50. opengeodeweb_back-6.17.0/src/opengeodeweb_back/routes/crs/schemas/align.py +88 -0
  51. opengeodeweb_back-6.17.0/src/opengeodeweb_back/routes/crs/schemas/assign_geographic.json +113 -0
  52. opengeodeweb_back-6.17.0/src/opengeodeweb_back/routes/crs/schemas/assign_geographic.py +69 -0
  53. opengeodeweb_back-6.17.0/src/opengeodeweb_back/routes/crs/schemas/convert_geographic.json +110 -0
  54. opengeodeweb_back-6.17.0/src/opengeodeweb_back/routes/crs/schemas/convert_geographic.py +68 -0
  55. opengeodeweb_back-6.16.1/src/opengeodeweb_back/routes/schemas/geographic_coordinate_systems.json → opengeodeweb_back-6.17.0/src/opengeodeweb_back/routes/crs/schemas/geographic_list.json +1 -1
  56. opengeodeweb_back-6.16.1/src/opengeodeweb_back/routes/schemas/geographic_coordinate_systems.py → opengeodeweb_back-6.17.0/src/opengeodeweb_back/routes/crs/schemas/geographic_list.py +6 -6
  57. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/__init__.py +1 -1
  58. opengeodeweb_back-6.17.0/src/opengeodeweb_back/routes/schemas/horizon_stack_info.json +70 -0
  59. opengeodeweb_back-6.17.0/src/opengeodeweb_back/routes/schemas/horizon_stack_info.py +50 -0
  60. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/save_viewable_file.json +21 -1
  61. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/save_viewable_file.py +11 -0
  62. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/test_utils.py +41 -0
  63. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/utils_functions.py +4 -0
  64. opengeodeweb_back-6.17.0/tests/test_basemap.py +510 -0
  65. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/tests/test_create_routes.py +72 -0
  66. opengeodeweb_back-6.17.0/tests/test_crs_routes.py +700 -0
  67. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/tests/test_routes.py +35 -71
  68. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/LICENSE +0 -0
  69. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/README.md +0 -0
  70. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/setup.cfg +0 -0
  71. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/OpenGeodeWeb_Back.egg-info/dependency_links.txt +0 -0
  72. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/OpenGeodeWeb_Back.egg-info/entry_points.txt +0 -0
  73. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/OpenGeodeWeb_Back.egg-info/top_level.txt +0 -0
  74. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/__init__.py +0 -0
  75. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/app_config.py +0 -0
  76. {opengeodeweb_back-6.16.1/src/opengeodeweb_back/routes → opengeodeweb_back-6.17.0/src/opengeodeweb_back/basemap}/__init__.py +0 -0
  77. {opengeodeweb_back-6.16.1/src/opengeodeweb_back/routes/create → opengeodeweb_back-6.17.0/src/opengeodeweb_back/crs}/__init__.py +0 -0
  78. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_functions.py +0 -0
  79. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_objects/__init__.py +0 -0
  80. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_objects/geode_cross_section.py +0 -0
  81. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_objects/geode_graph.py +0 -0
  82. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_objects/geode_grid2d.py +0 -0
  83. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_objects/geode_grid3d.py +0 -0
  84. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_objects/geode_horizon_stack3d.py +0 -0
  85. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_objects/geode_hybrid_solid3d.py +0 -0
  86. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_objects/geode_implicit_cross_section.py +0 -0
  87. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_objects/geode_implicit_structural_model.py +0 -0
  88. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_objects/geode_light_regular_grid2d.py +0 -0
  89. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_objects/geode_light_regular_grid3d.py +0 -0
  90. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_objects/geode_mesh.py +0 -0
  91. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_objects/geode_model.py +0 -0
  92. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_objects/geode_object.py +0 -0
  93. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_objects/geode_polygonal_surface2d.py +0 -0
  94. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_objects/geode_polygonal_surface3d.py +0 -0
  95. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_objects/geode_polyhedral_solid3d.py +0 -0
  96. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_objects/geode_raster_image2d.py +0 -0
  97. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_objects/geode_raster_image3d.py +0 -0
  98. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_objects/geode_regular_grid2d.py +0 -0
  99. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_objects/geode_regular_grid3d.py +0 -0
  100. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_objects/geode_structural_model.py +0 -0
  101. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_objects/geode_tetrahedral_solid3d.py +0 -0
  102. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_objects/geode_triangulated_surface2d.py +0 -0
  103. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_objects/geode_triangulated_surface3d.py +0 -0
  104. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/geode_objects/geode_vertex_set.py +0 -0
  105. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/py.typed +0 -0
  106. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/allowed_files.json +0 -0
  107. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/allowed_files.py +0 -0
  108. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/allowed_objects.json +0 -0
  109. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/allowed_objects.py +0 -0
  110. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/apply_time_series.json +0 -0
  111. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/apply_time_series.py +0 -0
  112. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/cell_attribute_names.json +0 -0
  113. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/cell_attribute_names.py +0 -0
  114. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/cell_attribute_range.json +0 -0
  115. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/cell_attribute_range.py +0 -0
  116. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/data_missing_files.json +0 -0
  117. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/data_missing_files.py +0 -0
  118. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/edge_attribute_names.json +0 -0
  119. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/edge_attribute_names.py +0 -0
  120. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/edge_attribute_range.json +0 -0
  121. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/edge_attribute_range.py +0 -0
  122. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/export_project.json +0 -0
  123. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/export_project.py +0 -0
  124. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/geode_object_inheritance.json +0 -0
  125. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/geode_object_inheritance.py +0 -0
  126. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/geode_objects_and_output_extensions.json +0 -0
  127. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/geode_objects_and_output_extensions.py +0 -0
  128. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/import_project.json +0 -0
  129. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/import_project.py +0 -0
  130. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/kill.json +0 -0
  131. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/kill.py +0 -0
  132. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/model_component_edge_attribute_names.json +0 -0
  133. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/model_component_edge_attribute_names.py +0 -0
  134. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/model_component_edge_attribute_range.json +0 -0
  135. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/model_component_edge_attribute_range.py +0 -0
  136. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/model_component_polygon_attribute_names.json +0 -0
  137. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/model_component_polygon_attribute_names.py +0 -0
  138. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/model_component_polygon_attribute_range.json +0 -0
  139. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/model_component_polygon_attribute_range.py +0 -0
  140. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/model_component_polyhedron_attribute_names.json +0 -0
  141. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/model_component_polyhedron_attribute_names.py +0 -0
  142. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/model_component_polyhedron_attribute_range.json +0 -0
  143. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/model_component_polyhedron_attribute_range.py +0 -0
  144. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/model_component_vertex_attribute_names.json +0 -0
  145. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/model_component_vertex_attribute_names.py +0 -0
  146. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/model_component_vertex_attribute_range.json +0 -0
  147. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/model_component_vertex_attribute_range.py +0 -0
  148. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/ping.json +0 -0
  149. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/ping.py +0 -0
  150. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/polygon_attribute_names.json +0 -0
  151. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/polygon_attribute_names.py +0 -0
  152. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/polygon_attribute_range.json +0 -0
  153. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/polygon_attribute_range.py +0 -0
  154. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/polyhedron_attribute_names.json +0 -0
  155. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/polyhedron_attribute_names.py +0 -0
  156. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/polyhedron_attribute_range.json +0 -0
  157. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/polyhedron_attribute_range.py +0 -0
  158. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/texture_coordinates.json +0 -0
  159. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/texture_coordinates.py +0 -0
  160. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/time_series_allowed_objects.json +0 -0
  161. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/time_series_allowed_objects.py +0 -0
  162. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/time_series_missing_files.json +0 -0
  163. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/time_series_missing_files.py +0 -0
  164. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/upload_file.json +0 -0
  165. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/upload_file.py +0 -0
  166. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/validate.json +0 -0
  167. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/validate.py +0 -0
  168. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/vertex_attribute_names.json +0 -0
  169. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/vertex_attribute_names.py +0 -0
  170. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/vertex_attribute_range.json +0 -0
  171. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/routes/schemas/vertex_attribute_range.py +0 -0
  172. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/src/opengeodeweb_back/typed_route.py +0 -0
  173. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/tests/test_events.py +0 -0
  174. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/tests/test_geode_functions.py +0 -0
  175. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/tests/test_typed_routes.py +0 -0
  176. {opengeodeweb_back-6.16.1 → opengeodeweb_back-6.17.0}/tests/test_utils_functions.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: OpenGeodeWeb-Back
3
- Version: 6.16.1
3
+ Version: 6.17.0
4
4
  Summary: OpenGeodeWeb-Back is an open source framework that proposes handy python functions and wrappers for the OpenGeode ecosystem
5
5
  Author-email: Geode-solutions <team-web@geode-solutions.com>
6
6
  Project-URL: Homepage, https://github.com/Geode-solutions/OpenGeodeWeb-Back
@@ -13,6 +13,7 @@ Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
14
  Requires-Dist: asgiref==3.*
15
15
  Requires-Dist: blinker==1.*
16
+ Requires-Dist: certifi==2026.7.22
16
17
  Requires-Dist: click==8.*
17
18
  Requires-Dist: defusedxml==0.7.1
18
19
  Requires-Dist: flask[async]==3.*
@@ -28,6 +29,7 @@ Requires-Dist: opengeode-geosciencesio==5.13.1
28
29
  Requires-Dist: opengeode-inspector==6.12.4
29
30
  Requires-Dist: opengeode-inspector-geosciences==1.2.4
30
31
  Requires-Dist: opengeode-io==7.11.2
32
+ Requires-Dist: pillow==13.0.0
31
33
  Requires-Dist: types-defusedxml==0.7.0.20260504
32
34
  Requires-Dist: werkzeug==3.1.8
33
35
  Requires-Dist: opengeodeweb-microservice==1.*,>=1.3.2
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "OpenGeodeWeb-Back"
7
- version = "6.16.1"
7
+ version = "6.17.0"
8
8
  dynamic = ["dependencies"]
9
9
  authors = [{ name = "Geode-solutions", email = "team-web@geode-solutions.com" }]
10
10
  description = "OpenGeodeWeb-Back is an open source framework that proposes handy python functions and wrappers for the OpenGeode ecosystem"
@@ -33,4 +33,6 @@ where = ["src"]
33
33
  "opengeodeweb_back.routes.schemas" = ["*.json"]
34
34
  "opengeodeweb_back.routes.models.schemas" = ["*.json"]
35
35
  "opengeodeweb_back.routes.create.schemas" = ["*.json"]
36
+ "opengeodeweb_back.routes.crs.schemas" = ["*.json"]
37
+ "opengeodeweb_back.routes.basemap.schemas" = ["*.json"]
36
38
  "opengeodeweb_back" = ["py.typed"]
@@ -8,6 +8,8 @@ asgiref==3.*
8
8
  # via flask
9
9
  blinker==1.*
10
10
  # via flask
11
+ certifi==2026.7.22
12
+ # via -r requirements.in
11
13
  click==8.*
12
14
  # via flask
13
15
  defusedxml==0.7.1
@@ -62,6 +64,8 @@ opengeode-io==7.11.2
62
64
  # -r requirements.in
63
65
  # geode-viewables
64
66
  # opengeode-geosciencesio
67
+ pillow==13.0.0
68
+ # via -r requirements.in
65
69
  types-defusedxml==0.7.0.20260504
66
70
  # via -r requirements.in
67
71
  werkzeug==3.1.8
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: OpenGeodeWeb-Back
3
- Version: 6.16.1
3
+ Version: 6.17.0
4
4
  Summary: OpenGeodeWeb-Back is an open source framework that proposes handy python functions and wrappers for the OpenGeode ecosystem
5
5
  Author-email: Geode-solutions <team-web@geode-solutions.com>
6
6
  Project-URL: Homepage, https://github.com/Geode-solutions/OpenGeodeWeb-Back
@@ -13,6 +13,7 @@ Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
14
  Requires-Dist: asgiref==3.*
15
15
  Requires-Dist: blinker==1.*
16
+ Requires-Dist: certifi==2026.7.22
16
17
  Requires-Dist: click==8.*
17
18
  Requires-Dist: defusedxml==0.7.1
18
19
  Requires-Dist: flask[async]==3.*
@@ -28,6 +29,7 @@ Requires-Dist: opengeode-geosciencesio==5.13.1
28
29
  Requires-Dist: opengeode-inspector==6.12.4
29
30
  Requires-Dist: opengeode-inspector-geosciences==1.2.4
30
31
  Requires-Dist: opengeode-io==7.11.2
32
+ Requires-Dist: pillow==13.0.0
31
33
  Requires-Dist: types-defusedxml==0.7.0.20260504
32
34
  Requires-Dist: werkzeug==3.1.8
33
35
  Requires-Dist: opengeodeweb-microservice==1.*,>=1.3.2
@@ -16,7 +16,16 @@ src/opengeodeweb_back/py.typed
16
16
  src/opengeodeweb_back/test_utils.py
17
17
  src/opengeodeweb_back/typed_route.py
18
18
  src/opengeodeweb_back/utils_functions.py
19
+ src/opengeodeweb_back/basemap/__init__.py
20
+ src/opengeodeweb_back/basemap/basemap_mesh.py
21
+ src/opengeodeweb_back/basemap/basemap_providers.py
22
+ src/opengeodeweb_back/basemap/basemap_tiles.py
23
+ src/opengeodeweb_back/crs/__init__.py
24
+ src/opengeodeweb_back/crs/crs_alignment.py
25
+ src/opengeodeweb_back/crs/crs_assignment.py
26
+ src/opengeodeweb_back/crs/crs_functions.py
19
27
  src/opengeodeweb_back/geode_objects/__init__.py
28
+ src/opengeodeweb_back/geode_objects/crs_coordinates.py
20
29
  src/opengeodeweb_back/geode_objects/geode_brep.py
21
30
  src/opengeodeweb_back/geode_objects/geode_cross_section.py
22
31
  src/opengeodeweb_back/geode_objects/geode_edged_curve2d.py
@@ -53,15 +62,37 @@ src/opengeodeweb_back/geode_objects/geode_triangulated_surface3d.py
53
62
  src/opengeodeweb_back/geode_objects/geode_vertex_set.py
54
63
  src/opengeodeweb_back/routes/__init__.py
55
64
  src/opengeodeweb_back/routes/blueprint_routes.py
65
+ src/opengeodeweb_back/routes/basemap/__init__.py
66
+ src/opengeodeweb_back/routes/basemap/blueprint_basemap.py
67
+ src/opengeodeweb_back/routes/basemap/schemas/__init__.py
68
+ src/opengeodeweb_back/routes/basemap/schemas/build.json
69
+ src/opengeodeweb_back/routes/basemap/schemas/build.py
70
+ src/opengeodeweb_back/routes/basemap/schemas/delete.json
71
+ src/opengeodeweb_back/routes/basemap/schemas/delete.py
56
72
  src/opengeodeweb_back/routes/create/__init__.py
57
73
  src/opengeodeweb_back/routes/create/blueprint_create.py
58
74
  src/opengeodeweb_back/routes/create/schemas/__init__.py
59
75
  src/opengeodeweb_back/routes/create/schemas/edged_curve.json
60
76
  src/opengeodeweb_back/routes/create/schemas/edged_curve.py
77
+ src/opengeodeweb_back/routes/create/schemas/horizon_stack.json
78
+ src/opengeodeweb_back/routes/create/schemas/horizon_stack.py
61
79
  src/opengeodeweb_back/routes/create/schemas/point_set.json
62
80
  src/opengeodeweb_back/routes/create/schemas/point_set.py
63
81
  src/opengeodeweb_back/routes/create/schemas/polygonal_surface.json
64
82
  src/opengeodeweb_back/routes/create/schemas/polygonal_surface.py
83
+ src/opengeodeweb_back/routes/crs/__init__.py
84
+ src/opengeodeweb_back/routes/crs/blueprint_crs.py
85
+ src/opengeodeweb_back/routes/crs/schemas/__init__.py
86
+ src/opengeodeweb_back/routes/crs/schemas/active.json
87
+ src/opengeodeweb_back/routes/crs/schemas/active.py
88
+ src/opengeodeweb_back/routes/crs/schemas/align.json
89
+ src/opengeodeweb_back/routes/crs/schemas/align.py
90
+ src/opengeodeweb_back/routes/crs/schemas/assign_geographic.json
91
+ src/opengeodeweb_back/routes/crs/schemas/assign_geographic.py
92
+ src/opengeodeweb_back/routes/crs/schemas/convert_geographic.json
93
+ src/opengeodeweb_back/routes/crs/schemas/convert_geographic.py
94
+ src/opengeodeweb_back/routes/crs/schemas/geographic_list.json
95
+ src/opengeodeweb_back/routes/crs/schemas/geographic_list.py
65
96
  src/opengeodeweb_back/routes/schemas/__init__.py
66
97
  src/opengeodeweb_back/routes/schemas/allowed_files.json
67
98
  src/opengeodeweb_back/routes/schemas/allowed_files.py
@@ -85,8 +116,8 @@ src/opengeodeweb_back/routes/schemas/geode_object_inheritance.json
85
116
  src/opengeodeweb_back/routes/schemas/geode_object_inheritance.py
86
117
  src/opengeodeweb_back/routes/schemas/geode_objects_and_output_extensions.json
87
118
  src/opengeodeweb_back/routes/schemas/geode_objects_and_output_extensions.py
88
- src/opengeodeweb_back/routes/schemas/geographic_coordinate_systems.json
89
- src/opengeodeweb_back/routes/schemas/geographic_coordinate_systems.py
119
+ src/opengeodeweb_back/routes/schemas/horizon_stack_info.json
120
+ src/opengeodeweb_back/routes/schemas/horizon_stack_info.py
90
121
  src/opengeodeweb_back/routes/schemas/import_project.json
91
122
  src/opengeodeweb_back/routes/schemas/import_project.py
92
123
  src/opengeodeweb_back/routes/schemas/kill.json
@@ -133,7 +164,9 @@ src/opengeodeweb_back/routes/schemas/vertex_attribute_names.json
133
164
  src/opengeodeweb_back/routes/schemas/vertex_attribute_names.py
134
165
  src/opengeodeweb_back/routes/schemas/vertex_attribute_range.json
135
166
  src/opengeodeweb_back/routes/schemas/vertex_attribute_range.py
167
+ tests/test_basemap.py
136
168
  tests/test_create_routes.py
169
+ tests/test_crs_routes.py
137
170
  tests/test_events.py
138
171
  tests/test_geode_functions.py
139
172
  tests/test_routes.py
@@ -1,5 +1,6 @@
1
1
  asgiref==3.*
2
2
  blinker==1.*
3
+ certifi==2026.7.22
3
4
  click==8.*
4
5
  defusedxml==0.7.1
5
6
  flask[async]==3.*
@@ -15,6 +16,7 @@ opengeode-geosciencesio==5.13.1
15
16
  opengeode-inspector==6.12.4
16
17
  opengeode-inspector-geosciences==1.2.4
17
18
  opengeode-io==7.11.2
19
+ pillow==13.0.0
18
20
  types-defusedxml==0.7.0.20260504
19
21
  werkzeug==3.1.8
20
22
  opengeodeweb-microservice==1.*,>=1.3.2
@@ -19,7 +19,9 @@ from werkzeug.exceptions import HTTPException
19
19
 
20
20
  from opengeodeweb_back import app_config, utils_functions
21
21
  from opengeodeweb_back.routes import blueprint_routes
22
+ from opengeodeweb_back.routes.basemap import blueprint_basemap
22
23
  from opengeodeweb_back.routes.create import blueprint_create
24
+ from opengeodeweb_back.routes.crs import blueprint_crs
23
25
 
24
26
  if TYPE_CHECKING:
25
27
  from collections.abc import Generator
@@ -171,6 +173,16 @@ def register_ogw_back_blueprints(app: flask.Flask) -> None:
171
173
  url_prefix="/opengeodeweb_back/create",
172
174
  name="opengeodeweb_create",
173
175
  )
176
+ app.register_blueprint(
177
+ blueprint_crs.routes,
178
+ url_prefix="/opengeodeweb_back/crs",
179
+ name="opengeodeweb_crs",
180
+ )
181
+ app.register_blueprint(
182
+ blueprint_basemap.routes,
183
+ url_prefix="/opengeodeweb_back/basemap",
184
+ name="opengeodeweb_basemap",
185
+ )
174
186
 
175
187
 
176
188
  def run_server(app: Flask) -> None:
@@ -0,0 +1,93 @@
1
+ from __future__ import annotations
2
+
3
+ # Standard library imports
4
+ import math
5
+ from typing import TYPE_CHECKING
6
+
7
+ # Third party imports
8
+ import opengeode as og
9
+ import opengeode_geosciences as og_geosciences
10
+
11
+ # Local application imports
12
+ from opengeodeweb_back.basemap.basemap_tiles import HALF_WORLD
13
+ from opengeodeweb_back.crs.crs_functions import convert_xy_corners
14
+ from opengeodeweb_back.geode_objects.geode_triangulated_surface3d import (
15
+ GeodeTriangulatedSurface3D,
16
+ )
17
+
18
+ if TYPE_CHECKING:
19
+ from opengeodeweb_back.basemap.basemap_tiles import TileRange
20
+
21
+ GRID_SIZE = 32
22
+ MESH_NAME = "basemap"
23
+ UV_ATTRIBUTE = "basemap_uv"
24
+ WEB_MERCATOR = og_geosciences.GeographicCoordinateSystemInfo(
25
+ "EPSG", "3857", "WGS 84 / Pseudo-Mercator"
26
+ )
27
+ OUTSIDE_MESSAGE = "Area outside the map projection limits"
28
+
29
+
30
+ class OutsideProjectionError(Exception):
31
+ pass
32
+
33
+
34
+ def bounds_to_3857(
35
+ bounds: tuple[float, float, float, float],
36
+ crs: og_geosciences.GeographicCoordinateSystemInfo,
37
+ ) -> tuple[float, float, float, float]:
38
+ """Bounding box in Web Mercator of the four corners of bounds, given in crs."""
39
+ try:
40
+ corners = convert_xy_corners(bounds, crs, WEB_MERCATOR)
41
+ except RuntimeError as error: # PROJ rejects coordinates outside either CRS domain
42
+ raise OutsideProjectionError(OUTSIDE_MESSAGE) from error
43
+ xs = [x for x, _ in corners]
44
+ ys = [y for _, y in corners]
45
+ projected = (min(xs), min(ys), max(xs), max(ys))
46
+ if any(not abs(value) <= HALF_WORLD for value in projected): # also rejects NaN
47
+ raise OutsideProjectionError(OUTSIDE_MESSAGE)
48
+ return projected
49
+
50
+
51
+ def build_mesh(
52
+ tiles: TileRange,
53
+ crs: og_geosciences.GeographicCoordinateSystemInfo,
54
+ z_value: float,
55
+ ) -> GeodeTriangulatedSurface3D:
56
+ """Grid covering the tiles, with texture coordinates, converted from Web Mercator to crs."""
57
+ x0, y0, x1, y1 = tiles.bounds_3857()
58
+ mesh = GeodeTriangulatedSurface3D()
59
+ builder = mesh.builder()
60
+ builder.set_name(MESH_NAME)
61
+ attributes = mesh.triangulated_surface.vertex_attribute_manager()
62
+ values = og.AttributeValuesArrayDouble2()
63
+ values.default_value = [0.0, 0.0]
64
+ uv_id = attributes.create_attribute_variable_arraydouble2(
65
+ UV_ATTRIBUTE, values, og.AttributeProperties()
66
+ )
67
+ uv = attributes.find_attribute_variable_arraydouble2(uv_id)
68
+ last = GRID_SIZE - 1
69
+ for row in range(GRID_SIZE):
70
+ for column in range(GRID_SIZE):
71
+ u, v = column / last, row / last
72
+ point = og.Point3D([x0 + u * (x1 - x0), y0 + v * (y1 - y0), z_value])
73
+ uv.set_value(builder.create_point(point), [u, v])
74
+ for row in range(last):
75
+ for column in range(last):
76
+ corner = row * GRID_SIZE + column
77
+ builder.create_triangle([corner, corner + 1, corner + GRID_SIZE + 1])
78
+ builder.create_triangle([corner, corner + GRID_SIZE + 1, corner + GRID_SIZE])
79
+ builder.compute_polygon_adjacencies()
80
+ mesh.assign_crs(WEB_MERCATOR.authority_code(), WEB_MERCATOR)
81
+ # Assigning then converting to the same CRS name is rejected by OpenGeode
82
+ if crs.authority_code() == WEB_MERCATOR.authority_code():
83
+ return mesh
84
+ try:
85
+ mesh.convert_crs(crs.authority_code(), crs)
86
+ except RuntimeError as error: # PROJ rejects tiles outside the project CRS domain
87
+ raise OutsideProjectionError(OUTSIDE_MESSAGE) from error
88
+ surface = mesh.triangulated_surface
89
+ for vertex in range(surface.nb_vertices()):
90
+ point = surface.point(vertex)
91
+ if not all(math.isfinite(point.value(axis)) for axis in range(3)):
92
+ raise OutsideProjectionError(OUTSIDE_MESSAGE)
93
+ return mesh
@@ -0,0 +1,100 @@
1
+ from __future__ import annotations
2
+
3
+ # Standard library imports
4
+ import os
5
+ from dataclasses import dataclass
6
+ from typing import TYPE_CHECKING
7
+
8
+ if TYPE_CHECKING:
9
+ from collections.abc import Container, Iterable
10
+
11
+ USER_AGENT = "Vease (+https://geode-solutions.com)"
12
+ # Every source is cut in 256 px Web Mercator tiles: they share one tile grid
13
+ TILE_SIZE = 256
14
+
15
+
16
+ @dataclass(frozen=True)
17
+ class Source:
18
+ url_template: str
19
+ max_zoom: int
20
+ attribution: str
21
+
22
+
23
+ _GEOPLATEFORME = (
24
+ "https://data.geopf.fr/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER={layer}"
25
+ "&STYLE=normal&TILEMATRIXSET=PM&TILEMATRIX={{z}}&TILEROW={{y}}&TILECOL={{x}}&FORMAT={format}"
26
+ )
27
+ _IGN = "© IGN"
28
+
29
+ SOURCES: dict[str, Source] = {
30
+ "eox_s2cloudless_2016": Source(
31
+ "https://tiles.maps.eox.at/wmts/1.0.0/s2cloudless_3857/default/g/{z}/{y}/{x}.jpg",
32
+ 17,
33
+ "Sentinel-2 cloudless 2016 by EOX IT Services GmbH "
34
+ "(Contains modified Copernicus Sentinel data 2016)",
35
+ ),
36
+ "ign_orthophotos": Source(
37
+ _GEOPLATEFORME.format(layer="ORTHOIMAGERY.ORTHOPHOTOS", format="image/jpeg"), 19, _IGN
38
+ ),
39
+ # Volunteer servers: to be replaced before production, see the basemap design
40
+ "osm": Source(
41
+ "https://tile.openstreetmap.org/{z}/{x}/{y}.png", 19, "© OpenStreetMap contributors"
42
+ ),
43
+ "ign_plan": Source(
44
+ _GEOPLATEFORME.format(layer="GEOGRAPHICALGRIDSYSTEMS.PLANIGNV2", format="image/png"),
45
+ 19,
46
+ _IGN,
47
+ ),
48
+ "ign_roads": Source(
49
+ _GEOPLATEFORME.format(layer="TRANSPORTNETWORKS.ROADS", format="image/png"), 18, _IGN
50
+ ),
51
+ "eox_overlay": Source(
52
+ "https://tiles.maps.eox.at/wmts/1.0.0/overlay_3857/default/g/{z}/{y}/{x}.png",
53
+ 14,
54
+ "© OpenStreetMap contributors, rendering © EOX",
55
+ ),
56
+ }
57
+
58
+ # The layers of each map, from the bottom up. A layer draws the first of its sources serving a
59
+ # tile: the next one is only fetched when it serves none
60
+ MAPS: dict[str, tuple[tuple[str, ...], ...]] = {
61
+ # The IGN covers the world imagery or map where it has tiles (France)
62
+ "satellite": (("eox_s2cloudless_2016",), ("ign_orthophotos",)),
63
+ "plan": (("osm",), ("ign_plan",)),
64
+ # Transparent: both sources would draw the same roads twice, EOX outside France
65
+ "routes": (("ign_roads", "eox_overlay"),),
66
+ }
67
+
68
+
69
+ def url_template(source: str) -> str:
70
+ # Local servers for tests: one variable per source, else one for all where {source} is replaced
71
+ template = os.environ.get(f"BASEMAP_TILE_URL_{source.upper()}")
72
+ if template is not None:
73
+ return template
74
+ shared = os.environ.get("BASEMAP_TILE_URL")
75
+ if shared is not None:
76
+ return shared.replace("{source}", source)
77
+ return SOURCES[source].url_template
78
+
79
+
80
+ def sorted_maps(maps: Iterable[str]) -> list[str]:
81
+ requested = set(maps)
82
+ return [name for name in MAPS if name in requested]
83
+
84
+
85
+ def map_layers(maps: list[str], served: Container[str]) -> dict[str, list[str]]:
86
+ """The served sources of each sorted map, from the bottom up: maps without any are left out."""
87
+ layers = {
88
+ name: [source for layer in MAPS[name] for source in layer if source in served]
89
+ for name in maps
90
+ }
91
+ return {name: sources for name, sources in layers.items() if sources}
92
+
93
+
94
+ def max_zoom(sources: Iterable[str]) -> int:
95
+ # The grid follows the finest source: coarser ones are overzoomed
96
+ return max(SOURCES[source].max_zoom for source in sources)
97
+
98
+
99
+ def attribution(sources: Iterable[str]) -> str:
100
+ return " | ".join(dict.fromkeys(SOURCES[source].attribution for source in sources))