Ignore:
Timestamp:
2012-03-28T16:47:09+02:00 (12 years ago)
Author:
joshdoe
Message:

conflation: now uses Java Conflation Suite and depends on JTS plugin

Currently depends on a few JUMP classes and the JCS files are directly included, this will change in the future.

Location:
applications/editors/josm/plugins/conflation
Files:
69 added
7 edited

Legend:

Unmodified
Added
Removed
  • applications/editors/josm/plugins/conflation/build.xml

    r27968 r28163  
    4040
    4141    <!-- Needs to be used after import, otherwise ${plugin.dist.dir} is not defined -->
    42     <property name="utilsplugin2" location="${plugin.dist.dir}/utilsplugin2.jar"/>   
     42    <property name="jtsplugin" location="${plugin.dist.dir}/jts.jar"/> 
     43    <property name="utilsplugin2" location="${plugin.dist.dir}/utilsplugin2.jar"/>
    4344
    4445    <!--
     
    5253            <classpath>
    5354                <pathelement path="${josm}"/>
     55                <pathelement location="${jtsplugin}"/>
    5456                <pathelement location="${utilsplugin2}"/>
    5557            </classpath>
     
    9597                <attribute name="Plugin-Description" value="(Warning: Experimental!) Tool for conflating (merging) data"/>
    9698                <attribute name="Plugin-Icon" value="images/conflation.png"/>
    97                 <attribute name="Plugin-Requires" value="utilsplugin2"/>
     99                <attribute name="Plugin-Requires" value="jts;utilsplugin2"/>
    98100                <attribute name="Plugin-Link" value="http://wiki.openstreetmap.org/wiki/JOSM/Plugins/Conflation"/>
    99101                <attribute name="Plugin-Mainversion" value="${plugin.main.version}"/>
  • applications/editors/josm/plugins/conflation/src/org/openstreetmap/josm/plugins/conflation/ConflationCandidate.java

    r27971 r28163  
    11package org.openstreetmap.josm.plugins.conflation;
    22
    3 import java.util.Iterator;
    4 import java.util.LinkedList;
    5 import java.util.List;
    63import org.openstreetmap.josm.data.osm.OsmPrimitive;
    7 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    84import static org.openstreetmap.josm.tools.I18n.tr;
    95
     
    1511
    1612    OsmPrimitive referenceObject;
    17     OsmDataLayer referenceLayer;
    1813    OsmPrimitive subjectObject;
    19     OsmDataLayer subjectLayer;
    20     double cost;
     14    double score;
    2115    double distance;
    2216
    23     public ConflationCandidate(OsmPrimitive referenceObject, OsmDataLayer referenceLayer,
    24             OsmPrimitive subjectObject, OsmDataLayer subjectLayer, double cost) {
     17    public ConflationCandidate(OsmPrimitive referenceObject,
     18            OsmPrimitive subjectObject, double score) {
    2519        if (referenceObject == null || subjectObject == null) {
    2620            throw new IllegalArgumentException(tr("Invalid reference or subject"));
    2721        }
    2822        this.referenceObject = referenceObject;
    29         this.referenceLayer = referenceLayer;
    3023        this.subjectObject = subjectObject;
    31         this.subjectLayer = subjectLayer;
    32         this.cost = cost;
    33         // TODO: use distance calculated in cost function, and make sure it's in meters?
     24        this.score = score;
     25        // TODO: use distance calculated in score function, and make sure it's in meters?
    3426        this.distance = ConflationUtils.getCenter(referenceObject).distance(ConflationUtils.getCenter(subjectObject));
    3527    }
     
    3830        return referenceObject;
    3931    }
    40    
    41     public OsmDataLayer getReferenceLayer() {
    42         return referenceLayer;
    43     }
    44    
    45     public OsmDataLayer getSubjectLayer() {
    46         return subjectLayer;
    47     }
    4832
    4933    public OsmPrimitive getSubjectObject() {
     
    5135    }
    5236
    53     public Object getCost() {
    54         return cost;
     37    public Object getScore() {
     38        return score;
    5539    }
    5640
  • applications/editors/josm/plugins/conflation/src/org/openstreetmap/josm/plugins/conflation/ConflationLayer.java

    r28036 r28163  
    1212import org.openstreetmap.josm.actions.RenameLayerAction;
    1313import org.openstreetmap.josm.data.Bounds;
    14 import org.openstreetmap.josm.data.osm.DataSet;
    1514import org.openstreetmap.josm.data.osm.Node;
    1615import org.openstreetmap.josm.data.osm.OsmPrimitive;
  • applications/editors/josm/plugins/conflation/src/org/openstreetmap/josm/plugins/conflation/ConflationToggleDialog.java

    r28037 r28163  
    11package org.openstreetmap.josm.plugins.conflation;
    22
     3import com.vividsolutions.jcs.conflate.polygonmatch.*;
     4import com.vividsolutions.jts.geom.Envelope;
     5import com.vividsolutions.jump.feature.*;
     6import com.vividsolutions.jump.task.TaskMonitor;
    37import java.awt.Component;
    48import java.awt.Dialog;
    5 import java.awt.event.*;
    6 import java.util.Arrays;
    7 import java.util.Collection;
    8 import java.util.List;
     9import java.awt.event.ActionEvent;
     10import java.awt.event.KeyEvent;
     11import java.awt.event.WindowAdapter;
     12import java.awt.event.WindowEvent;
     13import java.awt.geom.Point2D;
     14import java.util.*;
    915import javax.swing.*;
    1016import javax.swing.event.ListSelectionEvent;
     
    2329import org.openstreetmap.josm.gui.layer.Layer;
    2430import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    25 import org.openstreetmap.josm.plugins.utilsplugin2.replacegeometry.HungarianAlgorithm;
     31import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor;
    2632import org.openstreetmap.josm.plugins.utilsplugin2.replacegeometry.ReplaceGeometryUtils;
    2733import static org.openstreetmap.josm.tools.I18n.tr;
     
    211217            //FIXME: should layer listen for selection change?
    212218            ConflationCandidate c = conflationLayer.getSelectedCandidate();
    213             if (c.getReferenceLayer() != c.getSubjectLayer()) {
     219            if (settings.getReferenceLayer() != settings.getSubjectLayer()) {
    214220                JOptionPane.showMessageDialog(Main.parent, tr("Conflation between layers isn't supported yet."),
    215221                        tr("Cannot conflate between layes"), JOptionPane.ERROR_MESSAGE);
     
    263269    }
    264270
     271    /**
     272     * Create FeatureSchema using union of all keys from all selected primitives
     273     * @param prims
     274     * @return
     275     */
     276    private FeatureSchema createSchema(Collection<OsmPrimitive> prims) {
     277        Set<String> keys = new HashSet<String>();
     278        for (OsmPrimitive prim : prims) {
     279            keys.addAll(prim.getKeys().keySet());
     280        }
     281        FeatureSchema schema = new FeatureSchema();
     282        schema.addAttribute("__GEOMETRY__", AttributeType.GEOMETRY);
     283        for (String key : keys) {
     284            schema.addAttribute(key, AttributeType.STRING);
     285        }
     286        return schema;   
     287    }
     288   
     289    private FeatureCollection createFeatureCollection(Collection<OsmPrimitive> prims) {
     290        FeatureDataset dataset = new FeatureDataset(createSchema(prims));
     291        for (OsmPrimitive prim : prims) {
     292            dataset.add(new OsmFeature(prim));
     293        }
     294        return dataset;
     295    }
     296   
     297    /**
     298     * Progress monitor for use with JCS
     299     */
     300    private class JosmTaskMonitor extends PleaseWaitProgressMonitor implements TaskMonitor {
     301       
     302        @Override
     303        public void report(String description) {
     304            subTask(description);
     305        }
     306
     307        @Override
     308        public void report(int itemsDone, int totalItems, String itemDescription) {
     309            subTask(String.format("Processing %d of %d %s", itemsDone, totalItems, itemDescription));
     310        }
     311
     312        @Override
     313        public void report(Exception exception) {
     314            throw new UnsupportedOperationException("Not supported yet.");
     315        }
     316
     317        @Override
     318        public void allowCancellationRequests() {
     319            setCancelable(true);
     320        }
     321
     322        @Override
     323        public boolean isCancelRequested() {
     324            return isCanceled();
     325        }
     326       
     327    }
     328   
    265329    private ConflationCandidateList generateCandidates(ConflationSettings settings) {
    266         ConflationCandidateList cands = new ConflationCandidateList();
    267 
    268         // some initialization
    269         int n = settings.getSubjectSelection().size();
    270         int m = settings.getReferenceSelection().size();
    271         double[][] cost = new double[n][m];
    272         // calculate cost matrix
    273         for (int i = 0; i < n; i++) {
    274             for (int j = 0; j < m; j++) {
    275                 cost[i][j] = ConflationUtils.calcCost(
    276                         settings.getSubjectSelection().get(i), settings.getReferenceSelection().get(j), settings);
    277             }
    278         }
    279         // perform assignment using Hungarian algorithm
    280         int[][] assignment = HungarianAlgorithm.hgAlgorithm(cost, "min");
    281         OsmPrimitive subObject;
    282         OsmPrimitive refObject;
    283         for (int i = 0; i < n; i++) {
    284             int subIdx = assignment[i][0];
    285             int refIdx = assignment[i][1];
    286             if (subIdx < n) {
    287                 subObject = settings.getSubjectSelection().get(subIdx);
    288             } else {
    289                 subObject = null;
    290             }
    291             if (refIdx < m) {
    292                 refObject = settings.getReferenceSelection().get(refIdx);
    293             } else {
    294                 refObject = null;
    295             }
    296             if (subObject != null && refObject != null) {
    297                 // TODO: do something!
    298                 if (!(cands.hasCandidate(refObject, subObject) || cands.hasCandidate(subObject, refObject))) {
    299                     cands.add(new ConflationCandidate(
    300                             refObject, settings.getReferenceLayer(),
    301                             subObject, settings.getSubjectLayer(), cost[subIdx][refIdx]));
    302                 }
    303             }
    304         }
    305         return cands;
     330        JosmTaskMonitor monitor = new JosmTaskMonitor();
     331        monitor.beginTask("Generating conflation candidates");
     332       
     333        // create Features and collections from primitive selections
     334        Set<OsmPrimitive> allPrimitives = new HashSet<OsmPrimitive>();
     335        allPrimitives.addAll(settings.getReferenceSelection());
     336        allPrimitives.addAll(settings.getSubjectSelection());
     337        FeatureCollection allFeatures = createFeatureCollection(allPrimitives);
     338        FeatureCollection refColl = new FeatureDataset(allFeatures.getFeatureSchema());
     339        FeatureCollection subColl = new FeatureDataset(allFeatures.getFeatureSchema());
     340        for (Feature f : allFeatures.getFeatures()) {
     341            OsmFeature osmFeature = (OsmFeature)f;
     342            if (settings.getReferenceSelection().contains(osmFeature.getPrimitive()))
     343                refColl.add(osmFeature);
     344            if (settings.getSubjectSelection().contains(osmFeature.getPrimitive()))
     345                subColl.add(osmFeature);
     346        }
     347       
     348        // get maximum possible distance so scores can be scaled (FIXME: not quite accurate)
     349        Envelope envelope = refColl.getEnvelope();
     350        envelope.expandToInclude(subColl.getEnvelope());
     351        double maxDistance = Point2D.distance(
     352            envelope.getMinX(),
     353            envelope.getMinY(),
     354            envelope.getMaxX(),
     355            envelope.getMaxY());
     356       
     357        // build matcher
     358        CentroidDistanceMatcher centroid = new CentroidDistanceMatcher();
     359        centroid.setMaxDistance(maxDistance);
     360        IdenticalFeatureFilter identical = new IdenticalFeatureFilter();
     361        FeatureMatcher[] matchers = {centroid, identical};
     362        ChainMatcher chain = new ChainMatcher(matchers);
     363        BasicFCMatchFinder basicFinder = new BasicFCMatchFinder(chain);
     364        OneToOneFCMatchFinder finder = new OneToOneFCMatchFinder(basicFinder);
     365
     366        // FIXME: ignore/filter duplicate objects (i.e. same object in both sets)
     367        // FIXME: fix match functions to work on point/linestring features as well
     368        // find matches
     369        Map<OsmFeature, Matches> map = finder.match(refColl, subColl, monitor);
     370       
     371        monitor.subTask("Finishing conflation candidate list");
     372       
     373        // convert to simple one-to-one match
     374        ConflationCandidateList list = new ConflationCandidateList();
     375        for (Map.Entry<OsmFeature, Matches> entry: map.entrySet()) {
     376            OsmFeature target = entry.getKey();
     377            OsmFeature subject = (OsmFeature)entry.getValue().getTopMatch();
     378            list.add(new ConflationCandidate(target.getPrimitive(), subject.getPrimitive(),
     379                    entry.getValue().getTopScore()));
     380        }
     381       
     382        monitor.finishTask();
     383        monitor.close();
     384        return list;
    306385    }
    307386
  • applications/editors/josm/plugins/conflation/src/org/openstreetmap/josm/plugins/conflation/ConflationUtils.java

    r28037 r28163  
    55import org.openstreetmap.josm.data.coor.LatLon;
    66import org.openstreetmap.josm.data.osm.OsmPrimitive;
    7 import org.openstreetmap.josm.tools.StringMetrics;
    87
    98public final class ConflationUtils {
    10     private final static double MAX_COST = Double.MAX_VALUE;
    11        
     9
    1210    public static EastNorth getCenter(OsmPrimitive prim) {
    13             LatLon center = prim.getBBox().getTopLeft().getCenter(prim.getBBox().getBottomRight());
    14             return Main.map.mapView.getProjection().latlon2eastNorth(center);
    15     }
    16    
    17     /**
    18      * Calculate the cost of a pair of <code>OsmPrimitive</code>'s. A
    19      * simple cost consisting of the Euclidean distance is used
    20      * now, later we can also use dissimilarity between tags.
    21      *
    22      * @param   referenceObject      the reference <code>OsmPrimitive</code>.
    23      * @param   subjectObject   the non-reference <code>OsmPrimitive</code>.
    24      */
    25     public static double calcCost(OsmPrimitive referenceObject, OsmPrimitive subjectObject, ConflationSettings settings) {
    26         double cost;
    27 
    28         if (referenceObject==subjectObject) {
    29             return MAX_COST;
    30         }
    31 
    32         double distance = 0;
    33         double stringCost = 1.0;
    34         if (settings.distanceWeight != 0) {
    35             distance = getCenter(referenceObject).distance(getCenter(subjectObject));
    36         }
    37         if (settings.stringWeight != 0) {
    38             String referenceString = referenceObject.getKeys().get(settings.keyString);
    39             String subjectString = subjectObject.getKeys().get(settings.keyString);
    40            
    41             if (referenceString == null ? subjectString == null : referenceString.equals(subjectString))
    42                 stringCost = 0.0;
    43             else if (referenceString == null || subjectString == null)
    44                 stringCost = 1.0;
    45             else
    46                 stringCost = 1.0 - StringMetrics.getByName("levenshtein").getSimilarity(subjectString, referenceString);
    47         }
    48        
    49         if (distance > settings.distanceCutoff || stringCost > settings.stringCutoff)
    50             cost = MAX_COST;
    51         else
    52             cost = distance * settings.distanceWeight + stringCost * settings.stringWeight;
    53 
    54         return cost;
     11        LatLon center = prim.getBBox().getTopLeft().getCenter(prim.getBBox().getBottomRight());
     12        return Main.map.mapView.getProjection().latlon2eastNorth(center);
    5513    }
    5614}
  • applications/editors/josm/plugins/conflation/src/org/openstreetmap/josm/plugins/conflation/MatchTableModel.java

    r27971 r28163  
    1414
    1515    private ConflationCandidateList candidates = null;
    16     private final static String[] columnNames = {tr("Reference"), tr("Subject"), "Distance (m)", "Cost", "Tags"};
     16    private final static String[] columnNames = {tr("Reference"), tr("Subject"), "Distance (m)", "Score", "Tags"};
    1717
    1818    @Override
     
    3333    }
    3434
     35    @Override
    3536    public Object getValueAt(int row, int col) {
    3637        if (candidates == null)
     
    4546            return c.getDistance();
    4647        } else if (col == 3) {
    47             return c.getCost();
     48            return c.getScore();
    4849        }
    4950        if (col == 4) {
  • applications/editors/josm/plugins/conflation/src/org/openstreetmap/josm/plugins/conflation/SettingsDialog.java

    r28037 r28163  
    11package org.openstreetmap.josm.plugins.conflation;
    22
    3 import java.awt.Component;
    43import java.awt.GridBagLayout;
    54import java.awt.event.ActionEvent;
     
    117116        stringCheckBox = new JCheckBox();
    118117        stringCheckBox.setSelected(false);
     118        stringCheckBox.setEnabled(false);
    119119        stringCheckBox.setText(tr("String"));
    120120        costsPanel.add(stringCheckBox, GBC.std());
     
    126126        costsPanel.add(stringTextField, GBC.std());
    127127       
     128        costsPanel.setEnabled(false);
    128129        pnl.add(costsPanel);
    129130        setContent(pnl);
Note: See TracChangeset for help on using the changeset viewer.