Devland

from developers to developers
Welcome to Devland Sign in | Join | Help
in Search

Alessio Vecchi blog

  • Utilizzo delle Geolocation API per calcolare le distanze

    Recupero delle coordinate geografiche in una pagina WEB

    E' possibile ottenere le coordinate geografiche, in javascript, utilizzando le API di geolocalizzazione(http://dev.w3.org/geo/api/spec-source.html).

    Le coordinate possono essere recuperate dal disposito host se questo ha un GPS integrato, oppure possono essere dedotte dall'indirizzo IP o dalla cella GSM.

    L'oggetto navigator.geolocation si occupa del recupero delle informazioni geografiche dal dispositivo host.

    Nell'esempio che segue viene chiamata la funziona getCurrentPosition a cui vengono passate callback in caso successo e quella in caso di errore,

    Nel caso di successo  il parametro position contiene le informazioni di latitudine e longitudine.

      if (navigator.geolocation != null) {
            navigator.geolocation.getCurrentPosition(foundLocation, noLocation);
        }
        else {
            noLocation();
       }

    function foundLocation(position) {
        var lat = position.coords.latitude;
        var lng = position.coords.longitude;
        SetCoordinate(lat, lng);   
       //..
    }

    function SetCoordinate(lat, lng) {
        $('.coord-latitude').val(lat);
        $('.coord-longitude').val(lng);
    }

    function noLocation() {
        //..
    }

    Ottenute le coordinate si possono effettuare delle operazioni di ricerca geolocalizzate, ad esempio trovare il negozio più vicino in quel momento.

    Se si hanno a disposizione un elenco di indirizzi da geolocalizzare si vogliono ottenere le coordinate si può leggere il mio post precedente:

    http://www.devland.it/cs/blogs/alessio-vecchi/archive/2009/11/16/geolocalizzazione-con-google-HTTP-service.aspx

    Ottenute le coordinate dal dispositivo si può fare una chiamata POST a un WebService, che per comodità ho messo nel code behind della pagina, che restituisce la lista dei negozi ordinate per distanza decrescente.

     function StartSearch() {
        var lat = $('.coord-latitude').val();
        var lng = $('.coord-longitude').val();    
        $.ajax({
            type: "POST",
            url: "/index.aspx/SearchStore",
            data: "{'lat': " + lat + ", 'lng':" + lng + " }",
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            success: function(stores) {
                ApplyTemplate(stores);
            },
            error: function(XMLHttpRequest, textStatus, errorThrown) {
                alert(textStatus + ' ' + errorThrown);
            }
        });
    }

    Se la chiamata ha successo viene fatta una callback alla funzione ApplyTemplate:

     function ApplyTemplate(stores) {
        //alert(stores.d);
        $('#results').setTemplateURL('template/stores-result.html');
        $('#results').processTemplate(stores);
    }

    che utilizza il motore jTemplate per renderizzare i dati che ci vengono restituiti in formato json.

     Il file template stores-result.html è una listing semplicissimo:

    {#foreach $T.d as store}   
        <div class="store-item">
            <p>(#{$T.store.store.ID}) - {$T.store.store.Name}</p>
            <p>{$T.store.store.Address}</p>
            <p>{#if $T.store.distance<1 }
            {($T.store.distance*1000).toFixed(0)} metri
            {#else} {$T.store.distance.toFixed(0)} km {#/if}</p>
        </div> 
    {#/for}

     Ecco invece il codice della pagina e il code behind:

    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="index.aspx.cs" Inherits="index" %>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <title></title>

        <script src="http://maps.google.com/maps/api/js?sensor=false" type="text/javascript"></script>

        <script src="script/jquery-1.3.2.min.js" type="text/javascript"></script>

        <script src="script/jquery-jtemplates.js" type="text/javascript"></script>

        <script src="index.js" type="text/javascript"></script>

        <style type="text/css">
            .address
            {
                width:400px;
            }
            .store-item
            {
                background-color:#f0f0f0;
                width:400px;
                border-bottom:1px solid gray;
            }
        </style>
    </head>
    <body>
        <form id="form1" runat="server">
        <div class="content">
            Cerca lo store più vicino a te:<br />
            <div id="div-searching">
                ricerca in corso...
            </div>
            <br />
            <asp:TextBox ID="tbAddress" runat="server" CssClass="address" />
            <input type="text" id="tbLatitude" class="coord-latitude" runat="server" />
            <input type="text" id="tbLongitude" class="coord-longitude" runat="server" />
            <asp:HyperLink ID="hlSearch" runat="server" NavigateUrl="#" Text="cerca"
                CssClass="but-cerca search-button" />
        </div>
        <div id="results">
        </div>
        
        </form>
    </body>
    </html>
     

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.Services;
    using System.Collections;
    using System.Xml;
    using System.Xml.Linq;
    using System.Globalization;

    public partial class index : System.Web.UI.Page
    {


        protected void Page_Load(object sender, EventArgs e)
        {

            if (!Page.IsPostBack)
                Draw();
        }


        private void Draw()
        {
            tbAddress.Text = "";       
        }

        private const double RAD = 57.295779513082323;
        private static double Distance(double lat1, double lng1, double lat2, double lng2)
        {
            var a = Math.Sin(lat1 / RAD) * Math.Sin(lat2 / RAD);
            var b = Math.Cos(lat1 / RAD) * Math.Cos(lat2 / RAD) * Math.Cos((lng2 - lng1) / RAD);
            var distance = 6377.830272 * Math.Acos(a + b);
            return distance;

        }

        [WebMethod()]
        public static IEnumerable SearchStore(double lat, double lng)
        {
            return Store.Stores.
                OrderBy(s => Distance(s.Latitude, s.Longitude, lat, lng)).
                Select(s => new { store = s, distance = Distance(s.Latitude, s.Longitude, lat, lng) });
        }

    }

     

  • Geolocalizzazione con Google HTTP Service

    Ormai credo che a quasi tutti sia capitato di dover integrare il proprio sito web (e non) con qualche sistema di geolocalizzazione.

    I motivi possono essere molteplici: dal visulizzare un punto su una mappa, al ricercare l'indirizzo del negozio più vicino, al calcolo del percorso tra due indirizzi, ecc,

    Spesso capita di avere un database con un elenco di indirizzi e di volere trovare la latitudine e longitudine per poi salvarla sul database stesso.

    Per prima cosa bisogna appogiarsi a un servizio esterno che permette di localizzare il nostro indirizzo in coordinate terrestri (latitudine e longitudine).

    Per questo esempio utilizziamo il servizio di Geocoding fornito da Google:

    http://code.google.com/intl/it/apis/maps/documentation/geocoding/index.html

    Leggendo la documentazione si scopre che basta fare un chiamata in HTTP GET al segunete indirizzo web:

    http://maps.google.com/maps/geo

    nella query string bisogna fornire questi parametri obbligatori:

    • q (indirizzo),
    • key (la GoogleMap API Key),
    • sensor (a false),
    • output (il formato dell'output: xml, json, ecc)

    Se proviamo a fare una chiamata di prova mettendo come indirizzo milano:

    http://maps.google.com/maps/geo?q=milano&sensor=false&key=&output=xml

    si ottiene il seguente risultato:

    <?xml version="1.0" encoding="UTF-8" ?>
    <kml xmlns="http://earth.google.com/kml/2.0"><Response>
    <name>milano</name>
    <Status>
    <code>200</code>
    <request>geocode</request>
    </Status>
    <Placemark id="p1">
        <address>Milano, Italia</address>
    [..]
    <Point><coordinates>9.1881408,45.4636889,0</coordinates></Point>
      </Placemark>
    </Response></kml>

    Per effettuare un HTTP GET mi appoggio a una comoda classe di utility PostSubmitter scaricabile da:

    http://geekswithblogs.net/rakker/archive/2006/04/21/76044.aspx

    Ovviamente si possono usare le classi del framework .net.

    A questo punto scrivere una classa che dato un indirizzo ci fornisce la longitudine e la latitudine è una banalità:

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

    namespace GeoLocator
    {
        public class GeoLocator
        {
            private String GoogleGeoServiceUrl = ConfigurationManager.AppSettings["GoogleGeoServiceUrl"];
            private String GoogleMapAPIKey = ConfigurationManager.AppSettings["GoogleMapAPIKey"];
            private XNamespace GoogleNS = ConfigurationManager.AppSettings["GoogleNS"];

            public GeoLocator()
            {

            }

            public GeoLocator(String geoServiceUrl, String mapAPIKey, String googleNS)
            {
                this.GoogleGeoServiceUrl = geoServiceUrl;
                this.GoogleMapAPIKey = mapAPIKey;
                this.GoogleNS = googleNS;
            }
            
            public GeoCoordinate GetGeoCoordinate(String address)
            {
                PostSubmitter post = new PostSubmitter();
                post.PostItems.Add("q", address);
                post.PostItems.Add("key", GoogleMapAPIKey);
                post.PostItems.Add("sensor", "false");
                post.PostItems.Add("output", "xml");
                post.PostItems.Add("gl", ".it");
                post.Url = GoogleGeoServiceUrl;
                post.Type = PostSubmitter.PostTypeEnum.Get;
                String xmlText = post.Post();          
                XDocument doc = XDocument.Parse(xmlText);

                XElement coordinate =
                    doc.
                    Descendants(GoogleNS + "coordinates").
                    FirstOrDefault();


                GeoCoordinate geoCoordinate =null;
                if (coordinate != null)
                {
                    String[] coords = coordinate.Value.Split(',');

                    geoCoordinate = new GeoCoordinate()
                    {
                        Latitude = decimal.Parse(coords[0], CultureInfo.CreateSpecificCulture("en-us")),
                        Longitude = decimal.Parse(coords[1], CultureInfo.CreateSpecificCulture("en-us"))
                    };
                }
                
                return geoCoordinate;
            }
        }

        public class GeoCoordinate
        {
            public decimal Latitude { get; set; }
            public decimal Longitude { get; set; }
        }
    }

    dove l'App.config è così strutturato:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
        <appSettings>
            <add key="GoogleGeoServiceUrl" value="http://maps.google.com/maps/geo" />
            <add key="GoogleMapAPIKey" value="inserire la propria chiave" />
            <add key="GoogleNS" value="http://earth.google.com/kml/2.0" />
        </appSettings>
    </configuration>

     

    Nei file allegati al post c'è un esempio completo con un progetto di test che utilizza la libreria.

    Scarica HTTPServiceGeoLocator.zip 

More Posts
Powered by Community Server (Commercial Edition), by Telligent Systems