MouseOver on a PointFeature

Hi All,

When I have a map with different PointFeatures I would like to display a text ( tooltip ) if the mouse moves over the PointFeature. Any idea how to realise this ?

Regards
Felix

Greetings,

Right now, there is no out of the box solution for “tooltip”. But we have an issue that should implement it. Do you need solution like this?

Below click event, not mouse over
image

Best regards,
Dmitry

Hi @d.cherkasov

This issue would be the solution. If there are a lot of points and by hovering over you find the point to click on it, would be perfect.

image

Best regards
Felix

Okay, then I will back later with temporary solution before the ticket would be implemented

1 Like

Hello again,

I did temporary solution that will show “tiptools” when user clicks on Points (or whatever you need).

image
image

Basically, this is JS executed code + a bit html:

Screen:

package com.company.jmixmapstest.view.mappoint;

import com.company.jmixmapstest.view.main.MainView;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.dependency.CssImport;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.router.Route;
import io.jmix.flowui.Notifications;
import io.jmix.flowui.view.*;
import io.jmix.mapsflowui.component.GeoMap;
import io.jmix.mapsflowui.component.model.feature.MarkerFeature;
import io.jmix.mapsflowui.component.model.source.DataVectorSource;
import io.jmix.mapsflowui.component.model.source.SourceFeatureClickNotifier;
import io.jmix.mapsflowui.component.model.source.VectorSource;
import org.locationtech.jts.geom.Point;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static io.jmix.maps.utils.GeometryUtils.createPoint;

@Route(value = "map-points", layout = MainView.class)
@ViewController(id = "MapPoint.view")
@ViewDescriptor(path = "map-point-view.xml")
@CssImport("./styles/ol-popup.css")
public class MapPointView extends StandardView {
    @ViewComponent("map.dataVectorLayer.buildingSource")
    private DataVectorSource<Object> buildingSource;

    @ViewComponent("map.vectorLayer.vectorSource")
    private VectorSource vectorSource;

    @ViewComponent("map")
    private GeoMap map;

    @ViewComponent("popup")
    private Div popup;

    @ViewComponent("popupContent")
    private Div popupContent;

    @ViewComponent("popupCloser")
    private com.vaadin.flow.component.button.Button popupCloser;

    @Autowired
    private Notifications notifications;

    private Map<String, String> markerDescriptions = new HashMap<>();

    @Subscribe
    public void onBeforeShow(final BeforeShowEvent event) {
        // Initialize marker descriptions
        markerDescriptions.put("POINT (30.327877976068475 59.94327979807139)", "Saint Petersburg, Russia");
        markerDescriptions.put("POINT (13.722223426393388 51.05181869560283)", "Dresden, Germany");
        markerDescriptions.put("POINT (-2.2449710026111562 53.47839876123808)", "Manchester, UK");

        // Add click listener
        vectorSource.addSourceFeatureClickListener(this::onVectorSourceClick);

        // Add markers
        vectorSource.addAllFeatures(List.of(
                new MarkerFeature(createPoint(30.327877976068475, 59.94327979807139)),
                new MarkerFeature(createPoint(13.722223426393388, 51.05181869560283)),
                new MarkerFeature(createPoint(-2.2449710026111562, 53.47839876123808))
        ));

        // Initialize popup styles
        initPopupStyles();

        // Add click handler for popup closer
        popupCloser.addClickListener(e -> popup.setVisible(false));
    }

    private void initPopupStyles() {
        // Styles are now defined in the CSS file
        // We just need to make sure the popup is positioned correctly
        popupCloser.setText("✖");
        popupCloser.getStyle().set("background", "none");
        popupCloser.getStyle().set("border", "none");
        popupCloser.getStyle().set("cursor", "pointer");
        popupCloser.getStyle().set("font-size", "16px");
        popupCloser.getStyle().set("position", "absolute");
        popupCloser.getStyle().set("top", "2px");
        popupCloser.getStyle().set("right", "8px");
    }

    private void onVectorSourceClick(SourceFeatureClickNotifier.SourceFeatureClickEvent event) {
        Point point = ((MarkerFeature) event.getFeature()).getPoint();
        String pointText = point.toText();

        // Get description for this point
        String description = markerDescriptions.getOrDefault(pointText, "Location: " + pointText);

        // Set popup content
        popupContent.setText(description);

        // We don't need to center the map, just show the popup

        // Position the popup near the mouse cursor with a 30-pixel offset
        int mouseX = event.getMouseEventDetails().getPageX();
        int mouseY = event.getMouseEventDetails().getPageY();

        // Use setTimeout to ensure the popup is rendered before calculating its height
        UI.getCurrent().getPage().executeJs(
                "const popup = document.querySelector('.ol-popup');" +
                "if (popup) {" +
                "  popup.style.left = '" + (mouseX - 50) + "px';" +
                "  popup.style.top = '" + (mouseY - 10) + "px';" +
                        "popup.style.height = '50px';" + //todo bad solution, non-generic
                "  popup.style.transform = 'translate(0, -100%)';" +
                "}"
        );

        // Show popup
        popup.setVisible(true);
    }
}

XML:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<view xmlns="http://jmix.io/schema/flowui/view" xmlns:maps="http://jmix.io/schema/maps/ui"
      title="msg://com.company.jmixmapstest.view.mappoint/MapPointView.title">
    <data>
        <collection id="locationsDc" class="com.company.jmixmapstest.entity.MapPoint" fetchPlan="_base">
            <loader id="mapsDl">
                <query>
                    <![CDATA[select e from MapPoint e]]>
                </query>
            </loader>
        </collection>
    </data>
    <facets>
        <dataLoadCoordinator auto="true"/>
    </facets>
    <layout>
        <div id="mapContainer" height="100%" width="100%">
            <maps:geoMap id="map" height="100%" width="100%">
                <maps:layers>
                    <maps:tile>
                        <maps:osmSource/>
                    </maps:tile>
                    <maps:vector id="markersLayer">
                        <maps:vectorSource id="markersSource"/>
                    </maps:vector>
                    <maps:vector id="dataVectorLayer">
                        <maps:dataVectorSource id="buildingSource" dataContainer="locationsDc" property="point"/>
                    </maps:vector>
                    <maps:vector id="vectorLayer">
                        <maps:vectorSource id="vectorSource"/>
                    </maps:vector>
                </maps:layers>
                <maps:mapView centerY="39.8" centerX="-98.6" zoom="4"/>
            </maps:geoMap>
            <div id="popup" visible="false" classNames="ol-popup">
                <button id="popupCloser" classNames="ol-popup-closer"></button>
                <div id="popupContent"></div>
            </div>
        </div>
    </layout>
</view>

Styles(optional):


.ol-popup {
    position: absolute;
    background-color: white;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
    padding: 15px;
    border-radius: 10px;
    border: 1px solid #cccccc;
    bottom: 12px;
    left: -50px;
    min-width: 280px;
    z-index: 1000;

    height: auto;
    min-height: fit-content;
    max-width: 300px;
    overflow-wrap: break-word;
}

.ol-popup:after, .ol-popup:before {
    top: 100%;
    border: solid transparent;
    content: " ";
    height: 0;
    width: 0;
    position: absolute;
    pointer-events: none;
}

.ol-popup:after {
    border-top-color: white;
    border-width: 10px;
    left: 48px;
    margin-left: -10px;
}

.ol-popup:before {
    border-top-color: #cccccc;
    border-width: 11px;
    left: 48px;
    margin-left: -11px;
}

.ol-popup-closer {
    text-decoration: none;
    position: absolute;
    top: 2px;
    right: 8px;
}

.ol-popup-closer:after {
    content: "✖";
}

DEMO:
jmix-maps-test.zip (257.3 KB)

Warn you that I did this solution as workaround and may contains overkill css(and other unnecessary staff like xml tags in screen) for popup and also, you can use not classic html tiptoes (like in open layer tutorial) but vaadin’s one.

Best regards,
Dmitry

Thank you @d.cherkasov

I will check, if I find a possibility with hover :wink: so there is no need of a click.

Best regards

Felix

1 Like