source: josm/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java@ 100

Last change on this file since 100 was 100, checked in by imi, 18 years ago
  • fixed JOSM crash when importing GeoImages on layers without timestamp
  • fixed merging: incomplete segments do not overwrite complete on ways
  • fixed focus when entering the popups from PropertyDialog
  • fixed broken "draw lines between gps points"
  • added doubleclick on bookmarklist
  • added background color configuration
  • added GpxImport to import 1.0 and 1.1 GPX files

This is release JOSM 1.3

File size: 9.5 KB
Line 
1package org.openstreetmap.josm.gui.layer;
2
3import java.awt.Graphics;
4import java.awt.GridBagLayout;
5import java.awt.event.ActionEvent;
6import java.util.Collection;
7import java.util.HashSet;
8import java.util.Iterator;
9import java.util.LinkedList;
10import java.util.Set;
11import java.util.Stack;
12
13import javax.swing.Icon;
14import javax.swing.JLabel;
15import javax.swing.JMenuItem;
16import javax.swing.JOptionPane;
17import javax.swing.JPanel;
18import javax.swing.JPopupMenu;
19
20import org.openstreetmap.josm.Main;
21import org.openstreetmap.josm.actions.GpxExportAction;
22import org.openstreetmap.josm.actions.SaveAction;
23import org.openstreetmap.josm.command.Command;
24import org.openstreetmap.josm.data.osm.DataSet;
25import org.openstreetmap.josm.data.osm.Node;
26import org.openstreetmap.josm.data.osm.OsmPrimitive;
27import org.openstreetmap.josm.data.osm.Segment;
28import org.openstreetmap.josm.data.osm.Way;
29import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
30import org.openstreetmap.josm.data.osm.visitor.MergeVisitor;
31import org.openstreetmap.josm.data.osm.visitor.SimplePaintVisitor;
32import org.openstreetmap.josm.data.osm.visitor.Visitor;
33import org.openstreetmap.josm.gui.MapView;
34import org.openstreetmap.josm.gui.dialogs.ConflictDialog;
35import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
36import org.openstreetmap.josm.tools.GBC;
37import org.openstreetmap.josm.tools.ImageProvider;
38
39/**
40 * A layer holding data from a specific dataset.
41 * The data can be fully edited.
42 *
43 * @author imi
44 */
45public class OsmDataLayer extends Layer {
46
47 public final static class DataCountVisitor implements Visitor {
48 public final int[] normal = new int[3];
49 public final int[] deleted = new int[3];
50 public final String[] names = {"node", "segment", "way"};
51
52 private void inc(final OsmPrimitive osm, final int i) {
53 normal[i]++;
54 if (osm.deleted)
55 deleted[i]++;
56 }
57
58 public void visit(final Node n) {
59 inc(n, 0);
60 }
61
62 public void visit(final Segment ls) {
63 inc(ls, 1);
64 }
65
66 public void visit(final Way w) {
67 inc(w, 2);
68 }
69 }
70
71 public interface ModifiedChangedListener {
72 void modifiedChanged(boolean value, OsmDataLayer source);
73 }
74 public interface CommandQueueListener {
75 void commandChanged(int queueSize, int redoSize);
76 }
77
78 /**
79 * The data behind this layer.
80 */
81 public final DataSet data;
82
83 /**
84 * Whether the data of this layer was modified during the session.
85 */
86 private boolean modified = false;
87 /**
88 * Whether the data was modified due an upload of the data to the server.
89 */
90 public boolean uploadedModified = false;
91 /**
92 * Whether the data (or pieces of the data) was loaded from disk rather than from
93 * the server directly. This affects the modified state.
94 */
95 private boolean fromDisk = false;
96 /**
97 * All commands that were made on the dataset. Don't write from outside!
98 */
99 public final LinkedList<Command> commands = new LinkedList<Command>();
100 /**
101 * The stack for redoing commands
102 */
103 private final Stack<Command> redoCommands = new Stack<Command>();
104
105 public final LinkedList<ModifiedChangedListener> listenerModified = new LinkedList<ModifiedChangedListener>();
106 public final LinkedList<CommandQueueListener> listenerCommands = new LinkedList<CommandQueueListener>();
107
108 /**
109 * Construct a OsmDataLayer.
110 */
111 public OsmDataLayer(final DataSet data, final String name, final boolean fromDisk) {
112 super(name);
113 this.data = data;
114 this.fromDisk = fromDisk;
115 }
116
117 /**
118 * TODO: @return Return a dynamic drawn icon of the map data. The icon is
119 * updated by a background thread to not disturb the running programm.
120 */
121 @Override public Icon getIcon() {
122 return ImageProvider.get("layer", "osmdata");
123 }
124
125 /**
126 * Draw all primitives in this layer but do not draw modified ones (they
127 * are drawn by the edit layer).
128 * Draw nodes last to overlap the segments they belong to.
129 */
130 @Override public void paint(final Graphics g, final MapView mv) {
131 final SimplePaintVisitor visitor = new SimplePaintVisitor(g, mv);
132 for (final OsmPrimitive osm : data.segments)
133 if (!osm.deleted)
134 osm.visit(visitor);
135 for (final OsmPrimitive osm : data.ways)
136 if (!osm.deleted)
137 osm.visit(visitor);
138 for (final OsmPrimitive osm : data.nodes)
139 if (!osm.deleted)
140 osm.visit(visitor);
141 for (final OsmPrimitive osm : data.getSelected())
142 if (!osm.deleted)
143 osm.visit(visitor);
144 Main.map.conflictDialog.paintConflicts(g, mv);
145 }
146
147 @Override public String getToolTipText() {
148 return undeletedSize(data.nodes)+" nodes, "+
149 undeletedSize(data.segments)+" segments, "+
150 undeletedSize(data.ways)+" streets.";
151 }
152
153 @Override public void mergeFrom(final Layer from) {
154 final MergeVisitor visitor = new MergeVisitor(data);
155 for (final OsmPrimitive osm : ((OsmDataLayer)from).data.allPrimitives())
156 osm.visit(visitor);
157 visitor.fixReferences();
158 if (visitor.conflicts.isEmpty())
159 return;
160 final ConflictDialog dlg = Main.map.conflictDialog;
161 dlg.add(visitor.conflicts);
162 JOptionPane.showMessageDialog(Main.parent, "There were conflicts during import.");
163 if (!dlg.isVisible())
164 dlg.action.actionPerformed(new ActionEvent(this, 0, ""));
165 }
166
167 @Override public boolean isMergable(final Layer other) {
168 return other instanceof OsmDataLayer;
169 }
170
171 @Override public void visitBoundingBox(final BoundingXYVisitor v) {
172 for (final Node n : data.nodes)
173 if (!n.deleted)
174 v.visit(n);
175 }
176
177 /**
178 * Execute the command and add it to the intern command queue. Also mark all
179 * primitives in the command as modified.
180 */
181 public void add(final Command c) {
182 c.executeCommand();
183 commands.add(c);
184 redoCommands.clear();
185 setModified(true);
186 fireCommandsChanged();
187 }
188
189 /**
190 * Undoes the last added command.
191 */
192 public void undo() {
193 if (commands.isEmpty())
194 return;
195 final Command c = commands.removeLast();
196 c.undoCommand();
197 redoCommands.push(c);
198 //TODO: Replace with listener scheme
199 setModified(uploadedModified);
200 Main.ds.clearSelection();
201 fireCommandsChanged();
202 }
203 /**
204 * Redoes the last undoed command.
205 */
206 public void redo() {
207 if (redoCommands.isEmpty())
208 return;
209 final Command c = redoCommands.pop();
210 c.executeCommand();
211 commands.add(c);
212 setModified(true);
213 fireCommandsChanged();
214 }
215
216 /**
217 * Clean out the data behind the layer. This means clearing the redo/undo lists,
218 * really deleting all deleted objects and reset the modified flags. This is done
219 * after a successfull upload.
220 *
221 * @param processed A list of all objects, that were actually uploaded.
222 * May be <code>null</code>, which means nothing has been uploaded but
223 * saved to disk instead.
224 */
225 public void cleanData(final Collection<OsmPrimitive> processed, boolean dataAdded) {
226 redoCommands.clear();
227 commands.clear();
228
229 // if uploaded, clean the modified flags as well
230 if (processed != null) {
231 final Set<OsmPrimitive> processedSet = new HashSet<OsmPrimitive>(processed);
232 for (final Iterator<Node> it = data.nodes.iterator(); it.hasNext();)
233 cleanIterator(it, processedSet);
234 for (final Iterator<Segment> it = data.segments.iterator(); it.hasNext();)
235 cleanIterator(it, processedSet);
236 for (final Iterator<Way> it = data.ways.iterator(); it.hasNext();)
237 cleanIterator(it, processedSet);
238 }
239
240 // update the modified flag
241
242 if (fromDisk && processed != null && !dataAdded)
243 return; // do nothing when uploading non-harmful changes.
244
245 // modified if server changed the data (esp. the id).
246 uploadedModified = fromDisk && processed != null && dataAdded;
247 setModified(uploadedModified);
248 fireCommandsChanged();
249 }
250
251 public void fireCommandsChanged() {
252 for (final CommandQueueListener l : listenerCommands)
253 l.commandChanged(commands.size(), redoCommands.size());
254 }
255
256 /**
257 * Clean the modified flag for the given iterator over a collection if it is in the
258 * list of processed entries.
259 *
260 * @param it The iterator to change the modified and remove the items if deleted.
261 * @param processed A list of all objects that have been successfully progressed.
262 * If the object in the iterator is not in the list, nothing will be changed on it.
263 */
264 private void cleanIterator(final Iterator<? extends OsmPrimitive> it, final Collection<OsmPrimitive> processed) {
265 final OsmPrimitive osm = it.next();
266 if (!processed.remove(osm))
267 return;
268 osm.modified = false;
269 if (osm.deleted)
270 it.remove();
271 }
272
273 public boolean isModified() {
274 return modified;
275 }
276
277 public void setModified(final boolean modified) {
278 if (modified == this.modified)
279 return;
280 this.modified = modified;
281 for (final ModifiedChangedListener l : listenerModified)
282 l.modifiedChanged(modified, this);
283 }
284
285 /**
286 * @return The number of not-deleted primitives in the list.
287 */
288 private int undeletedSize(final Collection<? extends OsmPrimitive> list) {
289 int size = 0;
290 for (final OsmPrimitive osm : list)
291 if (!osm.deleted)
292 size++;
293 return size;
294 }
295
296 @Override public Object getInfoComponent() {
297 final DataCountVisitor counter = new DataCountVisitor();
298 for (final OsmPrimitive osm : data.allPrimitives())
299 osm.visit(counter);
300 final JPanel p = new JPanel(new GridBagLayout());
301 p.add(new JLabel(name+" consists of:"), GBC.eol());
302 for (int i = 0; i < counter.normal.length; ++i) {
303 String s = counter.normal[i]+" "+counter.names[i]+(counter.normal[i] != 1 ?"s":"");
304 if (counter.deleted[i] > 0)
305 s += " ("+counter.deleted[i]+" deleted)";
306 p.add(new JLabel(s, ImageProvider.get("data", counter.names[i]), JLabel.HORIZONTAL), GBC.eop().insets(15,0,0,0));
307 }
308 return p;
309 }
310
311 @Override public void addMenuEntries(final JPopupMenu menu) {
312 menu.add(new JMenuItem(new SaveAction()));
313 menu.add(new JMenuItem(new GpxExportAction(this)));
314 menu.addSeparator();
315 menu.add(new LayerListPopup.InfoAction(this));
316 }
317}
Note: See TracBrowser for help on using the repository browser.