IsochroneNodeStorage.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.partitioning.storage;

import com.carrotsearch.hppc.IntHashSet;
import com.carrotsearch.hppc.IntSet;
import com.graphhopper.storage.DataAccess;
import com.graphhopper.storage.Directory;
import com.graphhopper.storage.Storable;

import static org.heigit.ors.fastisochrones.storage.ByteConversion.byteArrayToInteger;
import static org.heigit.ors.fastisochrones.storage.ByteConversion.intToByteArray;

/**
 * Storage that maps nodeIds to their respective cells and borderness.
 * <p>
 *
 * @author Hendrik Leuschner
 */
public class IsochroneNodeStorage implements Storable<IsochroneNodeStorage> {
    private final DataAccess isochroneNodes;
    private final int cellBytes;
    private final int nodeCount;
    private final IntSet cellIdsSet = new IntHashSet();

    public IsochroneNodeStorage(int nodeCount, Directory dir) {
        isochroneNodes = dir.find("isochronenodes");
        this.nodeCount = nodeCount;
        // 5 bytes per node for its cell id.
        // 1 byte for isBordernode,
        // Maximum cell id = Integer.MAX_VALUE
        // Borderness of nodes is stored in a block after cellIds block. As it is one bit per node, it is condensed into blocks of 8 node information per byte.
        this.cellBytes = 5;
    }

    public boolean loadExisting() {
        if (isochroneNodes.loadExisting()) {
            for (int node = 0; node < nodeCount; node++)
                cellIdsSet.add(getCellId(node));
            return true;
        }
        return false;
    }

    public void setBorderness(boolean[] borderness) {
        if (nodeCount != borderness.length)
            throw new IllegalStateException("Nodecount and borderness array do not match");
        isochroneNodes.ensureCapacity((long) cellBytes * nodeCount);
        for (int node = 0; node < borderness.length; node++) {
            if (borderness[node])
                isochroneNodes.setBytes((long) node * cellBytes, new byte[]{(byte) 1}, 1);
            else
                isochroneNodes.setBytes((long) node * cellBytes, new byte[]{(byte) 0}, 1);
        }
    }

    public boolean getBorderness(int node) {
        byte[] buffer = new byte[1];
        isochroneNodes.getBytes((long) node * cellBytes, buffer, 1);
        return buffer[0] == 1;
    }

    public int getCellId(int node) {
        byte[] buffer = new byte[4];
        isochroneNodes.getBytes((long) node * cellBytes + 1, buffer, 4);
        return byteArrayToInteger(buffer);
    }

    public IntSet getCellIds() {
        return cellIdsSet;
    }

    public void setCellIds(int[] cellIds) {
        if (nodeCount != cellIds.length)
            throw new IllegalStateException("Nodecount and cellIds array do not match");
        isochroneNodes.create(1000);
        isochroneNodes.ensureCapacity((long) cellBytes * nodeCount);
        for (int node = 0; node < cellIds.length; node++) {
            int cellId = cellIds[node];
            cellIdsSet.add(cellId);
            isochroneNodes.setBytes((long) node * cellBytes + 1, intToByteArray(cellId), 4);
        }
    }

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

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

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

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

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