Store Locator - Moved to Codeplex

by Donn Felker 23. November 2008 07:29

image

The Store Locator I built for ASP.NET has been getting a lot of traffic over the last year and requests have been coming in for new features like crazy. To manage this project a little more effectively, I've decided to move the project to CodePlex.

 

You can now download the source from Codeplex here (codeplex.com/storelocator).

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

.NET | ASP.NET | GIS

C# Microsoft MapPoint 4.5 Geocode (Latitude and Longitude) Class

by Donn Felker 7. May 2007 04:18

A while back I created a Geocode Class that retrieved its Geocode information from Google Maps and its been downloaded a ton. I've also received a few emails asking if I had a Microsoft MapPoint implementation of the Geocode address. Apparently the examples in Microsofts SDK are not as easily accessible compared to that of Googles (from what others have said). So here it is, a Geocode class for Microsoft MapPoint.

I actually saved this as a project because you need the web reference and also a few settings in the app.config. You can download it, change a configuration setting, update a web reference, and then build and go. Please note, to test this, you'll need NUnit or something similar. I have included a test class that has one unit test in it. This is so you can be sure that the code is working correctly. The actual Geocode class is in the project.

The Microsoft MapPoint SDK

The SDK is full featured with a TON of classes to help with Geospatial programming. Check out the SDK when you get a chance.

Steps To get it up and running

1. Download the project at the bottom of this post
2. Get a developer account here.
3. Put the User/Pass word in the app.config file. It should be on line 10 of the app.config.
4. Update your web reference (right click on the web reference and click "Update Web Reference").
5. Run the unit test. I use TestDriven.NET for this.

Options

The code has a class called FindOptions which allows you to set the threshold of how close you want the search result to be. Look at the MapPointGeocode.cs file below for more info.

Code

The GeoCodeClass (please note, for this to work, you'll need the web reference to the MapPoint webservice. Download the example at the bottom of the post which has all references included.)

using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Configuration;
using MapPointGeocode.MapPointService;
using System.Web.Services.Protocols;
using System.Diagnostics;

namespace MapPointGeocode
{
   public class MapPointGeocode
   {
      /// <summary>
      /// These are the actual instances of the objects that call the MapPoint .NET service
      /// </summary>
      private MapPointService.RenderServiceSoap renderService;
      private MapPointService.FindServiceSoap findService;



      public MapPointGeocode()
      {
         InstantiateServices();
      }

      private void InstantiateServices()
      {
         // Create and set the logon information (note comment in web.config -- here would be the place to
         // decrypt/unhash the user/password from the config file).
         //NEW - Revised configuration settings (add ref to System.Configuration first):

         NetworkCredential ourCredentials = new NetworkCredential(ConfigurationManager.AppSettings["MPUser"], ConfigurationManager.AppSettings["MPPass"]);

         // Create the render service, pointing at the correct location
         renderService = new MapPointService.RenderServiceSoap();
         renderService.Credentials = ourCredentials;
         renderService.PreAuthenticate = true;

         // Create the find service, pointing at the correct location
         findService = new MapPointService.FindServiceSoap();
         // set the logon information
         findService.Credentials = ourCredentials;
         findService.PreAuthenticate = true;
      }

      /// <summary>
      /// Returns the geocode coordinates of an address. 
      /// </summary>
      /// <param name="addressLine">The address</param>
      /// <param name="city">The city</param>
      /// <param name="postalCode">The postal/zip code</param>
      /// <param name="country">The country. e.g.: USA, Canada</param>
      public LatLong GeocodeAddress(string addressLine, string city, string state, string postalCode, string country)
      {
      // Set up the address
         Address address = new Address();
         address.AddressLine = addressLine;
         address.PrimaryCity = city;
        address.PostalCode = postalCode;
        address.Subdivision = state; 
        address.CountryRegion = country;
        
        // Set up the specification for the address
        // Set up the specification object.
        FindAddressSpecification findAddressSpec = new FindAddressSpecification();
        findAddressSpec.InputAddress = address;
        findAddressSpec.DataSourceName = "MapPoint.NA";
// More info: http://msdn2.microsoft.com/en-us/library/ms982198.aspx and http://msdn2.microsoft.com/en-us/library/aa493004.aspx

        // Set the find options. Allow more return values by decreasing
        // the value of the ThresholdScore option.
        // Also, limit the number of results returned to 20.
        FindOptions myFindOptions = new FindOptions();
        myFindOptions.ThresholdScore = 0.5;
        myFindOptions.Range = new FindRange();
        myFindOptions.Range.StartIndex = 0;
        myFindOptions.Range.Count = 20;
        findAddressSpec.Options = myFindOptions;

        // Create a FindResults object to store the results of the FindAddress request.
        FindResults myFindResults;
        LatLong latLong = new LatLong(); 

        try
        {
           // Get the results and return them if there are any. 
            myFindResults = findService.FindAddress(findAddressSpec);
            FindResult[] myResults = myFindResults.Results;
            if(myResults!= null)
            {
               latLong = myResults[0].FoundLocation.LatLong;
            }

         }
         catch (SoapException myException)
      {
      // Your exception handling process goes here.
      Debug.Write(myException);
      }

      return latLong; 
      }
   }
}

Conclusion

The Microsoft MapPoint SDK might be a little more difficult to grok at first but one you start working with it you can see that it is full featured with many more options than the Google provided services.

Microsoft MapPoint 4.5 SDK Online Link

Download

MapPointGeocode.zip (276.41 KB)


kick it on DotNetKicks.com

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

.NET | GIS

HOWTO: Build a Store Locator in ASP.NET

by Donn Felker 17. March 2007 06:39

Full Visual Studio 2005 solution included at the bottom of this post.

Update 2008-11-22: Fixed a bug with the variable definition. Also Moved the source code to CodePlex. From now on, all updates to the source will be made to the codeplex site: codeplex.com/StoreLocator

Update 2008-10-14: Added a SQL file so that the DB can be generated for any other type of DB provider that you may wish to use. See bottom of post for SQL File.

Many organizations have multiple locations throughout the country. A lot of the time the organizations customers will want to know where the closet location of that store is. We’ve all become accustomed to going to a companies website and finding the store locator and locating the store that’s closest to us. This is a simple thing for a user to do. But building a store locator is far from simple and can become quite complex, real quick. I’ve put together a simple example that will enable you to add store locator functionality to your ASP.NET website in no time.

Let’s start by covering the basics….

Requirements for store locator functionality: 
   - Database to store your store locations
   - Each store location needs a Longitude / Latitude 
   - A method to retrieve Latitude / Longitude, also known as Geocoding
   - A Web Front end that allows users to search and view the results.

Screen Shots

Search Screen:

Results Screen (click for larger view):

 

The Database

The database is very simple. We have one table, two sprocs, and 3 functions. It is in the App_Data folder of this solution.

The Location table holds the store locations.

The GetNearbyLocations sproc gets the locations that are closest to the user through a mathematical calculation that is inside of the sproc.

The InsertLocation sproc inserts a new location into the Location table (we’ll get to that near the end).

The three functions: XAxis, YAxis, ZAxis are used in the distance calculation that is inside of the sproc.

Important Note: I’m not the genius that wrote this mathematical calculation. Therefore, I can’t take credit for it. The database table, functions and distance sproc are all modeled after this post on MSDN. I have altered quite a few things especially the GetNearbyLocations sproc where I actually calculate the Earths X,Y, and Z axis’ at runtime. 

 

The Geocoding – Getting the Latitude / Longitude

The latitude and longitude are needed to perform the distance calculation in the GetNearbyLocations stored procedure. The Latitude and Longitude of each store is stored in the Locations table (see the database image above) with the location record itself.

How to do we get the Latitude / Longitude?

That’s where the Google Maps API comes into place. Google Maps allows you to sign up for a free API key.

Important Note: If you are going to test this locally, you will need to get a API key that is associated with your local machine, e.g.: http://localhost:port/  where “port” is your port number, such as: localhost:4688, localhost:1637, etc).

Where put your Google Maps API Key
The key is located in two places (I know, its not best practice) but, it’s the only way I could get it to work without spending hours on figuring it out. It’s located in the web.Config and in the Default.aspx page.

Default.aspx page (click for larger view)

web.Config

In a previous post I had built a class that retrieved the latitude and longitude of an address. By using this class while a new location is being added, (AddNewStore.aspx), the Lat/Lon coordinates are then retrieved and saved.

protected void addNewStoreButton_Click(object sender, EventArgs e)
{
   string address = String.Format("{0}, {1}, {2} {3}"
   addressTextBox.Text,
   cityTextBox.Text,
   stateTextBox.Text,
   zipTextBox.Text);
   Coordinate addressCoordinates = Geocode.GetCoordinates(address);
   ... // Other code ommitted
}

The Web Front End

The web front end is simple (errrrrrrr… kind of). The application requires 2 things:
1. An address
2. A distance to search within.

The user enters this information, it’s passed to the database, the calculation is performed and the records that match the distance compared to the address are returned. Simple, right?

Now its time for the “somewhat” confusing part: the task of integrating the Google map with clickable markers. Google doesn’t offer markers that are numbered (from what I could find). So I found some on the net and included them in this project. Which leads me to the next point…

A maximum of 100 results will be returned.

This is set within the stored procedure. The reason we only return 100 records is because we only have 100 icon images (1 through 100). Plus, placing more markers on the page would have a negative impact on the memory and processor on the user’s machine. Furthermore, who’s really going to look at 100 results? The user is probably only looking for the closest couple of stores. Not the top 100.

Google Marker Placement

In order to place markers on the screen we needed lat/lon points to pass to Google’s API. This poses a problem because our data is in the DB, but Google Maps API needs it on the client to process it. Therefore, while on the server, while we have the data in a dataset (below) ...

/// <summary>
/// Gets the store locations and the coordinate for the "from" area (the address the user entered).
/// </summary>
/// <param name="coordinate">A coordinate.</param>
/// <param name="data">A LocationsData dataset.</param>
private LocationsData GetLocationData(Coordinate coordinate)
{
SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["StoresDb"].ConnectionString);
connection.Open();

string SQL = "GetNearbyLocations";
SqlCommand command = new SqlCommand(SQL, connection);
command.Parameters.Add(new SqlParameter("@CenterLatitude", coordinate.Latitude));
command.Parameters.Add(new SqlParameter("@CenterLongitude", coordinate.Longitude));
command.Parameters.Add(new SqlParameter("@SearchDistance", distanceDropDown.SelectedValue));
command.Parameters.Add(new SqlParameter("@EarthRadius", 3961)); // In Miles
command.CommandType = CommandType.StoredProcedure;

SqlDataAdapter da = new SqlDataAdapter(command);
LocationsData data = new LocationsData();
da.Fill(data.Location);
return data;
}

GetJSONLocations(LocationsData data);

... we have to create a string representation of a JSON object array that holds all this info, this is done in the GetJSONLocations method. Please see the method for more documentation on how that variable is created.

Info that the JSON object holds: 
   - Location Name
   - Address 
   - Url Encoded Address
   - Latitude
   - Longitude

This info was written to the screen using the RegisterClientScriptBlock.

Using the same method of as before we create another JSON variable (homeSpatialInfo) that holds the “home info”. This info is the address that the user typed in.

This information is used for the popup marker, as well as the link that is provided within the popup marker. Clicking on the “directions” link (within the popup marker) will take you to Google’s map site with the “to” and “from” address parameters already populated, therefore giving you a directional map from the location you typed in, to the location that you clicked on the map. Smooth, eh?

(click for larger view)

 

Adding a new Location

To add a new location, fire up the AddNewStore.aspx page and type in the required info (in this case I’m requiring all fields, but this can be changed to fit your own needs) and submit. If the address cannot be found, (Google Maps API returns “0” for both the Lat/Lon coordinates that it cannot find) it will inform you. Otherwise the address will be added and then upon your next search, it will be returned in the results (if the location is within the distance specified).

 

Database Note: The locations that are in the Stores.mdf that come with this solution are 200 Starbucks stores that are within the New York Area. This is not a comprehensive list, this is just a list I happened to have for testing purposes.


And that’s all folks!

Download and enjoy. :)

StoreLocator.zip (521.92 KB)

SQL for DB (7.3 KB)

kick it on DotNetKicks.com

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

ASP.NET | GIS | SQL Server

C# Google Geocode (Latitude and Longitude) Class

by Donn Felker 12. March 2007 20:23

Update 2007/05/07: There is also a Microsoft MapPoint v4.5 project I've written that does the same thing. Click here to go to that post.


Retrieve the Latitude and Longitude of any addresses in the United States, Canada, France, Germany, Italy, Spain and Japan (link) with this class. View the class below and download the class at the bottom of this post.

Code


using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Net;
using System.Web.UI;


namespace GoogleGeocoder
{
   public interface ISpatialCoordinate
   {
      decimal Latitude {get; set; } 
      decimal Longitude {get; set; } 
   }

   /// <summary>
   /// Coordiate structure. Holds Latitude and Longitude.
   /// </summary>
   public struct Coordinate : ISpatialCoordinate
   {
      private decimal _latitude; 
      private decimal _longitude;

      public Coordinate(decimal latitude, decimal longitude)
      {
         _latitude = latitude;
         _longitude = longitude; 
      }

      #region ISpatialCoordinate Members

      public decimal Latitude
      {
        get 
        { 
            return _latitude; 
        }
        set 
        { 
            this._latitude = value; 
        }
      }

      public decimal Longitude
      {
        get 
        { 
            return _longitude; 
        }
        set 
        { 
            this._longitude = value;
        }
      }

   #endregion
   }

   public class Geocode
   {
      private const string _googleUri = "http://maps.google.com/maps/geo?q=";
      private const string _googleKey = "yourkey";
      private const string _outputType = "csv"; // Available options: csv, xml, kml, json

      private static Uri GetGeocodeUri(string address)
      {
         address = HttpUtility.UrlEncode(address);
         return new Uri(String.Format("{0}{1}&output={2}&key={3}", _googleUri, address, _outputType, _googleKey));
      }

      /// <summary>
      /// Gets a Coordinate from a address.
      /// </summary>
      /// <param name="address">An address.
      /// <remarks>
      /// <example>1600 Amphitheatre Parkway Mountain View, CA 94043</example>
      /// </remarks>
      /// </param>
      /// <returns>A spatial coordinate that contains the latitude and longitude of the address.</returns>
      public static Coordinate GetCoordinates(string address)
      {
         WebClient client = new WebClient();
         Uri uri = GetGeocodeUri(address);


         /* The first number is the status code, 
         * the second is the accuracy, 
         * the third is the latitude, 
         * the fourth one is the longitude.
         */

         string[] geocodeInfo = client.DownloadString(uri).Split(',');

         return new Coordinate(Convert.ToDecimal(geocodeInfo[2]), Convert.ToDecimal(geocodeInfo[3]));
      }

   }
}


How To Use


  1. Replace "yourkey" with your google api key. Get one here.
  2. Include in your project, reference the class through a using directive.
  3. Call get the coordinates like this:
    1. Coordinate coordinate = Geocode.GetCoordinates("1600 Amphitheatre Parkway Mountain View, CA 94043");
      decimal latitude = coordinate.Latitude;
      decimal longitude = coordinate.Longitude;

Uses


For each record in your system, get the lat/long and save it to the database. This can be used for calculating distances. e.g.: "Find all stores within ___ miles of this zip code.

 

***Notes***


The maximum # of Geocode requests that can be completed in one day are 50,000 (details).

 

Download
Geocode.zip (1.05 KB)

kick it on DotNetKicks.com

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

.NET | GIS

Powered by BlogEngine.NET 1.4.5.0
Theme by Mads Kristensen

About the author

Donn Felker

Senior Consultant
MCTS
ScrumMaster
Agile Practitioner

About Me | Books I Recommend

Gotta Pay The Bills


Tag cloud

    Popular Posts

    RecentComments

    Comment RSS

    Calendar

    <<  December 2008  >>
    MoTuWeThFrSaSu
    24252627282930
    1234567
    891011121314
    15161718192021
    22232425262728
    2930311234

    View posts in large calendar