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

Last change on this file since 2396 was 2396, checked in by Gubaer, 14 years ago

Made nodes, ways, and relations in DataSet private. Plugin updates to be checked in shortly.
Also added setter/getter for DataSet version.

  • Property svn:eol-style set to native
File size: 26.4 KB
Line 
1// License: GPL. See LICENSE file for details.
2
3package org.openstreetmap.josm.gui.layer;
4
5import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
6import static org.openstreetmap.josm.tools.I18n.marktr;
7import static org.openstreetmap.josm.tools.I18n.tr;
8import static org.openstreetmap.josm.tools.I18n.trn;
9
10import java.awt.AlphaComposite;
11import java.awt.Color;
12import java.awt.Component;
13import java.awt.Composite;
14import java.awt.Graphics;
15import java.awt.Graphics2D;
16import java.awt.GridBagLayout;
17import java.awt.Point;
18import java.awt.Rectangle;
19import java.awt.TexturePaint;
20import java.awt.event.ActionEvent;
21import java.awt.geom.Area;
22import java.awt.image.BufferedImage;
23import java.io.File;
24import java.util.ArrayList;
25import java.util.Collection;
26import java.util.HashSet;
27import java.util.Iterator;
28import java.util.LinkedList;
29import java.util.Set;
30
31import javax.swing.AbstractAction;
32import javax.swing.Icon;
33import javax.swing.JLabel;
34import javax.swing.JMenuItem;
35import javax.swing.JOptionPane;
36import javax.swing.JPanel;
37import javax.swing.JSeparator;
38
39import org.openstreetmap.josm.Main;
40import org.openstreetmap.josm.actions.RenameLayerAction;
41import org.openstreetmap.josm.command.PurgePrimitivesCommand;
42import org.openstreetmap.josm.data.conflict.Conflict;
43import org.openstreetmap.josm.data.conflict.ConflictCollection;
44import org.openstreetmap.josm.data.coor.EastNorth;
45import org.openstreetmap.josm.data.coor.LatLon;
46import org.openstreetmap.josm.data.gpx.GpxData;
47import org.openstreetmap.josm.data.gpx.GpxTrack;
48import org.openstreetmap.josm.data.gpx.WayPoint;
49import org.openstreetmap.josm.data.osm.BackreferencedDataSet;
50import org.openstreetmap.josm.data.osm.DataSet;
51import org.openstreetmap.josm.data.osm.DataSource;
52import org.openstreetmap.josm.data.osm.Node;
53import org.openstreetmap.josm.data.osm.OsmPrimitive;
54import org.openstreetmap.josm.data.osm.Relation;
55import org.openstreetmap.josm.data.osm.Way;
56import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
57import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
58import org.openstreetmap.josm.data.osm.visitor.MapPaintVisitor;
59import org.openstreetmap.josm.data.osm.visitor.MergeVisitor;
60import org.openstreetmap.josm.data.osm.visitor.SimplePaintVisitor;
61import org.openstreetmap.josm.gui.HelpAwareOptionPane;
62import org.openstreetmap.josm.gui.MapView;
63import org.openstreetmap.josm.gui.HelpAwareOptionPane.ButtonSpec;
64import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
65import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
66import org.openstreetmap.josm.tools.DateUtils;
67import org.openstreetmap.josm.tools.GBC;
68import org.openstreetmap.josm.tools.ImageProvider;
69
70/**
71 * A layer holding data from a specific dataset.
72 * The data can be fully edited.
73 *
74 * @author imi
75 */
76public class OsmDataLayer extends Layer {
77 static public final String REQUIRES_SAVE_TO_DISK_PROP = OsmDataLayer.class.getName() + ".requiresSaveToDisk";
78 static public final String REQUIRES_UPLOAD_TO_SERVER_PROP = OsmDataLayer.class.getName() + ".requiresUploadToServer";
79
80 private boolean requiresSaveToFile = false;
81 private boolean requiresUploadToServer = false;
82
83 protected void setRequiresSaveToFile(boolean newValue) {
84 boolean oldValue = requiresSaveToFile;
85 requiresSaveToFile = newValue;
86 if (oldValue != newValue) {
87 propertyChangeSupport.firePropertyChange(REQUIRES_SAVE_TO_DISK_PROP, oldValue, newValue);
88 }
89 }
90
91 protected void setRequiresUploadToServer(boolean newValue) {
92 boolean oldValue = requiresUploadToServer;
93 requiresUploadToServer = newValue;
94 if (oldValue != newValue) {
95 propertyChangeSupport.firePropertyChange(REQUIRES_UPLOAD_TO_SERVER_PROP, oldValue, newValue);
96 }
97 }
98
99 /** the global counter for created data layers */
100 static private int dataLayerCounter = 0;
101
102 /**
103 * Replies a new unique name for a data layer
104 *
105 * @return a new unique name for a data layer
106 */
107 static public String createNewName() {
108 dataLayerCounter++;
109 return tr("Data Layer {0}", dataLayerCounter);
110 }
111
112 public final static class DataCountVisitor extends AbstractVisitor {
113 public int nodes;
114 public int ways;
115 public int relations;
116 public int deletedNodes;
117 public int deletedWays;
118 public int deletedRelations;
119
120 public void visit(final Node n) {
121 nodes++;
122 if (n.isDeleted()) {
123 deletedNodes++;
124 }
125 }
126
127 public void visit(final Way w) {
128 ways++;
129 if (w.isDeleted()) {
130 deletedWays++;
131 }
132 }
133
134 public void visit(final Relation r) {
135 relations++;
136 if (r.isDeleted()) {
137 deletedRelations++;
138 }
139 }
140 }
141
142 public interface CommandQueueListener {
143 void commandChanged(int queueSize, int redoSize);
144 }
145
146 /**
147 * The data behind this layer.
148 */
149 public final DataSet data;
150
151 /**
152 * the collection of conflicts detected in this layer
153 */
154 private ConflictCollection conflicts;
155
156 public final LinkedList<DataChangeListener> listenerDataChanged = new LinkedList<DataChangeListener>();
157
158 /**
159 * a paint texture for non-downloaded area
160 */
161 private static TexturePaint hatched;
162
163 static {
164 createHatchTexture();
165 }
166
167 /**
168 * Initialize the hatch pattern used to paint the non-downloaded area
169 */
170 public static void createHatchTexture() {
171 BufferedImage bi = new BufferedImage(15, 15, BufferedImage.TYPE_INT_ARGB);
172 Graphics2D big = bi.createGraphics();
173 big.setColor(Main.pref.getColor(marktr("background"), Color.BLACK));
174 Composite comp = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f);
175 big.setComposite(comp);
176 big.fillRect(0,0,15,15);
177 big.setColor(Main.pref.getColor(marktr("outside downloaded area"), Color.YELLOW));
178 big.drawLine(0,15,15,0);
179 Rectangle r = new Rectangle(0, 0, 15,15);
180 hatched = new TexturePaint(bi, r);
181 }
182
183 /**
184 * Construct a OsmDataLayer.
185 */
186 public OsmDataLayer(final DataSet data, final String name, final File associatedFile) {
187 super(name);
188 this.data = data;
189 this.setAssociatedFile(associatedFile);
190 conflicts = new ConflictCollection();
191 }
192
193 /**
194 * TODO: @return Return a dynamic drawn icon of the map data. The icon is
195 * updated by a background thread to not disturb the running programm.
196 */
197 @Override public Icon getIcon() {
198 return ImageProvider.get("layer", "osmdata_small");
199 }
200
201 /**
202 * Draw all primitives in this layer but do not draw modified ones (they
203 * are drawn by the edit layer).
204 * Draw nodes last to overlap the ways they belong to.
205 */
206 @Override public void paint(final Graphics g, final MapView mv) {
207 boolean active = mv.getActiveLayer() == this;
208 boolean inactive = !active && Main.pref.getBoolean("draw.data.inactive_color", true);
209 boolean virtual = !inactive && mv.isVirtualNodesEnabled();
210
211 // draw the hatched area for non-downloaded region. only draw if we're the active
212 // and bounds are defined; don't draw for inactive layers or loaded GPX files etc
213 if (active && Main.pref.getBoolean("draw.data.downloaded_area", true) && !data.dataSources.isEmpty()) {
214 // initialize area with current viewport
215 Rectangle b = mv.getBounds();
216 // on some platforms viewport bounds seem to be offset from the left,
217 // over-grow it just to be sure
218 b.grow(100, 100);
219 Area a = new Area(b);
220
221 // now succesively subtract downloaded areas
222 for (DataSource src : data.dataSources) {
223 if (src.bounds != null && !src.bounds.getMin().equals(src.bounds.getMax())) {
224 EastNorth en1 = mv.getProjection().latlon2eastNorth(src.bounds.getMin());
225 EastNorth en2 = mv.getProjection().latlon2eastNorth(src.bounds.getMax());
226 Point p1 = mv.getPoint(en1);
227 Point p2 = mv.getPoint(en2);
228 Rectangle r = new Rectangle(Math.min(p1.x, p2.x),Math.min(p1.y, p2.y),Math.abs(p2.x-p1.x),Math.abs(p2.y-p1.y));
229 a.subtract(new Area(r));
230 }
231 }
232
233 // paint remainder
234 ((Graphics2D)g).setPaint(hatched);
235 ((Graphics2D)g).fill(a);
236 }
237
238 SimplePaintVisitor painter;
239 if (Main.pref.getBoolean("draw.wireframe")) {
240 painter = new SimplePaintVisitor();
241 } else {
242 painter = new MapPaintVisitor();
243 }
244 painter.setGraphics(g);
245 painter.setNavigatableComponent(mv);
246 painter.inactive = inactive;
247 painter.visitAll(data, virtual);
248 Main.map.conflictDialog.paintConflicts(g, mv);
249 }
250
251 @Override public String getToolTipText() {
252 int nodes = undeletedSize(data.getNodes());
253 int ways = undeletedSize(data.getWays());
254
255 String tool = trn("{0} node", "{0} nodes", nodes, nodes)+", ";
256 tool += trn("{0} way", "{0} ways", ways, ways);
257
258 if (data.getVersion() != null) {
259 tool += ", " + tr("version {0}", data.getVersion());
260 }
261 File f = getAssociatedFile();
262 if (f != null) {
263 tool = "<html>"+tool+"<br>"+f.getPath()+"</html>";
264 }
265 return tool;
266 }
267
268 @Override public void mergeFrom(final Layer from) {
269 mergeFrom(((OsmDataLayer)from).data);
270 }
271
272 /**
273 * merges the primitives in dataset <code>from</code> into the dataset of
274 * this layer
275 *
276 * @param from the source data set
277 */
278 public void mergeFrom(final DataSet from) {
279 final MergeVisitor visitor = new MergeVisitor(data,from);
280 visitor.merge();
281
282 Area a = data.getDataSourceArea();
283
284 // copy the merged layer's data source info;
285 // only add source rectangles if they are not contained in the
286 // layer already.
287 for (DataSource src : from.dataSources) {
288 if (a == null || !a.contains(src.bounds.asRect())) {
289 data.dataSources.add(src);
290 }
291 }
292
293 // copy the merged layer's API version, downgrade if required
294 if (data.getVersion() == null) {
295 data.setVersion(from.getVersion());
296 } else if ("0.5".equals(data.getVersion()) ^ "0.5".equals(from.getVersion())) {
297 System.err.println(tr("Warning: mixing 0.6 and 0.5 data results in version 0.5"));
298 data.setVersion("0.5");
299 }
300
301 int numNewConflicts = 0;
302 for (Conflict<?> c : visitor.getConflicts()) {
303 if (!conflicts.hasConflict(c)) {
304 numNewConflicts++;
305 conflicts.add(c);
306 }
307 }
308 PurgePrimitivesCommand cmd = buildPurgeCommand();
309 if (cmd != null) {
310 Main.main.undoRedo.add(cmd);
311 }
312 fireDataChange();
313 // repaint to make sure new data is displayed properly.
314 Main.map.mapView.repaint();
315 warnNumNewConflicts(
316 numNewConflicts,
317 cmd == null ? 0 : cmd.getPurgedPrimitives().size()
318 );
319 }
320
321 /**
322 * Warns the user about the number of detected conflicts
323 *
324 * @param numNewConflicts the number of detected conflicts
325 * @param numPurgedPrimitives the number of automatically purged objects
326 */
327 protected void warnNumNewConflicts(int numNewConflicts, int numPurgedPrimitives) {
328 if (numNewConflicts == 0 && numPurgedPrimitives == 0) return;
329
330 String msg1 = trn(
331 "There was {0} conflict detected.",
332 "There were {0} conflicts detected.",
333 numNewConflicts,
334 numNewConflicts
335 );
336 String msg2 = trn(
337 "{0} conflict has been <strong>resolved automatically</strong> by purging {0} object<br>from the local dataset because it is deleted on the server.",
338 "{0} conflicts have been <strong>resolved automatically</strong> by purging {0} objects<br> from the local dataset because they are deleted on the server.",
339 numPurgedPrimitives,
340 numPurgedPrimitives
341 );
342 int numRemainingConflicts = numNewConflicts - numPurgedPrimitives;
343 String msg3 = "";
344 if (numRemainingConflicts >0) {
345 msg3 = trn(
346 "{0} conflict remains to be resolved.<br><br>Please open the Conflict List Dialog and manually resolve it.",
347 "{0} conflicts remain to be resolved.<br><br>Please open the Conflict List Dialog and manually resolve them.",
348 numRemainingConflicts,
349 numRemainingConflicts
350 );
351 }
352
353 StringBuffer sb = new StringBuffer();
354 sb.append("<html>").append(msg1);
355 if (numPurgedPrimitives > 0) {
356 sb.append("<br>").append(msg2);
357 }
358 if (numRemainingConflicts > 0) {
359 sb.append("<br>").append(msg3);
360 }
361 sb.append("</html>");
362 if (numNewConflicts > 0) {
363 ButtonSpec[] options = new ButtonSpec[] {
364 new ButtonSpec(
365 tr("OK"),
366 ImageProvider.get("ok"),
367 tr("Click to close this dialog and continue editing"),
368 null /* no specific help */
369 )
370 };
371 HelpAwareOptionPane.showOptionDialog(
372 Main.parent,
373 sb.toString(),
374 tr("Conflicts detected"),
375 JOptionPane.WARNING_MESSAGE,
376 null, /* no icon */
377 options,
378 options[0],
379 ht("/Concepts/Conflict#WarningAboutDetectedConflicts")
380 );
381 }
382 }
383
384 /**
385 * Builds the purge command for primitives which can be purged automatically
386 * from the local dataset because they've been deleted on the
387 * server.
388 *
389 * @return the purge command. <code>null</code> if no primitives have to
390 * be purged
391 */
392 protected PurgePrimitivesCommand buildPurgeCommand() {
393 BackreferencedDataSet ds = new BackreferencedDataSet(data);
394 ds.build();
395 ArrayList<OsmPrimitive> toPurge = new ArrayList<OsmPrimitive>();
396 conflictLoop: for (Conflict<?> c: conflicts) {
397 if (c.getMy().isDeleted() && !c.getTheir().isVisible()) {
398 // Local and server version of the primitive are deleted. We
399 // can purge it from the local dataset.
400 //
401 toPurge.add(c.getMy());
402 } else if (!c.getMy().isModified() && ! c.getTheir().isVisible()) {
403 // We purge deleted *ways* and *relations* automatically if they are
404 // deleted on the server and if they aren't modified in the local
405 // dataset.
406 //
407 if (c.getMy() instanceof Way || c.getMy() instanceof Relation) {
408 toPurge.add(c.getMy());
409 continue conflictLoop;
410 }
411 // We only purge nodes if they aren't part of a modified way.
412 // Otherwise the number of nodes of a modified way could drop
413 // below 2 and we would lose the modified data when the way
414 // gets purged.
415 //
416 for (OsmPrimitive parent: ds.getParents(c.getMy())) {
417 if (parent.isModified() && parent instanceof Way) {
418 continue conflictLoop;
419 }
420 }
421 toPurge.add(c.getMy());
422 }
423 }
424 if (toPurge.isEmpty()) return null;
425 PurgePrimitivesCommand cmd = new PurgePrimitivesCommand(this, toPurge);
426 cmd.setBackreferenceDataSet(ds);
427 return cmd;
428 }
429
430 @Override public boolean isMergable(final Layer other) {
431 return other instanceof OsmDataLayer;
432 }
433
434 @Override public void visitBoundingBox(final BoundingXYVisitor v) {
435 for (final Node n: data.getNodes()) {
436 if (n.isUsable()) {
437 v.visit(n);
438 }
439 }
440 }
441
442 /**
443 * Clean out the data behind the layer. This means clearing the redo/undo lists,
444 * really deleting all deleted objects and reset the modified flags. This should
445 * be done after an upload, even after a partial upload.
446 *
447 * @param processed A list of all objects that were actually uploaded.
448 * May be <code>null</code>, which means nothing has been uploaded
449 */
450 public void cleanupAfterUpload(final Collection<OsmPrimitive> processed) {
451 // return immediately if an upload attempt failed
452 if (processed == null || processed.isEmpty())
453 return;
454
455 Main.main.undoRedo.clean(this);
456
457 // if uploaded, clean the modified flags as well
458 final Set<OsmPrimitive> processedSet = new HashSet<OsmPrimitive>(processed);
459 data.clenupDeletedPrimitives();
460 for (final Iterator<Node> it = data.getNodes().iterator(); it.hasNext();) {
461 cleanIterator(it, processedSet);
462 }
463 for (final Iterator<Way> it = data.getWays().iterator(); it.hasNext();) {
464 cleanIterator(it, processedSet);
465 }
466 for (final Iterator<Relation> it = data.getRelations().iterator(); it.hasNext();) {
467 cleanIterator(it, processedSet);
468 }
469 }
470
471 /**
472 * Clean the modified flag for the given iterator over a collection if it is in the
473 * list of processed entries.
474 *
475 * @param it The iterator to change the modified and remove the items if deleted.
476 * @param processed A list of all objects that have been successfully progressed.
477 * If the object in the iterator is not in the list, nothing will be changed on it.
478 */
479 private void cleanIterator(final Iterator<? extends OsmPrimitive> it, final Collection<OsmPrimitive> processed) {
480 final OsmPrimitive osm = it.next();
481 if (!processed.remove(osm))
482 return;
483 osm.setModified(false);
484 }
485
486 /**
487 * @return The number of not-deleted primitives in the list.
488 */
489 private int undeletedSize(final Collection<? extends OsmPrimitive> list) {
490 int size = 0;
491 for (final OsmPrimitive osm : list)
492 if (!osm.isDeleted()) {
493 size++;
494 }
495 return size;
496 }
497
498 @Override public Object getInfoComponent() {
499 final DataCountVisitor counter = new DataCountVisitor();
500 for (final OsmPrimitive osm : data.allPrimitives()) {
501 osm.visit(counter);
502 }
503 final JPanel p = new JPanel(new GridBagLayout());
504
505 String nodeText = trn("{0} node", "{0} nodes", counter.nodes, counter.nodes);
506 if (counter.deletedNodes > 0) {
507 nodeText += " ("+trn("{0} deleted", "{0} deleted", counter.deletedNodes, counter.deletedNodes)+")";
508 }
509
510 String wayText = trn("{0} way", "{0} ways", counter.ways, counter.ways);
511 if (counter.deletedWays > 0) {
512 wayText += " ("+trn("{0} deleted", "{0} deleted", counter.deletedWays, counter.deletedWays)+")";
513 }
514
515 String relationText = trn("{0} relation", "{0} relations", counter.relations, counter.relations);
516 if (counter.deletedRelations > 0) {
517 relationText += " ("+trn("{0} deleted", "{0} deleted", counter.deletedRelations, counter.deletedRelations)+")";
518 }
519
520 p.add(new JLabel(tr("{0} consists of:", getName())), GBC.eol());
521 p.add(new JLabel(nodeText, ImageProvider.get("data", "node"), JLabel.HORIZONTAL), GBC.eop().insets(15,0,0,0));
522 p.add(new JLabel(wayText, ImageProvider.get("data", "way"), JLabel.HORIZONTAL), GBC.eop().insets(15,0,0,0));
523 p.add(new JLabel(relationText, ImageProvider.get("data", "relation"), JLabel.HORIZONTAL), GBC.eop().insets(15,0,0,0));
524 p.add(new JLabel(tr("API version: {0}", (data.getVersion() != null) ? data.getVersion() : tr("unset"))));
525
526 return p;
527 }
528
529 @Override public Component[] getMenuEntries() {
530 if (Main.applet)
531 return new Component[]{
532 new JMenuItem(LayerListDialog.getInstance().createActivateLayerAction(this)),
533 new JMenuItem(LayerListDialog.getInstance().createShowHideLayerAction(this)),
534 new JMenuItem(LayerListDialog.getInstance().createDeleteLayerAction(this)),
535 new JSeparator(),
536 new JMenuItem(LayerListDialog.getInstance().createMergeLayerAction(this)),
537 new JSeparator(),
538 new JMenuItem(new RenameLayerAction(getAssociatedFile(), this)),
539 new JSeparator(),
540 new JMenuItem(new LayerListPopup.InfoAction(this))};
541 return new Component[]{
542 new JMenuItem(LayerListDialog.getInstance().createActivateLayerAction(this)),
543 new JMenuItem(LayerListDialog.getInstance().createShowHideLayerAction(this)),
544 new JMenuItem(LayerListDialog.getInstance().createDeleteLayerAction(this)),
545 new JSeparator(),
546 new JMenuItem(LayerListDialog.getInstance().createMergeLayerAction(this)),
547 new JMenuItem(new LayerSaveAction(this)),
548 new JMenuItem(new LayerSaveAsAction(this)),
549 new JMenuItem(new LayerGpxExportAction(this)),
550 new JMenuItem(new ConvertToGpxLayerAction()),
551 new JSeparator(),
552 new JMenuItem(new RenameLayerAction(getAssociatedFile(), this)),
553 new JSeparator(),
554 new JMenuItem(new LayerListPopup.InfoAction(this))};
555 }
556
557 public void fireDataChange() {
558 setRequiresSaveToFile(true);
559 setRequiresUploadToServer(true);
560 for (DataChangeListener dcl : listenerDataChanged) {
561 dcl.dataChanged(this);
562 }
563 }
564
565 public static GpxData toGpxData(DataSet data, File file) {
566 GpxData gpxData = new GpxData();
567 gpxData.storageFile = file;
568 HashSet<Node> doneNodes = new HashSet<Node>();
569 for (Way w : data.getWays()) {
570 if (!w.isUsable()) {
571 continue;
572 }
573 GpxTrack trk = new GpxTrack();
574 gpxData.tracks.add(trk);
575
576 if (w.get("name") != null) {
577 trk.attr.put("name", w.get("name"));
578 }
579
580 ArrayList<WayPoint> trkseg = null;
581 for (Node n : w.getNodes()) {
582 if (!n.isUsable()) {
583 trkseg = null;
584 continue;
585 }
586 if (trkseg == null) {
587 trkseg = new ArrayList<WayPoint>();
588 trk.trackSegs.add(trkseg);
589 }
590 if (!n.isTagged()) {
591 doneNodes.add(n);
592 }
593 WayPoint wpt = new WayPoint(n.getCoor());
594 if (!n.isTimestampEmpty()) {
595 wpt.attr.put("time", DateUtils.fromDate(n.getTimestamp()));
596 wpt.setTime();
597 }
598 trkseg.add(wpt);
599 }
600 }
601
602 // what is this loop meant to do? it creates waypoints but never
603 // records them?
604 for (Node n : data.getNodes()) {
605 if (n.incomplete || n.isDeleted() || doneNodes.contains(n)) {
606 continue;
607 }
608 WayPoint wpt = new WayPoint(n.getCoor());
609 if (!n.isTimestampEmpty()) {
610 wpt.attr.put("time", DateUtils.fromDate(n.getTimestamp()));
611 wpt.setTime();
612 }
613 String name = n.get("name");
614 if (name != null) {
615 wpt.attr.put("name", name);
616 }
617 ;
618 }
619 return gpxData;
620 }
621
622 public GpxData toGpxData() {
623 return toGpxData(data, getAssociatedFile());
624 }
625
626 public class ConvertToGpxLayerAction extends AbstractAction {
627 public ConvertToGpxLayerAction() {
628 super(tr("Convert to GPX layer"), ImageProvider.get("converttogpx"));
629 }
630 public void actionPerformed(ActionEvent e) {
631 Main.main.addLayer(new GpxLayer(toGpxData(), tr("Converted from: {0}", getName())));
632 Main.main.removeLayer(OsmDataLayer.this);
633 }
634 }
635
636 public boolean containsPoint(LatLon coor) {
637 // we'll assume that if this has no data sources
638 // that it also has no borders
639 if (this.data.dataSources.isEmpty())
640 return true;
641
642 boolean layer_bounds_point = false;
643 for (DataSource src : this.data.dataSources) {
644 if (src.bounds.contains(coor)) {
645 layer_bounds_point = true;
646 break;
647 }
648 }
649 return layer_bounds_point;
650 }
651
652 /**
653 * replies the set of conflicts currently managed in this layer
654 *
655 * @return the set of conflicts currently managed in this layer
656 */
657 public ConflictCollection getConflicts() {
658 return conflicts;
659 }
660
661 /**
662 * Replies true if the data managed by this layer needs to be uploaded to
663 * the server because it contains at least one modified primitive.
664 *
665 * @return true if the data managed by this layer needs to be uploaded to
666 * the server because it contains at least one modified primitive; false,
667 * otherwise
668 */
669 public boolean requiresUploadToServer() {
670 return requiresUploadToServer;
671 }
672
673 /**
674 * Replies true if the data managed by this layer needs to be saved to
675 * a file. Only replies true if a file is assigned to this layer and
676 * if the data managed by this layer has been modified since the last
677 * save operation to the file.
678 *
679 * @return true if the data managed by this layer needs to be saved to
680 * a file
681 */
682 public boolean requiresSaveToFile() {
683 return getAssociatedFile() != null && requiresSaveToFile;
684 }
685
686 /**
687 * Initializes the layer after a successful load of OSM data from a file
688 *
689 */
690 public void onPostLoadFromFile() {
691 setRequiresSaveToFile(false);
692 setRequiresUploadToServer(data.isModified());
693 }
694
695 /**
696 * Initializes the layer after a successful save of OSM data to a file
697 *
698 */
699 public void onPostSaveToFile() {
700 setRequiresSaveToFile(false);
701 setRequiresUploadToServer(data.isModified());
702 }
703
704 /**
705 * Initializes the layer after a successful upload to the server
706 *
707 */
708 public void onPostUploadToServer() {
709 setRequiresUploadToServer(data.isModified());
710 // keep requiresSaveToDisk unchanged
711 }
712}
Note: See TracBrowser for help on using the repository browser.