Jim Jackson

Character Counts.
Do the right thing.

Sponsors

RIA Services - Iterate Items in EntityQuery Object

I’m trying to get EF working with SL3, SSL, RIA Services et al. It’s a long road and I know there are issues with any route you decide to take when building a business app in Silverlight 3. In building previous iterations of my little program I used straight WCF services and had issues with host headers when using SSL and the cross domain access file.

RIA Services is definitely easier to work with in terms of the plumbing. It sets up a new handler in the web.config and if you work out your EF model properly, the data access and communications portions go relatively quickly.

The issue I was working through this evening was that I am using the ESRI mapping control and I could not get it to databind the graphics layer to a collection of graphics objects bound to a data context query. The backup plan is to iterate items in the data context query results. Good luck finding a helper post anywhere to write that Linq query! So here is one I finally found in a forum post on the RIA Services list. I lost the URL but here is the relavent code:

// Instance the list of objects we’ll use to
// build the map graphics.
List<MyMapPoint> lst = new List<MyMapPoint>();
// Query that gets the list of points from
// the database.
var queryhist = _MyContext.GetPointsQuery();
// Execute the query.
var qOp = _MyContext.Load(qhist, (pts) =>
{
    foreach (var pt in pts.Entities)
    {
        lst.Add(
                new MyMapPoint(
                pt.PointID,
                pt.PointName, 
                pt.Latitude,
                pt.Longitude
                );
    }
}, null);

I know I could probably serialize my query results directly into my list but I’m just not there yet (read: I don’t know how to do that!).

The points of interest viewer/editor should be up fairly soon at www.muddygps.com. Some time thereafter, the GPS trail routing will also get rolling and then I can start fooling with the race interface. That will be the fun part! Incidentally, the app as it exists right now on the internet is being completely rewritten (again) so it should get a lot faster soon.

Another item up for bids is going to be trying to work out why the cross domain access policy isn’t working right. If I make the entire SilverLight 3 portion of the site SSL it works but as soon as I make it SSL only for the RIA Services data calls, it makes squishing noises. Not going to worry about that one for now because the data access seems to be going relatively quickly for the size of data I’m throwing around.

I’m also building on some of Brad Abrams tutorials for SEO and deep linking. Hopefully that will get my site some traffic. Of course I can’t expect much at the moment since I’ve got a face but no content online.

Good Documentation…

Getting nationalized health care:

+ $3,000,000,000,000.00

Socializing the United States of America:

+ $10,000,000,000,000.00

Tax Hike to 'Evil Rich People' to raise less than 20% of funds needed for health care:

5.4%

Good documentation about how the new health care system will work:

Priceless

http://docs.house.gov/gopleader/House-Democrats-Health-Plan.pdf

Give ya two guesses where the rest of the funds for this travesty of liberty will come from but if you’re listening you’ll only need one.

Posted: Jul 15 2009, 12:28 PM by axshon | with no comments
Filed under:
Small Person, Big Life

My friends and a few work associates know some of the details of my last trip to China to adopt my second daughter. While there my first adopted daughter, then 4 years old, got mysteriously sick. Very very sick. My wife and I spent a lot of time and obscene amounts of money playing the Ugly American role and advocating for our child in a medical and political system not set up with any specific value placed on an individual life. I love the Chinese people (all of them) and the Chinese culture is a part of my life for the sake of my children but I’m still in 1969 when it comes to Communist governments…

Nuff said about that. Suffice it to say that anyone ever who stands between me and my family will have problems much larger than they imagined possible. So when my kid gets sick and nobody knows what the hell is going on it’s incomprehensibly frustrating. You pray, you weep, you hold her hand and smile and say it’s going to get better soon even though you’re not at all sure it will.

So life being what it is, sometimes it doesn’t get better. Thus the reason for this post. The Spohrs family lost their fight last week for their daughter’s life. I don’t know them and I never will but I do feel a small part of their pain.

They have a raffle going to raise funds for the March of Dimes that you can find on their site but my reason for posting here is that Bill Simser is offering his MVP MSDN Premium Subscription with Team Suite for raffle. He has instructions on his blog about how to get involved. Cost is 10.00 per entry and it’s going to a damn good cause.

Please do this and please pass it on.

Posted: Apr 13 2009, 08:16 AM by axshon | with no comments
Filed under:
Silverlight 2 Drag and Scroll Image

This is somewhat based on Shawn Wildermuth’s blog post for drag and drop. I had two problems with his post.

  1. I’m not scrolling a canvas.
  2. If your mouse leaves the control with the button down you are still dragging when your mouse reenters the control.

So here’s my tweaked version:

The control has a scroll viewer as the root element and a simple grid with one column and one row inside that. There is a large image inside the grid cell. There are events firing from the grid’s mouse down, up, move and grid leave events.

<UserControl x:Class="TestMouseScroll.Page"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Width="300" Height="300" Background
="Transparent" >
  <ScrollViewer
    x:Name="scrollViewImage"
    HorizontalScrollBarVisibility="Visible"
    VerticalScrollBarVisibility
="Visible" >
    <Grid
      x:Name="gridImageContainer"
      Background="Gray"
      ShowGridLines
="True"
      MouseLeftButtonDown
="grid_MouseLeftButtonDown"
      MouseLeftButtonUp="grid_MouseLeftButtonUp"
      MouseMove
="grid_MouseMove"
      MouseLeave
="grid_MouseLeave"
   
  >
      <Grid.ColumnDefinitions
>
        <ColumnDefinition></ColumnDefinition
>
      </Grid.ColumnDefinitions
>
      <Grid.RowDefinitions
>
        <RowDefinition></RowDefinition
>
      </Grid.RowDefinitions
>
      <Image Source
=http://www.inetres.com/gp/military/infantry/rifle/M107/M107_1.jpg
         Grid.Row="0" Grid.Column="0" >
      </
Image
>
    </Grid
>
  </ScrollViewer
>
</
UserControl
>

The code looks like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace TestMouseScroll
{
  public partial class Page : UserControl
  {

    bool isTracked = false;
    Point startDrag;

    public Page()
    {
      InitializeComponent();
    }

    private void grid_MouseLeftButtonDown(object sender,
      MouseButtonEventArgs e)
    {
      isTracked = true;
      // Get the starting mouseposition based on
      // where the mouse is positioned on the image,
      // not the relative postion inside the scroller
      startDrag = e.GetPosition(gridImageContainer);
    }

    private void grid_MouseLeftButtonUp(object sender, 
      MouseButtonEventArgs e)
    {
      isTracked = false;
    }

    private void grid_MouseLeave(object sender, MouseEventArgs e)
    {
      isTracked = false;
    }

    private void grid_MouseMove(object sender, MouseEventArgs e)
    {
      if (isTracked)
      {
        Point newPos = e.GetPosition(scrollViewImage);
        try
        {
          scrollViewImage.ScrollToHorizontalOffset(startDrag.X - newPos.X);
          scrollViewImage.ScrollToVerticalOffset(startDrag.Y - newPos.Y);
        }
        catch { }
      }
    }
  }
}

I think this is pretty straight forward and works nicely for me.

Incidentally, the Barrett was a sniper rifle introduced into the T/E of my unit some time between ‘91 and ‘93. I don’t know exactly when but it wasn’t there the first time I deployed and it was the second time. Used primarily as a vehicular countermeasure, it is still in use today. It’s an awesome addition to any infantry or STA unit firing accurately in the multi-kilometer range (v/s ~1,000 meters for standard 7.62). Note that the kevlar helmet has a USMC Corporal’s rank insignia on it. That’s E4. So a 21 year old professional warrior is trained and deployed with this little monster. Happens every day. There are people who have served in Congress and the House for many decades who cannot be trusted with a spitwad in a McDonalds straw but they have the power to send these fine gentlemen into harms way to voluntarily perform honorably and under stresses and conditions that would make normal men soil themselves. My point is that there ought to be a qualification process to be allowed to make our laws similar to the qualification it takes to become a Marine Sniper. An old Marine can wish…

Silverlight 2 Sys.InvalidOperationException: ImageError error #4001 in control Xaml1

I got this last night while trying to load 2,150 images into a grid. I’m working on a map tiling control for my application because the maps are very large and custom stitched for my application. Incidentally, when loaded up, my IE windows consumes 1.3 Gb of Ram. Um, probably not gonna work in production…

Anyway, it appears that there is a mechanism internal to the System.Windows.Media.Imaging.BitmapImage object that, when instanced using a Uri, will go in search of the thumbs.db object. I have not looked into the source code for this library and I have not tested to see if this is also happening when on the internet but it doesn’t really matter because it is an easy solution.

To resolve the issue I just opened the images folder in IE and viewed it in thumbnail mode. This magically generates the thumbs.db file and the error goes away when I run my application again.

Incidentally, Xaml1 is the auto-generated name of the <asp:Silverlight /> control that is dropped into the test aspx page.

Using Linq to XML with C# to Read Gpx Files

GPX is the standardized file format for GPS file exchanges. A GPX file can contain a lot of different kinds of information. Take a look at the schema here. In general, the major things that you will work with are:

Waypoints

A waypoint is a specific position that is manually marked by a user for future reference. So when you get to the suspension bridge, mark a waypoint and you can find it again later as well as tell everyone else about it.

Tracks

Tracks are where you've been. When I want to mark out a trail for users of my application, I set up my GPS on my bike and just go for a ride. GPS antennae have come a long way in the last few years and my inexpensive Garmin eTrex keeps pretty accurate markers even when I'm in a deep draw. When I get home I have a complete listing of a few hundred points on my route, depending on how far apart or how long to wait I've preset my GPS to mark between saved track points.

Routes

A route is what you load into your GPS. It's essentially a list of positions you build by looking at a map or a file you get from someone else's track. When loaded, it will direct you to each point along the route in the appropriate order.

My Garmin saves files in a GDB format which is proprietary for the product. I load this file onto a machine with Garmin MapSource and immediately save the file as a GPX. This gets it into the standardized format that nearly all other GPS units and mapping software can use and I'm ready to load my data. At the end of this post is a well formed (but incomplete) GPX file. The original file had about 6,500 lines in it.

The Code

It's really pretty straight forward once you realize that you need to pull in the namespace object and then include it in each call to an element. My initial run at this netted attribute values but no element values which was really frustrating. Also, when working with Xml, remember that an element that doesn't exist results in a null object reference so you'll see in the code how I handled that for each element. The biggest issue for me with Linq is the inability to debug line-by-line. Still it's crazy fast and I'm loading a lot of data in only a few lines of code.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.Text;

namespace LinqXMLTester
{
  public class GPXLoader
  {
    /// <summary>
    /// Load the Xml document for parsing
    /// </summary>
    /// <param name="sFile">Fully qualified file name (local)</param>
    /// <returns>XDocument</returns>
    private XDocument GetGpxDoc(string sFile)
    {
      XDocument gpxDoc = XDocument.Load(sFile);
      return gpxDoc;
    }

    /// <summary>
    /// Load the namespace for a standard GPX document
    /// </summary>
    /// <returns></returns>
    private XNamespace GetGpxNameSpace()
    {
      XNamespace gpx = XNamespace.Get("http://www.topografix.com/GPX/1/1");
      return gpx;
    }

    /// <summary>
    /// When passed a file, open it and parse all waypoints from it.
    /// </summary>
    /// <param name="sFile">Fully qualified file name (local)</param>
    /// <returns>string containing line delimited waypoints from
    /// the file (for test)
</returns>
    /// <remarks>Normally, this would be used to populate the
    /// appropriate object model
</remarks>
    public string LoadGPXWaypoints(string sFile)
    {
      XDocument gpxDoc = GetGpxDoc(sFile);
      XNamespace gpx = GetGpxNameSpace();

      var waypoints = from waypoint in gpxDoc.Descendants(gpx + "wpt")
              select new
              {
                Latitude = waypoint.Attribute("lat").Value,
                Longitude = waypoint.Attribute("lon").Value,
                Elevation = waypoint.Element(gpx + "ele") != null ?
                    waypoint.Element(gpx + "ele").Value : null,
                Name = waypoint.Element(gpx + "name") != null ?
                    waypoint.Element(gpx + "name").Value : null,
                Dt = waypoint.Element(gpx + "cmt") != null ?
                    waypoint.Element(gpx + "cmt").Value : null
              };

      StringBuilder sb = new StringBuilder();
      foreach (var wpt in waypoints)
      {
        // This is where we'd instantiate data
        // containers for the information retrieved.
        sb.Append(
          string.Format("Name:{0} Latitude:{1} Longitude:{2} Elevation:{3} Date:{4}\n",
          wpt.Name,wpt.Latitude,wpt.Longitude,
          wpt.Elevation, wpt.Dt));
      }

      return sb.ToString();
    }

    /// <summary>
    /// When passed a file, open it and parse all tracks
    /// and track segments from it.
    /// </summary>
    /// <param name="sFile">Fully qualified file name (local)</param>
    /// <returns>string containing line delimited waypoints from the
    /// file (for test)</returns>
    public string LoadGPXTracks(string sFile)
    {
      XDocument gpxDoc = GetGpxDoc(sFile);
      XNamespace gpx = GetGpxNameSpace();
      var tracks = from track in gpxDoc.Descendants(gpx + "trk")
             select new
             {
               Name = track.Element(gpx + "name") != null ?
                track.Element(gpx + "name").Value : null,
               Segs = (
                    from trackpoint in track.Descendants(gpx + "trkpt")
                    select new
                    {
                      Latitude = trackpoint.Attribute("lat").Value,
                      Longitude = trackpoint.Attribute("lon").Value,
                      Elevation = trackpoint.Element(gpx + "ele") != null ?
                        trackpoint.Element(gpx + "ele").Value : null,
                      Time = trackpoint.Element(gpx + "time") != null ?
                        trackpoint.Element(gpx + "time").Value : null
                    }
                  )
             };

      StringBuilder sb = new StringBuilder();
      foreach (var trk in tracks)
      {
        // Populate track data objects.
        foreach (var trkSeg in trk.Segs)
        {
          // Populate detailed track segments
          // in the object model here.
          sb.Append(
            string.Format("Track:{0} - Latitude:{1} Longitude:{2} " +
                         "Elevation:{3} Date:{4}\n"
,
            trk.Name, trkSeg.Latitude,
            trkSeg.Longitude, trkSeg.Elevation,
            trkSeg.Time));
        }
      }
      return sb.ToString();
    }
  }
}


 

GPX Sample File

<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<
gpx xmlns="http://www.topografix.com/GPX/1/1"
   creator="MapSource 6.13.7"
   version="1.1"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.garmin.com/xmlschemas/GpxExtensions/v3
       http://www.garmin.com/xmlschemas/GpxExtensions/v3/GpxExtensionsv3.xsd
       http://www.topografix.com/GPX/1/1
       http://www.topografix.com/GPX/1/1/gpx.xsd
">

  <
metadata>
    <
link href="http://www.garmin.com">
      <
text>Garmin International</text>
    </
link>
    <
time>2009-03-08T20:11:54Z</time>
    <
bounds
      maxlat="39.2971185"
      maxlon="-76.6951826"
      minlat="39.2035537"
      minlon="-76.8203088"/>
  </
metadata>

  <
wpt lat="39.2445616" lon="-76.7194497">
    <
ele>88.8067627</ele>
    <
name>001</name>
    <
cmt>16-SEP-08 8:50:11AM</cmt>
    <
desc>16-SEP-08 8:50:11AM</desc>
    <
sym>Residence</sym>
    <
extensions>
       <
gpxx:WaypointExtension
       xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3">
         <
gpxx:DisplayMode>SymbolAndName</gpxx:DisplayMode>
      </
gpxx:WaypointExtension>
    </
extensions>
  </
wpt>

  <
wpt lat="39.2422711" lon="-76.7213488">
    <
ele>38.3380127</ele>
    <
name>009</name>
    <
cmt>16-SEP-08 9:40:46AM</cmt>
    <
desc>16-SEP-08 9:40:46AM</desc>
    <
sym>Residence</sym>
    <
extensions>
      <
gpxx:WaypointExtension
      xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3">
        <
gpxx:DisplayMode>SymbolAndName</gpxx:DisplayMode>
      </
gpxx:WaypointExtension>
    </
extensions>
  </
wpt>

  <
trk>
    <
name>Vinyard Springs Loop</name>

    <
extensions>
      <
gpxx:TrackExtension
     
xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3">
        <
gpxx:DisplayColor>Transparent</gpxx:DisplayColor>
      </
gpxx:TrackExtension>
    </
extensions>

    <
trkseg>
      <
trkpt lat="39.2446415" lon="-76.7199907">
        <
ele>60.2197266</ele>
        <
time>2009-03-08T13:18:25Z</time>
      </
trkpt>
      <
trkpt lat="39.2445078" lon="-76.7193838">
        <
ele>88.0980225</ele>
        <
time>2009-03-08T13:19:23Z</time>
      </
trkpt>
      <
trkpt lat="39.2440145" lon="-76.7200792">
        <
ele>81.3687744</ele>
        <
time>2009-03-08T13:19:43Z</time>
      </
trkpt>
      <
trkpt lat="39.2435182" lon="-76.7208523">
        <
ele>82.3300781</ele>
        <
time>2009-03-08T13:20:04Z</time>
      </
trkpt>
      <
trkpt lat="39.2427701" lon="-76.7212304">
        <
ele>78.9654541</ele>
        <
time>2009-03-08T13:20:52Z</time>
      </
trkpt>
      <
trkpt lat="39.2417241" lon="-76.7208448">
        <
ele>65.0263672</ele>
        <
time>2009-03-08T13:21:43Z</time>
      </
trkpt>
    </
trkseg>
  </
trk>

  <
trk>
    <
name>Patapsco Boundry Ref</name>

    <
extensions>
      <
gpxx:TrackExtension
      xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3">
        <
gpxx:DisplayColor>DarkRed</gpxx:DisplayColor>
      </
gpxx:TrackExtension>
    </
extensions>
   
    <
trkseg>
      <
trkpt lat="39.2253216" lon="-76.7073387">
        <
ele>46.2806396</ele>
      </
trkpt>
      <
trkpt lat="39.2248415" lon="-76.7065894">
        <
ele>43.3966064</ele>
      </
trkpt>
      <
trkpt lat="39.2194385" lon="-76.7042182">
        <
ele>17.4411621</ele>
      </
trkpt>
      <
trkpt lat="39.2188285" lon="-76.7043919">
        <
ele>15.0377197</ele>
      </
trkpt>
      <
trkpt lat="39.2183665" lon="-76.7051846">
        <
ele>14.0764160</ele>
      </
trkpt>
      <
trkpt lat="39.2180424" lon="-76.7061647">
        <
ele>12.6343994</ele>
      </
trkpt>
      <
trkpt lat="39.2177812" lon="-76.7070982">
        <
ele>11.1925049</ele>
      </
trkpt>
      <
trkpt lat="39.2485097" lon="-76.8098993">
        <
ele>127.5119629</ele>
      </
trkpt>
    </
trkseg>
  </
trk>

</
gpx>


 

Posted: Mar 10 2009, 09:10 PM by axshon | with 6 comment(s)
Filed under: , , ,
SQL 2008 Geography and Geometry Data Type Problems

My plan was to take my application into SQL 08 from 05 since my hosting service now has it available. I was intent on killing all of my lat/long information and storing my geospatial data in geography columns.

Here is what I’m finding about the Geography and Geometry data types. Initially, I expected to that once in, I’d be able to get everything back out exactly as entered.

Not so much…

Here is what I am finding so far. When/if I figure each of these out I’ll post an update.

  1. Geography and Geometry are across a uniform surface. That means no elevation. So if I’m trying to track the distance between points A and B I can only do it as if looking directly down at a map.
  2. Data cannot be read by humans from geography and geometry types. Put the value in and see what it looks like when you select it back out. Not a big deal but it sucks when you’re trying to QA input procedures that have 600 points on a single track.
  3. Similar to the previous item, data is for input only. If I put in a geography point, I cannot easily extract just the latitude only from that column. I can do it by parsing and using .ToString() but ugh!

So in my data I’ll be storing the latitude, longitude, elevation and ALSO the geography-coded value for the latitude/longitude.

The upside is that it should make it easier to calculate trip plans for individual users since this is part of my plan to try and pay for that big expensive camera that I’ll be using to snap a zillion panoramas. In the end, what I’ve found is that the geography and geometry data types are primarily useful for making geospatial calculations, not for viewing the actual geospatial data itself.

Beyond Compare 3

A substantial part of a developer’s job seems to be the logistical details of getting ‘it’ from here to there. When you run a team the problem becomes larger. Even with source control, there are documents that should be updated that aren’t and things that should be done that are missed. The two tools that I use these days are Beyond Compare from Scooter Software for files and folders and Red Gate’s SQL Compare for databases. The former is dirt cheap, the later has gotten more expensive over the years.

I’ve used Beyond Compare for a number of years now and have always been impressed with the comprehensive but simple nature of the software. You can open the UI for the first time and stumble around for about 5 minutes and know what you’re doing. In addition, the support is truly outstanding. Getting upgrade keys and information about the product reminds me of working with the the auto shop I have been going to for 10 years or more. I walk in and they know me by name even if I haven’t been around for 6 months. They give me exactly the services I need, listen carefully to any concerns I have and refuse to charge me when out of the ordinary things happen. Scooter Software’s licensing model is very straight forward for a single developer: As long as I’m using it personally, I can put it wherever I want. I have it on my personal dev box, my wife’s machine and my company dev box. If I ever get rid of the company box, I’ve got a note-to-self to remove it. Simple model based on trusting your client.

I also use Beyond Compare to keep my local backups up to date. In all the time that I have been taking digital photographs, I think I’ve actually deleted a half dozen. Needless to say I have thousands. Before I set up with carbonite.com, I used a rotating system of 2 external hard drives. I keep one attached to my local machine and one in the fireproof safe. I put all new images on my local internal media drive and then do a compare on the external, moving anything that doesn’t exist over. Then, every week or so, I swap the drives in the safe and do another compare. It takes a minute or two and I know that all the pictures and movies my family has are protected in the safe. My wife’s machine also gets an occasional copy of all images and movies so she can show off when she’s out and about doing FCC related stuff.

I wish I had done that for my Silverlight program. If I had I wouldn’t have lost 3 months of work due to sheer stupidity and a WD drive that went thud…

At any rate, I highly recommend both of these companies and their products.

WCF Services in a Shared Hosting Environment

I’m currently using a relatively well known hosting service for my Silverlight 2 application. The problem that I’ve had in getting the app up and running is that in a shared hosting environment, I have no control over host headers and most of the other settings in IIS. So since my url is https://www.singletrax.com, the ops people also added https://singletrax.com to the host headers for my site.

The problem is the order in which they added them. The www address is the second in the list so when I try to link to my services, I get an exception: Exception has been thrown by the target of an invocation. This is because the service is trying to load the host header index of 0 and there ain’t nuthin’ you can do about it that I’ve been able to find!

Except this:

First, add an AppSetting to your web project:
<appSettings>
    <add key="HostIndex" value="1" />
</appSettings>

Next, create two new objects:

using System;
using System.Collections.Generic;
using System.Web;
using System.Configuration;
using System.ServiceModel;
using System.ServiceModel.Activation;

namespace MyNamespace.Web.Objects
{
   class CustomHostFactory : ServiceHostFactory
   {
      protected override ServiceHost 
        
CreateServiceHost(Type serviceType, Uri[] baseAddresses)
      {
        
int hostIndex = 
         Convert.ToInt32(ConfigurationManager.AppSettings
            ["HostIndex"].ToString());

         CustomHost customServiceHost = 
            new CustomHost(serviceType, baseAddresses[hostIndex]);

         return customServiceHost;
      }
   }

   class CustomHost : ServiceHost
   {
      public CustomHost(Type serviceType,
        
params Uri[] baseAddresses) : base(serviceType, baseAddresses)
     
{ }

      protected override void ApplyConfiguration()
      {
         base.ApplyConfiguration();
      }
   }
}

Then, in your .svc file, reference the custom host this way:

<%@ ServiceHost Language="C#" Debug="true"
 Service="MyNamespace.Web.Services.MyService"
 CodeBehind="MyService.svc.cs"
 Factory="MyNamespace.Web.Objects.CustomHostFactory" %>

This works well for me. I’d be interested in what other hosting services do. Incidentally, I made the index an app setting because I hope that as traffic increases I’ll be able to afford a dedicated or virtual hosting environment.

Posted: Feb 13 2009, 12:37 PM by axshon | with 1 comment(s)
Filed under: ,
Calculating Distances Between Latitude and Longitude – T-SQL – Haversine

I’ve got lots of lat/long information for my Silverlight 2 application and I’m now tinkering with getting the elevation view working in my spare time. The problem is that the distances are very small and the haversine formulae I’ve located on the internet are all either miles or kilometers. Not going to work when your next position is only 50 feet away. So I wrote the simple SQL function below based directly on Seth Long’s version here.

Now, since I’m storing positional latitude, longitude and intersection information in my database, it’s simple to just calculate the numbers. The input and output values are of course immutable, so I added the columns to the appropriate table and will update them during the data entry phase of normal operations.

The haversine formula is also known as the ‘great circle formula’ that gives you a distance across the surface of a sphere based on an average curvature value for that sphere. The actual pear shape of the earth is a calculated max diff of only about 20 km so I’m not particularly worried about the error in two points that are literally spitting distance from each other.

By the way, the constant value 20,890,584 is the earth’s average radius in miles (3,956.55) multiplied by 5,280 feet per mile. The wikipedia article on haversine states that the average radius of the earth is 6,367.45 Km and I’m assuming it’s correct. I’m defaulting to feet since my Garmin GPS’ standard output for relative elevation is in feet.

@R is the radius of the earth.

This kind of begs for a discussion of whether it is more appropriate to calculate these values on the client and save the bandwidth or do the work on the server and pass it over. I chose the server because I’m still trying to figure out if I can market this application in the SAAS category. In that light, it seems better to keep as much data instantly available as I can.

Here’s the function:

Create Function [dbo].[fnGetDistance]
(
      @Lat1 Float(18), 
      @Long1 Float(18),
      @Lat2 Float(18),
      @Long2 Float(18),
      @ReturnType VarChar(10)
)

Returns Float(18)

AS

Begin

      Declare @R Float(8);
      Declare @dLat Float(18);
      Declare @dLon Float(18);
      Declare @a Float(18);
      Declare @c Float(18);
      Declare @d Float(18);

      Set @R = 
            Case @ReturnType 
            When 'Miles' Then 3956.55 
            When 'Kilometers' Then 6367.45
            When 'Feet' Then 20890584
            When 'Meters' Then 6367450
            Else 20890584 -- Default feet (Garmin rel elev)
            End

      Set @dLat = Radians(@lat2 - @lat1);

      Set @dLon = Radians(@long2 - @long1);

      Set @a = Sin(@dLat / 2) 
                 * Sin(@dLat / 2) 
                 + Cos(Radians(@lat1))
                 * Cos(Radians(@lat2)) 
                 * Sin(@dLon / 2) 
                 * Sin(@dLon / 2);
      Set @c = 2 * Asin(Min(Sqrt(@a)));

     
Set @d = @R * @c;
      Return @d;

End

More Posts Next page »