How to remove / lower small contour lines using matplotlib

I am trying to build contourpressure level lines. I am using a netCDF file that contains data with a higher resolution (from 3 km to 27 km). Due to the higher resolution dataset, I get a lot of pressure values ​​that do not have to be built (rather, I don't mind skipping a certain line of contours with insignificant values). I wrote several script graphs using the examples in this link http://matplotlib.org/basemap/users/examples.html .

After creating the image, the image looks like this:

Contour plot

, . , contour, , . : -

Internet image

, ,

  • , / , .

  1. ( ) / .

  1. ( ), 50 - 100 .

SO- Python: matplotlib.pyplot.contour()

- , .

.

Edit: -

@ print 'diameter is ', diameter del(level.get_paths()[kp]), , . filterd, if diameter < 15000::

diameter is  9099.66295612
diameter is  13264.7838257
diameter is  445.574234531
diameter is  1618.74618114
diameter is  1512.58974168

. , . , ( ).

plt.contour(x[::2,::2],y[::2,::2],mslp[::2,::2]) . , .

:

#!/usr/bin/env python
from netCDF4 import Dataset
import matplotlib
matplotlib.use('agg')
import matplotlib.pyplot as plt
import numpy as np
import scipy.ndimage
from mpl_toolkits.basemap import interp
from mpl_toolkits.basemap import Basemap


# Set default map
west_lon = 68
east_lon = 93
south_lat = 7
north_lat = 23

nc = Dataset('ncfile.nc')
# Get this variable for later calucation
temps = nc.variables['T2']
time = 0  # We will take only first interval for this example
# Draw basemap
m = Basemap(projection='merc', llcrnrlat=south_lat, urcrnrlat=north_lat,
                llcrnrlon=west_lon, urcrnrlon=east_lon, resolution='l')
m.drawcoastlines()
m.drawcountries(linewidth=1.0)
# This sets the standard grid point structure at full resolution
x, y = m(nc.variables['XLONG'][0], nc.variables['XLAT'][0])

# Set figure margins
width = 10
height = 8

plt.figure(figsize=(width, height))
plt.rc("figure.subplot", left=.001)
plt.rc("figure.subplot", right=.999)
plt.rc("figure.subplot", bottom=.001)
plt.rc("figure.subplot", top=.999)

plt.figure(figsize=(width, height), frameon=False)

# Convert Surface Pressure to Mean Sea Level Pressure
stemps = temps[time] + 6.5 * nc.variables['HGT'][time] / 1000.
mslp = nc.variables['PSFC'][time] * np.exp(9.81 / (287.0 * stemps) * nc.variables['HGT'][time]) * 0.01 + (
    6.7 * nc.variables['HGT'][time] / 1000)

# Contour only at 2 hpa interval
level = []
for i in range(mslp.min(), mslp.max(), 1):
    if i % 2 == 0:
        if i >= 1006 and i <= 1018:
            level.append(i)

# Save mslp values to upload to SO thread
# np.savetxt('mslp.txt', mslp, fmt='%.14f', delimiter=',')

P = plt.contour(x, y, mslp, V=2, colors='b', linewidths=2, levels=level)


# Solution suggested by Andras Deak
for level in P.collections:
    for kp,path in enumerate(level.get_paths()):
        # include test for "smallness" of your choice here:
        # I'm using a simple estimation for the diameter based on the
        #    x and y diameter...
        verts = path.vertices # (N,2)-shape array of contour line coordinates
        diameter = np.max(verts.max(axis=0) - verts.min(axis=0))

        if diameter < 15000: # threshold to be refined for your actual dimensions!
            #print 'diameter is ', diameter
            del(level.get_paths()[kp])  # no remove() for Path objects:(
            #level.remove() # This does not work. produces ValueError: list.remove(x): x not in list

plt.gcf().canvas.draw()


plt.savefig('dummy', bbox_inches='tight')
plt.close()

Peak working example

, . mslp, http://www.mediafire.com/download/7vi0mxqoe0y6pm9/mslp.txt

x y , , .

, . , ( ), , . , , . : -

slice = 15

CS = plt.contour(x[::slice,::slice],y[::slice,::slice],mslp[::slice,::slice], colors='b', linewidths=1, levels=levels)

.

irregular line

SO, : -

netcdf

, , . , , . , .

+4
2

, , : , - . , , contour(), .

, , . collections , contour(), Path , . figure :

# dummy example based on matplotlib.pyplot.clabel example:
import matplotlib
import numpy as np
import matplotlib.cm as cm
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt

delta = 0.025
x = np.arange(-3.0, 3.0, delta)
y = np.arange(-2.0, 2.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
# difference of Gaussians
Z = 10.0 * (Z2 - Z1)


plt.figure()
CS = plt.contour(X, Y, Z)

for level in CS.collections:
    for kp,path in reversed(list(enumerate(level.get_paths()))):
        # go in reversed order due to deletions!

        # include test for "smallness" of your choice here:
        # I'm using a simple estimation for the diameter based on the
        #    x and y diameter...
        verts = path.vertices # (N,2)-shape array of contour line coordinates
        diameter = np.max(verts.max(axis=0) - verts.min(axis=0))

        if diameter<1: # threshold to be refined for your actual dimensions!
            del(level.get_paths()[kp])  # no remove() for Path objects:(

# this might be necessary on interactive sessions: redraw figure
plt.gcf().canvas.draw()

() () 1 ( 0 ):

original for reference remove smaller than d = 1

, , , collections, . , CS.collections[k].remove(), , , ( , ).

, , , 2:

result with threshold 2

.


, . , level , np, . 2 ( arange, , p1 p2). , levels contour, , V=2 .

import numpy as np
import matplotlib.pyplot as plt

# insert actual data here...
Z = np.loadtxt('mslp.txt',delimiter=',')
X,Y = np.meshgrid(np.linspace(0,300000,Z.shape[1]),np.linspace(0,200000,Z.shape[0]))
p1,p2 = 1006,1018

# this is almost the same as the original, although it will produce
# [p1, p1+2, ...] instead of `[Z.min()+n, Z.min()+n+2, ...]`
levels = np.arange(np.maximum(Z.min(),p1),np.minimum(Z.max(),p2),2)


#control
plt.figure()
CS = plt.contour(X, Y, Z, colors='b', linewidths=2, levels=levels)


#modified
plt.figure()
CS = plt.contour(X, Y, Z, colors='b', linewidths=2, levels=levels)

for level in CS.collections:
    for kp,path in reversed(list(enumerate(level.get_paths()))):
        # go in reversed order due to deletions!

        # include test for "smallness" of your choice here:
        # I'm using a simple estimation for the diameter based on the
        #    x and y diameter...
        verts = path.vertices # (N,2)-shape array of contour line coordinates
        diameter = np.max(verts.max(axis=0) - verts.min(axis=0))

        if diameter<15000: # threshold to be refined for your actual dimensions!
            del(level.get_paths()[kp])  # no remove() for Path objects:(

# this might be necessary on interactive sessions: redraw figure
plt.gcf().canvas.draw()
plt.show()

, () ():

before after


. , , - , griddata (). , . , :

import scipy.interpolate as interp   #the new one

# assume you have X,Y,Z,levels defined as before

# start resampling stuff
dN = 10 # use every dN'th element of the gridded input data
my_slice = [slice(None,None,dN),slice(None,None,dN)]

# downsampled data
X2,Y2,Z2 = X[my_slice],Y[my_slice],Z[my_slice]
# same as X2 = X[::dN,::dN] etc.

# upsampling with griddata over original mesh
Zsmooth = interp.griddata(np.array([X2.ravel(),Y2.ravel()]).T,Z2.ravel(),(X,Y),method='cubic')

# plot
plt.figure()
CS = plt.contour(X, Y, Zsmooth, colors='b', linewidths=2, levels=levels)

, , , . : 'linear' , .

() upsampling ():

after downsample after upsample

, , (, , ). , , - , , / . , , griddata, .

+12

, , . get_contour_verts , , , matplotlib._cntr, . , , .. pop , . , , ; , .

LineCollection. , Figure Axes, Axes.add_collection, LineCollection .

, , , . , . , !


: MWE . plt._cntr.Cntr, plt.contour, . ; . checkDiameter . , Polygon , . , , , , .

import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt

def checkDiameter(seg, tol=.3):
    # Function for screening line segments. NB: Not actually a proper diameter.
    diam = (seg[:,0].max() - seg[:,0].min(),
            seg[:,1].max() - seg[:,1].min())
    return not (diam[0] < tol or diam[1] < tol)

# Create testing data
x = np.linspace(-1,1, 21)
xx, yy = np.meshgrid(x,x)
z = np.exp(-(xx**2 + .5*yy**2))

# Original plot with plt.contour
fig0, ax0 = plt.subplots()
# Make sure this contour object actually has a tiny contour to remove
cntrObj = ax0.contour(xx,yy,z, levels=[.2,.4,.6,.8,.9,.95,.99,.999])

# Primary loop: Copy contours into a new LineCollection
lineNew = list()
for lineOriginal in cntrObj.collections:
    # Get properties of the original LineCollection
    segments = lineOriginal.get_segments()
    propDict = lineOriginal.properties()
    propDict = {key: value for (key,value) in propDict.items()
        if key in ['linewidth','color','linestyle']}  # Whatever parameters you want to carry over
    # Filter out the lines with small diameters
    segments = [seg for seg in segments if checkDiameter(seg)]
    # Create new LineCollection out of the OK segments
    if len(segments) > 0:
        lineNew.append(mpl.collections.LineCollection(segments, **propDict))

# Make new plot with only these line collections; display results
fig1, ax1 = plt.subplots()
ax1.set_xlim(ax0.get_xlim())
ax1.set_ylim(ax0.get_ylim())
for line in lineNew:
    ax1.add_collection(line)
plt.show()

FYI: propDict . , . -, , . , -, , : , facecolors .. {key for key in propDict if I want key} - , , - .

0

All Articles