From 80da873ca754ecb3226681523173f1cc87717fc2 Mon Sep 17 00:00:00 2001 From: rldhont Date: Fri, 3 Nov 2023 13:45:27 +0100 Subject: [PATCH 01/46] \Lizmap\App\XmlTools Docstring --- lizmap/modules/lizmap/lib/App/XmlTools.php | 26 +++++++++++----------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/lizmap/modules/lizmap/lib/App/XmlTools.php b/lizmap/modules/lizmap/lib/App/XmlTools.php index 687ea73631..972f43590b 100644 --- a/lizmap/modules/lizmap/lib/App/XmlTools.php +++ b/lizmap/modules/lizmap/lib/App/XmlTools.php @@ -14,14 +14,14 @@ class XmlTools { - /* - * Interprets a string of XML into an object + /** + * Interprets a string of XML into an object. * * @param string $xml_str a well-formed XML string * - * @return SimpleXmlElement|string an object of class SimpleXMLElement with properties - * containing the data held within the XML document, or - * a string containing the error message. + * @return \SimpleXmlElement|string an object of class SimpleXMLElement with properties + * containing the data held within the XML document, or + * a string containing the error message */ public static function xmlFromString($xml_str) { @@ -34,14 +34,14 @@ public static function xmlFromString($xml_str) return $xml; } - /* - * Interprets an XML file into an object + /** + * Interprets an XML file into an object. * * @param string $xml_path the path to the xml file * - * @return SimpleXmlElement|string an object of class SimpleXMLElement with properties - * containing the data held within the XML document, or - * a string containing the error message. + * @return \SimpleXmlElement|string an object of class SimpleXMLElement with properties + * containing the data held within the XML document, or + * a string containing the error message */ public static function xmlFromFile($xml_path) { @@ -54,12 +54,12 @@ public static function xmlFromFile($xml_path) return $xml; } - /* - * Get XML error message + /** + * Get XML error message. * * Build an error message based on LibXMLError object * - * @return string the error message. + * @return string the error message */ private static function xmlErrorMsg() { From e7ad3fbe2a1c6ca23fe3d3cf599b721dff45b275 Mon Sep 17 00:00:00 2001 From: rldhont Date: Fri, 3 Nov 2023 15:04:06 +0100 Subject: [PATCH 02/46] Enhancing \Lizmap\App\XmlTools to get \XMLReader object --- lizmap/modules/lizmap/lib/App/XmlTools.php | 69 ++++++++++ tests/units/classes/App/XmlToolsTest.php | 140 +++++++++++++++++++++ 2 files changed, 209 insertions(+) diff --git a/lizmap/modules/lizmap/lib/App/XmlTools.php b/lizmap/modules/lizmap/lib/App/XmlTools.php index 972f43590b..e78978f30b 100644 --- a/lizmap/modules/lizmap/lib/App/XmlTools.php +++ b/lizmap/modules/lizmap/lib/App/XmlTools.php @@ -89,7 +89,76 @@ private static function xmlErrorMsg() $msg .= 'Column: '.$error->column.' '; $msg .= trim($error->message); } + // Clear libxml error buffer + libxml_clear_errors(); return $msg; } + + /** + * Interprets a string of XML into an XML Pull parser. + * It acts as a cursor going forward on the document stream and stopping at each node on the way. + * + * @param string $xml_str a well-formed XML string + * + * @return \XMLReader an object of class XMLReader with properties at the root document element + * containing the data held within the XML document + */ + public static function xmlReaderFromString($xml_str) + { + $oXml = new \XMLReader(); + // Set XML + if (!$oXml->XML($xml_str)) { + throw new \Exception(self::xmlErrorMsg()); + } + + // Read until we are at the root document element + while ($oXml->read()) { + if ($oXml->nodeType == \XMLReader::ELEMENT + && $oXml->depth == 0) { + break; + } + } + + $errorMsg = self::xmlErrorMsg(); + if ($errorMsg !== '') { + throw new \Exception($errorMsg); + } + + return $oXml; + } + + /** + * Interprets an XML file into an XML pull parser. + * It acts as a cursor going forward on the document stream and stopping at each node on the way. + * + * @param string $xml_path the path to the xml file + * + * @return \XMLReader an object of class XMLReader with properties at the root document element + * containing the data held within the XML document + */ + public static function xmlReaderFromFile($xml_path) + { + $oXml = new \XMLReader(); + + // Open file + if (!$oXml->open($xml_path)) { + throw new \Exception(self::xmlErrorMsg()); + } + + // Read until we are at the root document element + while ($oXml->read()) { + if ($oXml->nodeType == \XMLReader::ELEMENT + && $oXml->depth == 0) { + break; + } + } + + $errorMsg = self::xmlErrorMsg(); + if ($errorMsg !== '') { + throw new \Exception($errorMsg); + } + + return $oXml; + } } diff --git a/tests/units/classes/App/XmlToolsTest.php b/tests/units/classes/App/XmlToolsTest.php index 5680a8b9ae..657036660c 100644 --- a/tests/units/classes/App/XmlToolsTest.php +++ b/tests/units/classes/App/XmlToolsTest.php @@ -77,4 +77,144 @@ function testXmlFromFile() { $this->assertStringStartsNotWith('\n', $xml); $this->assertStringContainsString('Fatal', $xml); } + + function testXmlReaderFromString() { + $data = array( + 'properties' => array( + 'WMSServiceTitle' => 'title', + 'WMSServiceAbstract' => 'abstract', + 'WMSKeywordList' => array( + 'value' => array('key', 'word', 'WMS'), + ), + 'WMSExtent' => array( + 'value' => array('42', '24', '21', '12'), + ), + 'WMSOnlineResource' => 'ressource', + 'WMSContactMail' => 'test.mail@3liz.org', + 'WMSContactOrganization' => '3liz', + 'WMSContactPerson' => 'marvin', + 'WMSContactPhone' => '', + ), + ); + $qgis = new SimpleXMLElement(''); + $this->arrayToXml($data, $qgis); + $xml_str = $qgis->asXML(); + + $xml = App\XmlTools::xmlReaderFromString($xml_str); + $this->assertTrue(is_object($xml)); + $this->assertEquals($xml_str, ''."\n".$xml->readOuterXml()."\n"); + $this->assertEquals(\XMLReader::ELEMENT, $xml->nodeType); + $this->assertEquals('qgis', $xml->localName); + $this->assertEquals(0, $xml->depth); + + // first child element + $xml->read(); + $this->assertEquals(\XMLReader::ELEMENT, $xml->nodeType); + $this->assertEquals('properties', $xml->localName); + $this->assertEquals(1, $xml->depth); + + // Go to next sibling + $xml->next(); + // No sibling we are at the end of the parent element + $this->assertEquals(\XMLReader::END_ELEMENT, $xml->nodeType); + $this->assertEquals('qgis', $xml->localName); + $this->assertEquals(0, $xml->depth); + + // Removing the closing root element does not invalidate xml string + // for XML Reader + $xml_str_invalid = str_replace('', '', $xml_str); + $this->assertNotEquals($xml_str, $xml_str_invalid); + + $xml = App\XmlTools::xmlReaderFromString($xml_str_invalid); + $this->assertTrue(is_object($xml)); + $this->assertEquals(\XMLReader::ELEMENT, $xml->nodeType); + $this->assertEquals('qgis', $xml->localName); + + // The validate parser option must be enabled for + // this method to work properly + $xml->setParserProperty(\XMLReader::VALIDATE, true); + $this->assertTrue($xml->isValid()); + + // first child element + $xml->read(); + $this->assertEquals(\XMLReader::ELEMENT, $xml->nodeType); + $this->assertEquals('properties', $xml->localName); + $this->assertEquals(1, $xml->depth); + + // Go to next sibling + $xml->next(); + // No sibling we should be at the end of the parent element + // But the XML is invalid here + $this->assertFalse($xml->isValid()); + // And we are some where else + $this->assertEquals(\XMLReader::ELEMENT, $xml->nodeType); + $this->assertEquals('WMSContactPerson', $xml->localName); + $this->assertEquals(2, $xml->depth); + } + + function testXmlReaderFromStringException() { + $this->expectExceptionMessage('Fatal Error 5: Line: 1 Column: 7 Extra content at the end of the document'); + App\XmlTools::xmlReaderFromString(''); + } + + function testXmlReaderFromFile() { + $xml_path = __DIR__.'/../Project/Ressources/WMSInfotest.qgs'; + + // Open the document with XML Reader at the root element document + $xml = App\XmlTools::xmlReaderFromFile($xml_path); + $this->assertTrue(is_object($xml)); + $this->assertEquals(\XMLReader::ELEMENT, $xml->nodeType); + $this->assertEquals('qgis', $xml->localName); + $this->assertEquals(0, $xml->depth); + + // next element + $xml->read(); + $this->assertEquals(\XMLReader::SIGNIFICANT_WHITESPACE, $xml->nodeType); + // next element + $xml->read(); + $this->assertEquals(\XMLReader::ELEMENT, $xml->nodeType); + $this->assertEquals('homePath', $xml->localName); + $this->assertEquals(1, $xml->depth); + + // Go to next sibling + $xml->next(); + $this->assertEquals(\XMLReader::SIGNIFICANT_WHITESPACE, $xml->nodeType); + // Go to next sibling + $xml->next(); + $this->assertEquals(\XMLReader::ELEMENT, $xml->nodeType); + $this->assertEquals('title', $xml->localName); + $this->assertEquals(1, $xml->depth); + + $xml_path_invalid = __DIR__.'/../Project/Ressources/WMSInfotest_invalid.qgs'; + + // Open the document with XML Reader at the root element document + $xml = App\XmlTools::xmlReaderFromFile($xml_path_invalid); + $this->assertTrue(is_object($xml)); + $this->assertEquals(\XMLReader::ELEMENT, $xml->nodeType); + $this->assertEquals('qgis', $xml->localName); + $this->assertEquals(0, $xml->depth); + + // The validate parser option must be enabled for + // this method to work properly + $xml->setParserProperty(\XMLReader::VALIDATE, true); + $this->assertTrue($xml->isValid()); + + // next element + $xml->read(); + $this->assertEquals(\XMLReader::SIGNIFICANT_WHITESPACE, $xml->nodeType); + // next element + $xml->read(); + $this->assertEquals(\XMLReader::ELEMENT, $xml->nodeType); + $this->assertEquals('homePath', $xml->localName); + $this->assertEquals(1, $xml->depth); + + // Go to next sibling + $xml->next(); + $this->assertEquals(\XMLReader::SIGNIFICANT_WHITESPACE, $xml->nodeType); + // Go to next sibling + $xml->next(); + $this->assertEquals(\XMLReader::ELEMENT, $xml->nodeType); + $this->assertEquals('title', $xml->localName); + $this->assertEquals(1, $xml->depth); + } } From 1bb5e585ec0b43850205a5443072f18cb4c90edb Mon Sep 17 00:00:00 2001 From: rldhont Date: Wed, 25 Oct 2023 10:49:10 +0200 Subject: [PATCH 03/46] Create QgisProjectParser to try parsing with \XMLReader --- .../lizmap/lib/Project/QgisProject.php | 3 + .../lizmap/lib/Project/QgisProjectParser.php | 1687 +++++++++++ .../classes/Project/QgisProjectParserTest.php | 2527 +++++++++++++++++ 3 files changed, 4217 insertions(+) create mode 100644 lizmap/modules/lizmap/lib/Project/QgisProjectParser.php create mode 100644 tests/units/classes/Project/QgisProjectParserTest.php diff --git a/lizmap/modules/lizmap/lib/Project/QgisProject.php b/lizmap/modules/lizmap/lib/Project/QgisProject.php index 52f42f60b5..a27b605ebb 100644 --- a/lizmap/modules/lizmap/lib/Project/QgisProject.php +++ b/lizmap/modules/lizmap/lib/Project/QgisProject.php @@ -1231,6 +1231,9 @@ protected function readXmlProject($qgs_path) list($this->relations, $this->relationsFields) = $this->readRelations($qgsXml); } + /** + * @param \SimpleXMLElement $xml + */ protected function readWMSInformation($qgsLoad) { diff --git a/lizmap/modules/lizmap/lib/Project/QgisProjectParser.php b/lizmap/modules/lizmap/lib/Project/QgisProjectParser.php new file mode 100644 index 0000000000..e4b33d2c20 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/QgisProjectParser.php @@ -0,0 +1,1687 @@ +nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + if ($oXmlReader->localName != $localName) { + throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); + } + + $depth = $oXmlReader->depth; + $data = array( + 'version' => $oXmlReader->getAttribute('version'), + 'projectname' => $oXmlReader->getAttribute('projectname'), + ); + $tagNames = array( + 'title', + 'projectCrs', + // 'mapcanvas', // for theMapCanvas the CRS is provided by projectCrs + 'layer-tree-group', + 'projectlayers', + 'properties', + 'visibility-presets', + 'Layouts', + ); + while ($oXmlReader->read()) { + + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + $tagName = $oXmlReader->localName; + if (!in_array($tagName, $tagNames) + || $oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'properties') { + $data[$oXmlReader->localName] = self::readQgisProperties($oXmlReader); + } else if ($oXmlReader->localName == 'projectCrs') { + $data[$oXmlReader->localName] = self::readQgisProjectCrs($oXmlReader); + } else { + $data[$oXmlReader->localName] = $oXmlReader->readString(); + } + } + + return $data; + } + + /** + * @param \XMLReader $xml + * + * @return Array + */ + public static function readQgisProjectCrs($oXmlReader) + { + $localName = 'projectCrs'; + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + if ($oXmlReader->localName != $localName) { + throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); + } + + $depth = $oXmlReader->depth; + $data = array(); + while ($oXmlReader->read()) { + + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'spatialrefsys') { + $data = self::readSpatialRefSys($oXmlReader); + } + } + return $data; + } + + /** + * @param \XMLReader $xml + * + * @return Array + */ + public static function readQgisLayerTreeGroup($oXmlReader) + { + $localName = 'layer-tree-group'; + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + if ($oXmlReader->localName != $localName) { + throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); + } + + $depth = $oXmlReader->depth; + $data = array(); + $tagNames = array( + 'custom-order' + ); + while ($oXmlReader->read()) { + + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + $tagName = $oXmlReader->localName; + if (!in_array($tagName, $tagNames) + || $oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'custom-order') { + $data[$oXmlReader->localName] = self::readQgisCustomOrder($oXmlReader); + } + } + return $data; + } + + /** + * @param \XMLReader $xml + * + * @return Array + */ + public static function readQgisCustomOrder($oXmlReader) + { + $localName = 'custom-order'; + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + if ($oXmlReader->localName != $localName) { + throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); + } + + $depth = $oXmlReader->depth; + $data = array( + 'enabled' => filter_var($oXmlReader->getAttribute('enabled'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE), + ); + if ($data['enabled']) { + $data['items'] = self::readItems($oXmlReader); + } else { + while ($oXmlReader->read()) { + + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + } + } + return $data; + } + + /** + * @param \XMLReader $xml + * + * @return Array + */ + public static function readQgisProperties($oXmlReader) + { + $localName = 'properties'; + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + if ($oXmlReader->localName != $localName) { + throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); + } + + $depth = $oXmlReader->depth; + $data = array(); + $tagNames = array( + 'WMSServiceTitle', + 'WMSServiceAbstract', + 'WMSKeywordList', + 'WMSExtent', + // 'ProjectCrs', // it is in SpatialRefSys and is deprecated - the qgis/projectCrs is recommended + 'WMSOnlineResource', + 'WMSContactMail', + 'WMSContactOrganization', + 'WMSContactPerson', + 'WMSContactPhone', + 'WMSMaxWidth', + 'WMSMaxHeight', + 'WMSRestrictedComposers', + 'WMSUseLayerIDs', + // 'Gui', // it contains Canvas and Selection colors extracted by readQgisGuiProperties + // 'Variables', + ); + while ($oXmlReader->read()) { + + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + $tagName = $oXmlReader->localName; + if (!in_array($tagName, $tagNames) + || $oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'Gui') { + $data[$oXmlReader->localName] = self::readQgisGuiProperties($oXmlReader); + } else if ($oXmlReader->localName == 'Variables') { + $data[$oXmlReader->localName] = self::readQgisVariablesProperties($oXmlReader); + } + + $type = $oXmlReader->getAttribute('type'); + if ($type == 'QStringList') { + $data[$tagName] = array(); + if (!$oXmlReader->isEmptyElement) { + $data[$tagName] = self::readValues($oXmlReader); + } + } else if ($type == 'bool') { + $data[$tagName] = filter_var($oXmlReader->readString(), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + } else if ($type == 'int') { + $data[$tagName] = (int) $oXmlReader->readString(); + } else { + $data[$tagName] = $oXmlReader->readString(); + } + } + + return $data; + } + + /** + * @param \XMLReader $xml + * + * @return Array + */ + public static function readQgisGuiProperties($oXmlReader) + { + $localName = 'Gui'; + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + if ($oXmlReader->localName != $localName) { + throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); + } + + $depth = $oXmlReader->depth; + $data = array(); + $tagNames = array( + 'CanvasColorBluePart', + 'CanvasColorGreenPart', + 'CanvasColorRedPart', + 'SelectionColorAlphaPart', + 'SelectionColorBluePart', + 'SelectionColorGreenPart', + 'SelectionColorRedPart', + // 'Identify', // it contains disabledLayers + ); + while ($oXmlReader->read()) { + + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + $tagName = $oXmlReader->localName; + if (!in_array($tagName, $tagNames) + || $oXmlReader->depth != $depth + 1) { + continue; + } + + $type = $oXmlReader->getAttribute('type'); + if ($type == 'QStringList') { + $data[$tagName] = ''; + if (!$oXmlReader->isEmptyElement) { + $data[$tagName] = implode(', ', self::readValues($oXmlReader)); + } + } else if ($type == 'int') { + $data[$tagName] = (int) $oXmlReader->readString(); + } else { + $data[$tagName] = $oXmlReader->readString(); + } + } + + return $data; + } + + /** + * @param \XMLReader $xml + * + * @return Array + */ + public static function readQgisVariablesProperties($oXmlReader) + { + $localName = 'Variables'; + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + if ($oXmlReader->localName != $localName) { + throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); + } + + $depth = $oXmlReader->depth; + $data = array(); + $tagNames = array( + 'variableNames', + 'variableValues', + ); + while ($oXmlReader->read()) { + + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + $tagName = $oXmlReader->localName; + if (!in_array($tagName, $tagNames) + || $oXmlReader->depth != $depth + 1) { + continue; + } + + $type = $oXmlReader->getAttribute('type'); + if ($type == 'QStringList') { + $data[$tagName] = array(); + if (!$oXmlReader->isEmptyElement) { + $data[$tagName] = self::readValues($oXmlReader); + } + } else { + $data[$tagName] = $oXmlReader->readString(); + } + } + return $data; + } + + /** + * @param \XMLReader $xml + * + * @return Array + */ + public static function readQgisVisibilityPresets($oXmlReader) + { + $localName = 'visibility-presets'; + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + if ($oXmlReader->localName != $localName) { + throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); + } + + $depth = $oXmlReader->depth; + $data = array(); + while ($oXmlReader->read()) { + + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'visibility-preset') { + $data[] = self::readQgisVisibilityPreset($oXmlReader); + } + } + + return $data; + } + + /** + * @param \XMLReader $xml + * + * @return Array + */ + public static function readQgisVisibilityPreset($oXmlReader) + { + $localName = 'visibility-preset'; + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + if ($oXmlReader->localName != $localName) { + throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); + } + + $depth = $oXmlReader->depth; + $data = array( + 'name' => $oXmlReader->getAttribute('name'), + 'layers' => array(), + 'checkedGroupNodes' => array(), + 'expandedGroupNodes' => array(), + ); + while ($oXmlReader->read()) { + + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth > $depth + 2) { + continue; + } + + $tagName = $oXmlReader->localName; + if ( $tagName == 'layer' ) { + $data['layers'][] = array( + 'id' => $oXmlReader->getAttribute('id'), + 'visible' => filter_var($oXmlReader->getAttribute('visible'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE), + 'style' => $oXmlReader->getAttribute('style'), + 'expanded' => filter_var($oXmlReader->getAttribute('expanded'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE), + ); + } else if ( $tagName == 'checked-group-node' ) { + $data['checkedGroupNodes'][] = $oXmlReader->getAttribute('id'); + } else if ( $tagName == 'expanded-group-node' ) { + $data['expandedGroupNodes'][] = $oXmlReader->getAttribute('id'); + } + } + + return $data; + } + + /** + * @param \XMLReader $xml + * + * @return Array + */ + public static function readQgisProjectLayers($oXmlReader) + { + $localName = 'projectlayers'; + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + if ($oXmlReader->localName != $localName) { + throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); + } + + $depth = $oXmlReader->depth; + $data = array(); + while ($oXmlReader->read()) { + + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'maplayer') { + $data[] = self::readQgisMapLayer($oXmlReader); + } + } + + return $data; + } + + /** + * @param \XMLReader $xml + * + * @return Array + */ + public static function readQgisMapLayer($oXmlReader) + { + $localName = 'maplayer'; + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + if ($oXmlReader->localName != $localName) { + throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); + } + + // The maplayer can reference an embeded layer + $embedded = filter_var($oXmlReader->getAttribute('embedded'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + if ($embedded) { + return array( + 'id' => $oXmlReader->getAttribute('id'), + 'embedded' => true, + 'project' => $oXmlReader->getAttribute('project'), + ); + } + + $depth = $oXmlReader->depth; + $data = array( + 'type' => $oXmlReader->getAttribute('type'), + 'embedded' => false, + ); + $tagNames = array( + 'id', + 'layername', + 'shortname', + 'title', + 'abstract', + 'srs', + 'datasource', + 'provider', + 'keywordList', + // 'fieldConfiguration' + // 'aliases', + // 'excludeAttributesWFS' + // 'excludeAttributesWMS' + // 'defaults', + // 'constraints', + // 'constraintExpressions', + // 'map-layer-style-manager' + // 'attributetableconfig' + // 'vectorjoins' + ); + + while ($oXmlReader->read()) { + + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + $tagName = $oXmlReader->localName; + if (!in_array($tagName, $tagNames) + || $oXmlReader->depth != $depth + 1) { + continue; + } + + if ($tagName == 'keywordList') { + $data[$tagName] = ''; + if (!$oXmlReader->isEmptyElement) { + $data[$tagName] = self::readValues($oXmlReader); + } + } else if ($tagName == 'srs') { + $data[$tagName] = self::readQgisMapLayerSrs($oXmlReader); + } else if ($tagName == 'map-layer-style-manager') { + $data[$tagName] = self::readQgisMapLayerStyleManager($oXmlReader); + } else if ($tagName == 'fieldConfiguration') { + $data[$tagName] = self::readQgisMapLayerFieldConfiguration($oXmlReader); + } else if ($tagName == 'aliases') { + $data[$tagName] = self::readQgisMapLayerAliases($oXmlReader); + } else if ($tagName == 'defaults') { + $data[$tagName] = self::readQgisMapLayerDefaults($oXmlReader); + } else if ($tagName == 'constraints') { + $data[$tagName] = self::readQgisMapLayerConstraints($oXmlReader); + } else if ($tagName == 'constraintExpressions') { + $data[$tagName] = self::readQgisMapLayerConstraintExpressions($oXmlReader); + } else if ($tagName == 'excludeAttributesWFS' + || $tagName == 'excludeAttributesWMS') { + $data[$tagName] = self::readAttributes($oXmlReader); + } else if ($tagName == 'attributetableconfig') { + $data[$tagName] = self::readQgisMapLayerAttributeTableConfig($oXmlReader); + } else if ($tagName == 'vectorjoins') { + $data[$tagName] = self::readQgisMapLayerVectorJoins($oXmlReader); + } else { + $data[$tagName] = $oXmlReader->readString(); + } + } + + return $data; + } + + /** + * @param \XMLReader $xml + * + * @return Array + */ + public static function readQgisMapLayerSrs($oXmlReader) + { + $localName = 'srs'; + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + if ($oXmlReader->localName != $localName) { + throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); + } + + $depth = $oXmlReader->depth; + $data = array(); + while ($oXmlReader->read()) { + + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'spatialrefsys') { + $data = self::readSpatialRefSys($oXmlReader); + } + } + return $data; + } + + /** + * @param \XMLReader $xml + * + * @return Array + */ + public static function readQgisMapLayerStyleManager($oXmlReader) + { + $localName = 'map-layer-style-manager'; + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + if ($oXmlReader->localName != $localName) { + throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); + } + + $depth = $oXmlReader->depth; + $data = array( + 'current' => $oXmlReader->getAttribute('current'), + 'styles' => array(), + ); + while ($oXmlReader->read()) { + + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'map-layer-style') { + $data['styles'][] = $oXmlReader->getAttribute('name'); + } + } + return $data; + } + + /** + * @param \XMLReader $xml + * + * @return Array + */ + public static function readQgisMapLayerFieldConfiguration($oXmlReader) + { + $localName = 'fieldConfiguration'; + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + if ($oXmlReader->localName != $localName) { + throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); + } + + $depth = $oXmlReader->depth; + $data = array(); + while ($oXmlReader->read()) { + + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'field') { + $data[] = self::readQgisMapLayerField($oXmlReader); + } + } + return $data; + } + + /** + * @param \XMLReader $xml + * + * @return Array + */ + public static function readQgisMapLayerField($oXmlReader) + { + $localName = 'field'; + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + if ($oXmlReader->localName != $localName) { + throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); + } + + $depth = $oXmlReader->depth; + $data = array( + 'name' => $oXmlReader->getAttribute('name'), + 'configurationFlags' => $oXmlReader->getAttribute('configurationFlags'), + ); + while ($oXmlReader->read()) { + + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'editWidget') { + $data['editWidget'] = self::readQgisMapLayerEditWidget($oXmlReader); + } + } + return $data; + } + + /** + * @param \XMLReader $xml + * + * @return Array + */ + public static function readQgisMapLayerEditWidget($oXmlReader) + { + $localName = 'editWidget'; + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + if ($oXmlReader->localName != $localName) { + throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); + } + + $depth = $oXmlReader->depth; + $data = array( + 'type' => $oXmlReader->getAttribute('type'), + ); + while ($oXmlReader->read()) { + + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'config') { + $data['config'] = self::readQgisMapLayerEditWidgetConfig($oXmlReader); + } + } + return $data; + } + + /** + * @param \XMLReader $xml + * + * @return Array + */ + public static function readQgisMapLayerEditWidgetConfig($oXmlReader) + { + $localName = 'config'; + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + if ($oXmlReader->localName != $localName) { + throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); + } + + $depth = $oXmlReader->depth; + $data = array(); + while ($oXmlReader->read()) { + + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'Option') { + $data[] = self::readOption($oXmlReader); + } + } + return $data; + /* + + + + + + + + + + */ + } + + /** + * @param \XMLReader $xml + * + * @return Array + */ + public static function readQgisMapLayerAliases($oXmlReader) + { + $localName = 'aliases'; + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + if ($oXmlReader->localName != $localName) { + throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); + } + + $depth = $oXmlReader->depth; + $data = array(); + while ($oXmlReader->read()) { + + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'alias') { + $data[] = array( + 'index' => (int) $oXmlReader->getAttribute('index'), + 'field' => $oXmlReader->getAttribute('field'), + 'name' => $oXmlReader->getAttribute('name'), + ); + } + } + return $data; + } + + /** + * @param \XMLReader $xml + * + * @return Array + */ + public static function readQgisMapLayerDefaults($oXmlReader) + { + $localName = 'defaults'; + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + if ($oXmlReader->localName != $localName) { + throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); + } + + $depth = $oXmlReader->depth; + $data = array(); + while ($oXmlReader->read()) { + + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'default') { + $data[] = array( + 'field' => $oXmlReader->getAttribute('field'), + 'expression' => $oXmlReader->getAttribute('expression'), + 'applyOnUpdate' => filter_var($oXmlReader->getAttribute('applyOnUpdate'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE), + ); + } + } + return $data; + } + + /** + * @param \XMLReader $xml + * + * @return Array + */ + public static function readQgisMapLayerConstraints($oXmlReader) + { + $localName = 'constraints'; + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + if ($oXmlReader->localName != $localName) { + throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); + } + + $depth = $oXmlReader->depth; + $data = array(); + while ($oXmlReader->read()) { + + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'constraint') { + $data[] = array( + 'field' => $oXmlReader->getAttribute('field'), + 'constraints' => (int) $oXmlReader->getAttribute('constraints'), + 'notnull_strength' => filter_var($oXmlReader->getAttribute('notnull_strength'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE), + 'unique_strength' => filter_var($oXmlReader->getAttribute('unique_strength'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE), + 'exp_strength' => filter_var($oXmlReader->getAttribute('exp_strength'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE), + ); + } + } + + return $data; + } + + /** + * @param \XMLReader $xml + * + * @return Array + */ + public static function readQgisMapLayerConstraintExpressions($oXmlReader) + { + $localName = 'constraintExpressions'; + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + if ($oXmlReader->localName != $localName) { + throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); + } + + $depth = $oXmlReader->depth; + $data = array(); + while ($oXmlReader->read()) { + + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'constraint') { + $data[] = array( + 'field' => $oXmlReader->getAttribute('field'), + 'exp' => $oXmlReader->getAttribute('exp'), + 'desc' => $oXmlReader->getAttribute('desc'), + ); + } + } + + return $data; + } + + /** + * @param \XMLReader $xml + * + * @return Array + */ + public static function readQgisMapLayerAttributeTableConfig($oXmlReader) + { + $localName = 'attributetableconfig'; + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + if ($oXmlReader->localName != $localName) { + throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); + } + + $depth = $oXmlReader->depth; + $data = array(); + while ($oXmlReader->read()) { + + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'columns') { + $data[$oXmlReader->localName] = self::readQgisMapLayerAttributeTableColumns($oXmlReader); + } + } + + return $data; + } + + /** + * @param \XMLReader $xml + * + * @return Array + */ + public static function readQgisMapLayerAttributeTableColumns($oXmlReader) + { + $localName = 'columns'; + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + if ($oXmlReader->localName != $localName) { + throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); + } + + $depth = $oXmlReader->depth; + $data = array(); + while ($oXmlReader->read()) { + + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'column') { + $data[] = array( + 'type' => $oXmlReader->getAttribute('type'), + 'name' => $oXmlReader->getAttribute('name'), + 'hidden' => filter_var($oXmlReader->getAttribute('hidden'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE), + ); + } + } + + return $data; + } + + /** + * @param \XMLReader $xml + * + * @return Array + */ + public static function readQgisMapLayerVectorJoins($oXmlReader) + { + $localName = 'vectorjoins'; + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + if ($oXmlReader->localName != $localName) { + throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); + } + + $depth = $oXmlReader->depth; + $data = array(); + while ($oXmlReader->read()) { + + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'join') { + $data[] = array( + 'joinLayerId' => $oXmlReader->getAttribute('joinLayerId'), + 'joinFieldName' => $oXmlReader->getAttribute('joinFieldName'), + 'targetFieldName' => $oXmlReader->getAttribute('targetFieldName'), + ); + } + } + + return $data; + } + + /** + * @param \XMLReader $xml + * + * @return Array + */ + public static function readQgisLayouts($oXmlReader) + { + $localName = 'Layouts'; + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + if ($oXmlReader->localName != $localName) { + throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); + } + + $depth = $oXmlReader->depth; + $data = array(); + while ($oXmlReader->read()) { + + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'Layout') { + $data[] = self::readQgisLayout($oXmlReader); + } + } + + return $data; + } + + /** + * @param \XMLReader $xml + * + * @return Array + */ + public static function readQgisLayout($oXmlReader) + { + $localName = 'Layout'; + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + if ($oXmlReader->localName != $localName) { + throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); + } + + $depth = $oXmlReader->depth; + $data = array( + 'name' => $oXmlReader->getAttribute('name'), + 'labels' => array(), + 'maps' => array(), + ); + while ($oXmlReader->read()) { + + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'PageCollection') { + $data['pages'] = self::readQgisLayoutPageCollection($oXmlReader); + } else if ($oXmlReader->localName == 'LayoutItem') { + $item = self::readQgisLayoutItem($oXmlReader); + + if (!array_key_exists('typeName', $item)) { + continue; + } + + if ($item['typeName'] === 'label' && $item['id'] !== '') { + $data['labels'][] = $item; + } else if ($item['typeName'] === 'map') { + $item['id'] = 'map'.(string) count($data['maps']); + $data['maps'][] = $item; + } + } else if ($oXmlReader->localName == 'Atlas') { + $enabled = filter_var($oXmlReader->getAttribute('enabled'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + if ($enabled) { + $data['atlas'] = array( + 'enabled' => $enabled, + 'coverageLayer' => $oXmlReader->getAttribute('coverageLayer'), + ); + } + } + } + + return $data; + } + + /** + * @param \XMLReader $xml + * + * @return Array + */ + public static function readQgisLayoutPageCollection($oXmlReader) + { + $localName = 'PageCollection'; + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + if ($oXmlReader->localName != $localName) { + throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); + } + + $depth = $oXmlReader->depth; + $data = array(); + while ($oXmlReader->read()) { + + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'LayoutItem') { + $item = self::readQgisLayoutItem($oXmlReader); + + if (!array_key_exists('typeName', $item)) { + continue; + } + + if ($item['typeName'] !== 'page') { + continue; + } + + $data[] = $item; + } + } + return $data; + } + + /** + * @param \XMLReader $xml + * + * @return Array + */ + public static function readQgisLayoutItem($oXmlReader) + { + $localName = 'LayoutItem'; + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + if ($oXmlReader->localName != $localName) { + throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); + } + + $depth = $oXmlReader->depth; + $data = array( + 'type' => $oXmlReader->getAttribute('type'), + ); + + if ($data['type'] == '65638') { + $pageSize = explode(',', $oXmlReader->getAttribute('size')); + $pagePosition = explode(',', $oXmlReader->getAttribute('position')); + $data += array( + 'typeName' => 'page', + 'width' => (int) $pageSize[0], + 'height' => (int) $pageSize[1], + 'x' => (int) $pagePosition[0], + 'y' => (int) $pagePosition[1], + ); + } else if ($data['type'] == '65639') { + $mapSize = explode(',', $oXmlReader->getAttribute('size')); + $data += array( + 'typeName' => 'map', + 'uuid' => $oXmlReader->getAttribute('uuid'), + 'width' => (int) $mapSize[0], + 'height' => (int) $mapSize[1], + 'grid' => False, + ); + } else if ($data['type'] == '65641') { + $data += array( + 'typeName' => 'label', + 'id' => $oXmlReader->getAttribute('id'), + 'htmlState' => filter_var($oXmlReader->getAttribute('htmlState'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE), + 'text' => $oXmlReader->getAttribute('labelText'), + ); + } + + while ($oXmlReader->read()) { + + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($data['type'] !== '65639') { + continue; + } + + if ($oXmlReader->localName == 'ComposerMapOverview') { + $show = filter_var($oXmlReader->getAttribute('show'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + $frameMap = $oXmlReader->getAttribute('frameMap'); + if ($show && $frameMap !== '-1') { + $data += array( + 'overviewMap' => $frameMap, + ); + } + } else if ($oXmlReader->localName == 'ComposerMapGrid') { + $data += array( + 'grid' => filter_var($oXmlReader->getAttribute('show'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE), + ); + } + } + + return $data; + } + + /** + * @param \XMLReader $xml + * + * @return Array + */ + public static function readSpatialRefSys($oXmlReader) + { + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + if ($oXmlReader->localName != 'spatialrefsys') { + throw new \Exception('Provide a `spatialrefsys` element not `'.$oXmlReader->localName.'`!'); + } + + $depth = $oXmlReader->depth; + $data = array(); + $tagNames = array( + 'proj4', + 'srid', + 'authid', + 'description', + ); + while ($oXmlReader->read()) { + + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == 'spatialrefsys' + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + $tagName = $oXmlReader->localName; + if (!in_array($tagName, $tagNames) + || $oXmlReader->depth != $depth + 1) { + continue; + } + + if ($tagName == 'srid') { + $data[$tagName] = (int) $oXmlReader->readString(); + } else { + $data[$tagName] = $oXmlReader->readString(); + } + } + + return $data; + } + + /** + * @param \XMLReader $xml + * + * @return Array + */ + public static function readAttributes($oXmlReader) + { + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + $data = array(); + if ($oXmlReader->isEmptyElement) { + return $data; + } + + $localName = $oXmlReader->localName; + $depth = $oXmlReader->depth; + while ($oXmlReader->read()) { + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'attribute') { + $values[] = $oXmlReader->readString(); + } + } + return $values; + } + + /** + * @param \XMLReader $xml + * + * @return Array + */ + public static function readItems($oXmlReader) + { + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + + $localName = $oXmlReader->localName; + $depth = $oXmlReader->depth; + $values = array(); + while ($oXmlReader->read()) { + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'item') { + $values[] = $oXmlReader->readString(); + } + } + return $values; + } + + /** + * @param \XMLReader $xml + * + * @return Array + */ + public static function readValues($oXmlReader) + { + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + + $localName = $oXmlReader->localName; + $depth = $oXmlReader->depth; + $values = array(); + while ($oXmlReader->read()) { + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'value') { + $values[] = $oXmlReader->readString(); + } + } + return $values; + } + + public const MAP_VALUES_AS_VALUES = 0; + public const MAP_VALUES_AS_KEYS = 1; + public const MAP_ONLY_VALUES = 2; + + /** + * @param \XMLReader $xml + * @param int $extraction + * + * @return Array + */ + public static function readOption($oXmlReader, $extraction = QgisProjectParser::MAP_VALUES_AS_VALUES) + { + $localName = 'Option'; + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + if ($oXmlReader->localName != $localName) { + throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); + } + + $depth = $oXmlReader->depth; + $type = $oXmlReader->getAttribute('type'); + $name = $oXmlReader->getAttribute('name'); + $data = array(); + if (!$type && !$name) { + return $data; + } + if ($type == 'Map' || $type == 'List' || $type == 'StringList') { + if ($name == 'map') { + $extraction = self::MAP_VALUES_AS_KEYS; + } + if ($type == 'StringList') { + $extraction = self::MAP_ONLY_VALUES; + } + $options = array(); + while ($oXmlReader->read()) { + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->localName == $localName + && $oXmlReader->depth == $depth + 1) { + if ($extraction == self::MAP_ONLY_VALUES) { + $options = array_merge($options, self::readOption($oXmlReader, $extraction)); + } else { + $options += self::readOption($oXmlReader, $extraction); + } + } + } + if (!$name) { + $data = $options; + } else { + $data[$name] = $options; + } + } else { + $value = $oXmlReader->getAttribute('value'); + if ($type == 'bool'){ + $value = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + } else if ($type == 'int'){ + $value = (int) $value; + } + if ($extraction == self::MAP_ONLY_VALUES) { + $data[] = $value; + } else if ($extraction == self::MAP_VALUES_AS_KEYS) { + $data[$value] = $name; + } else if ($name) { + $data[$name] = $value; + } else { + $data[] = $value; + } + } + return $data; + } +} diff --git a/tests/units/classes/Project/QgisProjectParserTest.php b/tests/units/classes/Project/QgisProjectParserTest.php new file mode 100644 index 0000000000..4556b03ca8 --- /dev/null +++ b/tests/units/classes/Project/QgisProjectParserTest.php @@ -0,0 +1,2527 @@ +assertEquals(XMLReader::ELEMENT, $oXml->nodeType); + $this->assertEquals(0, $oXml->depth); + $this->assertEquals('qgis', $oXml->localName); + $this->assertEquals('3.10.5-A Coruña', $oXml->getAttribute('version')); + } + + public function testReadQgisGuiProperties() + { + $xmlStr = ' + + 255 + 255 + 255 + 255 + 0 + 255 + 255 + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $data = Project\QgisProjectParser::readQgisGuiProperties($oXml); + $expected = array( + 'CanvasColorBluePart' => 255, + 'CanvasColorGreenPart' => 255, + 'CanvasColorRedPart' => 255, + 'SelectionColorAlphaPart' => 255, + 'SelectionColorBluePart' => 0, + 'SelectionColorGreenPart' => 255, + 'SelectionColorRedPart' => 255, + ); + $this->assertEquals($expected, $data); + } + + public function testReadQgisVariablesProperties() + { + /*$oXml = new XMLReader(); + $xml_path = __DIR__.'/../Project/Ressources/montpellier.qgs'; + $oXml->open($xml_path); + + $data = array(); + while($oXml->read()){ + if($oXml->nodeType == XMLReader::ELEMENT + && $oXml->localName == 'Variables' + && $oXml->depth == 2) { + $data = Project\QgisProjectParser::readQgisVariablesProperties($oXml); + break; + } + }*/ + $xmlStr = ' + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $data = Project\QgisProjectParser::readQgisVariablesProperties($oXml); + $expected = array( + 'variableNames' => array(), + 'variableValues' => array(), + ); + $this->assertEquals($expected, $data); + + $xmlStr = ' + + + lizmap_user + lizmap_user_groups + + + lizmap + lizmap-group + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $data = Project\QgisProjectParser::readQgisVariablesProperties($oXml); + $expected = array( + 'variableNames' => array( + 'lizmap_user', + 'lizmap_user_groups', + ), + 'variableValues' => array( + 'lizmap', + 'lizmap-group', + ), + ); + $this->assertEquals($expected, $data); + } + + public function testReadQgisProperties() + { + $oXml = new XMLReader(); + $xml_path = __DIR__.'/../Project/Ressources/montpellier.qgs'; + $oXml->open($xml_path); + + $data = array(); + while($oXml->read()){ + if($oXml->nodeType == XMLReader::ELEMENT + && $oXml->localName == 'properties' + && $oXml->depth == 1) { + $data = Project\QgisProjectParser::readQgisProperties($oXml); + break; + } + } + $expected = array( + 'WMSServiceTitle' => 'Montpellier - Transports', + 'WMSServiceAbstract' => 'Demo project with bus and tramway lines in Montpellier, France. +Data is licensed under ODbl, OpenStreetMap contributors', + 'WMSKeywordList' => array(''), + 'WMSExtent' => array('417006.61373760335845873', '5394910.34090302512049675', '447158.04891100589884445', '5414844.99480544030666351'), + // 'ProjectCrs' => 'EPSG:3857', + 'WMSOnlineResource' => 'http://www.3liz.com/lizmap.html', + 'WMSContactMail' => 'info@3liz.com', + 'WMSContactOrganization' => '3liz', + 'WMSContactPerson' => '3liz', + 'WMSContactPhone' => '+334 67 16 64 51', + 'WMSRestrictedComposers' => array('Composeur1'), + 'WMSUseLayerIDs' => false, + ); + $this->assertEquals($expected, $data); + + $oXml = new XMLReader(); + $xml_path = __DIR__.'/../Project/Ressources/WMSInfotest.qgs'; + $oXml->open($xml_path); + + $data = array(); + while($oXml->read()){ + if($oXml->nodeType == XMLReader::ELEMENT + && $oXml->localName == 'properties' + && $oXml->depth == 1) { + $data = Project\QgisProjectParser::readQgisProperties($oXml); + break; + } + } + $expected = array( + 'WMSServiceTitle' => 'Touristic events around Montpellier, France', + 'WMSServiceAbstract' => '', + 'WMSKeywordList' => array(''), + 'WMSExtent' => array('390483.99668047408340499', '5375009.91444000415503979', '477899.4732063576229848', '5436768.56305211596190929'), + 'WMSOnlineResource' => '', + 'WMSContactMail' => '', + 'WMSContactOrganization' => '', + 'WMSContactPerson' => '', + 'WMSContactPhone' => '', + 'WMSRestrictedComposers' => array(), + 'WMSUseLayerIDs' => false, + ); + $this->assertEquals($expected, $data); + } + + public function testReadQgisVisibilityPresets() + { + $oXml = new XMLReader(); + $xml_path = __DIR__.'/../Project/Ressources/themes-3_22.qgs'; + $oXml->open($xml_path); + + $data = array(); + while($oXml->read()){ + if($oXml->nodeType == XMLReader::ELEMENT + && $oXml->localName == 'visibility-presets' + && $oXml->depth == 1) { + $data = Project\QgisProjectParser::readQgisVisibilityPresets($oXml); + break; + } + } + $expected = array( + array( + 'name'=>'theme1', + 'layers' => array( + array( + 'id' => 'quartiers_ef5b13e3_36db_4e0d_98b3_990de580367d', + 'visible' => true, + 'style' => 'style1', + 'expanded' => true, + ), + ), + 'checkedGroupNodes' => array(), + 'expandedGroupNodes' => array( + 'group1' + ), + ), + array( + 'name'=>'theme2', + 'layers' => array( + array( + 'id' => 'sousquartiers_7c49d0fc_0ee0_4308_a66d_45c144e59872', + 'visible' => true, + 'style' => 'défaut', + 'expanded' => true, + ), + array( + 'id' => 'quartiers_ef5b13e3_36db_4e0d_98b3_990de580367d', + 'visible' => true, + 'style' => 'style2', + 'expanded' => true, + ), + ), + 'checkedGroupNodes' => array( + 'group1' + ), + 'expandedGroupNodes' => array( + 'group1' + ), + + ), + ); + $this->assertEquals($expected, $data); + + $oXml = new XMLReader(); + $xml_path = __DIR__.'/../Project/Ressources/themes-3_26.qgs'; + $oXml->open($xml_path); + + $data = array(); + while($oXml->read()){ + if($oXml->nodeType == XMLReader::ELEMENT + && $oXml->localName == 'visibility-presets' + && $oXml->depth == 1) { + $data = Project\QgisProjectParser::readQgisVisibilityPresets($oXml); + break; + } + } + $expected = array( + array( + 'name'=>'theme1', + 'layers' => array( + array( + 'id' => 'sousquartiers_7c49d0fc_0ee0_4308_a66d_45c144e59872', + 'visible' => false, + 'style' => 'défaut', + 'expanded' => true, + ), + array( + 'id' => 'quartiers_ef5b13e3_36db_4e0d_98b3_990de580367d', + 'visible' => true, + 'style' => 'style1', + 'expanded' => true, + ), + ), + 'checkedGroupNodes' => array(), + 'expandedGroupNodes' => array( + 'group1' + ), + ), + array( + 'name'=>'theme2', + 'layers' => array( + array( + 'id' => 'sousquartiers_7c49d0fc_0ee0_4308_a66d_45c144e59872', + 'visible' => true, + 'style' => 'défaut', + 'expanded' => true, + ), + array( + 'id' => 'quartiers_ef5b13e3_36db_4e0d_98b3_990de580367d', + 'visible' => true, + 'style' => 'style2', + 'expanded' => true, + ), + ), + 'checkedGroupNodes' => array( + 'group1' + ), + 'expandedGroupNodes' => array( + 'group1' + ), + + ), + ); + $this->assertEquals($expected, $data); + } + + public function testReadQgisMapLayer() + { + $oXml = new XMLReader(); + $xml_path = __DIR__.'/../Project/Ressources/montpellier.qgs'; + $oXml->open($xml_path); + + $data = array(); + while($oXml->read()){ + if($oXml->nodeType == XMLReader::ELEMENT + && $oXml->localName == 'maplayer' + && $oXml->depth == 2) { + $data = Project\QgisProjectParser::readQgisMapLayer($oXml); + break; + } + } + $expected = array( + 'type' => 'vector', + 'embedded' => false, + 'id' => 'SousQuartiers20160121124316563', + 'layername' => 'SousQuartiers', + 'srs' => array( + 'proj4' => '+proj=lcc +lat_1=49 +lat_2=44 +lat_0=46.5 +lon_0=3 +x_0=700000 +y_0=6600000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs', + 'srid' => 2154, + 'authid' => 'EPSG:2154', + 'description' => 'RGF93 / Lambert-93', + ), + 'datasource' => './data/vector/VilleMTP_MTP_SousQuartiers_2011.shp', + 'provider' => 'ogr', + 'keywordList' => array(''), + ); + $this->assertEquals($expected, $data); + } + + public function testReadSpatialRefSys() + { + $oXml = new XMLReader(); + $xml_path = __DIR__.'/../Project/Ressources/montpellier.qgs'; + $oXml->open($xml_path); + + $data = array(); + // qgis/projectlayers/maplayer/srs/spatialrefsys + while($oXml->read()){ + if($oXml->nodeType == XMLReader::ELEMENT + && $oXml->localName == 'maplayer' + && $oXml->depth == 2) { + while($oXml->read()){ + if($oXml->nodeType == XMLReader::ELEMENT + && $oXml->localName == 'spatialrefsys' + && $oXml->depth == 4) { + $data = Project\QgisProjectParser::readSpatialRefSys($oXml); + break; + } + } + break; + } + } + $expected = array( + 'proj4' => '+proj=lcc +lat_1=49 +lat_2=44 +lat_0=46.5 +lon_0=3 +x_0=700000 +y_0=6600000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs', + 'srid' => 2154, + 'authid' => 'EPSG:2154', + 'description' => 'RGF93 / Lambert-93', + ); + $this->assertEquals($expected, $data); + } + + public function testReadQgisMapLayerAliases() + { + $oXml = new XMLReader(); + $xml_path = __DIR__.'/../Project/Ressources/montpellier.qgs'; + $oXml->open($xml_path); + + $data = array(); + while($oXml->read()){ + if($oXml->nodeType == XMLReader::ELEMENT + && $oXml->localName == 'maplayer' + && $oXml->depth == 2) { + while($oXml->read()){ + if($oXml->nodeType == XMLReader::ELEMENT + && $oXml->localName == 'aliases' + && $oXml->depth == 3) { + $data = Project\QgisProjectParser::readQgisMapLayerAliases($oXml); + break; + } + } + break; + } + } + $this->assertCount(11, $data); + $expected = array( + array( + 'index' => 0, + 'field' => 'QUARTMNO', + 'name' => 'District', + ), + array( + 'index' => 1, + 'field' => 'SQUARTMNO', + 'name' => 'Code', + ), + array( + 'index' => 2, + 'field' => 'LIBSQUART', + 'name' => 'Name', + ), + array( + 'index' => 3, + 'field' => 'socio_population_2009', + 'name' => 'Population (2009)', + ), + array( + 'index' => 4, + 'field' => 'socio_population_percentage', + 'name' => '% population Montpellier', + ), + array( + 'index' => 5, + 'field' => 'socio_average_income', + 'name' => 'Average income (€)', + ), + array( + 'index' => 6, + 'field' => 'socio_prop_percentage', + 'name' => 'Owners (%)', + ), + array( + 'index' => 7, + 'field' => 'socio_loc_percentage', + 'name' => 'Tenants (%)', + ), + array( + 'index' => 8, + 'field' => 'Quartiers_LIBQUART', + 'name' => 'District', + ), + array( + 'index' => 9, + 'field' => 'popdensity', + 'name' => 'Density ( inhabitants / km2 )', + ), + array( + 'index' => 10, + 'field' => 'area', + 'name' => 'Area ( km2)', + ), + ); + $this->assertEquals($expected, $data); + } + + public function testReadQgisMapLayerDefaults() + { + $oXml = new XMLReader(); + $xml_path = __DIR__.'/../Project/Ressources/montpellier.qgs'; + $oXml->open($xml_path); + + $data = array(); + while($oXml->read()){ + if($oXml->nodeType == XMLReader::ELEMENT + && $oXml->localName == 'maplayer' + && $oXml->depth == 2) { + while($oXml->read()){ + if($oXml->nodeType == XMLReader::ELEMENT + && $oXml->localName == 'defaults' + && $oXml->depth == 3) { + $data = Project\QgisProjectParser::readQgisMapLayerDefaults($oXml); + break; + } + } + break; + } + } + $this->assertCount(11, $data); + $expected = array( + 'field' => 'QUARTMNO', + 'expression' => '', + 'applyOnUpdate' => false, + ); + $this->assertEquals($expected, $data[0]); + $expected = array( + 'field' => 'socio_average_income', + 'expression' => '', + 'applyOnUpdate' => false, + ); + $this->assertEquals($expected, $data[5]); + $expected = array( + 'field' => 'area', + 'expression' => '', + 'applyOnUpdate' => false, + ); + $this->assertEquals($expected, $data[10]); + } + + public function testReadQgisMapLayerConstraints() + { + $oXml = new XMLReader(); + $xml_path = __DIR__.'/../Project/Ressources/montpellier.qgs'; + $oXml->open($xml_path); + + $data = array(); + while($oXml->read()){ + if($oXml->nodeType == XMLReader::ELEMENT + && $oXml->localName == 'maplayer' + && $oXml->depth == 2) { + while($oXml->read()){ + if($oXml->nodeType == XMLReader::ELEMENT + && $oXml->localName == 'constraints' + && $oXml->depth == 3) { + $data = Project\QgisProjectParser::readQgisMapLayerConstraints($oXml); + break; + } + } + break; + } + } + $this->assertCount(11, $data); + $expected = array( + 'field' => 'QUARTMNO', + 'constraints' => 0, + 'notnull_strength' => false, + 'unique_strength' => false, + 'exp_strength' => false, + ); + $this->assertEquals($expected, $data[0]); + $expected = array( + 'field' => 'socio_average_income', + 'constraints' => 0, + 'notnull_strength' => false, + 'unique_strength' => false, + 'exp_strength' => false, + ); + $this->assertEquals($expected, $data[5]); + $expected = array( + 'field' => 'area', + 'constraints' => 0, + 'notnull_strength' => false, + 'unique_strength' => false, + 'exp_strength' => false, + ); + $this->assertEquals($expected, $data[10]); + } + + public function testReadQgisMapLayerConstraintExpressions() + { + $oXml = new XMLReader(); + $xml_path = __DIR__.'/../Project/Ressources/montpellier.qgs'; + $oXml->open($xml_path); + + $data = array(); + while($oXml->read()){ + if($oXml->nodeType == XMLReader::ELEMENT + && $oXml->localName == 'maplayer' + && $oXml->depth == 2) { + while($oXml->read()){ + if($oXml->nodeType == XMLReader::ELEMENT + && $oXml->localName == 'constraintExpressions' + && $oXml->depth == 3) { + $data = Project\QgisProjectParser::readQgisMapLayerConstraintExpressions($oXml); + break; + } + } + break; + } + } + $this->assertCount(11, $data); + $expected = array( + 'field' => 'QUARTMNO', + 'exp' => '', + 'desc' => '', + ); + $this->assertEquals($expected, $data[0]); + $expected = array( + 'field' => 'socio_average_income', + 'exp' => '', + 'desc' => '', + ); + $this->assertEquals($expected, $data[5]); + $expected = array( + 'field' => 'area', + 'exp' => '', + 'desc' => '', + ); + $this->assertEquals($expected, $data[10]); + } + + public function testReadQgisMapLayerStyleManager() + { + $xmlStr = ' + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $data = Project\QgisProjectParser::readQgisMapLayerStyleManager($oXml); + $this->assertTrue(is_array($data)); + $expected = array( + 'current' => 'default', + 'styles' => array('default'), + ); + $this->assertEquals($expected, $data); + + $xmlStr = ' + + + + + + + + + + + + + + + + + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $data = Project\QgisProjectParser::readQgisMapLayerStyleManager($oXml); + $this->assertTrue(is_array($data)); + $expected = array( + 'current' => 'black', + 'styles' => array( + 'black', + 'colored', + ), + ); + $this->assertEquals($expected, $data); + } + + public function testReadQgisMapLayerAttributeTableColumns() + { + $xmlStr = ' + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $data = Project\QgisProjectParser::readQgisMapLayerAttributeTableColumns($oXml); + $this->assertTrue(is_array($data)); + $this->assertCount(15, $data); + $expected = array( + array( + 'type' => 'field', + 'name' => 'nid', + 'hidden' => false, + ), + array( + 'type' => 'field', + 'name' => 'titre', + 'hidden' => false, + ), + array( + 'type' => 'field', + 'name' => 'vignette_src', + 'hidden' => true, + ), + array( + 'type' => 'field', + 'name' => 'vignette_alt', + 'hidden' => true, + ), + array( + 'type' => 'field', + 'name' => 'field_date', + 'hidden' => false, + ), + array( + 'type' => 'field', + 'name' => 'description', + 'hidden' => true, + ), + array( + 'type' => 'field', + 'name' => 'field_communes', + 'hidden' => false, + ), + array( + 'type' => 'field', + 'name' => 'field_lieu', + 'hidden' => false, + ), + array( + 'type' => 'field', + 'name' => 'field_access', + 'hidden' => false, + ), + array( + 'type' => 'field', + 'name' => 'field_thematique', + 'hidden' => false, + ), + array( + 'type' => 'field', + 'name' => 'x', + 'hidden' => true, + ), + array( + 'type' => 'field', + 'name' => 'y', + 'hidden' => true, + ), + array( + 'type' => 'field', + 'name' => 'url', + 'hidden' => false, + ), + array( + 'type' => 'actions', + 'name' => null, + 'hidden' => true, + ), + array( + 'type' => 'field', + 'name' => 'fid', + 'hidden' => false, + ), + ); + $this->assertEquals($expected, $data); + } + + public function testReadQgisMapLayerAttributeTableConfig() + { + $xmlStr = ' + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $data = Project\QgisProjectParser::readQgisMapLayerAttributeTableConfig($oXml); + $this->assertTrue(is_array($data)); + $this->assertTrue(array_key_exists('columns', $data)); + $this->assertCount(15, $data['columns']); + $expected = array( + 'columns' => array( + array( + 'type' => 'field', + 'name' => 'nid', + 'hidden' => false, + ), + array( + 'type' => 'field', + 'name' => 'titre', + 'hidden' => false, + ), + array( + 'type' => 'field', + 'name' => 'vignette_src', + 'hidden' => true, + ), + array( + 'type' => 'field', + 'name' => 'vignette_alt', + 'hidden' => true, + ), + array( + 'type' => 'field', + 'name' => 'field_date', + 'hidden' => false, + ), + array( + 'type' => 'field', + 'name' => 'description', + 'hidden' => true, + ), + array( + 'type' => 'field', + 'name' => 'field_communes', + 'hidden' => false, + ), + array( + 'type' => 'field', + 'name' => 'field_lieu', + 'hidden' => false, + ), + array( + 'type' => 'field', + 'name' => 'field_access', + 'hidden' => false, + ), + array( + 'type' => 'field', + 'name' => 'field_thematique', + 'hidden' => false, + ), + array( + 'type' => 'field', + 'name' => 'x', + 'hidden' => true, + ), + array( + 'type' => 'field', + 'name' => 'y', + 'hidden' => true, + ), + array( + 'type' => 'field', + 'name' => 'url', + 'hidden' => false, + ), + array( + 'type' => 'actions', + 'name' => null, + 'hidden' => true, + ), + array( + 'type' => 'field', + 'name' => 'fid', + 'hidden' => false, + ), + ), + ); + $this->assertEquals($expected, $data); + } + + public function testReadQgisMapLayerVectorJoins() + { + $xmlStr = ' + + + + + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $data = Project\QgisProjectParser::readQgisMapLayerVectorJoins($oXml); + $this->assertTrue(is_array($data)); + $this->assertCount(2, $data); + $expected = array( + array( + 'joinLayerId' => 'donnes_sociodemo_sous_quartiers20160121144525075', + 'joinFieldName' => 'squartmno', + 'targetFieldName' => 'SQUARTMNO', + ), + array( + 'joinLayerId' => 'VilleMTP_MTP_Quartiers_2011_432620130116112610876', + 'joinFieldName' => 'QUARTMNO', + 'targetFieldName' => 'QUARTMNO', + ), + ); + $this->assertEquals($expected, $data); + } + + public function testReadAttributes() + { + $xmlStr = ' + + OGC_FID + wkt + from + html + to + colour + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $values = Project\QgisProjectParser::readAttributes($oXml); + $this->assertTrue(is_array($values)); + $this->assertCount(6, $values); + $expected = array( + 'OGC_FID', + 'wkt', + 'from', + 'html', + 'to', + 'colour', + ); + $this->assertEquals($expected, $values); + } + + public function testReadItems() + { + $xmlStr = ' + + edition_point20130118171631518 + edition_line20130409161630329 + edition_polygon20130409114333776 + bus_stops20121106170806413 + bus20121102133611751 + VilleMTP_MTP_Quartiers_2011_432620130116112610876 + VilleMTP_MTP_Quartiers_2011_432620130116112351546 + tramstop20150328114203878 + tramway20150328114206278 + publicbuildings20150420100958543 + SousQuartiers20160121124316563 + osm_stamen_toner20180315181710198 + osm_mapnik20180315181738526 + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $values = Project\QgisProjectParser::readItems($oXml); + $this->assertTrue(is_array($values)); + $this->assertCount(13, $values); + $this->assertEquals('edition_point20130118171631518', $values[0]); + $this->assertEquals('VilleMTP_MTP_Quartiers_2011_432620130116112610876', $values[5]); + $this->assertEquals('VilleMTP_MTP_Quartiers_2011_432620130116112351546', $values[6]); + $this->assertEquals('osm_mapnik20180315181738526', $values[12]); + } + + public function testReadValues() + { + $xmlStr = ' + + EPSG:2154 + EPSG:4326 + EPSG:3857 + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $values = Project\QgisProjectParser::readValues($oXml); + $this->assertTrue(is_array($values)); + $this->assertCount(3, $values); + $expected = array( + 'EPSG:2154', + 'EPSG:4326', + 'EPSG:3857', + ); + $this->assertEquals($expected, $values); + } + + public function testReadOption() + { + $xmlStr = ' + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $options = Project\QgisProjectParser::readOption($oXml); + $this->assertTrue(is_array($options)); + $this->assertCount(1, $options); + $expectedOptions = array( + 'Zone A' => 'A', + ); + $this->assertEquals($expectedOptions, $options); + + $xmlStr = ' + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $options = Project\QgisProjectParser::readOption($oXml); + $this->assertTrue(is_array($options)); + $this->assertCount(2, $options); + $expectedOptions = array( + 'IsMultiline' => '0', + 'UseHtml' => '0', + ); + $this->assertEquals($expectedOptions, $options); + + $xmlStr = ' + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $options = Project\QgisProjectParser::readOption($oXml); + $this->assertTrue(is_array($options)); + $this->assertCount(2, $options); + $expectedOptions = array( + 'IsMultiline' => 0, + 'UseHtml' => 0, + ); + $this->assertEquals($expectedOptions, $options); + + $xmlStr = ' + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $options = Project\QgisProjectParser::readOption($oXml); + $this->assertTrue(is_array($options)); + $this->assertCount(2, $options); + $expectedOptions = array( + 'IsMultiline' => false, + 'UseHtml' => false, + ); + $this->assertEquals($expectedOptions, $options); + + $xmlStr = ' + + + + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $options = Project\QgisProjectParser::readOption($oXml); + $this->assertTrue(is_array($options)); + $expectedOptions = array( + 'AllowNull' => true, + 'Max' => 2147483647, + 'Min' => -2147483648, + 'Precision' => 0, + 'Step' => 1, + 'Style' => 'SpinBox', + ); + $this->assertEquals($expectedOptions, $options); + + $xmlStr = ' + + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $options = Project\QgisProjectParser::readOption($oXml); + $this->assertTrue(is_array($options)); + $expectedOptions = array( + 'map' => array( + 'A' => 'Zone A', + 'B' => 'Zone B', + '{2839923C-8B7D-419E-B84B-CA2FE9B80EC7}' => 'No Zone', + ), + ); + $this->assertEquals($expectedOptions, $options); + + $xmlStr = ' + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $this->assertEquals('StringList', $oXml->getAttribute('type')); + + $options = Project\QgisProjectParser::readOption($oXml); + $this->assertTrue(is_array($options)); + $expectedOptions = array( + 'Zone A', + 'Zone B', + 'No Zone', + ); + $this->assertEquals($expectedOptions, $options); + + $xmlStr = ' + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $this->assertEquals('Map', $oXml->getAttribute('type')); + + $options = Project\QgisProjectParser::readOption($oXml); + + $this->assertTrue(is_array($options)); + $expectedOptions = array( + 'DocumentViewer' => 1, + 'DocumentViewerHeight' => 0, + 'DocumentViewerWidth' => 0, + 'FileWidget' => true, + 'FileWidgetButton' => true, + 'FileWidgetFilter' => '', + 'PropertyCollection' => array( + 'name' => '', + 'properties' => array( + 'storageUrl' => array ( + 'active' => true, + 'expression' => '\'http://webdav/shapeData/\'||file_name(@selected_file_path)', + 'type' => 3, + ), + ), + 'type' => 'collection', + ), + 'RelativeStorage' => 0, + 'StorageAuthConfigId' => 'k6k7lv8', + 'StorageMode' => 0, + 'StorageType' => 'WebDAV', + ); + $this->assertEquals($expectedOptions, $options); + } + + public function testReadQgisMapLayerEditWidgetConfig() + { + $xmlStr = ' + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $data = Project\QgisProjectParser::readQgisMapLayerEditWidgetConfig($oXml); + $this->assertTrue(is_array($data)); + $expectedOptions = array( + array( + 'IsMultiline' => '0', + 'UseHtml' => '0', + ), + ); + $this->assertEquals($expectedOptions, $data); + } + + public function testReadQgisMapLayerEditWidget() + { + $xmlStr = ' + + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $data = Project\QgisProjectParser::readQgisMapLayerEditWidget($oXml); + $this->assertTrue(is_array($data)); + $expectedOptions = array( + 'type' => 'TextEdit', + 'config' => array( + array( + 'IsMultiline' => '0', + 'UseHtml' => '0', + ), + ), + ); + $this->assertEquals($expectedOptions, $data); + } + + public function testReadQgisMapLayerField() + { + $xmlStr = ' + + + + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $data = Project\QgisProjectParser::readQgisMapLayerField($oXml); + $this->assertTrue(is_array($data)); + $expectedOptions = array( + 'name' => 'OGC_FID', + 'configurationFlags' => 'HideFromWms|HideFromWfs', + 'editWidget' => array( + 'type' => 'TextEdit', + 'config' => array( + array( + 'IsMultiline' => '0', + 'UseHtml' => '0', + ), + ), + ), + ); + $this->assertEquals($expectedOptions, $data); + } + + public function testReadQgisMapLayerFieldConfiguration() + { + $xmlStr = ' + + + + + + + + + + + + + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $data = Project\QgisProjectParser::readQgisMapLayerFieldConfiguration($oXml); + $this->assertTrue(is_array($data)); + $this->assertCount(2, $data); + $expectedOptions = array( + array( + 'name' => 'OGC_FID', + 'configurationFlags' => 'HideFromWms', + 'editWidget' => array( + 'type' => 'TextEdit', + 'config' => array( + array( + 'IsMultiline' => '0', + 'UseHtml' => '0', + ), + ), + ), + ), + array( + 'name' => 'tram_id', + 'configurationFlags' => null, + 'editWidget' => array( + 'type' => 'ValueRelation', + 'config' => array( + array( + 'AllowMulti' => '0', + 'AllowNull' => '1', + 'FilterExpression' => '', + 'Key' => 'osm_id', + 'Layer' => 'tramway20150328114206278', + 'OrderByValue' => '1', + 'UseCompleter' => '0', + 'Value' => 'test', + ), + ), + ), + ), + ); + $this->assertEquals($expectedOptions, $data); + } + + public function testReadQgisCustomOrder() + { + $xmlStr = ' + + edition_point20130118171631518 + edition_line20130409161630329 + edition_polygon20130409114333776 + bus_stops20121106170806413 + bus20121102133611751 + VilleMTP_MTP_Quartiers_2011_432620130116112610876 + VilleMTP_MTP_Quartiers_2011_432620130116112351546 + tramstop20150328114203878 + tramway20150328114206278 + publicbuildings20150420100958543 + SousQuartiers20160121124316563 + osm_stamen_toner20180315181710198 + osm_mapnik20180315181738526 + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $data = Project\QgisProjectParser::readQgisCustomOrder($oXml); + $this->assertTrue(is_array($data)); + $this->assertFalse($data['enabled']); + $this->assertFalse(array_key_exists('items', $data)); + + $xmlStr = ' + + edition_point20130118171631518 + edition_line20130409161630329 + edition_polygon20130409114333776 + bus_stops20121106170806413 + bus20121102133611751 + VilleMTP_MTP_Quartiers_2011_432620130116112610876 + VilleMTP_MTP_Quartiers_2011_432620130116112351546 + tramstop20150328114203878 + tramway20150328114206278 + publicbuildings20150420100958543 + SousQuartiers20160121124316563 + osm_stamen_toner20180315181710198 + osm_mapnik20180315181738526 + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $data = Project\QgisProjectParser::readQgisCustomOrder($oXml); + $this->assertTrue(is_array($data)); + $this->assertTrue($data['enabled']); + $this->assertTrue(array_key_exists('items', $data)); + $this->assertCount(13, $data['items']); + } + + public function testReadQgisProjectCrs() + { + $xmlStr = ' + + + PROJCS["unnamed",GEOGCS["unnamed ellipse",DATUM["unknown",SPHEROID["unnamed",6378137,0],EXTENSION["PROJ4_GRIDS","@null"]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433]],PROJECTION["Mercator_2SP"],PARAMETER["standard_parallel_1",0],PARAMETER["central_meridian",0],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["Meter",1],EXTENSION["PROJ4","+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs"]] + +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs + 100000 + 0 + USER:100000 + * SCR généré (+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs) + merc + + false + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $data = Project\QgisProjectParser::readQgisProjectCrs($oXml); + $this->assertTrue(is_array($data)); + $expected = array( + 'proj4' => '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs', + 'srid' => 0, + 'authid' => 'USER:100000', + 'description' => ' * SCR généré (+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs)', + ); + $this->assertEquals($expected, $data); + } + + public function testReadQgisLayoutItem() + { + $xmlStr = ' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $data = Project\QgisProjectParser::readQgisLayoutItem($oXml); + $this->assertTrue(is_array($data)); + $expected = array( + 'type' => '65638', + 'typeName' => 'page', + 'width' => 297, + 'height' => 210, + 'x' => 0, + 'y' => 0, + ); + $this->assertEquals($expected, $data); + + $xmlStr = ' + + + + + + + + + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $data = Project\QgisProjectParser::readQgisLayoutItem($oXml); + $this->assertTrue(is_array($data)); + $expected = array( + 'type' => '65641', + 'typeName' => 'label', + 'id' => '', + 'htmlState' => false, + 'text' => 'Tram stops in the district', + ); + $this->assertEquals($expected, $data); + + $xmlStr = ' + + + + + + + + + + + + VilleMTP_MTP_Quartiers_2011_432620130116112351546 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $data = Project\QgisProjectParser::readQgisLayoutItem($oXml); + $this->assertTrue(is_array($data)); + $expected = array( + 'type' => '65639', + 'typeName' => 'map', + 'uuid' => '{a50537f9-5e73-4610-955e-31b092d81b94}', + 'width' => 60, + 'height' => 45, + 'grid' => false, + 'overviewMap' => '{a228885d-4d7a-4ae0-af9b-02a4fe7cd814}', + ); + $this->assertEquals($expected, $data); + } + + public function testReadQgisLayout() + { + $xmlStr = ' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + VilleMTP_MTP_Quartiers_2011_432620130116112351546 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $data = Project\QgisProjectParser::readQgisLayout($oXml); + $this->assertTrue(is_array($data)); + $this->assertEquals('District card', $data['name']); + + $this->assertTrue(array_key_exists('pages', $data)); + $this->assertCount(3, $data['pages']); + + $expectedPage = array( + 'type' => '65638', + 'typeName' => 'page', + 'width' => 297, + 'height' => 210, + 'x' => 0, + 'y' => 0, + ); + $this->assertEquals($expectedPage, $data['pages'][0]); + + $expectedPage = array( + 'type' => '65638', + 'typeName' => 'page', + 'width' => 297, + 'height' => 210, + 'x' => 0, + 'y' => 220, + ); + $this->assertEquals($expectedPage, $data['pages'][1]); + + $expectedPage = array( + 'type' => '65638', + 'typeName' => 'page', + 'width' => 297, + 'height' => 210, + 'x' => 0, + 'y' => 440, + ); + $this->assertEquals($expectedPage, $data['pages'][2]); + + $this->assertTrue(array_key_exists('labels', $data)); + $this->assertCount(1, $data['labels']); + $expectedLabel = array( + 'type' => '65641', + 'typeName' => 'label', + 'id' => 'description', + 'htmlState' => false, + 'text' => 'Description', + ); + $this->assertEquals($expectedLabel, $data['labels'][0]); + + $this->assertTrue(array_key_exists('maps', $data)); + $this->assertCount(2, $data['maps']); + + $expectedMap = array( + 'type' => '65639', + 'typeName' => 'map', + 'uuid' => '{a50537f9-5e73-4610-955e-31b092d81b94}', + 'width' => 60, + 'height' => 45, + 'grid' => false, + 'overviewMap' => '{a228885d-4d7a-4ae0-af9b-02a4fe7cd814}', + 'id' => 'map0', + ); + $this->assertEquals($expectedMap, $data['maps'][0]); + + $expectedMap = array( + 'type' => '65639', + 'typeName' => 'map', + 'uuid' => '{a228885d-4d7a-4ae0-af9b-02a4fe7cd814}', + 'width' => 237, + 'height' => 191, + 'grid' => false, + 'id' => 'map1', + ); + $this->assertEquals($expectedMap, $data['maps'][1]); + $this->assertEquals($data['maps'][1]['uuid'], $data['maps'][0]['overviewMap']); + + $this->assertTrue(array_key_exists('atlas', $data)); + $expectedAtlas = array( + 'enabled' => true, + 'coverageLayer' => 'VilleMTP_MTP_Quartiers_2011_432620130116112610876', + ); + $this->assertEquals($expectedAtlas, $data['atlas']); + } + + public function testReadQgisDocument() + { + $xmlStr = ' + + Montpellier - Transports + + + PROJCS["unnamed",GEOGCS["unnamed ellipse",DATUM["unknown",SPHEROID["unnamed",6378137,0],EXTENSION["PROJ4_GRIDS","@null"]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433]],PROJECTION["Mercator_2SP"],PARAMETER["standard_parallel_1",0],PARAMETER["central_meridian",0],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["Meter",1],EXTENSION["PROJ4","+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs"]] + +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs + 100000 + 0 + USER:100000 + * SCR généré (+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs) + merc + + false + + + + + edition_point20130118171631518 + edition_line20130409161630329 + edition_polygon20130409114333776 + bus_stops20121106170806413 + bus20121102133611751 + VilleMTP_MTP_Quartiers_2011_432620130116112610876 + VilleMTP_MTP_Quartiers_2011_432620130116112351546 + tramstop20150328114203878 + tramway20150328114206278 + publicbuildings20150420100958543 + SousQuartiers20160121124316563 + osm_stamen_toner20180315181710198 + osm_mapnik20180315181738526 + + + + + 255 + 255 + 255 + 255 + 0 + 255 + 255 + + + 3857 + +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs + EPSG:3857 + 1 + + + + + + None + true + info@3liz.com + 3liz + 3liz + +334 67 16 64 51 + + + 417006.61373760335845873 + 5394910.34090302512049675 + 447158.04891100589884445 + 5414844.99480544030666351 + + conditions unknown + 90 + + + + http://www.3liz.com/lizmap.html + 8 + + Composeur1 + + + Demo project with bus and tramway lines in Montpellier, France. +Data is licensed under ODbl, OpenStreetMap contributors + true + Montpellier - Transports + + false + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $data = Project\QgisProjectParser::readQgisDocument($oXml); + $this->assertTrue(is_array($data)); + $this->assertTrue(array_key_exists('version', $data)); + $this->assertEquals('3.10.5-A Coruña', $data['version']); + $this->assertTrue(array_key_exists('projectname', $data)); + $this->assertEquals('Montpellier - Transports', $data['projectname']); + $this->assertTrue(array_key_exists('title', $data)); + $this->assertEquals('Montpellier - Transports', $data['title']); + $this->assertTrue(array_key_exists('projectCrs', $data)); + $expectedProjectCrs = array( + 'proj4' => '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs', + 'srid' => 0, + 'authid' => 'USER:100000', + 'description' => ' * SCR généré (+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs)', + ); + $this->assertEquals($expectedProjectCrs, $data['projectCrs']); + $this->assertTrue(array_key_exists('properties', $data)); + $expectedProperties = array( + 'WMSServiceTitle' => 'Montpellier - Transports', + 'WMSServiceAbstract' => 'Demo project with bus and tramway lines in Montpellier, France. +Data is licensed under ODbl, OpenStreetMap contributors', + 'WMSKeywordList' => array(''), + 'WMSExtent' => array('417006.61373760335845873', '5394910.34090302512049675', '447158.04891100589884445', '5414844.99480544030666351'), + // 'ProjectCrs' => 'EPSG:3857', + 'WMSOnlineResource' => 'http://www.3liz.com/lizmap.html', + 'WMSContactMail' => 'info@3liz.com', + 'WMSContactOrganization' => '3liz', + 'WMSContactPerson' => '3liz', + 'WMSContactPhone' => '+334 67 16 64 51', + 'WMSRestrictedComposers' => array('Composeur1'), + 'WMSUseLayerIDs' => false, + ); + $this->assertEquals($expectedProperties, $data['properties']); + } + + +} From 7cbf8baf7ee9c7b1ec9741947f5035aabee349f4 Mon Sep 17 00:00:00 2001 From: rldhont Date: Fri, 20 Sep 2024 19:28:53 +0200 Subject: [PATCH 04/46] QgisProjectParser -> Lizmap\Project\Qgis\Parser --- .../lizmap/lib/Project/Qgis/Parser.php | 254 ++ .../lizmap/lib/Project/QgisProjectParser.php | 1687 ----------- .../units/classes/Project/Qgis/ParserTest.php | 370 +++ .../classes/Project/QgisProjectParserTest.php | 2527 ----------------- 4 files changed, 624 insertions(+), 4214 deletions(-) create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/Parser.php delete mode 100644 lizmap/modules/lizmap/lib/Project/QgisProjectParser.php create mode 100644 tests/units/classes/Project/Qgis/ParserTest.php delete mode 100644 tests/units/classes/Project/QgisProjectParserTest.php diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/Parser.php b/lizmap/modules/lizmap/lib/Project/Qgis/Parser.php new file mode 100644 index 0000000000..706f03644d --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/Parser.php @@ -0,0 +1,254 @@ + + */ + public static function readAttributes($oXmlReader) + { + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + $data = array(); + if ($oXmlReader->isEmptyElement) { + return $data; + } + + $localName = $oXmlReader->localName; + $depth = $oXmlReader->depth; + while ($oXmlReader->read()) { + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'attribute') { + $data[] = $oXmlReader->readString(); + } + } + + return $data; + } + + /** + * @param \XMLReader $oXmlReader + * + * @return array + */ + public static function readItems($oXmlReader) + { + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + + $localName = $oXmlReader->localName; + $depth = $oXmlReader->depth; + $values = array(); + while ($oXmlReader->read()) { + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'item') { + $values[] = $oXmlReader->readString(); + } + } + + return $values; + } + + /** + * @param \XMLReader $oXmlReader + * + * @return array + */ + public static function readValues($oXmlReader) + { + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + + $localName = $oXmlReader->localName; + $depth = $oXmlReader->depth; + $values = array(); + while ($oXmlReader->read()) { + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'value') { + $values[] = $oXmlReader->readString(); + } + } + + return $values; + } + + public const MAP_VALUES_AS_VALUES = 0; + public const MAP_VALUES_AS_KEYS = 1; + public const MAP_ONLY_VALUES = 2; + + /** + * @param \XMLReader $oXmlReader + * @param int $extraction + * + * @return array + */ + public static function readOption($oXmlReader, $extraction = Parser::MAP_VALUES_AS_VALUES) + { + $localName = 'Option'; + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + if ($oXmlReader->localName != $localName) { + throw new \Exception('Provide a `'.$localName.'` element not `'.$oXmlReader->localName.'`!'); + } + + $depth = $oXmlReader->depth; + $type = $oXmlReader->getAttribute('type'); + $name = $oXmlReader->getAttribute('name'); + $data = array(); + if (!$type && !$name) { + return $data; + } + if ($type == 'Map' || $type == 'List' || $type == 'StringList') { + if ($name == 'map') { + $extraction = self::MAP_VALUES_AS_KEYS; + } + if ($type == 'StringList') { + $extraction = self::MAP_ONLY_VALUES; + } + $options = array(); + while ($oXmlReader->read()) { + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->localName == $localName + && $oXmlReader->depth == $depth + 1) { + if ($extraction == self::MAP_ONLY_VALUES) { + $options = array_merge($options, self::readOption($oXmlReader, $extraction)); + } else { + $options += self::readOption($oXmlReader, $extraction); + } + } + } + if (!$name) { + $data = $options; + } else { + $data[$name] = $options; + } + } else { + $value = $oXmlReader->getAttribute('value'); + if ($type == 'bool') { + $value = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + } elseif ($type == 'int') { + $value = (int) $value; + } + if ($extraction == self::MAP_ONLY_VALUES) { + $data[] = $value; + } elseif ($extraction == self::MAP_VALUES_AS_KEYS) { + $data[$value] = $name; + } elseif ($name) { + $data[$name] = $value; + } else { + $data[] = $value; + } + } + + return $data; + } + + /** + * @param \XMLReader $oXmlReader + * + * @return array + */ + public static function readCustomProperties($oXmlReader) + { + $localName = 'customproperties'; + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + if ($oXmlReader->localName != $localName) { + throw new \Exception('Provide a `'.$localName.'` element not `'.$oXmlReader->localName.'`!'); + } + + $depth = $oXmlReader->depth; + $data = array(); + if ($oXmlReader->isEmptyElement) { + return $data; + } + while ($oXmlReader->read()) { + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'Option') { + $data += self::readOption($oXmlReader); + } + } + + return $data; + } +} diff --git a/lizmap/modules/lizmap/lib/Project/QgisProjectParser.php b/lizmap/modules/lizmap/lib/Project/QgisProjectParser.php deleted file mode 100644 index e4b33d2c20..0000000000 --- a/lizmap/modules/lizmap/lib/Project/QgisProjectParser.php +++ /dev/null @@ -1,1687 +0,0 @@ -nodeType != \XMLReader::ELEMENT) { - throw new \Exception('Provide an XMLReader::ELEMENT!'); - } - if ($oXmlReader->localName != $localName) { - throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); - } - - $depth = $oXmlReader->depth; - $data = array( - 'version' => $oXmlReader->getAttribute('version'), - 'projectname' => $oXmlReader->getAttribute('projectname'), - ); - $tagNames = array( - 'title', - 'projectCrs', - // 'mapcanvas', // for theMapCanvas the CRS is provided by projectCrs - 'layer-tree-group', - 'projectlayers', - 'properties', - 'visibility-presets', - 'Layouts', - ); - while ($oXmlReader->read()) { - - if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT - && $oXmlReader->localName == $localName - && $oXmlReader->depth == $depth) { - break; - } - - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - continue; - } - - $tagName = $oXmlReader->localName; - if (!in_array($tagName, $tagNames) - || $oXmlReader->depth != $depth + 1) { - continue; - } - - if ($oXmlReader->localName == 'properties') { - $data[$oXmlReader->localName] = self::readQgisProperties($oXmlReader); - } else if ($oXmlReader->localName == 'projectCrs') { - $data[$oXmlReader->localName] = self::readQgisProjectCrs($oXmlReader); - } else { - $data[$oXmlReader->localName] = $oXmlReader->readString(); - } - } - - return $data; - } - - /** - * @param \XMLReader $xml - * - * @return Array - */ - public static function readQgisProjectCrs($oXmlReader) - { - $localName = 'projectCrs'; - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - throw new \Exception('Provide an XMLReader::ELEMENT!'); - } - if ($oXmlReader->localName != $localName) { - throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); - } - - $depth = $oXmlReader->depth; - $data = array(); - while ($oXmlReader->read()) { - - if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT - && $oXmlReader->localName == $localName - && $oXmlReader->depth == $depth) { - break; - } - - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - continue; - } - - if ($oXmlReader->depth != $depth + 1) { - continue; - } - - if ($oXmlReader->localName == 'spatialrefsys') { - $data = self::readSpatialRefSys($oXmlReader); - } - } - return $data; - } - - /** - * @param \XMLReader $xml - * - * @return Array - */ - public static function readQgisLayerTreeGroup($oXmlReader) - { - $localName = 'layer-tree-group'; - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - throw new \Exception('Provide an XMLReader::ELEMENT!'); - } - if ($oXmlReader->localName != $localName) { - throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); - } - - $depth = $oXmlReader->depth; - $data = array(); - $tagNames = array( - 'custom-order' - ); - while ($oXmlReader->read()) { - - if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT - && $oXmlReader->localName == $localName - && $oXmlReader->depth == $depth) { - break; - } - - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - continue; - } - - $tagName = $oXmlReader->localName; - if (!in_array($tagName, $tagNames) - || $oXmlReader->depth != $depth + 1) { - continue; - } - - if ($oXmlReader->localName == 'custom-order') { - $data[$oXmlReader->localName] = self::readQgisCustomOrder($oXmlReader); - } - } - return $data; - } - - /** - * @param \XMLReader $xml - * - * @return Array - */ - public static function readQgisCustomOrder($oXmlReader) - { - $localName = 'custom-order'; - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - throw new \Exception('Provide an XMLReader::ELEMENT!'); - } - if ($oXmlReader->localName != $localName) { - throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); - } - - $depth = $oXmlReader->depth; - $data = array( - 'enabled' => filter_var($oXmlReader->getAttribute('enabled'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE), - ); - if ($data['enabled']) { - $data['items'] = self::readItems($oXmlReader); - } else { - while ($oXmlReader->read()) { - - if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT - && $oXmlReader->localName == $localName - && $oXmlReader->depth == $depth) { - break; - } - - } - } - return $data; - } - - /** - * @param \XMLReader $xml - * - * @return Array - */ - public static function readQgisProperties($oXmlReader) - { - $localName = 'properties'; - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - throw new \Exception('Provide an XMLReader::ELEMENT!'); - } - if ($oXmlReader->localName != $localName) { - throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); - } - - $depth = $oXmlReader->depth; - $data = array(); - $tagNames = array( - 'WMSServiceTitle', - 'WMSServiceAbstract', - 'WMSKeywordList', - 'WMSExtent', - // 'ProjectCrs', // it is in SpatialRefSys and is deprecated - the qgis/projectCrs is recommended - 'WMSOnlineResource', - 'WMSContactMail', - 'WMSContactOrganization', - 'WMSContactPerson', - 'WMSContactPhone', - 'WMSMaxWidth', - 'WMSMaxHeight', - 'WMSRestrictedComposers', - 'WMSUseLayerIDs', - // 'Gui', // it contains Canvas and Selection colors extracted by readQgisGuiProperties - // 'Variables', - ); - while ($oXmlReader->read()) { - - if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT - && $oXmlReader->localName == $localName - && $oXmlReader->depth == $depth) { - break; - } - - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - continue; - } - - $tagName = $oXmlReader->localName; - if (!in_array($tagName, $tagNames) - || $oXmlReader->depth != $depth + 1) { - continue; - } - - if ($oXmlReader->localName == 'Gui') { - $data[$oXmlReader->localName] = self::readQgisGuiProperties($oXmlReader); - } else if ($oXmlReader->localName == 'Variables') { - $data[$oXmlReader->localName] = self::readQgisVariablesProperties($oXmlReader); - } - - $type = $oXmlReader->getAttribute('type'); - if ($type == 'QStringList') { - $data[$tagName] = array(); - if (!$oXmlReader->isEmptyElement) { - $data[$tagName] = self::readValues($oXmlReader); - } - } else if ($type == 'bool') { - $data[$tagName] = filter_var($oXmlReader->readString(), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); - } else if ($type == 'int') { - $data[$tagName] = (int) $oXmlReader->readString(); - } else { - $data[$tagName] = $oXmlReader->readString(); - } - } - - return $data; - } - - /** - * @param \XMLReader $xml - * - * @return Array - */ - public static function readQgisGuiProperties($oXmlReader) - { - $localName = 'Gui'; - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - throw new \Exception('Provide an XMLReader::ELEMENT!'); - } - if ($oXmlReader->localName != $localName) { - throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); - } - - $depth = $oXmlReader->depth; - $data = array(); - $tagNames = array( - 'CanvasColorBluePart', - 'CanvasColorGreenPart', - 'CanvasColorRedPart', - 'SelectionColorAlphaPart', - 'SelectionColorBluePart', - 'SelectionColorGreenPart', - 'SelectionColorRedPart', - // 'Identify', // it contains disabledLayers - ); - while ($oXmlReader->read()) { - - if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT - && $oXmlReader->localName == $localName - && $oXmlReader->depth == $depth) { - break; - } - - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - continue; - } - - $tagName = $oXmlReader->localName; - if (!in_array($tagName, $tagNames) - || $oXmlReader->depth != $depth + 1) { - continue; - } - - $type = $oXmlReader->getAttribute('type'); - if ($type == 'QStringList') { - $data[$tagName] = ''; - if (!$oXmlReader->isEmptyElement) { - $data[$tagName] = implode(', ', self::readValues($oXmlReader)); - } - } else if ($type == 'int') { - $data[$tagName] = (int) $oXmlReader->readString(); - } else { - $data[$tagName] = $oXmlReader->readString(); - } - } - - return $data; - } - - /** - * @param \XMLReader $xml - * - * @return Array - */ - public static function readQgisVariablesProperties($oXmlReader) - { - $localName = 'Variables'; - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - throw new \Exception('Provide an XMLReader::ELEMENT!'); - } - if ($oXmlReader->localName != $localName) { - throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); - } - - $depth = $oXmlReader->depth; - $data = array(); - $tagNames = array( - 'variableNames', - 'variableValues', - ); - while ($oXmlReader->read()) { - - if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT - && $oXmlReader->localName == $localName - && $oXmlReader->depth == $depth) { - break; - } - - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - continue; - } - - $tagName = $oXmlReader->localName; - if (!in_array($tagName, $tagNames) - || $oXmlReader->depth != $depth + 1) { - continue; - } - - $type = $oXmlReader->getAttribute('type'); - if ($type == 'QStringList') { - $data[$tagName] = array(); - if (!$oXmlReader->isEmptyElement) { - $data[$tagName] = self::readValues($oXmlReader); - } - } else { - $data[$tagName] = $oXmlReader->readString(); - } - } - return $data; - } - - /** - * @param \XMLReader $xml - * - * @return Array - */ - public static function readQgisVisibilityPresets($oXmlReader) - { - $localName = 'visibility-presets'; - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - throw new \Exception('Provide an XMLReader::ELEMENT!'); - } - if ($oXmlReader->localName != $localName) { - throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); - } - - $depth = $oXmlReader->depth; - $data = array(); - while ($oXmlReader->read()) { - - if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT - && $oXmlReader->localName == $localName - && $oXmlReader->depth == $depth) { - break; - } - - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - continue; - } - - if ($oXmlReader->depth != $depth + 1) { - continue; - } - - if ($oXmlReader->localName == 'visibility-preset') { - $data[] = self::readQgisVisibilityPreset($oXmlReader); - } - } - - return $data; - } - - /** - * @param \XMLReader $xml - * - * @return Array - */ - public static function readQgisVisibilityPreset($oXmlReader) - { - $localName = 'visibility-preset'; - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - throw new \Exception('Provide an XMLReader::ELEMENT!'); - } - if ($oXmlReader->localName != $localName) { - throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); - } - - $depth = $oXmlReader->depth; - $data = array( - 'name' => $oXmlReader->getAttribute('name'), - 'layers' => array(), - 'checkedGroupNodes' => array(), - 'expandedGroupNodes' => array(), - ); - while ($oXmlReader->read()) { - - if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT - && $oXmlReader->localName == $localName - && $oXmlReader->depth == $depth) { - break; - } - - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - continue; - } - - if ($oXmlReader->depth > $depth + 2) { - continue; - } - - $tagName = $oXmlReader->localName; - if ( $tagName == 'layer' ) { - $data['layers'][] = array( - 'id' => $oXmlReader->getAttribute('id'), - 'visible' => filter_var($oXmlReader->getAttribute('visible'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE), - 'style' => $oXmlReader->getAttribute('style'), - 'expanded' => filter_var($oXmlReader->getAttribute('expanded'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE), - ); - } else if ( $tagName == 'checked-group-node' ) { - $data['checkedGroupNodes'][] = $oXmlReader->getAttribute('id'); - } else if ( $tagName == 'expanded-group-node' ) { - $data['expandedGroupNodes'][] = $oXmlReader->getAttribute('id'); - } - } - - return $data; - } - - /** - * @param \XMLReader $xml - * - * @return Array - */ - public static function readQgisProjectLayers($oXmlReader) - { - $localName = 'projectlayers'; - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - throw new \Exception('Provide an XMLReader::ELEMENT!'); - } - if ($oXmlReader->localName != $localName) { - throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); - } - - $depth = $oXmlReader->depth; - $data = array(); - while ($oXmlReader->read()) { - - if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT - && $oXmlReader->localName == $localName - && $oXmlReader->depth == $depth) { - break; - } - - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - continue; - } - - if ($oXmlReader->depth != $depth + 1) { - continue; - } - - if ($oXmlReader->localName == 'maplayer') { - $data[] = self::readQgisMapLayer($oXmlReader); - } - } - - return $data; - } - - /** - * @param \XMLReader $xml - * - * @return Array - */ - public static function readQgisMapLayer($oXmlReader) - { - $localName = 'maplayer'; - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - throw new \Exception('Provide an XMLReader::ELEMENT!'); - } - if ($oXmlReader->localName != $localName) { - throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); - } - - // The maplayer can reference an embeded layer - $embedded = filter_var($oXmlReader->getAttribute('embedded'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); - if ($embedded) { - return array( - 'id' => $oXmlReader->getAttribute('id'), - 'embedded' => true, - 'project' => $oXmlReader->getAttribute('project'), - ); - } - - $depth = $oXmlReader->depth; - $data = array( - 'type' => $oXmlReader->getAttribute('type'), - 'embedded' => false, - ); - $tagNames = array( - 'id', - 'layername', - 'shortname', - 'title', - 'abstract', - 'srs', - 'datasource', - 'provider', - 'keywordList', - // 'fieldConfiguration' - // 'aliases', - // 'excludeAttributesWFS' - // 'excludeAttributesWMS' - // 'defaults', - // 'constraints', - // 'constraintExpressions', - // 'map-layer-style-manager' - // 'attributetableconfig' - // 'vectorjoins' - ); - - while ($oXmlReader->read()) { - - if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT - && $oXmlReader->localName == $localName - && $oXmlReader->depth == $depth) { - break; - } - - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - continue; - } - - $tagName = $oXmlReader->localName; - if (!in_array($tagName, $tagNames) - || $oXmlReader->depth != $depth + 1) { - continue; - } - - if ($tagName == 'keywordList') { - $data[$tagName] = ''; - if (!$oXmlReader->isEmptyElement) { - $data[$tagName] = self::readValues($oXmlReader); - } - } else if ($tagName == 'srs') { - $data[$tagName] = self::readQgisMapLayerSrs($oXmlReader); - } else if ($tagName == 'map-layer-style-manager') { - $data[$tagName] = self::readQgisMapLayerStyleManager($oXmlReader); - } else if ($tagName == 'fieldConfiguration') { - $data[$tagName] = self::readQgisMapLayerFieldConfiguration($oXmlReader); - } else if ($tagName == 'aliases') { - $data[$tagName] = self::readQgisMapLayerAliases($oXmlReader); - } else if ($tagName == 'defaults') { - $data[$tagName] = self::readQgisMapLayerDefaults($oXmlReader); - } else if ($tagName == 'constraints') { - $data[$tagName] = self::readQgisMapLayerConstraints($oXmlReader); - } else if ($tagName == 'constraintExpressions') { - $data[$tagName] = self::readQgisMapLayerConstraintExpressions($oXmlReader); - } else if ($tagName == 'excludeAttributesWFS' - || $tagName == 'excludeAttributesWMS') { - $data[$tagName] = self::readAttributes($oXmlReader); - } else if ($tagName == 'attributetableconfig') { - $data[$tagName] = self::readQgisMapLayerAttributeTableConfig($oXmlReader); - } else if ($tagName == 'vectorjoins') { - $data[$tagName] = self::readQgisMapLayerVectorJoins($oXmlReader); - } else { - $data[$tagName] = $oXmlReader->readString(); - } - } - - return $data; - } - - /** - * @param \XMLReader $xml - * - * @return Array - */ - public static function readQgisMapLayerSrs($oXmlReader) - { - $localName = 'srs'; - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - throw new \Exception('Provide an XMLReader::ELEMENT!'); - } - if ($oXmlReader->localName != $localName) { - throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); - } - - $depth = $oXmlReader->depth; - $data = array(); - while ($oXmlReader->read()) { - - if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT - && $oXmlReader->localName == $localName - && $oXmlReader->depth == $depth) { - break; - } - - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - continue; - } - - if ($oXmlReader->depth != $depth + 1) { - continue; - } - - if ($oXmlReader->localName == 'spatialrefsys') { - $data = self::readSpatialRefSys($oXmlReader); - } - } - return $data; - } - - /** - * @param \XMLReader $xml - * - * @return Array - */ - public static function readQgisMapLayerStyleManager($oXmlReader) - { - $localName = 'map-layer-style-manager'; - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - throw new \Exception('Provide an XMLReader::ELEMENT!'); - } - if ($oXmlReader->localName != $localName) { - throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); - } - - $depth = $oXmlReader->depth; - $data = array( - 'current' => $oXmlReader->getAttribute('current'), - 'styles' => array(), - ); - while ($oXmlReader->read()) { - - if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT - && $oXmlReader->localName == $localName - && $oXmlReader->depth == $depth) { - break; - } - - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - continue; - } - - if ($oXmlReader->depth != $depth + 1) { - continue; - } - - if ($oXmlReader->localName == 'map-layer-style') { - $data['styles'][] = $oXmlReader->getAttribute('name'); - } - } - return $data; - } - - /** - * @param \XMLReader $xml - * - * @return Array - */ - public static function readQgisMapLayerFieldConfiguration($oXmlReader) - { - $localName = 'fieldConfiguration'; - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - throw new \Exception('Provide an XMLReader::ELEMENT!'); - } - if ($oXmlReader->localName != $localName) { - throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); - } - - $depth = $oXmlReader->depth; - $data = array(); - while ($oXmlReader->read()) { - - if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT - && $oXmlReader->localName == $localName - && $oXmlReader->depth == $depth) { - break; - } - - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - continue; - } - - if ($oXmlReader->depth != $depth + 1) { - continue; - } - - if ($oXmlReader->localName == 'field') { - $data[] = self::readQgisMapLayerField($oXmlReader); - } - } - return $data; - } - - /** - * @param \XMLReader $xml - * - * @return Array - */ - public static function readQgisMapLayerField($oXmlReader) - { - $localName = 'field'; - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - throw new \Exception('Provide an XMLReader::ELEMENT!'); - } - if ($oXmlReader->localName != $localName) { - throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); - } - - $depth = $oXmlReader->depth; - $data = array( - 'name' => $oXmlReader->getAttribute('name'), - 'configurationFlags' => $oXmlReader->getAttribute('configurationFlags'), - ); - while ($oXmlReader->read()) { - - if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT - && $oXmlReader->localName == $localName - && $oXmlReader->depth == $depth) { - break; - } - - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - continue; - } - - if ($oXmlReader->depth != $depth + 1) { - continue; - } - - if ($oXmlReader->localName == 'editWidget') { - $data['editWidget'] = self::readQgisMapLayerEditWidget($oXmlReader); - } - } - return $data; - } - - /** - * @param \XMLReader $xml - * - * @return Array - */ - public static function readQgisMapLayerEditWidget($oXmlReader) - { - $localName = 'editWidget'; - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - throw new \Exception('Provide an XMLReader::ELEMENT!'); - } - if ($oXmlReader->localName != $localName) { - throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); - } - - $depth = $oXmlReader->depth; - $data = array( - 'type' => $oXmlReader->getAttribute('type'), - ); - while ($oXmlReader->read()) { - - if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT - && $oXmlReader->localName == $localName - && $oXmlReader->depth == $depth) { - break; - } - - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - continue; - } - - if ($oXmlReader->depth != $depth + 1) { - continue; - } - - if ($oXmlReader->localName == 'config') { - $data['config'] = self::readQgisMapLayerEditWidgetConfig($oXmlReader); - } - } - return $data; - } - - /** - * @param \XMLReader $xml - * - * @return Array - */ - public static function readQgisMapLayerEditWidgetConfig($oXmlReader) - { - $localName = 'config'; - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - throw new \Exception('Provide an XMLReader::ELEMENT!'); - } - if ($oXmlReader->localName != $localName) { - throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); - } - - $depth = $oXmlReader->depth; - $data = array(); - while ($oXmlReader->read()) { - - if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT - && $oXmlReader->localName == $localName - && $oXmlReader->depth == $depth) { - break; - } - - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - continue; - } - - if ($oXmlReader->depth != $depth + 1) { - continue; - } - - if ($oXmlReader->localName == 'Option') { - $data[] = self::readOption($oXmlReader); - } - } - return $data; - /* - - - - - - - - - - */ - } - - /** - * @param \XMLReader $xml - * - * @return Array - */ - public static function readQgisMapLayerAliases($oXmlReader) - { - $localName = 'aliases'; - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - throw new \Exception('Provide an XMLReader::ELEMENT!'); - } - if ($oXmlReader->localName != $localName) { - throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); - } - - $depth = $oXmlReader->depth; - $data = array(); - while ($oXmlReader->read()) { - - if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT - && $oXmlReader->localName == $localName - && $oXmlReader->depth == $depth) { - break; - } - - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - continue; - } - - if ($oXmlReader->depth != $depth + 1) { - continue; - } - - if ($oXmlReader->localName == 'alias') { - $data[] = array( - 'index' => (int) $oXmlReader->getAttribute('index'), - 'field' => $oXmlReader->getAttribute('field'), - 'name' => $oXmlReader->getAttribute('name'), - ); - } - } - return $data; - } - - /** - * @param \XMLReader $xml - * - * @return Array - */ - public static function readQgisMapLayerDefaults($oXmlReader) - { - $localName = 'defaults'; - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - throw new \Exception('Provide an XMLReader::ELEMENT!'); - } - if ($oXmlReader->localName != $localName) { - throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); - } - - $depth = $oXmlReader->depth; - $data = array(); - while ($oXmlReader->read()) { - - if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT - && $oXmlReader->localName == $localName - && $oXmlReader->depth == $depth) { - break; - } - - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - continue; - } - - if ($oXmlReader->depth != $depth + 1) { - continue; - } - - if ($oXmlReader->localName == 'default') { - $data[] = array( - 'field' => $oXmlReader->getAttribute('field'), - 'expression' => $oXmlReader->getAttribute('expression'), - 'applyOnUpdate' => filter_var($oXmlReader->getAttribute('applyOnUpdate'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE), - ); - } - } - return $data; - } - - /** - * @param \XMLReader $xml - * - * @return Array - */ - public static function readQgisMapLayerConstraints($oXmlReader) - { - $localName = 'constraints'; - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - throw new \Exception('Provide an XMLReader::ELEMENT!'); - } - if ($oXmlReader->localName != $localName) { - throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); - } - - $depth = $oXmlReader->depth; - $data = array(); - while ($oXmlReader->read()) { - - if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT - && $oXmlReader->localName == $localName - && $oXmlReader->depth == $depth) { - break; - } - - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - continue; - } - - if ($oXmlReader->depth != $depth + 1) { - continue; - } - - if ($oXmlReader->localName == 'constraint') { - $data[] = array( - 'field' => $oXmlReader->getAttribute('field'), - 'constraints' => (int) $oXmlReader->getAttribute('constraints'), - 'notnull_strength' => filter_var($oXmlReader->getAttribute('notnull_strength'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE), - 'unique_strength' => filter_var($oXmlReader->getAttribute('unique_strength'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE), - 'exp_strength' => filter_var($oXmlReader->getAttribute('exp_strength'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE), - ); - } - } - - return $data; - } - - /** - * @param \XMLReader $xml - * - * @return Array - */ - public static function readQgisMapLayerConstraintExpressions($oXmlReader) - { - $localName = 'constraintExpressions'; - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - throw new \Exception('Provide an XMLReader::ELEMENT!'); - } - if ($oXmlReader->localName != $localName) { - throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); - } - - $depth = $oXmlReader->depth; - $data = array(); - while ($oXmlReader->read()) { - - if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT - && $oXmlReader->localName == $localName - && $oXmlReader->depth == $depth) { - break; - } - - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - continue; - } - - if ($oXmlReader->depth != $depth + 1) { - continue; - } - - if ($oXmlReader->localName == 'constraint') { - $data[] = array( - 'field' => $oXmlReader->getAttribute('field'), - 'exp' => $oXmlReader->getAttribute('exp'), - 'desc' => $oXmlReader->getAttribute('desc'), - ); - } - } - - return $data; - } - - /** - * @param \XMLReader $xml - * - * @return Array - */ - public static function readQgisMapLayerAttributeTableConfig($oXmlReader) - { - $localName = 'attributetableconfig'; - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - throw new \Exception('Provide an XMLReader::ELEMENT!'); - } - if ($oXmlReader->localName != $localName) { - throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); - } - - $depth = $oXmlReader->depth; - $data = array(); - while ($oXmlReader->read()) { - - if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT - && $oXmlReader->localName == $localName - && $oXmlReader->depth == $depth) { - break; - } - - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - continue; - } - - if ($oXmlReader->depth != $depth + 1) { - continue; - } - - if ($oXmlReader->localName == 'columns') { - $data[$oXmlReader->localName] = self::readQgisMapLayerAttributeTableColumns($oXmlReader); - } - } - - return $data; - } - - /** - * @param \XMLReader $xml - * - * @return Array - */ - public static function readQgisMapLayerAttributeTableColumns($oXmlReader) - { - $localName = 'columns'; - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - throw new \Exception('Provide an XMLReader::ELEMENT!'); - } - if ($oXmlReader->localName != $localName) { - throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); - } - - $depth = $oXmlReader->depth; - $data = array(); - while ($oXmlReader->read()) { - - if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT - && $oXmlReader->localName == $localName - && $oXmlReader->depth == $depth) { - break; - } - - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - continue; - } - - if ($oXmlReader->depth != $depth + 1) { - continue; - } - - if ($oXmlReader->localName == 'column') { - $data[] = array( - 'type' => $oXmlReader->getAttribute('type'), - 'name' => $oXmlReader->getAttribute('name'), - 'hidden' => filter_var($oXmlReader->getAttribute('hidden'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE), - ); - } - } - - return $data; - } - - /** - * @param \XMLReader $xml - * - * @return Array - */ - public static function readQgisMapLayerVectorJoins($oXmlReader) - { - $localName = 'vectorjoins'; - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - throw new \Exception('Provide an XMLReader::ELEMENT!'); - } - if ($oXmlReader->localName != $localName) { - throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); - } - - $depth = $oXmlReader->depth; - $data = array(); - while ($oXmlReader->read()) { - - if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT - && $oXmlReader->localName == $localName - && $oXmlReader->depth == $depth) { - break; - } - - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - continue; - } - - if ($oXmlReader->depth != $depth + 1) { - continue; - } - - if ($oXmlReader->localName == 'join') { - $data[] = array( - 'joinLayerId' => $oXmlReader->getAttribute('joinLayerId'), - 'joinFieldName' => $oXmlReader->getAttribute('joinFieldName'), - 'targetFieldName' => $oXmlReader->getAttribute('targetFieldName'), - ); - } - } - - return $data; - } - - /** - * @param \XMLReader $xml - * - * @return Array - */ - public static function readQgisLayouts($oXmlReader) - { - $localName = 'Layouts'; - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - throw new \Exception('Provide an XMLReader::ELEMENT!'); - } - if ($oXmlReader->localName != $localName) { - throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); - } - - $depth = $oXmlReader->depth; - $data = array(); - while ($oXmlReader->read()) { - - if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT - && $oXmlReader->localName == $localName - && $oXmlReader->depth == $depth) { - break; - } - - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - continue; - } - - if ($oXmlReader->depth != $depth + 1) { - continue; - } - - if ($oXmlReader->localName == 'Layout') { - $data[] = self::readQgisLayout($oXmlReader); - } - } - - return $data; - } - - /** - * @param \XMLReader $xml - * - * @return Array - */ - public static function readQgisLayout($oXmlReader) - { - $localName = 'Layout'; - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - throw new \Exception('Provide an XMLReader::ELEMENT!'); - } - if ($oXmlReader->localName != $localName) { - throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); - } - - $depth = $oXmlReader->depth; - $data = array( - 'name' => $oXmlReader->getAttribute('name'), - 'labels' => array(), - 'maps' => array(), - ); - while ($oXmlReader->read()) { - - if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT - && $oXmlReader->localName == $localName - && $oXmlReader->depth == $depth) { - break; - } - - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - continue; - } - - if ($oXmlReader->depth != $depth + 1) { - continue; - } - - if ($oXmlReader->localName == 'PageCollection') { - $data['pages'] = self::readQgisLayoutPageCollection($oXmlReader); - } else if ($oXmlReader->localName == 'LayoutItem') { - $item = self::readQgisLayoutItem($oXmlReader); - - if (!array_key_exists('typeName', $item)) { - continue; - } - - if ($item['typeName'] === 'label' && $item['id'] !== '') { - $data['labels'][] = $item; - } else if ($item['typeName'] === 'map') { - $item['id'] = 'map'.(string) count($data['maps']); - $data['maps'][] = $item; - } - } else if ($oXmlReader->localName == 'Atlas') { - $enabled = filter_var($oXmlReader->getAttribute('enabled'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); - if ($enabled) { - $data['atlas'] = array( - 'enabled' => $enabled, - 'coverageLayer' => $oXmlReader->getAttribute('coverageLayer'), - ); - } - } - } - - return $data; - } - - /** - * @param \XMLReader $xml - * - * @return Array - */ - public static function readQgisLayoutPageCollection($oXmlReader) - { - $localName = 'PageCollection'; - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - throw new \Exception('Provide an XMLReader::ELEMENT!'); - } - if ($oXmlReader->localName != $localName) { - throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); - } - - $depth = $oXmlReader->depth; - $data = array(); - while ($oXmlReader->read()) { - - if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT - && $oXmlReader->localName == $localName - && $oXmlReader->depth == $depth) { - break; - } - - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - continue; - } - - if ($oXmlReader->depth != $depth + 1) { - continue; - } - - if ($oXmlReader->localName == 'LayoutItem') { - $item = self::readQgisLayoutItem($oXmlReader); - - if (!array_key_exists('typeName', $item)) { - continue; - } - - if ($item['typeName'] !== 'page') { - continue; - } - - $data[] = $item; - } - } - return $data; - } - - /** - * @param \XMLReader $xml - * - * @return Array - */ - public static function readQgisLayoutItem($oXmlReader) - { - $localName = 'LayoutItem'; - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - throw new \Exception('Provide an XMLReader::ELEMENT!'); - } - if ($oXmlReader->localName != $localName) { - throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); - } - - $depth = $oXmlReader->depth; - $data = array( - 'type' => $oXmlReader->getAttribute('type'), - ); - - if ($data['type'] == '65638') { - $pageSize = explode(',', $oXmlReader->getAttribute('size')); - $pagePosition = explode(',', $oXmlReader->getAttribute('position')); - $data += array( - 'typeName' => 'page', - 'width' => (int) $pageSize[0], - 'height' => (int) $pageSize[1], - 'x' => (int) $pagePosition[0], - 'y' => (int) $pagePosition[1], - ); - } else if ($data['type'] == '65639') { - $mapSize = explode(',', $oXmlReader->getAttribute('size')); - $data += array( - 'typeName' => 'map', - 'uuid' => $oXmlReader->getAttribute('uuid'), - 'width' => (int) $mapSize[0], - 'height' => (int) $mapSize[1], - 'grid' => False, - ); - } else if ($data['type'] == '65641') { - $data += array( - 'typeName' => 'label', - 'id' => $oXmlReader->getAttribute('id'), - 'htmlState' => filter_var($oXmlReader->getAttribute('htmlState'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE), - 'text' => $oXmlReader->getAttribute('labelText'), - ); - } - - while ($oXmlReader->read()) { - - if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT - && $oXmlReader->localName == $localName - && $oXmlReader->depth == $depth) { - break; - } - - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - continue; - } - - if ($oXmlReader->depth != $depth + 1) { - continue; - } - - if ($data['type'] !== '65639') { - continue; - } - - if ($oXmlReader->localName == 'ComposerMapOverview') { - $show = filter_var($oXmlReader->getAttribute('show'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); - $frameMap = $oXmlReader->getAttribute('frameMap'); - if ($show && $frameMap !== '-1') { - $data += array( - 'overviewMap' => $frameMap, - ); - } - } else if ($oXmlReader->localName == 'ComposerMapGrid') { - $data += array( - 'grid' => filter_var($oXmlReader->getAttribute('show'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE), - ); - } - } - - return $data; - } - - /** - * @param \XMLReader $xml - * - * @return Array - */ - public static function readSpatialRefSys($oXmlReader) - { - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - throw new \Exception('Provide an XMLReader::ELEMENT!'); - } - if ($oXmlReader->localName != 'spatialrefsys') { - throw new \Exception('Provide a `spatialrefsys` element not `'.$oXmlReader->localName.'`!'); - } - - $depth = $oXmlReader->depth; - $data = array(); - $tagNames = array( - 'proj4', - 'srid', - 'authid', - 'description', - ); - while ($oXmlReader->read()) { - - if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT - && $oXmlReader->localName == 'spatialrefsys' - && $oXmlReader->depth == $depth) { - break; - } - - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - continue; - } - - $tagName = $oXmlReader->localName; - if (!in_array($tagName, $tagNames) - || $oXmlReader->depth != $depth + 1) { - continue; - } - - if ($tagName == 'srid') { - $data[$tagName] = (int) $oXmlReader->readString(); - } else { - $data[$tagName] = $oXmlReader->readString(); - } - } - - return $data; - } - - /** - * @param \XMLReader $xml - * - * @return Array - */ - public static function readAttributes($oXmlReader) - { - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - throw new \Exception('Provide an XMLReader::ELEMENT!'); - } - $data = array(); - if ($oXmlReader->isEmptyElement) { - return $data; - } - - $localName = $oXmlReader->localName; - $depth = $oXmlReader->depth; - while ($oXmlReader->read()) { - if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT - && $oXmlReader->localName == $localName - && $oXmlReader->depth == $depth) { - break; - } - - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - continue; - } - - if ($oXmlReader->depth != $depth + 1) { - continue; - } - - if ($oXmlReader->localName == 'attribute') { - $values[] = $oXmlReader->readString(); - } - } - return $values; - } - - /** - * @param \XMLReader $xml - * - * @return Array - */ - public static function readItems($oXmlReader) - { - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - throw new \Exception('Provide an XMLReader::ELEMENT!'); - } - - $localName = $oXmlReader->localName; - $depth = $oXmlReader->depth; - $values = array(); - while ($oXmlReader->read()) { - if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT - && $oXmlReader->localName == $localName - && $oXmlReader->depth == $depth) { - break; - } - - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - continue; - } - - if ($oXmlReader->depth != $depth + 1) { - continue; - } - - if ($oXmlReader->localName == 'item') { - $values[] = $oXmlReader->readString(); - } - } - return $values; - } - - /** - * @param \XMLReader $xml - * - * @return Array - */ - public static function readValues($oXmlReader) - { - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - throw new \Exception('Provide an XMLReader::ELEMENT!'); - } - - $localName = $oXmlReader->localName; - $depth = $oXmlReader->depth; - $values = array(); - while ($oXmlReader->read()) { - if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT - && $oXmlReader->localName == $localName - && $oXmlReader->depth == $depth) { - break; - } - - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - continue; - } - - if ($oXmlReader->depth != $depth + 1) { - continue; - } - - if ($oXmlReader->localName == 'value') { - $values[] = $oXmlReader->readString(); - } - } - return $values; - } - - public const MAP_VALUES_AS_VALUES = 0; - public const MAP_VALUES_AS_KEYS = 1; - public const MAP_ONLY_VALUES = 2; - - /** - * @param \XMLReader $xml - * @param int $extraction - * - * @return Array - */ - public static function readOption($oXmlReader, $extraction = QgisProjectParser::MAP_VALUES_AS_VALUES) - { - $localName = 'Option'; - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - throw new \Exception('Provide an XMLReader::ELEMENT!'); - } - if ($oXmlReader->localName != $localName) { - throw new \Exception('Provide a `'. $localName .'` element not `'.$oXmlReader->localName.'`!'); - } - - $depth = $oXmlReader->depth; - $type = $oXmlReader->getAttribute('type'); - $name = $oXmlReader->getAttribute('name'); - $data = array(); - if (!$type && !$name) { - return $data; - } - if ($type == 'Map' || $type == 'List' || $type == 'StringList') { - if ($name == 'map') { - $extraction = self::MAP_VALUES_AS_KEYS; - } - if ($type == 'StringList') { - $extraction = self::MAP_ONLY_VALUES; - } - $options = array(); - while ($oXmlReader->read()) { - if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT - && $oXmlReader->localName == $localName - && $oXmlReader->depth == $depth) { - break; - } - - if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { - continue; - } - - if ($oXmlReader->localName == $localName - && $oXmlReader->depth == $depth + 1) { - if ($extraction == self::MAP_ONLY_VALUES) { - $options = array_merge($options, self::readOption($oXmlReader, $extraction)); - } else { - $options += self::readOption($oXmlReader, $extraction); - } - } - } - if (!$name) { - $data = $options; - } else { - $data[$name] = $options; - } - } else { - $value = $oXmlReader->getAttribute('value'); - if ($type == 'bool'){ - $value = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); - } else if ($type == 'int'){ - $value = (int) $value; - } - if ($extraction == self::MAP_ONLY_VALUES) { - $data[] = $value; - } else if ($extraction == self::MAP_VALUES_AS_KEYS) { - $data[$value] = $name; - } else if ($name) { - $data[$name] = $value; - } else { - $data[] = $value; - } - } - return $data; - } -} diff --git a/tests/units/classes/Project/Qgis/ParserTest.php b/tests/units/classes/Project/Qgis/ParserTest.php new file mode 100644 index 0000000000..a364a1f561 --- /dev/null +++ b/tests/units/classes/Project/Qgis/ParserTest.php @@ -0,0 +1,370 @@ +assertEquals(XMLReader::ELEMENT, $oXml->nodeType); + $this->assertEquals(0, $oXml->depth); + $this->assertEquals('qgis', $oXml->localName); + $this->assertEquals('3.10.5-A Coruña', $oXml->getAttribute('version')); + } + + public function testReadAttributes() + { + $xmlStr = ' + + OGC_FID + wkt + from + html + to + colour + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $values = Qgis\Parser::readAttributes($oXml); + $this->assertTrue(is_array($values)); + $this->assertCount(6, $values); + $expected = array( + 'OGC_FID', + 'wkt', + 'from', + 'html', + 'to', + 'colour', + ); + $this->assertEquals($expected, $values); + } + + public function testReadItems() + { + $xmlStr = ' + + edition_point20130118171631518 + edition_line20130409161630329 + edition_polygon20130409114333776 + bus_stops20121106170806413 + bus20121102133611751 + VilleMTP_MTP_Quartiers_2011_432620130116112610876 + VilleMTP_MTP_Quartiers_2011_432620130116112351546 + tramstop20150328114203878 + tramway20150328114206278 + publicbuildings20150420100958543 + SousQuartiers20160121124316563 + osm_stamen_toner20180315181710198 + osm_mapnik20180315181738526 + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $values = Qgis\Parser::readItems($oXml); + $this->assertTrue(is_array($values)); + $this->assertCount(13, $values); + $this->assertEquals('edition_point20130118171631518', $values[0]); + $this->assertEquals('VilleMTP_MTP_Quartiers_2011_432620130116112610876', $values[5]); + $this->assertEquals('VilleMTP_MTP_Quartiers_2011_432620130116112351546', $values[6]); + $this->assertEquals('osm_mapnik20180315181738526', $values[12]); + } + + public function testReadValues() + { + $xmlStr = ' + + EPSG:2154 + EPSG:4326 + EPSG:3857 + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $values = Qgis\Parser::readValues($oXml); + $this->assertTrue(is_array($values)); + $this->assertCount(3, $values); + $expected = array( + 'EPSG:2154', + 'EPSG:4326', + 'EPSG:3857', + ); + $this->assertEquals($expected, $values); + } + + public function testReadOption() + { + $xmlStr = ' + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $options = Qgis\Parser::readOption($oXml); + $this->assertTrue(is_array($options)); + $this->assertCount(1, $options); + $expectedOptions = array( + 'Zone A' => 'A', + ); + $this->assertEquals($expectedOptions, $options); + + $xmlStr = ' + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $options = Qgis\Parser::readOption($oXml); + $this->assertTrue(is_array($options)); + $this->assertCount(2, $options); + $expectedOptions = array( + 'IsMultiline' => '0', + 'UseHtml' => '0', + ); + $this->assertEquals($expectedOptions, $options); + + $xmlStr = ' + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $options = Qgis\Parser::readOption($oXml); + $this->assertTrue(is_array($options)); + $this->assertCount(2, $options); + $expectedOptions = array( + 'IsMultiline' => 0, + 'UseHtml' => 0, + ); + $this->assertEquals($expectedOptions, $options); + + $xmlStr = ' + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $options = Qgis\Parser::readOption($oXml); + $this->assertTrue(is_array($options)); + $this->assertCount(2, $options); + $expectedOptions = array( + 'IsMultiline' => false, + 'UseHtml' => false, + ); + $this->assertEquals($expectedOptions, $options); + + $xmlStr = ' + + + + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $options = Qgis\Parser::readOption($oXml); + $this->assertTrue(is_array($options)); + $expectedOptions = array( + 'AllowNull' => true, + 'Max' => 2147483647, + 'Min' => -2147483648, + 'Precision' => 0, + 'Step' => 1, + 'Style' => 'SpinBox', + ); + $this->assertEquals($expectedOptions, $options); + + $xmlStr = ' + + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + + $options = Qgis\Parser::readOption($oXml); + $this->assertTrue(is_array($options)); + $expectedOptions = array( + 'map' => array( + 'A' => 'Zone A', + 'B' => 'Zone B', + '{2839923C-8B7D-419E-B84B-CA2FE9B80EC7}' => 'No Zone', + ), + ); + $this->assertEquals($expectedOptions, $options); + + $xmlStr = ' + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $this->assertEquals('StringList', $oXml->getAttribute('type')); + + $options = Qgis\Parser::readOption($oXml); + $this->assertTrue(is_array($options)); + $expectedOptions = array( + 'Zone A', + 'Zone B', + 'No Zone', + ); + $this->assertEquals($expectedOptions, $options); + + $xmlStr = ' + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $this->assertEquals('Map', $oXml->getAttribute('type')); + + $options = Qgis\Parser::readOption($oXml); + + $this->assertTrue(is_array($options)); + $expectedOptions = array( + 'DocumentViewer' => 1, + 'DocumentViewerHeight' => 0, + 'DocumentViewerWidth' => 0, + 'FileWidget' => true, + 'FileWidgetButton' => true, + 'FileWidgetFilter' => '', + 'PropertyCollection' => array( + 'name' => '', + 'properties' => array( + 'storageUrl' => array ( + 'active' => true, + 'expression' => '\'http://webdav/shapeData/\'||file_name(@selected_file_path)', + 'type' => 3, + ), + ), + 'type' => 'collection', + ), + 'RelativeStorage' => 0, + 'StorageAuthConfigId' => 'k6k7lv8', + 'StorageMode' => 0, + 'StorageType' => 'WebDAV', + ); + $this->assertEquals($expectedOptions, $options); + } + + public function testReadCustomProperties() + { + $xmlStr = ' + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $this->assertEquals('customproperties', $oXml->localName); + + $customProperties = Qgis\Parser::readCustomProperties($oXml); + $this->assertCount(1, $customProperties); + $expectedCustomProperties = array( + 'wmsShortName' => 'Buildings', + ); + $this->assertEquals($expectedCustomProperties, $customProperties); + + $xmlStr = ' + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $this->assertEquals('customproperties', $oXml->localName); + + $customProperties = Qgis\Parser::readCustomProperties($oXml); + $this->assertCount(1, $customProperties); + $expectedCustomProperties = array( + 'expandedLegendNodes' => null, + ); + $this->assertEquals($expectedCustomProperties, $customProperties); + + $xmlStr = ' + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $this->assertEquals('customproperties', $oXml->localName); + + $customProperties = Qgis\Parser::readCustomProperties($oXml); + $this->assertCount(2, $customProperties); + $expectedCustomProperties = array( + 'expandedLegendNodes' => null, + 'showFeatureCount' => '1', + ); + $this->assertEquals($expectedCustomProperties, $customProperties); + + $xmlStr = ' + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $this->assertEquals('customproperties', $oXml->localName); + + $customProperties = Qgis\Parser::readCustomProperties($oXml); + $this->assertCount(0, $customProperties); + $expectedCustomProperties = array( + ); + $this->assertEquals($expectedCustomProperties, $customProperties); + } + +} diff --git a/tests/units/classes/Project/QgisProjectParserTest.php b/tests/units/classes/Project/QgisProjectParserTest.php deleted file mode 100644 index 4556b03ca8..0000000000 --- a/tests/units/classes/Project/QgisProjectParserTest.php +++ /dev/null @@ -1,2527 +0,0 @@ -assertEquals(XMLReader::ELEMENT, $oXml->nodeType); - $this->assertEquals(0, $oXml->depth); - $this->assertEquals('qgis', $oXml->localName); - $this->assertEquals('3.10.5-A Coruña', $oXml->getAttribute('version')); - } - - public function testReadQgisGuiProperties() - { - $xmlStr = ' - - 255 - 255 - 255 - 255 - 0 - 255 - 255 - - '; - $oXml = App\XmlTools::xmlReaderFromString($xmlStr); - - $data = Project\QgisProjectParser::readQgisGuiProperties($oXml); - $expected = array( - 'CanvasColorBluePart' => 255, - 'CanvasColorGreenPart' => 255, - 'CanvasColorRedPart' => 255, - 'SelectionColorAlphaPart' => 255, - 'SelectionColorBluePart' => 0, - 'SelectionColorGreenPart' => 255, - 'SelectionColorRedPart' => 255, - ); - $this->assertEquals($expected, $data); - } - - public function testReadQgisVariablesProperties() - { - /*$oXml = new XMLReader(); - $xml_path = __DIR__.'/../Project/Ressources/montpellier.qgs'; - $oXml->open($xml_path); - - $data = array(); - while($oXml->read()){ - if($oXml->nodeType == XMLReader::ELEMENT - && $oXml->localName == 'Variables' - && $oXml->depth == 2) { - $data = Project\QgisProjectParser::readQgisVariablesProperties($oXml); - break; - } - }*/ - $xmlStr = ' - - - - - '; - $oXml = App\XmlTools::xmlReaderFromString($xmlStr); - - $data = Project\QgisProjectParser::readQgisVariablesProperties($oXml); - $expected = array( - 'variableNames' => array(), - 'variableValues' => array(), - ); - $this->assertEquals($expected, $data); - - $xmlStr = ' - - - lizmap_user - lizmap_user_groups - - - lizmap - lizmap-group - - - '; - $oXml = App\XmlTools::xmlReaderFromString($xmlStr); - - $data = Project\QgisProjectParser::readQgisVariablesProperties($oXml); - $expected = array( - 'variableNames' => array( - 'lizmap_user', - 'lizmap_user_groups', - ), - 'variableValues' => array( - 'lizmap', - 'lizmap-group', - ), - ); - $this->assertEquals($expected, $data); - } - - public function testReadQgisProperties() - { - $oXml = new XMLReader(); - $xml_path = __DIR__.'/../Project/Ressources/montpellier.qgs'; - $oXml->open($xml_path); - - $data = array(); - while($oXml->read()){ - if($oXml->nodeType == XMLReader::ELEMENT - && $oXml->localName == 'properties' - && $oXml->depth == 1) { - $data = Project\QgisProjectParser::readQgisProperties($oXml); - break; - } - } - $expected = array( - 'WMSServiceTitle' => 'Montpellier - Transports', - 'WMSServiceAbstract' => 'Demo project with bus and tramway lines in Montpellier, France. -Data is licensed under ODbl, OpenStreetMap contributors', - 'WMSKeywordList' => array(''), - 'WMSExtent' => array('417006.61373760335845873', '5394910.34090302512049675', '447158.04891100589884445', '5414844.99480544030666351'), - // 'ProjectCrs' => 'EPSG:3857', - 'WMSOnlineResource' => 'http://www.3liz.com/lizmap.html', - 'WMSContactMail' => 'info@3liz.com', - 'WMSContactOrganization' => '3liz', - 'WMSContactPerson' => '3liz', - 'WMSContactPhone' => '+334 67 16 64 51', - 'WMSRestrictedComposers' => array('Composeur1'), - 'WMSUseLayerIDs' => false, - ); - $this->assertEquals($expected, $data); - - $oXml = new XMLReader(); - $xml_path = __DIR__.'/../Project/Ressources/WMSInfotest.qgs'; - $oXml->open($xml_path); - - $data = array(); - while($oXml->read()){ - if($oXml->nodeType == XMLReader::ELEMENT - && $oXml->localName == 'properties' - && $oXml->depth == 1) { - $data = Project\QgisProjectParser::readQgisProperties($oXml); - break; - } - } - $expected = array( - 'WMSServiceTitle' => 'Touristic events around Montpellier, France', - 'WMSServiceAbstract' => '', - 'WMSKeywordList' => array(''), - 'WMSExtent' => array('390483.99668047408340499', '5375009.91444000415503979', '477899.4732063576229848', '5436768.56305211596190929'), - 'WMSOnlineResource' => '', - 'WMSContactMail' => '', - 'WMSContactOrganization' => '', - 'WMSContactPerson' => '', - 'WMSContactPhone' => '', - 'WMSRestrictedComposers' => array(), - 'WMSUseLayerIDs' => false, - ); - $this->assertEquals($expected, $data); - } - - public function testReadQgisVisibilityPresets() - { - $oXml = new XMLReader(); - $xml_path = __DIR__.'/../Project/Ressources/themes-3_22.qgs'; - $oXml->open($xml_path); - - $data = array(); - while($oXml->read()){ - if($oXml->nodeType == XMLReader::ELEMENT - && $oXml->localName == 'visibility-presets' - && $oXml->depth == 1) { - $data = Project\QgisProjectParser::readQgisVisibilityPresets($oXml); - break; - } - } - $expected = array( - array( - 'name'=>'theme1', - 'layers' => array( - array( - 'id' => 'quartiers_ef5b13e3_36db_4e0d_98b3_990de580367d', - 'visible' => true, - 'style' => 'style1', - 'expanded' => true, - ), - ), - 'checkedGroupNodes' => array(), - 'expandedGroupNodes' => array( - 'group1' - ), - ), - array( - 'name'=>'theme2', - 'layers' => array( - array( - 'id' => 'sousquartiers_7c49d0fc_0ee0_4308_a66d_45c144e59872', - 'visible' => true, - 'style' => 'défaut', - 'expanded' => true, - ), - array( - 'id' => 'quartiers_ef5b13e3_36db_4e0d_98b3_990de580367d', - 'visible' => true, - 'style' => 'style2', - 'expanded' => true, - ), - ), - 'checkedGroupNodes' => array( - 'group1' - ), - 'expandedGroupNodes' => array( - 'group1' - ), - - ), - ); - $this->assertEquals($expected, $data); - - $oXml = new XMLReader(); - $xml_path = __DIR__.'/../Project/Ressources/themes-3_26.qgs'; - $oXml->open($xml_path); - - $data = array(); - while($oXml->read()){ - if($oXml->nodeType == XMLReader::ELEMENT - && $oXml->localName == 'visibility-presets' - && $oXml->depth == 1) { - $data = Project\QgisProjectParser::readQgisVisibilityPresets($oXml); - break; - } - } - $expected = array( - array( - 'name'=>'theme1', - 'layers' => array( - array( - 'id' => 'sousquartiers_7c49d0fc_0ee0_4308_a66d_45c144e59872', - 'visible' => false, - 'style' => 'défaut', - 'expanded' => true, - ), - array( - 'id' => 'quartiers_ef5b13e3_36db_4e0d_98b3_990de580367d', - 'visible' => true, - 'style' => 'style1', - 'expanded' => true, - ), - ), - 'checkedGroupNodes' => array(), - 'expandedGroupNodes' => array( - 'group1' - ), - ), - array( - 'name'=>'theme2', - 'layers' => array( - array( - 'id' => 'sousquartiers_7c49d0fc_0ee0_4308_a66d_45c144e59872', - 'visible' => true, - 'style' => 'défaut', - 'expanded' => true, - ), - array( - 'id' => 'quartiers_ef5b13e3_36db_4e0d_98b3_990de580367d', - 'visible' => true, - 'style' => 'style2', - 'expanded' => true, - ), - ), - 'checkedGroupNodes' => array( - 'group1' - ), - 'expandedGroupNodes' => array( - 'group1' - ), - - ), - ); - $this->assertEquals($expected, $data); - } - - public function testReadQgisMapLayer() - { - $oXml = new XMLReader(); - $xml_path = __DIR__.'/../Project/Ressources/montpellier.qgs'; - $oXml->open($xml_path); - - $data = array(); - while($oXml->read()){ - if($oXml->nodeType == XMLReader::ELEMENT - && $oXml->localName == 'maplayer' - && $oXml->depth == 2) { - $data = Project\QgisProjectParser::readQgisMapLayer($oXml); - break; - } - } - $expected = array( - 'type' => 'vector', - 'embedded' => false, - 'id' => 'SousQuartiers20160121124316563', - 'layername' => 'SousQuartiers', - 'srs' => array( - 'proj4' => '+proj=lcc +lat_1=49 +lat_2=44 +lat_0=46.5 +lon_0=3 +x_0=700000 +y_0=6600000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs', - 'srid' => 2154, - 'authid' => 'EPSG:2154', - 'description' => 'RGF93 / Lambert-93', - ), - 'datasource' => './data/vector/VilleMTP_MTP_SousQuartiers_2011.shp', - 'provider' => 'ogr', - 'keywordList' => array(''), - ); - $this->assertEquals($expected, $data); - } - - public function testReadSpatialRefSys() - { - $oXml = new XMLReader(); - $xml_path = __DIR__.'/../Project/Ressources/montpellier.qgs'; - $oXml->open($xml_path); - - $data = array(); - // qgis/projectlayers/maplayer/srs/spatialrefsys - while($oXml->read()){ - if($oXml->nodeType == XMLReader::ELEMENT - && $oXml->localName == 'maplayer' - && $oXml->depth == 2) { - while($oXml->read()){ - if($oXml->nodeType == XMLReader::ELEMENT - && $oXml->localName == 'spatialrefsys' - && $oXml->depth == 4) { - $data = Project\QgisProjectParser::readSpatialRefSys($oXml); - break; - } - } - break; - } - } - $expected = array( - 'proj4' => '+proj=lcc +lat_1=49 +lat_2=44 +lat_0=46.5 +lon_0=3 +x_0=700000 +y_0=6600000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs', - 'srid' => 2154, - 'authid' => 'EPSG:2154', - 'description' => 'RGF93 / Lambert-93', - ); - $this->assertEquals($expected, $data); - } - - public function testReadQgisMapLayerAliases() - { - $oXml = new XMLReader(); - $xml_path = __DIR__.'/../Project/Ressources/montpellier.qgs'; - $oXml->open($xml_path); - - $data = array(); - while($oXml->read()){ - if($oXml->nodeType == XMLReader::ELEMENT - && $oXml->localName == 'maplayer' - && $oXml->depth == 2) { - while($oXml->read()){ - if($oXml->nodeType == XMLReader::ELEMENT - && $oXml->localName == 'aliases' - && $oXml->depth == 3) { - $data = Project\QgisProjectParser::readQgisMapLayerAliases($oXml); - break; - } - } - break; - } - } - $this->assertCount(11, $data); - $expected = array( - array( - 'index' => 0, - 'field' => 'QUARTMNO', - 'name' => 'District', - ), - array( - 'index' => 1, - 'field' => 'SQUARTMNO', - 'name' => 'Code', - ), - array( - 'index' => 2, - 'field' => 'LIBSQUART', - 'name' => 'Name', - ), - array( - 'index' => 3, - 'field' => 'socio_population_2009', - 'name' => 'Population (2009)', - ), - array( - 'index' => 4, - 'field' => 'socio_population_percentage', - 'name' => '% population Montpellier', - ), - array( - 'index' => 5, - 'field' => 'socio_average_income', - 'name' => 'Average income (€)', - ), - array( - 'index' => 6, - 'field' => 'socio_prop_percentage', - 'name' => 'Owners (%)', - ), - array( - 'index' => 7, - 'field' => 'socio_loc_percentage', - 'name' => 'Tenants (%)', - ), - array( - 'index' => 8, - 'field' => 'Quartiers_LIBQUART', - 'name' => 'District', - ), - array( - 'index' => 9, - 'field' => 'popdensity', - 'name' => 'Density ( inhabitants / km2 )', - ), - array( - 'index' => 10, - 'field' => 'area', - 'name' => 'Area ( km2)', - ), - ); - $this->assertEquals($expected, $data); - } - - public function testReadQgisMapLayerDefaults() - { - $oXml = new XMLReader(); - $xml_path = __DIR__.'/../Project/Ressources/montpellier.qgs'; - $oXml->open($xml_path); - - $data = array(); - while($oXml->read()){ - if($oXml->nodeType == XMLReader::ELEMENT - && $oXml->localName == 'maplayer' - && $oXml->depth == 2) { - while($oXml->read()){ - if($oXml->nodeType == XMLReader::ELEMENT - && $oXml->localName == 'defaults' - && $oXml->depth == 3) { - $data = Project\QgisProjectParser::readQgisMapLayerDefaults($oXml); - break; - } - } - break; - } - } - $this->assertCount(11, $data); - $expected = array( - 'field' => 'QUARTMNO', - 'expression' => '', - 'applyOnUpdate' => false, - ); - $this->assertEquals($expected, $data[0]); - $expected = array( - 'field' => 'socio_average_income', - 'expression' => '', - 'applyOnUpdate' => false, - ); - $this->assertEquals($expected, $data[5]); - $expected = array( - 'field' => 'area', - 'expression' => '', - 'applyOnUpdate' => false, - ); - $this->assertEquals($expected, $data[10]); - } - - public function testReadQgisMapLayerConstraints() - { - $oXml = new XMLReader(); - $xml_path = __DIR__.'/../Project/Ressources/montpellier.qgs'; - $oXml->open($xml_path); - - $data = array(); - while($oXml->read()){ - if($oXml->nodeType == XMLReader::ELEMENT - && $oXml->localName == 'maplayer' - && $oXml->depth == 2) { - while($oXml->read()){ - if($oXml->nodeType == XMLReader::ELEMENT - && $oXml->localName == 'constraints' - && $oXml->depth == 3) { - $data = Project\QgisProjectParser::readQgisMapLayerConstraints($oXml); - break; - } - } - break; - } - } - $this->assertCount(11, $data); - $expected = array( - 'field' => 'QUARTMNO', - 'constraints' => 0, - 'notnull_strength' => false, - 'unique_strength' => false, - 'exp_strength' => false, - ); - $this->assertEquals($expected, $data[0]); - $expected = array( - 'field' => 'socio_average_income', - 'constraints' => 0, - 'notnull_strength' => false, - 'unique_strength' => false, - 'exp_strength' => false, - ); - $this->assertEquals($expected, $data[5]); - $expected = array( - 'field' => 'area', - 'constraints' => 0, - 'notnull_strength' => false, - 'unique_strength' => false, - 'exp_strength' => false, - ); - $this->assertEquals($expected, $data[10]); - } - - public function testReadQgisMapLayerConstraintExpressions() - { - $oXml = new XMLReader(); - $xml_path = __DIR__.'/../Project/Ressources/montpellier.qgs'; - $oXml->open($xml_path); - - $data = array(); - while($oXml->read()){ - if($oXml->nodeType == XMLReader::ELEMENT - && $oXml->localName == 'maplayer' - && $oXml->depth == 2) { - while($oXml->read()){ - if($oXml->nodeType == XMLReader::ELEMENT - && $oXml->localName == 'constraintExpressions' - && $oXml->depth == 3) { - $data = Project\QgisProjectParser::readQgisMapLayerConstraintExpressions($oXml); - break; - } - } - break; - } - } - $this->assertCount(11, $data); - $expected = array( - 'field' => 'QUARTMNO', - 'exp' => '', - 'desc' => '', - ); - $this->assertEquals($expected, $data[0]); - $expected = array( - 'field' => 'socio_average_income', - 'exp' => '', - 'desc' => '', - ); - $this->assertEquals($expected, $data[5]); - $expected = array( - 'field' => 'area', - 'exp' => '', - 'desc' => '', - ); - $this->assertEquals($expected, $data[10]); - } - - public function testReadQgisMapLayerStyleManager() - { - $xmlStr = ' - - - - '; - $oXml = App\XmlTools::xmlReaderFromString($xmlStr); - - $data = Project\QgisProjectParser::readQgisMapLayerStyleManager($oXml); - $this->assertTrue(is_array($data)); - $expected = array( - 'current' => 'default', - 'styles' => array('default'), - ); - $this->assertEquals($expected, $data); - - $xmlStr = ' - - - - - - - - - - - - - - - - - - - - - '; - $oXml = App\XmlTools::xmlReaderFromString($xmlStr); - - $data = Project\QgisProjectParser::readQgisMapLayerStyleManager($oXml); - $this->assertTrue(is_array($data)); - $expected = array( - 'current' => 'black', - 'styles' => array( - 'black', - 'colored', - ), - ); - $this->assertEquals($expected, $data); - } - - public function testReadQgisMapLayerAttributeTableColumns() - { - $xmlStr = ' - - - '; - $oXml = App\XmlTools::xmlReaderFromString($xmlStr); - - $data = Project\QgisProjectParser::readQgisMapLayerAttributeTableColumns($oXml); - $this->assertTrue(is_array($data)); - $this->assertCount(15, $data); - $expected = array( - array( - 'type' => 'field', - 'name' => 'nid', - 'hidden' => false, - ), - array( - 'type' => 'field', - 'name' => 'titre', - 'hidden' => false, - ), - array( - 'type' => 'field', - 'name' => 'vignette_src', - 'hidden' => true, - ), - array( - 'type' => 'field', - 'name' => 'vignette_alt', - 'hidden' => true, - ), - array( - 'type' => 'field', - 'name' => 'field_date', - 'hidden' => false, - ), - array( - 'type' => 'field', - 'name' => 'description', - 'hidden' => true, - ), - array( - 'type' => 'field', - 'name' => 'field_communes', - 'hidden' => false, - ), - array( - 'type' => 'field', - 'name' => 'field_lieu', - 'hidden' => false, - ), - array( - 'type' => 'field', - 'name' => 'field_access', - 'hidden' => false, - ), - array( - 'type' => 'field', - 'name' => 'field_thematique', - 'hidden' => false, - ), - array( - 'type' => 'field', - 'name' => 'x', - 'hidden' => true, - ), - array( - 'type' => 'field', - 'name' => 'y', - 'hidden' => true, - ), - array( - 'type' => 'field', - 'name' => 'url', - 'hidden' => false, - ), - array( - 'type' => 'actions', - 'name' => null, - 'hidden' => true, - ), - array( - 'type' => 'field', - 'name' => 'fid', - 'hidden' => false, - ), - ); - $this->assertEquals($expected, $data); - } - - public function testReadQgisMapLayerAttributeTableConfig() - { - $xmlStr = ' - - - - - '; - $oXml = App\XmlTools::xmlReaderFromString($xmlStr); - - $data = Project\QgisProjectParser::readQgisMapLayerAttributeTableConfig($oXml); - $this->assertTrue(is_array($data)); - $this->assertTrue(array_key_exists('columns', $data)); - $this->assertCount(15, $data['columns']); - $expected = array( - 'columns' => array( - array( - 'type' => 'field', - 'name' => 'nid', - 'hidden' => false, - ), - array( - 'type' => 'field', - 'name' => 'titre', - 'hidden' => false, - ), - array( - 'type' => 'field', - 'name' => 'vignette_src', - 'hidden' => true, - ), - array( - 'type' => 'field', - 'name' => 'vignette_alt', - 'hidden' => true, - ), - array( - 'type' => 'field', - 'name' => 'field_date', - 'hidden' => false, - ), - array( - 'type' => 'field', - 'name' => 'description', - 'hidden' => true, - ), - array( - 'type' => 'field', - 'name' => 'field_communes', - 'hidden' => false, - ), - array( - 'type' => 'field', - 'name' => 'field_lieu', - 'hidden' => false, - ), - array( - 'type' => 'field', - 'name' => 'field_access', - 'hidden' => false, - ), - array( - 'type' => 'field', - 'name' => 'field_thematique', - 'hidden' => false, - ), - array( - 'type' => 'field', - 'name' => 'x', - 'hidden' => true, - ), - array( - 'type' => 'field', - 'name' => 'y', - 'hidden' => true, - ), - array( - 'type' => 'field', - 'name' => 'url', - 'hidden' => false, - ), - array( - 'type' => 'actions', - 'name' => null, - 'hidden' => true, - ), - array( - 'type' => 'field', - 'name' => 'fid', - 'hidden' => false, - ), - ), - ); - $this->assertEquals($expected, $data); - } - - public function testReadQgisMapLayerVectorJoins() - { - $xmlStr = ' - - - - - - - - - '; - $oXml = App\XmlTools::xmlReaderFromString($xmlStr); - - $data = Project\QgisProjectParser::readQgisMapLayerVectorJoins($oXml); - $this->assertTrue(is_array($data)); - $this->assertCount(2, $data); - $expected = array( - array( - 'joinLayerId' => 'donnes_sociodemo_sous_quartiers20160121144525075', - 'joinFieldName' => 'squartmno', - 'targetFieldName' => 'SQUARTMNO', - ), - array( - 'joinLayerId' => 'VilleMTP_MTP_Quartiers_2011_432620130116112610876', - 'joinFieldName' => 'QUARTMNO', - 'targetFieldName' => 'QUARTMNO', - ), - ); - $this->assertEquals($expected, $data); - } - - public function testReadAttributes() - { - $xmlStr = ' - - OGC_FID - wkt - from - html - to - colour - - '; - $oXml = App\XmlTools::xmlReaderFromString($xmlStr); - - $values = Project\QgisProjectParser::readAttributes($oXml); - $this->assertTrue(is_array($values)); - $this->assertCount(6, $values); - $expected = array( - 'OGC_FID', - 'wkt', - 'from', - 'html', - 'to', - 'colour', - ); - $this->assertEquals($expected, $values); - } - - public function testReadItems() - { - $xmlStr = ' - - edition_point20130118171631518 - edition_line20130409161630329 - edition_polygon20130409114333776 - bus_stops20121106170806413 - bus20121102133611751 - VilleMTP_MTP_Quartiers_2011_432620130116112610876 - VilleMTP_MTP_Quartiers_2011_432620130116112351546 - tramstop20150328114203878 - tramway20150328114206278 - publicbuildings20150420100958543 - SousQuartiers20160121124316563 - osm_stamen_toner20180315181710198 - osm_mapnik20180315181738526 - - '; - $oXml = App\XmlTools::xmlReaderFromString($xmlStr); - - $values = Project\QgisProjectParser::readItems($oXml); - $this->assertTrue(is_array($values)); - $this->assertCount(13, $values); - $this->assertEquals('edition_point20130118171631518', $values[0]); - $this->assertEquals('VilleMTP_MTP_Quartiers_2011_432620130116112610876', $values[5]); - $this->assertEquals('VilleMTP_MTP_Quartiers_2011_432620130116112351546', $values[6]); - $this->assertEquals('osm_mapnik20180315181738526', $values[12]); - } - - public function testReadValues() - { - $xmlStr = ' - - EPSG:2154 - EPSG:4326 - EPSG:3857 - - '; - $oXml = App\XmlTools::xmlReaderFromString($xmlStr); - - $values = Project\QgisProjectParser::readValues($oXml); - $this->assertTrue(is_array($values)); - $this->assertCount(3, $values); - $expected = array( - 'EPSG:2154', - 'EPSG:4326', - 'EPSG:3857', - ); - $this->assertEquals($expected, $values); - } - - public function testReadOption() - { - $xmlStr = ' - - '; - $oXml = App\XmlTools::xmlReaderFromString($xmlStr); - - $options = Project\QgisProjectParser::readOption($oXml); - $this->assertTrue(is_array($options)); - $this->assertCount(1, $options); - $expectedOptions = array( - 'Zone A' => 'A', - ); - $this->assertEquals($expectedOptions, $options); - - $xmlStr = ' - - '; - $oXml = App\XmlTools::xmlReaderFromString($xmlStr); - - $options = Project\QgisProjectParser::readOption($oXml); - $this->assertTrue(is_array($options)); - $this->assertCount(2, $options); - $expectedOptions = array( - 'IsMultiline' => '0', - 'UseHtml' => '0', - ); - $this->assertEquals($expectedOptions, $options); - - $xmlStr = ' - - '; - $oXml = App\XmlTools::xmlReaderFromString($xmlStr); - - $options = Project\QgisProjectParser::readOption($oXml); - $this->assertTrue(is_array($options)); - $this->assertCount(2, $options); - $expectedOptions = array( - 'IsMultiline' => 0, - 'UseHtml' => 0, - ); - $this->assertEquals($expectedOptions, $options); - - $xmlStr = ' - - '; - $oXml = App\XmlTools::xmlReaderFromString($xmlStr); - - $options = Project\QgisProjectParser::readOption($oXml); - $this->assertTrue(is_array($options)); - $this->assertCount(2, $options); - $expectedOptions = array( - 'IsMultiline' => false, - 'UseHtml' => false, - ); - $this->assertEquals($expectedOptions, $options); - - $xmlStr = ' - - - - - - - - '; - $oXml = App\XmlTools::xmlReaderFromString($xmlStr); - - $options = Project\QgisProjectParser::readOption($oXml); - $this->assertTrue(is_array($options)); - $expectedOptions = array( - 'AllowNull' => true, - 'Max' => 2147483647, - 'Min' => -2147483648, - 'Precision' => 0, - 'Step' => 1, - 'Style' => 'SpinBox', - ); - $this->assertEquals($expectedOptions, $options); - - $xmlStr = ' - - - - - - '; - $oXml = App\XmlTools::xmlReaderFromString($xmlStr); - - $options = Project\QgisProjectParser::readOption($oXml); - $this->assertTrue(is_array($options)); - $expectedOptions = array( - 'map' => array( - 'A' => 'Zone A', - 'B' => 'Zone B', - '{2839923C-8B7D-419E-B84B-CA2FE9B80EC7}' => 'No Zone', - ), - ); - $this->assertEquals($expectedOptions, $options); - - $xmlStr = ' - - - - - '; - $oXml = App\XmlTools::xmlReaderFromString($xmlStr); - $this->assertEquals('StringList', $oXml->getAttribute('type')); - - $options = Project\QgisProjectParser::readOption($oXml); - $this->assertTrue(is_array($options)); - $expectedOptions = array( - 'Zone A', - 'Zone B', - 'No Zone', - ); - $this->assertEquals($expectedOptions, $options); - - $xmlStr = ' - - - - - '; - $oXml = App\XmlTools::xmlReaderFromString($xmlStr); - $this->assertEquals('Map', $oXml->getAttribute('type')); - - $options = Project\QgisProjectParser::readOption($oXml); - - $this->assertTrue(is_array($options)); - $expectedOptions = array( - 'DocumentViewer' => 1, - 'DocumentViewerHeight' => 0, - 'DocumentViewerWidth' => 0, - 'FileWidget' => true, - 'FileWidgetButton' => true, - 'FileWidgetFilter' => '', - 'PropertyCollection' => array( - 'name' => '', - 'properties' => array( - 'storageUrl' => array ( - 'active' => true, - 'expression' => '\'http://webdav/shapeData/\'||file_name(@selected_file_path)', - 'type' => 3, - ), - ), - 'type' => 'collection', - ), - 'RelativeStorage' => 0, - 'StorageAuthConfigId' => 'k6k7lv8', - 'StorageMode' => 0, - 'StorageType' => 'WebDAV', - ); - $this->assertEquals($expectedOptions, $options); - } - - public function testReadQgisMapLayerEditWidgetConfig() - { - $xmlStr = ' - - - - '; - $oXml = App\XmlTools::xmlReaderFromString($xmlStr); - - $data = Project\QgisProjectParser::readQgisMapLayerEditWidgetConfig($oXml); - $this->assertTrue(is_array($data)); - $expectedOptions = array( - array( - 'IsMultiline' => '0', - 'UseHtml' => '0', - ), - ); - $this->assertEquals($expectedOptions, $data); - } - - public function testReadQgisMapLayerEditWidget() - { - $xmlStr = ' - - - - - - '; - $oXml = App\XmlTools::xmlReaderFromString($xmlStr); - - $data = Project\QgisProjectParser::readQgisMapLayerEditWidget($oXml); - $this->assertTrue(is_array($data)); - $expectedOptions = array( - 'type' => 'TextEdit', - 'config' => array( - array( - 'IsMultiline' => '0', - 'UseHtml' => '0', - ), - ), - ); - $this->assertEquals($expectedOptions, $data); - } - - public function testReadQgisMapLayerField() - { - $xmlStr = ' - - - - - - - - '; - $oXml = App\XmlTools::xmlReaderFromString($xmlStr); - - $data = Project\QgisProjectParser::readQgisMapLayerField($oXml); - $this->assertTrue(is_array($data)); - $expectedOptions = array( - 'name' => 'OGC_FID', - 'configurationFlags' => 'HideFromWms|HideFromWfs', - 'editWidget' => array( - 'type' => 'TextEdit', - 'config' => array( - array( - 'IsMultiline' => '0', - 'UseHtml' => '0', - ), - ), - ), - ); - $this->assertEquals($expectedOptions, $data); - } - - public function testReadQgisMapLayerFieldConfiguration() - { - $xmlStr = ' - - - - - - - - - - - - - - - - - '; - $oXml = App\XmlTools::xmlReaderFromString($xmlStr); - - $data = Project\QgisProjectParser::readQgisMapLayerFieldConfiguration($oXml); - $this->assertTrue(is_array($data)); - $this->assertCount(2, $data); - $expectedOptions = array( - array( - 'name' => 'OGC_FID', - 'configurationFlags' => 'HideFromWms', - 'editWidget' => array( - 'type' => 'TextEdit', - 'config' => array( - array( - 'IsMultiline' => '0', - 'UseHtml' => '0', - ), - ), - ), - ), - array( - 'name' => 'tram_id', - 'configurationFlags' => null, - 'editWidget' => array( - 'type' => 'ValueRelation', - 'config' => array( - array( - 'AllowMulti' => '0', - 'AllowNull' => '1', - 'FilterExpression' => '', - 'Key' => 'osm_id', - 'Layer' => 'tramway20150328114206278', - 'OrderByValue' => '1', - 'UseCompleter' => '0', - 'Value' => 'test', - ), - ), - ), - ), - ); - $this->assertEquals($expectedOptions, $data); - } - - public function testReadQgisCustomOrder() - { - $xmlStr = ' - - edition_point20130118171631518 - edition_line20130409161630329 - edition_polygon20130409114333776 - bus_stops20121106170806413 - bus20121102133611751 - VilleMTP_MTP_Quartiers_2011_432620130116112610876 - VilleMTP_MTP_Quartiers_2011_432620130116112351546 - tramstop20150328114203878 - tramway20150328114206278 - publicbuildings20150420100958543 - SousQuartiers20160121124316563 - osm_stamen_toner20180315181710198 - osm_mapnik20180315181738526 - - '; - $oXml = App\XmlTools::xmlReaderFromString($xmlStr); - - $data = Project\QgisProjectParser::readQgisCustomOrder($oXml); - $this->assertTrue(is_array($data)); - $this->assertFalse($data['enabled']); - $this->assertFalse(array_key_exists('items', $data)); - - $xmlStr = ' - - edition_point20130118171631518 - edition_line20130409161630329 - edition_polygon20130409114333776 - bus_stops20121106170806413 - bus20121102133611751 - VilleMTP_MTP_Quartiers_2011_432620130116112610876 - VilleMTP_MTP_Quartiers_2011_432620130116112351546 - tramstop20150328114203878 - tramway20150328114206278 - publicbuildings20150420100958543 - SousQuartiers20160121124316563 - osm_stamen_toner20180315181710198 - osm_mapnik20180315181738526 - - '; - $oXml = App\XmlTools::xmlReaderFromString($xmlStr); - - $data = Project\QgisProjectParser::readQgisCustomOrder($oXml); - $this->assertTrue(is_array($data)); - $this->assertTrue($data['enabled']); - $this->assertTrue(array_key_exists('items', $data)); - $this->assertCount(13, $data['items']); - } - - public function testReadQgisProjectCrs() - { - $xmlStr = ' - - - PROJCS["unnamed",GEOGCS["unnamed ellipse",DATUM["unknown",SPHEROID["unnamed",6378137,0],EXTENSION["PROJ4_GRIDS","@null"]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433]],PROJECTION["Mercator_2SP"],PARAMETER["standard_parallel_1",0],PARAMETER["central_meridian",0],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["Meter",1],EXTENSION["PROJ4","+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs"]] - +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs - 100000 - 0 - USER:100000 - * SCR généré (+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs) - merc - - false - - - '; - $oXml = App\XmlTools::xmlReaderFromString($xmlStr); - - $data = Project\QgisProjectParser::readQgisProjectCrs($oXml); - $this->assertTrue(is_array($data)); - $expected = array( - 'proj4' => '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs', - 'srid' => 0, - 'authid' => 'USER:100000', - 'description' => ' * SCR généré (+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs)', - ); - $this->assertEquals($expected, $data); - } - - public function testReadQgisLayoutItem() - { - $xmlStr = ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - '; - $oXml = App\XmlTools::xmlReaderFromString($xmlStr); - - $data = Project\QgisProjectParser::readQgisLayoutItem($oXml); - $this->assertTrue(is_array($data)); - $expected = array( - 'type' => '65638', - 'typeName' => 'page', - 'width' => 297, - 'height' => 210, - 'x' => 0, - 'y' => 0, - ); - $this->assertEquals($expected, $data); - - $xmlStr = ' - - - - - - - - - - - - - '; - $oXml = App\XmlTools::xmlReaderFromString($xmlStr); - - $data = Project\QgisProjectParser::readQgisLayoutItem($oXml); - $this->assertTrue(is_array($data)); - $expected = array( - 'type' => '65641', - 'typeName' => 'label', - 'id' => '', - 'htmlState' => false, - 'text' => 'Tram stops in the district', - ); - $this->assertEquals($expected, $data); - - $xmlStr = ' - - - - - - - - - - - - VilleMTP_MTP_Quartiers_2011_432620130116112351546 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - '; - $oXml = App\XmlTools::xmlReaderFromString($xmlStr); - - $data = Project\QgisProjectParser::readQgisLayoutItem($oXml); - $this->assertTrue(is_array($data)); - $expected = array( - 'type' => '65639', - 'typeName' => 'map', - 'uuid' => '{a50537f9-5e73-4610-955e-31b092d81b94}', - 'width' => 60, - 'height' => 45, - 'grid' => false, - 'overviewMap' => '{a228885d-4d7a-4ae0-af9b-02a4fe7cd814}', - ); - $this->assertEquals($expected, $data); - } - - public function testReadQgisLayout() - { - $xmlStr = ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - VilleMTP_MTP_Quartiers_2011_432620130116112351546 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - '; - $oXml = App\XmlTools::xmlReaderFromString($xmlStr); - - $data = Project\QgisProjectParser::readQgisLayout($oXml); - $this->assertTrue(is_array($data)); - $this->assertEquals('District card', $data['name']); - - $this->assertTrue(array_key_exists('pages', $data)); - $this->assertCount(3, $data['pages']); - - $expectedPage = array( - 'type' => '65638', - 'typeName' => 'page', - 'width' => 297, - 'height' => 210, - 'x' => 0, - 'y' => 0, - ); - $this->assertEquals($expectedPage, $data['pages'][0]); - - $expectedPage = array( - 'type' => '65638', - 'typeName' => 'page', - 'width' => 297, - 'height' => 210, - 'x' => 0, - 'y' => 220, - ); - $this->assertEquals($expectedPage, $data['pages'][1]); - - $expectedPage = array( - 'type' => '65638', - 'typeName' => 'page', - 'width' => 297, - 'height' => 210, - 'x' => 0, - 'y' => 440, - ); - $this->assertEquals($expectedPage, $data['pages'][2]); - - $this->assertTrue(array_key_exists('labels', $data)); - $this->assertCount(1, $data['labels']); - $expectedLabel = array( - 'type' => '65641', - 'typeName' => 'label', - 'id' => 'description', - 'htmlState' => false, - 'text' => 'Description', - ); - $this->assertEquals($expectedLabel, $data['labels'][0]); - - $this->assertTrue(array_key_exists('maps', $data)); - $this->assertCount(2, $data['maps']); - - $expectedMap = array( - 'type' => '65639', - 'typeName' => 'map', - 'uuid' => '{a50537f9-5e73-4610-955e-31b092d81b94}', - 'width' => 60, - 'height' => 45, - 'grid' => false, - 'overviewMap' => '{a228885d-4d7a-4ae0-af9b-02a4fe7cd814}', - 'id' => 'map0', - ); - $this->assertEquals($expectedMap, $data['maps'][0]); - - $expectedMap = array( - 'type' => '65639', - 'typeName' => 'map', - 'uuid' => '{a228885d-4d7a-4ae0-af9b-02a4fe7cd814}', - 'width' => 237, - 'height' => 191, - 'grid' => false, - 'id' => 'map1', - ); - $this->assertEquals($expectedMap, $data['maps'][1]); - $this->assertEquals($data['maps'][1]['uuid'], $data['maps'][0]['overviewMap']); - - $this->assertTrue(array_key_exists('atlas', $data)); - $expectedAtlas = array( - 'enabled' => true, - 'coverageLayer' => 'VilleMTP_MTP_Quartiers_2011_432620130116112610876', - ); - $this->assertEquals($expectedAtlas, $data['atlas']); - } - - public function testReadQgisDocument() - { - $xmlStr = ' - - Montpellier - Transports - - - PROJCS["unnamed",GEOGCS["unnamed ellipse",DATUM["unknown",SPHEROID["unnamed",6378137,0],EXTENSION["PROJ4_GRIDS","@null"]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433]],PROJECTION["Mercator_2SP"],PARAMETER["standard_parallel_1",0],PARAMETER["central_meridian",0],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["Meter",1],EXTENSION["PROJ4","+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs"]] - +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs - 100000 - 0 - USER:100000 - * SCR généré (+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs) - merc - - false - - - - - edition_point20130118171631518 - edition_line20130409161630329 - edition_polygon20130409114333776 - bus_stops20121106170806413 - bus20121102133611751 - VilleMTP_MTP_Quartiers_2011_432620130116112610876 - VilleMTP_MTP_Quartiers_2011_432620130116112351546 - tramstop20150328114203878 - tramway20150328114206278 - publicbuildings20150420100958543 - SousQuartiers20160121124316563 - osm_stamen_toner20180315181710198 - osm_mapnik20180315181738526 - - - - - 255 - 255 - 255 - 255 - 0 - 255 - 255 - - - 3857 - +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs - EPSG:3857 - 1 - - - - - - None - true - info@3liz.com - 3liz - 3liz - +334 67 16 64 51 - - - 417006.61373760335845873 - 5394910.34090302512049675 - 447158.04891100589884445 - 5414844.99480544030666351 - - conditions unknown - 90 - - - - http://www.3liz.com/lizmap.html - 8 - - Composeur1 - - - Demo project with bus and tramway lines in Montpellier, France. -Data is licensed under ODbl, OpenStreetMap contributors - true - Montpellier - Transports - - false - - - '; - $oXml = App\XmlTools::xmlReaderFromString($xmlStr); - - $data = Project\QgisProjectParser::readQgisDocument($oXml); - $this->assertTrue(is_array($data)); - $this->assertTrue(array_key_exists('version', $data)); - $this->assertEquals('3.10.5-A Coruña', $data['version']); - $this->assertTrue(array_key_exists('projectname', $data)); - $this->assertEquals('Montpellier - Transports', $data['projectname']); - $this->assertTrue(array_key_exists('title', $data)); - $this->assertEquals('Montpellier - Transports', $data['title']); - $this->assertTrue(array_key_exists('projectCrs', $data)); - $expectedProjectCrs = array( - 'proj4' => '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs', - 'srid' => 0, - 'authid' => 'USER:100000', - 'description' => ' * SCR généré (+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs)', - ); - $this->assertEquals($expectedProjectCrs, $data['projectCrs']); - $this->assertTrue(array_key_exists('properties', $data)); - $expectedProperties = array( - 'WMSServiceTitle' => 'Montpellier - Transports', - 'WMSServiceAbstract' => 'Demo project with bus and tramway lines in Montpellier, France. -Data is licensed under ODbl, OpenStreetMap contributors', - 'WMSKeywordList' => array(''), - 'WMSExtent' => array('417006.61373760335845873', '5394910.34090302512049675', '447158.04891100589884445', '5414844.99480544030666351'), - // 'ProjectCrs' => 'EPSG:3857', - 'WMSOnlineResource' => 'http://www.3liz.com/lizmap.html', - 'WMSContactMail' => 'info@3liz.com', - 'WMSContactOrganization' => '3liz', - 'WMSContactPerson' => '3liz', - 'WMSContactPhone' => '+334 67 16 64 51', - 'WMSRestrictedComposers' => array('Composeur1'), - 'WMSUseLayerIDs' => false, - ); - $this->assertEquals($expectedProperties, $data['properties']); - } - - -} From 1da3129666ee57c40480340a278aafe36192f6c7 Mon Sep 17 00:00:00 2001 From: rldhont Date: Wed, 8 Nov 2023 23:18:03 +0100 Subject: [PATCH 05/46] Lizmap\Project\Qgis\BaseQgisObject class --- .../lib/Project/Qgis/BaseQgisObject.php | 179 ++++++++++++++++++ .../Project/Qgis/BaseQgisObjectTest.php | 51 +++++ 2 files changed, 230 insertions(+) create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/BaseQgisObject.php create mode 100644 tests/units/classes/Project/Qgis/BaseQgisObjectTest.php diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/BaseQgisObject.php b/lizmap/modules/lizmap/lib/Project/Qgis/BaseQgisObject.php new file mode 100644 index 0000000000..f030f930d6 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/BaseQgisObject.php @@ -0,0 +1,179 @@ + The instance properties */ + protected $properties = array(); + + /** @var array The not null properties */ + protected $mandatoryProperties = array(); + + /** @var array The default values for properties */ + protected $defaultValues = array(); + + /** @var array The instance data for properties */ + private $data = array(); + + /** + * Base QGIS object constructor. + * + * @param array $data the instance data + */ + public function __construct($data) + { + $newData = array_replace($this->defaultValues, $data); + if (count(array_diff_key(array_flip($this->mandatoryProperties), $newData)) !== 0) { + $exStr = '$data has to contain `'.implode('`, `', $this->mandatoryProperties).'` keys!'; + $exStr .= ' Missing keys: '.implode(', ', array_keys(array_diff_key(array_flip($this->mandatoryProperties), $newData))).'!'; + + throw new \Exception($exStr); + } + $this->set($newData); + } + + public function __get(mixed $property): mixed + { + if (!empty($this->properties) + && !in_array($property, $this->properties)) { + throw new \Exception(get_class($this)." no such property `{$property}`."); + } + + if (array_key_exists($property, $this->data)) { + return $this->data[$property]; + } + + if (!empty($this->properties)) { + return null; + } + + throw new \Exception(get_class($this)." no such property `{$property}`."); + } + + public function __isset(mixed $property): bool + { + return isset($this->data[$property]); + } + + /*public function offsetExists(mixed $key): bool + { + return isset($this->data[$key]); + }*/ + + /*public function offsetGet(mixed $key): mixed + { + return $this->data[$key]; + }*/ + + final public function __set(string $key, $val): void + { + throw new \Exception('immutable'); + } + + final public function __unset(string $key): void + { + throw new \Exception('immutable'); + } + + /*final public function offsetSet(mixed $key, mixed $value): void + { + throw Exception('immutable'); + }*/ + + /*final public function offsetUnset(mixed $key): void + { + throw Exception('immutable'); + }*/ + + /*public function count(): int + { + return count($this->data); + }*/ + + /*public function getIterator(): Traversable + { + return new ArrayIterator($this->data); + }*/ + + public function jsonSerialize(): mixed + { + return $this->getData(); + } + + protected function arrayToData($vArray): array + { + $data = array(); + foreach ($vArray as $k => $v) { + if ($v instanceof BaseQgisObject) { + $data[$k] = $v->getData(); + } elseif (is_array($v)) { + $data[$k] = $this->arrayToData($v); + } else { + $data[$k] = $v; + } + } + + return $data; + } + + public function getData(): array + { + $data = array(); + foreach ($this->properties as $property) { + if (!array_key_exists($property, $this->data)) { + continue; + } + $value = $this->data[$property]; + if ($value instanceof BaseQgisObject) { + $data[$property] = $value->getData(); + } elseif (is_array($value)) { + $data[$property] = $this->arrayToData($value); + } else { + $data[$property] = $value; + } + } + + return $data; + } + + protected function set(array $data): void + { + foreach ($data as $property => $value) { + if (!empty($this->properties) + && !in_array($property, $this->properties)) { + continue; + } + $this->data[$property] = $value; + } + } +} diff --git a/tests/units/classes/Project/Qgis/BaseQgisObjectTest.php b/tests/units/classes/Project/Qgis/BaseQgisObjectTest.php new file mode 100644 index 0000000000..fed0eef8b2 --- /dev/null +++ b/tests/units/classes/Project/Qgis/BaseQgisObjectTest.php @@ -0,0 +1,51 @@ + The instance properties*/ + protected $properties = array( + 'name', + 'parent', + 'children', + ); + + /** @var Array The not null properties */ + protected $mandatoryProperties = array( + 'name', + ); +} + +/** + * @internal + * @coversNothing + */ +class BaseQgisObjectTest extends TestCase +{ + public function testConstruct() + { + $this->expectException(Exception::class); + $this->expectExceptionMessage('$data has to contain `name` keys! Missing keys: name!'); + $p1 = new Person(array()); + } + + public function testGetData() + { + $p1Data = array('name' => 'Foo'); + $p1 = new Person($p1Data); + + $this->assertEquals($p1Data, $p1->getData()); + + $p2 = new Person(array('name' => 'Bar', 'parent' => $p1)); + $p2Data = array('name' => 'Bar', 'parent' => $p1Data); + + $this->assertEquals($p2Data, $p2->getData()); + + $p3 = new Person(array('name' => 'FooBar', 'children' => array($p1))); + $p3Data = array('name' => 'FooBar', 'children' => array($p1Data)); + + $this->assertEquals($p3Data, $p3->getData()); + } +} From c1593833cbcf01b4f338694eccf59574a51e6464 Mon Sep 17 00:00:00 2001 From: rldhont Date: Thu, 16 Nov 2023 17:18:34 +0100 Subject: [PATCH 06/46] Lizmap\Project\Qgis\BaseQgisXmlObject class --- .../lib/Project/Qgis/BaseQgisXmlObject.php | 180 ++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/BaseQgisXmlObject.php diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/BaseQgisXmlObject.php b/lizmap/modules/lizmap/lib/Project/Qgis/BaseQgisXmlObject.php new file mode 100644 index 0000000000..b902dd4a23 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/BaseQgisXmlObject.php @@ -0,0 +1,180 @@ + The XML element parsed children */ + protected static $children = array(); + + /** @var array The XML element needed children */ + protected static $mandatoryChildren = array(); + + /** @var array The XML element tagname associated with a collector property name */ + protected static $childrenCollection = array(); + + /** + * Get an QGIS object instance from an XMLReader instance at an element. + * + * @param \XMLReader $oXmlReader An XMLReader instance at an element + * + * @return BaseQgisObject the QGIS object instance corresponding to the element + */ + public static function fromXmlReader($oXmlReader) + { + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + if ($oXmlReader->localName != static::$qgisLocalName) { + throw new \Exception('Provide a `'.static::$qgisLocalName.'` element not `'.$oXmlReader->localName.'`!'); + } + + $depth = $oXmlReader->depth; + $data = static::getAttributes($oXmlReader); + + if ($oXmlReader->isEmptyElement) { + return static::buildInstance($data); + } + + while ($oXmlReader->read()) { + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == static::$qgisLocalName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + $tagName = $oXmlReader->localName; + $value = null; + + $childParserKeys = array(); + if (static::$childParsers !== null) { + $childParserKeys = array_keys(static::$childParsers); + } + if (!empty($childParserKeys) + && in_array($tagName, $childParserKeys)) { + $value = call_user_func(static::$childParsers[$tagName], $oXmlReader); + } + + if ($value === null + && !empty(static::$children) + && in_array($tagName, static::$children)) { + $value = static::parseChild($oXmlReader); + } + + if ($value === null + && static::$children !== null + && empty(static::$children)) { + $value = static::parseChild($oXmlReader); + } + + if ($value !== null) { + if (count(static::$childrenCollection) && array_key_exists($tagName, static::$childrenCollection)) { + $collectionName = static::$childrenCollection[$tagName]; + if (!array_key_exists($collectionName, $data)) { + $data[$collectionName] = array(); + } + $data[$collectionName][] = $value; + } elseif (!array_key_exists($tagName, $data)) { + $data[$tagName] = $value; + } elseif (is_array($data[$tagName])) { + $data[$tagName] = array_merge($data[$tagName], $value); + } + } + } + if (count(array_diff_key(array_flip(static::$mandatoryChildren), $data)) !== 0) { + $exStr = '`'.static::$qgisLocalName.'` element has to contain `'.implode('`, `', static::$mandatoryChildren).'` elements!'; + if (static::$childParsers !== null) { + $exStr .= ' `'.implode('`, `', array_keys(static::$childParsers)).'` parsers provided!'; + } + $exStr .= ' Missing elements: '.implode(', ', array_keys(array_diff_key(array_flip(static::$mandatoryChildren), $data))).'!'; + + throw new \Exception($exStr); + } + + return static::buildInstance($data); + } + + /** + * Get attributes from an XMLReader instance at an element. + * + * @param \XMLReader $oXmlReader An XMLReader instance at an element + * + * @return array the element attributes as keys / values + */ + protected static function getAttributes($oXmlReader) + { + return array(); + } + + protected static $childParsers; + + public static function registerChildParser($localName, $parser) + { + if (static::$childParsers === null) { + throw new \Exception('Not available register child parser!'); + } + if (!is_callable($parser)) { + throw new \Exception("Not callable parser '{$localName}'."); + } + static::$childParsers[$localName] = $parser; + } + + public static function unRegisterChildParser($localName, $parser) + { + if (isset(static::$childParsers[$localName])) { + unset(static::$childParsers[$localName]); + + return true; + } + + return false; + } + + /** + * Parse from an XMLReader instance at a child of an element. + * + * @param \XMLReader $oXmlReader An XMLReader instance at a child of an element + * + * @return mixed the result of the parsing + */ + protected static function parseChild($oXmlReader) + { + return $oXmlReader->readString(); + } + + /** + * Build an instance with data as an array. + * + * @param array $data the instance data + * + * @return BaseQgisXmlObject the instance + */ + protected static function buildInstance($data) + { + return new static($data); + } +} From 7db941dd30754bf0cca9450bb6b2d83928c5f1a6 Mon Sep 17 00:00:00 2001 From: rldhont Date: Tue, 7 Nov 2023 15:05:51 +0100 Subject: [PATCH 07/46] New Lizmap\Project\Qgis\SpatialRefSys for QGIS Spatial Reference System class --- .../lizmap/lib/Project/Qgis/SpatialRefSys.php | 137 +++++++++++++++ .../Project/Qgis/SpatialRefSysTest.php | 159 ++++++++++++++++++ 2 files changed, 296 insertions(+) create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/SpatialRefSys.php create mode 100644 tests/units/classes/Project/Qgis/SpatialRefSysTest.php diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/SpatialRefSys.php b/lizmap/modules/lizmap/lib/Project/Qgis/SpatialRefSys.php new file mode 100644 index 0000000000..3504881061 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/SpatialRefSys.php @@ -0,0 +1,137 @@ + The instance properties */ + protected $properties = array( + 'authid', + 'proj4', + 'srid', + 'description', + ); + + /** @var array The not null properties */ + protected $mandatoryProperties = array( + 'authid', + 'proj4', + ); + + /** + * @var array The stored instances + * + * @see SpatialRefSys::getInstance() + */ + private static $instances = array( + ); + + /** + * Get all Spatial Reference System instance stored. + * + * @return array + */ + public static function allInstances() + { + return array_values(self::$instances); + } + + /** + * Get all Spatial Reference System instance stored. + * + * @return array + */ + public static function clearInstances() + { + return self::$instances = array(); + } + + /** + * Get a Spatial Reference System instance from an array. + * if the `authid` is already stored, the Spatial Reference System in memory will be returned + * else a new Spatial Reference System instance is constructed, stored and returned. + * + * @param SpatialRefSysData $data An array describing Spatial Reference System + * + * @return SpatialRefSys the Spatial Reference System instance corresponding to the array + */ + public static function getInstance($data) + { + if (array_key_exists($data['authid'], self::$instances)) { + return self::$instances[$data['authid']]; + } + $inst = new self($data); + self::$instances[$inst->authid] = $inst; + + return $inst; + } + + /** @var string The XML element local name */ + protected static $qgisLocalName = 'spatialrefsys'; + + /** @var array The XML element parsed children */ + protected static $children = array( + 'authid', + 'proj4', + 'srid', + 'description', + ); + + /** @var array The XML element needed children */ + protected static $mandatoryChildren = array( + 'authid', + 'proj4', + ); + + /** + * Parse from an XMLReader instance at a child of an element. + * + * @param \XMLReader $oXmlReader An XMLReader instance at a child of an element + * + * @return int|string the result of the parsing + */ + protected static function parseChild($oXmlReader) + { + if ($oXmlReader->localName == 'srid') { + return (int) $oXmlReader->readString(); + } + + return $oXmlReader->readString(); + } + + /** + * Build and instance with data as an array. + * + * @param array $data the instance data + * + * @return SpatialRefSys the instance + */ + protected static function buildInstance($data) + { + if (array_key_exists($data['authid'], self::$instances)) { + return self::$instances[$data['authid']]; + } + + return self::getInstance($data); + } +} diff --git a/tests/units/classes/Project/Qgis/SpatialRefSysTest.php b/tests/units/classes/Project/Qgis/SpatialRefSysTest.php new file mode 100644 index 0000000000..ad761f4257 --- /dev/null +++ b/tests/units/classes/Project/Qgis/SpatialRefSysTest.php @@ -0,0 +1,159 @@ + '+proj=lcc +lat_1=49 +lat_2=44 +lat_0=46.5 +lon_0=3 +x_0=700000 +y_0=6600000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs', + 'srid' => 2154, + 'authid' => 'EPSG:2154', + 'description' => 'RGF93 / Lambert-93', + ); + + $srs = new Qgis\SpatialRefSys($data); + $this->assertEquals($data['proj4'], $srs->proj4); + $this->assertEquals($data['srid'], $srs->srid); + $this->assertEquals($data['authid'], $srs->authid); + $this->assertEquals($data['description'], $srs->description); + } + + public function testExceptionNoSuchProperty() + { + $data = array( + 'wkt' => 'PROJCS["WGS 84 / Pseudo-Mercator",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Mercator_1SP"],PARAMETER["central_meridian",0],PARAMETER["scale_factor",1],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["X",EAST],AXIS["Y",NORTH],EXTENSION["PROJ4","+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs"],AUTHORITY["EPSG","3857"]]', + 'proj4' => '+proj=lcc +lat_1=49 +lat_2=44 +lat_0=46.5 +lon_0=3 +x_0=700000 +y_0=6600000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs', + 'srid' => 2154, + 'authid' => 'EPSG:2154', + 'description' => 'RGF93 / Lambert-93', + ); + + $srs = new Qgis\SpatialRefSys($data); + $this->assertEquals($data['proj4'], $srs->proj4); + $this->assertEquals($data['srid'], $srs->srid); + $this->assertEquals($data['authid'], $srs->authid); + $this->assertEquals($data['description'], $srs->description); + + $this->expectException(Exception::class); + $this->expectExceptionMessage('no such property `wkt`.'); + $srs->wkt; + } + + public function testExceptionMandatoryProperties() + { + $data = array( + 'srid' => 2154, + 'authid' => 'EPSG:2154', + 'description' => 'RGF93 / Lambert-93', + ); + + $this->expectException(Exception::class); + $this->expectExceptionMessage('$data has to contain `authid`, `proj4` keys!'); + $srs = new Qgis\SpatialRefSys($data); + } + + public function testGetInstance() + { + Qgis\SpatialRefSys::clearInstances(); + $this->assertCount(0, Qgis\SpatialRefSys::allInstances()); + + $data = array( + 'proj4' => '+proj=lcc +lat_1=49 +lat_2=44 +lat_0=46.5 +lon_0=3 +x_0=700000 +y_0=6600000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs', + 'srid' => 2154, + 'authid' => 'EPSG:2154', + 'description' => 'RGF93 / Lambert-93', + ); + $srs = Qgis\SpatialRefSys::getInstance($data); + $this->assertEquals($data['proj4'], $srs->proj4); + $this->assertEquals($data['srid'], $srs->srid); + $this->assertEquals($data['authid'], $srs->authid); + $this->assertEquals($data['description'], $srs->description); + + $this->assertCount(1, Qgis\SpatialRefSys::allInstances()); + + $data = array( + 'proj4' => '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs', + 'srid' => 3857, + 'authid' => 'EPSG:3857', + 'description' => 'WGS 84 / Pseudo-Mercator', + ); + $srs = Qgis\SpatialRefSys::getInstance($data); + $this->assertEquals($data['proj4'], $srs->proj4); + $this->assertEquals($data['srid'], $srs->srid); + $this->assertEquals($data['authid'], $srs->authid); + $this->assertEquals($data['description'], $srs->description); + + $this->assertCount(2, Qgis\SpatialRefSys::allInstances()); + + $data = array( + 'proj4' => '+proj=lcc +lat_1=49 +lat_2=44 +lat_0=46.5 +lon_0=3 +x_0=700000 +y_0=6600000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs', + 'authid' => 'EPSG:2154', + ); + $srs = Qgis\SpatialRefSys::getInstance($data); + $this->assertEquals($data['proj4'], $srs->proj4); + $this->assertNotNull($srs->srid); + $this->assertEquals($data['authid'], $srs->authid); + $this->assertNotNull($srs->description); + + $this->assertCount(2, Qgis\SpatialRefSys::allInstances()); + } + + public function testFromXmlReader() + { + $xmlStr = ' + + PROJCS["WGS 84 / Pseudo-Mercator",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Mercator_1SP"],PARAMETER["central_meridian",0],PARAMETER["scale_factor",1],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["X",EAST],AXIS["Y",NORTH],EXTENSION["PROJ4","+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs"],AUTHORITY["EPSG","3857"]] + +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs + 3857 + 3857 + EPSG:3857 + WGS 84 / Pseudo-Mercator + merc + WGS84 + false + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $data = Qgis\SpatialRefSys::fromXmlReader($oXml); + $expected = new Qgis\SpatialRefSys( + array( + 'proj4' => '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs', + 'srid' => 3857, + 'authid' => 'EPSG:3857', + 'description' => 'WGS 84 / Pseudo-Mercator', + ) + ); + $this->assertEquals($expected->proj4, $data->proj4); + $this->assertEquals($expected->srid, $data->srid); + $this->assertEquals($expected->authid, $data->authid); + $this->assertEquals($expected->description, $data->description); + } + + public function testExceptionMandatoryElements() + { + $xmlStr = ' + + 3857 + 3857 + EPSG:3857 + WGS 84 / Pseudo-Mercator + merc + WGS84 + false + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $this->expectException(Exception::class); + $this->expectExceptionMessage('`spatialrefsys` element has to contain `authid`, `proj4` elements!'); + Qgis\SpatialRefSys::fromXmlReader($oXml); + } + +} From 87b8db9eaf878d03c7cb18a0a54d414d86e57bd5 Mon Sep 17 00:00:00 2001 From: rldhont Date: Thu, 9 Nov 2023 00:11:12 +0100 Subject: [PATCH 08/46] New Lizmap\Project\Qgis\ProjectGuiProperties for QGIS Project Gui Properties --- .../lib/Project/Qgis/ProjectGuiProperties.php | 105 +++++++++++++ .../Project/Qgis/ProjectGuiPropertiesTest.php | 138 ++++++++++++++++++ 2 files changed, 243 insertions(+) create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/ProjectGuiProperties.php create mode 100644 tests/units/classes/Project/Qgis/ProjectGuiPropertiesTest.php diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/ProjectGuiProperties.php b/lizmap/modules/lizmap/lib/Project/Qgis/ProjectGuiProperties.php new file mode 100644 index 0000000000..aeabd9d932 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/ProjectGuiProperties.php @@ -0,0 +1,105 @@ + The instance properties */ + protected $properties = array( + 'CanvasColorBluePart', + 'CanvasColorGreenPart', + 'CanvasColorRedPart', + 'SelectionColorAlphaPart', + 'SelectionColorBluePart', + 'SelectionColorGreenPart', + 'SelectionColorRedPart', + // 'Identify', // it contains disabledLayers + ); + + /** @var array The not null properties */ + protected $mandatoryProperties = array( + 'CanvasColorBluePart', + 'CanvasColorGreenPart', + 'CanvasColorRedPart', + ); + + /** @var string The XML element local name */ + protected static $qgisLocalName = 'Gui'; + + /** @var array The XML element parsed children */ + protected static $children = array( + 'CanvasColorBluePart', + 'CanvasColorGreenPart', + 'CanvasColorRedPart', + 'SelectionColorAlphaPart', + 'SelectionColorBluePart', + 'SelectionColorGreenPart', + 'SelectionColorRedPart', + // 'Identify', // it contains disabledLayers + ); + + /** @var array The XML element needed children */ + protected static $mandatoryChildren = array( + 'CanvasColorBluePart', + 'CanvasColorGreenPart', + 'CanvasColorRedPart', + ); + + /** + * Get the canvas color as RGB string. + * + * @return string The variables key / value array + */ + public function getCanvasColor() + { + return 'rgb('.$this->CanvasColorRedPart.', '.$this->CanvasColorGreenPart.', '.$this->CanvasColorBluePart.')'; + } + + /** + * Parse from an XMLReader instance at a child of an element. + * + * @param \XMLReader $oXmlReader An XMLReader instance at a child of an element + * + * @return array|int|string the result of the parsing + */ + protected static function parseChild($oXmlReader) + { + $type = $oXmlReader->getAttribute('type'); + if ($type == 'QStringList') { + if (!$oXmlReader->isEmptyElement) { + return Parser::readValues($oXmlReader); + } + + return array(); + } + + if ($type == 'int') { + return (int) $oXmlReader->readString(); + } + + return $oXmlReader->readString(); + } +} diff --git a/tests/units/classes/Project/Qgis/ProjectGuiPropertiesTest.php b/tests/units/classes/Project/Qgis/ProjectGuiPropertiesTest.php new file mode 100644 index 0000000000..9727f75ea5 --- /dev/null +++ b/tests/units/classes/Project/Qgis/ProjectGuiPropertiesTest.php @@ -0,0 +1,138 @@ + 255, + 'CanvasColorGreenPart' => 255, + 'CanvasColorRedPart' => 255, + 'SelectionColorAlphaPart' => 255, + 'SelectionColorBluePart' => 0, + 'SelectionColorGreenPart' => 255, + 'SelectionColorRedPart' => 255, + ); + + $properties = new Qgis\ProjectGuiProperties($data); + foreach($data as $prop => $value) { + $this->assertEquals($value, $properties->$prop); + } + } + + public function testGetCanvasColor() + { + $data = array( + 'CanvasColorBluePart' => 255, + 'CanvasColorGreenPart' => 255, + 'CanvasColorRedPart' => 255, + 'SelectionColorAlphaPart' => 255, + 'SelectionColorBluePart' => 0, + 'SelectionColorGreenPart' => 255, + 'SelectionColorRedPart' => 255, + ); + + $properties = new Qgis\ProjectGuiProperties($data); + $this->assertEquals('rgb(255, 255, 255)', $properties->getCanvasColor()); + + $data = array( + 'CanvasColorBluePart' => 200, + 'CanvasColorGreenPart' => 100, + 'CanvasColorRedPart' => 50, + 'SelectionColorAlphaPart' => 255, + 'SelectionColorBluePart' => 0, + 'SelectionColorGreenPart' => 255, + 'SelectionColorRedPart' => 255, + ); + + $properties = new Qgis\ProjectGuiProperties($data); + $this->assertEquals('rgb(50, 100, 200)', $properties->getCanvasColor()); + } + + public function testExceptionNoSuchProperty() + { + $data = array( + 'CanvasColorBluePart' => 255, + 'CanvasColorGreenPart' => 255, + 'CanvasColorRedPart' => 255, + 'CanvasColorAlphaPart' => 255, + ); + + $srs = new Qgis\ProjectGuiProperties($data); + $this->assertEquals($data['CanvasColorBluePart'], $srs->CanvasColorBluePart); + $this->assertEquals($data['CanvasColorGreenPart'], $srs->CanvasColorGreenPart); + $this->assertEquals($data['CanvasColorRedPart'], $srs->CanvasColorRedPart); + + $this->expectException(Exception::class); + $this->expectExceptionMessage('no such property `CanvasColorAlphaPart`.'); + $srs->CanvasColorAlphaPart; + } + + public function testExceptionMandatoryProperties() + { + $data = array( + 'SelectionColorAlphaPart' => 255, + 'SelectionColorBluePart' => 0, + 'SelectionColorGreenPart' => 255, + 'SelectionColorRedPart' => 255, + ); + + $this->expectException(Exception::class); + $this->expectExceptionMessage('$data has to contain `CanvasColorBluePart`, `CanvasColorGreenPart`, `CanvasColorRedPart` keys!'); + $srs = new Qgis\ProjectGuiProperties($data); + } + + public function testFromXmlReader() + { + $xmlStr = ' + + 255 + 255 + 255 + 255 + 0 + 255 + 255 + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $properties = Qgis\ProjectGuiProperties::fromXmlReader($oXml); + + $data = array( + 'CanvasColorBluePart' => 255, + 'CanvasColorGreenPart' => 255, + 'CanvasColorRedPart' => 255, + 'SelectionColorAlphaPart' => 255, + 'SelectionColorBluePart' => 0, + 'SelectionColorGreenPart' => 255, + 'SelectionColorRedPart' => 255, + ); + foreach($data as $prop => $value) { + $this->assertEquals($value, $properties->$prop, $prop); + } + } + + public function testExceptionMandatoryElements() + { + $xmlStr = ' + + 255 + 0 + 255 + 255 + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $this->expectException(Exception::class); + $this->expectExceptionMessage('`Gui` element has to contain `CanvasColorBluePart`, `CanvasColorGreenPart`, `CanvasColorRedPart` elements!'); + Qgis\ProjectGuiProperties::fromXmlReader($oXml); + } +} From 59dcddf08b0f617de981926d3fcfa762fb5e50b3 Mon Sep 17 00:00:00 2001 From: rldhont Date: Fri, 10 Nov 2023 09:02:39 +0100 Subject: [PATCH 09/46] New Lizmap\Project\Qgis\ProjectVariables for QGIS Project Variables --- .../lib/Project/Qgis/ProjectVariables.php | 115 ++++++++++++++++++ .../Project/Qgis/ProjectVariablesTest.php | 88 ++++++++++++++ 2 files changed, 203 insertions(+) create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/ProjectVariables.php create mode 100644 tests/units/classes/Project/Qgis/ProjectVariablesTest.php diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/ProjectVariables.php b/lizmap/modules/lizmap/lib/Project/Qgis/ProjectVariables.php new file mode 100644 index 0000000000..517fbe37b5 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/ProjectVariables.php @@ -0,0 +1,115 @@ + $variableNames + * @property array $variableValues + */ +class ProjectVariables extends BaseQgisXmlObject +{ + /** @var array The instance properties */ + protected $properties = array( + 'variableNames', + 'variableValues', + ); + + /** @var array The not null properties */ + protected $mandatoryProperties = array( + 'variableNames', + 'variableValues', + ); + + /** + * Get the variable value of the variable name. + * + * @param string $variableName The variable name + * + * @return bool The variable name exists + */ + public function hasVariableName($variableName) + { + return in_array($variableName, $this->variableNames); + } + + /** + * Get the variable value of the variable name. + * + * @param string $variableName The variable name + * + * @return string The variable value + */ + public function getVariableValue($variableName) + { + if (!$this->hasVariableName($variableName)) { + throw new \Exception("no such variable `{$variableName}`."); + } + + return $this->variableValues[array_search($variableName, $this->variableNames)]; + } + + /** + * Get the variables as key / value array. + * + * @return array The variables key / value array + */ + public function getVariablesAsKeyArray() + { + $variables = array(); + foreach ($this->variableNames as $variableIndex => $name) { + $variables[$name] = $this->variableValues[$variableIndex]; + } + + return $variables; + } + + /** @var string The XML element local name */ + protected static $qgisLocalName = 'Variables'; + + /** @var array The XML element parsed children */ + protected static $children = array( + 'variableNames', + 'variableValues', + ); + + /** @var array The XML element needed children */ + protected static $mandatoryChildren = array( + 'variableNames', + 'variableValues', + ); + + /** + * Parse from an XMLReader instance at a child of an element. + * + * @param \XMLReader $oXmlReader An XMLReader instance at a child of an element + * + * @return array|string the result of the parsing + */ + protected static function parseChild($oXmlReader) + { + $type = $oXmlReader->getAttribute('type'); + if ($type == 'QStringList') { + if (!$oXmlReader->isEmptyElement) { + return Parser::readValues($oXmlReader); + } + + return array(); + } + + return $oXmlReader->readString(); + } +} diff --git a/tests/units/classes/Project/Qgis/ProjectVariablesTest.php b/tests/units/classes/Project/Qgis/ProjectVariablesTest.php new file mode 100644 index 0000000000..1346be4673 --- /dev/null +++ b/tests/units/classes/Project/Qgis/ProjectVariablesTest.php @@ -0,0 +1,88 @@ + array('lizmap_user', 'lizmap_user_groups'), + 'variableValues' => array('lizmap', 'lizmap-group'), + ); + + $variables = new Qgis\ProjectVariables($data); + foreach($data as $prop => $value) { + $this->assertEquals($value, $variables->$prop); + } + $this->assertTrue($variables->hasVariableName('lizmap_user')); + $this->assertEquals('lizmap', $variables->getVariableValue('lizmap_user')); + } + + public function testFromXmlReader() + { + $xmlStr = ' + + + lizmap_user + lizmap_user_groups + + + lizmap + lizmap-group + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $variables = Qgis\ProjectVariables::fromXmlReader($oXml); + + $data = array( + 'variableNames' => array('lizmap_user', 'lizmap_user_groups'), + 'variableValues' => array('lizmap', 'lizmap-group'), + ); + foreach($data as $prop => $value) { + $this->assertEquals($value, $variables->$prop, $prop); + } + $this->assertTrue($variables->hasVariableName('lizmap_user')); + $this->assertEquals('lizmap', $variables->getVariableValue('lizmap_user')); + + $xmlStr = ' + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $variables = Qgis\ProjectVariables::fromXmlReader($oXml); + + $data = array( + 'variableNames' => array(), + 'variableValues' => array(), + ); + foreach($data as $prop => $value) { + $this->assertEquals($value, $variables->$prop, $prop); + } + $this->assertFalse($variables->hasVariableName('lizmap_user')); + } + + public function testGetVariablesAsKeyArray() + { + $data = array( + 'variableNames' => array('lizmap_user', 'lizmap_user_groups'), + 'variableValues' => array('lizmap', 'lizmap-group'), + ); + + $variables = new Qgis\ProjectVariables($data); + $variables = $variables->getVariablesAsKeyArray(); + foreach($data['variableNames'] as $varIndex => $prop) { + $this->assertArrayHasKey($prop, $variables); + $this->assertEquals($data['variableValues'][$varIndex], $variables[$prop], $prop); + } + } +} From ad59f20ac60bd1a910be8f578ee5a1d65ffacdcf Mon Sep 17 00:00:00 2001 From: rldhont Date: Wed, 8 Nov 2023 23:20:47 +0100 Subject: [PATCH 10/46] New Lizmap\Project\Qgis\ProjectProperties for QGIS Project Properties --- .../lib/Project/Qgis/ProjectProperties.php | 127 +++++++++++++++++ .../Project/Qgis/ProjectPropertiesTest.php | 130 ++++++++++++++++++ 2 files changed, 257 insertions(+) create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/ProjectProperties.php create mode 100644 tests/units/classes/Project/Qgis/ProjectPropertiesTest.php diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/ProjectProperties.php b/lizmap/modules/lizmap/lib/Project/Qgis/ProjectProperties.php new file mode 100644 index 0000000000..f0771f0d56 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/ProjectProperties.php @@ -0,0 +1,127 @@ + $WMSKeywordList + * @property null|array $WMSExtent + * @property null|string $WMSOnlineResource + * @property null|string $WMSContactMail + * @property null|string $WMSContactOrganization + * @property null|string $WMSContactPerson + * @property null|string $WMSContactPhone + * @property null|int $WMSMaxWidth + * @property null|int $WMSMaxHeight + * @property null|int $WMSMaxAtlasFeatures + * @property null|array $WMSRestrictedComposers + * @property null|array $WMSRestrictedLayers + * @property null|array $WFSLayers + * @property null|bool $WMSUseLayerIDs + * @property null|bool $WMSAddWktGeometry + * @property null|ProjectGuiProperties $Gui + * @property null|ProjectVariables $Variables + */ +class ProjectProperties extends BaseQgisXmlObject +{ + /** @var array The instance properties */ + protected $properties = array( + 'WMSServiceTitle', + 'WMSServiceAbstract', + 'WMSKeywordList', + 'WMSExtent', + 'WMSOnlineResource', + 'WMSContactMail', + 'WMSContactOrganization', + 'WMSContactPerson', + 'WMSContactPhone', + 'WMSMaxWidth', + 'WMSMaxHeight', + 'WMSMaxAtlasFeatures', + 'WMSRestrictedComposers', + 'WMSRestrictedLayers', + 'WFSLayers', + 'WMSUseLayerIDs', + 'WMSAddWktGeometry', + 'Gui', + 'Variables', + ); + + /** @var string The XML element local name */ + protected static $qgisLocalName = 'properties'; + + /** @var array The XML element parsed children */ + protected static $children = array( + 'WMSServiceTitle', + 'WMSServiceAbstract', + 'WMSKeywordList', + 'WMSExtent', + 'WMSOnlineResource', + 'WMSContactMail', + 'WMSContactOrganization', + 'WMSContactPerson', + 'WMSContactPhone', + 'WMSMaxWidth', + 'WMSMaxHeight', + 'WMSMaxAtlasFeatures', + 'WMSRestrictedComposers', + 'WMSRestrictedLayers', + 'WFSLayers', + 'WMSUseLayerIDs', + 'WMSAddWktGeometry', + // 'Gui', + // 'Variables', + ); + + /** + * Parse from an XMLReader instance at a child of an element. + * + * @param \XMLReader $oXmlReader An XMLReader instance at a child of an element + * + * @return array|bool|int|string the result of the parsing + */ + protected static function parseChild($oXmlReader) + { + $type = $oXmlReader->getAttribute('type'); + if ($type == 'QStringList') { + if (!$oXmlReader->isEmptyElement) { + return Parser::readValues($oXmlReader); + } + + return array(); + } + + if ($type == 'bool') { + return filter_var($oXmlReader->readString(), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + } + + if ($type == 'int') { + return (int) $oXmlReader->readString(); + } + + return $oXmlReader->readString(); + } + + protected static $childParsers = array(); +} +ProjectProperties::registerChildParser('Gui', function ($oXmlReader) { + return ProjectGuiProperties::fromXmlReader($oXmlReader); +}); +ProjectProperties::registerChildParser('Variables', function ($oXmlReader) { + return ProjectVariables::fromXmlReader($oXmlReader); +}); diff --git a/tests/units/classes/Project/Qgis/ProjectPropertiesTest.php b/tests/units/classes/Project/Qgis/ProjectPropertiesTest.php new file mode 100644 index 0000000000..dc94dbcd09 --- /dev/null +++ b/tests/units/classes/Project/Qgis/ProjectPropertiesTest.php @@ -0,0 +1,130 @@ + 'Montpellier - Transports', + 'WMSServiceAbstract' => 'Demo project with bus and tramway lines in Montpellier, France. +Data is licensed under ODbl, OpenStreetMap contributors', + 'WMSKeywordList' => array(''), + 'WMSExtent' => array('417006.61373760335845873', '5394910.34090302512049675', '447158.04891100589884445', '5414844.99480544030666351'), + 'WMSOnlineResource' => 'http://www.3liz.com/lizmap.html', + 'WMSContactMail' => 'info@3liz.com', + 'WMSContactOrganization' => '3liz', + 'WMSContactPerson' => '3liz', + 'WMSContactPhone' => '+334 67 16 64 51', + 'WMSRestrictedComposers' => array('Composeur1'), + 'WMSRestrictedLayers' => array(), + 'WMSUseLayerIDs' => false, + ); + + $properties = new Qgis\ProjectProperties($data); + foreach($data as $prop => $value) { + $this->assertEquals($value, $properties->$prop); + } + $this->assertNull($properties->Variables); + $this->assertNull($properties->Gui); + } + + public function testFromXmlReader() + { + $xmlStr = ' + + + 255 + 255 + 255 + 255 + 0 + 255 + 255 + + + + + + info@3liz.com + 3liz + 3liz + +334 67 16 64 51 + + + 417006.61373760335845873 + 5394910.34090302512049675 + 447158.04891100589884445 + 5414844.99480544030666351 + + + + + http://www.3liz.com/lizmap.html + + Composeur1 + + + Demo project with bus and tramway lines in Montpellier, France. +Data is licensed under ODbl, OpenStreetMap contributors + true + Montpellier - Transports + false + true + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $properties = Qgis\ProjectProperties::fromXmlReader($oXml); + + $data = array( + 'WMSServiceTitle' => 'Montpellier - Transports', + 'WMSServiceAbstract' => 'Demo project with bus and tramway lines in Montpellier, France. +Data is licensed under ODbl, OpenStreetMap contributors', + 'WMSKeywordList' => array(''), + 'WMSExtent' => array('417006.61373760335845873', '5394910.34090302512049675', '447158.04891100589884445', '5414844.99480544030666351'), + 'WMSOnlineResource' => 'http://www.3liz.com/lizmap.html', + 'WMSContactMail' => 'info@3liz.com', + 'WMSContactOrganization' => '3liz', + 'WMSContactPerson' => '3liz', + 'WMSContactPhone' => '+334 67 16 64 51', + 'WMSRestrictedComposers' => array('Composeur1'), + 'WMSRestrictedLayers' => array(), + 'WFSLayers' => null, + 'WMSUseLayerIDs' => false, + 'WMSAddWktGeometry' => true, + ); + foreach($data as $prop => $value) { + $this->assertEquals($value, $properties->$prop, $prop); + } + + $this->assertNotNull($properties->Variables); + $data = array( + 'variableNames' => array(), + 'variableValues' => array(), + ); + foreach($data as $prop => $value) { + $this->assertEquals($value, $properties->Variables->$prop, $prop); + } + + $this->assertNotNull($properties->Gui); + $data = array( + 'CanvasColorBluePart' => 255, + 'CanvasColorGreenPart' => 255, + 'CanvasColorRedPart' => 255, + 'SelectionColorAlphaPart' => 255, + 'SelectionColorBluePart' => 0, + 'SelectionColorGreenPart' => 255, + 'SelectionColorRedPart' => 255, + ); + foreach($data as $prop => $value) { + $this->assertEquals($value, $properties->Gui->$prop, $prop); + } + } +} From f91944b61f0485a249ecb4133a52ab39418bec0f Mon Sep 17 00:00:00 2001 From: rldhont Date: Thu, 16 Nov 2023 17:43:29 +0100 Subject: [PATCH 11/46] new Lizmap\Project\Qgis\LayerTree* classes --- .../lib/Project/Qgis/LayerTreeCustomOrder.php | 41 ++ .../lib/Project/Qgis/LayerTreeGroup.php | 134 +++++ .../lib/Project/Qgis/LayerTreeLayer.php | 53 ++ .../lizmap/lib/Project/Qgis/LayerTreeRoot.php | 91 ++++ .../Project/Qgis/LayerTreeGroupTest.php | 55 ++ .../Project/Qgis/LayerTreeLayerTest.php | 37 ++ .../Project/Qgis/LayerTreeRootTest.php | 468 ++++++++++++++++++ 7 files changed, 879 insertions(+) create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/LayerTreeCustomOrder.php create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/LayerTreeGroup.php create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/LayerTreeLayer.php create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/LayerTreeRoot.php create mode 100644 tests/units/classes/Project/Qgis/LayerTreeGroupTest.php create mode 100644 tests/units/classes/Project/Qgis/LayerTreeLayerTest.php create mode 100644 tests/units/classes/Project/Qgis/LayerTreeRootTest.php diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/LayerTreeCustomOrder.php b/lizmap/modules/lizmap/lib/Project/Qgis/LayerTreeCustomOrder.php new file mode 100644 index 0000000000..af63154a43 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/LayerTreeCustomOrder.php @@ -0,0 +1,41 @@ + $items + */ +class LayerTreeCustomOrder extends BaseQgisObject +{ + /** @var array The instance properties */ + protected $properties = array( + 'enabled', + 'items', + ); + + /** @var array The not null properties */ + protected $mandatoryProperties = array( + 'enabled', + ); + + protected function set(array $data): void + { + if (!$data['enabled']) { + $data['items'] = array(); + } + parent::set($data); + } +} diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/LayerTreeGroup.php b/lizmap/modules/lizmap/lib/Project/Qgis/LayerTreeGroup.php new file mode 100644 index 0000000000..931b53bc18 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/LayerTreeGroup.php @@ -0,0 +1,134 @@ + $items + */ +class LayerTreeGroup extends BaseQgisXmlObject +{ + /** @var array The instance properties */ + protected $properties = array( + 'name', + 'mutuallyExclusive', + 'customproperties', + 'items', + ); + + /** @var string The XML element local name */ + protected static $qgisLocalName = 'layer-tree-group'; + + protected static $childParsers = array(); + + /** @var array The XML element tagname associated with a collector property name */ + protected static $childrenCollection = array( + 'layer-tree-group' => 'items', + 'layer-tree-layer' => 'items', + ); + + /** + * Get attributes from an XMLReader instance at an element. + * + * @param \XMLReader $oXmlReader An XMLReader instance at an element + * + * @return array{'name': string} the element attributes as keys / values + */ + protected static function getAttributes($oXmlReader) + { + return array( + 'name' => $oXmlReader->getAttribute('name'), + 'mutuallyExclusive' => filter_var($oXmlReader->getAttribute('mutually-exclusive'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE), + ); + } + + /** + * Get group short names. + * + * @return array + */ + public function getGroupShortNames() + { + $data = array(); + foreach ($this->items as $item) { + if (!($item instanceof LayerTreeGroup)) { + continue; + } + $data += $item->getGroupShortNames(); + if (!array_key_exists('wmsShortName', $item->customproperties)) { + continue; + } + $data[$item->name] = $item->customproperties['wmsShortName']; + } + + return $data; + } + + /** + * Get groups mutually exclusive. + * + * @return array + */ + public function getGroupsMutuallyExclusive() + { + $data = array(); + foreach ($this->items as $item) { + if (!($item instanceof LayerTreeGroup)) { + continue; + } + $data = array_merge($data, $item->getGroupsMutuallyExclusive()); + if (!$item->mutuallyExclusive) { + continue; + } + $data[] = $item->name; + } + + return $data; + } + + /** + * Get layer show feature count. + * + * @return array + */ + public function getLayersShowFeatureCount() + { + $data = array(); + foreach ($this->items as $item) { + if ($item instanceof LayerTreeGroup) { + $data = array_merge($data, $item->getLayersShowFeatureCount()); + + continue; + } + if (!array_key_exists('showFeatureCount', $item->customproperties)) { + continue; + } + $data[] = $item->name; + } + + return $data; + } +} +LayerTreeGroup::registerChildParser('layer-tree-group', function ($oXmlReader) { + return LayerTreeGroup::fromXmlReader($oXmlReader); +}); +LayerTreeGroup::registerChildParser('layer-tree-layer', function ($oXmlReader) { + return LayerTreeLayer::fromXmlReader($oXmlReader); +}); +LayerTreeGroup::registerChildParser('customproperties', function ($oXmlReader) { + return Parser::readCustomProperties($oXmlReader); +}); diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/LayerTreeLayer.php b/lizmap/modules/lizmap/lib/Project/Qgis/LayerTreeLayer.php new file mode 100644 index 0000000000..c2cff19616 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/LayerTreeLayer.php @@ -0,0 +1,53 @@ + The instance properties */ + protected $properties = array( + 'name', + 'id', + 'customproperties', + ); + + /** @var string The XML element local name */ + protected static $qgisLocalName = 'layer-tree-layer'; + + protected static $childParsers = array(); + + /** + * Get attributes from an XMLReader instance at an element. + * + * @param \XMLReader $oXmlReader An XMLReader instance at an element + * + * @return array{'name': string, 'id': string} the element attributes as keys / values + */ + protected static function getAttributes($oXmlReader) + { + return array( + 'name' => $oXmlReader->getAttribute('name'), + 'id' => $oXmlReader->getAttribute('id'), + ); + } +} +LayerTreeLayer::registerChildParser('customproperties', function ($oXmlReader) { + return Parser::readCustomProperties($oXmlReader); +}); diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/LayerTreeRoot.php b/lizmap/modules/lizmap/lib/Project/Qgis/LayerTreeRoot.php new file mode 100644 index 0000000000..917d5a9b65 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/LayerTreeRoot.php @@ -0,0 +1,91 @@ + $items + * @property LayerTreeCustomOrder $customOrder + */ +class LayerTreeRoot extends LayerTreeGroup +{ + /** @var array The instance properties */ + protected $properties = array( + 'customproperties', + 'items', + 'customOrder', + ); + + /** @var array The not null properties */ + protected $mandatoryProperties = array( + 'customproperties', + 'items', + 'customOrder', + ); + + /** @var array The default values for properties */ + protected $defaultValues = array( + 'customproperties' => array(), + 'items' => array(), + ); + + /** @var array The XML element tagname associated with a collector property name */ + protected static $childrenCollection = array( + 'layer-tree-group' => 'items', + 'layer-tree-layer' => 'items', + ); + + /** @var string The XML element local name */ + protected static $qgisLocalName = 'layer-tree-group'; + + protected static $childParsers = array(); + + protected static function buildInstance($data) + { + if (array_key_exists('custom-order', $data)) { + $data['customOrder'] = $data['custom-order']; + unset($data['custom-order']); + } else { + $data['customOrder'] = new LayerTreeCustomOrder(array( + 'enabled' => false, + )); + } + + return new LayerTreeRoot($data); + } +} +LayerTreeRoot::registerChildParser('layer-tree-group', function ($oXmlReader) { + return LayerTreeGroup::fromXmlReader($oXmlReader); +}); +LayerTreeRoot::registerChildParser('layer-tree-layer', function ($oXmlReader) { + return LayerTreeLayer::fromXmlReader($oXmlReader); +}); +LayerTreeRoot::registerChildParser('customproperties', function ($oXmlReader) { + return Parser::readCustomProperties($oXmlReader); +}); +LayerTreeRoot::registerChildParser('custom-order', function ($oXmlReader) { + $data = array( + 'enabled' => filter_var($oXmlReader->getAttribute('enabled'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE), + 'items' => array(), + ); + if ($data['enabled']) { + $data['items'] = Parser::readItems($oXmlReader); + } else { + $oXmlReader->next(); + } + + return new LayerTreeCustomOrder($data); +}); diff --git a/tests/units/classes/Project/Qgis/LayerTreeGroupTest.php b/tests/units/classes/Project/Qgis/LayerTreeGroupTest.php new file mode 100644 index 0000000000..0392b61025 --- /dev/null +++ b/tests/units/classes/Project/Qgis/LayerTreeGroupTest.php @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $treeGroup = Qgis\LayerTreeGroup::fromXmlReader($oXml); + + $this->assertEquals('Buildings', $treeGroup->name); + $this->assertFalse($treeGroup->mutuallyExclusive); + + $expectedCustomproperties = array( + 'wmsShortName' => 'Buildings', + ); + $this->assertEquals($expectedCustomproperties, $treeGroup->customproperties); + + $this->assertCount(2, $treeGroup->items); + $this->assertInstanceOf(Qgis\LayerTreeLayer::class, $treeGroup->items[0]); + $this->assertEquals('publicbuildings', $treeGroup->items[0]->name); + $this->assertInstanceOf(Qgis\LayerTreeLayer::class, $treeGroup->items[1]); + $this->assertEquals('publicbuildings_tramstop', $treeGroup->items[1]->name); + } +} diff --git a/tests/units/classes/Project/Qgis/LayerTreeLayerTest.php b/tests/units/classes/Project/Qgis/LayerTreeLayerTest.php new file mode 100644 index 0000000000..38531de10b --- /dev/null +++ b/tests/units/classes/Project/Qgis/LayerTreeLayerTest.php @@ -0,0 +1,37 @@ + + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $treeLayer = Qgis\LayerTreeLayer::fromXmlReader($oXml); + + $this->assertEquals('tramway', $treeLayer->name); + $this->assertEquals('tramway20150328114206278', $treeLayer->id); + + $expectedCustomproperties = array( + 'expandedLegendNodes' => null, + 'showFeatureCount' => '1', + ); + $this->assertEquals($expectedCustomproperties, $treeLayer->customproperties); + } +} diff --git a/tests/units/classes/Project/Qgis/LayerTreeRootTest.php b/tests/units/classes/Project/Qgis/LayerTreeRootTest.php new file mode 100644 index 0000000000..7f3ada10cc --- /dev/null +++ b/tests/units/classes/Project/Qgis/LayerTreeRootTest.php @@ -0,0 +1,468 @@ + new Qgis\LayerTreeCustomOrder(array( + 'enabled' => False, + )), + ); + + $root = new Qgis\LayerTreeRoot($data); + $this->assertInstanceOf(Qgis\LayerTreeCustomOrder::class, $root->customOrder); + $this->assertFalse($root->customOrder->enabled); + $this->assertCount(0, $root->customOrder->items); + $this->assertEquals(array(), $root->customOrder->items); + + $items = array( + 'A', + 'B', + 'C', + ); + $data = array( + 'customOrder' => new Qgis\LayerTreeCustomOrder(array( + 'enabled' => True, + 'items' => $items, + )), + ); + $root = new Qgis\LayerTreeRoot($data); + $this->assertInstanceOf(Qgis\LayerTreeCustomOrder::class, $root->customOrder); + $this->assertTrue($root->customOrder->enabled); + $this->assertCount(3, $root->customOrder->items); + $this->assertEquals($items, $root->customOrder->items); + + $items = array( + 'A', + 'B', + 'C', + ); + $data = array( + 'customOrder' => new Qgis\LayerTreeCustomOrder(array( + 'enabled' => False, + 'items' => $items, + )), + ); + $root = new Qgis\LayerTreeRoot($data); + $this->assertInstanceOf(Qgis\LayerTreeCustomOrder::class, $root->customOrder); + $this->assertFalse($root->customOrder->enabled); + $this->assertCount(0, $root->customOrder->items); + $this->assertEquals(array(), $root->customOrder->items); + } + + public function testFromXmlReader() + { + $xmlStr = ' + + + edition_point20130118171631518 + edition_line20130409161630329 + edition_polygon20130409114333776 + bus_stops20121106170806413 + bus20121102133611751 + VilleMTP_MTP_Quartiers_2011_432620130116112610876 + VilleMTP_MTP_Quartiers_2011_432620130116112351546 + tramstop20150328114203878 + tramway20150328114206278 + publicbuildings20150420100958543 + SousQuartiers20160121124316563 + osm_stamen_toner20180315181710198 + osm_mapnik20180315181738526 + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $root = Qgis\LayerTreeRoot::fromXmlReader($oXml); + + $items = array(); + + $this->assertInstanceOf(Qgis\LayerTreeCustomOrder::class, $root->customOrder); + $this->assertFalse($root->customOrder->enabled); + $this->assertCount(0, $root->customOrder->items); + $this->assertEquals($items, $root->customOrder->items); + + $xmlStr = ' + + + edition_point20130118171631518 + edition_line20130409161630329 + edition_polygon20130409114333776 + bus_stops20121106170806413 + bus20121102133611751 + VilleMTP_MTP_Quartiers_2011_432620130116112610876 + VilleMTP_MTP_Quartiers_2011_432620130116112351546 + tramstop20150328114203878 + tramway20150328114206278 + publicbuildings20150420100958543 + SousQuartiers20160121124316563 + osm_stamen_toner20180315181710198 + osm_mapnik20180315181738526 + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $root = Qgis\LayerTreeRoot::fromXmlReader($oXml); + + $items = array( + 'edition_point20130118171631518', + 'edition_line20130409161630329', + 'edition_polygon20130409114333776', + 'bus_stops20121106170806413', + 'bus20121102133611751', + 'VilleMTP_MTP_Quartiers_2011_432620130116112610876', + 'VilleMTP_MTP_Quartiers_2011_432620130116112351546', + 'tramstop20150328114203878', + 'tramway20150328114206278', + 'publicbuildings20150420100958543', + 'SousQuartiers20160121124316563', + 'osm_stamen_toner20180315181710198', + 'osm_mapnik20180315181738526', + ); + + $this->assertInstanceOf(Qgis\LayerTreeCustomOrder::class, $root->customOrder); + $this->assertTrue($root->customOrder->enabled); + $this->assertCount(13, $root->customOrder->items); + $this->assertEquals($items, $root->customOrder->items); + + + $xmlStr = ' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + edition_point20130118171631518 + edition_line20130409161630329 + edition_polygon20130409114333776 + bus_stops20121106170806413 + bus20121102133611751 + VilleMTP_MTP_Quartiers_2011_432620130116112610876 + VilleMTP_MTP_Quartiers_2011_432620130116112351546 + tramstop20150328114203878 + tramway20150328114206278 + publicbuildings20150420100958543 + SousQuartiers20160121124316563 + osm_mapnik20180315181738526 + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $root = Qgis\LayerTreeRoot::fromXmlReader($oXml); + + $this->assertCount(0, $root->customproperties); + + $this->assertCount(7, $root->items); + + $this->assertInstanceOf(Qgis\LayerTreeGroup::class, $root->items[0]); + $this->assertEquals('Edition', $root->items[0]->name); + $this->assertCount(3, $root->items[0]->items); + + $this->assertInstanceOf(Qgis\LayerTreeLayer::class, $root->items[0]->items[0]); + $this->assertInstanceOf(Qgis\LayerTreeLayer::class, $root->items[0]->items[1]); + $this->assertInstanceOf(Qgis\LayerTreeLayer::class, $root->items[0]->items[2]); + + $this->assertInstanceOf(Qgis\LayerTreeGroup::class, $root->items[1]); + $this->assertEquals('datalayers', $root->items[1]->name); + $this->assertCount(3, $root->items[1]->items); + + $this->assertInstanceOf(Qgis\LayerTreeGroup::class, $root->items[1]->items[0]); + $this->assertEquals('Bus', $root->items[1]->items[0]->name); + $this->assertCount(2, $root->items[1]->items[0]->items); + $this->assertInstanceOf(Qgis\LayerTreeLayer::class, $root->items[1]->items[0]->items[0]); + $this->assertInstanceOf(Qgis\LayerTreeLayer::class, $root->items[1]->items[0]->items[1]); + + $this->assertInstanceOf(Qgis\LayerTreeGroup::class, $root->items[1]->items[1]); + $this->assertEquals('Tramway', $root->items[1]->items[1]->name); + $this->assertCount(5, $root->items[1]->items[1]->items); + $this->assertInstanceOf(Qgis\LayerTreeLayer::class, $root->items[1]->items[1]->items[0]); + $this->assertInstanceOf(Qgis\LayerTreeLayer::class, $root->items[1]->items[1]->items[1]); + $this->assertInstanceOf(Qgis\LayerTreeLayer::class, $root->items[1]->items[1]->items[2]); + $this->assertInstanceOf(Qgis\LayerTreeLayer::class, $root->items[1]->items[1]->items[3]); + $this->assertInstanceOf(Qgis\LayerTreeLayer::class, $root->items[1]->items[1]->items[4]); + + $this->assertInstanceOf(Qgis\LayerTreeGroup::class, $root->items[1]->items[2]); + $this->assertEquals('Buildings', $root->items[1]->items[2]->name); + $this->assertCount(2, $root->items[1]->items[2]->items); + + $this->assertInstanceOf(Qgis\LayerTreeLayer::class, $root->items[2]); + $this->assertEquals('donnes_sociodemo_sous_quartiers', $root->items[2]->name); + + $this->assertInstanceOf(Qgis\LayerTreeLayer::class, $root->items[3]); + $this->assertEquals('SousQuartiers', $root->items[3]->name); + + $this->assertInstanceOf(Qgis\LayerTreeLayer::class, $root->items[4]); + $this->assertEquals('Quartiers', $root->items[4]->name); + + $this->assertInstanceOf(Qgis\LayerTreeGroup::class, $root->items[5]); + $this->assertEquals('Overview', $root->items[5]->name); + + $this->assertInstanceOf(Qgis\LayerTreeGroup::class, $root->items[6]); + $this->assertEquals('Hidden', $root->items[6]->name); + + $this->assertInstanceOf(Qgis\LayerTreeCustomOrder::class, $root->customOrder); + $this->assertFalse($root->customOrder->enabled); + $this->assertCount(0, $root->customOrder->items); + + $groupShortNames = $root->getGroupShortNames(); + $this->assertCount(7, $groupShortNames); + + $expectedGroupShortNames = array( + 'Edition' => 'Edition', + 'Bus' => 'Bus', + 'Tramway' => 'Tramway', + 'Buildings' => 'Buildings', + 'datalayers' => 'datalayers', + 'Overview' => 'Overview', + 'Hidden' => 'Hidden', + ); + $this->assertEquals($expectedGroupShortNames, $groupShortNames); + + $groupsMutuallyExclusive = $root->getGroupsMutuallyExclusive(); + $this->assertCount(1, $groupsMutuallyExclusive); + $this->assertEquals('Hidden', $groupsMutuallyExclusive[0]); + + $layersShowFeatureCount = $root->getLayersShowFeatureCount(); + $this->assertCount(1, $layersShowFeatureCount); + $this->assertEquals('tramway', $layersShowFeatureCount[0]); + } +} From 079a13cba9df9c299880f4e0a67e6425f97e28d8 Mon Sep 17 00:00:00 2001 From: rldhont Date: Thu, 30 Nov 2023 12:18:42 +0100 Subject: [PATCH 12/46] new Lizmap\Project\Qgis\Layout\* classes --- .../lizmap/lib/Project/Qgis/Layout/Layout.php | 111 ++++++ .../lib/Project/Qgis/Layout/LayoutItem.php | 141 ++++++++ .../Project/Qgis/Layout/LayoutItemLabel.php | 57 +++ .../lib/Project/Qgis/Layout/LayoutItemMap.php | 56 +++ .../Project/Qgis/Layout/LayoutItemMapGrid.php | 43 +++ .../Qgis/Layout/LayoutItemMapOverview.php | 47 +++ .../Project/Qgis/Layout/LayoutItemPage.php | 48 +++ .../classes/Project/Qgis/LayoutItemTest.php | 339 ++++++++++++++++++ 8 files changed, 842 insertions(+) create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/Layout/Layout.php create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/Layout/LayoutItem.php create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/Layout/LayoutItemLabel.php create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/Layout/LayoutItemMap.php create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/Layout/LayoutItemMapGrid.php create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/Layout/LayoutItemMapOverview.php create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/Layout/LayoutItemPage.php create mode 100644 tests/units/classes/Project/Qgis/LayoutItemTest.php diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/Layout/Layout.php b/lizmap/modules/lizmap/lib/Project/Qgis/Layout/Layout.php new file mode 100644 index 0000000000..67927c2f25 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/Layout/Layout.php @@ -0,0 +1,111 @@ + $PageCollection + * @property array $Items + * @property array $Atlas + */ +class Layout extends Qgis\BaseQgisXmlObject +{ + /** @var array The instance properties */ + protected $properties = array( + 'name', + 'PageCollection', + 'Items', + 'Atlas', + ); + + /** @var array The not null properties */ + protected $mandatoryProperties = array( + 'name', + 'PageCollection', + 'Items', + ); + + /** @var string The XML element local name */ + protected static $qgisLocalName = 'Layout'; + + protected static $childParsers = array(); + + /** + * Get attributes from an XMLReader instance at an element. + * + * @param \XMLReader $oXmlReader An XMLReader instance at an element + * + * @return array{'name': string} the element attributes as keys / values + */ + protected static function getAttributes($oXmlReader) + { + return array( + 'name' => $oXmlReader->getAttribute('name'), + ); + } + + protected static function buildInstance($data) + { + $data['Items'] = array(); + if (array_key_exists('LayoutItem', $data)) { + $data['Items'] = $data['LayoutItem']; + unset($data['LayoutItem']); + } + + return new Layout($data); + } +} +Layout::registerChildParser('PageCollection', function ($oXmlReader) { + $depth = $oXmlReader->depth; + $localName = $oXmlReader->localName; + $data = array(); + if ($oXmlReader->isEmptyElement) { + return $data; + } + while ($oXmlReader->read()) { + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'LayoutItem') { + $data[] = LayoutItem::fromXmlReader($oXmlReader); + } + } + + return $data; +}); +Layout::registerChildParser('LayoutItem', function ($oXmlReader) { + return array( + LayoutItem::fromXmlReader($oXmlReader), + ); +}); +Layout::registerChildParser('Atlas', function ($oXmlReader) { + return array( + 'enabled' => filter_var($oXmlReader->getAttribute('enabled'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE), + 'coverageLayer' => $oXmlReader->getAttribute('coverageLayer'), + ); +}); diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/Layout/LayoutItem.php b/lizmap/modules/lizmap/lib/Project/Qgis/Layout/LayoutItem.php new file mode 100644 index 0000000000..f562c37263 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/Layout/LayoutItem.php @@ -0,0 +1,141 @@ + The instance properties */ + protected $properties = array( + 'type', + 'width', + 'height', + 'x', + 'y', + ); + + /** @var array The not null properties */ + protected $mandatoryProperties = array( + 'type', + ); + + /** @var string The XML element local name */ + protected static $qgisLocalName = 'LayoutItem'; + + protected static $childParsers = array(); + + /** + * Get attributes from an XMLReader instance at an element. + * + * @param \XMLReader $oXmlReader An XMLReader instance at an element + * + * @return array{'type': int, 'width': int, 'height': int, 'x': int, 'y': int} the element attributes as keys / values + */ + protected static function getAttributes($oXmlReader) + { + $size = explode(',', $oXmlReader->getAttribute('size')); + $position = explode(',', $oXmlReader->getAttribute('position')); + $data = array( + 'type' => (int) $oXmlReader->getAttribute('type'), + 'width' => (int) $size[0], + 'height' => (int) $size[1], + 'x' => (int) $position[0], + 'y' => (int) $position[1], + ); + + if ($data['type'] === 65638) { + $data['typeName'] = 'page'; + } elseif ($data['type'] === 65639) { + $data += array( + 'typeName' => 'map', + 'uuid' => $oXmlReader->getAttribute('uuid'), + 'grid' => false, + ); + } elseif ($data['type'] === 65641) { + $data += array( + 'typeName' => 'label', + 'id' => $oXmlReader->getAttribute('id'), + 'htmlState' => filter_var($oXmlReader->getAttribute('htmlState'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE), + 'text' => $oXmlReader->getAttribute('labelText'), + ); + } + + return $data; + } + + /** + * Build and instance with data as an array. + * + * @param array $data the instance data + * + * @return LayoutItem|LayoutItemLabel|LayoutItemMap|LayoutItemPage the instance + */ + protected static function buildInstance($data) + { + if (!array_key_exists('typeName', $data)) { + return new LayoutItem($data); + } + if ($data['typeName'] == 'page') { + return new LayoutItemPage($data); + } + if ($data['typeName'] == 'label') { + return new LayoutItemLabel($data); + } + if ($data['typeName'] == 'map') { + if (array_key_exists('ComposerMapOverview', $data)) { + foreach ($data['ComposerMapOverview'] as $overview) { + if ($overview->show && $overview->frameMap != '-1') { + $data['overviewMap'] = $overview->frameMap; + + break; + } + } + unset($data['ComposerMapOverview']); + } + if (array_key_exists('ComposerMapGrid', $data)) { + foreach ($data['ComposerMapGrid'] as $grid) { + if ($grid->show) { + $data['grid'] = true; + } + + break; + } + unset($data['ComposerMapGrid']); + } + + return new LayoutItemMap($data); + } + + return new LayoutItem($data); + } +} +LayoutItem::registerChildParser('ComposerMapOverview', function ($oXmlReader) { + return array( + LayoutItemMapOverview::fromXmlReader($oXmlReader), + ); +}); +LayoutItem::registerChildParser('ComposerMapGrid', function ($oXmlReader) { + return array( + LayoutItemMapGrid::fromXmlReader($oXmlReader), + ); +}); diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/Layout/LayoutItemLabel.php b/lizmap/modules/lizmap/lib/Project/Qgis/Layout/LayoutItemLabel.php new file mode 100644 index 0000000000..c9b463eb56 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/Layout/LayoutItemLabel.php @@ -0,0 +1,57 @@ + The instance properties */ + protected $properties = array( + 'type', + 'typeName', + 'width', + 'height', + 'x', + 'y', + 'id', + 'htmlState', + 'text', + ); + + /** @var array The not null properties */ + protected $mandatoryProperties = array( + 'type', + 'typeName', + 'width', + 'height', + 'x', + 'y', + 'id', + 'htmlState', + 'text', + ); +} diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/Layout/LayoutItemMap.php b/lizmap/modules/lizmap/lib/Project/Qgis/Layout/LayoutItemMap.php new file mode 100644 index 0000000000..b23b7c7f32 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/Layout/LayoutItemMap.php @@ -0,0 +1,56 @@ + The instance properties */ + protected $properties = array( + 'type', + 'typeName', + 'width', + 'height', + 'x', + 'y', + 'uuid', + 'grid', + 'overviewMap', + ); + + /** @var array The not null properties */ + protected $mandatoryProperties = array( + 'type', + 'typeName', + 'width', + 'height', + 'x', + 'y', + 'uuid', + 'grid', + ); +} diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/Layout/LayoutItemMapGrid.php b/lizmap/modules/lizmap/lib/Project/Qgis/Layout/LayoutItemMapGrid.php new file mode 100644 index 0000000000..ebe78619e7 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/Layout/LayoutItemMapGrid.php @@ -0,0 +1,43 @@ + The instance properties */ + protected $properties = array( + 'show', + ); + + /** @var array The not null properties */ + protected $mandatoryProperties = array( + 'show', + ); + + /** @var string The XML element local name */ + protected static $qgisLocalName = 'ComposerMapGrid'; + + protected static function getAttributes($oXmlReader) + { + return array( + 'show' => filter_var($oXmlReader->getAttribute('show'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE), + ); + } +} diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/Layout/LayoutItemMapOverview.php b/lizmap/modules/lizmap/lib/Project/Qgis/Layout/LayoutItemMapOverview.php new file mode 100644 index 0000000000..4a91519868 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/Layout/LayoutItemMapOverview.php @@ -0,0 +1,47 @@ + The instance properties */ + protected $properties = array( + 'show', + 'frameMap', + ); + + /** @var array The not null properties */ + protected $mandatoryProperties = array( + 'show', + 'frameMap', + ); + + /** @var string The XML element local name */ + protected static $qgisLocalName = 'ComposerMapOverview'; + + protected static function getAttributes($oXmlReader) + { + return array( + 'show' => filter_var($oXmlReader->getAttribute('show'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE), + 'frameMap' => $oXmlReader->getAttribute('frameMap'), + ); + } +} diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/Layout/LayoutItemPage.php b/lizmap/modules/lizmap/lib/Project/Qgis/Layout/LayoutItemPage.php new file mode 100644 index 0000000000..72561b1cdb --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/Layout/LayoutItemPage.php @@ -0,0 +1,48 @@ + The instance properties */ + protected $properties = array( + 'type', + 'typeName', + 'width', + 'height', + 'x', + 'y', + ); + + /** @var array The not null properties */ + protected $mandatoryProperties = array( + 'type', + 'typeName', + 'width', + 'height', + 'x', + 'y', + ); +} diff --git a/tests/units/classes/Project/Qgis/LayoutItemTest.php b/tests/units/classes/Project/Qgis/LayoutItemTest.php new file mode 100644 index 0000000000..76fcb9c2bf --- /dev/null +++ b/tests/units/classes/Project/Qgis/LayoutItemTest.php @@ -0,0 +1,339 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $item = Qgis\Layout\LayoutItem::fromXmlReader($oXml); + $this->assertInstanceOf(Qgis\Layout\LayoutItemPage::class, $item); + + $data = array( + 'type' => 65638, + 'typeName' => 'page', + 'width' => 297, + 'height' => 210, + 'x' => 0, + 'y' => 0, + ); + foreach($data as $prop => $value) { + $this->assertEquals($value, $item->$prop, $prop); + } + + // LayoutItemLabel + $xmlStr = ' + + + + + + + + + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $item = Qgis\Layout\LayoutItem::fromXmlReader($oXml); + $this->assertInstanceOf(Qgis\Layout\LayoutItemLabel::class, $item); + + $data = array( + 'type' => 65641, + 'typeName' => 'label', + 'id' => '', + 'htmlState' => false, + 'text' => 'Tram stops in the district', + ); + foreach($data as $prop => $value) { + $this->assertEquals($value, $item->$prop, $prop); + } + + // LayoutItemMap + $xmlStr = ' + + + + + + + + + + + + VilleMTP_MTP_Quartiers_2011_432620130116112351546 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $item = Qgis\Layout\LayoutItem::fromXmlReader($oXml); + $this->assertInstanceOf(Qgis\Layout\LayoutItemMap::class, $item); + + $data = array( + 'type' => 65639, + 'typeName' => 'map', + 'uuid' => '{a50537f9-5e73-4610-955e-31b092d81b94}', + 'width' => 60, + 'height' => 45, + 'grid' => false, + 'overviewMap' => '{a228885d-4d7a-4ae0-af9b-02a4fe7cd814}', + ); + foreach($data as $prop => $value) { + $this->assertEquals($value, $item->$prop, $prop); + } + + // LayoutItemMap + $xmlStr = ' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $item = Qgis\Layout\LayoutItem::fromXmlReader($oXml); + $this->assertInstanceOf(Qgis\Layout\LayoutItem::class, $item); + + $data = array( + 'type' => 65642, + 'width' => 85, + 'height' => 130, + 'x' => 252, + 'y' => 84, + ); + foreach($data as $prop => $value) { + $this->assertEquals($value, $item->$prop, $prop); + } + } +} From 57c62d29680feefbb8d9df5b94d47639dbefeb00 Mon Sep 17 00:00:00 2001 From: rldhont Date: Tue, 15 Oct 2024 17:56:44 +0200 Subject: [PATCH 13/46] new Lizmap\Project\Qgis\Layer\* classes --- .../Qgis/Layer/AttributeTableColumn.php | 51 + .../Qgis/Layer/AttributeTableConfig.php | 112 ++ .../lib/Project/Qgis/Layer/EmbeddedLayer.php | 62 + .../lib/Project/Qgis/Layer/MapLayer.php | 408 +++++++ .../Qgis/Layer/MapLayerStyleManager.php | 60 + .../lib/Project/Qgis/Layer/RendererV2.php | 86 ++ .../lib/Project/Qgis/Layer/VectorLayer.php | 161 +++ .../Project/Qgis/Layer/VectorLayerAlias.php | 51 + .../Qgis/Layer/VectorLayerConstraint.php | 59 + .../Layer/VectorLayerConstraintExpression.php | 51 + .../Project/Qgis/Layer/VectorLayerDefault.php | 51 + .../Qgis/Layer/VectorLayerEditableField.php | 47 + .../Project/Qgis/Layer/VectorLayerField.php | 70 ++ .../Project/Qgis/Layer/VectorLayerJoin.php | 51 + .../Project/Qgis/AttributeTableConfigTest.php | 184 +++ .../Project/Qgis/MapLayerStyleManagerTest.php | 478 ++++++++ .../classes/Project/Qgis/MapLayerTest.php | 1024 +++++++++++++++++ .../classes/Project/Qgis/RendererV2Test.php | 191 +++ .../VectorLayerConstraintExpressionTest.php | 36 + .../Qgis/VectorLayerConstraintTest.php | 54 + 20 files changed, 3287 insertions(+) create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/Layer/AttributeTableColumn.php create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/Layer/AttributeTableConfig.php create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/Layer/EmbeddedLayer.php create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/Layer/MapLayer.php create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/Layer/MapLayerStyleManager.php create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/Layer/RendererV2.php create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayer.php create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerAlias.php create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerConstraint.php create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerConstraintExpression.php create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerDefault.php create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerEditableField.php create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerField.php create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerJoin.php create mode 100644 tests/units/classes/Project/Qgis/AttributeTableConfigTest.php create mode 100644 tests/units/classes/Project/Qgis/MapLayerStyleManagerTest.php create mode 100644 tests/units/classes/Project/Qgis/MapLayerTest.php create mode 100644 tests/units/classes/Project/Qgis/RendererV2Test.php create mode 100644 tests/units/classes/Project/Qgis/VectorLayerConstraintExpressionTest.php create mode 100644 tests/units/classes/Project/Qgis/VectorLayerConstraintTest.php diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/Layer/AttributeTableColumn.php b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/AttributeTableColumn.php new file mode 100644 index 0000000000..9744e019a7 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/AttributeTableColumn.php @@ -0,0 +1,51 @@ + The instance properties */ + protected $properties = array( + 'type', + 'name', + 'hidden', + ); + + /** @var array The not null properties */ + protected $mandatoryProperties = array( + 'type', + 'name', + 'hidden', + ); + + /** @var string The XML element local name */ + protected static $qgisLocalName = 'column'; + + protected static function getAttributes($oXmlReader) + { + return array( + 'type' => $oXmlReader->getAttribute('type'), + 'name' => $oXmlReader->getAttribute('name'), + 'hidden' => filter_var($oXmlReader->getAttribute('hidden'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE), + ); + } +} diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/Layer/AttributeTableConfig.php b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/AttributeTableConfig.php new file mode 100644 index 0000000000..1f83aadf2d --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/AttributeTableConfig.php @@ -0,0 +1,112 @@ + $columns + */ +class AttributeTableConfig extends Project\Qgis\BaseQgisXmlObject +{ + /** @var array The instance properties */ + protected $properties = array( + 'sortExpression', + 'sortOrder', + 'columns', + ); + + /** @var array The not null properties */ + protected $mandatoryProperties = array( + 'sortExpression', + 'sortOrder', + 'columns', + ); + + /** + * Get vector layer attribute table config as key array. + * + * @return array + */ + public function toKeyArray() + { + $data = array(); + if ($this->columns) { + foreach ($this->columns as $idx => $column) { + if ($column->hidden) { + continue; + } + $data[] = array( + 'index' => $idx, + 'type' => $column->type, + 'name' => $column->name, + ); + } + } + + return array( + 'columns' => $data, + ); + } + + /** @var string The XML element local name */ + protected static $qgisLocalName = 'attributetableconfig'; + + protected static function getAttributes($oXmlReader) + { + return array( + 'sortExpression' => $oXmlReader->getAttribute('sortExpression'), + 'sortOrder' => (int) $oXmlReader->getAttribute('sortOrder'), + ); + } + + /** @var array The XML element parsed children */ + protected static $children = array( + 'columns', + ); + + protected static $childParsers = array(); +} +AttributeTableConfig::registerChildParser('columns', function ($oXmlReader) { + $depth = $oXmlReader->depth; + $localName = $oXmlReader->localName; + $data = array(); + if ($oXmlReader->isEmptyElement) { + return $data; + } + while ($oXmlReader->read()) { + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'column') { + $data[] = AttributeTableColumn::fromXmlReader($oXmlReader); + } + } + + return $data; +}); diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/Layer/EmbeddedLayer.php b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/EmbeddedLayer.php new file mode 100644 index 0000000000..dc0be61639 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/EmbeddedLayer.php @@ -0,0 +1,62 @@ + The instance properties */ + protected $properties = array( + 'id', + 'embedded', + 'type', + 'project', + ); + + /** @var array The not null properties */ + protected $mandatoryProperties = array( + 'id', + 'embedded', + 'project', + ); + + protected function set(array $data): void + { + $data['type'] = 'embedded'; + parent::set($data); + } + + /** + * Get embedded layer as key array. + * + * @return array + */ + public function toKeyArray() + { + return array( + 'id' => $this->id, + 'embedded' => $this->embedded, + 'type' => $this->type, + 'project' => $this->project, + ); + } +} diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/Layer/MapLayer.php b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/MapLayer.php new file mode 100644 index 0000000000..3ec4f78cf5 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/MapLayer.php @@ -0,0 +1,408 @@ + $keywordList + * @property float $layerOpacity + */ +class MapLayer extends Qgis\BaseQgisXmlObject +{ + /** @var array The instance properties */ + protected $properties = array( + 'id', + 'embedded', + 'type', + 'layername', + 'srs', + 'datasource', + 'provider', + 'styleManager', + 'shortname', + 'title', + 'abstract', + 'keywordList', + 'layerOpacity', + ); + + /** @var array The not null properties */ + protected $mandatoryProperties = array( + 'id', + 'embedded', + 'type', + 'layername', + 'srs', + 'datasource', + 'provider', + 'styleManager', + ); + + /** @var array The default values for properties */ + protected $defaultValues = array( + 'layerOpacity' => 1, + ); + + /** + * Get map layer as key array. + * + * @return array + */ + public function toKeyArray() + { + return array( + 'type' => $this->type, + 'id' => $this->id, + 'name' => $this->layername, + 'shortname' => $this->shortname !== null ? $this->shortname : '', + 'title' => $this->title !== null ? $this->title : $this->layername, + 'abstract' => $this->abstract !== null ? $this->abstract : '', + 'proj4' => $this->srs->proj4, + 'srid' => $this->srs->srid, + 'authid' => $this->srs->authid, + 'datasource' => $this->datasource, + 'provider' => $this->provider, + 'keywords' => $this->keywordList !== null ? $this->keywordList : array(), + ); + } + + /** @var string The XML element local name */ + protected static $qgisLocalName = 'maplayer'; + + /** @var array The XML element parsed children */ + protected static $children = array( + 'id', + 'layername', + 'shortname', + 'title', + 'abstract', + 'srs', + 'datasource', + 'provider', + 'keywordList', + 'previewExpression', + 'layerOpacity', + ); + + protected static $childParsers = array(); + + protected static function getAttributes($oXmlReader) + { + // The maplayer can reference an embeded layer + $embedded = filter_var($oXmlReader->getAttribute('embedded'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + if ($embedded) { + return array( + 'id' => $oXmlReader->getAttribute('id'), + 'embedded' => true, + 'project' => $oXmlReader->getAttribute('project'), + ); + } + + return array( + 'type' => $oXmlReader->getAttribute('type'), + 'embedded' => false, + ); + } + + /** + * Build an instance with data as an array. + * + * @param array $data the instance data + * + * @return EmbeddedLayer|MapLayer|VectorLayer the instance + */ + protected static function buildInstance($data) + { + if (array_key_exists('embedded', $data) + && $data['embedded']) { + return new EmbeddedLayer($data); + } + if (array_key_exists('layerOpacity', $data)) { + $data['layerOpacity'] = (float) $data['layerOpacity']; + } + if (array_key_exists('map-layer-style-manager', $data)) { + $data['styleManager'] = $data['map-layer-style-manager']; + unset($data['map-layer-style-manager']); + } + if (array_key_exists('renderer-v2', $data)) { + $data['rendererV2'] = $data['renderer-v2']; + unset($data['renderer-v2']); + } + if (array_key_exists('type', $data) + && $data['type'] === 'vector') { + return new VectorLayer($data); + } + + return new MapLayer($data); + } +} +MapLayer::registerChildParser('keywordList', function ($oXmlReader) { + return Qgis\Parser::readValues($oXmlReader); +}); +MapLayer::registerChildParser('srs', function ($oXmlReader) { + $depth = $oXmlReader->depth; + $localName = $oXmlReader->localName; + while ($oXmlReader->read()) { + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'spatialrefsys') { + break; + } + } + + return Qgis\SpatialRefSys::fromXmlReader($oXmlReader); +}); +MapLayer::registerChildParser('map-layer-style-manager', function ($oXmlReader) { + return MapLayerStyleManager::fromXmlReader($oXmlReader); +}); +MapLayer::registerChildParser('fieldConfiguration', function ($oXmlReader) { + $data = array(); + if ($oXmlReader->isEmptyElement) { + return $data; + } + $depth = $oXmlReader->depth; + $localName = $oXmlReader->localName; + while ($oXmlReader->read()) { + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'field') { + $data[] = VectorLayerField::fromXmlReader($oXmlReader); + } + } + + return $data; +}); +MapLayer::registerChildParser('aliases', function ($oXmlReader) { + $data = array(); + if ($oXmlReader->isEmptyElement) { + return $data; + } + $depth = $oXmlReader->depth; + $localName = $oXmlReader->localName; + while ($oXmlReader->read()) { + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'alias') { + $data[] = VectorLayerAlias::fromXmlReader($oXmlReader); + } + } + + return $data; +}); +MapLayer::registerChildParser('constraints', function ($oXmlReader) { + $data = array(); + if ($oXmlReader->isEmptyElement) { + return $data; + } + $depth = $oXmlReader->depth; + $localName = $oXmlReader->localName; + while ($oXmlReader->read()) { + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'constraint') { + $data[] = VectorLayerConstraint::fromXmlReader($oXmlReader); + } + } + + return $data; +}); +MapLayer::registerChildParser('constraintExpressions', function ($oXmlReader) { + $data = array(); + if ($oXmlReader->isEmptyElement) { + return $data; + } + $depth = $oXmlReader->depth; + $localName = $oXmlReader->localName; + while ($oXmlReader->read()) { + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'constraint') { + $data[] = VectorLayerConstraintExpression::fromXmlReader($oXmlReader); + } + } + + return $data; +}); +MapLayer::registerChildParser('defaults', function ($oXmlReader) { + $data = array(); + if ($oXmlReader->isEmptyElement) { + return $data; + } + $depth = $oXmlReader->depth; + $localName = $oXmlReader->localName; + while ($oXmlReader->read()) { + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'default') { + $data[] = VectorLayerDefault::fromXmlReader($oXmlReader); + } + } + + return $data; +}); +MapLayer::registerChildParser('editable', function ($oXmlReader) { + $data = array(); + if ($oXmlReader->isEmptyElement) { + return $data; + } + $depth = $oXmlReader->depth; + $localName = $oXmlReader->localName; + while ($oXmlReader->read()) { + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'field') { + $data[] = VectorLayerEditableField::fromXmlReader($oXmlReader); + } + } + + return $data; +}); +MapLayer::registerChildParser('vectorjoins', function ($oXmlReader) { + $data = array(); + if ($oXmlReader->isEmptyElement) { + return $data; + } + $depth = $oXmlReader->depth; + $localName = $oXmlReader->localName; + while ($oXmlReader->read()) { + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'join') { + $data[] = VectorLayerJoin::fromXmlReader($oXmlReader); + } + } + + return $data; +}); +MapLayer::registerChildParser('attributetableconfig', function ($oXmlReader) { + return AttributeTableConfig::fromXmlReader($oXmlReader); +}); +MapLayer::registerChildParser('excludeAttributesWFS', function ($oXmlReader) { + return Qgis\Parser::readAttributes($oXmlReader); +}); +MapLayer::registerChildParser('excludeAttributesWMS', function ($oXmlReader) { + return Qgis\Parser::readAttributes($oXmlReader); +}); +MapLayer::registerChildParser('renderer-v2', function ($oXmlReader) { + return RendererV2::fromXmlReader($oXmlReader); +}); diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/Layer/MapLayerStyleManager.php b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/MapLayerStyleManager.php new file mode 100644 index 0000000000..906285e535 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/MapLayerStyleManager.php @@ -0,0 +1,60 @@ + $styles + */ +class MapLayerStyleManager extends Qgis\BaseQgisXmlObject +{ + /** @var array The instance properties */ + protected $properties = array( + 'current', + 'styles', + ); + + /** @var string The XML element local name */ + protected static $qgisLocalName = 'map-layer-style-manager'; + + /** @var array The XML element parsed children */ + protected static $children = array( + 'map-layer-style', + ); + + protected static $childParsers = array(); + + protected static function getAttributes($oXmlReader) + { + return array( + 'current' => $oXmlReader->getAttribute('current'), + ); + } + + protected static function buildInstance($data) + { + if (array_key_exists('map-layer-style', $data)) { + $data['styles'] = $data['map-layer-style']; + unset($data['map-layer-style']); + } + + return new MapLayerStyleManager($data); + } +} +MapLayerStyleManager::registerChildParser('map-layer-style', function ($oXmlReader) { + return array($oXmlReader->getAttribute('name')); +}); diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/Layer/RendererV2.php b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/RendererV2.php new file mode 100644 index 0000000000..c343b9538b --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/RendererV2.php @@ -0,0 +1,86 @@ + The instance properties */ + protected $properties = array( + 'type', + 'categories', + ); + + /** @var array The not null properties */ + protected $mandatoryProperties = array( + 'type', + ); + + /** @var array The default values for properties */ + protected $defaultValues = array( + 'categories' => array(), + ); + + /** @var string The XML element local name */ + protected static $qgisLocalName = 'renderer-v2'; + + /** @var array The XML element parsed children */ + protected static $children = array( + 'categories', + ); + + protected static $childParsers = array(); + + protected static function getAttributes($oXmlReader) + { + return array( + 'type' => $oXmlReader->getAttribute('type'), + ); + } +} +RendererV2::registerChildParser('categories', function ($oXmlReader) { + $data = array(); + if ($oXmlReader->isEmptyElement) { + return $data; + } + $depth = $oXmlReader->depth; + $localName = $oXmlReader->localName; + while ($oXmlReader->read()) { + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'category') { + $data[$oXmlReader->getAttribute('value')] = $oXmlReader->getAttribute('label'); + } + } + + return $data; +}); diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayer.php b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayer.php new file mode 100644 index 0000000000..9d83030c77 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayer.php @@ -0,0 +1,161 @@ + $keywordList + * @property null|string $previewExpression + * @property float $layerOpacity + * @property MapLayerStyleManager $styleManager + * @property array $fieldConfiguration + * @property array $aliases + * @property array $constraints + * @property array $constraintExpressions + * @property array $defaults + * @property array $editable + * @property array $vectorjoins + * @property AttributeTableConfig $attributetableconfig + * @property null|array $excludeAttributesWMS + * @property null|array $excludeAttributesWFS + * @property null|RendererV2 $rendererV2 + */ +class VectorLayer extends Qgis\BaseQgisObject +{ + /** @var array The instance properties */ + protected $properties = array( + 'id', + 'embedded', + 'type', + 'layername', + 'srs', + 'datasource', + 'provider', + 'styleManager', + 'shortname', + 'title', + 'abstract', + 'keywordList', + 'previewExpression', + 'layerOpacity', + 'fieldConfiguration', + 'aliases', + 'defaults', + 'constraints', + 'constraintExpressions', + 'editable', + 'excludeAttributesWFS', + 'excludeAttributesWMS', + 'attributetableconfig', + 'vectorjoins', + 'rendererV2', + ); + + /** @var array The not null properties */ + protected $mandatoryProperties = array( + 'id', + 'embedded', + 'type', + 'layername', + 'srs', + 'datasource', + 'provider', + 'styleManager', + ); + + /** @var array The default values for properties */ + protected $defaultValues = array( + 'layerOpacity' => 1, + ); + + /** + * Get preview field. + * + * @return string + */ + public function getPreviewField() + { + if ($this->previewExpression === null) { + return ''; + } + $previewField = $this->previewExpression; + if (substr($previewField, 0, 8) == 'COALESCE') { + if (preg_match('/"([\S ]+)"/', $previewField, $matches) == 1) { + $previewField = $matches[1]; + } else { + $previewField = ''; + } + } elseif (substr($previewField, 0, 1) == '"' and substr($previewField, -1) == '"') { + $previewField = substr($previewField, 1, -1); + } + + return $previewField; + } + + /** + * Get field alias. + * + * @param mixed $fieldName + * + * @return null|string + */ + public function getFieldAlias($fieldName) + { + if ($this->aliases === null) { + return null; + } + foreach ($this->aliases as $alias) { + if ($alias->field === $fieldName) { + return $alias->name; + } + } + + return null; + } + + /** + * Get field editable. + * + * @param mixed $fieldName + * + * @return bool + */ + public function getFieldEditable($fieldName) + { + if (count($this->editable) == 0) { + return true; + } + foreach ($this->editable as $editable) { + if ($editable->name === $fieldName) { + return $editable->editable; + } + } + + return false; + } +} diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerAlias.php b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerAlias.php new file mode 100644 index 0000000000..ffb6fa3379 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerAlias.php @@ -0,0 +1,51 @@ + The instance properties */ + protected $properties = array( + 'index', + 'field', + 'name', + ); + + /** @var array The not null properties */ + protected $mandatoryProperties = array( + 'index', + 'field', + 'name', + ); + + /** @var string The XML element local name */ + protected static $qgisLocalName = 'alias'; + + protected static function getAttributes($oXmlReader) + { + return array( + 'index' => (int) $oXmlReader->getAttribute('index'), + 'field' => $oXmlReader->getAttribute('field'), + 'name' => $oXmlReader->getAttribute('name'), + ); + } +} diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerConstraint.php b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerConstraint.php new file mode 100644 index 0000000000..69280ceeaa --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerConstraint.php @@ -0,0 +1,59 @@ + The instance properties */ + protected $properties = array( + 'field', + 'constraints', + 'notnull_strength', + 'unique_strength', + 'exp_strength', + ); + + /** @var array The not null properties */ + protected $mandatoryProperties = array( + 'field', + 'constraints', + 'notnull_strength', + 'unique_strength', + 'exp_strength', + ); + + /** @var string The XML element local name */ + protected static $qgisLocalName = 'constraint'; + + protected static function getAttributes($oXmlReader) + { + return array( + 'field' => $oXmlReader->getAttribute('field'), + 'constraints' => (int) $oXmlReader->getAttribute('constraints'), + 'notnull_strength' => (int) $oXmlReader->getAttribute('notnull_strength') > 0, + 'unique_strength' => (int) $oXmlReader->getAttribute('unique_strength') > 0, + 'exp_strength' => (int) $oXmlReader->getAttribute('exp_strength') > 0, + ); + } +} diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerConstraintExpression.php b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerConstraintExpression.php new file mode 100644 index 0000000000..f3e7a86821 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerConstraintExpression.php @@ -0,0 +1,51 @@ + The instance properties */ + protected $properties = array( + 'field', + 'exp', + 'desc', + ); + + /** @var array The not null properties */ + protected $mandatoryProperties = array( + 'field', + 'exp', + 'desc', + ); + + /** @var string The XML element local name */ + protected static $qgisLocalName = 'constraint'; + + protected static function getAttributes($oXmlReader) + { + return array( + 'field' => $oXmlReader->getAttribute('field'), + 'exp' => $oXmlReader->getAttribute('exp'), + 'desc' => $oXmlReader->getAttribute('desc'), + ); + } +} diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerDefault.php b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerDefault.php new file mode 100644 index 0000000000..9d12d01d88 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerDefault.php @@ -0,0 +1,51 @@ + The instance properties */ + protected $properties = array( + 'field', + 'expression', + 'applyOnUpdate', + ); + + /** @var array The not null properties */ + protected $mandatoryProperties = array( + 'field', + 'expression', + 'applyOnUpdate', + ); + + /** @var string The XML element local name */ + protected static $qgisLocalName = 'default'; + + protected static function getAttributes($oXmlReader) + { + return array( + 'field' => $oXmlReader->getAttribute('field'), + 'expression' => $oXmlReader->getAttribute('expression'), + 'applyOnUpdate' => filter_var($oXmlReader->getAttribute('applyOnUpdate'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE), + ); + } +} diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerEditableField.php b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerEditableField.php new file mode 100644 index 0000000000..25052aeddb --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerEditableField.php @@ -0,0 +1,47 @@ + The instance properties */ + protected $properties = array( + 'name', + 'editable', + ); + + /** @var array The not null properties */ + protected $mandatoryProperties = array( + 'name', + 'editable', + ); + + /** @var string The XML element local name */ + protected static $qgisLocalName = 'field'; + + protected static function getAttributes($oXmlReader) + { + return array( + 'name' => $oXmlReader->getAttribute('name'), + 'editable' => filter_var($oXmlReader->getAttribute('editable'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE), + ); + } +} diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerField.php b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerField.php new file mode 100644 index 0000000000..f28ed13c59 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerField.php @@ -0,0 +1,70 @@ + The instance properties */ + protected $properties = array( + 'name', + 'configurationFlags', + ); + + /** @var array The not null properties */ + protected $mandatoryProperties = array( + 'name', + ); + + /** + * @return bool + */ + public function isHideFromWms() + { + if (!isset($this->configurationFlags)) { + return false; + } + + return strpos($this->configurationFlags, 'HideFromWms') !== false; + } + + /** + * @return bool + */ + public function isHideFromWfs() + { + if (!isset($this->configurationFlags)) { + return false; + } + + return strpos($this->configurationFlags, 'HideFromWfs') !== false; + } + + /** @var string The XML element local name */ + protected static $qgisLocalName = 'field'; + + protected static function getAttributes($oXmlReader) + { + return array( + 'name' => $oXmlReader->getAttribute('name'), + 'configurationFlags' => $oXmlReader->getAttribute('configurationFlags'), + ); + } +} diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerJoin.php b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerJoin.php new file mode 100644 index 0000000000..192b6715ba --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerJoin.php @@ -0,0 +1,51 @@ + The instance properties */ + protected $properties = array( + 'joinLayerId', + 'joinFieldName', + 'targetFieldName', + ); + + /** @var array The not null properties */ + protected $mandatoryProperties = array( + 'joinLayerId', + 'joinFieldName', + 'targetFieldName', + ); + + /** @var string The XML element local name */ + protected static $qgisLocalName = 'join'; + + protected static function getAttributes($oXmlReader) + { + return array( + 'joinLayerId' => $oXmlReader->getAttribute('joinLayerId'), + 'joinFieldName' => $oXmlReader->getAttribute('joinFieldName'), + 'targetFieldName' => $oXmlReader->getAttribute('targetFieldName'), + ); + } +} diff --git a/tests/units/classes/Project/Qgis/AttributeTableConfigTest.php b/tests/units/classes/Project/Qgis/AttributeTableConfigTest.php new file mode 100644 index 0000000000..7fc8cdd2f5 --- /dev/null +++ b/tests/units/classes/Project/Qgis/AttributeTableConfigTest.php @@ -0,0 +1,184 @@ + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $config = Qgis\Layer\AttributeTableConfig::fromXmlReader($oXml); + + $this->assertEquals('"field_communes"', $config->sortExpression); + $this->assertEquals(1, $config->sortOrder); + $this->assertCount(15, $config->columns); + + $columns = array( + array( + 'type' => 'field', + 'name' => 'nid', + 'hidden' => false, + ), + array( + 'type' => 'field', + 'name' => 'titre', + 'hidden' => false, + ), + array( + 'type' => 'field', + 'name' => 'vignette_src', + 'hidden' => true, + ), + array( + 'type' => 'field', + 'name' => 'vignette_alt', + 'hidden' => true, + ), + array( + 'type' => 'field', + 'name' => 'field_date', + 'hidden' => false, + ), + array( + 'type' => 'field', + 'name' => 'description', + 'hidden' => true, + ), + array( + 'type' => 'field', + 'name' => 'field_communes', + 'hidden' => false, + ), + array( + 'type' => 'field', + 'name' => 'field_lieu', + 'hidden' => false, + ), + array( + 'type' => 'field', + 'name' => 'field_access', + 'hidden' => false, + ), + array( + 'type' => 'field', + 'name' => 'field_thematique', + 'hidden' => false, + ), + array( + 'type' => 'field', + 'name' => 'x', + 'hidden' => true, + ), + array( + 'type' => 'field', + 'name' => 'y', + 'hidden' => true, + ), + array( + 'type' => 'field', + 'name' => 'url', + 'hidden' => false, + ), + array( + 'type' => 'actions', + 'name' => null, + 'hidden' => true, + ), + array( + 'type' => 'field', + 'name' => 'fid', + 'hidden' => false, + ), + ); + foreach($columns as $idx => $data) { + foreach($data as $prop => $value) { + $this->assertEquals($value, $config->columns[$idx]->$prop, $idx.' '.$prop); + } + } + + $configKeyArray = $config->toKeyArray(); + $this->assertTrue(is_array($configKeyArray)); + $this->assertArrayHasKey('columns', $configKeyArray); + $this->assertCount(9, $configKeyArray['columns']); + $columns = array( + array( + 'index' => 0, + 'type' => 'field', + 'name' => 'nid', + ), + array( + 'index' => 1, + 'type' => 'field', + 'name' => 'titre', + ), + array( + 'index' => 4, + 'type' => 'field', + 'name' => 'field_date', + ), + array( + 'index' => 6, + 'type' => 'field', + 'name' => 'field_communes', + ), + array( + 'index' => 7, + 'type' => 'field', + 'name' => 'field_lieu', + ), + array( + 'index' => 8, + 'type' => 'field', + 'name' => 'field_access', + ), + array( + 'index' => 9, + 'type' => 'field', + 'name' => 'field_thematique', + ), + array( + 'index' => 12, + 'type' => 'field', + 'name' => 'url', + ), + array( + 'index' => 14, + 'type' => 'field', + 'name' => 'fid', + ), + ); + foreach($columns as $idx => $data) { + foreach($data as $prop => $value) { + $this->assertEquals($value, $configKeyArray['columns'][$idx][$prop], $idx.' '.$prop); + } + } + } +} diff --git a/tests/units/classes/Project/Qgis/MapLayerStyleManagerTest.php b/tests/units/classes/Project/Qgis/MapLayerStyleManagerTest.php new file mode 100644 index 0000000000..4b0e96cf25 --- /dev/null +++ b/tests/units/classes/Project/Qgis/MapLayerStyleManagerTest.php @@ -0,0 +1,478 @@ + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $manager = Qgis\Layer\MapLayerStyleManager::fromXmlReader($oXml); + + $data = array( + 'current' => 'default', + 'styles' => array('default'), + ); + foreach($data as $prop => $value) { + $this->assertEquals($value, $manager->$prop, $prop); + } + + + $xmlStr = ' + + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $manager = Qgis\Layer\MapLayerStyleManager::fromXmlReader($oXml); + + $data = array( + 'current' => 'black', + 'styles' => array( + 'black', + 'colored', + 'other', + ), + ); + foreach($data as $prop => $value) { + $this->assertEquals($value, $manager->$prop, $prop); + } + + $xmlStr = ' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 0 + name + + + + + + + + + + + . + + + + + + + + + + + osm_id + OGC_FID + wkt + from + to + colour + name + ref + + + OGC_FID + wkt + from + html + to + colour + + + . + + 0 + . + # -*- coding: utf-8 -*- + """ + Les formulaires QGIS peuvent avoir une fonction Python qui sera appelée à l\'ouverture du formulaire. + + Utilisez cette fonction pour ajouter plus de fonctionnalités à vos formulaires. + + Entrez le nom de la fonction dans le champ + "Fonction d\'initialisation Python" + Voici un exemple à suivre: + """ + from PyQt4.QtGui import QWidget + + def my_form_open(dialog, layer, feature): + ⇥geom = feature.geometry() + ⇥control = dialog.findChild(QWidget, "MyLineEdit") + + 0 + generatedlayout + + + + + + + + + + 1 + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $manager = Qgis\Layer\MapLayerStyleManager::fromXmlReader($oXml); + + $data = array( + 'current' => 'black', + 'styles' => array( + 'black', + 'colored', + ), + ); + foreach($data as $prop => $value) { + $this->assertEquals($value, $manager->$prop, $prop); + } + } +} diff --git a/tests/units/classes/Project/Qgis/MapLayerTest.php b/tests/units/classes/Project/Qgis/MapLayerTest.php new file mode 100644 index 0000000000..e79aac84d7 --- /dev/null +++ b/tests/units/classes/Project/Qgis/MapLayerTest.php @@ -0,0 +1,1024 @@ + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $layer = Qgis\Layer\MapLayer::fromXmlReader($oXml); + $this->assertTrue($layer->embedded); + $this->assertInstanceOf(Qgis\Layer\EmbeddedLayer::class, $layer); + + $data = array( + 'id' => 'child_layer_8dec6d75_eeed_494b_b97f_5f2c7e16fd00', + 'embedded' => true, + 'type' => 'embedded', + 'project' => './relations_project.qgs', + ); + foreach($data as $prop => $value) { + $this->assertEquals($value, $layer->$prop, $prop); + } + + $keyData = $layer->toKeyArray(); + $this->assertTrue(is_array($keyData)); + foreach($data as $prop => $value) { + $this->assertEquals($value, $keyData[$prop], $prop); + } + + // MapLayer + $xmlStr = ' + + + -20037508.34278924390673637 + -20037508.34278925508260727 + 20037508.34278924390673637 + 20037508.34278924390673637 + + osm_mapnik20180315181738526 + crs=EPSG:3857&format=&type=xyz&url=http://tile.openstreetmap.org/%7Bz%7D/%7Bx%7D/%7By%7D.png + + + + osm-mapnik + + + PROJCS["WGS 84 / Pseudo-Mercator",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Mercator_1SP"],PARAMETER["central_meridian",0],PARAMETER["scale_factor",1],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["X",EAST],AXIS["Y",NORTH],EXTENSION["PROJ4","+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs"],AUTHORITY["EPSG","3857"]] + +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs + 3857 + 3857 + EPSG:3857 + WGS 84 / Pseudo-Mercator + merc + WGS84 + false + + + + + + + + + + + + + + + + + 0 + 0 + + + + + true + + + + + wms + + + + + + + + 1 + 1 + 1 + + + + + + + + + None + WholeRaster + Estimated + 0.02 + 0.98 + 2 + + + + + + + 0 + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $layer = Qgis\Layer\MapLayer::fromXmlReader($oXml); + $this->assertFalse($layer->embedded); + $this->assertInstanceOf(Qgis\Layer\MapLayer::class, $layer); + + $data = array( + 'id' => 'osm_mapnik20180315181738526', + 'embedded' => false, + 'type' => 'raster', + 'layername' => 'osm-mapnik', + //'srs', + 'datasource' => 'crs=EPSG:3857&format=&type=xyz&url=http://tile.openstreetmap.org/%7Bz%7D/%7Bx%7D/%7By%7D.png', + 'provider' => 'wms', + 'shortname' => null, + 'title' => null, + 'abstract' => null, + 'keywordList' => array(''), + ); + foreach($data as $prop => $value) { + $this->assertEquals($value, $layer->$prop, $prop); + } + + $this->assertNotNull($layer->srs); + $data = array( + 'proj4' => '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs', + 'srid' => 3857, + 'authid' => 'EPSG:3857', + 'description' => 'WGS 84 / Pseudo-Mercator', + ); + foreach($data as $prop => $value) { + $this->assertEquals($value, $layer->srs->$prop, $prop); + } + + $this->assertNotNull($layer->styleManager); + $data = array( + 'current' => 'default', + 'styles' => array('default'), + ); + foreach($data as $prop => $value) { + $this->assertEquals($value, $layer->styleManager->$prop, $prop); + } + + $keyData = $layer->toKeyArray(); + $this->assertTrue(is_array($keyData)); + $data = array( + 'type' => 'raster', + 'id' => 'osm_mapnik20180315181738526', + 'name' => 'osm-mapnik', + 'shortname' => '', + 'title' => 'osm-mapnik', + 'abstract' => '', + 'proj4' => '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs', + 'srid' => 3857, + 'authid' => 'EPSG:3857', + 'datasource' => 'crs=EPSG:3857&format=&type=xyz&url=http://tile.openstreetmap.org/%7Bz%7D/%7Bx%7D/%7By%7D.png', + 'provider' => 'wms', + 'keywords' => array(''), + ); + foreach($data as $prop => $value) { + $this->assertEquals($value, $keyData[$prop], $prop); + } + + // VectorLayer + $xmlStr = ' + + + 3.8097468000000001 + 43.55760910000000052 + 3.96392439999999979 + 43.65461210000000136 + + tramway20150328114206278 + dbname=\'./edition/transport.sqlite\' table="tramway" (geometry) sql= + Tram lines + + + + tramway + + + GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]] + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat + WGS84 + true + + + + + + + + + + + + + + + + + 0 + 0 + + + + + true + + + + + spatialite + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 0 + name + + + + + + + + + + + . + + + + + + + + + + + osm_id + OGC_FID + wkt + from + to + colour + name + ref + + + OGC_FID + wkt + from + html + to + colour + + + . + + 0 + . + # -*- coding: utf-8 -*- + """ + Les formulaires QGIS peuvent avoir une fonction Python qui sera appelée à l\'ouverture du formulaire. + + Utilisez cette fonction pour ajouter plus de fonctionnalités à vos formulaires. + + Entrez le nom de la fonction dans le champ + "Fonction d\'initialisation Python" + Voici un exemple à suivre: + """ + from PyQt4.QtGui import QWidget + + def my_form_open(dialog, layer, feature): + ⇥geom = feature.geometry() + ⇥control = dialog.findChild(QWidget, "MyLineEdit") + + 0 + generatedlayout + + + + + + + + + + 1 + + + + + + 1 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + wkt + from + OGC_FID + colour + osm_id + to + ref + name + + + wkt + html + OGC_FID + colour + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + . + + 0 + . + + 0 + generatedlayout + + + + + + + + COALESCE("name", \'<NULL>\') + + + '; + + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $layer = Qgis\Layer\MapLayer::fromXmlReader($oXml); + $this->assertFalse($layer->embedded); + $this->assertInstanceOf(Qgis\Layer\VectorLayer::class, $layer); + + $data = array( + 'id' => 'tramway20150328114206278', + 'embedded' => false, + 'type' => 'vector', + 'layername' => 'tramway', + //'srs', + 'datasource' => 'dbname=\'./edition/transport.sqlite\' table="tramway" (geometry) sql=', + 'provider' => 'spatialite', + 'shortname' => null, + 'title' => 'Tram lines', + 'abstract' => null, + 'keywordList' => array(''), + 'previewExpression' => 'COALESCE("name", \'\')', + 'layerOpacity' => 1, + ); + foreach($data as $prop => $value) { + $this->assertEquals($value, $layer->$prop, $prop); + } + + $this->assertNotNull($layer->srs); + $data = array( + 'proj4' => '+proj=longlat +datum=WGS84 +no_defs', + 'srid' => 4326, + 'authid' => 'EPSG:4326', + 'description' => 'WGS 84', + ); + foreach($data as $prop => $value) { + $this->assertEquals($value, $layer->srs->$prop, $prop); + } + + $this->assertNotNull($layer->styleManager); + $data = array( + 'current' => 'black', + 'styles' => array('black', 'colored'), + ); + foreach($data as $prop => $value) { + $this->assertEquals($value, $layer->styleManager->$prop, $prop); + } + + $this->assertNotNull($layer->fieldConfiguration); + $this->assertCount(9, $layer->fieldConfiguration); + + $this->assertNotNull($layer->aliases); + $this->assertCount(9, $layer->aliases); + + $this->assertNotNull($layer->defaults); + $this->assertCount(9, $layer->defaults); + + $this->assertNotNull($layer->constraints); + $this->assertCount(9, $layer->constraints); + + $this->assertNotNull($layer->constraintExpressions); + $this->assertCount(9, $layer->constraintExpressions); + + $this->assertNotNull($layer->excludeAttributesWFS); + $this->assertCount(4, $layer->excludeAttributesWFS); + + $this->assertNotNull($layer->excludeAttributesWMS); + $this->assertCount(8, $layer->excludeAttributesWMS); + + $this->assertNotNull($layer->attributetableconfig); + $this->assertCount(0, $layer->attributetableconfig->columns); + + $this->assertNotNull($layer->vectorjoins); + $this->assertCount(0, $layer->vectorjoins); + + $this->assertNotNull($layer->editable); + $this->assertCount(0, $layer->editable); + + $this->assertNotNull($layer->rendererV2); + $this->assertEquals($layer->rendererV2->type, 'singleSymbol'); + + $keyData = $layer->toKeyArray(); + $this->assertTrue(is_array($keyData)); + $data = array( + 'type' => 'vector', + 'id' => 'tramway20150328114206278', + 'name' => 'tramway', + 'shortname' => '', + 'title' => 'Tram lines', + 'abstract' => '', + 'proj4' => '+proj=longlat +datum=WGS84 +no_defs', + 'srid' => 4326, + 'authid' => 'EPSG:4326', + 'datasource' => 'dbname=\'./edition/transport.sqlite\' table="tramway" (geometry) sql=', + 'provider' => 'spatialite', + 'keywords' => array(''), + ); + foreach($data as $prop => $value) { + $this->assertEquals($value, $keyData[$prop], $prop); + } + + $formControls = $layer->getFormControls(); + $this->assertNotNull($formControls); + $this->assertCount(9, $formControls); + } +} diff --git a/tests/units/classes/Project/Qgis/RendererV2Test.php b/tests/units/classes/Project/Qgis/RendererV2Test.php new file mode 100644 index 0000000000..ab16dc3177 --- /dev/null +++ b/tests/units/classes/Project/Qgis/RendererV2Test.php @@ -0,0 +1,191 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $renderer = Qgis\Layer\RendererV2::fromXmlReader($oXml); + + $this->assertEquals($renderer->type, 'singleSymbol'); + $this->assertCount(0, $renderer->categories); + + + // categorizedSymbol + $xmlStr = ' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $renderer = Qgis\Layer\RendererV2::fromXmlReader($oXml); + + $this->assertEquals($renderer->type, 'categorizedSymbol'); + $this->assertCount(4, $renderer->categories); + + $data = array( + '1' => '1', + '2' => '2', + '3' => '3', + '4' => '4', + ); + foreach($data as $prop => $value) { + $this->assertEquals($value, $renderer->categories[$prop], $value); + } + } +} diff --git a/tests/units/classes/Project/Qgis/VectorLayerConstraintExpressionTest.php b/tests/units/classes/Project/Qgis/VectorLayerConstraintExpressionTest.php new file mode 100644 index 0000000000..74af9789b0 --- /dev/null +++ b/tests/units/classes/Project/Qgis/VectorLayerConstraintExpressionTest.php @@ -0,0 +1,36 @@ +'; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $constraint = Qgis\Layer\VectorLayerConstraintExpression::fromXmlReader($oXml); + + $this->assertInstanceOf(Qgis\Layer\VectorLayerConstraintExpression::class, $constraint); + $this->assertEquals('id', $constraint->field); + $this->assertEquals('', $constraint->exp); + $this->assertEquals('', $constraint->desc); + } + + public function testExpression() + { + $xmlStr =''; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $constraint = Qgis\Layer\VectorLayerConstraintExpression::fromXmlReader($oXml); + + $this->assertInstanceOf(Qgis\Layer\VectorLayerConstraintExpression::class, $constraint); + $this->assertEquals('website', $constraint->field); + $this->assertEquals('left( "website", 4) = \'http\'', $constraint->exp); + $this->assertEquals('Web site URL must start with \'http\'', $constraint->desc); + } +} diff --git a/tests/units/classes/Project/Qgis/VectorLayerConstraintTest.php b/tests/units/classes/Project/Qgis/VectorLayerConstraintTest.php new file mode 100644 index 0000000000..fa3fa6553e --- /dev/null +++ b/tests/units/classes/Project/Qgis/VectorLayerConstraintTest.php @@ -0,0 +1,54 @@ +'; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $constraint = Qgis\Layer\VectorLayerConstraint::fromXmlReader($oXml); + + $this->assertInstanceOf(Qgis\Layer\VectorLayerConstraint::class, $constraint); + $this->assertEquals('name', $constraint->field); + $this->assertEquals(0, $constraint->constraints); + $this->assertFalse($constraint->notnull_strength); + $this->assertFalse($constraint->unique_strength); + $this->assertFalse($constraint->exp_strength); + } + + public function testNotNull() + { + $xmlStr = ''; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $constraint = Qgis\Layer\VectorLayerConstraint::fromXmlReader($oXml); + + $this->assertInstanceOf(Qgis\Layer\VectorLayerConstraint::class, $constraint); + $this->assertEquals('pkuid', $constraint->field); + $this->assertEquals(3, $constraint->constraints); + $this->assertTrue($constraint->notnull_strength); + $this->assertTrue($constraint->unique_strength); + $this->assertFalse($constraint->exp_strength); + } + + public function testEnforceNotNull() + { + $xmlStr = ''; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $constraint = Qgis\Layer\VectorLayerConstraint::fromXmlReader($oXml); + + $this->assertInstanceOf(Qgis\Layer\VectorLayerConstraint::class, $constraint); + $this->assertEquals('test_not_null_only', $constraint->field); + $this->assertEquals(1, $constraint->constraints); + $this->assertTrue($constraint->notnull_strength); + $this->assertFalse($constraint->unique_strength); + $this->assertFalse($constraint->exp_strength); + } +} From e400ab2483d0baf8a57c61d6ea030ce130ad4dbd Mon Sep 17 00:00:00 2001 From: rldhont Date: Thu, 30 Nov 2023 12:21:44 +0100 Subject: [PATCH 14/46] new Lizmap\Project\Qgis\ProjectVisibilityPreset and ProjectVisibilityPresetLayer classes --- .../Project/Qgis/ProjectVisibilityPreset.php | 132 ++++++++++++++++++ .../Qgis/ProjectVisibilityPresetLayer.php | 40 ++++++ .../Qgis/ProjectVisibilityPresetTest.php | 98 +++++++++++++ 3 files changed, 270 insertions(+) create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/ProjectVisibilityPreset.php create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/ProjectVisibilityPresetLayer.php create mode 100644 tests/units/classes/Project/Qgis/ProjectVisibilityPresetTest.php diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/ProjectVisibilityPreset.php b/lizmap/modules/lizmap/lib/Project/Qgis/ProjectVisibilityPreset.php new file mode 100644 index 0000000000..d5274f6b29 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/ProjectVisibilityPreset.php @@ -0,0 +1,132 @@ + $layers + * @property array $checkedGroupNodes + * @property array $expandedGroupNodes + */ +class ProjectVisibilityPreset extends BaseQgisObject +{ + /** @var array The instance properties */ + protected $properties = array( + 'name', + 'layers', + 'checkedGroupNodes', + 'expandedGroupNodes', + ); + + /** @var array The not null properties */ + protected $mandatoryProperties = array( + 'name', + 'layers', + ); + + protected function set(array $data): void + { + if (!array_key_exists('checkedGroupNodes', $data)) { + $data['checkedGroupNodes'] = array(); + } + if (!array_key_exists('expandedGroupNodes', $data)) { + $data['expandedGroupNodes'] = array(); + } + parent::set($data); + } + + /** + * Get visibility preset as key array. + * + * @return array + */ + public function toKeyArray() + { + $data = array( + 'layers' => array(), + 'checkedGroupNode' => $this->checkedGroupNodes, + 'expandedGroupNode' => $this->expandedGroupNodes, + ); + foreach ($this->layers as $layer) { + // Since QGIS 3.26, theme contains every layers with visible attributes + // before only visible layers are in theme + // So do not keep layer not visible + if (!$layer->visible) { + continue; + } + $data['layers'][$layer->id] = array( + 'style' => $layer->style, + 'expanded' => $layer->expanded, + ); + } + + return $data; + } + + /** @var string The XML element local name */ + protected static $qgisLocalName = 'visibility-preset'; + + public static function fromXmlReader($oXmlReader) + { + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + $localName = static::$qgisLocalName; + if ($oXmlReader->localName != $localName) { + throw new \Exception('Provide a `'.$localName.'` element not `'.$oXmlReader->localName.'`!'); + } + + $depth = $oXmlReader->depth; + $data = array( + 'name' => $oXmlReader->getAttribute('name'), + 'layers' => array(), + 'checkedGroupNodes' => array(), + 'expandedGroupNodes' => array(), + ); + while ($oXmlReader->read()) { + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth > $depth + 2) { + continue; + } + + $tagName = $oXmlReader->localName; + if ($tagName == 'layer') { + $data['layers'][] = new ProjectVisibilityPresetLayer( + array( + 'id' => $oXmlReader->getAttribute('id'), + 'visible' => filter_var($oXmlReader->getAttribute('visible'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE), + 'style' => $oXmlReader->getAttribute('style'), + 'expanded' => filter_var($oXmlReader->getAttribute('expanded'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE), + ), + ); + } elseif ($tagName == 'checked-group-node') { + $data['checkedGroupNodes'][] = $oXmlReader->getAttribute('id'); + } elseif ($tagName == 'expanded-group-node') { + $data['expandedGroupNodes'][] = $oXmlReader->getAttribute('id'); + } + } + + return new ProjectVisibilityPreset($data); + } +} diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/ProjectVisibilityPresetLayer.php b/lizmap/modules/lizmap/lib/Project/Qgis/ProjectVisibilityPresetLayer.php new file mode 100644 index 0000000000..ce758a39b4 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/ProjectVisibilityPresetLayer.php @@ -0,0 +1,40 @@ + The instance properties */ + protected $properties = array( + 'id', + 'visible', + 'style', + 'expanded', + ); + + /** @var array The not null properties */ + protected $mandatoryProperties = array( + 'id', + 'visible', + 'style', + 'expanded', + ); +} diff --git a/tests/units/classes/Project/Qgis/ProjectVisibilityPresetTest.php b/tests/units/classes/Project/Qgis/ProjectVisibilityPresetTest.php new file mode 100644 index 0000000000..eb9dd426fc --- /dev/null +++ b/tests/units/classes/Project/Qgis/ProjectVisibilityPresetTest.php @@ -0,0 +1,98 @@ + + + + + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $visibilityPreset = Qgis\ProjectVisibilityPreset::fromXmlReader($oXml); + + $this->assertEquals('theme1', $visibilityPreset->name); + $this->assertCount(0, $visibilityPreset->checkedGroupNodes); + $this->assertCount(1, $visibilityPreset->expandedGroupNodes); + $this->assertCount(1, $visibilityPreset->layers); + $this->assertInstanceOf(Qgis\ProjectVisibilityPresetLayer::class, $visibilityPreset->layers[0]); + $data = array( + 'id' => 'quartiers_ef5b13e3_36db_4e0d_98b3_990de580367d', + 'visible' => True, + 'style' => 'style1', + 'expanded' => True, + ); + foreach($data as $prop => $value) { + $this->assertEquals($value, $visibilityPreset->layers[0]->$prop, $prop); + } + + $dataArray = $visibilityPreset->toKeyArray(); + $this->assertCount(1, $dataArray['layers']); + $this->assertArrayHasKey('quartiers_ef5b13e3_36db_4e0d_98b3_990de580367d', $dataArray['layers']); + $this->assertEquals('style1', $dataArray['layers']['quartiers_ef5b13e3_36db_4e0d_98b3_990de580367d']['style']); + $this->assertTrue($dataArray['layers']['quartiers_ef5b13e3_36db_4e0d_98b3_990de580367d']['expanded']); + $this->assertCount(0, $dataArray['checkedGroupNode']); + $this->assertCount(1, $dataArray['expandedGroupNode']); + + $xmlStr = ' + + + + + + + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $visibilityPreset = Qgis\ProjectVisibilityPreset::fromXmlReader($oXml); + + $this->assertEquals('theme1', $visibilityPreset->name); + $this->assertCount(0, $visibilityPreset->checkedGroupNodes); + $this->assertCount(1, $visibilityPreset->expandedGroupNodes); + $this->assertCount(2, $visibilityPreset->layers); + $this->assertInstanceOf(Qgis\ProjectVisibilityPresetLayer::class, $visibilityPreset->layers[0]); + $data = array( + 'id' => 'sousquartiers_7c49d0fc_0ee0_4308_a66d_45c144e59872', + 'visible' => False, + 'style' => 'défaut', + 'expanded' => True, + ); + foreach($data as $prop => $value) { + $this->assertEquals($value, $visibilityPreset->layers[0]->$prop, $prop); + } + $data = array( + 'id' => 'quartiers_ef5b13e3_36db_4e0d_98b3_990de580367d', + 'visible' => True, + 'style' => 'style1', + 'expanded' => True, + ); + foreach($data as $prop => $value) { + $this->assertEquals($value, $visibilityPreset->layers[1]->$prop, $prop); + } + + $dataArray = $visibilityPreset->toKeyArray(); + $this->assertCount(1, $dataArray['layers']); + $this->assertArrayHasKey('quartiers_ef5b13e3_36db_4e0d_98b3_990de580367d', $dataArray['layers']); + $this->assertEquals('style1', $dataArray['layers']['quartiers_ef5b13e3_36db_4e0d_98b3_990de580367d']['style']); + $this->assertTrue($dataArray['layers']['quartiers_ef5b13e3_36db_4e0d_98b3_990de580367d']['expanded']); + $this->assertCount(0, $dataArray['checkedGroupNode']); + $this->assertCount(1, $dataArray['expandedGroupNode']); + } +} From 03038a0905a9e962fedac060793263ec5acfcbfa Mon Sep 17 00:00:00 2001 From: rldhont Date: Fri, 22 Mar 2024 13:37:10 +0100 Subject: [PATCH 15/46] New Lizmap\Project\Qgis\ProjectRelation for QGIS Project Relation --- .../lib/Project/Qgis/ProjectRelation.php | 101 ++++++++++++++++++ .../Project/Qgis/ProjectRelationTest.php | 31 ++++++ 2 files changed, 132 insertions(+) create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/ProjectRelation.php create mode 100644 tests/units/classes/Project/Qgis/ProjectRelationTest.php diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/ProjectRelation.php b/lizmap/modules/lizmap/lib/Project/Qgis/ProjectRelation.php new file mode 100644 index 0000000000..67656809a1 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/ProjectRelation.php @@ -0,0 +1,101 @@ + The instance properties */ + protected $properties = array( + 'id', + 'name', + 'referencingLayer', + 'referencingField', + 'referencedLayer', + 'referencedField', + 'strength', + ); + + /** @var array The not null properties */ + protected $mandatoryProperties = array( + 'id', + 'name', + 'referencingLayer', + 'referencingField', + 'referencedLayer', + 'referencedField', + 'strength', + ); + + /** @var string The XML element local name */ + protected static $qgisLocalName = 'relation'; + + public static function fromXmlReader($oXmlReader) + { + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + throw new \Exception('Provide an XMLReader::ELEMENT!'); + } + $localName = static::$qgisLocalName; + if ($oXmlReader->localName != $localName) { + throw new \Exception('Provide a `'.$localName.'` element not `'.$oXmlReader->localName.'`!'); + } + + $depth = $oXmlReader->depth; + $data = array( + 'id' => $oXmlReader->getAttribute('id'), + 'name' => $oXmlReader->getAttribute('name'), + 'referencingLayer' => $oXmlReader->getAttribute('referencingLayer'), + 'referencedLayer' => $oXmlReader->getAttribute('referencedLayer'), + 'strength' => $oXmlReader->getAttribute('strength'), + ); + + while ($oXmlReader->read()) { + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + $tagName = $oXmlReader->localName; + if ($tagName == 'fieldRef') { + $data['referencingField'] = $oXmlReader->getAttribute('referencingField'); + $data['referencedField'] = $oXmlReader->getAttribute('referencedField'); + } + } + + return new ProjectRelation($data); + } +} +/* + + + + */ diff --git a/tests/units/classes/Project/Qgis/ProjectRelationTest.php b/tests/units/classes/Project/Qgis/ProjectRelationTest.php new file mode 100644 index 0000000000..5b1f01c098 --- /dev/null +++ b/tests/units/classes/Project/Qgis/ProjectRelationTest.php @@ -0,0 +1,31 @@ + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $relation = Qgis\ProjectRelation::fromXmlReader($oXml); + + $this->assertEquals('SousQuartiers20160121124316563_QUARTMNO_VilleMTP_MTP_Quartiers_2011_432620130116112610876_QUARTMNO', $relation->id); + $this->assertEquals('Subdistricts by district', $relation->name); + $this->assertEquals('SousQuartiers20160121124316563', $relation->referencingLayer); + $this->assertEquals('QUARTMNO', $relation->referencingField); + $this->assertEquals('VilleMTP_MTP_Quartiers_2011_432620130116112610876', $relation->referencedLayer); + $this->assertEquals('QUARTMNO', $relation->referencedField); + $this->assertEquals('Association', $relation->strength); + } +} From 0359e6bb13a5960651cd744459627393df44e3f3 Mon Sep 17 00:00:00 2001 From: rldhont Date: Mon, 4 Dec 2023 16:54:20 +0100 Subject: [PATCH 16/46] New Lizmap\Project\Qgis\Layer\EditWidget\* classes and Project\Qgis\Layer\VectorLayerEditWidget class --- .../Qgis/Layer/EditWidget/CheckBoxConfig.php | 47 +++ .../Qgis/Layer/EditWidget/DateTimeConfig.php | 70 ++++ .../EditWidget/ExternalResourceConfig.php | 149 +++++++++ .../Qgis/Layer/EditWidget/RangeConfig.php | 76 +++++ .../EditWidget/RelationReferenceConfig.php | 110 +++++++ .../Qgis/Layer/EditWidget/TextEditConfig.php | 72 +++++ .../Layer/EditWidget/UniqueValuesConfig.php | 48 +++ .../Qgis/Layer/EditWidget/ValueMapConfig.php | 39 +++ .../Layer/EditWidget/ValueRelationConfig.php | 83 +++++ .../Qgis/Layer/VectorLayerEditWidget.php | 121 +++++++ .../Qgis/VectorLayerEditWidgetTest.php | 302 ++++++++++++++++++ 11 files changed, 1117 insertions(+) create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/CheckBoxConfig.php create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/DateTimeConfig.php create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/ExternalResourceConfig.php create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/RangeConfig.php create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/RelationReferenceConfig.php create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/TextEditConfig.php create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/UniqueValuesConfig.php create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/ValueMapConfig.php create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/ValueRelationConfig.php create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerEditWidget.php create mode 100644 tests/units/classes/Project/Qgis/VectorLayerEditWidgetTest.php diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/CheckBoxConfig.php b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/CheckBoxConfig.php new file mode 100644 index 0000000000..75c2315d8c --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/CheckBoxConfig.php @@ -0,0 +1,47 @@ + + * + * + * + * . + * + * @property string $CheckedState + * @property string $UncheckedState + * @property int $TextDisplayMethod + */ +class CheckBoxConfig extends Qgis\BaseQgisObject +{ + /** @var array The instance properties */ + protected $properties = array( + 'CheckedState', + 'UncheckedState', + 'TextDisplayMethod', + ); + + /** @var array The default values */ + protected $defaultValues = array( + 'CheckedState' => '', + 'UncheckedState' => '', + 'TextDisplayMethod' => 0, + ); +} diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/DateTimeConfig.php b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/DateTimeConfig.php new file mode 100644 index 0000000000..bf7f189ba7 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/DateTimeConfig.php @@ -0,0 +1,70 @@ + + * + * + * + * . + * + * @property bool $allow_null + * @property bool $calendar_popup + * @property string $display_format + * @property string $field_format + * @property bool $field_iso_format + */ +class DateTimeConfig extends Qgis\BaseQgisObject +{ + /** @var array The instance properties */ + protected $properties = array( + 'allow_null', + 'calendar_popup', + 'display_format', + 'field_format', + 'field_iso_format', + ); + + /** @var array The default values */ + protected $defaultValues = array( + 'allow_null' => false, + 'calendar_popup' => false, + 'display_format' => '', + 'field_format' => 'yyyy-MM-dd', + 'field_iso_format' => false, + ); + + protected function set(array $data): void + { + if (array_key_exists('allow_null', $data)) { + $data['allow_null'] = filter_var($data['allow_null'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + } + if (array_key_exists('calendar_popup', $data)) { + $data['calendar_popup'] = filter_var($data['calendar_popup'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + } + if (array_key_exists('field_iso_format', $data)) { + $data['field_iso_format'] = filter_var($data['field_iso_format'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + } + parent::set($data); + } +} diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/ExternalResourceConfig.php b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/ExternalResourceConfig.php new file mode 100644 index 0000000000..d9a6b0eff3 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/ExternalResourceConfig.php @@ -0,0 +1,149 @@ + + * + * + * + * . + * + * @property int $DocumentViewer + * @property int $DocumentViewerHeight + * @property int $DocumentViewerWidth + * @property bool $FileWidget + * @property bool $FileWidgetButton + * @property string $FileWidgetFilter + * @property bool $UseLink + * @property bool $FullUrl + * @property array $PropertyCollection + * @property int $RelativeStorage + * @property string $StorageAuthConfigId + * @property int $StorageMode + * @property string $StorageType + */ +class ExternalResourceConfig extends Qgis\BaseQgisObject +{ + /** @var array The instance properties */ + protected $properties = array( + 'DocumentViewer', + 'DocumentViewerHeight', + 'DocumentViewerWidth', + 'FileWidget', + 'FileWidgetButton', + 'FileWidgetFilter', + 'UseLink', + 'FullUrl', + 'PropertyCollection', + 'RelativeStorage', + 'StorageAuthConfigId', + 'StorageMode', + 'StorageType', + ); + + protected function set(array $data): void + { + if (array_key_exists('DocumentViewer', $data)) { + $data['DocumentViewer'] = (int) $data['DocumentViewer']; + } + if (array_key_exists('DocumentViewerHeight', $data)) { + $data['DocumentViewerHeight'] = (int) $data['DocumentViewerHeight']; + } + if (array_key_exists('DocumentViewerWidth', $data)) { + $data['DocumentViewerWidth'] = (int) $data['DocumentViewerWidth']; + } + if (array_key_exists('FileWidget', $data)) { + $data['FileWidget'] = filter_var($data['FileWidget'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + } + if (array_key_exists('UseLink', $data)) { + $data['UseLink'] = filter_var($data['UseLink'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + } + if (array_key_exists('FullUrl', $data)) { + $data['FullUrl'] = filter_var($data['FullUrl'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + } + if (array_key_exists('RelativeStorage', $data)) { + $data['RelativeStorage'] = (int) $data['RelativeStorage']; + } + if (array_key_exists('StorageMode', $data)) { + $data['StorageMode'] = (int) $data['StorageMode']; + } + parent::set($data); + } +} + +/* + + + + + + + + + + + array( + 'DocumentViewer' => 1, + 'DocumentViewerHeight' => 0, + 'DocumentViewerWidth' => 0, + 'FileWidget' => true, + 'FileWidgetButton' => true, + 'FileWidgetFilter' => '', + 'PropertyCollection' => array( + 'name' => '', + 'properties' => array( + 'storageUrl' => array ( + 'active' => true, + 'expression' => '\'http://webdav/shapeData/\'||file_name(@selected_file_path)', + 'type' => 3, + ), + ), + 'type' => 'collection', + ), + 'RelativeStorage' => 0, + 'StorageAuthConfigId' => 'k6k7lv8', + 'StorageMode' => 0, + 'StorageType' => 'WebDAV', + ); +*/ diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/RangeConfig.php b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/RangeConfig.php new file mode 100644 index 0000000000..2c27ba4008 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/RangeConfig.php @@ -0,0 +1,76 @@ + + * + * + * + * + * + * + * + * + * + * . + * + * @property bool $AllowNull + * @property int $Max + * @property int $Min + * @property int $Precision + * @property float $Step + * @property string $Style + */ +class RangeConfig extends Qgis\BaseQgisObject +{ + /** @var array The instance properties */ + protected $properties = array( + 'AllowNull', + 'Max', + 'Min', + 'Precision', + 'Step', + 'Style', + ); + + /** @var array The default values */ + protected $defaultValues = array( + 'AllowNull' => true, + 'Style' => 'SpinBox', + ); + + protected function set(array $data): void + { + if (array_key_exists('AllowNull', $data)) { + $data['AllowNull'] = filter_var($data['AllowNull'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + } + if (array_key_exists('Max', $data)) { + $data['Max'] = (int) $data['Max']; + } + if (array_key_exists('Min', $data)) { + $data['Min'] = (int) $data['Min']; + } + if (array_key_exists('Precision', $data)) { + $data['Precision'] = (int) $data['Precision']; + } + if (array_key_exists('Step', $data)) { + $data['Step'] = (float) $data['Step']; + } + parent::set($data); + } +} diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/RelationReferenceConfig.php b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/RelationReferenceConfig.php new file mode 100644 index 0000000000..42c8687e23 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/RelationReferenceConfig.php @@ -0,0 +1,110 @@ + + * + * + * + * . + * + * @property bool $AllowAddFeatures + * @property bool $AllowNULL + * @property bool $MapIdentification + * @property bool $OrderByValue + * @property bool $ReadOnly + * @property string $ReferencedLayerDataSource + * @property string $ReferencedLayerId + * @property string $ReferencedLayerName + * @property string $ReferencedLayerProviderKey + * @property string $Relation + * @property bool $ShowForm + * @property bool $ShowOpenFormButton + */ +class RelationReferenceConfig extends Qgis\BaseQgisObject +{ + /** @var array The instance properties */ + protected $properties = array( + 'AllowAddFeatures', + 'AllowNULL', + 'MapIdentification', + 'OrderByValue', // TODO Remove when QGIS 3.32 will be the minimum version for allowing a QGIS project + 'ReadOnly', + 'ReferencedLayerDataSource', + 'ReferencedLayerId', + 'ReferencedLayerName', + 'ReferencedLayerProviderKey', + 'Relation', + 'ShowForm', + 'ShowOpenFormButton', + ); + + /** @var array The default values for properties */ + protected $defaultValues = array( + 'OrderByValue' => true, // TODO Remove when QGIS 3.32 will be the minimum version for allowing a QGIS project + ); + + protected function set(array $data): void + { + if (array_key_exists('AllowAddFeatures', $data)) { + $data['AllowAddFeatures'] = filter_var($data['AllowAddFeatures'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + } + if (array_key_exists('AllowNULL', $data)) { + $data['AllowNULL'] = filter_var($data['AllowNULL'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + } + if (array_key_exists('MapIdentification', $data)) { + $data['MapIdentification'] = filter_var($data['MapIdentification'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + } + // TODO Remove when QGIS 3.32 will be the minimum version for allowing a QGIS project + if (array_key_exists('OrderByValue', $data)) { + $data['OrderByValue'] = filter_var($data['OrderByValue'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + } + if (array_key_exists('ReadOnly', $data)) { + $data['ReadOnly'] = filter_var($data['ReadOnly'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + } + if (array_key_exists('ShowForm', $data)) { + $data['ShowForm'] = filter_var($data['ShowForm'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + } + if (array_key_exists('ShowOpenFormButton', $data)) { + $data['ShowOpenFormButton'] = filter_var($data['ShowOpenFormButton'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + } + parent::set($data); + } +} + +/* + + + + + + +*/ diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/TextEditConfig.php b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/TextEditConfig.php new file mode 100644 index 0000000000..02ba457011 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/TextEditConfig.php @@ -0,0 +1,72 @@ + + * + * + * + * . + * + * @property bool $IsMultiline + * @property bool $UseHtml + */ +class TextEditConfig extends Qgis\BaseQgisObject +{ + /** @var array The instance properties */ + protected $properties = array( + 'IsMultiline', + 'UseHtml', + ); + + /** @var array The default values */ + protected $defaultValues = array( + 'IsMultiline' => false, + 'UseHtml' => false, + ); + + protected function set(array $data): void + { + if (array_key_exists('IsMultiline', $data)) { + $data['IsMultiline'] = filter_var($data['IsMultiline'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + } + if (array_key_exists('UseHtml', $data)) { + $data['UseHtml'] = filter_var($data['UseHtml'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + } + parent::set($data); + } +} + +/* + + + + + + + + + + +*/ diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/UniqueValuesConfig.php b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/UniqueValuesConfig.php new file mode 100644 index 0000000000..90314d4477 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/UniqueValuesConfig.php @@ -0,0 +1,48 @@ + + * + * + * + * . + * + * @property bool $Editable + */ +class UniqueValuesConfig extends Qgis\BaseQgisObject +{ + /** @var array The instance properties */ + protected $properties = array( + 'Editable', + ); + + /** @var array The default values */ + protected $defaultValues = array( + 'Editable' => false, + ); + + protected function set(array $data): void + { + if (array_key_exists('Editable', $data)) { + $data['Editable'] = filter_var($data['Editable'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + } + parent::set($data); + } +} diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/ValueMapConfig.php b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/ValueMapConfig.php new file mode 100644 index 0000000000..c9a247b2f0 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/ValueMapConfig.php @@ -0,0 +1,39 @@ + + * + * + * + * + * . + * + * @property array-key $map + */ +class ValueMapConfig extends Qgis\BaseQgisObject +{ + /** @var array The instance properties */ + protected $properties = array( + 'map', + ); +} diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/ValueRelationConfig.php b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/ValueRelationConfig.php new file mode 100644 index 0000000000..5aced3bb90 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/EditWidget/ValueRelationConfig.php @@ -0,0 +1,83 @@ + + * + * + * + * . + * + * @property string $Layer + * @property string $LayerName + * @property string $LayerSource + * @property string $LayerProviderName + * @property string $Key + * @property string $Value + * @property string $Description + * @property bool $AllowMulti + * @property string $NofColumns + * @property bool $AllowNull + * @property bool $OrderByValue + * @property string $FilterExpression + * @property bool $UseCompleter + */ +class ValueRelationConfig extends Qgis\BaseQgisObject +{ + /** @var array The instance properties */ + protected $properties = array( + 'Layer', + 'LayerName', + 'LayerSource', + 'LayerProviderName', + 'Key', + 'Value', + 'Description', + 'AllowMulti', + 'NofColumns', + 'AllowNull', + 'OrderByValue', + 'FilterExpression', + 'UseCompleter', + ); + + protected function set(array $data): void + { + if (array_key_exists('AllowMulti', $data)) { + $data['AllowMulti'] = filter_var($data['AllowMulti'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + } + if (array_key_exists('AllowNull', $data)) { + $data['AllowNull'] = filter_var($data['AllowNull'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + } + if (array_key_exists('OrderByValue', $data)) { + $data['OrderByValue'] = filter_var($data['OrderByValue'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + } + if (array_key_exists('UseCompleter', $data)) { + $data['UseCompleter'] = filter_var($data['UseCompleter'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + } + parent::set($data); + } +} diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerEditWidget.php b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerEditWidget.php new file mode 100644 index 0000000000..1b3de9741b --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerEditWidget.php @@ -0,0 +1,121 @@ + The instance properties */ + protected $properties = array( + 'type', + 'config', + ); + + /** @var array The not null properties */ + protected $mandatoryProperties = array( + 'type', + 'config', + ); + + /** @var string The XML element local name */ + protected static $qgisLocalName = 'editWidget'; + + protected static function getAttributes($oXmlReader) + { + return array( + 'type' => $oXmlReader->getAttribute('type'), + ); + } + + /** @var array The XML element parsed children */ + protected static $children = array( + 'config', + ); + + protected static $childParsers = array(); + + protected static function buildInstance($data) + { + if ($data['type'] == 'TextEdit') { + $data['config'] = new EditWidget\TextEditConfig($data['config']); + } elseif ($data['type'] == 'CheckBox') { + $data['config'] = new EditWidget\CheckBoxConfig($data['config']); + } elseif ($data['type'] == 'DateTime') { + $data['config'] = new EditWidget\DateTimeConfig($data['config']); + } elseif ($data['type'] == 'Range') { + $data['config'] = new EditWidget\RangeConfig($data['config']); + } elseif ($data['type'] == 'ExternalResource') { + $data['config'] = new EditWidget\ExternalResourceConfig($data['config']); + } elseif ($data['type'] == 'ValueMap') { + $data['config'] = new EditWidget\ValueMapConfig($data['config']); + } elseif ($data['type'] == 'UniqueValues') { + $data['config'] = new EditWidget\UniqueValuesConfig($data['config']); + } elseif ($data['type'] == 'RelationReference') { + $data['config'] = new EditWidget\RelationReferenceConfig($data['config']); + } elseif ($data['type'] == 'ExternalResource') { + $data['config'] = new EditWidget\ValueRelationConfig($data['config']); + } + + return new VectorLayerEditWidget($data); + } +} +VectorLayerEditWidget::registerChildParser('config', function ($oXmlReader) { + $depth = $oXmlReader->depth; + $localName = $oXmlReader->localName; + $data = array(); + if ($oXmlReader->isEmptyElement) { + return $data; + } + while ($oXmlReader->read()) { + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'Option') { + $data += Qgis\Parser::readOption($oXmlReader); + } + } + + return $data; + /* + + + + + + + + + + */ +}); diff --git a/tests/units/classes/Project/Qgis/VectorLayerEditWidgetTest.php b/tests/units/classes/Project/Qgis/VectorLayerEditWidgetTest.php new file mode 100644 index 0000000000..7f4b5655f6 --- /dev/null +++ b/tests/units/classes/Project/Qgis/VectorLayerEditWidgetTest.php @@ -0,0 +1,302 @@ + + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $editWidget = Qgis\Layer\VectorLayerEditWidget::fromXmlReader($oXml); + + $this->assertEquals('TextEdit', $editWidget->type); + $this->assertNotNull($editWidget->config); + $this->assertInstanceOf(Qgis\Layer\EditWidget\TextEditConfig::class, $editWidget->config); + + $config = array( + 'IsMultiline' => false, + 'UseHtml' => false, + ); + foreach($config as $prop => $value) { + $this->assertEquals($value, $editWidget->config->$prop, $prop); + } + $this->assertEquals($config, $editWidget->config->getData()); + + // Simple default old + $xmlStr = ' + + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $editWidget = Qgis\Layer\VectorLayerEditWidget::fromXmlReader($oXml); + + $this->assertEquals('TextEdit', $editWidget->type); + $this->assertNotNull($editWidget->config); + $this->assertInstanceOf(Qgis\Layer\EditWidget\TextEditConfig::class, $editWidget->config); + + $config = array( + 'IsMultiline' => false, + 'UseHtml' => false, + ); + foreach($config as $prop => $value) { + $this->assertEquals($value, $editWidget->config->$prop, $prop); + } + + // Default config + $xmlStr = ' + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $editWidget = Qgis\Layer\VectorLayerEditWidget::fromXmlReader($oXml); + + $this->assertEquals('TextEdit', $editWidget->type); + $this->assertNotNull($editWidget->config); + $this->assertInstanceOf(Qgis\Layer\EditWidget\TextEditConfig::class, $editWidget->config); + + $config = array( + 'IsMultiline' => false, + 'UseHtml' => false, + ); + foreach($config as $prop => $value) { + $this->assertEquals($value, $editWidget->config->$prop, $prop); + } + } + + public function testCheckBoxFromXmlReader() + { + // Config + $xmlStr = ' + + + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $editWidget = Qgis\Layer\VectorLayerEditWidget::fromXmlReader($oXml); + + $this->assertEquals('CheckBox', $editWidget->type); + $this->assertNotNull($editWidget->config); + $this->assertInstanceOf(Qgis\Layer\EditWidget\CheckBoxConfig::class, $editWidget->config); + + $config = array( + 'CheckedState' => '1', + 'UncheckedState' => '0', + 'TextDisplayMethod' => 1, + ); + foreach($config as $prop => $value) { + $this->assertEquals($value, $editWidget->config->$prop, $prop); + } + + $xmlStr = ' + + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $editWidget = Qgis\Layer\VectorLayerEditWidget::fromXmlReader($oXml); + + $this->assertEquals('CheckBox', $editWidget->type); + $this->assertNotNull($editWidget->config); + $this->assertInstanceOf(Qgis\Layer\EditWidget\CheckBoxConfig::class, $editWidget->config); + + $config = array( + 'CheckedState' => '1', + 'UncheckedState' => '0', + 'TextDisplayMethod' => 0, + ); + foreach($config as $prop => $value) { + $this->assertEquals($value, $editWidget->config->$prop, $prop); + } + + // Default config + $xmlStr = ' + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $editWidget = Qgis\Layer\VectorLayerEditWidget::fromXmlReader($oXml); + + $this->assertEquals('CheckBox', $editWidget->type); + $this->assertNotNull($editWidget->config); + $this->assertInstanceOf(Qgis\Layer\EditWidget\CheckBoxConfig::class, $editWidget->config); + + $config = array( + 'CheckedState' => '', + 'UncheckedState' => '', + 'TextDisplayMethod' => 0, + ); + foreach($config as $prop => $value) { + $this->assertEquals($value, $editWidget->config->$prop, $prop); + } + } + + public function testDateTimeFromXmlReader() + { + // Config + $xmlStr = ' + + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $editWidget = Qgis\Layer\VectorLayerEditWidget::fromXmlReader($oXml); + + $this->assertEquals('DateTime', $editWidget->type); + $this->assertNotNull($editWidget->config); + $this->assertInstanceOf(Qgis\Layer\EditWidget\DateTimeConfig::class, $editWidget->config); + + $config = array( + 'allow_null' => false, + 'calendar_popup' => false, + 'display_format' => '', + 'field_format' => 'yyyy-MM-dd', + 'field_iso_format' => false, + ); + foreach($config as $prop => $value) { + $this->assertEquals($value, $editWidget->config->$prop, $prop); + } + + // Default config + $xmlStr = ' + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $editWidget = Qgis\Layer\VectorLayerEditWidget::fromXmlReader($oXml); + + $this->assertEquals('DateTime', $editWidget->type); + $this->assertNotNull($editWidget->config); + $this->assertInstanceOf(Qgis\Layer\EditWidget\DateTimeConfig::class, $editWidget->config); + + $config = array( + 'allow_null' => false, + 'calendar_popup' => false, + 'display_format' => '', + 'field_format' => 'yyyy-MM-dd', + 'field_iso_format' => false, + ); + foreach($config as $prop => $value) { + $this->assertEquals($value, $editWidget->config->$prop, $prop); + } + } + + public function testRangeFromXmlReader() + { + // Config + $xmlStr = ' + + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $editWidget = Qgis\Layer\VectorLayerEditWidget::fromXmlReader($oXml); + + $this->assertEquals('Range', $editWidget->type); + $this->assertNotNull($editWidget->config); + $this->assertInstanceOf(Qgis\Layer\EditWidget\RangeConfig::class, $editWidget->config); + + $config = array( + 'AllowNull' => true, + 'Max' => 2147483647, + 'Min' => -2147483648, + 'Precision' => 0, + 'Step' => 1, + 'Style' => 'SpinBox', + ); + foreach($config as $prop => $value) { + $this->assertEquals($value, $editWidget->config->$prop, $prop); + } + + // Default config + $xmlStr = ' + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $editWidget = Qgis\Layer\VectorLayerEditWidget::fromXmlReader($oXml); + + $this->assertEquals('Range', $editWidget->type); + $this->assertNotNull($editWidget->config); + $this->assertInstanceOf(Qgis\Layer\EditWidget\RangeConfig::class, $editWidget->config); + + $config = array( + 'AllowNull' => true, + 'Style' => 'SpinBox', + ); + foreach($config as $prop => $value) { + $this->assertEquals($value, $editWidget->config->$prop, $prop); + } + $notSet = array( + 'Max', + 'Min', + 'Precision', + 'Step', + ); + foreach($notSet as $not) { + $this->assertFalse(isset($editWidget->config->$not), $not); + } + } +} From 23ed90c66868f794be82d23a6960bfb8147ca32a Mon Sep 17 00:00:00 2001 From: rldhont Date: Sat, 23 Mar 2024 16:41:01 +0100 Subject: [PATCH 17/46] Lizmap\Project\Qgis\Layer\VectorLayerField parsing EditWidget --- .../Project/Qgis/Layer/VectorLayerField.php | 12 +- .../Project/Qgis/VectorLayerFieldTest.php | 110 ++++++++++++++++++ 2 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 tests/units/classes/Project/Qgis/VectorLayerFieldTest.php diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerField.php b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerField.php index f28ed13c59..5b5b5b1d88 100644 --- a/lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerField.php +++ b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayerField.php @@ -12,25 +12,28 @@ namespace Lizmap\Project\Qgis\Layer; -use Lizmap\Project; +use Lizmap\Project\Qgis; /** * QGIS Vector layer field. * * @property string $name * @property null|string $configurationFlags + * @property Qgis\Layer\VectorLayerEditWidget $editWidget */ -class VectorLayerField extends Project\Qgis\BaseQgisXmlObject +class VectorLayerField extends Qgis\BaseQgisXmlObject { /** @var array The instance properties */ protected $properties = array( 'name', 'configurationFlags', + 'editWidget', ); /** @var array The not null properties */ protected $mandatoryProperties = array( 'name', + 'editWidget', ); /** @@ -67,4 +70,9 @@ protected static function getAttributes($oXmlReader) 'configurationFlags' => $oXmlReader->getAttribute('configurationFlags'), ); } + + protected static $childParsers = array(); } +VectorLayerField::registerChildParser('editWidget', function ($oXmlReader) { + return VectorLayerEditWidget::fromXmlReader($oXmlReader); +}); diff --git a/tests/units/classes/Project/Qgis/VectorLayerFieldTest.php b/tests/units/classes/Project/Qgis/VectorLayerFieldTest.php new file mode 100644 index 0000000000..188be26898 --- /dev/null +++ b/tests/units/classes/Project/Qgis/VectorLayerFieldTest.php @@ -0,0 +1,110 @@ + + + + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $field = Qgis\Layer\VectorLayerField::fromXmlReader($oXml); + + $this->assertEquals('fid', $field->name); + $this->assertFalse($field->isHideFromWms()); + $this->assertFalse($field->isHideFromWfs()); + $this->assertNotNull($field->editWidget); + $this->assertEquals('TextEdit', $field->editWidget->type); + $this->assertNotNull($field->editWidget->config); + $this->assertInstanceOf(Qgis\Layer\EditWidget\TextEditConfig::class, $field->editWidget->config); + //$this->assertCount(2, $field->editWidget->config); + + $config = array( + 'IsMultiline' => false, + 'UseHtml' => false, + ); + foreach($config as $prop => $value) { + $this->assertEquals($value, $field->editWidget->config->$prop, $prop); + } + + // Old simple + $xmlStr = ' + + + + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $field = Qgis\Layer\VectorLayerField::fromXmlReader($oXml); + + $this->assertEquals('popdensity', $field->name); + $this->assertFalse($field->isHideFromWms()); + $this->assertFalse($field->isHideFromWfs()); + $this->assertNotNull($field->editWidget); + $this->assertEquals('TextEdit', $field->editWidget->type); + $this->assertNotNull($field->editWidget->config); + //$this->assertCount(2, $field->editWidget->config); + + $config = array( + 'IsMultiline' => false, + 'UseHtml' => false, + ); + foreach($config as $prop => $value) { + $this->assertEquals($value, $field->editWidget->config->$prop, $prop); + } + $this->assertEquals($config, $field->editWidget->config->getData()); + + // Default config + $xmlStr = ' + + + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $field = Qgis\Layer\VectorLayerField::fromXmlReader($oXml); + + $this->assertEquals('description', $field->name); + $this->assertFalse($field->isHideFromWms()); + $this->assertFalse($field->isHideFromWfs()); + $this->assertNotNull($field->editWidget); + $this->assertEquals('TextEdit', $field->editWidget->type); + $this->assertNotNull($field->editWidget->config); + //$this->assertCount(2, $field->editWidget->config); + + $config = array( + 'IsMultiline' => false, + 'UseHtml' => false, + ); + foreach($config as $prop => $value) { + $this->assertEquals($value, $field->editWidget->config->$prop, $prop); + } + } +} From d9f9160c747213a499b450fe1345f42c92daeb34 Mon Sep 17 00:00:00 2001 From: rldhont Date: Tue, 15 Oct 2024 17:58:15 +0200 Subject: [PATCH 18/46] Lizmap\Project\Qgis\Layer\VectorLayer methods toKeyArray and getFormControls --- .../lib/Project/Qgis/Layer/VectorLayer.php | 410 +++ .../classes/Project/Qgis/VectorLayerTest.php | 3275 +++++++++++++++++ 2 files changed, 3685 insertions(+) create mode 100644 tests/units/classes/Project/Qgis/VectorLayerTest.php diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayer.php b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayer.php index 9d83030c77..ef6d3fca2b 100644 --- a/lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayer.php +++ b/lizmap/modules/lizmap/lib/Project/Qgis/Layer/VectorLayer.php @@ -158,4 +158,414 @@ public function getFieldEditable($fieldName) return false; } + + /** + * Get vector layer as key array. + * + * @return array + */ + public function toKeyArray() + { + $data = array( + 'type' => $this->type, + 'id' => $this->id, + 'name' => $this->layername, + 'shortname' => $this->shortname !== null ? $this->shortname : '', + 'title' => $this->title !== null ? $this->title : $this->layername, + 'abstract' => $this->abstract !== null ? $this->abstract : '', + 'proj4' => $this->srs->proj4, + 'srid' => $this->srs->srid, + 'authid' => $this->srs->authid, + 'datasource' => $this->datasource, + 'provider' => $this->provider, + 'keywords' => $this->keywordList !== null ? $this->keywordList : array(), + ); + + $fields = array(); + $wfsFields = array(); + $aliases = array(); + $defaults = array(); + $constraints = array(); + $webDavFields = array(); + $webDavBaseUris = array(); + foreach ($this->fieldConfiguration as $field) { + if (in_array($field->name, $fields)) { + continue; // QGIS sometimes stores them twice + } + $fields[] = $field->name; + if (!$field->isHideFromWfs()) { + $wfsFields[] = $field->name; + } + $aliases[$field->name] = $field->name; + $defaults[$field->name] = null; + $constraints[$field->name] = null; + + if ($field->editWidget->type === 'ExternalResource') { + if ($field->editWidget->config instanceof Qgis\BaseQgisObject) { + $fieldEditOptions = $field->editWidget->config->getData(); + } else { + $fieldEditOptions = array_merge(array(), $field->editWidget->config); + } + if (array_key_exists('StorageType', $fieldEditOptions) && $fieldEditOptions['StorageType'] === 'WebDAV') { + $this->readWebDavStorageOptions($fieldEditOptions); + $webDavFields[] = $field->name; + $webDavBaseUris[] = $fieldEditOptions['webDAVStorageUrl']; + } + } + } + if ($this->aliases !== null) { + foreach ($this->aliases as $alias) { + $aliases[$alias->field] = $alias->name; + } + } + if ($this->defaults !== null) { + foreach ($this->defaults as $default) { + $defaults[$default->field] = $default->expression; + } + } + if ($this->constraints !== null) { + foreach ($this->constraints as $constraint) { + $c = array( + 'constraints' => 0, + 'notNull' => false, + 'unique' => false, + 'exp' => false, + ); + $c['constraints'] = $constraint->constraints; + if ($c['constraints'] > 0) { + $c['notNull'] = $constraint->notnull_strength; + $c['unique'] = $constraint->unique_strength; + $c['exp'] = $constraint->exp_strength; + } + $constraints[$constraint->field] = $c; + } + } + if ($this->constraintExpressions !== null) { + foreach ($this->constraintExpressions as $constraint) { + $c = array( + 'constraints' => 0, + 'notNull' => false, + 'unique' => false, + 'exp' => false, + ); + if (array_key_exists($constraint->field, $constraints)) { + $c = $constraints[$constraint->field]; + } + if ($constraint->exp !== '') { + $c['exp'] = true; + $c['exp_value'] = $constraint->exp; + $c['exp_desc'] = $constraint->desc; + } + $constraints[$constraint->field] = $c; + } + } + + $data['fields'] = $fields; + $data['aliases'] = $aliases; + $data['defaults'] = $defaults; + $data['constraints'] = $constraints; + $data['wfsFields'] = $wfsFields; + $data['webDavFields'] = $webDavFields; + $data['webDavBaseUris'] = $webDavBaseUris; + + if ($this->excludeAttributesWFS !== null) { + foreach ($this->excludeAttributesWFS as $eField) { + if (!in_array($eField, $wfsFields)) { + continue; // QGIS sometimes stores them twice + } + array_splice($wfsFields, array_search($eField, $wfsFields), 1); + } + $data['wfsFields'] = $wfsFields; + } + + return $data; + } + + /** @var array The options type */ + protected static $optionTypes = array( + 'Min' => 'f', + 'Max' => 'f', + 'Step' => 'i', + 'Precision' => 'i', + 'AllowMulti' => 'b', + 'AllowNull' => 'b', + 'UseCompleter' => 'b', + 'DocumentViewer' => 'b', + 'fieldEditable' => 'b', + 'editable' => 'b', + 'Editable' => 'b', + 'notNull' => 'b', + 'MapIdentification' => 'b', + 'IsMultiline' => 'b', + 'UseHtml' => 'b', + 'field_iso_format' => 'b', + ); + + /** + * Update options value by converting them. + * + * @param array $options + */ + protected function convertTypeOptions(&$options) + { + foreach ($options as $name => $val) { + if (isset(self::$optionTypes[$name])) { + switch (self::$optionTypes[$name]) { + case 'f': + $options[$name] = (float) $val; + + break; + + case 'i': + $options[$name] = (int) $val; + + break; + + case 'b': + $options[$name] = filter_var($val, FILTER_VALIDATE_BOOLEAN); + + break; + + } + } + } + } + + /** + * Get the HTML markup. + * + * @param string $fieldEditType Field edit type + * @param array $fieldEditOptions Field edit config options + * + * @return string The HTML markup corresponding to field edit type and config options + */ + protected function getMarkup($fieldEditType, $fieldEditOptions) + { + $qgisEdittypeMap = Form\QgisFormControl::getEditTypeMap(); + + if ($fieldEditType === 'TextEdit') { + $isMultiLine = false; + if (array_key_exists('IsMultiline', $fieldEditOptions)) { + $isMultiLine = $fieldEditOptions['IsMultiline']; + } + + if (!$isMultiLine) { + $fieldEditType = 'LineEdit'; + $markup = $qgisEdittypeMap[$fieldEditType]['jform']['markup']; + } else { + $useHtml = false; + if (array_key_exists('UseHtml', $fieldEditOptions)) { + $useHtml = $fieldEditOptions['UseHtml']; + } + if ($useHtml) { + $markup = $qgisEdittypeMap[$fieldEditType]['jform']['markup'][1]; + } else { + $markup = $qgisEdittypeMap[$fieldEditType]['jform']['markup'][0]; + } + } + } elseif ($fieldEditType === 'ValueRelation') { + $allowMulti = false; + if (array_key_exists('AllowMulti', $fieldEditOptions)) { + $allowMulti = $fieldEditOptions['AllowMulti']; + } + $markup = $qgisEdittypeMap[$fieldEditType]['jform']['markup'][$allowMulti]; + } elseif ($fieldEditType === 'Range' || $fieldEditType === 'EditRange') { + $markup = $qgisEdittypeMap[$fieldEditType]['jform']['markup'][0]; + } elseif ($fieldEditType === 'SliderRange' || $fieldEditType === 'DialRange') { + $markup = $qgisEdittypeMap[$fieldEditType]['jform']['markup'][1]; + } elseif ($fieldEditType === 'DateTime') { + $markup = 'date'; + $display_format = ''; + if (array_key_exists('display_format', $fieldEditOptions)) { + $display_format = $fieldEditOptions['display_format']; + } + // Use date AND time widget id type is DateTime and we find HH + if (preg_match('#HH#i', $display_format)) { + $markup = 'datetime'; + } + // Use only time if field is only time + if (preg_match('#HH#i', $display_format) and !preg_match('#YY#i', $display_format)) { + $markup = 'time'; + } + } elseif (array_key_exists($fieldEditType, $qgisEdittypeMap)) { + $markup = $qgisEdittypeMap[$fieldEditType]['jform']['markup']; + } else { + $markup = ''; + } + + return $markup; + } + + /** + * Update upload config options. + * + * @param string $fieldEditType Field edit type + * @param array $fieldEditOptions Field edit config options + */ + protected function readUploadOptions($fieldEditType, &$fieldEditOptions) + { + $mimeTypes = array(); + $acceptAttr = ''; + $captureAttr = ''; + $imageUpload = false; + $defaultRoot = ''; + + if ($fieldEditType === 'Photo') { + $mimeTypes = array('image/jpg', 'image/jpeg', 'image/pjpeg', 'image/png', 'image/gif'); + $acceptAttr = implode(', ', $mimeTypes); + $captureAttr = 'environment'; + $imageUpload = true; + } elseif ($fieldEditType === 'ExternalResource') { + $accepts = array(); + $FileWidgetFilter = $fieldEditOptions['FileWidgetFilter'] ?? ''; + if ($FileWidgetFilter) { + // QFileDialog::getOpenFileName filter + $FileWidgetFilter = explode(';;', $FileWidgetFilter); + $re = '/\*(\.\w{3,6})/'; + $hasNoImageItem = false; + foreach ($FileWidgetFilter as $FileFilter) { + $matches = array(); + if (preg_match_all($re, $FileFilter, $matches)) { + foreach ($matches[1] as $m) { + $type = \jFile::getMimeTypeFromFilename('f'.$m); + if ($type != 'application/octet-stream') { + $mimeTypes[] = $type; + } + if (strpos($type, 'image/') === 0) { + $imageUpload = true; + } else { + $hasNoImageItem = true; + } + $accepts[] = $m; + } + } + } + if ($hasNoImageItem) { + $imageUpload = false; + } + + if (count($accepts) > 0) { + $mimeTypes = array_unique($mimeTypes); + $accepts = array_unique($accepts); + $acceptAttr = implode(', ', $accepts); + } + } + $isDocumentViewer = $fieldEditOptions['DocumentViewer'] ?? ''; + if ($isDocumentViewer) { + if (count($accepts)) { + $mimeTypes = array(); + $typeTab = array( + '.gif' => 'image/gif', + '.png' => 'image/png', + '.jpg' => array('image/jpg', 'image/jpeg', 'image/pjpeg'), + '.jpeg' => array('image/jpg', 'image/jpeg', 'image/pjpeg'), + '.bm' => array('image/bmp', 'image/x-windows-bmp'), + '.bmp' => array('image/bmp', 'image/x-windows-bmp'), + '.pbm' => 'image/x-portable-bitmap', + '.pgm' => array('image/x-portable-graymap', 'image/x-portable-greymap'), + '.ppm' => 'image/x-portable-pixmap', + '.xbm' => array('image/xbm', 'image/x-xbm', 'image/x-xbitmap'), + '.xpm' => array('image/xpm', 'image/x-xpixmap'), + '.svg' => 'image/svg+xml', + ); + $filteredAccepts = array(); + foreach ($accepts as $a) { + if (array_key_exists($a, $typeTab)) { + if ((in_array($a, array('.jpg', '.jpeg')) && in_array('image/jpg', $mimeTypes)) + || (in_array($a, array('.bm', '.bmp')) && in_array('image/bmp', $mimeTypes))) { + continue; + } + if (is_array($typeTab[$a])) { + $mimeTypes = array_merge($mimeTypes, $typeTab[$a]); + } else { + $mimeTypes[] = $typeTab[$a]; + } + $filteredAccepts[] = $a; + } + } + $mimeTypes = array_unique($mimeTypes); + $accepts = array_unique($filteredAccepts); + $acceptAttr = implode(', ', $accepts); + } else { + $mimeTypes = array('image/jpg', 'image/jpeg', 'image/pjpeg', 'image/png', 'image/gif'); + $acceptAttr = 'image/jpg, image/jpeg, image/pjpeg, image/png, image/gif'; + } + $captureAttr = 'environment'; + $imageUpload = true; + } + $defaultRoot = $fieldEditOptions['DefaultRoot'] ?? ''; + + if ($defaultRoot + && (preg_match('#^../media(/)?#', $defaultRoot) + || preg_match('#^media(/)?#', $defaultRoot))) { + // Remove the last slashes and add only one + $defaultRoot = rtrim($defaultRoot, '/').'/'; + } else { + $defaultRoot = ''; + } + $this->readWebDavStorageOptions($fieldEditOptions); + } + + $fieldEditOptions['UploadMimeTypes'] = $mimeTypes; + $fieldEditOptions['DefaultRoot'] = $defaultRoot; + $fieldEditOptions['UploadAccept'] = $acceptAttr; + $fieldEditOptions['UploadCapture'] = $captureAttr; + $fieldEditOptions['UploadImage'] = $imageUpload; + } + + /** + * update the upload options with the property 'webDAVStorageUrl'. + * + * @param array $fieldEditOptions + */ + protected function readWebDavStorageOptions(&$fieldEditOptions) + { + $webDAV = (array_key_exists('StorageType', $fieldEditOptions) && $fieldEditOptions['StorageType'] == 'WebDAV') ? $fieldEditOptions['StorageType'] : null; + if ($webDAV) { + if (isset($fieldEditOptions['PropertyCollection'], $fieldEditOptions['PropertyCollection']['properties'], $fieldEditOptions['PropertyCollection']['properties']['storageUrl'], $fieldEditOptions['PropertyCollection']['properties']['storageUrl']['expression'])) { + $fieldEditOptions['webDAVStorageUrl'] = $fieldEditOptions['PropertyCollection']['properties']['storageUrl']['expression']; + } else { + $fieldEditOptions['webDAVStorageUrl'] = $fieldEditOptions['StorageUrl']; + } + } + } + + /** + * Get form controls. + * + * @return array + */ + public function getFormControls() + { + $formControls = array(); + foreach ($this->fieldConfiguration as $field) { + $fieldName = $field->name; + $fieldEditType = $field->editWidget->type; + if ($field->editWidget->config instanceof Qgis\BaseQgisObject) { + $fieldEditOptions = $field->editWidget->config->getData(); + } else { + $fieldEditOptions = array_merge(array(), $field->editWidget->config); + } + $fieldEditOptions['editable'] = $this->getFieldEditable($fieldName); + $this->convertTypeOptions($fieldEditOptions); + $markup = $this->getMarkup($fieldEditType, $fieldEditOptions); + if ($markup == 'upload') { + $this->readUploadOptions($fieldEditType, $fieldEditOptions); + } + $control = new Form\QgisFormControlProperties($fieldName, $fieldEditType, $markup, $fieldEditOptions); + + $alias = $this->getFieldAlias($fieldName); + if ($alias !== null) { + $control->setFieldAlias($alias); + } + + if ($this->rendererV2 && $this->rendererV2->type == 'categorizedSymbol') { + $control->setRendererCategories($this->rendererV2->categories); + } + + $formControls[$fieldName] = $control; + } + + return $formControls; + } } diff --git a/tests/units/classes/Project/Qgis/VectorLayerTest.php b/tests/units/classes/Project/Qgis/VectorLayerTest.php new file mode 100644 index 0000000000..2469733335 --- /dev/null +++ b/tests/units/classes/Project/Qgis/VectorLayerTest.php @@ -0,0 +1,3275 @@ + + + 3.84541513302194948 + 43.59438300000000055 + 3.8956343529661015 + 43.64203625857223301 + + edition_point20130118171631518 + dbname=\'./edition/edition_db.sqlite\' table="edition_point" (geometry) sql= + Points of interest + + + + points of interest + + + GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]] + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat + WGS84 + true + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + true + + + + + + + + + + + + + spatialite + + + + + + + + + + + 1 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + . + + 0 + . + + 0 + tablayout + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + COALESCE( "name", \'<NULL>\' ) + + + '; + + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $layer = Qgis\Layer\MapLayer::fromXmlReader($oXml); + $this->assertFalse($layer->embedded); + $this->assertInstanceOf(Qgis\Layer\VectorLayer::class, $layer); + + $this->assertNotNull($layer->constraints); + $this->assertCount(7, $layer->constraints); + + $this->assertInstanceOf(Qgis\Layer\VectorLayerConstraint::class, $layer->constraints[0]); + $this->assertEquals('pkuid', $layer->constraints[0]->field); + $this->assertEquals(3, $layer->constraints[0]->constraints); + $this->assertTrue($layer->constraints[0]->notnull_strength); + $this->assertTrue($layer->constraints[0]->unique_strength); + $this->assertFalse($layer->constraints[0]->exp_strength); + + $this->assertInstanceOf(Qgis\Layer\VectorLayerConstraint::class, $layer->constraints[1]); + $this->assertEquals('name', $layer->constraints[1]->field); + $this->assertEquals(0, $layer->constraints[1]->constraints); + $this->assertFalse($layer->constraints[1]->notnull_strength); + $this->assertFalse($layer->constraints[1]->unique_strength); + $this->assertFalse($layer->constraints[1]->exp_strength); + + $formControls = $layer->getFormControls(); + $this->assertNotNull($formControls); + $this->assertCount(7, $formControls); + + $this->assertInstanceOf(Form\QgisFormControlProperties::class, $formControls['pkuid']); + $this->assertEquals('pkuid', $formControls['pkuid']->getName()); + $this->assertEquals('TextEdit', $formControls['pkuid']->getFieldEditType()); + $this->assertTrue($formControls['pkuid']->isEditable()); + $this->assertEquals('', $formControls['pkuid']->getFieldAlias()); + $this->assertEquals('input', $formControls['pkuid']->getMarkup()); + $this->assertFalse($formControls['pkuid']->isMultiline()); + $this->assertFalse($formControls['pkuid']->useHtml()); + + $this->assertInstanceOf(Form\QgisFormControlProperties::class, $formControls['name']); + $this->assertEquals('name', $formControls['name']->getName()); + $this->assertEquals('TextEdit', $formControls['name']->getFieldEditType()); + $this->assertTrue($formControls['name']->isEditable()); + $this->assertEquals('', $formControls['name']->getFieldAlias()); + $this->assertEquals('input', $formControls['name']->getMarkup()); + $this->assertFalse($formControls['name']->isMultiline()); + $this->assertFalse($formControls['name']->useHtml()); + + $this->assertInstanceOf(Form\QgisFormControlProperties::class, $formControls['description']); + $this->assertEquals('description', $formControls['description']->getName()); + $this->assertEquals('TextEdit', $formControls['description']->getFieldEditType()); + $this->assertTrue($formControls['description']->isEditable()); + $this->assertEquals('', $formControls['description']->getFieldAlias()); + $this->assertEquals('textarea', $formControls['description']->getMarkup()); + $this->assertTrue($formControls['description']->isMultiline()); + $this->assertFalse($formControls['description']->useHtml()); + + $this->assertInstanceOf(Form\QgisFormControlProperties::class, $formControls['date']); + $this->assertEquals('date', $formControls['date']->getName()); + $this->assertEquals('DateTime', $formControls['date']->getFieldEditType()); + $this->assertTrue($formControls['date']->isEditable()); + $this->assertEquals('', $formControls['date']->getFieldAlias()); + $this->assertEquals('date', $formControls['date']->getMarkup()); + $attributes = array ( + 'allow_null' => false, + 'calendar_popup' => false, + 'display_format' => '', + 'field_format' => 'yyyy-MM-dd', + 'field_iso_format' => false, + 'filters' => [], + 'chainFilters' => false, + ); + $this->assertEquals($attributes, $formControls['date']->getEditAttributes()); + + $this->assertInstanceOf(Form\QgisFormControlProperties::class, $formControls['type']); + $this->assertEquals('type', $formControls['type']->getName()); + $this->assertEquals('Classification', $formControls['type']->getFieldEditType()); + $this->assertTrue($formControls['type']->isEditable()); + $this->assertEquals('', $formControls['type']->getFieldAlias()); + $this->assertEquals('menulist', $formControls['type']->getMarkup()); + $categories = array( + 1 => 'café', + 2 => 'pharmacy', + 3 => 'bus stop', + 4 => 'park', + ); + $this->assertEquals($categories, $formControls['type']->getRendererCategories()); + + $this->assertInstanceOf(Form\QgisFormControlProperties::class, $formControls['photo']); + $this->assertEquals('photo', $formControls['photo']->getName()); + $this->assertEquals('ExternalResource', $formControls['photo']->getFieldEditType()); + $this->assertTrue($formControls['photo']->isEditable()); + $this->assertEquals('', $formControls['photo']->getFieldAlias()); + $this->assertEquals('upload', $formControls['photo']->getMarkup()); + $mimeTypes = array( + 0 => 'image/gif', + 1 => 'image/jpg', + 2 => 'image/jpeg', + 3 => 'image/pjpeg', + 4 => 'image/png', + ); + $this->assertEquals($mimeTypes, $formControls['photo']->getMimeTypes()); + $this->assertEquals('.gif, .jpeg, .png', $formControls['photo']->getUploadAccept()); + $this->assertEquals('environment', $formControls['photo']->getUploadCapture()); + $this->assertTrue($formControls['photo']->isImageUpload()); + + $xmlStr = ' + + + 3.86227799999999988 + 43.60601400000000183 + 3.8948940000000003 + 43.64900599999998576 + + edition_polygon20130409114333776 + dbname=\'./edition/edition_db.sqlite\' table="edition_polygon" (geom) sql= + Areas of interest + + + + areas_of_interest + + + GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]] + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat + WGS84 + true + + + + + + + + + + + + + + + + + 0 + 0 + + + + + true + + + + + spatialite + + + + + + + + + + + 1 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + . + + 0 + . + + 0 + generatedlayout + + + + COALESCE( "description", \'<NULL>\' ) + + + '; + + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $layer = Qgis\Layer\MapLayer::fromXmlReader($oXml); + $this->assertFalse($layer->embedded); + $this->assertInstanceOf(Qgis\Layer\VectorLayer::class, $layer); + + $this->assertNotNull($layer->constraints); + $this->assertCount(5, $layer->constraints); + + $this->assertInstanceOf(Qgis\Layer\VectorLayerConstraint::class, $layer->constraints[0]); + $this->assertEquals('pkuid', $layer->constraints[0]->field); + $this->assertEquals(3, $layer->constraints[0]->constraints); + $this->assertTrue($layer->constraints[0]->notnull_strength); + $this->assertTrue($layer->constraints[0]->unique_strength); + $this->assertFalse($layer->constraints[0]->exp_strength); + + $this->assertInstanceOf(Qgis\Layer\VectorLayerConstraint::class, $layer->constraints[1]); + $this->assertEquals('label', $layer->constraints[1]->field); + $this->assertEquals(0, $layer->constraints[1]->constraints); + $this->assertFalse($layer->constraints[1]->notnull_strength); + $this->assertFalse($layer->constraints[1]->unique_strength); + $this->assertFalse($layer->constraints[1]->exp_strength); + + $formControls = $layer->getFormControls(); + $this->assertNotNull($formControls); + $this->assertCount(5, $formControls); + + $this->assertInstanceOf(Form\QgisFormControlProperties::class, $formControls['author']); + $this->assertEquals('author', $formControls['author']->getName()); + $this->assertEquals('UniqueValues', $formControls['author']->getFieldEditType()); + $this->assertTrue($formControls['author']->isEditable()); + $this->assertEquals('', $formControls['author']->getFieldAlias()); + $this->assertEquals('menulist', $formControls['author']->getMarkup()); + + + $this->assertInstanceOf(Form\QgisFormControlProperties::class, $formControls['checked']); + $this->assertEquals('checked', $formControls['checked']->getName()); + $this->assertEquals('CheckBox', $formControls['checked']->getFieldEditType()); + $this->assertTrue($formControls['checked']->isEditable()); + $this->assertEquals('', $formControls['checked']->getFieldAlias()); + $this->assertEquals('checkbox', $formControls['checked']->getMarkup()); + + $xmlStr = ' + + + 3.83129799999999987 + 43.5719143059459455 + 3.90753010594594707 + 43.67051018486487379 + + edition_line20130409161630329 + dbname=\'./edition/edition_db.sqlite\' table="edition_line" (geom) sql= + Bicycle rides + + + + edition_line + + + GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]] + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat + WGS84 + true + + + + + + + + + + + + + + + + + 0 + 0 + + + + + true + + + + + spatialite + + + + + + + + + + + 1 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + . + + 0 + . + + 0 + generatedlayout + + + + COALESCE( "description", \'<NULL>\' ) + + + '; + + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $layer = Qgis\Layer\MapLayer::fromXmlReader($oXml); + $this->assertFalse($layer->embedded); + $this->assertInstanceOf(Qgis\Layer\VectorLayer::class, $layer); + + $this->assertNotNull($layer->constraints); + $this->assertCount(4, $layer->constraints); + + $this->assertInstanceOf(Qgis\Layer\VectorLayerConstraint::class, $layer->constraints[0]); + $this->assertEquals('pkuid', $layer->constraints[0]->field); + $this->assertEquals(3, $layer->constraints[0]->constraints); + $this->assertTrue($layer->constraints[0]->notnull_strength); + $this->assertTrue($layer->constraints[0]->unique_strength); + $this->assertFalse($layer->constraints[0]->exp_strength); + + $this->assertInstanceOf(Qgis\Layer\VectorLayerConstraint::class, $layer->constraints[1]); + $this->assertEquals('label', $layer->constraints[1]->field); + $this->assertEquals(0, $layer->constraints[1]->constraints); + $this->assertFalse($layer->constraints[1]->notnull_strength); + $this->assertFalse($layer->constraints[1]->unique_strength); + $this->assertFalse($layer->constraints[1]->exp_strength); + + $layerToKeyArray = $layer->toKeyArray(); + $this->assertArrayHasKey('constraints', $layerToKeyArray); + $this->assertArrayHasKey('pkuid', $layerToKeyArray['constraints']); + $this->assertArrayHasKey('constraints', $layerToKeyArray['constraints']['pkuid']); + $this->assertEquals(3, $layerToKeyArray['constraints']['pkuid']['constraints']); + $this->assertTrue($layerToKeyArray['constraints']['pkuid']['notNull']); + $this->assertTrue($layerToKeyArray['constraints']['pkuid']['unique']); + $this->assertFalse($layerToKeyArray['constraints']['pkuid']['exp']); + + $formControls = $layer->getFormControls(); + $this->assertNotNull($formControls); + $this->assertCount(4, $formControls); + + $this->assertInstanceOf(Form\QgisFormControlProperties::class, $formControls['pkuid']); + $this->assertEquals('pkuid', $formControls['pkuid']->getName()); + $this->assertEquals('Hidden', $formControls['pkuid']->getFieldEditType()); + $this->assertTrue($formControls['pkuid']->isEditable()); + $this->assertEquals('', $formControls['pkuid']->getFieldAlias()); + $this->assertEquals('hidden', $formControls['pkuid']->getMarkup()); + + $this->assertInstanceOf(Form\QgisFormControlProperties::class, $formControls['difficulty']); + $this->assertEquals('difficulty', $formControls['difficulty']->getName()); + $this->assertEquals('Classification', $formControls['difficulty']->getFieldEditType()); + $this->assertTrue($formControls['difficulty']->isEditable()); + $this->assertEquals('', $formControls['difficulty']->getFieldAlias()); + $this->assertEquals('menulist', $formControls['difficulty']->getMarkup()); + $categories = array( + 1 => 'easy', + 2 => 'normal', + 3 => 'difficult', + ); + $this->assertEquals($categories, $formControls['difficulty']->getRendererCategories()); + + $xmlStr = ' + + + -0.63213155735301818 + 45.70204553664066083 + 0.63937547315889365 + 46.52953423903730368 + + + -0.63213155735301818 + 45.70204553664066083 + 0.63937547315889365 + 46.52953423903730368 + + for_edition_upload_webdav_shape_caf087fb_dfd0_40c5_93a4_ac1ae5648e96 + ./form_edition_upload_webdav_shape.shp + for_edition_upload_webdav_shape + + + + form_edition_upload_webdav_shape + + + GEOGCRS["WGS 84",ENSEMBLE["World Geodetic System 1984 ensemble",MEMBER["World Geodetic System 1984 (Transit)"],MEMBER["World Geodetic System 1984 (G730)"],MEMBER["World Geodetic System 1984 (G873)"],MEMBER["World Geodetic System 1984 (G1150)"],MEMBER["World Geodetic System 1984 (G1674)"],MEMBER["World Geodetic System 1984 (G1762)"],MEMBER["World Geodetic System 1984 (G2139)"],ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]],ENSEMBLEACCURACY[2.0]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],CS[ellipsoidal,2],AXIS["geodetic latitude (Lat)",north,ORDER[1],ANGLEUNIT["degree",0.0174532925199433]],AXIS["geodetic longitude (Lon)",east,ORDER[2],ANGLEUNIT["degree",0.0174532925199433]],USAGE[SCOPE["Horizontal component of 3D system."],AREA["World."],BBOX[-90,-180,90,180]],ID["EPSG",4326]] + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat + EPSG:7030 + true + + + + + + + dataset + + + + + + + + + + + + + + + + + GEOGCRS["WGS 84",ENSEMBLE["World Geodetic System 1984 ensemble",MEMBER["World Geodetic System 1984 (Transit)"],MEMBER["World Geodetic System 1984 (G730)"],MEMBER["World Geodetic System 1984 (G873)"],MEMBER["World Geodetic System 1984 (G1150)"],MEMBER["World Geodetic System 1984 (G1674)"],MEMBER["World Geodetic System 1984 (G1762)"],MEMBER["World Geodetic System 1984 (G2139)"],ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]],ENSEMBLEACCURACY[2.0]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],CS[ellipsoidal,2],AXIS["geodetic latitude (Lat)",north,ORDER[1],ANGLEUNIT["degree",0.0174532925199433]],AXIS["geodetic longitude (Lon)",east,ORDER[2],ANGLEUNIT["degree",0.0174532925199433]],USAGE[SCOPE["Horizontal component of 3D system."],AREA["World."],BBOX[-90,-180,90,180]],ID["EPSG",4326]] + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat + EPSG:7030 + true + + + + + + + + + + + + + ogr + + + + + + + + + + + 1 + 1 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + 0 + tablayout + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "remote" + <table class="table table-condensed table-striped table-bordered lizmapPopupTable"> + <thead> + <tr> + <th>Field</th> + <th>Value</th> + </tr> + </thead> + <tbody> + <tr> + <th>id</th> + <td>[% "id" %]</td> + </tr> + <tr> + <th>remote</th> + <td>[% "remote" %]</td> + </tr> + <tr> + <th>local</th> + <td>[% "local" %]</td> + </tr> + + </tbody> +</table> + + '; + + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $layer = Qgis\Layer\MapLayer::fromXmlReader($oXml); + $this->assertFalse($layer->embedded); + $this->assertInstanceOf(Qgis\Layer\VectorLayer::class, $layer); + + $layerToKeyArray = $layer->toKeyArray(); + $this->assertArrayHasKey('webDavFields', $layerToKeyArray); + $this->assertCount(1, $layerToKeyArray['webDavFields']); + $this->assertEquals(array('remote'), $layerToKeyArray['webDavFields']); + $this->assertArrayHasKey('webDavBaseUris', $layerToKeyArray); + $this->assertCount(1, $layerToKeyArray['webDavBaseUris']); + $this->assertEquals(array("'http://webdav/shapeData/'||file_name(@selected_file_path)"), $layerToKeyArray['webDavBaseUris']); + + $formControls = $layer->getFormControls(); + $this->assertNotNull($formControls); + $this->assertCount(3, $formControls); + + $this->assertInstanceOf(Form\QgisFormControlProperties::class, $formControls['id']); + $this->assertEquals('id', $formControls['id']->getName()); + $this->assertEquals('TextEdit', $formControls['id']->getFieldEditType()); + $this->assertTrue($formControls['id']->isEditable()); + $this->assertEquals('', $formControls['id']->getFieldAlias()); + $this->assertEquals('input', $formControls['id']->getMarkup()); + + $this->assertInstanceOf(Form\QgisFormControlProperties::class, $formControls['remote']); + $this->assertEquals('remote', $formControls['remote']->getName()); + $this->assertEquals('ExternalResource', $formControls['remote']->getFieldEditType()); + $this->assertTrue($formControls['remote']->isEditable()); + $this->assertEquals('', $formControls['remote']->getFieldAlias()); + $this->assertEquals('upload', $formControls['remote']->getMarkup()); + + $this->assertTrue($formControls['remote']->isImageUpload()); + $this->assertEquals( + array('image/jpg', 'image/jpeg', 'image/pjpeg', 'image/png', 'image/gif'), + $formControls['remote']->getMimeTypes() + ); + $this->assertEquals('image/jpg, image/jpeg, image/pjpeg, image/png, image/gif', $formControls['remote']->getUploadAccept()); + $this->assertEquals('environment', $formControls['remote']->getUploadCapture()); + $this->assertEquals('', $formControls['remote']->getEditAttribute('DefaultRoot')); + $this->assertEquals('WebDAV', $formControls['remote']->getEditAttribute('StorageType')); + $this->assertEquals("'http://webdav/shapeData/'||file_name(@selected_file_path)", $formControls['remote']->getEditAttribute('webDAVStorageUrl')); + $propertyCollection = $formControls['remote']->getEditAttribute('PropertyCollection'); + $this->assertTrue(isset($propertyCollection)); + $this->assertTrue(isset($propertyCollection['properties'])); + $this->assertFalse(isset($propertyCollection['properties']['propertyRootPath'])); + + $this->assertInstanceOf(Form\QgisFormControlProperties::class, $formControls['local']); + $this->assertEquals('local', $formControls['local']->getName()); + $this->assertEquals('ExternalResource', $formControls['local']->getFieldEditType()); + $this->assertTrue($formControls['local']->isEditable()); + $this->assertEquals('', $formControls['local']->getFieldAlias()); + $this->assertEquals('upload', $formControls['local']->getMarkup()); + + $this->assertFalse($formControls['local']->isImageUpload()); + $this->assertEquals( + array(), + $formControls['local']->getMimeTypes() + ); + $this->assertEquals('', $formControls['local']->getUploadAccept()); + $this->assertEquals('', $formControls['local']->getUploadCapture()); + $this->assertEquals('', $formControls['local']->getEditAttribute('DefaultRoot')); + $this->assertEquals('', $formControls['local']->getEditAttribute('StorageType')); + } + + public function testToKeyArray() { + $xmlStr = ' + + + 695509.79609184700530022 + 6321556.88189174793660641 + 695509.79609184700530022 + 6321556.88189174793660641 + + + 2.94402361346026709 + 43.99299944327356116 + 2.94402361346026709 + 43.99299944327356116 + + table_for_form_8a6a46b7_21ef_47d6_a5cd_134f6e84dace + service=\'lizmapdb\' sslmode=disable key=\'gid\' estimatedmetadata=true srid=2154 type=Point checkPrimaryKeyUnicity=\'1\' table="tests_projects"."table_for_form" (geom) + table_for_form + + + + table_for_form + + + PROJCRS["RGF93 v1 / Lambert-93",BASEGEOGCRS["RGF93 v1",DATUM["Reseau Geodesique Francais 1993 v1",ELLIPSOID["GRS 1980",6378137,298.257222101,LENGTHUNIT["metre",1]]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],ID["EPSG",4171]],CONVERSION["Lambert-93",METHOD["Lambert Conic Conformal (2SP)",ID["EPSG",9802]],PARAMETER["Latitude of false origin",46.5,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8821]],PARAMETER["Longitude of false origin",3,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8822]],PARAMETER["Latitude of 1st standard parallel",49,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8823]],PARAMETER["Latitude of 2nd standard parallel",44,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8824]],PARAMETER["Easting at false origin",700000,LENGTHUNIT["metre",1],ID["EPSG",8826]],PARAMETER["Northing at false origin",6600000,LENGTHUNIT["metre",1],ID["EPSG",8827]]],CS[Cartesian,2],AXIS["easting (X)",east,ORDER[1],LENGTHUNIT["metre",1]],AXIS["northing (Y)",north,ORDER[2],LENGTHUNIT["metre",1]],USAGE[SCOPE["Engineering survey, topographic mapping."],AREA["France - onshore and offshore, mainland and Corsica."],BBOX[41.15,-9.86,51.56,10.38]],ID["EPSG",2154]] + +proj=lcc +lat_0=46.5 +lon_0=3 +lat_1=49 +lat_2=44 +x_0=700000 +y_0=6600000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs + 145 + 2154 + EPSG:2154 + RGF93 v1 / Lambert-93 + lcc + EPSG:7019 + false + + + + + + + dataset + + + + + + + + + + + + + + + + + PROJCRS["RGF93 v1 / Lambert-93",BASEGEOGCRS["RGF93 v1",DATUM["Reseau Geodesique Francais 1993 v1",ELLIPSOID["GRS 1980",6378137,298.257222101,LENGTHUNIT["metre",1]]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],ID["EPSG",4171]],CONVERSION["Lambert-93",METHOD["Lambert Conic Conformal (2SP)",ID["EPSG",9802]],PARAMETER["Latitude of false origin",46.5,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8821]],PARAMETER["Longitude of false origin",3,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8822]],PARAMETER["Latitude of 1st standard parallel",49,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8823]],PARAMETER["Latitude of 2nd standard parallel",44,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8824]],PARAMETER["Easting at false origin",700000,LENGTHUNIT["metre",1],ID["EPSG",8826]],PARAMETER["Northing at false origin",6600000,LENGTHUNIT["metre",1],ID["EPSG",8827]]],CS[Cartesian,2],AXIS["easting (X)",east,ORDER[1],LENGTHUNIT["metre",1]],AXIS["northing (Y)",north,ORDER[2],LENGTHUNIT["metre",1]],USAGE[SCOPE["Engineering survey, topographic mapping."],AREA["France - onshore and offshore, mainland and Corsica."],BBOX[41.15,-9.86,51.56,10.38]],ID["EPSG",2154]] + +proj=lcc +lat_0=46.5 +lon_0=3 +lat_1=49 +lat_2=44 +x_0=700000 +y_0=6600000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs + 145 + 2154 + EPSG:2154 + RGF93 v1 / Lambert-93 + lcc + EPSG:7019 + false + + + + + + + + + + + + + postgres + + + + + + + + + + + 1 + 1 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + 0 + generatedlayout + + + + + + + + + + + + + + + + + + + + "gid" + + + '; + + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $layer = Qgis\Layer\MapLayer::fromXmlReader($oXml); + + $this->assertNotNull($layer->constraints); + $this->assertCount(5, $layer->constraints); + + $this->assertInstanceOf(Qgis\Layer\VectorLayerConstraint::class, $layer->constraints[3]); + $this->assertEquals('test_not_null_only', $layer->constraints[3]->field); + $this->assertEquals(1, $layer->constraints[3]->constraints); + $this->assertTrue($layer->constraints[3]->notnull_strength); + $this->assertFalse($layer->constraints[3]->unique_strength); + $this->assertFalse($layer->constraints[3]->exp_strength); + + $layerToKeyArray = $layer->toKeyArray(); + $this->assertArrayHasKey('constraints', $layerToKeyArray); + $this->assertArrayHasKey('test_not_null_only', $layerToKeyArray['constraints']); + $this->assertArrayHasKey('constraints', $layerToKeyArray['constraints']['test_not_null_only']); + $this->assertEquals(1, $layerToKeyArray['constraints']['test_not_null_only']['constraints']); + $this->assertTrue($layerToKeyArray['constraints']['test_not_null_only']['notNull']); + $this->assertFalse($layerToKeyArray['constraints']['test_not_null_only']['unique']); + $this->assertFalse($layerToKeyArray['constraints']['test_not_null_only']['exp']); + + $this->assertArrayHasKey('type', $layerToKeyArray); + $this->assertEquals('vector', $layerToKeyArray['type']); + + $this->assertArrayHasKey('id', $layerToKeyArray); + $this->assertEquals('table_for_form_8a6a46b7_21ef_47d6_a5cd_134f6e84dace', $layerToKeyArray['id']); + + $this->assertArrayHasKey('name', $layerToKeyArray); + $this->assertEquals('table_for_form', $layerToKeyArray['name']); + + $this->assertArrayHasKey('shortname', $layerToKeyArray); + $this->assertEquals('table_for_form', $layerToKeyArray['shortname']); + + $this->assertArrayHasKey('title', $layerToKeyArray); + $this->assertEquals('table_for_form', $layerToKeyArray['title']); + + $this->assertArrayHasKey('abstract', $layerToKeyArray); + $this->assertEquals('', $layerToKeyArray['abstract']); + + $this->assertArrayHasKey('proj4', $layerToKeyArray); + $this->assertEquals('+proj=lcc +lat_1=49 +lat_2=44 +lat_0=46.5 +lon_0=3 +x_0=700000 +y_0=6600000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs', $layerToKeyArray['proj4']); + + $this->assertArrayHasKey('srid', $layerToKeyArray); + $this->assertEquals(2154, $layerToKeyArray['srid']); + + $this->assertArrayHasKey('authid', $layerToKeyArray); + $this->assertEquals('EPSG:2154', $layerToKeyArray['authid']); + + $this->assertArrayHasKey('datasource', $layerToKeyArray); + $this->assertEquals("service='lizmapdb' sslmode=disable key='gid' estimatedmetadata=true srid=2154 type=Point checkPrimaryKeyUnicity='1' table=\"tests_projects\".\"table_for_form\" (geom)", $layerToKeyArray['datasource']); + + $this->assertArrayHasKey('provider', $layerToKeyArray); + $this->assertEquals('postgres', $layerToKeyArray['provider']); + + $this->assertArrayHasKey('keywords', $layerToKeyArray); + $this->assertCount(1, $layerToKeyArray['keywords']); + $this->assertEquals(array(''), $layerToKeyArray['keywords']); + + $this->assertArrayHasKey('fields', $layerToKeyArray); + $this->assertCount(5, $layerToKeyArray['fields']); + $this->assertEquals(array('gid', 'titre', 'test', 'test_not_null_only', 'test_empty_value_only'), $layerToKeyArray['fields']); + + $this->assertArrayHasKey('aliases', $layerToKeyArray); + $this->assertCount(5, $layerToKeyArray['aliases']); + $this->assertEquals( + array( + 'gid' => '', + 'titre' => '', + 'test' => '', + 'test_not_null_only' => 'Test constraint not null only', + 'test_empty_value_only' => 'Test with empty value only' + ), + $layerToKeyArray['aliases']); + + $this->assertArrayHasKey('defaults', $layerToKeyArray); + $this->assertCount(5, $layerToKeyArray['defaults']); + $this->assertEquals( + array( + 'gid' => '', + 'titre' => '', + 'test' => '', + 'test_not_null_only' => '', + 'test_empty_value_only' => '' + ), + $layerToKeyArray['defaults'] + ); + + $this->assertArrayHasKey('wfsFields', $layerToKeyArray); + $this->assertCount(5, $layerToKeyArray['wfsFields']); + $this->assertEquals(array('gid', 'titre', 'test', 'test_not_null_only', 'test_empty_value_only'), $layerToKeyArray['wfsFields']); + + $this->assertArrayHasKey('webDavFields', $layerToKeyArray); + $this->assertCount(0, $layerToKeyArray['webDavFields']); + $this->assertEquals(array(), $layerToKeyArray['webDavFields']); + + $this->assertArrayHasKey('webDavBaseUris', $layerToKeyArray); + $this->assertCount(0, $layerToKeyArray['webDavBaseUris']); + $this->assertEquals(array(), $layerToKeyArray['webDavBaseUris']); + } + + public function testToKeyArrayConstraintExpressions() + { + $xmlStr = ' + + form_advanced_point_0805ae82_fa78_4e67_a0cf_4ff25c4728b5 + service=\'lizmapdb\' sslmode=disable key=\'id\' estimatedmetadata=true srid=2154 type=Point checkPrimaryKeyUnicity=\'1\' table="tests_projects"."form_advanced_point" (geom) + form_advanced_point + + + + form_advanced_point + + + PROJCRS["RGF93 v1 / Lambert-93",BASEGEOGCRS["RGF93 v1",DATUM["Reseau Geodesique Francais 1993 v1",ELLIPSOID["GRS 1980",6378137,298.257222101,LENGTHUNIT["metre",1]]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],ID["EPSG",4171]],CONVERSION["Lambert-93",METHOD["Lambert Conic Conformal (2SP)",ID["EPSG",9802]],PARAMETER["Latitude of false origin",46.5,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8821]],PARAMETER["Longitude of false origin",3,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8822]],PARAMETER["Latitude of 1st standard parallel",49,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8823]],PARAMETER["Latitude of 2nd standard parallel",44,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8824]],PARAMETER["Easting at false origin",700000,LENGTHUNIT["metre",1],ID["EPSG",8826]],PARAMETER["Northing at false origin",6600000,LENGTHUNIT["metre",1],ID["EPSG",8827]]],CS[Cartesian,2],AXIS["easting (X)",east,ORDER[1],LENGTHUNIT["metre",1]],AXIS["northing (Y)",north,ORDER[2],LENGTHUNIT["metre",1]],USAGE[SCOPE["Engineering survey, topographic mapping."],AREA["France - onshore and offshore, mainland and Corsica."],BBOX[41.15,-9.86,51.56,10.38]],ID["EPSG",2154]] + +proj=lcc +lat_0=46.5 +lon_0=3 +lat_1=49 +lat_2=44 +x_0=700000 +y_0=6600000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs + 145 + 2154 + EPSG:2154 + RGF93 v1 / Lambert-93 + lcc + EPSG:7019 + false + + + + + + + dataset + + + + + + + + + + + + + + + + + PROJCRS["RGF93 v1 / Lambert-93",BASEGEOGCRS["RGF93 v1",DATUM["Reseau Geodesique Francais 1993 v1",ELLIPSOID["GRS 1980",6378137,298.257222101,LENGTHUNIT["metre",1]]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],ID["EPSG",4171]],CONVERSION["Lambert-93",METHOD["Lambert Conic Conformal (2SP)",ID["EPSG",9802]],PARAMETER["Latitude of false origin",46.5,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8821]],PARAMETER["Longitude of false origin",3,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8822]],PARAMETER["Latitude of 1st standard parallel",49,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8823]],PARAMETER["Latitude of 2nd standard parallel",44,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8824]],PARAMETER["Easting at false origin",700000,LENGTHUNIT["metre",1],ID["EPSG",8826]],PARAMETER["Northing at false origin",6600000,LENGTHUNIT["metre",1],ID["EPSG",8827]]],CS[Cartesian,2],AXIS["easting (X)",east,ORDER[1],LENGTHUNIT["metre",1]],AXIS["northing (Y)",north,ORDER[2],LENGTHUNIT["metre",1]],USAGE[SCOPE["Engineering survey, topographic mapping."],AREA["France - onshore and offshore, mainland and Corsica."],BBOX[41.15,-9.86,51.56,10.38]],ID["EPSG",2154]] + +proj=lcc +lat_0=46.5 +lon_0=3 +lat_1=49 +lat_2=44 +x_0=700000 +y_0=6600000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs + 145 + 2154 + EPSG:2154 + RGF93 v1 / Lambert-93 + lcc + EPSG:7019 + false + + + + + + + + + + + + + postgres + + + + + + + + + + + 1 + 1 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + # -*- coding: utf-8 -*- +""" +Les formulaires QGIS peuvent avoir une fonction Python qui sera appelée à l\'ouverture du formulaire. + +Utilisez cette fonction pour ajouter plus de fonctionnalités à vos formulaires. + +Entrez le nom de la fonction dans le champ "Fonction d\'initialisation Python". +Voici un exemple à suivre: +""" +from qgis.PyQt.QtWidgets import QWidget + +def my_form_open(dialog, layer, feature): +geom = feature.geometry() +control = dialog.findChild(QWidget, "MyLineEdit") + + + 0 + tablayout + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "id" + + + '; + + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $layer = Qgis\Layer\MapLayer::fromXmlReader($oXml); + + $this->assertNotNull($layer->constraints); + $this->assertCount(5, $layer->constraints); + + $this->assertNotNull($layer->constraintExpressions); + $this->assertCount(5, $layer->constraintExpressions); + + $this->assertInstanceOf(Qgis\Layer\VectorLayerConstraintExpression::class, $layer->constraintExpressions[2]); + $this->assertEquals('website', $layer->constraintExpressions[2]->field); + $this->assertEquals('left( "website", 4) = \'http\'', $layer->constraintExpressions[2]->exp); + $this->assertEquals('Web site URL must start with \'http\'', $layer->constraintExpressions[2]->desc); + + $layerToKeyArray = $layer->toKeyArray(); + $this->assertArrayHasKey('constraints', $layerToKeyArray); + $this->assertArrayHasKey('website', $layerToKeyArray['constraints']); + $this->assertArrayHasKey('constraints', $layerToKeyArray['constraints']['website']); + $this->assertEquals(4, $layerToKeyArray['constraints']['website']['constraints']); + $this->assertFalse($layerToKeyArray['constraints']['website']['notNull']); + $this->assertFalse($layerToKeyArray['constraints']['website']['unique']); + $this->assertTrue($layerToKeyArray['constraints']['website']['exp']); + $this->assertEquals('left( "website", 4) = \'http\'', $layerToKeyArray['constraints']['website']['exp_value']); + $this->assertEquals('Web site URL must start with \'http\'', $layerToKeyArray['constraints']['website']['exp_desc']); + } +} From dc9a9d55a32ae407542e72d0f3e11b40ef99e9fc Mon Sep 17 00:00:00 2001 From: rldhont Date: Fri, 22 Mar 2024 13:38:59 +0100 Subject: [PATCH 19/46] New Lizmap\Project\Qgis\ProjectInfo for QGIS Project Info --- .../lizmap/lib/Project/Qgis/ProjectInfo.php | 621 ++++++++++++++++++ .../classes/Project/Qgis/ProjectInfoTest.php | 258 ++++++++ 2 files changed, 879 insertions(+) create mode 100644 lizmap/modules/lizmap/lib/Project/Qgis/ProjectInfo.php create mode 100644 tests/units/classes/Project/Qgis/ProjectInfoTest.php diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/ProjectInfo.php b/lizmap/modules/lizmap/lib/Project/Qgis/ProjectInfo.php new file mode 100644 index 0000000000..808cf8fa35 --- /dev/null +++ b/lizmap/modules/lizmap/lib/Project/Qgis/ProjectInfo.php @@ -0,0 +1,621 @@ + $visibilityPresets + * @property array $relations + * @property array $projectlayers + * @property array $Layouts + */ +class ProjectInfo extends BaseQgisXmlObject +{ + /** @var array The instances created with ProjectInfo::fromQgisPath */ + protected static $instances = array(); + + /** + * Get a QGIS Project info instance from a QGIS Project path. + * + * @param string $qgisProjectPath A QGIS Project path + * + * @return ProjectInfo the QGIS project info instance corresponding to the path + */ + public static function fromQgisPath($qgisProjectPath) + { + if (!file_exists($qgisProjectPath)) { + throw new \Exception('The QGIS project '.basename($qgisProjectPath).' does not exist!'); + } + + $xmlFilePath = realpath($qgisProjectPath); + if (array_key_exists($xmlFilePath, static::$instances)) { + return static::$instances[$xmlFilePath]; + } + $xmlReader = App\XmlTools::xmlReaderFromFile($qgisProjectPath); + + /** @var ProjectInfo $project */ + $project = static::fromXmlReader($xmlReader); + static::$instances[$xmlFilePath] = $project; + + return $project; + } + + /** + * Get the QGIS project path if it has been created with ProjectInfo::fromQgisPath. + * + * @param ProjectInfo $qgisProject A QGIS Project info created with ProjectInfo::fromQgisPath + * + * @return null|string + */ + public static function getQgisPath($qgisProject) + { + foreach (static::$instances as $path => $project) { + if ($project === $qgisProject) { + return $path; + } + } + + return null; + } + + /** @var string The QGIS project path */ + protected $path; + + protected $properties = array( + 'version', + 'projectname', + 'saveDateTime', + 'title', + 'projectCrs', + 'properties', + 'layerTreeRoot', + 'visibilityPresets', + 'relations', + 'projectlayers', + 'Layouts', + ); + + protected $mandatoryProperties = array( + 'version', + 'projectname', + 'title', + 'projectCrs', + 'properties', + 'layerTreeRoot', + 'visibilityPresets', + 'relations', + 'projectlayers', + 'Layouts', + ); + + /** @var array The default values for properties */ + protected $defaultValues = array( + 'visibilityPresets' => array(), + 'relations' => array(), + 'projectlayers' => array(), + 'Layouts' => array(), + ); + + protected static $children = array( + 'title', + 'projectCrs', + 'properties', + ); + + protected static $mandatoryChildren = array( + 'title', + 'projectCrs', + 'properties', + ); + + /** + * Get the project path if it has been created with ProjectInfo::fromQgisPath. + * + * @return null|string + */ + public function getPath() + { + if ($this->path === null) { + $this->path = ProjectInfo::getQgisPath($this); + } + + return $this->path; + } + + /** + * Get the vWMS informations as key array. + * + * @return array + */ + public function getWmsInformationsAsKeyArray() + { + $prop = $this->__get('properties'); + + return array( + 'WMSServiceTitle' => $prop->WMSServiceTitle, + 'WMSServiceAbstract' => $prop->WMSServiceAbstract, + 'WMSKeywordList' => is_array($prop->WMSKeywordList) ? implode(', ', $prop->WMSKeywordList) : '', + 'WMSExtent' => is_array($prop->WMSExtent) ? implode(', ', $prop->WMSExtent) : '', + 'ProjectCrs' => $this->projectCrs->authid, + 'WMSOnlineResource' => $prop->WMSOnlineResource, + 'WMSContactMail' => $prop->WMSContactMail, + 'WMSContactOrganization' => $prop->WMSContactOrganization, + 'WMSContactPerson' => $prop->WMSContactPerson, + 'WMSContactPhone' => $prop->WMSContactPhone, + ); + } + + /** + * Get the visibility presets as key array. + * + * @return array + */ + public function getVisibilityPresetsAsKeyArray() + { + $data = array(); + foreach ($this->visibilityPresets as $visibilityPreset) { + $data[$visibilityPreset->name] = $visibilityPreset->toKeyArray(); + } + + return $data; + } + + /** + * Get proj as key array. + * + * @return array + */ + public function getProjAsKeyArray() + { + $data = array(); + $data[$this->projectCrs->authid] = $this->projectCrs->proj4; + foreach ($this->projectlayers as $layer) { + if ($layer->embedded) { + continue; + } + $data[$layer->srs->authid] = $layer->srs->proj4; + } + + return $data; + } + + /** + * Get layer by Id. + * + * @param string $layerId The layer id + * + * @return null|Layer\MapLayer|Layer\VectorLayer + */ + public function getLayerById($layerId) + { + foreach ($this->projectlayers as $layer) { + if ($layer->id !== $layerId) { + continue; + } + if ($layer->embedded) { + /** @var Project\Qgis\Layer\EmbeddedLayer $layer */ + $embeddedPath = realpath(dirname($this->getPath()).DIRECTORY_SEPARATOR.$layer->project); + $embeddedProject = ProjectInfo::fromQgisPath($embeddedPath); + foreach ($embeddedProject->projectlayers as $embeddedLayer) { + if ($embeddedLayer->id !== $layer->id) { + continue; + } + + return $embeddedLayer; + } + } + + return $layer; + } + + return null; + } + + /** + * Get layers as key array. + * + * @return array + */ + public function getLayersAsKeyArray() + { + $data = array(); + foreach ($this->projectlayers as $layer) { + if ($layer->embedded) { + /** @var Project\Qgis\Layer\EmbeddedLayer $layer */ + $embeddedLayer = $this->getLayerById($layer->id); + $embeddedPath = realpath(dirname($this->getPath()).DIRECTORY_SEPARATOR.$layer->project); + + $layerKeyArray = $embeddedLayer->toKeyArray(); + $layerKeyArray['qgsmtime'] = filemtime($embeddedPath); + $layerKeyArray['file'] = $embeddedPath; + $layerKeyArray['embedded'] = $layer->embedded; + $layerKeyArray['projectPath'] = $layer->project; + $layerKeyArray[] = $layer->toKeyArray(); + $data[] = $layerKeyArray; + + continue; + } + $data[] = $layer->toKeyArray(); + } + + return $data; + } + + /** + * Get relations as key array. + * + * @return array + */ + public function getRelationsAsKeyArray() + { + $data = array(); + $pivotGather = array(); + $pivot = array(); + foreach ($this->relations as $relation) { + // Get referenced layer + $referencedLayer = $this->getLayerById($relation->referencedLayer); + + // Build relations key array + if (!array_key_exists($relation->referencedLayer, $data)) { + $data[$relation->referencedLayer] = array(); + } + $previewField = $referencedLayer->getPreviewField(); + $data[$relation->referencedLayer][] = array( + 'referencingLayer' => $relation->referencingLayer, + 'referencedField' => $relation->referencedField, + 'referencingField' => $relation->referencingField, + 'previewField' => $previewField !== '' ? $previewField : $relation->referencedField, + 'relationName' => $relation->name, + 'relationId' => $relation->id, + ); + + // Collect pivot informations + if (!array_key_exists($relation->referencingLayer, $pivotGather)) { + $pivotGather[$relation->referencingLayer] = array(); + } + $pivotGather[$relation->referencingLayer][$relation->referencedLayer] = $relation->referencingField; + } + + // Keep only child with at least two parents + foreach ($pivotGather as $pi => $vo) { + if (count($vo) > 1) { + $pivot[$pi] = $vo; + } + } + $data['pivot'] = $pivot; + + return $data; + } + + /** + * Get relations as key array. + * + * @return array + */ + public function getRelationFieldsAsKeyArray() + { + $data = array(); + foreach ($this->relations as $relation) { + // Get referenced layer + $referencedLayer = $this->getLayerById($relation->referencedLayer); + $previewField = $referencedLayer->getPreviewField(); + $data[] = array( + 'id' => $relation->id, + 'layerName' => $referencedLayer->layername, + 'typeName' => $referencedLayer->shortname !== null ? $referencedLayer->shortname : str_replace(' ', '_', $referencedLayer->layername), + 'propertyName' => $previewField !== '' ? $relation->referencedField.','.$previewField : $relation->referencedField, + 'filterExpression' => '', + 'referencedField' => $relation->referencedField, + 'referencingField' => $relation->referencingField, + 'previewField' => $previewField, + ); + } + + return $data; + } + + /** + * Get layouts as key array. + * + * @return array + */ + public function getLayoutsAsKeyArray() + { + $data = array(); + // get restricted composers + $rComposers = array(); + if ($this->__get('properties')->WMSRestrictedComposers !== null) { + $rComposers = $this->__get('properties')->WMSRestrictedComposers; + } + foreach ($this->Layouts as $layout) { + // test restriction + if (in_array($layout->name, $rComposers)) { + continue; + } + // get page element + if (!$layout->PageCollection) { + continue; + } + $page = $layout->PageCollection[0]; + + // init print template element + $printTemplate = array( + 'title' => $layout->name, + 'width' => $page->width, + 'height' => $page->height, + 'maps' => array(), + 'labels' => array(), + ); + + // store mapping between uuid and id + $mapUuidId = array(); + foreach ($layout->Items as $item) { + if ($item->type == 65639) { + // Build map + $map = array( + 'id' => 'map'.(string) count($printTemplate['maps']), + 'uuid' => $item->uuid, + 'width' => $item->width, + 'height' => $item->height, + 'grid' => $item->grid, + 'overviewMap' => $item->overviewMap, + ); + + // store mapping between uuid and id + $mapUuidId[$map['uuid']] = $map['id']; + + // store map info + $printTemplate['maps'][] = $map; + } elseif ($item->type == 65641) { + // Check the label item has an id + // if not continue + if ($item->id == '') { + continue; + } + + // store label info + $printTemplate['labels'][] = array( + 'id' => $item->id, + 'htmlState' => $item->htmlState, + 'text' => $item->text, + ); + } + } + // Modifying overviewMap to id instead of uuid and remove null + foreach ($printTemplate['maps'] as $ptMap) { + if (!array_key_exists($ptMap['overviewMap'], $mapUuidId)) { + unset($ptMap['overviewMap']); + + continue; + } + $ptMap['overviewMap'] = $mapUuidId[$ptMap['overviewMap']]; + } + + // Atlas + if ($layout->Atlas) { + $printTemplate['atlas'] = $layout->Atlas; + } + + $data[] = $printTemplate; + } + + return $data; + } + + /** @var string The XML element local name */ + protected static $qgisLocalName = 'qgis'; + + /** + * Get attributes from an XMLReader instance at an element. + * + * @param \XMLReader $oXmlReader An XMLReader instance at an element + * + * @return array{'version': string, 'projectname': string} the element attributes as keys / values + */ + protected static function getAttributes($oXmlReader) + { + return array( + 'version' => $oXmlReader->getAttribute('version'), + 'projectname' => $oXmlReader->getAttribute('projectname'), + 'saveDateTime' => $oXmlReader->getAttribute('saveDateTime'), + ); + } + + protected static $childParsers = array(); + + /** + * Build an instance with data as an array. + * + * @param array $data the instance data + * + * @return ProjectInfo the instance + */ + protected static function buildInstance($data) + { + if (array_key_exists('layer-tree-group', $data)) { + $data['layerTreeRoot'] = $data['layer-tree-group']; + unset($data['layer-tree-group']); + } + if (array_key_exists('visibility-presets', $data)) { + $data['visibilityPresets'] = $data['visibility-presets']; + unset($data['visibility-presets']); + } + + return new ProjectInfo($data); + } +} + +ProjectInfo::registerChildParser('title', function ($oXmlReader) { + return $oXmlReader->readString(); +}); +ProjectInfo::registerChildParser('projectCrs', function ($oXmlReader) { + $depth = $oXmlReader->depth; + $localName = $oXmlReader->localName; + if ($oXmlReader->isEmptyElement) { + return null; + } + while ($oXmlReader->read()) { + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'spatialrefsys') { + break; + } + } + + return SpatialRefSys::fromXmlReader($oXmlReader); +}); +ProjectInfo::registerChildParser('properties', function ($oXmlReader) { + return ProjectProperties::fromXmlReader($oXmlReader); +}); +ProjectInfo::registerChildParser('layer-tree-group', function ($oXmlReader) { + return LayerTreeRoot::fromXmlReader($oXmlReader); +}); +ProjectInfo::registerChildParser('visibility-presets', function ($oXmlReader) { + $depth = $oXmlReader->depth; + $localName = $oXmlReader->localName; + $data = array(); + if ($oXmlReader->isEmptyElement) { + return $data; + } + while ($oXmlReader->read()) { + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'visibility-preset') { + $data[] = ProjectVisibilityPreset::fromXmlReader($oXmlReader); + } + } + + return $data; +}); +ProjectInfo::registerChildParser('relations', function ($oXmlReader) { + $depth = $oXmlReader->depth; + $localName = $oXmlReader->localName; + $data = array(); + if ($oXmlReader->isEmptyElement) { + return $data; + } + while ($oXmlReader->read()) { + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'relation') { + $data[] = ProjectRelation::fromXmlReader($oXmlReader); + } + } + + return $data; +}); +ProjectInfo::registerChildParser('projectlayers', function ($oXmlReader) { + $depth = $oXmlReader->depth; + $localName = $oXmlReader->localName; + $data = array(); + if ($oXmlReader->isEmptyElement) { + return $data; + } + while ($oXmlReader->read()) { + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'maplayer') { + $data[] = Layer\MapLayer::fromXmlReader($oXmlReader); + } + } + + return $data; +}); +ProjectInfo::registerChildParser('Layouts', function ($oXmlReader) { + $depth = $oXmlReader->depth; + $localName = $oXmlReader->localName; + $data = array(); + if ($oXmlReader->isEmptyElement) { + return $data; + } + while ($oXmlReader->read()) { + if ($oXmlReader->nodeType == \XMLReader::END_ELEMENT + && $oXmlReader->localName == $localName + && $oXmlReader->depth == $depth) { + break; + } + + if ($oXmlReader->nodeType != \XMLReader::ELEMENT) { + continue; + } + + if ($oXmlReader->depth != $depth + 1) { + continue; + } + + if ($oXmlReader->localName == 'Layout') { + $data[] = Layout\Layout::fromXmlReader($oXmlReader); + } + } + + return $data; +}); diff --git a/tests/units/classes/Project/Qgis/ProjectInfoTest.php b/tests/units/classes/Project/Qgis/ProjectInfoTest.php new file mode 100644 index 0000000000..87b541ae66 --- /dev/null +++ b/tests/units/classes/Project/Qgis/ProjectInfoTest.php @@ -0,0 +1,258 @@ + '3.10.5-A Coruña', + 'projectname' => 'Montpellier - Transports', + 'saveDateTime' => '', + 'title' => 'Montpellier - Transports', + ); + foreach($data as $prop => $value) { + $this->assertEquals($value, $project->$prop, $prop); + } + + $this->assertNotNull($project->properties); + $data = array( + 'WMSServiceTitle' => 'Montpellier - Transports', + 'WMSServiceAbstract' => 'Demo project with bus and tramway lines in Montpellier, France. +Data is licensed under ODbl, OpenStreetMap contributors', + 'WMSKeywordList' => array(''), + 'WMSExtent' => array('417006.61373760335845873', '5394910.34090302512049675', '447158.04891100589884445', '5414844.99480544030666351'), + 'WMSOnlineResource' => 'http://www.3liz.com/lizmap.html', + 'WMSContactMail' => 'info@3liz.com', + 'WMSContactOrganization' => '3liz', + 'WMSContactPerson' => '3liz', + 'WMSContactPhone' => '+334 67 16 64 51', + 'WMSRestrictedComposers' => array('Composeur1'), + 'WMSRestrictedLayers' => array(), + 'WMSUseLayerIDs' => false, + ); + foreach($data as $prop => $value) { + $this->assertEquals($value, $project->properties->$prop, $prop); + } + + $this->assertNotNull($project->projectCrs); + $data = array( + 'proj4' => '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs', + 'srid' => 0, + 'authid' => 'USER:100000', + 'description' => ' * SCR généré (+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs)', + ); + foreach($data as $prop => $value) { + $this->assertEquals($value, $project->projectCrs->$prop, $prop); + } + + $this->assertNotNull($project->layerTreeRoot); + $this->assertInstanceOf(Qgis\LayerTreeRoot::class, $project->layerTreeRoot); + $this->assertNotNull($project->layerTreeRoot->customOrder); + $this->assertInstanceOf(Qgis\LayerTreeCustomOrder::class, $project->layerTreeRoot->customOrder); + $this->assertFalse($project->layerTreeRoot->customOrder->enabled); + + $this->assertNotNull($project->visibilityPresets); + $this->assertTrue(is_array($project->visibilityPresets)); + $this->assertCount(3, $project->visibilityPresets); + $this->assertInstanceOf(Qgis\ProjectVisibilityPreset::class, $project->visibilityPresets[0]); + $this->assertCount(4, $project->visibilityPresets[0]->layers); + $this->assertInstanceOf(Qgis\ProjectVisibilityPresetLayer::class, $project->visibilityPresets[0]->layers[0]); + $data = array( + 'id' => 'SousQuartiers20160121124316563', + 'visible' => True, + 'style' => 'default', + 'expanded' => True, + ); + foreach($data as $prop => $value) { + $this->assertEquals($value, $project->visibilityPresets[0]->layers[0]->$prop, $prop); + } + + $this->assertNotNull($project->projectlayers); + $this->assertTrue(is_array($project->projectlayers)); + $this->assertCount(18, $project->projectlayers); + + $expectedWmsInformations = array( + 'WMSServiceTitle' => $project->properties->WMSServiceTitle, + 'WMSServiceAbstract' => $project->properties->WMSServiceAbstract, + 'WMSKeywordList' => '', + 'WMSExtent' => implode(', ', $project->properties->WMSExtent), + 'ProjectCrs' => $project->projectCrs->authid, + 'WMSOnlineResource' => $project->properties->WMSOnlineResource, + 'WMSContactMail' => $project->properties->WMSContactMail, + 'WMSContactOrganization' => $project->properties->WMSContactOrganization, + 'WMSContactPerson' => $project->properties->WMSContactPerson, + 'WMSContactPhone' => $project->properties->WMSContactPhone, + ); + $this->assertEquals($expectedWmsInformations, $project->getWmsInformationsAsKeyArray()); + + $layers = $project->getLayersAsKeyArray(); + $this->assertTrue(is_array($layers)); + $this->assertCount(18, $layers); + + $this->assertNotNull($project->relations); + $this->assertTrue(is_array($project->relations)); + $this->assertCount(7, $project->relations); + $this->assertInstanceOf(Qgis\ProjectRelation::class, $project->relations[0]); + + $relations = $project->getRelationsAsKeyArray(); + $this->assertNotNull($relations); + $this->assertTrue(is_array($relations)); + $this->assertCount(6, $relations); // 5 layers + pivot + $expectedRelationsKeys = array( + 'VilleMTP_MTP_Quartiers_2011_432620130116112610876', + 'tramstop20150328114203878', + 'tramway20150328114206278', + 'publicbuildings20150420100958543', + 'tramway_ref20150612171109044', + 'pivot', + ); + $this->assertEquals($expectedRelationsKeys, array_keys($relations)); + $this->assertCount(1, $relations['VilleMTP_MTP_Quartiers_2011_432620130116112610876']); + $this->assertCount(3, $relations['tramstop20150328114203878']); + $this->assertCount(1, $relations['tramway20150328114206278']); + $this->assertCount(1, $relations['publicbuildings20150420100958543']); + $this->assertCount(1, $relations['tramway_ref20150612171109044']); + $this->assertCount(2, $relations['pivot']); + $expectedPivotKeys = array( + 'jointure_tram_stop20150328114216806', + 'publicbuildings_tramstop20150420095614071', + ); + $this->assertEquals($expectedPivotKeys, array_keys($relations['pivot'])); + + $relationFields = $project->getRelationFieldsAsKeyArray(); + $this->assertNotNull($relationFields); + $this->assertTrue(is_array($relationFields)); + $this->assertCount(7, $relationFields); + $this->assertEquals('Quartiers', $relationFields[0]['layerName']); + $this->assertEquals('Quartiers', $relationFields[0]['typeName']); + $this->assertEquals('LIBQUART', $relationFields[0]['previewField']); + $this->assertEquals('QUARTMNO', $relationFields[0]['referencedField']); + $this->assertEquals('QUARTMNO,LIBQUART', $relationFields[0]['propertyName']); + $this->assertEquals('QUARTMNO', $relationFields[0]['referencingField']); + + $projections = $project->getProjAsKeyArray(); + $this->assertTrue(is_array($projections)); + $this->assertCount(4, $projections); + $this->assertArrayHasKey('EPSG:4326', $projections); + $this->assertArrayHasKey('EPSG:3857', $projections); + + $visibilityPresets = $project->getVisibilityPresetsAsKeyArray(); + $this->assertTrue(is_array($visibilityPresets)); + $this->assertCount(3, $visibilityPresets); + $this->assertArrayHasKey('Administrative', $visibilityPresets); + $this->assertArrayHasKey('Editable layers', $visibilityPresets); + $this->assertArrayHasKey('Transport', $visibilityPresets); + + $this->assertNotNull($project->Layouts); + $this->assertTrue(is_array($project->Layouts)); + $this->assertCount(3, $project->Layouts); + $this->assertInstanceOf(Qgis\Layout\Layout::class, $project->Layouts[0]); + $this->assertEquals('Composeur1', $project->Layouts[0]->name); + $this->assertTrue(is_array($project->Layouts[0]->PageCollection)); + $this->assertCount(1, $project->Layouts[0]->PageCollection); + $this->assertInstanceOf(Qgis\Layout\LayoutItemPage::class, $project->Layouts[0]->PageCollection[0]); + $this->assertTrue(is_array($project->Layouts[0]->Items)); + $this->assertCount(5, $project->Layouts[0]->Items); + $this->assertEquals(65642, $project->Layouts[0]->Items[0]->type); + $this->assertEquals(65641, $project->Layouts[0]->Items[1]->type); + $this->assertEquals(65640, $project->Layouts[0]->Items[2]->type); + $this->assertEquals(65640, $project->Layouts[0]->Items[3]->type); + $this->assertEquals(65639, $project->Layouts[0]->Items[4]->type); + + $layouts = $project->getLayoutsAsKeyArray(); + $this->assertTrue(is_array($layouts)); + $this->assertCount(2, $layouts); // 1 restricted layout + $this->assertEquals('Landscape A4', $layouts[0]['title']); + $this->assertEquals(297, $layouts[0]['width']); + $this->assertEquals(210, $layouts[0]['height']); + $this->assertCount(2, $layouts[0]['maps']); + $this->assertCount(2, $layouts[0]['labels']); + $this->assertFalse($layouts[0]['atlas']['enabled']); + $this->assertEquals('District card', $layouts[1]['title']); + $this->assertEquals(297, $layouts[1]['width']); + $this->assertEquals(210, $layouts[1]['height']); + $this->assertCount(2, $layouts[1]['maps']); + $this->assertCount(1, $layouts[1]['labels']); + $this->assertTrue($layouts[1]['atlas']['enabled']); + } + + public function testFromQgisPath() + { + $xml_path = __DIR__.'/../../Project/Ressources/montpellier.qgs'; + $project = Qgis\ProjectInfo::fromQgisPath($xml_path); + + $data = array( + 'version' => '3.10.5-A Coruña', + 'projectname' => 'Montpellier - Transports', + 'saveDateTime' => '', + 'title' => 'Montpellier - Transports', + ); + foreach($data as $prop => $value) { + $this->assertEquals($value, $project->$prop, $prop); + } + + $this->assertEquals(realpath($xml_path), Qgis\ProjectInfo::getQgisPath($project)); + $this->assertEquals(realpath($xml_path), $project->getPath()); + } + + public function testEmbeddedQgisProject() + { + $xml_path = __DIR__.'/../../Project/Ressources/relations_project_embed.qgs'; + $project = Qgis\ProjectInfo::fromQgisPath($xml_path); + + $data = array( + 'version' => '3.28.7-Firenze', + 'projectname' => '', + 'saveDateTime' => '2023-09-27T15:15:59', + 'title' => '', + ); + foreach($data as $prop => $value) { + $this->assertEquals($value, $project->$prop, $prop); + } + + $this->assertNotNull($project->projectlayers); + $this->assertTrue(is_array($project->projectlayers)); + $this->assertCount(2, $project->projectlayers); + + $layers = $project->getLayersAsKeyArray(); + $this->assertTrue(is_array($layers)); + $this->assertCount(2, $layers); + + $this->assertTrue($layers[0]['embedded']); + $this->assertEquals('./relations_project.qgs', $layers[0]['projectPath']); + $this->assertEquals(realpath(dirname(realpath($xml_path)).DIRECTORY_SEPARATOR.'./relations_project.qgs'), $layers[0]['file']); + $this->assertEquals('child_layer_8dec6d75_eeed_494b_b97f_5f2c7e16fd00', $layers[0]['id']); + $this->assertEquals('child_layer', $layers[0]['name']); + + $this->assertTrue($layers[1]['embedded']); + $this->assertEquals('./relations_project.qgs', $layers[1]['projectPath']); + $this->assertEquals(realpath(dirname(realpath($xml_path)).DIRECTORY_SEPARATOR.'./relations_project.qgs'), $layers[1]['file']); + $this->assertEquals('father_layer_79f5a996_39db_4a1f_b270_dfe21d3e44ff', $layers[1]['id']); + $this->assertEquals('father_layer', $layers[1]['name']); + } + + public function testJsonEncode() + { + $xml_path = __DIR__.'/../../Project/Ressources/montpellier.qgs'; + // Open the document with XML Reader at the root element document + $oXml = App\XmlTools::xmlReaderFromFile($xml_path); + $project = Qgis\ProjectInfo::fromXmlReader($oXml); + + $json = json_encode($project); + $this->assertNotNull($json); + $this->assertStringStartsWith('{"version":', $json); + } +} From 077db5b463190ae6ceb6cb64d7065d6757e61d4c Mon Sep 17 00:00:00 2001 From: rldhont Date: Wed, 18 Sep 2024 13:55:09 +0200 Subject: [PATCH 20/46] Lint QgisProject --- lizmap/modules/lizmap/lib/Project/QgisProject.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lizmap/modules/lizmap/lib/Project/QgisProject.php b/lizmap/modules/lizmap/lib/Project/QgisProject.php index a27b605ebb..50dc5ca575 100644 --- a/lizmap/modules/lizmap/lib/Project/QgisProject.php +++ b/lizmap/modules/lizmap/lib/Project/QgisProject.php @@ -1232,7 +1232,7 @@ protected function readXmlProject($qgs_path) } /** - * @param \SimpleXMLElement $xml + * @param \SimpleXMLElement $qgsLoad */ protected function readWMSInformation($qgsLoad) { From 99ee286a17763c717ebef1eea5ba30795691be3d Mon Sep 17 00:00:00 2001 From: rldhont Date: Fri, 27 Sep 2024 16:03:16 +0200 Subject: [PATCH 21/46] PHP: move getLayersWithLabels method form Project to ProjectConfig --- lizmap/modules/lizmap/lib/Project/Project.php | 31 +------------ .../lizmap/lib/Project/ProjectConfig.php | 43 +++++++++++++++++++ 2 files changed, 44 insertions(+), 30 deletions(-) diff --git a/lizmap/modules/lizmap/lib/Project/Project.php b/lizmap/modules/lizmap/lib/Project/Project.php index 61c657ebc3..387b4c6254 100644 --- a/lizmap/modules/lizmap/lib/Project/Project.php +++ b/lizmap/modules/lizmap/lib/Project/Project.php @@ -261,36 +261,7 @@ protected function readProject() */ protected function getLayersWithLabels() { - // Keep a list of layer ids for which to replace the code by labels - $layersWithLabeledFields = array(); - - // Attribute layers - foreach ($this->cfg->getAttributeLayers() as $key => $config) { - if ($config->hideLayer == 'True') { - continue; - } - $layersWithLabeledFields[] = $config->layerId; - } - - // Dataviz layers - foreach ($this->cfg->getDatavizLayers() as $o => $config) { - $layerId = $config->layerId; - if (array_key_exists($layerId, $layersWithLabeledFields)) { - continue; - } - $layersWithLabeledFields[] = $config->layerId; - } - - // Form filter layers - foreach ($this->cfg->getFormFilterLayers() as $o => $config) { - $layerId = $config->layerId; - if (array_key_exists($layerId, $layersWithLabeledFields)) { - continue; - } - $layersWithLabeledFields[] = $config->layerId; - } - - return $layersWithLabeledFields; + return $this->cfg->getLayersWithLabels(); } public function getQgisPath() diff --git a/lizmap/modules/lizmap/lib/Project/ProjectConfig.php b/lizmap/modules/lizmap/lib/Project/ProjectConfig.php index dc63c2a8b4..617a2f490d 100644 --- a/lizmap/modules/lizmap/lib/Project/ProjectConfig.php +++ b/lizmap/modules/lizmap/lib/Project/ProjectConfig.php @@ -522,6 +522,49 @@ public function getDatavizLayers() return $this->datavizLayers; } + /** + * List of the layers configured in the tools + * Attribute table, form filter & dataviz. + * + * We use this list to find all the fields for which + * we need to replace the code by their corresponding labels + * + * @return array Array of layer ids + */ + public function getLayersWithLabels() + { + // Keep a list of layer ids for which to replace the code by labels + $layersWithLabeledFields = array(); + + // Attribute layers + foreach ($this->getAttributeLayers() as $key => $config) { + if ($config->hideLayer == 'True') { + continue; + } + $layersWithLabeledFields[] = $config->layerId; + } + + // Dataviz layers + foreach ($this->getDatavizLayers() as $o => $config) { + $layerId = $config->layerId; + if (array_key_exists($layerId, $layersWithLabeledFields)) { + continue; + } + $layersWithLabeledFields[] = $config->layerId; + } + + // Form filter layers + foreach ($this->getFormFilterLayers() as $o => $config) { + $layerId = $config->layerId; + if (array_key_exists($layerId, $layersWithLabeledFields)) { + continue; + } + $layersWithLabeledFields[] = $config->layerId; + } + + return $layersWithLabeledFields; + } + /** Get the HTML template built from the Drag and drop layout * and override the original datavizTemplate configuration option. * From 2a1fb003705f41858339aafa71b28dff51cb19c7 Mon Sep 17 00:00:00 2001 From: rldhont Date: Wed, 18 Sep 2024 10:26:51 +0200 Subject: [PATCH 22/46] Using Lizmap\Project\Qgis\ProjectInfo in QgisProject::readXmlProject --- .../lizmap/lib/Project/QgisProject.php | 154 ++++++++++-------- 1 file changed, 82 insertions(+), 72 deletions(-) diff --git a/lizmap/modules/lizmap/lib/Project/QgisProject.php b/lizmap/modules/lizmap/lib/Project/QgisProject.php index 50dc5ca575..3d24a4f4ca 100644 --- a/lizmap/modules/lizmap/lib/Project/QgisProject.php +++ b/lizmap/modules/lizmap/lib/Project/QgisProject.php @@ -92,7 +92,7 @@ class QgisProject protected $lastSaveDateTime; /** - * @var array contains WMS info + * @var array contains WMS info */ protected $WMSInformation; @@ -102,12 +102,12 @@ class QgisProject protected $canvasColor = ''; /** - * @var array authid => proj4 + * @var array authid => proj4 */ protected $allProj4 = array(); /** - * @var array for each referenced layer, there is an item + * @var array for each referenced layer, there is an item * with referencingLayer, referencedField, referencingField keys. * There is also a 'pivot' key */ @@ -119,7 +119,7 @@ class QgisProject protected $relationsFields = array(); /** - * @var array list of themes + * @var array list of themes */ protected $themes = array(); @@ -293,6 +293,11 @@ public function getLastSaveDateTime() return $this->lastSaveDateTime; } + /** + * WMS informations + * + * @return array + */ public function getWMSInformation() { return $this->WMSInformation; @@ -303,6 +308,13 @@ public function getCanvasColor() return $this->canvasColor; } + /** + * Proj4 string for proj Auth Id if is known by the project + * + * @param string $authId + * + * @return string|null + */ public function getProj4($authId) { if (!array_key_exists($authId, $this->allProj4)) { @@ -312,16 +324,31 @@ public function getProj4($authId) return $this->allProj4[$authId]; } + /** + * All proj4 + * + * @return array + */ public function getAllProj4() { return $this->allProj4; } + /** + * Relations + * + * @return array + */ public function getRelations() { return $this->relations; } + /** + * Themes of the QGIS project + * + * @return array + */ public function getThemes() { return $this->themes; @@ -1165,74 +1192,46 @@ protected function readXmlProject($qgs_path) throw new \Exception('The QGIS project '.basename($qgs_path).' does not exist!'); } - $qgsXml = App\XmlTools::xmlFromFile($qgs_path); - if (!is_object($qgsXml)) { - $errormsg = '\n'.basename($qgs_path).'\n'.$qgsXml; - $errormsg = 'An error has been raised when loading QGIS Project:'.$errormsg; - \jLog::log($errormsg, 'lizmapadmin'); + $project = Qgis\ProjectInfo::fromQgisPath($qgs_path); - throw new \Exception('The QGIS project '.basename($qgs_path).' has invalid content!'); - } - $this->xml = $qgsXml; // Build data $this->data = array( + 'title' => $project->properties->WMSServiceTitle, + 'abstract' => $project->properties->WMSServiceAbstract, + 'keywordList' => is_array($project->properties->WMSKeywordList) ? implode(', ', $project->properties->WMSKeywordList) : '', + 'wmsMaxWidth' => $project->properties->WMSMaxWidth, + 'wmsMaxHeight' => $project->properties->WMSMaxHeight, ); - // get title from WMS properties - if (property_exists($qgsXml->properties, 'WMSServiceTitle')) { - if (!empty($qgsXml->properties->WMSServiceTitle)) { - $this->data['title'] = (string) $qgsXml->properties->WMSServiceTitle; - } - } - - // get abstract from WMS properties - if (property_exists($qgsXml->properties, 'WMSServiceAbstract')) { - $this->data['abstract'] = (string) $qgsXml->properties->WMSServiceAbstract; - } - - // get keyword list from WMS properties - if (property_exists($qgsXml->properties, 'WMSKeywordList')) { - $values = array(); - foreach ($qgsXml->properties->WMSKeywordList->value as $value) { - if ((string) $value !== '') { - $values[] = (string) $value; - } - } - $this->data['keywordList'] = implode(', ', $values); - } - - // get WMS max width - if (property_exists($qgsXml->properties, 'WMSMaxWidth')) { - $this->data['wmsMaxWidth'] = (int) $qgsXml->properties->WMSMaxWidth; - } - if (!array_key_exists('WMSMaxWidth', $this->data) or !$this->data['wmsMaxWidth']) { - unset($this->data['wmsMaxWidth']); - } - - // get WMS max height - if (property_exists($qgsXml->properties, 'WMSMaxHeight')) { - $this->data['wmsMaxHeight'] = (int) $qgsXml->properties->WMSMaxHeight; - } - if (!array_key_exists('WMSMaxHeight', $this->data) or !$this->data['wmsMaxHeight']) { - unset($this->data['wmsMaxHeight']); - } - // get QGIS project version - $this->qgisProjectVersion = $this->readQgisProjectVersion($qgsXml); - $this->lastSaveDateTime = $this->readLastSaveDateTime($qgs_path); - - $this->WMSInformation = $this->readWMSInformation($qgsXml); - $this->canvasColor = $this->readCanvasColor($qgsXml); - $this->allProj4 = $this->readAllProj4($qgsXml); - $this->themes = $this->readThemes($qgsXml); - $this->customProjectVariables = $this->readCustomProjectVariables($qgsXml); - $this->useLayerIDs = $this->readUseLayerIDs($qgsXml); - $this->layers = $this->readLayers($qgsXml); - list($this->relations, $this->relationsFields) = $this->readRelations($qgsXml); + //$this->qgisProjectVersion = $this->readQgisProjectVersion($qgsXml); + $this->qgisProjectVersion = $this->convertQgisProjectVersion($project->version); + //$this->lastSaveDateTime = $this->readLastSaveDateTime($qgs_path); + $this->lastSaveDateTime = $project->saveDateTime; + + //$this->WMSInformation = $this->readWMSInformation($qgsXml); + $this->WMSInformation = $project->getWmsInformationsAsKeyArray(); + //$this->canvasColor = $this->readCanvasColor($qgsXml); + $this->canvasColor = $project->properties->Gui->getCanvasColor(); + //$this->allProj4 = $this->readAllProj4($qgsXml); + $this->allProj4 = $project->getProjAsKeyArray(); + //$this->themes = $this->readThemes($qgsXml); + $this->themes = $project->getVisibilityPresetsAsKeyArray(); + //$this->customProjectVariables = $this->readCustomProjectVariables($qgsXml); + $this->customProjectVariables = $project->properties->Variables !== null ? $project->properties->Variables->getVariablesAsKeyArray() : array(); + //$this->useLayerIDs = $this->readUseLayerIDs($qgsXml); + $this->useLayerIDs = $project->properties->WMSUseLayerIDs !== null ? $project->properties->WMSUseLayerIDs : false; + //$this->layers = $this->readLayers($qgsXml); + $this->layers = $project->getLayersAsKeyArray(); + //list($this->relations, $this->relationsFields) = $this->readRelations($qgsXml); + $this->relations = $project->getRelationsAsKeyArray(); + $this->relationsFields = $project->getRelationFieldsAsKeyArray(); } /** * @param \SimpleXMLElement $qgsLoad + * + * @return array */ protected function readWMSInformation($qgsLoad) { @@ -1294,18 +1293,17 @@ protected function readWMSInformation($qgsLoad) } /** - * @param \SimpleXMLElement $xml + * @param string $version + * + * @return int */ - protected function readQgisProjectVersion($xml) + protected function convertQgisProjectVersion($version) { - $qgisRoot = $xml->xpath('//qgis'); - $qgisRootZero = $qgisRoot[0]; - $qgisProjectVersion = (string) $qgisRootZero->attributes()->version; - $qgisProjectVersion = explode('-', $qgisProjectVersion); - $qgisProjectVersion = $qgisProjectVersion[0]; - $qgisProjectVersion = explode('.', $qgisProjectVersion); + $version = explode('-', $version); + $version = $version[0]; + $version = explode('.', $version); $a = ''; - foreach ($qgisProjectVersion as $k) { + foreach ($version as $k) { if (strlen($k) == 1) { $a .= '0'.$k; } else { @@ -1316,6 +1314,18 @@ protected function readQgisProjectVersion($xml) return (int) $a; } + /** + * @param \SimpleXMLElement $xml + * + * @return int + */ + protected function readQgisProjectVersion($xml) + { + $qgisRoot = $xml->xpath('//qgis'); + $qgisRootZero = $qgisRoot[0]; + return $this->convertQgisProjectVersion((string) $qgisRootZero->attributes()->version); + } + /** * Read the last modified date of the QGS file. * @@ -1357,7 +1367,7 @@ protected function readCanvasColor($xml) /** * @param \SimpleXMLElement $xml * - * @return array + * @return array */ protected function readAllProj4($xml) { From 936a7cea539d9ebd61fa645fff288599033d99b3 Mon Sep 17 00:00:00 2001 From: rldhont Date: Wed, 18 Sep 2024 10:28:39 +0200 Subject: [PATCH 23/46] Using Lizmap\Project\Qgis\ProjectInfo in QgisProject::getPrintTemplates --- lizmap/modules/lizmap/lib/Project/QgisProject.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lizmap/modules/lizmap/lib/Project/QgisProject.php b/lizmap/modules/lizmap/lib/Project/QgisProject.php index 3d24a4f4ca..80d5db403c 100644 --- a/lizmap/modules/lizmap/lib/Project/QgisProject.php +++ b/lizmap/modules/lizmap/lib/Project/QgisProject.php @@ -782,6 +782,10 @@ public function getLayerNameByIdFromConfig($layerId, $layers) */ public function getPrintTemplates() { + if ($this->path) { + $project = Qgis\ProjectInfo::fromQgisPath($this->path); + return $project->getLayoutsAsKeyArray(); + } // get restricted composers $rComposers = array(); $restrictedComposers = $this->getXml()->xpath('//properties/WMSRestrictedComposers/value'); From 7b2748705c098e89f1fb6fb0e2becc238e798b52 Mon Sep 17 00:00:00 2001 From: rldhont Date: Wed, 18 Sep 2024 10:29:39 +0200 Subject: [PATCH 24/46] Using Lizmap\Project\Qgis\ProjectInfo in QgisProject::readLocateByLayer --- .../lizmap/lib/Project/QgisProject.php | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/lizmap/modules/lizmap/lib/Project/QgisProject.php b/lizmap/modules/lizmap/lib/Project/QgisProject.php index 80d5db403c..f6d92b007e 100644 --- a/lizmap/modules/lizmap/lib/Project/QgisProject.php +++ b/lizmap/modules/lizmap/lib/Project/QgisProject.php @@ -911,6 +911,58 @@ public function readLocateByLayer($locateByLayer) foreach ($locateByLayer as $k => $v) { $locateLayerIds[] = $v->layerId; } + + // update locateByLayer with project from path + if ($this->path) { + $project = Qgis\ProjectInfo::fromQgisPath($this->path); + + // update locateByLayer with alias and filter information + foreach ($locateByLayer as $k => $v) { + $updateLocate = False; + $layer = $project->getLayerById($v->layerId); + // Get field alias + $alias = $layer->getFieldAlias($v->fieldName); + if ($alias !== null) { + // Update locate with field alias + $v->fieldAlias = $alias; + $updateLocate = True; + } + if (property_exists($v, 'filterFieldName')) { + // Get filter field alias + $filterAlias = $layer->getFieldAlias($v->filterFieldName); + if ($filterAlias !== null) { + // Update locate with filter field alias + $v->filterFieldAlias = $filterAlias; + $updateLocate = True; + } + } + // Get joins + if ($layer->vectorjoins !== null && count($layer->vectorjoins) > 0) { + if (!property_exists($v, 'vectorjoins')) { + // Add joins to locate + $v->vectorjoins = array(); + $updateLocate = True; + } + foreach ($layer->vectorjoins as $vectorjoin) { + if (in_array($vectorjoin->joinLayerId, $locateLayerIds)) { + // Add join info to locate + $v->vectorjoins[] = (object) array( + 'joinFieldName' => $vectorjoin->joinFieldName, + 'targetFieldName' => $vectorjoin->targetFieldName, + 'joinLayerId' => $vectorjoin->joinLayerId, + ); + $updateLocate = True; + } + } + } + if ($updateLocate) { + // Update locate if needed + $locateByLayer->{$k} = $v; + } + } + return; + } + // update locateByLayer with alias and filter information foreach ($locateByLayer as $k => $v) { $xmlLayer = $this->getXmlLayer2($this->getXml(), $v->layerId); From cda52d030fd67db8d19da1f29cad8f303ba1631d Mon Sep 17 00:00:00 2001 From: rldhont Date: Wed, 18 Sep 2024 10:30:35 +0200 Subject: [PATCH 25/46] Using Lizmap\Project\Qgis\ProjectInfo in QgisProject::readEditionLayers --- .../lizmap/lib/Project/QgisProject.php | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/lizmap/modules/lizmap/lib/Project/QgisProject.php b/lizmap/modules/lizmap/lib/Project/QgisProject.php index f6d92b007e..11474d2a17 100644 --- a/lizmap/modules/lizmap/lib/Project/QgisProject.php +++ b/lizmap/modules/lizmap/lib/Project/QgisProject.php @@ -1010,6 +1010,27 @@ public function readLocateByLayer($locateByLayer) */ public function readEditionLayers($editionLayers) { + if ($this->path) { + $project = Qgis\ProjectInfo::fromQgisPath($this->path); + foreach ($editionLayers as $key => $obj) { + // Improve performance by getting provider directly from config + // Available for lizmap plugin >= 3.3.2 + if (property_exists($obj, 'provider')) { + if ($obj->provider == 'spatialite') { + unset($editionLayers->{$key}); + } + + continue; + } + // check for embedded layers + $layer = $project->getLayerById($obj->layerId); + if ($layer->provider == 'spatialite') { + unset($editionLayers->{$key}); + } + } + return; + } + foreach ($editionLayers as $key => $obj) { // Improve performance by getting provider directly from config // Available for lizmap plugin >= 3.3.2 From 398a5617caf66be13e6f73aa8aca1a3d2f88162c Mon Sep 17 00:00:00 2001 From: rldhont Date: Wed, 18 Sep 2024 10:31:40 +0200 Subject: [PATCH 26/46] Using Lizmap\Project\Qgis\ProjectInfo in QgisProject::readEditionForms --- .../lizmap/lib/Project/QgisProject.php | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/lizmap/modules/lizmap/lib/Project/QgisProject.php b/lizmap/modules/lizmap/lib/Project/QgisProject.php index 11474d2a17..d686b79466 100644 --- a/lizmap/modules/lizmap/lib/Project/QgisProject.php +++ b/lizmap/modules/lizmap/lib/Project/QgisProject.php @@ -1064,10 +1064,29 @@ public function readEditionLayers($editionLayers) /** * @param object $editionLayers - * @param Project $proj + * @param Project|null $proj */ - public function readEditionForms($editionLayers, $proj) + public function readEditionForms($editionLayers, $proj = null) { + if ($this->path) { + $project = Qgis\ProjectInfo::fromQgisPath($this->path); + foreach ($editionLayers as $key => $obj) { + $layer = $project->getLayerById($obj->layerId); + if ($layer === null) { + continue; + } + if ($layer->type !== 'vector') { + continue; + } + /** @var Qgis\Layer\VectorLayer $layer */ + + $formControls = $layer->getFormControls(); + if ($proj) { + $proj->getCacheHandler()->setEditableLayerFormCache($obj->layerId, $formControls); + } + } + return; + } $embeddedEditionLayers = array(); foreach ($editionLayers as $key => $obj) { // check for embedded layers From 5c539a09e0f97960f7915b74ddf9dc5d344a3b5e Mon Sep 17 00:00:00 2001 From: rldhont Date: Wed, 18 Sep 2024 10:33:21 +0200 Subject: [PATCH 27/46] Using Lizmap\Project\Qgis\ProjectInfo in QgisProject::readAttributeLayers and update JS --- assets/src/legacy/attributeTable.js | 25 +++++++++++++++++++ .../lizmap/lib/Project/QgisProject.php | 16 ++++++++++++ 2 files changed, 41 insertions(+) diff --git a/assets/src/legacy/attributeTable.js b/assets/src/legacy/attributeTable.js index b1f7ee8345..0027fd1814 100644 --- a/assets/src/legacy/attributeTable.js +++ b/assets/src/legacy/attributeTable.js @@ -1763,6 +1763,31 @@ var lizAttributeTable = function() { && config.attributeLayers[aName]['attributetableconfig'] && !$.isEmptyObject(config.attributeLayers[aName]['attributetableconfig']['columns']) ){ + if (!('column' in config.attributeLayers[aName]['attributetableconfig']['columns'])) { + var atc = config.attributeLayers[aName]['attributetableconfig']['columns']; + if(atc.length == 0){ + return colToReturn; + } + var lizcols = columns.slice(0, firstDisplayedColIndex); + var newcolumns = []; + for (var x in atc) { + var colx = atc[x]; + // Do nothing if the item does not reference a field + if (colx.type != 'field') { + continue; + } + for (const column of columns) { + if (!('data' in column)) { + continue; + } + if (colx.name == column.data) { + newcolumns.push(column); + } + } + } + colToReturn['columns'] = lizcols.concat(newcolumns); + return colToReturn; + } var atc = config.attributeLayers[aName]['attributetableconfig']['columns']['column']; if(atc.length == 0){ return colToReturn; diff --git a/lizmap/modules/lizmap/lib/Project/QgisProject.php b/lizmap/modules/lizmap/lib/Project/QgisProject.php index d686b79466..29952e1ebf 100644 --- a/lizmap/modules/lizmap/lib/Project/QgisProject.php +++ b/lizmap/modules/lizmap/lib/Project/QgisProject.php @@ -1221,6 +1221,22 @@ public function readLayersLabeledFieldsConfig($layerIds, $proj) */ public function readAttributeLayers($attributeLayers) { + if ($this->path) { + $project = Qgis\ProjectInfo::fromQgisPath($this->path); + // Get field order & visibility + foreach ($attributeLayers as $key => $obj) { + // Improve performance by getting custom_config status directly from config + // Available for lizmap plugin >= 3.3.3 + if (property_exists($obj, 'custom_config') && $obj->custom_config != 'True') { + continue; + } + + $layer = $project->getLayerById($obj->layerId); + $obj->attributetableconfig = $layer->attributetableconfig->toKeyArray(); + } + return; + } + // Get field order & visibility foreach ($attributeLayers as $key => $obj) { // Improve performance by getting custom_config status directly from config From 5100023047e325fec7eaa1c2d9ada302196bc0d1 Mon Sep 17 00:00:00 2001 From: rldhont Date: Wed, 18 Sep 2024 11:11:47 +0200 Subject: [PATCH 28/46] Using Lizmap\Project\Qgis\ProjectInfo in QgisProject::setShortNames --- lizmap/modules/lizmap/lib/Project/QgisProject.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lizmap/modules/lizmap/lib/Project/QgisProject.php b/lizmap/modules/lizmap/lib/Project/QgisProject.php index 29952e1ebf..642cfced37 100644 --- a/lizmap/modules/lizmap/lib/Project/QgisProject.php +++ b/lizmap/modules/lizmap/lib/Project/QgisProject.php @@ -381,6 +381,20 @@ public function setPropertiesAfterRead(ProjectConfig $cfg) */ protected function setShortNames(ProjectConfig $cfg) { + if ($this->path) { + $project = Qgis\ProjectInfo::fromQgisPath($this->path); + foreach ($project->projectlayers as $layer) { + if (!isset($layer->shortname)) { + continue; + } + $layerCfg = $cfg->getLayer($layer->layername); + if ($layerCfg) { + $layerCfg->shortname = $layer->shortname; + } + } + return; + } + $shortNames = $this->xpathQuery('//maplayer/shortname'); if ($shortNames) { foreach ($shortNames as $sname) { From 800ff948bbfe10e1ad6dd7ff43b4b6cb6bd708cb Mon Sep 17 00:00:00 2001 From: rldhont Date: Wed, 18 Sep 2024 14:46:38 +0200 Subject: [PATCH 29/46] Using Lizmap\Project\Qgis\ProjectInfo in QgisProject::setLayerOpacity --- lizmap/modules/lizmap/lib/Project/QgisProject.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lizmap/modules/lizmap/lib/Project/QgisProject.php b/lizmap/modules/lizmap/lib/Project/QgisProject.php index 642cfced37..8ce52901fa 100644 --- a/lizmap/modules/lizmap/lib/Project/QgisProject.php +++ b/lizmap/modules/lizmap/lib/Project/QgisProject.php @@ -418,6 +418,19 @@ protected function setShortNames(ProjectConfig $cfg) */ protected function setLayerOpacity(ProjectConfig $cfg) { + if ($this->path) { + $project = Qgis\ProjectInfo::fromQgisPath($this->path); + foreach ($project->projectlayers as $layer) { + if (!isset($layer->layerOpacity) || $layer->layerOpacity == 1) { + continue; + } + $layerCfg = $cfg->getLayer($layer->layername); + if ($layerCfg) { + $layerCfg->opacity = $layer->layerOpacity; + } + } + return; + } $layerWithOpacities = $this->xpathQuery('//maplayer/layerOpacity[.!=1]/parent::* | //maplayer/pipe/rasterrenderer/@opacity[.!=1]/ancestor::maplayer'); if ($layerWithOpacities) { foreach ($layerWithOpacities as $layerWithOpacity) { From c3844d711ba8b019ed209d080605ed0c207500f9 Mon Sep 17 00:00:00 2001 From: rldhont Date: Wed, 18 Sep 2024 14:52:24 +0200 Subject: [PATCH 30/46] Using Lizmap\Project\Qgis\ProjectInfo in QgisProject::unsetPropAfterRead --- .../lizmap/lib/Project/QgisProject.php | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/lizmap/modules/lizmap/lib/Project/QgisProject.php b/lizmap/modules/lizmap/lib/Project/QgisProject.php index 8ce52901fa..44d8d2176e 100644 --- a/lizmap/modules/lizmap/lib/Project/QgisProject.php +++ b/lizmap/modules/lizmap/lib/Project/QgisProject.php @@ -538,11 +538,20 @@ protected function setLayerShowFeatureCount(ProjectConfig $cfg) protected function unsetPropAfterRead(ProjectConfig $cfg) { // remove plugin layer - $pluginLayers = $this->xpathQuery('//maplayer[type="plugin"]'); - if ($pluginLayers) { - foreach ($pluginLayers as $layer) { - $name = (string) $layer->layername; - $cfg->removeLayer($name); + if ($this->path) { + $project = Qgis\ProjectInfo::fromQgisPath($this->path); + foreach ($project->projectlayers as $layer) { + if ($layer->type === 'plugin') { + $cfg->removeLayer($layer->layername); + } + } + } else { + $pluginLayers = $this->xpathQuery('//maplayer[type="plugin"]'); + if ($pluginLayers) { + foreach ($pluginLayers as $layer) { + $name = (string) $layer->layername; + $cfg->removeLayer($name); + } } } From ec597a4c4b23bfc1d41d0b03fd69f3c5f9d00ae7 Mon Sep 17 00:00:00 2001 From: rldhont Date: Wed, 18 Sep 2024 18:56:52 +0200 Subject: [PATCH 31/46] Using Lizmap\Project\Qgis\ProjectInfo in QgisProject::setLayerGroupData --- .../lizmap/lib/Project/QgisProject.php | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lizmap/modules/lizmap/lib/Project/QgisProject.php b/lizmap/modules/lizmap/lib/Project/QgisProject.php index 44d8d2176e..3f3a6053d7 100644 --- a/lizmap/modules/lizmap/lib/Project/QgisProject.php +++ b/lizmap/modules/lizmap/lib/Project/QgisProject.php @@ -454,6 +454,26 @@ protected function setLayerOpacity(ProjectConfig $cfg) */ protected function setLayerGroupData(ProjectConfig $cfg) { + if ($this->path) { + $project = Qgis\ProjectInfo::fromQgisPath($this->path); + $groupShortNames = $project->layerTreeRoot->getGroupShortNames(); + foreach ($groupShortNames as $name => $shortName) { + $layerCfg = $cfg->getLayer($name); + if (!$layerCfg) { + continue; + } + $layerCfg->shortname = $shortName; + } + $groupsMutuallyExclusive = $project->layerTreeRoot->getGroupsMutuallyExclusive(); + foreach ($groupsMutuallyExclusive as $group) { + $layerCfg = $cfg->getLayer($group); + if (!$layerCfg) { + continue; + } + $layerCfg->mutuallyExclusive = 'True'; + } + return; + } $groupsWithShortName = $this->xpathQuery("//layer-tree-group/customproperties/property[@key='wmsShortName']/parent::*/parent::*"); if ($groupsWithShortName) { foreach ($groupsWithShortName as $group) { From 61a64f530dd123341bcfa5ed9096f4c2bc9a8153 Mon Sep 17 00:00:00 2001 From: rldhont Date: Wed, 18 Sep 2024 22:12:39 +0200 Subject: [PATCH 32/46] Using Lizmap\Project\Qgis\ProjectInfo in QgisProject::setLayerShowFeatureCount --- lizmap/modules/lizmap/lib/Project/QgisProject.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lizmap/modules/lizmap/lib/Project/QgisProject.php b/lizmap/modules/lizmap/lib/Project/QgisProject.php index 3f3a6053d7..12bf547b21 100644 --- a/lizmap/modules/lizmap/lib/Project/QgisProject.php +++ b/lizmap/modules/lizmap/lib/Project/QgisProject.php @@ -537,6 +537,18 @@ protected function setLayerGroupData(ProjectConfig $cfg) */ protected function setLayerShowFeatureCount(ProjectConfig $cfg) { + if ($this->path) { + $project = Qgis\ProjectInfo::fromQgisPath($this->path); + $layersShowFeatureCount = $project->layerTreeRoot->getLayersShowFeatureCount(); + foreach ($layersShowFeatureCount as $layer) { + $layerCfg = $cfg->getLayer($layer); + if (!$layerCfg) { + continue; + } + $layerCfg->showFeatureCount = 'True'; + } + return; + } $layersWithShowFeatureCount = $this->xpathQuery("//layer-tree-layer/customproperties/property[@key='showFeatureCount'][@value='1']/parent::*/parent::*"); if (!$layersWithShowFeatureCount) { $layersWithShowFeatureCount = $this->xpathQuery("//layer-tree-layer/customproperties/Option[@type='Map']/Option[@name='showFeatureCount'][@value='1']/parent::*/parent::*/parent::*"); From 224cd3877a3e35d12d827dc5313a58aad978cdf9 Mon Sep 17 00:00:00 2001 From: rldhont Date: Fri, 20 Sep 2024 21:53:32 +0200 Subject: [PATCH 33/46] Lint Lizmap\Project\Project readLayersOrder method --- lizmap/modules/lizmap/lib/Project/Project.php | 4 +--- phpstan-baseline.neon | 5 ----- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/lizmap/modules/lizmap/lib/Project/Project.php b/lizmap/modules/lizmap/lib/Project/Project.php index 387b4c6254..44dd52af48 100644 --- a/lizmap/modules/lizmap/lib/Project/Project.php +++ b/lizmap/modules/lizmap/lib/Project/Project.php @@ -1727,13 +1727,11 @@ protected function readAttributeLayers(QgisProject $xml, ProjectConfig $cfg) } /** - * @param \SimpleXMLElement $xml - * * @return int[] */ protected function readLayersOrder(QgisProject $xml) { - return $this->qgis->readLayersOrder($xml, $this->getLayers()); + return $xml->readLayersOrder($this->cfg->getLayers()); } /** diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 92d9de7b51..c2b47eaa35 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -518,11 +518,6 @@ parameters: count: 1 path: lizmap/modules/lizmap/lib/Project/Project.php - - - message: "#^PHPDoc tag @param for parameter \\$xml with type SimpleXMLElement is incompatible with native type Lizmap\\\\Project\\\\QgisProject\\.$#" - count: 1 - path: lizmap/modules/lizmap/lib/Project/Project.php - - message: "#^Property Lizmap\\\\Project\\\\Project\\:\\:\\$printCapabilities \\(object\\) does not accept default value of type array\\.$#" count: 1 From 7df0495dd94d32f8aa4ca9b33b2f758cb1710cc8 Mon Sep 17 00:00:00 2001 From: rldhont Date: Fri, 20 Sep 2024 22:02:17 +0200 Subject: [PATCH 34/46] Using Lizmap\Project\Qgis\ProjectInfo in QgisProject::readLayersOrder --- .../lizmap/lib/Project/QgisProject.php | 100 +++++++++++------- 1 file changed, 63 insertions(+), 37 deletions(-) diff --git a/lizmap/modules/lizmap/lib/Project/QgisProject.php b/lizmap/modules/lizmap/lib/Project/QgisProject.php index 12bf547b21..f6fb883e06 100644 --- a/lizmap/modules/lizmap/lib/Project/QgisProject.php +++ b/lizmap/modules/lizmap/lib/Project/QgisProject.php @@ -108,8 +108,8 @@ class QgisProject /** * @var array for each referenced layer, there is an item - * with referencingLayer, referencedField, referencingField keys. - * There is also a 'pivot' key + * with referencingLayer, referencedField, referencingField keys. + * There is also a 'pivot' key */ protected $relations = array(); @@ -294,7 +294,7 @@ public function getLastSaveDateTime() } /** - * WMS informations + * WMS informations. * * @return array */ @@ -309,11 +309,11 @@ public function getCanvasColor() } /** - * Proj4 string for proj Auth Id if is known by the project + * Proj4 string for proj Auth Id if is known by the project. * * @param string $authId * - * @return string|null + * @return null|string */ public function getProj4($authId) { @@ -325,7 +325,7 @@ public function getProj4($authId) } /** - * All proj4 + * All proj4. * * @return array */ @@ -335,7 +335,7 @@ public function getAllProj4() } /** - * Relations + * Relations. * * @return array */ @@ -345,7 +345,7 @@ public function getRelations() } /** - * Themes of the QGIS project + * Themes of the QGIS project. * * @return array */ @@ -392,6 +392,7 @@ protected function setShortNames(ProjectConfig $cfg) $layerCfg->shortname = $layer->shortname; } } + return; } @@ -429,6 +430,7 @@ protected function setLayerOpacity(ProjectConfig $cfg) $layerCfg->opacity = $layer->layerOpacity; } } + return; } $layerWithOpacities = $this->xpathQuery('//maplayer/layerOpacity[.!=1]/parent::* | //maplayer/pipe/rasterrenderer/@opacity[.!=1]/ancestor::maplayer'); @@ -472,6 +474,7 @@ protected function setLayerGroupData(ProjectConfig $cfg) } $layerCfg->mutuallyExclusive = 'True'; } + return; } $groupsWithShortName = $this->xpathQuery("//layer-tree-group/customproperties/property[@key='wmsShortName']/parent::*/parent::*"); @@ -547,6 +550,7 @@ protected function setLayerShowFeatureCount(ProjectConfig $cfg) } $layerCfg->showFeatureCount = 'True'; } + return; } $layersWithShowFeatureCount = $this->xpathQuery("//layer-tree-layer/customproperties/property[@key='showFeatureCount'][@value='1']/parent::*/parent::*"); @@ -852,6 +856,7 @@ public function getPrintTemplates() { if ($this->path) { $project = Qgis\ProjectInfo::fromQgisPath($this->path); + return $project->getLayoutsAsKeyArray(); } // get restricted composers @@ -986,14 +991,14 @@ public function readLocateByLayer($locateByLayer) // update locateByLayer with alias and filter information foreach ($locateByLayer as $k => $v) { - $updateLocate = False; + $updateLocate = false; $layer = $project->getLayerById($v->layerId); // Get field alias $alias = $layer->getFieldAlias($v->fieldName); if ($alias !== null) { // Update locate with field alias $v->fieldAlias = $alias; - $updateLocate = True; + $updateLocate = true; } if (property_exists($v, 'filterFieldName')) { // Get filter field alias @@ -1001,7 +1006,7 @@ public function readLocateByLayer($locateByLayer) if ($filterAlias !== null) { // Update locate with filter field alias $v->filterFieldAlias = $filterAlias; - $updateLocate = True; + $updateLocate = true; } } // Get joins @@ -1009,7 +1014,7 @@ public function readLocateByLayer($locateByLayer) if (!property_exists($v, 'vectorjoins')) { // Add joins to locate $v->vectorjoins = array(); - $updateLocate = True; + $updateLocate = true; } foreach ($layer->vectorjoins as $vectorjoin) { if (in_array($vectorjoin->joinLayerId, $locateLayerIds)) { @@ -1019,7 +1024,7 @@ public function readLocateByLayer($locateByLayer) 'targetFieldName' => $vectorjoin->targetFieldName, 'joinLayerId' => $vectorjoin->joinLayerId, ); - $updateLocate = True; + $updateLocate = true; } } } @@ -1028,6 +1033,7 @@ public function readLocateByLayer($locateByLayer) $locateByLayer->{$k} = $v; } } + return; } @@ -1096,6 +1102,7 @@ public function readEditionLayers($editionLayers) unset($editionLayers->{$key}); } } + return; } @@ -1131,8 +1138,8 @@ public function readEditionLayers($editionLayers) } /** - * @param object $editionLayers - * @param Project|null $proj + * @param object $editionLayers + * @param null|Project $proj */ public function readEditionForms($editionLayers, $proj = null) { @@ -1146,13 +1153,14 @@ public function readEditionForms($editionLayers, $proj = null) if ($layer->type !== 'vector') { continue; } - /** @var Qgis\Layer\VectorLayer $layer */ + /** @var Qgis\Layer\VectorLayer $layer */ $formControls = $layer->getFormControls(); if ($proj) { $proj->getCacheHandler()->setEditableLayerFormCache($obj->layerId, $formControls); } } + return; } $embeddedEditionLayers = array(); @@ -1302,6 +1310,7 @@ public function readAttributeLayers($attributeLayers) $layer = $project->getLayerById($obj->layerId); $obj->attributetableconfig = $layer->attributetableconfig->toKeyArray(); } + return; } @@ -1332,23 +1341,21 @@ public function readAttributeLayers($attributeLayers) } /** - * @param \SimpleXMLElement $xml - * @param mixed $layers + * @param mixed $layers * * @return int[] */ - public function readLayersOrder($xml, $layers) + public function readLayersOrder($layers) { $layersOrder = array(); - $customOrder = $this->getXml()->xpath('layer-tree-group/custom-order'); - if (count($customOrder) == 0) { - return $layersOrder; - } - $customOrderZero = $customOrder[0]; - if (intval($customOrderZero->attributes()->enabled) == 1) { - $items = $customOrderZero->xpath('//item'); + if ($this->path) { + $project = Qgis\ProjectInfo::fromQgisPath($this->path); + $customOrder = $project->layerTreeRoot->customOrder; + if (!$customOrder->enabled) { + return $layersOrder; + } $lo = 0; - foreach ($items as $layerI) { + foreach ($customOrder->items as $layerI) { // Get layer name from config instead of XML for possible embedded layers $name = $this->getLayerNameByIdFromConfig($layerI, $layers); if ($name) { @@ -1356,6 +1363,24 @@ public function readLayersOrder($xml, $layers) } ++$lo; } + } else { + $customOrder = $this->getXml()->xpath('layer-tree-group/custom-order'); + if (count($customOrder) == 0) { + return $layersOrder; + } + $customOrderZero = $customOrder[0]; + if (intval($customOrderZero->attributes()->enabled) == 1) { + $items = $customOrderZero->xpath('//item'); + $lo = 0; + foreach ($items as $layerI) { + // Get layer name from config instead of XML for possible embedded layers + $name = $this->getLayerNameByIdFromConfig($layerI, $layers); + if ($name) { + $layersOrder[$name] = $lo; + } + ++$lo; + } + } } return $layersOrder; @@ -1384,26 +1409,26 @@ protected function readXmlProject($qgs_path) ); // get QGIS project version - //$this->qgisProjectVersion = $this->readQgisProjectVersion($qgsXml); + // $this->qgisProjectVersion = $this->readQgisProjectVersion($qgsXml); $this->qgisProjectVersion = $this->convertQgisProjectVersion($project->version); - //$this->lastSaveDateTime = $this->readLastSaveDateTime($qgs_path); + // $this->lastSaveDateTime = $this->readLastSaveDateTime($qgs_path); $this->lastSaveDateTime = $project->saveDateTime; - //$this->WMSInformation = $this->readWMSInformation($qgsXml); + // $this->WMSInformation = $this->readWMSInformation($qgsXml); $this->WMSInformation = $project->getWmsInformationsAsKeyArray(); - //$this->canvasColor = $this->readCanvasColor($qgsXml); + // $this->canvasColor = $this->readCanvasColor($qgsXml); $this->canvasColor = $project->properties->Gui->getCanvasColor(); - //$this->allProj4 = $this->readAllProj4($qgsXml); + // $this->allProj4 = $this->readAllProj4($qgsXml); $this->allProj4 = $project->getProjAsKeyArray(); - //$this->themes = $this->readThemes($qgsXml); + // $this->themes = $this->readThemes($qgsXml); $this->themes = $project->getVisibilityPresetsAsKeyArray(); - //$this->customProjectVariables = $this->readCustomProjectVariables($qgsXml); + // $this->customProjectVariables = $this->readCustomProjectVariables($qgsXml); $this->customProjectVariables = $project->properties->Variables !== null ? $project->properties->Variables->getVariablesAsKeyArray() : array(); - //$this->useLayerIDs = $this->readUseLayerIDs($qgsXml); + // $this->useLayerIDs = $this->readUseLayerIDs($qgsXml); $this->useLayerIDs = $project->properties->WMSUseLayerIDs !== null ? $project->properties->WMSUseLayerIDs : false; - //$this->layers = $this->readLayers($qgsXml); + // $this->layers = $this->readLayers($qgsXml); $this->layers = $project->getLayersAsKeyArray(); - //list($this->relations, $this->relationsFields) = $this->readRelations($qgsXml); + // list($this->relations, $this->relationsFields) = $this->readRelations($qgsXml); $this->relations = $project->getRelationsAsKeyArray(); $this->relationsFields = $project->getRelationFieldsAsKeyArray(); } @@ -1503,6 +1528,7 @@ protected function readQgisProjectVersion($xml) { $qgisRoot = $xml->xpath('//qgis'); $qgisRootZero = $qgisRoot[0]; + return $this->convertQgisProjectVersion((string) $qgisRootZero->attributes()->version); } From e7539805ceed663a4b911d4f3a0b7a0af5230044 Mon Sep 17 00:00:00 2001 From: rldhont Date: Sun, 29 Sep 2024 17:14:08 +0200 Subject: [PATCH 35/46] Using Lizmap\Project\Qgis\ProjectInfo in QgisProject::readLayersLabeledFieldsConfig --- .../lizmap/lib/Project/QgisProject.php | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/lizmap/modules/lizmap/lib/Project/QgisProject.php b/lizmap/modules/lizmap/lib/Project/QgisProject.php index f6fb883e06..6f085f4190 100644 --- a/lizmap/modules/lizmap/lib/Project/QgisProject.php +++ b/lizmap/modules/lizmap/lib/Project/QgisProject.php @@ -1231,6 +1231,64 @@ public function readLayersLabeledFieldsConfig($layerIds, $proj) { // Get QGIS form fields configurations for each layer $layersLabeledFieldsConfig = array(); + + if ($this->path) { + $project = Qgis\ProjectInfo::fromQgisPath($this->path); + foreach ($layerIds as $layerId) { + $layer = $project->getLayerById($layerId); + if ($layer === null) { + continue; + } + if ($layer->type !== 'vector') { + continue; + } + /** @var Qgis\Layer\VectorLayer $layer */ + + $formControls = $layer->getFormControls(); + + $fields_config = array(); + foreach ($formControls as $fieldName => $control) { + $editType = $control->getFieldEditType(); + if (!in_array($editType, array('ValueMap', 'ValueRelation', 'RelationReference'))) { + continue; + } + $fields_config[$fieldName] = array( + 'type' => $editType, + ); + if ($editType == 'ValueMap') { + $valueMap = $control->getValueMap(); + if ($valueMap) { + $fields_config[$fieldName]['data'] = $valueMap; + } + } elseif ($editType == 'ValueRelation') { + $valueRelationData = $control->getValueRelationData(); + $fields_config[$fieldName]['source_layer_id'] = $valueRelationData['layer']; + $fields_config[$fieldName]['source_layer'] = $valueRelationData['layerName']; + $fields_config[$fieldName]['code_field'] = $valueRelationData['key']; + $fields_config[$fieldName]['label_field'] = $valueRelationData['value']; + $fields_config[$fieldName]['exp_filter'] = $valueRelationData['filterExpression']; + } else { + // RelationReference + // We need to get the relation properties + $relationReferenceData = $control->getRelationReference(); + $relation = $relationReferenceData['relation']; + $referencedLayerId = $relationReferenceData['referencedLayerId']; + if (!array_key_exists($referencedLayerId, $this->relations)) { + continue; + } + $fields_config[$fieldName]['relation'] = $relation; + $fields_config[$fieldName]['source_layer_id'] = $referencedLayerId; + $fields_config[$fieldName]['source_layer'] = $relationReferenceData['referencedLayerName']; + $fields_config[$fieldName]['code_field'] = $this->relations[$referencedLayerId][0]['referencedField']; + $fields_config[$fieldName]['label_field'] = $this->relations[$referencedLayerId][0]['previewField']; + $fields_config[$fieldName]['exp_filter'] = $relationReferenceData['filterExpression']; + } + } + $layersLabeledFieldsConfig[$layer->layername] = $fields_config; + } + return $layersLabeledFieldsConfig; + } + foreach ($layerIds as $layerId) { $qgisProject = $this->getEmbeddedQgisProject($layerId); From 747896be250b246da44c8e71ede49fd75756641c Mon Sep 17 00:00:00 2001 From: rldhont Date: Sun, 29 Sep 2024 17:14:36 +0200 Subject: [PATCH 36/46] Tests PHP: QgisProject does not use DOM --- .../units/classes/Project/QgisProjectTest.php | 51 +++++++++++++++++++ tests/units/testslib/QgisProjectForTests.php | 7 ++- 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/tests/units/classes/Project/QgisProjectTest.php b/tests/units/classes/Project/QgisProjectTest.php index 354d4817b5..a1d076aff2 100644 --- a/tests/units/classes/Project/QgisProjectTest.php +++ b/tests/units/classes/Project/QgisProjectTest.php @@ -320,6 +320,8 @@ public function testEmbeddedRelation() $testQgis->setPath($file); $testQgis->readXMLProjectTest($file); + $this->assertNull($testQgis->getTheXmlAttribute()); + //check layers foreach ($testQgis->getLayers() as $layers){ $this->assertEquals($layers["embedded"],1); @@ -365,6 +367,8 @@ public function testEmbeddedRelation() $testQgisParent->setPath($file); $testQgisParent->readXMLProjectTest($file); + $this->assertNull($testQgis->getTheXmlAttribute()); + $parentRelations = $testQgisParent->getRelations(); $parentRelationFields = $testQgisParent->getRelationsFields(); $this->assertEquals($relations,$parentRelations); @@ -498,6 +502,8 @@ public function testReadEditionFormsForEmbeddedLayers() $testQgis->setPath($file); $testQgis->readXMLProjectTest($file); + $this->assertNull($testQgis->getTheXmlAttribute()); + $cfg = json_decode(file_get_contents($file.'.cfg')); $config = new Project\ProjectConfig($cfg); @@ -2218,7 +2224,52 @@ public function testUploadField() $this->assertEquals('', $remotePath->getUploadAccept()); $this->assertEquals(array(), $remotePath->getMimeTypes()); $this->assertFalse($remotePath->isImageUpload()); + } + + public function testReadProject() + { + //$services = new lizmapServices(array(), (object) array(), false, '', ''); + $testQgis = new qgisProjectForTests(array()); + $file = __DIR__.'/Ressources/montpellier.qgs'; + $testQgis->setPath($file); + $testQgis->readXMLProjectTest($file); + + $this->assertNull($testQgis->getTheXmlAttribute()); + + $cfg = json_decode(file_get_contents($file.'.cfg')); + $config = new Project\ProjectConfig($cfg); + + $testQgis->setPropertiesAfterRead($config); + + $this->assertNull($testQgis->getTheXmlAttribute()); + + $testQgis->getPrintTemplates(); + + $this->assertNull($testQgis->getTheXmlAttribute()); + + $testQgis->readLocateByLayer($config->getLocateByLayer()); + + $this->assertNull($testQgis->getTheXmlAttribute()); + + $testQgis->readEditionLayers($config->getEditionLayers()); + + $this->assertNull($testQgis->getTheXmlAttribute()); + + $testQgis->readLayersOrder($config->getLayers()); + + $this->assertNull($testQgis->getTheXmlAttribute()); + + $testQgis->readAttributeLayers($config->getAttributeLayers()); + + $this->assertNull($testQgis->getTheXmlAttribute()); + + $testQgis->readEditionForms($config->getEditionLayers(), null); + + $this->assertNull($testQgis->getTheXmlAttribute()); + + $testQgis->readLayersLabeledFieldsConfig($config->getLayersWithLabels(), null); + $this->assertNull($testQgis->getTheXmlAttribute()); } } diff --git a/tests/units/testslib/QgisProjectForTests.php b/tests/units/testslib/QgisProjectForTests.php index 8ad949013c..ef67591fd6 100644 --- a/tests/units/testslib/QgisProjectForTests.php +++ b/tests/units/testslib/QgisProjectForTests.php @@ -6,7 +6,7 @@ class QgisProjectForTests extends QgisProject { public function __construct($data = null) { - if ($data) { + if ($data !== null) { parent::__construct(null, new lizmapServices(null, (object) array(), false, '', ''), new ContextForTests(), $data); } } @@ -92,6 +92,11 @@ public function setLayerOpacityForTest($cfg) return $this->setLayerOpacity($cfg); } + public function getTheXmlAttribute() + { + return $this->xml; + } + public function getXmlForTest() { return $this->getXml(); From 0fb744da4ef0de2d7616f29484cb4de63d7f451f Mon Sep 17 00:00:00 2001 From: rldhont Date: Tue, 1 Oct 2024 16:17:26 +0200 Subject: [PATCH 37/46] Lint QgisProject after changes --- lizmap/modules/lizmap/lib/Project/QgisProject.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lizmap/modules/lizmap/lib/Project/QgisProject.php b/lizmap/modules/lizmap/lib/Project/QgisProject.php index 6f085f4190..f4d088f7a2 100644 --- a/lizmap/modules/lizmap/lib/Project/QgisProject.php +++ b/lizmap/modules/lizmap/lib/Project/QgisProject.php @@ -1242,8 +1242,8 @@ public function readLayersLabeledFieldsConfig($layerIds, $proj) if ($layer->type !== 'vector') { continue; } - /** @var Qgis\Layer\VectorLayer $layer */ + /** @var Qgis\Layer\VectorLayer $layer */ $formControls = $layer->getFormControls(); $fields_config = array(); @@ -1286,6 +1286,7 @@ public function readLayersLabeledFieldsConfig($layerIds, $proj) } $layersLabeledFieldsConfig[$layer->layername] = $fields_config; } + return $layersLabeledFieldsConfig; } From dcebf2791dafe8a2965c72db159f295556fb0864 Mon Sep 17 00:00:00 2001 From: rldhont Date: Tue, 1 Oct 2024 20:45:18 +0200 Subject: [PATCH 38/46] QgisProject remove unnecessary methods --- .../lizmap/lib/Project/QgisProject.php | 573 ------------------ .../units/classes/Project/QgisProjectTest.php | 283 --------- tests/units/testslib/QgisProjectForTests.php | 53 -- 3 files changed, 909 deletions(-) diff --git a/lizmap/modules/lizmap/lib/Project/QgisProject.php b/lizmap/modules/lizmap/lib/Project/QgisProject.php index f4d088f7a2..940ddcdc38 100644 --- a/lizmap/modules/lizmap/lib/Project/QgisProject.php +++ b/lizmap/modules/lizmap/lib/Project/QgisProject.php @@ -1468,94 +1468,20 @@ protected function readXmlProject($qgs_path) ); // get QGIS project version - // $this->qgisProjectVersion = $this->readQgisProjectVersion($qgsXml); $this->qgisProjectVersion = $this->convertQgisProjectVersion($project->version); - // $this->lastSaveDateTime = $this->readLastSaveDateTime($qgs_path); $this->lastSaveDateTime = $project->saveDateTime; - // $this->WMSInformation = $this->readWMSInformation($qgsXml); $this->WMSInformation = $project->getWmsInformationsAsKeyArray(); - // $this->canvasColor = $this->readCanvasColor($qgsXml); $this->canvasColor = $project->properties->Gui->getCanvasColor(); - // $this->allProj4 = $this->readAllProj4($qgsXml); $this->allProj4 = $project->getProjAsKeyArray(); - // $this->themes = $this->readThemes($qgsXml); $this->themes = $project->getVisibilityPresetsAsKeyArray(); - // $this->customProjectVariables = $this->readCustomProjectVariables($qgsXml); $this->customProjectVariables = $project->properties->Variables !== null ? $project->properties->Variables->getVariablesAsKeyArray() : array(); - // $this->useLayerIDs = $this->readUseLayerIDs($qgsXml); $this->useLayerIDs = $project->properties->WMSUseLayerIDs !== null ? $project->properties->WMSUseLayerIDs : false; - // $this->layers = $this->readLayers($qgsXml); $this->layers = $project->getLayersAsKeyArray(); - // list($this->relations, $this->relationsFields) = $this->readRelations($qgsXml); $this->relations = $project->getRelationsAsKeyArray(); $this->relationsFields = $project->getRelationFieldsAsKeyArray(); } - /** - * @param \SimpleXMLElement $qgsLoad - * - * @return array - */ - protected function readWMSInformation($qgsLoad) - { - - // Default metadata - $WMSServiceTitle = ''; - $WMSServiceAbstract = ''; - $WMSKeywordList = ''; - $WMSExtent = ''; - $ProjectCrs = ''; - $WMSOnlineResource = ''; - $WMSContactMail = ''; - $WMSContactOrganization = ''; - $WMSContactPerson = ''; - $WMSContactPhone = ''; - if ($qgsLoad) { - $WMSServiceTitle = (string) $qgsLoad->properties->WMSServiceTitle; - $WMSServiceAbstract = (string) $qgsLoad->properties->WMSServiceAbstract; - - if (property_exists($qgsLoad->properties, 'WMSKeywordList')) { - $values = array(); - foreach ($qgsLoad->properties->WMSKeywordList->value as $value) { - if ((string) $value !== '') { - $values[] = (string) $value; - } - } - $WMSKeywordList = implode(', ', $values); - } - - if (property_exists($qgsLoad->properties->WMSExtent, 'value') - && $qgsLoad->properties->WMSExtent->value !== null) { - $WMSExtent = $qgsLoad->properties->WMSExtent->value[0]; - $WMSExtent .= ', '.$qgsLoad->properties->WMSExtent->value[1]; - $WMSExtent .= ', '.$qgsLoad->properties->WMSExtent->value[2]; - $WMSExtent .= ', '.$qgsLoad->properties->WMSExtent->value[3]; - } - $WMSOnlineResource = (string) $qgsLoad->properties->WMSOnlineResource; - $WMSContactMail = (string) $qgsLoad->properties->WMSContactMail; - $WMSContactOrganization = (string) $qgsLoad->properties->WMSContactOrganization; - $WMSContactPerson = (string) $qgsLoad->properties->WMSContactPerson; - $WMSContactPhone = (string) $qgsLoad->properties->WMSContactPhone; - } - if (isset($qgsLoad->mapcanvas)) { - $ProjectCrs = (string) $qgsLoad->mapcanvas->destinationsrs->spatialrefsys->authid; - } - - return array( - 'WMSServiceTitle' => $WMSServiceTitle, - 'WMSServiceAbstract' => $WMSServiceAbstract, - 'WMSKeywordList' => $WMSKeywordList, - 'WMSExtent' => $WMSExtent, - 'ProjectCrs' => $ProjectCrs, - 'WMSOnlineResource' => $WMSOnlineResource, - 'WMSContactMail' => $WMSContactMail, - 'WMSContactOrganization' => $WMSContactOrganization, - 'WMSContactPerson' => $WMSContactPerson, - 'WMSContactPhone' => $WMSContactPhone, - ); - } - /** * @param string $version * @@ -1578,283 +1504,6 @@ protected function convertQgisProjectVersion($version) return (int) $a; } - /** - * @param \SimpleXMLElement $xml - * - * @return int - */ - protected function readQgisProjectVersion($xml) - { - $qgisRoot = $xml->xpath('//qgis'); - $qgisRootZero = $qgisRoot[0]; - - return $this->convertQgisProjectVersion((string) $qgisRootZero->attributes()->version); - } - - /** - * Read the last modified date of the QGS file. - * - * @param string $qgs_path the path to the QGS file - * - * @return string the last saved date contained in the QGS file - */ - protected function readLastSaveDateTime($qgs_path) - { - $fp = fopen($qgs_path, 'r'); - $version = ''; - for ($i = 0; $i < 5; ++$i) { - $line = fgets($fp); - if (preg_match('/saveDateTime="(?P[\S]*)"/', $line, $matches)) { - $version = $matches['date']; - - break; - } - } - fclose($fp); - - return $version; - } - - /** - * @param \SimpleXMLElement $xml - * - * @return string - */ - protected function readCanvasColor($xml) - { - $red = $xml->xpath('//properties/Gui/CanvasColorRedPart'); - $green = $xml->xpath('//properties/Gui/CanvasColorGreenPart'); - $blue = $xml->xpath('//properties/Gui/CanvasColorBluePart'); - - return 'rgb('.$red[0].','.$green[0].','.$blue[0].')'; - } - - /** - * @param \SimpleXMLElement $xml - * - * @return array - */ - protected function readAllProj4($xml) - { - $srsList = array(); - $spatialrefsys = $xml->xpath('//spatialrefsys'); - foreach ($spatialrefsys as $srs) { - $srsList[(string) $srs->authid] = (string) $srs->proj4; - } - - return $srsList; - } - - /** - * @param \SimpleXMLElement $xml - * - * @return null|array[] - */ - protected function readThemes($xml) - { - $xmlThemes = $xml->xpath('//visibility-presets'); - $themes = array(); - - if ($xmlThemes) { - foreach ($xmlThemes[0] as $theme) { - $themeObj = $theme->attributes(); - if (!array_key_exists((string) $themeObj->name, $themes)) { - $themes[(string) $themeObj->name] = array(); - } - - // Copy layers and their attributes - foreach ($theme->layer as $layer) { - $layerObj = $layer->attributes(); - // Since QGIS 3.26, theme contains every layers with visible attributes - // before only visible layers are in theme - // So do not keep layer with visible != '1' if it is defined - if (isset($layerObj->visible) && (string) $layerObj->visible != '1') { - continue; - } - $themes[(string) $themeObj->name]['layers'][(string) $layerObj->id] = array( - 'style' => (string) $layerObj->style, - 'expanded' => (string) $layerObj->expanded, - ); - } - - // Copy checked group nodes - if (isset($theme->{'checked-group-nodes'}->{'checked-group-node'})) { - foreach ($theme->{'checked-group-nodes'}->{'checked-group-node'} as $checkedGroupNode) { - $checkedGroupNodeObj = $checkedGroupNode->attributes(); - $themes[(string) $themeObj->name]['checkedGroupNode'][] = (string) $checkedGroupNodeObj->id; - } - } - - // Copy expanded group nodes - if (isset($theme->{'expanded-group-nodes'}->{'expanded-group-node'})) { - foreach ($theme->{'expanded-group-nodes'}->{'expanded-group-node'} as $expandedGroupNode) { - $expandedGroupNodeObj = $expandedGroupNode->attributes(); - $themes[(string) $themeObj->name]['expandedGroupNode'][] = (string) $expandedGroupNodeObj->id; - } - } - - // Copy expanded legend nodes - if (isset($theme->{'expanded-legend-nodes'}->{'expanded-legend-node'})) { - foreach ($theme->{'expanded-legend-nodes'}->{'expanded-legend-node'} as $expandedLegendNode) { - $expandedLegendNodeObj = $expandedLegendNode->attributes(); - $themes[(string) $themeObj->name]['expandedLegendNode'][] = (string) $expandedLegendNodeObj->id; - } - } - } - - return $themes; - } - - return null; - } - - /** - * @param \SimpleXMLElement $xml - * - * @return null|array array of custom variable name => variable value - */ - protected function readCustomProjectVariables($xml) - { - $xmlCustomProjectVariables = $xml->xpath('//properties/Variables'); - $customProjectVariables = array(); - - if ($xmlCustomProjectVariables) { - $variableIndex = 0; - foreach ($xmlCustomProjectVariables[0]->variableNames->value as $variableName) { - $customProjectVariables[(string) $variableName] = (string) $xmlCustomProjectVariables[0]->variableValues->value[$variableIndex]; - ++$variableIndex; - } - - return $customProjectVariables; - } - - return null; - } - - /** - * @param \SimpleXMLElement $xml - * - * @return null|array[] list of two list: reference between relation, and fields of relations - */ - protected function readRelations($xml) - { - $xmlRelations = $xml->xpath('//relations'); - $relations = array(); - $relationsFields = array(); - $pivotGather = array(); - $pivot = array(); - if ($xmlRelations) { - // Store qgisProjects references in a key=>value array and pass it by reference along the methods that loads and validates relations. - // This avoid to reload the same QgisProject instance multiple times, if there are many "embedded relations" referencing the same (embedded) qgis project - /** @var \SimpleXMLElement $relation */ - foreach ($xmlRelations[0] as $relation) { - $relationObj = $relation->attributes(); - - $relationField = $this->readRelationField($relation); - if ($relationField === null) { - // no corresponding layer - continue; - } - $relationsFields[] = $relationField; - - $referencedLayerId = (string) $relationObj->referencedLayer; - $referencingLayerId = (string) $relationObj->referencingLayer; - if (!array_key_exists($referencedLayerId, $relations)) { - $relations[$referencedLayerId] = array(); - } - $relations[$referencedLayerId][] = array( - 'referencingLayer' => $referencingLayerId, - 'referencedField' => $relationField['referencedField'], - 'referencingField' => $relationField['referencingField'], - 'previewField' => $relationField['previewField'], - 'relationName' => (string) $relationObj->name, - 'relationId' => (string) $relationObj->id, - ); - - if (!array_key_exists($referencingLayerId, $pivotGather)) { - $pivotGather[$referencingLayerId] = array(); - } - - $pivotGather[$referencingLayerId][$referencedLayerId] = $relationField['referencingField']; - } - - // Keep only child with at least two parents - foreach ($pivotGather as $pi => $vo) { - if (count($vo) > 1) { - $pivot[$pi] = $vo; - } - } - $relations['pivot'] = $pivot; - - return array($relations, $relationsFields); - } - - return null; - } - - /** - * @param \SimpleXMLElement $relationXml - */ - protected function readRelationField($relationXml) - { - $referencedLayerId = $relationXml->attributes()->referencedLayer; - - $_referencedLayerXml = $this->getXmlLayer($referencedLayerId); - if (count($_referencedLayerXml) == 0) { - return null; - } - $referencedLayerXml = $_referencedLayerXml[0]; - - $_layerName = $referencedLayerXml->xpath('layername'); - if (count($_layerName) == 0) { - return null; - } - $layerName = (string) $_layerName[0]; - $typeName = str_replace(' ', '_', $layerName); - $_shortname = $referencedLayerXml->xpath('shortname'); - if (count($_shortname) > 0) { - $shortname = (string) $_shortname[0]; - if (!empty($shortname)) { - $typeName = $shortname; - } - } - $referencedField = (string) $relationXml->fieldRef->attributes()->referencedField; - $referencingField = (string) $relationXml->fieldRef->attributes()->referencingField; - - $referenceField = array( - 'id' => (string) $relationXml->attributes()->id, - 'layerName' => $layerName, - 'typeName' => $typeName, - 'propertyName' => '', - 'filterExpression' => '', - 'referencedField' => $referencedField, - 'referencingField' => $referencingField, - 'previewField' => '', - ); - - $_previewExpression = $referencedLayerXml->xpath('previewExpression'); - if (count($_previewExpression) == 0) { - return $referenceField; - } - $previewExpression = (string) $_previewExpression[0]; - - $previewField = $previewExpression; - if (substr($previewField, 0, 8) == 'COALESCE') { - if (preg_match('/"([\S ]+)"/', $previewField, $matches) == 1) { - $previewField = $matches[1]; - } else { - $previewField = $referencedField; - } - } elseif (substr($previewField, 0, 1) == '"' and substr($previewField, -1) == '"') { - $previewField = substr($previewField, 1, -1); - } - - $referenceField['propertyName'] = $referencedField.','.$previewField; - $referenceField['previewField'] = $previewField; - - return $referenceField; - } - public function getRelationField($relationId) { $fields = array_filter($this->relationsFields, function ($rf) use ($relationId) { @@ -1870,228 +1519,6 @@ public function getRelationField($relationId) return null; } - /** - * @param \SimpleXMLElement $xml - * - * @return bool - */ - protected function readUseLayerIDs($xml) - { - $WMSUseLayerIDs = $xml->xpath('//properties/WMSUseLayerIDs'); - - return $WMSUseLayerIDs && $WMSUseLayerIDs[0] == 'true'; - } - - /** - * @param \SimpleXMLElement $xml - * - * @throws \Exception - * - * @return array list of layers. Each item is a list of layer properties - */ - protected function readLayers($xml) - { - $xmlLayers = $xml->xpath('//maplayer'); - $layers = array(); - if (!$xmlLayers) { - return $layers; - } - // Associative array that stores the embedded projects path as key and the list of embedded layers attributes as value. - // The the embedded layers definition are retreived outside the main foreach loop to avoid loading the same embedded qgis project multiple times - // for each embedded layer - $embeddedProjects = array(); - - foreach ($xmlLayers as $xmlLayer) { - $attributes = $xmlLayer->attributes(); - if (isset($attributes['embedded']) && (string) $attributes->embedded == '1') { - $xmlFile = realpath(dirname($this->path).DIRECTORY_SEPARATOR.(string) $attributes->project); - if (!array_key_exists($xmlFile, $embeddedProjects)) { - $embeddedProjects[$xmlFile] = array(); - } - // populate array of embedded layers - $embeddedProjects[$xmlFile][] = $attributes; - } else { - $layer = array( - 'type' => (string) $attributes->type, - 'id' => (string) $xmlLayer->id, - 'name' => (string) $xmlLayer->layername, - 'shortname' => (string) $xmlLayer->shortname, - 'title' => (string) $xmlLayer->title, - 'abstract' => (string) $xmlLayer->abstract, - 'proj4' => (string) $xmlLayer->srs->spatialrefsys->proj4, - 'srid' => (int) $xmlLayer->srs->spatialrefsys->srid, - 'authid' => (int) $xmlLayer->srs->spatialrefsys->authid, - 'datasource' => (string) $xmlLayer->datasource, - 'provider' => (string) $xmlLayer->provider, - 'keywords' => array(), - ); - $keywords = $xmlLayer->xpath('./keywordList/value'); - if ($keywords) { - foreach ($keywords as $keyword) { - if ((string) $keyword != '') { - $layer['keywords'][] = (string) $keyword; - } - } - } - - if ($layer['title'] == '') { - $layer['title'] = $layer['name']; - } - if ($layer['type'] == 'vector') { - $fields = array(); - $wfsFields = array(); - $aliases = array(); - $defaults = array(); - $constraints = array(); - $webDavFields = array(); - $webDavBaseUris = array(); - $edittypes = $xmlLayer->xpath('.//edittype'); - if ($edittypes) { - foreach ($edittypes as $edittype) { - $field = (string) $edittype->attributes()->name; - if (in_array($field, $fields)) { - continue; // QGIS sometimes stores them twice - } - $fields[] = $field; - $wfsFields[] = $field; - $aliases[$field] = $field; - $defaults[$field] = null; - $constraints[$field] = null; - } - } else { - $fieldconfigurations = $xmlLayer->xpath('.//fieldConfiguration/field'); - if ($fieldconfigurations) { - foreach ($fieldconfigurations as $fieldconfiguration) { - $field = (string) $fieldconfiguration->attributes()->name; - if (in_array($field, $fields)) { - continue; // QGIS sometimes stores them twice - } - $fields[] = $field; - $wfsFields[] = $field; - $aliases[$field] = $field; - $defaults[$field] = null; - $constraints[$field] = null; - // check for storage type - $storage = $fieldconfiguration->xpath('.//editWidget/config/Option/Option[@name="StorageType"]'); - if ($storage && count($storage) == 1) { - // expecting only one record - if ($storage[0]->attributes()->value == 'WebDAV') { - $storageUrlExpression = $fieldconfiguration->xpath('.//editWidget/config/Option/Option[@name="PropertyCollection"]/Option[@name="properties"]/Option[@name="storageUrl"]/Option[@name="expression"]'); - if ($storageUrlExpression && count($storageUrlExpression) == 1) { - if ($storageUrlExpression[0]->attributes()->value) { - $webDavFields[] = $field; - $webDavBaseUris[] = (string) $storageUrlExpression[0]->attributes()->value; - } - } - } - } - } - } - } - - if (isset($xmlLayer->aliases->alias)) { - foreach ($xmlLayer->aliases->alias as $alias) { - $aliases[(string) $alias['field']] = (string) $alias['name']; - } - } - - if (isset($xmlLayer->defaults->default)) { - foreach ($xmlLayer->defaults->default as $default) { - $defaults[(string) $default['field']] = (string) $default['expression']; - } - } - - if (isset($xmlLayer->constraints->constraint)) { - foreach ($xmlLayer->constraints->constraint as $constraint) { - $c = array( - 'constraints' => 0, - 'notNull' => false, - 'unique' => false, - 'exp' => false, - ); - $c['constraints'] = (int) $constraint['constraints']; - if ($c['constraints'] > 0) { - $c['notNull'] = ((int) $constraint['notnull_strength'] > 0); - $c['unique'] = ((int) $constraint['unique_strength'] > 0); - $c['exp'] = ((int) $constraint['exp_strength'] > 0); - } - $constraints[(string) $constraint['field']] = $c; - } - } - - if (isset($xmlLayer->constraintExpressions->constraint)) { - foreach ($xmlLayer->constraintExpressions->constraint as $constraint) { - $f = (string) $constraint['field']; - $c = array( - 'constraints' => 0, - 'notNull' => false, - 'unique' => false, - 'exp' => false, - ); - if (array_key_exists($f, $constraints)) { - $c = $constraints[$f]; - } - $exp_val = (string) $constraint['exp']; - if ($exp_val !== '') { - $c['exp'] = true; - $c['exp_value'] = $exp_val; - $c['exp_desc'] = (string) $constraint['desc']; - } - $constraints[$f] = $c; - } - } - - $layer['fields'] = $fields; - $layer['aliases'] = $aliases; - $layer['defaults'] = $defaults; - $layer['constraints'] = $constraints; - $layer['wfsFields'] = $wfsFields; - $layer['webDavFields'] = $webDavFields; - $layer['webDavBaseUris'] = $webDavBaseUris; - - // Do not expose fields with HideFromWfs parameter - // Format in .qgs has changed in QGIS 3.16 - if ($this->qgisProjectVersion >= 31600) { - $excludeFields = $xmlLayer->xpath('.//field[contains(@configurationFlags,"HideFromWfs")]/@name'); - } else { - $excludeFields = $xmlLayer->xpath('.//excludeAttributesWFS/attribute'); - } - - if ($excludeFields && is_array($excludeFields)) { - foreach ($excludeFields as $eField) { - $eField = (string) $eField; - if (!in_array($eField, $wfsFields)) { - continue; // QGIS sometimes stores them twice - } - array_splice($wfsFields, array_search($eField, $wfsFields), 1); - } - $layer['wfsFields'] = $wfsFields; - } - } - $layers[] = $layer; - } - } - // loop through the embedded projects if any, to get the embedded layers definition - foreach ($embeddedProjects as $projectPath => $layersAttributes) { - if (is_array($layersAttributes)) { - $embeddedProject = new QgisProject($projectPath, $this->services, $this->appContext); - foreach ($layersAttributes as $attributes) { - $layer = $embeddedProject->getLayerDefinition((string) $attributes->id); - $layer['qgsmtime'] = filemtime($projectPath); - $layer['file'] = $projectPath; - $layer['embedded'] = 1; - $layer['projectPath'] = (string) $attributes->project; - $layers[] = $layer; - } - if (!array_key_exists($projectPath, $this->qgisEmbeddedProjects)) { - $this->qgisEmbeddedProjects[$projectPath] = $embeddedProject; - } - } - } - - return $layers; - } - protected function readUploadOptions($fieldEditType, &$fieldEditOptions) { $mimeTypes = array(); diff --git a/tests/units/classes/Project/QgisProjectTest.php b/tests/units/classes/Project/QgisProjectTest.php index a1d076aff2..e913192629 100644 --- a/tests/units/classes/Project/QgisProjectTest.php +++ b/tests/units/classes/Project/QgisProjectTest.php @@ -30,289 +30,6 @@ public function arrayToXml(array $array, SimpleXMLElement &$xml) } } - public function testReadWMSInfo() - { - $data = array( - 'mapcanvas' => array( - 'destinationsrs' => array('spatialrefsys' => array('authid' => 'CRS4242')), - ), - 'properties' => array( - 'WMSServiceTitle' => 'title', - 'WMSServiceAbstract' => 'abstract', - 'WMSKeywordList' => array( - 'value' => array('key', 'word', 'WMS'), - ), - 'WMSExtent' => array( - 'value' => array('42', '24', '21', '12'), - ), - 'WMSOnlineResource' => 'ressource', - 'WMSContactMail' => 'test.mail@3liz.org', - 'WMSContactOrganization' => '3liz', - 'WMSContactPerson' => 'marvin', - 'WMSContactPhone' => '', - ), - ); - $expectedWMS = array( - 'WMSServiceTitle' => 'title', - 'WMSServiceAbstract' => 'abstract', - 'WMSKeywordList' => 'key, word, WMS', - 'WMSExtent' => '42, 24, 21, 12', - 'ProjectCrs' => 'CRS4242', - 'WMSOnlineResource' => 'ressource', - 'WMSContactMail' => 'test.mail@3liz.org', - 'WMSContactOrganization' => '3liz', - 'WMSContactPerson' => 'marvin', - 'WMSContactPhone' => '', - ); - $xml = new SimpleXMLElement(''); - $this->arrayToXml($data, $xml); - $testQgis = new qgisProjectForTests(); - $this->assertEquals($expectedWMS, $testQgis->readWMSInfoTest($xml)); - } - - public function testReadCanvasColor() - { - $data = array( - 'properties' => array( - 'Gui' => array( - 'CanvasColorGreenPart' => '21', - 'CanvasColorRedPart' => '42', - 'CanvasColorBluePart' => '84', - ), - ), - ); - $xml = new SimpleXMLElement(''); - $this->arrayToXml($data, $xml); - $testQgis = new qgisProjectForTests(); - $this->assertEquals('rgb(42,21,84)', $testQgis->readCanvasColorTest($xml)); - } - - public function testReadAllProj4() - { - $data = array( - array('spatialrefsys' => array( - 'authid' => 'CRS1', - 'proj4' => '1', - )), - array('spatialrefsys' => array( - 'authid' => 'CRS2', - 'proj4' => '2', - )), - array('spatialrefsys' => array( - 'authid' => 'CRS3', - 'proj4' => '3', - )), - array('spatialrefsys' => array( - 'authid' => 'CRS4', - 'proj4' => '4', - )), - ); - $expectedProj4 = array( - 'CRS1' => '1', - 'CRS2' => '2', - 'CRS3' => '3', - 'CRS4' => '4', - ); - $xml = new SimpleXMLElement(''); - $this->arrayToXml($data, $xml); - $testQgis = new qgisProjectForTests(); - $this->assertEquals($expectedProj4, $testQgis->readAllProj4Test($xml)); - } - - public function testReadUseLayersIDs() - { - $data = array( - 'properties' => array( - 'WMSUseLayerIDs' => 'false', - ), - ); - $xml = new SimpleXMLElement(''); - $this->arrayToXml($data, $xml); - $testQgis = new qgisProjectForTests(); - $this->assertFalse($testQgis->readUseLayersIDsTest($xml)); - } - - public function testReadThemes() - { - $expectedThemes = array( - 'Administrative' => array( - 'layers' => array( - 'SousQuartiers20160121124316563' => array( - 'style' => 'default', - 'expanded' => '1', - ), - 'VilleMTP_MTP_Quartiers_2011_432620130116112610876' => array( - 'style' => 'default', - 'expanded' => '0', - ), - ), - 'expandedGroupNode' => array( - 'datalayers/Buildings', - 'Overview', - 'datalayers/Bus', - 'datalayers', - ), - ), - ); - $file = __DIR__.'/Ressources/themes.qgs'; - $xml = simplexml_load_file($file); - $testQgis = new qgisProjectForTests(); - $themes = $testQgis->readThemesForTests($xml); - $this->assertEquals($expectedThemes, $themes); - - $expectedThemes = array( - 'theme2' => array( - 'layers' => array( - 'sousquartiers_7c49d0fc_0ee0_4308_a66d_45c144e59872' => array( - 'style' => 'défaut', - 'expanded' => '1', - ), - 'quartiers_ef5b13e3_36db_4e0d_98b3_990de580367d' => array( - 'style' => 'style2', - 'expanded' => '1', - ), - ), - 'expandedGroupNode' => array( - 'group1', - ), - 'checkedGroupNode' => array( - 'group1', - ), - ), - 'theme1' => array( - 'layers' => array( - 'quartiers_ef5b13e3_36db_4e0d_98b3_990de580367d' => array( - 'style' => 'style1', - 'expanded' => '1', - ), - ), - 'expandedGroupNode' => array( - 'group1', - ), - ), - ); - $file = __DIR__.'/Ressources/themes-3_22.qgs'; - $xml = simplexml_load_file($file); - $testQgis = new qgisProjectForTests(); - $themes = $testQgis->readThemesForTests($xml); - $this->assertEquals($expectedThemes, $themes); - - $file = __DIR__.'/Ressources/themes-3_26.qgs'; - $xml = simplexml_load_file($file); - $testQgis = new qgisProjectForTests(); - $themes = $testQgis->readThemesForTests($xml); - $this->assertEquals($expectedThemes, $themes); - } - - public function testReadCustomProjectVariables() - { - $expectedCustomProjectVariables = array( - 'lizmap_user' => 'lizmap', - 'lizmap_user_groups' => 'lizmap-group', - ); - $file = __DIR__.'/Ressources/customProjectVariables.qgs'; - $xml = simplexml_load_file($file); - $testQgis = new qgisProjectForTests(); - $customProjectVariables = $testQgis->readCustomProjectVariablesForTests($xml); - $this->assertEquals($expectedCustomProjectVariables, $customProjectVariables); - } - - public function testReadLayers() - { - // Test if WFS 'label' field is not exposed in 3.10 and 3.16 - $expectedWfsFields = array( - 0 => 'id', - ); - $testQgis = new qgisProjectForTests(); - - $fileVersions = array('310', '316'); - - foreach ($fileVersions as $fileVersion) { - $xml = simplexml_load_file(__DIR__.'/Ressources/readLayers_'.$fileVersion.'.qgs'); - $layers = $testQgis->readLayersForTests($xml); - $this->assertEquals($expectedWfsFields, $layers[0]['wfsFields']); - } - } - - public function testReadQgisMetadataFromXml() - { - $testQgis = new qgisProjectForTests(); - $xml = simplexml_load_file(__DIR__.'/Ressources/readLayers_316.qgs'); - $this->assertEquals('31607', $testQgis->readQgisVersionForTests($xml)); - - $testQgis = new qgisProjectForTests(); - $xml = simplexml_load_file(__DIR__.'/Ressources/readLayers_310.qgs'); - $this->assertEquals('31004', $testQgis->readQgisVersionForTests($xml)); - } - - public function testReadQgisMetadataFromLines() - { - $testQgis = new qgisProjectForTests(); - $xml_path = __DIR__.'/Ressources/readLayers_316.qgs'; - $this->assertEquals('2021-06-14T11:50:51', $testQgis->readLastSaveDateTimeForTests($xml_path)); - - $testQgis = new qgisProjectForTests(); - $xml_path = __DIR__.'/Ressources/readLayers_310.qgs'; - $this->assertEquals('', $testQgis->readLastSaveDateTimeForTests($xml_path)); - } - - public function testReadRelations() - { - $expectedRelations = array( - 'VilleMTP_MTP_Quartiers_2011_432620130116112610876' => array( - array('referencingLayer' => 'SousQuartiers20160121124316563', - 'referencedField' => 'QUARTMNO', - 'referencingField' => 'QUARTMNO', - 'previewField' => 'LIBQUART', - 'relationName' => 'Subdistricts by district', - 'relationId' => 'SousQuartiers20160121124316563_QUARTMNO_VilleMTP_MTP_Quartiers_2011_432620130116112610876_QUARTMNO', - - ), - ), - 'tramstop20150328114203878' => array( - array('referencingLayer' => 'jointure_tram_stop20150328114216806', - 'referencedField' => 'osm_id', - 'referencingField' => 'stop_id', - 'previewField' => 'unique_name', - 'relationName' => 'Tram stop -> pivot tram stop/tram line', - 'relationId' => 'jointure_tram_stop20150328114216806_stop_id_tramstop20150328114203878_osm_id', - - ), - ), - 'pivot' => array(), - ); - - $expectedFields = array( - array ( - 'id' => 'SousQuartiers20160121124316563_QUARTMNO_VilleMTP_MTP_Quartiers_2011_432620130116112610876_QUARTMNO', - 'layerName' => 'Quartiers', - 'typeName' => 'Quartiers', - 'propertyName' => 'QUARTMNO,LIBQUART', - 'filterExpression' => '', - 'referencedField' => 'QUARTMNO', - 'referencingField' => 'QUARTMNO', - 'previewField' => 'LIBQUART', - ), - array ( - 'id' => 'jointure_tram_stop20150328114216806_stop_id_tramstop20150328114203878_osm_id', - 'layerName' => 'tramstop', - 'typeName' => 'tramstop', - 'propertyName' => 'osm_id,unique_name', - 'filterExpression' => '', - 'referencedField' => 'osm_id', - 'referencingField' => 'stop_id', - 'previewField' => 'unique_name', - ), - ); - - $file = __DIR__.'/Ressources/relations.qgs'; - $xml = simplexml_load_file($file); - $testQgis = new qgisProjectForTests(); - list($relations, $relationsFields) = $testQgis->readRelationsForTests($xml); - $this->assertEquals($expectedRelations, $relations); - $this->assertEquals($expectedFields, $relationsFields); - } - public function testEmbeddedRelation() { $file = __DIR__.'/Ressources/relations_project_embed.qgs'; diff --git a/tests/units/testslib/QgisProjectForTests.php b/tests/units/testslib/QgisProjectForTests.php index ef67591fd6..5c87126ddb 100644 --- a/tests/units/testslib/QgisProjectForTests.php +++ b/tests/units/testslib/QgisProjectForTests.php @@ -28,59 +28,6 @@ public function getRelationsFields() { return $this->relationsFields; } - public function readWMSInfoTest($xml) - { - return $this->readWMSInformation($xml); - } - - public function readCanvasColorTest($xml) - { - return $this->readCanvasColor($xml); - } - - public function readAllProj4Test($xml) - { - return $this->readAllProj4($xml); - } - - public function readUseLayersIDsTest($xml) - { - return $this->readUseLayerIDs($xml); - } - - public function readThemesForTests($xml) - { - return $this->readThemes($xml); - } - - public function readCustomProjectVariablesForTests($xml) - { - return $this->readCustomProjectVariables($xml); - } - - public function readLayersForTests($xml) - { - // readLayers() needs $this->qgisProjectVersion to be set - $this->qgisProjectVersion = $this->readQgisProjectVersion($xml); - - return $this->readLayers($xml); - } - - public function readQgisVersionForTests($xml) - { - return $this->readQgisProjectVersion($xml); - } - - public function readLastSaveDateTimeForTests($file) - { - return $this->readLastSaveDateTime($file); - } - - public function readRelationsForTests($xml) - { - $this->xml = $xml; - return $this->readRelations($xml); - } public function setShortNamesForTest($cfg) { From 25c327b2627c710ff8763b3347f9f10f9af2fda1 Mon Sep 17 00:00:00 2001 From: rldhont Date: Fri, 4 Oct 2024 11:59:30 +0200 Subject: [PATCH 39/46] QgisProject remove code in setPropertiesAfterRead methods --- .../lizmap/lib/Project/QgisProject.php | 194 +--- .../units/classes/Project/QgisProjectTest.php | 35 +- .../Ressources/hiddengrouplayer.qgs.cfg | 28 + .../classes/Project/Ressources/opacity.qgs | 972 +++++++++++++++++- tests/units/testslib/QgisProjectForTests.php | 10 + 5 files changed, 1069 insertions(+), 170 deletions(-) create mode 100644 tests/units/classes/Project/Ressources/hiddengrouplayer.qgs.cfg diff --git a/lizmap/modules/lizmap/lib/Project/QgisProject.php b/lizmap/modules/lizmap/lib/Project/QgisProject.php index 940ddcdc38..578765539a 100644 --- a/lizmap/modules/lizmap/lib/Project/QgisProject.php +++ b/lizmap/modules/lizmap/lib/Project/QgisProject.php @@ -381,35 +381,18 @@ public function setPropertiesAfterRead(ProjectConfig $cfg) */ protected function setShortNames(ProjectConfig $cfg) { - if ($this->path) { - $project = Qgis\ProjectInfo::fromQgisPath($this->path); - foreach ($project->projectlayers as $layer) { - if (!isset($layer->shortname)) { - continue; - } - $layerCfg = $cfg->getLayer($layer->layername); - if ($layerCfg) { - $layerCfg->shortname = $layer->shortname; - } - } - + if (!$this->path) { return; } - $shortNames = $this->xpathQuery('//maplayer/shortname'); - if ($shortNames) { - foreach ($shortNames as $sname) { - $sname = (string) $sname; - $xmlLayer = $this->xpathQuery("//maplayer[shortname='{$sname}']"); - if (!$xmlLayer) { - continue; - } - $xmlLayer = $xmlLayer[0]; - $name = (string) $xmlLayer->layername; - $layerCfg = $cfg->getLayer($name); - if ($layerCfg) { - $layerCfg->shortname = $sname; - } + $project = Qgis\ProjectInfo::fromQgisPath($this->path); + foreach ($project->projectlayers as $layer) { + if (!isset($layer->shortname)) { + continue; + } + $layerCfg = $cfg->getLayer($layer->layername); + if ($layerCfg) { + $layerCfg->shortname = $layer->shortname; } } } @@ -419,34 +402,18 @@ protected function setShortNames(ProjectConfig $cfg) */ protected function setLayerOpacity(ProjectConfig $cfg) { - if ($this->path) { - $project = Qgis\ProjectInfo::fromQgisPath($this->path); - foreach ($project->projectlayers as $layer) { - if (!isset($layer->layerOpacity) || $layer->layerOpacity == 1) { - continue; - } - $layerCfg = $cfg->getLayer($layer->layername); - if ($layerCfg) { - $layerCfg->opacity = $layer->layerOpacity; - } - } - + if (!$this->path) { return; } - $layerWithOpacities = $this->xpathQuery('//maplayer/layerOpacity[.!=1]/parent::* | //maplayer/pipe/rasterrenderer/@opacity[.!=1]/ancestor::maplayer'); - if ($layerWithOpacities) { - foreach ($layerWithOpacities as $layerWithOpacity) { - $name = (string) $layerWithOpacity->layername; - $layerCfg = $cfg->getLayer($name); - $opacity = 1; - if ($layerCfg) { - if (isset($layerWithOpacity->layerOpacity)) { - $opacity = (float) $layerWithOpacity->layerOpacity; - } elseif (isset($layerWithOpacity->pipe->rasterrenderer['opacity'])) { - $opacity = (float) $layerWithOpacity->pipe->rasterrenderer['opacity']; - } - $layerCfg->opacity = $opacity; - } + + $project = Qgis\ProjectInfo::fromQgisPath($this->path); + foreach ($project->projectlayers as $layer) { + if (!isset($layer->layerOpacity) || $layer->layerOpacity == 1) { + continue; + } + $layerCfg = $cfg->getLayer($layer->layername); + if ($layerCfg) { + $layerCfg->opacity = $layer->layerOpacity; } } } @@ -456,82 +423,26 @@ protected function setLayerOpacity(ProjectConfig $cfg) */ protected function setLayerGroupData(ProjectConfig $cfg) { - if ($this->path) { - $project = Qgis\ProjectInfo::fromQgisPath($this->path); - $groupShortNames = $project->layerTreeRoot->getGroupShortNames(); - foreach ($groupShortNames as $name => $shortName) { - $layerCfg = $cfg->getLayer($name); - if (!$layerCfg) { - continue; - } - $layerCfg->shortname = $shortName; - } - $groupsMutuallyExclusive = $project->layerTreeRoot->getGroupsMutuallyExclusive(); - foreach ($groupsMutuallyExclusive as $group) { - $layerCfg = $cfg->getLayer($group); - if (!$layerCfg) { - continue; - } - $layerCfg->mutuallyExclusive = 'True'; - } - + if (!$this->path) { return; } - $groupsWithShortName = $this->xpathQuery("//layer-tree-group/customproperties/property[@key='wmsShortName']/parent::*/parent::*"); - if ($groupsWithShortName) { - foreach ($groupsWithShortName as $group) { - $name = (string) $group['name']; - $shortNameProperty = $group->xpath("customproperties/property[@key='wmsShortName']"); - if (!$shortNameProperty) { - continue; - } - - $shortNameProperty = $shortNameProperty[0]; - $sname = (string) $shortNameProperty['value']; - if (!$sname) { - continue; - } - - $layerCfg = $cfg->getLayer($name); - if (!$layerCfg) { - continue; - } - $layerCfg->shortname = $sname; - } - } else { - $groupsWithShortName = $this->xpathQuery("//layer-tree-group/customproperties/Option[@type='Map']/Option[@name='wmsShortName']/parent::*/parent::*/parent::*"); - if ($groupsWithShortName) { - foreach ($groupsWithShortName as $group) { - $name = (string) $group['name']; - $shortNameProperty = $group->xpath("customproperties/Option[@type='Map']/Option[@name='wmsShortName']"); - if (!$shortNameProperty) { - continue; - } - $shortNameProperty = $shortNameProperty[0]; - $sname = (string) $shortNameProperty['value']; - if (!$sname) { - continue; - } - - $layerCfg = $cfg->getLayer($name); - if (!$layerCfg) { - continue; - } - $layerCfg->shortname = $sname; - } + $project = Qgis\ProjectInfo::fromQgisPath($this->path); + $groupShortNames = $project->layerTreeRoot->getGroupShortNames(); + foreach ($groupShortNames as $name => $shortName) { + $layerCfg = $cfg->getLayer($name); + if (!$layerCfg) { + continue; } + $layerCfg->shortname = $shortName; } - - $groupsMutuallyExclusive = $this->xpathQuery("//layer-tree-group[@mutually-exclusive='1']"); - if ($groupsMutuallyExclusive) { - foreach ($groupsMutuallyExclusive as $group) { - $name = (string) $group['name']; - $layerCfg = $cfg->getLayer($name); - if ($layerCfg) { - $layerCfg->mutuallyExclusive = 'True'; - } + $groupsMutuallyExclusive = $project->layerTreeRoot->getGroupsMutuallyExclusive(); + foreach ($groupsMutuallyExclusive as $group) { + $layerCfg = $cfg->getLayer($group); + if (!$layerCfg) { + continue; } + $layerCfg->mutuallyExclusive = 'True'; } } @@ -540,31 +451,18 @@ protected function setLayerGroupData(ProjectConfig $cfg) */ protected function setLayerShowFeatureCount(ProjectConfig $cfg) { - if ($this->path) { - $project = Qgis\ProjectInfo::fromQgisPath($this->path); - $layersShowFeatureCount = $project->layerTreeRoot->getLayersShowFeatureCount(); - foreach ($layersShowFeatureCount as $layer) { - $layerCfg = $cfg->getLayer($layer); - if (!$layerCfg) { - continue; - } - $layerCfg->showFeatureCount = 'True'; - } - + if (!$this->path) { return; } - $layersWithShowFeatureCount = $this->xpathQuery("//layer-tree-layer/customproperties/property[@key='showFeatureCount'][@value='1']/parent::*/parent::*"); - if (!$layersWithShowFeatureCount) { - $layersWithShowFeatureCount = $this->xpathQuery("//layer-tree-layer/customproperties/Option[@type='Map']/Option[@name='showFeatureCount'][@value='1']/parent::*/parent::*/parent::*"); - } - if ($layersWithShowFeatureCount) { - foreach ($layersWithShowFeatureCount as $layer) { - $name = (string) $layer['name']; - $layerCfg = $cfg->getLayer($name); - if ($layerCfg) { - $layerCfg->showFeatureCount = 'True'; - } + + $project = Qgis\ProjectInfo::fromQgisPath($this->path); + $layersShowFeatureCount = $project->layerTreeRoot->getLayersShowFeatureCount(); + foreach ($layersShowFeatureCount as $layer) { + $layerCfg = $cfg->getLayer($layer); + if (!$layerCfg) { + continue; } + $layerCfg->showFeatureCount = 'True'; } } @@ -581,14 +479,6 @@ protected function unsetPropAfterRead(ProjectConfig $cfg) $cfg->removeLayer($layer->layername); } } - } else { - $pluginLayers = $this->xpathQuery('//maplayer[type="plugin"]'); - if ($pluginLayers) { - foreach ($pluginLayers as $layer) { - $name = (string) $layer->layername; - $cfg->removeLayer($name); - } - } } // unset cache for editionLayers diff --git a/tests/units/classes/Project/QgisProjectTest.php b/tests/units/classes/Project/QgisProjectTest.php index e913192629..c00fc59a45 100644 --- a/tests/units/classes/Project/QgisProjectTest.php +++ b/tests/units/classes/Project/QgisProjectTest.php @@ -112,16 +112,47 @@ public function testSetLayerOpacity() { $file = __DIR__.'/Ressources/simpleLayer.qgs.cfg'; $json = json_decode(file_get_contents($file)); +//<<<<<<< HEAD $expectedLayer = unserialize(serialize($json->layers)); +//======= +// $expectedLayer = json_decode(json_encode($json->layers)); +//>>>>>>> 6352dab26 (QgisProject remove code in setPropertiesAfterRead methods) $expectedLayer->montpellier_events->opacity = (float) 0.85; $expectedLayer->local_raster_layer->opacity = (float) 0.6835; $cfg = new Project\ProjectConfig((object) array('layers' => $json->layers)); $testProj = new qgisProjectForTests(); - $testProj->setXmlForTest(simplexml_load_file(__DIR__.'/Ressources/opacity.qgs')); + $testProj->setPath(__DIR__.'/Ressources/opacity.qgs'); $testProj->setLayerOpacityForTest($cfg); $this->assertEquals($expectedLayer, $cfg->getLayers()); } + public function testSetLayerGroupData() + { + $file = __DIR__.'/Ressources/hiddengrouplayer.qgs.cfg'; + $json = json_decode(file_get_contents($file)); + $expectedLayer = json_decode(json_encode($json->layers)); + $expectedLayer->Hidden->shortname = 'Hidden'; + $expectedLayer->Hidden->mutuallyExclusive = 'True'; + $cfg = new Project\ProjectConfig((object) array('layers' => $json->layers)); + $testProj = new qgisProjectForTests(); + $testProj->setPath(__DIR__.'/Ressources/opacity.qgs'); + $testProj->setLayerGroupDataForTest($cfg); + $this->assertEquals($expectedLayer->Hidden, $cfg->getLayers()->Hidden); + } + + public function testSetLayerShowFeatureCount() + { + $file = __DIR__.'/Ressources/simpleLayer.qgs.cfg'; + $json = json_decode(file_get_contents($file)); + $expectedLayer = json_decode(json_encode($json->layers)); + $expectedLayer->montpellier_events->showFeatureCount = 'True'; + $cfg = new Project\ProjectConfig((object) array('layers' => $json->layers)); + $testProj = new qgisProjectForTests(); + $testProj->setPath(__DIR__.'/Ressources/opacity.qgs'); + $testProj->setLayerShowFeatureCountForTest($cfg); + $this->assertEquals($expectedLayer, $cfg->getLayers()); + } + public static function getLayerData() { $layers = array( @@ -448,7 +479,7 @@ public function testSetShortNames($file, $lname, $sname) ), ); $testProj = new qgisProjectForTests(); - $testProj->setXmlForTest(simplexml_load_file($file)); + $testProj->setPath($file); $cfg = new Project\ProjectConfig((object) array('layers' => (object) $layers)); $testProj->setShortNamesForTest($cfg); $layer = $cfg->getLayers(); diff --git a/tests/units/classes/Project/Ressources/hiddengrouplayer.qgs.cfg b/tests/units/classes/Project/Ressources/hiddengrouplayer.qgs.cfg new file mode 100644 index 0000000000..1dbd2c928f --- /dev/null +++ b/tests/units/classes/Project/Ressources/hiddengrouplayer.qgs.cfg @@ -0,0 +1,28 @@ +{ + "layers": { + "Hidden": { + "id": "Hidden", + "name": "Hidden", + "type": "group", + "title": "Hidden", + "abstract": "", + "link": "", + "minScale": 1, + "maxScale": 1000000000000, + "toggled": "True", + "popup": "False", + "popupSource": "lizmap", + "popupTemplate": "", + "popupMaxFeatures": 10, + "popupDisplayChildren": "False", + "noLegendImage": "False", + "groupAsLayer": "False", + "baseLayer": "False", + "displayInLegend": "True", + "singleTile": "False", + "imageFormat": "image/png", + "cached": "False", + "clientCacheExpiration": 300 + } + } +} diff --git a/tests/units/classes/Project/Ressources/opacity.qgs b/tests/units/classes/Project/Ressources/opacity.qgs index e220a0edd9..452d69b440 100644 --- a/tests/units/classes/Project/Ressources/opacity.qgs +++ b/tests/units/classes/Project/Ressources/opacity.qgs @@ -1,27 +1,796 @@ - - + + + + + + + + +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs + 3857 + 3857 + EPSG:3857 + WGS 84 / Pseudo-Mercator + merc + WGS84 + false + + + + + + + + + + + + + + + + + + + + + + osm_mapnik20190220152650417 + osm_stamen_toner20190220152651073 + events_4c3b47b8_3939_4c8c_8e91_55bdb13a2101 + + + + + + + + + + meters + + 417828.47766224615043029 + 5393709.71128619741648436 + 441352.08966992393834516 + 5412981.91368997748941183 + + 0 + + + +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs + 3857 + 3857 + EPSG:3857 + WGS 84 / Pseudo-Mercator + merc + WGS84 + false + + + 0 + + + + + + + + + + + + + + + + + + + degrees + + 0 + 0 + 0 + 0 + + 0 + + + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat + WGS84 + true + + + 0 + + + degrees + + 0 + 0 + 0 + 0 + + 0 + + + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat + WGS84 + true + + + 0 + + + degrees + + 0 + 0 + 0 + 0 + + 0 + + + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat + WGS84 + true + + + 0 + + + + + 3.70694994926452637 + 43.51250076293945313 + 4.07507991790771396 + 43.75249862670898438 + + events_4c3b47b8_3939_4c8c_8e91_55bdb13a2101 + ./edition/events.gpkg|layername=events + Touristic events + + + + montpellier_events + + + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat + WGS84 + true + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + true + + + + + + + + + + + + + ogr + + + + + + + + + + + 1 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + COALESCE( "description", '<NULL>' ) + COALESCE( "description", '<NULL>' ) + + + + + 0 - not opacity 0 - 1 + 0.85 + + + + + + + + + + + - - - - 0 - montpellier_events - 0 - 0.85 - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /home/mdouchin/Documents/3liz/Infra/lizmap-demo/demo + + 0 + /home/mdouchin/Documents/3liz/Infra/lizmap-demo/demo + + 0 + generatedlayout + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + COALESCE( "description", '<NULL>' ) + <div style="padding:10px;"> +<p style="font-weight:bold;font-size:1.2em;">[% "titre" %]</p> + +<p> <i>[% "field_thematique" %]</i></p> + +<p> +[%'<i class="icon-time"></i> ' || format_date( + "field_date", + 'd MMMM yyyy - HH:mm' +)%] +<br/> +[%'<i class="icon-map-marker"></i> ' || "field_lieu"%] +</p> + +<img style="width: 100%;" src="[% "vignette_src" %]" alt="[% "vignette_alt" %]" title="[% "vignette_alt" %]"/> + +<p style="padding: 10px 0px; font-size:0.8em;">[% "description" %]</p> +<p style="font-size:0.8em;"><a href="[% "url" %]" target="_blank">Voir le site</a></p> + +</div> + + + + -20037508.34278924390673637 + -20037508.34278925508260727 + 20037508.34278924390673637 + 20037508.34278924390673637 + + osm_mapnik20190220152650417 + crs=EPSG:3857&format=&type=xyz&url=http://tile.openstreetmap.org/%7Bz%7D/%7Bx%7D/%7By%7D.png + + + + osm-mapnik + + + +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs + 3857 + 3857 + EPSG:3857 + WGS 84 / Pseudo-Mercator + merc + WGS84 + false + + + + + + + + + + + + + + + + 0 + 0 + + + + + false + + + + + wms + + + + + + + + 1 + 1 + 0 + + + + + + + + + None + WholeRaster + Estimated + 0.02 + 0.98 + 2 + + + + + + + 0 + + + + -20037508.34278924390673637 + -20037508.34278925508260727 + 20037508.34278924390673637 + 20037508.34278924390673637 + + osm_stamen_toner20190220152651073 + crs=EPSG:3857&format=&type=xyz&url=http://tile.stamen.com/toner-lite/%7Bz%7D/%7Bx%7D/%7By%7D.png + + + + osm-stamen-toner + + + +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs + 3857 + 3857 + EPSG:3857 + WGS 84 / Pseudo-Mercator + merc + WGS84 + false + + + + + + + + + + + + + + + + 0 + 0 + + + + + false + + + + + wms + + + + + + + + 1 + 1 + 0 + + + + + + + + + None + WholeRaster + Estimated + 0.02 + 0.98 + 2 + + + + + + + 0 + raster_78572dfa_41b3_42da_a9c6_933ead8bad8f ./media/raster.asc @@ -83,4 +852,175 @@ 0 + + + + + + + + + + + + 1 + EPSG:3857 + 3857 + +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs + + + conditions unknown + true + + 16 + 30 + false + 0 + false + 50 + true + false + 0 + + + edition_polygon_34db893a_6765_42e5_aa9a_712b69e30dc2 + events_4c3b47b8_3939_4c8c_8e91_55bdb13a2101 + tramstop_4cdf2dad_6f48_4491_b318_693cc9184208 + + + false + + + + 390483.99668047408340499 + 5375009.91444000415503979 + 477899.4732063576229848 + 5436768.56305211596190929 + + None + 8 + 90 + + + + + + 1 + + + 255 + + true + + + + MU + 2 + true + + + current_layer + to vertex and segment + 10 + 1 + + false + + 150 + 255 + 255 + 0 + 255 + 255 + 255 + + + + false + + + + + + + + + + + + false + + + + + + false + + + + WGS84 + + + + m2 + meters + + + true + + EPSG:4326 + EPSG:3857 + + + + + false + + + + + + + 5000 + + + + 8 + 8 + 8 + 8 + + Touristic events around Montpellier, France + + + false + + + + false + + + + + + + + + + + + + + + + + + + + + + 2000-01-01T00:00:00 + + + diff --git a/tests/units/testslib/QgisProjectForTests.php b/tests/units/testslib/QgisProjectForTests.php index 5c87126ddb..2c7a4510cc 100644 --- a/tests/units/testslib/QgisProjectForTests.php +++ b/tests/units/testslib/QgisProjectForTests.php @@ -39,6 +39,16 @@ public function setLayerOpacityForTest($cfg) return $this->setLayerOpacity($cfg); } + public function setLayerGroupDataForTest($cfg) + { + return $this->setLayerGroupData($cfg); + } + + public function setLayerShowFeatureCountForTest($cfg) + { + return $this->setLayerShowFeatureCount($cfg); + } + public function getTheXmlAttribute() { return $this->xml; From 1df21551e37201e2c59002006a33c94db5ca6146 Mon Sep 17 00:00:00 2001 From: rldhont Date: Fri, 4 Oct 2024 16:23:59 +0200 Subject: [PATCH 40/46] QgisProject remove code in read methods --- .../lizmap/lib/Project/QgisProject.php | 482 ++++-------------- .../units/classes/Project/QgisProjectTest.php | 14 +- 2 files changed, 103 insertions(+), 393 deletions(-) diff --git a/lizmap/modules/lizmap/lib/Project/QgisProject.php b/lizmap/modules/lizmap/lib/Project/QgisProject.php index 578765539a..e36dbd947f 100644 --- a/lizmap/modules/lizmap/lib/Project/QgisProject.php +++ b/lizmap/modules/lizmap/lib/Project/QgisProject.php @@ -744,124 +744,13 @@ public function getLayerNameByIdFromConfig($layerId, $layers) */ public function getPrintTemplates() { - if ($this->path) { - $project = Qgis\ProjectInfo::fromQgisPath($this->path); - - return $project->getLayoutsAsKeyArray(); - } - // get restricted composers - $rComposers = array(); - $restrictedComposers = $this->getXml()->xpath('//properties/WMSRestrictedComposers/value'); - if ($restrictedComposers && is_array($restrictedComposers)) { - foreach ($restrictedComposers as $restrictedComposer) { - $rComposers[] = (string) $restrictedComposer; - } + if (!$this->path) { + return array(); } - $printTemplates = array(); - // get layout qgs project version >= 3 - $layouts = $this->getXml()->xpath('//Layout'); - if ($layouts && is_array($layouts)) { - foreach ($layouts as $layout) { - // test restriction - if (in_array((string) $layout['name'], $rComposers)) { - continue; - } - // get page element - $page = $layout->xpath('PageCollection/LayoutItem[@type="65638"]'); - if (!$page) { - continue; - } - $page = $page[0]; - - $pageSize = explode(',', $page['size']); - // init print template element - $printTemplate = array( - 'title' => (string) $layout['name'], - 'width' => (int) $pageSize[0], - 'height' => (int) $pageSize[1], - 'maps' => array(), - 'labels' => array(), - ); - - // store mapping between uuid and id - $mapUuidId = array(); - // get layout maps - $lMaps = $layout->xpath('LayoutItem[@type="65639"]'); - if ($lMaps && is_array($lMaps)) { - // Convert xml to json config - foreach ($lMaps as $lMap) { - $lMapSize = explode(',', $lMap['size']); - $ptMap = array( - 'id' => 'map'.(string) count($printTemplate['maps']), - 'uuid' => (string) $lMap['uuid'], - 'width' => (int) $lMapSize[0], - 'height' => (int) $lMapSize[1], - ); - // store mapping between uuid and id - $mapUuidId[(string) $lMap['uuid']] = 'map'.(string) count($printTemplate['maps']); - - // Overview - $cMapOverviews = $lMap->xpath('ComposerMapOverview'); - foreach ($cMapOverviews as $cMapOverview) { - if ($cMapOverview and (string) $cMapOverview->attributes()->show !== '0' and (string) $cMapOverview->attributes()->frameMap != '-1') { - // frameMap is an uuid - $ptMap['overviewMap'] = (string) $cMapOverview->attributes()->frameMap; - } - } - // Grid - $cMapGrids = $lMap->xpath('ComposerMapGrid'); - foreach ($cMapGrids as $cMapGrid) { - if ($cMapGrid and (string) $cMapGrid->attributes()->show !== '0') { - $ptMap['grid'] = 'True'; - } - } - - $printTemplate['maps'][] = $ptMap; - } - // Modifying overviewMap to id instead of uuid - foreach ($printTemplate['maps'] as $ptMap) { - if (!array_key_exists('overviewMap', $ptMap)) { - continue; - } - if (!array_key_exists($ptMap['overviewMap'], $mapUuidId)) { - unset($ptMap['overviewMap']); - - continue; - } - $ptMap['overviewMap'] = $mapUuidId[$ptMap['overviewMap']]; - } - } - - // get layout labels - $lLabels = $layout->xpath('LayoutItem[@type="65641"]'); - if ($lLabels && is_array($lLabels)) { - foreach ($lLabels as $lLabel) { - if ((string) $lLabel['id'] == '') { - continue; - } - $printTemplate['labels'][] = array( - 'id' => (string) $lLabel['id'], - 'htmlState' => (int) $lLabel['htmlState'], - 'text' => (string) $lLabel['labelText'], - ); - } - } - - // Atlas - $atlas = $layout->xpath('Atlas'); - if ($atlas) { - $atlas = $atlas[0]; - $printTemplate['atlas'] = array( - 'enabled' => (string) $atlas['enabled'], - 'coverageLayer' => (string) $atlas['coverageLayer'], - ); - } - $printTemplates[] = $printTemplate; - } - } + $project = Qgis\ProjectInfo::fromQgisPath($this->path); - return $printTemplates; + return $project->getLayoutsAsKeyArray(); } /** @@ -869,6 +758,10 @@ public function getPrintTemplates() */ public function readLocateByLayer($locateByLayer) { + if (!$this->path) { + return; + } + // collect layerIds $locateLayerIds = array(); foreach ($locateByLayer as $k => $v) { @@ -876,94 +769,49 @@ public function readLocateByLayer($locateByLayer) } // update locateByLayer with project from path - if ($this->path) { - $project = Qgis\ProjectInfo::fromQgisPath($this->path); - - // update locateByLayer with alias and filter information - foreach ($locateByLayer as $k => $v) { - $updateLocate = false; - $layer = $project->getLayerById($v->layerId); - // Get field alias - $alias = $layer->getFieldAlias($v->fieldName); - if ($alias !== null) { - // Update locate with field alias - $v->fieldAlias = $alias; - $updateLocate = true; - } - if (property_exists($v, 'filterFieldName')) { - // Get filter field alias - $filterAlias = $layer->getFieldAlias($v->filterFieldName); - if ($filterAlias !== null) { - // Update locate with filter field alias - $v->filterFieldAlias = $filterAlias; - $updateLocate = true; - } - } - // Get joins - if ($layer->vectorjoins !== null && count($layer->vectorjoins) > 0) { - if (!property_exists($v, 'vectorjoins')) { - // Add joins to locate - $v->vectorjoins = array(); - $updateLocate = true; - } - foreach ($layer->vectorjoins as $vectorjoin) { - if (in_array($vectorjoin->joinLayerId, $locateLayerIds)) { - // Add join info to locate - $v->vectorjoins[] = (object) array( - 'joinFieldName' => $vectorjoin->joinFieldName, - 'targetFieldName' => $vectorjoin->targetFieldName, - 'joinLayerId' => $vectorjoin->joinLayerId, - ); - $updateLocate = true; - } - } - } - if ($updateLocate) { - // Update locate if needed - $locateByLayer->{$k} = $v; - } - } - - return; - } + $project = Qgis\ProjectInfo::fromQgisPath($this->path); // update locateByLayer with alias and filter information foreach ($locateByLayer as $k => $v) { - $xmlLayer = $this->getXmlLayer2($this->getXml(), $v->layerId); - if (is_null($xmlLayer)) { - continue; - } - // aliases - $alias = $xmlLayer->xpath("aliases/alias[@field='".$v->fieldName."']"); - if ($alias && is_array($alias)) { - $alias = $alias[0]; - $v->fieldAlias = (string) $alias['name']; - $locateByLayer->{$k} = $v; + $updateLocate = false; + $layer = $project->getLayerById($v->layerId); + // Get field alias + $alias = $layer->getFieldAlias($v->fieldName); + if ($alias !== null) { + // Update locate with field alias + $v->fieldAlias = $alias; + $updateLocate = true; } if (property_exists($v, 'filterFieldName')) { - $alias = $xmlLayer->xpath("aliases/alias[@field='".$v->filterFieldName."']"); - if ($alias && is_array($alias)) { - $alias = $alias[0]; - $v->filterFieldAlias = (string) $alias['name']; - $locateByLayer->{$k} = $v; + // Get filter field alias + $filterAlias = $layer->getFieldAlias($v->filterFieldName); + if ($filterAlias !== null) { + // Update locate with filter field alias + $v->filterFieldAlias = $filterAlias; + $updateLocate = true; } } - // vectorjoins - $vectorjoins = $xmlLayer->xpath('vectorjoins/join'); - if ($vectorjoins && is_array($vectorjoins)) { + // Get joins + if ($layer->vectorjoins !== null && count($layer->vectorjoins) > 0) { if (!property_exists($v, 'vectorjoins')) { + // Add joins to locate $v->vectorjoins = array(); + $updateLocate = true; } - foreach ($vectorjoins as $vectorjoin) { - $joinLayerId = (string) $vectorjoin['joinLayerId']; - if (in_array($joinLayerId, $locateLayerIds)) { + foreach ($layer->vectorjoins as $vectorjoin) { + if (in_array($vectorjoin->joinLayerId, $locateLayerIds)) { + // Add join info to locate $v->vectorjoins[] = (object) array( - 'joinFieldName' => (string) $vectorjoin['joinFieldName'], - 'targetFieldName' => (string) $vectorjoin['targetFieldName'], - 'joinLayerId' => (string) $vectorjoin['joinLayerId'], + 'joinFieldName' => $vectorjoin->joinFieldName, + 'targetFieldName' => $vectorjoin->targetFieldName, + 'joinLayerId' => $vectorjoin->joinLayerId, ); + $updateLocate = true; } } + } + if ($updateLocate) { + // Update locate if needed $locateByLayer->{$k} = $v; } } @@ -974,28 +822,11 @@ public function readLocateByLayer($locateByLayer) */ public function readEditionLayers($editionLayers) { - if ($this->path) { - $project = Qgis\ProjectInfo::fromQgisPath($this->path); - foreach ($editionLayers as $key => $obj) { - // Improve performance by getting provider directly from config - // Available for lizmap plugin >= 3.3.2 - if (property_exists($obj, 'provider')) { - if ($obj->provider == 'spatialite') { - unset($editionLayers->{$key}); - } - - continue; - } - // check for embedded layers - $layer = $project->getLayerById($obj->layerId); - if ($layer->provider == 'spatialite') { - unset($editionLayers->{$key}); - } - } - + if (!$this->path) { return; } + $project = Qgis\ProjectInfo::fromQgisPath($this->path); foreach ($editionLayers as $key => $obj) { // Improve performance by getting provider directly from config // Available for lizmap plugin >= 3.3.2 @@ -1007,21 +838,8 @@ public function readEditionLayers($editionLayers) continue; } // check for embedded layers - $qgisProject = $this->getEmbeddedQgisProject($obj->layerId); - if ($qgisProject) { - $xml = $qgisProject->getXml(); - } else { - $xml = $this->getXml(); - } - - // Read layer property from QGIS project XML - $layerXml = $this->getXmlLayer2($xml, $obj->layerId); - if (is_null($layerXml)) { - continue; - } - $provider = $layerXml->xpath('provider'); - $provider = (string) $provider[0]; - if ($provider == 'spatialite') { + $layer = $project->getLayerById($obj->layerId); + if ($layer->provider == 'spatialite') { unset($editionLayers->{$key}); } } @@ -1033,42 +851,25 @@ public function readEditionLayers($editionLayers) */ public function readEditionForms($editionLayers, $proj = null) { - if ($this->path) { - $project = Qgis\ProjectInfo::fromQgisPath($this->path); - foreach ($editionLayers as $key => $obj) { - $layer = $project->getLayerById($obj->layerId); - if ($layer === null) { - continue; - } - if ($layer->type !== 'vector') { - continue; - } - - /** @var Qgis\Layer\VectorLayer $layer */ - $formControls = $layer->getFormControls(); - if ($proj) { - $proj->getCacheHandler()->setEditableLayerFormCache($obj->layerId, $formControls); - } - } - + if (!$this->path) { return; } - $embeddedEditionLayers = array(); + + $project = Qgis\ProjectInfo::fromQgisPath($this->path); foreach ($editionLayers as $key => $obj) { - // check for embedded layers - $qgisProject = $this->getEmbeddedQgisProject($obj->layerId); - if ($qgisProject) { - $xml = $qgisProject->getXml(); - } else { - $xml = $this->getXml(); + $layer = $project->getLayerById($obj->layerId); + if ($layer === null) { + continue; } - - $layerXml = $this->getXmlLayer2($xml, $obj->layerId); - if (is_null($layerXml)) { + if ($layer->type !== 'vector') { continue; } - $formControls = $this->readFormControls($layerXml, $obj->layerId, $proj); - $proj->getCacheHandler()->setEditableLayerFormCache($obj->layerId, $formControls); + + /** @var Qgis\Layer\VectorLayer $layer */ + $formControls = $proj->getCacheHandler()->getEditableLayerFormCache($obj->layerId); + if (!$formControls && $proj) { + $proj->getCacheHandler()->setEditableLayerFormCache($obj->layerId, $layer->getFormControls()); + } } } @@ -1114,88 +915,37 @@ public function getEmbeddedQgisProject($layer) * * This concerns fields with ValueMap, ValueRelation & RelationReference config * - * @param array $layerIds List of layer identifiers - * @param Project $proj + * @param array $layerIds List of layer identifiers + * @param null|Project $proj */ - public function readLayersLabeledFieldsConfig($layerIds, $proj) + public function readLayersLabeledFieldsConfig($layerIds, $proj = null) { // Get QGIS form fields configurations for each layer $layersLabeledFieldsConfig = array(); - if ($this->path) { - $project = Qgis\ProjectInfo::fromQgisPath($this->path); - foreach ($layerIds as $layerId) { - $layer = $project->getLayerById($layerId); - if ($layer === null) { - continue; - } - if ($layer->type !== 'vector') { - continue; - } - - /** @var Qgis\Layer\VectorLayer $layer */ - $formControls = $layer->getFormControls(); - - $fields_config = array(); - foreach ($formControls as $fieldName => $control) { - $editType = $control->getFieldEditType(); - if (!in_array($editType, array('ValueMap', 'ValueRelation', 'RelationReference'))) { - continue; - } - $fields_config[$fieldName] = array( - 'type' => $editType, - ); - if ($editType == 'ValueMap') { - $valueMap = $control->getValueMap(); - if ($valueMap) { - $fields_config[$fieldName]['data'] = $valueMap; - } - } elseif ($editType == 'ValueRelation') { - $valueRelationData = $control->getValueRelationData(); - $fields_config[$fieldName]['source_layer_id'] = $valueRelationData['layer']; - $fields_config[$fieldName]['source_layer'] = $valueRelationData['layerName']; - $fields_config[$fieldName]['code_field'] = $valueRelationData['key']; - $fields_config[$fieldName]['label_field'] = $valueRelationData['value']; - $fields_config[$fieldName]['exp_filter'] = $valueRelationData['filterExpression']; - } else { - // RelationReference - // We need to get the relation properties - $relationReferenceData = $control->getRelationReference(); - $relation = $relationReferenceData['relation']; - $referencedLayerId = $relationReferenceData['referencedLayerId']; - if (!array_key_exists($referencedLayerId, $this->relations)) { - continue; - } - $fields_config[$fieldName]['relation'] = $relation; - $fields_config[$fieldName]['source_layer_id'] = $referencedLayerId; - $fields_config[$fieldName]['source_layer'] = $relationReferenceData['referencedLayerName']; - $fields_config[$fieldName]['code_field'] = $this->relations[$referencedLayerId][0]['referencedField']; - $fields_config[$fieldName]['label_field'] = $this->relations[$referencedLayerId][0]['previewField']; - $fields_config[$fieldName]['exp_filter'] = $relationReferenceData['filterExpression']; - } - } - $layersLabeledFieldsConfig[$layer->layername] = $fields_config; - } - + if (!$this->path) { return $layersLabeledFieldsConfig; } + $project = Qgis\ProjectInfo::fromQgisPath($this->path); foreach ($layerIds as $layerId) { - $qgisProject = $this->getEmbeddedQgisProject($layerId); - - if ($qgisProject) { - $xml = $qgisProject->getXml(); - } else { - $xml = $this->getXml(); + $layer = $project->getLayerById($layerId); + if ($layer === null) { + continue; } - - $layerXml = $this->getXmlLayer2($xml, $layerId); - if (is_null($layerXml)) { + if ($layer->type !== 'vector') { continue; } - $formControls = $this->readFormControls($layerXml, $layerId, $proj); - $getLayer = $this->getLayer($layerId, $proj); - $layerName = $getLayer->getName(); + + /** @var Qgis\Layer\VectorLayer $layer */ + $formControls = array(); + if ($proj) { + $formControls = $proj->getCacheHandler()->getEditableLayerFormCache($layerId); + } + if (!$formControls) { + $formControls = $layer->getFormControls(); + } + $fields_config = array(); foreach ($formControls as $fieldName => $control) { $editType = $control->getFieldEditType(); @@ -1234,8 +984,7 @@ public function readLayersLabeledFieldsConfig($layerIds, $proj) $fields_config[$fieldName]['exp_filter'] = $relationReferenceData['filterExpression']; } } - - $layersLabeledFieldsConfig[$layerName] = $fields_config; + $layersLabeledFieldsConfig[$layer->layername] = $fields_config; } return $layersLabeledFieldsConfig; @@ -1246,23 +995,11 @@ public function readLayersLabeledFieldsConfig($layerIds, $proj) */ public function readAttributeLayers($attributeLayers) { - if ($this->path) { - $project = Qgis\ProjectInfo::fromQgisPath($this->path); - // Get field order & visibility - foreach ($attributeLayers as $key => $obj) { - // Improve performance by getting custom_config status directly from config - // Available for lizmap plugin >= 3.3.3 - if (property_exists($obj, 'custom_config') && $obj->custom_config != 'True') { - continue; - } - - $layer = $project->getLayerById($obj->layerId); - $obj->attributetableconfig = $layer->attributetableconfig->toKeyArray(); - } - + if (!$this->path) { return; } + $project = Qgis\ProjectInfo::fromQgisPath($this->path); // Get field order & visibility foreach ($attributeLayers as $key => $obj) { // Improve performance by getting custom_config status directly from config @@ -1271,21 +1008,8 @@ public function readAttributeLayers($attributeLayers) continue; } - // Read layer property from QGIS project XML - $layerXml = $this->getXmlLayer2($this->getXml(), $obj->layerId); - if (is_null($layerXml)) { - continue; - } - $attributetableconfigXml = $layerXml->xpath('attributetableconfig'); - if (count($attributetableconfigXml) == 0) { - continue; - } - $attributetableconfig = str_replace( - '@', - '', - json_encode($attributetableconfigXml[0]) - ); - $obj->attributetableconfig = json_decode($attributetableconfig); + $layer = $project->getLayerById($obj->layerId); + $obj->attributetableconfig = $layer->attributetableconfig->toKeyArray(); } } @@ -1297,39 +1021,23 @@ public function readAttributeLayers($attributeLayers) public function readLayersOrder($layers) { $layersOrder = array(); - if ($this->path) { - $project = Qgis\ProjectInfo::fromQgisPath($this->path); - $customOrder = $project->layerTreeRoot->customOrder; - if (!$customOrder->enabled) { - return $layersOrder; - } - $lo = 0; - foreach ($customOrder->items as $layerI) { - // Get layer name from config instead of XML for possible embedded layers - $name = $this->getLayerNameByIdFromConfig($layerI, $layers); - if ($name) { - $layersOrder[$name] = $lo; - } - ++$lo; - } - } else { - $customOrder = $this->getXml()->xpath('layer-tree-group/custom-order'); - if (count($customOrder) == 0) { - return $layersOrder; - } - $customOrderZero = $customOrder[0]; - if (intval($customOrderZero->attributes()->enabled) == 1) { - $items = $customOrderZero->xpath('//item'); - $lo = 0; - foreach ($items as $layerI) { - // Get layer name from config instead of XML for possible embedded layers - $name = $this->getLayerNameByIdFromConfig($layerI, $layers); - if ($name) { - $layersOrder[$name] = $lo; - } - ++$lo; - } - } + if (!$this->path) { + return $layersOrder; + } + + $project = Qgis\ProjectInfo::fromQgisPath($this->path); + $customOrder = $project->layerTreeRoot->customOrder; + if (!$customOrder->enabled) { + return $layersOrder; + } + $lo = 0; + foreach ($customOrder->items as $layerI) { + // Get layer name from config instead of XML for possible embedded layers + $name = $this->getLayerNameByIdFromConfig($layerI, $layers); + if ($name) { + $layersOrder[$name] = $lo; + } + ++$lo; } return $layersOrder; diff --git a/tests/units/classes/Project/QgisProjectTest.php b/tests/units/classes/Project/QgisProjectTest.php index c00fc59a45..1d65119715 100644 --- a/tests/units/classes/Project/QgisProjectTest.php +++ b/tests/units/classes/Project/QgisProjectTest.php @@ -1,10 +1,12 @@ editionLayers; $testProj = new qgisProjectForTests(); - $testProj->setXmlForTest(simplexml_load_file($file)); + $testProj->setPath($file); $testProj->readEditionLayersForTest($eLayers); $this->assertEquals($expectedELayer, $eLayers); } @@ -440,15 +442,15 @@ public function testReadAttributeLayer()