Example #1
0
    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
Example #2
0
    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
Example #3
0
    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)
Example #4
0
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)
Example #5
0
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)
Example #6
0
    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'
Example #7
0
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']
Example #8
0
    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([])
Example #9
0
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
Example #10
0
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
Example #11
0
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
Example #12
0
    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([])
Example #13
0
    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'
Example #14
0
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
Example #15
0
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
Example #16
0
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