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

com.graphhopper.osm.OSMReader 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.osm;

import com.carrotsearch.hppc.IntLongMap;
import com.carrotsearch.hppc.LongArrayList;
import com.carrotsearch.hppc.LongIndexedContainer;
import com.carrotsearch.hppc.LongLongMap;
import com.carrotsearch.hppc.LongSet;
import com.graphhopper.coll.GHIntLongHashMap;
import com.graphhopper.coll.GHLongHashSet;
import com.graphhopper.coll.GHLongIntBTree;
import com.graphhopper.coll.GHLongLongHashMap;
import com.graphhopper.coll.LongIntMap;
import com.graphhopper.reader.*;
import com.graphhopper.reader.dem.ElevationProvider;
import com.graphhopper.osm.OSMTurnRelation.TurnCostTableEntry;
import com.graphhopper.routing.util.DefaultEdgeFilter;
import com.graphhopper.routing.util.EncodingManager;
import com.graphhopper.routing.util.FlagEncoder;
import com.graphhopper.routing.weighting.TurnWeighting;
import com.graphhopper.storage.*;
import com.graphhopper.util.*;
import com.graphhopper.util.shapes.GHPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.xml.stream.XMLStreamException;
import java.io.File;
import java.io.IOException;
import java.util.*;
import gnu.trove.map.TLongObjectMap;
import gnu.trove.map.hash.TLongObjectHashMap;
import javax.xml.stream.XMLStreamException;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.graphhopper.util.Helper.nf;

/**
 * This class parses an OSM xml or pbf file and creates a graph from it. It does so in a two phase
 * parsing processes in order to reduce memory usage compared to a single parsing processing.
 * 

* 1. a) Reads ways from OSM file and stores all associated node ids in osmNodeIdToIndexMap. If a * node occurs once it is a pillar node and if more it is a tower node, otherwise * osmNodeIdToIndexMap returns EMPTY. *

* 1. b) Reads relations from OSM file. In case that the relation is a route relation, it stores * specific relation attributes required for routing into osmWayIdToRouteWeigthMap for all the ways * of the relation. *

* 2.a) Reads nodes from OSM file and stores lat+lon information either into the intermediate * data structure for the pillar nodes (pillarLats/pillarLons) or, if a tower node, directly into the * graphStorage via setLatitude/setLongitude. It can also happen that a pillar node needs to be * transformed into a tower node e.g. via barriers or different speed values for one way. *

* 2.b) Reads ways OSM file and creates edges while calculating the speed etc from the OSM tags. * When creating an edge the pillar node information from the intermediate data structure will be * stored in the way geometry of that edge. *

* * @author Peter Karich */ public class OSMReader implements DataReader { protected static final int EMPTY_NODE = -1; // pillar node is >= 3 protected static final int PILLAR_NODE = 1; // tower node is <= -3 protected static final int TOWER_NODE = -2; private static final Logger LOGGER = LoggerFactory.getLogger(OSMReader.class); private final GraphStorage ghStorage; private final Graph graph; private final NodeAccess nodeAccess; private final LongIndexedContainer barrierNodeIds = new LongArrayList(); private final DistanceCalc distCalc = Helper.DIST_EARTH; private final DistanceCalc3D distCalc3D = Helper.DIST_3D; private final DouglasPeucker simplifyAlgo = new DouglasPeucker(); private final boolean exitOnlyPillarNodeException = true; private final Map outExplorerMap = new HashMap(); private final Map inExplorerMap = new HashMap(); protected long zeroCounter = 0; protected PillarInfo pillarInfo; private long locations; private long skippedLocations; private EncodingManager encodingManager = null; private int workerThreads = 2; // Using the correct Map is hard. We need a memory efficient and fast solution for big data sets! // // very slow: new SparseLongLongArray // only append and update possible (no unordered storage like with this doubleParse): new OSMIDMap // same here: not applicable as ways introduces the nodes in 'wrong' order: new OSMIDSegmentedMap // memory overhead due to open addressing and full rehash: // nodeOsmIdToIndexMap = new BigLongIntMap(expectedNodes, EMPTY); // smaller memory overhead for bigger data sets because of avoiding a "rehash" // remember how many times a node was used to identify tower nodes private LongIntMap osmNodeIdToInternalNodeMap; private GHLongLongHashMap osmNodeIdToNodeFlagsMap; private GHLongLongHashMap osmWayIdToRouteWeightMap; // stores osm way ids used by relations to identify which edge ids needs to be mapped later private GHLongHashSet osmWayIdSet = new GHLongHashSet(); private IntLongMap edgeIdToOsmWayIdMap; private boolean doSimplify = true; private int nextTowerId = 0; private int nextPillarId = 0; // negative but increasing to avoid clash with custom created OSM files private long newUniqueOsmId = -Long.MAX_VALUE; private ElevationProvider eleProvider = ElevationProvider.NOOP; private File osmFile; private Date osmDataDate; public OSMReader(GraphHopperStorage ghStorage) { this.ghStorage = ghStorage; this.graph = ghStorage; this.nodeAccess = graph.getNodeAccess(); osmNodeIdToInternalNodeMap = new GHLongIntBTree(200); osmNodeIdToNodeFlagsMap = new GHLongLongHashMap(200, .5f); osmWayIdToRouteWeightMap = new GHLongLongHashMap(200, .5f); pillarInfo = new PillarInfo(nodeAccess.is3D(), ghStorage.getDirectory()); } @Override public void readGraph() throws IOException { if (encodingManager == null) throw new IllegalStateException("Encoding manager was not set."); if (osmFile == null) throw new IllegalStateException("No OSM file specified"); if (!osmFile.exists()) throw new IllegalStateException("Your specified OSM file does not exist:" + osmFile.getAbsolutePath()); StopWatch sw1 = new StopWatch().start(); preProcess(osmFile); sw1.stop(); StopWatch sw2 = new StopWatch().start(); writeOsm2Graph(osmFile); sw2.stop(); LOGGER.info("time pass1:" + (int) sw1.getSeconds() + "s, " + "pass2:" + (int) sw2.getSeconds() + "s, " + "total:" + (int) (sw1.getSeconds() + sw2.getSeconds()) + "s"); } /** * Preprocessing of OSM file to select nodes which are used for highways. This allows a more * compact graph data structure. */ void preProcess(File osmFile) { OSMInputFile in = null; try { in = new OSMInputFile(osmFile).setWorkerThreads(workerThreads).open(); long tmpWayCounter = 1; long tmpRelationCounter = 1; ReaderElement item; while ((item = in.getNext()) != null) { if (item.isType(ReaderElement.WAY)) { final ReaderWay way = (ReaderWay) item; boolean valid = filterWay(way); if (valid) { LongIndexedContainer wayNodes = way.getNodes(); int s = wayNodes.size(); for (int index = 0; index < s; index++) { prepareHighwayNode(wayNodes.get(index)); } if (++tmpWayCounter % 10_000_000 == 0) { LOGGER.info(nf(tmpWayCounter) + " (preprocess), osmIdMap:" + nf(getNodeMap().getSize()) + " (" + getNodeMap().getMemoryUsage() + "MB) " + Helper.getMemInfo()); } } } else if (item.isType(ReaderElement.RELATION)) { final ReaderRelation relation = (ReaderRelation) item; if (!relation.isMetaRelation() && relation.hasTag("type", "route")) prepareWaysWithRelationInfo(relation); if (relation.hasTag("type", "restriction")) prepareRestrictionRelation(relation); if (++tmpRelationCounter % 100_000 == 0) { LOGGER.info(nf(tmpRelationCounter) + " (preprocess), osmWayMap:" + nf(getRelFlagsMap().size()) + " " + Helper.getMemInfo()); } } else if (item.isType(ReaderElement.FILEHEADER)) { final OSMFileHeader fileHeader = (OSMFileHeader) item; osmDataDate = Helper.createFormatter().parse(fileHeader.getTag("timestamp")); } } } catch (Exception ex) { throw new RuntimeException("Problem while parsing file", ex); } finally { Helper.close(in); } } private void prepareRestrictionRelation(ReaderRelation relation) { OSMTurnRelation turnRelation = createTurnRelation(relation); if (turnRelation != null) { getOsmWayIdSet().add(turnRelation.getOsmIdFrom()); getOsmWayIdSet().add(turnRelation.getOsmIdTo()); } } /** * @return all required osmWayIds to process e.g. relations. */ private LongSet getOsmWayIdSet() { return osmWayIdSet; } private IntLongMap getEdgeIdToOsmWayIdMap() { if (edgeIdToOsmWayIdMap == null) edgeIdToOsmWayIdMap = new GHIntLongHashMap(getOsmWayIdSet().size(), 0.5f); return edgeIdToOsmWayIdMap; } /** * Filter ways but do not analyze properties wayNodes will be filled with participating node * ids. *

* * @return true the current xml entry is a way entry and has nodes */ boolean filterWay(ReaderWay item) { // ignore broken geometry if (item.getNodes().size() < 2) return false; // ignore multipolygon geometry if (!item.hasTags()) return false; return encodingManager.acceptWay(item) > 0; } /** * Creates the graph with edges and nodes from the specified osm file. */ private void writeOsm2Graph(File osmFile) { int tmp = (int) Math.max(getNodeMap().getSize() / 50, 100); LOGGER.info("creating graph. Found nodes (pillar+tower):" + nf(getNodeMap().getSize()) + ", " + Helper.getMemInfo()); ghStorage.create(tmp); long wayStart = -1; long relationStart = -1; long counter = 1; OSMInputFile in = null; try { in = new OSMInputFile(osmFile).setWorkerThreads(workerThreads).open(); LongIntMap nodeFilter = getNodeMap(); ReaderElement item; while ((item = in.getNext()) != null) { switch (item.getType()) { case ReaderElement.NODE: if (nodeFilter.get(item.getId()) != -1) { processNode((ReaderNode) item); } break; case ReaderElement.WAY: if (wayStart < 0) { LOGGER.info(nf(counter) + ", now parsing ways"); wayStart = counter; } processWay((ReaderWay) item); break; case ReaderElement.RELATION: if (relationStart < 0) { LOGGER.info(nf(counter) + ", now parsing relations"); relationStart = counter; } processRelation((ReaderRelation) item); break; case ReaderElement.FILEHEADER: break; default: throw new IllegalStateException("Unknown type " + item.getType()); } if (++counter % 200_000_000 == 0) { LOGGER.info(nf(counter) + ", locs:" + nf(locations) + " (" + skippedLocations + ") " + Helper.getMemInfo()); } } if (in.getUnprocessedElements() > 0) throw new IllegalStateException("Still unprocessed elements in reader queue " + in.getUnprocessedElements()); // logger.info("storage nodes:" + storage.nodes() + " vs. graph nodes:" + storage.getGraph().nodes()); } catch (Exception ex) { throw new RuntimeException("Couldn't process file " + osmFile + ", error: " + ex.getMessage(), ex); } finally { Helper.close(in); } finishedReading(); if (graph.getNodes() == 0) throw new RuntimeException("Graph after reading OSM must not be empty. Read " + counter + " items and " + locations + " locations"); } /** * Process properties, encode flags and create edges for the way. */ void processWay(ReaderWay way) { if (way.getNodes().size() < 2) return; // ignore multipolygon geometry if (!way.hasTags()) return; long wayOsmId = way.getId(); long includeWay = encodingManager.acceptWay(way); if (includeWay == 0) return; long relationFlags = getRelFlagsMap().get(way.getId()); // TODO move this after we have created the edge and know the coordinates => encodingManager.applyWayTags LongArrayList osmNodeIds = way.getNodes(); // Estimate length of ways containing a route tag e.g. for ferry speed calculation if (osmNodeIds.size() > 1) { int first = getNodeMap().get(osmNodeIds.get(0)); int last = getNodeMap().get(osmNodeIds.get(osmNodeIds.size() - 1)); double firstLat = getTmpLatitude(first), firstLon = getTmpLongitude(first); double lastLat = getTmpLatitude(last), lastLon = getTmpLongitude(last); if (!Double.isNaN(firstLat) && !Double.isNaN(firstLon) && !Double.isNaN(lastLat) && !Double.isNaN(lastLon)) { double estimatedDist = distCalc.calcDist(firstLat, firstLon, lastLat, lastLon); // Add artificial tag for the estimated distance and center way.setTag("estimated_distance", estimatedDist); way.setTag("estimated_center", new GHPoint((firstLat + lastLat) / 2, (firstLon + lastLon) / 2)); } } if (way.getTag("duration") != null) { try { long dur = OSMTagParser.parseDuration(way.getTag("duration")); // Provide the duration value in seconds in an artificial graphhopper specific tag: way.setTag("duration:seconds", Long.toString(dur)); } catch (Exception ex) { LOGGER.warn("Parsing error in way with OSMID=" + way.getId() + " : " + ex.getMessage()); } } long wayFlags = encodingManager.handleWayTags(way, includeWay, relationFlags); if (wayFlags == 0) return; List createdEdges = new ArrayList(); // look for barriers along the way final int size = osmNodeIds.size(); int lastBarrier = -1; for (int i = 0; i < size; i++) { long nodeId = osmNodeIds.get(i); long nodeFlags = getNodeFlagsMap().get(nodeId); // barrier was spotted and way is otherwise passable for that mode of travel if (nodeFlags > 0) { if ((nodeFlags & wayFlags) > 0) { // remove barrier to avoid duplicates getNodeFlagsMap().put(nodeId, 0); // create shadow node copy for zero length edge long newNodeId = addBarrierNode(nodeId); if (i > 0) { // start at beginning of array if there was no previous barrier if (lastBarrier < 0) lastBarrier = 0; // add way up to barrier shadow node int length = i - lastBarrier + 1; LongArrayList partNodeIds = new LongArrayList(); partNodeIds.add(osmNodeIds.buffer, lastBarrier, length); partNodeIds.set(length - 1, newNodeId); createdEdges.addAll(addOSMWay(partNodeIds, wayFlags, wayOsmId)); // create zero length edge for barrier createdEdges.addAll(addBarrierEdge(newNodeId, nodeId, wayFlags, nodeFlags, wayOsmId)); } else { // run edge from real first node to shadow node createdEdges.addAll(addBarrierEdge(nodeId, newNodeId, wayFlags, nodeFlags, wayOsmId)); // exchange first node for created barrier node osmNodeIds.set(0, newNodeId); } // remember barrier for processing the way behind it lastBarrier = i; } } } // just add remainder of way to graph if barrier was not the last node if (lastBarrier >= 0) { if (lastBarrier < size - 1) { LongArrayList partNodeIds = new LongArrayList(); partNodeIds.add(osmNodeIds.buffer, lastBarrier, size - lastBarrier); createdEdges.addAll(addOSMWay(partNodeIds, wayFlags, wayOsmId)); } } else { // no barriers - simply add the whole way createdEdges.addAll(addOSMWay(way.getNodes(), wayFlags, wayOsmId)); } for (EdgeIteratorState edge : createdEdges) { encodingManager.applyWayTags(way, edge); } } public void processRelation(ReaderRelation relation) throws XMLStreamException { if (relation.hasTag("type", "restriction")) { OSMTurnRelation turnRelation = createTurnRelation(relation); if (turnRelation != null) { GraphExtension extendedStorage = graph.getExtension(); if (extendedStorage instanceof TurnCostExtension) { TurnCostExtension tcs = (TurnCostExtension) extendedStorage; Collection entries = analyzeTurnRelation(turnRelation); for (TurnCostTableEntry entry : entries) { tcs.addTurnInfo(entry.edgeFrom, entry.nodeVia, entry.edgeTo, entry.flags); } } } } } public Collection analyzeTurnRelation(OSMTurnRelation turnRelation) { Map entries = new LinkedHashMap(); for (FlagEncoder encoder : encodingManager.fetchEdgeEncoders()) { for (TurnCostTableEntry entry : analyzeTurnRelation(encoder, turnRelation)) { TurnCostTableEntry oldEntry = entries.get(entry.getItemId()); if (oldEntry != null) { // merging different encoders oldEntry.flags |= entry.flags; } else { entries.put(entry.getItemId(), entry); } } } return entries.values(); } public Collection analyzeTurnRelation(FlagEncoder encoder, OSMTurnRelation turnRelation) { if (!encoder.supports(TurnWeighting.class)) return Collections.emptyList(); EdgeExplorer edgeOutExplorer = outExplorerMap.get(encoder); EdgeExplorer edgeInExplorer = inExplorerMap.get(encoder); if (edgeOutExplorer == null || edgeInExplorer == null) { edgeOutExplorer = graph.createEdgeExplorer(new DefaultEdgeFilter(encoder, false, true)); outExplorerMap.put(encoder, edgeOutExplorer); edgeInExplorer = graph.createEdgeExplorer(new DefaultEdgeFilter(encoder, true, false)); inExplorerMap.put(encoder, edgeInExplorer); } return turnRelation.getRestrictionAsEntries(encoder, edgeOutExplorer, edgeInExplorer, this); } /** * @return OSM way ID from specified edgeId. Only previously stored OSM-way-IDs are returned in * order to reduce memory overhead. */ public long getOsmIdOfInternalEdge(int edgeId) { return getEdgeIdToOsmWayIdMap().get(edgeId); } public int getInternalNodeIdOfOsmNode(long nodeOsmId) { int id = getNodeMap().get(nodeOsmId); if (id < TOWER_NODE) return -id - 3; return EMPTY_NODE; } // TODO remove this ugly stuff via better preparsing phase! E.g. putting every tags etc into a helper file! double getTmpLatitude(int id) { if (id == EMPTY_NODE) return Double.NaN; if (id < TOWER_NODE) { // tower node id = -id - 3; return nodeAccess.getLatitude(id); } else if (id > -TOWER_NODE) { // pillar node id = id - 3; return pillarInfo.getLatitude(id); } else // e.g. if id is not handled from preparse (e.g. was ignored via isInBounds) return Double.NaN; } double getTmpLongitude(int id) { if (id == EMPTY_NODE) return Double.NaN; if (id < TOWER_NODE) { // tower node id = -id - 3; return nodeAccess.getLongitude(id); } else if (id > -TOWER_NODE) { // pillar node id = id - 3; return pillarInfo.getLon(id); } else // e.g. if id is not handled from preparse (e.g. was ignored via isInBounds) return Double.NaN; } private void processNode(ReaderNode node) { if (isInBounds(node)) { addNode(node); // analyze node tags for barriers if (node.hasTags()) { long nodeFlags = encodingManager.handleNodeTags(node); if (nodeFlags != 0) getNodeFlagsMap().put(node.getId(), nodeFlags); } locations++; } else { skippedLocations++; } } boolean addNode(ReaderNode node) { int nodeType = getNodeMap().get(node.getId()); if (nodeType == EMPTY_NODE) return false; double lat = node.getLat(); double lon = node.getLon(); double ele = getElevation(node); if (nodeType == TOWER_NODE) { addTowerNode(node.getId(), lat, lon, ele); } else if (nodeType == PILLAR_NODE) { pillarInfo.setNode(nextPillarId, lat, lon, ele); getNodeMap().put(node.getId(), nextPillarId + 3); nextPillarId++; } return true; } protected double getElevation(ReaderNode node) { return eleProvider.getEle(node.getLat(), node.getLon()); } void prepareWaysWithRelationInfo(ReaderRelation osmRelation) { // is there at least one tag interesting for the registed encoders? if (encodingManager.handleRelationTags(osmRelation, 0) == 0) return; for (ReaderRelation.Member member : osmRelation.getMembers()) { if (member.getType() != ReaderRelation.Member.WAY) continue; long osmId = member.getRef(); long oldRelationFlags = getRelFlagsMap().get(osmId); // Check if our new relation data is better comparated to the the last one long newRelationFlags = encodingManager.handleRelationTags(osmRelation, oldRelationFlags); if (oldRelationFlags != newRelationFlags) getRelFlagsMap().put(osmId, newRelationFlags); } } void prepareHighwayNode(long osmId) { int tmpGHNodeId = getNodeMap().get(osmId); if (tmpGHNodeId == EMPTY_NODE) { // osmId is used exactly once getNodeMap().put(osmId, PILLAR_NODE); } else if (tmpGHNodeId > EMPTY_NODE) { // mark node as tower node as it occured at least twice times getNodeMap().put(osmId, TOWER_NODE); } else { // tmpIndex is already negative (already tower node) } } int addTowerNode(long osmId, double lat, double lon, double ele) { if (nodeAccess.is3D()) nodeAccess.setNode(nextTowerId, lat, lon, ele); else nodeAccess.setNode(nextTowerId, lat, lon); int id = -(nextTowerId + 3); getNodeMap().put(osmId, id); nextTowerId++; return id; } /** * This method creates from an OSM way (via the osm ids) one or more edges in the graph. */ Collection addOSMWay(final LongIndexedContainer osmNodeIds, final long flags, final long wayOsmId) { PointList pointList = new PointList(osmNodeIds.size(), nodeAccess.is3D()); List newEdges = new ArrayList(5); int firstNode = -1; int lastIndex = osmNodeIds.size() - 1; int lastInBoundsPillarNode = -1; try { for (int i = 0; i < osmNodeIds.size(); i++) { long osmId = osmNodeIds.get(i); int tmpNode = getNodeMap().get(osmId); if (tmpNode == EMPTY_NODE) continue; // skip osmIds with no associated pillar or tower id (e.g. !OSMReader.isBounds) if (tmpNode == TOWER_NODE) continue; if (tmpNode == PILLAR_NODE) { // In some cases no node information is saved for the specified osmId. // ie. a way references a which does not exist in the current file. // => if the node before was a pillar node then convert into to tower node (as it is also end-standing). if (!pointList.isEmpty() && lastInBoundsPillarNode > -TOWER_NODE) { // transform the pillar node to a tower node tmpNode = lastInBoundsPillarNode; tmpNode = handlePillarNode(tmpNode, osmId, null, true); tmpNode = -tmpNode - 3; if (pointList.getSize() > 1 && firstNode >= 0) { // TOWER node newEdges.add(addEdge(firstNode, tmpNode, pointList, flags, wayOsmId)); pointList.clear(); pointList.add(nodeAccess, tmpNode); } firstNode = tmpNode; lastInBoundsPillarNode = -1; } continue; } if (tmpNode <= -TOWER_NODE && tmpNode >= TOWER_NODE) throw new AssertionError("Mapped index not in correct bounds " + tmpNode + ", " + osmId); if (tmpNode > -TOWER_NODE) { boolean convertToTowerNode = i == 0 || i == lastIndex; if (!convertToTowerNode) { lastInBoundsPillarNode = tmpNode; } // PILLAR node, but convert to towerNode if end-standing tmpNode = handlePillarNode(tmpNode, osmId, pointList, convertToTowerNode); } if (tmpNode < TOWER_NODE) { // TOWER node tmpNode = -tmpNode - 3; pointList.add(nodeAccess, tmpNode); if (firstNode >= 0) { newEdges.add(addEdge(firstNode, tmpNode, pointList, flags, wayOsmId)); pointList.clear(); pointList.add(nodeAccess, tmpNode); } firstNode = tmpNode; } } } catch (RuntimeException ex) { LOGGER.error("Couldn't properly add edge with osm ids:" + osmNodeIds, ex); if (exitOnlyPillarNodeException) throw ex; } return newEdges; } EdgeIteratorState addEdge(int fromIndex, int toIndex, PointList pointList, long flags, long wayOsmId) { // sanity checks if (fromIndex < 0 || toIndex < 0) throw new AssertionError("to or from index is invalid for this edge " + fromIndex + "->" + toIndex + ", points:" + pointList); if (pointList.getDimension() != nodeAccess.getDimension()) throw new AssertionError("Dimension does not match for pointList vs. nodeAccess " + pointList.getDimension() + " <-> " + nodeAccess.getDimension()); double towerNodeDistance = 0; double prevLat = pointList.getLatitude(0); double prevLon = pointList.getLongitude(0); double prevEle = pointList.is3D() ? pointList.getElevation(0) : Double.NaN; double lat, lon, ele = Double.NaN; PointList pillarNodes = new PointList(pointList.getSize() - 2, nodeAccess.is3D()); int nodes = pointList.getSize(); for (int i = 1; i < nodes; i++) { // we could save some lines if we would use pointList.calcDistance(distCalc); lat = pointList.getLatitude(i); lon = pointList.getLongitude(i); if (pointList.is3D()) { ele = pointList.getElevation(i); if (!distCalc.isCrossBoundary(lon, prevLon)) towerNodeDistance += distCalc3D.calcDist(prevLat, prevLon, prevEle, lat, lon, ele); prevEle = ele; } else if (!distCalc.isCrossBoundary(lon, prevLon)) towerNodeDistance += distCalc.calcDist(prevLat, prevLon, lat, lon); prevLat = lat; prevLon = lon; if (nodes > 2 && i < nodes - 1) { if (pillarNodes.is3D()) pillarNodes.add(lat, lon, ele); else pillarNodes.add(lat, lon); } } if (towerNodeDistance < 0.0001) { // As investigation shows often two paths should have crossed via one identical point // but end up in two very close points. zeroCounter++; towerNodeDistance = 0.0001; } double maxDistance = (Integer.MAX_VALUE - 1) / 1000d; if (Double.isNaN(towerNodeDistance)) { LOGGER.warn("Bug in OSM or GraphHopper. Illegal tower node distance " + towerNodeDistance + " reset to 1m, osm way " + wayOsmId); towerNodeDistance = 1; } if (Double.isInfinite(towerNodeDistance) || towerNodeDistance > maxDistance) { // Too large is very rare and often the wrong tagging. See #435 // so we can avoid the complexity of splitting the way for now (new towernodes would be required, splitting up geometry etc) LOGGER.warn("Bug in OSM or GraphHopper. Too big tower node distance " + towerNodeDistance + " reset to large value, osm way " + wayOsmId); towerNodeDistance = maxDistance; } EdgeIteratorState iter = graph.edge(fromIndex, toIndex).setDistance(towerNodeDistance).setFlags(flags); if (nodes > 2) { if (doSimplify) simplifyAlgo.simplify(pillarNodes); iter.setWayGeometry(pillarNodes); } storeOsmWayID(iter.getEdge(), wayOsmId); return iter; } /** * Stores only osmWayIds which are required for relations */ protected void storeOsmWayID(int edgeId, long osmWayId) { if (getOsmWayIdSet().contains(osmWayId)) { getEdgeIdToOsmWayIdMap().put(edgeId, osmWayId); } } /** * @return converted tower node */ private int handlePillarNode(int tmpNode, long osmId, PointList pointList, boolean convertToTowerNode) { tmpNode = tmpNode - 3; double lat = pillarInfo.getLatitude(tmpNode); double lon = pillarInfo.getLongitude(tmpNode); double ele = pillarInfo.getElevation(tmpNode); if (lat == Double.MAX_VALUE || lon == Double.MAX_VALUE) throw new RuntimeException("Conversion pillarNode to towerNode already happended!? " + "osmId:" + osmId + " pillarIndex:" + tmpNode); if (convertToTowerNode) { // convert pillarNode type to towerNode, make pillar values invalid pillarInfo.setNode(tmpNode, Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE); tmpNode = addTowerNode(osmId, lat, lon, ele); } else if (pointList.is3D()) pointList.add(lat, lon, ele); else pointList.add(lat, lon); return (int) tmpNode; } protected void finishedReading() { printInfo("way"); pillarInfo.clear(); eleProvider.release(); osmNodeIdToInternalNodeMap = null; osmNodeIdToNodeFlagsMap = null; osmWayIdToRouteWeightMap = null; osmWayIdSet = null; edgeIdToOsmWayIdMap = null; } /** * Create a copy of the barrier node */ long addBarrierNode(long nodeId) { ReaderNode newNode; int graphIndex = getNodeMap().get(nodeId); if (graphIndex < TOWER_NODE) { graphIndex = -graphIndex - 3; newNode = new ReaderNode(createNewNodeId(), nodeAccess, graphIndex); } else { graphIndex = graphIndex - 3; newNode = new ReaderNode(createNewNodeId(), pillarInfo, graphIndex); } final long id = newNode.getId(); prepareHighwayNode(id); addNode(newNode); return id; } private long createNewNodeId() { return newUniqueOsmId++; } /** * Add a zero length edge with reduced routing options to the graph. */ Collection addBarrierEdge(long fromId, long toId, long flags, long nodeFlags, long wayOsmId) { // clear barred directions from routing flags flags &= ~nodeFlags; // add edge barrierNodeIds.clear(); barrierNodeIds.add(fromId); barrierNodeIds.add(toId); return addOSMWay(barrierNodeIds, flags, wayOsmId); } /** * Creates an OSM turn relation out of an unspecified OSM relation *

* * @return the OSM turn relation, null, if unsupported turn relation */ OSMTurnRelation createTurnRelation(ReaderRelation relation) { OSMTurnRelation.Type type = OSMTurnRelation.Type.getRestrictionType(relation.getTag("restriction")); if (type != OSMTurnRelation.Type.UNSUPPORTED) { long fromWayID = -1; long viaNodeID = -1; long toWayID = -1; for (ReaderRelation.Member member : relation.getMembers()) { if (ReaderElement.WAY == member.getType()) { if ("from".equals(member.getRole())) { fromWayID = member.getRef(); } else if ("to".equals(member.getRole())) { toWayID = member.getRef(); } } else if (ReaderElement.NODE == member.getType() && "via".equals(member.getRole())) { viaNodeID = member.getRef(); } } if (fromWayID >= 0 && toWayID >= 0 && viaNodeID >= 0) { return new OSMTurnRelation(fromWayID, viaNodeID, toWayID, type); } } return null; } /** * Filter method, override in subclass */ boolean isInBounds(ReaderNode node) { return true; } /** * Maps OSM IDs (long) to internal node IDs (int) */ protected LongIntMap getNodeMap() { return osmNodeIdToInternalNodeMap; } protected LongLongMap getNodeFlagsMap() { return osmNodeIdToNodeFlagsMap; } GHLongLongHashMap getRelFlagsMap() { return osmWayIdToRouteWeightMap; } /** * Specify the type of the path calculation (car, bike, ...). */ @Override public OSMReader setEncodingManager(EncodingManager em) { this.encodingManager = em; return this; } @Override public OSMReader setWayPointMaxDistance(double maxDist) { doSimplify = maxDist > 0; simplifyAlgo.setMaxDistance(maxDist); return this; } @Override public OSMReader setWorkerThreads(int numOfWorkers) { this.workerThreads = numOfWorkers; return this; } @Override public OSMReader setElevationProvider(ElevationProvider eleProvider) { if (eleProvider == null) throw new IllegalStateException("Use the NOOP elevation provider instead of null or don't call setElevationProvider"); if (!nodeAccess.is3D() && ElevationProvider.NOOP != eleProvider) throw new IllegalStateException("Make sure you graph accepts 3D data"); this.eleProvider = eleProvider; return this; } @Override public DataReader setFile(File osmFile) { this.osmFile = osmFile; return this; } private void printInfo(String str) { LOGGER.info("finished " + str + " processing." + " nodes: " + graph.getNodes() + ", osmIdMap.size:" + getNodeMap().getSize() + ", osmIdMap:" + getNodeMap().getMemoryUsage() + "MB" + ", nodeFlagsMap.size:" + getNodeFlagsMap().size() + ", relFlagsMap.size:" + getRelFlagsMap().size() + ", zeroCounter:" + zeroCounter + " " + Helper.getMemInfo()); } @Override public Date getDataDate() { return osmDataDate; } @Override public String toString() { return getClass().getSimpleName(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy