Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

7.x 2.x #2

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions geospatial.info
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
name = GeoSpatial
description = An adaption of Affinity Bridge's spatial7 module that provides a geocoder handler for shapefiles.
package = Geo Spatial Tools
description = "Tools for processing spatial files. Includes a geocoder handler."
core = 7.x
dependencies[] = ogr2ogr
dependencies[] = geocoder
dependencies[] = file
version = 7.x-2.x-dev
49 changes: 48 additions & 1 deletion geospatial.module
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,56 @@

/**
* Implements hook_ctools_plugin_directory
*
*
* Tell ctools where to find plugins.
*/
function geospatial_ctools_plugin_directory($owner, $plugin_type) {
return 'plugins/' . $plugin_type;
}

/**
* Implements hook_permission().
*/
function geospatial_permission() {
return array(
'administer geospatial' => array(
'title' => t('Administer GeoSpatial'),
'description' => t('Access administration pages and settings related to the GeoSpatial module.'),
),
);
}

/**
* Implement hook_menu().
*/
function geospatial_menu() {
$items = array();

$items['admin/config/content/geospatial'] = array(
'title' => 'GeoSpatial',
'description' => 'Configure GeoSpatial settings and ogr2ogr options.',
'page callback' => 'drupal_get_form',
'page arguments' => array('geospatial_admin_settings'),
'access arguments' => array('administer geospatial'),
'file' => 'includes/geospatial.admin.inc',
);

return $items;
}

/**
* Get the ogr2ogr path...
*/
function geospatial_get_ogr2ogr_bin_path() {
// If the ogr2ogr variable hasn't been configured, attempt to find it with `which`.
$path = variable_get('geospatial_ogr2ogr_path', exec('which ogr2ogr'));
return $path;
}

/**
* Public wrapper function around the spatial file parsing function
*/
function geospatial_parse_wkt_from_spatial_file($uri) {
module_load_include('inc', 'geospatial', 'includes/spatial_file');
return _geospatial_parse_wkt_from_spatial_file($uri);
}
42 changes: 0 additions & 42 deletions geospatial_shapefile.class.inc

This file was deleted.

28 changes: 28 additions & 0 deletions includes/geospatial.admin.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php
/**
* @file
* geospatial.admin.inc
*/

/**
* GeoSpatial Admin settings form.
*/
function geospatial_admin_settings() {
$form = array();

$form['ogr2ogr'] = array(
'#type' => 'fieldset',
'#title' => t('ogr2ogr'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
);

$form['ogr2ogr']['geospatial_ogr2ogr_path'] = array(
'#type' => 'textfield',
'#title' => t('Path'),
'#description' => t('GeoSpatial will attempt to locate the ogr2ogr binary automatically, however the process of doing so is not reliable. Please set the value here.'),
'#default_value' => variable_get('geospatial_ogr2ogr_path', exec('which ogr2ogr')),
);

return system_settings_form($form);
}
132 changes: 132 additions & 0 deletions includes/ogr2ogr.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
<?php
/**
* @file cedar_gi.ogr2ogr.inc an abstraction layer for cedar_gi's ogr2ogr functionality
*
* @todo Restore bulk API compatibility. It was lost when adding the ability to process multi layered zips.
*/
class ogr2ogr {
// private $tmpdir;
// private $type;
// private $pointer;
// public $csv;

function __construct($uri) {
// Get ogr2ogr bin
$this->ogr2ogr_bin = geospatial_get_ogr2ogr_bin_path();

// Create tmp directory
$tmpdir = tempnam(sys_get_temp_dir(), 'mapshape');
unlink($tmpdir); // If this tmpdir exists already for some reason... remove it.
mkdir($tmpdir . "/ogr2ogr", 0777, TRUE);
$this->tmpdir = $tmpdir;
$this->uri = $uri;

$this->ogr2ogr_files = array(); // Zip files may contain multiple kmls or shps.
$this->current_file_index = 0;
}

private function extract() {
$zippath = drupal_realpath($this->uri);
$zip = new ArchiverZip($zippath);
$zip->extract($this->tmpdir);
}

private function find_files($type) {
return file_scan_directory($this->tmpdir, "/^.*\.(" . $type . ")$/");
}

function prepare() {
$urlinfo = parse_url($this->uri);
$fileinfo = pathinfo($urlinfo['path']);
$extension = $fileinfo['extension'];

if (in_array($extension, array('zip', 'shpz'))) {
$this->extract();
$shapefiles = $this->find_files('shp');

foreach ($shapefiles as $shp) {
$this->run_ogr2ogr($shp->uri);
}
} elseif ($extension == 'kmz') {
$this->extract();
$kml_files = $this->find_files('kml');

foreach ($kml_files as $kml) {
$this->run_ogr2ogr($kml->uri);
}
} elseif ($extension == 'kml') {
$this->run_ogr2ogr(drupal_realpath($this->uri));
}
}

/**
* Convert a spatial file into a CSV with WKT Geometries, and store it in the ogr2ogr_files property for later.
*/
private function run_ogr2ogr($uri) {
// Figure out what the csv will be called.
$csv_name = 'shape' . count($this->ogr2ogr_files) . '.csv';

// Convert shapefile into a csv file
$command = $this->ogr2ogr_bin . " -f csv -t_srs 'EPSG:4326' -lco GEOMETRY=AS_WKT " . $this->tmpdir . '/ogr2ogr/' . $csv_name . ' ' . $uri;
system(escapeshellcmd($command));

array_push($this->ogr2ogr_files, $this->tmpdir . '/ogr2ogr/' . $csv_name);
}

function open() {
if (file_exists($this->ogr2ogr_files[$this->current_file_index]) && $this->csv = fopen($this->ogr2ogr_files[$this->current_file_index], 'r')) {
return TRUE;
} else {
return FALSE;
}
}

/**
* This method may be used to reopen the current file and seek to the previous location when continuing
* the processing over multiple page loads.
*/
function reopen() {
$was_opened = $this->open();
fseek($this->csv, $this->pointer);

return $was_opened;
}

function fgetcsv() {
$data = fgetcsv($this->csv);
$this->pointer = ftell($this->csv);

if ($data) {
return $data; // In most cases this is as far as we'll get.

} else { // $data returned false.. Attempt to open the next file in the list and return the first line.
$next_file = $this->open(++$this->current_file_index);

if ($next_file) {
// Let's automatically skip the headers.
fgetcsv($this->csv);

// Then return the first line in the next csv.
$data = fgetcsv($this->csv);
$this->pointer = ftell($this->csv);
return $data;

} else {
// There was no next file to open...
return FALSE;
}
}
}

/**
* We use this close() method instead of __destruct() because this class is designed to be able
* to persist through multiple page loads so as to be usable with the bulk API. As such we do not
* want to delete the files generated by this instance until we know we are finished. At that point
* we might as well close the CSV generated by ogr2ogr at the same time as cleaning generated files.
*/
function close() {
@fclose($this->csv);
// Remove all files created by this instance.
file_unmanaged_delete_recursive($this->tmpdir);
}
}
28 changes: 28 additions & 0 deletions includes/spatial_file.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

/**
* Could also look at parsing shapefiles manually: http://en.wikipedia.org/wiki/Shapefile#Shapefile_shape_format_.28.shp.29
* Could also implement Iterator: http://php.net/manual/en/class.iterator.php
*/
function _geospatial_parse_wkt_from_spatial_file($uri) {
module_load_include('inc', 'geospatial', 'includes/ogr2ogr');
$wkt_values = array();

$ogr2ogr = new ogr2ogr($uri);

// Check the file type, extract any relevant files, and run ogr2ogr.
$ogr2ogr->prepare();

// Attempt to open the CSV created by ogr2ogr
if ($ogr2ogr->open()) {
$header = $ogr2ogr->fgetcsv();
$wkt_key = array_search('WKT', $header);

while ($row = $ogr2ogr->fgetcsv()) {
array_push($wkt_values, $row[$wkt_key]);
}
}

$ogr2ogr->close();
return $wkt_values;
}
65 changes: 65 additions & 0 deletions plugins/geocoder_handler/spatial_file.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php
// $Id$

/**
* @file
* Plugin to provide a kml geocoder.
*/

/**
* Plugins are described by creating a $plugin array which will be used
* by the system that includes this file.
*/
$plugin = array(
'title' => t("Spatial file"),
'description' => t('Get the geometry out of a spatially enabled file, such as kml, kmz, or shpz.'),
'callback' => 'geospatial_spatial_file',
'field_types' => array('file'),
'field_callback' => 'geospatial_spatial_file_field',
);

/**
* Process Markup
*/
function geospatial_spatial_file($wkt_features, $options = array()) {
geophp_load();
$wkt = array();

if ($wkt_features) {
// If there is more than one value to save, wrap in GeometryCollection
if (isset($wkt_features[1])) {
$wkt = array('geom' => sprintf('GEOMETRYCOLLECTION(%s)', implode(',', $wkt_features)));
}
else {
$wkt = array('geom' => $wkt_features[0]);
}
}

return geoPHP::load($wkt['geom'], 'wkt');
}

function geospatial_spatial_file_field($field, $field_item) {
if ($field['type'] == 'file') {
if ($field_item['fid']) {
$file = file_load($field_item['fid']);
//$data = file_get_contents($file->uri);
$features = geospatial_spatial_file_get_wkt($file->uri);
//$features = geospatial_file_parse_wkt_features($features);

return geospatial_spatial_file($features);
} else {
//if there's no file we want to put an empty value into the field.
return geospatial_spatial_file(array());
}
} else {
// Something has gone wrong...
}
}

/**
* extract geodata from spatial_file.
*/
function geospatial_spatial_file_get_wkt($spatial_file_uri) {
module_load_include('inc', 'geospatial', 'includes/spatial_file');
return _geospatial_parse_wkt_from_spatial_file($spatial_file_uri);
}
Loading