docs: def isoline_vmag(hemi, isolines=None, surface='midgray', min_length=2, **kw):
      '''
      isoline_vmag(hemi) calculates the visual magnification function f using the default set of
        iso-lines (as returned by neuropythy.vision.visual_isolines()). The hemi argument may
        alternately be a mesh object.
      isoline_vmag(hemi, isolns) uses the given iso-lines rather than the default ones.
      
      The return value of this funciton is a dictionary whose keys are 'tangential', 'radial', and
      'areal', and whose values are the estimated visual magnification functions. These functions
      are of the form f(x,y) where x and y can be numbers or arrays in the visual field.
      '''
      from neuropythy.util import (curry, zinv)
      from neuropythy.mri import is_cortex
      from neuropythy.vision import visual_isolines
      from neuropythy.geometry import to_mesh
      # if there's no isolines, get them
      if isolines is None: isolines = visual_isolines(hemi, **kw)
      # see if the isolines is a lazy map of visual areas; if so return a lazy map recursing...
      if pimms.is_vector(isolines.keys(), 'int'):
          f = lambda k: isoline_vmag(isolines[k], surface=surface, min_length=min_length)
          return pimms.lazy_map({k:curry(f, k) for k in six.iterkeys(isolines)})
      mesh = to_mesh((hemi, surface))
      # filter by min length
      if min_length is not None:
          isolines = {k: {kk: {kkk: [vvv[ii] for ii in iis] for (kkk,vvv) in six.iteritems(vv)}
                          for (kk,vv) in six.iteritems(v)
                          for iis in [[ii for (ii,u) in enumerate(vv['polar_angles'])
                                       if len(u) >= min_length]]
                          if len(iis) > 0}
                      for (k,v) in six.iteritems(isolines)}
      (rlns,tlns) = [isolines[k] for k in ['eccentricity', 'polar_angle']]
      if len(rlns) < 2: raise ValueError('fewer than 2 iso-eccentricity lines found')
      if len(tlns) < 2: raise ValueError('fewer than 2 iso-angle lines found')
      # grab the visual/surface lines
      ((rvlns,tvlns),(rslns,tslns)) = [[[u for lns in six.itervalues(xlns) for u in lns[k]]
                                        for xlns in (rlns,tlns)]
                                       for k in ('visual_coordinates','surface_coordinates')]
      # calculate some distances
      (rslen,tslen) = [[np.sqrt(np.sum((sx[:,:-1] - sx[:,1:])**2, 0)) for sx in slns]
                       for slns in (rslns,tslns)]
      (rvlen,tvlen) = [[np.sqrt(np.sum((vx[:,:-1] - vx[:,1:])**2, 0)) for vx in vlns]
                       for vlns in (rvlns,tvlns)]
      (rvxy, tvxy)  = [[0.5*(vx[:,:-1] + vx[:,1:]) for vx in vlns] for vlns in (rvlns,tvlns)]
      (rvlen,tvlen,rslen,tslen) = [np.concatenate(u) for u in (rvlen,tvlen,rslen,tslen)]
      (rvxy,tvxy)   = [np.hstack(vxy) for vxy in (rvxy,tvxy)]
      (rvmag,tvmag) = [vlen * zinv(slen) for (vlen,slen) in zip([rvlen,tvlen],[rslen,tslen])]
      return {k: {'visual_coordinates':vxy, 'visual_magnification': vmag,
                  'visual_lengths': vlen, 'surface_lengths': slen}
              for (k,vxy,vmag,vlen,slen) in zip(['radial','tangential'], [rvxy,tvxy],
                                                [rvmag,tvmag], [rvlen,tvlen], [rslen,tslen])}