13

I have two shapefiles: 1. an administrative boundary layer for a county in the UK known as an LSOA boundary that has 500 little zones in it 2. a flood zone.

Ideally, I want to find out which of the little LSOA zones are ≥50% within the flood zone and end up with a yes/no or a 1/0 for each of the 500 LSOA zones.

However, I don't know how to do this. I figured I could Join the two shapefiles, but there's no common attribute between them. Then I thought I could use the Join Attribute by Location function, which worked and shows me which LSOA are in the flood zone, but that's nearly all of them (see image 2).

I think this is an SQL problem, but I don't know. I'm new to QGIS and have never used PostgreSQL.

enter image description here

enter image description here

5 Answers 5

13

This is a relatively simple task using the geoprocessing tools included in QGIS.

  1. Calculate the area of your LSOA zones.
  • Open the LSOA layer attribute table.
  • Enable editing mode.
  • Open the field calculator.
  • Create a new field of type "Decimal number (real)" with the expression "$area".
  • Disable editing mode (saving edits).
  1. Merge the flood zone layer into a single multi-part feature.
  • Vector > Geometry Tools > Singleparts to Multipart. [EDIT: in recent versions this tool is renamed to Collect Geometries]
  • Select "--- Merge all ---" for the Unique ID field.
  1. Intersect the LSOA zone layer with the multipart flood zone layer.
  • Vector > Geoprocessing Tools > Intersect.
  • Input layer is the LSOA zones, intersect layer is the flood zones.
  1. The resulting layer will be the parts of the LSOA zones (with the attributes from the LSOA zones layer) which overlapped with the flood zones layer. To calculate the proportion of each LSOA zone within a flood zone:

    • Calculate the area of the intersected features (as in step #1), then
    • Add another field, dividing the original (total) area by the intersected area. The result is a decimal between 0 and 1. Multiply by 100 to give a percentage.
  2. Join the original LSOA layer to the intersected layer, using the unique ID shared by both layers.

  3. Export the joined layer as a new shapefile.

  4. Delete the duplicated attributes.

Et voilà!

Without step #2, an individual feature would be created for each different flood zone feature for each LSOA feature. This probably isn't what you want if you're only interested in the total coverage for each LSOA zone. If you want to differentiate between fluvial / tidal / pluvial flooding (and the flood zone data supports it), you could convert singlepart s to multipart specifying the "TYPE" field as the Unique ID field.

0
6

You can use spatialite and some spatial SQL functions.

Select t1.geometry, t1.ID, area(t1.geometry), area(t2.geometry) ...... (anything you need to have in the table results)

(area(intersection(t1.geometry,t2.geometry))) as "Commun_AREA"

, ("Commun_AREA"*100/(area(t1.geometry))) as "Percent_AREA"

From lsoa as t1, flood_zone as t2

Where Intersects( t1.geometry,t2.geometry ) = 1
0
5

The answer from @Snorfalorpagus was very thorough and effective (I used it several times!) but, as of QGIS 3.8, there is a new function called Overlap Analysis. This function:

"calculates the area and percentage cover by which features from an input layer are overlapped by features from a selection of overlay layers".

So, picking up off from step 2 in @Snorfalorpagus 's answer,

  1. Select Vector Analysis -> Overlap Analysis

  2. Input Layer (From example above): LSOA. Overlap Layer: Flood Areas.

This will create two new columns in LSOA:
- Column 1: actual area of overlap.
- Column 2: Percent area of overlap.

  1. You can easily convert to decimal (or proportion) from there using field calculator: (column_name / 100).
3

This seems like something that could be done much easier than the answers submitted. I would use a simple python script personally:

floodName = "the layer name here"
boundryName = "the layer name here"
fieldName = "the name of the field to contain the output 1/0"
minCoverage = 0.5 # the minimum amount of area covered to write 1
updateMap = [] # this will store values to be written    

# get layers
floodLayer = QgsMapLayerRegistry.instance().mapLayersByName(floodName)[0]
boundryLayer = QgsMapLayerRegistry.instance().mapLayersByName(boundryName)[0]
fieldIndex = boundryLayer.dataProvider().fieldNameIndex(fieldName)    

# iterate through boundries
for b in boundryLayer.getFeatures():
    # get only flood features that intersect with this feature's bounding box
    # this will make the script go way faster than it would otherwise
    request = QgsFeatureRequest().setFilterRect(b.geometry().boundingBox())
    floodGeom = geometry()
    floodFeat = QgsFeature()
    iter = floodLayer.getFeatures(request)
    iter.nextFeature(feat)
    while iter.nextFeature(feat):
        floodGeom = floodGeom.combine(feat.geometry())
    intersectGeom = b.geometry().intersection(feat.geometry())
    if intersectGeom.area() > minCoverage * b.geometry().area():
        updateMap[b.id()] = {fieldIndex : 1}
    else:
        updateMap[b.id()] = {fieldIndex : 0}

boundryLayer.dataProvider().changeAttributeValues(updateMap)

this only evaluates flood polygons that intersect with the bounding box of each boundry layer so it should be fairly quick to run, then it only updates one field in the existing layer (instead of a complex operation of making a whole new layer and copying old values then deleting)

2

I had the same problem as KJ following Snorfalorpagus' instructions using the "Intersect" method in Step 3. It took quite awhile to calculate and what I was left with was blank.

I tried following the same steps except using the "Clip" method in QGIS instead of Intersect -- so, in your example, what would be left would be the parts of the areas that are NOT covered by the flood zone. This seemed to work for some reason and I was able to use the "Area" field calculation from the previous step, plus a new "Area" calculation on the remaining parts of each polygon, to figure out the % of each area that was NOT covered by the other Polygon layer.

That's technically the reverse of what you asked for. But from there it's just a matter of subtracting each value from 1 to get what is covered by the flood zone.

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