Jim's Blog Ramblings about novels, comics, programming, and other geek topics

14Mar/123

How to convert a street address to longitude and latitude (geocoding) via web services (Google)

Google AdSense

Several years ago, I wrote a blog entry detailing how to use geocoder.us (and Yahoo!) web services to geocode an address. Since then both web services have changed a little and I found myself needing to update my code to support non-US locations. Below is sample code showing how to use Google’s Geocoding web service to get longitude and latitude values for addresses.

10Sep/080

Would you like a soda, pop, or coke with that?

Google AdSense

When I moved to California, I used the term "pop" to refer to carbonated soft drinks (such as Coca-Cola and Pepsi). I assume that's because it was the most popular term used where I was coming from. Very few people knew what I meant, except other people from the same region or the mid-west.

After a few years, I adapted and began calling it "soda." Now, most of the time I just order by name (if there's a choice on a menu, etc.) and just say "soda" or "drink" if it's one of those self-serve fountain machines.

StrangeMaps blog has a little discussion started on this interesting map. The "Soda vs Pop" web site has compiled the usage of these terms into a map based on individual respondents to their survey by county, which can be viewed on the Soda vs Pop web site

5Oct/0715

How to convert a street address to longitude and latitude (geocoding) via web services

Several years ago, I had a school project that required converting some addresses to longitude/latitude coordinates. We then took the coordinates and placed them on a street map for a mobile device application. The addresses came from a "Yellow Pages" like system lookup service which provided a phone number, street address, name, and business category. But the Yellow Pages service didn't provide a geographic coordinate.

First, I tracked down a free and very useful web service that provides this functionality. The web site GeoCoder.us provides a few different free web services including geocoding on street address, ZIP code, or city and state. GeoCoder.us offers SOAP, XML-RPC, REST-ful RDF, and REST-ful CSV formats.

We'll use the web service the requires a street address, so we can get a more accurate coordinate.

string street = "1060 west addison street";
string city = "chicago";
string state = "il";

The GeoCoder.us URL requires a specific parameter format. Here we append the street, city, and state to the base URL.  Then we create and load the URL into an XML document. By passing the URL to the XmlDocument Load method, it will handle calling and reading the web response.

string geocoderUri = string.Format(
    "http://rpc.geocoder.us/service/rest?address={0},{1},{2}",
    street, city, state);
XmlDocument geocoderXmlDoc = new XmlDocument();
geocoderXmlDoc.Load(geocoderUri);

Next, we need to add a namespace manager since GeoCoder.us uses the W3.org Basic Geo (WGS84 lat/long) vocabulary.  Below is an example of this vocabulary and the actual response that was loaded into our XmlDocument.

<rdf:RDF xmlns:dc="http://purl.org/dc/elements/1.1/" 
         xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" 
         xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<geo:Point rdf:nodeID="aid62658379">
<dc:description>1060 W Addison St, Chicago IL 60613</dc:description>
<geo:long>-87.655788</geo:long>
<geo:lat>41.947372</geo:lat>
</geo:Point>
</rdf:RDF>

The namespace manager is needed so that we can select the long and lat XML nodes. Below we select the two XML nodes and parse them.

XmlNamespaceManager nsMgr =
    new XmlNamespaceManager(geocoderXmlDoc.NameTable);
nsMgr.AddNamespace("geo",
    @"http://www.w3.org/2003/01/geo/wgs84_pos#");

string sLong = geocoderXmlDoc.DocumentElement.SelectSingleNode(
        @"//geo:long", nsMgr).InnerText;
string sLat = geocoderXmlDoc.DocumentElement.SelectSingleNode(
        @"//geo:lat", nsMgr).InnerText;

double latitude = Double.Parse(sLat);
double longitude = Double.Parse(sLong);

Console.WriteLine("Lat: " + latitude + " Lon: " + longitude);

Finally, the coordinates are printed to the console and appear as:

Lat: 41.947372 Lon: -87.655788

Update: 10/6/2007 Once you have the coordinates, you can perform radius searching. For example, you can query for all records within 5 miles of each other or find the nearest record to another record. Troy DeMonbreun provides an example of how to calculate the distance between two geographic coordinates using a SQL Server UDF (User Defined Function). The GeoCoder.us blog also discusses how to calculate distances using two geographic coordinates.

Update 2: 10/19/2007 Yahoo also provides a very nice Web Service for geocoding at Yahoo! Maps Web Services - GeoCoding API. Yahoo uses the same REST method for the accessing the Web Service, but with separate URL parameters for city, address, and state. Yahoo's XML response doesn't use namespaces and won't require the use of a NamespaceManager. You'll be able to just use SelectSingleNode(@"//Longitude") and SelectSingleNode(@"//Latitude") to collect the geographic coordinates.

 

kick it on DotNetKicks.com

4Oct/076

How to resize and/or convert large image files

If you happen to work in the GIS industry, you'll routinely come across large image data sets. Some TIFF files might be several hundred megabytes each.  Although, these files look great when printed and should be kept as the source imagery. They are a bit too large for daily use and especially if you are zoomed out to a scale where multiple images are tiled onto the same screen.

This is where you can make lower resolution and lower quality images and I'll demonstrate how simple it is to do using .NET System.Drawing.Imaging classes. First, we'll make a console application so we can run it unattended at night or use batch files to call and execute the application.

The application entry point will begin as:

static void Main(string[] args)

Our main function will contain four (4) arguments and be called such as "imgcnvt.exe [drive:][path] [drive:][path] [JPEG|TIFF] [scaleFactor]." For this example, we'll support converting to JPEG and TIFF formats and use a scale factor to determine if any resampling will occur.

ImageFormat outFormat = ImageFormat.Jpeg;            
switch (args[2].ToUpper())
{
     case "JPEG": case "JPG":
         outFormat = ImageFormat.Jpeg; break;
     case "TIFF": case "TIF":
         outFormat = ImageFormat.Tiff; break;
     default:
         Console.WriteLine("ERROR!");
         return;
}
float scaleFact;
if (!float.TryParse(args[3], out scaleFact))
{
      Console.WriteLine("ERROR! The scale factor is invalid.");

return;
} if (scaleFact <= 0f || scaleFact > 1f) { Console.WriteLine("ERROR! Please enter a value >= 0 and <= 1."); return; } string currentDir = args[0]; string outputDir = args[1]; if (!Directory.Exists(outputDir)) Directory.CreateDirectory(outputDir);

Now we have some basic error handling and our four variables (outFormat, scaleFact, currentDir, and outputDir) defined and ready for processing. Passing a scale factor value of 1 would result in no scaling and a scale factor of 0.5 would change a 5000x5000 pixel image to 2500x2500 pixels.

Next, we'll get a list of the images files in the current directory and loop through each file and call a Process TIF function using the filename and other settings as arguments.

 foreach(string filename in Directory.GetFiles(currentDir, "*.tif"))
     ProcessTif(filename, outputDir, outFormat, scaleFact);

In the ProcessTif function, we will first create a string that will hold the output file name. This file name will be the concatenation of the outputDir and the filename without extension (we will append the extension associated with the outFormat). For example, if the output is a ImageFormat.Tiff the output file name will be:

string outImage = Path.Combine(outputDir, 
               Path.GetFileName(filename));

Some of the images that I was processing were over five hundred megabytes and loading those into memory resulted in a few memory and processing problems, so I needed to specifically call the garbage collector before I began to process each image to clear out the old image. Calling the garbage collector fixed my memory problems, but you shouldn't ever need to do this.

System.GC.Collect();
System.GC.WaitForPendingFinalizers();

Okay, here's the important pieces of the code. We will now begin the processing of the image and change its format and/or scale.

using (Image fullSizeImg = Image.FromFile(filename))
{
     Image.GetThumbnailImageAbort dummyCallBack = 
          new Image.GetThumbnailImageAbort(ThumbnailCallback);
     int width = (int)(fullSizeImg.Width * scaleFactor);
     int height = (int)(fullSizeImg.Height * scaleFactor);
     using (Image lowResImg  = fullSizeImg.GetThumbnailImage(
             width, height, dummyCallBack, System.IntPtr.Zero))
     {
          lowResImg.Save(outImage, outFormat);
     }
}

Here we are loading the source image from file (filename) to fullSizeImg variable. Next, we setup a ThumbnailImageAbort delegate and calculate the output image's width and height. Then we call the GetThumbnailImage function using the output image dimensions, delegate, and callback data. Lastly, we call Save and provide the filename and image format.

For the ThumbnailImageAbort delegate, I just used a dummy function that always returns false such as:

private static bool ThumbnailCallback()
{ return false; }

Of course, there's much more error trapping and exception handling that you should add to these code snippets, but this should point you in the right direction.

By processing our image data using this technique we were able to create several hundred JPG images (with JGW files) that were between 5 and 15 megabytes. That was a huge performance gain and helped offload the network from processing the several hundred TIFF images that were about 500 megabytes each. Using a scale factor of 1, left us with great images that were only slightly lossy. Also,  our TIFF images were 32 bit and the resulting JPG images were 24 bit, but there weren't any loss in color.

World Files

You can also use a similar technique using StreamReader and StreamWriter to include processing of the world files (TWF) . Within a TFW file (or JGW), there are six lines:

  1. Line 1 - Cell size in the "X" direction
  2. Line 2 - "Rotation" term for the row
  3. Line 3 - "Rotation" term for the column
  4. Line 4 - Cell size in the "Y" direction
  5. Line 5 - Easting value of insertion point "X"
  6. Line 6 - Northing value of insertion point "Y"

To update the world file just divide the value in Line 1 and Line 4 by the scale factor used to process the image. All of the other lines in the world file will remain the same.

My Related Posts: , , ,