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

com.graphhopper.storage.GraphEdgeIdFinder Maven / Gradle / Ivy

/*
 *  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;

import com.carrotsearch.hppc.cursors.IntCursor;
import com.graphhopper.coll.GHIntHashSet;
import com.graphhopper.routing.util.EdgeFilter;
import com.graphhopper.storage.index.LocationIndex;
import com.graphhopper.util.*;
import com.graphhopper.util.shapes.*;
import org.locationtech.jts.algorithm.RectangleLineIntersector;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

import static com.graphhopper.util.shapes.BBox.toEnvelope;

/**
 * This class allows to find edges (or construct shapes) from shape filter.
 *
 * @author Robin Boldt
 */
public class GraphEdgeIdFinder {

    private static final int P_RADIUS = 5;
    private final Graph graph;
    private final LocationIndex locationIndex;

    public GraphEdgeIdFinder(Graph graph, LocationIndex locationIndex) {
        this.graph = graph;
        this.locationIndex = locationIndex;
    }

    static double calculateArea(Shape shape) {
        if (shape instanceof BBox)
            return calculateArea((BBox) shape);
        if (shape instanceof Polygon)
            return calculateArea((Polygon) shape);
        if (shape instanceof Circle)
            return calculateArea((Circle) shape);
        throw new IllegalStateException("Unsupported shape: " + shape);
    }
    
    /**
     * @param bBox
     * @return an estimated area in m^2 using the mean value of latitudes for longitude distance
     */
    static double calculateArea(BBox bBox) {
        double meanLat = (bBox.maxLat + bBox.minLat) / 2;
        return DistancePlaneProjection.DIST_PLANE.calcDist(meanLat, bBox.minLon, meanLat, bBox.maxLon)
                // left side should be equal to right side no mean value necessary
                * DistancePlaneProjection.DIST_PLANE.calcDist(bBox.minLat, bBox.minLon, bBox.maxLat, bBox.minLon);
    }

    static double calculateArea(Polygon polygon) {
        // for estimation use bounding box as reference:
        return calculateArea(polygon.getBounds()) * polygon.envelope.getArea() / polygon.prepPolygon.getGeometry().getArea();
    }

    static double calculateArea(Circle circle) {
        return Math.PI * circle.radiusInMeter * circle.radiusInMeter;
    }

    /**
     * This method creates an edgeIds hashset with edgeIds found inside the specified shape
     */
    private GHIntHashSet findEdgesInShape(final Shape shape, EdgeFilter filter) {
        GHIntHashSet edgeIds = new GHIntHashSet();
        locationIndex.query(shape.getBounds(), edgeId -> {
            EdgeIteratorState edge = graph.getEdgeIteratorStateForKey(edgeId * 2);
            if (filter.accept(edge) && shape.intersects(edge.fetchWayGeometry(FetchMode.ALL).makeImmutable()))
                edgeIds.add(edge.getEdge());
        });
        return edgeIds;
    }

    public static GraphEdgeIdFinder.BlockArea createBlockArea(Graph graph, LocationIndex locationIndex,
                                                              List points, PMap hints, EdgeFilter edgeFilter) {
        String blockAreaStr = hints.getString(Parameters.Routing.BLOCK_AREA, "");
        GraphEdgeIdFinder.BlockArea blockArea = new GraphEdgeIdFinder(graph, locationIndex).
                parseBlockArea(blockAreaStr, edgeFilter, hints.getDouble(Parameters.Routing.BLOCK_AREA + ".edge_id_max_area", 1000 * 1000));
        for (GHPoint p : points) {
            if (blockArea.contains(p))
                throw new IllegalArgumentException("Request with " + Parameters.Routing.BLOCK_AREA + " contained query point " + p + ". This is not allowed.");
        }
        return blockArea;
    }

    /**
     * This method reads the blockAreaString and creates a Collection of Shapes or a set of found edges if area is small enough.
     *
     * @param useEdgeIdsUntilAreaSize until the specified area (specified in m²) use the findEdgesInShape method
     */
    public BlockArea parseBlockArea(String blockAreaString, EdgeFilter filter, double useEdgeIdsUntilAreaSize) {
        final String objectSeparator = ";";
        final String innerObjSep = ",";
        BlockArea blockArea = new BlockArea(graph);

        // Add blocked circular areas or points
        if (!blockAreaString.isEmpty()) {
            String[] blockedCircularAreasArr = blockAreaString.split(objectSeparator);
            for (int i = 0; i < blockedCircularAreasArr.length; i++) {
                String objectAsString = blockedCircularAreasArr[i];
                String[] splittedObject = objectAsString.split(innerObjSep);

                Shape shape;
                boolean point = false;
                // always add the shape as we'll need this for virtual edges and for debugging.
                if (splittedObject.length > 4) {
                    shape = Polygon.parsePoints(objectAsString);
                } else if (splittedObject.length == 4) {
                    final BBox bbox = BBox.parseTwoPoints(objectAsString);
                    final RectangleLineIntersector cachedIntersector = new RectangleLineIntersector(toEnvelope(bbox));
                    shape = new BBox(bbox.minLon, bbox.maxLon, bbox.minLat, bbox.maxLat) {
                        @Override
                        public boolean intersects(PointList pointList) {
                            return BBox.intersects(cachedIntersector, pointList);
                        }
                    };
                } else if (splittedObject.length == 3) {
                    double lat = Double.parseDouble(splittedObject[0]);
                    double lon = Double.parseDouble(splittedObject[1]);
                    int radius = Integer.parseInt(splittedObject[2]);
                    shape = new Circle(lat, lon, radius);
                } else if (splittedObject.length == 2) {
                    double lat = Double.parseDouble(splittedObject[0]);
                    double lon = Double.parseDouble(splittedObject[1]);
                    shape = new Circle(lat, lon, P_RADIUS);
                    point = true;
                } else {
                    throw new IllegalArgumentException(objectAsString + " at index " + i + " need to be defined as lat,lon "
                            + "or as a circle lat,lon,radius or rectangular lat1,lon1,lat2,lon2");
                }
                
                
                if (point || calculateArea(shape) <= useEdgeIdsUntilAreaSize) {
                    GHIntHashSet blockedEdges = findEdgesInShape(shape, filter);
                    if (!blockedEdges.isEmpty()) {
                        blockArea.add(shape, blockedEdges);
                    }
                } else {
                    blockArea.add(shape);
                }
            }
        }
        return blockArea;
    }

    /**
     * This class handles edges and areas where access should be blocked.
     */
    public static class BlockArea {
        private final List edgesList = new ArrayList<>();
        private final List blockedShapes = new ArrayList<>();
        private final int baseEdgeCount;

        public BlockArea(Graph g) {
            baseEdgeCount = g.getAllEdges().length();
        }

        public boolean hasCachedEdgeIds(int shapeIndex) {
            return !edgesList.get(shapeIndex).isEmpty();
        }

        public String toString(int shapeIndex) {
            List returnList = new ArrayList<>();
            for (IntCursor intCursor : edgesList.get(shapeIndex)) {
                returnList.add(intCursor.value);
            }
            Collections.sort(returnList);
            return returnList.toString();
        }
        
        public void add(Shape shape) {
            add(shape, new GHIntHashSet());
        }

        public void add(Shape shape, GHIntHashSet blockedEdgeIds) {
            blockedShapes.add(shape);
            edgesList.add(Objects.requireNonNull(blockedEdgeIds));
        }

        public final boolean contains(GHPoint point) {
            for (Shape shape : blockedShapes) {
                if (shape.contains(point.lat, point.lon))
                    return true;
            }
            return false;
        }

        /**
         * @return true if the specified edgeState is part of this BlockArea
         */
        public final boolean intersects(EdgeIteratorState edgeState) {
            PointList pointList = null;
            BBox bbox = null;
            for (int shapeIdx = 0; shapeIdx < blockedShapes.size(); shapeIdx++) {
                GHIntHashSet blockedEdges = edgesList.get(shapeIdx);
                // blockedEdges acts as cache that is only useful when filled and for non-virtual edges
                if (!blockedEdges.isEmpty() && edgeState.getEdge() < baseEdgeCount) {
                    if (blockedEdges.contains(edgeState.getEdge()))
                        return true;
                    continue;
                }

                // compromise: avoid expensive fetch of pillar nodes, which isn't yet fast for being used in Weighting.calc
                if (bbox == null)
                    bbox = GHUtility.createBBox(edgeState);

                Shape shape = blockedShapes.get(shapeIdx);
                if (shape.getBounds().intersects(bbox)) {
                    if (pointList == null)
                        pointList = edgeState.fetchWayGeometry(FetchMode.ALL).makeImmutable();
                    if (shape.intersects(pointList))
                        return true;
                }
            }
            return false;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy