Skip to content

Commit

Permalink
NoisyRoadNearby encoded value implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
ratrun committed Aug 7, 2024
1 parent 90acd49 commit 7e5e020
Show file tree
Hide file tree
Showing 14 changed files with 494 additions and 26 deletions.
1 change: 1 addition & 0 deletions config-example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ graphhopper:
# More are: surface,smoothness,max_width,max_height,max_weight,max_weight_except,hgv,max_axle_load,max_length,
# hazmat,hazmat_tunnel,hazmat_water,lanes,osm_way_id,toll,track_type,mtb_rating,hike_rating,horse_rating,
# country,curvature,average_slope,max_slope,car_temporal_access,bike_temporal_access,foot_temporal_access
# noisy_road_nearby
graph.encoded_values: car_access, car_average_speed

#### Speed, hybrid and flexible mode ####
Expand Down
3 changes: 2 additions & 1 deletion core/src/main/java/com/graphhopper/GraphHopper.java
Original file line number Diff line number Diff line change
Expand Up @@ -914,7 +914,8 @@ protected void importOSM() {
}

logger.info("start creating graph from " + osmFile);
OSMReader reader = new OSMReader(baseGraph.getBaseGraph(), osmParsers, osmReaderConfig).setFile(_getOSMFile()).
OSMReader reader = new OSMReader(baseGraph.getBaseGraph(), encodingManager, osmParsers, osmReaderConfig)
.setFile(_getOSMFile()).
setAreaIndex(areaIndex).
setElevationProvider(eleProvider).
setCountryRuleFactory(countryRuleFactory);
Expand Down
55 changes: 55 additions & 0 deletions core/src/main/java/com/graphhopper/reader/osm/OSMNoisyRoad.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* 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.reader.osm;

import com.graphhopper.routing.util.NoiseIndex;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.impl.PackedCoordinateSequence;

import java.util.Arrays;

public class OSMNoisyRoad implements NoiseIndex.Road {
private static final GeometryFactory GEOMETRY_FACTORY = new GeometryFactory();
final PackedCoordinateSequence.Float road;

public OSMNoisyRoad(int numNodes) {
float[] coords = new float[numNodes * 2];
Arrays.fill(coords, Float.NaN);
road = new PackedCoordinateSequence.Float(coords, 2, 0);
}

public void setCoordinate(int index, double lat, double lon) {
// todonow: precision/double->float cast?
road.setX(index, (float) lon);
road.setY(index, (float) lat);
}

public boolean isValid() {
float[] coords = road.getRawCoordinates();
for (float f : coords)
if (Float.isNaN(f))
return false;
return coords.length >= 2 ;
}

public LineString getRoad() {
return GEOMETRY_FACTORY.createLineString(this.road);
}
}
113 changes: 113 additions & 0 deletions core/src/main/java/com/graphhopper/reader/osm/OSMNoisyRoadData.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* 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.reader.osm;

import com.carrotsearch.hppc.IntArrayList;
import com.carrotsearch.hppc.LongArrayList;
import com.carrotsearch.hppc.cursors.LongCursor;
import com.graphhopper.coll.GHLongLongBTree;
import com.graphhopper.coll.LongLongMap;
import com.graphhopper.reader.ReaderNode;
import com.graphhopper.reader.ReaderWay;
import com.graphhopper.storage.Directory;
import com.graphhopper.util.BitUtil;

import java.util.ArrayList;
import java.util.List;

public class OSMNoisyRoadData {

private final List<OSMNoisyRoad> osmNoisyRoads;
private final LongToTwoIntsMap osmNoisyRoadsNodeIndicesByOSMNodeIds;

public OSMNoisyRoadData() {
osmNoisyRoads = new ArrayList<>();
osmNoisyRoadsNodeIndicesByOSMNodeIds = new LongToTwoIntsMap();
}

public void addOSMNoisyRoadWithoutCoordinates(LongArrayList osmNodeIds) {
osmNoisyRoads.add(new OSMNoisyRoad(osmNodeIds.size()));
for (LongCursor node : osmNodeIds)
osmNoisyRoadsNodeIndicesByOSMNodeIds.put(node.value, osmNoisyRoads.size() - 1, node.index);
}

public void fillOSMNoisyRoadNodeCoordinates(ReaderNode node) {
long index = osmNoisyRoadsNodeIndicesByOSMNodeIds.get(node.getId());
if (index >= 0) {
int osmNoisyRoadIndex = BitUtil.LITTLE.getIntLow(index);
int nodeIndex = BitUtil.LITTLE.getIntHigh(index);
OSMNoisyRoad osmNoisyRoad = osmNoisyRoads.get(osmNoisyRoadIndex);
// Note that we set the coordinates only for one particular node for one particular
// osm area, even though the same osm node might be used in multiple such areas. We will
// fix the next time we get to see the osm area ways.
osmNoisyRoad.setCoordinate(nodeIndex, node.getLat(), node.getLon());
}
}

public void clear(){
osmNoisyRoadsNodeIndicesByOSMNodeIds.clear();
}

public void fixOSMNoisyRoads(int osmNoisyWayIndex, ReaderWay way) {
// The problem we solve here is that some osm nodes are used by multiple noisy ways.
// At the very least this is the case for the first and last node of the ways,
// but there are also many junctions that share a common node. Since we only store one
// index into the coordinate array of the noisy roads, the coordinates for some noisy roads
// nodes won't be set. Therefore, we need to make up for this here where we get to see the
// ways a second time. If this wasn't the case we could read the noisy ways&nodes in pass1
// instead of pass0 which would allow us to skip nodes and ways in pass0 (faster import).
OSMNoisyRoad actual = osmNoisyRoads.get(osmNoisyWayIndex);
for (LongCursor node : way.getNodes()) {
long index = osmNoisyRoadsNodeIndicesByOSMNodeIds.get(node.value);
int osmNoisyRoadIndex = BitUtil.LITTLE.getIntLow(index);
int nodeIndex = BitUtil.LITTLE.getIntHigh(index);
OSMNoisyRoad osmNoisyRoad = osmNoisyRoads.get(osmNoisyRoadIndex);
actual.road.setX(node.index, osmNoisyRoad.road.getX(nodeIndex));
actual.road.setY(node.index, osmNoisyRoad.road.getY(nodeIndex));
}
}

public List<OSMNoisyRoad> getOsmNoisyRoads() {
return osmNoisyRoads;
}

public static class LongToTwoIntsMap {
private final LongLongMap internalIdsByKey = new GHLongLongBTree(200, 4, -1);
private final IntArrayList vals1 = new IntArrayList();
private final IntArrayList vals2 = new IntArrayList();

public void put(long key, int val1, int val2) {
vals1.add(val1);
vals2.add(val2);
internalIdsByKey.put(key, vals1.size() - 1);
}

public long get(long key) {
long id = internalIdsByKey.get(key);
if (id < 0) return -1;
return BitUtil.LITTLE.toLong(vals1.get((int)id), vals2.get((int)id));
}

public void clear() {
internalIdsByKey.clear();
vals1.release();
vals2.release();
}
}
}
75 changes: 55 additions & 20 deletions core/src/main/java/com/graphhopper/reader/osm/OSMReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,9 @@
import com.graphhopper.routing.OSMReaderConfig;
import com.graphhopper.routing.ev.Country;
import com.graphhopper.routing.ev.EdgeIntAccess;
import com.graphhopper.routing.ev.NoisyRoadNearby;
import com.graphhopper.routing.ev.State;
import com.graphhopper.routing.util.AreaIndex;
import com.graphhopper.routing.util.CustomArea;
import com.graphhopper.routing.util.FerrySpeedCalculator;
import com.graphhopper.routing.util.OSMParsers;
import com.graphhopper.routing.util.*;
import com.graphhopper.routing.util.countryrules.CountryRule;
import com.graphhopper.routing.util.countryrules.CountryRuleFactory;
import com.graphhopper.routing.util.parsers.RestrictionSetter;
Expand Down Expand Up @@ -99,12 +97,19 @@ public class OSMReader {
private WayToEdgesMap restrictedWaysToEdgesMap = new WayToEdgesMap();
private List<ReaderRelation> restrictionRelations = new ArrayList<>();

public OSMReader(BaseGraph baseGraph, OSMParsers osmParsers, OSMReaderConfig config) {
private final OSMNoisyRoadData osmNoisyRoadData;
private NoiseIndex<OSMNoisyRoad> osmNoisyRoadNoiseIndex;
private final EncodingManager encodingManager;
final Set<String> noisyHighwayValues = new HashSet<>(Arrays.asList("motorway",
"motorway_link", "trunk", "trunk_link", "primary", "primary_link", "secondary", "secondary_link"));

public OSMReader(BaseGraph baseGraph, EncodingManager encodingManager, OSMParsers osmParsers, OSMReaderConfig config) {
this.baseGraph = baseGraph;
this.edgeIntAccess = baseGraph.getEdgeAccess();
this.config = config;
this.nodeAccess = baseGraph.getNodeAccess();
this.osmParsers = osmParsers;
this.encodingManager = encodingManager;
this.restrictionSetter = new RestrictionSetter(baseGraph, osmParsers.getRestrictionTagParsers().stream().map(RestrictionTagParser::getTurnRestrictionEnc).toList());

simplifyAlgo.setMaxDistance(config.getMaxWayPointDistance());
Expand All @@ -115,6 +120,7 @@ public OSMReader(BaseGraph baseGraph, OSMParsers osmParsers, OSMReaderConfig con
if (tempRelFlags.length != 2)
// we use a long to store relation flags currently, so the relation flags ints ref must have length 2
throw new IllegalArgumentException("OSMReader cannot use relation flags with != 2 integers");
osmNoisyRoadData = new OSMNoisyRoadData();
}

/**
Expand Down Expand Up @@ -162,16 +168,27 @@ public void readGraph() throws IOException {
if (!baseGraph.isInitialized())
throw new IllegalStateException("BaseGraph must be initialize before we can read OSM");

WaySegmentParser waySegmentParser = new WaySegmentParser.Builder(baseGraph.getNodeAccess(), baseGraph.getDirectory())
WaySegmentParser.Builder waySegmentParserBuilder = new WaySegmentParser.Builder(baseGraph.getNodeAccess(), baseGraph.getDirectory())
.setElevationProvider(this::getElevation)
.setWayFilter(this::acceptWay)
.setSplitNodeFilter(this::isBarrierNode)
.setWayPreprocessor(this::preprocessWay)
.setRelationPreprocessor(this::preprocessRelations)
.setRelationProcessor(this::processRelation)
.setEdgeHandler(this::addEdge)
.setWorkerThreads(config.getWorkerThreads())
.build();
.setWorkerThreads(config.getWorkerThreads());
if (encodingManager.hasEncodedValue(NoisyRoadNearby.KEY)) {
waySegmentParserBuilder
// todonow: we could move the pass1 way handling and the pass2 node handling of the standard way segment parser to pass0/1,
// then we could skip nodes in pass2 to speed up the import
.setPass0WayPreHook(this::handleOSMNoisyRoads)
.setPass1NodePreHook(osmNoisyRoadData::fillOSMNoisyRoadNodeCoordinates)
.setPass1WayPreHook(this::handleOSMNoisyRoadsAgain)
.setPass1FinishHook(osmNoisyRoadData::clear)
.setPass2AfterNodesHook(this::buildOSMNoisyRoadIndex);

}
WaySegmentParser waySegmentParser = waySegmentParserBuilder.build();
waySegmentParser.readOSM(osmFile);
osmDataDate = waySegmentParser.getTimestamp();
if (baseGraph.getNodes() == 0)
Expand All @@ -190,6 +207,31 @@ public Date getDataDate() {
return osmDataDate;
}

void handleOSMNoisyRoads(ReaderWay way) {
if (way.hasTag("highway", noisyHighwayValues))
osmNoisyRoadData.addOSMNoisyRoadWithoutCoordinates(way.getNodes());
}

int osmNoisyWayIndex = -1;
void handleOSMNoisyRoadsAgain(ReaderWay way) {
if (way.hasTag("highway",noisyHighwayValues)) {
osmNoisyWayIndex++;
osmNoisyRoadData.fixOSMNoisyRoads(osmNoisyWayIndex, way);
}
}

void buildOSMNoisyRoadIndex() {
List<OSMNoisyRoad> validRoads = osmNoisyRoadData.getOsmNoisyRoads().stream().filter(r -> {
if (!r.isValid()) {
OSM_WARNING_LOGGER.warn("invalid noisy road: " + r);
return false;
}
return true;
}).collect(Collectors.toList());
osmNoisyRoadData.getOsmNoisyRoads().clear();
osmNoisyRoadNoiseIndex = new NoiseIndex<>(validRoads);
}

protected double getElevation(ReaderNode node) {
double ele = eleProvider.getEle(node);
return Double.isNaN(ele) ? config.getDefaultElevation() : ele;
Expand Down Expand Up @@ -246,20 +288,13 @@ protected void setArtificialWayTags(PointList pointList, ReaderWay way, double d
way.removeTag("country_rule");
way.removeTag("custom_areas");

if (osmNoisyRoadNoiseIndex != null) {
way.setTag("noisy_road_nearby", osmNoisyRoadNoiseIndex.query(pointList.getMiddleLat(), pointList.getMiddleLon()));
}

List<CustomArea> customAreas;
if (areaIndex != null) {
double middleLat;
double middleLon;
if (pointList.size() > 2) {
middleLat = pointList.getLat(pointList.size() / 2);
middleLon = pointList.getLon(pointList.size() / 2);
} else {
double firstLat = pointList.getLat(0), firstLon = pointList.getLon(0);
double lastLat = pointList.getLat(pointList.size() - 1), lastLon = pointList.getLon(pointList.size() - 1);
middleLat = (firstLat + lastLat) / 2;
middleLon = (firstLon + lastLon) / 2;
}
customAreas = areaIndex.query(middleLat, middleLon);
customAreas = areaIndex.query(pointList.getMiddleLat(), pointList.getMiddleLon());
} else {
customAreas = emptyList();
}
Expand Down
Loading

0 comments on commit 7e5e020

Please sign in to comment.