from qgis.core import *
from qgis.PyQt.QtCore import QVariant
import math
--- Config ---
source_layer = iface.activeLayer()
fields_cats = ["Heterosexu", "Gay or les", "Bisexual", "Other", "Don't Know"]
colors = ["#4e79a7", "#f28e2b", "#e15759", "#76b7b2", "#59a14f"]
--- Output layer setup ---
out_layer = QgsVectorLayer("Polygon?crs=" + source_layer.crs().authid(), "pie_fill", "memory")
pr = out_layer.dataProvider()
pr.addAttributes([
QgsField("fid", QVariant.Int),
QgsField("category", QVariant.String),
QgsField("pct", QVariant.Double),
])
out_layer.updateFields()
feats_out = []
for feat in source_layer.getFeatures():
geom = feat.geometry()
centroid = geom.centroid().asPoint()
vals = [feat[f] or 0 for f in fields_cats]
total = sum(vals)
if total == 0:
continue
pcts = [v / total for v in vals]
# Build a large spoke polygon, then intersect with the feature geometry
# Use a radius large enough to always exceed the polygon extent
bbox = geom.boundingBox()
radius = math.sqrt(bbox.width()**2 + bbox.height()**2)
start_angle = 90 # start at top (north)
for i, (cat, pct) in enumerate(zip(fields_cats, pcts)):
if pct == 0:
continue
end_angle = start_angle - (pct * 360)
# Build wedge points
steps = max(int(abs(pct * 360) / 2), 3)
points = [QgsPointXY(centroid)]
for s in range(steps + 1):
angle_deg = start_angle - (pct * 360 * s / steps)
angle_rad = math.radians(angle_deg)
x = centroid.x() + radius * math.cos(angle_rad)
y = centroid.y() + radius * math.sin(angle_rad)
points.append(QgsPointXY(x, y))
points.append(QgsPointXY(centroid))
wedge_geom = QgsGeometry.fromPolygonXY([points])
clipped = geom.intersection(wedge_geom)
if clipped and not clipped.isEmpty():
f = QgsFeature()
f.setGeometry(clipped)
f.setAttributes([feat.id(), cat, round(pct * 100, 2)])
feats_out.append(f)
start_angle = end_angle
pr.addFeatures(feats_out)
out_layer.updateExtents()
QgsProject.instance().addMapLayer(out_layer)
Apply categorized style by category field
cats = []
for cat, color in zip(fields_cats, colors):
sym = QgsSymbol.defaultSymbol(out_layer.geometryType())
sym.setColor(QColor(color))
cats.append(QgsRendererCategory(cat, sym, cat))
renderer = QgsCategorizedSymbolRenderer("category", cats)
out_layer.setRenderer(renderer)
out_layer.triggerRepaint()
print("Done! pie_fill layer added.")
![[SOE_2024.jpeg]]