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.
- mapproxy/__init__.py +0 -0
- mapproxy/cache/__init__.py +36 -0
- mapproxy/cache/azureblob.py +145 -0
- mapproxy/cache/base.py +111 -0
- mapproxy/cache/compact.py +664 -0
- mapproxy/cache/couchdb.py +295 -0
- mapproxy/cache/dummy.py +34 -0
- mapproxy/cache/file.py +185 -0
- mapproxy/cache/geopackage.py +609 -0
- mapproxy/cache/legend.py +83 -0
- mapproxy/cache/mbtiles.py +392 -0
- mapproxy/cache/meta.py +78 -0
- mapproxy/cache/path.py +250 -0
- mapproxy/cache/redis.py +88 -0
- mapproxy/cache/renderd.py +95 -0
- mapproxy/cache/riak.py +202 -0
- mapproxy/cache/s3.py +177 -0
- mapproxy/cache/tile.py +699 -0
- mapproxy/client/__init__.py +0 -0
- mapproxy/client/arcgis.py +79 -0
- mapproxy/client/cgi.py +139 -0
- mapproxy/client/http.py +315 -0
- mapproxy/client/log.py +33 -0
- mapproxy/client/tile.py +150 -0
- mapproxy/client/wms.py +254 -0
- mapproxy/compat/__init__.py +46 -0
- mapproxy/compat/image.py +79 -0
- mapproxy/compat/itertools.py +29 -0
- mapproxy/compat/modules.py +13 -0
- mapproxy/config/__init__.py +22 -0
- mapproxy/config/config.py +201 -0
- mapproxy/config/coverage.py +107 -0
- mapproxy/config/defaults.py +98 -0
- mapproxy/config/loader.py +2286 -0
- mapproxy/config/spec.py +644 -0
- mapproxy/config/validator.py +239 -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 +593 -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 +142 -0
- mapproxy/featureinfo.py +252 -0
- mapproxy/grid.py +1170 -0
- mapproxy/image/__init__.py +536 -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 +75 -0
- mapproxy/image/merge.py +316 -0
- mapproxy/image/message.py +347 -0
- mapproxy/image/opts.py +182 -0
- mapproxy/image/tile.py +167 -0
- mapproxy/image/transform.py +350 -0
- mapproxy/layer.py +470 -0
- mapproxy/multiapp.py +231 -0
- mapproxy/proj.py +302 -0
- mapproxy/request/__init__.py +18 -0
- mapproxy/request/arcgis.py +259 -0
- mapproxy/request/base.py +476 -0
- mapproxy/request/tile.py +128 -0
- mapproxy/request/wms/__init__.py +793 -0
- mapproxy/request/wms/exception.py +99 -0
- mapproxy/request/wmts.py +436 -0
- mapproxy/response.py +237 -0
- mapproxy/script/__init__.py +0 -0
- mapproxy/script/conf/__init__.py +0 -0
- mapproxy/script/conf/app.py +195 -0
- mapproxy/script/conf/caches.py +45 -0
- mapproxy/script/conf/layers.py +54 -0
- mapproxy/script/conf/seeds.py +37 -0
- mapproxy/script/conf/sources.py +86 -0
- mapproxy/script/conf/utils.py +143 -0
- mapproxy/script/defrag.py +184 -0
- mapproxy/script/export.py +333 -0
- mapproxy/script/grids.py +188 -0
- mapproxy/script/scales.py +126 -0
- mapproxy/script/util.py +406 -0
- mapproxy/script/wms_capabilities.py +152 -0
- mapproxy/seed/__init__.py +0 -0
- mapproxy/seed/cachelock.py +121 -0
- mapproxy/seed/cleanup.py +187 -0
- mapproxy/seed/config.py +469 -0
- mapproxy/seed/script.py +388 -0
- mapproxy/seed/seeder.py +538 -0
- mapproxy/seed/spec.py +64 -0
- mapproxy/seed/util.py +254 -0
- mapproxy/service/__init__.py +14 -0
- mapproxy/service/base.py +46 -0
- mapproxy/service/demo.py +356 -0
- mapproxy/service/kml.py +331 -0
- mapproxy/service/ows.py +38 -0
- mapproxy/service/template_helper.py +53 -0
- mapproxy/service/templates/demo/capabilities_demo.html +16 -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 +103 -0
- mapproxy/service/templates/demo/wms_demo.html +140 -0
- mapproxy/service/templates/demo/wmts_demo.html +110 -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 +536 -0
- mapproxy/service/wms.py +851 -0
- mapproxy/service/wmts.py +381 -0
- mapproxy/source/__init__.py +75 -0
- mapproxy/source/arcgis.py +39 -0
- mapproxy/source/error.py +39 -0
- mapproxy/source/mapnik.py +259 -0
- mapproxy/source/tile.py +96 -0
- mapproxy/source/wms.py +270 -0
- mapproxy/srs.py +726 -0
- mapproxy/template.py +54 -0
- mapproxy/test/__init__.py +0 -0
- mapproxy/test/conftest.py +7 -0
- mapproxy/test/helper.py +247 -0
- mapproxy/test/http.py +494 -0
- mapproxy/test/image.py +210 -0
- mapproxy/test/mocker.py +2268 -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_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 +100 -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 +30 -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 +1134 -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_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 +106 -0
- mapproxy/test/system/test_demo_with_extra_service.py +53 -0
- mapproxy/test/system/test_dimensions.py +278 -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 +262 -0
- mapproxy/test/system/test_layergroups.py +160 -0
- mapproxy/test/system/test_legendgraphic.py +308 -0
- mapproxy/test/system/test_mapnik.py +161 -0
- mapproxy/test/system/test_mapserver.py +81 -0
- mapproxy/test/system/test_mixed_mode_format.py +195 -0
- mapproxy/test/system/test_multi_cache_layers.py +167 -0
- mapproxy/test/system/test_multiapp.py +92 -0
- mapproxy/test/system/test_refresh.py +207 -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 +422 -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 +276 -0
- mapproxy/test/system/test_tms_origin.py +46 -0
- mapproxy/test/system/test_util_conf.py +304 -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 +1611 -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 +425 -0
- mapproxy/test/test_http_helper.py +219 -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 +245 -0
- mapproxy/test/unit/test_auth.py +419 -0
- mapproxy/test/unit/test_cache.py +1193 -0
- mapproxy/test/unit/test_cache_azureblob.py +94 -0
- mapproxy/test/unit/test_cache_compact.py +319 -0
- mapproxy/test/unit/test_cache_couchdb.py +114 -0
- mapproxy/test/unit/test_cache_geopackage.py +221 -0
- mapproxy/test/unit/test_cache_redis.py +67 -0
- mapproxy/test/unit/test_cache_riak.py +76 -0
- mapproxy/test/unit/test_cache_s3.py +84 -0
- mapproxy/test/unit/test_cache_tile.py +427 -0
- mapproxy/test/unit/test_client.py +479 -0
- mapproxy/test/unit/test_client_arcgis.py +73 -0
- mapproxy/test/unit/test_client_cgi.py +136 -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 +1061 -0
- mapproxy/test/unit/test_conf_validator.py +416 -0
- mapproxy/test/unit/test_config.py +117 -0
- mapproxy/test/unit/test_decorate_img.py +185 -0
- mapproxy/test/unit/test_exceptions.py +258 -0
- mapproxy/test/unit/test_featureinfo.py +291 -0
- mapproxy/test/unit/test_file_lock_load.py +49 -0
- mapproxy/test/unit/test_geom.py +503 -0
- mapproxy/test/unit/test_grid.py +1258 -0
- mapproxy/test/unit/test_image.py +1053 -0
- mapproxy/test/unit/test_image_mask.py +181 -0
- mapproxy/test/unit/test_image_messages.py +197 -0
- mapproxy/test/unit/test_image_options.py +160 -0
- mapproxy/test/unit/test_isodate.py +122 -0
- mapproxy/test/unit/test_multiapp.py +163 -0
- mapproxy/test/unit/test_ogr_reader.py +50 -0
- mapproxy/test/unit/test_request.py +745 -0
- mapproxy/test/unit/test_request_wmts.py +178 -0
- mapproxy/test/unit/test_response.py +79 -0
- mapproxy/test/unit/test_seed.py +365 -0
- mapproxy/test/unit/test_seed_cachelock.py +90 -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 +69 -0
- mapproxy/tilefilter.py +59 -0
- mapproxy/util/__init__.py +0 -0
- mapproxy/util/async_.py +227 -0
- mapproxy/util/collections.py +132 -0
- mapproxy/util/coverage.py +329 -0
- mapproxy/util/escape.py +10 -0
- mapproxy/util/ext/__init__.py +14 -0
- mapproxy/util/ext/dictspec/__init__.py +1 -0
- mapproxy/util/ext/dictspec/spec.py +124 -0
- mapproxy/util/ext/dictspec/test/__init__.py +0 -0
- mapproxy/util/ext/dictspec/test/test_validator.py +274 -0
- mapproxy/util/ext/dictspec/validator.py +189 -0
- mapproxy/util/ext/local.py +196 -0
- mapproxy/util/ext/lockfile.py +138 -0
- mapproxy/util/ext/odict.py +330 -0
- mapproxy/util/ext/serving.py +508 -0
- mapproxy/util/ext/tempita/__init__.py +1174 -0
- mapproxy/util/ext/tempita/_looper.py +163 -0
- mapproxy/util/ext/tempita/compat3.py +46 -0
- mapproxy/util/ext/wmsparse/__init__.py +3 -0
- mapproxy/util/ext/wmsparse/duration.py +597 -0
- mapproxy/util/ext/wmsparse/parse.py +305 -0
- mapproxy/util/ext/wmsparse/test/__init__.py +0 -0
- mapproxy/util/ext/wmsparse/test/test_parse.py +162 -0
- mapproxy/util/ext/wmsparse/test/test_util.py +23 -0
- mapproxy/util/ext/wmsparse/test/wms-large-111.xml +2114 -0
- mapproxy/util/ext/wmsparse/test/wms-omniscale-111.xml +90 -0
- mapproxy/util/ext/wmsparse/test/wms-omniscale-130.xml +120 -0
- mapproxy/util/ext/wmsparse/test/wms_nasa_cap.xml +386 -0
- mapproxy/util/ext/wmsparse/util.py +187 -0
- mapproxy/util/fs.py +156 -0
- mapproxy/util/geom.py +295 -0
- mapproxy/util/lib.py +115 -0
- mapproxy/util/lock.py +163 -0
- mapproxy/util/ogr.py +231 -0
- mapproxy/util/py.py +81 -0
- mapproxy/util/times.py +75 -0
- mapproxy/util/yaml.py +56 -0
- mapproxy/version.py +31 -0
- mapproxy/wsgiapp.py +164 -0
- mapproxy-1.16.1.dist-info/METADATA +151 -0
- mapproxy-1.16.1.dist-info/RECORD +458 -0
- mapproxy-1.16.1.dist-info/WHEEL +5 -0
- mapproxy-1.16.1.dist-info/entry_points.txt +3 -0
- mapproxy-1.16.1.dist-info/licenses/AUTHORS.txt +33 -0
- mapproxy-1.16.1.dist-info/licenses/COPYING.txt +60 -0
- mapproxy-1.16.1.dist-info/licenses/LICENSE.txt +202 -0
- mapproxy-1.16.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,536 @@
|
|
|
1
|
+
# This file is part of the MapProxy project.
|
|
2
|
+
# Copyright (C) 2010 Omniscale <http://omniscale.de>
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
"""
|
|
17
|
+
Image and tile manipulation (transforming, merging, etc).
|
|
18
|
+
"""
|
|
19
|
+
import io
|
|
20
|
+
from io import BytesIO
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
from mapproxy.compat.image import Image, ImageChops, ImageFileDirectory_v2, TiffTags
|
|
24
|
+
from mapproxy.image.opts import create_image, ImageFormat
|
|
25
|
+
from mapproxy.config import base_config
|
|
26
|
+
from mapproxy.srs import make_lin_transf, get_epsg_num
|
|
27
|
+
from mapproxy.compat import string_type
|
|
28
|
+
|
|
29
|
+
import logging
|
|
30
|
+
from functools import reduce
|
|
31
|
+
log = logging.getLogger('mapproxy.image')
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
magic_bytes = [
|
|
36
|
+
('png', (b"\211PNG\r\n\032\n",)),
|
|
37
|
+
('jpeg', (b"\xFF\xD8",)),
|
|
38
|
+
('tiff', (b"MM\x00\x2a", b"II\x2a\x00",)),
|
|
39
|
+
('gif', (b"GIF87a", b"GIF89a",)),
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
def peek_image_format(buf):
|
|
43
|
+
buf.seek(0)
|
|
44
|
+
header = buf.read(10)
|
|
45
|
+
buf.seek(0)
|
|
46
|
+
for format, bytes in magic_bytes:
|
|
47
|
+
if header.startswith(bytes):
|
|
48
|
+
return format
|
|
49
|
+
return None
|
|
50
|
+
|
|
51
|
+
TIFF_MODELPIXELSCALETAG = 33550
|
|
52
|
+
TIFF_MODELTIEPOINTTAG = 33922
|
|
53
|
+
TIFF_GEOKEYDIRECTORYTAG = 34735
|
|
54
|
+
|
|
55
|
+
class GeoReference(object):
|
|
56
|
+
def __init__(self, bbox, srs):
|
|
57
|
+
self.bbox = bbox
|
|
58
|
+
self.srs = srs
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def tiepoints(self):
|
|
62
|
+
return (
|
|
63
|
+
0.0, 0.0, 0.0,
|
|
64
|
+
self.bbox[0], self.bbox[3], 0.0,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
def pixelscale(self, img_size):
|
|
68
|
+
width = self.bbox[2] - self.bbox[0]
|
|
69
|
+
height = self.bbox[3] - self.bbox[1]
|
|
70
|
+
return (
|
|
71
|
+
float(width)/img_size[0], float(height)/img_size[1], 0.0,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
def tiff_tags(self, img_size):
|
|
75
|
+
tags = ImageFileDirectory_v2()
|
|
76
|
+
tags[TIFF_MODELPIXELSCALETAG] = self.pixelscale(img_size)
|
|
77
|
+
tags.tagtype[TIFF_MODELPIXELSCALETAG] = TiffTags.DOUBLE
|
|
78
|
+
tags[TIFF_MODELTIEPOINTTAG] = self.tiepoints()
|
|
79
|
+
tags.tagtype[TIFF_MODELTIEPOINTTAG] = TiffTags.DOUBLE
|
|
80
|
+
|
|
81
|
+
model_type = 2 if self.srs.is_latlong else 1
|
|
82
|
+
tags[TIFF_GEOKEYDIRECTORYTAG] = (
|
|
83
|
+
1, 1, 0, 4, # {KeyDirectoryVersion, KeyRevision, MinorRevision, NumberOfKeys}
|
|
84
|
+
1024, 0, 1, model_type, # 1 projected, 2 geographic (lat/long)
|
|
85
|
+
1025, 0, 1, 1, # 1 RasterIsArea, 2 RasterIsPoint
|
|
86
|
+
3072, 0, 1, get_epsg_num(self.srs.srs_code),
|
|
87
|
+
)
|
|
88
|
+
tags.tagtype[TIFF_GEOKEYDIRECTORYTAG] = TiffTags.SHORT
|
|
89
|
+
return tags
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class BaseImageSource(object):
|
|
93
|
+
"""
|
|
94
|
+
Virtual parent class for ImageSource and BlankImageSource
|
|
95
|
+
"""
|
|
96
|
+
def __init__(self):
|
|
97
|
+
raise Exception("Virtual class BaseImageSource, cannot be instanciated.")
|
|
98
|
+
|
|
99
|
+
def as_image(self):
|
|
100
|
+
raise Exception("Virtual class BaseImageSource, method as_image cannot be called.")
|
|
101
|
+
|
|
102
|
+
def as_buffer(self, image_opts=None, format=None, seekable=False):
|
|
103
|
+
raise Exception("Virtual class BaseImageSource, method as_buffer cannot be called.")
|
|
104
|
+
|
|
105
|
+
def close_buffers(self):
|
|
106
|
+
pass
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class ImageSource(BaseImageSource):
|
|
110
|
+
"""
|
|
111
|
+
This class wraps either a PIL image, a file-like object, or a file name.
|
|
112
|
+
You can access the result as an image (`as_image` ) or a file-like buffer
|
|
113
|
+
object (`as_buffer`).
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
def __init__(self, source, size=None, image_opts=None, cacheable=True, georef=None):
|
|
117
|
+
"""
|
|
118
|
+
:param source: the image
|
|
119
|
+
:type source: PIL `Image`, image file object, or filename
|
|
120
|
+
:param format: the format of the ``source``
|
|
121
|
+
:param size: the size of the ``source`` in pixel
|
|
122
|
+
"""
|
|
123
|
+
self._img = None
|
|
124
|
+
self._buf = None
|
|
125
|
+
self._fname = None
|
|
126
|
+
self.source = source
|
|
127
|
+
self.image_opts = image_opts
|
|
128
|
+
self._size = size
|
|
129
|
+
self.cacheable = cacheable
|
|
130
|
+
self.georef = georef
|
|
131
|
+
self.authorize_stale = False
|
|
132
|
+
|
|
133
|
+
@property
|
|
134
|
+
def source(self):
|
|
135
|
+
return self._img or self._buf or self._fname
|
|
136
|
+
|
|
137
|
+
@source.setter
|
|
138
|
+
def source(self, source):
|
|
139
|
+
self._img = None
|
|
140
|
+
self._buf = None
|
|
141
|
+
if isinstance(source, string_type):
|
|
142
|
+
self._fname = source
|
|
143
|
+
elif isinstance(source, Image.Image):
|
|
144
|
+
self._img = source
|
|
145
|
+
else:
|
|
146
|
+
self._buf = source
|
|
147
|
+
|
|
148
|
+
def close_buffers(self):
|
|
149
|
+
if self._buf:
|
|
150
|
+
try:
|
|
151
|
+
self._buf.close()
|
|
152
|
+
except IOError:
|
|
153
|
+
pass
|
|
154
|
+
|
|
155
|
+
@property
|
|
156
|
+
def filename(self):
|
|
157
|
+
return self._fname
|
|
158
|
+
|
|
159
|
+
def as_image(self):
|
|
160
|
+
"""
|
|
161
|
+
Returns the image or the loaded image.
|
|
162
|
+
|
|
163
|
+
:rtype: PIL `Image`
|
|
164
|
+
"""
|
|
165
|
+
if not self._img:
|
|
166
|
+
self._make_seekable_buf()
|
|
167
|
+
log.debug('file(%s) -> image', self._fname or self._buf)
|
|
168
|
+
|
|
169
|
+
try:
|
|
170
|
+
img = Image.open(self._buf)
|
|
171
|
+
except Exception:
|
|
172
|
+
self.close_buffers()
|
|
173
|
+
raise
|
|
174
|
+
self._img = img
|
|
175
|
+
if self.image_opts and self.image_opts.transparent and self._img.mode == 'P':
|
|
176
|
+
self._img = self._img.convert('RGBA')
|
|
177
|
+
return self._img
|
|
178
|
+
|
|
179
|
+
def _make_seekable_buf(self):
|
|
180
|
+
if not self._buf and self._fname:
|
|
181
|
+
self._buf = open(self._fname, 'rb')
|
|
182
|
+
else:
|
|
183
|
+
try:
|
|
184
|
+
self._buf.seek(0)
|
|
185
|
+
except (io.UnsupportedOperation, AttributeError):
|
|
186
|
+
# PIL needs file objects with seek
|
|
187
|
+
self._buf = BytesIO(self._buf.read())
|
|
188
|
+
|
|
189
|
+
def _make_readable_buf(self):
|
|
190
|
+
if not self._buf and self._fname:
|
|
191
|
+
self._buf = open(self._fname, 'rb')
|
|
192
|
+
elif not hasattr(self._buf, 'seek'):
|
|
193
|
+
if not isinstance(self._buf, ReadBufWrapper):
|
|
194
|
+
self._buf = ReadBufWrapper(self._buf)
|
|
195
|
+
else:
|
|
196
|
+
try:
|
|
197
|
+
self._buf.seek(0)
|
|
198
|
+
except (io.UnsupportedOperation, AttributeError):
|
|
199
|
+
# PIL needs file objects with seek
|
|
200
|
+
self._buf = BytesIO(self._buf.read())
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def as_buffer(self, image_opts=None, format=None, seekable=False):
|
|
204
|
+
"""
|
|
205
|
+
Returns the image as a file object.
|
|
206
|
+
|
|
207
|
+
:param format: The format to encode an image.
|
|
208
|
+
Existing files will not be re-encoded.
|
|
209
|
+
:rtype: file-like object
|
|
210
|
+
"""
|
|
211
|
+
if format:
|
|
212
|
+
image_opts = (image_opts or self.image_opts).copy()
|
|
213
|
+
image_opts.format = ImageFormat(format)
|
|
214
|
+
if not self._buf and not self._fname:
|
|
215
|
+
if image_opts is None:
|
|
216
|
+
image_opts = self.image_opts
|
|
217
|
+
log.debug('image -> buf(%s)' % (image_opts.format,))
|
|
218
|
+
self._buf = img_to_buf(self._img, image_opts=image_opts, georef=self.georef)
|
|
219
|
+
else:
|
|
220
|
+
self._make_seekable_buf() if seekable else self._make_readable_buf()
|
|
221
|
+
if self.image_opts and image_opts and not self.image_opts.format and image_opts.format:
|
|
222
|
+
# need actual image_opts.format for next check
|
|
223
|
+
self.image_opts = self.image_opts.copy()
|
|
224
|
+
self.image_opts.format = peek_image_format(self._buf)
|
|
225
|
+
if self.image_opts and image_opts and self.image_opts.format != image_opts.format:
|
|
226
|
+
log.debug('converting image from %s -> %s' % (self.image_opts, image_opts))
|
|
227
|
+
self.source = self.as_image()
|
|
228
|
+
self._buf = None
|
|
229
|
+
self.image_opts = image_opts
|
|
230
|
+
# hide fname to prevent as_buffer from reading the file
|
|
231
|
+
fname = self._fname
|
|
232
|
+
self._fname = None
|
|
233
|
+
self.as_buffer(image_opts)
|
|
234
|
+
self._fname = fname
|
|
235
|
+
return self._buf
|
|
236
|
+
|
|
237
|
+
@property
|
|
238
|
+
def size(self):
|
|
239
|
+
if self._size is None:
|
|
240
|
+
self._size = self.as_image().size
|
|
241
|
+
return self._size
|
|
242
|
+
|
|
243
|
+
def SubImageSource(source, size, offset, image_opts, cacheable=True):
|
|
244
|
+
"""
|
|
245
|
+
Create a new ImageSource with `size` and `image_opts` and
|
|
246
|
+
place `source` image at `offset`.
|
|
247
|
+
"""
|
|
248
|
+
# force new image to contain alpha channel
|
|
249
|
+
new_image_opts = image_opts.copy()
|
|
250
|
+
new_image_opts.transparent = True
|
|
251
|
+
img = create_image(size, new_image_opts)
|
|
252
|
+
|
|
253
|
+
if not hasattr(source, 'as_image'):
|
|
254
|
+
source = ImageSource(source)
|
|
255
|
+
subimg = source.as_image()
|
|
256
|
+
img.paste(subimg, offset)
|
|
257
|
+
return ImageSource(img, size=size, image_opts=new_image_opts, cacheable=cacheable)
|
|
258
|
+
|
|
259
|
+
class BlankImageSource(BaseImageSource):
|
|
260
|
+
"""
|
|
261
|
+
ImageSource for transparent or solid-color images.
|
|
262
|
+
Implements optimized as_buffer() method.
|
|
263
|
+
"""
|
|
264
|
+
def __init__(self, size, image_opts, cacheable=False):
|
|
265
|
+
self.size = size
|
|
266
|
+
self.image_opts = image_opts
|
|
267
|
+
self._buf = None
|
|
268
|
+
self._img = None
|
|
269
|
+
self.cacheable = cacheable
|
|
270
|
+
self.authorize_stale = False
|
|
271
|
+
|
|
272
|
+
def as_image(self):
|
|
273
|
+
if not self._img:
|
|
274
|
+
self._img = create_image(self.size, self.image_opts)
|
|
275
|
+
return self._img
|
|
276
|
+
|
|
277
|
+
def as_buffer(self, image_opts=None, format=None, seekable=False):
|
|
278
|
+
if not self._buf:
|
|
279
|
+
image_opts = (image_opts or self.image_opts).copy()
|
|
280
|
+
if format:
|
|
281
|
+
image_opts.format = ImageFormat(format)
|
|
282
|
+
image_opts.colors = 0
|
|
283
|
+
self._buf = img_to_buf(self.as_image(), image_opts=image_opts)
|
|
284
|
+
return self._buf
|
|
285
|
+
|
|
286
|
+
def close_buffers(self):
|
|
287
|
+
pass
|
|
288
|
+
|
|
289
|
+
class ReadBufWrapper(object):
|
|
290
|
+
"""
|
|
291
|
+
This class wraps everything with a ``read`` method and adds support
|
|
292
|
+
for ``seek``, etc. A call to everything but ``read`` will create a
|
|
293
|
+
StringIO object of the ``readbuf``.
|
|
294
|
+
"""
|
|
295
|
+
def __init__(self, readbuf):
|
|
296
|
+
self.ok_to_seek = False
|
|
297
|
+
self.readbuf = readbuf
|
|
298
|
+
self.stringio = None
|
|
299
|
+
|
|
300
|
+
def read(self, *args, **kw):
|
|
301
|
+
if self.stringio:
|
|
302
|
+
return self.stringio.read(*args, **kw)
|
|
303
|
+
return self.readbuf.read(*args, **kw)
|
|
304
|
+
|
|
305
|
+
def __iter__(self):
|
|
306
|
+
if self.stringio:
|
|
307
|
+
return iter(self.stringio)
|
|
308
|
+
else:
|
|
309
|
+
return iter(self.readbuf)
|
|
310
|
+
|
|
311
|
+
def __getattr__(self, name):
|
|
312
|
+
if self.stringio is None:
|
|
313
|
+
if hasattr(self.readbuf, name):
|
|
314
|
+
return getattr(self.readbuf, name)
|
|
315
|
+
elif name == '__length_hint__':
|
|
316
|
+
raise AttributeError
|
|
317
|
+
self.ok_to_seek = True
|
|
318
|
+
self.stringio = BytesIO(self.readbuf.read())
|
|
319
|
+
return getattr(self.stringio, name)
|
|
320
|
+
|
|
321
|
+
def img_has_transparency(img):
|
|
322
|
+
if img.mode == 'P':
|
|
323
|
+
if img.info.get('transparency', False):
|
|
324
|
+
return True
|
|
325
|
+
# convert to RGBA and check alpha channel
|
|
326
|
+
img = img.convert('RGBA')
|
|
327
|
+
if img.mode == 'RGBA':
|
|
328
|
+
# any alpha except fully opaque
|
|
329
|
+
return any(img.histogram()[-256:-1])
|
|
330
|
+
return False
|
|
331
|
+
|
|
332
|
+
def img_to_buf(img, image_opts, georef=None):
|
|
333
|
+
defaults = {}
|
|
334
|
+
image_opts = image_opts.copy()
|
|
335
|
+
|
|
336
|
+
# convert I or L images to target mode
|
|
337
|
+
if image_opts.mode and img.mode[0] in ('I', 'L') and img.mode != image_opts.mode:
|
|
338
|
+
img = img.convert(image_opts.mode)
|
|
339
|
+
|
|
340
|
+
if (image_opts.colors is None and base_config().image.paletted
|
|
341
|
+
and image_opts.format.endswith('png')):
|
|
342
|
+
# force 255 colors for png with globals.image.paletted
|
|
343
|
+
image_opts.colors = 255
|
|
344
|
+
|
|
345
|
+
format = filter_format(image_opts.format.ext)
|
|
346
|
+
if format == 'mixed':
|
|
347
|
+
if img_has_transparency(img):
|
|
348
|
+
format = 'png'
|
|
349
|
+
image_opts.transparent = True
|
|
350
|
+
else:
|
|
351
|
+
format = 'jpeg'
|
|
352
|
+
image_opts.colors = None
|
|
353
|
+
image_opts.transparent = False
|
|
354
|
+
|
|
355
|
+
# quantize if colors is set, but not if we already have a paletted image
|
|
356
|
+
if image_opts.colors and not (img.mode == 'P' and len(img.getpalette()) == image_opts.colors*3):
|
|
357
|
+
quantizer = None
|
|
358
|
+
if 'quantizer' in image_opts.encoding_options:
|
|
359
|
+
quantizer = image_opts.encoding_options['quantizer']
|
|
360
|
+
if image_opts.transparent:
|
|
361
|
+
img = quantize(img, colors=image_opts.colors, alpha=True,
|
|
362
|
+
defaults=defaults, quantizer=quantizer)
|
|
363
|
+
else:
|
|
364
|
+
img = quantize(img, colors=image_opts.colors,
|
|
365
|
+
quantizer=quantizer)
|
|
366
|
+
if hasattr(Image, 'RLE'):
|
|
367
|
+
defaults['compress_type'] = Image.RLE
|
|
368
|
+
|
|
369
|
+
buf = BytesIO()
|
|
370
|
+
if format == 'jpeg':
|
|
371
|
+
img = img.convert('RGB')
|
|
372
|
+
if 'jpeg_quality' in image_opts.encoding_options:
|
|
373
|
+
defaults['quality'] = image_opts.encoding_options['jpeg_quality']
|
|
374
|
+
else:
|
|
375
|
+
defaults['quality'] = base_config().image.jpeg_quality
|
|
376
|
+
|
|
377
|
+
elif format == 'tiff':
|
|
378
|
+
if georef:
|
|
379
|
+
tags = georef.tiff_tags(img.size)
|
|
380
|
+
defaults['tiffinfo'] = tags
|
|
381
|
+
if 'tiff_compression' in image_opts.encoding_options:
|
|
382
|
+
defaults['compression'] = image_opts.encoding_options['tiff_compression']
|
|
383
|
+
if defaults['compression'] == 'jpeg':
|
|
384
|
+
if 'jpeg_quality' in image_opts.encoding_options:
|
|
385
|
+
defaults['quality'] = image_opts.encoding_options['jpeg_quality']
|
|
386
|
+
|
|
387
|
+
# unsupported transparency tuple can still be in non-RGB img.infos
|
|
388
|
+
# see: https://github.com/python-pillow/Pillow/pull/2633
|
|
389
|
+
if format == 'png' and img.mode != 'RGB' and 'transparency' in img.info and isinstance(img.info['transparency'], tuple):
|
|
390
|
+
del img.info['transparency']
|
|
391
|
+
|
|
392
|
+
img.save(buf, format, **defaults)
|
|
393
|
+
buf.seek(0)
|
|
394
|
+
return buf
|
|
395
|
+
|
|
396
|
+
def quantize(img, colors=256, alpha=False, defaults=None, quantizer=None):
|
|
397
|
+
if hasattr(Image, 'FASTOCTREE') and quantizer in (None, 'fastoctree'):
|
|
398
|
+
if not alpha:
|
|
399
|
+
img = img.convert('RGB')
|
|
400
|
+
try:
|
|
401
|
+
if img.mode == 'P':
|
|
402
|
+
# quantize with alpha does not work with P images
|
|
403
|
+
img = img.convert('RGBA')
|
|
404
|
+
img = img.quantize(colors, Image.FASTOCTREE)
|
|
405
|
+
except ValueError:
|
|
406
|
+
pass
|
|
407
|
+
else:
|
|
408
|
+
if alpha and img.mode == 'RGBA':
|
|
409
|
+
img.load() # split might fail if image is not loaded
|
|
410
|
+
alpha = img.split()[3]
|
|
411
|
+
img = img.convert('RGB').convert('P', palette=Image.ADAPTIVE, colors=colors-1)
|
|
412
|
+
mask = Image.eval(alpha, lambda a: 255 if a <=128 else 0)
|
|
413
|
+
img.paste(255, mask)
|
|
414
|
+
if defaults is not None:
|
|
415
|
+
defaults['transparency'] = 255
|
|
416
|
+
else:
|
|
417
|
+
img = img.convert('RGB').convert('P', palette=Image.ADAPTIVE, colors=colors)
|
|
418
|
+
|
|
419
|
+
return img
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def filter_format(format):
|
|
423
|
+
if format.lower() == 'geotiff':
|
|
424
|
+
format = 'tiff'
|
|
425
|
+
if format.lower().startswith('png'):
|
|
426
|
+
format = 'png'
|
|
427
|
+
return format
|
|
428
|
+
|
|
429
|
+
image_filter = {
|
|
430
|
+
'nearest': Image.NEAREST,
|
|
431
|
+
'bilinear': Image.BILINEAR,
|
|
432
|
+
'bicubic': Image.BICUBIC
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
def is_single_color_image(image):
|
|
437
|
+
"""
|
|
438
|
+
Checks if the `image` contains only one color.
|
|
439
|
+
Returns ``False`` if it contains more than one color, else
|
|
440
|
+
the color-tuple of the single color.
|
|
441
|
+
"""
|
|
442
|
+
result = image.getcolors(1)
|
|
443
|
+
# returns a list of (count, color), limit to one
|
|
444
|
+
if result is None:
|
|
445
|
+
return False
|
|
446
|
+
|
|
447
|
+
color = result[0][1]
|
|
448
|
+
if image.mode == 'P':
|
|
449
|
+
palette = image.getpalette()
|
|
450
|
+
return palette[color*3], palette[color*3+1], palette[color*3+2]
|
|
451
|
+
|
|
452
|
+
return result[0][1]
|
|
453
|
+
|
|
454
|
+
def make_transparent(img, color, tolerance=10):
|
|
455
|
+
"""
|
|
456
|
+
Create alpha channel for the given image and make each pixel
|
|
457
|
+
in `color` full transparent.
|
|
458
|
+
|
|
459
|
+
Returns an RGBA ImageSoruce.
|
|
460
|
+
|
|
461
|
+
Modifies the image in-place, unless it needs to be converted
|
|
462
|
+
first (P->RGB).
|
|
463
|
+
|
|
464
|
+
:param color: RGB color tuple
|
|
465
|
+
:param tolerance: tolerance applied to each color value
|
|
466
|
+
"""
|
|
467
|
+
result = _make_transparent(img.as_image(), color, tolerance)
|
|
468
|
+
image_opts = img.image_opts.copy()
|
|
469
|
+
image_opts.transparent = True
|
|
470
|
+
image_opts.mode = 'RGBA'
|
|
471
|
+
return ImageSource(result, size=result.size, image_opts=image_opts)
|
|
472
|
+
|
|
473
|
+
def _make_transparent(img, color, tolerance=10):
|
|
474
|
+
img.load()
|
|
475
|
+
|
|
476
|
+
if img.mode == 'P':
|
|
477
|
+
img = img.convert('RGBA')
|
|
478
|
+
|
|
479
|
+
channels = img.split()
|
|
480
|
+
mask_channels = []
|
|
481
|
+
for ch, c in zip(channels, color):
|
|
482
|
+
# create bit mask for each matched color
|
|
483
|
+
low_c, high_c = c-tolerance, c+tolerance
|
|
484
|
+
mask_channels.append(Image.eval(ch, lambda x: 255 if low_c <= x <= high_c else 0))
|
|
485
|
+
|
|
486
|
+
# multiply channel bit masks to get a single mask
|
|
487
|
+
alpha = reduce(ImageChops.multiply, mask_channels)
|
|
488
|
+
# invert to get alpha channel
|
|
489
|
+
alpha = ImageChops.invert(alpha)
|
|
490
|
+
|
|
491
|
+
if len(channels) == 4:
|
|
492
|
+
# multiply with existing alpha
|
|
493
|
+
alpha = ImageChops.multiply(alpha, channels[-1])
|
|
494
|
+
|
|
495
|
+
img.putalpha(alpha)
|
|
496
|
+
return img
|
|
497
|
+
|
|
498
|
+
def bbox_position_in_image(bbox, size, src_bbox):
|
|
499
|
+
"""
|
|
500
|
+
Calculate the position of ``bbox`` in an image of ``size`` and ``src_bbox``.
|
|
501
|
+
Returns the sub-image size and the offset in pixel from top-left corner
|
|
502
|
+
and the sub-bbox.
|
|
503
|
+
|
|
504
|
+
>>> bbox_position_in_image((-180, -90, 180, 90), (600, 300), (-180, -90, 180, 90))
|
|
505
|
+
((600, 300), (0, 0), (-180, -90, 180, 90))
|
|
506
|
+
>>> bbox_position_in_image((-200, -100, 200, 100), (600, 300), (-180, -90, 180, 90))
|
|
507
|
+
((540, 270), (30, 15), (-180, -90, 180, 90))
|
|
508
|
+
>>> bbox_position_in_image((-200, -50, 200, 100), (600, 300), (-180, -90, 180, 90))
|
|
509
|
+
((540, 280), (30, 20), (-180, -50, 180, 90))
|
|
510
|
+
>>> bbox_position_in_image((586400,196400,752800,362800), (256, 256), (586400,196400,752800,350000))
|
|
511
|
+
((256, 237), (0, 19), (586400, 196400, 752800, 350000))
|
|
512
|
+
"""
|
|
513
|
+
coord_to_px = make_lin_transf(bbox, (0, 0) + size)
|
|
514
|
+
offsets = [0, size[1], size[0], 0]
|
|
515
|
+
sub_bbox = list(bbox)
|
|
516
|
+
if src_bbox[0] > bbox[0]:
|
|
517
|
+
sub_bbox[0] = src_bbox[0]
|
|
518
|
+
x, y = coord_to_px((src_bbox[0], 0))
|
|
519
|
+
offsets[0] = int(x)
|
|
520
|
+
if src_bbox[1] > bbox[1]:
|
|
521
|
+
sub_bbox[1] = src_bbox[1]
|
|
522
|
+
x, y = coord_to_px((0, src_bbox[1]))
|
|
523
|
+
offsets[1] = int(y)
|
|
524
|
+
|
|
525
|
+
if src_bbox[2] < bbox[2]:
|
|
526
|
+
sub_bbox[2] = src_bbox[2]
|
|
527
|
+
x, y = coord_to_px((src_bbox[2], 0))
|
|
528
|
+
offsets[2] = int(x)
|
|
529
|
+
|
|
530
|
+
if src_bbox[3] < bbox[3]:
|
|
531
|
+
sub_bbox[3] = src_bbox[3]
|
|
532
|
+
x, y = coord_to_px((0, src_bbox[3]))
|
|
533
|
+
offsets[3] = int(y)
|
|
534
|
+
|
|
535
|
+
size = abs(offsets[2] - offsets[0]), abs(offsets[1] - offsets[3])
|
|
536
|
+
return size, (offsets[0], offsets[3]), tuple(sub_bbox)
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
Fonts are (c) Bitstream (see below). DejaVu changes are in public domain.
|
|
2
|
+
Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below)
|
|
3
|
+
|
|
4
|
+
Bitstream Vera Fonts Copyright
|
|
5
|
+
------------------------------
|
|
6
|
+
|
|
7
|
+
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is
|
|
8
|
+
a trademark of Bitstream, Inc.
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of the fonts accompanying this license ("Fonts") and associated
|
|
12
|
+
documentation files (the "Font Software"), to reproduce and distribute the
|
|
13
|
+
Font Software, including without limitation the rights to use, copy, merge,
|
|
14
|
+
publish, distribute, and/or sell copies of the Font Software, and to permit
|
|
15
|
+
persons to whom the Font Software is furnished to do so, subject to the
|
|
16
|
+
following conditions:
|
|
17
|
+
|
|
18
|
+
The above copyright and trademark notices and this permission notice shall
|
|
19
|
+
be included in all copies of one or more of the Font Software typefaces.
|
|
20
|
+
|
|
21
|
+
The Font Software may be modified, altered, or added to, and in particular
|
|
22
|
+
the designs of glyphs or characters in the Fonts may be modified and
|
|
23
|
+
additional glyphs or characters may be added to the Fonts, only if the fonts
|
|
24
|
+
are renamed to names not containing either the words "Bitstream" or the word
|
|
25
|
+
"Vera".
|
|
26
|
+
|
|
27
|
+
This License becomes null and void to the extent applicable to Fonts or Font
|
|
28
|
+
Software that has been modified and is distributed under the "Bitstream
|
|
29
|
+
Vera" names.
|
|
30
|
+
|
|
31
|
+
The Font Software may be sold as part of a larger software package but no
|
|
32
|
+
copy of one or more of the Font Software typefaces may be sold by itself.
|
|
33
|
+
|
|
34
|
+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
35
|
+
OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,
|
|
36
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,
|
|
37
|
+
TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME
|
|
38
|
+
FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING
|
|
39
|
+
ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
|
|
40
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
|
41
|
+
THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE
|
|
42
|
+
FONT SOFTWARE.
|
|
43
|
+
|
|
44
|
+
Except as contained in this notice, the names of Gnome, the Gnome
|
|
45
|
+
Foundation, and Bitstream Inc., shall not be used in advertising or
|
|
46
|
+
otherwise to promote the sale, use or other dealings in this Font Software
|
|
47
|
+
without prior written authorization from the Gnome Foundation or Bitstream
|
|
48
|
+
Inc., respectively. For further information, contact: fonts at gnome dot
|
|
49
|
+
org.
|
|
50
|
+
|
|
51
|
+
Arev Fonts Copyright
|
|
52
|
+
------------------------------
|
|
53
|
+
|
|
54
|
+
Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved.
|
|
55
|
+
|
|
56
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
57
|
+
a copy of the fonts accompanying this license ("Fonts") and
|
|
58
|
+
associated documentation files (the "Font Software"), to reproduce
|
|
59
|
+
and distribute the modifications to the Bitstream Vera Font Software,
|
|
60
|
+
including without limitation the rights to use, copy, merge, publish,
|
|
61
|
+
distribute, and/or sell copies of the Font Software, and to permit
|
|
62
|
+
persons to whom the Font Software is furnished to do so, subject to
|
|
63
|
+
the following conditions:
|
|
64
|
+
|
|
65
|
+
The above copyright and trademark notices and this permission notice
|
|
66
|
+
shall be included in all copies of one or more of the Font Software
|
|
67
|
+
typefaces.
|
|
68
|
+
|
|
69
|
+
The Font Software may be modified, altered, or added to, and in
|
|
70
|
+
particular the designs of glyphs or characters in the Fonts may be
|
|
71
|
+
modified and additional glyphs or characters may be added to the
|
|
72
|
+
Fonts, only if the fonts are renamed to names not containing either
|
|
73
|
+
the words "Tavmjong Bah" or the word "Arev".
|
|
74
|
+
|
|
75
|
+
This License becomes null and void to the extent applicable to Fonts
|
|
76
|
+
or Font Software that has been modified and is distributed under the
|
|
77
|
+
"Tavmjong Bah Arev" names.
|
|
78
|
+
|
|
79
|
+
The Font Software may be sold as part of a larger software package but
|
|
80
|
+
no copy of one or more of the Font Software typefaces may be sold by
|
|
81
|
+
itself.
|
|
82
|
+
|
|
83
|
+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
84
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
85
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
86
|
+
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
|
|
87
|
+
TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
88
|
+
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
89
|
+
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
90
|
+
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
|
91
|
+
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
92
|
+
|
|
93
|
+
Except as contained in this notice, the name of Tavmjong Bah shall not
|
|
94
|
+
be used in advertising or otherwise to promote the sale, use or other
|
|
95
|
+
dealings in this Font Software without prior written authorization
|
|
96
|
+
from Tavmjong Bah. For further information, contact: tavmjong @ free
|
|
97
|
+
. fr.
|
|
98
|
+
|
|
99
|
+
$Id: LICENSE 2133 2007-11-28 02:46:28Z lechimp $
|
|
File without changes
|