diff --git a/build.gradle b/build.gradle index e959361d65c..dc4d721e8ef 100644 --- a/build.gradle +++ b/build.gradle @@ -42,6 +42,7 @@ task testReport(type: TestReport, group: 'Build') { allprojects { repositories { + mavenLocal() google() mavenCentral() maven { diff --git a/changelog/unreleased/features/dd.md b/changelog/unreleased/features/dd.md new file mode 100644 index 00000000000..4ed12a068f2 --- /dev/null +++ b/changelog/unreleased/features/dd.md @@ -0,0 +1 @@ +- Added `MapboxRouteLineOptions#displayViolatedSections` option and `RouteLineColorResources#violatedSectionColor`, `RouteLineColorResources#alternativeRouteViolatedSectionColor`, `RouteLineResources#violatedSectionDashArray`, `RouteLineResources#violatedSectionOpacity`, `RouteLineResources#violatedSectionLineWidth` to modify the route line style of violated sections. The section is considered violated when some of the restricting request parameters (e. g. `maxHeight`, `maxWidth`, `exclude`, etc.) were violated. \ No newline at end of file diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index a6e1905ae91..062cb86afe8 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -19,7 +19,7 @@ ext { version = [ mapboxMapSdk : '10.13.0-rc.1', - mapboxSdkServices : '6.11.0', + mapboxSdkServices : '6.12.0-dzina', mapboxNavigator : "${mapboxNavigatorVersion}", mapboxCommonNative : '23.5.0-rc.1', mapboxCrashMonitor : '2.0.0', diff --git a/libnavui-maps/api/current.txt b/libnavui-maps/api/current.txt index 91d749190fb..ebcf0c15e9c 100644 --- a/libnavui-maps/api/current.txt +++ b/libnavui-maps/api/current.txt @@ -1087,6 +1087,7 @@ package com.mapbox.navigation.ui.maps.route.line.model { method public android.graphics.drawable.Drawable getDestinationIcon(); method public boolean getDisplayRestrictedRoadSections(); method public boolean getDisplaySoftGradientForTraffic(); + method public boolean getDisplayViolatedSections(); method public com.mapbox.maps.extension.style.layers.properties.generated.IconPitchAlignment getIconPitchAlignment(); method public double getLineDepthOcclusionFactor(); method public android.graphics.drawable.Drawable getOriginIcon(); @@ -1104,6 +1105,7 @@ package com.mapbox.navigation.ui.maps.route.line.model { property public final android.graphics.drawable.Drawable destinationIcon; property public final boolean displayRestrictedRoadSections; property public final boolean displaySoftGradientForTraffic; + property public final boolean displayViolatedSections; property public final com.mapbox.maps.extension.style.layers.properties.generated.IconPitchAlignment iconPitchAlignment; property public final double lineDepthOcclusionFactor; property public final android.graphics.drawable.Drawable originIcon; @@ -1124,6 +1126,7 @@ package com.mapbox.navigation.ui.maps.route.line.model { method public com.mapbox.navigation.ui.maps.route.line.model.MapboxRouteLineOptions build(); method public com.mapbox.navigation.ui.maps.route.line.model.MapboxRouteLineOptions.Builder displayRestrictedRoadSections(boolean displayRestrictedRoadSections); method public com.mapbox.navigation.ui.maps.route.line.model.MapboxRouteLineOptions.Builder displaySoftGradientForTraffic(boolean enable); + method public com.mapbox.navigation.ui.maps.route.line.model.MapboxRouteLineOptions.Builder displayViolatedSections(boolean displayViolatedSections); method public com.mapbox.navigation.ui.maps.route.line.model.MapboxRouteLineOptions.Builder iconPitchAlignment(com.mapbox.maps.extension.style.layers.properties.generated.IconPitchAlignment iconPitchAlignment); method @com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI public com.mapbox.navigation.ui.maps.route.line.model.MapboxRouteLineOptions.Builder lineDepthOcclusionFactor(@FloatRange(from=0.0, to=1.0) double lineDepthOcclusionFactor); method @com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI public com.mapbox.navigation.ui.maps.route.line.model.MapboxRouteLineOptions.Builder shareLineGeometrySources(boolean shareLineGeometrySources); @@ -1199,6 +1202,7 @@ package com.mapbox.navigation.ui.maps.route.line.model { method public int getAlternativeRouteRestrictedRoadColor(); method public int getAlternativeRouteSevereCongestionColor(); method public int getAlternativeRouteUnknownCongestionColor(); + method public int getAlternativeRouteViolatedSectionColor(); method public kotlin.ranges.IntRange getHeavyCongestionRange(); method public int getInActiveRouteLegsColor(); method public kotlin.ranges.IntRange getLowCongestionRange(); @@ -1215,6 +1219,7 @@ package com.mapbox.navigation.ui.maps.route.line.model { method public int getRouteSevereCongestionColor(); method public int getRouteUnknownCongestionColor(); method public kotlin.ranges.IntRange getSevereCongestionRange(); + method public int getViolatedSectionColor(); method public com.mapbox.navigation.ui.maps.route.line.model.RouteLineColorResources.Builder toBuilder(); property public final int alternativeRouteCasingColor; property public final int alternativeRouteClosureColor; @@ -1225,6 +1230,7 @@ package com.mapbox.navigation.ui.maps.route.line.model { property public final int alternativeRouteRestrictedRoadColor; property public final int alternativeRouteSevereCongestionColor; property public final int alternativeRouteUnknownCongestionColor; + property public final int alternativeRouteViolatedSectionColor; property public final kotlin.ranges.IntRange heavyCongestionRange; property public final int inActiveRouteLegsColor; property public final kotlin.ranges.IntRange lowCongestionRange; @@ -1241,6 +1247,7 @@ package com.mapbox.navigation.ui.maps.route.line.model { property public final int routeSevereCongestionColor; property public final int routeUnknownCongestionColor; property public final kotlin.ranges.IntRange severeCongestionRange; + property public final int violatedSectionColor; } public static final class RouteLineColorResources.Builder { @@ -1254,6 +1261,7 @@ package com.mapbox.navigation.ui.maps.route.line.model { method public com.mapbox.navigation.ui.maps.route.line.model.RouteLineColorResources.Builder alternativeRouteRestrictedRoadColor(@ColorInt int color); method public com.mapbox.navigation.ui.maps.route.line.model.RouteLineColorResources.Builder alternativeRouteSevereCongestionColor(@ColorInt int color); method public com.mapbox.navigation.ui.maps.route.line.model.RouteLineColorResources.Builder alternativeRouteUnknownCongestionColor(@ColorInt int color); + method public com.mapbox.navigation.ui.maps.route.line.model.RouteLineColorResources.Builder alternativeRouteViolatedSectionColor(@ColorInt int color); method public com.mapbox.navigation.ui.maps.route.line.model.RouteLineColorResources build(); method public com.mapbox.navigation.ui.maps.route.line.model.RouteLineColorResources.Builder heavyCongestionRange(kotlin.ranges.IntRange range); method public com.mapbox.navigation.ui.maps.route.line.model.RouteLineColorResources.Builder inActiveRouteLegsColor(@ColorInt int color); @@ -1271,6 +1279,7 @@ package com.mapbox.navigation.ui.maps.route.line.model { method public com.mapbox.navigation.ui.maps.route.line.model.RouteLineColorResources.Builder routeSevereCongestionColor(@ColorInt int color); method public com.mapbox.navigation.ui.maps.route.line.model.RouteLineColorResources.Builder routeUnknownCongestionColor(@ColorInt int color); method public com.mapbox.navigation.ui.maps.route.line.model.RouteLineColorResources.Builder severeCongestionRange(kotlin.ranges.IntRange range); + method public com.mapbox.navigation.ui.maps.route.line.model.RouteLineColorResources.Builder violatedSectionColor(@ColorInt int color); } public final class RouteLineData { @@ -1310,6 +1319,7 @@ package com.mapbox.navigation.ui.maps.route.line.model { method public com.mapbox.navigation.ui.maps.route.line.model.RouteLineExpressionProvider? getTrailCasingExpressionProvider(); method public com.mapbox.navigation.ui.maps.route.line.model.RouteLineExpressionProvider? getTrailExpressionProvider(); method public com.mapbox.navigation.ui.maps.route.line.model.RouteLineTrimOffset? getTrimOffset(); + method public com.mapbox.navigation.ui.maps.route.line.model.RouteLineExpressionProvider? getViolatedSectionExpressionProvider(); method public com.mapbox.navigation.ui.maps.route.line.model.RouteLineDynamicData.MutableRouteLineDynamicData toMutableValue(); property public final com.mapbox.navigation.ui.maps.route.line.model.RouteLineExpressionProvider baseExpressionProvider; property public final com.mapbox.navigation.ui.maps.route.line.model.RouteLineExpressionProvider casingExpressionProvider; @@ -1318,6 +1328,7 @@ package com.mapbox.navigation.ui.maps.route.line.model { property public final com.mapbox.navigation.ui.maps.route.line.model.RouteLineExpressionProvider? trailCasingExpressionProvider; property public final com.mapbox.navigation.ui.maps.route.line.model.RouteLineExpressionProvider? trailExpressionProvider; property public final com.mapbox.navigation.ui.maps.route.line.model.RouteLineTrimOffset? trimOffset; + property public final com.mapbox.navigation.ui.maps.route.line.model.RouteLineExpressionProvider? violatedSectionExpressionProvider; } public static final class RouteLineDynamicData.MutableRouteLineDynamicData { @@ -1328,6 +1339,7 @@ package com.mapbox.navigation.ui.maps.route.line.model { method public com.mapbox.navigation.ui.maps.route.line.model.RouteLineExpressionProvider? getTrailCasingExpressionProvider(); method public com.mapbox.navigation.ui.maps.route.line.model.RouteLineExpressionProvider? getTrailExpressionProvider(); method public com.mapbox.navigation.ui.maps.route.line.model.RouteLineTrimOffset? getTrimOffset(); + method public com.mapbox.navigation.ui.maps.route.line.model.RouteLineExpressionProvider? getViolatedSectionExpressionProvider(); method public void setBaseExpressionProvider(com.mapbox.navigation.ui.maps.route.line.model.RouteLineExpressionProvider); method public void setCasingExpressionProvider(com.mapbox.navigation.ui.maps.route.line.model.RouteLineExpressionProvider); method public void setRestrictedSectionExpressionProvider(com.mapbox.navigation.ui.maps.route.line.model.RouteLineExpressionProvider?); @@ -1335,6 +1347,7 @@ package com.mapbox.navigation.ui.maps.route.line.model { method public void setTrailCasingExpressionProvider(com.mapbox.navigation.ui.maps.route.line.model.RouteLineExpressionProvider?); method public void setTrailExpressionProvider(com.mapbox.navigation.ui.maps.route.line.model.RouteLineExpressionProvider?); method public void setTrimOffset(com.mapbox.navigation.ui.maps.route.line.model.RouteLineTrimOffset?); + method public void setViolatedSectionExpressionProvider(com.mapbox.navigation.ui.maps.route.line.model.RouteLineExpressionProvider?); method public com.mapbox.navigation.ui.maps.route.line.model.RouteLineDynamicData toImmutableValue(); property public final com.mapbox.navigation.ui.maps.route.line.model.RouteLineExpressionProvider baseExpressionProvider; property public final com.mapbox.navigation.ui.maps.route.line.model.RouteLineExpressionProvider casingExpressionProvider; @@ -1343,6 +1356,7 @@ package com.mapbox.navigation.ui.maps.route.line.model { property public final com.mapbox.navigation.ui.maps.route.line.model.RouteLineExpressionProvider? trailCasingExpressionProvider; property public final com.mapbox.navigation.ui.maps.route.line.model.RouteLineExpressionProvider? trailExpressionProvider; property public final com.mapbox.navigation.ui.maps.route.line.model.RouteLineTrimOffset? trimOffset; + property public final com.mapbox.navigation.ui.maps.route.line.model.RouteLineExpressionProvider? violatedSectionExpressionProvider; } public final class RouteLineError { @@ -1382,6 +1396,9 @@ package com.mapbox.navigation.ui.maps.route.line.model { method public com.mapbox.maps.extension.style.expressions.generated.Expression getRouteLineScaleExpression(); method public com.mapbox.maps.extension.style.expressions.generated.Expression getRouteTrafficLineScaleExpression(); method public java.util.List getTrafficBackfillRoadClasses(); + method public java.util.List getViolatedSectionDashArray(); + method public double getViolatedSectionLineWidth(); + method public double getViolatedSectionOpacity(); method public com.mapbox.navigation.ui.maps.route.line.model.RouteLineResources.Builder toBuilder(); property public final com.mapbox.maps.extension.style.expressions.generated.Expression alternativeRouteCasingLineScaleExpression; property public final com.mapbox.maps.extension.style.expressions.generated.Expression alternativeRouteLineScaleExpression; @@ -1397,6 +1414,9 @@ package com.mapbox.navigation.ui.maps.route.line.model { property public final com.mapbox.maps.extension.style.expressions.generated.Expression routeLineScaleExpression; property public final com.mapbox.maps.extension.style.expressions.generated.Expression routeTrafficLineScaleExpression; property public final java.util.List trafficBackfillRoadClasses; + property public final java.util.List violatedSectionDashArray; + property public final double violatedSectionLineWidth; + property public final double violatedSectionOpacity; } public static final class RouteLineResources.Builder { @@ -1416,6 +1436,9 @@ package com.mapbox.navigation.ui.maps.route.line.model { method public com.mapbox.navigation.ui.maps.route.line.model.RouteLineResources.Builder routeLineScaleExpression(com.mapbox.maps.extension.style.expressions.generated.Expression expression); method public com.mapbox.navigation.ui.maps.route.line.model.RouteLineResources.Builder routeTrafficLineScaleExpression(com.mapbox.maps.extension.style.expressions.generated.Expression expression); method public com.mapbox.navigation.ui.maps.route.line.model.RouteLineResources.Builder trafficBackfillRoadClasses(java.util.List roadClasses); + method public com.mapbox.navigation.ui.maps.route.line.model.RouteLineResources.Builder violatedSectionDashArray(java.util.List dashArray); + method public com.mapbox.navigation.ui.maps.route.line.model.RouteLineResources.Builder violatedSectionLineWidth(double width); + method public com.mapbox.navigation.ui.maps.route.line.model.RouteLineResources.Builder violatedSectionOpacity(double opacity); } public final class RouteLineScaleValue { diff --git a/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/internal/route/line/MapboxRouteLineUtils.kt b/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/internal/route/line/MapboxRouteLineUtils.kt index 0b218a96e45..8f8107406ab 100644 --- a/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/internal/route/line/MapboxRouteLineUtils.kt +++ b/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/internal/route/line/MapboxRouteLineUtils.kt @@ -75,6 +75,7 @@ internal object MapboxRouteLineUtils { private const val LOG_CATEGORY = "MapboxRouteLineUtils" internal const val VANISH_POINT_STOP_GAP = .00000000001 private const val NUMBER_OF_SUPPORTED_ROUTES = 3 + private const val TOLERANCE = 0.00000001 internal val extractRouteDataCache: LruCache< CacheResultUtils.CacheResultKeyRouteTraffic< @@ -97,7 +98,8 @@ internal object MapboxRouteLineUtils { RouteLayerConstants.LAYER_GROUP_1_CASING, RouteLayerConstants.LAYER_GROUP_1_MAIN, RouteLayerConstants.LAYER_GROUP_1_TRAFFIC, - RouteLayerConstants.LAYER_GROUP_1_RESTRICTED + RouteLayerConstants.LAYER_GROUP_1_RESTRICTED, + RouteLayerConstants.LAYER_GROUP_1_VIOLATED, ) // ordering is important @@ -107,7 +109,8 @@ internal object MapboxRouteLineUtils { RouteLayerConstants.LAYER_GROUP_2_CASING, RouteLayerConstants.LAYER_GROUP_2_MAIN, RouteLayerConstants.LAYER_GROUP_2_TRAFFIC, - RouteLayerConstants.LAYER_GROUP_2_RESTRICTED + RouteLayerConstants.LAYER_GROUP_2_RESTRICTED, + RouteLayerConstants.LAYER_GROUP_2_VIOLATED, ) // ordering is important @@ -117,7 +120,8 @@ internal object MapboxRouteLineUtils { RouteLayerConstants.LAYER_GROUP_3_CASING, RouteLayerConstants.LAYER_GROUP_3_MAIN, RouteLayerConstants.LAYER_GROUP_3_TRAFFIC, - RouteLayerConstants.LAYER_GROUP_3_RESTRICTED + RouteLayerConstants.LAYER_GROUP_3_RESTRICTED, + RouteLayerConstants.LAYER_GROUP_3_VIOLATED, ) // ordering is important @@ -127,7 +131,8 @@ internal object MapboxRouteLineUtils { RouteLayerConstants.MASKING_LAYER_CASING, RouteLayerConstants.MASKING_LAYER_MAIN, RouteLayerConstants.MASKING_LAYER_TRAFFIC, - RouteLayerConstants.MASKING_LAYER_RESTRICTED + RouteLayerConstants.MASKING_LAYER_RESTRICTED, + RouteLayerConstants.MASKING_LAYER_VIOLATED, ) val sourceLayerMap = mapOf>( @@ -606,6 +611,59 @@ internal object MapboxRouteLineUtils { return itemsToReturn } + internal fun extractViolatedSectionsData( + route: NavigationRoute, + distancesProvider: (NavigationRoute) -> RouteLineGranularDistances? + ): List { + val itemsToReturn = mutableListOf() + val granularDistances = distancesProvider(route) + route.directionsRoute.legs()?.forEachIndexed { legIndex, leg -> + val legNotificationIndices = extractNotificationIndices(leg) + val legDistances = granularDistances?.legsDistances?.getOrNull(legIndex) + val routeDistance = granularDistances?.completeDistance + legNotificationIndices.forEach { (startIndex, endIndex) -> + val startRouteLineDistance = legDistances?.getOrNull(startIndex) + val endRouteLineDistance = legDistances?.getOrNull(endIndex) + if (startRouteLineDistance != null && endRouteLineDistance != null) { + val startOffset = 1.0 - startRouteLineDistance.distanceRemaining / routeDistance!! + val endOffset = 1.0 - endRouteLineDistance.distanceRemaining / routeDistance + if (startOffset in 0.0..1.0 && endOffset in 0.0..1.0) { + if (itemsToReturn.isEmpty() && startIndex >= TOLERANCE) { + itemsToReturn.add(ExtractedRouteRestrictionData(0.0, false, 0)) + } + itemsToReturn.add( + ExtractedRouteRestrictionData( + startOffset, + true, + legIndex + ) + ) + itemsToReturn.add( + ExtractedRouteRestrictionData( + endOffset, + false, + legIndex + ) + ) + } + } + } + } + return itemsToReturn + } + + private fun extractNotificationIndices(leg: RouteLeg): List> { + val result = mutableListOf>() + leg.notifications()?.forEach { notification -> + val startIndex = notification.geometryIndexStart() + val endIndex = notification.geometryIndexEnd() + if (startIndex != null && endIndex != null) { + result.add(startIndex to endIndex) + } + } + return result + } + /** * Filters the [RouteLeg] for intersections that are designated as restricted. If there are * no restricted intersections null is returned. If there is at least one restricted intersection @@ -1262,6 +1320,27 @@ internal object MapboxRouteLineUtils { } } } + if (options.displayViolatedSections) { + if (!style.styleLayerExists(RouteLayerConstants.LAYER_GROUP_3_VIOLATED)) { + LineLayer( + RouteLayerConstants.LAYER_GROUP_3_VIOLATED, + RouteLayerConstants.LAYER_GROUP_3_SOURCE_ID + ) + .lineWidth(options.resourceProvider.violatedSectionLineWidth) + .lineJoin(LineJoin.ROUND) + .lineOpacity(options.resourceProvider.violatedSectionOpacity) + .lineColor(options.resourceProvider.routeLineColorResources.violatedSectionColor) + .lineDasharray(options.resourceProvider.violatedSectionDashArray) + .lineCap(LineCap.ROUND) + .apply { + style.addPersistentLayer(this, LayerPosition(null, belowLayerIdToUse, null)) + style.layerLineDepthOcclusionFactor( + layerId, + options.lineDepthOcclusionFactor + ) + } + } + } if (!style.styleLayerExists(RouteLayerConstants.LAYER_GROUP_2_TRAIL_CASING)) { LineLayer( @@ -1349,6 +1428,26 @@ internal object MapboxRouteLineUtils { } } } + if (options.displayViolatedSections) { + if (!style.styleLayerExists(RouteLayerConstants.LAYER_GROUP_2_VIOLATED)) { + LineLayer( + RouteLayerConstants.LAYER_GROUP_2_VIOLATED, + RouteLayerConstants.LAYER_GROUP_2_SOURCE_ID + ) + .lineWidth(options.resourceProvider.violatedSectionLineWidth) + .lineJoin(LineJoin.ROUND) + .lineOpacity(options.resourceProvider.violatedSectionOpacity) + .lineColor(options.resourceProvider.routeLineColorResources.violatedSectionColor) + .lineDasharray(options.resourceProvider.violatedSectionDashArray) + .apply { + style.addPersistentLayer(this, LayerPosition(null, belowLayerIdToUse, null)) + style.layerLineDepthOcclusionFactor( + layerId, + options.lineDepthOcclusionFactor + ) + } + } + } if (!style.styleLayerExists(RouteLayerConstants.LAYER_GROUP_1_TRAIL_CASING)) { LineLayer( @@ -1436,6 +1535,27 @@ internal object MapboxRouteLineUtils { } } } + if (options.displayViolatedSections) { + if (!style.styleLayerExists(RouteLayerConstants.LAYER_GROUP_1_VIOLATED)) { + LineLayer( + RouteLayerConstants.LAYER_GROUP_1_VIOLATED, + RouteLayerConstants.LAYER_GROUP_1_SOURCE_ID + ) + .lineWidth(options.resourceProvider.violatedSectionLineWidth) + .lineJoin(LineJoin.ROUND) + .lineOpacity(options.resourceProvider.violatedSectionOpacity) + .lineColor(options.resourceProvider.routeLineColorResources.violatedSectionColor) + .lineDasharray(options.resourceProvider.violatedSectionDashArray) + .lineCap(LineCap.ROUND) + .apply { + style.addPersistentLayer(this, LayerPosition(null, belowLayerIdToUse, null)) + style.layerLineDepthOcclusionFactor( + layerId, + options.lineDepthOcclusionFactor + ) + } + } + } if (!style.styleLayerExists(RouteLayerConstants.MASKING_LAYER_TRAIL_CASING)) { LineLayer( @@ -1523,6 +1643,27 @@ internal object MapboxRouteLineUtils { } } } + if (options.displayViolatedSections) { + if (!style.styleLayerExists(RouteLayerConstants.MASKING_LAYER_VIOLATED)) { + LineLayer( + RouteLayerConstants.MASKING_LAYER_VIOLATED, + RouteLayerConstants.LAYER_GROUP_1_SOURCE_ID + ) + .lineWidth(options.resourceProvider.violatedSectionLineWidth) + .lineJoin(LineJoin.ROUND) + .lineOpacity(options.resourceProvider.violatedSectionOpacity) + .lineColor(options.resourceProvider.routeLineColorResources.violatedSectionColor) + .lineDasharray(options.resourceProvider.violatedSectionDashArray) + .lineCap(LineCap.ROUND) + .apply { + style.addPersistentLayer(this, LayerPosition(null, belowLayerIdToUse, null)) + style.layerLineDepthOcclusionFactor( + layerId, + options.lineDepthOcclusionFactor + ) + } + } + } if (!style.styleLayerExists(RouteLayerConstants.TOP_LEVEL_ROUTE_LINE_LAYER_ID)) { style.addPersistentLayer( BackgroundLayer( @@ -1629,6 +1770,14 @@ internal object MapboxRouteLineUtils { style.styleLayerExists(RouteLayerConstants.LAYER_GROUP_3_RESTRICTED) } else { true + } && + if (options.displayViolatedSections) { + style.styleLayerExists(RouteLayerConstants.MASKING_LAYER_VIOLATED) && + style.styleLayerExists(RouteLayerConstants.LAYER_GROUP_1_VIOLATED) && + style.styleLayerExists(RouteLayerConstants.LAYER_GROUP_2_VIOLATED) && + style.styleLayerExists(RouteLayerConstants.LAYER_GROUP_3_VIOLATED) + } else { + true } } @@ -1690,12 +1839,12 @@ internal object MapboxRouteLineUtils { routeData: List, vanishingPointOffset: Double, activeLegIndex: Int, - routeLineColorResources: RouteLineColorResources + @ColorInt color: Int, ) = RouteLineExpressionProvider { getRestrictedLineExpression( vanishingPointOffset, activeLegIndex, - routeLineColorResources.restrictedRoadColor, + color, routeData ) } @@ -1912,6 +2061,8 @@ internal object MapboxRouteLineUtils { RouteLineTrimExpressionProvider { trimmedOffsetExpression }, restrictedSectionExpressionProvider = RouteLineTrimExpressionProvider { trimmedOffsetExpression }, + violatedSectionExpressionProvider = + RouteLineTrimExpressionProvider { trimmedOffsetExpression }, trailExpressionProvider = { trimmedOffsetExpression }, trailCasingExpressionProvider = { trimmedOffsetExpression }, ) @@ -1943,24 +2094,28 @@ internal object MapboxRouteLineUtils { style.removeStyleLayer(RouteLayerConstants.LAYER_GROUP_1_MAIN) style.removeStyleLayer(RouteLayerConstants.LAYER_GROUP_1_TRAFFIC) style.removeStyleLayer(RouteLayerConstants.LAYER_GROUP_1_RESTRICTED) + style.removeStyleLayer(RouteLayerConstants.LAYER_GROUP_1_VIOLATED) style.removeStyleLayer(RouteLayerConstants.LAYER_GROUP_2_TRAIL_CASING) style.removeStyleLayer(RouteLayerConstants.LAYER_GROUP_2_TRAIL) style.removeStyleLayer(RouteLayerConstants.LAYER_GROUP_2_CASING) style.removeStyleLayer(RouteLayerConstants.LAYER_GROUP_2_MAIN) style.removeStyleLayer(RouteLayerConstants.LAYER_GROUP_2_TRAFFIC) style.removeStyleLayer(RouteLayerConstants.LAYER_GROUP_2_RESTRICTED) + style.removeStyleLayer(RouteLayerConstants.LAYER_GROUP_2_VIOLATED) style.removeStyleLayer(RouteLayerConstants.LAYER_GROUP_3_TRAIL_CASING) style.removeStyleLayer(RouteLayerConstants.LAYER_GROUP_3_TRAIL) style.removeStyleLayer(RouteLayerConstants.LAYER_GROUP_3_CASING) style.removeStyleLayer(RouteLayerConstants.LAYER_GROUP_3_MAIN) style.removeStyleLayer(RouteLayerConstants.LAYER_GROUP_3_TRAFFIC) style.removeStyleLayer(RouteLayerConstants.LAYER_GROUP_3_RESTRICTED) + style.removeStyleLayer(RouteLayerConstants.LAYER_GROUP_3_VIOLATED) style.removeStyleLayer(RouteLayerConstants.MASKING_LAYER_TRAIL_CASING) style.removeStyleLayer(RouteLayerConstants.MASKING_LAYER_TRAIL) style.removeStyleLayer(RouteLayerConstants.MASKING_LAYER_CASING) style.removeStyleLayer(RouteLayerConstants.MASKING_LAYER_MAIN) style.removeStyleLayer(RouteLayerConstants.MASKING_LAYER_TRAFFIC) style.removeStyleLayer(RouteLayerConstants.MASKING_LAYER_RESTRICTED) + style.removeStyleLayer(RouteLayerConstants.MASKING_LAYER_VIOLATED) style.removeStyleLayer(RouteLayerConstants.WAYPOINT_LAYER_ID) style.removeStyleImage(RouteLayerConstants.ORIGIN_MARKER_NAME) style.removeStyleImage(RouteLayerConstants.DESTINATION_MARKER_NAME) diff --git a/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/RouteLayerConstants.kt b/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/RouteLayerConstants.kt index 9b84a48319c..c7c24d20338 100644 --- a/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/RouteLayerConstants.kt +++ b/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/RouteLayerConstants.kt @@ -94,6 +94,9 @@ object RouteLayerConstants { internal const val RESTRICTED_ROAD_LINE_OPACITY = 1.0 internal const val RESTRICTED_ROAD_LINE_WIDTH = 7.0 internal val RESTRICTED_ROAD_DASH_ARRAY = listOf(.5, 2.0) + internal const val VIOLATED_SECTION_LINE_OPACITY = 1.0 + internal const val VIOLATED_SECTION_LINE_WIDTH = 7.0 + internal val VIOLATED_SECTION_DASH_ARRAY = listOf(.5, 2.0) internal const val DEFAULT_VANISHING_POINT_MIN_UPDATE_INTERVAL_NANO = 62_500_000L internal val LOW_CONGESTION_RANGE = 0..39 @@ -155,9 +158,15 @@ object RouteLayerConstants { @ColorInt internal val RESTRICTED_ROAD_COLOR = Color.parseColor("#000000") + @ColorInt + internal val VIOLATED_SECTION_COLOR = Color.parseColor("#4C4E52") + @ColorInt internal val ALTERNATE_RESTRICTED_ROAD_COLOR = Color.parseColor("#333333") + @ColorInt + internal val ALTERNATE_VIOLATED_SECTION_COLOR = Color.parseColor("#708090") + @DrawableRes internal val ORIGIN_WAYPOINT_ICON: Int = R.drawable.mapbox_ic_route_origin @@ -192,6 +201,7 @@ object RouteLayerConstants { internal const val LAYER_GROUP_1_MAIN = "mapbox-layerGroup-1-main" internal const val LAYER_GROUP_1_TRAFFIC = "mapbox-layerGroup-1-traffic" internal const val LAYER_GROUP_1_RESTRICTED = "mapbox-layerGroup-1-restricted" + internal const val LAYER_GROUP_1_VIOLATED = "mapbox-layerGroup-1-violated" internal const val LAYER_GROUP_2_TRAIL_CASING = "mapbox-layerGroup-2-trailCasing" internal const val LAYER_GROUP_2_TRAIL = "mapbox-layerGroup-2-trail" @@ -199,6 +209,7 @@ object RouteLayerConstants { internal const val LAYER_GROUP_2_MAIN = "mapbox-layerGroup-2-main" internal const val LAYER_GROUP_2_TRAFFIC = "mapbox-layerGroup-2-traffic" internal const val LAYER_GROUP_2_RESTRICTED = "mapbox-layerGroup-2-restricted" + internal const val LAYER_GROUP_2_VIOLATED = "mapbox-layerGroup-2-violated" internal const val LAYER_GROUP_3_TRAIL_CASING = "mapbox-layerGroup-3-trailCasing" internal const val LAYER_GROUP_3_TRAIL = "mapbox-layerGroup-3-trail" @@ -206,6 +217,7 @@ object RouteLayerConstants { internal const val LAYER_GROUP_3_MAIN = "mapbox-layerGroup-3-main" internal const val LAYER_GROUP_3_TRAFFIC = "mapbox-layerGroup-3-traffic" internal const val LAYER_GROUP_3_RESTRICTED = "mapbox-layerGroup-3-restricted" + internal const val LAYER_GROUP_3_VIOLATED = "mapbox-layerGroup-3-violated" /* When a line on the map overlaps itself it appears as though the later part of the line @@ -226,4 +238,5 @@ object RouteLayerConstants { internal const val MASKING_LAYER_MAIN = "mapbox-masking-layer-main" internal const val MASKING_LAYER_TRAFFIC = "mapbox-masking-layer-traffic" internal const val MASKING_LAYER_RESTRICTED = "mapbox-masking-layer-restricted" + internal const val MASKING_LAYER_VIOLATED = "mapbox-masking-layer-violated" } diff --git a/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/line/api/MapboxRouteLineApi.kt b/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/line/api/MapboxRouteLineApi.kt index 8cd6db9078c..831205f0bcf 100644 --- a/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/line/api/MapboxRouteLineApi.kt +++ b/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/line/api/MapboxRouteLineApi.kt @@ -2,6 +2,7 @@ package com.mapbox.navigation.ui.maps.route.line.api import android.graphics.Color import android.util.LruCache +import androidx.annotation.ColorInt import com.mapbox.api.directions.v5.models.DirectionsRoute import com.mapbox.api.directions.v5.models.RouteOptions import com.mapbox.bindgen.Expected @@ -29,6 +30,7 @@ import com.mapbox.navigation.core.trip.session.RouteProgressObserver import com.mapbox.navigation.ui.base.util.MapboxNavigationConsumer import com.mapbox.navigation.ui.maps.internal.route.line.MapboxRouteLineUtils import com.mapbox.navigation.ui.maps.internal.route.line.MapboxRouteLineUtils.extractRouteRestrictionData +import com.mapbox.navigation.ui.maps.internal.route.line.MapboxRouteLineUtils.extractViolatedSectionsData import com.mapbox.navigation.ui.maps.internal.route.line.MapboxRouteLineUtils.getMatchingColors import com.mapbox.navigation.ui.maps.internal.route.line.MapboxRouteLineUtils.granularDistancesProvider import com.mapbox.navigation.ui.maps.internal.route.line.MapboxRouteLineUtils.layerGroup1SourceLayerIds @@ -201,6 +203,7 @@ class MapboxRouteLineApi( private val routes: MutableList = mutableListOf() private var routeLineExpressionData: List = emptyList() private var restrictedExpressionData: List = emptyList() + private var violatedExpressionData: List = emptyList() private var lastIndexUpdateTimeNano: Long = 0 private var lastPointUpdateTimeNano: Long = 0 private val routeFeatureData: MutableList = mutableListOf() @@ -580,6 +583,7 @@ class MapboxRouteLineApi( granularDistances, workingRouteLineExpressionData, restrictedExpressionData, + violatedExpressionData, routeLineOptions.resourceProvider, activeLegIndex, stopGap, @@ -622,20 +626,23 @@ class MapboxRouteLineApi( routeLineExpressionProviders.routeLineExpression, routeLineExpressionProviders.routeLineCasingExpression, routeLineExpressionProviders.trafficLineExpression, - routeLineExpressionProviders.restrictedRoadExpression + routeLineExpressionProviders.restrictedRoadExpression, + routeLineExpressionProviders.violatedSectionExpression, ), alternativeRouteLinesDynamicData = listOf( RouteLineDynamicData( alternativesProvider, alternativesProvider, alternativesProvider, - alternativesProvider + alternativesProvider, + alternativesProvider, ), RouteLineDynamicData( alternativesProvider, alternativesProvider, alternativesProvider, - alternativesProvider + alternativesProvider, + alternativesProvider, ) ), routeLineMaskingLayerDynamicData = maskingLayerDynamicData @@ -759,6 +766,23 @@ class MapboxRouteLineApi( } } + val violatedSectionExpressionProvider = when (violatedExpressionData.isEmpty()) { + true -> null + false -> { + { + MapboxRouteLineUtils.getRestrictedLineExpression( + offset, + -1, + routeLineOptions + .resourceProvider + .routeLineColorResources + .violatedSectionColor, + violatedExpressionData + ) + } + } + } + val alternativesProvider = { throw UnsupportedOperationException( "alternative routes do not support dynamic updates yet" @@ -776,20 +800,23 @@ class MapboxRouteLineApi( routeLineExpressionProvider, routeLineCasingExpressionProvider, trafficLineExpressionProvider, - restrictedLineExpressionProvider + restrictedLineExpressionProvider, + violatedSectionExpressionProvider, ), alternativeRouteLinesDynamicData = listOf( RouteLineDynamicData( alternativesProvider, alternativesProvider, alternativesProvider, - alternativesProvider + alternativesProvider, + alternativesProvider, ), RouteLineDynamicData( alternativesProvider, alternativesProvider, alternativesProvider, - alternativesProvider + alternativesProvider, + alternativesProvider, ) ), routeLineMaskingLayerDynamicData = maskingLayerDynamicData @@ -1005,12 +1032,18 @@ class MapboxRouteLineApi( routeLineOptions.resourceProvider.routeLineColorResources, restrictedExpressionData ) + val violatedExpProvider = getViolatedSectionExpressionProducerForLegIndex( + legIndex, + routeLineOptions.resourceProvider.routeLineColorResources, + violatedExpressionData + ) return RouteLineDynamicData( { mainExp }, { casingExp }, { trafficExp }, restrictedSectionExpressionProvider = restrictedExpProvider, + violatedSectionExpressionProvider = violatedExpProvider, trailExpressionProvider = { trailExp }, trailCasingExpressionProvider = { trailCasingExp } ) @@ -1178,6 +1211,25 @@ class MapboxRouteLineApi( routeLineOptions .resourceProvider .routeLineColorResources + .restrictedRoadColor + ) + } + } + } + + val violatedSectionExpressionProvider = + when (violatedExpressionData.isEmpty()) { + true -> null + false -> { + { + MapboxRouteLineUtils.getRestrictedLineExpressionProducer( + violatedExpressionData, + 0.0, + legIndexToHighlight, + routeLineOptions + .resourceProvider + .routeLineColorResources + .violatedSectionColor ) } } @@ -1194,20 +1246,23 @@ class MapboxRouteLineApi( routeLineExpressionProvider, casingLineExpressionProvider, trafficLineExpressionProvider, - restrictedLineExpressionProvider?.invoke() + restrictedLineExpressionProvider?.invoke(), + violatedSectionExpressionProvider?.invoke(), ), alternativeRouteLinesDynamicData = listOf( RouteLineDynamicData( alternativesProvider, alternativesProvider, alternativesProvider, - alternativesProvider + alternativesProvider, + alternativesProvider, ), RouteLineDynamicData( alternativesProvider, alternativesProvider, alternativesProvider, - alternativesProvider + alternativesProvider, + alternativesProvider, ) ), routeLineMaskingLayerDynamicData = maskingLayerData @@ -1457,6 +1512,11 @@ class MapboxRouteLineApi( } else { listOf() } + violatedExpressionData = if (routeLineOptions.displayViolatedSections) { + extractViolatedSectionsData(routes.first(), granularDistancesProvider) + } else { + listOf() + } } private suspend fun buildDrawRoutesState( @@ -1599,6 +1659,16 @@ class MapboxRouteLineApi( }?.generateExpression() } + val primaryRouteViolatedSectionsExpressionDef = jobControl.scope.async { + primaryRoute?.route?.run { + getViolatedSectionExpressionProducerForLegIndex( + legIndex, + routeLineOptions.resourceProvider.routeLineColorResources, + violatedExpressionData + ) + }?.generateExpression() + } + val wayPointsFeatureCollectionDef = jobControl.scope.async { primaryRoute?.route?.run { MapboxRouteLineUtils.buildWayPointFeatureCollection(this) @@ -1776,6 +1846,15 @@ class MapboxRouteLineApi( ) } + val alternateRoute1ViolatedSectionsExpressionProducer = + RouteLineExpressionProvider { + MapboxRouteLineUtils.getRouteLineExpression( + alternative1PercentageTraveled, + Color.TRANSPARENT, + Color.TRANSPARENT + ) + } + val alternateRoute2BaseExpressionProducer = RouteLineExpressionProvider { MapboxRouteLineUtils.getRouteLineExpression( @@ -1821,11 +1900,25 @@ class MapboxRouteLineApi( ) } + val alternateRoute2ViolatedSectionsExpressionProducer = + RouteLineExpressionProvider { + MapboxRouteLineUtils.getRouteLineExpression( + alternative2PercentageTraveled, + Color.TRANSPARENT, + Color.TRANSPARENT + ) + } + val primaryRouteRestrictedSectionsExpressionProducer = ifNonNull(primaryRouteRestrictedSectionsExpressionDef.await()) { exp -> RouteLineExpressionProvider { exp } } + val primaryRouteViolatedSectionsExpressionProducer = + ifNonNull(primaryRouteViolatedSectionsExpressionDef.await()) { exp -> + RouteLineExpressionProvider { exp } + } + val maskingLayerData = if ((primaryRoute?.route?.directionsRoute?.legs()?.size ?: 0) > 1) { getRouteLineDynamicDataForMaskingLayers(routeLineExpressionData, 0) } else { @@ -1839,6 +1932,7 @@ class MapboxRouteLineApi( casingExpressionProvider = { exp }, trafficExpressionProvider = { exp }, restrictedSectionExpressionProvider = { exp }, + violatedSectionExpressionProvider = { exp }, trimOffset = RouteLineTrimOffset(vanishingPointOffset), trailExpressionProvider = { exp }, trailCasingExpressionProvider = { exp }, @@ -1854,6 +1948,7 @@ class MapboxRouteLineApi( primaryRouteCasingExpressionProducer, primaryRouteTrafficLineExpressionProducer, primaryRouteRestrictedSectionsExpressionProducer, + primaryRouteViolatedSectionsExpressionProducer, RouteLineTrimOffset(vanishingPointOffset), primaryRouteTrailExpressionProducer, primaryRouteTrailCasingExpressionProducer @@ -1867,6 +1962,7 @@ class MapboxRouteLineApi( alternateRoute1CasingExpressionProducer, alternateRoute1TrafficExpressionProducer, alternateRoute1RestrictedSectionsExpressionProducer, + alternateRoute1ViolatedSectionsExpressionProducer, RouteLineTrimOffset(alternative1PercentageTraveled), alternateRoute1TrailExpressionProducer, alternateRoute1TrailCasingExpressionProducer @@ -1879,6 +1975,7 @@ class MapboxRouteLineApi( alternateRoute2CasingExpressionProducer, alternateRoute2TrafficExpressionProducer, alternateRoute2RestrictedSectionsExpressionProducer, + alternateRoute2ViolatedSectionsExpressionProducer, RouteLineTrimOffset(alternative2PercentageTraveled), alternateRoute2TrailExpressionProducer, alternateRoute2TrailCasingExpressionProducer @@ -1926,7 +2023,7 @@ class MapboxRouteLineApi( restrictedRouteExpressionData, vanishingPointOffset = 0.0, activeLegIndex = legIndex, - colorResources + colorResources.restrictedRoadColor ) } else { MapboxRouteLineUtils.getDisabledRestrictedLineExpressionProducer( @@ -1936,4 +2033,25 @@ class MapboxRouteLineApi( ) } } + + private fun getViolatedSectionExpressionProducerForLegIndex( + legIndex: Int, + colorResources: RouteLineColorResources, + violatedSectionExpressionData: List + ): RouteLineExpressionProvider { + return if (routeLineOptions.displayViolatedSections) { + MapboxRouteLineUtils.getRestrictedLineExpressionProducer( + violatedSectionExpressionData, + vanishingPointOffset = 0.0, + activeLegIndex = legIndex, + colorResources.violatedSectionColor + ) + } else { + MapboxRouteLineUtils.getDisabledRestrictedLineExpressionProducer( + 0.0, + legIndex, + colorResources.violatedSectionColor + ) + } + } } diff --git a/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/line/api/MapboxRouteLineView.kt b/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/line/api/MapboxRouteLineView.kt index 5ca0013177c..2fd55ca23b6 100644 --- a/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/line/api/MapboxRouteLineView.kt +++ b/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/line/api/MapboxRouteLineView.kt @@ -32,24 +32,28 @@ import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_1_RES import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_1_TRAFFIC import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_1_TRAIL import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_1_TRAIL_CASING +import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_1_VIOLATED import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_2_CASING import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_2_MAIN import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_2_RESTRICTED import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_2_TRAFFIC import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_2_TRAIL import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_2_TRAIL_CASING +import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_2_VIOLATED import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_3_CASING import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_3_MAIN import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_3_RESTRICTED import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_3_TRAFFIC import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_3_TRAIL import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_3_TRAIL_CASING +import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_3_VIOLATED import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.MASKING_LAYER_CASING import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.MASKING_LAYER_MAIN import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.MASKING_LAYER_RESTRICTED import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.MASKING_LAYER_TRAFFIC import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.MASKING_LAYER_TRAIL import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.MASKING_LAYER_TRAIL_CASING +import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.MASKING_LAYER_VIOLATED import com.mapbox.navigation.ui.maps.route.line.model.MapboxRouteLineOptions import com.mapbox.navigation.ui.maps.route.line.model.RouteLineClearValue import com.mapbox.navigation.ui.maps.route.line.model.RouteLineData @@ -155,6 +159,12 @@ class MapboxRouteLineView @VisibleForTesting internal constructor( LAYER_GROUP_3_RESTRICTED, MASKING_LAYER_RESTRICTED ) + private val violatedLayerIds = setOf( + LAYER_GROUP_1_VIOLATED, + LAYER_GROUP_2_VIOLATED, + LAYER_GROUP_3_VIOLATED, + MASKING_LAYER_VIOLATED + ) private val maskingRouteLineLayerGroup = setOf( MASKING_LAYER_MAIN, MASKING_LAYER_CASING, @@ -162,6 +172,7 @@ class MapboxRouteLineView @VisibleForTesting internal constructor( MASKING_LAYER_TRAFFIC, MASKING_LAYER_TRAIL_CASING, MASKING_LAYER_RESTRICTED, + MASKING_LAYER_VIOLATED, ) private val sourceToFeatureMap = mutableMapOf( Pair(MapboxRouteLineUtils.layerGroup1SourceKey, RouteLineFeatureId(null)), @@ -934,6 +945,7 @@ class MapboxRouteLineView @VisibleForTesting internal constructor( in mainLayerIds -> Pair(it, routeLineData.baseExpressionProvider) in trafficLayerIds -> Pair(it, routeLineData.trafficExpressionProvider) in restrictedLayerIds -> Pair(it, routeLineData.restrictedSectionExpressionProvider) + in violatedLayerIds -> Pair(it, routeLineData.violatedSectionExpressionProvider) else -> null } }.filter { it?.second != null }.map { @@ -1123,6 +1135,9 @@ class MapboxRouteLineView @VisibleForTesting internal constructor( in restrictedLayerIds -> { routeLineDynamicData.restrictedSectionExpressionProvider } + in violatedLayerIds -> { + routeLineDynamicData.violatedSectionExpressionProvider + } else -> null }.run { getExpressionUpdateFun( diff --git a/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/line/api/VanishingRouteLine.kt b/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/line/api/VanishingRouteLine.kt index a8f9417a0bb..cf7d7675dd6 100644 --- a/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/line/api/VanishingRouteLine.kt +++ b/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/line/api/VanishingRouteLine.kt @@ -134,11 +134,15 @@ internal class VanishingRouteLine { val restrictedRoadExpressionProvider = RouteLineTrimExpressionProvider { trimmedOffsetExpression } + val violatedSectionExpressionProvider = RouteLineTrimExpressionProvider { + trimmedOffsetExpression + } VanishingRouteLineExpressions( trafficLineExpressionProvider, routeLineExpressionProvider, routeLineCasingExpressionProvider, - restrictedRoadExpressionProvider + restrictedRoadExpressionProvider, + violatedSectionExpressionProvider, ) } } @@ -149,6 +153,7 @@ internal class VanishingRouteLine { granularDistances: RouteLineGranularDistances, routeLineExpressionData: List, restrictedLineExpressionData: List?, + violatedSectionExpressionData: List?, routeResourceProvider: RouteLineResources, activeLegIndex: Int, softGradientTransition: Double, @@ -202,12 +207,24 @@ internal class VanishingRouteLine { ) } } + val violatedSectionExpressionProvider = + ifNonNull(violatedSectionExpressionData) { expressionData -> + { + MapboxRouteLineUtils.getRestrictedLineExpression( + offset, + activeLegIndex, + routeResourceProvider.routeLineColorResources.violatedSectionColor, + expressionData + ) + } + } VanishingRouteLineExpressions( trafficLineExpressionProvider, routeLineExpressionProvider, routeLineCasingExpressionProvider, - restrictedRoadExpressionProvider + restrictedRoadExpressionProvider, + violatedSectionExpressionProvider ) } } diff --git a/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/line/model/MapboxRouteLineOptions.kt b/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/line/model/MapboxRouteLineOptions.kt index fa7d2ebbe29..38271c763c4 100644 --- a/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/line/model/MapboxRouteLineOptions.kt +++ b/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/line/model/MapboxRouteLineOptions.kt @@ -26,6 +26,8 @@ import kotlin.math.abs * @param tolerance the tolerance value used when configuring the underlying map source * @param displayRestrictedRoadSections indicates if the route line will display restricted * road sections with a dashed line + * @param displayViolatedSections indicates if the route line will display sections, + * where the route restrictions were violated, with a dashed line. * @param styleInactiveRouteLegsIndependently enabling this feature will change the color of the route * legs that aren't currently being navigated. See [RouteLineColorResources] to specify the color * used. @@ -55,6 +57,7 @@ class MapboxRouteLineOptions private constructor( internal var vanishingRouteLine: VanishingRouteLine? = null, val tolerance: Double, val displayRestrictedRoadSections: Boolean = false, + val displayViolatedSections: Boolean = false, val styleInactiveRouteLegsIndependently: Boolean = false, val displaySoftGradientForTraffic: Boolean = false, val softGradientTransition: Double = RouteLayerConstants.SOFT_GRADIENT_STOP_GAP_METERS, @@ -85,6 +88,7 @@ class MapboxRouteLineOptions private constructor( vanishingRouteLineEnabled, tolerance, displayRestrictedRoadSections, + displayViolatedSections, styleInactiveRouteLegsIndependently, displaySoftGradientForTraffic, softGradientTransition, @@ -113,6 +117,7 @@ class MapboxRouteLineOptions private constructor( if (vanishingRouteLine != other.vanishingRouteLine) return false if (tolerance != other.tolerance) return false if (displayRestrictedRoadSections != other.displayRestrictedRoadSections) return false + if (displayViolatedSections != other.displayViolatedSections) return false if (styleInactiveRouteLegsIndependently != other.styleInactiveRouteLegsIndependently) { return false } @@ -141,6 +146,7 @@ class MapboxRouteLineOptions private constructor( result = 31 * result + vanishingRouteLine.hashCode() result = 31 * result + (tolerance.hashCode()) result = 31 * result + (displayRestrictedRoadSections.hashCode()) + result = 31 * result + (displayViolatedSections.hashCode()) result = 31 * result + (styleInactiveRouteLegsIndependently.hashCode()) result = 31 * result + (displaySoftGradientForTraffic.hashCode()) result = 31 * result + (softGradientTransition.hashCode()) @@ -164,6 +170,7 @@ class MapboxRouteLineOptions private constructor( "vanishingRouteLine=$vanishingRouteLine, " + "tolerance=$tolerance, " + "displayRestrictedRoadSections=$displayRestrictedRoadSections, " + + "displayViolatedSections=$displayViolatedSections, " + "styleInactiveRouteLegsIndependently=$styleInactiveRouteLegsIndependently," + "displaySoftGradientForTraffic=$displaySoftGradientForTraffic," + "softGradientTransition=$softGradientTransition," + @@ -186,6 +193,8 @@ class MapboxRouteLineOptions private constructor( * @param vanishingRouteLineEnabled indicates if the vanishing route line feature is enabled * @param displayRestrictedRoadSections indicates if the route line will display restricted * road sections with a dashed line + * @param displayRestrictedRoadSections indicates if the route line will display restricted + * road sections with a dashed line * @param styleInactiveRouteLegsIndependently enabling this feature will change the color of the route * legs that aren't currently being navigated. See [RouteLineColorResources] to specify the color * used. @@ -210,6 +219,7 @@ class MapboxRouteLineOptions private constructor( private var vanishingRouteLineEnabled: Boolean, private var tolerance: Double, private var displayRestrictedRoadSections: Boolean, + private var displayViolatedSections: Boolean, private var styleInactiveRouteLegsIndependently: Boolean, private var displaySoftGradientForTraffic: Boolean, private var softGradientTransition: Double, @@ -238,6 +248,7 @@ class MapboxRouteLineOptions private constructor( false, false, false, + false, RouteLayerConstants.SOFT_GRADIENT_STOP_GAP_METERS, RouteLayerConstants.DEFAULT_VANISHING_POINT_MIN_UPDATE_INTERVAL_NANO, listOf(0.0, 0.0), @@ -310,6 +321,15 @@ class MapboxRouteLineOptions private constructor( fun displayRestrictedRoadSections(displayRestrictedRoadSections: Boolean): Builder = apply { this.displayRestrictedRoadSections = displayRestrictedRoadSections } + /** + * Indicates if the route line will display restricted + * road sections with a dashed line. False by default. + * + * @return the builder + */ + fun displayViolatedSections(displayViolatedSections: Boolean): Builder = + apply { this.displayViolatedSections = displayViolatedSections } + /** * Enabling this feature will result in route legs that aren't currently being navigated * to be color differently than the active leg. See [RouteLineColorResources] for the @@ -459,6 +479,7 @@ class MapboxRouteLineOptions private constructor( vanishingRouteLine, tolerance, displayRestrictedRoadSections, + displayViolatedSections, styleInactiveRouteLegsIndependently, displaySoftGradientForTraffic, softGradientTransition, diff --git a/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/line/model/RouteLineColorResources.kt b/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/line/model/RouteLineColorResources.kt index 2bd3e6ac63a..32ea5d3279c 100644 --- a/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/line/model/RouteLineColorResources.kt +++ b/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/line/model/RouteLineColorResources.kt @@ -19,6 +19,7 @@ import com.mapbox.navigation.ui.maps.route.RouteLayerConstants * @param routeUnknownCongestionColor the color used for representing unknown traffic congestion * @param routeClosureColor the color used for the route closure line * @param restrictedRoadColor the color for the restricted road indicator(s) + * @param violatedSectionColor the color for the violated section indicator(s) * @param alternativeRouteDefaultColor the default color used for alternative route lines * @param alternativeRouteLowCongestionColor the color used for representing low traffic congestion on * alternative routes @@ -32,6 +33,8 @@ import com.mapbox.navigation.ui.maps.route.RouteLayerConstants * congestion on alternative routes * @param alternativeRouteRestrictedRoadColor the color for the restricted road indicator(s) for * alternative routes. + * @param alternativeRouteViolatedSectionColor the color for the violated section indicator(s) for + * alternative routes. * @param alternativeRouteClosureColor the color used for the alternative route closure line(s) * @param routeLineTraveledColor the color of the section of route line behind the puck * representing the section of the route traveled @@ -95,8 +98,10 @@ class RouteLineColorResources private constructor( @ColorInt val alternativeRouteSevereCongestionColor: Int, @ColorInt val alternativeRouteUnknownCongestionColor: Int, @ColorInt val restrictedRoadColor: Int, + @ColorInt val violatedSectionColor: Int, @ColorInt val routeClosureColor: Int, @ColorInt val alternativeRouteRestrictedRoadColor: Int, + @ColorInt val alternativeRouteViolatedSectionColor: Int, @ColorInt val alternativeRouteClosureColor: Int, @ColorInt val routeLineTraveledColor: Int, @ColorInt val routeLineTraveledCasingColor: Int, @@ -122,6 +127,7 @@ class RouteLineColorResources private constructor( .routeUnknownCongestionColor(routeUnknownCongestionColor) .routeClosureColor(routeClosureColor) .restrictedRoadColor(restrictedRoadColor) + .violatedSectionColor(violatedSectionColor) .alternativeRouteDefaultColor(alternativeRouteDefaultColor) .alternativeRouteLowCongestionColor(alternativeRouteLowCongestionColor) .alternativeRouteModerateCongestionColor(alternativeRouteModerateCongestionColor) @@ -130,6 +136,7 @@ class RouteLineColorResources private constructor( .alternativeRouteUnknownCongestionColor(alternativeRouteUnknownCongestionColor) .alternativeRouteClosureColor(alternativeRouteClosureColor) .alternativeRouteRestrictedRoadColor(alternativeRouteRestrictedRoadColor) + .alternativeRouteViolatedSectionColor(alternativeRouteViolatedSectionColor) .routeLineTraveledColor(routeLineTraveledColor) .routeLineTraveledCasingColor(routeLineTraveledCasingColor) .routeCasingColor(routeCasingColor) @@ -154,6 +161,7 @@ class RouteLineColorResources private constructor( "routeUnknownCongestionColor=$routeUnknownCongestionColor, " + "routeClosureColor=$routeClosureColor, " + "restrictedRoadColor=$restrictedRoadColor, " + + "violatedSectionColor=$violatedSectionColor, " + "alternativeRouteDefaultColor=$alternativeRouteDefaultColor, " + "alternativeRouteLowCongestionColor=$alternativeRouteLowCongestionColor, " + "alternativeRouteModerateCongestionColor=$alternativeRouteModerateCongestionColor, " + @@ -161,6 +169,7 @@ class RouteLineColorResources private constructor( "alternativeRouteSevereCongestionColor=$alternativeRouteSevereCongestionColor, " + "alternativeRouteUnknownCongestionColor=$alternativeRouteUnknownCongestionColor, " + "alternativeRouteRestrictedRoadColor=$alternativeRouteRestrictedRoadColor, " + + "alternativeRouteViolatedSectionColor=$alternativeRouteViolatedSectionColor, " + "alternativeRouteClosureColor=$alternativeRouteClosureColor, " + "routeLineTraveledColor=$routeLineTraveledColor, " + "routeLineTraveledCasingColor=$routeLineTraveledCasingColor, " + @@ -186,6 +195,7 @@ class RouteLineColorResources private constructor( result = 31 * result + routeUnknownCongestionColor result = 31 * result + routeClosureColor result = 31 * result + restrictedRoadColor + result = 31 * result + violatedSectionColor result = 31 * result + alternativeRouteDefaultColor result = 31 * result + alternativeRouteLowCongestionColor result = 31 * result + alternativeRouteModerateCongestionColor @@ -193,6 +203,7 @@ class RouteLineColorResources private constructor( result = 31 * result + alternativeRouteSevereCongestionColor result = 31 * result + alternativeRouteUnknownCongestionColor result = 31 * result + alternativeRouteRestrictedRoadColor + result = 31 * result + alternativeRouteViolatedSectionColor result = 31 * result + alternativeRouteClosureColor result = 31 * result + routeLineTraveledColor result = 31 * result + routeLineTraveledCasingColor @@ -223,6 +234,7 @@ class RouteLineColorResources private constructor( if (routeUnknownCongestionColor != other.routeUnknownCongestionColor) return false if (routeClosureColor != other.routeClosureColor) return false if (restrictedRoadColor != other.restrictedRoadColor) return false + if (violatedSectionColor != other.violatedSectionColor) return false if (alternativeRouteDefaultColor != other.alternativeRouteDefaultColor) return false if (alternativeRouteLowCongestionColor != other.alternativeRouteLowCongestionColor) { return false @@ -247,6 +259,7 @@ class RouteLineColorResources private constructor( return false } if (alternativeRouteClosureColor != other.alternativeRouteClosureColor) return false + if (alternativeRouteViolatedSectionColor != other.alternativeRouteViolatedSectionColor) return false if (routeLineTraveledColor != other.routeLineTraveledColor) return false if (routeLineTraveledCasingColor != other.routeLineTraveledCasingColor) return false if (routeCasingColor != other.routeCasingColor) return false @@ -274,6 +287,7 @@ class RouteLineColorResources private constructor( private var routeUnknownCongestionColor: Int = RouteLayerConstants.ROUTE_UNKNOWN_TRAFFIC_COLOR private var restrictedRoadColor: Int = RouteLayerConstants.RESTRICTED_ROAD_COLOR + private var violatedSectionColor: Int = RouteLayerConstants.VIOLATED_SECTION_COLOR private var routeClosureColor: Int = RouteLayerConstants.ROUTE_CLOSURE_COLOR private var alternativeRouteDefaultColor: Int = RouteLayerConstants.ALTERNATE_ROUTE_DEFAULT_COLOR @@ -289,6 +303,8 @@ class RouteLineColorResources private constructor( RouteLayerConstants.ALTERNATE_ROUTE_UNKNOWN_TRAFFIC_COLOR private var alternativeRouteRestrictedRoadColor: Int = RouteLayerConstants.ALTERNATE_RESTRICTED_ROAD_COLOR + private var alternativeRouteViolatedSectionColor: Int = + RouteLayerConstants.ALTERNATE_VIOLATED_SECTION_COLOR private var alternativeRouteClosureColor: Int = RouteLayerConstants.ALTERNATIVE_ROUTE_CLOSURE_COLOR private var routeLineTraveledColor: Int = RouteLayerConstants.ROUTE_LINE_TRAVELED_COLOR @@ -446,6 +462,16 @@ class RouteLineColorResources private constructor( fun restrictedRoadColor(@ColorInt color: Int): Builder = apply { this.restrictedRoadColor = color } + /** + * The color used for the violated section representation. + * + * @param color the color to be used + * + * @return the builder + */ + fun violatedSectionColor(@ColorInt color: Int): Builder = + apply { this.violatedSectionColor = color } + /** * The color used for road closure sections of a route. * @@ -526,6 +552,16 @@ class RouteLineColorResources private constructor( fun alternativeRouteRestrictedRoadColor(@ColorInt color: Int): Builder = apply { this.alternativeRouteRestrictedRoadColor = color } + /** + * The color used for the alternative route violated section representation. + * + * @param color the color to be used + * + * @return the builder + */ + fun alternativeRouteViolatedSectionColor(@ColorInt color: Int): Builder = + apply { this.alternativeRouteViolatedSectionColor = color } + /** * The color used for road closure sections of an alternative route(s). * @@ -621,8 +657,10 @@ class RouteLineColorResources private constructor( alternativeRouteSevereCongestionColor, alternativeRouteUnknownCongestionColor, restrictedRoadColor, + violatedSectionColor, routeClosureColor, alternativeRouteRestrictedRoadColor, + alternativeRouteViolatedSectionColor, alternativeRouteClosureColor, routeLineTraveledColor, routeLineTraveledCasingColor, diff --git a/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/line/model/RouteLineDynamicData.kt b/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/line/model/RouteLineDynamicData.kt index d721a763ce7..699d606ab4b 100644 --- a/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/line/model/RouteLineDynamicData.kt +++ b/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/line/model/RouteLineDynamicData.kt @@ -7,6 +7,7 @@ package com.mapbox.navigation.ui.maps.route.line.model * @param casingExpressionProvider expression used to style the case of the line * @param trafficExpressionProvider expression used to style the congestion colors on the line * @param restrictedSectionExpressionProvider expression used to style the restricted sections on the line + * @param violatedSectionExpressionProvider expression used to style the violated sections on the line * @param trimOffset a value representing the section of the line that should be trimmed and made transparent. Null by default * @param trailExpressionProvider expression used to style the trail layer * @param trailCasingExpressionProvider expression used to style the trail casing layer @@ -16,6 +17,7 @@ class RouteLineDynamicData internal constructor( val casingExpressionProvider: RouteLineExpressionProvider, val trafficExpressionProvider: RouteLineExpressionProvider?, val restrictedSectionExpressionProvider: RouteLineExpressionProvider?, + val violatedSectionExpressionProvider: RouteLineExpressionProvider?, val trimOffset: RouteLineTrimOffset? = null, val trailExpressionProvider: RouteLineExpressionProvider? = null, val trailCasingExpressionProvider: RouteLineExpressionProvider? = null @@ -29,6 +31,7 @@ class RouteLineDynamicData internal constructor( casingExpressionProvider, trafficExpressionProvider, restrictedSectionExpressionProvider, + violatedSectionExpressionProvider, trimOffset, trailExpressionProvider, trailCasingExpressionProvider @@ -41,6 +44,7 @@ class RouteLineDynamicData internal constructor( * @param casingExpressionProvider expression used to style the case of the line * @param trafficExpressionProvider expression used to style the congestion colors on the line * @param restrictedSectionExpressionProvider expression used to style the restricted sections on the line + * @param violatedSectionExpressionProvider expression used to style the violated sections on the line * @param trimOffset a value representing the section of the line that should be trimmed and made transparent. Null by default * @param trailExpressionProvider expression used to style the trail layer * @param trailCasingExpressionProvider expression used to style the trail casing layer @@ -50,6 +54,7 @@ class RouteLineDynamicData internal constructor( var casingExpressionProvider: RouteLineExpressionProvider, var trafficExpressionProvider: RouteLineExpressionProvider?, var restrictedSectionExpressionProvider: RouteLineExpressionProvider?, + var violatedSectionExpressionProvider: RouteLineExpressionProvider?, var trimOffset: RouteLineTrimOffset? = null, var trailExpressionProvider: RouteLineExpressionProvider? = null, var trailCasingExpressionProvider: RouteLineExpressionProvider? = null @@ -63,6 +68,7 @@ class RouteLineDynamicData internal constructor( casingExpressionProvider, trafficExpressionProvider, restrictedSectionExpressionProvider, + violatedSectionExpressionProvider, trimOffset, trailExpressionProvider, trailCasingExpressionProvider @@ -84,6 +90,9 @@ class RouteLineDynamicData internal constructor( if (restrictedSectionExpressionProvider != other.restrictedSectionExpressionProvider) { return false } + if (violatedSectionExpressionProvider != other.violatedSectionExpressionProvider) { + return false + } if (trimOffset != other.trimOffset) return false if (trailExpressionProvider != other.trailExpressionProvider) return false if (trailCasingExpressionProvider != other.trailCasingExpressionProvider) return false @@ -99,6 +108,7 @@ class RouteLineDynamicData internal constructor( result = 31 * result + casingExpressionProvider.hashCode() result = 31 * result + (trafficExpressionProvider?.hashCode() ?: 0) result = 31 * result + (restrictedSectionExpressionProvider?.hashCode() ?: 0) + result = 31 * result + (violatedSectionExpressionProvider?.hashCode() ?: 0) result = 31 * result + (trimOffset?.hashCode() ?: 0) result = 31 * result + (trailExpressionProvider?.hashCode() ?: 0) result = 31 * result + (trailCasingExpressionProvider?.hashCode() ?: 0) @@ -114,6 +124,7 @@ class RouteLineDynamicData internal constructor( "casingExpressionProvider=$casingExpressionProvider, " + "trafficExpressionProvider=$trafficExpressionProvider, " + "restrictedSectionExpressionProvider=$restrictedSectionExpressionProvider," + + "violatedSectionExpressionProvider=$violatedSectionExpressionProvider," + "trimOffset=$trimOffset," + "trailExpressionProvider=$trailExpressionProvider," + "trailCasingExpressionProvider=$trailCasingExpressionProvider," + diff --git a/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/line/model/RouteLineResources.kt b/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/line/model/RouteLineResources.kt index 431e6d1bee9..4a3d426e8ab 100644 --- a/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/line/model/RouteLineResources.kt +++ b/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/line/model/RouteLineResources.kt @@ -10,6 +10,9 @@ import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.RESTRICTED_ROAD_L import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.RESTRICTED_ROAD_LINE_WIDTH import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.ROUNDED_LINE_CAP import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.TRAFFIC_BACKFILL_ROAD_CLASSES +import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.VIOLATED_SECTION_DASH_ARRAY +import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.VIOLATED_SECTION_LINE_OPACITY +import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.VIOLATED_SECTION_LINE_WIDTH /** * Contains colors an other values used to determine the appearance of the route line. @@ -35,6 +38,9 @@ import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.TRAFFIC_BACKFILL_ * @param restrictedRoadDashArray the dash array for the [LineLayer] used for displaying restricted roads * @param restrictedRoadOpacity the opacity of the restricted road [LineLayer] * @param restrictedRoadLineWidth the width of the restricted road [LineLayer] + * @param violatedSectionDashArray the dash array for the [LineLayer] used for displaying violated sections + * @param violatedSectionOpacity the opacity of the violated section [LineLayer] + * @param violatedSectionLineWidth the width of the violated section [LineLayer] */ class RouteLineResources private constructor( val routeLineColorResources: RouteLineColorResources, @@ -50,7 +56,10 @@ class RouteLineResources private constructor( val alternativeRouteTrafficLineScaleExpression: Expression, val restrictedRoadDashArray: List, val restrictedRoadOpacity: Double, - val restrictedRoadLineWidth: Double + val restrictedRoadLineWidth: Double, + val violatedSectionDashArray: List, + val violatedSectionOpacity: Double, + val violatedSectionLineWidth: Double, ) { /** @@ -72,6 +81,9 @@ class RouteLineResources private constructor( .restrictedRoadDashArray(restrictedRoadDashArray) .restrictedRoadOpacity(restrictedRoadOpacity) .restrictedRoadLineWidth(restrictedRoadLineWidth) + .violatedSectionDashArray(violatedSectionDashArray) + .violatedSectionOpacity(violatedSectionOpacity) + .violatedSectionLineWidth(violatedSectionLineWidth) } /** @@ -102,7 +114,10 @@ class RouteLineResources private constructor( "trafficBackfillRoadClasses=$trafficBackfillRoadClasses, " + "restrictedRoadDashArray=$restrictedRoadDashArray, " + "restrictedRoadOpacity=$restrictedRoadOpacity, " + - "restrictedRoadLineWidth=$restrictedRoadLineWidth" + + "restrictedRoadLineWidth=$restrictedRoadLineWidth, " + + "violatedSectionDashArray=$violatedSectionDashArray, " + + "violatedSectionOpacity=$violatedSectionOpacity, " + + "violatedSectionLineWidth=$violatedSectionLineWidth" + ")" } @@ -141,6 +156,9 @@ class RouteLineResources private constructor( if (restrictedRoadDashArray != other.restrictedRoadDashArray) return false if (restrictedRoadOpacity != other.restrictedRoadOpacity) return false if (restrictedRoadLineWidth != other.restrictedRoadLineWidth) return false + if (violatedSectionDashArray != other.violatedSectionDashArray) return false + if (violatedSectionOpacity != other.violatedSectionOpacity) return false + if (violatedSectionLineWidth != other.violatedSectionLineWidth) return false return true } @@ -163,6 +181,9 @@ class RouteLineResources private constructor( result = 31 * result + restrictedRoadDashArray.hashCode() result = 31 * result + restrictedRoadOpacity.hashCode() result = 31 * result + restrictedRoadLineWidth.hashCode() + result = 31 * result + violatedSectionDashArray.hashCode() + result = 31 * result + violatedSectionOpacity.hashCode() + result = 31 * result + violatedSectionLineWidth.hashCode() return result } @@ -178,6 +199,9 @@ class RouteLineResources private constructor( private var restrictedRoadDashArray: List = RESTRICTED_ROAD_DASH_ARRAY private var restrictedRoadOpacity: Double = RESTRICTED_ROAD_LINE_OPACITY private var restrictedRoadLineWidth: Double = RESTRICTED_ROAD_LINE_WIDTH + private var violatedSectionDashArray: List = VIOLATED_SECTION_DASH_ARRAY + private var violatedSectionOpacity: Double = VIOLATED_SECTION_LINE_OPACITY + private var violatedSectionLineWidth: Double = VIOLATED_SECTION_LINE_WIDTH private var routeLineScaleExpression: Expression = buildScalingExpression( listOf( @@ -378,6 +402,32 @@ class RouteLineResources private constructor( fun restrictedRoadLineWidth(width: Double): Builder = apply { this.restrictedRoadLineWidth = width } + /** + * The dash array parameter for the violated section layer. + * See [LineLayer] LineDashArray for more information. An empty list will show a + * solid line. The default is [.5, 2.0] + * + * @param dashArray value of the LineDash array + */ + fun violatedSectionDashArray(dashArray: List): Builder = + apply { this.violatedSectionDashArray = dashArray } + + /** + * The opacity for the violated section line. The default is 1.0. + * + * @param opacity the opacity value + */ + fun violatedSectionOpacity(opacity: Double): Builder = + apply { this.violatedSectionOpacity = opacity } + + /** + * The width of the violated section line + * + * @param width the width of the line + */ + fun violatedSectionLineWidth(width: Double): Builder = + apply { this.violatedSectionLineWidth = width } + /** * Creates a instance of RouteLineResources * @@ -400,7 +450,10 @@ class RouteLineResources private constructor( alternativeRouteTrafficLineScaleExpression, restrictedRoadDashArray, restrictedRoadOpacity, - restrictedRoadLineWidth + restrictedRoadLineWidth, + violatedSectionDashArray, + violatedSectionOpacity, + violatedSectionLineWidth, ) } } diff --git a/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/line/model/VanishingRouteLineExpressions.kt b/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/line/model/VanishingRouteLineExpressions.kt index cd1be21aa54..67c4e37fad5 100644 --- a/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/line/model/VanishingRouteLineExpressions.kt +++ b/libnavui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/line/model/VanishingRouteLineExpressions.kt @@ -4,5 +4,6 @@ internal data class VanishingRouteLineExpressions( val trafficLineExpression: RouteLineExpressionProvider, val routeLineExpression: RouteLineExpressionProvider, val routeLineCasingExpression: RouteLineExpressionProvider, - val restrictedRoadExpression: RouteLineExpressionProvider? + val restrictedRoadExpression: RouteLineExpressionProvider?, + val violatedSectionExpression: RouteLineExpressionProvider?, ) diff --git a/libnavui-maps/src/test/java/com/mapbox/navigation/ui/maps/internal/route/line/MapboxRouteLineUtilsRoboTest.kt b/libnavui-maps/src/test/java/com/mapbox/navigation/ui/maps/internal/route/line/MapboxRouteLineUtilsRoboTest.kt index c7eb650ae9c..031f95407cc 100644 --- a/libnavui-maps/src/test/java/com/mapbox/navigation/ui/maps/internal/route/line/MapboxRouteLineUtilsRoboTest.kt +++ b/libnavui-maps/src/test/java/com/mapbox/navigation/ui/maps/internal/route/line/MapboxRouteLineUtilsRoboTest.kt @@ -34,6 +34,7 @@ import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_1_SOU import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_1_TRAFFIC import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_1_TRAIL import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_1_TRAIL_CASING +import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_1_VIOLATED import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_2_CASING import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_2_MAIN import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_2_RESTRICTED @@ -41,6 +42,7 @@ import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_2_SOU import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_2_TRAFFIC import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_2_TRAIL import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_2_TRAIL_CASING +import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_2_VIOLATED import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_3_CASING import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_3_MAIN import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_3_RESTRICTED @@ -48,12 +50,14 @@ import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_3_SOU import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_3_TRAFFIC import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_3_TRAIL import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_3_TRAIL_CASING +import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_3_VIOLATED import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.MASKING_LAYER_CASING import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.MASKING_LAYER_MAIN import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.MASKING_LAYER_RESTRICTED import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.MASKING_LAYER_TRAFFIC import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.MASKING_LAYER_TRAIL import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.MASKING_LAYER_TRAIL_CASING +import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.MASKING_LAYER_VIOLATED import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.ORIGIN_MARKER_NAME import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.TOP_LEVEL_ROUTE_LINE_LAYER_ID import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.WAYPOINT_LAYER_ID @@ -105,6 +109,7 @@ class MapboxRouteLineUtilsRoboTest { val options = MapboxRouteLineOptions.Builder(ctx) .withRouteLineBelowLayerId(LocationComponentConstants.MODEL_LAYER) .displayRestrictedRoadSections(true) + .displayViolatedSections(true) .waypointLayerIconAnchor(IconAnchor.BOTTOM_RIGHT) .waypointLayerIconOffset(listOf(33.3, 44.4)) .iconPitchAlignment(IconPitchAlignment.VIEWPORT) @@ -141,6 +146,9 @@ class MapboxRouteLineUtilsRoboTest { every { styleLayerExists(LAYER_GROUP_1_RESTRICTED) } returns false + every { + styleLayerExists(LAYER_GROUP_1_VIOLATED) + } returns false every { styleLayerExists(LAYER_GROUP_2_TRAIL_CASING) } returns false every { styleLayerExists(LAYER_GROUP_2_TRAIL) @@ -157,6 +165,9 @@ class MapboxRouteLineUtilsRoboTest { every { styleLayerExists(LAYER_GROUP_2_RESTRICTED) } returns false + every { + styleLayerExists(LAYER_GROUP_2_VIOLATED) + } returns false every { styleLayerExists(LAYER_GROUP_3_TRAIL_CASING) } returns false every { styleLayerExists(LAYER_GROUP_3_TRAIL) @@ -173,6 +184,9 @@ class MapboxRouteLineUtilsRoboTest { every { styleLayerExists(LAYER_GROUP_3_RESTRICTED) } returns false + every { + styleLayerExists(LAYER_GROUP_3_VIOLATED) + } returns false every { styleLayerExists(BOTTOM_LEVEL_ROUTE_LINE_LAYER_ID) } returns false @@ -197,6 +211,9 @@ class MapboxRouteLineUtilsRoboTest { every { styleLayerExists(MASKING_LAYER_RESTRICTED) } returns false + every { + styleLayerExists(MASKING_LAYER_VIOLATED) + } returns false every { getStyleImage(ARROW_HEAD_ICON) } returns null every { getStyleImage(ARROW_HEAD_ICON_CASING) } returns null every { getStyleImage(ORIGIN_MARKER_NAME) } returns null @@ -380,108 +397,124 @@ class MapboxRouteLineUtilsRoboTest { (addStyleLayerSlots[6].contents as HashMap)["id"]!!.contents ) assertEquals( - "mapbox-layerGroup-2-trailCasing", + "mapbox-layerGroup-3-violated", (addStyleLayerSlots[7].contents as HashMap)["id"]!!.contents ) assertEquals( - "mapbox-layerGroup-2-trail", + "mapbox-layerGroup-2-trailCasing", (addStyleLayerSlots[8].contents as HashMap)["id"]!!.contents ) assertEquals( - "mapbox-layerGroup-2-casing", + "mapbox-layerGroup-2-trail", (addStyleLayerSlots[9].contents as HashMap)["id"]!!.contents ) assertEquals( - "mapbox-layerGroup-2-main", + "mapbox-layerGroup-2-casing", (addStyleLayerSlots[10].contents as HashMap)["id"]!!.contents ) assertEquals( - "mapbox-layerGroup-2-traffic", + "mapbox-layerGroup-2-main", (addStyleLayerSlots[11].contents as HashMap)["id"]!!.contents ) assertEquals( - "mapbox-layerGroup-2-restricted", + "mapbox-layerGroup-2-traffic", (addStyleLayerSlots[12].contents as HashMap)["id"]!!.contents ) assertEquals( - "mapbox-layerGroup-1-trailCasing", + "mapbox-layerGroup-2-restricted", (addStyleLayerSlots[13].contents as HashMap)["id"]!!.contents ) assertEquals( - "mapbox-layerGroup-1-trail", + "mapbox-layerGroup-2-violated", (addStyleLayerSlots[14].contents as HashMap)["id"]!!.contents ) assertEquals( - "mapbox-layerGroup-1-casing", + "mapbox-layerGroup-1-trailCasing", (addStyleLayerSlots[15].contents as HashMap)["id"]!!.contents ) assertEquals( - "mapbox-layerGroup-1-main", + "mapbox-layerGroup-1-trail", (addStyleLayerSlots[16].contents as HashMap)["id"]!!.contents ) assertEquals( - "mapbox-layerGroup-1-traffic", + "mapbox-layerGroup-1-casing", (addStyleLayerSlots[17].contents as HashMap)["id"]!!.contents ) assertEquals( - "mapbox-layerGroup-1-restricted", + "mapbox-layerGroup-1-main", (addStyleLayerSlots[18].contents as HashMap)["id"]!!.contents ) + assertEquals( + "mapbox-layerGroup-1-traffic", + (addStyleLayerSlots[19].contents as HashMap)["id"]!!.contents + ) + assertEquals( + "mapbox-layerGroup-1-restricted", + (addStyleLayerSlots[20].contents as HashMap)["id"]!!.contents + ) + assertEquals( + "mapbox-layerGroup-1-violated", + (addStyleLayerSlots[21].contents as HashMap)["id"]!!.contents + ) assertEquals( "mapbox-masking-layer-trailCasing", - (addStyleLayerSlots[19].contents as HashMap)["id"]!!.contents + (addStyleLayerSlots[22].contents as HashMap)["id"]!!.contents ) assertEquals( "mapbox-masking-layer-trail", - (addStyleLayerSlots[20].contents as HashMap)["id"]!!.contents + (addStyleLayerSlots[23].contents as HashMap)["id"]!!.contents ) assertEquals( "mapbox-masking-layer-casing", - (addStyleLayerSlots[21].contents as HashMap)["id"]!!.contents + (addStyleLayerSlots[24].contents as HashMap)["id"]!!.contents ) assertEquals( "mapbox-masking-layer-main", - (addStyleLayerSlots[22].contents as HashMap)["id"]!!.contents + (addStyleLayerSlots[25].contents as HashMap)["id"]!!.contents ) assertEquals( "mapbox-masking-layer-traffic", - (addStyleLayerSlots[23].contents as HashMap)["id"]!!.contents + (addStyleLayerSlots[26].contents as HashMap)["id"]!!.contents ) assertEquals( "mapbox-masking-layer-restricted", - (addStyleLayerSlots[24].contents as HashMap)["id"]!!.contents + (addStyleLayerSlots[27].contents as HashMap)["id"]!!.contents + ) + assertEquals( + "mapbox-masking-layer-violated", + (addStyleLayerSlots[28].contents as HashMap)["id"]!!.contents ) assertEquals( "mapbox-top-level-route-layer", - (addStyleLayerSlots[25].contents as HashMap)["id"]!!.contents + (addStyleLayerSlots[29].contents as HashMap)["id"]!!.contents ) assertEquals( "mapbox-navigation-waypoint-layer", - (addStyleLayerSlots[26].contents as HashMap)["id"]!!.contents + (addStyleLayerSlots[30].contents as HashMap)["id"]!!.contents ) assertEquals( "bottom-right", - (addStyleLayerSlots[26].contents as HashMap)["icon-anchor"]!!.contents + (addStyleLayerSlots[30].contents as HashMap)["icon-anchor"]!!.contents ) assertEquals( 33.3, ( - (addStyleLayerSlots[26].contents as HashMap) + (addStyleLayerSlots[30].contents as HashMap) ["icon-offset"]!!.contents as ArrayList ).first().contents ) assertEquals( 44.4, ( - (addStyleLayerSlots[26].contents as HashMap) + (addStyleLayerSlots[30].contents as HashMap) ["icon-offset"]!!.contents as ArrayList ).component2().contents ) assertEquals( "viewport", - (addStyleLayerSlots[26].contents as HashMap) + (addStyleLayerSlots[30].contents as HashMap) ["icon-pitch-alignment"]!!.contents ) assertEquals( diff --git a/libnavui-maps/src/test/java/com/mapbox/navigation/ui/maps/internal/route/line/MapboxRouteLineUtilsTest.kt b/libnavui-maps/src/test/java/com/mapbox/navigation/ui/maps/internal/route/line/MapboxRouteLineUtilsTest.kt index 8653869dc8a..1ead1203ddd 100644 --- a/libnavui-maps/src/test/java/com/mapbox/navigation/ui/maps/internal/route/line/MapboxRouteLineUtilsTest.kt +++ b/libnavui-maps/src/test/java/com/mapbox/navigation/ui/maps/internal/route/line/MapboxRouteLineUtilsTest.kt @@ -39,6 +39,7 @@ import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_1_SOU import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_1_TRAFFIC import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_1_TRAIL import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_1_TRAIL_CASING +import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_1_VIOLATED import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_2_CASING import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_2_MAIN import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_2_RESTRICTED @@ -46,6 +47,7 @@ import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_2_SOU import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_2_TRAFFIC import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_2_TRAIL import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_2_TRAIL_CASING +import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_2_VIOLATED import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_3_CASING import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_3_MAIN import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_3_RESTRICTED @@ -53,6 +55,7 @@ import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_3_SOU import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_3_TRAFFIC import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_3_TRAIL import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_3_TRAIL_CASING +import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LAYER_GROUP_3_VIOLATED import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.LOW_CONGESTION_VALUE import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.MASKING_LAYER_CASING import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.MASKING_LAYER_MAIN @@ -60,6 +63,7 @@ import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.MASKING_LAYER_RES import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.MASKING_LAYER_TRAFFIC import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.MASKING_LAYER_TRAIL import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.MASKING_LAYER_TRAIL_CASING +import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.MASKING_LAYER_VIOLATED import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.MODERATE_CONGESTION_VALUE import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.ORIGIN_MARKER_NAME import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.RESTRICTED_CONGESTION_VALUE @@ -240,6 +244,25 @@ class MapboxRouteLineUtilsTest { assertFalse(result[4].isInRestrictedSection) } + @Test + fun getViolatedSectionExpressionData() { + val route = loadNavigationRoute("route-with-violations.json") + + val result = MapboxRouteLineUtils.extractViolatedSectionsData( + route, + MapboxRouteLineUtils.granularDistancesProvider + ) + + assertEquals(7, result.size) + assertFalse(result[0].isInRestrictedSection) + assertTrue(result[1].isInRestrictedSection) + assertFalse(result[2].isInRestrictedSection) + assertTrue(result[3].isInRestrictedSection) + assertFalse(result[4].isInRestrictedSection) + assertTrue(result[5].isInRestrictedSection) + assertFalse(result[6].isInRestrictedSection) + } + @Test fun getRestrictedLineExpression() { val expectedExpressionContents = listOf( @@ -273,11 +296,45 @@ class MapboxRouteLineUtilsTest { checkExpression(expectedExpressionContents, expression) } + @Test + fun getViolatedSectionExpression() { + val expectedExpressionContents = listOf( + StringChecker("step"), + StringChecker("[line-progress]"), + StringChecker("[rgba, 0.0, 0.0, 0.0, 0.0]"), + DoubleChecker(0.2), + StringChecker("[rgba, 0.0, 0.0, 0.0, 0.0]"), + DoubleChecker(0.7515682978431553), + StringChecker("[rgba, 0.0, 255.0, 255.0, 1.0]"), + DoubleChecker(0.7709736343191205), + StringChecker("[rgba, 0.0, 0.0, 0.0, 0.0]"), + DoubleChecker(0.7712833059036543), + StringChecker("[rgba, 0.0, 255.0, 255.0, 1.0]"), + DoubleChecker(0.9302099290097133), + StringChecker("[rgba, 0.0, 0.0, 0.0, 0.0]"), + DoubleChecker(0.9303781273349865), + StringChecker("[rgba, 0.0, 255.0, 255.0, 1.0]"), + DoubleChecker(1.0), + StringChecker("[rgba, 0.0, 0.0, 0.0, 0.0]"), + ) + val route = loadNavigationRoute("route-with-violations.json") + val expData = MapboxRouteLineUtils.extractViolatedSectionsData( + route, + MapboxRouteLineUtils.granularDistancesProvider + ) + + val expression = MapboxRouteLineUtils.getRestrictedLineExpression( + 0.2, + 0, + Color.CYAN, + expData + ) + + checkExpression(expectedExpressionContents, expression) + } + @Test fun getRestrictedLineExpressionProducer() { - val colorResources = RouteLineColorResources.Builder() - .restrictedRoadColor(Color.CYAN) - .build() val expectedExpressionContents = listOf( StringChecker("step"), StringChecker("[line-progress]"), @@ -303,7 +360,44 @@ class MapboxRouteLineUtilsTest { expData, 0.2, 0, - colorResources + Color.CYAN + ).generateExpression() + + checkExpression(expectedExpressionContents, expression) + } + + @Test + fun getViolatedSectionExpressionProducer() { + val expectedExpressionContents = listOf( + StringChecker("step"), + StringChecker("[line-progress]"), + StringChecker("[rgba, 0.0, 0.0, 0.0, 0.0]"), + DoubleChecker(0.2), + StringChecker("[rgba, 0.0, 0.0, 0.0, 0.0]"), + DoubleChecker(0.7515682978431553), + StringChecker("[rgba, 0.0, 255.0, 255.0, 1.0]"), + DoubleChecker(0.7709736343191205), + StringChecker("[rgba, 0.0, 0.0, 0.0, 0.0]"), + DoubleChecker(0.7712833059036543), + StringChecker("[rgba, 0.0, 255.0, 255.0, 1.0]"), + DoubleChecker(0.9302099290097133), + StringChecker("[rgba, 0.0, 0.0, 0.0, 0.0]"), + DoubleChecker(0.9303781273349865), + StringChecker("[rgba, 0.0, 255.0, 255.0, 1.0]"), + DoubleChecker(1.0), + StringChecker("[rgba, 0.0, 0.0, 0.0, 0.0]"), + ) + val route = loadNavigationRoute("route-with-violations.json") + val expData = MapboxRouteLineUtils.extractViolatedSectionsData( + route, + MapboxRouteLineUtils.granularDistancesProvider + ) + + val expression = MapboxRouteLineUtils.getRestrictedLineExpressionProducer( + expData, + 0.2, + 0, + Color.CYAN, ).generateExpression() checkExpression(expectedExpressionContents, expression) @@ -343,6 +437,26 @@ class MapboxRouteLineUtilsTest { assertEquals(expectedExpression, expression.toString()) } + @Test + fun getViolatedSectionExpression_whenNoViolationsInRoute() { + val expectedExpression = "[step, [line-progress], [rgba, 0.0, 0.0, 0.0, 0.0], 0.2, " + + "[rgba, 0.0, 0.0, 0.0, 0.0]]" + val route = loadNavigationRoute("short_route.json") + val expData = MapboxRouteLineUtils.extractViolatedSectionsData( + route, + MapboxRouteLineUtils.granularDistancesProvider + ) + + val expression = MapboxRouteLineUtils.getRestrictedLineExpression( + 0.2, + 0, + -1, + expData + ) + + assertEquals(expectedExpression, expression.toString()) + } + @Test fun getVanishingRouteLineExpressionTest() { val expectedExpression = "[step, [line-progress], [rgba, 255.0, 77.0, 77.0, 1.0]" + @@ -367,6 +481,7 @@ class MapboxRouteLineUtilsTest { fun layersAreInitialized() { val options = mockk { every { displayRestrictedRoadSections } returns true + every { displayViolatedSections } returns true } val style = mockk