/*************************************************** * * cismet GmbH, Saarbruecken, Germany * * ... and it just works. * ****************************************************/ package de.cismet.commons.cismap.io; import com.vividsolutions.jts.geom.Geometry; import org.apache.log4j.Logger; import org.jdesktop.swingx.JXErrorPane; import org.jdesktop.swingx.error.ErrorInfo; import org.jdom.Element; import org.openide.DialogDisplayer; import org.openide.WizardDescriptor; import org.openide.util.ImageUtilities; import org.openide.util.Lookup; import org.openide.util.NbBundle; import java.awt.Component; import java.awt.Dialog; import java.awt.EventQueue; import java.awt.Frame; import java.awt.event.ActionEvent; import java.io.File; import java.io.PrintWriter; import java.io.StringWriter; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.ResourceBundle; import java.util.logging.Level; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JComponent; import de.cismet.cismap.commons.Crs; import de.cismet.cismap.commons.features.AbstractNewFeature.geomTypes; import de.cismet.cismap.commons.features.Feature; import de.cismet.cismap.commons.features.PureNewFeature; import de.cismet.cismap.commons.gui.MappingComponent; import de.cismet.cismap.commons.interaction.CismapBroker; import de.cismet.cismap.commons.raster.wms.simple.SimpleWmsGetMapUrl; import de.cismet.commons.cismap.io.converters.AbstractGeometryFromTextConverter; import de.cismet.commons.cismap.io.converters.GeometryConverter; import de.cismet.commons.cismap.io.converters.TextToGeometryConverter; import de.cismet.commons.converter.ConversionException; import de.cismet.commons.converter.Converter; import de.cismet.commons.gui.wizard.WizardUtils; import de.cismet.commons.gui.wizard.converter.AbstractConverterChooseWizardPanel; import de.cismet.commons.gui.wizard.converter.ConverterPreselectionMode; import de.cismet.tools.configuration.Configurable; import de.cismet.tools.configuration.NoWriteError; import de.cismet.tools.gui.StaticSwingTools; import de.cismet.tools.gui.WaitingDialogThread; /** * DOCUMENT ME! * * @author martin.scholl@cismet.de * @version 1.0 */ public final class AddGeometriesToMapWizardAction extends AbstractAction implements Configurable { //~ Static fields/initializers --------------------------------------------- public static final String PROP_AVAILABLE_CONVERTERS = "__prop_available_converters__"; // NOI18N public static final String PROP_INPUT_FILE = "__prop_input_file__"; // NOI18N public static final String PROP_CURRENT_CRS = "__prop_current_epsg_code__"; // NOI18N public static final String PROP_PREVIEW_GETMAP_URL = "__prop_preview_getmap_url__"; // NOI18N public static final String PROP_CONVERTER_PRESELECT_MODE = "__prop_converter_preselect_mode__"; // NOI18N public static final String CONF_SECTION = "addGeometriesToMapWizardAction"; // NOI18N public static final String CONF_CONV_PRESELECT = "converterPreselectionMode"; // NOI18N public static final String CONF_PREVIEW_GETMAP_URL = "previewGetMapUrl"; // NOI18N public static final String CONF_GEOM_FROM_TEXT_CONV_DECIMAL_SEP = "geometryFromTextConverterDecimalSeparator"; // NOI18N /** LOGGER. */ private static final transient Logger LOG = Logger.getLogger(AddGeometriesToMapWizardAction.class); //~ Instance fields -------------------------------------------------------- private transient Converter selectedConverter; private transient WizardDescriptor.Panel<WizardDescriptor>[] panels; private transient ConverterPreselectionMode converterPreselectionMode; private transient String previewGetMapUrl; private transient File inputFile; private transient String inputData; //~ Constructors ----------------------------------------------------------- /** * Creates a new AddGeometriesToMapWizardAction object. */ public AddGeometriesToMapWizardAction() { super( "", // NOI18N ImageUtilities.loadImageIcon( AddGeometriesToMapWizardAction.class.getPackage().getName().replace('.', '/') + "/new_geom_wiz_22.png", // NOI18N false)); putValue( Action.SHORT_DESCRIPTION, NbBundle.getMessage( AddGeometriesToMapWizardAction.class, "AddGeometriesToMapWizardAction.<init>.action.shortDescription")); // NOI18N setConverterPreselectionMode(getDefaultConverterPreselectionMode()); } //~ Methods ---------------------------------------------------------------- /** * DOCUMENT ME! * * @return DOCUMENT ME! */ @SuppressWarnings("unchecked") private WizardDescriptor.Panel<WizardDescriptor>[] getPanels() { assert EventQueue.isDispatchThread() : "can only be called from EDT"; // NOI18N if (panels == null) { final AddGeometriesToMapChooseConverterWizardPanel chooseConvPanel = new AddGeometriesToMapChooseConverterWizardPanel(); try { final ResourceBundle customBundle = ResourceBundle.getBundle(this.getClass().getPackage().getName() + ".ConverterPanelL10N"); // NOI18N chooseConvPanel.setResourceBundle(customBundle); } catch (final Exception e) { LOG.warn("cannot set custom converter bundle", e); // NOI18N } panels = new WizardDescriptor.Panel[] { new AddGeometriesToMapEnterDataWizardPanel(), chooseConvPanel, new AddGeometriesToMapPreviewWizardPanel() }; final String[] steps = new String[panels.length]; for (int i = 0; i < panels.length; i++) { final Component c = panels[i].getComponent(); // Default step name to component name of panel. Mainly useful // for getting the name of the target chooser to appear in the // list of steps. steps[i] = c.getName(); if (c instanceof JComponent) { // assume Swing components final JComponent jc = (JComponent)c; // Sets step number of a component jc.putClientProperty(WizardDescriptor.PROP_CONTENT_SELECTED_INDEX, Integer.valueOf(i)); // Sets steps names for a panel jc.putClientProperty(WizardDescriptor.PROP_CONTENT_DATA, steps); // Turn on subtitle creation on each step jc.putClientProperty(WizardDescriptor.PROP_AUTO_WIZARD_STYLE, Boolean.TRUE); // Show steps on the left side with the image on the // background jc.putClientProperty(WizardDescriptor.PROP_CONTENT_DISPLAYED, Boolean.TRUE); // Turn on numbering of all steps jc.putClientProperty(WizardDescriptor.PROP_CONTENT_NUMBERED, Boolean.TRUE); } } } return panels; } @Override public void actionPerformed(final ActionEvent e) { final WizardDescriptor wizard = new WizardDescriptor(getPanels()); wizard.setTitleFormat(new MessageFormat("{0}")); // NOI18N wizard.setTitle(NbBundle.getMessage( AddGeometriesToMapWizardAction.class, "AddGeometriesToMapWizardAction.actionPerformed(ActionEvent).wizard.title")); // NOI18N WizardUtils.setCustomButtonText( wizard, WizardDescriptor.FINISH_OPTION, NbBundle.getMessage( AddGeometriesToMapWizardAction.class, "AddGeometriesToMapWizardAction.actionPerformed(ActionEvent).wizard.finishButton.text")); // NOI18N final Collection<? extends TextToGeometryConverter> availableConverters = Lookup.getDefault() .lookupAll(TextToGeometryConverter.class); final ConverterPreselectionMode preselectionMode; if (ConverterPreselectionMode.DEFAULT == getConverterPreselectionMode()) { preselectionMode = getDefaultConverterPreselectionMode(); } else { preselectionMode = getConverterPreselectionMode(); } wizard.putProperty(AddGeometriesToMapChooseConverterWizardPanel.PROP_CONVERTER, selectedConverter); wizard.putProperty(PROP_PREVIEW_GETMAP_URL, getPreviewGetMapUrl()); wizard.putProperty(PROP_AVAILABLE_CONVERTERS, new ArrayList<Converter>(availableConverters)); wizard.putProperty(PROP_CURRENT_CRS, CismapBroker.getInstance().getSrs()); wizard.putProperty(PROP_CONVERTER_PRESELECT_MODE, preselectionMode); wizard.putProperty(PROP_INPUT_FILE, getInputFile()); wizard.putProperty(AddGeometriesToMapEnterDataWizardPanel.PROP_COORDINATE_DATA, getInputData()); final Frame parent = StaticSwingTools.getParentFrame(CismapBroker.getInstance().getMappingComponent()); final Dialog dialog = DialogDisplayer.getDefault().createDialog(wizard); dialog.pack(); dialog.setLocationRelativeTo(parent); dialog.setVisible(true); dialog.toFront(); // after wizard execution the input file and data is cleared from the action so that it won't be reused // NOTE: this can be changed so that the action may remember previous entries setInputFile(null); setInputData(null); if (wizard.getValue() == WizardDescriptor.FINISH_OPTION) { // remember the selected converter if ((ConverterPreselectionMode.SESSION_MEMORY == converterPreselectionMode) || (ConverterPreselectionMode.PERMANENT_MEMORY == converterPreselectionMode) || (ConverterPreselectionMode.CONFIGURE_AND_MEMORY == converterPreselectionMode)) { setSelectedConverter((Converter)wizard.getProperty( AddGeometriesToMapChooseConverterWizardPanel.PROP_CONVERTER)); } else { setSelectedConverter(null); } final WaitingDialogThread<Geometry> wdt = new WaitingDialogThread<Geometry>( parent, true, NbBundle.getMessage( AddGeometriesToMapWizardAction.class, "AddGeometriesToMapWizardAction.actionPerformed(ActionEvent).waitingDialogThread.message"), // NOI18N null, 50) { @Override @SuppressWarnings("unchecked") protected Geometry doInBackground() throws Exception { Geometry geometry = (Geometry)wizard.getProperty( AddGeometriesToMapPreviewWizardPanel.PROP_GEOMETRY); if (geometry == null) { final Converter converter = (Converter)wizard.getProperty( AbstractConverterChooseWizardPanel.PROP_CONVERTER); final Object data = wizard.getProperty( AddGeometriesToMapEnterDataWizardPanel.PROP_COORDINATE_DATA); final Crs crs = (Crs)wizard.getProperty(AddGeometriesToMapWizardAction.PROP_CURRENT_CRS); assert converter instanceof GeometryConverter : "illegal wizard initialisation"; // NOI18N final GeometryConverter geomConverter = (GeometryConverter)converter; geometry = geomConverter.convertForward(data, crs.getCode()); } return geometry; } @Override protected void done() { try { final Geometry geom = get(); final PureNewFeature feature = new PureNewFeature(geom); feature.setGeometryType(getGeomType(geom)); feature.setEditable(true); final MappingComponent map = CismapBroker.getInstance().getMappingComponent(); map.getFeatureCollection().addFeature(feature); map.getFeatureCollection().holdFeature(feature); // fixed extent means, don't move map at all if (!map.isFixedMapExtent()) { map.zoomToAFeatureCollection(Arrays.asList((Feature)feature), true, map.isFixedMapScale()); } } catch (final Exception ex) { final ErrorInfo errorInfo; final StringWriter stacktraceWriter = new StringWriter(); ex.printStackTrace(new PrintWriter(stacktraceWriter)); if (ex instanceof ConversionException) { errorInfo = new ErrorInfo( NbBundle.getMessage( AddGeometriesToMapWizardAction.class, "AddGeometriesToMapWizardAction.actionPerformed(ActionEvent).waitingDialogThread.conversionError.title"), // NOI18N NbBundle.getMessage( AddGeometriesToMapWizardAction.class, "AddGeometriesToMapWizardAction.actionPerformed(ActionEvent).waitingDialogThread.conversionError.message"), // NOI18N stacktraceWriter.toString(), "WARNING", // NOI18N ex, Level.WARNING, null); } else { errorInfo = new ErrorInfo( NbBundle.getMessage( AddGeometriesToMapWizardAction.class, "AddGeometriesToMapWizardAction.actionPerformed(ActionEvent).waitingDialogThread.genericError.title"), // NOI18N NbBundle.getMessage( AddGeometriesToMapWizardAction.class, "AddGeometriesToMapWizardAction.actionPerformed(ActionEvent).waitingDialogThread.genericError.message"), // NOI18N stacktraceWriter.toString(), "WARNING", // NOI18N ex, Level.WARNING, null); } JXErrorPane.showDialog(parent, errorInfo); } } // cannot map to ellipse private geomTypes getGeomType(final Geometry geom) { final String jtsGeomType = geom.getGeometryType(); // JTS v1.12 strings if ("Polygon".equals(jtsGeomType)) { // NOI18N if (geom.isRectangle()) { return geomTypes.RECTANGLE; } else { return geomTypes.POLYGON; } } else if ("Point".equals(jtsGeomType)) { // NOI18N return geomTypes.POINT; } else if ("LineString".equals(jtsGeomType)) { // NOI18N return geomTypes.LINESTRING; } else if ("MultiPolygon".equals(jtsGeomType)) { // NOI18N return geomTypes.MULTIPOLYGON; } else { return geomTypes.UNKNOWN; } } }; // FIXME: the WaitingDialogThread only works properly when using start, thus cannot be put in an executor wdt.start(); } } @Override public void configure(final Element parent) { // only properties that are directly changable by the user shall be configured here, currently there are non } @Override public void masterConfigure(final Element parent) { if (parent == null) { // no configuration section present, simply leave return; } final Element actionConfigElement = parent.getChild(CONF_SECTION); // NOI18N if (actionConfigElement == null) { // no configuration section present, simply leave return; } final Element convPreselectModeElement = actionConfigElement.getChild(CONF_CONV_PRESELECT); if (convPreselectModeElement == null) { setConverterPreselectionMode(ConverterPreselectionMode.DEFAULT); } else { final String convPreselectModeString = convPreselectModeElement.getText(); try { final ConverterPreselectionMode convPreselectMode = ConverterPreselectionMode.valueOf( convPreselectModeString); setConverterPreselectionMode(convPreselectMode); } catch (final IllegalArgumentException e) { LOG.warn("illegal value for " + CONF_CONV_PRESELECT + ", configuring DEFAULT", e); // NOI18N setConverterPreselectionMode(ConverterPreselectionMode.DEFAULT); } } final Element convPreviewGetMapUrlElement = actionConfigElement.getChild(CONF_PREVIEW_GETMAP_URL); if (convPreviewGetMapUrlElement == null) { setPreviewGetMapUrl(null); } else { setPreviewGetMapUrl(convPreviewGetMapUrlElement.getText().trim()); } final Element convDecimalSeparator = actionConfigElement.getChild(CONF_GEOM_FROM_TEXT_CONV_DECIMAL_SEP); if (convDecimalSeparator != null) { System.setProperty(AbstractGeometryFromTextConverter.SYS_PROP_DECIMAL_SEP, convDecimalSeparator.getText()); } } @Override public Element getConfiguration() throws NoWriteError { final Element sectionElement = new Element(CONF_SECTION); final Element convPreselectModeElement = new Element(CONF_CONV_PRESELECT); convPreselectModeElement.setText(getConverterPreselectionMode().toString()); final Element convPreviewGetMapUrlElement = new Element(CONF_PREVIEW_GETMAP_URL); convPreviewGetMapUrlElement.setText(getPreviewGetMapUrl()); final Element convDecimalSeparator = new Element(CONF_GEOM_FROM_TEXT_CONV_DECIMAL_SEP); convDecimalSeparator.setText(System.getProperty(AbstractGeometryFromTextConverter.SYS_PROP_DECIMAL_SEP)); // NOI18N sectionElement.addContent(convPreselectModeElement); sectionElement.addContent(convPreviewGetMapUrlElement); sectionElement.addContent(convDecimalSeparator); return sectionElement; } /** * Gets the current <code>ConverterPreselectionMode</code> that will be used in the wizard. * * @return the current <code>ConverterPreselectionMode</code> that will be used in the wizard */ public ConverterPreselectionMode getConverterPreselectionMode() { return converterPreselectionMode; } /** * Sets the <code>ConverterPreselectionMode</code> that will be used in the wizard. * * @param converterPreselectionMode the <code>ConverterPreselectionMode</code> to use * * @throws IllegalArgumentException if the given <code>ConverterPreselectionMode</code> is not supported by the * wizard (currently CONFIGURE, CONFIGURE_AND_MEMORY and PREMANENT_MEMORY) */ public void setConverterPreselectionMode(final ConverterPreselectionMode converterPreselectionMode) { switch (converterPreselectionMode) { case CONFIGURE: // fall-through case CONFIGURE_AND_MEMORY: // fall-through case PERMANENT_MEMORY: { throw new IllegalArgumentException("mode not supported yet: " + converterPreselectionMode); // NOI18N } default: { this.converterPreselectionMode = converterPreselectionMode; } } } /** * Gets the default <code>ConverterPreselectionMode</code>, currently <i>SESSION_MEMORY</i>. * * @return the default <code>ConverterPreselectionMode</code>, currently <i>SESSION_MEMORY</i> */ public ConverterPreselectionMode getDefaultConverterPreselectionMode() { return ConverterPreselectionMode.SESSION_MEMORY; } /** * Gets the current map preview url that will be used by the wizard as a background layer for the newly created * geometry. The preview url shall be a string that can be used with {@link SimpleWmsGetMapUrl}. * * @return the current map preview url */ public String getPreviewGetMapUrl() { return previewGetMapUrl; } /** * Sets the map preview url that will be used by the wizard as a background layer for the newly created geometry. * The preview url shall be a string that can be used with {@link SimpleWmsGetMapUrl}. * * @param previewGetMapUrl the new map preview url */ public void setPreviewGetMapUrl(final String previewGetMapUrl) { this.previewGetMapUrl = previewGetMapUrl; } /** * Gets the currently selected converter that will be used for the next wizard invocation. * * @return the currently selected converter that will for the next wizard invocation */ public Converter getSelectedConverter() { return selectedConverter; } /** * Sets the selected converter that will be used for the next wizard invocation. * * @param selectedConverter the new selected converter */ public void setSelectedConverter(final Converter selectedConverter) { this.selectedConverter = selectedConverter; } /** * Gets the input file that will be used for the next wizard invocation. * * @return the input file that will be used for the next wizard invocation */ public File getInputFile() { return inputFile; } /** * Sets input file that may be used for the next wizard invocation. After an invocation of the wizard the file is * cleared again. * * @param inputFile the input file to be used for the next wizard invocation */ public void setInputFile(final File inputFile) { this.inputFile = inputFile; } /** * Gets the input data that will be used for the next wizard invocation. * * @return the input data that will be used for the next wizard invocation */ public String getInputData() { return inputData; } /** * Sets input data that may be used for the next wizard invocation. After an invocation of the wizard the data is * cleared again. * * @param inputData the input data to be used for the next wizard invocation */ public void setInputData(final String inputData) { this.inputData = inputData; } }