Ignore:
Timestamp:
2011-01-09T13:49:23+01:00 (14 years ago)
Author:
mzdila
Message:
  • removing the best match according to description in #5421
  • merging nearby nodes in second step
File:
1 edited

Legend:

Unmodified
Added
Removed
  • applications/editors/josm/plugins/simplifyarea/src/sk/zdila/josm/plugin/simplify/SimplifyAreaAction.java

    r24846 r25007  
    1010import java.awt.event.ActionEvent;
    1111import java.awt.event.KeyEvent;
    12 import java.util.ArrayList;
    1312import java.util.Collection;
     13import java.util.HashMap;
    1414import java.util.HashSet;
    1515import java.util.LinkedList;
    1616import java.util.List;
     17import java.util.Map;
    1718
    1819import javax.swing.JOptionPane;
     
    168169     */
    169170        private SequenceCommand simplifyWay(final Way w) {
    170         final double angleThreshold = Main.pref.getDouble("simplify-area.angle", 10);
    171         final double mergeThreshold = Main.pref.getDouble("simplify-area.merge", 0.2);
    172         final double areaThreshold = Main.pref.getDouble("simplify-area.area", 5.0);
    173         final double distanceThreshold = Main.pref.getDouble("simplify-area.dist", 3);
     171        final double angleThreshold = Main.pref.getDouble("simplify-area.angle.threshold", 10);
     172        final double angleFactor = Main.pref.getDouble("simplify-area.angle.factor", 1.0);
     173        final double mergeThreshold = Main.pref.getDouble("simplify-area.merge.threshold", 0.2);
     174        final double areaThreshold = Main.pref.getDouble("simplify-area.area.threshold", 5.0);
     175        final double areaFactor = Main.pref.getDouble("simplify-area.area.factor", 1.0);
     176        final double distanceThreshold = Main.pref.getDouble("simplify-area.dist.threshold", 3);
     177        final double distanceFactor = Main.pref.getDouble("simplify-area.dist.factor", 3);
    174178
    175179        final List<Node> nodes = w.getNodes();
     
    180184        }
    181185
    182         final List<MoveCommand> moveCommandList = new ArrayList<MoveCommand>();
    183 
    184186        final boolean closed = nodes.get(0).equals(nodes.get(size - 1));
    185 
    186         final List<Node> newNodes = new ArrayList<Node>(size);
    187187
    188188        if (closed) {
     
    190190        }
    191191
    192         // remove near nodes
    193         for (int i = 0; i < size; i++) {
    194             final boolean closing = closed && i == size - 1;
    195             final Node n1 = closing ? nodes.get(0) : nodes.get(i);
    196 
    197             if (newNodes.isEmpty()) {
    198                 newNodes.add(n1);
    199                 continue;
    200             }
    201 
    202             final Node n2 = newNodes.get(newNodes.size() - 1);
    203 
    204             final LatLon coord1 = n1.getCoor();
    205             final LatLon coord2 = n2.getCoor();
    206 
    207             if (isRequiredNode(w, n1) || isRequiredNode(w, n2) || coord1.greatCircleDistance(coord2) > mergeThreshold) {
    208                 if (!closing) {
    209                     newNodes.add(n1);
    210                 }
    211             } else {
    212                 moveCommandList.add(new MoveCommand(n2, coord1.getCenter(coord2)));
    213                 if (closing) {
    214                     newNodes.remove(0);
    215                 }
    216             }
    217         }
    218 
    219         final int size2 = newNodes.size();
    220 
    221         final List<Node> newNodes2 = new ArrayList<Node>(size2);
    222 
    223         Node prevNode = null;
    224         LatLon coord1 = null;
    225         LatLon coord2 = null;
    226 
    227         for (int i = 0, j = 0, len = size2 + (closed ? 2 : 1); i < len; i++, j++) {
    228             final Node n = newNodes.get(j % newNodes.size());
    229             final LatLon coord3 = n.getCoor();
    230 
    231             if (coord1 != null) {
    232 //              System.out.print("AREA: " + computeArea(coord1, coord2, coord3) + "; ANGLE: " + computeConvectAngle(coord1, coord2, coord3) + "; XTE: " + crossTrackError(coord1, coord2, coord3));
    233 
    234 //              final double weight = isRequiredNode(w, prevNode) || !closed && i == len - 1 ? Double.MAX_VALUE :
    235 //                      computeConvectAngle(coord1, coord2, coord3) / angleThreshold +
    236 //                      computeArea(coord1, coord2, coord3) / areaThreshold +
    237 //                      Math.abs(crossTrackError(coord1, coord2, coord3)) / distanceThreshold;
    238 
    239                 if (isRequiredNode(w, prevNode) ||
    240                                 !closed && i == len - 1 || // don't remove last node of the not closed way
    241                                 computeConvectAngle(coord1, coord2, coord3) > angleThreshold ||
    242                                 computeArea(coord1, coord2, coord3) > areaThreshold ||
    243                         Math.abs(crossTrackError(coord1, coord2, coord3)) > distanceThreshold) {
    244                     newNodes2.add(prevNode);
    245 //                      System.out.println(" ... KEEP " + prevNode.getUniqueId());
    246                 } else {
    247 //                      System.out.println(" ... REMOVE " + prevNode.getUniqueId());
    248                     coord2 = coord1; // at the end of the iteration preserve coord1
    249                     newNodes.remove(prevNode);
    250                     j--;
    251                 }
    252             } else if (!closed && prevNode != null) {
    253                 newNodes2.add(prevNode);
    254             }
    255 
    256             coord1 = coord2;
    257             coord2 = coord3;
    258             prevNode = n;
    259         }
     192        // remove nodes within threshold
     193
     194        while (true) {
     195                Node prevNode = null;
     196                LatLon coord1 = null;
     197                LatLon coord2 = null;
     198
     199                double minWeight = Double.MAX_VALUE;
     200                Node bestMatch = null;
     201
     202                for (int i = 0, len = nodes.size() + (closed ? 2 : 1); i < len; i++) {
     203                    final Node n = nodes.get(i % nodes.size());
     204                    final LatLon coord3 = n.getCoor();
     205
     206                    if (coord1 != null) {
     207                        final double angleWeight = computeConvectAngle(coord1, coord2, coord3) / angleThreshold;
     208                        final double areaWeight = computeArea(coord1, coord2, coord3) / areaThreshold;
     209                        final double distanceWeight = Math.abs(crossTrackError(coord1, coord2, coord3)) / distanceThreshold;
     210
     211                        final double weight = isRequiredNode(w, prevNode) ||
     212                                !closed && i == len - 1 || // don't remove last node of the not closed way
     213                                angleWeight > 1.0 || areaWeight > 1.0 || distanceWeight > 1.0 ? Double.MAX_VALUE :
     214                                angleWeight * angleFactor + areaWeight * areaFactor + distanceWeight * distanceFactor;
     215
     216                        if (weight < minWeight) {
     217                                minWeight = weight;
     218                                bestMatch = prevNode;
     219                        }
     220                    }
     221
     222                    coord1 = coord2;
     223                    coord2 = coord3;
     224                    prevNode = n;
     225                }
     226
     227                if (bestMatch == null) {
     228                        break;
     229                }
     230
     231                nodes.remove(bestMatch);
     232        }
     233
     234
     235        // average nearby nodes
     236
     237        final Map<Node, LatLon> coordMap = new HashMap<Node, LatLon>();
     238        for (final Node n : nodes) {
     239                coordMap.put(n, n.getCoor());
     240        }
     241
     242        final Map<Node, MoveCommand> moveCommandList = new HashMap<Node, MoveCommand>();
     243
     244        while (true) {
     245                double minDist = Double.MAX_VALUE;
     246                Node node1 = null;
     247                Node node2 = null;
     248
     249                for (int i = 0, len = nodes.size() + (closed ? 2 : 1); i < len; i++) {
     250                    final Node n1 = nodes.get(i % nodes.size());
     251                    final Node n2 = nodes.get((i + 1) % nodes.size());
     252
     253                    if (isRequiredNode(w, n1) || isRequiredNode(w, n2)) {
     254                        continue;
     255                    }
     256
     257                    final double dist = coordMap.get(n1).greatCircleDistance(coordMap.get(n2));
     258                    if (dist < minDist && dist < mergeThreshold) {
     259                        minDist = dist;
     260                        node1 = n1;
     261                        node2 = n2;
     262                    }
     263                }
     264
     265                if (node1 == null || node2 == null) {
     266                        break;
     267                }
     268
     269
     270                final LatLon coord = coordMap.get(node1).getCenter(coordMap.get(node2));
     271                coordMap.put(node1, coord);
     272                        moveCommandList.put(node1, new MoveCommand(node1, coord));
     273
     274                        nodes.remove(node2);
     275                coordMap.remove(node2);
     276                moveCommandList.remove(node2);
     277        }
     278
    260279
    261280        if (closed) {
    262             newNodes2.add(newNodes2.get(0)); // set end node ( = start node)
    263         }
    264 
    265         final HashSet<Node> delNodes = new HashSet<Node>();
    266         delNodes.addAll(nodes);
    267         delNodes.removeAll(newNodes2);
     281            nodes.add(nodes.get(0)); // set end node ( = start node)
     282        }
     283
     284        final HashSet<Node> delNodes = new HashSet<Node>(w.getNodes());
     285        delNodes.removeAll(nodes);
    268286
    269287        if (delNodes.isEmpty()) {
     
    273291        final Collection<Command> cmds = new LinkedList<Command>();
    274292        final Way newWay = new Way(w);
    275         newWay.setNodes(newNodes2);
    276 
    277         cmds.addAll(moveCommandList);
     293        newWay.setNodes(nodes);
     294
     295        cmds.addAll(moveCommandList.values());
    278296        cmds.add(new ChangeCommand(w, newWay));
    279297        cmds.add(new DeleteCommand(delNodes));
     
    295313        final double p = (a + b + c) / 2.0;
    296314
    297         return Math.sqrt(p * (p - a) * (p - b) * (p - c));
     315        final double q = p * (p - a) * (p - b) * (p - c); // I found this negative in one case (:-o) when nodes were in line on a small area
     316                return q < 0.0 ? 0.0 : Math.sqrt(q);
    298317    }
    299318
Note: See TracChangeset for help on using the changeset viewer.