3

I have > 100 nested polygons in a unique SpatialPolygonsDataFrame. I desire to plot them with ggplot2 and all of them need to be visible in the map, i.e. overlaying larger polygons have to be in the background.

I found that I can achieve this by using alpha = 0 within the geom_polygon function but how can I assign a fill colour to each polygon?

Here an example of my code with just 2 polygons:

library(ggplot2)

Read csv file with two shapefiles merged and then converted to data.frame with fortify from maptools.

#read csv file shape_1_2.csv
shape_1_2 = read.csv('shape_1_2.csv', stringsAsFactors = FALSE)

#plot
ggplot() +
geom_polygon(data = shape_1_2, aes(x = long, y = lat, group = group), 
             colour = 'black', size = 1, linetype = 'solid', alpha = 0)

And relative map:

enter image description here

How can I fill with colour these two polygons?

I tried to add fill='black' in both aes and geom_polygon but it doesn't work.

Thanks


Update

I am sorry but I realised that my example data contains NO nested polygons.

So starting from the following data.frame as per https://gis.stackexchange.com/questions/280671/r-create-multipolygon-from-overlapping-polygons-using-sf-package :

shape_df = data.frame(
    lon = c(0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 2, 2, 0.8, 1, 1, 2, 2, 1, 1),
    lat = c(0, 0, 1, 1.5, 0, 1, 1, 2, 2, 1, 1, 1, 2, 2, 1, 0, 0, 1, 1, 0),
    var = c(1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3 ,3 ,3 ,3 ,3, 4 ,4 ,4, 4, 4)
)

And my plotting code (with alpha=0):

ggplot() +
    geom_polygon(data = shape_df, aes(x = lon, y = lat, group = var), 
                 colour = 'black', size = 1, linetype = 'solid', alpha = 0)

With relative map:

enter image description here

How can I fill the different areas present in the map with one OR up to 4 colours so that the larger polygons remains in the background of the smaller?

aaaaa
  • 149
  • 2
  • 18
  • 44
  • Please provide a _minimal reproducible example_ so that people willing to help easily can try out their code. – Henrik Jun 30 '18 at 12:04
  • thanks. I put a Dropbopx link from where to download my example data. – aaaaa Jun 30 '18 at 12:21
  • How do you intend to mark the order of polygons? Do you have a way that you're measuring the area of each? – camille Jun 30 '18 at 15:30
  • Hi, thanks. Good question..I already have the area of each polygon in a separate file. But what I need is just fill all of them with same colour AND show the borders. – aaaaa Jun 30 '18 at 15:46
  • 1
    This is not what you want or? (If not, could you include an image of how to result should look like?) `ggplot(shape_df) + aes(x = lon, y = lat, fill=factor(var)) + geom_polygon() + scale_fill_brewer(palette = "RdYlGn") + geom_path(color="black")` – SeGa Jun 30 '18 at 16:56
  • not really but it's my fault... – aaaaa Jun 30 '18 at 17:09

3 Answers3

5

If you do this with sf, you can use st_area to get the area of each polygon (area doesn't make a ton of sense with unprojected toy data, but will make sense with the actual shapes), then order polygons based on area. That way, ggplot will create polygons in order by ID. To use geom_sf, you need the github dev version of ggplot2, though it's being added to the next CRAN release, slated for next month (July 2018).

First create a simple features collection from the data. In this case, I had to use summarise(do_union = F) to make each series of points into a polygon in the proper order (per this recent question), then calculate the area of each.

library(tidyverse)
library(sf)
#> Linking to GEOS 3.6.1, GDAL 2.1.3, proj.4 4.9.3

shape_df <- data.frame(
  lon = c(0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 2, 2, 0.8, 1, 1, 2, 2, 1, 1),
  lat = c(0, 0, 1, 1.5, 0, 1, 1, 2, 2, 1, 1, 1, 2, 2, 1, 0, 0, 1, 1, 0),
  var = c(1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3 ,3 ,3 ,3 ,3, 4 ,4 ,4, 4, 4)
)

shape_areas <- shape_df %>%
  st_as_sf(coords = c("lon", "lat")) %>%
  group_by(var) %>%
  summarise(do_union = F) %>%
  st_cast("POLYGON") %>%
  st_cast("MULTIPOLYGON") %>%
  mutate(area = st_area(geometry)) %>% 
  mutate(var = as.factor(var)) 

shape_areas
#> Simple feature collection with 4 features and 3 fields
#> geometry type:  MULTIPOLYGON
#> dimension:      XY
#> bbox:           xmin: 0 ymin: 0 xmax: 2 ymax: 2
#> epsg (SRID):    NA
#> proj4string:    NA
#>   var do_union area                       geometry
#> 1   1    FALSE 1.25 MULTIPOLYGON (((0 0, 1 0, 1...
#> 2   2    FALSE 1.00 MULTIPOLYGON (((0 1, 1 1, 1...
#> 3   3    FALSE 1.10 MULTIPOLYGON (((1 1, 2 1, 2...
#> 4   4    FALSE 1.00 MULTIPOLYGON (((1 0, 2 0, 2...

If I plot at this point, the area has no bearing on the order of plotting; it just orders by var, numerically:

shape_areas %>%
  ggplot() +
    geom_sf(aes(fill = var), alpha = 0.9)

But if I use forcats::fct_reorder to reorder var as a factor by decreasing area, polygons will be plotting in order with the largest polygons at the bottom, and smaller polygons layering on top. Edit: as @SeGa pointed out below, this was originally putting larger shapes on top. Use -area or desc(area) to order descending.

shape_areas %>%
  mutate(var = var %>% fct_reorder(-area)) %>%
  ggplot() +
  geom_sf(aes(fill = var), alpha = 0.9)

Created on 2018-06-30 by the reprex package (v0.2.0).

camille
  • 16,432
  • 18
  • 38
  • 60
  • 1
    The dev version of `ggplot2`. I mentioned it in the post but I'll link to it – camille Jun 30 '18 at 17:24
  • 1
    Could you also include an example with `-area` in the `fct_reorder` function, as the final question asks how to keep larger polygons in the background. – SeGa Jun 30 '18 at 17:32
  • 1
    @SeGa Thank you, you're right—I was misreading the shapes, they were plotting in the reverse order from what I intended – camille Jun 30 '18 at 17:50
2

If you add a fill but have alpha=0, you wont see any colors, as they will be 100% transparent.

I'm not sure what exactly you want to achieve, as I can see 3 polygons, that you can color differently, not just 2.

What if you try this ggplot-call, which takes the id as fill variable (as factor):

ggplot() +
  geom_polygon(data = shape_1_2, aes(x = long, y = lat, group = group, fill=factor(id)), 
               colour = 'black', size = 1, linetype = 'solid', alpha = 1)

Or this one, which takes group as fill variable (as factor) and allows you to define your own colors.

ggplot(shape_1_2) +
  aes(x = long, y = lat, group = group, fill=factor(group)) + 
  geom_polygon() +
  scale_fill_manual(values = c("green", "red", "blue")) +
  geom_path(color="black")

enter image description here

You could also use the scale_fill_brewer function, which lets you pick a predefined color palette (show possible palettes with: display.brewer.all())

ggplot(shape_1_2) +
  aes(x = long, y = lat, group = group, fill=factor(group)) + 
  geom_polygon() +
  scale_fill_brewer(palette = "RdYlGn") +
  geom_path(color="black") +
  ggtitle("fill=factor(group)")
SeGa
  • 9,454
  • 3
  • 31
  • 70
  • thank you. this works for my example but I just found out that my two example polygons are not nested! – aaaaa Jun 30 '18 at 16:34
  • I'm not sure what exactly you mean with nested. Are they overlaying each other? In your example the polygon with id=0 has a hole, so 2 polygons belong to `id=0`. The group variable finds distinct polygons and each polygon will get a unique color. – SeGa Jun 30 '18 at 16:43
  • thank you. I updated my question. apologies for the confusion. – aaaaa Jun 30 '18 at 16:44
0

Adding a variable named "aux" to each of the shapes you can use "fill = aux" in ggplot2

You can try to do:

library(maptools)
library(ggplot2)

shape_1 <- readShapePoly('poly_1.shp') %>%
fortify()  %>%
mutate(aux = c("1"))

shape_2 <- readShapePoly('poly_2.shp') %>%
fortify()  %>%
mutate(aux = c("2"))

shape_1_2 = rbind(shape_1, shape_2)

ggplot() +
geom_polygon(data = shape_1_2, aes(x = long, y = lat, group = group), 
             colour = 'black', size = 0.1, linetype = 'solid', alpha = 0, fill = aux)