diff --git a/.github/workflows/publish-github-packages.yml b/.github/workflows/publish-github-packages.yml index fba2d03c124..83be4b2b42b 100644 --- a/.github/workflows/publish-github-packages.yml +++ b/.github/workflows/publish-github-packages.yml @@ -47,38 +47,3 @@ jobs: mvn -Dmaven.resolver.transport=wagon -B -DskipTests -Pskip-shaded-web-jar -Pskip-tools-jar source:jar deploy env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - build-only: - runs-on: ubuntu-20.04 - strategy: - fail-fast: false - matrix: - java-version: [ 18 ] - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-java@v3 - with: - java-version: ${{ matrix.java-version }} - distribution: temurin - - name: Cache Maven artifacts - uses: actions/cache@v3 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - - name: Cache node - uses: actions/cache@v3 - with: - path: web-bundle/node - key: ${{ runner.os }}-node-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os}}-node- - - name: Cache node_modules - uses: actions/cache@v3 - with: - path: web-bundle/node_modules - key: ${{ runner.os }}-node-${{ hashFiles('**/pom.xml', '**/package.json') }} - restore-keys: | - ${{ runner.os}}-node_modules- - - name: Build ${{ matrix.java-version }} - run: mvn -B clean test diff --git a/CHANGELOG.md b/CHANGELOG.md index 51099ef80d5..75e231c3a2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ - prefer cycleways, bicycle_road and cyclestreet for bike routing, see #2784 and #2778 - add support for further surfaces like pebblestones or concrete:lanes, see #2751 - reduced memory usage for urban density calculation, see #2828 +- urban density is now based on road junctions, so the according parameters need adjustment in case + the config file does not use the defaults, see #2842 ### 7.0 [14 Mar 2023] diff --git a/config-example.yml b/config-example.yml index 73b089ec769..9e20db37f9f 100644 --- a/config-example.yml +++ b/config-example.yml @@ -137,43 +137,47 @@ graphhopper: #### Urban density (built-up areas) #### - #### Country-dependent defaults for max speeds #### + #### Country-dependent defaults for max speeds #### - # This features sets a maximum speed in 'max_speed' encoded value if no maxspeed tag was found. It is country-dependent - # and based on several rules. See https://github.com/westnordost/osm-legal-default-speeds - # To use it uncomment the following, then enable urban density below and add 'country' to graph.encoded_values - # max_speed_calculator.enabled: true + # This features sets a maximum speed in 'max_speed' encoded value if no maxspeed tag was found. It is country-dependent + # and based on several rules. See https://github.com/westnordost/osm-legal-default-speeds + # To use it uncomment the following, then enable urban density below and add 'country' to graph.encoded_values + # max_speed_calculator.enabled: true - #### Urban density (built-up areas) #### + #### Urban density (built-up areas) #### - # This feature allows classifying roads into 'rural', 'residential' and 'city' areas (encoded value 'urban_density') - # Use 1 or more threads to enable the feature - # graph.urban_density.threads: 8 - # Use higher/lower sensitivities if too little/many roads fall into the according categories. - # Using smaller radii will speed up the classification, but only change these values if you know what you are doing. - # If you do not need the (rather slow) city classification set city_radius to zero. - # graph.urban_density.residential_radius: 300 - # graph.urban_density.residential_sensitivity: 60 - # graph.urban_density.city_radius: 2000 - # graph.urban_density.city_sensitivity: 30 + # This feature allows classifying roads into 'rural', 'residential' and 'city' areas (encoded value 'urban_density') + # Use 1 or more threads to enable the feature + # graph.urban_density.threads: 8 + # Use higher/lower sensitivities if too little/many roads fall into the according categories. + # Using smaller radii will speed up the classification, but only change these values if you know what you are doing. + # If you do not need the (rather slow) city classification set city_radius to zero. + # graph.urban_density.residential_radius: 400 + # graph.urban_density.residential_sensitivity: 6000 + # graph.urban_density.city_radius: 1500 + # graph.urban_density.city_sensitivity: 1000 - #### Subnetworks #### + #### Subnetworks #### - # In many cases the road network consists of independent components without any routes going in between. In - # the most simple case you can imagine an island without a bridge or ferry connection. The following parameter - # allows setting a minimum size (number of edges) for such detached components. This can be used to reduce the number - # of cases where a connection between locations might not be found. + # In many cases the road network consists of independent components without any routes going in between. In + # the most simple case you can imagine an island without a bridge or ferry connection. The following parameter + # allows setting a minimum size (number of edges) for such detached components. This can be used to reduce the number + # of cases where a connection between locations might not be found. prepare.min_network_size: 200 prepare.subnetworks.threads: 1 - #### Routing #### + #### Routing #### + + # You can define the maximum visited nodes when routing. This may result in not found connections if there is no + # connection between two points within the given visited nodes. The default is Integer.MAX_VALUE. Useful for flexibility mode + # routing.max_visited_nodes: 1000000 - # You can define the maximum visited nodes when routing. This may result in not found connections if there is no - # connection between two points within the given visited nodes. The default is Integer.MAX_VALUE. Useful for flexibility mode - # routing.max_visited_nodes: 1000000 + # The maximum time in milliseconds after which a routing request will be aborted. This has some routing algorithm + # specific caveats, but generally it should allow the prevention of long-running requests. The default is Long.MAX_VALUE + # routing.timeout_ms: 300000 # The maximum time in milliseconds after which a routing request will be aborted. This has some routing algorithm # specific caveats, but generally it should allow the prevention of long-running requests. The default is Long.MAX_VALUE diff --git a/core/src/main/java/com/graphhopper/GraphHopper.java b/core/src/main/java/com/graphhopper/GraphHopper.java index d2b2eff2af3..61e045ad1f9 100644 --- a/core/src/main/java/com/graphhopper/GraphHopper.java +++ b/core/src/main/java/com/graphhopper/GraphHopper.java @@ -114,10 +114,10 @@ public class GraphHopper { private int minNetworkSize = 200; private int subnetworksThreads = 1; // residential areas - private double residentialAreaRadius = 300; - private double residentialAreaSensitivity = 60; - private double cityAreaRadius = 2000; - private double cityAreaSensitivity = 30; + private double residentialAreaRadius = 400; + private double residentialAreaSensitivity = 6000; + private double cityAreaRadius = 1500; + private double cityAreaSensitivity = 1000; private int urbanDensityCalculationThreads = 0; // preparation handlers @@ -915,12 +915,9 @@ protected void postImport() { throw new IllegalArgumentException("Urban density calculation requires " + RoadClass.KEY); if (!encodingManager.hasEncodedValue(RoadClassLink.KEY)) throw new IllegalArgumentException("Urban density calculation requires " + RoadClassLink.KEY); - if (!encodingManager.hasEncodedValue(Country.KEY)) - throw new IllegalArgumentException("Urban density calculation requires " + Country.KEY); - EnumEncodedValue countryEnc = encodingManager.getEnumEncodedValue(Country.KEY, Country.class); EnumEncodedValue roadClassEnc = encodingManager.getEnumEncodedValue(RoadClass.KEY, RoadClass.class); BooleanEncodedValue roadClassLinkEnc = encodingManager.getBooleanEncodedValue(RoadClassLink.KEY); - UrbanDensityCalculator.calcUrbanDensity(baseGraph, urbanDensityEnc, countryEnc, roadClassEnc, + UrbanDensityCalculator.calcUrbanDensity(baseGraph, urbanDensityEnc, roadClassEnc, roadClassLinkEnc, residentialAreaRadius, residentialAreaSensitivity, cityAreaRadius, cityAreaSensitivity, urbanDensityCalculationThreads); } diff --git a/core/src/main/java/com/graphhopper/routing/querygraph/QueryOverlayBuilder.java b/core/src/main/java/com/graphhopper/routing/querygraph/QueryOverlayBuilder.java index 781b9203fdf..20098ec79b2 100644 --- a/core/src/main/java/com/graphhopper/routing/querygraph/QueryOverlayBuilder.java +++ b/core/src/main/java/com/graphhopper/routing/querygraph/QueryOverlayBuilder.java @@ -32,6 +32,8 @@ import static com.graphhopper.util.DistancePlaneProjection.DIST_PLANE; +import static com.graphhopper.util.DistancePlaneProjection.DIST_PLANE; + class QueryOverlayBuilder { private final int firstVirtualNodeId; private final int firstVirtualEdgeId; diff --git a/core/src/main/java/com/graphhopper/routing/util/RoadDensityCalculator.java b/core/src/main/java/com/graphhopper/routing/util/RoadDensityCalculator.java index 8a48fbe6d40..8bba01a2c0d 100644 --- a/core/src/main/java/com/graphhopper/routing/util/RoadDensityCalculator.java +++ b/core/src/main/java/com/graphhopper/routing/util/RoadDensityCalculator.java @@ -86,16 +86,15 @@ public double calcRoadDensity(EdgeIteratorState edge, double radius, ToDoubleFun final double radiusNormalized = DIST_PLANE.calcNormalizedDist(radius); while (!deque.isEmpty()) { int node = deque.removeFirst(); + double distance = DIST_PLANE.calcNormalizedDist(center.lat, center.lon, na.getLat(node), na.getLon(node)); + if (distance > radiusNormalized) + continue; EdgeIterator iter = edgeExplorer.setBaseNode(node); while (iter.next()) { if (visited.contains(iter.getAdjNode())) continue; visited.add(iter.getAdjNode()); - double distance = DIST_PLANE.calcNormalizedDist(center.lat, center.lon, getLat(na, iter.getBaseNode(), iter.getAdjNode()), getLon(na, iter.getBaseNode(), iter.getAdjNode())); - if (distance > radiusNormalized) - continue; - double roadLength = Math.min(2 * radius, iter.getDistance()); - totalRoadWeight += roadLength * calcRoadFactor.applyAsDouble(iter); + totalRoadWeight += calcRoadFactor.applyAsDouble(iter); deque.addLast(iter.getAdjNode()); } } diff --git a/core/src/main/java/com/graphhopper/routing/util/UrbanDensityCalculator.java b/core/src/main/java/com/graphhopper/routing/util/UrbanDensityCalculator.java index dc9657ad4f7..2cca6f447fc 100644 --- a/core/src/main/java/com/graphhopper/routing/util/UrbanDensityCalculator.java +++ b/core/src/main/java/com/graphhopper/routing/util/UrbanDensityCalculator.java @@ -43,14 +43,13 @@ public class UrbanDensityCalculator { * @param threads number of threads used to calculate the road densities */ public static void calcUrbanDensity(Graph graph, EnumEncodedValue urbanDensityEnc, - EnumEncodedValue countryEnc, EnumEncodedValue roadClassEnc, BooleanEncodedValue roadClassLinkEnc, double residentialAreaRadius, double residentialAreaSensitivity, double cityAreaRadius, double cityAreaSensitivity, int threads) { logger.info("Calculating residential areas ..., radius={}, sensitivity={}, threads={}", residentialAreaRadius, residentialAreaSensitivity, threads); StopWatch sw = StopWatch.started(); - calcResidential(graph, urbanDensityEnc, countryEnc, roadClassEnc, roadClassLinkEnc, residentialAreaRadius, residentialAreaSensitivity, threads); + calcResidential(graph, urbanDensityEnc, roadClassEnc, roadClassLinkEnc, residentialAreaRadius, residentialAreaSensitivity, threads); logger.info("Finished calculating residential areas, took: " + sw.stop().getSeconds() + "s"); if (cityAreaRadius > 1) { logger.info("Calculating city areas ..., radius={}, sensitivity={}, threads={}", cityAreaRadius, cityAreaSensitivity, threads); @@ -61,46 +60,24 @@ public static void calcUrbanDensity(Graph graph, EnumEncodedValue } private static void calcResidential(Graph graph, EnumEncodedValue urbanDensityEnc, - EnumEncodedValue countryEnc, EnumEncodedValue roadClassEnc, BooleanEncodedValue roadClassLinkEnc, double radius, double sensitivity, int threads) { final ToDoubleFunction calcRoadFactor = edge -> { - if (edge.get(roadClassLinkEnc)) - // highway exits sometimes have a 'high' density. By excluding them here we try to prevent them from - // being classified as residential areas when they are in the countryside. - return 0; RoadClass roadClass = edge.get(roadClassEnc); - switch (roadClass) { - // we're interested in the road density of urban roads, so residential areas are particularly interesting - case RESIDENTIAL: - // https://github.com/graphhopper/graphhopper/issues/2829 - return edge.get(countryEnc) == Country.USA ? 1 : 2; - case LIVING_STREET: - case FOOTWAY: - case CYCLEWAY: - case STEPS: - return 2; - case MOTORWAY: - case TRUNK: - case PRIMARY: - case SECONDARY: - case TERTIARY: - case SERVICE: - case OTHER: - return 1; - default: - return 0; - } + // we're interested in the road density of 'urban' roads, so dense road clusters of outdoor + // roads like tracks or paths and road class links should not contribute to the residential density + if (edge.get(roadClassLinkEnc) || + roadClass == RoadClass.TRACK || + roadClass == RoadClass.SERVICE || + roadClass == RoadClass.PATH || + roadClass == RoadClass.BRIDLEWAY) + return 0; + else + return 1; }; // temporarily write results to an external array for thread-safety boolean[] isResidential = new boolean[graph.getEdges()]; RoadDensityCalculator.calcRoadDensities(graph, (calculator, edge) -> { - RoadClass roadClass = edge.get(roadClassEnc); - if (roadClass == RoadClass.LIVING_STREET - || roadClass == RoadClass.RESIDENTIAL && edge.get(countryEnc) != Country.USA) { - isResidential[edge.getEdge()] = true; - return; - } double roadDensity = calculator.calcRoadDensity(edge, radius, calcRoadFactor); isResidential[edge.getEdge()] = roadDensity * sensitivity >= 1.0; }, threads); diff --git a/core/src/main/java/com/graphhopper/routing/util/VehicleEncodedValues.java b/core/src/main/java/com/graphhopper/routing/util/VehicleEncodedValues.java index fcf0a5ec237..bdb7a5a8922 100644 --- a/core/src/main/java/com/graphhopper/routing/util/VehicleEncodedValues.java +++ b/core/src/main/java/com/graphhopper/routing/util/VehicleEncodedValues.java @@ -148,13 +148,13 @@ public String getName() { public String toString() { return getName(); } - + + public static VehicleEncodedValues matrixbike(PMap properties) { return bike(new PMap(properties).putObject("name", properties.getString("name", "matrixbike"))); } + public static VehicleEncodedValues matrixcar(PMap properties) { return car(new PMap(properties).putObject("name", properties.getString("name", "matrixcar"))); } - -} - +} \ No newline at end of file diff --git a/core/src/main/java/com/graphhopper/routing/util/VehicleTagParsers.java b/core/src/main/java/com/graphhopper/routing/util/VehicleTagParsers.java index 57b1f31365a..fa01aec651a 100644 --- a/core/src/main/java/com/graphhopper/routing/util/VehicleTagParsers.java +++ b/core/src/main/java/com/graphhopper/routing/util/VehicleTagParsers.java @@ -47,14 +47,6 @@ public static VehicleTagParsers car(EncodedValueLookup lookup, PMap properties) ); } - public static VehicleTagParsers matrixcar(EncodedValueLookup lookup, PMap properties) { - return new VehicleTagParsers( - new MatrixCarAccessParser(lookup, properties).init(properties.getObject("date_range_parser", new DateRangeParser())), - new MatrixCarAverageSpeedParser(lookup, properties), - null - ); - } - public static VehicleTagParsers bike(EncodedValueLookup lookup, PMap properties) { return new VehicleTagParsers( new BikeAccessParser(lookup, properties).init(properties.getObject("date_range_parser", new DateRangeParser())), @@ -63,14 +55,6 @@ public static VehicleTagParsers bike(EncodedValueLookup lookup, PMap properties) ); } - public static VehicleTagParsers matrixbike(EncodedValueLookup lookup, PMap properties) { - return new VehicleTagParsers( - new MatrixBikeAccessParser(lookup, properties).init(properties.getObject("date_range_parser", new DateRangeParser())), - new MatrixBikeAverageSpeedParser(lookup, properties), - new MatrixBikePriorityParser(lookup, properties) - ); - } - public static VehicleTagParsers racingbike(EncodedValueLookup lookup, PMap properties) { return new VehicleTagParsers( new RacingBikeAccessParser(lookup, properties).init(properties.getObject("date_range_parser", new DateRangeParser())), @@ -103,7 +87,21 @@ public static VehicleTagParsers wheelchair(EncodedValueLookup lookup, PMap prope ); } + public static VehicleTagParsers matrixbike(EncodedValueLookup lookup, PMap properties) { + return new VehicleTagParsers( + new MatrixBikeAccessParser(lookup, properties).init(properties.getObject("date_range_parser", new DateRangeParser())), + new MatrixBikeAverageSpeedParser(lookup, properties), + new MatrixBikePriorityParser(lookup, properties) + ); + } + public static VehicleTagParsers matrixcar(EncodedValueLookup lookup, PMap properties) { + return new VehicleTagParsers( + new MatrixCarAccessParser(lookup, properties).init(properties.getObject("date_range_parser", new DateRangeParser())), + new MatrixCarAverageSpeedParser(lookup, properties), + null + ); + } public VehicleTagParsers(TagParser accessParser, TagParser speedParser, TagParser priorityParser) { this.accessParser = accessParser; diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/WheelchairAccessParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/WheelchairAccessParser.java index 98235915ac1..a0836292904 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/WheelchairAccessParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/WheelchairAccessParser.java @@ -126,4 +126,4 @@ public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way accessEnc.setBool(false, edgeId, edgeIntAccess, true); accessEnc.setBool(true, edgeId, edgeIntAccess, true); } -} \ No newline at end of file +} diff --git a/core/src/main/java/com/graphhopper/storage/CHStorageBuilder.java b/core/src/main/java/com/graphhopper/storage/CHStorageBuilder.java index 172feafd49d..87890e61229 100644 --- a/core/src/main/java/com/graphhopper/storage/CHStorageBuilder.java +++ b/core/src/main/java/com/graphhopper/storage/CHStorageBuilder.java @@ -87,7 +87,7 @@ public int addShortcutEdgeBased(int a, int b, int accessFlags, double weight, in } public int addShortcutEdgeBased(int a, int b, int accessFlags, double weight, double distance, long time, - int skippedEdge1, int skippedEdge2, + int skippedEdge1, int skippedEdge2, int origFirst, int origLast) { checkNewShortcut(a, b); int shortcut = storage.shortcutEdgeBased(a, b, accessFlags, weight, distance,time, skippedEdge1, skippedEdge2, origFirst, origLast); diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/AbstractBikeTagParserTester.java b/core/src/test/java/com/graphhopper/routing/util/parsers/AbstractBikeTagParserTester.java index 4eedd14a55f..d3b3a8d6b6d 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/AbstractBikeTagParserTester.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/AbstractBikeTagParserTester.java @@ -670,4 +670,4 @@ private void assertAccess(ReaderWay way, boolean fwd, boolean bwd) { if (fwd) assertTrue(accessEnc.getBool(false, edge, edgeIntAccess)); if (bwd) assertTrue(accessEnc.getBool(true, edge, edgeIntAccess)); } -} \ No newline at end of file +} diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/BikeTagParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/BikeTagParserTest.java index b1be74eb4c0..ab2baba8ca2 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/BikeTagParserTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/BikeTagParserTest.java @@ -596,4 +596,4 @@ public void testAvoidMotorway() { assertPriority(REACH_DESTINATION, osmWay); } -} \ No newline at end of file +} diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/CarTagParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/CarTagParserTest.java index 26651cd6af4..c2dbb4da03e 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/CarTagParserTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/CarTagParserTest.java @@ -730,4 +730,4 @@ void footway_etc_not_allowed_despite_vehicle_yes(String vehicle) { assertEquals(WayAccess.CAN_SKIP, parser.getAccess(way)); } } -} \ No newline at end of file +} diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/FootTagParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/FootTagParserTest.java index 8ae6c6b1ee0..83dd1769706 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/FootTagParserTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/FootTagParserTest.java @@ -557,4 +557,4 @@ public void maxSpeed() { // note that this test made more sense when we used encoders that defined a max speed. assertEquals(16, speedEnc.getNextStorableValue(15)); } -} \ No newline at end of file +} diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/MountainBikeTagParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/MountainBikeTagParserTest.java index 49974b7841c..acc46340994 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/MountainBikeTagParserTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/MountainBikeTagParserTest.java @@ -190,4 +190,4 @@ public void testBarrierAccess() { assertFalse(accessParser.isBarrier(node)); } -} \ No newline at end of file +} diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/RacingBikeTagParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/RacingBikeTagParserTest.java index bd4c196e5d6..015d47977b6 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/RacingBikeTagParserTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/RacingBikeTagParserTest.java @@ -291,4 +291,4 @@ public void testClassBicycle() { way.setTag("class:bicycle", "-2"); assertPriority(BEST, way); } -} \ No newline at end of file +} diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/WheelchairTagParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/WheelchairTagParserTest.java index f99e0a64e5f..60a3d77d88f 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/WheelchairTagParserTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/WheelchairTagParserTest.java @@ -588,4 +588,4 @@ public void testApplyWayTags() { assertEquals(0, edge45.get(wheelchairAvSpeedEnc), 0.1); assertEquals(0, edge45.getReverse(wheelchairAvSpeedEnc), 0.1); } -} \ No newline at end of file +} diff --git a/web/src/test/java/com/graphhopper/application/resources/RouteResourceTest.java b/web/src/test/java/com/graphhopper/application/resources/RouteResourceTest.java index 5eadebf4a35..4f233d4ba71 100644 --- a/web/src/test/java/com/graphhopper/application/resources/RouteResourceTest.java +++ b/web/src/test/java/com/graphhopper/application/resources/RouteResourceTest.java @@ -367,11 +367,11 @@ public void testPathDetailsWithoutGraphHopperWeb() { assertEquals("[34,38,50.0]", maxSpeed.get(2).toString()); assertEquals("[38,50,90.0]", maxSpeed.get(3).toString()); assertEquals("[50,52,50.0]", maxSpeed.get(4).toString()); - assertEquals("[52,78,90.0]", maxSpeed.get(5).toString()); + assertEquals("[52,60,90.0]", maxSpeed.get(5).toString()); JsonNode urbanDensityNode = details.get("urban_density"); assertEquals("[0,53,\"residential\"]", urbanDensityNode.get(0).toString()); - assertEquals("[53,68,\"rural\"]", urbanDensityNode.get(1).toString()); + assertEquals("[53,57,\"rural\"]", urbanDensityNode.get(1).toString()); } @Test