diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorWithEstimationDataSupportDecoratorImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorWithEstimationDataSupportDecoratorImpl.java index 8e13e8fe62e..12dc6008d8b 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorWithEstimationDataSupportDecoratorImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorWithEstimationDataSupportDecoratorImpl.java @@ -258,7 +258,9 @@ public class ManeuverDetectorWithEstimationDataSupportDecoratorImpl } finally { maneuverDetector.track.unlockAfterRead(); } - + if(courseAtMaxTurningRate == null) { + courseAtMaxTurningRate = stepWithLowestSpeed.getSpeedWithBearing().getBearing(); + } ManeuverLoss projectedManeuverLoss = maneuverDetector.getManeuverLoss(maneuverCurve.getMainCurveBoundaries()); Distance distanceSailedIfNotManeuvering = maneuverCurve.getMainCurveBoundaries().getSpeedWithBearingBefore() .travel(maneuverCurve.getMainCurveBoundaries().getDuration()); diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorTrackWithEstimationDataJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorTrackWithEstimationDataJsonSerializer.java index 7b7d9032416..a7a4d586914 100644 --- a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorTrackWithEstimationDataJsonSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorTrackWithEstimationDataJsonSerializer.java @@ -8,6 +8,8 @@ import org.json.simple.JSONObject; import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Waypoint; +import com.sap.sailing.domain.common.WindSource; +import com.sap.sailing.domain.common.WindSourceType; import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.maneuverdetection.impl.ManeuverDetectorImpl; import com.sap.sailing.domain.maneuverdetection.impl.TrackTimeInfo; @@ -36,6 +38,7 @@ public class CompetitorTrackWithEstimationDataJsonSerializer extends AbstractTra public static final String FIXES_COUNT_FOR_POLARS = "fixesCountForPolars"; public static final String MARK_PASSINGS_COUNT = "markPassingsCount"; public static final String WAYPOINTS_COUNT = "waypointsCount"; + public static final String WIND_QUALITY = "windQuality"; private final BoatClassJsonSerializer boatClassJsonSerializer; private final CompetitorTrackElementsJsonSerializer elementsJsonSerializer; @@ -63,6 +66,17 @@ public class CompetitorTrackWithEstimationDataJsonSerializer extends AbstractTra public JSONObject serialize(TrackedRace trackedRace) { final JSONObject result = new JSONObject(); JSONArray byCompetitorJson = new JSONArray(); + int windQuality = 0; + for (WindSource windSource : trackedRace.getWindSources()) { + if (windSource.getType() == WindSourceType.EXPEDITION) { + windQuality = 2; + break; + } + if (windSource.getType() == WindSourceType.RACECOMMITTEE) { + windQuality = 1; + } + } + result.put(WIND_QUALITY, windQuality); result.put(BYCOMPETITOR, byCompetitorJson); for (Competitor competitor : trackedRace.getRace().getCompetitors()) { ManeuverDetectorImpl maneuverDetector = new ManeuverDetectorImpl(trackedRace, competitor); diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/RaceWindJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/RaceWindJsonSerializer.java new file mode 100644 index 00000000000..4a1754382ce --- /dev/null +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/RaceWindJsonSerializer.java @@ -0,0 +1,111 @@ +package com.sap.sailing.server.gateway.serialization.impl; + +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; + +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; + +import com.sap.sailing.domain.common.Position; +import com.sap.sailing.domain.common.Wind; +import com.sap.sailing.domain.common.WindSource; +import com.sap.sailing.domain.common.WindSourceType; +import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sailing.domain.tracking.WindTrack; +import com.sap.sse.common.Duration; +import com.sap.sse.common.TimePoint; +import com.sap.sse.common.Util; + +/** + * + * @author Vladislav Chumak (D069712) + * + */ +public class RaceWindJsonSerializer extends AbstractTrackedRaceDataJsonSerializer { + public static final String WIND_SOURCES = "windSources"; + public static final String TYPE = "type"; + public static final String FIRST_POSITION = "firstPosition"; + public static final String START_TIME_POINT = "startUnixTime"; + public static final String END_TIME_POINT = "endUnixTime"; + public static final String SAMPLING_RATE = "samplingRate"; + public static final String FIXES = "fixes"; + + private final PositionJsonSerializer positionSerializer = new PositionJsonSerializer(); + private final WindJsonSerializer windSerializer = new WindJsonSerializer(positionSerializer); + + @Override + public JSONObject serialize(TrackedRace trackedRace) { + final TimePoint finalFrom = trackedRace.getStartOfTracking(); + final TimePoint finalTo = Util.getEarliestOfTimePoints(trackedRace.getEndOfTracking(), + trackedRace.getTimePointOfNewestEvent()); + List highQualityWindSources = trackedRace.getWindSources().stream() + .filter(windSource -> windSource.getType() == WindSourceType.EXPEDITION + || windSource.getType() == WindSourceType.RACECOMMITTEE) + .collect(Collectors.toList()); + JSONArray windSourcesJson = new JSONArray(); + TimePoint earliestTimePoint = null; + TimePoint latestTimePoint = null; + Position firstPosition = null; + double bestSamplingRate = Double.MAX_VALUE; + for (WindSource windSource : highQualityWindSources) { + WindTrack windTrack = trackedRace.getOrCreateWindTrack(windSource); + windTrack.lockForRead(); + try { + Iterator iterator = windTrack.getFixes(finalFrom, true, finalTo, true).iterator(); + if (iterator.hasNext()) { + Wind fix = iterator.next(); + if (fix.getPosition() != null) { + JSONArray windFixesJson = new JSONArray(); + TimePoint firstWindSourceTimePoint = fix.getTimePoint(); + TimePoint lastWindSourceTimePoint = null; + Position firstWindSourcePosition = fix.getPosition(); + if (firstPosition == null) { + firstPosition = firstWindSourcePosition; + } + if (earliestTimePoint == null || earliestTimePoint.after(fix.getTimePoint())) { + earliestTimePoint = fix.getTimePoint(); + } + do { + JSONObject windJson = windSerializer.serialize(fix); + windFixesJson.add(windJson); + if (iterator.hasNext()) { + fix = iterator.next(); + } else { + lastWindSourceTimePoint = fix.getTimePoint(); + if (latestTimePoint == null || latestTimePoint.before(fix.getTimePoint())) { + latestTimePoint = fix.getTimePoint(); + } + fix = null; + } + } while (fix != null); + Duration duration = firstWindSourceTimePoint.until(lastWindSourceTimePoint); + JSONObject windSourceJson = new JSONObject(); + windSourceJson.put(TYPE, windSource.getType().name()); + windSourceJson.put(FIRST_POSITION, positionSerializer.serialize(firstWindSourcePosition)); + windSourceJson.put(START_TIME_POINT, firstWindSourceTimePoint.asMillis()); + windSourceJson.put(END_TIME_POINT, lastWindSourceTimePoint.asMillis()); + double samplingRate = duration.asSeconds() > 0 ? windFixesJson.size() / duration.asSeconds() : 0; + windSourceJson.put(SAMPLING_RATE, samplingRate); + windSourceJson.put(FIXES, windFixesJson); + windSourcesJson.add(windSourceJson); + if (bestSamplingRate > samplingRate) { + bestSamplingRate = samplingRate; + } + } + } + } finally { + windTrack.unlockAfterRead(); + } + } + final JSONObject result = new JSONObject(); + if (!windSourcesJson.isEmpty()) { + result.put(FIRST_POSITION, positionSerializer.serialize(firstPosition)); + result.put(START_TIME_POINT, earliestTimePoint.asMillis()); + result.put(END_TIME_POINT, latestTimePoint.asMillis()); + result.put(SAMPLING_RATE, bestSamplingRate); + result.put(WIND_SOURCES, windSourcesJson); + } + return result; + } +} diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java index 3618eb99115..23f3b501477 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java @@ -105,6 +105,7 @@ import com.sap.sailing.server.gateway.serialization.impl.NationalityJsonSerializ import com.sap.sailing.server.gateway.serialization.impl.PersonJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.PositionJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.RaceEntriesJsonSerializer; +import com.sap.sailing.server.gateway.serialization.impl.RaceWindJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.RegattaJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.SeriesJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.TargetTimeInfoSerializer; @@ -934,6 +935,34 @@ public class RegattasResource extends AbstractSailingServerResource { } return response; } + + @GET + @Produces("application/json;charset=UTF-8") + @Path("{regattaname}/races/{racename}/highQualityWindFixes") + public Response getHighQualityWindFixes(@PathParam("regattaname") String regattaName, + @PathParam("racename") String raceName) { + Response response; + Regatta regatta = findRegattaByName(regattaName); + if (regatta == null) { + response = Response.status(Status.NOT_FOUND) + .entity("Could not find a regatta with name '" + StringEscapeUtils.escapeHtml(regattaName) + "'.") + .type(MediaType.TEXT_PLAIN).build(); + } else { + RaceDefinition race = findRaceByName(regatta, raceName); + if (race == null) { + response = Response.status(Status.NOT_FOUND) + .entity("Could not find a race with name '" + StringEscapeUtils.escapeHtml(raceName) + "'.") + .type(MediaType.TEXT_PLAIN).build(); + } else { + TrackedRace trackedRace = findTrackedRace(regattaName, raceName); + RaceWindJsonSerializer serializer = new RaceWindJsonSerializer(); + JSONObject jsonWindTracks = serializer.serialize(trackedRace); + String json = jsonWindTracks.toJSONString(); + return Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build(); + } + } + return response; + } @GET @Produces("application/json;charset=UTF-8")