A388

Google Maps API Implementation in C#

Google Maps probably doesn't need an introduction, but just in case, it is a mapping system from Google. It has a bunch of APIs to choose from. Just like with the other API implementations on here, I made a client for Google Maps as well.

I use Google Maps as a fallback if I can't get an accurate result out of Geocod.io in my system at work. As with the other clients, you need to pass in an instance of an HttpClient as well as your API key to a new instance of GoogleMapsClient.

Since I did release this as a NuGet package, available here, I did fully implement the different APIs I wanted to integrate. The client currently implements the Distance Matrix, Elevation, Geocode, and TimeZone APIs. I don't do request validation within the client and instead defer to the responses to indicate if there's any errors. I am thinking about implementing validation in the future, just haven't settled on how to do it, and currently work is keeping me quite busy in real life.

Here's how to use the client. For a short example of the client in use, please take a peek at the linked video above.

var googleMaps = new GoogleMapsClient(httpClient, "{key}");

Get Geocode

var geocode = await googleMaps.GetGeocodeAsync(
    "1600 Pennsylvania Ave NW, Washington, DC 20500"
);

Get Reverse Geocoding

var reverseGeocode = await googleMaps.GetReverseGeocodeAsync("38.897675,-77.036547");

Get Elevation

var elevation = await googleMaps.GetElevationAsync("38.897675,-77.036547");

Get Time Zone

var timeZone = await googleMaps.GetTimeZoneAsync("38.897675,-77.036547");

Get Distance Matrix

var distanceMatrix = await googleMaps.GetDistanceMatrixAsync(
    "East Capitol St NE & First St SE, Washington, DC 20004",
    "1600 Pennsylvania Ave NW, Washington, DC 20500"
);

HttpClient Hanging Inexplicably

As I was building the clients for the Carmine.io, Geocod.io, and Kraken.io APIs, I ran into an issue with posting to Kraken. The calls were just hanging without an error, besides the timeout exception that eventually triggers, but I don't count that. I spent hours scouring the Internet and StackOverflow trying to figure out why to no avail.

Eventually I stumbled onto the answer, and I'll be honest I'm not sure how I did, but I figured it would be a good thing to share. The problem ended up being in the nesting (of sorts) of the using statements for the contents and message. For example, from the Kraken source:

using (var content = new MultipartFormDataContent())
using (var stringContent = new StringContent(body))
using (var byteContent = new ByteArrayContent(request.FileBlob))
using (var message = await Client.PostAsync(request.Endpoint, content)) {
    content.Add(stringContent, "json");
    content.Add(byteContent, "file", request.FileName);

    return await message.Content.ReadAsStringAsync();
}

The above would trigger the issue and it came down to the message using statement being nested in with the other ones. I presume that since the POST was initiated before the content was established right below it caused it to hickup and hang. The solution was to separate out the message into a new using statement, like:

using (var content = new MultipartFormDataContent())
using (var stringContent = new StringContent(body))
using (var byteContent = new ByteArrayContent(request.FileBlob)) {
	content.Add(stringContent, "json");
	content.Add(byteContent, "file", request.FileName);

	using (var message = await Client.PostAsync(request.Endpoint, content)) {
		return await message.Content.ReadAsStringAsync();
	}
}

Now everything works as expected. So, be careful with using statement nesting because it can throw off the expected order of operations and might spend hours looking for a bug that didn't really exist, just like I did.

Kraken.io API Implementation in C#

Kraken.io is an image optimization service. I use it at work as part of an automated task from our system to optimize photos taken on job sites. As I'm writing this we've uploaded 3.24 million photos with a raw file size of 2.08 TB which were optimized down to 1.05 TB or about 50% saved. Those savings go a long way when storing, especially in the cloud.

There is an official Kraken.io client for .NET, but I didn't really feel comfortable with it. It just looked overly complex, so I opted for creating my own client. Also, when we first started using Kraken.io I had to make it as a scheduled task in Salesforce so I had to make my own client regardless. Eventually I ported it out of Salesforce and into our system because the Salesforce limitations and the amount of hoops I had to jump through to make it work were rediculous. Porting it to .NET and into our system allowed me to make the automated process do what it needed to without worrying about limits and constraints.

The client is available as a NuGet package here and targets .NET Standard 1.3.

To use the client simply create an instance of KrakenClient and pass in an instance of HttpClient and your API access and secret keys. For a short example of how it works, take a look at the linked video above.

On a side note, I really wish Kraken would just use a single API key like Carmine.io, Geocod.io and Google Maps, to name a few. The access and secret keys don't really do anything other than getting passed into requests. A single key would accomplish the same job. If there was an actual purpose to them like calculating a request signature like the AWS APIs, then they'd make sense, but they don't so it's an area where the API could be simplified in the future.

Let's get back on topic.

var kraken = new KrakenClient(httpClient, "{accessKey}", "{secretKey}");

Optimize

var optimize = await kraken.GetOptimizeAsync("{filePath}");

Optimize Wait

var optimizeWait = await kraken.GetOptimizeWaitAsync("{filePath}");

Download

var download = await kraken.DownloadAsync("{krakedUrl}");

Geocod.io API Implementation in C#

Geocod.io is a simple and straightforward geocoding service. I use it at my work to get the positions of job sites needed by some other automated processes.

The client is available as a NuGet package here and targets .NET Standard 1.1.

To use the client, create an instance of GeocodioClient and pass in an instance of HttpClient and your API key. For a short example of how it works, take a look at the linked video.

var geocodio = new GeocodioClient(httpClient, "{key}");

Once you have the client instance you can use the built in helper methods or compose custom requests. For example:

Get Geocode

var geocode = await geocodio.GetGeocodeAsync("1600 Pennsylvania Ave NW, Washington, DC 20500");

Get Geocode Batch

var geocodeBatch = await geocodio.GetGeocodeBatchAsync(new[] {
    "1600 Pennsylvania Ave NW, Washington, DC 20500",
    "East Capitol St NE & First St SE, Washington, DC 20004"
});

Get Reverse Geocode

var reverse = await geocodio.GetReverseGeocodeAsync("38.897675,-77.036547");

Get Reverse Geocode Batch

var reverseBatch = await geocodio.GetReverseGeocodeBatchAsync(new[] {
    "38.897675,-77.036547",
    "38.898976,-77.038219"
});

Carmine.io API Implementation in C#

Carmine.io is a fleet tracking service we use at my work. I integrated it into our system so I could to some more advanced vehicle to job site correlations. I had to make my own client to tap into their API and I want to share it.

It is available as NuGet package here and targets .NET Standard 1.1.

To use the client you just need to create an instance of CarmineClient and pass in an instance of HttpClient and your API key. Using dependency injection you can make this pretty simple, especially when it comes to the HttpClient instance. Take a peek at the linked video for a short example of how it works.

var carmine = new CarmineClient(httpClient, "{key}");

Once you have the client instance, you can just use the helper methods for quick operations, or build a custom request and pass it to the appropriate method. For example:

Get a List of Vehicles

var vehicles = await carmine.GetVehiclesAsync();

Get a Vehicle

var vehicle = await carmine.GetVehicleAsync("{vehicleId}");

Get a List of Users

var users = await carmine.GetUsersAsync();

Get a User

var user = await carmine.GetUserAsync("{userId}");

Get a List of Trips

var trips = await carmine.GetTripsAsync();

Get a Trip

var trip = await carmine.GetTripAsync("{tripId}");

Get Waypoints for a Trip

var waypoints = await carmine.GetWaypointsAsync("{tripId}");