-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDynamicGrid.java
365 lines (303 loc) · 13.5 KB
/
DynamicGrid.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
package de.fmi.searouter.hublablecreation;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;
/**
* This class stores the edges associated with every node in the grid. It is possible to add edges and remove nodes
* which are currently relevant. In addition, all edges which were at any point associated with each node is
* kept track of.
*/
public class DynamicGrid {
//ids of edges currently associated with nodes
private static int[][] currentEdgeIds;
//number of currently associated edges per node
private static int[] currentEdgeCount;
//ids of all edges associated with nodes at any point
private static int[][] allEdgeIds;
//number of all associated edges per node
private static int[] allEdgeCount;
/**
* Initialize the dynamic grid data.
* @param edges the current edges to set
* @param edgeCount the number of edges per node
*/
public static void initializeEdges(int[][] edges, int[]edgeCount) {
DynamicGrid.currentEdgeIds = edges;
DynamicGrid.currentEdgeCount = edgeCount;
DynamicGrid.allEdgeIds = Arrays.stream(edges).map(int[]::clone).toArray(int[][]::new);
DynamicGrid.allEdgeCount = Arrays.copyOf(edgeCount, edgeCount.length);
}
/**
* Get current edge id array for a given node. Keep in mind that not all elements are necessarily ids.
* In order to find out how many elements (counted from the front) are valid ids, use getCurrentEdgeCount.
* @param nodeId the id of the node
* @return an array containing current edge ids
*/
public static int[] getCurrentEdges(int nodeId) {
return currentEdgeIds[nodeId];
}
/**
* Get the number of current edge ids for a given node.
* @param nodeId the id of the node
* @return the number of current edge ids
*/
public static int getCurrentEdgeCount(int nodeId) {
return currentEdgeCount[nodeId];
}
/**
* Get all edge id array for a given node. Keep in mind that not all elements are necessarily ids.
* In order to find out how many elements (counted from the front) are valid ids, use getAllEdgeCount.
* @param nodeId the id of the node
* @return an array all current edge ids
*/
public static int[] getAllEdges(int nodeId) {
return allEdgeIds[nodeId];
}
/**
* Get the number of all edge ids for a given node.
* @param nodeId the id of the node
* @return the number of all edge ids
*/
public static int getAllEdgeCount(int nodeId) {
return allEdgeCount[nodeId];
}
/**
* Add an edge id to a node. The id will be tracked both as a current edge and be included in all edges.
* @param nodeId the id of the node to add the edge to
* @param edgeId the edge id to add
*/
public static void addEdge(int nodeId, int edgeId) {
//first, check if the current space is large enough
if(currentEdgeIds[nodeId].length == currentEdgeCount[nodeId]) {
growCurrent(nodeId);
}
if(allEdgeIds[nodeId].length == allEdgeCount[nodeId]) {
growAll(nodeId);
}
currentEdgeIds[nodeId][currentEdgeCount[nodeId]] = edgeId;
currentEdgeCount[nodeId]++;
allEdgeIds[nodeId][allEdgeCount[nodeId]] = edgeId;
allEdgeCount[nodeId]++;
}
/**
* Remove an edge going in the opposite direction as a given edge.
* @param startId start node id of the given edge
* @param destId destination node id of the given edge
*/
public static void removeBackwardsEdge(int startId, int destId) {
int position = -1; // -1 so it crashes if an unexpected error occurs
for (int i = 0; i < currentEdgeCount[destId]; i++) {
if(Edges.getDest(currentEdgeIds[destId][i]) == startId) {
position = i;
break;
}
}
//once we know the indices, delete the element
currentEdgeIds[destId][position] = currentEdgeIds[destId][currentEdgeCount[destId] - 1];
currentEdgeCount[destId]--;
}
/**
* Remove all edges from or to a given node from the current edges in the graph.
* @param nodeId the node to remove
*/
public static void removeNode(int nodeId) {
for (int i = 0; i < currentEdgeCount[nodeId]; i++) {
removeBackwardsEdge(nodeId, Edges.getDest(currentEdgeIds[nodeId][i]));
}
currentEdgeCount[nodeId] = 0;
currentEdgeIds[nodeId] = null;
}
/**
* Increase the size of an array for current edges for a given node.
* @param nodeId the id of the node
*/
private static void growCurrent(int nodeId) {
currentEdgeIds[nodeId] = Arrays.copyOf(currentEdgeIds[nodeId], currentEdgeIds[nodeId].length * 2);
}
/**
* Increase the size of an array for all edges for a given node.
* @param nodeId the id of the node
*/
private static void growAll(int nodeId) {
allEdgeIds[nodeId] = Arrays.copyOf(allEdgeIds[nodeId], allEdgeIds[nodeId].length * 2);
}
/**
* Only temporary used during the import of fmi files needed (for sorting).
*/
private static class TmpEdge {
public int startNode;
public int destNode;
public int dist;
public TmpEdge(int startNode, int destNode, int dist) {
this.startNode = startNode;
this.destNode = destNode;
this.dist = dist;
}
}
/**
* Only temporary during the import of fmi files needed (for sorting).
*/
private static class TmpNode {
public int id;
public double latitude;
public double longitude;
public TmpNode(int id, double latitude, double longitude) {
this.id = id;
this.latitude = latitude;
this.longitude = longitude;
}
}
/**
* Imports a grid graph of a .fmi file format.
*
* @param filePath The path within the resources folder where the file to import is placed.
* @throws IOException If I/O fails.
*/
public static void importFmiFile(String filePath) throws IOException {
Resource fmiResource = new ClassPathResource(filePath);
InputStream inputStream = fmiResource.getInputStream();
try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream))) {
br.readLine();
br.readLine();
// Map file id to internal used node id
Map<Integer, Integer> fileNodeIdToInternalUsedId = new HashMap<>();
int noNodes = Integer.parseInt(br.readLine().trim());
int noEdges = Integer.parseInt(br.readLine().trim());
List<DynamicGrid.TmpNode> nodeList = new ArrayList<>();
// Node handling
for (int nodeIdx = 0; nodeIdx < noNodes; nodeIdx++) {
String line = br.readLine();
line = line.trim();
String[] split = line.split(" ");
DynamicGrid.TmpNode node = new DynamicGrid.TmpNode(Integer.parseInt(split[0]),
Double.parseDouble(split[1]), Double.parseDouble(split[2]));
nodeList.add(node);
fileNodeIdToInternalUsedId.put(node.id, nodeIdx);
}
// Sort node list by id
nodeList.sort(new Comparator<DynamicGrid.TmpNode>() {
@Override
public int compare(DynamicGrid.TmpNode o1, DynamicGrid.TmpNode o2) {
return Integer.compare(fileNodeIdToInternalUsedId.get(o1.id), fileNodeIdToInternalUsedId.get(o2.id));
}
});
// Edge handling
List<DynamicGrid.TmpEdge> edgeList = new ArrayList<>();
for (int edgeIdx = 0; edgeIdx < noEdges; edgeIdx++) {
String line = br.readLine();
line = line.trim();
String[] split = line.split(" ");
DynamicGrid.TmpEdge edge = new DynamicGrid.TmpEdge(
fileNodeIdToInternalUsedId.get(Integer.parseInt(split[0])),
fileNodeIdToInternalUsedId.get(Integer.parseInt(split[1])),
Integer.parseInt(split[2])
);
edgeList.add(edge);
}
// Sort edges by start ids
edgeList.sort(new Comparator<DynamicGrid.TmpEdge>() {
@Override
public int compare(DynamicGrid.TmpEdge o1, DynamicGrid.TmpEdge o2) {
return Integer.compare(o1.startNode, o2.startNode);
}
});
// Build an adjacency map for the following operations
Map<Integer, List<DynamicGrid.TmpEdge>> adjacenceMap = new HashMap<>();
for (int edgeIdx = 0; edgeIdx < noEdges; edgeIdx++) {
int currStartNodeID = edgeList.get(edgeIdx).startNode;
if (!adjacenceMap.containsKey(currStartNodeID)) {
adjacenceMap.put(currStartNodeID, new ArrayList<>());
}
adjacenceMap.get(currStartNodeID).add(edgeList.get(edgeIdx));
}
// Assure that the graph is undirected by adding missing unidirectional edges
List<DynamicGrid.TmpEdge> additionalEdgesThatWereMissing = new ArrayList<>();
for (Map.Entry<Integer, List<DynamicGrid.TmpEdge>> e : adjacenceMap.entrySet()) {
List<DynamicGrid.TmpEdge> reverseEdgeStartNodesToCheck = e.getValue();
boolean oppositeEdgeFound = false;
for (DynamicGrid.TmpEdge revEdge : reverseEdgeStartNodesToCheck) {
List<DynamicGrid.TmpEdge> toCheck = adjacenceMap.get(revEdge.destNode);
for (DynamicGrid.TmpEdge edges : toCheck) {
if (edges.startNode == revEdge.destNode && edges.destNode == revEdge.startNode) {
oppositeEdgeFound = true;
break;
}
}
if (!oppositeEdgeFound) {
noEdges++;
additionalEdgesThatWereMissing.add(new DynamicGrid.TmpEdge(revEdge.destNode, revEdge.startNode, revEdge.dist));
System.out.println("Added edge " + revEdge.destNode + " | " + revEdge.startNode);
}
}
}
edgeList.addAll(additionalEdgesThatWereMissing);
// Sort edges by start ids
edgeList.sort(new Comparator<DynamicGrid.TmpEdge>() {
@Override
public int compare(DynamicGrid.TmpEdge o1, DynamicGrid.TmpEdge o2) {
return Integer.compare(o1.startNode, o2.startNode);
}
});
// Fill arrays
double[] latitude = new double[noNodes];
double[] longitude = new double[noNodes];
for (int i = 0; i < latitude.length; i++) {
latitude[i] = nodeList.get(i).latitude;
longitude[i] = nodeList.get(i).longitude;
}
Nodes.setLatitude(latitude);
Nodes.setLongitude(longitude);
Nodes.initializeLvls(noNodes);
int[] startNode = new int[noEdges];
int[] destNode = new int[noEdges];
int[] dist = new int[noEdges];
for (int i = 0; i < startNode.length; i++) {
startNode[i] = edgeList.get(i).startNode;
destNode[i] = edgeList.get(i).destNode;
dist[i] = edgeList.get(i).dist;
}
Edges.setOriginalEdgeStart(startNode);
Edges.setOriginalEdgeDest(destNode);
Edges.setOriginalEdgeDist(dist);
Edges.initializeForShortcutEdges(noEdges);
// sort edge ids based on start node ids
int[][] sortedEdges = new int[noNodes][4]; //at most 4 edges are connected
int[] edgeCounts = new int[noNodes];
Arrays.fill(edgeCounts, 0);
for (int i = 0; i < noEdges; i++) {
sortedEdges[startNode[i]][edgeCounts[startNode[i]]] = i;
edgeCounts[startNode[i]]++;
}
initializeEdges(sortedEdges, edgeCounts);
}
}
//getters and setters for serialization and deserialization
public static int[][] getCurrentEdgeIds() {
return currentEdgeIds;
}
public static int[] getCurrentEdgeCount() {
return currentEdgeCount;
}
public static int[][] getAllEdgeIds() {
return allEdgeIds;
}
public static int[] getAllEdgeCount() {
return allEdgeCount;
}
public static void setCurrentEdgeIds(int[][] currentEdgeIds) {
DynamicGrid.currentEdgeIds = currentEdgeIds;
}
public static void setCurrentEdgeCount(int[] currentEdgeCount) {
DynamicGrid.currentEdgeCount = currentEdgeCount;
}
public static void setAllEdgeIds(int[][] allEdgeIds) {
DynamicGrid.allEdgeIds = allEdgeIds;
}
public static void setAllEdgeCount(int[] allEdgeCount) {
DynamicGrid.allEdgeCount = allEdgeCount;
}
}