Copyright (c) 2002, DM Solutions Group Inc.
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
$bFoundAttributes = false;
/*
This file provides functions for finding layer attributes from a MapScript
layer object that is either local, WFS, or WMS with associated WFS.
Processing Logic:
Based on layer connection type and layer type. Only works if layer type
is one of:
MS_LAYER_POINT
MS_LAYER_LINE
MS_LAYER_POLYGON
We find attributes by doing a layer->open() and getting the first shapeObj
then looking at the shapeObj's values array.
Special case for WMS and WFS connection types, we can get the information from
the WFS server (in the WMS case, we try a DescribeLayer request to find out
about associated WFS)
layer types
MS_LAYER_POINT
MS_LAYER_LINE
MS_LAYER_POLYGON
MS_LAYER_RASTER
MS_LAYER_ANNOTATION
MS_LAYER_QUERY
MS_LAYER_CIRCLE
MS_LAYER_GRATICULE
connection types
MS_INLINE
MS_SHAPEFILE
MS_TILED_SHAPEFILE
MS_SDE
MS_OGR
MS_TILED_OGR
MS_POSTGIS
MS_WMS
MS_ORACLESPATIAL
MS_WFS
MS_GRATICULE
MS_MYGIS
*/
function GetLayerAttributes( $oLayer )
{
$aszAttributes = array();
if (//$oLayer->type == MS_LAYER_RASTER &&
$oLayer->connectiontype == MS_WMS)
{
//get the layer name from metadata if possible to avoid the problems
//with context layers being renamed
$szLayerName = $oLayer->getMetaData( "wms_name" );
if ($szLayerName == "")
$szLayerName = $oLayer->name;
// get WFS the connection string
//$szWFSConnection = getWFSConnection( $oLayer->connection, $szLayerName );
$aszInfo = getWFSConnection( $oLayer->connection, $szLayerName );
//$aszInfo['typename'] = $oLayer->getMetaData( "wfs_typename" ); // added by JC
$szWFSConnection = $aszInfo['wfs'];
//$szWFSConnection = $oLayer->getMetaData( "wfs_connection" ); //addd by JC
$szWFSTypeName = "";
$sep = '';
// loop and build typename
foreach( $aszInfo['typename'] as $szTypeName )
{
$szWFSTypeName .= $sep.$szTypeName;
$sep = ',';
}
// check for false (meaning no associated WFS)
if ( $szWFSConnection != "" )
{
$szWFSConnection = fixWFSURL( $szWFSConnection, $szWFSTypeName );
// get the attribute types
$aszAttributes['name'] = $oLayer->name;
$aszAttributes['index'] = $oLayer->index;
$aszAttributes['onlineresource'] = $szWFSConnection;
$aszAttributes['minx'] = "";
$aszAttributes['miny'] = "";
$aszAttributes['maxx'] = "";
$aszAttributes['maxy'] = "";
$aszAttributes['fields'] = getAttributeTypes( $szWFSConnection.
'request=describefeaturetype', $szLayerName );
}
}
elseif ($oLayer->type == MS_LAYER_POINT ||
$oLayer->type == MS_LAYER_LINE ||
$oLayer->type == MS_LAYER_POLYGON )
{
// process according to layer type
switch( $oLayer->connectiontype )
{
case MS_WFS:
//get the layer name from metadata if possible to avoid the problems
//with context layers being renamed
$szLayerName = $oLayer->getMetaData( "wms_name" );
if ($szLayerName == "")
$szLayerName = $oLayer->name;
// record the connection string
$szWFSConnection = $oLayer->connection;
// check the the WFS online resource contains the necessary params
$szWFSConnection = fixWFSURL( $szWFSConnection, $szLayerName );
// get the attribute types
$aszAttributes['name'] = $oLayer->name;
$aszAttributes['index'] = $oLayer->index;
$aszAttributes['onlineresource'] = $szWFSConnection;
$aszAttributes['minx'] = "";
$aszAttributes['miny'] = "";
$aszAttributes['maxx'] = "";
$aszAttributes['maxy'] = "";
$aszAttributes['fields'] = getAttributeTypes( $szWFSConnection.
'request=describefeaturetype', $szLayerName );
break;
default:
// open the layer
$oLayer->open();
//handle tiled shapefiles correctly, otherwise they don't get
//extents or attributes
if ($oLayer->connectiontype == MS_TILED_SHAPEFILE ||
$oLayer->tileindex != "")
{
//first tile is sufficient, assume that all attributes are the same
//in every tile (which is reasonable)
$nTile = 0;
}
else
{
//use -1 in getShape for non-tiled sources
$nTile = -1;
}
// read the first line of the datasource to get
// name, index, and fields
$oShape = $oLayer->getShape($nTile, 0 );
$aszAttributes['name'] = $oLayer->name;
$aszAttributes['index'] = $oLayer->index;
$oRect = $oShape->bounds;
$aszAttributes['minx'] = $oRect->minx;
$aszAttributes['miny'] = $oRect->miny;
$aszAttributes['maxx'] = $oRect->maxx;
$aszAttributes['maxy'] = $oRect->maxy;
foreach($oShape->values as $key => $value)
{
$aszAttributes['fields'][$key] = "string";
}
break;
}
}
return $aszAttributes;
}
/**
* GetLayerFeatures()
*
* Postcondition: This function returns a two dimensional array of layer
* feature values for either WMS or WFS layers.
*
* @param $oMap Object - The map object to use.
* @param $oLayer Object - The layer object to use.
* @param $aszIncludeAttributes Array - Optional list of attribute names to use.
* @return Array - Two dimensional array of features.
* @desc Returns 2-D array of features.
*/
function GetLayerFeatures( $oMap, $oLayer, $aszIncludeAttributes = array() )
{
// init vars
$aszFeatures = array();
$szLayerName = $oLayer->name;
$szWFSTypeName = '';
if ( $oLayer->connectiontype == MS_WMS )
{
// get the layer name from metadata if possible to avoid the problems
// with context layers being renamed
$szLayerName = $oLayer->getMetaData( "wms_name" );
if ($szLayerName == "")
$szLayerName = $oLayer->name;
// get WFS the connection string
$aszInfo = getWFSConnection( $oLayer->connection, $szLayerName );
$szWFSConnection = $aszInfo['wfs'];
// loop and build typename
if ( isset( $aszInfo['typename'] ) && is_array( $aszInfo['typename'] ) )
{
foreach( $aszInfo['typename'] as $szTypeName )
{
$szWFSTypeName .= $szTypeName.',';
}
}
// remove trailing ','
if ( substr( $szWFSTypeName, -1, 1 ) == ',' )
{
$szWFSTypeName = substr( $szWFSTypeName, 0, -1 );
}
// can only process WMS layer if WFS connection is associated
if ( $szWFSConnection != "" )
{
// fix WFS url as required
$szWFSConnection = fixWFSURL( $szWFSConnection, $szWFSTypeName );
// get the attribute types
$aszAttributes = getAttributeTypes( $szWFSConnection.
'request=describefeaturetype', $szLayerName );
// build list of attrtibutes
$szLayerType = '';
foreach( $aszAttributes as $key=>$value )
{
// get layer type
if ( $key == 'LayerType' )
{
$szLayerType = $value;
break;
}
}
// duplicate the layer
$oCloneLayer = ms_newLayerObj( $oMap, $oLayer );
// reset orig layer name temporarily
$oLayer->set( 'name', $oLayer->name.'tmp_backup' );
// if layer type is WMS then convert to WFS
$oCloneLayer->set( 'connectiontype', MS_WFS );
$oCloneLayer->set( 'connection', $szWFSConnection.'&ttt=1' );
$oCloneLayer->set( 'type', $szLayerType );
}
else
{
// no associated WFS layer so return empty
return array();
}
}
/*
we can really do this on any layer that isn't WMS
elseif ( $oLayer->connectiontype != MS_WFS )
{
// return empty array because not WMS or WFS
echo "here3
";
echo $oLayer->connectiontype."
";
return array();
}
*/
//we want to get all the features for the whole world here
//but this may not be the case in general so this may need
//to be redone to accomodate querying the current map extents
//as an option
/*
//this is supposed to be the optimized version and would remove the
//parseFeatures function, which would be quite slow compared to this,
//at least that's what we hope!
$oRect = ms_newRectObj();
$oRect->setExtent( 10000, 305000, 280000, 619000);
//if the layer or map has a projection then convert our
//lat/lon into that projection before querying
$szProjOut = $oLayer->getProjection();
if ($szProjOut == "")
{
$szProjOut = $oMap->getProjection();
}
if ($szProjOut != "")
{
$oProjIn = ms_newProjectionObj( "init=epsg:4326" );
$oProjOut = ms_newProjectionObj( $szProjOut );
$oRect->project( $oProjIn, $oProjOut );
}
print_r($oRect);
//at this point, $oRect should have the right things in it
$nResults = 0;
if (isset($oCloneLayer))
{
$oCloneLayer->set("template", "blah");
$oCloneLayer->queryByRect($oRect);
$nResults = $oCloneLayer->getNumResults();
}
else
{
$oLayer->set("template", "blah");
$oLayer->queryByRect($oRect);
$nResults = $oLayer->getNumResults();
}
if ($nResults > 0)
{
$oLayer->open();
for( $i=0; $i<$nResults; $i++ )
{
$oResult = $oLayer->getResult($i);
$oShape = $oLayer->getShape($oResult->tileindex, $oResult->shapeindex);
array_push( $aszFeatures, $oShape->values );
}
$oLayer->close();
}
*/
// debug
//$szTmpGML = getGML( $szWFSConnection.'request=getfeature' );
// create the gml file to parse into an array
$szTmpGML = !isset( $oCloneLayer ) ? $oLayer->executeWFSGetfeature() :
$oCloneLayer->executeWFSGetfeature();
// check if the file exists
if ( file_exists( $szTmpGML ) )
{
// read the file, parse it and return it as an array
$aszFeatures = parseFeatures( $szTmpGML, $szLayerName,
$aszIncludeAttributes );
// delete the file
unlink( $szTmpGML );
}
/**/
// delete cloned layer if necessary
if ( isset( $oCloneLayer ) )
{
// restore current layers name
$oLayer->set( 'name', substr( $oLayer->name, 0, -10 ) );
//$oCloneLayer->set( 'status', MS_DELETE );
}
// return the features
return $aszFeatures;
}
/*
* return the SRS value (ESPG:xxxx) for a given layer in a WFS server
* by grabbing the capabilities of the server, then looking for any
* SRS tag (since all layers are in the same SRS :)
*/
function getWFSSRS( $szXmlUrl )
{
// read the xml datasource
if (function_exists( 'file_get_contents' ))
{
$data = file_get_contents( $szXmlUrl );
}
else
{
$data = implode( '', file($szXmlUrl) );
}
if ( stristr( $data, 'type pairs.
*/
function getAttributeTypes( $szXmlUrl, $szLayerName )
{
// int vars
$data = "";
$axReturn = "";
// read the xml datasource
$aszFile = file( $szXmlUrl );
// combine results
if ( is_array( $aszFile ) )
{
foreach( $aszFile as $line )
$data .= $line;
}
// check to see if an error was generated
if ( !is_array( $aszFile ) ||
strpos( strtoupper( $data ), '$val)
{
// process ROWSET
if (strtoupper( $key ) == "SEQUENCE")
{
$ranges = $val;
// each contiguous pair of array entries are the
// lower and upper range for each SEQUENCE definition
$nCount = count($ranges);
for ($i=0; $i < $nCount; $i+=2)
{
$offset = $ranges[$i] + 1;
$len = $ranges[$i + 1] - $offset;
$axReturn = parseElements( array_slice($values, $offset, $len) );
}
}
else
{
// otherwise skip
continue;
}
}
// return
return $axReturn;
// end getAttributeTypes() function
}
/**
* parseElements()
*
* Postcondition: This function parses the given array returns an array of
* attribute->type pairs.
*
* @param axValues Array - Mixed array of XML tags to process.
* @return array - Array of att->type pairs if successful or empty array if not.
* @desc Parses array and returns array of att->type pairs.
*/
function parseElements( $axValues )
{
// init vars
$aszReturn = array();
// loop through each item of the array
$nCount = count( $axValues );
for ( $i=0; $i < $nCount; $i++ )
{
// only process "name" and "type" values
if ( strtoupper( $axValues[$i]["tag"] ) == "ELEMENT" )
{
// add to array
if ( isset( $axValues[$i]["attributes"]["name"] ) &&
$axValues[$i]["attributes"]["type"] )
{
$aszReturn[$axValues[$i]["attributes"]["name"]] =
$axValues[$i]["attributes"]["type"];
}
// check for the layer type
$szLayerType = '';
if ( isset( $axValues[$i]["attributes"]["ref"] ) )
$szLayerType = strtoupper( $axValues[$i]["attributes"]["ref"] );
elseif ( isset( $axValues[$i]["attributes"]["type"] ) &&
strtoupper( substr( $axValues[$i]["attributes"]["type"], 0, 4 ) ) == 'GML:' )
$szLayerType = strtoupper( $axValues[$i]["attributes"]["type"] );
// check for the type
if ( strlen( $szLayerType ) > 0 )
{
// check if poly
if ( strpos( $szLayerType, 'POLYGON' ) !== false )
$aszReturn['LayerType'] = 'MS_LAYER_POLYGON';
elseif ( strpos( $szLayerType, 'BOX' ) !== false )
$aszReturn['LayerType'] = 'MS_LAYER_POLYGON';
elseif ( strpos( $szLayerType, 'LINE' ) !== false )
$aszReturn['LayerType'] = 'MS_LAYER_LINE';
elseif ( strpos( $szLayerType, 'POINT' ) !== false )
$aszReturn['LayerType'] = 'MS_LAYER_POINT';
}
}
}
// return array
return $aszReturn;
// end parseElements() function
}
/**
* getWFSConnection()
*
* Postscript: This function reads the given WMS connection and returns the WFS
* online resource.
*
* @param $szWMSConnection string - WMS connection string.
* @return string - WFS online resource if successful, false if failed.
* @desc Parses given connection string and returns WFS online resource.
*/
function getWFSConnection( $szWMSConnection, $szSelectedLayerName )
{
// get the url to perform a describelayer on
$szUpperCase = strtoupper( $szWMSConnection );
// check last char for ? or &
if ( substr( $szWMSConnection, -1 ) != "?" &&
substr( $szWMSConnection, -1 ) != "&" )
{
// check for ?
if ( strpos( $szWMSConnection, "?" ) === false )
{
$szWMSConnection .= "?";
}
else
$szWMSConnection .= "&";
}
// check for and add VERSION
if ( strpos( $szUpperCase, "VERSION=" ) === false )
$szWMSConnection .= "VERSION=1.1.0&";
// check for and add SERVICE
if ( strpos( $szUpperCase, "SERVICE=" ) === false )
$szWMSConnection .= "SERVICE=WMS&";
// add LAYERS
if ( strpos( $szUpperCase, "LAYERS=" ) === false )
$szWMSConnection .= "LAYERS=".$szSelectedLayerName."&";
// add REQUEST
$szWMSConnection .= "REQUEST=DESCRIBELAYER";
//echo "wms connection = $szWMSConnection
";
// perform a describe layer and return
return getWFSResource( $szWMSConnection );
// end getWFSConnection function
}
/**
* getWFSResource()
*
* Postscript: This function reads the given url and returns the WFS online
* resource.
*
* @param $szURL string - URL to the GML source.
* @return string - WFS online resource if successful, false if failed.
* @desc Parses given url and returns WFS online resource.
*/
function getWFSResource( $szURL )
{
// init vars
$data = "";
$aszReturn = array( 'wfs'=>'', 'typename'=>array() );
// read the xml datasource
if (!function_exists('file_get_contents'))
{
$szXMLFile = implode( "", file($szURL) );
}
else
{
$szXMLFile = file_get_contents( $szURL );
}
// check to see if an error was generated
if ( strpos( strtoupper( $szXMLFile ), '$value )
{
// check for array
if ( is_array( $value ) )
{
$value = lowercaseArrayKeys( $value );
}
// add item to the new array
$axReturn[strtolower( $key )] = $value;
// sanity check
$i++;
if ( $i >= 1000000 )
{
die('Error in lowercaseArrayKeys() 1,000,000 entries was exceeded');
}
}
// return
return $axReturn;
}
/**
* parseFeatures()
*
* Postcondition: This function reads the give and parses it, passing
* the results to an array.
* Precondition: This function assumes the shared resource exists.
*
* @param $szFile string - The file to parse.
* @param $szLayer string - Layer to parse.
* @param $aszAttributes array - list of attributes to return.
* @return boolean - true if successful, false if not;
* @desc Reads and parses given file and passes results to an array.
*
**/
function parseFeatures( $szFile, $szLayer, $aszAttributes = array() )
{
// int vars
$data = "";
$aszReturn = array();
// read the xml datasource
/*
if ( function_exists( 'file_get_contents' ) )
{
// read the file to string
$data = file_get_contents( $szFile );
}
else
{
// read the file to string
$aszFile = file( $szFile );
foreach( $aszFile as $line )
{
// process line
$data .= $line;
}
}
*/
//copy( $szFile, "c:\\test.xml" );
$h = fopen($szFile, "r");
$loop = 0;
do
{
$line = fread( $h, 1024 );
if (strlen($line) == 0)
break;
$data .= $line;
$loop ++;
if ($loop > 100000) break;
}
while(true);
//echo "loop: $loop
";
//echo "szFile : $szFile
\n";
//echo "data :
\n";
//echo htmlentities($data);
// check to see if an error was generated
if ( strpos( strtoupper( $data ), '$val)
{
// process layer name
if ( strtoupper( $key ) == strtoupper( $szLayer ) )
{
$ranges = $val;
// each contiguous pair of array entries are the
// lower and upper range for each layer definition
$nCount = count($ranges);
for ($i=0; $i < $nCount; $i+=2)
{
if (isset($ranges[$i+1]))
{
$offset = $ranges[$i] + 1;
$len = $ranges[$i + 1] - $offset;
$axTmp = parseFeatureElements(
array_slice($values, $offset, $len ), $szLayer, $aszAttributes );
$aszReturn = array_merge( $aszReturn, $axTmp );
}
}
}
else
{
// otherwise skip
continue;
}
}
// return
return $aszReturn;
// end parseQueryResults
}
/**
* parseFeatureElements()
*
* Postcondition: This function parses the given array returns an array of
* values.
*
* @param axValues Array - Mixed array of XML tags to process.
* @param szLayer String - Layer name.
* @param $aszAttributes Array - List of layers to return.
* @return Array - Array of values.
* @desc Parses array and returns in an array.
*/
function parseFeatureElements( $axValues, $szLayer, $aszAttributes = array() )
{
// init vars
$axReturn = array();
$bAllowAll = ( count( $aszAttributes ) <= 0 ) ? true:false;
// loop through each item of the array
$aResult = array();
$nCount = count( $axValues );
$j = 0;
for ( $i=0; $i < $nCount; $i++ )
{
//echo "processing count $i
";
// only process all non "gml..." values
if ( strtoupper( substr( $axValues[$i]["tag"], 0, 3 ) ) != "GML" )
{
// only add attributes that are in the list
if ( $bAllowAll || in_array( $axValues[$i]['tag'], $aszAttributes ) )
{
// add to array
//$aResult[$j]['attribute'] = $axValues[$i]['tag'];
if ( isset( $axValues[$i]['value'] ) )
{
//$aResult[$j]['value'] = $axValues[$i]['value'];
$aResult[$axValues[$i]['tag']] = $axValues[$i]['value'];
}
else
{
$aResult[$axValues[$i]['tag']] = '';
}
$j++;
}
}
}
array_push( $axReturn, $aResult );
// return
return $axReturn;
// end parseElements() function
}
function getGML( $szURL )
{
// build temp file
$szTmpFile = $_SESSION['gszTmpPath'].md5(uniqid(rand(), true)).'.gml';
// read the xml datasource
$aszFile = file( $szURL );
// check to see if an error was generated
if ( !is_array( $aszFile ) ||
strpos( strtoupper( $aszFile[0] ), '