Skip to content

Commit ac850a0

Browse files
Merge pull request #620 from AdvSol-Darrel/2.0.2
HMCR-1062
2 parents 3562fcf + 358d4ff commit ac850a0

File tree

7 files changed

+365
-526
lines changed

7 files changed

+365
-526
lines changed

.pipeline/package-lock.json

+3-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.pipeline/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"license": "Apache-2.0",
2020
"dependencies": {
2121
"axios": "^0.21.1",
22-
"lodash": "^4.17.19",
22+
"lodash": "^4.17.21",
2323
"@bcgov/pipeline-cli": "^1.0.1-0"
2424
}
2525
}

api/Hmcr.Chris/Hmcr.Chris.csproj

+3-1
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@
2323
</ItemGroup>
2424

2525
<ItemGroup>
26+
<PackageReference Include="GeoJSON.Net" Version="1.2.19" />
2627
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.2" />
2728
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.2" />
2829
<PackageReference Include="Microsoft.Extensions.Http" Version="3.1.2" />
29-
<PackageReference Include="NetTopologySuite" Version="2.1.0" />
30+
<PackageReference Include="NetTopologySuite" Version="2.2.0" />
31+
<PackageReference Include="NetTopologySuite.IO.GeoJSON" Version="2.0.4" />
3032
</ItemGroup>
3133

3234
</Project>

api/Hmcr.Chris/InventoryApi.cs

+90-280
Large diffs are not rendered by default.

api/Hmcr.Chris/InventoryQueries.cs

+11
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@
33

44
namespace Hmcr.Chris
55
{
6+
public static class GeoServerEndpoint
7+
{
8+
public const string Guardrail = "GR_ASSOCIATED_WITH_RFI";
9+
public const string HighwayProfile = "HP_ASSOCIATED_WITH_RFI";
10+
public const string MaintenanceClass = "MC_ASSOCIATED_WITH_RFI";
11+
public const string SurfaceType = "SURF_ASSOCIATED_WITH_RFI";
12+
}
13+
614
public class InventoryQueries
715
{
816
private string _inventoryAssocWithLineQuery;
@@ -31,5 +39,8 @@ public readonly string StructureOnRfiSegment
3139

3240
public readonly string RestAreaOnRfiSegment
3341
= "service=WFS&version=1.1.0&request=GetFeature&typeName=cwr:RA_BY_RFI&srsName=EPSG:4326&outputFormat=application/json&cql_filter=RFI_UNIQUE='{0}'";
42+
43+
public readonly string InventoryAssociatedWithRFI
44+
= "service=WFS&version=1.0.0&request=GetFeature&typeName=cwr:{0}&maxFeatures={1}&srsName=EPSG:4326&outputFormat=application/json&viewParams=ne_unique:{2}";
3445
}
3546
}

api/Hmcr.Domain/Hangfire/WorkReportJobService.cs

+94-201
Large diffs are not rendered by default.

api/Hmcr.Domain/Services/SpatialService.cs

+163-40
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
using System.Collections.Generic;
77
using System.Linq;
88
using System.Threading.Tasks;
9-
9+
using GJFeature = GeoJSON.Net.Feature; // use an alias since Feature exists in HttpClients.Models
1010
namespace Hmcr.Domain.Services
1111
{
1212
public interface ISpatialService
@@ -19,17 +19,14 @@ public interface ISpatialService
1919
(decimal offset, string rfiSegment, string rfiSegmentName, string thresholdLevel, Dictionary<string, List<string>> errors);
2020
Task<(SpValidationResult result, decimal snappedStartOffset, decimal snappedEndOffset, Point startPoint, Point endPoint, List<Line> lines, RfiSegment rfiSegment)>
2121
ValidateLrsLineAsync(decimal startOffset, decimal endOffset, string rfiSegment, string rfiSegmentName, string thresholdLevel, Dictionary<string, List<string>> errors);
22-
Task<(SpValidationResult result, List<SurfaceType> surfaceTypes)> GetSurfaceTypeAssocWithLineAsync(NetTopologySuite.Geometries.Geometry geometry, string recordNumber);
23-
Task<(SpValidationResult result, SurfaceType surfaceType)> GetSurfaceTypeAssocWithPointAsync(NetTopologySuite.Geometries.Geometry geometry, string recordNumber);
24-
Task<(SpValidationResult result, List<MaintenanceClass> maintenanceClasses)> GetMaintenanceClassAssocWithLineAsync(NetTopologySuite.Geometries.Geometry geometry, string recordNumber);
25-
Task<(SpValidationResult result, MaintenanceClass maintenanceClass)> GetMaintenanceClassAssocWithPointAsync(NetTopologySuite.Geometries.Geometry geometry, string recordNumber);
22+
2623
Task<(SpValidationResult result, List<Structure> structures)> GetStructuresOnRFISegmentAsync(string rfiSegmentName, string recordNumber);
27-
Task<(SpValidationResult result, HighwayProfile highwayProfile)> GetHighwayProfileAssocWithPointAsync(NetTopologySuite.Geometries.Geometry geometry, string recordNumber);
28-
Task<(SpValidationResult result, List<HighwayProfile> highwayProfiles)> GetHighwayProfileAssocWithLineAsync(NetTopologySuite.Geometries.Geometry geometry, string recordNumber);
29-
Task<(SpValidationResult result, List<Guardrail> guardrails)> GetGuardrailAssociatedWithLineAsync(NetTopologySuite.Geometries.Geometry geometry, string recordNumber);
30-
Task<(SpValidationResult result, Guardrail guardrail)> GetGuardrailAssociatedWithPointAsync(NetTopologySuite.Geometries.Geometry geometry, string recordNumber);
3124
Task<(SpValidationResult result, List<RestArea> restAreas)> GetRestAreasOnRFISegmentAsync(string rfiSegmentName, string recordNumber);
3225

26+
Task<(SpValidationResult result, string message, List<SurfaceType> surfaceTypes)> GetSurfaceTypesAssocWithRFISegment(string rfiSegmentName, string recordNumber, decimal startOffset, decimal endOffset);
27+
Task<(SpValidationResult result, string message, List<MaintenanceClass> maintenanceClasses)> GetMaintenanceClassAssocWithRFISegment(string rfiSegmentName, string recordNumber, decimal startOffset, decimal endOffset);
28+
Task<(SpValidationResult result, string message, List<HighwayProfile> highwayProfiles)> GetHighwayProfileAssocWithRFISegment(string rfiSegmentName, string recordNumber, decimal startOffset, decimal endOffset);
29+
Task<(SpValidationResult result, string message, List<Guardrail> guardrails)> GetGuardrailsAssocWithRFISegment(string rfiSegmentName, string recordNumber, decimal startOffset, decimal endOffset);
3330
}
3431

3532
public class SpatialService : ISpatialService
@@ -254,67 +251,193 @@ public SpatialService(IOasApi oasApi, IFieldValidatorService validator, ILookupC
254251
return (SpValidationResult.Success, rfiDetail);
255252
}
256253

257-
public async Task<(SpValidationResult result, List<SurfaceType> surfaceTypes)> GetSurfaceTypeAssocWithLineAsync(NetTopologySuite.Geometries.Geometry geometry, string recordNumber)
254+
public async Task<(SpValidationResult result, string message, List<SurfaceType> surfaceTypes)> GetSurfaceTypesAssocWithRFISegment(string rfiSegmentName, string recordNumber,
255+
decimal startOffset, decimal endOffset)
258256
{
259-
var surfaceTypes = await _inventoryApi.GetSurfaceTypeAssociatedWithLine(geometry, recordNumber);
257+
var surfaceTypes = new List<SurfaceType>();
258+
var validationResult = SpValidationResult.Success;
260259

261-
return (SpValidationResult.Success, surfaceTypes);
262-
}
260+
//retrieve the feature collection for the highway unique from the ogs endpoint
261+
var (featureCollection, errorMsg) = await _inventoryApi.GetSurfaceType(rfiSegmentName, recordNumber);
262+
263+
var foundFeatures = FindFeaturesWithinSegment(featureCollection, startOffset, endOffset);
263264

264-
public async Task<(SpValidationResult result, SurfaceType surfaceType)> GetSurfaceTypeAssocWithPointAsync(NetTopologySuite.Geometries.Geometry geometry, string recordNumber)
265-
{
266-
var surfaceType = await _inventoryApi.GetSurfaceTypeAssociatedWithPoint(geometry, recordNumber);
265+
if (foundFeatures.Features.Count > 0)
266+
{
267+
foreach (var feature in foundFeatures.Features)
268+
{
269+
SurfaceType surfaceType = new SurfaceType();
270+
surfaceType.Length = (double)feature.Properties["LENGTH_KM"];
271+
surfaceType.Type = (string)feature.Properties["SURFACE_TYPE"];
272+
surfaceTypes.Add(surfaceType);
273+
}
274+
}
275+
else
276+
{
277+
if (errorMsg.Length > 0)
278+
validationResult = SpValidationResult.Fail;
279+
}
267280

268-
return (SpValidationResult.Success, surfaceType);
281+
return (validationResult, errorMsg, surfaceTypes);
269282
}
270283

271-
public async Task<(SpValidationResult result, MaintenanceClass maintenanceClass)> GetMaintenanceClassAssocWithPointAsync(NetTopologySuite.Geometries.Geometry geometry, string recordNumber)
284+
public async Task<(SpValidationResult result, string message, List<MaintenanceClass> maintenanceClasses)> GetMaintenanceClassAssocWithRFISegment(string rfiSegmentName, string recordNumber,
285+
decimal startOffset, decimal endOffset)
272286
{
273-
var maintenanceClass = await _inventoryApi.GetMaintenanceClassesAssociatedWithPoint(geometry, recordNumber);
287+
var maintenanceClasses = new List<MaintenanceClass>();
288+
var validationResult = SpValidationResult.Success;
289+
290+
var (featureCollection, errorMsg) = await _inventoryApi.GetMaintenanceClass(rfiSegmentName, recordNumber);
291+
292+
var foundFeatures = FindFeaturesWithinSegment(featureCollection, startOffset, endOffset);
293+
294+
if (foundFeatures != null)
295+
{
296+
foreach (var feature in foundFeatures.Features)
297+
{
298+
MaintenanceClass maintenanceClass = new MaintenanceClass();
299+
maintenanceClass.Length = (double)feature.Properties["LENGTH_KM"];
300+
maintenanceClass.SummerRating = (string)feature.Properties["SUMMER_CLASS_RATING"];
301+
maintenanceClass.WinterRating = (string)feature.Properties["WINTER_CLASS_RATING"];
302+
303+
maintenanceClasses.Add(maintenanceClass);
304+
}
305+
}
306+
else
307+
{
308+
if (errorMsg.Length > 0)
309+
validationResult = SpValidationResult.Fail;
310+
}
274311

275-
return (SpValidationResult.Success, maintenanceClass);
312+
return (validationResult, errorMsg, maintenanceClasses);
276313
}
277314

278-
public async Task<(SpValidationResult result, List<MaintenanceClass> maintenanceClasses)> GetMaintenanceClassAssocWithLineAsync(NetTopologySuite.Geometries.Geometry geometry, string recordNumber)
315+
public async Task<(SpValidationResult result, string message, List<HighwayProfile> highwayProfiles)> GetHighwayProfileAssocWithRFISegment(string rfiSegmentName, string recordNumber,
316+
decimal startOffset, decimal endOffset)
279317
{
280-
var maintenanceClasses = await _inventoryApi.GetMaintenanceClassesAssociatedWithLine(geometry, recordNumber);
318+
var highwayProfiles = new List<HighwayProfile>();
319+
var validationResult = SpValidationResult.Success;
281320

282-
return (SpValidationResult.Success, maintenanceClasses);
283-
}
321+
var (featureCollection, errorMsg) = await _inventoryApi.GetHighwayProfile(rfiSegmentName, recordNumber);
284322

285-
public async Task<(SpValidationResult result, List<Structure> structures)> GetStructuresOnRFISegmentAsync(string rfiSegmentName, string recordNumber)
286-
{
287-
var structures = await _inventoryApi.GetStructuresOnRFISegment(rfiSegmentName, recordNumber);
323+
var foundFeatures = FindFeaturesWithinSegment(featureCollection, startOffset, endOffset);
288324

289-
return (SpValidationResult.Success, structures);
325+
if (foundFeatures.Features.Count > 0)
326+
{
327+
foreach (var feature in foundFeatures.Features)
328+
{
329+
HighwayProfile highwayProfile = new HighwayProfile();
330+
highwayProfile.Length = (double)feature.Properties["LENGTH_KM"];
331+
highwayProfile.NumberOfLanes = Convert.ToInt32(feature.Properties["NUMBER_OF_LANES"]);
332+
highwayProfile.DividedHighwayFlag = (string)feature.Properties["DIVIDED_HIGHWAY_FLAG"];
333+
334+
highwayProfiles.Add(highwayProfile);
335+
}
336+
}
337+
else
338+
{
339+
if (errorMsg.Length > 0)
340+
validationResult = SpValidationResult.Fail;
341+
}
342+
343+
return (validationResult, errorMsg, highwayProfiles);
290344
}
291345

292-
public async Task<(SpValidationResult result, List<HighwayProfile> highwayProfiles)> GetHighwayProfileAssocWithLineAsync(NetTopologySuite.Geometries.Geometry geometry, string recordNumber)
346+
public async Task<(SpValidationResult result, string message, List<Guardrail> guardrails)> GetGuardrailsAssocWithRFISegment(string rfiSegmentName, string recordNumber,
347+
decimal startOffset, decimal endOffset)
293348
{
294-
var highwayProfiles = await _inventoryApi.GetHighwayProfileAssociatedWithLine(geometry, recordNumber);
349+
var guardrails = new List<Guardrail>();
350+
var validationResult = SpValidationResult.Success;
351+
352+
var (featureCollection, errorMsg) = await _inventoryApi.GetGuardrail(rfiSegmentName, recordNumber);
353+
354+
var foundFeatures = FindFeaturesWithinSegment(featureCollection, startOffset, endOffset);
295355

296-
return (SpValidationResult.Success, highwayProfiles);
356+
if (foundFeatures.Features.Count > 0)
357+
{
358+
foreach (var feature in foundFeatures.Features)
359+
{
360+
Guardrail guardrail = new Guardrail();
361+
guardrail.Length = (double)feature.Properties["LENGTH_KM"];
362+
guardrail.GuardrailType = (string)feature.Properties["GUARDRAIL_TYPE"];
363+
364+
guardrails.Add(guardrail);
365+
}
366+
}
367+
else
368+
{
369+
if (errorMsg.Length > 0)
370+
validationResult = SpValidationResult.Fail;
371+
}
372+
373+
return (validationResult, errorMsg, guardrails);
297374
}
298375

299-
public async Task<(SpValidationResult result, HighwayProfile highwayProfile)> GetHighwayProfileAssocWithPointAsync(NetTopologySuite.Geometries.Geometry geometry, string recordNumber)
376+
377+
private GJFeature.FeatureCollection FindFeaturesWithinSegment(GJFeature.FeatureCollection featureCollection, decimal startOffset, decimal endOffset)
300378
{
301-
var highwayProfile = await _inventoryApi.GetHighwayProfileAssociatedWithPoint(geometry, recordNumber);
379+
GJFeature.FeatureCollection foundFeatures = new GJFeature.FeatureCollection();
302380

303-
return (SpValidationResult.Success, highwayProfile);
381+
if (featureCollection != null)
382+
{
383+
//since we got the features for the entire highway we need to determine which ones actually fall within our offsets
384+
385+
//need to handle scenarios when the coordinates are 'backwards'
386+
// to do this we'll set the end offset as the larger value and the start as the smaller
387+
decimal adjStartOffset = (startOffset > endOffset) ? endOffset : startOffset;
388+
decimal adjEndOffset = (startOffset > endOffset) ? startOffset : endOffset;
389+
390+
decimal newStartOffset = adjStartOffset;
391+
392+
foreach (var feature in featureCollection.Features)
393+
{
394+
//get the begin & end KM, have to use convert instead of explicit cast because of 0
395+
var beginKM = Convert.ToDecimal(feature.Properties["BEGIN_KM"]);
396+
var endKM = Convert.ToDecimal(feature.Properties["END_KM"]);
397+
398+
//it's in our range
399+
if (newStartOffset <= adjEndOffset)
400+
{
401+
if ((newStartOffset >= beginKM) && (newStartOffset <= endKM))
402+
{
403+
var lengthKM = GetAdjustedLengthKM(adjStartOffset, adjEndOffset, beginKM, endKM, Convert.ToDecimal(feature.Properties["LENGTH_KM"]));
404+
newStartOffset += lengthKM;
405+
406+
feature.Properties["LENGTH_KM"] = (double)lengthKM;
407+
408+
foundFeatures.Features.Add(feature);
409+
}
410+
}
411+
}
412+
}
413+
414+
return foundFeatures;
304415
}
305416

306-
public async Task<(SpValidationResult result, List<Guardrail> guardrails)> GetGuardrailAssociatedWithLineAsync(NetTopologySuite.Geometries.Geometry geometry, string recordNumber)
417+
private decimal GetAdjustedLengthKM(decimal startOffset, decimal endOffset, decimal beginKM, decimal endKM, decimal lengthKM)
307418
{
308-
var guardrails = await _inventoryApi.GetGuardrailAssociatedWithLine(geometry, recordNumber);
419+
//length needs to be adjusted to segment begin
420+
if (startOffset >= beginKM)
421+
{
422+
//don't let it go below zero
423+
lengthKM -= Math.Max(startOffset - beginKM, 0);
424+
}
309425

310-
return (SpValidationResult.Success, guardrails);
426+
//length needs to be adjusted to segment end
427+
if (endOffset <= endKM)
428+
{
429+
lengthKM -= Math.Max(endKM - endOffset, 0);
430+
}
431+
432+
return lengthKM;
311433
}
312434

313-
public async Task<(SpValidationResult result, Guardrail guardrail)> GetGuardrailAssociatedWithPointAsync(NetTopologySuite.Geometries.Geometry geometry, string recordNumber)
435+
public async Task<(SpValidationResult result, List<Structure> structures)> GetStructuresOnRFISegmentAsync(string rfiSegmentName, string recordNumber)
314436
{
315-
var guardrail = await _inventoryApi.GetGuardrailAssociatedWithPoint(geometry, recordNumber);
437+
var structures = await _inventoryApi.GetStructuresOnRFISegment(rfiSegmentName, recordNumber);
438+
316439

317-
return (SpValidationResult.Success, guardrail);
440+
return (SpValidationResult.Success, structures);
318441
}
319442

320443
public async Task<(SpValidationResult result, List<RestArea> restAreas)> GetRestAreasOnRFISegmentAsync(string rfiSegmentName, string recordNumber)

0 commit comments

Comments
 (0)