Nibble

Branch/shop/… locator for Umbraco Razor style

A while ago I created a locator package for umbraco that made it pretty easy to add find nearest branch/shop/dealer/… functionality to your umbraco site.

To sharpen my razor skills I thought it would be great to try to port this from xslt to razor and turns out this was pretty easy (feel the power of the dark side).

Another update is that it now uses the google maps api v3 so no more need for api keys

It works in combination with the google maps datatype so you’ll need to make sure that your documents have coordinates stored before you are able to search (so a property of the type google maps using the google maps datatype).

image

Once the documents you want to search through have a long/lat stored you can use the locator macro.

image

The macro has 4 parameters

  • Document type alias of the documents you want to search through
  • Property alias of the property containing the coordinates
  • If you want the units in miles instead of km
  • Number of results to display

If you have the macro on a page/template you’ll need to provide it with a search location. So posting to that page with a querystring variable s will do the trick like ?s=gentbrugge

The default output is pretty simple,

  • Search location
  • Map with markers
  • Unordered list of results ordered by distance from search location

image

But that can be updated to your needs. There is some stuff going on in the locator assembly but the output can be completely customized by updating the razor script.

@inherits umbraco.MacroEngines.DynamicNodeContext
@using Locator
@using Locator.GeoCoding
@using Locator.GeoCoding.Services.Google
@using umbraco.MacroEngines;
 
@{
 
   @* Select the nodes you want to search through, defaults to children *@
   var nodesToSearchThrough = string.IsNullOrEmpty(Parameter.DocTypeAlias) ?
        Model.Children.Where("Visible"): Model.AncestorOrSelf(1).Descendants(Parameter.DocTypeAlias).Where("Visible");
   
   @* Alias of the property containing the long/lat, defaults to location *@
   string locationPropAlias = string.IsNullOrEmpty(Parameter.LocationPropAlias) ? 
        "location" : Parameter.locationPropAlias;
   
   @* Number of results, defaults to 5 *@
   int numberOfSearchResults = string.IsNullOrEmpty(Parameter.NumberOfResults)|| Parameter.NumberOfResults == "0" ? 
        5 : int.Parse(Parameter.NumberOfResults);
  
   @* Distance unit, defaults to km *@
   DistanceUnits distanceunit = Parameter.UnitInMiles == "1" ? 
        DistanceUnits.Miles : DistanceUnits.Kilometers;
  
   List<GeoItem> items = new List<GeoItem>();
   Location searchLocation = new Location();
  
   if(string.IsNullOrEmpty(Request["s"]))
   {
       @* Search term not provided *@ 
       <p>Please provide a search term</p>
   }
   else 
   {
      @* Lookup search location coordinates *@ 
      GeoResponse r = GoogleGeoCoder.CallGeoWS(Request["s"]);
    
      if(r.Results.Length == 0)
      {
        @* Location not found *@ 
        <p>Location not found:  @Request["s"]</p>
      }
      else
      {
        @* build up list of results *@       
        searchLocation = new Location(r.Results[0].Geometry.Location.Lat, r.Results[0].Geometry.Location.Lng);
       
        foreach (var node in nodesToSearchThrough) {
                         
              Location itemLocation = new Location(
                    Convert.ToDouble(node.GetProperty(locationPropAlias).ToString().Split(‘,’)[0], Utility.NumberFormatInfo),
                    Convert.ToDouble(node.GetProperty(locationPropAlias).ToString().Split(‘,’)[1], Utility.NumberFormatInfo));  
              
              items.Add(new GeoItem(node,itemLocation,searchLocation.DistanceBetween(itemLocation,distanceunit)));
                                                                                       
        }
        @* sort based on distance *@
        items.Sort(new GeoItemComparer());
       
      }
   }
}
@if(items.Count > 0)
{
@* Output Results *@ 
 
<p>Result for: @Request["s"]</p>
  
@* Output Map *@
 
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false">
</script>
 
<div id="map_canvas" style="width:500px; height:500px"></div>
 
<script type="text/javascript">
  function initialize() {
    var latlng = new google.maps.LatLng(
                    @searchLocation.Latitude.ToString(Utility.NumberFormatInfo), 
                    @searchLocation.Longitude.ToString(Utility.NumberFormatInfo));
    var myOptions = {
      zoom: 8,
      center: latlng,
      mapTypeId: google.maps.MapTypeId.ROADMAP
    };
    var map = new google.maps.Map(document.getElementById("map_canvas"),
        myOptions);
    @{int c = 1;}                      
    @foreach(GeoItem geo in items.Take(numberOfSearchResults)){
        dynamic node = ((DynamicNode)geo.Node);    
                                                          
        <text>
          var mLatlng@(c) = new google.maps.LatLng(
                                      @geo.Location.Latitude.ToString(Utility.NumberFormatInfo),
                                      @geo.Location.Longitude.ToString(Utility.NumberFormatInfo));
          var marker@(c) = new google.maps.Marker({
              position: mLatlng@(c) , 
              map: map, 
              title:"@node.Name"
          });                                                              
         </text>
        c++;                                                  
   }
  }
  initialize();
</script>
 
 
@* Output list *@
<ul>
@foreach(GeoItem geo in items.Take(numberOfSearchResults)){ 
  dynamic node = ((DynamicNode)geo.Node);                                                
  <li><a href="@node.Url">@node.Name - @geo.Distance.ToString("0.00") km</a></li>                               
}
</ul>
}

Package can be downloaded on the project page: http://our.umbraco.org/projects/developer-tools/locator-razor-style

5 Comments so far

  1. Tony Bolton on February 15th, 2012

    I know this is an old post, but awesome stuff. I’ve actually been asked to accommodate for markers within a map for local dealers, so this is a great position to start from. Thanks alot! :)

  2. Tim Geyssens on February 16th, 2012

    Ok just be aware that it needs a small update for umbraco 4.7 and up (due to some changes to the razor engine)

  3. Matthew on June 29th, 2012

    Hi Tim,

    I posted this on the Umbraco forum but thought I might get lucky and catch you here. Maybe this would be useful for others? I was trying to modify your razor script to place multiple markers on a google map when no user interaction is required. I’m stuck on how to create the array of locations but I’m sure that’s not the only problem with my cob job.

    Here’s where the post is on Umbraco:

    http://our.umbraco.org/forum/developers/razor/32900-Build-an-array-of-locations-for-multiple-Google-Maps-markers

    Any help is greatly appreciated but if you could even just point me to where I learn how to build arrays properly, I’d be almost as happy.

    Thanks much.

  4. mithun on March 12th, 2013

    Hi Tim,
    what are the updates?
    “Ok just be aware that it needs a small update for umbraco 4.7 and up (due to some changes to the razor engine)”

    regards
    mithun

  5. Adam Maidment on April 4th, 2013

    Hi Tim,

    Echoing mithun; what needs updating here as I’m getting a razor script error on the page?

    Many thanks

Leave a Reply