问题
Recently I tried to make a simple plot of the Pacific and Atlantic Oceans together, from 180W-20E. To cross the 0 degree line I used the roll method as follows:
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.feature as cf
import cartopy as cartopy
import numpy as np
import xarray as xr
from cartopy.mpl.ticker import LongitudeFormatter, LatitudeFormatter
from cartopy.util import add_cyclic_point
dset = xr.open_dataset('sst.mnmean.nc')
var = dset['sst'][:,:,:]
lat = dset['lat'][:]
lon = dset['lon'][:]
Using the roll method:
lonr = lon.roll(lon=300)
This will give me the following longitude array:
[ 120. 122. 124. 126. 128. 130. 132. 134. 136. 138. 140. 142.
144. 146. 148. 150. 152. 154. 156. 158. 160. 162. 164. 166.
168. 170. 172. 174. 176. 178. 180. 182. 184. 186. 188. 190.
192. 194. 196. 198. 200. 202. 204. 206. 208. 210. 212. 214.
216. 218. 220. 222. 224. 226. 228. 230. 232. 234. 236. 238.
240. 242. 244. 246. 248. 250. 252. 254. 256. 258. 260. 262.
264. 266. 268. 270. 272. 274. 276. 278. 280. 282. 284. 286.
288. 290. 292. 294. 296. 298. 300. 302. 304. 306. 308. 310.
312. 314. 316. 318. 320. 322. 324. 326. 328. 330. 332. 334.
336. 338. 340. 342. 344. 346. 348. 350. 352. 354. 356. 358.
0. 2. 4. 6. 8. 10. 12. 14. 16. 18. 20. 22.
24. 26. 28. 30. 32. 34. 36. 38. 40. 42. 44. 46.
48. 50. 52. 54. 56. 58. 60. 62. 64. 66. 68. 70.
72. 74. 76. 78. 80. 82. 84. 86. 88. 90. 92. 94.
96. 98. 100. 102. 104. 106. 108. 110. 112. 114. 116. 118.]
Then I proceed to extract the area I want with the 'sel' method:
lonr = lonr.sel(lon=slice(180,12))
lonr = lonr.roll(lon=104)
This last line was applied to avoid the weird horizontal lines over the plot as was discussed here. That is to say my sliced longitude array now looks like this:
[ 0. 2. 4. 6. 8. 10. 12. 180. 182. 184. 186. 188.
190. 192. 194. 196. 198. 200. 202. 204. 206. 208. 210. 212.
214. 216. 218. 220. 222. 224. 226. 228. 230. 232. 234. 236.
238. 240. 242. 244. 246. 248. 250. 252. 254. 256. 258. 260.
262. 264. 266. 268. 270. 272. 274. 276. 278. 280. 282. 284.
286. 288. 290. 292. 294. 296. 298. 300. 302. 304. 306. 308.
310. 312. 314. 316. 318. 320. 322. 324. 326. 328. 330. 332.
334. 336. 338. 340. 342. 344. 346. 348. 350. 352. 354. 356.
358.]
Now the problem is: this solves the area selection issue but it gives me the weird white line around the 0 longitude values when plotting, as the one you can see here. To solve it we would think of using the "add_cyclic_point" method, but this gives the following error:
var = var.sel(lat=slice(-30,30),
lon=lonr)
lon_idx = var.dims.index('lon')
var_c, lon_c = add_cyclic_point(var.values,
coord=lonr, axis=lon_idx)
(...)in add_cyclic_point
raise ValueError('The coordinate must be equally spaced.')
ValueError: The coordinate must be equally spaced.
As it says, I can't add the point to the array because it is not equally spaced. From the method's manual it looks clear why I can't use it, but of course I wouldn't like to live with that weird white line all over my plot (an example of what I did can be seen here).
Sorry for the rather lengthy question, but anyone knows how to solve this? Any help would be appreciated.
回答1:
Given we don't have access to your code to reproduce the problem, I'm going to invent some numbers to illustrate the problem with rolling in the way that you have:
In [1]: import numpy as np
In [2]: lons = np.arange(0, 360, 10)
In [3]: lons
Out[3]:
array([ 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120,
130, 140, 150, 160, 170, 180, 190, 200, 210, 220, 230, 240, 250,
260, 270, 280, 290, 300, 310, 320, 330, 340, 350])
Now, if we roll this as you have done:
In [4]: lons = np.roll(lons, -list(lons).index(100))
In [5]: lons
Out[5]:
array([100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200, 210, 220,
230, 240, 250, 260, 270, 280, 290, 300, 310, 320, 330, 340, 350,
0, 10, 20, 30, 40, 50, 60, 70, 80, 90])
The problem with these numbers is that they are fine if you consider them to be spherical (where 350 is just 10 degrees from 0), but if consider them to be Cartesian then there is a point where the distance between two of your datapoints is 350 degrees. When plotting your data, if you tell cartopy that your data is in the PlateCarree projected coordinate system, this is precisely what is happening.
In this case, there are many ways of fixing your numbers so that you don't have such a distance between some of your points. One way:
In [6]: np.diff(lons) < -180
Out[6]:
array([False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, True, False,
False, False, False, False, False, False, False, False], dtype=bool)
In [7]: np.cumsum(np.diff(lons) < -180)
Out[7]:
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
In [8]: new_lons = lons.copy()
In [8]: new_lons[1:] += np.cumsum(np.diff(lons) < -180) * 360
In [9]: new_lons
Out[9]:
array([100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200, 210, 220,
230, 240, 250, 260, 270, 280, 290, 300, 310, 320, 330, 340, 350,
360, 370, 380, 390, 400, 410, 420, 430, 440, 450])
You are now back in the territory of equidistant coordinates in projected space, and should have a lot more luck with things like add_cyclic_point.
来源:https://stackoverflow.com/questions/48265694/xarray-roll-and-cartopy-add-cyclic-point-functions-conflicting