[{"data":1,"prerenderedAt":2305},["ShallowReactive",2],{"doc:\u002Fpyqgis-cartography-visualization\u002Fgraduated-categorized-renderers":3},{"id":4,"title":5,"body":6,"description":2298,"extension":2299,"meta":2300,"navigation":359,"path":2301,"seo":2302,"stem":2303,"__hash__":2304},"docs\u002Fpyqgis-cartography-visualization\u002Fgraduated-categorized-renderers\u002Findex.md","Graduated & Categorized Renderers in PyQGIS",{"type":7,"value":8,"toc":2282},"minimark",[9,14,33,51,62,67,118,122,133,282,286,296,519,548,563,566,643,648,652,658,797,819,823,830,989,1018,1022,1029,1198,1223,1279,1299,1303,1313,1479,1498,1502,1505,1743,1769,1773,1780,1933,1959,1963,1966,2032,2053,2057,2066,2131,2137,2141,2182,2186,2192,2205,2215,2229,2250,2254,2278],[10,11,13],"h1",{"id":12},"graduated-and-categorized-renderers-in-pyqgis","Graduated and Categorized Renderers in PyQGIS",[15,16,17,18,22,23,26,27,32],"p",{},"Thematic mapping turns raw attribute tables into maps that communicate patterns at a glance. The two renderers at the heart of this work are ",[19,20,21],"code",{},"QgsCategorizedSymbolRenderer",", which assigns one symbol per distinct attribute value, and ",[19,24,25],{},"QgsGraduatedSymbolRenderer",", which slices a continuous numeric field into classified ranges. Mastering both — along with the classification methods and color ramps that drive them — lets you generate consistent, reproducible cartography entirely from code. This guide sits inside ",[28,29,31],"a",{"href":30},"\u002Fpyqgis-cartography-visualization\u002F","PyQGIS Cartography & Data Visualization"," and focuses specifically on data-driven symbol assignment: how QGIS decides which feature gets which color or size, and how you control that decision programmatically.",[15,34,35,36,40,41,45,46,50],{},"Where ",[28,37,39],{"href":38},"\u002Fpyqgis-cartography-visualization\u002Fprogrammatic-layer-styling\u002F","Programmatic Layer Styling"," deals with single symbols and per-feature properties, and ",[28,42,44],{"href":43},"\u002Fpyqgis-cartography-visualization\u002Flabeling-and-annotations\u002F","Labeling & Annotations"," deals with text, this page is about ",[47,48,49],"em",{},"classification"," — grouping features and mapping each group to a visual variable.",[15,52,53,54,57,58,61],{},"Both renderers share the same underlying model: a layer holds exactly one renderer, and that renderer owns a collection of class definitions plus an optional color ramp. A categorized renderer's collection is a list of ",[19,55,56],{},"QgsRendererCategory"," objects; a graduated renderer's is a list of ",[19,59,60],{},"QgsRendererRange"," objects. Understanding that shared structure is what lets you write generic styling code that branches only where the two genuinely differ — discovering categories versus computing numeric breaks. The sections below build that understanding step by step, from creating each renderer to inspecting, recoloring, and persisting it.",[63,64,66],"h2",{"id":65},"prerequisites","Prerequisites",[68,69,70,78,89,104],"ul",{},[71,72,73,77],"li",{},[74,75,76],"strong",{},"QGIS 3.34 LTR"," (bundles Python 3.12) or a comparable 3.x release with PyQGIS available.",[71,79,80,81,84,85,88],{},"A vector layer loaded in the project with at least one categorical field (e.g. ",[19,82,83],{},"land_use",") and one numeric field (e.g. ",[19,86,87],{},"population",").",[71,90,91,92,95,96,99,100,103],{},"Access to the ",[74,93,94],{},"QGIS Python Console"," (",[19,97,98],{},"Ctrl+Alt+P",") or a standalone script with an initialized ",[19,101,102],{},"QgsApplication",".",[71,105,106,107,110,111,114,115,103],{},"Familiarity with ",[19,108,109],{},"QgsVectorLayer",", ",[19,112,113],{},"QgsSymbol",", and how to retrieve layers via ",[19,116,117],{},"QgsProject.instance()",[63,119,121],{"id":120},"categorized-vs-graduated-at-a-glance","Categorized vs Graduated at a Glance",[15,123,124,125,128,129,132],{},"The single most common mistake is reaching for the wrong renderer. Categorized renderers answer ",[47,126,127],{},"\"what kind is it?\""," (nominal data), while graduated renderers answer ",[47,130,131],{},"\"how much of it is there?\""," (ordinal or ratio data). The diagram below contrasts how each maps source data to symbols.",[134,135,140,141,140,145,140,149,140,156,140,165,140,168,140,175,140,179,140,185,140,188,140,194,140,201,140,205,140,209,140,213,140,217,140,221,140,225,140,230,140,234,140,238,140,241,140,244,140,247,140,250,140,253,140,256,140,259,140,266,140,271,140,275,140,277,140,279],"svg",{"viewBox":136,"role":137,"ariaLabel":138,"xmlns":139},"0 0 640 360","img","Comparison of categorized and graduated renderers in PyQGIS","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","\n  ",[142,143,144],"title",{},"Categorized versus graduated renderer data flow",[146,147,148],"desc",{},"Left panel shows discrete attribute values each mapped to a distinct color; right panel shows a numeric range split into ordered classes mapped to a sequential color ramp.",[150,151],"rect",{"x":152,"y":152,"width":153,"height":154,"fill":155},"0","640","360","#f6f3ea",[150,157],{"x":158,"y":158,"width":159,"height":160,"rx":161,"fill":162,"stroke":163,"style":164},"20","290","320","8","#fffdf7","#17211d","stroke-width:2",[150,166],{"x":167,"y":158,"width":159,"height":160,"rx":161,"fill":162,"stroke":163,"style":164},"330",[169,170,174],"text",{"x":171,"y":172,"fill":163,"style":173},"165","48","text-anchor:middle;font-family:sans-serif;font-size:18px;font-weight:bold","Categorized",[169,176,178],{"x":177,"y":172,"fill":163,"style":173},"475","Graduated",[169,180,184],{"x":171,"y":181,"fill":182,"style":183},"68","#2f3b35","text-anchor:middle;font-family:sans-serif;font-size:12px","one symbol per unique value",[169,186,187],{"x":177,"y":181,"fill":182,"style":183},"numeric ranges into classes",[169,189,193],{"x":190,"y":191,"fill":182,"style":192},"40","100","font-family:monospace;font-size:13px","\"forest\"",[150,195],{"x":196,"y":197,"width":198,"height":199,"fill":200},"200","88","90","18","#15803d",[169,202,204],{"x":190,"y":203,"fill":182,"style":192},"132","\"urban\"",[150,206],{"x":196,"y":207,"width":198,"height":199,"fill":208},"120","#b45309",[169,210,212],{"x":190,"y":211,"fill":182,"style":192},"164","\"water\"",[150,214],{"x":196,"y":215,"width":198,"height":199,"fill":216},"152","#2563eb",[169,218,220],{"x":190,"y":219,"fill":182,"style":192},"196","\"crop\"",[150,222],{"x":196,"y":223,"width":198,"height":199,"fill":224},"184","#22c55e",[169,226,229],{"x":171,"y":227,"fill":228,"style":183},"245","#0f766e","distinct, unordered colors",[169,231,233],{"x":232,"y":191,"fill":182,"style":192},"350","0 – 50",[150,235],{"x":236,"y":197,"width":207,"height":199,"fill":237},"470","#edf8e9",[169,239,240],{"x":232,"y":203,"fill":182,"style":192},"50 – 120",[150,242],{"x":236,"y":207,"width":207,"height":199,"fill":243},"#a1d99b",[169,245,246],{"x":232,"y":211,"fill":182,"style":192},"120 – 300",[150,248],{"x":236,"y":215,"width":207,"height":199,"fill":249},"#41ab5d",[169,251,252],{"x":232,"y":219,"fill":182,"style":192},"300 – 900",[150,254],{"x":236,"y":223,"width":207,"height":199,"fill":255},"#005a32",[169,257,258],{"x":177,"y":227,"fill":228,"style":183},"ordered sequential ramp",[150,260],{"x":190,"y":261,"width":262,"height":263,"rx":264,"fill":265},"270","250","50","6","#26322d",[169,267,21],{"x":171,"y":268,"fill":269,"style":270},"292","#d9f99d","text-anchor:middle;font-family:monospace;font-size:11px",[169,272,274],{"x":171,"y":273,"fill":269,"style":270},"310",".createCategories()",[150,276],{"x":232,"y":261,"width":262,"height":263,"rx":264,"fill":265},[169,278,25],{"x":177,"y":268,"fill":269,"style":270},[169,280,281],{"x":177,"y":273,"fill":269,"style":270},".updateClasses()",[63,283,285],{"id":284},"_1-categorizing-a-layer-by-a-unique-attribute-value","1. Categorizing a Layer by a Unique Attribute Value",[15,287,288,289,291,292,295],{},"A categorized renderer iterates the distinct values of a field and binds each to its own ",[19,290,113],{},". The clean modern path is ",[19,293,294],{},"QgsCategorizedSymbolRenderer.createCategories()",", which collects the unique values, generates symbols from a template, and applies a color ramp in one call.",[297,298,303],"pre",{"className":299,"code":300,"language":301,"meta":302,"style":302},"language-python shiki shiki-themes github-dark","from qgis.core import (\n    QgsProject,\n    QgsCategorizedSymbolRenderer,\n    QgsSymbol,\n    QgsStyle,\n)\n\nlayer = QgsProject.instance().mapLayersByName(\"land_cover\")[0]\nfield_name = \"land_use\"\n\n# Template symbol carries geometry-appropriate defaults (fill, stroke).\ntemplate_symbol = QgsSymbol.defaultSymbol(layer.geometryType())\n\n# Pick a qualitative ramp so unordered categories read as distinct.\ndefault_style = QgsStyle.defaultStyle()\ncolor_ramp = default_style.colorRamp(\"Spectral\")\n\ncategories = QgsCategorizedSymbolRenderer.createCategories(\n    [], template_symbol, layer, field_name\n)\nrenderer = QgsCategorizedSymbolRenderer(field_name, categories)\nrenderer.updateColorRamp(color_ramp)\n\nlayer.setRenderer(renderer)\nlayer.triggerRepaint()\n","python","",[19,304,305,324,330,336,342,348,354,361,386,397,402,409,420,425,431,442,458,463,474,480,485,496,502,507,513],{"__ignoreMap":302},[306,307,310,314,318,321],"span",{"class":308,"line":309},"line",1,[306,311,313],{"class":312},"snl16","from",[306,315,317],{"class":316},"s95oV"," qgis.core ",[306,319,320],{"class":312},"import",[306,322,323],{"class":316}," (\n",[306,325,327],{"class":308,"line":326},2,[306,328,329],{"class":316},"    QgsProject,\n",[306,331,333],{"class":308,"line":332},3,[306,334,335],{"class":316},"    QgsCategorizedSymbolRenderer,\n",[306,337,339],{"class":308,"line":338},4,[306,340,341],{"class":316},"    QgsSymbol,\n",[306,343,345],{"class":308,"line":344},5,[306,346,347],{"class":316},"    QgsStyle,\n",[306,349,351],{"class":308,"line":350},6,[306,352,353],{"class":316},")\n",[306,355,357],{"class":308,"line":356},7,[306,358,360],{"emptyLinePlaceholder":359},true,"\n",[306,362,364,367,370,373,377,380,383],{"class":308,"line":363},8,[306,365,366],{"class":316},"layer ",[306,368,369],{"class":312},"=",[306,371,372],{"class":316}," QgsProject.instance().mapLayersByName(",[306,374,376],{"class":375},"sU2Wk","\"land_cover\"",[306,378,379],{"class":316},")[",[306,381,152],{"class":382},"sDLfK",[306,384,385],{"class":316},"]\n",[306,387,389,392,394],{"class":308,"line":388},9,[306,390,391],{"class":316},"field_name ",[306,393,369],{"class":312},[306,395,396],{"class":375}," \"land_use\"\n",[306,398,400],{"class":308,"line":399},10,[306,401,360],{"emptyLinePlaceholder":359},[306,403,405],{"class":308,"line":404},11,[306,406,408],{"class":407},"sAwPA","# Template symbol carries geometry-appropriate defaults (fill, stroke).\n",[306,410,412,415,417],{"class":308,"line":411},12,[306,413,414],{"class":316},"template_symbol ",[306,416,369],{"class":312},[306,418,419],{"class":316}," QgsSymbol.defaultSymbol(layer.geometryType())\n",[306,421,423],{"class":308,"line":422},13,[306,424,360],{"emptyLinePlaceholder":359},[306,426,428],{"class":308,"line":427},14,[306,429,430],{"class":407},"# Pick a qualitative ramp so unordered categories read as distinct.\n",[306,432,434,437,439],{"class":308,"line":433},15,[306,435,436],{"class":316},"default_style ",[306,438,369],{"class":312},[306,440,441],{"class":316}," QgsStyle.defaultStyle()\n",[306,443,445,448,450,453,456],{"class":308,"line":444},16,[306,446,447],{"class":316},"color_ramp ",[306,449,369],{"class":312},[306,451,452],{"class":316}," default_style.colorRamp(",[306,454,455],{"class":375},"\"Spectral\"",[306,457,353],{"class":316},[306,459,461],{"class":308,"line":460},17,[306,462,360],{"emptyLinePlaceholder":359},[306,464,466,469,471],{"class":308,"line":465},18,[306,467,468],{"class":316},"categories ",[306,470,369],{"class":312},[306,472,473],{"class":316}," QgsCategorizedSymbolRenderer.createCategories(\n",[306,475,477],{"class":308,"line":476},19,[306,478,479],{"class":316},"    [], template_symbol, layer, field_name\n",[306,481,483],{"class":308,"line":482},20,[306,484,353],{"class":316},[306,486,488,491,493],{"class":308,"line":487},21,[306,489,490],{"class":316},"renderer ",[306,492,369],{"class":312},[306,494,495],{"class":316}," QgsCategorizedSymbolRenderer(field_name, categories)\n",[306,497,499],{"class":308,"line":498},22,[306,500,501],{"class":316},"renderer.updateColorRamp(color_ramp)\n",[306,503,505],{"class":308,"line":504},23,[306,506,360],{"emptyLinePlaceholder":359},[306,508,510],{"class":308,"line":509},24,[306,511,512],{"class":316},"layer.setRenderer(renderer)\n",[306,514,516],{"class":308,"line":515},25,[306,517,518],{"class":316},"layer.triggerRepaint()\n",[15,520,521,524,525,528,529,531,532,535,536,539,540,543,544,547],{},[74,522,523],{},"Breakdown:"," ",[19,526,527],{},"createCategories([], ...)"," with an empty value list tells QGIS to scan the layer and discover every distinct value of ",[19,530,83],{},". ",[19,533,534],{},"QgsSymbol.defaultSymbol()"," returns a fill, line, or marker symbol matching the layer geometry, so the same code works for polygons or points. ",[19,537,538],{},"updateColorRamp()"," recolors all categories from the ramp in evenly spaced steps. Always call ",[19,541,542],{},"triggerRepaint()"," (or ",[19,545,546],{},"iface.mapCanvas().refresh()",") so the change is visible.",[15,549,550,551,554,555,558,559,562],{},"For nominal data, prefer a ",[47,552,553],{},"qualitative"," ramp such as ",[19,556,557],{},"Spectral"," or ",[19,560,561],{},"Set1"," rather than a sequential one — sequential ramps imply an order that categories do not have.",[15,564,565],{},"If you need to constrain the categories to a known set rather than discovering every value, pass that list explicitly instead of an empty one. This is useful when a field contains noise values you want to exclude, or when you want categories to appear in a deliberate order in the legend:",[297,567,569],{"className":299,"code":568,"language":301,"meta":302,"style":302},"# Only build categories for the three classes you care about,\n# in the order they should appear in the legend.\nwanted = [\"forest\", \"urban\", \"water\"]\ncategories = QgsCategorizedSymbolRenderer.createCategories(\n    wanted, template_symbol, layer, field_name\n)\nrenderer = QgsCategorizedSymbolRenderer(field_name, categories)\nrenderer.updateColorRamp(QgsStyle.defaultStyle().colorRamp(\"Set1\"))\nlayer.setRenderer(renderer)\n",[19,570,571,576,581,603,611,616,620,628,639],{"__ignoreMap":302},[306,572,573],{"class":308,"line":309},[306,574,575],{"class":407},"# Only build categories for the three classes you care about,\n",[306,577,578],{"class":308,"line":326},[306,579,580],{"class":407},"# in the order they should appear in the legend.\n",[306,582,583,586,588,591,593,595,597,599,601],{"class":308,"line":332},[306,584,585],{"class":316},"wanted ",[306,587,369],{"class":312},[306,589,590],{"class":316}," [",[306,592,193],{"class":375},[306,594,110],{"class":316},[306,596,204],{"class":375},[306,598,110],{"class":316},[306,600,212],{"class":375},[306,602,385],{"class":316},[306,604,605,607,609],{"class":308,"line":338},[306,606,468],{"class":316},[306,608,369],{"class":312},[306,610,473],{"class":316},[306,612,613],{"class":308,"line":344},[306,614,615],{"class":316},"    wanted, template_symbol, layer, field_name\n",[306,617,618],{"class":308,"line":350},[306,619,353],{"class":316},[306,621,622,624,626],{"class":308,"line":356},[306,623,490],{"class":316},[306,625,369],{"class":312},[306,627,495],{"class":316},[306,629,630,633,636],{"class":308,"line":363},[306,631,632],{"class":316},"renderer.updateColorRamp(QgsStyle.defaultStyle().colorRamp(",[306,634,635],{"class":375},"\"Set1\"",[306,637,638],{"class":316},"))\n",[306,640,641],{"class":308,"line":388},[306,642,512],{"class":316},[15,644,645,647],{},[74,646,523],{}," Supplying a non-empty value list short-circuits discovery, so QGIS builds one category per item you named and skips everything else. Any feature whose value is not in the list falls through to the renderer's \"all other values\" entry, which you can enable, hide, or restyle separately. This gives you full control over both membership and legend order, which automatic discovery (alphabetical by default) does not guarantee.",[63,649,651],{"id":650},"_2-adjusting-individual-categories","2. Adjusting Individual Categories",[15,653,654,655,657],{},"After building a categorized renderer you frequently need to recolor one class, rename a label, or hide a value. Each entry is a ",[19,656,56],{}," you can read and rewrite in place.",[297,659,661],{"className":299,"code":660,"language":301,"meta":302,"style":302},"from qgis.PyQt.QtGui import QColor\n\nrenderer = layer.renderer()  # QgsCategorizedSymbolRenderer\n\nfor i, category in enumerate(renderer.categories()):\n    value = category.value()\n    label = category.label()\n    if value == \"water\":\n        # Override the auto-generated color for a specific class.\n        symbol = category.symbol().clone()\n        symbol.setColor(QColor(\"#2563eb\"))\n        renderer.updateCategorySymbol(i, symbol)\n        renderer.updateCategoryLabel(i, \"Open Water\")\n\nlayer.triggerRepaint()\n",[19,662,663,675,679,691,695,712,722,732,749,754,764,774,779,789,793],{"__ignoreMap":302},[306,664,665,667,670,672],{"class":308,"line":309},[306,666,313],{"class":312},[306,668,669],{"class":316}," qgis.PyQt.QtGui ",[306,671,320],{"class":312},[306,673,674],{"class":316}," QColor\n",[306,676,677],{"class":308,"line":326},[306,678,360],{"emptyLinePlaceholder":359},[306,680,681,683,685,688],{"class":308,"line":332},[306,682,490],{"class":316},[306,684,369],{"class":312},[306,686,687],{"class":316}," layer.renderer()  ",[306,689,690],{"class":407},"# QgsCategorizedSymbolRenderer\n",[306,692,693],{"class":308,"line":338},[306,694,360],{"emptyLinePlaceholder":359},[306,696,697,700,703,706,709],{"class":308,"line":344},[306,698,699],{"class":312},"for",[306,701,702],{"class":316}," i, category ",[306,704,705],{"class":312},"in",[306,707,708],{"class":382}," enumerate",[306,710,711],{"class":316},"(renderer.categories()):\n",[306,713,714,717,719],{"class":308,"line":350},[306,715,716],{"class":316},"    value ",[306,718,369],{"class":312},[306,720,721],{"class":316}," category.value()\n",[306,723,724,727,729],{"class":308,"line":356},[306,725,726],{"class":316},"    label ",[306,728,369],{"class":312},[306,730,731],{"class":316}," category.label()\n",[306,733,734,737,740,743,746],{"class":308,"line":363},[306,735,736],{"class":312},"    if",[306,738,739],{"class":316}," value ",[306,741,742],{"class":312},"==",[306,744,745],{"class":375}," \"water\"",[306,747,748],{"class":316},":\n",[306,750,751],{"class":308,"line":388},[306,752,753],{"class":407},"        # Override the auto-generated color for a specific class.\n",[306,755,756,759,761],{"class":308,"line":399},[306,757,758],{"class":316},"        symbol ",[306,760,369],{"class":312},[306,762,763],{"class":316}," category.symbol().clone()\n",[306,765,766,769,772],{"class":308,"line":404},[306,767,768],{"class":316},"        symbol.setColor(QColor(",[306,770,771],{"class":375},"\"#2563eb\"",[306,773,638],{"class":316},[306,775,776],{"class":308,"line":411},[306,777,778],{"class":316},"        renderer.updateCategorySymbol(i, symbol)\n",[306,780,781,784,787],{"class":308,"line":422},[306,782,783],{"class":316},"        renderer.updateCategoryLabel(i, ",[306,785,786],{"class":375},"\"Open Water\"",[306,788,353],{"class":316},[306,790,791],{"class":308,"line":427},[306,792,360],{"emptyLinePlaceholder":359},[306,794,795],{"class":308,"line":433},[306,796,518],{"class":316},[15,798,799,524,801,804,805,808,809,812,813,816,817,103],{},[74,800,523],{},[19,802,803],{},"renderer.categories()"," returns the ordered list; the index ",[19,806,807],{},"i"," is what ",[19,810,811],{},"updateCategorySymbol()"," and ",[19,814,815],{},"updateCategoryLabel()"," expect. Cloning the symbol before mutating it avoids aliasing surprises where two categories share the same object. To control colors at the per-feature or per-symbol level rather than per-class, see ",[28,818,39],{"href":38},[63,820,822],{"id":821},"_3-graduating-a-numeric-field-into-classes","3. Graduating a Numeric Field into Classes",[15,824,825,826,829],{},"A graduated renderer maps a continuous field onto a small number of ordered classes. The workflow is: instantiate the renderer on the field, choose a classification method, set a class count and color ramp, then call ",[19,827,828],{},"updateClasses()"," to compute the break values.",[297,831,833],{"className":299,"code":832,"language":301,"meta":302,"style":302},"from qgis.core import (\n    QgsGraduatedSymbolRenderer,\n    QgsClassificationQuantile,\n    QgsStyle,\n    QgsSymbol,\n)\n\nlayer = QgsProject.instance().mapLayersByName(\"counties\")[0]\nfield_name = \"population\"\n\nrenderer = QgsGraduatedSymbolRenderer(field_name)\nrenderer.setSourceSymbol(QgsSymbol.defaultSymbol(layer.geometryType()))\n\n# Classification method drives where the class breaks fall.\nrenderer.setClassificationMethod(QgsClassificationQuantile())\n\n# Compute 5 quantile classes from the layer's population values.\nrenderer.updateClasses(layer, 5)\n\n# Apply a sequential ramp suited to ordered magnitude data.\nramp = QgsStyle.defaultStyle().colorRamp(\"Greens\")\nrenderer.updateColorRamp(ramp)\n\nlayer.setRenderer(renderer)\nlayer.triggerRepaint()\n",[19,834,835,845,850,855,859,863,867,871,888,897,901,910,915,919,924,929,933,938,948,952,957,972,977,981,985],{"__ignoreMap":302},[306,836,837,839,841,843],{"class":308,"line":309},[306,838,313],{"class":312},[306,840,317],{"class":316},[306,842,320],{"class":312},[306,844,323],{"class":316},[306,846,847],{"class":308,"line":326},[306,848,849],{"class":316},"    QgsGraduatedSymbolRenderer,\n",[306,851,852],{"class":308,"line":332},[306,853,854],{"class":316},"    QgsClassificationQuantile,\n",[306,856,857],{"class":308,"line":338},[306,858,347],{"class":316},[306,860,861],{"class":308,"line":344},[306,862,341],{"class":316},[306,864,865],{"class":308,"line":350},[306,866,353],{"class":316},[306,868,869],{"class":308,"line":356},[306,870,360],{"emptyLinePlaceholder":359},[306,872,873,875,877,879,882,884,886],{"class":308,"line":363},[306,874,366],{"class":316},[306,876,369],{"class":312},[306,878,372],{"class":316},[306,880,881],{"class":375},"\"counties\"",[306,883,379],{"class":316},[306,885,152],{"class":382},[306,887,385],{"class":316},[306,889,890,892,894],{"class":308,"line":388},[306,891,391],{"class":316},[306,893,369],{"class":312},[306,895,896],{"class":375}," \"population\"\n",[306,898,899],{"class":308,"line":399},[306,900,360],{"emptyLinePlaceholder":359},[306,902,903,905,907],{"class":308,"line":404},[306,904,490],{"class":316},[306,906,369],{"class":312},[306,908,909],{"class":316}," QgsGraduatedSymbolRenderer(field_name)\n",[306,911,912],{"class":308,"line":411},[306,913,914],{"class":316},"renderer.setSourceSymbol(QgsSymbol.defaultSymbol(layer.geometryType()))\n",[306,916,917],{"class":308,"line":422},[306,918,360],{"emptyLinePlaceholder":359},[306,920,921],{"class":308,"line":427},[306,922,923],{"class":407},"# Classification method drives where the class breaks fall.\n",[306,925,926],{"class":308,"line":433},[306,927,928],{"class":316},"renderer.setClassificationMethod(QgsClassificationQuantile())\n",[306,930,931],{"class":308,"line":444},[306,932,360],{"emptyLinePlaceholder":359},[306,934,935],{"class":308,"line":460},[306,936,937],{"class":407},"# Compute 5 quantile classes from the layer's population values.\n",[306,939,940,943,946],{"class":308,"line":465},[306,941,942],{"class":316},"renderer.updateClasses(layer, ",[306,944,945],{"class":382},"5",[306,947,353],{"class":316},[306,949,950],{"class":308,"line":476},[306,951,360],{"emptyLinePlaceholder":359},[306,953,954],{"class":308,"line":482},[306,955,956],{"class":407},"# Apply a sequential ramp suited to ordered magnitude data.\n",[306,958,959,962,964,967,970],{"class":308,"line":487},[306,960,961],{"class":316},"ramp ",[306,963,369],{"class":312},[306,965,966],{"class":316}," QgsStyle.defaultStyle().colorRamp(",[306,968,969],{"class":375},"\"Greens\"",[306,971,353],{"class":316},[306,973,974],{"class":308,"line":498},[306,975,976],{"class":316},"renderer.updateColorRamp(ramp)\n",[306,978,979],{"class":308,"line":504},[306,980,360],{"emptyLinePlaceholder":359},[306,982,983],{"class":308,"line":509},[306,984,512],{"class":316},[306,986,987],{"class":308,"line":515},[306,988,518],{"class":316},[15,990,991,524,993,996,997,1000,1001,1004,1005,1007,1008,110,1011,110,1014,1017],{},[74,992,523],{},[19,994,995],{},"setSourceSymbol()"," defines the base symbol that every class clones before recoloring. ",[19,998,999],{},"setClassificationMethod()"," plugs in the algorithm that decides break positions — swapping it changes the map dramatically. ",[19,1002,1003],{},"updateClasses(layer, 5)"," runs that method against the field and creates five ",[19,1006,60],{}," objects. Because magnitude has a natural order, a sequential ramp (",[19,1009,1010],{},"Greens",[19,1012,1013],{},"Blues",[19,1015,1016],{},"Viridis",") is correct here, unlike the qualitative ramp used for categories.",[63,1019,1021],{"id":1020},"_4-choosing-a-classification-method","4. Choosing a Classification Method",[15,1023,1024,1025,1028],{},"QGIS 3.x exposes classification as pluggable ",[19,1026,1027],{},"QgsClassificationMethod"," subclasses. The three you will reach for most are equal interval, quantile, and natural breaks. They produce different maps from identical data, so the choice is cartographic, not cosmetic.",[297,1030,1032],{"className":299,"code":1031,"language":301,"meta":302,"style":302},"from qgis.core import (\n    QgsClassificationEqualInterval,\n    QgsClassificationQuantile,\n    QgsClassificationJenks,\n)\n\nmethods = {\n    \"equal\": QgsClassificationEqualInterval(),\n    \"quantile\": QgsClassificationQuantile(),\n    \"jenks\": QgsClassificationJenks(),\n}\n\nfor name, method in methods.items():\n    renderer.setClassificationMethod(method)\n    renderer.updateClasses(layer, 5)\n    breaks = [f\"{r.lowerValue():.0f}-{r.upperValue():.0f}\"\n              for r in renderer.ranges()]\n    print(name, breaks)\n",[19,1033,1034,1044,1049,1053,1058,1062,1066,1076,1084,1092,1100,1105,1109,1121,1126,1135,1177,1190],{"__ignoreMap":302},[306,1035,1036,1038,1040,1042],{"class":308,"line":309},[306,1037,313],{"class":312},[306,1039,317],{"class":316},[306,1041,320],{"class":312},[306,1043,323],{"class":316},[306,1045,1046],{"class":308,"line":326},[306,1047,1048],{"class":316},"    QgsClassificationEqualInterval,\n",[306,1050,1051],{"class":308,"line":332},[306,1052,854],{"class":316},[306,1054,1055],{"class":308,"line":338},[306,1056,1057],{"class":316},"    QgsClassificationJenks,\n",[306,1059,1060],{"class":308,"line":344},[306,1061,353],{"class":316},[306,1063,1064],{"class":308,"line":350},[306,1065,360],{"emptyLinePlaceholder":359},[306,1067,1068,1071,1073],{"class":308,"line":356},[306,1069,1070],{"class":316},"methods ",[306,1072,369],{"class":312},[306,1074,1075],{"class":316}," {\n",[306,1077,1078,1081],{"class":308,"line":363},[306,1079,1080],{"class":375},"    \"equal\"",[306,1082,1083],{"class":316},": QgsClassificationEqualInterval(),\n",[306,1085,1086,1089],{"class":308,"line":388},[306,1087,1088],{"class":375},"    \"quantile\"",[306,1090,1091],{"class":316},": QgsClassificationQuantile(),\n",[306,1093,1094,1097],{"class":308,"line":399},[306,1095,1096],{"class":375},"    \"jenks\"",[306,1098,1099],{"class":316},": QgsClassificationJenks(),\n",[306,1101,1102],{"class":308,"line":404},[306,1103,1104],{"class":316},"}\n",[306,1106,1107],{"class":308,"line":411},[306,1108,360],{"emptyLinePlaceholder":359},[306,1110,1111,1113,1116,1118],{"class":308,"line":422},[306,1112,699],{"class":312},[306,1114,1115],{"class":316}," name, method ",[306,1117,705],{"class":312},[306,1119,1120],{"class":316}," methods.items():\n",[306,1122,1123],{"class":308,"line":427},[306,1124,1125],{"class":316},"    renderer.setClassificationMethod(method)\n",[306,1127,1128,1131,1133],{"class":308,"line":433},[306,1129,1130],{"class":316},"    renderer.updateClasses(layer, ",[306,1132,945],{"class":382},[306,1134,353],{"class":316},[306,1136,1137,1140,1142,1144,1147,1150,1153,1156,1159,1162,1165,1167,1170,1172,1174],{"class":308,"line":444},[306,1138,1139],{"class":316},"    breaks ",[306,1141,369],{"class":312},[306,1143,590],{"class":316},[306,1145,1146],{"class":312},"f",[306,1148,1149],{"class":375},"\"",[306,1151,1152],{"class":382},"{",[306,1154,1155],{"class":316},"r.lowerValue()",[306,1157,1158],{"class":312},":.0f",[306,1160,1161],{"class":382},"}",[306,1163,1164],{"class":375},"-",[306,1166,1152],{"class":382},[306,1168,1169],{"class":316},"r.upperValue()",[306,1171,1158],{"class":312},[306,1173,1161],{"class":382},[306,1175,1176],{"class":375},"\"\n",[306,1178,1179,1182,1185,1187],{"class":308,"line":460},[306,1180,1181],{"class":312},"              for",[306,1183,1184],{"class":316}," r ",[306,1186,705],{"class":312},[306,1188,1189],{"class":316}," renderer.ranges()]\n",[306,1191,1192,1195],{"class":308,"line":465},[306,1193,1194],{"class":382},"    print",[306,1196,1197],{"class":316},"(name, breaks)\n",[15,1199,1200,1202,1203,1206,1207,1210,1211,1214,1215,1218,1219,103],{},[74,1201,523],{}," Each method computes break points differently. ",[19,1204,1205],{},"QgsClassificationEqualInterval"," divides the value range into equal-width bins — fast and intuitive, but a few outliers can leave most classes empty. ",[19,1208,1209],{},"QgsClassificationQuantile"," puts an equal ",[47,1212,1213],{},"count"," of features in each class — great for ranked maps, but break values look arbitrary. ",[19,1216,1217],{},"QgsClassificationJenks"," (natural breaks) minimizes within-class variance and maximizes between-class variance, hugging natural clusters in the data. For a deep treatment of Jenks and when to prefer it, see ",[28,1220,1222],{"href":1221},"\u002Fpyqgis-cartography-visualization\u002Fgraduated-categorized-renderers\u002Fclassify-layer-natural-breaks-jenks-pyqgis\u002F","Classify a Layer with Natural Breaks (Jenks) in PyQGIS",[1224,1225,1226,1242],"table",{},[1227,1228,1229],"thead",{},[1230,1231,1232,1236,1239],"tr",{},[1233,1234,1235],"th",{},"Method",[1233,1237,1238],{},"Best for",[1233,1240,1241],{},"Watch out for",[1243,1244,1245,1257,1268],"tbody",{},[1230,1246,1247,1251,1254],{},[1248,1249,1250],"td",{},"Equal interval",[1248,1252,1253],{},"Evenly spread data, intuitive legends",[1248,1255,1256],{},"Skewed data leaves classes empty",[1230,1258,1259,1262,1265],{},[1248,1260,1261],{},"Quantile",[1248,1263,1264],{},"Ranked maps, even map ink balance",[1248,1266,1267],{},"Adjacent similar values split across classes",[1230,1269,1270,1273,1276],{},[1248,1271,1272],{},"Jenks (natural breaks)",[1248,1274,1275],{},"Clustered, real-world distributions",[1248,1277,1278],{},"Slower on very large layers; breaks vary with N",[15,1280,1281,1282,1285,1286,1289,1290,1292,1293,1295,1296,1298],{},"Two further methods are worth knowing. ",[19,1283,1284],{},"QgsClassificationLogarithmic"," spaces breaks on a log scale, which tames heavily right-skewed data such as population or income where a handful of huge values would otherwise compress everyone else into the bottom class. ",[19,1287,1288],{},"QgsClassificationPrettyBreaks"," rounds break values to human-friendly numbers (10, 25, 50) at the cost of slightly uneven class membership — ideal when a clean legend matters more than statistical precision. All of them are interchangeable: every method is a ",[19,1291,1027],{}," subclass, so ",[19,1294,999],{}," accepts any of them and ",[19,1297,828],{}," does the rest. That uniform interface is what makes it cheap to offer users a method dropdown in a plugin and apply their choice with one line.",[63,1300,1302],{"id":1301},"_5-color-ramps-and-updatecolorramp","5. Color Ramps and updateColorRamp()",[15,1304,1305,1306,1308,1309,1312],{},"Both renderers store classes (or categories) plus a color ramp, and ",[19,1307,538],{}," recolors every class without recomputing the breaks. Ramps come from ",[19,1310,1311],{},"QgsStyle.defaultStyle()",", which exposes every ramp in the user's QGIS style library by name.",[297,1314,1316],{"className":299,"code":1315,"language":301,"meta":302,"style":302},"from qgis.core import QgsStyle, QgsGradientColorRamp\nfrom qgis.PyQt.QtGui import QColor\n\nstyle = QgsStyle.defaultStyle()\n\n# Discover available ramp names.\nprint(style.colorRampNames()[:10])\n\n# Built-in ramp by name.\nviridis = style.colorRamp(\"Viridis\")\nrenderer.updateColorRamp(viridis)\n\n# Or build a custom two-color gradient on the fly.\ncustom = QgsGradientColorRamp(QColor(\"#edf8e9\"), QColor(\"#005a32\"))\nrenderer.updateColorRamp(custom)\n\n# Reverse a ramp so high values map to dark colors.\nviridis_inv = style.colorRamp(\"Viridis\")\nviridis_inv.invert()\nrenderer.updateColorRamp(viridis_inv)\n\nlayer.triggerRepaint()\n",[19,1317,1318,1329,1339,1343,1352,1356,1361,1375,1379,1384,1399,1404,1408,1413,1434,1439,1443,1448,1461,1466,1471,1475],{"__ignoreMap":302},[306,1319,1320,1322,1324,1326],{"class":308,"line":309},[306,1321,313],{"class":312},[306,1323,317],{"class":316},[306,1325,320],{"class":312},[306,1327,1328],{"class":316}," QgsStyle, QgsGradientColorRamp\n",[306,1330,1331,1333,1335,1337],{"class":308,"line":326},[306,1332,313],{"class":312},[306,1334,669],{"class":316},[306,1336,320],{"class":312},[306,1338,674],{"class":316},[306,1340,1341],{"class":308,"line":332},[306,1342,360],{"emptyLinePlaceholder":359},[306,1344,1345,1348,1350],{"class":308,"line":338},[306,1346,1347],{"class":316},"style ",[306,1349,369],{"class":312},[306,1351,441],{"class":316},[306,1353,1354],{"class":308,"line":344},[306,1355,360],{"emptyLinePlaceholder":359},[306,1357,1358],{"class":308,"line":350},[306,1359,1360],{"class":407},"# Discover available ramp names.\n",[306,1362,1363,1366,1369,1372],{"class":308,"line":356},[306,1364,1365],{"class":382},"print",[306,1367,1368],{"class":316},"(style.colorRampNames()[:",[306,1370,1371],{"class":382},"10",[306,1373,1374],{"class":316},"])\n",[306,1376,1377],{"class":308,"line":363},[306,1378,360],{"emptyLinePlaceholder":359},[306,1380,1381],{"class":308,"line":388},[306,1382,1383],{"class":407},"# Built-in ramp by name.\n",[306,1385,1386,1389,1391,1394,1397],{"class":308,"line":399},[306,1387,1388],{"class":316},"viridis ",[306,1390,369],{"class":312},[306,1392,1393],{"class":316}," style.colorRamp(",[306,1395,1396],{"class":375},"\"Viridis\"",[306,1398,353],{"class":316},[306,1400,1401],{"class":308,"line":404},[306,1402,1403],{"class":316},"renderer.updateColorRamp(viridis)\n",[306,1405,1406],{"class":308,"line":411},[306,1407,360],{"emptyLinePlaceholder":359},[306,1409,1410],{"class":308,"line":422},[306,1411,1412],{"class":407},"# Or build a custom two-color gradient on the fly.\n",[306,1414,1415,1418,1420,1423,1426,1429,1432],{"class":308,"line":427},[306,1416,1417],{"class":316},"custom ",[306,1419,369],{"class":312},[306,1421,1422],{"class":316}," QgsGradientColorRamp(QColor(",[306,1424,1425],{"class":375},"\"#edf8e9\"",[306,1427,1428],{"class":316},"), QColor(",[306,1430,1431],{"class":375},"\"#005a32\"",[306,1433,638],{"class":316},[306,1435,1436],{"class":308,"line":433},[306,1437,1438],{"class":316},"renderer.updateColorRamp(custom)\n",[306,1440,1441],{"class":308,"line":444},[306,1442,360],{"emptyLinePlaceholder":359},[306,1444,1445],{"class":308,"line":460},[306,1446,1447],{"class":407},"# Reverse a ramp so high values map to dark colors.\n",[306,1449,1450,1453,1455,1457,1459],{"class":308,"line":465},[306,1451,1452],{"class":316},"viridis_inv ",[306,1454,369],{"class":312},[306,1456,1393],{"class":316},[306,1458,1396],{"class":375},[306,1460,353],{"class":316},[306,1462,1463],{"class":308,"line":476},[306,1464,1465],{"class":316},"viridis_inv.invert()\n",[306,1467,1468],{"class":308,"line":482},[306,1469,1470],{"class":316},"renderer.updateColorRamp(viridis_inv)\n",[306,1472,1473],{"class":308,"line":487},[306,1474,360],{"emptyLinePlaceholder":359},[306,1476,1477],{"class":308,"line":498},[306,1478,518],{"class":316},[15,1480,1481,524,1483,1486,1487,1489,1490,1493,1494,1497],{},[74,1482,523],{},[19,1484,1485],{},"colorRampNames()"," lists everything available, including cpt-city ramps the user installed. ",[19,1488,538],{}," is non-destructive to your class breaks — it only restyles, so you can preview several ramps quickly. ",[19,1491,1492],{},"QgsGradientColorRamp"," lets you bypass the library entirely when you need brand-specific colors. ",[19,1495,1496],{},"invert()"," flips the direction, which matters when convention expects darker symbols for larger magnitudes.",[63,1499,1501],{"id":1500},"_6-reading-and-reusing-an-existing-renderer","6. Reading and Reusing an Existing Renderer",[15,1503,1504],{},"Production scripts often inherit a layer that is already styled and need to inspect or extend that styling rather than rebuild it from scratch. Renderers are introspectable: you can read the type, the field, the breaks, and the symbols, then make targeted changes. Guarding on the renderer class keeps the same routine safe whether the layer arrives categorized, graduated, or with a plain single-symbol renderer.",[297,1506,1508],{"className":299,"code":1507,"language":301,"meta":302,"style":302},"from qgis.core import (\n    QgsGraduatedSymbolRenderer,\n    QgsCategorizedSymbolRenderer,\n)\n\nlayer = QgsProject.instance().mapLayersByName(\"counties\")[0]\nrenderer = layer.renderer()\n\nif isinstance(renderer, QgsGraduatedSymbolRenderer):\n    print(f\"Graduated on '{renderer.classAttribute()}' \"\n          f\"with {len(renderer.ranges())} classes\")\n    # Clone so we never mutate the live renderer in place.\n    new_renderer = renderer.clone()\n    new_renderer.updateColorRamp(QgsStyle.defaultStyle().colorRamp(\"Magma\"))\n    layer.setRenderer(new_renderer)\n\nelif isinstance(renderer, QgsCategorizedSymbolRenderer):\n    print(f\"Categorized on '{renderer.classAttribute()}' \"\n          f\"with {len(renderer.categories())} categories\")\n\nelse:\n    print(f\"Single-symbol or unsupported renderer: {type(renderer).__name__}\")\n\nlayer.triggerRepaint()\n",[19,1509,1510,1520,1524,1528,1532,1536,1552,1561,1565,1576,1598,1619,1624,1634,1644,1649,1653,1663,1682,1700,1704,1711,1735,1739],{"__ignoreMap":302},[306,1511,1512,1514,1516,1518],{"class":308,"line":309},[306,1513,313],{"class":312},[306,1515,317],{"class":316},[306,1517,320],{"class":312},[306,1519,323],{"class":316},[306,1521,1522],{"class":308,"line":326},[306,1523,849],{"class":316},[306,1525,1526],{"class":308,"line":332},[306,1527,335],{"class":316},[306,1529,1530],{"class":308,"line":338},[306,1531,353],{"class":316},[306,1533,1534],{"class":308,"line":344},[306,1535,360],{"emptyLinePlaceholder":359},[306,1537,1538,1540,1542,1544,1546,1548,1550],{"class":308,"line":350},[306,1539,366],{"class":316},[306,1541,369],{"class":312},[306,1543,372],{"class":316},[306,1545,881],{"class":375},[306,1547,379],{"class":316},[306,1549,152],{"class":382},[306,1551,385],{"class":316},[306,1553,1554,1556,1558],{"class":308,"line":356},[306,1555,490],{"class":316},[306,1557,369],{"class":312},[306,1559,1560],{"class":316}," layer.renderer()\n",[306,1562,1563],{"class":308,"line":363},[306,1564,360],{"emptyLinePlaceholder":359},[306,1566,1567,1570,1573],{"class":308,"line":388},[306,1568,1569],{"class":312},"if",[306,1571,1572],{"class":382}," isinstance",[306,1574,1575],{"class":316},"(renderer, QgsGraduatedSymbolRenderer):\n",[306,1577,1578,1580,1583,1585,1588,1590,1593,1595],{"class":308,"line":399},[306,1579,1194],{"class":382},[306,1581,1582],{"class":316},"(",[306,1584,1146],{"class":312},[306,1586,1587],{"class":375},"\"Graduated on '",[306,1589,1152],{"class":382},[306,1591,1592],{"class":316},"renderer.classAttribute()",[306,1594,1161],{"class":382},[306,1596,1597],{"class":375},"' \"\n",[306,1599,1600,1603,1606,1609,1612,1614,1617],{"class":308,"line":404},[306,1601,1602],{"class":312},"          f",[306,1604,1605],{"class":375},"\"with ",[306,1607,1608],{"class":382},"{len",[306,1610,1611],{"class":316},"(renderer.ranges())",[306,1613,1161],{"class":382},[306,1615,1616],{"class":375}," classes\"",[306,1618,353],{"class":316},[306,1620,1621],{"class":308,"line":411},[306,1622,1623],{"class":407},"    # Clone so we never mutate the live renderer in place.\n",[306,1625,1626,1629,1631],{"class":308,"line":422},[306,1627,1628],{"class":316},"    new_renderer ",[306,1630,369],{"class":312},[306,1632,1633],{"class":316}," renderer.clone()\n",[306,1635,1636,1639,1642],{"class":308,"line":427},[306,1637,1638],{"class":316},"    new_renderer.updateColorRamp(QgsStyle.defaultStyle().colorRamp(",[306,1640,1641],{"class":375},"\"Magma\"",[306,1643,638],{"class":316},[306,1645,1646],{"class":308,"line":433},[306,1647,1648],{"class":316},"    layer.setRenderer(new_renderer)\n",[306,1650,1651],{"class":308,"line":444},[306,1652,360],{"emptyLinePlaceholder":359},[306,1654,1655,1658,1660],{"class":308,"line":460},[306,1656,1657],{"class":312},"elif",[306,1659,1572],{"class":382},[306,1661,1662],{"class":316},"(renderer, QgsCategorizedSymbolRenderer):\n",[306,1664,1665,1667,1669,1671,1674,1676,1678,1680],{"class":308,"line":465},[306,1666,1194],{"class":382},[306,1668,1582],{"class":316},[306,1670,1146],{"class":312},[306,1672,1673],{"class":375},"\"Categorized on '",[306,1675,1152],{"class":382},[306,1677,1592],{"class":316},[306,1679,1161],{"class":382},[306,1681,1597],{"class":375},[306,1683,1684,1686,1688,1690,1693,1695,1698],{"class":308,"line":476},[306,1685,1602],{"class":312},[306,1687,1605],{"class":375},[306,1689,1608],{"class":382},[306,1691,1692],{"class":316},"(renderer.categories())",[306,1694,1161],{"class":382},[306,1696,1697],{"class":375}," categories\"",[306,1699,353],{"class":316},[306,1701,1702],{"class":308,"line":482},[306,1703,360],{"emptyLinePlaceholder":359},[306,1705,1706,1709],{"class":308,"line":487},[306,1707,1708],{"class":312},"else",[306,1710,748],{"class":316},[306,1712,1713,1715,1717,1719,1722,1725,1728,1731,1733],{"class":308,"line":498},[306,1714,1194],{"class":382},[306,1716,1582],{"class":316},[306,1718,1146],{"class":312},[306,1720,1721],{"class":375},"\"Single-symbol or unsupported renderer: ",[306,1723,1724],{"class":382},"{type",[306,1726,1727],{"class":316},"(renderer).",[306,1729,1730],{"class":382},"__name__}",[306,1732,1149],{"class":375},[306,1734,353],{"class":316},[306,1736,1737],{"class":308,"line":504},[306,1738,360],{"emptyLinePlaceholder":359},[306,1740,1741],{"class":308,"line":509},[306,1742,518],{"class":316},[15,1744,1745,524,1747,1750,1751,1754,1755,558,1758,531,1761,1764,1765,1768],{},[74,1746,523],{},[19,1748,1749],{},"layer.renderer()"," returns the live renderer object; ",[19,1752,1753],{},"isinstance()"," checks let you branch on its concrete type before calling type-specific methods such as ",[19,1756,1757],{},"ranges()",[19,1759,1760],{},"categories()",[19,1762,1763],{},"classAttribute()"," reports the field the renderer is bound to, which is invaluable in batch jobs where the field name is not known ahead of time. Calling ",[19,1766,1767],{},"clone()"," before mutating is the safe habit: it produces an independent copy so an exception mid-edit cannot leave the displayed renderer half-modified. This pattern is the backbone of any tool that restyles many layers consistently — for example, applying a house color ramp across an atlas.",[63,1770,1772],{"id":1771},"_7-saving-loading-and-sharing-styles","7. Saving, Loading, and Sharing Styles",[15,1774,1775,1776,1779],{},"Once a renderer looks right, capture it so the same classification and colors apply to other layers or projects without re-running the classification logic. QGIS serializes styling to ",[19,1777,1778],{},".qml"," (QML) files and can also embed it in the data source via a sidecar or database table.",[297,1781,1783],{"className":299,"code":1782,"language":301,"meta":302,"style":302},"# Export the full symbology (renderer + labels) to a QML file.\nresult, message = layer.saveNamedStyle(\"\u002Ftmp\u002Fcounties.qml\")\nprint(\"saved\" if result else f\"failed: {message}\")\n\n# Apply that style to a second, similarly-structured layer.\nother = QgsProject.instance().mapLayersByName(\"counties_2020\")[0]\nok, msg = other.loadNamedStyle(\"\u002Ftmp\u002Fcounties.qml\")\nother.triggerRepaint()\n\n# Export only the symbology category (omit labels, diagrams) if needed.\nfrom qgis.core import QgsMapLayer\nlayer.saveNamedStyle(\n    \"\u002Ftmp\u002Fcounties_symbology.qml\",\n    categories=QgsMapLayer.Symbology,\n)\n",[19,1784,1785,1790,1805,1839,1843,1848,1866,1880,1885,1889,1894,1905,1910,1918,1929],{"__ignoreMap":302},[306,1786,1787],{"class":308,"line":309},[306,1788,1789],{"class":407},"# Export the full symbology (renderer + labels) to a QML file.\n",[306,1791,1792,1795,1797,1800,1803],{"class":308,"line":326},[306,1793,1794],{"class":316},"result, message ",[306,1796,369],{"class":312},[306,1798,1799],{"class":316}," layer.saveNamedStyle(",[306,1801,1802],{"class":375},"\"\u002Ftmp\u002Fcounties.qml\"",[306,1804,353],{"class":316},[306,1806,1807,1809,1811,1814,1817,1820,1822,1825,1828,1830,1833,1835,1837],{"class":308,"line":332},[306,1808,1365],{"class":382},[306,1810,1582],{"class":316},[306,1812,1813],{"class":375},"\"saved\"",[306,1815,1816],{"class":312}," if",[306,1818,1819],{"class":316}," result ",[306,1821,1708],{"class":312},[306,1823,1824],{"class":312}," f",[306,1826,1827],{"class":375},"\"failed: ",[306,1829,1152],{"class":382},[306,1831,1832],{"class":316},"message",[306,1834,1161],{"class":382},[306,1836,1149],{"class":375},[306,1838,353],{"class":316},[306,1840,1841],{"class":308,"line":338},[306,1842,360],{"emptyLinePlaceholder":359},[306,1844,1845],{"class":308,"line":344},[306,1846,1847],{"class":407},"# Apply that style to a second, similarly-structured layer.\n",[306,1849,1850,1853,1855,1857,1860,1862,1864],{"class":308,"line":350},[306,1851,1852],{"class":316},"other ",[306,1854,369],{"class":312},[306,1856,372],{"class":316},[306,1858,1859],{"class":375},"\"counties_2020\"",[306,1861,379],{"class":316},[306,1863,152],{"class":382},[306,1865,385],{"class":316},[306,1867,1868,1871,1873,1876,1878],{"class":308,"line":356},[306,1869,1870],{"class":316},"ok, msg ",[306,1872,369],{"class":312},[306,1874,1875],{"class":316}," other.loadNamedStyle(",[306,1877,1802],{"class":375},[306,1879,353],{"class":316},[306,1881,1882],{"class":308,"line":363},[306,1883,1884],{"class":316},"other.triggerRepaint()\n",[306,1886,1887],{"class":308,"line":388},[306,1888,360],{"emptyLinePlaceholder":359},[306,1890,1891],{"class":308,"line":399},[306,1892,1893],{"class":407},"# Export only the symbology category (omit labels, diagrams) if needed.\n",[306,1895,1896,1898,1900,1902],{"class":308,"line":404},[306,1897,313],{"class":312},[306,1899,317],{"class":316},[306,1901,320],{"class":312},[306,1903,1904],{"class":316}," QgsMapLayer\n",[306,1906,1907],{"class":308,"line":411},[306,1908,1909],{"class":316},"layer.saveNamedStyle(\n",[306,1911,1912,1915],{"class":308,"line":422},[306,1913,1914],{"class":375},"    \"\u002Ftmp\u002Fcounties_symbology.qml\"",[306,1916,1917],{"class":316},",\n",[306,1919,1920,1924,1926],{"class":308,"line":427},[306,1921,1923],{"class":1922},"s9osk","    categories",[306,1925,369],{"class":312},[306,1927,1928],{"class":316},"QgsMapLayer.Symbology,\n",[306,1930,1931],{"class":308,"line":433},[306,1932,353],{"class":316},[15,1934,1935,524,1937,1940,1941,1944,1945,1948,1949,1952,1953,1955,1956,1958],{},[74,1936,523],{},[19,1938,1939],{},"saveNamedStyle()"," returns a ",[19,1942,1943],{},"(bool, str)"," tuple — always check it, since a read-only path is a common silent failure. ",[19,1946,1947],{},"loadNamedStyle()"," applies a saved style to any compatible layer; the field referenced by the renderer must exist in the target or the classes render empty. The ",[19,1950,1951],{},"categories="," argument lets you export a subset (symbology only, labels only, or all), which is useful when you want to share class colors without overwriting a layer's existing labels configured in ",[28,1954,44],{"href":43},". For team workflows, commit the ",[19,1957,1778],{}," alongside the data so every analyst renders the layer identically.",[63,1960,1962],{"id":1961},"_8-refreshing-the-legend-and-canvas","8. Refreshing the Legend and Canvas",[15,1964,1965],{},"A renderer change does not automatically propagate to the Layers panel legend until you tell QGIS to rebuild it. In scripts, refresh both the symbology cache and the layer tree view.",[297,1967,1969],{"className":299,"code":1968,"language":301,"meta":302,"style":302},"from qgis.utils import iface\n\nlayer.setRenderer(renderer)\n\n# Rebuild the legend nodes for this layer in the tree.\nlayer.triggerRepaint()\nif iface is not None:\n    iface.layerTreeView().refreshLayerSymbology(layer.id())\n    iface.mapCanvas().refresh()\n",[19,1970,1971,1983,1987,1991,1995,2000,2004,2022,2027],{"__ignoreMap":302},[306,1972,1973,1975,1978,1980],{"class":308,"line":309},[306,1974,313],{"class":312},[306,1976,1977],{"class":316}," qgis.utils ",[306,1979,320],{"class":312},[306,1981,1982],{"class":316}," iface\n",[306,1984,1985],{"class":308,"line":326},[306,1986,360],{"emptyLinePlaceholder":359},[306,1988,1989],{"class":308,"line":332},[306,1990,512],{"class":316},[306,1992,1993],{"class":308,"line":338},[306,1994,360],{"emptyLinePlaceholder":359},[306,1996,1997],{"class":308,"line":344},[306,1998,1999],{"class":407},"# Rebuild the legend nodes for this layer in the tree.\n",[306,2001,2002],{"class":308,"line":350},[306,2003,518],{"class":316},[306,2005,2006,2008,2011,2014,2017,2020],{"class":308,"line":356},[306,2007,1569],{"class":312},[306,2009,2010],{"class":316}," iface ",[306,2012,2013],{"class":312},"is",[306,2015,2016],{"class":312}," not",[306,2018,2019],{"class":382}," None",[306,2021,748],{"class":316},[306,2023,2024],{"class":308,"line":363},[306,2025,2026],{"class":316},"    iface.layerTreeView().refreshLayerSymbology(layer.id())\n",[306,2028,2029],{"class":308,"line":388},[306,2030,2031],{"class":316},"    iface.mapCanvas().refresh()\n",[15,2033,2034,524,2036,2038,2039,2042,2043,2045,2046,2049,2050,103],{},[74,2035,523],{},[19,2037,542],{}," invalidates the rendered image. ",[19,2040,2041],{},"refreshLayerSymbology()"," regenerates the expandable legend entries (the swatches under the layer name). ",[19,2044,546],{}," forces an immediate redraw. The ",[19,2047,2048],{},"iface is not None"," guard keeps the same code safe in standalone scripts where there is no GUI. To save the styling for reuse, call ",[19,2051,2052],{},"layer.saveNamedStyle(\"\u002Fpath\u002Fto\u002Fstyle.qml\")",[63,2054,2056],{"id":2055},"version-compatibility","Version Compatibility",[15,2058,2059,2060,2062,2063,2065],{},"The classification-method API used here — ",[19,2061,999],{}," plus ",[19,2064,828],{}," — has been stable since QGIS 3.10 and is the recommended path on the 3.34 LTR baseline.",[1224,2067,2068,2078],{},[1227,2069,2070],{},[1230,2071,2072,2075],{},[1233,2073,2074],{},"QGIS \u002F Python",[1233,2076,2077],{},"Notes",[1243,2079,2080,2090,2101,2109],{},[1230,2081,2082,2087],{},[1248,2083,2084],{},[74,2085,2086],{},"3.34 LTR (Python 3.12)",[1248,2088,2089],{},"Baseline for this guide. All APIs above work as written.",[1230,2091,2092,2095],{},[1248,2093,2094],{},"3.28 LTR (Python 3.9)",[1248,2096,2097,2098,2100],{},"Fully supported. ",[19,2099,1217],{}," and friends available.",[1230,2102,2103,2106],{},[1248,2104,2105],{},"3.40 \u002F 3.44",[1248,2107,2108],{},"Same API; additional built-in ramps and refined Jenks performance.",[1230,2110,2111,2114],{},[1248,2112,2113],{},"Pre-3.10",[1248,2115,2116,2117,2120,2121,2124,2125,110,2128,2130],{},"Avoid: older code used ",[19,2118,2119],{},"QgsGraduatedSymbolRenderer.createRenderer(...)"," with a ",[19,2122,2123],{},"mode"," enum (",[19,2126,2127],{},"Jenks",[19,2129,1261],{},") instead of method objects.",[15,2132,2133,2134,2136],{},"If you maintain code targeting older releases, the legacy ",[19,2135,2123],{},"-enum constructor still exists but is deprecated; migrate to method objects for forward compatibility.",[63,2138,2140],{"id":2139},"key-takeaways","Key Takeaways",[68,2142,2143,2152,2166,2169,2174],{},[71,2144,2145,2146,2148,2149,2151],{},"Use ",[19,2147,21],{}," for nominal data (one symbol per value) and ",[19,2150,25],{}," for numeric magnitude (ordered classes).",[71,2153,2154,2155,2158,2159,2162,2163,103],{},"Build categories with ",[19,2156,2157],{},"createCategories([], template, layer, field)","; build classes with ",[19,2160,2161],{},"setClassificationMethod(...)"," then ",[19,2164,2165],{},"updateClasses(layer, n)",[71,2167,2168],{},"Pick the classification method deliberately: equal interval, quantile, and Jenks produce visibly different maps.",[71,2170,2171,2173],{},[19,2172,538],{}," restyles classes without recomputing breaks — use qualitative ramps for categories, sequential ramps for graduated values.",[71,2175,2176,2177,812,2179,2181],{},"Always ",[19,2178,542],{},[19,2180,2041],{}," so the canvas and legend reflect the new renderer.",[63,2183,2185],{"id":2184},"frequently-asked-questions","Frequently Asked Questions",[15,2187,2188,2191],{},[74,2189,2190],{},"When should I use a categorized renderer instead of a graduated one?","\nUse categorized for discrete, unordered values such as land-use class, administrative name, or zoning code. Use graduated when the field is numeric and the magnitude matters — population, income, elevation. If you graduate a code field, the legend will imply an order that does not exist.",[15,2193,2194,2197,2198,2200,2201,2204],{},[74,2195,2196],{},"Why is my map all one color after setting a graduated renderer?","\nAlmost always you set the renderer but never called ",[19,2199,2165],{},", so there are zero ranges, or the chosen field contains nulls\u002Fstrings that the method skipped. Confirm ",[19,2202,2203],{},"renderer.ranges()"," is non-empty and the field is numeric.",[15,2206,2207,2210,2211,2214],{},[74,2208,2209],{},"How do I keep the same class breaks but change colors?","\nCall ",[19,2212,2213],{},"renderer.updateColorRamp(new_ramp)",". It recolors every existing class or category in place and leaves your computed break values untouched, which makes ramp previewing fast.",[15,2216,2217,2220,2221,2224,2225,2228],{},[74,2218,2219],{},"Can I assign symbol size instead of color to graduated classes?","\nYes. Set ",[19,2222,2223],{},"renderer.setGraduatedMethod(QgsGraduatedSymbolRenderer.GraduatedSize)"," and provide a min\u002Fmax size with ",[19,2226,2227],{},"setSymbolSizes()"," (point\u002Fline layers). Graduated color is the default; graduated size produces proportional-symbol maps.",[15,2230,2231,2234,2235,2238,2239,2242,2243,2246,2247,103],{},[74,2232,2233],{},"Do renderer changes persist when I save the project?","\nYes — renderers are serialized into the ",[19,2236,2237],{},".qgz","\u002F",[19,2240,2241],{},".qgs"," project file. To reuse styling across projects, export it with ",[19,2244,2245],{},"layer.saveNamedStyle(\"style.qml\")"," and load it later with ",[19,2248,2249],{},"layer.loadNamedStyle()",[63,2251,2253],{"id":2252},"related","Related",[68,2255,2256,2260,2264,2268,2274],{},[71,2257,2258],{},[28,2259,31],{"href":30},[71,2261,2262],{},[28,2263,39],{"href":38},[71,2265,2266],{},[28,2267,44],{"href":43},[71,2269,2270],{},[28,2271,2273],{"href":2272},"\u002Fpyqgis-cartography-visualization\u002Fgraduated-categorized-renderers\u002Fcreate-choropleth-map-pyqgis\u002F","Create a Choropleth Map in PyQGIS",[71,2275,2276],{},[28,2277,1222],{"href":1221},[2279,2280,2281],"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 pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}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 .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}",{"title":302,"searchDepth":326,"depth":326,"links":2283},[2284,2285,2286,2287,2288,2289,2290,2291,2292,2293,2294,2295,2296,2297],{"id":65,"depth":326,"text":66},{"id":120,"depth":326,"text":121},{"id":284,"depth":326,"text":285},{"id":650,"depth":326,"text":651},{"id":821,"depth":326,"text":822},{"id":1020,"depth":326,"text":1021},{"id":1301,"depth":326,"text":1302},{"id":1500,"depth":326,"text":1501},{"id":1771,"depth":326,"text":1772},{"id":1961,"depth":326,"text":1962},{"id":2055,"depth":326,"text":2056},{"id":2139,"depth":326,"text":2140},{"id":2184,"depth":326,"text":2185},{"id":2252,"depth":326,"text":2253},"Build thematic maps in PyQGIS with QgsCategorizedSymbolRenderer and QgsGraduatedSymbolRenderer, classification methods, color ramps, and legends.","md",{},"\u002Fpyqgis-cartography-visualization\u002Fgraduated-categorized-renderers",{"title":5,"description":2298},"pyqgis-cartography-visualization\u002Fgraduated-categorized-renderers\u002Findex","unVGawpx5OYNUe6baPiSEUQIdqRY1kF7QVuA2ceyYoo",1781792483472]