MapProxy 2.1.0__py3-none-any.whl

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 (459) hide show
  1. MapProxy-2.1.0.dist-info/AUTHORS.txt +33 -0
  2. MapProxy-2.1.0.dist-info/COPYING.txt +60 -0
  3. MapProxy-2.1.0.dist-info/LICENSE.txt +202 -0
  4. MapProxy-2.1.0.dist-info/METADATA +165 -0
  5. MapProxy-2.1.0.dist-info/RECORD +459 -0
  6. MapProxy-2.1.0.dist-info/WHEEL +5 -0
  7. MapProxy-2.1.0.dist-info/entry_points.txt +4 -0
  8. MapProxy-2.1.0.dist-info/top_level.txt +1 -0
  9. mapproxy/__init__.py +0 -0
  10. mapproxy/cache/__init__.py +36 -0
  11. mapproxy/cache/azureblob.py +145 -0
  12. mapproxy/cache/base.py +120 -0
  13. mapproxy/cache/compact.py +665 -0
  14. mapproxy/cache/couchdb.py +301 -0
  15. mapproxy/cache/dummy.py +36 -0
  16. mapproxy/cache/file.py +200 -0
  17. mapproxy/cache/geopackage.py +647 -0
  18. mapproxy/cache/legend.py +87 -0
  19. mapproxy/cache/mbtiles.py +411 -0
  20. mapproxy/cache/meta.py +80 -0
  21. mapproxy/cache/path.py +261 -0
  22. mapproxy/cache/redis.py +152 -0
  23. mapproxy/cache/renderd.py +100 -0
  24. mapproxy/cache/riak.py +206 -0
  25. mapproxy/cache/s3.py +209 -0
  26. mapproxy/cache/tile.py +736 -0
  27. mapproxy/client/__init__.py +0 -0
  28. mapproxy/client/arcgis.py +82 -0
  29. mapproxy/client/cgi.py +141 -0
  30. mapproxy/client/http.py +295 -0
  31. mapproxy/client/log.py +33 -0
  32. mapproxy/client/tile.py +158 -0
  33. mapproxy/client/wms.py +255 -0
  34. mapproxy/compat/__init__.py +0 -0
  35. mapproxy/compat/image.py +86 -0
  36. mapproxy/config/__init__.py +22 -0
  37. mapproxy/config/config-schema.json +813 -0
  38. mapproxy/config/config.py +213 -0
  39. mapproxy/config/coverage.py +108 -0
  40. mapproxy/config/defaults.py +102 -0
  41. mapproxy/config/loader.py +2399 -0
  42. mapproxy/config/spec.py +657 -0
  43. mapproxy/config/validator.py +242 -0
  44. mapproxy/config_template/__init__.py +0 -0
  45. mapproxy/config_template/base_config/config.wsgi +10 -0
  46. mapproxy/config_template/base_config/full_example.yaml +598 -0
  47. mapproxy/config_template/base_config/full_seed_example.yaml +79 -0
  48. mapproxy/config_template/base_config/log.ini +35 -0
  49. mapproxy/config_template/base_config/mapproxy.yaml +60 -0
  50. mapproxy/config_template/base_config/seed.yaml +27 -0
  51. mapproxy/exception.py +149 -0
  52. mapproxy/featureinfo.py +251 -0
  53. mapproxy/grid.py +1199 -0
  54. mapproxy/image/__init__.py +549 -0
  55. mapproxy/image/fonts/DejaVuSans.ttf +0 -0
  56. mapproxy/image/fonts/DejaVuSansMono.ttf +0 -0
  57. mapproxy/image/fonts/LICENSE +99 -0
  58. mapproxy/image/fonts/__init__.py +0 -0
  59. mapproxy/image/mask.py +79 -0
  60. mapproxy/image/merge.py +323 -0
  61. mapproxy/image/message.py +357 -0
  62. mapproxy/image/opts.py +185 -0
  63. mapproxy/image/tile.py +171 -0
  64. mapproxy/image/transform.py +350 -0
  65. mapproxy/layer.py +489 -0
  66. mapproxy/multiapp.py +230 -0
  67. mapproxy/proj.py +309 -0
  68. mapproxy/request/__init__.py +18 -0
  69. mapproxy/request/arcgis.py +268 -0
  70. mapproxy/request/base.py +466 -0
  71. mapproxy/request/tile.py +131 -0
  72. mapproxy/request/wms/__init__.py +829 -0
  73. mapproxy/request/wms/exception.py +107 -0
  74. mapproxy/request/wmts.py +442 -0
  75. mapproxy/response.py +237 -0
  76. mapproxy/script/__init__.py +0 -0
  77. mapproxy/script/conf/__init__.py +0 -0
  78. mapproxy/script/conf/app.py +222 -0
  79. mapproxy/script/conf/caches.py +44 -0
  80. mapproxy/script/conf/geopackage.py +136 -0
  81. mapproxy/script/conf/layers.py +54 -0
  82. mapproxy/script/conf/seeds.py +36 -0
  83. mapproxy/script/conf/sources.py +88 -0
  84. mapproxy/script/conf/utils.py +148 -0
  85. mapproxy/script/defrag.py +187 -0
  86. mapproxy/script/export.py +337 -0
  87. mapproxy/script/grids.py +198 -0
  88. mapproxy/script/scales.py +134 -0
  89. mapproxy/script/util.py +410 -0
  90. mapproxy/script/wms_capabilities.py +160 -0
  91. mapproxy/seed/__init__.py +0 -0
  92. mapproxy/seed/cachelock.py +127 -0
  93. mapproxy/seed/cleanup.py +191 -0
  94. mapproxy/seed/config.py +481 -0
  95. mapproxy/seed/script.py +391 -0
  96. mapproxy/seed/seeder.py +551 -0
  97. mapproxy/seed/spec.py +66 -0
  98. mapproxy/seed/util.py +266 -0
  99. mapproxy/service/__init__.py +14 -0
  100. mapproxy/service/base.py +45 -0
  101. mapproxy/service/demo.py +364 -0
  102. mapproxy/service/kml.py +333 -0
  103. mapproxy/service/ows.py +39 -0
  104. mapproxy/service/template_helper.py +55 -0
  105. mapproxy/service/templates/demo/capabilities_demo.html +18 -0
  106. mapproxy/service/templates/demo/demo.html +181 -0
  107. mapproxy/service/templates/demo/openlayers-demo.cfg +16 -0
  108. mapproxy/service/templates/demo/static/img/blank.gif +0 -0
  109. mapproxy/service/templates/demo/static/img/east-mini.png +0 -0
  110. mapproxy/service/templates/demo/static/img/north-mini.png +0 -0
  111. mapproxy/service/templates/demo/static/img/south-mini.png +0 -0
  112. mapproxy/service/templates/demo/static/img/west-mini.png +0 -0
  113. mapproxy/service/templates/demo/static/img/zoom-minus-mini.png +0 -0
  114. mapproxy/service/templates/demo/static/img/zoom-plus-mini.png +0 -0
  115. mapproxy/service/templates/demo/static/img/zoom-world-mini.png +0 -0
  116. mapproxy/service/templates/demo/static/logo.png +0 -0
  117. mapproxy/service/templates/demo/static/ol.css +345 -0
  118. mapproxy/service/templates/demo/static/ol.js +4 -0
  119. mapproxy/service/templates/demo/static/proj4.min.js +1 -0
  120. mapproxy/service/templates/demo/static/proj4defs.js +1 -0
  121. mapproxy/service/templates/demo/static/site.css +137 -0
  122. mapproxy/service/templates/demo/static/theme/default/framedCloud.css +0 -0
  123. mapproxy/service/templates/demo/static/theme/default/google.css +17 -0
  124. mapproxy/service/templates/demo/static/theme/default/ie6-style.css +10 -0
  125. mapproxy/service/templates/demo/static/theme/default/style.css +482 -0
  126. mapproxy/service/templates/demo/static.html +34 -0
  127. mapproxy/service/templates/demo/tms_demo.html +117 -0
  128. mapproxy/service/templates/demo/wms_demo.html +144 -0
  129. mapproxy/service/templates/demo/wmts_demo.html +118 -0
  130. mapproxy/service/templates/tms_capabilities.xml +13 -0
  131. mapproxy/service/templates/tms_exception.xml +4 -0
  132. mapproxy/service/templates/tms_root_resource.xml +7 -0
  133. mapproxy/service/templates/tms_tilemap_capabilities.xml +14 -0
  134. mapproxy/service/templates/wms100capabilities.xml +112 -0
  135. mapproxy/service/templates/wms100exception.xml +4 -0
  136. mapproxy/service/templates/wms110capabilities.xml +152 -0
  137. mapproxy/service/templates/wms110exception.xml +5 -0
  138. mapproxy/service/templates/wms111capabilities.xml +183 -0
  139. mapproxy/service/templates/wms111exception.xml +5 -0
  140. mapproxy/service/templates/wms130capabilities.xml +326 -0
  141. mapproxy/service/templates/wms130exception.xml +8 -0
  142. mapproxy/service/templates/wmts100capabilities.xml +155 -0
  143. mapproxy/service/templates/wmts100exception.xml +9 -0
  144. mapproxy/service/tile.py +540 -0
  145. mapproxy/service/wms.py +868 -0
  146. mapproxy/service/wmts.py +387 -0
  147. mapproxy/source/__init__.py +83 -0
  148. mapproxy/source/arcgis.py +39 -0
  149. mapproxy/source/error.py +40 -0
  150. mapproxy/source/mapnik.py +262 -0
  151. mapproxy/source/tile.py +97 -0
  152. mapproxy/source/wms.py +273 -0
  153. mapproxy/srs.py +734 -0
  154. mapproxy/template.py +54 -0
  155. mapproxy/test/__init__.py +0 -0
  156. mapproxy/test/conftest.py +8 -0
  157. mapproxy/test/helper.py +255 -0
  158. mapproxy/test/http.py +511 -0
  159. mapproxy/test/image.py +219 -0
  160. mapproxy/test/mocker.py +2291 -0
  161. mapproxy/test/schemas/inspire/common/1.0/common.xsd +1461 -0
  162. mapproxy/test/schemas/inspire/common/1.0/enums/enum_bul.xsd +108 -0
  163. mapproxy/test/schemas/inspire/common/1.0/enums/enum_cze.xsd +108 -0
  164. mapproxy/test/schemas/inspire/common/1.0/enums/enum_dan.xsd +108 -0
  165. mapproxy/test/schemas/inspire/common/1.0/enums/enum_dut.xsd +108 -0
  166. mapproxy/test/schemas/inspire/common/1.0/enums/enum_eng.xsd +155 -0
  167. mapproxy/test/schemas/inspire/common/1.0/enums/enum_est.xsd +108 -0
  168. mapproxy/test/schemas/inspire/common/1.0/enums/enum_fin.xsd +108 -0
  169. mapproxy/test/schemas/inspire/common/1.0/enums/enum_fre.xsd +108 -0
  170. mapproxy/test/schemas/inspire/common/1.0/enums/enum_ger.xsd +108 -0
  171. mapproxy/test/schemas/inspire/common/1.0/enums/enum_gle.xsd +109 -0
  172. mapproxy/test/schemas/inspire/common/1.0/enums/enum_gre.xsd +108 -0
  173. mapproxy/test/schemas/inspire/common/1.0/enums/enum_hun.xsd +108 -0
  174. mapproxy/test/schemas/inspire/common/1.0/enums/enum_ita.xsd +108 -0
  175. mapproxy/test/schemas/inspire/common/1.0/enums/enum_lav.xsd +108 -0
  176. mapproxy/test/schemas/inspire/common/1.0/enums/enum_lit.xsd +108 -0
  177. mapproxy/test/schemas/inspire/common/1.0/enums/enum_mlt.xsd +108 -0
  178. mapproxy/test/schemas/inspire/common/1.0/enums/enum_pol.xsd +108 -0
  179. mapproxy/test/schemas/inspire/common/1.0/enums/enum_por.xsd +108 -0
  180. mapproxy/test/schemas/inspire/common/1.0/enums/enum_rum.xsd +108 -0
  181. mapproxy/test/schemas/inspire/common/1.0/enums/enum_slo.xsd +108 -0
  182. mapproxy/test/schemas/inspire/common/1.0/enums/enum_slv.xsd +108 -0
  183. mapproxy/test/schemas/inspire/common/1.0/enums/enum_spa.xsd +108 -0
  184. mapproxy/test/schemas/inspire/common/1.0/enums/enum_swe.xsd +108 -0
  185. mapproxy/test/schemas/inspire/common/1.0/network.xsd +521 -0
  186. mapproxy/test/schemas/inspire/inspire_vs/1.0/inspire_vs.xsd +19 -0
  187. mapproxy/test/schemas/kml/2.2.0/ReadMe.txt +14 -0
  188. mapproxy/test/schemas/kml/2.2.0/atom-author-link.xsd +66 -0
  189. mapproxy/test/schemas/kml/2.2.0/ogckml22.xsd +1646 -0
  190. mapproxy/test/schemas/kml/2.2.0/xAL.xsd +1680 -0
  191. mapproxy/test/schemas/ows/1.1.0/ReadMe.txt +87 -0
  192. mapproxy/test/schemas/ows/1.1.0/ows19115subset.xsd +235 -0
  193. mapproxy/test/schemas/ows/1.1.0/owsAll.xsd +23 -0
  194. mapproxy/test/schemas/ows/1.1.0/owsCommon.xsd +157 -0
  195. mapproxy/test/schemas/ows/1.1.0/owsContents.xsd +86 -0
  196. mapproxy/test/schemas/ows/1.1.0/owsDataIdentification.xsd +127 -0
  197. mapproxy/test/schemas/ows/1.1.0/owsDomainType.xsd +279 -0
  198. mapproxy/test/schemas/ows/1.1.0/owsExceptionReport.xsd +76 -0
  199. mapproxy/test/schemas/ows/1.1.0/owsGetCapabilities.xsd +112 -0
  200. mapproxy/test/schemas/ows/1.1.0/owsGetResourceByID.xsd +51 -0
  201. mapproxy/test/schemas/ows/1.1.0/owsInputOutputData.xsd +59 -0
  202. mapproxy/test/schemas/ows/1.1.0/owsManifest.xsd +125 -0
  203. mapproxy/test/schemas/ows/1.1.0/owsOperationsMetadata.xsd +140 -0
  204. mapproxy/test/schemas/ows/1.1.0/owsServiceIdentification.xsd +60 -0
  205. mapproxy/test/schemas/ows/1.1.0/owsServiceProvider.xsd +47 -0
  206. mapproxy/test/schemas/sld/1.1.0/sld_capabilities.xsd +27 -0
  207. mapproxy/test/schemas/wms/1.0.0/capabilities_1_0_0.dtd +353 -0
  208. mapproxy/test/schemas/wms/1.0.0/capabilities_1_0_0.xml +188 -0
  209. mapproxy/test/schemas/wms/1.0.7/capabilities_1_0_7.dtd +524 -0
  210. mapproxy/test/schemas/wms/1.0.7/capabilities_1_0_7.xml +260 -0
  211. mapproxy/test/schemas/wms/1.1.0/capabilities_1_1_0.dtd +273 -0
  212. mapproxy/test/schemas/wms/1.1.0/capabilities_1_1_0.xml +303 -0
  213. mapproxy/test/schemas/wms/1.1.0/exception_1_1_0.dtd +6 -0
  214. mapproxy/test/schemas/wms/1.1.0/exception_1_1_0.xml +33 -0
  215. mapproxy/test/schemas/wms/1.1.1/OGC-exception.xsd +68 -0
  216. mapproxy/test/schemas/wms/1.1.1/WMS_DescribeLayerResponse.dtd +22 -0
  217. mapproxy/test/schemas/wms/1.1.1/WMS_MS_Capabilities.dtd +274 -0
  218. mapproxy/test/schemas/wms/1.1.1/WMS_exception_1_1_1.dtd +5 -0
  219. mapproxy/test/schemas/wms/1.1.1/capabilities_1_1_1.dtd +276 -0
  220. mapproxy/test/schemas/wms/1.1.1/capabilities_1_1_1.xml +303 -0
  221. mapproxy/test/schemas/wms/1.1.1/exception_1_1_1.dtd +6 -0
  222. mapproxy/test/schemas/wms/1.1.1/exception_1_1_1.xml +33 -0
  223. mapproxy/test/schemas/wms/1.3.0/ReadMe.txt +8 -0
  224. mapproxy/test/schemas/wms/1.3.0/capabilities_1_3_0.xml +277 -0
  225. mapproxy/test/schemas/wms/1.3.0/capabilities_1_3_0.xsd +611 -0
  226. mapproxy/test/schemas/wms/1.3.0/exceptions_1_3_0.xml +34 -0
  227. mapproxy/test/schemas/wms/1.3.0/exceptions_1_3_0.xsd +28 -0
  228. mapproxy/test/schemas/wmsc/1.1.1/OGC-exception.xsd +68 -0
  229. mapproxy/test/schemas/wmsc/1.1.1/WMS_DescribeLayerResponse.dtd +22 -0
  230. mapproxy/test/schemas/wmsc/1.1.1/WMS_MS_Capabilities.dtd +283 -0
  231. mapproxy/test/schemas/wmsc/1.1.1/WMS_exception_1_1_1.dtd +5 -0
  232. mapproxy/test/schemas/wmsc/1.1.1/capabilities_1_1_1.dtd +276 -0
  233. mapproxy/test/schemas/wmsc/1.1.1/capabilities_1_1_1.xml +303 -0
  234. mapproxy/test/schemas/wmsc/1.1.1/exception_1_1_1.dtd +6 -0
  235. mapproxy/test/schemas/wmsc/1.1.1/exception_1_1_1.xml +33 -0
  236. mapproxy/test/schemas/wmts/1.0/ReadMe.txt +32 -0
  237. mapproxy/test/schemas/wmts/1.0/wmts.xsd +28 -0
  238. mapproxy/test/schemas/wmts/1.0/wmtsAbstract.wsdl +151 -0
  239. mapproxy/test/schemas/wmts/1.0/wmtsGetCapabilities_request.xsd +38 -0
  240. mapproxy/test/schemas/wmts/1.0/wmtsGetCapabilities_response.xsd +564 -0
  241. mapproxy/test/schemas/wmts/1.0/wmtsGetFeatureInfo_request.xsd +57 -0
  242. mapproxy/test/schemas/wmts/1.0/wmtsGetFeatureInfo_response.xsd +72 -0
  243. mapproxy/test/schemas/wmts/1.0/wmtsGetTile_request.xsd +91 -0
  244. mapproxy/test/schemas/wmts/1.0/wmtsKVP.xsd +76 -0
  245. mapproxy/test/schemas/wmts/1.0/wmtsPayload_response.xsd +70 -0
  246. mapproxy/test/schemas/xlink/1.0.0/ReadMe.txt +6 -0
  247. mapproxy/test/schemas/xlink/1.0.0/xlinks.xsd +122 -0
  248. mapproxy/test/schemas/xml.xsd +287 -0
  249. mapproxy/test/system/__init__.py +98 -0
  250. mapproxy/test/system/fixture/arcgis.yaml +57 -0
  251. mapproxy/test/system/fixture/auth.yaml +70 -0
  252. mapproxy/test/system/fixture/cache.mbtiles +0 -0
  253. mapproxy/test/system/fixture/cache_azureblob.yaml +59 -0
  254. mapproxy/test/system/fixture/cache_band_merge.yaml +73 -0
  255. mapproxy/test/system/fixture/cache_bulk_meta_tiles.yaml +24 -0
  256. mapproxy/test/system/fixture/cache_coverage.yaml +84 -0
  257. mapproxy/test/system/fixture/cache_data/dop_cache_EPSG3857/00/000/000/000/000/000/000.png +0 -0
  258. mapproxy/test/system/fixture/cache_data/wms_cache_EPSG900913/01/000/000/000/000/000/001.jpeg +0 -0
  259. mapproxy/test/system/fixture/cache_data/wms_cache_transparent_EPSG900913/01/000/000/000/000/000/001.png +0 -0
  260. mapproxy/test/system/fixture/cache_geopackage.yaml +56 -0
  261. mapproxy/test/system/fixture/cache_grid_names.yaml +50 -0
  262. mapproxy/test/system/fixture/cache_mbtiles.yaml +28 -0
  263. mapproxy/test/system/fixture/cache_s3.yaml +58 -0
  264. mapproxy/test/system/fixture/cache_source.yaml +81 -0
  265. mapproxy/test/system/fixture/combined_sources.yaml +130 -0
  266. mapproxy/test/system/fixture/coverage.yaml +77 -0
  267. mapproxy/test/system/fixture/demo.yaml +135 -0
  268. mapproxy/test/system/fixture/dimension.yaml +59 -0
  269. mapproxy/test/system/fixture/disable_storage.yaml +25 -0
  270. mapproxy/test/system/fixture/empty_ogrdata.geojson +1 -0
  271. mapproxy/test/system/fixture/formats.yaml +72 -0
  272. mapproxy/test/system/fixture/inspire.yaml +101 -0
  273. mapproxy/test/system/fixture/inspire_full.yaml +124 -0
  274. mapproxy/test/system/fixture/kml_layer.yaml +66 -0
  275. mapproxy/test/system/fixture/layer.yaml +260 -0
  276. mapproxy/test/system/fixture/layergroups.yaml +57 -0
  277. mapproxy/test/system/fixture/layergroups_root.yaml +106 -0
  278. mapproxy/test/system/fixture/legendgraphic.yaml +93 -0
  279. mapproxy/test/system/fixture/mapnik_source.yaml +66 -0
  280. mapproxy/test/system/fixture/mapproxy_export.yaml +12 -0
  281. mapproxy/test/system/fixture/mapserver.yaml +23 -0
  282. mapproxy/test/system/fixture/minimal_cgi.py +16 -0
  283. mapproxy/test/system/fixture/mixed_mode.yaml +49 -0
  284. mapproxy/test/system/fixture/multi_cache_layers.yaml +111 -0
  285. mapproxy/test/system/fixture/multiapp1.yaml +20 -0
  286. mapproxy/test/system/fixture/multiapp2.yaml +19 -0
  287. mapproxy/test/system/fixture/renderd_client.yaml +55 -0
  288. mapproxy/test/system/fixture/scalehints.yaml +70 -0
  289. mapproxy/test/system/fixture/seed.yaml +94 -0
  290. mapproxy/test/system/fixture/seed_mapproxy.yaml +39 -0
  291. mapproxy/test/system/fixture/seed_old.yaml +12 -0
  292. mapproxy/test/system/fixture/seed_timeouts.yaml +12 -0
  293. mapproxy/test/system/fixture/seed_timeouts_mapproxy.yaml +27 -0
  294. mapproxy/test/system/fixture/seedonly.yaml +51 -0
  295. mapproxy/test/system/fixture/sld.yaml +35 -0
  296. mapproxy/test/system/fixture/source_errors.yaml +84 -0
  297. mapproxy/test/system/fixture/source_errors_raise.yaml +82 -0
  298. mapproxy/test/system/fixture/tileservice_origin.yaml +26 -0
  299. mapproxy/test/system/fixture/tileservice_refresh.yaml +59 -0
  300. mapproxy/test/system/fixture/tilesource_minmax_res.yaml +22 -0
  301. mapproxy/test/system/fixture/util-conf-base-grids.yaml +5 -0
  302. mapproxy/test/system/fixture/util-conf-overwrite.yaml +13 -0
  303. mapproxy/test/system/fixture/util-conf-wms-111-cap.xml +90 -0
  304. mapproxy/test/system/fixture/util_grids.yaml +29 -0
  305. mapproxy/test/system/fixture/util_wms_capabilities111.xml +130 -0
  306. mapproxy/test/system/fixture/util_wms_capabilities130.xml +100 -0
  307. mapproxy/test/system/fixture/util_wms_capabilities_service_exception.xml +5 -0
  308. mapproxy/test/system/fixture/watermark.yaml +50 -0
  309. mapproxy/test/system/fixture/wms_srs_extent.yaml +39 -0
  310. mapproxy/test/system/fixture/wms_versions.yaml +38 -0
  311. mapproxy/test/system/fixture/wmts.yaml +134 -0
  312. mapproxy/test/system/fixture/wmts_dimensions.yaml +57 -0
  313. mapproxy/test/system/fixture/xslt_featureinfo.yaml +54 -0
  314. mapproxy/test/system/fixture/xslt_featureinfo_input.yaml +51 -0
  315. mapproxy/test/system/test_arcgis.py +156 -0
  316. mapproxy/test/system/test_auth.py +1133 -0
  317. mapproxy/test/system/test_behind_proxy.py +75 -0
  318. mapproxy/test/system/test_bulk_meta_tiles.py +106 -0
  319. mapproxy/test/system/test_cache_azureblob.py +127 -0
  320. mapproxy/test/system/test_cache_band_merge.py +103 -0
  321. mapproxy/test/system/test_cache_coverage.py +168 -0
  322. mapproxy/test/system/test_cache_geopackage.py +144 -0
  323. mapproxy/test/system/test_cache_grid_names.py +89 -0
  324. mapproxy/test/system/test_cache_mbtiles.py +85 -0
  325. mapproxy/test/system/test_cache_s3.py +115 -0
  326. mapproxy/test/system/test_cache_source.py +146 -0
  327. mapproxy/test/system/test_combined_sources.py +335 -0
  328. mapproxy/test/system/test_coverage.py +140 -0
  329. mapproxy/test/system/test_decorate_img.py +214 -0
  330. mapproxy/test/system/test_demo.py +56 -0
  331. mapproxy/test/system/test_demo_with_extra_service.py +57 -0
  332. mapproxy/test/system/test_dimensions.py +279 -0
  333. mapproxy/test/system/test_disable_storage.py +42 -0
  334. mapproxy/test/system/test_formats.py +219 -0
  335. mapproxy/test/system/test_inspire_vs.py +173 -0
  336. mapproxy/test/system/test_kml.py +264 -0
  337. mapproxy/test/system/test_layergroups.py +160 -0
  338. mapproxy/test/system/test_legendgraphic.py +308 -0
  339. mapproxy/test/system/test_mapnik.py +162 -0
  340. mapproxy/test/system/test_mapserver.py +83 -0
  341. mapproxy/test/system/test_mixed_mode_format.py +201 -0
  342. mapproxy/test/system/test_multi_cache_layers.py +169 -0
  343. mapproxy/test/system/test_multiapp.py +92 -0
  344. mapproxy/test/system/test_refresh.py +206 -0
  345. mapproxy/test/system/test_renderd_client.py +304 -0
  346. mapproxy/test/system/test_response_headers.py +54 -0
  347. mapproxy/test/system/test_scalehints.py +140 -0
  348. mapproxy/test/system/test_seed.py +425 -0
  349. mapproxy/test/system/test_seed_only.py +93 -0
  350. mapproxy/test/system/test_sld.py +120 -0
  351. mapproxy/test/system/test_source_errors.py +377 -0
  352. mapproxy/test/system/test_tilesource_minmax_res.py +54 -0
  353. mapproxy/test/system/test_tms.py +277 -0
  354. mapproxy/test/system/test_tms_origin.py +46 -0
  355. mapproxy/test/system/test_util_conf.py +434 -0
  356. mapproxy/test/system/test_util_export.py +210 -0
  357. mapproxy/test/system/test_util_grids.py +88 -0
  358. mapproxy/test/system/test_util_wms_capabilities.py +182 -0
  359. mapproxy/test/system/test_watermark.py +91 -0
  360. mapproxy/test/system/test_wms.py +1616 -0
  361. mapproxy/test/system/test_wms_srs_extent.py +165 -0
  362. mapproxy/test/system/test_wms_version.py +85 -0
  363. mapproxy/test/system/test_wmsc.py +116 -0
  364. mapproxy/test/system/test_wmts.py +334 -0
  365. mapproxy/test/system/test_wmts_dimensions.py +206 -0
  366. mapproxy/test/system/test_wmts_restful.py +198 -0
  367. mapproxy/test/system/test_xslt_featureinfo.py +423 -0
  368. mapproxy/test/test_http_helper.py +217 -0
  369. mapproxy/test/unit/__init__.py +0 -0
  370. mapproxy/test/unit/epsg +2 -0
  371. mapproxy/test/unit/polygons/polygons.dbf +0 -0
  372. mapproxy/test/unit/polygons/polygons.shp +0 -0
  373. mapproxy/test/unit/polygons/polygons.shx +0 -0
  374. mapproxy/test/unit/test_async.py +242 -0
  375. mapproxy/test/unit/test_auth.py +430 -0
  376. mapproxy/test/unit/test_cache.py +1356 -0
  377. mapproxy/test/unit/test_cache_azureblob.py +97 -0
  378. mapproxy/test/unit/test_cache_compact.py +324 -0
  379. mapproxy/test/unit/test_cache_couchdb.py +118 -0
  380. mapproxy/test/unit/test_cache_geopackage.py +256 -0
  381. mapproxy/test/unit/test_cache_redis.py +123 -0
  382. mapproxy/test/unit/test_cache_riak.py +80 -0
  383. mapproxy/test/unit/test_cache_s3.py +93 -0
  384. mapproxy/test/unit/test_cache_tile.py +477 -0
  385. mapproxy/test/unit/test_client.py +488 -0
  386. mapproxy/test/unit/test_client_arcgis.py +74 -0
  387. mapproxy/test/unit/test_client_cgi.py +140 -0
  388. mapproxy/test/unit/test_collections.py +116 -0
  389. mapproxy/test/unit/test_concat_legends.py +37 -0
  390. mapproxy/test/unit/test_conf_loader.py +1267 -0
  391. mapproxy/test/unit/test_conf_validator.py +427 -0
  392. mapproxy/test/unit/test_config.py +118 -0
  393. mapproxy/test/unit/test_decorate_img.py +185 -0
  394. mapproxy/test/unit/test_exceptions.py +270 -0
  395. mapproxy/test/unit/test_featureinfo.py +313 -0
  396. mapproxy/test/unit/test_file_lock_load.py +49 -0
  397. mapproxy/test/unit/test_geom.py +512 -0
  398. mapproxy/test/unit/test_grid.py +1279 -0
  399. mapproxy/test/unit/test_image.py +1051 -0
  400. mapproxy/test/unit/test_image_mask.py +181 -0
  401. mapproxy/test/unit/test_image_messages.py +209 -0
  402. mapproxy/test/unit/test_image_options.py +160 -0
  403. mapproxy/test/unit/test_isodate.py +118 -0
  404. mapproxy/test/unit/test_multiapp.py +163 -0
  405. mapproxy/test/unit/test_ogr_reader.py +51 -0
  406. mapproxy/test/unit/test_request.py +745 -0
  407. mapproxy/test/unit/test_request_wmts.py +178 -0
  408. mapproxy/test/unit/test_response.py +78 -0
  409. mapproxy/test/unit/test_seed.py +365 -0
  410. mapproxy/test/unit/test_seed_cachelock.py +91 -0
  411. mapproxy/test/unit/test_srs.py +215 -0
  412. mapproxy/test/unit/test_tiled_source.py +122 -0
  413. mapproxy/test/unit/test_tilefilter.py +31 -0
  414. mapproxy/test/unit/test_times.py +25 -0
  415. mapproxy/test/unit/test_timeutils.py +50 -0
  416. mapproxy/test/unit/test_util_conf_utils.py +75 -0
  417. mapproxy/test/unit/test_utils.py +476 -0
  418. mapproxy/test/unit/test_wms_capabilities.py +44 -0
  419. mapproxy/test/unit/test_wms_layer.py +113 -0
  420. mapproxy/test/unit/test_yaml.py +68 -0
  421. mapproxy/tilefilter.py +61 -0
  422. mapproxy/util/__init__.py +0 -0
  423. mapproxy/util/async_.py +229 -0
  424. mapproxy/util/collections.py +134 -0
  425. mapproxy/util/coverage.py +337 -0
  426. mapproxy/util/ext/__init__.py +14 -0
  427. mapproxy/util/ext/dictspec/__init__.py +1 -0
  428. mapproxy/util/ext/dictspec/spec.py +131 -0
  429. mapproxy/util/ext/dictspec/test/__init__.py +0 -0
  430. mapproxy/util/ext/dictspec/test/test_validator.py +278 -0
  431. mapproxy/util/ext/dictspec/validator.py +194 -0
  432. mapproxy/util/ext/local.py +198 -0
  433. mapproxy/util/ext/lockfile.py +140 -0
  434. mapproxy/util/ext/odict.py +321 -0
  435. mapproxy/util/ext/serving.py +491 -0
  436. mapproxy/util/ext/tempita/__init__.py +1093 -0
  437. mapproxy/util/ext/tempita/_looper.py +163 -0
  438. mapproxy/util/ext/tempita/string_utils.py +24 -0
  439. mapproxy/util/ext/wmsparse/__init__.py +3 -0
  440. mapproxy/util/ext/wmsparse/duration.py +600 -0
  441. mapproxy/util/ext/wmsparse/parse.py +307 -0
  442. mapproxy/util/ext/wmsparse/test/__init__.py +0 -0
  443. mapproxy/util/ext/wmsparse/test/test_parse.py +111 -0
  444. mapproxy/util/ext/wmsparse/test/test_util.py +23 -0
  445. mapproxy/util/ext/wmsparse/test/wms-example-111.xml +90 -0
  446. mapproxy/util/ext/wmsparse/test/wms-example-130.xml +120 -0
  447. mapproxy/util/ext/wmsparse/test/wms-large-111.xml +2114 -0
  448. mapproxy/util/ext/wmsparse/test/wms_nasa_cap.xml +386 -0
  449. mapproxy/util/ext/wmsparse/util.py +189 -0
  450. mapproxy/util/fs.py +164 -0
  451. mapproxy/util/geom.py +307 -0
  452. mapproxy/util/lib.py +117 -0
  453. mapproxy/util/lock.py +171 -0
  454. mapproxy/util/ogr.py +247 -0
  455. mapproxy/util/py.py +75 -0
  456. mapproxy/util/times.py +78 -0
  457. mapproxy/util/yaml.py +58 -0
  458. mapproxy/version.py +33 -0
  459. mapproxy/wsgiapp.py +167 -0
@@ -0,0 +1,191 @@
1
+ # This file is part of the MapProxy project.
2
+ # Copyright (C) 2010 Omniscale <http://omniscale.de>
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ from __future__ import print_function
17
+
18
+ import os
19
+ from itertools import zip_longest
20
+
21
+ from mapproxy.seed.util import format_cleanup_task
22
+ from mapproxy.util.fs import cleanup_directory
23
+ from mapproxy.seed.seeder import (
24
+ TileWorkerPool, TileWalker, TileCleanupWorker,
25
+ SeedProgress,
26
+ )
27
+
28
+
29
+ def cleanup(tasks, concurrency=2, dry_run=False, skip_geoms_for_last_levels=0,
30
+ verbose=True, progress_logger=None):
31
+ for task in tasks:
32
+ print(format_cleanup_task(task))
33
+
34
+ if task.coverage is False:
35
+ continue
36
+
37
+ # seed_progress for tilewalker cleanup
38
+ seed_progress = None
39
+ # cleanup_progress for os.walk based cleanup
40
+ cleanup_progress = None
41
+ if progress_logger and progress_logger.progress_store:
42
+ progress_logger.current_task_id = task.id
43
+ start_progress = progress_logger.progress_store.get(task.id)
44
+ seed_progress = SeedProgress(old_progress_identifier=start_progress)
45
+ cleanup_progress = DirectoryCleanupProgress(old_dir=start_progress)
46
+
47
+ if task.complete_extent:
48
+ if callable(getattr(task.tile_manager.cache, 'level_location', None)):
49
+ simple_cleanup(task, dry_run=dry_run, progress_logger=progress_logger,
50
+ cleanup_progress=cleanup_progress)
51
+ task.tile_manager.cleanup()
52
+ continue
53
+ elif callable(getattr(task.tile_manager.cache, 'remove_level_tiles_before', None)):
54
+ cache_cleanup(task, dry_run=dry_run, progress_logger=progress_logger)
55
+ task.tile_manager.cleanup()
56
+ continue
57
+
58
+ tilewalker_cleanup(task, dry_run=dry_run, concurrency=concurrency,
59
+ skip_geoms_for_last_levels=skip_geoms_for_last_levels,
60
+ progress_logger=progress_logger,
61
+ seed_progress=seed_progress,
62
+ )
63
+ task.tile_manager.cleanup()
64
+
65
+
66
+ def simple_cleanup(task, dry_run, progress_logger=None, cleanup_progress=None):
67
+ """
68
+ Cleanup cache level on file system level.
69
+ """
70
+
71
+ for level in task.levels:
72
+ level_dir = task.tile_manager.cache.level_location(level)
73
+ if dry_run:
74
+ def file_handler(filename):
75
+ print('removing ' + filename)
76
+ else:
77
+ file_handler = None
78
+ if progress_logger:
79
+ progress_logger.log_message('removing old tiles in ' + normpath(level_dir))
80
+ if progress_logger.progress_store:
81
+ cleanup_progress.step_dir(level_dir)
82
+ if cleanup_progress.already_processed():
83
+ continue
84
+ progress_logger.progress_store.add(
85
+ task.id,
86
+ cleanup_progress.current_progress_identifier(),
87
+ )
88
+ progress_logger.progress_store.write()
89
+
90
+ cleanup_directory(level_dir, task.remove_timestamp,
91
+ file_handler=file_handler, remove_empty_dirs=True)
92
+
93
+
94
+ def cache_cleanup(task, dry_run, progress_logger=None):
95
+ for level in task.levels:
96
+ if progress_logger:
97
+ progress_logger.log_message('removing old tiles for level %s' % level)
98
+ if not dry_run:
99
+ task.tile_manager.cache.remove_level_tiles_before(level, task.remove_timestamp)
100
+ task.tile_manager.cleanup()
101
+
102
+
103
+ def normpath(path):
104
+ # relpath doesn't support UNC
105
+ if path.startswith('\\'):
106
+ return path
107
+
108
+ path = os.path.relpath(path)
109
+
110
+ if path.startswith('../../'):
111
+ path = os.path.abspath(path)
112
+ return path
113
+
114
+
115
+ def tilewalker_cleanup(task, dry_run, concurrency, skip_geoms_for_last_levels,
116
+ progress_logger=None, seed_progress=None):
117
+ """
118
+ Cleanup tiles with tile traversal.
119
+ """
120
+ task.tile_manager._expire_timestamp = task.remove_timestamp
121
+ task.tile_manager.minimize_meta_requests = False
122
+ tile_worker_pool = TileWorkerPool(task, TileCleanupWorker, progress_logger=progress_logger,
123
+ dry_run=dry_run, size=concurrency)
124
+ tile_walker = TileWalker(task, tile_worker_pool, handle_stale=True,
125
+ work_on_metatiles=False, progress_logger=progress_logger,
126
+ skip_geoms_for_last_levels=skip_geoms_for_last_levels,
127
+ seed_progress=seed_progress)
128
+ try:
129
+ tile_walker.walk()
130
+ except KeyboardInterrupt:
131
+ tile_worker_pool.stop(force=True)
132
+ raise
133
+ finally:
134
+ tile_worker_pool.stop()
135
+
136
+
137
+ class DirectoryCleanupProgress(object):
138
+ def __init__(self, old_dir=None):
139
+ self.old_dir = old_dir
140
+ self.current_dir = None
141
+
142
+ def step_dir(self, dir):
143
+ self.current_dir = dir
144
+
145
+ def already_processed(self):
146
+ return self.can_skip(self.old_dir, self.current_dir)
147
+
148
+ def current_progress_identifier(self):
149
+ if self.already_processed() or self.current_dir is None:
150
+ return self.old_dir
151
+ return self.current_dir
152
+
153
+ @staticmethod
154
+ def can_skip(old_dir, current_dir):
155
+ """
156
+ Return True if the `current_dir` is before `old_dir` when compared
157
+ lexicographic.
158
+
159
+ >>> DirectoryCleanupProgress.can_skip(None, '/00')
160
+ False
161
+ >>> DirectoryCleanupProgress.can_skip(None, '/00/000/000')
162
+ False
163
+
164
+ >>> DirectoryCleanupProgress.can_skip('/01/000/001', '/00')
165
+ True
166
+ >>> DirectoryCleanupProgress.can_skip('/01/000/001', '/01/000/000')
167
+ True
168
+ >>> DirectoryCleanupProgress.can_skip('/01/000/001', '/01/000/000/000')
169
+ True
170
+ >>> DirectoryCleanupProgress.can_skip('/01/000/001', '/01/000/001')
171
+ False
172
+ >>> DirectoryCleanupProgress.can_skip('/01/000/001', '/01/000/001/000')
173
+ False
174
+ """
175
+ if old_dir is None:
176
+ return False
177
+ if current_dir is None:
178
+ return False
179
+ for old, current in zip_longest(old_dir.split(os.path.sep), current_dir.split(os.path.sep), fillvalue=None):
180
+ if old is None:
181
+ return False
182
+ if current is None:
183
+ return False
184
+ if old < current:
185
+ return False
186
+ if old > current:
187
+ return True
188
+ return False
189
+
190
+ def running(self):
191
+ return True
@@ -0,0 +1,481 @@
1
+ # This file is part of the MapProxy project.
2
+ # Copyright (C) 2011 Omniscale <http://omniscale.de>
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ from __future__ import print_function
17
+ import logging
18
+
19
+ import os
20
+ import sys
21
+ import time
22
+ import operator
23
+ from functools import reduce
24
+
25
+ from mapproxy.cache.dummy import DummyCache
26
+ from mapproxy.config import abspath
27
+ from mapproxy.config.loader import ConfigurationError
28
+ from mapproxy.config.coverage import load_coverage
29
+ from mapproxy.srs import SRS, TransformationError
30
+ from mapproxy.util.py import memoize
31
+ from mapproxy.util.times import timestamp_from_isodate, timestamp_before
32
+ from mapproxy.util.coverage import MultiCoverage, BBOXCoverage, GeomCoverage
33
+ from mapproxy.util.geom import GeometryError, EmptyGeometryError, CoverageReadError
34
+ from mapproxy.util.yaml import load_yaml_file, YAMLError
35
+ from mapproxy.seed.util import bidict
36
+ from mapproxy.seed.seeder import SeedTask, CleanupTask
37
+ from mapproxy.seed.spec import validate_seed_conf
38
+
39
+
40
+ class SeedConfigurationError(ConfigurationError):
41
+ pass
42
+
43
+
44
+ class EmptyCoverageError(Exception):
45
+ pass
46
+
47
+
48
+ log = logging.getLogger('mapproxy.seed.config')
49
+
50
+
51
+ def load_seed_tasks_conf(seed_conf_filename, mapproxy_conf):
52
+ try:
53
+ conf = load_yaml_file(seed_conf_filename)
54
+ except YAMLError as ex:
55
+ raise SeedConfigurationError(ex)
56
+
57
+ if 'views' in conf:
58
+ # TODO: deprecate old config
59
+ seed_conf = LegacySeedingConfiguration(conf, mapproxy_conf=mapproxy_conf)
60
+ else:
61
+ errors, informal_only = validate_seed_conf(conf)
62
+ for error in errors:
63
+ log.warning(error)
64
+ if not informal_only:
65
+ raise SeedConfigurationError('invalid configuration')
66
+ seed_conf = SeedingConfiguration(conf, mapproxy_conf=mapproxy_conf)
67
+ return seed_conf
68
+
69
+
70
+ class LegacySeedingConfiguration(object):
71
+ """
72
+ Read old seed.yaml configuration (with seed and views).
73
+ """
74
+
75
+ def __init__(self, seed_conf, mapproxy_conf):
76
+ self.conf = seed_conf
77
+ self.mapproxy_conf = mapproxy_conf
78
+ self.grids = bidict((name, grid_conf.tile_grid()) for name, grid_conf in self.mapproxy_conf.grids.items())
79
+ self.seed_tasks = []
80
+ self.cleanup_tasks = []
81
+ self._init_tasks()
82
+
83
+ def _init_tasks(self):
84
+ for cache_name, options in self.conf['seeds'].items():
85
+ remove_before = None
86
+ if 'remove_before' in options:
87
+ remove_before = before_timestamp_from_options(options['remove_before'])
88
+ try:
89
+ caches = self.mapproxy_conf.caches[cache_name].caches()
90
+ except KeyError:
91
+ print('error: cache %s not found. available caches: %s' % (
92
+ cache_name, ','.join(self.mapproxy_conf.caches.keys())), file=sys.stderr)
93
+ return
94
+ caches = dict((grid, tile_mgr) for grid, extent, tile_mgr in caches)
95
+ for view in options['views']:
96
+ view_conf = self.conf['views'][view]
97
+ coverage = load_coverage(view_conf)
98
+
99
+ cache_srs = view_conf.get('srs', None)
100
+ if cache_srs is not None:
101
+ cache_srs = [SRS(s) for s in cache_srs]
102
+
103
+ level = view_conf.get('level', None)
104
+ assert len(level) == 2
105
+
106
+ for grid, tile_mgr in caches.items():
107
+ if cache_srs and grid.srs not in cache_srs:
108
+ continue
109
+ md = dict(name=view, cache_name=cache_name, grid_name=self.grids[grid])
110
+ levels = list(range(level[0], level[1]+1))
111
+ if coverage:
112
+ if isinstance(coverage, GeomCoverage) and coverage.geom.is_empty:
113
+ continue
114
+ seed_coverage = coverage.transform_to(grid.srs)
115
+ else:
116
+ seed_coverage = BBOXCoverage(grid.bbox, grid.srs)
117
+
118
+ self.seed_tasks.append(SeedTask(md, tile_mgr, levels, remove_before, seed_coverage))
119
+
120
+ if remove_before:
121
+ levels = list(range(grid.levels))
122
+ complete_extent = bool(coverage)
123
+ self.cleanup_tasks.append(CleanupTask(md, tile_mgr, levels, remove_before,
124
+ seed_coverage, complete_extent=complete_extent))
125
+
126
+ def seed_tasks_names(self):
127
+ return self.conf['seeds'].keys()
128
+
129
+ def cleanup_tasks_names(self):
130
+ return self.conf['seeds'].keys()
131
+
132
+ def seeds(self, names=None):
133
+ if names is None:
134
+ return self.seed_tasks
135
+ else:
136
+ return [t for t in self.seed_tasks if t.md['name'] in names]
137
+
138
+ def cleanups(self, names=None):
139
+ if names is None:
140
+ return self.cleanup_tasks
141
+ else:
142
+ return [t for t in self.cleanup_tasks if t.md['name'] in names]
143
+
144
+
145
+ class SeedingConfiguration(object):
146
+ def __init__(self, seed_conf, mapproxy_conf):
147
+ self.conf = seed_conf
148
+ self.mapproxy_conf = mapproxy_conf
149
+ self.grids = bidict((name, grid_conf.tile_grid()) for name, grid_conf in self.mapproxy_conf.grids.items())
150
+
151
+ @memoize
152
+ def coverage(self, name):
153
+ coverage_conf = (self.conf.get('coverages') or {}).get(name)
154
+ if coverage_conf is None:
155
+ raise SeedConfigurationError('coverage %s not found. available coverages: %s' % (
156
+ name, ','.join((self.conf.get('coverages') or {}).keys())))
157
+
158
+ try:
159
+ coverage = load_coverage(coverage_conf)
160
+ except CoverageReadError as ex:
161
+ raise SeedConfigurationError("can't load coverage '%s'. %s" % (name, ex))
162
+ except GeometryError as ex:
163
+ raise SeedConfigurationError("invalid geometry in coverage '%s'. %s" % (name, ex))
164
+ except EmptyGeometryError as ex:
165
+ raise EmptyCoverageError("coverage '%s' contains no geometries. %s" % (name, ex))
166
+
167
+ # without extend we have an empty coverage
168
+ if not coverage.extent.llbbox:
169
+ raise EmptyCoverageError("coverage '%s' contains no geometries." % name)
170
+ return coverage
171
+
172
+ def cache(self, cache_name):
173
+ cache = {}
174
+ if cache_name not in self.mapproxy_conf.caches:
175
+ raise SeedConfigurationError('cache %s not found. available caches: %s' % (
176
+ cache_name, ','.join(self.mapproxy_conf.caches.keys())))
177
+ for tile_grid, extent, tile_mgr in self.mapproxy_conf.caches[cache_name].caches():
178
+ if isinstance(tile_mgr.cache, DummyCache):
179
+ raise SeedConfigurationError('can\'t seed cache %s (disable_storage: true)' %
180
+ cache_name)
181
+ grid_name = self.grids[tile_grid]
182
+ cache[grid_name] = tile_mgr
183
+ return cache
184
+
185
+ def seed_tasks_names(self):
186
+ seeds = self.conf.get('seeds') or {}
187
+ if seeds:
188
+ return seeds.keys()
189
+ return []
190
+
191
+ def cleanup_tasks_names(self):
192
+ cleanups = self.conf.get('cleanups') or {}
193
+ if cleanups:
194
+ return cleanups.keys()
195
+ return []
196
+
197
+ def seeds(self, names=None):
198
+ """
199
+ Return seed tasks.
200
+ """
201
+ tasks = []
202
+ if names is None:
203
+ names = (self.conf.get('seeds') or {}).keys()
204
+
205
+ for seed_name in names:
206
+ seed_conf = self.conf['seeds'][seed_name]
207
+ seed_conf = SeedConfiguration(seed_name, seed_conf, self)
208
+ for task in seed_conf.seed_tasks():
209
+ tasks.append(task)
210
+ return tasks
211
+
212
+ def cleanups(self, names=None):
213
+ """
214
+ Return cleanup tasks.
215
+ """
216
+ tasks = []
217
+ if names is None:
218
+ names = (self.conf.get('cleanups') or {}).keys()
219
+
220
+ for cleanup_name in names:
221
+ cleanup_conf = self.conf['cleanups'][cleanup_name]
222
+ cleanup_conf = CleanupConfiguration(cleanup_name, cleanup_conf, self)
223
+ for task in cleanup_conf.cleanup_tasks():
224
+ tasks.append(task)
225
+ return tasks
226
+
227
+
228
+ class ConfigurationBase(object):
229
+ def __init__(self, name, conf, seeding_conf):
230
+ self.name = name
231
+ self.conf = conf
232
+ self.seeding_conf = seeding_conf
233
+
234
+ self.coverage = self._coverages()
235
+ self.caches = self._caches()
236
+ self.grids = self._grids(self.caches)
237
+ self.levels = levels_from_options(conf)
238
+
239
+ def _coverages(self):
240
+ coverage = None
241
+ if 'coverages' in self.conf:
242
+ try:
243
+ coverages = [self.seeding_conf.coverage(c) for c in self.conf.get('coverages', {})]
244
+ except EmptyCoverageError:
245
+ return False
246
+ if len(coverages) == 1:
247
+ coverage = coverages[0]
248
+ else:
249
+ coverage = MultiCoverage(coverages)
250
+ return coverage
251
+
252
+ def _grids(self, caches):
253
+ grids = []
254
+
255
+ if 'grids' in self.conf:
256
+ # grids available for all caches
257
+ available_grids = reduce(operator.and_, (set(cache) for cache in caches.values()))
258
+ for grid_name in self.conf['grids']:
259
+ if grid_name not in available_grids:
260
+ raise SeedConfigurationError('%s not defined for caches' % grid_name)
261
+ grids.append(grid_name)
262
+ else:
263
+ # check that all caches have the same grids configured
264
+ last = []
265
+ for cache_grids in {cache.keys() for cache in caches.values()}:
266
+ if not last:
267
+ last = cache_grids
268
+ else:
269
+ if last != cache_grids:
270
+ raise SeedConfigurationError('caches in same seed task require identical grids')
271
+ grids = list(last or [])
272
+ return grids
273
+
274
+ def _caches(self):
275
+ """
276
+ Returns a dictionary with all caches for this seed.
277
+
278
+ e.g.: {'seed1': {'grid1': tilemanager1, 'grid2': tilemanager2}}
279
+ """
280
+ caches = {}
281
+ for cache_name in self.conf.get('caches', []):
282
+ caches[cache_name] = self.seeding_conf.cache(cache_name)
283
+ return caches
284
+
285
+
286
+ class SeedConfiguration(ConfigurationBase):
287
+ def __init__(self, name, conf, seeding_conf):
288
+ ConfigurationBase.__init__(self, name, conf, seeding_conf)
289
+
290
+ self.refresh_timestamp = None
291
+ if 'refresh_before' in self.conf:
292
+ self.refresh_timestamp = before_timestamp_from_options(self.conf['refresh_before'])
293
+
294
+ def seed_tasks(self):
295
+ for grid_name in self.grids:
296
+ for cache_name, cache in self.caches.items():
297
+ tile_manager = cache[grid_name]
298
+ grid = self.seeding_conf.grids[grid_name]
299
+ if self.coverage is False:
300
+ coverage = False
301
+ elif self.coverage:
302
+ coverage = self.coverage.transform_to(grid.srs)
303
+ else:
304
+ coverage = BBOXCoverage(grid.bbox, grid.srs)
305
+
306
+ try:
307
+ if coverage is not False:
308
+ coverage.extent.llbbox
309
+ except TransformationError:
310
+ raise SeedConfigurationError('%s: coverage transformation error' % self.name)
311
+
312
+ if self.levels:
313
+ levels = self.levels.for_grid(grid)
314
+ else:
315
+ levels = list(range(0, grid.levels))
316
+
317
+ if not tile_manager.cache.supports_timestamp:
318
+ if self.refresh_timestamp:
319
+ # remove everything
320
+ self.refresh_timestamp = 0
321
+
322
+ md = dict(name=self.name, cache_name=cache_name, grid_name=grid_name)
323
+
324
+ if tile_manager.rescale_tiles:
325
+ if tile_manager.rescale_tiles > 0:
326
+ levels = levels[::-1]
327
+ for level in levels:
328
+ yield SeedTask(md, tile_manager, [level], self.refresh_timestamp, coverage)
329
+ else:
330
+ yield SeedTask(md, tile_manager, levels, self.refresh_timestamp, coverage)
331
+
332
+
333
+ class CleanupConfiguration(ConfigurationBase):
334
+ def __init__(self, name, conf, seeding_conf):
335
+ ConfigurationBase.__init__(self, name, conf, seeding_conf)
336
+ self.init_time = time.time()
337
+
338
+ if self.conf.get('remove_all') is True:
339
+ self.remove_timestamp = 0
340
+ elif 'remove_before' in self.conf:
341
+ self.remove_timestamp = before_timestamp_from_options(self.conf['remove_before'])
342
+ else:
343
+ # use now as remove_before date. this should not remove
344
+ # fresh seeded tiles, since this should be configured before seeding
345
+ self.remove_timestamp = self.init_time
346
+
347
+ def cleanup_tasks(self):
348
+ for grid_name in self.grids:
349
+ for cache_name, cache in self.caches.items():
350
+ tile_manager = cache[grid_name]
351
+ grid = self.seeding_conf.grids[grid_name]
352
+ if self.coverage is False:
353
+ coverage = False
354
+ complete_extent = False
355
+ elif self.coverage:
356
+ coverage = self.coverage.transform_to(grid.srs)
357
+ complete_extent = False
358
+ else:
359
+ coverage = BBOXCoverage(grid.bbox, grid.srs)
360
+ complete_extent = True
361
+
362
+ try:
363
+ if coverage is not False:
364
+ coverage.extent.llbbox
365
+ except TransformationError:
366
+ raise SeedConfigurationError('%s: coverage transformation error' % self.name)
367
+
368
+ if self.levels:
369
+ levels = self.levels.for_grid(grid)
370
+ else:
371
+ levels = list(range(0, grid.levels))
372
+
373
+ if not tile_manager.cache.supports_timestamp:
374
+ # for caches without timestamp support (like MBTiles)
375
+ if self.remove_timestamp is self.init_time or self.remove_timestamp == 0:
376
+ # remove everything
377
+ self.remove_timestamp = 0
378
+ else:
379
+ raise SeedConfigurationError(
380
+ "cleanup does not support remove_before for '%s'"
381
+ " because cache '%s' does not support timestamps" % (self.name, cache_name))
382
+ md = dict(name=self.name, cache_name=cache_name, grid_name=grid_name)
383
+ yield CleanupTask(md, tile_manager, levels, self.remove_timestamp,
384
+ coverage=coverage, complete_extent=complete_extent)
385
+
386
+
387
+ def levels_from_options(conf):
388
+ levels = conf.get('levels')
389
+ if levels:
390
+ if isinstance(levels, list):
391
+ return LevelsList(levels)
392
+ from_level = levels.get('from')
393
+ to_level = levels.get('to')
394
+ return LevelsRange((from_level, to_level))
395
+ resolutions = conf.get('resolutions')
396
+ if resolutions:
397
+ if isinstance(resolutions, list):
398
+ return LevelsResolutionList(resolutions)
399
+ from_res = resolutions.get('from')
400
+ to_res = resolutions.get('to')
401
+ return LevelsResolutionRange((from_res, to_res))
402
+ return None
403
+
404
+
405
+ def before_timestamp_from_options(conf):
406
+ """
407
+ >>> import time
408
+ >>> t = before_timestamp_from_options({'hours': 4})
409
+ >>> time.time() - t - 4 * 60 * 60 < 1
410
+ True
411
+ """
412
+ if 'time' in conf:
413
+ try:
414
+ return timestamp_from_isodate(conf['time'])
415
+ except ValueError:
416
+ raise SeedConfigurationError(
417
+ "can't parse time '%s'. should be ISO time string" % (conf["time"], ))
418
+ if 'mtime' in conf:
419
+ datasource = abspath(conf['mtime'])
420
+ try:
421
+ return os.path.getmtime(datasource)
422
+ except OSError as ex:
423
+ raise SeedConfigurationError(
424
+ "can't parse last modified time from file '%s'." % (datasource, ), ex)
425
+ deltas = {}
426
+ for delta_type in ('weeks', 'days', 'hours', 'minutes', 'seconds'):
427
+ deltas[delta_type] = conf.get(delta_type, 0)
428
+ return timestamp_before(**deltas)
429
+
430
+
431
+ class LevelsList(object):
432
+ def __init__(self, levels=None):
433
+ self.levels = levels
434
+
435
+ def for_grid(self, grid):
436
+ uniqe_valid_levels = set(x for x in self.levels if 0 <= x <= (grid.levels-1))
437
+ return sorted(uniqe_valid_levels)
438
+
439
+
440
+ class LevelsRange(object):
441
+ def __init__(self, level_range=None):
442
+ self.level_range = level_range
443
+
444
+ def for_grid(self, grid):
445
+ start, stop = self.level_range
446
+ if start is None:
447
+ start = 0
448
+ if stop is None:
449
+ stop = 999
450
+
451
+ stop = min(stop, grid.levels-1)
452
+
453
+ return list(range(start, stop+1))
454
+
455
+
456
+ class LevelsResolutionRange(object):
457
+ def __init__(self, res_range=None):
458
+ self.res_range = res_range
459
+
460
+ def for_grid(self, grid):
461
+ start, stop = self.res_range
462
+ if start is None:
463
+ start = 0
464
+ else:
465
+ start = grid.closest_level(start)
466
+
467
+ if stop is None:
468
+ stop = grid.levels-1
469
+ else:
470
+ stop = grid.closest_level(stop)
471
+
472
+ return list(range(start, stop+1))
473
+
474
+
475
+ class LevelsResolutionList(object):
476
+ def __init__(self, resolutions=None):
477
+ self.resolutions = resolutions
478
+
479
+ def for_grid(self, grid):
480
+ levels = set(grid.closest_level(res) for res in self.resolutions)
481
+ return sorted(levels)