source: osm/applications/editors/josm/plugins/utilsplugin/src/UtilsPlugin/SimplifyWayAction.java@ 16419

Last change on this file since 16419 was 16419, checked in by guggis, 16 years ago

updated for core r1758 after rework of projection handling and of editor layer access

  • Property svn:eol-style set to native
File size: 8.3 KB
Line 
1package UtilsPlugin;
2
3import static org.openstreetmap.josm.tools.I18n.tr;
4import static org.openstreetmap.josm.tools.I18n.trn;
5
6import java.awt.event.ActionEvent;
7import java.awt.event.KeyEvent;
8import java.util.ArrayList;
9import java.util.Collection;
10import java.util.Collections;
11import java.util.HashSet;
12import java.util.LinkedList;
13
14import javax.swing.JOptionPane;
15
16import org.openstreetmap.josm.Main;
17import org.openstreetmap.josm.actions.JosmAction;
18import org.openstreetmap.josm.command.ChangeCommand;
19import org.openstreetmap.josm.command.Command;
20import org.openstreetmap.josm.command.DeleteCommand;
21import org.openstreetmap.josm.command.SequenceCommand;
22import org.openstreetmap.josm.data.Bounds;
23import org.openstreetmap.josm.data.osm.DataSource;
24import org.openstreetmap.josm.data.osm.Node;
25import org.openstreetmap.josm.data.osm.OsmPrimitive;
26import org.openstreetmap.josm.data.osm.Way;
27import org.openstreetmap.josm.data.osm.visitor.CollectBackReferencesVisitor;
28import org.openstreetmap.josm.gui.layer.OsmDataLayer;
29import org.openstreetmap.josm.tools.Shortcut;
30
31public class SimplifyWayAction extends JosmAction {
32 public SimplifyWayAction() {
33 super(tr("Simplify Way"), "simplify", tr("Delete unnecessary nodes from a way."), Shortcut.registerShortcut("tools:simplify", tr("Tool: {0}", tr("Simplify Way")),
34 KeyEvent.VK_Y, Shortcut.GROUP_EDIT, Shortcut.SHIFT_DEFAULT), true);
35 }
36
37 public void actionPerformed(ActionEvent e) {
38 Collection<OsmPrimitive> selection = Main.ds.getSelected();
39
40 int ways = 0;
41 LinkedList<Bounds> bounds = new LinkedList<Bounds>();
42 OsmDataLayer dataLayer = Main.map.mapView.getEditLayer();
43 for (DataSource ds : dataLayer.data.dataSources) {
44 if (ds.bounds != null)
45 bounds.add(ds.bounds);
46 }
47 for (OsmPrimitive prim : selection) {
48 if (prim instanceof Way) {
49 if (bounds.size() > 0) {
50 Way way = (Way) prim;
51 // We check if each node of each way is at least in one download
52 // bounding box. Otherwise nodes may get deleted that are necessary by
53 // unloaded ways (see Ticket #1594)
54 for (Node node : way.nodes) {
55 boolean isInsideOneBoundingBox = false;
56 for (Bounds b : bounds) {
57 if (b.contains(node.getCoor())) {
58 isInsideOneBoundingBox = true;
59 break;
60 }
61 }
62 if (!isInsideOneBoundingBox) {
63 int option = JOptionPane.showConfirmDialog(Main.parent,
64 tr("The selected way(s) have nodes outside of the downloaded data region.\n"
65 + "This can lead to nodes being deleted accidentally.\n"
66 + "Are you really sure to continue?"),
67 tr("Please abort if you are not sure"), JOptionPane.YES_NO_CANCEL_OPTION,
68 JOptionPane.WARNING_MESSAGE);
69
70 if (option != JOptionPane.YES_OPTION)
71 return;
72 break;
73 }
74 }
75 }
76
77 ways++;
78 }
79 }
80
81 if (ways == 0) {
82 JOptionPane.showMessageDialog(Main.parent, tr("Please select at least one way to simplify."));
83 return;
84 } else if (ways > 10) {
85 //TRANSLATION: Although for English the use of trn is needless it is important for other languages
86 int option = JOptionPane.showConfirmDialog(Main.parent, trn(
87 "The selection contains {0} way. Are you sure you want to simplify it?",
88 "The selection contains {0} ways. Are you sure you want to simplify them all?",
89 ways,ways),
90 tr("Are you sure?"), JOptionPane.YES_NO_OPTION);
91 if (option != JOptionPane.YES_OPTION)
92 return;
93 }
94
95 for (OsmPrimitive prim : selection) {
96 if (prim instanceof Way) {
97 simplifyWay((Way) prim);
98 }
99 }
100 }
101
102 public void simplifyWay(Way w) {
103 double threshold = Double.parseDouble(Main.pref.get("simplify-way.max-error", "3"));
104
105 Way wnew = new Way(w);
106
107 int toI = wnew.nodes.size() - 1;
108 for (int i = wnew.nodes.size() - 1; i >= 0; i--) {
109 CollectBackReferencesVisitor backRefsV = new CollectBackReferencesVisitor(Main.ds, false);
110 backRefsV.visit(wnew.nodes.get(i));
111 boolean used = false;
112 if (backRefsV.data.size() == 1) {
113 used = Collections.frequency(w.nodes, wnew.nodes.get(i)) > 1;
114 } else {
115 backRefsV.data.remove(w);
116 used = !backRefsV.data.isEmpty();
117 }
118 if (!used)
119 used = wnew.nodes.get(i).isTagged();
120
121 if (used) {
122 simplifyWayRange(wnew, i, toI, threshold);
123 toI = i;
124 }
125 }
126 simplifyWayRange(wnew, 0, toI, threshold);
127
128 HashSet<Node> delNodes = new HashSet<Node>();
129 delNodes.addAll(w.nodes);
130 delNodes.removeAll(wnew.nodes);
131
132 if (wnew.nodes.size() != w.nodes.size()) {
133 Collection<Command> cmds = new LinkedList<Command>();
134 cmds.add(new ChangeCommand(w, wnew));
135 cmds.add(new DeleteCommand(delNodes));
136 Main.main.undoRedo.add(new SequenceCommand(trn("Simplify Way (remove {0} node)", "Simplify Way (remove {0} nodes)", delNodes.size(), delNodes.size()), cmds));
137 Main.map.repaint();
138 }
139 }
140
141 public void simplifyWayRange(Way wnew, int from, int to, double thr) {
142 if (to - from >= 2) {
143 ArrayList<Node> ns = new ArrayList<Node>();
144 simplifyWayRange(wnew, from, to, ns, thr);
145 for (int j = to - 1; j > from; j--)
146 wnew.nodes.remove(j);
147 wnew.nodes.addAll(from + 1, ns);
148 }
149 }
150
151 /*
152 * Takes an interval [from,to] and adds nodes from (from,to) to ns.
153 * (from and to are indices of wnew.nodes.)
154 */
155 public void simplifyWayRange(Way wnew, int from, int to, ArrayList<Node> ns, double thr) {
156 Node fromN = wnew.nodes.get(from), toN = wnew.nodes.get(to);
157
158 int imax = -1;
159 double xtemax = 0;
160 for (int i = from + 1; i < to; i++) {
161 Node n = wnew.nodes.get(i);
162 double xte = Math.abs(EARTH_RAD
163 * xtd(fromN.getCoor().lat() * Math.PI / 180, fromN.getCoor().lon() * Math.PI / 180, toN.getCoor().lat() * Math.PI
164 / 180, toN.getCoor().lon() * Math.PI / 180, n.getCoor().lat() * Math.PI / 180, n.getCoor().lon() * Math.PI
165 / 180));
166 if (xte > xtemax) {
167 xtemax = xte;
168 imax = i;
169 }
170 }
171
172 if (imax != -1 && xtemax >= thr) {
173 simplifyWayRange(wnew, from, imax, ns, thr);
174 ns.add(wnew.nodes.get(imax));
175 simplifyWayRange(wnew, imax, to, ns, thr);
176 }
177 }
178
179 public static double EARTH_RAD = 6378137.0;
180
181 /* From Aviaton Formulary v1.3
182 * http://williams.best.vwh.net/avform.htm
183 */
184 public static double dist(double lat1, double lon1, double lat2, double lon2) {
185 return 2 * Math.asin(Math.sqrt(Math.pow(Math.sin((lat1 - lat2) / 2), 2) + Math.cos(lat1) * Math.cos(lat2)
186 * Math.pow(Math.sin((lon1 - lon2) / 2), 2)));
187 }
188
189 public static double course(double lat1, double lon1, double lat2, double lon2) {
190 return Math.atan2(Math.sin(lon1 - lon2) * Math.cos(lat2), Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1)
191 * Math.cos(lat2) * Math.cos(lon1 - lon2))
192 % (2 * Math.PI);
193 }
194
195 public static double xtd(double lat1, double lon1, double lat2, double lon2, double lat3, double lon3) {
196 double dist_AD = dist(lat1, lon1, lat3, lon3);
197 double crs_AD = course(lat1, lon1, lat3, lon3);
198 double crs_AB = course(lat1, lon1, lat2, lon2);
199 return Math.asin(Math.sin(dist_AD) * Math.sin(crs_AD - crs_AB));
200 }
201}
Note: See TracBrowser for help on using the repository browser.