关于plot:如何在R :: lattice :: layerplot上显示投影地图?

how to display a projected map on an R::lattice::layerplot?

摘要:我可以在lattice::layerplot上显示lon-lat映射,并且可以在spam::image上显示Lambert保形圆锥(LCC)映射。如何在lattice::layerplot上显示LCC映射?以下是如何不执行此操作的示例-赞赏了修复方面的帮助(甚至只是调试)。

详细信息:

我一直在成功使用网格图形(通过latticeExtrarasterVis)来显示未投影的经纬度的全球大气数据,效果很好(尽管我对ggplot / ggmap确实很感兴趣) 。特别是,我能够用地图覆盖这些图,这对于我正在做的工作是必不可少的。但是,我目前无法对某些区域数据投影的LCC使用lattice::layerplot:数据图,但是我无法覆盖地图。我可以通过更粗略的方式来执行此操作,但希望知道如何使用lattice/rasterVis或类似方法(例如ggplot/ggmap)执行此操作。接下来是两个几乎独立的示例...但是,如果您已经知道如何执行此操作,请告诉我,我将跳过调试。 (我在一个项目上落后了。)

netCDF数据ozone_lcc.nc随CRAN软件包= M3一起提供...除了M3将其提供为

1
system.file("extdata/ozone_lcc.ncf", package="M3")

,该文件扩展名(.ncf)当前导致CRAN package = raster出现问题(请参阅此文章)。您可以重命名该文件(并将其放在当前工作目录中),也可以仅下载该文件(270 kB),也可以从M3 tarball中获取它(但请记住重命名!)。

然后,您可以运行以下任何示例(如果未运行Windows,则提供的(IIRC),不会在其中构建package = M3(但为ICBW)),并根据需要更改常量以适应您的系统。示例1生成了一种类型的映射,我知道(根据以前的经验)该类型将与levelplot中的raster一起使用;但是,在这种情况下,地图的坐标值与数据/栅格的坐标值不匹配。示例2使用了较旧样式的基础图形,并同时绘制了数据和地图。不幸的是,我不知道如何制作在levelplot上生成的地图。正如我想使此代码与其他使用rasterlevelplot的代码一起使用一样,这是一个问题。

示例1:产生类似this

的输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
##### start example 1 #####

library("M3")        # http://cran.r-project.org/web/packages/M3/
library("rasterVis") # http://cran.r-project.org/web/packages/rasterVis/

## Use an example file with projection=Lambert conformal conic.
# lcc.file <- system.file("extdata/ozone_lcc.ncf", package="M3")
lcc.file <-"./ozone_lcc.nc" # unfortunate problem with raster::raster
lcc.proj4 <- M3::get.proj.info.M3(lcc.file)
lcc.proj4   # debugging
# [1]"+proj=lcc +lat_1=33 +lat_2=45 +lat_0=40 +lon_0=-97 +a=6370000 +b=6370000"
# Note +lat_0=40 +lat_1=33 +lat_2=45 for maps::map@projection (below)
lcc.crs <- sp::CRS(lcc.proj4)
lcc.pdf <-"./ozone_lcc.example1.pdf" # for output

## Read in variable with daily ozone.
# o3.raster <- raster::raster(x=lcc.file, varname="O3", crs=lcc.crs)
# ozone_lcc.nc has 4 timesteps, choose 1 at random
o3.raster <- raster::raster(x=lcc.file, varname="O3", crs=lcc.crs, level=1)
o3.raster@crs <- lcc.crs # why does the above assignment not take?
# start debugging
o3.raster
summary(coordinates(o3.raster)) # thanks, Felix Andrews!
M3::get.grid.info.M3(lcc.file)
#   end debugging

# > o3.raster
# class       : RasterLayer
# band        : 1
# dimensions  : 112, 148, 16576  (nrow, ncol, ncell)
# resolution  : 1, 1  (x, y)
# extent      : 0.5, 148.5, 0.5, 112.5  (xmin, xmax, ymin, ymax)
# coord. ref. : +proj=lcc +lat_1=33 +lat_2=45 +lat_0=40 +lon_0=-97 +a=6370000 +b=6370000
# data source : .../ozone_lcc.nc
# names       : O3
# z-value     : 1
# zvar        : O3
# level       : 1
# > summary(coordinates(o3.raster))
#        x                y        
#  Min.   :  1.00   Min.   :  1.00  
#  1st Qu.: 37.75   1st Qu.: 28.75  
#  Median : 74.50   Median : 56.50  
#  Mean   : 74.50   Mean   : 56.50  
#  3rd Qu.:111.25   3rd Qu.: 84.25  
#  Max.   :148.00   Max.   :112.00  
# > M3::get.grid.info.M3(lcc.file)
# $x.orig
# [1] -2736000
# $y.orig
# [1] -2088000
# $x.cell.width
# [1] 36000
# $y.cell.width
# [1] 36000
# $hz.units
# [1]"m"
# $ncols
# [1] 148
# $nrows
# [1] 112
# $nlays
# [1] 1

# The grid (`coordinates(o3.raster)`) is integers, because this
# data is stored using the IOAPI convention. IOAPI makes the grid
# Cartesian by using an (approximately) equiareal projection (LCC)
# and abstracting grid positioning to metadata stored in netCDF
# global attributes. Some of this spatial metadata can be accessed
# by `M3::get.grid.info.M3` (above), and some can be accessed via
# the coordinate reference system (`CRS`, see `lcc.proj4` above)

## Get US state boundaries in projection units.
state.map <- maps::map(
  database="state", projection="lambert", par=c(33,45), plot=FALSE)
#                  parameters to lambert: ^^^^^^^^^^^^
#                  see mapproj::mapproject
state.map.shp <-
  maptools::map2SpatialLines(state.map, proj4string=lcc.crs)
# start debugging
# thanks, Felix Andrews!
class(state.map.shp)
summary(do.call("rbind",
  unlist(coordinates(state.map.shp), recursive=FALSE)))
#   end debugging

# > class(state.map.shp)
# [1]"SpatialLines"
# attr(,"package")
# [1]"sp"
# >     summary(do.call("rbind",
# +       unlist(coordinates(state.map.shp), recursive=FALSE)))
#        V1                  V2        
#  Min.   :-0.234093   Min.   :-0.9169  
#  1st Qu.:-0.000333   1st Qu.:-0.8289  
#  Median : 0.080378   Median :-0.7660  
#  Mean   : 0.058492   Mean   :-0.7711  
#  3rd Qu.: 0.162993   3rd Qu.:-0.7116  
#  Max.   : 0.221294   Max.   :-0.6343  
# I can't see how to relate these coordinates to `coordinates(o3.raster)`

pdf(file=lcc.pdf)
rasterVis::levelplot(o3.raster, margin=FALSE
) + latticeExtra::layer(
  sp::sp.lines(state.map.shp, lwd=0.8, col='darkgray'))

dev.off()
# change this for viewing PDF on your system
system(sprintf("xpdf %s", lcc.pdf))
# data looks correct, map invisible

## Try again, with lambert(40,33)
state.map <- maps::map(
  database="state", projection="lambert", par=c(40,33), plot=FALSE)
#                  parameters to lambert: ^^^^^^^^^^^^
#                  see mapproj::mapproject
state.map.shp <-
  maptools::map2SpatialLines(state.map, proj4string=lcc.crs)
# start debugging
summary(do.call("rbind",
  unlist(coordinates(state.map.shp), recursive=FALSE)))
#   end debugging

# >     summary(do.call("rbind",
# +       unlist(coordinates(state.map.shp), recursive=FALSE)))
#        V1                  V2        
#  Min.   :-0.2226344   Min.   :-0.9124  
#  1st Qu.:-0.0003149   1st Qu.:-0.8295  
#  Median : 0.0763322   Median :-0.7706  
#  Mean   : 0.0553948   Mean   :-0.7752  
#  3rd Qu.: 0.1546465   3rd Qu.:-0.7190  
#  Max.   : 0.2112617   Max.   :-0.6458  
# no real change from previous `coordinates(state.map.shp)`

pdf(file=lcc.pdf)
rasterVis::levelplot(o3.raster, margin=FALSE
) + latticeExtra::layer(
  sp::sp.lines(state.map.shp, lwd=0.8, col='darkgray'))

dev.off()
system(sprintf("xpdf %s", lcc.pdf))
# as expected, same as before: data looks correct, map invisible

#####   end example 1 #####

示例2:产生类似this

的输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
##### start example 2 #####

# Following adapted from what is installed in my
# .../R/x86_64-pc-linux-gnu-library/2.14/m3AqfigExampleScript.r
# (probably by my sysadmin), which also greatly resembles
# https://wiki.epa.gov/amad/index.php/R_packages_developed_in_AMAD
# which is behind a firewall :-(

## EXAMPLE WITH LAMBERT CONIC CONFORMAL FILE.

library("M3")
library("aqfig") # http://cran.r-project.org/web/packages/aqfig/

## Use an example file with LCC projection: either local download ...
lcc.file <-"./ozone_lcc.nc"
## ... or as installed by package=M3:
# lcc.file <- system.file("extdata/ozone_lcc.ncf", package="M3")
## Choose the one that works for you.
lcc.pdf <-"./ozone_lcc.example2.pdf" # for output

##  READ AND PLOT OZONE FROM FILE WITH LCC PROJECTION.

## Read in variable with daily ozone. Note that we can give dates
## rather than date-times, and that will include all time steps
## anytime during those days.  Or, we can give lower and upper bounds
## and all time steps between these will be taken.
o3 <- M3::get.M3.var(
  file=lcc.file, var="O3", hz.units="m",
  ldatetime=as.Date("2001-07-01"), udatetime=as.Date("2001-07-04"))
# start debugging
class(o3)
summary(o3)
summary(o3$x.cell.ctr)
#   end debugging

# > class(o3)
# [1]"list"
# > summary(o3)
#            Length Class   Mode    
# data       66304  -none-  numeric  
# data.units     1  -none-  character
# x.cell.ctr   148  -none-  numeric  
# y.cell.ctr   112  -none-  numeric  
# hz.units       1  -none-  character
# rows         112  -none-  numeric  
# cols         148  -none-  numeric  
# layers         1  -none-  numeric  
# datetime       4  POSIXct numeric  
# > summary(o3$x.cell.ctr)
#     Min.  1st Qu.   Median     Mean  3rd Qu.     Max.
# -2718000 -1395000   -72000   -72000  1251000  2574000

# Note how these grid coordinates relate to the IOAPI metadata above:
# min(o3$x.cell.ctr)      == -2718000
# == -2736000 + (36000/2) == x.orig + (x.cell.width/2)

## Get colors and map boundaries for plot.
library("fields")
col.rng <- tim.colors(20)
detach("package:fields")

## Get state boundaries in projection units.
map.lines <- M3::get.map.lines.M3.proj(
  file=lcc.file, database="state", units="m")
# start debugging
class(map.lines)
summary(map.lines)
summary(map.lines$coords)
#   end debugging

# >     class(map.lines)
# [1]"list"
# >     summary(map.lines)
#        Length Class  Mode    
# coords 23374  -none- numeric  
# units      1  -none- character
# >     summary(map.lines$coords)
#        x                  y          
#  Min.   :-2272238   Min.   :-1567156  
#  1st Qu.:   94244   1st Qu.: -673953  
#  Median :  913204   Median :  -26948  
#  Mean   :  689997   Mean   :  -84644  
#  3rd Qu.: 1744969   3rd Qu.:  524531  
#  Max.   : 2322260   Max.   : 1265778  
#  NA's   :168        NA's   :168      

## Set color boundaries to encompass the complete data range.
z.rng <- range(as.vector(o3$data))

## Make a color tile plot of the ozone for the first day (2001-07-01).
pdf(file=lcc.pdf)
image(o3$x.cell.ctr, o3$y.cell.ctr, o3$data[,,1,1],
      col=col.rng, zlim=z.rng,
      xlab="x-coord (m)", ylab="y-coord (m)")

## Put date-time string and chemical name (O3) into a format I can use
## to label the actual figure.
date.str <- format(o3$datetime[1],"%Y-%m-%d")
title(main=bquote(paste(O[3]," on", .(date.str), sep="")))

## Put the state boundaries on the plot.
lines(map.lines$coords)

## Add legend to right of plot. NOTE: YOU CANNOT ADD TO THE PLOT
## AFTER USING vertical.image.legend(). Before making a new plot,
## open a new device or turn this device off.
vertical.image.legend(zlim=z.rng, col=col.rng)

dev.off() # close the plot if you haven't already, ...
# ... and change the following to display PDFs on your system.
system(sprintf("xpdf %s", lcc.pdf))
# data displays with state map

#####   end example 2 #####

但是我看不到如何从M3::get.map.lines.M3.proj返回的简单矩阵(等,但不多)到sp::sp.lines想要的SpatialLines,更不用说latticeExtra::layer想要的东西了。 (我是一个新手,足以发现难以理解的网格文档。)此外,我更希望避免手动进行上述IOAPI转换(尽管我当然愿意这样做,而不是跳过旧式图形的框架) )。


执行此操作的一种方法(尽管很丑)是使用线性IOAPI变换"固定"从maps::map获得的state.map中的坐标。例如,

示例3:产生类似this

的输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
##### start example 3 #####

library("M3")        # http://cran.r-project.org/web/packages/M3/
library("rasterVis") # http://cran.r-project.org/web/packages/rasterVis/

## Use an example file with projection=Lambert conformal conic.
# lcc.file <- system.file("extdata/ozone_lcc.ncf", package="M3")
# See notes in question regarding unfortunate problem with raster::raster,
# and remember to download or rename the file (symlinking alone will not work).
lcc.file <-"./ozone_lcc.nc"

lcc.proj4 <- M3::get.proj.info.M3(lcc.file)
lcc.proj4   # debugging
# [1]"+proj=lcc +lat_1=33 +lat_2=45 +lat_0=40 +lon_0=-97 +a=6370000 +b=6370000"
# Note +lat_0=40 +lat_1=33 +lat_2=45 for maps::map@projection (below)
lcc.crs <- sp::CRS(lcc.proj4)
lcc.pdf <-"./ozone_lcc.example3.pdf" # for output

## Read in variable with daily ozone.
# o3.raster <- raster::raster(x=lcc.file, varname="O3", crs=lcc.crs)
# ozone_lcc.nc has 4 timesteps, choose 1 at random
o3.raster <- raster::raster(x=lcc.file, varname="O3", crs=lcc.crs, level=1)
o3.raster@crs <- lcc.crs # why does the above assignment not take?
# start debugging
o3.raster
summary(coordinates(o3.raster)) # thanks, Felix Andrews!
M3::get.grid.info.M3(lcc.file)
#   end debugging

# > o3.raster
# class       : RasterLayer
# band        : 1
# dimensions  : 112, 148, 16576  (nrow, ncol, ncell)
# resolution  : 1, 1  (x, y)
# extent      : 0.5, 148.5, 0.5, 112.5  (xmin, xmax, ymin, ymax)
# coord. ref. : +proj=lcc +lat_1=33 +lat_2=45 +lat_0=40 +lon_0=-97 +a=6370000 +b=6370000
# data source : .../ozone_lcc.nc
# names       : O3
# z-value     : 1
# zvar        : O3
# level       : 1

# > summary(coordinates(o3.raster))
#        x                y        
#  Min.   :  1.00   Min.   :  1.00  
#  1st Qu.: 37.75   1st Qu.: 28.75  
#  Median : 74.50   Median : 56.50  
#  Mean   : 74.50   Mean   : 56.50  
#  3rd Qu.:111.25   3rd Qu.: 84.25  
#  Max.   :148.00   Max.   :112.00  

# > M3::get.grid.info.M3(lcc.file)
# $x.orig
# [1] -2736000
# $y.orig
# [1] -2088000
# $x.cell.width
# [1] 36000
# $y.cell.width
# [1] 36000
# $hz.units
# [1]"m"
# $ncols
# [1] 148
# $nrows
# [1] 112
# $nlays
# [1] 1

# The grid (`coordinates(o3.raster)`) is integers, because this
# data is stored using the IOAPI convention. IOAPI makes the grid
# Cartesian by using an (approximately) equiareal projection (LCC)
# and abstracting grid positioning to metadata stored in netCDF
# global attributes. Some of this spatial metadata can be accessed
# by `M3::get.grid.info.M3` (above), and some can be accessed via
# the coordinate reference system (`CRS`, see `lcc.proj4` above)

## Get US state boundaries in projection units.
state.map <- maps::map(
  database="state", projection="lambert", par=c(33,45), plot=FALSE)
#                  parameters to lambert: ^^^^^^^^^^^^
#                  see mapproj::mapproject

# replace its coordinates with
metadata.coords.IOAPI.list <- M3::get.grid.info.M3(lcc.file)
metadata.coords.IOAPI.x.orig <- metadata.coords.IOAPI.list$x.orig
metadata.coords.IOAPI.y.orig <- metadata.coords.IOAPI.list$y.orig
metadata.coords.IOAPI.x.cell.width <- metadata.coords.IOAPI.list$x.cell.width
metadata.coords.IOAPI.y.cell.width <- metadata.coords.IOAPI.list$y.cell.width
map.lines <- M3::get.map.lines.M3.proj(
  file=lcc.file, database="state", units="m")
map.lines.coords.IOAPI.x <-
  (map.lines$coords[,1] - metadata.coords.IOAPI.x.orig)/metadata.coords.IOAPI.x.cell.width
map.lines.coords.IOAPI.y <-
  (map.lines$coords[,2] - metadata.coords.IOAPI.y.orig)/metadata.coords.IOAPI.y.cell.width
map.lines.coords.IOAPI <-
  cbind(map.lines.coords.IOAPI.x, map.lines.coords.IOAPI.y)
# start debugging
class(map.lines.coords.IOAPI)
summary(map.lines.coords.IOAPI)
#   end debugging

# >     class(map.lines.coords.IOAPI)
# [1]"matrix"
# >     summary(map.lines.coords.IOAPI)
#  map.lines.coords.IOAPI.x map.lines.coords.IOAPI.y
#  Min.   : 12.88           Min.   :14.47          
#  1st Qu.: 78.62           1st Qu.:39.28          
#  Median :101.37           Median :57.25          
#  Mean   : 95.17           Mean   :55.65          
#  3rd Qu.:124.47           3rd Qu.:72.57          
#  Max.   :140.51           Max.   :93.16          
#  NA's   :168              NA's   :168        

coordinates(state.map.shp) <- map.lines.coords.IOAPI # FAIL:
> Error in `coordinates<-`(`*tmp*`, value = c(99.0242231482288, 99.1277727928691,  :
>   setting coordinates cannot be done on Spatial objects, where they have already been set

state.map.IOAPI <- state.map # copy
state.map.IOAPI$x <- map.lines.coords.IOAPI.x
state.map.IOAPI$y <- map.lines.coords.IOAPI.y
state.map.IOAPI$range <- c(
  min(map.lines.coords.IOAPI.x),
  max(map.lines.coords.IOAPI.x),
  min(map.lines.coords.IOAPI.y),
  max(map.lines.coords.IOAPI.y))
state.map.IOAPI.shp <-
  maptools::map2SpatialLines(state.map.IOAPI, proj4string=lcc.crs)
# start debugging
# thanks, Felix Andrews!
class(state.map.IOAPI.shp)
summary(do.call("rbind",
  unlist(coordinates(state.map.IOAPI.shp), recursive=FALSE)))
#   end debugging

# > class(state.map.IOAPI.shp)
# [1]"SpatialLines"
# attr(,"package")
# [1]"sp"

# > summary(do.call("rbind",
# +   unlist(coordinates(state.map.IOAPI.shp), recursive=FALSE)))
#        V1               V2      
#  Min.   : 12.88   Min.   :14.47  
#  1st Qu.: 78.62   1st Qu.:39.28  
#  Median :101.37   Median :57.25  
#  Mean   : 95.17   Mean   :55.65  
#  3rd Qu.:124.47   3rd Qu.:72.57  
#  Max.   :140.51   Max.   :93.16  

pdf(file=lcc.pdf)
rasterVis::levelplot(o3.raster, margin=FALSE
) + latticeExtra::layer(
  sp::sp.lines(state.map.IOAPI.shp, lwd=0.8, col='darkgray'))
dev.off()
# change this for viewing PDF on your system
system(sprintf("xpdf %s", lcc.pdf))

#####   end example 3 #####