docs: def calculate_vgp_data(self):
          """
          Calculates VGPS for all samples, sites, and locations
  
          Returns
          -------
          VGP_Data : dictionary of structure {sample: {comp: data},
              site: {comp: data}, location: {comp: data}}
          """
          # get criteria if it exists else use default
          crit_data = self.read_criteria_file()
          if crit_data == None:
              crit_data = pmag.default_criteria(0)
          accept = {}
          for critrec in crit_data:
              if type(critrec) != dict:
                  continue
              for key in list(critrec.keys()):
                  # need to migrate specimen_dang to specimen_int_dang
                  if 'IE-SPEC' in list(critrec.keys()) and 'specimen_dang' in list(critrec.keys()) and 'specimen_int_dang' not in list(critrec.keys()):
                      critrec['specimen_int_dang'] = critrec['specimen_dang']
                      del critrec['specimen_dang']
                  # need to get rid of ron shaars sample_int_sigma_uT
                  if 'sample_int_sigma_uT' in list(critrec.keys()):
                      critrec['sample_int_sigma'] = '%10.3e' % (
                          eval(critrec['sample_int_sigma_uT'])*1e-6)
                  if key not in list(accept.keys()) and critrec[key] != '':
                      accept[key] = critrec[key]
  
          Ns = []
          # retrieve specimen data to calculate VGPS with
          for s in list(self.pmag_results_data['specimens'].keys()):
              for fit in self.pmag_results_data['specimens'][s]:
                  if fit in self.bad_fits:
                      continue
                  pars = fit.get(self.COORDINATE_SYSTEM)
                  # check for interpretation data for fit
                  if not pars:
                      pars = self.get_PCA_parameters(
                          s, fit, fit.tmin, fit.tmax, self.COORDINATE_SYSTEM, fit.PCA_type)
                      if not pars or 'specimen_dec' not in list(pars.keys()) or 'specimen_inc' not in list(pars.keys()):
                          print(("Could not calculate interpretation for specimen %s and fit %s while calculating VGP data, skipping this component" % (
                              s, fit.name)))
                          continue
                      pars['er_specimen_name'] = s
                      pars['specimen_comp_name'] = fit.name
                  Ns.append(pars)
  
          SpecDirs = []
          if crit_data != None:  # use selection criteria
              for rec in Ns:  # look through everything with specimen_n for "good" data
                  kill = pmag.grade(rec, accept, 'specimen_dir', data_model=2.5)
                  if len(kill) == 0:  # nothing killed it
                      SpecDirs.append(rec)
          else:  # no criteria
              SpecDirs = Ns[:]  # take them all
  
          for i in range(len(SpecDirs)):
              if SpecDirs[i] == {}:
                  continue
              specimen = SpecDirs[i]['er_specimen_name']
              SpecDirs[i]['er_sample_name'] = self.Data_hierarchy['sample_of_specimen'][specimen]
              SpecDirs[i]['er_site_name'] = self.Data_hierarchy['site_of_specimen'][specimen]
              SpecDirs[i]['er_location_name'] = self.Data_hierarchy['location_of_specimen'][specimen]
  
          # init VGP data
          VGP_Data = {'samples': [], 'sites': [], 'locations': []}
  
          # obtain lat lon data
          SiteNFO = list(self.Data_info['er_sites'].values())
          for val in SiteNFO:
              site = val['er_site_name']
              not_found = []
              # check that lat/lon columns are present and non-null
              if 'site_lat' not in val:
                  not_found.append('latitude')
              elif not val['site_lat'] and val['site_lat'] is not 0:
                  not_found.append('latitude')
              if 'site_lon' not in val:
                  not_found.append('longitude')
              elif not val['site_lon'] and val['site_lon'] is not 0:
                  not_found.append('longitude')
              if not_found == []:
                  continue
              TEXT = "%s not found for site %s. Select 'yes' to enter the values now or 'no' to skip this site and all samples contained in it." % (
                  str(not_found), val['er_site_name'])
              dlg = wx.MessageDialog(
                  self, caption="Missing Data", message=TEXT, style=wx.YES_NO | wx.ICON_QUESTION)
              result = self.show_dlg(dlg)
              dlg.Destroy()
              if result == wx.ID_YES:
                  ui_dialog = demag_dialogs.user_input(self, ['Latitude', 'Longitude'], parse_funcs=[
                                                       float, float], heading="Missing Latitude or Longitude data for site: %s" % val['er_site_name'])
                  self.show_dlg(ui_dialog)
                  ui_data = ui_dialog.get_values()
                  if ui_data[0]:
                      self.Data_info['er_sites'][site]['site_lat'] = ui_data[1]['Latitude']
                      self.Data_info['er_sites'][site]['site_lon'] = ui_data[1]['Longitude']
                      val['site_lat'] = ui_data[1]['Latitude']
                      val['site_lon'] = ui_data[1]['Longitude']
  
          # calculate sample vgps
          for samp in self.samples:
              SampDir = pmag.get_dictitem(SpecDirs, 'er_sample_name', samp, 'T')
              if len(SampDir) <= 0:
                  continue
              for comp in self.all_fits_list:
                  CompDir = pmag.get_dictitem(
                      SampDir, 'specimen_comp_name', comp, 'T')
                  if len(CompDir) <= 0:
                      continue  # no data for comp
                  samp_mean = pmag.lnpbykey(CompDir, 'sample', 'specimen')
                  site = pmag.get_dictitem(
                      SiteNFO, 'er_site_name', CompDir[0]['er_site_name'], 'T')
                  dec = float(samp_mean['sample_dec'])
                  inc = float(samp_mean['sample_inc'])
                  if 'sample_alpha95' in samp_mean and samp_mean['sample_alpha95'] != "":
                      a95 = float(samp_mean['sample_alpha95'])
                  else:
                      a95 = 180.
                  try:
                      lat = float(site[0]['site_lat'])
                      lon = float(site[0]['site_lon'])
                  except (KeyError, IndexError, ValueError, TypeError) as e:
                      continue
                  plong, plat, dp, dm = pmag.dia_vgp(dec, inc, a95, lat, lon)
                  PmagResRec = {}
                  PmagResRec['name'] = samp
                  PmagResRec['comp_name'] = comp
                  PmagResRec['n'] = len(CompDir)
                  PmagResRec['color'] = [e.color for sl in list(self.pmag_results_data['specimens'].values(
                  )) for e in sl if e not in self.bad_fits and e.name == comp][0]
                  PmagResRec['vgp_lon'] = plong
                  PmagResRec['vgp_lat'] = plat
                  PmagResRec['lon'] = lon
                  PmagResRec['lat'] = lat
                  PmagResRec['vgp_dp'] = dp
                  PmagResRec['vgp_dm'] = dm
                  VGP_Data['samples'].append(PmagResRec)
  
          for site in self.sites:
              SiteDir = pmag.get_dictitem(SpecDirs, 'er_site_name', site, 'T')
              erSite = pmag.get_dictitem(SiteNFO, 'er_site_name', site, 'T')
              for comp in self.all_fits_list:
                  siteD = pmag.get_dictitem(
                      SiteDir, 'specimen_comp_name', comp, 'T')
                  if len(siteD) <= 0:
                      print(("no data for comp %s and site %s" % (comp, site)))
                      continue
                  SiteData = pmag.lnpbykey(siteD, 'site', 'specimen')
                  dec = float(SiteData['site_dec'])
                  inc = float(SiteData['site_inc'])
                  if 'site_alpha95' in SiteData and SiteData['site_alpha95'] != "":
                      a95 = float(SiteData['site_alpha95'])
                  else:
                      a95 = 180.
                  try:
                      lat = float(erSite[0]['site_lat'])
                      lon = float(erSite[0]['site_lon'])
                  except (KeyError, IndexError, TypeError) as e:
                      continue
                  plong, plat, dp, dm = pmag.dia_vgp(dec, inc, a95, lat, lon)
                  SiteData['name'] = site
                  SiteData['comp_name'] = comp
                  SiteData['n'] = len(siteD)
                  SiteData['vgp_lon'] = plong
                  SiteData['vgp_lat'] = plat
                  SiteData['lon'] = lon
                  SiteData['lat'] = lat
                  SiteData['vgp_dp'] = dp
                  SiteData['vgp_dm'] = dm
                  SiteData['color'] = [e.color for sl in list(self.pmag_results_data['specimens'].values(
                  )) for e in sl if e not in self.bad_fits and e.name == comp][0]
                  VGP_Data['sites'].append(SiteData)
  
          for loc in self.locations:
              LocDir = pmag.get_dictitem(SpecDirs, 'er_location_name', loc, 'T')
              for comp in self.all_fits_list:
                  LocCompData = pmag.get_dictitem(
                      LocDir, 'specimen_comp_name', comp, 'T')
                  if len(LocCompData) < 3:
                      print(("insufficient data for comp %s" % comp))
                      continue
                  precs = []
                  for rec in LocCompData:
                      prec = {'dec': rec['specimen_dec'], 'inc': rec['specimen_inc'],
                              'name': rec['er_site_name'], 'loc': rec['er_location_name']}
                      prec = {k: v if v != None else '' for k,
                              v in list(prec.items())}
                      precs.append(prec)
                  # you need at least 3 records to get anything back from pmag.fisher_by_pol
                  polpars = pmag.fisher_by_pol(precs)
                  # hunt through all the modes (normal=A, reverse=B, all=ALL)
                  for mode in list(polpars.keys()):
                      PolRes = {}
                      PolRes['name'] = polpars[mode]['locs']
                      PolRes["comp_name"] = comp+':'+mode
                      PolRes["dec"] = '%7.1f' % (polpars[mode]['dec'])
                      PolRes["inc"] = '%7.1f' % (polpars[mode]['inc'])
                      PolRes["n"] = '%i' % (polpars[mode]['n'])
                      PolRes["r"] = '%5.4f' % (polpars[mode]['r'])
                      PolRes["k"] = '%6.0f' % (polpars[mode]['k'])
                      PolRes['a95'] = '%7.1f' % (polpars[mode]['alpha95'])
                      dec, inc, a95 = PolRes["dec"], PolRes["inc"], PolRes["a95"]
                      lat, lon, loc_data = "", "", self.Data_info['er_locations']
                      if loc in loc_data and 'location_begin_lat' in loc_data[loc]:
                          lat = loc_data[loc]['location_begin_lat']
                      elif loc in loc_data and 'location_end_lat' in loc_data[loc]:
                          lat = loc_data[loc]['location_end_lat']
                      if loc in loc_data and 'location_begin_lon' in loc_data[loc]:
                          lon = loc_data[loc]['location_begin_lon']
                      elif loc in loc_data and 'location_end_lon' in loc_data[loc]:
                          lon = loc_data[loc]['location_end_lon']
                      if lat == "" or lon == "" or lat == None or lon == None:
                          ui_dialog = demag_dialogs.user_input(self, ['Latitude', 'Longitude'], parse_funcs=[
                                                               float, float], heading="Missing Latitude or Longitude data for location: %s" % loc)
                          self.show_dlg(ui_dialog)
                          ui_data = ui_dialog.get_values()
                          if ui_data[0]:
                              lat = ui_data[1]['Latitude']
                              lon = ui_data[1]['Longitude']
                              if loc not in loc_data:
                                  loc_data[loc] = {}
                              if len(loc_data) > 0:
                                  loc_data[loc]['location_begin_lat'] = lat
                                  loc_data[loc]['location_begin_lon'] = lon
                          else:
                              continue
                      try:
                          plong, plat, dp, dm = pmag.dia_vgp(
                              *list(map(float, [dec, inc, a95, lat, lon])))
                      except TypeError:
                          print(("Not valid parameters for vgp calculation on location: %s" %
                                 loc, dec, inc, a95, lat, lon))
                          continue
                      PolRes['vgp_lon'] = plong
                      PolRes['vgp_lat'] = plat
                      PolRes['lon'] = lon
                      PolRes['lat'] = lat
                      PolRes['vgp_dp'] = dp
                      PolRes['vgp_dm'] = dm
                      PolRes['color'] = [e.color for sl in list(self.pmag_results_data['specimens'].values(
                      )) for e in sl if e not in self.bad_fits and e.name == comp][0]
                      VGP_Data['locations'].append(PolRes)
  
          return VGP_Data