4

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?

  • The profile tool in QGIS is what you are looking for plugins.qgis.org/plugins/profiletool – Mapperz Sep 29 at 14:40
  • Have downloaded plugin but cannot connect it to height data. I am importing the data as text delimited which includes height data. I can see the tool connects with the layer because the colour of the line changes when tool is started but shows no height data. – Vic Sep 29 at 15:37
  • Have been trying various options in the profile tool and eventually found an error message "Active layer is not a profilable layer". I cannot find any information on what would be an acceptable layer. – Vic Sep 29 at 16:01
  • You will need a DEM of your area of interest youtube.com/watch?v=TOgpIe4R68E – Mapperz Sep 30 at 1:07
  • That seems to be a very roundabout way to make a graph, I can do that directly from the data. Still doesn't display what I had in mind or tell me what format the original file needs to be. – Vic Sep 30 at 7:43
4

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.

enter image description here

1

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:

  1. Densify the 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).

  2. Run Explode lines to 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"

  3. Use the Field Calculator to add the Z values to the attribute table, with this expression: z(start_point( $geometry))

  4. Buffer the 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.

  5. Apply a 2.5D style to the buffer layer. Choose the "z_value" field to control the height.

    enter image description here

  6. 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.22173 for `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 eval(@qgis_25d_height).

    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 $geometry for 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)

enter image description here

TIPS:

  • 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 value to add the "altitude" attribute to the points as a Z value.
  • Use the tool points to path to 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.

  • it is fabulously! – Taras Sep 30 at 18:48
  • I didn't expect such a detailed answer, exactly what I had in mind and a very good tutorial. Thank you csk. – Vic Oct 1 at 7:10
  • Have been trying to implement this and nothing seemed to be the same, then I realised the solution uses QGIS 2. Still poking around trying to find a workable solution with QGIS 3 Madeira – Vic Oct 4 at 17:44
  • I wrote this answer using QGIS 3. – csk Oct 7 at 20:55
  • I have just come back to this and trying to make it work, with no success. I think I may be starting with the wrong layer type. I am using a text file with the headings;-Date/Time,Lat,Lon,Altitude. Should it be converted to another format before trying the solution outlined? When I highlight the layer and open "densify" the layer name is not there and I can't enter it. If I point it to the original text file it says the file is not found. I tried using points to path and then densify but after conversion the layer has lost the altitude parameter. I will keep trying but not hopefull. – Vic Oct 13 at 13:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.