Here's the ASPX page:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="default.aspx.cs" Inherits="Web.farmers_markets._default" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <!-- title --> <title>State Farmers Markets</title> <!-- jquery --> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script> <!-- bootstrap --> <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet" /> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script> <!-- google maps --> <script language="javascript" type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key={key}&sensor=false&language=en"></script> <!-- scripts --> <script language="javascript" type="text/javascript" src="../js/functions.js?cache=2015062801"></script> <script language="javascript" type="text/javascript" src="../js/spin.min.js?cache=2015062801"></script> <!-- custom style --> <style> body { margin: 25px !important; } .form-group { width: 100%; } #map-canvas { width: 100%; height: 500px; } </style> <script language="javascript" type="text/javascript"> // initialize $(document).ready(function () { InitializeMap(); }); function InitializeMap() { // variables var options = { center: new google.maps.LatLng(0, 0), mapTypeId: google.maps.MapTypeId.ROADMAP, scrollwheel: false, draggable: true }; var map = new google.maps.Map(document.getElementById("map-canvas"), options); var bounds = new google.maps.LatLngBounds(); var locations = jQuery.parseJSON($("#lblLocations").val()); // add locations to map $.each(locations, function () { // variables var latLong = new google.maps.LatLng(this.latitude, this.longitude); var Url = this.url.replace(/&/g, "&"); // marker var marker = new google.maps.Marker({ map: map, title: "{0} ({1})".format(this.market, this.city), position: latLong, draggable: false, icon: "images/marker.png" }); // bounds bounds.extend(latLong); // click event if (Url.HasValue()) { google.maps.event.addListener(marker, 'click', function () { window.open(Url) }); } }); // fit bounds map.fitBounds(bounds); } function btnSearch_Click() { // controls var txtZipCode = $("#txtZipCode"); // variables var ZipCode = txtZipCode.val().trim(); // validation if (!IsValidZip(ZipCode)) { ShowMessage('Please enter a valid zip code.', 'txtZipCode', 'txtZipCode'); return false; } // show progress ShowProgress(); // return value return true; } </script> </head> <body> <form id="form1" runat="server"> <div class="form-group"> <h2> <asp:Literal ID="lblTitle" runat="server" /> </h2> </div> <!-- map --> <div class="form-group"> <div id="map-canvas"></div> </div> <!-- filter --> <div class="form-group" style="padding-top: 15px"> <asp:Panel ID="pnlSearch" DefaultButton="btnSearch" runat="server"> <asp:DropDownList ID="ddlList" CssClass="form-control" Width="250px" Style="display: inline" ClientIDMode="Static" runat="server" /> within <asp:DropDownList ID="ddlRadius" CssClass="form-control" Width="100px" Style="display: inline" ClientIDMode="Static" runat="server"> <asp:ListItem>25</asp:ListItem> <asp:ListItem>50</asp:ListItem> <asp:ListItem>75</asp:ListItem> <asp:ListItem>100</asp:ListItem> </asp:DropDownList> miles of: <asp:TextBox ID="txtZipCode" Text="46237" MaxLength="5" CssClass="form-control" Width="100px" Style="display: inline; margin-right: 10px" ClientIDMode="Static" runat="server" /> <asp:Button ID="btnSearch" Text="Search" CssClass="btn btn-primary" OnClientClick="javascript: return btnSearch_Click();" OnClick="btnSearch_Click" ClientIDMode="Static" runat="server" /> </asp:Panel> </div> <!-- source --> <div class="form-group"> <em>This demo is powered by data from mysafeinfo.com</em><br /> <asp:HyperLink ID="lnkList" Target="_blank" ClientIDMode="Static" runat="server" /> </div> <!-- grid --> <div class="form-group"> <asp:Literal ID="lblResults" runat="server" /> </div> <!-- hidden fields --> <asp:HiddenField ID="lblLocations" ClientIDMode="Static" runat="server" /> <!-- progess --> <div id="loading"> <div id="loadingcontent"> <p id="loadingspinner"> </p> </div> </div> <!-- message modal --> <div class="modal fade" id="modal-message" tabindex="-1" role="dialog" aria-labelledby="lblModalMessage" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button> <h4 class="modal-title" id="lblModalMessage">Message</h4> </div> <div class="modal-body"> <div class="message"></div> </div> <div class="modal-footer"> <div class="pull-left"> <button id="btnMessageClose" type="button" class="btn btn-primary" data-dismiss="modal">Close</button> </div> </div> </div> </div> </div> <!-- hidden fields --> <input type="hidden" name="lblFocus" id="lblFocus" /> <input type="hidden" name="lblSelect" id="lblSelect" /> </form> </body> </html>
The only references not available on a content delivery network (CDN) are the following:
<!-- scripts --> <script language="javascript" type="text/javascript" src="../js/functions.js?cache=2015062801"></script> <script language="javascript" type="text/javascript" src="../js/spin.min.js?cache=2015062801"></script>
These files can be downloaded directly using the following:
- http://pavey.azurewebsites.net/js/functions.js
- http://pavey.azurewebsites.net/js/spin.min.js
Next we'll look at the code-behind file:
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.Xml; using System.Xml.Linq; using Newtonsoft.Json; using Utilities; namespace Web.farmers_markets { public partial class _default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { // bind lists and data if (!Page.IsPostBack) { BindLists(); BindData(); } } private void BindData() { // variables string List = ddlList.SelectedValue; string Title = ddlList.SelectedItem.Text; string Url = string.Format("https://mysafeinfo.com/api/data?list={0}&format=xml&select=cd,ct,nm,add,lat,lng,url&rootname=data&elementname=r&alias=cd=state,ct=city,nm=market,add=address,lat=latitude,lng=longitude,url=url", List); XmlDocument XmlDoc = new XmlDocument(); string XmlData = string.Empty; string XslData = string.Empty; string XslFile = Path.Combine(Request.PhysicalApplicationPath, "farmers-markets", "xsl", "results.xsl"); string ZipCode = txtZipCode.Text.Trim(); int Radius = Convert.ToInt32(ddlRadius.SelectedValue); List<Market> Markets = new List<Market>(); // labels lblTitle.Text = Title; lnkList.NavigateUrl = Url; lnkList.Text = Url; // geocode zip code Coordinate Origin = GetLatLong(Address: ZipCode); // request using (WebClient client = new WebClient()) { XmlData = client.DownloadString(Url); } // load xml into xml document XmlDoc.LoadXml(XmlData); // iterate through data first so we can do distance calculation foreach(XmlElement x in XmlDoc.SelectNodes("//r")) { // variables string Latitude = x.GetAttribute("latitude"); string Longitude = x.GetAttribute("longitude"); double OriginLatitude = Origin.Latitude; double OriginLongitude = Origin.Longitude; double DestinationLatitude = 0; double DestinationLongitude = 0; // parse double.TryParse(Latitude, out DestinationLatitude); double.TryParse(Longitude, out DestinationLongitude); // calculate distance from origin double Distance = GeoCodeCalc.CalculateDistance(OriginLatitude: OriginLatitude, OriginLongitude: OriginLongitude, DestinationLatitude: DestinationLatitude, DestinationLongitude: DestinationLongitude); bool InRange = Distance <= Radius; // add attribute indicating distance to origin x.SetAttribute("distance", Distance.ToString()); // add attribute indicating if location is in range x.SetAttribute("inrange", InRange.ToString().ToLower()); // add to markets list for easy conversion to json later if (InRange) { Markets.Add(new Market() { state = x.GetAttribute("state"), city = x.GetAttribute("city"), market = x.GetAttribute("market"), address = x.GetAttribute("address"), latitude = x.GetAttribute("latitude"), longitude = x.GetAttribute("longitude"), url = x.GetAttribute("url") }); } } // xsl using (Utilities.Xsl Xsl = new Utilities.Xsl()) { using (Utilities.Xsl.XslConfiguration XslConfiguration = new Utilities.Xsl.XslConfiguration(XslFile)) { // parameters XslConfiguration.AddXslParameter("radius", Radius.ToString()); XslConfiguration.AddXslParameter("zipcode", ZipCode); XslConfiguration.AddXslParameter("state", Title); // transform XslData = Xsl.TransformXml(XmlType: Utilities.Xsl.XmlTypes.XmlData, XmlSource: XmlDoc.OuterXml, XslConfig: XslConfiguration); // check for errors if (Xsl.ErrorDescription.HasValue()) { XmlData = Xsl.ErrorDescription; } } } // bind grid lblResults.Text = XslData; // use Json.NET (Newtonsoft.Json) to serialize locations for the map // https://www.nuget.org/packages/Newtonsoft.Json lblLocations.Value = JsonConvert.SerializeObject(Markets); } private void BindLists() { // variables string Result = string.Empty; string Url = "https://mysafeinfo.com/api/data?list=all&format=json&sort=nm&select=lst,nm&lst=farmermarkets,startswith&alias=lst=list,nm=name"; List<MarketList> Lists = new List<MarketList>(); // request using (WebClient client = new WebClient()) { Result = client.DownloadString(Url); } // use Json.NET (Newtonsoft.Json) to deserialize json string into a strongly typed list // https://www.nuget.org/packages/Newtonsoft.Json Lists = JsonConvert.DeserializeObject<List<MarketList>>(Result); // bind ddlList.DataValueField = "List"; ddlList.DataTextField = "Name"; ddlList.DataSource = Lists; ddlList.DataBind(); // default ddlList.SetValue("farmermarketsin"); } protected void btnSearch_Click(object sender, EventArgs e) { BindData(); } public static Coordinate GetLatLong(string Address, string Country = "") { // variables string Url = string.Empty; // determine url if (Address.HasValue() && Country.HasValue()) { Url = string.Format("https://maps.googleapis.com/maps/api/geocode/xml?address={0}&components=country:{1}&sensor=false", Address.Trim(), Country.Trim()); } else { Url = string.Format("https://maps.googleapis.com/maps/api/geocode/xml?address={0}&sensor=false", Address.Trim()); } // return return GetLatLongByUrl(Url); } public static Coordinate GetLatLongByUrl(string Url) { // variables Coordinate Coordinate = new Coordinate(); XElement XmlElement = XElement.Load(Url); XElement Status = (from x in XmlElement.Descendants() where x.Name.ToString().IsEqual("status") select x).FirstOrDefault(); // check status if (Status != null && Status.Value.IsEqual("ok")) { // variables string strLatitude = XmlElement.Element("result").Element("geometry").Element("location").Element("lat").Value; string strLongitude = XmlElement.Element("result").Element("geometry").Element("location").Element("lng").Value; double Latitude = 0; double Longitude = 0; // parse double.TryParse(strLatitude, out Latitude); double.TryParse(strLongitude, out Longitude); // coordinates Coordinate.Latitude = Latitude; Coordinate.Longitude = Longitude; } //return value return Coordinate; } private class MarketList { public string List { get; set; } public string Name { get; set; } } private class Market { public string state { get; set; } public string city { get; set; } public string market { get; set; } public string address { get; set; } public string latitude { get; set; } public string longitude { get; set; } public string url { get; set; } } public class Coordinate { public double Latitude { get; set; } public double Longitude { get; set; } } // Calculate Distance Between Geocodes in C# and JavaScript // http://pietschsoft.com/post/2008/02/Calculate-Distance-Between-Geocodes-in-C-and-JavaScript public static class GeoCodeCalc { public const double EarthRadiusInMiles = 3956.0; public const double EarthRadiusInKilometers = 6367.0; public static double ToRadian(double val) { return val * (Math.PI / 180); } public static double DiffRadian(double val1, double val2) { return ToRadian(val2) - ToRadian(val1); } public static double CalculateDistance(double OriginLatitude, double OriginLongitude, double DestinationLatitude, double DestinationLongitude) { return CalculateDistance(OriginLatitude, OriginLongitude, DestinationLatitude, DestinationLongitude, GeoCodeCalcMeasurement.Miles); } public static double CalculateDistance(double OriginLatitude, double OriginLongitude, double DestinationLatitude, double DestinationLongitude, GeoCodeCalcMeasurement Measurement) { // variables double Radius = GeoCodeCalc.EarthRadiusInMiles; if (Measurement == GeoCodeCalcMeasurement.Kilometers) { Radius = GeoCodeCalc.EarthRadiusInKilometers; } // return return Radius * 2 * Math.Asin(Math.Min(1, Math.Sqrt((Math.Pow(Math.Sin((DiffRadian(OriginLatitude, DestinationLatitude)) / 2.0), 2.0) + Math.Cos(ToRadian(OriginLatitude)) * Math.Cos(ToRadian(DestinationLatitude)) * Math.Pow(Math.Sin((DiffRadian(OriginLongitude, DestinationLongitude)) / 2.0), 2.0))))); } } public enum GeoCodeCalcMeasurement : int { Miles = 0, Kilometers = 1 } } }
This example also relies on the following:
- Extensions.cs
- Xsl.cs
- results.xsl
- marker.png
This might look like a lot of code at first glance. But pulling all of this together in an ASP.Net Web Forms Application is very simple, and will help you understand the following key concepts:
- Using WebClient to retrieve XML data from a 3rd party RESTful web service
- Working with XML data using an XmlDocument and XmlElement
- Using Google Maps API to geocode a zip code
- Calculating the distance between two points
- Using an XSL transformation to show the results in a Bootstrap table
- Using Json.NET (Newtonsoft.Json) to serialize locations for the Google Map
- Using Json.NET (Newtonsoft.Json) to deserialize JSON data into a strongly typed list
- Using JavaScript and jQuery to iterate a JSON array
- Using JavaScript and the Google Maps API to add locations to the Google Map
- Using JavaScript and jQuery to validate a valid zip code is entered
- Using JavaScript, jQuery, and Bootstrap to show validation errors in modal
- Using JavaScript and jQuery to show custom progress spinner
Click here to see a full working example of this demo.
Matt Pavey is a Microsoft Certified software developer who specializes in ASP.Net, VB.Net, C#, AJAX, LINQ, XML, XSL, Web Services, SQL, jQuery, and more. Follow on Twitter @matthewpavey
0 comments: