Package org.osm2world.core.map_data.data

Source Code of org.osm2world.core.map_data.data.MapNode

package org.osm2world.core.map_data.data;

import static org.osm2world.core.math.VectorXZ.X_UNIT;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.openstreetmap.josm.plugins.graphview.core.data.TagGroup;
import org.osm2world.core.map_data.data.overlaps.MapOverlap;
import org.osm2world.core.math.AxisAlignedBoundingBoxXZ;
import org.osm2world.core.math.VectorXZ;
import org.osm2world.core.osm.data.OSMNode;
import org.osm2world.core.world.data.NodeWorldObject;

/**
* grid representation of an OSM node,
* references inbound and outbound {@link MapWaySegment}s.
* For each OSM node, one GridNode will be created.
*/
public class MapNode implements MapElement {

  private final VectorXZ pos;
  private final OSMNode osmNode;
 
  private List<NodeWorldObject> representations = new ArrayList<NodeWorldObject>(1);
 
  private List<MapWaySegment> connectedWaySegments = new ArrayList<MapWaySegment>();
  private List<MapSegment> connectedSegments = new ArrayList<MapSegment>();
 
  private List<MapWaySegment> inboundLines = new ArrayList<MapWaySegment>(); //TODO: maybe use list and sort by angle?
  private List<MapWaySegment> outboundLines = new ArrayList<MapWaySegment>();
   
  private Collection<MapArea> adjacentAreas;
 
  public MapNode(VectorXZ pos, OSMNode osmNode) {
   
    this.pos = pos;
    this.osmNode = osmNode;
    this.adjacentAreas = new ArrayList<MapArea>();
   
  }
 
  public VectorXZ getPos() {
    return pos;
  }
 
  @Override
  public int getLayer() {
    if (osmNode.tags.containsKey("layer")) {
      try {
        return Integer.parseInt(osmNode.tags.getValue("layer"));
      } catch (NumberFormatException nfe) {
        return 0;
      }
    }
    return 0;
  }
 
  public OSMNode getOsmNode() {
    return osmNode;
  }
 
  @Override
  public TagGroup getTags() {
    return getOsmNode().tags;
  }
 
  public Collection<MapArea> getAdjacentAreas() {
    return adjacentAreas;
  }
 
  public void addInboundLine(MapWaySegment inboundLine) {
   
    connectedWaySegments.add(inboundLine);
    connectedSegments.add(inboundLine);
    inboundLines.add(inboundLine);

    sortLinesByAngle(connectedWaySegments);
    sortLinesByAngle(connectedSegments);
    sortLinesByAngle(inboundLines);
   
  }
 
  public void addOutboundLine(MapWaySegment outboundLine) {
   
    connectedWaySegments.add(outboundLine);
    connectedSegments.add(outboundLine);
    outboundLines.add(outboundLine);
   
    sortLinesByAngle(connectedWaySegments);
    sortLinesByAngle(connectedSegments);
    sortLinesByAngle(outboundLines);
   
  }
 
  /**
   * returns those connected lines that end here.
   * Sorting is as for {@link #getConnectedWaySegments()}.
   */
  public List<MapWaySegment> getInboundLines() {
    return inboundLines;
  }
 
  /**
   * returns those connected lines that start here.
   * Sorting is as for {@link #getConnectedWaySegments()}.
   */
  public List<MapWaySegment> getOutboundLines() {
    return outboundLines;
  }
 
  public void addAdjacentArea(MapArea adjacentArea) {
    adjacentAreas.add(adjacentArea);
  }

  //TODO: with all that "needs to be called before x" etc. stuff (also in MapArea), switch to BUILDER?
  /** needs to be called after adding and completing all adjacent areas */
  public void calculateAdjacentAreaSegments() {
 
    for (MapArea adjacentArea : adjacentAreas) {
      for (MapAreaSegment areaSegment : adjacentArea.getAreaSegments()) {
        if (areaSegment.getStartNode() == this
            || areaSegment.getEndNode() == this) {
          connectedSegments.add(areaSegment);
        }
      }
    }

    sortLinesByAngle(connectedSegments);
  }
   
  /**
   * returns all way segments connected with this node.
   * They will be sorted according to the clockwise
   * (seen from above) angle between the vector
   * "this node -> other node of the segment"
   * and the positive x direction.
   */
  public List<MapWaySegment> getConnectedWaySegments() {
    return connectedWaySegments;
  }
 
  /**
   * returns all way segments and area segments connected with this node.
   * Sorted like {@link #getConnectedWaySegments()}.
   */
  public List<MapSegment> getConnectedSegments() {
    return connectedSegments;
  }
 
  /**
   * creates the ordering described for {@link #getConnectedSegments()}
   */
  private void sortLinesByAngle(List<? extends MapSegment> lines) {
    Collections.sort(lines, new Comparator<MapSegment>() {
      @Override
      public int compare(MapSegment l1, MapSegment l2) {
       
        VectorXZ d1 = l1.getDirection();
        VectorXZ d2 = l2.getDirection();
       
        if (inboundLines.contains(l1)) {
          d1 = d1.invert();
        }
        if (inboundLines.contains(l2)) {
          d2 = d2.invert();
        }
       
        //check whether the lines are in the first or second 180°
        //(the dot product formula will not be useful to distinguish
        //these cases - it only provides cos 0° to cos 180° - ,
        //so they need to be handled first)
       
        if (d1.z < 0 && d2.z > 0) {
          return -1;
        } else if (d1.z > 0 && d2.z < 0) {
          return +1;
        }
       
        //check the actual angles using the dot product with (1,0,0).
        //Two simplifications apply:
        //- we don't need to divide by the vector lengths' product,
        //  as getDirection returns vectors whose length is 1
        //- we don't need to actually calculate the angle itself,
        //  because if angle a > angle b, then cos a < cos b
        //  (in [0°, 180°])
       
        double comparison = d1.dot(X_UNIT) - d2.dot(X_UNIT);
       
        if (comparison == 0) {
          return 0;
        }
       
        if (d1.z < 0) { //and d2.z < 0
          return (comparison > 0) ? -1 : +1;
        } else { //d1.z > 0 and d2.z > 0
          return (comparison > 0) ? +1 : -1;
        }
       
      }
    });
  }
 
  @Override
  public List<NodeWorldObject> getRepresentations() {
    return representations;
  }
 
  @Override
  public NodeWorldObject getPrimaryRepresentation() {
    if (representations.isEmpty()) {
      return null;
    } else {
      return representations.get(0);
    }
  }

  /**
   * adds a visual representation for this node
   */
  public void addRepresentation(NodeWorldObject representation) {
    this.representations.add(representation);
  }
   
  @Override
  public String toString() {
    return osmNode.toString();
  }

  @Override
  public Collection<MapOverlap<?,?>> getOverlaps() {
    return Collections.emptyList();
  }

  @Override
  public AxisAlignedBoundingBoxXZ getAxisAlignedBoundingBoxXZ() {
    return new AxisAlignedBoundingBoxXZ(pos.x, pos.z, pos.x, pos.z);
  }
 
}
TOP

Related Classes of org.osm2world.core.map_data.data.MapNode

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.