Create javascript for lizmap

Hello everyone, I would like to know if it is possible and, most importantly, how to create a Java script that allows you to select an entity with just a click, without using the star-shaped selection tool. I want to be able to select my entity with a single click, just like QGIS does. Thank you for your feedback!

But in QGIS, you still need to activate the “selection tool”, which would be the same the “star-shaped” icon. So i guess you still mean to use the “star shaped” icon, but then having a single click to select a feature ?

If I remember correctly, this has already been discussed, but there was a technical limitation, I might get wrong, I also like the way it is done in QGIS.

Bonjour à Tous. Comment rajouter un script java à son projet lizmap?

I guess you mean JavaScript, not Java, which is totally different.

Did you read the documentation ? (I suggest the as video as well)

Show us what you have tried, what is the error, what you have looked.

lizmap.events.on({
‘ready’: function(e) {
const layerId = ‘zich@Zones inondables’; // format: ‘layername@projectname.qgs’
const container = $(‘#smart-filter’);

    function createCheckbox(name, value, checked = false) {
        const id = `${name}-${value}`;
        return `
            <div>
                <input type="checkbox" id="${id}" name="${name}" value="${value}" ${checked ? 'checked' : ''}>
                <label for="${id}">${value}</label>
            </div>
        `;
    }

    function loadUniqueValues(layerId, attribute, filter = null, callback) {
        let data = {
            'project': LizmapProject.project,
            'layer': layerId,
            'attribute': attribute
        };
        if (filter) {
            data['filter'] = filter;
        }

        $.ajax({
            url: Lizmap.baseUrl + 'server/api/uniqueValues',
            data: data,
            success: function(response) {
                if (response && response.values) {
                    callback(response.values);
                }
            }
        });
    }

    function updateFilters() {
        const selectedSecteurs = [];
        container.find('input[name="secteur"]:checked').each(function() {
            selectedSecteurs.push($(this).val());
        });

        const filterSecteur = selectedSecteurs.length > 0
            ? `"secteur" IN (${selectedSecteurs.map(v => `'${v}'`).join(',')})`
            : null;

        loadUniqueValues(layerId, 'categorie', filterSecteur, function(categories) {
            const catContainer = container.find('#categorie');
            catContainer.html('<strong>Catégories :</strong>');
            categories.forEach(cat => {
                catContainer.append(createCheckbox('categorie', cat));
            });
        });

        loadUniqueValues(layerId, 'hauteur_zich', filterSecteur, function(hauteurs) {
            const hContainer = container.find('#hauteur_zich');
            hContainer.html('<strong>Hauteurs :</strong>');
            hauteurs.forEach(h => {
                hContainer.append(createCheckbox('hauteur_zich', h));
            });
        });
    }

    function applyFilter() {
        const secteur = [];
        const categorie = [];
        const hauteur = [];

        container.find('input[name="secteur"]:checked').each(function() {
            secteur.push(`"secteur" = '${$(this).val()}'`);
        });
        container.find('input[name="categorie"]:checked').each(function() {
            categorie.push(`"categorie" = '${$(this).val()}'`);
        });
        container.find('input[name="hauteur_zich"]:checked').each(function() {
            hauteur.push(`"hauteur_zich" = '${$(this).val()}'`);
        });

        const clauses = [...secteur, ...categorie, ...hauteur];
        const filterExpression = clauses.join(' AND ');

        lizmap.setFilter(layerId, filterExpression);
    }

    // Initial UI
    container.html(`
        <div id="secteur"><strong>Secteurs :</strong></div>
        <div id="categorie"><strong>Catégories :</strong></div>
        <div id="hauteur_zich"><strong>Hauteurs :</strong></div>
        <button id="applyFilter">Appliquer le filtre</button>
    `);

    loadUniqueValues(layerId, 'secteur', null, function(secteurs) {
        const sContainer = container.find('#secteur');
        secteurs.forEach(s => {
            sContainer.append(createCheckbox('secteur', s));
        });

        // Mise à jour en cascade
        container.on('change', 'input[name="secteur"]', updateFilters);
    });

    // Bouton appliquer
    container.on('click', '#applyFilter', applyFilter);
}

});

I watched the tutorial, I even tried to do the same javascript as the tutorial but nothing happens, should I do some server-side manipulations?

In the administration panel, in your repositories page, you need to enable “JavaScript”.

Great, it works. One last question: is it possible to only show the layers and filter step in the guided tour? If so, how can I do this? Which part should I change?:
function htmlPopup(tour) {
var html = ‘’;
var title = ‘A Lire’;
var content = ‘Bienvenue’;

// Header ( l'entête)
html += '<div class="modal-header" style="background-color:rgba(0, 0, 0, 0.7);">';
html += '<h3 style="color:white;">' + title + '</h3></div>';

// Main content : body
html += '<div class="modal-body">';
html += '<p>' + content + '</p>';

// Project Metadata
html += $('#metadata').html();

// End of main content body
html += '</div>';

// Footer
html += '<div class="modal-footer" style="background-color:rgba(0, 0, 0, 0.7);"></div>';

// Return shepherd
return {
    id: 'Popup Startup',
    text: html,
    attachTo: {
        element: 'body',
    },
    buttons: [{ text: 'Voir le tutoriel', action: tour.next }],
};

}

// Check the browser’s localstorage
function dismissTour() {
if (!localStorage.getItem(‘shepherd-tour’)) {
localStorage.setItem(‘shepherd-tour’, ‘yes’);
}
}

// Start the tutorial
function startTutorial() {
// Create a tour
const tour = new Shepherd.Tour({
useModalOverlay: true,
defaultStepOptions: {
classes: ‘shepherd-theme-custom’,
scrollTo: true,
cancelIcon: { enabled: true },
keyboardNavigation: true,
exitOnEsc: true,
when: {
show() {
// Don’t show the progress bar on first and final step
if (tour.steps.indexOf(tour.currentStep) != 0 && tour.steps.indexOf(tour.currentStep) != tour.steps.length - 1) {
const currentStepElement = tour.currentStep.el;
const header = currentStepElement.querySelector(‘.shepherd-footer’);
const progress = document.createElement(‘div’);
const innerBar = document.createElement(‘span’);
const progressPercentage = ((tour.steps.indexOf(tour.currentStep) + 1) / tour.steps.length) * 100 + ‘%’;

                    progress.className = 'shepherd-progress-bar';
                    innerBar.style.width = progressPercentage;

                    progress.style.minWidth = '10px';

                    progress.appendChild(innerBar);
                    header.insertBefore(progress, currentStepElement.querySelector('.shepherd-button'));
                }
            }
        }
    },
});

// Add the first step (the popup)
tour.addStep(htmlPopup(tour));

// Add automatic tour steps
const tourSteps = $('.nav.nav-list li').filter(function () {
    return this.style.display !== 'none' && !this.classList.contains('hide');
}).map(function (index, element) {
    const aElement = $(element).find('a')[0];

    return {
        id: `Étape ${index + 1}`,
        title: `Étape ${index + 1}`,
        text: $(aElement).attr('data-original-title'),
        attachTo: {
            element: aElement,
            on: 'right',
        },
        buttons: [{ text: 'Précédent', action: tour.back }, { text: 'Suivant', action: tour.next }],
    };
}).get();

// Add 3 more personal tour steps
const stepHappyCoding = [
    {
        id: 'move-map',
        title: 'Déplacer la carte',
        text: 'Déplacez la carte pour explorer d\'autres zones.',
        attachTo: {
            element: 'span#navbar',
            on: 'left',
        },
        buttons: [{ text: 'Précédent', action: tour.back }, { text: 'Suivant', action: tour.next }],
    },

    {
        id: 'overview',
        title: 'Etape 2',
        text: 'La boîte de prévisualisation vous permet de voir une vue d\'ensemble de la carte.',
        attachTo: {
            element: '#overview-box',
            on: 'top',
        },
        buttons: [{ text: 'Précédent', action: tour.back }, { text: 'Suivant', action: tour.next }],
    },

    {
        id: 'happy-coding',
        title: 'Merci pour votre lecture',
        text: 'C\'est tout pour ce tutoriel, allez-y et commencez à explorer cette carte interactive 😉.',
        attachTo: {
            element: 'body',
        },
        buttons: [
            { text: 'Précédent', action: tour.back },
            {
                text: 'Terminer',
                action() {
                    // Dismiss the tour when the finish button is clicked
                    dismissTour();
                    return this.hide();
                }
            }],
    }
];

// Spread the steps into the tour array
tourSteps.push(...stepHappyCoding);

// Modification de l'étape 1
if (tourSteps.length > 0) {
    tourSteps[0].text += '<br><br>cliquez ici pour retourner sur la page d\'accueil';
	tourSteps[0].title = 'Page d\'acceuil';
}

// Modification de l'étape 2
if (tourSteps.length > 1) {
    tourSteps[1].text += 'Cliquez ici si vous ne voulez pas retourner sur la page d\'accueil, vous avez la possibilité d\'accéder aux répertoires des applications directement.';
    tourSteps[1].title = 'Page des répertoires';
}

// Modification de l'étape 3
if (tourSteps.length > 2) {
    tourSteps[2].text = 'Par cet onglet vous accédez à l\'ensemble des couches qui composent ce projet. Cliquez dessus pour les voir s\'afficher.';
    tourSteps[2].title = 'Les couches';
}

// Modification de l'étape 4
if (tourSteps.length > 3) {
    tourSteps[3].text = 'Vous trouverez ici les informations sur l\'ensemble du projet.';
    tourSteps[3].title = 'Informations';
}

// Modification de l'étape 5
if (tourSteps.length > 4) {
    tourSteps[4].text = 'Nous avons intégré à notre application des statistiques permettant de visualiser graphiquement le nombre de bâtiments potentiellement impactés par les ZICH.';
    tourSteps[4].title = 'Fenêtre contextuelle';
}

// Modification de l'étape 6
if (tourSteps.length > 5) {
    tourSteps[5].text = 'Nous avons intégré à notre applications des statistiques afin de voir graphiquement le nombre de bâtiments qui pourraient être impactés par les ZICH.';
    tourSteps[5].title = 'Statistiques';
}

// Modification de l'étape 7
if (tourSteps.length > 6) {
    tourSteps[6].text = 'pour filtrer, il vous faut cliquez sur un critère.';
    tourSteps[6].title = 'Filtres';
}

// Ajouter les étapes au tour
tour.addSteps(tourSteps);

// Démarrer le tutoriel automatiquement chaque fois que l'application s\'ouvre
tour.start();

// Retourner le tour
return tour;

}

lizMap.events.on({
uicreated: function () {
// Inclure le CSS CDN de shepherd
var link = document.createElement(‘link’);
link.href = ‘https://cdn.jsdelivr.net/npm/shepherd.js@10.0.1/dist/css/shepherd.css’;
link.rel = ‘stylesheet’;
document.head.appendChild(link);

    // Inclure le JS CDN de shepherd
    var script = document.createElement('script');
    script.src = 'https://cdn.jsdelivr.net/npm/shepherd.js@10.0.1/dist/js/shepherd.min.js';
    script.type = 'text/javascript';

    script.onload = function () {
        console.log('Shepherd.js a été chargé avec succès!');
        const tour = startTutorial();

        // Dismiss the tour when the cancel icon is clicked
        tour.on('cancel', dismissTour);
    };

    script.onerror = function () {
        console.error('Erreur lors du chargement de shepherd.js');
    };

    document.head.appendChild(script);
}

});