arrowHome arrow TechBlog Tuesday, 07 September 2010  
Main Menu
Home
Quality Snaps
TechBlog
PennCharts
Blog
Family
Auld Frosties
Links
Players
Players (uni)
Players (camb)
Contact Us
Pennchart Recent
pennchart radio

Wow, that was actually easy (skype api)

Requirement
To use skype API to place a call at a future point in time, navigate through some phone options (”press one for…”) and leave a voicemail from a WAV file.

This would likely be a bat file called make_call
make_call.exe [phone_number] [wav_file] [navigation_sequence]

Background

Babies...Phones

Childcare is frequently referred to as healthcare in my household — a necessary break to recharge the adult batteries. In fact, we chose our gym based on its provision of such services. The problem is that requests to have little johnny taken care of are over-subscribed; too much demand (parents) chasing too few services (daycare). So the gym in question has a policy of only allowing parents to book 2 days ahead. And a day, in its eyes, starts at midnight. The result is that we have to make a reservation by phone at one minute past midnight. In days of old this would have been no problemo, but now it’s a non-starter to infringe on precious sleeping rights

Skype API
As it would happen, there are 3 APIs out there; Python, Java & COM. Trying to be impartial, I decided to use whichever API had the most samples. Which was Microsoft, of course :)

The Good
Although I did read something giving the API a good slaggin for speed, it seemed mighty easy to program against.
ICall call = skype.PlaceCall(callee);
That’s code even a numpty like me can believe in. The whole thing took <4 hours to get going. Va va vooooom!

The Bad
Changing audio properties for playback of the WAV required installation of an (albeit free) driver, Virtual Audio Cable. All documented here.
It was all made a little trickier by testing via Remote Desktop. Remember to click the Local Resources options tab and configure Remote Audio.

The Ugly
The only rubbish part of the whole thing is that the API doesn’t allow you to authenticate, meaning you had better check the “sign me in automatically” option to skype or you will nae get it working. It also forced me to use a scheduled task as opposed to “at”, which would have been preferable.

OK, another rubbish part is that WAV files have to conform to this spec
File: WAV PCM
Sockets: raw PCM samples
16 KHz mono, 16 bit

and the otherwise loveable Windows 7 doesn’t have a proper sound recorder anymore (grrrr…Microsoft). Their one records in _wma_ format. WTB?? Anyway, you can grab a copy of XP’s sndrec32.exe and continue to use that (albeit with dodgy warnings). But this isn’t skype’s fault, so let’s move on.

C# Code
Precious little to it beyond sleeping and getting duration on the WAV.


using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using SKYPE4COMLib;

namespace SkypeTest
{
class Program
{
static void Main(string[] args)
{
try
{
string DTMF = "";
if (args.Length < 3)
{
Usage();
System.Environment.Exit(0);
}
string callee = args[0];
string wavFile = args[1];
int answerDelay = Convert.ToInt32(args[2]);
if (args.Length == 4)
{
DTMF = args[3];
}
int millisecondsToWait = GetSoundLength(wavFile);
SKYPE4COMLib.Skype skype = new Skype();

if (skype.Client.IsRunning == false)
{
skype.Client.Start(true, true);
}
skype.Attach(6, true);
ICall call = skype.PlaceCall(callee, null, null, null);

while (call.Status != TCallStatus.clsInProgress)
{
if (call.Status == TCallStatus.clsBusy ||
call.Status == TCallStatus.clsCancelled ||
call.Status == TCallStatus.clsRefused ||
call.Status == TCallStatus.clsFailed)
{
throw new ArgumentException("Call problems");
}

Thread.Sleep(500);
}
Thread.Sleep(answerDelay);
if (DTMF != "")
{
call.DTMF = DTMF;
Thread.Sleep(1500);
}
call.set_InputDevice(TCallIoDeviceType.callIoDeviceTypeFile, wavFile);
Thread.Sleep(millisecondsToWait);
Thread.Sleep(500); // half a second for luck

// Night night
if (call.Status != TCallStatus.clsFinished) call.Finish();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
static private void Usage()
{
Console.WriteLine("Usage: SkypeTest [phone_number/user] [wav_file] [delay_seconds] [[control char]]");
Console.WriteLine("Example: SkypeTest ppenn11 #c:\temp\applause.wav 3 #");
}
static int GetWavFileLength(string wavFile)
{
FileInfo fi = new FileInfo(wavFile);
long fileSize = fi.Length;
double secondsExact = ((fileSize - 44)/(44100*(16/2))/2);
return Convert.ToInt32(Math.Ceiling(secondsExact));
}
[DllImport("winmm.dll")]
private static extern uint mciSendString(
string command,
StringBuilder returnValue,
int returnLength,
IntPtr winHandle);

public static int GetSoundLength(string fileName)
{

StringBuilder lengthBuf = new StringBuilder(32);

mciSendString(string.Format("open \"{0}\" type waveaudio alias wave", fileName), null, 0, IntPtr.Zero);
mciSendString("status wave length", lengthBuf, lengthBuf.Capacity, IntPtr.Zero);
mciSendString("close wave", null, 0, IntPtr.Zero);

int length = 0;
int.TryParse(lengthBuf.ToString(), out length);

return length;

}

}
}

Respect to some geez called the uberoverlord.

Converting Lat/Long to/from Web Mercator in C#

ESRI has a utility class for converting Lat Long from/to Web Mercator for use with bing/google maps.

They provide this functionality through a dll (ESRI.ArcGIS.Client.Bing) which requires Silverlight to be installed. Don’t have/want Silverlight on your web server, don’t have/want ArcObjects? Then nab this:

private void WebMercatorToGeographic(double mercatorX, double mercatorY, out double lat, out double lon)
{
if ((mercatorX < -20037508.3427892) || (mercatorX > 20037508.3427892))
{
throw new ArgumentException(”Point does not fall within a valid range of the mercator projection.”);
}
double x = mercatorX;
double y = mercatorY;
double num3 = x / 6378137.0;
double num4 = num3 * 57.295779513082323;
double num5 = Math.Floor((double)((num4 + 180.0) / 360.0));
double num6 = num4 - (num5 * 360.0);
double num7 = 1.5707963267948966 - (2.0 * Math.Atan(Math.Exp((-1.0 * y) / 6378137.0)));
lat = num6;
lon = num7*57.295779513082323;
}

private void GeographicToWebMercator(double lat, double lon, out double mercatorX, out double mercatorY)
{
if ((lat < -90.0) || (lon > 90.0))
{
throw new ArgumentException(”Point does not fall within a valid range of a geographic coordinate system.”);
}
double num = lat * 0.017453292519943295;
double x = 6378137.0 * num;
double a = lon * 0.017453292519943295;

mercatorX = x;
mercatorY = 3189068.5*Math.Log((1.0 + Math.Sin(a))/(1.0 - Math.Sin(a)));
}

ArcGIS Web Editing Quagmire

We have a client with a slick-looking Flex GIS client. All good, but now they want to add editing to the mix. Editing with Flex isn’t until 9.4 (why is it that every feature you ever want is in the _next_ version?), which is out of the timeframe for delivery so we’re stuck between a rock and a hard place; the stubborn rock of the ADF and the hard place of writing product where there is none in Flex/WCF/SOE.

ADF is pretty well documented. For Flex we would have to have an architecture like so:

Flex <-> WCF <-> EditingSOE <-> Nonversioned / Default Geodatabase

Flex Woes

Here are just some of my Flex editing woes :

1) We have committed to doing job workflow for approving etc (e.g. using M&M SessionManager/ESRI JTX or Workflow Foundation). This is a major headache because it implies that we will be doing versioned editing. The REST API does not give you the ability to switch versions on-the-fly. With a stateful mapserverobject accessed by the ADF you can create/change versions.
2) Client-side editing (i.e. manipulating graphics) in flex might involve some effort without the help of an esri library. [Glenn has found this
http://thunderheadxpler.blogspot.com/2008/12/editing-graphic-geometry.html]. Second biggest woe under this heading is undo-redo.
3) Tight time constraints, the ADF editing is pretty much out-of-the-box. How long will flex editing take to implement?

Elephant in the Room

And here, for completeness, is the elephant in the room (ADF):

1) Expectation set by delivering a Flex client of a responsive, AJAX-y interface without fuss. ADF is a lumbering fool.
2) Getting the existing Flex interface to seamlessly integrate with ADF.
3) Ongoing reliability concerns, issues with stateful services.

Time to get prototyping…

NUnit /noshadow shocker

Today was one of those days…when you realize the limitations of your technical capabilities. Which in my case, is knee-high to a grasshopper.

In the course of trying to inch towards the twenty-first century, Ruprict has yet again saved my bacon from a problem which I thought was intractable.
After unsuccessfully dabbling with CIFactory, we’re using Hudson for continuous integration for our Flex project. It is beautifully simple to install & configure, and seems to be unobtrusive to the developer, except when you break the build and the results are emailed to some of your worst enemies co-developers.

Anyway, I was midway through putting the WCF Rest stuff (thanksbe to redsolo) into Hudson when I started getting bizarre errors upon trying to nunit my test project:

System.IO.FileLoadException : Could not load file or assembly ‘Newtonsoft.Jso
n, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null’ or one of its dependen
cies. Access is denied.

But hang on, the Newtonsoft.Json.dll is in the same folder as the test project, what are you telling me?? So, what had worked perfectly in TDD.net inside Visual Studio (i.e. running my tests) was suddenly not so in nunit-console.exe or the nunit GUI.
So after much faffing around I handed the problem off to Rupes and he found a solution. Should you get this unexpected glitch, try applying the /noshadow tag from commandline (or equivalent option in the gui).

Salvation: REST Print Service for ArcGIS Server 9.3

At version 9.3*, the non-ADF ArcGIS web clients (Flex, Javascript, Silverlight) don’t support high quality printing. By high quality I mean variable resolution (so as to support dpi greater than 96) and the ability to use the template authoring in ArcGIS Desktop for a richer, smoother cartographic experience. If you want to create a hard copy of a map in, say Flex, printing the screen is the only option out-of-the-box. If you want to create an A3-sized PDF map with legend, borders, scale bars, North Arrows etc then you are up a murky creek with nothing but a hole-ridden wooden spoon to paddle out with.

Domenico Ciavarella came up with a bravissimo solution to the problem, tailored to the ADF, which made use of a ServerObjectExtension, LayoutSOE. Basically it involved invoking the SOE to output the PageLayout from ArcObjects. I hacked that and found that it worked fine but with the following deficiencies for the REST world:-
- No support for transparency in graphics (because ArcMap doesn’t support that, doh!)
- Not REST based, you needed to have the layers ahead of time in an MXD. This was a configuration nightmare to make sure that the layers in the print service MXD were the same as the layers in each map service that the Flex client was using. And if you were consuming other people’s map services and they changed the symbology then you were SOL.
- Too slow, adding/removing layers dynamically was taking minutes of time (especially the rasters).

Anyway, as luck would have it, my homey introduced me to a clued-up chap called Vish at this year’s ESRI Developer Conference in Palm Springs and he pointed me in a radically different direction. Kind of like Skynet in Terminator 2 after they find the remnants of Arnie from T1. I was being so amateur. What would Christian Bale think?

REST to the RESCUE

REST printing with ExportMap is fast and furious. All I would need to do was:

  • Export the images from each Map Service
  • Deserialize and draw the graphics with GDI+
  • Draw a legend, using the ESRI SOAP API
  • Export Templates (PageLayout) via an ArcObjects SOE
  • Fuse the whole lot into a single image
  • Handle writing to a PDF

To continue the Bale theme, albeit in a Adam West kinda way, to the Batcave!

The Big Picture

Image


From the client perspective, all this architecture (REST service) had to do was:

  1. Provide a list of templates (e.g. Facilities Map, Transportation Map)
  2. Output the Map with the given format (e.g. PDF/PNG) from the selected template, including current layers/graphics/extent (WYSIWYG Printing)

Two methods! How hard could that be? Well, it was more than an afternoon’s work, but I’ll spare the vanilla stuff.

SOE what?

I half-inched** the aforementioned LayoutSOE to provide a user-friendly way of publishing & listing templates via the black magic of a ServerObjectExtension.
This gives the user the ability to specify templates in Catalog:
Image

To be honest, I was a little scared of the SOE thing. All this regasm/gaccing. DCOM is something I last did with VB6 a decade ago. To allay my cross-process “Unexpected Failure” fears, I made all types plain strings and arrays.

The SOE had but two functions:

  • Return Array of Templates. Done! That’s my kinda coding
  • Return PrintTemplateInfo. This was an object holding the PageLayout information e.g. map bounds, the legend bounds (if any). It was also responsible for generating the template as a PNG, for consumption by the REST service. More on that later

The second of these involved some reasonably involved ArcObjects, but I checked out everything in VBA before committing to .NET SOE. Interactively discovering ArcObjects is sooo much easier than try-compile-test in .NET. Once that PageLayout metadata was extracted, time to export the image with marginalia on it and an extent set so as to draw a scalebar. Again, the LayoutSOE helps with all that. Tante Grazie, Domenico!
Boom, baby! We’re ready to roll onto the sexy REST side. Time to let ArcObjects fade into obscurity, may it (and its retarded non-exception handling) never cross our paths again…

REST easy
The core of this architecture is a WCF-assisted REST service, which has no ties to ArcGIS Server except for connecting to the SOE. So all it needs is the Web ADF Runtime (free again at 9.3.1). The service exposes a GET service to GetTemplates, which is pretty much a passthrough to the SOE. The other service is a POST service to CreateMap, which is where the meat of the matter lies. Fasten your seatbelts, I’m going to hit the code button a few times…

Export Templates (PageLayout) via an ArcObjects SOE
The first thing that CreateMap had to do was generate the template with appropriate marginalia and scalebar. Not only was the template returned as a URL but also the extents of the map and legend within the pagelayout. From the map bounds the size was calculated (dpi * inches).

Export the images from each Map Service
CreateMap’s primary duty was to draw images from each map service, remembering the extent and the visible layers. This entailed nothing more than taking the incoming print request and converting it to a URL for executing ExportMap on the REST server. I used PNG so as not to lose quality.
So, having built a URL like “http://localhost/ArcGIS/rest/services/Basemaps/Street/MapServer/export?bbox=7659147.07493616,704961.792450929,7668305.86853904,711693.897150482&bboxSR=2913&imageSR=2913&layers=show:0,1&size=761,653&format=png24&dpi=96&f=json&transparent=false”
The extent you ask for is not the extent that you get when calling ExportMap, so we take that too. It will help with graphics transformation.

Here’s the down & dirty on getting an image from the ESRI REST service

WebRequest request = WebRequest.Create(url);
WebResponse response = request.GetResponse();
remoteStream = response.GetResponseStream();
readStream = new StreamReader(remoteStream);
System.Drawing.Image img = System.Drawing.Image.FromStream(remoteStream);

From this small acorn the might oak of REST printing will flourish…

Deserialize and draw the graphics with GDI+
Cycling through the graphics was a little daunting because it entailed diving into GDI+. But it turned out to be surprisingly straightforward, unArcObjects-esque.
The only snaggette was transforming coordinates to page coordinates, which I had to do myself. I didn’t get into GIS to sully my hands with this sort of thing, but anyway 6th grade Math(s) sufficed.
An example of drawing a square marker…


private void DrawSquare(Graphics g2d, float x, float y, float size)
{
g2d.FillRectangle(_symBrush, (float)(x - 0.5 * size), (float)(y - 0.5 * size), (float)size, (float)size);
g2d.DrawRectangle(_symPen, (float)(x - 0.5 * size), (float)(y - 0.5 * size), (float)size, (float)size);

}

Draw a legend, using the ESRI SOAP API
We had already dabbled with the Legend in Flex because the Flex API doesn’t bother drawing it for you.
So this was a case of calling that and drawing the results into a .NET image, again with GDI+.


MapServerProxy93 mapProxy = new MapServerProxy93(mapServiceURL.Replace("/rest/", "/"));

ImageType imgType = new ImageType();
imgType.ImageFormat = esriImageFormat.esriImagePNG24;
imgType.ImageReturnType = esriImageReturnType.esriImageReturnMimeData;
return mapProxy.GetLegendInfo(mapProxy.GetDefaultMapName(), null, null, imgType);

Fuse the whole lot into a single image
OK, OK, you have all of these images, how do you blend them into a single one fit for printing? Again, GDI+ to your salvation.
You grab a graphics context with the bottom image, which would be the first map service to draw (e.g. basemap aerial/street etc).

//create the graphic from the original base image
g2d = Graphics.FromImage(bottomImage);

Once you have that then Robert is your father’s brother.


static private void AddImageToGraphics(Graphics g2d, Image image, int x, int y, float imageOpacity)
{
System.Drawing.Imaging.ColorMatrix cm = new System.Drawing.Imaging.ColorMatrix();
cm.Matrix00 = cm.Matrix11 = cm.Matrix22 = cm.Matrix44 = 1;
cm.Matrix33 = imageOpacity;
System.Drawing.Imaging.ImageAttributes ia = new System.Drawing.Imaging.ImageAttributes();
ia.SetColorMatrix(cm);

g2d.DrawImage(image, new System.Drawing.Rectangle(x, y, image.Width, image.Height), 0, 0, image.Width, image.Height,
GraphicsUnit.Pixel, ia);
g2d.Save();

}

That’s it, you can keep doing that ad finitum. So, stack all your map service images, one on top of the other. Then add your graphics. Then your legend. Finally, put the template on top of it and you are done.

Handle writing to a PDF
Ah, but Imaging.Formats doesn’t include PDF. Nope, for that we’ll have to use iTextSharp — a free PDF writer for C#. It’s pretty simple to use.
Couple of lines of code to add the image to the PDF and finito.


iTextSharp.text.Image image = iTextSharp.text.Image.GetInstance(mapImage, null);
cb.AddImage(image);

Flex fool
Suffice to say that from the Flex client it was necessary to serialize the layers and the graphics to JSON. There were a few niggly parts to that, but mostly ESRI’s json serializer did the trick.

That’s about it, well, plenty more to tell but I can hear gongs in the background — I am out of time. At the end you have a blazing fast high quality cartographic map production engine for the web. As Arthur Daley once said, on ne peut pas dire plus juste que ca.


* All this will be remedied at 9.4 of ArcGIS. Why does it seem that half my gis life has been spent writing features that were just around the corner in the next version of the product?
** pinched, Cockney Rhyming slang, innit.

Testing

ipsum lorum why have i forgotten my latin?

Hello world!

Joomla! is the ideal tool for developing small to large dynamic community websites, intra company portals, corporate portals, weblogs and much more.

The MojoBlog component is a integration of WordPress within Joomla, Our aim is to develop a more semantic, flexible and powerful blogging tool for Joomla! users.

Frosties RandomSnap
top of page