de.symeda.sormas.ui.dashboard.map.DashboardMapComponent.java Source code

Java tutorial

Introduction

Here is the source code for de.symeda.sormas.ui.dashboard.map.DashboardMapComponent.java

Source

/*******************************************************************************
 * SORMAS - Surveillance Outbreak Response Management & Analysis System
 * Copyright  2016-2018 Helmholtz-Zentrum fr Infektionsforschung GmbH (HZI)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *******************************************************************************/
package de.symeda.sormas.ui.dashboard.map;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.vaadin.hene.popupbutton.PopupButton;

import com.vaadin.icons.VaadinIcons;
import com.vaadin.server.ExternalResource;
import com.vaadin.shared.ui.ContentMode;
import com.vaadin.ui.AbstractComponent;
import com.vaadin.ui.AbstractOrderedLayout;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Image;
import com.vaadin.ui.Label;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;
import com.vaadin.ui.themes.ValoTheme;
import com.vaadin.v7.shared.ui.grid.HeightMode;
import com.vaadin.v7.ui.CheckBox;
import com.vaadin.v7.ui.OptionGroup;

import de.symeda.sormas.api.CaseMeasure;
import de.symeda.sormas.api.Disease;
import de.symeda.sormas.api.FacadeProvider;
import de.symeda.sormas.api.caze.CaseClassification;
import de.symeda.sormas.api.caze.CaseDataDto;
import de.symeda.sormas.api.caze.CaseFacade;
import de.symeda.sormas.api.caze.MapCaseDto;
import de.symeda.sormas.api.contact.ContactClassification;
import de.symeda.sormas.api.contact.MapContactDto;
import de.symeda.sormas.api.event.DashboardEventDto;
import de.symeda.sormas.api.event.EventStatus;
import de.symeda.sormas.api.facility.FacilityDto;
import de.symeda.sormas.api.facility.FacilityReferenceDto;
import de.symeda.sormas.api.i18n.Captions;
import de.symeda.sormas.api.i18n.I18nProperties;
import de.symeda.sormas.api.i18n.Strings;
import de.symeda.sormas.api.region.DistrictDto;
import de.symeda.sormas.api.region.DistrictReferenceDto;
import de.symeda.sormas.api.region.GeoLatLon;
import de.symeda.sormas.api.region.RegionReferenceDto;
import de.symeda.sormas.api.user.UserDto;
import de.symeda.sormas.api.user.UserRole;
import de.symeda.sormas.api.utils.DataHelper;
import de.symeda.sormas.api.utils.DataHelper.Pair;
import de.symeda.sormas.ui.ControllerProvider;
import de.symeda.sormas.ui.UserProvider;
import de.symeda.sormas.ui.dashboard.DashboardDataProvider;
import de.symeda.sormas.ui.dashboard.DashboardType;
import de.symeda.sormas.ui.map.LeafletMap;
import de.symeda.sormas.ui.map.LeafletMap.MarkerClickEvent;
import de.symeda.sormas.ui.map.LeafletMap.MarkerClickListener;
import de.symeda.sormas.ui.map.LeafletMarker;
import de.symeda.sormas.ui.map.LeafletPolygon;
import de.symeda.sormas.ui.map.MarkerIcon;
import de.symeda.sormas.ui.utils.CssStyles;
import de.symeda.sormas.ui.utils.VaadinUiUtil;

@SuppressWarnings("serial")
public class DashboardMapComponent extends VerticalLayout {

    final static Logger logger = LoggerFactory.getLogger(DashboardMapComponent.class);

    private static final String CASES_GROUP_ID = "cases";
    private static final String CONTACTS_GROUP_ID = "contacts";
    private static final String EVENTS_GROUP_ID = "events";
    private static final String REGIONS_GROUP_ID = "regions";
    private static final String DISTRICTS_GROUP_ID = "districts";

    // Layouts and components
    private final DashboardDataProvider dashboardDataProvider;
    private final LeafletMap map;
    private PopupButton legendDropdown;

    // Layers
    private boolean showCases;
    private boolean showContacts;
    private boolean showConfirmedContacts;
    private boolean showUnconfirmedContacts;
    private boolean showEvents;
    private boolean showRegions;

    // Entities
    private final HashMap<FacilityReferenceDto, List<MapCaseDto>> casesByFacility = new HashMap<>();
    private List<MapCaseDto> mapCaseDtos = new ArrayList<>();
    private List<MapCaseDto> mapAndFacilityCases = new ArrayList<>();
    private List<MapContactDto> mapContactDtos = new ArrayList<>();

    // Markers
    private final List<FacilityReferenceDto> markerCaseFacilities = new ArrayList<FacilityReferenceDto>();
    private final List<MapContactDto> markerContacts = new ArrayList<MapContactDto>();
    private final List<DashboardEventDto> markerEvents = new ArrayList<DashboardEventDto>();
    private final List<RegionReferenceDto> polygonRegions = new ArrayList<RegionReferenceDto>();
    private final List<DistrictReferenceDto> polygonDistricts = new ArrayList<DistrictReferenceDto>();

    // Others
    private CaseMeasure caseMeasure = CaseMeasure.CASE_COUNT;
    private MapCaseDisplayMode mapCaseDisplayMode = MapCaseDisplayMode.HEALTH_FACILITY_OR_CASE_ADDRESS;
    private BigDecimal districtValuesLowerQuartile;
    private BigDecimal districtValuesMedian;
    private BigDecimal districtValuesUpperQuartile;
    private Consumer<Boolean> externalExpandListener;
    private boolean emptyPopulationDistrictPresent;

    public DashboardMapComponent(DashboardDataProvider dashboardDataProvider) {
        this.dashboardDataProvider = dashboardDataProvider;

        setMargin(false);
        setSpacing(false);
        setSizeFull();

        map = new LeafletMap();
        map.setSizeFull();
        map.addMarkerClickListener(new MarkerClickListener() {
            @Override
            public void markerClick(MarkerClickEvent event) {
                onMarkerClicked(event.getGroupId(), event.getMarkerIndex());
            }
        });

        if (UserProvider.getCurrent().hasUserRole(UserRole.NATIONAL_USER)
                || UserProvider.getCurrent().hasUserRole(UserRole.NATIONAL_CLINICIAN)
                || UserProvider.getCurrent().hasUserRole(UserRole.NATIONAL_OBSERVER)) {
            map.setZoom(6);
            GeoLatLon mapCenter = FacadeProvider.getGeoShapeProvider().getCenterOfAllRegions();
            map.setCenter(mapCenter.getLon(), mapCenter.getLat());
        } else {
            UserDto user = UserProvider.getCurrent().getUser();
            if (user.getRegion() != null) {
                GeoLatLon mapCenter = FacadeProvider.getGeoShapeProvider().getCenterOfRegion(user.getRegion());
                map.setCenter(mapCenter.getLon(), mapCenter.getLat());
            } else {
                GeoLatLon mapCenter = FacadeProvider.getGeoShapeProvider().getCenterOfAllRegions();
                map.setCenter(mapCenter.getLon(), mapCenter.getLat());
            }
            map.setZoom(6);
        }

        if (dashboardDataProvider.getDashboardType() == DashboardType.SURVEILLANCE) {
            showCases = true;
            showContacts = true;
            showEvents = true;
            showConfirmedContacts = true;
            showUnconfirmedContacts = true;
        } else if (dashboardDataProvider.getDashboardType() == DashboardType.CONTACTS) {
            showCases = false;
            showContacts = true;
            showEvents = false;
            showConfirmedContacts = true;
            showUnconfirmedContacts = true;
        }

        this.setMargin(true);

        // Add components
        addComponent(createHeader());
        addComponent(map);
        addComponent(createFooter());
        setExpandRatio(map, 1);
    }

    public void refreshMap() {
        clearRegionShapes();
        clearCaseMarkers();
        clearContactMarkers();
        clearEventMarkers();

        Date fromDate = dashboardDataProvider.getFromDate();
        Date toDate = dashboardDataProvider.getToDate();
        RegionReferenceDto region = dashboardDataProvider.getRegion();
        DistrictReferenceDto district = dashboardDataProvider.getDistrict();
        Disease disease = dashboardDataProvider.getDisease();

        if (showRegions) {
            showRegionsShapes(caseMeasure, fromDate, toDate, dashboardDataProvider.getDisease());
        }
        if (showCases) {
            showCaseMarkers(FacadeProvider.getCaseFacade().getCasesForMap(region, district, disease, fromDate,
                    toDate, UserProvider.getCurrent().getUuid()));
        }
        if (showContacts) {
            if (!showCases) {
                // Case lists need to be filled even when cases are hidden because they are
                // needed to retrieve the contacts
                fillCaseLists(FacadeProvider.getCaseFacade().getCasesForMap(region, district, disease, fromDate,
                        toDate, UserProvider.getCurrent().getUuid()));
            }
            showContactMarkers(FacadeProvider.getContactFacade().getContactsForMap(region, district, disease,
                    fromDate, toDate, UserProvider.getCurrent().getUuid(), mapAndFacilityCases));
        }
        if (showEvents) {
            showEventMarkers(dashboardDataProvider.getEvents());
        }

        // Re-create the map key layout to only show the keys for the selected layers
        legendDropdown.setContent(createLegend());
    }

    public List<CaseDataDto> getCasesForFacility(FacilityReferenceDto facility) {
        List<CaseDataDto> casesForFacility = new ArrayList<>();
        CaseFacade caseFacade = FacadeProvider.getCaseFacade();
        for (MapCaseDto mapCaseDto : casesByFacility.get(facility)) {
            casesForFacility.add(caseFacade.getCaseDataByUuid(mapCaseDto.getUuid()));
        }
        return casesForFacility;
    }

    public void setExpandListener(Consumer<Boolean> listener) {
        externalExpandListener = listener;
    }

    private HorizontalLayout createHeader() {
        HorizontalLayout mapHeaderLayout = new HorizontalLayout();
        mapHeaderLayout.setWidth(100, Unit.PERCENTAGE);
        mapHeaderLayout.setSpacing(true);
        CssStyles.style(mapHeaderLayout, CssStyles.VSPACE_4);

        Label mapLabel = new Label();
        if (dashboardDataProvider.getDashboardType() == DashboardType.SURVEILLANCE) {
            mapLabel.setValue(I18nProperties.getString(Strings.headingCaseStatusMap));
        } else {
            mapLabel.setValue(I18nProperties.getString(Strings.headingContactMap));
        }
        mapLabel.setSizeUndefined();
        CssStyles.style(mapLabel, CssStyles.H2, CssStyles.VSPACE_4, CssStyles.VSPACE_TOP_NONE);

        mapHeaderLayout.addComponent(mapLabel);
        mapHeaderLayout.setComponentAlignment(mapLabel, Alignment.BOTTOM_LEFT);
        mapHeaderLayout.setExpandRatio(mapLabel, 1);

        // "Expand" and "Collapse" buttons
        Button expandMapButton = new Button("", VaadinIcons.EXPAND);
        CssStyles.style(expandMapButton, CssStyles.BUTTON_SUBTLE);
        expandMapButton.addStyleName(CssStyles.VSPACE_NONE);
        Button collapseMapButton = new Button("", VaadinIcons.COMPRESS);
        CssStyles.style(collapseMapButton, CssStyles.BUTTON_SUBTLE);
        collapseMapButton.addStyleName(CssStyles.VSPACE_NONE);

        expandMapButton.addClickListener(e -> {
            externalExpandListener.accept(true);
            mapHeaderLayout.removeComponent(expandMapButton);
            mapHeaderLayout.addComponent(collapseMapButton);
            mapHeaderLayout.setComponentAlignment(collapseMapButton, Alignment.MIDDLE_RIGHT);
        });
        collapseMapButton.addClickListener(e -> {
            externalExpandListener.accept(false);
            mapHeaderLayout.removeComponent(collapseMapButton);
            mapHeaderLayout.addComponent(expandMapButton);
            mapHeaderLayout.setComponentAlignment(expandMapButton, Alignment.MIDDLE_RIGHT);
        });
        mapHeaderLayout.addComponent(expandMapButton);
        mapHeaderLayout.setComponentAlignment(expandMapButton, Alignment.MIDDLE_RIGHT);

        return mapHeaderLayout;
    }

    private HorizontalLayout createFooter() {
        HorizontalLayout mapFooterLayout = new HorizontalLayout();
        mapFooterLayout.setWidth(100, Unit.PERCENTAGE);
        mapFooterLayout.setSpacing(true);
        CssStyles.style(mapFooterLayout, CssStyles.VSPACE_4, CssStyles.VSPACE_TOP_3);

        // Map key dropdown button
        legendDropdown = new PopupButton(I18nProperties.getCaption(Captions.dashboardMapKey));
        CssStyles.style(legendDropdown, CssStyles.BUTTON_SUBTLE);
        legendDropdown.setContent(createLegend());
        mapFooterLayout.addComponent(legendDropdown);
        mapFooterLayout.setComponentAlignment(legendDropdown, Alignment.MIDDLE_RIGHT);
        mapFooterLayout.setExpandRatio(legendDropdown, 1);

        // Layers dropdown button
        PopupButton layersDropdown = new PopupButton(I18nProperties.getCaption(Captions.dashboardMapLayers));
        {
            CssStyles.style(layersDropdown, CssStyles.BUTTON_SUBTLE);

            VerticalLayout layersLayout = new VerticalLayout();
            layersLayout.setMargin(true);
            layersLayout.setSpacing(false);
            layersLayout.setSizeUndefined();
            layersDropdown.setContent(layersLayout);

            // Add check boxes and apply button
            {
                OptionGroup mapCaseDisplayModeSelect = new OptionGroup();
                mapCaseDisplayModeSelect.setWidth(100, Unit.PERCENTAGE);
                mapCaseDisplayModeSelect.addItems((Object[]) MapCaseDisplayMode.values());
                mapCaseDisplayModeSelect.setValue(mapCaseDisplayMode);
                mapCaseDisplayModeSelect.addValueChangeListener(event -> {
                    mapCaseDisplayMode = (MapCaseDisplayMode) event.getProperty().getValue();
                    refreshMap();
                });

                HorizontalLayout showCasesLayout = new HorizontalLayout();
                {
                    showCasesLayout.setMargin(false);
                    showCasesLayout.setSpacing(false);
                    CheckBox showCasesCheckBox = new CheckBox();
                    showCasesCheckBox.setCaption(I18nProperties.getCaption(Captions.dashboardShowCases));
                    showCasesCheckBox.setValue(showCases);
                    showCasesCheckBox.addValueChangeListener(e -> {
                        showCases = (boolean) e.getProperty().getValue();
                        mapCaseDisplayModeSelect.setEnabled(showCases);
                        mapCaseDisplayModeSelect.setValue(mapCaseDisplayMode);
                        refreshMap();
                    });
                    showCasesLayout.addComponent(showCasesCheckBox);

                    Label infoLabel = new Label(VaadinIcons.INFO_CIRCLE.getHtml(), ContentMode.HTML);
                    infoLabel.setDescription(I18nProperties.getString(Strings.infoCaseMap));
                    CssStyles.style(infoLabel, CssStyles.LABEL_MEDIUM, CssStyles.LABEL_SECONDARY,
                            CssStyles.HSPACE_LEFT_3);
                    infoLabel.setHeightUndefined();
                    showCasesLayout.addComponent(infoLabel);
                    showCasesLayout.setComponentAlignment(infoLabel, Alignment.TOP_CENTER);
                }
                layersLayout.addComponent(showCasesLayout);

                layersLayout.addComponent(mapCaseDisplayModeSelect);
                mapCaseDisplayModeSelect.setEnabled(showCases);

                CheckBox showConfirmedContactsCheckBox = new CheckBox();
                CheckBox showUnconfirmedContactsCheckBox = new CheckBox();

                CheckBox showContactsCheckBox = new CheckBox();
                showContactsCheckBox.setCaption(I18nProperties.getCaption(Captions.dashboardShowContacts));
                showContactsCheckBox.setValue(showContacts);
                showContactsCheckBox.addValueChangeListener(e -> {
                    showContacts = (boolean) e.getProperty().getValue();
                    showConfirmedContactsCheckBox.setEnabled(showContacts);
                    showConfirmedContactsCheckBox.setValue(true);
                    showUnconfirmedContactsCheckBox.setEnabled(showContacts);
                    showUnconfirmedContactsCheckBox.setValue(true);
                    refreshMap();
                });
                layersLayout.addComponent(showContactsCheckBox);

                showConfirmedContactsCheckBox
                        .setCaption(I18nProperties.getCaption(Captions.dashboardShowConfirmedContacts));
                showConfirmedContactsCheckBox.setValue(showConfirmedContacts);
                showConfirmedContactsCheckBox.addValueChangeListener(e -> {
                    showConfirmedContacts = (boolean) e.getProperty().getValue();
                    refreshMap();
                });
                layersLayout.addComponent(showConfirmedContactsCheckBox);

                CssStyles.style(showUnconfirmedContactsCheckBox, CssStyles.VSPACE_3);
                showUnconfirmedContactsCheckBox
                        .setCaption(I18nProperties.getCaption(Captions.dashboardShowUnconfirmedContacts));
                showUnconfirmedContactsCheckBox.setValue(showUnconfirmedContacts);
                showUnconfirmedContactsCheckBox.addValueChangeListener(e -> {
                    showUnconfirmedContacts = (boolean) e.getProperty().getValue();
                    refreshMap();
                });
                layersLayout.addComponent(showUnconfirmedContactsCheckBox);

                showConfirmedContactsCheckBox.setEnabled(showContacts);
                showUnconfirmedContactsCheckBox.setEnabled(showContacts);

                CheckBox showEventsCheckBox = new CheckBox();
                CssStyles.style(showEventsCheckBox, CssStyles.VSPACE_3);
                showEventsCheckBox.setCaption(I18nProperties.getCaption(Captions.dashboardShowEvents));
                showEventsCheckBox.setValue(showEvents);
                showEventsCheckBox.addValueChangeListener(e -> {
                    showEvents = (boolean) e.getProperty().getValue();
                    refreshMap();
                });
                layersLayout.addComponent(showEventsCheckBox);

                if (UserProvider.getCurrent().hasUserRole(UserRole.NATIONAL_USER)
                        || UserProvider.getCurrent().hasUserRole(UserRole.NATIONAL_CLINICIAN)
                        || UserProvider.getCurrent().hasUserRole(UserRole.NATIONAL_OBSERVER)) {
                    OptionGroup regionMapVisualizationSelect = new OptionGroup();
                    regionMapVisualizationSelect.setWidth(100, Unit.PERCENTAGE);
                    regionMapVisualizationSelect.addItems((Object[]) CaseMeasure.values());
                    regionMapVisualizationSelect.setValue(caseMeasure);
                    regionMapVisualizationSelect.addValueChangeListener(event -> {
                        caseMeasure = (CaseMeasure) event.getProperty().getValue();
                        refreshMap();
                    });

                    HorizontalLayout showRegionsLayout = new HorizontalLayout();
                    {
                        showRegionsLayout.setMargin(false);
                        showRegionsLayout.setSpacing(false);
                        CheckBox showRegionsCheckBox = new CheckBox();
                        showRegionsCheckBox.setCaption(I18nProperties.getCaption(Captions.dashboardShowRegions));
                        showRegionsCheckBox.setValue(showRegions);
                        showRegionsCheckBox.addValueChangeListener(e -> {
                            showRegions = (boolean) e.getProperty().getValue();
                            regionMapVisualizationSelect.setEnabled(showRegions);
                            regionMapVisualizationSelect.setValue(caseMeasure);
                            refreshMap();
                        });
                        showRegionsLayout.addComponent(showRegionsCheckBox);

                        Label infoLabel = new Label(VaadinIcons.INFO_CIRCLE.getHtml(), ContentMode.HTML);
                        infoLabel.setDescription(I18nProperties.getString(Strings.infoCaseIncidence));
                        CssStyles.style(infoLabel, CssStyles.LABEL_MEDIUM, CssStyles.LABEL_SECONDARY,
                                CssStyles.HSPACE_LEFT_3);
                        infoLabel.setHeightUndefined();
                        showRegionsLayout.addComponent(infoLabel);
                        showRegionsLayout.setComponentAlignment(infoLabel, Alignment.TOP_CENTER);
                    }
                    layersLayout.addComponent(showRegionsLayout);
                    layersLayout.addComponent(regionMapVisualizationSelect);
                    regionMapVisualizationSelect.setEnabled(showRegions);
                }
            }
        }
        mapFooterLayout.addComponent(layersDropdown);
        mapFooterLayout.setComponentAlignment(layersDropdown, Alignment.MIDDLE_RIGHT);

        return mapFooterLayout;
    }

    private VerticalLayout createLegend() {
        VerticalLayout legendLayout = new VerticalLayout();
        legendLayout.setSpacing(false);
        legendLayout.setMargin(true);
        legendLayout.setSizeUndefined();

        // Disable map key dropdown if no layers have been selected
        if (showCases || showContacts || showEvents || showRegions) {
            legendDropdown.setEnabled(true);
        } else {
            legendDropdown.setEnabled(false);
            return legendLayout;
        }

        // Health facilities

        // Cases
        if (showCases) {
            if (mapCaseDisplayMode == MapCaseDisplayMode.HEALTH_FACILITY
                    || mapCaseDisplayMode == MapCaseDisplayMode.HEALTH_FACILITY_OR_CASE_ADDRESS) {
                Label facilitiesKeyLabel = new Label(I18nProperties.getCaption(Captions.dashboardHealthFacilities));
                CssStyles.style(facilitiesKeyLabel, CssStyles.H4, CssStyles.VSPACE_4, CssStyles.VSPACE_TOP_NONE);
                legendLayout.addComponent(facilitiesKeyLabel);

                HorizontalLayout facilitiesKeyLayout = new HorizontalLayout();
                {
                    facilitiesKeyLayout.setSpacing(false);
                    facilitiesKeyLayout.setMargin(false);
                    HorizontalLayout legendEntry = buildMarkerLegendEntry(MarkerIcon.FACILITY_UNCLASSIFIED,
                            I18nProperties.getCaption(Captions.dashboardNotYetClassifiedOnly));
                    CssStyles.style(legendEntry, CssStyles.HSPACE_RIGHT_3);
                    facilitiesKeyLayout.addComponent(legendEntry);
                    legendEntry = buildMarkerLegendEntry(MarkerIcon.FACILITY_SUSPECT,
                            I18nProperties.getCaption(Captions.dashboardGt1SuspectCases));
                    CssStyles.style(legendEntry, CssStyles.HSPACE_RIGHT_3);
                    facilitiesKeyLayout.addComponent(legendEntry);
                    legendEntry = buildMarkerLegendEntry(MarkerIcon.FACILITY_PROBABLE,
                            I18nProperties.getCaption(Captions.dashboardGt1ProbableCases));
                    CssStyles.style(legendEntry, CssStyles.HSPACE_RIGHT_3);
                    facilitiesKeyLayout.addComponent(legendEntry);
                    legendEntry = buildMarkerLegendEntry(MarkerIcon.FACILITY_CONFIRMED,
                            I18nProperties.getCaption(Captions.dashboardGt1ConfirmedCases));
                    facilitiesKeyLayout.addComponent(legendEntry);
                }
                legendLayout.addComponent(facilitiesKeyLayout);
            }

            Label casesKeyLabel = new Label(I18nProperties.getString(Strings.entityCases));
            if (mapCaseDisplayMode == MapCaseDisplayMode.HEALTH_FACILITY
                    || mapCaseDisplayMode == MapCaseDisplayMode.HEALTH_FACILITY_OR_CASE_ADDRESS) {
                CssStyles.style(casesKeyLabel, CssStyles.H4, CssStyles.VSPACE_4, CssStyles.VSPACE_TOP_3);
            } else {
                CssStyles.style(casesKeyLabel, CssStyles.H4, CssStyles.VSPACE_4, CssStyles.VSPACE_TOP_NONE);
            }
            legendLayout.addComponent(casesKeyLabel);

            HorizontalLayout casesKeyLayout = new HorizontalLayout();
            {
                casesKeyLayout.setSpacing(false);
                casesKeyLayout.setMargin(false);
                HorizontalLayout legendEntry = buildMarkerLegendEntry(MarkerIcon.CASE_UNCLASSIFIED,
                        I18nProperties.getCaption(Captions.dashboardNotYetClassified));
                CssStyles.style(legendEntry, CssStyles.HSPACE_RIGHT_3);
                casesKeyLayout.addComponent(legendEntry);
                legendEntry = buildMarkerLegendEntry(MarkerIcon.CASE_SUSPECT,
                        I18nProperties.getCaption(Captions.dashboardSuspect));
                CssStyles.style(legendEntry, CssStyles.HSPACE_RIGHT_3);
                casesKeyLayout.addComponent(legendEntry);
                legendEntry = buildMarkerLegendEntry(MarkerIcon.CASE_PROBABLE,
                        I18nProperties.getCaption(Captions.dashboardProbable));
                CssStyles.style(legendEntry, CssStyles.HSPACE_RIGHT_3);
                casesKeyLayout.addComponent(legendEntry);
                legendEntry = buildMarkerLegendEntry(MarkerIcon.CASE_CONFIRMED,
                        I18nProperties.getCaption(Captions.dashboardConfirmed));
                casesKeyLayout.addComponent(legendEntry);
            }
            legendLayout.addComponent(casesKeyLayout);
        }

        // Contacts
        if (showContacts) {
            Label contactsKeyLabel = new Label(I18nProperties.getString(Strings.entityContacts));
            if (showCases) {
                CssStyles.style(contactsKeyLabel, CssStyles.H4, CssStyles.VSPACE_4, CssStyles.VSPACE_TOP_3);
            } else {
                CssStyles.style(contactsKeyLabel, CssStyles.H4, CssStyles.VSPACE_4, CssStyles.VSPACE_TOP_NONE);
            }
            legendLayout.addComponent(contactsKeyLabel);

            HorizontalLayout contactsKeyLayout = new HorizontalLayout();
            {
                contactsKeyLayout.setSpacing(false);
                contactsKeyLayout.setMargin(false);
                HorizontalLayout legendEntry = buildMarkerLegendEntry(MarkerIcon.CONTACT_OK,
                        I18nProperties.getCaption(Captions.dashboardLastVisitLt24));
                CssStyles.style(legendEntry, CssStyles.HSPACE_RIGHT_3);
                contactsKeyLayout.addComponent(legendEntry);
                legendEntry = buildMarkerLegendEntry(MarkerIcon.CONTACT_OVERDUE,
                        I18nProperties.getCaption(Captions.dashboardLastVisitLt48));
                CssStyles.style(legendEntry, CssStyles.HSPACE_RIGHT_3);
                contactsKeyLayout.addComponent(legendEntry);
                legendEntry = buildMarkerLegendEntry(MarkerIcon.CONTACT_LONG_OVERDUE,
                        I18nProperties.getCaption(Captions.dashboardLastVisitGt48));
                contactsKeyLayout.addComponent(legendEntry);
            }
            legendLayout.addComponent(contactsKeyLayout);
        }

        // Events
        if (showEvents) {
            Label eventsKeyLabel = new Label(I18nProperties.getString(Strings.entityEvents));
            if (showCases || showContacts) {
                CssStyles.style(eventsKeyLabel, CssStyles.H4, CssStyles.VSPACE_4, CssStyles.VSPACE_TOP_3);
            } else {
                CssStyles.style(eventsKeyLabel, CssStyles.H4, CssStyles.VSPACE_4, CssStyles.VSPACE_TOP_NONE);
            }
            legendLayout.addComponent(eventsKeyLabel);

            HorizontalLayout eventsKeyLayout = new HorizontalLayout();
            {
                eventsKeyLayout.setSpacing(false);
                eventsKeyLayout.setMargin(false);
                HorizontalLayout legendEntry = buildMarkerLegendEntry(MarkerIcon.EVENT_RUMOR,
                        EventStatus.POSSIBLE.toString());
                CssStyles.style(legendEntry, CssStyles.HSPACE_RIGHT_3);
                eventsKeyLayout.addComponent(legendEntry);
                legendEntry = buildMarkerLegendEntry(MarkerIcon.EVENT_OUTBREAK, EventStatus.CONFIRMED.toString());
                eventsKeyLayout.addComponent(legendEntry);
            }
            legendLayout.addComponent(eventsKeyLayout);
        }

        // Districts
        if (showRegions && districtValuesLowerQuartile != null && districtValuesMedian != null
                && districtValuesUpperQuartile != null) {
            Label districtsKeyLabel = new Label(I18nProperties.getString(Strings.entityDistricts));
            if (showCases || showContacts || showEvents) {
                CssStyles.style(districtsKeyLabel, CssStyles.H4, CssStyles.VSPACE_4, CssStyles.VSPACE_TOP_3);
            } else {
                CssStyles.style(districtsKeyLabel, CssStyles.H4, CssStyles.VSPACE_4, CssStyles.VSPACE_TOP_NONE);
            }
            legendLayout.addComponent(districtsKeyLabel);
            legendLayout.addComponent(buildRegionLegend(false, caseMeasure, emptyPopulationDistrictPresent,
                    districtValuesLowerQuartile, districtValuesMedian, districtValuesUpperQuartile));

            Label descLabel = new Label(I18nProperties.getString(Strings.infoDashboardIncidence));
            CssStyles.style(descLabel, CssStyles.LABEL_SMALL);
            legendLayout.addComponent(descLabel);
        }

        return legendLayout;
    }

    public static HorizontalLayout buildMarkerLegendEntry(MarkerIcon icon, String labelCaption) {
        return buildLegendEntry(new Label(icon.getHtmlElement("16px"), ContentMode.HTML), labelCaption);
    }

    public static HorizontalLayout buildMapIconLegendEntry(String iconName, String labelCaption) {
        Image icon = new Image(null, new ExternalResource("VAADIN/map/marker/" + iconName + ".png"));
        icon.setWidth(12.375f, Unit.PIXELS);
        icon.setHeight(16.875f, Unit.PIXELS);
        return buildLegendEntry(icon, labelCaption);
    }

    private static HorizontalLayout buildLegendEntry(AbstractComponent icon, String labelCaption) {
        HorizontalLayout entry = new HorizontalLayout();
        entry.setSpacing(false);
        entry.setSizeUndefined();
        CssStyles.style(icon, CssStyles.HSPACE_RIGHT_4);
        entry.addComponent(icon);
        Label label = new Label(labelCaption);
        label.setSizeUndefined();
        label.addStyleName(ValoTheme.LABEL_SMALL);
        entry.addComponent(label);
        return entry;
    }

    public static AbstractOrderedLayout buildRegionLegend(boolean vertical, CaseMeasure caseMeasure,
            boolean emptyPopulationDistrictPresent, BigDecimal districtShapesLowerQuartile,
            BigDecimal districtShapesMedian, BigDecimal districtShapesUpperQuartile) {
        AbstractOrderedLayout regionLegendLayout = vertical ? new VerticalLayout() : new HorizontalLayout();
        regionLegendLayout.setSpacing(true);
        CssStyles.style(regionLegendLayout, CssStyles.LAYOUT_MINIMAL);
        regionLegendLayout.setSizeUndefined();

        HorizontalLayout legendEntry;
        switch (caseMeasure) {
        case CASE_COUNT:
            legendEntry = buildMapIconLegendEntry("lowest-region-small",
                    districtShapesLowerQuartile.compareTo(BigDecimal.ONE) > 0
                            ? "1 - " + districtShapesLowerQuartile + " "
                                    + I18nProperties.getString(Strings.entityCases)
                            : "1 " + I18nProperties.getString(Strings.entityCase));
            break;
        case CASE_INCIDENCE:
            legendEntry = buildMapIconLegendEntry("lowest-region-small",
                    "<= " + DataHelper.getTruncatedBigDecimal(districtShapesLowerQuartile) + " "
                            + I18nProperties.getString(Strings.entityCases) + " / "
                            + DistrictDto.CASE_INCIDENCE_DIVISOR);
            break;
        default:
            throw new IllegalArgumentException(caseMeasure.toString());
        }
        regionLegendLayout.addComponent(legendEntry);

        if (districtShapesLowerQuartile.compareTo(districtShapesMedian) < 0) {
            switch (caseMeasure) {
            case CASE_COUNT:
                legendEntry = buildMapIconLegendEntry("low-region-small",
                        districtShapesMedian.compareTo(districtShapesLowerQuartile.add(BigDecimal.ONE)) > 0
                                ? districtShapesLowerQuartile.add(BigDecimal.ONE) + " - " + districtShapesMedian
                                        + " " + I18nProperties.getString(Strings.entityCases)
                                : districtShapesMedian + " " + I18nProperties.getString(Strings.entityCases));
                break;
            case CASE_INCIDENCE:
                legendEntry = buildMapIconLegendEntry("low-region-small",
                        DataHelper
                                .getTruncatedBigDecimal(districtShapesLowerQuartile.add(new BigDecimal(0.1))
                                        .setScale(1, RoundingMode.HALF_UP))
                                + " - " + DataHelper.getTruncatedBigDecimal(districtShapesMedian) + " "
                                + I18nProperties.getString(Strings.entityCases) + " / "
                                + DistrictDto.CASE_INCIDENCE_DIVISOR);
                break;
            default:
                throw new IllegalArgumentException(caseMeasure.toString());
            }

            regionLegendLayout.addComponent(legendEntry);
        }

        if (districtShapesMedian.compareTo(districtShapesUpperQuartile) < 0) {
            switch (caseMeasure) {
            case CASE_COUNT:
                legendEntry = buildMapIconLegendEntry("high-region-small",
                        districtShapesUpperQuartile.compareTo(districtShapesMedian.add(BigDecimal.ONE)) > 0
                                ? districtShapesMedian.add(BigDecimal.ONE) + " - " + districtShapesUpperQuartile
                                        + " " + I18nProperties.getString(Strings.entityCases)
                                : districtShapesUpperQuartile + " "
                                        + I18nProperties.getString(Strings.entityCases));
                break;
            case CASE_INCIDENCE:
                legendEntry = buildMapIconLegendEntry("high-region-small",
                        DataHelper.getTruncatedBigDecimal(
                                districtShapesMedian.add(new BigDecimal(0.1)).setScale(1, RoundingMode.HALF_UP))
                                + " - " + DataHelper.getTruncatedBigDecimal(districtShapesUpperQuartile) + " "
                                + I18nProperties.getString(Strings.entityCases) + " / "
                                + DistrictDto.CASE_INCIDENCE_DIVISOR);
                break;
            default:
                throw new IllegalArgumentException(caseMeasure.toString());
            }

            regionLegendLayout.addComponent(legendEntry);
        }

        switch (caseMeasure) {
        case CASE_COUNT:
            legendEntry = buildMapIconLegendEntry("highest-region-small",
                    "> " + districtShapesUpperQuartile + " " + I18nProperties.getString(Strings.entityCases));
            break;
        case CASE_INCIDENCE:
            legendEntry = buildMapIconLegendEntry("highest-region-small",
                    "> " + DataHelper.getTruncatedBigDecimal(districtShapesUpperQuartile) + " "
                            + I18nProperties.getString(Strings.entityCases) + " / "
                            + DistrictDto.CASE_INCIDENCE_DIVISOR);
            break;
        default:
            throw new IllegalArgumentException(caseMeasure.toString());
        }
        regionLegendLayout.addComponent(legendEntry);

        if (caseMeasure == CaseMeasure.CASE_INCIDENCE && emptyPopulationDistrictPresent) {
            legendEntry = buildMapIconLegendEntry("no-population-region-small",
                    I18nProperties.getCaption(Captions.dashboardNoPopulationData));
            regionLegendLayout.addComponent(legendEntry);
        }

        return regionLegendLayout;
    }

    private void clearRegionShapes() {

        map.removeGroup(REGIONS_GROUP_ID);
        map.removeGroup(DISTRICTS_GROUP_ID);
        polygonRegions.clear();
        polygonDistricts.clear();

        emptyPopulationDistrictPresent = false;
        map.setTileLayerOpacity(1);
    }

    private void showRegionsShapes(CaseMeasure caseMeasure, Date fromDate, Date toDate, Disease disease) {

        clearRegionShapes();
        map.setTileLayerOpacity(0.5f);

        List<RegionReferenceDto> regions = FacadeProvider.getRegionFacade().getAllAsReference();
        List<LeafletPolygon> regionPolygons = new ArrayList<LeafletPolygon>();

        // draw outlines of all regions
        for (RegionReferenceDto region : regions) {

            GeoLatLon[][] regionShape = FacadeProvider.getGeoShapeProvider().getRegionShape(region);
            if (regionShape == null) {
                continue;
            }

            for (GeoLatLon[] regionShapePart : regionShape) {
                LeafletPolygon polygon = new LeafletPolygon();
                polygon.setCaption(region.getCaption());
                // fillOpacity is used, so we can still hover the region
                polygon.setOptions("{\"weight\": 1, \"color\": '#444', \"fillOpacity\": 0.02}");
                polygon.setLatLons(regionShapePart);
                regionPolygons.add(polygon);
                polygonRegions.add(region);
            }
        }

        map.addPolygonGroup(REGIONS_GROUP_ID, regionPolygons);

        List<Pair<DistrictDto, BigDecimal>> measurePerDistrict = FacadeProvider.getCaseFacade()
                .getCaseMeasurePerDistrict(fromDate, toDate, disease, caseMeasure);
        if (caseMeasure == CaseMeasure.CASE_COUNT) {
            districtValuesLowerQuartile = measurePerDistrict.size() > 0
                    ? measurePerDistrict.get((int) (measurePerDistrict.size() * 0.25)).getElement1()
                    : null;
            districtValuesMedian = measurePerDistrict.size() > 0
                    ? measurePerDistrict.get((int) (measurePerDistrict.size() * 0.5)).getElement1()
                    : null;
            districtValuesUpperQuartile = measurePerDistrict.size() > 0
                    ? measurePerDistrict.get((int) (measurePerDistrict.size() * 0.75)).getElement1()
                    : null;
        } else {
            // For case incidence, districts without or with a population <= 0 should not be
            // used for the calculation of the quartiles because they will falsify the
            // result
            List<Pair<DistrictDto, BigDecimal>> measurePerDistrictWithoutMissingPopulations = new ArrayList<>();
            measurePerDistrictWithoutMissingPopulations.addAll(measurePerDistrict);
            measurePerDistrictWithoutMissingPopulations
                    .removeIf(d -> d.getElement0().getPopulation() == null || d.getElement0().getPopulation() <= 0);
            districtValuesLowerQuartile = measurePerDistrictWithoutMissingPopulations.size() > 0
                    ? measurePerDistrictWithoutMissingPopulations
                            .get((int) (measurePerDistrictWithoutMissingPopulations.size() * 0.25)).getElement1()
                    : null;
            districtValuesMedian = measurePerDistrictWithoutMissingPopulations.size() > 0
                    ? measurePerDistrictWithoutMissingPopulations
                            .get((int) (measurePerDistrictWithoutMissingPopulations.size() * 0.5)).getElement1()
                    : null;
            districtValuesUpperQuartile = measurePerDistrictWithoutMissingPopulations.size() > 0
                    ? measurePerDistrictWithoutMissingPopulations
                            .get((int) (measurePerDistrictWithoutMissingPopulations.size() * 0.75)).getElement1()
                    : null;
        }

        List<LeafletPolygon> districtPolygons = new ArrayList<LeafletPolygon>();

        // Draw relevant district fills
        for (Pair<DistrictDto, BigDecimal> districtMeasure : measurePerDistrict) {

            DistrictDto district = districtMeasure.getElement0();
            DistrictReferenceDto districtRef = district.toReference();
            BigDecimal districtValue = districtMeasure.getElement1();
            GeoLatLon[][] districtShape = FacadeProvider.getGeoShapeProvider().getDistrictShape(districtRef);
            if (districtShape == null) {
                continue;
            }

            String fillColor;
            if (districtValue.compareTo(BigDecimal.ZERO) == 0) {
                fillColor = "#000";
            } else if (districtValue.compareTo(districtValuesLowerQuartile) < 0) {
                fillColor = "#FEDD6C";
            } else if (districtValue.compareTo(districtValuesMedian) < 0) {
                fillColor = "#FDBF44";
            } else if (districtValue.compareTo(districtValuesUpperQuartile) < 0) {
                fillColor = "#F47B20";
            } else {
                fillColor = "#ED1B24";
            }

            if (caseMeasure == CaseMeasure.CASE_INCIDENCE) {
                if (district.getPopulation() == null || district.getPopulation() <= 0) {
                    // grey when region has no population data
                    emptyPopulationDistrictPresent = true;
                    fillColor = "#999";
                }
            }

            for (GeoLatLon[] districtShapePart : districtShape) {
                LeafletPolygon polygon = new LeafletPolygon();
                polygon.setCaption(district.getName() + "<br>" + districtValue);
                polygon.setOptions("{\"stroke\": false, \"color\": '" + fillColor + "', \"fillOpacity\": 0.8}");
                polygon.setLatLons(districtShapePart);
                districtPolygons.add(polygon);
                polygonDistricts.add(districtRef);
            }
        }

        map.addPolygonGroup(DISTRICTS_GROUP_ID, districtPolygons);
    }

    private void clearCaseMarkers() {

        map.removeGroup(CASES_GROUP_ID);
        markerCaseFacilities.clear();
        casesByFacility.clear();
        mapCaseDtos.clear();
        mapAndFacilityCases.clear();
    }

    private void showCaseMarkers(List<MapCaseDto> cases) {

        clearCaseMarkers();

        fillCaseLists(cases);

        List<LeafletMarker> caseMarkers = new ArrayList<LeafletMarker>();

        for (FacilityReferenceDto facilityReference : casesByFacility.keySet()) {

            List<MapCaseDto> casesList = casesByFacility.get(facilityReference);
            // colorize the icon by the "strongest" classification type (order as in enum)
            // and set its size depending
            // on the number of cases
            int numberOfCases = casesList.size();
            Set<CaseClassification> classificationSet = new HashSet<>();
            for (MapCaseDto caze : casesList) {
                classificationSet.add(caze.getCaseClassification());
            }

            MarkerIcon icon;
            if (classificationSet.contains(CaseClassification.CONFIRMED)) {
                icon = MarkerIcon.FACILITY_CONFIRMED;
            } else if (classificationSet.contains(CaseClassification.PROBABLE)) {
                icon = MarkerIcon.FACILITY_PROBABLE;
            } else if (classificationSet.contains(CaseClassification.SUSPECT)) {
                icon = MarkerIcon.FACILITY_SUSPECT;
            } else {
                icon = MarkerIcon.FACILITY_UNCLASSIFIED;
            }

            // create and place the marker
            markerCaseFacilities.add(facilityReference);

            MapCaseDto firstCase = casesList.get(0);
            LeafletMarker leafletMarker = new LeafletMarker();
            leafletMarker.setLatLon(firstCase.getHealthFacilityLat(), firstCase.getHealthFacilityLon());
            leafletMarker.setIcon(icon);
            leafletMarker.setMarkerCount(numberOfCases);
            caseMarkers.add(leafletMarker);
        }

        for (MapCaseDto caze : mapCaseDtos) {
            LeafletMarker marker = new LeafletMarker();
            if (caze.getCaseClassification() == CaseClassification.CONFIRMED) {
                marker.setIcon(MarkerIcon.CASE_CONFIRMED);
            } else if (caze.getCaseClassification() == CaseClassification.PROBABLE) {
                marker.setIcon(MarkerIcon.CASE_PROBABLE);
            } else if (caze.getCaseClassification() == CaseClassification.SUSPECT) {
                marker.setIcon(MarkerIcon.CASE_SUSPECT);
            } else {
                marker.setIcon(MarkerIcon.CASE_UNCLASSIFIED);
            }

            if (caze.getAddressLat() != null && caze.getAddressLon() != null) {
                marker.setLatLon(caze.getAddressLat(), caze.getAddressLon());
            } else {
                marker.setLatLon(caze.getReportLat(), caze.getReportLon());
            }

            caseMarkers.add(marker);
        }

        map.addMarkerGroup("cases", caseMarkers);
    }

    private void fillCaseLists(List<MapCaseDto> cases) {
        for (MapCaseDto caze : cases) {
            CaseClassification classification = caze.getCaseClassification();
            if (classification == null || classification == CaseClassification.NO_CASE)
                continue;
            boolean hasCaseGps = (caze.getAddressLat() != null && caze.getAddressLon() != null)
                    || (caze.getReportLat() != null || caze.getReportLon() != null);
            boolean hasFacilityGps = caze.getHealthFacilityLat() != null && caze.getHealthFacilityLon() != null;
            if (!hasCaseGps && !hasFacilityGps) {
                continue; // no gps at all
            }

            if (mapCaseDisplayMode == MapCaseDisplayMode.CASE_ADDRESS) {
                if (!hasCaseGps) {
                    continue;
                }
                mapCaseDtos.add(caze);
            } else {
                if (caze.getHealthFacilityUuid().equals(FacilityDto.NONE_FACILITY_UUID)
                        || caze.getHealthFacilityUuid().equals(FacilityDto.OTHER_FACILITY_UUID)
                        || !hasFacilityGps) {
                    if (mapCaseDisplayMode == MapCaseDisplayMode.HEALTH_FACILITY_OR_CASE_ADDRESS) {
                        if (!hasCaseGps) {
                            continue;
                        }
                        mapCaseDtos.add(caze);
                    } else {
                        continue;
                    }
                } else {
                    if (!hasFacilityGps) {
                        continue;
                    }
                    FacilityReferenceDto facility = new FacilityReferenceDto();
                    facility.setUuid(caze.getHealthFacilityUuid());
                    if (casesByFacility.get(facility) == null) {
                        casesByFacility.put(facility, new ArrayList<MapCaseDto>());
                    }
                    casesByFacility.get(facility).add(caze);
                }
            }

            mapAndFacilityCases.add(caze);
        }
    }

    private void clearContactMarkers() {
        map.removeGroup(CONTACTS_GROUP_ID);
        markerContacts.clear();
        mapContactDtos.clear();
    }

    private void showContactMarkers(List<MapContactDto> contacts) {

        clearContactMarkers();

        List<LeafletMarker> contactMarkers = new ArrayList<LeafletMarker>();

        for (MapContactDto contact : contacts) {

            // Don't show a marker for contacts that don't have geo coordinates
            if (contact.getAddressLat() == null || contact.getAddressLon() == null) {
                if (contact.getReportLat() == null || contact.getReportLon() == null) {
                    continue;
                }
            }

            // Don't show a marker for contacts that are filtered out
            if (!showUnconfirmedContacts
                    && contact.getContactClassification() == ContactClassification.UNCONFIRMED) {
                continue;
            }
            if (!showConfirmedContacts && contact.getContactClassification() != ContactClassification.UNCONFIRMED) {
                continue;
            }

            MarkerIcon icon;
            Date lastVisitDateTime = contact.getLastVisitDateTime();
            long currentTime = new Date().getTime();
            if (lastVisitDateTime != null) {
                // 1000 ms = 1 second; 3600 seconds = 1 hour
                if (currentTime - lastVisitDateTime.getTime() >= 1000 * 3600 * 48) {
                    icon = MarkerIcon.CONTACT_LONG_OVERDUE;
                } else if (currentTime - lastVisitDateTime.getTime() >= 1000 * 3600 * 24) {
                    icon = MarkerIcon.CONTACT_OVERDUE;
                } else {
                    icon = MarkerIcon.CONTACT_OK;
                }
            } else {
                icon = MarkerIcon.CONTACT_LONG_OVERDUE;
            }

            LeafletMarker marker = new LeafletMarker();
            marker.setIcon(icon);
            if (contact.getAddressLat() != null && contact.getAddressLon() != null) {
                marker.setLatLon(contact.getAddressLat(), contact.getAddressLon());
            } else {
                marker.setLatLon(contact.getReportLat(), contact.getReportLon());
            }
            markerContacts.add(contact);
            contactMarkers.add(marker);
        }
        map.addMarkerGroup(CONTACTS_GROUP_ID, contactMarkers);
    }

    private void clearEventMarkers() {
        map.removeGroup(EVENTS_GROUP_ID);
        markerEvents.clear();
    }

    private void showEventMarkers(List<DashboardEventDto> events) {

        clearEventMarkers();

        List<LeafletMarker> eventMarkers = new ArrayList<LeafletMarker>();

        for (DashboardEventDto event : events) {
            MarkerIcon icon;
            switch (event.getEventStatus()) {
            case CONFIRMED:
                icon = MarkerIcon.EVENT_OUTBREAK;
                break;
            case POSSIBLE:
                icon = MarkerIcon.EVENT_RUMOR;
                break;
            default:
                continue;
            }

            LeafletMarker marker = new LeafletMarker();
            if (event.getReportLat() != null && event.getReportLon() != null) {
                marker.setLatLon(event.getReportLat(), event.getReportLon());
            } else if (event.getDistrict() != null) {
                GeoLatLon districtCenter = FacadeProvider.getGeoShapeProvider()
                        .getCenterOfDistrict(event.getDistrict());
                marker.setLatLon(districtCenter.getLat(), districtCenter.getLon());
            } else {
                continue;
            }

            marker.setIcon(icon);
            markerEvents.add(event);
            eventMarkers.add(marker);
        }

        map.addMarkerGroup(EVENTS_GROUP_ID, eventMarkers);
    }

    private void onMarkerClicked(String groupId, int markerIndex) {

        switch (groupId) {
        case CASES_GROUP_ID:

            if (markerIndex < markerCaseFacilities.size()) {
                FacilityReferenceDto facility = markerCaseFacilities.get(markerIndex);
                VerticalLayout layout = new VerticalLayout();
                Window window = VaadinUiUtil.showPopupWindow(layout);
                CasePopupGrid caseGrid = new CasePopupGrid(window, facility, DashboardMapComponent.this);
                caseGrid.setHeightMode(HeightMode.ROW);
                layout.addComponent(caseGrid);
                layout.setMargin(true);
                FacilityDto facilityDto = FacadeProvider.getFacilityFacade().getByUuid(facility.getUuid());
                window.setCaption(
                        I18nProperties.getCaption(Captions.dashboardCasesIn) + " " + facilityDto.toString());
            } else {
                markerIndex -= markerCaseFacilities.size();
                MapCaseDto caze = mapCaseDtos.get(markerIndex);
                ControllerProvider.getCaseController().navigateToCase(caze.getUuid(), true);
            }
            break;
        case CONTACTS_GROUP_ID: {
            MapContactDto contact = markerContacts.get(markerIndex);
            ControllerProvider.getContactController().navigateToData(contact.getUuid(), true);
        }
            break;
        case EVENTS_GROUP_ID: {
            DashboardEventDto event = markerEvents.get(markerIndex);
            ControllerProvider.getEventController().navigateToData(event.getUuid(), true);
        }
            break;
        }
    }
}