public async Task Should_remove_candidates_below_accept_score() { var candidates = new[] { new Candidate { Address = "winner", Score = 1, AddressGrid = "grid", Location = new Point(0, 0) }, new Candidate { Address = "remove", Score = 0, AddressGrid = "grid", Location = new Point(1, 1) } }; var options = new GeocodingOptions { AcceptScore = 1, Suggest = 1 }; var address = new GeocodeAddress(new CleansedAddress()) { AddressGrids = new[] { new ZipGridLink(0, "grid", 0) } }; var request = new FilterCandidates.Command(candidates, options, "street", "zone", address); var result = await Handler.Handle(request, CancellationToken.None); result.Candidates.ShouldBeEmpty(); result.MatchAddress.ShouldBe("winner"); }
public async Task Should_return_candidate_from_poboxes() { const int pobox = -1; const int zip = 84114; var parsedAddress = new CleansedAddress("inputAddress", 1, 0, pobox, Direction.North, "street", StreetType.Alley, Direction.South, 0, zip, false, false); var address = new GeocodeAddress(parsedAddress) { AddressGrids = new[] { new ZipGridLink(84114, "grid", 0) } }; var geocodeOptions = new GeocodingOptions { PoBox = true, SpatialReference = 26912 }; var request = new PoBoxLocation.Command(address, geocodeOptions); var result = await _handler.Handle(request, new CancellationToken()); result.Score.ShouldBe(100); result.Locator.ShouldBe("Post Office Point"); result.Location.X.ShouldBe(1); result.Location.Y.ShouldBe(1); result.AddressGrid.ShouldBe("grid"); }
public async Task Should_create_extra_for_address_reversal() { var parsedAddress = new CleansedAddress("inputAddress", 1, 0, 0, Direction.North, "2", StreetType.Alley, Direction.South, 0, 84114, false, false); var address = new GeocodeAddress(parsedAddress) { AddressGrids = new[] { new PlaceGridLink("place", "grid", 0) } }; var geocodeOptions = new GeocodingOptions { Locators = LocatorType.RoadCenterlines, SpatialReference = 26912 }; var request = new LocatorsForGeocode.Command(address, geocodeOptions); var result = await Handler.Handle(request, new CancellationToken()); result.Count.ShouldBe(2); result.Count(x => x.Url == "proto://test:1/arcgis/rest/services/Geolocators/Roads_AddressSystem_STREET/GeocodeServer/findAddressCandidates?f=json&Street=1+North+2+Alley+South&City=grid&outSR=26912") .ShouldBe(1); result.Count(x => x.Url == "proto://test:1/arcgis/rest/services/Geolocators/Roads_AddressSystem_STREET/GeocodeServer/findAddressCandidates?f=json&Street=2+South+Alley+1+North&City=grid&outSR=26912") .ShouldBe(1); }
public async Task Should_calculate_score_difference() { var candidates = new[] { new Candidate { Address = "winner", Score = 10, AddressGrid = "grid", Location = new Point(0, 0) }, new Candidate { Address = "suggest", Score = 1, AddressGrid = "grid", Location = new Point(1, 1) } }; var options = new GeocodingOptions { AcceptScore = 1, ScoreDifference = true }; var address = new GeocodeAddress(new CleansedAddress()) { AddressGrids = new[] { new ZipGridLink(0, "grid", 0) } }; var request = new FilterCandidates.Command(candidates, options, "street", "zone", address); var result = await Handler.Handle(request, CancellationToken.None); result.Candidates.ShouldBeEmpty(); result.ScoreDifference.ShouldBe(9); }
public async Task Should_return_all_geocoders() { var parsedAddress = new CleansedAddress("inputAddress", 1, 0, 0, Direction.North, "street", StreetType.Alley, Direction.South, 0, 84114, false, false); var address = new GeocodeAddress(parsedAddress) { AddressGrids = new[] { new PlaceGridLink("place", "grid", 0) } }; var geocodeOptions = new GeocodingOptions { Locators = LocatorType.All, SpatialReference = 26912 }; var request = new LocatorsForGeocode.Command(address, geocodeOptions); var result = await Handler.Handle(request, new CancellationToken()); result.Count.ShouldBe(2); geocodeOptions = new GeocodingOptions { Locators = LocatorType.Default, SpatialReference = 26912 }; request = new LocatorsForGeocode.Command(address, geocodeOptions); result = await Handler.Handle(request, new CancellationToken()); result.Count.ShouldBe(2); }
public void Initialize(GeocodeAddress address, GeocodingOptions options) { Address = address; Options = options; BuildAddressPermutations(); BuildLocatorLookup(); }
public async Task Should_return_null_for_address_without_zip_code() { var parsedAddress = new CleansedAddress("inputAddress", 1, 0, 0, Direction.North, "street", StreetType.Alley, Direction.South, 0, new int?(), false, false); var address = new GeocodeAddress(parsedAddress); var geocodeOptions = new GeocodingOptions { PoBox = true }; var request = new PoBoxLocation.Command(address, geocodeOptions); var result = await _handler.Handle(request, new CancellationToken()); result.ShouldBeNull(); }
public async Task Should_return_empty_when_no_grids() { var parsedAddress = new CleansedAddress("inputAddress", 1, 0, 0, Direction.North, "street", StreetType.Alley, Direction.South, 0, 84114, false, false); var address = new GeocodeAddress(parsedAddress) { AddressGrids = Array.Empty <GridLinkable>() }; var geocodeOptions = new GeocodingOptions { Locators = LocatorType.RoadCenterlines, SpatialReference = 26912 }; var request = new LocatorsForGeocode.Command(address, geocodeOptions); var result = await Handler.Handle(request, new CancellationToken()); result.ShouldBeEmpty(); }
public Command(IList <Candidate> candidates, GeocodingOptions geocodeOptions, string street, string zone, GeocodeAddress geocodedAddress) { GeocodeOptions = geocodeOptions; Street = street; Zone = zone; GeocodedAddress = geocodedAddress; if (candidates == null) { candidates = Array.Empty <Candidate>(); } foreach (var candidate in candidates) { candidate.ScoreDifference = -1; } Candidates = candidates; }
public ChooseBestAddressCandidateCommand(IEnumerable <Candidate> candidates, GeocodingOptions geocodeOptions, string street, string zone, GeocodeAddress geocodedAddress) { GeocodeOptions = geocodeOptions; Street = street; Zone = zone; GeocodedAddress = geocodedAddress; if (candidates == null) { candidates = new List <Candidate>(); } var enumerable = candidates as IList <Candidate> ?? candidates.ToList(); foreach (var candidate in enumerable) { candidate.ScoreDifference = -1; } Candidates = enumerable.ToList(); }
public async Task Should_return_centerline_geocoder_only() { var parsedAddress = new CleansedAddress("inputAddress", 1, 0, 0, Direction.North, "street", StreetType.Alley, Direction.South, 0, 84114, false, false); var address = new GeocodeAddress(parsedAddress) { AddressGrids = new[] { new PlaceGridLink("place", "grid", 0) } }; var geocodeOptions = new GeocodingOptions { Locators = LocatorType.RoadCenterlines, SpatialReference = 26912 }; var request = new LocatorsForGeocode.Command(address, geocodeOptions); var result = await Handler.Handle(request, new CancellationToken()); result.ShouldHaveSingleItem(); var locator = result.First(); locator.Url.ShouldBe("proto://test:1/arcgis/rest/services/Geolocators/Roads_AddressSystem_STREET/GeocodeServer/findAddressCandidates?f=json&Street=1+North+street+Alley+South&City=grid&outSR=26912"); locator.Name.ShouldBe("Centerlines.StatewideRoads"); }
public void Initialize(GeocodeAddress geocodeAddress, GeocodingOptions options) { _options = options; _geocodedAddress = geocodeAddress; }
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 })); }
public Command(GeocodeAddress address, GeocodingOptions options) { Address = address; Options = options; }