diff --git a/src/module_vmap/module/javascript/app/vmap/map/map.js b/src/module_vmap/module/javascript/app/vmap/map/map.js index 7c0ba9a4a13165ccacc01ee5c7584bd9b561101d..fbd9fabb1a9b9305ce02faa83d8b38f4bfa9b227 100644 --- a/src/module_vmap/module/javascript/app/vmap/map/map.js +++ b/src/module_vmap/module/javascript/app/vmap/map/map.js @@ -389,6 +389,12 @@ nsVmap.Map = function () { if (oVmap['properties']['is_mobile']) { this_.trackGPSPosition(); } + + // Termine les dessins par le clic droit + this.oOpenLayersMap_.getViewport().addEventListener('contextmenu', function (evt) { + evt.preventDefault(); + this_.termineDrawInteractions(); + }) }; /** @@ -1209,6 +1215,29 @@ nsVmap.Map.prototype.trackGPSPosition = function () { } }; +/** + * Termine all the draw interactions + * + */ +nsVmap.Map.prototype.termineDrawInteractions = function () { + oVmap.log('nsVmap.Map.prototype.termineDrawInteractions'); + + for (var i = 0; i < this.vmapInteractions_.length; i++) { + if (this.vmapInteractions_[i] instanceof ol.interaction.Draw) { + try { + // Termine le dessin en cours + this.vmapInteractions_[i].finishDrawing(); + // Desactive la fonctionnalié pour ne pas commencer un autre dessin + this.vmapInteractions_[i].setActive(false); + // Réactive la fonctionnalité + setTimeout(angular.bind(this, function(oDrawInteraction){ + oDrawInteraction.setActive(true); + }, this.vmapInteractions_[i]), 500); + } catch (e) {} + } + } +}; + /******************************************** * INTERFACE MOBILE *******************************************/ diff --git a/src/module_vmap/module/javascript/app/vmap/tools/insert.js b/src/module_vmap/module/javascript/app/vmap/tools/insert.js index 803d13add3e95a2916e129c4eb60e6db3b981b01..456136bce313f4f78931835bdeec5e500dec4500 100644 --- a/src/module_vmap/module/javascript/app/vmap/tools/insert.js +++ b/src/module_vmap/module/javascript/app/vmap/tools/insert.js @@ -187,6 +187,7 @@ nsVmap.nsToolsManager.Insert.prototype.inserttoolController = function ($scope, this['snapOptions']['mode'] = oVmap['properties']['snapping']['defaut_snapp_mode']; this['snapOptions']['limit'] = oVmap['properties']['snapping']['defaut_limit']; this['snapOptions']['visible'] = oVmap['properties']['snapping']['defaut_visibility']; + this['snapOptions']['avoidSuperpositions'] = {}; } /** @@ -257,6 +258,11 @@ nsVmap.nsToolsManager.Insert.prototype.inserttoolController = function ($scope, */ $scope['aQueryableBOs'] = oVmap.getMapManager().getQueryableBusinessObjectsAsArray(true); + /** + * Business objects avaliable for avoiding superpositions + */ + $scope['aAvoidSuperpositionsBOs'] = []; + /** * Object to insert * oInsertObject.sFormDefinitionName name of the form (insert, upload, display) @@ -366,8 +372,10 @@ nsVmap.nsToolsManager.Insert.prototype.inserttoolController = function ($scope, // Au changement d'objet métier en cours d'insertion this.$scope_.$watch('selectedBoId', function () { - this_.updateInsertForm(); - this_.selectCurrentBoForSnapping(); + this_.updateInsertForm(null, function(){ + this_.selectCurrentBoForSnapping(); + this_.loadAvoidSuperpositionBos(); + }); }); // Affiche les modales en plein écran pour la version mobile @@ -411,6 +419,7 @@ nsVmap.nsToolsManager.Insert.prototype.inserttoolController.prototype.loadInsert }); this.checkEditionScale(); + this.loadAvoidSuperpositionBos(); }; /** @@ -433,6 +442,40 @@ nsVmap.nsToolsManager.Insert.prototype.inserttoolController.prototype.selectCurr this.checkEditionScale(); }; + +/** + * Load this.aAvoidSuperpositionsBOs + * + */ +nsVmap.nsToolsManager.Insert.prototype.inserttoolController.prototype.loadAvoidSuperpositionBos = function () { + oVmap.log('nsVmap.nsToolsManager.Insert.inserttoolController.loadAvoidSuperpositionBos'); + + var this_ = this; + + // Types de géométries pour le calcul des jointures + var aActiveGeomTypes = ['POLYGON', 'MULTIPOLYGON', 'GEOMETRY', 'GEOMETRYCOLLECTION']; + + // Objets métiers actifs pour la calcul des jointures + this.$scope_['aAvoidSuperpositionsBOs'] = []; + + if (goog.isDefAndNotNull(this.$scope_['oInsertObject']['sGeomType'])) { + + // Si la couche en cours d'insertion est dans aActiveGeomTypes + if (aActiveGeomTypes.indexOf(this.$scope_['oInsertObject']['sGeomType']) !== -1) { + for (var i = 0; i < this_.$scope_['aQueryableBOs'].length; i++) { + + // Si la couche cible est dans aActiveGeomTypes + if (goog.isDefAndNotNull(this_.$scope_['aQueryableBOs'][i]['bo_geom_type'])) { + if(aActiveGeomTypes.indexOf(this_.$scope_['aQueryableBOs'][i]['bo_geom_type']) !== -1){ + + this.$scope_['aAvoidSuperpositionsBOs'].push(this_.$scope_['aQueryableBOs'][i]); + } + } + } + } + } +} + /** * Vérifie que l'échelle en cours respecte les spécification de l'objet métier */ @@ -1068,9 +1111,11 @@ nsVmap.nsToolsManager.Insert.prototype.inserttoolController.prototype.startDrawi var this_ = this; this.draw_.on('drawend', function (evt) { - // Si la géométrie est un cercle, alors elle est remplacée par un polygone à 32 côtés var feature = evt.feature; + // Si la géométrie est un cercle, alors elle est remplacée par un polygone à 32 côtés feature = this_.updateCircleGeoms(feature); + // Évite les superpositions avec les autres couches si besoin + this_.avoidSuperpositions(feature); }, this); @@ -1141,6 +1186,118 @@ nsVmap.nsToolsManager.Insert.prototype.inserttoolController.prototype.updateCirc return feature; }; +/** + * Edit the given feature to avoid superpositions with layers defined in snapOptions.avoidSuperpositions + * @param {ol.Feature} olFeature + * @returns {ol.Feature} + */ +nsVmap.nsToolsManager.Insert.prototype.inserttoolController.prototype.avoidSuperpositions = function (olFeature) { + oVmap.log('nsVmap.nsToolsManager.Insert.inserttoolController.avoidSuperpositions'); + + // Objets métiers à intersecter + var aSuperpositionBos = []; + for (var bo_id in this['snapOptions']['avoidSuperpositions']) { + if (this['snapOptions']['avoidSuperpositions'][bo_id] === true) { + aSuperpositionBos.push(bo_id); + } + } + + // Remplace la géométrie par celle calculée + if (aSuperpositionBos.length > 0) { + this.setDiffGeom_(aSuperpositionBos, olFeature) + } +} + +/** + * Modify the feature geometry to avoid superpositions with BOs in aSuperpositionBos + * + * @param {string} aSuperpositionBos + * @param {ol.Feature} olFeature + * @param {object|undefines} opt_options + */ +nsVmap.nsToolsManager.Insert.prototype.inserttoolController.prototype.setDiffGeom_ = function (aSuperpositionBos, olFeature, opt_options) { + oVmap.log('nsVmap.nsToolsManager.Insert.inserttoolController.setDiffGeom_'); + + if (!goog.isArray(aSuperpositionBos)) { + return 0; + } + if (!goog.isDefAndNotNull(olFeature)) { + return 0; + } + if (!goog.isDefAndNotNull(opt_options)) { + opt_options = { + bo_index: 0 + } + } + + var this_ = this; + var sEWKTGeom = oVmap.getEWKTFromGeom(olFeature.getGeometry()); + + this.getDiffGeom_(aSuperpositionBos[opt_options.bo_index], sEWKTGeom).then(function(sNewEWKTGeom){ + + // Remplace la géométrie + olFeature.setGeometry(oVmap.getGeomFromEWKT(sNewEWKTGeom)); + + // Relance la fonction pour les autres objets métiers + opt_options.bo_index ++; + if (opt_options.bo_index < aSuperpositionBos.length) { + this_.setDiffGeom_(aSuperpositionBos, olFeature, opt_options); + } + }, function(err){ + console.error('Cannot get the diff geom: ', err) + }); + +} + +/** + * Get the diff superposition geometry + * + * @param {string} sBoId + * @param {string} sEWKTGeom + * @return {promise} + */ +nsVmap.nsToolsManager.Insert.prototype.inserttoolController.prototype.getDiffGeom_ = function (sBoId, sEWKTGeom) { + oVmap.log('nsVmap.nsToolsManager.Insert.inserttoolController.getDiffGeom_'); + + var deferred = this.$q_.defer(); + ajaxRequest({ + 'method': 'POST', + 'url': oVmap['properties']['api_url'] + '/vmap/querys/' + sBoId + '/diff_geometry', + 'headers': { + 'X-HTTP-Method-Override': 'GET', + 'Accept': 'application/x-vm-json' + }, + 'data': { + 'intersect_geom': sEWKTGeom + }, + 'scope': this.$scope_, + 'success': function (response) { + + if (!goog.isDefAndNotNull(response['data'])) { + deferred.reject('response.data not defined'); + return 0; + } + if (goog.isDefAndNotNull(response['data']['errorMessage'])) { + deferred.reject(response['data']['errorMessage']); + return 0; + } + if (!goog.isDefAndNotNull(response['data'][0])) { + deferred.reject('response.data[0] not defined'); + return 0; + } + if (!goog.isDefAndNotNull(response['data'][0]['diff_geom'])) { + deferred.reject('response.data[0].diff_geom not defined'); + return 0; + } + deferred.resolve(response['data'][0]['diff_geom']); + }, + 'error': function (response) { + deferred.reject(response); + } + }); + return deferred.promise; +} + /** * Update feature for scope.oInsertObject */ @@ -1478,7 +1635,17 @@ nsVmap.nsToolsManager.Insert.prototype.inserttoolController.prototype.getMobileB nsVmap.nsToolsManager.Insert.prototype.inserttoolController.prototype.showSnappingOptionsModal = function () { oVmap.log('nsVmap.nsToolsManager.Insert.prototype.inserttoolController.prototype.showSnappingOptionsModal'); + var this_ = this; this['tmpSnapOptions'] = angular.copy(this['snapOptions']); + + this.$scope_.$applyAsync(function(){ + for (var i = 0; i < this_.$scope_['aQueryableBOs'].length; i++) { + if (!goog.isDefAndNotNull(this_['tmpSnapOptions']['avoidSuperpositions'][this_.$scope_['aQueryableBOs'][i]['bo_id']])) { + this_['tmpSnapOptions']['avoidSuperpositions'][this_.$scope_['aQueryableBOs'][i]['bo_id']] = false; + } + } + }); + $('#vmap-insert-snap-options-modal').modal('show'); }; @@ -1597,6 +1764,10 @@ nsVmap.nsToolsManager.Insert.prototype.inserttoolController.prototype.loadBoVect oBo['bo_snapping_loaded'] = null; return 0; } + if (!goog.isDefAndNotNull(response['data'][0])) { + oBo['bo_snapping_loaded'] = null; + return 0; + } if (response['data'][0]['count'] > this_['snapOptions']['snappingObjectsLimit']) { oBo['bo_snapping_loaded'] = null; var text = 'Limit de points atteinte pour object '; @@ -1609,7 +1780,8 @@ nsVmap.nsToolsManager.Insert.prototype.inserttoolController.prototype.loadBoVect if (goog.isDefAndNotNull(response['data'][i]['geom'])) { geom = response['data'][i]['geom']; aFeatures.push(new ol.Feature({ - geometry: oVmap.getGeomFromEWKT(geom) + geometry: oVmap.getGeomFromEWKT(geom), + 'bo_id': bo_id })); } } diff --git a/src/module_vmap/module/javascript/app/vmap/tools/select/select.js b/src/module_vmap/module/javascript/app/vmap/tools/select/select.js index 6a7eddf0e1b15822a8732b90e569e4e05093d0fd..9c89bc22cbe78198752e73c76846568ace3d2d68 100755 --- a/src/module_vmap/module/javascript/app/vmap/tools/select/select.js +++ b/src/module_vmap/module/javascript/app/vmap/tools/select/select.js @@ -366,6 +366,7 @@ nsVmap.nsToolsManager.Select.prototype.selectController = function ($scope, $tim this['snapOptions']['mode'] = oVmap['properties']['snapping']['defaut_snapp_mode']; this['snapOptions']['limit'] = oVmap['properties']['snapping']['defaut_limit']; this['snapOptions']['visible'] = oVmap['properties']['snapping']['defaut_visibility']; + this['snapOptions']['avoidSuperpositions'] = {}; } /** @@ -373,6 +374,16 @@ nsVmap.nsToolsManager.Select.prototype.selectController = function ($scope, $tim */ this['tmpSnapOptions'] = angular.copy(this['snapOptions']); + /** + * Queryable Business Objects + */ + this['aQueryableBOs'] = []; + + /** + * Business objects avaliable for avoiding superpositions + */ + $scope['aAvoidSuperpositionsBOs'] = []; + /** * Tree resulting the various GetFeatureInfo requests */ @@ -523,10 +534,31 @@ nsVmap.nsToolsManager.Select.prototype.selectController = function ($scope, $tim features: this.oOverlayFeatures_ }); - this.modify_.on('modifyend', function () { + this.modify_.on('modifyend', function (evt) { + + // Récupère la feature ajoutée + var aFeatures = evt.features.getArray(); + + // Évite les superpositions avec les autres couches si besoin + var j = 0; + for (var i = 0; i < aFeatures.length; i++) { + this_.avoidSuperpositions(aFeatures[i]).then(function(){ + j++; + if (!j < aFeatures.length) { + setTimeout(function () { + this_.putFeaturesOnTheElement(this_.oOverlayFeatures_.getArray()); + }); + } + }); + } + + // Cas où avoidSuperpositions n'ait pas effectué de callback setTimeout(function () { - this_.putFeaturesOnTheElement(this_.oOverlayFeatures_.getArray()); - }); + if (j === 0) { + console.error('avoidSuperpositions never sents callback'); + this_.putFeaturesOnTheElement(this_.oOverlayFeatures_.getArray()); + } + }, 3000); }); /** @@ -685,6 +717,39 @@ nsVmap.nsToolsManager.Select.prototype.selectController.prototype.loadQueryableB }); }; +/** + * Load this.aAvoidSuperpositionsBOs + * + */ +nsVmap.nsToolsManager.Select.prototype.selectController.prototype.loadAvoidSuperpositionBos = function () { + oVmap.log('nsVmap.nsToolsManager.Select.selectController.loadAvoidSuperpositionBos'); + + var this_ = this; + + // Types de géométries pour le calcul des jointures + var aActiveGeomTypes = ['POLYGON', 'MULTIPOLYGON', 'GEOMETRY', 'GEOMETRYCOLLECTION']; + + // Objets métiers actifs pour la calcul des jointures + this.$scope_['aAvoidSuperpositionsBOs'] = []; + + if (goog.isDefAndNotNull(this['editableFeatureType'])) { + + // Si la couche en cours d'insertion est dans aActiveGeomTypes + if (aActiveGeomTypes.indexOf(this['editableFeatureType']) !== -1) { + for (var i = 0; i < this['aQueryableBOs'].length; i++) { + + // Si la couche cible est dans aActiveGeomTypes + if (goog.isDefAndNotNull(this['aQueryableBOs'][i]['bo_geom_type'])) { + if(aActiveGeomTypes.indexOf(this['aQueryableBOs'][i]['bo_geom_type']) !== -1){ + + this.$scope_['aAvoidSuperpositionsBOs'].push(this['aQueryableBOs'][i]); + } + } + } + } + } +} + /** * Vérifie que l'échelle en cours respecte les spécification de l'objet métier */ @@ -1880,6 +1945,9 @@ nsVmap.nsToolsManager.Select.prototype.selectController.prototype.editFeature = // Affiche la palette de modification $('#basictools-select-modify-palette').show(); + // Chage le liste des BO intersectables par calcul de polygones jointifs + this_.loadAvoidSuperpositionBos(); + // Timeout car lors du toggleOutTools, clearOverlays est lancé setTimeout(function () { // Ajoute la/les feature(s) @@ -2126,12 +2194,160 @@ nsVmap.nsToolsManager.Select.prototype.selectController.prototype.startDrawing = this_.oOverlayLayer_.getSource().addFeature(feature); } + // Évite les superpositions avec les autres couches si besoin + this_.avoidSuperpositions(feature); + // Ajoute les géométries en mode multi ou simple à editableSelection.olFeature this_.putFeaturesOnTheElement(this_.oOverlayFeatures_.getArray()); }); }, this); }; +/** + * Edit the given feature to avoid superpositions with layers defined in snapOptions.avoidSuperpositions + * @param {ol.Feature} olFeature + * @returns {ol.Feature} + */ +nsVmap.nsToolsManager.Select.prototype.selectController.prototype.avoidSuperpositions = function (olFeature) { + oVmap.log('nsVmap.nsToolsManager.Select.selectController.avoidSuperpositions'); + + var deferred = this.$q_.defer(); + + // Objets métiers à intersecter + var aSuperpositionBos = []; + for (var bo_id in this['snapOptions']['avoidSuperpositions']) { + if (this['snapOptions']['avoidSuperpositions'][bo_id] === true) { + aSuperpositionBos.push(bo_id); + } + } + + // Remplace la géométrie par celle calculée + if (aSuperpositionBos.length > 0) { + this.setDiffGeom_(aSuperpositionBos, olFeature).then(function(olFeature){ + deferred.resolve(olFeature); + }); + } else { + deferred.resolve(olFeature); + } + + return deferred.promise; +} + +/** + * Modify the feature geometry to avoid superpositions with BOs in aSuperpositionBos + * + * @param {string} aSuperpositionBos + * @param {ol.Feature} olFeature + * @param {object|undefines} opt_options + */ +nsVmap.nsToolsManager.Select.prototype.selectController.prototype.setDiffGeom_ = function (aSuperpositionBos, olFeature, opt_options) { + oVmap.log('nsVmap.nsToolsManager.Select.selectController.setDiffGeom_'); + + if (!goog.isArray(aSuperpositionBos)) { + return 0; + } + if (!goog.isDefAndNotNull(olFeature)) { + return 0; + } + if (!goog.isDefAndNotNull(opt_options)) { + opt_options = { + bo_index: 0 + } + } + + var this_ = this; + var deferred = this.$q_.defer(); + var sEWKTGeom = oVmap.getEWKTFromGeom(olFeature.getGeometry()); + + this.getDiffGeom_(aSuperpositionBos[opt_options.bo_index], sEWKTGeom).then(function(sNewEWKTGeom){ + + // Remplace la géométrie + olFeature.setGeometry(oVmap.getGeomFromEWKT(sNewEWKTGeom)); + + // Relance la fonction pour les autres objets métiers + opt_options.bo_index ++; + if (opt_options.bo_index < aSuperpositionBos.length) { + this_.setDiffGeom_(aSuperpositionBos, olFeature, opt_options).then(function(){ + deferred.resolve(olFeature); + }, function(error){ + console.error(error); + }); + } else { + deferred.resolve(olFeature); + } + }, function(err){ + console.error('Cannot get the diff geom: ', err) + }); + + return deferred.promise; +} + +/** + * Get the diff superposition geometry + * + * @param {string} sBoId + * @param {string} sEWKTGeom + * @return {promise} + */ +nsVmap.nsToolsManager.Select.prototype.selectController.prototype.getDiffGeom_ = function (sBoId, sEWKTGeom) { + oVmap.log('nsVmap.nsToolsManager.Select.selectController.getDiffGeom_'); + + var oParams = { + 'intersect_geom': sEWKTGeom + }; + + // Filtre pour ne pas s'éviter lui même + if (goog.isDefAndNotNull(this['editableSelection'])) { + if (this['editableSelection']['bo_type'] === sBoId) { + if (goog.isDefAndNotNull(this['editableSelection']['bo_id_field']) && + goog.isDefAndNotNull(this['editableSelection']['bo_id_value'])) { + + oParams['filter'] = { + 'column': this['editableSelection']['bo_id_field'], + 'compare_operator': '!=', + 'value': this['editableSelection']['bo_id_value'] + } + } + } + } + + var deferred = this.$q_.defer(); + ajaxRequest({ + 'method': 'POST', + 'url': oVmap['properties']['api_url'] + '/vmap/querys/' + sBoId + '/diff_geometry', + 'headers': { + 'X-HTTP-Method-Override': 'GET', + 'Accept': 'application/x-vm-json' + }, + 'data': oParams, + 'scope': this.$scope_, + 'success': function (response) { + + if (!goog.isDefAndNotNull(response['data'])) { + deferred.reject('response.data not defined'); + return 0; + } + if (goog.isDefAndNotNull(response['data']['errorMessage'])) { + deferred.reject(response['data']['errorMessage']); + return 0; + } + if (!goog.isDefAndNotNull(response['data'][0])) { + deferred.reject('response.data[0] not defined'); + return 0; + } + if (!goog.isDefAndNotNull(response['data'][0]['diff_geom'])) { + deferred.reject('response.data[0].diff_geom not defined'); + return 0; + } + deferred.resolve(response['data'][0]['diff_geom']); + }, + 'error': function (response) { + deferred.reject(response); + } + }); + return deferred.promise; +} + /** * Put the array of features passed on aFeatures on editableSelection.olFeature with MULTI... form * @param {array} aFeatures @@ -2363,7 +2579,17 @@ nsVmap.nsToolsManager.Select.prototype.selectController.prototype.finishEdition nsVmap.nsToolsManager.Select.prototype.selectController.prototype.showSnappingOptionsModal = function () { oVmap.log('nsVmap.nsToolsManager.Select.prototype.selectController.prototype.showSnappingOptionsModal'); + var this_ = this; this['tmpSnapOptions'] = angular.copy(this['snapOptions']); + + this.$scope_.$applyAsync(function(){ + for (var i = 0; i < this_['aQueryableBOs'].length; i++) { + if (!goog.isDefAndNotNull(this_['tmpSnapOptions']['avoidSuperpositions'][this_['aQueryableBOs'][i]['bo_id']])) { + this_['tmpSnapOptions']['avoidSuperpositions'][this_['aQueryableBOs'][i]['bo_id']] = false; + } + } + }); + $('#vmap-select-snap-options-modal').modal('show'); }; @@ -2477,6 +2703,10 @@ nsVmap.nsToolsManager.Select.prototype.selectController.prototype.loadBoVectorSn oBo['bo_snapping_loaded'] = null; return 0; } + if (!goog.isDefAndNotNull(response['data'][0])) { + oBo['bo_snapping_loaded'] = null; + return 0; + } if (response['data'][0]['count'] > this_['snapOptions']['snappingObjectsLimit']) { oBo['bo_snapping_loaded'] = null; var text = 'Limit de points atteinte pour object '; @@ -2489,7 +2719,8 @@ nsVmap.nsToolsManager.Select.prototype.selectController.prototype.loadBoVectorSn if (goog.isDefAndNotNull(response['data'][i]['geom'])) { geom = response['data'][i]['geom']; aFeatures.push(new ol.Feature({ - geometry: oVmap.getGeomFromEWKT(geom) + geometry: oVmap.getGeomFromEWKT(geom), + 'bo_id': bo_id })); } } diff --git a/src/module_vmap/module/lang/lang-en.json b/src/module_vmap/module/lang/lang-en.json index fc1484e29b1d7924daf36b156634486aca812d54..29d2d9f92d85c94d9d0f1e4a057bb501cf571635 100644 --- a/src/module_vmap/module/lang/lang-en.json +++ b/src/module_vmap/module/lang/lang-en.json @@ -287,7 +287,12 @@ "FORM_ENABLE_GETFEATUREINFO_CONFIGURATION_VMAP_CONFIG": "Enable GetFeatureInfo selection", "FORM_SELECTION_BUFFER_CONFIGURATION_CONFIGURATION_VMAP_CONFIG": "Buffer (en mm sur l'écran)", "FORM_SELECTION_MAX_SCALE_BUFFER_CONFIGURATION_CONFIGURATION_VMAP_CONFIG": "Buffer maximum (en mètres sur le terrain)", - "TITLE_SNAPPING_CONFIGURATION_CONFIGURATION_VMAP_CONFIG": "Default snapping options", + "TITLE_SNAPPING_CONFIGURATION_CONFIGURATION_VMAP_CONFIG": "Snapping options", + "FORM_SNAPPING_LIMIT_CONFIGURATION_CONFIGURATION_VMAP_CONFIG": "Manimum limit of points per object", + "FORM_SNAPPING_TOLERANCE_CONFIGURATION_CONFIGURATION_VMAP_CONFIG": "Snapping tolerance (in pixels)", + "FORM_SNAPPING_TYPE_ACC_CONFIGURATION_CONFIGURATION_VMAP_CONFIG": "Default snapping type", + "FORM_SNAPPING_VISIBILITY_CONFIGURATION_CONFIGURATION_VMAP_CONFIG": "Selected objects visibility", + "FORM_SNAPPING_AVOID_SUPERPOSITIONS_CONFIGURATION_CONFIGURATION_VMAP_CONFIG": "Avoid superposition", "FORM_SNAPPING_DEFAUT_LIMIT_CONFIGURATION_CONFIGURATION_VMAP_CONFIG": "Manimum limit of points per object", "FORM_SNAPPING_DEFAUT_TOLERANCE_CONFIGURATION_CONFIGURATION_VMAP_CONFIG": "Snapping tolerance (in pixels)", "FORM_SNAPPING_DEFAUT_TYPE_ACC_CONFIGURATION_CONFIGURATION_VMAP_CONFIG": "Default snapping type", diff --git a/src/module_vmap/module/lang/lang-fr.json b/src/module_vmap/module/lang/lang-fr.json index 970eebf515a273666fd84369acf99d6700229a7a..82fca25d0f2fa89bab33f3cd82e694948c3b78cc 100644 --- a/src/module_vmap/module/lang/lang-fr.json +++ b/src/module_vmap/module/lang/lang-fr.json @@ -287,7 +287,12 @@ "FORM_ENABLE_GETFEATUREINFO_CONFIGURATION_VMAP_CONFIG": "Interrogation par GetFeatureInfo", "FORM_SELECTION_BUFFER_CONFIGURATION_CONFIGURATION_VMAP_CONFIG": "Buffer de sélection (en mm sur l'écran)", "FORM_SELECTION_MAX_SCALE_BUFFER_CONFIGURATION_CONFIGURATION_VMAP_CONFIG": "Buffer maximum (en mètres sur le terrain)", - "TITLE_SNAPPING_CONFIGURATION_CONFIGURATION_VMAP_CONFIG": "Options d'accrochage par défaut", + "TITLE_SNAPPING_CONFIGURATION_CONFIGURATION_VMAP_CONFIG": "Options d'accrochage", + "FORM_SNAPPING_LIMIT_CONFIGURATION_CONFIGURATION_VMAP_CONFIG": "Limite de points accrochables", + "FORM_SNAPPING_TOLERANCE_CONFIGURATION_CONFIGURATION_VMAP_CONFIG": "Tolérance d'accrochage (pixels)", + "FORM_SNAPPING_TYPE_ACC_CONFIGURATION_CONFIGURATION_VMAP_CONFIG": "Type d'accrochage", + "FORM_SNAPPING_VISIBILITY_CONFIGURATION_CONFIGURATION_VMAP_CONFIG": "Visibilité des zones d'accrochage", + "FORM_SNAPPING_AVOID_SUPERPOSITIONS_CONFIGURATION_CONFIGURATION_VMAP_CONFIG": "Eviter les superpositions", "FORM_SNAPPING_DEFAUT_LIMIT_CONFIGURATION_CONFIGURATION_VMAP_CONFIG": "Limite de points accrochables", "FORM_SNAPPING_DEFAUT_TOLERANCE_CONFIGURATION_CONFIGURATION_VMAP_CONFIG": "Tolérance d'accrochage (pixels)", "FORM_SNAPPING_DEFAUT_TYPE_ACC_CONFIGURATION_CONFIGURATION_VMAP_CONFIG": "Type d'accrochage", diff --git a/src/module_vmap/module/template/tools/insert.html b/src/module_vmap/module/template/tools/insert.html index 8011c69fe13be35cb5aa0684448a4108da9abb2b..b18ed5994bdc1bfbf72020ce20aae6262f22d3be 100644 --- a/src/module_vmap/module/template/tools/insert.html +++ b/src/module_vmap/module/template/tools/insert.html @@ -242,32 +242,69 @@ <h4 class="modal-title" data-translate="TITLE_SNAPPING_CONFIGURATION_CONFIGURATION_VMAP_CONFIG"></h4> </div> <div class="modal-body modal-body-big-with-footer-3 font-12"> - <div class="col-sm-6"> - <h5 data-translate="FORM_SNAPPING_DEFAUT_TYPE_ACC_CONFIGURATION_CONFIGURATION_VMAP_CONFIG"></h5> - <select class="form-control" - ng-model="ctrl.tmpSnapOptions.mode"> - <option value="segment_edge_node" data-translate="FORM_SNAPPING_METHOD_SEN_CONFIGURATION_CONFIGURATION_VMAP_CONFIG"></option> - <option value="edge_node" data-translate="FORM_SNAPPING_METHOD_EN_CONFIGURATION_CONFIGURATION_VMAP_CONFIG"></option> - <option value="node" data-translate="FORM_SNAPPING_METHOD_N_CONFIGURATION_CONFIGURATION_VMAP_CONFIG"></option> - </select> - </div> - <div class="col-sm-6"> - <h5 data-translate="FORM_SNAPPING_DEFAUT_TOLERANCE_CONFIGURATION_CONFIGURATION_VMAP_CONFIG"></h5> - <input type="number" class="form-control" ng-model="ctrl.tmpSnapOptions.tolerance"> - </div> - <div class="col-sm-6"> - <h5 data-translate="FORM_SNAPPING_DEFAUT_LIMIT_CONFIGURATION_CONFIGURATION_VMAP_CONFIG"></h5> - <input type="number" class="form-control" ng-model="ctrl.tmpSnapOptions.limit"> + <div class="row"> + <div class="col-sm-6"> + <h5 data-translate="FORM_SNAPPING_TYPE_ACC_CONFIGURATION_CONFIGURATION_VMAP_CONFIG"></h5> + <select class="form-control" + ng-model="ctrl.tmpSnapOptions.mode"> + <option value="segment_edge_node" data-translate="FORM_SNAPPING_METHOD_SEN_CONFIGURATION_CONFIGURATION_VMAP_CONFIG"></option> + <option value="edge_node" data-translate="FORM_SNAPPING_METHOD_EN_CONFIGURATION_CONFIGURATION_VMAP_CONFIG"></option> + <option value="node" data-translate="FORM_SNAPPING_METHOD_N_CONFIGURATION_CONFIGURATION_VMAP_CONFIG"></option> + </select> + </div> + <div class="col-sm-6"> + <h5 data-translate="FORM_SNAPPING_TOLERANCE_CONFIGURATION_CONFIGURATION_VMAP_CONFIG"></h5> + <input type="number" class="form-control" ng-model="ctrl.tmpSnapOptions.tolerance"> + </div> </div> - <div class="col-sm-6"> - <h5 data-translate="FORM_SNAPPING_DEFAUT_VISIBILITY_CONFIGURATION_CONFIGURATION_VMAP_CONFIG"></h5> - <div class="radio radio-inline"> - <input type="radio" name="vmap_insert_snap_visible" id="vmap_insert_snap_visible_1" ng-model="ctrl.tmpSnapOptions.visible" ng-value="true"> - <label for="vmap_insert_snap_visible_1">Oui</label> + <div class="row"> + <div class="col-sm-6"> + <h5 data-translate="FORM_SNAPPING_LIMIT_CONFIGURATION_CONFIGURATION_VMAP_CONFIG"></h5> + <input type="number" class="form-control" ng-model="ctrl.tmpSnapOptions.limit"> </div> - <div class="radio radio-inline"> - <input type="radio" name="vmap_insert_snap_visible" id="vmap_insert_snap_visible_2" ng-model="ctrl.tmpSnapOptions.visible" ng-value="false"> - <label for="vmap_insert_snap_visible_2">Non</label> + <div class="col-sm-6"> + <h5 data-translate="FORM_SNAPPING_VISIBILITY_CONFIGURATION_CONFIGURATION_VMAP_CONFIG"></h5> + <div class="radio radio-inline"> + <input type="radio" name="vmap_insert_snap_visible" id="vmap_insert_snap_visible_1" ng-model="ctrl.tmpSnapOptions.visible" ng-value="true"> + <label for="vmap_insert_snap_visible_1">Oui</label> + </div> + <div class="radio radio-inline"> + <input type="radio" name="vmap_insert_snap_visible" id="vmap_insert_snap_visible_2" ng-model="ctrl.tmpSnapOptions.visible" ng-value="false"> + <label for="vmap_insert_snap_visible_2">Non</label> + </div> + </div> + </div> + <div class="row"> + <div class="col-sm-6" ng-show="aAvoidSuperpositionsBOs.length > 0"> + <div class="row"> + <div class="col-sm-12 text-right"> + <h5 data-translate="FORM_SNAPPING_AVOID_SUPERPOSITIONS_CONFIGURATION_CONFIGURATION_VMAP_CONFIG"></h5> + </div> + </div> + <div class="row" + ng-repeat="bo in aAvoidSuperpositionsBOs"> + <div class="col-sm-5 text-right"> + <label>{{bo.bo_title}}</label> + </div> + <div class="col-sm-7 text-right"> + <div class="radio radio-inline"> + <input type="radio" + name="vmap_insert_snap_avoid_superpositions_{{$index}}" + id="vmap_insert_snap_avoid_superpositions_1_{{$index}}" + ng-model="ctrl.tmpSnapOptions.avoidSuperpositions[bo.bo_id]" + ng-value="true"> + <label for="vmap_insert_snap_avoid_superpositions_1_{{$index}}">Oui</label> + </div> + <div class="radio radio-inline"> + <input type="radio" + name="vmap_insert_snap_avoid_superpositions_{{$index}}" + id="vmap_insert_snap_avoid_superpositions_2_{{$index}}" + ng-model="ctrl.tmpSnapOptions.avoidSuperpositions[bo.bo_id]" + ng-value="false"> + <label for="vmap_insert_snap_avoid_superpositions_2_{{$index}}">Non</label> + </div> + </div> + </div> </div> </div> </div> diff --git a/src/module_vmap/module/template/tools/select.html b/src/module_vmap/module/template/tools/select.html index 420c5b37e81360aa9fd3fcd128e1d46bb3f5e001..24ae5ca8505ff719e24f57588e36d5e262265c44 100644 --- a/src/module_vmap/module/template/tools/select.html +++ b/src/module_vmap/module/template/tools/select.html @@ -290,32 +290,69 @@ <h4 class="modal-title" data-translate="TITLE_SNAPPING_CONFIGURATION_CONFIGURATION_VMAP_CONFIG"></h4> </div> <div class="modal-body modal-body-big-with-footer-3 font-12"> - <div class="col-sm-6"> - <h5 data-translate="FORM_SNAPPING_DEFAUT_TYPE_ACC_CONFIGURATION_CONFIGURATION_VMAP_CONFIG"></h5> - <select class="form-control" - ng-model="ctrl.tmpSnapOptions.mode"> - <option value="segment_edge_node" data-translate="FORM_SNAPPING_METHOD_SEN_CONFIGURATION_CONFIGURATION_VMAP_CONFIG"></option> - <option value="edge_node" data-translate="FORM_SNAPPING_METHOD_EN_CONFIGURATION_CONFIGURATION_VMAP_CONFIG"></option> - <option value="node" data-translate="FORM_SNAPPING_METHOD_N_CONFIGURATION_CONFIGURATION_VMAP_CONFIG"></option> - </select> - </div> - <div class="col-sm-6"> - <h5 data-translate="FORM_SNAPPING_DEFAUT_TOLERANCE_CONFIGURATION_CONFIGURATION_VMAP_CONFIG"></h5> - <input type="number" class="form-control" ng-model="ctrl.tmpSnapOptions.tolerance"> - </div> - <div class="col-sm-6"> - <h5 data-translate="FORM_SNAPPING_DEFAUT_LIMIT_CONFIGURATION_CONFIGURATION_VMAP_CONFIG"></h5> - <input type="number" class="form-control" ng-model="ctrl.tmpSnapOptions.limit"> + <div class="row"> + <div class="col-sm-6"> + <h5 data-translate="FORM_SNAPPING_TYPE_ACC_CONFIGURATION_CONFIGURATION_VMAP_CONFIG"></h5> + <select class="form-control" + ng-model="ctrl.tmpSnapOptions.mode"> + <option value="segment_edge_node" data-translate="FORM_SNAPPING_METHOD_SEN_CONFIGURATION_CONFIGURATION_VMAP_CONFIG"></option> + <option value="edge_node" data-translate="FORM_SNAPPING_METHOD_EN_CONFIGURATION_CONFIGURATION_VMAP_CONFIG"></option> + <option value="node" data-translate="FORM_SNAPPING_METHOD_N_CONFIGURATION_CONFIGURATION_VMAP_CONFIG"></option> + </select> + </div> + <div class="col-sm-6"> + <h5 data-translate="FORM_SNAPPING_TOLERANCE_CONFIGURATION_CONFIGURATION_VMAP_CONFIG"></h5> + <input type="number" class="form-control" ng-model="ctrl.tmpSnapOptions.tolerance"> + </div> </div> - <div class="col-sm-6"> - <h5 data-translate="FORM_SNAPPING_DEFAUT_VISIBILITY_CONFIGURATION_CONFIGURATION_VMAP_CONFIG"></h5> - <div class="radio radio-inline"> - <input type="radio" name="vmap_select_snap_visible" id="vmap_select_snap_visible_1" ng-model="ctrl.tmpSnapOptions.visible" ng-value="true"> - <label for="vmap_select_snap_visible_1">Oui</label> + <div class="row"> + <div class="col-sm-6"> + <h5 data-translate="FORM_SNAPPING_LIMIT_CONFIGURATION_CONFIGURATION_VMAP_CONFIG"></h5> + <input type="number" class="form-control" ng-model="ctrl.tmpSnapOptions.limit"> </div> - <div class="radio radio-inline"> - <input type="radio" name="vmap_select_snap_visible" id="vmap_select_snap_visible_2" ng-model="ctrl.tmpSnapOptions.visible" ng-value="false"> - <label for="vmap_select_snap_visible_2">Non</label> + <div class="col-sm-6"> + <h5 data-translate="FORM_SNAPPING_VISIBILITY_CONFIGURATION_CONFIGURATION_VMAP_CONFIG"></h5> + <div class="radio radio-inline"> + <input type="radio" name="vmap_select_snap_visible" id="vmap_select_snap_visible_1" ng-model="ctrl.tmpSnapOptions.visible" ng-value="true"> + <label for="vmap_select_snap_visible_1">Oui</label> + </div> + <div class="radio radio-inline"> + <input type="radio" name="vmap_select_snap_visible" id="vmap_select_snap_visible_2" ng-model="ctrl.tmpSnapOptions.visible" ng-value="false"> + <label for="vmap_select_snap_visible_2">Non</label> + </div> + </div> + </div> + <div class="row"> + <div class="col-sm-6" ng-show="aAvoidSuperpositionsBOs.length > 0"> + <div class="row"> + <div class="col-sm-12 text-right"> + <h5 data-translate="FORM_SNAPPING_AVOID_SUPERPOSITIONS_CONFIGURATION_CONFIGURATION_VMAP_CONFIG"></h5> + </div> + </div> + <div class="row" + ng-repeat="bo in aAvoidSuperpositionsBOs"> + <div class="col-sm-5 text-right"> + <label>{{bo.bo_title}}</label> + </div> + <div class="col-sm-7 text-right"> + <div class="radio radio-inline"> + <input type="radio" + name="vmap_select_snap_avoid_superpositions_{{$index}}" + id="vmap_select_snap_avoid_superpositions_1_{{$index}}" + ng-model="ctrl.tmpSnapOptions.avoidSuperpositions[bo.bo_id]" + ng-value="true"> + <label for="vmap_select_snap_avoid_superpositions_1_{{$index}}">Oui</label> + </div> + <div class="radio radio-inline"> + <input type="radio" + name="vmap_select_snap_avoid_superpositions_{{$index}}" + id="vmap_select_snap_avoid_superpositions_2_{{$index}}" + ng-model="ctrl.tmpSnapOptions.avoidSuperpositions[bo.bo_id]" + ng-value="false"> + <label for="vmap_select_snap_avoid_superpositions_2_{{$index}}">Non</label> + </div> + </div> + </div> </div> </div> </div> diff --git a/src/module_vmap/web_service/ws/Querys.class.inc b/src/module_vmap/web_service/ws/Querys.class.inc index a5e4b7deed246263e5d67a6856fb62af7d503fa9..3aed39da27e23ba5bbe1615d9fa027c6127d5f48 100644 --- a/src/module_vmap/web_service/ws/Querys.class.inc +++ b/src/module_vmap/web_service/ws/Querys.class.inc @@ -515,14 +515,54 @@ class Querys extends Vmap { * ) * ) */ + /** + * @SWG\Get(path="/querys/{business_object_id}/diff_geometry", + * tags={"Querys"}, + * summary="Get business object form querys", + * description="Get the difference geom between intersect_geom and the BO using st_difference", + * operationId="GET", + * produces={"application/json", "application/x-vm-json"}, + * @SWG\Parameter( + * name="token", + * in="query", + * description="user token", + * required=true, + * type="string" + * ), + * @SWG\Parameter( + * name="business_object_id", + * in="path", + * description="business object id", + * required=true, + * type="integer" + * ), + * @SWG\Parameter( + * name="intersect_geom", + * in="query", + * description="EWKT intersect geometry", + * required=true, + * type="integer" + * ), + * @SWG\Response( + * response=200, + * description="Poprerties Response", + * @SWG\Schema(ref="#/definitions/users") + * ) + * ) + */ /** * get Querys * @return Querys */ function GET($bOnlyReturnStatus = FALSE) { - if (isset($this->aPath[3]) && ($this->aPath[3] == 'geometry')) { - return $this->getBoGeomsFromIntersect($this->aPath[2]); + if (($this->aPath[3] == 'geometry') || ($this->aPath[3] == 'diff_geometry')) { + if (($this->aPath[3] == 'geometry')) { + return $this->getBoGeomsFromIntersect($this->aPath[2]); + } + if (($this->aPath[3] == 'diff_geometry')) { + return $this->getBoDiffGeomFromIntersect($this->aPath[2]); + } } else if (isset($this->aPath[3])) { return $this->queryBusinessObject($this->aPath[3]); } else { @@ -597,7 +637,6 @@ class Querys extends Vmap { // Valeurs par défaut $intersect_column = empty($intersect_column) ? $geom_column : $intersect_column; -// $intersect_buffer = empty($intersect_buffer) ? 0.01 : $intersect_buffer; $geom_field = empty($geom_field) ? $intersect_column : $geom_field; $get_geom = empty($geom_field) || empty($get_geom) ? false : $get_geom; $use_intersect = empty($intersect_geom) || empty($intersect_column) ? false : true; @@ -1205,6 +1244,121 @@ class Querys extends Vmap { } } + /** + * Get the difference geometry with a business object intersecting intersect_geom + */ + function getBoDiffGeomFromIntersect($sBusinessObjectId) { + + if (!empty($this->oConnection->oError)) { + $aXmlRacineAttribute['status'] = 0; + $sMessage = $this->oConnection->oError->asDocument('', 'vitis', $this->aValues['sEncoding'], True, $aXmlRacineAttribute, $this->aValues['sSourceEncoding'], $this->aValues['output']); + $aReturn = array('sStatus' => $aXmlRacineAttribute['status'], "sMessage" => $sMessage); + return $aReturn['sMessage']; + } + if (empty($sBusinessObjectId) || empty($this->aValues['intersect_geom'])) { + $oError = new VitisError(0, 'Parameters business_object_id, intersect_geom required'); + $aXmlRacineAttribute['status'] = 0; + $sMessage = $oError->asDocument('', 'vitis', $this->aValues['sEncoding'], True, $aXmlRacineAttribute, $this->aValues['sSourceEncoding'], $this->aValues['output']); + $aReturn = array('sStatus' => $aXmlRacineAttribute['status'], "sMessage" => $sMessage); + return $aReturn['sMessage']; + } + + // Objet BusinessObject correspondant + $aPath = array('vmap', 'businessobjects', $sBusinessObjectId); + $aValues = array( + 'token' => $this->aValues['token'], + 'output' => 'application/json', + 'sEncoding' => 'UTF-8', + 'sSourceEncoding' => 'UTF-8', + 'my_vitis_id' => $sBusinessObjectId, + 'module' => 'vmap', + ); + $oBusinessObject = new BusinessObject($aPath, $aValues, $this->aProperties, $this->oConnection); + $oBusinessObject->GET(); + + // Vérifie l'éxistance de l'objet métier + if (empty($oBusinessObject->aFields['business_object_id'])) { + $oError = new VitisError(0, 'Business object ' . $sBusinessObjectId . ' not founded'); + $aXmlRacineAttribute['status'] = 0; + $sMessage = $oError->asDocument('', 'vitis', $this->aValues['sEncoding'], True, $aXmlRacineAttribute, $this->aValues['sSourceEncoding'], $this->aValues['output']); + $aReturn = array('sStatus' => $aXmlRacineAttribute['status'], "sMessage" => $sMessage); + return $aReturn['sMessage']; + } + + // Paramètres retenus dans l'url + $sSchema = $oBusinessObject->aFields['schema']; + $sTable = $oBusinessObject->aFields['table']; + $sIntersectGeom = $this->aValues['intersect_geom']; + $sInputFilter = $this->aValues['filter']; + + // Paramètres retenus dans le business object + $sGeomColumn = $oBusinessObject->aFields['geom_column']; + $sDatabase = $oBusinessObject->aFields['database']; + + // Base de données + if (!empty($sDatabase) && $sDatabase != $this->oConnection->oBd->base) { + $this->oConnection->oBd = new Vm($this->oConnection->oBd->login, $this->oConnection->oBd->mdp, $sDatabase, $this->oConnection->oBd->serveur, $this->oConnection->oBd->port, $this->oConnection->oBd->sgbd, $this->oConnection->oBd->sPageEncoding); + } + + // Projection de la colonne intersectée + $iColumnProj = $this->oConnection->oBd->getColumnSRID($sSchema, $sTable, $sGeomColumn); + if (empty($iColumnProj)) { + $iColumnProj = '2154'; + } + + // Paramètres de la requête + $aParams = array(); + $aParams['sSchema'] = array('value' => $sSchema, 'type' => 'schema_name'); + $aParams['sTable'] = array('value' => $sTable, 'type' => 'table_name'); + $aParams['sGeomColumn'] = array('value' => $sGeomColumn, 'type' => 'column_name'); + $aParams['sIntersectGeom'] = array('value' => $sIntersectGeom, 'type' => 'geometry'); + + // Filtre + $aFilter = array( + 'column' => $sGeomColumn, + 'compare_operator' => 'intersect', + 'compare_operator_options' => array( + 'source_proj' => $iColumnProj, + ), + 'value' => $sIntersectGeom + ); + + if (!empty($sInputFilter)) { + $aFilter = $this->addFilterOperator($sInputFilter, $aFilter); + } + + $aDecodedFilter = $this->decodeJSONFilter($aFilter, $sSchema, $sTable); + + $sSecuredFilter = $aDecodedFilter['request']; + foreach ($aDecodedFilter['params'] as $key => $value) { + $aParams[$key] = $value; + } + + $sSql = ' + WITH geoms AS ( + SELECT (ST_Dump(st_difference( + ST_GeomFromEWKT([sIntersectGeom]), + ST_Union([sGeomColumn]) + ))).geom AS geom + FROM [sSchema].[sTable] WHERE ' . $sSecuredFilter . ' + ) + SELECT ST_AsEWKT(geom) as diff_geom + FROM geoms + ORDER BY ST_Area(geom) DESC + LIMIT 1 + '; + + $oResult = $this->oConnection->oBd->executeWithParams($sSql, $aParams); + if ($this->oConnection->oBd->enErreur()) { + $aXmlRacineAttribute['status'] = 0; + writeToErrorLog($this->oConnection->oBd->getBDMessage()); + return json_encode(array('errorMessage' => 'Error while calculating diff geom')); + } else { + $aResult = $this->oConnection->oBd->getResultTableAssoc($oResult); + return json_encode($aResult); + } + } + /** * @SWG\Put(path="/querys/{business_object_id}", * tags={"Querys"},