GraphHopper.com | Forum | GitHub | Maps | Blog

How to store an array of speeds in flag encoders?


#1

Hello! I need to store an array of speeds at some edge for a given flag encoder. Each item in this array indicate speed for a week day:
0 - Monday - 5km/h
1 - Tuesday - 10km/h
2 - Wednesday - 12km/h
etc…
I need this because the traffic jam in our city is not the same everyday, it depends only on weekday. I have seen @karussell example:


Looks fantastic! But it stores only one speed.
I have an idea to extend CarFlagEncoder by seven other classes:
MondayCarFlagEncoder, TuesdayCarFlagEncoder, Wed… etc and store speed for each weekday in the appropriate flag encoder.
But the solution looks weird. Do you have any idea? Or some better solution?
Thank you!


#2

I have a new idea:
What if I save EncodedValues for each day? Like it was done in MotorcycleFlagEncoder:
mondayWayEncoder = new EncodedValue(“Monday”);
tuesdayWayEncoder = new EncodedValue(“Tuesday”)


#3

You could do it this way but this will probably not scale for more values. For some internal projects we use a SpeedArray class that stores the speeds in a DataAccess object and the set+get methods look like

    public double getSpeed(int edgeId, boolean reverse, int timeSliceIndex) {
        if (edgeId > maxEdgeId)
            throw new IllegalStateException("too big edgeId " + edgeId + ", maxEdgeId: " + maxEdgeId);

        if (timeSliceIndex >= numSpeedValues)
            throw new IllegalArgumentException("Cannot access time slice " + timeSliceIndex + ". Maximum time slices of " + numSpeedValues + " allowed.");
        int offset = reverse ? 1 : 0;
        return speeds.getInt(4L * (2L * edgeId * numSpeedValues + 2 * timeSliceIndex + offset)) / factor;
    }

    public void setSpeed(int edgeId, boolean reverse, int timeSliceIndex, double value) {
        if (timeSliceIndex >= numSpeedValues)
            throw new IllegalArgumentException("Cannot access time slice " + timeSliceIndex + ". Maximum time slices of " + numSpeedValues + " allowed.");
        ensureCapacity(edgeId);
        int offset = reverse ? 1 : 0;
        speeds.setInt(4L * (2L * edgeId * numSpeedValues + 2 * timeSliceIndex + offset), (int) Math.round(value * factor));
    }

This is easy for speed values that are independent of the direction but is a bit more tricky if you need direction dependent values. For this case we do inside the encoder:

boolean reverse = directedBit.getValue(edge.getFlags()) == 0;

and the directedBit needs this handling in the encoder:

    @Override
    public long reverseFlags(long flags) {
        // swap access
        flags = super.reverseFlags(flags);

        // swap speeds
        long val = directedBit.getValue(flags);
        return directedBit.setValue(flags, val == 0 ? 1 : 0);
    }

Another edge case are virtual edges (edges at query locations) those currently need ugly special handling:

    if (edgeId > speedArray.getMaxEdgeId()) {
         EdgeIteratorState edgeState = edge.detach(false);
         if (edgeState instanceof VirtualEdgeIteratorState)
             edgeId = GHUtility.getEdgeFromEdgeKey(((VirtualEdgeIteratorState) edgeState).getOriginalTraversalKey());
    }

Of course this is all a bit too complicated and so we work on a much simpler API.


#4

Thank you very much! This looks easier than 7 encoded values. I will try the solution with DataAccess. As I complete traffic jams with multiple speeds, I am going to share source code in github with graphhopper’s amazing community.