This repository has been archived by the owner on Feb 14, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 562
/
GeocodeLocations.cs
109 lines (100 loc) · 4.91 KB
/
GeocodeLocations.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using System.Web;
using Microsoft.Extensions.Logging;
using Statiq.Common;
using Statiq.Core;
namespace DotnetFoundationWeb
{
// Looks for documents that contain "Location" metadata but not "Lat" and "Lon" and queries the Azure Maps API to add them
public class GeocodeLocations : Module
{
public static readonly JsonSerializerOptions DefaultJsonSerializerOptions = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
// Cache responses for each location
private readonly ConcurrentDictionary<string, Task<CoordinateAbbreviated>> _coordinateCache =
new ConcurrentDictionary<string, Task<CoordinateAbbreviated>>(StringComparer.OrdinalIgnoreCase);
private readonly Config<string> _subscriptionKey;
public GeocodeLocations(Config<string> subscriptionKey)
{
_subscriptionKey = subscriptionKey.ThrowIfNull(nameof(subscriptionKey));
// Add coordinate overrides to cache
_coordinateCache["the netherlands"] = Task.FromResult(new CoordinateAbbreviated { Lat = 52.371807, Lon = 4.896029 });
}
protected override async Task<IEnumerable<IDocument>> ExecuteInputAsync(IDocument input, IExecutionContext context)
{
if (input.ContainsKey(SiteKeys.Location) && !input.ContainsKey(SiteKeys.Lat) && !input.ContainsKey(SiteKeys.Lon))
{
string location = input.GetString(SiteKeys.Location);
if (!location.IsNullOrWhiteSpace())
{
CoordinateAbbreviated coordinates = await _coordinateCache.GetOrAdd(location, async _ =>
{
context.LogInformation($"Geocoding location {location} for {input.ToSafeDisplayString()}");
string subscriptionKey = await _subscriptionKey.GetValueAsync(input, context);
if (!subscriptionKey.IsNullOrWhiteSpace())
{
using (HttpClient client = context.CreateHttpClient())
{
HttpResponseMessage responseMessage = await client.SendWithRetryAsync($"https://atlas.microsoft.com/search/address/json?&subscription-key={subscriptionKey}&api-version=1.0&language=en-US&limit=1&query={HttpUtility.UrlEncode(location)}");
if (responseMessage.IsSuccessStatusCode)
{
SearchAddressResponse searchAddressResponse;
using (Stream responseStream = await responseMessage.Content.ReadAsStreamAsync())
{
searchAddressResponse = await JsonSerializer.DeserializeAsync<SearchAddressResponse>(responseStream, DefaultJsonSerializerOptions);
}
if (searchAddressResponse.Results.Length > 0)
{
return searchAddressResponse.Results[0].Position;
}
else
{
context.LogWarning($"No results while geocoding location {location} for {input.ToSafeDisplayString()}");
}
}
else
{
context.LogWarning($"Error {responseMessage.StatusCode} while geocoding location {location} for {input.ToSafeDisplayString()}");
}
}
}
return null;
});
if (coordinates is object)
{
return input
.Clone(new MetadataItems
{
{ SiteKeys.Lat, coordinates.Lat },
{ SiteKeys.Lon, coordinates.Lon }
})
.Yield();
}
}
}
return input.Yield();
}
public class SearchAddressResponse
{
public SearchAddressResult[] Results { get; set; }
}
public class SearchAddressResult
{
public CoordinateAbbreviated Position { get; set; }
}
public class CoordinateAbbreviated
{
public double Lat { get; set; }
public double Lon { get; set; }
}
}
}