<?php
#
# buffer_select.php (c) 2007 Dan "Ducky" Little
# Written for the GeoMOOSE project, sponsored by the OpenMNND Organization
#

# Include GeoMOOSE PHP Utility Function Library
include('Configuration.php');
include('geomoose_php_util.php');

# Make the PHP Script Shut Up, but still give something useful if there
# is a legit error or parsing bug.
# error_reporting(E_ERROR | E_PARSE);

# Get in the information from the request
$coordinates = $_REQUEST['coords'];
$title = $_REQUEST['title'];
$shapeType = $_REQUEST['shape'];
$buffer = $_REQUEST['buffer'];
$selectLayer = $_REQUEST['select_layer'];
$qLayer = $_REQUEST['query_layer'];
$output = $_REQUEST['output'];
$identifyMap = $_REQUEST['map'];
$selectMap = $_REQUEST['select'];

# Check the CGI for the layer/column Delimiter,
# If it's not found, then use the default.
$delim = $_REQUEST['delim'];
if(!isset($delim)) {
	$delim = $DEFAULT_DELIM;
}

if(!isset($title)) {
	$title = 'My Selection';
}

# This splits the input layer sting into the Layer Name and the Column Containing the ID's for the layer
list($selectLayer, $queryColumn) = explode($delim, $selectLayer);
list($qLayer, $queryColumn) = explode($delim, $qLayer);

# This checks for the existance of the select mapfile.
# If it does not exist the application quits with an error message.
if(!file_exists($selectMap)) {
	appError('Could not find: '.$selectMap);
}

# Check for some vitals
if(!isset($selectLayer)) {
	appError('Layer value is not set or invalid: '.$selectLayer);
}

if(!isset($qLayer)) {
	appError('Query Layer value is not set or invalid: '.$qLayer);
}

if(!isset($output)) {
	$output = $defaultOutput;
}

if(!isset($identifyMap)) {
	$identifyMap = '/ms4w/apps/geomoose/maps_countydemo/identify.map';
}


# Unique Id, the Process Number combined with the UNIX time.
$id = getmypid().time();


# This checks for the existance of the select mapfile.
# If it does not exist the application quits with an error message.
if(!file_exists($DRAWING_MAP)) {
	appError('Could not find: '.$DRAWING_MAP);
}

if(!isset($coordinates)) {
	appError('Coordinates not set! ('.$coordinates.')');
}

if(!isset($output)) {
	$output = 'xml';
}

# This checks to see what "shape" is being passed to the script to query against.
$shapeFileType = MS_SHP_POLYGON;
if($shapeType == "point") {
	$shapeType = MS_SHAPE_POINT;
	$shapeFileType = MS_SHP_POINT;
} elseif($shapeType == 'circle') {
} elseif($shapeType == 'line') {
	$shapeType = MS_SHAPE_LINE;
	if(empty($buffer)) {
		$buffer = 1;	# Makes the line work if there is no buffer specified or the buffer = 0
	}
} else {
	$shapeType = MS_SHAPE_POLYGON;
}

# Convert the coordinates to a new shape object
# This takes the coordinates from the CGI and adds them to a Shape for querying.
$shape = ms_newShapeObj($shapeType);
$coords = array();
$coords = explode(' ', $coordinates);
$line = ms_newLineObj();
foreach ($coords as $coord) {
	list($x, $y) = explode(',',$coord);
	$line->addXY($x,$y);
}
$shape->add($line);	# This adds the line with all the coordinates from the CGI
#$shape->setBounds();	# This initalizes the shape.

# Search a map for the shapes

# Set up the query to perform the select, with the buffer and all!
# If the Map does not open correctly, error out.

$queryMap = ms_newMapObj($selectMap) or appError('Could not open mapfile: '.$selectMap."\n".'Please verify the Map file is valid.');

# This section of code searches for the query layer.
# If a layer is not the query layer, it is turned off so that errorneous/extraneous results are not
# also returned.  This also helps speed queries so that multiple large datasets are not simultaneously queried.
$queryLayerFound = false;	# Set an error condition in case we do not find the layer
for($l = 0; $l < $queryMap->{numlayers}; $l++) {
	$queryLayer = $queryMap->getLayer($l);
	$queryLayer->set('status', MS_OFF);		# Turn the layer off
	if($queryLayer->{name} == $selectLayer) {		# If it's the layer we're trying to search...
		$queryLayer->set('status',MS_ON);	# Turn it on!
		$queryLayerFound = true;		# "Unset" the Error Condition
	}
}

if(!$queryLayerFound) {	# If they layer is not found in the mapfile, error out.
	appError("The Query Layer '$selectLayer' could not be found in the mapfile ($selectMap).");
}
# Perform the Query.
$queryResultCheck = $queryMap->queryByShape($shape);
if($queryResultCheck == MS_FAILURE) {		# If the query fails, error out.
	# Check to see if the Map has any pointers to an empty file
	$empty = $queryMap->web->empty;
	# If it's set then show that HTML instead. :D
	if(isset($empty) and $empty != '') {
		readfile($empty);
		exit(0);
	} else {
		appError('Query Failed to return any results!');
	}
}

$queryResultShape = null;

$shapesToBuffer = getShapesFromLayer($queryMap, $selectLayer, $shape);

if(sizeof($shapesToBuffer) < 0) {
	appError('Failed to select any shapes from layer: '.$selectLayer);
}



$queryResultShape = false;
for($s = 0; $s < sizeof($shapesToBuffer); $s++) {
#	for($fl = 0; $fl < $shapesToBuffer[$s]->numlines; $fl++) {
#		$queryResultShape->add($shapesToBuffer[$s]->line($fl));
#	}

	if($queryResultShape) {
		$queryResultShape = $queryResultShape->union_geos($shapesToBuffer[$s]);
	} else {
		$queryResultShape = $shapesToBuffer[$s];
	}
}
#print $shapesToBuffer[0]->toWKT();

if(isset($buffer)) {
	# This is a bit of a hack, but allows for the selection to work for
	# lines and points.
	if($buffer == 0) {
		$buffer = .0001;
	}
	# Mapscript using GEOS to bufffer the queryResultShape
	$queryResultShape = $queryResultShape->buffer($buffer);
	# Check to make sure the function returned a valid queryResultShape, if not, error.
	if(!isset($queryResultShape)) {
		appError("Buffering Failed. Buffer size: $buffer. Verify mapscript was linked with GEOS and that the buffer is a positive-real number.");
	}
}




$resultShapes = getShapesFromLayer($queryMap, $qLayer, $queryResultShape);
$shape = $queryResultShape;

$resultContents = array();

$queryLayerObj = $queryMap->getLayerByName($qLayer);
$resultsShapeType = -1;

# Get the layer type and set it
$layerType = $queryLayerObj->{type};
$drawLayer = 'SelectedPolygon';
if($layerType == MS_LAYER_POINT) {
	$resultsShapeType = MS_SHP_POINT;
	$drawLayer = 'SelectedPoint';
} else { # Assume it's a polygon, eh? Okay.
	$resultsShapeType = MS_SHP_POLYGON;
}


# Process the query template from the mapfile and return it to the user
# This way the results are displayed exactly as they are definied in the mapfile.
$queryMap->getLayerByName($selectLayer)->set('status',MS_OFF);
$queryMap->getLayerByName($qLayer)->set('status',MS_ON);
$queryMap->queryByShape($shape);
$template = $queryMap->processQueryTemplate(array(), array());

$template = str_replace('[QUERY_ID]', $id,$template);

# Get the "selected" list
$queryColumns = array();	# Hash table to hold the column names.
$queryLayerObj->queryByShape($shape);
$queryLayerObj->open();				# Open the layer for operation.
for($item = 0; $item < $queryLayerObj->{numitems}; $item++) {
	$queryColumns{$queryLayerObj->getItem($item)} = $item;
}
if($queryLayerObj->getNumResults() > 0) {  # Check to make sure we have SOME results
	for($result = 0; $result < $queryLayerObj->getNumResults(); $result++) {
		$queryResult = $queryLayerObj->getResult($result);
		# Get the feature from that result.
		# I left getFeature in here, as getShape will be deprecated for getFeature
		#$queryFeature = $queryLayerObj->getFeature($queryResult->{shapeindex}, $queryResult->{tileindex});
		$queryFeature = $queryLayerObj->getShape($queryResult->{tileindex}, $queryResult->{shapeindex});
		# Here's where the array from before becomes very handy.  The column *name* is passed
		# into the CGI, so we need to result the shapefile-column index and the name,
		# which is query %queryColumns does! So now, we can just pull the ID column from the object.
		$v = $queryFeature->{values}[$queryColumn];
		# And put that ID onto the stack of results...
		array_push($resultContents, $v);
	}
}
$queryLayerObj->close(); # Close the layer


# Create a new Coordinate string based on any potential buffering.
# This is needed because after the object has been buffered it will have a new coordinate string.
# This information is passed back to the GeoMOOSE client so that it can set the "mapshape" parameter
# which displays the layer highlighting.
$coordString = '';
for($line = 0; $line < $shape->{numlines}; $line++) {
	$lineObj = $shape->line($line);
	for($point = 0; $point < $lineObj->{numpoints}; $point++) {
		$pointObj = $lineObj->point($point);
		$coordString = $coordString.$pointObj->{x}.' '.$pointObj->{y}.' ';
	}
}

# Store some useful information in a Shapefile

#
# Create the shapefile from the selection area
#

# Create the necessary dbase files...
$selectionId = $id.'_selection';
$bufferedSelectionId = $id.'_buffered';

# Write out the selection shape
writeShapeFile($TEMP_DIRECTORY.'/'.$id, MS_SHP_POLYGON, array($shape), 'selection');
# And the shapes selected from the selection shapes
#print 'N shapes: '.sizeof($resultShapes);
writeShapeFile($TEMP_DIRECTORY.'/'.$selectionId, $resultsShapeType, $resultShapes, 'shape');
# And the resulting shapes
writeShapeFile($TEMP_DIRECTORY.'/'.$bufferedSelectionId, $shapeFileType, $shapesToBuffer,'buffered_selection');

# Create a Mapfile for displaying the correct information
$mapfileTemplate = file_get_contents($DRAWING_MAP);
$majorKeys = array();
$majorKeys['TEMPDIR'] = $TEMP_DIRECTORY;	# Tells the mapfile where the shapes are stored
$majorKeys['ID'] = $id;
$majorKeys['SELECTIONID'] = $selectionId;
$majorKeys['BUFFEREDSELECTIONID'] = $bufferedSelectionId;
foreach($majorKeys as $key=>$value) {
	$mapfileTemplate = str_replace('%'.$key.'%', $value, $mapfileTemplate);
}

$outputMapfileName = $TEMP_DIRECTORY.'/'.$id.'.map';
$outputMapfile = fopen($outputMapfileName, "w");
fputs($outputMapfile, $mapfileTemplate);
fclose($outputMapfile);

$activeLayers = array($drawLayer, 'BufferedSelection', 'SelectedArea');

# Give the client the ID back so it can reference it later
if($output == 'xml') {
	header('Content-type: text/xml');
	# Return the XML that tells GeoMOOSE what to do!
	#
	# By passing "mapbook fragments" back to GeoMOOSE we can change the behavior and
	# definition of the maps being displayed.  $layer contains the mapbook name of the
	# layer we were selecting-against.  The "<param.../>" line will set the mapshape parameter for
	# the layer we selected against.
	print "<return>";
	print "<mapbook>";
	print "<selection title='$title' id='$id'/>";
	printf("<map title='Highlight' default='true'>", $layer);
	print "	<file>$DRAWING_MAP</file>";
	print "	<param name='ID' value='$id'/>";
	for($i = 0; $i < count($activeLayers); $i++) {
		printf("<layer name='%s' title='Highlight %d' default='true'/>", $activeLayers[$i], $i);
	}


    	#<layer name="city_labels" title="Labels" default="true"/>
	#<layer name="city_poly" title="Borders" default="true" locked="true"/>
	print "</map>";

	# This takes the ID's from the results stack and puts it inside the layer.
	foreach ($resultContents as $id) {
		#	print "\t\t<selected id='$id'/>\n";
		printf("	<selected id='%s'/>", $id);
	}

	print "\t</map>";
	print "</mapbook>";

	# Print some header information.
	print '<results title="Select Results">';
	print '		<header><col value="Selected Objects:"/></header>';
	print "		<row><col value='".count($resultContents)."'/></row>";
	print '</results>';


	print $template;

	print "</return>";	# End the XML Page.
} elseif($output == 'html') {
	header('Content-type: text/html');
	## HTML OUTPUT
	# This is a HTML/XML trick that uses XML namespaces in order to embed a GeoMOOSE
	# mapbook into a HTML document.
	print '<html xmlns:moose="http://geomoose.org/">';
	print "<head>\n";
	print "<moose:mapbook>\n";
	print "<moose:selection title='$title' id='$id'/>";
	#	print "<moose:map title="'.$layer.'" default="true">\n";
	printf("<moose:map title='Highlight' default='true'>\n");
	printf("	<moose:file>%s</moose:file>", $outputMapfileName); #$DRAWING_MAP);
	printf("	<moose:param name='ID' value='%s'/>", $id);
	printf("	<moose:param name='SELECTIONID' value='%s'/>", $selectionId);
	printf("	<moose:param name='BUFFEREDSELECTIONID' value='%s'/>", $bufferedSelectionId);

	for($i = 0; $i < count($activeLayers); $i++) {
		printf("<moose:layer name='%s' title='Highlight %d' default='true'/>", $activeLayers[$i], $i);
	}

	foreach ($resultContents as $id) {
		printf("	<moose:selected id='%s'/>\n", $id);
	}
	print "</moose:map>\n";
	print "</moose:mapbook>\n";
	print "</head>";
	print "<body>";
	print "<![CDATA[";
	print $template;
	print "]]>";
	print "</body>";
	print "</html>";
} else {
	appError('Unrecognized output option: '.$output);
}



?>
