From c4514116a1b0eb7bff058ec71e7ba9adf7c4a384 Mon Sep 17 00:00:00 2001 From: Michael Douchin Date: Mon, 4 Dec 2023 10:40:20 +0100 Subject: [PATCH 1/6] Tests - Use LWC 3.6 instead of 3.5 --- tests/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Makefile b/tests/Makefile index 68a6eb9..375ca31 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -8,7 +8,7 @@ LIZMAP_DIR=$(shell pwd)/lizmap LIZMAP_USER_ID:=$(shell id -u) LIZMAP_USER_GID:=$(shell id -g) -LIZMAP_VERSION_TAG:=3.5 +LIZMAP_VERSION_TAG:=3.6 QGIS_VERSION_TAG:=ltr-rc POSTGIS_VERSION_TAG:=13-3 From f0cba66ad6d31dc22daabba17cefed7e3ccd895d Mon Sep 17 00:00:00 2001 From: Michael Douchin Date: Tue, 5 Dec 2023 15:10:37 +0100 Subject: [PATCH 2/6] Rercherche - Ajout d'un onglet de recherche spatiale par intersection des objets d'une couche --- .gitignore | 6 + .../classes/cadastreDockable.listener.php | 41 ++++ .../listParcelleSpatialDatasource.class.php | 194 ++++++++++++++++++ .../listSpatialLayerFieldDatasource.class.php | 87 ++++++++ cadastre/daos/parcelle_info.dao.xml | 60 +++++- cadastre/forms/search.form.xml | 52 ++++- .../locales/en_US/search.UTF-8.properties | 16 +- .../locales/fr_FR/search.UTF-8.properties | 15 +- cadastre/templates/cadastre_search.tpl | 89 +++++--- cadastre/www/cadastre.js | 85 ++++++-- 10 files changed, 597 insertions(+), 48 deletions(-) create mode 100644 cadastre/classes/listParcelleSpatialDatasource.class.php create mode 100644 cadastre/classes/listSpatialLayerFieldDatasource.class.php diff --git a/.gitignore b/.gitignore index c2f6e37..9e4cea3 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,9 @@ tests/lizmap/qgis-server-plugins/ tests/lizmap/cache-python tests/lizmap/local-python tests/sql/majic/*.sql + +# dev tools +*.code-workspace + +# QGIS useless files +*_attachments.zip diff --git a/cadastre/classes/cadastreDockable.listener.php b/cadastre/classes/cadastreDockable.listener.php index 1a7748a..337638d 100644 --- a/cadastre/classes/cadastreDockable.listener.php +++ b/cadastre/classes/cadastreDockable.listener.php @@ -58,6 +58,47 @@ public function onmapDockable($event) $hasMajic = '1'; } $searchForm->setData('has_majic', $hasMajic); + + // Add the PostgreSQL layers of the same database in the 3rd search tab "Spatial" + if ($hasMajic == '1') { + // Get the project + $p = lizmap::getProject($event->repository . '~' . $event->project); + if ($p === null) { + throw new Exception("Spatial search: Unknown repository/project {$event->repository}.'~'.{$event->project}"); + } + // Get the PostgreSQL database info of the Parcelle layer + /** @var \qgisVectorLayer $parcelleLayer The QGIS vector layer instance */ + $parcelleLayer = $p->getLayer($parcelleId); + $parcelleProfile = $parcelleLayer->getDatasourceProfile(30, false); + + // Get the list of PostgreSQL layers + $layers = array(); + foreach ($p->getLayers() as $layer) { + /** @var \qgisVectorLayer $qgisLayer The QGIS vector layer instance */ + $qgisLayer = $p->getLayer($layer->id); + // Only for existing layers + if (!$qgisLayer) { + continue; + } + // Not the parcelle layer itself + if ($qgisLayer->getId() == $parcelleId) { + continue; + } + // Only PostgreSQL layers + if ($qgisLayer->getProvider() != 'postgres') { + continue; + } + // Check if the database is the same as the Parcelle layer + if ($qgisLayer->getDatasourceProfile(30, false) != $parcelleProfile) { + continue; + } + $layers[$layer->id] = $layer->name; + }; + $datasource = new \jFormsStaticDatasource(); + $datasource->data = $layers; + $searchForm->getControl('spatial_layer_id')->datasource = $datasource; + } + $assign = array( 'form' => $searchForm, 'has_majic' => $hasMajic, diff --git a/cadastre/classes/listParcelleSpatialDatasource.class.php b/cadastre/classes/listParcelleSpatialDatasource.class.php new file mode 100644 index 0000000..a3fcf99 --- /dev/null +++ b/cadastre/classes/listParcelleSpatialDatasource.class.php @@ -0,0 +1,194 @@ +getData($this->criteriaFrom[0]); + $project = $form->getData($this->criteriaFrom[1]); + $layerId = $form->getData($this->criteriaFrom[2]); + $spatialLayerId = $form->getData($this->criteriaFrom[3]); + $spatialLayerUseSelected = $form->getData($this->criteriaFrom[4]); + $spatialLayerSelectedIds = $form->getData($this->criteriaFrom[5]); + $spatialLayerField = $form->getData($this->criteriaFrom[6]); + $spatialLayerBuffer = $form->getData($this->criteriaFrom[7]); + + if (empty($spatialLayerId)) { + return array(); + } + $this->profile = cadastreProfile::getWithLayerId($repository, $project, $layerId); + + if ($this->dao === null) { + $this->dao = jDao::get($this->selector, $this->profile); + } + + $searchConditions = jDao::createConditions(); + + $config = cadastreConfig::get($repository, $project); + $layerConditions = null; + $layerSql = cadastreConfig::getLayerSql($repository, $project, $config->parcelle->id); + $polygonFilter = cadastreConfig::getPolygonFilter($repository, $project, $config->parcelle->id); + $loginFilter = cadastreConfig::getLoginFilter($repository, $project, $config->parcelle->id); + $layerFilters = array(); + if ($layerSql !== null) { + $layerFilters[] = $layerSql; + } + if ($polygonFilter !== null) { + $layerFilters[] = $polygonFilter; + } + if ($loginFilter !== null) { + $layerFilters[] = $loginFilter; + } + if (count($layerFilters) != 0) { + if (count($layerFilters) == 1) { + $layerConditions = $layerFilters[0]; + } else { + $layerConditions = '(' . implode(') AND (', $layerFilters) . ')'; + } + } + + foreach ((array) $this->labelProperty as $property) { + $searchConditions->addItemOrder($property, 'asc'); + } + + // Get spatial layer name, table info, fields, etc. + $p = lizmap::getProject($repository . '~' . $project); + if ($p === null) { + throw new Exception("Spatial search: Unknown repository/project {$repository}.'~'.{$project}"); + } + + /** @var \qgisVectorLayer $qgisLayer The QGIS vector layer instance */ + $qgisLayer = $p->getLayer($spatialLayerId); + if (!$qgisLayer) { + throw new Exception("Spatial search: Unknown layer {$layerId} used in spatial search"); + } + + // Get the PostgreSQL information + $dataSourceParameters = $qgisLayer->getDatasourceParameters(); + $spatialLayerSchema = $dataSourceParameters->schema; + $spatialLayerTable = $dataSourceParameters->tablename; + + // Check that the given field exists + $fields = $qgisLayer->getFields(); + if (!empty($spatialLayerField) && !in_array($spatialLayerField, $fields)) { + throw new Exception("Spatial search: the field {$spatialLayerField} does not exist in the table "); + } + + // Get the table primary key field + // Only one field supported + $dbFieldsInfo = $qgisLayer->getDbFieldsInfo(); + $spatialLayerPk = null; + foreach ($dbFieldsInfo->primaryKeys as $key) { + $spatialLayerPk = $key; + + break; + } + + // Get the optional selection + // JS 3.6 : lizMap.config.layers["Parcelles"]['selectedFeatures'] + // JS 3.7 : lizMap.mainLizmap.config.layers["Parcelles"]['selectedFeatures'] + $selectedIds = array(); + if ($spatialLayerUseSelected == 'oui') { + if (!empty($spatialLayerSelectedIds)) { + $selectedIds = explode(',', $spatialLayerSelectedIds); + } else { + $selectedIds = array('-9999999'); + } + } + + // Run the query by using the DAO method + $found = $this->dao->{$this->method}( + $layerConditions, + $searchConditions, + $spatialLayerSchema, + $spatialLayerTable, + $spatialLayerPk, + $selectedIds, + $spatialLayerField, + $spatialLayerBuffer + ); + + $result = array(); + + foreach ($found as $obj) { + $label = $this->buildLabel($obj); + $value = $obj->{$this->valueProperty}; + + $result[$value] = $label; + } + + return $result; + } + + public function getLabel2($key, $form) + { + if ($key === null || $key == '') { + return null; + } + + $repository = $form->getData($this->criteriaFrom[0]); + $project = $form->getData($this->criteriaFrom[1]); + $layerId = $form->getData($this->criteriaFrom[2]); + $this->profile = cadastreProfile::getWithLayerId($repository, $project, $layerId); + + if ($this->dao === null) { + $this->dao = jDao::get($this->selector, $this->profile); + } + + $method = $this->labelMethod; + + $rec = $this->dao->{$method}($key); + + if ($rec) { + return $this->buildLabel($rec); + } + + return null; + } + + protected function buildLabel($rec) + { + $label = ''; + foreach ((array) $this->labelProperty as $property) { + if ((string) $rec->{$property} !== '') { + $label .= $rec->{$property} . $this->labelSeparator; + } + } + if ($this->labelSeparator != '') { + $label = substr($label, 0, -strlen($this->labelSeparator)); + } + + return $label; + } + + public function setCriteriaControls($criteriaFrom = null) + { + if (count($criteriaFrom) !== 8) { + throw new Exception( + '8 criterias needed: repository, project, parcelleLayerId, spatial_layer_id, spatial_layer_use_selected, spatial_layer_selected_ids, spatial_layer_field, spatial_layer_buffer' + ); + } + $this->criteriaFrom = $criteriaFrom; + } +} diff --git a/cadastre/classes/listSpatialLayerFieldDatasource.class.php b/cadastre/classes/listSpatialLayerFieldDatasource.class.php new file mode 100644 index 0000000..571facc --- /dev/null +++ b/cadastre/classes/listSpatialLayerFieldDatasource.class.php @@ -0,0 +1,87 @@ +getData($this->criteriaFrom[0]); + $project = $form->getData($this->criteriaFrom[1]); + $spatialLayerId = $form->getData($this->criteriaFrom[2]); + if (empty($spatialLayerId)) { + return array(); + } + + // Get spatial layer name, table info, fields, etc. + $p = lizmap::getProject($repository . '~' . $project); + if ($p === null) { + throw new Exception("Spatial search: Unknown repository/project {$repository}.'~'.{$project}"); + } + + /** @var \qgisVectorLayer $qgisLayer The QGIS vector layer instance */ + $qgisLayer = $p->getLayer($spatialLayerId); + if (!$qgisLayer) { + throw new Exception("Spatial search: Unknown layer {$spatialLayerId} used in spatial search"); + } + + // Get fields and aliases + $this->fields = $qgisLayer->getFields(); + $this->aliases = $qgisLayer->getAliasFields(); + $result = array(); + foreach ($this->fields as $field) { + $label = $field; + if (array_key_exists($field, $this->aliases) && !empty($this->aliases[$field])) { + $label = $this->aliases[$field]; + } + $result[$field] = $label; + } + + return $result; + } + + public function getLabel2($key, $form) + { + if ($key === null || $key == '') { + return null; + } + $repository = $form->getData($this->criteriaFrom[0]); + $project = $form->getData($this->criteriaFrom[1]); + $spatialLayerId = $form->getData($this->criteriaFrom[2]); + + return $key; + + return null; + } + + protected function buildLabel($key) + { + return $key; + } + + public function setCriteriaControls($criteriaFrom = null) + { + if (count($criteriaFrom) !== 3) { + throw new Exception( + '3 criterias needed: repository, project, spatial_layer_id' + ); + } + $this->criteriaFrom = $criteriaFrom; + } +} diff --git a/cadastre/daos/parcelle_info.dao.xml b/cadastre/daos/parcelle_info.dao.xml index 3817165..6397bce 100644 --- a/cadastre/daos/parcelle_info.dao.xml +++ b/cadastre/daos/parcelle_info.dao.xml @@ -18,8 +18,8 @@ - + selectpattern="parcelle_info.codecommune || ' - ' || CASE WHEN substr(parcelle_info.idu,4,3) != '000' THEN substr(parcelle_info.idu,4,3) || ' - ' ELSE '' END || substr(parcelle_info.idu,7,6)"/> + @@ -354,5 +354,61 @@ ]]> + + + + + + + + + + + + _selectClause; + if (!empty($spatialLayerField)) { + $sql .= ' , i."' . trim($spatialLayerField, " '\n\r\t\v\x00") . '"'; + } + + $sql .= $this->_fromClause; + $sql .= ' JOIN "' . $spatialLayerSchema . '"."' . $spatialLayerTable . '" AS i'; + $sql .= ' ON ST_DWithin(i.geom, "parcelle_info".geom, ' . $this->_prepareValue($spatialLayerBuffer,'string') .')'; + + if (count($spatialLayerSelectedIds) > 0) { + $sql .= ' AND i."' . trim($spatialLayerPk, " '\n\r\t\v\x00") . '" IN ('; + $virg = ''; + foreach($spatialLayerSelectedIds as $v) { + $sql .= $virg . $this->_prepareValue(trim($v), 'string'); + $virg = ', '; + } + $sql .= ' ) '; + } + + $sql .= $this->_whereClause; + + if ($layerConditions || $searchConditions->hasConditions()) { + $sql.= ($this->_whereClause == ''?' WHERE ':' AND '); + } + + if ($layerConditions) { + $sql.= '('. $layerConditions .')'; + } + + if ($searchConditions->hasConditions()) { + if ($layerConditions) { + $sql.= ' AND '; + } + $sql.= '('. $this->_createConditionsClause($searchConditions) .')'; + } + + $sql.= $this->_createOrderClause($searchConditions); + + $rs = $this->_conn->query($sql); + $rs->setFetchMode(8,$this->_DaoRecordClassName); + return $rs; + ]]> + + diff --git a/cadastre/forms/search.form.xml b/cadastre/forms/search.form.xml index 8cf6960..adcd0e0 100644 --- a/cadastre/forms/search.form.xml +++ b/cadastre/forms/search.form.xml @@ -56,6 +56,43 @@ + + + + + + + + + + + + + + + + + + +