All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.graphhopper.storage.index.LineIntIndex Maven / Gradle / Ivy

Go to download

GraphHopper is a fast and memory efficient Java road routing engine working seamlessly with OpenStreetMap data.

There is a newer version: 10.0
Show newest version
/*
 *  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 com.graphhopper.storage.index;

import com.carrotsearch.hppc.IntArrayList;
import com.carrotsearch.hppc.IntHashSet;
import com.graphhopper.geohash.SpatialKeyAlgo;
import com.graphhopper.storage.DAType;
import com.graphhopper.storage.DataAccess;
import com.graphhopper.storage.Directory;
import com.graphhopper.util.Constants;
import com.graphhopper.util.GHUtility;
import com.graphhopper.util.Helper;
import com.graphhopper.util.shapes.BBox;

import java.util.function.IntConsumer;

public class LineIntIndex {
    // do not start with 0 as a positive value means leaf and a negative means "entry with subentries"
    static final int START_POINTER = 1;

    final DataAccess dataAccess;
    private final BBox bounds;
    private int minResolutionInMeter = 300;
    private int size;
    private int leafs;
    private int checksum;
    private IndexStructureInfo indexStructureInfo;
    private int[] entries;
    private byte[] shifts;
    private boolean initialized = false;
    private SpatialKeyAlgo keyAlgo;

    public LineIntIndex(BBox bBox, Directory dir, String name) {
        this(bBox, dir, name, dir.getDefaultType(name, true));
    }

    public LineIntIndex(BBox bBox, Directory dir, String name, DAType daType) {
        this.bounds = bBox;
        this.dataAccess = dir.create(name, daType);
    }

    public boolean loadExisting() {
        if (initialized)
            throw new IllegalStateException("Call loadExisting only once");

        if (!dataAccess.loadExisting())
            return false;

        GHUtility.checkDAVersion("location_index", Constants.VERSION_LOCATION_IDX, dataAccess.getHeader(0));
        checksum = dataAccess.getHeader(1 * 4);
        minResolutionInMeter = dataAccess.getHeader(2 * 4);
        indexStructureInfo = IndexStructureInfo.create(bounds, minResolutionInMeter);
        keyAlgo = indexStructureInfo.getKeyAlgo();
        entries = indexStructureInfo.getEntries();
        shifts = indexStructureInfo.getShifts();
        initialized = true;
        return true;
    }

    public void store(InMemConstructionIndex inMem) {
        indexStructureInfo = IndexStructureInfo.create(bounds, minResolutionInMeter);
        keyAlgo = indexStructureInfo.getKeyAlgo();
        entries = indexStructureInfo.getEntries();
        shifts = indexStructureInfo.getShifts();
        dataAccess.create(64 * 1024);
        try {
            store(inMem.root, START_POINTER);
        } catch (Exception ex) {
            throw new IllegalStateException("Problem while storing location index. " + Helper.getMemInfo(), ex);
        }
        initialized = true;
    }

    private int store(InMemConstructionIndex.InMemEntry entry, int intPointer) {
        long pointer = (long) intPointer * 4;
        if (entry.isLeaf()) {
            InMemConstructionIndex.InMemLeafEntry leaf = ((InMemConstructionIndex.InMemLeafEntry) entry);
            IntArrayList entries = leaf.getResults();
            int len = entries.size();
            if (len == 0) {
                return intPointer;
            }
            size += len;
            intPointer++;
            leafs++;
            dataAccess.ensureCapacity((long) (intPointer + len + 1) * 4);
            if (len == 1) {
                // less disc space for single entries
                dataAccess.setInt(pointer, -entries.get(0) - 1);
            } else {
                for (int index = 0; index < len; index++, intPointer++) {
                    dataAccess.setInt((long) intPointer * 4, entries.get(index));
                }
                dataAccess.setInt(pointer, intPointer);
            }
        } else {
            InMemConstructionIndex.InMemTreeEntry treeEntry = ((InMemConstructionIndex.InMemTreeEntry) entry);
            int len = treeEntry.subEntries.length;
            intPointer += len;
            for (int subCounter = 0; subCounter < len; subCounter++, pointer += 4) {
                InMemConstructionIndex.InMemEntry subEntry = treeEntry.subEntries[subCounter];
                if (subEntry == null) {
                    continue;
                }
                dataAccess.ensureCapacity((long) (intPointer + 1) * 4);
                int prevIntPointer = intPointer;
                intPointer = store(subEntry, prevIntPointer);
                if (intPointer == prevIntPointer) {
                    dataAccess.setInt(pointer, 0);
                } else {
                    dataAccess.setInt(pointer, prevIntPointer);
                }
            }
        }
        return intPointer;
    }

    private void fillIDs(long keyPart, IntConsumer consumer) {
        int intPointer = START_POINTER;
        for (int depth = 0; depth < entries.length; depth++) {
            int offset = (int) (keyPart >>> (64 - shifts[depth]));
            int nextIntPointer = dataAccess.getInt((long) (intPointer + offset) * 4);
            if (nextIntPointer <= 0) {
                // empty cell
                return;
            }
            keyPart = keyPart << shifts[depth];
            intPointer = nextIntPointer;
        }
        int data = dataAccess.getInt((long) intPointer * 4);
        if (data < 0) {
            // single data entries (less disc space)
            int edgeId = -(data + 1);
            consumer.accept(edgeId);
        } else {
            // "data" is index of last data item
            for (int leafIndex = intPointer + 1; leafIndex < data; leafIndex++) {
                int edgeId = dataAccess.getInt((long) leafIndex * 4);
                consumer.accept(edgeId);
            }
        }
    }

    public void query(BBox queryShape, final LocationIndex.Visitor function) {
        query(LocationIndex.createBBoxTileFilter(queryShape), function);
    }

    public void query(LocationIndex.TileFilter tileFilter, final LocationIndex.Visitor function) {
        final IntHashSet set = new IntHashSet();
        query(START_POINTER, tileFilter,
                bounds.minLat, bounds.minLon, bounds.maxLat - bounds.minLat, bounds.maxLon - bounds.minLon,
                new LocationIndex.Visitor() {
                    @Override
                    public boolean isTileInfo() {
                        return function.isTileInfo();
                    }

                    @Override
                    public void onTile(BBox bbox, int width) {
                        function.onTile(bbox, width);
                    }

                    @Override
                    public void onEdge(int edgeId) {
                        if (set.add(edgeId))
                            function.onEdge(edgeId);
                    }
                }, 0);
    }

    private void query(int intPointer, LocationIndex.TileFilter tileFilter,
                       double minLat, double minLon,
                       double deltaLatPerDepth, double deltaLonPerDepth,
                       LocationIndex.Visitor function, int depth) {
        long pointer = (long) intPointer * 4;
        if (depth == entries.length) {
            int nextIntPointer = dataAccess.getInt(pointer);
            if (nextIntPointer < 0) {
                // single data entries (less disc space)
                function.onEdge(-(nextIntPointer + 1));
            } else {
                long maxPointer = (long) nextIntPointer * 4;
                // loop through every leaf entry => nextIntPointer is maxPointer
                for (long leafPointer = pointer + 4; leafPointer < maxPointer; leafPointer += 4) {
                    // we could read the whole info at once via getBytes instead of getInt
                    function.onEdge(dataAccess.getInt(leafPointer));
                }
            }
            return;
        }
        int max = (1 << shifts[depth]);
        int factor = max == 4 ? 2 : 4;
        deltaLonPerDepth /= factor;
        deltaLatPerDepth /= factor;
        for (int cellIndex = 0; cellIndex < max; cellIndex++) {
            int nextIntPointer = dataAccess.getInt(pointer + cellIndex * 4);
            if (nextIntPointer <= 0)
                continue;
            int[] pixelXY = keyAlgo.decode(cellIndex);
            double tmpMinLon = minLon + deltaLonPerDepth * pixelXY[0];
            double tmpMinLat = minLat + deltaLatPerDepth * pixelXY[1];

            BBox bbox = (tileFilter != null || function.isTileInfo()) ? new BBox(tmpMinLon, tmpMinLon + deltaLonPerDepth, tmpMinLat, tmpMinLat + deltaLatPerDepth) : null;
            if (function.isTileInfo())
                function.onTile(bbox, depth);
            if (tileFilter == null || tileFilter.acceptAll(bbox)) {
                // fill without a restriction!
                query(nextIntPointer, null, tmpMinLat, tmpMinLon, deltaLatPerDepth, deltaLonPerDepth, function, depth + 1);
            } else if (tileFilter.acceptPartially(bbox)) {
                query(nextIntPointer, tileFilter, tmpMinLat, tmpMinLon, deltaLatPerDepth, deltaLonPerDepth, function, depth + 1);
            }
        }
    }

    /**
     * This method collects edge ids from the neighborhood of a point and puts them into foundEntries.
     * 

* If it is called with iteration = 0, it just looks in the tile the query point is in. * If it is called with iteration = 0,1,2,.., it will look in additional tiles further and further * from the start tile. (In a square that grows by one pixel in all four directions per iteration). *

* See discussion at issue #221. *

*/ public void findEdgeIdsInNeighborhood(double queryLat, double queryLon, int iteration, IntConsumer foundEntries) { int x = keyAlgo.x(queryLon); int y = keyAlgo.y(queryLat); for (int yreg = -iteration; yreg <= iteration; yreg++) { int subqueryY = y + yreg; int subqueryXA = x - iteration; int subqueryXB = x + iteration; if (subqueryXA >= 0 && subqueryY >= 0 && subqueryXA < indexStructureInfo.getParts() && subqueryY < indexStructureInfo.getParts()) { long keyPart = keyAlgo.encode(subqueryXA, subqueryY) << (64 - keyAlgo.getBits()); fillIDs(keyPart, foundEntries); } if (iteration > 0 && subqueryXB >= 0 && subqueryY >= 0 && subqueryXB < indexStructureInfo.getParts() && subqueryY < indexStructureInfo.getParts()) { long keyPart = keyAlgo.encode(subqueryXB, subqueryY) << (64 - keyAlgo.getBits()); fillIDs(keyPart, foundEntries); } } for (int xreg = -iteration + 1; xreg <= iteration - 1; xreg++) { int subqueryX = x + xreg; int subqueryYA = y - iteration; int subqueryYB = y + iteration; if (subqueryX >= 0 && subqueryYA >= 0 && subqueryX < indexStructureInfo.getParts() && subqueryYA < indexStructureInfo.getParts()) { long keyPart = keyAlgo.encode(subqueryX, subqueryYA) << (64 - keyAlgo.getBits()); fillIDs(keyPart, foundEntries); } if (subqueryX >= 0 && subqueryYB >= 0 && subqueryX < indexStructureInfo.getParts() && subqueryYB < indexStructureInfo.getParts()) { long keyPart = keyAlgo.encode(subqueryX, subqueryYB) << (64 - keyAlgo.getBits()); fillIDs(keyPart, foundEntries); } } } public int getChecksum() { return checksum; } public int getMinResolutionInMeter() { return minResolutionInMeter; } public void setMinResolutionInMeter(int minResolutionInMeter) { this.minResolutionInMeter = minResolutionInMeter; } public void flush() { dataAccess.setHeader(0, Constants.VERSION_LOCATION_IDX); dataAccess.setHeader(1 * 4, checksum); dataAccess.setHeader(2 * 4, minResolutionInMeter); // saving space not necessary: dataAccess.trimTo((lastPointer + 1) * 4); dataAccess.flush(); } public void close() { dataAccess.close(); } public boolean isClosed() { return dataAccess.isClosed(); } public long getCapacity() { return dataAccess.getCapacity(); } public void setChecksum(int checksum) { this.checksum = checksum; } public int getSize() { return size; } public int getLeafs() { return leafs; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy