geofield.install 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. <?php
  2. /**
  3. * @file
  4. * Install, update and uninstall functions for the geofield module.
  5. */
  6. /**
  7. * Implements hook_field_schema().
  8. */
  9. function geofield_field_schema($field) {
  10. $schema_callback = '';
  11. ctools_include('plugins');
  12. if (isset($field['settings']['backend'])) {
  13. $backend = ctools_get_plugins('geofield', 'geofield_backend', $field['settings']['backend']);
  14. }
  15. else {
  16. $backend = ctools_get_plugins('geofield', 'geofield_backend', 'default');
  17. }
  18. if (!empty($backend['schema'])) {
  19. $schema_callback = $backend['schema'];
  20. } else {
  21. /**
  22. * Edge case, during an update between 7.x-1.x and 7.x-2.x, if ctools is enabled,
  23. * ctools will not see the geofield plugin without manually truncating the cache
  24. * tables. To bypass, we manually seed the $backend array directly if
  25. * $backend['schema'] still isn't declared.
  26. **/
  27. module_load_include('inc', 'geofield', 'includes/geofield_backend/default');
  28. $schema_callback = 'geofield_backend_default_schema';
  29. }
  30. $geom_schema = $schema_callback($field);
  31. return array(
  32. 'columns' => array(
  33. 'geom' => $geom_schema, // Defined by the backend plugin
  34. 'geo_type' => array(
  35. 'type' => 'varchar',
  36. 'default' => '',
  37. 'length' => 64,
  38. ),
  39. 'lat' => array(
  40. 'type' => 'numeric',
  41. 'precision' => 18,
  42. 'scale' => 12,
  43. 'not null' => FALSE,
  44. ),
  45. 'lon' => array(
  46. 'type' => 'numeric',
  47. 'precision' => 18,
  48. 'scale' => 12,
  49. 'not null' => FALSE,
  50. ),
  51. 'left' => array(
  52. 'type' => 'numeric',
  53. 'precision' => 18,
  54. 'scale' => 12,
  55. 'not null' => FALSE,
  56. ),
  57. 'top' => array(
  58. 'type' => 'numeric',
  59. 'precision' => 18,
  60. 'scale' => 12,
  61. 'not null' => FALSE,
  62. ),
  63. 'right' => array(
  64. 'type' => 'numeric',
  65. 'precision' => 18,
  66. 'scale' => 12,
  67. 'not null' => FALSE,
  68. ),
  69. 'bottom' => array(
  70. 'type' => 'numeric',
  71. 'precision' => 18,
  72. 'scale' => 12,
  73. 'not null' => FALSE,
  74. ),
  75. 'geohash' => array(
  76. 'type' => 'varchar',
  77. 'length' => GEOFIELD_GEOHASH_LENGTH,
  78. 'not null' => FALSE,
  79. ),
  80. ),
  81. 'indexes' => array(
  82. //'geo_type' => array('geo_type'),
  83. 'lat' => array('lat'),
  84. 'lon' => array('lon'),
  85. 'top' => array('top'),
  86. 'bottom' => array('bottom'),
  87. 'left' => array('left'),
  88. 'right' => array('right'),
  89. 'geohash' => array('geohash'),
  90. 'centroid' => array('lat','lon'),
  91. 'bbox' => array('top','bottom','left','right'),
  92. ),
  93. );
  94. }
  95. /**
  96. * Changing srid from int to text
  97. */
  98. function geofield_update_7100() {
  99. $fields = field_info_fields();
  100. foreach ($fields as $field_name => $field) {
  101. if ($field['type'] == 'geofield' && $field['storage']['type'] == 'field_sql_storage') {
  102. $srid_schema = array(
  103. 'type' => 'text',
  104. 'not null' => FALSE,
  105. );
  106. foreach ($field['storage']['details']['sql'] as $type => $table_info) {
  107. foreach ($table_info as $table_name => $columns) {
  108. $column_name = _field_sql_storage_columnname($field_name, 'srid');
  109. // Get srid int values
  110. $values = db_select($table_name, 'f')
  111. ->fields('f', array($column_name, 'entity_type', 'bundle', 'entity_id'))
  112. ->execute()
  113. ->fetchAssoc();
  114. db_change_field($table_name, $column_name, $column_name, $srid_schema);
  115. if (!empty($values)) {
  116. // Put the values back as text
  117. foreach ($values as $value) {
  118. if ($value->{$column_name}) {
  119. $new_value = 'EPSG:'.$value->{$column_name};
  120. db_update($table_name)
  121. ->fields(array(
  122. $column_name => $new_value
  123. ))
  124. ->condition('entity_type', $value->entity_type)
  125. ->condition('bundle', $value->bundle)
  126. ->condition('entity_id', $value->entity_id)
  127. ->execute();
  128. }
  129. }
  130. }
  131. }
  132. }
  133. }
  134. }
  135. field_cache_clear();
  136. }
  137. /**
  138. * Change geofield lat, lon, left, top, right and bottom from floats to numeric
  139. * fields with precision of 18 and scale of 12.
  140. */
  141. function geofield_update_7200() {
  142. if (!module_exists('field_sql_storage')) {
  143. return;
  144. }
  145. $field_keys = array('lat', 'lon', 'left', 'top', 'right', 'bottom');
  146. foreach (field_info_fields() as $field_name => $field) {
  147. if ($field['type'] != 'geofield') {
  148. // Not a geofield field.
  149. continue;
  150. }
  151. if ($field['storage']['type'] !== 'field_sql_storage') {
  152. // Field doesn't use SQL storage, we cannot modify the schema.
  153. continue;
  154. }
  155. $table_name = _field_sql_storage_tablename($field);
  156. $revision_table_name = _field_sql_storage_revision_tablename($field);
  157. foreach ($field_keys as $field_key) {
  158. db_change_field($table_name, $field_name . '_' . $field_key, $field_name . '_' . $field_key, array(
  159. 'type' => 'numeric',
  160. 'precision' => 18,
  161. 'scale' => 12,
  162. 'not null' => FALSE,
  163. ));
  164. db_change_field($revision_table_name, $field_name . '_' . $field_key, $field_name . '_' . $field_key, array(
  165. 'type' => 'numeric',
  166. 'precision' => 18,
  167. 'scale' => 12,
  168. 'not null' => FALSE,
  169. ));
  170. }
  171. }
  172. }
  173. /**
  174. * Converts the wkt field into a geom field. Converts already existing data from wkt storage to wkb.
  175. *
  176. * Much inspiration for this implementation comes from taxonomy_update_7005.
  177. */
  178. function geofield_update_7201(&$sandbox) {
  179. // $sandbox contents:
  180. // - total: The total number of geofield wkt rows to migrate.
  181. // - count: The number of geofield wkt rows that have been
  182. // migrated so far.
  183. // - batch_count: The number of rows processed in this batch.
  184. // - last: The db_query_range() offset to use when querying
  185. // an individual table.
  186. // - geofield_tables: An array of tables with the following keys
  187. // - table_name: Name of the table
  188. // - field_name: Name of the field
  189. // - count: Number of rows in this particular table
  190. // - current_table_index: The row in geofield_tables that we're
  191. // currently processing.
  192. $max_batch_count = 1000;
  193. geophp_load();
  194. if (!isset($sandbox['total'])) {
  195. // First pass. Find all the geofields in the db, add _geom field. Create
  196. // helper variables that we'll need to potentially process thousands
  197. // of entries.
  198. $total = 0;
  199. $tables = array();
  200. foreach (field_info_fields() as $field_name => $field) {
  201. if ($field['type'] != 'geofield') {
  202. // Not a geofield field.
  203. continue;
  204. }
  205. $table_name = _field_sql_storage_tablename($field);
  206. $revision_table_name = _field_sql_storage_revision_tablename($field);
  207. db_add_field($table_name, $field_name . '_geom', array(
  208. 'type' => 'blob',
  209. 'size' => 'big',
  210. 'not null' => FALSE,
  211. ));
  212. db_add_field($revision_table_name, $field_name . '_geom', array(
  213. 'type' => 'blob',
  214. 'size' => 'big',
  215. 'not null' => FALSE,
  216. ));
  217. // Primary field table
  218. $table_count = db_query('SELECT COUNT(*) FROM {' . $table_name . '};')->fetchField();
  219. $tables[] = array(
  220. 'table_name' => $table_name,
  221. 'field_name' => $field_name,
  222. 'count' => $table_count,
  223. );
  224. $total += $table_count;
  225. // Revision field table
  226. $table_count = db_query('SELECT COUNT(*) FROM {' . $revision_table_name . '};')->fetchField();
  227. $tables[] = array(
  228. 'table_name' => $revision_table_name,
  229. 'field_name' => $field_name,
  230. 'count' => $table_count,
  231. );
  232. $total += $table_count;
  233. }
  234. $sandbox['total'] = $total;
  235. $sandbox['count'] = 0;
  236. $sandbox['last'] = 0;
  237. $sandbox['current_table_index'] = 0;
  238. if (!empty($tables)) {
  239. $sandbox['geofield_tables'] = $tables;
  240. }
  241. }
  242. $sandbox['batch_count'] = 0;
  243. $sandbox['#finished'] = TRUE; // sensible default
  244. // Primary loop. Run through each table and transfer data from _wkt field
  245. // to the _geom field. A conversion (via geoPHP) is required to go from
  246. // wkt to wkb.
  247. while ($sandbox['count'] < $sandbox['total'] && $sandbox['batch_count'] < $max_batch_count) {
  248. $i = $sandbox['current_table_index'];
  249. if (!empty($sandbox['geofield_tables'][$i])) {
  250. $query = 'SELECT ' . $sandbox['geofield_tables'][$i]['field_name'] . '_wkt AS wkt, entity_id, revision_id, delta FROM {' . $sandbox['geofield_tables'][$i]['table_name'] . '}';
  251. $result = db_query_range($query, $sandbox['last'], $max_batch_count - $sandbox['batch_count']);
  252. $query_total = $result->rowCount();
  253. foreach ($result as $record) {
  254. if ($record->wkt) {
  255. $geom = geoPHP::load($record->wkt, 'wkt');
  256. db_update($sandbox['geofield_tables'][$i]['table_name'])
  257. ->fields(array(
  258. $sandbox['geofield_tables'][$i]['field_name'] . '_geom' => $geom->out('wkb'),
  259. ))
  260. ->condition('entity_id', $record->entity_id)
  261. ->condition('revision_id', $record->revision_id)
  262. ->condition('delta', $record->delta)
  263. ->execute();
  264. }
  265. $sandbox['batch_count']++;
  266. $sandbox['count']++;
  267. }
  268. // Check to see if we've updated all the rows associated with this field.
  269. if ($sandbox['last'] + $query_total < $sandbox['geofield_tables'][$i]['count']) {
  270. $sandbox['last'] += $query_total;
  271. }
  272. else {
  273. $sandbox['current_table_index']++;
  274. $sandbox['last'] = 0;
  275. }
  276. // Let the queue system know if we're done or not with this migration.
  277. $sandbox['#finished'] = ($sandbox['count'] < $sandbox['total']) ? FALSE : TRUE;
  278. }
  279. }
  280. // Field cleanup. If we're done, get rid of _wkt field.
  281. if ($sandbox['#finished'] == TRUE) {
  282. foreach($sandbox['geofield_tables'] as $table) {
  283. db_drop_field($table['table_name'], $table['field_name'] . '_wkt');
  284. }
  285. }
  286. }
  287. /**
  288. * Drops unused table fields srid, accuracy and source, adds
  289. * geohash field, populates it.
  290. */
  291. function geofield_update_7202(&$sandbox) {
  292. foreach (field_info_fields() as $field_name => $field) {
  293. if ($field['type'] != 'geofield') {
  294. // Not a geofield field.
  295. continue;
  296. }
  297. $table_name = _field_sql_storage_tablename($field);
  298. $revision_table_name = _field_sql_storage_revision_tablename($field);
  299. db_add_field($table_name, $field_name . '_geohash', array(
  300. 'type' => 'varchar',
  301. 'length' => 16,
  302. 'not null' => FALSE,
  303. ));
  304. db_add_field($revision_table_name, $field_name . '_geohash', array(
  305. 'type' => 'varchar',
  306. 'length' => 16,
  307. 'not null' => FALSE,
  308. ));
  309. // Populate geohash column.
  310. geophp_load();
  311. $results = db_query('SELECT ' . $field_name . '_geom AS geom, entity_id, revision_id, delta FROM {' . $table_name . '}');
  312. foreach ($results as $record) {
  313. if (!empty($record->geom)) {
  314. $geom = geoPHP::load($record->geom);
  315. // Truncate geohash to max length.
  316. $geohash_truncated = substr($geom->out('geohash'), 0, GEOFIELD_GEOHASH_LENGTH);
  317. db_update($table_name)
  318. ->fields(array(
  319. $field_name . '_geohash' => $geohash_truncated,
  320. ))
  321. ->condition('entity_id', $record->entity_id)
  322. ->condition('revision_id', $record->revision_id)
  323. ->condition('delta', $record->delta)
  324. ->execute();
  325. }
  326. }
  327. $results = db_query('SELECT ' . $field_name . '_geom AS geom, entity_id, revision_id, delta FROM {' . $revision_table_name . '}');
  328. foreach ($results as $record) {
  329. if (!empty($record->geom)) {
  330. $geom = geoPHP::load($record->geom);
  331. // Truncate geohash to max length.
  332. $geohash_truncated = substr($geom->out('geohash'), 0, GEOFIELD_GEOHASH_LENGTH);
  333. db_update($revision_table_name)
  334. ->fields(array(
  335. $field_name . '_geohash' => $geohash_truncated
  336. ))
  337. ->condition('entity_id', $record->entity_id)
  338. ->condition('revision_id', $record->revision_id)
  339. ->condition('delta', $record->delta)
  340. ->execute();
  341. }
  342. }
  343. $deleted_columns = array('srid', 'accuracy', 'source');
  344. foreach ($deleted_columns as $column) {
  345. db_drop_field($table_name, $field_name . '_' . $column);
  346. db_drop_field($revision_table_name, $field_name . '_' . $column);
  347. }
  348. }
  349. }