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

eu.limetri.client.mapviewer.swing.jxmap.map.MapWidgetFactory Maven / Gradle / Ivy

/**
 *  Copyright (C) 2008-2013 LimeTri. All rights reserved.
 *
 *  AgroSense 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.
 *
 *  There are special exceptions to the terms and conditions of the GPLv3 as it
 *  is applied to this software, see the FLOSS License Exception
 *  .
 *
 *  AgroSense 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
 *  AgroSense. If not, see .
 */
package eu.limetri.client.mapviewer.swing.jxmap.map;

import java.awt.Point;
import java.beans.PropertyChangeEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Logger;

import javax.swing.Action;
import javax.swing.JPopupMenu;

import org.netbeans.api.visual.action.ActionFactory;
import org.netbeans.api.visual.action.PopupMenuProvider;
import org.netbeans.api.visual.widget.Widget;
import org.openide.nodes.Node;
import org.openide.util.Utilities;

import eu.limetri.api.geo.Geographical;
import eu.limetri.api.geo.Geometrical;
import eu.limetri.api.geo.DataSet;
import eu.limetri.client.mapviewer.api.HasMapActions;

import eu.limetri.client.mapviewer.api.Layer;
import eu.limetri.client.mapviewer.api.Palette;
import eu.limetri.client.mapviewer.api.SplitGeometryHandler;
import eu.limetri.client.mapviewer.swing.jxmap.MapProperties;
import eu.limetri.client.mapviewer.swing.render.DataSetWidget;
import eu.limetri.client.mapviewer.swing.render.JXMapGeoTranslator;
import eu.limetri.client.mapviewer.swing.render.GeoTranslator;
import eu.limetri.client.mapviewer.swing.render.GeometricalWidget;
import eu.limetri.client.mapviewer.swing.render.GridCoverage2DWidget;
import eu.limetri.client.mapviewer.swing.render.IconWidget;
import java.util.Set;
import org.geotools.coverage.grid.GridCoverage2D;
import org.openide.util.actions.SystemAction;

/**
 * factory class for the creation of map widgets
 *
 *
 * @author Timon Veenstra
 */
public class MapWidgetFactory {

    private static final Logger LOGGER = Logger.getLogger(MapWidgetFactory.class.getCanonicalName());

    /**
     * Create a widget from a node and a scene
     *
     * @param node node to create a widgets for
     * @param scene scene to create the widget on
     * @return the created widget
     */
    public static Widget createWidgetFromNode(final Node node, MapScene scene) {
        Geometrical geometrical = node.getLookup().lookup(Geometrical.class);
        Layer layer = Layer.Finder.findLayer(node);
        Palette palette = getPalette(node, layer);

        if (layer == null) {
            LOGGER.warning("FIXME:Attempt to create a widget from a node without a layer in its hierarchy.");
            return null;
        }

        if (scene.getObjects().contains(node)) {
            LOGGER.warning("FIXME:Attempt to create a widget from a node which is already present in the scene");
            return null;
        }

        if (geometrical == null) {
            Geographical geographical = node.getLookup().lookup(Geographical.class);
            if (geographical == null) {
                LOGGER.finest("Attempt to create a widget from a node without a geographical or geometrical, could be a parent node");
                return null;
            } else {
                if (geographical.getBoundingBox() == null) {
                    LOGGER.finest("Geographical does not have a boundingbox, not able to process it");
                    return null;
                }

                if (DataSet.class.equals(geographical.getType())) {
                    LOGGER.finest("Creating DataSetWidget");
                    
                    final DataSetWidget widget = new DataSetWidget(layer.getName(), geographical, palette, geographical.getBoundingBox(), new JXMapGeoTranslator(scene.getMapViewer()), scene);
                    if (palette.equals(layer.getPalette())) {
                        layer.addPropertyChangeListener(Layer.PROP_PALETTE, (PropertyChangeEvent evt) -> {
                            widget.setPalette((Palette) evt.getNewValue());
                        });
                    }
                    addActions(widget, node, scene);
                    return widget;                    
                }
                
                if (GridCoverage2D.class.equals(geographical.getType())) {

                    LOGGER.finest("Creating GridCoverage2DWidget");

                    final GridCoverage2DWidget widget = new GridCoverage2DWidget(layer.getName(), geographical, palette, geographical.getBoundingBox(), new JXMapGeoTranslator(scene.getMapViewer()), scene);
                    if (palette.equals(layer.getPalette())) {
                        layer.addPropertyChangeListener(Layer.PROP_PALETTE, (PropertyChangeEvent evt) -> {
                            widget.setPalette((Palette) evt.getNewValue());
                        });
                    }
                    addActions(widget, node, scene);
                    return widget;
                }
            }
        }

        // support empty geometries:
        if (geometrical.getGeometry() == null) {
            return null;
        }

        GeometricalWidget widget = new GeometricalWidget(scene, new JXMapGeoTranslator(scene.getMapViewer()), geometrical, palette, null);
        addActions(widget, node, scene);

        return widget;
    }

    private static Palette getPalette(Node node, Layer layer) {
        // add palette is present in lookup of node, otherwise use the palette from the layer
        Palette palette = node.getLookup().lookup(Palette.class);
        if (palette == null) {
            LOGGER.finest("using layers palette");
            palette = layer.getPalette();
        } else {
            LOGGER.finest("using palette from node lookup");
        }
        return palette;
    }

    /**
     * Tries to create and returns an icon widget for the given node. No widget is created if the node's lookup doesn't contain
     * a geographical or the geographical doesn't have an icon.
     *
     * @param scene
     * @param node The node (with a geometrical in its lookup)
     * @return The created icon widget or null if none created.
     */
    public static Widget createIconWidgetFromNode(final Node node, MapScene scene) {
        Geometrical geometrical = node.getLookup().lookup(Geometrical.class);
        Layer layer = Layer.Finder.findLayer(node);
        GeoTranslator geoTranslator = new JXMapGeoTranslator(scene.getMapViewer());
        Widget iconWidget = null;
        if (geometrical != null && geometrical.getIcon() != null) {
            iconWidget = new IconWidget(geometrical, getPalette(node, layer), geoTranslator, scene);
            addActions(iconWidget, node, scene);
        }
        return iconWidget;
    }

    /**
     * Adds hover, select and the node's actions to the given widget. Doesn't do anything if the given widget is null. Will
     * first check with {@link MapProperties#getWidgetActionsEnabledValue()} if this actions should be added
     *
     * @param widget The widget to add the actions to. Can be n
     * @param node The node to some of the actions from
     * @param scene The scene the widget is on
     */
    private static void addActions(Widget widget, final Node node, final MapScene scene) {
        if (widget != null) {
            if (MapProperties.getWidgetStateActionsEnabledValue()) {
                widget.getActions().addAction(scene.createObjectHoverAction());
                widget.getActions().addAction(scene.createWidgetHoverAction());
                widget.getActions().addAction(scene.createSelectAction());
            }
            if (MapProperties.getNodeActionsAvailableOnWidgetValue()) {
                widget.getActions().addAction(ActionFactory.createPopupMenuAction(new PopupMenuProvider() {

                    @Override
                    public JPopupMenu getPopupMenu(Widget widget, Point localLocation) {
                        List actionList = new ArrayList<>();
                        actionList.addAll(Arrays.asList(node.getActions(true)));
                        actionList.add(getSplitGeometryAction(node));
                        actionList.add(SystemAction.get(RemoveLayerAction.class));
                        actionList.add(null);
                        actionList.addAll(Arrays.asList(getMapActions(node, scene)));

                        //TODO: add LocationPicker for DynamicPoint
                        //FIXME: change action filtering, also see LayerListNode
                        Action actionToRemove = null;
                        for (Action action : actionList) {
                            if (action != null && "DelegateAction".equals(action.getClass().getSimpleName())) {
                                actionToRemove = action;
                                break;
                            }
                        }

                        if (actionToRemove != null) {
                            actionList.remove(actionToRemove);
                        }
                        Action[] actions = actionList.toArray(new Action[actionList.size()]);

                        JPopupMenu menu;
                        menu = Utilities.actionsToPopup(actions, node.getLookup());
                        menu = MapProperties.addTitleToPopup(menu, node.getDisplayName());
                        return menu;
                    }
                }));
            }
        }
    }

    /**
     * Creates and return a list of the map specific actions for the node. Adds the default actions for DynamicGeometrical
     * (currently only PickLocationAction) and checks the DynamicGeometrial for the implementation's specific map actions.
     * Returns an empty list if the node does not contain a DynamicGeometrical.
     *
     * @param node The node
     * @param scene The scene the node's widget is rendered on
     * @return The map specific actions for the node
     */
    private static Action[] getMapActions(final Node node, final MapScene scene) {
        HasMapActions object = node.getLookup().lookup(HasMapActions.class);
        if (object != null) {
            return object.getMapActions();
        } else {
            return new Action[0];
        }
    }

    private static Action getSplitGeometryAction(Node node) {
        Action action = null;
        if (node.getLookup().lookup(SplitGeometryHandler.class) != null) {
            action = new SplitGeometryAction(node);
        }
        return action;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy