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,647 @@
1
+ # This file is part of the MapProxy project.
2
+ # Copyright (C) 2011-2013 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 datetime
18
+ import hashlib
19
+ import logging
20
+ import os
21
+ import re
22
+ import sqlite3
23
+ import threading
24
+ import time
25
+ from io import BytesIO
26
+ from itertools import groupby
27
+
28
+ from mapproxy.cache.base import TileCacheBase, tile_buffer, REMOVE_ON_UNLOCK
29
+ from mapproxy.image import ImageSource
30
+ from mapproxy.srs import get_epsg_num
31
+ from mapproxy.util.fs import ensure_directory
32
+ from mapproxy.util.lock import FileLock
33
+
34
+
35
+ log = logging.getLogger(__name__)
36
+
37
+
38
+ class GeopackageCache(TileCacheBase):
39
+ supports_timestamp = False
40
+
41
+ def __init__(
42
+ self, geopackage_file, tile_grid, table_name, with_timestamps=False, timeout=30, wal=False, coverage=None):
43
+ super(GeopackageCache, self).__init__(coverage)
44
+ self.tile_grid = tile_grid
45
+ self.table_name = self._check_table_name(table_name)
46
+ md5 = hashlib.new('md5', geopackage_file.encode('utf-8'), usedforsecurity=False)
47
+ self.lock_cache_id = 'gpkg' + md5.hexdigest()
48
+ self.geopackage_file = geopackage_file
49
+
50
+ if coverage:
51
+ self.bbox = coverage.transform_to(self.tile_grid.srs).bbox
52
+ else:
53
+ self.bbox = self.tile_grid.bbox
54
+ # XXX timestamps not implemented
55
+ self.supports_timestamp = with_timestamps
56
+ self.timeout = timeout
57
+ self.wal = wal
58
+ self.ensure_gpkg()
59
+ self._db_conn_cache = threading.local()
60
+
61
+ @property
62
+ def db(self):
63
+ if not getattr(self._db_conn_cache, 'db', None):
64
+ self.ensure_gpkg()
65
+ self._db_conn_cache.db = sqlite3.connect(self.geopackage_file, timeout=self.timeout)
66
+ return self._db_conn_cache.db
67
+
68
+ def cleanup(self):
69
+ """
70
+ Close all open connection and remove them from cache.
71
+ """
72
+ if getattr(self._db_conn_cache, 'db', None):
73
+ self._db_conn_cache.db.close()
74
+ self._db_conn_cache.db = None
75
+
76
+ @staticmethod
77
+ def _check_table_name(table_name):
78
+ """
79
+ >>> GeopackageCache._check_table_name("test")
80
+ 'test'
81
+ >>> GeopackageCache._check_table_name("test_2")
82
+ 'test_2'
83
+ >>> GeopackageCache._check_table_name("test-2")
84
+ 'test-2'
85
+ >>> GeopackageCache._check_table_name("test3;")
86
+ Traceback (most recent call last):
87
+ ...
88
+ ValueError: The table_name test3; contains unsupported characters.
89
+ >>> GeopackageCache._check_table_name("table name")
90
+ Traceback (most recent call last):
91
+ ...
92
+ ValueError: The table_name table name contains unsupported characters.
93
+
94
+ @param table_name: A desired name for an geopackage table.
95
+ @return: The name of the table if it is good, otherwise an exception.
96
+ """
97
+ # Regex string indicating table names which will be accepted.
98
+ regex_str = '^[a-zA-Z0-9_-]+$'
99
+ if re.match(regex_str, table_name):
100
+ return table_name
101
+ else:
102
+ msg = ("The table name may only contain alphanumeric characters, an underscore, "
103
+ "or a dash: {}".format(regex_str))
104
+ log.info(msg)
105
+ raise ValueError("The table_name {0} contains unsupported characters.".format(table_name))
106
+
107
+ def ensure_gpkg(self):
108
+ if not os.path.isfile(self.geopackage_file):
109
+ with FileLock(self.geopackage_file + '.init.lck',
110
+ remove_on_unlock=REMOVE_ON_UNLOCK):
111
+ ensure_directory(self.geopackage_file)
112
+ self._initialize_gpkg()
113
+ else:
114
+ if not self.check_gpkg():
115
+ ensure_directory(self.geopackage_file)
116
+ self._initialize_gpkg()
117
+
118
+ def check_gpkg(self):
119
+ if not self._verify_table():
120
+ return False
121
+ if not self._verify_gpkg_contents():
122
+ return False
123
+ if not self._verify_tile_size():
124
+ return False
125
+ return True
126
+
127
+ def _verify_table(self):
128
+ with sqlite3.connect(self.geopackage_file) as db:
129
+ cur = db.execute("""SELECT name FROM sqlite_master WHERE type='table' AND name=?""",
130
+ (self.table_name,))
131
+ content = cur.fetchone()
132
+ if not content:
133
+ # Table doesn't exist _initialize_gpkg will create a new one.
134
+ return False
135
+ return True
136
+
137
+ def _verify_gpkg_contents(self):
138
+ with sqlite3.connect(self.geopackage_file) as db:
139
+ cur = db.execute("""SELECT * FROM gpkg_contents WHERE table_name = ?""", (self.table_name,))
140
+
141
+ results = cur.fetchone()
142
+ if not results:
143
+ # Table doesn't exist in gpkg_contents _initialize_gpkg will add it.
144
+ return False
145
+ gpkg_data_type = results[1]
146
+ gpkg_srs_id = results[9]
147
+ cur = db.execute("""SELECT * FROM gpkg_spatial_ref_sys WHERE srs_id = ?""", (gpkg_srs_id,))
148
+
149
+ gpkg_coordsys_id = cur.fetchone()[3]
150
+ if gpkg_data_type.lower() != "tiles":
151
+ log.info("The geopackage table name already exists for a data type other than tiles.")
152
+ raise ValueError("table_name is improperly configured.")
153
+ if gpkg_coordsys_id != get_epsg_num(self.tile_grid.srs.srs_code):
154
+ log.info(
155
+ f"The geopackage {self.geopackage_file} table name {self.table_name} already exists and has an SRS of"
156
+ f" {gpkg_coordsys_id}, which does not match the configured Mapproxy SRS of"
157
+ f" {get_epsg_num(self.tile_grid.srs.srs_code)}.")
158
+ raise ValueError("srs is improperly configured.")
159
+ return True
160
+
161
+ def _verify_tile_size(self):
162
+ with sqlite3.connect(self.geopackage_file) as db:
163
+ cur = db.execute(
164
+ """SELECT * FROM gpkg_tile_matrix WHERE table_name = ?""",
165
+ (self.table_name,))
166
+
167
+ results = cur.fetchall()
168
+ results = results[0]
169
+ tile_size = self.tile_grid.tile_size
170
+
171
+ if not results:
172
+ # There is no tile conflict. Return to allow the creation of new tiles.
173
+ return True
174
+
175
+ gpkg_table_name, gpkg_zoom_level, gpkg_matrix_width, gpkg_matrix_height, gpkg_tile_width, gpkg_tile_height, \
176
+ gpkg_pixel_x_size, gpkg_pixel_y_size = results
177
+ resolution = self.tile_grid.resolution(gpkg_zoom_level)
178
+ if gpkg_tile_width != tile_size[0] or gpkg_tile_height != tile_size[1]:
179
+ log.info(
180
+ f"The geopackage {self.geopackage_file} table name {self.table_name} already exists and has tile sizes"
181
+ f" of ({gpkg_tile_width},{gpkg_tile_height}) which is different than the configure tile sizes of"
182
+ f" ({tile_size[0]},{tile_size[1]}).")
183
+ log.info("The current mapproxy configuration is invalid for this geopackage.")
184
+ raise ValueError("tile_size is improperly configured.")
185
+ if not is_close(gpkg_pixel_x_size, resolution) or not is_close(gpkg_pixel_y_size, resolution):
186
+ log.info(
187
+ f"The geopackage {self.geopackage_file} table name {self.table_name} already exists and level"
188
+ f" {gpkg_zoom_level} a resolution of ({gpkg_pixel_x_size:.13f},{gpkg_pixel_y_size:.13f})"
189
+ f" which is different than the configured resolution of ({resolution:.13f},{resolution:.13f}).")
190
+ log.info("The current mapproxy configuration is invalid for this geopackage.")
191
+ raise ValueError("res is improperly configured.")
192
+ return True
193
+
194
+ def _initialize_gpkg(self):
195
+ log.info('initializing Geopackage file %s', self.geopackage_file)
196
+ db = sqlite3.connect(self.geopackage_file)
197
+
198
+ if self.wal:
199
+ db.execute('PRAGMA journal_mode=wal')
200
+
201
+ proj = get_epsg_num(self.tile_grid.srs.srs_code)
202
+ stmts = [
203
+ """
204
+ CREATE TABLE IF NOT EXISTS gpkg_contents(
205
+ table_name TEXT NOT NULL PRIMARY KEY,
206
+ -- The name of the tiles, or feature table
207
+ data_type TEXT NOT NULL,
208
+ -- Type of data stored in the table: "features" per clause Features
209
+ -- (http://www.geopackage.org/spec/#features), "tiles" per clause Tiles
210
+ -- (http://www.geopackage.org/spec/#tiles), or an implementer-defined value for other data
211
+ -- tables per clause in an Extended GeoPackage
212
+ identifier TEXT UNIQUE,
213
+ -- A human-readable identifier (e.g. short name) for the table_name content
214
+ description TEXT DEFAULT '',
215
+ -- A human-readable description for the table_name content
216
+ last_change DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
217
+ -- Timestamp value in ISO 8601 format as defined by the strftime function %Y-%m-%dT%H:%M:%fZ
218
+ -- format string applied to the current time
219
+ min_x DOUBLE,
220
+ -- Bounding box minimum easting or longitude for all content in table_name
221
+ min_y DOUBLE,
222
+ -- Bounding box minimum northing or latitude for all content in table_name
223
+ max_x DOUBLE,
224
+ -- Bounding box maximum easting or longitude for all content in table_name
225
+ max_y DOUBLE,
226
+ -- Bounding box maximum northing or latitude for all content in table_name
227
+ srs_id INTEGER,
228
+ -- Spatial Reference System ID: gpkg_spatial_ref_sys.srs_id; when data_type is features,
229
+ -- SHALL also match gpkg_geometry_columns.srs_id; When data_type is tiles, SHALL also match
230
+ -- gpkg_tile_matrix_set.srs.id
231
+ CONSTRAINT fk_gc_r_srs_id FOREIGN KEY (srs_id) REFERENCES gpkg_spatial_ref_sys(srs_id))
232
+ """,
233
+ """
234
+ CREATE TABLE IF NOT EXISTS gpkg_spatial_ref_sys(
235
+ srs_name TEXT NOT NULL,
236
+ -- Human readable name of this SRS (Spatial Reference System)
237
+ srs_id INTEGER NOT NULL PRIMARY KEY,
238
+ -- Unique identifier for each Spatial Reference System within a GeoPackage
239
+ organization TEXT NOT NULL,
240
+ -- Case-insensitive name of the defining organization e.g. EPSG or epsg
241
+ organization_coordsys_id INTEGER NOT NULL,
242
+ -- Numeric ID of the Spatial Reference System assigned by the organization
243
+ definition TEXT NOT NULL,
244
+ -- Well-known Text representation of the Spatial Reference System
245
+ description TEXT)
246
+ """,
247
+ """
248
+ CREATE TABLE IF NOT EXISTS gpkg_tile_matrix
249
+ (table_name TEXT NOT NULL, -- Tile Pyramid User Data Table Name
250
+ zoom_level INTEGER NOT NULL, -- 0 <= zoom_level <= max_level for table_name
251
+ matrix_width INTEGER NOT NULL, -- Number of columns (>= 1) in tile matrix at this zoom level
252
+ matrix_height INTEGER NOT NULL, -- Number of rows (>= 1) in tile matrix at this zoom level
253
+ tile_width INTEGER NOT NULL, -- Tile width in pixels (>= 1) for this zoom level
254
+ tile_height INTEGER NOT NULL, -- Tile height in pixels (>= 1) for this zoom level
255
+ pixel_x_size DOUBLE NOT NULL, -- In t_table_name srid units or default meters for srid 0 (>0)
256
+ pixel_y_size DOUBLE NOT NULL, -- In t_table_name srid units or default meters for srid 0 (>0)
257
+ CONSTRAINT pk_ttm PRIMARY KEY (table_name, zoom_level),
258
+ CONSTRAINT fk_tmm_table_name FOREIGN KEY (table_name) REFERENCES gpkg_contents(table_name))
259
+ """,
260
+ """
261
+ CREATE TABLE IF NOT EXISTS gpkg_tile_matrix_set(
262
+ table_name TEXT NOT NULL PRIMARY KEY,
263
+ -- Tile Pyramid User Data Table Name
264
+ srs_id INTEGER NOT NULL,
265
+ -- Spatial Reference System ID: gpkg_spatial_ref_sys.srs_id
266
+ min_x DOUBLE NOT NULL,
267
+ -- Bounding box minimum easting or longitude for all content in table_name
268
+ min_y DOUBLE NOT NULL,
269
+ -- Bounding box minimum northing or latitude for all content in table_name
270
+ max_x DOUBLE NOT NULL,
271
+ -- Bounding box maximum easting or longitude for all content in table_name
272
+ max_y DOUBLE NOT NULL,
273
+ -- Bounding box maximum northing or latitude for all content in table_name
274
+ CONSTRAINT fk_gtms_table_name FOREIGN KEY (table_name) REFERENCES gpkg_contents(table_name),
275
+ CONSTRAINT fk_gtms_srs FOREIGN KEY (srs_id) REFERENCES gpkg_spatial_ref_sys (srs_id))
276
+ """,
277
+ """
278
+ CREATE TABLE IF NOT EXISTS [{0}]
279
+ (id INTEGER PRIMARY KEY AUTOINCREMENT, -- Autoincrement primary key
280
+ zoom_level INTEGER NOT NULL, -- min(zoom_level) <= zoom_level <= max(zoom_level)
281
+ -- for t_table_name
282
+ tile_column INTEGER NOT NULL, -- 0 to tile_matrix matrix_width - 1
283
+ tile_row INTEGER NOT NULL, -- 0 to tile_matrix matrix_height - 1
284
+ tile_data BLOB NOT NULL, -- Of an image MIME type specified in clauses Tile
285
+ -- Encoding PNG, Tile Encoding JPEG, Tile Encoding WEBP
286
+ UNIQUE (zoom_level, tile_column, tile_row))
287
+ """.format(self.table_name)]
288
+
289
+ for stmt in stmts:
290
+ db.execute(stmt)
291
+
292
+ db.execute("PRAGMA foreign_keys = 1;")
293
+
294
+ # List of WKT execute statements and data.("""
295
+ wkt_statement = """
296
+ INSERT OR REPLACE INTO gpkg_spatial_ref_sys (
297
+ srs_id,
298
+ organization,
299
+ organization_coordsys_id,
300
+ srs_name,
301
+ definition)
302
+ VALUES (?, ?, ?, ?, ?)
303
+ """
304
+ wkt_entries = [(3857, 'epsg', 3857, 'WGS 84 / Pseudo-Mercator',
305
+ """
306
+ PROJCS["WGS 84 / Pseudo-Mercator",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,\
307
+ AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],\
308
+ UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],\
309
+ PROJECTION["Mercator_1SP"],PARAMETER["central_meridian",0],PARAMETER["scale_factor",1],PARAMETER["false_easting",0],\
310
+ PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["X",EAST],AXIS["Y",NORTH],\
311
+ AUTHORITY["EPSG","3857"]]\
312
+ """
313
+ ),
314
+ (4326, 'epsg', 4326, 'WGS 84',
315
+ """
316
+ GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],\
317
+ AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,\
318
+ AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]\
319
+ """
320
+ ),
321
+ (-1, 'NONE', -1, ' ', 'undefined'),
322
+ (0, 'NONE', 0, ' ', 'undefined')
323
+ ]
324
+
325
+ if get_epsg_num(self.tile_grid.srs.srs_code) not in [4326, 3857]:
326
+ wkt_entries.append((proj, 'epsg', proj, 'Not provided', "Added via Mapproxy."))
327
+ db.commit()
328
+
329
+ # Add geopackage version to the header (1.0)
330
+ db.execute("PRAGMA application_id = 1196437808;")
331
+ db.commit()
332
+
333
+ for wkt_entry in wkt_entries:
334
+ try:
335
+ db.execute(wkt_statement, (wkt_entry[0], wkt_entry[1], wkt_entry[2], wkt_entry[3], wkt_entry[4]))
336
+ except sqlite3.IntegrityError:
337
+ log.info("srs_id already exists.")
338
+ db.commit()
339
+
340
+ last_change = datetime.datetime.utcfromtimestamp(
341
+ int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))
342
+ )
343
+
344
+ # Ensure that tile table exists here, don't overwrite a valid entry.
345
+ try:
346
+ db.execute("""
347
+ INSERT INTO gpkg_contents (
348
+ table_name,
349
+ data_type,
350
+ identifier,
351
+ description,
352
+ last_change,
353
+ min_x,
354
+ max_x,
355
+ min_y,
356
+ max_y,
357
+ srs_id)
358
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
359
+ """, (self.table_name,
360
+ "tiles",
361
+ self.table_name,
362
+ "Created with Mapproxy.",
363
+ last_change,
364
+ self.bbox[0],
365
+ self.bbox[2],
366
+ self.bbox[1],
367
+ self.bbox[3],
368
+ proj))
369
+ except sqlite3.IntegrityError:
370
+ pass
371
+ db.commit()
372
+
373
+ # Ensure that tile set exists here, don't overwrite a valid entry.
374
+ try:
375
+ db.execute("""
376
+ INSERT INTO gpkg_tile_matrix_set (table_name, srs_id, min_x, max_x, min_y, max_y)
377
+ VALUES (?, ?, ?, ?, ?, ?);
378
+ """, (
379
+ self.table_name, proj, self.bbox[0], self.bbox[2], self.bbox[1], self.bbox[3]))
380
+ except sqlite3.IntegrityError:
381
+ pass
382
+ db.commit()
383
+
384
+ tile_size = self.tile_grid.tile_size
385
+ for grid, resolution, level in zip(self.tile_grid.grid_sizes,
386
+ self.tile_grid.resolutions, range(20)):
387
+ db.execute(
388
+ """INSERT OR REPLACE INTO gpkg_tile_matrix (table_name, zoom_level, matrix_width, matrix_height,
389
+ tile_width, tile_height, pixel_x_size, pixel_y_size) VALUES(?, ?, ?, ?, ?, ?, ?, ?)""",
390
+ (self.table_name, level, grid[0], grid[1], tile_size[0], tile_size[1], resolution, resolution))
391
+ db.commit()
392
+ db.close()
393
+
394
+ def is_cached(self, tile, dimensions=None):
395
+ if tile.coord is None:
396
+ return True
397
+ if tile.source:
398
+ return True
399
+
400
+ return self.load_tile(tile, dimensions=dimensions)
401
+
402
+ def store_tile(self, tile, dimensions=None):
403
+ if tile.stored:
404
+ return True
405
+ return self._store_bulk([tile])
406
+
407
+ def store_tiles(self, tiles, dimensions=None):
408
+ tiles = [t for t in tiles if not t.stored]
409
+ return self._store_bulk(tiles)
410
+
411
+ def _store_bulk(self, tiles):
412
+ records = []
413
+ # tile_buffer (as_buffer) will encode the tile to the target format
414
+ # we collect all tiles before, to avoid having the db transaction
415
+ # open during this slow encoding
416
+ for tile in tiles:
417
+ with tile_buffer(tile) as buf:
418
+ content = buf.read()
419
+ x, y, level = tile.coord
420
+ records.append((level, x, y, content))
421
+
422
+ cursor = self.db.cursor()
423
+ try:
424
+ stmt = (f"INSERT OR REPLACE INTO [{self.table_name}] (zoom_level, tile_column, tile_row, tile_data)"
425
+ f" VALUES (?,?,?,?)")
426
+ cursor.executemany(stmt, records)
427
+ self.db.commit()
428
+ except sqlite3.OperationalError as ex:
429
+ log.warning('unable to store tile: %s', ex)
430
+ return False
431
+ return True
432
+
433
+ def load_tile(self, tile, with_metadata=False, dimensions=None):
434
+ if tile.source or tile.coord is None:
435
+ return True
436
+
437
+ cur = self.db.cursor()
438
+ cur.execute("""SELECT tile_data FROM [{0}]
439
+ WHERE tile_column = ? AND
440
+ tile_row = ? AND
441
+ zoom_level = ?""".format(self.table_name), tile.coord)
442
+
443
+ content = cur.fetchone()
444
+ if content:
445
+ tile.source = ImageSource(BytesIO(content[0]))
446
+ return True
447
+ else:
448
+ return False
449
+
450
+ def load_tiles(self, tiles, with_metadata=False, dimensions=None):
451
+ # associate the right tiles with the cursor
452
+ tile_dict = {}
453
+ coords = []
454
+ for tile in tiles:
455
+ if tile.source or tile.coord is None:
456
+ continue
457
+ x, y, level = tile.coord
458
+ coords.append(x)
459
+ coords.append(y)
460
+ coords.append(level)
461
+ tile_dict[(x, y)] = tile
462
+
463
+ if not tile_dict:
464
+ # all tiles loaded or coords are None
465
+ return True
466
+
467
+ stmt_base = "SELECT tile_column, tile_row, tile_data FROM [{0}] WHERE ".format(self.table_name)
468
+
469
+ loaded_tiles = 0
470
+
471
+ # SQLite is limited to 1000 args -> split into multiple requests if more arguments are needed
472
+ while coords:
473
+ cur_coords = coords[:999]
474
+
475
+ stmt = stmt_base + ' OR '.join(
476
+ ['(tile_column = ? AND tile_row = ? AND zoom_level = ?)'] * (len(cur_coords) // 3))
477
+
478
+ cursor = self.db.cursor()
479
+ cursor.execute(stmt, cur_coords)
480
+
481
+ for row in cursor:
482
+ loaded_tiles += 1
483
+ tile = tile_dict[(row[0], row[1])]
484
+ data = row[2]
485
+ tile.size = len(data)
486
+ tile.source = ImageSource(BytesIO(data))
487
+ cursor.close()
488
+
489
+ coords = coords[999:]
490
+
491
+ return loaded_tiles == len(tile_dict)
492
+
493
+ def remove_tile(self, tile, dimensions=None):
494
+ cursor = self.db.cursor()
495
+ cursor.execute(
496
+ "DELETE FROM [{0}] WHERE (tile_column = ? AND tile_row = ? AND zoom_level = ?)".format(self.table_name),
497
+ tile.coord)
498
+ self.db.commit()
499
+ if cursor.rowcount:
500
+ return True
501
+ return False
502
+
503
+ def remove_level_tiles_before(self, level, timestamp):
504
+ if timestamp == 0:
505
+ cursor = self.db.cursor()
506
+ cursor.execute(
507
+ "DELETE FROM [{0}] WHERE (zoom_level = ?)".format(self.table_name), (level,))
508
+ self.db.commit()
509
+ log.info("Cursor rowcount = {0}".format(cursor.rowcount))
510
+ if cursor.rowcount:
511
+ return True
512
+ return False
513
+
514
+ def load_tile_metadata(self, tile, dimensions=None):
515
+ if not self.supports_timestamp:
516
+ # GPKG specification does not include tile timestamps.
517
+ # This sets the timestamp of the tile to epoch (1970s)
518
+ tile.timestamp = -1
519
+ else:
520
+ self.load_tile(tile, dimensions=dimensions)
521
+
522
+
523
+ class GeopackageLevelCache(TileCacheBase):
524
+
525
+ def __init__(self, geopackage_dir, tile_grid, table_name, timeout=30, wal=False, coverage=None):
526
+ super(GeopackageLevelCache, self).__init__(coverage)
527
+ md5 = hashlib.new('md5', geopackage_dir.encode('utf-8'), usedforsecurity=False)
528
+ self.lock_cache_id = 'gpkg-' + md5.hexdigest()
529
+ self.cache_dir = geopackage_dir
530
+ self.tile_grid = tile_grid
531
+ self.table_name = table_name
532
+ self.timeout = timeout
533
+ self.wal = wal
534
+ self._geopackage = {}
535
+ self._geopackage_lock = threading.Lock()
536
+
537
+ def _get_level(self, level):
538
+ if level in self._geopackage:
539
+ return self._geopackage[level]
540
+
541
+ with self._geopackage_lock:
542
+ if level not in self._geopackage:
543
+ geopackage_filename = os.path.join(self.cache_dir, '%s.gpkg' % level)
544
+ self._geopackage[level] = GeopackageCache(
545
+ geopackage_filename,
546
+ self.tile_grid,
547
+ self.table_name,
548
+ with_timestamps=False,
549
+ timeout=self.timeout,
550
+ wal=self.wal,
551
+ coverage=self.coverage
552
+ )
553
+
554
+ return self._geopackage[level]
555
+
556
+ def cleanup(self):
557
+ """
558
+ Close all open connection and remove them from cache.
559
+ """
560
+ with self._geopackage_lock:
561
+ for gp in self._geopackage.values():
562
+ gp.cleanup()
563
+
564
+ def is_cached(self, tile, dimensions=None):
565
+ if tile.coord is None:
566
+ return True
567
+ if tile.source:
568
+ return True
569
+
570
+ return self._get_level(tile.coord[2]).is_cached(tile, dimensions=dimensions)
571
+
572
+ def store_tile(self, tile, dimensions=None):
573
+ if tile.stored:
574
+ return True
575
+
576
+ return self._get_level(tile.coord[2]).store_tile(tile, dimensions=dimensions)
577
+
578
+ def store_tiles(self, tiles, dimensions=None):
579
+ failed = False
580
+ for level, tiles in groupby(tiles, key=lambda t: t.coord[2]):
581
+ tiles = [t for t in tiles if not t.stored]
582
+ res = self._get_level(level).store_tiles(tiles, dimensions=dimensions)
583
+ if not res:
584
+ failed = True
585
+ return failed
586
+
587
+ def load_tile(self, tile, with_metadata=False, dimensions=None):
588
+ if tile.source or tile.coord is None:
589
+ return True
590
+
591
+ return self._get_level(tile.coord[2]).load_tile(tile, with_metadata=with_metadata, dimensions=dimensions)
592
+
593
+ def load_tiles(self, tiles, with_metadata=False, dimensions=None):
594
+ level = None
595
+ for tile in tiles:
596
+ if tile.source or tile.coord is None:
597
+ continue
598
+ level = tile.coord[2]
599
+ break
600
+
601
+ if not level:
602
+ return True
603
+
604
+ return self._get_level(level).load_tiles(tiles, with_metadata=with_metadata, dimensions=dimensions)
605
+
606
+ def remove_tile(self, tile, dimensions=None):
607
+ if tile.coord is None:
608
+ return True
609
+
610
+ return self._get_level(tile.coord[2]).remove_tile(tile, dimensions=dimensions)
611
+
612
+ def remove_level_tiles_before(self, level, timestamp):
613
+ level_cache = self._get_level(level)
614
+ if timestamp == 0:
615
+ level_cache.cleanup()
616
+ os.unlink(level_cache.geopackage_file)
617
+ return True
618
+ else:
619
+ return level_cache.remove_level_tiles_before(level, timestamp)
620
+
621
+ def load_tile_metadata(self, tile, dimensions=None):
622
+ return self._get_level(tile.coord[2]).load_tile_metadata(tile, dimensions=dimensions)
623
+
624
+
625
+ def is_close(a, b, rel_tol=1e-09, abs_tol=0.0):
626
+ """
627
+ See PEP 485, added here for legacy versions.
628
+
629
+ >>> is_close(0.0, 0.0)
630
+ True
631
+ >>> is_close(1, 1.0)
632
+ True
633
+ >>> is_close(0.01, 0.001)
634
+ False
635
+ >>> is_close(0.0001001, 0.0001, rel_tol=1e-02)
636
+ True
637
+ >>> is_close(0.0001001, 0.0001)
638
+ False
639
+
640
+ @param a: An int or float.
641
+ @param b: An int or float.
642
+ @param rel_tol: Relative tolerance - maximumed allow difference between two numbers.
643
+ @param abs_tol: Absolute tolerance - minimum absolute tolerance.
644
+ @return: True if the values a and b are close.
645
+
646
+ """
647
+ return abs(a - b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)