1

I would like to use igraph plotting a network on top of a map. For igraph nodes, I would like to use coordinates from the map (the centroid of polygons). I have now tried for hours but somehow I cannot find out how to correctly rescale the node positions of the igraph. Would it be possible to project from lon lat to a x,y scale?

  • Load packages.
    library(rnaturalearth)
    library(igraph)
    library(sf)
    library(tmap)
    library(dplyr)
    
  • Load countries from package. Use European countries.
    countries <- st_as_sf(ne_countries(scale = 110, continent = "Europe"))
    countries <- countries[!countries$adm0_a3 %in% c("RUS", "FRA"),]
    
  • Populate matrix for with a three ties.
    m <- matrix(0, length(countries$adm0_a3), length(countries$adm0_a3))
    m[2,3] <- 1
    m[3,6] <- 1
    m[10,2] <- 1
    
  • Get igraph object from matrix.
    g <- graph_from_adjacency_matrix(m)
    
  • Transform projection for centroid calculation to European projection.
    countries <- st_transform(st_as_sf(countries), 3042)
    
  • Extract centroids from country polygons.
    points <- countries %>%
      st_centroid() %>%
      unlist() %>%
      matrix(ncol=2, byrow=TRUE) %>%
      as_tibble()
    
  • Now plot first map and then igraph.
    tm_shape(countries) +
      tm_polygons(col = "white")
    par(new = T) # allows layering plots of all sorts
    plot(g,
         layout = as.matrix(points), # feed centroid coordinates to igraph
         # xlim = c(-180, 180), 
         # ylim = c(-90, 90), 
         # rescale = FALSE
         )
    

Image shows, igraph and map coords do not match. How can best I resolve that?

enter image description here

3
  • I think you're approaching this problem the wrong way. Rather than trying to rescale and adjust igraph node points, I would instead create a new spatial lines object from the polygon centroids and the igraph relationships. Individual lines should be created with the from and to points (centroids) defined from the igraph relationship table. Then you can simply plot your polygons, then your line relationships and finally add the labeled centroid points on top. – Matthew Bayly Mar 24 '20 at 18:06
  • Thanks for the input. I would prefer igraph because it is generally user friendly with many options already implemented – S Front Mar 24 '20 at 20:12
  • I'm working my way through this post now r-bloggers.com/three-ways-of-visualizing-a-graph-on-a-map – Michael Apr 20 '20 at 14:51
1

One way to approach this is to add the spatial information directly to the graph object. For this we can use {tidygraph} and {sfnetworks} and adapt the original script as:

# Load packages.
library(rnaturalearth)
library(igraph)
library(sf)
library(tmap)
library(dplyr)
library(sfnetworks)
library(tidygraph)

# Load countries from package. Use European countries.
countries <- st_as_sf(ne_countries(scale = 110, continent = "Europe"))
countries <- countries[!countries$adm0_a3 %in% c("RUS", "FRA"),]
# Populate matrix for with a three ties.
m <- matrix(0, length(countries$adm0_a3), length(countries$adm0_a3))
m[2,3] <- 1
m[3,6] <- 1
m[10,2] <- 1
# Add row and column names so that there is an index available 
# for identifying the nodes in the graph
row.names(m) <- countries$adm0_a3
colnames(m) <- countries$adm0_a3

# Get igraph object from matrix.
g <- graph_from_adjacency_matrix(m)

# Convert to tbl_graph
g <- as_tbl_graph(g) 

# Get centroids from countries 
countries_centroid <- countries %>% 
  st_transform(3042) %>% 
  st_centroid()
#> Warning in st_centroid.sf(.): st_centroid assumes attributes are constant over
#> geometries of x

Then we can create an sfnetwork by joining the POINT data from the countries_centroid object.

# Join the tbl_graph with the countries centroids by the node index
(g_sfn <- g %>% 
  left_join(countries_centroid, by = c('name'='adm0_a3')) %>% 
  # This can be directly converted to an sfnetwork because a POINT
  # geometry column was added during the left join
  # edges_as_lines creates spatially explicit edges
  as_sfnetwork(directed = TRUE, edges_as_lines = TRUE))
#> Checking if spatial network structure is valid...
#> Spatial network structure is valid
#> # A sfnetwork with 37 nodes and 3 edges
#> #
#> # CRS:  EPSG:3042 
#> #
#> # A rooted forest with 34 trees with spatially explicit edges
#> #
#> # Node Data:     37 x 64 (active)
#> # Geometry type: POINT
#> # Dimension:     XY
#> # Bounding box:  xmin: -235672 ymin: 4396380 xmax: 3004720 ymax: 7483925
#>   name  scalerank featurecla labelrank sovereignt sov_a3 adm0_dif level type 
#>   <chr>     <int> <chr>          <dbl> <chr>      <chr>     <dbl> <dbl> <chr>
#> 1 ALB           1 Admin-0 c~         6 Albania    ALB           0     2 Sove~
#> 2 AUT           1 Admin-0 c~         4 Austria    AUT           0     2 Sove~
#> 3 BEL           1 Admin-0 c~         2 Belgium    BEL           0     2 Sove~
#> 4 BGR           1 Admin-0 c~         4 Bulgaria   BGR           0     2 Sove~
#> 5 BIH           1 Admin-0 c~         5 Bosnia an~ BIH           0     2 Sove~
#> 6 BLR           1 Admin-0 c~         4 Belarus    BLR           0     2 Sove~
#> # ... with 31 more rows, and 55 more variables: admin <chr>, geou_dif <dbl>,
#> #   geounit <chr>, gu_a3 <chr>, su_dif <dbl>, subunit <chr>, su_a3 <chr>,
#> #   brk_diff <dbl>, name.y <chr>, name_long <chr>, brk_a3 <chr>,
#> #   brk_name <chr>, brk_group <chr>, abbrev <chr>, postal <chr>,
#> #   formal_en <chr>, formal_fr <chr>, note_adm0 <chr>, note_brk <chr>,
#> #   name_sort <chr>, name_alt <chr>, mapcolor7 <dbl>, mapcolor8 <dbl>,
#> #   mapcolor9 <dbl>, mapcolor13 <dbl>, pop_est <dbl>, gdp_md_est <dbl>,
#> #   pop_year <dbl>, lastcensus <dbl>, gdp_year <dbl>, economy <chr>,
#> #   income_grp <chr>, wikipedia <dbl>, fips_10 <chr>, iso_a2 <chr>,
#> #   iso_a3 <chr>, iso_n3 <chr>, un_a3 <chr>, wb_a2 <chr>, wb_a3 <chr>,
#> #   woe_id <dbl>, adm0_a3_is <chr>, adm0_a3_us <chr>, adm0_a3_un <dbl>,
#> #   adm0_a3_wb <dbl>, continent <chr>, region_un <chr>, subregion <chr>,
#> #   region_wb <chr>, name_len <dbl>, long_len <dbl>, abbrev_len <dbl>,
#> #   tiny <dbl>, homepart <dbl>, geometry <POINT [m]>
#> #
#> # Edge Data:     3 x 3
#> # Geometry type: LINESTRING
#> # Dimension:     XY
#> # Bounding box:  xmin: 1036132 ymin: 5417527 xmax: 2522298 ymax: 6385757
#>    from    to                           geometry
#>   <int> <int>                   <LINESTRING [m]>
#> 1     2     3 (1781097 5417527, 1036132 5638697)
#> 2     3     6 (1036132 5638697, 2522298 6385757)
#> 3    10     2 (1300083 6287745, 1781097 5417527)

To do plotting with packages like tmap, extracting the edges and nodes from the sfnetwork object is needed:


# Plot with tmap
tm_shape(countries) +
  tm_polygons(col = "white") +
  tm_shape(st_as_sf(g_sfn, 'edges')) +
  tm_lines(lwd = 2, col = 'red') +
  tm_shape(st_as_sf(g_sfn, 'nodes')) +
  tm_dots(col = 'yellow', size = 0.5) 

But this can also be achieved with the base plot from R:

# Plot with base R 
par(mar = c(0,0,0,0))
plot(st_geometry(countries))
plot(g_sfn, cex = 1.5, col = 'red', add = TRUE)

Note that the g_sfn object subclasses igraph so many of the methods can be directly used.

class(g_sfn)
#> [1] "sfnetwork" "tbl_graph" "igraph"

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.