[3719] | 1 | // License: GPL. For details, see LICENSE file.
|
---|
[343] | 2 | package org.openstreetmap.josm.data.osm;
|
---|
| 3 |
|
---|
[1925] | 4 | import java.util.ArrayList;
|
---|
[3347] | 5 | import java.util.Arrays;
|
---|
[2308] | 6 | import java.util.Collection;
|
---|
[6564] | 7 | import java.util.Collections;
|
---|
[2070] | 8 | import java.util.HashSet;
|
---|
[343] | 9 | import java.util.List;
|
---|
[5676] | 10 | import java.util.Map;
|
---|
[11553] | 11 | import java.util.Optional;
|
---|
[2070] | 12 | import java.util.Set;
|
---|
[11038] | 13 | import java.util.stream.Collectors;
|
---|
[343] | 14 |
|
---|
[12809] | 15 | import org.openstreetmap.josm.data.osm.visitor.OsmPrimitiveVisitor;
|
---|
[4100] | 16 | import org.openstreetmap.josm.data.osm.visitor.PrimitiveVisitor;
|
---|
[12846] | 17 | import org.openstreetmap.josm.spi.preferences.Config;
|
---|
[1925] | 18 | import org.openstreetmap.josm.tools.CopyList;
|
---|
[10657] | 19 | import org.openstreetmap.josm.tools.SubclassFilteredCollection;
|
---|
[5578] | 20 | import org.openstreetmap.josm.tools.Utils;
|
---|
[343] | 21 |
|
---|
| 22 | /**
|
---|
[7432] | 23 | * A relation, having a set of tags and any number (0...n) of members.
|
---|
[1169] | 24 | *
|
---|
[6830] | 25 | * @author Frederik Ramm
|
---|
[343] | 26 | */
|
---|
[4098] | 27 | public final class Relation extends OsmPrimitive implements IRelation {
|
---|
[343] | 28 |
|
---|
[3347] | 29 | private RelationMember[] members = new RelationMember[0];
|
---|
[343] | 30 |
|
---|
[2982] | 31 | private BBox bbox;
|
---|
| 32 |
|
---|
[1925] | 33 | /**
|
---|
| 34 | * @return Members of the relation. Changes made in returned list are not mapped
|
---|
| 35 | * back to the primitive, use setMembers() to modify the members
|
---|
| 36 | * @since 1925
|
---|
| 37 | */
|
---|
| 38 | public List<RelationMember> getMembers() {
|
---|
[7005] | 39 | return new CopyList<>(members);
|
---|
[1925] | 40 | }
|
---|
| 41 |
|
---|
| 42 | /**
|
---|
| 43 | *
|
---|
[1926] | 44 | * @param members Can be null, in that case all members are removed
|
---|
[1925] | 45 | * @since 1925
|
---|
| 46 | */
|
---|
| 47 | public void setMembers(List<RelationMember> members) {
|
---|
[3348] | 48 | boolean locked = writeLock();
|
---|
| 49 | try {
|
---|
[3943] | 50 | for (RelationMember rm : this.members) {
|
---|
[3348] | 51 | rm.getMember().removeReferrer(this);
|
---|
[3943] | 52 | rm.getMember().clearCachedStyle();
|
---|
[3348] | 53 | }
|
---|
[2407] | 54 |
|
---|
[3348] | 55 | if (members != null) {
|
---|
| 56 | this.members = members.toArray(new RelationMember[members.size()]);
|
---|
[3359] | 57 | } else {
|
---|
| 58 | this.members = new RelationMember[0];
|
---|
[3348] | 59 | }
|
---|
[3943] | 60 | for (RelationMember rm : this.members) {
|
---|
[3348] | 61 | rm.getMember().addReferrer(this);
|
---|
[3943] | 62 | rm.getMember().clearCachedStyle();
|
---|
[3348] | 63 | }
|
---|
| 64 |
|
---|
| 65 | fireMembersChanged();
|
---|
| 66 | } finally {
|
---|
| 67 | writeUnlock(locked);
|
---|
[1926] | 68 | }
|
---|
[1925] | 69 | }
|
---|
| 70 |
|
---|
[4098] | 71 | @Override
|
---|
[1926] | 72 | public int getMembersCount() {
|
---|
[3347] | 73 | return members.length;
|
---|
[1926] | 74 | }
|
---|
| 75 |
|
---|
[11038] | 76 | /**
|
---|
| 77 | * Returns the relation member at the specified index.
|
---|
| 78 | * @param index the index of the relation member
|
---|
| 79 | * @return relation member at the specified index
|
---|
| 80 | */
|
---|
[1926] | 81 | public RelationMember getMember(int index) {
|
---|
[3347] | 82 | return members[index];
|
---|
[1926] | 83 | }
|
---|
| 84 |
|
---|
[11038] | 85 | /**
|
---|
| 86 | * Adds the specified relation member at the last position.
|
---|
| 87 | * @param member the member to add
|
---|
| 88 | */
|
---|
[1951] | 89 | public void addMember(RelationMember member) {
|
---|
[3348] | 90 | boolean locked = writeLock();
|
---|
| 91 | try {
|
---|
[6717] | 92 | members = Utils.addInArrayCopy(members, member);
|
---|
[3348] | 93 | member.getMember().addReferrer(this);
|
---|
[3943] | 94 | member.getMember().clearCachedStyle();
|
---|
[3348] | 95 | fireMembersChanged();
|
---|
| 96 | } finally {
|
---|
| 97 | writeUnlock(locked);
|
---|
| 98 | }
|
---|
[1951] | 99 | }
|
---|
| 100 |
|
---|
[11038] | 101 | /**
|
---|
| 102 | * Adds the specified relation member at the specified index.
|
---|
| 103 | * @param member the member to add
|
---|
| 104 | * @param index the index at which the specified element is to be inserted
|
---|
| 105 | */
|
---|
[1951] | 106 | public void addMember(int index, RelationMember member) {
|
---|
[3348] | 107 | boolean locked = writeLock();
|
---|
| 108 | try {
|
---|
| 109 | RelationMember[] newMembers = new RelationMember[members.length + 1];
|
---|
| 110 | System.arraycopy(members, 0, newMembers, 0, index);
|
---|
| 111 | System.arraycopy(members, index, newMembers, index + 1, members.length - index);
|
---|
| 112 | newMembers[index] = member;
|
---|
| 113 | members = newMembers;
|
---|
| 114 | member.getMember().addReferrer(this);
|
---|
[3943] | 115 | member.getMember().clearCachedStyle();
|
---|
[3348] | 116 | fireMembersChanged();
|
---|
| 117 | } finally {
|
---|
| 118 | writeUnlock(locked);
|
---|
| 119 | }
|
---|
[1951] | 120 | }
|
---|
| 121 |
|
---|
| 122 | /**
|
---|
| 123 | * Replace member at position specified by index.
|
---|
[8470] | 124 | * @param index index (positive integer)
|
---|
| 125 | * @param member relation member to set
|
---|
[1951] | 126 | * @return Member that was at the position
|
---|
| 127 | */
|
---|
| 128 | public RelationMember setMember(int index, RelationMember member) {
|
---|
[3348] | 129 | boolean locked = writeLock();
|
---|
| 130 | try {
|
---|
| 131 | RelationMember originalMember = members[index];
|
---|
| 132 | members[index] = member;
|
---|
[10662] | 133 | if (originalMember.getMember() != member.getMember()) {
|
---|
[3348] | 134 | member.getMember().addReferrer(this);
|
---|
[3943] | 135 | member.getMember().clearCachedStyle();
|
---|
[3348] | 136 | originalMember.getMember().removeReferrer(this);
|
---|
[3943] | 137 | originalMember.getMember().clearCachedStyle();
|
---|
[3348] | 138 | fireMembersChanged();
|
---|
| 139 | }
|
---|
| 140 | return originalMember;
|
---|
| 141 | } finally {
|
---|
| 142 | writeUnlock(locked);
|
---|
[2407] | 143 | }
|
---|
[1951] | 144 | }
|
---|
| 145 |
|
---|
| 146 | /**
|
---|
| 147 | * Removes member at specified position.
|
---|
[8470] | 148 | * @param index index (positive integer)
|
---|
[1951] | 149 | * @return Member that was at the position
|
---|
| 150 | */
|
---|
| 151 | public RelationMember removeMember(int index) {
|
---|
[3348] | 152 | boolean locked = writeLock();
|
---|
| 153 | try {
|
---|
| 154 | List<RelationMember> members = getMembers();
|
---|
| 155 | RelationMember result = members.remove(index);
|
---|
| 156 | setMembers(members);
|
---|
| 157 | return result;
|
---|
| 158 | } finally {
|
---|
| 159 | writeUnlock(locked);
|
---|
| 160 | }
|
---|
[1951] | 161 | }
|
---|
| 162 |
|
---|
[4098] | 163 | @Override
|
---|
| 164 | public long getMemberId(int idx) {
|
---|
| 165 | return members[idx].getUniqueId();
|
---|
| 166 | }
|
---|
| 167 |
|
---|
| 168 | @Override
|
---|
| 169 | public String getRole(int idx) {
|
---|
| 170 | return members[idx].getRole();
|
---|
| 171 | }
|
---|
| 172 |
|
---|
| 173 | @Override
|
---|
| 174 | public OsmPrimitiveType getMemberType(int idx) {
|
---|
| 175 | return members[idx].getType();
|
---|
| 176 | }
|
---|
| 177 |
|
---|
[12813] | 178 | /**
|
---|
| 179 | * @deprecated no longer supported
|
---|
| 180 | */
|
---|
[7556] | 181 | @Override
|
---|
[12809] | 182 | @Deprecated
|
---|
| 183 | public void accept(org.openstreetmap.josm.data.osm.visitor.Visitor visitor) {
|
---|
[1169] | 184 | visitor.visit(this);
|
---|
| 185 | }
|
---|
[343] | 186 |
|
---|
[7556] | 187 | @Override
|
---|
[12809] | 188 | public void accept(OsmPrimitiveVisitor visitor) {
|
---|
| 189 | visitor.visit(this);
|
---|
| 190 | }
|
---|
| 191 |
|
---|
| 192 | @Override
|
---|
[7556] | 193 | public void accept(PrimitiveVisitor visitor) {
|
---|
[4100] | 194 | visitor.visit(this);
|
---|
| 195 | }
|
---|
| 196 |
|
---|
[2284] | 197 | protected Relation(long id, boolean allowNegative) {
|
---|
| 198 | super(id, allowNegative);
|
---|
| 199 | }
|
---|
| 200 |
|
---|
[1169] | 201 | /**
|
---|
[2070] | 202 | * Create a new relation with id 0
|
---|
[1169] | 203 | */
|
---|
[2070] | 204 | public Relation() {
|
---|
[2284] | 205 | super(0, false);
|
---|
[1169] | 206 | }
|
---|
[343] | 207 |
|
---|
[6140] | 208 | /**
|
---|
| 209 | * Constructs an identical clone of the argument.
|
---|
| 210 | * @param clone The relation to clone
|
---|
[8509] | 211 | * @param clearMetadata If {@code true}, clears the OSM id and other metadata as defined by {@link #clearOsmMetadata}.
|
---|
| 212 | * If {@code false}, does nothing
|
---|
[6140] | 213 | */
|
---|
| 214 | public Relation(Relation clone, boolean clearMetadata) {
|
---|
[2410] | 215 | super(clone.getUniqueId(), true);
|
---|
| 216 | cloneFrom(clone);
|
---|
[6140] | 217 | if (clearMetadata) {
|
---|
| 218 | clearOsmMetadata();
|
---|
[2410] | 219 | }
|
---|
| 220 | }
|
---|
| 221 |
|
---|
[1169] | 222 | /**
|
---|
[2070] | 223 | * Create an identical clone of the argument (including the id)
|
---|
[6140] | 224 | * @param clone The relation to clone, including its id
|
---|
[1169] | 225 | */
|
---|
[2070] | 226 | public Relation(Relation clone) {
|
---|
[2410] | 227 | this(clone, false);
|
---|
[1169] | 228 | }
|
---|
[343] | 229 |
|
---|
[1169] | 230 | /**
|
---|
[6830] | 231 | * Creates a new relation for the given id. If the id > 0, the way is marked
|
---|
[2070] | 232 | * as incomplete.
|
---|
[2284] | 233 | *
|
---|
[6830] | 234 | * @param id the id. > 0 required
|
---|
[8291] | 235 | * @throws IllegalArgumentException if id < 0
|
---|
[1169] | 236 | */
|
---|
[8291] | 237 | public Relation(long id) {
|
---|
[2284] | 238 | super(id, false);
|
---|
[1169] | 239 | }
|
---|
[755] | 240 |
|
---|
[2620] | 241 | /**
|
---|
[2932] | 242 | * Creates new relation
|
---|
[8470] | 243 | * @param id the id
|
---|
| 244 | * @param version version number (positive integer)
|
---|
[2620] | 245 | */
|
---|
| 246 | public Relation(long id, int version) {
|
---|
[2932] | 247 | super(id, version, false);
|
---|
[2620] | 248 | }
|
---|
| 249 |
|
---|
[7556] | 250 | @Override
|
---|
| 251 | public void cloneFrom(OsmPrimitive osm) {
|
---|
[11383] | 252 | if (!(osm instanceof Relation))
|
---|
| 253 | throw new IllegalArgumentException("Not a relation: " + osm);
|
---|
[3348] | 254 | boolean locked = writeLock();
|
---|
| 255 | try {
|
---|
| 256 | super.cloneFrom(osm);
|
---|
| 257 | // It's not necessary to clone members as RelationMember class is immutable
|
---|
[8510] | 258 | setMembers(((Relation) osm).getMembers());
|
---|
[3348] | 259 | } finally {
|
---|
| 260 | writeUnlock(locked);
|
---|
| 261 | }
|
---|
[1169] | 262 | }
|
---|
[755] | 263 |
|
---|
[7556] | 264 | @Override
|
---|
| 265 | public void load(PrimitiveData data) {
|
---|
[11383] | 266 | if (!(data instanceof RelationData))
|
---|
| 267 | throw new IllegalArgumentException("Not a relation data: " + data);
|
---|
[3348] | 268 | boolean locked = writeLock();
|
---|
| 269 | try {
|
---|
| 270 | super.load(data);
|
---|
[2284] | 271 |
|
---|
[3348] | 272 | RelationData relationData = (RelationData) data;
|
---|
[2284] | 273 |
|
---|
[7005] | 274 | List<RelationMember> newMembers = new ArrayList<>();
|
---|
[3348] | 275 | for (RelationMemberData member : relationData.getMembers()) {
|
---|
[11553] | 276 | newMembers.add(new RelationMember(member.getRole(), Optional.ofNullable(getDataSet().getPrimitiveById(member))
|
---|
| 277 | .orElseThrow(() -> new AssertionError("Data consistency problem - relation with missing member detected"))));
|
---|
[3348] | 278 | }
|
---|
| 279 | setMembers(newMembers);
|
---|
| 280 | } finally {
|
---|
| 281 | writeUnlock(locked);
|
---|
[2284] | 282 | }
|
---|
| 283 | }
|
---|
| 284 |
|
---|
[11383] | 285 | @Override
|
---|
| 286 | public RelationData save() {
|
---|
[2284] | 287 | RelationData data = new RelationData();
|
---|
| 288 | saveCommonAttributes(data);
|
---|
| 289 | for (RelationMember member:getMembers()) {
|
---|
| 290 | data.getMembers().add(new RelationMemberData(member.getRole(), member.getMember()));
|
---|
| 291 | }
|
---|
| 292 | return data;
|
---|
| 293 | }
|
---|
| 294 |
|
---|
[8379] | 295 | @Override
|
---|
| 296 | public String toString() {
|
---|
[10242] | 297 | StringBuilder result = new StringBuilder(32);
|
---|
[8379] | 298 | result.append("{Relation id=")
|
---|
| 299 | .append(getUniqueId())
|
---|
| 300 | .append(" version=")
|
---|
| 301 | .append(getVersion())
|
---|
[8390] | 302 | .append(' ')
|
---|
[8379] | 303 | .append(getFlagsAsString())
|
---|
| 304 | .append(" [");
|
---|
[2363] | 305 | for (RelationMember rm:getMembers()) {
|
---|
[8379] | 306 | result.append(OsmPrimitiveType.from(rm.getMember()))
|
---|
[8390] | 307 | .append(' ')
|
---|
[8379] | 308 | .append(rm.getMember().getUniqueId())
|
---|
| 309 | .append(", ");
|
---|
[2363] | 310 | }
|
---|
[8379] | 311 | result.delete(result.length()-2, result.length())
|
---|
[8390] | 312 | .append("]}");
|
---|
[2363] | 313 | return result.toString();
|
---|
[1169] | 314 | }
|
---|
| 315 |
|
---|
[1690] | 316 | @Override
|
---|
[9979] | 317 | public boolean hasEqualSemanticAttributes(OsmPrimitive other, boolean testInterestingTagsOnly) {
|
---|
[11292] | 318 | return (other instanceof Relation)
|
---|
| 319 | && hasEqualSemanticFlags(other)
|
---|
| 320 | && Arrays.equals(members, ((Relation) other).members)
|
---|
| 321 | && super.hasEqualSemanticAttributes(other, testInterestingTagsOnly);
|
---|
[1690] | 322 | }
|
---|
| 323 |
|
---|
[4098] | 324 | @Override
|
---|
[1169] | 325 | public int compareTo(OsmPrimitive o) {
|
---|
[8365] | 326 | return o instanceof Relation ? Long.compare(getUniqueId(), o.getUniqueId()) : -1;
|
---|
[1169] | 327 | }
|
---|
| 328 |
|
---|
[8365] | 329 | /**
|
---|
| 330 | * Returns the first member.
|
---|
| 331 | * @return first member, or {@code null}
|
---|
| 332 | */
|
---|
[1596] | 333 | public RelationMember firstMember() {
|
---|
[8365] | 334 | return (isIncomplete() || members.length == 0) ? null : members[0];
|
---|
| 335 | }
|
---|
[3348] | 336 |
|
---|
[8365] | 337 | /**
|
---|
| 338 | * Returns the last member.
|
---|
| 339 | * @return last member, or {@code null}
|
---|
| 340 | */
|
---|
[1596] | 341 | public RelationMember lastMember() {
|
---|
[8365] | 342 | return (isIncomplete() || members.length == 0) ? null : members[members.length - 1];
|
---|
[1596] | 343 | }
|
---|
[1670] | 344 |
|
---|
| 345 | /**
|
---|
| 346 | * removes all members with member.member == primitive
|
---|
[1677] | 347 | *
|
---|
[1670] | 348 | * @param primitive the primitive to check for
|
---|
| 349 | */
|
---|
| 350 | public void removeMembersFor(OsmPrimitive primitive) {
|
---|
[6564] | 351 | removeMembersFor(Collections.singleton(primitive));
|
---|
[1670] | 352 | }
|
---|
[1990] | 353 |
|
---|
[2407] | 354 | @Override
|
---|
| 355 | public void setDeleted(boolean deleted) {
|
---|
[3348] | 356 | boolean locked = writeLock();
|
---|
| 357 | try {
|
---|
| 358 | for (RelationMember rm:members) {
|
---|
| 359 | if (deleted) {
|
---|
| 360 | rm.getMember().removeReferrer(this);
|
---|
| 361 | } else {
|
---|
| 362 | rm.getMember().addReferrer(this);
|
---|
| 363 | }
|
---|
[2407] | 364 | }
|
---|
[3348] | 365 | super.setDeleted(deleted);
|
---|
| 366 | } finally {
|
---|
| 367 | writeUnlock(locked);
|
---|
[2407] | 368 | }
|
---|
| 369 | }
|
---|
| 370 |
|
---|
[2308] | 371 | /**
|
---|
[6564] | 372 | * Obtains all members with member.member == primitive
|
---|
| 373 | * @param primitives the primitives to check for
|
---|
[9243] | 374 | * @return all relation members for the given primitives
|
---|
[6564] | 375 | */
|
---|
| 376 | public Collection<RelationMember> getMembersFor(final Collection<? extends OsmPrimitive> primitives) {
|
---|
[10657] | 377 | return SubclassFilteredCollection.filter(getMembers(), member -> primitives.contains(member.getMember()));
|
---|
[6564] | 378 | }
|
---|
| 379 |
|
---|
| 380 | /**
|
---|
[2308] | 381 | * removes all members with member.member == primitive
|
---|
| 382 | *
|
---|
| 383 | * @param primitives the primitives to check for
|
---|
[5613] | 384 | * @since 5613
|
---|
[2308] | 385 | */
|
---|
[5613] | 386 | public void removeMembersFor(Collection<? extends OsmPrimitive> primitives) {
|
---|
[2308] | 387 | if (primitives == null || primitives.isEmpty())
|
---|
| 388 | return;
|
---|
| 389 |
|
---|
[3348] | 390 | boolean locked = writeLock();
|
---|
| 391 | try {
|
---|
| 392 | List<RelationMember> members = getMembers();
|
---|
[6564] | 393 | members.removeAll(getMembersFor(primitives));
|
---|
[3348] | 394 | setMembers(members);
|
---|
| 395 | } finally {
|
---|
| 396 | writeUnlock(locked);
|
---|
[2308] | 397 | }
|
---|
| 398 | }
|
---|
| 399 |
|
---|
[1990] | 400 | @Override
|
---|
| 401 | public String getDisplayName(NameFormatter formatter) {
|
---|
| 402 | return formatter.format(this);
|
---|
| 403 | }
|
---|
[2070] | 404 |
|
---|
| 405 | /**
|
---|
[5266] | 406 | * Replies the set of {@link OsmPrimitive}s referred to by at least one
|
---|
[2070] | 407 | * member of this relation
|
---|
[2284] | 408 | *
|
---|
[5266] | 409 | * @return the set of {@link OsmPrimitive}s referred to by at least one
|
---|
[2070] | 410 | * member of this relation
|
---|
[11038] | 411 | * @see #getMemberPrimitivesList()
|
---|
[2070] | 412 | */
|
---|
| 413 | public Set<OsmPrimitive> getMemberPrimitives() {
|
---|
[11038] | 414 | return getMembers().stream().map(RelationMember::getMember).collect(Collectors.toSet());
|
---|
[2070] | 415 | }
|
---|
[2399] | 416 |
|
---|
[11038] | 417 | /**
|
---|
| 418 | * Returns the {@link OsmPrimitive}s of the specified type referred to by at least one member of this relation.
|
---|
| 419 | * @param tClass the type of the primitive
|
---|
| 420 | * @param <T> the type of the primitive
|
---|
| 421 | * @return the primitives
|
---|
| 422 | */
|
---|
[6575] | 423 | public <T extends OsmPrimitive> Collection<T> getMemberPrimitives(Class<T> tClass) {
|
---|
[11038] | 424 | return Utils.filteredCollection(getMemberPrimitivesList(), tClass);
|
---|
[6575] | 425 | }
|
---|
| 426 |
|
---|
[11038] | 427 | /**
|
---|
| 428 | * Returns an unmodifiable list of the {@link OsmPrimitive}s referred to by at least one member of this relation.
|
---|
| 429 | * @return an unmodifiable list of the primitives
|
---|
| 430 | */
|
---|
[5578] | 431 | public List<OsmPrimitive> getMemberPrimitivesList() {
|
---|
[10717] | 432 | return Utils.transform(getMembers(), RelationMember::getMember);
|
---|
[5578] | 433 | }
|
---|
| 434 |
|
---|
[4098] | 435 | @Override
|
---|
[2399] | 436 | public OsmPrimitiveType getType() {
|
---|
| 437 | return OsmPrimitiveType.RELATION;
|
---|
| 438 | }
|
---|
[2427] | 439 |
|
---|
[4098] | 440 | @Override
|
---|
[3844] | 441 | public OsmPrimitiveType getDisplayType() {
|
---|
[9716] | 442 | return isMultipolygon() && !isBoundary() ? OsmPrimitiveType.MULTIPOLYGON : OsmPrimitiveType.RELATION;
|
---|
[3844] | 443 | }
|
---|
| 444 |
|
---|
[9716] | 445 | /**
|
---|
| 446 | * Determines if this relation is a boundary.
|
---|
| 447 | * @return {@code true} if a boundary relation
|
---|
| 448 | */
|
---|
| 449 | public boolean isBoundary() {
|
---|
| 450 | return "boundary".equals(get("type"));
|
---|
| 451 | }
|
---|
| 452 |
|
---|
[10716] | 453 | @Override
|
---|
[3844] | 454 | public boolean isMultipolygon() {
|
---|
[9716] | 455 | return "multipolygon".equals(get("type")) || isBoundary();
|
---|
[3844] | 456 | }
|
---|
| 457 |
|
---|
[2427] | 458 | @Override
|
---|
| 459 | public BBox getBBox() {
|
---|
[11269] | 460 | if (getDataSet() != null && bbox != null)
|
---|
| 461 | return new BBox(bbox); // use cached value
|
---|
[3348] | 462 |
|
---|
[11269] | 463 | BBox box = new BBox();
|
---|
| 464 | addToBBox(box, new HashSet<PrimitiveId>());
|
---|
| 465 | if (getDataSet() != null)
|
---|
[11316] | 466 | setBBox(box); // set cache
|
---|
[11269] | 467 | return new BBox(box);
|
---|
[2427] | 468 | }
|
---|
[2437] | 469 |
|
---|
[11316] | 470 | private void setBBox(BBox bbox) {
|
---|
| 471 | this.bbox = bbox;
|
---|
| 472 | }
|
---|
| 473 |
|
---|
[11269] | 474 | @Override
|
---|
| 475 | protected void addToBBox(BBox box, Set<PrimitiveId> visited) {
|
---|
| 476 | for (RelationMember rm : members) {
|
---|
| 477 | if (visited.add(rm.getMember()))
|
---|
| 478 | rm.getMember().addToBBox(box, visited);
|
---|
[2982] | 479 | }
|
---|
| 480 | }
|
---|
| 481 |
|
---|
[2437] | 482 | @Override
|
---|
| 483 | public void updatePosition() {
|
---|
[11316] | 484 | setBBox(null); // make sure that it is recalculated
|
---|
| 485 | setBBox(getBBox());
|
---|
[2437] | 486 | }
|
---|
[2439] | 487 |
|
---|
[2970] | 488 | @Override
|
---|
[7796] | 489 | void setDataset(DataSet dataSet) {
|
---|
[2970] | 490 | super.setDataset(dataSet);
|
---|
| 491 | checkMembers();
|
---|
[11316] | 492 | setBBox(null); // bbox might have changed if relation was in ds, was removed, modified, added back to dataset
|
---|
[2970] | 493 | }
|
---|
| 494 |
|
---|
[10632] | 495 | /**
|
---|
| 496 | * Checks that members are part of the same dataset, and that they're not deleted.
|
---|
| 497 | * @throws DataIntegrityProblemException if one the above conditions is not met
|
---|
| 498 | */
|
---|
| 499 | private void checkMembers() {
|
---|
[2963] | 500 | DataSet dataSet = getDataSet();
|
---|
| 501 | if (dataSet != null) {
|
---|
[3348] | 502 | RelationMember[] members = this.members;
|
---|
[2963] | 503 | for (RelationMember rm: members) {
|
---|
| 504 | if (rm.getMember().getDataSet() != dataSet)
|
---|
[8509] | 505 | throw new DataIntegrityProblemException(
|
---|
| 506 | String.format("Relation member must be part of the same dataset as relation(%s, %s)",
|
---|
| 507 | getPrimitiveId(), rm.getMember().getPrimitiveId()));
|
---|
[2963] | 508 | }
|
---|
[12846] | 509 | if (Config.getPref().getBoolean("debug.checkDeleteReferenced", true)) {
|
---|
[3032] | 510 | for (RelationMember rm: members) {
|
---|
| 511 | if (rm.getMember().isDeleted())
|
---|
| 512 | throw new DataIntegrityProblemException("Deleted member referenced: " + toString());
|
---|
| 513 | }
|
---|
| 514 | }
|
---|
[2439] | 515 | }
|
---|
| 516 | }
|
---|
[2970] | 517 |
|
---|
[10632] | 518 | /**
|
---|
| 519 | * Fires the {@code RelationMembersChangedEvent} to listeners.
|
---|
| 520 | * @throws DataIntegrityProblemException if members are not valid
|
---|
| 521 | * @see #checkMembers
|
---|
| 522 | */
|
---|
| 523 | private void fireMembersChanged() {
|
---|
[2970] | 524 | checkMembers();
|
---|
| 525 | if (getDataSet() != null) {
|
---|
| 526 | getDataSet().fireRelationMembersChanged(this);
|
---|
| 527 | }
|
---|
| 528 | }
|
---|
[3102] | 529 |
|
---|
| 530 | /**
|
---|
[7432] | 531 | * Determines if at least one child primitive is incomplete.
|
---|
[3107] | 532 | *
|
---|
[3102] | 533 | * @return true if at least one child primitive is incomplete
|
---|
| 534 | */
|
---|
| 535 | public boolean hasIncompleteMembers() {
|
---|
[3348] | 536 | RelationMember[] members = this.members;
|
---|
[3102] | 537 | for (RelationMember rm: members) {
|
---|
| 538 | if (rm.getMember().isIncomplete()) return true;
|
---|
| 539 | }
|
---|
| 540 | return false;
|
---|
| 541 | }
|
---|
| 542 |
|
---|
| 543 | /**
|
---|
[7432] | 544 | * Replies a collection with the incomplete children this relation refers to.
|
---|
[3107] | 545 | *
|
---|
[3102] | 546 | * @return the incomplete children. Empty collection if no children are incomplete.
|
---|
| 547 | */
|
---|
| 548 | public Collection<OsmPrimitive> getIncompleteMembers() {
|
---|
[7005] | 549 | Set<OsmPrimitive> ret = new HashSet<>();
|
---|
[3348] | 550 | RelationMember[] members = this.members;
|
---|
[3102] | 551 | for (RelationMember rm: members) {
|
---|
| 552 | if (!rm.getMember().isIncomplete()) {
|
---|
| 553 | continue;
|
---|
| 554 | }
|
---|
| 555 | ret.add(rm.getMember());
|
---|
| 556 | }
|
---|
| 557 | return ret;
|
---|
| 558 | }
|
---|
[5676] | 559 |
|
---|
| 560 | @Override
|
---|
| 561 | protected void keysChangedImpl(Map<String, String> originalKeys) {
|
---|
| 562 | super.keysChangedImpl(originalKeys);
|
---|
[11038] | 563 | for (OsmPrimitive member : getMemberPrimitivesList()) {
|
---|
[7091] | 564 | member.clearCachedStyle();
|
---|
[5676] | 565 | }
|
---|
| 566 | }
|
---|
[6491] | 567 |
|
---|
| 568 | @Override
|
---|
| 569 | public boolean concernsArea() {
|
---|
| 570 | return isMultipolygon() && hasAreaTags();
|
---|
| 571 | }
|
---|
[6639] | 572 |
|
---|
| 573 | @Override
|
---|
| 574 | public boolean isOutsideDownloadArea() {
|
---|
| 575 | return false;
|
---|
| 576 | }
|
---|
[7556] | 577 |
|
---|
| 578 | /**
|
---|
| 579 | * Returns the set of roles used in this relation.
|
---|
| 580 | * @return the set of roles used in this relation. Can be empty but never null
|
---|
| 581 | * @since 7556
|
---|
| 582 | */
|
---|
| 583 | public Set<String> getMemberRoles() {
|
---|
| 584 | Set<String> result = new HashSet<>();
|
---|
| 585 | for (RelationMember rm : members) {
|
---|
| 586 | String role = rm.getRole();
|
---|
| 587 | if (!role.isEmpty()) {
|
---|
| 588 | result.add(role);
|
---|
| 589 | }
|
---|
| 590 | }
|
---|
| 591 | return result;
|
---|
| 592 | }
|
---|
[343] | 593 | }
|
---|