1

I have two multipolygons and I want to test intersections between their geometries based for different cycles of years. Basically I have a flood dataset that contains flood events and their geometry and an election dataset which has each election as ward*year units and the wards' geometry. I want to see if there are any intersections in the electoral ward each cycle prior to each election. So if the election was in 2009 and the cycle was 2007-2009 I want to see if its ward was flooded in 2007, 08 or 09.

    df.election
    election.df
  year ward_ons     cycle                       geometry
1 2007       E1   NA-2007 POLYGON ((527370.8 183470.7...
2 2008       E1 2007-2008 POLYGON ((528891.1 182192.6...
3 2009       E2   NA-2009 POLYGON ((370294.2 414678.7...
4 2010       E3   NA-2010 POLYGON ((375025.4 414992.1...
5 2011       E3 2010-2011 POLYGON ((375150.8 410809.8...
6 2018       E3 2011-2018 POLYGON ((373286.3 414364.5...
7 2007       E4   NA-2007 POLYGON ((373168.6 411597.8...
8 2010       E4 2007-2010 POLYGON ((374783.2 406209.4...

Flood data:

    df.floods
    Simple feature collection with 8 features and 2 fields
geometry type:  GEOMETRY
dimension:      XY
bbox:           xmin: 317656.2 ymin: 90783.2 xmax: 546460.6 ymax: 631125.7
projected CRS:  OSGB 1936 / British National Grid
  year            name                       geometry
1 2007      River 2007 POLYGON ((359637.7 268239.7...
2 2007       Tank 2007 POLYGON ((325444.1 92717.57...
3 2008  Yorkshire 2008 POLYGON ((318550.7 103058.8...
4 2009 Flood East 2009 POLYGON ((541472.6 112593, ...
5 2010  Occurence 2010 MULTIPOLYGON (((545863.4 11...
6 2012      Storm 2012 POLYGON ((473637.4 103927, ...
7 2011      Flood 2011 MULTIPOLYGON (((524617.6 42...
8 2017      River 2017 POLYGON ((393387.6 631125.7...

The cycles' unique values for the entire dataframe are these:

df.election$cycle%>% unique()
 [1] "NA-2007"   "NA-2008"   "2007-2008" "NA-2009"   "2008-2009" "2007-2009" "NA-2010"   "2009-2010" "2008-2010" "2007-2010" "2010-2011" "2007-2011"
[13] "2008-2011" "2009-2011" "NA-2011"   "2010-2012" "2011-2012" "NA-2012"   "2008-2012" "2009-2012" "2007-2012" "2010-2013" "2012-2013" "2011-2013"
[25] "2007-2013" "NA-2013"   "2009-2013" "2010-2014" "2012-2014" "2011-2014" "NA-2014"   "2013-2014" "2014-2015" "2012-2015" "2011-2015" "NA-2015"  
[37] "2013-2015" "2007-2015" "2009-2015" "2014-2016" "2015-2016" "2012-2016" "NA-2016"   "2011-2016" "2013-2016" "2016-2017" "2015-2017" "2013-2017"
[49] "2009-2017" "NA-2017"   "2012-2017" "2008-2017" "2014-2018" "2016-2018" "2017-2018" "2012-2018" "2010-2018" "2015-2018" "NA-2018"   "2007-2018"

The NA values in cycle mean that there is no election prior to it. In those cases I want it to evaluate just for that year. So if the cycle is NA-2015 I want it to test if that ward was flooded in 2015. I want each election*year to have a value for flood that is 1 if there was an intersection during the years of its cycle value and a 0 if not.

So the ideal outcome would be something like the following:

        ideal.df
Simple feature collection with 8 features and 4 fields
geometry type:  POLYGON
dimension:      XY
bbox:           xmin: 368816.4 ymin: 181032 xmax: 528891.1 ymax: 416703.1
projected CRS:  OSGB 1936 / British National Grid
  year ward     cycle flood                       geometry
1 2007   E1   NA-2007     1 POLYGON ((527370.8 183470.7...
2 2008   E1 2007-2008     0 POLYGON ((528891.1 182192.6...
3 2009   E2   NA-2009     1 POLYGON ((370294.2 414678.7...
4 2010   E3   NA-2010     0 POLYGON ((375025.4 414992.1...
5 2011   E3 2010-2011     1 POLYGON ((375150.8 410809.8...
6 2018   E3 2011-2018     0 POLYGON ((373286.3 414364.5...
7 2007   E4   NA-2007     0 POLYGON ((373168.6 411597.8...
8 2010   E4 2007-2010     0 POLYGON ((374783.2 406209.4...

I tried several loops for this, using st_intersects which basically tests whether two geometries intersect.

for(i in votes.sp) {
  if(cycle =="NA-2007") int = st_intersects(recorded.full[recorded.full$year == 2007, ], i, sparse = FALSE) else
    if(cycle =="2007-2008") int = st_intersects(recorded.full[recorded.full$year%in% c(2007, 2008), ], i, sparse = FALSE) else
      int = FALSE}

And repeated this for every value of cycles.

I'm getting different errors, like: Error in cycle == "NA-2007" : comparison (1) is possible only for atomic and list types.

Based on Test intersections with time indicators in R I also tried creating a new variable with the lowest value in the cycle called lag.year2 and this loop:

 for(row in 1:nrow(df.election)) {
    rec_sub = st_union(subset(df.floods, year<= row$year & year>=row$lag.year2))
    int = st_intersects(
        n, 
        rec_sub,
        sparse = FALSE
    )
    if(any(int)) df.election$flood.cycle[n]= int[ ,1] else df.election$flood.cycle[n] = FALSE
}

But it isn't working either, I get:

Error in row$year : $ operator is invalid for atomic vectors

4
  • 1
    Why are all those geometries "EMPTY" in your "ideal outcome"? – Spacedman Dec 10 '20 at 9:51
  • 1
    Have you tried writing a function that goes from your "cycle" text column to a vector of years? – Spacedman Dec 10 '20 at 9:52
  • Sorry, because I made mistake while creating the mock data. I want it to be the same as in df.elecrtion, I corrected for that now. And no, I haven't tried it with a function, how would you go about it? – AntVal Dec 10 '20 at 10:00
  • A couple of hints, first I would split my data. Having each subset in a list object would allow you to then use lapply in leu of a for loop. Also, I do not see where cycle is being correctly specified in your if statements. At the very least it should be x$cycle but, also seems like it should represent a subset condition, which is not happening. You are also attempting to iterate through an object votes.sp without specifying the iterator eg., for(i in 1:nrow(df.election)) or for(i in df.election$cycles). You are experiencing "user error" and not package issues. – Jeffrey Evans Dec 14 '20 at 21:01
2

Let's start with a simple example of using a for loop to relate cycle and year. We are just going to use vectors for cycle (n=12) and year (n=100)

( cycle <- c("NA-2007","NA-2008","2007-2008","NA-2009","2008-2009","2007-2009",
         "NA-2010","2009-2010","2008-2010","2007-2010","2010-2011","2007-2011") )
( year <- sample(c(2007:2011), 100, replace=TRUE) )

The for loop uses cycle as the iterator which, in this case, is the unique values in the character vector. Since strings that start with NA are a single year we split the vector into two parts eg., start ("NA") and end ("2007). The if statement checks if the start value is NA and if so assigns it the end value. We can then use start:end to create a vector of all subset years. The loop nearly prints the number of matching elements in each iteration.

for(i in cycle) {
 start <- unlist(strsplit(i, "-"))[1]
 end <- unlist(strsplit(i, "-"))[2] 
  if(start == "NA") start = end
  y <- start:end
  cat(length(which(year %in% y)), "observations for", i, "\n") 
}

Now, we can expand this logic to get at your problem. Say we have a feature class "votes" that contain columns year and cycle (just as the above vectors) and a feature class called "flood" that we want to test intersection for.

flooded <- vector()
  for(i in unique(votes$cycle)) {
   start <- unlist(strsplit(i, "-"))[1]
   end <- unlist(strsplit(i, "-"))[2] 
    if(start == "NA") start = end
    cond <- st_intersects(votes[which(votes$year %in% start:end),], flood, sparse = FALSE)
    flooded <- append(flooded, any(cond == TRUE))
  }
votes$flooded <- flooded

In this example, we are using which(votes$year %in% start:end) to create a row index for subsetting our votes data. Since it is possible for an intersection to produce multiple conditions based on multiple intersecting geometries, we use any to return a TRUE if there are any intersections. The result would be the boolean "flooded" vector which will be a one-to-one ordered match to the "votes" data.

2
  • Thank you so much - this was super helpful and detailed. I'm getting very close to what I'm looking for but the object "flooded" I'm getting out is just 66 values equal to TRUE. 66 is also the number of unique values I have for cycle in the object votes. So I'm assuming what it is doing is to test whether there are any intersection for each value of cycle, not for each row. I've been trying to tweak the loop to try to get it but I'm failing. Do you have any suggestion? Thanks! – AntVal Jan 22 at 8:42
  • any tip would be much appreciated! – AntVal Feb 9 at 12:55

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.