Просмотр исходного кода

moved parse get feature args to get feature class

Piotr Labudda 8 лет назад
Родитель
Сommit
7841ce3b4b

+ 190 - 0
SE/se-lib/Api/Wfs/GetFeature.php

@@ -5,6 +5,196 @@ Lib::loadClass('Core_AclHelper');
 
 class Api_Wfs_GetFeature {
 
+	/**
+	 * @param $_GET [ , POST Body - XML file ]
+	 * @return array $args
+	 * TODO: get $acl and check more restrictions
+	 */
+	public static function parseGetFeatureArgsFromRequest() {
+		$rawArgs = $_GET;// $_REQUEST; ($_POST values not allowed, only xml in post body)
+		$args = array();
+		$args['xsd:type'] = null;// @from: TYPENAME, typeName (typename)
+		$args['typePrefix'] = null;// @from: TYPENAME, typeName (typename)
+		$args['typeName'] = null;// @from: TYPENAME, typeName (typename)
+		$args['srsname'] = null;// @from: SRSNAME (srsname)
+		$args['limit'] = 0;// @from: MAXFEATURES, maxFeatures, COUNT (maxfeatures, count)
+		$args['offset'] = 0;// @from: startIndex (startindex, count)
+		$args['ogc:filter'] = null;// @from: FILTER, Filter (filter)
+		$args['sortBy'] = null;// @from: sortBy (sortby)
+		$args['wfs:propertyName'] = null;// @from: propertyName (propertyname)
+		$args['filterFields'] = array();// array of field names to show - @from: propertyName
+		$args['resultType'] = null;// @from: resultType (resulttype)
+		$args['bbox'] = null;// @from: BBOX (bbox)
+		$args['wfs:featureID'] = null;// @from: featureID, featureId (featureid)
+		$args['primaryKey'] = null;// primaryKey type - @from: featureID or $req[primaryKey]
+		$args['xlink'] = V::get('xlink', '', $rawArgs); // xlink = "https://biuro.biall-net.pl/wfs/default_db/CRM_PROCES#PROCES.100" but with '#' replaced with '/'
+		$args['outputBlobFormat'] = V::get('outputBlobFormat', 'base64', $rawArgs); // how to print blob fields ('base64', 'link') - default base64
+		$args['resolve'] = V::get('resolve', '', $rawArgs);
+		$args['resolveDepth'] = V::get('resolveDepth', 0, $rawArgs, 'int'); // TODO: if ('*' === resolveDepth) - recurse resolve
+
+		$lowerArgs = array(); foreach ($rawArgs as $name => $value) $lowerArgs[ strtolower($name) ] = trim($value);
+
+		$args['xsd:type'] = V::get('typename', '', $lowerArgs);
+		$exType = explode(':', $args['xsd:type']);
+		if (count($exType) != 2) throw new HttpException("Wrong param TYPENAME", 400);
+		$args['typePrefix'] = $exType[0];
+		$args['typeName'] = $exType[1];
+
+		$args['srsname'] = V::get('SRSNAME', '', $lowerArgs);// eg. EPSG:4326
+
+		$args['limit'] = V::get('maxfeatures', $args['limit'], $lowerArgs, 'int');
+		$args['limit'] = V::get('count', $args['limit'], $lowerArgs, 'int');
+		$defaultFeaturesLimit = ('biuro.biall-net.pl' == V::get('SERVER_NAME', '', $_SERVER)) ? 50000 : 10000;
+		if ($args['limit'] <= 0) $args['limit'] = $defaultFeaturesLimit;// default limit
+
+		$args['offset'] = V::get('startindex', 0, $lowerArgs, 'int');
+
+		$args['ogc:filter'] = urldecode(V::get('filter', '', $lowerArgs));
+		if (empty($args['ogc:filter'])) {// read ogc:Filter from POST body if not defined in GET
+			$reqBody = Request::getRequestBody();
+			if (!empty($reqBody)) {
+				$parsedRequest = self::parseOgcFilterRequest($reqBody);
+				DBG::log($parsedRequest, 'array', 'parsed ogc query request');
+				if (!empty($parsedRequest['ogc:Filter'])) $args['ogc:filter'] = $parsedRequest['ogc:Filter'];
+				if (!empty($parsedRequest['wfs:PropertyName'])) {
+					$args['filterFields'] = array_merge($args['filterFields'], $parsedRequest['wfs:PropertyName']);
+				}
+			} else if ($args['resolve'] && $args['resolveDepth'] > 0) {
+				$args['filterFields'][] = '*' . str_repeat("/*", $args['resolveDepth'] - 1);
+			}
+		}
+
+		$args['sortBy'] = V::get('sortby', '', $lowerArgs);// TODO: split to array of array(fieldName, (asc | desc))
+
+		$args['wfs:propertyName'] = trim(V::get('propertyname', '', $lowerArgs));
+		if (strlen($args['wfs:propertyName']) > 0) {
+			$exFields = explode(',', $args['wfs:propertyName']);
+			$exFields = array_map('trim', $exFields);
+			$exFields = array_filter($exFields, [ 'V', 'filterNotEmpty' ]);
+			$args['filterFields'] = array_merge($args['filterFields'], $exFields);
+		}
+
+		$args['resultType'] = V::get('resulttype', '', $lowerArgs);
+
+		$args['bbox'] = V::get('bbox', '', $lowerArgs);
+		if (!empty($args['bbox'])) {
+			// BBOX may have EPSG at then end - example: ",EPSG:4326"
+			$num = "\d+.?\d*?";// "\d+(.\d+)?"
+			if (!preg_match("/^({$num}),({$num}),({$num}),({$num})(\,EPSG:\d+)?$/", $args['bbox'], $matches)) throw new Exception("Illegal BBOX format");
+			// QGIS send BBOX in correct order: 54.23580872176457,18.46844302390853,54.25220902538883,18.492990600812696
+			// first number should not be smaller then second
+			// example $matches:
+			//     [0] => 18.492990600812696,54.23580872176457,18.46844302390853,54.25220902538883
+			//     [1] => 18.492990600812696
+			//     [2] => 54.23580872176457
+			//     [3] => 18.46844302390853
+			//     [4] => 54.25220902538883
+			// (optional) EPGS
+			$bboxPoints = ($matches[1] > $matches[2])
+				? [ $matches[1], $matches[2], $matches[3], $matches[4] ]
+				: [ $matches[2], $matches[3], $matches[4], $matches[1] ];
+
+			$args['bbox'] = implode(",", $bboxPoints);
+		}
+
+		$args['wfs:featureID'] = V::get('featureid', null, $lowerArgs);// TODO: allow multiply feature id (csv)
+		if ($args['wfs:featureID']) {
+			$dotPos = strpos($args['wfs:featureID'], '.');
+			if (!$dotPos) throw new Exception("Wrong param featureID");
+			if ($args['typeName'] != substr($args['wfs:featureID'], 0, $dotPos)) throw new Exception("Wrong typeName in param featureID");
+			$args['primaryKey'] = substr($args['wfs:featureID'], $dotPos + 1);
+		}
+
+		$pk = V::get('primarykey', '', $lowerArgs);
+		if ($pk) {
+			$args['primaryKey'] = $pk;
+		}
+
+		if(DBG::isActive() && V::get('DBG', 0, $_GET)){ echo "\$lowerArgs:";print_r($lowerArgs); }
+		if(DBG::isActive() && V::get('DBG', 0, $_GET)){ echo "\$args:";print_r($args); }
+
+		return $args;
+	}
+
+	public static function parseOgcFilterRequest($requestOgcFilter) {
+		// GetFeature: @maxFeatures, @traverseXlinkDepth, @traverseXlinkExpiry
+		// \-- (1..) wfs:Query: @typeName
+		//     \--- (0..) ---: wfs:PropertyName
+		//                   : wfs:XlinkPropertyName
+		//                   : ogc:Function
+		//          ogc:Filter
+		//          ogc:SortBy
+		//          \--- (1..) ogc:SortProperty
+		//               \---: ogc:PropertyName
+		//                   : ogc:SortOrder ( DESC / ASC )
+		// <ogc:SortBy>
+		//     <ogc:SortProperty>
+		//         <ogc:PropertyName>ASTA</ogc:PropertyName>
+		//         <ogc:SortOrder>ASC</ogc:SortOrder>
+		//     </ogc:SortProperty>
+		// </ogc:SortBy>
+
+		if (empty($requestOgcFilter)) return '';
+		$requestXml = new DOMDocument();
+		$requestXml->loadXml($requestOgcFilter);
+		$rootNode = $requestXml->documentElement;
+		// if ($rootNode->getAttribute('resolve'))
+		DBG::log([$rootNode->getAttribute('resolve'), $rootNode->getAttribute('resolveDepth')], 'array', "TODO: use wfs:GetFeature @resolve, @resolveDepth");
+		$nodesQuery = [];
+		foreach ($requestXml->getElementsByTagNameNS('http://www.opengis.net/wfs', 'Query') as $element) {
+			DBG::log($element->nodeName, 'array', "main loop - wfs:Query");
+			$nodesQuery[] = $element;
+		}
+
+		if (empty($nodesQuery)) { // legacy - try to find 'ogc:Filter'
+			$tagsFilter = [];
+			foreach ($requestXml->getElementsByTagNameNS('http://www.opengis.net/ogc', 'Filter') as $element) {
+				DBG::log($element->nodeName, 'array', "loop element (0 * wfs:Query)");
+				$tagsFilter[] = $requestXml->saveXML($element);
+			}
+			DBG::log($tagsFilter, 'array', "\$tagsFilter (0 * wfs:Query)");
+			return [
+				'ogc:Filter' => (!empty($tagsFilter))
+					? reset($tagsFilter) // TODO: only one ogc:Filter allowed
+					: ''
+			];
+		}
+
+		if (1 === count($nodesQuery)) {
+			$tagsFilter = [];
+			foreach ($requestXml->getElementsByTagNameNS('http://www.opengis.net/ogc', 'Filter') as $element) {
+				// DBG::log($element->nodeName, 'array', "loop ogc:Filter (1 * wfs:Query)");
+				$tagsFilter[] = $requestXml->saveXML($element);
+			}
+			DBG::log($tagsFilter, 'array', "\$tagsFilter (1 * wfs:Query)");
+			$tagsWfsPropertyName = [];
+			foreach ($requestXml->getElementsByTagNameNS('http://www.opengis.net/wfs', 'PropertyName') as $element) {
+				// DBG::log($element->nodeValue, 'array', "loop wfs:PropertyName (1 * wfs:Query)");
+				if ($element->getAttribute('resolve')) DBG::log([$element->getAttribute('resolve'), $element->getAttribute('resolveDepth'), $element->getAttribute('resolvePath')], 'array', "TODO: use wfs:PropertyName @resolve, @resolveDepth, @resolvePath");
+				$value = $element->nodeValue;
+				if (in_array($element->getAttribute('resolve'), ['all', 'local', 'remote'])) {
+					$depth = $element->getAttribute('resolveDepth');
+					if ('*' !== substr($value, -1)) { // TODO: propertyName is not regex
+						if ('*' === $depth) $value .= "/**"; // TODO: resolveDepth="*" - resolve all
+						else if ((int)$depth > 0) $value .= str_repeat("/*", $depth);
+					}
+				}
+				$tagsWfsPropertyName[] = $value;
+			}
+			DBG::log($tagsWfsPropertyName, 'array', "\$tagsWfsPropertyName (1 * wfs:Query)");
+			return array_filter([
+				'ogc:Filter' => (!empty($tagsFilter))
+					? reset($tagsFilter)
+					: '',
+				'wfs:PropertyName' => (!empty($tagsWfsPropertyName))
+					? $tagsWfsPropertyName
+					: '',
+			], function ($value) { return !empty($value); });
+		}
+
+		throw new Exception("Not imeplemented multiple ogc:Query"); // multiple ogc:Query require ogc:Query/@typeName
+	}
+
 	public static function convertOgcPropertyListToFeatureQueryCols(&$schemaCache, $ogcPropertyList, $acl) {
 		return self::convertOgcPropsRecurse($schemaCache, $ogcPropertyList, $acl, $dbgLoopNr = 0, $dbgXpath = $acl->getNamespace());
 	}

+ 3 - 3
SE/se-lib/Api/WfsDataServer.php

@@ -7,6 +7,7 @@ Lib::loadClass('Api_WfsNs');
 Lib::loadClass('Core_XmlWriter');
 Lib::loadClass('DBG');
 Lib::loadClass('Api_Wfs_GetCapabilities');
+Lib::loadClass('Api_Wfs_GetFeature');
 
 class Api_WfsDataServer extends Api_WfsServerBase {
 
@@ -67,7 +68,7 @@ class Api_WfsDataServer extends Api_WfsServerBase {
 	}
 
 	public function getFeatureAction() {
-		$args = $this->parseGetFeatureArgsFromRequest();
+		$args = Api_Wfs_GetFeature::parseGetFeatureArgsFromRequest();
 		if ('hits' == $args['resultType']) {
 			return $this->getTotalFeatures($args, $simple = true);
 		} else {
@@ -76,7 +77,7 @@ class Api_WfsDataServer extends Api_WfsServerBase {
 	}
 
 	public function getFeatureAdvancedAction() {
-		$args = $this->parseGetFeatureArgsFromRequest();
+		$args = Api_Wfs_GetFeature::parseGetFeatureArgsFromRequest();
 		if ('hits' == $args['resultType']) {
 			return $this->getTotalFeatures($args, $simple = false);
 		} else {
@@ -188,7 +189,6 @@ class Api_WfsDataServer extends Api_WfsServerBase {
 
 		DBG::log($args, 'array', "\$args");
 		$schemaCache = array();
-		Lib::loadClass('Api_Wfs_GetFeature');
 		try {
 			$searchParams['cols'] = Api_Wfs_GetFeature::convertOgcPropertyListToFeatureQueryCols($schemaCache, $args['filterFields'], $acl); // convert $args['filterFields'] to field list
 		} catch (Exception $e) {

+ 2 - 1
SE/se-lib/Api/WfsQgisServer.php

@@ -5,6 +5,7 @@ Lib::loadClass('Api_WfsException');
 Lib::loadClass('Api_WfsGeomTypeConverter');
 Lib::loadClass('DBG');
 Lib::loadClass('Api_Wfs_GetCapabilities');
+Lib::loadClass('Api_Wfs_GetFeature');
 
 class Api_WfsQgisServer extends Api_WfsServerBase {
 
@@ -44,7 +45,7 @@ class Api_WfsQgisServer extends Api_WfsServerBase {
 	}
 
 	public function getFeatureAction() {
-		$args = $this->parseGetFeatureArgsFromRequest();
+		$args = Api_Wfs_GetFeature::parseGetFeatureArgsFromRequest();
 		// TODO: if ('hits' == $args['resultType']) ...
 		return $this->getFeatures($args);
 	}

+ 0 - 194
SE/se-lib/Api/WfsServerBase.php

@@ -66,121 +66,6 @@ class Api_WfsServerBase {
 		return $acl;
 	}
 
-	/**
-	 * @param $_GET [ , POST Body - XML file ]
-	 * @return array $args
-	 * TODO: get $acl and check more restrictions
-	 */
-	public function parseGetFeatureArgsFromRequest() {
-		$rawArgs = $_GET;// $_REQUEST; ($_POST values not allowed, only xml in post body)
-		$args = array();
-		$args['xsd:type'] = null;// @from: TYPENAME, typeName (typename)
-		$args['typePrefix'] = null;// @from: TYPENAME, typeName (typename)
-		$args['typeName'] = null;// @from: TYPENAME, typeName (typename)
-		$args['srsname'] = null;// @from: SRSNAME (srsname)
-		$args['limit'] = 0;// @from: MAXFEATURES, maxFeatures, COUNT (maxfeatures, count)
-		$args['offset'] = 0;// @from: startIndex (startindex, count)
-		$args['ogc:filter'] = null;// @from: FILTER, Filter (filter)
-		$args['sortBy'] = null;// @from: sortBy (sortby)
-		$args['wfs:propertyName'] = null;// @from: propertyName (propertyname)
-		$args['filterFields'] = array();// array of field names to show - @from: propertyName
-		$args['resultType'] = null;// @from: resultType (resulttype)
-		$args['bbox'] = null;// @from: BBOX (bbox)
-		$args['wfs:featureID'] = null;// @from: featureID, featureId (featureid)
-		$args['primaryKey'] = null;// primaryKey type - @from: featureID or $req[primaryKey]
-		$args['xlink'] = V::get('xlink', '', $rawArgs); // xlink = "https://biuro.biall-net.pl/wfs/default_db/CRM_PROCES#PROCES.100" but with '#' replaced with '/'
-		$args['outputBlobFormat'] = V::get('outputBlobFormat', 'base64', $rawArgs); // how to print blob fields ('base64', 'link') - default base64
-		$args['resolve'] = V::get('resolve', '', $rawArgs);
-		$args['resolveDepth'] = V::get('resolveDepth', 0, $rawArgs, 'int'); // TODO: if ('*' === resolveDepth) - recurse resolve
-
-		$lowerArgs = array(); foreach ($rawArgs as $name => $value) $lowerArgs[ strtolower($name) ] = trim($value);
-
-		$args['xsd:type'] = V::get('typename', '', $lowerArgs);
-		$exType = explode(':', $args['xsd:type']);
-		if (count($exType) != 2) throw new HttpException("Wrong param TYPENAME", 400);
-		$args['typePrefix'] = $exType[0];
-		$args['typeName'] = $exType[1];
-
-		$args['srsname'] = V::get('SRSNAME', '', $lowerArgs);// eg. EPSG:4326
-
-		$args['limit'] = V::get('maxfeatures', $args['limit'], $lowerArgs, 'int');
-		$args['limit'] = V::get('count', $args['limit'], $lowerArgs, 'int');
-		$defaultFeaturesLimit = ('biuro.biall-net.pl' == V::get('SERVER_NAME', '', $_SERVER)) ? 50000 : 10000;
-		if ($args['limit'] <= 0) $args['limit'] = $defaultFeaturesLimit;// default limit
-
-		$args['offset'] = V::get('startindex', 0, $lowerArgs, 'int');
-
-		$args['ogc:filter'] = urldecode(V::get('filter', '', $lowerArgs));
-		if (empty($args['ogc:filter'])) {// read ogc:Filter from POST body if not defined in GET
-			$reqBody = Request::getRequestBody();
-			if (!empty($reqBody)) {
-				$parsedRequest = $this->parseOgcFilterRequest($reqBody);
-				DBG::log($parsedRequest, 'array', 'parsed ogc query request');
-				if (!empty($parsedRequest['ogc:Filter'])) $args['ogc:filter'] = $parsedRequest['ogc:Filter'];
-				if (!empty($parsedRequest['wfs:PropertyName'])) {
-					foreach ($parsedRequest['wfs:PropertyName'] as $fieldXpath) {
-						$args['filterFields'][] = $fieldXpath;
-					}
-				}
-			} else if ($args['resolve'] && $args['resolveDepth'] > 0) {
-				$args['filterFields'][] = '*' . str_repeat("/*", $args['resolveDepth'] - 1);
-			}
-		}
-
-		$args['sortBy'] = V::get('sortby', '', $lowerArgs);// TODO: split to array of array(fieldName, (asc | desc))
-
-		$args['wfs:propertyName'] = trim(V::get('propertyname', '', $lowerArgs));// TODO: fields to show - split by ','
-		if (strlen($args['wfs:propertyName']) > 0) {
-			$exFields = explode(',', $args['wfs:propertyName']);
-			foreach ($exFields as $fieldName) {
-				$fieldName = trim($fieldName);
-				if (!empty($fieldName)) $args['filterFields'][] = $fieldName;
-			}
-		}
-
-		$args['resultType'] = V::get('resulttype', '', $lowerArgs);
-
-		$args['bbox'] = V::get('bbox', '', $lowerArgs);
-		if (!empty($args['bbox'])) {
-			// BBOX may have EPSG at then end - example: ",EPSG:4326"
-			$num = "\d+.?\d*?";// "\d+(.\d+)?"
-			if (!preg_match("/^({$num}),({$num}),({$num}),({$num})(\,EPSG:\d+)?$/", $args['bbox'], $matches)) throw new Exception("Illegal BBOX format");
-			// QGIS send BBOX in correct order: 54.23580872176457,18.46844302390853,54.25220902538883,18.492990600812696
-			// first number should not be smaller then second
-			// example $matches:
-			//     [0] => 18.492990600812696,54.23580872176457,18.46844302390853,54.25220902538883
-			//     [1] => 18.492990600812696
-			//     [2] => 54.23580872176457
-			//     [3] => 18.46844302390853
-			//     [4] => 54.25220902538883
-			// (optional) EPGS
-			$bboxPoints = ($matches[1] > $matches[2])
-				? [ $matches[1], $matches[2], $matches[3], $matches[4] ]
-				: [ $matches[2], $matches[3], $matches[4], $matches[1] ];
-
-			$args['bbox'] = implode(",", $bboxPoints);
-		}
-
-		$args['wfs:featureID'] = V::get('featureid', null, $lowerArgs);// TODO: allow multiply feature id (csv)
-		if ($args['wfs:featureID']) {
-			$dotPos = strpos($args['wfs:featureID'], '.');
-			if (!$dotPos) throw new Exception("Wrong param featureID");
-			if ($args['typeName'] != substr($args['wfs:featureID'], 0, $dotPos)) throw new Exception("Wrong typeName in param featureID");
-			$args['primaryKey'] = substr($args['wfs:featureID'], $dotPos + 1);
-		}
-
-		$pk = V::get('primarykey', '', $lowerArgs);
-		if ($pk) {
-			$args['primaryKey'] = $pk;
-		}
-
-		if(DBG::isActive() && V::get('DBG', 0, $_GET)){ echo "\$lowerArgs:";print_r($lowerArgs); }
-		if(DBG::isActive() && V::get('DBG', 0, $_GET)){ echo "\$args:";print_r($args); }
-
-		return $args;
-	}
-
-
 	public function _getXmlSchemaLocation() {
 		$schemaLocations = array();
 		//$schemaLocations[] = 'http://www.opengis.net/wfs http://webgis.regione.sardegna.it:80/geoserver/schemas/wfs/1.0.0/WFS-capabilities.xsd';// @from http://webgis.regione.sardegna.it/geoserver/ows?service=WFS&request=GetCapabilities
@@ -1303,83 +1188,4 @@ if($DBG){echo 'L.' . __LINE__ . ' $validateConvertedTransactionXsdString:';print
 		$this->_logger->DBG($reqLog, $lineNr, $funName, $className);
 	}
 
-	public function parseOgcFilterRequest($requestOgcFilter) {
-		// GetFeature: @maxFeatures, @traverseXlinkDepth, @traverseXlinkExpiry
-		// \-- (1..) wfs:Query: @typeName
-		//     \--- (0..) ---: wfs:PropertyName
-		//                   : wfs:XlinkPropertyName
-		//                   : ogc:Function
-		//          ogc:Filter
-		//          ogc:SortBy
-		//          \--- (1..) ogc:SortProperty
-		//               \---: ogc:PropertyName
-		//                   : ogc:SortOrder ( DESC / ASC )
-		// <ogc:SortBy>
-		//     <ogc:SortProperty>
-		//         <ogc:PropertyName>ASTA</ogc:PropertyName>
-		//         <ogc:SortOrder>ASC</ogc:SortOrder>
-		//     </ogc:SortProperty>
-		// </ogc:SortBy>
-
-		if (empty($requestOgcFilter)) return '';
-		$requestXml = new DOMDocument();
-		$requestXml->loadXml($requestOgcFilter);
-		$rootNode = $requestXml->documentElement;
-		// if ($rootNode->getAttribute('resolve'))
-		DBG::log([$rootNode->getAttribute('resolve'), $rootNode->getAttribute('resolveDepth')], 'array', "TODO: use wfs:GetFeature @resolve, @resolveDepth");
-		$nodesQuery = [];
-		foreach ($requestXml->getElementsByTagNameNS('http://www.opengis.net/wfs', 'Query') as $element) {
-			DBG::log($element->nodeName, 'array', "main loop - wfs:Query");
-			$nodesQuery[] = $element;
-		}
-
-		if (empty($nodesQuery)) { // legacy - try to find 'ogc:Filter'
-			$tagsFilter = [];
-			foreach ($requestXml->getElementsByTagNameNS('http://www.opengis.net/ogc', 'Filter') as $element) {
-				DBG::log($element->nodeName, 'array', "loop element (0 * wfs:Query)");
-				$tagsFilter[] = $requestXml->saveXML($element);
-			}
-			DBG::log($tagsFilter, 'array', "\$tagsFilter (0 * wfs:Query)");
-			return [
-				'ogc:Filter' => (!empty($tagsFilter))
-					? reset($tagsFilter) // TODO: only one ogc:Filter allowed
-					: ''
-			];
-		}
-
-		if (1 === count($nodesQuery)) {
-			$tagsFilter = [];
-			foreach ($requestXml->getElementsByTagNameNS('http://www.opengis.net/ogc', 'Filter') as $element) {
-				// DBG::log($element->nodeName, 'array', "loop ogc:Filter (1 * wfs:Query)");
-				$tagsFilter[] = $requestXml->saveXML($element);
-			}
-			DBG::log($tagsFilter, 'array', "\$tagsFilter (1 * wfs:Query)");
-			$tagsWfsPropertyName = [];
-			foreach ($requestXml->getElementsByTagNameNS('http://www.opengis.net/wfs', 'PropertyName') as $element) {
-				// DBG::log($element->nodeValue, 'array', "loop wfs:PropertyName (1 * wfs:Query)");
-				if ($element->getAttribute('resolve')) DBG::log([$element->getAttribute('resolve'), $element->getAttribute('resolveDepth'), $element->getAttribute('resolvePath')], 'array', "TODO: use wfs:PropertyName @resolve, @resolveDepth, @resolvePath");
-				$value = $element->nodeValue;
-				if (in_array($element->getAttribute('resolve'), ['all', 'local', 'remote'])) {
-					$depth = $element->getAttribute('resolveDepth');
-					if ('*' !== substr($value, -1)) { // TODO: propertyName is not regex
-						if ('*' === $depth) $value .= "/**"; // TODO: resolveDepth="*" - resolve all
-						else if ((int)$depth > 0) $value .= str_repeat("/*", $depth);
-					}
-				}
-				$tagsWfsPropertyName[] = $value;
-			}
-			DBG::log($tagsWfsPropertyName, 'array', "\$tagsWfsPropertyName (1 * wfs:Query)");
-			return array_filter([
-				'ogc:Filter' => (!empty($tagsFilter))
-					? reset($tagsFilter)
-					: '',
-				'wfs:PropertyName' => (!empty($tagsWfsPropertyName))
-					? $tagsWfsPropertyName
-					: '',
-			], function ($value) { return !empty($value); });
-		}
-
-		throw new Exception("Not imeplemented multiple ogc:Query"); // multiple ogc:Query require ogc:Query/@typeName
-	}
-
 }