diff --git a/.gitignore b/.gitignore index 094871b..f2e383b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ /examples/packages AssemblyInfoCommon.cs app.config +app.config.temp docs nuget.exe AssemblyVersion.cs @@ -103,4 +104,5 @@ Thumbs.db # Folder config file Desktop.ini -.directory \ No newline at end of file +.directory +*.userprefs diff --git a/README.md b/README.md index a122ae1..8539c92 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,31 @@ #Generic C# Geocoding API -Includes a model and interface for communicating with three popular Geocoding providers. Current implementations include: +Includes a model and interface for communicating with three popular Geocoding providers. Original implementations forked from (https://github.com/chadly/Geocoding.net) includes: * [Google Maps](https://developers.google.com/maps/) - [docs](https://developers.google.com/maps/documentation/geocoding/) * [Yahoo! BOSS Geo Services](http://developer.yahoo.com/boss/geo/) - [docs](http://developer.yahoo.com/geo/placefinder/guide/index.html) * [Bing Maps (aka Virtual Earth)](http://www.microsoft.com/maps/) - [docs](http://msdn.microsoft.com/en-us/library/ff701715.aspx) - + +This fork adds: + + * MapQuest [(Comercial API)](http://www.mapquestapi.com/) - [docs](http://www.mapquestapi.com/geocoding/) + * MapQuest [(OpenStreetMap)](http://open.mapquestapi.com/) - [docs](http://open.mapquestapi.com/geocoding/) + * Mono compatibility + 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. See latest [release notes](https://github.com/chadly/Geocoding.net/wiki/Release-Notes). ##Installation -Install via nuget: - -``` -Install-Package Geocoding.net -``` - -Or download the [latest release](https://github.com/chadly/Geocoding.net/releases) and add a reference to `Geocoding.dll` in your project. +Pull from appropriate branch (Master if stability is required) and build it yourself. ##Example Usage ###Simple Example ```csharp -IGeocoder geocoder = new GoogleGeocoder() { ApiKey = "this-is-my-optional-google-api-key" }; +IGeocoder geocoder = new MapQuestGeocoder("this-is-my-required-mapquest-api-key"); Address[] addresses = geocoder.Geocode("C"); Console.WriteLine("Formatted: " + addresses[0].FormattedAddress); //Formatted: 1600 Pennslyvania Avenue Northwest, Presiden'ts Park, Washington, DC 20500, USA Console.WriteLine("Coordinates: " + addresses[0].Coordinates.Latitude + ", " + addresses[0].Coordinates.Longitude); //Coordinates: 38.8978378, -77.0365123 @@ -62,6 +62,7 @@ Bing [requires an API key](http://msdn.microsoft.com/en-us/library/ff428642.aspx You will need a [consumer secret and consumer key](http://developer.yahoo.com/boss/geo/BOSS_Signup.pdf) (PDF) for Yahoo. +MapQuest API requires a key. Sign up here: (http://developer.mapquest.com/web/products/open) ##How to Build from Source diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..23053de --- /dev/null +++ b/src/.gitignore @@ -0,0 +1 @@ +packages/ diff --git a/src/.nuget/NuGet.Config b/src/.nuget/NuGet.Config new file mode 100644 index 0000000..67f8ea0 --- /dev/null +++ b/src/.nuget/NuGet.Config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/.nuget/NuGet.targets b/src/.nuget/NuGet.targets new file mode 100644 index 0000000..3f8c37b --- /dev/null +++ b/src/.nuget/NuGet.targets @@ -0,0 +1,144 @@ + + + + $(MSBuildProjectDirectory)\..\ + + + false + + + false + + + true + + + false + + + + + + + + + + + $([System.IO.Path]::Combine($(SolutionDir), ".nuget")) + + + + + $(SolutionDir).nuget + + + + $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName.Replace(' ', '_')).config + $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName).config + + + + $(MSBuildProjectDirectory)\packages.config + $(PackagesProjectConfig) + + + + + $(NuGetToolsPath)\NuGet.exe + @(PackageSource) + + "$(NuGetExePath)" + mono --runtime=v4.0.30319 "$(NuGetExePath)" + + $(TargetDir.Trim('\\')) + + -RequireConsent + -NonInteractive + + "$(SolutionDir) " + "$(SolutionDir)" + + + $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir) + $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols + + + + RestorePackages; + $(BuildDependsOn); + + + + + $(BuildDependsOn); + BuildPackage; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Core/Address.cs b/src/Core/Address.cs index 2df24cf..eada676 100644 --- a/src/Core/Address.cs +++ b/src/Core/Address.cs @@ -3,43 +3,57 @@ namespace Geocoding { + /// + /// Most basic and generic form of address. + /// Just the full address string and a lat/long + /// public abstract class Address { - readonly string formattedAddress; - readonly Location coordinates; - readonly string provider; + string formattedAddress = string.Empty; + Location coordinates; + string provider = string.Empty; - public string FormattedAddress + public Address(string formattedAddress, Location coordinates, string provider) { - get { return formattedAddress ?? ""; } + FormattedAddress = formattedAddress; + Coordinates = coordinates; + Provider = provider; } - public Location Coordinates + public virtual string FormattedAddress { - get { return coordinates; } - } + get { return formattedAddress; } + set + { + if (string.IsNullOrWhiteSpace(value)) + throw new ArgumentException("FormattedAddress is null or blank"); - public string Provider - { - get { return provider ?? ""; } + formattedAddress = value.Trim(); + } } - public Address(string formattedAddress, Location coordinates, string provider) + public virtual Location Coordinates { - formattedAddress = (formattedAddress ?? "").Trim(); - - if (String.IsNullOrEmpty(formattedAddress)) - throw new ArgumentNullException("formattedAddress"); + get { return coordinates; } + set + { + if (value == null) + throw new ArgumentNullException("Coordinates"); - if (coordinates == null) - throw new ArgumentNullException("coordinates"); + coordinates = value; + } + } - if (provider == null) - throw new ArgumentNullException("provider"); + public virtual string Provider + { + get { return provider; } + protected set + { + if (string.IsNullOrWhiteSpace(value)) + throw new ArgumentException("Provider can not be null or blank"); - this.formattedAddress = formattedAddress; - this.coordinates = coordinates; - this.provider = provider; + provider = value; + } } public virtual Distance DistanceBetween(Address address) diff --git a/src/Core/Bounds.cs b/src/Core/Bounds.cs index b0f7a6a..2ecbec8 100644 --- a/src/Core/Bounds.cs +++ b/src/Core/Bounds.cs @@ -55,7 +55,7 @@ public override int GetHashCode() public override string ToString() { - return String.Format("{0} | {1}", southWest, northEast); + return string.Format("{0} | {1}", southWest, northEast); } } } diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj index 5b8c4b6..ce5a266 100644 --- a/src/Core/Core.csproj +++ b/src/Core/Core.csproj @@ -35,6 +35,8 @@ false true + ..\ + true true @@ -56,25 +58,28 @@ true + + ..\packages\Newtonsoft.Json.6.0.3\lib\net40\Newtonsoft.Json.dll + - 3.5 + 4.0 - - Properties\AssemblyInfoCommon.cs - + + + - + @@ -84,6 +89,7 @@ Properties\geocoding.snk + @@ -105,4 +111,11 @@ + + + + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + \ No newline at end of file diff --git a/src/Core/Distance.cs b/src/Core/Distance.cs index 037d732..2ae2b0f 100644 --- a/src/Core/Distance.cs +++ b/src/Core/Distance.cs @@ -100,7 +100,7 @@ public override int GetHashCode() public override string ToString() { - return String.Format("{0} {1}", value, units); + return string.Format("{0} {1}", value, units); } #region Operators diff --git a/src/Core/Extensions.cs b/src/Core/Extensions.cs new file mode 100644 index 0000000..e07b9bd --- /dev/null +++ b/src/Core/Extensions.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Geocoding +{ + public static class Extensions + { + public static bool IsNullOrEmpty(this ICollection col) + { + return col == null || col.Count == 0; + } + + public static void ForEach(this IEnumerable self, Action actor) + { + if(actor == null) + throw new ArgumentNullException("actor"); + + if (self == null) + return; + + foreach (T item in self) + { + actor(item); + } + } + + //Universal ISO DT Converter + static readonly JsonConverter[] JSON_CONVERTERS = new JsonConverter[] + { + new IsoDateTimeConverter { DateTimeStyles = System.Globalization.DateTimeStyles.AssumeUniversal }, + new StringEnumConverter(), + }; + public static string ToJSON(this object o) + { + string result = null; + if (o != null) + result = JsonConvert.SerializeObject(o, Formatting.Indented, JSON_CONVERTERS); + return result ?? string.Empty; + } + + public static T FromJSON(this string json) + { + T o = default(T); + if (!string.IsNullOrWhiteSpace(json)) + o = JsonConvert.DeserializeObject(json, JSON_CONVERTERS); + return o; + } + } +} diff --git a/src/Core/IBatchGeocoder.cs b/src/Core/IBatchGeocoder.cs new file mode 100644 index 0000000..add12ed --- /dev/null +++ b/src/Core/IBatchGeocoder.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Geocoding +{ + public interface IBatchGeocoder + { + ICollection Geocode(IEnumerable addresses); + ICollection ReverseGeocode(IEnumerable locations); + } +} diff --git a/src/Core/Location.cs b/src/Core/Location.cs index e497b4b..9f107fa 100644 --- a/src/Core/Location.cs +++ b/src/Core/Location.cs @@ -1,51 +1,66 @@ -using System; +using System; +using System.Linq; +using System.Collections.Generic; +using Newtonsoft.Json; namespace Geocoding { public class Location { - readonly double latitude; - readonly double longitude; + double latitude; + double longitude; - public double Latitude + [JsonProperty("lat")] + public virtual double Latitude { get { return latitude; } + set + { + if (value < -90 || value > 90) + throw new ArgumentOutOfRangeException("Latitude", value, "Value must be between -90(inclusive) and 90(inclusive)."); + if (double.IsNaN(value)) + throw new ArgumentException("Latitude must be a valid number.", "Latitude"); + + latitude = value; + } } - public double Longitude + [JsonProperty("lng")] + public virtual double Longitude { get { return longitude; } + set + { + if (value <= -180 || value > 180) + throw new ArgumentOutOfRangeException("Longitude", value, "Value must be between -180 and 180 (inclusive)."); + if (double.IsNaN(value)) + throw new ArgumentException("Longitude must be a valid number.", "Longitude"); + + longitude = value; + } } + protected Location() + : this(0, 0) + { + } public Location(double latitude, double longitude) { - if (longitude <= -180 || longitude > 180) - throw new ArgumentOutOfRangeException("longitude", longitude, "Value must be between -180 and 180 (inclusive)."); - - if (latitude < -90 || latitude > 90) - throw new ArgumentOutOfRangeException("latitude", latitude, "Value must be between -90(inclusive) and 90(inclusive)."); - - if (double.IsNaN(longitude)) - throw new ArgumentException("Longitude must be a valid number.", "longitude"); - - if (double.IsNaN(latitude)) - throw new ArgumentException("Latitude must be a valid number.", "latitude"); - - this.latitude = latitude; - this.longitude = longitude; + Latitude = latitude; + Longitude = longitude; } - private double ToRadian(double val) + protected virtual double ToRadian(double val) { return (Math.PI / 180.0) * val; } - public Distance DistanceBetween(Location location) + public virtual Distance DistanceBetween(Location location) { return DistanceBetween(location, DistanceUnits.Miles); } - public Distance DistanceBetween(Location location, DistanceUnits units) + public virtual Distance DistanceBetween(Location location, DistanceUnits units) { double earthRadius = (units == DistanceUnits.Miles) ? Distance.EarthRadiusInMiles : Distance.EarthRadiusInKilometers; @@ -83,7 +98,7 @@ public override int GetHashCode() public override string ToString() { - return String.Format("{0}, {1}", latitude, longitude); + return string.Format("{0}, {1}", latitude, longitude); } } } \ No newline at end of file diff --git a/src/Core/ParsedAddress.cs b/src/Core/ParsedAddress.cs new file mode 100644 index 0000000..88636b1 --- /dev/null +++ b/src/Core/ParsedAddress.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Geocoding +{ + /// + /// Generic parsed address with each field separated out form the original FormattedAddress + /// + public class ParsedAddress : Address + { + public ParsedAddress(string formattedAddress, Location coordinates, string provider) + : base(formattedAddress, coordinates, provider) + { + } + + public virtual string Street { get; set; } + public virtual string City { get; set; } + public virtual string County { get; set; } + public virtual string State { get; set; } + public virtual string Country { get; set; } + public virtual string PostCode { get; set; } + } +} diff --git a/src/Core/Properties/AssemblyInfo.cs b/src/Core/Properties/AssemblyInfo.cs index eafd902..3115750 100644 --- a/src/Core/Properties/AssemblyInfo.cs +++ b/src/Core/Properties/AssemblyInfo.cs @@ -1,3 +1,3 @@ -using System.Reflection; +using System.Reflection; [assembly: AssemblyTitle("Geocoding Core")] \ No newline at end of file diff --git a/src/Core/ResultItem.cs b/src/Core/ResultItem.cs new file mode 100644 index 0000000..053aacc --- /dev/null +++ b/src/Core/ResultItem.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Geocoding +{ + public class ResultItem + { + public ResultItem(Address request, IEnumerable
response) + { + Request = request; + Response = response; + } + + Address input; + /// + /// Original input for this response + /// + public Address Request + { + get { return input; } + set + { + if (value == null) + throw new ArgumentNullException("Input"); + + input = value; + } + } + + IEnumerable
output; + /// + /// Output for the given input + /// + public IEnumerable
Response + { + get { return output; } + set + { + if (value == null) + throw new ArgumentNullException("Response"); + + output = value; + } + } + } +} diff --git a/src/Core/packages.config b/src/Core/packages.config new file mode 100644 index 0000000..12fef57 --- /dev/null +++ b/src/Core/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/Geocoding.sln b/src/Geocoding.sln index 8786d38..faf0e96 100644 --- a/src/Geocoding.sln +++ b/src/Geocoding.sln @@ -1,8 +1,6 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.30110.0 -MinimumVisualStudioVersion = 10.0.40219.1 +# Visual Studio 2012 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{891021E5-7521-4F93-9946-B7B630DF3192}" ProjectSection(SolutionItems) = preProject ..\build-with-tests.bat = ..\build-with-tests.bat @@ -22,14 +20,24 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "Core\Core.csproj", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{BB5F66AC-286B-4BA5-86EC-6DED28426132}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google", "Google\Google.csproj", "{2E608F4E-29F7-4AC3-922F-23D971312CAE}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "APIs", "APIs", "{F742864D-9400-4CE2-957A-DBD0A0237277}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google", "Google\Google.csproj", "{2E608F4E-29F7-4AC3-922F-23D971312CAE}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yahoo", "Yahoo\Yahoo.csproj", "{B2863E87-3983-46EF-8B3E-37A4475ECA13}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft", "Microsoft\Microsoft.csproj", "{74BCB608-4674-452F-A50C-7EE61AC47EAF}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MapQuest", "MapQuest\MapQuest.csproj", "{B37FC059-5E9E-4893-994A-64D835D2A54F}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{B59E8640-AAE1-4447-AB9B-5CB3AD7A4549}" + ProjectSection(SolutionItems) = preProject + .nuget\NuGet.Config = .nuget\NuGet.Config + .nuget\NuGet.exe = .nuget\NuGet.exe + .nuget\NuGet.targets = .nuget\NuGet.targets + .nuget\packages.config = .nuget\packages.config + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -56,6 +64,10 @@ Global {74BCB608-4674-452F-A50C-7EE61AC47EAF}.Debug|Any CPU.Build.0 = Debug|Any CPU {74BCB608-4674-452F-A50C-7EE61AC47EAF}.Release|Any CPU.ActiveCfg = Release|Any CPU {74BCB608-4674-452F-A50C-7EE61AC47EAF}.Release|Any CPU.Build.0 = Release|Any CPU + {B37FC059-5E9E-4893-994A-64D835D2A54F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B37FC059-5E9E-4893-994A-64D835D2A54F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B37FC059-5E9E-4893-994A-64D835D2A54F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B37FC059-5E9E-4893-994A-64D835D2A54F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -67,5 +79,9 @@ Global {2E608F4E-29F7-4AC3-922F-23D971312CAE} = {F742864D-9400-4CE2-957A-DBD0A0237277} {B2863E87-3983-46EF-8B3E-37A4475ECA13} = {F742864D-9400-4CE2-957A-DBD0A0237277} {74BCB608-4674-452F-A50C-7EE61AC47EAF} = {F742864D-9400-4CE2-957A-DBD0A0237277} + {B37FC059-5E9E-4893-994A-64D835D2A54F} = {F742864D-9400-4CE2-957A-DBD0A0237277} + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = Tests\Tests.csproj EndGlobalSection EndGlobal diff --git a/src/Google/BusinessKey.cs b/src/Google/BusinessKey.cs index c74262d..433af42 100644 --- a/src/Google/BusinessKey.cs +++ b/src/Google/BusinessKey.cs @@ -20,7 +20,7 @@ public BusinessKey(string clientId, string signingKey) string CheckParam(string value, string name) { - if (String.IsNullOrEmpty(value)) + if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(name, "Value cannot be null or empty."); return value.Trim(); @@ -66,7 +66,7 @@ public override int GetHashCode() public override string ToString() { - return String.Format("ClientId: {0}, SigningKey: {1}", ClientId, SigningKey); + return string.Format("ClientId: {0}, SigningKey: {1}", ClientId, SigningKey); } } } \ No newline at end of file diff --git a/src/Google/Google.csproj b/src/Google/Google.csproj index 22b0965..8cdf57d 100644 --- a/src/Google/Google.csproj +++ b/src/Google/Google.csproj @@ -47,9 +47,6 @@ - - Properties\AssemblyInfoCommon.cs - @@ -58,7 +55,6 @@ - diff --git a/src/Google/GoogleAddressComponent.cs b/src/Google/GoogleAddressComponent.cs index f04c2ca..626cc4e 100644 --- a/src/Google/GoogleAddressComponent.cs +++ b/src/Google/GoogleAddressComponent.cs @@ -23,7 +23,7 @@ public GoogleAddressComponent(GoogleAddressType[] types, string longName, string public override string ToString() { - return String.Format("{0}: {1}", Types[0], LongName); + return string.Format("{0}: {1}", Types[0], LongName); } } } \ No newline at end of file diff --git a/src/Google/GoogleGeocoder.cs b/src/Google/GoogleGeocoder.cs index 9490e2f..78ea682 100644 --- a/src/Google/GoogleGeocoder.cs +++ b/src/Google/GoogleGeocoder.cs @@ -21,6 +21,16 @@ public class GoogleGeocoder : IGeocoder, IAsyncGeocoder BusinessKey businessKey; const string keyMessage = "Only one of BusinessKey or ApiKey should be set on the GoogleGeocoder."; + public GoogleGeocoder(BusinessKey businessKey) + { + BusinessKey = businessKey; + } + + public GoogleGeocoder(string apiKey) + { + ApiKey = apiKey; + } + public string ApiKey { get { return apiKey; } @@ -28,6 +38,9 @@ public string ApiKey { if (businessKey != null) throw new InvalidOperationException(keyMessage); + if (string.IsNullOrWhiteSpace(value)) + throw new ArgumentException("ApiKey can not be null or empty"); + apiKey = value; } } @@ -37,8 +50,11 @@ public BusinessKey BusinessKey get { return businessKey; } set { - if (!String.IsNullOrEmpty(apiKey)) + if (!string.IsNullOrEmpty(apiKey)) throw new InvalidOperationException(keyMessage); + if (value == null) + throw new ArgumentException("BusinessKey can not be null"); + businessKey = value; } } @@ -55,19 +71,19 @@ public string ServiceUrl var builder = new StringBuilder(); builder.Append("https://maps.googleapis.com/maps/api/geocode/xml?{0}={1}&sensor=false"); - if (!String.IsNullOrEmpty(Language)) + if (!string.IsNullOrEmpty(Language)) { builder.Append("&language="); builder.Append(HttpUtility.UrlEncode(Language)); } - if (!String.IsNullOrEmpty(RegionBias)) + if (!string.IsNullOrEmpty(RegionBias)) { builder.Append("®ion="); builder.Append(HttpUtility.UrlEncode(RegionBias)); } - if (!String.IsNullOrEmpty(ApiKey)) + if (!string.IsNullOrEmpty(ApiKey)) { builder.Append("&key="); builder.Append(HttpUtility.UrlEncode(ApiKey)); @@ -97,7 +113,7 @@ public string ServiceUrl public IEnumerable Geocode(string address) { - if (String.IsNullOrEmpty(address)) + if (string.IsNullOrEmpty(address)) throw new ArgumentNullException("address"); HttpWebRequest request = BuildWebRequest("address", HttpUtility.UrlEncode(address)); @@ -120,7 +136,7 @@ public IEnumerable ReverseGeocode(double latitude, double longitu public Task> GeocodeAsync(string address) { - if (String.IsNullOrEmpty(address)) + if (string.IsNullOrEmpty(address)) throw new ArgumentNullException("address"); HttpWebRequest request = BuildWebRequest("address", HttpUtility.UrlEncode(address)); @@ -129,7 +145,7 @@ public Task> GeocodeAsync(string address) public Task> GeocodeAsync(string address, CancellationToken cancellationToken) { - if (String.IsNullOrEmpty(address)) + if (string.IsNullOrEmpty(address)) throw new ArgumentNullException("address"); HttpWebRequest request = BuildWebRequest("address", HttpUtility.UrlEncode(address)); @@ -150,12 +166,12 @@ public Task> ReverseGeocodeAsync(double latitude, dou private string BuildAddress(string street, string city, string state, string postalCode, string country) { - return String.Format("{0} {1}, {2} {3}, {4}", street, city, state, postalCode, country); + return string.Format("{0} {1}, {2} {3}, {4}", street, city, state, postalCode, country); } private string BuildGeolocation(double latitude, double longitude) { - return String.Format(CultureInfo.InvariantCulture, "{0},{1}", latitude, longitude); + return string.Format(CultureInfo.InvariantCulture, "{0},{1}", latitude, longitude); } private IEnumerable ProcessRequest(HttpWebRequest request) @@ -289,12 +305,12 @@ Task> IAsyncGeocoder.ReverseGeocodeAsync(double latitude, d private HttpWebRequest BuildWebRequest(string type, string value) { - string url = String.Format(ServiceUrl, type, value); + string url = string.Format(ServiceUrl, type, value); if (BusinessKey != null) url = BusinessKey.GenerateSignature(url); - HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url); + var req = WebRequest.Create(url) as HttpWebRequest; req.Proxy = Proxy; req.Method = "GET"; return req; diff --git a/src/MapQuest/BaseRequest.cs b/src/MapQuest/BaseRequest.cs new file mode 100644 index 0000000..d87820a --- /dev/null +++ b/src/MapQuest/BaseRequest.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Web; +using Newtonsoft.Json; + +namespace Geocoding.MapQuest +{ + /// + /// Geo-code request object + /// + /// + public abstract class BaseRequest + { + protected BaseRequest(string key) //output only, no need for default ctor + { + Key = key; + } + + [JsonIgnore] + string key; + /// + /// A REQUIRED unique key to authorize use of the Routing Service. + /// + /// + [JsonIgnore] + public virtual string Key + { + get { return key; } + set + { + if (string.IsNullOrWhiteSpace(value)) + throw new ArgumentException("An application key is required for MapQuest"); + + key = value; + } + } + + /// + /// Defaults to json + /// + [JsonIgnore] + public virtual DataFormat InputFormat { get; private set; } + + /// + /// Defaults to json + /// + [JsonIgnore] + public virtual DataFormat OutputFormat { get; private set; } + + [JsonIgnore] + RequestOptions op = new RequestOptions(); + /// + /// Optional settings + /// + [JsonProperty("options")] + public virtual RequestOptions Options + { + get { return op; } + protected set + { + if (value == null) + throw new ArgumentNullException("Options"); + + op = value; + } + } + + /// + /// if true, use Open Street Map, else use commercial map + /// + public virtual bool UseOSM { get; set; } + + /// + /// We are using v1 of MapQuest OSM API + /// + protected virtual string BaseRequestPath + { + get + { + if (UseOSM) + return @"http://open.mapquestapi.com/geocoding/v1/"; + else + return @"http://www.mapquestapi.com/geocoding/v1/"; + } + } + + /// + /// The full path for the request + /// + [JsonIgnore] + public virtual Uri RequestUri + { + get + { + var sb = new StringBuilder(BaseRequestPath); + sb.Append(RequestAction); + sb.Append("?"); + //no need to escape this key, it is already escaped by MapQuest at generation + sb.AppendFormat("key={0}&", Key); + + if (InputFormat != DataFormat.json) + sb.AppendFormat("inFormat={0}&", InputFormat); + + if (OutputFormat != DataFormat.json) + sb.AppendFormat("outFormat={0}&", OutputFormat); + + sb.Length--; + return new Uri(sb.ToString()); + } + } + + [JsonIgnore] + public abstract string RequestAction { get; } + + [JsonIgnore] + string _verb = "POST"; + /// + /// Default request verb is POST for security and large batch payloads + /// + [JsonIgnore] + public virtual string RequestVerb + { + get { return _verb; } + protected set { _verb = string.IsNullOrWhiteSpace(value) ? "POST" : value.Trim().ToUpper(); } + } + + /// + /// Request body if request verb is applicable (POST, PUT, etc) + /// + [JsonIgnore] + public virtual string RequestBody + { + get + { + return this.ToJSON(); + } + } + + public override string ToString() + { + return this.RequestBody; + } + } +} diff --git a/src/MapQuest/BatchGeocodeRequest.cs b/src/MapQuest/BatchGeocodeRequest.cs new file mode 100644 index 0000000..c3d3e38 --- /dev/null +++ b/src/MapQuest/BatchGeocodeRequest.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Web; +using Newtonsoft.Json; + +namespace Geocoding.MapQuest +{ + public class BatchGeocodeRequest : BaseRequest + { + public BatchGeocodeRequest(string key, ICollection addresses) + : base(key) + { + if (addresses.IsNullOrEmpty()) + throw new ArgumentException("addresses can not be null or empty"); + + Locations = (from l in addresses select new LocationRequest(l)).ToArray(); + } + + [JsonIgnore] + readonly List _locations = new List(); + /// + /// Required collection of concatenated address string + /// Note input will be hashed for uniqueness. + /// Order is not guaranteed. + /// + [JsonProperty("locations")] + public ICollection Locations + { + get { return _locations; } + set + { + if (value.IsNullOrEmpty()) + throw new ArgumentNullException("Locations can not be null or empty!"); + + _locations.Clear(); + (from v in value + where v != null + select v).ForEach(v => _locations.Add(v)); + + if (_locations.Count == 0) + throw new InvalidOperationException("At least one valid Location is required"); + } + } + + public override string RequestAction + { + get { return "batch"; } + } + } +} diff --git a/src/MapQuest/ClassDiagram.cd b/src/MapQuest/ClassDiagram.cd new file mode 100644 index 0000000..1afd152 --- /dev/null +++ b/src/MapQuest/ClassDiagram.cd @@ -0,0 +1,124 @@ + + + + + + + + + + AAgAAAAAADAAAABMIAAAAEQAIIMwAAAAAQIAhAAAAAA= + MapQuestLocation.cs + + + + + + + + + AIAgAAAAAAAAAAQBAEAEASAgAAAAAAAAAAAAAAAAQAA= + MapQuestGeocoder.cs + + + + + + + + + + + AAAAAAAAAAAAAAUEAAAOAQABAAAAAAAACAAYAgBAAAA= + BaseRequest.cs + + + + + + + + + + + + + AAAAAAAAAAAAAAAAAAIIAEAAAAAAAAAAAAAAAAAAAAA= + BatchGeocodeRequest.cs + + + + + + + + + + + + + AAAAAAAAAAAAAAAEAAIAAAACAAAAAgAAAAIAAAAAAAA= + LocationRequest.cs + + + + + + + + + + AAAAAAAAAAAAAAAAAAIICAAAAAAAAAAAAAAAAAAAAAA= + ReverseGeocodeRequest.cs + + + + + + + + + AAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAA= + GeocodeRequest.cs + + + + + + + + + AAAAAACAAAAAAAABAAAAAAEAAAAAAQAAAAAAIAAAAAA= + RequestOptions.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJAAAAAAAAA= + LocationType.cs + + + + + + AAAAAAAAAAAACAAAIAAAAAAAAEAAAAAAACABAAAAAAA= + ResponseStatus.cs + + + + + + QACAAAAAAAAAAAAAgAAAAIAABkAIAAAAAAAAACAAAAA= + Quality.cs + + + + + + AAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAABACAAAAAAA= + DataFormat.cs + + + + \ No newline at end of file diff --git a/src/MapQuest/DataFormat.cs b/src/MapQuest/DataFormat.cs new file mode 100644 index 0000000..70b91a5 --- /dev/null +++ b/src/MapQuest/DataFormat.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Geocoding.MapQuest +{ + public enum DataFormat + { + json, + xml, + csv, + } +} diff --git a/src/MapQuest/GeocodeRequest.cs b/src/MapQuest/GeocodeRequest.cs new file mode 100644 index 0000000..a2dbc6b --- /dev/null +++ b/src/MapQuest/GeocodeRequest.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json; + +namespace Geocoding.MapQuest +{ + public class GeocodeRequest : ReverseGeocodeRequest + { + public GeocodeRequest(string key, string address) + : this(key, new LocationRequest(address)) + { + } + + public GeocodeRequest(string key, LocationRequest loc) + : base(key, loc) + { + } + + public override string RequestAction + { + get { return "address"; } + } + } +} diff --git a/src/MapQuest/LocationRequest.cs b/src/MapQuest/LocationRequest.cs new file mode 100644 index 0000000..a1cb338 --- /dev/null +++ b/src/MapQuest/LocationRequest.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json; + +namespace Geocoding.MapQuest +{ + public class LocationRequest + { + public LocationRequest(string street) + { + Street = street; + } + + public LocationRequest(Location location) + { + Location = location; + } + + [JsonIgnore] + string street; + /// + /// Full street address or intersection for geocoding + /// + [JsonProperty("street", NullValueHandling = NullValueHandling.Ignore)] + public virtual string Street + { + get { return street; } + set + { + if (string.IsNullOrWhiteSpace(value)) + throw new ArgumentException("Street can not be null or blank"); + + street = value; + } + } + + [JsonIgnore] + Location location; + /// + /// Latitude and longitude for reverse geocoding + /// + [JsonProperty("latLng", NullValueHandling=NullValueHandling.Ignore)] + public virtual Location Location + { + get { return location; } + set + { + if (value == null) + throw new ArgumentNullException("Location"); + + location = value; + } + } + + public override string ToString() + { + return string.Format("street: {0}", Street); + } + + } +} diff --git a/src/MapQuest/LocationType.cs b/src/MapQuest/LocationType.cs new file mode 100644 index 0000000..717e212 --- /dev/null +++ b/src/MapQuest/LocationType.cs @@ -0,0 +1,19 @@ +using System; + +namespace Geocoding.MapQuest +{ + /// + /// http://code.google.com/apis/maps/documentation/geocoding/#Types + /// + public enum LocationType + { + /// + /// Stop: default + /// + s, + /// + /// Via + /// + v, + } +} \ No newline at end of file diff --git a/src/MapQuest/MapQuest.csproj b/src/MapQuest/MapQuest.csproj new file mode 100644 index 0000000..c2866e5 --- /dev/null +++ b/src/MapQuest/MapQuest.csproj @@ -0,0 +1,95 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {B37FC059-5E9E-4893-994A-64D835D2A54F} + Library + Properties + Geocoding.MapQuest + Geocoding.MapQuest + v3.5 + v4.0 + 512 + + ..\ + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + none + true + bin\Release\ + TRACE + prompt + 4 + true + + + true + + + ..\geocoding.snk + + + + + + + + + ..\packages\Newtonsoft.Json.6.0.3\lib\net40\Newtonsoft.Json.dll + + + + + + + + + + + + + + + + + + + + + + + + Properties\geocoding.snk + + + + + + + {654812CF-D009-4420-B9EC-D07B030926A1} + Core + + + + + + + + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + \ No newline at end of file diff --git a/src/MapQuest/MapQuestGeocoder.cs b/src/MapQuest/MapQuestGeocoder.cs new file mode 100644 index 0000000..9d81845 --- /dev/null +++ b/src/MapQuest/MapQuestGeocoder.cs @@ -0,0 +1,266 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Text; +using System.Web; + +namespace Geocoding.MapQuest +{ + /// + /// + /// + /// + public class MapQuestGeocoder : IGeocoder, IBatchGeocoder + { + readonly string key; + + volatile bool useOSM; + /// + /// When true, will use the Open Street Map API + /// + public virtual bool UseOSM + { + get { return useOSM; } + set { useOSM = value; } + } + + public MapQuestGeocoder(string key) + { + if (string.IsNullOrWhiteSpace(key)) + throw new ArgumentException("key can not be null or blank"); + + this.key = key; + } + + IEnumerable
HandleSingleResponse(MapQuestResponse res) + { + if (res != null && !res.Results.IsNullOrEmpty()) + { + return HandleSingleResponse(from r in res.Results + where r != null && !r.Locations.IsNullOrEmpty() + from l in r.Locations + select l); + } + else + return new Address[0]; + } + + IEnumerable
HandleSingleResponse(IEnumerable locs) + { + if (locs == null) + return new Address[0]; + else + { + return from l in locs + where l != null && l.Quality < Quality.COUNTRY + let q = (int)l.Quality + let c = string.IsNullOrWhiteSpace(l.Confidence) ? "ZZZZZZ" : l.Confidence + orderby q ascending, c ascending + select l; + } + } + + public IEnumerable
Geocode(string address) + { + if (string.IsNullOrWhiteSpace(address)) + throw new ArgumentException("address can not be null or empty!"); + + var f = new GeocodeRequest(key, address) { UseOSM = this.UseOSM }; + MapQuestResponse res = Execute(f); + return HandleSingleResponse(res); + } + + public IEnumerable
Geocode(string street, string city, string state, string postalCode, string country) + { + var sb = new StringBuilder (); + if (!string.IsNullOrWhiteSpace (street)) + sb.AppendFormat ("{0}, ", street); + if (!string.IsNullOrWhiteSpace (city)) + sb.AppendFormat ("{0}, ", city); + if (!string.IsNullOrWhiteSpace (state)) + sb.AppendFormat ("{0} ", state); + if (!string.IsNullOrWhiteSpace (postalCode)) + sb.AppendFormat ("{0} ", postalCode); + if (!string.IsNullOrWhiteSpace (country)) + sb.AppendFormat ("{0} ", country); + + if (sb.Length > 1) + sb.Length--; + + string s = sb.ToString ().Trim (); + if (string.IsNullOrWhiteSpace (s)) + throw new ArgumentException ("Concatenated input values can not be null or blank"); + + if (s.Last () == ',') + s = s.Remove (s.Length - 1); + + return Geocode (s); + } + + public IEnumerable
ReverseGeocode(Location location) + { + if (location == null) + throw new ArgumentNullException ("location"); + + var f = new ReverseGeocodeRequest(key, location) { UseOSM = this.UseOSM }; + MapQuestResponse res = Execute(f); + return HandleSingleResponse(res); + } + + public IEnumerable
ReverseGeocode(double latitude, double longitude) + { + return ReverseGeocode(new Location(latitude, longitude)); + } + + public MapQuestResponse Execute(BaseRequest f) + { + HttpWebRequest request = Send(f); + MapQuestResponse r = Parse(request); + if (r != null && !r.Results.IsNullOrEmpty()) + { + foreach (MapQuestResult o in r.Results) + { + if (o == null) + continue; + + foreach (MapQuestLocation l in o.Locations) + { + if (!string.IsNullOrWhiteSpace(l.FormattedAddress) || o.ProvidedLocation == null) + continue; + + if (string.Compare(o.ProvidedLocation.FormattedAddress, "unknown", true) != 0) + l.FormattedAddress = o.ProvidedLocation.FormattedAddress; + else + l.FormattedAddress = o.ProvidedLocation.ToString(); + } + } + } + return r; + } + + HttpWebRequest Send(BaseRequest f) + { + if (f == null) + throw new ArgumentNullException("f"); + + HttpWebRequest request; + bool hasBody = false; + switch (f.RequestVerb) + { + case "GET": + case "DELETE": + case "HEAD": + { + var u = string.Format("{0}json={1}&", f.RequestUri, HttpUtility.UrlEncode(f.RequestBody)); + request = WebRequest.Create(u) as HttpWebRequest; + } + break; + case "POST": + case "PUT": + default: + { + request = WebRequest.Create(f.RequestUri) as HttpWebRequest; + hasBody = !string.IsNullOrWhiteSpace(f.RequestBody); + } + break; + } + request.Method = f.RequestVerb; + request.ContentType = "application/" + f.InputFormat; + request.Expect = "application/" + f.OutputFormat; + + if (hasBody) + { + byte[] buffer = Encoding.UTF8.GetBytes(f.RequestBody); + request.ContentLength = buffer.Length; + using (Stream rs = request.GetRequestStream()) + { + rs.Write(buffer, 0, buffer.Length); + rs.Flush(); + rs.Close(); + } + } + return request; + } + + MapQuestResponse Parse(HttpWebRequest request) + { + if (request == null) + throw new ArgumentNullException("request"); + + string requestInfo = string.Format("[{0}] {1}", request.Method, request.RequestUri); + try + { + string json; + using (HttpWebResponse response = request.GetResponse() as HttpWebResponse) + { + if ((int)response.StatusCode >= 300) //error + throw new HttpException((int)response.StatusCode, response.StatusDescription); + + using (var sr = new StreamReader(response.GetResponseStream())) + json = sr.ReadToEnd(); + } + if (string.IsNullOrWhiteSpace(json)) + throw new ApplicationException("Remote system response with blank: " + requestInfo); + + MapQuestResponse o = json.FromJSON(); + if (o == null) + throw new ApplicationException("Unable to deserialize remote response: " + requestInfo + " => " + json); + + return o; + } + catch (WebException wex) //convert to simple exception & close the response stream + { + using (HttpWebResponse response = wex.Response as HttpWebResponse) + { + var sb = new StringBuilder(requestInfo); + sb.Append(" | "); + sb.Append(response.StatusDescription); + sb.Append(" | "); + using (var sr = new StreamReader(response.GetResponseStream())) + { + sb.Append(sr.ReadToEnd()); + } + throw new HttpException((int)response.StatusCode, sb.ToString()); + } + } + } + + public ICollection Geocode(IEnumerable addresses) + { + if (addresses == null) + throw new ArgumentNullException("addresses"); + + string[] adr = (from a in addresses + where !string.IsNullOrWhiteSpace(a) + group a by a into ag + select ag.Key).ToArray(); + if (adr.IsNullOrEmpty()) + throw new ArgumentException("Atleast one none blank item is required in addresses"); + + var f = new BatchGeocodeRequest(key, adr) { UseOSM = this.UseOSM }; + MapQuestResponse res = Execute(f); + return HandleBatchResponse(res); + } + + ICollection HandleBatchResponse(MapQuestResponse res) + { + if (res != null && !res.Results.IsNullOrEmpty()) + { + return (from r in res.Results + where r != null && !r.Locations.IsNullOrEmpty() + let resp = HandleSingleResponse(r.Locations) + where resp != null + select new ResultItem(r.ProvidedLocation, resp)).ToArray(); + } + else + return new ResultItem[0]; + } + + public ICollection ReverseGeocode(IEnumerable locations) + { + throw new InvalidOperationException("ReverseGeocode(...) is not available for MapQuestGeocoder."); + } + } +} \ No newline at end of file diff --git a/src/MapQuest/MapQuestLocation.cs b/src/MapQuest/MapQuestLocation.cs new file mode 100644 index 0000000..d113c4c --- /dev/null +++ b/src/MapQuest/MapQuestLocation.cs @@ -0,0 +1,150 @@ +using System; +using System.Text; +using System.Linq; +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Geocoding.MapQuest +{ + /// + /// MapQuest address obj. + /// + /// + public class MapQuestLocation : ParsedAddress + { + const string UNKNOWN = "unknown"; + static readonly string DEFAULT_LOC = new Location(0, 0).ToString(); + + public MapQuestLocation(string formattedAddress, Location coordinates) + : base( + string.IsNullOrWhiteSpace(formattedAddress) ? UNKNOWN : formattedAddress, + coordinates ?? new Location(0, 0), + "MapQuest") + { + DisplayCoordinates = coordinates; + } + + [JsonProperty("location")] + public override string FormattedAddress + { + get + { + return ToString(); + } + set { base.FormattedAddress = value; } + } + + [JsonProperty("latLng")] + public override Location Coordinates + { + get { return base.Coordinates; } + set { base.Coordinates = value; } + } + + [JsonProperty("displayLatLng")] + public virtual Location DisplayCoordinates { get; set; } + + [JsonProperty("street")] + public override string Street { get; set; } + + [JsonProperty("adminArea5")] + public override string City { get; set; } + + [JsonProperty("adminArea4")] + public override string County { get; set; } + + [JsonProperty("adminArea3")] + public override string State { get; set; } + + [JsonProperty("adminArea1")] + public override string Country { get; set; } + + [JsonProperty("postalCode")] + public override string PostCode { get; set; } + + public override string ToString() + { + if (base.FormattedAddress != UNKNOWN) + return base.FormattedAddress; + else + { + var sb = new StringBuilder(); + if (!string.IsNullOrWhiteSpace(Street)) + sb.AppendFormat("{0}, ", Street); + + if (!string.IsNullOrWhiteSpace(City)) + sb.AppendFormat("{0}, ", City); + + if (!string.IsNullOrWhiteSpace(State)) + sb.AppendFormat("{0} ", State); + else if (!string.IsNullOrWhiteSpace(County)) + sb.AppendFormat("{0} ", County); + + if (!string.IsNullOrWhiteSpace(PostCode)) + sb.AppendFormat("{0} ", PostCode); + + if (!string.IsNullOrWhiteSpace(Country)) + sb.AppendFormat("{0} ", Country); + + if (sb.Length > 1) + { + sb.Length--; + + string s = sb.ToString(); + if (s.Last() == ',') + s = s.Remove(s.Length - 1); + + return s; + } + else if (Coordinates != null && Coordinates.ToString() != DEFAULT_LOC) + return Coordinates.ToString(); + else + return UNKNOWN; + } + } + + /// + /// Type of location + /// + [JsonProperty("type")] + public virtual LocationType Type { get; set; } + + /// + /// Granularity code of quality/accuracy guarantee + /// + /// + [JsonProperty("geocodeQuality")] + public virtual Quality Quality { get; set; } + + /// + /// Text string comparable, sort able score + /// + /// + [JsonProperty("geocodeQualityCode")] + public virtual string Confidence { get; set; } + + /// + /// Identifies the closest road to the address for routing purposes. + /// + [JsonProperty("linkId")] + public virtual string LinkId { get; set; } + + /// + /// Which side of the street this address is in + /// + [JsonProperty("sideOfStreet")] + public virtual SideOfStreet SideOfStreet { get; set; } + + /// + /// Url to a MapQuest map + /// + [JsonProperty("mapUrl")] + public virtual Uri MapUrl { get; set; } + + [JsonProperty("adminArea1Type")] + public virtual string CountryLabel { get; set; } + + [JsonProperty("adminArea3Type")] + public virtual string StateLabel { get; set; } + } +} \ No newline at end of file diff --git a/src/MapQuest/MapQuestResponse.cs b/src/MapQuest/MapQuestResponse.cs new file mode 100644 index 0000000..bfd9d62 --- /dev/null +++ b/src/MapQuest/MapQuestResponse.cs @@ -0,0 +1,20 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Geocoding.MapQuest +{ + public class MapQuestResponse + { + //[JsonArray(AllowNullItems=true)] + [JsonProperty("results")] + public IList Results { get; set; } + + [JsonProperty("options")] + public RequestOptions Options { get; set; } + + [JsonProperty("info")] + public ResponseInfo Info { get; set; } + } +} diff --git a/src/MapQuest/MapQuestResult.cs b/src/MapQuest/MapQuestResult.cs new file mode 100644 index 0000000..2710f94 --- /dev/null +++ b/src/MapQuest/MapQuestResult.cs @@ -0,0 +1,19 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Geocoding.MapQuest +{ + /// + /// Result obj returned in a collection of OSM response under the property: results + /// + public class MapQuestResult + { + [JsonProperty("locations")] + public IList Locations { get; set; } + + [JsonProperty("providedLocation")] + public MapQuestLocation ProvidedLocation { get; set; } + } +} diff --git a/src/MapQuest/Properties/.gitignore b/src/MapQuest/Properties/.gitignore new file mode 100644 index 0000000..cfd2c47 --- /dev/null +++ b/src/MapQuest/Properties/.gitignore @@ -0,0 +1 @@ +/AssemblyVersion.cs diff --git a/src/MapQuest/Properties/AssemblyInfo.cs b/src/MapQuest/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..eb735d8 --- /dev/null +++ b/src/MapQuest/Properties/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Reflection; + +[assembly: AssemblyTitle("Geocoding MapQuest API")] \ No newline at end of file diff --git a/src/MapQuest/Quality.cs b/src/MapQuest/Quality.cs new file mode 100644 index 0000000..758f96f --- /dev/null +++ b/src/MapQuest/Quality.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Geocoding.MapQuest +{ + public enum Quality : int + { + /// + /// P1 A specific point location. + /// + POINT = 0, + /// + /// L1 A specific street address location. + /// + ADDRESS = 1, + /// + /// I1 An intersection of two or more streets. + /// + INTERSECTION = 2, + /// + /// B1 The center of a single street block. House number ranges are returned if available. + /// B2 The center of a single street block, which is located closest to the geographic center of all matching street blocks. No house number range is returned. + /// B3 The center of a single street block whose numbered range is nearest to the input number. House number range is returned. + /// + STREET = 3, + /// + /// Z2 Postal code. For USA, a ZIP+2. + /// Z3 Postal code. For USA, a ZIP+4. + /// + ZIP_EXTENDED = 4, + /// + /// Z1 Postal code, largest. For USA, a ZIP. + /// Z4 Postal code, smallest. Unused in USA. + /// + ZIP = 5, + /// + /// A5 Admin area. For USA, a city. + /// + CITY = 6, + /// + /// A4 Admin area. For USA, a county. + /// + COUNTY = 7, + /// + /// A3 Admin area. For USA, a state. + /// + STATE = 8, + /// + /// A1 Admin area, largest. For USA, a country. + /// + COUNTRY = 9, + } +} diff --git a/src/MapQuest/RequestOptions.cs b/src/MapQuest/RequestOptions.cs new file mode 100644 index 0000000..6881b49 --- /dev/null +++ b/src/MapQuest/RequestOptions.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json; + +namespace Geocoding.MapQuest +{ + public class RequestOptions + { + ///// + ///// A delimiter is used only when outFormat=csv. The delimiter is the single character used to separate the fields of a character delimited file. + ///// The delimiter defaults to a comma(,). + ///// The valid choices are ,|:; + ///// + //[JsonProperty("delimiter")] + //public virtual char Delimiter { get; private set; } + + [JsonIgnore] + int _maxResults = -1; + /// + /// The number of results to limit the response to in the case of an ambiguous address. + /// Defaults: -1 (indicates no limit) + /// + [JsonProperty("maxResults")] + public virtual int MaxResults + { + get { return _maxResults; } + set { _maxResults = value > 0 ? value : -1; } + } + + /// + /// This parameter tells the service whether it should return a URL to a static map thumbnail image for a location being geocoded. + /// + [JsonProperty("thumbMaps")] + public virtual bool ThumbMap { get; set; } + + /// + /// This option tells the service whether it should fail when given a latitude/longitude pair in an address or batch geocode call, or if it should ignore that and try and geo-code what it can. + /// + [JsonProperty("ignoreLatLngInput")] + public virtual bool IgnoreLatLngInput { get; set; } + + /// + /// Optional name of JSONP callback method. + /// + [JsonProperty("callback", NullValueHandling = NullValueHandling.Ignore)] + public virtual string JsonpCallBack { get; set; } + } +} diff --git a/src/MapQuest/ResponseInfo.cs b/src/MapQuest/ResponseInfo.cs new file mode 100644 index 0000000..55c128f --- /dev/null +++ b/src/MapQuest/ResponseInfo.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json; + +namespace Geocoding.MapQuest +{ + public class ResponseInfo + { + /// + /// Extended copyright info + /// + //[JsonDictionary] + [JsonProperty("copyright")] + public IDictionary Copyright { get; set; } + + /// + /// Maps to HTTP response code generally + /// + [JsonProperty("statuscode")] + public ResponseStatus Status { get; set; } + + /// + /// Error or status messages if applicable + /// + //[JsonArray(AllowNullItems=true)] + [JsonProperty("messages")] + public IList Messages { get; set; } + } +} diff --git a/src/MapQuest/ResponseStatus.cs b/src/MapQuest/ResponseStatus.cs new file mode 100644 index 0000000..3f41536 --- /dev/null +++ b/src/MapQuest/ResponseStatus.cs @@ -0,0 +1,13 @@ +using System; + +namespace Geocoding.MapQuest +{ + public enum ResponseStatus : int + { + Ok = 0, + OkBatch = 100, + ErrorInput = 400, + ErrorAccountKey = 403, + ErrorUnknown = 500, + } +} \ No newline at end of file diff --git a/src/MapQuest/ReverseGeocodeRequest.cs b/src/MapQuest/ReverseGeocodeRequest.cs new file mode 100644 index 0000000..bdb5575 --- /dev/null +++ b/src/MapQuest/ReverseGeocodeRequest.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json; + +namespace Geocoding.MapQuest +{ + public class ReverseGeocodeRequest : BaseRequest + { + public ReverseGeocodeRequest(string key, double latitude, double longitude) + : this(key, new Location(latitude, longitude)) + { + + } + + public ReverseGeocodeRequest(string key, Location loc) + : this(key, new LocationRequest(loc)) + { + } + + public ReverseGeocodeRequest(string key, LocationRequest loc) + : base(key) + { + Location = loc; + } + + [JsonIgnore] + LocationRequest loc; + /// + /// Latitude and longitude for the request + /// + [JsonProperty("location")] + public virtual LocationRequest Location + { + get { return loc; } + set + { + if (value == null) + throw new ArgumentNullException("Location"); + + loc = value; + } + } + + [JsonIgnore] + public override string RequestAction + { + get { return "reverse"; } + } + } +} diff --git a/src/MapQuest/SideOfStreet.cs b/src/MapQuest/SideOfStreet.cs new file mode 100644 index 0000000..29a1675 --- /dev/null +++ b/src/MapQuest/SideOfStreet.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Geocoding.MapQuest +{ + public enum SideOfStreet + { + /// + /// None: default + /// + N, + /// + /// Left + /// + L, + /// + /// Right + /// + R, + } +} diff --git a/src/MapQuest/packages.config b/src/MapQuest/packages.config new file mode 100644 index 0000000..12fef57 --- /dev/null +++ b/src/MapQuest/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/Microsoft/BingMapsGeocoder.cs b/src/Microsoft/BingMapsGeocoder.cs index 7a08ff6..2447749 100644 --- a/src/Microsoft/BingMapsGeocoder.cs +++ b/src/Microsoft/BingMapsGeocoder.cs @@ -35,6 +35,9 @@ public class BingMapsGeocoder : IGeocoder, IAsyncGeocoder public BingMapsGeocoder(string bingKey) { + if (string.IsNullOrWhiteSpace(bingKey)) + throw new ArgumentException("bingKey can not be null or empty"); + this.bingKey = bingKey; } @@ -64,14 +67,14 @@ private string GetQueryUrl(string street, string city, string state, string post private string GetQueryUrl(double latitude, double longitude) { - var builder = new StringBuilder(string.Format(UNFORMATTED_QUERY, String.Format(CultureInfo.InvariantCulture, "{0},{1}", latitude, longitude), bingKey)); + var builder = new StringBuilder(string.Format(UNFORMATTED_QUERY, string.Format(CultureInfo.InvariantCulture, "{0},{1}", latitude, longitude), bingKey)); AppendGlobalParameters(builder, false); return builder.ToString(); } private IEnumerable> GetGlobalParameters() { - if (!String.IsNullOrEmpty(Culture)) + if (!string.IsNullOrEmpty(Culture)) yield return new KeyValuePair("c", Culture); if (UserLocation != null) @@ -295,7 +298,7 @@ private IEnumerable ParseResponse(Json.Response response) private HttpWebRequest CreateRequest(string url) { - HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); + var request = WebRequest.Create(url) as HttpWebRequest; request.Proxy = Proxy; return request; } diff --git a/src/Microsoft/Microsoft.csproj b/src/Microsoft/Microsoft.csproj index f268563..6dac222 100644 --- a/src/Microsoft/Microsoft.csproj +++ b/src/Microsoft/Microsoft.csproj @@ -42,25 +42,22 @@ - 3.5 + 4.0 - 3.0 + 4.0 - 3.0 + 4.0 - 3.5 + 4.0 - + - - Properties\AssemblyInfoCommon.cs - @@ -69,7 +66,6 @@ - diff --git a/src/Tests/.gitignore b/src/Tests/.gitignore new file mode 100644 index 0000000..b36c8ce --- /dev/null +++ b/src/Tests/.gitignore @@ -0,0 +1,2 @@ +/app.config.temp +/app.config diff --git a/src/Tests/AddressAssertionExtensions.cs b/src/Tests/AddressAssertionExtensions.cs index 61e3e52..6ac30b4 100644 --- a/src/Tests/AddressAssertionExtensions.cs +++ b/src/Tests/AddressAssertionExtensions.cs @@ -7,17 +7,24 @@ public static class AddressAssertionExtensions { public static void AssertWhiteHouse(this Address address) { + string adr = address.FormattedAddress.ToLower(); Assert.True( - address.FormattedAddress.Contains("The White House") || - address.FormattedAddress.Contains("1600 Pennsylvania Ave NW") || - address.FormattedAddress.Contains("1600 Pennsylvania Avenue Northwest") + adr.Contains("The White House") || + adr.Contains("1600 pennsylvania ave nw") || + adr.Contains("1600 pennsylvania avenue northwest") || + adr.Contains("1600 pennsylvania avenue nw") || + adr.Contains("1600 pennsylvania ave northwest") ); AssertWhiteHouseArea(address); } public static void AssertWhiteHouseArea(this Address address) { - Assert.True(address.FormattedAddress.Contains("Washington, DC")); + string adr = address.FormattedAddress.ToLower(); + Assert.True( + adr.Contains("washington") && + (adr.Contains("dc") || adr.Contains("district of columbia")) + ); //just hoping that each geocoder implementation gets it somewhere near the vicinity double lat = Math.Round(address.Coordinates.Latitude, 2); @@ -29,9 +36,11 @@ public static void AssertWhiteHouseArea(this Address address) public static void AssertCanadianPrimeMinister(this Address address) { - Assert.True(address.FormattedAddress.Contains("24 Sussex")); - Assert.True(address.FormattedAddress.Contains("Ottawa, ON")); - Assert.True(address.FormattedAddress.Contains("K1M")); + string adr = address.FormattedAddress.ToLower(); + Assert.True(adr.Contains("24 sussex")); + Assert.True(adr.Contains(" ottawa")); + Assert.True(adr.Contains(" on")); + Assert.True(adr.Contains("k1m")); } } } \ No newline at end of file diff --git a/src/Tests/BatchGeocoderTest.cs b/src/Tests/BatchGeocoderTest.cs new file mode 100644 index 0000000..e00f643 --- /dev/null +++ b/src/Tests/BatchGeocoderTest.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading; +using System.Text; +using Xunit; +using Xunit.Extensions; + +namespace Geocoding.Tests +{ + public abstract class BatchGeocoderTest + { + readonly IBatchGeocoder batchGeocoder; + + public BatchGeocoderTest() + { + Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("en-us"); + + batchGeocoder = CreateBatchGeocoder(); + } + + protected abstract IBatchGeocoder CreateBatchGeocoder(); + + [Theory] + [PropertyData("BatchGeoCodeData")] + public virtual void CanGeoCodeAddress(string[] addresses) + { + Assert.NotEmpty(addresses); + + ICollection results = batchGeocoder.Geocode(addresses); + Assert.NotEmpty(results); + Assert.Equal(addresses.Length, results.Count); + + var ahash = new HashSet(addresses); + Assert.Equal(ahash.Count, results.Count); + + foreach (ResultItem r in results) + { + Assert.NotNull(r); + Assert.NotNull(r.Request); + Assert.NotNull(r.Response); + + Assert.Contains(r.Request.FormattedAddress, ahash); + + Address[] respa = r.Response.ToArray(); + Assert.NotEmpty(respa); + + ahash.Remove(r.Request.FormattedAddress); + } + Assert.Empty(ahash); + } + + public static IEnumerable BatchGeoCodeData + { + get + { + yield return new object[] + { + new string[] + { + "1600 pennsylvania ave nw, washington dc", + "1460 4th Street Ste 304, Santa Monica CA 90401", + }, + }; + } + } + + } +} diff --git a/src/Tests/GeocoderTest.cs b/src/Tests/GeocoderTest.cs index 2e1a102..13ae4ec 100644 --- a/src/Tests/GeocoderTest.cs +++ b/src/Tests/GeocoderTest.cs @@ -19,24 +19,25 @@ public GeocoderTest() protected abstract IGeocoder CreateGeocoder(); - [Fact] - public void CanGeocodeAddress() + [Theory] + [InlineData("1600 pennsylvania ave nw, washington dc")] + public virtual void CanGeocodeAddress(string address) { - Address[] addresses = geocoder.Geocode("1600 pennsylvania ave washington dc").ToArray(); + Address[] addresses = geocoder.Geocode(address).ToArray(); addresses[0].AssertWhiteHouse(); } [Fact] - public void CanGeocodeNormalizedAddress() + public virtual void CanGeocodeNormalizedAddress() { - Address[] addresses = geocoder.Geocode("1600 pennsylvania ave", "washington", "dc", null, null).ToArray(); + Address[] addresses = geocoder.Geocode("1600 pennsylvania ave nw", "washington", "dc", null, null).ToArray(); addresses[0].AssertWhiteHouse(); } [Theory] [InlineData("en-US")] [InlineData("cs-CZ")] - public void CanGeocodeAddressUnderDifferentCultures(string cultureName) + public virtual void CanGeocodeAddressUnderDifferentCultures(string cultureName) { Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(cultureName); @@ -47,7 +48,7 @@ public void CanGeocodeAddressUnderDifferentCultures(string cultureName) [Theory] [InlineData("en-US")] [InlineData("cs-CZ")] - public void CanReverseGeocodeAddressUnderDifferentCultures(string cultureName) + public virtual void CanReverseGeocodeAddressUnderDifferentCultures(string cultureName) { Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(cultureName); @@ -56,23 +57,24 @@ public void CanReverseGeocodeAddressUnderDifferentCultures(string cultureName) } [Fact] - public void ShouldNotBlowUpOnBadAddress() + public virtual void ShouldNotBlowUpOnBadAddress() { - var addresses = geocoder.Geocode("sdlkf;jasl;kjfldksjfasldf"); + Address[] addresses = geocoder.Geocode("sdlkf;jasl;kjfldksj,fasldf").ToArray(); Assert.Empty(addresses); } - [Fact] - public void CanGeocodeWithSpecialCharacters() + [Theory] + [InlineData("Wilshire & Bundy, Los Angeles")] + public virtual void CanGeocodeWithSpecialCharacters(string address) { - var addresses = geocoder.Geocode("Fried St & 2nd St, Gretna, LA 70053"); + Address[] addresses = geocoder.Geocode(address).ToArray(); //asserting no exceptions are thrown and that we get something Assert.NotEmpty(addresses); } [Fact] - public void CanReverseGeocode() + public virtual void CanReverseGeocode() { Address[] addresses = geocoder.ReverseGeocode(38.8976777, -77.036517).ToArray(); addresses[0].AssertWhiteHouseArea(); @@ -82,7 +84,7 @@ public void CanReverseGeocode() [InlineData("1 Robert Wood Johnson Hosp New Brunswick, NJ 08901 USA")] [InlineData("miss, MO")] //https://github.com/chadly/Geocoding.net/issues/6 - public void CanGeocodeInvalidZipCodes(string address) + public virtual void CanGeocodeInvalidZipCodes(string address) { Address[] addresses = geocoder.Geocode(address).ToArray(); Assert.NotEmpty(addresses); diff --git a/src/Tests/GoogleAsyncGeocoderTest.cs b/src/Tests/GoogleAsyncGeocoderTest.cs index 8491321..e3909bb 100644 --- a/src/Tests/GoogleAsyncGeocoderTest.cs +++ b/src/Tests/GoogleAsyncGeocoderTest.cs @@ -12,10 +12,7 @@ public class GoogleAsyncGeocoderTest : AsyncGeocoderTest protected override IAsyncGeocoder CreateAsyncGeocoder() { - geoCoder = new GoogleGeocoder - { - ApiKey = ConfigurationManager.AppSettings["googleApiKey"] - }; + geoCoder = new GoogleGeocoder(ConfigurationManager.AppSettings["googleApiKey"]); return geoCoder; } diff --git a/src/Tests/GoogleGeocoderTest.cs b/src/Tests/GoogleGeocoderTest.cs index c461075..0f5c630 100644 --- a/src/Tests/GoogleGeocoderTest.cs +++ b/src/Tests/GoogleGeocoderTest.cs @@ -12,10 +12,7 @@ public class GoogleGeocoderTest : GeocoderTest protected override IGeocoder CreateGeocoder() { - geocoder = new GoogleGeocoder - { - ApiKey = ConfigurationManager.AppSettings["googleApiKey"] - }; + geocoder = new GoogleGeocoder(ConfigurationManager.AppSettings["googleApiKey"]); return geocoder; } diff --git a/src/Tests/MapQuestBatchGeocoderTest.cs b/src/Tests/MapQuestBatchGeocoderTest.cs new file mode 100644 index 0000000..2f1fc94 --- /dev/null +++ b/src/Tests/MapQuestBatchGeocoderTest.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Configuration; +using Xunit; +using Xunit.Extensions; + +using Geocoding.MapQuest; + +namespace Geocoding.Tests +{ + public class MapQuestBatchGeocoderTest : BatchGeocoderTest + { + protected override IBatchGeocoder CreateBatchGeocoder() + { + string k = ConfigurationManager.AppSettings["mapQuestKey"]; + return new MapQuest.MapQuestGeocoder(k); + } + } +} diff --git a/src/Tests/MapQuestGeocoderTest.cs b/src/Tests/MapQuestGeocoderTest.cs new file mode 100644 index 0000000..8b349fe --- /dev/null +++ b/src/Tests/MapQuestGeocoderTest.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Configuration; +using Xunit; +using Xunit.Extensions; + +using Geocoding.MapQuest; + +namespace Geocoding.Tests +{ + public class MapQuestGeocoderTest : GeocoderTest + { + protected override IGeocoder CreateGeocoder() + { + string k = ConfigurationManager.AppSettings["mapQuestKey"]; + return new MapQuestGeocoder(k); + } + + [Theory] + [InlineData("Wilshire & Bundy, Los Angeles")] + [InlineData("Fried St & 2nd St, Gretna, LA 70053")] + public override void CanGeocodeWithSpecialCharacters(string address) + { + base.CanGeocodeWithSpecialCharacters(address); + } + } +} diff --git a/src/Tests/Tests.csproj b/src/Tests/Tests.csproj index 66e0e16..6f66ceb 100644 --- a/src/Tests/Tests.csproj +++ b/src/Tests/Tests.csproj @@ -35,6 +35,8 @@ false true + ..\ + true true @@ -44,7 +46,6 @@ DEBUG;TRACE prompt 4 - false none @@ -57,15 +58,18 @@ - - 3.5 + 4.0 - 3.0 + 4.0 + + + ..\packages\Newtonsoft.Json.6.0.3\lib\net40\Newtonsoft.Json.dll + ..\..\lib\xunit.1.9.2\lib\net20\xunit.dll @@ -74,11 +78,9 @@ - - Properties\AssemblyInfoCommon.cs - + @@ -87,8 +89,9 @@ + + - @@ -96,6 +99,10 @@ {2E608F4E-29F7-4AC3-922F-23D971312CAE} Google + + {B37FC059-5E9E-4893-994A-64D835D2A54F} + MapQuest + {74BCB608-4674-452F-A50C-7EE61AC47EAF} Microsoft @@ -113,7 +120,9 @@ Properties\geocoding.snk - + + Designer + @@ -139,4 +148,11 @@ + + + + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + \ No newline at end of file diff --git a/src/Tests/app.config.temp b/src/Tests/app.config.temp deleted file mode 100644 index 99cea39..0000000 --- a/src/Tests/app.config.temp +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Tests/packages.config b/src/Tests/packages.config index 998913d..f940a59 100644 --- a/src/Tests/packages.config +++ b/src/Tests/packages.config @@ -1,5 +1,6 @@  + \ No newline at end of file diff --git a/src/Yahoo/OAuthBase.cs b/src/Yahoo/OAuthBase.cs index b89f1d7..4b5cc76 100644 --- a/src/Yahoo/OAuthBase.cs +++ b/src/Yahoo/OAuthBase.cs @@ -143,7 +143,7 @@ protected string UrlEncode(string value) { if (unreservedChars.IndexOf(symbol) != -1) { result.Append(symbol); } else { - result.Append('%' + String.Format("{0:X2}", (int)symbol)); + result.Append('%' + string.Format("{0:X2}", (int)symbol)); } } diff --git a/src/Yahoo/Yahoo.csproj b/src/Yahoo/Yahoo.csproj index 110db06..d54dd43 100644 --- a/src/Yahoo/Yahoo.csproj +++ b/src/Yahoo/Yahoo.csproj @@ -47,11 +47,7 @@ - - Properties\AssemblyInfoCommon.cs - - diff --git a/src/Yahoo/YahooGeocoder.cs b/src/Yahoo/YahooGeocoder.cs index 989f757..a18145a 100644 --- a/src/Yahoo/YahooGeocoder.cs +++ b/src/Yahoo/YahooGeocoder.cs @@ -32,10 +32,10 @@ public string ConsumerSecret public YahooGeocoder(string consumerKey, string consumerSecret) { - if (String.IsNullOrEmpty(consumerKey)) + if (string.IsNullOrEmpty(consumerKey)) throw new ArgumentNullException("consumerKey"); - if (String.IsNullOrEmpty(consumerSecret)) + if (string.IsNullOrEmpty(consumerSecret)) throw new ArgumentNullException("consumerSecret"); this.consumerKey = consumerKey; @@ -44,10 +44,10 @@ public YahooGeocoder(string consumerKey, string consumerSecret) public IEnumerable Geocode(string address) { - if (String.IsNullOrEmpty(address)) + if (string.IsNullOrEmpty(address)) throw new ArgumentNullException("address"); - string url = String.Format(ServiceUrl, HttpUtility.UrlEncode(address)); + string url = string.Format(ServiceUrl, HttpUtility.UrlEncode(address)); HttpWebRequest request = BuildWebRequest(url); return ProcessRequest(request); @@ -55,7 +55,7 @@ public IEnumerable Geocode(string address) public IEnumerable Geocode(string street, string city, string state, string postalCode, string country) { - string url = String.Format(ServiceUrlNormal, HttpUtility.UrlEncode(street), HttpUtility.UrlEncode(city), HttpUtility.UrlEncode(state), HttpUtility.UrlEncode(postalCode), HttpUtility.UrlEncode(country)); + string url = string.Format(ServiceUrlNormal, HttpUtility.UrlEncode(street), HttpUtility.UrlEncode(city), HttpUtility.UrlEncode(state), HttpUtility.UrlEncode(postalCode), HttpUtility.UrlEncode(country)); HttpWebRequest request = BuildWebRequest(url); return ProcessRequest(request); @@ -71,7 +71,7 @@ public IEnumerable ReverseGeocode(Location location) public IEnumerable ReverseGeocode(double latitude, double longitude) { - string url = String.Format(ServiceUrlReverse, String.Format(CultureInfo.InvariantCulture, "{0} {1}", latitude, longitude)); + string url = string.Format(ServiceUrlReverse, string.Format(CultureInfo.InvariantCulture, "{0} {1}", latitude, longitude)); HttpWebRequest request = BuildWebRequest(url); return ProcessRequest(request); @@ -121,7 +121,7 @@ IEnumerable
IGeocoder.ReverseGeocode(double latitude, double longitude) private HttpWebRequest BuildWebRequest(string url) { url = GenerateOAuthSignature(new Uri(url)); - HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url); + var req = WebRequest.Create(url) as HttpWebRequest; req.Method = "GET"; return req; } @@ -148,7 +148,7 @@ string GenerateOAuthSignature(Uri uri) out param ); - return String.Format("{0}?{1}&oauth_signature={2}", url, param, signature); + return string.Format("{0}?{1}&oauth_signature={2}", url, param, signature); } private IEnumerable ProcessWebResponse(WebResponse response) @@ -231,8 +231,8 @@ private string ParseFormattedAddress(XPathNavigator nav) lines[2] = (string)nav.Evaluate("string(line3)"); lines[3] = (string)nav.Evaluate("string(line4)"); - lines = lines.Select(s => (s ?? "").Trim()).Where(s => !String.IsNullOrEmpty(s)).ToArray(); - return String.Join(", ", lines); + lines = lines.Select(s => (s ?? "").Trim()).Where(s => !string.IsNullOrEmpty(s)).ToArray(); + return string.Join(", ", lines); } private YahooError EvaluateError(int errorCode) @@ -245,7 +245,7 @@ private YahooError EvaluateError(int errorCode) public override string ToString() { - return String.Format("Yahoo Geocoder: {0}, {1}", consumerKey, consumerSecret); + return string.Format("Yahoo Geocoder: {0}, {1}", consumerKey, consumerSecret); } } } \ No newline at end of file diff --git a/src/common.targets b/src/common.targets index e029e15..053e720 100644 --- a/src/common.targets +++ b/src/common.targets @@ -29,7 +29,7 @@ - + - + \ No newline at end of file