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] ), '