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,540 @@
1
+ # -:- encoding: utf-8 -:-
2
+ # This file is part of the MapProxy project.
3
+ # Copyright (C) 2010 Omniscale <http://omniscale.de>
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ from __future__ import division
18
+ from mapproxy.template import template_loader, bunch
19
+
20
+ import math
21
+ import time
22
+
23
+ from mapproxy.response import Response
24
+ from mapproxy.exception import RequestError
25
+ from mapproxy.service.base import Server
26
+ from mapproxy.request.tile import tile_request
27
+ from mapproxy.request.base import split_mime_type
28
+ from mapproxy.source import SourceError
29
+ from mapproxy.srs import SRS
30
+ from mapproxy.grid import default_bboxs
31
+ from mapproxy.image import BlankImageSource
32
+ from mapproxy.image.opts import ImageOptions
33
+ from mapproxy.image.mask import mask_image_source_from_coverage
34
+ from mapproxy.util.ext.odict import odict
35
+ from mapproxy.util.coverage import load_limited_to
36
+
37
+ import logging
38
+ log = logging.getLogger(__name__)
39
+
40
+
41
+ get_template = template_loader(__package__, 'templates')
42
+
43
+
44
+ class TileServer(Server):
45
+ """
46
+ A Tile Server. Supports strict TMS and non-TMS requests. The difference is the
47
+ support for profiles. The our internal tile cache starts with one tile at the
48
+ first level (like KML, etc.), but the global-geodetic and global-mercator
49
+ start with two and four tiles. The ``tile_request`` should set ``use_profiles``
50
+ accordingly (eg. False if first level is one tile)
51
+ """
52
+ names = ('tiles', 'tms')
53
+ request_parser = staticmethod(tile_request)
54
+ request_methods = ('map', 'tms_capabilities, tms_root_resource')
55
+ template_file = 'tms_capabilities.xml'
56
+ layer_template_file = 'tms_tilemap_capabilities.xml'
57
+ root_resource_template_file = 'tms_root_resource.xml'
58
+
59
+ def __init__(self, layers, md, max_tile_age=None, use_dimension_layers=False, origin=None):
60
+ Server.__init__(self)
61
+ self.layers = layers
62
+ self.md = md
63
+ self.max_tile_age = max_tile_age
64
+ self.use_dimension_layers = use_dimension_layers
65
+ self.origin = origin
66
+
67
+ def map(self, tile_request):
68
+ """
69
+ :return: the requested tile
70
+ :rtype: Response
71
+ """
72
+ if self.origin and not tile_request.origin:
73
+ tile_request.origin = self.origin
74
+ layer, limit_to = self.layer(tile_request)
75
+
76
+ def decorate_img(image):
77
+ query_extent = (layer.grid.srs.srs_code,
78
+ layer.tile_bbox(tile_request, use_profiles=tile_request.use_profiles))
79
+ return self.decorate_img(image, 'tms', [layer.name], tile_request.http.environ, query_extent)
80
+
81
+ tile = layer.render(tile_request, use_profiles=tile_request.use_profiles,
82
+ coverage=limit_to, decorate_img=decorate_img)
83
+
84
+ tile_format = getattr(tile, 'format', tile_request.format)
85
+ resp = Response(tile.as_buffer(), content_type='image/' + tile_format)
86
+ if tile.cacheable:
87
+ resp.cache_headers(tile.timestamp, etag_data=(tile.timestamp, tile.size),
88
+ max_age=self.max_tile_age)
89
+ else:
90
+ resp.cache_headers(no_cache=True)
91
+ resp.make_conditional(tile_request.http)
92
+ return resp
93
+
94
+ def _internal_layer(self, tile_request):
95
+ if '_layer_spec' in tile_request.dimensions:
96
+ name = tile_request.layer + '_' + tile_request.dimensions['_layer_spec']
97
+ else:
98
+ name = tile_request.layer
99
+ if name in self.layers:
100
+ return self.layers[name]
101
+ if name + '_EPSG900913' in self.layers:
102
+ return self.layers[name + '_EPSG900913']
103
+ if name + '_EPSG4326' in self.layers:
104
+ return self.layers[name + '_EPSG4326']
105
+ return None
106
+
107
+ def _internal_dimension_layer(self, tile_request):
108
+ key = (tile_request.layer, tile_request.dimensions.get('_layer_spec'))
109
+ return self.layers.get(key)
110
+
111
+ def layer(self, tile_request):
112
+ if self.use_dimension_layers:
113
+ internal_layer = self._internal_dimension_layer(tile_request)
114
+ else:
115
+ internal_layer = self._internal_layer(tile_request)
116
+ if internal_layer is None:
117
+ raise RequestError('unknown layer: ' + tile_request.layer, request=tile_request)
118
+
119
+ limit_to = self.authorize_tile_layer(internal_layer, tile_request)
120
+ return internal_layer, limit_to
121
+
122
+ def authorize_tile_layer(self, tile_layer, request):
123
+ if 'mapproxy.authorize' in request.http.environ:
124
+ if request.tile:
125
+ query_extent = (tile_layer.grid.srs.srs_code,
126
+ tile_layer.tile_bbox(request, use_profiles=request.use_profiles))
127
+ else:
128
+ query_extent = None # for layer capabilities
129
+ result = request.http.environ['mapproxy.authorize']('tms', [tile_layer.name],
130
+ query_extent=query_extent, environ=request.http.environ)
131
+ if result['authorized'] == 'unauthenticated':
132
+ raise RequestError('unauthorized', status=401)
133
+ if result['authorized'] == 'full':
134
+ return
135
+ if result['authorized'] == 'partial':
136
+ if result['layers'].get(tile_layer.name, {}).get('tile', False) is True:
137
+ limited_to = result['layers'][tile_layer.name].get('limited_to')
138
+ if not limited_to:
139
+ limited_to = result.get('limited_to')
140
+ if limited_to:
141
+ return load_limited_to(limited_to)
142
+ else:
143
+ return None
144
+ raise RequestError('forbidden', status=403)
145
+
146
+ def authorized_tile_layers(self, env):
147
+ if 'mapproxy.authorize' in env:
148
+ result = env['mapproxy.authorize']('tms', [x for x in self.layers],
149
+ query_extent=None, environ=env)
150
+ if result['authorized'] == 'unauthenticated':
151
+ raise RequestError('unauthorized', status=401)
152
+ if result['authorized'] == 'full':
153
+ return self.layers
154
+ if result['authorized'] == 'none':
155
+ raise RequestError('forbidden', status=403)
156
+ allowed_layers = odict()
157
+ for layer in self.layers.values():
158
+ if result['layers'].get(layer.name, {}).get('tile', False) is True:
159
+ allowed_layers[layer.name] = layer
160
+ return allowed_layers
161
+ else:
162
+ return self.layers
163
+
164
+ def tms_capabilities(self, tms_request):
165
+ """
166
+ :return: the rendered tms capabilities
167
+ :rtype: Response
168
+ """
169
+ service = self._service_md(tms_request)
170
+ if hasattr(tms_request, 'layer'):
171
+ layer, limit_to = self.layer(tms_request)
172
+ result = self._render_layer_template(layer, service)
173
+ else:
174
+ layers = self.authorized_tile_layers(tms_request.http.environ)
175
+ result = self._render_template(layers, service)
176
+
177
+ return Response(result, mimetype='text/xml')
178
+
179
+ def tms_root_resource(self, tms_request):
180
+ """
181
+ :return: root resource with all available versions of the service
182
+ :rtype: Response
183
+ """
184
+ service = self._service_md(tms_request)
185
+ result = self._render_root_resource_template(service)
186
+ return Response(result, mimetype='text/xml')
187
+
188
+ def _service_md(self, map_request):
189
+ md = dict(self.md)
190
+ md['url'] = map_request.http.base_url
191
+ return md
192
+
193
+ def _render_template(self, layers, service):
194
+ template = get_template(self.template_file)
195
+ return template.substitute(service=bunch(default='', **service), layers=layers)
196
+
197
+ def _render_layer_template(self, layer, service):
198
+ template = get_template(self.layer_template_file)
199
+ return template.substitute(service=bunch(default='', **service), layer=layer)
200
+
201
+ def _render_root_resource_template(self, service):
202
+ template = get_template(self.root_resource_template_file)
203
+ return template.substitute(service=bunch(default='', **service))
204
+
205
+
206
+ class TileLayer(object):
207
+ def __init__(self, name, title, md, tile_manager, info_sources=[], dimensions=None):
208
+ """
209
+ :param md: the layer metadata
210
+ :param tile_manager: the layer tile manager
211
+ """
212
+ self.name = name
213
+ self.title = title
214
+ self.md = md
215
+ self.tile_manager = tile_manager
216
+ self.info_sources = info_sources
217
+ self.dimensions = dimensions
218
+ self.grid = TileServiceGrid(tile_manager.grid)
219
+ self.extent = self.md.get('extent').transform(tile_manager.grid.srs)
220
+ self._empty_tile = None
221
+ self._mixed_format = True if self.md.get('format', False) == 'mixed' else False
222
+ self.empty_response_as_png = True
223
+
224
+ @property
225
+ def bbox(self):
226
+ return self.extent.bbox
227
+
228
+ @property
229
+ def srs(self):
230
+ return self.grid.srs
231
+
232
+ @property
233
+ def format(self):
234
+ _mime_class, format, _options = split_mime_type(self.format_mime_type)
235
+ return format
236
+
237
+ @property
238
+ def queryable(self):
239
+ return bool(self.info_sources)
240
+
241
+ @property
242
+ def format_mime_type(self):
243
+ # force png format for capabilities & requests if mixed format
244
+ if self._mixed_format:
245
+ return 'image/png'
246
+ return self.md.get('format', 'image/png')
247
+
248
+ def _internal_tile_coord(self, tile_request, use_profiles=False):
249
+ tile_coord = self.grid.internal_tile_coord(tile_request.tile, use_profiles)
250
+ if tile_coord is None:
251
+ raise RequestError('The requested tile is outside the bounding box'
252
+ ' of the tile map.', request=tile_request,
253
+ code='TileOutOfRange')
254
+ if tile_request.origin == 'nw' and self.grid.origin not in ('ul', 'nw'):
255
+ tile_coord = self.grid.flip_tile_coord(tile_coord)
256
+ elif tile_request.origin == 'sw' and self.grid.origin not in ('ll', 'sw', None):
257
+ tile_coord = self.grid.flip_tile_coord(tile_coord)
258
+
259
+ return tile_coord
260
+
261
+ def empty_response(self):
262
+ if self.empty_response_as_png:
263
+ format = 'png'
264
+ else:
265
+ format = self.format
266
+ if not self._empty_tile:
267
+ img = BlankImageSource(size=self.grid.tile_size,
268
+ image_opts=ImageOptions(format=format, transparent=True))
269
+ self._empty_tile = img.as_buffer().read()
270
+ return ImageResponse(self._empty_tile, format=format, timestamp=time.time())
271
+
272
+ def tile_bbox(self, tile_request, use_profiles=False, limit=False):
273
+ tile_coord = self._internal_tile_coord(tile_request, use_profiles=use_profiles)
274
+ return self.grid.tile_bbox(tile_coord, limit=limit)
275
+
276
+ def checked_dimensions(self, tile_request):
277
+ dimensions = {}
278
+
279
+ for dimension, values in self.dimensions.items():
280
+ value = tile_request.dimensions.get(dimension)
281
+ if value in values:
282
+ dimensions[dimension] = value
283
+ elif not value or value == 'default':
284
+ dimensions[dimension] = values.default
285
+ else:
286
+ raise RequestError('invalid dimension value (%s=%s).'
287
+ % (dimension, value), request=tile_request,
288
+ code='InvalidParameterValue')
289
+ return dimensions
290
+
291
+ def render(self, tile_request, use_profiles=False, coverage=None, decorate_img=None):
292
+ if tile_request.format != self.format:
293
+ raise RequestError('invalid format (%s). this tile set only supports (%s)'
294
+ % (tile_request.format, self.format), request=tile_request,
295
+ code='InvalidParameterValue')
296
+
297
+ tile_coord = self._internal_tile_coord(tile_request, use_profiles=use_profiles)
298
+
299
+ coverage_intersects = False
300
+ if coverage:
301
+ tile_bbox = self.grid.tile_bbox(tile_coord)
302
+ if coverage.contains(tile_bbox, self.grid.srs):
303
+ pass
304
+ elif coverage.intersects(tile_bbox, self.grid.srs):
305
+ coverage_intersects = True
306
+ else:
307
+ return self.empty_response()
308
+
309
+ dimensions = self.checked_dimensions(tile_request)
310
+
311
+ try:
312
+ with self.tile_manager.session():
313
+ tile = self.tile_manager.load_tile_coord(tile_coord,
314
+ dimensions=dimensions, with_metadata=True)
315
+ if tile.source is None:
316
+ return self.empty_response()
317
+
318
+ # Provide the wrapping WSGI app or filter the opportunity to process the
319
+ # image before it's wrapped up in a response
320
+ if decorate_img:
321
+ tile.source = decorate_img(tile.source)
322
+
323
+ if coverage_intersects:
324
+ if self.empty_response_as_png:
325
+ format = 'png'
326
+ image_opts = ImageOptions(transparent=True, format='png')
327
+ else:
328
+ format = self.format
329
+ image_opts = tile.source.image_opts
330
+
331
+ tile.source = mask_image_source_from_coverage(
332
+ tile.source, tile_bbox, self.grid.srs, coverage, image_opts)
333
+
334
+ return TileResponse(tile, format=format, image_opts=image_opts)
335
+
336
+ format = None if self._mixed_format else tile_request.format
337
+ return TileResponse(tile, format=format, image_opts=self.tile_manager.image_opts)
338
+ except SourceError as e:
339
+ raise RequestError(e.args[0], request=tile_request, internal=True)
340
+
341
+ def get_info(self, info_request, coverage=None, decorate_img=None):
342
+ if info_request.format != self.format:
343
+ raise RequestError('invalid format (%s). this tile set only supports (%s)'
344
+ % (info_request.format, self.format), request=info_request,
345
+ code='InvalidParameterValue')
346
+
347
+ tile_coord = self._internal_tile_coord(info_request)
348
+
349
+ coverage_intersects = False
350
+ if coverage:
351
+ tile_bbox = self.grid.tile_bbox(tile_coord)
352
+ if coverage.contains(tile_bbox, self.grid.srs):
353
+ pass
354
+ elif coverage.intersects(tile_bbox, self.grid.srs):
355
+ coverage_intersects = True
356
+ else:
357
+ return self.empty_response()
358
+
359
+ dimensions = self.checked_dimensions(info_request)
360
+
361
+ try:
362
+ with self.tile_manager.session():
363
+ tile = self.tile_manager.load_tile_coord(tile_coord,
364
+ dimensions=dimensions, with_metadata=True)
365
+ if tile.source is None:
366
+ return self.empty_response()
367
+
368
+ # Provide the wrapping WSGI app or filter the opportunity to process the
369
+ # image before it's wrapped up in a response
370
+ if decorate_img:
371
+ tile.source = decorate_img(tile.source)
372
+
373
+ if coverage_intersects:
374
+ if self.empty_response_as_png:
375
+ format = 'png'
376
+ image_opts = ImageOptions(transparent=True, format='png')
377
+ else:
378
+ format = self.format
379
+ image_opts = tile.source.image_opts
380
+
381
+ tile.source = mask_image_source_from_coverage(
382
+ tile.source, tile_bbox, self.grid.srs, coverage, image_opts)
383
+
384
+ return TileResponse(tile, format=format, image_opts=image_opts)
385
+
386
+ format = None if self._mixed_format else info_request.format
387
+ return TileResponse(tile, format=format, image_opts=self.tile_manager.image_opts)
388
+ except SourceError as e:
389
+ raise RequestError(e.args[0], request=info_request, internal=True)
390
+
391
+
392
+ class ImageResponse(object):
393
+ """
394
+ Response from an image.
395
+ """
396
+
397
+ def __init__(self, img, format, timestamp):
398
+ self.img = img
399
+ self.timestamp = timestamp
400
+ self.format = format
401
+ self.size = 0
402
+ self.cacheable = True
403
+
404
+ def as_buffer(self):
405
+ return self.img
406
+
407
+
408
+ class TileResponse(object):
409
+ """
410
+ Response from a Tile.
411
+ """
412
+
413
+ def __init__(self, tile, format=None, timestamp=None, image_opts=None):
414
+ self.tile = tile
415
+ self.timestamp = tile.timestamp
416
+ self.size = tile.size
417
+ self.cacheable = tile.cacheable
418
+ self._buf = self.tile.source_buffer(format=format, image_opts=image_opts)
419
+ self.format = format or self._format_from_magic_bytes()
420
+
421
+ def as_buffer(self):
422
+ return self._buf
423
+
424
+ def _format_from_magic_bytes(self):
425
+ # read the 2 magic bytes from the buffer
426
+ magic_bytes = self._buf.read(2)
427
+ self._buf.seek(0)
428
+ if magic_bytes == b'\xFF\xD8':
429
+ return 'jpeg'
430
+ return 'png'
431
+
432
+
433
+ class TileServiceGrid(object):
434
+ """
435
+ Wraps a `TileGrid` and adds some ``TileService`` specific methods.
436
+ """
437
+
438
+ def __init__(self, grid):
439
+ self.grid = grid
440
+ self.profile = None
441
+
442
+ if self.grid.srs == SRS(900913) and self.grid.bbox == default_bboxs[SRS((900913))]:
443
+ self.profile = 'global-mercator'
444
+ self.srs_name = 'OSGEO:41001' # as required by TMS 1.0.0
445
+ self._skip_first_level = True
446
+
447
+ elif self.grid.srs == SRS(4326) and self.grid.bbox == default_bboxs[SRS((4326))]:
448
+ self.profile = 'global-geodetic'
449
+ self.srs_name = 'EPSG:4326'
450
+ self._skip_first_level = True
451
+ else:
452
+ self.profile = 'local'
453
+ self.srs_name = self.grid.srs.srs_code
454
+ self._skip_first_level = False
455
+
456
+ self._skip_odd_level = False
457
+
458
+ res_factor = self.grid.resolutions[0]/self.grid.resolutions[1]
459
+ if res_factor == math.sqrt(2):
460
+ self._skip_odd_level = True
461
+
462
+ def internal_level(self, level):
463
+ """
464
+ :return: the internal level
465
+ """
466
+ if self._skip_first_level:
467
+ level += 1
468
+ if self._skip_odd_level:
469
+ level += 1
470
+ if self._skip_odd_level:
471
+ level *= 2
472
+ return level
473
+
474
+ @property
475
+ def bbox(self):
476
+ """
477
+ :return: the bbox of all tiles of the first level
478
+ """
479
+ first_level = self.internal_level(0)
480
+ grid_size = self.grid.grid_sizes[first_level]
481
+ return self.grid._get_bbox([(0, 0, first_level),
482
+ (grid_size[0]-1, grid_size[1]-1, first_level)])
483
+
484
+ def __getattr__(self, key):
485
+ return getattr(self.grid, key)
486
+
487
+ @property
488
+ def tile_sets(self):
489
+ """
490
+ Get all public tile sets for this layer.
491
+ :return: the order and resolution of each tile set
492
+ """
493
+ tile_sets = []
494
+ num_levels = self.grid.levels
495
+ start = 0
496
+ step = 1
497
+ if self._skip_first_level:
498
+ if self._skip_odd_level:
499
+ start = 2
500
+ else:
501
+ start = 1
502
+ if self._skip_odd_level:
503
+ step = 2
504
+ for order, level in enumerate(range(start, num_levels, step)):
505
+ tile_sets.append((order, self.grid.resolutions[level]))
506
+ return tile_sets
507
+
508
+ def internal_tile_coord(self, tile_coord, use_profiles):
509
+ """
510
+ Converts public tile coords to internal tile coords.
511
+
512
+ :param tile_coord: the public tile coord
513
+ :param use_profiles: True if the tile service supports global
514
+ profiles (see `mapproxy.core.server.TileServer`)
515
+ """
516
+ x, y, z = tile_coord
517
+ if int(z) < 0:
518
+ return None
519
+ if use_profiles and self._skip_first_level:
520
+ z += 1
521
+ if self._skip_odd_level:
522
+ z *= 2
523
+ return self.grid.limit_tile((x, y, z))
524
+
525
+ def external_tile_coord(self, tile_coord, use_profiles):
526
+ """
527
+ Converts internal tile coords to external tile coords.
528
+
529
+ :param tile_coord: the internal tile coord
530
+ :param use_profiles: True if the tile service supports global
531
+ profiles (see `mapproxy.core.server.TileServer`)
532
+ """
533
+ x, y, z = tile_coord
534
+ if z < 0:
535
+ return None
536
+ if use_profiles and self._skip_first_level:
537
+ z -= 1
538
+ if self._skip_odd_level:
539
+ z //= 2
540
+ return (x, y, z)