All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openstreetmap.atlas.geography.atlas.complete.CompletePoint Maven / Gradle / Ivy

There is a newer version: 7.0.8
Show newest version
package org.openstreetmap.atlas.geography.atlas.complete;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.stream.Collectors;

import org.openstreetmap.atlas.exception.CoreException;
import org.openstreetmap.atlas.geography.Location;
import org.openstreetmap.atlas.geography.Rectangle;
import org.openstreetmap.atlas.geography.atlas.change.eventhandling.event.TagChangeEvent;
import org.openstreetmap.atlas.geography.atlas.change.eventhandling.listener.TagChangeListener;
import org.openstreetmap.atlas.geography.atlas.items.Point;
import org.openstreetmap.atlas.geography.atlas.items.Relation;

/**
 * Independent {@link Point} that contains its own data. At scale, use at your own risk.
 *
 * @author matthieun
 * @author Yazad Khambata
 */
public class CompletePoint extends Point implements CompleteLocationItem
{
    private static final long serialVersionUID = 309534717673911086L;

    private Rectangle bounds;
    private long identifier;
    private Location location;
    private Map tags;
    private Set relationIdentifiers;

    private final TagChangeDelegate tagChangeDelegate = TagChangeDelegate.newTagChangeDelegate();

    /**
     * Create a {@link CompletePoint} from a given {@link Point} reference. The
     * {@link CompletePoint}'s fields will match the fields of the reference. The returned
     * {@link CompletePoint} will be full, i.e. all of its associated fields will be non-null.
     *
     * @param point
     *            the {@link Point} to copy
     * @return the full {@link CompletePoint}
     */
    public static CompletePoint from(final Point point)
    {
        if (point instanceof CompletePoint && !((CompletePoint) point).isFull())
        {
            throw new CoreException("Point parameter was a CompletePoint but it was not full: {}",
                    point);
        }
        return new CompletePoint(point.getIdentifier(), point.getLocation(), point.getTags(), point
                .relations().stream().map(Relation::getIdentifier).collect(Collectors.toSet()));
    }

    /**
     * Create a shallow {@link CompletePoint} from a given {@link Point} reference. The
     * {@link CompletePoint}'s identifier will match the identifier of the reference {@link Point}.
     * The returned {@link CompletePoint} will be shallow, i.e. all of its associated fields will be
     * null except for the identifier.
     *
     * @param point
     *            the {@link Point} to copy
     * @return the shallow {@link CompletePoint}
     */
    public static CompletePoint shallowFrom(final Point point)
    {
        if (point.bounds() == null)
        {
            throw new CoreException("Point parameter bounds were null");
        }
        return new CompletePoint(point.getIdentifier()).withBoundsExtendedBy(point.bounds());
    }

    public CompletePoint(final Long identifier, final Location location,
            final Map tags, final Set relationIdentifiers)
    {
        super(new EmptyAtlas());

        if (identifier == null)
        {
            throw new CoreException("Identifier can never be null.");
        }

        this.bounds = location != null ? location.bounds() : null;

        this.identifier = identifier;
        this.location = location;
        this.tags = tags;
        this.relationIdentifiers = relationIdentifiers;
    }

    CompletePoint(final long identifier)
    {
        this(identifier, null, null, null);
    }

    @Override
    public void addTagChangeListener(final TagChangeListener tagChangeListener)
    {
        this.tagChangeDelegate.addTagChangeListener(tagChangeListener);
    }

    @Override
    public Rectangle bounds()
    {
        return this.bounds;
    }

    @Override
    public CompleteItemType completeItemType()
    {
        return CompleteItemType.POINT;
    }

    public CompletePoint copy()
    {
        return new CompletePoint(this.identifier, this.location, this.tags,
                this.relationIdentifiers);
    }

    @Override
    public boolean equals(final Object other)
    {
        if (other instanceof CompletePoint)
        {
            final CompletePoint that = (CompletePoint) other;
            return CompleteEntity.basicEqual(this, that)
                    && Objects.equals(this.getLocation(), that.getLocation());
        }
        return false;
    }

    @Override
    public void fireTagChangeEvent(final TagChangeEvent tagChangeEvent)
    {
        this.tagChangeDelegate.fireTagChangeEvent(tagChangeEvent);
    }

    @Override
    public long getIdentifier()
    {
        return this.identifier;
    }

    @Override
    public Location getLocation()
    {
        return this.location;
    }

    @Override
    public Map getTags()
    {
        return this.tags;
    }

    @Override
    public int hashCode()
    {
        return super.hashCode();
    }

    @Override
    public boolean isFull()
    {
        return this.bounds != null && this.location != null && this.tags != null
                && this.relationIdentifiers != null;
    }

    @Override
    public boolean isShallow()
    {
        return this.location == null && this.tags == null && this.relationIdentifiers == null;
    }

    @Override
    public String prettify(final PrettifyStringFormat format, final boolean truncate)
    {
        String separator = "";
        if (format == PrettifyStringFormat.MINIMAL_SINGLE_LINE)
        {
            separator = "";
        }
        else if (format == PrettifyStringFormat.MINIMAL_MULTI_LINE)
        {
            separator = "\n";
        }
        final StringBuilder builder = new StringBuilder();

        builder.append(this.getClass().getSimpleName() + " ");
        builder.append("[");
        builder.append(separator);
        builder.append("identifier: " + this.identifier + ", ");
        builder.append(separator);
        if (this.location != null)
        {
            builder.append("location: " + this.location + ", ");
            builder.append(separator);
        }
        if (this.tags != null)
        {
            builder.append("tags: " + new TreeMap<>(this.tags) + ", ");
            builder.append(separator);
        }
        if (this.relationIdentifiers != null)
        {
            builder.append("parentRelations: " + new TreeSet<>(this.relationIdentifiers).toString()
                    + ", ");
            builder.append(separator);
        }
        if (this.bounds != null)
        {
            builder.append("bounds: " + this.bounds.toWkt() + ", ");
            builder.append(separator);
        }
        builder.append("]");

        return builder.toString();
    }

    @Override
    public Set relationIdentifiers()
    {
        return this.relationIdentifiers;
    }

    @Override
    public Set relations()
    {
        /*
         * Note that the Relations returned by this method will technically break the Located
         * contract, since they have null bounds.
         */
        return this.relationIdentifiers == null ? null
                : this.relationIdentifiers.stream().map(CompleteRelation::new)
                        .collect(Collectors.toSet());
    }

    @Override
    public void removeTagChangeListeners()
    {
        this.tagChangeDelegate.removeTagChangeListeners();
    }

    @Override
    public void setTags(final Map tags)
    {
        this.tags = tags != null ? new HashMap<>(tags) : null;
    }

    @Override
    public String toString()
    {
        return this.getClass().getSimpleName() + " [identifier=" + this.identifier + ", location="
                + this.location + ", tags=" + this.tags + ", relationIdentifiers="
                + this.relationIdentifiers + "]";
    }

    @Override
    public String toWkt()
    {
        if (this.location == null)
        {
            return null;
        }
        return this.location.toWkt();
    }

    @Override
    public CompletePoint withAddedRelationIdentifier(final Long relationIdentifier)
    {
        this.relationIdentifiers.add(relationIdentifier);
        return this;
    }

    public CompletePoint withBoundsExtendedBy(final Rectangle bounds)
    {
        if (this.bounds == null)
        {
            this.bounds = bounds;
            return this;
        }
        this.bounds = Rectangle.forLocated(this.bounds, bounds);
        return this;
    }

    @Override
    public CompletePoint withGeometry(final Iterable locations)
    {
        if (!locations.iterator().hasNext())
        {
            throw new CoreException("Cannot interpret empty Iterable as a Location");
        }
        return this.withLocation(locations.iterator().next());
    }

    @Override
    public CompletePoint withIdentifier(final long identifier)
    {
        this.identifier = identifier;
        return this;
    }

    @Override
    public CompletePoint withLocation(final Location location)
    {
        this.location = location;
        if (this.location != null)
        {
            this.bounds = location.bounds();
        }
        return this;
    }

    @Override
    public CompletePoint withRelationIdentifiers(final Set relationIdentifiers)
    {
        this.relationIdentifiers = relationIdentifiers;
        return this;
    }

    @Override
    public CompletePoint withRelations(final Set relations)
    {
        this.relationIdentifiers = relations.stream().map(Relation::getIdentifier)
                .collect(Collectors.toSet());
        return this;
    }

    @Override
    public CompletePoint withRemovedRelationIdentifier(final Long relationIdentifier)
    {
        this.relationIdentifiers = this.relationIdentifiers.stream()
                .filter(keepId -> keepId != relationIdentifier.longValue())
                .collect(Collectors.toSet());
        return this;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy