From f5091c58dabb4a2eb24172288fb42834d5bc3369 Mon Sep 17 00:00:00 2001
From: JustinCanton <67930245+JustinCanton@users.noreply.github.com>
Date: Fri, 27 Feb 2026 13:46:41 -0500
Subject: [PATCH 1/6] feat(arcgis): updating the parameters to add new
parameters for the endpoints, and adding missing respnose parameters (#121)
---
.../Abstractions/IArcGISGeocoding.cs | 13 +
.../Parameters/AddressCandidateParameters.cs | 3 +
.../FindAddressCandidatesParameters.cs | 162 +++++++
.../Models/Parameters/GeocodingParameters.cs | 6 +
.../Parameters/PlaceCandidateParameters.cs | 2 +
src/Geo.ArcGIS/Models/Responses/Address.cs | 46 +-
src/Geo.ArcGIS/Models/Responses/Attribute.cs | 430 +++++++++++++++++-
.../Services/ArcGISGeocoding.Designer.cs | 11 +-
.../Services/ArcGISGeocoding.en.resx | 5 +-
.../Resources/Services/ArcGISGeocoding.resx | 5 +-
src/Geo.ArcGIS/Services/ArcGISGeocoding.cs | 245 ++++++++++
.../Services/ArcGISGeocodingShould.cs | 213 ++++++++-
.../TestData/CultureTestData.cs | 16 +-
.../TestData/CultureTestData.cs | 16 +-
.../TestData/CultureTestData.cs | 16 +-
.../TestData/CultureTestData.cs | 16 +-
.../TestData/CultureTestData.cs | 16 +-
.../TestData/CultureTestData.cs | 16 +-
.../TestData/CultureTestData.cs | 16 +-
.../TestData/CultureTestData.cs | 16 +-
20 files changed, 1209 insertions(+), 60 deletions(-)
create mode 100644 src/Geo.ArcGIS/Models/Parameters/FindAddressCandidatesParameters.cs
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/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/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/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/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