Trying to configure reprojection of Tile Source to Webmercator using Mapproxy
I have a source of tiles served from a WMTS, these tiles are cached in a prpriatare scale format and is not following any of the standards as GLOBAL_WEBMERCATOR.
I have a client that wants to fetch tiles, but can only do so in WEBMERCATOR. So i thought i could use use Mapproxy to reproject the tiles.
The given Capabilities file (some info omitted):
BaseMap
BaseMap
-11655.832400001585 6258393.602399997
3770690.9673999995 1.1255899910499997E7
Default Style
default
image/png
default028mm
TileMatrix using 0.28mm
The tile matrix set that has scale values calculated based on the dpi defined by OGC specification (dpi assumes 0.28mm as the physical distance of a pixel).
default028mm
urn:ogc:def:crs:EPSG::3857
0
1.7471320750895232E7
-2.0037508342789248E7 2.0037508342789248E7
256
256
20
12
1
8735660.375447616
-2.0037508342789248E7 2.0037508342789248E7
256
256
39
23
2
4367830.187723808
-2.0037508342789248E7 2.0037508342789248E7
256
256
77
45
3
2183915.093861904
-2.0037508342789248E7 2.0037508342789248E7
256
256
153
89
4
1091957.5469309513
-2.0037508342789248E7 2.0037508342789248E7
256
256
305
177
5
545978.773465476
-2.0037508342789248E7 2.0037508342789248E7
256
256
609
353
6
272989.38673273794
-2.0037508342789248E7 2.0037508342789248E7
256
256
1217
705
7
136494.693366369
-2.0037508342789248E7 2.0037508342789248E7
256
256
2434
1409
8
68247.34668318449
-2.0037508342789248E7 2.0037508342789248E7
256
256
4867
2817
9
34123.67334159225
-2.0037508342789248E7 2.0037508342789248E7
256
256
9734
5634
10
17061.836670796125
-2.0037508342789248E7 2.0037508342789248E7
256
256
19468
11267
11
8530.918335398062
-2.0037508342789248E7 2.0037508342789248E7
256
256
38935
22534
12
4265.459167699031
-2.0037508342789248E7 2.0037508342789248E7
256
256
77869
45067
i devised a mapproxy config:
services:
demo:
wmts:
layers:
- name: "basemap"
title: "basemap"
sources: [basemap_cache]
caches:
basemap_cache:
cache:
type: file
directory: /cache_data/basemap
sources: basemap_wmts]
grids: [GLOBAL_WEBMERCATOR]
sources:
basemap_wmts:
type: tile
url: http://foobar/%(z)s/%(x)s/%(y)s.png
grid: custom
grids:
custom:
base: GLOBAL_MERCATOR
origin: 'nw'
bbox: [-11655.832400001247, 6258393.6023999965, 3770690.9674, 11255899.910499997]
bbox_srs: 'EPSG::3857'
res: [
# res level scale @90.7 DPI
4891.9698102507, # 0 17471320.75089523
2445.9849051253, # 1 8735660.37544762
1222.9924525627, # 2 4367830.18772381
611.4962262813, # 3 2183915.09386190
305.7481131407, # 4 1091957.54693095
152.8740565703, # 5 545978.77346548
76.4370282852, # 6 272989.38673274
38.2185141426, # 7 136494.69336637
19.1092570713, # 8 68247.34668318
9.5546285356, # 9 34123.67334159
4.7773142678, # 10 17061.83667080
2.3886571339, # 11 8530.91833540
1.1943285670, # 12 4265.45916770
]
The problem here is that its not working, im getting exceptions in the mapproxy logs.
First it doesnt like my bounding box i think:
2025-04-08 08:56:01,117 - INFO - mapproxy.system - using pyproj for coordinate transformation
Traceback (most recent call last):
File "/mapproxy/app.py", line 26, in
application = make_wsgi_app(r'/mapproxy/config/mapproxy.yaml', reloader=True)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/site-packages/mapproxy/wsgiapp.py", line 53, in make_wsgi_app
return ReloaderApp(services_conf, make_app)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/site-packages/mapproxy/wsgiapp.py", line 76, in __init__
self.app = make_app_func()
^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/site-packages/mapproxy/wsgiapp.py", line 52, in make_app
return make_wsgi_app(services_conf=services_conf, debug=debug, reloader=False)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/site-packages/mapproxy/wsgiapp.py", line 57, in make_wsgi_app
services = conf.configured_services()
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/site-packages/mapproxy/config/loader.py", line 217, in configured_services
return self.services.services()
^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/site-packages/mapproxy/config/loader.py", line 2168, in services
new_services = creator(service_conf or {})
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/site-packages/mapproxy/config/loader.py", line 2331, in demo_service
lyr = layer_conf.wms_layer()
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/site-packages/mapproxy/util/py.py", line 73, in wrapper
cache[key] = func(self, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/site-packages/mapproxy/config/loader.py", line 1930, in wms_layer
map_layer = self.context.caches[source_name].map_layer()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/site-packages/mapproxy/util/py.py", line 73, in wrapper
cache[key] = func(self, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/site-packages/mapproxy/config/loader.py", line 1844, in map_layer
for grid, extent, tile_manager in self.caches():
^^^^^^^^^^^^^
File "/usr/lib/python3.12/site-packages/mapproxy/util/py.py", line 73, in wrapper
cache[key] = func(self, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/site-packages/mapproxy/config/loader.py", line 1713, in caches
sources, source_image_opts = self._sources_for_grid(
^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/site-packages/mapproxy/config/loader.py", line 1592, in _sources_for_grid
source = source_conf.source({'format': request_format})
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/site-packages/mapproxy/config/loader.py", line 1064, in source
grid = self.context.grids[grid_name].tile_grid()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/site-packages/mapproxy/util/py.py", line 73, in wrapper
cache[key] = func(self, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/site-packages/mapproxy/config/loader.py", line 309, in tile_grid
grid = tile_grid(
^^^^^^^^^^
File "/usr/lib/python3.12/site-packages/mapproxy/grid.py", line 117, in tile_grid
bbox = grid_bbox(bbox, srs=srs, bbox_srs=bbox_srs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/site-packages/mapproxy/grid.py", line 229, in grid_bbox
bbox = SRS(bbox_srs).transform_bbox_to(srs, bbox)
^^^^^^^^^^^^^
File "/usr/lib/python3.12/site-packages/mapproxy/srs.py", line 117, in SRS
srs = _srs_impl(srs_code)
^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/site-packages/mapproxy/srs.py", line 350, in __init__
epsg_num = get_epsg_num(srs_code)
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/site-packages/mapproxy/srs.py", line 49, in get_epsg_num
epsg_code = int(epsg_code.split(':')[1])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: invalid literal for int() with base 10: ''
Even though its the bounding box defined in the capabilities file.
If i remove the bounding box defined in the Mapproxy configuration i get:
2025-04-08 08:47:55,055 - CRITICAL - mapproxy.wsgiapp - fatal error in wmts for /wmts/basemap/GLOBAL_WEBMERCATOR/5/18/13.png?
Traceback (most recent call last):
File "/usr/lib/python3.12/site-packages/mapproxy/wsgiapp.py", line 155, in __call__
resp = self.handlers[handler_name].handle(req)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/site-packages/mapproxy/service/base.py", line 31, in handle
return handler(parsed_req)
^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/site-packages/mapproxy/service/wmts.py", line 101, in tile
tile = tile_layer.render(request, coverage=limited_to, decorate_img=decorate_img)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/site-packages/mapproxy/service/tile.py", line 314, in render
tile = self.tile_manager.load_tile_coord(tile_coord,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/site-packages/mapproxy/cache/tile.py", line 122, in load_tile_coord
return self.load_tile_coords(
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/site-packages/mapproxy/cache/tile.py", line 151, in load_tile_coords
tiles = self._load_tile_coords(
^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/site-packages/mapproxy/cache/tile.py", line 206, in _load_tile_coords
created_tiles = creator.create_tiles(uncached_tiles)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/site-packages/mapproxy/cache/tile.py", line 373, in create_tiles
created_tiles = self._create_single_tiles(tiles)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/site-packages/mapproxy/cache/tile.py", line 397, in _create_single_tiles
created_tiles.extend(self._create_single_tile(tile))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/site-packages/mapproxy/cache/tile.py", line 415, in _create_single_tile
source = self._query_sources(query)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/site-packages/mapproxy/cache/tile.py", line 455, in _query_sources
return self.sources[0].get_map(query)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/site-packages/mapproxy/source/tile.py", line 68, in get_map
_bbox, grid, tiles = self.grid.get_affected_tiles(query.bbox, query.size)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/site-packages/mapproxy/grid.py", line 520, in get_affected_tiles
src_bbox, level = self.get_affected_bbox_and_level(bbox, size, req_srs=req_srs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/site-packages/mapproxy/grid.py", line 530, in get_affected_bbox_and_level
raise NoTiles()
mapproxy.grid.NoTiles
Which basically just straight up says there is no tile?
I dont really understand how i should configure mapproxy to convert from the defined format from my source, to a more standardized format as WEBMERCATOR.
Is it even possible?