docs: def plot_map(fignum, lats, lons, Opts):
      """
      makes a cartopy map  with lats/lons
      Requires installation of cartopy
  
      Parameters:
      _______________
      fignum : matplotlib figure number
      lats : array or list of latitudes
      lons : array or list of longitudes
      Opts : dictionary of plotting options:
          Opts.keys=
              proj : projection [supported cartopy projections:
                  pc = Plate Carree
                  aea = Albers Equal Area
                  aeqd = Azimuthal Equidistant
                  lcc = Lambert Conformal
                  lcyl = Lambert Cylindrical
                  merc = Mercator
                  mill = Miller Cylindrical
                  moll = Mollweide [default]
                  ortho = Orthographic
                  robin = Robinson
                  sinu = Sinusoidal
                  stere = Stereographic
                  tmerc = Transverse Mercator
                  utm = UTM [set zone and south keys in Opts]
                  laea = Lambert Azimuthal Equal Area
                  geos = Geostationary
                  npstere = North-Polar Stereographic
                  spstere = South-Polar Stereographic
              latmin : minimum latitude for plot
              latmax : maximum latitude for plot
              lonmin : minimum longitude for plot
              lonmax : maximum longitude
              lat_0 : central latitude
              lon_0 : central longitude
              sym : matplotlib symbol
              symsize : symbol size in pts
              edge : markeredgecolor
              cmap : matplotlib color map
              res :  resolution [c,l,i,h] for low/crude, intermediate, high
              boundinglat : bounding latitude
              sym : matplotlib symbol for plotting
              symsize : matplotlib symbol size for plotting
              names : list of names for lats/lons (if empty, none will be plotted)
              pltgrd : if True, put on grid lines
              padlat : padding of latitudes
              padlon : padding of longitudes
              gridspace : grid line spacing
              global : global projection [default is True]
              oceancolor : 'azure'
              landcolor : 'bisque' [choose any of the valid color names for matplotlib
                see https://matplotlib.org/examples/color/named_colors.html
              details : dictionary with keys:
                  coasts : if True, plot coastlines
                  rivers : if True, plot rivers
                  states : if True, plot states
                  countries : if True, plot countries
                  ocean : if True, plot ocean
                  fancy : if True, plot etopo 20 grid
                      NB:  etopo must be installed
          if Opts keys not set :these are the defaults:
             Opts={'latmin':-90,'latmax':90,'lonmin':0,'lonmax':360,'lat_0':0,'lon_0':0,'proj':'moll','sym':'ro,'symsize':5,'edge':'black','pltgrid':1,'res':'c','boundinglat':0.,'padlon':0,'padlat':0,'gridspace':30,'details':all False,'edge':None,'cmap':'jet','fancy':0,'zone':'','south':False,'oceancolor':'azure','landcolor':'bisque'}
  
      """
      if not has_cartopy:
          print('This function requires installation of cartopy')
          return
      from matplotlib import cm
      # draw meridian labels on the bottom [left,right,top,bottom]
      mlabels = [0, 0, 0, 1]
      plabels = [1, 0, 0, 0]  # draw parallel labels on the left
      Opts_defaults = {'latmin': -90, 'latmax': 90, 'lonmin': 0, 'lonmax': 360,
                       'lat_0': 0, 'lon_0': 0, 'proj': 'moll', 'sym': 'ro', 'symsize': 5,
                       'edge': None, 'pltgrid': 1, 'res': 'c', 'boundinglat': 0.,
                       'padlon': 0, 'padlat': 0, 'gridspace': 30, 'global': 1, 'cmap': 'jet','oceancolor':'azure','landcolor':'bisque',
                       'details': {'fancy': 0, 'coasts': 0, 'rivers': 0, 'states': 0, 'countries': 0, 'ocean': 0},
                       'edgecolor': 'face'}
      for key in Opts_defaults.keys():
          if key not in Opts.keys() and key != 'details':
              Opts[key] = Opts_defaults[key]
          if key == 'details':
              if key not in Opts.keys():
                  Opts[key] = Opts_defaults[key]
              for detail_key in Opts_defaults[key].keys():
                  if detail_key not in Opts[key].keys():
                      Opts[key][detail_key] = Opts_defaults[key][detail_key]
      if Opts['proj'] == 'pc':
          ax = plt.axes(projection=ccrs.PlateCarree())
          ax.set_extent([Opts['lonmin'], Opts['lonmax'], Opts['latmin'], Opts['latmax']],
                        crs=ccrs.PlateCarree())
      if Opts['proj'] == 'aea':
          ax = plt.axes(projection=ccrs.AlbersEqualArea(
              central_longitude=Opts['lon_0'],
              central_latitude=Opts['lat_0'],
              false_easting=0.0, false_northing=0.0, standard_parallels=(20.0, 50.0),
              globe=None))
      if Opts['proj'] == 'lcc':
          proj = ccrs.LambertConformal(central_longitude=Opts['lon_0'],  central_latitude=Opts['lat_0'],\
                 false_easting=0.0, false_northing=0.0, standard_parallels=(20.0, 50.0),
                 globe=None)
          fig=plt.figure(fignum,figsize=(6,6),frameon=True)
          ax = plt.axes(projection=proj)
          ax.set_extent([Opts['lonmin'], Opts['lonmax'], Opts['latmin'], Opts['latmax']],
                        crs=ccrs.PlateCarree())
      if Opts['proj'] == 'lcyl':
          ax = plt.axes(projection=ccrs.LambertCylindrical(
              central_longitude=Opts['lon_0']))
  
      if Opts['proj'] == 'merc':
          ax = plt.axes(projection=ccrs.Mercator(
              central_longitude=Opts['lon_0'], min_latitude=Opts['latmin'],
              max_latitude=Opts['latmax'], latitude_true_scale=0.0, globe=None))
          ax.set_extent([Opts['lonmin'],Opts['lonmax'],\
                       Opts['latmin'],Opts['latmax']])
      if Opts['proj'] == 'mill':
          ax = plt.axes(projection=ccrs.Miller(
              central_longitude=Opts['lon_0']))
      if Opts['proj'] == 'moll':
          ax = plt.axes(projection=ccrs.Mollweide(
              central_longitude=Opts['lat_0'], globe=None))
      if Opts['proj'] == 'ortho':
          ax = plt.axes(projection=ccrs.Orthographic(
              central_longitude=Opts['lon_0'],
              central_latitude=Opts['lat_0']))
      if Opts['proj'] == 'robin':
          ax = plt.axes(projection=ccrs.Robinson(
              central_longitude=Opts['lon_0'],
              globe=None))
  
      if Opts['proj'] == 'sinu':
          ax = plt.axes(projection=ccrs.Sinusoidal(
              central_longitude=Opts['lon_0'],
              false_easting=0.0, false_northing=0.0,
              globe=None))
  
      if Opts['proj'] == 'stere':
          ax = plt.axes(projection=ccrs.Stereographic(
              central_longitude=Opts['lon_0'],
              false_easting=0.0, false_northing=0.0,
              true_scale_latitude=None,
              scale_factor=None,
              globe=None))
      if Opts['proj'] == 'tmerc':
          ax = plt.axes(projection=ccrs.TransverseMercator(
              central_longitude=Opts['lon_0'], central_latitude=Opts['lat_0'],
              false_easting=0.0, false_northing=0.0,
              scale_factor=None,
              globe=None))
      if Opts['proj'] == 'utm':
          ax = plt.axes(projection=ccrs.UTM(
              zone=Opts['zone'],
              southern_hemisphere=Opts['south'],
              globe=None))
      if Opts['proj'] == 'geos':
          ax = plt.axes(projection=ccrs.Geostationary(
              central_longitude=Opts['lon_0'],
              false_easting=0.0, false_northing=0.0,
              satellite_height=35785831,
              sweep_axis='y',
              globe=None))
      if Opts['proj'] == 'laea':
          ax = plt.axes(projection=ccrs.LambertAzimuthalEqualArea(
              central_longitude=Opts['lon_0'], central_latitude=Opts['lat_0'],
              false_easting=0.0, false_northing=0.0,
              globe=None))
      if Opts['proj'] == 'npstere':
          ax = plt.axes(projection=ccrs.NorthPolarStereo(
              central_longitude=Opts['lon_0'],
              true_scale_latitude=None,
              globe=None))
      if Opts['proj'] == 'spstere':
          ax = plt.axes(projection=ccrs.SouthPolarStereo(
              central_longitude=Opts['lon_0'],
              true_scale_latitude=None,
              globe=None))
  
      if 'details' in list(Opts.keys()):
          if Opts['details']['fancy'] == 1:
              pmag_data_dir = find_pmag_dir.get_data_files_dir()
              EDIR = os.path.join(pmag_data_dir, "etopo20")
              etopo_path = os.path.join(EDIR, 'etopo20data.gz')
              etopo = np.loadtxt(os.path.join(EDIR, 'etopo20data.gz'))
              elons = np.loadtxt(os.path.join(EDIR, 'etopo20lons.gz'))
              elats = np.loadtxt(os.path.join(EDIR, 'etopo20lats.gz'))
              xx, yy = np.meshgrid(elons, elats)
              levels = np.arange(-10000, 8000, 500)  # define contour intervals
              m = ax.contourf(xx, yy, etopo, levels,
                              transform=ccrs.PlateCarree(),
                              cmap=Opts['cmap'])
              cbar=plt.colorbar(m)
          if Opts['details']['coasts'] == 1:
              if Opts['res']=='c' or Opts['res']=='l':
                  ax.coastlines(resolution='110m')
              elif Opts['res']=='i':
                  ax.coastlines(resolution='50m')
              elif Opts['res']=='h':
                  ax.coastlines(resolution='10m')
          if Opts['details']['rivers'] == 1:
              ax.add_feature(cfeature.RIVERS)
          if Opts['details']['states'] == 1:
              states_provinces = cfeature.NaturalEarthFeature(
                  category='cultural',
                  name='admin_1_states_provinces_lines',
                  scale='50m',
                  edgecolor='black',
                  facecolor='none',
                  linestyle='dotted')
              ax.add_feature(states_provinces)
          if Opts['details']['countries'] == 1:
              ax.add_feature(BORDERS, linestyle='-', linewidth=2)
          if Opts['details']['ocean'] == 1:
              ax.add_feature(OCEAN, color=Opts['oceancolor'])
              ax.add_feature(LAND, color=Opts['landcolor'])
              ax.add_feature(LAKES, color=Opts['oceancolor'])
      if Opts['proj'] in ['merc', 'pc','lcc']:
          if Opts['pltgrid']:
              if Opts['proj']=='lcc':
                  fig.canvas.draw()
                  #xticks=list(np.arange(Opts['lonmin'],Opts['lonmax']+Opts['gridspace'],Opts['gridspace']))
                  #yticks=list(np.arange(Opts['latmin'],Opts['latmax']+Opts['gridspace'],Opts['gridspace']))
                  xticks=list(np.arange(-180,180,Opts['gridspace']))
                  yticks=list(np.arange(-90,90,Opts['gridspace']))
                  ax.gridlines(ylocs=yticks,xlocs=xticks,linewidth=2,
                                linestyle='dotted')
                  ax.xaxis.set_major_formatter(LONGITUDE_FORMATTER) # you need this here
                  ax.yaxis.set_major_formatter(LATITUDE_FORMATTER)# you need this here, too
  
                  try:
                      import pmagpy.lcc_ticks as lcc_ticks
                      lcc_ticks.lambert_xticks(ax, xticks)
                      lcc_ticks.lambert_yticks(ax, yticks)
  
                  except:
                      print ('plotting of tick marks on Lambert Conformal requires the package "shapely".\n Try importing with "conda install -c conda-forge shapely"')
              else:
                  gl = ax.gridlines(crs=ccrs.PlateCarree(), linewidth=2,
                                linestyle='dotted', draw_labels=True)
                  gl.ylocator = mticker.FixedLocator(np.arange(-80, 81, Opts['gridspace']))
                  gl.xlocator = mticker.FixedLocator(np.arange(-180, 181, Opts['gridspace']))
                  gl.xformatter = LONGITUDE_FORMATTER
                  gl.yformatter = LATITUDE_FORMATTER
                  gl.xlabels_top = False
          else:
              gl = ax.gridlines(crs=ccrs.PlateCarree(),
                                linewidth=2, linestyle='dotted')
      elif Opts['pltgrid']:
          print('gridlines only supported for PlateCarree, Lambert Conformal, and Mercator plots currently')
      prn_name, symsize = 0, 5
      # if 'names' in list(Opts.keys()) > 0:
      #    names = Opts['names']
      #    if len(names) > 0:
      #        prn_name = 1
  ##
      X, Y, T, k = [], [], [], 0
      if 'symsize' in list(Opts.keys()):
          symsize = Opts['symsize']
      if Opts['sym'][-1] != '-':  # just plot points
          color, symbol = Opts['sym'][0], Opts['sym'][1]
          ax.scatter(lons, lats, s=Opts['symsize'], c=color, marker=symbol,
                     transform=ccrs.Geodetic(), edgecolors=Opts['edgecolor'])
          if prn_name == 1:
              print('labels not yet implemented in plot_map')
              # for pt in range(len(lats)):
              #    T.append(plt.text(X[pt] + 5000, Y[pt] - 5000, names[pt]))
      else:  # for lines,  need to separate chunks using lat==100.
          ax.plot(lons, lats, Opts['sym'], transform=ccrs.Geodetic())
      if Opts['global']:
          ax.set_global()