4

I have a layer with polygons that describe road damages. I have then another layer with road network lines. The layers are shown in the picture.

enter image description here

I need to project the polygons as lines to the road network. How can I iterate over each polygon, find its two extreme points in relation to the nearest road line and project the line connecting the two extreme vertices onto the road line? Are there any tools for achieving the result in QGIS or is necessary to make an own Python script?

The final output is supposed to be something like the picture below. The colored lines represent the polygons and cover the road network.

enter image description here

Following the steps from the answer, I end up with the points being in a way randomly placed on different roads as shown on picture below (the projected_vertices layer is marked with yellow)

enter image description here

krltos
  • 223
  • 2
  • 7
  • 2
    What do you mean by "find its two extreme points"? – Erik Jan 13 '21 at 09:20
  • I mean to represent a polygon as a line that can be projected onto a road network line (center line of a road), so that it is visible what section of the road is affected by the damage. It is not important to know the extent of the damage on the surface, but only to know the road section. – krltos Jan 13 '21 at 09:52
  • 1
    I suppose the "start" and "end" points of the polygon were it represented as a centre line. – MarcM Jan 13 '21 at 09:53
  • By looking at data, how inhomogeneous your features in the data, I am afraid that there won't be one solution that fits all. I did not entirely understand how the final output should look like. An image may helpful if you could add the question as expected output? – Nil Jan 13 '21 at 10:06
  • 2
    Added a picture visualizing the desired final output. Each of the lines corresponds to a polygon from the original data layer. The new lines are projected onto the road network lines. – krltos Jan 13 '21 at 10:26

1 Answers1

4

First an algorithmic description how this solution works. Afterwards the implementation in QGIS, using expressions to create new geometries.

Description

You can create for each vertex of your polygons the closest point on the line representing the street: make a perpendicular line on the streetline going through each vertex and where this connecting line crosses the streetline, create a point. Like this, you project the polygon's vertices on the street line. You than just have to connect all projected points based a common attribute that groups together points/vertices belonging to the same polygon.

Implementation in QGIS

  1. Preparation of the data: On the polygon layer, create new attribute nearest_line (if using Shapefiles: nearest - Shapefiles have a limit of 10 characters in fieldnames) using this expression: array_to_string ( overlay_nearest( 'line',$id)). This assigns the streetline the polygon should be projected to (if you have more than one streetline). Also make sure you have a unique field 'id' in the attributes of the polygons. Using a field 'fid' creates some issues if you want to save the vertices in the next step as Geopackage (you have to delete the fid field before as it will be no longer unique: every vertex will have the fid of the polygon it belongs to). So just create a new field 'id' and copy the fid value there.

  2. Extract vertices of you polygons (menu Vector / Geometry tools), name the created point layer vertices.

  3. Project these vertices to the line using the following expression with Menu / Processing / Toolbox / Geometry by expression to get an output layer projected_vertices:


intersection( 
    extend (
        make_line (
            $geometry, 
            closest_point ( 
                geometry (
                    get_feature_by_id (
                        'line',
                         "nearest_line" 
                    )
                ), 
                $geometry
            )
        ),
        1,
        1
    ),
    geometry (
        get_feature_by_id(
            'line',
             "nearest_line" 
        )
    )
)

As you might notice, one of the vertices in every polygon is a duplicate. That's because a polygon to be valid, it must be closed. That means that start- and endpoint must be identical (same coordinates).

enter image description here

  1. Now you can connect the projected vertices with a line using the points to path tool. Use the id field to sort and group (see screenshot) to get a separate feature (line) for each polygon. In the screenshot, the projected line can be seen in red:

enter image description here

UPDATE

Using your data, I re-created the solution. As you can see, in some places a manual decision has to be taken as to where a polygon should belong to. See polygon 4455 in the next screenshot: it is projected to the road running east-west because the polygon intersects this road and thus is assigned to this line.

Depending on what you use the data for, you would possibly prefer this polygon to be projected to the road in the east, parallel to it. I checked the output and there are very few such cases. You could manually change the field nearest_line you created automatically in step 1 for this polygon and assign the roadline you want it be projected to.

For large datasets, of course you could refine the automatization process and add additional criteria as for which line the polygon should be projected to. As your polygons often have a very thin, long shape, an idea would be using main_angle ($geometry) and then to compare this with the azimuth of the nearest streets to assign the polygon to that steet where the street's azimuth and the polygons main_angle correspond best - thus where the polygons are more or less "parallel" to the street. For polygon 4455 on the screenshot, this for sure would work well.

enter image description here

Babel
  • 71,072
  • 14
  • 78
  • 208
  • Thank you for outlining the solution. I got stuck in point 2. When I create a projected vertices layer, they are all on one street which should not be the case. The layer has the same amount of features as the vertices layer. Also, all layers are in the same CRS. Do I need to change anything else in the expression you provided except for the 'line' parameter? – krltos Jan 13 '21 at 12:47
  • The expression contains get_feature_by_id ('line',1). It refers to the other layer (line) and takes feature no. 1. If you add a line, it will not be refered as it has another feature no.: 2, 3 or whatever. What you could do (for simple cases): create a multiline layer, thus merge all your line features to one multipart feature that you can refer to with the expression as above, see: https://i.stack.imgur.com/MZWVu.png However, as you can see, if polygons are too close to a crossroads, it can happen that they will have their vertices on different lines, as is the case with polygon 1 and 2. – Babel Jan 13 '21 at 13:48
  • So what you should do is to assign first every polygon to the line section you want to project it to and than process as described. – Babel Jan 13 '21 at 13:48
  • Changed my solution so that it works also with multiple lines. Hope that works? – Babel Jan 13 '21 at 14:54
  • I'm afraid it still doesn't work. In step 1 I assign the id of a road to each polygon in the nearest_line attribute. I have 42 roads and the nearest_line gets values in the range of 1 to 42. Could actually another attribute be used? Each road has an UniqueID. Anyway, in the step 3, the projected vertices appear in kinda random places on the roads. I added a picture to my question to visualize the results. – krltos Jan 14 '21 at 07:29
  • From the picture I guess that still all polygons are projected to the same streetline, thus something went wrong in step 1. Of course, you could use any unique attribute in step 1 (adapt the process accordingly). What exactly went wrong is difficult to say without having your data. Is it possible that you make it available for testing? – Babel Jan 14 '21 at 08:37
  • I dropped the polygon and road layers here https://dropmefiles.com/KjZox. I tried using for step 1 geomnearest('layer','attribute') from refFunctions plugin to add id from another attribute. The assignment seems correct, but the step 3 still fails even though it projects vertices to different roads. Probably I don't see something obvious. – krltos Jan 14 '21 at 09:46
  • The roads layer is empty, no lines there. – Babel Jan 14 '21 at 09:50
  • My mistake with the attached file. The proper layer file should be now available here https://dropmefiles.com/1wVDl – krltos Jan 14 '21 at 09:54
  • Now the layer source is invalid. You provided a shp file: if you use shapefile, you must provide all sidecar files a well. – Babel Jan 14 '21 at 09:56
  • Sorry for that mess, but I'm completely new to QGIS and GIS. Now hopefully all the files are here https://dropmefiles.com/GSwDJ – krltos Jan 14 '21 at 11:47
  • In your data you have three groups of polygons. The largest, in the west, does not have any streets nearby, so I deleted them. I was however able to apply my solution to the other data, see here for the result: https://drive.switch.ch/index.php/s/kAXbCLWneg91kWY (QGIS project with all data/layers - unzip everything before opening in QGIS) – Babel Jan 14 '21 at 15:46
  • In your data $id &fid have different values. fid is not an integer, so the polygons got assigned the "wrong" roads segments. I created a new field id in roads layer with $id and used this in the expression of step 1: array_to_string ( overlay_nearest( 'roads',id)). Also be sure to adapt layer names to the names you use: roads instead of line. Also, nearest_line as field name does not work as you use Shapefile (max. limit of fieldname 10 characters, use nearest instead). With these changes, I was able to get the solution as you can see in the linkek project files above. – Babel Jan 14 '21 at 15:51
  • Thank you for your help and providing a step by step solution easy to follow for a newbie. – krltos Jan 15 '21 at 06:35