2

I have a river layer and one of census boundaries. I know how to make a buffer around the river to extract adjacent census tracts. How could I then "warp" the river and adjacent census boundaries into an abstracted straight line?

To put it simply, how would one go about making this representation of the LA River?

PolyGeo
  • 65,136
  • 29
  • 109
  • 338
Tan
  • 103
  • 3
  • 1
    I think you might be looking for a "strip map" – Vince Dec 09 '22 at 18:55
  • Why not making a point every 1m along the river. And then you georeference your buffer, and each point should be at 0 for X, and 1 then 2 then 3 and so one for Y axis? – katagena Dec 09 '22 at 21:31

1 Answers1

6

You can do this in a few steps, basically using Geometry generator or Geometry by expression (see here for differences). The expression used for that starts with three variables that allow you to

  1. place the start point of the simplified line wherever you want,
  2. scale the length of the line continuously to fit your needs and,
  3. define the orientation (angle) of the line - like vertical, horizontal etc. in any direction.

Resulting blue line (representing river) and red buffer (census tracts); as you see, the selected parts of the river inside a census tract are highlighted in the simpified line as well, making it ease to identify: enter image description here

River represented horizontally with variable @az = radians(90) and scaled 1:1 with variable @scale = 1: enter image description here

A step by step procedure:

  1. Convert census tracts from polygons to lines: Menu Vector > Geometry Tools > Polygons to lines

  2. Split the river by the lines from step 1: Menu Processing > Toolbox > Split with lines

    You now have a separate river feature for each census tract the river flows through.

  3. Create an attribute called length for the length of each feature using Field calculator with the expression $length (assuming you use an appropriate CRS for measurements).

  4. Based on this, calculate the accumulated length for each feature: the length from the first point of the river up to the last point of the current feature with this expression, calling the field accumulated

     array_sum (
         array_foreach (
             generate_series(1,$id),
             attribute (
                 get_feature_by_id (@layer,@element),
                 'length'
             )
         )
     )
    
  5. This step is optional, but if you want to create actual geometries (see below), you might want the result to have attributes representing the ones from the initial census tract. Create a new attribute census_tract to get the id of the respective census tract - you can then use this id for a table join with the initial census tract layer later. Use this expression: overlay_intersects ('census_tracts', $id,sort_by_intersection_size:='des')[0]

  6. Now you can start to create your abstracted line. You basically need a point where to start the line and a direction. Let's say we simply use an arbitrary point, defined by it's x/y coordinates (in the local CRS I used): make_point (2564121,1182944) (line 1 in the expression below, change this value to fit your needs). The direction should be top-down, thus an azimuth of 180 degrees (change this in line 3).

    Now we have everything together to create the abstract line from top to bottom, representing the length the river passes through each census tract. You can use Geometry Generator on the same layer (this is for visualizaiton purpose only. If you want actual geometries, see below). The idea is to create for each feature (river inside each tract) a vertical line with end-point at a distance corresponding to the acummulated value (step 4) and its start point at the acummulated value of the feature before. We then have to add the first tract manually, as it does not have a feature before it. I used an if clause for that.

    To be easily able to adapt the length of the resulting line, I added a length scale factor in line 2 - changing this value, you can easily scale the lenght of the line. The third variable, az on line 3, allows you to rotate the orientation of the line - if you replace 180 by 90, you'll get a horizontal line instead of a vertical one. The whole expression then looks like this:

     with_variable('start',make_point (2564121,1182944),  -- change start point
     with_variable('factor', 0.6,   -- change length scale
     with_variable('az', radians(180),  -- rotate/change orientation of line
     if (
         $id=minimum($id),
         make_line (
             @start,
             project(
                 @start,
                 attribute (
                     get_feature_by_id(@layer, minimum($id)),
                     'cumulated'
                 )*@factor,
                 @az
             )
         ),
         make_line (
             project(
                 @start,
                 attribute (
                     get_feature_by_id (@layer,$id-1),
                     'cumulated'
                 )*@factor,
                 @az
             ),
             project(
                 @start,
                 attribute (
                     get_feature_by_id (@layer,$id),
                     'cumulated'
                 )*@factor,
                 @az
             )
         )
     ))))
    
  7. Now using another symbol layer of type Geometry generator, create a buffer around the line, symbolizing the census tracts the river intersects. Simply enclose the expression from above (abbreviated here as [expression]) in a buffer function: buffer ([expression], 1000, cap:='flat') - change the buffer distance of 1000 if necessary.


Creating actual geometries:

Using Geometry generator creates only a style for visualization. To create actual geometries, use Geometry by expression. For this, however, the expression must be modified a bit:

  1. instead of @layer, you must use the layer's name in single quotes ', e.g. 'my_layer_name'. There are three spots where you must do this: on lines 11, 21 and 29.
  2. Replace minimum($id) on lines 5 and 11 with the smallest $id value in your dataset. Normally, this should be 1. If unsure, simply type minimum($id) in the expression dialog window and see in the Previw what it returns, than manually insert this value in the expression.

The buffer (step 7) can now be done creating a simple buffer around the line. If you created the id in step 5, you can now make a table join to get the attributes from the initial census tracts layer.

The solution works well even when the river leaves a census tract an re-enters it, see example: enter image description here

Babel
  • 71,072
  • 14
  • 78
  • 208
  • 2
    I have to wonder when the book is coming out @Babel ;) – Matt Dec 10 '22 at 17:30
  • But as I can see on the exemple he gave, it is not only when river enter and goes out a polygon that is interesting, but also the polygon geometry inside the distance buffer! Or I’m wrong? – katagena Dec 10 '22 at 17:51
  • @katagena the question is very short and focuses (see title) on a "vertical, straight line representation of a river, including a buffer around it". By simply linking a picture, we do not know what further OP wants - also the polygon geometry? The colors as well? What about labels? Of course, we could start speculating... But as the soltuion demonstrates, even creating a straight line is quite a workflow - including all the other elements would be several more questions that - due to the policy ot this site - should be asked as separate questions if OP wants to add these additional features. – Babel Dec 10 '22 at 17:59
  • I think the focused Q&A format would be overcharged to ask for all this in one single post as there is no simple "click here" or "use algorithm x" answer. Each part needs an own, quite detailed answer. – Babel Dec 10 '22 at 18:02
  • 1
    This is an insightful and thorough guide to the core part of my question. Yes, I overbaked my question with an additional curiosity about how to “stretch” the census geometry, and I agree it’s more useful as a new post. – Tan Dec 12 '22 at 18:30