view gwt-client/src/main/java/org/dive4elements/river/client/client/ui/map/MapOutputTab.java @ 9561:5397555d74c9

MapOutputTab fix
author gernotbelger
date Thu, 25 Oct 2018 17:00:58 +0200
parents 992c188b7330
children
line wrap: on
line source
/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
 * Software engineering by Intevation GmbH
 *
 * This file is Free Software under the GNU AGPL (>=v3)
 * and comes with ABSOLUTELY NO WARRANTY! Check out the
 * documentation coming with Dive4Elements River for details.
 */

package org.dive4elements.river.client.client.ui.map;

import java.util.HashMap;
import java.util.List;

import org.dive4elements.river.client.client.Config;
import org.dive4elements.river.client.client.FLYSConstants;
import org.dive4elements.river.client.client.event.OutputParameterChangeEvent;
import org.dive4elements.river.client.client.event.OutputParameterChangeHandler;
import org.dive4elements.river.client.client.event.RedrawRequestEvent;
import org.dive4elements.river.client.client.event.RedrawRequestHandler;
import org.dive4elements.river.client.client.services.LoadArtifactService;
import org.dive4elements.river.client.client.services.LoadArtifactServiceAsync;
import org.dive4elements.river.client.client.services.MapOutputService;
import org.dive4elements.river.client.client.services.MapOutputServiceAsync;
import org.dive4elements.river.client.client.services.StepForwardService;
import org.dive4elements.river.client.client.services.StepForwardServiceAsync;
import org.dive4elements.river.client.client.ui.CollectionView;
import org.dive4elements.river.client.client.ui.OutputTab;
import org.dive4elements.river.client.client.ui.ThemePanel;
import org.dive4elements.river.client.shared.model.Artifact;
import org.dive4elements.river.client.shared.model.ArtifactDescription;
import org.dive4elements.river.client.shared.model.AttributedTheme;
import org.dive4elements.river.client.shared.model.Collection;
import org.dive4elements.river.client.shared.model.Data;
import org.dive4elements.river.client.shared.model.DataItem;
import org.dive4elements.river.client.shared.model.DataList;
import org.dive4elements.river.client.shared.model.DefaultData;
import org.dive4elements.river.client.shared.model.DefaultDataItem;
import org.dive4elements.river.client.shared.model.MapConfig;
import org.dive4elements.river.client.shared.model.OutputMode;
import org.dive4elements.river.client.shared.model.Recommendation;
import org.dive4elements.river.client.shared.model.Theme;
import org.dive4elements.river.client.shared.model.ThemeList;
import org.dive4elements.river.client.shared.model.WMSLayer;
import org.gwtopenmaps.openlayers.client.Bounds;
import org.gwtopenmaps.openlayers.client.Map;
import org.gwtopenmaps.openlayers.client.MapWidget;
import org.gwtopenmaps.openlayers.client.event.VectorFeatureAddedListener;
import org.gwtopenmaps.openlayers.client.event.VectorFeatureRemovedListener;
import org.gwtopenmaps.openlayers.client.feature.VectorFeature;
import org.gwtopenmaps.openlayers.client.format.GeoJSON;
import org.gwtopenmaps.openlayers.client.layer.Layer;
import org.gwtopenmaps.openlayers.client.layer.TransitionEffect;
import org.gwtopenmaps.openlayers.client.layer.Vector;
import org.gwtopenmaps.openlayers.client.layer.WMS;
import org.gwtopenmaps.openlayers.client.layer.WMSOptions;
import org.gwtopenmaps.openlayers.client.layer.WMSParams;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.smartgwt.client.util.SC;
import com.smartgwt.client.widgets.Canvas;
import com.smartgwt.client.widgets.events.ParentMovedEvent;
import com.smartgwt.client.widgets.events.ParentMovedHandler;
import com.smartgwt.client.widgets.events.ResizedEvent;
import com.smartgwt.client.widgets.events.ResizedHandler;
import com.smartgwt.client.widgets.layout.HLayout;
import com.smartgwt.client.widgets.layout.VLayout;
import com.smartgwt.client.widgets.tab.events.TabSelectedEvent;
import com.smartgwt.client.widgets.tab.events.TabSelectedHandler;

public class MapOutputTab extends OutputTab implements RedrawRequestHandler, ExternalWMSWindow.LayerLoader, TabSelectedHandler, OutputParameterChangeHandler {

    public static final String DEFAULT_SRID = "4326";

    public static final String BARRIERS_PARAMETER_KEY = "uesk.barriers";

    public static final String WSPLGEN_FACET = "floodmap.wsplgen";

    public static final String EXTERNAL_WMS_FACTORY = "externalwmsfactory";

    protected StepForwardServiceAsync feedService = GWT.create(StepForwardService.class);

    protected MapOutputServiceAsync mapService = GWT.create(MapOutputService.class);

    /** Service handle to clone and add artifacts to collection. */
    protected LoadArtifactServiceAsync loadArtifactService = GWT.create(LoadArtifactService.class);

    protected FLYSConstants MSG = GWT.create(FLYSConstants.class);

    protected MapToolbar controlPanel;
    protected ThemePanel themePanel;
    protected Canvas themePanelCanvas;
    protected MapWidget mapPanel;
    protected Canvas mapPanelCanvas;
    protected VLayout rootLayout = new VLayout();
    protected AbsolutePanel absPan = new AbsolutePanel();
    protected FloodMap floodMap;

    private boolean cancel = false;

    private final java.util.Map<String, String> wmsUrls = new HashMap<String, String>();

    public MapOutputTab(final String title, final Collection collection, final OutputMode mode, final CollectionView collectionView) {
        super(title, collection, collectionView, mode);

        collectionView.registerTabHandler(this);

        this.mapService.doOut(collection, new AsyncCallback<MapConfig>() {
            @Override
            public void onFailure(final Throwable caught) {
                GWT.log("MAP ERROR: " + caught.getMessage());
            }

            @Override
            public void onSuccess(final MapConfig c) {
                GWT.log("MAP SUCCESS!");

                if (!MapOutputTab.this.cancel) {
                    final Bounds max = boundsFromString(c.getMaxExtent());
                    Bounds initial = boundsFromString(c.getInitialExtent());

                    if (initial == null) {
                        GWT.log("Warning: No initial extent set.");
                        initial = max;
                    }

                    setFloodmap(new FloodMap(c.getSrid(), max, 640, 480));

                    initLayout();

                    initBarriers();

                    GWT.log("MAX EXTENT: " + max);
                    GWT.log("ZOOM TO: " + initial);
                    getMap().zoomToExtent(initial);
                }
            }
        });
    }

    public void cancelMapService() {

        this.cancel = true;

        // if (this.floodMap != null)
        // this.floodMap.destroy();
    }

    protected void initLayout() {
        this.rootLayout.setHeight100();
        this.rootLayout.setWidth100();
        this.rootLayout.setMembersMargin(2);

        final HLayout hlayout = new HLayout();
        hlayout.setMembersMargin(0);

        this.themePanelCanvas = createThemePanel();

        this.controlPanel = createControlPanel();
        this.mapPanel = this.floodMap.getMapWidget();

        this.rootLayout.addMember(this.controlPanel);
        this.rootLayout.addMember(this.absPan);
        this.absPan.setWidth("100%");
        this.absPan.setHeight("100%");
        this.absPan.add(this.themePanelCanvas);
        this.absPan.add(this.mapPanel);

        this.rootLayout.addResizedHandler(new ResizedHandler() {
            @Override
            public void onResized(final ResizedEvent e) {
                doLayout();
            }
        });

        this.rootLayout.addParentMovedHandler(new ParentMovedHandler() {
            @Override
            public void onParentMoved(final ParentMovedEvent event) {
                MapOutputTab.this.mapPanel.getMap().updateSize();
            }
        });

        setPane(this.rootLayout);
    }

    protected void doLayout() {
        if (!this.rootLayout.isVisible()) {
            return;
        }

        // Manually set the height of the AbsolutePanel,
        // somehow this is necessary
        this.absPan.setHeight(String.valueOf(this.rootLayout.getHeight() - this.controlPanel.getHeight() - 2) + "px");

        // Calculate bounds of Map
        final int height = this.rootLayout.getHeight() - this.controlPanel.getHeight() - 6;
        final int width = this.controlPanel.getWidth() - (this.themePanelCanvas.isVisible() ? this.themePanelCanvas.getWidth() + 4 : 2);

        // Set size and position of Map
        final String w = String.valueOf(width) + "px";
        final String h = String.valueOf(height) + "px";
        GWT.log("width=" + w);

        this.mapPanel.setSize(w, h);
        this.mapPanel.getMap().updateSize();
        if (this.themePanelCanvas.isVisible()) {
            this.absPan.setWidgetPosition(this.mapPanel, this.themePanelCanvas.getWidth() + 2, 0);
        } else {
            this.absPan.setWidgetPosition(this.mapPanel, 0, 0);
        }

        // Set bounds of ThemePanelCanvas
        this.themePanelCanvas.setSize(this.themePanelCanvas.getWidthAsString(), String.valueOf(height + 2) + "px");
    }

    protected void initBarriers() {
        final Vector vector = this.floodMap.getBarrierLayer();
        vector.addVectorFeatureAddedListener(new VectorFeatureAddedListener() {
            @Override
            public void onFeatureAdded(final FeatureAddedEvent e) {
                saveBarriers();
            }
        });

        vector.addVectorFeatureRemovedListener(new VectorFeatureRemovedListener() {
            @Override
            public void onFeatureRemoved(final FeatureRemovedEvent e) {
                saveBarriers();
            }
        });

        final Artifact artifact = getArtifact();

        if (artifact == null) {
            return;
        }

        final ArtifactDescription desc = artifact.getArtifactDescription();

        String geojson = getGeoJSONFromStatic(desc);
        geojson = geojson != null ? geojson : getGeoJSONFromDynamic(desc);

        if (geojson == null || geojson.length() == 0) {
            GWT.log("No geojson string found -> no barriers existing.");
            return;
        }

        final GeoJSON reader = new GeoJSON();
        final VectorFeature[] features = reader.read(geojson);

        vector.addFeatures(features);
    }

    public void addLayer(final Theme newTheme) {

        final Layer layer = createWMSLayer(newTheme);

        final FloodMap map = getFloodmap();

        if (map != null) {
            GWT.log("Add new layer '" + layer.getName() + "' to map.");
            map.addLayer(layer);
            if (layer instanceof WMS) {
                this.wmsUrls.put(layer.getName(), ((WMS) layer).getFullRequestString(new WMSParams(), null));
            }
        }
    }

    public void removeLayer(final Theme theme) {

        final String layerId = getLayerId(theme);

        final Map map = getMap();

        final Layer[] layers = map.getLayers();

        for (final Layer layer : layers) {
            if (layerId.equals(layer.getName())) {
                map.removeLayer(layer);
            }
        }
    }

    @Override
    public void onRedrawRequest(final RedrawRequestEvent event) {
        this.mapService.doOut(this.collection, new AsyncCallback<MapConfig>() {
            @Override
            public void onFailure(final Throwable caught) {
                GWT.log("MAP ERROR: " + caught.getMessage());
            }

            @Override
            public void onSuccess(final MapConfig c) {
                GWT.log("We want to refresh the map now!");
                MapOutputTab.this.themePanel.updateCollection();
                getFloodmap().update();
            }
        });
    }

    @Override
    public void load(final List<WMSLayer> toLoad) {
        GWT.log("The user wants to add " + toLoad.size() + " new WMS layers.");

        final int len = toLoad.size();

        final Recommendation[] recom = new Recommendation[len];

        for (int i = 0; i < len; i++) {
            final WMSLayer w = toLoad.get(i);

            final String ids = w.getServer() + ";" + w.getName() + ";" + w.getTitle();
            recom[i] = new Recommendation(EXTERNAL_WMS_FACTORY, ids);
        }

        final Collection c = getCollection();

        final Config config = Config.getInstance();
        final String locale = config.getLocale();

        this.loadArtifactService.loadMany(c, recom, EXTERNAL_WMS_FACTORY, locale, new AsyncCallback<Artifact[]>() {

            @Override
            public void onFailure(final Throwable throwable) {
                SC.warn(MapOutputTab.this.MSG.getString(throwable.getMessage()));
            }

            @Override
            public void onSuccess(final Artifact[] newArtifacts) {
                getThemePanel().updateCollection();
            }
        });
    }

    protected void setFloodmap(final FloodMap floodMap) {
        this.floodMap = floodMap;
    }

    protected FloodMap getFloodmap() {
        return this.floodMap;
    }

    protected Map getMap() {
        return this.floodMap.getMap();
    }

    protected ThemePanel getThemePanel() {
        return this.themePanel;
    }

    protected String getGeoJSONFromDynamic(final ArtifactDescription desc) {
        final DataList list = desc.getCurrentData();

        if (list == null) {
            return null;
        }

        final List<Data> datas = list.getAll();
        for (final Data data : datas) {
            final String key = data.getLabel();

            if (key != null && key.equals(BARRIERS_PARAMETER_KEY)) {
                final DataItem def = data.getDefault();

                if (def != null) {
                    return def.getStringValue();
                }
            }
        }

        return null;
    }

    protected String getGeoJSONFromStatic(final ArtifactDescription desc) {
        // TODO Implement this method, if there are reachable states right after
        // the floodmap state - which is currently not the case.
        return null;
    }

    public ThemeList getThemeList() {
        return this.collection.getThemeList(this.mode.getName());
    }

    public String getSrid() {
        final ThemeList themeList = getThemeList();

        final int num = themeList.getThemeCount();

        for (int i = 1; i <= num; i++) {
            final AttributedTheme theme = (AttributedTheme) themeList.getThemeAt(i);

            if (theme == null) {
                continue;
            }

            final String srid = theme.getAttr("srid");

            if (srid != null && srid.length() > 0) {
                return srid;
            }
        }

        return DEFAULT_SRID;
    }

    protected Bounds boundsFromString(final String bounds) {
        GWT.log("Create Bounds from String: '" + bounds + "'");
        if (bounds == null || bounds.length() == 0) {
            return null;
        }

        final String[] values = bounds.split(" ");

        if (values == null || values.length < 4) {
            return null;
        }

        try {
            return new Bounds(Double.valueOf(values[0]), Double.valueOf(values[1]), Double.valueOf(values[2]), Double.valueOf(values[3]));
        }
        catch (final NumberFormatException nfe) {
        }

        return null;
    }

    private Layer createWMSLayer(final Theme theme) {
        if (!(theme instanceof AttributedTheme)) {
            return null;
        }

        final AttributedTheme at = (AttributedTheme) theme;

        // final String desc = at.getAttr("description");
        final String url = at.getAttr("url");
        final String layers = at.getAttr("layers");

        final String layerId = getLayerId(theme);

        if (url == null || layers == null) {
            return null;
        }

        final WMSParams params = new WMSParams();
        params.setLayers(layers);
        params.setFormat("image/png");
        params.setIsTransparent(true);

        final WMSOptions opts = new WMSOptions();

        opts.setProjection("EPSG:" + getSrid());
        opts.setSingleTile(true);
        opts.setRatio(1);
        if (layers.equals("OSM-WMS-Dienst")) {
            opts.setAttribution(this.MSG.attribution());
            opts.setSingleTile(true);
            opts.setTransitionEffect(TransitionEffect.RESIZE);
        }
        final WMS wms = new WMS(layerId, url, params, opts);
        wms.setIsVisible(at.getActive() == 1);
        wms.setIsBaseLayer(false);
        // We can't set the full_url attribute here because map is not set
        // at.addAttr("full_url", wms.getFullRequestString(params, null));
        return wms;
    }

    private String getLayerId(final Theme theme) {

        final AttributedTheme at = (AttributedTheme) theme;

        final String name = at.getAttr("name");
        final String layers = at.getAttr("layers");

        return name + "_" + layers;
    }

    protected MapToolbar createControlPanel() {
        return new MapToolbar(this, this.floodMap, false);
    }

    protected Canvas createThemePanel() {
        final Canvas c = new Canvas();
        c.setMinWidth(300);
        c.setWidth(200);
        c.setHeight100();
        c.setCanDragResize(true);
        c.setBorder("1px solid black");

        this.themePanel = new MapThemePanel(this.getCollectionView(), this.mode, this, new MapThemePanel.ActivateCallback() {
            @Override
            public void activate(final Theme theme, final boolean active) {
                fireActivateTheme(theme, active);
            }
        }, new MapThemePanel.ThemeMovedCallback() {
            @Override
            public void onThemeMoved(final Theme theme, final int oldIdx, final int newIdx) {
                // this code synchronizes the ThemePanel and the OpenLayers
                // internal order of layers.
                final AttributedTheme at = (AttributedTheme) theme;

                final String layerId = getLayerId(theme);

                final Map map = getMap();
                final Layer[] layers = map.getLayersByName(layerId);

                if (layers == null || layers.length == 0) {
                    GWT.log("Error: Cannot find layer '" + layerId + "'");
                    return;
                }

                map.raiseLayer(layers[0], (newIdx - oldIdx) * -1);
                map.zoomTo(map.getZoom() - 1);
                map.zoomTo(map.getZoom() + 1);
            }
        }, new MapThemePanel.LayerZoomCallback() {
            @Override
            public void onLayerZoom(final Theme theme, final String extent) {
                final Bounds zoomTo = boundsFromString(extent);

                if (zoomTo == null) {
                    GWT.log("WARNING: No valid bounds for zooming found!");
                    return;
                }

                getMap().zoomToExtent(zoomTo);
            }
        });
        this.themePanel.addRedrawRequestHandler(this);
        this.themePanel.addOutputParameterChangeHandler(this);
        c.addChild(this.themePanel);

        return c;
    }

    private void fireActivateTheme(final Theme theme, final boolean active) {
        activateTheme(theme, active);
    }

    protected void activateTheme(final Theme theme, final boolean active) {

        final String layerId = getLayerId(theme);

        final Layer layer = this.floodMap.getMap().getLayerByName(layerId);

        GWT.log("Set visibility of '" + layerId + "': " + active);

        if (layer != null) {
            layer.setIsVisible(active);
        }
    }

    protected void saveBarriers() {
        final Vector layer = this.floodMap.getBarrierLayer();

        final GeoJSON format = new GeoJSON();
        final String features = format.write(layer.getFeatures());

        final DataItem item = new DefaultDataItem(BARRIERS_PARAMETER_KEY, BARRIERS_PARAMETER_KEY, features);

        final Data data = new DefaultData(BARRIERS_PARAMETER_KEY, BARRIERS_PARAMETER_KEY, "String", new DataItem[] { item });

        final Config config = Config.getInstance();
        final String locale = config.getLocale();

        this.feedService.go(locale, getArtifact(), new Data[] { data }, new AsyncCallback<Artifact>() {
            @Override
            public void onFailure(final Throwable caught) {
                GWT.log("Could not save barrier geometries: " + caught.getMessage());
            }

            @Override
            public void onSuccess(final Artifact artifact) {
                GWT.log("Successfully saved barrier geometries.");
            }
        });
    }

    @Override
    public void onTabSelected(final TabSelectedEvent tse) {
        if (this.floodMap == null) {
            return;
        }
        if (this.equals(tse.getTab())) {
            this.floodMap.activateScaleLine(true);
        } else {
            this.controlPanel.activateMeasureControl(false);
            this.floodMap.activateScaleLine(false);
        }
    }

    public void toogleThemePanel() {
        this.themePanelCanvas.setVisible(!this.themePanelCanvas.isVisible());

        // Trigger resize event handler
        doLayout();
    }

    @Override
    public void onOutputParameterChanged(final OutputParameterChangeEvent evt) {
        GWT.log("OutputParameterChanged");
        this.controlPanel.updateThemes(getThemePanel().getThemeList());
    }

    public String getWMSUrl(final Theme theme) {
        final String layerId = getLayerId(theme);
        return this.wmsUrls.get(layerId);
    }
}

http://dive4elements.wald.intevation.org