MapProxy 2.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- MapProxy-2.1.0.dist-info/AUTHORS.txt +33 -0
- MapProxy-2.1.0.dist-info/COPYING.txt +60 -0
- MapProxy-2.1.0.dist-info/LICENSE.txt +202 -0
- MapProxy-2.1.0.dist-info/METADATA +165 -0
- MapProxy-2.1.0.dist-info/RECORD +459 -0
- MapProxy-2.1.0.dist-info/WHEEL +5 -0
- MapProxy-2.1.0.dist-info/entry_points.txt +4 -0
- MapProxy-2.1.0.dist-info/top_level.txt +1 -0
- mapproxy/__init__.py +0 -0
- mapproxy/cache/__init__.py +36 -0
- mapproxy/cache/azureblob.py +145 -0
- mapproxy/cache/base.py +120 -0
- mapproxy/cache/compact.py +665 -0
- mapproxy/cache/couchdb.py +301 -0
- mapproxy/cache/dummy.py +36 -0
- mapproxy/cache/file.py +200 -0
- mapproxy/cache/geopackage.py +647 -0
- mapproxy/cache/legend.py +87 -0
- mapproxy/cache/mbtiles.py +411 -0
- mapproxy/cache/meta.py +80 -0
- mapproxy/cache/path.py +261 -0
- mapproxy/cache/redis.py +152 -0
- mapproxy/cache/renderd.py +100 -0
- mapproxy/cache/riak.py +206 -0
- mapproxy/cache/s3.py +209 -0
- mapproxy/cache/tile.py +736 -0
- mapproxy/client/__init__.py +0 -0
- mapproxy/client/arcgis.py +82 -0
- mapproxy/client/cgi.py +141 -0
- mapproxy/client/http.py +295 -0
- mapproxy/client/log.py +33 -0
- mapproxy/client/tile.py +158 -0
- mapproxy/client/wms.py +255 -0
- mapproxy/compat/__init__.py +0 -0
- mapproxy/compat/image.py +86 -0
- mapproxy/config/__init__.py +22 -0
- mapproxy/config/config-schema.json +813 -0
- mapproxy/config/config.py +213 -0
- mapproxy/config/coverage.py +108 -0
- mapproxy/config/defaults.py +102 -0
- mapproxy/config/loader.py +2399 -0
- mapproxy/config/spec.py +657 -0
- mapproxy/config/validator.py +242 -0
- mapproxy/config_template/__init__.py +0 -0
- mapproxy/config_template/base_config/config.wsgi +10 -0
- mapproxy/config_template/base_config/full_example.yaml +598 -0
- mapproxy/config_template/base_config/full_seed_example.yaml +79 -0
- mapproxy/config_template/base_config/log.ini +35 -0
- mapproxy/config_template/base_config/mapproxy.yaml +60 -0
- mapproxy/config_template/base_config/seed.yaml +27 -0
- mapproxy/exception.py +149 -0
- mapproxy/featureinfo.py +251 -0
- mapproxy/grid.py +1199 -0
- mapproxy/image/__init__.py +549 -0
- mapproxy/image/fonts/DejaVuSans.ttf +0 -0
- mapproxy/image/fonts/DejaVuSansMono.ttf +0 -0
- mapproxy/image/fonts/LICENSE +99 -0
- mapproxy/image/fonts/__init__.py +0 -0
- mapproxy/image/mask.py +79 -0
- mapproxy/image/merge.py +323 -0
- mapproxy/image/message.py +357 -0
- mapproxy/image/opts.py +185 -0
- mapproxy/image/tile.py +171 -0
- mapproxy/image/transform.py +350 -0
- mapproxy/layer.py +489 -0
- mapproxy/multiapp.py +230 -0
- mapproxy/proj.py +309 -0
- mapproxy/request/__init__.py +18 -0
- mapproxy/request/arcgis.py +268 -0
- mapproxy/request/base.py +466 -0
- mapproxy/request/tile.py +131 -0
- mapproxy/request/wms/__init__.py +829 -0
- mapproxy/request/wms/exception.py +107 -0
- mapproxy/request/wmts.py +442 -0
- mapproxy/response.py +237 -0
- mapproxy/script/__init__.py +0 -0
- mapproxy/script/conf/__init__.py +0 -0
- mapproxy/script/conf/app.py +222 -0
- mapproxy/script/conf/caches.py +44 -0
- mapproxy/script/conf/geopackage.py +136 -0
- mapproxy/script/conf/layers.py +54 -0
- mapproxy/script/conf/seeds.py +36 -0
- mapproxy/script/conf/sources.py +88 -0
- mapproxy/script/conf/utils.py +148 -0
- mapproxy/script/defrag.py +187 -0
- mapproxy/script/export.py +337 -0
- mapproxy/script/grids.py +198 -0
- mapproxy/script/scales.py +134 -0
- mapproxy/script/util.py +410 -0
- mapproxy/script/wms_capabilities.py +160 -0
- mapproxy/seed/__init__.py +0 -0
- mapproxy/seed/cachelock.py +127 -0
- mapproxy/seed/cleanup.py +191 -0
- mapproxy/seed/config.py +481 -0
- mapproxy/seed/script.py +391 -0
- mapproxy/seed/seeder.py +551 -0
- mapproxy/seed/spec.py +66 -0
- mapproxy/seed/util.py +266 -0
- mapproxy/service/__init__.py +14 -0
- mapproxy/service/base.py +45 -0
- mapproxy/service/demo.py +364 -0
- mapproxy/service/kml.py +333 -0
- mapproxy/service/ows.py +39 -0
- mapproxy/service/template_helper.py +55 -0
- mapproxy/service/templates/demo/capabilities_demo.html +18 -0
- mapproxy/service/templates/demo/demo.html +181 -0
- mapproxy/service/templates/demo/openlayers-demo.cfg +16 -0
- mapproxy/service/templates/demo/static/img/blank.gif +0 -0
- mapproxy/service/templates/demo/static/img/east-mini.png +0 -0
- mapproxy/service/templates/demo/static/img/north-mini.png +0 -0
- mapproxy/service/templates/demo/static/img/south-mini.png +0 -0
- mapproxy/service/templates/demo/static/img/west-mini.png +0 -0
- mapproxy/service/templates/demo/static/img/zoom-minus-mini.png +0 -0
- mapproxy/service/templates/demo/static/img/zoom-plus-mini.png +0 -0
- mapproxy/service/templates/demo/static/img/zoom-world-mini.png +0 -0
- mapproxy/service/templates/demo/static/logo.png +0 -0
- mapproxy/service/templates/demo/static/ol.css +345 -0
- mapproxy/service/templates/demo/static/ol.js +4 -0
- mapproxy/service/templates/demo/static/proj4.min.js +1 -0
- mapproxy/service/templates/demo/static/proj4defs.js +1 -0
- mapproxy/service/templates/demo/static/site.css +137 -0
- mapproxy/service/templates/demo/static/theme/default/framedCloud.css +0 -0
- mapproxy/service/templates/demo/static/theme/default/google.css +17 -0
- mapproxy/service/templates/demo/static/theme/default/ie6-style.css +10 -0
- mapproxy/service/templates/demo/static/theme/default/style.css +482 -0
- mapproxy/service/templates/demo/static.html +34 -0
- mapproxy/service/templates/demo/tms_demo.html +117 -0
- mapproxy/service/templates/demo/wms_demo.html +144 -0
- mapproxy/service/templates/demo/wmts_demo.html +118 -0
- mapproxy/service/templates/tms_capabilities.xml +13 -0
- mapproxy/service/templates/tms_exception.xml +4 -0
- mapproxy/service/templates/tms_root_resource.xml +7 -0
- mapproxy/service/templates/tms_tilemap_capabilities.xml +14 -0
- mapproxy/service/templates/wms100capabilities.xml +112 -0
- mapproxy/service/templates/wms100exception.xml +4 -0
- mapproxy/service/templates/wms110capabilities.xml +152 -0
- mapproxy/service/templates/wms110exception.xml +5 -0
- mapproxy/service/templates/wms111capabilities.xml +183 -0
- mapproxy/service/templates/wms111exception.xml +5 -0
- mapproxy/service/templates/wms130capabilities.xml +326 -0
- mapproxy/service/templates/wms130exception.xml +8 -0
- mapproxy/service/templates/wmts100capabilities.xml +155 -0
- mapproxy/service/templates/wmts100exception.xml +9 -0
- mapproxy/service/tile.py +540 -0
- mapproxy/service/wms.py +868 -0
- mapproxy/service/wmts.py +387 -0
- mapproxy/source/__init__.py +83 -0
- mapproxy/source/arcgis.py +39 -0
- mapproxy/source/error.py +40 -0
- mapproxy/source/mapnik.py +262 -0
- mapproxy/source/tile.py +97 -0
- mapproxy/source/wms.py +273 -0
- mapproxy/srs.py +734 -0
- mapproxy/template.py +54 -0
- mapproxy/test/__init__.py +0 -0
- mapproxy/test/conftest.py +8 -0
- mapproxy/test/helper.py +255 -0
- mapproxy/test/http.py +511 -0
- mapproxy/test/image.py +219 -0
- mapproxy/test/mocker.py +2291 -0
- mapproxy/test/schemas/inspire/common/1.0/common.xsd +1461 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_bul.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_cze.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_dan.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_dut.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_eng.xsd +155 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_est.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_fin.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_fre.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_ger.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_gle.xsd +109 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_gre.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_hun.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_ita.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_lav.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_lit.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_mlt.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_pol.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_por.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_rum.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_slo.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_slv.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_spa.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_swe.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/network.xsd +521 -0
- mapproxy/test/schemas/inspire/inspire_vs/1.0/inspire_vs.xsd +19 -0
- mapproxy/test/schemas/kml/2.2.0/ReadMe.txt +14 -0
- mapproxy/test/schemas/kml/2.2.0/atom-author-link.xsd +66 -0
- mapproxy/test/schemas/kml/2.2.0/ogckml22.xsd +1646 -0
- mapproxy/test/schemas/kml/2.2.0/xAL.xsd +1680 -0
- mapproxy/test/schemas/ows/1.1.0/ReadMe.txt +87 -0
- mapproxy/test/schemas/ows/1.1.0/ows19115subset.xsd +235 -0
- mapproxy/test/schemas/ows/1.1.0/owsAll.xsd +23 -0
- mapproxy/test/schemas/ows/1.1.0/owsCommon.xsd +157 -0
- mapproxy/test/schemas/ows/1.1.0/owsContents.xsd +86 -0
- mapproxy/test/schemas/ows/1.1.0/owsDataIdentification.xsd +127 -0
- mapproxy/test/schemas/ows/1.1.0/owsDomainType.xsd +279 -0
- mapproxy/test/schemas/ows/1.1.0/owsExceptionReport.xsd +76 -0
- mapproxy/test/schemas/ows/1.1.0/owsGetCapabilities.xsd +112 -0
- mapproxy/test/schemas/ows/1.1.0/owsGetResourceByID.xsd +51 -0
- mapproxy/test/schemas/ows/1.1.0/owsInputOutputData.xsd +59 -0
- mapproxy/test/schemas/ows/1.1.0/owsManifest.xsd +125 -0
- mapproxy/test/schemas/ows/1.1.0/owsOperationsMetadata.xsd +140 -0
- mapproxy/test/schemas/ows/1.1.0/owsServiceIdentification.xsd +60 -0
- mapproxy/test/schemas/ows/1.1.0/owsServiceProvider.xsd +47 -0
- mapproxy/test/schemas/sld/1.1.0/sld_capabilities.xsd +27 -0
- mapproxy/test/schemas/wms/1.0.0/capabilities_1_0_0.dtd +353 -0
- mapproxy/test/schemas/wms/1.0.0/capabilities_1_0_0.xml +188 -0
- mapproxy/test/schemas/wms/1.0.7/capabilities_1_0_7.dtd +524 -0
- mapproxy/test/schemas/wms/1.0.7/capabilities_1_0_7.xml +260 -0
- mapproxy/test/schemas/wms/1.1.0/capabilities_1_1_0.dtd +273 -0
- mapproxy/test/schemas/wms/1.1.0/capabilities_1_1_0.xml +303 -0
- mapproxy/test/schemas/wms/1.1.0/exception_1_1_0.dtd +6 -0
- mapproxy/test/schemas/wms/1.1.0/exception_1_1_0.xml +33 -0
- mapproxy/test/schemas/wms/1.1.1/OGC-exception.xsd +68 -0
- mapproxy/test/schemas/wms/1.1.1/WMS_DescribeLayerResponse.dtd +22 -0
- mapproxy/test/schemas/wms/1.1.1/WMS_MS_Capabilities.dtd +274 -0
- mapproxy/test/schemas/wms/1.1.1/WMS_exception_1_1_1.dtd +5 -0
- mapproxy/test/schemas/wms/1.1.1/capabilities_1_1_1.dtd +276 -0
- mapproxy/test/schemas/wms/1.1.1/capabilities_1_1_1.xml +303 -0
- mapproxy/test/schemas/wms/1.1.1/exception_1_1_1.dtd +6 -0
- mapproxy/test/schemas/wms/1.1.1/exception_1_1_1.xml +33 -0
- mapproxy/test/schemas/wms/1.3.0/ReadMe.txt +8 -0
- mapproxy/test/schemas/wms/1.3.0/capabilities_1_3_0.xml +277 -0
- mapproxy/test/schemas/wms/1.3.0/capabilities_1_3_0.xsd +611 -0
- mapproxy/test/schemas/wms/1.3.0/exceptions_1_3_0.xml +34 -0
- mapproxy/test/schemas/wms/1.3.0/exceptions_1_3_0.xsd +28 -0
- mapproxy/test/schemas/wmsc/1.1.1/OGC-exception.xsd +68 -0
- mapproxy/test/schemas/wmsc/1.1.1/WMS_DescribeLayerResponse.dtd +22 -0
- mapproxy/test/schemas/wmsc/1.1.1/WMS_MS_Capabilities.dtd +283 -0
- mapproxy/test/schemas/wmsc/1.1.1/WMS_exception_1_1_1.dtd +5 -0
- mapproxy/test/schemas/wmsc/1.1.1/capabilities_1_1_1.dtd +276 -0
- mapproxy/test/schemas/wmsc/1.1.1/capabilities_1_1_1.xml +303 -0
- mapproxy/test/schemas/wmsc/1.1.1/exception_1_1_1.dtd +6 -0
- mapproxy/test/schemas/wmsc/1.1.1/exception_1_1_1.xml +33 -0
- mapproxy/test/schemas/wmts/1.0/ReadMe.txt +32 -0
- mapproxy/test/schemas/wmts/1.0/wmts.xsd +28 -0
- mapproxy/test/schemas/wmts/1.0/wmtsAbstract.wsdl +151 -0
- mapproxy/test/schemas/wmts/1.0/wmtsGetCapabilities_request.xsd +38 -0
- mapproxy/test/schemas/wmts/1.0/wmtsGetCapabilities_response.xsd +564 -0
- mapproxy/test/schemas/wmts/1.0/wmtsGetFeatureInfo_request.xsd +57 -0
- mapproxy/test/schemas/wmts/1.0/wmtsGetFeatureInfo_response.xsd +72 -0
- mapproxy/test/schemas/wmts/1.0/wmtsGetTile_request.xsd +91 -0
- mapproxy/test/schemas/wmts/1.0/wmtsKVP.xsd +76 -0
- mapproxy/test/schemas/wmts/1.0/wmtsPayload_response.xsd +70 -0
- mapproxy/test/schemas/xlink/1.0.0/ReadMe.txt +6 -0
- mapproxy/test/schemas/xlink/1.0.0/xlinks.xsd +122 -0
- mapproxy/test/schemas/xml.xsd +287 -0
- mapproxy/test/system/__init__.py +98 -0
- mapproxy/test/system/fixture/arcgis.yaml +57 -0
- mapproxy/test/system/fixture/auth.yaml +70 -0
- mapproxy/test/system/fixture/cache.mbtiles +0 -0
- mapproxy/test/system/fixture/cache_azureblob.yaml +59 -0
- mapproxy/test/system/fixture/cache_band_merge.yaml +73 -0
- mapproxy/test/system/fixture/cache_bulk_meta_tiles.yaml +24 -0
- mapproxy/test/system/fixture/cache_coverage.yaml +84 -0
- mapproxy/test/system/fixture/cache_data/dop_cache_EPSG3857/00/000/000/000/000/000/000.png +0 -0
- mapproxy/test/system/fixture/cache_data/wms_cache_EPSG900913/01/000/000/000/000/000/001.jpeg +0 -0
- mapproxy/test/system/fixture/cache_data/wms_cache_transparent_EPSG900913/01/000/000/000/000/000/001.png +0 -0
- mapproxy/test/system/fixture/cache_geopackage.yaml +56 -0
- mapproxy/test/system/fixture/cache_grid_names.yaml +50 -0
- mapproxy/test/system/fixture/cache_mbtiles.yaml +28 -0
- mapproxy/test/system/fixture/cache_s3.yaml +58 -0
- mapproxy/test/system/fixture/cache_source.yaml +81 -0
- mapproxy/test/system/fixture/combined_sources.yaml +130 -0
- mapproxy/test/system/fixture/coverage.yaml +77 -0
- mapproxy/test/system/fixture/demo.yaml +135 -0
- mapproxy/test/system/fixture/dimension.yaml +59 -0
- mapproxy/test/system/fixture/disable_storage.yaml +25 -0
- mapproxy/test/system/fixture/empty_ogrdata.geojson +1 -0
- mapproxy/test/system/fixture/formats.yaml +72 -0
- mapproxy/test/system/fixture/inspire.yaml +101 -0
- mapproxy/test/system/fixture/inspire_full.yaml +124 -0
- mapproxy/test/system/fixture/kml_layer.yaml +66 -0
- mapproxy/test/system/fixture/layer.yaml +260 -0
- mapproxy/test/system/fixture/layergroups.yaml +57 -0
- mapproxy/test/system/fixture/layergroups_root.yaml +106 -0
- mapproxy/test/system/fixture/legendgraphic.yaml +93 -0
- mapproxy/test/system/fixture/mapnik_source.yaml +66 -0
- mapproxy/test/system/fixture/mapproxy_export.yaml +12 -0
- mapproxy/test/system/fixture/mapserver.yaml +23 -0
- mapproxy/test/system/fixture/minimal_cgi.py +16 -0
- mapproxy/test/system/fixture/mixed_mode.yaml +49 -0
- mapproxy/test/system/fixture/multi_cache_layers.yaml +111 -0
- mapproxy/test/system/fixture/multiapp1.yaml +20 -0
- mapproxy/test/system/fixture/multiapp2.yaml +19 -0
- mapproxy/test/system/fixture/renderd_client.yaml +55 -0
- mapproxy/test/system/fixture/scalehints.yaml +70 -0
- mapproxy/test/system/fixture/seed.yaml +94 -0
- mapproxy/test/system/fixture/seed_mapproxy.yaml +39 -0
- mapproxy/test/system/fixture/seed_old.yaml +12 -0
- mapproxy/test/system/fixture/seed_timeouts.yaml +12 -0
- mapproxy/test/system/fixture/seed_timeouts_mapproxy.yaml +27 -0
- mapproxy/test/system/fixture/seedonly.yaml +51 -0
- mapproxy/test/system/fixture/sld.yaml +35 -0
- mapproxy/test/system/fixture/source_errors.yaml +84 -0
- mapproxy/test/system/fixture/source_errors_raise.yaml +82 -0
- mapproxy/test/system/fixture/tileservice_origin.yaml +26 -0
- mapproxy/test/system/fixture/tileservice_refresh.yaml +59 -0
- mapproxy/test/system/fixture/tilesource_minmax_res.yaml +22 -0
- mapproxy/test/system/fixture/util-conf-base-grids.yaml +5 -0
- mapproxy/test/system/fixture/util-conf-overwrite.yaml +13 -0
- mapproxy/test/system/fixture/util-conf-wms-111-cap.xml +90 -0
- mapproxy/test/system/fixture/util_grids.yaml +29 -0
- mapproxy/test/system/fixture/util_wms_capabilities111.xml +130 -0
- mapproxy/test/system/fixture/util_wms_capabilities130.xml +100 -0
- mapproxy/test/system/fixture/util_wms_capabilities_service_exception.xml +5 -0
- mapproxy/test/system/fixture/watermark.yaml +50 -0
- mapproxy/test/system/fixture/wms_srs_extent.yaml +39 -0
- mapproxy/test/system/fixture/wms_versions.yaml +38 -0
- mapproxy/test/system/fixture/wmts.yaml +134 -0
- mapproxy/test/system/fixture/wmts_dimensions.yaml +57 -0
- mapproxy/test/system/fixture/xslt_featureinfo.yaml +54 -0
- mapproxy/test/system/fixture/xslt_featureinfo_input.yaml +51 -0
- mapproxy/test/system/test_arcgis.py +156 -0
- mapproxy/test/system/test_auth.py +1133 -0
- mapproxy/test/system/test_behind_proxy.py +75 -0
- mapproxy/test/system/test_bulk_meta_tiles.py +106 -0
- mapproxy/test/system/test_cache_azureblob.py +127 -0
- mapproxy/test/system/test_cache_band_merge.py +103 -0
- mapproxy/test/system/test_cache_coverage.py +168 -0
- mapproxy/test/system/test_cache_geopackage.py +144 -0
- mapproxy/test/system/test_cache_grid_names.py +89 -0
- mapproxy/test/system/test_cache_mbtiles.py +85 -0
- mapproxy/test/system/test_cache_s3.py +115 -0
- mapproxy/test/system/test_cache_source.py +146 -0
- mapproxy/test/system/test_combined_sources.py +335 -0
- mapproxy/test/system/test_coverage.py +140 -0
- mapproxy/test/system/test_decorate_img.py +214 -0
- mapproxy/test/system/test_demo.py +56 -0
- mapproxy/test/system/test_demo_with_extra_service.py +57 -0
- mapproxy/test/system/test_dimensions.py +279 -0
- mapproxy/test/system/test_disable_storage.py +42 -0
- mapproxy/test/system/test_formats.py +219 -0
- mapproxy/test/system/test_inspire_vs.py +173 -0
- mapproxy/test/system/test_kml.py +264 -0
- mapproxy/test/system/test_layergroups.py +160 -0
- mapproxy/test/system/test_legendgraphic.py +308 -0
- mapproxy/test/system/test_mapnik.py +162 -0
- mapproxy/test/system/test_mapserver.py +83 -0
- mapproxy/test/system/test_mixed_mode_format.py +201 -0
- mapproxy/test/system/test_multi_cache_layers.py +169 -0
- mapproxy/test/system/test_multiapp.py +92 -0
- mapproxy/test/system/test_refresh.py +206 -0
- mapproxy/test/system/test_renderd_client.py +304 -0
- mapproxy/test/system/test_response_headers.py +54 -0
- mapproxy/test/system/test_scalehints.py +140 -0
- mapproxy/test/system/test_seed.py +425 -0
- mapproxy/test/system/test_seed_only.py +93 -0
- mapproxy/test/system/test_sld.py +120 -0
- mapproxy/test/system/test_source_errors.py +377 -0
- mapproxy/test/system/test_tilesource_minmax_res.py +54 -0
- mapproxy/test/system/test_tms.py +277 -0
- mapproxy/test/system/test_tms_origin.py +46 -0
- mapproxy/test/system/test_util_conf.py +434 -0
- mapproxy/test/system/test_util_export.py +210 -0
- mapproxy/test/system/test_util_grids.py +88 -0
- mapproxy/test/system/test_util_wms_capabilities.py +182 -0
- mapproxy/test/system/test_watermark.py +91 -0
- mapproxy/test/system/test_wms.py +1616 -0
- mapproxy/test/system/test_wms_srs_extent.py +165 -0
- mapproxy/test/system/test_wms_version.py +85 -0
- mapproxy/test/system/test_wmsc.py +116 -0
- mapproxy/test/system/test_wmts.py +334 -0
- mapproxy/test/system/test_wmts_dimensions.py +206 -0
- mapproxy/test/system/test_wmts_restful.py +198 -0
- mapproxy/test/system/test_xslt_featureinfo.py +423 -0
- mapproxy/test/test_http_helper.py +217 -0
- mapproxy/test/unit/__init__.py +0 -0
- mapproxy/test/unit/epsg +2 -0
- mapproxy/test/unit/polygons/polygons.dbf +0 -0
- mapproxy/test/unit/polygons/polygons.shp +0 -0
- mapproxy/test/unit/polygons/polygons.shx +0 -0
- mapproxy/test/unit/test_async.py +242 -0
- mapproxy/test/unit/test_auth.py +430 -0
- mapproxy/test/unit/test_cache.py +1356 -0
- mapproxy/test/unit/test_cache_azureblob.py +97 -0
- mapproxy/test/unit/test_cache_compact.py +324 -0
- mapproxy/test/unit/test_cache_couchdb.py +118 -0
- mapproxy/test/unit/test_cache_geopackage.py +256 -0
- mapproxy/test/unit/test_cache_redis.py +123 -0
- mapproxy/test/unit/test_cache_riak.py +80 -0
- mapproxy/test/unit/test_cache_s3.py +93 -0
- mapproxy/test/unit/test_cache_tile.py +477 -0
- mapproxy/test/unit/test_client.py +488 -0
- mapproxy/test/unit/test_client_arcgis.py +74 -0
- mapproxy/test/unit/test_client_cgi.py +140 -0
- mapproxy/test/unit/test_collections.py +116 -0
- mapproxy/test/unit/test_concat_legends.py +37 -0
- mapproxy/test/unit/test_conf_loader.py +1267 -0
- mapproxy/test/unit/test_conf_validator.py +427 -0
- mapproxy/test/unit/test_config.py +118 -0
- mapproxy/test/unit/test_decorate_img.py +185 -0
- mapproxy/test/unit/test_exceptions.py +270 -0
- mapproxy/test/unit/test_featureinfo.py +313 -0
- mapproxy/test/unit/test_file_lock_load.py +49 -0
- mapproxy/test/unit/test_geom.py +512 -0
- mapproxy/test/unit/test_grid.py +1279 -0
- mapproxy/test/unit/test_image.py +1051 -0
- mapproxy/test/unit/test_image_mask.py +181 -0
- mapproxy/test/unit/test_image_messages.py +209 -0
- mapproxy/test/unit/test_image_options.py +160 -0
- mapproxy/test/unit/test_isodate.py +118 -0
- mapproxy/test/unit/test_multiapp.py +163 -0
- mapproxy/test/unit/test_ogr_reader.py +51 -0
- mapproxy/test/unit/test_request.py +745 -0
- mapproxy/test/unit/test_request_wmts.py +178 -0
- mapproxy/test/unit/test_response.py +78 -0
- mapproxy/test/unit/test_seed.py +365 -0
- mapproxy/test/unit/test_seed_cachelock.py +91 -0
- mapproxy/test/unit/test_srs.py +215 -0
- mapproxy/test/unit/test_tiled_source.py +122 -0
- mapproxy/test/unit/test_tilefilter.py +31 -0
- mapproxy/test/unit/test_times.py +25 -0
- mapproxy/test/unit/test_timeutils.py +50 -0
- mapproxy/test/unit/test_util_conf_utils.py +75 -0
- mapproxy/test/unit/test_utils.py +476 -0
- mapproxy/test/unit/test_wms_capabilities.py +44 -0
- mapproxy/test/unit/test_wms_layer.py +113 -0
- mapproxy/test/unit/test_yaml.py +68 -0
- mapproxy/tilefilter.py +61 -0
- mapproxy/util/__init__.py +0 -0
- mapproxy/util/async_.py +229 -0
- mapproxy/util/collections.py +134 -0
- mapproxy/util/coverage.py +337 -0
- mapproxy/util/ext/__init__.py +14 -0
- mapproxy/util/ext/dictspec/__init__.py +1 -0
- mapproxy/util/ext/dictspec/spec.py +131 -0
- mapproxy/util/ext/dictspec/test/__init__.py +0 -0
- mapproxy/util/ext/dictspec/test/test_validator.py +278 -0
- mapproxy/util/ext/dictspec/validator.py +194 -0
- mapproxy/util/ext/local.py +198 -0
- mapproxy/util/ext/lockfile.py +140 -0
- mapproxy/util/ext/odict.py +321 -0
- mapproxy/util/ext/serving.py +491 -0
- mapproxy/util/ext/tempita/__init__.py +1093 -0
- mapproxy/util/ext/tempita/_looper.py +163 -0
- mapproxy/util/ext/tempita/string_utils.py +24 -0
- mapproxy/util/ext/wmsparse/__init__.py +3 -0
- mapproxy/util/ext/wmsparse/duration.py +600 -0
- mapproxy/util/ext/wmsparse/parse.py +307 -0
- mapproxy/util/ext/wmsparse/test/__init__.py +0 -0
- mapproxy/util/ext/wmsparse/test/test_parse.py +111 -0
- mapproxy/util/ext/wmsparse/test/test_util.py +23 -0
- mapproxy/util/ext/wmsparse/test/wms-example-111.xml +90 -0
- mapproxy/util/ext/wmsparse/test/wms-example-130.xml +120 -0
- mapproxy/util/ext/wmsparse/test/wms-large-111.xml +2114 -0
- mapproxy/util/ext/wmsparse/test/wms_nasa_cap.xml +386 -0
- mapproxy/util/ext/wmsparse/util.py +189 -0
- mapproxy/util/fs.py +164 -0
- mapproxy/util/geom.py +307 -0
- mapproxy/util/lib.py +117 -0
- mapproxy/util/lock.py +171 -0
- mapproxy/util/ogr.py +247 -0
- mapproxy/util/py.py +75 -0
- mapproxy/util/times.py +78 -0
- mapproxy/util/yaml.py +58 -0
- mapproxy/version.py +33 -0
- mapproxy/wsgiapp.py +167 -0
mapproxy/seed/util.py
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
# This file is part of the MapProxy project.
|
|
2
|
+
# Copyright (C) 2010, 2011 Omniscale <http://omniscale.de>
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
from __future__ import print_function, division
|
|
17
|
+
|
|
18
|
+
import os
|
|
19
|
+
import sys
|
|
20
|
+
import stat
|
|
21
|
+
import math
|
|
22
|
+
import time
|
|
23
|
+
from datetime import datetime
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
import cPickle as pickle
|
|
27
|
+
except ImportError:
|
|
28
|
+
import pickle
|
|
29
|
+
|
|
30
|
+
from mapproxy.layer import map_extent_from_grid
|
|
31
|
+
from mapproxy.util.fs import write_atomic
|
|
32
|
+
|
|
33
|
+
import logging
|
|
34
|
+
log = logging.getLogger(__name__)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class bidict(dict):
|
|
38
|
+
"""
|
|
39
|
+
Simplest bi-directional dictionary.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def __init__(self, iterator):
|
|
43
|
+
for key, val in iterator:
|
|
44
|
+
dict.__setitem__(self, key, val)
|
|
45
|
+
dict.__setitem__(self, val, key)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class ProgressStore(object):
|
|
49
|
+
"""
|
|
50
|
+
Reads and stores seed progresses to a file.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def __init__(self, filename=None, continue_seed=True):
|
|
54
|
+
self.filename = filename
|
|
55
|
+
if continue_seed:
|
|
56
|
+
self.status = self.load()
|
|
57
|
+
else:
|
|
58
|
+
self.status = {}
|
|
59
|
+
|
|
60
|
+
def load(self):
|
|
61
|
+
if not os.path.exists(self.filename):
|
|
62
|
+
pass
|
|
63
|
+
elif os.stat(self.filename).st_mode & stat.S_IWOTH:
|
|
64
|
+
log.error('progress file (%s) is world writable, ignoring file',
|
|
65
|
+
self.filename)
|
|
66
|
+
else:
|
|
67
|
+
with open(self.filename, 'rb') as f:
|
|
68
|
+
try:
|
|
69
|
+
return pickle.load(f)
|
|
70
|
+
except (pickle.UnpicklingError, AttributeError,
|
|
71
|
+
EOFError, ImportError, IndexError):
|
|
72
|
+
log.error('unable to read progress file (%s), ignoring file',
|
|
73
|
+
self.filename)
|
|
74
|
+
|
|
75
|
+
return {}
|
|
76
|
+
|
|
77
|
+
def write(self):
|
|
78
|
+
try:
|
|
79
|
+
write_atomic(self.filename, pickle.dumps(self.status))
|
|
80
|
+
except (IOError, OSError) as ex:
|
|
81
|
+
log.error('unable to write seed progress: %s', ex)
|
|
82
|
+
|
|
83
|
+
def remove(self):
|
|
84
|
+
self.status = {}
|
|
85
|
+
if os.path.exists(self.filename):
|
|
86
|
+
os.remove(self.filename)
|
|
87
|
+
|
|
88
|
+
def get(self, task_identifier):
|
|
89
|
+
return self.status.get(task_identifier, None)
|
|
90
|
+
|
|
91
|
+
def add(self, task_identifier, progress_identifier):
|
|
92
|
+
self.status[task_identifier] = progress_identifier
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class ProgressLog(object):
|
|
96
|
+
def __init__(self, out=None, silent=False, verbose=True, progress_store=None):
|
|
97
|
+
if not out:
|
|
98
|
+
out = sys.stdout
|
|
99
|
+
self.out = out
|
|
100
|
+
self._laststep = time.time()
|
|
101
|
+
self._lastprogress = 0
|
|
102
|
+
|
|
103
|
+
self.verbose = verbose
|
|
104
|
+
self.silent = silent
|
|
105
|
+
self.current_task_id = None
|
|
106
|
+
self.progress_store = progress_store
|
|
107
|
+
|
|
108
|
+
def log_message(self, msg):
|
|
109
|
+
self.out.write('[%s] %s\n' % (
|
|
110
|
+
timestamp(), msg,
|
|
111
|
+
))
|
|
112
|
+
self.out.flush()
|
|
113
|
+
|
|
114
|
+
def log_step(self, progress):
|
|
115
|
+
if not self.verbose:
|
|
116
|
+
return
|
|
117
|
+
if (self._laststep + .5) < time.time():
|
|
118
|
+
# log progress at most every 500ms
|
|
119
|
+
self.out.write('[%s] %6.2f%%\t%-20s \r' % (
|
|
120
|
+
timestamp(), progress.progress*100, progress.progress_str,
|
|
121
|
+
))
|
|
122
|
+
self.out.flush()
|
|
123
|
+
self._laststep = time.time()
|
|
124
|
+
|
|
125
|
+
def log_progress(self, progress, level, bbox, tiles):
|
|
126
|
+
progress_interval = 1
|
|
127
|
+
if not self.verbose:
|
|
128
|
+
progress_interval = 30
|
|
129
|
+
|
|
130
|
+
log_progess = False
|
|
131
|
+
if progress.progress == 1.0 or (self._lastprogress + progress_interval) < time.time():
|
|
132
|
+
self._lastprogress = time.time()
|
|
133
|
+
log_progess = True
|
|
134
|
+
|
|
135
|
+
if log_progess:
|
|
136
|
+
if self.progress_store and self.current_task_id:
|
|
137
|
+
self.progress_store.add(self.current_task_id,
|
|
138
|
+
progress.current_progress_identifier())
|
|
139
|
+
self.progress_store.write()
|
|
140
|
+
|
|
141
|
+
if self.silent:
|
|
142
|
+
return
|
|
143
|
+
|
|
144
|
+
if log_progess:
|
|
145
|
+
self.out.write('[%s] %2s %6.2f%% %s (%d tiles)\n' % (
|
|
146
|
+
timestamp(), level, progress.progress*100,
|
|
147
|
+
format_bbox(bbox), tiles))
|
|
148
|
+
self.out.flush()
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def limit_sub_bbox(bbox, sub_bbox):
|
|
152
|
+
"""
|
|
153
|
+
>>> limit_sub_bbox((0, 1, 10, 11), (-1, -1, 9, 8))
|
|
154
|
+
(0, 1, 9, 8)
|
|
155
|
+
>>> limit_sub_bbox((0, 0, 10, 10), (5, 2, 18, 18))
|
|
156
|
+
(5, 2, 10, 10)
|
|
157
|
+
"""
|
|
158
|
+
minx = max(bbox[0], sub_bbox[0])
|
|
159
|
+
miny = max(bbox[1], sub_bbox[1])
|
|
160
|
+
maxx = min(bbox[2], sub_bbox[2])
|
|
161
|
+
maxy = min(bbox[3], sub_bbox[3])
|
|
162
|
+
return minx, miny, maxx, maxy
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def timestamp():
|
|
166
|
+
return datetime.now().strftime('%H:%M:%S')
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def format_bbox(bbox):
|
|
170
|
+
return ('%.5f, %.5f, %.5f, %.5f') % tuple(bbox)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def status_symbol(i, total):
|
|
174
|
+
"""
|
|
175
|
+
>>> status_symbol(0, 1)
|
|
176
|
+
'0'
|
|
177
|
+
>>> [status_symbol(i, 4) for i in range(5)]
|
|
178
|
+
['.', 'o', 'O', '0', 'X']
|
|
179
|
+
>>> [status_symbol(i, 10) for i in range(11)]
|
|
180
|
+
['.', '.', 'o', 'o', 'o', 'O', 'O', '0', '0', '0', 'X']
|
|
181
|
+
"""
|
|
182
|
+
symbols = list(' .oO0')
|
|
183
|
+
i += 1
|
|
184
|
+
if 0 < i > total:
|
|
185
|
+
return 'X'
|
|
186
|
+
else:
|
|
187
|
+
return symbols[int(math.ceil(i/(total/4)))]
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
class BackoffError(Exception):
|
|
191
|
+
pass
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def exp_backoff(func, args=(), kw={}, max_repeat=10, start_backoff_sec=2,
|
|
195
|
+
exceptions=(Exception,), ignore_exceptions=tuple(), max_backoff=60):
|
|
196
|
+
n = 0
|
|
197
|
+
while True:
|
|
198
|
+
try:
|
|
199
|
+
result = func(*args, **kw)
|
|
200
|
+
except ignore_exceptions:
|
|
201
|
+
time.sleep(0.01)
|
|
202
|
+
except exceptions as ex:
|
|
203
|
+
if n >= max_repeat:
|
|
204
|
+
print("An error occured. Giving up", file=sys.stderr)
|
|
205
|
+
raise BackoffError
|
|
206
|
+
wait_for = start_backoff_sec * 2**n
|
|
207
|
+
if wait_for > max_backoff:
|
|
208
|
+
wait_for = max_backoff
|
|
209
|
+
print("An error occured. Retry in %d seconds: %r. Retries left: %d" %
|
|
210
|
+
(wait_for, ex, (max_repeat - n)), file=sys.stderr)
|
|
211
|
+
time.sleep(wait_for)
|
|
212
|
+
n += 1
|
|
213
|
+
else:
|
|
214
|
+
return result
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def format_seed_task(task):
|
|
218
|
+
info = []
|
|
219
|
+
info.append(' %s:' % (task.md['name'], ))
|
|
220
|
+
if task.coverage is False:
|
|
221
|
+
info.append(" Empty coverage given for this task")
|
|
222
|
+
info.append(" Skipped")
|
|
223
|
+
return '\n'.join(info)
|
|
224
|
+
|
|
225
|
+
info.append(" Seeding cache '%s' with grid '%s' in %s" % (
|
|
226
|
+
task.md['cache_name'], task.md['grid_name'], task.grid.srs.srs_code))
|
|
227
|
+
if task.coverage:
|
|
228
|
+
info.append(' Limited to coverage in: %s (EPSG:4326)' % (format_bbox(task.coverage.extent.llbbox), ))
|
|
229
|
+
else:
|
|
230
|
+
info.append(' Complete grid: %s (EPSG:4326)' % (format_bbox(map_extent_from_grid(task.grid).llbbox), ))
|
|
231
|
+
info.append(' Levels: %s' % (task.levels, ))
|
|
232
|
+
|
|
233
|
+
if task.refresh_timestamp:
|
|
234
|
+
info.append(' Overwriting: tiles older than %s' %
|
|
235
|
+
datetime.fromtimestamp(task.refresh_timestamp))
|
|
236
|
+
elif task.refresh_timestamp == 0:
|
|
237
|
+
info.append(' Overwriting: all tiles')
|
|
238
|
+
else:
|
|
239
|
+
info.append(' Overwriting: no tiles')
|
|
240
|
+
|
|
241
|
+
return '\n'.join(info)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def format_cleanup_task(task):
|
|
245
|
+
info = []
|
|
246
|
+
info.append(' %s:' % (task.md['name'], ))
|
|
247
|
+
if task.coverage is False:
|
|
248
|
+
info.append(" Empty coverage given for this task")
|
|
249
|
+
info.append(" Skipped")
|
|
250
|
+
return '\n'.join(info)
|
|
251
|
+
|
|
252
|
+
info.append(" Cleaning up cache '%s' with grid '%s' in %s" % (
|
|
253
|
+
task.md['cache_name'], task.md['grid_name'], task.grid.srs.srs_code))
|
|
254
|
+
if task.coverage:
|
|
255
|
+
info.append(' Limited to coverage in: %s (EPSG:4326)' % (format_bbox(task.coverage.extent.llbbox), ))
|
|
256
|
+
else:
|
|
257
|
+
info.append(' Complete grid: %s (EPSG:4326)' % (format_bbox(map_extent_from_grid(task.grid).llbbox), ))
|
|
258
|
+
info.append(' Levels: %s' % (task.levels, ))
|
|
259
|
+
|
|
260
|
+
if task.remove_timestamp:
|
|
261
|
+
info.append(' Remove: tiles older than %s' %
|
|
262
|
+
datetime.fromtimestamp(task.remove_timestamp))
|
|
263
|
+
else:
|
|
264
|
+
info.append(' Remove: all tiles')
|
|
265
|
+
|
|
266
|
+
return '\n'.join(info)
|
|
@@ -0,0 +1,14 @@
|
|
|
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.
|
mapproxy/service/base.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
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
|
+
Service handler (WMS, TMS, etc.).
|
|
18
|
+
"""
|
|
19
|
+
from mapproxy.exception import RequestError
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Server(object):
|
|
23
|
+
names = tuple()
|
|
24
|
+
def request_parser(x): return None
|
|
25
|
+
request_methods = ()
|
|
26
|
+
|
|
27
|
+
def handle(self, req):
|
|
28
|
+
try:
|
|
29
|
+
parsed_req = self.parse_request(req)
|
|
30
|
+
handler = getattr(self, parsed_req.request_handler_name)
|
|
31
|
+
return handler(parsed_req)
|
|
32
|
+
except RequestError as e:
|
|
33
|
+
return e.render()
|
|
34
|
+
|
|
35
|
+
def parse_request(self, req):
|
|
36
|
+
return self.request_parser(req)
|
|
37
|
+
|
|
38
|
+
def decorate_img(self, image, service, layers, environ, query_extent):
|
|
39
|
+
""" Callback that allows the ImageSource associated with a response to
|
|
40
|
+
be modified before it is returned. The callback is passed the
|
|
41
|
+
ImageSource instance and must return a valid ImageSource """
|
|
42
|
+
if 'mapproxy.decorate_img' in environ:
|
|
43
|
+
image = environ['mapproxy.decorate_img'](
|
|
44
|
+
image, service, layers, environ=environ, query_extent=query_extent)
|
|
45
|
+
return image
|
mapproxy/service/demo.py
ADDED
|
@@ -0,0 +1,364 @@
|
|
|
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
|
+
Demo service handler
|
|
18
|
+
"""
|
|
19
|
+
from __future__ import division
|
|
20
|
+
|
|
21
|
+
import importlib_resources
|
|
22
|
+
import os
|
|
23
|
+
import mimetypes
|
|
24
|
+
from collections import defaultdict
|
|
25
|
+
|
|
26
|
+
from mapproxy.config.config import base_config
|
|
27
|
+
from mapproxy.util.ext.odict import odict
|
|
28
|
+
from mapproxy.exception import RequestError
|
|
29
|
+
from mapproxy.service.base import Server
|
|
30
|
+
from mapproxy.response import Response
|
|
31
|
+
from mapproxy.srs import SRS, get_epsg_num
|
|
32
|
+
from mapproxy.layer import SRSConditional, CacheMapLayer, ResolutionConditional
|
|
33
|
+
from mapproxy.source.wms import WMSSource
|
|
34
|
+
|
|
35
|
+
from urllib import request as urllib2
|
|
36
|
+
|
|
37
|
+
from mapproxy.template import template_loader, bunch
|
|
38
|
+
env = {'bunch': bunch}
|
|
39
|
+
get_template = template_loader(__package__, 'templates', namespace=env)
|
|
40
|
+
|
|
41
|
+
# Used by plugins
|
|
42
|
+
extra_demo_server_handlers = set()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def register_extra_demo_server_handler(handler):
|
|
46
|
+
""" Method used by plugins to register a new handler for the demo service.
|
|
47
|
+
The handler passed to this method is invoked by the DemoServer.handle()
|
|
48
|
+
method when receiving an incoming request, and let a chance to the
|
|
49
|
+
handler to process it, if it is relevant to it.
|
|
50
|
+
|
|
51
|
+
:param handler: New handler for incoming requests
|
|
52
|
+
:type handler: function that takes 2 arguments (DemoServer instance and req) and
|
|
53
|
+
returns a string with HTML content or None
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
extra_demo_server_handlers.add(handler)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
extra_substitution_handlers = set()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def register_extra_demo_substitution_handler(handler):
|
|
63
|
+
""" Method used by plugins to register a new handler for doing substitutions
|
|
64
|
+
to the HTML template used by the demo service.
|
|
65
|
+
The handler passed to this method is invoked by the DemoServer._render_template()
|
|
66
|
+
method. The handler may modify the passed substitutions dictionary
|
|
67
|
+
argument. Keys of particular interest are 'extra_services_html_beginning'
|
|
68
|
+
and 'extra_services_html_end' to add HTML content before/after built-in
|
|
69
|
+
services.
|
|
70
|
+
|
|
71
|
+
:param handler: New handler for incoming requests
|
|
72
|
+
:type handler: function that takes 3 arguments(DemoServer instance, req and a substitutions dictionary
|
|
73
|
+
argument).
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
extra_substitution_handlers.add(handler)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def static_filename(name):
|
|
80
|
+
if base_config().template_dir:
|
|
81
|
+
return os.path.join(base_config().template_dir, name)
|
|
82
|
+
else:
|
|
83
|
+
return importlib_resources.files(__package__).joinpath('templates').joinpath(name)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class DemoServer(Server):
|
|
87
|
+
names = ('demo',)
|
|
88
|
+
|
|
89
|
+
def __init__(self, layers, md, request_parser=None, tile_layers=None,
|
|
90
|
+
srs=None, image_formats=None, services=None, restful_template=None, background=None):
|
|
91
|
+
Server.__init__(self)
|
|
92
|
+
self.layers = layers
|
|
93
|
+
self.tile_layers = tile_layers or {}
|
|
94
|
+
self.md = md
|
|
95
|
+
self.image_formats = image_formats
|
|
96
|
+
filter_image_format = []
|
|
97
|
+
for format in self.image_formats:
|
|
98
|
+
if 'image/jpeg' == format or 'image/png' == format:
|
|
99
|
+
filter_image_format.append(format)
|
|
100
|
+
self.image_formats = filter_image_format
|
|
101
|
+
self.srs = srs
|
|
102
|
+
self.services = services or []
|
|
103
|
+
self.restful_template = restful_template
|
|
104
|
+
self.background = background
|
|
105
|
+
|
|
106
|
+
def handle(self, req):
|
|
107
|
+
if req.path.startswith('/demo/static/'):
|
|
108
|
+
if '..' in req.path:
|
|
109
|
+
return Response('file not found', content_type='text/plain', status=404)
|
|
110
|
+
filename = req.path.lstrip('/')
|
|
111
|
+
filename = static_filename(filename)
|
|
112
|
+
if not os.path.isfile(filename):
|
|
113
|
+
return Response('file not found', content_type='text/plain', status=404)
|
|
114
|
+
type, encoding = mimetypes.guess_type(filename)
|
|
115
|
+
return Response(open(filename, 'rb'), content_type=type)
|
|
116
|
+
|
|
117
|
+
# we don't authorize the static files (css, js)
|
|
118
|
+
# since they are not confidential
|
|
119
|
+
try:
|
|
120
|
+
authorized = self.authorized_demo(req.environ)
|
|
121
|
+
except RequestError as ex:
|
|
122
|
+
return ex.render()
|
|
123
|
+
if not authorized:
|
|
124
|
+
return Response('forbidden', content_type='text/plain', status=403)
|
|
125
|
+
|
|
126
|
+
for handler in extra_demo_server_handlers:
|
|
127
|
+
demo = handler(self, req)
|
|
128
|
+
if demo is not None:
|
|
129
|
+
return Response(demo, content_type='text/html')
|
|
130
|
+
|
|
131
|
+
if 'wms_layer' in req.args:
|
|
132
|
+
demo = self._render_wms_template('demo/wms_demo.html', req)
|
|
133
|
+
elif 'tms_layer' in req.args:
|
|
134
|
+
demo = self._render_tms_template('demo/tms_demo.html', req)
|
|
135
|
+
elif 'wmts_layer' in req.args:
|
|
136
|
+
demo = self._render_wmts_template('demo/wmts_demo.html', req)
|
|
137
|
+
elif 'wms_capabilities' in req.args:
|
|
138
|
+
internal_url = '%s/service?REQUEST=GetCapabilities' % (req.server_script_url)
|
|
139
|
+
if 'type' in req.args and req.args['type'] == 'external':
|
|
140
|
+
url = internal_url.replace(req.server_script_url, req.script_url)
|
|
141
|
+
else:
|
|
142
|
+
url = internal_url
|
|
143
|
+
capabilities = urllib2.urlopen(url)
|
|
144
|
+
demo = self._render_capabilities_template('demo/capabilities_demo.html', capabilities, 'WMS', url)
|
|
145
|
+
elif 'wmsc_capabilities' in req.args:
|
|
146
|
+
internal_url = '%s/service?REQUEST=GetCapabilities&tiled=true' % (req.server_script_url)
|
|
147
|
+
if 'type' in req.args and req.args['type'] == 'external':
|
|
148
|
+
url = internal_url.replace(req.server_script_url, req.script_url)
|
|
149
|
+
else:
|
|
150
|
+
url = internal_url
|
|
151
|
+
capabilities = urllib2.urlopen(url)
|
|
152
|
+
demo = self._render_capabilities_template('demo/capabilities_demo.html', capabilities, 'WMS-C', url)
|
|
153
|
+
elif 'wmts_capabilities_kvp' in req.args:
|
|
154
|
+
internal_url = '%s/service?REQUEST=GetCapabilities&SERVICE=WMTS' % (req.server_script_url)
|
|
155
|
+
if 'type' in req.args and req.args['type'] == 'external':
|
|
156
|
+
url = internal_url.replace(req.server_script_url, req.script_url)
|
|
157
|
+
else:
|
|
158
|
+
url = internal_url
|
|
159
|
+
capabilities = urllib2.urlopen(url)
|
|
160
|
+
demo = self._render_capabilities_template('demo/capabilities_demo.html', capabilities, 'WMTS', url)
|
|
161
|
+
elif 'wmts_capabilities' in req.args:
|
|
162
|
+
internal_url = '%s/wmts/1.0.0/WMTSCapabilities.xml' % (req.server_script_url)
|
|
163
|
+
if 'type' in req.args and req.args['type'] == 'external':
|
|
164
|
+
url = internal_url.replace(req.server_script_url, req.script_url)
|
|
165
|
+
else:
|
|
166
|
+
url = internal_url
|
|
167
|
+
capabilities = urllib2.urlopen(url)
|
|
168
|
+
demo = self._render_capabilities_template('demo/capabilities_demo.html', capabilities, 'WMTS', url)
|
|
169
|
+
elif 'tms_capabilities' in req.args:
|
|
170
|
+
if 'layer' in req.args and 'srs' in req.args:
|
|
171
|
+
# prevent dir traversal (seems it's not possible with urllib2, but better safe then sorry)
|
|
172
|
+
layer = req.args['layer'].replace('..', '')
|
|
173
|
+
srs = req.args['srs'].replace('..', '')
|
|
174
|
+
internal_url = '%s/tms/1.0.0/%s/%s' % (req.server_script_url, layer, srs)
|
|
175
|
+
else:
|
|
176
|
+
internal_url = '%s/tms/1.0.0/' % (req.server_script_url)
|
|
177
|
+
if 'type' in req.args and req.args['type'] == 'external':
|
|
178
|
+
url = internal_url.replace(req.server_script_url, req.script_url)
|
|
179
|
+
else:
|
|
180
|
+
url = internal_url
|
|
181
|
+
capabilities = urllib2.urlopen(url)
|
|
182
|
+
demo = self._render_capabilities_template('demo/capabilities_demo.html', capabilities, 'TMS', url)
|
|
183
|
+
elif req.path == '/demo/':
|
|
184
|
+
demo = self._render_template(req, 'demo/demo.html')
|
|
185
|
+
else:
|
|
186
|
+
resp = Response('', status=301)
|
|
187
|
+
resp.headers['Location'] = req.script_url.rstrip('/') + '/demo/'
|
|
188
|
+
return resp
|
|
189
|
+
return Response(demo, content_type='text/html')
|
|
190
|
+
|
|
191
|
+
def layer_srs(self, layer):
|
|
192
|
+
"""
|
|
193
|
+
Return a list tuples with title and name of all SRS for the layer.
|
|
194
|
+
The title of SRS that are native to the layer are suffixed with a '*'.
|
|
195
|
+
"""
|
|
196
|
+
cached_srs = []
|
|
197
|
+
for map_layer in layer.map_layers:
|
|
198
|
+
# TODO unify map_layers interface
|
|
199
|
+
if isinstance(map_layer, SRSConditional):
|
|
200
|
+
for srs_key in map_layer.srs_map.keys():
|
|
201
|
+
cached_srs.append(srs_key.srs_code)
|
|
202
|
+
elif isinstance(map_layer, CacheMapLayer):
|
|
203
|
+
cached_srs.append(map_layer.grid.srs.srs_code)
|
|
204
|
+
elif isinstance(map_layer, ResolutionConditional):
|
|
205
|
+
cached_srs.append(map_layer.srs.srs_code)
|
|
206
|
+
elif isinstance(map_layer, WMSSource):
|
|
207
|
+
if map_layer.supported_srs:
|
|
208
|
+
for supported_srs in map_layer.supported_srs:
|
|
209
|
+
cached_srs.append(supported_srs.srs_code)
|
|
210
|
+
|
|
211
|
+
uncached_srs = []
|
|
212
|
+
|
|
213
|
+
for srs_code in self.srs:
|
|
214
|
+
if srs_code not in cached_srs:
|
|
215
|
+
uncached_srs.append(srs_code)
|
|
216
|
+
|
|
217
|
+
def get_srs_key(srs):
|
|
218
|
+
epsg_num = get_epsg_num(srs)
|
|
219
|
+
return str(epsg_num if epsg_num else srs)
|
|
220
|
+
|
|
221
|
+
sorted_cached_srs = sorted(cached_srs, key=lambda srs: get_srs_key(srs))
|
|
222
|
+
sorted_uncached_srs = sorted(uncached_srs, key=lambda srs: get_srs_key(srs))
|
|
223
|
+
sorted_cached_srs = [(s + '*', s) for s in sorted_cached_srs]
|
|
224
|
+
sorted_uncached_srs = [(s, s) for s in sorted_uncached_srs]
|
|
225
|
+
return sorted_cached_srs + sorted_uncached_srs
|
|
226
|
+
|
|
227
|
+
def _render_template(self, req, template):
|
|
228
|
+
template = get_template(template, default_inherit="demo/static.html")
|
|
229
|
+
tms_tile_layers = defaultdict(list)
|
|
230
|
+
for layer in self.tile_layers:
|
|
231
|
+
name = self.tile_layers[layer].md.get('name')
|
|
232
|
+
tms_tile_layers[name].append(self.tile_layers[layer])
|
|
233
|
+
wmts_layers = tms_tile_layers.copy()
|
|
234
|
+
|
|
235
|
+
wmts_layers = odict(sorted(wmts_layers.items(), key=lambda x: x[0]))
|
|
236
|
+
tms_tile_layers = odict(sorted(tms_tile_layers.items(), key=lambda x: x[0]))
|
|
237
|
+
|
|
238
|
+
substitutions = dict(
|
|
239
|
+
extra_services_html_beginning='',
|
|
240
|
+
extra_services_html_end='',
|
|
241
|
+
layers=self.layers,
|
|
242
|
+
formats=self.image_formats,
|
|
243
|
+
srs=self.srs,
|
|
244
|
+
layer_srs=self.layer_srs,
|
|
245
|
+
tms_layers=tms_tile_layers,
|
|
246
|
+
wmts_layers=wmts_layers,
|
|
247
|
+
services=self.services
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
for add_substitution in extra_substitution_handlers:
|
|
251
|
+
add_substitution(self, req, substitutions)
|
|
252
|
+
|
|
253
|
+
return template.substitute(substitutions)
|
|
254
|
+
|
|
255
|
+
def _render_wms_template(self, template, req):
|
|
256
|
+
template = get_template(template, default_inherit="demo/static.html")
|
|
257
|
+
layer = self.layers[req.args['wms_layer']]
|
|
258
|
+
srs = escape(req.args['srs'])
|
|
259
|
+
bbox = layer.extent.bbox_for(SRS(srs))
|
|
260
|
+
width = bbox[2] - bbox[0]
|
|
261
|
+
height = bbox[3] - bbox[1]
|
|
262
|
+
min_res = max(width/256, height/256)
|
|
263
|
+
background_url = base_config().background.url
|
|
264
|
+
if self.background:
|
|
265
|
+
background_url = self.background["url"]
|
|
266
|
+
return template.substitute(layer=layer,
|
|
267
|
+
image_formats=self.image_formats,
|
|
268
|
+
format=escape(req.args['format']),
|
|
269
|
+
srs=srs,
|
|
270
|
+
layer_srs=self.layer_srs,
|
|
271
|
+
bbox=bbox,
|
|
272
|
+
res=min_res,
|
|
273
|
+
background_url=background_url)
|
|
274
|
+
|
|
275
|
+
def _render_tms_template(self, template, req):
|
|
276
|
+
template = get_template(template, default_inherit="demo/static.html")
|
|
277
|
+
for layer in self.tile_layers.values():
|
|
278
|
+
if (layer.name == req.args['tms_layer'] and
|
|
279
|
+
layer.grid.srs.srs_code == req.args['srs']):
|
|
280
|
+
tile_layer = layer
|
|
281
|
+
break
|
|
282
|
+
|
|
283
|
+
resolutions = tile_layer.grid.tile_sets
|
|
284
|
+
res = []
|
|
285
|
+
for level, resolution in resolutions:
|
|
286
|
+
res.append(resolution)
|
|
287
|
+
|
|
288
|
+
if tile_layer.grid.srs.is_latlong:
|
|
289
|
+
units = 'degree'
|
|
290
|
+
else:
|
|
291
|
+
units = 'm'
|
|
292
|
+
|
|
293
|
+
if tile_layer.grid.profile == 'local':
|
|
294
|
+
add_res_to_options = True
|
|
295
|
+
else:
|
|
296
|
+
add_res_to_options = False
|
|
297
|
+
background_url = base_config().background.url
|
|
298
|
+
if self.background:
|
|
299
|
+
background_url = self.background["url"]
|
|
300
|
+
return template.substitute(layer=tile_layer,
|
|
301
|
+
srs=escape(req.args['srs']),
|
|
302
|
+
format=escape(req.args['format']),
|
|
303
|
+
resolutions=res,
|
|
304
|
+
units=units,
|
|
305
|
+
add_res_to_options=add_res_to_options,
|
|
306
|
+
all_tile_layers=self.tile_layers,
|
|
307
|
+
background_url=background_url)
|
|
308
|
+
|
|
309
|
+
def _render_wmts_template(self, template, req):
|
|
310
|
+
template = get_template(template, default_inherit="demo/static.html")
|
|
311
|
+
for layer in self.tile_layers.values():
|
|
312
|
+
if (layer.name == req.args['wmts_layer'] and
|
|
313
|
+
layer.grid.srs.srs_code == req.args['srs']):
|
|
314
|
+
wmts_layer = layer
|
|
315
|
+
break
|
|
316
|
+
|
|
317
|
+
restful_url = self.restful_template.replace('{Layer}', wmts_layer.name, 1)
|
|
318
|
+
if '{Format}' in restful_url:
|
|
319
|
+
restful_url = restful_url.replace('{Format}', wmts_layer.format)
|
|
320
|
+
|
|
321
|
+
if wmts_layer.grid.srs.is_latlong:
|
|
322
|
+
units = 'degree'
|
|
323
|
+
else:
|
|
324
|
+
units = 'm'
|
|
325
|
+
background_url = base_config().background.url
|
|
326
|
+
if self.background:
|
|
327
|
+
background_url = self.background["url"]
|
|
328
|
+
return template.substitute(layer=wmts_layer,
|
|
329
|
+
matrix_set=wmts_layer.grid.name,
|
|
330
|
+
format=escape(req.args['format']),
|
|
331
|
+
srs=escape(req.args['srs']),
|
|
332
|
+
resolutions=wmts_layer.grid.resolutions,
|
|
333
|
+
units=units,
|
|
334
|
+
all_tile_layers=self.tile_layers,
|
|
335
|
+
restful_url=restful_url,
|
|
336
|
+
background_url=background_url)
|
|
337
|
+
|
|
338
|
+
def _render_capabilities_template(self, template, xmlfile, service, url):
|
|
339
|
+
template = get_template(template, default_inherit="demo/static.html")
|
|
340
|
+
return template.substitute(capabilities=xmlfile,
|
|
341
|
+
service=service,
|
|
342
|
+
url=url)
|
|
343
|
+
|
|
344
|
+
def authorized_demo(self, environ):
|
|
345
|
+
if 'mapproxy.authorize' in environ:
|
|
346
|
+
result = environ['mapproxy.authorize']('demo', [], environ=environ)
|
|
347
|
+
if result['authorized'] == 'unauthenticated':
|
|
348
|
+
raise RequestError('unauthorized', status=401)
|
|
349
|
+
if result['authorized'] == 'full':
|
|
350
|
+
return True
|
|
351
|
+
return False
|
|
352
|
+
return True
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def escape(data):
|
|
356
|
+
"""
|
|
357
|
+
Escape user-provided input data for safe inclusion in HTML _and_ JS to prevent XSS.
|
|
358
|
+
"""
|
|
359
|
+
data = data.replace('&', '&')
|
|
360
|
+
data = data.replace('>', '>')
|
|
361
|
+
data = data.replace('<', '<')
|
|
362
|
+
data = data.replace("'", '')
|
|
363
|
+
data = data.replace('"', '')
|
|
364
|
+
return data
|