Search code examples
openlayersopenlayers-5

Marker with icon and text


I am displaying marker with icon first in openlayers map and when zooming in certain zoom level text is showing as overlay below the icon but it doesn't work smoothly. Please find my code below:

var distance = heatMap ? 14 : 40;
this.ClusterSource = new ol.source.Cluster({
     distance: distance,
     source: vectorSource
});

var vectorLayer = new ol.layer.Vector({
    renderMode: 'image',
    source: this.ClusterSource,
    style: this.styleFunction,
    zIndex: 9999
});

styleFunction = (feature, resolution) => {
    let self = this;

    if (!feature || !resolution) return;

    let finalStyle: ol.style.Style;
    let features = <ol.Feature[]>feature.get("features");

    if (self.MapControl.getView().getZoom() > 12) {
        this.displayPopOver(features);
    } else {
        this.removePopOver(features);
    }

    if (features.length === 1) {
        const color = self.getIconColorSinglePlace(feature.get("features")[0]);
        finalStyle = (<any>window).styleCache[color];
        if (!finalStyle) {
            finalStyle = new ol.style.Style({
                image: new ol.style.Circle({
                    radius: 10,
                    fill: new ol.style.Fill({ color: `#${color}` }),
                    stroke: new ol.style.Stroke({
                        color: 'white', width: 2
                    })
                })
            });
            (<any>window).styleCache[color] = finalStyle;
        }
    }
    else if (features.length > 1) {
        if (resolution > 1) {
            finalStyle = self.getStyleForCluster(features.length);
        }
        else self.displayOverlapping(features);
    }

    return finalStyle;
}

// display name attached to marker
displayPopOver = (features: ol.Feature[]) => {
    if (features) {
        features.forEach((feature, index) => {
            // show popover overlay for each record
            this.popoverOverlay(feature, index);
        });
    }
};

// add pop overlay to display entity name
popoverOverlay = (feature, index) => {
    var element = document.createElement('div');
    element.style.cssText = 'margin-left: -50px; margin-top:5px;';
    element.innerHTML = (feature && feature.get('name').length > 0) ? feature.get('name') : '';

    let overlay = new ol.Overlay({
        id: index + 'featureName',
        element: element
    });

    const coordinate = (<any>feature.getGeometry()).getCoordinates();
    overlay.setPosition(coordinate);
    this.MapControl.addOverlay(overlay);
};

// remove all entity name attached to marker when zoom level below 10
removePopOver = (features: ol.Feature[]) => {
    if (features) {
        let overlays = <ol.Overlay[]>[];

        features.forEach((feature, index) => {
            this.MapControl.getOverlays().forEach(overlay => {
                if (overlay.getId() === index + 'featureName')
                    overlays.push(overlay);
            });
        });

        this.deleteOverlays(overlays);
    }
};

I want to make it smooth and perfect and in my current code sometimes it is slowing down the map. I could use style text instead of overlay but the problem is feature has features which need to manage by loop as a result I can't use feature.get to get the name of feature.


Solution

  • You can easily show text as labels in the styling of individual features. It doesn't make sense to show popovers for each feature when the features are clustered, it defeats the purpose of clustering as they would obscure each other and the cluster..

    styleFunction = (feature, resolution) => {
        let self = this;
    
        if (!feature || !resolution) return;
    
        let finalStyle: ol.style.Style;
        let features = <ol.Feature[]>feature.get("features");
    
        if (features.length === 1) {
            const color = self.getIconColorSinglePlace(feature.get("features")[0]);
            finalStyle = (<any>window).styleCache[color];
            if (!finalStyle) {
                finalStyle = new ol.style.Style({
                    image: new ol.style.Circle({
                        radius: 10,
                        fill: new ol.style.Fill({ color: `#${color}` }),
                        stroke: new ol.style.Stroke({
                            color: 'white', width: 2
                        })
                    }),
                    text: new ol.style.Text({
                        // add font and other options
                    })
                });
                (<any>window).styleCache[color] = finalStyle;
            }
            let label = '';
            if (resolution < map.getView().getResolutionForZoom(12)) {
              label = feature.get("features")[0].get('name');
            }
            finalStyle.getText().setText(label);
        }
        else if (features.length > 1) {
            if (resolution > 1) {
                finalStyle = self.getStyleForCluster(features.length);
            }
            else self.displayOverlapping(features);
        }
    
        return finalStyle;
    }