source: josm/trunk/src/org/openstreetmap/josm/data/osm/DataSet.java@ 2388

Last change on this file since 2388 was 2388, checked in by jttt, 14 years ago

Replace Dataset.nodes with getNodes(), etc

  • Property svn:eol-style set to native
File size: 22.9 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.data.osm;
3
4import java.awt.geom.Area;
5import java.util.ArrayList;
6import java.util.Arrays;
7import java.util.Collection;
8import java.util.Collections;
9import java.util.Comparator;
10import java.util.HashMap;
11import java.util.HashSet;
12import java.util.Iterator;
13import java.util.LinkedHashSet;
14import java.util.LinkedList;
15import java.util.List;
16import java.util.Set;
17
18import org.openstreetmap.josm.data.SelectionChangedListener;
19import org.openstreetmap.josm.data.osm.QuadBuckets.BBox;
20
21/**
22 * DataSet is the data behind the application. It can consists of only a few points up to the whole
23 * osm database. DataSet's can be merged together, saved, (up/down/disk)loaded etc.
24 *
25 * Note that DataSet is not an osm-primitive and so has no key association but a few members to
26 * store some information.
27 *
28 * @author imi
29 */
30public class DataSet implements Cloneable {
31
32 /**
33 * A list of listeners to selection changed events. The list is static, as listeners register
34 * themselves for any dataset selection changes that occur, regardless of the current active
35 * dataset. (However, the selection does only change in the active layer)
36 */
37 public static Collection<SelectionChangedListener> selListeners = new LinkedList<SelectionChangedListener>();
38
39 /**
40 * notifies all registered selection change listeners about the current selection of
41 * primitives
42 *
43 * @param sel the current selection
44 */
45 private static void notifySelectionChangeListeners(Collection<? extends OsmPrimitive> sel) {
46 for (SelectionChangedListener l : selListeners) {
47 l.selectionChanged(sel);
48 }
49 }
50
51 /**
52 * The API version that created this data set, if any.
53 */
54 public String version;
55
56 /**
57 * All nodes goes here, even when included in other data (ways etc). This enables the instant
58 * conversion of the whole DataSet by iterating over this data structure.
59 * @deprecated Use getNodes() for read-only operations, addPrimitive() and removePrimitive() for modifications
60 */
61 @Deprecated
62 public QuadBuckets<Node> nodes = new QuadBuckets<Node>();
63
64 public Collection<Node> getNodes() {
65 return Collections.unmodifiableCollection(nodes);
66 }
67
68 public List<Node> searchNodes(BBox bbox) {
69 return nodes.search(bbox);
70 }
71
72 /**
73 * All ways (Streets etc.) in the DataSet.
74 *
75 * The way nodes are stored only in the way list.
76 * @deprecated Use getWays() for read-only operations, addPrimitive() and removePrimitive() for modifications
77 */
78 @Deprecated
79 public QuadBuckets<Way> ways = new QuadBuckets<Way>();
80
81 public Collection<Way> getWays() {
82 return Collections.unmodifiableCollection(ways);
83 }
84
85 public List<Way> searchWays(BBox bbox) {
86 return ways.search(bbox);
87 }
88
89 /**
90 * All relations/relationships
91 * @deprecated Use getRelations() for read-only operations, addPrimitive() and removePrimitive() for modifications
92 */
93 @Deprecated
94 public Collection<Relation> relations = new LinkedList<Relation>();
95
96 public Collection<Relation> getRelations() {
97 return Collections.unmodifiableCollection(relations);
98 }
99
100 /**
101 * All data sources of this DataSet.
102 */
103 public Collection<DataSource> dataSources = new LinkedList<DataSource>();
104
105 /**
106 * @return A collection containing all primitives of the dataset. The data is ordered after:
107 * first come nodes, then ways, then relations. Ordering in between the categories is not
108 * guaranteed.
109 */
110 public List<OsmPrimitive> allPrimitives() {
111 List<OsmPrimitive> o = new LinkedList<OsmPrimitive>();
112 o.addAll(nodes);
113 o.addAll(ways);
114 o.addAll(relations);
115 return o;
116 }
117
118 /**
119 * @return A collection containing all not-deleted primitives (except keys).
120 */
121 public Collection<OsmPrimitive> allNonDeletedPrimitives() {
122 Collection<OsmPrimitive> o = new LinkedList<OsmPrimitive>();
123 for (OsmPrimitive osm : allPrimitives())
124 if (osm.isVisible() && !osm.isDeleted()) {
125 o.add(osm);
126 }
127 return o;
128 }
129
130 public Collection<OsmPrimitive> allNonDeletedCompletePrimitives() {
131 Collection<OsmPrimitive> o = new LinkedList<OsmPrimitive>();
132 for (OsmPrimitive osm : allPrimitives())
133 if (osm.isVisible() && !osm.isDeleted() && !osm.incomplete) {
134 o.add(osm);
135 }
136 return o;
137 }
138
139 public Collection<OsmPrimitive> allNonDeletedPhysicalPrimitives() {
140 Collection<OsmPrimitive> o = new LinkedList<OsmPrimitive>();
141 for (OsmPrimitive osm : allPrimitives())
142 if (osm.isVisible() && !osm.isDeleted() && !osm.incomplete && !(osm instanceof Relation)) {
143 o.add(osm);
144 }
145 return o;
146 }
147
148 /**
149 * Adds a primitive to the dataset
150 *
151 * @param primitive the primitive. Ignored if null.
152 */
153 public void addPrimitive(OsmPrimitive primitive) {
154 if (primitive instanceof Node) {
155 nodes.add((Node) primitive);
156 } else if (primitive instanceof Way) {
157 ways.add((Way) primitive);
158 } else if (primitive instanceof Relation) {
159 relations.add((Relation) primitive);
160 }
161 }
162
163 public OsmPrimitive addPrimitive(PrimitiveData data) {
164 if (data instanceof NodeData) {
165 Node node = new Node((NodeData)data, this);
166 nodes.add(node);
167 return node;
168 } else if (data instanceof WayData) {
169 Way way = new Way((WayData)data, this);
170 ways.add(way);
171 return way;
172 } else if (data instanceof RelationData) {
173 Relation relation = new Relation((RelationData)data, this);
174 relations.add(relation);
175 return relation;
176 } else
177 throw new AssertionError();
178 }
179
180 /**
181 * Removes a primitive from the dataset. This method only removes the
182 * primitive form the respective collection of primitives managed
183 * by this dataset, i.e. from {@see #nodes}, {@see #ways}, or
184 * {@see #relations}. References from other primitives to this
185 * primitive are left unchanged.
186 *
187 * @param primitive the primitive. Ignored if null.
188 */
189 public void removePrimitive(OsmPrimitive primitive) {
190 if (primitive == null)
191 return;
192 if (primitive instanceof Node) {
193 nodes.remove(primitive);
194 } else if (primitive instanceof Way) {
195 ways.remove(primitive);
196 } else if (primitive instanceof Relation) {
197 relations.remove(primitive);
198 }
199 selectedPrimitives.remove(primitive);
200 }
201
202 public void removePrimitive(long id, OsmPrimitiveType type) {
203 removePrimitive(getPrimitiveById(id, type));
204 }
205
206 public Collection<OsmPrimitive> getSelectedNodesAndWays() {
207 Collection<OsmPrimitive> sel = new LinkedList<OsmPrimitive>();
208 for (OsmPrimitive osm : selectedPrimitives) {
209 if (osm instanceof Way ||
210 osm instanceof Node) {
211 sel.add(osm);
212 }
213 }
214 return sel;
215 }
216
217
218 /**
219 * Return a list of all selected objects. Even keys are returned.
220 * @return List of all selected objects.
221 */
222 public Collection<OsmPrimitive> getSelected() {
223 // It would be nice to have this be a copy-on-write list
224 // or an Collections.unmodifiableList(). It would be
225 // much faster for large selections. May users just
226 // call this, and only check the .size().
227 return new ArrayList<OsmPrimitive>(selectedPrimitives);
228 }
229
230 /**
231 * Return selected nodes.
232 */
233 public Collection<OsmPrimitive> getSelectedNodes() {
234 return getSelected(nodes);
235 }
236
237 /**
238 * Return selected ways.
239 */
240 public Collection<OsmPrimitive> getSelectedWays() {
241 return getSelected(ways);
242 }
243
244 /**
245 * Return selected relations.
246 */
247 public Collection<OsmPrimitive> getSelectedRelations() {
248 return getSelected(relations);
249 }
250
251 public void setFiltered(Collection<? extends OsmPrimitive> selection) {
252 clearFiltered(nodes);
253 clearFiltered(ways);
254 clearFiltered(relations);
255 for (OsmPrimitive osm : selection) {
256 osm.setFiltered(true);
257 }
258 }
259
260 public void setFiltered(OsmPrimitive... osm) {
261 if (osm.length == 1 && osm[0] == null) {
262 setFiltered();
263 return;
264 }
265 clearFiltered(nodes);
266 clearFiltered(ways);
267 clearFiltered(relations);
268 for (OsmPrimitive o : osm)
269 if (o != null) {
270 o.setFiltered(true);
271 }
272 }
273
274 public void setDisabled(Collection<? extends OsmPrimitive> selection) {
275 clearDisabled(nodes);
276 clearDisabled(ways);
277 clearDisabled(relations);
278 for (OsmPrimitive osm : selection) {
279 osm.setDisabled(true);
280 }
281 }
282
283 LinkedHashSet<OsmPrimitive> selectedPrimitives = new LinkedHashSet<OsmPrimitive>();
284
285 public boolean toggleSelected(Collection<OsmPrimitive> osm) {
286 for (OsmPrimitive o : osm) {
287 this.__toggleSelected(o);
288 }
289 fireSelectionChanged();
290 return true;
291 }
292 public boolean toggleSelected(OsmPrimitive... osm) {
293 return this.toggleSelected(Arrays.asList(osm));
294 }
295 private boolean __toggleSelected(OsmPrimitive osm) {
296 if (!selectedPrimitives.remove(osm)) {
297 selectedPrimitives.add(osm);
298 }
299 return true;
300 }
301 public boolean isSelected(OsmPrimitive osm) {
302 return selectedPrimitives.contains(osm);
303 }
304
305 public void setDisabled(OsmPrimitive... osm) {
306 if (osm.length == 1 && osm[0] == null) {
307 setDisabled();
308 return;
309 }
310 clearDisabled(nodes);
311 clearDisabled(ways);
312 clearDisabled(relations);
313 for (OsmPrimitive o : osm)
314 if (o != null) {
315 o.setDisabled(true);
316 }
317 }
318
319 /**
320 * Sets the current selection to the primitives in <code>selection</code>.
321 * Notifies all {@see SelectionChangedListener} if <code>fireSelectionChangeEvent</code> is true.
322 *
323 * @param selection the selection
324 * @param fireSelectionChangeEvent true, if the selection change listeners are to be notified; false, otherwise
325 */
326 public void setSelected(Collection<? extends OsmPrimitive> selection, boolean fireSelectionChangeEvent) {
327 selectedPrimitives = new LinkedHashSet<OsmPrimitive>(selection);
328 if (fireSelectionChangeEvent) {
329 fireSelectionChanged();
330 }
331 }
332
333 /**
334 * Sets the current selection to the primitives in <code>selection</code>
335 * and notifies all {@see SelectionChangedListener}.
336 *
337 * @param selection the selection
338 */
339 public void setSelected(Collection<? extends OsmPrimitive> selection) {
340 setSelected(selection, true /* fire selection change event */);
341 }
342
343 /**
344 * Adds the primitives in <code>selection</code> to the current selection
345 * and notifies all {@see SelectionChangedListener}.
346 *
347 * @param selection the selection
348 */
349 public void addSelected(Collection<? extends OsmPrimitive> selection) {
350 addSelected(selection, true /* fire selection change event */);
351 }
352
353 public void addSelected(OsmPrimitive... osm) {
354 addSelected(Arrays.asList(osm));
355 }
356
357 /**
358 * Adds the primitives in <code>selection</code> to the current selection.
359 * Notifies all {@see SelectionChangedListener} if <code>fireSelectionChangeEvent</code> is true.
360 *
361 * @param selection the selection
362 * @param fireSelectionChangeEvent true, if the selection change listeners are to be notified; false, otherwise
363 */
364 public void addSelected(Collection<? extends OsmPrimitive> selection, boolean fireSelectionChangeEvent) {
365 selectedPrimitives.addAll(selection);
366 if (fireSelectionChangeEvent) {
367 fireSelectionChanged();
368 }
369 }
370
371
372 public void setSelected(OsmPrimitive... osm) {
373 if (osm.length == 1 && osm[0] == null) {
374 setSelected();
375 return;
376 }
377 List<OsmPrimitive> list = Arrays.asList(osm);
378 setSelected(list);
379 fireSelectionChanged();
380 }
381
382 /**
383 * Remove the filtered parameter from every value in the collection.
384 * @param list The collection to remove the filtered parameter from.
385 */
386 private void clearFiltered(Collection<? extends OsmPrimitive> list) {
387 if (list == null)
388 return;
389 for (OsmPrimitive osm : list) {
390 osm.setFiltered(false);
391 }
392 }
393 /**
394 * Remove the disabled parameter from every value in the collection.
395 * @param list The collection to remove the disabled parameter from.
396 */
397 private void clearDisabled(Collection<? extends OsmPrimitive> list) {
398 if (list == null)
399 return;
400 for (OsmPrimitive osm : list) {
401 osm.setDisabled(false);
402 }
403 }
404
405 /**
406 * Remove the selection from every value in the collection.
407 * @param list The collection to remove the selection from.
408 */
409 public void clearSelection(OsmPrimitive... osm) {
410 clearSelection(Arrays.asList(osm));
411 }
412 public void clearSelection(Collection<? extends OsmPrimitive> list) {
413 if (list == null)
414 return;
415 selectedPrimitives.removeAll(list);
416 }
417 public void clearSelection() {
418 selectedPrimitives.clear();
419 }
420
421 /**
422 * Return all selected items in the collection.
423 * @param list The collection from which the selected items are returned.
424 */
425 private Collection<OsmPrimitive> getSelected(Collection<? extends OsmPrimitive> list) {
426 if (list == null)
427 return new LinkedList<OsmPrimitive>();
428 // getSelected() is called with large lists, so
429 // creating the return list from the selection
430 // should be faster most of the time.
431 Collection<OsmPrimitive> sel = new LinkedHashSet<OsmPrimitive>(selectedPrimitives);
432 sel.retainAll(list);
433 return sel;
434 }
435
436 /**
437 * Notifies all registered {@see SelectionChangedListener} about the current selection in
438 * this dataset.
439 *
440 */
441 public void fireSelectionChanged(){
442 notifySelectionChangeListeners(selectedPrimitives);
443 }
444
445
446 @Override public DataSet clone() {
447 DataSet ds = new DataSet();
448 for (Node n : nodes) {
449 ds.addPrimitive(new Node(n));
450 }
451 for (Way w : ways) {
452 ds.addPrimitive(new Way(w));
453 }
454 for (Relation e : relations) {
455 ds.addPrimitive(new Relation(e));
456 }
457 for (DataSource source : dataSources) {
458 ds.dataSources.add(new DataSource(source.bounds, source.origin));
459 }
460 ds.version = version;
461 return ds;
462 }
463
464 /**
465 * Returns the total area of downloaded data (the "yellow rectangles").
466 * @return Area object encompassing downloaded data.
467 */
468 public Area getDataSourceArea() {
469 if (dataSources.isEmpty()) return null;
470 Area a = new Area();
471 for (DataSource source : dataSources) {
472 // create area from data bounds
473 a.add(new Area(source.bounds.asRect()));
474 }
475 return a;
476 }
477
478 // Provide well-defined sorting for collections of OsmPrimitives.
479 // FIXME: probably not a good place to put this code.
480 public static OsmPrimitive[] sort(Collection<? extends OsmPrimitive> list) {
481 OsmPrimitive[] selArr = new OsmPrimitive[list.size()];
482 final HashMap<Object, String> h = new HashMap<Object, String>();
483 selArr = list.toArray(selArr);
484 Arrays.sort(selArr, new Comparator<OsmPrimitive>() {
485 public int compare(OsmPrimitive a, OsmPrimitive b) {
486 if (a.getClass() == b.getClass()) {
487 String as = h.get(a);
488 if (as == null) {
489 as = a.getName() != null ? a.getName() : Long.toString(a.getId());
490 h.put(a, as);
491 }
492 String bs = h.get(b);
493 if (bs == null) {
494 bs = b.getName() != null ? b.getName() : Long.toString(b.getId());
495 h.put(b, bs);
496 }
497 int res = as.compareTo(bs);
498 if (res != 0)
499 return res;
500 }
501 return a.compareTo(b);
502 }
503 });
504 return selArr;
505 }
506
507 /**
508 * returns a primitive with a given id from the data set. null, if no such primitive
509 * exists
510 *
511 * @param id uniqueId of the primitive. Might be < 0 for newly created primitives
512 * @param type the type of the primitive. Must not be null.
513 * @return the primitive
514 * @exception NullPointerException thrown, if type is null
515 */
516 public OsmPrimitive getPrimitiveById(long id, OsmPrimitiveType type) {
517 return getPrimitiveById(id, type, false);
518 }
519
520 public OsmPrimitive getPrimitiveById(long id, OsmPrimitiveType type, boolean createNew) {
521 Collection<? extends OsmPrimitive> primitives = null;
522 switch(type) {
523 case NODE: primitives = nodes; break;
524 case WAY: primitives = ways; break;
525 case RELATION: primitives = relations; break;
526 }
527 for (OsmPrimitive primitive : primitives) {
528 if (primitive.getUniqueId() == id) return primitive;
529 }
530
531 if (createNew) {
532 OsmPrimitive result = null;
533 switch (type) {
534 case NODE: result = new Node(id, true); break;
535 case WAY: result = new Way(id, true); break;
536 case RELATION: result = new Relation(id, true); break;
537 }
538 addPrimitive(result);
539 return result;
540 } else
541 return null;
542 }
543
544 public Set<Long> getPrimitiveIds() {
545 HashSet<Long> ret = new HashSet<Long>();
546 for (OsmPrimitive primitive : nodes) {
547 ret.add(primitive.getId());
548 }
549 for (OsmPrimitive primitive : ways) {
550 ret.add(primitive.getId());
551 }
552 for (OsmPrimitive primitive : relations) {
553 ret.add(primitive.getId());
554 }
555 return ret;
556 }
557
558 protected void deleteWay(Way way) {
559 way.setNodes(null);
560 way.setDeleted(true);
561 }
562
563 /**
564 * removes all references from ways in this dataset to a particular node
565 *
566 * @param node the node
567 */
568 public void unlinkNodeFromWays(Node node) {
569 for (Way way: ways) {
570 List<Node> nodes = way.getNodes();
571 if (nodes.remove(node)) {
572 if (nodes.size() < 2) {
573 deleteWay(way);
574 } else {
575 way.setNodes(nodes);
576 }
577 }
578 }
579 }
580
581 /**
582 * removes all references from relations in this dataset to this primitive
583 *
584 * @param primitive the primitive
585 */
586 public void unlinkPrimitiveFromRelations(OsmPrimitive primitive) {
587 for (Relation relation : relations) {
588 Iterator<RelationMember> it = relation.getMembers().iterator();
589 while(it.hasNext()) {
590 RelationMember member = it.next();
591 if (member.getMember().equals(primitive)) {
592 it.remove();
593 }
594 }
595 }
596 }
597
598 /**
599 * removes all references from from other primitives to the
600 * referenced primitive
601 *
602 * @param referencedPrimitive the referenced primitive
603 */
604 public void unlinkReferencesToPrimitive(OsmPrimitive referencedPrimitive) {
605 if (referencedPrimitive instanceof Node) {
606 unlinkNodeFromWays((Node)referencedPrimitive);
607 unlinkPrimitiveFromRelations(referencedPrimitive);
608 } else {
609 unlinkPrimitiveFromRelations(referencedPrimitive);
610 }
611 }
612
613 /**
614 * Replies a list of parent relations which refer to the relation
615 * <code>child</code>. Replies an empty list if child is null.
616 *
617 * @param child the child relation
618 * @return a list of parent relations which refer to the relation
619 * <code>child</code>
620 */
621 public List<Relation> getParentRelations(Relation child) {
622 ArrayList<Relation> parents = new ArrayList<Relation>();
623 if (child == null)
624 return parents;
625 for (Relation parent : relations) {
626 if (parent == child) {
627 continue;
628 }
629 for (RelationMember member: parent.getMembers()) {
630 if (member.refersTo(child)) {
631 parents.add(parent);
632 break;
633 }
634 }
635 }
636 return parents;
637 }
638
639 /**
640 * Replies true if there is at least one primitive in this dataset with
641 * {@see OsmPrimitive#isModified()} == <code>true</code>.
642 *
643 * @return true if there is at least one primitive in this dataset with
644 * {@see OsmPrimitive#isModified()} == <code>true</code>.
645 */
646 public boolean isModified() {
647 for (Node n: nodes) {
648 if (n.isModified()) return true;
649 }
650 for (Way w: ways) {
651 if (w.isModified()) return true;
652 }
653 for (Relation r: relations) {
654 if (r.isModified()) return true;
655 }
656 return false;
657 }
658
659 public Set<Relation> getReferringRelations(Collection<? extends OsmPrimitive> primitives) {
660 return this.getReferringRelations(new HashSet<OsmPrimitive>(primitives));
661 }
662 public Set<Relation> getReferringRelations(Set<? extends OsmPrimitive> referred) {
663 HashSet<Relation> ret = new HashSet<Relation>();
664 if (referred == null) return ret;
665 referred.remove(null); // just in case - remove null element from primitives
666 for (Relation r: relations) {
667 if (r.isDeleted() || r.incomplete) {
668 continue;
669 }
670 Set<OsmPrimitive> memberPrimitives = r.getMemberPrimitives();
671 memberPrimitives.retainAll(referred);
672 if (!memberPrimitives.isEmpty()) {
673 ret.add(r);
674 }
675 }
676 return ret;
677 }
678
679 /**
680 * Reindex all nodes and ways after their coordinates were changed. This is a temporary solution, reindexing should
681 * be automatic in the future
682 */
683 public void reindexAll() {
684 List<Node> ntmp = new ArrayList<Node>(nodes);
685 nodes.clear();
686 nodes.addAll(ntmp);
687 List<Way> wtmp = new ArrayList<Way>(ways);
688 ways.clear();
689 ways.addAll(wtmp);
690 }
691
692 public void clenupDeletedPrimitives() {
693 cleanupDeleted(nodes.iterator());
694 cleanupDeleted(ways.iterator());
695 cleanupDeleted(relations.iterator());
696 }
697
698 private void cleanupDeleted(Iterator<? extends OsmPrimitive> it) {
699 while (it.hasNext()) {
700 if (it.next().isDeleted()) {
701 it.remove();
702 }
703 }
704 }
705}
Note: See TracBrowser for help on using the repository browser.