All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mware.web.product.map.util.cache.js Maven / Gradle / Ivy

The newest version!
/*
 * This file is part of the BigConnect project.
 *
 * Copyright (c) 2013-2020 MWARE SOLUTIONS SRL
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License version 3
 * as published by the Free Software Foundation with the addition of the
 * following permission added to Section 15 as permitted in Section 7(a):
 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
 * MWARE SOLUTIONS SRL, MWARE SOLUTIONS SRL DISCLAIMS THE WARRANTY OF
 * NON INFRINGEMENT OF THIRD PARTY RIGHTS
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA, 02110-1301 USA, or download the license from the following URL:
 * https://www.gnu.org/licenses/agpl-3.0.txt
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License.
 *
 * You can be released from the requirements of the license by purchasing
 * a commercial license. Buying such a license is mandatory as soon as you
 * develop commercial activities involving the BigConnect software without
 * disclosing the source code of your own applications.
 *
 * These activities include: offering paid services to customers as an ASP,
 * embedding the product in a web application, shipping BigConnect with a
 * closed source product.
 */
define(['openlayers', 'jscache'], function(ol, Cache) {

    const MAX_CACHE_ITEMS = 1000;
    const EXPIRATION_MINUTES = 5;
    const DEBUG = false;
    const SELECTED_COLOR = '#0088cc'

    const cache = new Cache(MAX_CACHE_ITEMS, DEBUG);
    const geoCache = new Cache(MAX_CACHE_ITEMS, DEBUG);
    const cacheOptions = {
        expirationSliding: EXPIRATION_MINUTES * 60
    }
    const FocusPadding = 3;
    const FocusCirclePadding = 6;
    const FocusRadius = 5;
    const FocusFill = new ol.style.Fill({ color: SELECTED_COLOR });
    const FocusStroke = new ol.style.Stroke({ color: '#ffffff', width: 1 });

    return {
        clear() {
            cache.clear();
            geoCache.clear();
        },

        getOrCreateGeometry(id, geoLocations) {
            const hash = ['geo', id, ...geoLocations.map(([latitude, longitude]) => latitude + ',' + longitude)].join(',')
            let geo = geoCache.getItem(hash);
            if (!geo) {
                geo = new ol.geom.MultiPoint(geoLocations.map(geo => ol.proj.fromLonLat(geo)))
                geoCache.setItem(hash, geo, cacheOptions);
            }
            return geo;
        },

        getOrCreateFeature(options, focused) {
            const key = hash(options, focused);
            const cached = cache.getItem(key)
            if (cached) {
                return cached;
            }

            const style = [new ol.style.Style({ image: new ol.style.Icon(options), zIndex: 1 })];
            if (focused) {
                const { imgSize, anchor = [0.5, 0.5] } = options;
                style.splice(0, 0, new ol.style.Style({
                    renderer(point, { context, feature, geometry, pixelRatio, resolution, rotation }) {
                        context.setTransform(1, 0, 0, 1, 0, 0);
                        const x = point[0] - imgSize[0] * anchor[0] - FocusPadding * pixelRatio;
                        const y = point[1] - imgSize[1] * anchor[1] - FocusPadding * pixelRatio;
                        const w = imgSize[0] + FocusPadding * 2 * pixelRatio;
                        const h = imgSize[1] + FocusPadding * 2 * pixelRatio;
                        const radius = FocusRadius * pixelRatio;

                        context.save();
                        context.globalAlpha = 0.2;
                        context.beginPath();
                        context.moveTo(x + radius, y);
                        context.lineTo(x + w - radius, y);
                        context.quadraticCurveTo(x + w, y, x + w, y + radius);
                        context.lineTo(x + w, y + h - radius);
                        context.quadraticCurveTo(x + w, y + h, x + w - radius, y + h);
                        context.lineTo(x + radius, y + h);
                        context.quadraticCurveTo(x, y + h, x, y + h - radius);
                        context.lineTo(x, y + radius);
                        context.quadraticCurveTo(x, y, x + radius, y);
                        context.closePath();
                        context.fillStyle = SELECTED_COLOR;
                        context.shadowBlur = radius / 2;
                        context.shadowColor = 'white';
                        context.shadowOffsetX = 0;
                        context.shadowOffsetY = 0;
                        context.fill();

                        context.restore();
                    },
                    zIndex: 0
                }))
            }
            cache.setItem(key, style, cacheOptions);

            return style;
        },

        getOrCreateCluster({
            count, radius: r, selected, selectionState, selectionCount,
            focusStats: { all, some, dim }
        }) {
            const key = [
                'cluster', count, r, selected,
                selectionState, selectionCount,
                all, some, dim
            ].join('|');

            let style = cache.getItem(key);
            if (style) {
                return style;
            }

            style = [
                new ol.style.Style({
                    image: new ol.style.Circle({
                        radius: r * devicePixelRatio,
                        fill: new ol.style.Fill({ color: 'rgba(255,255,255,0.01)' })
                    })
                }),
                new ol.style.Style({
                    renderer([x, y], { context, pixelRatio }) {
                        const radius = r * pixelRatio;
                        var unselectedFill = 'rgba(241,59,60,0.8)',
                            selectedFill = 'rgba(0,112,195,0.8)',
                            selectedFillNoAlpha = 'rgb(0,112,195)',
                            unselectedStroke = '#AD2E2E',
                            stroke = selected ? '#08538B' : unselectedStroke,
                            lineWidth = 2 * pixelRatio,
                            textStroke = stroke,
                            fill = selected ? selectedFill : unselectedFill;

                        if (selected && selectionState === 'some') {
                            fill = unselectedFill;
                            textStroke = unselectedStroke;
                            stroke = unselectedStroke;
                        }

                        context.save();
                        if (dim && !some) {
                            context.globalAlpha = 0.4;
                        }
                        context.setTransform(1, 0, 0, 1, 0, 0);
                        context.translate(x, y);

                        if (some) {
                            context.beginPath();
                            context.arc(0, 0, radius + FocusCirclePadding * pixelRatio, 0, 2 * Math.PI, true);
                            context.fillStyle = 'rgba(0, 136, 204, 0.2)';
                            context.fill();
                            context.closePath();
                        }

                        context.beginPath();
                        context.arc(0, 0, radius, 0, 2 * Math.PI, true);
                        context.fillStyle = fill;
                        context.fill();

                        if (selectionState === 'some') {
                            context.strokeStyle = stroke;
                            context.lineWidth = Math.max(4 * pixelRatio, lineWidth);
                            context.stroke();
                            context.closePath();
                            const portion = Math.max(0.1, Math.min(0.9, selectionCount / count));
                            context.beginPath();
                            context.arc(0, 0, radius, Math.PI / -2, Math.PI * 2 * portion - Math.PI / 2, false);
                            context.strokeStyle = selectedFillNoAlpha;
                            context.stroke();
                            context.closePath();
                        } else {
                            context.strokeStyle = stroke;
                            context.lineWidth = lineWidth;
                            context.stroke();
                            context.closePath();
                        }

                        context.font = `bold condensed ${radius}px sans-serif`;
                        context.textAlign = 'center';
                        context.fillStyle = 'white';
                        context.textBaseline = 'middle';
                        context.strokeStyle = textStroke;
                        context.lineWidth = pixelRatio;

                        if (some && some !== count) {
                            const text = some.toString();
                            context.strokeText(text, 0, radius * -0.4);
                            context.fillText(text, 0, radius * -0.4);

                            context.font = `bold condensed ${radius * 0.7}px sans-serif`;
                            context.fillStyle = 'rgba(255,255,255,0.8)';
                            context.strokeText(count.toString(), 0, radius * 0.55);
                            context.fillText(count.toString(), 0, radius * 0.55);

                            context.beginPath();
                            context.moveTo(radius * -0.5, radius * 0.1);
                            context.lineTo(radius * 0.5, radius * 0.1);
                            context.strokeStyle = 'rgba(255,255,255,0.3)';
                            context.stroke();
                            context.closePath();

                        } else {
                            const text = count.toString();
                            context.strokeText(text, 0, 0);
                            context.fillText(text, 0, 0);
                        }

                        context.restore();
                    }
                })
            ];

            cache.setItem(key, style, cacheOptions);
            return style;
        },

        addFocus(radius, list) {
            const key = `focus${radius}`;
            let focusStyle = cache.getItem(key);
            if (!focusStyle) {
                focusStyle = new ol.style.Style({
                    image: new ol.style.Circle({
                        radius: radius + 5,
                        fill: FocusFill,
                        stroke: FocusStroke
                    }),
                    zIndex: 0
                })
                focusStyle.getImage().setOpacity(0.2);
                cache.setItem(key, focusStyle, cacheOptions);
            }

            return [focusStyle, ...list];
        },

        addDim(radius, list) {
            const image = list.length && list[0].getImage();
            if (image) {
                image.setOpacity(0.4);
            }
            return list;
        },

        reset(radius, list) {
            const image = list.length && list[0].getImage();
            if (image && image.getOpacity() < 1) {
                image.setOpacity(1);
            }
            return list;
        }
    }

    function hash({ src, imgSize, scale, anchor }, focused) {
        return [focused, src, `${imgSize[0]},${imgSize[1]}`, scale, `${anchor[0]},${anchor[1]}`].join('|')
    }

});




© 2015 - 2024 Weber Informatics LLC | Privacy Policy