From 4589dfb1f97331ff920eab6dc655ad0020519b91 Mon Sep 17 00:00:00 2001 From: ansons Date: Mon, 23 May 2022 15:21:08 -0400 Subject: [PATCH 01/13] refactor: generify shapefile matching --- .../scenario/ModificationTypeResolver.java | 1 - .../r5/analyst/scenario/ShapefileLts.java | 5 +-- .../com/conveyal/r5/shapefile/LtsMatcher.java | 40 +++++++++++++++++ .../r5/shapefile/ShapefileMatcher.java | 44 +++++++------------ .../r5/shapefile/ShapefileMatcherMain.java | 2 +- 5 files changed, 58 insertions(+), 34 deletions(-) create mode 100644 src/main/java/com/conveyal/r5/shapefile/LtsMatcher.java diff --git a/src/main/java/com/conveyal/r5/analyst/scenario/ModificationTypeResolver.java b/src/main/java/com/conveyal/r5/analyst/scenario/ModificationTypeResolver.java index c4b95696e..d37784ade 100644 --- a/src/main/java/com/conveyal/r5/analyst/scenario/ModificationTypeResolver.java +++ b/src/main/java/com/conveyal/r5/analyst/scenario/ModificationTypeResolver.java @@ -1,6 +1,5 @@ package com.conveyal.r5.analyst.scenario; -import com.conveyal.r5.shapefile.ShapefileMatcher; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.databind.DatabindContext; import com.fasterxml.jackson.databind.JavaType; diff --git a/src/main/java/com/conveyal/r5/analyst/scenario/ShapefileLts.java b/src/main/java/com/conveyal/r5/analyst/scenario/ShapefileLts.java index 65d20b33d..77d54f6ae 100644 --- a/src/main/java/com/conveyal/r5/analyst/scenario/ShapefileLts.java +++ b/src/main/java/com/conveyal/r5/analyst/scenario/ShapefileLts.java @@ -4,8 +4,7 @@ import com.conveyal.analysis.datasource.DataSourceException; import com.conveyal.file.FileStorageFormat; import com.conveyal.file.FileStorageKey; -import com.conveyal.r5.rastercost.ElevationLoader; -import com.conveyal.r5.shapefile.ShapefileMatcher; +import com.conveyal.r5.shapefile.LtsMatcher; import com.conveyal.r5.transit.TransportNetwork; import com.conveyal.r5.transit.TransportNetworkCache; import com.conveyal.r5.util.ExceptionUtils; @@ -57,7 +56,7 @@ public boolean apply (TransportNetwork network) { // Replicate the entire flags array so we can write to it (following copy-on-write policy). // Otherwise the TIntAugmentedList only allows extending the base graph. network.streetLayer.edgeStore.flags = new TIntArrayList(network.streetLayer.edgeStore.flags); - ShapefileMatcher shapefileMatcher = new ShapefileMatcher(network.streetLayer); + LtsMatcher shapefileMatcher = new LtsMatcher(network.streetLayer); try { shapefileMatcher.match(localFile.getAbsolutePath(), ltsAttribute); } catch (Exception e) { diff --git a/src/main/java/com/conveyal/r5/shapefile/LtsMatcher.java b/src/main/java/com/conveyal/r5/shapefile/LtsMatcher.java new file mode 100644 index 000000000..ff7d2ee4a --- /dev/null +++ b/src/main/java/com/conveyal/r5/shapefile/LtsMatcher.java @@ -0,0 +1,40 @@ +package com.conveyal.r5.shapefile; + +import com.conveyal.r5.streets.EdgeStore; +import com.conveyal.r5.streets.StreetLayer; +import org.opengis.feature.simple.SimpleFeature; + +import static com.conveyal.r5.labeling.LevelOfTrafficStressLabeler.intToLts; +import static com.conveyal.r5.streets.EdgeStore.EdgeFlag.BIKE_LTS_EXPLICIT; + +public class LtsMatcher extends ShapefileMatcher { + + public LtsMatcher (StreetLayer streets) { + super(streets); + } + + /** + * Copy LTS attribute from the supplied feature to the pair of edges, setting the BIKE_LTS_EXPLICIT flag. This + * will prevent Conveyal OSM-inferred LTS from overwriting the shapefile-derived LTS. + * + * In current usage this is applied after the OSM is already completely loaded and converted to network edges, so + * it overwrites any data from OSM. Perhaps instead of BIKE_LTS_EXPLICIT we should have an LTS source flag: + * OSM_INFERRED, OSM_EXPLICIT, SHAPEFILE_MATCH etc. This could also apply to things like speeds and slopes. The + * values could be retained only for the duration of network building unless we have a reason to keep them. + */ + @Override + void setEdgePair (SimpleFeature feature, int attributeIndex, EdgeStore.Edge edge) { + // Set flags on forward and backward edges to match those on feature attribute + // TODO reuse code from LevelOfTrafficStressLabeler.label() + int lts = ((Number) feature.getAttribute(attributeIndex)).intValue(); + if (lts < 1 || lts > 4) { + LOG.error("Clamping LTS value to range [1...4]. Value in attribute is {}", lts); + } + EdgeStore.EdgeFlag ltsFlag = intToLts(lts); + edge.setFlag(BIKE_LTS_EXPLICIT); + edge.setFlag(ltsFlag); + edge.advance(); + edge.setFlag(BIKE_LTS_EXPLICIT); + edge.setFlag(ltsFlag); + } +} diff --git a/src/main/java/com/conveyal/r5/shapefile/ShapefileMatcher.java b/src/main/java/com/conveyal/r5/shapefile/ShapefileMatcher.java index f1327f281..380f70f92 100644 --- a/src/main/java/com/conveyal/r5/shapefile/ShapefileMatcher.java +++ b/src/main/java/com/conveyal/r5/shapefile/ShapefileMatcher.java @@ -1,6 +1,5 @@ package com.conveyal.r5.shapefile; -import com.conveyal.osmlib.OSM; import com.conveyal.r5.streets.EdgeStore; import com.conveyal.r5.streets.StreetLayer; import com.conveyal.r5.util.LambdaCounter; @@ -10,7 +9,6 @@ import org.locationtech.jts.geom.LineString; import org.locationtech.jts.geom.MultiLineString; import org.locationtech.jts.index.strtree.STRtree; -import org.opengis.feature.Property; import org.opengis.feature.simple.SimpleFeature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -20,9 +18,6 @@ import java.util.List; import java.util.stream.IntStream; -import static com.conveyal.r5.labeling.LevelOfTrafficStressLabeler.intToLts; -import static com.conveyal.r5.streets.EdgeStore.EdgeFlag.BIKE_LTS_EXPLICIT; - /** * The class ShapefileMain converts shapefiles to OSM data, which is in turn converted to R5 street networks. * For various reasons we may prefer to load true OSM data, then overlay the data from the shapefile onto the OSM. @@ -32,7 +27,7 @@ * two main ways: by creating new nodes wherever shapes cross, or by assuming any nodes in the same location are the * same node (and not two nodes stacked vertically for example). The former does not allow for separation of tunnels * and bridges. The latter requires exactly placed nodes on two or more features for every intersection. For example, - * the end of a road at a T insersection requires a node in the same location on the perpendicular road. + * the end of a road at a T intersection requires a node in the same location on the perpendicular road. * It would be possible to hybridize these approaches, for example automatically inserting nodes at T intersections but * requiring explicit duplicate nodes at crossing intersections to distinguish them from bridges and tunnels. The * details get tricky though: specifically, how close does a line or point have to be to another before it's connected? @@ -53,26 +48,20 @@ * * This class matches a supplied shapefile to an already-built network. */ -public class ShapefileMatcher { +public abstract class ShapefileMatcher { public static final Logger LOG = LoggerFactory.getLogger(ShapefileMatcher.class); private STRtree featureIndex; private StreetLayer streets; - private int ltsAttributeIndex = -1; + private int attributeIndex = -1; public ShapefileMatcher (StreetLayer streets) { this.streets = streets; } /** - * Match each pair of edges in the street layer to a feature in the shapefile. Copy LTS attribute from that feature - * to the pair of edges, setting the BIKE_LTS_EXPLICIT flag. This will prevent Conveyal OSM-inferred LTS from - * overwriting the shapefile-derived LTS. - * In current usage this is applied after the OSM is already completely loaded and converted to network edges, so - * it overwrites any data from OSM. Perhaps instead of BIKE_LTS_EXPLICIT we should have an LTS source flag: - * OSM_INFERRED, OSM_EXPLICIT, SHAPEFILE_MATCH etc. This could also apply to things like speeds and slopes. - * The values could be retained only for the duration of network building unless we have a reason to keep them. + * Match each pair of edges in the street layer to a feature in the shapefile, then set flags on the edges. */ public void match (String shapefileName, String attributeName) { try { @@ -89,18 +78,7 @@ public void match (String shapefileName, String attributeName) { LineString edgeGeometry = edge.getGeometry(); SimpleFeature bestFeature = findBestMatch(edgeGeometry); if (bestFeature != null) { - // Set flags on forward and backward edges to match those on feature attribute - // TODO reuse code from LevelOfTrafficStressLabeler.label() - int lts = ((Number) bestFeature.getAttribute(ltsAttributeIndex)).intValue(); - if (lts < 1 || lts > 4) { - LOG.error("Clamping LTS value to range [1...4]. Value in attribute is {}", lts); - } - EdgeStore.EdgeFlag ltsFlag = intToLts(lts); - edge.setFlag(BIKE_LTS_EXPLICIT); - edge.setFlag(ltsFlag); - edge.advance(); - edge.setFlag(BIKE_LTS_EXPLICIT); - edge.setFlag(ltsFlag); + setEdgePair(bestFeature, attributeIndex, edge); edgePairCounter.increment(); } }); @@ -110,6 +88,14 @@ public void match (String shapefileName, String attributeName) { streets.edgeStore.nEdgePairs() - edgePairCounter.getCount()); } + /** + * Set the appropriate edge flag to the value of the specified attribute in the corresponding (matched) feature. + * Subclasses are responsible for implementation details on which flag to set. + */ + void setEdgePair (SimpleFeature feature, int attributeIndex, EdgeStore.Edge edge) { + // Do nothing; subclasses should override. + } + // Match metric is currently Hausdorff distance, eventually replace with something that accounts for overlap length. private SimpleFeature findBestMatch (LineString edgeGeometry) { SimpleFeature bestFeature = null; @@ -129,7 +115,7 @@ private SimpleFeature findBestMatch (LineString edgeGeometry) { } // Index is in floating-point WGS84 - public void indexFeatures (String shapefileName, String attributeName) throws Throwable { + private void indexFeatures (String shapefileName, String attributeName) throws Throwable { featureIndex = new STRtree(); ShapefileReader reader = new ShapefileReader(new File(shapefileName)); Envelope envelope = reader.wgs84Bounds(); @@ -140,7 +126,7 @@ public void indexFeatures (String shapefileName, String attributeName) throws Th featureIndex.insert(featureGeom.getEnvelopeInternal(), feature); }); featureIndex.build(); // Index is now immutable. - ltsAttributeIndex = reader.findAttribute(attributeName, Number.class); + attributeIndex = reader.findAttribute(attributeName, Number.class); } // All the repetitive casting for multilinestring features containing a single linestring. diff --git a/src/main/java/com/conveyal/r5/shapefile/ShapefileMatcherMain.java b/src/main/java/com/conveyal/r5/shapefile/ShapefileMatcherMain.java index df86b3f9d..c9b0b7e40 100644 --- a/src/main/java/com/conveyal/r5/shapefile/ShapefileMatcherMain.java +++ b/src/main/java/com/conveyal/r5/shapefile/ShapefileMatcherMain.java @@ -20,7 +20,7 @@ public class ShapefileMatcherMain { public static void main (String[] args) throws Throwable { StreetLayer streetLayer = loadStreetLayer(); - ShapefileMatcher shapefileMatcher = new ShapefileMatcher(streetLayer); + ShapefileMatcher shapefileMatcher = new LtsMatcher(streetLayer); shapefileMatcher.match(SHAPE_FILE, SHAPE_FILE_ATTRIBUTE); } From e0077704328504060207be5a28214c933dfd49d7 Mon Sep 17 00:00:00 2001 From: ansons Date: Mon, 23 May 2022 15:59:56 -0400 Subject: [PATCH 02/13] refactor: clean up logging and unused variables --- src/main/java/com/conveyal/r5/shapefile/ShapefileMatcher.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/com/conveyal/r5/shapefile/ShapefileMatcher.java b/src/main/java/com/conveyal/r5/shapefile/ShapefileMatcher.java index 380f70f92..d9b984a96 100644 --- a/src/main/java/com/conveyal/r5/shapefile/ShapefileMatcher.java +++ b/src/main/java/com/conveyal/r5/shapefile/ShapefileMatcher.java @@ -5,7 +5,6 @@ import com.conveyal.r5.util.LambdaCounter; import com.conveyal.r5.util.ShapefileReader; import org.locationtech.jts.algorithm.distance.DiscreteHausdorffDistance; -import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.LineString; import org.locationtech.jts.geom.MultiLineString; import org.locationtech.jts.index.strtree.STRtree; @@ -69,7 +68,7 @@ public void match (String shapefileName, String attributeName) { } catch (Throwable t) { throw new RuntimeException("Could not load and index shapefile.", t); } - LOG.info("Matching edges and setting bike LTS flags..."); + LOG.info("Matching edges and setting flags..."); // Even single-threaded this is pretty fast for small extracts, but it's readily paralellized. final LambdaCounter edgePairCounter = new LambdaCounter(LOG, streets.edgeStore.nEdgePairs(), 25_000, "Edge pair {}/{}"); @@ -118,7 +117,6 @@ private SimpleFeature findBestMatch (LineString edgeGeometry) { private void indexFeatures (String shapefileName, String attributeName) throws Throwable { featureIndex = new STRtree(); ShapefileReader reader = new ShapefileReader(new File(shapefileName)); - Envelope envelope = reader.wgs84Bounds(); LOG.info("Indexing shapefile features"); // TODO add wgs84List(), pre-unwrap linestrings and attributes reader.wgs84Stream().forEach(feature -> { From 2564cb127c62017f36d9093243d7a8b2467b4ef9 Mon Sep 17 00:00:00 2001 From: ansons Date: Mon, 23 May 2022 16:05:23 -0400 Subject: [PATCH 03/13] feat: add shapefile speed matcher --- .../conveyal/r5/shapefile/SpeedMatcher.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/main/java/com/conveyal/r5/shapefile/SpeedMatcher.java diff --git a/src/main/java/com/conveyal/r5/shapefile/SpeedMatcher.java b/src/main/java/com/conveyal/r5/shapefile/SpeedMatcher.java new file mode 100644 index 000000000..d3e02922f --- /dev/null +++ b/src/main/java/com/conveyal/r5/shapefile/SpeedMatcher.java @@ -0,0 +1,25 @@ +package com.conveyal.r5.shapefile; + +import com.conveyal.r5.streets.EdgeStore; +import com.conveyal.r5.streets.StreetLayer; +import org.opengis.feature.simple.SimpleFeature; + +public class SpeedMatcher extends ShapefileMatcher { + + public SpeedMatcher (StreetLayer streets) { + super(streets); + } + + /** + * Set the speed value of an edge using a supplied feature with a speed attribute (in kilometers per hour) + */ + @Override + void setEdgePair (SimpleFeature feature, int attributeIndex, EdgeStore.Edge edge) { + if (feature.getAttribute(attributeIndex) != null) { + double speedKph = ((Number) feature.getAttribute(attributeIndex)).doubleValue(); + edge.setSpeedKph(speedKph); + edge.advance(); + edge.setSpeedKph(speedKph); + } + } +} From 039cc70d58663fa33e9d1b374493960c534a486d Mon Sep 17 00:00:00 2001 From: ansons Date: Mon, 23 May 2022 16:11:35 -0400 Subject: [PATCH 04/13] hack: use SpeedMatcher via ShapefileLts mod --- .../java/com/conveyal/r5/analyst/scenario/ShapefileLts.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/conveyal/r5/analyst/scenario/ShapefileLts.java b/src/main/java/com/conveyal/r5/analyst/scenario/ShapefileLts.java index 77d54f6ae..daf9374d2 100644 --- a/src/main/java/com/conveyal/r5/analyst/scenario/ShapefileLts.java +++ b/src/main/java/com/conveyal/r5/analyst/scenario/ShapefileLts.java @@ -4,7 +4,7 @@ import com.conveyal.analysis.datasource.DataSourceException; import com.conveyal.file.FileStorageFormat; import com.conveyal.file.FileStorageKey; -import com.conveyal.r5.shapefile.LtsMatcher; +import com.conveyal.r5.shapefile.SpeedMatcher; import com.conveyal.r5.transit.TransportNetwork; import com.conveyal.r5.transit.TransportNetworkCache; import com.conveyal.r5.util.ExceptionUtils; @@ -56,7 +56,7 @@ public boolean apply (TransportNetwork network) { // Replicate the entire flags array so we can write to it (following copy-on-write policy). // Otherwise the TIntAugmentedList only allows extending the base graph. network.streetLayer.edgeStore.flags = new TIntArrayList(network.streetLayer.edgeStore.flags); - LtsMatcher shapefileMatcher = new LtsMatcher(network.streetLayer); + SpeedMatcher shapefileMatcher = new SpeedMatcher(network.streetLayer); try { shapefileMatcher.match(localFile.getAbsolutePath(), ltsAttribute); } catch (Exception e) { From ce711eeaf26d8f9aad613ddc641b8e127ffaa388 Mon Sep 17 00:00:00 2001 From: Anson Stewart Date: Thu, 6 Apr 2023 16:16:06 -0400 Subject: [PATCH 05/13] Geotiff output: set unreachable cells to 999 --- .../com/conveyal/r5/analyst/cluster/TimeGridWriter.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/conveyal/r5/analyst/cluster/TimeGridWriter.java b/src/main/java/com/conveyal/r5/analyst/cluster/TimeGridWriter.java index c057c0912..ba79e252e 100644 --- a/src/main/java/com/conveyal/r5/analyst/cluster/TimeGridWriter.java +++ b/src/main/java/com/conveyal/r5/analyst/cluster/TimeGridWriter.java @@ -161,7 +161,11 @@ public void writeGeotiff (OutputStream out) { for (int x = 0; x < extents.width; x++) { for (int n = 0; n < travelTimeResult.nSamplesPerPoint; n++) { val = travelTimeResult.values[n][(y * extents.width + x)]; - if (val < FastRaptorWorker.UNREACHED) raster.setSample(x, y, n, val); + if (val < FastRaptorWorker.UNREACHED) { + raster.setSample(x, y, n, val); + } else { + raster.setSample(x, y, n, 999); + } } } } @@ -199,4 +203,4 @@ public void writeGeotiff (OutputStream out) { } -} \ No newline at end of file +} From 6bfa10e08fd4952169712431621149980ab6b7a9 Mon Sep 17 00:00:00 2001 From: ansons Date: Fri, 18 Aug 2023 16:04:42 -0400 Subject: [PATCH 06/13] Match only features within distance limit Without this change an edge can be matched to a feature if their bounding boxes overlap, even if they are far apart. --- .../r5/analyst/scenario/ShapefileLts.java | 6 ++-- .../r5/shapefile/ShapefileMatcher.java | 35 ++++++++++++++----- .../r5/shapefile/ShapefileMatcherMain.java | 2 +- 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/conveyal/r5/analyst/scenario/ShapefileLts.java b/src/main/java/com/conveyal/r5/analyst/scenario/ShapefileLts.java index bca945312..e5ff51cdc 100644 --- a/src/main/java/com/conveyal/r5/analyst/scenario/ShapefileLts.java +++ b/src/main/java/com/conveyal/r5/analyst/scenario/ShapefileLts.java @@ -23,8 +23,6 @@ */ public class ShapefileLts extends Modification { - public String ltsDataSource; - /** * ID of the linear shapefile DataSource containing bicycle LTS to be matched to streets. * We must assume its type because the workers don't have access to the DataStore metadata. @@ -34,6 +32,8 @@ public class ShapefileLts extends Modification { /** The name of the numeric attribute within the ltsDataSource containing LTS values from 1-4. */ public String ltsAttribute = "lts"; + public double matchLimitMeters = 3.0; + private FileStorageKey fileStorageKey; private File localFile; @@ -60,7 +60,7 @@ public boolean apply (TransportNetwork network) { network.streetLayer.edgeStore.flags = new TIntArrayList(network.streetLayer.edgeStore.flags); SpeedMatcher shapefileMatcher = new SpeedMatcher(network.streetLayer); try { - shapefileMatcher.match(localFile.getAbsolutePath(), ltsAttribute); + shapefileMatcher.match(localFile.getAbsolutePath(), ltsAttribute, matchLimitMeters); } catch (Exception e) { addError(ExceptionUtils.shortAndLongString(e)); } diff --git a/src/main/java/com/conveyal/r5/shapefile/ShapefileMatcher.java b/src/main/java/com/conveyal/r5/shapefile/ShapefileMatcher.java index d9b984a96..08740078e 100644 --- a/src/main/java/com/conveyal/r5/shapefile/ShapefileMatcher.java +++ b/src/main/java/com/conveyal/r5/shapefile/ShapefileMatcher.java @@ -1,13 +1,17 @@ package com.conveyal.r5.shapefile; +import com.conveyal.r5.common.SphericalDistanceLibrary; import com.conveyal.r5.streets.EdgeStore; import com.conveyal.r5.streets.StreetLayer; import com.conveyal.r5.util.LambdaCounter; import com.conveyal.r5.util.ShapefileReader; import org.locationtech.jts.algorithm.distance.DiscreteHausdorffDistance; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.LineString; import org.locationtech.jts.geom.MultiLineString; import org.locationtech.jts.index.strtree.STRtree; +import org.locationtech.jts.operation.distance.DistanceOp; import org.opengis.feature.simple.SimpleFeature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -55,6 +59,8 @@ public abstract class ShapefileMatcher { private StreetLayer streets; private int attributeIndex = -1; + private double matchLimitMeters; + public ShapefileMatcher (StreetLayer streets) { this.streets = streets; } @@ -62,13 +68,17 @@ public ShapefileMatcher (StreetLayer streets) { /** * Match each pair of edges in the street layer to a feature in the shapefile, then set flags on the edges. */ - public void match (String shapefileName, String attributeName) { + public void match (String shapefileName, String attributeName, double matchLimitMeters) { try { indexFeatures(shapefileName, attributeName); } catch (Throwable t) { throw new RuntimeException("Could not load and index shapefile.", t); } - LOG.info("Matching edges and setting flags..."); + + this.matchLimitMeters = matchLimitMeters; + + LOG.info("Matching edges and setting bike LTS flags..."); + // Even single-threaded this is pretty fast for small extracts, but it's readily paralellized. final LambdaCounter edgePairCounter = new LambdaCounter(LOG, streets.edgeStore.nEdgePairs(), 25_000, "Edge pair {}/{}"); @@ -87,6 +97,7 @@ public void match (String shapefileName, String attributeName) { streets.edgeStore.nEdgePairs() - edgePairCounter.getCount()); } + /** * Set the appropriate edge flag to the value of the specified attribute in the corresponding (matched) feature. * Subclasses are responsible for implementation details on which flag to set. @@ -96,18 +107,24 @@ void setEdgePair (SimpleFeature feature, int attributeIndex, EdgeStore.Edge edge } // Match metric is currently Hausdorff distance, eventually replace with something that accounts for overlap length. + // For features within the given matchLimitMeters, choose the closest based on Hausdorff distance. + // This could eventually be replaced with something that accounts for overlap length. + private SimpleFeature findBestMatch (LineString edgeGeometry) { SimpleFeature bestFeature = null; double bestDistance = Double.POSITIVE_INFINITY; List features = featureIndex.query(edgeGeometry.getEnvelopeInternal()); for (SimpleFeature feature : features) { - // Note that we're using unprojected coordinates so x distance is exaggerated realtive to y. - DiscreteHausdorffDistance dhd = new DiscreteHausdorffDistance(extractLineString(feature), edgeGeometry); - double distance = dhd.distance(); - // distance = overlap(extractLineString(feature), edgeGeometry); - if (bestDistance > distance) { - bestDistance = distance; - bestFeature = feature; + Coordinate[] nearestPoints = new DistanceOp(extractLineString(feature), edgeGeometry).nearestPoints(); + if (SphericalDistanceLibrary.fastDistance(nearestPoints[0], nearestPoints[1]) < matchLimitMeters) { + // Note that we're using unprojected coordinates so x distance is exaggerated relative to y. + DiscreteHausdorffDistance dhd = new DiscreteHausdorffDistance(extractLineString(feature), edgeGeometry); + double distance = dhd.distance(); + // distance = overlap(extractLineString(feature), edgeGeometry); + if (bestDistance > distance) { + bestDistance = distance; + bestFeature = feature; + } } } return bestFeature; diff --git a/src/main/java/com/conveyal/r5/shapefile/ShapefileMatcherMain.java b/src/main/java/com/conveyal/r5/shapefile/ShapefileMatcherMain.java index c9b0b7e40..522779f54 100644 --- a/src/main/java/com/conveyal/r5/shapefile/ShapefileMatcherMain.java +++ b/src/main/java/com/conveyal/r5/shapefile/ShapefileMatcherMain.java @@ -21,7 +21,7 @@ public class ShapefileMatcherMain { public static void main (String[] args) throws Throwable { StreetLayer streetLayer = loadStreetLayer(); ShapefileMatcher shapefileMatcher = new LtsMatcher(streetLayer); - shapefileMatcher.match(SHAPE_FILE, SHAPE_FILE_ATTRIBUTE); + shapefileMatcher.match(SHAPE_FILE, SHAPE_FILE_ATTRIBUTE, 2.0); } private static StreetLayer loadStreetLayer () { From 7b0c9860bb6d693b547b48dae6c854831e607c6c Mon Sep 17 00:00:00 2001 From: ansons Date: Tue, 5 Sep 2023 10:48:56 -0400 Subject: [PATCH 07/13] Fix import --- src/main/java/com/conveyal/r5/shapefile/LtsMatcher.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/conveyal/r5/shapefile/LtsMatcher.java b/src/main/java/com/conveyal/r5/shapefile/LtsMatcher.java index ff7d2ee4a..860812cda 100644 --- a/src/main/java/com/conveyal/r5/shapefile/LtsMatcher.java +++ b/src/main/java/com/conveyal/r5/shapefile/LtsMatcher.java @@ -4,8 +4,8 @@ import com.conveyal.r5.streets.StreetLayer; import org.opengis.feature.simple.SimpleFeature; -import static com.conveyal.r5.labeling.LevelOfTrafficStressLabeler.intToLts; import static com.conveyal.r5.streets.EdgeStore.EdgeFlag.BIKE_LTS_EXPLICIT; +import static com.conveyal.r5.streets.EdgeStore.intToLts; public class LtsMatcher extends ShapefileMatcher { From 9d7594daedae427a3a130cd30945b268ac55f454 Mon Sep 17 00:00:00 2001 From: ansons Date: Tue, 5 Sep 2023 15:26:58 -0400 Subject: [PATCH 08/13] Convert mph to kph --- src/main/java/com/conveyal/r5/shapefile/SpeedMatcher.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/conveyal/r5/shapefile/SpeedMatcher.java b/src/main/java/com/conveyal/r5/shapefile/SpeedMatcher.java index d3e02922f..73c815925 100644 --- a/src/main/java/com/conveyal/r5/shapefile/SpeedMatcher.java +++ b/src/main/java/com/conveyal/r5/shapefile/SpeedMatcher.java @@ -6,6 +6,7 @@ public class SpeedMatcher extends ShapefileMatcher { + static double KPH_PER_MPH = 1.609344; public SpeedMatcher (StreetLayer streets) { super(streets); } @@ -16,7 +17,7 @@ public SpeedMatcher (StreetLayer streets) { @Override void setEdgePair (SimpleFeature feature, int attributeIndex, EdgeStore.Edge edge) { if (feature.getAttribute(attributeIndex) != null) { - double speedKph = ((Number) feature.getAttribute(attributeIndex)).doubleValue(); + double speedKph = KPH_PER_MPH * ((Number) feature.getAttribute(attributeIndex)).doubleValue(); edge.setSpeedKph(speedKph); edge.advance(); edge.setSpeedKph(speedKph); From ed62e368dc5117398189255c285b105b9c594aab Mon Sep 17 00:00:00 2001 From: ansons Date: Sat, 30 Dec 2023 12:47:07 -0800 Subject: [PATCH 09/13] Force building car tables --- src/main/java/com/conveyal/r5/transit/TransportNetworkCache.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/conveyal/r5/transit/TransportNetworkCache.java b/src/main/java/com/conveyal/r5/transit/TransportNetworkCache.java index b7f1f1d8f..7e96d6638 100644 --- a/src/main/java/com/conveyal/r5/transit/TransportNetworkCache.java +++ b/src/main/java/com/conveyal/r5/transit/TransportNetworkCache.java @@ -201,6 +201,7 @@ private TransportNetworkConfig loadNetworkConfig (String networkId) { network.transitLayer.buildDistanceTables(null); Set buildGridsForModes = Sets.newHashSet(StreetMode.WALK); + buildGridsForModes.add(StreetMode.CAR); if (networkConfig != null && networkConfig.buildGridsForModes != null) { buildGridsForModes.addAll(networkConfig.buildGridsForModes); } From d5722b651835fd59941d1094aa78fc1a3b70103f Mon Sep 17 00:00:00 2001 From: ansons Date: Wed, 24 Jan 2024 16:04:33 -0500 Subject: [PATCH 10/13] Ignore custom cost fields in CAR routing This ignores custom costs (e.g. from elevation data) for the CAR street mode. Custom speeds for cars are handled separately. --- .../streets/MultistageTraversalTimeCalculator.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/conveyal/r5/streets/MultistageTraversalTimeCalculator.java b/src/main/java/com/conveyal/r5/streets/MultistageTraversalTimeCalculator.java index 952bb603c..382f99727 100644 --- a/src/main/java/com/conveyal/r5/streets/MultistageTraversalTimeCalculator.java +++ b/src/main/java/com/conveyal/r5/streets/MultistageTraversalTimeCalculator.java @@ -40,12 +40,14 @@ public MultistageTraversalTimeCalculator (TraversalTimeCalculator base, List Date: Wed, 24 Jan 2024 21:09:15 -0500 Subject: [PATCH 11/13] Remove unused imports --- .../r5/streets/MultistageTraversalTimeCalculator.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/main/java/com/conveyal/r5/streets/MultistageTraversalTimeCalculator.java b/src/main/java/com/conveyal/r5/streets/MultistageTraversalTimeCalculator.java index 382f99727..02ef32366 100644 --- a/src/main/java/com/conveyal/r5/streets/MultistageTraversalTimeCalculator.java +++ b/src/main/java/com/conveyal/r5/streets/MultistageTraversalTimeCalculator.java @@ -1,14 +1,8 @@ package com.conveyal.r5.streets; -import com.conveyal.r5.common.GeometryUtils; import com.conveyal.r5.profile.ProfileRequest; import com.conveyal.r5.profile.StreetMode; import com.conveyal.r5.rastercost.CostField; -import com.conveyal.r5.rastercost.SunLoader; -import org.apache.commons.math3.util.FastMath; -import org.locationtech.jts.algorithm.Angle; -import org.locationtech.jts.geom.Coordinate; -import org.locationtech.jts.geom.LineString; import org.slf4j.Logger; import org.slf4j.LoggerFactory; From e611e93a0fbf2c3066d3a0883883bde466dbf2e7 Mon Sep 17 00:00:00 2001 From: ansons Date: Wed, 24 Jan 2024 21:09:39 -0500 Subject: [PATCH 12/13] Switch streetMode conditional order --- .../conveyal/r5/streets/MultistageTraversalTimeCalculator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/conveyal/r5/streets/MultistageTraversalTimeCalculator.java b/src/main/java/com/conveyal/r5/streets/MultistageTraversalTimeCalculator.java index 02ef32366..ceb9f3226 100644 --- a/src/main/java/com/conveyal/r5/streets/MultistageTraversalTimeCalculator.java +++ b/src/main/java/com/conveyal/r5/streets/MultistageTraversalTimeCalculator.java @@ -34,7 +34,7 @@ public MultistageTraversalTimeCalculator (TraversalTimeCalculator base, List Date: Wed, 6 Nov 2024 16:53:18 -0500 Subject: [PATCH 13/13] Only process features with speed > 0 --- .../com/conveyal/r5/shapefile/SpeedMatcher.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/conveyal/r5/shapefile/SpeedMatcher.java b/src/main/java/com/conveyal/r5/shapefile/SpeedMatcher.java index 73c815925..6c918eb14 100644 --- a/src/main/java/com/conveyal/r5/shapefile/SpeedMatcher.java +++ b/src/main/java/com/conveyal/r5/shapefile/SpeedMatcher.java @@ -12,15 +12,19 @@ public SpeedMatcher (StreetLayer streets) { } /** - * Set the speed value of an edge using a supplied feature with a speed attribute (in kilometers per hour) + * Set the speed value of an edge using a supplied feature with a speed attribute (converts from miles per hour). + * If a supplied feature has speed 0 or less, it is ignored. */ @Override void setEdgePair (SimpleFeature feature, int attributeIndex, EdgeStore.Edge edge) { if (feature.getAttribute(attributeIndex) != null) { - double speedKph = KPH_PER_MPH * ((Number) feature.getAttribute(attributeIndex)).doubleValue(); - edge.setSpeedKph(speedKph); - edge.advance(); - edge.setSpeedKph(speedKph); + double speedMph = ((Number) feature.getAttribute(attributeIndex)).doubleValue(); + if (speedMph > 0) { + double speedKph = KPH_PER_MPH * speedMph; + edge.setSpeedKph(speedKph); + edge.advance(); + edge.setSpeedKph(speedKph); + } } } }