docs: def assoc(self, sitecol, assoc_dist, mode):
          """
          :param sitecol: a (filtered) site collection
          :param assoc_dist: the maximum distance for association
          :param mode: 'strict', 'warn' or 'filter'
          :returns: filtered site collection, filtered objects, discarded
          """
          assert mode in 'strict warn filter', mode
          dic = {}
          discarded = []
          for sid, lon, lat in zip(sitecol.sids, sitecol.lons, sitecol.lats):
              obj, distance = self.get_closest(lon, lat)
              if assoc_dist is None:
                  dic[sid] = obj  # associate all
              elif distance <= assoc_dist:
                  dic[sid] = obj  # associate within
              elif mode == 'warn':
                  dic[sid] = obj  # associate outside
                  logging.warning(
                      'The closest vs30 site (%.1f %.1f) is distant more than %d'
                      ' km from site #%d (%.1f %.1f)', obj['lon'], obj['lat'],
                      int(distance), sid, lon, lat)
              elif mode == 'filter':
                  discarded.append(obj)
              elif mode == 'strict':
                  raise SiteAssociationError(
                      'There is nothing closer than %s km '
                      'to site (%s %s)' % (assoc_dist, lon, lat))
          if not dic:
              raise SiteAssociationError(
                  'No sites could be associated within %s km' % assoc_dist)
          return (sitecol.filtered(dic),
                  numpy.array([dic[sid] for sid in sorted(dic)]),
                  discarded)