docs: def eqarea_magic(in_file='sites.txt', dir_path=".", input_dir_path="",
              spec_file="specimens.txt", samp_file="samples.txt",
              site_file="sites.txt", loc_file="locations.txt",
              plot_by="all", crd="g", ignore_tilt=False,
              save_plots=True, fmt="svg", contour=False, color_map="coolwarm",
              plot_ell="", n_plots=5, interactive=False):
  
      """
      makes equal area projections from declination/inclination data
  
      Parameters
      ----------
      in_file : str, default "sites.txt"
      dir_path : str
          output directory, default "."
      input_dir_path : str
          input file directory (if different from dir_path), default ""
      spec_file : str
          input specimen file name, default "specimens.txt"
      samp_file: str
          input sample file name, default "samples.txt"
      site_file : str
          input site file name, default "sites.txt"
      loc_file : str
          input location file name, default "locations.txt"
      plot_by : str
          [spc, sam, sit, loc, all] (specimen, sample, site, location, all), default "all"
      crd : ['s','g','t'], coordinate system for plotting whereby:
          s : specimen coordinates, aniso_tile_correction = -1
          g : geographic coordinates, aniso_tile_correction = 0 (default)
          t : tilt corrected coordinates, aniso_tile_correction = 100
      ignore_tilt : bool
          default False.  If True, data are unoriented (allows plotting of measurement dec/inc)
      save_plots : bool
          plot and save non-interactively, default True
      fmt : str
          ["png", "svg", "pdf", "jpg"], default "svg"
      contour : bool
          plot as color contour
      colormap : str
          color map for contour plotting, default "coolwarm"
          see cartopy documentation for more options
      plot_ell : str
          [F,K,B,Be,Bv] plot Fisher, Kent, Bingham, Bootstrap ellipses or Boostrap eigenvectors
          default "" plots none
      n_plots : int
          maximum number of plots to make, default 5
          if you want to make all possible plots, specify "all"
      interactive : bool, default False
          interactively plot and display for each specimen
          (this is best used on the command line or in the Python interpreter)
  
  
      Returns
      ---------
      type - Tuple : (True or False indicating if conversion was sucessful, file name(s) written)
      """
      saved = []
      # parse out input/out directories
      input_dir_path, dir_path = pmag.fix_directories(input_dir_path, dir_path)
      # initialize some variables
      verbose = pmagplotlib.verbose
      FIG = {}  # plot dictionary
      FIG['eqarea'] = 1  # eqarea is figure 1
      pmagplotlib.plot_init(FIG['eqarea'], 5, 5)
      # get coordinate system
      if crd == "s":
          coord = "-1"
      elif crd == "t":
          coord = "100"
      else:
          coord = "0"
      # get item to plot by
      if plot_by == 'all':
          plot_key = 'all'
      elif plot_by == 'sit':
          plot_key = 'site'
      elif plot_by == 'sam':
          plot_key = 'sample'
      elif plot_by == 'spc':
          plot_key = 'specimen'
      else:
          plot_by = 'all'
          plot_key = 'all'
      # get distribution to plot ellipses/eigenvectors if desired
  
      if save_plots:
          verbose = False
  
      # set keys
      dec_key = 'dir_dec'
      inc_key = 'dir_inc'
      tilt_key = 'dir_tilt_correction'
  
      # create contribution
      fnames = {"specimens": spec_file, "samples": samp_file,
                'sites': site_file, 'locations': loc_file}
  
      if not os.path.exists(pmag.resolve_file_name(in_file, input_dir_path)):
          print('-E- Could not find {}'.format(in_file))
          return False, []
  
      contribution = cb.Contribution(input_dir_path, custom_filenames=fnames,
                                     single_file=in_file)
      table_name = list(contribution.tables.keys())[0]
      contribution.add_magic_table("contribution")
  
      # get contribution id if available for server plots
      if pmagplotlib.isServer:
          con_id = contribution.get_con_id()
      # try to propagate all names to measurement level
      try:
          contribution.propagate_location_to_samples()
          contribution.propagate_location_to_specimens()
          contribution.propagate_location_to_measurements()
      except KeyError as ex:
          pass
  
      # the object that contains the DataFrame + useful helper methods:
      data_container = contribution.tables[table_name]
      # the actual DataFrame:
      data = data_container.df
      plot_type = data_container.dtype
  
      if plot_key != "all" and plot_key not in data.columns:
          print("-E- You can't plot by {} with the data provided".format(plot_key))
          return False, []
  
      # add tilt key into DataFrame columns if it isn't there already
      if tilt_key not in data.columns:
          data.loc[:, tilt_key] = None
  
      if verbose:
          print(len(data), ' records read from ', in_file)
  
      # find desired dec,inc data:
      dir_type_key = ''
      #
      # get plotlist if not plotting all records
      #
      plotlist = []
      if plot_key != "all":
  
          # return all where plot_key is not blank
          if plot_key not in data.columns:
              print('-E- Can\'t plot by "{}".  That header is not in infile: {}'.format(
                  plot_key, in_file))
              return False, []
          plots = data[data[plot_key].notnull()]
          plotlist = plots[plot_key].unique()  # grab unique values
      else:
          plotlist.append('All')
  
      if n_plots != "all":
          if len(plotlist) > n_plots:
              plotlist = plotlist[:n_plots]
      fignum = 0
      for plot in plotlist:
          fignum += 1
          FIG['eqarea'] = fignum
          pmagplotlib.plot_init(FIG['eqarea'], 5, 5)
          if plot_ell:
              dist = plot_ell.upper()
              # if dist type is unrecognized, use Fisher
              if dist not in ['F', 'K', 'B', 'BE', 'BV']:
                  dist = 'F'
              if dist == "BV":
                  fignum += 1
                  FIG['bdirs'] = fignum
                  pmagplotlib.plot_init(FIG['bdirs'], 5, 5)
  
  
          if verbose:
              print(plot)
          if plot == 'All':
              # plot everything at once
              plot_data = data
          else:
              # pull out only partial data
              plot_data = data[data[plot_key] == plot]
  
          # get location names for the data
          locs = []
          if 'location' in plot_data.columns:
              locs = plot_data['location'].dropna().unique()
  
          DIblock = []
          GCblock = []
          # SLblock, SPblock = [], []
          title = plot
          mode = 1
  
          if dec_key not in plot_data.columns:
              print("-W- No dec/inc data")
              continue
          # get all records where dec & inc values exist
          plot_data = plot_data[plot_data[dec_key].notnull()
                                & plot_data[inc_key].notnull()]
          if plot_data.empty:
              print("-W- No dec/inc data")
              continue
  
          # get metadata for naming the plot file
          locations = str(data_container.get_name('location', df_slice=plot_data))
          site = str(data_container.get_name('site', df_slice=plot_data))
          sample = str(data_container.get_name('sample', df_slice=plot_data))
          specimen = str(data_container.get_name('specimen', df_slice=plot_data))
  
          # make sure method_codes is in plot_data
          if 'method_codes' not in plot_data.columns:
              plot_data['method_codes'] = ''
  
          # get data blocks
          # would have to ignore tilt to use measurement level data
          DIblock = data_container.get_di_block(df_slice=plot_data,
                                                tilt_corr=coord, excl=['DE-BFP'], ignore_tilt=ignore_tilt)
  
          if title == 'All':
              if len(locs):
                  title = " ,".join(locs) + " - {} {} plotted".format(str(len(DIblock)), plot_type)
              else:
                  title = "{} {} plotted".format(str(len(DIblock)), plot_type)
          #SLblock = [[ind, row['method_codes']] for ind, row in plot_data.iterrows()]
          # get great circles
          great_circle_data = data_container.get_records_for_code('DE-BFP', incl=True,
                                                                  use_slice=True, sli=plot_data)
  
          if len(great_circle_data) > 0:
              gc_cond = great_circle_data[tilt_key] == coord
              GCblock = [[float(row[dec_key]), float(row[inc_key])]
                         for ind, row in great_circle_data[gc_cond].iterrows()]
              #SPblock = [[ind, row['method_codes']] for ind, row in great_circle_data[gc_cond].iterrows()]
  
          if len(DIblock) > 0:
              if not contour:
                  pmagplotlib.plot_eq(FIG['eqarea'], DIblock, title)
              else:
                  pmagplotlib.plot_eq_cont(
                      FIG['eqarea'], DIblock, color_map=color_map)
          else:
              pmagplotlib.plot_net(FIG['eqarea'])
          if len(GCblock) > 0:
              for rec in GCblock:
                  pmagplotlib.plot_circ(FIG['eqarea'], rec, 90., 'g')
          if len(DIblock) == 0 and len(GCblock) == 0:
              if verbose:
                  print("no records for plotting")
                  fignum -= 1
                  if 'bdirs' in FIG:
                      fignum -= 1
              continue
              # sys.exit()
          if plot_ell:
              ppars = pmag.doprinc(DIblock)  # get principal directions
              nDIs, rDIs, npars, rpars = [], [], [], []
              for rec in DIblock:
                  angle = pmag.angle([rec[0], rec[1]], [
                                     ppars['dec'], ppars['inc']])
                  if angle > 90.:
                      rDIs.append(rec)
                  else:
                      nDIs.append(rec)
              if dist == 'B':  # do on whole dataset
                  etitle = "Bingham confidence ellipse"
                  bpars = pmag.dobingham(DIblock)
                  for key in list(bpars.keys()):
                      if key != 'n' and verbose:
                          print("    ", key, '%7.1f' % (bpars[key]))
                      if key == 'n' and verbose:
                          print("    ", key, '       %i' % (bpars[key]))
                  npars.append(bpars['dec'])
                  npars.append(bpars['inc'])
                  npars.append(bpars['Zeta'])
                  npars.append(bpars['Zdec'])
                  npars.append(bpars['Zinc'])
                  npars.append(bpars['Eta'])
                  npars.append(bpars['Edec'])
                  npars.append(bpars['Einc'])
              if dist == 'F':
                  etitle = "Fisher confidence cone"
                  if len(nDIs) > 2:
                      fpars = pmag.fisher_mean(nDIs)
                      for key in list(fpars.keys()):
                          if key != 'n' and verbose:
                              print("    ", key, '%7.1f' % (fpars[key]))
                          if key == 'n' and verbose:
                              print("    ", key, '       %i' % (fpars[key]))
                      mode += 1
                      npars.append(fpars['dec'])
                      npars.append(fpars['inc'])
                      npars.append(fpars['alpha95'])  # Beta
                      npars.append(fpars['dec'])
                      isign = abs(fpars['inc']) / fpars['inc']
                      npars.append(fpars['inc']-isign*90.)  # Beta inc
                      npars.append(fpars['alpha95'])  # gamma
                      npars.append(fpars['dec']+90.)  # Beta dec
                      npars.append(0.)  # Beta inc
                  if len(rDIs) > 2:
                      fpars = pmag.fisher_mean(rDIs)
                      if verbose:
                          print("mode ", mode)
                      for key in list(fpars.keys()):
                          if key != 'n' and verbose:
                              print("    ", key, '%7.1f' % (fpars[key]))
                          if key == 'n' and verbose:
                              print("    ", key, '       %i' % (fpars[key]))
                      mode += 1
                      rpars.append(fpars['dec'])
                      rpars.append(fpars['inc'])
                      rpars.append(fpars['alpha95'])  # Beta
                      rpars.append(fpars['dec'])
                      isign = abs(fpars['inc']) / fpars['inc']
                      rpars.append(fpars['inc']-isign*90.)  # Beta inc
                      rpars.append(fpars['alpha95'])  # gamma
                      rpars.append(fpars['dec']+90.)  # Beta dec
                      rpars.append(0.)  # Beta inc
              if dist == 'K':
                  etitle = "Kent confidence ellipse"
                  if len(nDIs) > 3:
                      kpars = pmag.dokent(nDIs, len(nDIs))
                      if verbose:
                          print("mode ", mode)
                      for key in list(kpars.keys()):
                          if key != 'n' and verbose:
                              print("    ", key, '%7.1f' % (kpars[key]))
                          if key == 'n' and verbose:
                              print("    ", key, '       %i' % (kpars[key]))
                      mode += 1
                      npars.append(kpars['dec'])
                      npars.append(kpars['inc'])
                      npars.append(kpars['Zeta'])
                      npars.append(kpars['Zdec'])
                      npars.append(kpars['Zinc'])
                      npars.append(kpars['Eta'])
                      npars.append(kpars['Edec'])
                      npars.append(kpars['Einc'])
                  if len(rDIs) > 3:
                      kpars = pmag.dokent(rDIs, len(rDIs))
                      if verbose:
                          print("mode ", mode)
                      for key in list(kpars.keys()):
                          if key != 'n' and verbose:
                              print("    ", key, '%7.1f' % (kpars[key]))
                          if key == 'n' and verbose:
                              print("    ", key, '       %i' % (kpars[key]))
                      mode += 1
                      rpars.append(kpars['dec'])
                      rpars.append(kpars['inc'])
                      rpars.append(kpars['Zeta'])
                      rpars.append(kpars['Zdec'])
                      rpars.append(kpars['Zinc'])
                      rpars.append(kpars['Eta'])
                      rpars.append(kpars['Edec'])
                      rpars.append(kpars['Einc'])
              else:  # assume bootstrap
                  if dist == 'BE':
                      if len(nDIs) > 5:
                          BnDIs = pmag.di_boot(nDIs)
                          Bkpars = pmag.dokent(BnDIs, 1.)
                          if verbose:
                              print("mode ", mode)
                          for key in list(Bkpars.keys()):
                              if key != 'n' and verbose:
                                  print("    ", key, '%7.1f' % (Bkpars[key]))
                              if key == 'n' and verbose:
                                  print("    ", key, '       %i' % (Bkpars[key]))
                          mode += 1
                          npars.append(Bkpars['dec'])
                          npars.append(Bkpars['inc'])
                          npars.append(Bkpars['Zeta'])
                          npars.append(Bkpars['Zdec'])
                          npars.append(Bkpars['Zinc'])
                          npars.append(Bkpars['Eta'])
                          npars.append(Bkpars['Edec'])
                          npars.append(Bkpars['Einc'])
                      if len(rDIs) > 5:
                          BrDIs = pmag.di_boot(rDIs)
                          Bkpars = pmag.dokent(BrDIs, 1.)
                          if verbose:
                              print("mode ", mode)
                          for key in list(Bkpars.keys()):
                              if key != 'n' and verbose:
                                  print("    ", key, '%7.1f' % (Bkpars[key]))
                              if key == 'n' and verbose:
                                  print("    ", key, '       %i' % (Bkpars[key]))
                          mode += 1
                          rpars.append(Bkpars['dec'])
                          rpars.append(Bkpars['inc'])
                          rpars.append(Bkpars['Zeta'])
                          rpars.append(Bkpars['Zdec'])
                          rpars.append(Bkpars['Zinc'])
                          rpars.append(Bkpars['Eta'])
                          rpars.append(Bkpars['Edec'])
                          rpars.append(Bkpars['Einc'])
                      etitle = "Bootstrapped confidence ellipse"
                  elif dist == 'BV':
                      sym = {'lower': ['o', 'c'], 'upper': [
                          'o', 'g'], 'size': 3, 'edgecolor': 'face'}
                      if len(nDIs) > 5:
                          BnDIs = pmag.di_boot(nDIs)
                          pmagplotlib.plot_eq_sym(
                              FIG['bdirs'], BnDIs, 'Bootstrapped Eigenvectors', sym)
                      if len(rDIs) > 5:
                          BrDIs = pmag.di_boot(rDIs)
                          if len(nDIs) > 5:  # plot on existing plots
                              pmagplotlib.plot_di_sym(FIG['bdirs'], BrDIs, sym)
                          else:
                              pmagplotlib.plot_eq(
                                  FIG['bdirs'], BrDIs, 'Bootstrapped Eigenvectors')
              if dist == 'B':
                  if len(nDIs) > 3 or len(rDIs) > 3:
                      pmagplotlib.plot_conf(FIG['eqarea'], etitle, [], npars, 0)
              elif len(nDIs) > 3 and dist != 'BV':
                  pmagplotlib.plot_conf(FIG['eqarea'], etitle, [], npars, 0)
                  if len(rDIs) > 3:
                      pmagplotlib.plot_conf(FIG['eqarea'], etitle, [], rpars, 0)
              elif len(rDIs) > 3 and dist != 'BV':
                  pmagplotlib.plot_conf(FIG['eqarea'], etitle, [], rpars, 0)
  
          for key in list(FIG.keys()):
              files = {}
              #if filename:  # use provided filename
              #    filename += '.' + fmt
              if pmagplotlib.isServer:  # use server plot naming convention
                  if plot_key == 'all':
                      filename = 'LO:_'+locations+'_SI:__SA:__SP:__CO:_'+crd+'_TY:_'+key+'_.'+fmt
                  else:
                      filename = 'LO:_'+locations+'_SI:_'+site+'_SA:_'+sample + \
                          '_SP:_'+str(specimen)+'_CO:_'+crd+'_TY:_'+key+'_.'+fmt
              elif plot_key == 'all':
                  filename = 'all'
                  if locs:
                      loc_string = "_".join(
                          [str(loc).replace(' ', '_') for loc in locs])
                      filename += "_" + loc_string
                  filename += "_" + crd + "_" + key
                  filename += ".{}".format(fmt)
              else:  # use more readable naming convention
                  filename = ''
                  # fix this if plot_by is location , for example
                  use_names = {'location': [locations], 'site': [locations, site],
                               'sample': [locations, site, sample],
                               'specimen': [locations, site, sample, specimen]}
                  use = use_names[plot_key]
                  use.extend([crd, key])
                  # [locations, site, sample, specimen, crd, key]:
                  for item in use:
                      if item:
                          item = item.replace(' ', '_')
                          filename += item + '_'
                  if filename.endswith('_'):
                      filename = filename[:-1]
                  filename += ".{}".format(fmt)
  
              if not pmagplotlib.isServer:
                  filename = os.path.join(dir_path, filename)
              files[key] = filename
  
          if pmagplotlib.isServer:
              titles = {'eqarea': 'Equal Area Plot'}
              FIG = pmagplotlib.add_borders(FIG, titles, con_id=con_id)
              saved_figs = pmagplotlib.save_plots(FIG, files)
              saved.extend(saved_figs)
  
          elif save_plots:
              saved_figs = pmagplotlib.save_plots(FIG, files, incl_directory=True)
              saved.extend(saved_figs)
              continue
          elif interactive:
              pmagplotlib.draw_figs(FIG)
              ans = input(" S[a]ve to save plot, [q]uit, Return to continue:  ")
              if ans == "q":
                  return True, []
              if ans == "a":
                  saved_figs = pmagplotlib.save_plots(FIG, files, incl_directory=True)
                  saved.extend(saved)
          continue
      return True, saved