/*******************************************************************************
* Copyright (c) 2010 Stefan A. Tzeggai.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser Public License v2.1
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan A. Tzeggai - initial API and implementation
******************************************************************************/
package org.geopublishing.atlasStyler.swing;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Event;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import javax.jnlp.SingleInstanceListener;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.xml.transform.TransformerException;
import org.apache.commons.lang.SystemUtils;
import org.apache.log4j.Logger;
import org.geopublishing.atlasStyler.ASProps;
import org.geopublishing.atlasStyler.ASProps.Keys;
import org.geopublishing.atlasStyler.ASUtil;
import org.geopublishing.atlasStyler.AsSwingUtil;
import org.geopublishing.atlasStyler.AtlasStylerVector;
import org.geopublishing.atlasStyler.swing.importWizard.ImportWizard;
import org.geopublishing.atlasViewer.GpCoreUtil;
import org.geopublishing.atlasViewer.JNLPUtil;
import org.geopublishing.atlasViewer.swing.AVSwingUtil;
import org.geopublishing.atlasViewer.swing.Icons;
import org.geopublishing.geopublisher.GpUtil;
import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader;
import org.geotools.data.DataStore;
import org.geotools.data.DataUtilities;
import org.geotools.data.shapefile.indexed.IndexedShapefileDataStore;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.factory.Hints;
import org.geotools.map.event.MapLayerListEvent;
import org.geotools.map.event.MapLayerListListener;
import org.geotools.styling.NamedLayer;
import org.geotools.styling.SLDTransformer;
import org.geotools.styling.Style;
import org.geotools.styling.StyledLayerDescriptor;
import chrriis.dj.nativeswing.swtimpl.NativeInterface;
import de.schmitzm.geotools.MapContextManagerInterface;
import de.schmitzm.geotools.map.event.MapLayerListAdapter;
import de.schmitzm.geotools.styling.StyledFS;
import de.schmitzm.geotools.styling.StyledFeatureSourceInterface;
import de.schmitzm.geotools.styling.StyledLayerInterface;
import de.schmitzm.geotools.styling.StyledRasterInterface;
import de.schmitzm.geotools.styling.StylingUtil;
import de.schmitzm.i18n.Translation;
import de.schmitzm.io.IOUtil;
import de.schmitzm.lang.LangUtil;
import de.schmitzm.lang.ResourceProvider;
import de.schmitzm.swing.ExceptionDialog;
import de.schmitzm.swing.FileExtensionFilter;
import de.schmitzm.swing.ResourceProviderManagerFrame;
import de.schmitzm.swing.SwingUtil;
import de.schmitzm.versionnumber.ReleaseUtil;
import de.schmitzm.versionnumber.ReleaseUtil.License;
/**
* This is the main GUI for the AtlasStyler standalone. It looks like a
* condensed {@link AtlasViewer}, and its main purpose is to reach the "Style"
* item in the tools menu.
*
* @author Stefan A. Tzeggai
*
*/
public class AtlasStylerGUI extends JFrame implements SingleInstanceListener {
static {
AsSwingUtil.initAsLogging();
// Vom Benutzer hinzugefügte Übersetzungen aktivieren
ResourceProvider.setAutoResetResourceBundle(true, "Translation", true);
}
private static final long serialVersionUID = 1231321321258008431L;
final static private Logger LOGGER = LangUtil
.createLogger(AtlasStylerGUI.class);
private StylerMapView stylerMapView = null;
final private HashMap<String, StyledLayerInterface<?>> styledObjCache = new HashMap<String, StyledLayerInterface<?>>();
final private XMLCodeFrame xmlCodeFrame = new XMLCodeFrame(this,
getStylerMapView().getMapManager());
private final HashMap<String, DataStore> openDatastores = new HashMap<String, DataStore>();
/**
* This is the default constructor
*/
public AtlasStylerGUI() {
LOGGER.info("Starting " + AtlasStylerGUI.class.getSimpleName() + "... "
+ ReleaseUtil.getVersionInfo(GpCoreUtil.class));
// Output information about the LGPL license
LOGGER.info(ReleaseUtil.getLicense(License.LGPL3, "AtlasStyler"));
LOGGER.debug("checking for correct permissions on tmpdir");
GpUtil.checkAndResetTmpDir("/var/tmp");
System.setProperty("file.encoding", "UTF-8");
LOGGER.debug("checking for correct permissions on tmpdir");
GpUtil.checkAndResetTmpDir("/var/tmp");
JNLPUtil.registerAsSingleInstance(AtlasStylerGUI.this, true);
initialize();
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
exitAS(0);
}
@Override
public void windowClosing(WindowEvent e) {
exitAS(0);
}
});
/**
* Setting a nice AtlasStylerGUI icons
*/
List<Image> icons = new ArrayList<Image>(2);
ClassLoader cl = AsSwingUtil.class.getClassLoader();
final String imagePackageName = "icons/";
icons.add(new ImageIcon(cl.getResource(imagePackageName
+ "as_icon16.png")).getImage());
icons.add(new ImageIcon(cl.getResource(imagePackageName
+ "as_icon32.png")).getImage());
icons.add(new ImageIcon(cl.getResource(imagePackageName
+ "as_icon64.png")).getImage());
setIconImages(icons);
AVSwingUtil.initEPSG(AtlasStylerGUI.this);
}
private void initialize() {
this.setSize(750, 510);
this.setJMenuBar(createMenuBar());
this.setContentPane(getJContentPane());
String AtlasStyler_MainWindowTitle = "AtlasStyler "
+ ReleaseUtil.getVersionInfo(GpCoreUtil.class);
this.setTitle(AtlasStyler_MainWindowTitle);
// In Xubuntu (OS-Geo Live DVD) the JFrame otherwise is hidden behind
// the top-bar.
SwingUtil.centerFrameOnScreen(this);
}
/**
* Creates a nre {@link JMenuBar} instance
*/
private JMenuBar createMenuBar() {
JMenuBar jMenuBar = new JMenuBar();
JMenu fileMenu = new JMenu(AsSwingUtil.R("MenuBar.FileMenu"));
jMenuBar.add(fileMenu);
{ // Import WIzard
JMenuItem mi = new JMenuItem(new AbstractAction(
AsSwingUtil.R("MenuBar.FileMenu.ImportWizard")) {
@Override
public void actionPerformed(ActionEvent e) {
ImportWizard.showWizard(AtlasStylerGUI.this,
AtlasStylerGUI.this);
}
});
mi.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_I,
Event.CTRL_MASK, true));
fileMenu.add(mi);
}
fileMenu.add(SwingUtil.createChangeLog4JLevelJMenu());
/**
* MenuItem to create a new language
*/
JMenuItem manageLanguageJMenuitem = new JMenuItem(new AbstractAction(
ASUtil.R("TranslateSoftwareDialog.Title"),
Icons.ICON_FLAGS_SMALL) {
@Override
public void actionPerformed(ActionEvent e) {
String resPath = IOUtil.escapePath(System
.getProperty("user.home")
+ File.separator
+ ".Geopublishing");
ResourceProviderManagerFrame manLanguagesFrame = new ResourceProviderManagerFrame(
AtlasStylerGUI.this, true, AsSwingUtil.R(
"TranslateSoftwareDialog.Explanation.Html",
resPath, SystemUtils.IS_OS_WINDOWS ? "bat"
: "sh"));
manLanguagesFrame.setRootPath(new File(resPath));
manLanguagesFrame.setTitle(ASUtil
.R("TranslateSoftwareDialog.Title"));
manLanguagesFrame.setPreferredSize(new Dimension(780, 450));
manLanguagesFrame.setVisible(true);
}
});
fileMenu.add(manageLanguageJMenuitem);
AbstractAction optionsButton = new AbstractAction(
AtlasStylerVector.R("Options.ButtonLabel")) {
@Override
public void actionPerformed(ActionEvent e) {
new ASOptionsDialog(AtlasStylerGUI.this, AtlasStylerGUI.this);
}
};
fileMenu.add(optionsButton);
{ // Exit
JMenuItem mi = new JMenuItem(
new AbstractAction(
GpCoreUtil
.R("AtlasViewer.FileMenu.ExitMenuItem.exit_application"),
Icons.ICON_EXIT_SMALL) {
@Override
public void actionPerformed(ActionEvent e) {
exitAS(0);
}
});
fileMenu.add(mi);
}
return jMenuBar;
}
/**
* Closes the GUI of the stand-alone {@link AtlasStylerGUI}
*
* @param exitCode
* Code to pass to System.exit()
*/
protected void exitAS(int exitCode) {
/*
* Ask the use to save the changed SLDs
*/
List<StyledLayerInterface<?>> styledObjects = getMapManager()
.getStyledObjects();
for (StyledLayerInterface<?> styledObj : styledObjects) {
// if (styledObj instanceof StyledFS) {
// StyledFS stedFS = (StyledFS) styledObj;
askToSaveSld(styledObj);
// } else {
// JOptionPane
// .showMessageDialog(
// AtlasStylerGUI.this,
// "The type of "
// + styledObj.getTitle()
// + " is not recognized. That must be a bug. Sorry."); // i8n
// continue;
// }
}
dispose();
for (DataStore ds : openDatastores.values()) {
if (ds != null)
ds.dispose();
}
openDatastores.clear();
/*
* Unregister from JNLP SingleInstanceService
*/
JNLPUtil.registerAsSingleInstance(AtlasStylerGUI.this, false);
// Store the Logging Level in ~/.AtlasStyler/atlasStyler.properties
{
ASProps.set(Keys.logLevel, Logger.getRootLogger().getLevel()
.toString());
ASProps.store();
}
System.exit(exitCode);
}
/**
* This method initializes jContentPane
*
* @return javax.swing.JPanel
*/
private JPanel getJContentPane() {
JPanel jContentPane = new JPanel(new BorderLayout());
jContentPane.add(getStylerMapView(), BorderLayout.CENTER);
jContentPane.add(getJToolBar(), BorderLayout.NORTH);
return jContentPane;
}
public StylerMapView getStylerMapView() {
if (stylerMapView == null) {
stylerMapView = new StylerMapView(this);
stylerMapView.getMapManager().addMapLayerListListener(
new MapLayerListListener() {
public void layerAdded(MapLayerListEvent event) {
}
public void layerChanged(MapLayerListEvent event) {
}
public void layerMoved(MapLayerListEvent event) {
}
public void layerRemoved(MapLayerListEvent event) {
String id = event.getLayer().getTitle();
LOGGER.debug("layer id=" + id + " removed");
askToSaveSld(styledObjCache.get(id));
styledObjCache.remove(id);
// Dispose the datastore when removing the layer
DataStore openDs = openDatastores.get(id);
if (openDs != null)
openDs.dispose();
}
@Override
public void layerPreDispose(MapLayerListEvent event)
{
// TODO Auto-generated method stub
}
});
}
return stylerMapView;
}
/**
* A very basic dialog to asking the user to store the .SLD file for a
* layer.
*
* @param styledLayer
* The {@link StyledFS} that links to the geodata.
*/
protected void askToSaveSld(StyledLayerInterface<?> styledLayer) {
File sldFile = styledLayer.getSldFile();
// Only ask to save if the style has changed
if (!StylingUtil.isStyleDifferent(styledLayer.getStyle(), sldFile))
return;
if (sldFile == null) {
// There is no .SLD set so far. Lets ask the user where to save it.
styledLayer.setSldFile(sldFile);
// TODO no SLD, ask the user!
throw new RuntimeException(
"Not yet implemented. Please contact the authors.");
}
if (!SwingUtil.askYesNo(
AtlasStylerGUI.this,
ASUtil.R("AtlasStylerGUI.saveToSLDFileQuestion",
styledLayer.getTitle(), IOUtil.escapePath(sldFile))))
return;
Style style = styledLayer.getStyle();
if (style == null) {
AVSwingUtil.showMessageDialog(AtlasStylerGUI.this, "The Style for "
+ styledLayer.getTitle()
+ " is null. That must be a bug. Not saving.");
return;
}
new AtlasStylerSaveLayerToSLDAction(this, styledLayer)
.actionPerformed(null);
}
/**
* This method initializes jToolBar
*
* @return javax.swing.JToolBar
*/
private JToolBar getJToolBar() {
JToolBar jToolBar = new JToolBar();
jToolBar.setFloatable(false);
AbstractAction importWiazrdAction = new AbstractAction(
AtlasStylerVector.R("MenuBar.FileMenu.ImportWizard")) {
@Override
public void actionPerformed(ActionEvent e) {
ImportWizard.showWizard(AtlasStylerGUI.this,
AtlasStylerGUI.this);
}
};
importWiazrdAction.putValue(Action.LONG_DESCRIPTION,
KeyStroke.getKeyStroke(KeyEvent.VK_I, Event.CTRL_MASK, true));
jToolBar.add(importWiazrdAction);
jToolBar.add(getJTButtonShowXML());
jToolBar.add(getJTButtonExportAsSLD());
return jToolBar;
}
public JToggleButton getJTButtonShowXML() {
final JToggleButton jButtonShowXML = new JToggleButton(
ASUtil.R("AtlasStylerGUI.toolbarButton.show_xml"));
jButtonShowXML.setSelected(false);
jButtonShowXML.addActionListener(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
boolean anAus = !xmlCodeFrame.isVisible();
xmlCodeFrame.setVisible(anAus);
// jButtonShowXML.setSelected(anAus);
xmlCodeFrame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
jButtonShowXML.setSelected(false);
}
});
}
});
return jButtonShowXML;
}
/**
* A button to export all layers in form of one SLD XML file (starting a
* StyledLayerDescriptor tag)
*/
private JButton getJTButtonExportAsSLD() {
final JButton jButtonExportAsSLD = new JButton(
ASUtil.R("AtlasStylerGUI.toolbarButton.exportSLD"));
jButtonExportAsSLD.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
File saveDir = null;
String lastPath = ASProps
.get(ASProps.Keys.lastExportDirectory);
if (lastPath != null)
saveDir = new File(lastPath);
File exportSLDFile = AsSwingUtil.chooseFileSave(
AtlasStylerGUI.this,
saveDir,
ASUtil.R("AtlasStylerGUI.saveStyledLayerDescFileDialogTitle"),
new FileExtensionFilter(ASUtil.FILTER_SLD));
// File exportSLDFile = chooser.getSelectedFile();
if (exportSLDFile == null
// || result != JFileChooser.APPROVE_OPTION
|| exportSLDFile.isDirectory())
return;
ASProps.set(ASProps.Keys.lastExportDirectory, exportSLDFile
.getParentFile().getAbsolutePath());
// If the file exists, the user will be asked about
// overwriting it
if (exportSLDFile.exists()) {
if (!AVSwingUtil
.askOKCancel(
AtlasStylerGUI.this,
AtlasStylerVector
.R("AtlasStylerGUI.saveStyledLayerDescFileDialogTitle.OverwriteQuestion",
exportSLDFile.getName())))
return;
}
// Evt. wird .sld angehangen.
String filenamelc = exportSLDFile.getName().toLowerCase();
if (!filenamelc.endsWith(".sld")
&& !filenamelc.endsWith(".xml")
&& !filenamelc.endsWith(".se")) {
exportSLDFile = new File(exportSLDFile.getParentFile(),
filenamelc + ".sld");
JOptionPane.showMessageDialog(AtlasStylerGUI.this,
AsSwingUtil.R(
"AtlasStylerGUI.FileNameChangeTo.msg",
exportSLDFile.getName()));
}
// // Export
// Charset charset = Charset.forName("UTF-8");
StyledLayerDescriptor sldTag = CommonFactoryFinder
.getStyleFactory(null)
.createStyledLayerDescriptor();
/*******
* Export aller Styles als ein SLD Tag
*/
for (StyledLayerInterface smi : getMapManager()
.getStyledObjects()) {
try {
// if (!(smi instanceof StyledFS)) {
// LOGGER.info("Ein Layer aus dem MapContextManagerInterface ist kein StyledFeatureSourceInterface. Es wird ignoriert: "
// + smi.getTitle());
// continue;
// }
String name = null;
if (smi instanceof StyledFeatureSourceInterface) {
StyledFeatureSourceInterface featureGeoObj = (StyledFeatureSourceInterface) smi;
name = featureGeoObj.getGeoObject().getSchema()
.getTypeName();
} else if (smi instanceof StyledRasterInterface) {
StyledRasterInterface ri = (StyledRasterInterface) smi;
if (ri.getGeoObject() instanceof AbstractGridCoverage2DReader) {
AbstractGridCoverage2DReader agcr = (AbstractGridCoverage2DReader) ri
.getGeoObject();
if (agcr.getSource() instanceof File) {
File rfile = (File) agcr.getSource();
name = IOUtil.changeFileExt(rfile, "")
.getName();
} else
name = "someraster";
} else
name = "someraster";
}
NamedLayer namedLayer = CommonFactoryFinder
.getStyleFactory(null).createNamedLayer();
namedLayer.setName(name);
namedLayer.addStyle(smi.getStyle());
sldTag.addStyledLayer(namedLayer);
} catch (Exception e1) {
ExceptionDialog.show(AtlasStylerGUI.this, e1);
}
}
Writer w = null;
final SLDTransformer aTransformer = new SLDTransformer();
// if (charset != null) {
// aTransformer.setEncoding(charset);
// }
aTransformer.setIndentation(2);
final String xml = aTransformer.transform(sldTag);
w = new FileWriter(exportSLDFile);
w.write(xml);
w.close();
} catch (IOException e1) {
ExceptionDialog.show(AtlasStylerGUI.this, e1);
} catch (TransformerException e2) {
ExceptionDialog.show(AtlasStylerGUI.this, e2);
}
}
});
/**
* Activate the button when more than one layer is available
*/
jButtonExportAsSLD.setEnabled(getMapManager().getMapContext()
.getLayerCount() > 0);
getMapManager().addMapLayerListListener(new MapLayerListAdapter() {
@Override
public void layerRemoved(MapLayerListEvent arg0) {
jButtonExportAsSLD.setEnabled(getMapManager().getMapContext()
.getLayerCount() > 0);
}
@Override
public void layerAdded(MapLayerListEvent arg0) {
jButtonExportAsSLD.setEnabled(true);
}
});
return jButtonExportAsSLD;
}
/**
* Adds a layer to the map context to style it.
*/
public boolean addLayer(StyledLayerInterface<?> styledFS) {
if (styledFS.getStyle() == null) {
// Einen default Style erstellen
{
// AVSwingUtil.showMessageDialog(this, AtlasStyler.R(
// "AtlasStylerGUI.importVectorLayerNoSLD", styledFS
// .getSldFile().getName()));
styledFS.setStyle(AsSwingUtil.createDefaultStyle(styledFS));
}
}
styledObjCache.put(styledFS.getId(), styledFS);
return getMapManager().addStyledLayer(styledFS);
}
public MapContextManagerInterface getMapManager() {
return getStylerMapView().getMapManager();
}
/**
* AtlasStylerGUI main method.
*
* @param args
*/
public static void main(final String[] args) throws IOException {
// System.out.println("Classpath: "+System.getProperty("java.class.path"));
// Set the locale for running the application
try {
if (!ASProps.get(Keys.language, "system")
.equalsIgnoreCase("system")) {
Translation.setDefaultLocale(new Locale(ASProps.get(
Keys.language, "en")));
}
} catch (Exception e) {
LOGGER.error(
"Could not set locale to " + ASProps.get(Keys.language), e);
ExceptionDialog.show(e);
}
// Setup the AXIS order property TODO in settings GUI!
Hints.putSystemDefault(Hints.FORCE_LONGITUDE_FIRST_AXIS_ORDER,
ASProps.get(Keys.FORCE_LONGITUDE_FIRST_AXIS_ORDER, true));
/**
* Check for addFix arguments. If found, the GUI will not start,
*/
boolean addedIndexes = checkFixIndexCreation(args);
if (!addedIndexes) {
try {
NativeInterface.open();
} catch (Throwable e) {
LOGGER.warn("Couldn't initialize the SWT subsystem. Trying fallback to Swing.",e);
}
SwingUtilities.invokeLater(new Runnable() {
public void run() {
AtlasStylerGUI asg = new AtlasStylerGUI();
// TODO Switch to Apache Commons-CLI
if (args.length != 0) {
LOGGER.warn("Sorry, command line arguments are ignored in this version. Please contact geopublishing.org if you need this function.");
}
asg.setVisible(true);
}
});
try {
NativeInterface.runEventPump();
} catch (Throwable e) {
LOGGER.warn("Couldn't initialize the SWT subsystem. Trying fallback to Swing.",e);
}
} else {
LOGGER.info("Not starting GUI because command line parameter addFix was passed");
System.out
.println("Not starting GUI because command line parameter addFix was passed");
}
}
/**
* This method is evaluating command line arguments to look for
* "addFix=FILE" paramters. If at least one "addFix=" parameter is passen,
* the method returns <code>true</code> and the application is expected not
* to start a GUI.
*/
private static boolean checkFixIndexCreation(String[] args) {
boolean fixParamFound = false;
if (args.length != 0) {
for (String param : args) {
// Allows "addFix=/Eigene Dateien/Meine Dateien" etc...
if ((param.startsWith("\"")) && (param.endsWith("\""))) {
param = param.substring(1, param.length() - 1);
}
// Here we just handle addFix paramters (for now)
if (!param.startsWith("addFix="))
continue;
param = param.substring(7);
// Remember that a paramter was interpreted and the mnethod has
// to return true
fixParamFound = true;
final File fileParamter = new File(param);
if (!fileParamter.exists()) {
LOGGER.warn("Not understanding " + param
+ " as file to add an index.");
continue;
}
if (!fileParamter.canWrite()) {
LOGGER.warn("Can't write to " + param
+ " due to missing permissions, skipping.");
continue;
}
// Starting the .fix creation
final URL shpUrl = DataUtilities.fileToURL(fileParamter);
try {
final IndexedShapefileDataStore ds = new IndexedShapefileDataStore(
shpUrl);
try {
ds.createSpatialIndex();
} catch (final IOException e) {
LOGGER.warn("", e);
} finally {
ds.dispose();
}
LOGGER.info("Added .fix to " + fileParamter);
} catch (final MalformedURLException e) {
LOGGER.warn("Error adding a .fix to " + param, e);
}
}
}
return fixParamFound;
}
/**
* Called via SingleInstanceListener / SingleInstanceService. Does nothing
* except requesting the focus for the given application.
*/
@Override
public void newActivation(String[] arg0) {
LOGGER.info("A second instance of AtlasViewer has been started.. The single instance if requesting focus now...");
requestFocus();
toFront();
}
public void addOpenDatastore(String layerId, DataStore ds) {
openDatastores.put(layerId, ds);
}
}