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