[{"data":1,"prerenderedAt":2971},["ShallowReactive",2],{"doc:\u002Fpyqgis-cartography-visualization":3},{"id":4,"title":5,"body":6,"description":2964,"extension":2965,"meta":2966,"navigation":394,"path":2967,"seo":2968,"stem":2969,"__hash__":2970},"docs\u002Fpyqgis-cartography-visualization\u002Findex.md","PyQGIS Cartography & Data Visualization",{"type":7,"value":8,"toc":2950},"minimark",[9,13,17,25,40,238,243,246,287,290,294,325,362,616,636,648,663,670,674,689,1011,1051,1076,1091,1095,1108,1121,1261,1296,1307,1310,1325,1328,1338,1592,1635,1656,1678,1682,1693,1937,1977,1999,2007,2011,2022,2238,2269,2273,2299,2439,2473,2484,2488,2510,2695,2714,2741,2749,2753,2839,2843,2859,2878,2884,2893,2913,2917,2946],[10,11,5],"h1",{"id":12},"pyqgis-cartography-data-visualization",[14,15,16],"p",{},"Cartography is where geospatial analysis becomes communication. A correctly buffered, reprojected, and joined dataset still says nothing useful until it is rendered with deliberate symbology, classified into meaningful classes, and labeled clearly. PyQGIS exposes the entire QGIS rendering stack to Python, which means every styling decision a cartographer makes through the QGIS interface — fill color, stroke width, graduated breaks, label placement, raster color ramps — can be scripted, parameterized, and reproduced across hundreds of layers without touching a single dialog.",[14,18,19,20,24],{},"This guide is for GIS developers and analysts who already load and process data in PyQGIS and now want to control how it looks. We work from the rendering model upward: first the objects QGIS uses to turn features into pixels, then single-symbol styling, thematic classification, labeling, raster visualization, color ramps, style persistence, and finally pushing styled layers into export-ready deliverables. Every example targets ",[21,22,23],"strong",{},"QGIS 3.34 LTR"," (Python 3.12) and notes where the API diverges on 3.28 LTR (Python 3.9) or the newer 3.40\u002F3.44 line.",[14,26,27,28,31,32,35,36,39],{},"Why script cartography at all, when the QGIS symbology dialogs are excellent? Three reasons recur in production work. ",[21,29,30],{},"Consistency"," — a single function that styles every layer in a project guarantees identical breaks, ramps, and label rules across maps that must compare like-for-like. ",[21,33,34],{},"Scale"," — applying a style to one layer is a five-minute dialog session; applying it to two hundred monthly data drops is a loop. ",[21,37,38],{},"Reproducibility"," — a styling script is version-controllable, reviewable, and rerunnable, so a map produced today can be regenerated identically next year from updated data. Manual styling remains the right choice for exploratory, one-off design; everything repeatable belongs in code.",[41,42,47,48,47,52,47,56,47,47,63,47,72,47,79,47,47,85,47,93,47,98,47,106,47,111,47,113,47,117,47,119,47,123,47,126,47,47,130,47,137,47,143,47,147,47,151,47,155,47,159,47,163,47,167,47,171,47,47,175,47,181,47,186,47,190,47,193,47,197,47,47,201,47,205,47,210,47,213,47,219,47,221],"svg",{"viewBox":43,"role":44,"ariaLabel":45,"xmlns":46},"0 0 880 360","img","PyQGIS rendering pipeline from data source through a renderer and symbol layers to the map canvas and exported map","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","\n  ",[49,50,51],"title",{},"The PyQGIS rendering pipeline",[53,54,55],"desc",{},"A data source feeds a renderer (single-symbol, categorized, or graduated), which selects symbols built from symbol layers and combines them with labeling settings; the result is drawn to the map canvas and exported to a map deliverable.",[57,58],"rect",{"x":59,"y":59,"width":60,"height":61,"fill":62},"0","880","360","#f6f3ea",[57,64],{"x":65,"y":66,"width":66,"height":67,"rx":68,"fill":69,"stroke":70,"style":71},"20","140","80","8","#fffdf7","#17211d","stroke-width:2.5",[73,74,78],"text",{"x":75,"y":76,"fill":70,"style":77},"90","172","text-anchor:middle;font-family:sans-serif;font-size:15px;font-weight:bold","Data source",[73,80,84],{"x":75,"y":81,"fill":82,"style":83},"194","#2f3b35","text-anchor:middle;font-family:sans-serif;font-size:12px","vector \u002F raster",[57,86],{"x":87,"y":88,"width":89,"height":90,"rx":68,"fill":69,"stroke":91,"style":92},"220","40","200","280","#0f766e","stroke-width:3",[73,94,97],{"x":95,"y":96,"fill":91,"style":77},"320","68","Renderer",[57,99],{"x":100,"y":101,"width":102,"height":103,"rx":104,"fill":62,"stroke":82,"style":105},"240","86","160","44","6","stroke-width:2",[73,107,110],{"x":95,"y":108,"fill":82,"style":109},"113","text-anchor:middle;font-family:sans-serif;font-size:12.5px","Single symbol",[57,112],{"x":100,"y":66,"width":102,"height":103,"rx":104,"fill":62,"stroke":82,"style":105},[73,114,116],{"x":95,"y":115,"fill":82,"style":109},"167","Categorized",[57,118],{"x":100,"y":81,"width":102,"height":103,"rx":104,"fill":62,"stroke":82,"style":105},[73,120,122],{"x":95,"y":121,"fill":82,"style":109},"221","Graduated",[57,124],{"x":100,"y":125,"width":102,"height":103,"rx":104,"fill":62,"stroke":82,"style":105},"248",[73,127,129],{"x":95,"y":128,"fill":82,"style":109},"275","Raster \u002F pseudocolor",[57,131],{"x":132,"y":133,"width":134,"height":135,"rx":68,"fill":69,"stroke":136,"style":92},"480","60","180","110","#b45309",[73,138,142],{"x":139,"y":140,"fill":136,"style":141},"570","88","text-anchor:middle;font-family:sans-serif;font-size:14px;font-weight:bold","Symbol layers",[73,144,146],{"x":139,"y":145,"fill":82,"style":83},"112","fill \u002F line \u002F marker",[73,148,150],{"x":139,"y":149,"fill":82,"style":83},"132","color ramps",[73,152,154],{"x":139,"y":153,"fill":82,"style":83},"152","stacked layers",[57,156],{"x":132,"y":157,"width":134,"height":135,"rx":68,"fill":69,"stroke":158,"style":92},"190","#2563eb",[73,160,162],{"x":139,"y":161,"fill":158,"style":141},"218","Labeling",[73,164,166],{"x":139,"y":165,"fill":82,"style":83},"242","PalLayerSettings",[73,168,170],{"x":139,"y":169,"fill":82,"style":83},"262","placement \u002F buffer",[73,172,174],{"x":139,"y":173,"fill":82,"style":83},"282","expressions",[57,176],{"x":177,"y":178,"width":66,"height":67,"rx":68,"fill":179,"stroke":180,"style":92},"720","100","#26322d","#15803d",[73,182,185],{"x":183,"y":149,"fill":184,"style":141},"790","#d9f99d","Map canvas",[73,187,189],{"x":183,"y":188,"fill":184,"style":83},"154","live preview",[57,191],{"x":177,"y":89,"width":66,"height":67,"rx":68,"fill":179,"stroke":192,"style":92},"#22c55e",[73,194,196],{"x":183,"y":195,"fill":184,"style":141},"232","Exported map",[73,198,200],{"x":183,"y":199,"fill":184,"style":83},"254","PDF \u002F PNG",[202,203],"line",{"x1":102,"y1":134,"x2":161,"y2":134,"stroke":70,"style":204},"stroke-width:2.5;marker-end:url(#ar)",[202,206],{"x1":207,"y1":208,"x2":209,"y2":208,"stroke":70,"style":204},"420","115","478",[202,211],{"x1":207,"y1":212,"x2":209,"y2":212,"stroke":70,"style":204},"245",[202,214],{"x1":215,"y1":216,"x2":217,"y2":218,"stroke":70,"style":204},"660","130","718","135",[202,220],{"x1":215,"y1":212,"x2":217,"y2":100,"stroke":70,"style":204},[222,223,224,225,47],"defs",{},"\n    ",[226,227,233,234,224],"marker",{"id":228,"markerWidth":229,"markerHeight":229,"refX":68,"refY":230,"orient":231,"markerUnits":232},"ar","10","3","auto","strokeWidth","\n      ",[235,236],"path",{"d":237,"fill":70},"M0,0 L8,3 L0,6 Z",[239,240,242],"h2",{"id":241},"what-youll-learn","What You'll Learn",[14,244,245],{},"This guide is organized into three focused areas, each drilling into a major part of programmatic cartography:",[247,248,249,265,274],"ul",{},[250,251,252,259,260,264],"li",{},[21,253,254],{},[255,256,258],"a",{"href":257},"\u002Fpyqgis-cartography-visualization\u002Fprogrammatic-layer-styling\u002F","Programmatic Layer Styling"," — building and assigning ",[261,262,263],"code",{},"QgsSymbol"," objects, manipulating symbol layers, setting fill, stroke, marker, and line properties, and applying styles to both vector and raster layers from code.",[250,266,267,273],{},[21,268,269],{},[255,270,272],{"href":271},"\u002Fpyqgis-cartography-visualization\u002Fgraduated-categorized-renderers\u002F","Graduated & Categorized Renderers"," — turning attribute values into thematic maps: choropleths, classification methods (equal interval, quantile, natural breaks\u002FJenks), and category-per-value symbology.",[250,275,276,282,283,286],{},[21,277,278],{},[255,279,281],{"href":280},"\u002Fpyqgis-cartography-visualization\u002Flabeling-and-annotations\u002F","Labeling & Annotations"," — configuring ",[261,284,285],{},"QgsPalLayerSettings",", expression-driven and rule-based labels, text buffers, placement engines, and annotation items.",[14,288,289],{},"By the end you should be able to take an unstyled layer loaded by your processing pipeline and emit a finished, classified, labeled, export-ready map entirely from a script.",[239,291,293],{"id":292},"the-pyqgis-rendering-symbology-model","The PyQGIS Rendering & Symbology Model",[14,295,296,297,300,301,304,305,308,309,312,313,316,317,320,321,324],{},"Before styling anything, it helps to understand the chain of objects QGIS walks every time it draws a layer. A ",[261,298,299],{},"QgsVectorLayer"," owns a ",[21,302,303],{},"renderer"," — an object that decides, for each feature, which symbol to use. The renderer is retrieved with ",[261,306,307],{},"layer.renderer()"," and replaced with ",[261,310,311],{},"layer.setRenderer()",". The simplest renderer, ",[261,314,315],{},"QgsSingleSymbolRenderer",", returns the same symbol for every feature; ",[261,318,319],{},"QgsCategorizedSymbolRenderer"," and ",[261,322,323],{},"QgsGraduatedSymbolRenderer"," pick a symbol based on an attribute.",[14,326,327,328,331,332,334,335,338,339,342,343,346,347,331,350,353,354,357,358,361],{},"A ",[21,329,330],{},"symbol"," (",[261,333,263],{},", with the concrete subclasses ",[261,336,337],{},"QgsFillSymbol",", ",[261,340,341],{},"QgsLineSymbol",", and ",[261,344,345],{},"QgsMarkerSymbol",") is itself a container. It holds an ordered stack of ",[21,348,349],{},"symbol layers",[261,351,352],{},"QgsSymbolLayer","), and each symbol layer contributes one rendering pass. A single fill symbol might stack a ",[261,355,356],{},"QgsSimpleFillSymbolLayer"," underneath a ",[261,359,360],{},"QgsLinePatternFillSymbolLayer"," to produce a hatched polygon with a solid base. This layering is the source of QGIS's cartographic depth, and it is fully addressable from Python.",[363,364,369],"pre",{"className":365,"code":366,"language":367,"meta":368,"style":368},"language-python shiki shiki-themes github-dark","from qgis.core import QgsProject, QgsFillSymbol\n\n# Grab the active polygon layer and inspect its rendering objects.\nlayer = QgsProject.instance().mapLayersByName(\"districts\")[0]\n\nrenderer = layer.renderer()\nprint(\"Renderer type:\", renderer.type())          # e.g. 'singleSymbol'\nsymbol = renderer.symbol()                         # the QgsFillSymbol in use\nprint(\"Symbol layer count:\", symbol.symbolLayerCount())\n\nfor i in range(symbol.symbolLayerCount()):\n    sl = symbol.symbolLayer(i)\n    print(i, sl.layerType(), sl.color().name())\n\n# Replace the whole symbol with a freshly built one.\nnew_symbol = QgsFillSymbol.createSimple({\n    \"color\": \"#cfe8e3\",\n    \"outline_color\": \"#0f766e\",\n    \"outline_width\": \"0.4\",\n})\nrenderer.setSymbol(new_symbol)\nlayer.triggerRepaint()                             # redraw on the canvas\n","python","",[261,370,371,389,396,403,428,433,444,462,476,489,494,512,523,532,537,543,554,569,582,595,601,607],{"__ignoreMap":368},[372,373,375,379,383,386],"span",{"class":202,"line":374},1,[372,376,378],{"class":377},"snl16","from",[372,380,382],{"class":381},"s95oV"," qgis.core ",[372,384,385],{"class":377},"import",[372,387,388],{"class":381}," QgsProject, QgsFillSymbol\n",[372,390,392],{"class":202,"line":391},2,[372,393,395],{"emptyLinePlaceholder":394},true,"\n",[372,397,399],{"class":202,"line":398},3,[372,400,402],{"class":401},"sAwPA","# Grab the active polygon layer and inspect its rendering objects.\n",[372,404,406,409,412,415,419,422,425],{"class":202,"line":405},4,[372,407,408],{"class":381},"layer ",[372,410,411],{"class":377},"=",[372,413,414],{"class":381}," QgsProject.instance().mapLayersByName(",[372,416,418],{"class":417},"sU2Wk","\"districts\"",[372,420,421],{"class":381},")[",[372,423,59],{"class":424},"sDLfK",[372,426,427],{"class":381},"]\n",[372,429,431],{"class":202,"line":430},5,[372,432,395],{"emptyLinePlaceholder":394},[372,434,436,439,441],{"class":202,"line":435},6,[372,437,438],{"class":381},"renderer ",[372,440,411],{"class":377},[372,442,443],{"class":381}," layer.renderer()\n",[372,445,447,450,453,456,459],{"class":202,"line":446},7,[372,448,449],{"class":424},"print",[372,451,452],{"class":381},"(",[372,454,455],{"class":417},"\"Renderer type:\"",[372,457,458],{"class":381},", renderer.type())          ",[372,460,461],{"class":401},"# e.g. 'singleSymbol'\n",[372,463,465,468,470,473],{"class":202,"line":464},8,[372,466,467],{"class":381},"symbol ",[372,469,411],{"class":377},[372,471,472],{"class":381}," renderer.symbol()                         ",[372,474,475],{"class":401},"# the QgsFillSymbol in use\n",[372,477,479,481,483,486],{"class":202,"line":478},9,[372,480,449],{"class":424},[372,482,452],{"class":381},[372,484,485],{"class":417},"\"Symbol layer count:\"",[372,487,488],{"class":381},", symbol.symbolLayerCount())\n",[372,490,492],{"class":202,"line":491},10,[372,493,395],{"emptyLinePlaceholder":394},[372,495,497,500,503,506,509],{"class":202,"line":496},11,[372,498,499],{"class":377},"for",[372,501,502],{"class":381}," i ",[372,504,505],{"class":377},"in",[372,507,508],{"class":424}," range",[372,510,511],{"class":381},"(symbol.symbolLayerCount()):\n",[372,513,515,518,520],{"class":202,"line":514},12,[372,516,517],{"class":381},"    sl ",[372,519,411],{"class":377},[372,521,522],{"class":381}," symbol.symbolLayer(i)\n",[372,524,526,529],{"class":202,"line":525},13,[372,527,528],{"class":424},"    print",[372,530,531],{"class":381},"(i, sl.layerType(), sl.color().name())\n",[372,533,535],{"class":202,"line":534},14,[372,536,395],{"emptyLinePlaceholder":394},[372,538,540],{"class":202,"line":539},15,[372,541,542],{"class":401},"# Replace the whole symbol with a freshly built one.\n",[372,544,546,549,551],{"class":202,"line":545},16,[372,547,548],{"class":381},"new_symbol ",[372,550,411],{"class":377},[372,552,553],{"class":381}," QgsFillSymbol.createSimple({\n",[372,555,557,560,563,566],{"class":202,"line":556},17,[372,558,559],{"class":417},"    \"color\"",[372,561,562],{"class":381},": ",[372,564,565],{"class":417},"\"#cfe8e3\"",[372,567,568],{"class":381},",\n",[372,570,572,575,577,580],{"class":202,"line":571},18,[372,573,574],{"class":417},"    \"outline_color\"",[372,576,562],{"class":381},[372,578,579],{"class":417},"\"#0f766e\"",[372,581,568],{"class":381},[372,583,585,588,590,593],{"class":202,"line":584},19,[372,586,587],{"class":417},"    \"outline_width\"",[372,589,562],{"class":381},[372,591,592],{"class":417},"\"0.4\"",[372,594,568],{"class":381},[372,596,598],{"class":202,"line":597},20,[372,599,600],{"class":381},"})\n",[372,602,604],{"class":202,"line":603},21,[372,605,606],{"class":381},"renderer.setSymbol(new_symbol)\n",[372,608,610,613],{"class":202,"line":609},22,[372,611,612],{"class":381},"layer.triggerRepaint()                             ",[372,614,615],{"class":401},"# redraw on the canvas\n",[14,617,618,621,622,624,625,628,629,632,633,635],{},[21,619,620],{},"Breakdown:"," We read the layer's existing renderer, walk its symbol layers to see what is drawn, then swap in a new ",[261,623,337],{}," created from a property dictionary. ",[261,626,627],{},"createSimple()"," is the fastest way to build a one-layer symbol. ",[261,630,631],{},"triggerRepaint()"," is what actually refreshes the canvas — without it, the renderer changes but the display stays stale. On QGIS 3.28 the same code runs unchanged; the property keys for ",[261,634,627],{}," have been stable across the 3.x line.",[14,637,638,639,642,643,647],{},"If you are new to retrieving layers and the ",[261,640,641],{},"QgsProject"," singleton, the ",[255,644,646],{"href":645},"\u002Fpyqgis-fundamentals-environment-setup\u002F","PyQGIS Fundamentals & Environment Setup"," pillar covers the application context and project model these snippets assume.",[14,649,650,651,655,656,658,659,662],{},"Two properties of this model are worth internalizing because they shape every later section. First, the renderer is the ",[652,653,654],"em",{},"only"," thing that decides feature symbology — there is no per-feature color stored on the layer itself, so to vary appearance you either swap renderers or attach data-defined properties to a symbol layer. Second, symbols are mutable but cloning matters: when you assign one ",[261,657,263],{}," instance to several categories or layers, edits to it can leak across them, so call ",[261,660,661],{},"symbol.clone()"," whenever you reuse a symbol as a starting point. Getting these two facts right prevents the most common class of \"my changes affected the wrong features\" bugs.",[14,664,665,666,669],{},"It also helps to know the units system. Symbol sizes, line widths, and label sizes default to millimeters, but most properties accept a render-unit setter — ",[261,667,668],{},"setWidthUnit(QgsUnitTypes.RenderPixels)"," or map units for scale-dependent symbology. Mixing millimeters (constant on the printed page) with map units (constant on the ground) is a deliberate cartographic choice: roads that should stay a fixed visual width use millimeters, while buffers that represent a real-world distance use map units.",[239,671,673],{"id":672},"styling-vector-layers-programmatically","Styling Vector Layers Programmatically",[14,675,676,677,679,680,682,683,685,686,688],{},"Most styling work begins with single-symbol rendering: one consistent look applied to every feature in a layer. The pattern differs slightly by geometry type because each maps to a different symbol subclass — ",[261,678,337],{}," for polygons, ",[261,681,341],{}," for lines, and ",[261,684,345],{}," for points. The key insight is that you rarely need to construct symbol layers by hand; ",[261,687,627],{}," accepts a dictionary of the same properties exposed in the symbology dialog.",[363,690,692],{"className":365,"code":691,"language":367,"meta":368,"style":368},"from qgis.core import (\n    QgsProject,\n    QgsFillSymbol,\n    QgsLineSymbol,\n    QgsMarkerSymbol,\n    QgsSingleSymbolRenderer,\n)\n\n\ndef apply_single_symbol(layer, props):\n    \"\"\"Assign a single-symbol renderer appropriate to the layer geometry.\"\"\"\n    geom_type = layer.geometryType()  # 0=point, 1=line, 2=polygon\n    if geom_type == 2:\n        symbol = QgsFillSymbol.createSimple(props)\n    elif geom_type == 1:\n        symbol = QgsLineSymbol.createSimple(props)\n    else:\n        symbol = QgsMarkerSymbol.createSimple(props)\n    layer.setRenderer(QgsSingleSymbolRenderer(symbol))\n    layer.triggerRepaint()\n\n\nrivers = QgsProject.instance().mapLayersByName(\"rivers\")[0]\napply_single_symbol(rivers, {\n    \"line_color\": \"#2563eb\",\n    \"line_width\": \"0.6\",\n    \"capstyle\": \"round\",\n})\n\nstations = QgsProject.instance().mapLayersByName(\"stations\")[0]\napply_single_symbol(stations, {\n    \"name\": \"circle\",\n    \"color\": \"#b45309\",\n    \"size\": \"3\",\n    \"outline_color\": \"#17211d\",\n})\n",[261,693,694,705,710,715,720,725,730,735,739,743,755,760,773,790,800,814,823,830,839,844,849,853,857,876,882,895,908,921,926,931,950,956,969,981,994,1006],{"__ignoreMap":368},[372,695,696,698,700,702],{"class":202,"line":374},[372,697,378],{"class":377},[372,699,382],{"class":381},[372,701,385],{"class":377},[372,703,704],{"class":381}," (\n",[372,706,707],{"class":202,"line":391},[372,708,709],{"class":381},"    QgsProject,\n",[372,711,712],{"class":202,"line":398},[372,713,714],{"class":381},"    QgsFillSymbol,\n",[372,716,717],{"class":202,"line":405},[372,718,719],{"class":381},"    QgsLineSymbol,\n",[372,721,722],{"class":202,"line":430},[372,723,724],{"class":381},"    QgsMarkerSymbol,\n",[372,726,727],{"class":202,"line":435},[372,728,729],{"class":381},"    QgsSingleSymbolRenderer,\n",[372,731,732],{"class":202,"line":446},[372,733,734],{"class":381},")\n",[372,736,737],{"class":202,"line":464},[372,738,395],{"emptyLinePlaceholder":394},[372,740,741],{"class":202,"line":478},[372,742,395],{"emptyLinePlaceholder":394},[372,744,745,748,752],{"class":202,"line":491},[372,746,747],{"class":377},"def",[372,749,751],{"class":750},"svObZ"," apply_single_symbol",[372,753,754],{"class":381},"(layer, props):\n",[372,756,757],{"class":202,"line":496},[372,758,759],{"class":417},"    \"\"\"Assign a single-symbol renderer appropriate to the layer geometry.\"\"\"\n",[372,761,762,765,767,770],{"class":202,"line":514},[372,763,764],{"class":381},"    geom_type ",[372,766,411],{"class":377},[372,768,769],{"class":381}," layer.geometryType()  ",[372,771,772],{"class":401},"# 0=point, 1=line, 2=polygon\n",[372,774,775,778,781,784,787],{"class":202,"line":525},[372,776,777],{"class":377},"    if",[372,779,780],{"class":381}," geom_type ",[372,782,783],{"class":377},"==",[372,785,786],{"class":424}," 2",[372,788,789],{"class":381},":\n",[372,791,792,795,797],{"class":202,"line":534},[372,793,794],{"class":381},"        symbol ",[372,796,411],{"class":377},[372,798,799],{"class":381}," QgsFillSymbol.createSimple(props)\n",[372,801,802,805,807,809,812],{"class":202,"line":539},[372,803,804],{"class":377},"    elif",[372,806,780],{"class":381},[372,808,783],{"class":377},[372,810,811],{"class":424}," 1",[372,813,789],{"class":381},[372,815,816,818,820],{"class":202,"line":545},[372,817,794],{"class":381},[372,819,411],{"class":377},[372,821,822],{"class":381}," QgsLineSymbol.createSimple(props)\n",[372,824,825,828],{"class":202,"line":556},[372,826,827],{"class":377},"    else",[372,829,789],{"class":381},[372,831,832,834,836],{"class":202,"line":571},[372,833,794],{"class":381},[372,835,411],{"class":377},[372,837,838],{"class":381}," QgsMarkerSymbol.createSimple(props)\n",[372,840,841],{"class":202,"line":584},[372,842,843],{"class":381},"    layer.setRenderer(QgsSingleSymbolRenderer(symbol))\n",[372,845,846],{"class":202,"line":597},[372,847,848],{"class":381},"    layer.triggerRepaint()\n",[372,850,851],{"class":202,"line":603},[372,852,395],{"emptyLinePlaceholder":394},[372,854,855],{"class":202,"line":609},[372,856,395],{"emptyLinePlaceholder":394},[372,858,860,863,865,867,870,872,874],{"class":202,"line":859},23,[372,861,862],{"class":381},"rivers ",[372,864,411],{"class":377},[372,866,414],{"class":381},[372,868,869],{"class":417},"\"rivers\"",[372,871,421],{"class":381},[372,873,59],{"class":424},[372,875,427],{"class":381},[372,877,879],{"class":202,"line":878},24,[372,880,881],{"class":381},"apply_single_symbol(rivers, {\n",[372,883,885,888,890,893],{"class":202,"line":884},25,[372,886,887],{"class":417},"    \"line_color\"",[372,889,562],{"class":381},[372,891,892],{"class":417},"\"#2563eb\"",[372,894,568],{"class":381},[372,896,898,901,903,906],{"class":202,"line":897},26,[372,899,900],{"class":417},"    \"line_width\"",[372,902,562],{"class":381},[372,904,905],{"class":417},"\"0.6\"",[372,907,568],{"class":381},[372,909,911,914,916,919],{"class":202,"line":910},27,[372,912,913],{"class":417},"    \"capstyle\"",[372,915,562],{"class":381},[372,917,918],{"class":417},"\"round\"",[372,920,568],{"class":381},[372,922,924],{"class":202,"line":923},28,[372,925,600],{"class":381},[372,927,929],{"class":202,"line":928},29,[372,930,395],{"emptyLinePlaceholder":394},[372,932,934,937,939,941,944,946,948],{"class":202,"line":933},30,[372,935,936],{"class":381},"stations ",[372,938,411],{"class":377},[372,940,414],{"class":381},[372,942,943],{"class":417},"\"stations\"",[372,945,421],{"class":381},[372,947,59],{"class":424},[372,949,427],{"class":381},[372,951,953],{"class":202,"line":952},31,[372,954,955],{"class":381},"apply_single_symbol(stations, {\n",[372,957,959,962,964,967],{"class":202,"line":958},32,[372,960,961],{"class":417},"    \"name\"",[372,963,562],{"class":381},[372,965,966],{"class":417},"\"circle\"",[372,968,568],{"class":381},[372,970,972,974,976,979],{"class":202,"line":971},33,[372,973,559],{"class":417},[372,975,562],{"class":381},[372,977,978],{"class":417},"\"#b45309\"",[372,980,568],{"class":381},[372,982,984,987,989,992],{"class":202,"line":983},34,[372,985,986],{"class":417},"    \"size\"",[372,988,562],{"class":381},[372,990,991],{"class":417},"\"3\"",[372,993,568],{"class":381},[372,995,997,999,1001,1004],{"class":202,"line":996},35,[372,998,574],{"class":417},[372,1000,562],{"class":381},[372,1002,1003],{"class":417},"\"#17211d\"",[372,1005,568],{"class":381},[372,1007,1009],{"class":202,"line":1008},36,[372,1010,600],{"class":381},[14,1012,1013,1015,1016,1019,1020,1023,1024,1027,1028,1023,1031,1023,1034,1037,1038,1023,1040,1043,1044,1046,1047,1050],{},[21,1014,620],{}," A single helper handles all three geometry types by branching on ",[261,1017,1018],{},"layer.geometryType()",". Note that property keys are geometry-specific: lines use ",[261,1021,1022],{},"line_color","\u002F",[261,1025,1026],{},"line_width",", markers use ",[261,1029,1030],{},"name",[261,1032,1033],{},"size",[261,1035,1036],{},"color",", and fills use ",[261,1039,1036],{},[261,1041,1042],{},"outline_color",". Building a fresh ",[261,1045,315],{}," and assigning it with ",[261,1048,1049],{},"setRenderer()"," cleanly replaces whatever was there before.",[14,1052,1053,1054,1057,1058,1061,1062,338,1065,338,1068,1071,1072,1075],{},"For data-driven overrides — making stroke width respond to an attribute, for instance — you set a ",[261,1055,1056],{},"QgsProperty"," expression on a symbol layer using ",[261,1059,1060],{},"setDataDefinedProperty()",". A symbol layer exposes a registry of overridable properties (",[261,1063,1064],{},"QgsSymbolLayer.PropertyStrokeWidth",[261,1066,1067],{},"PropertyFillColor",[261,1069,1070],{},"PropertySize",", and many more); binding an expression such as ",[261,1073,1074],{},"QgsProperty.fromExpression('\"flow\" \u002F 50')"," to one of them lets a single symbol scale itself per feature without building a graduated renderer. This is the right tool when the visual mapping is continuous and formulaic rather than binned into discrete classes.",[14,1077,1078,1079,1082,1083,1085,1086,1090],{},"Stacking is the other half of expressive single-symbol styling. Because a symbol holds an ordered list of symbol layers, you can append a second pass — ",[261,1080,1081],{},"symbol.appendSymbolLayer(QgsSimpleLineSymbolLayer.create({...}))"," — to draw, say, a casing line beneath a road's center line, or a drop-shadow offset fill beneath a polygon. Order is bottom-up: index 0 draws first and sits visually lowest. The full toolkit of fills, line patterns, marker placement, casing, and data-defined symbology is the subject of the ",[255,1084,258],{"href":257}," cluster, including the common task of ",[255,1087,1089],{"href":1088},"\u002Fpyqgis-cartography-visualization\u002Fprogrammatic-layer-styling\u002Fset-vector-layer-symbol-color-pyqgis\u002F","setting a vector layer's symbol color",".",[239,1092,1094],{"id":1093},"thematic-maps-with-categorized-graduated-renderers","Thematic Maps with Categorized & Graduated Renderers",[14,1096,1097,1098,1101,1102,1104,1105,1107],{},"The leap from a styled layer to a ",[652,1099,1100],{},"map that says something"," is classification. Two renderers do the heavy lifting. ",[261,1103,319],{}," assigns a distinct symbol to each unique value of a field — ideal for nominal data like land-use class or administrative type. ",[261,1106,323],{}," bins a numeric field into ranges and ramps a symbol property across them — the engine behind every choropleth.",[14,1109,1110,1111,338,1114,338,1117,1120],{},"The graduated renderer needs three decisions: which field, how many classes, and which classification method. The method object (",[261,1112,1113],{},"QgsClassificationEqualInterval",[261,1115,1116],{},"QgsClassificationQuantile",[261,1118,1119],{},"QgsClassificationJenks",") computes the break points.",[363,1122,1124],{"className":365,"code":1123,"language":367,"meta":368,"style":368},"from qgis.core import (\n    QgsProject,\n    QgsGraduatedSymbolRenderer,\n    QgsClassificationJenks,\n    QgsStyle,\n)\n\nlayer = QgsProject.instance().mapLayersByName(\"counties\")[0]\n\nrenderer = QgsGraduatedSymbolRenderer()\nrenderer.setClassAttribute(\"pop_density\")\nrenderer.setClassificationMethod(QgsClassificationJenks())\n\n# Apply a built-in color ramp across 5 natural-breaks classes.\nramp = QgsStyle.defaultStyle().colorRamp(\"Reds\")\nrenderer.updateClasses(layer, QgsGraduatedSymbolRenderer.Jenks, 5)\nrenderer.updateColorRamp(ramp)\n\nlayer.setRenderer(renderer)\nlayer.triggerRepaint()\n",[261,1125,1126,1136,1140,1145,1150,1155,1159,1163,1180,1184,1193,1203,1208,1212,1217,1232,1242,1247,1251,1256],{"__ignoreMap":368},[372,1127,1128,1130,1132,1134],{"class":202,"line":374},[372,1129,378],{"class":377},[372,1131,382],{"class":381},[372,1133,385],{"class":377},[372,1135,704],{"class":381},[372,1137,1138],{"class":202,"line":391},[372,1139,709],{"class":381},[372,1141,1142],{"class":202,"line":398},[372,1143,1144],{"class":381},"    QgsGraduatedSymbolRenderer,\n",[372,1146,1147],{"class":202,"line":405},[372,1148,1149],{"class":381},"    QgsClassificationJenks,\n",[372,1151,1152],{"class":202,"line":430},[372,1153,1154],{"class":381},"    QgsStyle,\n",[372,1156,1157],{"class":202,"line":435},[372,1158,734],{"class":381},[372,1160,1161],{"class":202,"line":446},[372,1162,395],{"emptyLinePlaceholder":394},[372,1164,1165,1167,1169,1171,1174,1176,1178],{"class":202,"line":464},[372,1166,408],{"class":381},[372,1168,411],{"class":377},[372,1170,414],{"class":381},[372,1172,1173],{"class":417},"\"counties\"",[372,1175,421],{"class":381},[372,1177,59],{"class":424},[372,1179,427],{"class":381},[372,1181,1182],{"class":202,"line":478},[372,1183,395],{"emptyLinePlaceholder":394},[372,1185,1186,1188,1190],{"class":202,"line":491},[372,1187,438],{"class":381},[372,1189,411],{"class":377},[372,1191,1192],{"class":381}," QgsGraduatedSymbolRenderer()\n",[372,1194,1195,1198,1201],{"class":202,"line":496},[372,1196,1197],{"class":381},"renderer.setClassAttribute(",[372,1199,1200],{"class":417},"\"pop_density\"",[372,1202,734],{"class":381},[372,1204,1205],{"class":202,"line":514},[372,1206,1207],{"class":381},"renderer.setClassificationMethod(QgsClassificationJenks())\n",[372,1209,1210],{"class":202,"line":525},[372,1211,395],{"emptyLinePlaceholder":394},[372,1213,1214],{"class":202,"line":534},[372,1215,1216],{"class":401},"# Apply a built-in color ramp across 5 natural-breaks classes.\n",[372,1218,1219,1222,1224,1227,1230],{"class":202,"line":539},[372,1220,1221],{"class":381},"ramp ",[372,1223,411],{"class":377},[372,1225,1226],{"class":381}," QgsStyle.defaultStyle().colorRamp(",[372,1228,1229],{"class":417},"\"Reds\"",[372,1231,734],{"class":381},[372,1233,1234,1237,1240],{"class":202,"line":545},[372,1235,1236],{"class":381},"renderer.updateClasses(layer, QgsGraduatedSymbolRenderer.Jenks, ",[372,1238,1239],{"class":424},"5",[372,1241,734],{"class":381},[372,1243,1244],{"class":202,"line":556},[372,1245,1246],{"class":381},"renderer.updateColorRamp(ramp)\n",[372,1248,1249],{"class":202,"line":571},[372,1250,395],{"emptyLinePlaceholder":394},[372,1252,1253],{"class":202,"line":584},[372,1254,1255],{"class":381},"layer.setRenderer(renderer)\n",[372,1257,1258],{"class":202,"line":597},[372,1259,1260],{"class":381},"layer.triggerRepaint()\n",[14,1262,1263,1265,1266,320,1269,1272,1273,1276,1277,1280,1281,1284,1285,1288,1289,1292,1293,1295],{},[21,1264,620],{}," We set the classification ",[652,1267,1268],{},"field",[652,1270,1271],{},"method",", then call ",[261,1274,1275],{},"updateClasses()"," to compute the breaks from the layer's data and ",[261,1278,1279],{},"updateColorRamp()"," to color them. ",[261,1282,1283],{},"QgsStyle.defaultStyle().colorRamp(\"Reds\")"," pulls a named ramp from the bundled style library. On QGIS 3.34 the ",[261,1286,1287],{},"setClassificationMethod()"," API is the recommended path; on 3.28 the older ",[261,1290,1291],{},"setMode(QgsGraduatedSymbolRenderer.Jenks)"," enum still works and is what ",[261,1294,1275],{}," accepts in both versions. The newer 3.40+ releases keep both but emphasize the method-object API.",[14,1297,1298,1299,1302,1303,1306],{},"The categorized renderer follows a similar rhythm but enumerates unique values instead of computing breaks. You typically build it by querying the layer's distinct values, creating one ",[261,1300,1301],{},"QgsRendererCategory"," per value with its own symbol, and assembling them into a ",[261,1304,1305],{},"QgsCategorizedSymbolRenderer(field, categories)",". A practical refinement is reserving an explicit catch-all category with an empty value string, which captures any feature whose attribute does not match a defined class — without it, unmatched features render invisibly and quietly disappear from the map.",[14,1308,1309],{},"Two cartographic cautions apply to both renderers. First, normalize before you classify: mapping raw counts (population) instead of rates (population per km²) produces choropleths that simply restate where the big polygons are. Compute the rate into a field, or feed an expression-based class attribute, before classifying. Second, keep class counts modest — five to seven classes is the readable maximum for most sequential ramps, because the human eye cannot reliably distinguish more shades of a single hue.",[14,1311,1312,1313,320,1317,1321,1322,1324],{},"Choosing a classification method materially changes the story your map tells — quantile spreads features evenly across classes, while Jenks\u002Fnatural breaks minimizes within-class variance and equal interval preserves honest value spacing at the cost of lopsided bins on skewed data. The trade-offs, plus full recipes for building ",[255,1314,1316],{"href":1315},"\u002Fpyqgis-cartography-visualization\u002Fgraduated-categorized-renderers\u002Fcreate-choropleth-map-pyqgis\u002F","choropleth maps",[255,1318,1320],{"href":1319},"\u002Fpyqgis-cartography-visualization\u002Fgraduated-categorized-renderers\u002Fclassify-layer-natural-breaks-jenks-pyqgis\u002F","classifying a layer with natural breaks (Jenks)",", live in the ",[255,1323,272],{"href":271}," cluster.",[239,1326,281],{"id":1327},"labeling-annotations",[14,1329,1330,1331,1333,1334,1337],{},"A thematic map is incomplete without text. QGIS labeling is configured through ",[261,1332,285],{},", which controls everything from the field or expression that supplies label text to placement, priority, and rendering. You wrap those settings in a ",[261,1335,1336],{},"QgsVectorLayerSimpleLabeling"," object and hand it to the layer, then flip labels on.",[363,1339,1341],{"className":365,"code":1340,"language":367,"meta":368,"style":368},"from qgis.core import (\n    QgsProject,\n    QgsPalLayerSettings,\n    QgsTextFormat,\n    QgsTextBufferSettings,\n    QgsVectorLayerSimpleLabeling,\n)\nfrom qgis.PyQt.QtGui import QColor, QFont\n\nlayer = QgsProject.instance().mapLayersByName(\"cities\")[0]\n\ntext_format = QgsTextFormat()\ntext_format.setFont(QFont(\"Noto Sans\", 10))\ntext_format.setSize(10)\n\nbuffer = QgsTextBufferSettings()\nbuffer.setEnabled(True)\nbuffer.setSize(1.0)\nbuffer.setColor(QColor(\"#ffffff\"))\ntext_format.setBuffer(buffer)\n\nsettings = QgsPalLayerSettings()\nsettings.fieldName = \"\\\"name\\\" || ' (' || format_number(\\\"pop\\\") || ')'\"\nsettings.isExpression = True\nsettings.setFormat(text_format)\nsettings.placement = QgsPalLayerSettings.OverPoint\n\nlayer.setLabeling(QgsVectorLayerSimpleLabeling(settings))\nlayer.setLabelsEnabled(True)\nlayer.triggerRepaint()\n",[261,1342,1343,1353,1357,1362,1367,1372,1377,1381,1393,1397,1414,1418,1428,1443,1452,1456,1466,1476,1486,1496,1501,1505,1515,1545,1555,1560,1570,1574,1579,1588],{"__ignoreMap":368},[372,1344,1345,1347,1349,1351],{"class":202,"line":374},[372,1346,378],{"class":377},[372,1348,382],{"class":381},[372,1350,385],{"class":377},[372,1352,704],{"class":381},[372,1354,1355],{"class":202,"line":391},[372,1356,709],{"class":381},[372,1358,1359],{"class":202,"line":398},[372,1360,1361],{"class":381},"    QgsPalLayerSettings,\n",[372,1363,1364],{"class":202,"line":405},[372,1365,1366],{"class":381},"    QgsTextFormat,\n",[372,1368,1369],{"class":202,"line":430},[372,1370,1371],{"class":381},"    QgsTextBufferSettings,\n",[372,1373,1374],{"class":202,"line":435},[372,1375,1376],{"class":381},"    QgsVectorLayerSimpleLabeling,\n",[372,1378,1379],{"class":202,"line":446},[372,1380,734],{"class":381},[372,1382,1383,1385,1388,1390],{"class":202,"line":464},[372,1384,378],{"class":377},[372,1386,1387],{"class":381}," qgis.PyQt.QtGui ",[372,1389,385],{"class":377},[372,1391,1392],{"class":381}," QColor, QFont\n",[372,1394,1395],{"class":202,"line":478},[372,1396,395],{"emptyLinePlaceholder":394},[372,1398,1399,1401,1403,1405,1408,1410,1412],{"class":202,"line":491},[372,1400,408],{"class":381},[372,1402,411],{"class":377},[372,1404,414],{"class":381},[372,1406,1407],{"class":417},"\"cities\"",[372,1409,421],{"class":381},[372,1411,59],{"class":424},[372,1413,427],{"class":381},[372,1415,1416],{"class":202,"line":496},[372,1417,395],{"emptyLinePlaceholder":394},[372,1419,1420,1423,1425],{"class":202,"line":514},[372,1421,1422],{"class":381},"text_format ",[372,1424,411],{"class":377},[372,1426,1427],{"class":381}," QgsTextFormat()\n",[372,1429,1430,1433,1436,1438,1440],{"class":202,"line":525},[372,1431,1432],{"class":381},"text_format.setFont(QFont(",[372,1434,1435],{"class":417},"\"Noto Sans\"",[372,1437,338],{"class":381},[372,1439,229],{"class":424},[372,1441,1442],{"class":381},"))\n",[372,1444,1445,1448,1450],{"class":202,"line":534},[372,1446,1447],{"class":381},"text_format.setSize(",[372,1449,229],{"class":424},[372,1451,734],{"class":381},[372,1453,1454],{"class":202,"line":539},[372,1455,395],{"emptyLinePlaceholder":394},[372,1457,1458,1461,1463],{"class":202,"line":545},[372,1459,1460],{"class":381},"buffer ",[372,1462,411],{"class":377},[372,1464,1465],{"class":381}," QgsTextBufferSettings()\n",[372,1467,1468,1471,1474],{"class":202,"line":556},[372,1469,1470],{"class":381},"buffer.setEnabled(",[372,1472,1473],{"class":424},"True",[372,1475,734],{"class":381},[372,1477,1478,1481,1484],{"class":202,"line":571},[372,1479,1480],{"class":381},"buffer.setSize(",[372,1482,1483],{"class":424},"1.0",[372,1485,734],{"class":381},[372,1487,1488,1491,1494],{"class":202,"line":584},[372,1489,1490],{"class":381},"buffer.setColor(QColor(",[372,1492,1493],{"class":417},"\"#ffffff\"",[372,1495,1442],{"class":381},[372,1497,1498],{"class":202,"line":597},[372,1499,1500],{"class":381},"text_format.setBuffer(buffer)\n",[372,1502,1503],{"class":202,"line":603},[372,1504,395],{"emptyLinePlaceholder":394},[372,1506,1507,1510,1512],{"class":202,"line":609},[372,1508,1509],{"class":381},"settings ",[372,1511,411],{"class":377},[372,1513,1514],{"class":381}," QgsPalLayerSettings()\n",[372,1516,1517,1520,1522,1525,1528,1530,1532,1535,1537,1540,1542],{"class":202,"line":859},[372,1518,1519],{"class":381},"settings.fieldName ",[372,1521,411],{"class":377},[372,1523,1524],{"class":417}," \"",[372,1526,1527],{"class":424},"\\\"",[372,1529,1030],{"class":417},[372,1531,1527],{"class":424},[372,1533,1534],{"class":417}," || ' (' || format_number(",[372,1536,1527],{"class":424},[372,1538,1539],{"class":417},"pop",[372,1541,1527],{"class":424},[372,1543,1544],{"class":417},") || ')'\"\n",[372,1546,1547,1550,1552],{"class":202,"line":878},[372,1548,1549],{"class":381},"settings.isExpression ",[372,1551,411],{"class":377},[372,1553,1554],{"class":424}," True\n",[372,1556,1557],{"class":202,"line":884},[372,1558,1559],{"class":381},"settings.setFormat(text_format)\n",[372,1561,1562,1565,1567],{"class":202,"line":897},[372,1563,1564],{"class":381},"settings.placement ",[372,1566,411],{"class":377},[372,1568,1569],{"class":381}," QgsPalLayerSettings.OverPoint\n",[372,1571,1572],{"class":202,"line":910},[372,1573,395],{"emptyLinePlaceholder":394},[372,1575,1576],{"class":202,"line":923},[372,1577,1578],{"class":381},"layer.setLabeling(QgsVectorLayerSimpleLabeling(settings))\n",[372,1580,1581,1584,1586],{"class":202,"line":928},[372,1582,1583],{"class":381},"layer.setLabelsEnabled(",[372,1585,1473],{"class":424},[372,1587,734],{"class":381},[372,1589,1590],{"class":202,"line":933},[372,1591,1260],{"class":381},[14,1593,1594,1596,1597,1600,1601,1604,1605,1608,1609,1612,1613,1616,1617,1620,1621,338,1624,342,1627,1630,1631,1634],{},[21,1595,620],{}," ",[261,1598,1599],{},"QgsTextFormat"," defines the font and a white halo (",[261,1602,1603],{},"QgsTextBufferSettings",") so labels stay legible over busy backgrounds. Setting ",[261,1606,1607],{},"fieldName"," to an expression and ",[261,1610,1611],{},"isExpression = True"," lets us concatenate the city name with a formatted population. ",[261,1614,1615],{},"placement"," chooses the engine — ",[261,1618,1619],{},"OverPoint"," here, with ",[261,1622,1623],{},"AroundPoint",[261,1625,1626],{},"Line",[261,1628,1629],{},"Curved"," available for other geometries. ",[261,1632,1633],{},"setLabelsEnabled(True)"," is the switch that turns the layer's labels on.",[14,1636,1637,1638,1641,1642,338,1645,342,1648,1651,1652,1655],{},"Three settings separate amateur labels from professional ones. Placement priority and conflict resolution — QGIS's PAL engine drops labels that would overlap, so set ",[261,1639,1640],{},"settings.priority"," and mark important layers' labels as obstacles or non-removable to control which survive a crowded extent. Scale-based visibility, configured with ",[261,1643,1644],{},"settings.scaleVisibility",[261,1646,1647],{},"settings.minimumScale",[261,1649,1650],{},"settings.maximumScale",", keeps minor place names hidden until the user zooms in. And callout lines (",[261,1653,1654],{},"settings.setCallout()",") draw a leader from a displaced label back to its feature, which is essential when point density forces labels off their markers.",[14,1657,1658,1659,1662,1663,1666,1667,1670,1671,1675,1676,1324],{},"For polygons and lines, placement mode matters as much as text format: ",[261,1660,1661],{},"QgsPalLayerSettings.PerimeterCurved"," follows a river or coastline, while ",[261,1664,1665],{},"OffsetFromCentroid"," or ",[261,1668,1669],{},"Horizontal"," suit area features. Expression labels, rule-based label sets that change with scale, and annotation items (free-floating text and callouts) are covered in depth — including ",[255,1672,1674],{"href":1673},"\u002Fpyqgis-cartography-visualization\u002Flabeling-and-annotations\u002Fadd-rule-based-labels-pyqgis\u002F","adding rule-based labels"," — in the ",[255,1677,281],{"href":280},[239,1679,1681],{"id":1680},"raster-styling","Raster Styling",[14,1683,1684,1685,1688,1689,1692],{},"Rasters use a parallel but separate renderer hierarchy. For continuous single-band data — elevation, temperature, an index — the workhorse is ",[261,1686,1687],{},"QgsSingleBandPseudoColorRenderer",", which maps pixel values to colors through a ",[261,1690,1691],{},"QgsColorRampShader",". You define the shader's classes (value\u002Fcolor pairs), choose an interpolation mode, and attach it to the renderer.",[363,1694,1696],{"className":365,"code":1695,"language":367,"meta":368,"style":368},"from qgis.core import (\n    QgsProject,\n    QgsRasterShader,\n    QgsColorRampShader,\n    QgsSingleBandPseudoColorRenderer,\n)\nfrom qgis.PyQt.QtGui import QColor\n\ndem = QgsProject.instance().mapLayersByName(\"elevation\")[0]\nprovider = dem.dataProvider()\nband = 1\n\nstats = provider.bandStatistics(band)\nvmin, vmax = stats.minimumValue, stats.maximumValue\n\ncolor_shader = QgsColorRampShader(vmin, vmax)\ncolor_shader.setColorRampType(QgsColorRampShader.Interpolated)\ncolor_shader.setColorRampItemList([\n    QgsColorRampShader.ColorRampItem(vmin, QColor(\"#0f766e\"), \"low\"),\n    QgsColorRampShader.ColorRampItem((vmin + vmax) \u002F 2, QColor(\"#facc15\"), \"mid\"),\n    QgsColorRampShader.ColorRampItem(vmax, QColor(\"#b45309\"), \"high\"),\n])\n\nshader = QgsRasterShader()\nshader.setRasterShaderFunction(color_shader)\n\nrenderer = QgsSingleBandPseudoColorRenderer(provider, band, shader)\ndem.setRenderer(renderer)\ndem.triggerRepaint()\n",[261,1697,1698,1708,1712,1717,1722,1727,1731,1742,1746,1764,1774,1784,1788,1798,1808,1812,1822,1827,1832,1848,1876,1890,1895,1899,1909,1914,1918,1927,1932],{"__ignoreMap":368},[372,1699,1700,1702,1704,1706],{"class":202,"line":374},[372,1701,378],{"class":377},[372,1703,382],{"class":381},[372,1705,385],{"class":377},[372,1707,704],{"class":381},[372,1709,1710],{"class":202,"line":391},[372,1711,709],{"class":381},[372,1713,1714],{"class":202,"line":398},[372,1715,1716],{"class":381},"    QgsRasterShader,\n",[372,1718,1719],{"class":202,"line":405},[372,1720,1721],{"class":381},"    QgsColorRampShader,\n",[372,1723,1724],{"class":202,"line":430},[372,1725,1726],{"class":381},"    QgsSingleBandPseudoColorRenderer,\n",[372,1728,1729],{"class":202,"line":435},[372,1730,734],{"class":381},[372,1732,1733,1735,1737,1739],{"class":202,"line":446},[372,1734,378],{"class":377},[372,1736,1387],{"class":381},[372,1738,385],{"class":377},[372,1740,1741],{"class":381}," QColor\n",[372,1743,1744],{"class":202,"line":464},[372,1745,395],{"emptyLinePlaceholder":394},[372,1747,1748,1751,1753,1755,1758,1760,1762],{"class":202,"line":478},[372,1749,1750],{"class":381},"dem ",[372,1752,411],{"class":377},[372,1754,414],{"class":381},[372,1756,1757],{"class":417},"\"elevation\"",[372,1759,421],{"class":381},[372,1761,59],{"class":424},[372,1763,427],{"class":381},[372,1765,1766,1769,1771],{"class":202,"line":491},[372,1767,1768],{"class":381},"provider ",[372,1770,411],{"class":377},[372,1772,1773],{"class":381}," dem.dataProvider()\n",[372,1775,1776,1779,1781],{"class":202,"line":496},[372,1777,1778],{"class":381},"band ",[372,1780,411],{"class":377},[372,1782,1783],{"class":424}," 1\n",[372,1785,1786],{"class":202,"line":514},[372,1787,395],{"emptyLinePlaceholder":394},[372,1789,1790,1793,1795],{"class":202,"line":525},[372,1791,1792],{"class":381},"stats ",[372,1794,411],{"class":377},[372,1796,1797],{"class":381}," provider.bandStatistics(band)\n",[372,1799,1800,1803,1805],{"class":202,"line":534},[372,1801,1802],{"class":381},"vmin, vmax ",[372,1804,411],{"class":377},[372,1806,1807],{"class":381}," stats.minimumValue, stats.maximumValue\n",[372,1809,1810],{"class":202,"line":539},[372,1811,395],{"emptyLinePlaceholder":394},[372,1813,1814,1817,1819],{"class":202,"line":545},[372,1815,1816],{"class":381},"color_shader ",[372,1818,411],{"class":377},[372,1820,1821],{"class":381}," QgsColorRampShader(vmin, vmax)\n",[372,1823,1824],{"class":202,"line":556},[372,1825,1826],{"class":381},"color_shader.setColorRampType(QgsColorRampShader.Interpolated)\n",[372,1828,1829],{"class":202,"line":571},[372,1830,1831],{"class":381},"color_shader.setColorRampItemList([\n",[372,1833,1834,1837,1839,1842,1845],{"class":202,"line":584},[372,1835,1836],{"class":381},"    QgsColorRampShader.ColorRampItem(vmin, QColor(",[372,1838,579],{"class":417},[372,1840,1841],{"class":381},"), ",[372,1843,1844],{"class":417},"\"low\"",[372,1846,1847],{"class":381},"),\n",[372,1849,1850,1853,1856,1859,1861,1863,1866,1869,1871,1874],{"class":202,"line":597},[372,1851,1852],{"class":381},"    QgsColorRampShader.ColorRampItem((vmin ",[372,1854,1855],{"class":377},"+",[372,1857,1858],{"class":381}," vmax) ",[372,1860,1023],{"class":377},[372,1862,786],{"class":424},[372,1864,1865],{"class":381},", QColor(",[372,1867,1868],{"class":417},"\"#facc15\"",[372,1870,1841],{"class":381},[372,1872,1873],{"class":417},"\"mid\"",[372,1875,1847],{"class":381},[372,1877,1878,1881,1883,1885,1888],{"class":202,"line":603},[372,1879,1880],{"class":381},"    QgsColorRampShader.ColorRampItem(vmax, QColor(",[372,1882,978],{"class":417},[372,1884,1841],{"class":381},[372,1886,1887],{"class":417},"\"high\"",[372,1889,1847],{"class":381},[372,1891,1892],{"class":202,"line":609},[372,1893,1894],{"class":381},"])\n",[372,1896,1897],{"class":202,"line":859},[372,1898,395],{"emptyLinePlaceholder":394},[372,1900,1901,1904,1906],{"class":202,"line":878},[372,1902,1903],{"class":381},"shader ",[372,1905,411],{"class":377},[372,1907,1908],{"class":381}," QgsRasterShader()\n",[372,1910,1911],{"class":202,"line":884},[372,1912,1913],{"class":381},"shader.setRasterShaderFunction(color_shader)\n",[372,1915,1916],{"class":202,"line":897},[372,1917,395],{"emptyLinePlaceholder":394},[372,1919,1920,1922,1924],{"class":202,"line":910},[372,1921,438],{"class":381},[372,1923,411],{"class":377},[372,1925,1926],{"class":381}," QgsSingleBandPseudoColorRenderer(provider, band, shader)\n",[372,1928,1929],{"class":202,"line":923},[372,1930,1931],{"class":381},"dem.setRenderer(renderer)\n",[372,1933,1934],{"class":202,"line":928},[372,1935,1936],{"class":381},"dem.triggerRepaint()\n",[14,1938,1939,1941,1942,1945,1946,1948,1949,1952,1953,320,1956,1959,1960,1963,1964,1967,1968,1971,1972,1976],{},[21,1940,620],{}," We pull real min\u002Fmax from ",[261,1943,1944],{},"bandStatistics()"," so the ramp fits the data instead of guessing. The ",[261,1947,1691],{}," is built with three interpolated stops; ",[261,1950,1951],{},"Interpolated"," blends between them, while ",[261,1954,1955],{},"Discrete",[261,1957,1958],{},"Exact"," give stepped or value-matched output. The renderer needs the data provider, band number, and the wrapping ",[261,1961,1962],{},"QgsRasterShader",". For hillshade, you would instead use ",[261,1965,1966],{},"QgsHillshadeRenderer"," (or run ",[261,1969,1970],{},"processing.run(\"gdal:hillshade\", ...)",") and overlay it semi-transparently above the pseudocolor layer. Applying a ",[255,1973,1975],{"href":1974},"\u002Fpyqgis-cartography-visualization\u002Fprogrammatic-layer-styling\u002Fapply-color-ramp-to-raster-pyqgis\u002F","color ramp to a raster"," is detailed in the styling cluster.",[14,1978,1979,1980,1983,1984,1987,1988,1991,1992,1995,1996,1998],{},"Beyond pseudocolor, three other raster renderers cover most needs. ",[261,1981,1982],{},"QgsMultiBandColorRenderer"," composes red, green, and blue bands into a true- or false-color image — the standard for satellite and aerial imagery, where you also set per-band ",[261,1985,1986],{},"QgsContrastEnhancement"," (typically a 2–98% cumulative cut) so the visible range fills the display. ",[261,1989,1990],{},"QgsPalettedRasterRenderer"," maps discrete integer classes (land cover codes) to named colors. And ",[261,1993,1994],{},"QgsSingleBandGrayRenderer"," produces a stretched grayscale view useful for single-band panchromatic data. Switching renderer is always the same gesture — build the renderer against the data provider and call ",[261,1997,1049],{}," — which keeps raster styling conceptually parallel to vector styling.",[14,2000,2001,2002,2006],{},"The DEM and band statistics used here typically come out of a processing chain — see the ",[255,2003,2005],{"href":2004},"\u002Fspatial-data-processing-automation\u002F","Spatial Data Processing & Automation"," pillar for producing and reprojecting the rasters you visualize.",[239,2008,2010],{"id":2009},"color-ramps-qgsstyle","Color Ramps & QgsStyle",[14,2012,2013,2014,2017,2018,2021],{},"Hard-coding hex values works for one map; reusing a consistent palette across a project does not. ",[261,2015,2016],{},"QgsStyle"," is QGIS's style database — the registry of named symbols, color ramps, text formats, and label settings. ",[261,2019,2020],{},"QgsStyle.defaultStyle()"," gives you the bundled library (ColorBrewer ramps, named symbols), and you can register your own ramps for reuse.",[363,2023,2025],{"className":365,"code":2024,"language":367,"meta":368,"style":368},"from qgis.core import QgsStyle, QgsGradientColorRamp, QgsGradientStop\nfrom qgis.PyQt.QtGui import QColor\n\nstyle = QgsStyle.defaultStyle()\n\n# List available ramps, then build and register a custom one.\nprint(\"Bundled ramps:\", style.colorRampNames()[:8])\n\nbrand_ramp = QgsGradientColorRamp(\n    QColor(\"#f6f3ea\"),   # start\n    QColor(\"#0f766e\"),   # end\n    discrete=False,\n    stops=[QgsGradientStop(0.5, QColor(\"#facc15\"))],\n)\n\nif style.colorRamp(\"brand_teal\"):\n    style.removeColorRamp(\"brand_teal\")\nstyle.addColorRamp(\"brand_teal\", brand_ramp)\n\n# Retrieve it later by name from anywhere in the session.\nramp = style.colorRamp(\"brand_teal\")\nprint(\"Mid color:\", ramp.color(0.5).name())\n",[261,2026,2027,2038,2048,2052,2062,2066,2071,2087,2091,2101,2115,2126,2139,2159,2163,2167,2181,2190,2200,2204,2209,2221],{"__ignoreMap":368},[372,2028,2029,2031,2033,2035],{"class":202,"line":374},[372,2030,378],{"class":377},[372,2032,382],{"class":381},[372,2034,385],{"class":377},[372,2036,2037],{"class":381}," QgsStyle, QgsGradientColorRamp, QgsGradientStop\n",[372,2039,2040,2042,2044,2046],{"class":202,"line":391},[372,2041,378],{"class":377},[372,2043,1387],{"class":381},[372,2045,385],{"class":377},[372,2047,1741],{"class":381},[372,2049,2050],{"class":202,"line":398},[372,2051,395],{"emptyLinePlaceholder":394},[372,2053,2054,2057,2059],{"class":202,"line":405},[372,2055,2056],{"class":381},"style ",[372,2058,411],{"class":377},[372,2060,2061],{"class":381}," QgsStyle.defaultStyle()\n",[372,2063,2064],{"class":202,"line":430},[372,2065,395],{"emptyLinePlaceholder":394},[372,2067,2068],{"class":202,"line":435},[372,2069,2070],{"class":401},"# List available ramps, then build and register a custom one.\n",[372,2072,2073,2075,2077,2080,2083,2085],{"class":202,"line":446},[372,2074,449],{"class":424},[372,2076,452],{"class":381},[372,2078,2079],{"class":417},"\"Bundled ramps:\"",[372,2081,2082],{"class":381},", style.colorRampNames()[:",[372,2084,68],{"class":424},[372,2086,1894],{"class":381},[372,2088,2089],{"class":202,"line":464},[372,2090,395],{"emptyLinePlaceholder":394},[372,2092,2093,2096,2098],{"class":202,"line":478},[372,2094,2095],{"class":381},"brand_ramp ",[372,2097,411],{"class":377},[372,2099,2100],{"class":381}," QgsGradientColorRamp(\n",[372,2102,2103,2106,2109,2112],{"class":202,"line":491},[372,2104,2105],{"class":381},"    QColor(",[372,2107,2108],{"class":417},"\"#f6f3ea\"",[372,2110,2111],{"class":381},"),   ",[372,2113,2114],{"class":401},"# start\n",[372,2116,2117,2119,2121,2123],{"class":202,"line":496},[372,2118,2105],{"class":381},[372,2120,579],{"class":417},[372,2122,2111],{"class":381},[372,2124,2125],{"class":401},"# end\n",[372,2127,2128,2132,2134,2137],{"class":202,"line":514},[372,2129,2131],{"class":2130},"s9osk","    discrete",[372,2133,411],{"class":377},[372,2135,2136],{"class":424},"False",[372,2138,568],{"class":381},[372,2140,2141,2144,2146,2149,2152,2154,2156],{"class":202,"line":525},[372,2142,2143],{"class":2130},"    stops",[372,2145,411],{"class":377},[372,2147,2148],{"class":381},"[QgsGradientStop(",[372,2150,2151],{"class":424},"0.5",[372,2153,1865],{"class":381},[372,2155,1868],{"class":417},[372,2157,2158],{"class":381},"))],\n",[372,2160,2161],{"class":202,"line":534},[372,2162,734],{"class":381},[372,2164,2165],{"class":202,"line":539},[372,2166,395],{"emptyLinePlaceholder":394},[372,2168,2169,2172,2175,2178],{"class":202,"line":545},[372,2170,2171],{"class":377},"if",[372,2173,2174],{"class":381}," style.colorRamp(",[372,2176,2177],{"class":417},"\"brand_teal\"",[372,2179,2180],{"class":381},"):\n",[372,2182,2183,2186,2188],{"class":202,"line":556},[372,2184,2185],{"class":381},"    style.removeColorRamp(",[372,2187,2177],{"class":417},[372,2189,734],{"class":381},[372,2191,2192,2195,2197],{"class":202,"line":571},[372,2193,2194],{"class":381},"style.addColorRamp(",[372,2196,2177],{"class":417},[372,2198,2199],{"class":381},", brand_ramp)\n",[372,2201,2202],{"class":202,"line":584},[372,2203,395],{"emptyLinePlaceholder":394},[372,2205,2206],{"class":202,"line":597},[372,2207,2208],{"class":401},"# Retrieve it later by name from anywhere in the session.\n",[372,2210,2211,2213,2215,2217,2219],{"class":202,"line":603},[372,2212,1221],{"class":381},[372,2214,411],{"class":377},[372,2216,2174],{"class":381},[372,2218,2177],{"class":417},[372,2220,734],{"class":381},[372,2222,2223,2225,2227,2230,2233,2235],{"class":202,"line":609},[372,2224,449],{"class":424},[372,2226,452],{"class":381},[372,2228,2229],{"class":417},"\"Mid color:\"",[372,2231,2232],{"class":381},", ramp.color(",[372,2234,2151],{"class":424},[372,2236,2237],{"class":381},").name())\n",[14,2239,2240,1596,2242,2245,2246,2249,2250,2253,2254,2257,2258,2261,2262,2265,2266,2268],{},[21,2241,620],{},[261,2243,2244],{},"colorRampNames()"," enumerates what is available; ",[261,2247,2248],{},"QgsGradientColorRamp"," builds a custom gradient with intermediate ",[261,2251,2252],{},"QgsGradientStop","s. Registering it under a name with ",[261,2255,2256],{},"addColorRamp()"," makes it retrievable by ",[261,2259,2260],{},"colorRamp(\"brand_teal\")"," for the rest of the session, so a graduated renderer or shader can pull the exact same palette. To persist a ramp across sessions, save the style database with ",[261,2263,2264],{},"style.exportXml(path)",". The ",[261,2267,2252],{}," constructor signature has been stable since 3.x; on 3.28 the keyword form shown here works identically.",[239,2270,2272],{"id":2271},"saving-reusing-styles","Saving & Reusing Styles",[14,2274,2275,2276,2279,2280,2283,2284,2287,2288,2291,2292,2287,2295,2298],{},"Styling logic you build in a script should be persistable. QGIS supports two interchange formats: ",[21,2277,2278],{},"QML"," (its native, full-fidelity style XML) and ",[21,2281,2282],{},"SLD"," (OGC Styled Layer Descriptor, portable to GeoServer and other OGC servers but lossy for advanced QGIS features). Every map layer exposes ",[261,2285,2286],{},"saveNamedStyle()"," \u002F ",[261,2289,2290],{},"loadNamedStyle()"," for QML and ",[261,2293,2294],{},"saveSldStyle()",[261,2296,2297],{},"loadSldStyle()"," for SLD.",[363,2300,2302],{"className":365,"code":2301,"language":367,"meta":368,"style":368},"from qgis.core import QgsProject\nfrom qgis.PyQt.QtXml import QDomDocument\n\nsource = QgsProject.instance().mapLayersByName(\"counties\")[0]\n\n# Persist the fully-classified style to disk as QML, and as portable SLD.\nsource.saveNamedStyle(\"\u002Fdata\u002Fstyles\u002Fcounties.qml\")\nsource.saveSldStyle(\"\u002Fdata\u002Fstyles\u002Fcounties.sld\")\n\n# Copy a style between layers in memory, with no file round-trip.\ntarget = QgsProject.instance().mapLayersByName(\"counties_2020\")[0]\n\ndoc = QDomDocument()\nsource.exportNamedStyle(doc)        # serialize source renderer + labeling\ntarget.importNamedStyle(doc)        # apply it to the second layer\ntarget.triggerRepaint()\n",[261,2303,2304,2315,2327,2331,2348,2352,2357,2367,2377,2381,2386,2404,2408,2418,2426,2434],{"__ignoreMap":368},[372,2305,2306,2308,2310,2312],{"class":202,"line":374},[372,2307,378],{"class":377},[372,2309,382],{"class":381},[372,2311,385],{"class":377},[372,2313,2314],{"class":381}," QgsProject\n",[372,2316,2317,2319,2322,2324],{"class":202,"line":391},[372,2318,378],{"class":377},[372,2320,2321],{"class":381}," qgis.PyQt.QtXml ",[372,2323,385],{"class":377},[372,2325,2326],{"class":381}," QDomDocument\n",[372,2328,2329],{"class":202,"line":398},[372,2330,395],{"emptyLinePlaceholder":394},[372,2332,2333,2336,2338,2340,2342,2344,2346],{"class":202,"line":405},[372,2334,2335],{"class":381},"source ",[372,2337,411],{"class":377},[372,2339,414],{"class":381},[372,2341,1173],{"class":417},[372,2343,421],{"class":381},[372,2345,59],{"class":424},[372,2347,427],{"class":381},[372,2349,2350],{"class":202,"line":430},[372,2351,395],{"emptyLinePlaceholder":394},[372,2353,2354],{"class":202,"line":435},[372,2355,2356],{"class":401},"# Persist the fully-classified style to disk as QML, and as portable SLD.\n",[372,2358,2359,2362,2365],{"class":202,"line":446},[372,2360,2361],{"class":381},"source.saveNamedStyle(",[372,2363,2364],{"class":417},"\"\u002Fdata\u002Fstyles\u002Fcounties.qml\"",[372,2366,734],{"class":381},[372,2368,2369,2372,2375],{"class":202,"line":464},[372,2370,2371],{"class":381},"source.saveSldStyle(",[372,2373,2374],{"class":417},"\"\u002Fdata\u002Fstyles\u002Fcounties.sld\"",[372,2376,734],{"class":381},[372,2378,2379],{"class":202,"line":478},[372,2380,395],{"emptyLinePlaceholder":394},[372,2382,2383],{"class":202,"line":491},[372,2384,2385],{"class":401},"# Copy a style between layers in memory, with no file round-trip.\n",[372,2387,2388,2391,2393,2395,2398,2400,2402],{"class":202,"line":496},[372,2389,2390],{"class":381},"target ",[372,2392,411],{"class":377},[372,2394,414],{"class":381},[372,2396,2397],{"class":417},"\"counties_2020\"",[372,2399,421],{"class":381},[372,2401,59],{"class":424},[372,2403,427],{"class":381},[372,2405,2406],{"class":202,"line":514},[372,2407,395],{"emptyLinePlaceholder":394},[372,2409,2410,2413,2415],{"class":202,"line":525},[372,2411,2412],{"class":381},"doc ",[372,2414,411],{"class":377},[372,2416,2417],{"class":381}," QDomDocument()\n",[372,2419,2420,2423],{"class":202,"line":534},[372,2421,2422],{"class":381},"source.exportNamedStyle(doc)        ",[372,2424,2425],{"class":401},"# serialize source renderer + labeling\n",[372,2427,2428,2431],{"class":202,"line":539},[372,2429,2430],{"class":381},"target.importNamedStyle(doc)        ",[372,2432,2433],{"class":401},"# apply it to the second layer\n",[372,2435,2436],{"class":202,"line":545},[372,2437,2438],{"class":381},"target.triggerRepaint()\n",[14,2440,2441,1596,2443,2445,2446,2449,2450,2452,2453,2456,2457,2460,2461,2464,2465,2468,2469,2472],{},[21,2442,620],{},[261,2444,2286],{}," writes a ",[261,2447,2448],{},".qml"," that captures the renderer, labeling, and ramps exactly; ",[261,2451,2294],{}," emits OGC SLD for server-side reuse. To clone a style onto another layer without writing a file, serialize the source into a ",[261,2454,2455],{},"QDomDocument"," with ",[261,2458,2459],{},"exportNamedStyle()"," and read it back with ",[261,2462,2463],{},"importNamedStyle()"," — the most reliable in-memory copy. A separate concept worth knowing is ",[261,2466,2467],{},"layer.styleManager()",", which manages ",[652,2470,2471],{},"multiple named styles attached to one layer"," rather than copying the active style between layers. Prefer QML when staying inside QGIS; reach for SLD only when interoperating with non-QGIS OGC software.",[14,2474,2475,2476,2478,2479,2483],{},"A common automation pattern is to build one perfectly-styled template layer, save its QML once, then ",[261,2477,2290],{}," it onto every freshly-processed layer in a batch — see ",[255,2480,2482],{"href":2481},"\u002Fspatial-data-processing-automation\u002Fbatch-processing-with-pyqgis\u002F","Batch Processing with PyQGIS"," for the iteration scaffolding.",[239,2485,2487],{"id":2486},"from-canvas-to-deliverable","From Canvas to Deliverable",[14,2489,2490,2491,2494,2495,2498,2499,2502,2503,2506,2507,1090],{},"Styling pays off when it ships. Two output paths exist. For a quick image of the live canvas, ",[261,2492,2493],{},"QgsMapRendererParallelJob"," renders the current map to a ",[261,2496,2497],{},"QImage",". For publication-quality output with legends, scale bars, and titles, you build a ",[261,2500,2501],{},"QgsPrintLayout",", add a ",[261,2504,2505],{},"QgsLayoutItemMap",", and export with ",[261,2508,2509],{},"QgsLayoutExporter",[363,2511,2513],{"className":365,"code":2512,"language":367,"meta":368,"style":368},"from qgis.core import (\n    QgsProject,\n    QgsPrintLayout,\n    QgsLayoutItemMap,\n    QgsLayoutExporter,\n    QgsLayoutPoint,\n    QgsLayoutSize,\n    QgsUnitTypes,\n)\n\nproject = QgsProject.instance()\nlayout = QgsPrintLayout(project)\nlayout.initializeDefaults()      # A4 landscape page\nlayout.setName(\"auto_export\")\n\nmap_item = QgsLayoutItemMap(layout)\nmap_item.attemptMove(QgsLayoutPoint(10, 10, QgsUnitTypes.LayoutMillimeters))\nmap_item.attemptResize(QgsLayoutSize(277, 190, QgsUnitTypes.LayoutMillimeters))\nmap_item.setExtent(project.mapLayersByName(\"counties\")[0].extent())\nlayout.addLayoutItem(map_item)\n\nexporter = QgsLayoutExporter(layout)\nexporter.exportToPdf(\"\u002Fdata\u002Fout\u002Fcounties_map.pdf\",\n                     QgsLayoutExporter.PdfExportSettings())\n",[261,2514,2515,2525,2529,2534,2539,2544,2549,2554,2559,2563,2567,2577,2587,2595,2605,2609,2619,2633,2647,2661,2666,2670,2680,2690],{"__ignoreMap":368},[372,2516,2517,2519,2521,2523],{"class":202,"line":374},[372,2518,378],{"class":377},[372,2520,382],{"class":381},[372,2522,385],{"class":377},[372,2524,704],{"class":381},[372,2526,2527],{"class":202,"line":391},[372,2528,709],{"class":381},[372,2530,2531],{"class":202,"line":398},[372,2532,2533],{"class":381},"    QgsPrintLayout,\n",[372,2535,2536],{"class":202,"line":405},[372,2537,2538],{"class":381},"    QgsLayoutItemMap,\n",[372,2540,2541],{"class":202,"line":430},[372,2542,2543],{"class":381},"    QgsLayoutExporter,\n",[372,2545,2546],{"class":202,"line":435},[372,2547,2548],{"class":381},"    QgsLayoutPoint,\n",[372,2550,2551],{"class":202,"line":446},[372,2552,2553],{"class":381},"    QgsLayoutSize,\n",[372,2555,2556],{"class":202,"line":464},[372,2557,2558],{"class":381},"    QgsUnitTypes,\n",[372,2560,2561],{"class":202,"line":478},[372,2562,734],{"class":381},[372,2564,2565],{"class":202,"line":491},[372,2566,395],{"emptyLinePlaceholder":394},[372,2568,2569,2572,2574],{"class":202,"line":496},[372,2570,2571],{"class":381},"project ",[372,2573,411],{"class":377},[372,2575,2576],{"class":381}," QgsProject.instance()\n",[372,2578,2579,2582,2584],{"class":202,"line":514},[372,2580,2581],{"class":381},"layout ",[372,2583,411],{"class":377},[372,2585,2586],{"class":381}," QgsPrintLayout(project)\n",[372,2588,2589,2592],{"class":202,"line":525},[372,2590,2591],{"class":381},"layout.initializeDefaults()      ",[372,2593,2594],{"class":401},"# A4 landscape page\n",[372,2596,2597,2600,2603],{"class":202,"line":534},[372,2598,2599],{"class":381},"layout.setName(",[372,2601,2602],{"class":417},"\"auto_export\"",[372,2604,734],{"class":381},[372,2606,2607],{"class":202,"line":539},[372,2608,395],{"emptyLinePlaceholder":394},[372,2610,2611,2614,2616],{"class":202,"line":545},[372,2612,2613],{"class":381},"map_item ",[372,2615,411],{"class":377},[372,2617,2618],{"class":381}," QgsLayoutItemMap(layout)\n",[372,2620,2621,2624,2626,2628,2630],{"class":202,"line":556},[372,2622,2623],{"class":381},"map_item.attemptMove(QgsLayoutPoint(",[372,2625,229],{"class":424},[372,2627,338],{"class":381},[372,2629,229],{"class":424},[372,2631,2632],{"class":381},", QgsUnitTypes.LayoutMillimeters))\n",[372,2634,2635,2638,2641,2643,2645],{"class":202,"line":571},[372,2636,2637],{"class":381},"map_item.attemptResize(QgsLayoutSize(",[372,2639,2640],{"class":424},"277",[372,2642,338],{"class":381},[372,2644,157],{"class":424},[372,2646,2632],{"class":381},[372,2648,2649,2652,2654,2656,2658],{"class":202,"line":584},[372,2650,2651],{"class":381},"map_item.setExtent(project.mapLayersByName(",[372,2653,1173],{"class":417},[372,2655,421],{"class":381},[372,2657,59],{"class":424},[372,2659,2660],{"class":381},"].extent())\n",[372,2662,2663],{"class":202,"line":597},[372,2664,2665],{"class":381},"layout.addLayoutItem(map_item)\n",[372,2667,2668],{"class":202,"line":603},[372,2669,395],{"emptyLinePlaceholder":394},[372,2671,2672,2675,2677],{"class":202,"line":609},[372,2673,2674],{"class":381},"exporter ",[372,2676,411],{"class":377},[372,2678,2679],{"class":381}," QgsLayoutExporter(layout)\n",[372,2681,2682,2685,2688],{"class":202,"line":859},[372,2683,2684],{"class":381},"exporter.exportToPdf(",[372,2686,2687],{"class":417},"\"\u002Fdata\u002Fout\u002Fcounties_map.pdf\"",[372,2689,568],{"class":381},[372,2691,2692],{"class":202,"line":878},[372,2693,2694],{"class":381},"                     QgsLayoutExporter.PdfExportSettings())\n",[14,2696,2697,1596,2699,2702,2703,2705,2706,2709,2710,2713],{},[21,2698,620],{},[261,2700,2701],{},"initializeDefaults()"," gives a standard page; ",[261,2704,2505],{}," is the window onto your styled layers, positioned and sized in millimeters and zoomed via ",[261,2707,2708],{},"setExtent()",". ",[261,2711,2712],{},"QgsLayoutExporter.exportToPdf()"," flattens the layout — every renderer, label, and ramp you configured — into a PDF. Because the map item draws whatever symbology the layers currently carry, all the styling covered earlier flows straight through to the deliverable.",[14,2715,2716,2717,2720,2721,2724,2725,2728,2729,2732,2733,2736,2737,2740],{},"A few export details determine whether the result is publication-grade. Set DPI explicitly on the ",[261,2718,2719],{},"PdfExportSettings"," (300 for print, 96 for screen) and enable ",[261,2722,2723],{},"rasterizeWholeImage = False"," so vector symbology stays crisp and selectable in the PDF. For raster image output, ",[261,2726,2727],{},"exportToImage()"," takes a ",[261,2730,2731],{},"QgsLayoutExporter.ImageExportSettings"," where you fix the pixel dimensions or DPI. When a layout contains a legend (",[261,2734,2735],{},"QgsLayoutItemLegend","), call ",[261,2738,2739],{},"legend.setAutoUpdateModel(True)"," so it reflects the current renderer classes — otherwise a graduated legend can drift out of sync with the breaks you computed at runtime. Because the exporter renders the same renderer, symbol, and label objects you configured earlier, the discipline of styling correctly upstream is what makes a hands-off, repeatable export pipeline possible.",[14,2742,2743,2744,2748],{},"For multi-map and multi-page production — atlases driven by a coverage layer, batch PDF exports — the ",[255,2745,2747],{"href":2746},"\u002Fspatial-data-processing-automation\u002Fautomated-map-layout-generation\u002F","Automated Map Layout Generation"," cluster in the processing pillar extends exactly this pattern.",[239,2750,2752],{"id":2751},"key-takeaways","Key Takeaways",[247,2754,2755,2768,2784,2793,2802,2813,2830],{},[250,2756,2757,2758,2761,2762,1023,2765,2767],{},"Rendering is a chain: ",[21,2759,2760],{},"layer → renderer → symbol → symbol layers",", with labeling configured separately and merged at draw time. Master ",[261,2763,2764],{},"renderer()",[261,2766,1049],{}," and you control the whole pipeline.",[250,2769,2770,2771,2773,2774,2777,2778,1023,2780,1023,2782,1090],{},"Use ",[261,2772,627],{}," with property dictionaries for fast single-symbol styling; branch on ",[261,2775,2776],{},"geometryType()"," to pick the right ",[261,2779,337],{},[261,2781,341],{},[261,2783,345],{},[250,2785,2786,2787,2789,2790,2792],{},"Thematic maps come from ",[261,2788,319],{}," (nominal) and ",[261,2791,323],{}," (numeric); the classification method (equal interval, quantile, Jenks) shapes the story.",[250,2794,2795,2796,2798,2799,2801],{},"Labels live in ",[261,2797,285],{}," wrapped by ",[261,2800,1336],{},"; always add a text buffer for legibility.",[250,2803,2804,2805,2807,2808,2810,2811,1090],{},"Raster visualization uses ",[261,2806,1687],{}," + ",[261,2809,1691],{},", fitted to real ",[261,2812,1944],{},[250,2814,2815,2816,2818,2819,2821,2822,2825,2826,1023,2828,1090],{},"Centralize palettes in ",[261,2817,2016],{},", persist styles as ",[261,2820,2448],{}," (native) or ",[261,2823,2824],{},".sld"," (portable), and copy them between layers via ",[261,2827,2459],{},[261,2829,2463],{},[250,2831,2832,2833,2835,2836,2838],{},"Always call ",[261,2834,631],{}," after changing symbology, and feed styled layers into ",[261,2837,2509],{}," for export-ready maps.",[239,2840,2842],{"id":2841},"frequently-asked-questions","Frequently Asked Questions",[14,2844,2845,2848,2849,2852,2853,338,2855,2858],{},[21,2846,2847],{},"Why don't my styling changes show up after I set a new renderer?","\nThe renderer is updated in memory, but the canvas caches the last render. Call ",[261,2850,2851],{},"layer.triggerRepaint()"," after ",[261,2854,1049],{},[261,2856,2857],{},"setLabeling()",", or any symbol change. If the layer is in a layout map item, the layout picks up the change on its next refresh or export automatically.",[14,2860,2861,2864,2865,2867,2868,2870,2871,320,2874,2877],{},[21,2862,2863],{},"What is the difference between a symbol and a symbol layer?","\nA ",[261,2866,263],{}," is the complete visual for one feature class; a ",[261,2869,352],{}," is one drawing pass inside it. Stacking symbol layers (for example a solid fill beneath a hatch) is how QGIS builds complex cartography. ",[261,2872,2873],{},"symbol.symbolLayerCount()",[261,2875,2876],{},"symbol.symbolLayer(i)"," let you inspect and edit each pass independently.",[14,2879,2880,2883],{},[21,2881,2882],{},"Should I save styles as QML or SLD?","\nUse QML when the style stays within QGIS — it captures every feature, including data-defined overrides, blend modes, and rule-based labeling, with full fidelity. Use SLD only when you must share styling with OGC servers like GeoServer or MapServer; SLD cannot represent some QGIS-specific symbology and will silently drop it.",[14,2885,2886,2889,2890,2892],{},[21,2887,2888],{},"Which classification method should I use for a choropleth?","\nFor most thematic maps, natural breaks (Jenks) groups similar values and exposes real clusters in the data. Quantile guarantees equal feature counts per class, which is good for ranked comparisons but can mask outliers. Equal interval is honest about absolute value ranges but produces lopsided classes on skewed data. The ",[255,2891,272],{"href":271}," cluster compares them with worked examples.",[14,2894,2895,2898,2899,2902,2903,2906,2907,2909,2910,2912],{},[21,2896,2897],{},"Can I style layers in a headless script with no GUI?","\nYes. All the renderer, symbol, and labeling classes live in ",[261,2900,2901],{},"qgis.core"," and work under a standalone ",[261,2904,2905],{},"QgsApplication"," with no canvas. You can classify, label, and export to PDF entirely headless via ",[261,2908,2509],{}," — useful in CI and scheduled jobs. See the ",[255,2911,646],{"href":645}," pillar for initializing QGIS outside the desktop app.",[239,2914,2916],{"id":2915},"related","Related",[247,2918,2919,2923,2927,2931,2935,2940],{},[250,2920,2921],{},[255,2922,258],{"href":257},[250,2924,2925],{},[255,2926,272],{"href":271},[250,2928,2929],{},[255,2930,281],{"href":280},[250,2932,2933],{},[255,2934,646],{"href":645},[250,2936,2937],{},[255,2938,2939],{"href":2004},"Spatial Data Processing & Automation with PyQGIS",[250,2941,2942],{},[255,2943,2945],{"href":2944},"\u002Fqgis-plugin-development\u002F","QGIS Plugin Development Guide",[2947,2948,2949],"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 .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}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 .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}",{"title":368,"searchDepth":391,"depth":391,"links":2951},[2952,2953,2954,2955,2956,2957,2958,2959,2960,2961,2962,2963],{"id":241,"depth":391,"text":242},{"id":292,"depth":391,"text":293},{"id":672,"depth":391,"text":673},{"id":1093,"depth":391,"text":1094},{"id":1327,"depth":391,"text":281},{"id":1680,"depth":391,"text":1681},{"id":2009,"depth":391,"text":2010},{"id":2271,"depth":391,"text":2272},{"id":2486,"depth":391,"text":2487},{"id":2751,"depth":391,"text":2752},{"id":2841,"depth":391,"text":2842},{"id":2915,"depth":391,"text":2916},"Style vector and raster layers programmatically with PyQGIS — renderers, symbols, labeling, color ramps, and reusable QML\u002FSLD styles for publication maps.","md",{},"\u002Fpyqgis-cartography-visualization",{"title":5,"description":2964},"pyqgis-cartography-visualization\u002Findex","OylJn-VGOGtk9YXHxIhWdDKfNVxlzQfhe08brrTk3yc",1781792483472]