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/layer.py
ADDED
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
# -:- encoding: utf-8 -:-
|
|
2
|
+
# This file is part of the MapProxy project.
|
|
3
|
+
# Copyright (C) 2010 Omniscale <http://omniscale.de>
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
"""
|
|
18
|
+
Layers that can get maps/infos from different sources/caches.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import division
|
|
22
|
+
from mapproxy.grid import NoTiles, GridError, merge_resolution_range, bbox_intersects, bbox_contains
|
|
23
|
+
from mapproxy.image import SubImageSource, bbox_position_in_image
|
|
24
|
+
from mapproxy.image.opts import ImageOptions
|
|
25
|
+
from mapproxy.image.tile import TiledImage
|
|
26
|
+
from mapproxy.srs import SRS, bbox_equals, merge_bbox, make_lin_transf, SupportedSRS
|
|
27
|
+
from mapproxy.proj import ProjError
|
|
28
|
+
from mapproxy.compat import iteritems
|
|
29
|
+
|
|
30
|
+
import logging
|
|
31
|
+
from functools import reduce
|
|
32
|
+
log = logging.getLogger(__name__)
|
|
33
|
+
|
|
34
|
+
class BlankImage(Exception):
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
class MapError(Exception):
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
class MapBBOXError(Exception):
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
class MapLayer(object):
|
|
44
|
+
supports_meta_tiles = False
|
|
45
|
+
|
|
46
|
+
res_range = None
|
|
47
|
+
|
|
48
|
+
coverage = None
|
|
49
|
+
|
|
50
|
+
def __init__(self, image_opts=None):
|
|
51
|
+
self.image_opts = image_opts or ImageOptions()
|
|
52
|
+
|
|
53
|
+
def _get_opacity(self):
|
|
54
|
+
return self.image_opts.opacity
|
|
55
|
+
|
|
56
|
+
def _set_opacity(self, value):
|
|
57
|
+
self.image_opts.opacity = value
|
|
58
|
+
|
|
59
|
+
opacity = property(_get_opacity, _set_opacity)
|
|
60
|
+
|
|
61
|
+
def is_opaque(self, query):
|
|
62
|
+
"""
|
|
63
|
+
Whether the query result is opaque.
|
|
64
|
+
|
|
65
|
+
This method is used for optimizations: layers below an opaque
|
|
66
|
+
layer can be skipped. As sources with `transparent: false`
|
|
67
|
+
still can return transparent images (min_res/max_res/coverages),
|
|
68
|
+
implementations of this method need to be certain that the image
|
|
69
|
+
is indeed opaque. is_opaque should return False if in doubt.
|
|
70
|
+
"""
|
|
71
|
+
return False
|
|
72
|
+
|
|
73
|
+
def check_res_range(self, query):
|
|
74
|
+
if (self.res_range and
|
|
75
|
+
not self.res_range.contains(query.bbox, query.size, query.srs)):
|
|
76
|
+
raise BlankImage()
|
|
77
|
+
|
|
78
|
+
def get_map(self, query):
|
|
79
|
+
raise NotImplementedError
|
|
80
|
+
|
|
81
|
+
def combined_layer(self, other, query):
|
|
82
|
+
return None
|
|
83
|
+
|
|
84
|
+
class LimitedLayer(object):
|
|
85
|
+
"""
|
|
86
|
+
Wraps an existing layer temporary and stores additional
|
|
87
|
+
attributes for geographical limits.
|
|
88
|
+
"""
|
|
89
|
+
def __init__(self, layer, coverage):
|
|
90
|
+
self._layer = layer
|
|
91
|
+
self.coverage = coverage
|
|
92
|
+
|
|
93
|
+
def __getattr__(self, name):
|
|
94
|
+
return getattr(self._layer, name)
|
|
95
|
+
|
|
96
|
+
def combined_layer(self, other, query):
|
|
97
|
+
if self.coverage == other.coverage:
|
|
98
|
+
combined = self._layer.combined_layer(other, query)
|
|
99
|
+
if combined:
|
|
100
|
+
return LimitedLayer(combined, self.coverage)
|
|
101
|
+
return None
|
|
102
|
+
|
|
103
|
+
def get_info(self, query):
|
|
104
|
+
if self.coverage:
|
|
105
|
+
if not self.coverage.contains(query.coord, query.srs):
|
|
106
|
+
return None
|
|
107
|
+
return self._layer.get_info(query)
|
|
108
|
+
|
|
109
|
+
class InfoLayer(object):
|
|
110
|
+
def get_info(self, query):
|
|
111
|
+
raise NotImplementedError
|
|
112
|
+
|
|
113
|
+
class MapQuery(object):
|
|
114
|
+
"""
|
|
115
|
+
Internal query for a map with a specific extent, size, srs, etc.
|
|
116
|
+
"""
|
|
117
|
+
def __init__(self, bbox, size, srs, format='image/png', transparent=False,
|
|
118
|
+
tiled_only=False, dimensions=None):
|
|
119
|
+
self.bbox = bbox
|
|
120
|
+
self.size = size
|
|
121
|
+
self.srs = srs
|
|
122
|
+
self.format = format
|
|
123
|
+
self.transparent = transparent
|
|
124
|
+
self.tiled_only = tiled_only
|
|
125
|
+
self.dimensions = dimensions or {}
|
|
126
|
+
|
|
127
|
+
def dimensions_for_params(self, params):
|
|
128
|
+
"""
|
|
129
|
+
Return subset of the dimensions.
|
|
130
|
+
|
|
131
|
+
>>> mq = MapQuery(None, None, None, dimensions={'Foo': 1, 'bar': 2})
|
|
132
|
+
>>> mq.dimensions_for_params(set(['FOO', 'baz']))
|
|
133
|
+
{'Foo': 1}
|
|
134
|
+
"""
|
|
135
|
+
params = [p.lower() for p in params]
|
|
136
|
+
return dict((k, v) for k, v in iteritems(self.dimensions) if k.lower() in params)
|
|
137
|
+
|
|
138
|
+
def __repr__(self):
|
|
139
|
+
info = self.__dict__
|
|
140
|
+
serialized_dimensions = ", ".join(["'%s': '%s'" % (key, value) for (key, value) in self.dimensions.items()])
|
|
141
|
+
info["serialized_dimensions"] = serialized_dimensions
|
|
142
|
+
return "MapQuery(bbox=%(bbox)s, size=%(size)s, srs=%(srs)r, format=%(format)s, dimensions={%(serialized_dimensions)s)}" % info
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class InfoQuery(object):
|
|
146
|
+
def __init__(self, bbox, size, srs, pos, info_format, format=None,
|
|
147
|
+
feature_count=None):
|
|
148
|
+
self.bbox = bbox
|
|
149
|
+
self.size = size
|
|
150
|
+
self.srs = srs
|
|
151
|
+
self.pos = pos
|
|
152
|
+
self.info_format = info_format
|
|
153
|
+
self.format = format
|
|
154
|
+
self.feature_count = feature_count
|
|
155
|
+
|
|
156
|
+
@property
|
|
157
|
+
def coord(self):
|
|
158
|
+
return make_lin_transf((0, 0, self.size[0], self.size[1]), self.bbox)(self.pos)
|
|
159
|
+
|
|
160
|
+
class LegendQuery(object):
|
|
161
|
+
def __init__(self, format, scale):
|
|
162
|
+
self.format = format
|
|
163
|
+
self.scale = scale
|
|
164
|
+
|
|
165
|
+
class Dimension(list):
|
|
166
|
+
def __init__(self, identifier, values, default=None):
|
|
167
|
+
self.identifier = identifier
|
|
168
|
+
if not default and values:
|
|
169
|
+
default = values[0]
|
|
170
|
+
self.default = default
|
|
171
|
+
list.__init__(self, values)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def map_extent_from_grid(grid):
|
|
175
|
+
"""
|
|
176
|
+
>>> from mapproxy.grid import tile_grid_for_epsg
|
|
177
|
+
>>> map_extent_from_grid(tile_grid_for_epsg('EPSG:900913'))
|
|
178
|
+
... #doctest: +NORMALIZE_WHITESPACE
|
|
179
|
+
MapExtent((-20037508.342789244, -20037508.342789244,
|
|
180
|
+
20037508.342789244, 20037508.342789244), SRS('EPSG:900913'))
|
|
181
|
+
"""
|
|
182
|
+
return MapExtent(grid.bbox, grid.srs)
|
|
183
|
+
|
|
184
|
+
class MapExtent(object):
|
|
185
|
+
"""
|
|
186
|
+
>>> me = MapExtent((5, 45, 15, 55), SRS(4326))
|
|
187
|
+
>>> me.llbbox
|
|
188
|
+
(5, 45, 15, 55)
|
|
189
|
+
>>> [int(x) for x in me.bbox_for(SRS(900913))]
|
|
190
|
+
[556597, 5621521, 1669792, 7361866]
|
|
191
|
+
>>> [int(x) for x in me.bbox_for(SRS(4326))]
|
|
192
|
+
[5, 45, 15, 55]
|
|
193
|
+
"""
|
|
194
|
+
is_default = False
|
|
195
|
+
def __init__(self, bbox, srs):
|
|
196
|
+
self._llbbox = None
|
|
197
|
+
self.bbox = bbox
|
|
198
|
+
self.srs = srs
|
|
199
|
+
|
|
200
|
+
@property
|
|
201
|
+
def llbbox(self):
|
|
202
|
+
if not self._llbbox:
|
|
203
|
+
self._llbbox = self.srs.transform_bbox_to(self.srs.get_geographic_srs(), self.bbox)
|
|
204
|
+
return self._llbbox
|
|
205
|
+
|
|
206
|
+
def bbox_for(self, srs):
|
|
207
|
+
if srs == self.srs:
|
|
208
|
+
return self.bbox
|
|
209
|
+
|
|
210
|
+
return self.srs.transform_bbox_to(srs, self.bbox)
|
|
211
|
+
|
|
212
|
+
def __repr__(self):
|
|
213
|
+
return "%s(%r, %r)" % (self.__class__.__name__, self.bbox, self.srs)
|
|
214
|
+
|
|
215
|
+
def __eq__(self, other):
|
|
216
|
+
if not isinstance(other, MapExtent):
|
|
217
|
+
return NotImplemented
|
|
218
|
+
|
|
219
|
+
if self.srs != other.srs:
|
|
220
|
+
return False
|
|
221
|
+
|
|
222
|
+
if self.bbox != other.bbox:
|
|
223
|
+
return False
|
|
224
|
+
|
|
225
|
+
return True
|
|
226
|
+
|
|
227
|
+
def __ne__(self, other):
|
|
228
|
+
if not isinstance(other, MapExtent):
|
|
229
|
+
return NotImplemented
|
|
230
|
+
return not self.__eq__(other)
|
|
231
|
+
|
|
232
|
+
def __add__(self, other):
|
|
233
|
+
if not isinstance(other, MapExtent):
|
|
234
|
+
raise NotImplemented
|
|
235
|
+
if other.is_default:
|
|
236
|
+
return self
|
|
237
|
+
if self.is_default:
|
|
238
|
+
return other
|
|
239
|
+
return MapExtent(merge_bbox(self.llbbox, other.llbbox), self.srs.get_geographic_srs())
|
|
240
|
+
|
|
241
|
+
def contains(self, other):
|
|
242
|
+
if not isinstance(other, MapExtent):
|
|
243
|
+
raise NotImplemented
|
|
244
|
+
if self.is_default:
|
|
245
|
+
# DefaultMapExtent contains everything
|
|
246
|
+
return True
|
|
247
|
+
return bbox_contains(self.bbox, other.bbox_for(self.srs))
|
|
248
|
+
|
|
249
|
+
def intersects(self, other):
|
|
250
|
+
if not isinstance(other, MapExtent):
|
|
251
|
+
raise NotImplemented
|
|
252
|
+
return bbox_intersects(self.bbox, other.bbox_for(self.srs))
|
|
253
|
+
|
|
254
|
+
def intersection(self, other):
|
|
255
|
+
"""
|
|
256
|
+
Returns the intersection of `self` and `other`.
|
|
257
|
+
|
|
258
|
+
>>> e = DefaultMapExtent().intersection(MapExtent((0, 0, 10, 10), SRS(4326)))
|
|
259
|
+
>>> e.bbox, e.srs
|
|
260
|
+
((0, 0, 10, 10), SRS('EPSG:4326'))
|
|
261
|
+
"""
|
|
262
|
+
if not self.intersects(other):
|
|
263
|
+
return None
|
|
264
|
+
|
|
265
|
+
source = self.bbox
|
|
266
|
+
sub = other.bbox_for(self.srs)
|
|
267
|
+
|
|
268
|
+
return MapExtent((
|
|
269
|
+
max(source[0], sub[0]),
|
|
270
|
+
max(source[1], sub[1]),
|
|
271
|
+
min(source[2], sub[2]),
|
|
272
|
+
min(source[3], sub[3])),
|
|
273
|
+
self.srs)
|
|
274
|
+
|
|
275
|
+
def transform(self, srs):
|
|
276
|
+
return MapExtent(self.bbox_for(srs), srs)
|
|
277
|
+
|
|
278
|
+
class DefaultMapExtent(MapExtent):
|
|
279
|
+
"""
|
|
280
|
+
Default extent that covers the whole world.
|
|
281
|
+
Will not affect other extents when added.
|
|
282
|
+
|
|
283
|
+
>>> m1 = MapExtent((0, 0, 10, 10), SRS(4326))
|
|
284
|
+
>>> m2 = MapExtent((10, 0, 20, 10), SRS(4326))
|
|
285
|
+
>>> m3 = DefaultMapExtent()
|
|
286
|
+
>>> (m1 + m2).bbox
|
|
287
|
+
(0, 0, 20, 10)
|
|
288
|
+
>>> (m1 + m3).bbox
|
|
289
|
+
(0, 0, 10, 10)
|
|
290
|
+
"""
|
|
291
|
+
is_default = True
|
|
292
|
+
def __init__(self):
|
|
293
|
+
MapExtent.__init__(self, (-180, -90, 180, 90), SRS(4326))
|
|
294
|
+
|
|
295
|
+
def merge_layer_extents(layers):
|
|
296
|
+
if not layers:
|
|
297
|
+
return DefaultMapExtent()
|
|
298
|
+
layers = layers[:]
|
|
299
|
+
extent = layers.pop().extent
|
|
300
|
+
for layer in layers:
|
|
301
|
+
extent = extent + layer.extent
|
|
302
|
+
return extent
|
|
303
|
+
|
|
304
|
+
class ResolutionConditional(MapLayer):
|
|
305
|
+
supports_meta_tiles = True
|
|
306
|
+
def __init__(self, one, two, resolution, srs, extent, opacity=None):
|
|
307
|
+
MapLayer.__init__(self)
|
|
308
|
+
self.one = one
|
|
309
|
+
self.two = two
|
|
310
|
+
self.res_range = merge_layer_res_ranges([one, two])
|
|
311
|
+
self.resolution = resolution
|
|
312
|
+
self.srs = srs
|
|
313
|
+
|
|
314
|
+
self.opacity = opacity
|
|
315
|
+
self.extent = extent
|
|
316
|
+
|
|
317
|
+
def get_map(self, query):
|
|
318
|
+
self.check_res_range(query)
|
|
319
|
+
bbox = query.bbox
|
|
320
|
+
if query.srs != self.srs:
|
|
321
|
+
bbox = query.srs.transform_bbox_to(self.srs, bbox)
|
|
322
|
+
|
|
323
|
+
xres = (bbox[2] - bbox[0]) / query.size[0]
|
|
324
|
+
yres = (bbox[3] - bbox[1]) / query.size[1]
|
|
325
|
+
res = min(xres, yres)
|
|
326
|
+
log.debug('actual res: %s, threshold res: %s', res, self.resolution)
|
|
327
|
+
|
|
328
|
+
if res > self.resolution:
|
|
329
|
+
return self.one.get_map(query)
|
|
330
|
+
else:
|
|
331
|
+
return self.two.get_map(query)
|
|
332
|
+
|
|
333
|
+
class SRSConditional(MapLayer):
|
|
334
|
+
supports_meta_tiles = True
|
|
335
|
+
|
|
336
|
+
def __init__(self, layers, extent, opacity=None, preferred_srs=None):
|
|
337
|
+
MapLayer.__init__(self)
|
|
338
|
+
self.srs_map = {}
|
|
339
|
+
self.res_range = merge_layer_res_ranges([l[0] for l in layers])
|
|
340
|
+
|
|
341
|
+
supported_srs = []
|
|
342
|
+
for layer, srs in layers:
|
|
343
|
+
supported_srs.append(srs)
|
|
344
|
+
self.srs_map[srs] = layer
|
|
345
|
+
self.supported_srs = SupportedSRS(supported_srs, preferred_srs)
|
|
346
|
+
self.extent = extent
|
|
347
|
+
self.opacity = opacity
|
|
348
|
+
|
|
349
|
+
def get_map(self, query):
|
|
350
|
+
self.check_res_range(query)
|
|
351
|
+
layer = self._select_layer(query.srs)
|
|
352
|
+
return layer.get_map(query)
|
|
353
|
+
|
|
354
|
+
def _select_layer(self, query_srs):
|
|
355
|
+
srs = self.supported_srs.best_srs(query_srs)
|
|
356
|
+
return self.srs_map[srs]
|
|
357
|
+
|
|
358
|
+
class DirectMapLayer(MapLayer):
|
|
359
|
+
supports_meta_tiles = True
|
|
360
|
+
|
|
361
|
+
def __init__(self, source, extent):
|
|
362
|
+
MapLayer.__init__(self)
|
|
363
|
+
self.source = source
|
|
364
|
+
self.res_range = getattr(source, 'res_range', None)
|
|
365
|
+
self.extent = extent
|
|
366
|
+
|
|
367
|
+
def get_map(self, query):
|
|
368
|
+
self.check_res_range(query)
|
|
369
|
+
return self.source.get_map(query)
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def merge_layer_res_ranges(layers):
|
|
373
|
+
ranges = [s.res_range for s in layers
|
|
374
|
+
if hasattr(s, 'res_range')]
|
|
375
|
+
|
|
376
|
+
if ranges:
|
|
377
|
+
ranges = reduce(merge_resolution_range, ranges)
|
|
378
|
+
|
|
379
|
+
return ranges
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
class CacheMapLayer(MapLayer):
|
|
383
|
+
supports_meta_tiles = True
|
|
384
|
+
|
|
385
|
+
def __init__(self, tile_manager, extent=None, image_opts=None,
|
|
386
|
+
max_tile_limit=None):
|
|
387
|
+
MapLayer.__init__(self, image_opts=image_opts)
|
|
388
|
+
self.tile_manager = tile_manager
|
|
389
|
+
self.grid = tile_manager.grid
|
|
390
|
+
self.extent = extent or map_extent_from_grid(self.grid)
|
|
391
|
+
self.res_range = []
|
|
392
|
+
if not self.tile_manager.rescale_tiles:
|
|
393
|
+
self.res_range = merge_layer_res_ranges(self.tile_manager.sources)
|
|
394
|
+
self.max_tile_limit = max_tile_limit
|
|
395
|
+
|
|
396
|
+
def get_map(self, query):
|
|
397
|
+
self.check_res_range(query)
|
|
398
|
+
|
|
399
|
+
if query.tiled_only:
|
|
400
|
+
self._check_tiled(query)
|
|
401
|
+
|
|
402
|
+
query_extent = MapExtent(query.bbox, query.srs)
|
|
403
|
+
if not query.tiled_only and self.extent and not self.extent.contains(query_extent):
|
|
404
|
+
if not self.extent.intersects(query_extent):
|
|
405
|
+
raise BlankImage()
|
|
406
|
+
size, offset, bbox = bbox_position_in_image(query.bbox, query.size, self.extent.bbox_for(query.srs))
|
|
407
|
+
if size[0] == 0 or size[1] == 0:
|
|
408
|
+
raise BlankImage()
|
|
409
|
+
src_query = MapQuery(bbox, size, query.srs, query.format, dimensions=query.dimensions)
|
|
410
|
+
resp = self._image(src_query)
|
|
411
|
+
result = SubImageSource(resp, size=query.size, offset=offset, image_opts=self.image_opts,
|
|
412
|
+
cacheable=resp.cacheable)
|
|
413
|
+
else:
|
|
414
|
+
result = self._image(query)
|
|
415
|
+
return result
|
|
416
|
+
|
|
417
|
+
def _check_tiled(self, query):
|
|
418
|
+
if query.format != self.tile_manager.format:
|
|
419
|
+
raise MapError("invalid tile format, use %s" % self.tile_manager.format)
|
|
420
|
+
if query.size != self.grid.tile_size:
|
|
421
|
+
raise MapError("invalid tile size (use %dx%d)" % self.grid.tile_size)
|
|
422
|
+
|
|
423
|
+
def _image(self, query):
|
|
424
|
+
try:
|
|
425
|
+
src_bbox, tile_grid, affected_tile_coords = \
|
|
426
|
+
self.grid.get_affected_tiles(query.bbox, query.size,
|
|
427
|
+
req_srs=query.srs)
|
|
428
|
+
except NoTiles:
|
|
429
|
+
raise BlankImage()
|
|
430
|
+
except GridError as ex:
|
|
431
|
+
raise MapBBOXError(ex.args[0])
|
|
432
|
+
|
|
433
|
+
num_tiles = tile_grid[0] * tile_grid[1]
|
|
434
|
+
|
|
435
|
+
if self.max_tile_limit and num_tiles >= self.max_tile_limit:
|
|
436
|
+
raise MapBBOXError("too many tiles, max_tile_limit: %s, num_tiles: %s" % (self.max_tile_limit, num_tiles))
|
|
437
|
+
|
|
438
|
+
if query.tiled_only:
|
|
439
|
+
if num_tiles > 1:
|
|
440
|
+
raise MapBBOXError("not a single tile")
|
|
441
|
+
bbox = query.bbox
|
|
442
|
+
if not bbox_equals(bbox, src_bbox, abs((bbox[2]-bbox[0])/query.size[0]/10),
|
|
443
|
+
abs((bbox[3]-bbox[1])/query.size[1]/10)):
|
|
444
|
+
raise MapBBOXError("query does not align to tile boundaries")
|
|
445
|
+
|
|
446
|
+
with self.tile_manager.session():
|
|
447
|
+
tile_collection = self.tile_manager.load_tile_coords(affected_tile_coords, with_metadata=query.tiled_only, dimensions=query.dimensions)
|
|
448
|
+
|
|
449
|
+
if tile_collection.empty:
|
|
450
|
+
raise BlankImage()
|
|
451
|
+
|
|
452
|
+
if query.tiled_only:
|
|
453
|
+
tile = tile_collection[0].source
|
|
454
|
+
tile.image_opts = self.tile_manager.image_opts
|
|
455
|
+
tile.cacheable = tile_collection[0].cacheable
|
|
456
|
+
return tile
|
|
457
|
+
|
|
458
|
+
tile_sources = [tile.source for tile in tile_collection]
|
|
459
|
+
tiled_image = TiledImage(tile_sources, src_bbox=src_bbox, src_srs=self.grid.srs,
|
|
460
|
+
tile_grid=tile_grid, tile_size=self.grid.tile_size)
|
|
461
|
+
try:
|
|
462
|
+
return tiled_image.transform(query.bbox, query.srs, query.size,
|
|
463
|
+
self.tile_manager.image_opts)
|
|
464
|
+
except ProjError:
|
|
465
|
+
raise MapBBOXError("could not transform query BBOX")
|
|
466
|
+
except IOError as ex:
|
|
467
|
+
from mapproxy.source import SourceError
|
|
468
|
+
raise SourceError("unable to transform image: %s" % ex)
|
|
469
|
+
|
|
470
|
+
|
mapproxy/multiapp.py
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# -:- encoding: utf-8 -:-
|
|
2
|
+
# This file is part of the MapProxy project.
|
|
3
|
+
# Copyright (C) 2010 Omniscale <http://omniscale.de>
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
import os
|
|
18
|
+
|
|
19
|
+
from mapproxy.request import Request
|
|
20
|
+
from mapproxy.response import Response
|
|
21
|
+
from mapproxy.util.collections import LRU
|
|
22
|
+
from mapproxy.wsgiapp import make_wsgi_app as make_mapproxy_wsgi_app
|
|
23
|
+
from mapproxy.util.escape import escape_html
|
|
24
|
+
|
|
25
|
+
from threading import Lock
|
|
26
|
+
|
|
27
|
+
import logging
|
|
28
|
+
log = logging.getLogger(__name__)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def asbool(value):
|
|
32
|
+
"""
|
|
33
|
+
>>> all([asbool(True), asbool('trUE'), asbool('ON'), asbool(1)])
|
|
34
|
+
True
|
|
35
|
+
>>> any([asbool(False), asbool('false'), asbool('foo'), asbool(None)])
|
|
36
|
+
False
|
|
37
|
+
"""
|
|
38
|
+
value = str(value).lower()
|
|
39
|
+
return value in ('1', 'true', 'yes', 'on')
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def app_factory(global_options, config_dir, allow_listing=False, **local_options):
|
|
43
|
+
"""
|
|
44
|
+
Create a new MultiMapProxy app.
|
|
45
|
+
|
|
46
|
+
:param config_dir: directory with all mapproxy configurations
|
|
47
|
+
:param allow_listing: allow to list all available apps
|
|
48
|
+
"""
|
|
49
|
+
return make_wsgi_app(config_dir, asbool(allow_listing))
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def make_wsgi_app(config_dir, allow_listing=True, debug=False):
|
|
53
|
+
"""
|
|
54
|
+
Create a MultiMapProxy with the given config directory.
|
|
55
|
+
|
|
56
|
+
:param config_dir: the directory with all project configurations.
|
|
57
|
+
:param allow_listing: True if MapProxy should list all instances
|
|
58
|
+
at the root URL
|
|
59
|
+
"""
|
|
60
|
+
config_dir = os.path.abspath(config_dir)
|
|
61
|
+
loader = DirectoryConfLoader(config_dir)
|
|
62
|
+
return MultiMapProxy(loader, list_apps=allow_listing, debug=debug)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class MultiMapProxy(object):
|
|
66
|
+
|
|
67
|
+
def __init__(self, loader, list_apps=False, app_cache_size=100, debug=False):
|
|
68
|
+
self.loader = loader
|
|
69
|
+
self.list_apps = list_apps
|
|
70
|
+
self._app_init_lock = Lock()
|
|
71
|
+
self.apps = LRU(app_cache_size)
|
|
72
|
+
self.debug = debug
|
|
73
|
+
|
|
74
|
+
def __call__(self, environ, start_response):
|
|
75
|
+
req = Request(environ)
|
|
76
|
+
return self.handle(req)(environ, start_response)
|
|
77
|
+
|
|
78
|
+
def handle(self, req):
|
|
79
|
+
app_name = req.pop_path()
|
|
80
|
+
if not app_name:
|
|
81
|
+
return self.index_list(req)
|
|
82
|
+
|
|
83
|
+
if not app_name or (
|
|
84
|
+
app_name not in self.apps and not self.loader.app_available(app_name)
|
|
85
|
+
):
|
|
86
|
+
return Response('not found', status=404)
|
|
87
|
+
|
|
88
|
+
# safe instance/app name for authorization
|
|
89
|
+
req.environ['mapproxy.instance_name'] = app_name
|
|
90
|
+
return self.proj_app(app_name)
|
|
91
|
+
|
|
92
|
+
def index_list(self, req):
|
|
93
|
+
"""
|
|
94
|
+
Return greeting response with a list of available apps (if enabled with list_apps).
|
|
95
|
+
"""
|
|
96
|
+
import mapproxy.version
|
|
97
|
+
html = "<html><body><h1>Welcome to MapProxy %s</h1>" % mapproxy.version.version
|
|
98
|
+
|
|
99
|
+
url = escape_html(req.script_url)
|
|
100
|
+
if self.list_apps:
|
|
101
|
+
html += "<h2>available instances:</h2><ul>"
|
|
102
|
+
html += '\n'.join('<li><a href="%(url)s/%(name)s/">%(name)s</a></li>' % {'url': url, 'name': app}
|
|
103
|
+
for app in self.loader.available_apps())
|
|
104
|
+
html += '</ul>'
|
|
105
|
+
html += '</body></html>'
|
|
106
|
+
return Response(html, content_type='text/html')
|
|
107
|
+
|
|
108
|
+
def proj_app(self, proj_name):
|
|
109
|
+
"""
|
|
110
|
+
Return the (cached) project app.
|
|
111
|
+
"""
|
|
112
|
+
proj_app, timestamps = self.apps.get(proj_name, (None, None))
|
|
113
|
+
|
|
114
|
+
if proj_app:
|
|
115
|
+
if self.loader.needs_reload(proj_name, timestamps):
|
|
116
|
+
# discard cached app
|
|
117
|
+
proj_app = None
|
|
118
|
+
|
|
119
|
+
if not proj_app:
|
|
120
|
+
with self._app_init_lock:
|
|
121
|
+
proj_app, timestamps = self.apps.get(proj_name, (None, None))
|
|
122
|
+
if self.loader.needs_reload(proj_name, timestamps):
|
|
123
|
+
proj_app, timestamps = self.create_app(proj_name)
|
|
124
|
+
self.apps[proj_name] = proj_app, timestamps
|
|
125
|
+
else:
|
|
126
|
+
proj_app, timestamps = self.apps[proj_name]
|
|
127
|
+
|
|
128
|
+
return proj_app
|
|
129
|
+
|
|
130
|
+
def create_app(self, proj_name):
|
|
131
|
+
"""
|
|
132
|
+
Returns a new configured MapProxy app and a dict with the
|
|
133
|
+
timestamps of all configuration files.
|
|
134
|
+
"""
|
|
135
|
+
mapproxy_conf = self.loader.app_conf(proj_name)['mapproxy_conf']
|
|
136
|
+
log.info('initializing project app %s with %s', proj_name, mapproxy_conf)
|
|
137
|
+
app = make_mapproxy_wsgi_app(mapproxy_conf, debug=self.debug)
|
|
138
|
+
return app, app.config_files
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class ConfLoader(object):
|
|
142
|
+
def needs_reload(self, app_name, timestamps):
|
|
143
|
+
"""
|
|
144
|
+
Returns ``True`` if the configuration of `app_name` changed
|
|
145
|
+
since `timestamp`.
|
|
146
|
+
"""
|
|
147
|
+
raise NotImplementedError()
|
|
148
|
+
|
|
149
|
+
def app_available(self, app_name):
|
|
150
|
+
"""
|
|
151
|
+
Returns ``True`` if `app_name` is available.
|
|
152
|
+
"""
|
|
153
|
+
raise NotImplementedError()
|
|
154
|
+
|
|
155
|
+
def available_apps(self):
|
|
156
|
+
"""
|
|
157
|
+
Returns a list with all available lists.
|
|
158
|
+
"""
|
|
159
|
+
raise NotImplementedError()
|
|
160
|
+
|
|
161
|
+
def app_conf(self, app_name):
|
|
162
|
+
"""
|
|
163
|
+
Returns a configuration dict for the given `app_name`,
|
|
164
|
+
None if the app is not found.
|
|
165
|
+
|
|
166
|
+
The configuration dict contains at least 'mapproxy_conf'
|
|
167
|
+
with the filename of the configuration.
|
|
168
|
+
"""
|
|
169
|
+
raise NotImplementedError()
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class DirectoryConfLoader(ConfLoader):
|
|
173
|
+
"""
|
|
174
|
+
Load application configurations from a directory.
|
|
175
|
+
"""
|
|
176
|
+
|
|
177
|
+
def __init__(self, base_dir, suffix='.yaml'):
|
|
178
|
+
self.base_dir = base_dir
|
|
179
|
+
self.suffix = suffix
|
|
180
|
+
|
|
181
|
+
def needs_reload(self, app_name, timestamps):
|
|
182
|
+
if not timestamps:
|
|
183
|
+
return True
|
|
184
|
+
for conf_file, timestamp in timestamps.items():
|
|
185
|
+
m_time = os.path.getmtime(conf_file)
|
|
186
|
+
if m_time > timestamp:
|
|
187
|
+
return True
|
|
188
|
+
return False
|
|
189
|
+
|
|
190
|
+
def _is_conf_file(self, fname):
|
|
191
|
+
if not os.path.isfile(fname):
|
|
192
|
+
return False
|
|
193
|
+
if self.suffix:
|
|
194
|
+
return fname.lower().endswith(self.suffix)
|
|
195
|
+
else:
|
|
196
|
+
return True
|
|
197
|
+
|
|
198
|
+
def app_name_from_filename(self, fname):
|
|
199
|
+
"""
|
|
200
|
+
>>> DirectoryConfLoader('/tmp/').app_name_from_filename('/tmp/foobar.yaml')
|
|
201
|
+
'foobar'
|
|
202
|
+
"""
|
|
203
|
+
_path, fname = os.path.split(fname)
|
|
204
|
+
app_name, _ext = os.path.splitext(fname)
|
|
205
|
+
return app_name
|
|
206
|
+
|
|
207
|
+
def filename_from_app_name(self, app_name):
|
|
208
|
+
"""
|
|
209
|
+
>>> DirectoryConfLoader('/tmp/').filename_from_app_name('foobar')
|
|
210
|
+
'/tmp/foobar.yaml'
|
|
211
|
+
"""
|
|
212
|
+
return os.path.join(self.base_dir, app_name + self.suffix or '')
|
|
213
|
+
|
|
214
|
+
def available_apps(self):
|
|
215
|
+
apps = []
|
|
216
|
+
for f in os.listdir(self.base_dir):
|
|
217
|
+
if self._is_conf_file(os.path.join(self.base_dir, f)):
|
|
218
|
+
app_name = self.app_name_from_filename(f)
|
|
219
|
+
apps.append(app_name)
|
|
220
|
+
apps.sort()
|
|
221
|
+
return apps
|
|
222
|
+
|
|
223
|
+
def app_available(self, app_name):
|
|
224
|
+
conf_file = self.filename_from_app_name(app_name)
|
|
225
|
+
return self._is_conf_file(conf_file)
|
|
226
|
+
|
|
227
|
+
def app_conf(self, app_name):
|
|
228
|
+
conf_file = self.filename_from_app_name(app_name)
|
|
229
|
+
if not self._is_conf_file(conf_file):
|
|
230
|
+
return None
|
|
231
|
+
return {'mapproxy_conf': conf_file}
|