org.orbisgis.view.map.MapElement.java Source code

Java tutorial

Introduction

Here is the source code for org.orbisgis.view.map.MapElement.java

Source

/**
 * OrbisGIS is a GIS application dedicated to scientific spatial simulation.
 * This cross-platform GIS is developed at French IRSTV institute and is able to
 * manipulate and create vector and raster spatial information.
 *
 * OrbisGIS is distributed under GPL 3 license. It is produced by the "Atelier SIG"
 * team of the IRSTV Institute <http://www.irstv.fr/> CNRS FR 2488.
 *
 * Copyright (C) 2007-2012 IRSTV (FR CNRS 2488)
 *
 * This file is part of OrbisGIS.
 *
 * OrbisGIS 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.
 *
 * OrbisGIS 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
 * OrbisGIS. If not, see <http://www.gnu.org/licenses/>.
 *
 * For more information, please consult: <http://www.orbisgis.org/>
 * or contact directly:
 * info_at_ orbisgis.org
 */
package org.orbisgis.view.map;

import java.beans.EventHandler;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import javax.swing.JOptionPane;
import org.apache.commons.io.FilenameUtils;
import org.apache.log4j.Logger;
import org.gdms.data.DataSource;
import org.gdms.data.edition.EditionEvent;
import org.gdms.data.edition.EditionListener;
import org.gdms.data.edition.MultipleEditionEvent;
import org.orbisgis.core.Services;
import org.orbisgis.core.layerModel.ILayer;
import org.orbisgis.core.layerModel.LayerCollectionEvent;
import org.orbisgis.core.layerModel.LayerException;
import org.orbisgis.core.layerModel.LayerListenerAdapter;
import org.orbisgis.core.layerModel.LayerListenerEvent;
import org.orbisgis.core.layerModel.MapContext;
import org.orbisgis.core.layerModel.OwsMapContext;
import org.orbisgis.core.layerModel.SelectionEvent;
import org.orbisgis.progress.ProgressMonitor;
import org.orbisgis.sif.UIFactory;
import org.orbisgis.view.edition.AbstractEditableElement;
import org.orbisgis.view.edition.EditableElement;
import org.orbisgis.view.edition.EditorManager;
import org.orbisgis.view.toc.Toc;
import org.xnap.commons.i18n.I18n;
import org.xnap.commons.i18n.I18nFactory;

/**
 * MapElement is an editable document that contains a Map Context.
 * @note The source code, functionality is mainly provided by GeocognitionMapContext
 */
public final class MapElement extends AbstractEditableElement {
    public static final String EDITABLE_TYPE = "MapContext";
    private static final Logger LOGGER = Logger.getLogger("gui." + MapElement.class);
    private static final I18n I18N = I18nFactory.getI18n(MapElement.class);

    // The following events does not change the MapElement modification state.
    private Set<String> ignoredModificationEvents = new HashSet(Arrays.asList(new String[] {
            MapContext.PROP_ACTIVELAYER, MapContext.PROP_SELECTEDLAYERS, MapContext.PROP_SELECTEDSTYLES }));

    private MapContext mapContext;
    private MapEditor editor;
    private String mapId;
    private PropertyChangeListener mapContextPropertyUpdateListener = EventHandler
            .create(PropertyChangeListener.class, this, "onMapUpdate", "");
    private LayerUpdateListener layerUpdateListener = new LayerUpdateListener();
    private SourceUpdateListener sourceUpdateListener = new SourceUpdateListener();
    private File mapContextFile;

    public MapElement(MapContext mapContext, File mapContextFile) {
        this.mapContext = mapContext;
        this.mapContextFile = mapContextFile;
        mapId = String.valueOf(mapContext.getIdTime());
    }

    /**
     * Constructor that read the provided map context file
     * @param mapContextFile
     */
    public MapElement(File mapContextFile) {
        mapContext = new OwsMapContext();
        try {
            mapContext.read(new FileInputStream(mapContextFile));
        } catch (FileNotFoundException ex) {
            LOGGER.error(I18N.tr("The saved map context cannot be read, starting with an empty map context."), ex);
        } catch (IllegalArgumentException ex) {
            LOGGER.error(I18N.tr("The saved map context cannot be read, starting with an empty map context."), ex);
        }
        this.mapContextFile = mapContextFile;
        mapId = String.valueOf(mapContext.getIdTime());
    }

    /**
     * Use the EditorManager service and search for the first available editable map.
     * @return The map context or null if it is not found.
     */
    public static MapElement fetchFirstMapElement() {
        EditorManager editorManager = Services.getService(EditorManager.class);
        for (EditableElement editable : editorManager.getEditableElements()) {
            if (editable instanceof MapElement) {
                return (MapElement) editable;
            }
        }
        return null;
    }

    public File getMapContextFile() {
        return mapContextFile;
    }

    /**
     * Set this element as modified
     */
    public void onMapUpdate(PropertyChangeEvent evt) {
        if (!ignoredModificationEvents.contains(evt.getPropertyName())) {
            setModified(true);
        }
    }

    /**
     * Update the modified state
     * @param modified 
     */
    public void setModified(Boolean modified) {
        this.modified = modified;
    }

    private boolean hasNotWellKnownDataSources() {
        for (ILayer layer : mapContext.getLayers()) {
            DataSource source = layer.getDataSource();
            if (source != null) {
                if (!source.getSource().isWellKnownName()) {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean hasModifiedDataSources() {
        for (ILayer layer : mapContext.getLayers()) {
            DataSource source = layer.getDataSource();
            if (source != null) {
                if (source.isModified()) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * @return True if all sources saved without errors.
     */
    private boolean saveModifiedDataSources() {
        boolean commitWithoutErrors = true;
        for (ILayer layer : mapContext.getLayers()) {
            DataSource source = layer.getDataSource();
            if (source != null) {
                if (source.isModified()) {
                    try {
                        source.commit();
                    } catch (Exception ex) {
                        LOGGER.error(ex.getLocalizedMessage(), ex);
                        commitWithoutErrors = false;
                    }
                }
            }
        }
        return commitWithoutErrors;
    }

    @Override
    public void save() throws UnsupportedOperationException {
        // If a layer hold a not well known source then alert the user
        boolean doSave = true;
        if (hasNotWellKnownDataSources()) {
            int response = JOptionPane.showConfirmDialog(UIFactory.getMainFrame(), I18N.tr(
                    "Some layers use temporary data source, are you sure to save this map and loose layers with temporary data sources ?"),
                    I18N.tr("Temporary layers data source"), JOptionPane.YES_NO_OPTION,
                    JOptionPane.WARNING_MESSAGE);
            if (response == JOptionPane.NO_OPTION) {
                doSave = false;
            }
        }
        if (hasModifiedDataSources()) {
            int response = JOptionPane.showConfirmDialog(UIFactory.getMainFrame(),
                    I18N.tr("Some layers use modified data source, do you want to save also these modifications ?"),
                    I18N.tr("Save geometry edits"), JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
            if (response == JOptionPane.CANCEL_OPTION) {
                doSave = false;
            } else if (response == JOptionPane.YES_OPTION) {
                // Save DataSources
                if (!saveModifiedDataSources()) {
                    response = JOptionPane.showConfirmDialog(UIFactory.getMainFrame(), I18N.tr(
                            "Some layers data source modifications can not be saved, are you sure you want to continue ?"),
                            I18N.tr("Errors on data source save process"), JOptionPane.YES_NO_OPTION,
                            JOptionPane.WARNING_MESSAGE);
                    if (response == JOptionPane.NO_OPTION) {
                        doSave = false;
                    }
                }
            }
        }
        if (doSave) {
            // Save MapContext
            try {
                //Create folders if needed
                File parentFolder = mapContextFile.getParentFile();
                if (!parentFolder.exists()) {
                    parentFolder.mkdirs();
                }
                mapContext.write(new FileOutputStream(mapContextFile));
            } catch (FileNotFoundException ex) {
                throw new UnsupportedOperationException(ex);
            }
            if (doSave) {
                setModified(false);
            }
        }
    }

    @Override
    public void open(ProgressMonitor progressMonitor) throws UnsupportedOperationException {
        setModified(false);
        try {
            mapContext.open(progressMonitor);
            mapContext.addPropertyChangeListener(mapContextPropertyUpdateListener);
            setListeners(mapContext.getLayerModel());
        } catch (LayerException ex) {
            LOGGER.error(I18N.tr("Unable to load the map context"), ex);
        } catch (IllegalStateException ex) {
            LOGGER.error(I18N.tr("Unable to load the map context"), ex);
        }
    }

    @Override
    public void close(ProgressMonitor progressMonitor) throws UnsupportedOperationException {
        mapContext.close(progressMonitor);
        mapContext.removePropertyChangeListener(mapContextPropertyUpdateListener);
        removeListeners(mapContext.getLayerModel());
    }

    private void setListeners(ILayer layer) {
        if (layer == null) {
            return;
        }
        layer.addLayerListener(layerUpdateListener);
        DataSource dataSource = layer.getDataSource();
        if (dataSource != null && dataSource.isEditable()) {
            try {
                layer.getDataSource().addEditionListener(sourceUpdateListener);
            } catch (UnsupportedOperationException ex) {
                LOGGER.warn(ex.getLocalizedMessage(), ex);
            }
        }
        ILayer[] layers = layer.getLayersRecursively();
        if (layers != null) {
            for (ILayer subLayer : layers) {
                setListeners(subLayer);
            }
        }
    }

    private void removeListeners(ILayer layer) {
        if (layer == null) {
            return;
        }
        layer.removeLayerListener(layerUpdateListener);
        DataSource src = layer.getDataSource();
        if (src != null && src.isEditable()) {
            try {
                src.removeEditionListener(sourceUpdateListener);
            } catch (UnsupportedOperationException ex) {
                LOGGER.warn(ex.getLocalizedMessage(), ex);
            }
        }
        ILayer[] layers = layer.getLayersRecursively();
        if (layers != null) {
            for (ILayer subLayer : layers) {
                removeListeners(subLayer);
            }
        }
    }

    @Override
    public String getId() {
        return mapId;
    }

    /**
     * Return the edited map context
     * @return 
     */
    public MapContext getMapContext() {
        return mapContext;
    }

    @Override
    public Object getObject() throws UnsupportedOperationException {
        return mapContext;
    }

    @Override
    public String getTypeId() {
        return EDITABLE_TYPE;
    }

    @Override
    public String toString() {
        return I18N.tr("MapContext - {0}", FilenameUtils.getBaseName(mapContextFile.getName()));
    }

    /**
     * Gets the editor linked to this {@code MapElement}. This is needed
     * in order to be able to retrieve all the informations about the map
     * from the {@link Toc}
     * @return
     */
    public MapEditor getMapEditor() {
        return editor;
    }

    /**
     * Sets the editor linked to this {@code MapElement}. This is needed
     * in order to be able to retrieve all the informations about the map
     * from the {@link Toc}
     * @param edit
     */
    public void setMapEditor(MapEditor edit) {
        editor = edit;
    }

    private class SourceUpdateListener implements EditionListener {
        @Override
        public void singleModification(EditionEvent e) {
            if (e.getDataSource().isModified()) {
                setModified(true);
            }
        }

        @Override
        public void multipleModification(MultipleEditionEvent me) {
            for (EditionEvent e : me.getEvents()) {
                singleModification(e);
                break;
            }
        }
    }

    /**
     * Set the editable map as modified when the layer model change
     */
    private class LayerUpdateListener extends LayerListenerAdapter {

        @Override
        public void nameChanged(LayerListenerEvent e) {
            setModified(true);
        }

        @Override
        public void visibilityChanged(LayerListenerEvent e) {
            setModified(true);
        }

        @Override
        public void styleChanged(LayerListenerEvent e) {
            setModified(true);
        }

        @Override
        public void layerAdded(LayerCollectionEvent e) {
            for (final ILayer layer : e.getAffected()) {
                setListeners(layer);
            }
            setModified(true);
        }

        @Override
        public void layerRemoved(LayerCollectionEvent e) {
            for (final ILayer layer : e.getAffected()) {
                removeListeners(layer);
            }
            setModified(true);
        }

        @Override
        public void layerMoved(LayerCollectionEvent e) {
            setModified(true);
        }

        @Override
        public void selectionChanged(SelectionEvent e) {
            // row selection is not serialised in MapContext
        }

    }
}