Skip to content

Commit

Permalink
Merge pull request #220 from Curlack/master
Browse files Browse the repository at this point in the history
Implement AutocompleteService, Geocoder and PlacesService
  • Loading branch information
valentasm1 authored Nov 25, 2022
2 parents 7014860 + c0856e0 commit 13ad470
Show file tree
Hide file tree
Showing 34 changed files with 1,275 additions and 46 deletions.
40 changes: 40 additions & 0 deletions GoogleMapsComponents/EnumMemberConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System;
using System.ComponentModel;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace GoogleMapsComponents
{
internal class EnumMemberConverter<T> : JsonConverter<T> where T : IComparable, IFormattable, IConvertible
{
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
string jsonValue = reader.GetString();

foreach (var fi in typeToConvert.GetFields())
{
var description = (EnumMemberAttribute)fi.GetCustomAttribute(typeof(EnumMemberAttribute), false);

if (description != null)
{
if (string.Equals(description.Value, jsonValue, StringComparison.OrdinalIgnoreCase))
{
return (T)fi.GetValue(null);
}
}
}
throw new JsonException($"string {jsonValue} was not found as a description in the enum {typeToConvert}");
}

public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
var fi = value.GetType().GetField(value.ToString());

var description = (EnumMemberAttribute)fi.GetCustomAttribute(typeof(EnumMemberAttribute), false);

writer.WriteStringValue(description.Value);
}
}
}
50 changes: 48 additions & 2 deletions GoogleMapsComponents/Maps/Geocoder.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,52 @@
namespace GoogleMapsComponents.Maps
using System;
using Microsoft.JSInterop;
using System.Threading.Tasks;
using GoogleMapsComponents.Maps.Places;

namespace GoogleMapsComponents.Maps
{
public class Geocoder
/// <summary>
/// A service for converting between an address and a LatLng.
/// </summary>
public class Geocoder : IDisposable
{
private readonly JsObjectRef _jsObjectRef;

/// <summary>
/// Creates a new instance of a Geocoder that sends geocode requests to Google servers.
/// </summary>
/// <param name="jsRuntime"></param>
/// <returns></returns>
public async static Task<Geocoder> CreateAsync(IJSRuntime jsRuntime)
{
var jsObjectRef = await JsObjectRef.CreateAsync(jsRuntime, "google.maps.Geocoder");
var obj = new Geocoder(jsObjectRef);

return obj;
}

/// <summary>
/// Creates a new instance of a Geocoder that sends geocode requests to Google servers.
/// </summary>
/// <param name="jsObjectRef"></param>
private Geocoder(JsObjectRef jsObjectRef)
{
_jsObjectRef = jsObjectRef;
}

/// <summary>
/// Geocode a request
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public async Task<GeocoderResponse> Geocode(GeocoderRequest request)
{
return await _jsObjectRef.InvokeAsync<GeocoderResponse>("geocode", request);
}

public void Dispose()
{
_jsObjectRef.Dispose();
}
}
}
20 changes: 17 additions & 3 deletions GoogleMapsComponents/Maps/GeocoderAddressComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,28 @@

namespace GoogleMapsComponents.Maps
{
/// <summary>
/// A single address component within a <see cref="GeocoderResult"></see>. A full address may consist of multiple address components.
/// </summary>
public class GeocoderAddressComponent
{
/// <summary>
/// The full text.
/// </summary>
[JsonPropertyName("long_name")]
public string LongName { get; set; }
public string LongName { get; set; } = default!;

/// <summary>
/// The abbreviated, short text.
/// </summary>
[JsonPropertyName("short_name")]
public string ShortName { get; set; }
public string ShortName { get; set; } = default!;

public IEnumerable<string> Types { get; set; }
/// <summary>
/// An array of strings denoting the type.
/// A list of valid types can be found at:
/// https://developers.google.com/maps/documentation/javascript/geocoding#GeocodingAddressTypes
/// </summary>
public string[] Types { get; set; } = default!;
}
}
29 changes: 29 additions & 0 deletions GoogleMapsComponents/Maps/GeocoderComponentRestrictions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,35 @@
namespace GoogleMapsComponents.Maps
{
/// <summary>
/// GeocoderComponentRestrictions represents a set of filters that resolve to a specific area.
/// For details on how this works, see:
/// https://developers.google.com/maps/documentation/javascript/geocoding#ComponentFiltering
/// </summary>
public class GeocoderComponentRestrictions
{
/// <summary>
/// (optional) Matches all the administrative_area levels.
/// </summary>
public string? AdministrativeArea { get; set; }

/// <summary>
/// Matches a country name or a two letter ISO 3166-1 country code.
/// </summary>
public string? Country { get; set; }

/// <summary>
/// Matches against both locality and sublocality types.
/// </summary>
public string? Locality { get; set; }

/// <summary>
/// Matches postal_code and postal_code_prefix.
/// </summary>
public string? PostalCode { get; set; }

/// <summary>
/// Matches the long or short name of a route.
/// </summary>
public string? Route { get; set; }
}
}
28 changes: 27 additions & 1 deletion GoogleMapsComponents/Maps/GeocoderGeometry.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,32 @@
namespace GoogleMapsComponents.Maps
using System.Text.Json.Serialization;

namespace GoogleMapsComponents.Maps
{
/// <summary>
/// Geometry information about the <see cref="GeocoderResult"></see>
/// </summary>
public class GeocoderGeometry
{
/// <summary>
/// The latitude/longitude coordinates of this result
/// </summary>
public LatLngLiteral Location { get; set; } = default!;

/// <summary>
/// The type of location returned in <see cref="Location"></see>
/// </summary>
[JsonPropertyName("location_type")]
[JsonConverter(typeof(EnumMemberConverter<GeocoderLocationType>))]
public GeocoderLocationType LocationType { get; set; }

/// <summary>
/// The bounds of the recommended viewport for displaying the <see cref="GeocoderResult"></see>
/// </summary>
public LatLngBoundsLiteral Viewport { get; set; } = default!;

/// <summary>
/// (optional) The precise bounds of the <see cref="GeocoderResult"></see>, if applicable
/// </summary>
public LatLngBoundsLiteral? Bounds { get; set; }
}
}
39 changes: 39 additions & 0 deletions GoogleMapsComponents/Maps/GeocoderLocationType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System.Runtime.Serialization;

namespace GoogleMapsComponents.Maps
{
/// <summary>
/// The status returned by the Geocoder on the completion of a call to geocode(). Specify these by value, or by using the constant's name.
/// For example, 'OK' or google.maps.GeocoderStatus.OK.
/// </summary>
[JsonConverter(typeof(StringEnumConverter))]
public enum GeocoderLocationType
{
/// <summary>
/// The returned result is approximate.
/// </summary>
[EnumMember(Value = "approximate")]
Approximate,

/// <summary>
/// The returned result is the geometric center of a result such a line (e.g. street) or polygon (region).
/// </summary>
[EnumMember(Value = "geometric_center")]
GeometricCenter,

/// <summary>
/// The returned result reflects an approximation (usually on a road) interpolated between two precise points (such as intersections).
/// Interpolated results are generally returned when rooftop geocodes are unavailable for a street address.
/// </summary>
[EnumMember(Value = "range_interpolated")]
RangeInterpolated,

/// <summary>
/// The returned result reflects a precise geocode.
/// </summary>
[EnumMember(Value = "rooftop")]
Rooftop
}
}
55 changes: 54 additions & 1 deletion GoogleMapsComponents/Maps/GeocoderRequest.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,59 @@
namespace GoogleMapsComponents.Maps
using Newtonsoft.Json;
using OneOf;

namespace GoogleMapsComponents.Maps
{
/// <summary>
/// The specification for a geocoding request to be sent to the <see cref="Geocoder"></see>.
/// </summary>
public class GeocoderRequest
{
/// <summary>
/// (optional) Address to geocode.
/// One, and only one, of address, location and placeId must be supplied.
/// </summary>
public string? Address { get; set; }

/// <summary>
/// (optional) LatLngBounds within which to search.
/// </summary>
[JsonConverter(typeof(OneOfConverter))]
public OneOf<LatLngBounds, LatLngBoundsLiteral>? Bounds { get; set; }

/// <summary>
/// (optional) Components are used to restrict results to a specific area.
/// A filter consists of one or more of: route, locality, administrativeArea, postalCode, country.
/// Only the results that match all the filters will be returned.
/// Filter values support the same methods of spelling correction and partial matching as other geocoding requests.
/// </summary>
public GeocoderComponentRestrictions? ComponentRestrictions { get; set; }

/// <summary>
/// A language identifier for the language in which results should be returned, when possible.
/// Find list of supported languages at:
/// https://developers.google.com/maps/faq#languagesupport
/// </summary>
public string? Language { get; set; }

/// <summary>
/// (optional) LatLng (or LatLngLiteral) for which to search.
/// The geocoder performs a reverse geocode. See Reverse Geocoding for more information.
/// One, and only one, of address, location and placeId must be supplied.
/// </summary>
public LatLngLiteral? Location { get; set; }

/// <summary>
/// (optional) The place ID associated with the location. Place IDs uniquely identify a place in the Google Places database and on Google Maps.
/// Learn more about place IDs in the Places API developer guide.
/// The geocoder performs a reverse geocode. See Reverse Geocoding for more information.
/// One, and only one, of address, location and placeId must be supplied.
/// </summary>
public string? PlaceId { get; set; }

/// <summary>
/// (optional) Country code used to bias the search, specified as a two-character (non-numeric) Unicode region subtag / CLDR identifier.
/// See Google Maps Platform Coverage Details for supported regions.
/// </summary>
public string? Region { get; set; }
}
}
18 changes: 18 additions & 0 deletions GoogleMapsComponents/Maps/GeocoderResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System.Text.Json.Serialization;

namespace GoogleMapsComponents.Maps
{
/// <summary>
/// A Geocoder response returned by the <see cref="Geocoder"></see> containing the list of <see cref="GeocoderResult"></see>s.
/// </summary>
public class GeocoderResponse
{
/// <summary>
/// The list of <see cref="GeocoderResult"></see>s
/// </summary>
public GeocoderResult[] Results { get; set; } = new GeocoderResult[] { };

[JsonConverter(typeof(EnumMemberConverter<GeocoderStatus>))]
public GeocoderStatus Status { get; set; }
}
}
60 changes: 59 additions & 1 deletion GoogleMapsComponents/Maps/GeocoderResult.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,64 @@
namespace GoogleMapsComponents.Maps
using GoogleMapsComponents.Maps.Places;
using System.Text.Json.Serialization;

namespace GoogleMapsComponents.Maps
{
/// <summary>
/// A single geocoder result retrieved from the geocode server.
/// A geocode request may return multiple result objects.
/// Note that though this result is "JSON-like," it is not strictly JSON, as it indirectly includes a LatLng object.
/// </summary>
public class GeocoderResult
{
/// <summary>
/// An array of <see cref="GeocoderAddressComponent"></see>s
/// </summary>
[JsonPropertyName("address_components")]
public GeocoderAddressComponent[] AddressComponents { get; set; } = default!;

/// <summary>
/// A string containing the human-readable address of this location.
/// </summary>
[JsonPropertyName("formatted_address")]
public string FormattedAddress { get; set; } = default!;

/// <summary>
/// A GeocoderGeometry object
/// </summary>
public GeocoderGeometry Geometry { get; set; } = default!;

/// <summary>
/// The place ID associated with the location.
/// Place IDs uniquely identify a place in the Google Places database and on Google Maps.
/// Learn more about Place IDs in the Places API developer guide.
/// </summary>
[JsonPropertyName("place_id")]
public string PlaceId { get; set; } = default!;

/// <summary>
/// An array of strings denoting the type of the returned geocoded element.
/// For a list of possible strings, refer to the Address Component Types section of the Developer's Guide.
/// </summary>
public string[] Types { get; set; } = default!;

/// <summary>
/// (optional) Whether the geocoder did not return an exact match for the original request, though it was able to match part of the requested address.
/// If an exact match, the value will be undefined.
/// </summary>
[JsonPropertyName("partial_match")]
public bool? PartialMatch { get; set; }

/// <summary>
/// (optional) The plus code associated with the location.
/// </summary>
[JsonPropertyName("plus_code")]
public PlacePlusCode? PlusCode { get; set; }

/// <summary>
/// (optional) An array of strings denoting all the localities contained in a postal code.
/// This is only present when the result is a postal code that contains multiple localities.
/// </summary>
[JsonPropertyName("postcode_localities")]
public string[]? PostcodeLocalities { get; set; }
}
}
Loading

0 comments on commit 13ad470

Please sign in to comment.