Skip to content

Commit 6578e2c

Browse files
authored
Merge pull request #18 from AdrianAlzateA/feature/support-detour-road-events
Add support for Detour Road Events and upgrade library to NET 9
2 parents bb60ae0 + 777337b commit 6578e2c

File tree

11 files changed

+2314
-1943
lines changed

11 files changed

+2314
-1943
lines changed

.github/workflows/publish-package.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
- uses: actions/checkout@v3
1717
- uses: actions/setup-dotnet@v2
1818
with:
19-
dotnet-version: 6.0.x
19+
dotnet-version: 9.0.x
2020
- name: Build
2121
run: dotnet build --configuration Release
2222
- name: Run unit tests

README.md

+3-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# WZDx .NET Library
22

3-
This repository contains the source code for IBI Group's [.NET 6.0](https://docs.microsoft.com/en-us/dotnet/core/whats-new/dotnet-6) [WZDx (Work Zone Data Exchange)](https://github.com/usdot-jpo-ode/wzdx) class library, `IBI.WZDx`.
3+
This repository contains the source code for IBI Group's [.NET 9.0](https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-9/overview) [WZDx (Work Zone Data Exchange)](https://github.com/usdot-jpo-ode/wzdx) class library, `IBI.WZDx`.
44

55
## About
66

7-
The `IBI.WZDx` class library provides models and utitlies for producing and consuming [Work Zone Data Exchange (WZDx)](https://github.com/usdot-jpo-ode/wzdx) data feeds.
7+
The `IBI.WZDx` class library provides models and utilities for producing and consuming [Work Zone Data Exchange (WZDx)](https://github.com/usdot-jpo-ode/wzdx) data feeds.
88

99
The library provides the following functionality:
1010

@@ -16,8 +16,6 @@ The library provides the following functionality:
1616

1717
WZDx versions 4.0, 4.1, and 4.2 are supported; the [WzdxSerializer](./src/IBI.WZDx/Serialization/WzdxSerializer.cs) defaults to outputting v4.2 (latest WZDx).
1818

19-
[Detour road events](https://github.com/usdot-jpo-ode/wzdx/blob/main/spec-content/objects/DetourRoadEvent.md) are not supported. When provided with a Work Zone Feed that includes detour road events, the WzdxSerializer.DeserializeFeed method will deserialize the detour events into a [RoadEventFeature](./src/IBI.WZDx/Models/RoadEvents/RoadEventFeature.cs) with `Properties` as `null`.
20-
2119
## Usage
2220

2321
### NuGet Package
@@ -71,7 +69,7 @@ The PATCH version number is incremented as changes are made to the library that
7169

7270
## Tests
7371

74-
This solution includes a [IBI.WZDx.UnitTests](/tests/IBI.WZDx.UnitTests/) Xunit test project. Run all unit tests with the following command:
72+
This solution includes a [IBI.WZDx.UnitTests](tests/IBI.WZDx.UnitTests) Xunit test project. Run all unit tests with the following command:
7573

7674
```
7775
dotnet test
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using System;
2+
3+
namespace IBI.WZDx.Equality;
4+
5+
/// <summary>
6+
/// Extensions for <see cref="double"/> related to equality.
7+
/// </summary>
8+
public static class DoubleExtensions
9+
{
10+
/// <summary>
11+
/// Determines whether two nullable double values are approximately equal.
12+
/// </summary>
13+
/// <param name="value">The first nullable double value to compare.</param>
14+
/// <param name="other">The second nullable double value to compare.</param>
15+
/// <returns>
16+
/// True if both values are null, or their absolute difference is less than or equal
17+
/// to <see cref="double.Epsilon"/>; otherwise, false.
18+
/// </returns>
19+
public static bool NullEqualsApproximation(this double? value, double? other)
20+
{
21+
if (value is null && other is null)
22+
return true;
23+
24+
if (value is null || other is null)
25+
return false;
26+
27+
return Math.Abs(value.Value - other.Value) <= double.Epsilon;
28+
}
29+
}

src/IBI.WZDx/IBI.WZDx.csproj

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net6.0</TargetFramework>
4+
<TargetFramework>net9.0</TargetFramework>
55
<Nullable>enable</Nullable>
66
<PackageId>IBI.WZDx</PackageId>
77
<Description>Models and utitlies for producing and consuming Work Zone Data Exchange (WZDx) data feeds.</Description>
88
<PackageTags>WZDx;Work Zone Data Exchange;Work Zone Feed;Road Event;Device Feed;Field Device;GeoJSON</PackageTags>
99
<Authors>IBI Group</Authors>
10-
<VersionPrefix>4.2.0</VersionPrefix>
10+
<VersionPrefix>4.2.1</VersionPrefix>
1111
<GenerateDocumentationFile>true</GenerateDocumentationFile>
1212
<PackageReadmeFile>README.md</PackageReadmeFile>
1313
<RepositoryType>git</RepositoryType>
@@ -21,8 +21,8 @@
2121
</ItemGroup>
2222

2323
<ItemGroup>
24-
<PackageReference Include="Macross.Json.Extensions" Version="2.2.0" />
25-
<PackageReference Include="System.Text.Json" Version="6.0.2" />
24+
<PackageReference Include="Macross.Json.Extensions" Version="3.0.0" />
25+
<PackageReference Include="System.Text.Json" Version="9.0.2" />
2626
</ItemGroup>
2727

2828
</Project>

src/IBI.WZDx/Models/RoadEvents/RoadEventCoreDetails.cs

+7-1
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,13 @@ public record RoadEventCoreDetails(
5757
string? Description = null,
5858
DateTimeOffset? CreationDate = null,
5959
DateTimeOffset? UpdateDate = null,
60+
#pragma warning disable CS0618 // Type or member is obsolete
6061
[property: Obsolete("Use RelatedRoadEvents instead.")]Relationship? Relationship = null
62+
#pragma warning restore CS0618 // Type or member is obsolete
6163
)
6264
{
6365
/// <summary>
64-
/// Determine if an other <see cref="RoadEventCoreDetails"/> is equal to this <see cref="RoadEventCoreDetails"/>.
66+
/// Determine if another <see cref="RoadEventCoreDetails"/> is equal to this <see cref="RoadEventCoreDetails"/>.
6567
/// </summary>
6668
public virtual bool Equals(RoadEventCoreDetails? other)
6769
{
@@ -75,7 +77,9 @@ public virtual bool Equals(RoadEventCoreDetails? other)
7577
&& Description == other.Description
7678
&& CreationDate == other.CreationDate
7779
&& UpdateDate == other.UpdateDate
80+
#pragma warning disable CS0618 // Type or member is obsolete
7881
&& Relationship == other.Relationship;
82+
#pragma warning restore CS0618 // Type or member is obsolete
7983
}
8084

8185
/// <inheritdoc/>
@@ -105,7 +109,9 @@ public override int GetHashCode()
105109
hash.Add(Description);
106110
hash.Add(CreationDate);
107111
hash.Add(UpdateDate);
112+
#pragma warning disable CS0618 // Type or member is obsolete
108113
hash.Add(Relationship);
114+
#pragma warning restore CS0618 // Type or member is obsolete
109115

110116
return hash.ToHashCode();
111117
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using IBI.WZDx.Equality;
4+
5+
namespace IBI.WZDx.Models.RoadEvents.WorkZones;
6+
7+
/// <summary>
8+
/// Describes a detour on a roadway. It can be either a segment of a detour
9+
/// (each segment represented by its own <see cref="DetourRoadEvent"/>) or the entire detour.
10+
/// </summary>
11+
/// <param name="CoreDetails">
12+
/// The core details of the road event that apply to all types of road events, not specific to detour road events.
13+
/// </param>
14+
/// <param name="StartDate">
15+
/// The UTC time and date when the event begins.
16+
/// </param>
17+
/// <param name="EndDate">
18+
/// The UTC time and date when the event ends.
19+
/// </param>
20+
/// <param name="IsStartDateVerified">
21+
/// Indicates if work has been confirmed to have started, such as from a person or field device.
22+
/// </param>
23+
/// <param name="IsEndDateVerified">
24+
/// Indicates if work has been confirmed to have ended, such as from a person or field device.
25+
/// </param>
26+
/// <param name="BeginningCrossStreet">
27+
/// Name or number of the nearest cross street along the roadway where the event begins.
28+
/// </param>
29+
/// <param name="EndingCrossStreet">
30+
/// Name or number of the nearest cross street along the roadway where the event ends.
31+
/// </param>
32+
/// <param name="BeginningMilepost">
33+
/// The linear distance measured against a milepost marker along a roadway where the event begins.
34+
/// </param>
35+
/// <param name="EndingMilepost">
36+
/// The linear distance measured against a milepost marker along a roadway where the event ends.
37+
/// </param>
38+
/// <param name="EventStatus">
39+
/// The status of the event.
40+
/// </param>
41+
/// <param name="StartDateAccuracy">
42+
/// A measure of how accurate the start date-time is.
43+
/// </param>
44+
/// <param name="EndDateAccuracy">
45+
/// A measure of how accurate the end date-time is.
46+
/// </param>
47+
public record DetourRoadEvent(
48+
RoadEventCoreDetails CoreDetails,
49+
DateTimeOffset StartDate,
50+
DateTimeOffset EndDate,
51+
bool? IsStartDateVerified = null,
52+
bool? IsEndDateVerified = null,
53+
string? BeginningCrossStreet = null,
54+
string? EndingCrossStreet = null,
55+
double? BeginningMilepost = null,
56+
double? EndingMilepost = null,
57+
#pragma warning disable CS0618 // Type or member is obsolete
58+
[property: Obsolete("Determine an event's status based on the dates and verification properties.")]
59+
EventStatus? EventStatus = null,
60+
[property: Obsolete("Use IsStartDateVerified instead.")]TimeVerification? StartDateAccuracy = null,
61+
[property: Obsolete("Use IsEndDateVerified instead.")]TimeVerification? EndDateAccuracy = null
62+
#pragma warning restore CS0618 // Type or member is obsolete
63+
) : IRoadEvent
64+
{
65+
/// <summary>
66+
/// Determine if another <see cref="DetourRoadEvent"/> is equal to this <see cref="DetourRoadEvent"/>.
67+
/// </summary>
68+
public virtual bool Equals(DetourRoadEvent? other)
69+
{
70+
return other != null
71+
&& CoreDetails == other.CoreDetails
72+
&& StartDate == other.StartDate
73+
&& EndDate == other.EndDate
74+
&& IsStartDateVerified == other.IsStartDateVerified
75+
&& IsEndDateVerified == other.IsEndDateVerified
76+
&& BeginningCrossStreet == other.BeginningCrossStreet
77+
&& EndingCrossStreet == other.EndingCrossStreet
78+
&& BeginningMilepost.NullEqualsApproximation(other.BeginningMilepost)
79+
&& EndingMilepost.NullEqualsApproximation(other.EndingMilepost)
80+
#pragma warning disable CS0618 // Type or member is obsolete
81+
&& EventStatus == other.EventStatus
82+
&& StartDateAccuracy == other.StartDateAccuracy
83+
&& EndDateAccuracy == other.EndDateAccuracy;
84+
#pragma warning restore CS0618 // Type or member is obsolete
85+
}
86+
87+
/// <inheritdoc/>
88+
public override int GetHashCode()
89+
{
90+
var hash = new HashCode();
91+
92+
hash.Add(CoreDetails);
93+
hash.Add(StartDate);
94+
hash.Add(EndDate);
95+
hash.Add(IsStartDateVerified);
96+
hash.Add(IsEndDateVerified);
97+
hash.Add(BeginningCrossStreet);
98+
hash.Add(EndingCrossStreet);
99+
hash.Add(BeginningMilepost);
100+
hash.Add(EndingMilepost);
101+
#pragma warning disable CS0618 // Type or member is obsolete
102+
hash.Add(EventStatus);
103+
hash.Add(StartDateAccuracy);
104+
hash.Add(EndDateAccuracy);
105+
#pragma warning restore CS0618 // Type or member is obsolete
106+
107+
return hash.ToHashCode();
108+
}
109+
}

src/IBI.WZDx/Models/RoadEvents/WorkZones/WorkZoneRoadEvent.cs

+17-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ namespace IBI.WZDx.Models.RoadEvents.WorkZones;
7474
/// The status of the event.
7575
/// </param>
7676
/// <param name="TypesOfWork">
77-
/// A list of the types of work being done in a road event and an indiciation of if each type
77+
/// A list of the types of work being done in a road event and an indication of if each type
7878
/// results in an architectural change to the roadway.
7979
/// </param>
8080
/// <param name="WorkerPresence">
@@ -108,8 +108,10 @@ public record WorkZoneRoadEvent(
108108
VehicleImpact VehicleImpact,
109109
bool? IsStartDateVerified = null,
110110
bool? IsEndDateVerified = null,
111+
#pragma warning disable CS0618 // Type or member is obsolete
111112
[property: Obsolete("Use IsStartDateVerified instead.")]TimeVerification? StartDateAccuracy = null,
112113
[property: Obsolete("Use IsEndDateVerified instead.")]TimeVerification? EndDateAccuracy = null,
114+
#pragma warning restore CS0618 // Type or member is obsolete
113115
bool? IsStartPositionVerified = null,
114116
bool? IsEndPositionVerified = null,
115117
[property: Obsolete("Use IsStartPositionVerified instead.")]SpatialVerification? BeginningAccuracy = null,
@@ -119,8 +121,10 @@ public record WorkZoneRoadEvent(
119121
string? EndingCrossStreet = null,
120122
double? BeginningMilepost = null,
121123
double? EndingMilepost = null,
124+
#pragma warning disable CS0618 // Type or member is obsolete
122125
[property: Obsolete("Determine an event's status based on the dates and verification properties.")]
123126
EventStatus? EventStatus = null,
127+
#pragma warning restore CS0618 // Type or member is obsolete
124128
IEnumerable<TypeOfWork>? TypesOfWork = null,
125129
WorkerPresence? WorkerPresence = null,
126130
double? ReducedSpeedLimitKph = null,
@@ -142,18 +146,24 @@ public virtual bool Equals(WorkZoneRoadEvent? other)
142146
&& VehicleImpact == other.VehicleImpact
143147
&& IsStartDateVerified == other.IsStartDateVerified
144148
&& IsEndDateVerified == other.IsEndDateVerified
149+
#pragma warning disable CS0618 // Type or member is obsolete
145150
&& StartDateAccuracy == other.StartDateAccuracy
146151
&& EndDateAccuracy == other.EndDateAccuracy
152+
#pragma warning restore CS0618 // Type or member is obsolete
147153
&& IsStartPositionVerified == other.IsStartPositionVerified
148154
&& IsEndPositionVerified == other.IsEndPositionVerified
155+
#pragma warning disable CS0618 // Type or member is obsolete
149156
&& BeginningAccuracy == other.BeginningAccuracy
150157
&& EndingAccuracy == other.EndingAccuracy
158+
#pragma warning restore CS0618 // Type or member is obsolete
151159
&& Lanes.NullHandlingSequenceEqual(other.Lanes)
152160
&& BeginningCrossStreet == other.BeginningCrossStreet
153161
&& EndingCrossStreet == other.EndingCrossStreet
154162
&& BeginningMilepost == other.BeginningMilepost
155163
&& EndingMilepost == other.EndingMilepost
164+
#pragma warning disable CS0618 // Type or member is obsolete
156165
&& EventStatus == other.EventStatus
166+
#pragma warning restore CS0618 // Type or member is obsolete
157167
&& TypesOfWork.NullHandlingSequenceEqual(other.TypesOfWork)
158168
&& WorkerPresence == other.WorkerPresence
159169
&& ReducedSpeedLimitKph == other.ReducedSpeedLimitKph
@@ -174,12 +184,16 @@ public override int GetHashCode()
174184
hash.Add(VehicleImpact);
175185
hash.Add(IsStartDateVerified);
176186
hash.Add(IsEndDateVerified);
187+
#pragma warning disable CS0618 // Type or member is obsolete
177188
hash.Add(StartDateAccuracy);
178189
hash.Add(EndDateAccuracy);
190+
#pragma warning restore CS0618 // Type or member is obsolete
179191
hash.Add(IsStartPositionVerified);
180192
hash.Add(IsEndPositionVerified);
193+
#pragma warning disable CS0618 // Type or member is obsolete
181194
hash.Add(BeginningAccuracy);
182195
hash.Add(EndingAccuracy);
196+
#pragma warning restore CS0618 // Type or member is obsolete
183197

184198
if (Lanes != null)
185199
{
@@ -193,7 +207,9 @@ public override int GetHashCode()
193207
hash.Add(EndingCrossStreet);
194208
hash.Add(BeginningMilepost);
195209
hash.Add(EndingMilepost);
210+
#pragma warning disable CS0618 // Type or member is obsolete
196211
hash.Add(EventStatus);
212+
#pragma warning restore CS0618 // Type or member is obsolete
197213

198214
if (TypesOfWork != null)
199215
{

src/IBI.WZDx/Serialization/RoadEventConverter.cs

+10-14
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,18 @@ internal class RoadEventConverter : JsonConverter<IRoadEvent>
3434
/// </returns>
3535
public override IRoadEvent? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
3636
{
37-
using var jsonDocument = JsonDocument.ParseValue(ref reader);
38-
var jsonObject = jsonDocument.RootElement.GetRawText();
39-
var eventType = jsonDocument.RootElement.GetProperty("core_details")
37+
using JsonDocument jsonDocument = JsonDocument.ParseValue(ref reader);
38+
string jsonObject = jsonDocument.RootElement.GetRawText();
39+
string? eventType = jsonDocument.RootElement.GetProperty("core_details")
4040
.GetProperty("event_type")
4141
.GetString();
4242

43-
Func<Type, IRoadEvent?> deserializeAsType = type =>
44-
JsonSerializer.Deserialize(jsonObject, type, options) as IRoadEvent;
45-
43+
IRoadEvent? DeserializeAsType(Type type) => JsonSerializer.Deserialize(jsonObject, type, options) as IRoadEvent;
44+
4645
return eventType switch
4746
{
48-
"work-zone" => deserializeAsType(typeof(WorkZoneRoadEvent)),
49-
"detour" => null,
47+
"work-zone" => DeserializeAsType(typeof(WorkZoneRoadEvent)),
48+
"detour" => DeserializeAsType(typeof(DetourRoadEvent)),
5049
_ => throw new JsonException($"Unsupported event type '{eventType}'.")
5150
};
5251
}
@@ -60,12 +59,9 @@ internal class RoadEventConverter : JsonConverter<IRoadEvent>
6059
/// <param name="options">An object that specifies serialization options to use.</param>
6160
/// <exception cref="ArgumentNullException"><paramref name="value"/> cannot be null.</exception>
6261
public override void Write(Utf8JsonWriter writer, IRoadEvent value, JsonSerializerOptions options)
63-
{
64-
if (value == null)
65-
throw new ArgumentNullException("value");
66-
67-
var type = value.GetType();
68-
62+
{
63+
ArgumentNullException.ThrowIfNull(value);
64+
Type type = value.GetType();
6965
JsonSerializer.Serialize(writer, value, type, options);
7066
}
7167
}

0 commit comments

Comments
 (0)