BordersGraphStorage.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.*;
/**
* Graph storage class for the Border Restriction routing
*/
public class BordersGraphStorage implements GraphExtension {
public enum Property {TYPE, START, END}
/* pointer for no entry */
protected static final int NO_ENTRY = -1;
private static final int EF_BORDER = 0; // byte location of border type
private static final int EF_START = 2; // byte location of the start country id
private static final int EF_END = 4; // byte location of the end country id
// border types
public static final short NO_BORDER = 0;
public static final short OPEN_BORDER = 2;
public static final short CONTROLLED_BORDER = 1;
private DataAccess orsEdges;
private int edgeEntryBytes;
private int edgesCount; // number of edges with custom values
public BordersGraphStorage() {
int edgeEntryIndex = 0;
edgeEntryBytes = edgeEntryIndex + 6; // item uses 3 short values which are 2 bytes length each
edgesCount = 0;
}
/**
* Set values to the edge based on the border type and countries<br/><br/>
* <p>
* This method takes the internal ID of the edge and adds the information obtained from the Borders CSV file to it
* so that the values can be taken into account when generating a route.
*
* @param edgeId Internal ID of the graph edge
* @param borderType Level of border crossing (0 - No border, 1 - controlled border, 2 - open border=
* @param start ID of the country that the edge starts in
* @param end ID of the country that the edge ends in
*/
public void setEdgeValue(int edgeId, short borderType, short start, short end) {
edgesCount++;
ensureEdgesIndex(edgeId);
// add entry
long edgePointer = (long) edgeId * edgeEntryBytes;
orsEdges.setShort(edgePointer + EF_BORDER, borderType);
orsEdges.setShort(edgePointer + EF_START, start);
orsEdges.setShort(edgePointer + EF_END, end);
}
private void ensureEdgesIndex(int edgeId) {
orsEdges.ensureCapacity(((long) edgeId + 1) * edgeEntryBytes);
}
/**
* Get the specified custom value of the edge that was assigned to it in the setValueEdge method<br/><br/>
* <p>
* The method takes an identifier to the edge and then gets the requested value for the edge from the storage
*
* @param edgeId Internal ID of the edge to get values for
* @param prop The property of the edge to get (TYPE - border type (0,1,2), START - the ID of the country
* the edge starts in, END - the ID of the country the edge ends in.
* @return The value of the requested property
*/
public short getEdgeValue(int edgeId, Property prop) {
long edgePointer = (long) edgeId * edgeEntryBytes;
short border = orsEdges.getShort(edgePointer + EF_BORDER);
short start = orsEdges.getShort(edgePointer + EF_START);
short end = orsEdges.getShort(edgePointer + EF_END);
return switch (prop) {
case TYPE -> border;
case START -> start;
case END -> end;
default -> 0;
};
}
/**
* initializes the extended storage by giving the base graph
*
* @param graph
* @param dir
*/
@Override
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_borders");
}
/**
* initializes the extended storage to be empty - required for testing purposes as the ext_storage aren't created
* at the time tests are run
*/
public void init() {
if (edgesCount > 0)
throw new AssertionError("The ORS storage must be initialized only once.");
Directory d = new RAMDirectory();
this.orsEdges = d.find("");
}
/**
* @return true if successfully loaded from persistent storage.
*/
@Override
public boolean loadExisting() {
if (!orsEdges.loadExisting())
throw new IllegalStateException("Unable to load storage 'ext_borders'. corrupt file or directory?");
edgeEntryBytes = orsEdges.getHeader(0);
edgesCount = orsEdges.getHeader(4);
return true;
}
/**
* Creates the underlying storage. First operation if it cannot be loaded.
*
* @param initBytes
*/
@Override
public BordersGraphStorage create(long initBytes) {
orsEdges.create(initBytes * edgeEntryBytes);
return this;
}
/**
* This method makes sure that the underlying data is written to the storage. Keep in mind that
* a disc normally has an IO cache so that flush() is (less) probably not save against power
* loses.
*/
@Override
public void flush() {
orsEdges.setHeader(0, edgeEntryBytes);
orsEdges.setHeader(4, edgesCount);
orsEdges.flush();
}
/**
* This method makes sure that the underlying used resources are released. WARNING: it does NOT
* flush on close!
*/
@Override
public void close() {
orsEdges.close();
}
@Override
public boolean isClosed() {
return false;
}
/**
* @return the allocated storage size in bytes
*/
@Override
public long getCapacity() {
return orsEdges.getCapacity();
}
}