def makegrid(self, nx, ny): """ return arrays of shape (ny,nx) containing lon,lat coordinates of an equally spaced native projection grid. """ dx = (self.urcrnrx - self.llcrnrx) / (nx - 1) dy = (self.urcrnry - self.llcrnry) / (ny - 1) x = self.llcrnrx + dx * N.indices((ny, nx))[1, :, :] y = self.llcrnry + dy * N.indices((ny, nx))[0, :, :] lons, lats = self(x, y, inverse=True) # mercator coordinate is plain longitude in x. if self.projparams['proj'] == 'merc': dx = (self.urcrnrlon - self.llcrnrlon) / (nx - 1) lons = self.llcrnrlon + dx * N.indices((ny, nx))[1, :, :] return lons, lats
def makegrid(self,nx,ny): """ return arrays of shape (ny,nx) containing lon,lat coordinates of an equally spaced native projection grid. """ dx = (self.urcrnrx-self.llcrnrx)/(nx-1) dy = (self.urcrnry-self.llcrnry)/(ny-1) x = self.llcrnrx+dx*N.indices((ny,nx))[1,:,:] y = self.llcrnry+dy*N.indices((ny,nx))[0,:,:] lons, lats = self(x, y, inverse=True) # mercator coordinate is plain longitude in x. if self.projparams['proj'] == 'merc': dx = (self.urcrnrlon-self.llcrnrlon)/(nx-1) lons = self.llcrnrlon+dx*N.indices((ny,nx))[1,:,:] return lons, lats
def drawmeridians(self,ax,meridians,color='k',linewidth=1., \ linestyle='--',dashes=[1,1]): """ draw meridians (longitude lines). ax - current axis instance. meridians - list containing longitude values to draw (in degrees). color - color to draw meridians (default black). linewidth - line width for meridians (default 1.) linestyle - line style for meridians (default '--', i.e. dashed). dashes - dash pattern for meridians (default [1,1], i.e. 1 pixel on, 1 pixel off). """ if self.projection not in ['merc','cyl']: lats = N.arange(-80,81).astype('f') else: lats = N.arange(-90,91).astype('f') xdelta = 0.1*(self.xmax-self.xmin) ydelta = 0.1*(self.ymax-self.ymin) for merid in meridians: lons = merid*N.ones(len(lats),'f') x,y = self(lons,lats) # remove points outside domain. testx = N.logical_and(x>=self.xmin-xdelta,x<=self.xmax+xdelta) x = N.compress(testx, x) y = N.compress(testx, y) testy = N.logical_and(y>=self.ymin-ydelta,y<=self.ymax+ydelta) x = N.compress(testy, x) y = N.compress(testy, y) if len(x) > 1 and len(y) > 1: # split into separate line segments if necessary. # (not necessary for mercator or cylindrical). xd = (x[1:]-x[0:-1])**2 yd = (y[1:]-y[0:-1])**2 dist = N.sqrt(xd+yd) split = dist > 500000. if N.sum(split) and self.projection not in ['merc','cyl']: ind = (N.compress(split,MLab.squeeze(split*N.indices(xd.shape)))+1).tolist() xl = [] yl = [] iprev = 0 ind.append(len(xd)) for i in ind: xl.append(x[iprev:i]) yl.append(y[iprev:i]) iprev = i else: xl = [x] yl = [y] # draw each line segment. for x,y in zip(xl,yl): # skip if only a point. if len(x) > 1 and len(y) > 1: l = Line2D(x,y,linewidth=linewidth,linestyle=linestyle) l.set_color(color) l.set_dashes(dashes) ax.add_line(l)
def generate_binary_structure(rank, connectivity): """Generate a binary structure for binary morphological operations. The inputs are the rank of the array to which the structure will be applied and the square of the connectivity of the structure. """ if connectivity < 1: connectivity = 1 if rank < 1: if connectivity < 1: return numarray.array(0, type = numarray.Bool) else: return numarray.array(1, type = numarray.Bool) output = numarray.zeros([3] * rank, numarray.Bool) output = numarray.abs(numarray.indices([3] * rank) - 1) output = numarray.add.reduce(output, 0) return numarray.asarray(output <= connectivity, type = numarray.Bool)
def generate_binary_structure(rank, connectivity): """Generate a binary structure for binary morphological operations. The inputs are the rank of the array to which the structure will be applied and the square of the connectivity of the structure. """ if connectivity < 1: connectivity = 1 if rank < 1: if connectivity < 1: return numarray.array(0, type=numarray.Bool) else: return numarray.array(1, type=numarray.Bool) output = numarray.zeros([3] * rank, numarray.Bool) output = numarray.abs(numarray.indices([3] * rank) - 1) output = numarray.add.reduce(output, 0) return numarray.asarray(output <= connectivity, type=numarray.Bool)
urcornerx = dx*(nx-1); urcornery = dy*(ny-1) llcornerlon, llcornerlat = awips221(llcornerx, llcornery, inverse=True) lrcornerlon, lrcornerlat = awips221(lrcornerx, lrcornery, inverse=True) urcornerlon, urcornerlat = awips221(urcornerx, urcornery, inverse=True) ulcornerlon, ulcornerlat = awips221(ulcornerx, ulcornery, inverse=True) print '4 corners of AWIPS grid 221:' print llcornerlon, llcornerlat print lrcornerlon, lrcornerlat print urcornerlon, urcornerlat print ulcornerlon, ulcornerlat print 'from GRIB docs' print '(see http://www.nco.ncep.noaa.gov/pmb/docs/on388/tableb.html)' print ' -145.5 1.0' print ' -68.318 0.897' print ' -2.566 46.352' print ' 148.639 46.635' # compute lons and lats for the whole AWIPS grid 221 (377x249). x = numarray.zeros((nx,ny),'d') y = numarray.zeros((nx,ny),'d') x = dx*numarray.indices(x.shape)[0,:,:] y = dy*numarray.indices(y.shape)[1,:,:] import time; t1 = time.clock() lons, lats = awips221(x, y, inverse=True) t2 = time.clock() print 'compute lats/lons for all points on AWIPS 221 grid (%sx%s)' %(nx,ny) print 'max/min lons' print min(numarray.ravel(lons)),max(numarray.ravel(lons)) print 'max/min lats' print min(numarray.ravel(lats)),max(numarray.ravel(lats)) print 'took',t2-t1,'secs'
params['R'] = 63712000 params['lat_1'] = 50 params['lat_2'] = 50 params['lon_0'] = -107 proj = Proj(params) llcornerx, llcornery = proj(-145.5,1.) xmax=11297266.68; ymax=8959901.16 params['x_0'] = -llcornerx # add cartesian offset so lower left corner = (0,0) params['y_0'] = -llcornery # create a Proj instance for desired map. proj = Proj(params) # define grid (nx x ny regularly spaced native projection grid) nx = 349; ny = 277 dx = xmax/(nx-1); dy = ymax/(ny-1) xgrid = dx*numarray.indices((ny,nx))[1,:,:] ygrid = dy*numarray.indices((ny,nx))[0,:,:] # compute lons, lats of regular projection grid. lonout, latout = proj(xgrid, ygrid, inverse=True) # make sure lons are between 0 and 360 lonout = numarray.where(lonout < 0, lonout+360, lonout) # make lat into colat (monotonically increasing from 0 at S Pole # to 180 at N Pole). latout = latout+90 # read in topo data from pickle (on a regular lat/lon grid) topodict = cPickle.load(open('etopo20.pickle','rb')) lons = topodict['lons'] lats = topodict['lats'] topoin = topodict['topo']
def drawmeridians(self,ax,meridians,color='k',linewidth=1., \ linestyle='--',dashes=[1,1],labels=[0,0,0,0],\ font='rm',fontsize=12): """ draw meridians (longitude lines). ax - current axis instance. meridians - list containing longitude values to draw (in degrees). color - color to draw meridians (default black). linewidth - line width for meridians (default 1.) linestyle - line style for meridians (default '--', i.e. dashed). dashes - dash pattern for meridians (default [1,1], i.e. 1 pixel on, 1 pixel off). labels - list of 4 values (default [0,0,0,0]) that control whether meridians are labelled where they intersect the left, right, top or bottom of the plot. For example labels=[1,0,0,1] will cause meridians to be labelled where they intersect the left and bottom of the plot, but not the right and top. Labels are located with a precision of 0.1 degrees and are drawn using mathtext. font - mathtext font used for labels ('rm','tt','it' or 'cal', default 'rm'. fontsize - font size in points for labels (default 12). """ # don't draw meridians past latmax, always draw parallel at latmax. latmax = 80. # not used for cyl, merc projections. # offset for labels. yoffset = (self.urcrnry-self.llcrnry)/100./self.aspect xoffset = (self.urcrnrx-self.llcrnrx)/100. if self.projection not in ['merc','cyl']: lats = N.arange(-latmax,latmax+1).astype('f') else: lats = N.arange(-90,91).astype('f') xdelta = 0.1*(self.xmax-self.xmin) ydelta = 0.1*(self.ymax-self.ymin) for merid in meridians: lons = merid*N.ones(len(lats),'f') x,y = self(lons,lats) # remove points outside domain. testx = N.logical_and(x>=self.xmin-xdelta,x<=self.xmax+xdelta) x = N.compress(testx, x) y = N.compress(testx, y) testy = N.logical_and(y>=self.ymin-ydelta,y<=self.ymax+ydelta) x = N.compress(testy, x) y = N.compress(testy, y) if len(x) > 1 and len(y) > 1: # split into separate line segments if necessary. # (not necessary for mercator or cylindrical). xd = (x[1:]-x[0:-1])**2 yd = (y[1:]-y[0:-1])**2 dist = N.sqrt(xd+yd) split = dist > 500000. if N.sum(split) and self.projection not in ['merc','cyl']: ind = (N.compress(split,pylab.squeeze(split*N.indices(xd.shape)))+1).tolist() xl = [] yl = [] iprev = 0 ind.append(len(xd)) for i in ind: xl.append(x[iprev:i]) yl.append(y[iprev:i]) iprev = i else: xl = [x] yl = [y] # draw each line segment. for x,y in zip(xl,yl): # skip if only a point. if len(x) > 1 and len(y) > 1: l = Line2D(x,y,linewidth=linewidth,linestyle=linestyle) l.set_color(color) l.set_dashes(dashes) ax.add_line(l) # draw labels for meridians. # search along edges of map to see if parallels intersect. # if so, find x,y location of intersection and draw a label there. if self.projection == 'cyl': dx = 0.01; dy = 0.01 elif self.projection == 'merc': dx = 0.01; dy = 1000 else: dx = 1000; dy = 1000 for dolab,side in zip(labels,['l','r','t','b']): if not dolab: continue # for cyl or merc, don't draw meridians on left or right. if self.projection in ['cyl','merc'] and side in ['l','r']: continue if side in ['l','r']: nmax = int((self.ymax-self.ymin)/dy+1) if self.urcrnry < self.llcrnry: yy = self.llcrnry-dy*N.arange(nmax) else: yy = self.llcrnry+dy*N.arange(nmax) if side == 'l': lons,lats = self(self.llcrnrx*N.ones(yy.shape,'f'),yy,inverse=True) else: lons,lats = self(self.urcrnrx*N.ones(yy.shape,'f'),yy,inverse=True) lons = N.where(lons < 0, lons+360, lons) lons = [int(lon*10) for lon in lons.tolist()] lats = [int(lat*10) for lat in lats.tolist()] else: nmax = int((self.xmax-self.xmin)/dx+1) if self.urcrnrx < self.llcrnrx: xx = self.llcrnrx-dx*N.arange(nmax) else: xx = self.llcrnrx+dx*N.arange(nmax) if side == 'b': lons,lats = self(xx,self.llcrnry*N.ones(xx.shape,'f'),inverse=True) else: lons,lats = self(xx,self.urcrnry*N.ones(xx.shape,'f'),inverse=True) lons = N.where(lons < 0, lons+360, lons) lons = [int(lon*10) for lon in lons.tolist()] lats = [int(lat*10) for lat in lats.tolist()] for lon in meridians: if lon<0: lon=lon+360. # find index of meridian (there may be two, so # search from left and right). try: nl = lons.index(int(lon*10)) except: nl = -1 try: nr = len(lons)-lons[::-1].index(int(lon*10))-1 except: nr = -1 if lon>180: lonlab = r'$\%s{%g\/^{\circ}\/W}$'%(font,N.fabs(lon-360)) elif lon<180 and lon != 0: lonlab = r'$\%s{%g\/^{\circ}\/E}$'%(font,lon) else: lonlab = r'$\%s{%g\/^{\circ}}$'%(font,lon) # meridians can intersect each map edge twice. for i,n in enumerate([nl,nr]): lat = lats[n]/10. # no meridians > latmax for projections other than merc,cyl. if self.projection not in ['merc','cyl'] and lat > latmax: continue # don't bother if close to the first label. if i and abs(nr-nl) < 100: continue if n > 0: if side == 'l': pylab.text(self.llcrnrx-xoffset,yy[n],lonlab,horizontalalignment='right',verticalalignment='center',fontsize=fontsize) elif side == 'r': pylab.text(self.urcrnrx+xoffset,yy[n],lonlab,horizontalalignment='left',verticalalignment='center',fontsize=fontsize) elif side == 'b': pylab.text(xx[n],self.llcrnry-yoffset,lonlab,horizontalalignment='center',verticalalignment='top',fontsize=fontsize) else: pylab.text(xx[n],self.urcrnry+yoffset,lonlab,horizontalalignment='center',verticalalignment='bottom',fontsize=fontsize) # make sure axis ticks are turned off ax.set_xticks([]) ax.set_yticks([])
def distance_transform_edt(input, sampling = None, return_distances = True, return_indices = False, distances = None, indices = None): """Exact euclidean distance transform. In addition to the distance transform, the feature transform can be calculated. In this case the index of the closest background element is returned along the first axis of the result. The return_distances, and return_indices flags can be used to indicate if the distance transform, the feature transform, or both must be returned. Optionally the sampling along each axis can be given by the sampling parameter which should be a sequence of length equal to the input rank, or a single number in which the sampling is assumed to be equal along all axes. the distances and indices arguments can be used to give optional output arrays that must be of the correct size and type (Float64 and Int32). """ if (not return_distances) and (not return_indices): msg = 'at least one of distances/indices must be specified' raise RuntimeError, msg ft_inplace = isinstance(indices, numarray.NumArray) dt_inplace = isinstance(distances, numarray.NumArray) # calculate the feature transform input = numarray.where(input, 1, 0).astype(numarray.Int8) if sampling is not None: sampling = _ni_support._normalize_sequence(sampling, input.rank) sampling = numarray.asarray(sampling, type = numarray.Float64) if not sampling.iscontiguous(): sampling = sampling.copy() if ft_inplace: ft = indices if ft.shape != (input.rank,) + input.shape: raise RuntimeError, 'indices has wrong shape' if ft.type() != numarray.Int32: raise RuntimeError, 'indices must be of Int32 type' else: ft = numarray.zeros((input.rank,) + input.shape, type = numarray.Int32) _nd_image.euclidean_feature_transform(input, sampling, ft) # if requested, calculate the distance transform if return_distances: dt = ft - numarray.indices(input.shape, type = ft.type()) dt = dt.astype(numarray.Float64) if sampling is not None: for ii in range(len(sampling)): dt[ii, ...] *= sampling[ii] numarray.multiply(dt, dt, dt) if dt_inplace: dt = numarray.add.reduce(dt, axis = 0) if distances.shape != dt.shape: raise RuntimeError, 'indices has wrong shape' if distances.type() != numarray.Float64: raise RuntimeError, 'indices must be of Float64 type' numarray.sqrt(dt, distances) del dt else: dt = numarray.add.reduce(dt, axis = 0) dt = numarray.sqrt(dt) # construct and return the result result = [] if return_distances and not dt_inplace: result.append(dt) if return_indices and not ft_inplace: result.append(ft) if len(result) == 2: return tuple(result) elif len(result) == 1: return result[0] else: return None
def distance_transform_cdt(input, structure = 'chessboard', return_distances = True, return_indices = False, distances = None, indices = None): """Distance transform for chamfer type of transforms. The structure determines the type of chamfering that is done. If the structure is equal to 'cityblock' a structure is generated using generate_binary_structure with a squared distance equal to 1. If the structure is equal to 'chessboard', a structure is generated using generate_binary_structure with a squared distance equal to the rank of the array. These choices correspond to the common interpretations of the cityblock and the chessboard distance metrics in two dimensions. In addition to the distance transform, the feature transform can be calculated. In this case the index of the closest background element is returned along the first axis of the result. The return_distances, and return_indices flags can be used to indicate if the distance transform, the feature transform, or both must be returned. The distances and indices arguments can be used to give optional output arrays that must be of the correct size and type (both Int32). """ if (not return_distances) and (not return_indices): msg = 'at least one of distances/indices must be specified' raise RuntimeError, msg ft_inplace = isinstance(indices, numarray.NumArray) dt_inplace = isinstance(distances, numarray.NumArray) input = numarray.asarray(input) if structure == 'cityblock': rank = input.rank structure = generate_binary_structure(rank, 1) elif structure == 'chessboard': rank = input.rank structure = generate_binary_structure(rank, rank) else: try: structure = numarray.asarray(structure) except: raise RuntimeError, 'invalid structure provided' for s in structure.shape: if s != 3: raise RuntimeError, 'structure sizes must be equal to 3' if not structure.iscontiguous(): structure = structure.copy() if dt_inplace: if distances.type() != numarray.Int32: raise RuntimeError, 'distances must be of Int32 type' if distances.shape != input.shape: raise RuntimeError, 'distances has wrong shape' dt = distances dt[...] = numarray.where(input, -1, 0).astype(numarray.Int32) else: dt = numarray.where(input, -1, 0).astype(numarray.Int32) rank = dt.rank if return_indices: sz = dt.nelements() ft = numarray.arange(sz, shape=dt.shape, type = numarray.Int32) else: ft = None _nd_image.distance_transform_op(structure, dt, ft) dt = dt[tuple([slice(None, None, -1)] * rank)] if return_indices: ft = ft[tuple([slice(None, None, -1)] * rank)] _nd_image.distance_transform_op(structure, dt, ft) dt = dt[tuple([slice(None, None, -1)] * rank)] if return_indices: ft = ft[tuple([slice(None, None, -1)] * rank)] ft = numarray.ravel(ft) if ft_inplace: if indices.type() != numarray.Int32: raise RuntimeError, 'indices must of Int32 type' if indices.shape != (dt.rank,) + dt.shape: raise RuntimeError, 'indices has wrong shape' tmp = indices else: tmp = numarray.indices(dt.shape, type = numarray.Int32) for ii in range(tmp.shape[0]): rtmp = numarray.ravel(tmp[ii, ...])[ft] rtmp.setshape(dt.shape) tmp[ii, ...] = rtmp ft = tmp # construct and return the result result = [] if return_distances and not dt_inplace: result.append(dt) if return_indices and not ft_inplace: result.append(ft) if len(result) == 2: return tuple(result) elif len(result) == 1: return result[0] else: return None
def distance_transform_bf(input, metric = "euclidean", sampling = None, return_distances = True, return_indices = False, distances = None, indices = None): """Distance transform function by a brute force algorithm. This function calculates the distance transform of the input, by replacing each background element (zero values), with its shortest distance to the foreground (any element non-zero). Three types of distance metric are supported: 'euclidean', 'city_block' and 'chessboard'. In addition to the distance transform, the feature transform can be calculated. In this case the index of the closest background element is returned along the first axis of the result. The return_distances, and return_indices flags can be used to indicate if the distance transform, the feature transform, or both must be returned. Optionally the sampling along each axis can be given by the sampling parameter which should be a sequence of length equal to the input rank, or a single number in which the sampling is assumed to be equal along all axes. This parameter is only used in the case of the euclidean distance transform. This function employs a slow brute force algorithm, see also the function distance_transform_cdt for more efficient city_block and chessboard algorithms. the distances and indices arguments can be used to give optional output arrays that must be of the correct size and type (Float64 and Int32). """ if (not return_distances) and (not return_indices): msg = 'at least one of distances/indices must be specified' raise RuntimeError, msg tmp1 = numarray.asarray(input) != 0 struct = generate_binary_structure(tmp1.rank, tmp1.rank) tmp2 = binary_dilation(tmp1, struct) tmp2 = numarray.logical_xor(tmp1, tmp2) tmp1 = tmp1.astype(numarray.Int8) - tmp2.astype(numarray.Int8) del tmp2 metric = metric.lower() if metric == 'euclidean': metric = 1 elif metric == 'cityblock': metric = 2 elif metric == 'chessboard': metric = 3 else: raise RuntimeError, 'distance metric not supported' if sampling != None: sampling = _ni_support._normalize_sequence(sampling, tmp1.rank) sampling = numarray.asarray(sampling, type = numarray.Float64) if not sampling.iscontiguous(): sampling = sampling.copy() if return_indices: ft = numarray.zeros(tmp1.shape, type = numarray.Int32) else: ft = None if return_distances: if distances == None: if metric == 1: dt = numarray.zeros(tmp1.shape, type = numarray.Float64) else: dt = numarray.zeros(tmp1.shape, type = numarray.UInt32) else: if distances.shape != tmp1.shape: raise RuntimeError, 'distances array has wrong shape' if metric == 1: if distances.type() != numarray.Float64: raise RuntimeError, 'distances array must be Float64' else: if distances.type() != numarray.UInt32: raise RuntimeError, 'distances array must be UInt32' dt = distances else: dt = None _nd_image.distance_transform_bf(tmp1, metric, sampling, dt, ft) if return_indices: if isinstance(indices, numarray.NumArray): if indices.type() != numarray.Int32: raise RuntimeError, 'indices must of Int32 type' if indices.shape != (tmp1.rank,) + tmp1.shape: raise RuntimeError, 'indices has wrong shape' tmp2 = indices else: tmp2 = numarray.indices(tmp1.shape, type = numarray.Int32) ft = numarray.ravel(ft) for ii in range(tmp2.shape[0]): rtmp = numarray.ravel(tmp2[ii, ...])[ft] rtmp.setshape(tmp1.shape) tmp2[ii, ...] = rtmp ft = tmp2 # construct and return the result result = [] if return_distances and not isinstance(distances, numarray.NumArray): result.append(dt) if return_indices and not isinstance(indices, numarray.NumArray): result.append(ft) if len(result) == 2: return tuple(result) elif len(result) == 1: return result[0] else: return None
def drawmeridians(self,ax,meridians,color='k',linewidth=1., \ linestyle='--',dashes=[1,1],labels=[0,0,0,0],\ font='rm',fontsize=12): """ draw meridians (longitude lines). ax - current axis instance. meridians - list containing longitude values to draw (in degrees). color - color to draw meridians (default black). linewidth - line width for meridians (default 1.) linestyle - line style for meridians (default '--', i.e. dashed). dashes - dash pattern for meridians (default [1,1], i.e. 1 pixel on, 1 pixel off). labels - list of 4 values (default [0,0,0,0]) that control whether meridians are labelled where they intersect the left, right, top or bottom of the plot. For example labels=[1,0,0,1] will cause meridians to be labelled where they intersect the left and bottom of the plot, but not the right and top. Labels are located with a precision of 0.1 degrees and are drawn using mathtext. font - mathtext font used for labels ('rm','tt','it' or 'cal', default 'rm'. fontsize - font size in points for labels (default 12). """ # don't draw meridians past latmax, always draw parallel at latmax. latmax = 80. # not used for cyl, merc projections. # offset for labels. yoffset = (self.urcrnry - self.llcrnry) / 100. / self.aspect xoffset = (self.urcrnrx - self.llcrnrx) / 100. if self.projection not in ['merc', 'cyl']: lats = N.arange(-latmax, latmax + 1).astype('f') else: lats = N.arange(-90, 91).astype('f') xdelta = 0.1 * (self.xmax - self.xmin) ydelta = 0.1 * (self.ymax - self.ymin) for merid in meridians: lons = merid * N.ones(len(lats), 'f') x, y = self(lons, lats) # remove points outside domain. testx = N.logical_and(x >= self.xmin - xdelta, x <= self.xmax + xdelta) x = N.compress(testx, x) y = N.compress(testx, y) testy = N.logical_and(y >= self.ymin - ydelta, y <= self.ymax + ydelta) x = N.compress(testy, x) y = N.compress(testy, y) if len(x) > 1 and len(y) > 1: # split into separate line segments if necessary. # (not necessary for mercator or cylindrical). xd = (x[1:] - x[0:-1])**2 yd = (y[1:] - y[0:-1])**2 dist = N.sqrt(xd + yd) split = dist > 500000. if N.sum(split) and self.projection not in ['merc', 'cyl']: ind = (N.compress( split, pylab.squeeze(split * N.indices(xd.shape))) + 1).tolist() xl = [] yl = [] iprev = 0 ind.append(len(xd)) for i in ind: xl.append(x[iprev:i]) yl.append(y[iprev:i]) iprev = i else: xl = [x] yl = [y] # draw each line segment. for x, y in zip(xl, yl): # skip if only a point. if len(x) > 1 and len(y) > 1: l = Line2D(x, y, linewidth=linewidth, linestyle=linestyle) l.set_color(color) l.set_dashes(dashes) ax.add_line(l) # draw labels for meridians. # search along edges of map to see if parallels intersect. # if so, find x,y location of intersection and draw a label there. if self.projection == 'cyl': dx = 0.01 dy = 0.01 elif self.projection == 'merc': dx = 0.01 dy = 1000 else: dx = 1000 dy = 1000 for dolab, side in zip(labels, ['l', 'r', 't', 'b']): if not dolab: continue # for cyl or merc, don't draw meridians on left or right. if self.projection in ['cyl', 'merc'] and side in ['l', 'r']: continue if side in ['l', 'r']: nmax = int((self.ymax - self.ymin) / dy + 1) if self.urcrnry < self.llcrnry: yy = self.llcrnry - dy * N.arange(nmax) else: yy = self.llcrnry + dy * N.arange(nmax) if side == 'l': lons, lats = self(self.llcrnrx * N.ones(yy.shape, 'f'), yy, inverse=True) else: lons, lats = self(self.urcrnrx * N.ones(yy.shape, 'f'), yy, inverse=True) lons = N.where(lons < 0, lons + 360, lons) lons = [int(lon * 10) for lon in lons.tolist()] lats = [int(lat * 10) for lat in lats.tolist()] else: nmax = int((self.xmax - self.xmin) / dx + 1) if self.urcrnrx < self.llcrnrx: xx = self.llcrnrx - dx * N.arange(nmax) else: xx = self.llcrnrx + dx * N.arange(nmax) if side == 'b': lons, lats = self(xx, self.llcrnry * N.ones(xx.shape, 'f'), inverse=True) else: lons, lats = self(xx, self.urcrnry * N.ones(xx.shape, 'f'), inverse=True) lons = N.where(lons < 0, lons + 360, lons) lons = [int(lon * 10) for lon in lons.tolist()] lats = [int(lat * 10) for lat in lats.tolist()] for lon in meridians: if lon < 0: lon = lon + 360. # find index of meridian (there may be two, so # search from left and right). try: nl = lons.index(int(lon * 10)) except: nl = -1 try: nr = len(lons) - lons[::-1].index(int(lon * 10)) - 1 except: nr = -1 if lon > 180: lonlab = r'$\%s{%g\/^{\circ}\/W}$' % (font, N.fabs(lon - 360)) elif lon < 180 and lon != 0: lonlab = r'$\%s{%g\/^{\circ}\/E}$' % (font, lon) else: lonlab = r'$\%s{%g\/^{\circ}}$' % (font, lon) # meridians can intersect each map edge twice. for i, n in enumerate([nl, nr]): lat = lats[n] / 10. # no meridians > latmax for projections other than merc,cyl. if self.projection not in ['merc', 'cyl'] and lat > latmax: continue # don't bother if close to the first label. if i and abs(nr - nl) < 100: continue if n > 0: if side == 'l': pylab.text(self.llcrnrx - xoffset, yy[n], lonlab, horizontalalignment='right', verticalalignment='center', fontsize=fontsize) elif side == 'r': pylab.text(self.urcrnrx + xoffset, yy[n], lonlab, horizontalalignment='left', verticalalignment='center', fontsize=fontsize) elif side == 'b': pylab.text(xx[n], self.llcrnry - yoffset, lonlab, horizontalalignment='center', verticalalignment='top', fontsize=fontsize) else: pylab.text(xx[n], self.urcrnry + yoffset, lonlab, horizontalalignment='center', verticalalignment='bottom', fontsize=fontsize) # make sure axis ticks are turned off ax.set_xticks([]) ax.set_yticks([])
lrcornerlon, lrcornerlat = awips221(lrcornerx, lrcornery, inverse=True) urcornerlon, urcornerlat = awips221(urcornerx, urcornery, inverse=True) ulcornerlon, ulcornerlat = awips221(ulcornerx, ulcornery, inverse=True) print '4 corners of AWIPS grid 221:' print llcornerlon, llcornerlat print lrcornerlon, lrcornerlat print urcornerlon, urcornerlat print ulcornerlon, ulcornerlat print 'from GRIB docs' print '(see http://www.nco.ncep.noaa.gov/pmb/docs/on388/tableb.html)' print ' -145.5 1.0' print ' -68.318 0.897' print ' -2.566 46.352' print ' 148.639 46.635' # compute lons and lats for the whole AWIPS grid 221 (377x249). x = numarray.zeros((nx, ny), 'd') y = numarray.zeros((nx, ny), 'd') x = dx * numarray.indices(x.shape)[0, :, :] y = dy * numarray.indices(y.shape)[1, :, :] import time t1 = time.clock() lons, lats = awips221(x, y, inverse=True) t2 = time.clock() print 'compute lats/lons for all points on AWIPS 221 grid (%sx%s)' % (nx, ny) print 'max/min lons' print min(numarray.ravel(lons)), max(numarray.ravel(lons)) print 'max/min lats' print min(numarray.ravel(lats)), max(numarray.ravel(lats)) print 'took', t2 - t1, 'secs'
def distance_transform_edt(input, sampling=None, return_distances=True, return_indices=False, distances=None, indices=None): """Exact euclidean distance transform. In addition to the distance transform, the feature transform can be calculated. In this case the index of the closest background element is returned along the first axis of the result. The return_distances, and return_indices flags can be used to indicate if the distance transform, the feature transform, or both must be returned. Optionally the sampling along each axis can be given by the sampling parameter which should be a sequence of length equal to the input rank, or a single number in which the sampling is assumed to be equal along all axes. the distances and indices arguments can be used to give optional output arrays that must be of the correct size and type (Float64 and Int32). """ if (not return_distances) and (not return_indices): msg = 'at least one of distances/indices must be specified' raise RuntimeError, msg ft_inplace = isinstance(indices, numarray.NumArray) dt_inplace = isinstance(distances, numarray.NumArray) # calculate the feature transform input = numarray.where(input, 1, 0).astype(numarray.Int8) if sampling is not None: sampling = _ni_support._normalize_sequence(sampling, input.rank) sampling = numarray.asarray(sampling, type=numarray.Float64) if not sampling.iscontiguous(): sampling = sampling.copy() if ft_inplace: ft = indices if ft.shape != (input.rank, ) + input.shape: raise RuntimeError, 'indices has wrong shape' if ft.type() != numarray.Int32: raise RuntimeError, 'indices must be of Int32 type' else: ft = numarray.zeros((input.rank, ) + input.shape, type=numarray.Int32) _nd_image.euclidean_feature_transform(input, sampling, ft) # if requested, calculate the distance transform if return_distances: dt = ft - numarray.indices(input.shape, type=ft.type()) dt = dt.astype(numarray.Float64) if sampling is not None: for ii in range(len(sampling)): dt[ii, ...] *= sampling[ii] numarray.multiply(dt, dt, dt) if dt_inplace: dt = numarray.add.reduce(dt, axis=0) if distances.shape != dt.shape: raise RuntimeError, 'indices has wrong shape' if distances.type() != numarray.Float64: raise RuntimeError, 'indices must be of Float64 type' numarray.sqrt(dt, distances) del dt else: dt = numarray.add.reduce(dt, axis=0) dt = numarray.sqrt(dt) # construct and return the result result = [] if return_distances and not dt_inplace: result.append(dt) if return_indices and not ft_inplace: result.append(ft) if len(result) == 2: return tuple(result) elif len(result) == 1: return result[0] else: return None
def distance_transform_cdt(input, structure='chessboard', return_distances=True, return_indices=False, distances=None, indices=None): """Distance transform for chamfer type of transforms. The structure determines the type of chamfering that is done. If the structure is equal to 'cityblock' a structure is generated using generate_binary_structure with a squared distance equal to 1. If the structure is equal to 'chessboard', a structure is generated using generate_binary_structure with a squared distance equal to the rank of the array. These choices correspond to the common interpretations of the cityblock and the chessboard distance metrics in two dimensions. In addition to the distance transform, the feature transform can be calculated. In this case the index of the closest background element is returned along the first axis of the result. The return_distances, and return_indices flags can be used to indicate if the distance transform, the feature transform, or both must be returned. The distances and indices arguments can be used to give optional output arrays that must be of the correct size and type (both Int32). """ if (not return_distances) and (not return_indices): msg = 'at least one of distances/indices must be specified' raise RuntimeError, msg ft_inplace = isinstance(indices, numarray.NumArray) dt_inplace = isinstance(distances, numarray.NumArray) input = numarray.asarray(input) if structure == 'cityblock': rank = input.rank structure = generate_binary_structure(rank, 1) elif structure == 'chessboard': rank = input.rank structure = generate_binary_structure(rank, rank) else: try: structure = numarray.asarray(structure) except: raise RuntimeError, 'invalid structure provided' for s in structure.shape: if s != 3: raise RuntimeError, 'structure sizes must be equal to 3' if not structure.iscontiguous(): structure = structure.copy() if dt_inplace: if distances.type() != numarray.Int32: raise RuntimeError, 'distances must be of Int32 type' if distances.shape != input.shape: raise RuntimeError, 'distances has wrong shape' dt = distances dt[...] = numarray.where(input, -1, 0).astype(numarray.Int32) else: dt = numarray.where(input, -1, 0).astype(numarray.Int32) rank = dt.rank if return_indices: sz = dt.nelements() ft = numarray.arange(sz, shape=dt.shape, type=numarray.Int32) else: ft = None _nd_image.distance_transform_op(structure, dt, ft) dt = dt[tuple([slice(None, None, -1)] * rank)] if return_indices: ft = ft[tuple([slice(None, None, -1)] * rank)] _nd_image.distance_transform_op(structure, dt, ft) dt = dt[tuple([slice(None, None, -1)] * rank)] if return_indices: ft = ft[tuple([slice(None, None, -1)] * rank)] ft = numarray.ravel(ft) if ft_inplace: if indices.type() != numarray.Int32: raise RuntimeError, 'indices must of Int32 type' if indices.shape != (dt.rank, ) + dt.shape: raise RuntimeError, 'indices has wrong shape' tmp = indices else: tmp = numarray.indices(dt.shape, type=numarray.Int32) for ii in range(tmp.shape[0]): rtmp = numarray.ravel(tmp[ii, ...])[ft] rtmp.setshape(dt.shape) tmp[ii, ...] = rtmp ft = tmp # construct and return the result result = [] if return_distances and not dt_inplace: result.append(dt) if return_indices and not ft_inplace: result.append(ft) if len(result) == 2: return tuple(result) elif len(result) == 1: return result[0] else: return None
def distance_transform_bf(input, metric="euclidean", sampling=None, return_distances=True, return_indices=False, distances=None, indices=None): """Distance transform function by a brute force algorithm. This function calculates the distance transform of the input, by replacing each background element (zero values), with its shortest distance to the foreground (any element non-zero). Three types of distance metric are supported: 'euclidean', 'city_block' and 'chessboard'. In addition to the distance transform, the feature transform can be calculated. In this case the index of the closest background element is returned along the first axis of the result. The return_distances, and return_indices flags can be used to indicate if the distance transform, the feature transform, or both must be returned. Optionally the sampling along each axis can be given by the sampling parameter which should be a sequence of length equal to the input rank, or a single number in which the sampling is assumed to be equal along all axes. This parameter is only used in the case of the euclidean distance transform. This function employs a slow brute force algorithm, see also the function distance_transform_cdt for more efficient city_block and chessboard algorithms. the distances and indices arguments can be used to give optional output arrays that must be of the correct size and type (Float64 and Int32). """ if (not return_distances) and (not return_indices): msg = 'at least one of distances/indices must be specified' raise RuntimeError, msg tmp1 = numarray.asarray(input) != 0 struct = generate_binary_structure(tmp1.rank, tmp1.rank) tmp2 = binary_dilation(tmp1, struct) tmp2 = numarray.logical_xor(tmp1, tmp2) tmp1 = tmp1.astype(numarray.Int8) - tmp2.astype(numarray.Int8) del tmp2 metric = metric.lower() if metric == 'euclidean': metric = 1 elif metric == 'cityblock': metric = 2 elif metric == 'chessboard': metric = 3 else: raise RuntimeError, 'distance metric not supported' if sampling != None: sampling = _ni_support._normalize_sequence(sampling, tmp1.rank) sampling = numarray.asarray(sampling, type=numarray.Float64) if not sampling.iscontiguous(): sampling = sampling.copy() if return_indices: ft = numarray.zeros(tmp1.shape, type=numarray.Int32) else: ft = None if return_distances: if distances == None: if metric == 1: dt = numarray.zeros(tmp1.shape, type=numarray.Float64) else: dt = numarray.zeros(tmp1.shape, type=numarray.UInt32) else: if distances.shape != tmp1.shape: raise RuntimeError, 'distances array has wrong shape' if metric == 1: if distances.type() != numarray.Float64: raise RuntimeError, 'distances array must be Float64' else: if distances.type() != numarray.UInt32: raise RuntimeError, 'distances array must be UInt32' dt = distances else: dt = None _nd_image.distance_transform_bf(tmp1, metric, sampling, dt, ft) if return_indices: if isinstance(indices, numarray.NumArray): if indices.type() != numarray.Int32: raise RuntimeError, 'indices must of Int32 type' if indices.shape != (tmp1.rank, ) + tmp1.shape: raise RuntimeError, 'indices has wrong shape' tmp2 = indices else: tmp2 = numarray.indices(tmp1.shape, type=numarray.Int32) ft = numarray.ravel(ft) for ii in range(tmp2.shape[0]): rtmp = numarray.ravel(tmp2[ii, ...])[ft] rtmp.setshape(tmp1.shape) tmp2[ii, ...] = rtmp ft = tmp2 # construct and return the result result = [] if return_distances and not isinstance(distances, numarray.NumArray): result.append(dt) if return_indices and not isinstance(indices, numarray.NumArray): result.append(ft) if len(result) == 2: return tuple(result) elif len(result) == 1: return result[0] else: return None