package net.mvla.mvhs.map; import com.google.android.gms.maps.model.LatLng; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * Map Data */ public class MapData { private final static Set<LocationNode> tempAddedNodes = new HashSet<>(); public static Map<LatLng, LocationNode> locationNodeMap = new HashMap<>(); public static Map<LatLng, Node> pathNodeMap = new HashMap<>(); public static void init(String string) throws JSONException { JSONObject object = new JSONObject(string); JSONArray locations = object.getJSONArray("locationNodes"); locationNodeMap = new HashMap<>(); for (int i = 0; i < locations.length(); i++) { JSONArray location = locations.getJSONArray(i); String name = location.getString(0); JSONArray coord = location.getJSONArray(1); JSONArray pathCoord = location.getJSONArray(2); List<String> tags = new ArrayList<>(); if (location.length() == 4) { JSONArray tagsArray = location.getJSONArray(3); for (int j = 0; j < tagsArray.length(); j++) { tags.add(tagsArray.getString(j)); } } LocationNode node = new LocationNode( coord.getDouble(0), coord.getDouble(1), pathCoord.getDouble(0), pathCoord.getDouble(1), name, tags); locationNodeMap.put(node.latLng, node); } JSONArray paths = object.getJSONArray("pathNodes"); pathNodeMap = new HashMap<>(); for (int j = 0; j < paths.length(); j++) { //Iterate through each path List<Node> added = new ArrayList<>(); JSONArray path = paths.getJSONArray(j); for (int k = 0; k < path.length(); k++) { JSONArray pathNode = path.getJSONArray(k); Node node = new Node(pathNode.getDouble(0), pathNode.getDouble(1)); added.add(node); } //If node already exists on node map, discard current one for (int i = 0; i < added.size(); i++) { Node nodeAdd = added.get(i); if (pathNodeMap.containsValue(nodeAdd)) { added.set(i, pathNodeMap.get(nodeAdd.latLng)); } } //TODO: remove duplicate nodes (after using the ones from the path already added) //Connect all added nodes in the path up, add it to node map for (int i = 0; i < added.size(); i++) { if (i != added.size() - 1) { int r = i + 1; added.get(i).addConnected(added.get(r)); added.get(r).addConnected(added.get(i)); } pathNodeMap.put(added.get(i).latLng, added.get(i)); } } } /** * Finds path. * * @param start Node from path node map * @param end Node with co-ords of end (a location node) * @return path */ public static List<Node> findPath(LatLng start, LatLng end) { cleanTempNodes(); LocationNode endNode = MapData.addTempLocationNodeToPaths(end); Node startNode = pathNodeMap.get(start); if (locationNodeMap.containsKey(start)) { startNode = MapData.addTempLocationNodeToPaths(start); } SortedNodeList openList = new SortedNodeList(); List<Node> closedList = new ArrayList<>(); openList.setTarget(endNode); openList.add(startNode); while (true) { if (openList.size() == 0) { return null; } Node lowestF = openList.first(); openList.remove(lowestF); closedList.add(lowestF); if (lowestF.equals(endNode)) { break; } for (Node n : lowestF.getConnected()) { if (!closedList.contains(n)) { if (!openList.contains(n)) { n.setParent(lowestF); openList.add(n); } else { double gFromLowestFToN = lowestF.getG() + Node.distance(lowestF, n); if (gFromLowestFToN < n.getG()) { n.setParent(lowestF); } openList.sort(); } } } } List<Node> path = new ArrayList<>(); Node child = endNode; path.add(endNode); while (!child.equals(startNode)) { Node parent = child.getParent(); path.add(parent); child = parent; } Collections.reverse(path); return path; } public static LocationNode addTempLocationNodeToPaths(LatLng latLng) { Map<LatLng, Node> added = new HashMap<>(); LocationNode locationNode = locationNodeMap.get(latLng); if (locationNode == null) { return null; } else if (pathNodeMap.containsKey(locationNode.latLng)) { return locationNode; } added.put(locationNode.latLng, locationNode); added.put(locationNode.getPathNode().latLng, locationNode.getPathNode()); //If the added node is on the line of two other nodes on node map, connect it up for (Node pathNode : pathNodeMap.values()) { for (Node nodeAdded : added.values()) { Node pathNodeConnected = pathNode.nodeLiesOnConnectedPath(nodeAdded); if (pathNodeConnected != null && !pathNodeConnected.equals(nodeAdded)) { nodeAdded.addConnected(pathNode); nodeAdded.addConnected(pathNodeConnected); pathNodeConnected.addConnected(nodeAdded); pathNodeConnected.removeConnected(pathNode); pathNode.addConnected(nodeAdded); pathNode.removeConnected(pathNodeConnected); break; } } } pathNodeMap.putAll(added); tempAddedNodes.add(locationNode); return locationNode; } public static void cleanTempNodes() { for (Node node : tempAddedNodes) { removeLocationNode((LocationNode) node); } tempAddedNodes.clear(); } private static void removeLocationNode(LocationNode node) { Node locationPathNode = node.getPathNode(); List<Node> locationPathNodeConnected = new ArrayList<>(locationPathNode.getConnected()); locationPathNodeConnected.remove(node); if (locationPathNodeConnected.size() == 2) { locationPathNodeConnected.get(0).addConnected(locationPathNodeConnected.get(1)); locationPathNodeConnected.get(1).addConnected(locationPathNodeConnected.get(0)); locationPathNodeConnected.get(0).removeConnected(locationPathNode); locationPathNodeConnected.get(1).removeConnected(locationPathNode); locationPathNode.removeConnected(locationPathNodeConnected.get(0)); locationPathNode.removeConnected(locationPathNodeConnected.get(1)); } pathNodeMap.remove(locationPathNode.latLng); pathNodeMap.remove(node.latLng); } public static boolean onCampus(Node node) { return node.latLng.latitude > 37.356813 && node.latLng.latitude < 37.361323 && node.latLng.longitude > -122.068730 && node.latLng.longitude < -122.065080; } public static boolean nodesLiesOnOnePath(List<Node> check) { if (check.size() == 2) { return true; } boolean lies = true; Node start = check.get(0); Node end = check.get(check.size() - 1); for (int i = 1; i < check.size() - 1; i++) { Node checking = check.get(i); if (!((Node.distance(start, checking) + Node.distance(checking, end) - Node.distance(start, end)) < 0.000001)) { lies = false; } } return lies; } }