We need to discuss IF and HOW Geolocation Field should store address information (either geocoded or entered by the user).

Google's geocoder for example returns *a lot* information (Try this demo). Here is an example, somewhere on La Rambla in Barcelona, Spain.

  • Lat: 41.38131735079649
  • Lng: 2.173009026050594
  • street_number: 77
  • route: La Rambla
  • locality, political: Barcelona
  • administrative_area_level_2, political: Province Barcelona
  • administrative_area_level_1, political: Catalonia
  • country, political: Spain
  • postal_code: 08002

Google's geocoder is just one widget that helps to enter/generate this information. Geolocation Filed should be flexible enough to provide ONE database structure for any kind of input widget.

So first we need to define which information we want to store.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

mcaden’s picture

FileSize
4.69 KB

I needed to display the address along with the map. All of the logic to deal with the addresses and everything was already there. I added it to the schema, then added options to the google maps widget. The address in this case is simply the "address" text field that is already in use. Here's the patch.

I agree with storing the full geocode data. I simply did this as a quick solution. Hopefully you can gain something from it.

supportPIC’s picture

Another way of doing based on a classical text field:
- add a text field in the content type.
- delete the text field in the module Goole map widget
- modify the js file to:
- impact the added text field for input => replace all off $(.geolocation-input) by $("#edit-field-geolocation").prev().find(':text').
- always update the formatted address from google in the text field => delete condition on mapclick line 21

I am new to Drupal so I even if it is working I don't know if it is a good way of doing... (and sorry I will learn to do patch files)

note: if you want to have more than one field using the module, you can too replace $(.geolocation-input) by $("[id^='edit-field-geo']") so that any field name field_geo... is impacted by the script.

derjochenmeyer’s picture

Thanks for sharing this, supportPIC.

There are several possible routes that we could take in developing Geolocation Field.

  1. Only store geoloction data (lat, lng and some precalculated values to help proximity queries):
    #241484: Geolocation proximity filter for views.

    All other data could be made available in tokens, and every user could choose WHAT he/she wants to store and WHERE (e.g. text fields). That's what supportPIC is suggesting and what we started to discuss here:
    #1036056: Suggestion: Populate custom fields with geocoded data

  2. Develope a standardized way of storing address information along with geolocation data
    That sounds the best option for me. We could start with the hCard (Wikipedia) microformat as a foundation. Here is another hCard specification. This sounds promising:
    In September 2010 Google announced their intention to surface hCard, hReview information in their local search results.

I want a solid, easy solution for the data storage and output. All fancy stuff can be accomplished by custom widgets and formaters.

Coming from there. If we want to be able to output this:

 <div class="vcard">
   <div class="fn org">Some Company in Barcelona</div>
   <div class="adr">
     <div class="street-address">77 La Rambla</div>
     <div> <span class="locality">Barcelona</span>, <span class="region">Catalonia</span> <span class="postal-code">08002</span></div>
     <div class="country-name">Spain</div>
   </div>
   <div class="geo">
     <div class="latitude">41.38131735079649</div>
     <div class="longitude">2.173009026050594</div>
   </div>
 </div>

We need to store this (Barcelona Example from above):

  • Lat: >> lat
  • Lng: >> lng
  • street_number: street_number
  • route: street
  • locality, political: locality (i.e. city)
  • administrative_area_level_2, political: ?
  • administrative_area_level_1, political: region
  • country, political: country_name
  • postal_code: postal_code

Please feel free to tell me I'm wrong.

BenK’s picture

Subscribing

BenK’s picture

@derjochenmeyer: Is there anything that precludes implementing option 2. first and then adding elements of option 1. as an additional feature?

For instance, in addition to storing the address as an hCard microformat, tokens could be provided for those who want to store the address some other way. I like a native and simple way to store the address that is provided by the module, but the flexibility to store it differently if needed.

We could then provide a checkbox setting that enables storage of the address by the module itself (as an hCard microformat). If this setting was left unchecked, then it would be up to the site administrator to use the tokens to store the data some other way. To facilitate this, we could provide a Rules module (http://drupal.org/project/rules) event when "An address was geocoded". Then using Rules' default "Set a data value" action (in combination with tokens provided by the module), site administrators could populate any custom fields on the site with the address information.

Thoughts?

--Ben

derjochenmeyer’s picture

Hi Ben, thanks for sharing your thoughts.

Is there anything that precludes implementing option 2. first and then adding elements of option 1. as an additional feature?

This sounds like a good idea.

My focus is not on storing the information "as an hCard", but rather be able to output it as such. Token and Rules integration sounds very good. Have you experience as a developer with Rules and Token?

BenK’s picture

@derjochenmeyer: Sounds good. Yes, sorry for my sloppy language about storing "as an hCard"... I was just writing my comment quickly. I totally understand what you mean by output rather than storage.

As for Token and Rules integration, I've done a lot of testing/reviewing of contrib modules that integrate with those modules (as well as some limited development). However, I work regularly with a couple of developers who do a lot of development with Token and Rules. So if we were going down that path and needed some input, I could definitely ask them for some help.

Cheers,
Ben

derjochenmeyer’s picture

Ok good, I'll typ to come up with a patch for a first implementation. But you are very welcome to jump in. As stated on the project page "Seeking co-maintainer(s)" :)

Alan D.’s picture

Wouldn't adding additional features like this take away from the share beauty of this module!! KISS

There are so many other geo lookup modules, like location, that are (IMHO) buggy and bloated.

An alternative approach is an independent module that supports multiple lookup engines & stores it's data in a custom multi-component field, which one of these could be a geolocation field.

mcaden’s picture

@Alan: Isn't this thread simply talking about simply storage and display which are what a "field" does?

Storing the data in a detailed and structured manner drastically widens the possibilities of what can be done.

Alan D.’s picture

fair enough.

here is a quick old school way (hacked so not to interact with existing module), like imagefield data array.

some wtf occurred, like serialize wasn't picked up & when using multiples, hook_field_is_empty() was not being called. possibly both major field bugs if there is not errors in my coding.

bad draw back - serialized data so bad / hard views integration

// $Id$

/**
 * @file
 * A latlng field using the Field API.
 */

/**
 * Implements hook_field_info().
 */
function latlng_field_info() {
  return array(
    'latlng' => array(
      'label' => t('Lat Lng'),
      'description' => t('Lat Lng input.'),
      'default_widget' => 'latlng_text',
      'default_formatter' => 'latlng_text',
    ),
  );
}

/**
 * Implements hook_field_validate().
 */
function latlng_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
  foreach ($items as $delta => $item) {
    if (!empty($item['lat'])) {
      if ( !is_numeric($item['lat']) ) {
        $errors[$field['field_name']][$langcode][$delta][] = array(
          'error' => 'latlng_invalid',
          'message' => t('Invalid Latitude.'),
        );
      }
      if ( !is_numeric($item['lng']) ) {
        $errors[$field['field_name']][$langcode][$delta][] = array(
          'error' => 'latlng_invalid',
          'message' => t('Invalid Longitude.'),
        );
      }
    }
  }
}

/**
 * Implements hook_field_presave().
 * $entity_type, $entity, $field, $instance, $langcode, &$items
 */
function latlng_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) {

  unset($items['add_more']);
  foreach ($items AS $delta => $item) {
    if (latlng_field_is_empty($item, $field)) {
      unset($items[$delta]);
    }
    else {
      $item['lat'] = $item['coords']['lat'];
      $item['lng'] = $item['coords']['lng'];
      $item['data'] = serialize($item['data']);
      $item = array_intersect_key($item, array('lat' => '', 'lng' => '', 'data' => ''));
      $items[$delta] = $item;
    }
  }
}

/**
 * Implements hook_field_is_empty().
 */
function latlng_field_is_empty($item, $field) {
  if ( empty($item['coords']['lat']) && $item['coords']['lat'] !== '0' ) {
    return TRUE;
  }
  if ( empty($item['coords']['lng']) && $item['coords']['lng'] !== '0' ) {
    return TRUE;
  }
}

/**
 * Implements hook_field_formatter_info().
 */
function latlng_field_formatter_info() {
  return array(
    'latlng_text' => array(
      'label' => t('Simple text-based formatter'),
      'field types' => array('latlng'),
    ),
    'latlng_latitude' => array(
      'label' => t('Latitude text-based formatter'),
      'field types' => array('latlng'),
    ),
    'latlng_longitude' => array(
      'label' => t('Longitude text-based formatter'),
      'field types' => array('latlng'),
    ),
  );
}

/**
 * Implements hook_field_formatter_view().
 */
function latlng_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  $element = array();

  switch ($display['type']) {

    case 'latlng_text':
      foreach ($items as $delta => $item) {
        $element[$delta]['#markup'] = '<p>' . t('Geolocation is @lat, @lng', array('@lat' => $item['lat'], '@lng' => $item['lng'])) . '</p>';
      }
      break;
    case 'latlng_latitude':
      foreach ($items as $delta => $item) {
        $element[$delta]['#markup'] = $item['lat'];
      }
      break;
    case 'latlng_longitude':
      foreach ($items as $delta => $item) {
        $element[$delta]['#markup'] = $item['lng'];
      }
      break;
  }
  return $element;
}

/**
 * Implements hook_field_widget_info().
 */
function latlng_field_widget_info() {
  return array(
    'latlng_text' => array(
      'label' => t('Latitude/Longitude'),
      'field types' => array('latlng'),
    ),
  );
}

function latlng_field_load($entity_type, $entities, $field, $instances, $langcode, &$items, $age) {
  foreach ($entities as $id => $entity) {
    foreach ($items[$id] as $delta => $item) {
      $items[$id][$delta]['data'] = !empty($item['data']) ? unserialize($item['data']) : array();
    }
  }
}

/**
 * Implements hook_field_widget_form().
 */
function latlng_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
  $lat_value = isset($items[$delta]['lat']) ? $items[$delta]['lat'] : '';
  $lng_value = isset($items[$delta]['lng']) ? $items[$delta]['lng'] : '';
  $data = empty($items[$delta]['data']) ? array() : $items[$delta]['data'];

  $element += array(
    '#delta' => $delta,
  );
  $field['settings'] += array(
    'components' => array(),
    'labels' => array(),
  );
  $field['settings']['components'] = array_filter($field['settings']['components']);
  switch ($instance['widget']['type']) {

    case 'latlng_text':
      $element['data'] = array(
        '#tree' => TRUE,
      );
      foreach(latlng_field_components() as $key => $components) {
        if (in_array($key, $field['settings']['components'])) {
          $element['data'][$key] = array(
            '#type' => $components['type'],
            '#title' => empty($field['settings']['labels'][$key])
                ? $components['title'] : $field['settings']['labels'][$key],
            '#default_value' => isset($data[$key]) ? $data[$key] : '',
          );
        }
      }
      $element['coords']['#prefix'] = '<div class="container-inline">';
      $element['coords']['#suffix'] = '</div>';
      $element['coords']['lat'] = array(
        '#title' => t('Latitude'),
        '#type' => 'textfield',
        '#default_value' => $lat_value,
        '#size' => 15,
        '#maxlength' => 30,
      );
      $element['coords']['lng'] = array(
        '#title' => t('Longitude'),
        '#type' => 'textfield',
        '#default_value' => $lng_value,
        '#size' => 15,
        '#maxlength' => 30,
      );
      break;

  }
  return $element;
}

/**
 * Implements hook_field_widget_error().
 */
function latlng_field_widget_error($element, $error, $form, &$form_state) {
  switch ($error['error']) {
    case 'latlng_invalid':
      form_error($element, $error['message']);
      break;
  }
}

/**
 * Implements hook_field_schema().
 */
function latlng_field_schema($field) {
  $columns = array(
    'lat' => array(
      'description' => 'Stores the latitude value',
      'type' => 'float',
      'size' => 'big',
      'not null' => TRUE,
      'default' => 0,
    ),
    'lng' => array(
      'description' => 'Stores the longitude value',
      'type' => 'float',
      'size' => 'big',
      'not null' => TRUE,
      'default' => 0,
    ),
    'data' => array(
      'type' => 'text',
      'description' => 'Stores additional data',
    ),
  );
  $indexes = array(
    'lat' => array('lat'),
    'lng' => array('lng'),
  );
  return array(
    'columns' => $columns,
    'indexes' => $indexes,
  );
}

function latlng_field_components() {
  $components = array(
    'name' => array('title' => t('Name'), 'type' => 'textfield'),
    'desc' => array('title' => t('Description'), 'type' => 'textarea'),
    'addr1' => array('title' => t('Addr 1'), 'type' => 'textfield'),
    'addr2' => array('title' => t('Addr 2'), 'type' => 'textfield'),
    'addr3' => array('title' => t('Addr 3'), 'type' => 'textfield'),
    'addr4' => array('title' => t('Addr 4'), 'type' => 'textfield'),
    'addr5' => array('title' => t('Addr 5'), 'type' => 'textfield'),
    'addr6' => array('title' => t('Addr 6'), 'type' => 'textfield'),
    'region' => array('title' => t('Region'), 'type' => 'textfield'),
    'country' => array('title' => t('Country'), 'type' => 'textfield'),
    'postcode' => array('title' => t('Post code'), 'type' => 'textfield'),
  );

  return $components;
}

/**
 * Implements hook_field_settings_form().
 */
function latlng_field_settings_form($field, $instance, $has_data) {
  $settings = $field['settings'];
  $settings += array(
    'components' => array(),
    'labels' => array(),
  );
  $form = array('#tree' => TRUE);
  $components = latlng_field_components();
  $options = array();
  foreach ($components as $key => $info) {
    $options[$key] = $info['title'] .'('. $info['type'] .')';
  }
  $form['components'] = array(
    '#type' => 'checkboxes',
    '#title' => t('Components'),
    '#default_value' => $settings['components'],
    '#description' => t('Store additional information about the location.'),
    '#options' => $options,
  );
  $form['labels'] = array();

  foreach ($components as $key => $info) {
    $form['labels'][$key] = array(
      '#type' => 'textfield',
      '#title' => t('Label for !title', array('!title' => $info['title'])),
      '#default_value' => isset($settings['labels'][$key]) ? $settings['labels'][$key] : '',
    );
  }

  return $form;
}
jjchinquist’s picture

Would not defining an API be the best route - define a hook_geolocation_field_store_address_info... and pass all address information from the google api in the hook? Then any contrib module could implement and manage the data. It would keep the geolocation field module separate and not truly responsible for the address data management.

Adam S’s picture

Much of the function requests in this thread can be found using the OpenLayers Geocoder and OpenLayers Proximity modules. However, OpenLayers will not support any of these functions in Drupal 7.

Here I broke down the basic strategy of using a third party service (Google Maps API) to populate via Ajax text fields in a node form based on autocomplete by example of OpenLayers Geocoder. http://drupal.org/node/1044286#comment-4062056

If the Geolocation module can map Lat and Lon to a text field, the text fields then can be used to build an OpenLayers data map using Views. Super easy. To sum up extending this module is make it use information that is already available to populate text fields for other modules to use.

I would very much like to see an address widget that function exactly like the one on the add a business form of yelp.com.

steinmb’s picture

#13 +1

ccardea’s picture

I just briefly took a look at this thread and I'm way too tired right now to take a serious look at anything, but I do have a couple of thoughts. First a little background, I've been working on a project in Drupal 6 using Openlayers Geocoder and Openlayers Proximity. There doesn't seem to be any interest on the part of the maintainers in porting those projects to Drupal 7, which is why I'm here. Now for my thoughts.

  1. I noticed that you started out by saying 'first we need to define what information we want to store. I think we need to take a step back and decide what are our use cases. The information that we need to store is defined at least partially by the use case scenarios. The other part of the equation is what does this module need to do that other modules or core aren't already doing?
  2. For my use case, I need to replace the parts of Openlayers that aren't being ported to Drupal 7, that is
    • OpenLayers CCK
    • Openlayers Geocoder
    • Openlayers Proximity
  3. For my use case, I need for users to type in an address into a search box which returns lat/long data so that I can display points on a map. For that I need.
    • The field to store a single lat/long point
    • The geocoder widget
  4. What to do with the rest of the data provided by the geocoder?
    • The Openlayers Geocoder provided tokens that could be used to fill CCK fields. That is nice to have but not necessarily a requirement.
    • It probably makes sense to let Field API handle storing the remaining data, since they already do that very well. What they do not do is store the geo data, so that is the clear need.
  5. Do you need to store multiple points? Probably. People might need the ability to input multiple points for plotting lines and polygons.
  6. For my use case I also need to have proximity filters for the data I collect. Does this module need to provide that? Not necessarily but possibly. The Openlayers modules worked well for my purposes and could probably be ported to D7 or used as the basis for other modules. The big hangup right now is they are not going to replace the CCK field. So the main problem is just getting the field to work and integrated with views so that it can be used to create data layers.

I probably have way too much to say for my level of expertise. I'm still trying to figure out how to do things in Drupal 6, but I think that may be about to change.

Adam S’s picture

There is a discussion going on at http://groups.drupal.org/location-and-mapping. You should get involved.

lloydpearsoniv’s picture

Why not integrate it with a module that already stores addresses?
http://drupal.org/project/addressfield

steinmb’s picture

@lloydpearsoniv that is one of the things we are discussing, if you follow the URL in #16. The group have created a wiki page where we try to sum it up, and a discussion thread Building the ultimate street address management system and geocoding module for Drupal 7. Pls join us over there :)

Mile23’s picture

I'm just discovering this module, and I think I'll probably end up using it. :-)

Personally I think data could be stored minimally for v.1.0. That is, have table columns for lat, long, and the precalc'ed fields. Also have a column for 'data' that will be a serialized keyed array of whatever else comes back from Google. As the project develops, various use cases will show which information needs to graduate out of the 'data' pile and into its own column. This way, there is an achievable goal for v.1.0 and clear room for change.

My first suggestion: A date/time field so you can locate yourself in spacetime.

lloydpearsoniv’s picture

@steinmb

I followed the link & joined the convo right after i left my comment. Thanks for inviting me. ;)

ccardea’s picture

Hey guys, sorry to have jumped in without giving the issue a thorough read first. I've seen some good ideas here and I have to admit I'm still a little fuzzy about how things work in Drupal 7. It seems to me that the basic idea of a field type module is to just to provide the field type definitions. It looks like you need to provide a default widget and a formatter but basically the field type definition is de-coupled from both the widget definition and the field storage definition.

Am I correct in assuming that you're trying to figure out the schema for your field, and just use sql field storage? If that is the case then it would be possible to define different field types with different schemas, and people could choose the one that works best for their use case. That might actually be the way to achieve the flexibility you're looking for, rather than trying to provide a single structure that works for every kind of input widget.

If you're writing a field storage module then that's a different problem. I should probably take a look at the code. I like the idea of providing tokens that can be used to complete other fields, but as I learn more about this, I'm beginning to think that is more the job of the geo-coder module.

Adam S’s picture

I've tried to make a hybrid between this and the Address field module. It can be found on github at https://github.com/adam-s/addressfield/tree/Plain . You can type in the address parts then click the geocode button OR just move the marker on the map.

Since on the website I'm building everybody will know their address there isn't much need to have the reverse geocode. I just though maybe somebody else would like it so I kept it there.

jjchinquist’s picture

I heartily agree with Mile23's suggestion.

Furthermore, I suggest adding a hook invokation after the data is prepared by Geolocation Field but before serialization and insertion into the DB. It would will allow other maintainers to manage the data in a customized fashion. I envision the scenario where the data should be saved twice, once with the serialized data before the invokation, and once with the data returned by all hook invokations. It could prevent legacy issues for future versions of the Geolocation Field.

ccardea’s picture

@derjochenmeyer

I finally got around to checking out your module and I have to say this is nice work and I apologize being such an idiot. Focusing a little on your original thought:

I want a solid, easy solution for the data storage and output. All fancy stuff can be accomplished by custom widgets and formaters.

AddressField is already doing that, at least the storage part, so I think it would be a mistake to duplicate what they're doing. (I've been doing a little code review and finding out that module duplication is being highly frowned upon by the powers that be.) Integration either by tokens or through an API would be way cool though.

jherencia’s picture

Sub

larowlan’s picture

subs

jm.federico’s picture

I'm here just to say that I think using Address Field should be the way to go. It will be highly used because of commerce and seems good enough.

@derjochenmeyer
Any thoughts on going that way?

jherencia’s picture

Well, I don't know the current state of Addrees Field, but I think it just stores addresses. Most of the developers (me included) prefer Geofield because has integration with Openlayers ant its Views integration.

There are too many modules made to store locations in too many different ways, we should try to centralize efforts in just one and try to make it work in the most number of situations as possible.

Geolocation Field has a great input widget, so we should try to improve what it does better than any other module and let other modules to handle storage. Adding compability to geofield (#1129512: Make Google maps geolocation widget compatible with Geofield) is a necessary step in that direction.

steinmb’s picture

@jherencia pls read #18

Summit’s picture

Subscribing, greetings, Martijn

brandy.brown’s picture

I implemented the patch in #1, but I get this error: Notice: Undefined property: stdClass::$field_location_address in field_sql_storage_field_storage_load() (line 336 of /modules/field/modules/field_sql_storage/field_sql_storage.module). Ideas?

mototribe’s picture

Sub

andrea.cavattoni’s picture

subscribe

vhwouter’s picture

@brandy brown: same here.

sub

phayes’s picture

Hi guys, just an FYI that geocoder module (http://drupal.org/project/geocoder) is now stable and has an AJAX API. It might be a good idea to use it as your geocoding backend. For addresses it currently supports google, yahoo, and yandex (russian) with more on the way.

Also note that it already has support for geolocation in a non-interactive fashion. (grab an address or text field and geocode it to a geolocation field on entity save)

Sinan Erdem’s picture

Are there any improvements on this issue?

I really like to store address data (preferably city and country in separate places), but couldnt find a solution yet.

phayes’s picture

etcetera9,

You can now do this with addressfield, geocoder, and geofield modules.

Sinan Erdem’s picture

@phayes,

Without using Geolocation module?

I am not sure if Geocoder does reverse geocoding yet. What I want is that user selects a point from a map, then I store the address info (city, country etc) in another field.

If you know the way to do it, can you please briefly explain it?

derjochenmeyer’s picture

Thanks for the input and all the thoughts on this topic.

If anybody is interested in further discussing the details, I will split this in to seperate issues for each feature. Starting with
#1780092: Introduce new 'data'-column to store whatever comes back from Google's Geocoder

Thanks Mile23 for comment #19.

Anonymous’s picture

FileSize
97.33 KB

I would like to do the same thing like this: Use another Geolocation field for "Set location" button

But a bit different:
My idea, i make my own CCK adressfields (in the top of the screenshot below) and the user fill in the adressdata in this fields. The adressfield from the geolocation-module is hidden. The data from the CCK fields will automatically copy to the hidden geolocation-adressfield. After filling, the user click on the visible button "Get location". After that, i have all data that i need. The longitude and latitude compatible to the adressdata from the CCK-fields. But i have no acces to the geolocation-adressfield... Where is the data stored from this field? I know... not in the database... But when i edit a user i can see the data in the field, thus, the data are stored somewhere or I'm wrong? Can anybody help me with this?

derjochenmeyer’s picture

@FeanorCC its not stored. Only lat,lng is stored. The adress data you see, is the closest address returned by googles geocoder (reverse geocoded from lat,lng).

TravisJohnston’s picture

The patch included at the top has the right idea, but is not compatible with the recent version of Geolocation. Anyone willing to rewrite it? The ability to show the address instead of "Geolocation Long 999999999 Lat 99999" is a pretty common need and should be a default option to show the Address and the Address + Map. Integration with Location Module could help.

TravisJohnston’s picture

I managed to get this working. In my case, there turned out to be a select number of locations that were going to be used often in the website so I did this.

Create a Location Entity (Content Type)
- Postal Address Field for standard address information
- Geolocation Field for map

Create an Entity Reference field in the chosen entity, for my case it was a Commerce Product Type

Then your done. Obviously only works with with a specific set of locations, though the benefit of this is its able to be searchable and both the address and the map show.

pcbudde’s picture

Issue summary: View changes

Has this patch been made compatible with the latest version of geolocation?

Travis I do not understand your example. Do users then have the address field module to input data?

I could really use this functionality however I am not a seasoned php user. Can the previewed address that is shown with geolocation be stored with a computed field and how?

derjochenmeyer’s picture

This issue needs a direction :)

First of all we discuss two things here:

  1. Store user input (the data ENTERED by the user)
  2. Store geocoded address

The first task is easy to accomplish but at the same time not very useful.
The second task (store data that Google geocoder returns) may violate the Google Maps API Terms of Service.

Caching Considerations

The Google Maps API allows you to cache geocodes (i.e. store them on your server for a limited period). Caching can be useful if you have to repeatedly look up the same address. However, there are two important things to keep in mind.

1. The Google Maps API Terms of Service allow you to use geocodes derived from the service on Google Maps or Google Earth only. You may not sell or distribute them in other fashion.

2. Geocoding changes often as our data gets more and more accurate. So even if you have cached data, you should refresh it periodically, to make sure you are getting the best geocodes for your locations.

See this link.

Well what is a "limited period"?

However even storing latlng values for an "unlimited period" or displaying the data outside of Goolge Maps or Google earth violates the Google Maps API Terms of Service.

But on the other hand this very much depends on the type of service a website offers. Using the geocoder to get address and geolocation data is certainly what Google wants and encourages us to do. Well and creating a node is kind of caching that data (certainly as long as we display a Google map alongside the data).

So back to the beginning. What should we store?

Google returns a lot of stuff.

The easiest option is to store the formatted_address which is just one string seperated by commas.

But there will be users who want more controll. Storing all possibly returned stuff is certainly too much. Storing just the string might be not enough.

pcbudde’s picture

okay I see. Its difficult to select which data is the most important because each website has its unique demand.

Would it be possible to create user settings where its possible to change the lookup to the desired address?

Currently I am only in need of the country to make it sortable in views global filter. (what would a quick workaround be?)

JordiTR’s picture

Just my 5 cents. Why no saving on a new column what the user entered on the address field? On the module description it clearly states to focus on being a light-weight, easy-to-use solution, in front of more complex proposals. That's what I think it makes great this module. So... where's the problem to store the address entered by the user to be rendered by a formatter as an alternative option?

There´s no need to develop highly flexible address models, since then maybe the module also should develop more complex mapping solutions, shouldn't it? If that solutions for storing and presenting addresses was not enought for some people, no problem, there are other modules for Drupal mapping and addressing. Maybe the mapping solution offered by this module is not enough for many people that finally decided to use other modules (and there are tons of users who took that direction).

What I can't understand is why I'm not able to display on the page the simple address I entered next to the map, being a "light-weight" solution ;-)

kvit’s picture

Supporting #47

derjochenmeyer’s picture

Status: Active » Needs work

I really want to find a clean solution for storing user input and addtional "meta" information provided by the gecoding service.

But the hurdle we have is that Geolocation field stores input from different widgets. Even on the same site different widgets could be used, e.g. Google Maps for Desktop users and HTML5 Geolocation for mobile users. Or HTML5 for comments (like facebooks "share my location" option) while when adding an event a text based input would be required.

If then we want to display locations entered with different tools, we have to deal with a differing data structure.

In HTML5 geolocation the browser always returns lat, lng and accuracy while some information is optional (timestamp, altitude , altitudeAccuracy, ...). Accuracy is important information, and maybe later we want to display only locations with a accuracy higher than X. While other widgets won't provide this piece of information at all.

Plain Text widget    HTML5 geolocation widget     Goolge Maps widget
lat, lng             lat, lng, accuracy           lat, lng, address
                                                  +address input (user)
-----------------    ------------------------     ------------------
       |                        |                         |
       |                        |                         |
       |                        |                         |
       |                        |                         |
       ----------------------------------------------------
                                |
                    ------------------------
                    Gelocation field storage
                    ------------------------
                               /|\
                              / | \
                             /  |  \
                            /   |   \
                           /    |    \
                          /     |     \
                         /      |      \
                        /       |       \
                   Templates  Views     XYZ
JordiTR’s picture

Hi Jochen and thanks for your nice module!

I understand your worries for having a solid data model, a good architecture. But I think people is asking for something different, simply having the choice to print the address on screen... keeping the " light-weight, easy-to-use solution" philosophy. So, maybe is interesting to have a data container, somewhere on the database, for saving the different models you preview. But on the other hand I feel people is asking for a new column on the database where the address entered is saved and a formatter for rendering it next to the map. IMHO :-)

derjochenmeyer’s picture

Thanks for the feedback. It still reminds me of Pandora's box :-)

[...] an action that may seem small or innocent, but that turns out to have severely detrimental and far-reaching consequences. (Wikipedia)

Everybody has a slightly different usecase

  • just displaying the users input
  • just displaying the one-line representation of the geocoded address
  • just storing the json array Google returns -> for later use
  • just creating an infobox in the map with the address
  • ...

All are 100% valid usecases.

Sure, not storing and not beeing able to use any of that information is bad. Geolocation Field needs that feature. I totally agree, I just dont see any generic solution.

Maybe this one. Geolocation provides 2 hidden fields without beeing responsible for storing this information. One for user input one for user output.

<input type="hidden" name="input[]" value="USER-INPUT">
<input type="hidden" name="geocoder_response[]" value="JSON-OUTPUT">

This data can then be used in hooks, tokens, etc. and for every usecase the information can be handled differently, and passed to a seperate textfield, address field, etc.

Another option is to store selected data:

  • raw user input for Goolge geocoder widget
  • raw json array
  • accuracy for HTML5 widget

But then geolocation field (and the stored data) depends on the widgets and formatters.

Pandora's box.

JordiTR’s picture

Hi Jochen. As long as I've participated on that thread I just would say that openning the Pandora's box would be trying to push the module too far from its initial goals, which were the goals you decided initially. I think on the model "lightweight solution" that you promote on the description of the field. So, we input an address, the systems prints that address, or the map, or thee coordinates. That simple :-)

Openning the Pandora box probably would move me to another more complete solutions for addressing or mapping, and I've already done it on complex projects. That's what I love of your project, the simplicity... but I still lack the possibility to print the address I used to get the map :-)

JordiTR’s picture

The only debate, for me, is: saving the user's initial address or the one returned by Google...
;-)

heddn’s picture

derjochenmeyer’s picture

That means it already violates Googles TOS to store lat, lng values and to begin with, even create a "Wrapper" like the Geocoder widget of this module?

Bad news: https://developers.google.com/maps/terms#section_10_2

Or is there another interpretation?

heddn’s picture

Google isn't the only one out there in the pond. Bing lets you store lat/long. And even Google encourages caching of lat/long, just not longer than 30 days. So for Goole just set up a cron run to flush the data prior to 30 days and everything is fine.

derjochenmeyer’s picture

What about storing a GeoJSON string.

In addition to the indexed and queriable lat, lng values this could make sense to store any additional information.

{
  "type": "Feature",
  "geometry": {
    "type": "Point",
    "coordinates": [125.6, 10.1]
  },
  "properties": {
    "user_input": "Dinagat Islands",
    "geocoder_return": "..."
  }
}

Anybody who can give advice on how to store GeoJSON?

mtoscano’s picture

The lack of this feature is blocking me from using this great module, that offer much better user experience in the edit form. We definitely need a way to display the address, and if Google TOS are too restrictive probably we need to look somewhere else.
Anyway, at least the option to store and display the address entered by the user can be a good staring point.

mtoscano’s picture

I tried to use the solution provided here https://www.drupal.org/node/1369390#comment-6854534, adding the code to the geolocation.js in the geolocation folder, modifying the field name to match mine and clearing the cache, but it doesn't works.
Should I put that jQuery code in geolocation.js or somewhere else?

dinarcon’s picture

Hello,

I needed to store the formatted address along with the other field information. This is suggested in #45, although it seems that storing any information permanently is against the TOS as referred in #54. Does flushing the data and bulk importing it again would overcome this limitation?

While we figure this out, let me share a proposed implementation for this feature. The recipe that I followed is:

  1. Alter the field schema definition using the patch in #691932-92: Add hook_field_schema_alter(). With this I add an 'address' column where the formatted address would be stored. Applying the patch is required for this proposal to work.
  2. Create a new field field formatter called 'Reversed geocoded address formatter' that renders the field as a formatted address
  3. In the edit form, I also auto autoupdate the user entered location with the formatted address that is returned. This happens when clicking the 'Get location' button or by manually placing the marker on the map.

The attached patch applies to the latest dev and stable versions.

What are your thoughts?

dinarcon’s picture

Status: Needs work » Needs review
mikefyfer’s picture

Any hope of this getting added to the D8 branch?

derjochenmeyer’s picture

This is my personal top feature request. I would love to have a patch for D8 address module integration.

Summit’s picture

Hi,
I would love this in D7 and then ported to D8:) A lot of people, like myself are still on D7 because also of other modules which are not developed, or not enough developed on D8.
Thanks for your hard work on this great module until now!
Greetings, Martijn

giufog’s picture

#60patch per me non ha funzionato (errore del server)

platinum1’s picture

@derjochenmayer I would love the integration you proposed in #63

malcomio’s picture

Yes, integration with Address would be great - I've created #2681335: Dump address result from geocoder widget in address field on the D8 version for this.

hanoii’s picture

Nice patch!!! Well, it works. Obviously as stated by @dinarcon, you MUST apply #691932-92: Add hook_field_schema_alter().

I just improved very lightly by pre-filling the previously stored address in the address field and preventing the reverse geocoding to work on load. This is because you expect an entered address, even after reverse geocoding, to be kept as is, rather than somehow translated to a range of addresses.

Status: Needs review » Needs work

The last submitted patch, 68: geolocation-store_geocoded_address-1040640-68-7.x.patch, failed testing.

hanoii’s picture

Something was not quite right in the previous patch, as I missed something with latest dev. I don't think #60 works on dev, it needs to be re-rolled. This one applied fine to 1.6.

Status: Needs review » Needs work

The last submitted patch, 70: geolocation-store_geocoded_address-1040640-69-7.x.patch, failed testing.

hanoii’s picture

Hmm, another issue with the original patch. sorry.

Alan D.’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 72: geolocation-store_geocoded_address-1040640-70-7.x.patch, failed testing.

hanoii’s picture

It's failing tests because it doesn't apply properly to dev. I will review but something added by this patch on #60 doesn't seem to still be there on dev, so probably either removed, reworked or something. It does need a proper reroll for dev.

hanoii’s picture

Status: Needs work » Needs review
FileSize
9.59 KB

Some code was moved to an internal function.

Attached is a patch that applies properly to dev so tests passes, although there aren't any, but so it shows as needs review.

Status: Needs review » Needs work

The last submitted patch, 76: geolocation-store_geocoded_address-1040640-76-7.x.patch, failed testing.

hanoii’s picture

Status: Needs work » Needs review
hanoii’s picture

Fixed an undefined notice.

I will work on the patch against dev only for now on. See interdiff for the simple fix for the stable applying patch in #60.

Status: Needs review » Needs work

The last submitted patch, 79: geolocation-store_geocoded_address-1040640-79-7.x.patch, failed testing.

hanoii’s picture

Status: Needs work » Needs review
FileSize
9.89 KB
904 bytes

Another undefined notice

Status: Needs review » Needs work

The last submitted patch, 81: geolocation-store_geocoded_address-1040640-81-7.x.patch, failed testing.

hanoii’s picture

Status: Needs work » Needs review
mtoscano’s picture

Applied #83 and #691932-92: Add hook_field_schema_alter(), but at the end I wasn't able to test because the new field formatter called 'Reversed geocoded address formatter' does not support Geofield as storage, and even on form edit the user submitted address is not retained.
Anyway this solution requires core patching (at least in Drupal 7), not the best way to store address in this module.

panhead490’s picture

Tried to use the hook_field_schema_alter() patch and it ran fine but is there anything else that I need to do? Not seeing the table structure change.

Thanks for any help or guidance. Forgive the file/patch references -- thought I had everything unchecked but alas I did not.

jeppy64’s picture

Let me start by saying I am not a module writer, or even coder for that matter. But I have been following this thread for a while. This module is super as is but I do see a very valid need for the plain text address concept. The address field is great but it's hefty in many ways and almost "too much" complexity in my opinion. Maybe looking at the Simple Google Maps module would offer a new thought direction? If there's a way to combine what that module does with the single plain text field taking comma separated address values and bundling that into what this module has... it's a winner as I see it.

lias’s picture

Component: Geolocation field » Geolocation Field / Backend

I would like to second jeppy64 comment re: adding plain text address to this module. I also think Address in 8x is just too much - I have no need for commerce features and all the dependencies.

I've used Simple Google Maps paired with Computed Field to provide a basic map and text address display but would LOVE to be able to add additional addresses to map and proximity search.

That would be a great location/map module and it seems like Geolocation Field is almost there.

If there is a module out there that already fits this description and is production ready for D8 I'd appreciate a pointer - not Address please.

imclean’s picture

I've had a quick skim through but I'm not completely sure of the scope of this issue. From the issue summary, it appears caching the responses from the Geocoder would be a good first step.

We're using the Drupal 8 version with Nominatim which has a usage policy which includes:

Websites and Apps

Use that is directly triggered by the end-user (for example, user searches for something) is ok, provided that your number of users is moderate. Note that the usage limits above apply per website/application: the sum of traffic by all your users should not exceed the limits.

Apps must make sure that they can switch the service at our request at any time (in particular, switching should be possible without requiring a software update). If at all possible, set up a proxy and also enable caching of requests.

Note: periodic requests from apps are considered bulk geocoding and as such are strongly discouraged. It may be okay if your app has very few users and applies appropriate caching of results. Make sure you stay well below the API usage limits.

Bulk Geocoding

As a general rule, bulk geocoding of larger amounts of data is not encouraged. If you have regular geocoding tasks, please, look into alternatives below. Smaller one-time bulk tasks may be permissible, if these additional rules are followed

  • limit your requests to a single thread
  • limited to 1 machine only, no distributed scripts (including multiple Amazon EC2 instances or similar)
  • Results must be cached on your side. Clients sending repeatedly the same query may be classified as faulty and blocked.

We're using the API directly via Drupal\geolocation\GeocoderManager, so we could set up some custom caching. It will also have some pretty limited lookups, with possibly around 10 to 20 different places in total. Which, based on the usage policy, necessitates some local caching.

This could be a simple key:value storage of the lookup. For example:

Key: "Town, State"
Value: serialised location result

imclean’s picture

Any caching should probably be more flexible. For example, the following, and other variations, should all return the same cached response:

  • Town, state
  • Country, town, state
  • Country town state
  • State town
  • State, town
ChristianAdamski’s picture

Status: Needs review » Closed (won't fix)

Closing all 7.x issues. It's time.