Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.graphhopper.routing.ev;

/**
* This class is just an example for showing how to use
* encoded values dynamically.
*/
public class DynamicData {
public static final String KEY = "dynamic_data";

public static BooleanEncodedValue create() {
return new SimpleBooleanEncodedValue(KEY, false);
}

private DynamicData() {
// hide implicit constructor
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.graphhopper.routing.ev;

public class PointData {
public static final String KEY = "point_data";

public static DecimalEncodedValue create() {
return new UnsignedDecimalEncodedValue(KEY, 31, 0.01, false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ private int[] pointIdsOutOfBounds(BBox bounds, Coordinate[] coords) {
return idsArray;
}

// TODO: refactor using org.heigit.ors.snapping.Snapper instead to match Snapping and loadExternalPointData
private void resolveLocations(String profileName, Coordinate[] coords, List<Snap> queryResults, double maxSearchRadius) {
for (Coordinate p : coords) {
LocationEntry ld = locationCache.get(p);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@

import com.graphhopper.config.CHProfile;
import com.graphhopper.routing.ev.*;
import com.graphhopper.routing.util.EncodingManager;
import com.graphhopper.storage.GraphHopperStorage;
import com.graphhopper.storage.IntsRef;
import com.graphhopper.storage.StorableProperties;
import com.graphhopper.storage.index.Snap;
import com.graphhopper.util.EdgeIteratorState;
import org.apache.log4j.Logger;
import org.heigit.ors.config.EngineProperties;
import org.heigit.ors.config.profile.ExecutionProperties;
Expand All @@ -26,17 +30,23 @@
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.pathprocessors.ORSPathProcessorFactory;
import org.heigit.ors.snapping.Snapper;
import org.heigit.ors.util.TimeUtility;
import org.json.simple.JSONObject;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.Function;

import static com.graphhopper.routing.util.EncodingManager.getKey;

/**
* This class generates {@link RoutingProfile} classes and is used by mostly all service classes e.g.
* <p>
Expand Down Expand Up @@ -75,6 +85,8 @@ public RoutingProfile(String profileName, ProfileProperties profile, EnginePrope
astarApproximation = execution.getMethods().getAstar().getApproximation();
if (execution.getMethods().getAstar().getEpsilon() != null)
astarEpsilon = execution.getMethods().getAstar().getEpsilon();

if (hasExternalData()) loadStaticExternalData(mGraphHopper);
}


Expand Down Expand Up @@ -144,6 +156,70 @@ public ORSGraphHopper initGraphHopper(RoutingProfileLoadContext loadCntx) throws
return gh;
}

private boolean hasExternalData() {
return true; // TODO: implement properly
}

private void loadStaticExternalData(ORSGraphHopper gh) throws IOException {
EncodingManager em = gh.getEncodingManager();
DecimalEncodedValue dev = em.getDecimalEncodedValue(getKey("pedestrian_ors", PointData.KEY));
if (dev == null) {
LOGGER.error("Could not create EncodedValue %s.".formatted(PointData.KEY));
return;
}

Path csvFile = Path.of("external_point_data.csv").toAbsolutePath(); // TODO: parametrize

try (BufferedReader csvBuffer = new BufferedReader(new FileReader(csvFile.toString()))) {
// Read header line
String row = csvBuffer.readLine();
String[] columnNames = Arrays.stream(row.split(",")).toArray(String[]::new);
// TODO: check that column names are lon, lat, value, maxSearchRadius

Snapper snapper = new Snapper(this, WeightingMethod.CUSTOM);

int numIgnored = 0;
int numNotSnapped = 0;
int numLoaded = 0;

while ((row = csvBuffer.readLine()) != null) {
String[] fields = row.split(",", 5);
if (fields.length != 4) { // ignore rows with too few or too many entires
numIgnored ++;
continue;
}

float lon = Float.parseFloat(fields[0].trim());
float lat = Float.parseFloat(fields[1].trim());
double value = Double.parseDouble(fields[2].trim());
double maxSearchRadius = Double.parseDouble(fields[3].trim());

Snap snap = snapper.snapToGraph(lon, lat);
if (!snap.isValid() || snap.getQueryDistance() > maxSearchRadius) {
numNotSnapped ++;
continue;
}

EdgeIteratorState closestEdge = snap.getClosestEdge();
IntsRef edgeFlags = closestEdge.getFlags();

double current = dev.getDecimal(false, edgeFlags);
if (current != dev.getMaxDecimal()) value += current;

dev.setDecimal(false, edgeFlags, value);
closestEdge.setFlags(edgeFlags);
numLoaded ++;
}
LOGGER.info("External data points: "
+ numLoaded + " loaded, "
+ numNotSnapped + " not snapped, "
+ numIgnored + " ignored.");
} catch (IOException openFileEx) {
LOGGER.error(openFileEx.getStackTrace());
throw openFileEx;
}
}

public boolean hasCHProfile(String profileName) {
boolean hasCHProfile = false;
for (CHProfile chProfile : getGraphhopper().getCHPreparationHandler().getCHProfiles()) {
Expand Down Expand Up @@ -193,6 +269,10 @@ public String name() {
return this.profileName;
}

public int profileType() {
return RoutingProfileType.getFromString(profileName);
}

public ProfileProperties getProfileProperties() {
return this.profileProperties;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
import com.graphhopper.routing.ev.DefaultEncodedValueFactory;
import com.graphhopper.routing.ev.EncodedValue;
import com.graphhopper.routing.ev.EncodedValueFactory;
import com.graphhopper.routing.ev.PointData;
import com.graphhopper.util.Helper;
import org.heigit.ors.routing.graphhopper.extensions.ev.DynamicData;
import com.graphhopper.routing.ev.DynamicData;

public class OrsEncodedValueFactory implements EncodedValueFactory {
DefaultEncodedValueFactory defaultEncodedValueFactory = new DefaultEncodedValueFactory();
Expand All @@ -19,17 +20,14 @@ public EncodedValue create(String encodedValueString) {
if (Helper.isEmpty(encodedValueString))
throw new IllegalArgumentException("No string provided to load EncodedValue");

final EncodedValue enc;
String name = encodedValueString.split("\\|")[0];
if (name.isEmpty())
throw new IllegalArgumentException("To load EncodedValue a name is required. " + encodedValueString);

if (DynamicData.KEY.equals(name)) {
enc = DynamicData.create();
} else {
// Fallback to GraphHopper's EncodedValues
enc = defaultEncodedValueFactory.create(encodedValueString);
}
return enc;
return switch (name) {
case DynamicData.KEY -> DynamicData.create();
case PointData.KEY -> PointData.create();
default -> defaultEncodedValueFactory.create(encodedValueString); // fall back to GraphHopper's EncodedValues
};
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,9 @@ public void createEncodedValues(List<EncodedValue> registerNewEncodedValue, Stri
registerNewEncodedValue.add(conditionalAccessEncoder);
}
footRouteEnc = getEnumEncodedValue(RouteNetwork.key("foot"), RouteNetwork.class);

// TODO: this should be available for all flag encoders
registerNewEncodedValue.add(new UnsignedDecimalEncodedValue(getKey(prefix, PointData.KEY), 31, 0.01, false));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import com.graphhopper.util.BitUtil;
import com.graphhopper.util.Helper;
import com.graphhopper.util.PMap;
import org.heigit.ors.routing.graphhopper.extensions.ev.DynamicData;
import com.graphhopper.routing.ev.DynamicData;

import java.util.Arrays;
import java.util.HashMap;
Expand Down
55 changes: 55 additions & 0 deletions ors-engine/src/main/java/org/heigit/ors/snapping/Snapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package org.heigit.ors.snapping;

import com.graphhopper.routing.ev.BooleanEncodedValue;
import com.graphhopper.routing.ev.Subnetwork;
import com.graphhopper.routing.util.DefaultSnapFilter;
import com.graphhopper.routing.util.EdgeFilter;
import com.graphhopper.routing.weighting.Weighting;
import com.graphhopper.storage.index.LocationIndex;
import com.graphhopper.storage.index.Snap;
import com.graphhopper.util.PMap;
import org.heigit.ors.matrix.ResolvedLocation;
import org.heigit.ors.routing.RoutingProfile;
import org.heigit.ors.routing.RoutingProfileType;
import org.heigit.ors.routing.WeightingMethod;
import org.heigit.ors.routing.graphhopper.extensions.ORSGraphHopper;
import org.heigit.ors.routing.graphhopper.extensions.ORSWeightingFactory;
import org.heigit.ors.util.ProfileTools;
import org.locationtech.jts.geom.Coordinate;

public class Snapper {
LocationIndex locationIndex;
EdgeFilter edgeFilter;
private int profileType;

public Snapper(RoutingProfile rp, int weightingMethod) {
profileType = rp.profileType();
this.locationIndex = rp.getGraphhopper().getLocationIndex();
this.edgeFilter = snappingEdgeFilter(rp.getGraphhopper(), weightingMethod);
}

public Snap snapToGraph(double lon, double lat) {
return locationIndex.findClosest(lat, lon, edgeFilter);
}

public ResolvedLocation resolveLocation(Coordinate coordinate) {
Snap snap = snapToGraph(coordinate.y, coordinate.x);
Coordinate snappedCoordinates = new Coordinate(snap.getSnappedPoint().getLon(), snap.getSnappedPoint().getLat());
return new ResolvedLocation(snappedCoordinates, snap.getClosestEdge().getName(), snap.getQueryDistance());
}

private EdgeFilter snappingEdgeFilter(ORSGraphHopper gh, int weightingMethod) {
String encoderName = RoutingProfileType.getEncoderName(profileType);
PMap hintsMap = new PMap();
ProfileTools.setWeightingMethod(hintsMap, weightingMethod, profileType, false);
ProfileTools.setWeighting(hintsMap, weightingMethod, profileType, false);
// TODO: which effectiveWeighting used at different places is correct?
// String effectiveWeighting = hintsMap.getString("weightingMethod", "");
String effectiveWeighting = WeightingMethod.getName(weightingMethod);
String localProfileName = ProfileTools.makeProfileName(encoderName, effectiveWeighting, false);
Weighting weighting = new ORSWeightingFactory(gh.getGraphHopperStorage(), gh.getEncodingManager())
.createWeighting(gh.getProfile(localProfileName), hintsMap, false);
BooleanEncodedValue profileSubnetwork = gh.getEncodingManager().getBooleanEncodedValue(Subnetwork.key(localProfileName));
return new DefaultSnapFilter(weighting, profileSubnetwork);
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,13 @@
package org.heigit.ors.snapping;

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.common.ServiceRequest;
import org.heigit.ors.matrix.MatrixSearchContext;
import org.heigit.ors.matrix.MatrixSearchContextBuilder;
import org.heigit.ors.matrix.ResolvedLocation;
import org.heigit.ors.routing.RoutingProfile;
import org.heigit.ors.routing.RoutingProfileType;
import org.heigit.ors.routing.WeightingMethod;
import org.heigit.ors.routing.graphhopper.extensions.ORSWeightingFactory;
import org.heigit.ors.util.ProfileTools;
import org.locationtech.jts.geom.Coordinate;

import java.util.stream.Stream;

public class SnappingRequest extends ServiceRequest {
private String profileName;
private final int profileType;
Expand Down Expand Up @@ -58,21 +50,12 @@ public int getMaximumLocations() {
}

public SnappingResult computeResult(RoutingProfile rp) throws Exception {
GraphHopper gh = rp.getGraphhopper();
String encoderName = RoutingProfileType.getEncoderName(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, getProfileType(), false);
ProfileTools.setWeighting(hintsMap, weightingMethod, getProfileType(), false);
String localProfileName = ProfileTools.makeProfileName(encoderName, hintsMap.getString("weighting", ""), false);
GraphHopperStorage ghStorage = gh.getGraphHopperStorage();
String graphDate = ghStorage.getProperties().get("datareader.import.date");
Snapper snapper = new Snapper(rp, WeightingMethod.RECOMMENDED);

ResolvedLocation[] resolvedLocations = Stream.of(getLocations()).map(snapper::resolveLocation).toArray(ResolvedLocation[]::new);

// 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(localProfileName), hintsMap, false);
MatrixSearchContext mtxSearchCntx = builder.create(ghStorage.getBaseGraph(), null, weighting, localProfileName, getLocations(), getLocations(), getMaximumSearchRadius());
return new SnappingResult(mtxSearchCntx.getSources().getLocations(), graphDate);
String graphDate = rp.getGraphhopper().getGraphHopperStorage().getProperties().get("datareader.import.date");
return new SnappingResult(resolvedLocations, graphDate);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import com.graphhopper.storage.IntsRef;
import com.graphhopper.util.EdgeIteratorState;
import org.heigit.ors.routing.graphhopper.extensions.edgefilters.BooleanEncodedValueEdgeFilter;
import org.heigit.ors.routing.graphhopper.extensions.ev.DynamicData;
import com.graphhopper.routing.ev.DynamicData;
import org.heigit.ors.util.ToyGraphCreationUtil;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
Expand Down