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
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
# This file is part of the MapProxy project.
|
|
2
|
+
# Copyright (C) 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
|
+
|
|
17
|
+
import codecs
|
|
18
|
+
import datetime
|
|
19
|
+
import json
|
|
20
|
+
import socket
|
|
21
|
+
import time
|
|
22
|
+
import hashlib
|
|
23
|
+
import base64
|
|
24
|
+
from io import BytesIO
|
|
25
|
+
|
|
26
|
+
from mapproxy.image import ImageSource
|
|
27
|
+
from mapproxy.cache.base import (
|
|
28
|
+
TileCacheBase,
|
|
29
|
+
tile_buffer, CacheBackendError,)
|
|
30
|
+
from mapproxy.source import SourceError
|
|
31
|
+
from mapproxy.srs import SRS
|
|
32
|
+
|
|
33
|
+
from threading import Lock
|
|
34
|
+
|
|
35
|
+
try:
|
|
36
|
+
import requests
|
|
37
|
+
except ImportError:
|
|
38
|
+
requests = None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
import logging
|
|
42
|
+
log = logging.getLogger(__name__)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class UnexpectedResponse(CacheBackendError):
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class CouchDBCache(TileCacheBase):
|
|
50
|
+
def __init__(self, url, db_name,
|
|
51
|
+
file_ext, tile_grid, md_template=None,
|
|
52
|
+
tile_id_template=None, coverage=None):
|
|
53
|
+
super(CouchDBCache, self).__init__(coverage)
|
|
54
|
+
|
|
55
|
+
if requests is None:
|
|
56
|
+
raise ImportError("CouchDB backend requires 'requests' package.")
|
|
57
|
+
|
|
58
|
+
md5 = hashlib.new('md5', (url + db_name).encode('utf-8'), usedforsecurity=False)
|
|
59
|
+
self.lock_cache_id = 'couchdb-' + md5.hexdigest()
|
|
60
|
+
self.file_ext = file_ext
|
|
61
|
+
self.tile_grid = tile_grid
|
|
62
|
+
self.md_template = md_template
|
|
63
|
+
self.couch_url = '%s/%s' % (url.rstrip('/'), db_name.lower())
|
|
64
|
+
self.req_session = requests.Session()
|
|
65
|
+
self.req_session.timeout = 5
|
|
66
|
+
self.db_initialised = False
|
|
67
|
+
self.app_init_db_lock = Lock()
|
|
68
|
+
self.tile_id_template = tile_id_template
|
|
69
|
+
|
|
70
|
+
def init_db(self):
|
|
71
|
+
with self.app_init_db_lock:
|
|
72
|
+
if self.db_initialised:
|
|
73
|
+
return
|
|
74
|
+
try:
|
|
75
|
+
self.req_session.put(self.couch_url)
|
|
76
|
+
self.db_initialised = True
|
|
77
|
+
except requests.exceptions.RequestException as ex:
|
|
78
|
+
log.warning('unable to initialize CouchDB: %s', ex)
|
|
79
|
+
|
|
80
|
+
def tile_url(self, coord):
|
|
81
|
+
return self.document_url(coord) + '/tile'
|
|
82
|
+
|
|
83
|
+
def document_url(self, coord, relative=False):
|
|
84
|
+
x, y, z = coord
|
|
85
|
+
grid_name = self.tile_grid.name
|
|
86
|
+
couch_url = self.couch_url
|
|
87
|
+
if relative:
|
|
88
|
+
if self.tile_id_template:
|
|
89
|
+
if self.tile_id_template.startswith('%(couch_url)s/'):
|
|
90
|
+
tile_id_template = self.tile_id_template[len('%(couch_url)s/'):]
|
|
91
|
+
else:
|
|
92
|
+
tile_id_template = self.tile_id_template
|
|
93
|
+
return tile_id_template % locals()
|
|
94
|
+
else:
|
|
95
|
+
return '%(grid_name)s-%(z)s-%(x)s-%(y)s' % locals()
|
|
96
|
+
else:
|
|
97
|
+
if self.tile_id_template:
|
|
98
|
+
return self.tile_id_template % locals()
|
|
99
|
+
else:
|
|
100
|
+
return '%(couch_url)s/%(grid_name)s-%(z)s-%(x)s-%(y)s' % locals()
|
|
101
|
+
|
|
102
|
+
def is_cached(self, tile, dimensions=None):
|
|
103
|
+
if tile.coord is None or tile.source:
|
|
104
|
+
return True
|
|
105
|
+
url = self.document_url(tile.coord)
|
|
106
|
+
try:
|
|
107
|
+
self.init_db()
|
|
108
|
+
resp = self.req_session.get(url)
|
|
109
|
+
if resp.status_code == 200:
|
|
110
|
+
doc = json.loads(codecs.decode(resp.content, 'utf-8'))
|
|
111
|
+
tile.timestamp = doc.get(self.md_template.timestamp_key)
|
|
112
|
+
return True
|
|
113
|
+
except (requests.exceptions.RequestException, socket.error) as ex:
|
|
114
|
+
# is_cached should not fail (would abort seeding for example),
|
|
115
|
+
# so we catch these errors here and just return False
|
|
116
|
+
log.warning('error while requesting %s: %s', url, ex)
|
|
117
|
+
return False
|
|
118
|
+
if resp.status_code == 404:
|
|
119
|
+
return False
|
|
120
|
+
raise SourceError('%r: %r' % (resp.status_code, resp.content))
|
|
121
|
+
|
|
122
|
+
def _tile_doc(self, tile):
|
|
123
|
+
tile_id = self.document_url(tile.coord, relative=True)
|
|
124
|
+
if self.md_template:
|
|
125
|
+
tile_doc = self.md_template.doc(tile, self.tile_grid)
|
|
126
|
+
else:
|
|
127
|
+
tile_doc = {}
|
|
128
|
+
tile_doc['_id'] = tile_id
|
|
129
|
+
|
|
130
|
+
with tile_buffer(tile) as buf:
|
|
131
|
+
data = buf.read()
|
|
132
|
+
tile_doc['_attachments'] = {
|
|
133
|
+
'tile': {
|
|
134
|
+
'content_type': 'image/' + self.file_ext,
|
|
135
|
+
'data': codecs.decode(
|
|
136
|
+
base64.b64encode(data).replace(b'\n', b''),
|
|
137
|
+
'ascii',
|
|
138
|
+
),
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return tile_id, tile_doc
|
|
142
|
+
|
|
143
|
+
def _store_bulk(self, tiles):
|
|
144
|
+
tile_docs = {}
|
|
145
|
+
for tile in tiles:
|
|
146
|
+
tile_id, tile_doc = self._tile_doc(tile)
|
|
147
|
+
tile_docs[tile_id] = tile_doc
|
|
148
|
+
|
|
149
|
+
duplicate_tiles = self._post_bulk(tile_docs)
|
|
150
|
+
|
|
151
|
+
if duplicate_tiles:
|
|
152
|
+
self._fill_rev_ids(duplicate_tiles)
|
|
153
|
+
self._post_bulk(duplicate_tiles, no_conflicts=True)
|
|
154
|
+
|
|
155
|
+
return True
|
|
156
|
+
|
|
157
|
+
def _post_bulk(self, tile_docs, no_conflicts=False):
|
|
158
|
+
"""
|
|
159
|
+
POST multiple tiles, returns all tile docs with conflicts during POST.
|
|
160
|
+
"""
|
|
161
|
+
doc = {'docs': list(tile_docs.values())}
|
|
162
|
+
data = json.dumps(doc)
|
|
163
|
+
self.init_db()
|
|
164
|
+
resp = self.req_session.post(self.couch_url + '/_bulk_docs', data=data,
|
|
165
|
+
headers={'Content-type': 'application/json'})
|
|
166
|
+
if resp.status_code != 201:
|
|
167
|
+
raise UnexpectedResponse('got unexpected resp (%d) from CouchDB: %s' % (resp.status_code, resp.content))
|
|
168
|
+
|
|
169
|
+
resp_doc = json.loads(codecs.decode(resp.content, 'utf-8'))
|
|
170
|
+
duplicate_tiles = {}
|
|
171
|
+
for tile in resp_doc:
|
|
172
|
+
if tile.get('error', 'false') == 'conflict':
|
|
173
|
+
duplicate_tiles[tile['id']] = tile_docs[tile['id']]
|
|
174
|
+
|
|
175
|
+
if no_conflicts and duplicate_tiles:
|
|
176
|
+
raise UnexpectedResponse('got unexpected resp (%d) from CouchDB: %s' % (resp.status_code, resp.content))
|
|
177
|
+
|
|
178
|
+
return duplicate_tiles
|
|
179
|
+
|
|
180
|
+
def _fill_rev_ids(self, tile_docs):
|
|
181
|
+
"""
|
|
182
|
+
Request all revs for tile_docs and insert it into the tile_docs.
|
|
183
|
+
"""
|
|
184
|
+
keys_doc = {'keys': list(tile_docs.keys())}
|
|
185
|
+
data = json.dumps(keys_doc)
|
|
186
|
+
self.init_db()
|
|
187
|
+
resp = self.req_session.post(self.couch_url + '/_all_docs', data=data,
|
|
188
|
+
headers={'Content-type': 'application/json'})
|
|
189
|
+
if resp.status_code != 200:
|
|
190
|
+
raise UnexpectedResponse('got unexpected resp (%d) from CouchDB: %s' % (resp.status_code, resp.content))
|
|
191
|
+
|
|
192
|
+
resp_doc = json.loads(codecs.decode(resp.content, 'utf-8'))
|
|
193
|
+
for tile in resp_doc['rows']:
|
|
194
|
+
tile_docs[tile['id']]['_rev'] = tile['value']['rev']
|
|
195
|
+
|
|
196
|
+
def store_tile(self, tile, dimensions=None):
|
|
197
|
+
if tile.stored:
|
|
198
|
+
return True
|
|
199
|
+
|
|
200
|
+
return self._store_bulk([tile])
|
|
201
|
+
|
|
202
|
+
def store_tiles(self, tiles, dimensions=None):
|
|
203
|
+
tiles = [t for t in tiles if not t.stored]
|
|
204
|
+
return self._store_bulk(tiles)
|
|
205
|
+
|
|
206
|
+
def load_tile_metadata(self, tile, dimensions=None):
|
|
207
|
+
if tile.timestamp:
|
|
208
|
+
return
|
|
209
|
+
|
|
210
|
+
# is_cached loads metadata
|
|
211
|
+
self.is_cached(tile, dimensions=None)
|
|
212
|
+
|
|
213
|
+
def load_tile(self, tile, with_metadata=False, dimensions=None):
|
|
214
|
+
# bulk loading with load_tiles is not implemented, because
|
|
215
|
+
# CouchDB's /all_docs? does not include attachments
|
|
216
|
+
|
|
217
|
+
if tile.source or tile.coord is None:
|
|
218
|
+
return True
|
|
219
|
+
url = self.document_url(tile.coord) + '?attachments=true'
|
|
220
|
+
self.init_db()
|
|
221
|
+
resp = self.req_session.get(url, headers={'Accept': 'application/json'})
|
|
222
|
+
if resp.status_code == 200:
|
|
223
|
+
doc = json.loads(codecs.decode(resp.content, 'utf-8'))
|
|
224
|
+
tile_data = BytesIO(base64.b64decode(doc['_attachments']['tile']['data']))
|
|
225
|
+
tile.source = ImageSource(tile_data)
|
|
226
|
+
tile.timestamp = doc.get(self.md_template.timestamp_key)
|
|
227
|
+
return True
|
|
228
|
+
return False
|
|
229
|
+
|
|
230
|
+
def remove_tile(self, tile):
|
|
231
|
+
if tile.coord is None:
|
|
232
|
+
return True
|
|
233
|
+
url = self.document_url(tile.coord)
|
|
234
|
+
resp = requests.head(url)
|
|
235
|
+
if resp.status_code == 404:
|
|
236
|
+
# already removed
|
|
237
|
+
return True
|
|
238
|
+
rev_id = resp.headers['etag']
|
|
239
|
+
url += '?rev=' + rev_id.strip('"')
|
|
240
|
+
self.init_db()
|
|
241
|
+
resp = self.req_session.delete(url)
|
|
242
|
+
if resp.status_code == 200:
|
|
243
|
+
return True
|
|
244
|
+
return False
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def utc_now_isoformat():
|
|
248
|
+
now = datetime.datetime.utcnow()
|
|
249
|
+
now = now.isoformat()
|
|
250
|
+
# remove milliseconds, add Zulu timezone
|
|
251
|
+
now = now.rsplit('.', 1)[0] + 'Z'
|
|
252
|
+
return now
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
class CouchDBMDTemplate(object):
|
|
256
|
+
def __init__(self, attributes):
|
|
257
|
+
self.attributes = attributes
|
|
258
|
+
for key, value in attributes.items():
|
|
259
|
+
if value == '{{timestamp}}':
|
|
260
|
+
self.timestamp_key = key
|
|
261
|
+
break
|
|
262
|
+
else:
|
|
263
|
+
attributes['timestamp'] = '{{timestamp}}'
|
|
264
|
+
self.timestamp_key = 'timestamp'
|
|
265
|
+
|
|
266
|
+
def doc(self, tile, grid):
|
|
267
|
+
doc = {}
|
|
268
|
+
x, y, z = tile.coord
|
|
269
|
+
for key, value in self.attributes.items():
|
|
270
|
+
if not isinstance(value, str) or not value.startswith('{{'):
|
|
271
|
+
doc[key] = value
|
|
272
|
+
continue
|
|
273
|
+
|
|
274
|
+
if value == '{{timestamp}}':
|
|
275
|
+
doc[key] = time.time()
|
|
276
|
+
elif value == '{{x}}':
|
|
277
|
+
doc[key] = x
|
|
278
|
+
elif value == '{{y}}':
|
|
279
|
+
doc[key] = y
|
|
280
|
+
elif value in ('{{z}}', '{{level}}'):
|
|
281
|
+
doc[key] = z
|
|
282
|
+
elif value == '{{utc_iso}}':
|
|
283
|
+
doc[key] = utc_now_isoformat()
|
|
284
|
+
elif value == '{{wgs_tile_centroid}}':
|
|
285
|
+
tile_bbox = grid.tile_bbox(tile.coord)
|
|
286
|
+
centroid = (
|
|
287
|
+
tile_bbox[0] + (tile_bbox[2]-tile_bbox[0])/2,
|
|
288
|
+
tile_bbox[1] + (tile_bbox[3]-tile_bbox[1])/2
|
|
289
|
+
)
|
|
290
|
+
centroid = grid.srs.transform_to(SRS(4326), centroid)
|
|
291
|
+
doc[key] = centroid
|
|
292
|
+
elif value == '{{tile_centroid}}':
|
|
293
|
+
tile_bbox = grid.tile_bbox(tile.coord)
|
|
294
|
+
centroid = (
|
|
295
|
+
tile_bbox[0] + (tile_bbox[2]-tile_bbox[0])/2,
|
|
296
|
+
tile_bbox[1] + (tile_bbox[3]-tile_bbox[1])/2
|
|
297
|
+
)
|
|
298
|
+
doc[key] = centroid
|
|
299
|
+
else:
|
|
300
|
+
raise ValueError('unknown CouchDB tile_metadata value: %r' % (value, ))
|
|
301
|
+
return doc
|
mapproxy/cache/dummy.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# This file is part of the MapProxy project.
|
|
2
|
+
# Copyright (C) 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 mapproxy.cache.base import TileCacheBase
|
|
17
|
+
from mapproxy.util.lock import DummyLock
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class DummyCache(TileCacheBase):
|
|
21
|
+
def is_cached(self, tile, dimensions=None):
|
|
22
|
+
return False
|
|
23
|
+
|
|
24
|
+
def lock(self, tile):
|
|
25
|
+
return DummyLock()
|
|
26
|
+
|
|
27
|
+
def load_tile(self, tile, with_metadata=False, dimensions=None):
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
def store_tile(self, tile, dimensions=None):
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class DummyLocker(object):
|
|
35
|
+
def lock(self, tile):
|
|
36
|
+
return DummyLock()
|
mapproxy/cache/file.py
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
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
|
+
import os
|
|
17
|
+
import errno
|
|
18
|
+
import hashlib
|
|
19
|
+
|
|
20
|
+
from mapproxy.util.fs import ensure_directory, write_atomic
|
|
21
|
+
from mapproxy.image import ImageSource, is_single_color_image
|
|
22
|
+
from mapproxy.cache import path
|
|
23
|
+
from mapproxy.cache.base import TileCacheBase, tile_buffer
|
|
24
|
+
|
|
25
|
+
import logging
|
|
26
|
+
log = logging.getLogger('mapproxy.cache.file')
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class FileCache(TileCacheBase):
|
|
30
|
+
"""
|
|
31
|
+
This class is responsible to store and load the actual tile data.
|
|
32
|
+
"""
|
|
33
|
+
supports_dimensions = True
|
|
34
|
+
|
|
35
|
+
def __init__(self, cache_dir, file_ext, directory_layout='tc',
|
|
36
|
+
link_single_color_images=False, coverage=None, image_opts=None):
|
|
37
|
+
"""
|
|
38
|
+
:param cache_dir: the path where the tile will be stored
|
|
39
|
+
:param file_ext: the file extension that will be appended to
|
|
40
|
+
each tile (e.g. 'png')
|
|
41
|
+
"""
|
|
42
|
+
super(FileCache, self).__init__(coverage)
|
|
43
|
+
md5 = hashlib.new('md5', cache_dir.encode('utf-8'), usedforsecurity=False)
|
|
44
|
+
self.lock_cache_id = md5.hexdigest()
|
|
45
|
+
self.cache_dir = cache_dir
|
|
46
|
+
self.file_ext = file_ext
|
|
47
|
+
self.image_opts = image_opts
|
|
48
|
+
self.link_single_color_images = link_single_color_images
|
|
49
|
+
self._tile_location, self._level_location = path.location_funcs(layout=directory_layout)
|
|
50
|
+
if self._level_location is None:
|
|
51
|
+
self.level_location = None # disable level based clean-ups
|
|
52
|
+
|
|
53
|
+
def tile_location(self, tile, create_dir=False, dimensions=None):
|
|
54
|
+
if dimensions is not None and len(dimensions) > 0:
|
|
55
|
+
items = list(dimensions.keys())
|
|
56
|
+
items.sort()
|
|
57
|
+
dimensions_str = ['{key}-{value}'.format(key=i, value=dimensions[i].replace('/', '_')) for i in items]
|
|
58
|
+
# todo: cache_dir is not used. should it get returned or removed?
|
|
59
|
+
cache_dir = os.path.join(self.cache_dir, '_'.join(dimensions_str)) # noqa
|
|
60
|
+
return self._tile_location(tile, self.cache_dir, self.file_ext, create_dir=create_dir, dimensions=dimensions)
|
|
61
|
+
|
|
62
|
+
def level_location(self, level, dimensions=None):
|
|
63
|
+
"""
|
|
64
|
+
Return the path where all tiles for `level` will be stored.
|
|
65
|
+
|
|
66
|
+
>>> c = FileCache(cache_dir='/tmp/cache/', file_ext='png')
|
|
67
|
+
>>> c.level_location(2)
|
|
68
|
+
'/tmp/cache/02'
|
|
69
|
+
"""
|
|
70
|
+
return self._level_location(level, self.cache_dir, dimensions)
|
|
71
|
+
|
|
72
|
+
def _single_color_tile_location(self, color, create_dir=False):
|
|
73
|
+
"""
|
|
74
|
+
>>> c = FileCache(cache_dir='/tmp/cache/', file_ext='png')
|
|
75
|
+
>>> c._single_color_tile_location((254, 0, 4)).replace('\\\\', '/')
|
|
76
|
+
'/tmp/cache/single_color_tiles/fe0004.png'
|
|
77
|
+
"""
|
|
78
|
+
parts = (
|
|
79
|
+
self.cache_dir,
|
|
80
|
+
'single_color_tiles',
|
|
81
|
+
''.join('%02x' % v for v in color) + '.' + self.file_ext
|
|
82
|
+
)
|
|
83
|
+
location = os.path.join(*parts)
|
|
84
|
+
if create_dir:
|
|
85
|
+
ensure_directory(location)
|
|
86
|
+
return location
|
|
87
|
+
|
|
88
|
+
def load_tile_metadata(self, tile, dimensions=None):
|
|
89
|
+
location = self.tile_location(tile, dimensions=dimensions)
|
|
90
|
+
try:
|
|
91
|
+
stats = os.lstat(location)
|
|
92
|
+
tile.timestamp = stats.st_mtime
|
|
93
|
+
tile.size = stats.st_size
|
|
94
|
+
except OSError as ex:
|
|
95
|
+
if ex.errno != errno.ENOENT:
|
|
96
|
+
raise
|
|
97
|
+
tile.timestamp = 0
|
|
98
|
+
tile.size = 0
|
|
99
|
+
|
|
100
|
+
def is_cached(self, tile, dimensions=None):
|
|
101
|
+
"""
|
|
102
|
+
Returns ``True`` if the tile data is present.
|
|
103
|
+
"""
|
|
104
|
+
if tile.is_missing():
|
|
105
|
+
location = self.tile_location(tile, dimensions=dimensions)
|
|
106
|
+
if os.path.exists(location):
|
|
107
|
+
return True
|
|
108
|
+
else:
|
|
109
|
+
return False
|
|
110
|
+
else:
|
|
111
|
+
return True
|
|
112
|
+
|
|
113
|
+
def load_tile(self, tile, with_metadata=False, dimensions=None):
|
|
114
|
+
"""
|
|
115
|
+
Fills the `Tile.source` of the `tile` if it is cached.
|
|
116
|
+
If it is not cached or if the ``.coord`` is ``None``, nothing happens.
|
|
117
|
+
"""
|
|
118
|
+
if not tile.is_missing():
|
|
119
|
+
return True
|
|
120
|
+
|
|
121
|
+
location = self.tile_location(tile, dimensions=dimensions)
|
|
122
|
+
|
|
123
|
+
if os.path.exists(location):
|
|
124
|
+
if with_metadata:
|
|
125
|
+
self.load_tile_metadata(tile, dimensions=dimensions)
|
|
126
|
+
tile.source = ImageSource(location, image_opts=self.image_opts)
|
|
127
|
+
return True
|
|
128
|
+
return False
|
|
129
|
+
|
|
130
|
+
def remove_tile(self, tile, dimensions=None):
|
|
131
|
+
location = self.tile_location(tile, dimensions=dimensions)
|
|
132
|
+
try:
|
|
133
|
+
os.remove(location)
|
|
134
|
+
except OSError as ex:
|
|
135
|
+
if ex.errno != errno.ENOENT:
|
|
136
|
+
raise
|
|
137
|
+
|
|
138
|
+
def store_tile(self, tile, dimensions=None):
|
|
139
|
+
"""
|
|
140
|
+
Add the given `tile` to the file cache. Stores the `Tile.source` to
|
|
141
|
+
`FileCache.tile_location`.
|
|
142
|
+
"""
|
|
143
|
+
if tile.stored:
|
|
144
|
+
return
|
|
145
|
+
|
|
146
|
+
tile_loc = self.tile_location(tile, create_dir=True, dimensions=dimensions)
|
|
147
|
+
|
|
148
|
+
if self.link_single_color_images:
|
|
149
|
+
color = is_single_color_image(tile.source.as_image())
|
|
150
|
+
if color:
|
|
151
|
+
self._store_single_color_tile(tile, tile_loc, color)
|
|
152
|
+
else:
|
|
153
|
+
self._store(tile, tile_loc)
|
|
154
|
+
else:
|
|
155
|
+
self._store(tile, tile_loc)
|
|
156
|
+
|
|
157
|
+
def _store(self, tile, location):
|
|
158
|
+
if os.path.islink(location):
|
|
159
|
+
os.unlink(location)
|
|
160
|
+
|
|
161
|
+
with tile_buffer(tile) as buf:
|
|
162
|
+
log.debug('writing %r to %s' % (tile.coord, location))
|
|
163
|
+
write_atomic(location, buf.read())
|
|
164
|
+
|
|
165
|
+
def _store_single_color_tile(self, tile, tile_loc, color):
|
|
166
|
+
real_tile_loc = self._single_color_tile_location(color, create_dir=True)
|
|
167
|
+
if not os.path.exists(real_tile_loc):
|
|
168
|
+
self._store(tile, real_tile_loc)
|
|
169
|
+
|
|
170
|
+
log.debug('linking %r from %s to %s',
|
|
171
|
+
tile.coord, real_tile_loc, tile_loc)
|
|
172
|
+
|
|
173
|
+
# remove any file before symlinking.
|
|
174
|
+
# exists() returns False if it links to non-
|
|
175
|
+
# existing file, islink() test to check that
|
|
176
|
+
if os.path.exists(tile_loc) or os.path.islink(tile_loc):
|
|
177
|
+
os.unlink(tile_loc)
|
|
178
|
+
|
|
179
|
+
if self.link_single_color_images == 'hardlink':
|
|
180
|
+
try:
|
|
181
|
+
os.link(real_tile_loc, tile_loc)
|
|
182
|
+
except OSError as e:
|
|
183
|
+
# ignore error if link was created by other process
|
|
184
|
+
if e.errno != errno.EEXIST:
|
|
185
|
+
raise e
|
|
186
|
+
else:
|
|
187
|
+
# Use relative path for the symlink
|
|
188
|
+
real_tile_loc = os.path.relpath(real_tile_loc, os.path.dirname(tile_loc))
|
|
189
|
+
|
|
190
|
+
try:
|
|
191
|
+
os.symlink(real_tile_loc, tile_loc)
|
|
192
|
+
except OSError as e:
|
|
193
|
+
# ignore error if link was created by other process
|
|
194
|
+
if e.errno != errno.EEXIST:
|
|
195
|
+
raise e
|
|
196
|
+
|
|
197
|
+
return
|
|
198
|
+
|
|
199
|
+
def __repr__(self):
|
|
200
|
+
return '%s(%r, %r)' % (self.__class__.__name__, self.cache_dir, self.file_ext)
|