Leaflet需按OBJECTID排序.shp数据的原因及图例排序方法
我正在用R的leaflet包构建带交互式地图的Shiny应用,流程是把统计数据合并到SpatialPolygonsDataFrame的@data部分,代码如下:
map <- readOGR("FilePath","map") # 合并处理好的数据框与地图数据 map@data = merge(map@data, stats, by= 'name', all = TRUE)
合并后的属性表结构如下:
| name | OBJECTID | ID_NUM | SHAPE_Leng | SHAPE_Area | Stat1 | Stat2 |
|---|---|---|---|---|---|---|
| First | 1 | 3 | x1 | y1 | .90 | 32 |
| Second | 2 | 4 | x2 | y2 | .85 | 33 |
我发现必须执行map@data <- map@data[order(map@data$OBJECTID),]按OBJECTID排序,Leaflet才能把数据正确匹配到对应区域并显示标签。现在有两个问题:
- 为何不排序会导致Leaflet的标签匹配错误?
- 如何自定义图例的排序方式(可以用leaflet或dplyr实现)?
问题1:不排序导致匹配错误的原因
SpatialPolygonsDataFrame的核心是空间多边形几何信息和属性表数据的一一对应关系:
- 几何部分(
map@polygons)是按原始加载顺序存储的多边形对象列表 - 属性表(
map@data)的每一行必须和几何列表的索引完全对应
当你用merge()合并数据时,merge()会默认按合并键(这里是name)重新排序属性表,导致map@data的行顺序和map@polygons的几何顺序脱节。比如原来几何列表第1个是OBJECTID=1的多边形,但合并后属性表第1行可能变成OBJECTID=2的数据,Leaflet渲染时会按索引对应,自然就把数据贴错了区域。
用order(map@data$OBJECTID)重新排序,就是让属性表的行顺序和几何部分的原始索引(通常和OBJECTID对应)重新对齐,从而恢复一一对应的关系。
问题2:自定义图例排序的方法
方法1:用dplyr提前处理数据的因子顺序
如果你的图例是基于分类变量,先把变量转成指定顺序的因子:
library(dplyr) # 比如要让Stat1的图例按从高到低排序,先把Stat1转成带顺序的因子 map@data <- map@data %>% mutate(Stat1_cat = cut(Stat1, breaks = c(0, 0.85, 0.9, 1), labels = c("低", "中", "高")) %>% factor(levels = c("高", "中", "低"))) # 自定义图例显示顺序
然后在leaflet里调用这个因子变量,图例就会按你设定的levels顺序显示。
方法2:在leaflet中手动指定图例顺序
如果是连续变量转成的颜色分级,或者想直接控制图例项的顺序,可以在addLegend()里通过values参数指定顺序:
library(leaflet) # 先定义颜色映射 pal <- colorFactor(palette = c("green", "yellow", "red"), levels = c("高", "中", "低"), # 这里指定顺序 data = map@data$Stat1_cat) # 添加图例时直接用定义好的pal,或者手动传values leaflet(map) %>% addTiles() %>% addPolygons(fillColor = ~pal(Stat1_cat)) %>% addLegend(pal = pal, values = ~Stat1_cat, title = "Stat1分级", position = "bottomright")
如果是数值型的图例,比如想让图例从大到小显示,可以先把数值按目标顺序排序后传给values:
# 比如让Stat2的图例从33到32显示 sorted_vals <- sort(unique(map@data$Stat2), decreasing = TRUE) pal_num <- colorNumeric(palette = "Blues", domain = map@data$Stat2) leaflet(map) %>% addTiles() %>% addPolygons(fillColor = ~pal_num(Stat2)) %>% addLegend(pal = pal_num, values = sorted_vals, title = "Stat2数值", position = "bottomright")
内容的提问来源于stack exchange,提问作者PotterFan

