I have seen Google Earth screen grabs of GPS bird trackers that show the altitude as a "curtain" extending down to ground level from the track. Is this possible in QGIS?
Have you considered the Qgis2threejs plugin? Very easy to use! A screenshot of a simple example is below, and you can rotate the actual output here.
The display is the initial portion of an actual Gulfstream III flight, departing Deadhorse, Alaska on August 16, 2014, climbing to the northwest, then turning southerly at a cruising altitude of 40,000 feet. Altitude is depicted by the "curtain" height, and airspeed is symbolized categorically with color.
QGIS has a 3D map view, but I couldn't get it to display a line with Z values. Here's a workaround using the 2.5 D renderer for polygons.
If you buffer a line, the 2.5D renderer setting will display the buffer as a "curtain." The problem is that the elevation of the top of the "curtain" is the same for the entire buffer. So you would need to split the buffer into many different sections, with a separate "elevation" attribute for each one. You'll need a lot of sections, many more than your original line has vertices. Here's a workflow for that:
Densifythe line so that there are many vertices. The Z values will be interpolated from the nearest vertices (eg, if you add one vertex between two vertices whose Z values are 100 and 200, the new vertex will have a Z value of 150).
Explode linesto split the densified line into separate line segments. The exploded line layer will retain the interpolated Z values. Note: if you don't want a full explanation, skip down to where it says "skip to here for a quick solution"
Use the Field Calculator to add the Z values to the attribute table, with this expression:
Bufferthe exploded layer with a narrow buffer width (I used 1 m). This creates a polygon layer, to which we can apply a 2.5D style.
Apply a 2.5D style to the buffer layer. Choose the "z_value" field to control the height.
Once you have the 2.5D rendering set up to your liking, convert it back to simple rendering to fine-tune the other aspects. This will convert the 2.5D rendering to two levels of geometry generated style, "building walls" and "building roofs". You can remove the first symbol level, the "building roofs," which is khaki-colored by default. The geometry generator expression for the "building walls" looks like this:
order_parts( extrude( segments_to_lines( $geometry ), cos( radians( eval( @qgis_25d_angle ) ) ) * eval( @qgis_25d_height ), sin( radians( eval( @qgis_25d_angle ) ) ) * eval( @qgis_25d_height ) ), 'distance( $geometry, translate( @map_extent_center, 1000 * @map_extent_width * cos( radians( @qgis_25d_angle + 180 )), 1000 * @map_extent_width * sin( radians( @qgis_25d_angle + 180 ))))', False)
You can simplify the geometry generator expression by replacing the
eval(variable) terms with fixed values.
- Substitute 70 degrees (or whatever value you choose for Angle in the 2.5D renderer window) for
eval(@qgis_25d_angle). If you want, you can simplify it further by substituting the radian equivalent of that angle for the
radians()term, eg substitute
1.22173for `radians(70). I chose not to do this, because I find it easier to think of angles in degrees, and I might later want to input a different angle value.
Substitute the z_value field for
The simplified expression looks like this:
order_parts( extrude( segments_to_lines( $geometry ), cos( radians( 70 ) ) * "z_value", sin( radians( 70 ) ) * "z_value" ), 'distance( $geometry, translate( @map_extent_center, 1000 * @map_extent_width * cos( radians(70 + 180)), 1000 * @map_extent_width * sin( radians(70 + 180))))', False)
At this point, you may notice that the first term inside the
extrude function is
segments_to_lines($geometry), which converts the polygon edges into line segments. But we actually already had line segments, back in step 2. We only created buffers because the 2.5D renderer requires them. We can cut out the middleman here. Substitute
segments_to_lines($geometry), and use the modified expression on the exploded layer.
Skip to here for a quick solution
Paste this expression into a geometry generator style layer for the exploded layer (created in step 2):
order_parts( extrude( $geometry, cos( radians( 70 ) ) * "z_value", sin( radians( 70 ) ) * "z_value" ), 'distance( $geometry, translate( @map_extent_center, 1000 * @map_extent_width * cos( radians(70 + 180)), 1000 * @map_extent_width * sin( radians(70 + 180))))', False)
- Save frequently. 3D and 2.5D rendering may make QGIS crash.
- Once you have the workflow figured out, put it into a Graphical Processing Model. Then you can run the whole model in one step every time you want to convert a line into a "curtain."
There are "steps" between every buffer section. The steps are nearly invisible where the vertical difference is small, but they're quite noticeable where the vertical difference is larger.
- Using a smaller vertex density interval in step 1 would make the steps less noticeable.
- Explode the line first, before densifying. Add a "slope" attribute based on how steep the line slope is. Line slope =
(z(start_point($geometry))-z(end_point($geometry)))/distance_to_vertex( $geometry,1). When you densify, use the "slope" attibute to control the vertex density, so you have more vertices along line segments with a steeper slope.
A note about the starting data type:
This method starts with data in the form of a line layer with altitude stored as Z coordinates of the vertices. If your data is point coordinates in a text file, follow these steps to convert it.
- Import your text file from the layer menu > add layer > add delimited text layer. It should import as a point layer. If the coordinates are in latitude and longitude, choose EPSG:4326 as the CRS and remember that latitude is the Y coordinate and longitude is the X coordinate.
- Use the tool
set Z valueto add the "altitude" attribute to the points as a Z value.
- Use the tool
points to pathto connect the points into a line. This tool requires that the points had a field that defines what order they should be connected in. If the points have a time or date field you can use that.
It should be possible to directly use the text file in this method. You would need to write an expression that draws a line from the current point to the next point, something like
make_line($geometry, #next point#) except with an actual function to get the next point. Substitute it for the first
$geometry term in the geometry generator expression.