location.inc 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679
  1. <?php
  2. /**
  3. * @defgroup Location Location: An API for working with locations.
  4. *
  5. * Public API for the Location module.
  6. */
  7. /**
  8. * @file
  9. * An implementation of a universal API for location manipulation. Provides functions for
  10. * postal_code proximity searching, deep-linking into online mapping services. Currently,
  11. * some options are configured through an interface provided by location.module.
  12. */
  13. include_once(DRUPAL_ROOT . '/' . drupal_get_path('module', 'location') . '/earth.inc');
  14. /**
  15. * Get a deep-link to a mapping service such as Yahoo! Maps or MapPoint given an location. The
  16. * call is delegated based on the 'country' value in the $location parameter.
  17. *
  18. * @param $location
  19. * An associative array where
  20. * 'street' => A string representing the street location
  21. * 'additional' => A string for any additional portion of the street location
  22. * 'city' => A string for the city name
  23. * 'province' => The standard postal abbreviation for the province
  24. * 'country' => The two-letter ISO code for the country of the location (REQUIRED)
  25. * 'postal_code' => The international postal code for the location
  26. *
  27. * @return
  28. * A link to a map provided by a third-party. The idea is to encode the appropriate
  29. * parameters as HTTP GET variables to the URL.
  30. *
  31. * @ingroup Location
  32. */
  33. function location_map_link($location = array(), $link_text = 'See map: ') {
  34. if (!isset($location['country']) || $location['country'] == 'xx') {
  35. return '';
  36. }
  37. location_load_country($location['country']);
  38. $default_func = 'location_map_link_'. $location['country'] .'_default_providers';
  39. $providers_func = 'location_map_link_'. $location['country'] .'_providers';
  40. $providers = function_exists($providers_func) ? $providers_func() : array();
  41. $selected_providers = variable_get('location_map_link_'. $location['country'], function_exists($default_func) ? $default_func() : array());
  42. $links = array();
  43. foreach ($selected_providers as $mapper) {
  44. $link_func = 'location_map_link_'. $location['country'] .'_'. $mapper;
  45. if (function_exists($link_func)) {
  46. if ($link = $link_func($location)) {
  47. $links[] = '<a href="'. $link .'"'. (variable_get('location_maplink_external', 0) ? ' '. variable_get('location_maplink_external_method', 'target="_blank"') : '') .'>'. $providers[$mapper]['name'] .'</a>';
  48. }
  49. }
  50. }
  51. if (count($links)) {
  52. return '<div class="location map-link">'. t($link_text) . implode($links, ", ") .'</div>';
  53. }
  54. else {
  55. return NULL;
  56. }
  57. }
  58. /**
  59. * Try to extract the the Latitude and Longitude data from the
  60. * postal code.
  61. *
  62. * @param $location
  63. * Array. the location data
  64. * -> the values are:
  65. * 'street' => the string representing the street location (REQUIRED)
  66. * 'additional' => the string representing the additional street location portion in the location form
  67. * 'city' => the city name (REQUIRED)
  68. * 'province' => the province code defined in the country-specific include file
  69. * 'country' => the lower-case of the two-letter ISO code (REQUIRED)
  70. * 'postal_code' => the postal-code (REQUIRED)
  71. *
  72. * @return
  73. * Array or NULL. NULL if the delegated-to function that does the
  74. * actual look-up does not exist. If the appropriate function exists,
  75. * then this function returns an associative array where
  76. * 'lon' => A floating point number for the longitude coordinate of the parameter location
  77. * 'lat' => A floating point number for the latitude coordinate of the parameter location
  78. *
  79. * @ingroup Location
  80. */
  81. function location_get_postalcode_data($location = array()) {
  82. $location['country'] = isset($location['country']) ? trim($location['country']) : NULL;
  83. $location['postal_code'] = isset($location['postal_code']) ? trim($location['postal_code']) : NULL;
  84. if (is_null($location['postal_code']) || is_null($location['country']) || empty($location['country']) || empty($location['postal_code']) || $location['postal_code'] == 'xx') {
  85. return NULL;
  86. }
  87. location_load_country($location['country']);
  88. $country_specific_function = 'location_get_postalcode_data_'. $location['country'];
  89. if (function_exists($country_specific_function)) {
  90. return $country_specific_function($location);
  91. }
  92. else {
  93. return NULL;
  94. }
  95. }
  96. /**
  97. * Given two points in lat/lon form, returns the distance between them.
  98. *
  99. * @param $latlon_a
  100. * An associative array where
  101. * 'lon' => is a floating point of the longitude coordinate for the point given by latlonA
  102. * 'lat' => is a floating point of the latitude coordinate for the point given by latlonB
  103. *
  104. * @param $latlon_b
  105. * Another point formatted like $latlon_b
  106. *
  107. * @param $distance_unit
  108. * A string that is either 'km' or 'mile'.
  109. * If neither 'km' or 'mile' is passed, the parameter is forced to 'km'
  110. *
  111. * @return
  112. * NULL if sense can't be made of the parameters.
  113. * An associative array where
  114. * 'scalar' => Is the distance between the two lat/lon parameter points
  115. * 'distance_unit' => Is the unit of distance being represented by 'scalar'.
  116. * This will be 'km' unless 'mile' is passed for the $distance_unit param
  117. *
  118. * @ingroup Location
  119. */
  120. function location_distance_between($latlon_a = array(), $latlon_b = array(), $distance_unit = 'km') {
  121. if (!isset($latlon_a['lon']) || !isset($latlon_a['lat']) || !isset($latlon_b['lon']) || !isset($latlon_b['lat'])) {
  122. return NULL;
  123. }
  124. if ($distance_unit != 'km' && $distance_unit != 'mile') {
  125. return NULL;
  126. }
  127. // $conversion_factor = number to divide by to convert meters to $distance_unit
  128. // At this point, $distance_unit == 'km' or 'mile' and nothing else
  129. //$conversion_factor = ($distance_unit == 'km') ? 1000.0 : 1609.347;
  130. $meters = earth_distance($latlon_a['lon'], $latlon_a['lat'], $latlon_b['lon'], $latlon_b['lat']);
  131. return array('scalar' => round($meters/(($distance_unit == 'km') ? 1000.0 : 1609.347), 1), 'distance_unit' => $distance_unit);
  132. }
  133. /**
  134. * Takes two locations and tries to return a deep-link to driving directions.
  135. *
  136. * Parameters:
  137. * @param $location_a
  138. * An associative array that represents an location where
  139. * 'street' => the street portions of the location
  140. * 'additional' => additional street portion of the location
  141. * 'city' => the city name
  142. * 'province' => the province, state, or territory
  143. * 'country' => lower-cased two-letter ISO code (REQUIRED)
  144. * 'postal_code' => the postal code
  145. *
  146. * @param $location_b
  147. * An associative array that represents an location in the same way that
  148. * parameter $location_a does.
  149. *
  150. * @param $link_text
  151. * The text of the HTML link that is to be generated.
  152. *
  153. * @return
  154. * A deep-link to driving directions on Yahoo! or some other mapping service, if enough fields are filled in the parameters.
  155. * A deep-link to a form for driving directions with information pre-filled if not enough, but some fields are filled in the parameters.
  156. * The empty string if no information is provided (or if so little information is provided that there is no function to which to delegate
  157. * the call.
  158. *
  159. * We dispatch the call to a country-specific function. The country-specific function, in this case,
  160. * will be the one reflected by the country parameter of the first function. We require that
  161. * both locationes supplied have a country field at the minimum.
  162. *
  163. * The country-specific functions will ultimately decide, with the parameters given, whether to
  164. * to link to a form for driving directions is provided, where this form will be
  165. * pre-populated with whatever values were available or whether to link directly to the driving
  166. * directions themselves if enough fields are filled for each location.
  167. *
  168. * @ingroup Location
  169. */
  170. function location_driving_directions_link($location_a = array(), $location_b = array(), $link_text = 'Get directions') {
  171. if (!isset($location_a['country']) or !isset($location_b['country'])) {
  172. return '';
  173. }
  174. // For now, return empty string if starting-point and destinations are in different countries
  175. //if ($location_a['country'] != $location_b['country']) {
  176. // return '';
  177. //}
  178. // Lines above commented out because I want to let the country-specific function of the departure point decide
  179. // what it will do with driving destination locationes from other countries. As an example, Yahoo! Maps supports driving
  180. // direction queries for locations between the U.S. and Canada.
  181. $driving_direction_function = 'location_driving_directions_link_'. $location_a['country'];
  182. if (function_exists($driving_direction_function)) {
  183. $http_link = $driving_direction_function($location_a, $location_b);
  184. if (strlen($http_link)) {
  185. return '<a href="'. $http_link .'">'. $link_text .'</a>';
  186. }
  187. else {
  188. return '';
  189. }
  190. }
  191. return '';
  192. }
  193. /**
  194. * @param $distance
  195. * A number in either miles or km.
  196. *
  197. * @param $distance_unit
  198. * String (optional). Either 'mile' or 'km' (default)
  199. *
  200. * @return
  201. * A floating point number where the number in meters after the initially passed scalar has been ceil()'d
  202. * This is done after the $distance_unit parmeter is forced to be 'km' or 'mile'
  203. */
  204. function _location_convert_distance_to_meters($distance, $distance_unit = 'km') {
  205. if (!is_numeric($distance)) {
  206. return NULL;
  207. }
  208. if ($distance == 0) {
  209. return NULL;
  210. }
  211. if ($distance_unit != 'km' && $distance_unit != 'mile') {
  212. $distance_unit = 'km';
  213. }
  214. // Convert distance to meters
  215. $retval = round(floatval($distance) * (($distance_unit == 'km') ? 1000.0 : 1609.347), 2);
  216. return $retval;
  217. }
  218. /**
  219. * Takes an location and returns a "rough" latitude/longitude pair based on the postal code
  220. * data available for the given country.
  221. *
  222. * @param $location
  223. * An associative array $location where
  224. * 'street' => the street portion of the location
  225. * 'additional' => additional street portion of the location
  226. * 'province' => the province, state, or territory
  227. * 'country' => lower-cased two-letter ISO code (REQUIRED)
  228. * 'postal_code' => international postal code (REQUIRED)
  229. *
  230. * @return
  231. * NULL if data cannont be found.
  232. * Otherwise, an associative array where
  233. * 'lat' => is a floating point of the latitude coordinate of this location
  234. * 'lon' => is a floating point of the longitude coordinate of this location
  235. *
  236. * @ingroup Location
  237. */
  238. function location_latlon_rough($location = array()) {
  239. if (!isset($location['country']) || !isset($location['postal_code'])) {
  240. return NULL;
  241. }
  242. location_load_country($location['country']);
  243. $latlon_function = 'location_latlon_rough_'. $location['country'];
  244. if (function_exists($latlon_function) && $result = $latlon_function($location)) {
  245. return $result;
  246. }
  247. else {
  248. return location_latlon_rough_default($location);
  249. }
  250. }
  251. /**
  252. * Returns a lat/lon pair of the approximate center of the given postal code in the given country
  253. * This function is a default implementation, in case the country support doesn't implement a proper function for this.
  254. *
  255. * @param $location
  256. * An associative array $location where
  257. * 'street' => the street portion of the location
  258. * 'supplemental' => additional street portion of the location
  259. * 'province' => the province, state, or territory
  260. * 'country' => lower-cased two-letter ISO code (REQUIRED)
  261. * 'postal_code' => the international postal code for this location (REQUIRED)
  262. *
  263. * @return
  264. * An associative array where
  265. * 'lat' => approximate latitude of the center of the postal code's area
  266. * 'lon' => approximate longitude of the center of the postal code's area
  267. *
  268. */
  269. function location_latlon_rough_default($location = array()) {
  270. if (!isset($location['country']) || !isset($location['postal_code'])) {
  271. return NULL;
  272. }
  273. $query = db_select('zipcodes', 'z');
  274. $query->addField('z', 'latitude', 'lat');
  275. $query->addField('z', 'longitude', 'lon');
  276. $coords = $query->condition('country', $location['country'])
  277. ->condition('zip', $location['postal_code'])
  278. ->execute()
  279. ->fetchAssoc();
  280. if ($coords) {
  281. return $coords;
  282. }
  283. // if nothing found internally (new zipcode, or incomplete zipcodes table)
  284. elseif (($newlatlong = location_latlon_exact($location)) != NULL) {
  285. // try a one-time external geocoding
  286. if ($newlatlong['lat']) {
  287. // and store results in zipcodes table for next lookups being internally handled
  288. // (yeah this is missing city/state info a.t.m., but is way better than nothing!)
  289. db_insert('zipcodes')
  290. ->fields(array(
  291. 'latitude' => $newlatlong['lat'],
  292. 'longitude' => $newlatlong['lon'],
  293. 'country' => $location['country'],
  294. 'zip' => $location['postal_code'],
  295. ))
  296. ->execute();
  297. }
  298. return $newlatlong;
  299. }
  300. else {
  301. return NULL;
  302. }
  303. }
  304. /**
  305. * Get an appropriate bounding box for showing an entire country on a map.
  306. * Target is a bounding box large enough to show the country in both spherical
  307. * mercator and mercator projections.
  308. * @param $location
  309. * Either a location array or a country code.
  310. *
  311. * @return
  312. * An array with 'minlng', 'minlat', 'maxlng', and 'maxlat' elements.
  313. */
  314. function location_country_bounds($location = array()) {
  315. if (!is_array($location)) {
  316. $location = array('country' => $location);
  317. }
  318. if (!empty($location['country'])) {
  319. location_load_country($location['country']);
  320. }
  321. $country_bounds_function = 'location_bounds_' . $location['country'];
  322. if (function_exists($country_bounds_function)) {
  323. return $country_bounds_function();
  324. }
  325. else {
  326. return array(
  327. 'minlng' => -180.0,
  328. 'minlat' => -90.0,
  329. 'maxlng' => 180.0,
  330. 'maxlat' => 90.0,
  331. );
  332. }
  333. }
  334. /**
  335. * Currently, this is not a priority until there is an implementable use for exact longitude,
  336. * latitude coordinates for an location. The idea is that this call will eventually retrieve
  337. * information through a web-service. Whereas location_latlon_rough() returns an approximate
  338. * lat/lon pair based strictly on the postal code where this lat/lon pair is pulled from a
  339. * database table, this function is intended to send the entire location to a web-service and
  340. * to retrieve exact lat/lon coordinates.
  341. *
  342. * @param $location
  343. * An array where
  344. * -> the key values are 'street', 'additional', 'province', 'country', 'postal_code'
  345. * -> the values are:
  346. * 'street' => the string representing the street location (REQUIRED)
  347. * 'additional' => the string representing the additional street location portion in the location form
  348. * 'city' => the city name (REQUIRED)
  349. * 'province' => the province code defined in the country-specific include file
  350. * 'country' => the lower-case of the two-letter ISO code (REQUIRED)
  351. * 'postal_code' => the postal-code (REQUIRED)
  352. *
  353. * @return
  354. * NULL if the delegated-to function that does the actual look-up does not exist.
  355. * If the appropriate function exists, then this function returns an associative array where
  356. * 'lon' => A floating point number for the longitude coordinate of the parameter location
  357. * 'lat' => A floating point number for the latitude coordinate of the parameter location
  358. *
  359. * @ingroup Location
  360. */
  361. function location_latlon_exact($location = array()) {
  362. $country = $location['country'];
  363. location_standardize_country_code($country);
  364. $service = variable_get('location_geocode_'. $country, 'none');
  365. if (!empty($country) && $service != 'none') {
  366. // figure out what the exact function should be
  367. if (strpos($service, '|')) {
  368. location_load_country($country);
  369. // The code change below fixes the problem of the country specific
  370. // function for geocoding not being correctly called (it removes any
  371. // text from the pipe (|) onwards)
  372. $exact_latlon_function = 'location_geocode_'. $country .'_'. substr($service, 0, strpos($service, '|'));
  373. }
  374. else {
  375. location_load_geocoder($service);
  376. $exact_latlon_function = $service .'_geocode_location';
  377. }
  378. if (function_exists($exact_latlon_function)) {
  379. return $exact_latlon_function($location);
  380. }
  381. else {
  382. return NULL;
  383. }
  384. }
  385. return NULL;
  386. }
  387. /**
  388. * Returns an associative array of countries currently supported
  389. * by the location system where
  390. * -> the keys represent the two-letter ISO code and
  391. * -> the values represent the English name of the country.
  392. * The array is sorted the index (i.e., by the short English name of the country).
  393. *
  394. * Please note the different between "supported" countries and "configured"
  395. * countries: A country being "supported" means that there is an include file
  396. * to support the country while "configure" implies that the site admin has
  397. * configured the site to actually use that country.
  398. *
  399. * @ingroup Location
  400. */
  401. function _location_supported_countries() {
  402. $supported_countries = &drupal_static(__FUNCTION__, array());
  403. // If this function has already been called this request, we can avoid a DB hit.
  404. if (!empty($supported_countries)) {
  405. return $supported_countries;
  406. }
  407. // Try first to load from cache, it's much faster than the scan below.
  408. if ($cache = cache_get('location:supported-countries', 'cache_location')) {
  409. $supported_countries = $cache->data;
  410. }
  411. else {
  412. // '<ISO two-letter code>' => '<English name for country>'
  413. $iso_list = location_get_iso3166_list();
  414. $path = drupal_get_path('module', 'location') .'/supported/location.';
  415. foreach ($iso_list as $cc => $name) {
  416. if (file_exists($path . $cc .'.inc')) {
  417. $supported_countries[$cc] = $name;
  418. }
  419. }
  420. cache_set('location:supported-countries', $supported_countries, 'cache_location');
  421. }
  422. return $supported_countries;
  423. }
  424. // @@@ New in 3.x, document.
  425. /**
  426. * Fetch the provinces for a country.
  427. */
  428. function location_get_provinces($country = 'us') {
  429. $provinces = &drupal_static(__FUNCTION__, array());
  430. location_standardize_country_code($country);
  431. if (isset($provinces[$country])) {
  432. return $provinces[$country];
  433. }
  434. if ($cache = cache_get("provinces:$country", 'cache_location')) {
  435. $provinces[$country] = $cache->data;
  436. return $provinces[$country];
  437. }
  438. location_load_country($country);
  439. $func = 'location_province_list_'. $country;
  440. if (function_exists($func)) {
  441. $provinces[$country] = $func();
  442. cache_set("provinces:$country", $provinces[$country], 'cache_location');
  443. return $provinces[$country];
  444. }
  445. return array();
  446. }
  447. // @@@ New in 3.x, document.
  448. /**
  449. * Get the translated name of a country code.
  450. */
  451. function location_country_name($country = 'us') {
  452. location_standardize_country_code($country);
  453. $countries = location_get_iso3166_list();
  454. if (isset($countries[$country])) {
  455. return $countries[$country];
  456. }
  457. else {
  458. return '';
  459. }
  460. }
  461. // @@@ New in 3.x, document.
  462. /**
  463. * Get the full name of a province code.
  464. */
  465. function location_province_name($country = 'us', $province = 'xx') {
  466. $provinces = location_get_provinces($country);
  467. $province = strtoupper($province);
  468. if (isset($provinces[$province])) {
  469. return $provinces[$province];
  470. }
  471. else {
  472. return '';
  473. }
  474. }
  475. // @@@ New in 3.x, document.
  476. /**
  477. * Get a province code from a code or full name and a country.
  478. */
  479. function location_province_code($country = 'us', $province = 'xx') {
  480. // An array of countries is useful if someone specified multiple countries
  481. // in an autoselect for example.
  482. // It *is* possibly ambiguous, especially if the province was already a code.
  483. // We make an array here for single (the usual case) for code simplicity reasons.
  484. if (!is_array($country)) {
  485. $country = array($country);
  486. }
  487. $p = strtoupper($province);
  488. foreach ($country as $c) {
  489. $provinces = location_get_provinces($c);
  490. foreach ($provinces as $k => $v) {
  491. if ($p == strtoupper($k) || $p == strtoupper($v)) {
  492. return $k;
  493. }
  494. }
  495. }
  496. return '';
  497. }
  498. // @@@ New in 3.x, document.
  499. /**
  500. * Canonicalize a country code.
  501. */
  502. function location_standardize_country_code(&$country) {
  503. $country = trim($country);
  504. // @@@ Double check the validity of this validity check. ;)
  505. if (!ctype_alpha($country) || strlen($country) != 2) {
  506. $country = 'xx';
  507. return FALSE;
  508. }
  509. else {
  510. $country = strtolower($country);
  511. return TRUE;
  512. }
  513. }
  514. /**
  515. * Load support for a country.
  516. *
  517. * This function will load support for a country identified by its two-letter ISO code.
  518. *
  519. * @param $country
  520. * Two-letter ISO code for country.
  521. *
  522. * @return
  523. * TRUE if the file was found and loaded, FALSE otherwise.
  524. */
  525. function location_load_country($country) {
  526. location_standardize_country_code($country);
  527. $file = DRUPAL_ROOT . '/' . drupal_get_path('module', 'location') . '/supported/location.' . $country . '.inc';
  528. if (file_exists($file)) {
  529. include_once($file);
  530. return TRUE;
  531. }
  532. return FALSE;
  533. }
  534. // @@@ New in 3.x, document.
  535. /**
  536. * Load a general geocoding service.
  537. */
  538. function location_load_geocoder($geocoder) {
  539. include_once(DRUPAL_ROOT . '/' . drupal_get_path('module', 'location') . '/geocoding/' . $geocoder . '.inc');
  540. }
  541. /**
  542. * Create a single line address.
  543. *
  544. * @param $location
  545. * Array. The address parts
  546. * @return
  547. * String. The single line address
  548. */
  549. function location_address2singleline($location = array()) {
  550. // Check if its a valid address
  551. if (empty($location)) {
  552. return '';
  553. }
  554. $address = '';
  555. if (!empty($location['street'])) {
  556. $address .= $location['street'];
  557. }
  558. if (!empty($location['city'])) {
  559. if (!empty($location['street'])) {
  560. $address .= ', ';
  561. }
  562. $address .= $location['city'];
  563. }
  564. if (!empty($location['province'])) {
  565. if (!empty($location['street']) || !empty($location['city'])) {
  566. $address .= ', ';
  567. }
  568. // @@@ Fix this!
  569. if (substr($location['province'], 0, 3) == $location['country'] .'-') {
  570. $address .= substr($location['province'], 3);
  571. watchdog('Location', 'BUG: Country found in province attribute.');
  572. }
  573. else {
  574. $address .= $location['province'];
  575. }
  576. }
  577. if (!empty($location['postal_code'])) {
  578. if (!empty($address)) {
  579. $address .= ' ';
  580. }
  581. $address .= $location['postal_code'];
  582. }
  583. if (!empty($location['country'])) {
  584. $address .= ', '. $location['country'];
  585. }
  586. return $address;
  587. }
  588. function location_get_general_geocoder_list() {
  589. $list = &drupal_static(__FUNCTION__, array());
  590. if (!count($list)) {
  591. $files = file_scan_directory(drupal_get_path('module', 'location') . '/geocoding', '/\.inc$/', array('nomask' => '/(\.\.?|CVS|\.svn)$/'));
  592. foreach ($files as $full_path_name => $fileinfo) {
  593. $list[] = $fileinfo->name;
  594. }
  595. }
  596. return $list;
  597. }
  598. /**
  599. * The following is an array of all
  600. * countrycode => country-name pairs as layed out in
  601. * ISO 3166-1 alpha-2
  602. */
  603. function location_get_iso3166_list($upper = FALSE) {
  604. include_once DRUPAL_ROOT . '/includes/locale.inc';
  605. // Statically cache a version of the core Drupal list of countries
  606. // with lower case country codes for use by this module.
  607. $countries = &drupal_static(__FUNCTION__);
  608. if ($upper) {
  609. // Drupal core stores ISO 3166-1 alpha2 codes in upper case, as
  610. // per the ISO standard.
  611. return country_get_list();
  612. }
  613. elseif (!isset($countries)) {
  614. // Location module uses lower-case ISO 3166-1 alpha2 codes, so we need
  615. // to convert.
  616. $countries = array_change_key_case(country_get_list(), CASE_LOWER);
  617. }
  618. return $countries;
  619. }