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.
- MapProxy-2.1.0.dist-info/AUTHORS.txt +33 -0
- MapProxy-2.1.0.dist-info/COPYING.txt +60 -0
- MapProxy-2.1.0.dist-info/LICENSE.txt +202 -0
- MapProxy-2.1.0.dist-info/METADATA +165 -0
- MapProxy-2.1.0.dist-info/RECORD +459 -0
- MapProxy-2.1.0.dist-info/WHEEL +5 -0
- MapProxy-2.1.0.dist-info/entry_points.txt +4 -0
- MapProxy-2.1.0.dist-info/top_level.txt +1 -0
- mapproxy/__init__.py +0 -0
- mapproxy/cache/__init__.py +36 -0
- mapproxy/cache/azureblob.py +145 -0
- mapproxy/cache/base.py +120 -0
- mapproxy/cache/compact.py +665 -0
- mapproxy/cache/couchdb.py +301 -0
- mapproxy/cache/dummy.py +36 -0
- mapproxy/cache/file.py +200 -0
- mapproxy/cache/geopackage.py +647 -0
- mapproxy/cache/legend.py +87 -0
- mapproxy/cache/mbtiles.py +411 -0
- mapproxy/cache/meta.py +80 -0
- mapproxy/cache/path.py +261 -0
- mapproxy/cache/redis.py +152 -0
- mapproxy/cache/renderd.py +100 -0
- mapproxy/cache/riak.py +206 -0
- mapproxy/cache/s3.py +209 -0
- mapproxy/cache/tile.py +736 -0
- mapproxy/client/__init__.py +0 -0
- mapproxy/client/arcgis.py +82 -0
- mapproxy/client/cgi.py +141 -0
- mapproxy/client/http.py +295 -0
- mapproxy/client/log.py +33 -0
- mapproxy/client/tile.py +158 -0
- mapproxy/client/wms.py +255 -0
- mapproxy/compat/__init__.py +0 -0
- mapproxy/compat/image.py +86 -0
- mapproxy/config/__init__.py +22 -0
- mapproxy/config/config-schema.json +813 -0
- mapproxy/config/config.py +213 -0
- mapproxy/config/coverage.py +108 -0
- mapproxy/config/defaults.py +102 -0
- mapproxy/config/loader.py +2399 -0
- mapproxy/config/spec.py +657 -0
- mapproxy/config/validator.py +242 -0
- mapproxy/config_template/__init__.py +0 -0
- mapproxy/config_template/base_config/config.wsgi +10 -0
- mapproxy/config_template/base_config/full_example.yaml +598 -0
- mapproxy/config_template/base_config/full_seed_example.yaml +79 -0
- mapproxy/config_template/base_config/log.ini +35 -0
- mapproxy/config_template/base_config/mapproxy.yaml +60 -0
- mapproxy/config_template/base_config/seed.yaml +27 -0
- mapproxy/exception.py +149 -0
- mapproxy/featureinfo.py +251 -0
- mapproxy/grid.py +1199 -0
- mapproxy/image/__init__.py +549 -0
- mapproxy/image/fonts/DejaVuSans.ttf +0 -0
- mapproxy/image/fonts/DejaVuSansMono.ttf +0 -0
- mapproxy/image/fonts/LICENSE +99 -0
- mapproxy/image/fonts/__init__.py +0 -0
- mapproxy/image/mask.py +79 -0
- mapproxy/image/merge.py +323 -0
- mapproxy/image/message.py +357 -0
- mapproxy/image/opts.py +185 -0
- mapproxy/image/tile.py +171 -0
- mapproxy/image/transform.py +350 -0
- mapproxy/layer.py +489 -0
- mapproxy/multiapp.py +230 -0
- mapproxy/proj.py +309 -0
- mapproxy/request/__init__.py +18 -0
- mapproxy/request/arcgis.py +268 -0
- mapproxy/request/base.py +466 -0
- mapproxy/request/tile.py +131 -0
- mapproxy/request/wms/__init__.py +829 -0
- mapproxy/request/wms/exception.py +107 -0
- mapproxy/request/wmts.py +442 -0
- mapproxy/response.py +237 -0
- mapproxy/script/__init__.py +0 -0
- mapproxy/script/conf/__init__.py +0 -0
- mapproxy/script/conf/app.py +222 -0
- mapproxy/script/conf/caches.py +44 -0
- mapproxy/script/conf/geopackage.py +136 -0
- mapproxy/script/conf/layers.py +54 -0
- mapproxy/script/conf/seeds.py +36 -0
- mapproxy/script/conf/sources.py +88 -0
- mapproxy/script/conf/utils.py +148 -0
- mapproxy/script/defrag.py +187 -0
- mapproxy/script/export.py +337 -0
- mapproxy/script/grids.py +198 -0
- mapproxy/script/scales.py +134 -0
- mapproxy/script/util.py +410 -0
- mapproxy/script/wms_capabilities.py +160 -0
- mapproxy/seed/__init__.py +0 -0
- mapproxy/seed/cachelock.py +127 -0
- mapproxy/seed/cleanup.py +191 -0
- mapproxy/seed/config.py +481 -0
- mapproxy/seed/script.py +391 -0
- mapproxy/seed/seeder.py +551 -0
- mapproxy/seed/spec.py +66 -0
- mapproxy/seed/util.py +266 -0
- mapproxy/service/__init__.py +14 -0
- mapproxy/service/base.py +45 -0
- mapproxy/service/demo.py +364 -0
- mapproxy/service/kml.py +333 -0
- mapproxy/service/ows.py +39 -0
- mapproxy/service/template_helper.py +55 -0
- mapproxy/service/templates/demo/capabilities_demo.html +18 -0
- mapproxy/service/templates/demo/demo.html +181 -0
- mapproxy/service/templates/demo/openlayers-demo.cfg +16 -0
- mapproxy/service/templates/demo/static/img/blank.gif +0 -0
- mapproxy/service/templates/demo/static/img/east-mini.png +0 -0
- mapproxy/service/templates/demo/static/img/north-mini.png +0 -0
- mapproxy/service/templates/demo/static/img/south-mini.png +0 -0
- mapproxy/service/templates/demo/static/img/west-mini.png +0 -0
- mapproxy/service/templates/demo/static/img/zoom-minus-mini.png +0 -0
- mapproxy/service/templates/demo/static/img/zoom-plus-mini.png +0 -0
- mapproxy/service/templates/demo/static/img/zoom-world-mini.png +0 -0
- mapproxy/service/templates/demo/static/logo.png +0 -0
- mapproxy/service/templates/demo/static/ol.css +345 -0
- mapproxy/service/templates/demo/static/ol.js +4 -0
- mapproxy/service/templates/demo/static/proj4.min.js +1 -0
- mapproxy/service/templates/demo/static/proj4defs.js +1 -0
- mapproxy/service/templates/demo/static/site.css +137 -0
- mapproxy/service/templates/demo/static/theme/default/framedCloud.css +0 -0
- mapproxy/service/templates/demo/static/theme/default/google.css +17 -0
- mapproxy/service/templates/demo/static/theme/default/ie6-style.css +10 -0
- mapproxy/service/templates/demo/static/theme/default/style.css +482 -0
- mapproxy/service/templates/demo/static.html +34 -0
- mapproxy/service/templates/demo/tms_demo.html +117 -0
- mapproxy/service/templates/demo/wms_demo.html +144 -0
- mapproxy/service/templates/demo/wmts_demo.html +118 -0
- mapproxy/service/templates/tms_capabilities.xml +13 -0
- mapproxy/service/templates/tms_exception.xml +4 -0
- mapproxy/service/templates/tms_root_resource.xml +7 -0
- mapproxy/service/templates/tms_tilemap_capabilities.xml +14 -0
- mapproxy/service/templates/wms100capabilities.xml +112 -0
- mapproxy/service/templates/wms100exception.xml +4 -0
- mapproxy/service/templates/wms110capabilities.xml +152 -0
- mapproxy/service/templates/wms110exception.xml +5 -0
- mapproxy/service/templates/wms111capabilities.xml +183 -0
- mapproxy/service/templates/wms111exception.xml +5 -0
- mapproxy/service/templates/wms130capabilities.xml +326 -0
- mapproxy/service/templates/wms130exception.xml +8 -0
- mapproxy/service/templates/wmts100capabilities.xml +155 -0
- mapproxy/service/templates/wmts100exception.xml +9 -0
- mapproxy/service/tile.py +540 -0
- mapproxy/service/wms.py +868 -0
- mapproxy/service/wmts.py +387 -0
- mapproxy/source/__init__.py +83 -0
- mapproxy/source/arcgis.py +39 -0
- mapproxy/source/error.py +40 -0
- mapproxy/source/mapnik.py +262 -0
- mapproxy/source/tile.py +97 -0
- mapproxy/source/wms.py +273 -0
- mapproxy/srs.py +734 -0
- mapproxy/template.py +54 -0
- mapproxy/test/__init__.py +0 -0
- mapproxy/test/conftest.py +8 -0
- mapproxy/test/helper.py +255 -0
- mapproxy/test/http.py +511 -0
- mapproxy/test/image.py +219 -0
- mapproxy/test/mocker.py +2291 -0
- mapproxy/test/schemas/inspire/common/1.0/common.xsd +1461 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_bul.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_cze.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_dan.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_dut.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_eng.xsd +155 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_est.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_fin.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_fre.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_ger.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_gle.xsd +109 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_gre.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_hun.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_ita.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_lav.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_lit.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_mlt.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_pol.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_por.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_rum.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_slo.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_slv.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_spa.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_swe.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/network.xsd +521 -0
- mapproxy/test/schemas/inspire/inspire_vs/1.0/inspire_vs.xsd +19 -0
- mapproxy/test/schemas/kml/2.2.0/ReadMe.txt +14 -0
- mapproxy/test/schemas/kml/2.2.0/atom-author-link.xsd +66 -0
- mapproxy/test/schemas/kml/2.2.0/ogckml22.xsd +1646 -0
- mapproxy/test/schemas/kml/2.2.0/xAL.xsd +1680 -0
- mapproxy/test/schemas/ows/1.1.0/ReadMe.txt +87 -0
- mapproxy/test/schemas/ows/1.1.0/ows19115subset.xsd +235 -0
- mapproxy/test/schemas/ows/1.1.0/owsAll.xsd +23 -0
- mapproxy/test/schemas/ows/1.1.0/owsCommon.xsd +157 -0
- mapproxy/test/schemas/ows/1.1.0/owsContents.xsd +86 -0
- mapproxy/test/schemas/ows/1.1.0/owsDataIdentification.xsd +127 -0
- mapproxy/test/schemas/ows/1.1.0/owsDomainType.xsd +279 -0
- mapproxy/test/schemas/ows/1.1.0/owsExceptionReport.xsd +76 -0
- mapproxy/test/schemas/ows/1.1.0/owsGetCapabilities.xsd +112 -0
- mapproxy/test/schemas/ows/1.1.0/owsGetResourceByID.xsd +51 -0
- mapproxy/test/schemas/ows/1.1.0/owsInputOutputData.xsd +59 -0
- mapproxy/test/schemas/ows/1.1.0/owsManifest.xsd +125 -0
- mapproxy/test/schemas/ows/1.1.0/owsOperationsMetadata.xsd +140 -0
- mapproxy/test/schemas/ows/1.1.0/owsServiceIdentification.xsd +60 -0
- mapproxy/test/schemas/ows/1.1.0/owsServiceProvider.xsd +47 -0
- mapproxy/test/schemas/sld/1.1.0/sld_capabilities.xsd +27 -0
- mapproxy/test/schemas/wms/1.0.0/capabilities_1_0_0.dtd +353 -0
- mapproxy/test/schemas/wms/1.0.0/capabilities_1_0_0.xml +188 -0
- mapproxy/test/schemas/wms/1.0.7/capabilities_1_0_7.dtd +524 -0
- mapproxy/test/schemas/wms/1.0.7/capabilities_1_0_7.xml +260 -0
- mapproxy/test/schemas/wms/1.1.0/capabilities_1_1_0.dtd +273 -0
- mapproxy/test/schemas/wms/1.1.0/capabilities_1_1_0.xml +303 -0
- mapproxy/test/schemas/wms/1.1.0/exception_1_1_0.dtd +6 -0
- mapproxy/test/schemas/wms/1.1.0/exception_1_1_0.xml +33 -0
- mapproxy/test/schemas/wms/1.1.1/OGC-exception.xsd +68 -0
- mapproxy/test/schemas/wms/1.1.1/WMS_DescribeLayerResponse.dtd +22 -0
- mapproxy/test/schemas/wms/1.1.1/WMS_MS_Capabilities.dtd +274 -0
- mapproxy/test/schemas/wms/1.1.1/WMS_exception_1_1_1.dtd +5 -0
- mapproxy/test/schemas/wms/1.1.1/capabilities_1_1_1.dtd +276 -0
- mapproxy/test/schemas/wms/1.1.1/capabilities_1_1_1.xml +303 -0
- mapproxy/test/schemas/wms/1.1.1/exception_1_1_1.dtd +6 -0
- mapproxy/test/schemas/wms/1.1.1/exception_1_1_1.xml +33 -0
- mapproxy/test/schemas/wms/1.3.0/ReadMe.txt +8 -0
- mapproxy/test/schemas/wms/1.3.0/capabilities_1_3_0.xml +277 -0
- mapproxy/test/schemas/wms/1.3.0/capabilities_1_3_0.xsd +611 -0
- mapproxy/test/schemas/wms/1.3.0/exceptions_1_3_0.xml +34 -0
- mapproxy/test/schemas/wms/1.3.0/exceptions_1_3_0.xsd +28 -0
- mapproxy/test/schemas/wmsc/1.1.1/OGC-exception.xsd +68 -0
- mapproxy/test/schemas/wmsc/1.1.1/WMS_DescribeLayerResponse.dtd +22 -0
- mapproxy/test/schemas/wmsc/1.1.1/WMS_MS_Capabilities.dtd +283 -0
- mapproxy/test/schemas/wmsc/1.1.1/WMS_exception_1_1_1.dtd +5 -0
- mapproxy/test/schemas/wmsc/1.1.1/capabilities_1_1_1.dtd +276 -0
- mapproxy/test/schemas/wmsc/1.1.1/capabilities_1_1_1.xml +303 -0
- mapproxy/test/schemas/wmsc/1.1.1/exception_1_1_1.dtd +6 -0
- mapproxy/test/schemas/wmsc/1.1.1/exception_1_1_1.xml +33 -0
- mapproxy/test/schemas/wmts/1.0/ReadMe.txt +32 -0
- mapproxy/test/schemas/wmts/1.0/wmts.xsd +28 -0
- mapproxy/test/schemas/wmts/1.0/wmtsAbstract.wsdl +151 -0
- mapproxy/test/schemas/wmts/1.0/wmtsGetCapabilities_request.xsd +38 -0
- mapproxy/test/schemas/wmts/1.0/wmtsGetCapabilities_response.xsd +564 -0
- mapproxy/test/schemas/wmts/1.0/wmtsGetFeatureInfo_request.xsd +57 -0
- mapproxy/test/schemas/wmts/1.0/wmtsGetFeatureInfo_response.xsd +72 -0
- mapproxy/test/schemas/wmts/1.0/wmtsGetTile_request.xsd +91 -0
- mapproxy/test/schemas/wmts/1.0/wmtsKVP.xsd +76 -0
- mapproxy/test/schemas/wmts/1.0/wmtsPayload_response.xsd +70 -0
- mapproxy/test/schemas/xlink/1.0.0/ReadMe.txt +6 -0
- mapproxy/test/schemas/xlink/1.0.0/xlinks.xsd +122 -0
- mapproxy/test/schemas/xml.xsd +287 -0
- mapproxy/test/system/__init__.py +98 -0
- mapproxy/test/system/fixture/arcgis.yaml +57 -0
- mapproxy/test/system/fixture/auth.yaml +70 -0
- mapproxy/test/system/fixture/cache.mbtiles +0 -0
- mapproxy/test/system/fixture/cache_azureblob.yaml +59 -0
- mapproxy/test/system/fixture/cache_band_merge.yaml +73 -0
- mapproxy/test/system/fixture/cache_bulk_meta_tiles.yaml +24 -0
- mapproxy/test/system/fixture/cache_coverage.yaml +84 -0
- mapproxy/test/system/fixture/cache_data/dop_cache_EPSG3857/00/000/000/000/000/000/000.png +0 -0
- mapproxy/test/system/fixture/cache_data/wms_cache_EPSG900913/01/000/000/000/000/000/001.jpeg +0 -0
- mapproxy/test/system/fixture/cache_data/wms_cache_transparent_EPSG900913/01/000/000/000/000/000/001.png +0 -0
- mapproxy/test/system/fixture/cache_geopackage.yaml +56 -0
- mapproxy/test/system/fixture/cache_grid_names.yaml +50 -0
- mapproxy/test/system/fixture/cache_mbtiles.yaml +28 -0
- mapproxy/test/system/fixture/cache_s3.yaml +58 -0
- mapproxy/test/system/fixture/cache_source.yaml +81 -0
- mapproxy/test/system/fixture/combined_sources.yaml +130 -0
- mapproxy/test/system/fixture/coverage.yaml +77 -0
- mapproxy/test/system/fixture/demo.yaml +135 -0
- mapproxy/test/system/fixture/dimension.yaml +59 -0
- mapproxy/test/system/fixture/disable_storage.yaml +25 -0
- mapproxy/test/system/fixture/empty_ogrdata.geojson +1 -0
- mapproxy/test/system/fixture/formats.yaml +72 -0
- mapproxy/test/system/fixture/inspire.yaml +101 -0
- mapproxy/test/system/fixture/inspire_full.yaml +124 -0
- mapproxy/test/system/fixture/kml_layer.yaml +66 -0
- mapproxy/test/system/fixture/layer.yaml +260 -0
- mapproxy/test/system/fixture/layergroups.yaml +57 -0
- mapproxy/test/system/fixture/layergroups_root.yaml +106 -0
- mapproxy/test/system/fixture/legendgraphic.yaml +93 -0
- mapproxy/test/system/fixture/mapnik_source.yaml +66 -0
- mapproxy/test/system/fixture/mapproxy_export.yaml +12 -0
- mapproxy/test/system/fixture/mapserver.yaml +23 -0
- mapproxy/test/system/fixture/minimal_cgi.py +16 -0
- mapproxy/test/system/fixture/mixed_mode.yaml +49 -0
- mapproxy/test/system/fixture/multi_cache_layers.yaml +111 -0
- mapproxy/test/system/fixture/multiapp1.yaml +20 -0
- mapproxy/test/system/fixture/multiapp2.yaml +19 -0
- mapproxy/test/system/fixture/renderd_client.yaml +55 -0
- mapproxy/test/system/fixture/scalehints.yaml +70 -0
- mapproxy/test/system/fixture/seed.yaml +94 -0
- mapproxy/test/system/fixture/seed_mapproxy.yaml +39 -0
- mapproxy/test/system/fixture/seed_old.yaml +12 -0
- mapproxy/test/system/fixture/seed_timeouts.yaml +12 -0
- mapproxy/test/system/fixture/seed_timeouts_mapproxy.yaml +27 -0
- mapproxy/test/system/fixture/seedonly.yaml +51 -0
- mapproxy/test/system/fixture/sld.yaml +35 -0
- mapproxy/test/system/fixture/source_errors.yaml +84 -0
- mapproxy/test/system/fixture/source_errors_raise.yaml +82 -0
- mapproxy/test/system/fixture/tileservice_origin.yaml +26 -0
- mapproxy/test/system/fixture/tileservice_refresh.yaml +59 -0
- mapproxy/test/system/fixture/tilesource_minmax_res.yaml +22 -0
- mapproxy/test/system/fixture/util-conf-base-grids.yaml +5 -0
- mapproxy/test/system/fixture/util-conf-overwrite.yaml +13 -0
- mapproxy/test/system/fixture/util-conf-wms-111-cap.xml +90 -0
- mapproxy/test/system/fixture/util_grids.yaml +29 -0
- mapproxy/test/system/fixture/util_wms_capabilities111.xml +130 -0
- mapproxy/test/system/fixture/util_wms_capabilities130.xml +100 -0
- mapproxy/test/system/fixture/util_wms_capabilities_service_exception.xml +5 -0
- mapproxy/test/system/fixture/watermark.yaml +50 -0
- mapproxy/test/system/fixture/wms_srs_extent.yaml +39 -0
- mapproxy/test/system/fixture/wms_versions.yaml +38 -0
- mapproxy/test/system/fixture/wmts.yaml +134 -0
- mapproxy/test/system/fixture/wmts_dimensions.yaml +57 -0
- mapproxy/test/system/fixture/xslt_featureinfo.yaml +54 -0
- mapproxy/test/system/fixture/xslt_featureinfo_input.yaml +51 -0
- mapproxy/test/system/test_arcgis.py +156 -0
- mapproxy/test/system/test_auth.py +1133 -0
- mapproxy/test/system/test_behind_proxy.py +75 -0
- mapproxy/test/system/test_bulk_meta_tiles.py +106 -0
- mapproxy/test/system/test_cache_azureblob.py +127 -0
- mapproxy/test/system/test_cache_band_merge.py +103 -0
- mapproxy/test/system/test_cache_coverage.py +168 -0
- mapproxy/test/system/test_cache_geopackage.py +144 -0
- mapproxy/test/system/test_cache_grid_names.py +89 -0
- mapproxy/test/system/test_cache_mbtiles.py +85 -0
- mapproxy/test/system/test_cache_s3.py +115 -0
- mapproxy/test/system/test_cache_source.py +146 -0
- mapproxy/test/system/test_combined_sources.py +335 -0
- mapproxy/test/system/test_coverage.py +140 -0
- mapproxy/test/system/test_decorate_img.py +214 -0
- mapproxy/test/system/test_demo.py +56 -0
- mapproxy/test/system/test_demo_with_extra_service.py +57 -0
- mapproxy/test/system/test_dimensions.py +279 -0
- mapproxy/test/system/test_disable_storage.py +42 -0
- mapproxy/test/system/test_formats.py +219 -0
- mapproxy/test/system/test_inspire_vs.py +173 -0
- mapproxy/test/system/test_kml.py +264 -0
- mapproxy/test/system/test_layergroups.py +160 -0
- mapproxy/test/system/test_legendgraphic.py +308 -0
- mapproxy/test/system/test_mapnik.py +162 -0
- mapproxy/test/system/test_mapserver.py +83 -0
- mapproxy/test/system/test_mixed_mode_format.py +201 -0
- mapproxy/test/system/test_multi_cache_layers.py +169 -0
- mapproxy/test/system/test_multiapp.py +92 -0
- mapproxy/test/system/test_refresh.py +206 -0
- mapproxy/test/system/test_renderd_client.py +304 -0
- mapproxy/test/system/test_response_headers.py +54 -0
- mapproxy/test/system/test_scalehints.py +140 -0
- mapproxy/test/system/test_seed.py +425 -0
- mapproxy/test/system/test_seed_only.py +93 -0
- mapproxy/test/system/test_sld.py +120 -0
- mapproxy/test/system/test_source_errors.py +377 -0
- mapproxy/test/system/test_tilesource_minmax_res.py +54 -0
- mapproxy/test/system/test_tms.py +277 -0
- mapproxy/test/system/test_tms_origin.py +46 -0
- mapproxy/test/system/test_util_conf.py +434 -0
- mapproxy/test/system/test_util_export.py +210 -0
- mapproxy/test/system/test_util_grids.py +88 -0
- mapproxy/test/system/test_util_wms_capabilities.py +182 -0
- mapproxy/test/system/test_watermark.py +91 -0
- mapproxy/test/system/test_wms.py +1616 -0
- mapproxy/test/system/test_wms_srs_extent.py +165 -0
- mapproxy/test/system/test_wms_version.py +85 -0
- mapproxy/test/system/test_wmsc.py +116 -0
- mapproxy/test/system/test_wmts.py +334 -0
- mapproxy/test/system/test_wmts_dimensions.py +206 -0
- mapproxy/test/system/test_wmts_restful.py +198 -0
- mapproxy/test/system/test_xslt_featureinfo.py +423 -0
- mapproxy/test/test_http_helper.py +217 -0
- mapproxy/test/unit/__init__.py +0 -0
- mapproxy/test/unit/epsg +2 -0
- mapproxy/test/unit/polygons/polygons.dbf +0 -0
- mapproxy/test/unit/polygons/polygons.shp +0 -0
- mapproxy/test/unit/polygons/polygons.shx +0 -0
- mapproxy/test/unit/test_async.py +242 -0
- mapproxy/test/unit/test_auth.py +430 -0
- mapproxy/test/unit/test_cache.py +1356 -0
- mapproxy/test/unit/test_cache_azureblob.py +97 -0
- mapproxy/test/unit/test_cache_compact.py +324 -0
- mapproxy/test/unit/test_cache_couchdb.py +118 -0
- mapproxy/test/unit/test_cache_geopackage.py +256 -0
- mapproxy/test/unit/test_cache_redis.py +123 -0
- mapproxy/test/unit/test_cache_riak.py +80 -0
- mapproxy/test/unit/test_cache_s3.py +93 -0
- mapproxy/test/unit/test_cache_tile.py +477 -0
- mapproxy/test/unit/test_client.py +488 -0
- mapproxy/test/unit/test_client_arcgis.py +74 -0
- mapproxy/test/unit/test_client_cgi.py +140 -0
- mapproxy/test/unit/test_collections.py +116 -0
- mapproxy/test/unit/test_concat_legends.py +37 -0
- mapproxy/test/unit/test_conf_loader.py +1267 -0
- mapproxy/test/unit/test_conf_validator.py +427 -0
- mapproxy/test/unit/test_config.py +118 -0
- mapproxy/test/unit/test_decorate_img.py +185 -0
- mapproxy/test/unit/test_exceptions.py +270 -0
- mapproxy/test/unit/test_featureinfo.py +313 -0
- mapproxy/test/unit/test_file_lock_load.py +49 -0
- mapproxy/test/unit/test_geom.py +512 -0
- mapproxy/test/unit/test_grid.py +1279 -0
- mapproxy/test/unit/test_image.py +1051 -0
- mapproxy/test/unit/test_image_mask.py +181 -0
- mapproxy/test/unit/test_image_messages.py +209 -0
- mapproxy/test/unit/test_image_options.py +160 -0
- mapproxy/test/unit/test_isodate.py +118 -0
- mapproxy/test/unit/test_multiapp.py +163 -0
- mapproxy/test/unit/test_ogr_reader.py +51 -0
- mapproxy/test/unit/test_request.py +745 -0
- mapproxy/test/unit/test_request_wmts.py +178 -0
- mapproxy/test/unit/test_response.py +78 -0
- mapproxy/test/unit/test_seed.py +365 -0
- mapproxy/test/unit/test_seed_cachelock.py +91 -0
- mapproxy/test/unit/test_srs.py +215 -0
- mapproxy/test/unit/test_tiled_source.py +122 -0
- mapproxy/test/unit/test_tilefilter.py +31 -0
- mapproxy/test/unit/test_times.py +25 -0
- mapproxy/test/unit/test_timeutils.py +50 -0
- mapproxy/test/unit/test_util_conf_utils.py +75 -0
- mapproxy/test/unit/test_utils.py +476 -0
- mapproxy/test/unit/test_wms_capabilities.py +44 -0
- mapproxy/test/unit/test_wms_layer.py +113 -0
- mapproxy/test/unit/test_yaml.py +68 -0
- mapproxy/tilefilter.py +61 -0
- mapproxy/util/__init__.py +0 -0
- mapproxy/util/async_.py +229 -0
- mapproxy/util/collections.py +134 -0
- mapproxy/util/coverage.py +337 -0
- mapproxy/util/ext/__init__.py +14 -0
- mapproxy/util/ext/dictspec/__init__.py +1 -0
- mapproxy/util/ext/dictspec/spec.py +131 -0
- mapproxy/util/ext/dictspec/test/__init__.py +0 -0
- mapproxy/util/ext/dictspec/test/test_validator.py +278 -0
- mapproxy/util/ext/dictspec/validator.py +194 -0
- mapproxy/util/ext/local.py +198 -0
- mapproxy/util/ext/lockfile.py +140 -0
- mapproxy/util/ext/odict.py +321 -0
- mapproxy/util/ext/serving.py +491 -0
- mapproxy/util/ext/tempita/__init__.py +1093 -0
- mapproxy/util/ext/tempita/_looper.py +163 -0
- mapproxy/util/ext/tempita/string_utils.py +24 -0
- mapproxy/util/ext/wmsparse/__init__.py +3 -0
- mapproxy/util/ext/wmsparse/duration.py +600 -0
- mapproxy/util/ext/wmsparse/parse.py +307 -0
- mapproxy/util/ext/wmsparse/test/__init__.py +0 -0
- mapproxy/util/ext/wmsparse/test/test_parse.py +111 -0
- mapproxy/util/ext/wmsparse/test/test_util.py +23 -0
- mapproxy/util/ext/wmsparse/test/wms-example-111.xml +90 -0
- mapproxy/util/ext/wmsparse/test/wms-example-130.xml +120 -0
- mapproxy/util/ext/wmsparse/test/wms-large-111.xml +2114 -0
- mapproxy/util/ext/wmsparse/test/wms_nasa_cap.xml +386 -0
- mapproxy/util/ext/wmsparse/util.py +189 -0
- mapproxy/util/fs.py +164 -0
- mapproxy/util/geom.py +307 -0
- mapproxy/util/lib.py +117 -0
- mapproxy/util/lock.py +171 -0
- mapproxy/util/ogr.py +247 -0
- mapproxy/util/py.py +75 -0
- mapproxy/util/times.py +78 -0
- mapproxy/util/yaml.py +58 -0
- mapproxy/version.py +33 -0
- mapproxy/wsgiapp.py +167 -0
mapproxy/image/mask.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# This file is part of the MapProxy project.
|
|
2
|
+
# Copyright (C) 2012 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 mapproxy.compat.image import Image, ImageDraw
|
|
17
|
+
from mapproxy.srs import SRS, make_lin_transf
|
|
18
|
+
from mapproxy.image import ImageSource
|
|
19
|
+
from mapproxy.image.opts import create_image
|
|
20
|
+
from mapproxy.util.geom import flatten_to_polygons
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def mask_image_source_from_coverage(img_source, bbox, bbox_srs, coverage,
|
|
24
|
+
image_opts=None):
|
|
25
|
+
if image_opts is None:
|
|
26
|
+
image_opts = img_source.image_opts
|
|
27
|
+
img = img_source.as_image()
|
|
28
|
+
img = mask_image(img, bbox, bbox_srs, coverage)
|
|
29
|
+
result = create_image(img.size, image_opts)
|
|
30
|
+
result.paste(img, (0, 0), img)
|
|
31
|
+
return ImageSource(result, image_opts=image_opts)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def mask_image(img, bbox, bbox_srs, coverage):
|
|
35
|
+
geom = mask_polygons(bbox, SRS(bbox_srs), coverage)
|
|
36
|
+
mask = image_mask_from_geom(img.size, bbox, geom)
|
|
37
|
+
img = img.convert('RGBA')
|
|
38
|
+
img.paste((255, 255, 255, 0), (0, 0), mask)
|
|
39
|
+
return img
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def mask_polygons(bbox, bbox_srs, coverage):
|
|
43
|
+
coverage = coverage.transform_to(bbox_srs)
|
|
44
|
+
coverage = coverage.intersection(bbox, bbox_srs)
|
|
45
|
+
return flatten_to_polygons(coverage.geom)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def image_mask_from_geom(size, bbox, polygons):
|
|
49
|
+
mask = Image.new('L', size, 255)
|
|
50
|
+
if len(polygons) == 0:
|
|
51
|
+
return mask
|
|
52
|
+
|
|
53
|
+
transf = make_lin_transf(bbox, (0, 0) + size)
|
|
54
|
+
|
|
55
|
+
# use negative ~.1 pixel buffer
|
|
56
|
+
buffer = -0.1 * min((bbox[2] - bbox[0]) / size[0], (bbox[3] - bbox[1]) / size[1])
|
|
57
|
+
|
|
58
|
+
draw = ImageDraw.Draw(mask)
|
|
59
|
+
|
|
60
|
+
def draw_polygon(p):
|
|
61
|
+
draw.polygon([transf(coord) for coord in p.exterior.coords], fill=0)
|
|
62
|
+
for ring in p.interiors:
|
|
63
|
+
draw.polygon([transf(coord) for coord in ring.coords], fill=255)
|
|
64
|
+
|
|
65
|
+
for p in polygons:
|
|
66
|
+
# little bit smaller polygon does not include touched pixels outside coverage
|
|
67
|
+
buffered = p.buffer(buffer, resolution=1, join_style=2)
|
|
68
|
+
|
|
69
|
+
if buffered.is_empty: # can be empty after negative buffer
|
|
70
|
+
continue
|
|
71
|
+
|
|
72
|
+
if buffered.geom_type == 'MultiPolygon':
|
|
73
|
+
# negative buffer can turn polygon into multipolygon
|
|
74
|
+
for p in buffered:
|
|
75
|
+
draw_polygon(p)
|
|
76
|
+
else:
|
|
77
|
+
draw_polygon(buffered)
|
|
78
|
+
|
|
79
|
+
return mask
|
mapproxy/image/merge.py
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
# This file is part of the MapProxy project.
|
|
2
|
+
# Copyright (C) 2010-2016 Omniscale <http://omniscale.de>
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
"""
|
|
17
|
+
Image and tile manipulation (transforming, merging, etc).
|
|
18
|
+
"""
|
|
19
|
+
import json
|
|
20
|
+
from collections import namedtuple
|
|
21
|
+
from mapproxy.compat.image import Image, ImageColor, ImageChops, ImageMath
|
|
22
|
+
from mapproxy.compat.image import has_alpha_composite_support
|
|
23
|
+
from mapproxy.image import BlankImageSource, ImageSource
|
|
24
|
+
from mapproxy.image.opts import create_image, ImageOptions
|
|
25
|
+
from mapproxy.image.mask import mask_image
|
|
26
|
+
|
|
27
|
+
import logging
|
|
28
|
+
log = logging.getLogger('mapproxy.image')
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class LayerMerger(object):
|
|
32
|
+
"""
|
|
33
|
+
Merge multiple layers into one image.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(self):
|
|
37
|
+
self.layers = []
|
|
38
|
+
self.cacheable = True
|
|
39
|
+
|
|
40
|
+
def add(self, img, coverage=None):
|
|
41
|
+
"""
|
|
42
|
+
Add one layer image to merge. Bottom-layers first.
|
|
43
|
+
"""
|
|
44
|
+
if img is not None:
|
|
45
|
+
self.layers.append((img, coverage))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class LayerMerger(LayerMerger):
|
|
49
|
+
|
|
50
|
+
def merge(self, image_opts, size=None, bbox=None, bbox_srs=None, coverage=None):
|
|
51
|
+
"""
|
|
52
|
+
Merge the layers. If the format is not 'png' just return the last image.
|
|
53
|
+
|
|
54
|
+
:param format: The image format for the result.
|
|
55
|
+
:param size: The size for the merged output.
|
|
56
|
+
:rtype: `ImageSource`
|
|
57
|
+
"""
|
|
58
|
+
if not self.layers:
|
|
59
|
+
return BlankImageSource(size=size, image_opts=image_opts, cacheable=True)
|
|
60
|
+
if len(self.layers) == 1:
|
|
61
|
+
layer_img, layer_coverage = self.layers[0]
|
|
62
|
+
layer_opts = layer_img.image_opts
|
|
63
|
+
if (((layer_opts and not layer_opts.transparent) or image_opts.transparent)
|
|
64
|
+
and (not size or size == layer_img.size)
|
|
65
|
+
and (not layer_coverage or not layer_coverage.clip)
|
|
66
|
+
and not coverage):
|
|
67
|
+
# layer is opaque, no need to make transparent or add bgcolor
|
|
68
|
+
return layer_img
|
|
69
|
+
|
|
70
|
+
if size is None:
|
|
71
|
+
size = self.layers[0][0].size
|
|
72
|
+
|
|
73
|
+
cacheable = self.cacheable
|
|
74
|
+
result = create_image(size, image_opts)
|
|
75
|
+
for layer_img, layer_coverage in self.layers:
|
|
76
|
+
if not layer_img.cacheable:
|
|
77
|
+
cacheable = False
|
|
78
|
+
img = layer_img.as_image()
|
|
79
|
+
layer_image_opts = layer_img.image_opts
|
|
80
|
+
if layer_image_opts is None:
|
|
81
|
+
opacity = None
|
|
82
|
+
else:
|
|
83
|
+
opacity = layer_image_opts.opacity
|
|
84
|
+
|
|
85
|
+
if layer_coverage and layer_coverage.clip:
|
|
86
|
+
img = mask_image(img, bbox, bbox_srs, layer_coverage)
|
|
87
|
+
|
|
88
|
+
if result.mode != 'RGBA':
|
|
89
|
+
merge_composite = False
|
|
90
|
+
else:
|
|
91
|
+
merge_composite = has_alpha_composite_support()
|
|
92
|
+
|
|
93
|
+
if 'transparency' in img.info:
|
|
94
|
+
# non-paletted PNGs can have a fixed transparency value
|
|
95
|
+
# convert to RGBA to have full alpha
|
|
96
|
+
img = img.convert('RGBA')
|
|
97
|
+
|
|
98
|
+
if merge_composite:
|
|
99
|
+
if opacity is not None and opacity < 1.0:
|
|
100
|
+
# fade-out img to add opacity value
|
|
101
|
+
img = img.convert("RGBA")
|
|
102
|
+
alpha = img.split()[3]
|
|
103
|
+
alpha = ImageChops.multiply(
|
|
104
|
+
alpha,
|
|
105
|
+
ImageChops.constant(alpha, int(255 * opacity))
|
|
106
|
+
)
|
|
107
|
+
img.putalpha(alpha)
|
|
108
|
+
if img.mode in ('RGBA', 'P'):
|
|
109
|
+
# assume paletted images have transparency
|
|
110
|
+
if img.mode == 'P':
|
|
111
|
+
img = img.convert('RGBA')
|
|
112
|
+
result = Image.alpha_composite(result, img)
|
|
113
|
+
else:
|
|
114
|
+
result.paste(img, (0, 0))
|
|
115
|
+
else:
|
|
116
|
+
if opacity is not None and opacity < 1.0:
|
|
117
|
+
img = img.convert(result.mode)
|
|
118
|
+
result = Image.blend(result, img, layer_image_opts.opacity)
|
|
119
|
+
elif img.mode in ('RGBA', 'P'):
|
|
120
|
+
# assume paletted images have transparency
|
|
121
|
+
if img.mode == 'P':
|
|
122
|
+
img = img.convert('RGBA')
|
|
123
|
+
# paste w transparency mask from layer
|
|
124
|
+
result.paste(img, (0, 0), img)
|
|
125
|
+
else:
|
|
126
|
+
result.paste(img, (0, 0))
|
|
127
|
+
|
|
128
|
+
# apply global clip coverage
|
|
129
|
+
if coverage:
|
|
130
|
+
bg = create_image(size, image_opts)
|
|
131
|
+
mask = mask_image(result, bbox, bbox_srs, coverage)
|
|
132
|
+
bg.paste(result, (0, 0), mask)
|
|
133
|
+
result = bg
|
|
134
|
+
|
|
135
|
+
return ImageSource(result, size=size, image_opts=image_opts, cacheable=cacheable)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
band_ops = namedtuple("band_ops", ["dst_band", "src_img", "src_band", "factor"])
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class BandMerger(object):
|
|
142
|
+
"""
|
|
143
|
+
Merge bands from multiple sources into one image.
|
|
144
|
+
|
|
145
|
+
sources:
|
|
146
|
+
r: [{source: nir_cache, band: 0, factor: 0.4}, {source: dop_cache, band: 0, factor: 0.6}]
|
|
147
|
+
g: [{source: dop_cache, band: 2}]
|
|
148
|
+
b: [{source: dop_cache, band: 1}]
|
|
149
|
+
|
|
150
|
+
sources:
|
|
151
|
+
l: [
|
|
152
|
+
{source: dop_cache, band: 0, factor: 0.6},
|
|
153
|
+
{source: dop_cache, band: 1, factor: 0.3},
|
|
154
|
+
{source: dop_cache, band: 2, factor: 0.1},
|
|
155
|
+
]
|
|
156
|
+
"""
|
|
157
|
+
|
|
158
|
+
def __init__(self, mode=None):
|
|
159
|
+
self.ops = []
|
|
160
|
+
self.cacheable = True
|
|
161
|
+
self.mode = mode
|
|
162
|
+
self.max_band = {}
|
|
163
|
+
self.max_src_images = 0
|
|
164
|
+
|
|
165
|
+
def add_ops(self, dst_band, src_img, src_band, factor=1.0):
|
|
166
|
+
self.ops.append(band_ops(
|
|
167
|
+
dst_band=dst_band,
|
|
168
|
+
src_img=src_img,
|
|
169
|
+
src_band=src_band,
|
|
170
|
+
factor=factor,
|
|
171
|
+
))
|
|
172
|
+
# store highest requested band index for each source
|
|
173
|
+
self.max_band[src_img] = max(self.max_band.get(src_img, 0), src_band)
|
|
174
|
+
self.max_src_images = max(src_img+1, self.max_src_images)
|
|
175
|
+
|
|
176
|
+
def merge(self, sources, image_opts, size=None, bbox=None, bbox_srs=None, coverage=None):
|
|
177
|
+
if len(sources) < self.max_src_images:
|
|
178
|
+
return BlankImageSource(size=size, image_opts=image_opts, cacheable=True)
|
|
179
|
+
|
|
180
|
+
if size is None:
|
|
181
|
+
size = sources[0].size
|
|
182
|
+
|
|
183
|
+
# load src bands
|
|
184
|
+
src_img_bands = []
|
|
185
|
+
for i, layer_img in enumerate(sources):
|
|
186
|
+
img = layer_img.as_image()
|
|
187
|
+
|
|
188
|
+
if i not in self.max_band:
|
|
189
|
+
# do not split img if not requested by any op
|
|
190
|
+
src_img_bands.append(None)
|
|
191
|
+
continue
|
|
192
|
+
|
|
193
|
+
if self.max_band[i] == 3 and img.mode != 'RGBA':
|
|
194
|
+
# convert to RGBA if band idx 3 is requestd (e.g. P or RGB src)
|
|
195
|
+
img = img.convert('RGBA')
|
|
196
|
+
elif img.mode == 'P':
|
|
197
|
+
img = img.convert('RGB')
|
|
198
|
+
src_img_bands.append(img.split())
|
|
199
|
+
|
|
200
|
+
tmp_mode = self.mode
|
|
201
|
+
|
|
202
|
+
if tmp_mode == 'RGBA':
|
|
203
|
+
result_bands = [None, None, None, None]
|
|
204
|
+
elif tmp_mode == 'RGB':
|
|
205
|
+
result_bands = [None, None, None]
|
|
206
|
+
elif tmp_mode == 'L':
|
|
207
|
+
result_bands = [None]
|
|
208
|
+
else:
|
|
209
|
+
raise ValueError("unsupported destination mode %s", image_opts.mode)
|
|
210
|
+
|
|
211
|
+
for op in self.ops:
|
|
212
|
+
chan = src_img_bands[op.src_img][op.src_band]
|
|
213
|
+
if op.factor != 1.0:
|
|
214
|
+
chan = ImageMath.eval("convert(int(float(a) * %f), 'L')" % op.factor, a=chan)
|
|
215
|
+
if result_bands[op.dst_band] is None:
|
|
216
|
+
result_bands[op.dst_band] = chan
|
|
217
|
+
else:
|
|
218
|
+
result_bands[op.dst_band] = ImageChops.add(
|
|
219
|
+
result_bands[op.dst_band],
|
|
220
|
+
chan,
|
|
221
|
+
)
|
|
222
|
+
else:
|
|
223
|
+
result_bands[op.dst_band] = chan
|
|
224
|
+
|
|
225
|
+
for i, b in enumerate(result_bands):
|
|
226
|
+
if b is None:
|
|
227
|
+
# band not set
|
|
228
|
+
b = Image.new("L", size, 255 if i == 3 else 0)
|
|
229
|
+
result_bands[i] = b
|
|
230
|
+
|
|
231
|
+
result = Image.merge(tmp_mode, result_bands)
|
|
232
|
+
return ImageSource(result, size=size, image_opts=image_opts)
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def merge_images(layers, image_opts, size=None, bbox=None, bbox_srs=None, merger=None):
|
|
236
|
+
"""
|
|
237
|
+
Merge multiple images into one.
|
|
238
|
+
|
|
239
|
+
:param images: list of `ImageSource`, bottom image first
|
|
240
|
+
:param format: the format of the output `ImageSource`
|
|
241
|
+
:param size: size of the merged image, if ``None`` the size
|
|
242
|
+
of the first image is used
|
|
243
|
+
:param bbox: Bounding box
|
|
244
|
+
:param bbox_srs: Bounding box SRS
|
|
245
|
+
:param merger: Image merger
|
|
246
|
+
:rtype: `ImageSource`
|
|
247
|
+
"""
|
|
248
|
+
if merger is None:
|
|
249
|
+
merger = LayerMerger()
|
|
250
|
+
|
|
251
|
+
# BandMerger does not have coverage support, passing only images
|
|
252
|
+
if isinstance(merger, BandMerger):
|
|
253
|
+
sources = [x[0] if isinstance(x, tuple) else x for x in layers]
|
|
254
|
+
return merger.merge(sources, image_opts=image_opts, size=size, bbox=bbox, bbox_srs=bbox_srs)
|
|
255
|
+
|
|
256
|
+
for layer in layers:
|
|
257
|
+
if isinstance(layer, tuple):
|
|
258
|
+
merger.add(layer[0], layer[1])
|
|
259
|
+
else:
|
|
260
|
+
merger.add(layer)
|
|
261
|
+
|
|
262
|
+
return merger.merge(image_opts=image_opts, size=size, bbox=bbox, bbox_srs=bbox_srs)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def concat_legends(legends, format='png', size=None, bgcolor='#ffffff', transparent=True):
|
|
266
|
+
"""
|
|
267
|
+
Merge multiple legends into one
|
|
268
|
+
:param images: list of `ImageSource`, bottom image first
|
|
269
|
+
:param format: the format of the output `ImageSource`
|
|
270
|
+
:param size: size of the merged image, if ``None`` the size
|
|
271
|
+
will be calculated
|
|
272
|
+
:rtype: `ImageSource`
|
|
273
|
+
"""
|
|
274
|
+
if not legends:
|
|
275
|
+
return BlankImageSource(size=(1, 1), image_opts=ImageOptions(bgcolor=bgcolor, transparent=transparent))
|
|
276
|
+
if len(legends) == 1:
|
|
277
|
+
return legends[0]
|
|
278
|
+
|
|
279
|
+
legends = legends[:]
|
|
280
|
+
legends.reverse()
|
|
281
|
+
if size is None:
|
|
282
|
+
legend_width = 0
|
|
283
|
+
legend_height = 0
|
|
284
|
+
legend_position_y = []
|
|
285
|
+
# iterate through all legends, last to first, calc img size and remember the y-position
|
|
286
|
+
for legend in legends:
|
|
287
|
+
legend_position_y.append(legend_height)
|
|
288
|
+
tmp_img = legend.as_image()
|
|
289
|
+
legend_width = max(legend_width, tmp_img.size[0])
|
|
290
|
+
legend_height += tmp_img.size[1] # images shall not overlap themselfs
|
|
291
|
+
|
|
292
|
+
size = [legend_width, legend_height]
|
|
293
|
+
bgcolor = ImageColor.getrgb(bgcolor)
|
|
294
|
+
|
|
295
|
+
if transparent:
|
|
296
|
+
img = Image.new('RGBA', size, bgcolor+(0,))
|
|
297
|
+
else:
|
|
298
|
+
img = Image.new('RGB', size, bgcolor)
|
|
299
|
+
for i in range(len(legends)):
|
|
300
|
+
legend_img = legends[i].as_image()
|
|
301
|
+
if legend_img.mode == 'RGBA':
|
|
302
|
+
# paste w transparency mask from layer
|
|
303
|
+
img.paste(legend_img, (0, legend_position_y[i]), legend_img)
|
|
304
|
+
else:
|
|
305
|
+
img.paste(legend_img, (0, legend_position_y[i]))
|
|
306
|
+
return ImageSource(img, image_opts=ImageOptions(format=format))
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def concat_json_legends(legends):
|
|
310
|
+
"""
|
|
311
|
+
Merge multiple json legends into one
|
|
312
|
+
:param legends: list of HttpResponse objects
|
|
313
|
+
:rtype json formatted string
|
|
314
|
+
"""
|
|
315
|
+
legends_dict = {'Legend': []}
|
|
316
|
+
legends = legends[:]
|
|
317
|
+
for legend in legends:
|
|
318
|
+
legend_str = legend.read().decode('utf-8')
|
|
319
|
+
legend_dict = json.loads(legend_str)
|
|
320
|
+
if 'Legend' in legend_dict:
|
|
321
|
+
legend_dict = legend_dict['Legend'][0]
|
|
322
|
+
legends_dict['Legend'].append(legend_dict)
|
|
323
|
+
return json.dumps(legends_dict)
|