docs: def assoc2(self, assets_by_site, assoc_dist, mode, asset_refs):
          """
          Associated a list of assets by site to the site collection used
          to instantiate GeographicObjects.
  
          :param assets_by_sites: a list of lists of assets
          :param assoc_dist: the maximum distance for association
          :param mode: 'strict', 'warn' or 'filter'
          :param asset_ref: ID of the assets are a list of strings
          :returns: filtered site collection, filtered assets by site, discarded
          """
          assert mode in 'strict filter', mode
          self.objects.filtered  # self.objects must be a SiteCollection
          asset_dt = numpy.dtype(
              [('asset_ref', vstr), ('lon', F32), ('lat', F32)])
          assets_by_sid = collections.defaultdict(list)
          discarded = []
          for assets in assets_by_site:
              lon, lat = assets[0].location
              obj, distance = self.get_closest(lon, lat)
              if distance <= assoc_dist:
                  # keep the assets, otherwise discard them
                  assets_by_sid[obj['sids']].extend(assets)
              elif mode == 'strict':
                  raise SiteAssociationError(
                      'There is nothing closer than %s km '
                      'to site (%s %s)' % (assoc_dist, lon, lat))
              else:
                  discarded.extend(assets)
          sids = sorted(assets_by_sid)
          if not sids:
              raise SiteAssociationError(
                  'Could not associate any site to any assets within the '
                  'asset_hazard_distance of %s km' % assoc_dist)
          assets_by_site = [
              sorted(assets_by_sid[sid], key=operator.attrgetter('ordinal'))
              for sid in sids]
          data = [(asset_refs[asset.ordinal],) + asset.location
                  for asset in discarded]
          discarded = numpy.array(data, asset_dt)
          return self.objects.filtered(sids), assets_by_site, discarded