-
Notifications
You must be signed in to change notification settings - Fork 5
/
IdCoordinateSource.java
180 lines (162 loc) · 6.63 KB
/
IdCoordinateSource.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
/*
* © 2021. TU Dortmund University,
* Institute of Energy Systems, Energy Efficiency and Energy Economics,
* Research group Distribution grid planning and operation
*/
package edu.ie3.datamodel.io.source;
import edu.ie3.datamodel.exceptions.SourceException;
import edu.ie3.util.geo.CoordinateDistance;
import edu.ie3.util.geo.GeoUtils;
import java.util.*;
import javax.measure.quantity.Length;
import org.locationtech.jts.geom.Point;
import tech.units.indriya.ComparableQuantity;
/**
* This class serves mapping purposes between the ID of a coordinate and the actual coordinate with
* latitude and longitude values, which is especially needed for data source that don't offer
* combined primary or foreign keys.
*/
public interface IdCoordinateSource {
/**
* Method to retrieve the fields found in the source.
*
* @return an option for the found fields
*/
Optional<Set<String>> getSourceFields() throws SourceException;
/** Returns a set of fields that needs to be unique for the source. */
default Set<String> getUniqueFields() {
return Set.of("");
}
/**
* Get the matching coordinate for the given ID
*
* @param id the ID to look up
* @return matching coordinate
*/
Optional<Point> getCoordinate(int id);
/**
* Get the matching coordinates for the given IDs
*
* @param ids the IDs to look up
* @return the matching coordinates
*/
Collection<Point> getCoordinates(int... ids);
/**
* Get the ID for the coordinate point
*
* @param coordinate the coordinate to look up
* @return the matching ID
*/
Optional<Integer> getId(Point coordinate);
/**
* Returns all the coordinates of this source
*
* @return all available coordinates
*/
Collection<Point> getAllCoordinates();
/**
* Returns the nearest n coordinate points. If n is greater than four, this method will try to
* return the corner points of the bounding box.
*
* @param coordinate the coordinate to look up
* @param n number of searched points
* @return the nearest n coordinates or all coordinates if n is less than all available points
*/
List<CoordinateDistance> getNearestCoordinates(Point coordinate, int n);
/**
* Returns the closest n coordinate points to the given coordinate, that are inside a given
* bounding box, from a collection of all available points. The bounding box is calculated with
* the given distance. If n is greater than four, this method will try to return the corner points
* of the bounding box.
*
* @param coordinate the coordinate to look up the nearest neighbours for
* @param n how many neighbours to look up
* @param distance to the borders of the envelope that contains the coordinates
* @return the nearest n coordinates to the given point
*/
List<CoordinateDistance> getClosestCoordinates(
Point coordinate, int n, ComparableQuantity<Length> distance);
/**
* Calculates and returns the nearest n coordinate distances to the given coordinate from a given
* collection of points. If the set is empty or null an empty list is returned. If n is greater
* than four, this method will try to return the corner points of the bounding box.
*
* @param coordinate the coordinate to look up the nearest neighbours for
* @param n how many neighbours to look up
* @param coordinates the collection of points
* @return a list of the nearest n coordinates to the given point or an empty list
*/
default List<CoordinateDistance> calculateCoordinateDistances(
Point coordinate, int n, Collection<Point> coordinates) {
if (coordinates != null && !coordinates.isEmpty()) {
SortedSet<CoordinateDistance> sortedDistances =
GeoUtils.calcOrderedCoordinateDistances(coordinate, coordinates);
return restrictToBoundingBox(coordinate, sortedDistances, n);
} else {
return Collections.emptyList();
}
}
/**
* Method for evaluating the found points. This method tries to return the four corner points of
* the bounding box of the given coordinate. If one of the found points matches the given
* coordinate, only this point is returned. If the given number of searched points is less than
* four, this method will only return the nearest n corner points. If the given number of searched
* points is greater than four, this method will return the four corner points plus the nearest n
* points to match the number of searched points.
*
* <p>To work properly, the given collection of {@link CoordinateDistance}'s should already be
* sorted by distance.
*
* @param coordinate at the center of the bounding box
* @param distances list of found points with their distances
* @param numberOfPoints that should be returned
* @return list of distances
*/
default List<CoordinateDistance> restrictToBoundingBox(
Point coordinate, Collection<CoordinateDistance> distances, int numberOfPoints) {
boolean topLeft = false;
boolean topRight = false;
boolean bottomLeft = false;
boolean bottomRight = false;
List<CoordinateDistance> resultingDistances = new ArrayList<>();
List<CoordinateDistance> other = new ArrayList<>();
// search for smallest bounding box
for (CoordinateDistance distance : distances) {
Point point = distance.getCoordinateB();
// check for bounding box
if (coordinate.equalsExact(point, 1e-6)) {
// if current point is matching the given coordinate, we need to return only the current
// point
resultingDistances.clear();
resultingDistances.add(distance);
return resultingDistances;
} else if (!topLeft
&& (point.getX() < coordinate.getX() && point.getY() > coordinate.getY())) {
resultingDistances.add(distance);
topLeft = true;
} else if (!topRight
&& (point.getX() > coordinate.getX() && point.getY() > coordinate.getY())) {
resultingDistances.add(distance);
topRight = true;
} else if (!bottomLeft
&& (point.getX() < coordinate.getX() && point.getY() < coordinate.getY())) {
resultingDistances.add(distance);
bottomLeft = true;
} else if (!bottomRight
&& (point.getX() > coordinate.getX() && point.getY() < coordinate.getY())) {
resultingDistances.add(distance);
bottomRight = true;
} else {
other.add(distance);
}
}
// check if n distances are found
int diff = numberOfPoints - resultingDistances.size();
if (diff > 0) {
resultingDistances.addAll(other.stream().limit(diff).toList());
} else if (diff < 0) {
return resultingDistances.stream().limit(numberOfPoints).toList();
}
return resultingDistances;
}
}