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
@@ -0,0 +1,259 @@
1
+ # This file is part of the MapProxy project.
2
+ # Copyright (C) 2011 Omniscale <http://omniscale.de>
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ from __future__ import absolute_import
17
+
18
+ import sys
19
+ import time
20
+ import threading
21
+ import multiprocessing
22
+
23
+ from mapproxy.grid import tile_grid
24
+ from mapproxy.image import ImageSource
25
+ from mapproxy.image.opts import ImageOptions
26
+ from mapproxy.layer import MapExtent, DefaultMapExtent, BlankImage, MapLayer
27
+ from mapproxy.source import SourceError
28
+ from mapproxy.client.log import log_request
29
+ from mapproxy.util.py import reraise_exception
30
+ from mapproxy.util.async_ import run_non_blocking
31
+ from mapproxy.compat import BytesIO
32
+
33
+ try:
34
+ import mapnik
35
+ mapnik
36
+ except ImportError:
37
+ try:
38
+ # for 2.0 alpha/rcs and first 2.0 release
39
+ import mapnik2 as mapnik
40
+ except ImportError:
41
+ mapnik = None
42
+
43
+ try:
44
+ import queue
45
+ Queue = queue.Queue
46
+ Empty = queue.Empty
47
+ Full = queue.Full
48
+ except ImportError: # in python2 it is called Queue
49
+ import Queue
50
+ Empty = Queue.Empty
51
+ Full = Queue.Full
52
+ MAX_UNUSED_MAPS=10
53
+
54
+ # fake 2.0 API for older versions
55
+ if mapnik and not hasattr(mapnik, 'Box2d'):
56
+ mapnik.Box2d = mapnik.Envelope
57
+
58
+ import logging
59
+ log = logging.getLogger(__name__)
60
+
61
+ class MapnikSource(MapLayer):
62
+ supports_meta_tiles = True
63
+ def __init__(self, mapfile, layers=None, image_opts=None, coverage=None,
64
+ res_range=None, lock=None, reuse_map_objects=False,
65
+ scale_factor=None, multithreaded=False):
66
+ MapLayer.__init__(self, image_opts=image_opts)
67
+ self.mapfile = mapfile
68
+ self.coverage = coverage
69
+ self.res_range = res_range
70
+ self.layers = set(layers) if layers else None
71
+ self.scale_factor = scale_factor
72
+ self.lock = lock
73
+ self.multithreaded = multithreaded
74
+ self._cache_map_obj = reuse_map_objects
75
+ if self.coverage:
76
+ self.extent = MapExtent(self.coverage.bbox, self.coverage.srs)
77
+ else:
78
+ self.extent = DefaultMapExtent()
79
+ if multithreaded:
80
+ # global objects allow caching over multiple instances within the same worker process
81
+ global _map_objs # mapnik map objects by cachekey
82
+ _map_objs = {}
83
+ global _map_objs_lock
84
+ _map_objs_lock = threading.Lock()
85
+ global _map_objs_queues # queues of unused mapnik map objects by PID and mapfile
86
+ _map_objs_queues = {}
87
+ else:
88
+ # instance variables guarantee separation of caches
89
+ self._map_objs = {}
90
+ self._map_objs_lock = threading.Lock()
91
+
92
+ def _map_cache(self):
93
+ """Get the cache for map objects.
94
+
95
+ Uses an instance variable for containment when multithreaded
96
+ operation is disabled.
97
+ """
98
+ if self.multithreaded:
99
+ return _map_objs
100
+ return self._map_objs
101
+ def _map_cache_lock(self):
102
+ """Get the cache-locks for map objects.
103
+
104
+ Uses an instance variable for containment when multithreaded
105
+ operation is disabled.
106
+ """
107
+ if self.multithreaded:
108
+ return _map_objs_lock
109
+ return self._map_objs_lock
110
+
111
+ def get_map(self, query):
112
+ if self.res_range and not self.res_range.contains(query.bbox, query.size,
113
+ query.srs):
114
+ raise BlankImage()
115
+ if self.coverage and not self.coverage.intersects(query.bbox, query.srs):
116
+ raise BlankImage()
117
+
118
+ try:
119
+ resp = self.render(query)
120
+ except RuntimeError as ex:
121
+ log.error('could not render Mapnik map: %s', ex)
122
+ reraise_exception(SourceError(ex.args[0]), sys.exc_info())
123
+ resp.opacity = self.opacity
124
+ return resp
125
+
126
+ def render(self, query):
127
+ mapfile = self.mapfile
128
+ if '%(webmercator_level)' in mapfile:
129
+ _bbox, level = tile_grid(3857).get_affected_bbox_and_level(
130
+ query.bbox, query.size, req_srs=query.srs)
131
+ mapfile = mapfile % {'webmercator_level': level}
132
+
133
+ if self.lock:
134
+ with self.lock():
135
+ return self.render_mapfile(mapfile, query)
136
+ else:
137
+ return self.render_mapfile(mapfile, query)
138
+
139
+ def _create_map_obj(self, mapfile, process_id):
140
+ m = mapnik.Map(0, 0)
141
+ mapnik.load_map(m, str(mapfile))
142
+ m.map_obj_pid = process_id
143
+ return m
144
+
145
+ def _get_map_obj(self, mapfile):
146
+ if not self.multithreaded:
147
+ return self._create_map_obj(mapfile, None)
148
+
149
+ process_id = multiprocessing.current_process()._identity
150
+ queue_cachekey = (process_id, mapfile)
151
+ if queue_cachekey in _map_objs_queues:
152
+ try:
153
+ m = _map_objs_queues[queue_cachekey].get_nowait()
154
+ # check explicitly for the process ID to ensure that
155
+ # map objects cannot move between processes
156
+ if m.map_object_pid == process_id:
157
+ return m
158
+ except Empty:
159
+ pass
160
+ return self._create_map_obj(mapfile, process_id)
161
+
162
+ def _put_unused_map_obj(self, mapfile, m):
163
+ process_id = multiprocessing.current_process()._identity
164
+ queue_cachekey = (process_id, mapfile)
165
+ if not queue_cachekey in _map_objs_queues:
166
+ _map_objs_queues[queue_cachekey] = Queue(MAX_UNUSED_MAPS)
167
+ try:
168
+ _map_objs_queues[queue_cachekey].put_nowait(m)
169
+ except Full:
170
+ # cleanup the data and drop the map, so it can be garbage collected
171
+ m.remove_all()
172
+ mapnik.clear_cache()
173
+
174
+ def _get_cachekey(self, mapfile):
175
+ if not self.multithreaded or self._cache_map_obj:
176
+ # all MapnikSources with the same mapfile share the same Mapnik Map.
177
+ return (None, None, mapfile)
178
+ thread_id = threading.current_thread().ident
179
+ process_id = multiprocessing.current_process()._identity
180
+ return (process_id, thread_id, mapfile)
181
+
182
+ def _cleanup_unused_cached_maps(self, mapfile):
183
+ if not self.multithreaded:
184
+ return
185
+ # clean up no longer used cached maps
186
+ process_id = multiprocessing.current_process()._identity
187
+ process_cache_keys = [k for k in self._map_cache().keys()
188
+ if k[0] == process_id]
189
+ # To avoid time-consuming cleanup whenever one thread in the
190
+ # threadpool finishes, allow ignoring up to 5 dead mapnik
191
+ # instances. (5 is empirical)
192
+ if len(process_cache_keys) > (5 + threading.active_count()):
193
+ active_thread_ids = set(i.ident for i in threading.enumerate())
194
+ for k in process_cache_keys:
195
+ with self._map_cache_lock():
196
+ if not k[1] in active_thread_ids and k in self._map_cache():
197
+ try:
198
+ m = self._map_cache()[k]
199
+ del self._map_cache()[k]
200
+ # put the map into the queue of unused
201
+ # maps so it can be re-used from another
202
+ # thread.
203
+ self._put_unused_map_obj(mapfile, m)
204
+ except KeyError:
205
+ continue
206
+
207
+ def map_obj(self, mapfile):
208
+ # cache loaded map objects
209
+ # only works when a single proc/thread accesses the map
210
+ # (forking the render process doesn't work because of open database
211
+ # file handles that gets passed to the child)
212
+ # segment the cache by process and thread to avoid interference
213
+ cachekey = self._get_cachekey(mapfile)
214
+ with self._map_cache_lock():
215
+ if cachekey not in self._map_cache():
216
+ self._map_cache()[cachekey] = self._get_map_obj(mapfile)
217
+ mapnik_map = self._map_cache()[cachekey]
218
+
219
+ self._cleanup_unused_cached_maps(mapfile)
220
+
221
+ return mapnik_map
222
+
223
+ def render_mapfile(self, mapfile, query):
224
+ return run_non_blocking(self._render_mapfile, (mapfile, query))
225
+
226
+ def _render_mapfile(self, mapfile, query):
227
+ start_time = time.time()
228
+
229
+ m = self.map_obj(mapfile)
230
+ m.resize(query.size[0], query.size[1])
231
+ m.srs = '+init=%s' % str(query.srs.srs_code.lower())
232
+ envelope = mapnik.Box2d(*query.bbox)
233
+ m.zoom_to_box(envelope)
234
+ data = None
235
+
236
+ try:
237
+ if self.layers:
238
+ i = 0
239
+ for layer in m.layers[:]:
240
+ if layer.name != 'Unknown' and layer.name not in self.layers:
241
+ del m.layers[i]
242
+ else:
243
+ i += 1
244
+
245
+ img = mapnik.Image(query.size[0], query.size[1])
246
+ if self.scale_factor:
247
+ mapnik.render(m, img, self.scale_factor)
248
+ else:
249
+ mapnik.render(m, img)
250
+ data = img.tostring(str(query.format))
251
+ finally:
252
+ size = None
253
+ if data:
254
+ size = len(data)
255
+ log_request('%s:%s:%s:%s' % (mapfile, query.bbox, query.srs.srs_code, query.size),
256
+ status='200' if data else '500', size=size, method='API', duration=time.time()-start_time)
257
+
258
+ return ImageSource(BytesIO(data), size=query.size,
259
+ image_opts=ImageOptions(format=query.format))
@@ -0,0 +1,96 @@
1
+ # This file is part of the MapProxy project.
2
+ # Copyright (C) 2010 Omniscale <http://omniscale.de>
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ """
17
+ Retrieve tiles from different tile servers (TMS/TileCache/etc.).
18
+ """
19
+
20
+ import sys
21
+ from mapproxy.image.opts import ImageOptions
22
+ from mapproxy.source import SourceError
23
+ from mapproxy.client.http import HTTPClientError
24
+ from mapproxy.source import InvalidSourceQuery
25
+ from mapproxy.layer import BlankImage, map_extent_from_grid, CacheMapLayer, MapLayer
26
+ from mapproxy.util.py import reraise_exception
27
+
28
+ import logging
29
+ log = logging.getLogger('mapproxy.source.tile')
30
+ log_config = logging.getLogger('mapproxy.config')
31
+
32
+ class TiledSource(MapLayer):
33
+ def __init__(self, grid, client, coverage=None, image_opts=None, error_handler=None,
34
+ res_range=None):
35
+ MapLayer.__init__(self, image_opts=image_opts)
36
+ self.grid = grid
37
+ self.client = client
38
+ self.image_opts = image_opts or ImageOptions()
39
+ self.coverage = coverage
40
+ self.extent = coverage.extent if coverage else map_extent_from_grid(grid)
41
+ self.res_range = res_range
42
+ self.error_handler = error_handler
43
+
44
+ def get_map(self, query):
45
+ if self.grid.tile_size != query.size:
46
+ ex = InvalidSourceQuery(
47
+ 'tile size of cache and tile source do not match: %s != %s'
48
+ % (self.grid.tile_size, query.size)
49
+ )
50
+ log_config.error(ex)
51
+ raise ex
52
+
53
+ if self.grid.srs != query.srs:
54
+ ex = InvalidSourceQuery(
55
+ 'SRS of cache and tile source do not match: %r != %r'
56
+ % (self.grid.srs, query.srs)
57
+ )
58
+ log_config.error(ex)
59
+ raise ex
60
+
61
+ if self.res_range and not self.res_range.contains(query.bbox, query.size,
62
+ query.srs):
63
+ raise BlankImage()
64
+ if self.coverage and not self.coverage.intersects(query.bbox, query.srs):
65
+ raise BlankImage()
66
+
67
+ _bbox, grid, tiles = self.grid.get_affected_tiles(query.bbox, query.size)
68
+
69
+ if grid != (1, 1):
70
+ raise InvalidSourceQuery('BBOX does not align to tile')
71
+
72
+ tile_coord = next(tiles)
73
+
74
+ try:
75
+ return self.client.get_tile(tile_coord, format=query.format)
76
+ except HTTPClientError as e:
77
+ if self.error_handler:
78
+ resp = self.error_handler.handle(e.response_code, query)
79
+ if resp:
80
+ return resp
81
+ log.warning('could not retrieve tile: %s', e)
82
+ reraise_exception(SourceError(e.args[0]), sys.exc_info())
83
+
84
+ class CacheSource(CacheMapLayer):
85
+ def __init__(self, tile_manager, extent=None, image_opts=None,
86
+ max_tile_limit=None, tiled_only=False):
87
+ CacheMapLayer.__init__(self, tile_manager, extent=extent, image_opts=image_opts,
88
+ max_tile_limit=max_tile_limit)
89
+ self.supports_meta_tiles = not tiled_only
90
+ self.tiled_only = tiled_only
91
+
92
+ def get_map(self, query):
93
+ if self.tiled_only:
94
+ query.tiled_only = True
95
+ return CacheMapLayer.get_map(self, query)
96
+
mapproxy/source/wms.py ADDED
@@ -0,0 +1,270 @@
1
+ # This file is part of the MapProxy project.
2
+ # Copyright (C) 2010 Omniscale <http://omniscale.de>
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ """
16
+ Retrieve maps/information from WMS servers.
17
+ """
18
+ import sys
19
+ from mapproxy.request.base import split_mime_type
20
+ from mapproxy.cache.legend import Legend, legend_identifier
21
+ from mapproxy.image import make_transparent, ImageSource, SubImageSource, bbox_position_in_image
22
+ from mapproxy.image.merge import concat_legends, concat_json_legends
23
+ from mapproxy.image.transform import ImageTransformer
24
+ from mapproxy.layer import MapExtent, DefaultMapExtent, BlankImage, LegendQuery, MapQuery, MapLayer
25
+ from mapproxy.source import InfoSource, SourceError, LegendSource
26
+ from mapproxy.client.http import HTTPClientError
27
+ from mapproxy.util.py import reraise_exception
28
+
29
+ import logging
30
+ log = logging.getLogger('mapproxy.source.wms')
31
+
32
+ class WMSSource(MapLayer):
33
+ supports_meta_tiles = True
34
+ def __init__(self, client, image_opts=None, coverage=None, res_range=None,
35
+ transparent_color=None, transparent_color_tolerance=None,
36
+ supported_srs=None, supported_formats=None, fwd_req_params=None,
37
+ error_handler=None):
38
+ MapLayer.__init__(self, image_opts=image_opts)
39
+ self.client = client
40
+ self.supported_srs = supported_srs or []
41
+ self.supported_formats = supported_formats or []
42
+ self.fwd_req_params = fwd_req_params or set()
43
+
44
+ self.transparent_color = transparent_color
45
+ self.transparent_color_tolerance = transparent_color_tolerance
46
+ if self.transparent_color:
47
+ self.image_opts.transparent = True
48
+ self.coverage = coverage
49
+ self.res_range = res_range
50
+ if self.coverage:
51
+ self.extent = MapExtent(self.coverage.bbox, self.coverage.srs)
52
+ else:
53
+ self.extent = DefaultMapExtent()
54
+ self.error_handler = error_handler
55
+
56
+ def is_opaque(self, query):
57
+ """
58
+ Returns true if we are sure that the image is not transparent.
59
+ """
60
+ if self.res_range and not self.res_range.contains(query.bbox, query.size,
61
+ query.srs):
62
+ return False
63
+
64
+ if self.image_opts.transparent:
65
+ return False
66
+
67
+ if self.opacity is not None and (0.0 < self.opacity < 0.99):
68
+ return False
69
+
70
+ if not self.coverage:
71
+ # not transparent and no coverage
72
+ return True
73
+
74
+ if self.coverage.contains(query.bbox, query.srs):
75
+ # not transparent and completely inside coverage
76
+ return True
77
+
78
+ return False
79
+
80
+ def get_map(self, query):
81
+ if self.res_range and not self.res_range.contains(query.bbox, query.size,
82
+ query.srs):
83
+ raise BlankImage()
84
+ if self.coverage and not self.coverage.intersects(query.bbox, query.srs):
85
+ raise BlankImage()
86
+ try:
87
+ resp = self._get_map(query)
88
+ if self.transparent_color:
89
+ resp = make_transparent(resp, self.transparent_color,
90
+ self.transparent_color_tolerance)
91
+ resp.opacity = self.opacity
92
+ return resp
93
+
94
+ except HTTPClientError as e:
95
+ if self.error_handler:
96
+ resp = self.error_handler.handle(e.response_code, query)
97
+ if resp:
98
+ return resp
99
+ log.warning('could not retrieve WMS map: %s', e.full_msg or e)
100
+ reraise_exception(SourceError(e.args[0]), sys.exc_info())
101
+
102
+ def _get_map(self, query):
103
+ format = self.image_opts.format
104
+ if not format:
105
+ format = query.format
106
+ if self.supported_formats and format not in self.supported_formats:
107
+ format = self.supported_formats[0]
108
+ if self.supported_srs:
109
+ # srs can be equal while still having a different srs_code (EPSG:3857/900913), make sure to use a supported srs_code
110
+ request_srs = None
111
+ for srs in self.supported_srs:
112
+ if query.srs == srs:
113
+ request_srs = srs
114
+ break
115
+ if request_srs is None:
116
+ return self._get_transformed(query, format)
117
+ if query.srs.srs_code != request_srs.srs_code:
118
+ query.srs = request_srs
119
+ if self.extent and not self.extent.contains(MapExtent(query.bbox, query.srs)):
120
+ return self._get_sub_query(query, format)
121
+ resp = self.client.retrieve(query, format)
122
+ return ImageSource(resp, size=query.size, image_opts=self.image_opts)
123
+
124
+ def _get_sub_query(self, query, format):
125
+ size, offset, bbox = bbox_position_in_image(query.bbox, query.size, self.extent.bbox_for(query.srs))
126
+ if size[0] == 0 or size[1] == 0:
127
+ raise BlankImage()
128
+ src_query = MapQuery(bbox, size, query.srs, format, dimensions=query.dimensions)
129
+ resp = self.client.retrieve(src_query, format)
130
+ return SubImageSource(resp, size=query.size, offset=offset, image_opts=self.image_opts)
131
+
132
+ def _get_transformed(self, query, format):
133
+ dst_srs = query.srs
134
+ src_srs = self.supported_srs.best_srs(dst_srs)
135
+ dst_bbox = query.bbox
136
+ src_bbox = dst_srs.transform_bbox_to(src_srs, dst_bbox)
137
+
138
+ src_width, src_height = src_bbox[2]-src_bbox[0], src_bbox[3]-src_bbox[1]
139
+ ratio = src_width/src_height
140
+ dst_size = query.size
141
+ xres, yres = src_width/dst_size[0], src_height/dst_size[1]
142
+ if xres < yres:
143
+ src_size = dst_size[0], int(dst_size[0]/ratio + 0.5)
144
+ else:
145
+ src_size = int(dst_size[1]*ratio +0.5), dst_size[1]
146
+
147
+ src_query = MapQuery(src_bbox, src_size, src_srs, format, dimensions=query.dimensions)
148
+
149
+ if self.coverage and not self.coverage.contains(src_bbox, src_srs):
150
+ img = self._get_sub_query(src_query, format)
151
+ else:
152
+ resp = self.client.retrieve(src_query, format)
153
+ img = ImageSource(resp, size=src_size, image_opts=self.image_opts)
154
+
155
+ img = ImageTransformer(src_srs, dst_srs).transform(img, src_bbox,
156
+ query.size, dst_bbox, self.image_opts)
157
+
158
+ img.format = format
159
+ return img
160
+
161
+ def _is_compatible(self, other, query):
162
+ if not isinstance(other, WMSSource):
163
+ return False
164
+
165
+ if self.opacity is not None or other.opacity is not None:
166
+ return False
167
+
168
+ if self.supported_srs != other.supported_srs:
169
+ return False
170
+
171
+ if self.supported_formats != other.supported_formats:
172
+ return False
173
+
174
+ if self.transparent_color != other.transparent_color:
175
+ return False
176
+
177
+ if self.transparent_color_tolerance != other.transparent_color_tolerance:
178
+ return False
179
+
180
+ if self.coverage != other.coverage:
181
+ return False
182
+
183
+
184
+ if (query.dimensions_for_params(self.fwd_req_params) !=
185
+ query.dimensions_for_params(other.fwd_req_params)):
186
+ return False
187
+
188
+ return True
189
+
190
+ def combined_layer(self, other, query):
191
+ if not self._is_compatible(other, query):
192
+ return None
193
+
194
+ client = self.client.combined_client(other.client, query)
195
+ if not client:
196
+ return None
197
+
198
+ return WMSSource(client, image_opts=self.image_opts,
199
+ transparent_color=self.transparent_color,
200
+ transparent_color_tolerance=self.transparent_color_tolerance,
201
+ supported_srs=self.supported_srs,
202
+ supported_formats=self.supported_formats,
203
+ res_range=None, # layer outside res_range should already be filtered out
204
+ coverage=self.coverage,
205
+ fwd_req_params=self.fwd_req_params,
206
+ )
207
+
208
+ class WMSInfoSource(InfoSource):
209
+ def __init__(self, client, fi_transformer=None, coverage=None):
210
+ self.client = client
211
+ self.fi_transformer = fi_transformer
212
+ self.coverage = coverage
213
+
214
+ def get_info(self, query):
215
+ if self.coverage and not self.coverage.contains(query.coord, query.srs):
216
+ return None
217
+ doc = self.client.get_info(query)
218
+ if self.fi_transformer:
219
+ doc = self.fi_transformer(doc)
220
+ return doc
221
+
222
+
223
+ class WMSLegendSource(LegendSource):
224
+ def __init__(self, clients, legend_cache, static=False):
225
+ self.clients = clients
226
+ self.identifier = legend_identifier([c.identifier for c in self.clients])
227
+ self._cache = legend_cache
228
+ self._size = None
229
+ self.static = static
230
+
231
+ @property
232
+ def size(self):
233
+ if not self._size:
234
+ legend = self.get_legend(LegendQuery(format='image/png', scale=None))
235
+ # TODO image size without as_image?
236
+ self._size = legend.as_image().size
237
+ return self._size
238
+
239
+ def get_legend(self, query):
240
+ if self.static:
241
+ # prevent caching of static legends for different scales
242
+ legend = Legend(id=self.identifier, scale=None)
243
+ else:
244
+ legend = Legend(id=self.identifier, scale=query.scale)
245
+
246
+ if not self._cache.load(legend) or query.format == 'json':
247
+ legends = []
248
+ error_occured = False
249
+ for client in self.clients:
250
+ try:
251
+ legends.append(client.get_legend(query))
252
+ except HTTPClientError as e:
253
+ error_occured = True
254
+ log.error(e.args[0])
255
+ except SourceError as e:
256
+ error_occured = True
257
+ # TODO errors?
258
+ log.error(e.args[0])
259
+ format = split_mime_type(query.format)[1]
260
+
261
+ if format == 'json':
262
+ return concat_json_legends(legends)
263
+
264
+ legend = Legend(source=concat_legends(legends, format=format),
265
+ id=self.identifier, scale=query.scale)
266
+ if not error_occured:
267
+ self._cache.store(legend)
268
+
269
+ # returns ImageSource
270
+ return legend.source