Changeset 3

Show
Ignore:
Timestamp:
10/07/08 11:12:56 (3 years ago)
Author:
blundeln
Message:

Tidied up the code a bit. I thought that, since its now under illuminate's new version control system, I'd better
write nicer code ;)

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • drupal/modules/simple_maps/trunk/TODO

    r2 r3  
    1 - Rename module. 
    2 - put under version control. 
     1- bit more testing 
  • drupal/modules/simple_maps/trunk/simple_maps.info

    r2 r3  
    11name = Simple Maps 
    2 description = A very easy way to create location-aware nodes and maps based on standard node views. 
     2description = A very easy way to create location-aware nodes and maps based on standard node views.  Simply add an address and/or postcode CCK field to any node and it will be geocoded when saved. 
    33package = IlluminateICT 
    44dependencies = gmap 
    5 version = "5.x-1.0.a1" 
     5version = "5.x-1.0-a1" 
  • drupal/modules/simple_maps/trunk/simple_maps.module

    r2 r3  
    11<?php 
    2  
     2/* GPL: Nick Blundell (www.illuminateict.org.uk) 2008 */ 
     3 
     4 
     5/*  
     6 * =============================== 
     7 * GLOBALS AND DEFINITIONS 
     8 * =============================== 
     9 */ 
     10 
     11/* To improve geocoding (Google API is less accurate than the google maps server.) use this to identify nearby locations. */ 
    312define(PROXIMITY_THRESHOLD, 0.0565); 
    413 
    5 function d($message, $item=Null, $print=False) { 
    6   if ($item) { 
    7     $message .= ": " . print_r($item,True); 
    8   } 
    9   if ($print) { 
    10     print("DEBUG: <pre>$message</pre>"); 
    11   } else { 
    12     drupal_set_message("DEBUG: <pre>$message</pre>"); 
    13   } 
    14 
    15  
     14 
     15 
     16/*  
     17 * =============================== 
     18 * DEBUG STUFF 
     19 * =============================== 
     20 */ 
     21 
     22if (!function_exists("d")) { 
     23  function d($message, $item=null, $print=false) { 
     24    if ($item) { 
     25      $message .= ": " . print_r($item,true); 
     26    } 
     27    if ($print) { 
     28      print("debug: <pre>$message</pre>"); 
     29    } else { 
     30      drupal_set_message("debug: <pre>$message</pre>"); 
     31    } 
     32  }  
     33
     34 
     35 
     36/* 
     37 * =============================== 
     38 * DRUPAL HOOKS 
     39 * =============================== 
     40 */ 
     41 
     42/* Define our menu items. */ 
    1643function simple_maps_menu($may_cache) { 
    1744  $items = array(); 
     
    3764} 
    3865 
    39 function _normaliseAddress($address) { 
    40   $nAddress = strtolower($address); 
    41    
    42   if (stristr($nAddress, "<br/>")) { $nAddress = str_replace("<br/>", "\n", $nAddress); } 
    43   if (stristr($nAddress, "<br>")) { $nAddress = str_replace("<br>", "\n", $nAddress); } 
    44   if (stristr($nAddress, "</p>")) { $nAddress = str_replace("</p>", "\n", $nAddress); } 
    45   if (stristr($nAddress, ",")) { $nAddress = str_replace(",", "\n", $nAddress); } 
    46  
    47   //d("nAddress: $nAddress",Null, True); 
    48   return $nAddress; 
    49 
    50  
    51 function _validateUKLocation($loc) { 
    52   /* Check in UK */ 
    53   if ($loc["status"] != 200) { 
    54     return Null; 
    55   } 
    56  
    57   if ($loc["latitude"] > 62.0 or $loc["latitude"] < 50.0) { 
    58     return Null; 
    59   } 
    60  
    61   if ($loc["longitude"] < -12.0 or $loc["longitude"] > 4.0) { 
    62     return Null; 
    63   } 
    64  
    65   return $loc; 
    66 
    67  
    68 function _smart_geocode($address, $areaCode=Null) { 
    69    
    70   // Get area code (eg. postcode). 
    71   $areaLoc = gmap_geocode($areaCode); 
    72   $areaLoc = _validateUKLocation($areaLoc); 
    73    
    74   // Normalise address into lines. 
    75   $nAddress = _normaliseAddress($address); 
    76   $addressLines = split("[\n]+", $nAddress); 
    77   //d("addressLines", $addressLines, True); 
    78  
    79   $addressLoc = Null; 
    80  
    81   /* Sometimes the postcode throws google api off, so if we have an explicit postcode, remove it from address. */ 
    82   //if (preg_match('/^[0-9]*$/i', $addressLines[count($addressLines)-1])) { 
    83   //  $addressLines[count($addressLines)-1] = ""; 
    84   //} 
    85  
    86   /* Google doesn't like the things at the top of address (e.g. company name, building), so prune it down. */ 
    87   while ($addressLines) { 
    88     $partAddress = implode(", ", $addressLines); 
    89  
    90     $partLoc = gmap_geocode($partAddress); 
    91     $partLoc = _validateUKLocation($partLoc); 
    92  
    93     if ($partLoc["status"] != 200) { 
    94       //pass 
    95     } else { 
    96       if ($areaLoc) { 
    97         $dist = _location_dist($areaLoc, $partLoc); 
    98       } else { 
    99         $dist = PROXIMITY_THRESHOLD * 0.5; 
    100       } 
    101  
    102       if ($dist < PROXIMITY_THRESHOLD) { 
    103         if ($addressLoc) { 
    104           if ($partLoc["accuracy"] > $addressLoc["accuracy"]) { 
    105             $addressLoc = $partLoc; 
    106           } 
    107         } else { 
    108           $addressLoc = $partLoc; 
    109         } 
    110       } 
    111     }  
    112  
    113     //d("partAddress $partAddress", $partLoc, True); 
    114     array_shift($addressLines); 
    115   } 
    116    
    117   // Choose the most accurate location. 
    118   if ($addressLoc and !$areaLoc) { 
    119     return $addressLoc; 
    120   } else if ($areaLoc and !$addressLoc) { 
    121     return $areaLoc; 
    122   } else if ($addressLoc and $areaLoc) { 
    123     if ($addressLoc["accuracy"] > $areaLoc["accuracy"]) { 
    124       return $addressLoc; 
    125     } else { 
    126       return $areaLoc; 
    127     } 
    128   } 
    129  
    130   return Null; 
    131 
    132  
    133 function _set_node_geocode(&$node) { 
    134   //Updates the location of a node based on specific fields (e.g. address, postcode). 
    135    
    136   $addressField = variable_get("simplemap.addressfield", "field_address"); 
    137   eval("\$address = \$node->".$addressField."[0]['value'];"); 
    138  
    139   // Try to find a postcode cck field. 
    140   $postcode = $node->field_postcode[0]["value"]; 
    141  
    142   if (!$address and !$postcode) { 
    143     variable_del("simplemap.nloc-$node->nid"); 
    144     return; 
    145   } 
    146    
    147   // Do geocode lookup. 
    148   $location = _smart_geocode($address, $postcode); 
    149   //d("lookup of $address/$postcode", $location); 
    150  
    151   if ($location["status"] != "200") { 
    152     variable_del("simplemap.nloc-$node->nid"); 
    153     return; 
    154   } 
    155     
    156   variable_set("simplemap.nloc-$node->nid", $location); 
    157   drupal_set_message("The geographic location of this $node->type has been found and set."); 
    158 
    159  
    160 function _get_node_location($nid) { 
    161   // Gets the geographic location of a node with nid. 
    162   return variable_get("simplemap.nloc-$nid", Null); 
    163 
    164  
     66 
     67/* Capture node saves to process geocodes. */ 
    16568function simple_maps_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) { 
    16669 
     
    17376} 
    17477 
    175 function _get_markers($view) { 
    176   
    177   global $base_url; 
    178  
    179   $markers = array(); 
    180   $viewResults = views_build_view('items', $view, Null, false, $view->nodes_per_block); 
    181   foreach ($viewResults['items'] as $i => $item) { 
    182     //d("item",$item); 
    183     $location = _get_node_location($item->nid); 
    184     //$location = _smart_geocode("Warwickshire Association of Youth Clubs Jubilee House, Westlea Road Leamington Spa, CV31 3JE"); 
    185     //$location = _smart_geocode("Westlea Road Leamington Spa CV31 3JE"); 
    186     //$location = _smart_geocode("CV31 3JE"); 
    187     if (!$location) { 
    188       d("$item->node_title as no location"); 
    189       continue; 
    190     } 
    191     
    192     $url = "$base_url/node/$item->nid"; 
    193  
    194     $markers[] = array( 
    195       'latitude' => $location["latitude"], 
    196       'longitude' => $location["longitude"], 
    197       'markername' => "", 
    198       'text' => "<a href='$url'>".$item->node_title."</a>", 
    199       'offset' => 0, 
    200     ); 
    201   } 
    202  
    203   //d("markers",$markers); 
    204   return $markers; 
    205 
    206  
    207 function _render_map($markers=Null) { 
    208   $output = ""; 
    209   $map = gmap_defaults(); 
    210    
    211   if ($markers) { 
    212     $map["markers"] = $markers; 
    213   } 
    214   //d("",$map); 
    215   $output .= theme('gmap', array('#settings' => $map)); 
    216  
    217   return $output; 
    218 
    219  
    220  
     78 
     79/* Initialise the module. */ 
     80function simple_maps_init() { 
     81  //cache_clear_all('*', 'cache_menu', TRUE); 
     82  //_tests(); 
     83  //_update_locations(); 
     84
     85 
     86 
     87/* 
     88 * =============================== 
     89 * PAGES 
     90 * =============================== 
     91 */ 
     92 
     93 
     94/* Display a map of location-aware nodes in the specified view. */ 
    22195function view_map($viewName=Null) { 
    22296  $output = ""; 
     
    234108} 
    235109 
    236 function _location_dist($loc1, $loc2) { 
    237   return sqrt(pow($loc2["latitude"] - $loc1["latitude"], 2) + pow($loc2["longitude"] - $loc1["longitude"], 2)); 
    238 
    239  
     110 
     111/* Test page. */ 
    240112function test_page() { 
    241113  $testAddress = "CVSC<br/> 6th floor<br> market way, coventry</p> cv1 1ea"; 
     
    272144} 
    273145 
     146 
     147/* 
     148 * =============================== 
     149 * MAIN FUNCTIONS 
     150 * =============================== 
     151 */ 
     152 
     153/* Looks at permutations of the address and postcode to try to improve accuracy of google api. */ 
     154function _smart_geocode($address, $areaCode=Null) { 
     155   
     156  // Get area code (eg. postcode). 
     157  $areaLoc = gmap_geocode($areaCode); 
     158  $areaLoc = _validateUKLocation($areaLoc); 
     159  $addressLoc = Null; 
     160   
     161  // Normalise address into lines. 
     162  $nAddress = _normaliseAddress($address); 
     163  $addressLines = split("[\n]+", $nAddress); 
     164  //d("addressLines", $addressLines, True); 
     165 
     166 
     167  /* Sometimes the postcode throws google api off, so if we have an explicit postcode, remove it from address. */ 
     168  //if (preg_match('/^[0-9]*$/i', $addressLines[count($addressLines)-1])) { 
     169  //  $addressLines[count($addressLines)-1] = ""; 
     170  //} 
     171 
     172  /* Google doesn't like the things at the top of address (e.g. company name, building), so prune it down, line-by-line. */ 
     173  while ($addressLines) { 
     174     
     175    /* Create comma seperated part-address */ 
     176    $partAddress = implode(", ", $addressLines); 
     177 
     178    /* Do a lookup */ 
     179    $partLoc = gmap_geocode($partAddress); 
     180    $partLoc = _validateUKLocation($partLoc); 
     181 
     182    if ($partLoc["status"] != 200) { 
     183      // pass: google could not find it. 
     184    } else { 
     185      /*  If we have a postcode, use it as a reference point for finding more accurate location nearby. */ 
     186      if ($areaLoc) { 
     187        $dist = _location_dist($areaLoc, $partLoc); 
     188      } else { 
     189        $dist = PROXIMITY_THRESHOLD * 0.5; 
     190      } 
     191 
     192      if ($dist < PROXIMITY_THRESHOLD) { 
     193        if ($addressLoc) { 
     194          if ($partLoc["accuracy"] > $addressLoc["accuracy"]) { 
     195            $addressLoc = $partLoc; 
     196          } 
     197        } else { 
     198          $addressLoc = $partLoc; 
     199        } 
     200      } 
     201    }  
     202 
     203    //d("partAddress $partAddress", $partLoc, True); 
     204    /* Prune a line from the top of the address. */ 
     205    array_shift($addressLines); 
     206  } 
     207   
     208  // Return the most accurate location. 
     209  if ($addressLoc and !$areaLoc) { 
     210    return $addressLoc; 
     211  } else if ($areaLoc and !$addressLoc) { 
     212    return $areaLoc; 
     213  } else if ($addressLoc and $areaLoc) { 
     214    if ($addressLoc["accuracy"] > $areaLoc["accuracy"]) { 
     215      return $addressLoc; 
     216    } else { 
     217      return $areaLoc; 
     218    } 
     219  } 
     220 
     221  return Null; 
     222} 
     223 
     224/* This automatically tries to find a location for a node, based on a CCK field called 'address' or on a 'postcode' field. */ 
     225function _set_node_geocode(&$node) { 
     226   
     227  $addressField = variable_get("simplemap.addressfield", "field_address"); 
     228  eval("\$address = \$node->".$addressField."[0]['value'];"); 
     229 
     230  // Try to find a postcode cck field. 
     231  $postcode = $node->field_postcode[0]["value"]; 
     232 
     233  /* If no address info could be find, remove the location var for this node - if there was one. */ 
     234  if (!$address and !$postcode) { 
     235    variable_del("simplemap.nloc-$node->nid"); 
     236    return; 
     237  } 
     238   
     239  // Do geocode lookup. 
     240  $location = _smart_geocode($address, $postcode); 
     241  //d("lookup of $address/$postcode", $location); 
     242 
     243  /* If the lookcup failed, unset the location info for this node. */ 
     244  if ($location["status"] != "200") { 
     245    variable_del("simplemap.nloc-$node->nid"); 
     246    return; 
     247  } 
     248    
     249  /* Store the location info for this node. */ 
     250  variable_set("simplemap.nloc-$node->nid", $location); 
     251  drupal_set_message("The geographic location of '$node->title' has been found and set."); 
     252} 
     253 
     254 
     255/* Gets the location of this node, if there is one. */ 
     256function _get_node_location($nid) { 
     257  // Gets the geographic location of a node with nid. 
     258  return variable_get("simplemap.nloc-$nid", Null); 
     259} 
     260 
     261 
     262 
     263/* 
     264 * =============================== 
     265 * UTILITY FUNCTIONS 
     266 * =============================== 
     267 */ 
     268 
     269 
     270/* Normalises an geographical address so that it has newlines for easier parsing. */ 
     271function _normaliseAddress($address) { 
     272  $nAddress = strtolower($address); 
     273   
     274  if (stristr($nAddress, "<br/>")) { $nAddress = str_replace("<br/>", "\n", $nAddress); } 
     275  if (stristr($nAddress, "<br>")) { $nAddress = str_replace("<br>", "\n", $nAddress); } 
     276  if (stristr($nAddress, "</p>")) { $nAddress = str_replace("</p>", "\n", $nAddress); } 
     277  if (stristr($nAddress, ",")) { $nAddress = str_replace(",", "\n", $nAddress); } 
     278 
     279  //d("nAddress: $nAddress",Null, True); 
     280  return $nAddress; 
     281} 
     282 
     283 
     284/* Sanity check that a location lies in or around the UK. */ 
     285function _validateUKLocation($loc) { 
     286   
     287  if ($loc["status"] != 200) { 
     288    return Null; 
     289  } 
     290 
     291  if ($loc["latitude"] > 62.0 or $loc["latitude"] < 50.0) { 
     292    return Null; 
     293  } 
     294 
     295  if ($loc["longitude"] < -12.0 or $loc["longitude"] > 4.0) { 
     296    return Null; 
     297  } 
     298 
     299  return $loc; 
     300} 
     301 
     302 
     303/* Given a view, returns a list of gmap markers of node locations in the view. */ 
     304function _get_markers($view) { 
     305  
     306  global $base_url; 
     307 
     308  $markers = array(); 
     309  $viewResults = views_build_view('items', $view, Null, false, $view->nodes_per_block); 
     310  foreach ($viewResults['items'] as $i => $item) { 
     311    //d("item",$item); 
     312    $location = _get_node_location($item->nid); 
     313    //$location = _smart_geocode("Warwickshire Association of Youth Clubs Jubilee House, Westlea Road Leamington Spa, CV31 3JE"); 
     314    //$location = _smart_geocode("Westlea Road Leamington Spa CV31 3JE"); 
     315    //$location = _smart_geocode("CV31 3JE"); 
     316    if (!$location) { 
     317      d("$item->node_title as no location"); 
     318      continue; 
     319    } 
     320    
     321    $url = "$base_url/node/$item->nid"; 
     322 
     323    $markers[] = array( 
     324      'latitude' => $location["latitude"], 
     325      'longitude' => $location["longitude"], 
     326      'markername' => "", 
     327      'text' => "<a href='$url'>".$item->node_title."</a>", 
     328      'offset' => 0, 
     329    ); 
     330  } 
     331 
     332  //d("markers",$markers); 
     333  return $markers; 
     334} 
     335 
     336/* Renders a gmap to diaply the specified markers. */ 
     337function _render_map($markers=Null) { 
     338  $output = ""; 
     339  $map = gmap_defaults(); 
     340   
     341  if ($markers) { 
     342    $map["markers"] = $markers; 
     343  } 
     344  //d("",$map); 
     345  $output .= theme('gmap', array('#settings' => $map)); 
     346 
     347  return $output; 
     348} 
     349 
     350/* Calculates the distance between two locations. */ 
     351function _location_dist($loc1, $loc2) { 
     352  return sqrt(pow($loc2["latitude"] - $loc1["latitude"], 2) + pow($loc2["longitude"] - $loc1["longitude"], 2)); 
     353} 
     354 
     355/* Update the locations of all nodes in the site - for debug really. */ 
    274356function _update_locations() { 
    275357  $results = db_query('SELECT nid FROM {node}'); 
     
    281363} 
    282364 
    283 function simple_maps_init() { 
    284   cache_clear_all('*', 'cache_menu', TRUE); 
    285   //_tests(); 
    286   //_update_locations(); 
    287 } 
    288365 
    289366?>