mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-09-16 02:38:44 +00:00
bug5451: added getLocation() on Event and use it during export; link regatta to event
This commit is contained in:
@@ -9,26 +9,29 @@ import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
import com.sap.sailing.domain.base.Event;
|
||||
import com.sap.sailing.domain.base.Venue;
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.common.Placemark;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.WindSource;
|
||||
import com.sap.sailing.domain.common.WindSourceType;
|
||||
import com.sap.sailing.domain.common.scalablevalue.impl.ScalablePosition;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
|
||||
import com.sap.sailing.domain.tracking.MarkPositionAtTimePointCache;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.TrackingConnectorInfo;
|
||||
import com.sap.sailing.domain.tracking.impl.MarkPositionAtTimePointCacheImpl;
|
||||
import com.sap.sailing.geocoding.ReverseGeocoder;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
public class EventImpl extends EventBaseImpl implements Event {
|
||||
private static final long serialVersionUID = 855135446595485715L;
|
||||
private static final Logger logger = Logger.getLogger(EventImpl.class.getName());
|
||||
|
||||
private ConcurrentLinkedQueue<LeaderboardGroup> leaderboardGroups;
|
||||
|
||||
@@ -132,13 +135,15 @@ public class EventImpl extends EventBaseImpl implements Event {
|
||||
final Set<Position> positionSamples = new HashSet<>();
|
||||
for (final LeaderboardGroup leaderboardGroup : getLeaderboardGroups()) {
|
||||
for (final Leaderboard leaderboard : leaderboardGroup.getLeaderboards()) {
|
||||
for (final TrackedRace trackedRace : leaderboard.getTrackedRaces()) {
|
||||
if (trackedRace.getStartOfRace() != null) {
|
||||
final Position centerOfCourse = trackedRace.getCenterOfCourse(trackedRace.getStartOfRace());
|
||||
if (centerOfCourse != null) {
|
||||
positionSamples.add(centerOfCourse);
|
||||
if (positionSamples.size() >= MAX_NUMBER_OF_POSITION_SAMPLES) {
|
||||
return getAveragePosition(positionSamples);
|
||||
if (Util.containsAny(leaderboard.getCourseAreas(), getVenue().getCourseAreas())) {
|
||||
for (final TrackedRace trackedRace : leaderboard.getTrackedRaces()) {
|
||||
if (trackedRace.getStartOfRace() != null) {
|
||||
final Position centerOfCourse = trackedRace.getCenterOfCourse(trackedRace.getStartOfRace());
|
||||
if (centerOfCourse != null) {
|
||||
positionSamples.add(centerOfCourse);
|
||||
if (positionSamples.size() >= MAX_NUMBER_OF_POSITION_SAMPLES) {
|
||||
return getAveragePosition(positionSamples);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -147,8 +152,15 @@ public class EventImpl extends EventBaseImpl implements Event {
|
||||
}
|
||||
// try to geo-code the venue:
|
||||
final ReverseGeocoder geocoder = ReverseGeocoder.INSTANCE;
|
||||
// TODO enhance ReverseGeocoder by the use of the forward search, such as http://api.geonames.org/search?name=Kiel&username=sailtracking3, then use Placemark.ByPopulation to sort
|
||||
return null;
|
||||
try {
|
||||
final Placemark placemark = geocoder.getPlacemark(getVenue().getName(), new Placemark.ByPopulation().reversed());
|
||||
if (placemark != null) {
|
||||
positionSamples.add(placemark.getPosition());
|
||||
}
|
||||
} catch (IOException | ParseException e) {
|
||||
logger.log(Level.WARNING, "Problem while trying to resolve venue name "+getVenue().getName()+" with geocoder", e);
|
||||
}
|
||||
return positionSamples.isEmpty() ? null : getAveragePosition(positionSamples);
|
||||
}
|
||||
|
||||
private Position getAveragePosition(Set<Position> positionSamples) {
|
||||
|
||||
+11
-2
@@ -1,11 +1,14 @@
|
||||
package com.sap.sailing.geocoding.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.json.simple.parser.ParseException;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -16,8 +19,6 @@ import com.sap.sailing.domain.common.impl.PlacemarkImpl;
|
||||
import com.sap.sailing.geocoding.ReverseGeocoder;
|
||||
import com.sap.sailing.geocoding.impl.ReverseGeocoderImpl;
|
||||
|
||||
import org.junit.Assert;
|
||||
|
||||
public class ReverseGeocoderTest {
|
||||
private ReverseGeocoder geocoder;
|
||||
private static final Placemark KIEL = new PlacemarkImpl("Kiel", "DE", new DegreePosition(54.32132926107913, 10.1348876953125), 232758);
|
||||
@@ -28,6 +29,14 @@ public class ReverseGeocoderTest {
|
||||
geocoder = new ReverseGeocoderImpl(); // ensure we don't see any caching effects across test case executions
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getByNameTest() throws IOException, ParseException {
|
||||
final Placemark kiel = geocoder.getPlacemark("Kiel", new Placemark.ByPopulation().reversed());
|
||||
assertNotNull(kiel);
|
||||
assertTrue(kiel.getPopulation() > 200000);
|
||||
assertEquals("DE", kiel.getCountryCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPlacemarkSimpleTest() {
|
||||
//Simple Test in Kiel center to check the connection and the parsing from JSONObject to Placemark
|
||||
|
||||
@@ -9,5 +9,6 @@ Bundle-ActivationPolicy: lazy
|
||||
Export-Package: com.sap.sailing.geocoding
|
||||
Require-Bundle: org.json.simple;bundle-version="1.1.0",
|
||||
com.sap.sailing.domain.common,
|
||||
com.sap.sse.common
|
||||
com.sap.sse.common,
|
||||
com.sap.sse
|
||||
Automatic-Module-Name: com.sap.sailing.geocoding
|
||||
|
||||
@@ -66,4 +66,10 @@ public interface ReverseGeocoder {
|
||||
*/
|
||||
Placemark getPlacemarkFirst(Position position, double radius, Comparator<Placemark> comp) throws IOException,
|
||||
ParseException;
|
||||
|
||||
/**
|
||||
* Actually not a "reverse" geocoding but a name search that emits placemarks sorted with the comparator
|
||||
* and returning the first element in the sorting order or {@code null} if the result is empty.
|
||||
*/
|
||||
Placemark getPlacemark(String name, Comparator<Placemark> comp) throws IOException, ParseException;
|
||||
}
|
||||
|
||||
+34
-27
@@ -6,6 +6,7 @@ import java.io.InputStreamReader;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -25,11 +26,15 @@ import com.sap.sailing.domain.common.impl.DegreePosition;
|
||||
import com.sap.sailing.domain.common.impl.PlacemarkImpl;
|
||||
import com.sap.sailing.domain.common.quadtree.QuadTree;
|
||||
import com.sap.sailing.geocoding.ReverseGeocoder;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.util.HttpUrlConnectionHelper;
|
||||
|
||||
public class ReverseGeocoderImpl implements ReverseGeocoder {
|
||||
|
||||
private final String NEARBY_PLACE_SERVICE = "http://api.geonames.org/findNearbyPlaceNameJSON?";
|
||||
private static final String BASE_URL = "http://api.geonames.org";
|
||||
private static final String NEARBY_PLACE_SERVICE = BASE_URL+"/findNearbyPlaceNameJSON?";
|
||||
private static final String SEARCH_BY_NAME_SERVICE = BASE_URL+"/searchJSON?";
|
||||
/**
|
||||
* Maximal distance in degree for the cache.<br />
|
||||
* The first number is the distance in kilometers and the second number is a needed calculation factor and mustn't
|
||||
@@ -48,14 +53,12 @@ public class ReverseGeocoderImpl implements ReverseGeocoder {
|
||||
public Placemark getPlacemarkNearest(Position position) throws IOException, ParseException {
|
||||
Placemark p = null;
|
||||
Util.Triple<Position, Double, List<Placemark>> cachedPlacemarks = checkCache(position);
|
||||
|
||||
if (cachedPlacemarks != null && cachedPlacemarks.getC() != null && !cachedPlacemarks.getC().isEmpty()) {
|
||||
p = cachedPlacemarks.getC().get(0);
|
||||
} else {
|
||||
JSONArray geonames = callNearestService(position);
|
||||
if (geonames != null && !geonames.isEmpty()) {
|
||||
p = JSONToPlacemark((JSONObject) geonames.get(0));
|
||||
|
||||
p = jsonToPlacemark((JSONObject) geonames.get(0));
|
||||
if (p != null) {
|
||||
List<Placemark> placemarks = new ArrayList<Placemark>();
|
||||
placemarks.add(p);
|
||||
@@ -63,7 +66,6 @@ public class ReverseGeocoderImpl implements ReverseGeocoder {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -71,14 +73,12 @@ public class ReverseGeocoderImpl implements ReverseGeocoder {
|
||||
public List<Placemark> getPlacemarksNear(Position position, double radius) throws IOException, ParseException {
|
||||
List<Placemark> placemarks = null;
|
||||
Util.Triple<Position, Double, List<Placemark>> cachedPlacemarks = checkCache(position);
|
||||
|
||||
//Calculating the search radius and the maximum number of returning Placemarks
|
||||
// Calculating the search radius and the maximum number of returning Placemarks
|
||||
double limitedRadius = Math.min(radius, MAX_RADIUS);
|
||||
int radiusInt = (int) limitedRadius;
|
||||
int xKmRadius = radiusInt / XKM_RADIUS;
|
||||
int maxRows = (int) (ROWS_PER_XKM_RADIUS * Math.pow(2, xKmRadius));
|
||||
maxRows = Math.min(maxRows, MAX_ROW_NUMBER);
|
||||
|
||||
if (cachedPlacemarks != null && cachedPlacemarks.getB() >= limitedRadius) {
|
||||
if (cachedPlacemarks.getC().size() > maxRows) {
|
||||
placemarks = cachedPlacemarks.getC().subList(0, maxRows);
|
||||
@@ -86,7 +86,7 @@ public class ReverseGeocoderImpl implements ReverseGeocoder {
|
||||
placemarks = new ArrayList<Placemark>(cachedPlacemarks.getC());
|
||||
}
|
||||
} else {
|
||||
//Recalculating the radius and resetting the search position to keep the cache correct
|
||||
// Recalculating the radius and resetting the search position to keep the cache correct
|
||||
Position searchPosition = null;
|
||||
if (cachedPlacemarks != null) {
|
||||
searchPosition = cachedPlacemarks.getA();
|
||||
@@ -95,16 +95,15 @@ public class ReverseGeocoderImpl implements ReverseGeocoder {
|
||||
} else {
|
||||
searchPosition = position;
|
||||
}
|
||||
|
||||
JSONArray geonames = callNearbyService(searchPosition, limitedRadius, maxRows);
|
||||
if (geonames != null) {
|
||||
Iterator<Object> iterator = geonames.iterator();
|
||||
placemarks = iterator.hasNext() ? new ArrayList<Placemark>() : null;
|
||||
while (iterator.hasNext()) {
|
||||
JSONObject object = (JSONObject) iterator.next();
|
||||
Placemark place = JSONToPlacemark(object);
|
||||
Placemark place = jsonToPlacemark(object);
|
||||
if (place != null) {
|
||||
placemarks.add(JSONToPlacemark(object));
|
||||
placemarks.add(jsonToPlacemark(object));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,7 +114,6 @@ public class ReverseGeocoderImpl implements ReverseGeocoder {
|
||||
updateCachedPlacemarks(searchPosition, limitedRadius, placemarks);
|
||||
}
|
||||
}
|
||||
|
||||
return placemarks;
|
||||
}
|
||||
|
||||
@@ -132,6 +130,18 @@ public class ReverseGeocoderImpl implements ReverseGeocoder {
|
||||
List<Placemark> placemarks = getPlacemarksNearSorted(position, radius, comp);
|
||||
return placemarks == null ? null : placemarks.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Placemark getPlacemark(String name, Comparator<Placemark> comp) throws IOException, ParseException {
|
||||
StringBuilder url = new StringBuilder(SEARCH_BY_NAME_SERVICE);
|
||||
url.append("name=" + URLEncoder.encode(name, "UTF-8"));
|
||||
URLConnection connection = addUsernameParameterAndConnect(url);
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(), Charset.forName("UTF-8")));
|
||||
final JSONParser parser = new JSONParser();
|
||||
final JSONObject obj = (JSONObject) parser.parse(in);
|
||||
final JSONArray geonames = (JSONArray) obj.get("geonames");
|
||||
return geonames.stream().map(o->jsonToPlacemark((JSONObject) o)).sorted(comp).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
private List<Placemark> getPlacemarksNearSorted(Position position, double radius, Comparator<Placemark> comp)
|
||||
throws IOException, ParseException {
|
||||
@@ -150,10 +160,9 @@ public class ReverseGeocoderImpl implements ReverseGeocoder {
|
||||
* @return A {@link Placemark} or <code>null</code>, if the object doesn't contain a name, a postion or the
|
||||
* if the population is 0
|
||||
*/
|
||||
private Placemark JSONToPlacemark(JSONObject json) {
|
||||
private Placemark jsonToPlacemark(JSONObject json) {
|
||||
String name = (String) json.get("toponymName");
|
||||
String countryCode = (String) json.get("countryCode");
|
||||
|
||||
// Tries are necessary, because some latitude or longitude values delivered by Geonames have no decimal places
|
||||
// and are interpreted as Long
|
||||
// Casting a Long to a Double raises a ClassCastException
|
||||
@@ -234,12 +243,9 @@ public class ReverseGeocoderImpl implements ReverseGeocoder {
|
||||
|
||||
private JSONArray callNearestService(Position position) throws MalformedURLException, IOException, ParseException {
|
||||
StringBuilder url = new StringBuilder(NEARBY_PLACE_SERVICE);
|
||||
url.append("&lat=" + Double.toString(position.getLatDeg()));
|
||||
url.append("lat=" + Double.toString(position.getLatDeg()));
|
||||
url.append("&lng=" + Double.toString(position.getLngDeg()));
|
||||
url.append("&username=" + getGeonamesUser());
|
||||
|
||||
URL request = new URL(url.toString());
|
||||
URLConnection connection = request.openConnection();
|
||||
URLConnection connection = addUsernameParameterAndConnect(url);
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(), Charset.forName("UTF-8")));
|
||||
JSONParser parser = new JSONParser();
|
||||
JSONObject obj = (JSONObject) parser.parse(in);
|
||||
@@ -247,18 +253,20 @@ public class ReverseGeocoderImpl implements ReverseGeocoder {
|
||||
return geonames;
|
||||
}
|
||||
|
||||
private URLConnection addUsernameParameterAndConnect(StringBuilder url) throws MalformedURLException, IOException {
|
||||
url.append("&username=");
|
||||
url.append(getGeonamesUser());
|
||||
return HttpUrlConnectionHelper.redirectConnection(new URL(url.toString()), Duration.ONE_MINUTE, c->c.setRequestProperty("User-Agent", ""));
|
||||
}
|
||||
|
||||
private JSONArray callNearbyService(Position position, double radius, int maxRows) throws MalformedURLException,
|
||||
IOException, ParseException {
|
||||
StringBuilder url = new StringBuilder(NEARBY_PLACE_SERVICE);
|
||||
url.append("&lat=" + Double.toString(position.getLatDeg()));
|
||||
url.append("lat=" + Double.toString(position.getLatDeg()));
|
||||
url.append("&lng=" + Double.toString(position.getLngDeg()));
|
||||
url.append("&radius=" + Double.toString(radius));
|
||||
url.append("&maxRows=" + Integer.toString(maxRows));
|
||||
url.append("&username=" + getGeonamesUser());
|
||||
|
||||
URL request = new URL(url.toString());
|
||||
URLConnection connection = request.openConnection();
|
||||
connection.setRequestProperty("User-Agent", "");
|
||||
URLConnection connection = addUsernameParameterAndConnect(url);
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(), Charset.forName("UTF-8")));
|
||||
JSONParser parser = new JSONParser();
|
||||
JSONObject obj = (JSONObject) parser.parse(in);
|
||||
@@ -270,5 +278,4 @@ public class ReverseGeocoderImpl implements ReverseGeocoder {
|
||||
private String getGeonamesUser() {
|
||||
return GEONAMES_USER+new Random().nextInt(10);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ CREATE COLUMN TABLE SAILING."Regatta" (
|
||||
"boatClass" NVARCHAR(20) NOT NULL,
|
||||
"scoringScheme" NVARCHAR(255) NOT NULL,
|
||||
"rankingMetric" NVARCHAR(255) NOT NULL,
|
||||
"eventId" NVARCHAR(36)
|
||||
"eventId" NVARCHAR(36),
|
||||
FOREIGN KEY ("boatClass") REFERENCES SAILING."BoatClass" ("id"),
|
||||
FOREIGN KEY ("eventId") REFERENCES SAILING."Event" ("id"),
|
||||
FOREIGN KEY ("scoringScheme") REFERENCES SAILING."ScoringScheme" ("id")
|
||||
|
||||
+3
-1
@@ -32,6 +32,7 @@ import com.sap.sailing.domain.common.LegType;
|
||||
import com.sap.sailing.domain.common.ManeuverType;
|
||||
import com.sap.sailing.domain.common.MaxPointsReason;
|
||||
import com.sap.sailing.domain.common.NoWindException;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.ScoringSchemeType;
|
||||
import com.sap.sailing.domain.common.Tack;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
@@ -158,7 +159,8 @@ public class HanaCloudSacExportResource extends SharedAbstractSailingServerResou
|
||||
insertEvents.setString(5, event.getVenue().getName());
|
||||
insertEvents.setBoolean(6, event.isPublic());
|
||||
insertEvents.setString(7, event.getDescription());
|
||||
insertEvents.setString(8, "POINT(49.5 -2.4)");
|
||||
final Position location = event.getLocation();
|
||||
insertEvents.setString(8, location != null ? String.format("POINT(%1.5f %1.5f)", location.getLatDeg(), location.getLngDeg()) : null);
|
||||
insertEvents.execute();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ public class HttpUrlConnectionHelper {
|
||||
}
|
||||
urlConnection.setDoOutput(true);
|
||||
if (optionalRequestMethod != null) {
|
||||
((HttpURLConnection)urlConnection).setRequestMethod(optionalRequestMethod);
|
||||
((HttpURLConnection) urlConnection).setRequestMethod(optionalRequestMethod);
|
||||
}
|
||||
urlConnection.setReadTimeout((int) timeout.asMillis());
|
||||
if (urlConnection instanceof HttpURLConnection) {
|
||||
|
||||
Reference in New Issue
Block a user