I'm using QGIS 2.18. I need to create sector lights for navigational purposes on a map.
I have the light sector data as fields giving becon_id, start degree, end degree and color in a shapefile with over 500 buoys, beacons and lighthouses that need to be shown on the map. For each beacon, there can be many rows, each describing one light sector (for instance a white sector)
The end result should look about like below: The light sectors in the right colors, with the color marked as the character in the color field (R G W), and dotted lines from 100m to 1000m from the buoy/beacon/lighthouse.
This should most likely be created as a ruled-based symbol, but needs some python I guess?
Here below is an example of the shapefile data for a lighthouse (unfortunately not the above one) which has a green sector between 114 and 154 degrees, a white sector between 154 and 168 degrees, a red sector between 168 and 237 degrees, a green sector between 237 and 314 degrees, a white sector between 314 and 320 degrees, a red sector between 320 and 337 degrees (for some reason, 0 is not north but south):
Answer
EDIT I edited the answer for managing particular situations (due to specific angle values) and for not displaying the dotted lines when a round angle is defined.
I propose a solution by only recurring to rule-based symbology and labeling.
Before starting, 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.
Furthermore, this solution only works if you assume that 0
degree is North instead of South (if 0
is South, instead, it would be enough summing a 180
value every time that appears a '90' in formulas which deal with angles, e.g. cos(radians(90))
would become cos(radians(180 + 90))
). I preferred doing this only for the sake of giving a more general solution.
We will render the points with a Single symbol
and by recurring to one Simple Marker
and three Geometry generator
symbol layers:
In the further explanation, I will follow the same order of the symbols in the image above.
1) Simple Marker
I chose a default symbol of a black star (this is the easier part of this tutorial), having a size of 3 mm and a width of 0.4 mm.
2) Geometry Generator No. 1
Add a new symbol layer and select the Geometry generator
type:
Insert this expression in the Expression
field:
CASE
WHEN abs( "ALKUKULMA" - "LOPPUKULMA") < 360
THEN
make_line(
$geometry,
make_point(
$x + 1000*cos(radians(90 - "ALKUKULMA")),
$y + 1000*sin(radians(90 - "ALKUKULMA"))
)
)
END
We have just defined the first line which points towards the point where the light sector starts from. This line is 1000 m long and it's created only when the opening angle of the sector light is not a round angle (this happens for avoiding that the line would break a whole circle).
3) Geometry Generator No. 2
Same as above but, in this step, you need to use this expression:
CASE
WHEN abs( "ALKUKULMA" - "LOPPUKULMA") < 360
THEN
make_line(
$geometry,
make_point(
$x + 1000*cos(radians(90 - "LOPPUKULMA")),
$y + 1000*sin(radians(90 - "LOPPUKULMA"))
)
)
END
We have just defined the first line which points towards the point where the light sector ends at. This line is 1000 m long and it's created only when the opening angle of the sector light is not a round angle (this happens for avoiding that the line would break a whole circle).
4) Geometry Generator No. 3
Insert this expression in the Expression
field:
CASE
WHEN abs("ALKUKULMA" - "LOPPUKULMA") <= 180 AND "ALKUKULMA" >= "LOPPUKULMA"
THEN
difference(
boundary(
buffer(
$geometry, 900)
),
make_polygon(
geom_from_wkt(
geom_to_wkt(
make_line(
$geometry,
make_point($x + 2000*cos(radians(90 - "ALKUKULMA" )), $y + 2000*sin(radians((90 - "ALKUKULMA" )))),
make_point($x + 2000*cos(radians(90 - ("LOPPUKULMA" + "ALKUKULMA")/2 )), $y + 2000*sin(radians((90 - ("LOPPUKULMA" + "ALKUKULMA")/2 )))),
make_point($x + 2000*cos(radians(90 - "LOPPUKULMA")), $y + 2000*sin(radians((90 - "LOPPUKULMA")))),
$geometry)
)
)
)
)
WHEN abs("ALKUKULMA" - "LOPPUKULMA") <= 180 AND "ALKUKULMA" <= "LOPPUKULMA"
THEN
intersection(
boundary(
buffer(
$geometry, 900)
),
make_polygon(
geom_from_wkt(
geom_to_wkt(
make_line(
$geometry,
make_point($x + 2000*cos(radians(90 - "ALKUKULMA" )), $y + 2000*sin(radians((90 - "ALKUKULMA" )))),
make_point($x + 2000*cos(radians(90 - ("LOPPUKULMA" + "ALKUKULMA")/2 )), $y + 2000*sin(radians((90 - ("LOPPUKULMA" + "ALKUKULMA")/2 )))),
make_point($x + 2000*cos(radians(90 - "LOPPUKULMA")), $y + 2000*sin(radians((90 - "LOPPUKULMA")))),
$geometry)
)
)
)
)
WHEN abs("ALKUKULMA" - "LOPPUKULMA") > 180 AND "ALKUKULMA" >= "LOPPUKULMA"
THEN
intersection(
boundary(
buffer(
$geometry, 900)
),
make_polygon(
geom_from_wkt(
geom_to_wkt(
make_line(
$geometry,
make_point($x + 2000*cos(radians(90 - "ALKUKULMA" )), $y + 2000*sin(radians((90 - "ALKUKULMA" )))),
make_point($x - 2000*cos(radians(90 - ("LOPPUKULMA" + "ALKUKULMA")/2 )), $y - 2000*sin(radians((90 - ("LOPPUKULMA" + "ALKUKULMA")/2 )))),
make_point($x + 2000*cos(radians(90 - "LOPPUKULMA")), $y + 2000*sin(radians((90 - "LOPPUKULMA")))),
$geometry)
)
)
)
)
WHEN abs("ALKUKULMA" - "LOPPUKULMA") > 180 AND "ALKUKULMA" <= "LOPPUKULMA"
THEN
difference(
boundary(
buffer(
$geometry, 900)
),
make_polygon(
geom_from_wkt(
geom_to_wkt(
make_line(
$geometry,
make_point($x + 2000*cos(radians(90 - "ALKUKULMA" )), $y + 2000*sin(radians((90 - "ALKUKULMA" )))),
make_point($x - 2000*cos(radians(90 - ("LOPPUKULMA" + "ALKUKULMA")/2 )), $y - 2000*sin(radians((90 - ("LOPPUKULMA" + "ALKUKULMA")/2 )))),
make_point($x + 2000*cos(radians(90 - "LOPPUKULMA")), $y + 2000*sin(radians((90 - "LOPPUKULMA")))),
$geometry)
)
)
)
)
END
We have just defined the arc between the starting and ending points of the light sector (please note that 2000
is an arbitrary value because I'm trying to create a polygon to intersect with the boundary of the circle having a radius of 900 m).
Furthermore, we need to set the color which is stored in the "VARIS"
field. For doing this, we need to specify it with a custom expression. Follow the arrow in the image below:
and then type this expression after having clicked on the Edit...
button:
CASE
WHEN "VARIS" = 'vi' THEN color_rgb(51,160,44)
WHEN "VARIS" = 'v' THEN color_rgb(255,255,255)
WHEN "VARIS" = 'p' THEN color_rgb(227,26,28)
END
Please note that, for this symbol layer, I created two lines: the upper line defines the color to use (in fact I set the custom expression for this one), while the lower one is useful for defining a black border (it will have a width which is bigger than the one of the upper line). Remember also to set Flat
as Cap style
for both lines for avoiding any color overlapping.
1) Setting the labels
Go to Layer Properties
> Labels
and, as usual, follow the red arrows:
and then type this expression:
CASE
WHEN "VARIS" = 'vi' THEN 'G'
WHEN "VARIS" = 'v' THEN 'W'
WHEN "VARIS" = 'p' THEN 'R'
END
We have just defined the color rule using the value stored in the "VARIS"
field.
2) Setting the placement for labels
Select the Placement
option in the Labels
Menu and select Offset from point
.
Then, with reference to the image below:
follow the red arrow and type this expression:
CASE
WHEN "ALKUKULMA" > "LOPPUKULMA"
THEN
concat(
-1000*cos(radians(90 - ("ALKUKULMA" + "LOPPUKULMA")/2)),
',',
1000*sin(radians(90 - ("ALKUKULMA" + "LOPPUKULMA")/2))
)
WHEN "ALKUKULMA" <= "LOPPUKULMA"
THEN
concat(
1000*cos(radians(90 - ("ALKUKULMA" + "LOPPUKULMA")/2)),
',',
-1000*sin(radians(90 - ("ALKUKULMA" + "LOPPUKULMA")/2))
)
END
Then, follow the green arrow and type this expression:
CASE
WHEN "ALKUKULMA" >= "LOPPUKULMA"
THEN
180-(("ALKUKULMA" + "LOPPUKULMA")/2)
WHEN "ALKUKULMA" < "LOPPUKULMA"
THEN
- (("ALKUKULMA" + "LOPPUKULMA")/2)
END
If you correctly performed the previous tasks, you should be able to get this result:
Bonus
Since the minor parameters were too many for being completely covered within this answer, I have attached the style here: you may open this code with any text editor and save it as a QGIS Layer Style file (i.e. with a .qml
extension).
The above style was created using QGIS 2.18.4 (it must have the same name of the shapefile you are using).
No comments:
Post a Comment