« Questionnaire carto avec Limesurvey » : différence entre les versions
(Page créée avec « À la demande d'une doctorante, j'ai rédigé ce petit bout de code en Javascript permettant d'intégrer une question de type ''Tracez un polygone sur la carte'' dans un questionnaire Limesurvey. Ce code est à insérer dans l'énoncé d'une question de type texte long. La réponse sera générée automatiquement par Leaflet : il s'agira d'un geojson contenant le polygone tracé par l'utilisateur.<syntaxhighlight lang="html"> <!-- CSS Leaflet --> <link href="https:... ») |
|||
(10 versions intermédiaires par le même utilisateur non affichées) | |||
Ligne 1 : | Ligne 1 : | ||
À la demande d'une doctorante, j'ai rédigé ce petit bout de code en Javascript permettant d'intégrer une question de type ''Tracez un polygone sur la carte'' dans un questionnaire Limesurvey. | À la demande d'une doctorante, j'ai rédigé ce petit bout de code en Javascript permettant d'intégrer une question de type ''Tracez un polygone sur la carte'' dans un questionnaire Limesurvey. | ||
Ce code est à insérer dans l'énoncé d'une question de type texte long. La réponse sera générée automatiquement par Leaflet : il s'agira d'un geojson contenant le polygone tracé par l'utilisateur.<syntaxhighlight lang="html"> | Ce code est à insérer dans l'énoncé d'une question de type texte long. La réponse sera générée automatiquement par Leaflet : il s'agira d'un geojson contenant le polygone tracé par l'utilisateur. | ||
<!-- | |||
Pour masquer le champ de réponse, il faut ajouter la classe <code>question-carto</code> aux styles css de la question dans les options de LimeSurvey. | |||
== Version 1 - éditeur vectoriel classique == | |||
Cet éditeur classique utilise la librairie Leaflet-Draw la plus couramment utilisée. Les polygones doivent être tracés point par point, comme dans un SIG. | |||
[[Fichier:Tracer Polygone LimeSurvey.webm|760x760px]]<syntaxhighlight lang="html"> | |||
<!-- Leaflet --> | |||
<link href="https://unpkg.com/leaflet@1.9.1/dist/leaflet.css" rel="stylesheet" /> | <link href="https://unpkg.com/leaflet@1.9.1/dist/leaflet.css" rel="stylesheet" /> | ||
<script src="https://unpkg.com/leaflet@1.9.1/dist/leaflet.js"></script> | <script src="https://unpkg.com/leaflet@1.9.1/dist/leaflet.js"></script> | ||
<!-- | <!-- Leaflet Draw --> | ||
<link href="https:// | <link href="https://unpkg.com/leaflet-draw@1.0.4/dist/leaflet.draw.css" rel="stylesheet" /> | ||
<script src="https://unpkg.com/leaflet-draw@1.0.4/dist/leaflet.draw.js"></script> | |||
<script src="https:// | |||
<!-- | <!-- Leaflet GeoSearch --> | ||
<script src="https:// | <link href="https://unpkg.com/leaflet-geosearch@3.6.1/dist/geosearch.css" rel="stylesheet" /> | ||
<script src="https://unpkg.com/leaflet-geosearch@3.6.1/dist/geosearch.umd.js"></script> | |||
<!-- CSS pour la taille de la carte et pour cacher le formulaire de réponse --> | <!-- CSS pour la taille de la carte et pour cacher le formulaire de réponse --> | ||
Ligne 25 : | Ligne 25 : | ||
height: 500px; | height: 500px; | ||
} | } | ||
.answer-container { | .question-carto > div.answer-container { | ||
display:none; | display:none; | ||
} | } | ||
Ligne 32 : | Ligne 32 : | ||
<!-- Question --> | <!-- Question --> | ||
Carte : | Carte : | ||
<div id="map"> | <div id="map"> </div> | ||
<!-- JS de la carte --> | <!-- JS de la carte --> | ||
Ligne 42 : | Ligne 42 : | ||
minZoom: 5, | minZoom: 5, | ||
maxZoom: 18, | maxZoom: 18, | ||
}).setView([45.763, 4.732], 13); | }).setView([45.763, 4.732], 13); // Définir ici l'emplacement par défaut de la carte et son niveau de zoom | ||
// Ajout de la fonction de recherche | // Ajout de la fonction de recherche | ||
const search = new GeoSearch.GeoSearchControl({ | |||
provider: new GeoSearch.OpenStreetMapProvider(), | |||
}); | |||
map.addControl(search); | |||
})); | |||
// Ajout de la couche WMS géoportail | // Ajout de la couche WMS géoportail | ||
Ligne 100 : | Ligne 94 : | ||
// Evenements à déclencher lors de la suppression d'une entité | // Evenements à déclencher lors de la suppression d'une entité | ||
map.on(L.Draw.Event.DELETED, function (event) { | map.on(L.Draw.Event.DELETED, function (event) { | ||
var JSONoutput = JSON.stringify(drawnItems.toGeoJSON()); | |||
if (drawnItems.toGeoJSON().features.length == 0) { | |||
var JSONoutput = ""; | |||
} else { | |||
var JSONoutput = JSON.stringify(drawnItems.toGeoJSON()); | |||
} | |||
$('#answer{SGQ}').val(JSONoutput); | $('#answer{SGQ}').val(JSONoutput); | ||
}); | }); | ||
Ligne 110 : | Ligne 110 : | ||
}); | }); | ||
</script> | </script> | ||
</syntaxhighlight> | |||
== Version 2 - éditeur de type "pinceau à polygones" == | |||
Cet éditeur utilise le plugin [https://github.com/tcoupin/leaflet-paintpolygon Leaflet-PaintPolygon créé par Thibault Coupin]. Les polygones sont tracés avec un pinceau circulaire dont la taille est réglable. C'est moins conventionnel, mais peut se révéler plus ergonomique pour les enquêtés qui ne sont pas habitués aux SIG. | |||
[[Fichier:Peindre Polygone LimeSurvey.webm|760x760px]] <syntaxhighlight lang="html"> | |||
<!-- Leaflet --> | |||
<link href="https://unpkg.com/leaflet@1.9.1/dist/leaflet.css" rel="stylesheet" /> | |||
<script src="https://unpkg.com/leaflet@1.9.1/dist/leaflet.js"></script> | |||
<!-- Leaflet GeoSearch --> | |||
<link href="https://unpkg.com/leaflet-geosearch@3.6.1/dist/geosearch.css" rel="stylesheet" /> | |||
<script src="https://unpkg.com/leaflet-geosearch@3.6.1/dist/geosearch.umd.js"></script> | |||
<!-- Leaflet Paint-Polygon by Thibault Coupin --> | |||
<script src="https://cdn.jsdelivr.net/npm/leaflet-paintpolygon@1.2.1-alpha.1/dist/Leaflet.PaintPolygon.min.js"></script> | |||
<!-- CSS pour la taille de la carte et pour cacher le formulaire de réponse --> | |||
<style type="text/css"> | |||
#map { | |||
height: 500px; | |||
} | |||
.question-carto > div.answer-container { | |||
display:none; | |||
} | |||
/* Ci-dessous un fix pour le contrôle de la taille du pinceau qui s'affiche mal sur certaines versions de Limesurvey */ | |||
.leaflet-control-paintpolygon-menu-open { | |||
width: 150px; | |||
} | |||
</style> | |||
<!-- Question --> | |||
Carte : | |||
<div id="map"> </div> | |||
<!-- JS de la carte --> | |||
<script type="text/javascript" charset="utf-8"> | |||
// Paramètres de la carte de base | |||
var map = L.map('map', { | |||
crs: L.CRS.EPSG3857, | |||
minZoom: 5, | |||
maxZoom: 18, | |||
}).setView([45.763, 4.732], 13); // Définir ici l'emplacement par défaut de la carte et son niveau de zoom | |||
// Ajout de la fonction de recherche | |||
const search = new GeoSearch.GeoSearchControl({ | |||
provider: new GeoSearch.OpenStreetMapProvider(), | |||
}); | |||
map.addControl(search); | |||
// Ajout de la couche WMS géoportail | |||
var wmsGeoportail = L.tileLayer.wms('https://wxs.ign.fr/cartes/geoportail/r/wms?SERVICE=WMS&VERSION=1.3.0&CRS=EPSG:3857', { | |||
layers: 'GEOGRAPHICALGRIDSYSTEMS.PLANIGNV2', | |||
attribution: 'IGN-F' | |||
}).addTo(map); | |||
// Ajout du pinceau à polygones | |||
var paintControl = L.control.paintPolygon({ | |||
// Définition du style des polygones tracés | |||
layerOptions: { | |||
"color": "#ff7800", | |||
"weight": 5, | |||
"opacity": 0.65 | |||
}, | |||
// Définition de la position de la barre d'outils sur la carte | |||
position: "topleft", | |||
minRadius: 10, // Taille minimale du pinceau (en pixels) | |||
maxRadius: 50, // Taille maximale du pinceau (en pixels) | |||
menu: { | |||
size: true // Afficher (true) ou non (false) le réglage de la taille du pinceau | |||
} | |||
}); | |||
paintControl.addTo(map); | |||
function updateDrawnItems(){ | |||
var JSONoutput = JSON.stringify(paintControl.getData()); | |||
$('#answer{SGQ}').val(JSONoutput); | |||
} | |||
setInterval('updateDrawnItems()', 1000); // Mise à jour des entités tracées toutes les secondes | |||
</script> | |||
</syntaxhighlight> | |||
== Script R pour extraire les entités vectorielles des résultats LimeSurvey == | |||
Le script suivant permet d'extraire toutes les réponses vectorielles d'une question dans un geopackage, en prenant soin de créer un champ "id_limesurvey" dans celui-ci pour pouvoir faire des jointures attributaires a posteriori. <syntaxhighlight lang="r"> | |||
library(dplyr) | |||
library(sf) | |||
library(geojsonsf) | |||
# Lire les résultats de l'enquête | |||
data = read.csv('./results-survey.csv') %>% as_tibble() | |||
# Definir une fonction pour ajouter l'id limesurvey à une collection de géométries sf | |||
add_id = function(sf, id) { | |||
sf = sf %>% mutate(id_limesurvey = id) | |||
return(sf) | |||
} | |||
###### | |||
### Dans toutes les lignes suivantes, remplacez "QUESTIONID" par le nom du champ contenant les géométries à extraire | |||
###### | |||
geom_table = data %>% | |||
filter(grepl("\"type\":\"FeatureCollection\"", QUESTIONID)) %>% # Filtrer les réponses où des géométries ont été saisies | |||
select(QUESTIONID, id) %>% | |||
mutate(sf = lapply(QUESTIONID, geojson_sf), # Convertir le geojson en objets sf | |||
sf = mapply(add_id, sf, id, SIMPLIFY = FALSE)) # Appliquer la fonction add_id pour ajouter l'id_limesurvey | |||
# Fusionner toutes les géométries dans un seul objet sf | |||
geometries = bind_rows(geom_table$sf) | |||
# Ecrire l'objet sf dans un geopackage | |||
st_write(geometries, dsn = './QUESTIONID.gpkg') | |||
</syntaxhighlight> | </syntaxhighlight> | ||
[[Catégorie:Tutoriel]] | [[Catégorie:Tutoriel]] |
Version actuelle datée du 6 juin 2023 à 14:29
À la demande d'une doctorante, j'ai rédigé ce petit bout de code en Javascript permettant d'intégrer une question de type Tracez un polygone sur la carte dans un questionnaire Limesurvey.
Ce code est à insérer dans l'énoncé d'une question de type texte long. La réponse sera générée automatiquement par Leaflet : il s'agira d'un geojson contenant le polygone tracé par l'utilisateur.
Pour masquer le champ de réponse, il faut ajouter la classe question-carto
aux styles css de la question dans les options de LimeSurvey.
Version 1 - éditeur vectoriel classique
Cet éditeur classique utilise la librairie Leaflet-Draw la plus couramment utilisée. Les polygones doivent être tracés point par point, comme dans un SIG.
Fichier:Tracer Polygone LimeSurvey.webm
<!-- Leaflet -->
<link href="https://unpkg.com/leaflet@1.9.1/dist/leaflet.css" rel="stylesheet" />
<script src="https://unpkg.com/leaflet@1.9.1/dist/leaflet.js"></script>
<!-- Leaflet Draw -->
<link href="https://unpkg.com/leaflet-draw@1.0.4/dist/leaflet.draw.css" rel="stylesheet" />
<script src="https://unpkg.com/leaflet-draw@1.0.4/dist/leaflet.draw.js"></script>
<!-- Leaflet GeoSearch -->
<link href="https://unpkg.com/leaflet-geosearch@3.6.1/dist/geosearch.css" rel="stylesheet" />
<script src="https://unpkg.com/leaflet-geosearch@3.6.1/dist/geosearch.umd.js"></script>
<!-- CSS pour la taille de la carte et pour cacher le formulaire de réponse -->
<style type="text/css">
#map {
height: 500px;
}
.question-carto > div.answer-container {
display:none;
}
</style>
<!-- Question -->
Carte :
<div id="map"> </div>
<!-- JS de la carte -->
<script type="text/javascript" charset="utf-8">
// Paramètres de la carte de base
var map = L.map('map', {
crs: L.CRS.EPSG3857,
minZoom: 5,
maxZoom: 18,
}).setView([45.763, 4.732], 13); // Définir ici l'emplacement par défaut de la carte et son niveau de zoom
// Ajout de la fonction de recherche
const search = new GeoSearch.GeoSearchControl({
provider: new GeoSearch.OpenStreetMapProvider(),
});
map.addControl(search);
// Ajout de la couche WMS géoportail
var wmsGeoportail = L.tileLayer.wms('https://wxs.ign.fr/cartes/geoportail/r/wms?SERVICE=WMS&VERSION=1.3.0&CRS=EPSG:3857', {
layers: 'GEOGRAPHICALGRIDSYSTEMS.PLANIGNV2',
attribution: 'IGN-F'
}).addTo(map);
// Définir une FeatureCollection pour les polygones tracés
var drawnItems = new L.FeatureGroup();
map.addLayer(drawnItems);
// Définir le style de la couche
var myStyle = {
"color": "#ff7800",
"weight": 5,
"opacity": 0.65
};
drawnItems.setStyle(myStyle);
// Paramètres de la barre d'outils d'édition
var drawControl = new L.Control.Draw({
draw: {
circle: false,
rectangle: false,
polyline: false,
marker: false
},
edit: {
featureGroup: drawnItems
}
});
map.addControl(drawControl);
// Evenements à déclencher lors de la création d'une entité
map.on(L.Draw.Event.CREATED, function (event) {
var layer = event.layer;
drawnItems.addLayer(layer);
var JSONoutput = JSON.stringify(drawnItems.toGeoJSON());
$('#answer{SGQ}').val(JSONoutput);
});
// Evenements à déclencher lors de la suppression d'une entité
map.on(L.Draw.Event.DELETED, function (event) {
if (drawnItems.toGeoJSON().features.length == 0) {
var JSONoutput = "";
} else {
var JSONoutput = JSON.stringify(drawnItems.toGeoJSON());
}
$('#answer{SGQ}').val(JSONoutput);
});
// Evenements à déclencher lors de l'édition d'une entité
map.on(L.Draw.Event.EDITED, function (event) {
var JSONoutput = JSON.stringify(drawnItems.toGeoJSON());
$('#answer{SGQ}').val(JSONoutput);
});
</script>
Version 2 - éditeur de type "pinceau à polygones"
Cet éditeur utilise le plugin Leaflet-PaintPolygon créé par Thibault Coupin. Les polygones sont tracés avec un pinceau circulaire dont la taille est réglable. C'est moins conventionnel, mais peut se révéler plus ergonomique pour les enquêtés qui ne sont pas habitués aux SIG.
Fichier:Peindre Polygone LimeSurvey.webm
<!-- Leaflet -->
<link href="https://unpkg.com/leaflet@1.9.1/dist/leaflet.css" rel="stylesheet" />
<script src="https://unpkg.com/leaflet@1.9.1/dist/leaflet.js"></script>
<!-- Leaflet GeoSearch -->
<link href="https://unpkg.com/leaflet-geosearch@3.6.1/dist/geosearch.css" rel="stylesheet" />
<script src="https://unpkg.com/leaflet-geosearch@3.6.1/dist/geosearch.umd.js"></script>
<!-- Leaflet Paint-Polygon by Thibault Coupin -->
<script src="https://cdn.jsdelivr.net/npm/leaflet-paintpolygon@1.2.1-alpha.1/dist/Leaflet.PaintPolygon.min.js"></script>
<!-- CSS pour la taille de la carte et pour cacher le formulaire de réponse -->
<style type="text/css">
#map {
height: 500px;
}
.question-carto > div.answer-container {
display:none;
}
/* Ci-dessous un fix pour le contrôle de la taille du pinceau qui s'affiche mal sur certaines versions de Limesurvey */
.leaflet-control-paintpolygon-menu-open {
width: 150px;
}
</style>
<!-- Question -->
Carte :
<div id="map"> </div>
<!-- JS de la carte -->
<script type="text/javascript" charset="utf-8">
// Paramètres de la carte de base
var map = L.map('map', {
crs: L.CRS.EPSG3857,
minZoom: 5,
maxZoom: 18,
}).setView([45.763, 4.732], 13); // Définir ici l'emplacement par défaut de la carte et son niveau de zoom
// Ajout de la fonction de recherche
const search = new GeoSearch.GeoSearchControl({
provider: new GeoSearch.OpenStreetMapProvider(),
});
map.addControl(search);
// Ajout de la couche WMS géoportail
var wmsGeoportail = L.tileLayer.wms('https://wxs.ign.fr/cartes/geoportail/r/wms?SERVICE=WMS&VERSION=1.3.0&CRS=EPSG:3857', {
layers: 'GEOGRAPHICALGRIDSYSTEMS.PLANIGNV2',
attribution: 'IGN-F'
}).addTo(map);
// Ajout du pinceau à polygones
var paintControl = L.control.paintPolygon({
// Définition du style des polygones tracés
layerOptions: {
"color": "#ff7800",
"weight": 5,
"opacity": 0.65
},
// Définition de la position de la barre d'outils sur la carte
position: "topleft",
minRadius: 10, // Taille minimale du pinceau (en pixels)
maxRadius: 50, // Taille maximale du pinceau (en pixels)
menu: {
size: true // Afficher (true) ou non (false) le réglage de la taille du pinceau
}
});
paintControl.addTo(map);
function updateDrawnItems(){
var JSONoutput = JSON.stringify(paintControl.getData());
$('#answer{SGQ}').val(JSONoutput);
}
setInterval('updateDrawnItems()', 1000); // Mise à jour des entités tracées toutes les secondes
</script>
Script R pour extraire les entités vectorielles des résultats LimeSurvey
Le script suivant permet d'extraire toutes les réponses vectorielles d'une question dans un geopackage, en prenant soin de créer un champ "id_limesurvey" dans celui-ci pour pouvoir faire des jointures attributaires a posteriori.
library(dplyr)
library(sf)
library(geojsonsf)
# Lire les résultats de l'enquête
data = read.csv('./results-survey.csv') %>% as_tibble()
# Definir une fonction pour ajouter l'id limesurvey à une collection de géométries sf
add_id = function(sf, id) {
sf = sf %>% mutate(id_limesurvey = id)
return(sf)
}
######
### Dans toutes les lignes suivantes, remplacez "QUESTIONID" par le nom du champ contenant les géométries à extraire
######
geom_table = data %>%
filter(grepl("\"type\":\"FeatureCollection\"", QUESTIONID)) %>% # Filtrer les réponses où des géométries ont été saisies
select(QUESTIONID, id) %>%
mutate(sf = lapply(QUESTIONID, geojson_sf), # Convertir le geojson en objets sf
sf = mapply(add_id, sf, id, SIMPLIFY = FALSE)) # Appliquer la fonction add_id pour ajouter l'id_limesurvey
# Fusionner toutes les géométries dans un seul objet sf
geometries = bind_rows(geom_table$sf)
# Ecrire l'objet sf dans un geopackage
st_write(geometries, dsn = './QUESTIONID.gpkg')