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
mapproxy/cache/tile.py
ADDED
|
@@ -0,0 +1,699 @@
|
|
|
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
|
+
Tile caching (creation, caching and retrieval of tiles).
|
|
18
|
+
|
|
19
|
+
.. digraph:: Schematic Call Graph
|
|
20
|
+
|
|
21
|
+
ranksep = 0.1;
|
|
22
|
+
node [shape="box", height="0", width="0"]
|
|
23
|
+
|
|
24
|
+
cl [label="CacheMapLayer" href="<mapproxy.core.layer.CacheMapLayer>"]
|
|
25
|
+
tm [label="TileManager", href="<TileManager>"];
|
|
26
|
+
fc [label="FileCache", href="<FileCache>"];
|
|
27
|
+
s [label="Source", href="<mapproxy.core.source.Source>"];
|
|
28
|
+
|
|
29
|
+
{
|
|
30
|
+
cl -> tm [label="load_tile_coords"];
|
|
31
|
+
tm -> fc [label="load\\nstore\\nis_cached"];
|
|
32
|
+
tm -> s [label="get_map"]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
"""
|
|
37
|
+
from functools import partial
|
|
38
|
+
from contextlib import contextmanager
|
|
39
|
+
from mapproxy.grid import MetaGrid
|
|
40
|
+
from mapproxy.image import BlankImageSource
|
|
41
|
+
from mapproxy.image.opts import ImageOptions
|
|
42
|
+
from mapproxy.image.merge import merge_images
|
|
43
|
+
from mapproxy.image.tile import TileSplitter, TiledImage
|
|
44
|
+
from mapproxy.layer import MapQuery, BlankImage
|
|
45
|
+
from mapproxy.source import SourceError
|
|
46
|
+
from mapproxy.util import async_
|
|
47
|
+
from mapproxy.util.py import reraise, reraise_exception
|
|
48
|
+
import sys
|
|
49
|
+
import logging
|
|
50
|
+
log = logging.getLogger('mapproxy.cache.tile')
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class TileManager(object):
|
|
54
|
+
"""
|
|
55
|
+
Manages tiles for a single grid.
|
|
56
|
+
Loads tiles from the cache, creates new tiles from sources and stores them
|
|
57
|
+
into the cache, or removes tiles.
|
|
58
|
+
|
|
59
|
+
:param pre_store_filter: a list with filter. each filter will be called
|
|
60
|
+
with a tile before it will be stored to disc. the filter should
|
|
61
|
+
return this or a new tile object.
|
|
62
|
+
"""
|
|
63
|
+
def __init__(self, grid, cache, sources, format, locker, image_opts=None, request_format=None,
|
|
64
|
+
meta_buffer=None, meta_size=None, minimize_meta_requests=False, identifier=None,
|
|
65
|
+
pre_store_filter=None, concurrent_tile_creators=1, tile_creator_class=None,
|
|
66
|
+
bulk_meta_tiles=False,
|
|
67
|
+
rescale_tiles=0,
|
|
68
|
+
cache_rescaled_tiles=False,
|
|
69
|
+
dimensions=None
|
|
70
|
+
):
|
|
71
|
+
self.grid = grid
|
|
72
|
+
self.cache = cache
|
|
73
|
+
self.locker = locker
|
|
74
|
+
self.identifier = identifier
|
|
75
|
+
self.meta_grid = None
|
|
76
|
+
self.format = format
|
|
77
|
+
self.image_opts = image_opts
|
|
78
|
+
self.request_format = request_format or format
|
|
79
|
+
self.sources = sources
|
|
80
|
+
self.minimize_meta_requests = minimize_meta_requests
|
|
81
|
+
self._expire_timestamp = None
|
|
82
|
+
self._refresh_before = {}
|
|
83
|
+
self.pre_store_filter = pre_store_filter or []
|
|
84
|
+
self.concurrent_tile_creators = concurrent_tile_creators
|
|
85
|
+
self.tile_creator_class = tile_creator_class or TileCreator
|
|
86
|
+
self.dimensions = dimensions
|
|
87
|
+
|
|
88
|
+
self.rescale_tiles = rescale_tiles
|
|
89
|
+
self.cache_rescaled_tiles = cache_rescaled_tiles
|
|
90
|
+
|
|
91
|
+
if meta_buffer or (meta_size and not meta_size == [1, 1]):
|
|
92
|
+
if all(source.supports_meta_tiles for source in sources):
|
|
93
|
+
self.meta_grid = MetaGrid(grid, meta_size=meta_size, meta_buffer=meta_buffer)
|
|
94
|
+
elif any(source.supports_meta_tiles for source in sources):
|
|
95
|
+
raise ValueError('meta tiling configured but not supported by all sources')
|
|
96
|
+
elif meta_size and not meta_size == [1, 1] and bulk_meta_tiles:
|
|
97
|
+
# meta tiles configured but all sources are tiled
|
|
98
|
+
# use bulk_meta_tile mode that download tiles in parallel
|
|
99
|
+
self.meta_grid = MetaGrid(grid, meta_size=meta_size, meta_buffer=0)
|
|
100
|
+
self.tile_creator_class = partial(self.tile_creator_class, bulk_meta_tiles=True)
|
|
101
|
+
|
|
102
|
+
@contextmanager
|
|
103
|
+
def session(self):
|
|
104
|
+
"""
|
|
105
|
+
Context manager for access to the cache. Cleans up after usage
|
|
106
|
+
for connection based caches.
|
|
107
|
+
|
|
108
|
+
>>> with tile_manager.session(): #doctest: +SKIP
|
|
109
|
+
... tile_manager.load_tile_coords(tile_coords)
|
|
110
|
+
|
|
111
|
+
"""
|
|
112
|
+
yield
|
|
113
|
+
self.cleanup()
|
|
114
|
+
|
|
115
|
+
def cleanup(self):
|
|
116
|
+
if hasattr(self.cache, 'cleanup'):
|
|
117
|
+
self.cache.cleanup()
|
|
118
|
+
|
|
119
|
+
def load_tile_coord(self, tile_coord, dimensions=None, with_metadata=False):
|
|
120
|
+
return self.load_tile_coords(
|
|
121
|
+
[tile_coord], dimensions=dimensions, with_metadata=with_metadata,
|
|
122
|
+
)[0]
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def load_tile_coords(self, tile_coords, dimensions=None, with_metadata=False):
|
|
126
|
+
tiles = TileCollection(tile_coords)
|
|
127
|
+
rescale_till_zoom = 0
|
|
128
|
+
if self.rescale_tiles:
|
|
129
|
+
rescaled_tiles = {}
|
|
130
|
+
|
|
131
|
+
for t in tiles.tiles:
|
|
132
|
+
# Use zoom level from first None tile.
|
|
133
|
+
if t.coord is not None:
|
|
134
|
+
rescale_till_zoom = t.coord[2] + self.rescale_tiles
|
|
135
|
+
break
|
|
136
|
+
else:
|
|
137
|
+
return tiles
|
|
138
|
+
|
|
139
|
+
if rescale_till_zoom < 0:
|
|
140
|
+
rescale_till_zoom = 0
|
|
141
|
+
if rescale_till_zoom > self.grid.levels:
|
|
142
|
+
rescale_till_zoom = self.grid.levels
|
|
143
|
+
|
|
144
|
+
tiles = self._load_tile_coords(
|
|
145
|
+
tiles, dimensions=dimensions, with_metadata=with_metadata,
|
|
146
|
+
rescale_till_zoom=rescale_till_zoom, rescaled_tiles={},
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
for t in tiles.tiles:
|
|
150
|
+
# Remove our internal marker source, for missing tiles.
|
|
151
|
+
if t.source is RESCALE_TILE_MISSING:
|
|
152
|
+
t.source = None
|
|
153
|
+
|
|
154
|
+
return tiles
|
|
155
|
+
|
|
156
|
+
def _load_tile_coords(self, tiles, dimensions=None, with_metadata=False,
|
|
157
|
+
rescale_till_zoom=None, rescaled_tiles=None
|
|
158
|
+
):
|
|
159
|
+
uncached_tiles = []
|
|
160
|
+
|
|
161
|
+
if rescaled_tiles:
|
|
162
|
+
for t in tiles:
|
|
163
|
+
if t.coord in rescaled_tiles:
|
|
164
|
+
t.source = rescaled_tiles[t.coord].source
|
|
165
|
+
|
|
166
|
+
# load all in batch
|
|
167
|
+
self.cache.load_tiles(tiles, with_metadata, dimensions=dimensions)
|
|
168
|
+
|
|
169
|
+
for tile in tiles:
|
|
170
|
+
if tile.coord is not None and not self.is_cached(tile, dimensions=dimensions):
|
|
171
|
+
# missing or staled
|
|
172
|
+
uncached_tiles.append(tile)
|
|
173
|
+
|
|
174
|
+
if uncached_tiles:
|
|
175
|
+
creator = self.creator(dimensions=dimensions)
|
|
176
|
+
created_tiles = creator.create_tiles(uncached_tiles)
|
|
177
|
+
if not created_tiles and self.rescale_tiles:
|
|
178
|
+
created_tiles = [self._scaled_tile(t, rescale_till_zoom, rescaled_tiles) for t in uncached_tiles]
|
|
179
|
+
|
|
180
|
+
for created_tile in created_tiles:
|
|
181
|
+
if created_tile.coord in tiles:
|
|
182
|
+
tiles[created_tile.coord].source = created_tile.source
|
|
183
|
+
|
|
184
|
+
return tiles
|
|
185
|
+
|
|
186
|
+
def remove_tile_coords(self, tile_coords, dimensions=None):
|
|
187
|
+
tiles = TileCollection(tile_coords)
|
|
188
|
+
self.cache.remove_tiles(tiles)
|
|
189
|
+
|
|
190
|
+
def creator(self, dimensions=None):
|
|
191
|
+
return self.tile_creator_class(self, dimensions=dimensions)
|
|
192
|
+
|
|
193
|
+
def lock(self, tile):
|
|
194
|
+
if self.meta_grid:
|
|
195
|
+
tile = Tile(self.meta_grid.main_tile(tile.coord))
|
|
196
|
+
return self.locker.lock(tile)
|
|
197
|
+
|
|
198
|
+
def is_cached(self, tile, dimensions=None):
|
|
199
|
+
"""
|
|
200
|
+
Return True if the tile is cached.
|
|
201
|
+
"""
|
|
202
|
+
if isinstance(tile, tuple):
|
|
203
|
+
tile = Tile(tile)
|
|
204
|
+
if tile.coord is None:
|
|
205
|
+
return True
|
|
206
|
+
cached = self.cache.is_cached(tile, dimensions=dimensions)
|
|
207
|
+
max_mtime = self.expire_timestamp(tile)
|
|
208
|
+
if cached and max_mtime is not None:
|
|
209
|
+
self.cache.load_tile_metadata(tile)
|
|
210
|
+
# file time stamp must be rounded to integer since time conversion functions
|
|
211
|
+
# mktime and timetuple strip decimals from seconds
|
|
212
|
+
stale = int(tile.timestamp) <= max_mtime
|
|
213
|
+
if stale:
|
|
214
|
+
cached = False
|
|
215
|
+
return cached
|
|
216
|
+
|
|
217
|
+
def is_stale(self, tile, dimensions=None):
|
|
218
|
+
"""
|
|
219
|
+
Return True if tile exists _and_ is expired.
|
|
220
|
+
"""
|
|
221
|
+
if isinstance(tile, tuple):
|
|
222
|
+
tile = Tile(tile)
|
|
223
|
+
if self.cache.is_cached(tile, dimensions=dimensions):
|
|
224
|
+
# tile exists
|
|
225
|
+
if not self.is_cached(tile, dimensions=dimensions):
|
|
226
|
+
# expired
|
|
227
|
+
return True
|
|
228
|
+
return False
|
|
229
|
+
return False
|
|
230
|
+
|
|
231
|
+
def expire_timestamp(self, tile=None):
|
|
232
|
+
"""
|
|
233
|
+
Return the timestamp until which a tile should be accepted as up-to-date,
|
|
234
|
+
or ``None`` if the tiles should not expire.
|
|
235
|
+
|
|
236
|
+
:note: Returns _expire_timestamp by default.
|
|
237
|
+
"""
|
|
238
|
+
if self._refresh_before:
|
|
239
|
+
from mapproxy.seed.config import before_timestamp_from_options
|
|
240
|
+
return before_timestamp_from_options(self._refresh_before)
|
|
241
|
+
return self._expire_timestamp
|
|
242
|
+
|
|
243
|
+
def apply_tile_filter(self, tile):
|
|
244
|
+
"""
|
|
245
|
+
Apply all `pre_store_filter` to this tile.
|
|
246
|
+
Returns filtered tile.
|
|
247
|
+
"""
|
|
248
|
+
if tile.stored:
|
|
249
|
+
return tile
|
|
250
|
+
|
|
251
|
+
for img_filter in self.pre_store_filter:
|
|
252
|
+
tile = img_filter(tile)
|
|
253
|
+
return tile
|
|
254
|
+
|
|
255
|
+
def _scaled_tile(self, tile, stop_zoom, rescaled_tiles):
|
|
256
|
+
"""
|
|
257
|
+
Try to load tile by loading, scaling and clipping tiles from zoom levels above or
|
|
258
|
+
below. stop_zoom determines if tiles from above should be scaled up, or if tiles
|
|
259
|
+
from below should be scaled down.
|
|
260
|
+
Returns an empty Tile if tile zoom level is stop_zoom.
|
|
261
|
+
"""
|
|
262
|
+
if tile.coord in rescaled_tiles:
|
|
263
|
+
return rescaled_tiles[tile.coord]
|
|
264
|
+
|
|
265
|
+
# Cache tile in rescaled_tiles. We initially set source to a fixed
|
|
266
|
+
# BlankImageSource and overwrite it if we actually rescaled the tile.
|
|
267
|
+
tile.source = RESCALE_TILE_MISSING
|
|
268
|
+
rescaled_tiles[tile.coord] = tile
|
|
269
|
+
|
|
270
|
+
tile_bbox = self.grid.tile_bbox(tile.coord)
|
|
271
|
+
current_zoom = tile.coord[2]
|
|
272
|
+
if stop_zoom == current_zoom:
|
|
273
|
+
return tile
|
|
274
|
+
if stop_zoom > current_zoom:
|
|
275
|
+
src_level = current_zoom + 1
|
|
276
|
+
else:
|
|
277
|
+
src_level = current_zoom - 1
|
|
278
|
+
|
|
279
|
+
src_bbox, src_tile_grid, affected_tile_coords = self.grid.get_affected_level_tiles(tile_bbox, src_level)
|
|
280
|
+
|
|
281
|
+
affected_tiles = TileCollection(affected_tile_coords)
|
|
282
|
+
for t in affected_tiles:
|
|
283
|
+
# Add sources of cached tiles, to avoid loading same tile multiple times
|
|
284
|
+
# loading recursive.
|
|
285
|
+
if t.coord in rescaled_tiles:
|
|
286
|
+
t.source = rescaled_tiles[t.coord].source
|
|
287
|
+
|
|
288
|
+
tile_collection = self._load_tile_coords(
|
|
289
|
+
affected_tiles,
|
|
290
|
+
rescale_till_zoom=stop_zoom,
|
|
291
|
+
rescaled_tiles=rescaled_tiles,
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
if tile_collection.blank:
|
|
295
|
+
return tile
|
|
296
|
+
|
|
297
|
+
tile_sources = []
|
|
298
|
+
for t in tile_collection:
|
|
299
|
+
# Replace RESCALE_TILE_MISSING with None, before transforming tiles.
|
|
300
|
+
tile_sources.append(t.source if t.source is not RESCALE_TILE_MISSING else None)
|
|
301
|
+
|
|
302
|
+
tiled_image = TiledImage(tile_sources, src_bbox=src_bbox, src_srs=self.grid.srs,
|
|
303
|
+
tile_grid=src_tile_grid, tile_size=self.grid.tile_size)
|
|
304
|
+
tile.source = tiled_image.transform(tile_bbox, self.grid.srs, self.grid.tile_size, self.image_opts)
|
|
305
|
+
|
|
306
|
+
if self.cache_rescaled_tiles:
|
|
307
|
+
self.cache.store_tile(tile)
|
|
308
|
+
return tile
|
|
309
|
+
|
|
310
|
+
# RESCALE_TILE_MISSING is a dummy source to prevent a tile cache from loading
|
|
311
|
+
# a tile that we already found out is missing.
|
|
312
|
+
RESCALE_TILE_MISSING = BlankImageSource((256, 256), ImageOptions())
|
|
313
|
+
|
|
314
|
+
class TileCreator(object):
|
|
315
|
+
def __init__(self, tile_mgr, dimensions=None, image_merger=None, bulk_meta_tiles=False):
|
|
316
|
+
self.cache = tile_mgr.cache
|
|
317
|
+
self.sources = tile_mgr.sources
|
|
318
|
+
self.grid = tile_mgr.grid
|
|
319
|
+
self.meta_grid = tile_mgr.meta_grid
|
|
320
|
+
self.bulk_meta_tiles = bulk_meta_tiles
|
|
321
|
+
self.tile_mgr = tile_mgr
|
|
322
|
+
self.dimensions = dimensions
|
|
323
|
+
self.image_merger = image_merger
|
|
324
|
+
|
|
325
|
+
def is_cached(self, tile, dimensions=None):
|
|
326
|
+
"""
|
|
327
|
+
Return True if the tile is cached.
|
|
328
|
+
"""
|
|
329
|
+
return self.tile_mgr.is_cached(tile, dimensions=dimensions)
|
|
330
|
+
|
|
331
|
+
def is_stale(self, tile):
|
|
332
|
+
"""
|
|
333
|
+
Return True if the tile exists in cache and is expired.
|
|
334
|
+
"""
|
|
335
|
+
return self.tile_mgr.is_stale(tile)
|
|
336
|
+
|
|
337
|
+
def create_tiles(self, tiles):
|
|
338
|
+
if not self.sources:
|
|
339
|
+
return []
|
|
340
|
+
if not self.meta_grid:
|
|
341
|
+
created_tiles = self._create_single_tiles(tiles)
|
|
342
|
+
elif self.tile_mgr.minimize_meta_requests and len(tiles) > 1:
|
|
343
|
+
# use minimal requests only for mulitple tile requests (ie not for TMS)
|
|
344
|
+
meta_tile = self.meta_grid.minimal_meta_tile([t.coord for t in tiles])
|
|
345
|
+
created_tiles = self._create_meta_tile(meta_tile)
|
|
346
|
+
else:
|
|
347
|
+
meta_tiles = []
|
|
348
|
+
meta_bboxes = set()
|
|
349
|
+
for tile in tiles:
|
|
350
|
+
meta_tile = self.meta_grid.meta_tile(tile.coord)
|
|
351
|
+
if meta_tile.bbox not in meta_bboxes:
|
|
352
|
+
meta_tiles.append(meta_tile)
|
|
353
|
+
meta_bboxes.add(meta_tile.bbox)
|
|
354
|
+
|
|
355
|
+
created_tiles = self._create_meta_tiles(meta_tiles)
|
|
356
|
+
|
|
357
|
+
return created_tiles
|
|
358
|
+
|
|
359
|
+
def _create_single_tiles(self, tiles):
|
|
360
|
+
if self.tile_mgr.concurrent_tile_creators > 1 and len(tiles) > 1:
|
|
361
|
+
return self._create_threaded(self._create_single_tile, tiles)
|
|
362
|
+
|
|
363
|
+
created_tiles = []
|
|
364
|
+
for tile in tiles:
|
|
365
|
+
created_tiles.extend(self._create_single_tile(tile))
|
|
366
|
+
return created_tiles
|
|
367
|
+
|
|
368
|
+
def _create_threaded(self, create_func, tiles):
|
|
369
|
+
result = []
|
|
370
|
+
async_pool = async_.Pool(self.tile_mgr.concurrent_tile_creators)
|
|
371
|
+
for new_tiles in async_pool.imap(create_func, tiles):
|
|
372
|
+
result.extend(new_tiles)
|
|
373
|
+
return result
|
|
374
|
+
|
|
375
|
+
def _create_single_tile(self, tile, dimensions=None):
|
|
376
|
+
tile_bbox = self.grid.tile_bbox(tile.coord)
|
|
377
|
+
query = MapQuery(tile_bbox, self.grid.tile_size, self.grid.srs,
|
|
378
|
+
self.tile_mgr.request_format, dimensions=self.dimensions)
|
|
379
|
+
with self.tile_mgr.lock(tile):
|
|
380
|
+
if not self.is_cached(tile, dimensions=dimensions):
|
|
381
|
+
source = None
|
|
382
|
+
try:
|
|
383
|
+
source = self._query_sources(query)
|
|
384
|
+
# if source is not available, try to serve tile in cache
|
|
385
|
+
except SourceError as e:
|
|
386
|
+
if self.is_stale(tile):
|
|
387
|
+
self.cache.load_tile(tile)
|
|
388
|
+
else:
|
|
389
|
+
reraise_exception(e, sys.exc_info())
|
|
390
|
+
if not source: return []
|
|
391
|
+
if source.authorize_stale and self.is_stale(tile):
|
|
392
|
+
# The configuration authorises blank tiles generated by the error_handler
|
|
393
|
+
# to be replaced by stale tiles from cache.
|
|
394
|
+
self.cache.load_tile(tile)
|
|
395
|
+
return [tile]
|
|
396
|
+
if self.tile_mgr.image_opts != source.image_opts:
|
|
397
|
+
# call as_buffer to force conversion into cache format
|
|
398
|
+
source.as_buffer(self.tile_mgr.image_opts)
|
|
399
|
+
source.image_opts = self.tile_mgr.image_opts
|
|
400
|
+
tile.source = source
|
|
401
|
+
tile.cacheable = source.cacheable
|
|
402
|
+
tile = self.tile_mgr.apply_tile_filter(tile)
|
|
403
|
+
if source.cacheable:
|
|
404
|
+
self.cache.store_tile(tile)
|
|
405
|
+
else:
|
|
406
|
+
self.cache.load_tile(tile)
|
|
407
|
+
return [tile]
|
|
408
|
+
|
|
409
|
+
def _query_sources(self, query):
|
|
410
|
+
"""
|
|
411
|
+
Query all sources and return the results as a single ImageSource.
|
|
412
|
+
Multiple sources will be merged into a single image.
|
|
413
|
+
"""
|
|
414
|
+
|
|
415
|
+
# directly return get_map without merge if ...
|
|
416
|
+
if (len(self.sources) == 1 and
|
|
417
|
+
not self.image_merger and # no special image_merger (like BandMerger)
|
|
418
|
+
not (self.sources[0].coverage and # no clipping coverage
|
|
419
|
+
self.sources[0].coverage.clip and
|
|
420
|
+
self.sources[0].coverage.intersects(query.bbox, query.srs))
|
|
421
|
+
):
|
|
422
|
+
try:
|
|
423
|
+
return self.sources[0].get_map(query)
|
|
424
|
+
except BlankImage:
|
|
425
|
+
return None
|
|
426
|
+
|
|
427
|
+
def get_map_from_source(source):
|
|
428
|
+
try:
|
|
429
|
+
img = source.get_map(query)
|
|
430
|
+
except BlankImage:
|
|
431
|
+
return None, None
|
|
432
|
+
else:
|
|
433
|
+
return (img, source.coverage)
|
|
434
|
+
|
|
435
|
+
layers = []
|
|
436
|
+
for layer in async_.imap(get_map_from_source, self.sources):
|
|
437
|
+
if layer[0] is not None:
|
|
438
|
+
layers.append(layer)
|
|
439
|
+
|
|
440
|
+
return merge_images(layers, size=query.size, bbox=query.bbox, bbox_srs=query.srs,
|
|
441
|
+
image_opts=self.tile_mgr.image_opts, merger=self.image_merger)
|
|
442
|
+
|
|
443
|
+
def _create_meta_tiles(self, meta_tiles):
|
|
444
|
+
if self.bulk_meta_tiles:
|
|
445
|
+
created_tiles = []
|
|
446
|
+
for meta_tile in meta_tiles:
|
|
447
|
+
created_tiles.extend(self._create_bulk_meta_tile(meta_tile))
|
|
448
|
+
return created_tiles
|
|
449
|
+
|
|
450
|
+
if self.tile_mgr.concurrent_tile_creators > 1 and len(meta_tiles) > 1:
|
|
451
|
+
return self._create_threaded(self._create_meta_tile, meta_tiles)
|
|
452
|
+
|
|
453
|
+
created_tiles = []
|
|
454
|
+
for meta_tile in meta_tiles:
|
|
455
|
+
created_tiles.extend(self._create_meta_tile(meta_tile))
|
|
456
|
+
return created_tiles
|
|
457
|
+
|
|
458
|
+
def _create_meta_tile(self, meta_tile):
|
|
459
|
+
"""
|
|
460
|
+
_create_meta_tile queries a single meta tile and splits it into
|
|
461
|
+
tiles.
|
|
462
|
+
"""
|
|
463
|
+
tile_size = self.grid.tile_size
|
|
464
|
+
query = MapQuery(meta_tile.bbox, meta_tile.size, self.grid.srs, self.tile_mgr.request_format,
|
|
465
|
+
dimensions=self.dimensions)
|
|
466
|
+
main_tile = Tile(meta_tile.main_tile_coord)
|
|
467
|
+
with self.tile_mgr.lock(main_tile):
|
|
468
|
+
if not all(self.is_cached(t, dimensions=self.dimensions) for t in meta_tile.tiles if t is not None):
|
|
469
|
+
meta_tile_image = self._query_sources(query)
|
|
470
|
+
if not meta_tile_image: return []
|
|
471
|
+
splitted_tiles = split_meta_tiles(meta_tile_image, meta_tile.tile_patterns,
|
|
472
|
+
tile_size, self.tile_mgr.image_opts)
|
|
473
|
+
splitted_tiles = [self.tile_mgr.apply_tile_filter(t) for t in splitted_tiles]
|
|
474
|
+
if meta_tile_image.cacheable:
|
|
475
|
+
self.cache.store_tiles(splitted_tiles,dimensions=self.dimensions)
|
|
476
|
+
return splitted_tiles
|
|
477
|
+
# else
|
|
478
|
+
tiles = [Tile(coord) for coord in meta_tile.tiles]
|
|
479
|
+
self.cache.load_tiles(tiles, dimensions=self.dimensions)
|
|
480
|
+
return tiles
|
|
481
|
+
|
|
482
|
+
def _create_bulk_meta_tile(self, meta_tile):
|
|
483
|
+
"""
|
|
484
|
+
_create_bulk_meta_tile queries each tile of the meta tile in parallel
|
|
485
|
+
(using concurrent_tile_creators).
|
|
486
|
+
"""
|
|
487
|
+
tile_size = self.grid.tile_size
|
|
488
|
+
main_tile = Tile(meta_tile.main_tile_coord)
|
|
489
|
+
with self.tile_mgr.lock(main_tile):
|
|
490
|
+
if not all(self.is_cached(t, dimensions=self.dimensions) for t in meta_tile.tiles if t is not None):
|
|
491
|
+
async_pool = async_.Pool(self.tile_mgr.concurrent_tile_creators)
|
|
492
|
+
def query_tile(coord):
|
|
493
|
+
try:
|
|
494
|
+
query = MapQuery(self.grid.tile_bbox(coord), tile_size, self.grid.srs, self.tile_mgr.request_format,
|
|
495
|
+
dimensions=self.dimensions)
|
|
496
|
+
tile_image = self._query_sources(query)
|
|
497
|
+
if tile_image is None:
|
|
498
|
+
return None
|
|
499
|
+
|
|
500
|
+
if self.tile_mgr.image_opts != tile_image.image_opts:
|
|
501
|
+
# call as_buffer to force conversion into cache format
|
|
502
|
+
tile_image.as_buffer(self.tile_mgr.image_opts)
|
|
503
|
+
|
|
504
|
+
tile = Tile(coord, cacheable=tile_image.cacheable)
|
|
505
|
+
tile.source = tile_image
|
|
506
|
+
tile = self.tile_mgr.apply_tile_filter(tile)
|
|
507
|
+
except BlankImage:
|
|
508
|
+
return None
|
|
509
|
+
else:
|
|
510
|
+
return tile
|
|
511
|
+
|
|
512
|
+
tiles = []
|
|
513
|
+
for tile_task in async_pool.imap(query_tile,
|
|
514
|
+
[t for t in meta_tile.tiles if t is not None],
|
|
515
|
+
use_result_objects=True,
|
|
516
|
+
):
|
|
517
|
+
if tile_task.exception is None:
|
|
518
|
+
tile = tile_task.result
|
|
519
|
+
if tile is not None:
|
|
520
|
+
tiles.append(tile)
|
|
521
|
+
else:
|
|
522
|
+
ex = tile_task.exception
|
|
523
|
+
async_pool.shutdown(True)
|
|
524
|
+
reraise(ex)
|
|
525
|
+
|
|
526
|
+
self.cache.store_tiles([t for t in tiles if t.cacheable])
|
|
527
|
+
return tiles
|
|
528
|
+
|
|
529
|
+
# else
|
|
530
|
+
tiles = [Tile(coord) for coord in meta_tile.tiles]
|
|
531
|
+
self.cache.load_tiles(tiles, dimensions=self.dimensions)
|
|
532
|
+
return tiles
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
class Tile(object):
|
|
536
|
+
"""
|
|
537
|
+
Internal data object for all tiles. Stores the tile-``coord`` and the tile data.
|
|
538
|
+
|
|
539
|
+
:ivar source: the data of this tile
|
|
540
|
+
:type source: ImageSource
|
|
541
|
+
"""
|
|
542
|
+
def __init__(self, coord, source=None, cacheable=True):
|
|
543
|
+
self.coord = coord
|
|
544
|
+
self.source = source
|
|
545
|
+
self.location = None
|
|
546
|
+
self.stored = False
|
|
547
|
+
self._cacheable = cacheable
|
|
548
|
+
self.size = None
|
|
549
|
+
self.timestamp = None
|
|
550
|
+
|
|
551
|
+
def _cacheable_get(self):
|
|
552
|
+
return CacheInfo(cacheable=self._cacheable, timestamp=self.timestamp,
|
|
553
|
+
size=self.size)
|
|
554
|
+
|
|
555
|
+
def _cacheable_set(self, cacheable):
|
|
556
|
+
if isinstance(cacheable, bool):
|
|
557
|
+
self._cacheable = cacheable
|
|
558
|
+
else: # assume cacheable is CacheInfo
|
|
559
|
+
self._cacheable = cacheable.cacheable
|
|
560
|
+
self.timestamp = cacheable.timestamp
|
|
561
|
+
self.size = cacheable.size
|
|
562
|
+
|
|
563
|
+
cacheable = property(_cacheable_get, _cacheable_set)
|
|
564
|
+
|
|
565
|
+
def source_buffer(self, *args, **kw):
|
|
566
|
+
if self.source is not None:
|
|
567
|
+
return self.source.as_buffer(*args, **kw)
|
|
568
|
+
else:
|
|
569
|
+
return None
|
|
570
|
+
|
|
571
|
+
def source_image(self, *args, **kw):
|
|
572
|
+
if self.source is not None:
|
|
573
|
+
return self.source.as_image(*args, **kw)
|
|
574
|
+
else:
|
|
575
|
+
return None
|
|
576
|
+
|
|
577
|
+
def is_missing(self):
|
|
578
|
+
"""
|
|
579
|
+
Returns ``True`` when the tile has no ``data``, except when the ``coord``
|
|
580
|
+
is ``None``. It doesn't check if the tile exists.
|
|
581
|
+
|
|
582
|
+
>>> Tile((1, 2, 3)).is_missing()
|
|
583
|
+
True
|
|
584
|
+
>>> Tile((1, 2, 3), './tmp/foo').is_missing()
|
|
585
|
+
False
|
|
586
|
+
>>> Tile(None).is_missing()
|
|
587
|
+
False
|
|
588
|
+
"""
|
|
589
|
+
if self.coord is None:
|
|
590
|
+
return False
|
|
591
|
+
return self.source is None
|
|
592
|
+
|
|
593
|
+
def __eq__(self, other):
|
|
594
|
+
"""
|
|
595
|
+
>>> Tile((0, 0, 1)) == Tile((0, 0, 1))
|
|
596
|
+
True
|
|
597
|
+
>>> Tile((0, 0, 1)) == Tile((1, 0, 1))
|
|
598
|
+
False
|
|
599
|
+
>>> Tile((0, 0, 1)) == None
|
|
600
|
+
False
|
|
601
|
+
"""
|
|
602
|
+
if isinstance(other, Tile):
|
|
603
|
+
return (self.coord == other.coord and
|
|
604
|
+
self.source == other.source)
|
|
605
|
+
else:
|
|
606
|
+
return NotImplemented
|
|
607
|
+
def __ne__(self, other):
|
|
608
|
+
"""
|
|
609
|
+
>>> Tile((0, 0, 1)) != Tile((0, 0, 1))
|
|
610
|
+
False
|
|
611
|
+
>>> Tile((0, 0, 1)) != Tile((1, 0, 1))
|
|
612
|
+
True
|
|
613
|
+
>>> Tile((0, 0, 1)) != None
|
|
614
|
+
True
|
|
615
|
+
"""
|
|
616
|
+
equal_result = self.__eq__(other)
|
|
617
|
+
if equal_result is NotImplemented:
|
|
618
|
+
return NotImplemented
|
|
619
|
+
else:
|
|
620
|
+
return not equal_result
|
|
621
|
+
|
|
622
|
+
def __repr__(self):
|
|
623
|
+
return 'Tile(%r, source=%r)' % (self.coord, self.source)
|
|
624
|
+
|
|
625
|
+
class CacheInfo(object):
|
|
626
|
+
def __init__(self, cacheable=True, timestamp=None, size=None):
|
|
627
|
+
self.cacheable = cacheable
|
|
628
|
+
self.timestamp = timestamp
|
|
629
|
+
self.size = size
|
|
630
|
+
|
|
631
|
+
def __bool__(self):
|
|
632
|
+
return self.cacheable
|
|
633
|
+
|
|
634
|
+
# PY2 compat
|
|
635
|
+
__nonzero__ = __bool__
|
|
636
|
+
|
|
637
|
+
class TileCollection(object):
|
|
638
|
+
def __init__(self, tile_coords):
|
|
639
|
+
self.tiles = [Tile(coord) for coord in tile_coords]
|
|
640
|
+
self.tiles_dict = {}
|
|
641
|
+
for tile in self.tiles:
|
|
642
|
+
self.tiles_dict[tile.coord] = tile
|
|
643
|
+
|
|
644
|
+
def __getitem__(self, idx_or_coord):
|
|
645
|
+
if isinstance(idx_or_coord, int):
|
|
646
|
+
return self.tiles[idx_or_coord]
|
|
647
|
+
if idx_or_coord in self.tiles_dict:
|
|
648
|
+
return self.tiles_dict[idx_or_coord]
|
|
649
|
+
return Tile(idx_or_coord)
|
|
650
|
+
|
|
651
|
+
def __contains__(self, tile_or_coord):
|
|
652
|
+
if isinstance(tile_or_coord, tuple):
|
|
653
|
+
return tile_or_coord in self.tiles_dict
|
|
654
|
+
if hasattr(tile_or_coord, 'coord'):
|
|
655
|
+
return tile_or_coord.coord in self.tiles_dict
|
|
656
|
+
return False
|
|
657
|
+
|
|
658
|
+
def __len__(self):
|
|
659
|
+
return len(self.tiles)
|
|
660
|
+
|
|
661
|
+
def __iter__(self):
|
|
662
|
+
return iter(self.tiles)
|
|
663
|
+
|
|
664
|
+
@property
|
|
665
|
+
def empty(self):
|
|
666
|
+
"""
|
|
667
|
+
Returns True if no tile in this collection contains a source.
|
|
668
|
+
"""
|
|
669
|
+
return all((t.source is None for t in self.tiles))
|
|
670
|
+
|
|
671
|
+
@property
|
|
672
|
+
def blank(self):
|
|
673
|
+
"""
|
|
674
|
+
Returns True if all sources collection are BlankImageSources or have not source at all.
|
|
675
|
+
"""
|
|
676
|
+
return all((t.source is None or isinstance(t.source, BlankImageSource) for t in self.tiles))
|
|
677
|
+
|
|
678
|
+
def __repr__(self):
|
|
679
|
+
return 'TileCollection(%r)' % self.tiles
|
|
680
|
+
|
|
681
|
+
|
|
682
|
+
def split_meta_tiles(meta_tile, tiles, tile_size, image_opts):
|
|
683
|
+
try:
|
|
684
|
+
# TODO png8
|
|
685
|
+
# if not self.transparent and format == 'png':
|
|
686
|
+
# format = 'png8'
|
|
687
|
+
splitter = TileSplitter(meta_tile, image_opts)
|
|
688
|
+
except IOError:
|
|
689
|
+
# TODO
|
|
690
|
+
raise
|
|
691
|
+
split_tiles = []
|
|
692
|
+
for tile in tiles:
|
|
693
|
+
tile_coord, crop_coord = tile
|
|
694
|
+
if tile_coord is None: continue
|
|
695
|
+
data = splitter.get_tile(crop_coord, tile_size)
|
|
696
|
+
new_tile = Tile(tile_coord, cacheable=meta_tile.cacheable)
|
|
697
|
+
new_tile.source = data
|
|
698
|
+
split_tiles.append(new_tile)
|
|
699
|
+
return split_tiles
|
|
File without changes
|