Using CouchDB For Storing Google Geocoded JSON Data

Google provides a free service that takes any string as an input and returns a bunch of JSON encoded data if that string matches a physical location. Check it out here: http://code.google.com/apis/maps/documentation/services.html#Geocoding

Unfortunately, Google's geocoding service is limited to 15k requests per day per IP. Sounds like a lot, but for certain applications this limit can be reached very quickly.

The following small library can help you get around these limitations by storing a local repository of data in CouchDB. This is a nice fit because CouchDB is JSON-native and the data returned from google is JSON-native.

The following is a rough and tumble library I put together called GeoCouch that you can use to easily handle geocoded data in CouchDB. This lib has a narrow scope right now - the requirements are:

Download the file here: http://geocouch.googlecode.com/files/geocouch.php. You can also find the full source at the bottom of the page.

Usage

<?php
        require ('geocouch.php');
        
        /*
         * Don't forget to edit the $GeoCouch->conf parameters!
         */
        $GeoCouch = new GeoCouch();
        
        /*
         * The all-in-one method.
         * This geocodes the string and writes it to CouchDB
         * The second parameter is any other fields other
         * than the Google data that you want to save along
         * with this document.
         * 
         * NOTE: if this address already exists in CouchDB
         * a new revision is created.
         * 
         * Returns the CouchDB response, i.e.:
         * {"ok" : true, "rev":"3825793742", "id" : "dallas-tx" }
         */
        $GeoCouch->save('Dallas, TX', array('custom_field' => 'value')); 
        
        /*
         * Simply geo coding.  
         * Does not write to CouchDB.
         * Returns an Google Geocoded Object.
         */
        $geoObj = $GeoCouch->geoCode('Dallas, TX');
        
        /*
         * Write some Geo JSON to CouchDB.
         * First parameter is a unique name for the data
         * Second parameter is the JSON - in 
         * this case the json_encoded $geoObj from above.
         */
        $GeoCouch->put('Dallas, TX', json_encode($geoObj));
        
        /*
         * Get some existing geo data
         */
        $geoObj = $GeoCouch->get('Dallas, TX');
?>

GeoCouch Class

<?php

        class GeoCouch
        {
                var $conf = array(
                        'host' => 'localhost',
                        'port' => '5984',
                        'db' => 'sf_geo',
                        'geocoder' => array(
                                        'url' => 'http://maps.google.com/maps/geo?key=',
                                        'key' => 'Your Google API Key',
                                ),
                );
                
                var $address;
                var $geoJSONResponse;
                var $geoObj;
                
                function GeoCouch() {
                        
                }
                
                function geoCode($address = null)
                {
                        $this->address = $address;
                        $url = $this->conf['geocoder']['url'].$this->conf['geocoder']['key'];
                        $url .= '&q='.urlencode($address);
                        
                        $this->geoJSONResponse = $this->_geoCodeRequest($url);
                        $this->geoObj = json_decode($this->geoJSONResponse);
                        
                        if(empty($this->geoObj->Status->code) || $this->geoObj->Status->code != 200) {
                                return false;
                        } else {
                                return $this->geoJSONResponse;
                        }       
                }
                
                function _geoCodeRequest($url) 
                {
                        $ch = curl_init();
                        curl_setopt ($ch, CURLOPT_URL, $url);
                        curl_setopt ($ch, CURLOPT_HEADER, 0);
                        ob_start();
                        curl_exec ($ch);
                        curl_close ($ch);
                        $string = ob_get_contents();
                        ob_end_clean();
                        return $string;
                }
                
                function locationName($str) {
                        return trim(preg_replace('/[^a-z0-9]+/i', '-', $str), '_');
                }
                
                function save($address, $extra = array())
                {
                        $existing = $this->get($address);
                        
                        if($this->geoCode($address)) 
                        {
                                if(!empty($existing->_rev)) {
                                        $this->geoObj->_rev = $existing->_rev;
                                }
                                
                                foreach($extra as $field => $value) {
                                        $this->geoObj->$field = $value;
                                }
                                
                                return $this->put($address, json_encode($this->geoObj));
                        }
                        else {
                                return false;
                        }
                }
                
                function get($name = null)
                {
                        $s = $this->openSock();
                        
                        $url = '/'.$this->conf['db'].'/'.$this->locationName($name);
                        $request = 'GET '.$url.' HTTP/1.0'. "\r\n";
                        $request .= 'Host: localhost'. "\r\n\r\n";
                        fwrite($s, $request);
                        
                        return $this->parseCouchResponse($s);           
                }
                
                function put($name = null, $json = null)
                {
                        $s = $this->openSock();
                        
                        $url = '/'.$this->conf['db'].'/'.$this->locationName($name);
                        $request = 'PUT '.$url.' HTTP/1.0'. "\r\n";
                        $request .= 'Host: localhost'. "\r\n";
                        $request .= 'Content-Length: '.strlen($json)."\r\n\r\n";
                        $request .= $json."\r\n";
                        fwrite($s, $request);
                        
                        return $this->parseCouchResponse($s);   
                }
                
                function openSock() 
                {
                        $s = fsockopen($this->conf['host'], $this->conf['port'], $errno, $errstr);
                        if(!$s) {
                                return $errno.':'.$errstr;
                        } else {
                                return $s;
                        }
                }
                
                function parseCouchResponse($s) 
                {
                        $response = '';
                        while(!feof($s)) {
                                $response .= fgets($s);
                        }
                        fclose($s);
                        
                        list($headers, $body) = explode("\r\n\r\n", $response);
                        return json_decode($body);
                }
        }
?>

Storing_GeoData (last edited 2009-09-20 21:44:49 by localhost)