Search code examples
vuejs3openlayersfeature-clustering

OpenLayers cluster get incorrect style


I want to paint several clusters red, but when zoom the red flags disappear. I code on vue3 optionAPI.

script.js

import View from 'ol/View'
import Map from 'ol/Map'
import TileLayer from 'ol/layer/Tile'
import OSM from 'ol/source/OSM'

import VectorLayer from 'ol/layer/Vector'
import Point from 'ol/geom/Point';
import LineString from 'ol/geom/LineString';
import VectorSource from 'ol/source/Vector';
import Feature from 'ol/Feature'
import {
    Circle as CircleStyle,
    Fill,
    Stroke,
    Style,
    Text,
} from 'ol/style';
import Overlay from 'ol/Overlay';
import Cluster from "ol/source/Cluster"

import { useGeographic } from 'ol/proj';
import 'ol/ol.css'
import './style.css'
import axios from 'axios'
export default {
    name: 'MapContainer',
    components: {},`your text`
    props: {},
    data() {
        return {
            place: [43.984506, 56.305298],
            data: [],
            featuresPoints: [],
            featuresLines: [],
        }
    },
    methods: {
        async getData() {
            axios("https://someRestApiLink.com").then(res => {
                this.data = res.data;

                this.createFeachers();
                this.renderMap();
            });
        },

        setCircleStyle(feature) {
            const size = feature.get('features').length;

            let style = styleCache[size];
            if (!style) {
                style = new Style({
                    image: new CircleStyle({
                        radius: 10,
                        stroke: new Stroke({
                            color: '#fff',
                        }),
                        fill: new Fill({
                            color: '#3399CC',
                        }),
                    }),
                    text: new Text({
                        text: size.toString(),
                        fill: new Fill({
                            color: '#fff',
                        }),
                    }),
                });
                styleCache[size] = style;
            }
            return style;
        },

        createFeachers() {
            for (let item of this.data) {
                let coords = JSON.parse(item.coords);
                if (coords.length === 1) {
                    let feature = new Feature(new Point(coords[0].reverse()));
                    feature.mydata = item;
                    this.featuresPoints.push(feature);
                } else {
                    let rightCoords = coords.map(el => el.reverse());
                    let isValidFeacture = true;
                    for (let i = 0; i < rightCoords.length - 1; i++) {
                        if (Math.abs(rightCoords[i][0] - rightCoords[i + 1][0]) > .01) {
                            isValidFeacture = false;
                            break
                        }
                    }
                    if (!isValidFeacture) continue;
                    let feature = new Feature({
                        geometry: new LineString(rightCoords)
                    });
                    feature.setStyle(new Style({
                        stroke: new Stroke({
                            color: '#0000ff',
                            width: 3
                        })
                    }))
                    feature.mydata = item
                    this.featuresLines.push(feature);
                }
            }
        },

        createMap() {
            return new Map({
                target: this.$refs['map-root'],
                view: new View({
                    zoom: 12,
                    center: this.place
                }),
                layers: [
                    new TileLayer({
                        source: new OSM()
                    }),

                    this.createLineLayer(),
                    this.createPointLayer(),
                ],
            });
        },

        createPointLayer() {
            const styleCache = {};
            let cluster = new Cluster({
                distance: 15,
                minDistance: 6,
                source: new VectorSource({
                    features: this.featuresPoints,
                })
            });
            
            const mainCluster = new VectorLayer({
                source: cluster,
                style: function (feature) {

                    function calculateFired(cluster, length) {
                        let pointList = cluster.values_.features;

                        let countFired = 0;
                        for (let point of pointList) {
                            if (point.mydata.status === "Просрочен") {
                                countFired++;
                            }
                        }

                        return countFired === length ? "full" : countFired > 0 ? "several" : "none";
                    }

                    const size = feature.get('features').length;
                    let style = styleCache[size];
                    if (!style) {
                        let hasFired = calculateFired(feature, size);

                        style = new Style({
                            image: new CircleStyle({
                                radius: 10,
                                stroke: new Stroke({
                                    color: hasFired === "none" ? '#fff' : "#f00",
                                    // color: '#fff',
                                }),
                                fill: new Fill({
                                    color: hasFired === "full" ? "#f00" : '#3399CC',
                                    // color: '#3399CC',
                                }),
                            }),
                            text: new Text({
                                text: size.toString(),
                                fill: new Fill({
                                    color: '#fff',
                                }),
                            }),
                        });
                        styleCache[size] = style;
                        // console.log(style);
                    }
                    return style;
                },
            });
            mainCluster.mydata = this.featuresPoints.map(el => el.mydata);


            return mainCluster
        },

        createLineLayer() {
            return new VectorLayer({
                source: new VectorSource({
                    features: this.featuresLines,
                }),
            })
        },

        renderMap() {
            useGeographic();

            const createPopUp = this.createPopUp;

            var container = document.getElementById("popup");
            var content = document.getElementById("popup-content");

            const map = this.createMap();

            const overlay = new Overlay({
                element: container,
                autoPan: true
            });
            map.on('click', function (e) {
                let pixel = map.getEventPixel(e.originalEvent);
                if (document.getElementsByClassName('popup-content').length != 0) {
                    document.getElementsByClassName('popup-content')[0].style.display = 'none'
                }
                map.forEachFeatureAtPixel(pixel, function (feature) {
                    let data = feature.mydata ?? feature.values_.features.map(el => el.mydata);
                    let coodinate = e.coordinate;
                
                    content.innerHTML = createPopUp(data);

                    overlay.setPosition(coodinate);
                    map.addOverlay(overlay);
                });
            });
        },
    
        createPopUp(data) {

            let content = "";
            if (data.length) {
                let sortedData = data.sort((a, b)=> {
                    if (a.time.split(".").reverse().join("-") > b.time.split(".").reverse().join("-")) return 1;
                    if (a.time.split(".").reverse().join("-") < b.time.split(".").reverse().join("-")) return -1;
                    return 0;
                });
                for (let el of sortedData) {
                    content += `
                        <div class="popup-content__valueBlock">
                            <div class="popup-content__valueBlock__organization">
                                ${el.organization}:
                            </div>
                            <div class="popup-content__valueBlock__aimOfWorks">
                                Тип: ${el.aim_of_works}
                            </div>
                            <div class="popup-content__valueBlock__stripSurface">
                                Работы ведутся над: ${el.strip_surface}
                            </div>
                            <div class="popup-content__valueBlock__status">
                                ${el.status} - ${el.finish_date}
                            </div>
                        </div>
                    `;
                }
            } else {
                content = `
                    <div class="popup-content__valueBlock">
                        <div class="popup-content__valueBlock__organization">
                            ${data.organization}:
                        </div>
                        <div class="popup-content__valueBlock__aimOfWorks">
                            Тип: ${data.aim_of_works}
                        </div>
                        <div class="popup-content__valueBlock__stripSurface">
                            Работы ведутся над: ${data.strip_surface}
                        </div>
                        <div class="popup-content__valueBlock__status">
                            ${data.status} - ${data.time}
                        </div>
                    </div>
                `;
            }

            return `
                <div class="popup-content">
                    <span class="close" onclick="closePopup()">x</span>
                    <span class="count">Количество выбранных ордеров: ${data.length ?? 1}</span>
                    ${content}
                </div>
            `;
        },
    },
    mounted() {
        this.getData();
    },
}

I checked ol_uid of cluster when I rezoomed, and I was so surprised by changed this property at the same cluster. Also I tried to rerender map at every time when I changed zoom, but this not work too.

I think that I mb do something wrong on creating or rendering map or clusters.


Solution

  • I just remove cache from render function of feature. Cache which I get from doc has cache on value of cluster feature, about this on rerender clusters styles was incorrect.