thomasp85 / ggraph

Grammar of Graph Graphics
https://ggraph.data-imaginist.com
Other
1.08k stars 116 forks source link

Add support for nodes and edges in geographical space #357

Closed loreabad6 closed 9 months ago

loreabad6 commented 10 months ago

See #275 for details. Main issues for now are:

  1. the implementation of GeomEdgeSf in geom_edge_sf.R
  2. facetting for certain cases
  3. handling sfnetworks and sf in the NAMESPACE
thomasp85 commented 10 months ago

As for your list of things, I've addressed 1) in my suggestions, 2) is unknown to me - do you have some examples of failures and I'll be happy to look into them, and 3) is also handled in the suggestions I think.

loreabad6 commented 10 months ago

Thank you for the suggestions, I have incorporated them.

Following up on the list:

  1. GeomEdgeSf now recognizes aesthetics but does not recognize parameters outside aes() unless edge_* is added.
    ggraph(net, 'sf') +
    geom_node_sf(aes(color = central)) +
    geom_edge_sf(color = 'grey')
    #> Warning in layer_sf(geom = GeomEdgeSf, data = data, mapping = mapping, stat = StatFilterSf, :
    #> Ignoring unknown parameters: `colour`


### Need to write edge_* to get parameter
ggraph(net, 'sf') +
  geom_node_sf(aes(color = central)) +
  geom_edge_sf(edge_color = 'grey')

loreabad6 commented 10 months ago

And an example of a failed facetting case for point 2:

library(sfnetworks)
library(tidygraph)
library(ggraph)

net = roxel %>% 
  as_sfnetwork() %>% 
  mutate(centrality = centrality_betweenness()) %>% 
  mutate(central = ifelse(centrality > 1000, T, F)) %>% 
  activate('edges') %>% 
  mutate(azimuth = edge_azimuth(), length = edge_length())

ggraph(net, 'sf') +
  geom_node_sf(color = 'red', size = 0.05) +
  geom_edge_sf(aes(color = type)) +
  facet_graph(type ~ central) 
#> Warning: Unknown or uninitialised column: `.ggraph.index`.
#> Error in `x[i, , drop = drop]`:
#> ! Can't subset rows with `i`.
#> ✖ Logical subscript `i` must be size 1 or 701, not 0.
#> Backtrace:
#>      ▆
#>   1. ├─base::tryCatch(...)
#>   2. │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
#>   3. │   ├─base (local) tryCatchOne(...)
#>   4. │   │ └─base (local) doTryCatch(return(expr), name, parentenv, handler)
#>   5. │   └─base (local) tryCatchList(expr, names[-nh], parentenv, handlers[-nh])
#>   6. │     └─base (local) tryCatchOne(expr, names, parentenv, handlers[[1L]])
#>   7. │       └─base (local) doTryCatch(return(expr), name, parentenv, handler)
#>   8. ├─base::withCallingHandlers(...)
#>   9. ├─base::saveRDS(...)
#>  10. ├─base::do.call(...)
#>  11. ├─base (local) `<fn>`(...)
#>  12. ├─global `<fn>`(input = base::quote("cheap-stoat_reprex.R"))
#>  13. │ └─rmarkdown::render(input, quiet = TRUE, envir = globalenv(), encoding = "UTF-8")
#>  14. │   └─knitr::knit(knit_input, knit_output, envir = envir, quiet = quiet)
#>  15. │     └─knitr:::process_file(text, output)
#>  16. │       ├─knitr:::handle_error(...)
#>  17. │       │ └─base::withCallingHandlers(...)
#>  18. │       ├─base::withCallingHandlers(...)
#>  19. │       ├─knitr:::process_group(group)
#>  20. │       └─knitr:::process_group.block(group)
#>  21. │         └─knitr:::call_block(x)
#>  22. │           └─knitr:::block_exec(params)
#>  23. │             └─knitr:::eng_r(options)
#>  24. │               ├─knitr:::in_input_dir(...)
#>  25. │               │ └─knitr:::in_dir(input_dir(), expr)
#>  26. │               └─knitr (local) evaluate(...)
#>  27. │                 └─evaluate::evaluate(...)
#>  28. │                   └─evaluate:::evaluate_call(...)
#>  29. │                     ├─evaluate (local) handle(...)
#>  30. │                     │ └─base::try(f, silent = TRUE)
#>  31. │                     │   └─base::tryCatch(...)
#>  32. │                     │     └─base (local) tryCatchList(expr, classes, parentenv, handlers)
#>  33. │                     │       └─base (local) tryCatchOne(expr, names, parentenv, handlers[[1L]])
#>  34. │                     │         └─base (local) doTryCatch(return(expr), name, parentenv, handler)
#>  35. │                     ├─base::withCallingHandlers(...)
#>  36. │                     ├─base::withVisible(value_fun(ev$value, ev$visible))
#>  37. │                     └─knitr (local) value_fun(ev$value, ev$visible)
#>  38. │                       └─knitr (local) fun(x, options = options)
#>  39. │                         ├─base::withVisible(knit_print(x, ...))
#>  40. │                         ├─knitr::knit_print(x, ...)
#>  41. │                         └─knitr:::knit_print.default(x, ...)
#>  42. │                           └─evaluate (local) normal_print(x)
#>  43. │                             ├─base::print(x)
#>  44. │                             └─ggplot2:::print.ggplot(x)
#>  45. │                               ├─ggplot2::ggplot_build(x)
#>  46. │                               ├─ggraph:::ggplot_build.ggraph(x)
#>  47. │                               ├─base::NextMethod() at ggraph/R/ggraph.R:149:3
#>  48. │                               └─ggplot2:::ggplot_build.ggplot(x)
#>  49. │                                 └─layout$setup(data, plot$data, plot$plot_env)
#>  50. │                                   └─ggplot2 (local) setup(..., self = self)
#>  51. │                                     └─base::lapply(...)
#>  52. │                                       └─ggplot2 (local) FUN(X[[i]], ...)
#>  53. │                                         └─ggraph (local) map_data(...)
#>  54. │                                           └─base::lapply(...) at ggraph/R/facet_graph.R:157:9
#>  55. │                                             └─ggraph (local) FUN(X[[i]], ...)
#>  56. │                                               ├─data[data$.ggraph.index %in% nodes, , drop = FALSE] at ggraph/R/facet_graph.R:158:11
#>  57. │                                               └─sf:::`[.sf`(data, data$.ggraph.index %in% nodes, , drop = FALSE) at ggraph/R/facet_graph.R:158:11
#>  58. │                                                 ├─x[i, , drop = drop]
#>  59. │                                                 └─tibble:::`[.tbl_df`(x, i, , drop = drop)
#>  60. │                                                   └─tibble:::vectbl_as_row_index(i, x, i_arg)
#>  61. │                                                     └─tibble:::vectbl_as_row_location(i, nr, i_arg, assign, call)
#>  62. │                                                       ├─tibble:::subclass_row_index_errors(...)
#>  63. │                                                       │ └─base::withCallingHandlers(...)
#>  64. │                                                       └─vctrs::vec_as_location(...)
#>  65. └─vctrs (local) `<fn>`()
#>  66.   └─vctrs:::stop_indicator_size(...)
#>  67.     └─rlang::cnd_signal(...)
thomasp85 commented 10 months ago

The edge param thing was an oversight when I made the last batch of suggestions.

Let me have a look at the facet thing

thomasp85 commented 10 months ago

So, it seems everything works if geom_node_sf() follows the behaviour of the other node geoms and set data = NULL rather than data = get_sf_nodes(). Would there be any concern with this? It seems all the relevant CRS information is captured in the geometry column in the layout so get_sf_nodes() is not needed

loreabad6 commented 10 months ago

So, it seems everything works if geom_node_sf() follows the behaviour of the other node geoms and set data = NULL rather than data = get_sf_nodes(). Would there be any concern with this? It seems all the relevant CRS information is captured in the geometry column in the layout so get_sf_nodes() is not needed

So I realized the main reason to include it is that when creating a ggraph only with sf nodes the projection of the data is lost, so there is distorsion since the nodes just adapt to the panel area. Maybe it could be handled with coord_sf? I will try to experiment

thomasp85 commented 9 months ago

Yeah, I can see the CRS are not picked up with the basic layout as nodes

loreabad6 commented 9 months ago

Thank you very much for reviewing this @thomasp85, I tested again and seems like all the issues I raised work now.

thomasp85 commented 9 months ago

Great - can I get you to add a NEWS bullet as well

loreabad6 commented 9 months ago

Great - can I get you to add a NEWS bullet as well

Done!

thomasp85 commented 9 months ago

Thank you!!!