MapProxy 2.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (459) hide show
  1. MapProxy-2.1.0.dist-info/AUTHORS.txt +33 -0
  2. MapProxy-2.1.0.dist-info/COPYING.txt +60 -0
  3. MapProxy-2.1.0.dist-info/LICENSE.txt +202 -0
  4. MapProxy-2.1.0.dist-info/METADATA +165 -0
  5. MapProxy-2.1.0.dist-info/RECORD +459 -0
  6. MapProxy-2.1.0.dist-info/WHEEL +5 -0
  7. MapProxy-2.1.0.dist-info/entry_points.txt +4 -0
  8. MapProxy-2.1.0.dist-info/top_level.txt +1 -0
  9. mapproxy/__init__.py +0 -0
  10. mapproxy/cache/__init__.py +36 -0
  11. mapproxy/cache/azureblob.py +145 -0
  12. mapproxy/cache/base.py +120 -0
  13. mapproxy/cache/compact.py +665 -0
  14. mapproxy/cache/couchdb.py +301 -0
  15. mapproxy/cache/dummy.py +36 -0
  16. mapproxy/cache/file.py +200 -0
  17. mapproxy/cache/geopackage.py +647 -0
  18. mapproxy/cache/legend.py +87 -0
  19. mapproxy/cache/mbtiles.py +411 -0
  20. mapproxy/cache/meta.py +80 -0
  21. mapproxy/cache/path.py +261 -0
  22. mapproxy/cache/redis.py +152 -0
  23. mapproxy/cache/renderd.py +100 -0
  24. mapproxy/cache/riak.py +206 -0
  25. mapproxy/cache/s3.py +209 -0
  26. mapproxy/cache/tile.py +736 -0
  27. mapproxy/client/__init__.py +0 -0
  28. mapproxy/client/arcgis.py +82 -0
  29. mapproxy/client/cgi.py +141 -0
  30. mapproxy/client/http.py +295 -0
  31. mapproxy/client/log.py +33 -0
  32. mapproxy/client/tile.py +158 -0
  33. mapproxy/client/wms.py +255 -0
  34. mapproxy/compat/__init__.py +0 -0
  35. mapproxy/compat/image.py +86 -0
  36. mapproxy/config/__init__.py +22 -0
  37. mapproxy/config/config-schema.json +813 -0
  38. mapproxy/config/config.py +213 -0
  39. mapproxy/config/coverage.py +108 -0
  40. mapproxy/config/defaults.py +102 -0
  41. mapproxy/config/loader.py +2399 -0
  42. mapproxy/config/spec.py +657 -0
  43. mapproxy/config/validator.py +242 -0
  44. mapproxy/config_template/__init__.py +0 -0
  45. mapproxy/config_template/base_config/config.wsgi +10 -0
  46. mapproxy/config_template/base_config/full_example.yaml +598 -0
  47. mapproxy/config_template/base_config/full_seed_example.yaml +79 -0
  48. mapproxy/config_template/base_config/log.ini +35 -0
  49. mapproxy/config_template/base_config/mapproxy.yaml +60 -0
  50. mapproxy/config_template/base_config/seed.yaml +27 -0
  51. mapproxy/exception.py +149 -0
  52. mapproxy/featureinfo.py +251 -0
  53. mapproxy/grid.py +1199 -0
  54. mapproxy/image/__init__.py +549 -0
  55. mapproxy/image/fonts/DejaVuSans.ttf +0 -0
  56. mapproxy/image/fonts/DejaVuSansMono.ttf +0 -0
  57. mapproxy/image/fonts/LICENSE +99 -0
  58. mapproxy/image/fonts/__init__.py +0 -0
  59. mapproxy/image/mask.py +79 -0
  60. mapproxy/image/merge.py +323 -0
  61. mapproxy/image/message.py +357 -0
  62. mapproxy/image/opts.py +185 -0
  63. mapproxy/image/tile.py +171 -0
  64. mapproxy/image/transform.py +350 -0
  65. mapproxy/layer.py +489 -0
  66. mapproxy/multiapp.py +230 -0
  67. mapproxy/proj.py +309 -0
  68. mapproxy/request/__init__.py +18 -0
  69. mapproxy/request/arcgis.py +268 -0
  70. mapproxy/request/base.py +466 -0
  71. mapproxy/request/tile.py +131 -0
  72. mapproxy/request/wms/__init__.py +829 -0
  73. mapproxy/request/wms/exception.py +107 -0
  74. mapproxy/request/wmts.py +442 -0
  75. mapproxy/response.py +237 -0
  76. mapproxy/script/__init__.py +0 -0
  77. mapproxy/script/conf/__init__.py +0 -0
  78. mapproxy/script/conf/app.py +222 -0
  79. mapproxy/script/conf/caches.py +44 -0
  80. mapproxy/script/conf/geopackage.py +136 -0
  81. mapproxy/script/conf/layers.py +54 -0
  82. mapproxy/script/conf/seeds.py +36 -0
  83. mapproxy/script/conf/sources.py +88 -0
  84. mapproxy/script/conf/utils.py +148 -0
  85. mapproxy/script/defrag.py +187 -0
  86. mapproxy/script/export.py +337 -0
  87. mapproxy/script/grids.py +198 -0
  88. mapproxy/script/scales.py +134 -0
  89. mapproxy/script/util.py +410 -0
  90. mapproxy/script/wms_capabilities.py +160 -0
  91. mapproxy/seed/__init__.py +0 -0
  92. mapproxy/seed/cachelock.py +127 -0
  93. mapproxy/seed/cleanup.py +191 -0
  94. mapproxy/seed/config.py +481 -0
  95. mapproxy/seed/script.py +391 -0
  96. mapproxy/seed/seeder.py +551 -0
  97. mapproxy/seed/spec.py +66 -0
  98. mapproxy/seed/util.py +266 -0
  99. mapproxy/service/__init__.py +14 -0
  100. mapproxy/service/base.py +45 -0
  101. mapproxy/service/demo.py +364 -0
  102. mapproxy/service/kml.py +333 -0
  103. mapproxy/service/ows.py +39 -0
  104. mapproxy/service/template_helper.py +55 -0
  105. mapproxy/service/templates/demo/capabilities_demo.html +18 -0
  106. mapproxy/service/templates/demo/demo.html +181 -0
  107. mapproxy/service/templates/demo/openlayers-demo.cfg +16 -0
  108. mapproxy/service/templates/demo/static/img/blank.gif +0 -0
  109. mapproxy/service/templates/demo/static/img/east-mini.png +0 -0
  110. mapproxy/service/templates/demo/static/img/north-mini.png +0 -0
  111. mapproxy/service/templates/demo/static/img/south-mini.png +0 -0
  112. mapproxy/service/templates/demo/static/img/west-mini.png +0 -0
  113. mapproxy/service/templates/demo/static/img/zoom-minus-mini.png +0 -0
  114. mapproxy/service/templates/demo/static/img/zoom-plus-mini.png +0 -0
  115. mapproxy/service/templates/demo/static/img/zoom-world-mini.png +0 -0
  116. mapproxy/service/templates/demo/static/logo.png +0 -0
  117. mapproxy/service/templates/demo/static/ol.css +345 -0
  118. mapproxy/service/templates/demo/static/ol.js +4 -0
  119. mapproxy/service/templates/demo/static/proj4.min.js +1 -0
  120. mapproxy/service/templates/demo/static/proj4defs.js +1 -0
  121. mapproxy/service/templates/demo/static/site.css +137 -0
  122. mapproxy/service/templates/demo/static/theme/default/framedCloud.css +0 -0
  123. mapproxy/service/templates/demo/static/theme/default/google.css +17 -0
  124. mapproxy/service/templates/demo/static/theme/default/ie6-style.css +10 -0
  125. mapproxy/service/templates/demo/static/theme/default/style.css +482 -0
  126. mapproxy/service/templates/demo/static.html +34 -0
  127. mapproxy/service/templates/demo/tms_demo.html +117 -0
  128. mapproxy/service/templates/demo/wms_demo.html +144 -0
  129. mapproxy/service/templates/demo/wmts_demo.html +118 -0
  130. mapproxy/service/templates/tms_capabilities.xml +13 -0
  131. mapproxy/service/templates/tms_exception.xml +4 -0
  132. mapproxy/service/templates/tms_root_resource.xml +7 -0
  133. mapproxy/service/templates/tms_tilemap_capabilities.xml +14 -0
  134. mapproxy/service/templates/wms100capabilities.xml +112 -0
  135. mapproxy/service/templates/wms100exception.xml +4 -0
  136. mapproxy/service/templates/wms110capabilities.xml +152 -0
  137. mapproxy/service/templates/wms110exception.xml +5 -0
  138. mapproxy/service/templates/wms111capabilities.xml +183 -0
  139. mapproxy/service/templates/wms111exception.xml +5 -0
  140. mapproxy/service/templates/wms130capabilities.xml +326 -0
  141. mapproxy/service/templates/wms130exception.xml +8 -0
  142. mapproxy/service/templates/wmts100capabilities.xml +155 -0
  143. mapproxy/service/templates/wmts100exception.xml +9 -0
  144. mapproxy/service/tile.py +540 -0
  145. mapproxy/service/wms.py +868 -0
  146. mapproxy/service/wmts.py +387 -0
  147. mapproxy/source/__init__.py +83 -0
  148. mapproxy/source/arcgis.py +39 -0
  149. mapproxy/source/error.py +40 -0
  150. mapproxy/source/mapnik.py +262 -0
  151. mapproxy/source/tile.py +97 -0
  152. mapproxy/source/wms.py +273 -0
  153. mapproxy/srs.py +734 -0
  154. mapproxy/template.py +54 -0
  155. mapproxy/test/__init__.py +0 -0
  156. mapproxy/test/conftest.py +8 -0
  157. mapproxy/test/helper.py +255 -0
  158. mapproxy/test/http.py +511 -0
  159. mapproxy/test/image.py +219 -0
  160. mapproxy/test/mocker.py +2291 -0
  161. mapproxy/test/schemas/inspire/common/1.0/common.xsd +1461 -0
  162. mapproxy/test/schemas/inspire/common/1.0/enums/enum_bul.xsd +108 -0
  163. mapproxy/test/schemas/inspire/common/1.0/enums/enum_cze.xsd +108 -0
  164. mapproxy/test/schemas/inspire/common/1.0/enums/enum_dan.xsd +108 -0
  165. mapproxy/test/schemas/inspire/common/1.0/enums/enum_dut.xsd +108 -0
  166. mapproxy/test/schemas/inspire/common/1.0/enums/enum_eng.xsd +155 -0
  167. mapproxy/test/schemas/inspire/common/1.0/enums/enum_est.xsd +108 -0
  168. mapproxy/test/schemas/inspire/common/1.0/enums/enum_fin.xsd +108 -0
  169. mapproxy/test/schemas/inspire/common/1.0/enums/enum_fre.xsd +108 -0
  170. mapproxy/test/schemas/inspire/common/1.0/enums/enum_ger.xsd +108 -0
  171. mapproxy/test/schemas/inspire/common/1.0/enums/enum_gle.xsd +109 -0
  172. mapproxy/test/schemas/inspire/common/1.0/enums/enum_gre.xsd +108 -0
  173. mapproxy/test/schemas/inspire/common/1.0/enums/enum_hun.xsd +108 -0
  174. mapproxy/test/schemas/inspire/common/1.0/enums/enum_ita.xsd +108 -0
  175. mapproxy/test/schemas/inspire/common/1.0/enums/enum_lav.xsd +108 -0
  176. mapproxy/test/schemas/inspire/common/1.0/enums/enum_lit.xsd +108 -0
  177. mapproxy/test/schemas/inspire/common/1.0/enums/enum_mlt.xsd +108 -0
  178. mapproxy/test/schemas/inspire/common/1.0/enums/enum_pol.xsd +108 -0
  179. mapproxy/test/schemas/inspire/common/1.0/enums/enum_por.xsd +108 -0
  180. mapproxy/test/schemas/inspire/common/1.0/enums/enum_rum.xsd +108 -0
  181. mapproxy/test/schemas/inspire/common/1.0/enums/enum_slo.xsd +108 -0
  182. mapproxy/test/schemas/inspire/common/1.0/enums/enum_slv.xsd +108 -0
  183. mapproxy/test/schemas/inspire/common/1.0/enums/enum_spa.xsd +108 -0
  184. mapproxy/test/schemas/inspire/common/1.0/enums/enum_swe.xsd +108 -0
  185. mapproxy/test/schemas/inspire/common/1.0/network.xsd +521 -0
  186. mapproxy/test/schemas/inspire/inspire_vs/1.0/inspire_vs.xsd +19 -0
  187. mapproxy/test/schemas/kml/2.2.0/ReadMe.txt +14 -0
  188. mapproxy/test/schemas/kml/2.2.0/atom-author-link.xsd +66 -0
  189. mapproxy/test/schemas/kml/2.2.0/ogckml22.xsd +1646 -0
  190. mapproxy/test/schemas/kml/2.2.0/xAL.xsd +1680 -0
  191. mapproxy/test/schemas/ows/1.1.0/ReadMe.txt +87 -0
  192. mapproxy/test/schemas/ows/1.1.0/ows19115subset.xsd +235 -0
  193. mapproxy/test/schemas/ows/1.1.0/owsAll.xsd +23 -0
  194. mapproxy/test/schemas/ows/1.1.0/owsCommon.xsd +157 -0
  195. mapproxy/test/schemas/ows/1.1.0/owsContents.xsd +86 -0
  196. mapproxy/test/schemas/ows/1.1.0/owsDataIdentification.xsd +127 -0
  197. mapproxy/test/schemas/ows/1.1.0/owsDomainType.xsd +279 -0
  198. mapproxy/test/schemas/ows/1.1.0/owsExceptionReport.xsd +76 -0
  199. mapproxy/test/schemas/ows/1.1.0/owsGetCapabilities.xsd +112 -0
  200. mapproxy/test/schemas/ows/1.1.0/owsGetResourceByID.xsd +51 -0
  201. mapproxy/test/schemas/ows/1.1.0/owsInputOutputData.xsd +59 -0
  202. mapproxy/test/schemas/ows/1.1.0/owsManifest.xsd +125 -0
  203. mapproxy/test/schemas/ows/1.1.0/owsOperationsMetadata.xsd +140 -0
  204. mapproxy/test/schemas/ows/1.1.0/owsServiceIdentification.xsd +60 -0
  205. mapproxy/test/schemas/ows/1.1.0/owsServiceProvider.xsd +47 -0
  206. mapproxy/test/schemas/sld/1.1.0/sld_capabilities.xsd +27 -0
  207. mapproxy/test/schemas/wms/1.0.0/capabilities_1_0_0.dtd +353 -0
  208. mapproxy/test/schemas/wms/1.0.0/capabilities_1_0_0.xml +188 -0
  209. mapproxy/test/schemas/wms/1.0.7/capabilities_1_0_7.dtd +524 -0
  210. mapproxy/test/schemas/wms/1.0.7/capabilities_1_0_7.xml +260 -0
  211. mapproxy/test/schemas/wms/1.1.0/capabilities_1_1_0.dtd +273 -0
  212. mapproxy/test/schemas/wms/1.1.0/capabilities_1_1_0.xml +303 -0
  213. mapproxy/test/schemas/wms/1.1.0/exception_1_1_0.dtd +6 -0
  214. mapproxy/test/schemas/wms/1.1.0/exception_1_1_0.xml +33 -0
  215. mapproxy/test/schemas/wms/1.1.1/OGC-exception.xsd +68 -0
  216. mapproxy/test/schemas/wms/1.1.1/WMS_DescribeLayerResponse.dtd +22 -0
  217. mapproxy/test/schemas/wms/1.1.1/WMS_MS_Capabilities.dtd +274 -0
  218. mapproxy/test/schemas/wms/1.1.1/WMS_exception_1_1_1.dtd +5 -0
  219. mapproxy/test/schemas/wms/1.1.1/capabilities_1_1_1.dtd +276 -0
  220. mapproxy/test/schemas/wms/1.1.1/capabilities_1_1_1.xml +303 -0
  221. mapproxy/test/schemas/wms/1.1.1/exception_1_1_1.dtd +6 -0
  222. mapproxy/test/schemas/wms/1.1.1/exception_1_1_1.xml +33 -0
  223. mapproxy/test/schemas/wms/1.3.0/ReadMe.txt +8 -0
  224. mapproxy/test/schemas/wms/1.3.0/capabilities_1_3_0.xml +277 -0
  225. mapproxy/test/schemas/wms/1.3.0/capabilities_1_3_0.xsd +611 -0
  226. mapproxy/test/schemas/wms/1.3.0/exceptions_1_3_0.xml +34 -0
  227. mapproxy/test/schemas/wms/1.3.0/exceptions_1_3_0.xsd +28 -0
  228. mapproxy/test/schemas/wmsc/1.1.1/OGC-exception.xsd +68 -0
  229. mapproxy/test/schemas/wmsc/1.1.1/WMS_DescribeLayerResponse.dtd +22 -0
  230. mapproxy/test/schemas/wmsc/1.1.1/WMS_MS_Capabilities.dtd +283 -0
  231. mapproxy/test/schemas/wmsc/1.1.1/WMS_exception_1_1_1.dtd +5 -0
  232. mapproxy/test/schemas/wmsc/1.1.1/capabilities_1_1_1.dtd +276 -0
  233. mapproxy/test/schemas/wmsc/1.1.1/capabilities_1_1_1.xml +303 -0
  234. mapproxy/test/schemas/wmsc/1.1.1/exception_1_1_1.dtd +6 -0
  235. mapproxy/test/schemas/wmsc/1.1.1/exception_1_1_1.xml +33 -0
  236. mapproxy/test/schemas/wmts/1.0/ReadMe.txt +32 -0
  237. mapproxy/test/schemas/wmts/1.0/wmts.xsd +28 -0
  238. mapproxy/test/schemas/wmts/1.0/wmtsAbstract.wsdl +151 -0
  239. mapproxy/test/schemas/wmts/1.0/wmtsGetCapabilities_request.xsd +38 -0
  240. mapproxy/test/schemas/wmts/1.0/wmtsGetCapabilities_response.xsd +564 -0
  241. mapproxy/test/schemas/wmts/1.0/wmtsGetFeatureInfo_request.xsd +57 -0
  242. mapproxy/test/schemas/wmts/1.0/wmtsGetFeatureInfo_response.xsd +72 -0
  243. mapproxy/test/schemas/wmts/1.0/wmtsGetTile_request.xsd +91 -0
  244. mapproxy/test/schemas/wmts/1.0/wmtsKVP.xsd +76 -0
  245. mapproxy/test/schemas/wmts/1.0/wmtsPayload_response.xsd +70 -0
  246. mapproxy/test/schemas/xlink/1.0.0/ReadMe.txt +6 -0
  247. mapproxy/test/schemas/xlink/1.0.0/xlinks.xsd +122 -0
  248. mapproxy/test/schemas/xml.xsd +287 -0
  249. mapproxy/test/system/__init__.py +98 -0
  250. mapproxy/test/system/fixture/arcgis.yaml +57 -0
  251. mapproxy/test/system/fixture/auth.yaml +70 -0
  252. mapproxy/test/system/fixture/cache.mbtiles +0 -0
  253. mapproxy/test/system/fixture/cache_azureblob.yaml +59 -0
  254. mapproxy/test/system/fixture/cache_band_merge.yaml +73 -0
  255. mapproxy/test/system/fixture/cache_bulk_meta_tiles.yaml +24 -0
  256. mapproxy/test/system/fixture/cache_coverage.yaml +84 -0
  257. mapproxy/test/system/fixture/cache_data/dop_cache_EPSG3857/00/000/000/000/000/000/000.png +0 -0
  258. mapproxy/test/system/fixture/cache_data/wms_cache_EPSG900913/01/000/000/000/000/000/001.jpeg +0 -0
  259. mapproxy/test/system/fixture/cache_data/wms_cache_transparent_EPSG900913/01/000/000/000/000/000/001.png +0 -0
  260. mapproxy/test/system/fixture/cache_geopackage.yaml +56 -0
  261. mapproxy/test/system/fixture/cache_grid_names.yaml +50 -0
  262. mapproxy/test/system/fixture/cache_mbtiles.yaml +28 -0
  263. mapproxy/test/system/fixture/cache_s3.yaml +58 -0
  264. mapproxy/test/system/fixture/cache_source.yaml +81 -0
  265. mapproxy/test/system/fixture/combined_sources.yaml +130 -0
  266. mapproxy/test/system/fixture/coverage.yaml +77 -0
  267. mapproxy/test/system/fixture/demo.yaml +135 -0
  268. mapproxy/test/system/fixture/dimension.yaml +59 -0
  269. mapproxy/test/system/fixture/disable_storage.yaml +25 -0
  270. mapproxy/test/system/fixture/empty_ogrdata.geojson +1 -0
  271. mapproxy/test/system/fixture/formats.yaml +72 -0
  272. mapproxy/test/system/fixture/inspire.yaml +101 -0
  273. mapproxy/test/system/fixture/inspire_full.yaml +124 -0
  274. mapproxy/test/system/fixture/kml_layer.yaml +66 -0
  275. mapproxy/test/system/fixture/layer.yaml +260 -0
  276. mapproxy/test/system/fixture/layergroups.yaml +57 -0
  277. mapproxy/test/system/fixture/layergroups_root.yaml +106 -0
  278. mapproxy/test/system/fixture/legendgraphic.yaml +93 -0
  279. mapproxy/test/system/fixture/mapnik_source.yaml +66 -0
  280. mapproxy/test/system/fixture/mapproxy_export.yaml +12 -0
  281. mapproxy/test/system/fixture/mapserver.yaml +23 -0
  282. mapproxy/test/system/fixture/minimal_cgi.py +16 -0
  283. mapproxy/test/system/fixture/mixed_mode.yaml +49 -0
  284. mapproxy/test/system/fixture/multi_cache_layers.yaml +111 -0
  285. mapproxy/test/system/fixture/multiapp1.yaml +20 -0
  286. mapproxy/test/system/fixture/multiapp2.yaml +19 -0
  287. mapproxy/test/system/fixture/renderd_client.yaml +55 -0
  288. mapproxy/test/system/fixture/scalehints.yaml +70 -0
  289. mapproxy/test/system/fixture/seed.yaml +94 -0
  290. mapproxy/test/system/fixture/seed_mapproxy.yaml +39 -0
  291. mapproxy/test/system/fixture/seed_old.yaml +12 -0
  292. mapproxy/test/system/fixture/seed_timeouts.yaml +12 -0
  293. mapproxy/test/system/fixture/seed_timeouts_mapproxy.yaml +27 -0
  294. mapproxy/test/system/fixture/seedonly.yaml +51 -0
  295. mapproxy/test/system/fixture/sld.yaml +35 -0
  296. mapproxy/test/system/fixture/source_errors.yaml +84 -0
  297. mapproxy/test/system/fixture/source_errors_raise.yaml +82 -0
  298. mapproxy/test/system/fixture/tileservice_origin.yaml +26 -0
  299. mapproxy/test/system/fixture/tileservice_refresh.yaml +59 -0
  300. mapproxy/test/system/fixture/tilesource_minmax_res.yaml +22 -0
  301. mapproxy/test/system/fixture/util-conf-base-grids.yaml +5 -0
  302. mapproxy/test/system/fixture/util-conf-overwrite.yaml +13 -0
  303. mapproxy/test/system/fixture/util-conf-wms-111-cap.xml +90 -0
  304. mapproxy/test/system/fixture/util_grids.yaml +29 -0
  305. mapproxy/test/system/fixture/util_wms_capabilities111.xml +130 -0
  306. mapproxy/test/system/fixture/util_wms_capabilities130.xml +100 -0
  307. mapproxy/test/system/fixture/util_wms_capabilities_service_exception.xml +5 -0
  308. mapproxy/test/system/fixture/watermark.yaml +50 -0
  309. mapproxy/test/system/fixture/wms_srs_extent.yaml +39 -0
  310. mapproxy/test/system/fixture/wms_versions.yaml +38 -0
  311. mapproxy/test/system/fixture/wmts.yaml +134 -0
  312. mapproxy/test/system/fixture/wmts_dimensions.yaml +57 -0
  313. mapproxy/test/system/fixture/xslt_featureinfo.yaml +54 -0
  314. mapproxy/test/system/fixture/xslt_featureinfo_input.yaml +51 -0
  315. mapproxy/test/system/test_arcgis.py +156 -0
  316. mapproxy/test/system/test_auth.py +1133 -0
  317. mapproxy/test/system/test_behind_proxy.py +75 -0
  318. mapproxy/test/system/test_bulk_meta_tiles.py +106 -0
  319. mapproxy/test/system/test_cache_azureblob.py +127 -0
  320. mapproxy/test/system/test_cache_band_merge.py +103 -0
  321. mapproxy/test/system/test_cache_coverage.py +168 -0
  322. mapproxy/test/system/test_cache_geopackage.py +144 -0
  323. mapproxy/test/system/test_cache_grid_names.py +89 -0
  324. mapproxy/test/system/test_cache_mbtiles.py +85 -0
  325. mapproxy/test/system/test_cache_s3.py +115 -0
  326. mapproxy/test/system/test_cache_source.py +146 -0
  327. mapproxy/test/system/test_combined_sources.py +335 -0
  328. mapproxy/test/system/test_coverage.py +140 -0
  329. mapproxy/test/system/test_decorate_img.py +214 -0
  330. mapproxy/test/system/test_demo.py +56 -0
  331. mapproxy/test/system/test_demo_with_extra_service.py +57 -0
  332. mapproxy/test/system/test_dimensions.py +279 -0
  333. mapproxy/test/system/test_disable_storage.py +42 -0
  334. mapproxy/test/system/test_formats.py +219 -0
  335. mapproxy/test/system/test_inspire_vs.py +173 -0
  336. mapproxy/test/system/test_kml.py +264 -0
  337. mapproxy/test/system/test_layergroups.py +160 -0
  338. mapproxy/test/system/test_legendgraphic.py +308 -0
  339. mapproxy/test/system/test_mapnik.py +162 -0
  340. mapproxy/test/system/test_mapserver.py +83 -0
  341. mapproxy/test/system/test_mixed_mode_format.py +201 -0
  342. mapproxy/test/system/test_multi_cache_layers.py +169 -0
  343. mapproxy/test/system/test_multiapp.py +92 -0
  344. mapproxy/test/system/test_refresh.py +206 -0
  345. mapproxy/test/system/test_renderd_client.py +304 -0
  346. mapproxy/test/system/test_response_headers.py +54 -0
  347. mapproxy/test/system/test_scalehints.py +140 -0
  348. mapproxy/test/system/test_seed.py +425 -0
  349. mapproxy/test/system/test_seed_only.py +93 -0
  350. mapproxy/test/system/test_sld.py +120 -0
  351. mapproxy/test/system/test_source_errors.py +377 -0
  352. mapproxy/test/system/test_tilesource_minmax_res.py +54 -0
  353. mapproxy/test/system/test_tms.py +277 -0
  354. mapproxy/test/system/test_tms_origin.py +46 -0
  355. mapproxy/test/system/test_util_conf.py +434 -0
  356. mapproxy/test/system/test_util_export.py +210 -0
  357. mapproxy/test/system/test_util_grids.py +88 -0
  358. mapproxy/test/system/test_util_wms_capabilities.py +182 -0
  359. mapproxy/test/system/test_watermark.py +91 -0
  360. mapproxy/test/system/test_wms.py +1616 -0
  361. mapproxy/test/system/test_wms_srs_extent.py +165 -0
  362. mapproxy/test/system/test_wms_version.py +85 -0
  363. mapproxy/test/system/test_wmsc.py +116 -0
  364. mapproxy/test/system/test_wmts.py +334 -0
  365. mapproxy/test/system/test_wmts_dimensions.py +206 -0
  366. mapproxy/test/system/test_wmts_restful.py +198 -0
  367. mapproxy/test/system/test_xslt_featureinfo.py +423 -0
  368. mapproxy/test/test_http_helper.py +217 -0
  369. mapproxy/test/unit/__init__.py +0 -0
  370. mapproxy/test/unit/epsg +2 -0
  371. mapproxy/test/unit/polygons/polygons.dbf +0 -0
  372. mapproxy/test/unit/polygons/polygons.shp +0 -0
  373. mapproxy/test/unit/polygons/polygons.shx +0 -0
  374. mapproxy/test/unit/test_async.py +242 -0
  375. mapproxy/test/unit/test_auth.py +430 -0
  376. mapproxy/test/unit/test_cache.py +1356 -0
  377. mapproxy/test/unit/test_cache_azureblob.py +97 -0
  378. mapproxy/test/unit/test_cache_compact.py +324 -0
  379. mapproxy/test/unit/test_cache_couchdb.py +118 -0
  380. mapproxy/test/unit/test_cache_geopackage.py +256 -0
  381. mapproxy/test/unit/test_cache_redis.py +123 -0
  382. mapproxy/test/unit/test_cache_riak.py +80 -0
  383. mapproxy/test/unit/test_cache_s3.py +93 -0
  384. mapproxy/test/unit/test_cache_tile.py +477 -0
  385. mapproxy/test/unit/test_client.py +488 -0
  386. mapproxy/test/unit/test_client_arcgis.py +74 -0
  387. mapproxy/test/unit/test_client_cgi.py +140 -0
  388. mapproxy/test/unit/test_collections.py +116 -0
  389. mapproxy/test/unit/test_concat_legends.py +37 -0
  390. mapproxy/test/unit/test_conf_loader.py +1267 -0
  391. mapproxy/test/unit/test_conf_validator.py +427 -0
  392. mapproxy/test/unit/test_config.py +118 -0
  393. mapproxy/test/unit/test_decorate_img.py +185 -0
  394. mapproxy/test/unit/test_exceptions.py +270 -0
  395. mapproxy/test/unit/test_featureinfo.py +313 -0
  396. mapproxy/test/unit/test_file_lock_load.py +49 -0
  397. mapproxy/test/unit/test_geom.py +512 -0
  398. mapproxy/test/unit/test_grid.py +1279 -0
  399. mapproxy/test/unit/test_image.py +1051 -0
  400. mapproxy/test/unit/test_image_mask.py +181 -0
  401. mapproxy/test/unit/test_image_messages.py +209 -0
  402. mapproxy/test/unit/test_image_options.py +160 -0
  403. mapproxy/test/unit/test_isodate.py +118 -0
  404. mapproxy/test/unit/test_multiapp.py +163 -0
  405. mapproxy/test/unit/test_ogr_reader.py +51 -0
  406. mapproxy/test/unit/test_request.py +745 -0
  407. mapproxy/test/unit/test_request_wmts.py +178 -0
  408. mapproxy/test/unit/test_response.py +78 -0
  409. mapproxy/test/unit/test_seed.py +365 -0
  410. mapproxy/test/unit/test_seed_cachelock.py +91 -0
  411. mapproxy/test/unit/test_srs.py +215 -0
  412. mapproxy/test/unit/test_tiled_source.py +122 -0
  413. mapproxy/test/unit/test_tilefilter.py +31 -0
  414. mapproxy/test/unit/test_times.py +25 -0
  415. mapproxy/test/unit/test_timeutils.py +50 -0
  416. mapproxy/test/unit/test_util_conf_utils.py +75 -0
  417. mapproxy/test/unit/test_utils.py +476 -0
  418. mapproxy/test/unit/test_wms_capabilities.py +44 -0
  419. mapproxy/test/unit/test_wms_layer.py +113 -0
  420. mapproxy/test/unit/test_yaml.py +68 -0
  421. mapproxy/tilefilter.py +61 -0
  422. mapproxy/util/__init__.py +0 -0
  423. mapproxy/util/async_.py +229 -0
  424. mapproxy/util/collections.py +134 -0
  425. mapproxy/util/coverage.py +337 -0
  426. mapproxy/util/ext/__init__.py +14 -0
  427. mapproxy/util/ext/dictspec/__init__.py +1 -0
  428. mapproxy/util/ext/dictspec/spec.py +131 -0
  429. mapproxy/util/ext/dictspec/test/__init__.py +0 -0
  430. mapproxy/util/ext/dictspec/test/test_validator.py +278 -0
  431. mapproxy/util/ext/dictspec/validator.py +194 -0
  432. mapproxy/util/ext/local.py +198 -0
  433. mapproxy/util/ext/lockfile.py +140 -0
  434. mapproxy/util/ext/odict.py +321 -0
  435. mapproxy/util/ext/serving.py +491 -0
  436. mapproxy/util/ext/tempita/__init__.py +1093 -0
  437. mapproxy/util/ext/tempita/_looper.py +163 -0
  438. mapproxy/util/ext/tempita/string_utils.py +24 -0
  439. mapproxy/util/ext/wmsparse/__init__.py +3 -0
  440. mapproxy/util/ext/wmsparse/duration.py +600 -0
  441. mapproxy/util/ext/wmsparse/parse.py +307 -0
  442. mapproxy/util/ext/wmsparse/test/__init__.py +0 -0
  443. mapproxy/util/ext/wmsparse/test/test_parse.py +111 -0
  444. mapproxy/util/ext/wmsparse/test/test_util.py +23 -0
  445. mapproxy/util/ext/wmsparse/test/wms-example-111.xml +90 -0
  446. mapproxy/util/ext/wmsparse/test/wms-example-130.xml +120 -0
  447. mapproxy/util/ext/wmsparse/test/wms-large-111.xml +2114 -0
  448. mapproxy/util/ext/wmsparse/test/wms_nasa_cap.xml +386 -0
  449. mapproxy/util/ext/wmsparse/util.py +189 -0
  450. mapproxy/util/fs.py +164 -0
  451. mapproxy/util/geom.py +307 -0
  452. mapproxy/util/lib.py +117 -0
  453. mapproxy/util/lock.py +171 -0
  454. mapproxy/util/ogr.py +247 -0
  455. mapproxy/util/py.py +75 -0
  456. mapproxy/util/times.py +78 -0
  457. mapproxy/util/yaml.py +58 -0
  458. mapproxy/version.py +33 -0
  459. mapproxy/wsgiapp.py +167 -0
@@ -0,0 +1,262 @@
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
+ import logging
18
+
19
+ import sys
20
+ import time
21
+ import threading
22
+ import multiprocessing
23
+ from io import BytesIO
24
+
25
+ from mapproxy.grid import tile_grid
26
+ from mapproxy.image import ImageSource
27
+ from mapproxy.image.opts import ImageOptions
28
+ from mapproxy.layer import MapExtent, DefaultMapExtent, BlankImage, MapLayer
29
+ from mapproxy.source import SourceError
30
+ from mapproxy.client.log import log_request
31
+ from mapproxy.util.py import reraise_exception
32
+ from mapproxy.util.async_ import run_non_blocking
33
+
34
+ try:
35
+ import mapnik
36
+ mapnik
37
+ except ImportError:
38
+ try:
39
+ # for 2.0 alpha/rcs and first 2.0 release
40
+ import mapnik2 as mapnik
41
+ except ImportError:
42
+ mapnik = None
43
+
44
+ try:
45
+ import queue
46
+ Queue = queue.Queue
47
+ Empty = queue.Empty
48
+ Full = queue.Full
49
+ except ImportError: # in python2 it is called Queue
50
+ import Queue
51
+ Empty = Queue.Empty
52
+ Full = Queue.Full
53
+ MAX_UNUSED_MAPS = 10
54
+
55
+ # fake 2.0 API for older versions
56
+ if mapnik and not hasattr(mapnik, 'Box2d'):
57
+ mapnik.Box2d = mapnik.Envelope
58
+
59
+ log = logging.getLogger(__package__)
60
+
61
+
62
+ class MapnikSource(MapLayer):
63
+ supports_meta_tiles = True
64
+
65
+ def __init__(self, mapfile, layers=None, image_opts=None, coverage=None,
66
+ res_range=None, lock=None, reuse_map_objects=False,
67
+ scale_factor=None, multithreaded=False):
68
+ MapLayer.__init__(self, image_opts=image_opts)
69
+ self.mapfile = mapfile
70
+ self.coverage = coverage
71
+ self.res_range = res_range
72
+ self.layers = set(layers) if layers else None
73
+ self.scale_factor = scale_factor
74
+ self.lock = lock
75
+ self.multithreaded = multithreaded
76
+ self._cache_map_obj = reuse_map_objects
77
+ if self.coverage:
78
+ self.extent = MapExtent(self.coverage.bbox, self.coverage.srs)
79
+ else:
80
+ self.extent = DefaultMapExtent()
81
+ if multithreaded:
82
+ # global objects allow caching over multiple instances within the same worker process
83
+ global _map_objs # mapnik map objects by cachekey
84
+ _map_objs = {}
85
+ global _map_objs_lock
86
+ _map_objs_lock = threading.Lock()
87
+ global _map_objs_queues # queues of unused mapnik map objects by PID and mapfile
88
+ _map_objs_queues = {}
89
+ else:
90
+ # instance variables guarantee separation of caches
91
+ self._map_objs = {}
92
+ self._map_objs_lock = threading.Lock()
93
+
94
+ def _map_cache(self):
95
+ """Get the cache for map objects.
96
+
97
+ Uses an instance variable for containment when multithreaded
98
+ operation is disabled.
99
+ """
100
+ if self.multithreaded:
101
+ return _map_objs
102
+ return self._map_objs
103
+
104
+ def _map_cache_lock(self):
105
+ """Get the cache-locks for map objects.
106
+
107
+ Uses an instance variable for containment when multithreaded
108
+ operation is disabled.
109
+ """
110
+ if self.multithreaded:
111
+ return _map_objs_lock
112
+ return self._map_objs_lock
113
+
114
+ def get_map(self, query):
115
+ if self.res_range and not self.res_range.contains(query.bbox, query.size,
116
+ query.srs):
117
+ raise BlankImage()
118
+ if self.coverage and not self.coverage.intersects(query.bbox, query.srs):
119
+ raise BlankImage()
120
+
121
+ try:
122
+ resp = self.render(query)
123
+ except RuntimeError as ex:
124
+ log.error('could not render Mapnik map: %s', ex)
125
+ reraise_exception(SourceError(ex.args[0]), sys.exc_info())
126
+ resp.opacity = self.opacity
127
+ return resp
128
+
129
+ def render(self, query):
130
+ mapfile = self.mapfile
131
+ if '%(webmercator_level)' in mapfile:
132
+ _bbox, level = tile_grid(3857).get_affected_bbox_and_level(
133
+ query.bbox, query.size, req_srs=query.srs)
134
+ mapfile = mapfile % {'webmercator_level': level}
135
+
136
+ if self.lock:
137
+ with self.lock():
138
+ return self.render_mapfile(mapfile, query)
139
+ else:
140
+ return self.render_mapfile(mapfile, query)
141
+
142
+ def _create_map_obj(self, mapfile, process_id):
143
+ m = mapnik.Map(0, 0)
144
+ mapnik.load_map(m, str(mapfile))
145
+ m.map_obj_pid = process_id
146
+ return m
147
+
148
+ def _get_map_obj(self, mapfile):
149
+ if not self.multithreaded:
150
+ return self._create_map_obj(mapfile, None)
151
+
152
+ process_id = multiprocessing.current_process()._identity
153
+ queue_cachekey = (process_id, mapfile)
154
+ if queue_cachekey in _map_objs_queues:
155
+ try:
156
+ m = _map_objs_queues[queue_cachekey].get_nowait()
157
+ # check explicitly for the process ID to ensure that
158
+ # map objects cannot move between processes
159
+ if m.map_obj_pid == process_id:
160
+ return m
161
+ except Empty:
162
+ pass
163
+ return self._create_map_obj(mapfile, process_id)
164
+
165
+ def _put_unused_map_obj(self, mapfile, m):
166
+ process_id = multiprocessing.current_process()._identity
167
+ queue_cachekey = (process_id, mapfile)
168
+ if queue_cachekey not in _map_objs_queues:
169
+ _map_objs_queues[queue_cachekey] = Queue(MAX_UNUSED_MAPS)
170
+ try:
171
+ _map_objs_queues[queue_cachekey].put_nowait(m)
172
+ except Full:
173
+ # cleanup the data and drop the map, so it can be garbage collected
174
+ m.remove_all()
175
+ mapnik.clear_cache()
176
+
177
+ def _get_cachekey(self, mapfile):
178
+ if not self.multithreaded or self._cache_map_obj:
179
+ # all MapnikSources with the same mapfile share the same Mapnik Map.
180
+ return (None, None, mapfile)
181
+ thread_id = threading.current_thread().ident
182
+ process_id = multiprocessing.current_process()._identity
183
+ return (process_id, thread_id, mapfile)
184
+
185
+ def _cleanup_unused_cached_maps(self, mapfile):
186
+ if not self.multithreaded:
187
+ return
188
+ # clean up no longer used cached maps
189
+ process_id = multiprocessing.current_process()._identity
190
+ process_cache_keys = [k for k in self._map_cache().keys()
191
+ if k[0] == process_id]
192
+ # To avoid time-consuming cleanup whenever one thread in the
193
+ # threadpool finishes, allow ignoring up to 5 dead mapnik
194
+ # instances. (5 is empirical)
195
+ if len(process_cache_keys) > (5 + threading.active_count()):
196
+ active_thread_ids = set(i.ident for i in threading.enumerate())
197
+ for k in process_cache_keys:
198
+ with self._map_cache_lock():
199
+ if not k[1] in active_thread_ids and k in self._map_cache():
200
+ try:
201
+ m = self._map_cache()[k]
202
+ del self._map_cache()[k]
203
+ # put the map into the queue of unused
204
+ # maps so it can be re-used from another
205
+ # thread.
206
+ self._put_unused_map_obj(mapfile, m)
207
+ except KeyError:
208
+ continue
209
+
210
+ def map_obj(self, mapfile):
211
+ # cache loaded map objects
212
+ # only works when a single proc/thread accesses the map
213
+ # (forking the render process doesn't work because of open database
214
+ # file handles that gets passed to the child)
215
+ # segment the cache by process and thread to avoid interference
216
+ cachekey = self._get_cachekey(mapfile)
217
+ with self._map_cache_lock():
218
+ if cachekey not in self._map_cache():
219
+ self._map_cache()[cachekey] = self._get_map_obj(mapfile)
220
+ mapnik_map = self._map_cache()[cachekey]
221
+
222
+ self._cleanup_unused_cached_maps(mapfile)
223
+
224
+ return mapnik_map
225
+
226
+ def render_mapfile(self, mapfile, query):
227
+ return run_non_blocking(self._render_mapfile, (mapfile, query))
228
+
229
+ def _render_mapfile(self, mapfile, query):
230
+ start_time = time.time()
231
+
232
+ m = self.map_obj(mapfile)
233
+ m.resize(query.size[0], query.size[1])
234
+ m.srs = '+init=%s' % str(query.srs.srs_code.lower())
235
+ envelope = mapnik.Box2d(*query.bbox)
236
+ m.zoom_to_box(envelope)
237
+ data = None
238
+
239
+ try:
240
+ if self.layers:
241
+ i = 0
242
+ for layer in m.layers[:]:
243
+ if layer.name != 'Unknown' and layer.name not in self.layers:
244
+ del m.layers[i]
245
+ else:
246
+ i += 1
247
+
248
+ img = mapnik.Image(query.size[0], query.size[1])
249
+ if self.scale_factor:
250
+ mapnik.render(m, img, self.scale_factor)
251
+ else:
252
+ mapnik.render(m, img)
253
+ data = img.tostring(str(query.format))
254
+ finally:
255
+ size = None
256
+ if data:
257
+ size = len(data)
258
+ log_request('%s:%s:%s:%s' % (mapfile, query.bbox, query.srs.srs_code, query.size),
259
+ status='200' if data else '500', size=size, method='API', duration=time.time()-start_time)
260
+
261
+ return ImageSource(BytesIO(data), size=query.size,
262
+ image_opts=ImageOptions(format=query.format))
@@ -0,0 +1,97 @@
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
+
33
+ class TiledSource(MapLayer):
34
+ def __init__(self, grid, client, coverage=None, image_opts=None, error_handler=None,
35
+ res_range=None):
36
+ MapLayer.__init__(self, image_opts=image_opts)
37
+ self.grid = grid
38
+ self.client = client
39
+ self.image_opts = image_opts or ImageOptions()
40
+ self.coverage = coverage
41
+ self.extent = coverage.extent if coverage else map_extent_from_grid(grid)
42
+ self.res_range = res_range
43
+ self.error_handler = error_handler
44
+
45
+ def get_map(self, query):
46
+ if self.grid.tile_size != query.size:
47
+ ex = InvalidSourceQuery(
48
+ 'tile size of cache and tile source do not match: %s != %s'
49
+ % (self.grid.tile_size, query.size)
50
+ )
51
+ log_config.error(ex)
52
+ raise ex
53
+
54
+ if self.grid.srs != query.srs:
55
+ ex = InvalidSourceQuery(
56
+ 'SRS of cache and tile source do not match: %r != %r'
57
+ % (self.grid.srs, query.srs)
58
+ )
59
+ log_config.error(ex)
60
+ raise ex
61
+
62
+ if self.res_range and not self.res_range.contains(query.bbox, query.size,
63
+ query.srs):
64
+ raise BlankImage()
65
+ if self.coverage and not self.coverage.intersects(query.bbox, query.srs):
66
+ raise BlankImage()
67
+
68
+ _bbox, grid, tiles = self.grid.get_affected_tiles(query.bbox, query.size)
69
+
70
+ if grid != (1, 1):
71
+ raise InvalidSourceQuery('BBOX does not align to tile')
72
+
73
+ tile_coord = next(tiles)
74
+
75
+ try:
76
+ return self.client.get_tile(tile_coord, format=query.format)
77
+ except HTTPClientError as e:
78
+ if self.error_handler:
79
+ resp = self.error_handler.handle(e.response_code, query)
80
+ if resp:
81
+ return resp
82
+ log.warning('could not retrieve tile: %s', e)
83
+ reraise_exception(SourceError(e.args[0]), sys.exc_info())
84
+
85
+
86
+ class CacheSource(CacheMapLayer):
87
+ def __init__(self, tile_manager, extent=None, image_opts=None,
88
+ max_tile_limit=None, tiled_only=False):
89
+ CacheMapLayer.__init__(self, tile_manager, extent=extent, image_opts=image_opts,
90
+ max_tile_limit=max_tile_limit)
91
+ self.supports_meta_tiles = not tiled_only
92
+ self.tiled_only = tiled_only
93
+
94
+ def get_map(self, query):
95
+ if self.tiled_only:
96
+ query.tiled_only = True
97
+ return CacheMapLayer.get_map(self, query)
mapproxy/source/wms.py ADDED
@@ -0,0 +1,273 @@
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
+
33
+ class WMSSource(MapLayer):
34
+ supports_meta_tiles = True
35
+
36
+ def __init__(self, client, image_opts=None, coverage=None, res_range=None,
37
+ transparent_color=None, transparent_color_tolerance=None,
38
+ supported_srs=None, supported_formats=None, fwd_req_params=None,
39
+ error_handler=None):
40
+ MapLayer.__init__(self, image_opts=image_opts)
41
+ self.client = client
42
+ self.supported_srs = supported_srs or []
43
+ self.supported_formats = supported_formats or []
44
+ self.fwd_req_params = fwd_req_params or set()
45
+
46
+ self.transparent_color = transparent_color
47
+ self.transparent_color_tolerance = transparent_color_tolerance
48
+ if self.transparent_color:
49
+ self.image_opts.transparent = True
50
+ self.coverage = coverage
51
+ self.res_range = res_range
52
+ if self.coverage:
53
+ self.extent = MapExtent(self.coverage.bbox, self.coverage.srs)
54
+ else:
55
+ self.extent = DefaultMapExtent()
56
+ self.error_handler = error_handler
57
+
58
+ def is_opaque(self, query):
59
+ """
60
+ Returns true if we are sure that the image is not transparent.
61
+ """
62
+ if self.res_range and not self.res_range.contains(query.bbox, query.size,
63
+ query.srs):
64
+ return False
65
+
66
+ if self.image_opts.transparent:
67
+ return False
68
+
69
+ if self.opacity is not None and (0.0 < self.opacity < 0.99):
70
+ return False
71
+
72
+ if not self.coverage:
73
+ # not transparent and no coverage
74
+ return True
75
+
76
+ if self.coverage.contains(query.bbox, query.srs):
77
+ # not transparent and completely inside coverage
78
+ return True
79
+
80
+ return False
81
+
82
+ def get_map(self, query):
83
+ if self.res_range and not self.res_range.contains(query.bbox, query.size,
84
+ query.srs):
85
+ raise BlankImage()
86
+ if self.coverage and not self.coverage.intersects(query.bbox, query.srs):
87
+ raise BlankImage()
88
+ try:
89
+ resp = self._get_map(query)
90
+ if self.transparent_color:
91
+ resp = make_transparent(resp, self.transparent_color,
92
+ self.transparent_color_tolerance)
93
+ resp.opacity = self.opacity
94
+ return resp
95
+
96
+ except HTTPClientError as e:
97
+ if self.error_handler:
98
+ resp = self.error_handler.handle(e.response_code, query)
99
+ if resp:
100
+ return resp
101
+ log.warning('could not retrieve WMS map: %s', e.full_msg or e)
102
+ reraise_exception(SourceError(e.args[0]), sys.exc_info())
103
+
104
+ def _get_map(self, query):
105
+ format = self.image_opts.format
106
+ if not format:
107
+ format = query.format
108
+ if self.supported_formats and format not in self.supported_formats:
109
+ format = self.supported_formats[0]
110
+ if self.supported_srs:
111
+ # srs can be equal while still having a different srs_code (EPSG:3857/900913),
112
+ # make sure to use a supported srs_code
113
+ request_srs = None
114
+ for srs in self.supported_srs:
115
+ if query.srs == srs:
116
+ request_srs = srs
117
+ break
118
+ if request_srs is None:
119
+ return self._get_transformed(query, format)
120
+ if query.srs.srs_code != request_srs.srs_code:
121
+ query.srs = request_srs
122
+ if self.extent and not self.extent.contains(MapExtent(query.bbox, query.srs)):
123
+ return self._get_sub_query(query, format)
124
+ resp = self.client.retrieve(query, format)
125
+ return ImageSource(resp, size=query.size, image_opts=self.image_opts)
126
+
127
+ def _get_sub_query(self, query, format):
128
+ size, offset, bbox = bbox_position_in_image(query.bbox, query.size, self.extent.bbox_for(query.srs))
129
+ if size[0] == 0 or size[1] == 0:
130
+ raise BlankImage()
131
+ src_query = MapQuery(bbox, size, query.srs, format, dimensions=query.dimensions)
132
+ resp = self.client.retrieve(src_query, format)
133
+ return SubImageSource(resp, size=query.size, offset=offset, image_opts=self.image_opts)
134
+
135
+ def _get_transformed(self, query, format):
136
+ dst_srs = query.srs
137
+ src_srs = self.supported_srs.best_srs(dst_srs)
138
+ dst_bbox = query.bbox
139
+ src_bbox = dst_srs.transform_bbox_to(src_srs, dst_bbox)
140
+
141
+ src_width, src_height = src_bbox[2]-src_bbox[0], src_bbox[3]-src_bbox[1]
142
+ ratio = src_width/src_height
143
+ dst_size = query.size
144
+ xres, yres = src_width/dst_size[0], src_height/dst_size[1]
145
+ if xres < yres:
146
+ src_size = dst_size[0], int(dst_size[0]/ratio + 0.5)
147
+ else:
148
+ src_size = int(dst_size[1]*ratio + 0.5), dst_size[1]
149
+
150
+ src_query = MapQuery(src_bbox, src_size, src_srs, format, dimensions=query.dimensions)
151
+
152
+ if self.coverage and not self.coverage.contains(src_bbox, src_srs):
153
+ img = self._get_sub_query(src_query, format)
154
+ else:
155
+ resp = self.client.retrieve(src_query, format)
156
+ img = ImageSource(resp, size=src_size, image_opts=self.image_opts)
157
+
158
+ img = ImageTransformer(src_srs, dst_srs).transform(img, src_bbox,
159
+ query.size, dst_bbox, self.image_opts)
160
+
161
+ img.format = format
162
+ return img
163
+
164
+ def _is_compatible(self, other, query):
165
+ if not isinstance(other, WMSSource):
166
+ return False
167
+
168
+ if self.opacity is not None or other.opacity is not None:
169
+ return False
170
+
171
+ if self.supported_srs != other.supported_srs:
172
+ return False
173
+
174
+ if self.supported_formats != other.supported_formats:
175
+ return False
176
+
177
+ if self.transparent_color != other.transparent_color:
178
+ return False
179
+
180
+ if self.transparent_color_tolerance != other.transparent_color_tolerance:
181
+ return False
182
+
183
+ if self.coverage != other.coverage:
184
+ return False
185
+
186
+ if (query.dimensions_for_params(self.fwd_req_params) !=
187
+ query.dimensions_for_params(other.fwd_req_params)):
188
+ return False
189
+
190
+ return True
191
+
192
+ def combined_layer(self, other, query):
193
+ if not self._is_compatible(other, query):
194
+ return None
195
+
196
+ client = self.client.combined_client(other.client, query)
197
+ if not client:
198
+ return None
199
+
200
+ return WMSSource(client, image_opts=self.image_opts,
201
+ transparent_color=self.transparent_color,
202
+ transparent_color_tolerance=self.transparent_color_tolerance,
203
+ supported_srs=self.supported_srs,
204
+ supported_formats=self.supported_formats,
205
+ res_range=None, # layer outside res_range should already be filtered out
206
+ coverage=self.coverage,
207
+ fwd_req_params=self.fwd_req_params,
208
+ )
209
+
210
+
211
+ class WMSInfoSource(InfoSource):
212
+ def __init__(self, client, fi_transformer=None, coverage=None):
213
+ self.client = client
214
+ self.fi_transformer = fi_transformer
215
+ self.coverage = coverage
216
+
217
+ def get_info(self, query):
218
+ if self.coverage and not self.coverage.contains(query.coord, query.srs):
219
+ return None
220
+ doc = self.client.get_info(query)
221
+ if self.fi_transformer:
222
+ doc = self.fi_transformer(doc)
223
+ return doc
224
+
225
+
226
+ class WMSLegendSource(LegendSource):
227
+ def __init__(self, clients, legend_cache, static=False):
228
+ self.clients = clients
229
+ self.identifier = legend_identifier([c.identifier for c in self.clients])
230
+ self._cache = legend_cache
231
+ self._size = None
232
+ self.static = static
233
+
234
+ @property
235
+ def size(self):
236
+ if not self._size:
237
+ legend = self.get_legend(LegendQuery(format='image/png', scale=None))
238
+ # TODO image size without as_image?
239
+ self._size = legend.as_image().size
240
+ return self._size
241
+
242
+ def get_legend(self, query):
243
+ if self.static:
244
+ # prevent caching of static legends for different scales
245
+ legend = Legend(id=self.identifier, scale=None)
246
+ else:
247
+ legend = Legend(id=self.identifier, scale=query.scale)
248
+
249
+ if not self._cache.load(legend) or query.format == 'json':
250
+ legends = []
251
+ error_occured = False
252
+ for client in self.clients:
253
+ try:
254
+ legends.append(client.get_legend(query))
255
+ except HTTPClientError as e:
256
+ error_occured = True
257
+ log.error(e.args[0])
258
+ except SourceError as e:
259
+ error_occured = True
260
+ # TODO errors?
261
+ log.error(e.args[0])
262
+ format = split_mime_type(query.format)[1]
263
+
264
+ if format == 'json':
265
+ return concat_json_legends(legends)
266
+
267
+ legend = Legend(source=concat_legends(legends, format=format),
268
+ id=self.identifier, scale=query.scale)
269
+ if not error_occured:
270
+ self._cache.store(legend)
271
+
272
+ # returns ImageSource
273
+ return legend.source