Mapping/Geolocating multiple locations based on keyword mix

Topics: General
Nov 7, 2010 at 2:37 AM

I have been playing with Gmap.Net for a few days and quite easily embedded map searching functionality into my desktop WInForm app. Great work, radioman. When I have time I am considering to put an obelisk in my backyard with your name on it ;-)
I can easily map a contact provided he has address or even part of it. The problem I sumbled on was in a simple request of my daughter to make it find all adacent starbucks, something that current API do not provide (or at least I did not find how). So I read about 20 discussions here and adopted piece of code of somebody (hopefully the latter will forgive me for not remembering his name). Basically, my keywords to search are "Starbucks, Seattle" which produces URL: http://maps.google.com/maps?q=Starbucks,+Seattle,+WA&hl=en This produces response and when I parse it by searching "latlng:{" in a loop and able to discover the lat/lng, address, and even the phone number of the location. So far so good. I am getting 10 locations. So since I wondered why there are so few Starbucks in Seattle I entered the same URL on the address line in the Google Map in IE. Therem I got way more locations displayed (by order of magnitude). I save the response that I get in GMap.NET and researched it again thoroughly but could not discover there where all those locations/coordinates that I am missing... (I which I could attached two map here for comparison). At any rate my question is how do I find the rest of the locations? I guess the browser is doing some addition prosessing, that is sending additional requests for more info?

Here the code I added to Gmaps.cs:

public GeoCoderStatusCode GetLatLngFromAddress(string keywords, Hashtable posCollection)
      {
          return GetLatLngFromAddressUrl(MakeAddressUrl(keywords), UseGeocoderCache, posCollection);
      }

      internal string MakeAddressUrl(string keywords)
      {
          string key = keywords.Replace(' ', '+');
          return string.Format("http://maps.google.com/maps?q={0}&hl={1}", key, LanguageStr);
      }

      internal GeoCoderStatusCode GetLatLngFromAddressUrl(string url, bool useCache, Hashtable posCollection)
      {
          GeoCoderStatusCode status = GeoCoderStatusCode.Unknow;
          //PointLatLng? ret = null;
          try
          {
              string urlEnd = url.Substring(url.IndexOf("maps?q="));

#if !PocketPC
              char[] ilg = Path.GetInvalidFileNameChars();
#else
            char[] ilg = new char[41];
            for(int i = 0; i < 32; i++)
               ilg[i] = (char) i;

            ilg[32] = '"';
            ilg[33] = '<';
            ilg[34] = '>';
            ilg[35] = '|';
            ilg[36] = '?';
            ilg[37] = ':';
            ilg[38] = '/';
            ilg[39] = '\\';
            ilg[39] = '*';
#endif

              foreach (char c in ilg)
              {
                  urlEnd = urlEnd.Replace(c, '_');
              }

              string addr = useCache ? Cache.Instance.GetGeocoderFromCache(urlEnd) : string.Empty;

              if (string.IsNullOrEmpty(addr))
              {
                  HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
                  if (Proxy != null)
                  {
                      request.Proxy = Proxy;
#if !PocketPC
                      request.PreAuthenticate = true;
#endif
                  }
                  else
                  {
#if !PocketPC
                      request.Proxy = WebRequest.DefaultWebProxy;
#else
                  request.Proxy = GlobalProxySelection.GetEmptyWebProxy();
#endif
                  }

                  request.UserAgent = UserAgent;
                  request.Timeout = Timeout;
                  request.ReadWriteTimeout = Timeout * 6;
                  request.KeepAlive = false;

                  using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
                  {
                      using (Stream responseStream = response.GetResponseStream())
                      {
                          using (StreamReader read = new StreamReader(responseStream))
                          {
                              addr = read.ReadToEnd();
                          }
                      }
                  }
              }

              if (addr.Length != 0)
              {
                  string[] chunks;
                  string[] ltln_separators = new string[] { "latlng:{", "lat:", "lng:", "}", "," };

                  do
                  {
                      var lt_ln_s = addr.IndexOf("latlng:{");
                      if (lt_ln_s == -1)
                      {
                          status = GeoCoderStatusCode.G_GEO_UNKNOWN_ADDRESS;
                          break;
                      }

                      var lt_ln_e = addr.IndexOf("}", lt_ln_s);
                      if (lt_ln_e == -1)
                      {
                          status = GeoCoderStatusCode.G_GEO_UNKNOWN_ADDRESS;
                          break;
                      }

                      try
                      {
                          var ltlns = addr.Substring(lt_ln_s, lt_ln_e - lt_ln_s);
                          addr = addr.Remove(0, lt_ln_e);
                          chunks = ltlns.Split(ltln_separators, StringSplitOptions.None);
                          double lat = double.Parse(chunks[2], CultureInfo.InvariantCulture);
                          double lng = double.Parse(chunks[4], CultureInfo.InvariantCulture);

                          var poi_address = "";
                          var phone = "";
                          var geocode = "";
                          var idx2 = 0;

                          var idx1 = addr.IndexOf("laddr:\"");
                          if (idx1 != -1)
                          {
                              addr = addr.Remove(0,idx1+7);
                              idx2 = addr.IndexOf("\"");
                              if (idx2 != -1)
                                  poi_address = addr.Substring(0, idx2);
                          }

                          idx1 = addr.IndexOf("geocode:\"");
                          if (idx1 != -1)
                          {
                              addr = addr.Remove(0,idx1+9);
                              idx2 = addr.IndexOf("\"");
                              if (idx2 != -1)
                                  geocode = addr.Substring(0, idx2);
                              else
                                  continue;
                          }

                          idx1 = addr.IndexOf("sxph:\"");
                          if (idx1 != -1)
                          {
                              addr = addr.Remove(0,idx1+6);
                              idx2 = addr.IndexOf("\"");
                              if (idx2 != -1)
                                  phone = addr.Substring(0, idx2);
                          }
                          
                          Placemark geoPos = new Placemark(lat, lng, poi_address, phone);
                          posCollection.Add(geocode, geoPos);

                      }
                      catch (Exception ex)
                      {
                          status = GeoCoderStatusCode.G_GEO_BAD_REQUEST;
                          Debug.WriteLine("GetLatLngFromGeocoderUrl: " + ex.ToString());
                          throw;
                      }


                  } while (true);
              }

          }
          catch (Exception ex)
          {
              Debug.WriteLine("GetLatLngFromGeocoderUrl: " + ex.ToString());
          }

          if (posCollection.Count != 0)
              status = GeoCoderStatusCode.G_GEO_SUCCESS;

          return status;
      }

 

and here is the code I added to GMapControl:

      public GeoCoderStatusCode GetPositionsByKeywords(string keys, Hashtable posCollection)
      {
          GeoCoderStatusCode status = GeoCoderStatusCode.Unknow;
          status = Manager.GetLatLngFromAddress(keys, posCollection);
          return status;
      }

On return I have hashtable that is filled with Placemarker objects while the keys are the corresponding IDs that I retrieve from  "geocode:" part of the response (sort of combo of small and capital letters - looks unique, I hope)
I also changed Placemark class and simily added there PointLatLng in order to have lat/lng be associated with the address, phone, etc...

I'd really would appreciate if anybody can shed some light on how to get ALL of the locations the same way that you can see in IE.

Coordinator
Nov 7, 2010 at 4:48 PM

;} hm..

Nov 7, 2010 at 9:46 PM
Edited Nov 8, 2010 at 8:12 AM

I did some additional research and came to conclusion that the url that is being submitted to google (for now I am into google only) can be improved by providing additional parapeters. My goal is to find all establishment (like Starbucks) around me, in a distance of X miles/km.
Here is the list of parameters that can be used for the url:

&sll=lat,lng - the lat/lng of the center point of the local search
&spn=lats,lngs - the span of the entire map (lat and lng)
&sspn=lats,lngs - search span for the local search

these are not all by all means, but mostly relevant.
there is also $radius=value but this one does not seem to work consistently - I am still playing with it from IE address box...
also there is &num=X where X by default is 10 and has range of 0-20.

So I started creating API like that GMAP and will be done in couple of hours and let you know what I discovered.
In fact Google has Place API which are here: http://code.google.com//apis/maps/documentation/places but it seems they have more sever restrictions and requirements... another topic for research. Google recommends to use them when the keywords contain non-address components (like Starbucks).

I'll get back to you on this when I see the results that I get from within GMAP

A quick question: can you point me how to conver this span (which is I think in degrees) to miles/km?

=== tested it from within the GMAP - the results are different than on IE address line while inputing the same URL. I used &sll= in the user and also &num=20 - those really work, however I dont get all those secondary points of starbucks that you can see on the map when inputing the same url on IE address line. It is more annoying than critical - something that Google does not tell us?