You are here

views_geojson.module in Views GeoJSON 6

Same filename and directory in other branches
  1. 7 views_geojson.module

Provide a GeoJSON Views feed style.

File

views_geojson.module
View source
<?php

/**
 * @file
 * Provide a GeoJSON Views feed style.
 */

/**
 * Implements hook_views_api().
 */
function views_geojson_views_api() {
  return array(
    'api' => '2.0',
    'path' => drupal_get_path('module', 'views_geojson') . '/views',
  );
}

/**
 * We almost duplicate the content_handler_field_multiple::render function
 * to get the multiple rendered field values in an array
 * @param $field
 * @param $values
 * @return unknown_type
 */
function _views_geojson_render_multiple_field($field, $values) {
  $options = $field->options;

  // If this is not a grouped field, use content_handler_field::render().
  if (!$field->defer_query) {
    return $field
      ->render($values);
  }

  // We're down to a single node here, so we can retrieve the actual field
  // definition for the node type being considered.
  $content_field = content_fields($field->content_field['field_name'], $values->{$field->aliases['type']});
  $vid = $values->{$field->field_alias};
  if (isset($field->field_values[$vid])) {

    // Gather items, respecting the 'Display n values starting from m' settings.
    $count_skipped = 0;
    $items = array();
    foreach ($field->field_values[$vid] as $item) {
      if (empty($options['multiple']['multiple_from']) || $count_skipped >= $options['multiple']['multiple_from']) {
        if (empty($options['multiple']['multiple_number']) || count($items) < $options['multiple']['multiple_number']) {

          // Grab the nid - needed for render_link().
          $nid = $item['_nid'];
          unset($item['_nid']);
          $items[] = $item;
        }
        else {
          break;
        }
      }
      $count_skipped++;
    }

    // Build a pseudo-node from the retrieved values.
    $node = drupal_clone($values);

    // content_format and formatters will need a 'type'.
    $node->type = $values->{$field->aliases['type']};
    $node->nid = $values->{$field->aliases['nid']};
    $node->vid = $values->{$field->aliases['vid']};

    // Some formatters need to behave differently depending on the build_mode
    // (for instance: preview), so we provide one.
    $node->build_mode = NODE_BUILD_NORMAL;

    // Render items.
    $formatter_name = $options['format'];
    if ($items && ($formatter = _content_get_formatter($formatter_name, $content_field['type']))) {
      $rendered = array();
      if (content_handle('formatter', 'multiple values', $formatter) == CONTENT_HANDLE_CORE) {

        // Single-value formatter.
        $n = 0;
        foreach ($items as $item) {
          $output = content_format($content_field, $item, $formatter_name, $node);
          if (!empty($output)) {
            $rendered[++$n] = $field
              ->render_link($output, (object) array(
              'nid' => $nid,
            ));
          }
        }
      }
      else {

        // Multiple values formatter.
        $output = content_format($content_field, $items, $formatter_name, $values);
        if (!empty($output)) {
          $rendered[++$n] = $field
            ->render_link($output, (object) array(
            'nid' => $nid,
          ));
        }
      }
      if (count($rendered) > 1) {

        // TODO: could we use generic field display ?

        //return theme('content_view_multiple_field', $rendered, $content_field, $values);
        return $rendered;
      }
      elseif ($rendered) {
        return $rendered[1];
      }
    }
  }
  return '';
}

/**
 * Takes each field from a row object and renders the field as determined by the field's theme.
 *
 * @param $view
 *    View the row belongs to
 * @param $row
 *   Row object
 * @return array
 *   Object containing all the raw and rendered fields
 */
function _views_geojson_render_fields($view, $row, $index) {
  $excluded_fields = array();
  $feature = array(
    'type' => 'Feature',
  );
  $data_source = $view->style_plugin->options['data_source'];
  $field_ids = array_keys($view->field);

  // Pre-render fields to handle those rewritten with tokens
  foreach ($view->field as $field_idx => $field) {
    $field
      ->advanced_render($row);
  }
  switch ($data_source['value']) {
    case 'latlon':
      $options = array(
        'latitude',
        'longitude',
      );
      $latitude = NULL;
      $longitude = NULL;
      foreach ($view->field as $field_idx => $field) {
        foreach ($options as $option) {
          if ($data_source[$option] == $field_idx) {
            ${$option} = $field
              ->advanced_render($row);
            $excluded_fields[] = $field_idx;
          }
        }
      }
      if (!empty($latitude) && !empty($longitude)) {
        $feature['geometry'] = array(
          'type' => 'Point',
          'coordinates' => array(
            floatval($longitude),
            floatval($latitude),
          ),
        );
      }
      break;
    case 'geofield':
      foreach ($view->field as $field_idx => $field) {
        if ($data_source['geofield'] == $field_idx) {
          $geofield = $view->style_plugin
            ->get_field_value($view->row_index, $field_idx);
          $geofield = $geofield[0]['wkt'];
          $view->row_index = $index;
          $excluded_fields[] = $field_idx;
        }
      }
      if (!empty($geofield)) {
        geophp_load();
        $json = geoPHP::load($geofield, 'wkt');
        if (is_object($json)) {
          $feature['geometry'] = json_decode($json
            ->out('json'));
        }
      }
      break;
    case 'wkt':
      foreach ($view->field as $field_idx => $field) {
        if ($data_source['wkt'] == $field_idx) {
          $wkt = $field
            ->advanced_render($row);
          $excluded_fields[] = $field_idx;
        }
      }
      if (!empty($wkt)) {
        geophp_load();
        $json = geoPHP::load($wkt, 'wkt');
        if (is_object($json)) {
          $feature['geometry'] = json_decode($json
            ->out('json'));
        }
      }
      break;
  }

  // Only add features with geometry data
  if (empty($feature['geometry'])) {
    return;
  }

  // Add the name and description attributes
  // as chosen through interface
  if ($data_source['name_field']) {
    foreach ($view->field as $field_idx => $field) {
      if ($data_source['name_field'] == $field_idx) {
        $name_field = $field
          ->advanced_render($row);
        $excluded_fields[] = $field_idx;
      }
    }
    $feature['properties']['name'] = $name_field;
  }
  else {
    $feature['properties']['name'] = '';
  }
  if ($data_source['description_field']) {
    foreach ($view->field as $field_idx => $field) {
      if ($data_source['description_field'] == $field_idx) {
        $description_field = $field
          ->advanced_render($row);
        $excluded_fields[] = $field_idx;
      }
      if ($data_source['description_field'] === '#node') {
        $view_mode = $data_source['view_mode'] ? $data_source['view_mode'] : 'full';
        $description_field = drupal_render(node_view(node_load($row->nid), $view_mode));
      }
    }
    $feature['properties']['description'] = $description_field;
  }
  else {
    $feature['properties']['description'] = '';
  }

  // Fill in attributes that are not:
  // - Coordinate fields
  // - Name/description (already processed)
  // - Views "excluded" fields
  foreach ($field_ids as $id) {
    $field = $view->field[$id];
    if (!in_array($id, $excluded_fields, TRUE) && !$field->options['exclude']) {
      $value_rendered = $field
        ->advanced_render($row);
      $feature['properties'][$id] = is_numeric($value_rendered) ? floatval($value_rendered) : $value_rendered;
    }
  }
  return $feature;
}

/**
 * Encodes GeoJSON in a pretty-printed fashion.
 */
function _views_geojson_encode_formatted($v, $depth = 0) {
  $base_indent = '&nbsp;&nbsp;';
  $eol = '<br />';
  $indent = str_repeat($base_indent, $depth);

  // This is based on the drupal_to_js() function.
  switch (gettype($v)) {
    case 'boolean':

      // Lowercase is necessary!
      return $v ? 'true' : 'false';
    case 'integer':
    case 'double':
      return $v;
    case 'resource':
    case 'string':
      $search = array(
        '"',
        chr(92),
        chr(8),
        chr(12),
        chr(13) . chr(10),
        chr(10),
        chr(13),
        chr(9),
      );
      $replace = array(
        '\\"',
        '\\',
        '\\b',
        '\\f',
        '\\n',
        '\\n',
        '\\r',
        '\\t',
      );
      $output = str_replace($search, $replace, $v);

      /* *
            $output = str_replace(array("\r", "\n", "<", ">", "&"),
                                 array('\r', '\n', '\x3c', '\x3e', '\x26'),
                                 addslashes($output));
      /* */
      return '"' . check_plain($output) . '"';
    case 'array':

      // Arrays in JSON can't be associative.  If the array is empty or if it
      // has sequential whole number keys starting with 0, it's not associative
      // so we can go ahead and convert it as an array.
      if (empty($v) || array_keys($v) === range(0, sizeof($v) - 1)) {
        $output = array();
        foreach ($v as $val) {
          $output[] = $indent . $base_indent . _views_geojson_encode_formatted($val, $depth + 1);
        }
        return '[' . (!empty($output) ? $eol . implode(',' . $eol, $output) . $eol . $indent : '') . ']';
      }

    // Otherwise, fall through to convert the array as an object.
    case 'object':
      $output = array();
      foreach ($v as $key => $val) {
        $output[] = $indent . $base_indent . _views_geojson_encode_formatted(strval($key)) . ' : ' . _views_geojson_encode_formatted($val, $depth + 1);
      }
      return '{' . (!empty($output) ? $eol . implode(',' . $eol, $output) . $eol . $indent : '') . '}';
    default:
      return 'null';
  }
}

Functions

Namesort descending Description
views_geojson_views_api Implements hook_views_api().
_views_geojson_encode_formatted Encodes GeoJSON in a pretty-printed fashion.
_views_geojson_render_fields Takes each field from a row object and renders the field as determined by the field's theme.
_views_geojson_render_multiple_field We almost duplicate the content_handler_field_multiple::render function to get the multiple rendered field values in an array