Pierre's Embedded and Mobile Blog

Soulevons le capot des systèmes embarqués et mobiles

June, 2009

Posts
  • Pierre's Embedded and Mobile Blog

    La Boite à Outils de l'Application Géolocalisée: Partie 3: Afficher une carte

    • 1 Comments

    Cet article fait partie d'une série de 3: La Boite à Outils de l'Application Géolocalisée

    Une fois que la position est connue, reste à l’afficher sur une carte J. Pour cela les API en lignes de Bing Maps et Google Maps sont très faciles à utiliser dans leurs versions mobiles.

    Bing Maps

    Pour afficher la carte nous utilisons la même approche que pour le geocoding, mais le service utilisé change. Plutôt que d’utiliser un FindServiceSoap, j’utilise un RenderServiceSoap.

    Dans mon formulaire, je rajoute la création du service Soap qui me servira à communiquer avec la plateforme MapPoint. J’en profite pour rajouter quelques propriétés qui me serviront plus tard :

    private RenderServiceSoap renderService = new RenderServiceSoap();

    private LatLong mapCenter = null;

    private double mapScale = 50000;

    public double Latitude { get; private set; }

    public double Longitude { get; private set; }

     

    Puis je m’occupe de l’authentification : je stocke mon compte utilisateur (sous la forme de 6 chiffres) et mon mot de passe dans les ressources, et je crée un méthode statique pour factoriser ce code :

    private void SetMapPointCredentials()

    {

        // You have to provide MapPoint credentials in order to call out to the MapPoint WS

        NetworkCredential myCredentials = new NetworkCredential(Properties.Resources.MapPointUserName, Properties.Resources.MapPointPassword);

     

        renderService.Credentials = myCredentials;

        renderService.PreAuthenticate = true;

    }

     

    Ensuite, il me suffira à l’initialisation du formulaire d’appeler cette méthode pour mettre en place les bonnes authentifications.

    Ensuite, je vais utiliser un contrôle de type PictureBox pour afficher la carte. Pour peupler cette PictureBox, il faut une Image, que je vais créer à partir d’un objet de type MapImage que j’aurai fait généré par le service.

    Une MapImage est générée par le service à partir d’une MapView, qui est la vue de la carte (quelle échelle, quel centre, etc).Par dessus, on peut rajouter une liste de PushPins, qui correspondent à des petits indicateurs (drapeaux, etc) sur la carte. Voici donc à quoi ressemble le code :

    MapView[] myViews = new MapView[1];

    ViewByScale myView = new ViewByScale();

     

    mapCenter = new LatLong();

    mapCenter.Latitude = this.Latitude;

    mapCenter.Longitude = this.Longitude;

     

    myView.CenterPoint = mapCenter;

    myView.MapScale = mapScale;

     

    myViews[0] = myView;

     

    Pushpin[] myPushpins = new Pushpin[1];

    myPushpins[0] = new Pushpin();

    myPushpins[0].IconDataSource = "MapPoint.Icons";

    myPushpins[0].IconName = "31";

    //myPushpins[0].Label = "Location";

    myPushpins[0].LatLong = new LatLong();

    myPushpins[0].LatLong.Latitude = mapCenter.Latitude;

    myPushpins[0].LatLong.Longitude = mapCenter.Longitude;

     

    MapSpecification mapSpec = new MapSpecification();

    mapSpec.DataSourceName = "MapPoint.EU";

    mapSpec.Views = myViews;

    mapSpec.Pushpins = myPushpins;

     

    MapImage[] mapImages = renderService.GetMap(mapSpec);

    if (mapImages[0] != null)

    {

        pbMap.Image = new System.Drawing.Bitmap(new System.IO.MemoryStream(mapImages[0].MimeData.Bits));

        pbMap.Visible = true;

    }

     

    Google Maps

    Pour Google Maps, le plus simple pour le mobile est d’utiliser l’API statique : http://code.google.com/apis/maps/documentation/staticmaps/

    Ici aussi il faut une clef d’API, qu’on peut récupérer gratuitement ici : http://code.google.com/apis/maps/signup.html

    Ensuite, il suffit d’envoyer les bons paramètres dans une URL pour récupérer une image, qu’on peut héberger dans une PictureBox. Voici une façon simple de construire la requête :

    #region Construction des arguments de l'URL

    StringBuilder args = new StringBuilder();

    args.Append("center=" + this.Latitude.ToString() + "," + this.Longitude.ToString());

    args.Append("&zoom=12");

    args.Append("&size=" + pbMap.Width + "x" + pbMap.Height);

    args.Append("&format=png");     // gif, png, jpg

    args.Append("&maptype=mobile"); // satellite, terrain, hybrid, mobile

    args.Append("&markers=" + this.Latitude + "," + this.Longitude + "," + "blue");

    //args.Append("&path=");

    //args.Append("&span=");

    //args.Append("&frame=");

    //args.Append("&hl=");

    args.Append("&key=" + this.GoogleMapApiKey);

    args.Append("&sensor=false");   // true if my app uses GPS sensor

    #endregion

     

    try

    {

     

        WebRequest myRequest = WebRequest.Create(GoogleMapBaseUrl + args.ToString());

        WebResponse myResponse = myRequest.GetResponse();

     

        pbMap.Image = new Bitmap(myResponse.GetResponseStream());

        myResponse.Close();

    }

    catch (WebException ex)

    {

        MessageBox.Show(ex.Message);

    }

     

    Si on résume ce qu’on vient de voir, vous êtes maintenant capables de récupérer une position que ce soit à partir du GPS ou avec l’identifiant de la cellule GSM, et de l’afficher sur une carte, que ce soit de chez Google ou de chez Bing. Vous avez donc toutes les cartes en mains pour proposer une application géolocalisée, il ne manque plus que l’idée « métier » derrière J

    Si vous vous sentez l’âme d’un contributeur, il reste de nombreux services à utiliser : Yahoo Geocoding Services, Open Street Map, etc. N’hésitez pas !

  • Pierre's Embedded and Mobile Blog

    La Boite à Outils de l'Application Géolocalisée: Partie 2: Geocoding d'adresse

    • 0 Comments

    Cet article fait partie d'une série de 3: La Boite à Outils de l'Application Géolocalisée

    Il existe un moyen précis d’obtenir une localisation, il s’agit tout simplement de l’adresse à laquelle on se trouve J. Des services en ligne permettent d’obtenir une localisation précise à partir d’une adresse donnée. Encore une fois, nous avons le choix entre plusieurs services :

    Google Geocoding services

    Le service de geocoding Google fonctionne très simplement en REST. L’API est décrite en ligne ici : http://code.google.com/apis/maps/documentation/geocoding/index.html

    Il suffit de construire une URL particulière et les coordonnées sont renvoyées en XML : voici un bout de code simple qui permet de traiter une adresse :

    static public string GeoCodeAddress(string street, string town, string zipcode, string country)

    {

        XNamespace ns1 = "http://earth.google.com/kml/2.0";

        string BaseUrl = "http://maps.google.com/maps/geo?";

        string GoogleMapApiKey = "<INSERT_API_KEY_HERE>";

        string result;

        string[] splittedResult;

        StringBuilder args = new StringBuilder();

        args.Append("q=" + street.Replace(' ', '+') + "+" + town.Replace(' ', '+') + "+" + zipcode.Replace(' ', '+') + "+" + country.Replace(' ', '+'));

        args.Append("&key=" + GoogleMapApiKey);

        args.Append("&sensor=false");

        args.Append("&output=xml");

        args.Append("&oe=utf8");

        //args.Append("&ll=");

        //args.Append("&spn=");

        //args.Append("&gl=");

        try

        {

            string url = BaseUrl + args.ToString();

            WebRequest myRequest = WebRequest.Create(new Uri(url));

            WebResponse myResponse = myRequest.GetResponse();

            XmlReader sr = XmlReader.Create(myResponse.GetResponseStream());

            try

            {

                result = (from el in XElement.Load(sr).Descendants(ns1 + "Point")

                         select el.Element(ns1 + "coordinates").Value).Single();

            }

            finally

            {

                sr.Close();

                myResponse.Close();

            }

            

        }

        catch (WebException webex)

        {

            return webex.Message;

        }

        splittedResult = result.Split(',');

        return splittedResult[1] + "|" + splittedResult[0] ;

    }

    Encore une fois je retourne latitude et longitude dans une string séparées par un ‘| ‘ pour être consistant entre mes différentes manières de récupérer la localisation.

    Bing Maps geocoding services

    Contrairement à l’approche Google qui utilise REST, Bing Maps propose des services similaires en SOAP. Personnellement je préfère cette approche, qui est beaucoup plus lisible et maintenable au fur et à mesure de l’enrichissement et de l’évolution des API.

    La première chose à faire est de demander un compte de développeur sur la plateforme : https://mappoint-css.live.com/mwssignup/Default.aspx?wa=wsignin1.0. Ensuite, depuis un projet mobile, on peut cliquer sur « Add Web Reference » : un petit wizard s’ouvre et nous demande l’url du webservice : il faut rajouter : http://staging.mappoint.net/standard-30/ puis cliquer sur la version « staging ». Ensuite il est conseillé de enommer le service : je l’appelle MapPoint. Ceci étant fait, le proxy vers le service est généré, et à partir de là on peut utiliser dans l’application le service.

    Je vais donc commencer par créer un nouveau service, auprès duquel je vais m’authentifier :

    FindServiceSoap myService = new FindServiceSoap();

    myService.Credentials = new NetworkCredential(Properties.Resources.MapPointUserName, Properties.Resources.MapPointPassword);

    myService.PreAuthenticate = true;

    Ensuite il me faut formater l’adresse : rien de compliqué, j’utilise les mêmes champs qu’avec l’API Google, mais le format est légèrement différent : j’utilise un objet Address :

    Address myAddress = new Address();

    myAddress.FormattedAddress = street + ", " + zipcode + " " + town + ", " + country;

    J’en profite pour configurer quelques options, notamment le fait de pouvoir utiliser l’algorithme « Rooftop » qui me donne des résultats très précis :

    FindOptions myOptions = new FindOptions();

    myOptions.ResultMask = FindResultMask.RooftopFlag |

                           FindResultMask.LatLongFlag |

                           FindResultMask.EntityFlag |

                           FindResultMask.MatchDetailsFlag |

                           FindResultMask.AddressFlag;

    Enfin, il ne reste plus qu’à packager tout ça pour l’envoyer au service : c’est le but de la classe FindAddressSpecification :

    FindAddressSpecification fas = new FindAddressSpecification();

    fas.InputAddress = myAddress;

    fas.Options = myOptions;

    fas.DataSourceName = "MapPoint.EU";

    Dernière étape : récupérer les coordonnées à partir de l’adresse et le renvoyer au format habituel : latitude|longitude :

    FindResults results = myService.FindAddress(fas);

    FindResult bestResult = results.Results[0];

     

    return bestResult.BestViewableLocation.LatLong.Latitude.ToString() + "|" + bestResult.BestViewableLocation.LatLong.Longitude.ToString();

    Un bon article sur le geocoding avec les services Bing: http://msdn.microsoft.com/en-us/library/bb545004.aspx.

    Nous voila donc avec 2 options pour une manière de plus de retrouver des coordonnées GPS précises : à partir d’une adresse.

    Prochaine étape: Afficher une carte!

  • Pierre's Embedded and Mobile Blog

    La Boite à Outils de l’Application Géolocalisée: Partie 1: GPS, et CellId

    • 2 Comments

    Cet article fait partie d'une série de 3: La Boite à Outils de l'Application Géolocalisée

    Une des révolutions de l'intégration du GPS dans le smartphone, c'est la possibilité pour l'utilisateur de se localiser ou qu'il soit et de trouver des informations en rapport avec sa position: que ce soit une carte ou des directions pour aller quelque part, les horaires de métro de la station d'à coté, les critiques du restaurant devant lequel il passe, etc.

    Windows Mobile propose un certain nombre d’outils pour facilement trouver la localisation de l’utilisateur: avec ou sans GPS. Cet article va détailler un certain nombre d’outils pratiques pour l’application qui voudrait proposer du contenu géolocalisé.

    Récupérer sa position

    Avec le GPS

    Le SDK Windows Mobile propose un code d’exemple (Sample Code) qui expose en .NET les API natives permettant d’accéder au GPS. La première chose à faire est donc de compiler ce sample qui est situé dans C:\Program Files\Windows Mobile 6 SDK\Samples\PocketPC\CS\GPS. Ceci étant fait, on peut référencer la DLL générée (qui se trouve dans bin/Debug ou bin/Release) dans un projet et ajouter :

    using Microsoft.WindowsMobile.Samples.Location;

    Ceci étant fait, l’utilisation du GPS est pire que simple. Voici un tout petit bout de code qui permet de récupérer la position courante et qui le place dans le champ Text d’un contrôle de type Label:

    Gps myGps = new Gps();

    GpsPosition myPosition = myGps.GetPosition();

    if (myPosition.LatitudeValid && myPosition.LongitudeValid)

    {

        lblLatResult.Text = myPosition.Latitude.ToString();

        lblLngResult.Text = myPosition.Longitude.ToString();

    }

    else

    {

        MessageBox.Show("Can't get a valid position");

    }

    myGps.Close();

     

    Avec l’identifiant de cellule (CellId)

    Si on veut faire de la géolocalisastion sans avoir de GPS (soit parce qu’il n’y en a pas sur le smartphone, soit parce que la couverture GPS ne le permet pas (à l’intérieur d’un bâtiment par exemple) il est possible de récupérer une localisation à quelques dizaines, voire centaines de mètres près, en utilisant l’identifiant de la cellule dans laquelle le téléphone est connecté au réseau de l’opérateur. En effet, il existe des bases de données qui permettent de faire correspondre un identifiant de cellule à un couple latitude/longitude.

    L’identifiant de station de base est en fait constitué de 4 champs différents :

    -          L’identifiant de pays : (Mobile Country Code)

    -          L’identifiant de réseau : (Mobile Network Code)

    -          Le code de zone (Location Area Code)

    -          L’identifiant de la cellule : (Cell Identifier)

    Ces quatre champs peuvent être récupérés avec une fonction de l’API RIL (Radio Interface Layer) : RIL_GetCellTowerInfo.

    Pour utiliser cette API depuis un programme en .NET il faut donc utiliser un wrapper, mais je n’ai pas besoin de le réinventer : un développeur de la communauté .NET, Dale Lane, l’a déjà fait !

    -          Son code : http://dalelane.co.uk/blog/post-images/080312-rilcode.cs.txt

    -          Son Blog : http://dalelane.co.uk/blog/

    J’ai légèrement modifié le code de Dale, qui à l’origine ne retournait pas de Mobile Network Code. Maintenant, la fonction GetCellTowerInfo() retourne les 4 champs séparés par des tirets de la façon suivante : CellId-LAC-MCC-MNC :

    class CellIdWrapper

    {

        // string used to store the CellID string

        private static string celltowerinfo = "";

     

        /*

         * Uses RIL to get CellID from the phone.

         */

        public static string GetCellTowerInfo()

        {

            // initialise handles

            IntPtr hRil = IntPtr.Zero;

            IntPtr hRes = IntPtr.Zero;

     

            // initialise result

            celltowerinfo = "";

     

            // initialise RIL

            hRes = RIL_Initialize(1,                                        // RIL port 1

                                  new RILRESULTCALLBACK(rilResultCallback), // function to call with result

                                  null,                                     // function to call with notify

                                  0,                                        // classes of notification to enable

                                  0,                                        // RIL parameters

                                  out hRil);                                // RIL handle returned

     

            if (hRes != IntPtr.Zero)

            {

                return "Failed to initialize RIL";

            }

     

            // initialised successfully

     

            // use RIL to get cell tower info with the RIL handle just created

            hRes = RIL_GetCellTowerInfo(hRil);

     

            // wait for cell tower info to be returned

            waithandle.WaitOne();

     

            // finished - release the RIL handle

            RIL_Deinitialize(hRil);

     

            // return the result from GetCellTowerInfo

            return celltowerinfo;

        }

     

     

        // event used to notify user function that a response has

        //  been received from RIL

        private static AutoResetEvent waithandle = new AutoResetEvent(false);

     

     

        public static void rilResultCallback(uint dwCode,

                                             IntPtr hrCmdID,

                                             IntPtr lpData,

                                             uint cbData,

                                             uint dwParam)

        {

            // create empty structure to store cell tower info in

            RILCELLTOWERINFO rilCellTowerInfo = new RILCELLTOWERINFO();

     

            // copy result returned from RIL into structure

            Marshal.PtrToStructure(lpData, rilCellTowerInfo);

     

            // get the bits out of the RIL cell tower response that we want

            celltowerinfo = rilCellTowerInfo.dwCellID + "-" +

                            rilCellTowerInfo.dwLocationAreaCode + "-" +

                            rilCellTowerInfo.dwMobileCountryCode + "-" +

                            rilCellTowerInfo.dwMobileNetworkCode;

     

            // notify caller function that we have a result

            waithandle.Set();

        }

     

     

     

        // -------------------------------------------------------------------

        //  RIL function definitions

        // -------------------------------------------------------------------

     

        /*

         * Function definition converted from the definition

         *  RILRESULTCALLBACK from MSDN:

         *

         * http://msdn2.microsoft.com/en-us/library/aa920069.aspx

         */

        public delegate void RILRESULTCALLBACK(uint dwCode,

                                               IntPtr hrCmdID,

                                               IntPtr lpData,

                                               uint cbData,

                                               uint dwParam);

     

     

        /*

         * Function definition converted from the definition

         *  RILNOTIFYCALLBACK from MSDN:

         *

         * http://msdn2.microsoft.com/en-us/library/aa922465.aspx

         */

        public delegate void RILNOTIFYCALLBACK(uint dwCode,

                                               IntPtr lpData,

                                               uint cbData,

                                               uint dwParam);

     

        /*

         * Class definition converted from the struct definition

         *  RILCELLTOWERINFO from MSDN:

         *

         * http://msdn2.microsoft.com/en-us/library/aa921533.aspx

         */

        public class RILCELLTOWERINFO

        {

            public uint cbSize;

            public uint dwParams;

            public uint dwMobileCountryCode;

            public uint dwMobileNetworkCode;

            public uint dwLocationAreaCode;

            public uint dwCellID;

            public uint dwBaseStationID;

            public uint dwBroadcastControlChannel;

            public uint dwRxLevel;

            public uint dwRxLevelFull;

            public uint dwRxLevelSub;

            public uint dwRxQuality;

            public uint dwRxQualityFull;

            public uint dwRxQualitySub;

            public uint dwIdleTimeSlot;

            public uint dwTimingAdvance;

            public uint dwGPRSCellID;

            public uint dwGPRSBaseStationID;

            public uint dwNumBCCH;

        }

     

        // -------------------------------------------------------------------

        //  RIL DLL functions

        // -------------------------------------------------------------------

     

        /* Definition from: http://msdn2.microsoft.com/en-us/library/aa919106.aspx */

        [DllImport("ril.dll")]

        private static extern IntPtr RIL_Initialize(uint dwIndex,

                                                    RILRESULTCALLBACK pfnResult,

                                                    RILNOTIFYCALLBACK pfnNotify,

                                                    uint dwNotificationClasses,

                                                    uint dwParam,

                                                    out IntPtr lphRil);

     

        /* Definition from: http://msdn2.microsoft.com/en-us/library/aa923065.aspx */

        [DllImport("ril.dll")]

        private static extern IntPtr RIL_GetCellTowerInfo(IntPtr hRil);

     

        /* Definition from: http://msdn2.microsoft.com/en-us/library/aa919624.aspx */

        [DllImport("ril.dll")]

        private static extern IntPtr RIL_Deinitialize(IntPtr hRil);

     

    }

    J’intègre le code de Dale dans une classe CellIdWrapper que je rajoute à mon projet dans un fichier séparé.

    Puis, dans le code de mon application, je récupère ces informations de la façon suivante :

    string cellIdInfo = CellIdWrapper.GetCellTowerInfo();

    string[] splittedInfos = cellIdInfo.Split('-');

     

    lblCellIdResult.Text = splittedInfos[0];

    lblLACResult.Text = splittedInfos[1];

    lblMCCResult.Text = splittedInfos[2];

    lblMNCResult.Text = splittedInfos[3];

    Il faut maintenant que je fasse correspondre cet identifiant à une latitude et une longitude. Pour cela, j’ai trouvé 2 bases différentes à utiliser :

    -          Une API « cachée » de Google Maps

    -          Une API ouverte du groupe OpenCellID

    L’API Cachée de Google Maps

    Petit disclaimer obligatoire : il ne s’agit pas d’un API officielle, elle n’est donc pas supportée par Google, il est tout à fait possible qu’un jour ça ne marche plus…

    Encore une fois, pas besoin de tout réécrire depuis zéro, la communauté va travailler pour moi. Un développeur du nom de Neil Young a développé un bout de wrapper qui fait exactement ce que je veux. Problème, son site n’existe plus, heureusement il a été repris :

    -          Article de Wei Meng sur DevX : http://www.devx.com/wireless/Article/39709/1954

    Comme avec le wrapper de Dale Lane, je rajoute ce wrapper dans un fichier séparé, dans une classe que j’appelle GoogleMapsApi.

    static byte[] PostData(int MCC, int MNC, int LAC, int CID,

                           bool shortCID)

    {

        /* The shortCID parameter follows heuristic experiences:

         * Sometimes UMTS CIDs are build up from the original GSM CID (lower 4 hex digits)

         * and the RNC-ID left shifted into the upper 4 digits.

         */

        byte[] pd = new byte[] {

            0x00, 0x0e,

            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

            0x00, 0x00,

            0x00, 0x00,

            0x00, 0x00,

     

            0x1b,

            0x00, 0x00, 0x00, 0x00, // Offset 0x11

            0x00, 0x00, 0x00, 0x00, // Offset 0x15

            0x00, 0x00, 0x00, 0x00, // Offset 0x19

            0x00, 0x00,

            0x00, 0x00, 0x00, 0x00, // Offset 0x1f

            0x00, 0x00, 0x00, 0x00, // Offset 0x23

            0x00, 0x00, 0x00, 0x00, // Offset 0x27

            0x00, 0x00, 0x00, 0x00, // Offset 0x2b

            0xff, 0xff, 0xff, 0xff,

            0x00, 0x00, 0x00, 0x00

        };

     

        bool isUMTSCell = ((Int64)CID > 65535);

     

        if (isUMTSCell)

            Console.WriteLine("UMTS CID. {0}", shortCID ?

                "Using short CID to resolve." : "");

        else

            Console.WriteLine("GSM CID given.");

     

        if (shortCID)

            CID &= 0xFFFF;      /* Attempt to resolve the cell using the

                                GSM CID part */

     

        if ((Int64)CID > 65536) /* GSM: 4 hex digits, UTMS: 6 hex

                                digits */

            pd[0x1c] = 5;

        else

            pd[0x1c] = 3;

     

        pd[0x11] = (byte)((MNC >> 24) & 0xFF);

        pd[0x12] = (byte)((MNC >> 16) & 0xFF);

        pd[0x13] = (byte)((MNC >> 8) & 0xFF);

        pd[0x14] = (byte)((MNC >> 0) & 0xFF);

     

        pd[0x15] = (byte)((MCC >> 24) & 0xFF);

        pd[0x16] = (byte)((MCC >> 16) & 0xFF);

        pd[0x17] = (byte)((MCC >> 8) & 0xFF);

        pd[0x18] = (byte)((MCC >> 0) & 0xFF);

     

        pd[0x27] = (byte)((MNC >> 24) & 0xFF);

        pd[0x28] = (byte)((MNC >> 16) & 0xFF);

        pd[0x29] = (byte)((MNC >> 8) & 0xFF);

        pd[0x2a] = (byte)((MNC >> 0) & 0xFF);

     

        pd[0x2b] = (byte)((MCC >> 24) & 0xFF);

        pd[0x2c] = (byte)((MCC >> 16) & 0xFF);

        pd[0x2d] = (byte)((MCC >> 8) & 0xFF);

        pd[0x2e] = (byte)((MCC >> 0) & 0xFF);

     

        pd[0x1f] = (byte)((CID >> 24) & 0xFF);

        pd[0x20] = (byte)((CID >> 16) & 0xFF);

        pd[0x21] = (byte)((CID >> 8) & 0xFF);

        pd[0x22] = (byte)((CID >> 0) & 0xFF);

     

        pd[0x23] = (byte)((LAC >> 24) & 0xFF);

        pd[0x24] = (byte)((LAC >> 16) & 0xFF);

        pd[0x25] = (byte)((LAC >> 8) & 0xFF);

        pd[0x26] = (byte)((LAC >> 0) & 0xFF);

     

        return pd;

    }

     

    static public string GetLatLng(string[] args)

    {

        if (args.Length < 4)

        {

            return string.Empty;

        }

        string shortCID = "";   /* Default, no change at all */

        if (args.Length == 5)

            shortCID = args[4].ToLower();

        try

        {

            String url = "http://www.google.com/glm/mmap";

            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(

                new Uri(url));

            req.Method = "POST";

     

            int MCC = Convert.ToInt32(args[0]);

            int MNC = Convert.ToInt32(args[1]);

            int LAC = Convert.ToInt32(args[2]);

            int CID = Convert.ToInt32(args[3]);

            byte[] pd = PostData(MCC, MNC, LAC, CID,

                shortCID == "shortcid");

     

            req.ContentLength = pd.Length;

            req.ContentType = "application/binary";

            Stream outputStream = req.GetRequestStream();

            outputStream.Write(pd, 0, pd.Length);

            outputStream.Close();

     

            HttpWebResponse res = (HttpWebResponse)req.GetResponse();

            byte[] ps = new byte[res.ContentLength];

            int totalBytesRead = 0;

            while (totalBytesRead < ps.Length)

            {

                totalBytesRead += res.GetResponseStream().Read(

                    ps, totalBytesRead, ps.Length - totalBytesRead);

            }

     

            if (res.StatusCode == HttpStatusCode.OK)

            {

                short opcode1 = (short)(ps[0] << 8 | ps[1]);

                byte opcode2 = ps[2];

                int ret_code = (int)((ps[3] << 24) | (ps[4] << 16) |

                               (ps[5] << 8) | (ps[6]));

                if (ret_code == 0)

                {

                    double lat = ((double)((ps[7] << 24) | (ps[8] << 16)

                                 | (ps[9] << 8) | (ps[10]))) / 1000000;

                    double lon = ((double)((ps[11] << 24) | (ps[12] <<

                                 16) | (ps[13] << 8) | (ps[14]))) /

                                 1000000;

                    return lat + "|" + lon;

                }

                else

                    return string.Empty;

            }

            else

                return string.Empty;

        }

        catch (Exception ex)

        {

            return ex.Message;

        }

    }

    Super simple à réutiliser depuis mon code avec la méthode GetLatLng  à qui on passe un tableau de string avec dans l’ordre MCC, MNC, LAC et CellId, et qui me renvoie dans une chaine de caractères la latitude et la longitude séparée par un caractère ‘|’  (pipe) :

    string result = GoogleMapsApi.GetLatLng(args);

     

    string[] splittedResult = result.Split('|');

    lblLatResult.Text = splittedResult[0];

    lblLngResult.Text = splittedResult[1];

    L’API ouverte du groupe OpenCellID

     

    OpenCellId est une initiative qui vise à crowdsourcer un maximum de coordonnées de tour cellulaires. C’est un peu moins précis que la base de données de Google, mais c’est en participant que ca s’améliorera J

    -          Le site web : http://opencellid.org/

    -          L’application Windows Mobile pour aider à crowdsourcer les informations : http://opencellclient.sourceforge.net/

    Pour utiliser l’API OpenCellID, il faut demander une clé d’API, qui est envoyée automatiquement lorsqu’on s’enregistre sur le site d’OpenCellID : http://opencellid.org/users/signup.

    L’API OpenCellID est très simple : c’est du REST, donc j’envoie une requête http, et ils me renvoient les informations dans du XML : l’API est détaillée à l’adresse suivante : http://opencellid.org/api

    Pour plus d’homogénéité dans mon projet je vais encore créer un autre fichier avec à l’intérieur une classe OpenCellIdApi qui contiendra, comme le wrapper pour l’API Google, une fonction GetLatLng, avec le même tableau d’arguments en entrée, et la même chaine de caractères en sortie.

    Voici à quoi ressemble du code pour interroger la base OpenCellID :

    struct OCIWebServiceResponse

    {

        public string lat;

        public string lng;

        public string status;

        public string nbSamples;

        public string range;

     

    }

     

    static public string GetLatLng(string[] args)

    {

        if (args.Length < 4)

        {

            return "Not Enough Arguments";

        }

        OCIWebServiceResponse response;

        StringBuilder urlArgs = new StringBuilder("key=" + myApiKey);

        urlArgs.Append("&mnc=" + args[1]);

        urlArgs.Append("&mcc=" + args[0]);

        urlArgs.Append("&lac=" + args[2]);

        urlArgs.Append("&cellid=" + args[3]);

        try

        {

            WebRequest myRequest = WebRequest.Create(new Uri(BaseUrl + urlArgs.ToString()));

            WebResponse myResponse = myRequest.GetResponse();

            XmlReader myReader = XmlReader.Create(myResponse.GetResponseStream());

            try

            {

                response = (from rsp in XDocument.Load(myReader).Descendants("rsp")

                            let cell = rsp.Descendants("cell").Single()

                            select new OCIWebServiceResponse

                            {

                                status = rsp.Attribute("stat").Value,

                                lat = cell.Attribute("lat").Value,

                                lng = cell.Attribute("lon").Value,

                                nbSamples = cell.Attribute("nbSamples").Value,

                                range = cell.Attribute("range").Value

                            }).Single();

            }

            finally

            {

                myReader.Close();

                myResponse.Close();

            }

        }

        catch (Exception ex)

        {

            return ex.Message;

        }

        if (response.status != "ok")

            return "Response from OpenCellId not OK!";

        else

            return response.lat + "|" + response.lng;

    }

     

    Pour parser la réponse en XML, j’utilise une requête LINQ et une structure « maison » qui rend mon code plus lisible.

    Point important, c’est du code de démo, la gestion des erreurs est assez pauvre, même carrément mauvaise : il ne faudrait pas retourner une chaine de caractère comme je le fais mais plutôt lever une exception !

    Prochaine étape: Geocoding d'adresse!

  • Pierre's Embedded and Mobile Blog

    Windows Embedded s’offre un tout nouveau site en Français

    • 0 Comments

    En remplacement d’un site produit vieillissant, et en complément du centre de développement embedded sur MSDN, la division Windows Embedded vient de localiser complètement son site web en Français: http://www.windowsembedded.fr. Vous y retrouverez les dernières versions des produits, les nouveautés, les liens utiles, les certifications… bref, tout ce qu’il faut savoir sur l’embarqué chez Microsoft :) Consécration s’il en est, nous avons même le droit à un lien en homepage du http://www.microsoft.fr (la classe!)

  • Pierre's Embedded and Mobile Blog

    Le Porting Kit du .NET MicroFramework est maintenant disponible gratuitement

    • 0 Comments

    Petit à petit, le .NET MicroFramework va devenir Open Source. C’est ce qu’annonçait Colin Miller, la tête pensante de l’équipe, sur son blog dont je vous repointe l’historique:

    Il annonce aujourd’hui que le Porting Kit est disponible gratuitement sur download.microsoft.com:

    Tout ça fleure relativement bon, et je prédit du bon contenu autour du .NET MicroFramework cet été :)

  • Pierre's Embedded and Mobile Blog

    Débuter avec Windows Mobile: facile avec le coach!

    • 0 Comments

    A l’occasion du Mobility Briefing, la grand-messe de la mobilité de Microsoft France, nous avons lancé le premier atelier du coach Windows Mobile. C’est le point de départ idéal pour commencer à se former au développement mobile: c’est un tutoriel qui regroupe à la fois un webcast, un article, un cahier d’exercice, en C# et en VB.NET, et ce premier atelier explique l’installation des outils, l’écriture du premier programme, l’utilisation de l’émulateur, et du debugger.

    Ce n’est que le début d’une grande vague de contenu qui prépare l’arrivée de Windows Mobile 6.5 dans les bacs! Déjà de nombreux autres ateliers vont voir le jour au cours des semaines à venir, en parallèle d’une semaine spéciale Mobilité dans l’opération “J’en ai rien à coder!” de cet été, d’une série d’articles et de webcasts techniques sur des sujets pointus (connection manager, openGL, etc). Si vous ne savez pas quoi faire avec votre laptop sur la plage cet été, je crois que vous venez de trouver!

    J’en profite pour rajouter les quelques liens qu’on me demande souvent:

  • Pierre's Embedded and Mobile Blog

    Le SDK de Windows Mobile 6.5 est sorti!!

    • 0 Comments

    Woooohhooooooo! depuis le temps qu’on l’attendait celui là! Voici le lien à partir duquel vous pourrez télécharger les émulateurs pour Windows Mobile 6.5 Pro mais aussi standard, et en versions localisées s’il vous plait!

    Bien entendu, ces émulateurs contiennent le nouveau home screen et les contrôles reskinnés et pilotables au doigt, mais également, le moteur de Widgets, IE Mobile 6, et même… Roulement de tamblours… la petite surprise… (tambours plus forts…) La Gesture API! Oui mesdames et messieurs, une API pour comprendre les gestes de votre utilisateur (pour écran tactiles uniquement donc…) et les utiliser dans votre application: et comme on ne fait pas les choses à moitié, on la livre avec un code d’exemple, situé dans le dossier Samples du SDK

    On parlera de ces nouveautés beaucoup plus en détail sur ce blog d’ici très peu!

    Et pour le plaisir, encore une fois, le lien!

  • Pierre's Embedded and Mobile Blog

    En direct de la Mobile Incubation Week à Londres!

    • 3 Comments

    J’en parlais dans un billet précédent, et c’est maintenant un réalité: la Mobile Incubation Week a démarré aujourd’hui. 8 startups sont hébergées pour une semaine dans les bureaux de Microsoft à Londres, en partenariat avec Muranosoft et Avanade pour une semaine de développement intensive avec de l’aide de coachs, architectes, et développeurs supplémentaires, en appliquant des méthodes de développement agile et d’eXtreme Programming :) Le blog de la Microsoft Startup Zone annonçait déjà les sélectionnés la semaine dernière, je vous laisse consulter l’info à la source :)

    Ce type d’évènement a déjà été un succès aux Etats-Unis, et semble bien parti en Angleterre. Peut-être le verrons nous répliqué en France? Qu’en pensez-vous? Des volontaires peut-être?

Page 1 of 1 (8 items)