Leaflet Polylines do not go over date line 传单
Leaflet Polylines do not go over date line Leaflet
对于 Shiny uni 项目,我遇到了折线穿过太平洋日期变更线的问题。从一个中心(武汉)我想创建 72 条线来显示它们在不同地区的连接。我已经创建了一个循环以确保经度 > 0 已更改为大于 0 的经度。有没有人有解决方案来使多段线正确穿过日期变更线?
在照片中你可以看到我目前的情节没有正确越界。
library(leaflet)
library(geosphere)
library(sp)
df <- read.table(text = "
Longitude.Central Latitude.Central Longitude Latitude
112.2707 30.97564 117.2264 31.82571
112.2707 30.97564 116.4142 40.18238
112.2707 30.97564 4.4699 50.50390
112.2707 30.97564 -71.0589 42.36010
112.2707 30.97564 -123.1210 49.28270
112.2707 30.97564 104.9910 12.56570",
header = TRUE)
p1 <- as.matrix(df[,c(1,2)])
p2 <- data.frame(df[,c(3,4)])
p3 <- matrix(nrow = length(p2))
for (j in 1:nrow(p2)) {
if (p2[j,]$Longitude < 0) {
p3[j] <- p2[j,]$Longitude + 360
} else {
p3[j] <- p2[j,]$Longitude
}
}
p2 <- as.matrix(cbind(p3, p2$Latitude))
df2 <- gcIntermediate(
p1, p2,
n = 72,
addStartEnd = TRUE,
sp = T)
leaflet( ) %>%
setView(df$Longitude.Central[[1]], lat = df$Latitude.Central[[1]], 1) %>%
addTiles(urlTemplate = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png") %>%
addPolylines(data = df2, weight = 1
)
# Head of the data
> head(df)
# A tibble: 6 x 4
Longitude.Central Latitude.Central Longitude Latitude
<dbl> <dbl> <dbl> <dbl>
1 112.2707 30.97564 117.2264 31.82571
2 112.2707 30.97564 116.4142 40.18238
3 112.2707 30.97564 4.4699 50.50390
4 112.2707 30.97564 -71.0589 42.36010
5 112.2707 30.97564 -123.1210 49.28270
6 112.2707 30.97564 104.9910 12.56570
您可以尝试几件事。一种是在 gcIntermediate
:
中使用 breakAtDateLine = TRUE
选项
df2 <- gcIntermediate(
p1, p2,
n = 72,
addStartEnd = TRUE,
sp = T,
breakAtDateLine = T)
leaflet( ) %>%
setView(lng = df$Longitude.Central[[1]], lat = df$Latitude.Central[[1]], 1) %>%
addTiles(urlTemplate = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png") %>%
addPolylines(data = df2, weight = 1
)
如您所见,它打断了线条,但在屏幕左侧继续显示,这并不理想。
我们可以尝试的另一件事是 运行 使用 breakAtDateLine = FALSE
的 gcIntermediate 函数并手动调整纬度和经度 在 我们 运行功能。如果我们设置 sp=FALSE
.
这将是最简单的
注意我们如何只需要更正从我们所在位置向东的线 - 这些是唯一穿过日期变更线的线。每个位置都不一样,但逻辑应该是相似的。
p1 <- as.matrix(df[,c(1,2)])
p2 <- data.frame(df[,c(3,4)])
df2 <- gcIntermediate(
as.matrix(p1),
as.matrix(p2),
n = 72,
addStartEnd = TRUE,
breakAtDateLine = F,
sp = F)
# correct the longitudes
res <- lapply(df2, function(x) {
# if direction is east, correct lon, else don't
if(x[,'lon'][2] - x[,'lon'][1] > 0){
cbind(lon =ifelse(x[,'lon']<0, x[,'lon'] + 360, x[,'lon']), lat = x[,'lat'])
} else {
x
}
})
# and convert back to sp (I just took this code from the gcIntermediate function)
for (i in 1:length(res)) {
if (!is.list(res[[i]])) {
res[[i]] <- Lines(list(Line(res[[i]])), ID = as.character(i))
}
else {
res[[i]] <- Lines(list(Line(res[[i]][[1]]), Line(res[[i]][[2]])),
ID = as.character(i))
}
}
res <- SpatialLines(res, CRS("+proj=longlat +ellps=WGS84"))
leaflet() %>%
setView(df$Latitude.Central[[1]], lat = df$Longitude.Central[[1]], 1) %>%
addTiles(urlTemplate = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png") %>%
addPolylines(data = res, weight = 1)
当它到达地图顶部时仍然有点奇怪,但希望这是您可以接受的
我最近在处理仪表板时遇到了同样的问题。我得出的解决方案有点混合了 Chris 上面的评论。差异:
不创建p1和p2,直接在gcIntermediate中调用df列
保持sp = T
和breakAtDateLine = T
for 循环对生成的 sp 对象执行,不修改 df 并使用 SpatialLines
进行转换
df2 <- gcIntermediate(
df[,c(1,2)],
df[,c(3,4)],
n = 72,
addStartEnd = TRUE,
sp = T,
breakAtDateLine = T)
for (l in 1:length(df2@lines)){
if (length(df2@lines[[l]]@Lines) > 1){
df2@lines[[l]]@Lines[[2]]@coords[ ,1] <- df2@lines[[l]]@Lines[[2]]@coords[ ,1] +360
df2@lines[[l]]@Lines[[1]]@coords <- rbind(df2@lines[[l]]@Lines[[1]]@coords,
df2@lines[[l]]@Lines[[2]]@coords)
df2@lines[[l]]@Lines[[2]] <- NULL
}
}
leaflet() %>%
setView(df$Latitude.Central[[1]], lat = df$Longitude.Central[[1]], 1) %>%
addTiles(urlTemplate = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png") %>%
addPolylines(data = df2, weight = 1)
output (similar to Chris's)
该循环利用了 gcIntermediate()
通常只为不跨越 ante-meridiem 的要素创建一条线这一事实。但是当线确实交叉时,它会为该特征制作两条线,设置在负坐标中的线始终在列表中排在第二位。因此,循环查找具有 2 个特征的行,取第二个特征的长并添加 +360,将第二行坐标绑定到第一行并将第二行设为 Nulls。
这个解决方案做的事情几乎相同,但速度并不快(我针对 Chris 的第二个答案 1000 倍做了基准测试 运行,这个解决方案平均只快 0.3%)。但是,它确实减少了代码、对象混乱以及通过转换处理数据。
对于 Shiny uni 项目,我遇到了折线穿过太平洋日期变更线的问题。从一个中心(武汉)我想创建 72 条线来显示它们在不同地区的连接。我已经创建了一个循环以确保经度 > 0 已更改为大于 0 的经度。有没有人有解决方案来使多段线正确穿过日期变更线?
在照片中你可以看到我目前的情节没有正确越界。
library(leaflet)
library(geosphere)
library(sp)
df <- read.table(text = "
Longitude.Central Latitude.Central Longitude Latitude
112.2707 30.97564 117.2264 31.82571
112.2707 30.97564 116.4142 40.18238
112.2707 30.97564 4.4699 50.50390
112.2707 30.97564 -71.0589 42.36010
112.2707 30.97564 -123.1210 49.28270
112.2707 30.97564 104.9910 12.56570",
header = TRUE)
p1 <- as.matrix(df[,c(1,2)])
p2 <- data.frame(df[,c(3,4)])
p3 <- matrix(nrow = length(p2))
for (j in 1:nrow(p2)) {
if (p2[j,]$Longitude < 0) {
p3[j] <- p2[j,]$Longitude + 360
} else {
p3[j] <- p2[j,]$Longitude
}
}
p2 <- as.matrix(cbind(p3, p2$Latitude))
df2 <- gcIntermediate(
p1, p2,
n = 72,
addStartEnd = TRUE,
sp = T)
leaflet( ) %>%
setView(df$Longitude.Central[[1]], lat = df$Latitude.Central[[1]], 1) %>%
addTiles(urlTemplate = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png") %>%
addPolylines(data = df2, weight = 1
)
# Head of the data
> head(df)
# A tibble: 6 x 4
Longitude.Central Latitude.Central Longitude Latitude
<dbl> <dbl> <dbl> <dbl>
1 112.2707 30.97564 117.2264 31.82571
2 112.2707 30.97564 116.4142 40.18238
3 112.2707 30.97564 4.4699 50.50390
4 112.2707 30.97564 -71.0589 42.36010
5 112.2707 30.97564 -123.1210 49.28270
6 112.2707 30.97564 104.9910 12.56570
您可以尝试几件事。一种是在 gcIntermediate
:
breakAtDateLine = TRUE
选项
df2 <- gcIntermediate(
p1, p2,
n = 72,
addStartEnd = TRUE,
sp = T,
breakAtDateLine = T)
leaflet( ) %>%
setView(lng = df$Longitude.Central[[1]], lat = df$Latitude.Central[[1]], 1) %>%
addTiles(urlTemplate = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png") %>%
addPolylines(data = df2, weight = 1
)
如您所见,它打断了线条,但在屏幕左侧继续显示,这并不理想。
我们可以尝试的另一件事是 运行 使用 breakAtDateLine = FALSE
的 gcIntermediate 函数并手动调整纬度和经度 在 我们 运行功能。如果我们设置 sp=FALSE
.
注意我们如何只需要更正从我们所在位置向东的线 - 这些是唯一穿过日期变更线的线。每个位置都不一样,但逻辑应该是相似的。
p1 <- as.matrix(df[,c(1,2)])
p2 <- data.frame(df[,c(3,4)])
df2 <- gcIntermediate(
as.matrix(p1),
as.matrix(p2),
n = 72,
addStartEnd = TRUE,
breakAtDateLine = F,
sp = F)
# correct the longitudes
res <- lapply(df2, function(x) {
# if direction is east, correct lon, else don't
if(x[,'lon'][2] - x[,'lon'][1] > 0){
cbind(lon =ifelse(x[,'lon']<0, x[,'lon'] + 360, x[,'lon']), lat = x[,'lat'])
} else {
x
}
})
# and convert back to sp (I just took this code from the gcIntermediate function)
for (i in 1:length(res)) {
if (!is.list(res[[i]])) {
res[[i]] <- Lines(list(Line(res[[i]])), ID = as.character(i))
}
else {
res[[i]] <- Lines(list(Line(res[[i]][[1]]), Line(res[[i]][[2]])),
ID = as.character(i))
}
}
res <- SpatialLines(res, CRS("+proj=longlat +ellps=WGS84"))
leaflet() %>%
setView(df$Latitude.Central[[1]], lat = df$Longitude.Central[[1]], 1) %>%
addTiles(urlTemplate = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png") %>%
addPolylines(data = res, weight = 1)
当它到达地图顶部时仍然有点奇怪,但希望这是您可以接受的
我最近在处理仪表板时遇到了同样的问题。我得出的解决方案有点混合了 Chris 上面的评论。差异:
不创建p1和p2,直接在gcIntermediate中调用df列
保持
sp = T
和breakAtDateLine = T
for 循环对生成的 sp 对象执行,不修改 df 并使用 SpatialLines
进行转换df2 <- gcIntermediate( df[,c(1,2)], df[,c(3,4)], n = 72, addStartEnd = TRUE, sp = T, breakAtDateLine = T) for (l in 1:length(df2@lines)){ if (length(df2@lines[[l]]@Lines) > 1){ df2@lines[[l]]@Lines[[2]]@coords[ ,1] <- df2@lines[[l]]@Lines[[2]]@coords[ ,1] +360 df2@lines[[l]]@Lines[[1]]@coords <- rbind(df2@lines[[l]]@Lines[[1]]@coords, df2@lines[[l]]@Lines[[2]]@coords) df2@lines[[l]]@Lines[[2]] <- NULL } } leaflet() %>% setView(df$Latitude.Central[[1]], lat = df$Longitude.Central[[1]], 1) %>% addTiles(urlTemplate = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png") %>% addPolylines(data = df2, weight = 1)
output (similar to Chris's)
该循环利用了 gcIntermediate()
通常只为不跨越 ante-meridiem 的要素创建一条线这一事实。但是当线确实交叉时,它会为该特征制作两条线,设置在负坐标中的线始终在列表中排在第二位。因此,循环查找具有 2 个特征的行,取第二个特征的长并添加 +360,将第二行坐标绑定到第一行并将第二行设为 Nulls。
这个解决方案做的事情几乎相同,但速度并不快(我针对 Chris 的第二个答案 1000 倍做了基准测试 运行,这个解决方案平均只快 0.3%)。但是,它确实减少了代码、对象混乱以及通过转换处理数据。