MapProxy 1.16.1__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 (458) hide show
  1. mapproxy/__init__.py +0 -0
  2. mapproxy/cache/__init__.py +36 -0
  3. mapproxy/cache/azureblob.py +145 -0
  4. mapproxy/cache/base.py +111 -0
  5. mapproxy/cache/compact.py +664 -0
  6. mapproxy/cache/couchdb.py +295 -0
  7. mapproxy/cache/dummy.py +34 -0
  8. mapproxy/cache/file.py +185 -0
  9. mapproxy/cache/geopackage.py +609 -0
  10. mapproxy/cache/legend.py +83 -0
  11. mapproxy/cache/mbtiles.py +392 -0
  12. mapproxy/cache/meta.py +78 -0
  13. mapproxy/cache/path.py +250 -0
  14. mapproxy/cache/redis.py +88 -0
  15. mapproxy/cache/renderd.py +95 -0
  16. mapproxy/cache/riak.py +202 -0
  17. mapproxy/cache/s3.py +177 -0
  18. mapproxy/cache/tile.py +699 -0
  19. mapproxy/client/__init__.py +0 -0
  20. mapproxy/client/arcgis.py +79 -0
  21. mapproxy/client/cgi.py +139 -0
  22. mapproxy/client/http.py +315 -0
  23. mapproxy/client/log.py +33 -0
  24. mapproxy/client/tile.py +150 -0
  25. mapproxy/client/wms.py +254 -0
  26. mapproxy/compat/__init__.py +46 -0
  27. mapproxy/compat/image.py +79 -0
  28. mapproxy/compat/itertools.py +29 -0
  29. mapproxy/compat/modules.py +13 -0
  30. mapproxy/config/__init__.py +22 -0
  31. mapproxy/config/config.py +201 -0
  32. mapproxy/config/coverage.py +107 -0
  33. mapproxy/config/defaults.py +98 -0
  34. mapproxy/config/loader.py +2286 -0
  35. mapproxy/config/spec.py +644 -0
  36. mapproxy/config/validator.py +239 -0
  37. mapproxy/config_template/__init__.py +0 -0
  38. mapproxy/config_template/base_config/config.wsgi +10 -0
  39. mapproxy/config_template/base_config/full_example.yaml +593 -0
  40. mapproxy/config_template/base_config/full_seed_example.yaml +79 -0
  41. mapproxy/config_template/base_config/log.ini +35 -0
  42. mapproxy/config_template/base_config/mapproxy.yaml +60 -0
  43. mapproxy/config_template/base_config/seed.yaml +27 -0
  44. mapproxy/exception.py +142 -0
  45. mapproxy/featureinfo.py +252 -0
  46. mapproxy/grid.py +1170 -0
  47. mapproxy/image/__init__.py +536 -0
  48. mapproxy/image/fonts/DejaVuSans.ttf +0 -0
  49. mapproxy/image/fonts/DejaVuSansMono.ttf +0 -0
  50. mapproxy/image/fonts/LICENSE +99 -0
  51. mapproxy/image/fonts/__init__.py +0 -0
  52. mapproxy/image/mask.py +75 -0
  53. mapproxy/image/merge.py +316 -0
  54. mapproxy/image/message.py +347 -0
  55. mapproxy/image/opts.py +182 -0
  56. mapproxy/image/tile.py +167 -0
  57. mapproxy/image/transform.py +350 -0
  58. mapproxy/layer.py +470 -0
  59. mapproxy/multiapp.py +231 -0
  60. mapproxy/proj.py +302 -0
  61. mapproxy/request/__init__.py +18 -0
  62. mapproxy/request/arcgis.py +259 -0
  63. mapproxy/request/base.py +476 -0
  64. mapproxy/request/tile.py +128 -0
  65. mapproxy/request/wms/__init__.py +793 -0
  66. mapproxy/request/wms/exception.py +99 -0
  67. mapproxy/request/wmts.py +436 -0
  68. mapproxy/response.py +237 -0
  69. mapproxy/script/__init__.py +0 -0
  70. mapproxy/script/conf/__init__.py +0 -0
  71. mapproxy/script/conf/app.py +195 -0
  72. mapproxy/script/conf/caches.py +45 -0
  73. mapproxy/script/conf/layers.py +54 -0
  74. mapproxy/script/conf/seeds.py +37 -0
  75. mapproxy/script/conf/sources.py +86 -0
  76. mapproxy/script/conf/utils.py +143 -0
  77. mapproxy/script/defrag.py +184 -0
  78. mapproxy/script/export.py +333 -0
  79. mapproxy/script/grids.py +188 -0
  80. mapproxy/script/scales.py +126 -0
  81. mapproxy/script/util.py +406 -0
  82. mapproxy/script/wms_capabilities.py +152 -0
  83. mapproxy/seed/__init__.py +0 -0
  84. mapproxy/seed/cachelock.py +121 -0
  85. mapproxy/seed/cleanup.py +187 -0
  86. mapproxy/seed/config.py +469 -0
  87. mapproxy/seed/script.py +388 -0
  88. mapproxy/seed/seeder.py +538 -0
  89. mapproxy/seed/spec.py +64 -0
  90. mapproxy/seed/util.py +254 -0
  91. mapproxy/service/__init__.py +14 -0
  92. mapproxy/service/base.py +46 -0
  93. mapproxy/service/demo.py +356 -0
  94. mapproxy/service/kml.py +331 -0
  95. mapproxy/service/ows.py +38 -0
  96. mapproxy/service/template_helper.py +53 -0
  97. mapproxy/service/templates/demo/capabilities_demo.html +16 -0
  98. mapproxy/service/templates/demo/demo.html +181 -0
  99. mapproxy/service/templates/demo/openlayers-demo.cfg +16 -0
  100. mapproxy/service/templates/demo/static/img/blank.gif +0 -0
  101. mapproxy/service/templates/demo/static/img/east-mini.png +0 -0
  102. mapproxy/service/templates/demo/static/img/north-mini.png +0 -0
  103. mapproxy/service/templates/demo/static/img/south-mini.png +0 -0
  104. mapproxy/service/templates/demo/static/img/west-mini.png +0 -0
  105. mapproxy/service/templates/demo/static/img/zoom-minus-mini.png +0 -0
  106. mapproxy/service/templates/demo/static/img/zoom-plus-mini.png +0 -0
  107. mapproxy/service/templates/demo/static/img/zoom-world-mini.png +0 -0
  108. mapproxy/service/templates/demo/static/logo.png +0 -0
  109. mapproxy/service/templates/demo/static/ol.css +345 -0
  110. mapproxy/service/templates/demo/static/ol.js +4 -0
  111. mapproxy/service/templates/demo/static/proj4.min.js +1 -0
  112. mapproxy/service/templates/demo/static/proj4defs.js +1 -0
  113. mapproxy/service/templates/demo/static/site.css +137 -0
  114. mapproxy/service/templates/demo/static/theme/default/framedCloud.css +0 -0
  115. mapproxy/service/templates/demo/static/theme/default/google.css +17 -0
  116. mapproxy/service/templates/demo/static/theme/default/ie6-style.css +10 -0
  117. mapproxy/service/templates/demo/static/theme/default/style.css +482 -0
  118. mapproxy/service/templates/demo/static.html +34 -0
  119. mapproxy/service/templates/demo/tms_demo.html +103 -0
  120. mapproxy/service/templates/demo/wms_demo.html +140 -0
  121. mapproxy/service/templates/demo/wmts_demo.html +110 -0
  122. mapproxy/service/templates/tms_capabilities.xml +13 -0
  123. mapproxy/service/templates/tms_exception.xml +4 -0
  124. mapproxy/service/templates/tms_root_resource.xml +7 -0
  125. mapproxy/service/templates/tms_tilemap_capabilities.xml +14 -0
  126. mapproxy/service/templates/wms100capabilities.xml +112 -0
  127. mapproxy/service/templates/wms100exception.xml +4 -0
  128. mapproxy/service/templates/wms110capabilities.xml +152 -0
  129. mapproxy/service/templates/wms110exception.xml +5 -0
  130. mapproxy/service/templates/wms111capabilities.xml +183 -0
  131. mapproxy/service/templates/wms111exception.xml +5 -0
  132. mapproxy/service/templates/wms130capabilities.xml +326 -0
  133. mapproxy/service/templates/wms130exception.xml +8 -0
  134. mapproxy/service/templates/wmts100capabilities.xml +155 -0
  135. mapproxy/service/templates/wmts100exception.xml +9 -0
  136. mapproxy/service/tile.py +536 -0
  137. mapproxy/service/wms.py +851 -0
  138. mapproxy/service/wmts.py +381 -0
  139. mapproxy/source/__init__.py +75 -0
  140. mapproxy/source/arcgis.py +39 -0
  141. mapproxy/source/error.py +39 -0
  142. mapproxy/source/mapnik.py +259 -0
  143. mapproxy/source/tile.py +96 -0
  144. mapproxy/source/wms.py +270 -0
  145. mapproxy/srs.py +726 -0
  146. mapproxy/template.py +54 -0
  147. mapproxy/test/__init__.py +0 -0
  148. mapproxy/test/conftest.py +7 -0
  149. mapproxy/test/helper.py +247 -0
  150. mapproxy/test/http.py +494 -0
  151. mapproxy/test/image.py +210 -0
  152. mapproxy/test/mocker.py +2268 -0
  153. mapproxy/test/schemas/inspire/common/1.0/common.xsd +1461 -0
  154. mapproxy/test/schemas/inspire/common/1.0/enums/enum_bul.xsd +108 -0
  155. mapproxy/test/schemas/inspire/common/1.0/enums/enum_cze.xsd +108 -0
  156. mapproxy/test/schemas/inspire/common/1.0/enums/enum_dan.xsd +108 -0
  157. mapproxy/test/schemas/inspire/common/1.0/enums/enum_dut.xsd +108 -0
  158. mapproxy/test/schemas/inspire/common/1.0/enums/enum_eng.xsd +155 -0
  159. mapproxy/test/schemas/inspire/common/1.0/enums/enum_est.xsd +108 -0
  160. mapproxy/test/schemas/inspire/common/1.0/enums/enum_fin.xsd +108 -0
  161. mapproxy/test/schemas/inspire/common/1.0/enums/enum_fre.xsd +108 -0
  162. mapproxy/test/schemas/inspire/common/1.0/enums/enum_ger.xsd +108 -0
  163. mapproxy/test/schemas/inspire/common/1.0/enums/enum_gle.xsd +109 -0
  164. mapproxy/test/schemas/inspire/common/1.0/enums/enum_gre.xsd +108 -0
  165. mapproxy/test/schemas/inspire/common/1.0/enums/enum_hun.xsd +108 -0
  166. mapproxy/test/schemas/inspire/common/1.0/enums/enum_ita.xsd +108 -0
  167. mapproxy/test/schemas/inspire/common/1.0/enums/enum_lav.xsd +108 -0
  168. mapproxy/test/schemas/inspire/common/1.0/enums/enum_lit.xsd +108 -0
  169. mapproxy/test/schemas/inspire/common/1.0/enums/enum_mlt.xsd +108 -0
  170. mapproxy/test/schemas/inspire/common/1.0/enums/enum_pol.xsd +108 -0
  171. mapproxy/test/schemas/inspire/common/1.0/enums/enum_por.xsd +108 -0
  172. mapproxy/test/schemas/inspire/common/1.0/enums/enum_rum.xsd +108 -0
  173. mapproxy/test/schemas/inspire/common/1.0/enums/enum_slo.xsd +108 -0
  174. mapproxy/test/schemas/inspire/common/1.0/enums/enum_slv.xsd +108 -0
  175. mapproxy/test/schemas/inspire/common/1.0/enums/enum_spa.xsd +108 -0
  176. mapproxy/test/schemas/inspire/common/1.0/enums/enum_swe.xsd +108 -0
  177. mapproxy/test/schemas/inspire/common/1.0/network.xsd +521 -0
  178. mapproxy/test/schemas/inspire/inspire_vs/1.0/inspire_vs.xsd +19 -0
  179. mapproxy/test/schemas/kml/2.2.0/ReadMe.txt +14 -0
  180. mapproxy/test/schemas/kml/2.2.0/atom-author-link.xsd +66 -0
  181. mapproxy/test/schemas/kml/2.2.0/ogckml22.xsd +1646 -0
  182. mapproxy/test/schemas/kml/2.2.0/xAL.xsd +1680 -0
  183. mapproxy/test/schemas/ows/1.1.0/ReadMe.txt +87 -0
  184. mapproxy/test/schemas/ows/1.1.0/ows19115subset.xsd +235 -0
  185. mapproxy/test/schemas/ows/1.1.0/owsAll.xsd +23 -0
  186. mapproxy/test/schemas/ows/1.1.0/owsCommon.xsd +157 -0
  187. mapproxy/test/schemas/ows/1.1.0/owsContents.xsd +86 -0
  188. mapproxy/test/schemas/ows/1.1.0/owsDataIdentification.xsd +127 -0
  189. mapproxy/test/schemas/ows/1.1.0/owsDomainType.xsd +279 -0
  190. mapproxy/test/schemas/ows/1.1.0/owsExceptionReport.xsd +76 -0
  191. mapproxy/test/schemas/ows/1.1.0/owsGetCapabilities.xsd +112 -0
  192. mapproxy/test/schemas/ows/1.1.0/owsGetResourceByID.xsd +51 -0
  193. mapproxy/test/schemas/ows/1.1.0/owsInputOutputData.xsd +59 -0
  194. mapproxy/test/schemas/ows/1.1.0/owsManifest.xsd +125 -0
  195. mapproxy/test/schemas/ows/1.1.0/owsOperationsMetadata.xsd +140 -0
  196. mapproxy/test/schemas/ows/1.1.0/owsServiceIdentification.xsd +60 -0
  197. mapproxy/test/schemas/ows/1.1.0/owsServiceProvider.xsd +47 -0
  198. mapproxy/test/schemas/sld/1.1.0/sld_capabilities.xsd +27 -0
  199. mapproxy/test/schemas/wms/1.0.0/capabilities_1_0_0.dtd +353 -0
  200. mapproxy/test/schemas/wms/1.0.0/capabilities_1_0_0.xml +188 -0
  201. mapproxy/test/schemas/wms/1.0.7/capabilities_1_0_7.dtd +524 -0
  202. mapproxy/test/schemas/wms/1.0.7/capabilities_1_0_7.xml +260 -0
  203. mapproxy/test/schemas/wms/1.1.0/capabilities_1_1_0.dtd +273 -0
  204. mapproxy/test/schemas/wms/1.1.0/capabilities_1_1_0.xml +303 -0
  205. mapproxy/test/schemas/wms/1.1.0/exception_1_1_0.dtd +6 -0
  206. mapproxy/test/schemas/wms/1.1.0/exception_1_1_0.xml +33 -0
  207. mapproxy/test/schemas/wms/1.1.1/OGC-exception.xsd +68 -0
  208. mapproxy/test/schemas/wms/1.1.1/WMS_DescribeLayerResponse.dtd +22 -0
  209. mapproxy/test/schemas/wms/1.1.1/WMS_MS_Capabilities.dtd +274 -0
  210. mapproxy/test/schemas/wms/1.1.1/WMS_exception_1_1_1.dtd +5 -0
  211. mapproxy/test/schemas/wms/1.1.1/capabilities_1_1_1.dtd +276 -0
  212. mapproxy/test/schemas/wms/1.1.1/capabilities_1_1_1.xml +303 -0
  213. mapproxy/test/schemas/wms/1.1.1/exception_1_1_1.dtd +6 -0
  214. mapproxy/test/schemas/wms/1.1.1/exception_1_1_1.xml +33 -0
  215. mapproxy/test/schemas/wms/1.3.0/ReadMe.txt +8 -0
  216. mapproxy/test/schemas/wms/1.3.0/capabilities_1_3_0.xml +277 -0
  217. mapproxy/test/schemas/wms/1.3.0/capabilities_1_3_0.xsd +611 -0
  218. mapproxy/test/schemas/wms/1.3.0/exceptions_1_3_0.xml +34 -0
  219. mapproxy/test/schemas/wms/1.3.0/exceptions_1_3_0.xsd +28 -0
  220. mapproxy/test/schemas/wmsc/1.1.1/OGC-exception.xsd +68 -0
  221. mapproxy/test/schemas/wmsc/1.1.1/WMS_DescribeLayerResponse.dtd +22 -0
  222. mapproxy/test/schemas/wmsc/1.1.1/WMS_MS_Capabilities.dtd +283 -0
  223. mapproxy/test/schemas/wmsc/1.1.1/WMS_exception_1_1_1.dtd +5 -0
  224. mapproxy/test/schemas/wmsc/1.1.1/capabilities_1_1_1.dtd +276 -0
  225. mapproxy/test/schemas/wmsc/1.1.1/capabilities_1_1_1.xml +303 -0
  226. mapproxy/test/schemas/wmsc/1.1.1/exception_1_1_1.dtd +6 -0
  227. mapproxy/test/schemas/wmsc/1.1.1/exception_1_1_1.xml +33 -0
  228. mapproxy/test/schemas/wmts/1.0/ReadMe.txt +32 -0
  229. mapproxy/test/schemas/wmts/1.0/wmts.xsd +28 -0
  230. mapproxy/test/schemas/wmts/1.0/wmtsAbstract.wsdl +151 -0
  231. mapproxy/test/schemas/wmts/1.0/wmtsGetCapabilities_request.xsd +38 -0
  232. mapproxy/test/schemas/wmts/1.0/wmtsGetCapabilities_response.xsd +564 -0
  233. mapproxy/test/schemas/wmts/1.0/wmtsGetFeatureInfo_request.xsd +57 -0
  234. mapproxy/test/schemas/wmts/1.0/wmtsGetFeatureInfo_response.xsd +72 -0
  235. mapproxy/test/schemas/wmts/1.0/wmtsGetTile_request.xsd +91 -0
  236. mapproxy/test/schemas/wmts/1.0/wmtsKVP.xsd +76 -0
  237. mapproxy/test/schemas/wmts/1.0/wmtsPayload_response.xsd +70 -0
  238. mapproxy/test/schemas/xlink/1.0.0/ReadMe.txt +6 -0
  239. mapproxy/test/schemas/xlink/1.0.0/xlinks.xsd +122 -0
  240. mapproxy/test/schemas/xml.xsd +287 -0
  241. mapproxy/test/system/__init__.py +98 -0
  242. mapproxy/test/system/fixture/arcgis.yaml +57 -0
  243. mapproxy/test/system/fixture/auth.yaml +70 -0
  244. mapproxy/test/system/fixture/cache.mbtiles +0 -0
  245. mapproxy/test/system/fixture/cache_azureblob.yaml +59 -0
  246. mapproxy/test/system/fixture/cache_band_merge.yaml +73 -0
  247. mapproxy/test/system/fixture/cache_bulk_meta_tiles.yaml +24 -0
  248. mapproxy/test/system/fixture/cache_data/dop_cache_EPSG3857/00/000/000/000/000/000/000.png +0 -0
  249. mapproxy/test/system/fixture/cache_data/wms_cache_EPSG900913/01/000/000/000/000/000/001.jpeg +0 -0
  250. mapproxy/test/system/fixture/cache_data/wms_cache_transparent_EPSG900913/01/000/000/000/000/000/001.png +0 -0
  251. mapproxy/test/system/fixture/cache_geopackage.yaml +56 -0
  252. mapproxy/test/system/fixture/cache_grid_names.yaml +50 -0
  253. mapproxy/test/system/fixture/cache_mbtiles.yaml +28 -0
  254. mapproxy/test/system/fixture/cache_s3.yaml +58 -0
  255. mapproxy/test/system/fixture/cache_source.yaml +81 -0
  256. mapproxy/test/system/fixture/combined_sources.yaml +130 -0
  257. mapproxy/test/system/fixture/coverage.yaml +77 -0
  258. mapproxy/test/system/fixture/demo.yaml +135 -0
  259. mapproxy/test/system/fixture/dimension.yaml +59 -0
  260. mapproxy/test/system/fixture/disable_storage.yaml +25 -0
  261. mapproxy/test/system/fixture/empty_ogrdata.geojson +1 -0
  262. mapproxy/test/system/fixture/formats.yaml +72 -0
  263. mapproxy/test/system/fixture/inspire.yaml +101 -0
  264. mapproxy/test/system/fixture/inspire_full.yaml +124 -0
  265. mapproxy/test/system/fixture/kml_layer.yaml +66 -0
  266. mapproxy/test/system/fixture/layer.yaml +260 -0
  267. mapproxy/test/system/fixture/layergroups.yaml +57 -0
  268. mapproxy/test/system/fixture/layergroups_root.yaml +106 -0
  269. mapproxy/test/system/fixture/legendgraphic.yaml +93 -0
  270. mapproxy/test/system/fixture/mapnik_source.yaml +66 -0
  271. mapproxy/test/system/fixture/mapproxy_export.yaml +12 -0
  272. mapproxy/test/system/fixture/mapserver.yaml +23 -0
  273. mapproxy/test/system/fixture/minimal_cgi.py +16 -0
  274. mapproxy/test/system/fixture/mixed_mode.yaml +49 -0
  275. mapproxy/test/system/fixture/multi_cache_layers.yaml +100 -0
  276. mapproxy/test/system/fixture/multiapp1.yaml +20 -0
  277. mapproxy/test/system/fixture/multiapp2.yaml +19 -0
  278. mapproxy/test/system/fixture/renderd_client.yaml +55 -0
  279. mapproxy/test/system/fixture/scalehints.yaml +70 -0
  280. mapproxy/test/system/fixture/seed.yaml +94 -0
  281. mapproxy/test/system/fixture/seed_mapproxy.yaml +39 -0
  282. mapproxy/test/system/fixture/seed_old.yaml +12 -0
  283. mapproxy/test/system/fixture/seed_timeouts.yaml +12 -0
  284. mapproxy/test/system/fixture/seed_timeouts_mapproxy.yaml +27 -0
  285. mapproxy/test/system/fixture/seedonly.yaml +51 -0
  286. mapproxy/test/system/fixture/sld.yaml +35 -0
  287. mapproxy/test/system/fixture/source_errors.yaml +84 -0
  288. mapproxy/test/system/fixture/source_errors_raise.yaml +82 -0
  289. mapproxy/test/system/fixture/tileservice_origin.yaml +26 -0
  290. mapproxy/test/system/fixture/tileservice_refresh.yaml +59 -0
  291. mapproxy/test/system/fixture/tilesource_minmax_res.yaml +22 -0
  292. mapproxy/test/system/fixture/util-conf-base-grids.yaml +5 -0
  293. mapproxy/test/system/fixture/util-conf-overwrite.yaml +13 -0
  294. mapproxy/test/system/fixture/util-conf-wms-111-cap.xml +90 -0
  295. mapproxy/test/system/fixture/util_grids.yaml +30 -0
  296. mapproxy/test/system/fixture/util_wms_capabilities111.xml +130 -0
  297. mapproxy/test/system/fixture/util_wms_capabilities130.xml +100 -0
  298. mapproxy/test/system/fixture/util_wms_capabilities_service_exception.xml +5 -0
  299. mapproxy/test/system/fixture/watermark.yaml +50 -0
  300. mapproxy/test/system/fixture/wms_srs_extent.yaml +39 -0
  301. mapproxy/test/system/fixture/wms_versions.yaml +38 -0
  302. mapproxy/test/system/fixture/wmts.yaml +134 -0
  303. mapproxy/test/system/fixture/wmts_dimensions.yaml +57 -0
  304. mapproxy/test/system/fixture/xslt_featureinfo.yaml +54 -0
  305. mapproxy/test/system/fixture/xslt_featureinfo_input.yaml +51 -0
  306. mapproxy/test/system/test_arcgis.py +156 -0
  307. mapproxy/test/system/test_auth.py +1134 -0
  308. mapproxy/test/system/test_behind_proxy.py +75 -0
  309. mapproxy/test/system/test_bulk_meta_tiles.py +106 -0
  310. mapproxy/test/system/test_cache_azureblob.py +127 -0
  311. mapproxy/test/system/test_cache_band_merge.py +103 -0
  312. mapproxy/test/system/test_cache_geopackage.py +144 -0
  313. mapproxy/test/system/test_cache_grid_names.py +89 -0
  314. mapproxy/test/system/test_cache_mbtiles.py +85 -0
  315. mapproxy/test/system/test_cache_s3.py +115 -0
  316. mapproxy/test/system/test_cache_source.py +146 -0
  317. mapproxy/test/system/test_combined_sources.py +335 -0
  318. mapproxy/test/system/test_coverage.py +140 -0
  319. mapproxy/test/system/test_decorate_img.py +214 -0
  320. mapproxy/test/system/test_demo.py +106 -0
  321. mapproxy/test/system/test_demo_with_extra_service.py +53 -0
  322. mapproxy/test/system/test_dimensions.py +278 -0
  323. mapproxy/test/system/test_disable_storage.py +42 -0
  324. mapproxy/test/system/test_formats.py +219 -0
  325. mapproxy/test/system/test_inspire_vs.py +173 -0
  326. mapproxy/test/system/test_kml.py +262 -0
  327. mapproxy/test/system/test_layergroups.py +160 -0
  328. mapproxy/test/system/test_legendgraphic.py +308 -0
  329. mapproxy/test/system/test_mapnik.py +161 -0
  330. mapproxy/test/system/test_mapserver.py +81 -0
  331. mapproxy/test/system/test_mixed_mode_format.py +195 -0
  332. mapproxy/test/system/test_multi_cache_layers.py +167 -0
  333. mapproxy/test/system/test_multiapp.py +92 -0
  334. mapproxy/test/system/test_refresh.py +207 -0
  335. mapproxy/test/system/test_renderd_client.py +304 -0
  336. mapproxy/test/system/test_response_headers.py +54 -0
  337. mapproxy/test/system/test_scalehints.py +140 -0
  338. mapproxy/test/system/test_seed.py +422 -0
  339. mapproxy/test/system/test_seed_only.py +93 -0
  340. mapproxy/test/system/test_sld.py +120 -0
  341. mapproxy/test/system/test_source_errors.py +377 -0
  342. mapproxy/test/system/test_tilesource_minmax_res.py +54 -0
  343. mapproxy/test/system/test_tms.py +276 -0
  344. mapproxy/test/system/test_tms_origin.py +46 -0
  345. mapproxy/test/system/test_util_conf.py +304 -0
  346. mapproxy/test/system/test_util_export.py +210 -0
  347. mapproxy/test/system/test_util_grids.py +88 -0
  348. mapproxy/test/system/test_util_wms_capabilities.py +182 -0
  349. mapproxy/test/system/test_watermark.py +91 -0
  350. mapproxy/test/system/test_wms.py +1611 -0
  351. mapproxy/test/system/test_wms_srs_extent.py +165 -0
  352. mapproxy/test/system/test_wms_version.py +85 -0
  353. mapproxy/test/system/test_wmsc.py +116 -0
  354. mapproxy/test/system/test_wmts.py +334 -0
  355. mapproxy/test/system/test_wmts_dimensions.py +206 -0
  356. mapproxy/test/system/test_wmts_restful.py +198 -0
  357. mapproxy/test/system/test_xslt_featureinfo.py +425 -0
  358. mapproxy/test/test_http_helper.py +219 -0
  359. mapproxy/test/unit/__init__.py +0 -0
  360. mapproxy/test/unit/epsg +2 -0
  361. mapproxy/test/unit/polygons/polygons.dbf +0 -0
  362. mapproxy/test/unit/polygons/polygons.shp +0 -0
  363. mapproxy/test/unit/polygons/polygons.shx +0 -0
  364. mapproxy/test/unit/test_async.py +245 -0
  365. mapproxy/test/unit/test_auth.py +419 -0
  366. mapproxy/test/unit/test_cache.py +1193 -0
  367. mapproxy/test/unit/test_cache_azureblob.py +94 -0
  368. mapproxy/test/unit/test_cache_compact.py +319 -0
  369. mapproxy/test/unit/test_cache_couchdb.py +114 -0
  370. mapproxy/test/unit/test_cache_geopackage.py +221 -0
  371. mapproxy/test/unit/test_cache_redis.py +67 -0
  372. mapproxy/test/unit/test_cache_riak.py +76 -0
  373. mapproxy/test/unit/test_cache_s3.py +84 -0
  374. mapproxy/test/unit/test_cache_tile.py +427 -0
  375. mapproxy/test/unit/test_client.py +479 -0
  376. mapproxy/test/unit/test_client_arcgis.py +73 -0
  377. mapproxy/test/unit/test_client_cgi.py +136 -0
  378. mapproxy/test/unit/test_collections.py +116 -0
  379. mapproxy/test/unit/test_concat_legends.py +37 -0
  380. mapproxy/test/unit/test_conf_loader.py +1061 -0
  381. mapproxy/test/unit/test_conf_validator.py +416 -0
  382. mapproxy/test/unit/test_config.py +117 -0
  383. mapproxy/test/unit/test_decorate_img.py +185 -0
  384. mapproxy/test/unit/test_exceptions.py +258 -0
  385. mapproxy/test/unit/test_featureinfo.py +291 -0
  386. mapproxy/test/unit/test_file_lock_load.py +49 -0
  387. mapproxy/test/unit/test_geom.py +503 -0
  388. mapproxy/test/unit/test_grid.py +1258 -0
  389. mapproxy/test/unit/test_image.py +1053 -0
  390. mapproxy/test/unit/test_image_mask.py +181 -0
  391. mapproxy/test/unit/test_image_messages.py +197 -0
  392. mapproxy/test/unit/test_image_options.py +160 -0
  393. mapproxy/test/unit/test_isodate.py +122 -0
  394. mapproxy/test/unit/test_multiapp.py +163 -0
  395. mapproxy/test/unit/test_ogr_reader.py +50 -0
  396. mapproxy/test/unit/test_request.py +745 -0
  397. mapproxy/test/unit/test_request_wmts.py +178 -0
  398. mapproxy/test/unit/test_response.py +79 -0
  399. mapproxy/test/unit/test_seed.py +365 -0
  400. mapproxy/test/unit/test_seed_cachelock.py +90 -0
  401. mapproxy/test/unit/test_srs.py +215 -0
  402. mapproxy/test/unit/test_tiled_source.py +122 -0
  403. mapproxy/test/unit/test_tilefilter.py +31 -0
  404. mapproxy/test/unit/test_times.py +25 -0
  405. mapproxy/test/unit/test_timeutils.py +50 -0
  406. mapproxy/test/unit/test_util_conf_utils.py +75 -0
  407. mapproxy/test/unit/test_utils.py +476 -0
  408. mapproxy/test/unit/test_wms_capabilities.py +44 -0
  409. mapproxy/test/unit/test_wms_layer.py +113 -0
  410. mapproxy/test/unit/test_yaml.py +69 -0
  411. mapproxy/tilefilter.py +59 -0
  412. mapproxy/util/__init__.py +0 -0
  413. mapproxy/util/async_.py +227 -0
  414. mapproxy/util/collections.py +132 -0
  415. mapproxy/util/coverage.py +329 -0
  416. mapproxy/util/escape.py +10 -0
  417. mapproxy/util/ext/__init__.py +14 -0
  418. mapproxy/util/ext/dictspec/__init__.py +1 -0
  419. mapproxy/util/ext/dictspec/spec.py +124 -0
  420. mapproxy/util/ext/dictspec/test/__init__.py +0 -0
  421. mapproxy/util/ext/dictspec/test/test_validator.py +274 -0
  422. mapproxy/util/ext/dictspec/validator.py +189 -0
  423. mapproxy/util/ext/local.py +196 -0
  424. mapproxy/util/ext/lockfile.py +138 -0
  425. mapproxy/util/ext/odict.py +330 -0
  426. mapproxy/util/ext/serving.py +508 -0
  427. mapproxy/util/ext/tempita/__init__.py +1174 -0
  428. mapproxy/util/ext/tempita/_looper.py +163 -0
  429. mapproxy/util/ext/tempita/compat3.py +46 -0
  430. mapproxy/util/ext/wmsparse/__init__.py +3 -0
  431. mapproxy/util/ext/wmsparse/duration.py +597 -0
  432. mapproxy/util/ext/wmsparse/parse.py +305 -0
  433. mapproxy/util/ext/wmsparse/test/__init__.py +0 -0
  434. mapproxy/util/ext/wmsparse/test/test_parse.py +162 -0
  435. mapproxy/util/ext/wmsparse/test/test_util.py +23 -0
  436. mapproxy/util/ext/wmsparse/test/wms-large-111.xml +2114 -0
  437. mapproxy/util/ext/wmsparse/test/wms-omniscale-111.xml +90 -0
  438. mapproxy/util/ext/wmsparse/test/wms-omniscale-130.xml +120 -0
  439. mapproxy/util/ext/wmsparse/test/wms_nasa_cap.xml +386 -0
  440. mapproxy/util/ext/wmsparse/util.py +187 -0
  441. mapproxy/util/fs.py +156 -0
  442. mapproxy/util/geom.py +295 -0
  443. mapproxy/util/lib.py +115 -0
  444. mapproxy/util/lock.py +163 -0
  445. mapproxy/util/ogr.py +231 -0
  446. mapproxy/util/py.py +81 -0
  447. mapproxy/util/times.py +75 -0
  448. mapproxy/util/yaml.py +56 -0
  449. mapproxy/version.py +31 -0
  450. mapproxy/wsgiapp.py +164 -0
  451. mapproxy-1.16.1.dist-info/METADATA +151 -0
  452. mapproxy-1.16.1.dist-info/RECORD +458 -0
  453. mapproxy-1.16.1.dist-info/WHEEL +5 -0
  454. mapproxy-1.16.1.dist-info/entry_points.txt +3 -0
  455. mapproxy-1.16.1.dist-info/licenses/AUTHORS.txt +33 -0
  456. mapproxy-1.16.1.dist-info/licenses/COPYING.txt +60 -0
  457. mapproxy-1.16.1.dist-info/licenses/LICENSE.txt +202 -0
  458. mapproxy-1.16.1.dist-info/top_level.txt +1 -0
mapproxy/layer.py ADDED
@@ -0,0 +1,470 @@
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
+ """
18
+ Layers that can get maps/infos from different sources/caches.
19
+ """
20
+
21
+ from __future__ import division
22
+ from mapproxy.grid import NoTiles, GridError, merge_resolution_range, bbox_intersects, bbox_contains
23
+ from mapproxy.image import SubImageSource, bbox_position_in_image
24
+ from mapproxy.image.opts import ImageOptions
25
+ from mapproxy.image.tile import TiledImage
26
+ from mapproxy.srs import SRS, bbox_equals, merge_bbox, make_lin_transf, SupportedSRS
27
+ from mapproxy.proj import ProjError
28
+ from mapproxy.compat import iteritems
29
+
30
+ import logging
31
+ from functools import reduce
32
+ log = logging.getLogger(__name__)
33
+
34
+ class BlankImage(Exception):
35
+ pass
36
+
37
+ class MapError(Exception):
38
+ pass
39
+
40
+ class MapBBOXError(Exception):
41
+ pass
42
+
43
+ class MapLayer(object):
44
+ supports_meta_tiles = False
45
+
46
+ res_range = None
47
+
48
+ coverage = None
49
+
50
+ def __init__(self, image_opts=None):
51
+ self.image_opts = image_opts or ImageOptions()
52
+
53
+ def _get_opacity(self):
54
+ return self.image_opts.opacity
55
+
56
+ def _set_opacity(self, value):
57
+ self.image_opts.opacity = value
58
+
59
+ opacity = property(_get_opacity, _set_opacity)
60
+
61
+ def is_opaque(self, query):
62
+ """
63
+ Whether the query result is opaque.
64
+
65
+ This method is used for optimizations: layers below an opaque
66
+ layer can be skipped. As sources with `transparent: false`
67
+ still can return transparent images (min_res/max_res/coverages),
68
+ implementations of this method need to be certain that the image
69
+ is indeed opaque. is_opaque should return False if in doubt.
70
+ """
71
+ return False
72
+
73
+ def check_res_range(self, query):
74
+ if (self.res_range and
75
+ not self.res_range.contains(query.bbox, query.size, query.srs)):
76
+ raise BlankImage()
77
+
78
+ def get_map(self, query):
79
+ raise NotImplementedError
80
+
81
+ def combined_layer(self, other, query):
82
+ return None
83
+
84
+ class LimitedLayer(object):
85
+ """
86
+ Wraps an existing layer temporary and stores additional
87
+ attributes for geographical limits.
88
+ """
89
+ def __init__(self, layer, coverage):
90
+ self._layer = layer
91
+ self.coverage = coverage
92
+
93
+ def __getattr__(self, name):
94
+ return getattr(self._layer, name)
95
+
96
+ def combined_layer(self, other, query):
97
+ if self.coverage == other.coverage:
98
+ combined = self._layer.combined_layer(other, query)
99
+ if combined:
100
+ return LimitedLayer(combined, self.coverage)
101
+ return None
102
+
103
+ def get_info(self, query):
104
+ if self.coverage:
105
+ if not self.coverage.contains(query.coord, query.srs):
106
+ return None
107
+ return self._layer.get_info(query)
108
+
109
+ class InfoLayer(object):
110
+ def get_info(self, query):
111
+ raise NotImplementedError
112
+
113
+ class MapQuery(object):
114
+ """
115
+ Internal query for a map with a specific extent, size, srs, etc.
116
+ """
117
+ def __init__(self, bbox, size, srs, format='image/png', transparent=False,
118
+ tiled_only=False, dimensions=None):
119
+ self.bbox = bbox
120
+ self.size = size
121
+ self.srs = srs
122
+ self.format = format
123
+ self.transparent = transparent
124
+ self.tiled_only = tiled_only
125
+ self.dimensions = dimensions or {}
126
+
127
+ def dimensions_for_params(self, params):
128
+ """
129
+ Return subset of the dimensions.
130
+
131
+ >>> mq = MapQuery(None, None, None, dimensions={'Foo': 1, 'bar': 2})
132
+ >>> mq.dimensions_for_params(set(['FOO', 'baz']))
133
+ {'Foo': 1}
134
+ """
135
+ params = [p.lower() for p in params]
136
+ return dict((k, v) for k, v in iteritems(self.dimensions) if k.lower() in params)
137
+
138
+ def __repr__(self):
139
+ info = self.__dict__
140
+ serialized_dimensions = ", ".join(["'%s': '%s'" % (key, value) for (key, value) in self.dimensions.items()])
141
+ info["serialized_dimensions"] = serialized_dimensions
142
+ return "MapQuery(bbox=%(bbox)s, size=%(size)s, srs=%(srs)r, format=%(format)s, dimensions={%(serialized_dimensions)s)}" % info
143
+
144
+
145
+ class InfoQuery(object):
146
+ def __init__(self, bbox, size, srs, pos, info_format, format=None,
147
+ feature_count=None):
148
+ self.bbox = bbox
149
+ self.size = size
150
+ self.srs = srs
151
+ self.pos = pos
152
+ self.info_format = info_format
153
+ self.format = format
154
+ self.feature_count = feature_count
155
+
156
+ @property
157
+ def coord(self):
158
+ return make_lin_transf((0, 0, self.size[0], self.size[1]), self.bbox)(self.pos)
159
+
160
+ class LegendQuery(object):
161
+ def __init__(self, format, scale):
162
+ self.format = format
163
+ self.scale = scale
164
+
165
+ class Dimension(list):
166
+ def __init__(self, identifier, values, default=None):
167
+ self.identifier = identifier
168
+ if not default and values:
169
+ default = values[0]
170
+ self.default = default
171
+ list.__init__(self, values)
172
+
173
+
174
+ def map_extent_from_grid(grid):
175
+ """
176
+ >>> from mapproxy.grid import tile_grid_for_epsg
177
+ >>> map_extent_from_grid(tile_grid_for_epsg('EPSG:900913'))
178
+ ... #doctest: +NORMALIZE_WHITESPACE
179
+ MapExtent((-20037508.342789244, -20037508.342789244,
180
+ 20037508.342789244, 20037508.342789244), SRS('EPSG:900913'))
181
+ """
182
+ return MapExtent(grid.bbox, grid.srs)
183
+
184
+ class MapExtent(object):
185
+ """
186
+ >>> me = MapExtent((5, 45, 15, 55), SRS(4326))
187
+ >>> me.llbbox
188
+ (5, 45, 15, 55)
189
+ >>> [int(x) for x in me.bbox_for(SRS(900913))]
190
+ [556597, 5621521, 1669792, 7361866]
191
+ >>> [int(x) for x in me.bbox_for(SRS(4326))]
192
+ [5, 45, 15, 55]
193
+ """
194
+ is_default = False
195
+ def __init__(self, bbox, srs):
196
+ self._llbbox = None
197
+ self.bbox = bbox
198
+ self.srs = srs
199
+
200
+ @property
201
+ def llbbox(self):
202
+ if not self._llbbox:
203
+ self._llbbox = self.srs.transform_bbox_to(self.srs.get_geographic_srs(), self.bbox)
204
+ return self._llbbox
205
+
206
+ def bbox_for(self, srs):
207
+ if srs == self.srs:
208
+ return self.bbox
209
+
210
+ return self.srs.transform_bbox_to(srs, self.bbox)
211
+
212
+ def __repr__(self):
213
+ return "%s(%r, %r)" % (self.__class__.__name__, self.bbox, self.srs)
214
+
215
+ def __eq__(self, other):
216
+ if not isinstance(other, MapExtent):
217
+ return NotImplemented
218
+
219
+ if self.srs != other.srs:
220
+ return False
221
+
222
+ if self.bbox != other.bbox:
223
+ return False
224
+
225
+ return True
226
+
227
+ def __ne__(self, other):
228
+ if not isinstance(other, MapExtent):
229
+ return NotImplemented
230
+ return not self.__eq__(other)
231
+
232
+ def __add__(self, other):
233
+ if not isinstance(other, MapExtent):
234
+ raise NotImplemented
235
+ if other.is_default:
236
+ return self
237
+ if self.is_default:
238
+ return other
239
+ return MapExtent(merge_bbox(self.llbbox, other.llbbox), self.srs.get_geographic_srs())
240
+
241
+ def contains(self, other):
242
+ if not isinstance(other, MapExtent):
243
+ raise NotImplemented
244
+ if self.is_default:
245
+ # DefaultMapExtent contains everything
246
+ return True
247
+ return bbox_contains(self.bbox, other.bbox_for(self.srs))
248
+
249
+ def intersects(self, other):
250
+ if not isinstance(other, MapExtent):
251
+ raise NotImplemented
252
+ return bbox_intersects(self.bbox, other.bbox_for(self.srs))
253
+
254
+ def intersection(self, other):
255
+ """
256
+ Returns the intersection of `self` and `other`.
257
+
258
+ >>> e = DefaultMapExtent().intersection(MapExtent((0, 0, 10, 10), SRS(4326)))
259
+ >>> e.bbox, e.srs
260
+ ((0, 0, 10, 10), SRS('EPSG:4326'))
261
+ """
262
+ if not self.intersects(other):
263
+ return None
264
+
265
+ source = self.bbox
266
+ sub = other.bbox_for(self.srs)
267
+
268
+ return MapExtent((
269
+ max(source[0], sub[0]),
270
+ max(source[1], sub[1]),
271
+ min(source[2], sub[2]),
272
+ min(source[3], sub[3])),
273
+ self.srs)
274
+
275
+ def transform(self, srs):
276
+ return MapExtent(self.bbox_for(srs), srs)
277
+
278
+ class DefaultMapExtent(MapExtent):
279
+ """
280
+ Default extent that covers the whole world.
281
+ Will not affect other extents when added.
282
+
283
+ >>> m1 = MapExtent((0, 0, 10, 10), SRS(4326))
284
+ >>> m2 = MapExtent((10, 0, 20, 10), SRS(4326))
285
+ >>> m3 = DefaultMapExtent()
286
+ >>> (m1 + m2).bbox
287
+ (0, 0, 20, 10)
288
+ >>> (m1 + m3).bbox
289
+ (0, 0, 10, 10)
290
+ """
291
+ is_default = True
292
+ def __init__(self):
293
+ MapExtent.__init__(self, (-180, -90, 180, 90), SRS(4326))
294
+
295
+ def merge_layer_extents(layers):
296
+ if not layers:
297
+ return DefaultMapExtent()
298
+ layers = layers[:]
299
+ extent = layers.pop().extent
300
+ for layer in layers:
301
+ extent = extent + layer.extent
302
+ return extent
303
+
304
+ class ResolutionConditional(MapLayer):
305
+ supports_meta_tiles = True
306
+ def __init__(self, one, two, resolution, srs, extent, opacity=None):
307
+ MapLayer.__init__(self)
308
+ self.one = one
309
+ self.two = two
310
+ self.res_range = merge_layer_res_ranges([one, two])
311
+ self.resolution = resolution
312
+ self.srs = srs
313
+
314
+ self.opacity = opacity
315
+ self.extent = extent
316
+
317
+ def get_map(self, query):
318
+ self.check_res_range(query)
319
+ bbox = query.bbox
320
+ if query.srs != self.srs:
321
+ bbox = query.srs.transform_bbox_to(self.srs, bbox)
322
+
323
+ xres = (bbox[2] - bbox[0]) / query.size[0]
324
+ yres = (bbox[3] - bbox[1]) / query.size[1]
325
+ res = min(xres, yres)
326
+ log.debug('actual res: %s, threshold res: %s', res, self.resolution)
327
+
328
+ if res > self.resolution:
329
+ return self.one.get_map(query)
330
+ else:
331
+ return self.two.get_map(query)
332
+
333
+ class SRSConditional(MapLayer):
334
+ supports_meta_tiles = True
335
+
336
+ def __init__(self, layers, extent, opacity=None, preferred_srs=None):
337
+ MapLayer.__init__(self)
338
+ self.srs_map = {}
339
+ self.res_range = merge_layer_res_ranges([l[0] for l in layers])
340
+
341
+ supported_srs = []
342
+ for layer, srs in layers:
343
+ supported_srs.append(srs)
344
+ self.srs_map[srs] = layer
345
+ self.supported_srs = SupportedSRS(supported_srs, preferred_srs)
346
+ self.extent = extent
347
+ self.opacity = opacity
348
+
349
+ def get_map(self, query):
350
+ self.check_res_range(query)
351
+ layer = self._select_layer(query.srs)
352
+ return layer.get_map(query)
353
+
354
+ def _select_layer(self, query_srs):
355
+ srs = self.supported_srs.best_srs(query_srs)
356
+ return self.srs_map[srs]
357
+
358
+ class DirectMapLayer(MapLayer):
359
+ supports_meta_tiles = True
360
+
361
+ def __init__(self, source, extent):
362
+ MapLayer.__init__(self)
363
+ self.source = source
364
+ self.res_range = getattr(source, 'res_range', None)
365
+ self.extent = extent
366
+
367
+ def get_map(self, query):
368
+ self.check_res_range(query)
369
+ return self.source.get_map(query)
370
+
371
+
372
+ def merge_layer_res_ranges(layers):
373
+ ranges = [s.res_range for s in layers
374
+ if hasattr(s, 'res_range')]
375
+
376
+ if ranges:
377
+ ranges = reduce(merge_resolution_range, ranges)
378
+
379
+ return ranges
380
+
381
+
382
+ class CacheMapLayer(MapLayer):
383
+ supports_meta_tiles = True
384
+
385
+ def __init__(self, tile_manager, extent=None, image_opts=None,
386
+ max_tile_limit=None):
387
+ MapLayer.__init__(self, image_opts=image_opts)
388
+ self.tile_manager = tile_manager
389
+ self.grid = tile_manager.grid
390
+ self.extent = extent or map_extent_from_grid(self.grid)
391
+ self.res_range = []
392
+ if not self.tile_manager.rescale_tiles:
393
+ self.res_range = merge_layer_res_ranges(self.tile_manager.sources)
394
+ self.max_tile_limit = max_tile_limit
395
+
396
+ def get_map(self, query):
397
+ self.check_res_range(query)
398
+
399
+ if query.tiled_only:
400
+ self._check_tiled(query)
401
+
402
+ query_extent = MapExtent(query.bbox, query.srs)
403
+ if not query.tiled_only and self.extent and not self.extent.contains(query_extent):
404
+ if not self.extent.intersects(query_extent):
405
+ raise BlankImage()
406
+ size, offset, bbox = bbox_position_in_image(query.bbox, query.size, self.extent.bbox_for(query.srs))
407
+ if size[0] == 0 or size[1] == 0:
408
+ raise BlankImage()
409
+ src_query = MapQuery(bbox, size, query.srs, query.format, dimensions=query.dimensions)
410
+ resp = self._image(src_query)
411
+ result = SubImageSource(resp, size=query.size, offset=offset, image_opts=self.image_opts,
412
+ cacheable=resp.cacheable)
413
+ else:
414
+ result = self._image(query)
415
+ return result
416
+
417
+ def _check_tiled(self, query):
418
+ if query.format != self.tile_manager.format:
419
+ raise MapError("invalid tile format, use %s" % self.tile_manager.format)
420
+ if query.size != self.grid.tile_size:
421
+ raise MapError("invalid tile size (use %dx%d)" % self.grid.tile_size)
422
+
423
+ def _image(self, query):
424
+ try:
425
+ src_bbox, tile_grid, affected_tile_coords = \
426
+ self.grid.get_affected_tiles(query.bbox, query.size,
427
+ req_srs=query.srs)
428
+ except NoTiles:
429
+ raise BlankImage()
430
+ except GridError as ex:
431
+ raise MapBBOXError(ex.args[0])
432
+
433
+ num_tiles = tile_grid[0] * tile_grid[1]
434
+
435
+ if self.max_tile_limit and num_tiles >= self.max_tile_limit:
436
+ raise MapBBOXError("too many tiles, max_tile_limit: %s, num_tiles: %s" % (self.max_tile_limit, num_tiles))
437
+
438
+ if query.tiled_only:
439
+ if num_tiles > 1:
440
+ raise MapBBOXError("not a single tile")
441
+ bbox = query.bbox
442
+ if not bbox_equals(bbox, src_bbox, abs((bbox[2]-bbox[0])/query.size[0]/10),
443
+ abs((bbox[3]-bbox[1])/query.size[1]/10)):
444
+ raise MapBBOXError("query does not align to tile boundaries")
445
+
446
+ with self.tile_manager.session():
447
+ tile_collection = self.tile_manager.load_tile_coords(affected_tile_coords, with_metadata=query.tiled_only, dimensions=query.dimensions)
448
+
449
+ if tile_collection.empty:
450
+ raise BlankImage()
451
+
452
+ if query.tiled_only:
453
+ tile = tile_collection[0].source
454
+ tile.image_opts = self.tile_manager.image_opts
455
+ tile.cacheable = tile_collection[0].cacheable
456
+ return tile
457
+
458
+ tile_sources = [tile.source for tile in tile_collection]
459
+ tiled_image = TiledImage(tile_sources, src_bbox=src_bbox, src_srs=self.grid.srs,
460
+ tile_grid=tile_grid, tile_size=self.grid.tile_size)
461
+ try:
462
+ return tiled_image.transform(query.bbox, query.srs, query.size,
463
+ self.tile_manager.image_opts)
464
+ except ProjError:
465
+ raise MapBBOXError("could not transform query BBOX")
466
+ except IOError as ex:
467
+ from mapproxy.source import SourceError
468
+ raise SourceError("unable to transform image: %s" % ex)
469
+
470
+
mapproxy/multiapp.py ADDED
@@ -0,0 +1,231 @@
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
+ import os
18
+
19
+ from mapproxy.request import Request
20
+ from mapproxy.response import Response
21
+ from mapproxy.util.collections import LRU
22
+ from mapproxy.wsgiapp import make_wsgi_app as make_mapproxy_wsgi_app
23
+ from mapproxy.util.escape import escape_html
24
+
25
+ from threading import Lock
26
+
27
+ import logging
28
+ log = logging.getLogger(__name__)
29
+
30
+
31
+ def asbool(value):
32
+ """
33
+ >>> all([asbool(True), asbool('trUE'), asbool('ON'), asbool(1)])
34
+ True
35
+ >>> any([asbool(False), asbool('false'), asbool('foo'), asbool(None)])
36
+ False
37
+ """
38
+ value = str(value).lower()
39
+ return value in ('1', 'true', 'yes', 'on')
40
+
41
+
42
+ def app_factory(global_options, config_dir, allow_listing=False, **local_options):
43
+ """
44
+ Create a new MultiMapProxy app.
45
+
46
+ :param config_dir: directory with all mapproxy configurations
47
+ :param allow_listing: allow to list all available apps
48
+ """
49
+ return make_wsgi_app(config_dir, asbool(allow_listing))
50
+
51
+
52
+ def make_wsgi_app(config_dir, allow_listing=True, debug=False):
53
+ """
54
+ Create a MultiMapProxy with the given config directory.
55
+
56
+ :param config_dir: the directory with all project configurations.
57
+ :param allow_listing: True if MapProxy should list all instances
58
+ at the root URL
59
+ """
60
+ config_dir = os.path.abspath(config_dir)
61
+ loader = DirectoryConfLoader(config_dir)
62
+ return MultiMapProxy(loader, list_apps=allow_listing, debug=debug)
63
+
64
+
65
+ class MultiMapProxy(object):
66
+
67
+ def __init__(self, loader, list_apps=False, app_cache_size=100, debug=False):
68
+ self.loader = loader
69
+ self.list_apps = list_apps
70
+ self._app_init_lock = Lock()
71
+ self.apps = LRU(app_cache_size)
72
+ self.debug = debug
73
+
74
+ def __call__(self, environ, start_response):
75
+ req = Request(environ)
76
+ return self.handle(req)(environ, start_response)
77
+
78
+ def handle(self, req):
79
+ app_name = req.pop_path()
80
+ if not app_name:
81
+ return self.index_list(req)
82
+
83
+ if not app_name or (
84
+ app_name not in self.apps and not self.loader.app_available(app_name)
85
+ ):
86
+ return Response('not found', status=404)
87
+
88
+ # safe instance/app name for authorization
89
+ req.environ['mapproxy.instance_name'] = app_name
90
+ return self.proj_app(app_name)
91
+
92
+ def index_list(self, req):
93
+ """
94
+ Return greeting response with a list of available apps (if enabled with list_apps).
95
+ """
96
+ import mapproxy.version
97
+ html = "<html><body><h1>Welcome to MapProxy %s</h1>" % mapproxy.version.version
98
+
99
+ url = escape_html(req.script_url)
100
+ if self.list_apps:
101
+ html += "<h2>available instances:</h2><ul>"
102
+ html += '\n'.join('<li><a href="%(url)s/%(name)s/">%(name)s</a></li>' % {'url': url, 'name': app}
103
+ for app in self.loader.available_apps())
104
+ html += '</ul>'
105
+ html += '</body></html>'
106
+ return Response(html, content_type='text/html')
107
+
108
+ def proj_app(self, proj_name):
109
+ """
110
+ Return the (cached) project app.
111
+ """
112
+ proj_app, timestamps = self.apps.get(proj_name, (None, None))
113
+
114
+ if proj_app:
115
+ if self.loader.needs_reload(proj_name, timestamps):
116
+ # discard cached app
117
+ proj_app = None
118
+
119
+ if not proj_app:
120
+ with self._app_init_lock:
121
+ proj_app, timestamps = self.apps.get(proj_name, (None, None))
122
+ if self.loader.needs_reload(proj_name, timestamps):
123
+ proj_app, timestamps = self.create_app(proj_name)
124
+ self.apps[proj_name] = proj_app, timestamps
125
+ else:
126
+ proj_app, timestamps = self.apps[proj_name]
127
+
128
+ return proj_app
129
+
130
+ def create_app(self, proj_name):
131
+ """
132
+ Returns a new configured MapProxy app and a dict with the
133
+ timestamps of all configuration files.
134
+ """
135
+ mapproxy_conf = self.loader.app_conf(proj_name)['mapproxy_conf']
136
+ log.info('initializing project app %s with %s', proj_name, mapproxy_conf)
137
+ app = make_mapproxy_wsgi_app(mapproxy_conf, debug=self.debug)
138
+ return app, app.config_files
139
+
140
+
141
+ class ConfLoader(object):
142
+ def needs_reload(self, app_name, timestamps):
143
+ """
144
+ Returns ``True`` if the configuration of `app_name` changed
145
+ since `timestamp`.
146
+ """
147
+ raise NotImplementedError()
148
+
149
+ def app_available(self, app_name):
150
+ """
151
+ Returns ``True`` if `app_name` is available.
152
+ """
153
+ raise NotImplementedError()
154
+
155
+ def available_apps(self):
156
+ """
157
+ Returns a list with all available lists.
158
+ """
159
+ raise NotImplementedError()
160
+
161
+ def app_conf(self, app_name):
162
+ """
163
+ Returns a configuration dict for the given `app_name`,
164
+ None if the app is not found.
165
+
166
+ The configuration dict contains at least 'mapproxy_conf'
167
+ with the filename of the configuration.
168
+ """
169
+ raise NotImplementedError()
170
+
171
+
172
+ class DirectoryConfLoader(ConfLoader):
173
+ """
174
+ Load application configurations from a directory.
175
+ """
176
+
177
+ def __init__(self, base_dir, suffix='.yaml'):
178
+ self.base_dir = base_dir
179
+ self.suffix = suffix
180
+
181
+ def needs_reload(self, app_name, timestamps):
182
+ if not timestamps:
183
+ return True
184
+ for conf_file, timestamp in timestamps.items():
185
+ m_time = os.path.getmtime(conf_file)
186
+ if m_time > timestamp:
187
+ return True
188
+ return False
189
+
190
+ def _is_conf_file(self, fname):
191
+ if not os.path.isfile(fname):
192
+ return False
193
+ if self.suffix:
194
+ return fname.lower().endswith(self.suffix)
195
+ else:
196
+ return True
197
+
198
+ def app_name_from_filename(self, fname):
199
+ """
200
+ >>> DirectoryConfLoader('/tmp/').app_name_from_filename('/tmp/foobar.yaml')
201
+ 'foobar'
202
+ """
203
+ _path, fname = os.path.split(fname)
204
+ app_name, _ext = os.path.splitext(fname)
205
+ return app_name
206
+
207
+ def filename_from_app_name(self, app_name):
208
+ """
209
+ >>> DirectoryConfLoader('/tmp/').filename_from_app_name('foobar')
210
+ '/tmp/foobar.yaml'
211
+ """
212
+ return os.path.join(self.base_dir, app_name + self.suffix or '')
213
+
214
+ def available_apps(self):
215
+ apps = []
216
+ for f in os.listdir(self.base_dir):
217
+ if self._is_conf_file(os.path.join(self.base_dir, f)):
218
+ app_name = self.app_name_from_filename(f)
219
+ apps.append(app_name)
220
+ apps.sort()
221
+ return apps
222
+
223
+ def app_available(self, app_name):
224
+ conf_file = self.filename_from_app_name(app_name)
225
+ return self._is_conf_file(conf_file)
226
+
227
+ def app_conf(self, app_name):
228
+ conf_file = self.filename_from_app_name(app_name)
229
+ if not self._is_conf_file(conf_file):
230
+ return None
231
+ return {'mapproxy_conf': conf_file}