[GIS] How to lock a WMS layer to scale in QGIS

printingqgiswms

I am making a A0 size poster map and need a very detailed basemap. Is there a way to lock the OpenStreetMap basemap to a certain scale/zoom so that it would be very detailed in the final print map? I can only get it to print in a big scale which simplifies the map a lot. The area I need to map is fairly big but I need it to still be detailed. I'm using QGIS but have access to ArcGis Pro as well.

Best Answer

I struggled around with the same problem: always getting unreadable text when printing WMS layers to poster format in QGIS.

Even though the reduction of the layout resolution (i.e. 150DPI) helps sometimes, I found a really nice solution by using the fantastic MapProxy web map proxy server.

Therefor I did some minor adjustments to enable an optional WMS-URL parameter called "minres", which locks the resolution to a specific value.

Here are my changes to 3 of the MapProxy Python files:

/site-packages/mapproxy/grid.py

    ...

    def get_affected_tiles(self, bbox, size, req_srs=None, minres=0): # added minres 

        src_bbox, level = self.get_affected_bbox_and_level(bbox, size, req_srs=req_srs, minres=minres) # added minres
        return self.get_affected_level_tiles(src_bbox, level)

    def get_affected_bbox_and_level(self, bbox, size, req_srs=None, minres=0): # added minres
        if req_srs and req_srs != self.srs:
            src_bbox = req_srs.transform_bbox_to(self.srs, bbox)
        else:
            src_bbox = bbox

        if not bbox_intersects(self.bbox, src_bbox):
            raise NoTiles()

        res = get_resolution(src_bbox, size)

        ## added code for minres
        if res < minres:
            res = minres
        ##

        level = self.closest_level(res)

        if res > self.resolutions[0]*self.max_shrink_factor:
            raise NoTiles()

        return src_bbox, level
    ...

/site-packages/mapproxy/layer.py

    ...

    def _image(self, query):
        ### added code for minres
        if not hasattr(query,'minres'):
            query.minres = 0
        ###
        try:
            src_bbox, tile_grid, affected_tile_coords = \
                self.grid.get_affected_tiles(query.bbox, query.size,
                                             req_srs=query.srs,minres=query.minres) # added "minres"
        except NoTiles:
            raise BlankImage()
        except GridError as ex:
            raise MapBBOXError(ex.args[0])
    ...

/site-packages/mapproxy/service/wms.py

    ...

    def map(self, map_request):
        self.check_map_request(map_request)

        params = map_request.params
        query = MapQuery(params.bbox, params.size, SRS(params.srs), params.format)
        ### added code for minres
        query.minres = int(map_request.params.get('minres','0').replace('?',''))
        ###

        if map_request.params.get('tiled', 'false').lower() == 'true':
            query.tiled_only = True
        orig_query = query

     ...

     def capabilities(self, map_request):
        # TODO: debug layer
        # if '__debug__' in map_request.params:
        #     layers = self.layers.values()
        # else:
        #     layers = [layer for name, layer in iteritems(self.layers)
        #               if name != '__debug__']

        if map_request.params.get('tiled', 'false').lower() == 'true':
            tile_layers = self.tile_layers.values()
        else:
            tile_layers = []

        service = self._service_md(map_request)

        ### added code for minres
        if 'minres' in map_request.raw_params:
            service['url'] = service['url'] + '?minres=' + map_request.raw_params['minres']
        ###

        root_layer = self.authorized_capability_layers(map_request.http.environ)

     ...

After all changes we can add "minres=xx" to the WMS URL to lock the layer resolution in QGIS:

http://localhost:8080/mapproxy/service_name/service?minres=10

Please don't ask me about the right "minres" value ... I always have to try different values to achieve the best result. :-)