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
mapproxy/srs.py ADDED
@@ -0,0 +1,734 @@
1
+ # -*- coding: utf-8 -*-
2
+ # This file is part of the MapProxy project.
3
+ # Copyright (C) 2010 Omniscale <http://omniscale.de>
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ """
18
+ Spatial reference systems and transformation of coordinates.
19
+ """
20
+ from __future__ import division
21
+
22
+ import math
23
+ import threading
24
+ from mapproxy.proj import USE_PROJ4_API
25
+ # Old Proj.4 API
26
+ from mapproxy.proj import Proj, transform, set_datapath
27
+ # New Proj API
28
+ from mapproxy.proj import CRS, Transformer
29
+ from mapproxy.config import base_config
30
+
31
+ import logging
32
+ log_system = logging.getLogger('mapproxy.system')
33
+ log_proj = logging.getLogger('mapproxy.proj')
34
+
35
+
36
+ def get_epsg_num(epsg_code):
37
+ """
38
+ >>> get_epsg_num('ePsG:4326')
39
+ 4326
40
+ >>> get_epsg_num(4313)
41
+ 4313
42
+ >>> get_epsg_num('31466')
43
+ 31466
44
+ >>> get_epsg_num('IGNF:ETRS89UTM28') is None
45
+ True
46
+ """
47
+ if isinstance(epsg_code, str):
48
+ if ':' in epsg_code and epsg_code.upper().startswith('EPSG'):
49
+ epsg_code = int(epsg_code.split(':')[1])
50
+ elif epsg_code.isdigit():
51
+ epsg_code = int(epsg_code)
52
+ else:
53
+ return
54
+ return epsg_code
55
+
56
+
57
+ def get_authority(srs_code):
58
+ """
59
+ >>> get_authority('IAU:1000')
60
+ ('IAU', '1000')
61
+ """
62
+ if isinstance(srs_code, str) and ':' in srs_code:
63
+ auth_name, auth_id = srs_code.rsplit(':', 1)
64
+ return auth_name, auth_id
65
+
66
+
67
+ def _clean_srs_code(code):
68
+ """
69
+ >>> _clean_srs_code(4326)
70
+ 'EPSG:4326'
71
+ >>> _clean_srs_code('31466')
72
+ 'EPSG:31466'
73
+ >>> _clean_srs_code('crs:84')
74
+ 'CRS:84'
75
+ """
76
+ if isinstance(code, str) and ':' in code:
77
+ return code.upper()
78
+ else:
79
+ return 'EPSG:' + str(code)
80
+
81
+
82
+ class TransformationError(Exception):
83
+ pass
84
+
85
+
86
+ _proj_initialized = False
87
+
88
+
89
+ def _init_proj():
90
+ global _proj_initialized
91
+ if not _proj_initialized and 'proj_data_dir' in base_config().srs:
92
+ proj_data_dir = base_config().srs['proj_data_dir']
93
+ if proj_data_dir is None:
94
+ _proj_initialized = True
95
+ return
96
+ log_system.info('loading proj data from %s', proj_data_dir)
97
+ set_datapath(proj_data_dir)
98
+ _proj_initialized = True
99
+
100
+
101
+ _thread_local = threading.local()
102
+
103
+
104
+ def SRS(srs_code):
105
+ _init_proj()
106
+ if isinstance(srs_code, _srs_impl):
107
+ return srs_code
108
+
109
+ srs_code = _clean_srs_code(srs_code)
110
+
111
+ if not hasattr(_thread_local, 'srs_cache'):
112
+ _thread_local.srs_cache = {}
113
+
114
+ if srs_code in _thread_local.srs_cache:
115
+ return _thread_local.srs_cache[srs_code]
116
+ else:
117
+ srs = _srs_impl(srs_code)
118
+ _thread_local.srs_cache[srs_code] = srs
119
+ return srs
120
+
121
+
122
+ WEBMERCATOR_EPSG = set(('EPSG:900913', 'EPSG:3857',
123
+ 'EPSG:102100', 'EPSG:102113'))
124
+
125
+
126
+ class _SRS_Proj4_API(object):
127
+ # http://trac.openlayers.org/wiki/SphericalMercator
128
+ proj_init = {
129
+ 'EPSG:4326': lambda: Proj('+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs +over'),
130
+ 'CRS:84': lambda: Proj('+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs +over'),
131
+ }
132
+ for _epsg in WEBMERCATOR_EPSG:
133
+ proj_init[_epsg] = lambda: Proj(
134
+ '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 '
135
+ '+lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m '
136
+ '+nadgrids=@null +no_defs +over')
137
+
138
+ """
139
+ This class represents a Spatial Reference System.
140
+
141
+ Abstracts transformations between different projections.
142
+ Uses the old Proj.4 API, either via pyproj 1 or c-types.
143
+ """
144
+
145
+ def __init__(self, srs_code):
146
+ """
147
+ Create a new SRS with the given `srs_code` code.
148
+ """
149
+ self.srs_code = srs_code
150
+
151
+ init = self.proj_init.get(srs_code, None)
152
+ if init is not None:
153
+ self.proj = init()
154
+ else:
155
+ epsg_num = get_epsg_num(srs_code)
156
+ if epsg_num is not None:
157
+ self.proj = Proj(init='epsg:%d' % epsg_num)
158
+ else:
159
+ raise ValueError("the old Proj.4 API doesn't support non-EPSG authorities")
160
+
161
+ def transform_to(self, other_srs, points):
162
+ """
163
+ :type points: ``(x, y)`` or ``[(x1, y1), (x2, y2), …]``
164
+
165
+ >>> srs1 = SRS(4326)
166
+ >>> srs2 = SRS(900913)
167
+ >>> [str(round(x, 5)) for x in srs1.transform_to(srs2, (8.22, 53.15))]
168
+ ['915046.21432', '7010792.20171']
169
+ >>> srs1.transform_to(srs1, (8.25, 53.5))
170
+ (8.25, 53.5)
171
+ >>> [(str(round(x, 5)), str(round(y, 5))) for x, y in
172
+ ... srs1.transform_to(srs2, [(8.2, 53.1), (8.22, 53.15), (8.3, 53.2)])]
173
+ ... #doctest: +NORMALIZE_WHITESPACE
174
+ [('912819.8245', '7001516.67745'),
175
+ ('915046.21432', '7010792.20171'),
176
+ ('923951.77358', '7020078.53264')]
177
+ """
178
+ if self == other_srs:
179
+ return points
180
+ if isinstance(points[0], (int, float)) and 2 >= len(points) <= 3:
181
+ return transform(self.proj, other_srs.proj, *points)
182
+
183
+ x = [p[0] for p in points]
184
+ y = [p[1] for p in points]
185
+ transf_pts = transform(self.proj, other_srs.proj, x, y)
186
+ return zip(transf_pts[0], transf_pts[1])
187
+
188
+ def transform_bbox_to(self, other_srs, bbox, with_points=16):
189
+ """
190
+
191
+ :param with_points: the number of points to use for the transformation.
192
+ A bbox transformation with only two or four points may cut off some
193
+ parts due to distortions.
194
+
195
+ >>> ['%.3f' % x for x in
196
+ ... SRS(4326).transform_bbox_to(SRS(3857), (-180.0, -90.0, 180.0, 90.0))]
197
+ ['-20037508.343', '-147730762.670', '20037508.343', '147730758.195']
198
+ >>> ['%.5f' % x for x in
199
+ ... SRS(4326).transform_bbox_to(SRS(3857), (8.2, 53.1, 8.3, 53.2))]
200
+ ['912819.82450', '7001516.67745', '923951.77358', '7020078.53264']
201
+ >>> SRS(4326).transform_bbox_to(SRS(4326), (8.25, 53.0, 8.5, 53.75))
202
+ (8.25, 53.0, 8.5, 53.75)
203
+ """
204
+ if self == other_srs:
205
+ return bbox
206
+ bbox = self.align_bbox(bbox)
207
+ points = generate_envelope_points(bbox, with_points)
208
+ transf_pts = self.transform_to(other_srs, points)
209
+ result = calculate_bbox(transf_pts)
210
+
211
+ log_proj.debug('transformed from %r to %r (%s -> %s)',
212
+ self, other_srs, bbox, result)
213
+
214
+ return result
215
+
216
+ def align_bbox(self, bbox):
217
+ """
218
+ Align bbox to reasonable values to prevent errors in transformations.
219
+ E.g. transformations from EPSG:4326 with lat=90 or -90 will fail, so
220
+ we subtract a tiny delta.
221
+
222
+ At the moment only EPSG:4326 bbox will be modifyed.
223
+
224
+ >>> bbox = SRS(4326).align_bbox((-180, -90, 180, 90))
225
+ >>> -90 < bbox[1] < -89.99999998
226
+ True
227
+ >>> 90 > bbox[3] > 89.99999998
228
+ True
229
+ """
230
+ # TODO should not be needed anymore since we transform with +over
231
+ # still a few tests depend on the rounding behavior of this
232
+ if self.srs_code == 'EPSG:4326':
233
+ delta = 0.00000001
234
+ (minx, miny, maxx, maxy) = bbox
235
+ if abs(miny - -90.0) < 1e-6:
236
+ miny = -90.0 + delta
237
+ if abs(maxy - 90.0) < 1e-6:
238
+ maxy = 90.0 - delta
239
+ bbox = minx, miny, maxx, maxy
240
+ return bbox
241
+
242
+ @property
243
+ def is_latlong(self):
244
+ """
245
+ >>> SRS(4326).is_latlong
246
+ True
247
+ >>> SRS(31466).is_latlong
248
+ False
249
+ """
250
+ return self.proj.is_latlong()
251
+
252
+ def get_geographic_srs(self):
253
+ """ Return the "canonical" geographic CRS corresponding to this CRS.
254
+ Always EPSG:4326 for Proj4 implementation """
255
+ return SRS(4326)
256
+
257
+ @property
258
+ def is_axis_order_ne(self):
259
+ """
260
+ Returns `True` if the axis order is North, then East
261
+ (i.e. y/x or lat/lon).
262
+
263
+ >>> SRS(4326).is_axis_order_ne
264
+ True
265
+ >>> SRS('CRS:84').is_axis_order_ne
266
+ False
267
+ >>> SRS(31468).is_axis_order_ne
268
+ True
269
+ >>> SRS(31463).is_axis_order_ne
270
+ False
271
+ >>> SRS(25831).is_axis_order_ne
272
+ False
273
+ """
274
+ if self.srs_code in base_config().srs.axis_order_ne:
275
+ return True
276
+ if self.srs_code in base_config().srs.axis_order_en:
277
+ return False
278
+ if self.is_latlong:
279
+ return True
280
+ return False
281
+
282
+ @property
283
+ def is_axis_order_en(self):
284
+ """
285
+ Returns `True` if the axis order is East then North
286
+ (i.e. x/y or lon/lat).
287
+ """
288
+ return not self.is_axis_order_ne
289
+
290
+ def __eq__(self, other):
291
+ """
292
+ >>> SRS(4326) == SRS("EpsG:4326")
293
+ True
294
+ >>> SRS(4326) == SRS("4326")
295
+ True
296
+ >>> SRS(4326) == SRS(3857)
297
+ False
298
+ """
299
+ if isinstance(other, _SRS_Proj4_API):
300
+ return self.proj.srs == other.proj.srs
301
+ else:
302
+ return NotImplemented
303
+
304
+ def __ne__(self, other):
305
+ """
306
+ >>> SRS(3857) != SRS(3857)
307
+ False
308
+ >>> SRS(4326) != SRS(900913)
309
+ True
310
+ """
311
+ equal_result = self.__eq__(other)
312
+ if equal_result is NotImplemented:
313
+ return NotImplemented
314
+ else:
315
+ return not equal_result
316
+
317
+ def __str__(self):
318
+ return "SRS %s ('%s')" % (self.srs_code, self.proj.srs)
319
+
320
+ def __repr__(self):
321
+ """
322
+ >>> repr(SRS(4326))
323
+ "SRS('EPSG:4326')"
324
+ """
325
+ return "SRS('%s')" % (self.srs_code,)
326
+
327
+ def __hash__(self):
328
+ return hash(self.srs_code)
329
+
330
+
331
+ class _SRS(object):
332
+ """
333
+ This class represents a Spatial Reference System.
334
+
335
+ Abstracts transformations between different projections.
336
+ Uses the new Proj API via pyproj >=2.
337
+ """
338
+
339
+ def __init__(self, srs_code):
340
+ """
341
+ Create a new SRS with the given `srs_code` code.
342
+ """
343
+ self.srs_code = srs_code
344
+
345
+ if srs_code in WEBMERCATOR_EPSG:
346
+ epsg_num = 3857
347
+ elif srs_code == 'CRS:84':
348
+ epsg_num = 4326
349
+ else:
350
+ epsg_num = get_epsg_num(srs_code)
351
+
352
+ if epsg_num is not None:
353
+ self.proj = CRS.from_epsg(epsg_num)
354
+ else:
355
+ auth_name, auth_id = get_authority(srs_code)
356
+ self.proj = CRS.from_authority(auth_name, auth_id)
357
+
358
+ self._transformers = {}
359
+
360
+ def _transformer(self, other_srs):
361
+ if other_srs in self._transformers:
362
+ return self._transformers[other_srs]
363
+
364
+ t = Transformer.from_crs(self.proj, other_srs.proj, always_xy=True)
365
+ self._transformers[other_srs] = t
366
+ return t
367
+
368
+ def transform_to(self, other_srs, points):
369
+ """
370
+ :type points: ``(x, y)`` or ``[(x1, y1), (x2, y2), …]``
371
+
372
+ >>> srs1 = SRS(4326)
373
+ >>> srs2 = SRS(900913)
374
+ >>> [str(round(x, 5)) for x in srs1.transform_to(srs2, (8.22, 53.15))]
375
+ ['915046.21432', '7010792.20171']
376
+ >>> srs1.transform_to(srs1, (8.25, 53.5))
377
+ (8.25, 53.5)
378
+ >>> [(str(round(x, 5)), str(round(y, 5))) for x, y in
379
+ ... srs1.transform_to(srs2, [(8.2, 53.1), (8.22, 53.15), (8.3, 53.2)])]
380
+ ... #doctest: +NORMALIZE_WHITESPACE
381
+ [('912819.8245', '7001516.67745'),
382
+ ('915046.21432', '7010792.20171'),
383
+ ('923951.77358', '7020078.53264')]
384
+ """
385
+ if self == other_srs:
386
+ return points
387
+
388
+ transformer = self._transformer(other_srs)
389
+ if isinstance(points[0], (int, float)) and 2 >= len(points) <= 3:
390
+ return transformer.transform(*points)
391
+
392
+ x = [p[0] for p in points]
393
+ y = [p[1] for p in points]
394
+ transf_pts = transformer.transform(x, y)
395
+ return zip(transf_pts[0], transf_pts[1])
396
+
397
+ def transform_bbox_to(self, other_srs, bbox, with_points=16):
398
+ """
399
+
400
+ :param with_points: the number of points to use for the transformation.
401
+ A bbox transformation with only two or four points may cut off some
402
+ parts due to distortions.
403
+
404
+ >>> ['%.3f' % x for x in
405
+ ... SRS(4326).transform_bbox_to(SRS(3857), (-180.0, -90.0, 180.0, 90.0))]
406
+ ['-20037508.343', '-20037508.343', '20037508.343', '20037508.343']
407
+ >>> ['%.5f' % x for x in
408
+ ... SRS(4326).transform_bbox_to(SRS(3857), (8.2, 53.1, 8.3, 53.2))]
409
+ ['912819.82450', '7001516.67745', '923951.77358', '7020078.53264']
410
+ >>> SRS(4326).transform_bbox_to(SRS(4326), (8.25, 53.0, 8.5, 53.75))
411
+ (8.25, 53.0, 8.5, 53.75)
412
+ """
413
+ if self == other_srs:
414
+ return bbox
415
+ bbox = bbox
416
+ points = generate_envelope_points(bbox, with_points)
417
+ transf_pts = list(self.transform_to(other_srs, points))
418
+ result = calculate_bbox(transf_pts)
419
+
420
+ log_proj.debug('transformed from %r to %r (%s -> %s)',
421
+ self, other_srs, bbox, result)
422
+
423
+ # XXX: 3857 is only defined within 85.06 N/S, new Proj returns 'inf' for coords
424
+ # outside of these bounds. Adjust bbox for 4326->3857 transformations to the old
425
+ # behavior, as this is expected in a few places (WMS layer extents and quite a few
426
+ # tests).
427
+ if self.srs_code == 'EPSG:4326' and other_srs.srs_code in ('EPSG:3857', 'EPSG:900913'):
428
+ minx, miny, maxx, maxy = result
429
+ if bbox[0] <= -180.0:
430
+ minx = -20037508.342789244
431
+ if bbox[1] <= -85.06:
432
+ miny = -20037508.342789244
433
+ if bbox[2] >= 180.0:
434
+ maxx = 20037508.342789244
435
+ if bbox[3] >= 85.06:
436
+ maxy = 20037508.342789244
437
+ result = (minx, miny, maxx, maxy)
438
+ return result
439
+
440
+ @property
441
+ def is_latlong(self):
442
+ """
443
+ >>> SRS(4326).is_latlong
444
+ True
445
+ >>> SRS(31466).is_latlong
446
+ False
447
+ """
448
+ return self.proj.is_geographic
449
+
450
+ def get_geographic_srs(self):
451
+ """ Return the "canonical" geographic CRS corresponding to this CRS.
452
+ EPSG:4326 for Earth CRS, or another one from other celestial bodies """
453
+ auth = self.proj.to_authority()
454
+ if auth is None or not auth[0].startswith('IAU'):
455
+ ret = SRS(4326)
456
+ else:
457
+ return _SRS(':'.join(self.proj.geodetic_crs.to_authority()))
458
+ return ret
459
+
460
+ @property
461
+ def is_axis_order_ne(self):
462
+ """
463
+ Returns `True` if the axis order is North, then East
464
+ (i.e. y/x or lat/lon).
465
+
466
+ >>> SRS(4326).is_axis_order_ne
467
+ True
468
+ >>> SRS('CRS:84').is_axis_order_ne
469
+ False
470
+ >>> SRS(31468).is_axis_order_ne
471
+ True
472
+ >>> SRS(31463).is_axis_order_ne
473
+ False
474
+ >>> SRS(25831).is_axis_order_ne
475
+ False
476
+ """
477
+ if self.srs_code == 'CRS:84':
478
+ return False
479
+ return self.proj.axis_info[0].direction == 'north'
480
+
481
+ @property
482
+ def is_axis_order_en(self):
483
+ """
484
+ Returns `True` if the axis order is East then North
485
+ (i.e. x/y or lon/lat).
486
+ """
487
+ return not self.is_axis_order_ne
488
+
489
+ def align_bbox(self, bbox):
490
+ """
491
+ Align bbox to reasonable values to prevent errors in transformations.
492
+ E.g. transformations from EPSG:4326 with lat=90 or -90 will fail, so
493
+ we subtract a tiny delta.
494
+
495
+ At the moment only EPSG:4326 bbox will be modifyed.
496
+
497
+ >>> bbox = SRS(4326).align_bbox((-180, -90, 180, 90))
498
+ >>> -90 < bbox[1] < -89.99999998
499
+ True
500
+ >>> 90 > bbox[3] > 89.99999998
501
+ True
502
+ """
503
+ # TODO should not be needed anymore since we transform with +over
504
+ # still a few tests depend on the rounding behavior of this
505
+ if self.srs_code == 'EPSG:4326':
506
+ delta = 0.00000001
507
+ (minx, miny, maxx, maxy) = bbox
508
+ if abs(miny - -90.0) < 1e-6:
509
+ miny = -90.0 + delta
510
+ if abs(maxy - 90.0) < 1e-6:
511
+ maxy = 90.0 - delta
512
+ bbox = minx, miny, maxx, maxy
513
+ return bbox
514
+
515
+ def __eq__(self, other):
516
+ """
517
+ >>> SRS(4326) == SRS("EpsG:4326")
518
+ True
519
+ >>> SRS(4326) == SRS("4326")
520
+ True
521
+ >>> SRS(4326) == SRS(3857)
522
+ False
523
+ """
524
+ if isinstance(other, _SRS):
525
+ return self.proj.srs == other.proj.srs
526
+ else:
527
+ return NotImplemented
528
+
529
+ def __ne__(self, other):
530
+ """
531
+ >>> SRS(3857) != SRS(3857)
532
+ False
533
+ >>> SRS(4326) != SRS(900913)
534
+ True
535
+ """
536
+ equal_result = self.__eq__(other)
537
+ if equal_result is NotImplemented:
538
+ return NotImplemented
539
+ else:
540
+ return not equal_result
541
+
542
+ def __str__(self):
543
+ return "SRS %s ('%s')" % (self.srs_code, self.proj.srs)
544
+
545
+ def __repr__(self):
546
+ """
547
+ >>> repr(SRS(4326))
548
+ "SRS('EPSG:4326')"
549
+ """
550
+ return "SRS('%s')" % (self.srs_code,)
551
+
552
+ def __hash__(self):
553
+ return hash(self.srs_code)
554
+
555
+
556
+ if USE_PROJ4_API:
557
+ _srs_impl = _SRS_Proj4_API
558
+ del _SRS
559
+ else:
560
+ _srs_impl = _SRS
561
+ del _SRS_Proj4_API
562
+
563
+
564
+ def generate_envelope_points(bbox, n):
565
+ """
566
+ Generates points that form a linestring around a given bbox.
567
+
568
+ @param bbox: bbox to generate linestring for
569
+ @param n: the number of points to generate around the bbox
570
+
571
+ >>> generate_envelope_points((10.0, 5.0, 20.0, 15.0), 4)
572
+ [(10.0, 5.0), (20.0, 5.0), (20.0, 15.0), (10.0, 15.0)]
573
+ >>> generate_envelope_points((10.0, 5.0, 20.0, 15.0), 8)
574
+ ... #doctest: +NORMALIZE_WHITESPACE
575
+ [(10.0, 5.0), (15.0, 5.0), (20.0, 5.0), (20.0, 10.0),\
576
+ (20.0, 15.0), (15.0, 15.0), (10.0, 15.0), (10.0, 10.0)]
577
+ """
578
+ (minx, miny, maxx, maxy) = bbox
579
+ if n <= 4:
580
+ n = 0
581
+ else:
582
+ n = int(math.ceil((n - 4) / 4.0))
583
+
584
+ width = maxx - minx
585
+ height = maxy - miny
586
+
587
+ minx, maxx = min(minx, maxx), max(minx, maxx)
588
+ miny, maxy = min(miny, maxy), max(miny, maxy)
589
+
590
+ n += 1
591
+ xstep = width / n
592
+ ystep = height / n
593
+ result = []
594
+ for i in range(n+1):
595
+ result.append((minx + i*xstep, miny))
596
+ for i in range(1, n):
597
+ result.append((maxx, miny + i*ystep))
598
+ for i in range(n, -1, -1):
599
+ result.append((minx + i*xstep, maxy))
600
+ for i in range(n-1, 0, -1):
601
+ result.append((minx, miny + i*ystep))
602
+ return result
603
+
604
+
605
+ def calculate_bbox(points):
606
+ """
607
+ Calculates the bbox of a list of points.
608
+
609
+ >>> calculate_bbox([(-5, 20), (3, 8), (99, 0)])
610
+ (-5, 0, 99, 20)
611
+
612
+ @param points: list of points [(x0, y0), (x1, y2), ...]
613
+ @returns: bbox of the input points.
614
+ """
615
+ points = list(points)
616
+ # points can be INF for invalid transformations, filter out
617
+ try:
618
+ minx = min(p[0] for p in points if p[0] != float('inf'))
619
+ miny = min(p[1] for p in points if p[1] != float('inf'))
620
+ maxx = max(p[0] for p in points if p[0] != float('inf'))
621
+ maxy = max(p[1] for p in points if p[1] != float('inf'))
622
+ return (minx, miny, maxx, maxy)
623
+ except ValueError: # min/max are called with empty list when everything is inf
624
+ raise TransformationError()
625
+
626
+
627
+ def merge_bbox(bbox1, bbox2):
628
+ """
629
+ Merge two bboxes.
630
+
631
+ >>> merge_bbox((-10, 20, 0, 30), (30, -20, 90, 10))
632
+ (-10, -20, 90, 30)
633
+
634
+ """
635
+ minx = min(bbox1[0], bbox2[0])
636
+ miny = min(bbox1[1], bbox2[1])
637
+ maxx = max(bbox1[2], bbox2[2])
638
+ maxy = max(bbox1[3], bbox2[3])
639
+ return (minx, miny, maxx, maxy)
640
+
641
+
642
+ def bbox_equals(src_bbox, dst_bbox, x_delta=None, y_delta=None):
643
+ """
644
+ Compares two bbox and checks if they are equal, or nearly equal.
645
+
646
+ :param x_delta: how precise the comparison should be.
647
+ should be reasonable small, like a tenth of a pixel.
648
+ defaults to 1/1.000.000th of the width.
649
+ :type x_delta: bbox units
650
+
651
+ >>> src_bbox = (939258.20356824622, 6887893.4928338043,
652
+ ... 1095801.2374962866, 7044436.5267618448)
653
+ >>> dst_bbox = (939258.20260000182, 6887893.4908000007,
654
+ ... 1095801.2365000017, 7044436.5247000009)
655
+ >>> bbox_equals(src_bbox, dst_bbox, 61.1, 61.1)
656
+ True
657
+ >>> bbox_equals(src_bbox, dst_bbox, 0.0001)
658
+ False
659
+ """
660
+ if x_delta is None:
661
+ x_delta = abs(src_bbox[0] - src_bbox[2]) / 1000000.0
662
+ if y_delta is None:
663
+ y_delta = x_delta
664
+ return (abs(src_bbox[0] - dst_bbox[0]) < x_delta and
665
+ abs(src_bbox[1] - dst_bbox[1]) < x_delta and
666
+ abs(src_bbox[2] - dst_bbox[2]) < y_delta and
667
+ abs(src_bbox[3] - dst_bbox[3]) < y_delta)
668
+
669
+
670
+ def make_lin_transf(src_bbox, dst_bbox):
671
+ """
672
+ Create a transformation function that transforms linear between two
673
+ plane coordinate systems.
674
+ One needs to be cartesian (0, 0 at the lower left, x goes up) and one
675
+ needs to be an image coordinate system (0, 0 at the top left, x goes down).
676
+
677
+ :return: function that takes src x/y and returns dest x/y coordinates
678
+
679
+ >>> transf = make_lin_transf((7, 50, 8, 51), (0, 0, 500, 400))
680
+ >>> transf((7.5, 50.5))
681
+ (250.0, 200.0)
682
+ >>> transf((7.0, 50.0))
683
+ (0.0, 400.0)
684
+ >>> transf = make_lin_transf((7, 50, 8, 51), (200, 300, 700, 700))
685
+ >>> transf((7.5, 50.5))
686
+ (450.0, 500.0)
687
+ """
688
+ def func(x_y): return (dst_bbox[0] + (x_y[0] - src_bbox[0]) *
689
+ (dst_bbox[2]-dst_bbox[0]) / (src_bbox[2] - src_bbox[0]),
690
+ dst_bbox[1] + (src_bbox[3] - x_y[1]) *
691
+ (dst_bbox[3]-dst_bbox[1]) / (src_bbox[3] - src_bbox[1]))
692
+ return func
693
+
694
+
695
+ class PreferredSrcSRS(object):
696
+ def __init__(self):
697
+ self.target_proj = {}
698
+
699
+ def add(self, target, prefered_srs):
700
+ self.target_proj[target] = prefered_srs
701
+
702
+ def preferred_src(self, target, available_src):
703
+ if not available_src:
704
+ raise ValueError("no available src SRS")
705
+ if target in available_src:
706
+ return target
707
+ if target in self.target_proj:
708
+ for preferred in self.target_proj[target]:
709
+ if preferred in available_src:
710
+ return preferred
711
+
712
+ for avail in available_src:
713
+ if avail.is_latlong == target.is_latlong:
714
+ return avail
715
+ return available_src[0]
716
+
717
+
718
+ class SupportedSRS(object):
719
+ def __init__(self, supported_srs, preferred_srs=None):
720
+ self.supported_srs = supported_srs
721
+ self.preferred_srs = preferred_srs or PreferredSrcSRS()
722
+
723
+ def __iter__(self):
724
+ return iter(self.supported_srs)
725
+
726
+ def __contains__(self, srs):
727
+ return srs in self.supported_srs
728
+
729
+ def best_srs(self, target):
730
+ return self.preferred_srs.preferred_src(target, self.supported_srs)
731
+
732
+ def __eq__(self, other):
733
+ # .prefered_srs is set global, so we only compare .supported_srs
734
+ return self.supported_srs == other.supported_srs