0

I use QGIS 3.20. I created a Python function the changes the extents of a map in a layout. The main map in the layout is linked to an atlas.

I run the function in a Text item (because I do not know where else to put it) and it works correctly when I step though the atlas features but it only sometimes work when I export the atlas.

It is as if the function is sometimes too slow or runs too late and is not taken into account when a page is rendered. It also seems that the order of the items on the layout is important.

Can someone please suggest a better method to run such code in a layout?

The code I used:

from qgis.core import *
from qgis.gui import *

@qgsfunction(args='auto', group='Custom', referenced_columns=[])
def SetMapExtents(map_id, layout_name, detaillayer_name, detailfield_name, detailvalue, feature, parent):
    """
    Sets the extents of the map according the detail feature's extent
    Add this function to a text item e.g. SetMapExtents('MapB',@layout_name,'DetailMaps','Name','1MapB')
    It hide a map if the detail feature cannot be found
    """
    detaillayer = QgsProject.instance().mapLayersByName(detaillayer_name)[0]
    expr = "\"{}\" = '{}'".format(detailfield_name,detailvalue)
    detailfeatures = detaillayer.getFeatures( QgsFeatureRequest(QgsExpression(expr)))
    projectLayoutManager = QgsProject.instance().layoutManager()
    layout = projectLayoutManager.layoutByName(layout_name)
    map = layout.itemById(map_id)
    no_features = True
    for detailfeature in detailfeatures:
        no_features = False
        map.setVisibility(True)
        detailextent=detailfeature.geometry().boundingBox()
        map.setExtent(fit_extent(map.extent(),detailfeature.geometry().boundingBox()))
    if no_features:
        #Set map to invisible
        map.setVisibility(False)
    return
    
def fit_extent(map_extent,detail_extent): #extents must be type QgsRectangle
    """This funciton returns an extent (QgsRectangle) that fits into the map extent preserving the aspect ratioof map_extent
    At least one set of the borders of the detail_extent coincide with the map_extent.
    The other borders are centred in the map_extent borders
    The fitted_extent should not change the size and position of the map
    Note: Only the height of a map changes when the extents change. The width & position stays the same
    The top_left position does not change even if a different map reference point is selected
    """
    det_aspect=detail_extent.height()/detail_extent.width()
    map_aspect=map_extent.height()/map_extent.width()
    xmin=detail_extent.xMinimum()
    ymin=detail_extent.yMinimum()
    xmax=detail_extent.xMaximum()
    ymax=detail_extent.yMaximum()
    if map_aspect<det_aspect: # Want to put a map into an layoutitem that is too wide
        fact=det_aspect/map_aspect
        adj=detail_extent.width()*(fact-1)/2
        xmin -= adj
        xmax += adj
    else: # Want to put a map into an layoutitem that is too high
        fact=map_aspect/det_aspect
        adj=detail_extent.height()*(fact-1)/2
        ymin -= adj
        ymax += adj
    fitted_extent = QgsRectangle(xmin,ymin,xmax,ymax)
    return fitted_extent #QgsRectangle
1
  • 1
    I think we need to see your code
    – Ian Turton
    Sep 24 '21 at 17:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.