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: