001    package aima.search.map;
002    
003    import java.util.Collections;
004    import java.util.Hashtable;
005    import java.util.List;
006    
007    import aima.util.LabeledGraph;
008    import aima.util.Util;
009    
010    /**
011     * Implements a map with locations, distance labeled links between the
012     * locations, straight line distances, and 2d-placement positions of locations.
013     * Locations are represented by strings and travel distances by integer values.
014     * Locations and links can be added dynamically and removed after creation. This
015     * enables to read maps from file or to modify them with respect to newly
016     * obtained knowledge.
017     * 
018     * @author R. Lunde
019     */
020    public class ExtendableMap implements Map {
021    
022            /**
023             * Stores map data. Locations are represented as vertices and connections
024             * (links) as directed edges labeled with corresponding travel distances.
025             */
026            private final LabeledGraph<String, Double> links;
027    
028            /** Stores xy-coordinates for each location. */
029            private final Hashtable<String, Point2D> locationPositions;
030    
031            /** Creates an empty map. */
032            public ExtendableMap() {
033                    links = new LabeledGraph<String, Double>();
034                    locationPositions = new Hashtable<String, Point2D>();
035            }
036    
037            /** Removes everything. */
038            public void clear() {
039                    links.clear();
040                    locationPositions.clear();
041            }
042    
043            /** Clears all connections but keeps location position informations. */
044            public void clearLinks() {
045                    links.clear();
046            }
047    
048            /** Returns a list of all locations. */
049            public List<String> getLocations() {
050                    return links.getVertexLabels();
051            }
052    
053            /** Checks whether the given string is the name of a location. */
054            public boolean isLocation(String str) {
055                    return links.isVertexLabel(str);
056            }
057    
058            /**
059             * Answers to the question: Where can I get, following one of the
060             * connections starting at the specified location?
061             */
062            public List<String> getLocationsLinkedTo(String fromLocation) {
063                    List<String> result = links.getSuccessors(fromLocation);
064                    Collections.sort(result);
065                    return result;
066            }
067    
068            /**
069             * Returns the travel distance between the two specified locations if they
070             * are linked by a connection and null otherwise.
071             */
072            public Double getDistance(String fromLocation, String toLocation) {
073                    return links.get(fromLocation, toLocation);
074            }
075    
076            /** Adds a one-way connection to the map. */
077            public void addUnidirectionalLink(String fromLocation, String toLocation,
078                            Double distance) {
079                    links.set(fromLocation, toLocation, distance);
080            }
081    
082            /**
083             * Adds a connection which can be traveled in both direction. Internally,
084             * such a connection is represented as two one-way connections.
085             */
086            public void addBidirectionalLink(String fromLocation, String toLocation,
087                            Double distance) {
088                    links.set(fromLocation, toLocation, distance);
089                    links.set(toLocation, fromLocation, distance);
090            }
091    
092            /**
093             * Returns a location which is selected by random.
094             */
095            public String randomlyGenerateDestination() {
096                    return Util.selectRandomlyFromList(getLocations());
097            }
098    
099            /** Removes a one-way connection. */
100            public void removeUnidirectionalLink(String fromLocation, String toLocation) {
101                    links.remove(fromLocation, toLocation);
102            }
103    
104            /** Removes the two corresponding one-way connections. */
105            public void removeBidirectionalLink(String fromLocation, String toLocation) {
106                    links.remove(fromLocation, toLocation);
107                    links.remove(toLocation, fromLocation);
108            }
109    
110            /**
111             * Defines the position of a location as with respect to an orthogonal
112             * coordinate system.
113             */
114            public void setPosition(String loc, double x, double y) {
115                    locationPositions.put(loc, new Point2D(x, y));
116            }
117    
118            /**
119             * Defines the position of a location within the map. Using this method, one
120             * location should be selected as reference position (<code>dist=0</code>
121             * and <code>dir=0</code>) and all the other location should be placed
122             * relative to it.
123             * 
124             * @param loc
125             *            location name
126             * @param dist
127             *            distance to a reference position
128             * @param dir
129             *            bearing (compass direction) in which the location is seen from
130             *            the reference position
131             */
132            public void setDistAndDirToRefLocation(String loc, double dist, int dir) {
133                    Point2D coords = new Point2D(
134                                    -Math.sin(dir * Math.PI / 180.0) * dist,
135                                    Math.cos(dir * Math.PI / 180.0) * dist);
136                    links.addVertex(loc);
137                    locationPositions.put(loc, coords);
138            }
139    
140            /**
141             * Returns an array with two integers describing the the position of the
142             * specified location.
143             */
144            public Point2D getPosition(String loc) {
145                    return locationPositions.get(loc);
146            }
147    
148    }