1

It is easier to explain what am I trying to do through the following reproducible example:

library(sf)
library(brazilmaps)
library(spdep)

# loading cities map
cities_mg <- brazilmaps::get_brmap(geo        = 'City', 
                                   geo.filter = list(State = 31),
                                   class      = 'sf')

# creating adjacency matrix (neighborhood graph)
nb_mg <- spdep::poly2nb(cities_mg)

# plotting
coords_mg <- st_coordinates(st_centroid(st_geometry(cities_mg)))

par(bg = '#2E2E2E')
plot(nb_mg, coords_mg, pch = 19, cex = .6, lwd = .4, 
     col = '#00C040', points = T)

This piece of code generates the following plot enter image description here

What I would like to do, is coloring the nodes (or vertices) with the same color as the edges. Is it possible?

Thank you in advance.

Session info (for reproducibility) below.

R version 3.6.1 (2019-07-05)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 19.04

Matrix products: default
BLAS:   /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.8.0
LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.8.0

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8     LC_MONETARY=en_US.UTF-8   
 [6] LC_MESSAGES=en_US.UTF-8    LC_PAPER=en_US.UTF-8       LC_NAME=C                  LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] spdep_1.1-3      spData_0.3.2     sp_1.3-1         brazilmaps_0.1.0 sf_0.8-0        

loaded via a namespace (and not attached):
 [1] gtools_3.8.1       tidyselect_0.2.5   xfun_0.10          purrr_0.3.3        splines_3.6.1      lattice_0.20-38    expm_0.999-4      
 [8] htmltools_0.4.0    XML_3.98-1.20      rlang_0.4.1        pillar_1.4.2       e1071_1.7-2        glue_1.3.1         withr_2.1.2       
[15] DBI_1.0.0          semver_0.2.0       binman_0.1.1       caTools_1.17.1.2   coda_0.19-3        evaluate_0.14      knitr_1.25        
[22] wdman_0.2.4        callr_3.3.2        ps_1.3.0           class_7.3-15       Rcpp_1.0.2         KernSmooth_2.23-16 clipr_0.7.0       
[29] openssl_1.4.1      classInt_0.4-1     gdata_2.18.0       RSelenium_1.7.5    deldir_0.1-23      fs_1.3.1           askpass_1.1       
[36] digest_0.6.22      gmodels_2.18.1     processx_3.4.1     dplyr_0.8.3        grid_3.6.1         tools_3.6.1        bitops_1.0-6      
[43] LearnBayes_2.15.1  magrittr_1.5       tibble_2.1.3       crayon_1.3.4       pkgconfig_2.0.3    whisker_0.4        MASS_7.3-51.4     
[50] Matrix_1.2-17      reprex_0.3.0       rstudioapi_0.10    assertthat_0.2.1   rmarkdown_1.16     R6_2.4.0           boot_1.3-23       
[57] units_0.6-5        nlme_3.1-141       compiler_3.6.1    
lcgodoy
  • 763
  • 5
  • 14
  • I cannot produce the graphic based on your code. I think the last plot line has something wrong. – jazzurro Nov 13 '19 at 15:01
  • You can't reproduce because there is a bug on this function. Also, we should replace `points = F` by `points = T`. I'm editing it in order to make it reproducible. Thank you – lcgodoy Nov 13 '19 at 15:18
  • 1
    This needs a recent version of `spdep` to be reproducible, because until recently `spdep` didn't return `sf` objects, only `sp` objects which would then break the `st_etc` functions. – Spacedman Nov 14 '19 at 16:35

2 Answers2

3

Here's a trick. The plot.nb calls points to draw the points, and this is an S3 method. So if we can make it run on an object which we can define a class for, we can make it do anything.

At the moment it takes a 2-column matrix from st_coordinates, and the plot.nb function takes the two columns and feeds those to points(x,y,...). If we instead construct a 2-column data frame from the matrix:

coords_mg = data.frame(coords_mg[,1],coords_mg[,2])

then it will still work. If we then give the first column a new class:

class(coords_mg[,1]) = "adjpts"

then the points(x,y,...) will call the method for adjpts, which we define as this:

points.adjpts=function(x,y,...){
  points(unclass(x),unclass(y),col="#00c040",pch=19)
}

Then doing:

plot(nb_mg, coords_mg, pch = 19, cex = .6, lwd = .4,col = '#00C040', points = T)

does the magic resulting in:

enter image description here

Spacedman
  • 92,590
  • 12
  • 140
  • 224
  • Oh! This is really cool! I think that replacing `points(unclass(x),unclass(y),col="#00c040",pch=19)` by `points(unclass(x),unclass(y),col="#00c040", ...)` gives us a little bit more of flexibility. – lcgodoy Nov 15 '19 at 20:46
2

The best shot I can have is the following. You want to use pch = 21 and bg = '#00C040'. Then you can see the same color in the points. The only thing that I cannot remove is the outler black line of the circles.

plot(cities_mg['geometry'], border = 'black', bg = 'white', lwd = .4, cex = .4)
plot.nb(x = nb_mg, coords = coords_mg, pch = 21,
        cex = 1, lwd = .4, col = '#00C040', bg = '#00C040',
        points = TRUE, add = TRUE)

enter image description here

jazzurro
  • 23,179
  • 35
  • 66
  • 76
  • 1
    The source code of `plot.nb` is quite clearly written, and the problem is that any `col=` argument is only used in drawing the lines, and the only things passed to `points` are the `...` arguments. You could edit the code and add a new argument - `pcol` perhaps - that is passed through to the `points` call in the last line... – Spacedman Nov 14 '19 at 16:32
  • 1
    @Spacedman Thanks for the feedback. When I saw the help page, I thought there was not much space to move around and this is what I could do. Thanks for helping me think how I could have moved around in this case. +1 for the trick! – jazzurro Nov 15 '19 at 02:14