WheelchairAttributesGraphStorage.java
/* This file is part of Openrouteservice.
*
* Openrouteservice is free software; you can redistribute it and/or modify it under the terms of the
* GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License along with this library;
* if not, see <https://www.gnu.org/licenses/>.
*/
package org.heigit.ors.routing.graphhopper.extensions.storages;
import com.graphhopper.storage.DataAccess;
import com.graphhopper.storage.Directory;
import com.graphhopper.storage.Graph;
import com.graphhopper.storage.GraphExtension;
import org.heigit.ors.routing.graphhopper.extensions.WheelchairAttributes;
import org.heigit.ors.routing.graphhopper.extensions.flagencoders.EncodedValueOld;
public class WheelchairAttributesGraphStorage implements GraphExtension {
protected static final int WIDTH_MAX_VALUE = 300;
protected static final int KERB_MAX_VALUE = 15;
protected static final int INCLINE_MAX_VALUE = 30;
protected static final int TRACK_TYPE_MAX_VALUE = 5;
protected static final int SMOOTHNESS_MAX_VALUE = 8;
protected static final int SURFACE_MAX_VALUE = 30;
/* pointer for no entry */
protected final int efWheelchairAttributes;
protected DataAccess orsEdges;
protected int edgeEntryIndex = 0;
protected int edgeEntryBytes;
protected int edgesCount; // number of edges with custom values
private final byte[] buffer;
// bit encoders
private final EncodedValueOld surfaceEncoder;
private final EncodedValueOld smoothnessEncoder;
private final EncodedValueOld trackTypeEncoder;
private final EncodedValueOld sideFlagEncoder;
private final EncodedValueOld kerbHeightEncoder;
private final EncodedValueOld hasKerbHeightEncoder;
private final EncodedValueOld inclineEncoder;
private final EncodedValueOld hasInclineEncoder;
private final EncodedValueOld widthEncoder;
private final EncodedValueOld surfaceQualityKnownEncoder;
private final EncodedValueOld pedestrianisedEncoder;
public static final int BYTE_COUNT = 5;
public WheelchairAttributesGraphStorage() {
buffer = new byte[BYTE_COUNT];
efWheelchairAttributes = 0;
edgeEntryBytes = edgeEntryIndex + BYTE_COUNT;
edgesCount = 0;
int shift = 1;
surfaceEncoder = new EncodedValueOld("surface", shift, 5, 1, 0, SURFACE_MAX_VALUE);
shift += surfaceEncoder.getBits();
smoothnessEncoder = new EncodedValueOld("smoothness", shift, 4, 1, 0, SMOOTHNESS_MAX_VALUE);
shift += smoothnessEncoder.getBits();
trackTypeEncoder = new EncodedValueOld("tracktype", shift, 3, 1, 0, TRACK_TYPE_MAX_VALUE);
shift += trackTypeEncoder.getBits();
inclineEncoder = new EncodedValueOld("incline", shift, 5, 1, 0, INCLINE_MAX_VALUE);
shift += inclineEncoder.getBits();
kerbHeightEncoder = new EncodedValueOld("kerbHeight", shift, 4, 1, 0, KERB_MAX_VALUE);
shift += kerbHeightEncoder.getBits();
widthEncoder = new EncodedValueOld("width", shift, 5, 10, 0, WIDTH_MAX_VALUE);
shift += widthEncoder.getBits();
sideFlagEncoder = new EncodedValueOld("side", shift, 2, 1, 0, 2);
shift += sideFlagEncoder.getBits();
hasKerbHeightEncoder = new EncodedValueOld("hasKerbHeight", shift, 1, 1, 0, 1);
shift += 1;
hasInclineEncoder = new EncodedValueOld("hasIncline", shift, 1, 1, 0, 1);
shift += 1;
surfaceQualityKnownEncoder = new EncodedValueOld("surfaceQualityKnown", shift, 1, 1, 0, 1);
shift += 1;
pedestrianisedEncoder = new EncodedValueOld("pedestrianised", shift, 1, 1, 0, 1);
}
public void init(Graph graph, Directory dir) {
if (edgesCount > 0)
throw new AssertionError("The ORS storage must be initialized only once.");
this.orsEdges = dir.find("ext_wheelchair");
}
public WheelchairAttributesGraphStorage create(long initBytes) {
orsEdges.create(initBytes * edgeEntryBytes);
return this;
}
public void flush() {
orsEdges.setHeader(0, edgeEntryBytes);
orsEdges.setHeader(4, edgesCount);
orsEdges.flush();
}
public void close() {
orsEdges.close();
}
@Override
public long getCapacity() {
return orsEdges.getCapacity();
}
public int entries() {
return edgesCount;
}
public boolean loadExisting() {
if (!orsEdges.loadExisting())
throw new IllegalStateException("Unable to load storage 'ext_wheelchair'. corrupt file or directory? ");
edgeEntryBytes = orsEdges.getHeader(0);
edgesCount = orsEdges.getHeader(4);
return true;
}
void ensureEdgesIndex(int edgeIndex) {
orsEdges.ensureCapacity(((long) edgeIndex + 1) * edgeEntryBytes);
}
public void setEdgeValues(int edgeId, WheelchairAttributes attrs) {
edgesCount++;
ensureEdgesIndex(edgeId);
long edgePointer = (long) edgeId * edgeEntryBytes;
encodeAttributes(attrs, buffer);
orsEdges.setBytes(edgePointer + efWheelchairAttributes, buffer, BYTE_COUNT);
}
private void encodeAttributes(WheelchairAttributes attrs, byte[] buffer) {
/*
* | flag | surface | smoothness | tracktype | incline | kerbHeight | width | side | hasKerbHeight | hasIncline | surfaceQualityKnown | pedestrianised
* lsb-> | 1 bit | 5 bits | 4 bits | 3 bits | 5 bits | 4 bits | 5 bits | 2 bit | 1 bit | 1 bit | 1 bit | 1 bit | 33 bits in total which can fit into 5 bytes
*
*
*/
if (attrs.hasValues()) {
long encodedValue = 0;
// set first bit to 1 to mark that we have wheelchair specific attributes for this edge
encodedValue |= (1L);
if (attrs.getSurfaceType() > 0)
encodedValue = surfaceEncoder.setValue(encodedValue, attrs.getSurfaceType());
if (attrs.getSmoothnessType() > 0)
encodedValue = smoothnessEncoder.setValue(encodedValue, attrs.getSmoothnessType());
if (attrs.getTrackType() > 0)
encodedValue = trackTypeEncoder.setValue(encodedValue, attrs.getTrackType());
if (attrs.getIncline() > -1) {
encodedValue = hasInclineEncoder.setValue(encodedValue, 1);
encodedValue = inclineEncoder.setValue(encodedValue, attrs.getIncline());
}
if (attrs.getSlopedKerbHeight() > 0.0) {
encodedValue = hasKerbHeightEncoder.setValue(encodedValue, 1);
encodedValue = kerbHeightEncoder.setValue(encodedValue, attrs.getSlopedKerbHeight());
}
if (attrs.getWidth() > 0.0)
encodedValue = widthEncoder.setValue(encodedValue, attrs.getWidth());
switch (attrs.getSide()) {
case LEFT -> encodedValue = sideFlagEncoder.setValue(encodedValue, 1);
case RIGHT -> encodedValue = sideFlagEncoder.setValue(encodedValue, 2);
default -> {
}
}
if (attrs.isSurfaceQualityKnown()) {
encodedValue = surfaceQualityKnownEncoder.setValue(encodedValue, 1);
}
if (attrs.isSuitable()) {
encodedValue = pedestrianisedEncoder.setValue(encodedValue, 1);
}
buffer[4] = (byte) ((encodedValue >> 32) & 0xFF);
buffer[3] = (byte) ((encodedValue >> 24) & 0xFF);
buffer[2] = (byte) ((encodedValue >> 16) & 0xFF);
buffer[1] = (byte) ((encodedValue >> 8) & 0xFF);
buffer[0] = (byte) ((encodedValue) & 0xFF);
} else {
buffer[0] = 0;
buffer[1] = 0;
buffer[2] = 0;
buffer[3] = 0;
buffer[4] = 0;
}
}
private void decodeAttributes(WheelchairAttributes attrs, byte[] buffer) {
attrs.reset();
if (buffer[0] == 0)
return;
long encodedValue = (long) buffer[0] & 0xFF;
encodedValue |= (long) (buffer[1] & 0xFF) << 8;
encodedValue |= (long) (buffer[2] & 0xFF) << 16;
encodedValue |= (long) (buffer[3] & 0xFF) << 24;
encodedValue |= (long) (buffer[4] & 0xFF) << 32;
if ((1 & encodedValue) != 0) {
long iValue = surfaceEncoder.getValue(encodedValue);
if (iValue != 0)
attrs.setSurfaceType((int) iValue);
iValue = smoothnessEncoder.getValue(encodedValue);
if (iValue != 0)
attrs.setSmoothnessType((int) iValue);
iValue = trackTypeEncoder.getValue(encodedValue);
if (iValue != 0)
attrs.setTrackType((int) iValue);
long hasIncline = hasInclineEncoder.getValue(encodedValue);
if (hasIncline > 0) {
iValue = inclineEncoder.getValue(encodedValue);
attrs.setIncline((int) (iValue));
}
long hasKerbHeight = hasKerbHeightEncoder.getValue(encodedValue);
if (hasKerbHeight > 0) {
iValue = kerbHeightEncoder.getValue(encodedValue);
attrs.setSlopedKerbHeight((int) (iValue));
}
iValue = widthEncoder.getValue(encodedValue);
if (iValue != 0)
attrs.setWidth((int) (iValue));
iValue = sideFlagEncoder.getValue(encodedValue);
switch ((int) iValue) {
case 1 -> attrs.setSide(WheelchairAttributes.Side.LEFT);
case 2 -> attrs.setSide(WheelchairAttributes.Side.RIGHT);
default -> attrs.setSide(WheelchairAttributes.Side.UNKNOWN);
}
iValue = surfaceQualityKnownEncoder.getValue(encodedValue);
attrs.setSurfaceQualityKnown((iValue != 0));
iValue = pedestrianisedEncoder.getValue(encodedValue);
attrs.setSuitable((iValue != 0));
}
}
public void getEdgeValues(int edgeId, WheelchairAttributes attrs, byte[] buffer) {
long edgePointer = (long) edgeId * (long) edgeEntryBytes;
orsEdges.getBytes(edgePointer + efWheelchairAttributes, buffer, BYTE_COUNT);
decodeAttributes(attrs, buffer);
}
public boolean isRequireNodeField() {
return false;
}
public boolean isRequireEdgeField() {
// we require the additional field in the graph to point to the first
// entry in the node table
return true;
}
public int getDefaultNodeFieldValue() {
throw new UnsupportedOperationException("Not supported by this storage");
}
public int getDefaultEdgeFieldValue() {
return -1;
}
@Override
public boolean isClosed() {
return false;
}
}