SnappingService.java
package org.heigit.ors.api.services;
import com.graphhopper.GraphHopper;
import com.graphhopper.routing.util.AccessFilter;
import com.graphhopper.routing.util.FlagEncoder;
import com.graphhopper.routing.weighting.Weighting;
import com.graphhopper.storage.GraphHopperStorage;
import com.graphhopper.util.PMap;
import org.heigit.ors.api.requests.snapping.SnappingApiRequest;
import org.heigit.ors.common.StatusCode;
import org.heigit.ors.exceptions.ParameterValueException;
import org.heigit.ors.exceptions.PointNotFoundException;
import org.heigit.ors.exceptions.StatusCodeException;
import org.heigit.ors.matrix.MatrixSearchContext;
import org.heigit.ors.matrix.MatrixSearchContextBuilder;
import org.heigit.ors.routing.RoutingProfile;
import org.heigit.ors.routing.RoutingProfileManager;
import org.heigit.ors.routing.RoutingProfileType;
import org.heigit.ors.routing.WeightingMethod;
import org.heigit.ors.routing.graphhopper.extensions.ORSWeightingFactory;
import org.heigit.ors.snapping.SnappingErrorCodes;
import org.heigit.ors.snapping.SnappingRequest;
import org.heigit.ors.snapping.SnappingResult;
import org.heigit.ors.util.ProfileTools;
import org.locationtech.jts.geom.Coordinate;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class SnappingService extends ApiService {
public SnappingResult generateSnappingFromRequest(SnappingApiRequest snappingApiRequest) throws StatusCodeException {
SnappingRequest snappingRequest = this.convertSnappingRequest(snappingApiRequest);
try {
RoutingProfileManager rpm = RoutingProfileManager.getInstance();
RoutingProfile rp = rpm.getProfiles().getRouteProfile(snappingRequest.getProfileType());
GraphHopper gh = rp.getGraphhopper();
return computeResult(snappingRequest, gh);
} catch (PointNotFoundException e) {
throw new StatusCodeException(StatusCode.NOT_FOUND, SnappingErrorCodes.POINT_NOT_FOUND, e.getMessage());
} catch (StatusCodeException e) {
throw e;
} catch (Exception e) {
throw new StatusCodeException(StatusCode.INTERNAL_SERVER_ERROR, SnappingErrorCodes.UNKNOWN);
}
}
private SnappingRequest convertSnappingRequest(SnappingApiRequest snappingApiRequest) throws StatusCodeException {
int profileType = -1;
try {
profileType = convertRouteProfileType(snappingApiRequest.getProfile());
} catch (Exception e) {
throw new ParameterValueException(SnappingErrorCodes.INVALID_PARAMETER_VALUE, SnappingApiRequest.PARAM_PROFILE);
}
SnappingRequest snappingRequest = new SnappingRequest(profileType,
convertLocations(snappingApiRequest.getLocations()), snappingApiRequest.getMaximumSearchRadius());
if (snappingApiRequest.hasId())
snappingRequest.setId(snappingApiRequest.getId());
return snappingRequest;
}
private Coordinate[] convertLocations(List<List<Double>> locations) throws StatusCodeException {
Coordinate[] coordinates = new Coordinate[locations.size()];
int i = 0; // apparently stream().map() does not work with exceptions
for (var location: locations) {
coordinates[i] = convertLocation(location);
i++;
}
return coordinates;
}
private static Coordinate convertLocation(List<Double> location) throws StatusCodeException {
if (location.size() != 2) {
throw new ParameterValueException(SnappingErrorCodes.INVALID_PARAMETER_VALUE, SnappingApiRequest.PARAM_LOCATIONS);
}
return new Coordinate(location.get(0), location.get(1));
}
public SnappingResult computeResult(SnappingRequest snappingRequest, GraphHopper gh) throws Exception {
String encoderName = RoutingProfileType.getEncoderName(snappingRequest.getProfileType());
FlagEncoder flagEncoder = gh.getEncodingManager().getEncoder(encoderName);
PMap hintsMap = new PMap();
int weightingMethod = WeightingMethod.RECOMMENDED; // Only needed to create the profile string
ProfileTools.setWeightingMethod(hintsMap, weightingMethod, snappingRequest.getProfileType(), false);
ProfileTools.setWeighting(hintsMap, weightingMethod, snappingRequest.getProfileType(), false);
String profileName = ProfileTools.makeProfileName(encoderName, hintsMap.getString("weighting", ""), false);
GraphHopperStorage ghStorage = gh.getGraphHopperStorage();
String graphDate = ghStorage.getProperties().get("datareader.import.date");
// TODO: replace usage of matrix search context by snapping-specific class
MatrixSearchContextBuilder builder = new MatrixSearchContextBuilder(ghStorage, gh.getLocationIndex(), AccessFilter.allEdges(flagEncoder.getAccessEnc()), true);
Weighting weighting = new ORSWeightingFactory(ghStorage, gh.getEncodingManager()).createWeighting(gh.getProfile(profileName), hintsMap, false);
MatrixSearchContext mtxSearchCntx = builder.create(ghStorage.getBaseGraph(), null, weighting, profileName, snappingRequest.getLocations(), snappingRequest.getLocations(), snappingRequest.getMaximumSearchRadius());
return new SnappingResult(mtxSearchCntx.getSources().getLocations(), graphDate);
}
}