BorderNodeDistanceStorage.java

/*
 *  Licensed to GraphHopper GmbH under one or more contributor
 *  license agreements. See the NOTICE file distributed with this work for
 *  additional information regarding copyright ownership.
 *
 *  GraphHopper GmbH licenses this file to you under the Apache License,
 *  Version 2.0 (the "License"); you may not use this file except in
 *  compliance with the License. You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.heigit.ors.fastisochrones.storage;

import com.carrotsearch.hppc.DoubleArrayList;
import com.carrotsearch.hppc.IntArrayList;
import com.carrotsearch.hppc.IntIntHashMap;
import com.carrotsearch.hppc.IntLongHashMap;
import com.carrotsearch.hppc.cursors.IntIntCursor;
import com.carrotsearch.hppc.cursors.IntLongCursor;
import com.graphhopper.routing.weighting.Weighting;
import com.graphhopper.storage.DataAccess;
import com.graphhopper.storage.Directory;
import com.graphhopper.storage.Storable;
import org.heigit.ors.fastisochrones.partitioning.storage.IsochroneNodeStorage;
import org.heigit.ors.util.FileUtility;

import static org.heigit.ors.fastisochrones.storage.ByteConversion.*;

/**
 * Stores distances of bordernodes in a cell.
 *
 * @author Hendrik Leuschner
 */
public class BorderNodeDistanceStorage implements Storable<BorderNodeDistanceStorage> {
    private final DataAccess borderNodes;
    private final int byteCount;
    private int borderNodeIndexOffset;
    private final int nodeCount;
    private int borderNodeCount;
    private int necessaryCapacity = 0;
    private long borderNodePointer;
    private final IsochroneNodeStorage isochroneNodeStorage;
    private IntLongHashMap borderNodeToPointerMap;
    private final Weighting weighting;

    public BorderNodeDistanceStorage(Directory dir, Weighting weighting, IsochroneNodeStorage isochroneNodeStorage, int nodeCount) {
        final String name = FileUtility.weightingToFileName(weighting);
        this.isochroneNodeStorage = isochroneNodeStorage;
        borderNodes = dir.find("bordernodes_" + name);
        this.weighting = weighting;
        byteCount = 12; //adj bordernode id (int 4B) and distance (double 8B)
        this.nodeCount = nodeCount;
    }

    public boolean loadExisting() {
        if (borderNodes.loadExisting()) {
            borderNodeCount = borderNodes.getHeader(0);
            borderNodeIndexOffset = borderNodeCount * byteCount;
            borderNodePointer = borderNodeIndexOffset;
            borderNodeToPointerMap = new IntLongHashMap(borderNodeCount);
            fillBorderNodeToPointerMap();
            return true;
        }
        return false;
    }

    public void init() {
        borderNodes.create(1000);
        getNumBorderNodes();
        borderNodes.ensureCapacity((long) borderNodeCount * byteCount + (long) necessaryCapacity * byteCount + borderNodeCount * 4L);
        borderNodes.setHeader(0, borderNodeCount);
        borderNodeIndexOffset = borderNodeCount * byteCount;
        borderNodePointer = borderNodeIndexOffset;
        borderNodeToPointerMap = new IntLongHashMap();
    }

    private void getNumBorderNodes() {
        //Count number of border nodes and count number of border nodes per cell to calculate capacity later
        int count = 0;
        IntIntHashMap cellBNodes = new IntIntHashMap(isochroneNodeStorage.getCellIds().size());
        for (int node = 0; node < nodeCount; node++) {
            if (isochroneNodeStorage.getBorderness(node)) {
                count++;
                cellBNodes.putOrAdd(isochroneNodeStorage.getCellId(node), 1, 1);
            }
        }
        for (IntIntCursor cell : cellBNodes) {
            necessaryCapacity += cell.value * cell.value;
        }
        borderNodeCount = count;
    }

    public synchronized void storeBorderNodeDistanceSet(int nodeId, BorderNodeDistanceSet bnds) {
        if (bnds.getAdjBorderNodeDistances().length != bnds.getAdjBorderNodeIds().length)
            throw new IllegalArgumentException("Corrupted distance set");
        borderNodeToPointerMap.put(nodeId, borderNodePointer);

        for (int i = 0; i < bnds.getAdjBorderNodeIds().length; i++) {
            borderNodes.setInt(borderNodePointer, bnds.adjBorderNodeIds[i]);
            borderNodePointer += 4;
            borderNodes.setBytes(borderNodePointer, doubleToByteArray(bnds.adjBorderNodeDistances[i]), 8);
            borderNodePointer += 8;
        }
        //Add trailing -1
        borderNodes.setInt(borderNodePointer, -1);
        borderNodePointer += 4;
    }

    public BorderNodeDistanceSet getBorderNodeDistanceSet(int nodeId) {
        long pointer = borderNodeToPointerMap.get(nodeId);
        int currentNode = borderNodes.getInt(pointer);
        IntArrayList ids = new IntArrayList(5);
        DoubleArrayList distances = new DoubleArrayList(5);
        while (currentNode != -1) {
            byte[] buffer = new byte[8];
            ids.add(currentNode);
            pointer += 4;
            borderNodes.getBytes(pointer, buffer, 8);
            distances.add(byteArrayToDouble(buffer));
            pointer += 8;
            currentNode = borderNodes.getInt(pointer);
        }
        return new BorderNodeDistanceSet(ids.toArray(), distances.toArray());
    }

    public void storeBorderNodeToPointerMap() {
        long listPointer = 0;
        long nodePointer;
        //Store the number of contours (= num cells + num supercells)
        for (IntLongCursor borderNode : borderNodeToPointerMap) {
            borderNodes.setInt(listPointer, borderNode.key);
            listPointer = listPointer + 4;
            nodePointer = borderNode.value;
            borderNodes.setBytes(listPointer, longToByteArray(nodePointer), 8);
            listPointer = listPointer + 8;
        }
    }

    private void fillBorderNodeToPointerMap() {
        byte[] buffer = new byte[8];
        long listPointer = 0;
        for (int i = 0; i < borderNodeCount; i++) {
            int borderNode = borderNodes.getInt(listPointer);
            listPointer = listPointer + 4;

            borderNodes.getBytes(listPointer, buffer, 8);
            long nodePointer = byteArrayToLong(buffer);
            listPointer = listPointer + (long) 8;
            borderNodeToPointerMap.put(borderNode, nodePointer);
        }
    }

    public BorderNodeDistanceStorage create(long byteCount) {
        throw new IllegalStateException("Do not call BorderNodeDistanceStorage.create directly");
    }

    public void flush() {
        borderNodes.flush();
    }

    @Override
    public void close() {
        borderNodes.close();
    }

    @Override
    public boolean isClosed() {
        return borderNodes.isClosed();
    }

    public long getCapacity() {
        return borderNodes.getCapacity();
    }

    public Weighting getWeighting() {
        return weighting;
    }

    public boolean hasWeighting(Weighting weighting) {
        return getWeighting().getName() != null
                && getWeighting().getName().equals(weighting.getName())
                && getWeighting().getFlagEncoder().toString() != null
                && getWeighting().getFlagEncoder().toString().equals(weighting.getFlagEncoder().toString());
    }
}