public void Levenshtein_distance_breaks_score_and_weight_tie() { const int suggestCount = 2; const string address = "669 E 3rd ave"; var topCandidates = new TopAddressCandidates(suggestCount, new CandidateComparer(address.ToUpperInvariant())); var candidates = new List <Candidate> { new Candidate { Address = "669 W 3RD AVE", Score = 90.87, Weight = 1 }, new Candidate { Address = "669 E 3RD AVE", Score = 90.87, Weight = 1 }, new Candidate { Address = "670 W 3RD AVE", Score = 69.87, Weight = 1 }, new Candidate { Address = "670 E 3RD AVE", Score = 69.87, Weight = 1 } }; candidates.ForEach(topCandidates.Add); var items = topCandidates.Get(); const int addOneForWinnerWhichIsRemoved = 1; items.Count.ShouldBe(suggestCount + addOneForWinnerWhichIsRemoved); topCandidates.Get().First().Address.ShouldBe(address.ToUpperInvariant()); }
public void Greater_weight_breaks_score_tie() { const int topItemCount = 3; const string tieBreakerInput = "GOLD"; var topCandidates = new TopAddressCandidates(topItemCount, new CandidateComparer(tieBreakerInput)); topCandidates.Add(new Candidate { Address = "GOLD", Score = 5, Weight = 100 }); topCandidates.Add(new Candidate { Address = "GOLDS", Score = 5, Weight = 99 }); topCandidates.Add(new Candidate { Address = "BRONZE", Score = 5, Weight = 1 }); topCandidates.Add(new Candidate { Address = "SILVER", Score = 5, Weight = 50 }); topCandidates.Add(new Candidate { Address = "Runner up", Score = 5, Weight = 0 }); var items = topCandidates.Get(); var candidate = items.First(); const int addOneForWinnerWhichIsRemoved = 1; items.Count.ShouldBe(topItemCount + addOneForWinnerWhichIsRemoved); candidate.Score.ShouldBe(5); candidate.Address.ShouldBe("GOLD"); }
public void Size_is_one_larger_than_input_to_get_exact_suggestion_count() { const int suggestCount = 2; const string tieBreakerInput = ""; var topCandidates = new TopAddressCandidates(suggestCount, new CandidateComparer(tieBreakerInput)); topCandidates.Add(new Candidate { Address = "GOLD", Score = 5, Weight = 100 }); topCandidates.Add(new Candidate { Address = "GOLDS", Score = 5, Weight = 100 }); topCandidates.Add(new Candidate { Address = "BRONZE", Score = 5, Weight = 1 }); topCandidates.Add(new Candidate { Address = "SILVER", Score = 5, Weight = 50 }); topCandidates.Add(new Candidate { Address = "Runner up", Score = 5, Weight = 0 }); topCandidates.Get().ToList().Count.ShouldBe(suggestCount + 1); }
public void Size_is_two_when_suggest_is_zero_for_score_difference_calculating() { const int suggestCount = 0; const string tieBreakerInput = ""; var topCandidates = new TopAddressCandidates(suggestCount, new CandidateComparer(tieBreakerInput)); topCandidates.Add(new Candidate { Address = "GOLD", Score = 5, Weight = 100 }); topCandidates.Add(new Candidate { Address = "GOLDS", Score = 5, Weight = 100 }); topCandidates.Add(new Candidate { Address = "BRONZE", Score = 5, Weight = 1 }); topCandidates.Add(new Candidate { Address = "SILVER", Score = 5, Weight = 50 }); topCandidates.Add(new Candidate { Address = "Runner up", Score = 5, Weight = 0 }); topCandidates.Get().ToList().Count.ShouldBe(2); }
public async Task <ObjectResult> Get(string street, string zone, [FromQuery] GeocodingOptions options) { _log.Debug("Geocoding {street}, {zone} with options: {options}", street, zone, options); #region validation var errors = ""; if (string.IsNullOrEmpty(street)) { errors = "Street is empty."; } if (string.IsNullOrEmpty(zone)) { errors += "Zip code or city name is emtpy"; } if (errors.Length > 0) { _log.Debug("Bad geocode request", errors); return(BadRequest(new ApiResponseContainer <GeocodeAddressApiResponse> { Status = (int)HttpStatusCode.BadRequest, Message = errors })); } street = street?.Trim(); zone = zone?.Trim(); #endregion var parseAddressCommand = new AddressParsing.Command(street); var parsedStreet = await _mediator.Send(parseAddressCommand); var parseZoneCommand = new ZoneParsing.Command(zone, new GeocodeAddress(parsedStreet)); var parsedAddress = await _mediator.Send(parseZoneCommand); if (options.PoBox && parsedAddress.IsPoBox && parsedAddress.Zip5.HasValue) { var poboxCommand = new PoBoxLocation.Command(parsedAddress, options); var result = await _mediator.Send(poboxCommand); if (result != null) { var model = result.ToResponseObject(street, zone); var standard = parsedAddress.StandardizedAddress.ToLowerInvariant(); var input = street?.ToLowerInvariant(); if (input != standard) { model.StandardizedAddress = standard; } _log.Debug("Result score: {score} from {locator}", model.Score, model.Locator); return(Ok(new ApiResponseContainer <GeocodeAddressApiResponse> { Result = model, Status = (int)HttpStatusCode.OK })); } } var deliveryPointCommand = new UspsDeliveryPointLocation.Command(parsedAddress, options); var uspsPoint = await _mediator.Send(deliveryPointCommand); if (uspsPoint != null) { var model = uspsPoint.ToResponseObject(street, zone); var standard = parsedAddress.StandardizedAddress.ToLowerInvariant(); var input = street?.ToLowerInvariant(); if (input != standard) { model.StandardizedAddress = standard; } _log.Debug("Result score: {score} from {locator}", model.Score, model.Locator); return(Ok(new ApiResponseContainer <GeocodeAddressApiResponse> { Result = model, Status = (int)HttpStatusCode.OK })); } var topCandidates = new TopAddressCandidates(options.Suggest, new CandidateComparer(parsedAddress.StandardizedAddress .ToUpperInvariant())); var getLocatorsForAddressCommand = new LocatorsForGeocode.Command(parsedAddress, options); var locators = await _mediator.Send(getLocatorsForAddressCommand); if (locators == null || !locators.Any()) { _log.Debug("No locators found for address {parsedAddress}", parsedAddress); return(NotFound(new ApiResponseContainer { Message = $"No address candidates found with a score of {options.AcceptScore} or better.", Status = (int)HttpStatusCode.NotFound })); } var tasks = await Task.WhenAll(locators.Select(locator => _mediator.Send(new Geocode.Command(locator))) .ToArray()); var candidates = tasks.SelectMany(x => x); foreach (var candidate in candidates) { topCandidates.Add(candidate); } var highestScores = topCandidates.Get(); var chooseBestAddressCandidateCommand = new FilterCandidates.Command(highestScores, options, street, zone, parsedAddress); var winner = await _mediator.Send(chooseBestAddressCandidateCommand); if (winner == null || winner.Score < 0) { _log.Warning("Could not find match for {Street}, {Zone} with a score of {Score} or better.", street, zone, options.AcceptScore); return(NotFound(new ApiResponseContainer { Message = $"No address candidates found with a score of {options.AcceptScore} or better.", Status = (int)HttpStatusCode.NotFound })); } if (winner.Location == null) { _log.Warning("Could not find match for {Street}, {Zone} with a score of {Score} or better.", street, zone, options.AcceptScore); } winner.Wkid = options.SpatialReference; _log.Debug("Result score: {score} from {locator}", winner.Score, winner.Locator); return(Ok(new ApiResponseContainer <GeocodeAddressApiResponse> { Result = winner, Status = (int)HttpStatusCode.OK })); }
public async Task <ObjectResult> Get(string street, string zone, [FromQuery] GeocodingOptions options) { #region validation var errors = ""; if (string.IsNullOrEmpty(street)) { errors = "Street is empty."; } if (string.IsNullOrEmpty(zone)) { errors += "Zip code or city name is emtpy"; } if (errors.Length > 0) { return(BadRequest(new ApiResponseContainer <GeocodeAddressApiResponse> { Status = (int)HttpStatusCode.BadRequest, Message = errors })); } street = street?.Trim(); zone = zone?.Trim(); #endregion _parseAddressCommand.Initialize(street); var parsedStreet = CommandExecutor.ExecuteCommand(_parseAddressCommand); _parseZoneCommand.Initialize(zone, new GeocodeAddress(parsedStreet)); var parsedAddress = CommandExecutor.ExecuteCommand(_parseZoneCommand); if (options.PoBox && parsedAddress.IsPoBox && parsedAddress.Zip5.HasValue) { _poboxCommand.Initialize(parsedAddress, options); var result = await _poboxCommand.Execute(); if (result != null) { // TODO this is silly change it var model = new GeocodeAddressApiResponse { MatchAddress = result.Address, Score = result.Score, Locator = result.Locator, Location = result.Location, AddressGrid = result.AddressGrid, InputAddress = $"{street}, {zone}", ScoreDifference = result.ScoreDifference }; var standard = parsedAddress.StandardizedAddress.ToLowerInvariant(); var input = street?.ToLowerInvariant(); if (input != standard) { model.StandardizedAddress = standard; } return(Ok(new ApiResponseContainer <GeocodeAddressApiResponse> { Result = model })); } } _deliveryPointCommand.Initialize(parsedAddress, options); var uspsPoint = await _deliveryPointCommand.Execute(); if (uspsPoint != null) { // TODO this is silly change it var model = new GeocodeAddressApiResponse { MatchAddress = uspsPoint.Address, Score = uspsPoint.Score, Locator = uspsPoint.Locator, Location = uspsPoint.Location, AddressGrid = uspsPoint.AddressGrid, InputAddress = $"{street}, {zone}", ScoreDifference = uspsPoint.ScoreDifference }; var standard = parsedAddress.StandardizedAddress.ToLowerInvariant(); var input = street?.ToLowerInvariant(); if (input != standard) { model.StandardizedAddress = standard; } return(Ok(new ApiResponseContainer <GeocodeAddressApiResponse> { Result = model })); } var topCandidates = new TopAddressCandidates(options.Suggest, new CandidateComparer(parsedAddress.StandardizedAddress .ToUpperInvariant())); _getLocatorsForAddressCommand.Initialize(parsedAddress, options); var locators = CommandExecutor.ExecuteCommand(_getLocatorsForAddressCommand); if (locators == null || !locators.Any()) { return(NotFound(new ApiResponseContainer { Message = $"No address candidates found with a score of {options.AcceptScore} or better.", Status = (int)HttpStatusCode.NotFound })); } var commandsToExecute = new ConcurrentQueue <GetAddressCandidatesCommand>(); foreach (var locator in locators) { var geocodeWithLocator = new GetAddressCandidatesCommand(_clientFactory); geocodeWithLocator.Initialize(locator); commandsToExecute.Enqueue(geocodeWithLocator); } var tasks = new Collection <Task <IEnumerable <Candidate> > >(); while (commandsToExecute.TryDequeue(out GetAddressCandidatesCommand currentCommand)) { tasks.Add(currentCommand.Execute()); } await Task.WhenAll(tasks); var candidates = tasks.Where(x => x.Result != null).SelectMany(x => x.Result); foreach (var candidate in candidates) { topCandidates.Add(candidate); } var highestScores = topCandidates.Get(); var chooseBestAddressCandidateCommand = new ChooseBestAddressCandidateCommand(highestScores, options, street, zone, parsedAddress); var winner = CommandExecutor.ExecuteCommand(chooseBestAddressCandidateCommand); if (winner == null || winner.Score < 0) { // Log.Warning("Could not find match for {Street}, {Zone} with a score of {Score} or better.", street, zone, // options.AcceptScore); return(NotFound(new ApiResponseContainer { Message = $"No address candidates found with a score of {options.AcceptScore} or better.", Status = (int)HttpStatusCode.NotFound })); } if (winner.Location == null) { // Log.Warning("Could not find match for {Street}, {Zone} with a score of {Score} or better.", street, zone, // options.AcceptScore); } winner.Wkid = options.SpatialReference; return(Ok(new ApiResponseContainer <GeocodeAddressApiResponse> { Result = winner })); }