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,347 @@
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
+ from __future__ import division
17
+ import os
18
+ import pkg_resources
19
+
20
+ from mapproxy.config import base_config, abspath
21
+ from mapproxy.compat.image import Image, ImageColor, ImageDraw, ImageFont
22
+ from mapproxy.image import ImageSource
23
+ from mapproxy.image.opts import create_image, ImageOptions
24
+ from mapproxy.compat import string_type
25
+
26
+ _pil_ttf_support = True
27
+
28
+
29
+ import logging
30
+ log_system = logging.getLogger('mapproxy.system')
31
+
32
+ def message_image(message, size, image_opts, bgcolor='#ffffff',
33
+ transparent=False):
34
+ """
35
+ Creates an image with text (`message`). This can be used
36
+ to create in_image exceptions.
37
+
38
+ For dark `bgcolor` the font color is white, otherwise black.
39
+
40
+ :param message: the message to put in the image
41
+ :param size: the size of the output image
42
+ :param format: the output format of the image
43
+ :param bgcolor: the background color of the image
44
+ :param transparent: if True and the `format` supports it,
45
+ return a transparent image
46
+ :rtype: `ImageSource`
47
+ """
48
+ eimg = ExceptionImage(message, image_opts=image_opts)
49
+ return eimg.draw(size=size)
50
+
51
+ def attribution_image(message, size, image_opts=None, inverse=False):
52
+ """
53
+ Creates an image with text attribution (`message`).
54
+
55
+ :param message: the message to put in the image
56
+ :param size: the size of the output image
57
+ :param format: the output format of the image
58
+ :param inverse: if true, write white text
59
+ :param transparent: if True and the `format` supports it,
60
+ return a transparent image
61
+ :rtype: `ImageSource`
62
+ """
63
+ if image_opts is None:
64
+ image_opts = ImageOptions(transparent=True)
65
+ aimg = AttributionImage(message, image_opts=image_opts,
66
+ inverse=inverse)
67
+ return aimg.draw(size=size)
68
+
69
+ class MessageImage(object):
70
+ """
71
+ Base class for text rendering in images (for watermarks, exception images, etc.)
72
+
73
+ :ivar font_name: the font name for the text
74
+ :ivar font_size: the font size of the text
75
+ :ivar font_color: the color of the font as a tuple
76
+ :ivar box_color: the color of the box behind the text.
77
+ color as a tuple or ``None``
78
+ """
79
+ font_name = 'DejaVu Sans Mono'
80
+ font_size = 10
81
+ font_color = ImageColor.getrgb('black')
82
+ box_color = None
83
+ linespacing = 5
84
+ padding = 3
85
+ placement = 'ul'
86
+
87
+ def __init__(self, message, image_opts):
88
+ self.message = message
89
+ self.image_opts = image_opts
90
+ self._font = None
91
+
92
+ @property
93
+ def font(self):
94
+ global _pil_ttf_support
95
+ if self._font is None:
96
+ if self.font_name != 'default' and _pil_ttf_support:
97
+ try:
98
+ self._font = ImageFont.truetype(font_file(self.font_name),
99
+ self.font_size)
100
+ except ImportError:
101
+ _pil_ttf_support = False
102
+ log_system.warning("Couldn't load TrueType fonts, "
103
+ "PIL needs to be build with freetype support.")
104
+ except IOError:
105
+ _pil_ttf_support = False
106
+ log_system.warning("Couldn't load find TrueType font ", self.font_name)
107
+ if self._font is None:
108
+ self._font = ImageFont.load_default()
109
+ return self._font
110
+
111
+ def new_image(self, size):
112
+ return Image.new('RGBA', size)
113
+
114
+ def draw(self, img=None, size=None, in_place=True):
115
+ """
116
+ Create the message image. Either draws on top of `img` or creates a
117
+ new image with the given `size`.
118
+ """
119
+ if not ((img and not size) or (size and not img)):
120
+ raise TypeError('need either img or size argument')
121
+
122
+ if img is None:
123
+ base_img = self.new_image(size)
124
+ elif not in_place:
125
+ size = img.size
126
+ base_img = self.new_image(size)
127
+ else:
128
+ base_img = img.as_image()
129
+ size = base_img.size
130
+
131
+ if not self.message:
132
+ if img is not None:
133
+ return img
134
+ return ImageSource(base_img, size=size, image_opts=self.image_opts)
135
+
136
+ draw = ImageDraw.Draw(base_img)
137
+ self.draw_msg(draw, size)
138
+ image_opts = self.image_opts
139
+ if not in_place and img:
140
+ image_opts = image_opts or img.image_opts
141
+ img = img.as_image()
142
+ converted = False
143
+ if len(self.font_color) == 4 and img.mode != 'RGBA':
144
+ # we need RGBA to keep transparency from text
145
+ converted = img.mode
146
+ img = img.convert('RGBA')
147
+ img.paste(base_img, (0, 0), base_img)
148
+ if converted == 'RGB':
149
+ # convert image back
150
+ img = img.convert('RGB')
151
+ base_img = img
152
+
153
+ return ImageSource(base_img, size=size, image_opts=image_opts)
154
+
155
+ def draw_msg(self, draw, size):
156
+ td = TextDraw(self.message, font=self.font, bg_color=self.box_color,
157
+ font_color=self.font_color, placement=self.placement,
158
+ linespacing=self.linespacing, padding=self.padding)
159
+ td.draw(draw, size)
160
+
161
+
162
+ class ExceptionImage(MessageImage):
163
+ """
164
+ Image for exceptions.
165
+ """
166
+ font_name = 'default'
167
+ font_size = 9
168
+ def __init__(self, message, image_opts):
169
+ MessageImage.__init__(self, message, image_opts=image_opts.copy())
170
+ if not self.image_opts.bgcolor:
171
+ self.image_opts.bgcolor = '#ffffff'
172
+
173
+ def new_image(self, size):
174
+ return create_image(size, self.image_opts)
175
+
176
+ @property
177
+ def font_color(self):
178
+ if self.image_opts.transparent:
179
+ return ImageColor.getrgb('black')
180
+ if _luminance(ImageColor.getrgb(self.image_opts.bgcolor)) < 128:
181
+ return ImageColor.getrgb('white')
182
+ return ImageColor.getrgb('black')
183
+
184
+
185
+ class WatermarkImage(MessageImage):
186
+ """
187
+ Image with large, faded message.
188
+ """
189
+ font_name = 'DejaVu Sans'
190
+ font_size = 24
191
+ font_color = (128, 128, 128)
192
+
193
+ def __init__(self, message, image_opts, placement='c', opacity=None, font_color=None, font_size=None):
194
+ MessageImage.__init__(self, message, image_opts=image_opts)
195
+ if opacity is None:
196
+ opacity = 30
197
+ if font_size:
198
+ self.font_size = font_size
199
+ if font_color:
200
+ self.font_color = font_color
201
+ self.font_color = self.font_color + tuple([opacity])
202
+ self.placement = placement
203
+
204
+ def draw_msg(self, draw, size):
205
+ td = TextDraw(self.message, self.font, self.font_color)
206
+ if self.placement in ('l', 'b'):
207
+ td.placement = 'cL'
208
+ td.draw(draw, size)
209
+ if self.placement in ('r', 'b'):
210
+ td.placement = 'cR'
211
+ td.draw(draw, size)
212
+ if self.placement == 'c':
213
+ td.placement = 'cc'
214
+ td.draw(draw, size)
215
+
216
+
217
+ class AttributionImage(MessageImage):
218
+ """
219
+ Image with attribution information.
220
+ """
221
+ font_name = 'DejaVu Sans'
222
+ font_size = 10
223
+ placement = 'lr'
224
+
225
+ def __init__(self, message, image_opts, inverse=False):
226
+ MessageImage.__init__(self, message, image_opts=image_opts)
227
+ self.inverse = inverse
228
+
229
+ @property
230
+ def font_color(self):
231
+ if self.inverse:
232
+ return ImageColor.getrgb('white')
233
+ else:
234
+ return ImageColor.getrgb('black')
235
+
236
+ @property
237
+ def box_color(self):
238
+ if self.inverse:
239
+ return (0, 0, 0, 100)
240
+ else:
241
+ return (255, 255, 255, 120)
242
+
243
+
244
+ class TextDraw(object):
245
+ def __init__(self, text, font, font_color=None, bg_color=None,
246
+ placement='ul', padding=5, linespacing=3):
247
+ if isinstance(text, string_type):
248
+ text = text.split('\n')
249
+ self.text = text
250
+ self.font = font
251
+ self.bg_color = bg_color
252
+ self.font_color = font_color
253
+ self.placement = placement
254
+ self.padding = (padding, padding, padding, padding)
255
+ self.linespacing = linespacing
256
+
257
+ def text_boxes(self, draw, size):
258
+ try:
259
+ total_bbox, boxes = self._relative_text_boxes(draw)
260
+ except UnicodeEncodeError:
261
+ # raised if font does not support unicode
262
+ self.text = [l.encode('ascii', 'replace') for l in self.text]
263
+ total_bbox, boxes = self._relative_text_boxes(draw)
264
+ return self._place_boxes(total_bbox, boxes, size)
265
+
266
+ def draw(self, draw, size):
267
+ total_bbox, boxes = self.text_boxes(draw, size)
268
+ if self.bg_color:
269
+ draw.rectangle(
270
+ (total_bbox[0]-self.padding[0],
271
+ total_bbox[1]-self.padding[1],
272
+ total_bbox[2]+self.padding[2],
273
+ total_bbox[3]+self.padding[3]),
274
+ fill=self.bg_color)
275
+
276
+ for text, box in zip(self.text, boxes):
277
+ draw.text((box[0], box[1]), text, font=self.font, fill=self.font_color)
278
+
279
+ def _relative_text_boxes(self, draw):
280
+ total_bbox = (1e9, 1e9, -1e9, -1e9)
281
+ boxes = []
282
+ y_offset = 0
283
+ for i, line in enumerate(self.text):
284
+ text_size = draw.textsize(line, font=self.font)
285
+ text_box = (0, y_offset, text_size[0], text_size[1]+y_offset)
286
+ boxes.append(text_box)
287
+ total_bbox = (min(total_bbox[0], text_box[0]),
288
+ min(total_bbox[1], text_box[1]),
289
+ max(total_bbox[2], text_box[2]),
290
+ max(total_bbox[3], text_box[3]),
291
+ )
292
+
293
+ y_offset += text_size[1] + self.linespacing
294
+ return total_bbox, boxes
295
+
296
+ def _move_bboxes(self, boxes, offsets):
297
+ result = []
298
+ for box in boxes:
299
+ box = box[0]+offsets[0], box[1]+offsets[1], box[2]+offsets[0], box[3]+offsets[1]
300
+ result.append(tuple(int(x) for x in box))
301
+ return result
302
+
303
+ def _place_boxes(self, total_bbox, boxes, size):
304
+ x_offset = y_offset = None
305
+ text_size = (total_bbox[2] - total_bbox[0]), (total_bbox[3] - total_bbox[1])
306
+
307
+ if self.placement[0] == 'u':
308
+ y_offset = self.padding[1]
309
+ elif self.placement[0] == 'l':
310
+ y_offset = size[1] - self.padding[3] - text_size[1]
311
+ elif self.placement[0] == 'c':
312
+ y_offset = size[1] // 2 - text_size[1] // 2
313
+
314
+ if self.placement[1] == 'l':
315
+ x_offset = self.padding[0]
316
+ if self.placement[1] == 'L':
317
+ x_offset = -text_size[0] // 2
318
+ elif self.placement[1] == 'r':
319
+ x_offset = size[0] - self.padding[1] - text_size[0]
320
+ elif self.placement[1] == 'R':
321
+ x_offset = size[0] - text_size[0] // 2
322
+ elif self.placement[1] == 'c':
323
+ x_offset = size[0] // 2 - text_size[0] // 2
324
+
325
+ if x_offset is None or y_offset is None:
326
+ raise ValueError('placement %r not supported' % self.placement)
327
+
328
+ offsets = x_offset, y_offset
329
+ return self._move_bboxes([total_bbox], offsets)[0], self._move_bboxes(boxes, offsets)
330
+
331
+ def font_file(font_name):
332
+ font_dir = base_config().image.font_dir
333
+ font_name = font_name.replace(' ', '')
334
+ if font_dir:
335
+ abspath(font_dir)
336
+ path = os.path.join(font_dir, font_name + '.ttf')
337
+ else:
338
+ path = pkg_resources.resource_filename(__name__, 'fonts/' + font_name + '.ttf')
339
+ return path
340
+
341
+
342
+ def _luminance(color):
343
+ """
344
+ Returns the luminance of a RGB tuple. Uses ITU-R 601-2 luma transform.
345
+ """
346
+ r, g, b = color
347
+ return r * 299/1000 + g * 587/1000 + b * 114/1000
mapproxy/image/opts.py ADDED
@@ -0,0 +1,182 @@
1
+ # -:- encoding: utf-8 -:-
2
+ # This file is part of the MapProxy project.
3
+ # Copyright (C) 2011 Omniscale <http://omniscale.de>
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ import copy
18
+ from mapproxy.compat import string_type
19
+
20
+ class ImageOptions(object):
21
+ def __init__(self, mode=None, transparent=None, opacity=None, resampling=None,
22
+ format=None, bgcolor=None, colors=None, encoding_options=None):
23
+ self.transparent = transparent
24
+ self.opacity = opacity
25
+ self.resampling = resampling
26
+ if format is not None:
27
+ format = ImageFormat(format)
28
+ self.format = format
29
+ self.mode = mode
30
+ self.bgcolor = bgcolor
31
+ self.colors = colors
32
+ self.encoding_options = encoding_options or {}
33
+
34
+ def __repr__(self):
35
+ options = []
36
+ for k in dir(self):
37
+ if k.startswith('_'):
38
+ continue
39
+ v = getattr(self, k)
40
+ if v is not None and not hasattr(v, 'im_func') and not hasattr(v, '__func__'):
41
+ options.append('%s=%r' % (k, v))
42
+ return 'ImageOptions(%s)' % (', '.join(options), )
43
+
44
+ def __eq__(self, other):
45
+ if not isinstance(other, ImageOptions):
46
+ return NotImplemented
47
+
48
+ return (
49
+ self.transparent == other.transparent
50
+ and self.opacity == other.opacity
51
+ and self.resampling == other.resampling
52
+ and self.format == other.format
53
+ and self.mode == other.mode
54
+ and self.bgcolor == other.bgcolor
55
+ and self.colors == other.colors
56
+ and self.encoding_options == other.encoding_options
57
+ )
58
+
59
+ def copy(self):
60
+ return copy.copy(self)
61
+
62
+ class ImageFormat(str):
63
+ def __new__(cls, value, *args, **keywargs):
64
+ if isinstance(value, ImageFormat):
65
+ return value
66
+ return str.__new__(cls, value)
67
+
68
+ @property
69
+ def mime_type(self):
70
+ if self.startswith('image/'):
71
+ return self
72
+ return 'image/' + self
73
+
74
+ @property
75
+ def ext(self):
76
+ ext = self
77
+ if '/' in ext:
78
+ ext = ext.split('/', 1)[1]
79
+ if ';' in ext:
80
+ ext = ext.split(';', 1)[0]
81
+
82
+ return ext.strip()
83
+
84
+ def __eq__(self, other):
85
+ if isinstance(other, string_type):
86
+ other = ImageFormat(other)
87
+ elif not isinstance(other, ImageFormat):
88
+ return NotImplemented
89
+
90
+ return self.ext == other.ext
91
+
92
+ def __hash__(self):
93
+ return hash(str(self))
94
+
95
+ def __ne__(self, other):
96
+ return not (self == other)
97
+
98
+ def create_image(size, image_opts=None):
99
+ """
100
+ Create a new image that is compatible with the given `image_opts`.
101
+ Takes into account mode, transparent, bgcolor.
102
+ """
103
+ from mapproxy.compat.image import Image, ImageColor
104
+
105
+ if image_opts is None:
106
+ mode = 'RGB'
107
+ bgcolor = (255, 255, 255)
108
+ else:
109
+ mode = image_opts.mode
110
+ if mode in (None, 'P'):
111
+ if image_opts.transparent:
112
+ mode = 'RGBA'
113
+ else:
114
+ mode = 'RGB'
115
+
116
+ bgcolor = image_opts.bgcolor or (255, 255, 255)
117
+
118
+ if isinstance(bgcolor, string_type):
119
+ bgcolor = ImageColor.getrgb(bgcolor)
120
+
121
+ if image_opts.transparent and len(bgcolor) == 3:
122
+ bgcolor = bgcolor + (0, )
123
+
124
+ if image_opts.mode == 'I':
125
+ bgcolor = bgcolor[0]
126
+
127
+ return Image.new(mode, size, bgcolor)
128
+
129
+
130
+ class ImageFormats(object):
131
+ def __init__(self):
132
+ self.format_options = {}
133
+
134
+ def add(self, opts):
135
+ assert opts.format is not None
136
+ self.format_options[opts.format] = opts
137
+
138
+ def options(self, format):
139
+ opts = self.format_options.get(format)
140
+ if not opts:
141
+ opts = ImageOptions(transparent=False, format=format)
142
+ return opts
143
+
144
+ def compatible_image_options(img_opts, base_opts=None):
145
+ """
146
+ Return ImageOptions that is compatible with all given `img_opts`.
147
+
148
+ """
149
+ if any(True for o in img_opts if o.colors == 0):
150
+ colors = 0
151
+ else:
152
+ colors = max(o.colors or 0 for o in img_opts)
153
+
154
+ transparent = None
155
+ for o in img_opts:
156
+ if o.transparent == False:
157
+ transparent = False
158
+ break
159
+ if o.transparent == True:
160
+ transparent = True
161
+
162
+ if any(True for o in img_opts if o.mode):
163
+ # I < P < RGB < RGBA :)
164
+ mode = max(o.mode for o in img_opts if o.mode)
165
+ else:
166
+ mode = None
167
+
168
+ if base_opts:
169
+ options = base_opts.copy()
170
+ if options.colors is None:
171
+ options.colors = colors
172
+ if options.mode is None:
173
+ options.mode = mode
174
+ if options.transparent is None:
175
+ options.transparent = transparent
176
+ else:
177
+ options = img_opts[0].copy()
178
+ options.colors = colors
179
+ options.transparent = transparent
180
+ options.mode = mode
181
+
182
+ return options
mapproxy/image/tile.py ADDED
@@ -0,0 +1,167 @@
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
+ import os
17
+ from mapproxy.image import ImageSource
18
+ from mapproxy.image.transform import ImageTransformer
19
+ from mapproxy.image.opts import create_image
20
+
21
+ import logging
22
+ log = logging.getLogger(__name__)
23
+
24
+ class TileMerger(object):
25
+ """
26
+ Merge multiple tiles into one image.
27
+ """
28
+ def __init__(self, tile_grid, tile_size):
29
+ """
30
+ :param tile_grid: the grid size
31
+ :type tile_grid: ``(int(x_tiles), int(y_tiles))``
32
+ :param tile_size: the size of each tile
33
+ """
34
+ self.tile_grid = tile_grid
35
+ self.tile_size = tile_size
36
+
37
+ def merge(self, ordered_tiles, image_opts):
38
+ """
39
+ Merge all tiles into one image.
40
+
41
+ :param ordered_tiles: list of tiles, sorted row-wise (top to bottom)
42
+ :rtype: `ImageSource`
43
+ """
44
+ if self.tile_grid == (1, 1):
45
+ assert len(ordered_tiles) == 1
46
+ if ordered_tiles[0] is not None:
47
+ tile = ordered_tiles.pop()
48
+ return tile
49
+ src_size = self._src_size()
50
+
51
+ result = create_image(src_size, image_opts)
52
+
53
+ cacheable = True
54
+
55
+ for i, source in enumerate(ordered_tiles):
56
+ if source is None:
57
+ continue
58
+ try:
59
+ if not source.cacheable:
60
+ cacheable = False
61
+ tile = source.as_image()
62
+ pos = self._tile_offset(i)
63
+ tile.draft(image_opts.mode, self.tile_size)
64
+ result.paste(tile, pos)
65
+ source.close_buffers()
66
+ except IOError as e:
67
+ if e.errno is None: # PIL error
68
+ log.warning('unable to load tile %s, removing it (reason was: %s)'
69
+ % (source, str(e)))
70
+ if getattr(source, 'filename'):
71
+ if os.path.exists(source.filename):
72
+ os.remove(source.filename)
73
+ else:
74
+ raise
75
+ return ImageSource(result, size=src_size, image_opts=image_opts, cacheable=cacheable)
76
+
77
+ def _src_size(self):
78
+ width = self.tile_grid[0]*self.tile_size[0]
79
+ height = self.tile_grid[1]*self.tile_size[1]
80
+ return width, height
81
+
82
+ def _tile_offset(self, i):
83
+ """
84
+ Return the image offset (upper-left coord) of the i-th tile,
85
+ where the tiles are ordered row-wise, top to bottom.
86
+ """
87
+ return (i%self.tile_grid[0]*self.tile_size[0],
88
+ i//self.tile_grid[0]*self.tile_size[1])
89
+
90
+
91
+ class TileSplitter(object):
92
+ """
93
+ Splits a large image into multiple tiles.
94
+ """
95
+ def __init__(self, meta_tile, image_opts):
96
+ self.meta_img = meta_tile.as_image()
97
+ self.image_opts = image_opts
98
+
99
+ def get_tile(self, crop_coord, tile_size):
100
+ """
101
+ Return the cropped tile.
102
+ :param crop_coord: the upper left pixel coord to start
103
+ :param tile_size: width and height of the new tile
104
+ :rtype: `ImageSource`
105
+ """
106
+ minx, miny = crop_coord
107
+ maxx = minx + tile_size[0]
108
+ maxy = miny + tile_size[1]
109
+
110
+ if (minx < 0 or miny < 0 or maxx > self.meta_img.size[0]
111
+ or maxy > self.meta_img.size[1]):
112
+
113
+ crop = self.meta_img.crop((
114
+ max(minx, 0),
115
+ max(miny, 0),
116
+ min(maxx, self.meta_img.size[0]),
117
+ min(maxy, self.meta_img.size[1])))
118
+ result = create_image(tile_size, self.image_opts)
119
+ result.paste(crop, (abs(min(minx, 0)), abs(min(miny, 0))))
120
+ crop = result
121
+ else:
122
+ crop = self.meta_img.crop((minx, miny, maxx, maxy))
123
+ return ImageSource(crop, size=tile_size, image_opts=self.image_opts)
124
+
125
+
126
+ class TiledImage(object):
127
+ """
128
+ An image built-up from multiple tiles.
129
+ """
130
+ def __init__(self, tiles, tile_grid, tile_size, src_bbox, src_srs):
131
+ """
132
+ :param tiles: all tiles (sorted row-wise, top to bottom)
133
+ :param tile_grid: the tile grid size
134
+ :type tile_grid: ``(int(x_tiles), int(y_tiles))``
135
+ :param tile_size: the size of each tile
136
+ :param src_bbox: the bbox of all tiles
137
+ :param src_srs: the srs of the bbox
138
+ :param transparent: if the sources are transparent
139
+ """
140
+ self.tiles = tiles
141
+ self.tile_grid = tile_grid
142
+ self.tile_size = tile_size
143
+ self.src_bbox = src_bbox
144
+ self.src_srs = src_srs
145
+
146
+ def image(self, image_opts):
147
+ """
148
+ Return the tiles as one merged image.
149
+
150
+ :rtype: `ImageSource`
151
+ """
152
+ tm = TileMerger(self.tile_grid, self.tile_size)
153
+ return tm.merge(self.tiles, image_opts=image_opts)
154
+
155
+ def transform(self, req_bbox, req_srs, out_size, image_opts):
156
+ """
157
+ Return the the tiles as one merged and transformed image.
158
+
159
+ :param req_bbox: the bbox of the output image
160
+ :param req_srs: the srs of the req_bbox
161
+ :param out_size: the size in pixel of the output image
162
+ :rtype: `ImageSource`
163
+ """
164
+ transformer = ImageTransformer(self.src_srs, req_srs)
165
+ src_img = self.image(image_opts)
166
+ return transformer.transform(src_img, self.src_bbox, out_size, req_bbox,
167
+ image_opts)