Java tutorial
/* Copyright (c) 2011 Danish Maritime Authority. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dk.dma.ais.track.rest.resource; import com.google.common.collect.Sets; import dk.dma.ais.packet.AisPacketSource; import dk.dma.ais.packet.AisPacketSourceFilters; import dk.dma.ais.track.AisTrackService; import dk.dma.ais.track.rest.resource.exceptions.CannotParseFilterExpressionException; import dk.dma.ais.track.rest.resource.exceptions.TargetNotFoundException; import dk.dma.ais.tracker.targetTracker.TargetInfo; import dk.dma.enav.model.geometry.Area; import dk.dma.enav.model.geometry.BoundingBox; import dk.dma.enav.model.geometry.Circle; import dk.dma.enav.model.geometry.CoordinateSystem; import dk.dma.enav.model.geometry.Position; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.annotation.PostConstruct; import javax.inject.Inject; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.time.Instant; import java.util.List; import java.util.Set; import java.util.function.Predicate; import static org.apache.commons.lang3.StringUtils.isBlank; /** * @author tbsalling */ @RestController @RequestMapping(value = "ais-track") public class TrackResource { @Inject AisTrackService trackService; @PostConstruct private void init() { trackService.start(); } private final Instant timeStarted = Instant.now(); /** * Show status page. * * Example URL: * - http://localhost:8080/?sourceFilter=s.country%20in%20(DK) * * @param sourceFilterExpression Source filter expression * @return */ @RequestMapping(value = "/", produces = MediaType.TEXT_PLAIN_VALUE) String home(@RequestParam(value = "sourceFilter", required = false) String sourceFilterExpression) { StringBuilder sb = new StringBuilder(); sb.append("Danish Maritime Authority - AIS Tracker\n").append("---------------------------------------\n") .append("\n").append("Tracking started: ").append(timeStarted).append('\n').append("Current time: ") .append(Instant.now()).append('\n').append("Total targets tracked: ") .append(trackService.numberOfTargets()).append('\n'); if (!isBlank(sourceFilterExpression)) sb.append("Targets matching source filter expression: ") .append(trackService.numberOfTargets(createSourceFilterPredicate(sourceFilterExpression))) .append('\n'); return sb.toString(); } /** * Return JSON for the TargetInfo matching mmsi and sourceFilter. * * URL examples (with URL encoded filter expressions): * - http://localhost:8080/track/257742710?sourceFilter=s.country%20in%20(DK%2C%20NO) * - http://localhost:8080/track/257742710?sourceFilter=s.country%20in%20(DK) * * @param sourceFilterExpression * @return */ @RequestMapping(value = "/track/{mmsi}", produces = MediaType.APPLICATION_JSON_VALUE) TargetInfo track(@PathVariable int mmsi, @RequestParam(value = "sourceFilter", required = false) String sourceFilterExpression) { TargetInfo target = trackService.target(mmsi, createSourceFilterPredicate(sourceFilterExpression)); if (target == null) { throw new TargetNotFoundException(mmsi); } return target; } /** * Return JSON for all TargetInfo's matching sourceFilter and targetFilter. * * URL examples (with URL encoded filter expressions): * - http://localhost:8080/tracks?sourceFilter=s.region%3D806 * - http://localhost:8080/tracks?sourceFilter=s.country%3DDK * - http://localhost:8080/tracks?sourceFilter=s.country%3DDK%26s.bs%3D2190047 * - http://localhost:8080/tracks?sourceFilter=s.country%20in%20(DK%2C%20NO) * * - http://localhost:8080/tracks?mmsi=244820404&mmsi=345070335 * * - http://localhost:8080/tracks?area=52.3|4.8|52.5|4.9 * - http://localhost:8080/tracks?area=52.3|4.8|52.5|4.9&area=20.0|100.0|21.0|110.0 * * - http://localhost:8080/tracks?mmsi=244820404&mmsi=345070335&area=52.0|4.0|52.5|5.0 * * - http://localhost:8080/tracks?mmsi=244820404&mmsi=345070335&area=52.0|4.0|52.5|5.0&area=20.0|100.0|21.0|110.0&sourceFilter=s.region%3D806|s.country%20in%20DK) * * @param sourceFilterExpression * @param mmsiParams mmsi numbers to include in the result * @param areaParams areas to include in the result * @return */ @RequestMapping(value = "/tracks", produces = MediaType.APPLICATION_JSON_VALUE) Set<TargetInfo> tracks(@RequestParam(value = "sourceFilter", required = false) String sourceFilterExpression, @RequestParam(value = "baseArea", required = false) List<String> baseAreaParams, @RequestParam(value = "area", required = false) List<String> areaParams, @RequestParam(value = "mmsi", required = false) List<String> mmsiParams) { Set<Integer> mmsis = Sets.newHashSet(); if (mmsiParams != null && mmsiParams.size() > 0) { mmsiParams.forEach(mmsi -> mmsis.add(Integer.valueOf(mmsi))); } Set<Area> areas = map2Areas(areaParams); Set<Area> baseAreas = map2Areas(baseAreaParams); return trackService.targets(createSourceFilterPredicate(sourceFilterExpression), createTargetFilterPredicate(mmsis, baseAreas, areas)); } private static Set<Area> map2Areas(List<String> areaParams) { Set<Area> areas = Sets.newHashSet(); if (areaParams != null && areaParams.size() > 0) { areaParams.forEach(area -> { if (area.trim().startsWith("circle")) { try { area = URLDecoder.decode(area, "UTF-8"); System.out.println(area); int p1 = area.indexOf("("); int p2 = area.indexOf(")"); String[] values = area.substring(p1 + 1, p2).split(","); Double lat = Double.valueOf(values[0].trim()); Double lon = Double.valueOf(values[1].trim()); Double radius = Double.valueOf(values[2].trim()); areas.add(new Circle(Position.create(lat, lon), radius, CoordinateSystem.CARTESIAN)); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } else { String[] bordersAsString = area.split("\\|"); if (bordersAsString == null || bordersAsString.length != 4) throw new IllegalArgumentException( "Expected four floating point values for area argument separated by vertical bar, not: " + area); Double lat1 = Double.valueOf(bordersAsString[0]); Double lon1 = Double.valueOf(bordersAsString[1]); Double lat2 = Double.valueOf(bordersAsString[2]); Double lon2 = Double.valueOf(bordersAsString[3]); areas.add(BoundingBox.create(Position.create(lat1, lon1), Position.create(lat2, lon2), CoordinateSystem.CARTESIAN)); } }); } return areas; } /** Create a Predicate<AisPacketSource> out of a user supplied expression string */ static Predicate<AisPacketSource> createSourceFilterPredicate(String sourceFilterExpression) { Predicate<AisPacketSource> sourceFilter; if (!isBlank(sourceFilterExpression)) { try { sourceFilter = AisPacketSourceFilters.parseSourceFilter(sourceFilterExpression); } catch (Exception e) { throw new CannotParseFilterExpressionException(e, sourceFilterExpression); } } else { sourceFilter = src -> true; } return sourceFilter; } /** Create a Predicate<TargetInfo> out of user supplied mmsi and area information */ static Predicate<TargetInfo> createTargetFilterPredicate(Set<Integer> mmsis, Set<Area> baseAreas, Set<Area> areas) { Predicate<TargetInfo> mmsiPredicate = null; if (mmsis != null && mmsis.size() > 0) { mmsiPredicate = targetInfo -> mmsis.contains(targetInfo.getMmsi()); } Predicate<TargetInfo> baseAreaPredicate = null; if (baseAreas != null && baseAreas.size() > 0) { baseAreaPredicate = targetInfo -> baseAreas.stream() .anyMatch(area -> targetInfo.getPosition() != null && area.contains(targetInfo.getPosition())); } Predicate<TargetInfo> areaPredicate = null; if (areas != null && areas.size() > 0) { areaPredicate = targetInfo -> areas.stream() .anyMatch(area -> targetInfo.getPosition() != null && area.contains(targetInfo.getPosition())); } Predicate<TargetInfo> resultingAreaPredicate = null; if (baseAreaPredicate != null && areaPredicate == null) { resultingAreaPredicate = baseAreaPredicate; } else if (baseAreaPredicate != null && areaPredicate != null) { resultingAreaPredicate = baseAreaPredicate.and(areaPredicate); } else { resultingAreaPredicate = areaPredicate; } if (mmsiPredicate == null && resultingAreaPredicate == null) return t -> true; else if (mmsiPredicate != null && resultingAreaPredicate == null) return mmsiPredicate; else if (mmsiPredicate == null && resultingAreaPredicate != null) return resultingAreaPredicate; else return mmsiPredicate.or(resultingAreaPredicate); } }