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,301 @@
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
+
17
+ import codecs
18
+ import datetime
19
+ import json
20
+ import socket
21
+ import time
22
+ import hashlib
23
+ import base64
24
+ from io import BytesIO
25
+
26
+ from mapproxy.image import ImageSource
27
+ from mapproxy.cache.base import (
28
+ TileCacheBase,
29
+ tile_buffer, CacheBackendError,)
30
+ from mapproxy.source import SourceError
31
+ from mapproxy.srs import SRS
32
+
33
+ from threading import Lock
34
+
35
+ try:
36
+ import requests
37
+ except ImportError:
38
+ requests = None
39
+
40
+
41
+ import logging
42
+ log = logging.getLogger(__name__)
43
+
44
+
45
+ class UnexpectedResponse(CacheBackendError):
46
+ pass
47
+
48
+
49
+ class CouchDBCache(TileCacheBase):
50
+ def __init__(self, url, db_name,
51
+ file_ext, tile_grid, md_template=None,
52
+ tile_id_template=None, coverage=None):
53
+ super(CouchDBCache, self).__init__(coverage)
54
+
55
+ if requests is None:
56
+ raise ImportError("CouchDB backend requires 'requests' package.")
57
+
58
+ md5 = hashlib.new('md5', (url + db_name).encode('utf-8'), usedforsecurity=False)
59
+ self.lock_cache_id = 'couchdb-' + md5.hexdigest()
60
+ self.file_ext = file_ext
61
+ self.tile_grid = tile_grid
62
+ self.md_template = md_template
63
+ self.couch_url = '%s/%s' % (url.rstrip('/'), db_name.lower())
64
+ self.req_session = requests.Session()
65
+ self.req_session.timeout = 5
66
+ self.db_initialised = False
67
+ self.app_init_db_lock = Lock()
68
+ self.tile_id_template = tile_id_template
69
+
70
+ def init_db(self):
71
+ with self.app_init_db_lock:
72
+ if self.db_initialised:
73
+ return
74
+ try:
75
+ self.req_session.put(self.couch_url)
76
+ self.db_initialised = True
77
+ except requests.exceptions.RequestException as ex:
78
+ log.warning('unable to initialize CouchDB: %s', ex)
79
+
80
+ def tile_url(self, coord):
81
+ return self.document_url(coord) + '/tile'
82
+
83
+ def document_url(self, coord, relative=False):
84
+ x, y, z = coord
85
+ grid_name = self.tile_grid.name
86
+ couch_url = self.couch_url
87
+ if relative:
88
+ if self.tile_id_template:
89
+ if self.tile_id_template.startswith('%(couch_url)s/'):
90
+ tile_id_template = self.tile_id_template[len('%(couch_url)s/'):]
91
+ else:
92
+ tile_id_template = self.tile_id_template
93
+ return tile_id_template % locals()
94
+ else:
95
+ return '%(grid_name)s-%(z)s-%(x)s-%(y)s' % locals()
96
+ else:
97
+ if self.tile_id_template:
98
+ return self.tile_id_template % locals()
99
+ else:
100
+ return '%(couch_url)s/%(grid_name)s-%(z)s-%(x)s-%(y)s' % locals()
101
+
102
+ def is_cached(self, tile, dimensions=None):
103
+ if tile.coord is None or tile.source:
104
+ return True
105
+ url = self.document_url(tile.coord)
106
+ try:
107
+ self.init_db()
108
+ resp = self.req_session.get(url)
109
+ if resp.status_code == 200:
110
+ doc = json.loads(codecs.decode(resp.content, 'utf-8'))
111
+ tile.timestamp = doc.get(self.md_template.timestamp_key)
112
+ return True
113
+ except (requests.exceptions.RequestException, socket.error) as ex:
114
+ # is_cached should not fail (would abort seeding for example),
115
+ # so we catch these errors here and just return False
116
+ log.warning('error while requesting %s: %s', url, ex)
117
+ return False
118
+ if resp.status_code == 404:
119
+ return False
120
+ raise SourceError('%r: %r' % (resp.status_code, resp.content))
121
+
122
+ def _tile_doc(self, tile):
123
+ tile_id = self.document_url(tile.coord, relative=True)
124
+ if self.md_template:
125
+ tile_doc = self.md_template.doc(tile, self.tile_grid)
126
+ else:
127
+ tile_doc = {}
128
+ tile_doc['_id'] = tile_id
129
+
130
+ with tile_buffer(tile) as buf:
131
+ data = buf.read()
132
+ tile_doc['_attachments'] = {
133
+ 'tile': {
134
+ 'content_type': 'image/' + self.file_ext,
135
+ 'data': codecs.decode(
136
+ base64.b64encode(data).replace(b'\n', b''),
137
+ 'ascii',
138
+ ),
139
+ }
140
+ }
141
+ return tile_id, tile_doc
142
+
143
+ def _store_bulk(self, tiles):
144
+ tile_docs = {}
145
+ for tile in tiles:
146
+ tile_id, tile_doc = self._tile_doc(tile)
147
+ tile_docs[tile_id] = tile_doc
148
+
149
+ duplicate_tiles = self._post_bulk(tile_docs)
150
+
151
+ if duplicate_tiles:
152
+ self._fill_rev_ids(duplicate_tiles)
153
+ self._post_bulk(duplicate_tiles, no_conflicts=True)
154
+
155
+ return True
156
+
157
+ def _post_bulk(self, tile_docs, no_conflicts=False):
158
+ """
159
+ POST multiple tiles, returns all tile docs with conflicts during POST.
160
+ """
161
+ doc = {'docs': list(tile_docs.values())}
162
+ data = json.dumps(doc)
163
+ self.init_db()
164
+ resp = self.req_session.post(self.couch_url + '/_bulk_docs', data=data,
165
+ headers={'Content-type': 'application/json'})
166
+ if resp.status_code != 201:
167
+ raise UnexpectedResponse('got unexpected resp (%d) from CouchDB: %s' % (resp.status_code, resp.content))
168
+
169
+ resp_doc = json.loads(codecs.decode(resp.content, 'utf-8'))
170
+ duplicate_tiles = {}
171
+ for tile in resp_doc:
172
+ if tile.get('error', 'false') == 'conflict':
173
+ duplicate_tiles[tile['id']] = tile_docs[tile['id']]
174
+
175
+ if no_conflicts and duplicate_tiles:
176
+ raise UnexpectedResponse('got unexpected resp (%d) from CouchDB: %s' % (resp.status_code, resp.content))
177
+
178
+ return duplicate_tiles
179
+
180
+ def _fill_rev_ids(self, tile_docs):
181
+ """
182
+ Request all revs for tile_docs and insert it into the tile_docs.
183
+ """
184
+ keys_doc = {'keys': list(tile_docs.keys())}
185
+ data = json.dumps(keys_doc)
186
+ self.init_db()
187
+ resp = self.req_session.post(self.couch_url + '/_all_docs', data=data,
188
+ headers={'Content-type': 'application/json'})
189
+ if resp.status_code != 200:
190
+ raise UnexpectedResponse('got unexpected resp (%d) from CouchDB: %s' % (resp.status_code, resp.content))
191
+
192
+ resp_doc = json.loads(codecs.decode(resp.content, 'utf-8'))
193
+ for tile in resp_doc['rows']:
194
+ tile_docs[tile['id']]['_rev'] = tile['value']['rev']
195
+
196
+ def store_tile(self, tile, dimensions=None):
197
+ if tile.stored:
198
+ return True
199
+
200
+ return self._store_bulk([tile])
201
+
202
+ def store_tiles(self, tiles, dimensions=None):
203
+ tiles = [t for t in tiles if not t.stored]
204
+ return self._store_bulk(tiles)
205
+
206
+ def load_tile_metadata(self, tile, dimensions=None):
207
+ if tile.timestamp:
208
+ return
209
+
210
+ # is_cached loads metadata
211
+ self.is_cached(tile, dimensions=None)
212
+
213
+ def load_tile(self, tile, with_metadata=False, dimensions=None):
214
+ # bulk loading with load_tiles is not implemented, because
215
+ # CouchDB's /all_docs? does not include attachments
216
+
217
+ if tile.source or tile.coord is None:
218
+ return True
219
+ url = self.document_url(tile.coord) + '?attachments=true'
220
+ self.init_db()
221
+ resp = self.req_session.get(url, headers={'Accept': 'application/json'})
222
+ if resp.status_code == 200:
223
+ doc = json.loads(codecs.decode(resp.content, 'utf-8'))
224
+ tile_data = BytesIO(base64.b64decode(doc['_attachments']['tile']['data']))
225
+ tile.source = ImageSource(tile_data)
226
+ tile.timestamp = doc.get(self.md_template.timestamp_key)
227
+ return True
228
+ return False
229
+
230
+ def remove_tile(self, tile):
231
+ if tile.coord is None:
232
+ return True
233
+ url = self.document_url(tile.coord)
234
+ resp = requests.head(url)
235
+ if resp.status_code == 404:
236
+ # already removed
237
+ return True
238
+ rev_id = resp.headers['etag']
239
+ url += '?rev=' + rev_id.strip('"')
240
+ self.init_db()
241
+ resp = self.req_session.delete(url)
242
+ if resp.status_code == 200:
243
+ return True
244
+ return False
245
+
246
+
247
+ def utc_now_isoformat():
248
+ now = datetime.datetime.utcnow()
249
+ now = now.isoformat()
250
+ # remove milliseconds, add Zulu timezone
251
+ now = now.rsplit('.', 1)[0] + 'Z'
252
+ return now
253
+
254
+
255
+ class CouchDBMDTemplate(object):
256
+ def __init__(self, attributes):
257
+ self.attributes = attributes
258
+ for key, value in attributes.items():
259
+ if value == '{{timestamp}}':
260
+ self.timestamp_key = key
261
+ break
262
+ else:
263
+ attributes['timestamp'] = '{{timestamp}}'
264
+ self.timestamp_key = 'timestamp'
265
+
266
+ def doc(self, tile, grid):
267
+ doc = {}
268
+ x, y, z = tile.coord
269
+ for key, value in self.attributes.items():
270
+ if not isinstance(value, str) or not value.startswith('{{'):
271
+ doc[key] = value
272
+ continue
273
+
274
+ if value == '{{timestamp}}':
275
+ doc[key] = time.time()
276
+ elif value == '{{x}}':
277
+ doc[key] = x
278
+ elif value == '{{y}}':
279
+ doc[key] = y
280
+ elif value in ('{{z}}', '{{level}}'):
281
+ doc[key] = z
282
+ elif value == '{{utc_iso}}':
283
+ doc[key] = utc_now_isoformat()
284
+ elif value == '{{wgs_tile_centroid}}':
285
+ tile_bbox = grid.tile_bbox(tile.coord)
286
+ centroid = (
287
+ tile_bbox[0] + (tile_bbox[2]-tile_bbox[0])/2,
288
+ tile_bbox[1] + (tile_bbox[3]-tile_bbox[1])/2
289
+ )
290
+ centroid = grid.srs.transform_to(SRS(4326), centroid)
291
+ doc[key] = centroid
292
+ elif value == '{{tile_centroid}}':
293
+ tile_bbox = grid.tile_bbox(tile.coord)
294
+ centroid = (
295
+ tile_bbox[0] + (tile_bbox[2]-tile_bbox[0])/2,
296
+ tile_bbox[1] + (tile_bbox[3]-tile_bbox[1])/2
297
+ )
298
+ doc[key] = centroid
299
+ else:
300
+ raise ValueError('unknown CouchDB tile_metadata value: %r' % (value, ))
301
+ return doc
@@ -0,0 +1,36 @@
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 mapproxy.cache.base import TileCacheBase
17
+ from mapproxy.util.lock import DummyLock
18
+
19
+
20
+ class DummyCache(TileCacheBase):
21
+ def is_cached(self, tile, dimensions=None):
22
+ return False
23
+
24
+ def lock(self, tile):
25
+ return DummyLock()
26
+
27
+ def load_tile(self, tile, with_metadata=False, dimensions=None):
28
+ pass
29
+
30
+ def store_tile(self, tile, dimensions=None):
31
+ pass
32
+
33
+
34
+ class DummyLocker(object):
35
+ def lock(self, tile):
36
+ return DummyLock()
mapproxy/cache/file.py ADDED
@@ -0,0 +1,200 @@
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
+ import os
17
+ import errno
18
+ import hashlib
19
+
20
+ from mapproxy.util.fs import ensure_directory, write_atomic
21
+ from mapproxy.image import ImageSource, is_single_color_image
22
+ from mapproxy.cache import path
23
+ from mapproxy.cache.base import TileCacheBase, tile_buffer
24
+
25
+ import logging
26
+ log = logging.getLogger('mapproxy.cache.file')
27
+
28
+
29
+ class FileCache(TileCacheBase):
30
+ """
31
+ This class is responsible to store and load the actual tile data.
32
+ """
33
+ supports_dimensions = True
34
+
35
+ def __init__(self, cache_dir, file_ext, directory_layout='tc',
36
+ link_single_color_images=False, coverage=None, image_opts=None):
37
+ """
38
+ :param cache_dir: the path where the tile will be stored
39
+ :param file_ext: the file extension that will be appended to
40
+ each tile (e.g. 'png')
41
+ """
42
+ super(FileCache, self).__init__(coverage)
43
+ md5 = hashlib.new('md5', cache_dir.encode('utf-8'), usedforsecurity=False)
44
+ self.lock_cache_id = md5.hexdigest()
45
+ self.cache_dir = cache_dir
46
+ self.file_ext = file_ext
47
+ self.image_opts = image_opts
48
+ self.link_single_color_images = link_single_color_images
49
+ self._tile_location, self._level_location = path.location_funcs(layout=directory_layout)
50
+ if self._level_location is None:
51
+ self.level_location = None # disable level based clean-ups
52
+
53
+ def tile_location(self, tile, create_dir=False, dimensions=None):
54
+ if dimensions is not None and len(dimensions) > 0:
55
+ items = list(dimensions.keys())
56
+ items.sort()
57
+ dimensions_str = ['{key}-{value}'.format(key=i, value=dimensions[i].replace('/', '_')) for i in items]
58
+ # todo: cache_dir is not used. should it get returned or removed?
59
+ cache_dir = os.path.join(self.cache_dir, '_'.join(dimensions_str)) # noqa
60
+ return self._tile_location(tile, self.cache_dir, self.file_ext, create_dir=create_dir, dimensions=dimensions)
61
+
62
+ def level_location(self, level, dimensions=None):
63
+ """
64
+ Return the path where all tiles for `level` will be stored.
65
+
66
+ >>> c = FileCache(cache_dir='/tmp/cache/', file_ext='png')
67
+ >>> c.level_location(2)
68
+ '/tmp/cache/02'
69
+ """
70
+ return self._level_location(level, self.cache_dir, dimensions)
71
+
72
+ def _single_color_tile_location(self, color, create_dir=False):
73
+ """
74
+ >>> c = FileCache(cache_dir='/tmp/cache/', file_ext='png')
75
+ >>> c._single_color_tile_location((254, 0, 4)).replace('\\\\', '/')
76
+ '/tmp/cache/single_color_tiles/fe0004.png'
77
+ """
78
+ parts = (
79
+ self.cache_dir,
80
+ 'single_color_tiles',
81
+ ''.join('%02x' % v for v in color) + '.' + self.file_ext
82
+ )
83
+ location = os.path.join(*parts)
84
+ if create_dir:
85
+ ensure_directory(location)
86
+ return location
87
+
88
+ def load_tile_metadata(self, tile, dimensions=None):
89
+ location = self.tile_location(tile, dimensions=dimensions)
90
+ try:
91
+ stats = os.lstat(location)
92
+ tile.timestamp = stats.st_mtime
93
+ tile.size = stats.st_size
94
+ except OSError as ex:
95
+ if ex.errno != errno.ENOENT:
96
+ raise
97
+ tile.timestamp = 0
98
+ tile.size = 0
99
+
100
+ def is_cached(self, tile, dimensions=None):
101
+ """
102
+ Returns ``True`` if the tile data is present.
103
+ """
104
+ if tile.is_missing():
105
+ location = self.tile_location(tile, dimensions=dimensions)
106
+ if os.path.exists(location):
107
+ return True
108
+ else:
109
+ return False
110
+ else:
111
+ return True
112
+
113
+ def load_tile(self, tile, with_metadata=False, dimensions=None):
114
+ """
115
+ Fills the `Tile.source` of the `tile` if it is cached.
116
+ If it is not cached or if the ``.coord`` is ``None``, nothing happens.
117
+ """
118
+ if not tile.is_missing():
119
+ return True
120
+
121
+ location = self.tile_location(tile, dimensions=dimensions)
122
+
123
+ if os.path.exists(location):
124
+ if with_metadata:
125
+ self.load_tile_metadata(tile, dimensions=dimensions)
126
+ tile.source = ImageSource(location, image_opts=self.image_opts)
127
+ return True
128
+ return False
129
+
130
+ def remove_tile(self, tile, dimensions=None):
131
+ location = self.tile_location(tile, dimensions=dimensions)
132
+ try:
133
+ os.remove(location)
134
+ except OSError as ex:
135
+ if ex.errno != errno.ENOENT:
136
+ raise
137
+
138
+ def store_tile(self, tile, dimensions=None):
139
+ """
140
+ Add the given `tile` to the file cache. Stores the `Tile.source` to
141
+ `FileCache.tile_location`.
142
+ """
143
+ if tile.stored:
144
+ return
145
+
146
+ tile_loc = self.tile_location(tile, create_dir=True, dimensions=dimensions)
147
+
148
+ if self.link_single_color_images:
149
+ color = is_single_color_image(tile.source.as_image())
150
+ if color:
151
+ self._store_single_color_tile(tile, tile_loc, color)
152
+ else:
153
+ self._store(tile, tile_loc)
154
+ else:
155
+ self._store(tile, tile_loc)
156
+
157
+ def _store(self, tile, location):
158
+ if os.path.islink(location):
159
+ os.unlink(location)
160
+
161
+ with tile_buffer(tile) as buf:
162
+ log.debug('writing %r to %s' % (tile.coord, location))
163
+ write_atomic(location, buf.read())
164
+
165
+ def _store_single_color_tile(self, tile, tile_loc, color):
166
+ real_tile_loc = self._single_color_tile_location(color, create_dir=True)
167
+ if not os.path.exists(real_tile_loc):
168
+ self._store(tile, real_tile_loc)
169
+
170
+ log.debug('linking %r from %s to %s',
171
+ tile.coord, real_tile_loc, tile_loc)
172
+
173
+ # remove any file before symlinking.
174
+ # exists() returns False if it links to non-
175
+ # existing file, islink() test to check that
176
+ if os.path.exists(tile_loc) or os.path.islink(tile_loc):
177
+ os.unlink(tile_loc)
178
+
179
+ if self.link_single_color_images == 'hardlink':
180
+ try:
181
+ os.link(real_tile_loc, tile_loc)
182
+ except OSError as e:
183
+ # ignore error if link was created by other process
184
+ if e.errno != errno.EEXIST:
185
+ raise e
186
+ else:
187
+ # Use relative path for the symlink
188
+ real_tile_loc = os.path.relpath(real_tile_loc, os.path.dirname(tile_loc))
189
+
190
+ try:
191
+ os.symlink(real_tile_loc, tile_loc)
192
+ except OSError as e:
193
+ # ignore error if link was created by other process
194
+ if e.errno != errno.EEXIST:
195
+ raise e
196
+
197
+ return
198
+
199
+ def __repr__(self):
200
+ return '%s(%r, %r)' % (self.__class__.__name__, self.cache_dir, self.file_ext)