-
Notifications
You must be signed in to change notification settings - Fork 156
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feature Us Census Bureau #118
base: master
Are you sure you want to change the base?
Changes from all commits
b8fa2fe
e4609f7
d57aba3
5f04403
f2e7521
9a88c88
e4be78d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<Description>Includes a model and interface for communicating with four popular Geocoding providers. Current implementations include: Google Maps, Yahoo! PlaceFinder, Bing Maps (aka Virtual Earth), and Mapquest. The API returns latitude/longitude coordinates and normalized address information. This can be used to perform address validation, real time mapping of user-entered addresses, distance calculations, and much more.</Description> | ||
<AssemblyTitle>Geocoding.net UsCensusBureau</AssemblyTitle> | ||
<VersionPrefix>4.0.0-beta1</VersionPrefix> | ||
<Authors>chadly</Authors> | ||
<TargetFrameworks>netstandard1.3;net46</TargetFrameworks> | ||
<AssemblyName>Geocoding.UsCensusBureau</AssemblyName> | ||
<PackageId>Geocoding.UsCensusBureau</PackageId> | ||
<PackageTags>geocoding;geocode;geocoder;maps;address;validation;normalization;google-maps;bing-maps;yahoo-placefinder;mapquest</PackageTags> | ||
<PackageReleaseNotes>https://github.com/chadly/Geocoding.net/releases/latest</PackageReleaseNotes> | ||
<PackageProjectUrl>https://github.com/chadly/Geocoding.net</PackageProjectUrl> | ||
<PackageLicenseUrl>https://github.com/chadly/Geocoding.net/blob/master/LICENSE</PackageLicenseUrl> | ||
<RepositoryUrl>https://github.com/chadly/Geocoding.net.git</RepositoryUrl> | ||
<RepositoryType>git</RepositoryType> | ||
<Version>4.0.0</Version> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\Geocoding.Core\Geocoding.Core.csproj" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup Condition="'$(TargetFramework)'=='netstandard1.3'"> | ||
<PackageReference Include="System.Net.Http" Version="4.3.1" /> | ||
<PackageReference Include="System.Runtime.Serialization.Json" Version="4.3.0" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup Condition="'$(TargetFramework)'=='net46'"> | ||
<Reference Include="System.Net.Http" /> | ||
</ItemGroup> | ||
|
||
</Project> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
namespace Geocoding.UsCensusBureau | ||
{ | ||
public class UsCensusBureauAddress : Address | ||
{ | ||
public UsCensusBureauAddress(string formattedAddress, Location coordinates) | ||
: base(formattedAddress, coordinates, UsCensusBureauConstants.Provider) | ||
{ | ||
|
||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
namespace Geocoding.UsCensusBureau | ||
{ | ||
public class UsCensusBureauConstants | ||
{ | ||
public const string Provider = "UsCensusBureau"; | ||
|
||
public const string BaseUrl = "https://geocoding.geo.census.gov/geocoder/"; | ||
public const string OneLineAddressPath = "locations/onelineaddress?"; | ||
public const string AddressPath = "locations/address?"; | ||
|
||
public const string AddressMatchesKey = "addressMatches"; | ||
public const string MatchedAddressKey = "matchedAddress"; | ||
public const string CoordinatesKey = "coordinates"; | ||
public const string ResultKey = "result"; | ||
public const string ErrorsKey = "errors"; | ||
public const string XKey = "x"; | ||
public const string YKey = "y"; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Net; | ||
using System.Net.Http; | ||
using System.Text; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Newtonsoft.Json.Linq; | ||
|
||
namespace Geocoding.UsCensusBureau | ||
{ | ||
/// <summary> | ||
/// refs: | ||
/// - https://geocoding.geo.census.gov/ | ||
/// - https://geocoding.geo.census.gov/geocoder/Geocoding_Services_API.pdf | ||
/// </summary> | ||
public class UsCensusBureauGeocoder : IGeocoder | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider adding an IBatchGeocoder, as this looks to be supported by the API. |
||
{ | ||
private readonly int _benchmark; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. API defines benchmark as a string, and the example use, for example, "Public_AR_Census2020". Should this be a string? It might be worth referencing https://geocoding.geo.census.gov/geocoder/benchmarks for the list of available benchmarks. |
||
private readonly string _format; | ||
private readonly HttpClient _client; | ||
|
||
public UsCensusBureauGeocoder(int benchmark = 4, string format = "json") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the magic number "4" (benchmark)? Can this be made into a (string) constant? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove the format parameter - the code only supports JSON, so hardcode it. |
||
{ | ||
_benchmark = benchmark; | ||
_format = format; | ||
_client = new HttpClient { BaseAddress = new Uri(UsCensusBureauConstants.BaseUrl) }; | ||
} | ||
|
||
public async Task<IEnumerable<Address>> GeocodeAsync(string address, CancellationToken cancellationToken = default(CancellationToken)) | ||
{ | ||
// Build Query String | ||
var sb = new StringBuilder(UsCensusBureauConstants.OneLineAddressPath); | ||
sb.Append("address=").Append(WebUtility.UrlEncode(address)) | ||
.Append("&benchmark=").Append(_benchmark) | ||
.Append("&format=").Append(_format); | ||
|
||
// Get Request | ||
var response = await _client.GetAsync(sb.ToString(), cancellationToken); | ||
var content = await response.Content.ReadAsStringAsync(); | ||
|
||
// Read Result | ||
return GetAddresses(content); | ||
} | ||
|
||
public async Task<IEnumerable<Address>> GeocodeAsync(string street, string city, string state, string postalCode, string country, CancellationToken cancellationToken = default(CancellationToken)) | ||
{ | ||
// Build Query String | ||
var sb = new StringBuilder(UsCensusBureauConstants.AddressPath); | ||
sb.Append("street=").Append(WebUtility.UrlEncode(street)) | ||
.Append("&city=").Append(WebUtility.UrlEncode(city)) | ||
.Append("&state=").Append(WebUtility.UrlEncode(state)) | ||
.Append("&zip=").Append(WebUtility.UrlEncode(postalCode)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this work if any parameters are omitted? Perhaps add a test for partial address. |
||
.Append("&benchmark=").Append(_benchmark) | ||
.Append("&format=").Append(_format); | ||
|
||
// Get Request | ||
var response = await _client.GetAsync(sb.ToString(), cancellationToken); | ||
var content = await response.Content.ReadAsStringAsync(); | ||
|
||
// Read Result | ||
return GetAddresses(content); | ||
} | ||
|
||
public Task<IEnumerable<Address>> ReverseGeocodeAsync(Location location, CancellationToken cancellationToken = default(CancellationToken)) | ||
{ | ||
throw new NotSupportedException(); | ||
} | ||
|
||
public Task<IEnumerable<Address>> ReverseGeocodeAsync(double latitude, double longitude, CancellationToken cancellationToken = default(CancellationToken)) | ||
{ | ||
throw new NotSupportedException(); | ||
} | ||
|
||
private static IEnumerable<UsCensusBureauAddress> GetAddresses(string response) | ||
{ | ||
var json = JObject.Parse(response); | ||
|
||
var errors = json[UsCensusBureauConstants.ErrorsKey]; | ||
if (errors != null) | ||
return new UsCensusBureauAddress[] {}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't swallow errors. Create and throw a UsCensusBureauGeocodingException (wow, that's a long name - how about Ucb?) to wrap and expose the error. |
||
|
||
var result = json[UsCensusBureauConstants.ResultKey]; | ||
return result[UsCensusBureauConstants.AddressMatchesKey] | ||
.Select(match => | ||
{ | ||
var matched = match[UsCensusBureauConstants.MatchedAddressKey].ToString(); | ||
var coordinates = match[UsCensusBureauConstants.CoordinatesKey]; | ||
var x = double.Parse(coordinates[UsCensusBureauConstants.XKey].ToString()); | ||
var y = double.Parse(coordinates[UsCensusBureauConstants.YKey].ToString()); | ||
|
||
return new UsCensusBureauAddress(matched, new Location(y, x)); | ||
}) | ||
.ToArray(); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
using System; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using Geocoding.UsCensusBureau; | ||
using Xunit; | ||
|
||
namespace Geocoding.Tests | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't skip tests. Assert that they behave as expected (such as throwing NotSupportedException, or returning/throwing an appropriate error). |
||
{ | ||
[Collection("Settings")] | ||
public class UsCensusBureauTest : GeocoderTest | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add tests for other "benchmark" values. |
||
{ | ||
public UsCensusBureauTest(SettingsFixture settings) : base(settings) | ||
{ | ||
} | ||
|
||
protected override IGeocoder CreateGeocoder() | ||
{ | ||
return new UsCensusBureauGeocoder(); | ||
} | ||
|
||
[Theory(Skip = "not supported - us addresses only")] | ||
public override Task CanGeocodeAddressUnderDifferentCultures(string cultureName) | ||
{ | ||
return Task.CompletedTask; | ||
} | ||
|
||
[Theory(Skip = "not supported - reverse geocode")] | ||
public override Task CanReverseGeocodeAddressUnderDifferentCultures(string cultureName) | ||
{ | ||
return Task.CompletedTask; | ||
} | ||
|
||
[Theory(Skip = "using different input with CanGeocodeWithSpecialCharacters2")] | ||
public override Task CanGeocodeWithSpecialCharacters(string address) | ||
{ | ||
return Task.CompletedTask; | ||
} | ||
|
||
[Theory] | ||
[InlineData("12110 CLAYTON ROAD TOWN & COUNTRY,SAINT LOUIS,MO,63131,US")] | ||
public async Task CanGeocodeWithSpecialCharacters2(string address) | ||
{ | ||
Address[] addresses = (await geocoder.GeocodeAsync(address)).ToArray(); | ||
|
||
//asserting no exceptions are thrown and that we get something | ||
Assert.NotEmpty(addresses); | ||
} | ||
|
||
[Theory(Skip = "not supported - exact addresses only")] | ||
public override Task CanHandleStreetIntersectionsByAmpersand(string address) | ||
{ | ||
return Task.CompletedTask; | ||
} | ||
|
||
[Fact(Skip = "not supported - reverse geocode")] | ||
public override Task CanReverseGeocodeAsync() | ||
{ | ||
return Task.CompletedTask; | ||
} | ||
|
||
[Theory(Skip = "not supported")] | ||
public override Task CanGeocodeInvalidZipCodes(string address) | ||
{ | ||
return Task.CompletedTask; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Formatting in this file could be improved - Visual Studio autoformat should be sufficient.