In trying to create programmatically a bunch of taxonomy terms with geolocation field attached.
I run into problem with the geolocation module overriding what I am setting for the fields, and claiming that it's now overridden values hasn't got the necessary lat_sin.
My code to enter the data I have coming from another database, including lat+ Long:
function newterm($newterm){
$term = new stdClass();
$term->name = $newterm['name'];
$term->description = $newterm['name'];
if (isset($newterm['street']))$term->field_custom_field_street[LANGUAGE_NONE][0]['value'] = $newterm['street'];
if (isset($newterm['addrinfo']))$term->field_custom_field_addrinfo[LANGUAGE_NONE][0]['value'] = $newterm['addrinfo'];
if (isset($newterm['suburb']))$term->field_custom_field_suburb[LANGUAGE_NONE][0]['value'] = $newterm['suburb'];
if (isset($newterm['area']))$term->field_custom_field_area[LANGUAGE_NONE][0]['value'] = $newterm['area'];
if (isset($newterm['postcode']))$term->field_custom_field_postcode[LANGUAGE_NONE][0]['value'] = $newterm['postcode'];
if (isset($newterm['latitude'])){
$term->field_custom_field_geolocation[LANGUAGE_NONE][0]['lat'] = $newterm['latitude'];
$term->field_custom_field_geolocation[LANGUAGE_NONE][0]['lat_sin'] = round(sin($newterm['latitude']),5);
$term->field_custom_field_geolocation[LANGUAGE_NONE][0]['lat_cos'] = round(cos($newterm['latitude']),5);
}
if (isset($newterm['longitude'])){
$term->field_custom_field_geolocation[LANGUAGE_NONE][0]['lng'] = $newterm['longitude'];
$term->field_custom_field_geolocation[LANGUAGE_NONE][0]['lng_rad'] = round(deg2rad($newterm['longitude']),5);
}
$term->vid = 4;
print_r($term);
taxonomy_term_save($term);
return;
}Geolocation's presave:
/**
* Implements hook_field_presave().
*/
function geolocation_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) {
foreach ($items as $delta => $item) {
// Precalculate some goodness.
$item['lat_sin'] = sin(deg2rad($item['lat']));
$item['lat_cos'] = cos(deg2rad($item['lat']));
$item['lng_rad'] = deg2rad($item['lng']);
$items[$delta] = $item;
}
}Which causes the Geolocation module's field_presave to be called, resulting in data looking like this:
Entity type: taxonomy_term
Entity : stdClass Object ( [name] => Palmerston North, Linton Military Camp [description] => Palmerston North, Linton Military Camp [field_custom_field_street] => Array ( [und] => Array ( [0] => Array ( [value] => 42nd Street ) ) ) [field_custom_field_addrinfo] => Array ( [und] => Array ( [0] => Array ( [value] => ) ) ) [field_custom_field_suburb] => Array ( [und] => Array ( [0] => Array ( [value] => Linton Military Camp ) ) ) [field_custom_field_area] => Array ( [und] => Array ( [0] => Array ( [value] => Palmerston North ) ) ) [field_custom_field_postcode] => Array ( [und] => Array ( [0] => Array ( [value] => 4820 ) ) ) [field_custom_field_geolocation] => Array ( [und] => Array ( [0] => Array ( [lat] => -40.4048 [lat_sin] => -0.42223 [lat_cos] => -0.90649 [lng] => 175.585 [lng_rad] => 3.06454 ) ) ) [vid] => 4 [vocabulary_machine_name] => post_codes )
Field:Array ( [translatable] => 0 [entity_types] => Array ( ) [settings] => Array ( ) [storage] => Array ( [type] => field_sql_storage [settings] => Array ( ) [module] => field_sql_storage [active] => 1 [details] => Array ( [sql] => Array ( [FIELD_LOAD_CURRENT] => Array ( [field_data_field_geolocation] => Array ( [lat] => field_geolocation_lat [lng] => field_geolocation_lng [lat_sin] => field_geolocation_lat_sin [lat_cos] => field_geolocation_lat_cos [lng_rad] => field_geolocation_lng_rad ) ) [FIELD_LOAD_REVISION] => Array ( [field_revision_field_geolocation] => Array ( [lat] => field_geolocation_lat [lng] => field_geolocation_lng [lat_sin] => field_geolocation_lat_sin [lat_cos] => field_geolocation_lat_cos [lng_rad] => field_geolocation_lng_rad ) ) ) ) ) [foreign keys] => Array ( ) [indexes] => Array ( [lat] => Array ( [0] => lat ) [lng] => Array ( [0] => lng ) ) [id] => 16 [field_name] => field_geolocation [type] => geolocation_latlng [module] => geolocation [active] => 1 [locked] => 0 [cardinality] => 1 [deleted] => 0 [columns] => Array ( [lat] => Array ( [description] => Stores the latitude value [type] => float [size] => big [not null] => 1 [default] => 0 ) [lng] => Array ( [description] => Stores the longitude value [type] => float [size] => big [not null] => 1 [default] => 0 ) [lat_sin] => Array ( [description] => Stores the sine of latitude [type] => float [not null] => 1 [default] => 0 ) [lat_cos] => Array ( [description] => Stores the cosine of latitude [type] => float [not null] => 1 [default] => 0 ) [lng_rad] => Array ( [description] => Stores the radian longitude [type] => float [not null] => 1 [default] => 0 ) ) [bundles] => Array ( [node] => Array ( [0] => activity ) [taxonomy_term] => Array ( [0] => post_codes ) ) )
Instance:Array ( [label] => Geolocation [widget] => Array ( [weight] => 36 [type] => geolocation_googlemap [module] => geolocation_googlemaps [active] => 1 [settings] => Array ( [scrollwheel] => 0 [marker_draggable] => 0 ) ) [settings] => Array ( [user_register_form] => ) [display] => Array ( [default] => Array ( [label] => above [type] => geolocation_text [settings] => Array ( ) [module] => geolocation [weight] => 2 ) ) [required] => 0 [description] => Coordinates from a GPS [default_value] => Array ( [0] => Array ( [address] => Array ( [field] => the actual address on the GPS, not the input details ) [lat] => -37.6225978 [lng] => 175.8369236 ) ) [id] => 44 [field_id] => 16 [field_name] => field_geolocation [entity_type] => taxonomy_term [bundle] => post_codes [deleted] => 0 )
ERROR:
PDOException: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'field_geolocation_lat_sin' cannot be null: INSERT INTO {field_data_field_geolocation} (entity_type, entity_id, revision_id, bundle, delta, language, field_geolocation_lat, field_geolocation_lng, field_geolocation_lat_sin, field_geolocation_lat_cos, field_geolocation_lng_rad) VALUES (:db_insert_placeholder_0, :db_insert_placeholder_1, :db_insert_placeholder_2, :db_insert_placeholder_3, :db_insert_placeholder_4, :db_insert_placeholder_5, :db_insert_placeholder_6, :db_insert_placeholder_7, :db_insert_placeholder_8, :db_insert_placeholder_9, :db_insert_placeholder_10); Array ( [:db_insert_placeholder_0] => taxonomy_term [:db_insert_placeholder_1] => 17 [:db_insert_placeholder_2] => 17 [:db_insert_placeholder_3] => post_codes [:db_insert_placeholder_4] => 0 [:db_insert_placeholder_5] => und [:db_insert_placeholder_6] => actual lat [:db_insert_placeholder_7] => actual long [:db_insert_placeholder_8] => [:db_insert_placeholder_9] => [:db_insert_placeholder_10] => ) in...
NB: actual latitude and longitude has been fudged above. The main point of which is that my field data is not getting through, this presave function comes along and picks up a new $instance, with the default GPS cooordinates, then it breaks because it hsan't got the data in $items.
How do I program the custom fields without the presave firing, or can I somehow pass a different instance to the presave? Or am I just doing it WRONG?
Comments
Comment #1
felgharb commentedHello Did you get to fix that?
Im getting the same error :
I have a content type with a geolocation field with a default value : save my location.
I cannot create this content programatically, Im getting this error always...
Comment #2
Sel_Space commentedI have the same issue, the geolocation field isn't required and it throws that exception when trying to create it programatically
Comment #3
Sel_Space commentedWhen I remove the default value from the field it works without exception.
Comment #4
doitDave commentedSadly, this field seems to be no option in terms of automation.
I have been fiddling around for several hours now trying to simply set a value programmatically. It simply won't work like you would expect from common field/entity API terms.
I simply cannot figure one working way that covers new nodes (hook_node_update + entity_metadata_wrapper workaround was the only thing to work at all).
If I miss anything, kindly point it out. If anyone has found himself a workaround (not necessarily with this very field but with any location field) with no 999 dependencies or prerequisites, I would appreciate your sharing it.
All I want to do is - conditionally - set lat and lng from an image field on the same node (I have an Exif extension that even provides lat and lng tokens for any loaded entity and with any other field I would simply add a rules data value action setting the corresponding property in question to [node:field-image:file:myexifmodule:bla:foo:lat] and [...:lng].
It would be _really_ amazing if geolocation's overall simplicity would also cover this (I suppose: frequent) usecase.
Comment #5
doitDave commentedAlso updated overall issue, since support will not be enough (from what I find in the codebase).
Comment #6
derjochenmeyer commentedThankful for any idea on how to make this happen.
Comment #7
doitDave commentedSince your field is still the best choice (for my actual patient at least), let's fix it. :)
first here's how I worked around it in rules (no big deal actually), in hook_node_presave I added some PHP extracted from a form_state dump:
Then, what could we do. Setting with tokens via rules/"set data value" is probably sophisticated. In any way we should at first move the lat_sin etc. to a later point (if there is one, would need some digging in field_api). In entity terms, this would be probably best done on hook_entity_insert/update (so after _presave additions have been done). But I understand that field is a bit different.
Are you more familiar with fields? Otherwise I can look it up too, but maybe not immediately.
A second step might be to figure a way to set both lat and lng in rules in order to use tokens and other standard tools.
If we got this done, I'd figure that a real boost since as I said anything else is very nice and handy and lightweight (it should stay that way IMO). But integration with more Drupal standard contribs such as rules and token would really be good.
I could actually help with tokens, too.
Comment #8
doitDave commentedPragmatic suggestion as a first step:
Let's add an official API function to retrieve the correct field type data (that array from the code above).
some function like
the field_presave callback could also use that function (to reduce c&p code and redundancy).
Comment #9
doitDave commentedComment #10
derjochenmeyer commentedGood suggestion.
But no patch to review > active :)
Comment #11
doitDave commentedok, will create one. good to know you like it. :)
Comment #12
juliencarnot commentedSorry if this is slightly off-topic, but it seems related to my current quest: prepopulating a geolocation-field Google Maps formatter with values from the URL, using Prepopulate module. I also tried with a Geolocation Lat/Lng formatter, with the same results.
I cannot guess a working syntax to match the input names and make it work. I've posted an issue for Prepopulate, but after reading usage.txt once again, I'm under the impression that, like for the body field in Drupal, there could be an array containing the field name [field_r_geoloc][0][lat], which led me to this issue.
I gave it a try with [geolocation_latlng][field_r_geoloc][0][lat] following doitDave's comment in #7, but still no luck. Reading the other comments, I'm now wondering if geolocation_field can support the prepopulate mechanism in it's current state.
Would be grateful for any hints or confirmation about this! If I can't get it to work, I might be interested in the upcoming patch and try to work something out with rules!
Comment #13
florianmuellerchMaybe these two issues are related, despite targeting different Drupal major versions.
Comment #14
christianadamski commented