Another approach would be recurring to symbology only: the main advantage is that the result will be customizable with practically infinite combinations of parameters.
The following procedure will use a custom function and it will work independently from the number of categories you will use.
Assuming you are working on a projected CRS (instead, if you are using a Geographic Coordinate System, see the note at the end of the answer), I want to underline that I will focus the attention on the explanation of the minimal things to do for reproducing the desired result: this means that some other minor parameters (like sizes, widths and so on) should be easily adjusted by you for better fitting your needs.
You will discover that reading the following solution will require more time than applying it directly (since the custom function will automatically do the most of the job).
Context
Let assume to start from this point vector layer:

which stores several categories that we want to display:

Solution
Assuming we want to categorize the "cat_1"
, "cat_2"
and "cat_3"
fields,
we will render the point at the center (the yellow point in your original question) with one Geometry generator
symbol layer. Then, we will render the several categories using a number of Geometry generator
symbol layers which is equal to the number of categories themselves. So, for this case, we will use three Geometry generator
symbol layers:

In the further explanation, the colors of the squares in the above image will help you to understand which geometry we are creating.
1) Geometry Generator No. 1
Add a new symbol layer and select the Geometry generator
and the Polygon / MultiPolygon
types:

Insert this expression in the Expression field:
buffer($geometry,10)
We have just defined a circle which has a radius of 10 meters.
2) Geometry Generator No. 2
From the Style
dialog, add a new Geometry generator
symbol layer having Polygon / MultiPolygon
as geometry type. Then click on the Function Editor
tab:

Then, click on New file
and type piechart
as the name of the new function:

You will see that a new function has been created and it is listed on the left side of the dialog. Now, click on the name of the function and replace the default @qgsfunction
with the following code (don't delete the libraries imported by default and remember to add the import math
line):
from qgis.core import *
from qgis.gui import *
import math
@qgsfunction(args=-1, group='Custom')
def piechart(in_list, feature, parent):
to_show =in_list[-1] - 1
fields =in_list[:-2]
len_fields = len(in_list)
sum_val = sum([feature[k] for k in fields])
geom = feature.geometry()
radius = in_list[-2]
buffered = geom.buffer(radius, -1)
first = True
slices = []
for field in fields:
point_1 = geom.asPoint()
points = [point_1]
perim = buffered.length()
percent = float(feature[field])/(sum_val)
l = percent * perim
azimuth = l/radius
if first:
start = 0
end = azimuth
first = False
else:
start = end
end += azimuth
if abs(math.degrees(start - end)) <= 180:
dist_x, dist_y = (2 * radius * math.cos(math.radians(90) - start), 2 *radius* math.sin(math.radians(90) - start))
point_2 = QgsPoint(point_1[0] + dist_x, point_1[1] + dist_y)
dist_x, dist_y = (2 * radius * math.cos(math.radians(90) - (start + end)/2), 2 *radius* math.sin(math.radians(90) - (start + end)/2))
point_3 = QgsPoint(point_1[0] + dist_x, point_1[1] + dist_y)
dist_x, dist_y = (2 * radius * math.cos(math.radians(90) - end), 2 *radius* math.sin(math.radians(90) - end))
point_4 = QgsPoint(point_1[0] + dist_x, point_1[1] + dist_y)
else:
dist_x, dist_y = (2 * radius * math.cos(math.radians(90) - start), 2 *radius* math.sin(math.radians(90) - start))
point_2 = QgsPoint(point_1[0] + dist_x, point_1[1] + dist_y)
dist_x, dist_y = (2 * radius * math.cos(math.radians(90) - (start + end)/2), 2 *radius* math.sin(math.radians(90) - (start + end)/2))
point_3 = QgsPoint(point_1[0] - dist_x, point_1[1] - dist_y)
dist_x, dist_y = (2 * radius * math.cos(math.radians(90) - end), 2 *radius* math.sin(math.radians(90) - end))
point_4 = QgsPoint(point_1[0] + dist_x, point_1[1] + dist_y)
points.append(point_2)
points.append(point_3)
points.append(point_4)
trGeom = QgsGeometry().fromPolygon([points])
if math.degrees(azimuth) <= 180:
if start >= end:
slice = buffered.difference(trGeom)
else:
slice = buffered.intersection(trGeom)
else:
if start >= end:
slice = buffered.intersection(trGeom)
else:
slice = buffered.difference(trGeom)
slices.append(slice)
return slices[to_show]
Once you have done this, click on the Load
button and you will be able to see the function from the Custom
Menu of the Expression
dialog.
Now, type this expression (see the image below as reference):
piechart('cat_1', 'cat_2', 'cat_3', 20, 1)

We have just defined the first sector of the pie chart by entering the names of the fields to categorize ('cat_1', 'cat_2', 'cat_3'
), the radius of the pie chart (20
) and a number which indicates the category (starting from 1) to display: for this case, we will render the first category from the list, so we need to set 1
as value. As you will see in the following steps, this number will be the unique value to change.
3) Geometry Generator No. 3
From the Style
dialog, add a new Geometry generator
symbol layer having Polygon / MultiPolygon
as geometry type. Then click on the Function Editor
tab:

Then, type this expression in the Expression
field:
piechart('cat_1', 'cat_2', 'cat_3', 20, 2)
We have just defined the second sector of the pie chart. The unique difference with the previous step is that we set 2 as the last parameter: this because we want to render the second category field (i.e. "cat_2"
).
4) Geometry Generator No. 4
From the Style
dialog, add a new Geometry generator
symbol layer having Polygon / MultiPolygon
as geometry type. Then click on the Function Editor
tab:

Then, type this expression in the Expression
field:
piechart('cat_1', 'cat_2', 'cat_3', 20, 3)
We have just defined the third sector of the pie chart. The unique difference with the previous step is that we set 3 as the last parameter: this because we want to render the third category field (i.e. "cat_3"
).
Final result
Click on the Apply
button for applying the renderer: we have finished. If you correctly performed the previous tasks, you should be able to get something like this:

Final notes
If you are using a Geographic Coordinate System, i.e. if you are dealing with degrees and not with distances, it should be enough using the proper value for the fourth parameter of the piechart
function: this means that you need to replace it with other arbitrary values expressed in degrees (for example, 0.0002, 0.002 and so on).
Please note that my example will work fon any number of fields to categorize, assuming that you will create the proper number of geometry generators and set the correct values for the fourth parameter in the piechart
function.