9191import com .sap .sailing .domain .common .RegattaNameAndRaceName ;
9292import com .sap .sailing .domain .common .Tack ;
9393import com .sap .sailing .domain .common .TargetTimeInfo ;
94+ import com .sap .sailing .domain .common .Wind ;
9495import com .sap .sailing .domain .common .WindSource ;
96+ import com .sap .sailing .domain .common .WindSourceType ;
9597import com .sap .sailing .domain .common .abstractlog .NotRevokableException ;
9698import com .sap .sailing .domain .common .dto .CompetitorDTO ;
9799import com .sap .sailing .domain .common .dto .FleetDTO ;
98100import com .sap .sailing .domain .common .dto .LeaderboardDTO ;
99101import com .sap .sailing .domain .common .dto .LeaderboardEntryDTO ;
100102import com .sap .sailing .domain .common .dto .LeaderboardRowDTO ;
101103import com .sap .sailing .domain .common .dto .LegEntryDTO ;
104+ import com .sap .sailing .domain .common .impl .WindImpl ;
102105import com .sap .sailing .domain .common .polars .NotEnoughDataHasBeenAddedException ;
103106import com .sap .sailing .domain .common .security .SecuredDomainType ;
104107import com .sap .sailing .domain .common .security .SecuredDomainType .LeaderboardActions ;
178181import com .sap .sse .common .Color ;
179182import com .sap .sse .common .Distance ;
180183import com .sap .sse .common .Duration ;
184+ import com .sap .sse .common .Position ;
181185import com .sap .sse .common .Speed ;
182186import com .sap .sse .common .SpeedWithBearing ;
183187import com .sap .sse .common .TimePoint ;
186190import com .sap .sse .common .Util .Pair ;
187191import com .sap .sse .common .Util .Triple ;
188192import com .sap .sse .common .WithID ;
193+ import com .sap .sse .common .impl .DegreePosition ;
189194import com .sap .sse .common .impl .MillisecondsDurationImpl ;
190195import com .sap .sse .common .impl .MillisecondsTimePoint ;
191196import com .sap .sse .common .impl .RGBColor ;
209214@ Path ("/v1/regattas" )
210215public class RegattasResource extends AbstractSailingServerResource {
211216 private static final String SECONDARY_USER_BEARER_TOKEN = "secondaryuserbearertoken" ;
212-
217+ private static final int MAX_NUMBER_OF_WIND_FIXES_PER_REQUEST = 10000 ;
213218 private static final Logger logger = Logger .getLogger (RegattasResource .class .getName ());
214219
215220 private DataMiningResource dataMiningResource ;
@@ -1775,6 +1780,11 @@ public Response getHighQualityWindFixes(@PathParam("regattaname") String regatta
17751780 return response ;
17761781 }
17771782
1783+ @ FunctionalInterface
1784+ private static interface WindForTrackedRaceSerializerFactory {
1785+ JsonSerializer <TrackedRace > getSerializer (TimePoint finalFrom , TimePoint finalTo );
1786+ }
1787+
17781788 @ GET
17791789 @ Produces ("application/json;charset=UTF-8" )
17801790 @ Path ("{regattaname}/races/{racename}/wind" )
@@ -1785,6 +1795,93 @@ public Response getWind(@PathParam("regattaname") String regattaName, @PathParam
17851795 @ QueryParam ("totimeasmillis" ) Long totimeasmillis ,
17861796 @ HeaderParam (SECONDARY_USER_BEARER_TOKEN ) String secondaryUserBearerToken ,
17871797 @ Context HttpServletRequest request ) {
1798+ return getWindWithSerializer (regattaName , raceName , fromtime , fromtimeasmillis , totime , totimeasmillis , secondaryUserBearerToken , request ,
1799+ (finalFrom , finalTo )->new TrackedRaceJsonSerializer (
1800+ ws -> new DefaultWindTrackJsonSerializer (MAX_NUMBER_OF_WIND_FIXES_PER_REQUEST , finalFrom , finalTo ,ws ), windSource , windSourceId ));
1801+ }
1802+
1803+ @ GET
1804+ @ Produces ("application/json;charset=UTF-8" )
1805+ @ Path ("{regattaname}/races/{racename}/wind_at_position" )
1806+ public Response getWindAtPosition (@ PathParam ("regattaname" ) String regattaName , @ PathParam ("racename" ) String raceName ,
1807+ @ QueryParam ("fromtime" ) String fromtime ,
1808+ @ QueryParam ("fromtimeasmillis" ) Long fromtimeasmillis , @ QueryParam ("totime" ) String totime ,
1809+ @ QueryParam ("totimeasmillis" ) Long totimeasmillis ,
1810+ @ QueryParam ("intervalasmillis" ) Long intervalasmillis ,
1811+ @ QueryParam ("latDeg[]" ) List <Double > latitudesInDegrees ,
1812+ @ QueryParam ("lngDeg[]" ) List <Double > longitudesInDegrees ,
1813+ @ HeaderParam (SECONDARY_USER_BEARER_TOKEN ) String secondaryUserBearerToken ,
1814+ @ Context HttpServletRequest request ) {
1815+ final Response response ;
1816+ if (latitudesInDegrees == null || longitudesInDegrees == null ) {
1817+ response = Response .serverError ().entity ("Latitudes and longitudes must be provided as query parameters latDeg[] and lngDeg[]" ).build ();
1818+ } else if (latitudesInDegrees .size () != longitudesInDegrees .size ()) {
1819+ response = Response .serverError ().entity ("Latitudes and longitudes arrays in query parameters latDeg[] and lngDeg[] must have equal count" ).build ();
1820+ } else if (intervalasmillis == null ) {
1821+ response = Response .serverError ().entity ("intervalasmillis query parameter is missing" ).build ();
1822+ } else if (intervalasmillis <= 0 ) {
1823+ response = Response .serverError ().entity ("intervalasmillis query parameter must be a positive value" ).build ();
1824+ } else {
1825+ response = getWindWithSerializer (regattaName , raceName , fromtime , fromtimeasmillis , totime , totimeasmillis , secondaryUserBearerToken , request ,
1826+ new WindForTrackedRaceSerializerFactory () {
1827+ @ Override
1828+ public JsonSerializer <TrackedRace > getSerializer (TimePoint finalFrom , TimePoint finalTo ) {
1829+ return new JsonSerializer <TrackedRace >() {
1830+ @ Override
1831+ public JSONObject serialize (TrackedRace trackedRace ) {
1832+ return serializeWindAtPositionsAndTimePoints (trackedRace , finalFrom , finalTo ,
1833+ new MillisecondsDurationImpl (intervalasmillis ), latitudesInDegrees ,
1834+ longitudesInDegrees );
1835+ }
1836+ };
1837+ }
1838+ });
1839+ }
1840+ return response ;
1841+ }
1842+
1843+ private JSONObject serializeWindAtPositionsAndTimePoints (TrackedRace trackedRace , TimePoint from ,
1844+ TimePoint to , Duration interval , List <Double > latitudesInDegrees , List <Double > longitudesInDegrees ) {
1845+ final Iterable <Wind > windIterable = new Iterable <Wind >() {
1846+ @ Override
1847+ public Iterator <Wind > iterator () {
1848+ return new Iterator <Wind >() {
1849+ private TimePoint timePoint = from ;
1850+ private int count = 0 ;
1851+
1852+ @ Override
1853+ public boolean hasNext () {
1854+ return timePoint != null && (to == null || !timePoint .after (to ));
1855+ }
1856+
1857+ @ Override
1858+ public Wind next () {
1859+ final Position position = new DegreePosition (latitudesInDegrees .get (count ), longitudesInDegrees .get (count ));
1860+ // TODO bug6281: the position gets lost when asking the tracked race for wind at that position because we get an average from all wind sources,
1861+ // including the averaged position. We may want to pass the requested position into the response, too, but how?
1862+ final Wind windFromRace = trackedRace .getWind (position , timePoint );
1863+ final Wind result = new WindImpl (position , timePoint , windFromRace );
1864+ count ++;
1865+ if (count >= latitudesInDegrees .size ()) {
1866+ timePoint = timePoint .plus (interval );
1867+ count = 0 ;
1868+ }
1869+ return result ;
1870+ }
1871+ };
1872+ }
1873+ };
1874+ return DefaultWindTrackJsonSerializer .serializeFixesFromWindSource (trackedRace .getWindSources (WindSourceType .COMBINED ).iterator ().next (),
1875+ windIterable , wind ->wind , MAX_NUMBER_OF_WIND_FIXES_PER_REQUEST );
1876+ }
1877+
1878+ private Response getWindWithSerializer (final String regattaName , final String raceName ,
1879+ final String fromtime ,
1880+ final Long fromtimeasmillis , final String totime ,
1881+ final Long totimeasmillis ,
1882+ final String secondaryUserBearerToken ,
1883+ final HttpServletRequest request ,
1884+ final WindForTrackedRaceSerializerFactory serializerFactory ) {
17881885 Response response ;
17891886 final String clientIP = HttpRequestUtils .getClientIP (request );
17901887 final String userAgent = HttpRequestUtils .getUserAgent (request );
@@ -1830,12 +1927,7 @@ public Response getWind(@PathParam("regattaname") String regattaName, @PathParam
18301927 final TimePoint finalFrom = Util .getLatestOfTimePoints (from , trackedRace .getStartOfTracking ());
18311928 final TimePoint finalTo = Util .getEarliestOfTimePoints (to , Util .getEarliestOfTimePoints (
18321929 trackedRace .getEndOfTracking (), trackedRace .getTimePointOfNewestEvent ()));
1833- final TrackedRaceJsonSerializer serializer = new TrackedRaceJsonSerializer (
1834- ws -> new DefaultWindTrackJsonSerializer (/* maxNumberOfFixes */ 10000 , finalFrom , finalTo ,
1835- ws ),
1836- windSource , windSourceId );
1837-
1838- final JSONObject jsonWindTracks = serializer .serialize (trackedRace );
1930+ final JSONObject jsonWindTracks = serializerFactory .getSerializer (finalFrom , finalTo ).serialize (trackedRace );
18391931 response = Response .ok (streamingOutput (jsonWindTracks )).build ();
18401932 }
18411933 }
0 commit comments