Skip to content

Commit

Permalink
Merge pull request #11 from davetimmins/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
davetimmins authored Jan 15, 2018
2 parents 07721d3 + e17f823 commit 7077329
Show file tree
Hide file tree
Showing 11 changed files with 339 additions and 13 deletions.
8 changes: 8 additions & 0 deletions NUGET-DOC.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ Create an instance of that by specifying the root url of your server. The format

```c#
var gateway = new PortalGateway("https://sampleserver3.arcgisonline.com/ArcGIS/");

// If you want to access secure resources then pass in a username / password
// this assumes the token service is in the default location for the ArcGIS Server
var secureGateway = new PortalGateway("https://sampleserver3.arcgisonline.com/ArcGIS/", "username", "password");

// Or use the static Create method which will discover the token service Url from the server Info endpoint
var autoTokenProviderLocationGateway = await PortalGateway.Create("https://sampleserver3.arcgisonline.com/ArcGIS/", "username", "password");
```

Now you have access to the various operations supported by it. For example to call a query against a service
Expand Down Expand Up @@ -47,6 +54,7 @@ Supports the following as typed operations:
- `DescribeLayer` return layer information
- `HealthCheck` verify that the server is accepting requests
- `GetFeature` return a feature from a map/feature service
- `ExportMap` get an image (or url to the image) of a service

REST admin operations:

Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ Create an instance of that by specifying the root url of your server. The format

```c#
var gateway = new PortalGateway("https://sampleserver3.arcgisonline.com/ArcGIS/");

// If you want to access secure resources then pass in a username / password
// this assumes the token service is in the default location for the ArcGIS Server
var secureGateway = new PortalGateway("https://sampleserver3.arcgisonline.com/ArcGIS/", "username", "password");

// Or use the static Create method which will discover the token service Url from the server Info endpoint
var autoTokenProviderLocationGateway = await PortalGateway.Create("https://sampleserver3.arcgisonline.com/ArcGIS/", "username", "password");
```

Now you have access to the various operations supported by it. For example to call a query against a service
Expand Down Expand Up @@ -66,6 +73,7 @@ Supports the following as typed operations:
- `DescribeLayer` return layer information
- `HealthCheck` verify that the server is accepting requests
- `GetFeature` return a feature from a map/feature service
- `ExportMap` get an image (or url to the image) of a service

REST admin operations:

Expand Down
2 changes: 1 addition & 1 deletion build.cake
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ var target = Argument("target", "Default");
var configuration = Argument("configuration", "Release");
var solution = "./Anywhere.ArcGIS.sln";

var version = "1.1.0";
var version = "1.2.0";
var versionSuffix = Environment.GetEnvironmentVariable("VERSION_SUFFIX");

//////////////////////////////////////////////////////////////////////
Expand Down
9 changes: 4 additions & 5 deletions src/Anywhere.ArcGIS/Anywhere.ArcGIS.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@
<RepositoryUrl>https://github.com/davetimmins/Anywhere.ArcGIS</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>ArcGIS ArcGISServer ArcGISOnline Esri REST netstandard anywhere GIS Mapping Map Location GeoLocation OAuth</PackageTags>
<PackageReleaseNotes>Initial port of ArcGIS.PCL to netstandard</PackageReleaseNotes>
<Version>1.1.0</Version>
<Version>1.2.0</Version>
</PropertyGroup>

<ItemGroup>
Expand All @@ -25,8 +24,8 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="SourceLink.Create.GitHub" Version="2.6.0" PrivateAssets="All" />
<DotNetCliToolReference Include="dotnet-sourcelink-git" Version="2.6.0" />
<DotNetCliToolReference Include="dotnet-sourcelink" Version="2.6.0" />
<PackageReference Include="SourceLink.Create.GitHub" Version="2.7.4" PrivateAssets="All" />
<DotNetCliToolReference Include="dotnet-sourcelink-git" Version="2.7.4" />
<DotNetCliToolReference Include="dotnet-sourcelink" Version="2.7.4" />
</ItemGroup>
</Project>
6 changes: 5 additions & 1 deletion src/Anywhere.ArcGIS/Extensions/StringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,11 @@ public static string AsRootUrl(this string rootUrl)

public static string UrlEncode(this string text)
{
return string.IsNullOrWhiteSpace(text) ? text : Uri.EscapeDataString(text);
return string.IsNullOrWhiteSpace(text)
? text
: text.Length > 65520
? text // this will get sent to POST anyway so don't bother escaping
: Uri.EscapeDataString(text);
}

/// <summary>
Expand Down
1 change: 1 addition & 0 deletions src/Anywhere.ArcGIS/Operation/CommonParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public static class Operations
public const string Simplify = "simplify";
public const string Buffer = "buffer";
public const string Project = "project";
public const string ExportMap = "export";
public const string PublicKey = "publicKey";
public const string ServiceStatus = "services/{0}.{1}/status";
public const string StartService = "services/{0}.{1}/start";
Expand Down
184 changes: 184 additions & 0 deletions src/Anywhere.ArcGIS/Operation/ExportMap.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
using Anywhere.ArcGIS.Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;

namespace Anywhere.ArcGIS.Operation
{
[DataContract]
public class ExportMap : ArcGISServerOperation
{
public ExportMap(string relativeUrl, Action beforeRequest = null, Action afterRequest = null)
: this(relativeUrl.AsEndpoint(), beforeRequest, afterRequest)
{ }

/// <summary>
/// Requests an export of the map resources. Returns the image link in the response
/// </summary>
/// <param name="endpoint">Resource to apply the export against</param>
public ExportMap(ArcGISServerEndpoint endpoint, Action beforeRequest = null, Action afterRequest = null)
: base(endpoint.RelativeUrl.Trim('/') + "/" + Operations.ExportMap, beforeRequest, afterRequest)
{
Size = new List<int> { 400, 400 };
Dpi = 96;
ImageFormat = "png";
}

/// <summary>
/// (Required) The extent (bounding box) of the exported image.
/// Unless the bboxSR parameter has been specified, the bbox is assumed to be in the spatial reference of the map.
/// </summary>
[IgnoreDataMember]
public Extent ExportExtent { get; set; }

/// <summary>
/// (Required) The extent (bounding box) of the exported image.
/// Unless the bboxSR parameter has been specified, the bbox is assumed to be in the spatial reference of the map.
/// </summary>
[DataMember(Name = "bbox")]
public string ExportExtentBoundingBox
{
get
{
return ExportExtent == null ?
string.Empty :
string.Format("{0},{1},{2},{3}", ExportExtent.XMin, ExportExtent.YMin, ExportExtent.XMax, ExportExtent.YMax);
}
}

/// <summary>
/// The spatial reference of the ExportExtentBoundingBox.
/// </summary>
[DataMember(Name = "bboxSR")]
public SpatialReference ExportExtentBoundingBoxSpatialReference
{
get { return ExportExtent == null ? null : ExportExtent.SpatialReference ?? null; }
}

/// <summary>
/// The size (width * height) of the exported image in pixels.
/// If the size is not specified, an image with a default size of 400 * 400 will be exported.
/// </summary>
[IgnoreDataMember]
public List<int> Size { get; set; }

/// <summary>
/// The size width *height) of the exported image in pixels.
/// If the size is not specified, an image with a default size of 400 * 400 will be exported.
/// </summary>
[DataMember(Name = "size")]
public string SizeValue
{
get
{
if (Size?.Count > 0 && Size?.Count != 2)
{
throw new ArgumentOutOfRangeException(nameof(Size), "If you want to use the SizeRange parameter then you need to supply exactly 2 values: the lower and upper range");
}

return Size == null || !Size.Any() ? null : string.Join(",", Size);
}
}

/// <summary>
/// The device resolution of the exported image (dots per inch).
/// If the dpi is not specified, an image with a default DPI of 96 will be exported.
/// </summary>
[DataMember(Name = "dpi")]
public int Dpi { get; set; }

/// <summary>
/// The spatial reference of the exported image.
/// </summary>
[DataMember(Name = "imageSR")]
public SpatialReference ImageSpatialReference { get; set; }

/// <summary>
/// The format of the exported image. The default format is png.
/// Values: png | png8 | png24 | jpg | pdf | bmp | gif | svg | svgz | emf | ps | png32
/// </summary>
[DataMember(Name = "format")]
public string ImageFormat { get; set; }

/// <summary>
/// Allows you to filter the features of individual layers in the exported map by specifying definition expressions for those layers.
/// Definition expression for a layer that is published with the service will be always honored.
/// </summary>
[DataMember(Name = "layerDefs")]
public Dictionary<int, string> LayerDefinitions { get; set; }

/// <summary>
/// If true, the image will be exported with the background color of the map set as its transparent color.
/// The default is false.
/// Only the .png and .gif formats support transparency.
/// Internet Explorer 6 does not display transparency correctly for png24 image formats.
/// </summary>
[DataMember(Name = "transparent")]
public bool TransparentBackground { get; set; }

[IgnoreDataMember]
public DateTime? From { get; set; }

[IgnoreDataMember]
public DateTime? To { get; set; }

/// <summary>
/// The time instant or the time extent to query.
/// </summary>
/// <remarks>If no To value is specified we will use the From value again, equivalent of using a time instant.</remarks>
[DataMember(Name = "time")]
public string Time
{
get
{
return (From == null) ? null : string.Format("{0},{1}",
From.Value.ToUnixTime(),
(To ?? From.Value).ToUnixTime());
}
}

/// <summary>
/// GeoDatabase version to export from.
/// </summary>
[DataMember(Name = "gdbVersion")]
public string GeodatabaseVersion { get; set; }

/// <summary>
/// Use this parameter to export a map image at a specific scale, with the map centered around the center of the specified bounding box (bbox).
/// </summary>
[DataMember(Name = "mapScale")]
public long MapScale { get; set; }

/// <summary>
/// Use this parameter to export a map image rotated at a specific angle, with the map centered around the center of the specified bounding box (bbox).
/// It could be positive or negative number.
/// </summary>
[DataMember(Name = "rotation")]
public int Rotation { get; set; }
}

[DataContract]
public class ExportMapResponse : PortalResponse
{
[DataMember(Name = "href")]
public string ImageUrl { get; set; }

[DataMember(Name = "width")]
public int Width { get; set; }

[DataMember(Name = "height")]
public int Height { get; set; }

[DataMember(Name = "extent")]
public Extent Extent { get; set; }

[DataMember(Name = "scale")]
public double Scale { get; set; }

public string ImageFormat { get { return string.IsNullOrEmpty(ImageUrl) ? string.Empty : ImageUrl.Substring(ImageUrl.LastIndexOf(".") + 1); } }

public SpatialReference ImageSpatialReference { get { return Extent?.SpatialReference; } }
}
}
1 change: 1 addition & 0 deletions src/Anywhere.ArcGIS/Operation/Query.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public SpatialReference InputSpatialReference
{
get { return Geometry == null ? null : Geometry.SpatialReference ?? null; }
}

/// <summary>
/// The type of geometry specified by the geometry parameter.
/// The geometry type can be an envelope, point, line, or polygon.
Expand Down
61 changes: 61 additions & 0 deletions src/Anywhere.ArcGIS/PortalGatewayBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,25 @@ public virtual async Task<FileInfo> DownloadAttachmentToLocal(Attachment attachm
return Get<QueryDomainsResponse, QueryDomains>(queryDomains, ct);
}

/// <summary>
/// The export operation is performed on a map service resource.
/// The result of this operation is a map image resource.
/// This resource provides information about the exported map image such as its URL, its width and height, extent and scale.
///
/// Note that the extent displayed in the exported map image may not exactly match the extent sent in the bbox parameter when the aspect ratio of the image size does not match the aspect ratio of the bbox.
/// The aspect ratio is the height divided by the width.
/// In these cases the extent is re-sized to prevent map images from appearing stretched.
/// The exported map's extent is sent along with the response and may be used in client side calculations.
/// So it is important that the client-side code update its extent based on the response.
/// </summary>
/// <param name="exportMap"></param>
/// <param name="ct"></param>
/// <returns></returns>
public virtual Task<ExportMapResponse> ExportMap(ExportMap exportMap, CancellationToken ct = default(CancellationToken))
{
return Get<ExportMapResponse, ExportMap>(exportMap, ct);
}

async Task<Token> CheckGenerateToken(CancellationToken ct)
{
if (TokenProvider == null)
Expand All @@ -489,6 +508,48 @@ async Task<Token> CheckGenerateToken(CancellationToken ct)
return token;
}

/// <summary>
///
/// </summary>
/// <param name="exportMapResponse"></param>
/// <param name="folderLocation"></param>
/// <param name="fileName">If not specified a Guid will be used for the name</param>
/// <returns></returns>
public virtual async Task<FileInfo> DownloadExportMapToLocal(ExportMapResponse exportMapResponse, string folderLocation, string fileName = null)
{
LiteGuard.Guard.AgainstNullArgument(nameof(exportMapResponse), exportMapResponse);

if (string.IsNullOrWhiteSpace(exportMapResponse.ImageUrl))
{
throw new ArgumentNullException(nameof(exportMapResponse.ImageUrl));
}

if (string.IsNullOrWhiteSpace(folderLocation))
{
throw new ArgumentNullException(nameof(folderLocation));
}

if (string.IsNullOrWhiteSpace(fileName))
{
fileName = Guid.NewGuid().ToString();
}

var response = await _httpClient.GetAsync(exportMapResponse.ImageUrl);
response.EnsureSuccessStatusCode();
await response.Content.LoadIntoBufferAsync();

var fileInfo = new FileInfo(Path.Combine(folderLocation, $"{fileName}.{exportMapResponse.ImageFormat}"));

using (var fileStream = new FileStream(fileInfo.FullName, FileMode.Create, FileAccess.Write, FileShare.None))
{
await response.Content.CopyToAsync(fileStream);
}

_logger.DebugFormat("Saved export map response to {0}.", fileInfo.FullName);

return new FileInfo(fileInfo.FullName);
}

void CheckRefererHeader(string referrer)
{
if (_httpClient == null || string.IsNullOrWhiteSpace(referrer))
Expand Down
Loading

0 comments on commit 7077329

Please sign in to comment.