Skip to content

Commit

Permalink
Fixing error 414 when too many objects are requested.
Browse files Browse the repository at this point in the history
Adding GetElements(OsmGeoKey[]) which can request different types in one call.
  • Loading branch information
blackboxlogic committed Feb 6, 2020
1 parent 3fa1dc1 commit 6f1c8c4
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 9 deletions.
5 changes: 4 additions & 1 deletion FunctionalTests/Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Threading.Tasks;
using OsmSharp.API;
using OsmSharp.Changesets;
using OsmSharp.Db;
using OsmSharp.Tags;

namespace OsmSharp.IO.API.FunctionalTests
Expand Down Expand Up @@ -70,7 +71,9 @@ public static async Task TestClient(INonAuthClient client)
var multifetchNodes = await client.GetNodes(new Dictionary<long, long?>() { { nodeId, null }, { nodeId + 1, 1 } });
var nodeRelations = await client.GetNodeRelations(nodeId);
var nodeWays = await client.GetNodeWays(nodeId);
True(nodeHistory?.Any(), multifetchNodes?.Any(), nodeRelations?.Any(), nodeWays?.Any());
var multifetchElements = await client.GetElements(
map.Nodes.Select(n => new OsmGeoKey(n)).Concat(map.Ways.Select(n => new OsmGeoKey(n))).ToArray());
True(nodeHistory?.Any(), multifetchNodes?.Any(), nodeRelations?.Any(), nodeWays?.Any(), multifetchElements?.Any());
var changeset = await client.GetChangeset(node.ChangeSetId.Value);
var changesetWithDiscussion = await client.GetChangeset(node.ChangeSetId.Value, true);
NotNull(changeset, changesetWithDiscussion?.Discussion);
Expand Down
3 changes: 3 additions & 0 deletions src/INonAuthClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using OsmSharp.API;
using OsmSharp.Changesets;
using OsmSharp.Complete;
using OsmSharp.Db;

namespace OsmSharp.IO.API
{
Expand All @@ -29,6 +30,8 @@ public interface INonAuthClient
Task<Node[]> GetNodes(params long[] ids);
Task<Way[]> GetWays(params long[] ids);
Task<Relation[]> GetRelations(params long[] ids);
Task<OsmGeo[]> GetElements(params OsmGeoKey[] elementKeys);
Task<OsmGeo[]> GetElements(Dictionary<OsmGeoKey, long?> elementKeyVersions);
Task<Node[]> GetNodes(IEnumerable<KeyValuePair<long, long?>> idVersions);
Task<Way[]> GetWays(IEnumerable<KeyValuePair<long, long?>> idVersions);
Task<Relation[]> GetRelations(IEnumerable<KeyValuePair<long, long?>> idVersions);
Expand Down
51 changes: 45 additions & 6 deletions src/NonAuthClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using System.IO;
using System.Web;
using Microsoft.Extensions.Logging;
using OsmSharp.Db;

namespace OsmSharp.IO.API
{
Expand Down Expand Up @@ -342,19 +343,57 @@ public async Task<Relation[]> GetRelations(IEnumerable<KeyValuePair<long, long?>
return await GetElements<Relation>(idVersions);
}

public async Task<OsmGeo[]> GetElements(params OsmGeoKey[] elementKeys)
{
return await GetElements(elementKeys.ToDictionary(ek => ek, ek => (long?)null));
}

public async Task<OsmGeo[]> GetElements(Dictionary<OsmGeoKey, long?> elementKeyVersions)
{
var elements = new List<OsmGeo>();

foreach (var typeGroup in elementKeyVersions.GroupBy(kvp => kvp.Key.Type))
{
var chunkAsDictionary = typeGroup.ToDictionary(kvp => kvp.Key.Id, kvp => kvp.Value);
if(typeGroup.Key == OsmGeoType.Node)
elements.AddRange(await GetElements<Node>(chunkAsDictionary));
else if (typeGroup.Key == OsmGeoType.Way)
elements.AddRange(await GetElements<Way>(chunkAsDictionary));
else if (typeGroup.Key == OsmGeoType.Relation)
elements.AddRange(await GetElements<Relation>(chunkAsDictionary));
}

return elements.ToArray();
}

/// <summary>
/// Elements Multifetch
/// <see href="https://wiki.openstreetmap.org/wiki/API_v0.6#Multi_fetch:_GET_.2Fapi.2F0.6.2F.5Bnodes.7Cways.7Crelations.5D.3F.23parameters">
/// GET /api/0.6/[nodes|ways|relations]?#parameters</see>.
/// </summary>
private async Task<TOsmGeo[]> GetElements<TOsmGeo>(IEnumerable<KeyValuePair<long, long?>> idVersions) where TOsmGeo : OsmGeo, new()
{
var type = new TOsmGeo().Type.ToString().ToLower();
// For exmple: "12,13,14v1,15v1"
var parameters = string.Join(",", idVersions.Select(e => e.Value.HasValue ? $"{e.Key}v{e.Value}" : e.Key.ToString()));
var address = BaseAddress + $"0.6/{type}s?{type}s={parameters}";
var elements = await GetOfType<TOsmGeo>(address);
return elements.ToArray();
var tasks = new List<Task<IEnumerable<TOsmGeo>>>();

foreach (var chunk in Chunks(idVersions, 400)) // to avoid http error code 414, UIR too long.
{
var type = new TOsmGeo().Type.ToString().ToLower();
// For exmple: "12,13,14v1,15v1"
var parameters = string.Join(",", idVersions.Select(e => e.Value.HasValue ? $"{e.Key}v{e.Value}" : e.Key.ToString()));
var address = BaseAddress + $"0.6/{type}s?{type}s={parameters}";
tasks.Add(GetOfType<TOsmGeo>(address));
}

await Task.WhenAll(tasks);

return tasks.SelectMany(t => t.Result).ToArray();
}

private IEnumerable<T[]> Chunks<T>(IEnumerable<T> elements, int chunkSize)
{
return elements.Select((e, i) => new { e, i })
.GroupBy(ei => ei.i / chunkSize) // Intentional integer division
.Select(g => g.Select(ei => ei.e).ToArray());
}

/// <summary>
Expand Down
4 changes: 2 additions & 2 deletions src/OsmApiClient.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<PackageId>OsmApiClient</PackageId>
<Version>0.0.1</Version>
<Version>0.0.2</Version>
<RepositoryUrl>https://github.com/OsmSharp/osm-api-client/</RepositoryUrl>
<PackageReleaseNotes>First Release</PackageReleaseNotes>
<PackageReleaseNotes>Adding GetElements(OsmGeoKey[] keys) which can fetch multiple element types in one call.</PackageReleaseNotes>
<PackageProjectUrl>https://github.com/OsmSharp/osm-api-client/</PackageProjectUrl>
<Description>This is a simple C# client to allow using OSM API easily.</Description>
<PackageTags>osm;openstreetmap;client</PackageTags>
Expand Down

0 comments on commit 6f1c8c4

Please sign in to comment.