diff --git a/.github/workflows/dotnet-core.yml b/.github/workflows/dotnet-core.yml index 1422085..922d4ec 100644 --- a/.github/workflows/dotnet-core.yml +++ b/.github/workflows/dotnet-core.yml @@ -11,25 +11,24 @@ on: jobs: build: + name: Build runs-on: ubuntu-latest + outputs: + semver: ${{ steps.gitversion.outputs.semVer }} steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: fetch-depth: 0 # Setup .Net SDKs - - name: Setup .NET Core 3.1 - uses: actions/setup-dotnet@v1 - with: - dotnet-version: '3.1.x' - name: Setup .NET 6.0 - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v4 with: dotnet-version: '6.0.x' - name: Setup .NET 8.0 - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v4 with: dotnet-version: '8.0.x' @@ -38,17 +37,21 @@ jobs: run: dotnet restore - name: Build run: dotnet build --no-restore --configuration Release - - name: Test - run: dotnet test --no-restore --configuration Release + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: build-output + path: '**/bin/Release' # Get the version information - name: Install GitVersion - uses: gittools/actions/gitversion/setup@v0.9.7 + uses: gittools/actions/gitversion/setup@v3.0 if: ${{ success() && contains(github.event_name, 'push') }} with: versionSpec: '5.x' - name: Determine Version - uses: gittools/actions/gitversion/execute@v0.9.7 + id: gitversion + uses: gittools/actions/gitversion/execute@v3.0 if: ${{ success() && contains(github.event_name, 'push') }} with: useConfigFile: true @@ -57,12 +60,96 @@ jobs: run: | echo "SemVer: $GITVERSION_SEMVER" - # Package and publish + test-linux: + name: Test (Linux) + needs: build + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup .NET 6.0 + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '6.0.x' + - name: Setup .NET 8.0 + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.0.x' + + - name: Restore + run: dotnet restore + + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: build-output + + - name: Test (net6.0) + run: dotnet test --no-build --configuration Release --framework net6.0 + - name: Test (net8.0) + run: dotnet test --no-build --configuration Release --framework net8.0 + + test-windows: + name: Test (Windows) + needs: build + runs-on: windows-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup .NET Core 3.1 + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '3.1.x' + - name: Setup .NET 8.0 + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.0.x' + + - name: Restore + run: dotnet restore + + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: build-output + + - name: Test (netcoreapp3.1) + run: dotnet test --no-build --configuration Release --framework netcoreapp3.1 + - name: Test (net48) + run: dotnet test --no-build --configuration Release --framework net48 + + publish: + name: Package and Publish + needs: [build, test-linux, test-windows] + runs-on: ubuntu-latest + if: ${{ success() && contains(github.event_name, 'push') }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup .NET 8.0 + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.0.x' + + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: build-output - name: Package - if: ${{ success() && contains(github.event_name, 'push') }} - run: dotnet pack --no-build --configuration Release --output ./Packages -p:PackageVersion=$GITVERSION_SEMVER + run: dotnet pack --no-build --configuration Release --output ./Packages -p:PackageVersion=${{ needs.build.outputs.semver }} - name: Push - if: ${{ success() && contains(github.event_name, 'push') }} run: | cd Packages dotnet nuget push "*.nupkg" --source https://api.nuget.org/v3/index.json --api-key $NUGET_API_KEY diff --git a/src/Geo.ArcGIS/Abstractions/IArcGISGeocoding.cs b/src/Geo.ArcGIS/Abstractions/IArcGISGeocoding.cs index 71d1690..8096a59 100644 --- a/src/Geo.ArcGIS/Abstractions/IArcGISGeocoding.cs +++ b/src/Geo.ArcGIS/Abstractions/IArcGISGeocoding.cs @@ -5,6 +5,7 @@ namespace Geo.ArcGIS { + using System; using System.Threading; using System.Threading.Tasks; using Geo.ArcGIS.Models.Parameters; @@ -16,6 +17,16 @@ namespace Geo.ArcGIS /// public interface IArcGISGeocoding { + /// + /// Calls the ArcGIS findAddressCandidates API and returns the results. + /// Supports both single-line and structured (multi-field) address input. + /// + /// A with the parameters of the request. + /// A used to cancel the request. + /// A with the response from ArcGIS. + /// Thrown for multiple different reasons. Check the inner exception for more information. + Task FindAddressCandidatesAsync(FindAddressCandidatesParameters parameters, CancellationToken cancellationToken = default); + /// /// Calls the ArcGIS address candidate API and returns the results. /// @@ -23,6 +34,7 @@ public interface IArcGISGeocoding /// A used to cancel the request. /// A with the response from ArcGIS. /// Thrown for multiple different reasons. Check the inner exception for more information. + [Obsolete("Use FindAddressCandidatesAsync instead.")] Task AddressCandidateAsync(AddressCandidateParameters parameters, CancellationToken cancellationToken = default); /// @@ -32,6 +44,7 @@ public interface IArcGISGeocoding /// A used to cancel the request. /// A with the response from ArcGIS. /// Thrown for multiple different reasons. Check the inner exception for more information. + [Obsolete("Use FindAddressCandidatesAsync instead.")] Task PlaceCandidateAsync(PlaceCandidateParameters parameters, CancellationToken cancellationToken = default); /// diff --git a/src/Geo.ArcGIS/Converters/AttributeConverter.cs b/src/Geo.ArcGIS/Converters/AttributeConverter.cs deleted file mode 100644 index d8e0aea..0000000 --- a/src/Geo.ArcGIS/Converters/AttributeConverter.cs +++ /dev/null @@ -1,130 +0,0 @@ -// -// Copyright (c) Geo.NET. -// Licensed under the MIT license. See the LICENSE file in the solution root for full license information. -// - -namespace Geo.ArcGIS.Converters -{ - using System; - using System.Globalization; - using System.Text.Json; - using System.Text.Json.Serialization; - using Geo.ArcGIS.Models.Responses; - using Geo.Core.Extensions; - - /// - /// A converter class for the Attribute class. - /// - public class AttributeConverter : JsonConverter - { - private const string LocationAttribute = "LongLabel"; - private const string AddressAttribute = "Match_addr"; - private static readonly Type LocationType = typeof(LocationAttribute); - private static readonly Type AddressType = typeof(AddressAttribute); - private static readonly Type PlaceType = typeof(PlaceAttribute); - - /// - public override Models.Responses.Attribute Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - if (typeToConvert == null) - { - throw new ArgumentNullException(nameof(typeToConvert)); - } - - if (options == null) - { - throw new ArgumentNullException(nameof(options)); - } - - var typeReader = reader; - - if (typeReader.TokenType == JsonTokenType.Null) - { - return null; - } - - if (typeReader.TokenType != JsonTokenType.StartObject) - { - throw new JsonException(string.Format(CultureInfo.InvariantCulture, "Unexpected token while parsing the attribute. Expected to find an object, instead found {0}", reader.TokenType.GetName())); - } - - // 0 = Location - // 1 = Address - // 2 = Place - var type = 2; - - while (typeReader.Read()) - { - if (typeReader.TokenType == JsonTokenType.EndObject) - { - break; - } - - if (typeReader.TokenType != JsonTokenType.PropertyName) - { - throw new JsonException(string.Format(CultureInfo.InvariantCulture, "Unexpected token while parsing the attribute. Expected to find a property name, instead found {0}", reader.TokenType.GetName())); - } - - if (typeReader.GetString() == LocationAttribute) - { - type = 0; - break; - } - - if (typeReader.GetString() == AddressAttribute) - { - type = 1; - break; - } - - typeReader.Read(); - } - - if (type == 0) - { - return JsonSerializer.Deserialize(ref reader, options); - } - else if (type == 1) - { - return JsonSerializer.Deserialize(ref reader, options); - } - else - { - return JsonSerializer.Deserialize(ref reader, options); - } - } - - /// - public override void Write(Utf8JsonWriter writer, Models.Responses.Attribute value, JsonSerializerOptions options) - { - if (writer == null) - { - throw new ArgumentNullException(nameof(writer)); - } - - if (options == null) - { - throw new ArgumentNullException(nameof(options)); - } - - if (value == null) - { - writer.WriteNullValue(); - return; - } - - if (value.GetType() == LocationType) - { - JsonSerializer.Serialize(writer, value, LocationType, options); - } - else if (value.GetType() == AddressType) - { - JsonSerializer.Serialize(writer, value, AddressType, options); - } - else - { - JsonSerializer.Serialize(writer, value, PlaceType, options); - } - } - } -} diff --git a/src/Geo.ArcGIS/Models/Parameters/AddressCandidateParameters.cs b/src/Geo.ArcGIS/Models/Parameters/AddressCandidateParameters.cs index 7c209de..bdc5cff 100644 --- a/src/Geo.ArcGIS/Models/Parameters/AddressCandidateParameters.cs +++ b/src/Geo.ArcGIS/Models/Parameters/AddressCandidateParameters.cs @@ -5,9 +5,12 @@ namespace Geo.ArcGIS.Models.Parameters { + using System; + /// /// A parameters object for the address candidates ArcGIS request. /// + [Obsolete("Use FindAddressCandidatesParameters instead.")] public class AddressCandidateParameters : StorageParameters, IClientCredentialsParameters { /// diff --git a/src/Geo.ArcGIS/Models/Parameters/FindAddressCandidatesParameters.cs b/src/Geo.ArcGIS/Models/Parameters/FindAddressCandidatesParameters.cs new file mode 100644 index 0000000..69c9441 --- /dev/null +++ b/src/Geo.ArcGIS/Models/Parameters/FindAddressCandidatesParameters.cs @@ -0,0 +1,162 @@ +// +// Copyright (c) Geo.NET. +// Licensed under the MIT license. See the LICENSE file in the solution root for full license information. +// + +namespace Geo.ArcGIS.Models.Parameters +{ + using System.Collections.Generic; + using System.Globalization; + using Geo.ArcGIS.Enums; + using Geo.ArcGIS.Models.Responses; + + /// + /// A parameters object for the ArcGIS findAddressCandidates request. + /// Supports both single-line and structured (multi-field) address input. + /// At least one of or a structured field (e.g. ) must be provided. + /// + public class FindAddressCandidatesParameters : StorageParameters, IClientCredentialsParameters + { + // ── Single-line input ──────────────────────────────────────────────── + + /// + /// Gets or sets the address you want to geocode as a single line of text. + /// Mutually exclusive with structured address fields; if both are supplied, single-line takes precedence. + /// + public string SingleLineAddress { get; set; } + + /// + /// Gets or sets an ID attribute value that, along with , links a suggestion + /// returned by the suggest operation to a specific address or place. + /// + public string MagicKey { get; set; } + + // ── Structured (multi-field) address input ─────────────────────────── + + /// + /// Gets or sets the first line of the street address (street name and house number). + /// + public string Address { get; set; } + + /// + /// Gets or sets the second line of a street address. + /// This can include a building name, suite, subunit, or place name. + /// + public string Address2 { get; set; } + + /// + /// Gets or sets the third line of a street address. + /// This can include a building name, suite, subunit, or place name. + /// + public string Address3 { get; set; } + + /// + /// Gets or sets the neighbourhood of the location. + /// + public string Neighbourhood { get; set; } + + /// + /// Gets or sets the city of the location. + /// + public string City { get; set; } + + /// + /// Gets or sets the subregion (county or equivalent) of the location. + /// + public string Subregion { get; set; } + + /// + /// Gets or sets the region (state, province, or equivalent) of the location. + /// + public string Region { get; set; } + + /// + /// Gets or sets the standard postal code for an address, typically a three- to six-digit alphanumeric code. + /// + public string Postal { get; set; } + + /// + /// Gets or sets a postal code extension, such as the United States Postal Service ZIP+4 code. + /// Provides finer resolution or higher accuracy when combined with . + /// + public string PostalExt { get; set; } + + /// + /// Gets or sets a value representing the country. Providing this value increases geocoding speed. + /// Acceptable values include the full country name, the two-character country code, or the three-character country code. + /// + public string CountryCode { get; set; } + + // ── Common optional parameters ─────────────────────────────────────── + + /// + /// Gets or sets a comma-separated list of attribute fields to include in the response. + /// Use * to return all fields. + /// + public string OutFields { get; set; } = "Match_addr,Addr_type"; + + /// + /// Gets or sets an origin point used to prefer or boost geocoding candidates based on their proximity to the location. + /// Candidates near the location are prioritised relative to those further away. + /// + public Coordinate Location { get; set; } + + /// + /// Gets or sets a place or address type that can be used to filter results. + /// The parameter supports a single category value or multiple comma-separated values. + /// + public string Category { get; set; } + + /// + /// Gets or sets a set of bounding box coordinates that limit the search area to a specific region. + /// + public BoundingBox SearchExtent { get; set; } + + /// + /// Gets or sets the maximum number of candidates to return. + /// Valid range is 1–50. A value of 0 (the default) means the parameter is not sent and the service default is used. + /// + public uint MaximumLocations { get; set; } = 0; + + /// + /// Gets or sets the spatial reference of the x/y coordinates returned by a geocode request. + /// For a list of valid WKID values, see + /// https://developers.arcgis.com/rest/services-reference/geographic-coordinate-systems.htm. + /// + public int OutSpatialReference { get; set; } + + /// + /// Gets or sets the language in which geocoded addresses are returned. + /// Addresses in many countries are available in more than one language. + /// See https://developers.arcgis.com/rest/geocode/api-reference/geocode-coverage.htm for valid language code values per country. + /// + public CultureInfo LanguageCode { get; set; } + + /// + /// Gets or sets a value that specifies whether the output geometry of PointAddress and Subaddress matches + /// should be the rooftop point or the street entrance location. + /// The default value is . + /// + public LocationType LocationType { get; set; } = LocationType.Rooftop; + + /// + /// Gets or sets a configuration of output fields returned in a response by specifying which address + /// component values should be included in output label fields. + /// The default value is . + /// + public PreferredLabelValue PreferredLabelValue { get; set; } = PreferredLabelValue.PostalCity; + + /// + /// Gets a list of countries to restrict geocoding results to. + /// When values are passed, all input addresses are geocoded within the specified countries only. + /// You can specify multiple country codes to limit results to more than one country. + /// + public IList SourceCountry { get; } = new List(); + + /// + public string ClientId { get; set; } + + /// + public string ClientSecret { get; set; } + } +} diff --git a/src/Geo.ArcGIS/Models/Parameters/GeocodingParameters.cs b/src/Geo.ArcGIS/Models/Parameters/GeocodingParameters.cs index 8a87e68..da8483e 100644 --- a/src/Geo.ArcGIS/Models/Parameters/GeocodingParameters.cs +++ b/src/Geo.ArcGIS/Models/Parameters/GeocodingParameters.cs @@ -74,6 +74,12 @@ public class GeocodingParameters : IClientCredentialsParameters /// public PreferredLabelValue PreferredLabelValue { get; set; } = PreferredLabelValue.PostalCity; + /// + /// Gets or sets a comma-separated list of attribute fields to include in the response. + /// Use * to return all fields. + /// + public string OutFields { get; set; } + /// public string ClientId { get; set; } diff --git a/src/Geo.ArcGIS/Models/Parameters/PlaceCandidateParameters.cs b/src/Geo.ArcGIS/Models/Parameters/PlaceCandidateParameters.cs index 6091cae..e2aa5d4 100644 --- a/src/Geo.ArcGIS/Models/Parameters/PlaceCandidateParameters.cs +++ b/src/Geo.ArcGIS/Models/Parameters/PlaceCandidateParameters.cs @@ -5,6 +5,7 @@ namespace Geo.ArcGIS.Models.Parameters { + using System; using System.Globalization; using Geo.ArcGIS.Enums; using Geo.ArcGIS.Models.Responses; @@ -12,6 +13,7 @@ namespace Geo.ArcGIS.Models.Parameters /// /// A parameters object for the place candidates ArcGIS request. /// + [Obsolete("Use FindAddressCandidatesParameters instead.")] public class PlaceCandidateParameters : StorageParameters, IClientCredentialsParameters { /// diff --git a/src/Geo.ArcGIS/Models/Responses/Address.cs b/src/Geo.ArcGIS/Models/Responses/Address.cs index a4144de..d01ff3e 100644 --- a/src/Geo.ArcGIS/Models/Responses/Address.cs +++ b/src/Geo.ArcGIS/Models/Responses/Address.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) Geo.NET. // Licensed under the MIT license. See the LICENSE file in the solution root for full license information. // @@ -8,7 +8,7 @@ namespace Geo.ArcGIS.Models.Responses using System.Text.Json.Serialization; /// - /// The address information. + /// The address information returned by a reverseGeocode request. /// public class Address { @@ -108,6 +108,12 @@ public class Address [JsonPropertyName("Region")] public string Region { get; set; } + /// + /// Gets or sets the abbreviated region (e.g. CA for California). + /// + [JsonPropertyName("RegionAbbr")] + public string RegionAbbreviation { get; set; } + /// /// Gets or sets the territory of the address. /// @@ -137,5 +143,41 @@ public class Address /// [JsonPropertyName("CountryCode")] public string CountryCode { get; set; } + + /// + /// Gets or sets the longitude of the matched location. + /// + [JsonPropertyName("X")] + public double Longitude { get; set; } + + /// + /// Gets or sets the latitude of the matched location. + /// + [JsonPropertyName("Y")] + public double Latitude { get; set; } + + /// + /// Gets or sets the longitude of the input point used for the reverse geocode. + /// + [JsonPropertyName("InputX")] + public double InputLongitude { get; set; } + + /// + /// Gets or sets the latitude of the input point used for the reverse geocode. + /// + [JsonPropertyName("InputY")] + public double InputLatitude { get; set; } + + /// + /// Gets or sets the structure type of the address. + /// + [JsonPropertyName("StrucType")] + public string StructureType { get; set; } + + /// + /// Gets or sets the structure details of the address. + /// + [JsonPropertyName("StrucDet")] + public string StructureDetails { get; set; } } } diff --git a/src/Geo.ArcGIS/Models/Responses/AddressAttribute.cs b/src/Geo.ArcGIS/Models/Responses/AddressAttribute.cs deleted file mode 100644 index 7bdeba9..0000000 --- a/src/Geo.ArcGIS/Models/Responses/AddressAttribute.cs +++ /dev/null @@ -1,27 +0,0 @@ -// -// Copyright (c) Geo.NET. -// Licensed under the MIT license. See the LICENSE file in the solution root for full license information. -// - -namespace Geo.ArcGIS.Models.Responses -{ - using System.Text.Json.Serialization; - - /// - /// Attribute information returned when searching for an address. - /// - public class AddressAttribute : Attribute - { - /// - /// Gets or sets the match address information. - /// - [JsonPropertyName("Match_addr")] - public string MatchAddress { get; set; } - - /// - /// Gets or sets the address type information. - /// - [JsonPropertyName("Addr_type")] - public string AddressType { get; set; } - } -} diff --git a/src/Geo.ArcGIS/Models/Responses/Attribute.cs b/src/Geo.ArcGIS/Models/Responses/Attribute.cs index 8f905c2..3407eb8 100644 --- a/src/Geo.ArcGIS/Models/Responses/Attribute.cs +++ b/src/Geo.ArcGIS/Models/Responses/Attribute.cs @@ -1,18 +1,440 @@ -// +// // Copyright (c) Geo.NET. // Licensed under the MIT license. See the LICENSE file in the solution root for full license information. // namespace Geo.ArcGIS.Models.Responses { + using System; using System.Text.Json.Serialization; - using Geo.ArcGIS.Converters; /// - /// A base attribute class. + /// The attributes returned for a geocoding candidate (address or place). + /// This single class covers all fields returned by the ArcGIS findAddressCandidates endpoint. /// - [JsonConverter(typeof(AttributeConverter))] public class Attribute { + /// + /// Gets or sets the id of the result. + /// + [JsonPropertyName("ResultID")] + public int ResultId { get; set; } + + /// + /// Gets or sets the match address. + /// + [JsonPropertyName("Match_addr")] + public string MatchAddress { get; set; } + + /// + /// Gets or sets the long label string for the address. + /// + [JsonPropertyName("LongLabel")] + public string LongLabel { get; set; } + + /// + /// Gets or sets the short label string for the address. + /// + [JsonPropertyName("ShortLabel")] + public string ShortLabel { get; set; } + + /// + /// Gets or sets the address type. + /// + [JsonPropertyName("Addr_type")] + public string AddressType { get; set; } + + /// + /// Gets or sets the type of address. + /// + [JsonPropertyName("Type")] + public string Type { get; set; } + + /// + /// Gets or sets the place name if the address has a name. + /// + [JsonPropertyName("PlaceName")] + public string PlaceName { get; set; } + + /// + /// Gets or sets the place address. + /// + [JsonPropertyName("Place_addr")] + public string PlaceAddress { get; set; } + + /// + /// Gets or sets the phone number of the location. + /// + [JsonPropertyName("Phone")] + public string Phone { get; set; } + + /// + /// Gets or sets the url of the location. + /// + [JsonPropertyName("URL")] + public Uri URL { get; set; } + + /// + /// Gets or sets the rank of the location. + /// + [JsonPropertyName("Rank")] + public float Rank { get; set; } + + /// + /// Gets or sets the additional building information for a location. + /// + [JsonPropertyName("AddBldg")] + public string AddBuilding { get; set; } + + /// + /// Gets or sets the additional number information for a building. + /// + [JsonPropertyName("AddNum")] + public string AdditionalNumber { get; set; } + + /// + /// Gets or sets the additional number from information for a location. + /// + [JsonPropertyName("AddNumFrom")] + public string AdditionalNumberFrom { get; set; } + + /// + /// Gets or sets the additional number to information for a location. + /// + [JsonPropertyName("AddNumTo")] + public string AdditionalNumberTo { get; set; } + + /// + /// Gets or sets the additional range for a location. + /// + [JsonPropertyName("AddRange")] + public string AddRange { get; set; } + + /// + /// Gets or sets the side information for a location. + /// + [JsonPropertyName("Side")] + public string Side { get; set; } + + /// + /// Gets or sets the street pre-direction for a location. + /// + [JsonPropertyName("StPreDir")] + public string StreetPreDirection { get; set; } + + /// + /// Gets or sets the street pre-type for a location. + /// + [JsonPropertyName("StPreType")] + public string StreetPreType { get; set; } + + /// + /// Gets or sets the street name a location is on. + /// + [JsonPropertyName("StName")] + public string StreetName { get; set; } + + /// + /// Gets or sets the street type a location is on. + /// + [JsonPropertyName("StType")] + public string StreetType { get; set; } + + /// + /// Gets or sets the street direction a location is on. + /// + [JsonPropertyName("StDir")] + public string StreetDirection { get; set; } + + /// + /// Gets or sets the building complex name of a location. + /// + [JsonPropertyName("BldgComp")] + public string BuildingComplex { get; set; } + + /// + /// Gets or sets the building type of a location. + /// + [JsonPropertyName("BldgType")] + public string BuildingType { get; set; } + + /// + /// Gets or sets the building name of a location. + /// + [JsonPropertyName("BldgName")] + public string BuildingName { get; set; } + + /// + /// Gets or sets the level type of a location. + /// + [JsonPropertyName("LevelType")] + public string LevelType { get; set; } + + /// + /// Gets or sets the level name of a location. + /// + [JsonPropertyName("LevelName")] + public string LevelName { get; set; } + + /// + /// Gets or sets the unit type of a location. + /// + [JsonPropertyName("UnitType")] + public string UnitType { get; set; } + + /// + /// Gets or sets the unit name of a location. + /// + [JsonPropertyName("UnitName")] + public string UnitName { get; set; } + + /// + /// Gets or sets the room type of a location. + /// + [JsonPropertyName("RoomType")] + public string RoomType { get; set; } + + /// + /// Gets or sets the room name/identifier of a location. + /// + [JsonPropertyName("RoomName")] + public string RoomName { get; set; } + + /// + /// Gets or sets the building wing type of a location. + /// + [JsonPropertyName("WingType")] + public string WingType { get; set; } + + /// + /// Gets or sets the building wing name of a location. + /// + [JsonPropertyName("WingName")] + public string WingName { get; set; } + + /// + /// Gets or sets the sub-address of a location. + /// + [JsonPropertyName("SubAddr")] + public string SubAddress { get; set; } + + /// + /// Gets or sets the street address of a location. + /// + [JsonPropertyName("StAddr")] + public string StreetAddress { get; set; } + + /// + /// Gets or sets the block name. + /// + [JsonPropertyName("Block")] + public string Block { get; set; } + + /// + /// Gets or sets the sector of the address. + /// + [JsonPropertyName("Sector")] + public string Sector { get; set; } + + /// + /// Gets or sets the neighbourhood of a location. + /// + [JsonPropertyName("Nbrhd")] + public string LocationNeighbourhood { get; set; } + + /// + /// Gets or sets the neighbourhood information for the address. + /// + [JsonPropertyName("Neighborhood")] + public string Neighbourhood { get; set; } + + /// + /// Gets or sets the district of the address. + /// + [JsonPropertyName("District")] + public string District { get; set; } + + /// + /// Gets or sets the city of the address. + /// + [JsonPropertyName("City")] + public string City { get; set; } + + /// + /// Gets or sets the metropolitian area of the address. + /// + [JsonPropertyName("MetroArea")] + public string MetropolitanArea { get; set; } + + /// + /// Gets or sets the sub-region of the address. + /// + [JsonPropertyName("Subregion")] + public string Subregion { get; set; } + + /// + /// Gets or sets the region of the address. + /// + [JsonPropertyName("Region")] + public string Region { get; set; } + + /// + /// Gets or sets the region abbreviation of a location. + /// + [JsonPropertyName("RegionAbbr")] + public string RegionAbbreviation { get; set; } + + /// + /// Gets or sets the territory of the address. + /// + [JsonPropertyName("Territory")] + public string Territory { get; set; } + + /// + /// Gets or sets the zone of a location. + /// + [JsonPropertyName("Zone")] + public string Zone { get; set; } + + /// + /// Gets or sets the postal code of the address. + /// + [JsonPropertyName("Postal")] + public string PostalCode { get; set; } + + /// + /// Gets or sets the postal code extension of the address. + /// + [JsonPropertyName("PostalExt")] + public string PostalCodeExtension { get; set; } + + /// + /// Gets or sets the country of a location. + /// + [JsonPropertyName("CntryName")] + public string Country { get; set; } + + /// + /// Gets or sets the country code of the address. + /// + [JsonPropertyName("CountryCode")] + public string CountryCode { get; set; } + + /// + /// Sets the country code by an alternate name. + /// + [JsonPropertyName("Country")] + public string AlternateCountryCode + { + set { CountryCode = value; } + } + + /// + /// Gets or sets the language code of a location. + /// + [JsonPropertyName("LangCode")] + public string LanguageCode { get; set; } + + /// + /// Gets or sets the distance to a location. + /// + [JsonPropertyName("Distance")] + public double Distance { get; set; } + + /// + /// Gets or sets the longitude of a location. + /// + [JsonPropertyName("X")] + public double Longitude { get; set; } + + /// + /// Gets or sets the latitude of a location. + /// + [JsonPropertyName("Y")] + public double Latitude { get; set; } + + /// + /// Gets or sets the display longitude of a location. + /// + [JsonPropertyName("DisplayX")] + public double DisplayLongitude { get; set; } + + /// + /// Gets or sets the display latitude of a location. + /// + [JsonPropertyName("DisplayY")] + public double DisplayLatitude { get; set; } + + /// + /// Gets or sets the west-most longitude of a location. + /// + [JsonPropertyName("Xmin")] + public double WestLongitude { get; set; } + + /// + /// Gets or sets the east-most longitude of a location. + /// + [JsonPropertyName("Xmax")] + public double EastLongitude { get; set; } + + /// + /// Gets or sets the south-most latitude of a location. + /// + [JsonPropertyName("Ymin")] + public double SouthLatitude { get; set; } + + /// + /// Gets or sets the north-most latitude of a location. + /// + [JsonPropertyName("Ymax")] + public double NorthLatitude { get; set; } + + /// + /// Gets or sets any extra information about a location. + /// + [JsonPropertyName("ExInfo")] + public string ExtraInformation { get; set; } + + /// + /// Gets or sets the internal match identifier. + /// + [JsonPropertyName("MatchID")] + public string MatchId { get; set; } + + /// + /// Gets or sets the internal potential match identifier. + /// + [JsonPropertyName("PotentialID")] + public string PotentialId { get; set; } + + /// + /// Gets or sets the structure type of a location. + /// + [JsonPropertyName("StrucType")] + public string StructureType { get; set; } + + /// + /// Gets or sets the structure details of a location. + /// + [JsonPropertyName("StrucDet")] + public string StructureDetails { get; set; } + + /// + /// Gets or sets the location name. + /// + [JsonPropertyName("Loc_name")] + public string LocationName { get; set; } + + /// + /// Gets or sets the status of the result. + /// + [JsonPropertyName("Status")] + public string Status { get; set; } + + /// + /// Gets or sets the score of the result. + /// + [JsonPropertyName("Score")] + public double Score { get; set; } } } diff --git a/src/Geo.ArcGIS/Models/Responses/LocationAttribute.cs b/src/Geo.ArcGIS/Models/Responses/LocationAttribute.cs deleted file mode 100644 index 56bfc34..0000000 --- a/src/Geo.ArcGIS/Models/Responses/LocationAttribute.cs +++ /dev/null @@ -1,385 +0,0 @@ -// -// Copyright (c) Geo.NET. -// Licensed under the MIT license. See the LICENSE file in the solution root for full license information. -// - -namespace Geo.ArcGIS.Models.Responses -{ - using System; - using System.Text.Json.Serialization; - - /// - /// The location attributes. - /// - public class LocationAttribute : Attribute - { - /// - /// Gets or sets the id of the result. - /// - [JsonPropertyName("ResultID")] - public int ResultId { get; set; } - - /// - /// Gets or sets the match address. - /// - [JsonPropertyName("Match_addr")] - public string MatchAddress { get; set; } - - /// - /// Gets or sets the long label string for the address. - /// - [JsonPropertyName("LongLabel")] - public string LongLabel { get; set; } - - /// - /// Gets or sets the short label string for the address. - /// - [JsonPropertyName("ShortLabel")] - public string ShortLabel { get; set; } - - /// - /// Gets or sets the address type. - /// - [JsonPropertyName("Addr_type")] - public string AddressType { get; set; } - - /// - /// Gets or sets the type of address. - /// - [JsonPropertyName("Type")] - public string Type { get; set; } - - /// - /// Gets or sets the place name if the address has a name. - /// - [JsonPropertyName("PlaceName")] - public string PlaceName { get; set; } - - /// - /// Gets or sets the block name. - /// - [JsonPropertyName("Block")] - public string Block { get; set; } - - /// - /// Gets or sets the sector of the address. - /// - [JsonPropertyName("Sector")] - public string Sector { get; set; } - - /// - /// Gets or sets the neighbourhood information for the address. - /// - [JsonPropertyName("Neighborhood")] - public string Neighbourhood { get; set; } - - /// - /// Gets or sets the district of the address. - /// - [JsonPropertyName("District")] - public string District { get; set; } - - /// - /// Gets or sets the city of the address. - /// - [JsonPropertyName("City")] - public string City { get; set; } - - /// - /// Gets or sets the metropolitian area of the address. - /// - [JsonPropertyName("MetroArea")] - public string MetropolitanArea { get; set; } - - /// - /// Gets or sets the sub-region of the address. - /// - [JsonPropertyName("Subregion")] - public string Subregion { get; set; } - - /// - /// Gets or sets the region of the address. - /// - [JsonPropertyName("Region")] - public string Region { get; set; } - - /// - /// Gets or sets the territory of the address. - /// - [JsonPropertyName("Territory")] - public string Territory { get; set; } - - /// - /// Gets or sets the postal code of the address. - /// - [JsonPropertyName("Postal")] - public string PostalCode { get; set; } - - /// - /// Gets or sets the postal code extension of the address. - /// - [JsonPropertyName("PostalExt")] - public string PostalCodeExtension { get; set; } - - /// - /// Gets or sets the country code of the address. - /// - [JsonPropertyName("CountryCode")] - public string CountryCode { get; set; } - - /// - /// Sets the country code by an alternate name. - /// - [JsonPropertyName("Country")] - public string AlternateCountryCode - { - set { CountryCode = value; } - } - - /// - /// Gets or sets the location name. - /// - [JsonPropertyName("Loc_name")] - public string LocationName { get; set; } - - /// - /// Gets or sets the status of the result. - /// - [JsonPropertyName("Status")] - public string Status { get; set; } - - /// - /// Gets or sets the score of the result. - /// - [JsonPropertyName("Score")] - public double Score { get; set; } - - /// - /// Gets or sets the place address. - /// - [JsonPropertyName("Place_addr")] - public string PlaceAddress { get; set; } - - /// - /// Gets or sets the phone number of the location. - /// - [JsonPropertyName("Phone")] - public string Phone { get; set; } - - /// - /// Gets or sets the url of the location. - /// - [JsonPropertyName("URL")] - public Uri URL { get; set; } - - /// - /// Gets or sets the rank of the location. - /// - [JsonPropertyName("Rank")] - public float Rank { get; set; } - - /// - /// Gets or sets the additional building information for a location. - /// - [JsonPropertyName("AddBldg")] - public string AddBuilding { get; set; } - - /// - /// Gets or sets the additional number information for a building. - /// - [JsonPropertyName("AddNum")] - public string AdditionalNumber { get; set; } - - /// - /// Gets or sets the additional number from information for a location. - /// - [JsonPropertyName("AddNumFrom")] - public string AdditionalNumberFrom { get; set; } - - /// - /// Gets or sets the additional number to information for a location. - /// - [JsonPropertyName("AddNumTo")] - public string AdditionalNumberTo { get; set; } - - /// - /// Gets or sets the additional range for a location. - /// - [JsonPropertyName("AddRange")] - public string AddRange { get; set; } - - /// - /// Gets or sets the side information for a location. - /// - [JsonPropertyName("Side")] - public string Side { get; set; } - - /// - /// Gets or sets the street pre-direction for a location. - /// - [JsonPropertyName("StPreDir")] - public string StreetPreDirection { get; set; } - - /// - /// Gets or sets the street pre-type for a location. - /// - [JsonPropertyName("StPreType")] - public string StreetPreType { get; set; } - - /// - /// Gets or sets the street name a location is on. - /// - [JsonPropertyName("StName")] - public string StreetName { get; set; } - - /// - /// Gets or sets the street type a location is on. - /// - [JsonPropertyName("StType")] - public string StreetType { get; set; } - - /// - /// Gets or sets the street direction a location is on. - /// - [JsonPropertyName("StDir")] - public string StreetDirection { get; set; } - - /// - /// Gets or sets the building type of a location. - /// - [JsonPropertyName("BldgType")] - public string BuildingType { get; set; } - - /// - /// Gets or sets the building name of a location. - /// - [JsonPropertyName("BldgName")] - public string BuildingName { get; set; } - - /// - /// Gets or sets the level type of a location. - /// - [JsonPropertyName("LevelType")] - public string LevelType { get; set; } - - /// - /// Gets or sets the level name of a location. - /// - [JsonPropertyName("LevelName")] - public string LevelName { get; set; } - - /// - /// Gets or sets the unit type of a location. - /// - [JsonPropertyName("UnitType")] - public string UnitType { get; set; } - - /// - /// Gets or sets the unit name of a location. - /// - [JsonPropertyName("UnitName")] - public string UnitName { get; set; } - - /// - /// Gets or sets the sub-address of a location. - /// - [JsonPropertyName("SubAddr")] - public string SubAddress { get; set; } - - /// - /// Gets or sets the street address of a location. - /// - [JsonPropertyName("StAddr")] - public string StreetAddress { get; set; } - - /// - /// Gets or sets the neighbourhood of a location. - /// - [JsonPropertyName("Nbrhd")] - public string LocationNeighbourhood { get; set; } - - /// - /// Gets or sets the region abbreviation of a location. - /// - [JsonPropertyName("RegionAbbr")] - public string RegionAbbreviation { get; set; } - - /// - /// Gets or sets the zone of a location. - /// - [JsonPropertyName("Zone")] - public string Zone { get; set; } - - /// - /// Gets or sets the country of a location. - /// - [JsonPropertyName("CntryName")] - public string Country { get; set; } - - /// - /// Gets or sets the language code of a location. - /// - [JsonPropertyName("LangCode")] - public string LanguageCode { get; set; } - - /// - /// Gets or sets the distance to a location. - /// - [JsonPropertyName("Distance")] - public double Distance { get; set; } - - /// - /// Gets or sets the longitude of a location. - /// - [JsonPropertyName("X")] - public double Longitude { get; set; } - - /// - /// Gets or sets the latitude of a location. - /// - [JsonPropertyName("Y")] - public double Latitude { get; set; } - - /// - /// Gets or sets the display longitude of a location. - /// - [JsonPropertyName("DisplayX")] - public double DisplayLongitude { get; set; } - - /// - /// Gets or sets the display latitude of a location. - /// - [JsonPropertyName("DisplayY")] - public double DisplayLatitude { get; set; } - - /// - /// Gets or sets the west-most longitude of a location. - /// - [JsonPropertyName("Xmin")] - public double WestLongitude { get; set; } - - /// - /// Gets or sets the east-most longitude of a location. - /// - [JsonPropertyName("Xmax")] - public double EastLongitude { get; set; } - - /// - /// Gets or sets the south-most latitude of a location. - /// - [JsonPropertyName("Ymin")] - public double SouthLatitude { get; set; } - - /// - /// Gets or sets the north-most latitude of a location. - /// - [JsonPropertyName("Ymax")] - public double NorthLatitude { get; set; } - - /// - /// Gets or sets any extra information about a location. - /// - [JsonPropertyName("ExInfo")] - public string ExtraInformation { get; set; } - } -} diff --git a/src/Geo.ArcGIS/Models/Responses/PlaceAttribute.cs b/src/Geo.ArcGIS/Models/Responses/PlaceAttribute.cs deleted file mode 100644 index 7a94860..0000000 --- a/src/Geo.ArcGIS/Models/Responses/PlaceAttribute.cs +++ /dev/null @@ -1,27 +0,0 @@ -// -// Copyright (c) Geo.NET. -// Licensed under the MIT license. See the LICENSE file in the solution root for full license information. -// - -namespace Geo.ArcGIS.Models.Responses -{ - using System.Text.Json.Serialization; - - /// - /// Attribute information returned when searching for an place. - /// - public class PlaceAttribute : Attribute - { - /// - /// Gets or sets the place address information. - /// - [JsonPropertyName("Place_addr")] - public string PlaceAddress { get; set; } - - /// - /// Gets or sets the place name information. - /// - [JsonPropertyName("PlaceName")] - public string PlaceName { get; set; } - } -} diff --git a/src/Geo.ArcGIS/Resources/Services/ArcGISGeocoding.Designer.cs b/src/Geo.ArcGIS/Resources/Services/ArcGISGeocoding.Designer.cs index 08ff156..8e8435f 100644 --- a/src/Geo.ArcGIS/Resources/Services/ArcGISGeocoding.Designer.cs +++ b/src/Geo.ArcGIS/Resources/Services/ArcGISGeocoding.Designer.cs @@ -248,7 +248,16 @@ internal static string Invalid_Search_Extent { return ResourceManager.GetString("Invalid Search Extent", resourceCulture); } } - + + /// + /// Looks up a localized string similar to Either a single-line address or at least one structured address field must be provided.. + /// + internal static string Invalid_Find_Address_Candidates { + get { + return ResourceManager.GetString("Invalid Find Address Candidates", resourceCulture); + } + } + /// /// Looks up a localized string similar to The single line address cannot be null or empty.. /// diff --git a/src/Geo.ArcGIS/Resources/Services/ArcGISGeocoding.en.resx b/src/Geo.ArcGIS/Resources/Services/ArcGISGeocoding.en.resx index 593b5f6..1a9e276 100644 --- a/src/Geo.ArcGIS/Resources/Services/ArcGISGeocoding.en.resx +++ b/src/Geo.ArcGIS/Resources/Services/ArcGISGeocoding.en.resx @@ -180,6 +180,9 @@ The search extent is invalid and will not be used. + + Either a single-line address or at least one structured address field must be provided. + The single line address cannot be null or empty. @@ -195,4 +198,4 @@ The ArcGIS parameters are null. - \ No newline at end of file + diff --git a/src/Geo.ArcGIS/Resources/Services/ArcGISGeocoding.resx b/src/Geo.ArcGIS/Resources/Services/ArcGISGeocoding.resx index 593b5f6..1a9e276 100644 --- a/src/Geo.ArcGIS/Resources/Services/ArcGISGeocoding.resx +++ b/src/Geo.ArcGIS/Resources/Services/ArcGISGeocoding.resx @@ -180,6 +180,9 @@ The search extent is invalid and will not be used. + + Either a single-line address or at least one structured address field must be provided. + The single line address cannot be null or empty. @@ -195,4 +198,4 @@ The ArcGIS parameters are null. - \ No newline at end of file + diff --git a/src/Geo.ArcGIS/Services/ArcGISGeocoding.cs b/src/Geo.ArcGIS/Services/ArcGISGeocoding.cs index e83f3c9..c113377 100644 --- a/src/Geo.ArcGIS/Services/ArcGISGeocoding.cs +++ b/src/Geo.ArcGIS/Services/ArcGISGeocoding.cs @@ -61,6 +61,18 @@ public ArcGISGeocoding( protected override string ApiName => "ArcGIS"; /// + public async Task FindAddressCandidatesAsync( + FindAddressCandidatesParameters parameters, + CancellationToken cancellationToken = default) + { + var uri = await ValidateAndBuildUri(parameters, BuildFindAddressCandidatesRequest, cancellationToken).ConfigureAwait(false); + + return await GetAsync(uri, cancellationToken).ConfigureAwait(false); + } + + /// +#pragma warning disable CS0618 + [Obsolete("Use FindAddressCandidatesAsync instead.")] public async Task AddressCandidateAsync( AddressCandidateParameters parameters, CancellationToken cancellationToken = default) @@ -69,8 +81,11 @@ public async Task AddressCandidateAsync( return await GetAsync(uri, cancellationToken).ConfigureAwait(false); } +#pragma warning restore CS0618 /// +#pragma warning disable CS0618 + [Obsolete("Use FindAddressCandidatesAsync instead.")] public async Task PlaceCandidateAsync( PlaceCandidateParameters parameters, CancellationToken cancellationToken = default) @@ -79,6 +94,7 @@ public async Task PlaceCandidateAsync( return await GetAsync(uri, cancellationToken).ConfigureAwait(false); } +#pragma warning restore CS0618 /// public async Task SuggestAsync( @@ -153,13 +169,235 @@ internal async Task ValidateAndBuildUri( } } + /// + /// Builds the findAddressCandidates uri based on the passed parameters. + /// Supports both single-line and structured (multi-field) address input. + /// + /// A with the parameters to build the uri with. + /// A used to cancel the request. + /// A with the completed ArcGIS geocoding uri. + internal async Task BuildFindAddressCandidatesRequest(FindAddressCandidatesParameters parameters, CancellationToken cancellationToken) + { + var uriBuilder = new UriBuilder(CandidatesUri); + var query = QueryString.Empty; + query = query.Add("f", "json"); + query = query.Add("outFields", parameters.OutFields); + + if (!string.IsNullOrWhiteSpace(parameters.SingleLineAddress)) + { + query = query.Add("singleLine", parameters.SingleLineAddress); + + if (!string.IsNullOrWhiteSpace(parameters.MagicKey)) + { + query = query.Add("magicKey", parameters.MagicKey); + } + } + else if (!string.IsNullOrWhiteSpace(parameters.Address) || + !string.IsNullOrWhiteSpace(parameters.Address2) || + !string.IsNullOrWhiteSpace(parameters.Address3) || + !string.IsNullOrWhiteSpace(parameters.Neighbourhood) || + !string.IsNullOrWhiteSpace(parameters.City) || + !string.IsNullOrWhiteSpace(parameters.Subregion) || + !string.IsNullOrWhiteSpace(parameters.Region) || + !string.IsNullOrWhiteSpace(parameters.Postal) || + !string.IsNullOrWhiteSpace(parameters.PostalExt) || + !string.IsNullOrWhiteSpace(parameters.CountryCode)) + { + if (!string.IsNullOrWhiteSpace(parameters.Address)) + { + query = query.Add("address", parameters.Address); + } + else + { + _logger.ArcGISDebug(Resources.Services.ArcGISGeocoding.Invalid_Address); + } + + if (!string.IsNullOrWhiteSpace(parameters.Address2)) + { + query = query.Add("address2", parameters.Address2); + } + else + { + _logger.ArcGISDebug(Resources.Services.ArcGISGeocoding.Invalid_Address2); + } + + if (!string.IsNullOrWhiteSpace(parameters.Address3)) + { + query = query.Add("address3", parameters.Address3); + } + else + { + _logger.ArcGISDebug(Resources.Services.ArcGISGeocoding.Invalid_Address3); + } + + if (!string.IsNullOrWhiteSpace(parameters.Neighbourhood)) + { + query = query.Add("neighborhood", parameters.Neighbourhood); + } + else + { + _logger.ArcGISDebug(Resources.Services.ArcGISGeocoding.Invalid_Neighbourhood); + } + + if (!string.IsNullOrWhiteSpace(parameters.City)) + { + query = query.Add("city", parameters.City); + } + else + { + _logger.ArcGISDebug(Resources.Services.ArcGISGeocoding.Invalid_City); + } + + if (!string.IsNullOrWhiteSpace(parameters.Subregion)) + { + query = query.Add("subregion", parameters.Subregion); + } + else + { + _logger.ArcGISDebug(Resources.Services.ArcGISGeocoding.Invalid_Subregion); + } + + if (!string.IsNullOrWhiteSpace(parameters.Region)) + { + query = query.Add("region", parameters.Region); + } + else + { + _logger.ArcGISDebug(Resources.Services.ArcGISGeocoding.Invalid_Region); + } + + if (!string.IsNullOrWhiteSpace(parameters.Postal)) + { + query = query.Add("postal", parameters.Postal); + } + else + { + _logger.ArcGISDebug(Resources.Services.ArcGISGeocoding.Invalid_Postal); + } + + if (!string.IsNullOrWhiteSpace(parameters.PostalExt)) + { + query = query.Add("postalExt", parameters.PostalExt); + } + else + { + _logger.ArcGISDebug(Resources.Services.ArcGISGeocoding.Invalid_PostalExt); + } + + if (!string.IsNullOrWhiteSpace(parameters.CountryCode)) + { + query = query.Add("countryCode", parameters.CountryCode); + } + else + { + _logger.ArcGISDebug(Resources.Services.ArcGISGeocoding.Invalid_Country_Code); + } + } + else + { + _logger.ArcGISError(Resources.Services.ArcGISGeocoding.Invalid_Find_Address_Candidates); + throw new ArgumentException(Resources.Services.ArcGISGeocoding.Invalid_Find_Address_Candidates, nameof(parameters.SingleLineAddress)); + } + + if (!string.IsNullOrWhiteSpace(parameters.Category)) + { + query = query.Add("category", parameters.Category); + } + else + { + _logger.ArcGISDebug(Resources.Services.ArcGISGeocoding.Invalid_Category); + } + + if (parameters.Location != null) + { + query = query.Add("location", parameters.Location.ToString()); + } + else + { + _logger.ArcGISDebug(Resources.Services.ArcGISGeocoding.Invalid_Location); + } + + if (parameters.SearchExtent != null) + { + query = query.Add("searchExtent", parameters.SearchExtent.ToString()); + } + else + { + _logger.ArcGISDebug(Resources.Services.ArcGISGeocoding.Invalid_Search_Extent); + } + + if (parameters.MaximumLocations > 0 && parameters.MaximumLocations <= 50) + { + query = query.Add("maxLocations", parameters.MaximumLocations.ToString(CultureInfo.InvariantCulture)); + } + else + { + _logger.ArcGISWarning(Resources.Services.ArcGISGeocoding.Invalid_Maximum_Locations); + } + + if (parameters.OutSpatialReference > 0) + { + query = query.Add("outSR", parameters.OutSpatialReference.ToString(CultureInfo.InvariantCulture)); + } + else + { + _logger.ArcGISDebug(Resources.Services.ArcGISGeocoding.Invalid_Out_Spatial_Reference); + } + + if (parameters.LanguageCode != null) + { + query = query.Add("langCode", parameters.LanguageCode.TwoLetterISOLanguageName); + } + else + { + _logger.ArcGISDebug(Resources.Services.ArcGISGeocoding.Invalid_Language_Code); + } + + if (parameters.LocationType >= 0) + { + query = query.Add("locationType", parameters.LocationType.ToEnumString()); + } + else + { + _logger.ArcGISWarning(Resources.Services.ArcGISGeocoding.Invalid_Location_Type); + } + + if (parameters.PreferredLabelValue >= 0) + { + query = query.Add("preferredLabelValues", parameters.PreferredLabelValue.ToEnumString()); + } + else + { + _logger.ArcGISWarning(Resources.Services.ArcGISGeocoding.Invalid_Preferred_Label_Value); + } + + if (parameters.SourceCountry.Count != 0) + { + query = query.Add("sourceCountry", string.Join(",", parameters.SourceCountry.Select(x => x.ThreeLetterISORegionName))); + } + else + { + _logger.ArcGISDebug(Resources.Services.ArcGISGeocoding.Invalid_Source_Country); + } + + AddStorageParameter(parameters, ref query); + + query = await AddArcGISToken(parameters, query, cancellationToken).ConfigureAwait(false); + + uriBuilder.AddQuery(query); + + return uriBuilder.Uri; + } + /// /// Builds the address candidate uri based on the passed parameters. /// /// A with the address candidate parameters to build the uri with. /// A used to cancel the request. /// A with the completed ArcGIS geocoding uri. +#pragma warning disable CS0618 internal async Task BuildAddressCandidateRequest(AddressCandidateParameters parameters, CancellationToken cancellationToken) +#pragma warning restore CS0618 { if (string.IsNullOrWhiteSpace(parameters.SingleLineAddress)) { @@ -194,7 +432,9 @@ internal async Task BuildAddressCandidateRequest(AddressCandidateParameters /// A with the place candidate parameters to build the uri with. /// A used to cancel the request. /// A with the completed ArcGIS geocoding uri. +#pragma warning disable CS0618 internal async Task BuildPlaceCandidateRequest(PlaceCandidateParameters parameters, CancellationToken cancellationToken) +#pragma warning restore CS0618 { var uriBuilder = new UriBuilder(CandidatesUri); var query = QueryString.Empty; @@ -575,6 +815,11 @@ internal async Task BuildGeocodingRequest(GeocodingParameters parameters, C query = query.Add("matchOutOfRange", parameters.MatchOutOfRange.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()); #pragma warning restore CA1308 // Normalize strings to uppercase + if (!string.IsNullOrWhiteSpace(parameters.OutFields)) + { + query = query.Add("outFields", parameters.OutFields); + } + if (!string.IsNullOrWhiteSpace(parameters.Category)) { query = query.Add("category", parameters.Category); diff --git a/test/Geo.ArcGIS.Tests/Converters/AttributeConverterShould.cs b/test/Geo.ArcGIS.Tests/Converters/AttributeConverterShould.cs deleted file mode 100644 index 07e86aa..0000000 --- a/test/Geo.ArcGIS.Tests/Converters/AttributeConverterShould.cs +++ /dev/null @@ -1,56 +0,0 @@ -// -// Copyright (c) Geo.NET. -// Licensed under the MIT license. See the LICENSE file in the solution root for full license information. -// - -namespace Geo.ArcGIS.Tests.Converters -{ - using System.Text.Json; - using FluentAssertions; - using Geo.ArcGIS.Converters; - using Geo.ArcGIS.Models.Responses; - using Geo.ArcGIS.Tests.Models; - using Xunit; - - /// - /// Unit tests for the class. - /// - public class AttributeConverterShould - { - /// - /// Tests the address attribute is correctly parsed. - /// - [Fact] - public void CorrectlyParseAddressAttribute() - { - var obj = JsonSerializer.Deserialize("{\"Attribute\":{\"Match_addr\":\"123 East\",\"Addr_type\":\"POI\"}}"); - obj.Attribute.GetType().Should().Be(typeof(AddressAttribute)); - ((AddressAttribute)obj.Attribute).MatchAddress.Should().Be("123 East"); - ((AddressAttribute)obj.Attribute).AddressType.Should().Be("POI"); - } - - /// - /// Tests the location attribute is correctly parsed. - /// - [Fact] - public void CorrectlyParseLocationAttribute() - { - var obj = JsonSerializer.Deserialize("{\"Attribute\":{\"ResultID\":123,\"LongLabel\":\"123 East\"}}"); - obj.Attribute.GetType().Should().Be(typeof(LocationAttribute)); - ((LocationAttribute)obj.Attribute).ResultId.Should().Be(123); - ((LocationAttribute)obj.Attribute).LongLabel.Should().Be("123 East"); - } - - /// - /// Tests the place attribute is correctly parsed. - /// - [Fact] - public void CorrectlyParsePlaceAttribute() - { - var obj = JsonSerializer.Deserialize("{\"Attribute\":{\"Place_addr\":\"123 East\",\"PlaceName\":\"East Side Company\"}}"); - obj.Attribute.GetType().Should().Be(typeof(PlaceAttribute)); - ((PlaceAttribute)obj.Attribute).PlaceAddress.Should().Be("123 East"); - ((PlaceAttribute)obj.Attribute).PlaceName.Should().Be("East Side Company"); - } - } -} \ No newline at end of file diff --git a/test/Geo.ArcGIS.Tests/Models/AttributeObject.cs b/test/Geo.ArcGIS.Tests/Models/AttributeObject.cs deleted file mode 100644 index 954c57b..0000000 --- a/test/Geo.ArcGIS.Tests/Models/AttributeObject.cs +++ /dev/null @@ -1,14 +0,0 @@ -// -// Copyright (c) Geo.NET. -// Licensed under the MIT license. See the LICENSE file in the solution root for full license information. -// - -namespace Geo.ArcGIS.Tests.Models -{ - using Geo.ArcGIS.Models.Responses; - - public class AttributeObject - { - public Attribute Attribute { get; set; } - } -} diff --git a/test/Geo.ArcGIS.Tests/Services/ArcGISGeocodingShould.cs b/test/Geo.ArcGIS.Tests/Services/ArcGISGeocodingShould.cs index 0b91810..2c05353 100644 --- a/test/Geo.ArcGIS.Tests/Services/ArcGISGeocodingShould.cs +++ b/test/Geo.ArcGIS.Tests/Services/ArcGISGeocodingShould.cs @@ -321,6 +321,144 @@ public async Task BuildPlaceCandidateRequestSuccessfully(CultureInfo culture) Thread.CurrentThread.CurrentCulture = oldCulture; } + /// + /// Tests the findAddressCandidates uri is built properly using a single-line address. + /// + /// The culture to set the current running thread to. + /// A with the results. + [Theory] + [ClassData(typeof(CultureTestData))] + public async Task BuildFindAddressCandidatesRequestWithSingleLineSuccessfully(CultureInfo culture) + { + // Arrange + var oldCulture = Thread.CurrentThread.CurrentCulture; + Thread.CurrentThread.CurrentCulture = culture; + + var sut = BuildService(); + + var parameters = new FindAddressCandidatesParameters() + { + SingleLineAddress = "123 East", + MagicKey = "myMagicKey", + Location = new Coordinate() + { + Latitude = 56.789, + Longitude = 123.456, + }, + SearchExtent = new BoundingBox() + { + EastLongitude = 123.456, + WestLongitude = 121.323, + NorthLatitude = 67.89, + SouthLatitude = 65.432, + }, + MaximumLocations = 5, + OutSpatialReference = 12345, + LanguageCode = new CultureInfo("en-CA"), + ForStorage = true, + }; + + parameters.SourceCountry.Add(new RegionInfo("FR")); + + // Act + var uri = await sut.BuildFindAddressCandidatesRequest(parameters, CancellationToken.None); + + // Assert + var query = HttpUtility.UrlDecode(uri.PathAndQuery); + query.Should().Contain("f=json"); + query.Should().Contain("outFields=Match_addr,Addr_type"); + query.Should().Contain("singleLine=123 East"); + query.Should().Contain("magicKey=myMagicKey"); + query.Should().Contain("location=123.456,56.789"); + query.Should().Contain("searchExtent=121.323,67.89,123.456,65.432"); + query.Should().Contain("maxLocations=5"); + query.Should().Contain("outSR=12345"); + query.Should().Contain("langCode=en"); + query.Should().Contain("locationType=rooftop"); + query.Should().Contain("preferredLabelValues=postalCity"); + query.Should().Contain("sourceCountry=FRA"); + query.Should().Contain("forStorage=true"); + query.Should().Contain("token=token123"); + + Thread.CurrentThread.CurrentCulture = oldCulture; + } + + /// + /// Tests the findAddressCandidates uri is built properly using structured address fields. + /// + /// The culture to set the current running thread to. + /// A with the results. + [Theory] + [ClassData(typeof(CultureTestData))] + public async Task BuildFindAddressCandidatesRequestWithStructuredAddressSuccessfully(CultureInfo culture) + { + // Arrange + var oldCulture = Thread.CurrentThread.CurrentCulture; + Thread.CurrentThread.CurrentCulture = culture; + + var sut = BuildService(); + + var parameters = new FindAddressCandidatesParameters() + { + Address = "123 East", + Address2 = "Suite 100", + Address3 = "Building C", + Neighbourhood = "Downtown", + City = "Eastville", + Subregion = "East County", + Region = "East State", + Postal = "12345", + PostalExt = "6789", + CountryCode = "USA", + Category = "restaurant", + OutSpatialReference = 12345, + ForStorage = false, + }; + + // Act + var uri = await sut.BuildFindAddressCandidatesRequest(parameters, CancellationToken.None); + + // Assert + var query = HttpUtility.UrlDecode(uri.PathAndQuery); + query.Should().Contain("f=json"); + query.Should().Contain("address=123 East"); + query.Should().Contain("address2=Suite 100"); + query.Should().Contain("address3=Building C"); + query.Should().Contain("neighborhood=Downtown"); + query.Should().Contain("city=Eastville"); + query.Should().Contain("subregion=East County"); + query.Should().Contain("region=East State"); + query.Should().Contain("postal=12345"); + query.Should().Contain("postalExt=6789"); + query.Should().Contain("countryCode=USA"); + query.Should().Contain("category=restaurant"); + query.Should().Contain("outSR=12345"); + query.Should().Contain("forStorage=false"); + query.Should().Contain("token=token123"); + query.Should().NotContain("singleLine"); + + Thread.CurrentThread.CurrentCulture = oldCulture; + } + + /// + /// Tests the findAddressCandidates uri isn't built if neither a single-line address nor any structured address field is provided. + /// + [Fact] + public void BuildFindAddressCandidatesRequestWithException() + { + var sut = BuildService(); + + Action act = () => sut.BuildFindAddressCandidatesRequest(new FindAddressCandidatesParameters(), CancellationToken.None).GetAwaiter().GetResult(); + + act.Should() + .Throw() +#if NETCOREAPP3_1_OR_GREATER + .WithMessage("*(Parameter 'SingleLineAddress')"); +#else + .WithMessage("*Parameter name: SingleLineAddress"); +#endif + } + /// /// Tests the suggest uri is built properly. /// @@ -537,6 +675,33 @@ public void BuildGeocodingRequestWithException() #endif } + /// + /// Tests the geocoding uri includes outFields when the parameter is set. + /// + /// A with the results. + [Fact] + public async Task BuildGeocodingRequestWithOutFieldsSuccessfully() + { + var sut = BuildService(); + + var parameters = new GeocodingParameters() + { + OutFields = "*", + }; + + parameters.AddressAttributes.Add( + new AddressAttributeParameter() + { + ObjectId = 1, + SingleLine = "123 East", + }); + + var uri = await sut.BuildGeocodingRequest(parameters, CancellationToken.None); + + var query = HttpUtility.UrlDecode(uri.PathAndQuery); + query.Should().Contain("outFields=*"); + } + /// /// Tests the geocoding returns a response successfully. /// @@ -640,8 +805,50 @@ public async Task AddressCandidateAsyncSuccessfully() response.Candidates.Count.Should().Be(1); response.SpatialReference.WellKnownID.Should().Be(4326); response.Candidates[0].Attributes.Should().NotBeNull(); - (response.Candidates[0].Attributes as LocationAttribute).Country.Should().Be("United States of America"); - (response.Candidates[0].Attributes as LocationAttribute).CountryCode.Should().Be("USA"); + response.Candidates[0].Attributes.Country.Should().Be("United States of America"); + response.Candidates[0].Attributes.CountryCode.Should().Be("USA"); + } + + /// + /// Tests the findAddressCandidates returns a response successfully using a single-line address. + /// + /// A . + [Fact] + public async Task FindAddressCandidatesAsyncWithSingleLineSuccessfully() + { + var sut = BuildService(); + + var parameters = new FindAddressCandidatesParameters() + { + SingleLineAddress = "123 East", + }; + + var response = await sut.FindAddressCandidatesAsync(parameters); + response.Candidates.Count.Should().Be(1); + response.SpatialReference.WellKnownID.Should().Be(4326); + response.Candidates[0].Attributes.Should().NotBeNull(); + response.Candidates[0].Attributes.Country.Should().Be("United States of America"); + response.Candidates[0].Attributes.CountryCode.Should().Be("USA"); + } + + /// + /// Tests the findAddressCandidates returns a response successfully using structured address fields. + /// + /// A . + [Fact] + public async Task FindAddressCandidatesAsyncWithStructuredAddressSuccessfully() + { + var sut = BuildService(); + + var parameters = new FindAddressCandidatesParameters() + { + City = "Eastville", + Category = "restaurants", + }; + + var response = await sut.FindAddressCandidatesAsync(parameters); + response.Candidates.Count.Should().Be(0); + response.SpatialReference.WellKnownID.Should().Be(4326); } /// @@ -673,4 +880,4 @@ private ArcGISGeocoding BuildService() return new ArcGISGeocoding(_httpClient, _tokenProvider.Object, _options.Object); } } -} \ No newline at end of file +} diff --git a/test/Geo.ArcGIS.Tests/TestData/CultureTestData.cs b/test/Geo.ArcGIS.Tests/TestData/CultureTestData.cs index 0503119..f70f54f 100644 --- a/test/Geo.ArcGIS.Tests/TestData/CultureTestData.cs +++ b/test/Geo.ArcGIS.Tests/TestData/CultureTestData.cs @@ -10,7 +10,9 @@ namespace Geo.ArcGIS.Tests using System.Globalization; /// - /// Test data when testing different cultures. This test data returns all cultures in dotnet. + /// Test data when testing different cultures. This test data returns a representative set of cultures + /// to test culture-invariant behaviour without running tests for every culture in dotnet. + /// Covers: invariant, period-decimal (en-US), comma-decimal (de-DE, fr-FR, ru-RU), Arabic, and Chinese. /// public class CultureTestData : IEnumerable { @@ -20,11 +22,13 @@ public class CultureTestData : IEnumerable /// An of []. public IEnumerator GetEnumerator() { - var cultures = CultureInfo.GetCultures(CultureTypes.AllCultures); - foreach (var culture in cultures) - { - yield return new object[] { culture }; - } + yield return new object[] { CultureInfo.InvariantCulture }; + yield return new object[] { new CultureInfo("en-US") }; + yield return new object[] { new CultureInfo("de-DE") }; + yield return new object[] { new CultureInfo("fr-FR") }; + yield return new object[] { new CultureInfo("ar-SA") }; + yield return new object[] { new CultureInfo("zh-CN") }; + yield return new object[] { new CultureInfo("ru-RU") }; } /// diff --git a/test/Geo.Bing.Tests/TestData/CultureTestData.cs b/test/Geo.Bing.Tests/TestData/CultureTestData.cs index a8059bc..445335a 100644 --- a/test/Geo.Bing.Tests/TestData/CultureTestData.cs +++ b/test/Geo.Bing.Tests/TestData/CultureTestData.cs @@ -10,7 +10,9 @@ namespace Geo.Bing.Tests using System.Globalization; /// - /// Test data when testing different cultures. This test data returns all cultures in dotnet. + /// Test data when testing different cultures. This test data returns a representative set of cultures + /// to test culture-invariant behaviour without running tests for every culture in dotnet. + /// Covers: invariant, period-decimal (en-US), comma-decimal (de-DE, fr-FR, ru-RU), Arabic, and Chinese. /// public class CultureTestData : IEnumerable { @@ -20,11 +22,13 @@ public class CultureTestData : IEnumerable /// An of []. public IEnumerator GetEnumerator() { - var cultures = CultureInfo.GetCultures(CultureTypes.AllCultures); - foreach (var culture in cultures) - { - yield return new object[] { culture }; - } + yield return new object[] { CultureInfo.InvariantCulture }; + yield return new object[] { new CultureInfo("en-US") }; + yield return new object[] { new CultureInfo("de-DE") }; + yield return new object[] { new CultureInfo("fr-FR") }; + yield return new object[] { new CultureInfo("ar-SA") }; + yield return new object[] { new CultureInfo("zh-CN") }; + yield return new object[] { new CultureInfo("ru-RU") }; } /// diff --git a/test/Geo.Google.Tests/TestData/CultureTestData.cs b/test/Geo.Google.Tests/TestData/CultureTestData.cs index 057791b..1c3dc8f 100644 --- a/test/Geo.Google.Tests/TestData/CultureTestData.cs +++ b/test/Geo.Google.Tests/TestData/CultureTestData.cs @@ -10,7 +10,9 @@ namespace Geo.Google.Tests using System.Globalization; /// - /// Test data when testing different cultures. This test data returns all cultures in dotnet. + /// Test data when testing different cultures. This test data returns a representative set of cultures + /// to test culture-invariant behaviour without running tests for every culture in dotnet. + /// Covers: invariant, period-decimal (en-US), comma-decimal (de-DE, fr-FR, ru-RU), Arabic, and Chinese. /// public class CultureTestData : IEnumerable { @@ -20,11 +22,13 @@ public class CultureTestData : IEnumerable /// An of []. public IEnumerator GetEnumerator() { - var cultures = CultureInfo.GetCultures(CultureTypes.AllCultures); - foreach (var culture in cultures) - { - yield return new object[] { culture }; - } + yield return new object[] { CultureInfo.InvariantCulture }; + yield return new object[] { new CultureInfo("en-US") }; + yield return new object[] { new CultureInfo("de-DE") }; + yield return new object[] { new CultureInfo("fr-FR") }; + yield return new object[] { new CultureInfo("ar-SA") }; + yield return new object[] { new CultureInfo("zh-CN") }; + yield return new object[] { new CultureInfo("ru-RU") }; } /// diff --git a/test/Geo.Here.Tests/TestData/CultureTestData.cs b/test/Geo.Here.Tests/TestData/CultureTestData.cs index d33a47d..641a84e 100644 --- a/test/Geo.Here.Tests/TestData/CultureTestData.cs +++ b/test/Geo.Here.Tests/TestData/CultureTestData.cs @@ -10,7 +10,9 @@ namespace Geo.Here.Tests using System.Globalization; /// - /// Test data when testing different cultures. This test data returns all cultures in dotnet. + /// Test data when testing different cultures. This test data returns a representative set of cultures + /// to test culture-invariant behaviour without running tests for every culture in dotnet. + /// Covers: invariant, period-decimal (en-US), comma-decimal (de-DE, fr-FR, ru-RU), Arabic, and Chinese. /// public class CultureTestData : IEnumerable { @@ -20,11 +22,13 @@ public class CultureTestData : IEnumerable /// An of []. public IEnumerator GetEnumerator() { - var cultures = CultureInfo.GetCultures(CultureTypes.AllCultures); - foreach (var culture in cultures) - { - yield return new object[] { culture }; - } + yield return new object[] { CultureInfo.InvariantCulture }; + yield return new object[] { new CultureInfo("en-US") }; + yield return new object[] { new CultureInfo("de-DE") }; + yield return new object[] { new CultureInfo("fr-FR") }; + yield return new object[] { new CultureInfo("ar-SA") }; + yield return new object[] { new CultureInfo("zh-CN") }; + yield return new object[] { new CultureInfo("ru-RU") }; } /// diff --git a/test/Geo.MapBox.Tests/TestData/CultureTestData.cs b/test/Geo.MapBox.Tests/TestData/CultureTestData.cs index c622e80..49242d5 100644 --- a/test/Geo.MapBox.Tests/TestData/CultureTestData.cs +++ b/test/Geo.MapBox.Tests/TestData/CultureTestData.cs @@ -10,7 +10,9 @@ namespace Geo.MapBox.Tests using System.Globalization; /// - /// Test data when testing different cultures. This test data returns all cultures in dotnet. + /// Test data when testing different cultures. This test data returns a representative set of cultures + /// to test culture-invariant behaviour without running tests for every culture in dotnet. + /// Covers: invariant, period-decimal (en-US), comma-decimal (de-DE, fr-FR, ru-RU), Arabic, and Chinese. /// public class CultureTestData : IEnumerable { @@ -20,11 +22,13 @@ public class CultureTestData : IEnumerable /// An of []. public IEnumerator GetEnumerator() { - var cultures = CultureInfo.GetCultures(CultureTypes.AllCultures); - foreach (var culture in cultures) - { - yield return new object[] { culture }; - } + yield return new object[] { CultureInfo.InvariantCulture }; + yield return new object[] { new CultureInfo("en-US") }; + yield return new object[] { new CultureInfo("de-DE") }; + yield return new object[] { new CultureInfo("fr-FR") }; + yield return new object[] { new CultureInfo("ar-SA") }; + yield return new object[] { new CultureInfo("zh-CN") }; + yield return new object[] { new CultureInfo("ru-RU") }; } /// diff --git a/test/Geo.MapQuest.Tests/TestData/CultureTestData.cs b/test/Geo.MapQuest.Tests/TestData/CultureTestData.cs index 69e6e1e..b898b1c 100644 --- a/test/Geo.MapQuest.Tests/TestData/CultureTestData.cs +++ b/test/Geo.MapQuest.Tests/TestData/CultureTestData.cs @@ -10,7 +10,9 @@ namespace Geo.MapQuest.Tests using System.Globalization; /// - /// Test data when testing different cultures. This test data returns all cultures in dotnet. + /// Test data when testing different cultures. This test data returns a representative set of cultures + /// to test culture-invariant behaviour without running tests for every culture in dotnet. + /// Covers: invariant, period-decimal (en-US), comma-decimal (de-DE, fr-FR, ru-RU), Arabic, and Chinese. /// public class CultureTestData : IEnumerable { @@ -20,11 +22,13 @@ public class CultureTestData : IEnumerable /// An of []. public IEnumerator GetEnumerator() { - var cultures = CultureInfo.GetCultures(CultureTypes.AllCultures); - foreach (var culture in cultures) - { - yield return new object[] { culture }; - } + yield return new object[] { CultureInfo.InvariantCulture }; + yield return new object[] { new CultureInfo("en-US") }; + yield return new object[] { new CultureInfo("de-DE") }; + yield return new object[] { new CultureInfo("fr-FR") }; + yield return new object[] { new CultureInfo("ar-SA") }; + yield return new object[] { new CultureInfo("zh-CN") }; + yield return new object[] { new CultureInfo("ru-RU") }; } /// diff --git a/test/Geo.Positionstack.Tests/TestData/CultureTestData.cs b/test/Geo.Positionstack.Tests/TestData/CultureTestData.cs index 4c4fd2a..b0e3aac 100644 --- a/test/Geo.Positionstack.Tests/TestData/CultureTestData.cs +++ b/test/Geo.Positionstack.Tests/TestData/CultureTestData.cs @@ -10,7 +10,9 @@ namespace Geo.Positionstack.Tests using System.Globalization; /// - /// Test data when testing different cultures. This test data returns all cultures in dotnet. + /// Test data when testing different cultures. This test data returns a representative set of cultures + /// to test culture-invariant behaviour without running tests for every culture in dotnet. + /// Covers: invariant, period-decimal (en-US), comma-decimal (de-DE, fr-FR, ru-RU), Arabic, and Chinese. /// public class CultureTestData : IEnumerable { @@ -20,11 +22,13 @@ public class CultureTestData : IEnumerable /// An of []. public IEnumerator GetEnumerator() { - var cultures = CultureInfo.GetCultures(CultureTypes.AllCultures); - foreach (var culture in cultures) - { - yield return new object[] { culture }; - } + yield return new object[] { CultureInfo.InvariantCulture }; + yield return new object[] { new CultureInfo("en-US") }; + yield return new object[] { new CultureInfo("de-DE") }; + yield return new object[] { new CultureInfo("fr-FR") }; + yield return new object[] { new CultureInfo("ar-SA") }; + yield return new object[] { new CultureInfo("zh-CN") }; + yield return new object[] { new CultureInfo("ru-RU") }; } /// diff --git a/test/Geo.Radar.Tests/TestData/CultureTestData.cs b/test/Geo.Radar.Tests/TestData/CultureTestData.cs index 56c02b8..e715f96 100644 --- a/test/Geo.Radar.Tests/TestData/CultureTestData.cs +++ b/test/Geo.Radar.Tests/TestData/CultureTestData.cs @@ -10,7 +10,9 @@ namespace Geo.Radar.Tests using System.Globalization; /// - /// Test data when testing different cultures. This test data returns all cultures in dotnet. + /// Test data when testing different cultures. This test data returns a representative set of cultures + /// to test culture-invariant behaviour without running tests for every culture in dotnet. + /// Covers: invariant, period-decimal (en-US), comma-decimal (de-DE, fr-FR, ru-RU), Arabic, and Chinese. /// public class CultureTestData : IEnumerable { @@ -20,11 +22,13 @@ public class CultureTestData : IEnumerable /// An of []. public IEnumerator GetEnumerator() { - var cultures = CultureInfo.GetCultures(CultureTypes.AllCultures); - foreach (var culture in cultures) - { - yield return new object[] { culture }; - } + yield return new object[] { CultureInfo.InvariantCulture }; + yield return new object[] { new CultureInfo("en-US") }; + yield return new object[] { new CultureInfo("de-DE") }; + yield return new object[] { new CultureInfo("fr-FR") }; + yield return new object[] { new CultureInfo("ar-SA") }; + yield return new object[] { new CultureInfo("zh-CN") }; + yield return new object[] { new CultureInfo("ru-RU") }; } ///