Index: /applications/editors/josm/plugins/conflation/build.xml
===================================================================
--- /applications/editors/josm/plugins/conflation/build.xml	(revision 28162)
+++ /applications/editors/josm/plugins/conflation/build.xml	(revision 28163)
@@ -40,5 +40,6 @@
 
     <!-- Needs to be used after import, otherwise ${plugin.dist.dir} is not defined -->
-    <property name="utilsplugin2" location="${plugin.dist.dir}/utilsplugin2.jar"/>   
+    <property name="jtsplugin" location="${plugin.dist.dir}/jts.jar"/>  
+    <property name="utilsplugin2" location="${plugin.dist.dir}/utilsplugin2.jar"/>
 
     <!--
@@ -52,4 +53,5 @@
             <classpath>
                 <pathelement path="${josm}"/>
+                <pathelement location="${jtsplugin}"/>
                 <pathelement location="${utilsplugin2}"/>
             </classpath>
@@ -95,5 +97,5 @@
                 <attribute name="Plugin-Description" value="(Warning: Experimental!) Tool for conflating (merging) data"/>
                 <attribute name="Plugin-Icon" value="images/conflation.png"/>
-                <attribute name="Plugin-Requires" value="utilsplugin2"/>
+                <attribute name="Plugin-Requires" value="jts;utilsplugin2"/>
                 <attribute name="Plugin-Link" value="http://wiki.openstreetmap.org/wiki/JOSM/Plugins/Conflation"/>
                 <attribute name="Plugin-Mainversion" value="${plugin.main.version}"/>
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/algorithm/EuclideanDistanceToPoint.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/algorithm/EuclideanDistanceToPoint.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/algorithm/EuclideanDistanceToPoint.java	(revision 28163)
@@ -0,0 +1,94 @@
+
+
+/*
+ * The JCS 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.algorithm;
+
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * Computes the Euclidean distance (L2 metric) from a Point to a Geometry.
+ * Also computes two points which are separated by the distance.
+ */
+public class EuclideanDistanceToPoint {
+
+  // used for point-line distance calculation
+  private static LineSegment tempSegment = new LineSegment();
+
+  public EuclideanDistanceToPoint() {
+  }
+
+  public static void computeDistance(Geometry geom, Coordinate pt, PointPairDistance ptDist)
+  {
+    if (geom instanceof LineString) {
+      computeDistance((LineString) geom, pt, ptDist);
+    }
+    else if (geom instanceof Polygon) {
+      computeDistance((Polygon) geom, pt, ptDist);
+    }
+    else if (geom instanceof GeometryCollection) {
+      GeometryCollection gc = (GeometryCollection) geom;
+      for (int i = 0; i < gc.getNumGeometries(); i++) {
+        Geometry g = gc.getGeometryN(i);
+        computeDistance(g, pt, ptDist);
+      }
+    }
+    else { // assume geom is Point
+      ptDist.setMinimum(geom.getCoordinate(), pt);
+    }
+  }
+  public static void computeDistance(LineString line, Coordinate pt, PointPairDistance ptDist)
+  {
+    Coordinate[] coords = line.getCoordinates();
+    for (int i = 0; i < coords.length - 1; i++) {
+      tempSegment.setCoordinates(coords[i], coords[i + 1]);
+      // this is somewhat inefficient - could do better
+      Coordinate closestPt = tempSegment.closestPoint(pt);
+      ptDist.setMinimum(closestPt, pt);
+    }
+  }
+
+  public static void computeDistance(LineSegment segment, Coordinate pt, PointPairDistance ptDist)
+  {
+    Coordinate closestPt = segment.closestPoint(pt);
+    ptDist.setMinimum(closestPt, pt);
+  }
+
+  public static void computeDistance(Polygon poly, Coordinate pt, PointPairDistance ptDist)
+  {
+    computeDistance(poly.getExteriorRing(), pt, ptDist);
+    for (int i = 0; i < poly.getNumInteriorRing(); i++) {
+      computeDistance(poly.getInteriorRingN(i), pt, ptDist);
+    }
+  }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/algorithm/PointPairDistance.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/algorithm/PointPairDistance.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/algorithm/PointPairDistance.java	(revision 28163)
@@ -0,0 +1,115 @@
+
+
+/*
+ * The JCS 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.algorithm;
+
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * Contains a pair of points and the distance between them.
+ * Provides methods to update with a new point pair with
+ * either maximum or minimum distance.
+ */
+public class PointPairDistance {
+
+  private Coordinate[] pt = { new Coordinate(), new Coordinate() };
+  private double distance = 0.0;
+  private boolean isNull = true;
+
+  public PointPairDistance()
+  {
+  }
+
+  public void initialize() { isNull = true; }
+
+  public void initialize(Coordinate p0, Coordinate p1)
+  {
+    pt[0].setCoordinate(p0);
+    pt[1].setCoordinate(p1);
+    distance = p0.distance(p1);
+    isNull = false;
+  }
+
+  /**
+   * Initializes the points, avoiding recomputing the distance.
+   * @param p0
+   * @param p1
+   * @param distance the distance between p0 and p1
+   */
+  private void initialize(Coordinate p0, Coordinate p1, double distance)
+  {
+    pt[0].setCoordinate(p0);
+    pt[1].setCoordinate(p1);
+    this.distance = distance;
+    isNull = false;
+  }
+
+  public double getDistance() { return distance; }
+
+  public Coordinate[] getCoordinates() { return pt; }
+
+  public Coordinate getCoordinate(int i) { return pt[i]; }
+
+  public void setMaximum(PointPairDistance ptDist)
+  {
+    setMaximum(ptDist.pt[0], ptDist.pt[1]);
+  }
+
+  public void setMaximum(Coordinate p0, Coordinate p1)
+  {
+    if (isNull) {
+      initialize(p0, p1);
+      return;
+    }
+    double dist = p0.distance(p1);
+    if (dist > distance)
+      initialize(p0, p1, dist);
+  }
+
+  public void setMinimum(PointPairDistance ptDist)
+  {
+    setMinimum(ptDist.pt[0], ptDist.pt[1]);
+  }
+
+  public void setMinimum(Coordinate p0, Coordinate p1)
+  {
+    if (isNull) {
+      initialize(p0, p1);
+      return;
+    }
+    double dist = p0.distance(p1);
+    if (dist < distance)
+      initialize(p0, p1, dist);
+  }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/algorithm/VertexHausdorffDistance.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/algorithm/VertexHausdorffDistance.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/algorithm/VertexHausdorffDistance.java	(revision 28163)
@@ -0,0 +1,129 @@
+
+
+/*
+ * The JCS 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.algorithm;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.CoordinateFilter;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.LineSegment;
+/**
+ * Implements algorithm for computing a distance metric
+ * which can be thought of as the "Maximum Vertex Distance".
+ * This is the Hausdorff distance restricted to vertices for
+ * one of the geometries.
+ * Also computes two points of the Geometries which are separated by the computed distance.
+ * <p>
+ * <b>NOTE: This algorithm does NOT compute the full Hausdorff distance correctly, but
+ * an approximation that is correct for a large subset of useful cases.
+ * One important part of this subset is Linestrings that are roughly parallel to each other,
+ * and roughly equal in length.
+ * </b>
+ */
+public class VertexHausdorffDistance {
+
+  PointPairDistance ptDist = new PointPairDistance();
+
+  public VertexHausdorffDistance(Geometry g0, Geometry g1)
+  {
+    compute(g0, g1);
+  }
+
+  public VertexHausdorffDistance(LineSegment seg0, LineSegment seg1)
+  {
+    compute(seg0, seg1);
+  }
+
+  public double distance() { return ptDist.getDistance(); }
+
+  public Coordinate[] getCoordinates() { return ptDist.getCoordinates(); }
+
+  private void compute(LineSegment seg0, LineSegment seg1)
+  {
+    computeMaxPointDistance(seg0, seg1, ptDist);
+    computeMaxPointDistance(seg1, seg0, ptDist);
+  }
+
+  /**
+   * Computes the maximum oriented distance between two line segments,
+   * as well as the point pair separated by that distance.
+   *
+   * @param seg0 the line segment containing the furthest point
+   * @param seg1 the line segment containing the closest point
+   * @param ptDist the point pair and distance to be updated
+   */
+  private void computeMaxPointDistance(LineSegment seg0, LineSegment seg1, PointPairDistance ptDist)
+  {
+    Coordinate closestPt0 = seg0.closestPoint(seg1.p0);
+    ptDist.setMaximum(closestPt0, seg1.p0);
+    Coordinate closestPt1 = seg0.closestPoint(seg1.p1);
+    ptDist.setMaximum(closestPt1, seg1.p1);
+  }
+
+  private void compute(Geometry g0, Geometry g1)
+  {
+    computeMaxPointDistance(g0, g1, ptDist);
+    computeMaxPointDistance(g1, g0, ptDist);
+  }
+
+  private void computeMaxPointDistance(Geometry pointGeom, Geometry geom, PointPairDistance ptDist)
+  {
+    MaxPointDistanceFilter distFilter = new MaxPointDistanceFilter(geom);
+    pointGeom.apply(distFilter);
+    ptDist.setMaximum(distFilter.getMaxPointDistance());
+  }
+
+  public static class MaxPointDistanceFilter
+      implements CoordinateFilter
+  {
+    private PointPairDistance maxPtDist = new PointPairDistance();
+    private PointPairDistance minPtDist = new PointPairDistance();
+    private Geometry geom;
+
+    public MaxPointDistanceFilter(Geometry geom)
+    {
+      this.geom = geom;
+    }
+
+        @Override
+    public void filter(Coordinate pt)
+    {
+      minPtDist.initialize();
+      EuclideanDistanceToPoint.computeDistance(geom, pt, minPtDist);
+      maxPtDist.setMaximum(minPtDist);
+    }
+
+    public PointPairDistance getMaxPointDistance() { return maxPtDist; }
+  }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/AbstractDistanceMatcher.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/AbstractDistanceMatcher.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/AbstractDistanceMatcher.java	(revision 28163)
@@ -0,0 +1,42 @@
+package com.vividsolutions.jcs.conflate.polygonmatch;
+
+import com.vividsolutions.jts.geom.Envelope;
+import com.vividsolutions.jts.geom.Geometry;
+import java.awt.geom.Point2D;
+
+public abstract class AbstractDistanceMatcher extends IndependentCandidateMatcher {
+    protected double maxDistance = 0;
+    
+    @Override
+    public double match(Geometry target, Geometry candidate) {
+        double distance = distance(target, candidate);
+        if (maxDistance > 0) {
+            return 1 - (distance / maxDistance); // FIXME: allow negative scores?
+        } else {
+            return 1
+                    - (distance
+                    / combinedEnvelopeDiagonalDistance(target, candidate));
+        }
+    }
+
+    protected abstract double distance(Geometry target, Geometry candidate);
+
+    private double combinedEnvelopeDiagonalDistance(
+        Geometry target,
+        Geometry candidate) {
+        Envelope envelope = new Envelope(target.getEnvelopeInternal());
+        envelope.expandToInclude(candidate.getEnvelopeInternal());
+        return Point2D.distance(
+            envelope.getMinX(),
+            envelope.getMinY(),
+            envelope.getMaxX(),
+            envelope.getMaxY());
+    }
+
+    public void setMaxDistance(double maxDistance) {
+        if (maxDistance < 0)
+            this.maxDistance = 0;
+        else
+            this.maxDistance = maxDistance;
+    }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/AngleHistogramMatcher.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/AngleHistogramMatcher.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/AngleHistogramMatcher.java	(revision 28163)
@@ -0,0 +1,133 @@
+/*
+ * The JCS 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.geom.Coordinate;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.io.WKTReader;
+import com.vividsolutions.jts.util.Assert;
+import com.vividsolutions.jcs.geom.Angle;
+import com.vividsolutions.jump.util.CoordinateArrays;
+import java.util.Iterator;
+import java.util.List;
+/**
+ * Matches geometries by comparing their "angle histograms". An angle histogram
+ * is a histogram of segment angles (with the positive x-axis), weighted by
+ * segment length. Angles range from -pi to +pi.
+ */
+public class AngleHistogramMatcher extends IndependentCandidateMatcher {
+    /**
+     * Creates an AngleHistogramMatcher with 0 bins. Be sure to call #setBinCount.
+     */
+    public AngleHistogramMatcher() {
+        this(18);
+    }
+    /**
+     * Creates an AngleHistogramMatcher with the given number of bins.
+     * @param binCount the number of bins into which -pi to +pi should be split
+     */
+    public AngleHistogramMatcher(int binCount) {
+        this.binCount = binCount;
+    }
+    private int binCount;
+    /**
+     * Finds the symmetric difference between the angle histograms of the two
+     * features.
+     * @param target the feature to match
+     * @param candidate the feature to compare with the target
+     * @return a linear function of the symmetric difference: 1 if
+     * the histograms perfectly overlap; 0 if they do not overlap at all.
+     */
+    @Override
+    public double match(Geometry target, Geometry candidate) {
+        Histogram targetHist = angleHistogram(target, binCount);
+        Histogram candidateHist = angleHistogram(candidate, binCount);
+        return MatcherUtil.toScoreFromSymDiffArea(
+            targetHist.getTotalScore(),
+            candidateHist.getTotalScore(),
+            targetHist.symDiff(candidateHist));
+    }
+    /**
+     * Creates an angle histogram for the given Geometry. The sum of the histogram
+     * scores will equal the sum of the Geometry's segment lengths.
+     * @param g the Geometry to analyze
+     * @param binCount the number of bins into which -pi to +pi should be split
+     * @return a histogram of g's segment angles (with the positive x-axis),
+     * weighted by segment length
+     */
+    protected Histogram angleHistogram(Geometry g, int binCount) {
+        Geometry clone = (Geometry) g.clone();
+        //#normalize makes linestrings and polygons use a standard orientation.
+        //[Jon Aquino]
+        clone.normalize();
+        //In #toCoordinateArrays call, set orientPolygons=false because
+        //#normalize takes care of orienting the rings. [Jon Aquino]
+        List lineStrings = CoordinateArrays.toCoordinateArrays(clone, false);
+        Histogram h = new Histogram(binCount);
+        for (Iterator i = lineStrings.iterator(); i.hasNext();) {
+            Coordinate[] lineString = (Coordinate[]) i.next();
+            h.add(angleHistogram(lineString, binCount));
+        }
+        return h;
+    }
+    private Histogram angleHistogram(Coordinate[] lineString, int binCount) {
+        Histogram h = new Histogram(binCount);
+        for (int i = 1; i < lineString.length; i++) { //start 1
+            h.addToBinScore(
+                bin(Angle.angle(lineString[i - 1], lineString[i]), binCount),
+                lineString[i - 1].distance(lineString[i]));
+        }
+        return h;
+    }
+    /**
+     * Returns the histogram bin that the angle should go into.
+     * @param angle in radians
+     * @param binCount the number of bins into which -pi to pi is divided
+     * @return the index of the bin that the angle should go into (0, 1, 2, ...)
+     */
+    protected int bin(double angle, int binCount) {
+        Assert.isTrue(angle >= -Math.PI);
+        Assert.isTrue(angle <= Math.PI);
+        double binSize = 2 * Math.PI / binCount;
+        int bin = (int) Math.floor((angle + Math.PI) / binSize);
+        return Math.min(bin, binCount - 1); //360 case
+    }
+    public static void main(String[] args) throws Exception {
+        String s =
+            "POLYGON (( 138 314, 114 307, 89 293, 75 262, 71 219, 71 188, 75 146, 82 125, 110 122, 152 149, 194 184, 222 237, 225 275, 201 300, 159 317, 138 314 ))";
+        //String s = "POLYGON((0 0, 10 0, 10 100, 0 100, 0 0))";
+        Histogram h =
+            new AngleHistogramMatcher().angleHistogram(new WKTReader().read(s), 18);
+        for (int i = 0; i < h.getBinCount(); i++) {
+            System.out.println(h.getBinScore(i));
+        }
+    }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/AreaFilterFCMatchFinder.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/AreaFilterFCMatchFinder.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/AreaFilterFCMatchFinder.java	(revision 28163)
@@ -0,0 +1,121 @@
+
+
+/*
+ * The JCS 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.*;
+import com.vividsolutions.jump.task.TaskMonitor;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Speeds up processing by ignoring target and candidate features with areas
+ * greater than a specified maximum or less than a specified minimum.
+ */
+public class AreaFilterFCMatchFinder implements FCMatchFinder {
+
+    private FCMatchFinder matchFinder;
+    private double minArea;
+    private double maxArea;
+
+    public AreaFilterFCMatchFinder(
+        double minArea,
+        double maxArea,
+        FCMatchFinder matchFinder) {
+        Assert.isTrue(minArea < maxArea);
+        this.minArea = minArea;
+        this.maxArea = maxArea;
+        this.matchFinder = matchFinder;
+    }
+
+    @Override
+    public Map match(
+        FeatureCollection targetFC,
+        FeatureCollection candidateFC,
+        TaskMonitor monitor) {
+        monitor.allowCancellationRequests();
+        Map filteredTargetToMatchesMap =
+            matchFinder.match(
+                filter(targetFC, "targets", monitor),
+                filter(candidateFC, "candidates", monitor),
+                monitor);
+        //      Put back the targets that were filtered out (albeit with no matches). [Jon Aquino]
+        Map targetToMatchesMap =
+            blankTargetToMatchesMap(
+                targetFC.getFeatures(),
+                candidateFC.getFeatureSchema());
+        targetToMatchesMap.putAll(filteredTargetToMatchesMap);
+        return targetToMatchesMap;
+    }
+
+    private IndexedFeatureCollection filter(
+        FeatureCollection fc,
+        String name,
+        TaskMonitor monitor) {
+        monitor.report("Filtering " + name + " by area");
+        int featuresProcessed = 0;
+        int totalFeatures = fc.size();
+        FeatureDataset filteredFC = new FeatureDataset(fc.getFeatureSchema());
+        for (Iterator i = fc.iterator(); i.hasNext() && !monitor.isCancelRequested();) {
+            Feature feature = (Feature) i.next();
+            featuresProcessed++;
+            monitor.report(featuresProcessed, totalFeatures, "features");
+            if (!satisfiesAreaCriterion(feature)) {
+                continue;
+            }
+            filteredFC.add(feature);
+        }
+        return new IndexedFeatureCollection(filteredFC);
+    }
+
+    private boolean satisfiesAreaCriterion(Feature feature) {
+        double area = feature.getGeometry().getArea();
+        return minArea <= area && area <= maxArea;
+    }
+
+    public static Map blankTargetToMatchesMap(
+        Collection targets,
+        FeatureSchema matchesSchema) {
+        Map blankTargetToMatchesMap = new HashMap();
+        for (Iterator i = targets.iterator(); i.hasNext();) {
+            Feature target = (Feature) i.next();
+            blankTargetToMatchesMap.put(target, new Matches(matchesSchema));
+        }
+        return blankTargetToMatchesMap;
+    }
+
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/BasicFCMatchFinder.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/BasicFCMatchFinder.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/BasicFCMatchFinder.java	(revision 28163)
@@ -0,0 +1,79 @@
+
+
+/*
+ * The JCS 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.jump.feature.Feature;
+import com.vividsolutions.jump.feature.FeatureCollection;
+import com.vividsolutions.jump.task.TaskMonitor;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Applies a FeatureMatcher to each item in a FeatureCollection
+ */
+public class BasicFCMatchFinder implements FCMatchFinder {
+
+    /**
+     * Creates a FeatureCollectionMatcher that uses the given FeatureMatcher.
+     * @param matcher typically a composite of other FeatureMatchers
+     */
+    public BasicFCMatchFinder(FeatureMatcher matcher) {
+        this.matcher = matcher;
+    }
+
+    private FeatureMatcher matcher;
+
+    @Override
+    public Map match(
+        FeatureCollection targetFC,
+        FeatureCollection candidateFC,
+        TaskMonitor monitor) {
+        monitor.allowCancellationRequests();
+        monitor.report("Finding matches");
+        TreeMap map = new TreeMap();
+        int featuresProcessed = 0;
+        int totalFeatures = targetFC.size();
+        for (Iterator i = targetFC.iterator();
+            i.hasNext() && !monitor.isCancelRequested();
+            ) {
+            Feature subjectFeature = (Feature) i.next();
+            featuresProcessed++;
+            monitor.report(featuresProcessed, totalFeatures, "features");
+            map.put(subjectFeature, matcher.match(subjectFeature, candidateFC));
+        }
+        return map;
+    }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/CentroidAligner.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/CentroidAligner.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/CentroidAligner.java	(revision 28163)
@@ -0,0 +1,18 @@
+package com.vividsolutions.jcs.conflate.polygonmatch;
+
+import com.vividsolutions.jts.geom.Geometry;
+
+public class CentroidAligner extends IndependentCandidateMatcher {
+    private IndependentCandidateMatcher matcher;
+    public CentroidAligner(IndependentCandidateMatcher matcher) {
+        this.matcher = matcher;
+    }
+    public double match(Geometry target, Geometry candidate) {
+        return matcher.match(align(target), align(candidate));
+    }
+    private Geometry align(Geometry original) {
+        Geometry aligned = (Geometry) original.clone();
+        MatcherUtil.align(aligned, aligned.getCentroid().getCoordinate());
+        return aligned;
+    }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/CentroidDistanceMatcher.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/CentroidDistanceMatcher.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/CentroidDistanceMatcher.java	(revision 28163)
@@ -0,0 +1,10 @@
+package com.vividsolutions.jcs.conflate.polygonmatch;
+
+import com.vividsolutions.jts.geom.Geometry;
+
+public class CentroidDistanceMatcher extends AbstractDistanceMatcher {
+    protected double distance(Geometry target, Geometry candidate) {
+        return target.getCentroid().distance(
+            candidate.getCentroid());
+    }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/ChainMatcher.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/ChainMatcher.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/ChainMatcher.java	(revision 28163)
@@ -0,0 +1,82 @@
+
+
+/*
+ * The JCS 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.jump.feature.Feature;
+import com.vividsolutions.jump.feature.FeatureCollection;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+
+/**
+ * Composes several FeatureMatchers into one. Candidate features are whittled
+ * down by applying each FeatureMatcher.
+ * <P>
+ * Note: Only the last FeatureMatcher's scores are preserved; the other scores
+ * are lost. However, this behaviour should be acceptable for most situations
+ * Typically you use the Chained Matcher to do some initial filtering before
+ * the "real" matching. The scores from this initial filtering are usually
+ * ignored (they're usually just 1 or 0, as in the case of WindowFilter).
+ */
+public class ChainMatcher implements FeatureMatcher {
+
+  /**
+   * Creates a ChainMatcher composed of the given matchers.
+   * @param matchers the matchers to link together
+   */
+  public ChainMatcher(FeatureMatcher[] matchers) {
+        this.matchers.addAll(Arrays.asList(matchers));
+  }
+
+  private ArrayList matchers = new ArrayList();
+
+  /**
+   * Applies the FeatureMatchers, in sequence, to the list of candidates.
+   * @param target the feature to match
+   * @param candidates the features to search for matches
+   * @return the candidates surviving all the FeatureMatchers. The scores are
+   * those returned by the last FeatureMatcher.
+   */
+    @Override
+  public Matches match(Feature target, FeatureCollection candidates) {
+    Matches survivors = new Matches(
+        candidates.getFeatureSchema(), candidates.getFeatures());
+    for (Iterator i = matchers.iterator(); i.hasNext(); ) {
+      FeatureMatcher matcher = (FeatureMatcher) i.next();
+      survivors = matcher.match(target, survivors);
+    }
+    return survivors;
+  }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/CombinatorialFCMatchFinder.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/CombinatorialFCMatchFinder.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/CombinatorialFCMatchFinder.java	(revision 28163)
@@ -0,0 +1,306 @@
+
+
+/*
+ * 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.geom.Coordinate;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.util.Assert;
+import com.vividsolutions.jump.feature.*;
+import com.vividsolutions.jump.task.TaskMonitor;
+import com.vividsolutions.jump.util.CollectionMap;
+import com.vividsolutions.jump.util.CollectionUtil;
+import com.vividsolutions.jump.util.CoordinateArrays;
+import java.util.*;
+
+/**
+ *  An FCMatchFinder wrapper that also treats pairs of adjacent target features
+ *  as themselves target features. Such pairs are formed into composite target
+ *  features. These composites are temporary -- before the results are returned,
+ *  each composite is split into its constituent features. <P>
+ *
+ *  The result returned is a one-to-one mapping of target feature to matched
+ *  candidate feature; the one-to-one mapping is achieved by discarding all
+ *  matches except for those with the highest scores, for each feature (target
+ *  and matched candidate). <P>
+ *
+ *  Note on composites: if a composite's top score is higher than the top score
+ *  of each of its constituents, the composite match is retained and constituent
+ *  matches are discarded; otherwise, the composite match is discarded and
+ *  constituent matches are retained.
+ */
+public class CombinatorialFCMatchFinder implements FCMatchFinder {
+
+  private FCMatchFinder matchFinder;
+
+  private int maxCompositeSize;
+
+  /**
+   *@param  maxCompositeSize  the maximum number of adjacent target features to
+   *      try combining
+   *@param  matchFinder       the FCMatchFinder to wrap
+   */
+  public CombinatorialFCMatchFinder(int maxCompositeSize, FCMatchFinder matchFinder) {
+    this.maxCompositeSize = maxCompositeSize;
+    this.matchFinder = new OneToOneFCMatchFinder(matchFinder);
+  }
+
+  public Map match(IndexedFeatureCollection targetFC, IndexedFeatureCollection candidateFC,
+      TaskMonitor monitor) {
+    monitor.allowCancellationRequests();
+    FeatureCollection compositeTargetFC = new FeatureDataset(targetFC.getFeatureSchema());
+    CollectionMap constituentToCompositesMap = new CollectionMap();
+    createComposites(targetFC, constituentToCompositesMap, compositeTargetFC, monitor);
+    Map targetFeatureToMatchesMap = matchFinder.match(
+        new IndexedFeatureCollection(compositeTargetFC),
+        candidateFC, monitor);
+    deleteInferiorComposites(targetFeatureToMatchesMap, constituentToCompositesMap, monitor);
+    return splitComposites(targetFeatureToMatchesMap, monitor);
+  }
+
+  protected void createComposites(FeatureCollection fc, CollectionMap constituentToCompositesMap, FeatureCollection compositeFC, TaskMonitor monitor) {
+    Assert.isTrue(constituentToCompositesMap.isEmpty());
+    Assert.isTrue(compositeFC.isEmpty());
+    Set composites = createCompositeSet(fc, monitor);
+    add(composites, constituentToCompositesMap, monitor);
+    add(composites, compositeFC, monitor);
+  }
+
+  /**
+   *  Removes from the compositeToMatchesMap any composites sharing constituents
+   *  with other composites but having a lower match score than any of the other
+   *  composites.
+   */
+  protected void deleteInferiorComposites(Map compositeToMatchesMap, CollectionMap constituentToCompositesMap, TaskMonitor monitor) {
+    monitor.report("Discarding inferior composites");
+    int featuresProcessed = 0;
+    int totalFeatures = constituentToCompositesMap.size();
+    for (Iterator i = constituentToCompositesMap.keySet().iterator(); i.hasNext() && ! monitor.isCancelRequested(); ) {
+      Feature constituent = (Feature) i.next();
+      featuresProcessed++;
+      monitor.report(featuresProcessed, totalFeatures, "features");
+      Collection composites = constituentToCompositesMap.getItems(constituent);
+      Assert.isTrue(!composites.isEmpty());
+      double bestScore = -1;
+      CompositeFeature bestComposite = null;
+      Matches bestMatches = null;
+      for (Iterator j = composites.iterator(); j.hasNext(); ) {
+        CompositeFeature composite = (CompositeFeature) j.next();
+        Matches matches = (Matches) compositeToMatchesMap.get(composite);
+        if (matches == null) {
+          continue;
+        }
+        if (matches.getTopScore() > bestScore) {
+          bestScore = matches.getTopScore();
+          bestComposite = composite;
+          bestMatches = matches;
+        }
+      }
+      CollectionUtil.removeKeys(composites, compositeToMatchesMap);
+      if (bestMatches == null) {
+        continue;
+      }
+      compositeToMatchesMap.put(bestComposite, bestMatches);
+    }
+  }
+
+  protected List featuresWithCommonEdge(Feature feature, FeatureCollection fc) {
+    ArrayList featuresWithCommonEdge = new ArrayList();
+    List candidates = fc.query(feature.getGeometry().getEnvelopeInternal());
+    for (Iterator i = candidates.iterator(); i.hasNext(); ) {
+      Feature candidate = (Feature) i.next();
+      if (feature == candidate || shareEdge(feature.getGeometry(), candidate.getGeometry())) {
+        featuresWithCommonEdge.add(candidate);
+      }
+    }
+    return featuresWithCommonEdge;
+  }
+
+  protected boolean shareEdge(Geometry a, Geometry b) {
+    Set aEdges = edges(a);
+    Set bEdges = edges(b);
+    for (Iterator i = bEdges.iterator(); i.hasNext(); ) {
+      Edge bEdge = (Edge) i.next();
+      if (aEdges.contains(bEdge)) { return true; }
+    }
+    return false;
+  }
+
+    @Override
+    public Map match(FeatureCollection targetFC, FeatureCollection candidateFC, TaskMonitor monitor) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+  private static class Edge implements Comparable {
+    private Coordinate p0, p1;
+    public Edge(Coordinate a, Coordinate b) {
+      if (a.compareTo(b) < 1) {
+        p0 = a;
+        p1 = b;
+      }
+      else {
+        p0 = b;
+        p1 = a;
+      }
+    }
+        @Override
+    public int compareTo(Object o) {
+      Edge other = (Edge) o;
+      int result = p0.compareTo(other.p0);
+      if (result != 0) return result;
+      return p1.compareTo(other.p1);
+    }
+  }
+
+  private Set edges(Geometry g) {
+    TreeSet edges = new TreeSet();
+    for (Iterator i = CoordinateArrays.toCoordinateArrays(g, false).iterator(); i.hasNext(); ) {
+      Coordinate[] coordinates = (Coordinate[]) i.next();
+      for (int j = 1; j < coordinates.length; j++) { //1
+        edges.add(new Edge(coordinates[j], coordinates[j-1]));
+      }
+    }
+    return edges;
+  }
+
+  /**
+   *  Splits each composite target into its constituent features.
+   */
+  protected Map splitComposites(Map compositeToMatchesMap, TaskMonitor monitor) {
+    monitor.report("Splitting composites");
+    int compositesProcessed = 0;
+    int totalComposites = compositeToMatchesMap.size();
+    Map newMap = new HashMap();
+    for (Iterator i = compositeToMatchesMap.keySet().iterator(); i.hasNext() && ! monitor.isCancelRequested(); ) {
+      CompositeFeature composite = (CompositeFeature) i.next();
+      compositesProcessed++;
+      monitor.report(compositesProcessed, totalComposites, "composites");
+      Matches matches = (Matches) compositeToMatchesMap.get(composite);
+      //Because we use OneToOneFCMatchFinder, all targets will be associated
+      //with one and only one match.
+      Assert.isTrue(1 == matches.size());
+      for (Iterator j = composite.getFeatures().iterator(); j.hasNext(); ) {
+        Feature constituent = (Feature) j.next();
+        Assert.isTrue(!newMap.containsKey(constituent));
+        Matches matchesCopy = new Matches(matches.getFeatureSchema());
+        matchesCopy.add(matches.getTopMatch(), matches.getTopScore());
+        newMap.put(constituent, matchesCopy);
+      }
+    }
+    return newMap;
+  }
+
+  private Set createCompositeSet(FeatureCollection fc, TaskMonitor monitor) {
+    monitor.report("Creating composites of adjacent features");
+    int featuresProcessed = 0;
+    int totalFeatures = fc.getFeatures().size();
+    //Use a Set to prevent duplicate composites [Jon Aquino]
+    HashSet composites = new HashSet();
+    for (Iterator i = fc.getFeatures().iterator(); i.hasNext() && !monitor.isCancelRequested(); ) {
+      Feature feature = (Feature) i.next();
+      featuresProcessed++;
+      monitor.report(featuresProcessed, totalFeatures, "features");
+      List featuresWithCommonEdge = featuresWithCommonEdge(feature, fc);
+      for (Iterator j = CollectionUtil.combinations(
+          featuresWithCommonEdge, maxCompositeSize, feature).iterator(); j.hasNext() && !monitor.isCancelRequested(); ) {
+        List combination = (List) j.next();
+        composites.add(new CompositeFeature(fc.getFeatureSchema(), combination));
+      }
+    }
+    return composites;
+  }
+
+  private void add(Set composites, CollectionMap constituentToCompositesMap, TaskMonitor monitor) {
+    monitor.report("Creating feature-to-composite map");
+    int compositesProcessed = 0;
+    int totalComposites = composites.size();
+    for (Iterator i = composites.iterator(); i.hasNext() && !monitor.isCancelRequested(); ) {
+      CompositeFeature composite = (CompositeFeature) i.next();
+      compositesProcessed++;
+      monitor.report(compositesProcessed, totalComposites, "composites");
+      for (Iterator j = composite.getFeatures().iterator(); j.hasNext() && !monitor.isCancelRequested(); ) {
+        Feature constituent = (Feature) j.next();
+        constituentToCompositesMap.addItem(constituent, composite);
+      }
+    }
+  }
+
+  public static class CompositeFeature extends BasicFeature {
+    private List features;
+    private int hashCode;
+
+    public CompositeFeature(FeatureSchema schema, List features) {
+      super(schema);
+      this.features = features;
+      Geometry union = ((Feature) features.get(0)).getGeometry();
+      hashCode = ((Feature) features.get(0)).hashCode();
+      for (int i = 1; i < features.size(); i++) {
+        Feature feature = (Feature) features.get(i);
+        union = union.union(feature.getGeometry());
+        hashCode = Math.min(hashCode, feature.hashCode());
+      }
+      setGeometry(union);
+    }
+
+    public List getFeatures() {
+      return features;
+    }
+
+        @Override
+    public boolean equals(Object obj) {
+      Assert.isTrue(obj instanceof CompositeFeature, obj.getClass().toString());
+      CompositeFeature other = (CompositeFeature) obj;
+      if (features.size() != other.features.size()) {
+        return false;
+      }
+      for (Iterator i = features.iterator(); i.hasNext(); ) {
+        Feature myFeature = (Feature) i.next();
+        if (!other.features.contains(myFeature)) {
+          return false;
+        }
+      }
+      return true;
+    }
+
+        @Override
+    public int hashCode() {
+      return hashCode;
+    }
+  }
+
+  private void add(Collection features, FeatureCollection fc, TaskMonitor monitor) {
+    monitor.report("Building feature-collection");
+    fc.addAll(features);
+  }
+
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/CompactnessMatcher.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/CompactnessMatcher.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/CompactnessMatcher.java	(revision 28163)
@@ -0,0 +1,64 @@
+
+
+/*
+ * The JCS 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.geom.Geometry;
+import com.vividsolutions.jts.util.Assert;
+
+/**
+ * Uses (4 x pi x Area) / (Perimeter^2) as a shape characteristic. The
+ * maximum value is 1 (for circles).
+ */
+public class CompactnessMatcher extends IndependentCandidateMatcher {
+
+  public CompactnessMatcher() {
+  }
+
+  /**
+   * @return 1 - the difference between the values of the shape
+   * characteristic, defined above.
+   */
+  public double match(Geometry target, Geometry candidate) {
+    double score = 1 - Math.abs(characteristic(target)
+                              - characteristic(candidate));
+    Assert.isTrue(score >= 0);
+    Assert.isTrue(score <= 1);
+    return score;
+  }
+
+  protected double characteristic(Geometry g) {
+    return 4 * Math.PI * g.getArea() / Math.pow(g.getLength(), 2);
+  }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/DisambiguatingFCMatchFinder.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/DisambiguatingFCMatchFinder.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/DisambiguatingFCMatchFinder.java	(revision 28163)
@@ -0,0 +1,54 @@
+package com.vividsolutions.jcs.conflate.polygonmatch;
+import com.vividsolutions.jump.feature.Feature;
+import com.vividsolutions.jump.feature.FeatureCollection;
+import com.vividsolutions.jump.task.TaskMonitor;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.SortedSet;
+/**
+ * Enforces a one-to-one relationship between target features and
+ * matched candidate features, in the returned result set.
+ * "Aggressive" because 2nd, 3rd, 4th, etc. best
+ * matches are tried if the 1st, 2nd, 3rd, etc. match is "taken" by another 
+ * feature.
+ */
+public class DisambiguatingFCMatchFinder implements FCMatchFinder {
+    private FCMatchFinder matchFinder;
+    public DisambiguatingFCMatchFinder(FCMatchFinder matchFinder) {
+        this.matchFinder = matchFinder;
+    }
+    @Override
+    public Map match(
+        FeatureCollection targetFC,
+        FeatureCollection candidateFC,
+        TaskMonitor monitor) {
+        ArrayList targets = new ArrayList();
+        ArrayList candidates = new ArrayList();
+        ArrayList scores = new ArrayList();
+        SortedSet matchSet = DisambiguationMatch.createDisambiguationMatches(matchFinder.match(targetFC, candidateFC, monitor), monitor);
+        monitor.report("Discarding inferior matches");
+        int j = 0;
+        for (Iterator i = matchSet.iterator(); i.hasNext();) {
+            DisambiguationMatch match = (DisambiguationMatch) i.next();
+            monitor.report(++j, matchSet.size(), "matches");
+            if (targets.contains(match.getTarget()) || candidates.contains(match.getCandidate())) {
+                continue;
+            }
+            targets.add(match.getTarget());
+            candidates.add(match.getCandidate());
+            scores.add(new Double(match.getScore()));
+        }
+        //Re-add filtered-out targets, but with zero-score matches [Jon Aquino]
+        Map targetToMatchesMap =
+            AreaFilterFCMatchFinder.blankTargetToMatchesMap(
+                targetFC.getFeatures(),
+                candidateFC.getFeatureSchema());
+        for (int i = 0; i < targets.size(); i++) {
+            Matches matches = new Matches(candidateFC.getFeatureSchema());
+            matches.add((Feature)candidates.get(i), ((Double)scores.get(i)).doubleValue());
+            targetToMatchesMap.put(targets.get(i), matches);
+        }
+        return targetToMatchesMap;
+    }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/DisambiguationMatch.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/DisambiguationMatch.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/DisambiguationMatch.java	(revision 28163)
@@ -0,0 +1,57 @@
+package com.vividsolutions.jcs.conflate.polygonmatch;
+
+import com.vividsolutions.jts.util.Assert;
+import com.vividsolutions.jump.feature.Feature;
+import com.vividsolutions.jump.task.TaskMonitor;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+class DisambiguationMatch implements Comparable {
+    private Feature target;
+    private Feature candidate;
+    private double score;
+    public double getScore() {
+        return score;
+    }
+
+    public Feature getCandidate() {
+        return candidate;
+    }
+
+    public Feature getTarget() {
+        return target;
+    }
+
+    public DisambiguationMatch(Feature target, Feature candidate, double score) {
+        this.target = target;
+        this.candidate = candidate;
+        this.score = score;
+    }
+    @Override
+    public int compareTo(Object o) {
+        DisambiguationMatch other = (DisambiguationMatch) o;
+        //Highest scores first. [Jon Aquino]
+        if (score > other.score) { return -1; }
+        if (score < other.score) { return 1; }
+        if (target.compareTo(other.target) != 0) { return target.compareTo(other.target); } 
+        if (candidate.compareTo(other.candidate) != 0) { return candidate.compareTo(other.candidate); }
+        Assert.shouldNeverReachHere("Unexpected duplicate match?"); 
+        return -1;
+    }
+    public static SortedSet createDisambiguationMatches(Map targetToMatchesMap, TaskMonitor monitor) {
+        TreeSet set = new TreeSet();
+        monitor.report("Sorting scores");
+        int k = 0;
+        for (Iterator i = targetToMatchesMap.keySet().iterator(); i.hasNext();) {
+            Feature target = (Feature) i.next();
+            Matches matches = (Matches) targetToMatchesMap.get(target);
+            monitor.report(++k, targetToMatchesMap.keySet().size(), "features");
+            for (int j = 0; j < matches.size(); j++) {
+                set.add(new DisambiguationMatch(target, matches.getFeature(j), matches.getScore(j)));
+            }
+        }
+        return set;
+    }        
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/FCMatchFinder.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/FCMatchFinder.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/FCMatchFinder.java	(revision 28163)
@@ -0,0 +1,50 @@
+
+
+/*
+ * The JCS 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.jump.feature.FeatureCollection;
+import com.vividsolutions.jump.task.TaskMonitor;
+import java.util.Map;
+
+public interface FCMatchFinder {
+    /**
+     * For each target feature, finds matches among the candidate features.
+     * @return a map of target-feature to matching-features (a Matches object)
+     */
+    public Map match(
+        FeatureCollection targetFC,
+        FeatureCollection candidateFC,
+        TaskMonitor monitor);
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/FeatureCollectionMatcher.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/FeatureCollectionMatcher.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/FeatureCollectionMatcher.java	(revision 28163)
@@ -0,0 +1,70 @@
+
+
+/*
+ * 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.jump.feature.Feature;
+import com.vividsolutions.jump.feature.FeatureCollection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Applies a FeatureMatcher to each item in a FeatureCollection
+ */
+public class FeatureCollectionMatcher {
+
+  /**
+   * Creates a FeatureCollectionMatcher that uses the given FeatureMatcher.
+   * @param matcher typically a composite of other FeatureMatchers
+   */
+  public FeatureCollectionMatcher(FeatureMatcher matcher) {
+    this.matcher = matcher;
+  }
+
+  private FeatureMatcher matcher;
+
+  /**
+   * For each target feature, finds matches among the candidate features.
+   * @return a map of target-feature to matching-features (a Matches object)
+   */
+  public Map match(FeatureCollection targetFC, FeatureCollection candidateFC) {
+    TreeMap map = new TreeMap();
+    for (Iterator i = targetFC.iterator(); i.hasNext(); ) {
+      Feature subjectFeature = (Feature) i.next();
+      map.put(subjectFeature, matcher.match(subjectFeature, candidateFC));
+    }
+    return map;
+  }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/FeatureMatcher.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/FeatureMatcher.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/FeatureMatcher.java	(revision 28163)
@@ -0,0 +1,63 @@
+
+
+/*
+ * The JCS 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.jump.feature.Feature;
+import com.vividsolutions.jump.feature.FeatureCollection;
+
+/**
+ * An algorithm for finding potential matches for a feature from a collection
+ * of candidate features. To facilitate specification using an XML file in the
+ * future:
+ * <UL>
+ *   <LI>there should be a constructor that takes no parameters
+ *   <LI>when the 0-parameter constructor is used, initialization should be
+ *       done using setter methods
+ *   <LI>composite FeatureMatchers should have an #add(FeatureMatcher) method
+ * </UL>
+ */
+public interface FeatureMatcher {
+
+  /**
+   * Searches a collection of candidate features for those that match the given
+   * target feature.
+   * @param target the feature to match
+   * @param candidates the features to search for matches
+   * @return the matching features, and a score for each. (Implementors should
+   * document how they do their scoring).
+   */
+  public Matches match(Feature target, FeatureCollection candidates);
+
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/HausdorffDistanceMatcher.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/HausdorffDistanceMatcher.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/HausdorffDistanceMatcher.java	(revision 28163)
@@ -0,0 +1,16 @@
+package com.vividsolutions.jcs.conflate.polygonmatch;
+
+import com.vividsolutions.jcs.algorithm.VertexHausdorffDistance;
+import com.vividsolutions.jts.geom.Geometry;
+
+/**
+ * Uses an approximation of the Hausdorff distance.
+ * @see VertexHausdorffDistance
+ */
+public class HausdorffDistanceMatcher extends AbstractDistanceMatcher {
+
+    protected double distance(Geometry target, Geometry candidate) {
+        return new VertexHausdorffDistance(target, candidate).distance();
+    }
+    
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/Histogram.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/Histogram.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/Histogram.java	(revision 28163)
@@ -0,0 +1,127 @@
+
+
+/*
+ * The JCS 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;
+
+/**
+ * Generic model for a bar graph.
+ */
+public class Histogram {
+
+  //<<TODO:DESIGN>> Add functions to make Histograms n-dimensional. But leave
+  //the original 1D API intact, for the majority of users who need just 1D
+  //[Jon Aquino]
+
+  /**
+   * Creates a Histogram with the given number of bins.
+   * @param binCount the number of bins
+   */
+  public Histogram(int binCount) {
+    bins = new double[binCount];
+  }
+
+  private double[] bins;
+
+  /**
+   * Adds a score to the ith bin.
+   * @param i 0, 1, 2, ...
+   * @param score the amount by which the ith bin will be incremented
+   */
+  public void addToBinScore(int i, double score) {
+    bins[i] += score;
+  }
+
+  /**
+   * Returns the score for the ith bin.
+   * @param i 0, 1, 2, ...
+   * @return the score for the ith bin
+   */
+  public double getBinScore(int i) {
+    return bins[i];
+  }
+
+  /**
+   * Adds the scores from all the bins.
+   * @return the sum of the scores in each bin. If the sum is 1, this
+   * histogram is said to be "normalized".
+   */
+  public double getTotalScore() {
+    double total = 0;
+    for (int i = 0; i < getBinCount(); i++) {
+      total += getBinScore(i);
+    }
+    return total;
+  }
+
+  /**
+   * Returns the number of bins.
+   * @return the number of bins that make up this Histogram
+   */
+  public int getBinCount() {
+    return bins.length;
+  }
+
+  /**
+   * Returns the symmetric difference between this Histogram and another
+   * Histogram.
+   * @param other the Histogram with which the symmetric difference will be
+   * computed
+   * @return the sum of the symmetric differences between corresponding
+   * bins. The symmetric difference between two corresponding bins is
+   * simply the absolute value of the difference.
+   */
+  public double symDiff(Histogram other) {
+    Assert.isTrue(getBinCount() == other.getBinCount());
+    double symDiff = 0;
+    for (int i = 0; i < getBinCount(); i++) {
+      symDiff += Math.abs(getBinScore(i) - other.getBinScore(i));
+    }
+    return symDiff;
+  }
+
+  /**
+   * Adds another Histogram's bin scores to this Histogram's bin scores.
+   * The number of bins must be the same in both Histograms.
+   * @param other the Histogram whose scores will be added to this Histogram
+   */
+  public void add(Histogram other) {
+    Assert.isTrue(getBinCount() == other.getBinCount());
+    for (int i = 0; i < getBinCount(); i++) {
+      addToBinScore(i, other.getBinScore(i));
+    }
+  }
+
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/IdenticalFeatureFilter.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/IdenticalFeatureFilter.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/IdenticalFeatureFilter.java	(revision 28163)
@@ -0,0 +1,28 @@
+package com.vividsolutions.jcs.conflate.polygonmatch;
+
+import com.vividsolutions.jump.feature.Feature;
+import com.vividsolutions.jump.feature.FeatureCollection;
+
+/**
+ * Filters out matches where features are identical.
+ */
+public class IdenticalFeatureFilter implements FeatureMatcher {
+    
+  /**
+   * Filters out matches where features are identical.
+   * @param target the Feature which is the target
+   * @param candidates a Matches object created by another FeatureMatcher
+   * @return the candidates that aren't identical to the target
+   */
+    @Override
+  public Matches match(Feature target, FeatureCollection candidates) {
+    Matches survivors = new Matches(candidates.getFeatureSchema());
+    Matches allMatches = (Matches) candidates;
+    for (int i = 0; i < allMatches.size(); i++) {
+      if (!allMatches.getFeature(i).equals(target)) {
+        survivors.add(allMatches.getFeature(i), allMatches.getScore(i));
+      }
+    }
+    return survivors;
+  }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/IndependentCandidateMatcher.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/IndependentCandidateMatcher.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/IndependentCandidateMatcher.java	(revision 28163)
@@ -0,0 +1,71 @@
+
+
+/*
+ * The JCS 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.geom.Geometry;
+import com.vividsolutions.jump.feature.Feature;
+import com.vividsolutions.jump.feature.FeatureCollection;
+import java.util.Iterator;
+
+/**
+ * Base class of FeatureMatchers that compare the target to each candidate
+ * in turn -- the comparisons only use one candidate at a time.
+ */
+public abstract class IndependentCandidateMatcher implements FeatureMatcher {
+
+  public IndependentCandidateMatcher() {
+  }
+
+    @Override
+  public Matches match(Feature target, FeatureCollection candidates) {
+    Matches matches = new Matches(candidates.getFeatureSchema());
+    for (Iterator i = candidates.iterator(); i.hasNext(); ) {
+      Feature candidate = (Feature) i.next();
+      double score = match(target.getGeometry(), candidate.getGeometry());
+      if (score > 0) { matches.add(candidate, score); }
+    }
+    return matches;
+  }
+
+  /**
+   * Compares the target to the candidate feature. Called for each candidate
+   * feature by #match(Feature, FeatureCollection).
+   * @param target the feature to match
+   * @param candidate the feature to compare with the target
+   * @return a score from 0 to 1 indicating how well the candidate matches the
+   * target
+   */
+  public abstract double match(Geometry target, Geometry candidate);
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/MatcherUtil.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/MatcherUtil.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/MatcherUtil.java	(revision 28163)
@@ -0,0 +1,109 @@
+
+
+/*
+ * The JCS 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.geom.Coordinate;
+import com.vividsolutions.jts.geom.CoordinateFilter;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jump.geom.CoordUtil;
+import com.vividsolutions.jump.util.CoordinateArrays;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Functions useful to FeatureMatchers in general.
+ */
+public class MatcherUtil {
+
+  /**
+   * Moves g so that the outline centre-of-mass is at (0,0).
+   * @param g the Geometry to modify
+   */
+  public static void alignByOutlineCentreOfMass(Geometry g) {
+    align(g, outlineCentreOfMass(g));
+  }
+
+  /**
+   * Moves g so that c is at (0,0).
+   * @param g the Geometry to modify
+   * @param c the point to move to the origin
+   */
+  public static void align(Geometry g, Coordinate c) {
+    final Coordinate move = CoordUtil.subtract(new Coordinate(0,0), c);
+    g.apply(new CoordinateFilter() {
+            @Override
+      public void filter(Coordinate coordinate) {
+        coordinate.x += move.x;
+        coordinate.y += move.y;
+      }
+    });
+  }
+
+  /**
+   * Returns the centre-of-mass of g's line segments
+   * @param g the Geometry to analyze
+   * @return the weighted average of the midpoints of g's line segments,
+   * weighted by segment length
+   */
+  public static Coordinate outlineCentreOfMass(Geometry g) {
+    Coordinate weightedSum = new Coordinate();
+    double totalLength = 0;
+    List coordArrays = CoordinateArrays.toCoordinateArrays(g, false);
+    for (Iterator i = coordArrays.iterator(); i.hasNext(); ) {
+      Coordinate[] coords = (Coordinate[]) i.next();
+      for (int j = 1; j < coords.length; j++) {
+        double length = coords[j-1].distance(coords[j]);
+        totalLength += length;
+        weightedSum = CoordUtil.add(weightedSum, CoordUtil.multiply(length,
+            CoordUtil.average(coords[j-1], coords[j])));
+      }
+    }
+    return CoordUtil.divide(weightedSum, totalLength);
+  }
+
+  /**
+   * Returns a FeatureMatcher score based on the symmetric difference
+   * @param targetArea area of the target shape
+   * @param candidateArea area of the candidate shape
+   * @param symDiffArea area of the symmetric difference between the two shapes
+   * @return a linear function of the symmetric difference: 1 if the shapes perfectly
+   * overlap; 0 if the shapes do not overlap at all
+   */
+  public static double toScoreFromSymDiffArea(
+      double targetArea, double candidateArea, double symDiffArea) {
+    return 1d - (symDiffArea / (targetArea + candidateArea));
+  }
+
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/Matches.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/Matches.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/Matches.java	(revision 28163)
@@ -0,0 +1,229 @@
+
+
+/*
+ * The JCS 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.geom.Envelope;
+import com.vividsolutions.jts.util.Assert;
+import com.vividsolutions.jump.feature.Feature;
+import com.vividsolutions.jump.feature.FeatureCollection;
+import com.vividsolutions.jump.feature.FeatureDataset;
+import com.vividsolutions.jump.feature.FeatureSchema;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A FeatureCollection that stores the "score" of each Feature.  The score is
+ * a number between 0.0 and 1.0 that indicates the confidence of a match.
+ */
+public class Matches implements FeatureCollection, Cloneable {
+
+    /**
+     * Creates a Matches object.
+     * @param schema metadata applicable to the features that will be stored in
+     * this Matches object
+     */
+    public Matches(FeatureSchema schema) {
+        dataset = new FeatureDataset(schema);
+    }
+
+    @Override
+    protected Object clone() {
+        Matches clone = new Matches(dataset.getFeatureSchema());
+        for (int i = 0; i < size(); i++) {
+            clone.add(getFeature(i), getScore(i));
+        }
+        return clone;
+    }
+
+    /**
+     * Creates a Matches object, initialized with the given Feature's.
+     * @param schema metadata applicable to the features that will be stored in
+     * this Matches object
+     * @param features added to the Matches, each with the max score (1.0)
+     */
+    public Matches(FeatureSchema schema, List features) {
+        this(schema);
+        for (Iterator i = features.iterator(); i.hasNext();) {
+            Feature match = (Feature) i.next();
+            add(match, 1);
+        }
+    }
+
+    private FeatureDataset dataset;
+    private ArrayList scores = new ArrayList();
+
+    /**
+     * This method is not supported, because added features need to be associated
+     * with a score. Use #add(Feature, double) instead.
+     * @param feature a feature to add as a match
+     * @see #add(Feature, double)
+     */
+    public void add(Feature feature) {
+        throw new UnsupportedOperationException("Use #add(feature, score) instead");
+    }
+
+    /**
+     * This method is not supported, because added features need to be associated
+     * with a score. Use #add(Feature, double) instead.
+     */
+    public void addAll(Collection features) {
+        throw new UnsupportedOperationException("Use #add(feature, score) instead");
+    }
+
+    /**
+     * This method is not supported, because added features need to be associated
+     * with a score. Use #add(Feature, double) instead.
+     * @param feature a feature to add as a match
+     * @see #add(Feature, double)
+     */
+    public void add(int index, Feature feature) {
+        throw new UnsupportedOperationException("Use #add(feature, score) instead");
+    }
+
+    /**
+     * This method is not supported, because Matches should not normally need to
+     * have matches removed.
+     */
+    public Collection remove(Envelope envelope) {
+        //If we decide to implement this, remember to remove the corresponding
+        //score. [Jon Aquino]
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * This method is not supported, because Matches should not normally need to
+     * have matches removed.
+     */
+    public void clear() {
+        //If we decide to implement this, remember to remove the corresponding
+        //score. [Jon Aquino]
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * This method is not supported, because Matches should not normally need to
+     * have matches removed.
+     */
+    public void removeAll(Collection features) {
+        //If we decide to implement this, remember to remove the corresponding
+        //score. [Jon Aquino]
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * This method is not supported, because Matches should not normally need to
+     * have matches removed.
+     * @param feature a feature to remove
+     */
+    public void remove(Feature feature) {
+        //If we decide to implement this, remember to remove the corresponding
+        //score. [Jon Aquino]
+        throw new UnsupportedOperationException();
+    }
+    /**
+     * Adds a match. Features with zero-scores are ignored.
+     * @param feature a feature to add as a match
+     * @param score the confidence of the match, ranging from 0 to 1
+     */
+    public void add(Feature feature, double score) {
+        Assert.isTrue(0 <= score && score <= 1, "Score = " + score);
+        if (score == 0) {
+            return;
+        }
+        scores.add(new Double(score));
+        dataset.add(feature);
+        if (score > topScore) {
+            topScore = score;
+            topMatch = feature;
+        }
+    }
+
+    private Feature topMatch;
+    private double topScore = 0;
+
+    public double getTopScore() {
+        return topScore;
+    }
+
+    /**
+     * @return the feature with the highest score
+     */
+    public Feature getTopMatch() {
+        return topMatch;
+    }
+
+    /**
+     * Returns the score of the ith feature
+     * @param i 0, 1, 2, ...
+     * @return the confidence of the ith match
+     */
+    public double getScore(int i) {
+        return ((Double) scores.get(i)).doubleValue();
+    }
+
+    public FeatureSchema getFeatureSchema() {
+        return dataset.getFeatureSchema();
+    }
+
+    public Envelope getEnvelope() {
+        return dataset.getEnvelope();
+    }
+
+    public int size() {
+        return dataset.size();
+    }
+
+    public boolean isEmpty() {
+        return dataset.isEmpty();
+    }
+
+    public Feature getFeature(int index) {
+        return dataset.getFeature(index);
+    }
+
+    public List getFeatures() {
+        return dataset.getFeatures();
+    }
+
+    public Iterator iterator() {
+        return dataset.iterator();
+    }
+
+    public List query(Envelope envelope) {
+        return dataset.query(envelope);
+    }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/MinScoreMatcher.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/MinScoreMatcher.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/MinScoreMatcher.java	(revision 28163)
@@ -0,0 +1,87 @@
+
+
+/*
+ * 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.jump.feature.Feature;
+import com.vividsolutions.jump.feature.FeatureCollection;
+
+/**
+ * Filters out shapes with a score below a given value.
+ */
+public class MinScoreMatcher implements FeatureMatcher {
+
+  /**
+   * Creates a MinScoreMatcher with a minimum score of 0. Be sure to call
+   * #setMinScore.
+   */
+  public MinScoreMatcher() {
+  }
+
+  /**
+   * Creates a MinScoreMatcher with the given minimum score.
+   * @param minScore the score below which shapes will be filtered out
+   */
+  public MinScoreMatcher(double minScore) {
+    setMinScore(minScore);
+  }
+
+  /**
+   * Sets the threshold score.
+   * @param minScore the score below which shapes will be filtered out
+   */
+  public void setMinScore(double minScore) { this.minScore = minScore; }
+
+  private double minScore;
+
+  /**
+   * Filters out shapes with a score below the minimum score threshold.
+   * @param target ignored
+   * @param candidates a Matches object created by another FeatureMatcher
+   * @return the candidates having a score greater than or equal to the
+   * threshold score. The scores are preserved from the original Matches
+   * object.
+   */
+    @Override
+  public Matches match(Feature target, FeatureCollection candidates) {
+    Matches survivors = new Matches(candidates.getFeatureSchema());
+    Matches allMatches = (Matches) candidates;
+    for (int i = 0; i < allMatches.size(); i++) {
+      if (allMatches.getScore(i) >= minScore) {
+        survivors.add(allMatches.getFeature(i), allMatches.getScore(i));
+      }
+    }
+    return survivors;
+  }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/OneToOneFCMatchFinder.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/OneToOneFCMatchFinder.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/OneToOneFCMatchFinder.java	(revision 28163)
@@ -0,0 +1,141 @@
+
+
+/*
+ * 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;
+  }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/OverlapMatcher.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/OverlapMatcher.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/OverlapMatcher.java	(revision 28163)
@@ -0,0 +1,13 @@
+package com.vividsolutions.jcs.conflate.polygonmatch;
+
+import com.vividsolutions.jts.geom.Geometry;
+
+public class OverlapMatcher extends IndependentCandidateMatcher {
+
+    public double match(Geometry target, Geometry candidate) {
+        //Impose the min to curb roundoff error in exact matches (a situation which
+        //arose during testing (identical datasets)) [Jon Aquino]
+        return Math.min(1, (2 * target.intersection(candidate).getArea())
+            / (target.getArea() + candidate.getArea()));
+    }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/ScaleScoresMatcher.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/ScaleScoresMatcher.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/ScaleScoresMatcher.java	(revision 28163)
@@ -0,0 +1,105 @@
+
+
+/*
+ * 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.jump.feature.Feature;
+import com.vividsolutions.jump.feature.FeatureCollection;
+
+/**
+ * Re-scales the scores output from another FeatureMatcher
+ */
+public class ScaleScoresMatcher implements FeatureMatcher {
+
+  /**
+   * Creates a ScaleScoresMatcher without setting newZeroScore nor newFullScore.
+   * Be sure to set them.
+   */
+  public ScaleScoresMatcher() {
+  }
+
+  /**
+   * Creates a ScaleScoresMatcher with the given control points.
+   * @param newZeroScore the score that will be warped to 0
+   * @param newFullScore the score that will be warped to 1
+   */
+  public ScaleScoresMatcher(double newZeroScore, double newFullScore) {
+    setNewZeroScore(newZeroScore);
+    setNewFullScore(newFullScore);
+  }
+
+  /**
+   * Sets one of the control points.
+   * @param newZeroScore the score that will be warped to 0
+   */
+  public void setNewZeroScore(double newZeroScore) {
+    this.newZeroScore = newZeroScore;
+  }
+
+  private double newZeroScore = 0;
+  private double newFullScore = 1;
+
+  /**
+   * Sets one of the control points.
+   * @param newFullScore the score that will be warped to 1
+   */
+  public void setNewFullScore(double newFullScore) {
+    this.newFullScore = newFullScore;
+  }
+
+  /**
+   * Scales the scores so that #newZeroScore becomes 0 and #newFullScore
+   * becomes 1. Scores outside of 0 and 1 get set to 0 and 1 respectively.
+   * @param target ignored
+   * @param candidates a Matches object created by another FeatureMatcher
+   * @return the scaled scores
+   */
+    @Override
+  public Matches match(Feature target, FeatureCollection candidates) {
+    Matches oldMatches = (Matches) candidates;
+    Matches newMatches = new Matches(candidates.getFeatureSchema());
+    for (int i = 0; i < oldMatches.size(); i++) {
+      newMatches.add(oldMatches.getFeature(i), convert(oldMatches.getScore(i)));
+    }
+    return newMatches;
+  }
+
+  private double convert(double oldScore) {
+    //y = m x + b; v = m u + b
+    double x = newZeroScore, y = 0, u = newFullScore, v = 1;
+    double m = (y - v) / (x - u);
+    double b = y - (m * x);
+    return Math.min(1, Math.max(0, (m * oldScore) + b));
+  }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/ScoreStretcher.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/ScoreStretcher.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/ScoreStretcher.java	(revision 28163)
@@ -0,0 +1,82 @@
+
+
+/*
+ * The JCS 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.jump.feature.Feature;
+import com.vividsolutions.jump.feature.FeatureCollection;
+
+/**
+ * Re-scales the scores output from another FeatureMatcher
+ */
+public class ScoreStretcher implements FeatureMatcher {
+
+  /**
+   * Creates a StretchFilter with the given control points.
+   * @param minScore the score that will be warped to 0
+   * @param maxScore the score that will be warped to 1
+   */
+  public ScoreStretcher(double minScore, double maxScore) {
+    this.minScore = minScore;
+    this.maxScore = maxScore;
+  }
+
+  private double minScore;
+  private double maxScore;
+
+  /**
+   * Scales the scores so that #minScore becomes 0 and #maxScore
+   * becomes 1. Scores outside of 0 and 1 get set to 0 and 1 respectively.
+   * @param target ignored
+   * @param candidates a Matches object created by another FeatureMatcher
+   * @return the scaled scores
+   */
+    @Override
+  public Matches match(Feature target, FeatureCollection candidates) {
+    Matches oldMatches = (Matches) candidates;
+    Matches newMatches = new Matches(candidates.getFeatureSchema());
+    for (int i = 0; i < oldMatches.size(); i++) {
+      newMatches.add(oldMatches.getFeature(i), convert(oldMatches.getScore(i)));
+    }
+    return newMatches;
+  }
+
+  private double convert(double oldScore) {
+    //y = m x + b; v = m u + b
+    double x = minScore, y = 0, u = maxScore, v = 1;
+    double m = (y - v) / (x - u);
+    double b = y - (m * x);
+    return Math.min(1, Math.max(0, (m * oldScore) + b));
+  }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/SymDiffMatcher.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/SymDiffMatcher.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/SymDiffMatcher.java	(revision 28163)
@@ -0,0 +1,64 @@
+
+
+/*
+ * The JCS 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.geom.Geometry;
+
+/**
+ * Uses symmetric difference as the criterion for determining match scores.
+ */
+public class SymDiffMatcher extends IndependentCandidateMatcher {
+
+  public SymDiffMatcher() {
+  }
+
+  /**
+   * The score is a linear function of the symmetric difference: 1 if the shapes perfectly
+   * overlap; 0 if the shapes do not overlap at all.
+   * @param target the feature to match
+   * @param candidate the feature to compare with the target
+   * @return candidates with a score greater than 0 (typically all the candidates).
+   */
+  public double match(Geometry target, Geometry candidate) {
+    Geometry targetGeom = (Geometry) target.clone();
+    Geometry candidateGeom = (Geometry) candidate.clone();
+    if (targetGeom.isEmpty() || candidateGeom.isEmpty()) {
+      return 0; //avoid div by 0 in centre-of-mass calc [Jon Aquino]
+    }
+    return MatcherUtil.toScoreFromSymDiffArea(
+        targetGeom.getArea(), candidateGeom.getArea(),
+        targetGeom.symDifference(candidateGeom).getArea());
+  }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/TargetUnioningFCMatchFinder.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/TargetUnioningFCMatchFinder.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/TargetUnioningFCMatchFinder.java	(revision 28163)
@@ -0,0 +1,328 @@
+/*
+ * The JCS 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.geom.Coordinate;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.util.Assert;
+import com.vividsolutions.jump.feature.*;
+import com.vividsolutions.jump.task.TaskMonitor;
+import com.vividsolutions.jump.util.CollectionUtil;
+import com.vividsolutions.jump.util.CoordinateArrays;
+import java.util.*;
+/**
+ *  An FCMatchFinder wrapper that also treats unions of adjacent target features
+ *  as themselves target features. Such unions are formed into composite target
+ *  features. These composites are temporary -- before the results are returned,
+ *  each composite is split into its constituent features. <P>
+ *
+ *  The result returned is a one-to-one mapping of target feature to matched
+ *  candidate feature; the one-to-one mapping is achieved by discarding all
+ *  matches except for those with the highest scores, for each feature (target
+ *  and matched candidate). <P>
+ *
+ *  Note on composites: if a composite's top score is higher than the top score
+ *  of each of its constituents, the composite match is retained and constituent
+ *  matches are discarded; otherwise, the composite match is discarded and
+ *  constituent matches are retained.
+ */
+public class TargetUnioningFCMatchFinder implements FCMatchFinder {
+    private FCMatchFinder matchFinder;
+    private int maxCompositeSize;
+    /**
+     *@param  maxCompositeSize  the maximum number of adjacent target features to
+     *      try combining
+     *@param  matchFinder       the FCMatchFinder to wrap
+     */
+    public TargetUnioningFCMatchFinder(int maxCompositeSize, FCMatchFinder matchFinder) {
+        this.maxCompositeSize = maxCompositeSize;
+        this.matchFinder = matchFinder;
+    }
+    @Override
+    public Map match(
+        FeatureCollection targetFC,
+        FeatureCollection candidateFC,
+        TaskMonitor monitor) {
+        monitor.allowCancellationRequests();
+        FeatureCollection compositeTargetFC = createCompositeFC(targetFC, monitor);
+        Map compositeTargetFeatureToMatchesMap =
+            matchFinder.match(compositeTargetFC, candidateFC, monitor);
+        compositeTargetFeatureToMatchesMap =
+            disambiguateCompositeTargetConstituents(
+                compositeTargetFeatureToMatchesMap,
+                candidateFC.getFeatureSchema(),
+                monitor);
+        createUnionIDs(compositeTargetFeatureToMatchesMap, monitor);                
+        Map filteredTargetToMatchesMap =
+            splitCompositeTargets(compositeTargetFeatureToMatchesMap, monitor);
+        //Zero-score targets will have been filtered out. Put them back. [Jon Aquino]
+        Map targetToMatchesMap =
+            AreaFilterFCMatchFinder.blankTargetToMatchesMap(
+                targetFC.getFeatures(),
+                candidateFC.getFeatureSchema());
+        targetToMatchesMap.putAll(filteredTargetToMatchesMap);
+        return targetToMatchesMap;
+    }
+    private List lastTargetConstituents;
+    private List lastUnionIDs;
+    private void createUnionIDs(final Map compositeTargetFeatureToMatchesMap, TaskMonitor monitor) {
+        monitor.report("Creating union IDs");
+        ArrayList compositeTargets = new ArrayList(compositeTargetFeatureToMatchesMap.keySet());
+        Collections.sort(compositeTargets, new Comparator() {
+            @Override
+            public int compare(Object o1, Object o2) {
+                double s1 = ((Matches)compositeTargetFeatureToMatchesMap.get(o1)).getTopScore();
+                double s2 = ((Matches)compositeTargetFeatureToMatchesMap.get(o2)).getTopScore();
+                return s1 < s2 ? -1 : s1 > s2 ? 1 : 0;
+            }
+        });
+        lastTargetConstituents = new ArrayList();
+        lastUnionIDs = new ArrayList();
+        int unionID = 0;
+        for (int i = 0; i < compositeTargets.size(); i++) {
+            monitor.report(i+1, compositeTargets.size(), "unions");
+            CompositeFeature compositeTarget = (CompositeFeature) compositeTargets.get(i);
+            if (compositeTarget.getFeatures().size() == 1) {
+                continue;
+            }
+            unionID++;
+            for (Iterator j = compositeTarget.getFeatures().iterator(); j.hasNext(); ) {
+                Feature targetConstituent = (Feature) j.next();
+                lastTargetConstituents.add(targetConstituent);
+                lastUnionIDs.add(new Integer(unionID));
+            }
+        }
+    }
+    protected FeatureCollection createCompositeFC(
+        FeatureCollection fc,
+        TaskMonitor monitor) {
+        FeatureCollection compositeFC = new FeatureDataset(fc.getFeatureSchema());
+        Set composites = createCompositeSet(fc, monitor);
+        add(composites, compositeFC, monitor);
+        return new IndexedFeatureCollection(compositeFC);
+    }
+    /**
+     * Returns a composite-target-to-Matches map in which each target constituent will be 
+     * found in at most one composite target. Does not disambiguate composite targets
+     * or matches (use DisambiguatingFCMatchFinder to do that), just composite target
+     * constituents.
+     */
+    protected Map disambiguateCompositeTargetConstituents(
+        Map compositeTargetToMatchesMap,
+        FeatureSchema candidateSchema,
+        TaskMonitor monitor) {
+        ArrayList targetConstituentsEncountered = new ArrayList();
+        ArrayList compositeTargets = new ArrayList();
+        ArrayList candidates = new ArrayList();
+        ArrayList scores = new ArrayList();
+        SortedSet matchSet =
+            DisambiguationMatch.createDisambiguationMatches(compositeTargetToMatchesMap, monitor);
+        monitor.report("Discarding inferior composite matches");
+        int j = 0;
+        outer : for (Iterator i = matchSet.iterator(); i.hasNext();) {
+            DisambiguationMatch match = (DisambiguationMatch) i.next();
+            monitor.report(++j, matchSet.size(), "matches");
+            for (Iterator k =
+                ((CompositeFeature) match.getTarget()).getFeatures().iterator();
+                k.hasNext();
+                ) {
+                Feature targetConstituent = (Feature) k.next();
+                if (targetConstituentsEncountered.contains(targetConstituent)) {
+                    continue outer;
+                }
+            }
+            compositeTargets.add(match.getTarget());
+            candidates.add(match.getCandidate());
+            scores.add(new Double(match.getScore()));
+            targetConstituentsEncountered.addAll(((CompositeFeature) match.getTarget()).getFeatures());
+        }
+        Map newMap = new HashMap();
+        for (int i = 0; i < compositeTargets.size(); i++) {
+            Matches matches = new Matches(candidateSchema);
+            matches.add(
+                (Feature) candidates.get(i),
+                ((Double) scores.get(i)).doubleValue());
+            newMap.put(compositeTargets.get(i), matches);
+        }
+        return newMap;
+    }
+    private List featuresWithCommonEdge(Feature feature, FeatureCollection fc) {
+        ArrayList featuresWithCommonEdge = new ArrayList();
+        List candidates = fc.query(feature.getGeometry().getEnvelopeInternal());
+        for (Iterator i = candidates.iterator(); i.hasNext();) {
+            Feature candidate = (Feature) i.next();
+            if (feature == candidate
+                || shareEdge(feature.getGeometry(), candidate.getGeometry())) {
+                featuresWithCommonEdge.add(candidate);
+            }
+        }
+        return featuresWithCommonEdge;
+    }
+    protected boolean shareEdge(Geometry a, Geometry b) {
+        Set aEdges = edges(a);
+        Set bEdges = edges(b);
+        for (Iterator i = bEdges.iterator(); i.hasNext();) {
+            Edge bEdge = (Edge) i.next();
+            if (aEdges.contains(bEdge)) {
+                return true;
+            }
+        }
+        return false;
+    }
+    private static class Edge implements Comparable {
+        private Coordinate p0, p1;
+        public Edge(Coordinate a, Coordinate b) {
+            if (a.compareTo(b) < 1) {
+                p0 = a;
+                p1 = b;
+            } else {
+                p0 = b;
+                p1 = a;
+            }
+        }
+        @Override
+        public int compareTo(Object o) {
+            Edge other = (Edge) o;
+            int result = p0.compareTo(other.p0);
+            if (result != 0)
+                return result;
+            return p1.compareTo(other.p1);
+        }
+    }
+    private Set edges(Geometry g) {
+        TreeSet edges = new TreeSet();
+        for (Iterator i = CoordinateArrays.toCoordinateArrays(g, false).iterator();
+            i.hasNext();
+            ) {
+            Coordinate[] coordinates = (Coordinate[]) i.next();
+            for (int j = 1; j < coordinates.length; j++) { //1
+                edges.add(new Edge(coordinates[j], coordinates[j - 1]));
+            }
+        }
+        return edges;
+    }
+    /**
+     *  Splits each composite target into its constituent features.
+     */
+    protected Map splitCompositeTargets(Map compositeToMatchesMap, TaskMonitor monitor) {
+        monitor.report("Splitting composites");
+        int compositesProcessed = 0;
+        int totalComposites = compositeToMatchesMap.size();
+        Map newMap = new HashMap();
+        for (Iterator i = compositeToMatchesMap.keySet().iterator();
+            i.hasNext() && !monitor.isCancelRequested();
+            ) {
+            CompositeFeature composite = (CompositeFeature) i.next();
+            compositesProcessed++;
+            monitor.report(compositesProcessed, totalComposites, "composites");
+            Matches matches = (Matches) compositeToMatchesMap.get(composite);
+            for (Iterator j = composite.getFeatures().iterator(); j.hasNext();) {
+                Feature targetConstituent = (Feature) j.next();
+                Assert.isTrue(!newMap.containsKey(targetConstituent));
+                newMap.put(targetConstituent, matches.clone());
+            }
+        }
+        return newMap;
+    }
+    private Set createCompositeSet(FeatureCollection fc, TaskMonitor monitor) {
+        monitor.report("Creating composites of adjacent features");
+        int featuresProcessed = 0;
+        int totalFeatures = fc.getFeatures().size();
+        //Use a Set to prevent duplicate composites [Jon Aquino]
+        HashSet composites = new HashSet();
+        for (Iterator i = fc.getFeatures().iterator();
+            i.hasNext() && !monitor.isCancelRequested();
+            ) {
+            Feature feature = (Feature) i.next();
+            featuresProcessed++;
+            monitor.report(featuresProcessed, totalFeatures, "features");
+            List featuresWithCommonEdge = featuresWithCommonEdge(feature, fc);
+            for (Iterator j =
+                CollectionUtil
+                    .combinations(featuresWithCommonEdge, maxCompositeSize, feature)
+                    .iterator();
+                j.hasNext() && !monitor.isCancelRequested();
+                ) {
+                List combination = (List) j.next();
+                composites.add(new CompositeFeature(fc.getFeatureSchema(), combination));
+            }
+        }
+        return composites;
+    }
+
+    public static class CompositeFeature extends BasicFeature {
+        private List features;
+        private int hashCode;
+        public CompositeFeature(FeatureSchema schema, List features) {
+            super(schema);
+            this.features = features;
+            Geometry union = ((Feature) features.get(0)).getGeometry();
+            hashCode = ((Feature) features.get(0)).hashCode();
+            for (int i = 1; i < features.size(); i++) {
+                Feature feature = (Feature) features.get(i);
+                union = union.union(feature.getGeometry());
+                hashCode = Math.min(hashCode, feature.hashCode());
+            }
+            setGeometry(union);
+        }
+        public List getFeatures() {
+            return features;
+        }
+        @Override
+        public boolean equals(Object obj) {
+            Assert.isTrue(obj instanceof CompositeFeature, obj.getClass().toString());
+            CompositeFeature other = (CompositeFeature) obj;
+            if (features.size() != other.features.size()) {
+                return false;
+            }
+            for (Iterator i = features.iterator(); i.hasNext();) {
+                Feature myFeature = (Feature) i.next();
+                if (!other.features.contains(myFeature)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+        @Override
+        public int hashCode() {
+            return hashCode;
+        }
+    }
+    private void add(Collection features, FeatureCollection fc, TaskMonitor monitor) {
+        monitor.report("Building feature-collection");
+        fc.addAll(features);
+    }
+    public Integer getUnionID(Feature target) {
+        int i = lastTargetConstituents.indexOf(target);
+        if (i == -1) { return null; }
+        return (Integer)lastUnionIDs.get(i);
+    }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/ThresholdFilter.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/ThresholdFilter.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/ThresholdFilter.java	(revision 28163)
@@ -0,0 +1,74 @@
+
+
+/*
+ * The JCS 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.jump.feature.Feature;
+import com.vividsolutions.jump.feature.FeatureCollection;
+
+/**
+ * Filters out shapes with a score below a given value.
+ */
+public class ThresholdFilter implements FeatureMatcher {
+
+  /**
+   * Creates a ThresholdFilter with the given minimum score.
+   * @param minScore the score below which shapes will be filtered out
+   */
+  public ThresholdFilter(double minScore) {
+    this.minScore = minScore;
+  }
+
+  private double minScore;
+
+  /**
+   * Filters out shapes with a score below the minimum score threshold.
+   * @param target ignored
+   * @param candidates a Matches object created by another FeatureMatcher
+   * @return the candidates having a score greater than or equal to the
+   * threshold score. The scores are preserved from the original Matches
+   * object.
+   */
+    @Override
+  public Matches match(Feature target, FeatureCollection candidates) {
+    Matches survivors = new Matches(candidates.getFeatureSchema());
+    Matches allMatches = (Matches) candidates;
+    for (int i = 0; i < allMatches.size(); i++) {
+      if (allMatches.getScore(i) >= minScore) {
+        survivors.add(allMatches.getFeature(i), allMatches.getScore(i));
+      }
+    }
+    return survivors;
+  }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/TopMatchDisambiguatingFCMatchFinder.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/TopMatchDisambiguatingFCMatchFinder.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/TopMatchDisambiguatingFCMatchFinder.java	(revision 28163)
@@ -0,0 +1,176 @@
+
+
+/*
+ * The JCS 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.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.
+ * "Conservative" because only top matches are allowed.
+ * <P>
+ * <b>Note:</b> DisambiguatingFCMatchFinder seems to give better results.
+ * <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).
+ * * @see DisambiguatingFCMatchFinder 
+ */
+public class TopMatchDisambiguatingFCMatchFinder implements FCMatchFinder {
+
+    private FCMatchFinder matchFinder;
+
+    public TopMatchDisambiguatingFCMatchFinder(FCMatchFinder matchFinder) {
+        this.matchFinder = matchFinder;
+    }
+
+    @Override
+    public Map match(
+        FeatureCollection targetFC,
+        FeatureCollection candidateFC,
+        TaskMonitor monitor) {
+        Map originalTargetToMatchesMap =
+            matchFinder.match(targetFC, candidateFC, monitor);
+        monitor.allowCancellationRequests();
+        monitor.report("Finding best forward matches");
+        Map bestForwardMatches = filterMatches(originalTargetToMatchesMap, monitor);
+        monitor.report("Finding best reverse matches");
+        Map bestReverseMatches =
+            filterMatches(invert(originalTargetToMatchesMap, 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]
+        Map filteredTargetToMatchesMap =
+            commonMatches(
+                bestForwardMatches,
+                invert(bestReverseMatches, monitor),
+                monitor);
+        //Put back the targets that were filtered out (albeit with no matches). [Jon Aquino]
+        Map targetToMatchesMap =
+            AreaFilterFCMatchFinder.blankTargetToMatchesMap(
+                targetFC.getFeatures(),
+                candidateFC.getFeatureSchema());
+        targetToMatchesMap.putAll(filteredTargetToMatchesMap);
+        return targetToMatchesMap;
+    }
+
+    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 = 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;
+    }
+
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/TopScoreFilter.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/TopScoreFilter.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/TopScoreFilter.java	(revision 28163)
@@ -0,0 +1,74 @@
+
+
+/*
+ * The JCS 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.jump.feature.Feature;
+import com.vividsolutions.jump.feature.FeatureCollection;
+
+/**
+ * Filters out all shapes except the one with the top score. Not needed
+ * if you are using OneToOneFCMatchFinder.
+ */
+public class TopScoreFilter implements FeatureMatcher {
+
+  public TopScoreFilter() {
+  }
+
+  /**
+   * Finds the candidate with the highest score. If several candidates share
+   * the top score, only one of them will be returned.
+   * @param target ignored
+   * @param candidates a Matches object created by another FeatureMatcher
+   * @return the candidate having the greatest score. The scores is
+   * preserved from the original Matches object.
+   */
+    @Override
+  public Matches match(Feature target, FeatureCollection candidates) {
+    Matches survivors = new Matches(candidates.getFeatureSchema());
+    Feature survivor = null;
+    double topScore = 0;
+    Matches matches = (Matches) candidates;
+    for (int i = 0; i < matches.size(); i++) {
+      if (matches.getScore(i) >= topScore) {
+        topScore = matches.getScore(i);
+        survivor = matches.getFeature(i);
+      }
+    }
+    if (survivor != null) {
+      survivors.add(survivor, topScore);
+    }
+    return survivors;
+  }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/TopScoreMatcher.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/TopScoreMatcher.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/TopScoreMatcher.java	(revision 28163)
@@ -0,0 +1,73 @@
+
+
+/*
+ * 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.jump.feature.Feature;
+import com.vividsolutions.jump.feature.FeatureCollection;
+
+/**
+ * Filters out all shapes except the one with the top score.
+ */
+public class TopScoreMatcher implements FeatureMatcher {
+
+  public TopScoreMatcher() {
+  }
+
+  /**
+   * Finds the candidate with the highest score. If several candidates share
+   * the top score, only one of them will be returned.
+   * @param target ignored
+   * @param candidates a Matches object created by another FeatureMatcher
+   * @return the candidate having the greatest score. The scores is
+   * preserved from the original Matches object.
+   */
+    @Override
+  public Matches match(Feature target, FeatureCollection candidates) {
+    Matches survivors = new Matches(candidates.getFeatureSchema());
+    Feature survivor = null;
+    double topScore = 0;
+    Matches matches = (Matches) candidates;
+    for (int i = 0; i < matches.size(); i++) {
+      if (matches.getScore(i) >= topScore) {
+        topScore = matches.getScore(i);
+        survivor = matches.getFeature(i);
+      }
+    }
+    if (survivor != null) {
+      survivors.add(survivor, topScore);
+    }
+    return survivors;
+  }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/WeightedMatcher.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/WeightedMatcher.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/WeightedMatcher.java	(revision 28163)
@@ -0,0 +1,153 @@
+
+
+/*
+ * The JCS 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.FeatureSchema;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Runs multiple FeatureMatchers, and combines their scores using a weighted
+ * average.
+ */
+public class WeightedMatcher implements FeatureMatcher {
+
+    /**
+   * Creates a WeightedMatcher with the given matchers and their weights.
+   * @param matchersAndWeights alternates between FeatureMatchers and Doubles
+   */
+  public WeightedMatcher(Object[] matchersAndWeights) {
+    Assert.isTrue(matchersAndWeights.length % 2 == 0);
+    for (int i = 0; i < matchersAndWeights.length; i += 2) {       
+      add((FeatureMatcher) matchersAndWeights[i+1],
+          ((Number) matchersAndWeights[i]).doubleValue());
+      //Number rather than Double so parties (e.g. Jython) can pass in Integers. [Jon Aquino]          
+    }
+  }
+
+  /**
+   * Adds a matcher to the WeightedMatcher's matchers. If weight is 0, the
+   * matcher will be ignored.
+   * @param matcher a matcher to add
+   * @param weight the weight given to scores returned by the matcher
+   */
+  private void add(FeatureMatcher matcher, double weight) {
+    Assert.isTrue(weight >= 0);
+    if (weight == 0) {
+        return;
+    }
+    matcherToWeightMap.put(matcher, new Double(weight));
+  }
+
+  private Map matcherToWeightMap = new HashMap();
+
+  /**
+   * Searches a collection of candidate features for those that match the given
+   * target feature, using each FeatureMatcher.
+   * @param target the feature to match
+   * @param candidates the features to search for matches
+   * @return the candidates that pass at least one FeatureMatcher. Each score is
+   * a weighted average of the scores from the FeatureMatchers.
+   */
+    @Override
+  public Matches match(Feature target, FeatureCollection candidates) {
+    if (weightTotal() == 0) { return new Matches(candidates.getFeatureSchema()); }
+    Map matcherToMatchesMap = matcherToMatchesMap(target, candidates);
+    Map featureToScoreMap = featureToScoreMap(matcherToMatchesMap);
+    return toMatches(featureToScoreMap, candidates.getFeatureSchema());
+  }
+
+  private Matches toMatches(Map featureToScoreMap, FeatureSchema schema) {
+    Matches matches = new Matches(schema);
+    for (Iterator i = featureToScoreMap.keySet().iterator(); i.hasNext(); ) {
+      Feature feature = (Feature) i.next();
+      double score = ((Double) featureToScoreMap.get(feature)).doubleValue();
+      matches.add(feature, score);
+    }
+    return matches;
+  }
+
+  private Map matcherToMatchesMap(Feature feature, FeatureCollection candidates) {
+    HashMap matcherToMatchesMap = new HashMap();
+    for (Iterator i = matcherToWeightMap.keySet().iterator(); i.hasNext(); ) {
+      FeatureMatcher matcher = (FeatureMatcher) i.next();
+      if (normalizedWeight(matcher) == 0) { continue; }
+      matcherToMatchesMap.put(matcher, matcher.match(feature, candidates));
+    }
+    return matcherToMatchesMap;
+  }
+
+  private Map featureToScoreMap(Map matcherToMatchesMap) {
+    TreeMap featureToScoreMap = new TreeMap();
+    for (Iterator i = matcherToMatchesMap.keySet().iterator(); i.hasNext(); ) {
+      FeatureMatcher matcher = (FeatureMatcher) i.next();
+      Matches matches = (Matches) matcherToMatchesMap.get(matcher);
+      addToFeatureToScoreMap(matches, matcher, featureToScoreMap);
+    }
+    return featureToScoreMap;
+  }
+
+  private void addToFeatureToScoreMap(Matches matches, FeatureMatcher matcher,
+                                      Map featureToScoreMap) {
+    for (int i = 0; i < matches.size(); i++) {
+      double score = matches.getScore(i) * normalizedWeight(matcher);
+      addToFeatureToScoreMap(matches.getFeature(i), score, featureToScoreMap);
+    }
+  }
+
+  private void addToFeatureToScoreMap(Feature feature, double score, Map featureToScoreMap) {
+    Double oldScore = (Double) featureToScoreMap.get(feature);
+    if (oldScore == null) { oldScore = new Double(0); }
+    featureToScoreMap.put(feature, new Double(oldScore.doubleValue() + score));
+  }
+
+  private double normalizedWeight(FeatureMatcher matcher) {
+    return ((Double)matcherToWeightMap.get(matcher)).doubleValue() / weightTotal();
+  }
+
+  private double weightTotal() {
+    double weightTotal = 0;
+    for (Iterator i = matcherToWeightMap.values().iterator(); i.hasNext(); ) {
+      Double weight = (Double) i.next();
+      weightTotal += weight.doubleValue();
+    }
+    return weightTotal;
+  }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/WindowFilter.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/WindowFilter.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/WindowFilter.java	(revision 28163)
@@ -0,0 +1,77 @@
+
+
+/*
+ * The JCS 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.geom.Envelope;
+import com.vividsolutions.jump.feature.Feature;
+import com.vividsolutions.jump.feature.FeatureCollection;
+import com.vividsolutions.jump.geom.EnvelopeUtil;
+
+/**
+ * Quickly filters out shapes that lie outside a given distance from the feature's
+ * envelope.
+ */
+public class WindowFilter implements FeatureMatcher {
+
+  /**
+   * Creates a new WindowFilter, with envelope buffering.
+   * @param buffer for each feature, the window will be the envelope extended on each
+   * side by this amount
+   */
+  public WindowFilter(double buffer) {
+    this.buffer = buffer;
+  }
+
+  /**
+   * Creates a WindowFilter, with no envelope buffering.
+   */
+  public WindowFilter() {}
+
+  private double buffer;
+  /**
+   * Quickly filters out shapes that lie outside a given distance from the feature's
+   * envelope.
+   * @param target the feature to match
+   * @param candidates the features to search for matches
+   * @return the candidates with envelopes intersecting the window. Each will
+   * have a score of 1.
+   */
+    @Override
+  public Matches match(Feature target, FeatureCollection candidates) {
+    Envelope window = new Envelope(target.getGeometry().getEnvelopeInternal());
+    window = EnvelopeUtil.expand(window, buffer);
+    return new Matches(candidates.getFeatureSchema(), candidates.query(window));
+  }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/WindowMatcher.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/WindowMatcher.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/conflate/polygonmatch/WindowMatcher.java	(revision 28163)
@@ -0,0 +1,84 @@
+
+
+/*
+ * 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.geom.Envelope;
+import com.vividsolutions.jump.feature.Feature;
+import com.vividsolutions.jump.feature.FeatureCollection;
+import com.vividsolutions.jump.geom.EnvelopeUtil;
+
+/**
+ * Quickly filters out shapes that lie outside a given distance from the feature's
+ * envelope.
+ */
+public class WindowMatcher implements FeatureMatcher {
+
+  /**
+   * Creates a new WindowMatcher, with envelope buffering.
+   * @param buffer for each feature, the window will be the envelope extended on each
+   * side by this amount
+   */
+  public WindowMatcher(double buffer) {
+    setBuffer(buffer);
+  }
+
+  /**
+   * Creates a WindowMatcher, with no envelope buffering.
+   */
+  public WindowMatcher() {}
+
+  /**
+   * Sets the amount by which to buffer the envelope
+   * @param buffer for each feature, the window will be the envelope extended on each
+   * side by this amount
+   */
+  public void setBuffer(double buffer) { this.buffer = buffer; }
+
+  private double buffer;
+  /**
+   * Quickly filters out shapes that lie outside a given distance from the feature's
+   * envelope.
+   * @param target the feature to match
+   * @param candidates the features to search for matches
+   * @return the candidates with envelopes intersecting the window. Each will
+   * have a score of 1.
+   */
+    @Override
+  public Matches match(Feature target, FeatureCollection candidates) {
+    Envelope window = new Envelope(target.getGeometry().getEnvelopeInternal());
+    window = EnvelopeUtil.expand(window, buffer);
+    return new Matches(candidates.getFeatureSchema(), candidates.query(window));
+  }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/geom/Angle.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/geom/Angle.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jcs/geom/Angle.java	(revision 28163)
@@ -0,0 +1,176 @@
+
+/*
+ * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
+ * for visualizing and manipulating spatial features with geometry and attributes.
+ *
+ * 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.geom;
+
+import com.vividsolutions.jts.geom.Coordinate;
+
+
+/**
+ * Utility functions for working with angles.
+ */
+public class Angle {
+  public static final double PI_TIMES_2 = 2.0 * Math.PI;
+  public static final double PI_OVER_2 = Math.PI / 2.0;
+  public static final double PI_OVER_4 = Math.PI / 4.0;
+
+  /** General constant representing counterclockwise orientation */
+  public static int COUNTERCLOCKWISE = 0;
+
+  /** General constant representing clockwise orientation */
+  public static int CLOCKWISE = 1;
+
+  /** General constant representing no orientation */
+  public static int NONE = 2;
+
+  /**
+   * Converts from radians to degrees.
+   * @param radians an angle in radians
+   * @return the angle in degrees
+   */
+  public static double toDegrees(double radians) {
+      return (radians * 180) / (Math.PI);
+  }
+
+  /**
+   * Returns the angle of the vector from p0 to p1. The angle will be between
+   * -Pi and Pi.
+   * @param p0 the angle of one vector
+   * @param p1 the angle of the other vector
+   * @return the angle (in radians) that p0p1 makes with the positive x-axis.
+   */
+  public static double angle(Coordinate p0, Coordinate p1) {
+      double dx = p1.x - p0.x;
+      double dy = p1.y - p0.y;
+
+      return Math.atan2(dy, dx);
+  }
+
+  /**
+   * Converts from degrees to radians.
+   * @param angleDegrees an angle in degrees
+   * @return the angle in radians
+   */
+  public static double toRadians(double angleDegrees) {
+      return (angleDegrees * Math.PI) / 180.0;
+  }
+
+  /**
+   * Returns the angle between two vectors. Will be between 0 and Pi.
+   * @param tail the tail of each vector
+   * @param tip1 the tip of one vector
+   * @param tip2 the tip of the other vector
+   * @return the angle between tail-tip1 and tail-tip2
+   */
+  public static double angleBetween(Coordinate tail, Coordinate tip1,
+      Coordinate tip2) {
+      double a1 = angle(tail, tip1);
+      double a2 = angle(tail, tip2);
+
+      return diff(a1, a2);
+  }
+
+  /**
+   * Computes the interior angle between two segments of a ring.
+   * The ring is assumed to be oriented in a clockwise direction.
+   * @param p0 a point of the ring
+   * @param p1 the next point of the ring
+   * @param p2 the next point of the ring
+   * @return the interior angle based at <code>p1</code>
+   */
+  public static double interiorAngle(Coordinate p0, Coordinate p1, Coordinate p2)
+  {
+    double anglePrev = Angle.angle(p1, p0);
+    double angleNext = Angle.angle(p1, p2);
+    return Math.abs(angleNext - anglePrev);
+  }
+
+  /**
+   * Returns whether an angle must turn clockwise or counterclockwise
+   * to overlap another angle.
+   * @param a1 an angle in radians
+   * @param a2 an angle in radians
+   * @return whether a1 must turn CLOCKWISE, COUNTERCLOCKWISE or NONE to
+   * overlap a2.
+   */
+  public static int getTurn(double a1, double a2) {
+      double crossproduct = Math.sin(a2 - a1);
+
+      if (crossproduct > 0) {
+          return COUNTERCLOCKWISE;
+      }
+
+      if (crossproduct < 0) {
+          return CLOCKWISE;
+      }
+
+      return NONE;
+  }
+
+  /**
+   * Computes the normalized value of an angle, which is the
+   * equivalent angle lying between -Pi and Pi.
+   *
+   * @param angle the angle to compute the normalized value of
+   * @return the normalized value of the angle
+   */
+  public static double normalize(double angle)
+  {
+    while (angle > Math.PI)
+      angle -= PI_TIMES_2;
+    while (angle < -Math.PI)
+      angle += PI_TIMES_2;
+    return angle;
+  }
+  /**
+   * Returns the angle between two vectors.
+   * @param a1 the angle of one vector, between -Pi and Pi
+   * @param a2 the angle of the other vector, between -Pi and Pi
+   * @return the angle (in radians) between the two vectors, between 0 and Pi
+   */
+  public static double diff(double a1, double a2) {
+      double da;
+
+      if (a1 < a2) {
+          da = a2 - a1;
+      } else {
+          da = a1 - a2;
+      }
+
+      if (da > Math.PI) {
+          da = (2 * Math.PI) - da;
+      }
+
+      return da;
+  }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/feature/AbstractBasicFeature.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/feature/AbstractBasicFeature.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/feature/AbstractBasicFeature.java	(revision 28163)
@@ -0,0 +1,186 @@
+package com.vividsolutions.jump.feature;
+
+import java.util.*;
+
+import com.vividsolutions.jts.geom.Geometry;
+
+/**
+ * Subclasses need to implement only the four remaining Feature methods:
+ * #getAttribute, #setAttribute, #getAttributes, #setAttributes
+ */
+public abstract class AbstractBasicFeature implements Feature {
+    
+    private FeatureSchema schema;
+    private int id;
+    /**
+     * A low-level accessor that is not normally used.
+     */    
+    public void setSchema(FeatureSchema schema) {
+        this.schema = schema;
+    }            
+
+    /**
+     *  Creates a new Feature based on the given metadata.
+     *
+     *@param  featureSchema  the metadata containing information on
+     *      each column
+     */
+    public AbstractBasicFeature(FeatureSchema featureSchema) {
+        id = FeatureUtil.nextID();
+        this.schema = featureSchema;
+    }
+
+    /**
+     * Returns a number that uniquely identifies this feature. This number is not
+     * persistent.
+     * @return n, where this feature is the nth Feature created by this application
+     */
+    public int getID() {
+        return id;
+    }
+
+    /**
+     *  Sets the specified attribute.
+     *
+     *@param  attributeName  the name of the attribute to set
+     *@param  newAttribute   the new attribute
+     */
+    public void setAttribute(String attributeName, Object newAttribute) {
+        setAttribute(schema.getAttributeIndex(attributeName),
+            newAttribute);
+    }
+
+    /**
+     *  Convenience method for setting the spatial attribute. JUMP Workbench
+     * PlugIns and CursorTools should not use this method directly, but should use an
+     * EditTransaction, so that the proper events are fired.
+     *
+     *@param  geometry  the new spatial attribute
+     */
+    public void setGeometry(Geometry geometry) {
+        setAttribute(schema.getGeometryIndex(), geometry);
+    }
+
+    /**
+     *  Returns the specified attribute.
+     *
+     *@param  name  the name of the attribute to get
+     *@return the attribute
+     */
+    public Object getAttribute(String name) {
+        return getAttribute(schema.getAttributeIndex(name));
+    }
+
+    //<<TODO:DOC>>Update JavaDoc -- the attribute need not be a String [Jon Aquino]
+
+    /**
+     *  Returns a String attribute. The attribute at the given index must be a
+     *  String.
+     *
+     *@param  attributeIndex  the array index of the attribute
+     *@return                 the String attribute at the given index
+     */
+    public String getString(int attributeIndex) {
+        // return (String) attributes[attributeIndex];
+        //Dave B changed this so you can convert Integers->Strings
+        //Automatic conversion of integers to strings is a bit hack-like.
+        //Instead one should do #getAttribute followed by #toString.
+        //#getString should be strict: it should throw an Exception if it is used
+        //on a non-String attribute. [Jon Aquino]
+        Object result = getAttribute(attributeIndex);
+
+        //We used to eat ArrayOutOfBoundsExceptions here. I've removed this behaviour
+        //because ArrayOutOfBoundsExceptions are bugs and should be exposed. [Jon Aquino]        
+
+        //Is it valid for an attribute to be null? If not, we should put an
+        //Assert here [Jon Aquino]
+        if (result != null) {
+            return result.toString();
+        } else {
+            return "";
+        }
+    }
+
+    /**
+     *  Returns a integer attribute.
+     *
+     *@param  attributeIndex the index of the attribute to retrieve
+     *@return                the integer attribute with the given name
+     */
+    public int getInteger(int attributeIndex) {
+        return ((Integer) getAttribute(attributeIndex)).intValue();
+    }
+
+    /**
+     *  Returns a double attribute.
+     *
+     *@param  attributeIndex the index of the attribute to retrieve
+     *@return                the double attribute with the given name
+     */
+    public double getDouble(int attributeIndex) {
+        return ((Double) getAttribute(attributeIndex)).doubleValue();
+    }
+
+    //<<TODO:DOC>>Update JavaDoc -- the attribute need not be a String [Jon Aquino]
+
+    /**
+     *  Returns a String attribute. The attribute with the given name must be a
+     *  String.
+     *
+     *@param  attributeName  the name of the attribute to retrieve
+     *@return                the String attribute with the given name
+     */
+    public String getString(String attributeName) {
+        return getString(schema.getAttributeIndex(attributeName));
+    }
+
+    /**
+     *  Convenience method for returning the spatial attribute.
+     *
+     *@return    the feature's spatial attribute
+     */
+    public Geometry getGeometry() {
+        return (Geometry) getAttribute(schema.getGeometryIndex());
+    }
+
+    /**
+     *  Returns the feature's metadata
+     *
+     *@return    the metadata describing the names and types of the attributes
+     */
+    public FeatureSchema getSchema() {
+        return schema;
+    }
+
+    /**
+     * Clones this Feature. The geometry will also be cloned.
+     * @return a new Feature with the same attributes as this Feature
+     */
+    public Object clone() {
+        return clone(true);
+    }
+
+    /**
+     * Clones this Feature.
+     * @param deep whether or not to clone the geometry
+     * @return a new Feature with the same attributes as this Feature
+     */
+    public Feature clone(boolean deep) {
+        Feature clone = new BasicFeature(schema);
+
+        for (int i = 0; i < schema.getAttributeCount(); i++) {
+            if (schema.getAttributeType(i) == AttributeType.GEOMETRY) {
+                clone.setAttribute(i,
+                    deep ? getGeometry().clone() : getGeometry());
+            } else {
+                clone.setAttribute(i, getAttribute(i));
+            }
+        }
+
+        return clone;
+    }
+
+    public int compareTo(Object o) {
+        return getGeometry().compareTo(((Feature) o).getGeometry());
+    }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/feature/AttributeType.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/feature/AttributeType.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/feature/AttributeType.java	(revision 28163)
@@ -0,0 +1,117 @@
+/*
+ * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI 
+ * for visualizing and manipulating spatial features with geometry and attributes.
+ *
+ * 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.jump.feature;
+
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.Point;
+import com.vividsolutions.jts.geom.PrecisionModel;
+
+
+/**
+ * A type for the attributes of a feature.
+ * @see FeatureSchema.
+ */
+public class AttributeType {
+    private static HashMap nameToAttributeTypeMap = new HashMap();
+    public static Collection allTypes() {
+        return nameToAttributeTypeMap.values();
+    }
+    /** For strings */
+    public final static AttributeType STRING = new AttributeType("STRING", String.class);
+
+    /** For spatial data */
+    public final static AttributeType GEOMETRY = new AttributeType("GEOMETRY", Geometry.class);
+
+    /** For long values (64-bit) */
+    public final static AttributeType INTEGER = new AttributeType("INTEGER", Integer.class);
+    
+    public final static AttributeType DATE= new AttributeType("DATE", Date.class);
+
+    //<<TODO:IMPROVE>> If it's a long, perhaps we should name it LONG instead
+    //of INTEGER. [Jon Aquino] 
+    //Why was it necessary to use long values? [Jon Aquino]
+
+    /** For double-precision values (64-bit) */
+    public final static AttributeType DOUBLE = new AttributeType("DOUBLE", Double.class);
+            
+    public final static AttributeType OBJECT = new AttributeType("OBJECT", Object.class );
+
+    private String name;
+
+    private AttributeType(String name, Class javaClass) {
+        this.name = name;
+        this.javaClass = javaClass;
+        nameToAttributeTypeMap.put(name, this);
+    }
+
+    public String toString() {
+        return name;
+    }
+
+    /**
+     * Converts a type name to an AttributeType.
+     * @param typename the name of the AttributeType to retrieve
+     * @return the corresponding AttributeType
+     * @throws InvalidAttributeTypeException if the type name is unrecognized
+     */
+    public final static AttributeType toAttributeType(String name) {
+        AttributeType type = (AttributeType) nameToAttributeTypeMap.get(name);
+
+        if (type == null) {
+            throw new IllegalArgumentException();
+        }
+
+        return type;
+    }
+    
+	public Class toJavaClass() {
+		return javaClass;
+	}
+	
+	private Class javaClass;
+
+    public static AttributeType toAttributeType(Class javaClass) {
+        for (Iterator i = allTypes().iterator(); i.hasNext(); ) {
+            AttributeType type = (AttributeType) i.next();
+            if (type.toJavaClass() == javaClass) {
+                return type;
+            }
+        }
+        return null;        
+    }
+    
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/feature/BasicFeature.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/feature/BasicFeature.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/feature/BasicFeature.java	(revision 28163)
@@ -0,0 +1,101 @@
+
+/*
+ * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI 
+ * for visualizing and manipulating spatial features with geometry and attributes.
+ *
+ * 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.jump.feature;
+
+import java.util.*;
+
+import com.vividsolutions.jts.geom.Geometry;
+
+
+/**
+ *  A geographic feature, defined in the OGC Abstract Specification to be "an
+ *  abstraction of a real world phenomenon; it is a geographic feature if it is
+ *  associated with a location relative to the Earth".
+ *
+ *@version    $Revision: 1.3 $
+ *@author     $Author: jaquino $
+ */
+
+//CVS will automatically fill in the Author and Revision above. Unfortunately
+//it requires the ugly dollar signs. [Jon Aquino]
+//For more information on how to write JavaDoc, see
+//http://java.sun.com/j2se/javadoc/writingdoccomments/
+//[Jon Aquino]
+public class BasicFeature extends AbstractBasicFeature {
+
+    private Object[] attributes;
+
+    public BasicFeature(FeatureSchema featureSchema) {
+        super(featureSchema);
+        attributes = new Object[featureSchema.getAttributeCount()];        
+    }
+
+    /**
+     * A low-level accessor that is not normally used. It is called by ViewSchemaPlugIn.
+     */
+    public void setAttributes(Object[] attributes) {
+        this.attributes = attributes;
+    }
+
+    /**
+     *  Sets the specified attribute.
+     *
+     *@param  attributeIndex  the array index at which to put the new attribute
+     *@param  newAttribute    the new attribute
+     */
+    public void setAttribute(int attributeIndex, Object newAttribute) {
+        attributes[attributeIndex] = newAttribute;
+    }
+
+    /**
+     *  Returns the specified attribute.
+     *
+     *@param  i the index of the attribute to get
+     *@return the attribute
+     */
+    public Object getAttribute(int i) {
+        return attributes[i];
+        //We used to eat ArrayOutOfBoundsExceptions here. I've removed this behaviour
+        //because ArrayOutOfBoundsExceptions are bugs and should be exposed. [Jon Aquino]
+    }
+
+    /**
+     * A low-level accessor that is not normally used. It is called by ViewSchemaPlugIn.
+     */
+    public Object[] getAttributes() {
+        return attributes;
+    }
+
+
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/feature/Feature.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/feature/Feature.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/feature/Feature.java	(revision 28163)
@@ -0,0 +1,104 @@
+package com.vividsolutions.jump.feature;
+
+import com.vividsolutions.jts.geom.Geometry;
+
+public interface Feature extends Cloneable, Comparable {
+	/**
+	 * A low-level accessor that is not normally used. attributes may have a different
+     * length than the current attributes.
+	 */
+	public abstract void setAttributes(Object[] attributes);
+	/**
+	 * A low-level accessor that is not normally used.
+	 */
+	public abstract void setSchema(FeatureSchema schema);
+	/**
+	 * Returns a number that uniquely identifies this feature. This number is not
+	 * persistent. (Implementors can obtain an ID from FeatureUtil#nextID).
+	 * @return n, where this feature is the nth Feature created by this application
+	 */
+	public abstract int getID();
+	/**
+	 *  Sets the specified attribute.
+	 *
+	 *@param  attributeIndex  the array index at which to put the new attribute
+	 *@param  newAttribute    the new attribute
+	 */
+	public abstract void setAttribute(int attributeIndex, Object newAttribute);
+	/**
+	 *  Sets the specified attribute.
+	 *
+	 *@param  attributeName  the name of the attribute to set
+	 *@param  newAttribute   the new attribute
+	 */
+	public abstract void setAttribute(
+		String attributeName,
+		Object newAttribute);
+	/**
+	 *  Convenience method for setting the spatial attribute. JUMP Workbench
+	 * PlugIns and CursorTools should not use this method directly, but should use an
+	 * EditTransaction, so that the proper events are fired.
+	 *
+	 *@param  geometry  the new spatial attribute
+	 */
+	public abstract void setGeometry(Geometry geometry);
+	/**
+	 *  Returns the specified attribute.
+	 *
+	 *@param  i the index of the attribute to get
+	 *@return the attribute
+	 */
+	public abstract Object getAttribute(int i);
+	/**
+	 *  Returns the specified attribute.
+	 *
+	 *@param  name  the name of the attribute to get
+	 *@return the attribute
+	 */
+	public abstract Object getAttribute(String name);
+	//<<TODO:DOC>>Update JavaDoc -- the attribute need not be a String [Jon Aquino]
+	public abstract String getString(int attributeIndex);
+	/**
+	 *  Returns a integer attribute.
+	 *
+	 *@param  attributeIndex the index of the attribute to retrieve
+	 *@return                the integer attribute with the given name
+	 */
+	public abstract int getInteger(int attributeIndex);
+	/**
+	 *  Returns a double attribute.
+	 *
+	 *@param  attributeIndex the index of the attribute to retrieve
+	 *@return                the double attribute with the given name
+	 */
+	public abstract double getDouble(int attributeIndex);
+	//<<TODO:DOC>>Update JavaDoc -- the attribute need not be a String [Jon Aquino]
+	public abstract String getString(String attributeName);
+	/**
+	 *  Convenience method for returning the spatial attribute.
+	 *
+	 *@return    the feature's spatial attribute
+	 */
+	public abstract Geometry getGeometry();
+	/**
+	 *  Returns the feature's metadata
+	 *
+	 *@return    the metadata describing the names and types of the attributes
+	 */
+	public abstract FeatureSchema getSchema();
+	/**
+	 * Clones this Feature. The geometry will also be cloned.
+	 * @return a new Feature with the same attributes as this Feature
+	 */
+	public abstract Object clone();
+	/**
+	 * Clones this Feature.
+	 * @param deep whether or not to clone the geometry
+	 * @return a new Feature with the same attributes as this Feature
+	 */
+	public abstract Feature clone(boolean deep);
+	/**
+	 * A low-level accessor that is not normally used.
+	 */
+	public abstract Object[] getAttributes();
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/feature/FeatureCollection.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/feature/FeatureCollection.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/feature/FeatureCollection.java	(revision 28163)
@@ -0,0 +1,111 @@
+
+/*
+ * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI 
+ * for visualizing and manipulating spatial features with geometry and attributes.
+ *
+ * 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.jump.feature;
+
+import java.util.*;
+
+import com.vividsolutions.jts.geom.Envelope;
+
+
+/**
+ * A collection of Features, with a special method for querying the Features
+ * that lie within a given Envelope.
+ */
+public interface FeatureCollection {
+    /**
+     * Returns information about this FeatureCollection
+     * @return the types of the attributes of the features in this collection
+     */
+    FeatureSchema getFeatureSchema();
+
+    /**
+     * Returns the bounds of this collection.
+     * @return the smallest Envelope enclosing all the Features in this collection
+     */
+    Envelope getEnvelope();
+
+    /**
+     * Returns the number of features in this collection.
+     * @return the number of features in this collection
+     */
+    int size();
+
+    /**
+     * Returns whether this collection has no features.
+     * @return whether or not the size of this collection is 0
+     */
+    boolean isEmpty();
+
+    /**
+     * Returns an unmodifiable List of the features in this collection
+     * @return a read-only view of all the features
+     */
+    List<Feature> getFeatures();
+
+    /**
+     * Returns an Iterator over the features
+     * @return an Iterator over the features
+     */
+    Iterator iterator();
+
+    /**
+     * A quick search for features, using an envelope comparison.
+     * @param envelope the envelope to query against
+     * @return features whose envelopes intersect the given envelope
+     */
+    List query(Envelope envelope);
+
+    /**
+     * Adds a feature to this collection.
+     * @param feature a Feature to add to the end of this collection
+     */
+    void add(Feature feature);
+
+    void addAll(Collection features);
+
+    void removeAll(Collection features);
+
+    /**
+     * Removes a feature from this collection.
+     * @param feature a Feature to remove from this collection
+     */
+    void remove(Feature feature);
+
+    void clear();
+
+    /**
+     * @return the removed features
+     */
+    Collection remove(Envelope env);
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/feature/FeatureCollectionWrapper.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/feature/FeatureCollectionWrapper.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/feature/FeatureCollectionWrapper.java	(revision 28163)
@@ -0,0 +1,128 @@
+/*
+ * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI 
+ * for visualizing and manipulating spatial features with geometry and attributes.
+ *
+ * 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.jump.feature;
+
+import java.util.*;
+
+import com.vividsolutions.jts.geom.Envelope;
+import com.vividsolutions.jts.util.Assert;
+
+
+public abstract class FeatureCollectionWrapper implements FeatureCollection {
+    protected FeatureCollection fc;
+
+    public FeatureCollectionWrapper(FeatureCollection fc) {
+        this.fc = fc;
+    }
+
+    public FeatureCollection getUltimateWrappee() {
+        FeatureCollection currentWrappee = fc;
+        while (currentWrappee instanceof FeatureCollectionWrapper) {
+            currentWrappee = ((FeatureCollectionWrapper)currentWrappee).fc;
+        }
+        return currentWrappee;
+    }
+
+    public void checkNotWrappingSameClass() {
+        Assert.isTrue(!(fc instanceof FeatureCollectionWrapper &&
+            ((FeatureCollectionWrapper) fc).hasWrapper(getClass())));
+    }
+
+    public Collection remove(Envelope env) {
+        return fc.remove(env);
+    }
+
+    public boolean hasWrapper(Class c) {
+        Assert.isTrue(FeatureCollectionWrapper.class.isAssignableFrom(c));
+
+        if (c.isInstance(this)) {
+            return true;
+        }
+
+        return fc instanceof FeatureCollectionWrapper &&
+        ((FeatureCollectionWrapper) fc).hasWrapper(c);
+    }
+
+    public FeatureCollection getWrappee() {
+        return fc;
+    }
+
+    public FeatureSchema getFeatureSchema() {
+        return fc.getFeatureSchema();
+    }
+
+    public Envelope getEnvelope() {
+        return fc.getEnvelope();
+    }
+
+    public int size() {
+        return fc.size();
+    }
+
+    public boolean isEmpty() {
+        return fc.isEmpty();
+    }
+
+    public List getFeatures() {
+        return fc.getFeatures();
+    }
+
+    public Iterator iterator() {
+        return fc.iterator();
+    }
+
+    public List query(Envelope envelope) {
+        return fc.query(envelope);
+    }
+
+    public void add(Feature feature) {
+        fc.add(feature);
+    }
+
+    public void remove(Feature feature) {
+        fc.remove(feature);
+    }
+
+    public void addAll(Collection features) {
+        fc.addAll(features);
+    }
+
+    public void removeAll(Collection features) {
+        fc.removeAll(features);
+    }
+
+    public void clear() {
+        //Create a new ArrayList to avoid a ConcurrentModificationException. [Jon Aquino]
+        removeAll(new ArrayList(getFeatures()));
+    }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/feature/FeatureDataset.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/feature/FeatureDataset.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/feature/FeatureDataset.java	(revision 28163)
@@ -0,0 +1,196 @@
+
+/*
+ * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI 
+ * for visualizing and manipulating spatial features with geometry and attributes.
+ *
+ * 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.jump.feature;
+
+import java.util.*;
+
+import com.vividsolutions.jts.geom.Envelope;
+
+
+/**
+ * Basic implementation of FeatureCollection.
+ */
+public class FeatureDataset implements FeatureCollection {
+    private FeatureSchema featureSchema;
+
+    //<<TODO>> Possibly use hashtable to do spatial indexing [Jon Aquino]
+    private ArrayList features;
+    private Envelope envelope = null;
+
+    /**
+     * Creates a FeatureDataset, initialized with a group of Features.
+     * @param newFeatures an initial group of features to add to this FeatureDataset
+     * @param featureSchema the types of the attributes of the features in this collection
+     */
+    public FeatureDataset(Collection newFeatures, FeatureSchema featureSchema) {
+        features = new ArrayList(newFeatures);
+        this.featureSchema = featureSchema;
+    }
+
+    /**
+     * Creates a FeatureDataset.
+     * @param featureSchema the types of the attributes of the features in this collection
+     */
+    public FeatureDataset(FeatureSchema featureSchema) {
+        this(new ArrayList(), featureSchema);
+    }
+
+    public Feature getFeature(int index) {
+        return (Feature) features.get(index);
+    }
+
+    public FeatureSchema getFeatureSchema() {
+        return featureSchema;
+    }
+
+    /**
+     * Because the envelope is cached, the envelope may be incorrect if you
+     * later change a Feature's geometry using Feature#setGeometry.
+     */
+    public Envelope getEnvelope() {
+        if (envelope == null) {
+            envelope = new Envelope();
+
+            for (Iterator i = features.iterator(); i.hasNext();) {
+                Feature feature = (Feature) i.next();
+                envelope.expandToInclude(feature.getGeometry()
+                                                .getEnvelopeInternal());
+            }
+        }
+
+        return envelope;
+    }
+
+    public List getFeatures() {
+        return Collections.unmodifiableList(features);
+    }
+
+    public boolean isEmpty() {
+        return size() == 0;
+    }
+
+    /**
+     *@return    a List containing the features whose envelopes intersect the
+     *      given envelope
+     */
+
+    //<<TODO:DESIGN>> Perhaps return value should be a Set, not a List, because order
+    //doesn't matter. [Jon Aquino]
+    public List query(Envelope envelope) {
+        if (!envelope.intersects(getEnvelope())) {
+            return new ArrayList();
+        }
+
+        //<<TODO:NAMING>> Rename this method to getFeatures(Envelope), to parallel
+        //getFeatures() [Jon Aquino]
+        ArrayList queryResult = new ArrayList();
+
+        for (Iterator i = features.iterator(); i.hasNext();) {
+            Feature feature = (Feature) i.next();
+
+            if (feature.getGeometry().getEnvelopeInternal().intersects(envelope)) {
+                queryResult.add(feature);
+            }
+        }
+
+        return queryResult;
+    }
+
+    public void add(Feature feature) {
+        features.add(feature);
+        if (envelope != null) {
+            envelope.expandToInclude(feature.getGeometry().getEnvelopeInternal());
+        }
+    }
+
+    /**
+     * Returns whether or not this Feature is in this collection
+     * @return true if this feature is in this collection, as determined using
+     * Feature#equals
+     */
+    public boolean contains(Feature feature) {
+        return features.contains(feature);
+    }
+
+    /**
+     * Removes the features which intersect the given envelope
+     * @param env
+     */
+    public Collection remove(Envelope env) {
+        Collection features = query(env);
+        removeAll(features);
+
+        return features;
+    }
+
+    public void remove(Feature feature) {
+        features.remove(feature);
+        invalidateEnvelope();
+    }
+
+    /**
+     * Removes all features from this collection.
+     */
+    public void clear() {
+        invalidateEnvelope();
+        features.clear();
+    }
+
+    public int size() {
+        return features.size();
+    }
+
+    public Iterator iterator() {
+        return features.iterator();
+    }
+
+    public void invalidateEnvelope() {
+        envelope = null;
+    }
+
+    public void addAll(Collection features) {
+        this.features.addAll(features);
+        if (envelope != null) {
+            for (Iterator i = features.iterator(); i.hasNext(); ) {
+                Feature feature = (Feature) i.next();
+                envelope.expandToInclude(feature.getGeometry().getEnvelopeInternal());
+            }            
+        }
+    }
+
+    public void removeAll(Collection features) {
+        this.features.removeAll(features);
+        invalidateEnvelope();
+    }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/feature/FeatureSchema.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/feature/FeatureSchema.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/feature/FeatureSchema.java	(revision 28163)
@@ -0,0 +1,166 @@
+
+/*
+ * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI 
+ * for visualizing and manipulating spatial features with geometry and attributes.
+ *
+ * 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.jump.feature;
+
+import java.util.*;
+
+import com.vividsolutions.jts.util.Assert;
+
+
+public class FeatureSchema implements Cloneable {
+    //<<TODO:QUESTION>> Is this an efficient implementation? Must cast the Integer to
+    //an int. [Jon Aquino]
+    private HashMap attributeNameToIndexMap = new HashMap();
+    private int geometryIndex = -1;
+    private int attributeCount = 0;
+    private ArrayList attributeNames = new ArrayList();
+    private ArrayList attributeTypes = new ArrayList();
+
+    public FeatureSchema() {
+    }
+
+    /**
+     *@param  attributeName              case-sensitive
+     *@throws  IllegalArgumentException  if attributeName is unrecognized
+     */
+    public int getAttributeIndex(String attributeName) {
+        //<<TODO:RECONSIDER>> Attribute names are currently case sensitive.
+        //I wonder whether or not this is desirable. [Jon Aquino]
+        Integer index = (Integer) attributeNameToIndexMap.get(attributeName);
+
+        if (index == null) {
+            throw new IllegalArgumentException("Unrecognized attribute name: " +
+                attributeName);
+        }
+
+        return index.intValue();
+    }
+
+    /**
+     * Returns whether this FeatureSchema has an attribute with this name
+     * @param attributeName the name to look up
+     * @return whether this FeatureSchema has an attribute with this name
+     */
+    public boolean hasAttribute(String attributeName) {
+        return attributeNameToIndexMap.get(attributeName) != null;
+    }
+
+    /**
+     *@return    the attribute index of the Geometry, or -1 if there is no
+     *      Geometry attribute
+     */
+    public int getGeometryIndex() {
+        return geometryIndex;
+    }
+
+    public String getAttributeName(int attributeIndex) {
+        return (String) attributeNames.get(attributeIndex);
+    }
+
+    public AttributeType getAttributeType(int attributeIndex) {
+        return (AttributeType) attributeTypes.get(attributeIndex);
+    }
+
+    public AttributeType getAttributeType(String attributeName) {
+        return getAttributeType(getAttributeIndex(attributeName));
+    }
+
+    public int getAttributeCount() {
+        return attributeCount;
+    }
+
+    /**
+     *@param  geometry  true if the attribute is a Geometry
+     */
+    public void addAttribute(String attributeName, AttributeType attributeType) {
+        if (AttributeType.GEOMETRY == attributeType) {
+            //<<TODO:QUESTION>> Using Assert from JTS package ok? [Jon Aquino]
+            Assert.isTrue(geometryIndex == -1);
+            geometryIndex = attributeCount;
+        }
+
+        attributeNames.add(attributeName);
+        attributeNameToIndexMap.put(attributeName, new Integer(attributeCount));
+        attributeTypes.add(attributeType);
+        attributeCount++;
+    }
+
+    /**
+     *@return    true if the two metadatas have the same attribute names with the
+     *      same types in the same order
+     */
+    public boolean equals(Object other) {
+        return this.equals(other, false);
+    }
+    public boolean equals(Object other, boolean orderMatters) {
+        if (!(other instanceof FeatureSchema)) {
+            return false;
+        }
+
+        FeatureSchema otherFeatureSchema = (FeatureSchema) other;
+
+        if (attributeNames.size() != otherFeatureSchema.attributeNames.size()) {
+            return false;
+        }
+
+        for (int i = 0; i < attributeNames.size(); i++) {
+            String attributeName = (String) attributeNames.get(i);
+            
+            if (!otherFeatureSchema.attributeNames.contains(attributeName)) {
+                return false;
+            }
+            
+            if (orderMatters && !otherFeatureSchema.attributeNames.get(i).equals(attributeName)) {
+                return false;
+            }
+
+            if (getAttributeType(attributeName) != otherFeatureSchema.getAttributeType(
+                        attributeName)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    public Object clone() {
+        try {
+            return super.clone();
+        } catch (CloneNotSupportedException ex) {
+            Assert.shouldNeverReachHere();
+
+            return null;
+        }
+    }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/feature/FeatureUtil.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/feature/FeatureUtil.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/feature/FeatureUtil.java	(revision 28163)
@@ -0,0 +1,91 @@
+package com.vividsolutions.jump.feature;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+import com.vividsolutions.jts.geom.Geometry;
+
+public class FeatureUtil {
+	
+	/**
+	 * Creates a new Feature from the given Geometry, with nominal values for
+	 * the attributes.
+	 * @param g the Geometry to convert
+	 * @param schema metadata for the Feature to create
+	 * @return a new Feature containing the Geometry and default values for the
+	 * attributes
+	 */
+//	public static Feature toFeature(Geometry g, FeatureSchema schema) {
+//	    Feature feature = new BasicFeature(schema);
+//	    feature.setGeometry(g);
+//	    return feature;
+//	}
+
+	public static List toGeometries(Collection features) {
+	    ArrayList list = new ArrayList();
+	
+	    for (Iterator i = features.iterator(); i.hasNext();) {
+	        Feature feature = (Feature) i.next();
+	        list.add(feature.getGeometry());
+	    }
+	
+	    return list;
+	}
+	
+	public static class IDComparator implements Comparator {
+		/**
+		 *  Compares two Features for order based on their ID.
+		 *
+		 *@param  o1  the first <code>Feature</code>
+		 *@param  o2  the second <code>Feature</code>
+		 *
+		 *@return    a negative integer, zero, or a positive integer
+		 *        as the first argument is less than, equal to,
+		 *        or greater than the second.
+		 */
+		public int compare(Object o1, Object o2) {
+			Feature f1 = (Feature) o1;
+			Feature f2 = (Feature) o2;
+
+			if (f1.getID() < f2.getID()) {
+				return -1;
+			}
+
+			if (f1.getID() > f2.getID()) {
+				return 1;
+			}
+
+			return 0;
+		}
+	}	
+	
+	private static int lastID = 0;
+	
+	public static int nextID() { return ++lastID; }
+
+    /**
+     * Although Feature implements Cloneable, this method is useful
+     * when the two Features are implemented with different classes.
+     */
+    public static void copyAttributes(Feature a, Feature b) {
+        for (int i = 0; i < a.getSchema().getAttributeCount(); i++) {
+            b.setAttribute(i, a.getAttribute(i));
+        }
+    }
+
+    public static boolean areAllNonSpatialAttributesNull(Feature feature) {
+        for (int i = 0; i < feature.getSchema().getAttributeCount(); i++) {
+            if (AttributeType.GEOMETRY == feature.getSchema().getAttributeType(i)) {
+                continue;
+            }
+            if (feature.getAttribute(i) != null) {
+                return false;
+            }
+        }
+        return true;
+    }	
+	
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/feature/IndexedFeatureCollection.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/feature/IndexedFeatureCollection.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/feature/IndexedFeatureCollection.java	(revision 28163)
@@ -0,0 +1,114 @@
+
+/*
+ * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI 
+ * for visualizing and manipulating spatial features with geometry and attributes.
+ *
+ * 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.jump.feature;
+
+import java.util.*;
+
+import com.vividsolutions.jts.geom.Envelope;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.index.SpatialIndex;
+import com.vividsolutions.jts.index.strtree.STRtree;
+
+
+/**
+ *  An IndexedFeatureCollection creates a new collection which is backed by a
+ *  FeatureCollection, but which is indexed for query purposes.
+ */
+public class IndexedFeatureCollection extends FeatureCollectionWrapper {
+    private SpatialIndex spatialIndex;
+
+    public IndexedFeatureCollection(FeatureCollection fc) {
+        //Based on tests on Victoria ICI data, 10 is an optimum node-capacity for
+        //fast queries. [Jon Aquino]
+        this(fc, new STRtree(10));
+    }
+
+    public IndexedFeatureCollection(FeatureCollection fc,
+        SpatialIndex spatialIndex) {
+        super(fc);
+        this.spatialIndex = spatialIndex;
+        createIndex();
+    }
+
+    public void add(Feature feature) {
+        throw new UnsupportedOperationException("Index cannot be modified");
+    }
+
+    public void remove(Feature feature) {
+        throw new UnsupportedOperationException("Index cannot be modified");
+    }
+
+    public List query(Envelope env) {
+        //System.out.println("FC size = " + base.size());
+        //System.out.println("Quadtree size = " + quadtreeIndex.size());
+        // index query returns list of *potential* overlaps (e.g. it is a primary filter)
+        List candidate = spatialIndex.query(env);
+
+        // filter out only Features where envelope actually intersects
+        List result = new ArrayList();
+
+        for (Iterator i = candidate.iterator(); i.hasNext();) {
+            Feature f = (Feature) i.next();
+            Geometry g = f.getGeometry();
+
+            if (env.intersects(g.getEnvelopeInternal())) {
+                result.add(f);
+            }
+        }
+
+        return result;
+    }
+
+    private void createIndex() {
+        int count = 0; // debugging
+
+        for (Iterator i = iterator(); i.hasNext();) {
+            Feature f = (Feature) i.next();
+            spatialIndex.insert(f.getGeometry().getEnvelopeInternal(), f);
+            count++;
+        }
+    }
+
+    public void addAll(Collection features) {
+        throw new UnsupportedOperationException("Index cannot be modified");
+    }
+
+    public Collection remove(Envelope env) {
+        throw new UnsupportedOperationException("Index cannot be modified");
+    }
+
+    public void removeAll(Collection features) {
+        throw new UnsupportedOperationException("Index cannot be modified");
+    }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/geom/CoordUtil.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/geom/CoordUtil.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/geom/CoordUtil.java	(revision 28163)
@@ -0,0 +1,152 @@
+
+/*
+ * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI 
+ * for visualizing and manipulating spatial features with geometry and attributes.
+ *
+ * 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.jump.geom;
+
+import java.awt.geom.Point2D;
+import java.util.Collection;
+import java.util.Iterator;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.util.Assert;
+import com.vividsolutions.jump.util.MathUtil;
+
+
+/**
+ * Utility functions for working with Coordinates.
+ */
+public class CoordUtil {
+    /**
+     * Returns the average of two Coordinates.
+     * @param c1 one coordinate
+     * @param c2 another coordinate
+     * @return a new Coordinate with the average x and average y
+     */
+    public static Coordinate average(Coordinate c1, Coordinate c2) {
+        return new Coordinate(MathUtil.avg(c1.x, c2.x), MathUtil.avg(c1.y, c2.y));
+    }
+
+    /**
+     * @param coordinates not empty
+     */
+    public static Coordinate average(Collection coordinates) {
+        Assert.isTrue(!coordinates.isEmpty());
+
+        double xSum = 0;
+        double ySum = 0;
+
+        for (Iterator i = coordinates.iterator(); i.hasNext();) {
+            Coordinate coordinate = (Coordinate) i.next();
+            xSum += coordinate.x;
+            ySum += coordinate.y;
+        }
+
+        return new Coordinate(xSum / coordinates.size(),
+            ySum / coordinates.size());
+    }
+
+    /**
+     * @param coordinates not empty
+     */
+    public static Coordinate closest(Collection coordinates, Coordinate p) {
+        Assert.isTrue(!coordinates.isEmpty());
+
+        Coordinate closest = (Coordinate) coordinates.iterator().next();
+
+        for (Iterator i = coordinates.iterator(); i.hasNext();) {
+            Coordinate candidate = (Coordinate) i.next();
+
+            if (p.distance(candidate) < p.distance(closest)) {
+                closest = candidate;
+            }
+        }
+
+        return closest;
+    }
+
+    /**
+     * Adds two coordinates.
+     * @param c1 the first coordinate
+     * @param c2 the second coordinate
+     * @return a new coordinate: c1 + c2
+     */
+    public static Coordinate add(Coordinate c1, Coordinate c2) {
+        return new Coordinate(c1.x + c2.x, c1.y + c2.y);
+    }
+
+    /**
+     * Subtracts two coordinates.
+     * @param c1 the first coordinate
+     * @param c2 the second coordinate
+     * @return a new coordinate: c1 - c2
+     */
+    public static Coordinate subtract(Coordinate c1, Coordinate c2) {
+        return new Coordinate(c1.x - c2.x, c1.y - c2.y);
+    }
+
+    /**
+     * Multiplies a scalar and a coordinate.
+     * @param d the scalar
+     * @param c the coordinate
+     * @return a new coordinate: d * c
+     */
+    public static Coordinate multiply(double d, Coordinate c) {
+        return new Coordinate(d * c.x, d * c.y);
+    }
+
+    /**
+     * Divides a coordinate by a scalar.
+     * @param c the coordinate
+     * @param d the scalar   *
+     * @return a new coordinate: c / d
+     */
+    public static Coordinate divide(Coordinate c, double d) {
+        return new Coordinate(c.x / d, c.y / d);
+    }
+
+    public static Coordinate toCoordinate(Point2D point) {
+        return new Coordinate(point.getX(), point.getY());
+    }
+
+    public static Point2D toPoint2D(Coordinate coordinate) {
+        return new Point2D.Double(coordinate.x, coordinate.y);
+    }
+    
+    public static Point2D add(Point2D a, Point2D b) {
+        return new Point2D.Double(a.getX() + b.getX(), a.getY() + b.getY());
+    }
+
+    public static Point2D subtract(Point2D a, Point2D b) {
+        return new Point2D.Double(a.getX() - b.getX(), a.getY() - b.getY());
+    }    
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/geom/EnvelopeUtil.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/geom/EnvelopeUtil.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/geom/EnvelopeUtil.java	(revision 28163)
@@ -0,0 +1,131 @@
+
+/*
+ * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI 
+ * for visualizing and manipulating spatial features with geometry and attributes.
+ *
+ * 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.jump.geom;
+
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jump.util.MathUtil;
+
+
+/**
+ * Utility functions for {@link Envelope}s.
+ */
+public class EnvelopeUtil {
+    private static GeometryFactory factory = new GeometryFactory();
+
+    /**
+     *  Expands an Envelope by a given distance.
+     * Both positive and negative distances are handled.
+     */
+    public static Envelope expand(Envelope env, double distance) {
+        /**
+         *  If creating a negative buffer, check if Envelope becomes null (0-size)
+         */
+        if (distance < 0) {
+            double minSize = 2.0 * -distance;
+
+            if (env.getWidth() < minSize) {
+                return new Envelope();
+            }
+
+            if (env.getHeight() < minSize) {
+                return new Envelope();
+            }
+        }
+
+        return new Envelope(env.getMinX() - distance, env.getMaxX() + distance,
+            env.getMinY() - distance, env.getMaxY() + distance);
+    }
+
+    public static void translate(Envelope e, Coordinate displacement) {
+        if (e.isNull()) {
+            return;
+        }
+
+        e.init(e.getMinX() + displacement.x, e.getMaxX() + displacement.x,
+            e.getMinY() + displacement.y, e.getMaxY() + displacement.y);
+    }
+
+    /**
+     * @param minExtent the minimum buffer distance
+     * @param extentFraction the buffer distance expressed as a fraction of the
+     * average envelope extent
+     */
+    public static Envelope bufferByFraction(Envelope originalEnvelope,
+        double extentFraction) {
+        Envelope bufferedEnvelope = new Envelope(originalEnvelope);
+        double averageExtent = (bufferedEnvelope.getWidth() +
+            bufferedEnvelope.getHeight()) / 2d;
+        double buffer = averageExtent * extentFraction;
+
+        if (averageExtent == 0) {
+            //Point feature. Set the buffer to something reasonable. [Jon Aquino]
+            buffer = 10;
+        }
+
+        bufferedEnvelope.expandToInclude(bufferedEnvelope.getMaxX() + buffer,
+            bufferedEnvelope.getMaxY() + buffer);
+        bufferedEnvelope.expandToInclude(bufferedEnvelope.getMinX() - buffer,
+            bufferedEnvelope.getMinY() - buffer);
+
+        return bufferedEnvelope;
+    }
+
+    public static Coordinate centre(Envelope e) {
+        return new Coordinate(MathUtil.avg(e.getMinX(), e.getMaxX()),
+            MathUtil.avg(e.getMinY(), e.getMaxY()));
+    }
+
+    public static Geometry toGeometry(Envelope envelope) {
+        if ((envelope.getWidth() == 0) && (envelope.getHeight() == 0)) {
+            return factory.createPoint(new Coordinate(envelope.getMinX(),
+                    envelope.getMinY()));
+        }
+
+        if ((envelope.getWidth() == 0) || (envelope.getHeight() == 0)) {
+            return factory.createLineString(new Coordinate[] {
+                    new Coordinate(envelope.getMinX(), envelope.getMinY()),
+                    new Coordinate(envelope.getMaxX(), envelope.getMaxY())
+                });
+        }
+
+        return factory.createPolygon(factory.createLinearRing(
+                new Coordinate[] {
+                    new Coordinate(envelope.getMinX(), envelope.getMinY()),
+                    new Coordinate(envelope.getMinX(), envelope.getMaxY()),
+                    new Coordinate(envelope.getMaxX(), envelope.getMaxY()),
+                    new Coordinate(envelope.getMaxX(), envelope.getMinY()),
+                    new Coordinate(envelope.getMinX(), envelope.getMinY())
+                }), null);
+    }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/task/TaskMonitor.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/task/TaskMonitor.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/task/TaskMonitor.java	(revision 28163)
@@ -0,0 +1,77 @@
+
+/*
+ * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI 
+ * for visualizing and manipulating spatial features with geometry and attributes.
+ *
+ * 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.jump.task;
+
+
+/**
+ * Provides a simple interface between an operation (or "task") and the
+ * application in which it executes. Enables the task to report its progress,
+ * and to check whether the application has requested that it be cancelled.
+ */
+public interface TaskMonitor {
+    /**
+     * Describes the status of the task.
+     * @param description a description of the progress of the overall task
+     */
+    public void report(String description);
+
+    /**
+     * Reports the number of items processed.
+     * @param itemsDone the number of items that have been processed
+     * @param totalItems the total number of items being processed, or -1 if the
+     * total number is not known
+     * @param itemDescription a one-word description of the items, such as "features"
+     */
+    public void report(int itemsDone, int totalItems, String itemDescription);
+
+    /**
+     * Reports an Exception that occurred. The task may choose to carry on.
+     * @param exception an Exception that occurred during the execution of the task.
+     */
+    public void report(Exception exception);
+
+    /**
+     * Notifies parties that the task will accept requests for cancellation
+     * (though the task is not obligated to cancel immediately, or at all
+     * for that matter).
+     */
+    public void allowCancellationRequests();
+
+    /**
+     * Checks whether a party has requested that the task be cancelled. However,
+     * the task is not obligated to cancel immediately (or at all).
+     * @return whether a party has requested that the task be cancelled
+     */
+    public boolean isCancelRequested();
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/util/Block.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/util/Block.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/util/Block.java	(revision 28163)
@@ -0,0 +1,21 @@
+package com.vividsolutions.jump.util;
+
+/**
+ * Simply a chunk of code that can be passed around. Facilitates 
+ * Smalltalk-like programming. Also useful as a "lexical closure"
+ * i.e. a chunk of code with variables having long lifetimes.
+ * <p>
+ * Typically only one of the #yield methods needs to be implemented.
+ * Which one depends on the context.
+ */
+public abstract class Block {
+    public Object yield(Object arg1, Object arg2) {
+        throw new UnsupportedOperationException();
+    }
+    public Object yield(Object arg) {
+        throw new UnsupportedOperationException();            
+    }
+    public Object yield() {
+        throw new UnsupportedOperationException();            
+    }    
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/util/CollectionMap.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/util/CollectionMap.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/util/CollectionMap.java	(revision 28163)
@@ -0,0 +1,205 @@
+
+/*
+ * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI 
+ * for visualizing and manipulating spatial features with geometry and attributes.
+ *
+ * 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.jump.util;
+
+import java.util.*;
+
+import com.vividsolutions.jts.util.Assert;
+
+
+/**
+ *  A Map whose values are Collections.
+ */
+public class CollectionMap implements Map {
+    private Map map;
+    private Class collectionClass = ArrayList.class;
+
+    /**
+     * Creates a CollectionMap backed by the given Map class.
+     * @param mapClass a Class that implements Map
+     */
+    public CollectionMap(Class mapClass) {
+        try {
+            map = (Map) mapClass.newInstance();
+        } catch (InstantiationException e) {
+            Assert.shouldNeverReachHere();
+        } catch (IllegalAccessException e) {
+            Assert.shouldNeverReachHere();
+        }
+    }
+    
+    public CollectionMap(Class mapClass, Class collectionClass) {
+        this.collectionClass = collectionClass;
+        try {
+            map = (Map) mapClass.newInstance();
+        } catch (InstantiationException e) {
+            Assert.shouldNeverReachHere();
+        } catch (IllegalAccessException e) {
+            Assert.shouldNeverReachHere();
+        }
+    }    
+
+    /**
+     * Creates a CollectionMap.
+     */
+    public CollectionMap() {
+        this(HashMap.class);
+    }
+    
+    private Collection getItemsInternal(Object key) {
+        Collection collection = (Collection) map.get(key);
+        if (collection == null) {
+            try {
+                collection = (Collection) collectionClass.newInstance();
+            } catch (InstantiationException e) {
+                Assert.shouldNeverReachHere();
+            } catch (IllegalAccessException e) {
+                Assert.shouldNeverReachHere();
+            }
+            map.put(key, collection);
+        }
+        return collection;
+    }
+
+    /**
+     * Adds the item to the Collection at the given key, creating a new Collection if
+     * necessary.
+     * @param key the key to the Collection to which the item should be added
+     * @param item the item to add
+     */
+    public void addItem(Object key, Object item) {
+        getItemsInternal(key).add(item);
+    }
+    
+    public void removeItem(Object key, Object item) {
+        getItemsInternal(key).remove(item);
+    }
+
+    public void clear() {
+        map.clear();
+    }
+
+    /**
+     * Adds the items to the Collection at the given key, creating a new Collection if
+     * necessary.
+     * @param key the key to the Collection to which the items should be added
+     * @param items the items to add
+     */
+    public void addItems(Object key, Collection items) {
+        for (Iterator i = items.iterator(); i.hasNext();) {
+            addItem(key, i.next());
+        }
+    }
+    
+    public void addItems(CollectionMap other) {
+        for (Iterator i = other.keySet().iterator(); i.hasNext(); ) {
+            Object key = i.next();
+            addItems(key, other.getItems(key));
+        }
+    }
+
+    /**
+     * Returns the values.
+     * @return a view of the values, backed by this CollectionMap
+     */
+    public Collection values() {
+        return map.values();
+    }
+
+    /**
+     * Returns the keys.
+     * @return a view of the keys, backed by this CollectionMap
+     */
+    public Set keySet() {
+        return map.keySet();
+    }
+
+    /**
+     * Returns the number of mappings.
+     * @return the number of key-value pairs
+     */
+    public int size() {
+        return map.size();
+    }
+
+    public Object get(Object key) {
+        return getItems(key);
+    }
+
+    public Collection getItems(Object key) {
+        return Collections.unmodifiableCollection(getItemsInternal(key));
+    }
+
+    public Object remove(Object key) {
+        return map.remove(key);
+    }
+
+    public boolean containsKey(Object key) {
+        return map.containsKey(key);
+    }
+
+    public boolean containsValue(Object value) {
+        return map.containsValue(value);
+    }
+
+    public Set entrySet() {
+        return map.entrySet();
+    }
+
+    public boolean isEmpty() {
+        return map.isEmpty();
+    }
+
+    public Object put(Object key, Object value) {
+        Assert.isTrue(value instanceof Collection);
+
+        return map.put(key, value);
+    }
+
+    public void putAll(Map map) {
+        for (Iterator i = map.keySet().iterator(); i.hasNext();) {
+            Object key = i.next();
+            //Delegate to #put so that the assertion is made. [Jon Aquino]
+            put(key, map.get(key));
+        }
+    }
+    public void removeItems(Object key, Collection items) {
+        getItemsInternal(key).removeAll(items);
+    }
+
+	public Map getMap() {
+		return map;
+	}
+
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/util/CollectionUtil.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/util/CollectionUtil.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/util/CollectionUtil.java	(revision 28163)
@@ -0,0 +1,308 @@
+
+/*
+ * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI 
+ * for visualizing and manipulating spatial features with geometry and attributes.
+ *
+ * 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.jump.util;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import com.vividsolutions.jts.util.Assert;
+
+public class CollectionUtil {
+    public CollectionUtil() {}
+
+    /**
+     * Returns a List of Lists: all combinations of the elements of the given List.
+     * @param maxCombinationSize combinations larger than this value are discarded
+     */
+    public static List combinations(List original, int maxCombinationSize) {
+        return combinations(original, maxCombinationSize, null);
+    }
+
+    public static Map inverse(Map map) {
+        Map inverse;
+        try {
+            inverse = (Map) map.getClass().newInstance();
+        } catch (InstantiationException e) {
+            Assert.shouldNeverReachHere(e.toString());
+            return null;
+        } catch (IllegalAccessException e) {
+            Assert.shouldNeverReachHere(e.toString());
+            return null;
+        }
+        for (Iterator i = map.keySet().iterator(); i.hasNext();) {
+            Object key = i.next();
+            Object value = map.get(key);
+            inverse.put(value, key);
+        }
+        return inverse;
+    }
+
+    /**
+     * Returns a List of Lists: all combinations of the elements of the given List.
+     * @param maxCombinationSize combinations larger than this value are discarded
+     * @param mandatoryItem an item that all returned combinations must contain,
+     * or null to leave unspecified
+     */
+    public static List combinations(
+        List original,
+        int maxCombinationSize,
+        Object mandatoryItem) {
+        ArrayList combinations = new ArrayList();
+
+        //Combinations are given by the bits of each binary number from 1 to 2^N
+        for (int i = 1; i <= ((int) Math.pow(2, original.size()) - 1); i++) {
+            ArrayList combination = new ArrayList();
+
+            for (int j = 0; j < original.size(); j++) {
+                if ((i & (int) Math.pow(2, j)) > 0) {
+                    combination.add(original.get(j));
+                }
+            }
+
+            if (combination.size() > maxCombinationSize) {
+                continue;
+            }
+
+            if ((mandatoryItem != null) && !combination.contains(mandatoryItem)) {
+                continue;
+            }
+
+            combinations.add(combination);
+        }
+
+        return combinations;
+    }
+
+    /**
+     * Returns a List of Lists: all combinations of the elements of the given List.
+     */
+    public static List combinations(List original) {
+        return combinations(original, original.size(), null);
+    }
+
+    public static void removeKeys(Collection keys, Map map) {
+        for (Iterator i = keys.iterator(); i.hasNext();) {
+            Object key = (Object) i.next();
+            map.remove(key);
+        }
+    }
+
+    /**
+     * The nth key corresponds to the nth value
+     */
+    public static List[] keysAndCorrespondingValues(Map map) {
+        ArrayList keys = new ArrayList(map.keySet());
+        ArrayList values = new ArrayList();
+        for (Iterator i = keys.iterator(); i.hasNext();) {
+            Object key = i.next();
+            values.add(map.get(key));
+        }
+        return new List[] { keys, values };
+    }
+
+    public static Collection concatenate(Collection collections) {
+        ArrayList concatenation = new ArrayList();
+        for (Iterator i = collections.iterator(); i.hasNext();) {
+            Collection collection = (Collection) i.next();
+            concatenation.addAll(collection);
+        }
+        return concatenation;
+    }
+
+    public static Object randomElement(List list) {
+        return list.get((int) Math.floor(Math.random() * list.size()));
+    }
+
+    public static SortedSet reverseSortedSet(int[] ints) {
+        TreeSet sortedSet = new TreeSet(Collections.reverseOrder());
+        for (int i = 0; i < ints.length; i++) {
+            sortedSet.add(new Integer(ints[i]));
+        }
+        return sortedSet;
+    }
+
+    public static List reverse(List list) {
+        Collections.reverse(list);
+        return list;
+    }
+
+    /**
+     * Data is evenly discarded or duplicated to attain the new size
+     */
+    public static Collection stretch(
+        Collection source,
+        Collection destination,
+        int destinationSize) {
+        try {
+            Assert.isTrue(destination.isEmpty());
+            List originalList =
+                source instanceof List ? (List) source : new ArrayList(source);
+            for (int i = 0; i < destinationSize; i++) {
+                destination.add(
+                    originalList.get(
+                        (int) Math.round(
+                            i * originalList.size() / (double) destinationSize)));
+            }
+        } finally {
+            return destination;
+        }
+    }
+
+    public static Object ifNotIn(Object o, Collection c, Object alternative) {
+        return c.contains(o) ? o : alternative;
+    }
+
+    public static void setIfNull(int i, List list, String value) {
+        if (i >= list.size()) {
+            resize(list, i + 1);
+        }
+        if (list.get(i) != null) {
+            return;
+        }
+        list.set(i, value);
+    }
+
+    public static void resize(List list, int newSize) {
+        if (newSize < list.size()) {
+            list.subList(newSize, list.size()).clear();
+        } else {
+            list.addAll(Collections.nCopies(newSize - list.size(), null));
+        }
+    }
+
+    public static boolean containsReference(Object[] objects, Object o) {
+        for (int i = 0; i < objects.length; i++) {
+            if (objects[i] == o) {
+                return true;
+            }
+        }
+        return false;
+    }
+    /**
+     * Brute force, for when HashSet and TreeSet won't work (e.g. #hashCode
+     * implementation isn't appropriate). The original Collection is not modified.
+     */
+    public static Collection removeDuplicates(Collection original) {
+        ArrayList result = new ArrayList();
+        for (Iterator i = original.iterator(); i.hasNext();) {
+            Object item = i.next();
+            if (!result.contains(item)) {
+                result.add(item);
+            }
+        }
+        return result;
+    }
+
+    public static void addIfNotNull(Object item, Collection collection) {
+        if (item != null) {
+            collection.add(item);
+        }
+    }
+
+    /**
+     * Modifies and returns the collection.
+     */
+    public static Collection filterByClass(Collection collection, Class c) {
+        for (Iterator i = collection.iterator(); i.hasNext();) {
+            Object item = i.next();
+            if (!c.isInstance(item)) {
+                i.remove();
+            }
+        }
+        return collection;
+    }
+
+    public static Map createMap(Object[] alternatingKeysAndValues) {
+        return createMap(HashMap.class, alternatingKeysAndValues);
+    }
+
+    public static Map createMap(Class mapClass, Object[] alternatingKeysAndValues) {
+        Map map = null;
+        try {
+            map = (Map) mapClass.newInstance();
+        } catch (Exception e) {
+            Assert.shouldNeverReachHere(e.toString());
+        }
+        for (int i = 0; i < alternatingKeysAndValues.length; i += 2) {
+            map.put(alternatingKeysAndValues[i], alternatingKeysAndValues[i + 1]);
+        }
+        return map;
+    }
+
+    /**
+     * The Smalltalk #collect method.
+     */
+    public static Collection collect(Collection collection, Block block) {
+        ArrayList result = new ArrayList();
+        for (Iterator i = collection.iterator(); i.hasNext();) {
+            Object item = i.next();
+            result.add(block.yield(item));
+        }
+        return result;
+    }
+    
+    /**
+     * The Smalltalk #select method.
+     */
+    public static Collection select(Collection collection, Block block) {
+        ArrayList result = new ArrayList();
+        for (Iterator i = collection.iterator(); i.hasNext();) {
+            Object item = i.next();
+            if(Boolean.TRUE.equals(block.yield(item))) {
+                result.add(item);
+            };
+        }
+        return result;
+    }    
+    
+    public static Object get(Class c, Map map) {
+        if (map.keySet().contains(c)) {
+            return map.get(c);
+        }
+        for (Iterator i = map.keySet().iterator(); i.hasNext();) {
+            Class candidateClass = (Class) i.next();
+            if (candidateClass.isAssignableFrom(c)) {
+                return map.get(candidateClass);
+            }
+        }
+        return null;        
+    }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/util/CoordinateArrays.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/util/CoordinateArrays.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/util/CoordinateArrays.java	(revision 28163)
@@ -0,0 +1,221 @@
+
+/*
+ * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI 
+ * for visualizing and manipulating spatial features with geometry and attributes.
+ *
+ * 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.jump.util;
+
+import java.util.*;
+
+import com.vividsolutions.jts.algorithm.CGAlgorithms;
+import com.vividsolutions.jts.algorithm.RobustCGAlgorithms;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.util.Assert;
+
+
+/**
+ * Some utility functions for handling Coordinate arrays;
+ */
+public class CoordinateArrays {
+    //<<TODO:REFACTORING>> JTS already has a class named CoordinateArrays.
+    //I wonder if we should collapse this class into that one. [Jon Aquino]
+    // MD - yep, at some point.
+    private static final CGAlgorithms cga = new RobustCGAlgorithms();
+    private final static Coordinate[] coordArrayType = new Coordinate[0];
+
+    public static Coordinate[] toCoordinateArray(List coordList) {
+        return (Coordinate[]) coordList.toArray(coordArrayType);
+    }
+
+    //<<TODO:REFACTORING>> This functionality is duplicated in
+    //the protected method Geometry#reversePointOrder. Perhaps we should
+    //make that method public and deprecate this method, or have this method
+    //delegate to the other. [Jon Aquino]
+    //MD: Geometry#reversePointOrder could delegate to this method.  Can't do it other way around.
+    public static void reverse(Coordinate[] coord) {
+        int last = coord.length - 1;
+        int mid = last / 2;
+
+        for (int i = 0; i <= mid; i++) {
+            Coordinate tmp = coord[i];
+            coord[i] = coord[last - i];
+            coord[last - i] = tmp;
+        }
+    }
+
+    /**
+     * Converts an array of coordinates to a line or point, as appropriate.
+     * @param coords the coordinates of a line or point
+     * @param fact a factory used to create the Geometry
+     * @return a line if there is more than one coordinate; a point if there is
+     * just one coordinate; an empty point otherwise
+     */
+    public static Geometry toLineOrPoint(Coordinate[] coords,
+        GeometryFactory fact) {
+        if (coords.length > 1) {
+            return fact.createLineString(coords);
+        }
+
+        if (coords.length == 1) {
+            return fact.createPoint(coords[0]);
+        }
+
+        return fact.createPoint((Coordinate)null);
+    }
+
+    public static boolean equals(Coordinate[] coord1, Coordinate[] coord2) {
+        if (coord1 == coord2) {
+            return true;
+        }
+
+        if ((coord1 == null) || (coord2 == null)) {
+            return false;
+        }
+
+        if (coord1.length != coord2.length) {
+            return false;
+        }
+
+        for (int i = 0; i < coord1.length; i++) {
+            if (!coord1[i].equals(coord2[i])) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Converts a collection of coordinate arrays to a collection of geometries.
+     * @param coordArrays a collection of Coordinate[]
+     * @param fact a factory used to create the Geometry's
+     * @return a collection of LineStrings and Points
+     */
+    public static List fromCoordinateArrays(List coordArrays,
+        GeometryFactory fact) {
+        List geomList = new ArrayList();
+
+        for (Iterator i = coordArrays.iterator(); i.hasNext();) {
+            Coordinate[] coords = (Coordinate[]) i.next();
+            Geometry geom = toLineOrPoint(coords, fact);
+            geomList.add(geom);
+        }
+
+        return geomList;
+    }
+
+    /**
+     * Extract the coordinate arrays for a geometry into a List.
+     * @param g the Geometry to extract from
+     * @param coordArrayList the List to add the coordinate arrays to
+     * @param orientPolygons whether or not the arrays in the List should be
+     * oriented (clockwise for the shell, counterclockwise for the holes)
+     */
+    public static void addCoordinateArrays(Geometry g, boolean orientPolygons,
+        List coordArrayList) {
+        if (g.getDimension() <= 0) {
+            return;
+        } else if (g instanceof LineString) {
+            LineString l = (LineString) g;
+            coordArrayList.add(l.getCoordinates());
+        } else if (g instanceof Polygon) {
+            Polygon poly = (Polygon) g;
+            Coordinate[] shell = poly.getExteriorRing().getCoordinates();
+
+            if (orientPolygons) {
+                shell = ensureOrientation(shell, CGAlgorithms.CLOCKWISE);
+            }
+
+            coordArrayList.add(shell);
+
+            for (int i = 0; i < poly.getNumInteriorRing(); i++) {
+                Coordinate[] hole = poly.getInteriorRingN(i).getCoordinates();
+
+                if (orientPolygons) {
+                    hole = ensureOrientation(hole, CGAlgorithms.COUNTERCLOCKWISE);
+                }
+
+                coordArrayList.add(hole);
+            }
+        } else if (g instanceof GeometryCollection) {
+            GeometryCollection gc = (GeometryCollection) g;
+
+            for (int i = 0; i < gc.getNumGeometries(); i++) {
+                addCoordinateArrays(gc.getGeometryN(i), orientPolygons,
+                    coordArrayList);
+            }
+        } else {
+            Assert.shouldNeverReachHere("Geometry of type " +
+                g.getClass().getName() + " not handled");
+        }
+    }
+
+    /**
+     * Sets the orientation of an array of coordinates.
+     * @param coord the coordinates to inspect
+     * @param desiredOrientation CGAlgorithms.CLOCKWISE or CGAlgorithms.COUNTERCLOCKWISE
+     * @return a new array with entries in reverse order, if the orientation is
+     * incorrect; otherwise, the original array
+     */
+    public static Coordinate[] ensureOrientation(Coordinate[] coord,
+        int desiredOrientation) {
+        if (coord.length == 0) {
+            return coord;
+        }
+
+        int orientation = cga.isCCW(coord) ? CGAlgorithms.COUNTERCLOCKWISE
+                                           : CGAlgorithms.CLOCKWISE;
+
+        if (orientation != desiredOrientation) {
+            Coordinate[] reverse = (Coordinate[]) coord.clone();
+            CoordinateArrays.reverse(reverse);
+
+            return reverse;
+        }
+
+        return coord;
+    }
+
+    /**
+     * Extract the coordinate arrays for a geometry.
+     * Polygons will be checked to ensure their rings are oriented correctly.
+     * Note: coordinates from Points or MultiPoints will not be extracted.
+     * @param g the Geometry to extract from
+     * @param orientPolygons ensure that Polygons are correctly oriented
+     * @return a list of Coordinate[]
+     */
+    public static List toCoordinateArrays(Geometry g, boolean orientPolygons) {
+        List coordArrayList = new ArrayList();
+        addCoordinateArrays(g, orientPolygons, coordArrayList);
+
+        return coordArrayList;
+    }
+}
Index: /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/util/MathUtil.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/util/MathUtil.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/com/vividsolutions/jump/util/MathUtil.java	(revision 28163)
@@ -0,0 +1,66 @@
+
+/*
+ * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI 
+ * for visualizing and manipulating spatial features with geometry and attributes.
+ *
+ * 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.jump.util;
+
+
+/**
+ * Additional math utilities.
+ * @see Math
+ */
+public class MathUtil {
+    public MathUtil() {
+    }   
+
+    public static double orderOfMagnitude(double x) {
+        return base10Log(x);
+    }
+
+    public static double base10Log(double x) {
+        return Math.log(x) / Math.log(10);
+    }
+
+    public static int mostSignificantDigit(double x) {
+        return (int) (x / Math.pow(10, Math.floor(MathUtil.orderOfMagnitude(x))));
+    }
+
+    /**
+     * Returns the average of two doubles
+     * @param a one of the doubles to average
+     * @param b the other double to average
+     * @return the average of two doubles
+     */
+    public static double avg(double a, double b) {
+        return (a + b) / 2d;
+    }
+}
Index: /applications/editors/josm/plugins/conflation/src/org/openstreetmap/josm/plugins/conflation/ConflationCandidate.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/org/openstreetmap/josm/plugins/conflation/ConflationCandidate.java	(revision 28162)
+++ /applications/editors/josm/plugins/conflation/src/org/openstreetmap/josm/plugins/conflation/ConflationCandidate.java	(revision 28163)
@@ -1,9 +1,5 @@
 package org.openstreetmap.josm.plugins.conflation;
 
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import static org.openstreetmap.josm.tools.I18n.tr;
 
@@ -15,21 +11,17 @@
 
     OsmPrimitive referenceObject;
-    OsmDataLayer referenceLayer;
     OsmPrimitive subjectObject;
-    OsmDataLayer subjectLayer;
-    double cost;
+    double score;
     double distance;
 
-    public ConflationCandidate(OsmPrimitive referenceObject, OsmDataLayer referenceLayer,
-            OsmPrimitive subjectObject, OsmDataLayer subjectLayer, double cost) {
+    public ConflationCandidate(OsmPrimitive referenceObject,
+            OsmPrimitive subjectObject, double score) {
         if (referenceObject == null || subjectObject == null) {
             throw new IllegalArgumentException(tr("Invalid reference or subject"));
         }
         this.referenceObject = referenceObject;
-        this.referenceLayer = referenceLayer;
         this.subjectObject = subjectObject;
-        this.subjectLayer = subjectLayer;
-        this.cost = cost;
-        // TODO: use distance calculated in cost function, and make sure it's in meters?
+        this.score = score;
+        // TODO: use distance calculated in score function, and make sure it's in meters?
         this.distance = ConflationUtils.getCenter(referenceObject).distance(ConflationUtils.getCenter(subjectObject));
     }
@@ -38,12 +30,4 @@
         return referenceObject;
     }
-    
-    public OsmDataLayer getReferenceLayer() {
-        return referenceLayer;
-    }
-    
-    public OsmDataLayer getSubjectLayer() {
-        return subjectLayer;
-    }
 
     public OsmPrimitive getSubjectObject() {
@@ -51,6 +35,6 @@
     }
 
-    public Object getCost() {
-        return cost;
+    public Object getScore() {
+        return score;
     }
 
Index: /applications/editors/josm/plugins/conflation/src/org/openstreetmap/josm/plugins/conflation/ConflationLayer.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/org/openstreetmap/josm/plugins/conflation/ConflationLayer.java	(revision 28162)
+++ /applications/editors/josm/plugins/conflation/src/org/openstreetmap/josm/plugins/conflation/ConflationLayer.java	(revision 28163)
@@ -12,5 +12,4 @@
 import org.openstreetmap.josm.actions.RenameLayerAction;
 import org.openstreetmap.josm.data.Bounds;
-import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
Index: /applications/editors/josm/plugins/conflation/src/org/openstreetmap/josm/plugins/conflation/ConflationToggleDialog.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/org/openstreetmap/josm/plugins/conflation/ConflationToggleDialog.java	(revision 28162)
+++ /applications/editors/josm/plugins/conflation/src/org/openstreetmap/josm/plugins/conflation/ConflationToggleDialog.java	(revision 28163)
@@ -1,10 +1,16 @@
 package org.openstreetmap.josm.plugins.conflation;
 
+import com.vividsolutions.jcs.conflate.polygonmatch.*;
+import com.vividsolutions.jts.geom.Envelope;
+import com.vividsolutions.jump.feature.*;
+import com.vividsolutions.jump.task.TaskMonitor;
 import java.awt.Component;
 import java.awt.Dialog;
-import java.awt.event.*;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.awt.geom.Point2D;
+import java.util.*;
 import javax.swing.*;
 import javax.swing.event.ListSelectionEvent;
@@ -23,5 +29,5 @@
 import org.openstreetmap.josm.gui.layer.Layer;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.plugins.utilsplugin2.replacegeometry.HungarianAlgorithm;
+import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor;
 import org.openstreetmap.josm.plugins.utilsplugin2.replacegeometry.ReplaceGeometryUtils;
 import static org.openstreetmap.josm.tools.I18n.tr;
@@ -211,5 +217,5 @@
             //FIXME: should layer listen for selection change?
             ConflationCandidate c = conflationLayer.getSelectedCandidate();
-            if (c.getReferenceLayer() != c.getSubjectLayer()) {
+            if (settings.getReferenceLayer() != settings.getSubjectLayer()) {
                 JOptionPane.showMessageDialog(Main.parent, tr("Conflation between layers isn't supported yet."),
                         tr("Cannot conflate between layes"), JOptionPane.ERROR_MESSAGE);
@@ -263,45 +269,118 @@
     }
 
+    /**
+     * Create FeatureSchema using union of all keys from all selected primitives
+     * @param prims
+     * @return 
+     */
+    private FeatureSchema createSchema(Collection<OsmPrimitive> prims) {
+        Set<String> keys = new HashSet<String>();
+        for (OsmPrimitive prim : prims) {
+            keys.addAll(prim.getKeys().keySet());
+        }
+        FeatureSchema schema = new FeatureSchema();
+        schema.addAttribute("__GEOMETRY__", AttributeType.GEOMETRY);
+        for (String key : keys) {
+            schema.addAttribute(key, AttributeType.STRING);
+        }
+        return schema;   
+    }
+    
+    private FeatureCollection createFeatureCollection(Collection<OsmPrimitive> prims) {
+        FeatureDataset dataset = new FeatureDataset(createSchema(prims));
+        for (OsmPrimitive prim : prims) {
+            dataset.add(new OsmFeature(prim));
+        }
+        return dataset;
+    }
+    
+    /**
+     * Progress monitor for use with JCS
+     */
+    private class JosmTaskMonitor extends PleaseWaitProgressMonitor implements TaskMonitor {
+        
+        @Override
+        public void report(String description) {
+            subTask(description);
+        }
+
+        @Override
+        public void report(int itemsDone, int totalItems, String itemDescription) {
+            subTask(String.format("Processing %d of %d %s", itemsDone, totalItems, itemDescription));
+        }
+
+        @Override
+        public void report(Exception exception) {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        @Override
+        public void allowCancellationRequests() {
+            setCancelable(true);
+        }
+
+        @Override
+        public boolean isCancelRequested() {
+            return isCanceled();
+        }
+        
+    }
+    
     private ConflationCandidateList generateCandidates(ConflationSettings settings) {
-        ConflationCandidateList cands = new ConflationCandidateList();
-
-        // some initialization
-        int n = settings.getSubjectSelection().size();
-        int m = settings.getReferenceSelection().size();
-        double[][] cost = new double[n][m];
-        // calculate cost matrix
-        for (int i = 0; i < n; i++) {
-            for (int j = 0; j < m; j++) {
-                cost[i][j] = ConflationUtils.calcCost(
-                        settings.getSubjectSelection().get(i), settings.getReferenceSelection().get(j), settings);
-            }
-        }
-        // perform assignment using Hungarian algorithm
-        int[][] assignment = HungarianAlgorithm.hgAlgorithm(cost, "min");
-        OsmPrimitive subObject;
-        OsmPrimitive refObject;
-        for (int i = 0; i < n; i++) {
-            int subIdx = assignment[i][0];
-            int refIdx = assignment[i][1];
-            if (subIdx < n) {
-                subObject = settings.getSubjectSelection().get(subIdx);
-            } else {
-                subObject = null;
-            }
-            if (refIdx < m) {
-                refObject = settings.getReferenceSelection().get(refIdx);
-            } else {
-                refObject = null;
-            }
-            if (subObject != null && refObject != null) {
-                // TODO: do something!
-                if (!(cands.hasCandidate(refObject, subObject) || cands.hasCandidate(subObject, refObject))) {
-                    cands.add(new ConflationCandidate(
-                            refObject, settings.getReferenceLayer(),
-                            subObject, settings.getSubjectLayer(), cost[subIdx][refIdx]));
-                }
-            }
-        }
-        return cands;
+        JosmTaskMonitor monitor = new JosmTaskMonitor();
+        monitor.beginTask("Generating conflation candidates");
+        
+        // create Features and collections from primitive selections
+        Set<OsmPrimitive> allPrimitives = new HashSet<OsmPrimitive>();
+        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);
+        }
+        
+        // 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
+        CentroidDistanceMatcher centroid = new CentroidDistanceMatcher();
+        centroid.setMaxDistance(maxDistance);
+        IdenticalFeatureFilter identical = new IdenticalFeatureFilter();
+        FeatureMatcher[] matchers = {centroid, identical};
+        ChainMatcher chain = new ChainMatcher(matchers);
+        BasicFCMatchFinder basicFinder = new BasicFCMatchFinder(chain);
+        OneToOneFCMatchFinder finder = new OneToOneFCMatchFinder(basicFinder);
+
+        // 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<OsmFeature, Matches> map = finder.match(refColl, subColl, monitor);
+        
+        monitor.subTask("Finishing conflation candidate list");
+        
+        // convert to simple one-to-one match
+        ConflationCandidateList list = new ConflationCandidateList();
+        for (Map.Entry<OsmFeature, Matches> entry: map.entrySet()) {
+            OsmFeature target = entry.getKey();
+            OsmFeature subject = (OsmFeature)entry.getValue().getTopMatch();
+            list.add(new ConflationCandidate(target.getPrimitive(), subject.getPrimitive(),
+                    entry.getValue().getTopScore()));
+        }
+        
+        monitor.finishTask();
+        monitor.close();
+        return list;
     }
 
Index: /applications/editors/josm/plugins/conflation/src/org/openstreetmap/josm/plugins/conflation/ConflationUtils.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/org/openstreetmap/josm/plugins/conflation/ConflationUtils.java	(revision 28162)
+++ /applications/editors/josm/plugins/conflation/src/org/openstreetmap/josm/plugins/conflation/ConflationUtils.java	(revision 28163)
@@ -5,52 +5,10 @@
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.tools.StringMetrics;
 
 public final class ConflationUtils {
-    private final static double MAX_COST = Double.MAX_VALUE;
-        
+
     public static EastNorth getCenter(OsmPrimitive prim) {
-            LatLon center = prim.getBBox().getTopLeft().getCenter(prim.getBBox().getBottomRight());
-            return Main.map.mapView.getProjection().latlon2eastNorth(center);
-    }
-    
-    /**
-     * Calculate the cost of a pair of <code>OsmPrimitive</code>'s. A
-     * simple cost consisting of the Euclidean distance is used
-     * now, later we can also use dissimilarity between tags.
-     *
-     * @param   referenceObject      the reference <code>OsmPrimitive</code>.
-     * @param   subjectObject   the non-reference <code>OsmPrimitive</code>.
-     */
-    public static double calcCost(OsmPrimitive referenceObject, OsmPrimitive subjectObject, ConflationSettings settings) {
-        double cost;
-
-        if (referenceObject==subjectObject) {
-            return MAX_COST;
-        }
-
-        double distance = 0;
-        double stringCost = 1.0;
-        if (settings.distanceWeight != 0) {
-            distance = getCenter(referenceObject).distance(getCenter(subjectObject));
-        }
-        if (settings.stringWeight != 0) {
-            String referenceString = referenceObject.getKeys().get(settings.keyString);
-            String subjectString = subjectObject.getKeys().get(settings.keyString);
-            
-            if (referenceString == null ? subjectString == null : referenceString.equals(subjectString))
-                stringCost = 0.0;
-            else if (referenceString == null || subjectString == null)
-                stringCost = 1.0;
-            else
-                stringCost = 1.0 - StringMetrics.getByName("levenshtein").getSimilarity(subjectString, referenceString);
-        }
-        
-        if (distance > settings.distanceCutoff || stringCost > settings.stringCutoff)
-            cost = MAX_COST;
-        else
-            cost = distance * settings.distanceWeight + stringCost * settings.stringWeight;
-
-        return cost;
+        LatLon center = prim.getBBox().getTopLeft().getCenter(prim.getBBox().getBottomRight());
+        return Main.map.mapView.getProjection().latlon2eastNorth(center);
     }
 }
Index: /applications/editors/josm/plugins/conflation/src/org/openstreetmap/josm/plugins/conflation/MatchTableModel.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/org/openstreetmap/josm/plugins/conflation/MatchTableModel.java	(revision 28162)
+++ /applications/editors/josm/plugins/conflation/src/org/openstreetmap/josm/plugins/conflation/MatchTableModel.java	(revision 28163)
@@ -14,5 +14,5 @@
 
     private ConflationCandidateList candidates = null;
-    private final static String[] columnNames = {tr("Reference"), tr("Subject"), "Distance (m)", "Cost", "Tags"};
+    private final static String[] columnNames = {tr("Reference"), tr("Subject"), "Distance (m)", "Score", "Tags"};
 
     @Override
@@ -33,4 +33,5 @@
     }
 
+    @Override
     public Object getValueAt(int row, int col) {
         if (candidates == null)
@@ -45,5 +46,5 @@
             return c.getDistance();
         } else if (col == 3) {
-            return c.getCost();
+            return c.getScore();
         }
         if (col == 4) {
Index: /applications/editors/josm/plugins/conflation/src/org/openstreetmap/josm/plugins/conflation/OsmFeature.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/org/openstreetmap/josm/plugins/conflation/OsmFeature.java	(revision 28163)
+++ /applications/editors/josm/plugins/conflation/src/org/openstreetmap/josm/plugins/conflation/OsmFeature.java	(revision 28163)
@@ -0,0 +1,64 @@
+package org.openstreetmap.josm.plugins.conflation;
+
+import com.vividsolutions.jump.feature.AbstractBasicFeature;
+import com.vividsolutions.jump.feature.AttributeType;
+import com.vividsolutions.jump.feature.FeatureSchema;
+import java.util.Map;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.plugins.jts.JTSUtils;
+
+public class OsmFeature extends AbstractBasicFeature {
+    private Object[] attributes;
+    private OsmPrimitive primitive;
+    
+    /**
+     * Create a copy of the OSM geometry
+     * TODO: update from underlying primitive
+     * @param prim 
+     */
+    public OsmFeature(OsmPrimitive prim) {
+        super(new FeatureSchema());
+        primitive = prim;
+        Map<String, String> keys = prim.getKeys();
+        attributes = new Object[keys.size() + 1];
+        getSchema().addAttribute("GEOMETRY", AttributeType.GEOMETRY);
+        for (String key : keys.keySet()) {
+            getSchema().addAttribute(key, AttributeType.STRING);
+            setAttribute(key, keys.get(key));
+        }
+        JTSUtils conversion = new JTSUtils();
+        setGeometry(conversion.convert(prim));
+    }
+
+    @Override
+    public void setAttributes(Object[] attributes) {
+        this.attributes = attributes;
+    }
+
+    @Override
+    public void setAttribute(int attributeIndex, Object newAttribute) {
+        attributes[attributeIndex] = newAttribute;
+    }
+
+    @Override
+    public Object getAttribute(int i) {
+        return attributes[i];
+    }
+
+    @Override
+    public Object[] getAttributes() {
+        return attributes;
+    }
+    
+    public OsmPrimitive getPrimitive() {
+        return primitive;
+    }
+    
+    @Override
+    public int getID() {
+        // FIXME: should work most of the time, GeoAPI more robust, need to
+        // consider the dataset (e.g. two non-uploaded layers can have different
+        // objects with the same id
+        return (int) primitive.getId();
+    }
+}
Index: /applications/editors/josm/plugins/conflation/src/org/openstreetmap/josm/plugins/conflation/SettingsDialog.java
===================================================================
--- /applications/editors/josm/plugins/conflation/src/org/openstreetmap/josm/plugins/conflation/SettingsDialog.java	(revision 28162)
+++ /applications/editors/josm/plugins/conflation/src/org/openstreetmap/josm/plugins/conflation/SettingsDialog.java	(revision 28163)
@@ -1,5 +1,4 @@
 package org.openstreetmap.josm.plugins.conflation;
 
-import java.awt.Component;
 import java.awt.GridBagLayout;
 import java.awt.event.ActionEvent;
@@ -117,4 +116,5 @@
         stringCheckBox = new JCheckBox();
         stringCheckBox.setSelected(false);
+        stringCheckBox.setEnabled(false);
         stringCheckBox.setText(tr("String"));
         costsPanel.add(stringCheckBox, GBC.std());
@@ -126,4 +126,5 @@
         costsPanel.add(stringTextField, GBC.std());
         
+        costsPanel.setEnabled(false);
         pnl.add(costsPanel);
         setContent(pnl);
