I needed a bit more customisation than what Monticolo's answer would allow, so this is what I ended up doing. For each page in the atlas, defined by the detail map, find the corresponding polygon in the overview data set, set the extent of the overview map using the overview polygon. (And in case the overview polygon does not exist, use the same extent on both maps)
I do not want the shape of the overview map to change, therefore I am adjusting the bounding box to fit into the size of the defined overview map.
@qgsfunction(args='auto', group='Custom')
def getextent(feature,parent):
overviewlayername='punkt_oversikt'
layoutname='detaljkart-atlas'
searchfield='navn'
overviewmap='Hovedkart'
detailmap='Detaljkart'
projectInstance= QgsProject.instance()
projectLayoutManager = projectInstance.layoutManager()
layout=projectLayoutManager.layoutByName(layoutname)
atl=layout.atlas()
pg=atl.nameForPage(atl.currentFeatureNumber()) # The same as returned by @atlas_pagename
ovLayer = QgsProject.instance().mapLayersByName(overviewlayername)[0] # The one and only
xpr='"{}" = \'{}\''.format(searchfield,pg) # Search string - same format as used in filter
expr = QgsExpression(xpr)
it = ovLayer.getFeatures( QgsFeatureRequest( expr ) )
n=0
for item in it: # Only one item is to be expected
n+=1
bb=item.geometry().boundingBox()
if n==0:
# In case no items are found, use the same extent for both maps
detmap=layout.itemById(detailmap)
bb=detmap.extent()
xmax=bb.xMaximum()
ymax=bb.yMaximum()
xmin=bb.xMinimum()
ymin=bb.yMinimum()
aspect=bb.width()/bb.height()
ovmap=layout.itemById(overviewmap)
ovbb=ovmap.extent()
ovaspect=ovbb.width()/ovbb.height()
if ovaspect>aspect: # Want to put a map into an layoutitem that is too wide
fact=ovaspect/aspect
adj=bb.width()*(fact-1)/2
xmin=xmin-adj
xmax=xmax+adj
else: # Want to put a map into an layoutitem that is too high
fact=aspect/ovaspect
adj=bb.height()*(fact-1)/2
ymin=ymin-adj
ymax=ymax+adj
bbox=QgsRectangle(xmin,ymin,xmax,ymax)
ovmap.setExtent(bbox)
return "" # This is called from a textbox -
The function is being called by an invisible textbox. I have to look into a more elegant solution, but that is another question.