package edu.kit.pse.ws2013.routekit.precalculation; import java.util.ArrayList; import java.util.EnumSet; import java.util.List; import java.util.Map; import edu.kit.pse.ws2013.routekit.map.EdgeProperties; import edu.kit.pse.ws2013.routekit.map.HeightRestriction; import edu.kit.pse.ws2013.routekit.map.HighwayType; import edu.kit.pse.ws2013.routekit.map.MultipleRestrictions; import edu.kit.pse.ws2013.routekit.map.Restriction; import edu.kit.pse.ws2013.routekit.map.VehicleTypeRestriction; import edu.kit.pse.ws2013.routekit.map.WeightRestriction; import edu.kit.pse.ws2013.routekit.map.WidthRestriction; import edu.kit.pse.ws2013.routekit.profiles.VehicleType; /** * A way in an OpenStreetMap file. This is only a helper class used by the * {@link OSMParser}. */ public class OSMWay { private final Map<String, String> tags; private EdgeProperties props = null; private int id = Integer.MIN_VALUE; /** * Creates a new object from the given OSM tags. * * @param tags * a list of OSM tags (name/value pairs) */ public OSMWay(Map<String, String> tags) { this.tags = tags; } /** * Returns the OSM way identifier of this way. * * @return the ID * @throws IllegalStateException * if the ID has not been set yet */ public int getId() { if (id == Integer.MIN_VALUE) { throw new IllegalStateException(); } return id; } /** * Sets the OSM way identifier of this way. * * @param id * the ID to set */ public void setId(int id) { this.id = id; } /** * Returns a {@link Restriction} object with the restriction(s) applicable * for this way. * * @return the restriction(s) of this way, or {@code null} if none */ public Restriction getRestriction() { List<Restriction> restrictions = new ArrayList<>(); if (tags.containsKey("maxweight")) { try { String[] parts = tags.get("maxweight").split(" "); int multiplier; if (parts.length <= 1 || "t".equals(parts[1])) { multiplier = 1000; } else if ("kg".equals(parts[1])) { multiplier = 1; } else { // will be caught throw new NumberFormatException("Unknown weight unit " + parts[1]); } int maxWeight = (int) (Float.parseFloat(parts[0]) * multiplier); restrictions.add(WeightRestriction.getInstance(maxWeight)); } catch (NumberFormatException e) { // Ignore invalid value for the maxweight tag } } if (tags.containsKey("maxwidth")) { try { String[] parts = tags.get("maxwidth").split(" "); int multiplier; if (parts.length <= 1 || "m".equals(parts[1])) { multiplier = 100; } else { // will be caught throw new NumberFormatException("Unknown weight unit " + parts[1]); } int maxWidth = (int) (Float.parseFloat(parts[0]) * multiplier); restrictions.add(WidthRestriction.getInstance(maxWidth)); } catch (NumberFormatException e) { // Ignore invalid value for the maxwidth tag } } if (tags.containsKey("maxheight")) { try { String[] parts = tags.get("maxheight").split(" "); int multiplier; if (parts.length <= 1 || "m".equals(parts[1])) { multiplier = 100; } else { // will be caught throw new NumberFormatException("Unknown height unit " + parts[1]); } int maxHeight = (int) (Float.parseFloat(parts[0]) * multiplier); restrictions.add(HeightRestriction.getInstance(maxHeight)); } catch (NumberFormatException e) { // Ignore invalid value for the maxheight tag } } EnumSet<VehicleType> restrictedTypes = EnumSet .noneOf(VehicleType.class); for (String type : new String[] { "access", "vehicle", "motor_vehicle" }) { if (tags.containsKey(type)) { if (tags.get(type).equals("yes")) { restrictedTypes.clear(); } else { restrictedTypes = EnumSet.allOf(VehicleType.class); } } } if (tags.containsKey("motorcar")) { if (tags.get("motorcar").equals("yes")) { restrictedTypes.remove(VehicleType.Car); } else { restrictedTypes.add(VehicleType.Car); } } if (tags.containsKey("motorcycle")) { if (tags.get("motorcycle").equals("yes")) { restrictedTypes.remove(VehicleType.Motorcycle); } else { restrictedTypes.add(VehicleType.Motorcycle); } } if (tags.containsKey("hgv")) { if (tags.get("hgv").equals("yes")) { restrictedTypes.remove(VehicleType.Truck); } else { restrictedTypes.add(VehicleType.Truck); } } if (tags.containsKey("bus")) { if (tags.get("bus").equals("yes")) { restrictedTypes.remove(VehicleType.Bus); } else { restrictedTypes.add(VehicleType.Bus); } } for (VehicleType type : restrictedTypes) { restrictions.add(VehicleTypeRestriction.getInstance(type)); } if (restrictions.isEmpty()) { return null; } return MultipleRestrictions.getInstance(restrictions); } /** * Determines whether this way is (part of) a roundabout. * * @return {@code true} if it is a roundabout, otherwise {@code false} */ public boolean isRoundabout() { return tags.containsKey("junction") && tags.get("junction").equals("roundabout"); } /** * Determines whether this way constitutes a one-way street. * * @return {@code true} if this way is a one-way street, or {@code false} if * it can also be used in the opposite direction */ public boolean isOneway() { if (tags.containsKey("oneway")) { switch (tags.get("oneway").toLowerCase()) { case "yes": case "true": case "1": return true; case "no": case "false": case "0": case "-1": return false; } } return isRoundabout() || getHighwayType() == HighwayType.Motorway; } /** * Determines whether this way constitutes a one-way street in reversed way * order. * * @return {@code true} if this way is a one-way street in the opposite * direction, or {@code false} if not */ public boolean isReversedOneway() { return tags.containsKey("oneway") && tags.get("oneway").equals("-1"); } /** * Determines if this way is a (relevant) highway and, if yes, what type it * is. * * @return the highway type of this way, or {@code null} if no highway */ public HighwayType getHighwayType() { if (!tags.containsKey("highway")) { return null; } switch (tags.get("highway")) { case "motorway": case "motorway_link": return HighwayType.Motorway; case "trunk": case "trunk_link": return HighwayType.Trunk; case "primary": case "primary_link": return HighwayType.Primary; case "secondary": case "secondary_link": return HighwayType.Secondary; case "tertiary": case "tertiary_link": return HighwayType.Tertiary; case "unclassified": case "road": return HighwayType.Unclassified; case "residential": return HighwayType.Residential; default: return null; } } /** * Determines whether this way is a highway link. * * @return {@code true} if it is a link, {@code false} otherwise */ public boolean isHighwayLink() { if (!tags.containsKey("highway")) { return false; } return tags.get("highway").endsWith("_link"); } /** * Returns an {@link EdgeProperties} object containing the properties of * this way. * * @return the said properties object */ public EdgeProperties getEdgeProperties() { if (props == null) { props = createEdgeProperties(); } return props; } private EdgeProperties createEdgeProperties() { int maxSpeed; try { maxSpeed = Math.max(Integer.parseInt(tags.get("maxspeed")), 0); } catch (NumberFormatException e) { maxSpeed = 0; } String name = tags.get("name"); if (name != null && name.isEmpty()) { name = null; } String ref = tags.get("ref"); if (ref != null && ref.isEmpty()) { ref = null; } if (maxSpeed == 0 && getHighwayType() == HighwayType.Residential) { maxSpeed = 50; } return new EdgeProperties(getHighwayType(), name, ref, maxSpeed); } }