将地图中的多边形着色,使相邻的多边形具有不同的颜色。

5

我制作了以下地图:

library(sf)  
library(leaflet)
library(leafgl)
library(colourvalues)
library(leaflet.extras)


nc <- st_read(system.file("gpkg/nc.gpkg", package="sf"), quiet = TRUE) %>% 
  st_transform(st_crs(4326)) %>% 
  st_cast('POLYGON')

leaflet(data = nc) %>% addPolygons( stroke = FALSE) %>% addTiles(group = "OSM") %>%  addProviderTiles(provider = providers$OpenStreetMap) %>% addPolygons(data = nc, weight=1, popup = ~NAME,
                label = ~NAME, group = "name", col = 'blue') %>% 
    addSearchFeatures(targetGroups  = 'name', options = searchFeaturesOptions(zoom=10, openPopup=TRUE))

enter image description here

我想要给多边形上色,使它们更容易被看到 - 我通过随机分配颜色来实现这一点:
nc$color <- sample(c("red", "blue", "green", "yellow", "purple"), nrow(nc), replace = TRUE)

leaflet(data = nc) %>% 
    addTiles(group = "OSM") %>% 
    addProviderTiles(provider = providers$OpenStreetMap) %>% 
    addPolygons(data = nc, weight=1, popup = ~NAME,
                label = ~NAME, group = "name", fillColor = ~color, fillOpacity = 0.5) %>% 
    addSearchFeatures(targetGroups  = 'name', options = searchFeaturesOptions(zoom=10, openPopup=TRUE))

enter image description here

我的问题:受到这个著名的计算机科学问题https://en.wikipedia.org/wiki/Four_color_theorem的启发,我想以随机的方式对多边形进行着色,使相邻的多边形颜色不同。

我认为我首先需要将 shapefile/地图转换成网络图:

library(igraph)
adj <- st_touches(nc, sparse = TRUE)
g <- graph_from_adjacency_matrix(as.matrix(adj))
plot(g)

我不确定如何继续解决这个问题 - 目前,我想到了一种间接的方法,即简单地选择许多不同的随机颜色来减少两个多边形具有相同颜色的可能性,但我对学习解决原始问题的新颖和创造性方法感兴趣。

请问有人可以向我展示如何做到这一点吗?

谢谢!


1
你想使用多少种颜色有限制吗? - tospig
@ tospig:感谢您的回复!我以前从未考虑过这个限制!我可以接受任何数量的颜色!例如,我们假设有6种颜色?红色、蓝色、绿色、黄色、紫色、橙色?但是我对任何事情都持开放态度!非常感谢! - stats_noob
这不是一个“R”问题。我建议您搜索讨论四色地图分配的简单(可以这么说)实现的页面。 - Carl Witthoft
1
igraph有greedy_vertex_coloring()函数。通常情况下,它无法仅使用四种颜色完成染色。当然,您需要构建多边形的邻接图。 - Szabolcs
https://igraph.org/r/html/1.3.0/greedy_vertex_coloring.html - stats_noob
显示剩余6条评论
2个回答

2

您可以使用MapColoring包。该包使用DSatur算法,在这种情况下能够找到问题的最小(四色)解决方案,而基于贪婪算法的解决方案则不行。一般来说,DSatur 已被证明比贪婪算法更能得出更好的顶点着色。

devtools::install_github("hunzikp/MapColoring")
library(sp)
library(MapColoring)

my.palette = RColorBrewer::brewer.pal(4, 'Set1')
nc$color = my.palette[getColoring(as(nc, 'Spatial'))]

要查看地图,您可以使用与问题中相同的代码:

leaflet(data = nc) %>% 
    addTiles(group = "OSM") %>% 
    addProviderTiles(provider = providers$OpenStreetMap) %>% 
    addPolygons(data = nc, weight=1, popup = ~NAME,
                label = ~NAME, group = "name", fillColor = ~color, fillOpacity = 0.5) %>% 
    addSearchFeatures(targetGroups  = 'name', options = searchFeaturesOptions(zoom=10, openPopup=TRUE))

enter image description here


1
关于“这将为您提供最小(四色)解决方案”的声明:MapColoring软件包声称使用DSatur算法,但该算法不能保证最优解,甚至对于平面图也是如此。我知道该软件包还声称使用最少数量的颜色,但如果依赖于DSatur,那么这个说法就不正确。 - Szabolcs
顺便提一下,igraph 0.10 包含了 DSatur 的准确实现(MapColoring 的实现与论文有些微小的偏差)。一旦 R/igraph 更新到 0.10 版本,它将在 greedy_vertex_coloring() 中可用,希望能在今年夏天更新。 - Szabolcs
1
感谢您的评论,@Szabolcs。我更新了答案以使其更准确。 - dww
因此,将DSatur与“贪心算法”进行比较是没有意义的。在R/igraph 1.4中,“greedy_vertex_coloring()”实现了一种单一的排序启发式方法:我们要着色的下一个顶点是已经着色邻居最多的顶点。DSatur选择具有最多相邻颜色的顶点。两者都有一些额外的细微差别,如何打破平局。还有其他几种可能的排序启发式方法,例如NetworkX目前实现了7种不同的方法:https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.coloring.greedy_color.html - Szabolcs
@dww:我加载了tmaptools库并运行了您的代码,但是怎样才能查看地图呢? - stats_noob
显示剩余4条评论

2

使用 tmap

library(tmap)
tmap_mode(mode = "view") # if you want an interactive leaflet map
tm_shape(nc) + tm_polygons(col = "MAP_COLORS", minimize = TRUE)

tm_polygons中,当col设置为"MAP_COLORS"时:

多边形将被着色,以使相邻的多边形不会得到相同的颜色。有关详细信息,请参见底层函数tmaptools中的map_coloring

?map_coloring了解minimize参数:

逻辑值,确定算法是否将搜索最小数量的颜色


使用tm_polygons中的palette参数设置首选颜色。

供参考,非交互式ggplot地图相关帖子:

使用ggplot在地图上以不同颜色着色相邻区域


我已经尝试了tmaptools::map_coloring,但是算法在4种颜色上无法收敛(它为一个县使用了第五种颜色)。你能否检查这是否按预期工作,因为我认为它取决于相同的函数。 - dww
@dww 上面的代码在 OP 的 nc 上运行正常。我没有硬编码颜色数量。干杯 - Henrik
我尝试了一下,正如您可以在这张图片中看到的,一个县有额外的第五种颜色。 - dww
1
@dww确实如此,但也请注意OP的评论:_我可以接受任何数量的颜色!例如,假设有6种?红色、蓝色、绿色、黄色、紫色、橙色?但我对任何事情都持开放态度!_。我并不声称我的代码会产生四种颜色;这只是解决问题的一种(方便)方式。干杯 - Henrik
@ Henrik:非常感谢您的回答!我真的很喜欢tmaptools中的这个功能,它允许您单击任何多边形并查看与该多边形对应的行的所有列的值。非常好! - stats_noob
@stats_noob 这不是tmaptool的一个特性,而是在渲染leaflet图时使用了tmap的默认设置。正如我在我的答案中所写的那样,tmap_mode(mode = "view")会生成一个交互式的leaflet小部件。然后检查tm_fill(从tm_polygons调用)的popup.vars参数:“在“视图”模式下显示在弹出窗口中的数据变量的名称。[...]如果未指定它们,则会显示所有变量。” - Henrik

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接