尝试绘制简单纬度/经度点时遇到奇怪的问题

时间:2019-08-21 06:12:26

标签: matplotlib cartopy

我有以下数据框(相应的csv托管在这里:http://www.sharecsv.com/s/3795d862c1973efa311d8a770e978215/t.csv

            lat     lon
count   6159.000000     6159.000000
mean    37.764859   -122.355491
std     0.028214    0.038874
min     37.742200   -122.482783
25%     37.746317   -122.360133
50%     37.746417   -122.333717
75%     37.785825   -122.331300
max     37.818133   -122.331167

以下代码正确绘制:

    test_ax = plt.axes(projection=ccrs.Mercator())
    test_ax.plot(test_df['lon'], test_df['lat'], color="blue", linewidth=4, alpha=1.0,
            transform=ccrs.Geodetic())
    plt.show()

Plot1

但是,如果我采用一个子集,则不会:

    test_ax = plt.axes(projection=ccrs.Mercator())
    test_ax.plot(test_df['lon'][:1001], test_df['lat'][:1001], color="blue", linewidth=4, alpha=1.0,
            transform=ccrs.Geodetic())
    plt.show()

Plot2

但是对另一个子集也是如此。

    test_ax = plt.axes(projection=ccrs.Mercator())
    test_ax.plot(test_df['lon'][:3501], test_df['lat'][:3501], color="blue", linewidth=4, alpha=1.0,
            transform=ccrs.Geodetic())
    plt.show()

Plot3

我很确定自己做的有些愚蠢,但我只是无法弄清这种行为的原因。

编辑:

在进一步的实验中,我发现如果我手动将地图范围设置为包括0子午线,则先前未显示的子集:1001的图开始显示(附近的蓝点旧金山)。

    test_ax = plt.axes(projection=ccrs.Mercator())
    test_ax.plot(test_df['lon'][:1001], test_df['lat'][:1001], color="blue", linewidth=4, alpha=1.0, transform=ccrs.Geodetic())
    test_ax.coastlines()
    test_ax.set_extent([-130, 0, 30, 40], crs=ccrs.Geodetic())
    test_ax.gridlines(draw_labels=True)
    plt.show()

Plot4

编辑:带有可复制的示例

(用于jupyter笔记本)

import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import pandas as pd

df_csv_url = 'http://www.sharecsv.com/dl/76dd767525a37180ca54cd1d9314b9dc/t1.csv'
test_df = pd.read_csv(df_csv_url)
figure_params = { 'width': 9.6, 'height': 5.4 }

fig = plt.figure(
        figsize=(figure_params["width"], figure_params["height"])        
    )
test_ax = fig.add_axes((0, 0.5, 0.5, 0.5), projection=ccrs.Mercator(), label="map1")
test_ax.plot(test_df['lon'], test_df['lat'], color="blue", linewidth=4, alpha=1.0, transform=ccrs.Geodetic())
test_ax.coastlines()
test_ax.gridlines(draw_labels=True)
test_ax.set_title("Path doesn\'t show", y=1.5)

# Including 0 meridian in extent shows the path
test_ax1 = fig.add_axes((0, 0, 0.5, 0.5), projection=ccrs.Mercator(), label="map2")
test_ax1.plot(test_df['lon'], test_df['lat'], color="blue", linewidth=4, alpha=1.0, transform=ccrs.Geodetic())
test_ax1.set_extent([-130, 0, 30, 40], crs=ccrs.Geodetic())
test_ax1.coastlines()
test_ax1.gridlines(draw_labels=True)
test_ax1.set_title("Path shows (blue dot near San Francisco)", y=1.1)

plt.show()

Plot5

修改

(带有简化的可复制示例)

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import cartopy.crs as ccrs

test_df = pd.DataFrame()
test_df['lon'] = np.linspace(-120, -60, num=1000)
test_df['lat'] = 38

test_df1 = pd.DataFrame()
test_df1['lon'] = np.linspace(-120, -60, num=1001)
test_df1['lat'] = 38


fig = plt.figure()

meridian=0

test_ax = fig.add_axes((0, 0, 1, 0.6), projection=ccrs.Mercator())
test_ax.plot(test_df['lon'], test_df['lat'], color="blue", linewidth=4, alpha=1.0, transform=ccrs.Geodetic())
test_ax.coastlines()
test_ax.set_extent((-125, meridian, 36, 38))
gl = test_ax.gridlines(draw_labels=True)
gl.xlabels_top = False
gl.ylabels_left = False
test_ax.set_title('Path with {} points, eastern edge={}'.format(len(test_df),meridian))


test_ax1 = fig.add_axes((0, 0.7, 1, 0.6), projection=ccrs.Mercator())
test_ax1.plot(test_df1['lon'], test_df1['lat'], color="red", linewidth=4, alpha=1.0, transform=ccrs.Geodetic())
test_ax1.coastlines()
test_ax1.set_extent((-125, meridian, 36, 38))
gl1 = test_ax1.gridlines(draw_labels=True)
gl1.xlabels_top = False
gl1.ylabels_left = False
test_ax1.set_title('Path with {} points, eastern edge={}'.format(len(test_df1),meridian))


meridian=-10

test_ax2 = fig.add_axes((0, 1.4, 1, 0.6), projection=ccrs.Mercator())
test_ax2.plot(test_df['lon'], test_df['lat'], color="black", linewidth=4, alpha=1.0, transform=ccrs.Geodetic())
test_ax2.coastlines()
test_ax2.set_extent((-125, -10, 36, 38))
gl2 = test_ax2.gridlines(draw_labels=True)
gl2.xlabels_top = False
gl2.ylabels_left = False
test_ax2.set_title('Path with {} points, eastern edge={}'.format(len(test_df),meridian))


test_ax3 = fig.add_axes((0, 2.1, 1, 0.6), projection=ccrs.Mercator())
test_ax3.plot(test_df1['lon'], test_df1['lat'], color="green", linewidth=4, alpha=1.0, transform=ccrs.Geodetic())
test_ax3.coastlines()
test_ax3.set_extent((-125, -10, 36, 38))
gl3 = test_ax3.gridlines(draw_labels=True)
gl3.xlabels_top = False
gl3.ylabels_left = False
test_ax3.set_title('Path with {} points, eastern edge={}'.format(len(test_df1),meridian))

plt.show()

enter image description here

2 个答案:

答案 0 :(得分:2)

鉴于Cartopy在运行中似乎存在一些问题,我认为最好的解决方法是将您的数据分成少于1000个的大块,然后绘制出所有部分。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import cartopy.crs as ccrs

test_df = pd.DataFrame()
test_df['lon'] = np.linspace(-120, -60, num=1001)
test_df['lat'] = 38

fig = plt.figure()

meridian=0

test_ax = fig.add_axes((0, 0.05, 1, 0.3), projection=ccrs.Mercator())
test_ax.plot(test_df['lon'], test_df['lat'], color="red", 
                       linewidth=4, alpha=1.0, transform=ccrs.Geodetic())
test_ax.coastlines()
test_ax.set_extent((-125, meridian, 36, 38))
gl = test_ax.gridlines(draw_labels=True)
gl.xlabels_top = False
gl.ylabels_left = False
test_ax.set_title('Path with {} points, eastern edge={}'.format(len(test_df),meridian))

meridian=-10

test_ax3 = fig.add_axes((0, 0.55, 1, 0.3), projection=ccrs.Mercator())
# plot first 500
test_ax3.plot(test_df['lon'][:500], test_df['lat'][:500], color="green", 
                        linewidth=4, alpha=1.0, transform=ccrs.Geodetic())
# plot to cover the gap
test_ax3.plot(test_df['lon'][499:501], test_df['lat'][499:501], color="blue", 
                        linewidth=4, alpha=1.0, transform=ccrs.Geodetic())
# plot last 501
test_ax3.plot(test_df['lon'][500:], test_df['lat'][500:], color="yellow", 
                        linewidth=4, alpha=1.0, transform=ccrs.Geodetic())
test_ax3.coastlines()
test_ax3.set_extent((-125, -10, 36, 38))
gl3 = test_ax3.gridlines(draw_labels=True)
gl3.xlabels_top = False
gl3.ylabels_left = False
test_ax3.set_title('Path with {} points, eastern edge={}'.format(len(test_df),meridian))

plt.show()

对于1001点,我将其分为500点和501点。

plot line in sections

由于您正在绘制直线,因此我还添加了该图以覆盖间隙,放大时以蓝色显示。

zoom on gap

如果您还要绘制点,则需要设置间隙填充符而不是使两个部分重叠,原因如下:

test_ax3.plot(test_df['lon'][:500], test_df['lat'][:500], color="green",
                        linewidth=1, alpha=1.0, transform=ccrs.Geodetic(), marker='.')
# plot to cover the gap
test_ax3.plot(test_df['lon'][499:501], test_df['lat'][499:501], color="blue",
                        linewidth=1, alpha=1.0, transform=ccrs.Geodetic(), marker=None)
# plot last 501
test_ax3.plot(test_df['lon'][500:], test_df['lat'][500:], color="yellow",
                        linewidth=1, alpha=1.0, transform=ccrs.Geodetic(), marker='.')

通过分离填充符,可以确保不重复点,如果您的alpha值小于1.0,则可能会出现问题。

enter image description here

将其应用于原始数据,您可以创建一个函数,以等于所需大小的块形式遍历数据框。

import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import pandas as pd

PLOT_LIMIT = 1000

df_csv_url = 'http://www.sharecsv.com/dl/76dd767525a37180ca54cd1d9314b9dc/t1.csv'
test_df = pd.read_csv(df_csv_url)
figure_params = { 'width': 9.6, 'height': 5.4 }

fig = plt.figure(
        figsize=(figure_params["width"], figure_params["height"])
    )

print(len(test_df['lon']))

def ax_plot(test_ax, test_df):
    # this function will loop over the dataframe in chunks equal to PLOT_LIMIT
    len_df = len(test_df)
    n=0
    for i in range(len_df//PLOT_LIMIT):
        test_ax.plot(test_df['lon'][1000*i:1000*(i+1)], test_df['lat'][1000*i:1000*(i+1)], color="blue",
                        linewidth=4, alpha=1.0, transform=ccrs.Geodetic())
        if (len_df-((n+1)*PLOT_LIMIT)) != 0:
            test_ax.plot(test_df['lon'][(1000*i)-1:(1000*(i+1))+1], test_df['lat'][(1000*i)-1:(1000*(i+1))+1], color="blue",
                            linewidth=4, alpha=1.0, transform=ccrs.Geodetic(), marker='None')
        n+=1

    test_ax.plot(test_df['lon'][1000*n:], test_df['lat'][1000*n:], color="blue",
                    linewidth=4, alpha=1.0, transform=ccrs.Geodetic())


test_ax1 = fig.add_axes((0, 0.55, 1, 0.45), projection=ccrs.Mercator(), label="map1")
ax_plot(test_ax1, test_df)
test_ax1.coastlines()
test_ax1.gridlines(draw_labels=True)
test_ax1.set_title("Path shows", y=1.5)

# Including 0 meridian in extent shows the path
test_ax2 = fig.add_axes((0, 0.1, 1, 0.45), projection=ccrs.Mercator(), label="map2")
ax_plot(test_ax2, test_df)
test_ax2.set_extent([-130, -30, 30, 40], crs=ccrs.Geodetic())
test_ax2.coastlines()
test_ax2.gridlines(draw_labels=True)
test_ax2.set_title("Path shows (blue dot near San Francisco)", y=1.1)

plt.show()

enter image description here

如您所见,您现在应该可以灵活地在地图上设置查看窗口。我没有检查过跨子午线之类的边缘情况,但是在给出的情况下它是可行的。

答案 1 :(得分:2)

我能够找到另一个解决方法。如果在使用plot函数之前对点进行了转换(而不是传递transform参数),它将起作用。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import cartopy.crs as ccrs

test_df = pd.DataFrame()
test_df['lon'] = np.linspace(-120, -60, num=1000)
test_df['lat'] = 38

test_df1 = pd.DataFrame()
test_df1['lon'] = np.linspace(-120, -60, num=1001)
test_df1['lat'] = 38


fig = plt.figure()

meridian=0

test_ax = fig.add_axes((0, 0, 1, 0.6), projection=ccrs.Mercator())
test_ax.plot(test_df['lon'], test_df['lat'], color="blue", linewidth=4, alpha=1.0, transform=ccrs.Geodetic())
test_ax.coastlines()
test_ax.set_extent((-125, meridian, 36, 38))
gl = test_ax.gridlines(draw_labels=True)
gl.xlabels_top = False
gl.ylabels_left = False
test_ax.set_title('Path with {} points, eastern edge={}'.format(len(test_df),meridian))


test_ax1 = fig.add_axes((0, 0.7, 1, 0.6), projection=ccrs.Mercator())
test_ax1.plot(test_df1['lon'], test_df1['lat'], color="red", linewidth=4, alpha=1.0, transform=ccrs.Geodetic())
test_ax1.coastlines()
test_ax1.set_extent((-125, meridian, 36, 38))
gl1 = test_ax1.gridlines(draw_labels=True)
gl1.xlabels_top = False
gl1.ylabels_left = False
test_ax1.set_title('Path with {} points, eastern edge={}'.format(len(test_df1),meridian))


meridian=-10

test_ax2 = fig.add_axes((0, 1.4, 1, 0.6), projection=ccrs.Mercator())
test_ax2.plot(test_df['lon'], test_df['lat'], color="black", linewidth=4, alpha=1.0, transform=ccrs.Geodetic())
test_ax2.coastlines()
test_ax2.set_extent((-125, -10, 36, 38))
gl2 = test_ax2.gridlines(draw_labels=True)
gl2.xlabels_top = False
gl2.ylabels_left = False
test_ax2.set_title('Path with {} points, eastern edge={}'.format(len(test_df),meridian))


test_ax3 = fig.add_axes((0, 2.1, 1, 0.6), projection=ccrs.Mercator())
test_ax3.plot(test_df1['lon'], test_df1['lat'], color="green", linewidth=4, alpha=1.0, transform=ccrs.Geodetic())
test_ax3.coastlines()
test_ax3.set_extent((-125, -10, 36, 38))
gl3 = test_ax3.gridlines(draw_labels=True)
gl3.xlabels_top = False
gl3.ylabels_left = False
test_ax3.set_title('Path with {} points, eastern edge={}'.format(len(test_df1),meridian))


test_ax4 = fig.add_axes((0, 2.8, 1, 0.6), projection=ccrs.Mercator())
# Instead of transforming within the plot function, transform and then plot
transformed_points = ccrs.Mercator().transform_points(ccrs.Geodetic(), test_df1['lon'].values, test_df1['lat'].values)
test_ax4.plot([p[0] for p in transformed_points], [p[1] for p in transformed_points], color="green", linewidth=4, alpha=1.0)
test_ax4.coastlines()
test_ax4.set_extent((-125, -10, 36, 38))
gl3 = test_ax4.gridlines(draw_labels=True)
gl3.xlabels_top = False
gl3.ylabels_left = False
test_ax4.set_title('Path with {} prior transformed points, eastern edge={}'.format(len(test_df1),meridian))


plt.show()

enter image description here