org.opennms.features.vaadin.nodemaps.internal.gwt.client.ui.NodeMapWidget.java Source code

Java tutorial

Introduction

Here is the source code for org.opennms.features.vaadin.nodemaps.internal.gwt.client.ui.NodeMapWidget.java

Source

/*******************************************************************************
 * This file is part of OpenNMS(R).
 *
 * Copyright (C) 2013-2014 The OpenNMS Group, Inc.
 * OpenNMS(R) is Copyright (C) 1999-2014 The OpenNMS Group, Inc.
 *
 * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
 *
 * OpenNMS(R) is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * OpenNMS(R) 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 OpenNMS(R).  If not, see:
 *      http://www.gnu.org/licenses/
 *
 * For more information contact:
 *     OpenNMS(R) Licensing <license@opennms.org>
 *     http://www.opennms.org/
 *     http://www.opennms.com/
 *******************************************************************************/

package org.opennms.features.vaadin.nodemaps.internal.gwt.client.ui;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.discotools.gwt.leaflet.client.Options;
import org.discotools.gwt.leaflet.client.controls.zoom.Zoom;
import org.discotools.gwt.leaflet.client.controls.zoom.ZoomOptions;
import org.discotools.gwt.leaflet.client.layers.ILayer;
import org.discotools.gwt.leaflet.client.layers.raster.TileLayer;
import org.discotools.gwt.leaflet.client.map.MapOptions;
import org.discotools.gwt.leaflet.client.types.LatLng;
import org.discotools.gwt.leaflet.client.types.LatLngBounds;
import org.opennms.features.geocoder.Coordinates;
import org.opennms.features.vaadin.nodemaps.internal.gwt.client.AlarmSeverity;
import org.opennms.features.vaadin.nodemaps.internal.gwt.client.ComponentTracker;
import org.opennms.features.vaadin.nodemaps.internal.gwt.client.JSNodeMarker;
import org.opennms.features.vaadin.nodemaps.internal.gwt.client.Map;
import org.opennms.features.vaadin.nodemaps.internal.gwt.client.NodeMapState;
import org.opennms.features.vaadin.nodemaps.internal.gwt.client.NodeMarker;
import org.opennms.features.vaadin.nodemaps.internal.gwt.client.OpenNMSEventManager;
import org.opennms.features.vaadin.nodemaps.internal.gwt.client.Option;
import org.opennms.features.vaadin.nodemaps.internal.gwt.client.event.ApplicationInitializedEvent;
import org.opennms.features.vaadin.nodemaps.internal.gwt.client.event.ApplicationInitializedEventHandler;
import org.opennms.features.vaadin.nodemaps.internal.gwt.client.event.FilteredMarkersUpdatedEvent;
import org.opennms.features.vaadin.nodemaps.internal.gwt.client.event.FilteredMarkersUpdatedEventHandler;
import org.opennms.features.vaadin.nodemaps.internal.gwt.client.event.IconCreateCallback;
import org.opennms.features.vaadin.nodemaps.internal.gwt.client.event.NodeMarkerClusterCallback;
import org.opennms.features.vaadin.nodemaps.internal.gwt.client.ui.controls.alarm.AlarmControl;
import org.opennms.features.vaadin.nodemaps.internal.gwt.client.ui.controls.search.SearchControl;
import org.opennms.features.vaadin.nodemaps.internal.gwt.client.ui.controls.search.SearchStateManager;

import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.RepeatingCommand;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.DivElement;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.logical.shared.AttachEvent;
import com.google.gwt.event.logical.shared.AttachEvent.Handler;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.SimplePanel;

public class NodeMapWidget extends AbsolutePanel
        implements MarkerProvider, FilteredMarkersUpdatedEventHandler, ApplicationInitializedEventHandler {
    private final Logger LOG = Logger.getLogger(getClass().getName());

    private final DivElement m_div;
    private Map m_map;
    private ILayer m_layer;
    private MarkerContainer m_markerContainer;
    private MarkerClusterGroup m_markerClusterGroup;
    private MarkerClusterGroup[] m_stateClusterGroups;

    private boolean m_firstUpdate = true;
    private SearchControl m_searchControl;
    private AlarmControl m_alarmControl;
    private MarkerFilterImpl m_filter;
    private NodeIdSelectionRpc m_clientToServerRpc;
    private boolean m_groupByState;
    private int m_maxClusterRadius;

    private OpenNMSEventManager m_eventManager;
    private ComponentTracker m_componentTracker;

    private SimplePanel m_mapPanel = new SimplePanel();

    private boolean initialized = false;

    public NodeMapWidget() {
        m_eventManager = new OpenNMSEventManager();
        m_eventManager.addHandler(FilteredMarkersUpdatedEvent.TYPE, this);
        m_eventManager.addHandler(ApplicationInitializedEvent.TYPE, this);

        m_componentTracker = new ComponentTracker(m_eventManager);

        m_componentTracker.track(MarkerContainer.class);
        m_componentTracker.track(MarkerFilterImpl.class);
        m_componentTracker.track(AlarmControl.class);
        m_componentTracker.track(SearchControl.class);
        m_componentTracker.track(SearchStateManager.class);

        m_mapPanel.setWidth("100%");
        m_mapPanel.setHeight("100%");
        final Style mapStyle = m_mapPanel.getElement().getStyle();
        mapStyle.setPosition(Position.ABSOLUTE);
        mapStyle.setTop(0, Unit.PX);
        mapStyle.setLeft(0, Unit.PX);

        this.setWidth("100%");
        this.setHeight("100%");

        this.add(m_mapPanel);
        m_div = m_mapPanel.getElement().cast();
        m_div.setId("gwt-map");

        setStyleName("v-openlayers");
        LOG.info("NodeMapWidget(): div ID = " + m_div.getId());

        // addPassThroughHandlers();

        addAttachHandler(new Handler() {
            @Override
            public void onAttachOrDetach(final AttachEvent event) {
                if (event.isAttached()) {
                    LOG.info("NodeMapWidget.onAttach()");

                    m_filter = new MarkerFilterImpl("", AlarmSeverity.NORMAL, m_eventManager, m_componentTracker);
                    m_markerContainer = new MarkerContainer(m_filter, m_eventManager, m_componentTracker);
                } else {
                    LOG.info("NodeMapwidget.onDetach()");
                    if (m_markerContainer != null)
                        m_markerContainer.onUnload();
                    if (m_filter != null)
                        m_filter.onUnload();
                    destroyMap();
                }
            }
        });
    }

    public void initialize(final NodeMapState state) {
        LOG.info("NodeMapWidget.initializeMap()");

        // Defer actual initialization until after the browser
        // event loop because otherwise Vaadin isn't done setting
        // up yet.
        Scheduler.get().scheduleDeferred(new ScheduledCommand() {
            @Override
            public void execute() {
                doInitialization(state);
            }
        });
    }

    private void doInitialization(final NodeMapState state) {
        // layers
        createMap(m_div.getId());
        addTileLayer(state.tileServerUrl, state.tileLayerOptions);
        addMarkerLayer();

        // overlay controls
        addSearchControl();
        addAlarmControl();
        addZoomControl();

        m_filter.onLoad();
        m_markerContainer.onLoad();
        m_searchControl.focusInput();
        m_componentTracker.ready(getClass());
        initialized = true;
        LOG.info("NodeMapWidget.initializeMap(): finished");
    }

    public boolean isInitialized() {
        return initialized;
    }

    private void createMap(final String divId) {
        final MapOptions options = new MapOptions();
        options.setCenter(new LatLng(0, 0));
        options.setProperty("zoomControl", false);
        options.setZoom(1);
        options.setMaxZoom(15);
        m_map = new Map(divId, options);
    }

    private void addTileLayer(String tileUrl, List<Option> optionsList) {
        LOG.info("NodeMapWidget.setTileLayer(String, Option...) start");
        LOG.info("NodeMapWidget.setTileLayer tileServerUrl: " + tileUrl + "," + optionsList);
        final Options tileOptions = new Options();
        for (Option eachOption : optionsList) {
            tileOptions.setProperty(eachOption.getKey(), eachOption.getValue());
        }
        m_layer = new TileLayer(tileUrl, tileOptions);
        m_map.addLayer(m_layer, true);
        LOG.info("NodeMapWidget.setTileLayer(String, Option...) end");
    }

    private void addMarkerLayer() {
        LOG.info("NodeMapWidget.addMarkerLayer()");

        if (m_markerClusterGroup != null) {
            m_map.removeLayer(m_markerClusterGroup);
        }

        final Options markerClusterOptions = new Options();
        markerClusterOptions.setProperty("zoomToBoundsOnClick", false);
        markerClusterOptions.setProperty("iconCreateFunction", new IconCreateCallback());
        // markerClusterOptions.setProperty("disableClusteringAtZoom", 13);
        m_markerClusterGroup = new MarkerClusterGroup(markerClusterOptions);

        final NodeMarkerClusterCallback callback = new NodeMarkerClusterCallback();
        m_markerClusterGroup.on("clusterclick", callback);
        m_markerClusterGroup.on("clustertouchend", callback);
        m_map.addLayer(m_markerClusterGroup);

        m_stateClusterGroups = new MarkerClusterGroup[52];

        LOG.info("Creating marker cluster with maximum cluster radius " + m_maxClusterRadius);

        final Options[] stateClusterOptions = new Options[m_stateClusterGroups.length];
        for (int i = 0; i < m_stateClusterGroups.length; i++) {
            //stateClusterOptions[i] = new Options();
            stateClusterOptions[i] = markerClusterOptions;
            stateClusterOptions[i].setProperty("maxClusterRadius",
                    m_maxClusterRadius > 0 ? m_maxClusterRadius : 350);
            stateClusterOptions[i].setProperty("inUs", true);
            stateClusterOptions[i].setProperty("stateID", i);
            stateClusterOptions[i].setProperty("stateData", StatesData.getPolygonInfo(i, StatesData.getInstance()));
            //stateClusterOptions[i].setProperty("zoomToBoundsOnClick", false);
            //stateClusterOptions[i].setProperty("iconCreateFunction", new IconCreateCallback());
            m_stateClusterGroups[i] = new MarkerClusterGroup(stateClusterOptions[i]);
            m_stateClusterGroups[i].on("clusterclick", callback);
            m_stateClusterGroups[i].on("clustertouchend", callback);
            m_map.addLayer(m_stateClusterGroups[i]);
        }
    }

    private void addSearchControl() {
        LOG.info("NodeMapWidget.addSearchControl()");
        m_searchControl = new SearchControl(m_markerContainer, this, m_eventManager, m_componentTracker);
        final String id = m_searchControl.getElement().getId();
        if (id == null || "".equals(id)) {
            m_searchControl.getElement().setId("search-control");
        } else {
            LOG.info("NodeMapWidget.addSearchControl(): id = " + id);
        }
        final HTMLPanel mapParent = HTMLPanel.wrap(m_mapPanel.getParent().getElement());
        final Style searchStyle = m_searchControl.getElement().getStyle();
        searchStyle.setPosition(Position.ABSOLUTE);
        searchStyle.setTop(5, Unit.PX);
        searchStyle.setLeft(5, Unit.PX);
        searchStyle.setZIndex(1000);
        mapParent.add(m_searchControl);
    }

    private void addAlarmControl() {
        LOG.info("NodeMapWidget.addAlarmControl()");

        m_alarmControl = new AlarmControl(m_eventManager, m_componentTracker);
        final String id = m_alarmControl.getElement().getId();
        if (id == null || "".equals(id)) {
            m_alarmControl.getElement().setId("alarm-control");
        } else {
            LOG.info("NodeMapWidget.addAlarmControl(): id = " + id);
        }

        final HTMLPanel mapParent = HTMLPanel.wrap(m_mapPanel.getParent().getElement());
        final Style searchStyle = m_alarmControl.getElement().getStyle();
        searchStyle.setPosition(Position.ABSOLUTE);
        searchStyle.setTop(5, Unit.PX);
        searchStyle.setRight(5, Unit.PX);
        searchStyle.setZIndex(1000);
        mapParent.add(m_alarmControl);
    }

    private void addZoomControl() {
        LOG.info("NodeMapWidget.addZoomControl()");
        final ZoomOptions options = new ZoomOptions();
        options.setPosition("topright");
        m_map.addControl(new Zoom(options));
    }

    public boolean markerShouldBeVisible(final JSNodeMarker marker) {
        return m_filter.matches(marker);
    }

    public void updateMarkerClusterLayer() {
        if (m_markerContainer == null || m_markerClusterGroup == null) {
            LOG.info(
                    "NodeMapWidget.updateMarkerClusterLayout(): markers or marker clusters not initialized yet, deferring refresh");
            // try again in 1 second
            Scheduler.get().scheduleDeferred(new ScheduledCommand() {
                @Override
                public void execute() {
                    updateMarkerClusterLayer();
                }
            });
            return;
        }

        clearExistingMarkers();
        addNewMarkers();
        removeDisabledMarkers();
        zoomToFit();
        sendSelectionToBackend();
    }

    private void clearExistingMarkers() {
        LOG.info("NodeMapWidget.clearExistingMarkers()");
        if (m_markerClusterGroup != null) {
            m_markerClusterGroup.clearLayers();
        }
        if (m_stateClusterGroups != null) {
            for (int i = 0; i < m_stateClusterGroups.length; i++) {
                m_stateClusterGroups[i].clearLayers();
            }
        }
    }

    private void addNewMarkers() {
        // add new markers
        LOG.info("NodeMapWidget.addNewMarkers(): adding " + m_markerContainer.size() + " markers to the map.");
        Scheduler.get().scheduleIncremental(new RepeatingCommand() {
            final ListIterator<JSNodeMarker> m_markerIterator = m_markerContainer.listIterator();

            @Override
            public boolean execute() {
                if (m_markerIterator.hasNext()) {
                    final JSNodeMarker marker = m_markerIterator.next();
                    final Coordinates coordinates = marker.getCoordinates();
                    if (coordinates == null) {
                        LOG.log(Level.WARNING,
                                "NodeMapWidget.addNewMarkers(): no coordinates found for marker! " + marker);
                        return true;
                    }
                    if (m_groupByState && StatesData.inUs(coordinates.getLatitudeAsDouble(),
                            coordinates.getLongitudeAsDouble(), StatesData.getUsShape())) {
                        final int stateId = StatesData.getStateId(marker.getLatLng().lat(),
                                marker.getLatLng().lng(), StatesData.getInstance());
                        if (!m_stateClusterGroups[stateId].hasLayer(marker)) {
                            m_stateClusterGroups[stateId].addLayer(marker);
                        }
                    } else {
                        if (!m_markerClusterGroup.hasLayer(marker)) {
                            m_markerClusterGroup.addLayer(marker);
                        }
                    }
                    return true;
                }

                LOG.info("NodeMapWidget.addNewMarkers(): finished adding visible markers ("
                        + m_markerContainer.size() + " entries)");
                return false;
            }

        });
    }

    private void removeDisabledMarkers() {
        // remove disabled markers
        final List<JSNodeMarker> disabledMarkers = m_markerContainer.getDisabledMarkers();
        LOG.info("NodeMapWidget.removeDisabledMarkers(): removing " + disabledMarkers.size()
                + " disabled markers from the map.");
        Scheduler.get().scheduleIncremental(new RepeatingCommand() {
            final ListIterator<JSNodeMarker> m_markerIterator = disabledMarkers.listIterator();

            @Override
            public boolean execute() {
                if (m_markerIterator.hasNext()) {
                    final JSNodeMarker marker = m_markerIterator.next();
                    marker.closePopup();
                    final Coordinates coordinates = marker.getCoordinates();
                    if (coordinates == null) {
                        LOG.log(Level.WARNING,
                                "NodeMapWidget.removeDisabledMarkers(): no coordinates found for marker! "
                                        + marker);
                        return true;
                    }
                    if (m_groupByState && StatesData.inUs(marker.getLatLng().lat(), marker.getLatLng().lng(),
                            StatesData.getUsShape())) {
                        final int stateId = StatesData.getStateId(marker.getLatLng().lat(),
                                marker.getLatLng().lng(), StatesData.getInstance());
                        m_stateClusterGroups[stateId].removeLayer(marker);
                    } else {
                        m_markerClusterGroup.removeLayer(marker);
                    }
                    return true;
                }

                LOG.info("NodeMapWidget.removeDisabledMarkers(): finished removing filtered markers ("
                        + disabledMarkers.size() + " entries)");
                return false;
            }
        });
    }

    private void zoomToFit() {
        // zoom on first run
        if (m_firstUpdate) {
            LOG.info("NodeMapWidget.zoomToFit(): first update, zooming to bounds.");
            Scheduler.get().scheduleDeferred(new ScheduledCommand() {
                @Override
                public void execute() {
                    if (m_firstUpdate) {
                        final List<JSNodeMarker> allMarkers = m_markerContainer.getAllMarkers();

                        if (allMarkers.size() == 0) {
                            LOG.info("NodeMapWidget.zoomToFit(): no bounds yet, skipping.");
                        } else {
                            final LatLngBounds bounds = new LatLngBounds();
                            for (final NodeMarker marker : allMarkers) {
                                LOG.info("NodeMapWidget.zoomToFit(): processing marker: " + marker);
                                final Coordinates coordinates = marker.getCoordinates();
                                if (coordinates == null) {
                                    LOG.log(Level.WARNING,
                                            "NodeMapWidget.zoomToFit(): no coordinates found for marker! "
                                                    + marker);
                                } else {
                                    bounds.extend(JSNodeMarker.coordinatesToLatLng(coordinates));
                                }
                            }
                            LOG.info("NodeMapWidget.zoomToFit(): setting boundary to " + bounds.toBBoxString()
                                    + ".");
                            m_map.fitBounds(bounds);
                            m_firstUpdate = false;
                        }
                    }
                }
            });
        }
    }

    private void sendSelectionToBackend() {
        Scheduler.get().scheduleDeferred(new ScheduledCommand() {
            @Override
            public void execute() {
                final List<Integer> nodeIds = new ArrayList<Integer>();
                for (final JSNodeMarker marker : m_markerContainer.getMarkers()) {
                    final Integer nodeId = marker.getNodeId();
                    if (nodeId != null) {
                        nodeIds.add(nodeId);
                    }
                }
                m_clientToServerRpc.setSelectedNodes(nodeIds);
                LOG.info("NodeMapWidget.sendSelectionToBackend(): sent " + nodeIds.size() + " nodes to backend.");
            }
        });
    }

    @Override
    public List<JSNodeMarker> getMarkers() {
        return m_markerContainer.getMarkers();
    }

    public void setMarkers(final List<JSNodeMarker> markers) {
        if (markers != null) {
            m_markerContainer.setMarkers(markers);
        } else {
            m_markerContainer.setMarkers(new ArrayList<JSNodeMarker>());
        }
    }

    private final void destroyMap() {
        if (m_markerClusterGroup != null) {
            m_markerClusterGroup.clearLayers();
            if (m_stateClusterGroups != null) {
                for (int i = 0; i < m_stateClusterGroups.length; i++) {
                    m_stateClusterGroups[i].clearLayers();
                }
            }
        }
        if (m_map != null) {
            m_map.removeLayer(m_markerClusterGroup);
            m_map.removeLayer(m_layer);
            m_map = null;
        }
    }

    public void setRpc(final NodeIdSelectionRpc rpc) {
        m_clientToServerRpc = rpc;
    }

    @Override
    public void onFilteredMarkersUpdatedEvent(final FilteredMarkersUpdatedEvent event) {
        LOG.info("NodeMapWidget.onFilteredMarkersUpdated(), refreshing node map widgets");
        updateMarkerClusterLayer();
    }

    @Override
    public void onApplicationInitialized(final ApplicationInitializedEvent event) {
        LOG.info("NodeMapWidget.onApplicationInitialized(): triggering a backend refresh");
        Scheduler.get().scheduleFixedDelay(new RepeatingCommand() {
            @Override
            public boolean execute() {
                LOG.info("NodeMapWidget.onApplicationInitialized(): refreshing");
                m_clientToServerRpc.refresh();
                return false;
            }
        }, 5000);
    }

    public OpenNMSEventManager getEventManager() {
        return m_eventManager;
    }

    public void setGroupByState(final boolean groupByState) {
        m_groupByState = groupByState;
        LOG.info("NodeMapWidget.setGroupByState(): group by state: " + (groupByState ? "yes" : "no"));
    }

    public void setMaxClusterRadius(final int maxClusterRadius) {
        if (m_maxClusterRadius != maxClusterRadius) {
            m_maxClusterRadius = maxClusterRadius;
            if (m_map != null) {
                addMarkerLayer();
            }
        }
    }
}