content: package aimax.osm.data.impl;
  
  import java.util.Collections;
  import java.util.List;
  
  import aimax.osm.data.BoundingBox;
  import aimax.osm.data.EntityVisitor;
  import aimax.osm.data.entities.MapNode;
  import aimax.osm.data.entities.MapWay;
  
  /**
   * Default implementation of a node.
   * @author Ruediger Lunde
   */
  public class DefaultMapWay extends DefaultMapEntity implements MapWay {
  	private List<MapNode> nodes;
  	// Implicit (storage efficient) representation of a bounding box 
  	private short latMinIdx;
  	private short lonMinIdx;
  	private short latMaxIdx;
  	private short lonMaxIdx;
  	
  	/** Creates a way with a specified ID. */
  	public DefaultMapWay(long id) {
  		this.id = id;
  		nodes = Collections.emptyList();
  		latMinIdx = -1;
  	}
  	
  	/** Assigns a way description (as list of nodes) to the way. */
  	public void setNodes(List<MapNode> nodes) {
  		this.nodes = nodes;
  		latMinIdx = -1;
  	}
  	
  	/** {@inheritDoc} */
  	@Override
  	public boolean isOneway() {
  		return "yes".equals(getAttributeValue("oneway"));
  	}
  	
  	/** {@inheritDoc} */
  	@Override
  	public boolean isArea() {
  		return "yes".equals(getAttributeValue("area"));
  	}
  	
  	/** {@inheritDoc} */
  	@Override
  	public List<MapNode> getNodes() {
  		return Collections.unmodifiableList(nodes);
  	}
  
  	/** {@inheritDoc} */
  	@Override
  	public void accept(EntityVisitor visitor) {
  		visitor.visitMapWay(this);
  	}
  	
  	/** {@inheritDoc} */
  	@Override
  	public BoundingBox computeBoundingBox() {
  		updateBoundingBox();
  		return new BoundingBox(nodes.get(latMinIdx).getLat(),
  				nodes.get(lonMinIdx).getLon(), nodes.get(latMaxIdx).getLat(),
  				nodes.get(lonMaxIdx).getLon());
  	}
  	
  	/** {@inheritDoc} */
  	@Override
  	public float getBoundingBoxSize() {
  		updateBoundingBox();
  		float latDiff = nodes.get(latMaxIdx).getLat()
  		- nodes.get(latMinIdx).getLat();
  		float lonDiff = nodes.get(lonMaxIdx).getLon()
  		- nodes.get(lonMinIdx).getLon();
  		return latDiff + lonDiff;
  //		latDiff * latDiff
  //		+ (float) Math.cos(nodes.get(latMinIdx).getLat() / 180.0 * Math.PI) *
  //		lonDiff * lonDiff;
  	}
  	
  	private void updateBoundingBox() {
  		if (latMinIdx == -1) {
  			latMinIdx = 0;
  			lonMinIdx = 0;
  			latMaxIdx = 0;
  			lonMaxIdx = 0;
  			for (short i = 1; i < nodes.size(); i++) {
  				float lat = nodes.get(i).getLat();
  				float lon = nodes.get(i).getLon();
  				if (lat < nodes.get(latMinIdx).getLat())
  					latMinIdx = i;
  				else if (lat > nodes.get(latMaxIdx).getLat())
  					latMaxIdx = i;
  				if (lon < nodes.get(lonMinIdx).getLon())
  					lonMinIdx = i;
  				else if (lon > nodes.get(lonMaxIdx).getLon())
  					lonMaxIdx = i;
  			}
  		}
  	}
  	
  	@Override
  	public String toString() {
  		StringBuilder result = new StringBuilder("Way(" + id + ", [ ");
  		for (MapNode node : nodes)
  			result.append(node.getId()).append(" ");
  		result.append("])");
  		return result.toString();
  	}
  	
  	/////////////////////////////////////////////////////////////////
  	// extensions for KDTree
  
  	/** {@inheritDoc} */
  	@Override
  	public int compareLatitude(float lat) {
  		updateBoundingBox();
  		int result = ((DefaultMapNode) nodes.get(latMinIdx))
  				.compareLatitude(lat);
  		if (result != ((DefaultMapNode) nodes.get(latMaxIdx))
  				.compareLatitude(lat))
  			result = 0;
  		return result;
  	}
  	
  	/** {@inheritDoc} */
  	@Override
  	public int compareLongitude(float lon) {
  		updateBoundingBox();
  		int result = ((DefaultMapNode) nodes.get(lonMinIdx))
  				.compareLongitude(lon);
  		if (result != ((DefaultMapNode) nodes.get(lonMaxIdx))
  				.compareLongitude(lon))
  			result = 0;
  		return result;
  	}
  }
  