[{"data":1,"prerenderedAt":1315},["ShallowReactive",2],{"doc:\u002Fspatial-data-processing-automation\u002Fautomating-atlas-map-series\u002Fconfigure-atlas-coverage-layer-pyqgis":3},{"id":4,"title":5,"body":6,"description":1304,"extension":1305,"meta":1306,"navigation":270,"path":1311,"seo":1312,"stem":1313,"__hash__":1314},"docs\u002Fspatial-data-processing-automation\u002Fautomating-atlas-map-series\u002Fconfigure-atlas-coverage-layer-pyqgis\u002Findex.md","Configure an Atlas Coverage Layer in PyQGIS",{"type":7,"value":8,"toc":1290},"minimark",[9,13,17,26,206,211,233,237,388,426,430,530,542,546,554,641,661,770,774,798,811,887,891,951,983,987,990,999,1009,1013,1081,1092,1096,1155,1159,1171,1175,1190,1200,1206,1212,1222,1235,1245,1255,1259,1286],[10,11,5],"h1",{"id":12},"configure-an-atlas-coverage-layer-in-pyqgis",[14,15,16],"p",{},"An atlas turns one layout into one map per feature: a page per ward, per site, per survey square. The layout work is cartography, but the configuration — which layer drives it, which features are included, in what order, at what scale, saved under what name — is a dozen properties, and setting them from Python is what makes an atlas a scheduled job rather than a click-through.",[14,18,19,20,25],{},"This recipe belongs to ",[21,22,24],"a",{"href":23},"\u002Fspatial-data-processing-automation\u002Fautomating-atlas-map-series\u002F","Automating Atlas Map Series",". It covers pointing an atlas at a coverage layer, filtering and sorting the pages, controlling how each page frames its feature, naming the output files from an expression, and checking a page before committing to a two-hundred-page export.",[14,27,28],{},[29,30,35,39,43,50,67,76,85,91,101,108,111,115,118,122,125,129,134,142,147,151,155,162,166,169,172,175,179,182,186,191,196,198,202],"svg",{"viewBox":31,"role":32,"ariaLabel":33,"xmlns":34},"0 0 760 282","img","Diagram of an atlas taking a coverage layer of five features and producing one page per feature, with the map item extent following each feature in turn","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg",[36,37,38],"title",{},"One coverage feature, one page",[40,41,42],"desc",{},"The coverage layer holds five ward polygons. The atlas iterates them in the configured order, and for each one the layout's map item recentres on that feature at the configured margin or scale. The result is five pages from one layout, with labels and titles that can reference the current feature's attributes.",[44,45],"rect",{"x":46,"y":46,"width":47,"height":48,"fill":49},"0","760","282","#f6f3ea",[51,52,53],"defs",{},[54,55,62],"marker",{"id":56,"viewBox":57,"refX":58,"refY":59,"markerWidth":60,"markerHeight":60,"orient":61},"atlasArrow","0 0 10 10","8","5","7","auto-start-reverse",[63,64],"path",{"d":65,"fill":66},"M0 0 L10 5 L0 10 z","#0f766e",[68,69,75],"text",{"x":70,"y":71,"style":72,"fill":73,"textAnchor":74},"380","28","text-anchor:middle;font-size:14px;font-weight:bold;font-family:sans-serif","#17211d","middle","The layout is designed once; the atlas repeats it",[44,77],{"x":78,"y":79,"width":80,"height":81,"rx":82,"fill":83,"stroke":66,"style":84},"20","56","220","180","10","#eef7f4","stroke-width:2.5",[68,86,90],{"x":87,"y":88,"style":89,"fill":66,"textAnchor":74},"130","82","text-anchor:middle;font-size:12px;font-weight:bold;font-family:sans-serif","coverage layer",[44,92],{"x":93,"y":94,"width":95,"height":96,"rx":97,"fill":98,"stroke":99,"style":100},"44","98","172","24","4","#fffdf7","#59645f","stroke-width:1.2",[68,102,107],{"x":103,"y":104,"style":105,"fill":106},"58","115","font-size:11px;font-family:sans-serif","#2f3b35","North ward",[44,109],{"x":93,"y":110,"width":95,"height":96,"rx":97,"fill":98,"stroke":99,"style":100},"126",[68,112,114],{"x":103,"y":113,"style":105,"fill":106},"143","Riverside ward",[44,116],{"x":93,"y":117,"width":95,"height":96,"rx":97,"fill":98,"stroke":99,"style":100},"154",[68,119,121],{"x":103,"y":120,"style":105,"fill":106},"171","Central ward",[44,123],{"x":93,"y":124,"width":95,"height":96,"rx":97,"fill":98,"stroke":99,"style":100},"182",[68,126,128],{"x":103,"y":127,"style":105,"fill":106},"199","Upland ward",[68,130,133],{"x":87,"y":131,"style":132,"fill":99,"textAnchor":74},"224","text-anchor:middle;font-size:10px;font-family:sans-serif","filtered and sorted by you",[44,135],{"x":136,"y":137,"width":138,"height":139,"rx":82,"fill":140,"stroke":141,"style":84},"288","102","164","88","#eff3ff","#2563eb",[68,143,146],{"x":144,"y":145,"style":89,"fill":141,"textAnchor":74},"370","132","the layout",[68,148,150],{"x":144,"y":117,"style":149,"fill":106,"textAnchor":74},"text-anchor:middle;font-size:11px;font-family:sans-serif","map, title, legend",[68,152,154],{"x":144,"y":153,"style":149,"fill":106,"textAnchor":74},"174","designed once",[44,156],{"x":157,"y":79,"width":158,"height":159,"rx":58,"fill":98,"stroke":160,"style":161},"500","240","42","#15803d","stroke-width:2",[68,163,165],{"x":164,"y":88,"style":149,"fill":106,"textAnchor":74},"620","north.pdf — framed on North",[44,167],{"x":157,"y":168,"width":158,"height":159,"rx":58,"fill":98,"stroke":160,"style":161},"104",[68,170,171],{"x":164,"y":87,"style":149,"fill":106,"textAnchor":74},"riverside.pdf",[44,173],{"x":157,"y":174,"width":158,"height":159,"rx":58,"fill":98,"stroke":160,"style":161},"152",[68,176,178],{"x":164,"y":177,"style":149,"fill":106,"textAnchor":74},"178","central.pdf",[44,180],{"x":157,"y":181,"width":158,"height":159,"rx":58,"fill":98,"stroke":160,"style":161},"200",[68,183,185],{"x":164,"y":184,"style":149,"fill":106,"textAnchor":74},"226","upland.pdf",[187,188],"line",{"x1":158,"y1":189,"x2":48,"y2":189,"stroke":66,"style":190},"146","stroke-width:2;marker-end:url(#atlasArrow)",[187,192],{"x1":193,"y1":87,"x2":194,"y2":195,"stroke":66,"style":190},"452","494","90",[187,197],{"x1":193,"y1":189,"x2":194,"y2":189,"stroke":66,"style":190},[187,199],{"x1":193,"y1":200,"x2":194,"y2":201,"stroke":66,"style":190},"162","210",[68,203,205],{"x":70,"y":204,"style":149,"fill":99,"textAnchor":74},"268","Titles and labels read the current feature, so each page describes itself",[207,208,210],"h2",{"id":209},"prerequisites","Prerequisites",[212,213,214,222,225],"ul",{},[215,216,217,221],"li",{},[218,219,220],"strong",{},"QGIS 3.34 LTR"," (bundled Python 3.12) or newer.",[215,223,224],{},"A project containing a layout with at least one map item, and a polygon or point layer to drive the atlas.",[215,226,227,228,232],{},"Familiarity with layouts — ",[21,229,231],{"href":230},"\u002Fspatial-data-processing-automation\u002Fautomated-map-layout-generation\u002F","Automated Map Layout Generation"," covers building one from scratch.",[207,234,236],{"id":235},"point-the-atlas-at-a-coverage-layer","Point the atlas at a coverage layer",[238,239,244],"pre",{"className":240,"code":241,"language":242,"meta":243,"style":243},"language-python shiki shiki-themes github-dark","from qgis.core import QgsProject\n\nproject = QgsProject.instance()\nlayout = project.layoutManager().layoutByName(\"Ward maps\")\nwards = project.mapLayersByName(\"Wards\")[0]\n\natlas = layout.atlas()\natlas.setEnabled(True)\natlas.setCoverageLayer(wards)\natlas.setHideCoverage(False)\n\nprint(atlas.count(), \"pages\")\n","python","",[245,246,247,265,272,284,302,325,330,341,352,358,369,374],"code",{"__ignoreMap":243},[248,249,251,255,259,262],"span",{"class":187,"line":250},1,[248,252,254],{"class":253},"snl16","from",[248,256,258],{"class":257},"s95oV"," qgis.core ",[248,260,261],{"class":253},"import",[248,263,264],{"class":257}," QgsProject\n",[248,266,268],{"class":187,"line":267},2,[248,269,271],{"emptyLinePlaceholder":270},true,"\n",[248,273,275,278,281],{"class":187,"line":274},3,[248,276,277],{"class":257},"project ",[248,279,280],{"class":253},"=",[248,282,283],{"class":257}," QgsProject.instance()\n",[248,285,287,290,292,295,299],{"class":187,"line":286},4,[248,288,289],{"class":257},"layout ",[248,291,280],{"class":253},[248,293,294],{"class":257}," project.layoutManager().layoutByName(",[248,296,298],{"class":297},"sU2Wk","\"Ward maps\"",[248,300,301],{"class":257},")\n",[248,303,305,308,310,313,316,319,322],{"class":187,"line":304},5,[248,306,307],{"class":257},"wards ",[248,309,280],{"class":253},[248,311,312],{"class":257}," project.mapLayersByName(",[248,314,315],{"class":297},"\"Wards\"",[248,317,318],{"class":257},")[",[248,320,46],{"class":321},"sDLfK",[248,323,324],{"class":257},"]\n",[248,326,328],{"class":187,"line":327},6,[248,329,271],{"emptyLinePlaceholder":270},[248,331,333,336,338],{"class":187,"line":332},7,[248,334,335],{"class":257},"atlas ",[248,337,280],{"class":253},[248,339,340],{"class":257}," layout.atlas()\n",[248,342,344,347,350],{"class":187,"line":343},8,[248,345,346],{"class":257},"atlas.setEnabled(",[248,348,349],{"class":321},"True",[248,351,301],{"class":257},[248,353,355],{"class":187,"line":354},9,[248,356,357],{"class":257},"atlas.setCoverageLayer(wards)\n",[248,359,361,364,367],{"class":187,"line":360},10,[248,362,363],{"class":257},"atlas.setHideCoverage(",[248,365,366],{"class":321},"False",[248,368,301],{"class":257},[248,370,372],{"class":187,"line":371},11,[248,373,271],{"emptyLinePlaceholder":270},[248,375,377,380,383,386],{"class":187,"line":376},12,[248,378,379],{"class":321},"print",[248,381,382],{"class":257},"(atlas.count(), ",[248,384,385],{"class":297},"\"pages\"",[248,387,301],{"class":257},[14,389,390,393,394,397,398,401,402,405,406,409,410,413,414,417,418,421,422,425],{},[218,391,392],{},"Breakdown:"," ",[245,395,396],{},"layoutByName()"," returns ",[245,399,400],{},"None"," when the name does not match exactly, and the resulting ",[245,403,404],{},"AttributeError"," on ",[245,407,408],{},".atlas()"," is the usual first error — print the names from ",[245,411,412],{},"layoutManager().printLayouts()"," if in doubt. ",[245,415,416],{},"setEnabled(True)"," is what turns the layout into an atlas; without it every other property is set and ignored. ",[245,419,420],{},"setHideCoverage()"," controls whether the driving layer is drawn on the map, which is usually wanted for ward boundaries and usually not for an invisible grid of survey squares. ",[245,423,424],{},"count()"," reports how many pages the current configuration produces, and checking it before exporting is the cheapest possible sanity check.",[207,427,429],{"id":428},"filter-and-sort-the-pages","Filter and sort the pages",[238,431,433],{"className":240,"code":432,"language":242,"meta":243,"style":243},"atlas.setFilterFeatures(True)\natlas.setFilterExpression(\"\\\"status\\\" = 'active' AND \\\"population\\\" > 500\")\n\natlas.setSortFeatures(True)\natlas.setSortExpression(\"\\\"ward_name\\\"\")\natlas.setSortAscending(True)\n\nprint(atlas.count(), \"pages after filtering\")\n",[245,434,435,444,475,479,488,506,515,519],{"__ignoreMap":243},[248,436,437,440,442],{"class":187,"line":250},[248,438,439],{"class":257},"atlas.setFilterFeatures(",[248,441,349],{"class":321},[248,443,301],{"class":257},[248,445,446,449,452,455,458,460,463,465,468,470,473],{"class":187,"line":267},[248,447,448],{"class":257},"atlas.setFilterExpression(",[248,450,451],{"class":297},"\"",[248,453,454],{"class":321},"\\\"",[248,456,457],{"class":297},"status",[248,459,454],{"class":321},[248,461,462],{"class":297}," = 'active' AND ",[248,464,454],{"class":321},[248,466,467],{"class":297},"population",[248,469,454],{"class":321},[248,471,472],{"class":297}," > 500\"",[248,474,301],{"class":257},[248,476,477],{"class":187,"line":274},[248,478,271],{"emptyLinePlaceholder":270},[248,480,481,484,486],{"class":187,"line":286},[248,482,483],{"class":257},"atlas.setSortFeatures(",[248,485,349],{"class":321},[248,487,301],{"class":257},[248,489,490,493,495,497,500,502,504],{"class":187,"line":304},[248,491,492],{"class":257},"atlas.setSortExpression(",[248,494,451],{"class":297},[248,496,454],{"class":321},[248,498,499],{"class":297},"ward_name",[248,501,454],{"class":321},[248,503,451],{"class":297},[248,505,301],{"class":257},[248,507,508,511,513],{"class":187,"line":327},[248,509,510],{"class":257},"atlas.setSortAscending(",[248,512,349],{"class":321},[248,514,301],{"class":257},[248,516,517],{"class":187,"line":332},[248,518,271],{"emptyLinePlaceholder":270},[248,520,521,523,525,528],{"class":187,"line":343},[248,522,379],{"class":321},[248,524,382],{"class":257},[248,526,527],{"class":297},"\"pages after filtering\"",[248,529,301],{"class":257},[14,531,532,534,535,538,539,541],{},[218,533,392],{}," The filter is a QGIS expression evaluated against the coverage layer, with field names in double quotes and string literals in single — the usual quoting trap. Both the filter and the sort need their ",[245,536,537],{},"set...Features(True)"," companion, and setting the expression without enabling it is a silent no-op that produces a full, unsorted atlas. Sorting matters more than it looks in a printed document: pages in alphabetical order can be found by a reader, pages in feature-id order cannot. Re-checking ",[245,540,424],{}," after filtering confirms the expression did what you meant rather than excluding everything.",[207,543,545],{"id":544},"control-how-each-page-frames-its-feature","Control how each page frames its feature",[14,547,548,549,553],{},"Framing is a property of the ",[550,551,552],"em",{},"map item",", not the atlas, which is the part that most often confuses people.",[238,555,557],{"className":240,"code":556,"language":242,"meta":243,"style":243},"from qgis.core import QgsLayoutItemMap\n\nmap_item = next(item for item in layout.items() if isinstance(item, QgsLayoutItemMap))\n\nmap_item.setAtlasDriven(True)\nmap_item.setAtlasScalingMode(QgsLayoutItemMap.Auto)\nmap_item.setAtlasMargin(0.10)          # 10 percent around the feature\n",[245,558,559,570,574,608,612,621,626],{"__ignoreMap":243},[248,560,561,563,565,567],{"class":187,"line":250},[248,562,254],{"class":253},[248,564,258],{"class":257},[248,566,261],{"class":253},[248,568,569],{"class":257}," QgsLayoutItemMap\n",[248,571,572],{"class":187,"line":267},[248,573,271],{"emptyLinePlaceholder":270},[248,575,576,579,581,584,587,590,593,596,599,602,605],{"class":187,"line":274},[248,577,578],{"class":257},"map_item ",[248,580,280],{"class":253},[248,582,583],{"class":321}," next",[248,585,586],{"class":257},"(item ",[248,588,589],{"class":253},"for",[248,591,592],{"class":257}," item ",[248,594,595],{"class":253},"in",[248,597,598],{"class":257}," layout.items() ",[248,600,601],{"class":253},"if",[248,603,604],{"class":321}," isinstance",[248,606,607],{"class":257},"(item, QgsLayoutItemMap))\n",[248,609,610],{"class":187,"line":286},[248,611,271],{"emptyLinePlaceholder":270},[248,613,614,617,619],{"class":187,"line":304},[248,615,616],{"class":257},"map_item.setAtlasDriven(",[248,618,349],{"class":321},[248,620,301],{"class":257},[248,622,623],{"class":187,"line":327},[248,624,625],{"class":257},"map_item.setAtlasScalingMode(QgsLayoutItemMap.Auto)\n",[248,627,628,631,634,637],{"class":187,"line":332},[248,629,630],{"class":257},"map_item.setAtlasMargin(",[248,632,633],{"class":321},"0.10",[248,635,636],{"class":257},")          ",[248,638,640],{"class":639},"sjoCn","# 10 percent around the feature\n",[14,642,643,393,645,648,649,652,653,656,657,660],{},[218,644,392],{},[245,646,647],{},"setAtlasDriven(True)"," is what makes the map follow the atlas feature at all; a layout with an atlas configured but no atlas-driven map produces identical pages showing the same extent. The scaling mode decides the framing rule: ",[245,650,651],{},"Auto"," fits each feature with the given margin, so a large ward and a small one are both filled sensibly at different scales; ",[245,654,655],{},"Fixed"," keeps the layout's scale and simply recentres, which is what you want when pages must be comparable; ",[245,658,659],{},"Predefined"," picks the nearest scale from the project's list, which gives varying zoom with round-number scales for a printed series. Choosing between them is a cartographic decision, and the wrong one shows up immediately as pages where the subject is a dot or overflows the frame.",[14,662,663],{},[29,664,667,670,673,676,679,685,688,693,697,700,703,707,711,715,718,720,723,726,729,732,735,738,741,746,749,752,755,758,761,764,767],{"viewBox":665,"role":32,"ariaLabel":666,"xmlns":34},"0 0 760 258","Comparison of the three atlas scaling modes showing automatic fitting with a margin, fixed scale recentring, and predefined scale selection",[36,668,669],{},"Three ways to frame each page",[40,671,672],{},"Automatic scaling fits every feature to the frame with a set margin, so pages vary in scale but each subject fills the page. Fixed scaling keeps one scale and recentres, so pages are directly comparable but small features look lost and large ones do not fit. Predefined scaling picks the nearest round scale from a list, giving variation with printable scale bars.",[44,674],{"x":46,"y":46,"width":47,"height":675,"fill":49},"258",[68,677,678],{"x":70,"y":71,"style":72,"fill":73,"textAnchor":74},"Pick by whether pages must be comparable",[44,680],{"x":681,"y":682,"width":683,"height":684,"rx":82,"fill":83,"stroke":66,"style":84},"16","48","232","188",[68,686,651],{"x":145,"y":687,"style":89,"fill":66,"textAnchor":74},"74",[44,689],{"x":93,"y":195,"width":690,"height":691,"rx":97,"fill":98,"stroke":99,"style":692},"80","60","stroke-width:1.5",[63,694],{"d":695,"fill":66,"fillOpacity":696,"stroke":66,"style":692},"M52 100 L114 100 L114 140 L52 140 Z",0.25,[44,698],{"x":699,"y":195,"width":690,"height":691,"rx":97,"fill":98,"stroke":99,"style":692},"140",[63,701],{"d":702,"fill":66,"fillOpacity":696,"stroke":66,"style":692},"M148 100 L210 100 L210 140 L148 140 Z",[68,704,706],{"x":145,"y":705,"style":149,"fill":106,"textAnchor":74},"176","each feature fills the page",[68,708,710],{"x":145,"y":709,"style":149,"fill":106,"textAnchor":74},"198","scale differs per page",[68,712,714],{"x":145,"y":713,"style":132,"fill":99,"textAnchor":74},"222","good for site plans",[44,716],{"x":717,"y":682,"width":683,"height":684,"rx":82,"fill":140,"stroke":141,"style":84},"264",[68,719,655],{"x":70,"y":687,"style":89,"fill":141,"textAnchor":74},[44,721],{"x":722,"y":195,"width":690,"height":691,"rx":97,"fill":98,"stroke":99,"style":692},"292",[63,724],{"d":725,"fill":141,"fillOpacity":696,"stroke":141,"style":692},"M320 108 L344 108 L344 132 L320 132 Z",[44,727],{"x":728,"y":195,"width":690,"height":691,"rx":97,"fill":98,"stroke":99,"style":692},"388",[63,730],{"d":731,"fill":141,"fillOpacity":696,"stroke":141,"style":692},"M396 96 L472 96 L472 144 L396 144 Z",[68,733,734],{"x":70,"y":705,"style":149,"fill":106,"textAnchor":74},"one scale throughout",[68,736,737],{"x":70,"y":709,"style":149,"fill":106,"textAnchor":74},"pages compare directly",[68,739,740],{"x":70,"y":713,"style":132,"fill":99,"textAnchor":74},"large features overflow",[44,742],{"x":743,"y":682,"width":683,"height":684,"rx":82,"fill":744,"stroke":745,"style":84},"512","#fdf2e2","#b45309",[68,747,659],{"x":748,"y":687,"style":89,"fill":745,"textAnchor":74},"628",[44,750],{"x":751,"y":195,"width":690,"height":691,"rx":97,"fill":98,"stroke":99,"style":692},"540",[63,753],{"d":754,"fill":745,"fillOpacity":696,"stroke":745,"style":692},"M552 102 L608 102 L608 138 L552 138 Z",[44,756],{"x":757,"y":195,"width":690,"height":691,"rx":97,"fill":98,"stroke":99,"style":692},"636",[63,759],{"d":760,"fill":745,"fillOpacity":696,"stroke":745,"style":692},"M646 100 L708 100 L708 140 L646 140 Z",[68,762,763],{"x":748,"y":705,"style":149,"fill":106,"textAnchor":74},"nearest scale from a list",[68,765,766],{"x":748,"y":709,"style":149,"fill":106,"textAnchor":74},"round numbers on the bar",[68,768,769],{"x":748,"y":713,"style":132,"fill":99,"textAnchor":74},"good for printed series",[207,771,773],{"id":772},"name-the-output-files-from-the-data","Name the output files from the data",[238,775,777],{"className":240,"code":776,"language":242,"meta":243,"style":243},"atlas.setFilenameExpression(\"'ward_' || lower(replace(\\\"ward_name\\\", ' ', '_'))\")\n",[245,778,779],{"__ignoreMap":243},[248,780,781,784,787,789,791,793,796],{"class":187,"line":250},[248,782,783],{"class":257},"atlas.setFilenameExpression(",[248,785,786],{"class":297},"\"'ward_' || lower(replace(",[248,788,454],{"class":321},[248,790,499],{"class":297},[248,792,454],{"class":321},[248,794,795],{"class":297},", ' ', '_'))\"",[248,797,301],{"class":257},[14,799,800,802,803,806,807,810],{},[218,801,392],{}," The file name expression is evaluated per page against the coverage feature, so the output becomes ",[245,804,805],{},"ward_north.pdf",", ",[245,808,809],{},"ward_riverside.pdf"," and so on rather than a folder of numbered files nobody can navigate. Normalising the value — lower case, spaces replaced — matters because the result becomes a real file name: a ward called \"St Mary's \u002F Old Town\" produces a path that fails on Windows and confuses everything else. No extension is included; the exporter appends it. Where the coverage layer has a genuinely unique code, use it and put the readable name in the page title instead, which sidesteps the sanitising question entirely.",[14,812,813],{},[29,814,817,820,823,826,833,836,838,844,848,850,853,856,859,863,866,869,873,879,883],{"viewBox":815,"role":32,"ariaLabel":816,"xmlns":34},"0 0 760 244","Diagram showing a coverage feature name passing through an expression that lower-cases it and replaces spaces, producing a safe unique file name",[36,818,819],{},"From a field value to a file name",[40,821,822],{},"A ward name containing capitals, spaces and punctuation is transformed by the file name expression into lower case with spaces replaced, and a unique code appended. The result is a file name that is safe on every platform and cannot collide with another page, which matters because a colliding name means every page overwrites the same file.",[44,824],{"x":46,"y":46,"width":47,"height":825,"fill":49},"244",[51,827,828],{},[54,829,831],{"id":830,"viewBox":57,"refX":58,"refY":59,"markerWidth":60,"markerHeight":60,"orient":61},"fnArrow",[63,832],{"d":65,"fill":66},[68,834,835],{"x":70,"y":71,"style":72,"fill":73,"textAnchor":74},"The expression output becomes a real file name",[44,837],{"x":78,"y":691,"width":181,"height":691,"rx":58,"fill":98,"stroke":99,"style":161},[68,839,843],{"x":840,"y":841,"style":842,"fill":73,"textAnchor":74},"120","84","text-anchor:middle;font-size:11px;font-weight:bold;font-family:sans-serif","the field value",[68,845,847],{"x":840,"y":846,"style":149,"fill":106,"textAnchor":74},"106","St Mary's Old Town",[44,849],{"x":717,"y":691,"width":683,"height":691,"rx":58,"fill":83,"stroke":66,"style":84},[68,851,852],{"x":70,"y":841,"style":842,"fill":66,"textAnchor":74},"the expression",[68,854,855],{"x":70,"y":846,"style":149,"fill":106,"textAnchor":74},"lower, replace spaces, add code",[44,857],{"x":751,"y":691,"width":181,"height":691,"rx":58,"fill":858,"stroke":160,"style":84},"#edf8e9",[68,860,862],{"x":861,"y":841,"style":842,"fill":160,"textAnchor":74},"640","the file",[68,864,865],{"x":861,"y":846,"style":149,"fill":106,"textAnchor":74},"ward st marys w04.pdf",[187,867],{"x1":80,"y1":195,"x2":675,"y2":195,"stroke":66,"style":868},"stroke-width:2;marker-end:url(#fnArrow)",[187,870],{"x1":871,"y1":195,"x2":872,"y2":195,"stroke":66,"style":868},"496","534",[44,874],{"x":840,"y":875,"width":876,"height":877,"rx":58,"fill":744,"stroke":878,"style":161},"160","520","62","#b91c1c",[68,880,882],{"x":70,"y":881,"style":842,"fill":878,"textAnchor":74},"184","a NULL field yields an empty name",[68,884,886],{"x":70,"y":885,"style":149,"fill":106,"textAnchor":74},"206","every page then overwrites the same file, and 200 pages become one",[207,888,890],{"id":889},"preview-one-page-before-exporting-all-of-them","Preview one page before exporting all of them",[238,892,894],{"className":240,"code":893,"language":242,"meta":243,"style":243},"atlas.beginRender()\natlas.first()\nprint(atlas.currentFeatureNumber(), atlas.nameForPage(0))\n\natlas.seekTo(3)\nlayout.refresh()\n# inspect the layout here, or export this single page\n\natlas.endRender()\n",[245,895,896,901,906,918,922,932,937,942,946],{"__ignoreMap":243},[248,897,898],{"class":187,"line":250},[248,899,900],{"class":257},"atlas.beginRender()\n",[248,902,903],{"class":187,"line":267},[248,904,905],{"class":257},"atlas.first()\n",[248,907,908,910,913,915],{"class":187,"line":274},[248,909,379],{"class":321},[248,911,912],{"class":257},"(atlas.currentFeatureNumber(), atlas.nameForPage(",[248,914,46],{"class":321},[248,916,917],{"class":257},"))\n",[248,919,920],{"class":187,"line":286},[248,921,271],{"emptyLinePlaceholder":270},[248,923,924,927,930],{"class":187,"line":304},[248,925,926],{"class":257},"atlas.seekTo(",[248,928,929],{"class":321},"3",[248,931,301],{"class":257},[248,933,934],{"class":187,"line":327},[248,935,936],{"class":257},"layout.refresh()\n",[248,938,939],{"class":187,"line":332},[248,940,941],{"class":639},"# inspect the layout here, or export this single page\n",[248,943,944],{"class":187,"line":343},[248,945,271],{"emptyLinePlaceholder":270},[248,947,948],{"class":187,"line":354},[248,949,950],{"class":257},"atlas.endRender()\n",[14,952,953,393,955,958,959,962,963,966,967,970,971,973,974,977,978,982],{},[218,954,392],{},[245,956,957],{},"beginRender()"," puts the atlas into iteration mode, ",[245,960,961],{},"first()"," and ",[245,964,965],{},"next()"," step through it, and ",[245,968,969],{},"seekTo()"," jumps to a page by index — which is how you check page 4 of 200 without exporting the other 199. Every ",[245,972,957],{}," needs its ",[245,975,976],{},"endRender()",", or the layout stays in atlas mode and subsequent operations behave oddly. Refreshing after seeking is what updates the map item and any label expressions. Rendering a single page this way — with the layout exporter, as described in ",[21,979,981],{"href":980},"\u002Fspatial-data-processing-automation\u002Fautomating-atlas-map-series\u002Fgenerate-atlas-pdf-pyqgis\u002F","Export an Atlas to PDF in PyQGIS"," — turns a slow feedback loop into a fast one while you are getting the framing right.",[207,984,986],{"id":985},"save-the-configuration-into-the-project","Save the configuration into the project",[14,988,989],{},"Everything set here is stored in the layout, so writing the project persists the whole atlas configuration:",[238,991,993],{"className":240,"code":992,"language":242,"meta":243,"style":243},"project.write()\n",[245,994,995],{"__ignoreMap":243},[248,996,997],{"class":187,"line":250},[248,998,992],{"class":257},[14,1000,1001,1003,1004,1008],{},[218,1002,392],{}," That is the point of configuring an atlas from Python rather than exporting from a script every time: the project becomes a reusable artefact a cartographer can open, check and adjust, and the scheduled export is then a two-line job that opens the project and exports. It also means a change to the filter or the sort is a change to one file rather than to a script somebody has to find. The project-level side of this is covered in ",[21,1005,1007],{"href":1006},"\u002Fpyqgis-fundamentals-environment-setup\u002Fworking-with-qgis-projects\u002F","Working with QGIS Projects in PyQGIS",".",[207,1010,1012],{"id":1011},"qgis-version-compatibility","QGIS version compatibility",[1014,1015,1016,1032],"table",{},[1017,1018,1019],"thead",{},[1020,1021,1022,1026,1029],"tr",{},[1023,1024,1025],"th",{},"QGIS version",[1023,1027,1028],{},"Python",[1023,1030,1031],{},"Notes",[1033,1034,1035,1050,1060,1071],"tbody",{},[1020,1036,1037,1041,1044],{},[1038,1039,1040],"td",{},"3.22 LTR",[1038,1042,1043],{},"3.9",[1038,1045,1046,1049],{},[245,1047,1048],{},"layout.atlas()"," with all properties shown.",[1020,1051,1052,1055,1057],{},[1038,1053,1054],{},"3.28 LTR",[1038,1056,1043],{},[1038,1058,1059],{},"Identical.",[1020,1061,1062,1065,1068],{},[1038,1063,1064],{},"3.34 LTR",[1038,1066,1067],{},"3.12",[1038,1069,1070],{},"Baseline for this page.",[1020,1072,1073,1076,1078],{},[1038,1074,1075],{},"3.40 \u002F 3.44",[1038,1077,1067],{},[1038,1079,1080],{},"Identical API; the atlas panel gained interface refinements only.",[14,1082,1083,1084,1087,1088,1091],{},"QGIS 2's ",[245,1085,1086],{},"QgsAtlasComposition"," no longer exists; any snippet mentioning ",[245,1089,1090],{},"composition"," is pre-3.0 and needs rewriting against the layout API rather than patching.",[207,1093,1095],{"id":1094},"troubleshooting","Troubleshooting",[212,1097,1098,1113,1121,1130,1138,1144],{},[215,1099,1100,393,1107,1109,1110,1112],{},[218,1101,1102,405,1104,1008],{},[245,1103,404],{},[245,1105,1106],{},"atlas()",[245,1108,396],{}," returned ",[245,1111,400],{},". Check the exact layout name.",[215,1114,1115,1118,1119,1008],{},[218,1116,1117],{},"Every page shows the same extent."," The map item is not atlas-driven. Set ",[245,1120,647],{},[215,1122,1123,393,1126,1129],{},[218,1124,1125],{},"The filter is ignored.",[245,1127,1128],{},"setFilterFeatures(True)"," was not called, or the expression has quoting reversed.",[215,1131,1132,1137],{},[218,1133,1134,1136],{},[245,1135,424],{}," is zero."," The filter excludes everything, or the coverage layer has no features.",[215,1139,1140,1143],{},[218,1141,1142],{},"File names collide and pages overwrite each other."," The name expression is not unique. Include a code, or fall back to the feature id.",[215,1145,1146,393,1149,1151,1152,1154],{},[218,1147,1148],{},"The layout behaves strangely after a script.",[245,1150,957],{}," without ",[245,1153,976],{},". Always pair them.",[207,1156,1158],{"id":1157},"conclusion","Conclusion",[14,1160,1161,1162,1164,1165,1167,1168,1170],{},"Enable the atlas, point it at a coverage layer, and check ",[245,1163,424],{},". Filter and sort with expressions — remembering that each needs its enabling call — and set the framing on the ",[550,1166,552],{}," with the scaling mode that matches whether pages must be comparable. Drive file names from an expression that produces safe, unique names, preview a page with ",[245,1169,969],{}," before exporting hundreds, and save the project so the configuration is a reusable artefact.",[207,1172,1174],{"id":1173},"frequently-asked-questions","Frequently Asked Questions",[14,1176,1177,1180,1181,1183,1184,1186,1187,1189],{},[218,1178,1179],{},"Can the coverage layer be points rather than polygons?","\nYes. With ",[245,1182,651],{}," scaling a point has no extent to fit, so use ",[245,1185,655],{}," or ",[245,1188,659],{}," scaling, or buffer the points into a coverage layer of areas.",[14,1191,1192,1195,1196,1199],{},[218,1193,1194],{},"How do I put the current feature's name in the title?","\nUse a label with an expression such as ",[245,1197,1198],{},"[% \"ward_name\" %]","; inside an atlas, expressions evaluate against the current feature automatically.",[14,1201,1202,1205],{},[218,1203,1204],{},"Can one project hold several atlases?","\nYes — one per layout, each with its own coverage layer and configuration.",[14,1207,1208,1211],{},[218,1209,1210],{},"How do I exclude features without editing the layer?","\nThe filter expression. It is evaluated per feature and changes nothing in the data.",[14,1213,1214,1217,1218,1221],{},[218,1215,1216],{},"Does the coverage layer have to be visible in the map?","\nNo. ",[245,1219,1220],{},"setHideCoverage(True)"," keeps it driving the atlas without drawing it, which is right for an invisible grid.",[14,1223,1224,1227,1228,962,1231,1234],{},[218,1225,1226],{},"How do I add a page number to each sheet?","\nUse the ",[245,1229,1230],{},"@atlas_featurenumber",[245,1232,1233],{},"@atlas_totalfeatures"," variables in a label expression. They are populated during iteration, so \"Sheet 4 of 42\" needs no extra configuration.",[14,1236,1237,1240,1241,1244],{},[218,1238,1239],{},"Can other layers react to the current atlas feature?","\nYes — a layer filter or a rule-based style can reference ",[245,1242,1243],{},"@atlas_feature"," and its attributes, which is how a series highlights the current ward while still drawing its neighbours in grey.",[14,1246,1247,1250,1251,1254],{},[218,1248,1249],{},"Can I sort by something that is not a field?","\nYes — the sort expression is a full QGIS expression, so ",[245,1252,1253],{},"length(\"ward_name\")"," or a computed value works as well as a field.",[207,1256,1258],{"id":1257},"related","Related",[212,1260,1261,1266,1270,1276,1280],{},[215,1262,1263,1265],{},[21,1264,24],{"href":23}," — the guide this recipe belongs to",[215,1267,1268],{},[21,1269,981],{"href":980},[215,1271,1272],{},[21,1273,1275],{"href":1274},"\u002Fspatial-data-processing-automation\u002Fautomating-atlas-map-series\u002Fexport-atlas-pages-to-individual-pdfs-pyqgis\u002F","Export Atlas Pages to Individual PDFs",[215,1277,1278],{},[21,1279,231],{"href":230},[215,1281,1282],{},[21,1283,1285],{"href":1284},"\u002Fspatial-data-processing-automation\u002Fautomated-map-layout-generation\u002Fadd-map-item-and-set-extent-pyqgis\u002F","Add a Map Item and Set Its Extent in PyQGIS",[1287,1288,1289],"style",{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sjoCn, html code.shiki .sjoCn{--shiki-default:#9AA79F}",{"title":243,"searchDepth":267,"depth":267,"links":1291},[1292,1293,1294,1295,1296,1297,1298,1299,1300,1301,1302,1303],{"id":209,"depth":267,"text":210},{"id":235,"depth":267,"text":236},{"id":428,"depth":267,"text":429},{"id":544,"depth":267,"text":545},{"id":772,"depth":267,"text":773},{"id":889,"depth":267,"text":890},{"id":985,"depth":267,"text":986},{"id":1011,"depth":267,"text":1012},{"id":1094,"depth":267,"text":1095},{"id":1157,"depth":267,"text":1158},{"id":1173,"depth":267,"text":1174},{"id":1257,"depth":267,"text":1258},"Set up an atlas from Python — choose the coverage layer, filter and sort features, control page margins and scale, drive the output file name from an expression, and preview a page before exporting.","md",{"slug":1307,"type":1308,"breadcrumb":1309,"datePublished":1310,"dateModified":1310},"configure-atlas-coverage-layer-pyqgis","article","Atlas Coverage Layer","2026-08-15","\u002Fspatial-data-processing-automation\u002Fautomating-atlas-map-series\u002Fconfigure-atlas-coverage-layer-pyqgis",{"title":5,"description":1304},"spatial-data-processing-automation\u002Fautomating-atlas-map-series\u002Fconfigure-atlas-coverage-layer-pyqgis\u002Findex","47xRI0iINInidJT1fvnsOOZxvMZIx6_ik5s_yGTsdew",1786789586003]