summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordasjo2012-11-15 18:07:23 (GMT)
committer dasjo2012-11-15 18:07:23 (GMT)
commite0a606535f73eca97f19dd7ac5b93a362291643c (patch)
tree0df5f8266f06f6b3d2271b69dc8431af7e492471
parente1b43ee1d5f1a8c2d3e7dbcdc5fb6ed73a74ecad (diff)
Group by working, php-based clustering not yet ported.
-rw-r--r--geocluster.geohash.inc173
-rw-r--r--geocluster.install14
-rw-r--r--geocluster.module195
-rw-r--r--geocluster.views.inc2
-rw-r--r--includes/Geocluster.inc321
-rw-r--r--includes/GeoclusterHelper.inc15
-rw-r--r--views/GeoclusterViewsDisplayExtender.inc5
7 files changed, 285 insertions, 440 deletions
diff --git a/geocluster.geohash.inc b/geocluster.geohash.inc
new file mode 100644
index 0000000..5735563
--- /dev/null
+++ b/geocluster.geohash.inc
@@ -0,0 +1,173 @@
+<?php
+
+/**
+ * @file
+ * Geohash-specifics for geocluster.
+ */
+
+/**
+ * Diameter of the Earth in kilometers.
+ */
+define('GEOCLUSTER_GEOHASH_LENGTH', 12);
+
+/**
+ * Add geohash columns required by geocluster to geofield schemata.
+ *
+ * Implements hook_field_schema_alter().
+ * @todo: this hook requires a core patch, see http://drupal.org/node/691932
+ */
+function geocluster_field_schema_alter(&$schema, $field) {
+ if ($field['type'] == 'geofield') {
+ // Add an addtional column of data.
+ $schema['columns']['geohash'] = array(
+ 'description' => "Geohash.",
+ 'type' => 'varchar',
+ 'length' => 128,
+ 'not null' => FALSE,
+ );
+ $schema['indexes']['fid_geohash'] = array('fid', 'geohash');
+
+ for ($i = GEOCLUSTER_GEOHASH_LENGTH; $i > 0; $i--) {
+ $name = 'geocluster_index_' . $i;
+ $schema['columns'][$name] = array(
+ 'description' => 'Geocluster index ' . $i,
+ 'type' => 'varchar',
+ 'length' => 128, // could be $i + 3.
+ 'not null' => FALSE,
+ );
+ $schema['indexes']['fid_' . $name] = array('fid', $name);
+ }
+ }
+}
+
+/**
+ * Save geohash information to columns we have added.
+ *
+ * Implements hook_field_attach_presave().
+ */
+function geocluster_field_attach_presave($entity_type, $entity) {
+ $field_info = field_info_field_by_ids();
+ foreach (field_info_instances($entity_type, $entity->type) as $name => $field_info_instance) {
+ $is_valid_field_instance = isset($field_info_instance['field_id']) && isset($field_info[$field_info_instance['field_id']]['type']);
+ if ($is_valid_field_instance
+ &&'geofield' === $field_info[$field_info_instance['field_id']]['type']
+ && !empty($entity->{$name}[LANGUAGE_NONE][0])) {
+ $field = &$entity->{$name}[LANGUAGE_NONE][0];
+ $geohash = _geocluster_get_geohash($field['geom']);
+
+ // Add data for standard geohash column.
+ $field['geohash'] = $geohash;
+
+ // Add data for geohash prefix columns.
+ for ($i = GEOCLUSTER_GEOHASH_LENGTH; $i > 0; $i--) {
+ $field['geocluster_index_' . $i] = _geocluster_get_geohash_prefix($geohash, $i);
+ }
+ }
+ }
+}
+
+/**
+ * Add geohash property for all geofields.
+ *
+ * Implements hook_entity_property_info_alter().
+ */
+function geocluster_entity_property_info_alter(&$info) {
+ foreach ($info as &$entity_type) {
+ _geocluster_add_geohash_property($entity_type);
+ if (isset($entity_type['bundles'])) {
+ foreach ($entity_type['bundles'] as &$bundle) {
+ _geocluster_add_geohash_property($bundle);
+ }
+ }
+ }
+}
+
+/**
+ * Helper function that adds geohash property for a geofield.
+ */
+function _geocluster_add_geohash_property(&$value) {
+ if (isset($value['properties'])) {
+ foreach ($value['properties'] as &$property) {
+ if (isset($property['type']) && $property['type'] == 'geofield') {
+ $property['property info']['geohash'] = array(
+ 'label' => t('Geohash'),
+ 'type' => 'string',
+ 'description' => t('Returns the location as geohash'),
+ 'getter callback' => 'geocluster_geohash_property_get_callback',
+ 'computed' => TRUE,
+ );
+
+ for ($i = GEOCLUSTER_GEOHASH_LENGTH; $i > 0; $i--) {
+ $property['property info']['geocluster_index_' . $i] = array(
+ 'label' => t('Geocluster index ' . $i),
+ 'type' => 'string',
+ 'description' => t('Returns the location as geohash with length ' . $i),
+ 'getter callback' => 'geocluster_index_property_get_callback',
+ 'geohash length' => $i,
+ 'computed' => TRUE,
+ );
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Getter callback for a geofield geohash property.
+ */
+function geocluster_geohash_property_get_callback($entity, array $options, $name, $type, $info) {
+ // @todo: use stored geohash directly instead of converting from geom.
+ return _geocluster_get_geohash($entity['geom']);
+}
+
+/**
+ * Returns geohash prefixes numbered by length.
+ *
+ * see http://stackoverflow.com/questions/11319465/geoclusters-in-solr/11321723#11321723
+ *
+ * example:
+ * 1_u
+ * 2_u2
+ * 3_u2s
+ * ...
+ */
+function geocluster_index_property_get_callback($entity, array $options, $name, $type, $info) {
+ $geohash = geocluster_geohash_property_get_callback($entity, $options, $name, $type, $info);
+ // Output an specifc geohash prefix by the specified length.
+ if (isset($info['geohash length'])) {
+ $length = min($info['geohash length'], strlen($geohash));
+ return _geocluster_get_geohash_prefix($geohash, $length);
+ }
+ // No length specified.
+ // Output an array of all geohash prefixed numbered by their length.
+ $geohash_index = array();
+ for ($i = strlen($geohash); $i > 0; $i--) {
+ $geohash_index[] = _geocluster_get_geohash_prefix($geohash, $i);
+ }
+ return $geohash_index;
+}
+
+/*** INTERNAL HELPERS ***/
+
+/**
+ * Get a geohash from a given geofield geom value.
+ *
+ * @param $geom
+ * @return mixed
+ */
+function _geocluster_get_geohash($geom) {
+ geophp_load();
+ $geometry = geoPHP::load($geom);
+ return $geometry->out('geohash');
+}
+
+/**
+ * Get a geohash prefix of a specified, maximum length.
+ *
+ * @param $geohash
+ * @param $length
+ * @return string
+ */
+function _geocluster_get_geohash_prefix($geohash, $length) {
+ return $length . '_' . substr($geohash, 0, min($length, strlen($geohash)));
+}
diff --git a/geocluster.install b/geocluster.install
index 93e5247..ff9c21e 100644
--- a/geocluster.install
+++ b/geocluster.install
@@ -1,23 +1,31 @@
<?php
-include_once 'geocluster.module';
+require_once 'geocluster.geohash.inc';
+/**
+ * Implements hook_install().
+ */
function geocluster_install() {
_geocluster_handle_fields();
}
+/**
+ * Implements hook_uninstall().
+ */
function geocluster_uninstall() {
_geocluster_handle_fields('uninstall');
}
/**
- * Manually add/remove db columns for existint fields
+ * Manually add/remove db columns for existing fields.
+ *
+ * Based on http://drupal.org/node/691932#comment-6413832.
*/
function _geocluster_handle_fields($op='install') {
$fields = field_info_fields();
foreach ($fields as $field) {
if ($field['type'] == 'geofield') {
- //Perform actions on every table including revisions tables
+ // Perform actions on every table including revisions tables.
foreach($field['storage']['details']['sql'] as $sql) {
foreach($sql as $table_name => $table_data) {
$schema['columns'] = array();
diff --git a/geocluster.module b/geocluster.module
index e3d3d29..93bd83f 100644
--- a/geocluster.module
+++ b/geocluster.module
@@ -1,16 +1,13 @@
<?php
+require_once 'geocluster.geohash.inc';
+
/**
* @file
* Geocluster extension for geofield and maps.
*/
/**
- * Diameter of the Earth in kilometers.
- */
-define('GEOCLUSTER_GEOHASH_LENGTH', 12);
-
-/**
* Implements hook_views_api().
*/
function geocluster_views_api() {
@@ -25,22 +22,12 @@ function geocluster_views_api() {
* Implements hook_views_post_execute_query().
*/
function geocluster_views_post_execute_query(&$view) {
- if ($cluster = geocluster_get_instance($view)) {
+ if ($cluster = _geocluster_get_instance($view)) {
$cluster->cluster($view);
};
}
/**
- * @param $view view
- */
-function geocluster_get_instance(&$view) {
- $i = 1;
- if ($cluster = $view->display_handler->get_option('geocluster_instance')) {
- return $cluster;
- }
-}
-
-/**
* Adds geocluster information to a leaflet data item.
*
* Implements hook_leaflet_views_alter_points_data().
@@ -62,15 +49,10 @@ function geocluster_leaflet_views_alter_points_data($result, &$points) {
*/
function geocluster_views_geojson_render_fields_alter(&$feature, $view, $row, $index) {
if (!empty($row->clustered)) {
- geocluster_add_geojson_cluster_info($feature, count($row->ids));
+ _geocluster_add_geojson_cluster_info($feature, count($row->ids));
}
}
-function geocluster_add_geojson_cluster_info(&$feature, $cluster_items) {
- $feature['clustered'] = TRUE;
- $feature['cluster_items'] = $cluster_items;
-}
-
/**
* Implements theme_preprocess_leaflet_map().
*/
@@ -88,169 +70,26 @@ function geocluster_preprocess_leaflet_map($variables) {
);
}
-/**
- * Add geohash property for all geofields.
- *
- * Implements hook_entity_property_info_alter().
- */
-function geocluster_entity_property_info_alter(&$info) {
- foreach ($info as &$entity_type) {
- geocluster_add_geohash_property($entity_type);
- if (isset($entity_type['bundles'])) {
- foreach ($entity_type['bundles'] as &$bundle) {
- geocluster_add_geohash_property($bundle);
- }
- }
- }
-}
+/*** INTERNAL HELPERS ***/
/**
- * Helper function that adds geohash property for a geofield.
- */
-function geocluster_add_geohash_property(&$value) {
- if (isset($value['properties'])) {
- foreach ($value['properties'] as &$property) {
- if (isset($property['type']) && $property['type'] == 'geofield') {
- $property['property info']['geohash'] = array(
- 'label' => t('Geohash'),
- 'type' => 'string',
- 'description' => t('Returns the location as geohash'),
- 'getter callback' => 'geocluster_geohash_property_get_callback',
- 'computed' => TRUE,
- );
-
- for ($i = GEOCLUSTER_GEOHASH_LENGTH; $i > 0; $i--) {
- $property['property info']['geocluster_index_' . $i] = array(
- 'label' => t('Geocluster index ' . $i),
- 'type' => 'string',
- 'description' => t('Returns the location as geohash with length ' . $i),
- 'getter callback' => 'geocluster_index_property_get_callback',
- 'geohash length' => $i,
- 'computed' => TRUE,
- );
- }
- }
- }
- }
-}
-
-function geocluster_get_geohash($geom) {
- geophp_load();
- $geometry = geoPHP::load($geom);
- return $geometry->out('geohash');
-}
-
-function geocluster_get_geohash_prefix($geohash, $length) {
- return $length . '_' . substr($geohash, 0, min($length, strlen($geohash)));
-}
-
-/**
- * Getter callback for a geofield geohash property.
- */
-function geocluster_geohash_property_get_callback($entity, array $options, $name, $type, $info) {
- return geocluster_get_geohash($entity['geom']);
-}
-
-/**
- * Returns geohash prefixes numbered by length.
+ * Retrieves the geocluster instance from a given view.
*
- * see http://stackoverflow.com/questions/11319465/geoclusters-in-solr/11321723#11321723
- *
- * example:
- * 1_u
- * 2_u2
- * 3_u2s
- * ...
- */
-function geocluster_index_property_get_callback($entity, array $options, $name, $type, $info) {
- $geohash = geocluster_geohash_property_get_callback($entity, $options, $name, $type, $info);
- // Output an specifc geohash prefix by the specified length.
- if (isset($info['geohash length'])) {
- $length = min($info['geohash length'], strlen($geohash));
- return geocluster_get_geohash_prefix($geohash, $length);
- }
- // No length specified.
- // Output an array of all geohash prefixed numbered by their length.
- $geohash_index = array();
- for ($i = strlen($geohash); $i > 0; $i--) {
- $geohash_index[] = geocluster_get_geohash_prefix($geohash, $i);
- }
- return $geohash_index;
-}
-
-function geocluster_field_attach_presave($entity_type, $entity) {
- // Add geohash information for columns we have added.
- $field_info = field_info_field_by_ids();
- foreach (field_info_instances($entity_type, $entity->type) as $name => $field_info_instance) {
- if ('geofield' === $field_info[$field_info_instance['field_id']]['type']) {
- $field = &$entity->{$name}[LANGUAGE_NONE][0];
- $geohash = geocluster_get_geohash($field['geom']);
-
- // Add data for standard geohash column.
- $field['geohash'] = $geohash;
-
- // Add data for geohash prefix columns.
- for ($i = GEOCLUSTER_GEOHASH_LENGTH; $i > 0; $i--) {
- $field['geocluster_index_' . $i] = geocluster_get_geohash_prefix($geohash, $i);
- }
- }
- }
-}
-
-/**
- * Implements hook_field_schema_alter().
+ * @param $view view
*/
-function geocluster_field_schema_alter(&$schema, $field) {
- if ($field['type'] == 'geofield') {
- // Add an addtional column of data.
- $schema['columns']['geohash'] = array(
- 'description' => "Geohash.",
- 'type' => 'varchar',
- 'length' => 128,
- 'not null' => FALSE,
- );
- // Add an additional index.
- $schema['indexes']['fid_geohash'] = array('fid', 'geohash');
-
- for ($i = GEOCLUSTER_GEOHASH_LENGTH; $i > 0; $i--) {
- $name = 'geocluster_index_' . $i;
- $schema['columns'][$name] = array(
- 'description' => 'Geocluster index ' . $i,
- 'type' => 'varchar',
- 'length' => 128, // could be $i + 3.
- 'not null' => FALSE,
- );
- // Add an additional index.
- $schema['indexes']['fid_' . $name] = array('fid', $name);
- }
+function _geocluster_get_instance(&$view) {
+ if ($cluster = $view->display_handler->get_option('geocluster_instance')) {
+ return $cluster;
}
}
/**
- * @param $view
- * @param $query views_plugin_query
+ * Adds cluster info to a views_geojson feature.
+ *
+ * @param $feature
+ * @param $cluster_items
*/
-function geocluster_views_query_alter(&$view, &$query) {
- // (Example assuming a view with an exposed filter on node title.)
- // If the input for the title filter is a positive integer, filter against
- // node ID instead of node title.
- if ($view->name == 'grouptest') {
- $i = 1;
- /*
- // Traverse through the 'where' part of the query.
- foreach ($query->where as &$condition_group) {
- foreach ($condition_group['conditions'] as &$condition) {
- // If this is the part of the query filtering on title, chang the
- // condition to filter on node ID.
- if ($condition['field'] == 'node.title') {
- $condition = array(
- 'field' => 'node.nid',
- 'value' => $view->exposed_raw_input['title'],
- 'operator' => '=',
- );
- }
- }
- }
- */
- }
+function _geocluster_add_geojson_cluster_info(&$feature, $cluster_items) {
+ $feature['clustered'] = TRUE;
+ $feature['cluster_items'] = $cluster_items;
}
diff --git a/geocluster.views.inc b/geocluster.views.inc
index f1207a4..9697407 100644
--- a/geocluster.views.inc
+++ b/geocluster.views.inc
@@ -46,6 +46,7 @@ function geocluster_views_data_alter(&$data) {
),
);
$data[$table_name][$geocluster_field . '_lat'] = array(
+ 'real field' => $field['field_name'] . '_lat',
'group' => 'Content',
'title' => 'Geocluster lat',
'help' => 'Geocluster lat',
@@ -55,6 +56,7 @@ function geocluster_views_data_alter(&$data) {
),
);
$data[$table_name][$geocluster_field . '_lon'] = array(
+ 'real field' => $field['field_name'] . '_lon',
'group' => 'Content',
'title' => 'Geocluster lon',
'help' => 'Geocluster lon',
diff --git a/includes/Geocluster.inc b/includes/Geocluster.inc
index ce2ded5..68f0ed0 100644
--- a/includes/Geocluster.inc
+++ b/includes/Geocluster.inc
@@ -16,6 +16,7 @@ class Geocluster {
/**
* Minimum cluster distance, as defined by GeoclusterViewsDisplayExtender.
+ * @todo: document unit.
*
* @var float
*/
@@ -43,6 +44,13 @@ class Geocluster {
*/
var $geohash_length;
+ /**
+ * Constructor.
+ *
+ * @param $cluster_distance
+ * @param $zoom
+ * @param $field_handler views_handler_field
+ */
function __construct($cluster_distance, $zoom, $field_handler) {
$this->field_handler = $field_handler;
$this->cluster_distance = $cluster_distance;
@@ -61,14 +69,7 @@ class Geocluster {
dd($debug);
}
- function get_cluster_field_alias() {
- $name = $this->get_cluster_field_name();
- return $this->field_handler->aliases[$name];
- }
-
- function get_cluster_field_name() {
- return current($this->field_handler->group_fields);
- }
+ /*** MAIN ALGORITHM ***/
/**
* Perform clustering on a given views result set.
@@ -81,56 +82,54 @@ class Geocluster {
geophp_load();
$values = &$view->result;
- dd("started clustering " . count($values) . " items");
timer_start("geocluster");
+ dd("started clustering " . count($values) . " items");
- // Geohash-based sort algorithm.
- $this->geohashCluster($values);
- }
-
- function geohashCluster(&$values) {
- $use_database_cluster = TRUE;
- if ($use_database_cluster) {
- $group_field = $this->get_cluster_field_alias();
- foreach ($values as $row => $value) {
- $hash_prefix = $value->$group_field;
- if (empty($hash_prefix)) {
- continue;
- }
+ $results_by_geohash = $this->preClusterByGeohash($values);
+ dd("pre-created clusters: " . timer_read("geocluster"));
- $lon = $value->geocluster_lon;
- $lat = $value->geocluster_lat;
- $value->geocluster_geometry = new Point($lon, $lat);
+ $this->clusterByNeighborCheck($values, $results_by_geohash);
+ dd("merged & finalized clusters: " . timer_read("geocluster"));
+ timer_stop("geocluster");
+ }
- $results_by_geohash[$hash_prefix] = array(
- $row => $value,
- );
+ protected function preClusterByGeohash(&$values) {
+ $results_by_geohash = array();
+ $group_field = $this->get_cluster_field_alias();
+ foreach ($values as $row => &$value) {
+ $hash_prefix = $value->$group_field;
+ if (empty($hash_prefix)) {
+ continue;
}
+ $this->initCluster($value);
+ $results_by_geohash[$hash_prefix] = array(
+ $row => $value,
+ );
}
- else {
- $this->clusterByHash($values);
- }
-
- dd("prepared entities: " . timer_read("geocluster"));
-
- dd("created clusters: " . timer_read("geocluster"));
+ return $results_by_geohash;
+ }
- // Loop over geohash-based pre-clusters to create real clusters.
- // Check top right neighbor hashes for overlapping points.
- // Top-right is enough because by the way geohash is structured,
- // future geohashes are always top, topright or right
+ /**
+ * Create final clusters by checking for overlapping neighbors.
+ *
+ * @param $results_by_geohash
+ */
+ protected function clusterByNeighborCheck(&$values, &$results_by_geohash) {
foreach ($results_by_geohash as $current_hash => &$results) {
if (empty($current_hash)) {
continue;
}
$item_key = current(array_keys($results));
$item = $results[$item_key];
+ // Check top right neighbor hashes for overlapping points.
+ // Top-right is enough because by the way geohash is structured,
+ // future geohashes are always top, topright or right
$hash_stack = GeohashHelper::getTopRightNeighbors($current_hash);
foreach ($hash_stack as $hash) {
if (isset($results_by_geohash[$hash])) {
$other_item_key = current(array_keys($results_by_geohash[$hash]));
$other_item = $results_by_geohash[$hash][$other_item_key];
- if ($this->shouldCluster($values, $values[$item_key], $values[$other_item_key])) {
+ if ($this->shouldCluster($values[$item_key], $values[$other_item_key])) {
$this->addCluster($values, $item_key, $other_item_key, $current_hash, $hash, $results_by_geohash, TRUE);
if (!isset($results_by_geohash[$current_hash])) {
continue 2;
@@ -138,33 +137,16 @@ class Geocluster {
}
}
}
- $this->finalizeCluster($values, $item_key);
}
-
- dd("merged & finalized clusters: " . timer_read("geocluster"));
- timer_stop("geocluster");
}
- protected function clusterByHash(&$values) {
- // Prepare input data & parameters.
- $entities_by_type = $this->entities_by_type($values);
- // Generate geohash-based pre-clusters.
- $results_by_geohash = $this->load_entity_fields($entities_by_type, $values, $this->geohash_length);
-
- // Loop over geohash-based pre-clusters to create real clusters.
- foreach ($results_by_geohash as $current_hash => &$entities) {
- $cluster_id = NULL;
- // Add all points within the current geohash to a cluster.
- foreach ($entities as $key => &$entity) {
- if (!isset($cluster_id)) {
- $cluster_id = $key;
- }
- else {
- $this->addCluster($values, $cluster_id, $key, $current_hash, $current_hash, $results_by_geohash);
- }
- }
- $this->updateCluster($values, $cluster_id);
- }
+ /*** ALGORITHM HELPERS ***/
+
+ protected function initCluster(&$value) {
+ $lon = $value->geocluster_lon;
+ $lat = $value->geocluster_lat;
+ $value->geocluster_geometry = new Point($lon, $lat);
+ $value->clustered = TRUE;
}
/**
@@ -175,218 +157,45 @@ class Geocluster {
* @param $row_id2 the second row to be clustered
*/
protected function addCluster(&$values, $row_id, $row_id2, $hash, $hash2, &$entities_by_geohash, $update_center = FALSE) {
- $result1 = &$values[$row_id];
- $result2 = &$values[$row_id2];
- $field_alias = $this->field_handler->field_alias;
-
- if (isset($result1->clustered) || !isset($result2->clustered)) {
- $cluster = &$result1;
- $other = &$result2;
- $other_id = $row_id2;
- $other_hash = $hash2;
- }
- else {
- $cluster = &$result2;
- $other = &$result1;
- $other_id = $row_id;
- $other_hash = $hash;
- }
-
- // @todo: make a real cluster.
- if (!isset($cluster->clustered)) {
- $cluster->cluster_items = array($cluster);
- $cluster->clustered = TRUE;
- }
-
- if (!isset($other->clustered)) {
- $cluster->cluster_items[] = $other;
- }
- else {
- $cluster->cluster_items = array_merge($cluster->cluster_items, $other->cluster_items);
- }
+ $result1 = &$values[$row_id]; $result2 = &$values[$row_id2];
+ // Merge cluster data.
+ $result1->geocluster_ids .= ',' . $result2->geocluster_ids;
+ $result1->geocluster_count += $result2->geocluster_count;
if ($update_center) {
// Calculate new center from all points.
- $center = $this->getCenter(array($cluster, $other));
-
- $cluster->geocluster_geometry = $center;
-
- // php version
- // Overwrite cluster geofield with center value.
- // $geofield = geofield_get_values_from_geometry($center->centroid());
- // $this->set_geofield($cluster, $geofield);
+ $center = GeoclusterHelper::getCenter(array($result1, $result2));
+ $result1->geocluster_geometry = $center;
}
- unset($values[$other_id]);
- unset($entities_by_geohash[$other_hash][$other_id]);
-
- if (count($entities_by_geohash[$other_hash]) == 0) {
- unset($entities_by_geohash[$other_hash]);
+ // Remove other result data that has been merged into the cluster.
+ unset($values[$row_id2]);
+ unset($entities_by_geohash[$hash2][$row_id2]);
+ if (count($entities_by_geohash[$hash2]) == 0) {
+ unset($entities_by_geohash[$hash2]);
}
}
- protected function updateCluster(&$values, $cluster_id) {
- $cluster = &$values[$cluster_id];
- if (isset($cluster->clustered)) {
- $field_alias = $this->field_handler->field_alias;
-
- // Calculate new center from all points.
- $center = $this->getCenter($cluster->cluster_items);
-
- // Overwrite cluster geofield with center value.
- $geofield = geofield_get_values_from_geometry($center->centroid());
- $this->set_geofield($cluster, $geofield);
- }
- }
-
- protected function getCenter($items) {
- foreach ($items as $item) {
- $lat = $item->geocluster_geometry->getY();
- $lon = $item->geocluster_geometry->getX();
- $lat_min = isset($lat_min) ? min($lat_min, $lat) : $lat;
- $lat_max = isset($lat_max) ? max($lat_max, $lat) : $lat;
- $lon_min = isset($lon_min) ? min($lon_min, $lon) : $lon;
- $lon_max = isset($lon_max) ? max($lon_max, $lon) : $lon;
- }
- $lat = ($lat_max + $lat_min) / 2;
- $lon = ($lon_max + $lon_min) / 2;
- $center = new Point($lon, $lat);
- return $center;
- }
-
- protected function finalizeCluster(&$values, $cluster_id) {
- $cluster = &$values[$cluster_id];
- if (isset($cluster->clustered)) {
- $field_alias = $this->field_handler->field_alias;
- $cluster->ids = array();
- $cluster->node_title = 'Cluster ';
- foreach ($cluster->cluster_items as $cluster_item) {
- $cluster->node_title .= '- ' . $cluster_item->node_title;
- $cluster->ids[] = $cluster_item->{$field_alias};
- }
- // Avoid cluster being treated as a normal item.
- if (isset($cluster->{$field_alias})) {
- unset($cluster->{$field_alias});
- }
- }
- $geofield = geofield_get_values_from_geometry($cluster->geocluster_geometry);
- $this->set_geofield($cluster, $geofield);
- }
-
/**
* Determine if two geofields should be clustered as of their distance.
*/
- protected function shouldCluster($values, $value, $value2) {
- $field_alias = $this->field_handler->field_alias;
-
- // Load geofields.
- $geometry = $value->geocluster_geometry;
- $geometry2 = $value2->geocluster_geometry;
-
+ protected function shouldCluster($value, $value2) {
// Calculate distance.
- $distance = GeoclusterHelper::distance_pixels($geometry, $geometry2, $this->resolution);
+ $distance = GeoclusterHelper::distance_pixels($value->geocluster_geometry, $value2->geocluster_geometry, $this->resolution);
return $distance <= $this->cluster_distance;
}
- /**
- * see views_handler_field_field::post_execute()
- */
- function entities_by_type(&$values) {
- // Divide the entity ids by entity type, so they can be loaded in bulk.
- $entities_by_type = array();
- $revisions_by_type = array();
- foreach ($values as $key => $object) {
- if (isset($object->{$this->field_handler->field_alias}) && !isset($values[$key]->_field_data[$this->field_handler->field_alias])) {
- $entity_type = $object->{$this->field_handler->aliases['entity_type']};
- if (empty($this->field_handler->definition['is revision'])) {
- $entity_id = $object->{$this->field_handler->field_alias};
- $entities_by_type[$entity_type][$key] = $entity_id;
- }
- else {
- $revision_id = $object->{$this->field_handler->field_alias};
- $entity_id = $object->{$this->field_handler->aliases['entity_id']};
- $entities_by_type[$entity_type][$key] = array($entity_id, $revision_id);
- }
- }
- }
- return $entities_by_type;
- }
-
- function load_entity_fields($entities_by_type, &$values, $geohash_length) {
- // Load only the field data required for geoclustering.
- // This saves us unnecessary entity loads.
- foreach ($entities_by_type as $entity_type => $my_entities) {
- // Use EFQ for preparing entities to be used in field_attach_load().
- $query = new EntityFieldQuery();
- $query->entityCondition('entity_type', 'node');
- $query->entityCondition('entity_id', $my_entities, 'IN');
- $result = $query->execute();
- $entities = $result[$entity_type];
- field_attach_load(
- $entity_type,
- $entities,
- FIELD_LOAD_CURRENT,
- array('field_id' => $this->field_handler->field_info['id'])
- );
- // @todo handle revisions?
-
- $keys = $my_entities;
- $entities_by_geohash = array();
-
- foreach ($keys as $key => $entity_id) {
- // If this is a revision, load the revision instead.
- if (isset($entities[$entity_id])) {
- $values[$key]->_field_data[$this->field_handler->field_alias] = array(
- 'entity_type' => $entity_type,
- 'entity' => $entities[$entity_id],
- );
-
- $geofield = $this->get_geofield_with_geometry($values[$key]);
- $geohash_key = substr($geofield['geohash'], 0, $geohash_length);
- if (!isset($entities_by_geohash[$geohash_key])) {
- $entities_by_geohash[$geohash_key] = array();
- }
- $entities_by_geohash[$geohash_key][$key] = $entities[$entity_id];
- }
- }
- }
- ksort($entities_by_geohash);
- return $entities_by_geohash;
+ protected function get_cluster_field_alias() {
+ $name = $this->get_cluster_field_name();
+ return $this->field_handler->aliases[$name];
}
- /**
- * Helper function to get the geofield with its geometry for a given result.
- *
- * Geometry will only we loaded once and stored in the geofield.
- *
- * @param $entities all result entities that have been loaded
- * @param $value the current result row value set
- */
- function &get_geofield_with_geometry(&$value) {
- $entity = &$value->_field_data[$this->field_handler->field_alias]['entity'];
-
- $geofield = &$entity->{$this->field_handler->field_info['field_name']}[LANGUAGE_NONE][0];
- if (!isset($geofield['geometry'])) {
- $geofield['geometry'] = geoPHP::load($geofield['geom'], 'wkb');
- }
- if (!isset($geofield['geohash'])) {
- $geofield['geohash'] = $geofield['geometry']->out('geohash');
- }
- return $geofield;
+ protected function get_cluster_field_name() {
+ return current($this->field_handler->group_fields);
}
- function set_geofield(&$value, &$geofield) {
- $data = &$value->geocluster_center;
- // $data = &$value->_field_data[$this->field_handler->field_alias];
- $data['entity']->{$this->field_handler->field_info['field_name']}[LANGUAGE_NONE][0] = $geofield;
- /*
- if (!isset($data['entity_type'])) {
- $data['entity_type'] = "node";
- }
- $data['entity']['type'] = 'article';
- */
- }
+ /*** GETTERS & SETTERS ***/
/**
* @return float
diff --git a/includes/GeoclusterHelper.inc b/includes/GeoclusterHelper.inc
index 5182ff6..900d725 100644
--- a/includes/GeoclusterHelper.inc
+++ b/includes/GeoclusterHelper.inc
@@ -135,6 +135,21 @@ class GeoclusterHelper {
);
}
+ static function getCenter($items) {
+ foreach ($items as $item) {
+ $lat = $item->geocluster_geometry->getY();
+ $lon = $item->geocluster_geometry->getX();
+ $lat_min = isset($lat_min) ? min($lat_min, $lat) : $lat;
+ $lat_max = isset($lat_max) ? max($lat_max, $lat) : $lat;
+ $lon_min = isset($lon_min) ? min($lon_min, $lon) : $lon;
+ $lon_max = isset($lon_max) ? max($lon_max, $lon) : $lon;
+ }
+ $lat = ($lat_max + $lat_min) / 2;
+ $lon = ($lon_max + $lon_min) / 2;
+ $center = new Point($lon, $lat);
+ return $center;
+ }
+
/*
// other, simpler distance implementations that didn't really work out.
diff --git a/views/GeoclusterViewsDisplayExtender.inc b/views/GeoclusterViewsDisplayExtender.inc
index 8b676ca..1e2d40a 100644
--- a/views/GeoclusterViewsDisplayExtender.inc
+++ b/views/GeoclusterViewsDisplayExtender.inc
@@ -127,8 +127,7 @@ class GeoclusterViewsDisplayExtender extends views_plugin_display_extender {
// Add customizations based on style plugin.
// @todo: make this a separate function or hook
- $display = $view->display[$view->current_display];
- $style_plugin_name = $display->display_options['style_plugin'];
+ $style_plugin_name = $this->get_option('style_plugin');
switch ($style_plugin_name) {
case 'leaflet':
$map = $view->style_plugin->options['map'];
@@ -164,7 +163,7 @@ class GeoclusterViewsDisplayExtender extends views_plugin_display_extender {
$this->add_fields(array('geocluster_ids' => 'entity_id'), 'group_concat', $field);
// Add count(entity_id).
- $this->add_fields(array('entity_id'), 'count', $field);
+ $this->add_fields(array('geocluster_count' => 'entity_id'), 'count', $field);
// Add center point: avg(lat), avg(lng).
$avg_fields = array(