001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.data.vector;
003
004import java.util.ArrayList;
005import java.util.Collections;
006import java.util.List;
007import java.util.stream.Collectors;
008
009import org.openstreetmap.josm.data.osm.BBox;
010import org.openstreetmap.josm.data.osm.INode;
011import org.openstreetmap.josm.data.osm.IWay;
012import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
013import org.openstreetmap.josm.data.osm.UniqueIdGenerator;
014import org.openstreetmap.josm.data.osm.visitor.PrimitiveVisitor;
015
016/**
017 * The "Way" type for a Vector layer
018 *
019 * @author Taylor Smock
020 * @since 17862
021 */
022public class VectorWay extends VectorPrimitive implements IWay<VectorNode> {
023    private static final UniqueIdGenerator WAY_GENERATOR = new UniqueIdGenerator();
024    private final List<VectorNode> nodes = new ArrayList<>();
025    private BBox cachedBBox;
026
027    /**
028     * Create a new way for a layer
029     * @param layer The layer for the way
030     */
031    public VectorWay(String layer) {
032        super(layer);
033    }
034
035    @Override
036    public UniqueIdGenerator getIdGenerator() {
037        return WAY_GENERATOR;
038    }
039
040    @Override
041    public void accept(PrimitiveVisitor visitor) {
042        visitor.visit(this);
043    }
044
045    @Override
046    public BBox getBBox() {
047        if (this.cachedBBox == null) {
048            BBox tBBox = new BBox();
049            for (INode node : this.getNodes()) {
050                tBBox.add(node.getBBox());
051            }
052            this.cachedBBox = tBBox.toImmutable();
053        }
054        return this.cachedBBox;
055    }
056
057    @Override
058    public int getNodesCount() {
059        return this.getNodes().size();
060    }
061
062    @Override
063    public VectorNode getNode(int index) {
064        return this.getNodes().get(index);
065    }
066
067    @Override
068    public List<VectorNode> getNodes() {
069        return Collections.unmodifiableList(this.nodes);
070    }
071
072    @Override
073    public void setNodes(List<VectorNode> nodes) {
074        this.nodes.forEach(node -> node.removeReferrer(this));
075        this.nodes.clear();
076        if (nodes != null) {
077            nodes.forEach(node -> node.addReferrer(this));
078            this.nodes.addAll(nodes);
079        }
080        this.cachedBBox = null;
081    }
082
083    @Override
084    public List<Long> getNodeIds() {
085        return this.getNodes().stream().map(VectorNode::getId).collect(Collectors.toList());
086    }
087
088    @Override
089    public long getNodeId(int idx) {
090        return this.getNodes().get(idx).getId();
091    }
092
093    @Override
094    public boolean isClosed() {
095        return this.firstNode() != null && this.firstNode().equals(this.lastNode());
096    }
097
098    @Override
099    public VectorNode firstNode() {
100        if (this.nodes.isEmpty()) {
101            return null;
102        }
103        return this.getNode(0);
104    }
105
106    @Override
107    public VectorNode lastNode() {
108        if (this.nodes.isEmpty()) {
109            return null;
110        }
111        return this.getNode(this.getNodesCount() - 1);
112    }
113
114    @Override
115    public boolean isFirstLastNode(INode n) {
116        if (this.nodes.isEmpty()) {
117            return false;
118        }
119        return this.firstNode().equals(n) || this.lastNode().equals(n);
120    }
121
122    @Override
123    public boolean isInnerNode(INode n) {
124        if (this.nodes.isEmpty()) {
125            return false;
126        }
127        return !this.firstNode().equals(n) && !this.lastNode().equals(n) && this.nodes.stream()
128          .anyMatch(vectorNode -> vectorNode.equals(n));
129    }
130
131    @Override
132    public OsmPrimitiveType getType() {
133        return this.isClosed() ? OsmPrimitiveType.CLOSEDWAY : OsmPrimitiveType.WAY;
134    }
135}