Viglino / ol-ext

Cool extensions for Openlayers (ol) - animated clusters, CSS popup, Font Awesome symbol renderer, charts for statistical map (pie/bar), layer switcher, wikipedia layer, animations, canvas filters.
https://viglino.github.io/ol-ext/
Other
1.24k stars 462 forks source link

What's the best way to store styles in cache with cluster and dynamic text top of feature? #407

Closed asimus90 closed 4 years ago

asimus90 commented 4 years ago

Hi @Viglino

i am trying to cache styles in a list. But i have over 3k devices on the map and if i don't store styles in cache, screen freezes while zooming in/out even i use clusters.

i use styleCache mechanism for clusters with a chart. But i am stacked at device features.

There are 2 parameter to choose image style and dynamic title/text style for each device. How do i store these styles in a static list by javascript?

activeState = 1, 2, 3 .. (active, passive, standing..) this choose color of image (blue, green, red) deviceType = 1, 2, 3 (regular gps device, mobile, special staff..) this set actual image and also these icons have rotations/directions came from gateway. title/text: every device has own title. And i've to show own device name top of image.

You may understand with code snippet below (more than explanation above.)

var clusterSourceDevices = new ol.source.Cluster({
    distance: 40,
    source: new ol.source.Vector()
});

var clusterLayerDevices = new ol.layer.AnimatedCluster({
    name: 'Cluster',
    baseLayer: false,
    source: clusterSourceDevices,
    animationDuration: 700,
    style: getStyleDevices
});

var styleCacheDevice = [];

function getStyleDevices(feature, resolution) {

    var features = feature.get('features');

    var size = features.length;

    if (size === 1) {
        return featureStyleDevice(feature);
    } else {
        var data = [0, 0, 0, 0]; (type of devices)

        for (var i = 0; i < size; i++) {
            var f = features[i];
            data[f.get('type')]++;
        }

        var style = styleCacheDevice[data.join(',')];

        if (!style) {
            var radius = Math.min(size + 17, 20);

            style = styleCacheDevice[data.join(',')] = new ol.style.Style({
                image: new ol.style.Chart({
                    type: 'donut',
                    radius: radius,
                    data: data, //defined above - type of devices
                    rotateWithView: true,
                    stroke: new ol.style.Stroke({
                        color: "rgba(0,0,0,0)",
                        width: 0
                    })
                }),
                text: new ol.style.Text({
                    text: size.toString(), // cluster count
                    font: 'bold 15px Arial',
                    fill: new ol.style.Fill({
                        color: '#000'
                    })
                })
            });
        }

        return [style];
    }
}

function featureStyleDevice(f) {

    var sel = f.get('features')

    if (sel) {
        var type = sel[0].get('type');
        var direction = sel[0].get('direction');
        var style = styleCacheDevice[type];
        var deviceType = sel[0].get('deviceType');
        var info = sel[0].get('info');

        var imgSource;

        // title of device
        var styleDeviceText = new ol.style.Text({
            offsetY: -35,
            font: '14px Calibri,san-serif',
            fill: new ol.style.Fill({ color: '#000000' }),
            backgroundFill: new ol.style.Fill({ color: '#FFFFFF' }),
            stroke: new ol.style.Stroke({
                color: '#000000', width: 1
            }),
            text: info // title of device
        });

        if (!style) {
            if (type == 1) {
                if (deviceType == 1) {
                    imgSource = '/greenGpsDevice_44.png';
                }
                else if (deviceType == 2) {
                    imgSource = '/greenMobileDevice_44.png';
                }

                style = new ol.style.Style({
                    image: new ol.style.Icon(({ opacity: 1, src: imgSource, rotation: direction })),
                    text: styleDeviceText
                });
            }
            else if (type == 2) {
                if (deviceType == 1) {
                    imgSource = '/redGpsDevice_44.png';

                }
                else if (deviceType == 2) {
                    imgSource = '/redMobileDevice_44.png';
                }

                style = new ol.style.Style({
                    image: new ol.style.Icon(({ opacity: 1, src: imgSource, rotation: direction })),
                    text: styleDeviceText
                });
            }
            else if (type == 3) {
                if (deviceType == 1) {
                    imgSource = '/blueGpsDevice_44.png';
                }
                else if (deviceType == 2) {
                    imgSource = '/blueMobileDevice_44.png';
                }

                style = new ol.style.Style({
                    image: new ol.style.Icon(({ opacity: 1, src: imgSource, rotation: direction })),
                    text: styleDeviceText //title of device
                });
            }
        }

        return [style];
    }
    else return [new ol.style.Style({
        stroke: new ol.style.Stroke({
            color: "#fff",
            width: 1
        })
    })];
}

I can see over 100k feature without any freezing or lag on your demo. But i couldn't handle 3k feature at mine. What's the problem above? Hope you help me at this openlayers and ol-ext experiments.

asimus90 commented 4 years ago

This is also update function that data come from gateway (gps devices send data to gateway and it sends data through web sockets to simulate.)

`function UpdateDeviceFeature(data) {

var featureText = "";
var curFeature = clusterSourceDevice.getSource().getFeatureById(data.deviceId);

if (curFeature != null) {
    var geometry = curFeature.getGeometry();

    var direction = parseInt(data.course);
    var radDirection = direction * Math.PI / 180;
    var speed = parseInt(data.speed);

    curFeature.set('direction', radDirection);
    curFeature.set('speed', speed);

    var devDate = new Date(data.dateTime);
    var dDate = diffDate(devDate);

    var deviceType = parseInt(data.type);

    if (deviceType == 1) { // regular gps device

        var title = data.info;

        featureText = title;

    } else if (deviceType == 2) { // mobile device

        var deviceName = data.deviceName;
        featuretext = deviceName;
    }
    // Calculating active/passive/standing states here.(comparing between current time and device last datetime)
    if (dDate > devTimeoutTime) {
        curFeature.set('type', 1);
    }
    else { // also i check speed here to set active state type
        if (parseInt(data.speed) == 0) {
            curFeature.set('type', 2);
        }
        else {
            curFeature.set('type', 3);
        }
    }

    curFeature.getGeometry().setCoordinates(ol.proj.transform([data.longitude, data.latitude], 'EPSG:4326', 'EPSG:3857'));
}

} `

Viglino commented 4 years ago

You have to cache your style or you'll calculate a new style on each feature draw. It seems featureStyleDevice doesn't use the cache and always returns a new ol.style.Style each time.

asimus90 commented 4 years ago

What do you suggest for it? Should i store 6x geometry (there are 6x different images) in an array/matrices and call them from there? I've tried something like that but some features disappeared after show up in a sec. I couldn't determine problem why do those features disappear a second later. Thanks again. i'll keep researching a better solution for it.

Viglino commented 4 years ago

Do you have a working example on codepen or jsfiddle or so?