You are here

public function views_geojson_bbox_argument::query in Views GeoJSON 7

Same name and namespace in other branches
  1. 6 views/views_geojson_bbox_argument.inc \views_geojson_bbox_argument::query()

Use the argument to modify the query.

Overrides views_handler_argument::query

File

views/views_geojson_bbox_argument.inc, line 60

Class

views_geojson_bbox_argument
Custom filter to return only points within a provided bounding box.

Code

public function query($group_by = FALSE) {

  // Apply no bbox filtering if the argument is absent or if this is a
  // search_api based view (which doesn't support sql specific query changes).
  if (empty($this->argument) || $this->view->base_field === 'search_api_id') {

    // @TODO: Implement Search API filtering if Solr BBOX is not an option.
    // @see: https://www.drupal.org/project/search_api_location_solr_bboxfield
    return;
  }
  $this
    ->ensure_my_table();

  // Process coordinates: split string and make sure coordinates are clean.
  $bbox = $this
    ->_explode_bbox_coords($this->argument);
  if (empty($bbox)) {

    // Return no values if arg is invalid.
    $this->argument = FALSE;
    $this->view->built = TRUE;
    $this->view->executed = TRUE;
    return;
  }
  if (isset($this->view->display_handler->display->display_options['style_options'])) {
    $data_source = $this->view->display_handler->display->display_options['style_options']['data_source'];
  }
  else {
    $data_source = $this->view->display['default']->display_options['style_options']['data_source'];
  }

  // We handle latlon and geofield data_source types.
  // No support yet for WKT.
  if ($data_source['value'] === 'latlon') {
    $lat_field_obj = $this->view->field[$data_source['latitude']];
    $lon_field_obj = $this->view->field[$data_source['longitude']];
    $lat_field_table = $lat_field_obj->table;
    $lon_field_table = $lon_field_obj->table;
  }
  elseif ($data_source['value'] === 'geofield') {

    // Geofield includes both Lat and Lon fields.
    // We pretend they're distinct fields so we can use same code as for
    // latlon, since we're just going to get lat & lon from geofield anyway.
    $lat_field_obj = $lon_field_obj = $this->view->field[$data_source['geofield']];
    $lat_field_table = $lon_field_table = $lat_field_obj->table;
  }
  else {

    // @TODO: Implement WKT (and possibly other data source types).
    $this->argument = FALSE;
    $this->view->built = TRUE;
    $this->view->executed = TRUE;
    return;
  }

  // If the Latitude / Longitude fields are really geofields,
  // we need to dig a bit deeper to find field names.
  if (isset($lat_field_obj->field_info) && $lat_field_obj->field_info['type'] === 'geofield') {

    // @TODO: Maybe the field name can really just be "${lat_field_obj}_lat"?
    $lat_field_name = $lat_field_obj->field_info['storage']['details']['sql']['FIELD_LOAD_CURRENT'][$lat_field_table]['lat'];
  }
  else {
    $lat_field_name = $lat_field_obj->real_field;
  }
  if (isset($lon_field_obj->field_info) && $lon_field_obj->field_info['type'] === 'geofield') {
    $lon_field_name = $lon_field_obj->field_info['storage']['details']['sql']['FIELD_LOAD_CURRENT'][$lon_field_table]['lon'];
  }
  else {
    $lon_field_name = $lon_field_obj->real_field;
  }

  // Add JOIN(s) to query.
  $this->query
    ->ensure_table($lat_field_table);
  if ($lon_field_table !== $lat_field_table) {
    $this->query
      ->ensure_table($lon_field_table);
  }

  // Add WHERE(s) to query.
  $left = $bbox['left'];
  $bottom = $bbox['bottom'];
  $right = $bbox['right'];
  $top = $bbox['top'];
  if ($this->options['bbox_wrap']) {

    // OpenLayers' longitude bbox values can be >180 or <-180 when the map
    // wraps around. We need to turn these into BETWEENs with OR.
    if ($right - $left < 360) {
      $group = $this->query
        ->set_where_group('AND');
      if ($left > -180 && $left <= 180) {
        $this->query
          ->add_where($group, "{$lon_field_table}.{$lon_field_name}", $left, '>=');
      }
      else {
        $this->query
          ->set_where_group('OR', $group);
        $left = ($left + 180) % 360;
        if ($left <= 0) {
          $left += 360;
        }

        // -12 % 7 will result in -5 and not 2 as expected
        $left -= 180;
        $this->query
          ->add_where($group, "{$lon_field_table}.{$lon_field_name}", array(
          $left,
          180,
        ), 'BETWEEN');
      }
      if ($right > -180 && $right <= 180) {
        $this->query
          ->add_where($group, "{$lon_field_table}.{$lon_field_name}", $right, '<=');
      }
      else {
        $this->query
          ->set_where_group('OR', $group);
        $right = ($right + 180) % 360;
        if ($right <= 0) {
          $right += 360;
        }

        // -12 % 7 will result in -5 and not 2 as expected
        $right -= 180;
        $this->query
          ->add_where($group, "{$lon_field_table}.{$lon_field_name}", array(
          -180,
          $right,
        ), 'BETWEEN');
      }
    }
  }
  else {
    $group = $this->query
      ->set_where_group('AND');
    $this->query
      ->add_where($group, "{$lon_field_table}.{$lon_field_name}", $left, '>=');
    $this->query
      ->add_where($group, "{$lon_field_table}.{$lon_field_name}", $right, '<=');
  }
  $group = $this->query
    ->set_where_group('AND');
  $this->query
    ->add_where($group, "{$lat_field_table}.{$lat_field_name}", $bottom, '>=');
  $this->query
    ->add_where($group, "{$lat_field_table}.{$lat_field_name}", $top, '<=');
}