private SimpleMatchList generateMatches(SimpleMatchSettings settings) {
    JosmTaskMonitor monitor = new JosmTaskMonitor();
    monitor.beginTask("Generating matches");

    // create Features and collections from primitive selections
    Set<OsmPrimitive> allPrimitives = new HashSet<>();
    allPrimitives.addAll(settings.getReferenceSelection());
    allPrimitives.addAll(settings.getSubjectSelection());
    FeatureCollection allFeatures = createFeatureCollection(allPrimitives);
    FeatureCollection refColl = new FeatureDataset(allFeatures.getFeatureSchema());
    FeatureCollection subColl = new FeatureDataset(allFeatures.getFeatureSchema());
    for (Feature f : allFeatures.getFeatures()) {
      OsmFeature osmFeature = (OsmFeature) f;
      if (settings.getReferenceSelection().contains(osmFeature.getPrimitive()))
        refColl.add(osmFeature);
      if (settings.getSubjectSelection().contains(osmFeature.getPrimitive()))
        subColl.add(osmFeature);
    }

    // TODO: pass to MatchFinderPanel to use as hint/default for DistanceMatchers
    // get maximum possible distance so scores can be scaled (FIXME: not quite accurate)
    //        Envelope envelope = refColl.getEnvelope();
    //        envelope.expandToInclude(subColl.getEnvelope());
    //        double maxDistance = Point2D.distance(
    //            envelope.getMinX(),
    //            envelope.getMinY(),
    //            envelope.getMaxX(),
    //            envelope.getMaxY());

    // build matcher
    FCMatchFinder finder = settings.getMatchFinder();

    // FIXME: ignore/filter duplicate objects (i.e. same object in both sets)
    // FIXME: fix match functions to work on point/linestring features as well
    // find matches
    Map<Feature, Matches> map = finder.match(refColl, subColl, monitor);

    monitor.subTask("Finishing match list");

    // convert to simple one-to-one match
    SimpleMatchList list = new SimpleMatchList();
    for (Map.Entry<Feature, Matches> entry : map.entrySet()) {
      OsmFeature target = (OsmFeature) entry.getKey();
      OsmFeature subject = (OsmFeature) entry.getValue().getTopMatch();
      if (target != null && subject != null)
        list.add(
            new SimpleMatch(
                target.getPrimitive(), subject.getPrimitive(), entry.getValue().getTopScore()));
    }

    monitor.finishTask();
    monitor.close();
    return list;
  }
  private void selectAllListSelectedPrimitives() {
    List<OsmPrimitive> refSelected = getSelectedReferencePrimitives();
    List<OsmPrimitive> subSelected = getSelectedSubjectPrimitives();

    // clear current selection and add list-selected primitives, handling both
    // same and different reference/subject layers
    settings.getReferenceDataSet().clearSelection();
    settings.getSubjectDataSet().clearSelection();
    settings.getReferenceDataSet().addSelected(refSelected);
    settings.getSubjectDataSet().addSelected(subSelected);
  }
  private void performMatching() {
    matches = generateMatches(settings);

    // populate unmatched objects
    List<OsmPrimitive> referenceOnly = new ArrayList<>(settings.getReferenceSelection());
    List<OsmPrimitive> subjectOnly = new ArrayList<>(settings.getSubjectSelection());
    for (SimpleMatch match : matches) {
      referenceOnly.remove(match.getReferenceObject());
      subjectOnly.remove(match.getSubjectObject());
    }

    referenceOnlyListModel.clear();
    referenceOnlyListModel.addAll(referenceOnly);
    subjectOnlyListModel.clear();
    subjectOnlyListModel.addAll(subjectOnly);

    updateTabTitles();

    matchTableModel.setMatches(matches);
    matches.addConflationListChangedListener(matchTableModel);
    matches.addConflationListChangedListener(conflateAction);
    matches.addConflationListChangedListener(removeAction);
    matches.addConflationListChangedListener(this);
    settings.getSubjectDataSet().addDataSetListener(this);
    settings.getReferenceDataSet().addDataSetListener(this);
    // add conflation layer
    try {
      if (conflationLayer == null) {
        conflationLayer = new ConflationLayer(matches);
        Main.main.addLayer(conflationLayer);
      }
    } catch (Exception ex) {
      JOptionPane.showMessageDialog(
          Main.parent, ex.toString(), "Error adding conflation layer", JOptionPane.ERROR_MESSAGE);
    }
    //        matches.addConflationListChangedListener(conflationLayer);
  }