Tuesday, September 17, 2013

Converting latitude and longitude to UTM using ProjNet

When you need to convert latitude and longitude (Geographic coordinate system) into UTM (Universal Transverse Mercator coordinate system) in C# you can choose from several libraries. However I couldn't find any that would do all the work for me.

Here is a little code that you can use together with Proj.Net library (available also using NuGet) to convert two doubles like 49.89463 and 15.24215 into string "33U 517392 5526944".

private static string GetBand(double latitude)
{
    if (latitude <= 84 && latitude >= 72)
        return "X";
    else if (latitude < 72 && latitude >= 64)
        return "W";
    else if (latitude < 64 && latitude >= 56)
        return "V";
    else if (latitude < 56 && latitude >= 48)
        return "U";
    else if (latitude < 48 && latitude >= 40)
        return "T";
    else if (latitude < 40 && latitude >= 32)
        return "S";
    else if (latitude < 32 && latitude >= 24)
        return "R";
    else if (latitude < 24 && latitude >= 16)
        return "Q";
    else if (latitude < 16 && latitude >= 8)
        return "P";
    else if (latitude < 8 && latitude >= 0)
        return "N";
    else if (latitude < 0 && latitude >= -8)
        return "M";
    else if (latitude < -8 && latitude >= -16)
        return "L";
    else if (latitude < -16 && latitude >= -24)
        return "K";
    else if (latitude < -24 && latitude >= -32)
        return "J";
    else if (latitude < -32 && latitude >= -40)
        return "H";
    else if (latitude < -40 && latitude >= -48)
        return "G";
    else if (latitude < -48 && latitude >= -56)
        return "F";
    else if (latitude < -56 && latitude >= -64)
        return "E";
    else if (latitude < -64 && latitude >= -72)
        return "D";
    else if (latitude < -72 && latitude >= -80)
        return "C";
    else
        return null;
}

private static int GetZone(double latitude, double longitude)
{
    // Norway
    if (latitude >= 56 && latitude < 64 && longitude >= 3 && longitude < 13)
        return 32;

    // Spitsbergen
    if (latitude >= 72 && latitude < 84)
    {
        if (longitude >= 0 && longitude < 9)
            return 31;
        else if (longitude >= 9 && longitude < 21)
            return 33;
        if (longitude >= 21 && longitude < 33)
            return 35;
        if (longitude >= 33 && longitude < 42)
            return 37;
    }

    return (int)Math.Ceiling((longitude + 180) / 6);
}

public static string ConvertToUtmString(double latitude, double longitude)
{
    if (latitude < -80 || latitude > 84)
        return null;

    int zone = GetZone(latitude, longitude);
    string band = GetBand(latitude);

    //Transform to UTM
    CoordinateTransformationFactory ctfac = new CoordinateTransformationFactory();
    ICoordinateSystem wgs84geo = ProjNet.CoordinateSystems.GeographicCoordinateSystem.WGS84;
    ICoordinateSystem utm = ProjNet.CoordinateSystems.ProjectedCoordinateSystem.WGS84_UTM(zone, latitude > 0);
    ICoordinateTransformation trans = ctfac.CreateFromCoordinateSystems(wgs84geo, utm);
    double[] pUtm = trans.MathTransform.Transform(new double[] { longitude, latitude });

    double easting = pUtm[0];
    double northing = pUtm[1];

    return String.Format("{0}{1} {2:0} {3:0}", zone, band, easting, northing);
}

Thursday, March 14, 2013

ASP MVC WEB API OData IComparable Error

Did you get this error?
<message>An error has occurred.</message>
<exceptionmessage>
The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'.
</exceptionmessage>
<exceptiontype>System.InvalidOperationException</exceptiontype>
<stacktrace>
<innerexception>
<message>An error has occurred.</message>
<exceptionmessage>At least one object must implement IComparable.</exceptionmessage>
<exceptiontype>System.ArgumentException</exceptiontype>

For something as simple as this?
[Queryable(EnsureStableOrdering = false)]
public class TestClass
{
    public string Name { get; set; }
    public byte[] Data { get; set; }
}

public class ValuesController : ApiController
{
    [Queryable]
    public IQueryable Get()
    {
        var data = new TestClass[]
        {
            new TestClass { Name = "1", Data = Encoding.UTF8.GetBytes("abc")},
            new TestClass { Name = "2", Data = Encoding.UTF8.GetBytes("xyz")},
            new TestClass { Name = "3", Data = Encoding.UTF8.GetBytes("")},

        };
        return data.AsQueryable();
    }
}

The bad guy that is making troubles here is the byte array. All you need to do is to add one parameter to your Queryable attribute.

[Queryable(EnsureStableOrdering = false)]


Thursday, February 2, 2012

ObservableCollection<T>.Synchronise(IEnumerable<T> newItems)

Removes items from ObservableCollection that are redundant and adds items that are missing, so eventually the ObservableCollection contains the same items in the same order as passed enumeration.

public static void Synchronise<T>(this ObservableCollection<T> currentItems, IEnumerable<T> newItems)
{
    var newIndex = 0;
    foreach (var item in newItems)
    {
        var currentIndex = currentItems.IndexOf(item);
        if (currentIndex == -1)
        {
            currentItems.Insert(newIndex, item);
        }
        else
        {
            while (currentIndex > newIndex)
            {
                currentItems.RemoveAt(newIndex);
                currentIndex--;
            }
        }
        newIndex++;
    }
    while (currentItems.Count > newIndex)
        currentItems.RemoveAt(newIndex);
}

Sunday, December 4, 2011

Turn your laptop into Web Cam Web site

My father in law asked me few days ago if there is any way how he could watch his garden using web cam. Yes, he is interested in watching moles destroying his lawn.

So I had this idea, that you can start a simple app on your laptop that captures webcam image every few seconds and uploads it to web server of your choice using ftp. I tried it and found that it works really nicely. I called it WebCamWeb.

 

Tuesday, January 25, 2011

Sample AJAX Web Application for FCO Travel Advisory Service

We published Foreign & Commonwealth Office data recently and here is a short sample that shows you how these data can be used from your own application.

I decided to create a sample AJAX based web application that displays UK embassies on a map as you can see on the following picture.


Create this app in two steps


I started with empty html file. As I use jQuery and Bing Maps, I added links to javascript files in my header. Then I added an onload function that loads the default map.

<html>
<head>

  <script type="text/javascript" src="http://ajax.microsoft.com/ajax/jquery/jquery-1.4.4.min.js" ></script>
  <script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2"></script>  

  <script type="text/javascript">
    $(document).ready(function () {
      // load map
      map = new VEMap('myMap');         
      map.LoadMap();
    });
  </script>

</head>
<body>
  <div id='myMap' style="position:relative; width:100%; height:100%;"></div>
</body>
</html>

The second step is to add the code that uses our service and loads the embassy data. I added the following script block after the first one. It uses JQuery to asynchronously load the data from our OData service. Once the data are loaded it loops through the records and creates a new push pin for each embassy with its latitude and longitude.

<script type="text/javascript">
  $(document).ready(function () {
    // load embassy positions
    var url = "http://traveladvisor.cloudapp.net/TravelAdvisorService.svc/Embassies";
    $.getJSON(url, ProcessData);
  });
 
  function ProcessData(result)
  {
    // for each embassy 
    for (var id in result.d)
    {
      // create new pushpin on the map
      var embassy = result.d[id];
      AddPushpin(embassy.Lat, embassy.Long, embassy.Title);
    }
  }
 
  function AddPushpin(latitude, longitude, title)
  {
    // ignore invalid latitude or longitude
    if (isNaN(latitude) || isNaN(latitude))
      return;
 
    // add pushpin
    var position = new VELatLong(latitude, longitude);
    var pushpin  = new VEShape(VEShapeType.Pushpin, position);
    pushpin.SetTitle(title);
    map.AddShape(pushpin);
  }
</script>

And that’s all, open your file in browser and you will get the result as seen on the screenshot above.