/* * The Java Conflation Suite (JCS) is a library of Java classes that * can be used to build automated or semi-automated conflation solutions. * * Copyright (C) 2003 Vivid Solutions * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * For more information, contact: * * Vivid Solutions * Suite #1A * 2328 Government Street * Victoria BC V8T 5G5 * Canada * * (250)385-6040 * www.vividsolutions.com */ package com.vividsolutions.jcs.conflate.polygonmatch; import com.vividsolutions.jts.util.Assert; import com.vividsolutions.jump.feature.Feature; import com.vividsolutions.jump.feature.FeatureCollection; import com.vividsolutions.jump.feature.IndexedFeatureCollection; import com.vividsolutions.jump.task.TaskMonitor; import java.util.HashMap; import java.util.Iterator; import java.util.Map; /** * Enforces a one-to-one relationship between target features and * matched candidate features, in the returned result set. The one-to-one * constraint is achieved by discarding all matches except the top match, * for each feature (target and candidate). * <P> * Example: OneToOneFCMatchFinder wraps a FCMatchFinder that returns the * following matches (and scores): T1-C1 (0.8), T2-C1 (0.9), T2-C2 (0.8), * T2-C3 (1.0), T3-C4 (0.5). T1 and T2 are from the target dataset, whereas * C1, C2, and C3 are from the candidate dataset. OneToOneFCMatchFinder filters * out all matches except the top ones, for each feature, leaving: * T2-C3 (1.0), T3-C4 (0.5). */ public class OneToOneFCMatchFinder implements FCMatchFinder { private FCMatchFinder matchFinder; public OneToOneFCMatchFinder(FCMatchFinder matchFinder) { this.matchFinder = matchFinder; } @Override public Map match(FeatureCollection targetFC, FeatureCollection candidateFC, TaskMonitor monitor) { Map targetToMatchesMap = matchFinder.match(targetFC, candidateFC, monitor); monitor.allowCancellationRequests(); monitor.report("Finding best forward matches"); Map bestForwardMatches = filterMatches(targetToMatchesMap, monitor); monitor.report("Finding best reverse matches"); Map bestReverseMatches = filterMatches(invert(targetToMatchesMap, monitor), monitor); monitor.report("Finding common best matches"); //Want matches that are "best" regardless of whether forward or reverse. //This is the only scheme I can think of right now that will satisfy //the case described in the class comment. [Jon Aquino] return commonMatches(bestForwardMatches, invert(bestReverseMatches, monitor), monitor); } private Map commonMatches(Map featureToMatchesMap1, Map featureToMatchesMap2, TaskMonitor monitor) { int featuresProcessed = 0; int totalFeatures = featureToMatchesMap1.size(); Map commonMatches = new HashMap(); for (Iterator i = featureToMatchesMap1.keySet().iterator(); i.hasNext() && ! monitor.isCancelRequested(); ) { Feature key1 = (Feature) i.next(); featuresProcessed++; monitor.report(featuresProcessed, totalFeatures, "features"); if (! featureToMatchesMap2.containsKey(key1)) { continue; } Matches matches1 = (Matches) featureToMatchesMap1.get(key1); Matches matches2 = (Matches) featureToMatchesMap2.get(key1); if (matches1.getTopMatch() == matches2.getTopMatch()) { Assert.isTrue(matches1.getTopScore() == matches2.getTopScore()); commonMatches.put(key1, matches1); } } return commonMatches; } private Map filterMatches(Map featureToMatchesMap, TaskMonitor monitor) { int featuresProcessed = 0; int totalFeatures = featureToMatchesMap.size(); HashMap newMap = new HashMap(); if (featureToMatchesMap.isEmpty()) { return newMap; } for (Iterator i = featureToMatchesMap.keySet().iterator(); i.hasNext() && ! monitor.isCancelRequested(); ) { Feature feature = (Feature) i.next(); featuresProcessed++; monitor.report(featuresProcessed, totalFeatures, "features filtered"); Matches oldMatches = (Matches) featureToMatchesMap.get(feature); if (oldMatches.isEmpty()) { continue; } Matches newMatches = new Matches(oldMatches.getFeatureSchema()); newMatches.add(oldMatches.getTopMatch(), oldMatches.getTopScore()); newMap.put(feature, newMatches); } return newMap; } protected Map invert(Map featureToMatchesMap, TaskMonitor monitor) { int featuresProcessed = 0; int totalFeatures = featureToMatchesMap.size(); HashMap newMap = new HashMap(); if (featureToMatchesMap.isEmpty()) { return newMap; } for (Iterator i = featureToMatchesMap.keySet().iterator(); i.hasNext() && ! monitor.isCancelRequested(); ) { Feature oldKey = (Feature) i.next(); featuresProcessed++; monitor.report(featuresProcessed, totalFeatures, "features inverted"); Matches oldMatches = (Matches) featureToMatchesMap.get(oldKey); for (int j = 0; j < oldMatches.size(); j++) { Feature newKey = (Feature) oldMatches.getFeature(j); Matches newMatches = (Matches) newMap.get(newKey); if (newMatches == null) { newMatches = new Matches(oldKey.getSchema()); } newMatches.add(oldKey, oldMatches.getScore(j)); newMap.put(newKey, newMatches); } } return newMap; } }