Skip to content

Commit

Permalink
refactor: move build isochrones (#1932)
Browse files Browse the repository at this point in the history
  • Loading branch information
sfendrich authored Dec 13, 2024
2 parents b24a801 + 9a749aa commit 81638e4
Show file tree
Hide file tree
Showing 7 changed files with 195 additions and 203 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,22 +47,18 @@ public IsochronesService(EndpointsProperties endpointsProperties, ApiEnginePrope
}

public void generateIsochronesFromRequest(IsochronesRequest isochronesRequest) throws Exception {
isochronesRequest.setIsochroneRequest(convertIsochroneRequest(isochronesRequest));
IsochroneRequest isochroneRequest = convertIsochroneRequest(isochronesRequest);
isochronesRequest.setIsochroneRequest(isochroneRequest);
// request object is built, now check if ors config allows all settings
List<TravellerInfo> travellers = isochronesRequest.getIsochroneRequest().getTravellers();
List<TravellerInfo> travellers = isochroneRequest.getTravellers();

// TODO REFACTORING where should we put the validation code?
validateAgainstConfig(isochronesRequest.getIsochroneRequest(), travellers);
validateAgainstConfig(isochroneRequest);

if (!travellers.isEmpty()) {
isochronesRequest.setIsoMaps(new IsochroneMapCollection());

for (int i = 0; i < travellers.size(); ++i) {
IsochroneSearchParameters searchParams = isochronesRequest.getIsochroneRequest().getSearchParameters(i);
IsochroneMap isochroneMap = RoutingProfileManager.getInstance().buildIsochrone(searchParams);
isochronesRequest.getIsoMaps().add(isochroneMap);
}

IsochroneMapCollection isoMaps = isochroneRequest.computeIsochrones(RoutingProfileManager.getInstance());
// TODO: is this necessary? It seems unusual to transport the response through the request object
isochronesRequest.setIsoMaps(isoMaps);
}
}

Expand Down Expand Up @@ -288,7 +284,8 @@ RouteSearchParameters processIsochronesRequestOptions(IsochronesRequest isochron
return parameters;
}

void validateAgainstConfig(IsochroneRequest isochroneRequest, List<TravellerInfo> travellers) throws StatusCodeException {
void validateAgainstConfig(IsochroneRequest isochroneRequest) throws StatusCodeException {
List<TravellerInfo> travellers = isochroneRequest.getTravellers();
if (!isochroneRequest.isAllowComputeArea() && isochroneRequest.hasAttribute("area"))
throw new StatusCodeException(StatusCode.BAD_REQUEST, IsochronesErrorCodes.FEATURE_NOT_SUPPORTED, "Area computation is not enabled.");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,24 @@
*/
package org.heigit.ors.isochrones;

import org.apache.log4j.Logger;
import org.heigit.ors.common.ServiceRequest;
import org.heigit.ors.common.TravelRangeType;
import org.heigit.ors.common.TravellerInfo;
import org.heigit.ors.exceptions.InternalServerException;
import org.heigit.ors.isochrones.statistics.StatisticsProvider;
import org.heigit.ors.isochrones.statistics.StatisticsProviderConfiguration;
import org.heigit.ors.routing.RoutingProfileType;
import org.heigit.ors.routing.WeightingMethod;
import org.heigit.ors.isochrones.statistics.StatisticsProviderFactory;
import org.heigit.ors.routing.*;
import org.heigit.ors.util.DebugUtility;
import org.heigit.ors.util.ProfileTools;
import org.heigit.ors.util.TemporaryUtilShelter;
import org.locationtech.jts.geom.Coordinate;

import java.util.*;

public class IsochroneRequest extends ServiceRequest {
public static final Logger LOGGER = Logger.getLogger(IsochroneRequest.class);
private String profileName;
private final List<TravellerInfo> travellers;
private String calcMethod;
Expand Down Expand Up @@ -282,4 +289,89 @@ public Map<String, StatisticsProviderConfiguration> getStatsProviders() {
public void setStatsProviders(Map<String, StatisticsProviderConfiguration> statsProviders) {
this.statsProviders = statsProviders;
}

public IsochroneMapCollection computeIsochrones(RoutingProfileManager routingProfileManager) throws Exception {
IsochroneMapCollection isoMaps = new IsochroneMapCollection();
for (int i = 0; i < getTravellers().size(); ++i) {
IsochroneSearchParameters searchParams = getSearchParameters(i);
RoutingProfile rp = routingProfileManager.getRoutingProfile(searchParams.getRouteParameters().getProfileName());
IsochroneMap isochroneMap = buildIsochrone(searchParams, rp);
isoMaps.add(isochroneMap);
}
return isoMaps;
}

/**
* This function creates the actual {@link IsochroneMap}.
* So the first step in the function is a checkup on that.
*
* @param parameters The input are {@link IsochroneSearchParameters}
* @param routingProfile
* @return The return will be an {@link IsochroneMap}
* @throws Exception
*/
public IsochroneMap buildIsochrone(IsochroneSearchParameters parameters, RoutingProfile routingProfile) throws Exception {
// TODO: refactor buildIsochrone as to not need to pass the SearchParameters as they are already present
// in IsochroneRequest. maybe merge with computeIsochrones
IsochroneMap result;

try {
RouteSearchContext searchCntx = TemporaryUtilShelter.createSearchContext(parameters.getRouteParameters(), routingProfile);
IsochroneMapBuilderFactory isochroneMapBuilderFactory = new IsochroneMapBuilderFactory(searchCntx);
result = isochroneMapBuilderFactory.buildMap(parameters);
} catch (Exception ex) {
if (DebugUtility.isDebug()) {
LOGGER.error(ex);
}
throw new InternalServerException(IsochronesErrorCodes.UNKNOWN, "Unable to build an isochrone map.");
}

if (result.getIsochronesCount() > 0) {
if (parameters.hasAttribute(ProfileTools.KEY_TOTAL_POP)) {
try {
Map<StatisticsProviderConfiguration, List<String>> mapProviderToAttrs = new HashMap<>();
StatisticsProviderConfiguration provConfig = parameters.getStatsProviders().get(ProfileTools.KEY_TOTAL_POP);
if (provConfig != null) {
List<String> attrList = new ArrayList<>();
attrList.add(ProfileTools.KEY_TOTAL_POP);
mapProviderToAttrs.put(provConfig, attrList);
}
for (Map.Entry<StatisticsProviderConfiguration, List<String>> entry : mapProviderToAttrs.entrySet()) {
provConfig = entry.getKey();
StatisticsProvider provider = StatisticsProviderFactory.getProvider(provConfig.getName(), provConfig.getParameters());
String[] provAttrs = provConfig.getMappedProperties(entry.getValue());

for (Isochrone isochrone : result.getIsochrones()) {

double[] attrValues = provider.getStatistics(isochrone, provAttrs);
isochrone.setAttributes(entry.getValue(), attrValues, provConfig.getAttribution());

}
}
} catch (Exception ex) {
LOGGER.error(ex);

throw new InternalServerException(IsochronesErrorCodes.UNKNOWN, "Unable to compute isochrone total_pop attribute.");
}
}
if (parameters.hasAttribute("reachfactor") || parameters.hasAttribute("area")) {
for (Isochrone isochrone : result.getIsochrones()) {
String units = parameters.getUnits();
String areaUnits = parameters.getAreaUnits();
if (areaUnits != null) units = areaUnits;
double area = isochrone.calcArea(units);
if (parameters.hasAttribute("area")) {
isochrone.setArea(area);
}
if (parameters.hasAttribute("reachfactor")) {
double reachfactor = isochrone.calcReachfactor(units);
// reach factor could be > 1, which would confuse people
reachfactor = (reachfactor > 1) ? 1 : reachfactor;
isochrone.setReachfactor(reachfactor);
}
}
}
}
return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.heigit.ors.routing.graphhopper.extensions.ORSGraphHopperStorage;
import org.heigit.ors.routing.graphhopper.extensions.ORSWeightingFactory;
import org.heigit.ors.util.ProfileTools;
import org.heigit.ors.util.TemporaryUtilShelter;
import org.locationtech.jts.geom.Coordinate;

public class MatrixRequest extends ServiceRequest {
Expand Down Expand Up @@ -282,7 +283,7 @@ private MatrixResult computeDijkstraMatrix(GraphHopper gh, FlagEncoder flagEncod
private MatrixResult computeCoreMatrix(GraphHopper gh, FlagEncoder flagEncoder, PMap hintsMap, String profileName, RoutingProfile routingProfile) throws Exception {
Weighting weighting = new ORSWeightingFactory(gh.getGraphHopperStorage(), gh.getEncodingManager()).createWeighting(gh.getProfile(profileName), hintsMap, false);
RoutingCHGraph graph = ((ORSGraphHopperStorage) gh.getGraphHopperStorage()).getCoreGraph(profileName);
RouteSearchContext searchCntx = routingProfile.createSearchContext(getSearchParameters());
RouteSearchContext searchCntx = TemporaryUtilShelter.createSearchContext(getSearchParameters(), routingProfile);
PMap additionalHints = searchCntx.getProperties();
EdgeFilter edgeFilter = new ORSEdgeFilterFactory().createEdgeFilter(additionalHints, flagEncoder, gh.getGraphHopperStorage());

Expand Down
170 changes: 0 additions & 170 deletions ors-engine/src/main/java/org/heigit/ors/routing/RoutingProfile.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,12 @@
import org.heigit.ors.config.profile.ExecutionProperties;
import org.heigit.ors.config.profile.PreparationProperties;
import org.heigit.ors.config.profile.ProfileProperties;
import org.heigit.ors.exceptions.InternalServerException;
import org.heigit.ors.isochrones.*;
import org.heigit.ors.isochrones.statistics.StatisticsProvider;
import org.heigit.ors.isochrones.statistics.StatisticsProviderConfiguration;
import org.heigit.ors.isochrones.statistics.StatisticsProviderFactory;
import org.heigit.ors.routing.graphhopper.extensions.*;
import org.heigit.ors.routing.graphhopper.extensions.flagencoders.FlagEncoderNames;
import org.heigit.ors.routing.graphhopper.extensions.manage.ORSGraphManager;
import org.heigit.ors.routing.graphhopper.extensions.storages.builders.BordersGraphStorageBuilder;
import org.heigit.ors.routing.graphhopper.extensions.storages.builders.GraphStorageBuilder;
import org.heigit.ors.routing.graphhopper.extensions.util.ORSParameters;
import org.heigit.ors.routing.parameters.ProfileParameters;
import org.heigit.ors.routing.pathprocessors.ORSPathProcessorFactory;
import org.heigit.ors.util.DebugUtility;
import org.heigit.ors.util.ProfileTools;
import org.heigit.ors.util.StringUtility;
import org.heigit.ors.util.TimeUtility;
Expand Down Expand Up @@ -385,10 +377,6 @@ public ORSGraphHopper getGraphhopper() {
return mGraphHopper;
}

public BBox getBounds() {
return mGraphHopper.getGraphHopperStorage().getBounds();
}

public StorableProperties getGraphProperties() {
return mGraphHopper.getGraphHopperStorage().getProperties();
}
Expand All @@ -397,14 +385,6 @@ public ProfileProperties getProfileConfiguration() {
return profileProperties;
}

public Integer[] getPreferences() {
return mRoutePrefs;
}

public boolean isCHEnabled() {
return mGraphHopper != null && mGraphHopper.getCHPreparationHandler().isEnabled();
}

public void close() {
mGraphHopper.close();
}
Expand All @@ -417,85 +397,6 @@ public Double getAstarEpsilon() {
return astarEpsilon;
}

public RouteSearchContext createSearchContext(RouteSearchParameters searchParams) throws InternalServerException {
PMap props = new PMap();

int profileType = searchParams.getProfileType();
String encoderName = RoutingProfileType.getEncoderName(profileType);

if (FlagEncoderNames.UNKNOWN.equals(encoderName))
throw new InternalServerException(RoutingErrorCodes.UNKNOWN, "unknown vehicle profile.");

if (!mGraphHopper.getEncodingManager().hasEncoder(encoderName)) {
throw new IllegalArgumentException("Vehicle " + encoderName + " unsupported. " + "Supported are: "
+ mGraphHopper.getEncodingManager());
}

FlagEncoder flagEncoder = mGraphHopper.getEncodingManager().getEncoder(encoderName);
ProfileParameters profileParams = searchParams.getProfileParameters();

// PARAMETERS FOR PathProcessorFactory

props.putObject("routing_extra_info", searchParams.getExtraInfo());
props.putObject("routing_suppress_warnings", searchParams.getSuppressWarnings());

props.putObject("routing_profile_type", profileType);
props.putObject("routing_profile_params", profileParams);

/*
* PARAMETERS FOR EdgeFilterFactory
* ======================================================================================================
*/

/* Avoid areas */
if (searchParams.hasAvoidAreas()) {
props.putObject("avoid_areas", searchParams.getAvoidAreas());
}

/* Heavy vehicle filter */
if (profileType == RoutingProfileType.DRIVING_HGV) {
props.putObject("edgefilter_hgv", searchParams.getVehicleType());
}

/* Wheelchair filter */
else if (profileType == RoutingProfileType.WHEELCHAIR) {
props.putObject("edgefilter_wheelchair", "true");
}

/* Avoid features */
if (searchParams.hasAvoidFeatures()) {
props.putObject("avoid_features", searchParams);
}

/* Avoid borders of some form */
if ((searchParams.hasAvoidBorders() || searchParams.hasAvoidCountries())
&& (RoutingProfileType.isDriving(profileType) || RoutingProfileType.isCycling(profileType))) {
props.putObject("avoid_borders", searchParams);
if (searchParams.hasAvoidCountries())
props.putObject("avoid_countries", Arrays.toString(searchParams.getAvoidCountries()));
}

if (profileParams != null && profileParams.hasWeightings()) {
props.putObject(ProfileTools.KEY_CUSTOM_WEIGHTINGS, true);
Iterator<ProfileWeighting> iterator = profileParams.getWeightings().getIterator();
while (iterator.hasNext()) {
ProfileWeighting weighting = iterator.next();
if (!weighting.getParameters().isEmpty()) {
String name = ProfileWeighting.encodeName(weighting.getName());
for (Map.Entry<String, Object> kv : weighting.getParameters().toMap().entrySet())
props.putObject(name + kv.getKey(), kv.getValue());
}
}
}

String localProfileName = ProfileTools.makeProfileName(encoderName, WeightingMethod.getName(searchParams.getWeightingMethod()), Boolean.TRUE.equals(profileProperties.getBuild().getEncoderOptions().getTurnCosts()));
String profileNameCH = ProfileTools.makeProfileName(encoderName, WeightingMethod.getName(searchParams.getWeightingMethod()), false);
RouteSearchContext searchCntx = new RouteSearchContext(mGraphHopper, flagEncoder, localProfileName, profileNameCH);
searchCntx.setProperties(props);

return searchCntx;
}

/**
* Set the speedup techniques used for calculating the route.
* Reults in usage of CH, Core or ALT/AStar, if they are enabled.
Expand Down Expand Up @@ -545,77 +446,6 @@ boolean requiresTimeDependentAlgorithm(RouteSearchParameters searchParams, Route
|| mGraphHopper.isTrafficEnabled();
}

/**
* This function creates the actual {@link IsochroneMap}.
* So the first step in the function is a checkup on that.
*
* @param parameters The input are {@link IsochroneSearchParameters}
* @return The return will be an {@link IsochroneMap}
* @throws Exception
*/
public IsochroneMap buildIsochrone(IsochroneSearchParameters parameters) throws Exception {
IsochroneMap result;

try {
RouteSearchContext searchCntx = createSearchContext(parameters.getRouteParameters());
IsochroneMapBuilderFactory isochroneMapBuilderFactory = new IsochroneMapBuilderFactory(searchCntx);
result = isochroneMapBuilderFactory.buildMap(parameters);
} catch (Exception ex) {
if (DebugUtility.isDebug()) {
LOGGER.error(ex);
}
throw new InternalServerException(IsochronesErrorCodes.UNKNOWN, "Unable to build an isochrone map.");
}

if (result.getIsochronesCount() > 0) {
if (parameters.hasAttribute(ProfileTools.KEY_TOTAL_POP)) {
try {
Map<StatisticsProviderConfiguration, List<String>> mapProviderToAttrs = new HashMap<>();
StatisticsProviderConfiguration provConfig = parameters.getStatsProviders().get(ProfileTools.KEY_TOTAL_POP);
if (provConfig != null) {
List<String> attrList = new ArrayList<>();
attrList.add(ProfileTools.KEY_TOTAL_POP);
mapProviderToAttrs.put(provConfig, attrList);
}
for (Map.Entry<StatisticsProviderConfiguration, List<String>> entry : mapProviderToAttrs.entrySet()) {
provConfig = entry.getKey();
StatisticsProvider provider = StatisticsProviderFactory.getProvider(provConfig.getName(), provConfig.getParameters());
String[] provAttrs = provConfig.getMappedProperties(entry.getValue());

for (Isochrone isochrone : result.getIsochrones()) {

double[] attrValues = provider.getStatistics(isochrone, provAttrs);
isochrone.setAttributes(entry.getValue(), attrValues, provConfig.getAttribution());

}
}
} catch (Exception ex) {
LOGGER.error(ex);

throw new InternalServerException(IsochronesErrorCodes.UNKNOWN, "Unable to compute isochrone total_pop attribute.");
}
}
if (parameters.hasAttribute("reachfactor") || parameters.hasAttribute("area")) {
for (Isochrone isochrone : result.getIsochrones()) {
String units = parameters.getUnits();
String areaUnits = parameters.getAreaUnits();
if (areaUnits != null) units = areaUnits;
double area = isochrone.calcArea(units);
if (parameters.hasAttribute("area")) {
isochrone.setArea(area);
}
if (parameters.hasAttribute("reachfactor")) {
double reachfactor = isochrone.calcReachfactor(units);
// reach factor could be > 1, which would confuse people
reachfactor = (reachfactor > 1) ? 1 : reachfactor;
isochrone.setReachfactor(reachfactor);
}
}
}
}
return result;
}

public boolean equals(Object o) {
return o != null && o.getClass().equals(RoutingProfile.class) && this.hashCode() == o.hashCode();
}
Expand Down
Loading

0 comments on commit 81638e4

Please sign in to comment.