This is the first of two patches that adds support for the feeds module. http://drupal.org/project/feeds

This patch adds geo_field support so that users can map spatial data to geo_field cck fields.
I'm working on a patch (hopefully done tonight) that will add a shapefile parser so users can create feeds from shapefiles.

CommentFileSizeAuthor
#2 GeoShapefileParser.inc6.44 KBphayes
#1 feeds2.patch1.27 KBphayes
feeds1.patch1.79 KBphayes

Comments

phayes’s picture

StatusFileSize
new1.27 KB

Attached is the second patch.

This one adds support for using shapefiles as a parser for feeds. So using this we can import nodes using data from a shapefile. Very very cool.

The second file should be placed at /includes/feeds/

phayes’s picture

StatusFileSize
new6.44 KB

And I forgot to include the second file....

phayes’s picture

One final note. You'll note that i've replicated some of the functionality of geo_shp2sql . I thought it better to start this patch mainly as an addition to current code, without a lot of modification. If you all think it's a good idea, I will modify this code as well as geo_shp2sql so that there is less duplication.

summit’s picture

Subscribing, could this also be used for http://drupal.org/project/openlayers ?
greetings, Martijn

phayes’s picture

Status: Needs review » Fixed

Okay. I've rolled this out into a new module: http://drupal.org/project/spatial

This will work for both geo fields and openlayers_wkt fields. So no need to do this in geo anymore.

allie micka’s picture

Status: Fixed » Needs work

I'm still going to mark this as "needs work" - native feeds integration is still valuable to Geo, but it must be written using the API. Otherwise, resulting data will not be recognized by Views, fields or other relationships. Meanwhile, this parsing is still necessary for geo_data; so why maintain the same code in multiple places?

Thanks!

phayes’s picture

I had to hack around the shp2sql converter quite a bit to get it to work. But I would like to combine them back into one when I have the time. Had already created an issue for it: http://drupal.org/node/696094

jerdavis’s picture

I've been meaning to roll this back in as well in some way to get this ball rolling. This makes lat/lon to geo available as a mapping option for feeds

// $Id:$

/**
 * @file
 * On behalf implementation of Feeds mapping API for geo.module (CCK).
 */

/**
 * Implementation of hook_feeds_node_processor_targets_alter()
 */
function geo_feeds_node_processor_targets_alter($targets, $content_type) {
  $info = content_types($content_type);
  $fields = array();
  if (isset($info['fields']) && count($info['fields'])) {
    foreach ($info['fields'] as $field_name => $field) {
      if (in_array($field['type'], array('geo'))) {
        $name = isset($field['widget']['label']) ? $field['widget']['label'] : $field_name;
        $targets[$field_name.'#lat'] = array(
          'name' => $name . ' (' . t('lat').')',
          'callback' => 'geo_feeds_set_target',
          'description' => t('The URL for the CCK !name field of the node.', array('!name' => $name)),
        );
        $targets[$field_name.'#lon'] = array(
          'name' => $name . ' (' . t('lon').')',
          'callback' => 'geo_feeds_set_target',
          'description' => t('The title for the CCK !name field of the node.', array('!name' => $name)),
        );
      }
    }
  }
}

/**
 * Implementation of hook_feeds_set_target().
 */
function geo_feeds_set_target($node, $target, $value) {
  list($field_name, $sub_field) = split('#', $target);

  $field_data = isset($node->$field_name) ? $node->$field_name : array();

  if(!is_array($value)) $value = array($value);
  $i = 0;
  foreach ($value as $v) {
    $field_data[$i][$sub_field] = $v;

    // once we have both lat and lon, add as point
    if (($lat = $field_data[$i]['lat']) && ($lon = $field_data[$i]['lon'])) {
      $field_data[$i]['geo'] = 'POINT('. $lon .' '. $lat .')';
    }
    $i++;
  }
  $node->$field_name = $field_data;
}

Putting this up here for review/discussion/use/combining in the interim.

phayes’s picture

Below is a full file for ready to go for feeds integration. Name to geo_field.feeds.inc and include.

// $Id $

/**
 * Implementation of feeds_node_processor_targets_alter().
 *
 * Enable mapping to geo_field for feeds
 */
function geo_field_feeds_node_processor_targets_alter(&$targets, $content_type) {
  $info = content_types($content_type);
  $fields = array();
  if (isset($info['fields']) && count($info['fields'])) {
    foreach ($info['fields'] as $field_name => $field) {
      if ($field['type'] == 'geo') {
        $fields[$field_name.'#wkt'] = array(
        'name' => $name . ' (' . t('WKT').')',
        'callback' => 'geo_field_feeds_set_target',
        'description' => t('The URL for the CCK !name field of the node.', array('!name' => $name)),
      );
      $targets[$field_name.'#lat'] = array(
        'name' => $name . ' (' . t('lat').')',
        'callback' => 'geo_field_feeds_set_target',
        'description' => t('The URL for the CCK !name field of the node.', array('!name' => $name)),
      );
      $targets[$field_name.'#lon'] = array(
        'name' => $name . ' (' . t('lon').')',
        'callback' => 'geo_field_feeds_set_target',
        'description' => t('The title for the CCK !name field of the node.', array('!name' => $name)),
      );
    }
  }
  
}

/**
 * Set Feeds Target
 * 
 * Callback for mapping. Here we map feeds data to the actual field of the node.
 */
function geo_field_feeds_set_target(&$node, $target, $value) {
  list($field_name, $sub_field) = split('#', $target);
  
  // Load the field from the node
  $field = isset($node->$field_name) ? $node->$field_name : array();
  
  
  if(!is_array($value)) $value = array($value);
  
  // Handle multiple value fields.
  $i = 0;
  foreach ($value as $v) {
    if (!is_array($v) && !is_object($v)) {
      $field[$i]['geo'] = $v;
    }
    $i++;
  }
  
  $i = 0;
  foreach ($value as $v) {
    if ($sub_field == 'lat' || $sub_field == 'lon') {
      // We are dealing with lat / lon
      $field_data[$i][$sub_field] = $v;
  
      // once we have both lat and lon, add as point
      if (($lat = $field_data[$i]['lat']) && ($lon = $field_data[$i]['lon'])) {
        $field_data[$i]['geo'] = 'POINT('. $lon .' '. $lat .')';
      }
    }
    elseif ($sub_field == 'wkt'){
      // We are dealing with wkt
      $field_data[$i]['geo'] = $v;
    }
    
    $i++;
  }
  

  $node->$field_name = $field;
}

Also, i've removed geo fields feeds integration in Spatial Import module, so there will be no conflict.

steinmb’s picture

Very very useful, but have not yet been able to get it working. I think we need to roll a complete patch against head so it is easier to review and test. I must be missing a few steps:

#9 contain the code that goes in geo/modules/geo_field/geo_field.feeds.inc ?
If this is correct, where is code where we include this function? geo/modules/geo_field/geo_field.module?

phayes’s picture

steinmb,

Correct, you just need to do a module_load_include somewhere in geo_field.module, likely near the start, and likely wrapped in a module_exists function.

jordanderson’s picture

I just want to make sure I understand what this does. I have a CSV file with data that includes latitude and longitude columns. Will the patch and/or snippet in #9 create a feeds plug-in that will allow me to map those fields to a spatial field?

I included #9 as indicated (I think), but the spatial field is not showing up as a possible target when I set up the import mapping. Do I first need to apply the attached patch(es)?

I have added the spatial module linked in #5 to my site. Maybe I need to remove that to get #9 working?

Thanks.

phayes’s picture

Someone needs to re-roll this patch as feeds API has changed a littla bit in the intervening time.

jonathan_hunt’s picture

Version: » 6.x-1.0-alpha5

Here's a modified version that works for me, but I'm only importing lat and lon. Main change is that geo_value() expects data as an array of lat and lon.

<?php
// $Id $
/**
 * @file
 * Allow Feeds data to set a Geo field.
 * From http://drupal.org/node/667080#comment-2574662.
 */

/**
 * Implements feeds_node_processor_targets_alter().
 *
 * Enable mapping to geo_field for feeds
 */
function geo_field_feeds_node_processor_targets_alter(&$targets, $content_type) {
  $info = content_types($content_type);
  $fields = array();
  if (isset($info['fields']) && count($info['fields'])) {
    foreach ($info['fields'] as $field_name => $field) {
      if ($field['type'] == 'geo') {
        $targets[$field_name .'#wkt'] = array(
          'name' => $name . ' (' . t('WKT') .')',
          'callback' => 'geo_field_feeds_set_target',
          'description' => t('The URL for the CCK !name field of the node.', array('!name' => $name)),
        );
        $targets[$field_name .'#lat'] = array(
          'name' => $name . ' (' . t('lat') .')',
          'callback' => 'geo_field_feeds_set_target',
          'description' => t('The URL for the CCK !name field of the node.', array('!name' => $name)),
        );
        $targets[$field_name .'#lon'] = array(
          'name' => $name .' (' . t('lon') .')',
          'callback' => 'geo_field_feeds_set_target',
          'description' => t('The title for the CCK !name field of the node.', array('!name' => $name)),
        );
      }
    }
  }
}

/**
 * Set Feeds Target, invoked once each for lat and lon fields.
 *
 * Callback for mapping. Here we map feeds data to the actual field of the node.
 */
function geo_field_feeds_set_target(&$node, $target, $value) {
  list($field_name, $sub_field) = split('#', $target);

  // Load the field from the node
  $field = isset($node->$field_name) ? $node->$field_name : array();

  if(!is_array($value)) {
    $value = array($value);
  }

  $i = 0;
  // Grab existing field data, as we may be gathering data from multiple invocations for lat lon.
  $field_data = $node->$field_name;
  foreach ($value as $v) {
    if ($sub_field == 'lat' || $sub_field == 'lon') {
      // We are dealing with lat / lon
      $field_data[$i][$sub_field] = $v;

      // Once we have both lat and lon, add as point
      if (($lat = $field_data[$i]['lat']) && ($lon = $field_data[$i]['lon'])) {
        //$field_data[$i]['geo'] = 'POINT('. $lon .' '. $lat .')';
        // geo_value() expects data as an array of lat and lon.
        $field_data[$i]['geo'] = array('lat' => $lat, 'lon' => $lon);
      }
    }
    elseif ($sub_field == 'wkt'){
      // We are dealing with wkt
      $field_data[$i]['geo'] = $v;
    }
    $i++;
  }

  if (!empty($field_data)) {
    $node->$field_name = $field_data;
  }
}
steinmb’s picture

Ah, thanx Jonathan :)

--
Best regards from Norway, not a bad place to Kayak.