docs: def areal_cmag(mesh, retinotopy='any', mask=None, surface_area=None, weight=None, nnearest=None):
      '''
      areal_cmag(obj) yields an areal cortical magnification function for the given vertex-set object
        or mapping of retinotopy data obj. If obj is a mapping of retinotopy data, then it must also
        contain the key 'surface_area' specifying the surface area of each vertex in mm^2.
  
      The areal cmag function is a measurement of how much surface area in a retinotopy dataset is
      found within a circle found in the visual field. The size of the circle is chosen automatically
      by looking up the <n> nearest neighbors of the point in question, summing their surface areas,
      and dividing by the area of the circle in the visual field that is big enough to hold them.
  
      The returned object cm = areal_cmag(obj) is a special cortical magnification object that can be
      called as cm(x, y) or cm(coord) where x and y may be lists or values and coord may be a 2D
      vector or an (n x 2) matrix. The yielded value(s) will always be in units mm^2 / deg^2.
  
      The following options may be provided (also in argument order):
        * retinotopy (default: 'any') specifies the retinotopy data to be used; this is passed to
          the retinotopy_data function with the mesh.
        * mask (default: None) specifies the mask that should be used; this is interpreted by a call
          to mesh.mask(mask)
        * surface_area (default: 'midgray_surface_area') specifies the vertex area property that
          should be used; this is interpreted by a call to mesh.property(surface_area) so may be
          either a property name or a list of values
        * weight (default: None) specifies additional weights that should be used; this is generally
          discouraged
        * nnearest (default: None) specifies the number of nearest neighbors to search for when
          calculating the magnification; if None, then uses int(numpy.ceil(numpy.sqrt(n) * np.log(n)))
          where n is the number of vertices included in the mask
      '''
      # First, find the retino data
      retino = retinotopy_data(mesh, retinotopy)
      # Convert from polar angle/eccen to longitude/latitude
      (ang,ecc) = as_retinotopy(retino, 'visual')
      # get the surface area
      if surface_area is None: surface_area = 'midgray_surface_area'
      if pimms.is_str(surface_area):
          if surface_area in retino: surface_area = retino[surface_area]
          elif geo.is_vset(mesh):    surface_area = mesh.property(surface_area)
          else:                      surface_area = mesh[surface_area]
      # get the weight
      if weight is not None:
          if pimms.is_str(weight):
              if weight in retino:    weight = retino[weight]
              elif geo.is_vset(mesh): weight = mesh.property(weight)
              else:                   weight = mesh[weight]
      wgt = next((retino[q] for q in ('variance_explained', 'weight') if q in retino), None)
      # Get the indices we care about
      ii = geo.to_mask(mesh, mask, indices=True)
      ii = ii[np.isfinite(ang[ii]) & np.isfinite(ecc[ii])]
      # get our nnearest
      if nnearest is None: nnearest = int(np.ceil(np.sqrt(len(ii)) * np.log(len(ii))))
      return ArealCorticalMagnification(ang[ii], ecc[ii], surface_area[ii],
                                        weight=(None if wgt is None else wgt[ii]),
                                        nnearest=nnearest)