Location: associate geographic location
The location module allows you to associate a geographic location with content and users. Users can do proximity searches by postal code. This is useful for organizing communities that have a geographic presence.
To administer locative information for content, use the content type administration page. To support most location enabled features, you will need to install the country specific include file. To support postal code proximity searches for a particular country, you will need a database dump of postal code data for that country. As of June 2005 only U.S. postal codes are supported.
A database dump for most countries postal codes can be downloaded from GeoNames. The current full URL for the downloads page is http://download.geonames.org/export/zip/.
You can
- administer locative information at administer >> content types to configure a type and see the locative information.
- administer location at administer >> settings >> location.
- use a database dump for a U.S. postal codes table that can be found at zipcode database.
- enable a CiviCRM profile with street address, supplemental address 1, city, postal code, state, and country that will be synchronized between CiviCRM and the location module.
- file issues, read about known bugs, and download the latest version on the Location project page.

Example of programmatically adding a node with a location
Hope this helps someone.
Problem: I needed to create a new node/location in the callback function of a Views Bulk Operation (VBO). The view has provided the callback function a list of contractor node IDs within a given radius of an entered postcode (in $objects). and now we need to create a new enquiry node pre-populated with the list of contractors and the postcode entered in the view filter (form $context).
The user must then be presented with an edit form to complete the rest of the fields.
Solution:
/**
* Implementation of a Drupal action.
* Create a new enquiry node and populate the passed contractors in the contractors CCK field.
* Create a new location attached to the node, prepopulated with the postcode from the view
* Redirect user to enquiry edit form to manually complete the details
* @param $objects array of selected contractor node IDs
* @param $context array of other parameters (parameters from the view are in here)
*/
function contractor_enquiry(
$objects,
$context = array()
) {
global $user;
module_load_include('inc', 'node', 'node.pages');
$node = array();
$node['type'] = 'enquiry';
$node['name'] = $user->name; // node created by
/*
* populate contractor list from the contractor node_id list in $objects
*/
foreach ( $objects as $key => $nid ){
$node['field_contractor'][$key]['nid']= $nid;
}
$node = (object) $node; // cast to object
$node = node_submit($node); // prepare node for save and allow module hooks
node_save($node);
$view = (array) $context['view'];
/*
* Create a location already populated with the postcode from the view filter.
*/
$locations = array();
$locations[0]['postal_code'] = $view['exposed_input']['distance']['postal_code'];
$criteria = array();
$criteria['nid'] = $node->nid;
$criteria['vid'] = $node->nid;
$criteria['genid'] = 'contractor_enquiry';
location_save_locations( $locations, $criteria );
drupal_goto( 'node/'. $node->nid . '/edit' );
}