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,1356 @@
|
|
|
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
|
+
import base64
|
|
18
|
+
import os
|
|
19
|
+
import re
|
|
20
|
+
import shutil
|
|
21
|
+
import tempfile
|
|
22
|
+
import threading
|
|
23
|
+
import time
|
|
24
|
+
|
|
25
|
+
from io import BytesIO
|
|
26
|
+
from collections import defaultdict
|
|
27
|
+
|
|
28
|
+
import pytest
|
|
29
|
+
|
|
30
|
+
from mapproxy.cache.base import TileLocker
|
|
31
|
+
from mapproxy.cache.file import FileCache
|
|
32
|
+
from mapproxy.cache.tile import Tile, TileManager
|
|
33
|
+
from mapproxy.client.http import HTTPClient
|
|
34
|
+
from mapproxy.client.wms import WMSClient
|
|
35
|
+
from mapproxy.compat.image import Image
|
|
36
|
+
from mapproxy.config.coverage import load_coverage
|
|
37
|
+
from mapproxy.grid import TileGrid, resolution_range
|
|
38
|
+
from mapproxy.image import ImageSource, BlankImageSource
|
|
39
|
+
from mapproxy.image.opts import ImageOptions
|
|
40
|
+
from mapproxy.layer import (
|
|
41
|
+
BlankImage,
|
|
42
|
+
CacheMapLayer,
|
|
43
|
+
DirectMapLayer,
|
|
44
|
+
MapBBOXError,
|
|
45
|
+
MapExtent,
|
|
46
|
+
MapLayer,
|
|
47
|
+
MapQuery,
|
|
48
|
+
ResolutionConditional,
|
|
49
|
+
SRSConditional,
|
|
50
|
+
)
|
|
51
|
+
from mapproxy.request.wms import WMS111MapRequest
|
|
52
|
+
from mapproxy.source import InvalidSourceQuery, SourceError
|
|
53
|
+
from mapproxy.source.tile import TiledSource
|
|
54
|
+
from mapproxy.source.wms import WMSSource
|
|
55
|
+
from mapproxy.source.error import HTTPSourceErrorHandler
|
|
56
|
+
from mapproxy.srs import SRS, SupportedSRS, PreferredSrcSRS
|
|
57
|
+
from mapproxy.test.helper import TempFile
|
|
58
|
+
from mapproxy.test.http import assert_query_eq, wms_query_eq, query_eq, mock_httpd
|
|
59
|
+
from mapproxy.test.image import create_debug_img, is_png, tmp_image, create_tmp_image_buf
|
|
60
|
+
from mapproxy.util.coverage import BBOXCoverage, coverage
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
TEST_SERVER_ADDRESS = ('127.0.0.1', 56413)
|
|
64
|
+
GLOBAL_GEOGRAPHIC_EXTENT = MapExtent((-180, -90, 180, 90), SRS(4326))
|
|
65
|
+
|
|
66
|
+
tmp_lock_dir = None
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def setup():
|
|
70
|
+
global tmp_lock_dir
|
|
71
|
+
tmp_lock_dir = tempfile.mkdtemp()
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def teardown():
|
|
75
|
+
shutil.rmtree(tmp_lock_dir)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class counting_set(object):
|
|
79
|
+
def __init__(self, items):
|
|
80
|
+
self.data = defaultdict(int)
|
|
81
|
+
for item in items:
|
|
82
|
+
self.data[item] += 1
|
|
83
|
+
|
|
84
|
+
def add(self, item):
|
|
85
|
+
self.data[item] += 1
|
|
86
|
+
|
|
87
|
+
def __repr__(self):
|
|
88
|
+
return 'counting_set(%r)' % dict(self.data)
|
|
89
|
+
|
|
90
|
+
def __eq__(self, other):
|
|
91
|
+
return self.data == other.data
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class MockTileClient(object):
|
|
95
|
+
def __init__(self):
|
|
96
|
+
self.requested_tiles = []
|
|
97
|
+
|
|
98
|
+
def get_tile(self, tile_coord, format=None):
|
|
99
|
+
self.requested_tiles.append(tile_coord)
|
|
100
|
+
return ImageSource(create_debug_img((256, 256)))
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class TestTiledSourceGlobalGeodetic(object):
|
|
104
|
+
def setup_method(self):
|
|
105
|
+
self.grid = TileGrid(SRS(4326), bbox=[-180, -90, 180, 90])
|
|
106
|
+
self.client = MockTileClient()
|
|
107
|
+
self.source = TiledSource(self.grid, self.client)
|
|
108
|
+
|
|
109
|
+
def test_match(self):
|
|
110
|
+
self.source.get_map(MapQuery([-180, -90, 0, 90], (256, 256), SRS(4326)))
|
|
111
|
+
self.source.get_map(MapQuery([0, -90, 180, 90], (256, 256), SRS(4326)))
|
|
112
|
+
assert self.client.requested_tiles == [(0, 0, 1), (1, 0, 1)]
|
|
113
|
+
|
|
114
|
+
def test_wrong_size(self):
|
|
115
|
+
with pytest.raises(InvalidSourceQuery):
|
|
116
|
+
self.source.get_map(MapQuery([-180, -90, 0, 90], (512, 256), SRS(4326)))
|
|
117
|
+
|
|
118
|
+
def test_wrong_srs(self):
|
|
119
|
+
with pytest.raises(InvalidSourceQuery):
|
|
120
|
+
self.source.get_map(MapQuery([-180, -90, 0, 90], (512, 256), SRS(4326)))
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class RecordFileCache(FileCache):
|
|
124
|
+
def __init__(self, *args, **kw):
|
|
125
|
+
super(RecordFileCache, self).__init__(*args, **kw)
|
|
126
|
+
self.stored_tiles = set()
|
|
127
|
+
self.loaded_tiles = counting_set([])
|
|
128
|
+
self.is_cached_call_count = 0
|
|
129
|
+
|
|
130
|
+
def store_tile(self, tile, dimensions=None):
|
|
131
|
+
assert tile.coord not in self.stored_tiles
|
|
132
|
+
self.stored_tiles.add(tile.coord)
|
|
133
|
+
if self.cache_dir != '/dev/null':
|
|
134
|
+
FileCache.store_tile(self, tile, dimensions=dimensions)
|
|
135
|
+
|
|
136
|
+
def load_tile(self, tile, with_metadata=False, dimensions=None):
|
|
137
|
+
if tile.source:
|
|
138
|
+
# Do not record tiles with source as "loaded" as FileCache will
|
|
139
|
+
# return tile without checking/loading from filesystem.
|
|
140
|
+
return True
|
|
141
|
+
self.loaded_tiles.add(tile.coord)
|
|
142
|
+
return FileCache.load_tile(self, tile, with_metadata, dimensions=dimensions)
|
|
143
|
+
|
|
144
|
+
def is_cached(self, tile, dimensions=None):
|
|
145
|
+
self.is_cached_call_count += 1
|
|
146
|
+
return tile.coord in self.stored_tiles
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def create_cached_tile(tile, cache, timestamp=None):
|
|
150
|
+
loc = cache.tile_location(tile, create_dir=True)
|
|
151
|
+
with open(loc, 'wb') as f:
|
|
152
|
+
f.write(b'foo')
|
|
153
|
+
|
|
154
|
+
if timestamp:
|
|
155
|
+
os.utime(loc, (timestamp, timestamp))
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@pytest.fixture
|
|
159
|
+
def file_cache(tmpdir):
|
|
160
|
+
return FileCache(cache_dir=tmpdir.join('cache').strpath, file_ext='png')
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
@pytest.fixture
|
|
164
|
+
def tile_locker(tmpdir):
|
|
165
|
+
return TileLocker(tmpdir.join('lock').strpath, 10, "id")
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@pytest.fixture
|
|
169
|
+
def mock_tile_client():
|
|
170
|
+
return MockTileClient()
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
@pytest.fixture
|
|
174
|
+
def mock_file_cache():
|
|
175
|
+
return RecordFileCache('/dev/null', 'png')
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class TestTileManagerStaleTiles(object):
|
|
179
|
+
|
|
180
|
+
@pytest.fixture
|
|
181
|
+
def tile_mgr(self, file_cache, tile_locker):
|
|
182
|
+
grid = TileGrid(SRS(4326), bbox=[-180, -90, 180, 90])
|
|
183
|
+
client = MockTileClient()
|
|
184
|
+
source = TiledSource(grid, client)
|
|
185
|
+
tile_mgr = TileManager(grid, file_cache, [source], 'png', locker=tile_locker)
|
|
186
|
+
return tile_mgr
|
|
187
|
+
|
|
188
|
+
def test_is_stale_missing(self, tile_mgr):
|
|
189
|
+
assert not tile_mgr.is_stale(Tile((0, 0, 1)))
|
|
190
|
+
|
|
191
|
+
def test_is_stale_not_expired(self, tile_mgr, file_cache):
|
|
192
|
+
create_cached_tile(Tile((0, 0, 1)), file_cache)
|
|
193
|
+
assert not tile_mgr.is_stale(Tile((0, 0, 1)))
|
|
194
|
+
|
|
195
|
+
def test_is_stale_expired(self, tile_mgr, file_cache):
|
|
196
|
+
create_cached_tile(Tile((0, 0, 1)), file_cache, timestamp=time.time()-3600)
|
|
197
|
+
tile_mgr._expire_timestamp = time.time()
|
|
198
|
+
assert tile_mgr.is_stale(Tile((0, 0, 1)))
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
class TestTileManagerRemoveTiles(object):
|
|
202
|
+
@pytest.fixture
|
|
203
|
+
def tile_mgr(self, file_cache, tile_locker):
|
|
204
|
+
grid = TileGrid(SRS(4326), bbox=[-180, -90, 180, 90])
|
|
205
|
+
client = MockTileClient()
|
|
206
|
+
source = TiledSource(grid, client)
|
|
207
|
+
image_opts = ImageOptions(format='image/png')
|
|
208
|
+
return TileManager(grid, file_cache, [source], 'png',
|
|
209
|
+
image_opts=image_opts,
|
|
210
|
+
locker=tile_locker)
|
|
211
|
+
|
|
212
|
+
def test_remove_missing(self, tile_mgr):
|
|
213
|
+
tile_mgr.remove_tile_coords([(0, 0, 0), (0, 0, 1)])
|
|
214
|
+
|
|
215
|
+
def test_remove_existing(self, tile_mgr, file_cache):
|
|
216
|
+
create_cached_tile(Tile((0, 0, 1)), file_cache)
|
|
217
|
+
assert tile_mgr.is_cached(Tile((0, 0, 1)))
|
|
218
|
+
tile_mgr.remove_tile_coords([(0, 0, 0), (0, 0, 1)])
|
|
219
|
+
assert not tile_mgr.is_cached(Tile((0, 0, 1)))
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class TestTileManagerTiledSource(object):
|
|
223
|
+
@pytest.fixture
|
|
224
|
+
def tile_mgr(self, tile_locker, mock_file_cache, mock_tile_client):
|
|
225
|
+
grid = TileGrid(SRS(4326), bbox=[-180, -90, 180, 90])
|
|
226
|
+
source = TiledSource(grid, mock_tile_client)
|
|
227
|
+
image_opts = ImageOptions(format='image/png')
|
|
228
|
+
return TileManager(grid, mock_file_cache, [source], 'png',
|
|
229
|
+
image_opts=image_opts,
|
|
230
|
+
locker=tile_locker,
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
def test_create_tiles(self, tile_mgr, mock_file_cache, mock_tile_client):
|
|
234
|
+
tile_mgr.creator().create_tiles([Tile((0, 0, 1)), Tile((1, 0, 1))])
|
|
235
|
+
assert mock_file_cache.stored_tiles == set([(0, 0, 1), (1, 0, 1)])
|
|
236
|
+
assert sorted(mock_tile_client.requested_tiles) == [(0, 0, 1), (1, 0, 1)]
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
class TestTileManagerDifferentSourceGrid(object):
|
|
240
|
+
@pytest.fixture
|
|
241
|
+
def tile_mgr(self, mock_file_cache, mock_tile_client, tile_locker):
|
|
242
|
+
grid = TileGrid(SRS(4326), bbox=[-180, -90, 180, 90])
|
|
243
|
+
source_grid = TileGrid(SRS(4326), bbox=[0, -90, 180, 90])
|
|
244
|
+
source = TiledSource(source_grid, mock_tile_client)
|
|
245
|
+
image_opts = ImageOptions(format='image/png')
|
|
246
|
+
return TileManager(grid, mock_file_cache, [source], 'png',
|
|
247
|
+
image_opts=image_opts,
|
|
248
|
+
locker=tile_locker,
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
def test_create_tiles(self, tile_mgr, mock_file_cache, mock_tile_client):
|
|
252
|
+
tile_mgr.creator().create_tiles([Tile((1, 0, 1))])
|
|
253
|
+
assert mock_file_cache.stored_tiles == set([(1, 0, 1)])
|
|
254
|
+
assert mock_tile_client.requested_tiles == [(0, 0, 0)]
|
|
255
|
+
|
|
256
|
+
def test_create_tiles_out_of_bounds(self, tile_mgr):
|
|
257
|
+
with pytest.raises(InvalidSourceQuery):
|
|
258
|
+
tile_mgr.creator().create_tiles([Tile((0, 0, 0))])
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
class MockSource(MapLayer):
|
|
262
|
+
def __init__(self, *args):
|
|
263
|
+
MapLayer.__init__(self, *args)
|
|
264
|
+
self.requested = []
|
|
265
|
+
|
|
266
|
+
def _image(self, size):
|
|
267
|
+
return create_debug_img(size)
|
|
268
|
+
|
|
269
|
+
def get_map(self, query):
|
|
270
|
+
self.requested.append((query.bbox, query.size, query.srs))
|
|
271
|
+
return ImageSource(self._image(query.size))
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
@pytest.fixture
|
|
275
|
+
def mock_source():
|
|
276
|
+
return MockSource()
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
class TestTileManagerSource(object):
|
|
280
|
+
|
|
281
|
+
@pytest.fixture
|
|
282
|
+
def tile_mgr(self, mock_file_cache, mock_source, tile_locker):
|
|
283
|
+
grid = TileGrid(SRS(4326), bbox=[-180, -90, 180, 90])
|
|
284
|
+
image_opts = ImageOptions(format='image/png')
|
|
285
|
+
return TileManager(grid, mock_file_cache, [mock_source], 'png',
|
|
286
|
+
image_opts=image_opts,
|
|
287
|
+
locker=tile_locker,
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
def test_create_tile(self, tile_mgr, mock_file_cache, mock_source):
|
|
291
|
+
tile_mgr.creator().create_tiles([Tile((0, 0, 1)), Tile((1, 0, 1))])
|
|
292
|
+
assert mock_file_cache.stored_tiles == set([(0, 0, 1), (1, 0, 1)])
|
|
293
|
+
assert sorted(mock_source.requested) == \
|
|
294
|
+
[((-180.0, -90.0, 0.0, 90.0), (256, 256), SRS(4326)),
|
|
295
|
+
((0.0, -90.0, 180.0, 90.0), (256, 256), SRS(4326))]
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
class MockWMSClient(object):
|
|
299
|
+
def __init__(self):
|
|
300
|
+
self.requested = []
|
|
301
|
+
|
|
302
|
+
def retrieve(self, query, format):
|
|
303
|
+
self.requested.append((query.bbox, query.size, query.srs))
|
|
304
|
+
return create_debug_img(query.size)
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
@pytest.fixture
|
|
308
|
+
def mock_wms_client():
|
|
309
|
+
return MockWMSClient()
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
class TestTileManagerWMSSource(object):
|
|
313
|
+
@pytest.fixture
|
|
314
|
+
def tile_mgr(self, mock_file_cache, tile_locker, mock_wms_client):
|
|
315
|
+
grid = TileGrid(SRS(4326), bbox=[-180, -90, 180, 90])
|
|
316
|
+
source = WMSSource(mock_wms_client)
|
|
317
|
+
image_opts = ImageOptions(format='image/png')
|
|
318
|
+
return TileManager(grid, mock_file_cache, [source], 'png',
|
|
319
|
+
meta_size=[2, 2], meta_buffer=0, image_opts=image_opts,
|
|
320
|
+
locker=tile_locker,
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
def test_same_lock_for_meta_tile(self, tile_mgr):
|
|
324
|
+
assert tile_mgr.lock(Tile((0, 0, 1))).lock_file == \
|
|
325
|
+
tile_mgr.lock(Tile((1, 0, 1))).lock_file
|
|
326
|
+
|
|
327
|
+
def test_locks_for_meta_tiles(self, tile_mgr):
|
|
328
|
+
assert tile_mgr.lock(Tile((0, 0, 2))).lock_file != \
|
|
329
|
+
tile_mgr.lock(Tile((2, 0, 2))).lock_file
|
|
330
|
+
|
|
331
|
+
def test_create_tile_first_level(self, tile_mgr, mock_file_cache, mock_wms_client):
|
|
332
|
+
tile_mgr.creator().create_tiles([Tile((0, 0, 1)), Tile((1, 0, 1))])
|
|
333
|
+
assert mock_file_cache.stored_tiles == set([(0, 0, 1), (1, 0, 1)])
|
|
334
|
+
assert mock_wms_client.requested == \
|
|
335
|
+
[((-180.0, -90.0, 180.0, 90.0), (512, 256), SRS(4326))]
|
|
336
|
+
|
|
337
|
+
def test_create_tile(self, tile_mgr, mock_file_cache, mock_wms_client):
|
|
338
|
+
tile_mgr.creator().create_tiles([Tile((0, 0, 2))])
|
|
339
|
+
assert mock_file_cache.stored_tiles == \
|
|
340
|
+
set([(0, 0, 2), (1, 0, 2), (0, 1, 2), (1, 1, 2)])
|
|
341
|
+
assert sorted(mock_wms_client.requested) == \
|
|
342
|
+
[((-180.0, -90.0, 0.0, 90.0), (512, 512), SRS(4326))]
|
|
343
|
+
|
|
344
|
+
def test_create_tiles(self, tile_mgr, mock_file_cache, mock_wms_client):
|
|
345
|
+
tile_mgr.creator().create_tiles([Tile((0, 0, 2)), Tile((2, 0, 2))])
|
|
346
|
+
assert mock_file_cache.stored_tiles == \
|
|
347
|
+
set([(0, 0, 2), (1, 0, 2), (0, 1, 2), (1, 1, 2),
|
|
348
|
+
(2, 0, 2), (3, 0, 2), (2, 1, 2), (3, 1, 2)])
|
|
349
|
+
assert sorted(mock_wms_client.requested) == \
|
|
350
|
+
[((-180.0, -90.0, 0.0, 90.0), (512, 512), SRS(4326)),
|
|
351
|
+
((0.0, -90.0, 180.0, 90.0), (512, 512), SRS(4326))]
|
|
352
|
+
|
|
353
|
+
def test_load_tile_coords(self, tile_mgr, mock_file_cache, mock_wms_client):
|
|
354
|
+
tiles = tile_mgr.load_tile_coords(((0, 0, 2), (2, 0, 2)))
|
|
355
|
+
assert tiles[0].coord == (0, 0, 2)
|
|
356
|
+
assert isinstance(tiles[0].source, ImageSource)
|
|
357
|
+
assert tiles[1].coord == (2, 0, 2)
|
|
358
|
+
assert isinstance(tiles[1].source, ImageSource)
|
|
359
|
+
|
|
360
|
+
assert mock_file_cache.stored_tiles == \
|
|
361
|
+
set([(0, 0, 2), (1, 0, 2), (0, 1, 2), (1, 1, 2),
|
|
362
|
+
(2, 0, 2), (3, 0, 2), (2, 1, 2), (3, 1, 2)])
|
|
363
|
+
assert sorted(mock_wms_client.requested) == \
|
|
364
|
+
[((-180.0, -90.0, 0.0, 90.0), (512, 512), SRS(4326)),
|
|
365
|
+
((0.0, -90.0, 180.0, 90.0), (512, 512), SRS(4326))]
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
class TestTileManagerWMSSourceConcurrent(TestTileManagerWMSSource):
|
|
369
|
+
@pytest.fixture
|
|
370
|
+
def tile_mgr(self, mock_file_cache, tile_locker, mock_wms_client):
|
|
371
|
+
grid = TileGrid(SRS(4326), bbox=[-180, -90, 180, 90])
|
|
372
|
+
source = WMSSource(mock_wms_client)
|
|
373
|
+
image_opts = ImageOptions(format='image/png')
|
|
374
|
+
return TileManager(grid, mock_file_cache, [source], 'png',
|
|
375
|
+
meta_size=[2, 2], meta_buffer=0, image_opts=image_opts,
|
|
376
|
+
locker=tile_locker,
|
|
377
|
+
concurrent_tile_creators=2,
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
class TestTileManagerWMSSourceMinimalMetaRequests(object):
|
|
382
|
+
@pytest.fixture
|
|
383
|
+
def tile_mgr(self, mock_file_cache, mock_wms_client, tile_locker):
|
|
384
|
+
grid = TileGrid(SRS(4326), bbox=[-180, -90, 180, 90])
|
|
385
|
+
source = WMSSource(mock_wms_client)
|
|
386
|
+
return TileManager(grid, mock_file_cache, [source], 'png',
|
|
387
|
+
meta_size=[2, 2], meta_buffer=10, minimize_meta_requests=True,
|
|
388
|
+
locker=tile_locker,
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
def test_create_tile_single(self, tile_mgr, mock_file_cache, mock_wms_client):
|
|
392
|
+
# not enabled for single tile requests
|
|
393
|
+
tile_mgr.creator().create_tiles([Tile((0, 0, 2))])
|
|
394
|
+
assert mock_file_cache.stored_tiles == \
|
|
395
|
+
set([(0, 0, 2), (0, 1, 2), (1, 0, 2), (1, 1, 2)])
|
|
396
|
+
assert sorted(mock_wms_client.requested) == \
|
|
397
|
+
[((-180.0, -90.0, 3.515625, 90.0), (522, 512), SRS(4326))]
|
|
398
|
+
|
|
399
|
+
def test_create_tile_multiple(self, tile_mgr, mock_file_cache, mock_wms_client):
|
|
400
|
+
tile_mgr.creator().create_tiles([Tile((4, 0, 3)), Tile((4, 1, 3)), Tile((4, 2, 3))])
|
|
401
|
+
assert mock_file_cache.stored_tiles == \
|
|
402
|
+
set([(4, 0, 3), (4, 1, 3), (4, 2, 3)])
|
|
403
|
+
assert sorted(mock_wms_client.requested) == \
|
|
404
|
+
[((-1.7578125, -90, 46.7578125, 46.7578125), (276, 778), SRS(4326))]
|
|
405
|
+
|
|
406
|
+
def test_create_tile_multiple_fragmented(self, tile_mgr, mock_file_cache, mock_wms_client):
|
|
407
|
+
tile_mgr.creator().create_tiles([Tile((4, 0, 3)), Tile((5, 2, 3))])
|
|
408
|
+
assert mock_file_cache.stored_tiles == \
|
|
409
|
+
set([(4, 0, 3), (4, 1, 3), (4, 2, 3), (5, 0, 3), (5, 1, 3), (5, 2, 3)])
|
|
410
|
+
assert sorted(mock_wms_client.requested) == \
|
|
411
|
+
[((-1.7578125, -90, 91.7578125, 46.7578125), (532, 778), SRS(4326))]
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
class SlowMockSource(MockSource):
|
|
415
|
+
supports_meta_tiles = True
|
|
416
|
+
|
|
417
|
+
def get_map(self, query):
|
|
418
|
+
time.sleep(0.1)
|
|
419
|
+
return MockSource.get_map(self, query)
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
class TestTileManagerLocking(object):
|
|
423
|
+
@pytest.fixture
|
|
424
|
+
def slow_source(self):
|
|
425
|
+
return SlowMockSource()
|
|
426
|
+
|
|
427
|
+
@pytest.fixture
|
|
428
|
+
def file_cache(self, tmpdir):
|
|
429
|
+
return RecordFileCache(tmpdir.strpath, 'png')
|
|
430
|
+
|
|
431
|
+
@pytest.fixture
|
|
432
|
+
def tile_mgr(self, file_cache, slow_source, tile_locker):
|
|
433
|
+
grid = TileGrid(SRS(4326), bbox=[-180, -90, 180, 90])
|
|
434
|
+
image_opts = ImageOptions(format='image/png')
|
|
435
|
+
return TileManager(grid, file_cache, [slow_source], 'png',
|
|
436
|
+
meta_size=[2, 2], meta_buffer=0, image_opts=image_opts,
|
|
437
|
+
locker=tile_locker,
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
def test_get_single(self, tile_mgr, file_cache, slow_source):
|
|
441
|
+
tile_mgr.creator().create_tiles([Tile((0, 0, 1)), Tile((1, 0, 1))])
|
|
442
|
+
assert file_cache.stored_tiles == set([(0, 0, 1), (1, 0, 1)])
|
|
443
|
+
assert slow_source.requested == \
|
|
444
|
+
[((-180.0, -90.0, 180.0, 90.0), (512, 256), SRS(4326))]
|
|
445
|
+
|
|
446
|
+
def test_concurrent(self, tile_mgr, file_cache, slow_source):
|
|
447
|
+
def do_it():
|
|
448
|
+
tile_mgr.creator().create_tiles([Tile((0, 0, 1)), Tile((1, 0, 1))])
|
|
449
|
+
|
|
450
|
+
threads = [threading.Thread(target=do_it) for _ in range(3)]
|
|
451
|
+
[t.start() for t in threads]
|
|
452
|
+
[t.join() for t in threads]
|
|
453
|
+
|
|
454
|
+
assert file_cache.stored_tiles == set([(0, 0, 1), (1, 0, 1)])
|
|
455
|
+
assert file_cache.loaded_tiles == counting_set([(0, 0, 1), (1, 0, 1), (0, 0, 1), (1, 0, 1)])
|
|
456
|
+
assert slow_source.requested == \
|
|
457
|
+
[((-180.0, -90.0, 180.0, 90.0), (512, 256), SRS(4326))]
|
|
458
|
+
|
|
459
|
+
assert os.path.exists(file_cache.tile_location(Tile((0, 0, 1))))
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
class TestTileManagerMultipleSources(object):
|
|
463
|
+
@pytest.fixture
|
|
464
|
+
def source_base(self):
|
|
465
|
+
return MockSource()
|
|
466
|
+
|
|
467
|
+
@pytest.fixture
|
|
468
|
+
def source_overlay(self):
|
|
469
|
+
return MockSource()
|
|
470
|
+
|
|
471
|
+
@pytest.fixture
|
|
472
|
+
def tile_mgr(self, mock_file_cache, tile_locker, source_base, source_overlay):
|
|
473
|
+
grid = TileGrid(SRS(4326), bbox=[-180, -90, 180, 90])
|
|
474
|
+
image_opts = ImageOptions(format='image/png')
|
|
475
|
+
return TileManager(grid, mock_file_cache,
|
|
476
|
+
[source_base, source_overlay], 'png',
|
|
477
|
+
image_opts=image_opts,
|
|
478
|
+
locker=tile_locker,
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
def test_get_single(self, tile_mgr, mock_file_cache, source_base, source_overlay):
|
|
482
|
+
tile_mgr.creator().create_tiles([Tile((0, 0, 1))])
|
|
483
|
+
assert mock_file_cache.stored_tiles == set([(0, 0, 1)])
|
|
484
|
+
assert source_base.requested == \
|
|
485
|
+
[((-180.0, -90.0, 0.0, 90.0), (256, 256), SRS(4326))]
|
|
486
|
+
assert source_overlay.requested == \
|
|
487
|
+
[((-180.0, -90.0, 0.0, 90.0), (256, 256), SRS(4326))]
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
class SolidColorMockSource(MockSource):
|
|
491
|
+
def __init__(self, color='#ff0000'):
|
|
492
|
+
MockSource.__init__(self)
|
|
493
|
+
self.color = color
|
|
494
|
+
|
|
495
|
+
def _image(self, size):
|
|
496
|
+
return Image.new('RGB', size, self.color)
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
class TestTileManagerMultipleSourcesWithMetaTiles(object):
|
|
500
|
+
@pytest.fixture
|
|
501
|
+
def source_base(self):
|
|
502
|
+
src = SolidColorMockSource(color='#ff0000')
|
|
503
|
+
src.supports_meta_tiles = True
|
|
504
|
+
return src
|
|
505
|
+
|
|
506
|
+
@pytest.fixture
|
|
507
|
+
def source_overlay(self):
|
|
508
|
+
src = MockSource()
|
|
509
|
+
src.supports_meta_tiles = True
|
|
510
|
+
return src
|
|
511
|
+
|
|
512
|
+
@pytest.fixture
|
|
513
|
+
def tile_mgr(self, mock_file_cache, tile_locker, source_base, source_overlay):
|
|
514
|
+
grid = TileGrid(SRS(4326), bbox=[-180, -90, 180, 90])
|
|
515
|
+
image_opts = ImageOptions(format='image/png')
|
|
516
|
+
return TileManager(grid, mock_file_cache,
|
|
517
|
+
[source_base, source_overlay], 'png',
|
|
518
|
+
image_opts=image_opts,
|
|
519
|
+
meta_size=[2, 2], meta_buffer=0,
|
|
520
|
+
locker=tile_locker,
|
|
521
|
+
)
|
|
522
|
+
|
|
523
|
+
def test_merged_tiles(self, tile_mgr, mock_file_cache, source_base, source_overlay):
|
|
524
|
+
tiles = tile_mgr.creator().create_tiles([Tile((0, 0, 1)), Tile((1, 0, 1))])
|
|
525
|
+
assert mock_file_cache.stored_tiles == set([(0, 0, 1), (1, 0, 1)])
|
|
526
|
+
assert source_base.requested == \
|
|
527
|
+
[((-180.0, -90.0, 180.0, 90.0), (512, 256), SRS(4326))]
|
|
528
|
+
assert source_overlay.requested == \
|
|
529
|
+
[((-180.0, -90.0, 180.0, 90.0), (512, 256), SRS(4326))]
|
|
530
|
+
|
|
531
|
+
hist = tiles[0].source.as_image().histogram()
|
|
532
|
+
# lots of red (base), but not everything (overlay)
|
|
533
|
+
assert 55000 < hist[255] < 60000 # red = 0xff
|
|
534
|
+
assert 55000 < hist[256] # green = 0x00
|
|
535
|
+
assert 55000 < hist[512] # blue = 0x00
|
|
536
|
+
|
|
537
|
+
def test_sources_with_mixed_support_for_meta_tiles(self, mock_file_cache, source_base, source_overlay, tile_locker):
|
|
538
|
+
source_base.supports_meta_tiles = False
|
|
539
|
+
grid = TileGrid(SRS(4326), bbox=[-180, -90, 180, 90])
|
|
540
|
+
with pytest.raises(ValueError):
|
|
541
|
+
TileManager(grid, file_cache,
|
|
542
|
+
[source_base, source_overlay], 'png',
|
|
543
|
+
meta_size=[2, 2], meta_buffer=0,
|
|
544
|
+
locker=tile_locker)
|
|
545
|
+
|
|
546
|
+
def test_sources_with_no_support_for_meta_tiles(self, mock_file_cache, source_base, source_overlay, tile_locker):
|
|
547
|
+
source_base.supports_meta_tiles = False
|
|
548
|
+
source_overlay.supports_meta_tiles = False
|
|
549
|
+
|
|
550
|
+
grid = TileGrid(SRS(4326), bbox=[-180, -90, 180, 90])
|
|
551
|
+
tile_mgr = TileManager(grid, mock_file_cache,
|
|
552
|
+
[source_base, source_overlay], 'png',
|
|
553
|
+
meta_size=[2, 2], meta_buffer=0,
|
|
554
|
+
locker=tile_locker)
|
|
555
|
+
|
|
556
|
+
assert tile_mgr.meta_grid is None
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
class TestTileManagerBulkMetaTiles(object):
|
|
560
|
+
@pytest.fixture
|
|
561
|
+
def source_base(self):
|
|
562
|
+
src = SolidColorMockSource(color='#ff0000')
|
|
563
|
+
src.supports_meta_tiles = False
|
|
564
|
+
return src
|
|
565
|
+
|
|
566
|
+
@pytest.fixture
|
|
567
|
+
def source_overlay(self):
|
|
568
|
+
src = MockSource()
|
|
569
|
+
src.supports_meta_tiles = False
|
|
570
|
+
return src
|
|
571
|
+
|
|
572
|
+
@pytest.fixture
|
|
573
|
+
def tile_mgr(self, mock_file_cache, source_base, source_overlay, tile_locker):
|
|
574
|
+
grid = TileGrid(SRS(4326), bbox=[-180, -90, 180, 90], origin='ul')
|
|
575
|
+
return TileManager(grid, mock_file_cache,
|
|
576
|
+
[source_base, source_overlay], 'png',
|
|
577
|
+
meta_size=[2, 2], meta_buffer=0,
|
|
578
|
+
locker=tile_locker,
|
|
579
|
+
bulk_meta_tiles=True,
|
|
580
|
+
)
|
|
581
|
+
|
|
582
|
+
def test_bulk_get(self, tile_mgr, mock_file_cache, source_base, source_overlay):
|
|
583
|
+
tiles = tile_mgr.creator().create_tiles([Tile((0, 0, 2))])
|
|
584
|
+
assert len(tiles) == 2*2
|
|
585
|
+
assert mock_file_cache.stored_tiles == set([(0, 0, 2), (1, 0, 2), (0, 1, 2), (1, 1, 2)])
|
|
586
|
+
for requested in [source_base.requested, source_overlay.requested]:
|
|
587
|
+
assert set(requested) == set([
|
|
588
|
+
((-180.0, 0.0, -90.0, 90.0), (256, 256), SRS(4326)),
|
|
589
|
+
((-90.0, 0.0, 0.0, 90.0), (256, 256), SRS(4326)),
|
|
590
|
+
((-180.0, -90.0, -90.0, 0.0), (256, 256), SRS(4326)),
|
|
591
|
+
((-90.0, -90.0, 0.0, 0.0), (256, 256), SRS(4326)),
|
|
592
|
+
])
|
|
593
|
+
|
|
594
|
+
def test_bulk_get_error(self, tile_mgr, source_base):
|
|
595
|
+
tile_mgr.sources = [source_base, ErrorSource()]
|
|
596
|
+
try:
|
|
597
|
+
tile_mgr.creator().create_tiles([Tile((0, 0, 2))])
|
|
598
|
+
except Exception as ex:
|
|
599
|
+
assert ex.args[0] == "source error"
|
|
600
|
+
|
|
601
|
+
def test_bulk_get_multiple_meta_tiles(self, tile_mgr, mock_file_cache):
|
|
602
|
+
tiles = tile_mgr.creator().create_tiles([Tile((1, 0, 2)), Tile((2, 0, 2))])
|
|
603
|
+
assert len(tiles) == 2*2*2
|
|
604
|
+
assert mock_file_cache.stored_tiles, set([
|
|
605
|
+
(0, 0, 2), (1, 0, 2), (0, 1, 2), (1, 1, 2),
|
|
606
|
+
(2, 0, 2), (3, 0, 2), (2, 1, 2), (3, 1, 2),
|
|
607
|
+
])
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
class TestTileManagerBulkMetaTilesConcurrent(TestTileManagerBulkMetaTiles):
|
|
611
|
+
@pytest.fixture
|
|
612
|
+
def tile_mgr(self, mock_file_cache, source_base, source_overlay, tile_locker):
|
|
613
|
+
grid = TileGrid(SRS(4326), bbox=[-180, -90, 180, 90], origin='ul')
|
|
614
|
+
return TileManager(
|
|
615
|
+
grid, mock_file_cache,
|
|
616
|
+
[source_base, source_overlay], 'png',
|
|
617
|
+
meta_size=[2, 2], meta_buffer=0,
|
|
618
|
+
locker=tile_locker,
|
|
619
|
+
bulk_meta_tiles=True,
|
|
620
|
+
concurrent_tile_creators=2,
|
|
621
|
+
)
|
|
622
|
+
|
|
623
|
+
|
|
624
|
+
class ErrorSource(MapLayer):
|
|
625
|
+
def __init__(self, *args):
|
|
626
|
+
MapLayer.__init__(self, *args)
|
|
627
|
+
self.requested = []
|
|
628
|
+
|
|
629
|
+
def get_map(self, query):
|
|
630
|
+
self.requested.append((query.bbox, query.size, query.srs))
|
|
631
|
+
raise Exception("source error")
|
|
632
|
+
|
|
633
|
+
|
|
634
|
+
default_image_opts = ImageOptions(resampling='bicubic')
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
class TestCacheMapLayer(object):
|
|
638
|
+
@pytest.fixture
|
|
639
|
+
def layer(self, mock_file_cache, mock_wms_client, tile_locker):
|
|
640
|
+
grid = TileGrid(SRS(4326), bbox=[-180, -90, 180, 90])
|
|
641
|
+
source = WMSSource(mock_wms_client)
|
|
642
|
+
image_opts = ImageOptions(resampling='nearest')
|
|
643
|
+
tile_mgr = TileManager(grid, mock_file_cache, [source], 'png',
|
|
644
|
+
meta_size=[2, 2], meta_buffer=0, image_opts=image_opts,
|
|
645
|
+
locker=tile_locker)
|
|
646
|
+
return CacheMapLayer(tile_mgr, image_opts=default_image_opts)
|
|
647
|
+
|
|
648
|
+
def test_get_map_small(self, layer, mock_file_cache):
|
|
649
|
+
result = layer.get_map(MapQuery((-180, -90, 180, 90), (300, 150), SRS(4326), 'png'))
|
|
650
|
+
assert mock_file_cache.stored_tiles == set([(0, 0, 1), (1, 0, 1)])
|
|
651
|
+
assert result.size == (300, 150)
|
|
652
|
+
|
|
653
|
+
def test_get_map_large(self, layer, mock_file_cache):
|
|
654
|
+
# gets next resolution layer
|
|
655
|
+
result = layer.get_map(MapQuery((-180, -90, 180, 90), (600, 300), SRS(4326), 'png'))
|
|
656
|
+
assert mock_file_cache.stored_tiles == \
|
|
657
|
+
set([(0, 0, 2), (1, 0, 2), (0, 1, 2), (1, 1, 2),
|
|
658
|
+
(2, 0, 2), (3, 0, 2), (2, 1, 2), (3, 1, 2)])
|
|
659
|
+
assert result.size == (600, 300)
|
|
660
|
+
|
|
661
|
+
def test_transformed(self, layer, mock_file_cache):
|
|
662
|
+
result = layer.get_map(MapQuery(
|
|
663
|
+
(-20037508.34, -20037508.34, 20037508.34, 20037508.34), (500, 500),
|
|
664
|
+
SRS(900913), 'png'))
|
|
665
|
+
assert mock_file_cache.stored_tiles == \
|
|
666
|
+
set([(0, 0, 2), (1, 0, 2), (0, 1, 2), (1, 1, 2),
|
|
667
|
+
(2, 0, 2), (3, 0, 2), (2, 1, 2), (3, 1, 2)])
|
|
668
|
+
assert result.size == (500, 500)
|
|
669
|
+
|
|
670
|
+
def test_single_tile_match(self, layer, mock_file_cache):
|
|
671
|
+
result = layer.get_map(MapQuery(
|
|
672
|
+
(0.001, 0, 90, 90), (256, 256), SRS(4326), 'png', tiled_only=True))
|
|
673
|
+
assert mock_file_cache.stored_tiles == \
|
|
674
|
+
set([(3, 0, 2), (2, 0, 2), (3, 1, 2), (2, 1, 2)])
|
|
675
|
+
assert result.size == (256, 256)
|
|
676
|
+
|
|
677
|
+
def test_single_tile_no_match(self, layer):
|
|
678
|
+
with pytest.raises(MapBBOXError):
|
|
679
|
+
layer.get_map(
|
|
680
|
+
MapQuery((0.1, 0, 90, 90), (256, 256),
|
|
681
|
+
SRS(4326), 'png', tiled_only=True)
|
|
682
|
+
)
|
|
683
|
+
|
|
684
|
+
def test_get_map_with_res_range(self, mock_file_cache, mock_wms_client, tile_locker):
|
|
685
|
+
grid = TileGrid(SRS(4326), bbox=[-180, -90, 180, 90])
|
|
686
|
+
res_range = resolution_range(1000, 10)
|
|
687
|
+
source = WMSSource(mock_wms_client, res_range=res_range)
|
|
688
|
+
image_opts = ImageOptions(resampling='nearest')
|
|
689
|
+
tile_mgr = TileManager(grid, mock_file_cache, [source], 'png',
|
|
690
|
+
meta_size=[2, 2], meta_buffer=0, image_opts=image_opts,
|
|
691
|
+
locker=tile_locker)
|
|
692
|
+
layer = CacheMapLayer(tile_mgr, image_opts=default_image_opts)
|
|
693
|
+
|
|
694
|
+
with pytest.raises(BlankImage):
|
|
695
|
+
result = layer.get_map(MapQuery(
|
|
696
|
+
(-20037508.34, -20037508.34, 20037508.34, 20037508.34), (500, 500),
|
|
697
|
+
SRS(900913), 'png'))
|
|
698
|
+
assert mock_file_cache.stored_tiles == set()
|
|
699
|
+
|
|
700
|
+
result = layer.get_map(MapQuery(
|
|
701
|
+
(0, 0, 10000, 10000), (50, 50),
|
|
702
|
+
SRS(900913), 'png'))
|
|
703
|
+
assert mock_file_cache.stored_tiles == \
|
|
704
|
+
set([(512, 257, 10), (513, 256, 10), (512, 256, 10), (513, 257, 10)])
|
|
705
|
+
assert result.size == (50, 50)
|
|
706
|
+
|
|
707
|
+
|
|
708
|
+
class TestCacheMapLayerWithExtent(object):
|
|
709
|
+
@pytest.fixture
|
|
710
|
+
def source(self, mock_wms_client):
|
|
711
|
+
return WMSSource(mock_wms_client)
|
|
712
|
+
|
|
713
|
+
@pytest.fixture
|
|
714
|
+
def layer(self, mock_file_cache, source, tile_locker):
|
|
715
|
+
grid = TileGrid(SRS(4326), bbox=[-180, -90, 180, 90])
|
|
716
|
+
image_opts = ImageOptions(resampling='nearest', format='png')
|
|
717
|
+
tile_mgr = TileManager(grid, mock_file_cache, [source], 'png',
|
|
718
|
+
meta_size=[1, 1], meta_buffer=0, image_opts=image_opts,
|
|
719
|
+
locker=tile_locker)
|
|
720
|
+
layer = CacheMapLayer(tile_mgr, image_opts=default_image_opts)
|
|
721
|
+
layer.extent = BBOXCoverage([0, 0, 90, 45], SRS(4326)).extent
|
|
722
|
+
return layer
|
|
723
|
+
|
|
724
|
+
def test_get_outside_extent(self, layer):
|
|
725
|
+
with pytest.raises(BlankImage):
|
|
726
|
+
layer.get_map(MapQuery((-180, -90, 0, 0), (300, 150), SRS(4326), 'png'))
|
|
727
|
+
|
|
728
|
+
def test_get_map_small(self, layer, mock_file_cache, mock_wms_client):
|
|
729
|
+
result = layer.get_map(MapQuery((-180, -90, 180, 90), (300, 150), SRS(4326), 'png'))
|
|
730
|
+
assert mock_file_cache.stored_tiles == set([(1, 0, 1)])
|
|
731
|
+
# source requests one tile (no meta-tiling configured)
|
|
732
|
+
assert mock_wms_client.requested == [((0.0, -90.0, 180.0, 90.0), (256, 256), SRS('EPSG:4326'))]
|
|
733
|
+
assert result.size == (300, 150)
|
|
734
|
+
|
|
735
|
+
def test_get_map_small_with_source_extent(self, source, layer, mock_file_cache, mock_wms_client):
|
|
736
|
+
source.extent = BBOXCoverage([0, 0, 90, 45], SRS(4326)).extent
|
|
737
|
+
result = layer.get_map(MapQuery((-180, -90, 180, 90), (300, 150), SRS(4326), 'png'))
|
|
738
|
+
assert mock_file_cache.stored_tiles == set([(1, 0, 1)])
|
|
739
|
+
# source requests one tile (no meta-tiling configured) limited to source.extent
|
|
740
|
+
assert mock_wms_client.requested == [((0, 0, 90, 45), (128, 64), (SRS(4326)))]
|
|
741
|
+
assert result.size == (300, 150)
|
|
742
|
+
|
|
743
|
+
|
|
744
|
+
class TestDirectMapLayer(object):
|
|
745
|
+
@pytest.fixture
|
|
746
|
+
def layer(self, mock_wms_client):
|
|
747
|
+
source = WMSSource(mock_wms_client)
|
|
748
|
+
return DirectMapLayer(source, GLOBAL_GEOGRAPHIC_EXTENT)
|
|
749
|
+
|
|
750
|
+
def test_get_map(self, layer, mock_wms_client):
|
|
751
|
+
result = layer.get_map(MapQuery((-180, -90, 180, 90), (300, 150), SRS(4326), 'png'))
|
|
752
|
+
assert mock_wms_client.requested == [((-180, -90, 180, 90), (300, 150), SRS(4326))]
|
|
753
|
+
assert result.size == (300, 150)
|
|
754
|
+
|
|
755
|
+
def test_get_map_mercator(self, layer, mock_wms_client):
|
|
756
|
+
result = layer.get_map(MapQuery(
|
|
757
|
+
(-20037508.34, -20037508.34, 20037508.34, 20037508.34), (500, 500),
|
|
758
|
+
SRS(900913), 'png'))
|
|
759
|
+
assert mock_wms_client.requested == \
|
|
760
|
+
[((-20037508.34, -20037508.34, 20037508.34, 20037508.34), (500, 500),
|
|
761
|
+
SRS(900913))]
|
|
762
|
+
assert result.size == (500, 500)
|
|
763
|
+
|
|
764
|
+
|
|
765
|
+
class TestDirectMapLayerWithSupportedSRS(object):
|
|
766
|
+
@pytest.fixture
|
|
767
|
+
def layer(self, mock_wms_client):
|
|
768
|
+
source = WMSSource(mock_wms_client)
|
|
769
|
+
return DirectMapLayer(source, GLOBAL_GEOGRAPHIC_EXTENT)
|
|
770
|
+
|
|
771
|
+
def test_get_map(self, layer, mock_wms_client):
|
|
772
|
+
result = layer.get_map(MapQuery((-180, -90, 180, 90), (300, 150), SRS(4326), 'png'))
|
|
773
|
+
assert mock_wms_client.requested == [((-180, -90, 180, 90), (300, 150), SRS(4326))]
|
|
774
|
+
assert result.size == (300, 150)
|
|
775
|
+
|
|
776
|
+
def test_get_map_mercator(self, layer, mock_wms_client):
|
|
777
|
+
result = layer.get_map(MapQuery(
|
|
778
|
+
(-20037508.34, -20037508.34, 20037508.34, 20037508.34), (500, 500),
|
|
779
|
+
SRS(900913), 'png'))
|
|
780
|
+
assert mock_wms_client.requested == \
|
|
781
|
+
[((-20037508.34, -20037508.34, 20037508.34, 20037508.34), (500, 500),
|
|
782
|
+
SRS(900913))]
|
|
783
|
+
assert result.size == (500, 500)
|
|
784
|
+
|
|
785
|
+
|
|
786
|
+
class MockHTTPClient(object):
|
|
787
|
+
def __init__(self):
|
|
788
|
+
self.requested = []
|
|
789
|
+
|
|
790
|
+
def open(self, url, data=None):
|
|
791
|
+
self.requested.append(url)
|
|
792
|
+
w = int(re.search(r'width=(\d+)', url, re.IGNORECASE).group(1))
|
|
793
|
+
h = int(re.search(r'height=(\d+)', url, re.IGNORECASE).group(1))
|
|
794
|
+
format = re.search(r'format=image(/|%2F)(\w+)', url, re.IGNORECASE).group(2)
|
|
795
|
+
transparent = re.search(r'transparent=(\w+)', url, re.IGNORECASE)
|
|
796
|
+
transparent = True if transparent and transparent.group(1).lower() == 'true' else False
|
|
797
|
+
result = BytesIO()
|
|
798
|
+
create_debug_img((int(w), int(h)), transparent).save(result, format=format)
|
|
799
|
+
result.seek(0)
|
|
800
|
+
result.headers = {'Content-type': 'image/'+format}
|
|
801
|
+
return result
|
|
802
|
+
|
|
803
|
+
|
|
804
|
+
@pytest.fixture
|
|
805
|
+
def mock_http_client():
|
|
806
|
+
return MockHTTPClient()
|
|
807
|
+
|
|
808
|
+
|
|
809
|
+
class TestWMSSourceTransform(object):
|
|
810
|
+
@pytest.fixture
|
|
811
|
+
def source(self, mock_http_client):
|
|
812
|
+
req_template = WMS111MapRequest(url='http://localhost/service?', param={
|
|
813
|
+
'format': 'image/png', 'layers': 'foo'
|
|
814
|
+
})
|
|
815
|
+
client = WMSClient(req_template, http_client=mock_http_client)
|
|
816
|
+
return WMSSource(client, supported_srs=SupportedSRS([SRS(4326)]),
|
|
817
|
+
image_opts=ImageOptions(resampling='bilinear'))
|
|
818
|
+
|
|
819
|
+
def test_get_map(self, source, mock_http_client):
|
|
820
|
+
source.get_map(MapQuery((-180, -90, 180, 90), (300, 150), SRS(4326)))
|
|
821
|
+
assert query_eq(mock_http_client.requested[0], "http://localhost/service?"
|
|
822
|
+
"layers=foo&width=300&version=1.1.1&bbox=-180,-90,180,90&service=WMS"
|
|
823
|
+
"&format=image%2Fpng&styles=&srs=EPSG%3A4326&request=GetMap&height=150")
|
|
824
|
+
|
|
825
|
+
def test_get_map_transformed(self, source, mock_http_client):
|
|
826
|
+
source.get_map(MapQuery(
|
|
827
|
+
(556597, 4865942, 1669792, 7361866), (300, 150), SRS(900913)))
|
|
828
|
+
assert wms_query_eq(mock_http_client.requested[0], "http://localhost/service?"
|
|
829
|
+
"layers=foo&width=300&version=1.1.1"
|
|
830
|
+
"&bbox=4.99999592195,39.9999980766,14.999996749,54.9999994175&service=WMS"
|
|
831
|
+
"&format=image%2Fpng&styles=&srs=EPSG%3A4326&request=GetMap&height=450")
|
|
832
|
+
|
|
833
|
+
|
|
834
|
+
class TestWMSSourceWithClient(object):
|
|
835
|
+
|
|
836
|
+
@pytest.fixture
|
|
837
|
+
def req_template(self):
|
|
838
|
+
return WMS111MapRequest(
|
|
839
|
+
url='http://%s:%d/service?' % TEST_SERVER_ADDRESS,
|
|
840
|
+
param={'format': 'image/png', 'layers': 'foo'},
|
|
841
|
+
)
|
|
842
|
+
|
|
843
|
+
@pytest.fixture
|
|
844
|
+
def client(self, req_template):
|
|
845
|
+
return WMSClient(req_template)
|
|
846
|
+
|
|
847
|
+
@pytest.fixture
|
|
848
|
+
def source(self, client):
|
|
849
|
+
return WMSSource(client)
|
|
850
|
+
|
|
851
|
+
def test_get_map(self, source):
|
|
852
|
+
with tmp_image((512, 512)) as img:
|
|
853
|
+
expected_req = ({'path': r'/service?LAYERS=foo&SERVICE=WMS&FORMAT=image%2Fpng'
|
|
854
|
+
'&REQUEST=GetMap&HEIGHT=512&SRS=EPSG%3A4326&styles='
|
|
855
|
+
'&VERSION=1.1.1&BBOX=0.0,10.0,10.0,20.0&WIDTH=512'},
|
|
856
|
+
{'body': img.read(), 'headers': {'content-type': 'image/png'}})
|
|
857
|
+
with mock_httpd(TEST_SERVER_ADDRESS, [expected_req]):
|
|
858
|
+
q = MapQuery((0.0, 10.0, 10.0, 20.0), (512, 512), SRS(4326))
|
|
859
|
+
result = source.get_map(q)
|
|
860
|
+
assert isinstance(result, ImageSource)
|
|
861
|
+
assert result.size == (512, 512)
|
|
862
|
+
assert is_png(result.as_buffer(seekable=True))
|
|
863
|
+
assert result.as_image().size == (512, 512)
|
|
864
|
+
|
|
865
|
+
def test_get_map_non_image_content_type(self, source):
|
|
866
|
+
with tmp_image((512, 512)) as img:
|
|
867
|
+
expected_req = ({'path': r'/service?LAYERS=foo&SERVICE=WMS&FORMAT=image%2Fpng'
|
|
868
|
+
'&REQUEST=GetMap&HEIGHT=512&SRS=EPSG%3A4326&styles='
|
|
869
|
+
'&VERSION=1.1.1&BBOX=0.0,10.0,10.0,20.0&WIDTH=512'},
|
|
870
|
+
{'body': img.read(), 'headers': {'content-type': 'text/plain'}})
|
|
871
|
+
with mock_httpd(TEST_SERVER_ADDRESS, [expected_req]):
|
|
872
|
+
q = MapQuery((0.0, 10.0, 10.0, 20.0), (512, 512), SRS(4326))
|
|
873
|
+
try:
|
|
874
|
+
source.get_map(q)
|
|
875
|
+
except SourceError as e:
|
|
876
|
+
assert 'no image returned' in e.args[0]
|
|
877
|
+
else:
|
|
878
|
+
assert False, 'no SourceError raised'
|
|
879
|
+
|
|
880
|
+
def test_basic_auth(self, req_template, client, source):
|
|
881
|
+
http_client = HTTPClient(req_template.url, username='foo', password='bar@')
|
|
882
|
+
client.http_client = http_client
|
|
883
|
+
|
|
884
|
+
def assert_auth(req_handler):
|
|
885
|
+
assert 'Authorization' in req_handler.headers
|
|
886
|
+
auth_data = req_handler.headers['Authorization'].split()[1]
|
|
887
|
+
auth_data = base64.b64decode(auth_data.encode('utf-8')).decode('utf-8')
|
|
888
|
+
assert auth_data == 'foo:bar@'
|
|
889
|
+
return True
|
|
890
|
+
expected_req = ({'path': r'/service?LAYERS=foo&SERVICE=WMS&FORMAT=image%2Fpng'
|
|
891
|
+
'&REQUEST=GetMap&HEIGHT=512&SRS=EPSG%3A4326'
|
|
892
|
+
'&VERSION=1.1.1&BBOX=0.0,10.0,10.0,20.0&WIDTH=512&STYLES=',
|
|
893
|
+
'require_basic_auth': True,
|
|
894
|
+
'req_assert_function': assert_auth},
|
|
895
|
+
{'body': b'no image', 'headers': {'content-type': 'image/png'}})
|
|
896
|
+
with mock_httpd(TEST_SERVER_ADDRESS, [expected_req]):
|
|
897
|
+
q = MapQuery((0.0, 10.0, 10.0, 20.0), (512, 512), SRS(4326))
|
|
898
|
+
source.get_map(q)
|
|
899
|
+
|
|
900
|
+
def test_http_error_handler(self, client):
|
|
901
|
+
error_handler = HTTPSourceErrorHandler()
|
|
902
|
+
error_handler.add_handler(500, (255, 0, 0), cacheable=True)
|
|
903
|
+
error_handler.add_handler(400, (0, 0, 0), cacheable=False)
|
|
904
|
+
source = WMSSource(client, error_handler=error_handler)
|
|
905
|
+
expected_req = [
|
|
906
|
+
(
|
|
907
|
+
{
|
|
908
|
+
'path': r'/service?LAYERS=foo&SERVICE=WMS&FORMAT=image%2Fpng'
|
|
909
|
+
'&REQUEST=GetMap&HEIGHT=512&SRS=EPSG%3A4326'
|
|
910
|
+
'&VERSION=1.1.1&BBOX=0.0,10.0,10.0,20.0&WIDTH=512&STYLES='
|
|
911
|
+
},
|
|
912
|
+
{
|
|
913
|
+
'body': b'error',
|
|
914
|
+
'status': 500,
|
|
915
|
+
'headers': {'content-type': 'text/plain'},
|
|
916
|
+
},
|
|
917
|
+
),
|
|
918
|
+
(
|
|
919
|
+
{
|
|
920
|
+
'path': r'/service?LAYERS=foo&SERVICE=WMS&FORMAT=image%2Fpng'
|
|
921
|
+
'&REQUEST=GetMap&HEIGHT=512&SRS=EPSG%3A4326'
|
|
922
|
+
'&VERSION=1.1.1&BBOX=0.0,10.0,10.0,20.0&WIDTH=512&STYLES='
|
|
923
|
+
},
|
|
924
|
+
{
|
|
925
|
+
'body': b'error',
|
|
926
|
+
'status': 400,
|
|
927
|
+
'headers': {'content-type': 'text/plain'},
|
|
928
|
+
},
|
|
929
|
+
),
|
|
930
|
+
]
|
|
931
|
+
with mock_httpd(TEST_SERVER_ADDRESS, expected_req):
|
|
932
|
+
query = MapQuery((0.0, 10.0, 10.0, 20.0), (512, 512), SRS(4326))
|
|
933
|
+
resp = source.get_map(query)
|
|
934
|
+
assert resp.cacheable
|
|
935
|
+
assert resp.as_image().getcolors() == [((512 * 512), (255, 0, 0))]
|
|
936
|
+
|
|
937
|
+
resp = source.get_map(query)
|
|
938
|
+
assert not resp.cacheable
|
|
939
|
+
assert resp.as_image().getcolors() == [((512 * 512), (0, 0, 0))]
|
|
940
|
+
|
|
941
|
+
|
|
942
|
+
TESTSERVER_URL = 'http://%s:%d' % TEST_SERVER_ADDRESS
|
|
943
|
+
|
|
944
|
+
|
|
945
|
+
class TestWMSSource(object):
|
|
946
|
+
|
|
947
|
+
@pytest.fixture
|
|
948
|
+
def source(self, mock_http_client):
|
|
949
|
+
req = WMS111MapRequest(url=TESTSERVER_URL + '/service?map=foo', param={'layers': 'foo'})
|
|
950
|
+
wms = WMSClient(req, http_client=mock_http_client)
|
|
951
|
+
return WMSSource(wms, supported_srs=SupportedSRS([SRS(4326)]),
|
|
952
|
+
image_opts=ImageOptions(resampling='bilinear'))
|
|
953
|
+
|
|
954
|
+
def test_request(self, source, mock_http_client):
|
|
955
|
+
req = MapQuery((-180.0, -90.0, 180.0, 90.0), (512, 256), SRS(4326), 'png')
|
|
956
|
+
source.get_map(req)
|
|
957
|
+
assert len(mock_http_client.requested) == 1
|
|
958
|
+
assert_query_eq(mock_http_client.requested[0],
|
|
959
|
+
TESTSERVER_URL+'/service?map=foo&LAYERS=foo&SERVICE=WMS&FORMAT=image%2Fpng'
|
|
960
|
+
'&REQUEST=GetMap&HEIGHT=256&SRS=EPSG%3A4326'
|
|
961
|
+
'&VERSION=1.1.1&BBOX=-180.0,-90.0,180.0,90.0&WIDTH=512&STYLES=')
|
|
962
|
+
|
|
963
|
+
def test_transformed_request(self, source, mock_http_client):
|
|
964
|
+
req = MapQuery((-200000, -200000, 200000, 200000), (512, 512), SRS(900913), 'png')
|
|
965
|
+
resp = source.get_map(req)
|
|
966
|
+
assert len(mock_http_client.requested) == 1
|
|
967
|
+
|
|
968
|
+
assert wms_query_eq(mock_http_client.requested[0],
|
|
969
|
+
TESTSERVER_URL+'/service?map=foo&LAYERS=foo&SERVICE=WMS&FORMAT=image%2Fpng'
|
|
970
|
+
'&REQUEST=GetMap&HEIGHT=512&SRS=EPSG%3A4326'
|
|
971
|
+
'&VERSION=1.1.1&WIDTH=512&STYLES='
|
|
972
|
+
'&BBOX=-1.79663056824,-1.7963362121,1.79663056824,1.7963362121')
|
|
973
|
+
img = resp.as_image()
|
|
974
|
+
assert img.mode in ('P', 'RGB')
|
|
975
|
+
|
|
976
|
+
def test_transformed_request_transparent(self, mock_http_client):
|
|
977
|
+
req = WMS111MapRequest(url=TESTSERVER_URL + '/service?map=foo',
|
|
978
|
+
param={'layers': 'foo', 'transparent': 'true'})
|
|
979
|
+
wms = WMSClient(req, http_client=mock_http_client)
|
|
980
|
+
source = WMSSource(wms, supported_srs=SupportedSRS([SRS(4326)]),
|
|
981
|
+
image_opts=ImageOptions(resampling='bilinear'))
|
|
982
|
+
|
|
983
|
+
req = MapQuery((-200000, -200000, 200000, 200000), (512, 512), SRS(900913), 'png')
|
|
984
|
+
resp = source.get_map(req)
|
|
985
|
+
assert len(mock_http_client.requested) == 1
|
|
986
|
+
|
|
987
|
+
assert wms_query_eq(mock_http_client.requested[0],
|
|
988
|
+
TESTSERVER_URL+'/service?map=foo&LAYERS=foo&SERVICE=WMS&FORMAT=image%2Fpng'
|
|
989
|
+
'&REQUEST=GetMap&HEIGHT=512&SRS=EPSG%3A4326'
|
|
990
|
+
'&VERSION=1.1.1&WIDTH=512&STYLES=&transparent=true'
|
|
991
|
+
'&BBOX=-1.79663056824,-1.7963362121,1.79663056824,1.7963362121')
|
|
992
|
+
img = resp.as_image()
|
|
993
|
+
assert img.mode in ('P', 'RGBA')
|
|
994
|
+
img = img.convert('RGBA')
|
|
995
|
+
assert img.getpixel((5, 5))[3] == 0
|
|
996
|
+
|
|
997
|
+
|
|
998
|
+
class MockLayer(object):
|
|
999
|
+
def __init__(self):
|
|
1000
|
+
self.requested = []
|
|
1001
|
+
|
|
1002
|
+
def get_map(self, query):
|
|
1003
|
+
self.requested.append((query.bbox, query.size, query.srs))
|
|
1004
|
+
|
|
1005
|
+
|
|
1006
|
+
@pytest.mark.parametrize('case,map_query,low_requested', [
|
|
1007
|
+
['low', MapQuery((0, 0, 10000, 10000), (100, 100), SRS(3857)), True],
|
|
1008
|
+
['high', MapQuery((0, 0, 100, 100), (100, 100), SRS(3857)), False],
|
|
1009
|
+
['match', MapQuery((0, 0, 10, 10), (100, 100), SRS(3857)), False],
|
|
1010
|
+
['low_transform', MapQuery((0, 0, 0.1, 0.1), (100, 100), SRS(4326)), True],
|
|
1011
|
+
['high_transform', MapQuery((0, 0, 0.005, 0.005), (100, 100), SRS(4326)), False],
|
|
1012
|
+
])
|
|
1013
|
+
def test_resolution_conditional_layers(case, map_query, low_requested):
|
|
1014
|
+
low = MockLayer()
|
|
1015
|
+
high = MockLayer()
|
|
1016
|
+
layer = ResolutionConditional(low, high, 10, SRS(3857),
|
|
1017
|
+
GLOBAL_GEOGRAPHIC_EXTENT)
|
|
1018
|
+
|
|
1019
|
+
layer.get_map(map_query)
|
|
1020
|
+
assert bool(low.requested) == low_requested
|
|
1021
|
+
assert bool(high.requested) != low_requested
|
|
1022
|
+
|
|
1023
|
+
|
|
1024
|
+
def test_srs_conditional_layers():
|
|
1025
|
+
l4326 = MockLayer()
|
|
1026
|
+
l3857 = MockLayer()
|
|
1027
|
+
l25832 = MockLayer()
|
|
1028
|
+
preferred = PreferredSrcSRS()
|
|
1029
|
+
preferred.add(SRS(31467), [SRS(25832), SRS(3857)])
|
|
1030
|
+
layer = SRSConditional([
|
|
1031
|
+
(l4326, SRS(4326)),
|
|
1032
|
+
(l3857, SRS(3857)),
|
|
1033
|
+
(l25832, SRS(25832)),
|
|
1034
|
+
], GLOBAL_GEOGRAPHIC_EXTENT, preferred_srs=preferred,
|
|
1035
|
+
)
|
|
1036
|
+
|
|
1037
|
+
# srs match
|
|
1038
|
+
assert layer._select_layer(SRS(4326)) == l4326
|
|
1039
|
+
assert layer._select_layer(SRS(3857)) == l3857
|
|
1040
|
+
assert layer._select_layer(SRS(25832)) == l25832
|
|
1041
|
+
# type match (projected)
|
|
1042
|
+
assert layer._select_layer(SRS(31466)) == l3857
|
|
1043
|
+
assert layer._select_layer(SRS(32633)) == l3857
|
|
1044
|
+
assert layer._select_layer(SRS(4258)) == l4326
|
|
1045
|
+
# preferred
|
|
1046
|
+
assert layer._select_layer(SRS(31467)) == l25832
|
|
1047
|
+
|
|
1048
|
+
|
|
1049
|
+
@pytest.mark.parametrize('case,map_query,is_direct,is_l3857,is_l4326', [
|
|
1050
|
+
['high_3857', MapQuery((0, 0, 100, 100), (100, 100), SRS(900913)), True, False, False],
|
|
1051
|
+
['high_4326', MapQuery((0, 0, 0.0001, 0.0001), (100, 100), SRS(4326)), True, False, False],
|
|
1052
|
+
['low_4326', MapQuery((0, 0, 10, 10), (100, 100), SRS(4326)), False, False, True],
|
|
1053
|
+
['low_3857', MapQuery((0, 0, 10000, 10000), (100, 100), SRS(31467)), False, True, False],
|
|
1054
|
+
['low_projected', MapQuery((0, 0, 10000, 10000), (100, 100), SRS(31467)), False, True, False],
|
|
1055
|
+
])
|
|
1056
|
+
def test_neasted_conditional_layers(case, map_query, is_direct, is_l3857, is_l4326):
|
|
1057
|
+
direct = MockLayer()
|
|
1058
|
+
l3857 = MockLayer()
|
|
1059
|
+
l4326 = MockLayer()
|
|
1060
|
+
layer = ResolutionConditional(
|
|
1061
|
+
SRSConditional([
|
|
1062
|
+
(l3857, SRS('EPSG:3857')),
|
|
1063
|
+
(l4326, SRS('EPSG:4326'))
|
|
1064
|
+
], GLOBAL_GEOGRAPHIC_EXTENT),
|
|
1065
|
+
direct, 10, SRS(3857), GLOBAL_GEOGRAPHIC_EXTENT
|
|
1066
|
+
)
|
|
1067
|
+
layer.get_map(map_query)
|
|
1068
|
+
assert bool(direct.requested) == is_direct
|
|
1069
|
+
assert bool(l3857.requested) == is_l3857
|
|
1070
|
+
assert bool(l4326.requested) == is_l4326
|
|
1071
|
+
|
|
1072
|
+
|
|
1073
|
+
def is_blank(tiles):
|
|
1074
|
+
return all([t.source is None or isinstance(t.source, BlankImageSource) for t in tiles.tiles])
|
|
1075
|
+
|
|
1076
|
+
|
|
1077
|
+
class TestTileManagerEmptySources(object):
|
|
1078
|
+
def test_upscale(self, mock_file_cache, tile_locker):
|
|
1079
|
+
grid = TileGrid(SRS(4326), bbox=[-180, -90, 180, 90])
|
|
1080
|
+
image_opts = ImageOptions(format='image/png')
|
|
1081
|
+
tm = TileManager(
|
|
1082
|
+
grid, mock_file_cache, [], 'png',
|
|
1083
|
+
locker=tile_locker,
|
|
1084
|
+
image_opts=image_opts,
|
|
1085
|
+
)
|
|
1086
|
+
|
|
1087
|
+
assert is_blank(tm.load_tile_coords([(0, 0, 0)]))
|
|
1088
|
+
assert is_blank(tm.load_tile_coords([(3, 2, 5)]))
|
|
1089
|
+
|
|
1090
|
+
assert mock_file_cache.stored_tiles == set()
|
|
1091
|
+
assert mock_file_cache.loaded_tiles == counting_set([(0, 0, 0), (3, 2, 5)])
|
|
1092
|
+
|
|
1093
|
+
|
|
1094
|
+
class TestTileManagerRescaleTiles(object):
|
|
1095
|
+
@pytest.fixture
|
|
1096
|
+
def file_cache(self, tmpdir):
|
|
1097
|
+
return RecordFileCache(tmpdir.strpath, 'png')
|
|
1098
|
+
|
|
1099
|
+
@pytest.mark.parametrize("name,rescale_tiles,tiles,store,expected_load,output", [
|
|
1100
|
+
(
|
|
1101
|
+
"no-scale: missing tile, no rescale",
|
|
1102
|
+
0, [(0, 0, 0)], [], [(0, 0, 0)], "blank",
|
|
1103
|
+
),
|
|
1104
|
+
(
|
|
1105
|
+
"downscale: missing tile, 1 level rescale with None tiles",
|
|
1106
|
+
1, [(0, 0, 0)], [], [(0, 0, 0), None, None, (0, 0, 1), (1, 0, 1)], "blank",
|
|
1107
|
+
),
|
|
1108
|
+
(
|
|
1109
|
+
"downscale: missing tile, 1 level rescale",
|
|
1110
|
+
1, [(1, 2, 4)], [], [(1, 2, 4), (2, 4, 5), (3, 4, 5), (2, 5, 5), (3, 5, 5)], "blank",
|
|
1111
|
+
),
|
|
1112
|
+
(
|
|
1113
|
+
"downscale: missing tile, 2 level rescale",
|
|
1114
|
+
2, [(1, 2, 4)], [], [
|
|
1115
|
+
(1, 2, 4),
|
|
1116
|
+
(2, 4, 5), (3, 4, 5), (2, 5, 5), (3, 5, 5),
|
|
1117
|
+
(4, 8, 6), (5, 8, 6), (6, 8, 6), (7, 8, 6),
|
|
1118
|
+
(4, 9, 6), (5, 9, 6), (6, 9, 6), (7, 9, 6),
|
|
1119
|
+
(4, 10, 6), (5, 10, 6), (6, 10, 6), (7, 10, 6),
|
|
1120
|
+
(4, 11, 6), (5, 11, 6), (6, 11, 6), (7, 11, 6),
|
|
1121
|
+
], "blank",
|
|
1122
|
+
),
|
|
1123
|
+
(
|
|
1124
|
+
"downscale: exact tile cached",
|
|
1125
|
+
1, [(0, 0, 1)], [(0, 0, 1)], [(0, 0, 1)], "full",
|
|
1126
|
+
),
|
|
1127
|
+
(
|
|
1128
|
+
"downscale: next level tiles partially cached",
|
|
1129
|
+
1, [(0, 0, 1)], [(0, 0, 2)], [(0, 0, 1), (0, 0, 2), (0, 1, 2), (1, 0, 2), (1, 1, 2)], "partial",
|
|
1130
|
+
),
|
|
1131
|
+
(
|
|
1132
|
+
"downscale: next level tiles fully cached",
|
|
1133
|
+
1, [(0, 0, 1)], [(0, 0, 2), (0, 1, 2), (1, 0, 2), (1, 1, 2)],
|
|
1134
|
+
[(0, 0, 1), (0, 0, 2), (0, 1, 2), (1, 0, 2), (1, 1, 2)], "full",
|
|
1135
|
+
),
|
|
1136
|
+
(
|
|
1137
|
+
"upscale: missing tile level 1 rescale",
|
|
1138
|
+
-1, [(16, 8, 5)], [], [(16, 8, 5), (8, 4, 4)], "blank",
|
|
1139
|
+
),
|
|
1140
|
+
(
|
|
1141
|
+
"upscale: missing tile level 1 rescale, odd coords",
|
|
1142
|
+
-1, [(15, 7, 5)], [], [(15, 7, 5), (7, 3, 4)], "blank",
|
|
1143
|
+
),
|
|
1144
|
+
(
|
|
1145
|
+
"upscale: missing tile level 1 rescale, multiple tiles",
|
|
1146
|
+
-1, [(15, 6, 5), (16, 6, 5), (15, 7, 5), (16, 7, 5)], [],
|
|
1147
|
+
[(15, 6, 5), (16, 6, 5), (15, 7, 5), (16, 7, 5), (7, 3, 4), (8, 3, 4)], "blank",
|
|
1148
|
+
),
|
|
1149
|
+
(
|
|
1150
|
+
"upscale: tile in level 2",
|
|
1151
|
+
-3, [(15, 7, 5)], [(3, 1, 3)], [(15, 7, 5), (7, 3, 4), (3, 1, 3)], "full",
|
|
1152
|
+
),
|
|
1153
|
+
(
|
|
1154
|
+
"upscale: missing tile level 99 rescale",
|
|
1155
|
+
-99, [(16, 8, 5)], [], [(16, 8, 5), (8, 4, 4), (4, 2, 3), (2, 1, 2), (1, 0, 1), (0, 0, 0)], "blank",
|
|
1156
|
+
),
|
|
1157
|
+
(
|
|
1158
|
+
"upscale: unregular grid, partial match",
|
|
1159
|
+
-2, [(201, 101, 10)], [(78, 40, 8), (79, 40, 8), (79, 39, 8)], [
|
|
1160
|
+
(201, 101, 10), (100, 50, 9),
|
|
1161
|
+
# check all four tiles above 100/50/9
|
|
1162
|
+
(78, 40, 8), (79, 40, 8), (78, 39, 8), (79, 39, 8),
|
|
1163
|
+
], "partial",
|
|
1164
|
+
),
|
|
1165
|
+
(
|
|
1166
|
+
"upscale: unregular grid, multiple tiles, partial match",
|
|
1167
|
+
-2, [(200, 100, 10), (201, 100, 10), (200, 101, 10), (201, 101, 10)],
|
|
1168
|
+
[(78, 40, 8), (79, 40, 8), (79, 39, 8)], [
|
|
1169
|
+
(200, 100, 10), (201, 100, 10), (200, 101, 10), (201, 101, 10),
|
|
1170
|
+
(100, 50, 9),
|
|
1171
|
+
(78, 40, 8), (79, 40, 8), (78, 39, 8), (79, 39, 8),
|
|
1172
|
+
], "partial",
|
|
1173
|
+
),
|
|
1174
|
+
(
|
|
1175
|
+
"upscale: unregular grid",
|
|
1176
|
+
-3, [(200, 100, 10)], [], [
|
|
1177
|
+
(200, 100, 10), (100, 50, 9),
|
|
1178
|
+
# check all four tiles above 100/50/9
|
|
1179
|
+
(78, 40, 8), (79, 40, 8), (78, 39, 8), (79, 39, 8),
|
|
1180
|
+
# check tiles above level 8 for each tile individually.
|
|
1181
|
+
# this is due to the recursive nature of our rescaling algorithm
|
|
1182
|
+
(49, 24, 7),
|
|
1183
|
+
(50, 24, 7),
|
|
1184
|
+
(49, 25, 7),
|
|
1185
|
+
(50, 25, 7),
|
|
1186
|
+
(49, 26, 7),
|
|
1187
|
+
(50, 26, 7),
|
|
1188
|
+
],
|
|
1189
|
+
"blank",
|
|
1190
|
+
),
|
|
1191
|
+
])
|
|
1192
|
+
def test_scaled_tiles(self, name, file_cache, tile_locker, rescale_tiles, tiles, store, expected_load, output):
|
|
1193
|
+
res = [
|
|
1194
|
+
1.40625, # 0
|
|
1195
|
+
0.703125, # 1
|
|
1196
|
+
0.3515625, # 2
|
|
1197
|
+
0.17578125, # 3
|
|
1198
|
+
0.087890625, # 4
|
|
1199
|
+
0.0439453125, # 5
|
|
1200
|
+
0.02197265625, # 6
|
|
1201
|
+
0.010986328125, # 7
|
|
1202
|
+
0.007, # 8 additional resolution to test unregular grids
|
|
1203
|
+
0.0054931640625, # 9
|
|
1204
|
+
0.00274658203125, # 10
|
|
1205
|
+
]
|
|
1206
|
+
grid = TileGrid(SRS(4326), origin='sw', bbox=[-180, -90, 180, 90], res=res)
|
|
1207
|
+
image_opts = ImageOptions(format='image/png', resampling='nearest')
|
|
1208
|
+
tm = TileManager(
|
|
1209
|
+
grid, file_cache, [], 'png',
|
|
1210
|
+
locker=tile_locker,
|
|
1211
|
+
image_opts=image_opts,
|
|
1212
|
+
rescale_tiles=rescale_tiles,
|
|
1213
|
+
)
|
|
1214
|
+
|
|
1215
|
+
if store:
|
|
1216
|
+
colors = set()
|
|
1217
|
+
if output == "partial":
|
|
1218
|
+
colors.add((255, 255, 255))
|
|
1219
|
+
for i, t in enumerate(store):
|
|
1220
|
+
color = (150+i*35, 5+i*35, 5+i*35)
|
|
1221
|
+
colors.add(color)
|
|
1222
|
+
tile = Tile(t, ImageSource(create_tmp_image_buf((256, 256), color=color)))
|
|
1223
|
+
file_cache.store_tile(tile)
|
|
1224
|
+
|
|
1225
|
+
loaded_tiles = tm.load_tile_coords(tiles)
|
|
1226
|
+
assert not is_blank(loaded_tiles)
|
|
1227
|
+
assert len(loaded_tiles) == len(tiles)
|
|
1228
|
+
got_colors = set()
|
|
1229
|
+
for t in loaded_tiles:
|
|
1230
|
+
got_colors.update([c for _, c in t.source.as_image().getcolors()])
|
|
1231
|
+
assert got_colors == colors
|
|
1232
|
+
else:
|
|
1233
|
+
loaded_tiles = tm.load_tile_coords(tiles)
|
|
1234
|
+
assert is_blank(loaded_tiles) == (output == "blank")
|
|
1235
|
+
assert len(loaded_tiles.tiles) == len(tiles)
|
|
1236
|
+
|
|
1237
|
+
assert file_cache.stored_tiles == set(store)
|
|
1238
|
+
assert file_cache.loaded_tiles == counting_set(expected_load)
|
|
1239
|
+
assert file_cache.is_cached_call_count == 0
|
|
1240
|
+
|
|
1241
|
+
|
|
1242
|
+
class TileCacheTestBase(object):
|
|
1243
|
+
cache = None # set by subclasses
|
|
1244
|
+
|
|
1245
|
+
def setup_method(self):
|
|
1246
|
+
self.cache_dir = tempfile.mkdtemp()
|
|
1247
|
+
|
|
1248
|
+
def teardown_method(self):
|
|
1249
|
+
if hasattr(self.cache, 'cleanup'):
|
|
1250
|
+
self.cache.cleanup()
|
|
1251
|
+
if hasattr(self, 'cache_dir') and os.path.exists(self.cache_dir):
|
|
1252
|
+
shutil.rmtree(self.cache_dir)
|
|
1253
|
+
|
|
1254
|
+
|
|
1255
|
+
class TestTileManagerCacheBboxCoverage(TileCacheTestBase):
|
|
1256
|
+
def setup_method(self):
|
|
1257
|
+
TileCacheTestBase.setup_method(self)
|
|
1258
|
+
self.cache = RecordFileCache(self.cache_dir, 'png', coverage=coverage([-50, -50, 50, 50], SRS(4326)))
|
|
1259
|
+
|
|
1260
|
+
def test_load_tiles_in_coverage(self, tile_locker):
|
|
1261
|
+
grid = TileGrid(SRS(4326), bbox=[-180, -90, 180, 90], origin='ul')
|
|
1262
|
+
image_opts = ImageOptions(format='image/png')
|
|
1263
|
+
tm = TileManager(
|
|
1264
|
+
grid, self.cache, [], 'png',
|
|
1265
|
+
locker=tile_locker,
|
|
1266
|
+
image_opts=image_opts,
|
|
1267
|
+
)
|
|
1268
|
+
|
|
1269
|
+
coords = [(2, 1, 2), (36, 7, 6), (18082, 6028, 15)]
|
|
1270
|
+
collection = tm.load_tile_coords(coords)
|
|
1271
|
+
|
|
1272
|
+
# Check that tiles inside of coverage loaded
|
|
1273
|
+
assert all(coord in collection for coord in coords)
|
|
1274
|
+
assert all(t.coord is not None for t in collection)
|
|
1275
|
+
|
|
1276
|
+
all(t.coord in self.cache.stored_tiles for t in collection)
|
|
1277
|
+
assert self.cache.loaded_tiles == counting_set(coords)
|
|
1278
|
+
assert self.cache.is_cached_call_count == 0
|
|
1279
|
+
|
|
1280
|
+
def test_empty_tiles_outside_coverage(self, tile_locker):
|
|
1281
|
+
grid = TileGrid(SRS(4326), bbox=[-180, -90, 180, 90], origin='ul')
|
|
1282
|
+
image_opts = ImageOptions(format='image/png')
|
|
1283
|
+
tm = TileManager(
|
|
1284
|
+
grid, self.cache, [], 'png',
|
|
1285
|
+
locker=tile_locker,
|
|
1286
|
+
image_opts=image_opts,
|
|
1287
|
+
)
|
|
1288
|
+
|
|
1289
|
+
coords = [(0, 1, 1), (33, 5, 5), (19449, 3638, 15)]
|
|
1290
|
+
collection = tm.load_tile_coords(coords)
|
|
1291
|
+
|
|
1292
|
+
# Check that tiles did not load
|
|
1293
|
+
assert collection.blank
|
|
1294
|
+
assert all(t.coord is None for t in collection)
|
|
1295
|
+
|
|
1296
|
+
assert self.cache.stored_tiles == set([])
|
|
1297
|
+
assert self.cache.loaded_tiles == counting_set([None for _ in coords])
|
|
1298
|
+
|
|
1299
|
+
|
|
1300
|
+
# From: https://www.kaggle.com/datasets/chapagain/country-state-geo-location
|
|
1301
|
+
boundary_geojson = (
|
|
1302
|
+
b"""
|
|
1303
|
+
{"type":"FeatureCollection","features":[
|
|
1304
|
+
{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-155.54211,19.08348],[-155.68817,18.91619],[-155.93665,19.05939],[-155.90806,19.33888],[-156.07347,19.70294],[-156.02368,19.81422],[-155.85008,19.97729],[-155.91907,20.17395],[-155.86108,20.26721],[-155.78505,20.2487],[-155.40214,20.07975],[-155.22452,19.99302],[-155.06226,19.8591],[-154.80741,19.50871],[-154.83147,19.45328],[-155.22217,19.23972],[-155.54211,19.08348]]],[[[-156.07926,20.64397],[-156.41445,20.57241],[-156.58673,20.783],[-156.70167,20.8643],[-156.71055,20.92676],[-156.61258,21.01249],[-156.25711,20.91745],[-155.99566,20.76404],[-156.07926,20.64397]]],[[[-156.75824,21.17684],[-156.78933,21.06873],[-157.32521,21.09777],[-157.25027,21.21958],[-156.75824,21.17684]]],[[[-157.65283,21.32217],[-157.70703,21.26442],[-157.7786,21.27729],[-158.12667,21.31244],[-158.2538,21.53919],[-158.29265,21.57912],[-158.0252,21.71696],[-157.94161,21.65272],[-157.65283,21.32217]]],[[[-159.34512,21.982],[-159.46372,21.88299],[-159.80051,22.06533],[-159.74877,22.1382],[-159.5962,22.23618],[-159.36569,22.21494],[-159.34512,21.982]]],[[[-94.81758,49.38905],[-94.64,48.84],[-94.32914,48.67074],[-93.63087,48.60926],[-92.61,48.45],[-91.64,48.14],[-90.83,48.27],[-89.6,48.01],[-89.272917,48.019808],[-88.378114,48.302918],[-87.439793,47.94],[-86.461991,47.553338],[-85.652363,47.220219],[-84.87608,46.900083],[-84.779238,46.637102],[-84.543749,46.538684],[-84.6049,46.4396],[-84.3367,46.40877],[-84.14212,46.512226],[-84.091851,46.275419],[-83.890765,46.116927],[-83.616131,46.116927],[-83.469551,45.994686],[-83.592851,45.816894],[-82.550925,45.347517],[-82.337763,44.44],[-82.137642,43.571088],[-82.43,42.98],[-82.9,42.43],[-83.12,42.08],[-83.142,41.975681],[-83.02981,41.832796],[-82.690089,41.675105],[-82.439278,41.675105],[-81.277747,42.209026],[-80.247448,42.3662],[-78.939362,42.863611],[-78.92,42.965],[-79.01,43.27],[-79.171674,43.466339],[-78.72028,43.625089],[-77.737885,43.629056],[-76.820034,43.628784],[-76.5,44.018459],[-76.375,44.09631],[-75.31821,44.81645],[-74.867,45.00048],[-73.34783,45.00738],[-71.50506,45.0082],[-71.405,45.255],[-71.08482,45.30524],[-70.66,45.46],[-70.305,45.915],[-69.99997,46.69307],[-69.237216,47.447781],[-68.905,47.185],[-68.23444,47.35486],[-67.79046,47.06636],[-67.79134,45.70281],[-67.13741,45.13753],[-66.96466,44.8097],[-68.03252,44.3252],[-69.06,43.98],[-70.11617,43.68405],[-70.645476,43.090238],[-70.81489,42.8653],[-70.825,42.335],[-70.495,41.805],[-70.08,41.78],[-70.185,42.145],[-69.88497,41.92283],[-69.96503,41.63717],[-70.64,41.475],[-71.12039,41.49445],[-71.86,41.32],[-72.295,41.27],[-72.87643,41.22065],[-73.71,40.931102],[-72.24126,41.11948],[-71.945,40.93],[-73.345,40.63],[-73.982,40.628],[-73.952325,40.75075],[-74.25671,40.47351],[-73.96244,40.42763],[-74.17838,39.70926],[-74.90604,38.93954],[-74.98041,39.1964],[-75.20002,39.24845],[-75.52805,39.4985],[-75.32,38.96],[-75.071835,38.782032],[-75.05673,38.40412],[-75.37747,38.01551],[-75.94023,37.21689],[-76.03127,37.2566],[-75.72205,37.93705],[-76.23287,38.319215],[-76.35,39.15],[-76.542725,38.717615],[-76.32933,38.08326],[-76.989998,38.239992],[-76.30162,37.917945],[-76.25874,36.9664],[-75.9718,36.89726],[-75.86804,36.55125],[-75.72749,35.55074],[-76.36318,34.80854],[-77.397635,34.51201],[-78.05496,33.92547],[-78.55435,33.86133],[-79.06067,33.49395],[-79.20357,33.15839],[-80.301325,32.509355],[-80.86498,32.0333],[-81.33629,31.44049],[-81.49042,30.72999],[-81.31371,30.03552],[-80.98,29.18],[-80.535585,28.47213],[-80.53,28.04],[-80.056539,26.88],[-80.088015,26.205765],[-80.13156,25.816775],[-80.38103,25.20616],[-80.68,25.08],[-81.17213,25.20126],[-81.33,25.64],[-81.71,25.87],[-82.24,26.73],[-82.70515,27.49504],[-82.85526,27.88624],[-82.65,28.55],[-82.93,29.1],[-83.70959,29.93656],[-84.1,30.09],[-85.10882,29.63615],[-85.28784,29.68612],[-85.7731,30.15261],[-86.4,30.4],[-87.53036,30.27433],[-88.41782,30.3849],[-89.18049,30.31598],[-89.593831,30.159994],[-89.413735,29.89419],[-89.43,29.48864],[-89.21767,29.29108],[-89.40823,29.15961],[-89.77928,29.30714],[-90.15463,29.11743],[-90.880225,29.148535],[-91.626785,29.677],[-92.49906,29.5523],[-93.22637,29.78375],[-93.84842,29.71363],[-94.69,29.48],[-95.60026,28.73863],[-96.59404,28.30748],[-97.14,27.83],[-97.37,27.38],[-97.38,26.69],[-97.33,26.21],[-97.14,25.87],[-97.53,25.84],[-98.24,26.06],[-99.02,26.37],[-99.3,26.84],[-99.52,27.54],[-100.11,28.11],[-100.45584,28.69612],[-100.9576,29.38071],[-101.6624,29.7793],[-102.48,29.76],[-103.11,28.97],[-103.94,29.27],[-104.45697,29.57196],[-104.70575,30.12173],[-105.03737,30.64402],[-105.63159,31.08383],[-106.1429,31.39995],[-106.50759,31.75452],[-108.24,31.754854],[-108.24194,31.34222],[-109.035,31.34194],[-111.02361,31.33472],[-113.30498,32.03914],[-114.815,32.52528],[-114.72139,32.72083],[-115.99135,32.61239],[-117.12776,32.53534],[-117.295938,33.046225],[-117.944,33.621236],[-118.410602,33.740909],[-118.519895,34.027782],[-119.081,34.078],[-119.438841,34.348477],[-120.36778,34.44711],[-120.62286,34.60855],[-120.74433,35.15686],[-121.71457,36.16153],[-122.54747,37.55176],[-122.51201,37.78339],[-122.95319,38.11371],[-123.7272,38.95166],[-123.86517,39.76699],[-124.39807,40.3132],[-124.17886,41.14202],[-124.2137,41.99964],[-124.53284,42.76599],[-124.14214,43.70838],[-124.020535,44.615895],[-123.89893,45.52341],[-124.079635,46.86475],[-124.39567,47.72017],[-124.68721,48.184433],[-124.566101,48.379715],[-123.12,48.04],[-122.58736,47.096],[-122.34,47.36],[-122.5,48.18],[-122.84,49],[-120,49],[-117.03121,49],[-116.04818,49],[-113,49],[-110.05,49],[-107.05,49],[-104.04826,48.99986],[-100.65,49],[-97.22872,49.0007],[-95.15907,49],[-95.15609,49.38425],[-94.81758,49.38905]]],[[[-153.006314,57.115842],[-154.00509,56.734677],[-154.516403,56.992749],[-154.670993,57.461196],[-153.76278,57.816575],[-153.228729,57.968968],[-152.564791,57.901427],[-152.141147,57.591059],[-153.006314,57.115842]]],[[[-165.579164,59.909987],[-166.19277,59.754441],[-166.848337,59.941406],[-167.455277,60.213069],[-166.467792,60.38417],[-165.67443,60.293607],[-165.579164,59.909987]]],[[[-171.731657,63.782515],[-171.114434,63.592191],[-170.491112,63.694975],[-169.682505,63.431116],[-168.689439,63.297506],[-168.771941,63.188598],[-169.52944,62.976931],[-170.290556,63.194438],[-170.671386,63.375822],[-171.553063,63.317789],[-171.791111,63.405846],[-171.731657,63.782515]]],[[[-155.06779,71.147776],[-154.344165,70.696409],[-153.900006,70.889989],[-152.210006,70.829992],[-152.270002,70.600006],[-150.739992,70.430017],[-149.720003,70.53001],[-147.613362,70.214035],[-145.68999,70.12001],[-144.920011,69.989992],[-143.589446,70.152514],[-142.07251,69.851938],[-140.985988,69.711998],[-140.992499,66.000029],[-140.99777,60.306397],[-140.012998,60.276838],[-139.039,60.000007],[-138.34089,59.56211],[-137.4525,58.905],[-136.47972,59.46389],[-135.47583,59.78778],[-134.945,59.27056],[-134.27111,58.86111],[-133.355549,58.410285],[-132.73042,57.69289],[-131.70781,56.55212],[-130.00778,55.91583],[-129.979994,55.284998],[-130.53611,54.802753],[-131.085818,55.178906],[-131.967211,55.497776],[-132.250011,56.369996],[-133.539181,57.178887],[-134.078063,58.123068],[-135.038211,58.187715],[-136.628062,58.212209],[-137.800006,58.499995],[-139.867787,59.537762],[-140.825274,59.727517],[-142.574444,60.084447],[-143.958881,59.99918],[-145.925557,60.45861],[-147.114374,60.884656],[-148.224306,60.672989],[-148.018066,59.978329],[-148.570823,59.914173],[-149.727858,59.705658],[-150.608243,59.368211],[-151.716393,59.155821],[-151.859433,59.744984],[-151.409719,60.725803],[-150.346941,61.033588],[-150.621111,61.284425],[-151.895839,60.727198],[-152.57833,60.061657],[-154.019172,59.350279],[-153.287511,58.864728],[-154.232492,58.146374],[-155.307491,57.727795],[-156.308335,57.422774],[-156.556097,56.979985],[-158.117217,56.463608],[-158.433321,55.994154],[-159.603327,55.566686],[-160.28972,55.643581],[-161.223048,55.364735],[-162.237766,55.024187],[-163.069447,54.689737],[-164.785569,54.404173],[-164.942226,54.572225],[-163.84834,55.039431],[-162.870001,55.348043],[-161.804175,55.894986],[-160.563605,56.008055],[-160.07056,56.418055],[-158.684443,57.016675],[-158.461097,57.216921],[-157.72277,57.570001],[-157.550274,58.328326],[-157.041675,58.918885],[-158.194731,58.615802],[-158.517218,58.787781],[-159.058606,58.424186],[-159.711667,58.93139],[-159.981289,58.572549],[-160.355271,59.071123],[-161.355003,58.670838],[-161.968894,58.671665],[-162.054987,59.266925],[-161.874171,59.633621],[-162.518059,59.989724],[-163.818341,59.798056],[-164.662218,60.267484],[-165.346388,60.507496],[-165.350832,61.073895],[-166.121379,61.500019],[-165.734452,62.074997],[-164.919179,62.633076],[-164.562508,63.146378],[-163.753332,63.219449],[-163.067224,63.059459],[-162.260555,63.541936],[-161.53445,63.455817],[-160.772507,63.766108],[-160.958335,64.222799],[-161.518068,64.402788],[-160.777778,64.788604],[-161.391926,64.777235],[-162.45305,64.559445],[-162.757786,64.338605],[-163.546394,64.55916],[-164.96083,64.446945],[-166.425288,64.686672],[-166.845004,65.088896],[-168.11056,65.669997],[-166.705271,66.088318],[-164.47471,66.57666],[-163.652512,66.57666],[-163.788602,66.077207],[-161.677774,66.11612],[-162.489715,66.735565],[-163.719717,67.116395],[-164.430991,67.616338],[-165.390287,68.042772],[-166.764441,68.358877],[-166.204707,68.883031],[-164.430811,68.915535],[-163.168614,69.371115],[-162.930566,69.858062],[-161.908897,70.33333],[-160.934797,70.44769],[-159.039176,70.891642],[-158.119723,70.824721],[-156.580825,71.357764],[-155.06779,71.147776]]]]}}
|
|
1305
|
+
]}
|
|
1306
|
+
""".strip()
|
|
1307
|
+
)
|
|
1308
|
+
|
|
1309
|
+
|
|
1310
|
+
class TestTileManagerCacheGeojsonCoverage(TileCacheTestBase):
|
|
1311
|
+
def setup_method(self):
|
|
1312
|
+
TileCacheTestBase.setup_method(self)
|
|
1313
|
+
|
|
1314
|
+
with TempFile() as tf:
|
|
1315
|
+
with open(tf, 'wb') as f:
|
|
1316
|
+
f.write(boundary_geojson)
|
|
1317
|
+
conf = {'datasource': tf, 'srs': 'EPSG:4326'}
|
|
1318
|
+
self.cache = RecordFileCache(self.cache_dir, 'png', coverage=load_coverage(conf))
|
|
1319
|
+
|
|
1320
|
+
def test_load_tiles_in_coverage(self, tile_locker):
|
|
1321
|
+
grid = TileGrid(SRS(4326), bbox=[-180, -90, 180, 90], origin='ul')
|
|
1322
|
+
image_opts = ImageOptions(format='image/png')
|
|
1323
|
+
tm = TileManager(
|
|
1324
|
+
grid, self.cache, [], 'png',
|
|
1325
|
+
locker=tile_locker,
|
|
1326
|
+
image_opts=image_opts,
|
|
1327
|
+
)
|
|
1328
|
+
|
|
1329
|
+
coords = [(3, 2, 4), (11, 9, 6), (17, 11, 7), (359, 325, 11), (2996, 2513, 14), (22923, 20919, 17)]
|
|
1330
|
+
collection = tm.load_tile_coords(coords)
|
|
1331
|
+
|
|
1332
|
+
# Check that tiles inside of coverage loaded
|
|
1333
|
+
assert all(coord in collection for coord in coords)
|
|
1334
|
+
assert all(t.coord is not None for t in collection)
|
|
1335
|
+
|
|
1336
|
+
all(t.coord in self.cache.stored_tiles for t in collection)
|
|
1337
|
+
assert self.cache.loaded_tiles == counting_set(coords)
|
|
1338
|
+
|
|
1339
|
+
def test_empty_tiles_outside_coverage(self, tile_locker):
|
|
1340
|
+
grid = TileGrid(SRS(4326), bbox=[-180, -90, 180, 90], origin='ul')
|
|
1341
|
+
image_opts = ImageOptions(format='image/png')
|
|
1342
|
+
tm = TileManager(
|
|
1343
|
+
grid, self.cache, [], 'png',
|
|
1344
|
+
locker=tile_locker,
|
|
1345
|
+
image_opts=image_opts,
|
|
1346
|
+
)
|
|
1347
|
+
|
|
1348
|
+
coords = [(3, 3, 4), (5, 3, 4), (8, 11, 6), (19, 11, 7), (38, 25, 8), (359, 328, 11), (22922, 20922, 17)]
|
|
1349
|
+
collection = tm.load_tile_coords(coords)
|
|
1350
|
+
|
|
1351
|
+
# Check that tiles did not load
|
|
1352
|
+
assert collection.blank
|
|
1353
|
+
assert all(t.coord is None for t in collection)
|
|
1354
|
+
|
|
1355
|
+
assert self.cache.stored_tiles == set([])
|
|
1356
|
+
assert self.cache.loaded_tiles == counting_set([None for _ in coords])
|