diff --git a/Geocoding.sln b/Geocoding.sln
index 6cbe577..29f7a5f 100644
--- a/Geocoding.sln
+++ b/Geocoding.sln
@@ -19,6 +19,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Geocoding.Tests", "test\Geo
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Geocoding.Here", "src\Geocoding.Here\Geocoding.Here.csproj", "{41F9E0D3-2094-4CE7-A2CD-F3CAF585A048}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Geocoding.UsCensusBureau", "src\Geocoding.UsCensusBureau\Geocoding.UsCensusBureau.csproj", "{45E82D63-BEC0-4DF2-AC0E-AEB2D235ED5B}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -53,6 +55,10 @@ Global
{41F9E0D3-2094-4CE7-A2CD-F3CAF585A048}.Debug|Any CPU.Build.0 = Debug|Any CPU
{41F9E0D3-2094-4CE7-A2CD-F3CAF585A048}.Release|Any CPU.ActiveCfg = Release|Any CPU
{41F9E0D3-2094-4CE7-A2CD-F3CAF585A048}.Release|Any CPU.Build.0 = Release|Any CPU
+ {45E82D63-BEC0-4DF2-AC0E-AEB2D235ED5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {45E82D63-BEC0-4DF2-AC0E-AEB2D235ED5B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {45E82D63-BEC0-4DF2-AC0E-AEB2D235ED5B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {45E82D63-BEC0-4DF2-AC0E-AEB2D235ED5B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -63,6 +69,7 @@ Global
{27F58640-D424-40F5-945E-42BF8BC872A0} = {F742864D-9400-4CE2-957A-DBD0A0237277}
{9773C7DA-DD1A-490D-B4D8-CEA18804AD1E} = {F742864D-9400-4CE2-957A-DBD0A0237277}
{41F9E0D3-2094-4CE7-A2CD-F3CAF585A048} = {F742864D-9400-4CE2-957A-DBD0A0237277}
+ {45E82D63-BEC0-4DF2-AC0E-AEB2D235ED5B} = {F742864D-9400-4CE2-957A-DBD0A0237277}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {085E97E6-56E0-4099-94E9-10F9080F2DD1}
diff --git a/README.md b/README.md
index 3c515f2..93796b1 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,7 @@ Includes a model and interface for communicating with five popular Geocoding pro
* :warning: MapQuest [(Commercial API)](http://www.mapquestapi.com/) - [docs](http://www.mapquestapi.com/geocoding/)
* :warning: MapQuest [(OpenStreetMap)](http://open.mapquestapi.com/) - [docs](http://open.mapquestapi.com/geocoding/)
* [HERE](https://www.here.com/) - [docs](https://developer.here.com/documentation)
+ * [U.S. Census Geocoder](https://geocoding.geo.census.gov/) - [docs](https://geocoding.geo.census.gov/geocoder/Geocoding_Services_API.pdf) (note: US Only & reverse geocoding not supported)
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.
diff --git a/src/Geocoding.UsCensusBureau/Geocoding.UsCensusBureau.csproj b/src/Geocoding.UsCensusBureau/Geocoding.UsCensusBureau.csproj
new file mode 100644
index 0000000..e37daeb
--- /dev/null
+++ b/src/Geocoding.UsCensusBureau/Geocoding.UsCensusBureau.csproj
@@ -0,0 +1,37 @@
+
+
+
+ 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.
+ Geocoding.net UsCensusBureau
+ 4.0.0-beta1
+ chadly
+ netstandard1.3;net46
+ Geocoding.UsCensusBureau
+ Geocoding.UsCensusBureau
+ geocoding;geocode;geocoder;maps;address;validation;normalization;google-maps;bing-maps;yahoo-placefinder;mapquest
+ https://github.com/chadly/Geocoding.net/releases/latest
+ https://github.com/chadly/Geocoding.net
+ https://github.com/chadly/Geocoding.net/blob/master/LICENSE
+ https://github.com/chadly/Geocoding.net.git
+ git
+ 4.0.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Geocoding.UsCensusBureau/UsCensusBureauAddress.cs b/src/Geocoding.UsCensusBureau/UsCensusBureauAddress.cs
new file mode 100644
index 0000000..2e90d79
--- /dev/null
+++ b/src/Geocoding.UsCensusBureau/UsCensusBureauAddress.cs
@@ -0,0 +1,11 @@
+namespace Geocoding.UsCensusBureau
+{
+ public class UsCensusBureauAddress : Address
+ {
+ public UsCensusBureauAddress(string formattedAddress, Location coordinates)
+ : base(formattedAddress, coordinates, UsCensusBureauConstants.Provider)
+ {
+
+ }
+ }
+}
diff --git a/src/Geocoding.UsCensusBureau/UsCensusBureauConstants.cs b/src/Geocoding.UsCensusBureau/UsCensusBureauConstants.cs
new file mode 100644
index 0000000..247fec5
--- /dev/null
+++ b/src/Geocoding.UsCensusBureau/UsCensusBureauConstants.cs
@@ -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";
+ }
+}
diff --git a/src/Geocoding.UsCensusBureau/UsCensusBureauGeocoder.cs b/src/Geocoding.UsCensusBureau/UsCensusBureauGeocoder.cs
new file mode 100644
index 0000000..84fec09
--- /dev/null
+++ b/src/Geocoding.UsCensusBureau/UsCensusBureauGeocoder.cs
@@ -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
+{
+ ///
+ /// refs:
+ /// - https://geocoding.geo.census.gov/
+ /// - https://geocoding.geo.census.gov/geocoder/Geocoding_Services_API.pdf
+ ///
+ public class UsCensusBureauGeocoder : IGeocoder
+ {
+ private readonly int _benchmark;
+ private readonly string _format;
+ private readonly HttpClient _client;
+
+ public UsCensusBureauGeocoder(int benchmark = 4, string format = "json")
+ {
+ _benchmark = benchmark;
+ _format = format;
+ _client = new HttpClient { BaseAddress = new Uri(UsCensusBureauConstants.BaseUrl) };
+ }
+
+ public async Task> 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> 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))
+ .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> ReverseGeocodeAsync(Location location, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ throw new NotSupportedException();
+ }
+
+ public Task> ReverseGeocodeAsync(double latitude, double longitude, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ throw new NotSupportedException();
+ }
+
+ private static IEnumerable GetAddresses(string response)
+ {
+ var json = JObject.Parse(response);
+
+ var errors = json[UsCensusBureauConstants.ErrorsKey];
+ if (errors != null)
+ return new UsCensusBureauAddress[] {};
+
+ 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();
+ }
+ }
+}
diff --git a/test/Geocoding.Tests/GeocoderTest.cs b/test/Geocoding.Tests/GeocoderTest.cs
index 82be0a9..fb75d6f 100644
--- a/test/Geocoding.Tests/GeocoderTest.cs
+++ b/test/Geocoding.Tests/GeocoderTest.cs
@@ -1,15 +1,12 @@
-using System.Globalization;
-using System.Linq;
-using System.Threading;
+using System.Linq;
using System.Threading.Tasks;
using Xunit;
-using Xunit.Extensions;
namespace Geocoding.Tests
{
public abstract class GeocoderTest
{
- readonly IGeocoder geocoder;
+ protected readonly IGeocoder geocoder;
protected readonly SettingsFixture settings;
public GeocoderTest(SettingsFixture settings)
@@ -106,4 +103,4 @@ public virtual async Task CanGeocodeInvalidZipCodes(string address)
Assert.NotEmpty(addresses);
}
}
-}
\ No newline at end of file
+}
diff --git a/test/Geocoding.Tests/Geocoding.Tests.csproj b/test/Geocoding.Tests/Geocoding.Tests.csproj
index df58082..41db7b3 100644
--- a/test/Geocoding.Tests/Geocoding.Tests.csproj
+++ b/test/Geocoding.Tests/Geocoding.Tests.csproj
@@ -23,6 +23,7 @@
+
diff --git a/test/Geocoding.Tests/UsCensusBureauTest.cs b/test/Geocoding.Tests/UsCensusBureauTest.cs
new file mode 100644
index 0000000..6e89b0c
--- /dev/null
+++ b/test/Geocoding.Tests/UsCensusBureauTest.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using Geocoding.UsCensusBureau;
+using Xunit;
+
+namespace Geocoding.Tests
+{
+ [Collection("Settings")]
+ public class UsCensusBureauTest : GeocoderTest
+ {
+ 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;
+ }
+ }
+}