| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349 | <?php/** * Implements hook_field_widget_info(). */function geocoder_field_widget_info() {  return array(    'geocoder' => array(      'label' => t('Geocode from another field'),      'field types' => array('geofield', 'geolocation_latlng', 'location'),      'behaviors' => array(        'multiple values' => FIELD_BEHAVIOR_CUSTOM,        'default value' => FIELD_BEHAVIOR_NONE,      ),    ),  );}/** * Implements field_widget_settings_form(). */function geocoder_field_widget_settings_form($this_field, $instance) {  $settings = $instance['widget']['settings'];    $entity_fields = field_info_instances($instance['entity_type'], $instance['bundle']);  $all_fields = field_info_fields();  $supported_field_types = geocoder_supported_field_types();  $processors = geocoder_handler_info();  $handlers_by_type = array();  $field_types = array();  $valid_fields = array();  $available_handlers = array();    // Add in the title/name  //@@TODO Do this programatically by getting entity_info  switch ($instance['entity_type']) {    case 'node':      $all_fields['title'] = array(        'field_name' => 'title',        'type' => 'text',      );      $entity_fields['title']['label'] = t('Title');      break;    case 'taxonomy_term':      $all_fields['name'] = array(        'field_name' => 'name',        'type' => 'text',      );      $entity_fields['name']['label'] = t('Name');      break;  }    // Get a list of all valid fields that we both support and are part of this entity  foreach ($all_fields as $field) {    if (array_key_exists($field['field_name'], $entity_fields)) {      if (in_array($field['type'], array_keys($supported_field_types)) && ($field['field_name'] != $this_field['field_name'])) {        $valid_fields[$field['field_name']] = $entity_fields[$field['field_name']]['label'];        foreach ($supported_field_types[$field['type']] as $handler) {          $available_handlers[$handler] = $processors[$handler]['title'];          $handlers_by_type[$field['type']][] = $handler;          $field_types[$field['field_name']] = $field['type'];        }      }    }  }    $form['geocoder_field'] = array(    '#type' => 'select',    '#title' => t('Geocode from field'),    '#default_value' => isset($settings['geocoder_field']) ? $settings['geocoder_field']: '',    '#options' => $valid_fields,    '#description' => t('Select which field you would like to geocode from.'),    '#required' => TRUE,    '#attributes' => array('onchange' => 'geocoder_admin_field_selected();'),  );    $form['geocoder_handler'] = array(    '#type' => 'select',    '#title' => t('Geocoder'),    '#prefix' => '<div id="geocoder-handler-div">',    '#suffix' => '</div>',    '#default_value' => isset($settings['geocoder_handler']) ? $settings['geocoder_handler']: '',    '#options' => $available_handlers,    '#description' => t('Select which type of geocoding handler you would like to use'),    '#attributes' => array('onchange' => 'geocoder_admin_handler_selected();'),    '#required' => TRUE,  );    $form['handler_settings'] = array(    '#tree' => TRUE,  );    // Add the handler settings forms  foreach ($processors as $handler_id => $handler) {    if (isset($handler['settings_callback']) || isset($handler['terms_of_service'])) {      $default_values = isset($settings['handler_settings'][$handler_id]) ? $settings['handler_settings'][$handler_id] : array();      $form['handler_settings'][$handler_id] = array();      $form['handler_settings'][$handler_id]['#type'] = 'fieldset';      $form['handler_settings'][$handler_id]['#attributes'] = array('class' => array('geocoder-handler-setting', 'geocoder-handler-setting-' . $handler_id));      $form['handler_settings'][$handler_id]['#title'] = $handler['title'] . ' Settings';            if (isset($handler['terms_of_service'])) {        $form['handler_settings'][$handler_id]['tos'] = array(          '#type' => 'item',          '#markup' => t('This handler has terms of service. Click the following link to learn more.') . ' ' . l($handler['terms_of_service'], $handler['terms_of_service']),        );      }      if (isset($handler['settings_callback'])) {        $settings_callback = $handler['settings_callback'];        $form['handler_settings'][$handler_id] = array_merge($form['handler_settings'][$handler_id], $settings_callback($default_values));      }    }  }    $form['delta_handling'] = array(    '#type' => 'select',    '#title' => t('Multi-value input handling'),    '#description' => t('Should geometries from multiple inputs be: <ul><li>Matched with each input (e.g. One POINT for each address field)</li><li>Aggregated into a single MULTIPOINT geofield (e.g. One MULTIPOINT polygon from multiple address fields)</li><li>Broken up into multiple geometries (e.g. One MULTIPOINT to multiple POINTs.)</li></ul>'),    '#default_value' => isset($settings['delta_handling']) ? $settings['delta_handling']: 'default',    '#options' => array(      'default' => 'Match Multiples (default)',      'm_to_s' =>  'Multiple to Single',      's_to_m' =>  'Single to Multiple',      'c_to_s' =>  'Concatenate to Single',      'c_to_m' =>  'Concatenate to Multiple',    ),    '#required' => TRUE,  );    drupal_add_js(array('geocoder_widget_settings' => array('handlers' => $handlers_by_type, 'types' => $field_types)), 'setting');  drupal_add_js(drupal_get_path('module', 'geocoder') . '/geocoder.admin.js', 'file');    return $form;}/** * Implements hook_field_attach_presave(). * * Geocoding for the geocoder widget is done here to ensure that only validated * and fully processed fields values are accessed. */function geocoder_field_attach_presave($entity_type, $entity) {  // Loop over any geofield using our geocode widget  $entity_info = entity_get_info($entity_type);  $bundle_name = empty($entity_info['entity keys']['bundle']) ? $entity_type : $entity->{$entity_info['entity keys']['bundle']};  foreach (field_info_instances($entity_type, $bundle_name) as $field_instance) {    if ($field_instance['widget']['type'] === 'geocoder') {      // Required settings      if (isset($field_instance['widget']['settings']['geocoder_handler']) && isset($field_instance['widget']['settings']['geocoder_field'])) {        $handler = geocoder_get_handler($field_instance['widget']['settings']['geocoder_handler']);        $field_name = is_array($field_instance['widget']['settings']['geocoder_field']) ? reset($field_instance['widget']['settings']['geocoder_field']) : $field_instance['widget']['settings']['geocoder_field'];        $target_info = field_info_field($field_instance['field_name']);        $target_type = $target_info['type'];                // Get the handler-specific-settings        if (isset($field_instance['widget']['settings']['handler_settings'][$handler['name']])) {          $handler_settings = $field_instance['widget']['settings']['handler_settings'][$handler['name']];        }                // Determine how we deal with deltas (multi-value fields)        if (empty($field_instance['widget']['settings']['delta_handling'])) {          $delta_handling = 'default';        }        else {          $delta_handling = $field_instance['widget']['settings']['delta_handling'];        }                list($field_info, $items) = geocoder_widget_get_field_data($entity_type, $entity, $field_name, $delta_handling);                if (is_array($items) && count($items)) {          $values = array();                    // Geocode geometries          $geometries = array();          foreach ($items as $delta => $item) {            // Geocode any value from our source field.            try {              if (isset($handler_settings)) {                $geometry = call_user_func($handler['field_callback'], $field_info, $item, $handler_settings);              }              else {                $geometry = call_user_func($handler['field_callback'], $field_info, $item);              }              if ($geometry instanceof Geometry) {                $geometries[] = $geometry;              }            }            catch (Exception $e) {              drupal_set_message($e->getMessage(), 'error');            }          }                    if (empty($geometries)) $values = array(NULL);          else {            // Resolve multiple-values - get back values from our delta-resolver            $values = geocoder_widget_resolve_deltas($geometries, $delta_handling, $target_type);          }                    // Set the values - geofields do not support languages          $entity->{$field_instance['field_name']}[LANGUAGE_NONE] = $values;        }      }    }  }}/** * Get field items and info *  * We always pass the full field-item array (with all columns) to the handler, but there is some preprocessing * that we need to do for the special case of entity-labels and multi-field contactenation * For these two special cases we "mock-up" a text-field and pass it back for geocoding */function geocoder_widget_get_field_data($entity_type, $entity, $field_name, $delta_handling) {  $entity_info = entity_get_info($entity_type);    // First check to see if it's an entity field  if (in_array($field_name, $entity_info['entity keys'])) {    $items[]['value'] = $entity->$field_name;    $field_info = array(      'type' => 'text',      'entity key' => TRUE,    );  }  else {    // It's a normal field-api field    $field_info = field_info_field($field_name);    $langcode = field_language($entity_type, $entity, $field_name);    $items = field_get_items($entity_type, $entity, $field_name, $langcode);  }    // Check if we should concatenate  if ($delta_handling == 'c_to_s' || $delta_handling == 'c_to_m') {    $support_concat = array('text', 'text_long', 'computed', 'text_with_summary', 'computed');    if (!in_array($field_info['type'], $support_concat)) {      drupal_set_message(t('Trying to concatenate a non-text field'));      return;    }    $concat = '';    foreach ($items as $item) {      $concat .= trim($item['value']) . ', ';    }    $concat = trim($concat, ', ');    $items = array(array('value' => $concat));    $field_info = array(      'type' => 'text',      'concated' => TRUE,    );  }    return array(    $field_info,    $items,   );}/** * Geocder Widget - Resolve Deltas *  * Given a list of geometries, and user configuration on how to handle deltas,  * we created a list of items to be inserted into the fields. */function geocoder_widget_resolve_deltas($geometries, $delta_handling = 'default', $field_type) {  $values = array();    // Default delta handling: just pass one delta to the next  if ($delta_handling == 'default') {    foreach ($geometries as $geometry) {      $values[] = geocoder_widget_values_from_geometry($geometry, $field_type);    }  }    // Single-to-multiple handling - if we can, explode out the component geometries  if ($delta_handling == 's_to_m' || $delta_handling = 'c_to_m') {    $type = $geometries[0]->geometryType();    if (in_array($type, array('MultiPoint', 'MultiLineString', 'MultiPolygon', 'GeometryCollection'))) {      $components = $geometries[0]->getComponents();      foreach ($components as $component) {        $values[] = geocoder_widget_values_from_geometry($component, $field_type);      }    }    else {      $values[] = geocoder_widget_values_from_geometry($geometries[0], $field_type);    }  }    // For multiple-to-single handling, run it though geometryReduce   if ($delta_handling == 'm_to_s' || $delta_handling = 'c_to_s') {    $reduced_geom = geoPHP::geometryReduce($geometries);    $values[] = geocoder_widget_values_from_geometry($reduced_geom, $field_type);  }    return $values;}/** * Geocder Widget - Field values from geometry *  * Given a geometry and the field type, return back a values array for that field. * The passed back array represents a single delta.  */function geocoder_widget_values_from_geometry($geometry, $field_type) {  if ($field_type == 'geofield') return geofield_get_values_from_geometry($geometry);  if ($field_type == 'geolocation_latlng') {    $centroid = $geometry->centroid();    $lat = $centroid->y();    $lng = $centroid->x();        return array(      'lat' => $lat,      'lng' => $lng,      'lat_sin' => sin(deg2rad($lat)),      'lat_cos' => cos(deg2rad($lng)),      'lng_rad' => deg2rad($lng),    );  }  if ($field_type == 'location') {    $centroid = $geometry->centroid();    return array(      'latitude' => $centroid->y(),      'longitude' => $centroid->x(),      'source' => 2,    );  }}/** * Geocoder Widget - Parse an address field */function geocoder_widget_parse_addressfield($field_item) {  $address = '';  if (!empty($field_item['premise']))                 $address .= $field_item['premise'] . ',';  if (!empty($field_item['thoroughfare']))            $address .= $field_item['thoroughfare'] . ',';  if (!empty($field_item['locality']))                $address .= $field_item['locality'] . ',';  if (!empty($field_item['administrative_area']))     $address .= $field_item['administrative_area'] . ',';  if (!empty($field_item['sub_administrative_area'])) $address .= $field_item['sub_administrative_area'] . ',';  if (!empty($field_item['country']))                 $address .= $field_item['country'] . ',';  if (!empty($field_item['postal_code']))             $address .= $field_item['postal_code'] . ',';    $address = rtrim($address, ', ');    return $address;}
 |