using System.Diagnostics; using Microsoft.Extensions.Options; using NetTopologySuite.Features; using NetTopologySuite.Geometries; using NetTopologySuite.IO; using SzakatsA.Result; namespace ProofOfConcept.Services; public class ZoneDeterminatorService { private readonly ILogger logger; private ZoneDeterminatorServiceConfiguration configuration; private FeatureCollection parkingZones; private bool initialized; public ZoneDeterminatorService(ILogger logger, IOptions options) { this.logger = logger; this.configuration = options.Value; this.parkingZones = new FeatureCollection(); this.initialized = false; } public async Task> DetermineZoneCodeAsync(double latitude, double longitude, CancellationToken cancellationToken = default(CancellationToken)) { this.logger.LogTrace("Determinating parking zone code for coordinates: {Latitude}, {Longitude}...", latitude, longitude); if (!this.initialized) await InitializeAsync(cancellationToken); Point point = new Point(longitude, latitude); IFeature? zone = this.parkingZones.FirstOrDefault(f => f.Geometry.Contains(point)); if (zone is null) return Result.Success(String.Empty); else if (!zone.Attributes.Exists("zoneid")) return Result.Fail(new MissingFieldException("Zone ID not found for parking zone")); else if (zone.Attributes["zoneid"] is null) return Result.Fail(new NullReferenceException("Zone ID null for parking zone")); else if (zone.Attributes["zoneid"].ToString() is null) return Result.Fail(new InvalidCastException($"Zone ID is of type {zone.Attributes["zoneID"].GetType().FullName}")); else return Result.Success(zone.Attributes["zoneid"].ToString()!); } public async Task InitializeAsync(CancellationToken cancellationToken = default(CancellationToken)) { this.logger.LogTrace("Initializing..."); Stopwatch sw = Stopwatch.StartNew(); this.logger.LogTrace("Reading file..."); string geojson = await File.ReadAllTextAsync(this.configuration.ZoneFilePath, cancellationToken); this.logger.LogTrace("File read in {Elapsed} ms", sw.ElapsedMilliseconds); this.logger.LogTrace("Parsing geojson..."); GeoJsonReader reader = new GeoJsonReader(); this.parkingZones = reader.Read(geojson); this.logger.LogTrace("Geojson parsed in {Elapsed} ms: {FeatureCount} features", sw.ElapsedMilliseconds, this.parkingZones.Count); this.initialized = true; this.logger.LogInformation("Initialized"); } } public class ZoneDeterminatorServiceConfiguration { public string ZoneFilePath { get; set; } = "Resources/parking_zones.geojson"; }