//$HeadURL: svn+ssh://aschmitz@wald.intevation.org/deegree/base/trunk/resources/eclipse/files_template.xml $ /*---------------------------------------------------------------------------- This file is part of deegree, http://deegree.org/ Copyright (C) 2001-2012 by: - Department of Geography, University of Bonn - and - lat/lon GmbH - This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Contact information: lat/lon GmbH Aennchenstr. 19, 53177 Bonn Germany http://lat-lon.de/ Department of Geography, University of Bonn Prof. Dr. Klaus Greve Postfach 1147, 53001 Bonn Germany http://www.geographie.uni-bonn.de/deegree/ e-mail: info@deegree.org ----------------------------------------------------------------------------*/ package org.deegree.igeo.views.swing.georef; import static java.awt.GridBagConstraints.BOTH; import static java.awt.GridBagConstraints.CENTER; import static java.awt.GridBagConstraints.NONE; import static java.util.Collections.singletonList; import static javax.swing.BorderFactory.createTitledBorder; import static org.deegree.igeo.i18n.Messages.get; import java.awt.Container; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.prefs.Preferences; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JToggleButton; import javax.xml.bind.JAXBElement; import org.apache.poi.util.IOUtils; import org.deegree.graphics.sld.SLDFactory; import org.deegree.graphics.sld.StyledLayerDescriptor; import org.deegree.graphics.sld.UserStyle; import org.deegree.igeo.ApplicationContainer; import org.deegree.igeo.commands.GeoRefCommand; import org.deegree.igeo.commands.model.AddFileLayerCommand; import org.deegree.igeo.commands.model.ZoomCommand; import org.deegree.igeo.config.DirectStyleType; import org.deegree.igeo.config.EnvelopeType; import org.deegree.igeo.config.LayerType.MetadataURL; import org.deegree.igeo.config.MemoryDatasourceType; import org.deegree.igeo.config.ObjectFactory; import org.deegree.igeo.config.StyleType; import org.deegree.igeo.config.ToolbarEntryType; import org.deegree.igeo.desktop.IGeoDesktop; import org.deegree.igeo.i18n.Messages; import org.deegree.igeo.mapmodel.Datasource; import org.deegree.igeo.mapmodel.DirectStyle; import org.deegree.igeo.mapmodel.Layer; import org.deegree.igeo.mapmodel.MapModel; import org.deegree.igeo.mapmodel.MemoryDatasource; import org.deegree.igeo.mapmodel.NamedStyle; import org.deegree.igeo.mapmodel.SystemLayer; import org.deegree.igeo.modules.DefaultMapModule; import org.deegree.igeo.modules.georef.AffineTransformation; import org.deegree.igeo.modules.georef.ControlPointModel; import org.deegree.igeo.views.swing.ButtonGroup; import org.deegree.igeo.views.swing.map.DefaultMapComponent; import org.deegree.igeo.views.swing.util.GenericFileChooser; import org.deegree.igeo.views.swing.util.GenericFileChooser.FILECHOOSERTYPE; import org.deegree.igeo.views.swing.util.IGeoFileFilter; import org.deegree.kernel.CommandProcessedEvent; import org.deegree.kernel.CommandProcessedListener; import org.deegree.kernel.ProcessMonitor; import org.deegree.kernel.ProcessMonitorFactory; import org.deegree.model.Identifier; import org.deegree.model.feature.FeatureFactory; import org.deegree.model.spatialschema.Envelope; import org.deegree.model.spatialschema.GeometryFactory; /** * TODO add class documentation * * @author <a href="mailto:wanhoff@lat-lon.de">Jeronimo Wanhoff</a> * @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a> * @author last edited by: $Author: stranger $ * * @version $Revision: $, $Date: $ */ public class GeoreferencingControlPanel extends JPanel implements ActionListener { private static final long serialVersionUID = 7031021591515735164L; DefaultMapModule<?> leftModule, rightModule; Layer leftLayer, rightLayer; MapModel left, right; Buttons buttons = new Buttons(); ControlPointModel points; private File worldFile; private File sourceFile; // needs to be cleaned up upon tool close MouseAdapter leftMouseAdapter; public GeoreferencingControlPanel() { setLayout( new GridBagLayout() ); GridBagConstraints gb = new GridBagConstraints(); gb.gridx = 0; gb.gridy = 0; gb.gridwidth = 2; gb.anchor = CENTER; gb.insets = new Insets( 2, 2, 2, 2 ); add( buttons.load = new JButton( get( "$DI10074" ) ), gb ); buttons.load.addActionListener( this ); gb = (GridBagConstraints) gb.clone(); ++gb.gridy; buttons.transformList = new JComboBox( new String[] { get( "$DI10075" ) } ); add( buttons.transformList, gb ); gb = (GridBagConstraints) gb.clone(); gb.gridx = 2; gb.gridy = 0; gb.gridwidth = 4; gb.gridheight = 2; gb.fill = BOTH; JPanel panel = new JPanel(); panel.setBorder( createTitledBorder( get( "$DI10085" ) ) ); panel.add( new JLabel( get( "$DI10086" ) ) ); add( panel, gb ); panel = new JPanel(); panel.setLayout( new GridBagLayout() ); gb = (GridBagConstraints) gb.clone(); gb.gridx = 0; gb.gridy = 0; gb.gridwidth = 1; gb.gridheight = 1; gb.fill = NONE; panel.add( buttons.loadTable = new JButton( get( "$DI10077" ) ), gb ); buttons.loadTable.addActionListener( this ); gb = (GridBagConstraints) gb.clone(); ++gb.gridx; panel.add( buttons.activate = new JToggleButton( get( "$DI10076" ) ), gb ); buttons.activate.addActionListener( this ); gb = (GridBagConstraints) gb.clone(); ++gb.gridx; panel.add( buttons.saveTable = new JButton( get( "$DI10078" ) ), gb ); buttons.saveTable.addActionListener( this ); gb = (GridBagConstraints) gb.clone(); gb.gridx = 0; gb.gridwidth = 6; gb.gridy = 2; add( panel, gb ); gb = (GridBagConstraints) gb.clone(); gb.gridx = 0; gb.gridy = 3; gb.gridwidth = 6; gb.fill = BOTH; gb.weightx = 1; gb.weighty = 1; buttons.table = new JTable( points = new ControlPointModel() ); add( new JScrollPane( buttons.table ), gb ); panel = new JPanel(); panel.setLayout( new GridBagLayout() ); gb = (GridBagConstraints) gb.clone(); ++gb.gridy; gb.gridwidth = 6; gb.weightx = 0; gb.weighty = 0; gb.fill = NONE; add( panel, gb ); gb = (GridBagConstraints) gb.clone(); gb.gridx = 0; gb.gridy = 0; gb.gridwidth = 1; panel.add( buttons.delete = new JButton( get( "$DI10081" ) ), gb ); buttons.delete.addActionListener( this ); gb = (GridBagConstraints) gb.clone(); ++gb.gridx; panel.add( buttons.reset = new JButton( get( "$DI10082" ) ), gb ); buttons.reset.addActionListener( this ); gb = (GridBagConstraints) gb.clone(); ++gb.gridx; panel.add( buttons.start = new JButton( get( "$DI10083" ) ), gb ); buttons.start.addActionListener( this ); buttons.enable( false ); } /** * * @param leftModule * @param left * @param rightModule * @param right map model. Must not be <code>null</code>. */ public void setMapModel( DefaultMapModule<?> leftModule, MapModel left, DefaultMapModule<?> rightModule, MapModel right ) { this.leftModule = leftModule; this.left = left; this.rightModule = rightModule; this.right = right; leftLayer = addPointsLayer( left ); rightLayer = addPointsLayer( right ); DefaultMapComponent mc = (DefaultMapComponent) rightModule.getMapContainer(); mc.addMouseListener( new MouseAdapter() { @Override public void mouseClicked( MouseEvent e ) { if ( !buttons.activate.isSelected() ) { return; } points.clickedRight( e.getX(), e.getY() ); AffineTransformation.approximate( points.getPoints() ); points.fireTableDataChanged(); } } ); mc = (DefaultMapComponent) leftModule.getMapContainer(); mc.addMouseListener( this.leftMouseAdapter = new MouseAdapter() { @Override public void mouseClicked( MouseEvent e ) { if ( !buttons.activate.isSelected() ) { return; } points.clickedLeft( e.getX(), e.getY() ); AffineTransformation.approximate( points.getPoints() ); points.fireTableDataChanged(); } } ); points.updateMaps( left, leftLayer, right, rightLayer ); } @Override public void actionPerformed( ActionEvent e ) { if ( e.getSource() == buttons.load ) { loadRaster(); } if ( e.getSource() == buttons.start ) { startTransformation(); } if ( e.getSource() == buttons.activate ) { toggleControlPointMode(); } if ( e.getSource() == buttons.loadTable ) { loadCsv(); } if ( e.getSource() == buttons.saveTable ) { saveCsv(); } if ( e.getSource() == buttons.reset ) { points.removeAll(); } if ( e.getSource() == buttons.delete ) { points.remove( buttons.table.getSelectedRows() ); AffineTransformation.approximate( points.getPoints() ); points.fireTableDataChanged(); points.updateMaps(); } } private void toggleControlPointMode() { if ( buttons.activate.isSelected() ) { leftModule.getMapTool().resetState(); rightModule.getMapTool().resetState(); // TODO some proper support from the maptool would be nice Map<String, ButtonGroup> groups = leftModule.getApplicationContainer().getButtonGroups(); for ( ToolbarEntryType tp : leftModule.getToolBarEntries() ) { ButtonGroup bg = groups.get( tp.getAssignedGroup() ); if ( bg != null ) { bg.clearSelection(); bg.removeSelection(); } } for ( ToolbarEntryType tp : rightModule.getToolBarEntries() ) { ButtonGroup bg = groups.get( tp.getAssignedGroup() ); if ( bg != null ) { bg.clearSelection(); bg.removeSelection(); } } } } private void loadCsv() { ApplicationContainer<?> appContainer = rightModule.getApplicationContainer(); File file = null; Preferences prefs = Preferences.userNodeForPackage( GeoreferencingControlPanel.class ); List<IGeoFileFilter> ff = new ArrayList<IGeoFileFilter>(); ff.add( new IGeoFileFilter( "csv", "pkt", "pas" ) ); file = GenericFileChooser.showOpenDialog( FILECHOOSERTYPE.externalResource, appContainer, ( (IGeoDesktop) appContainer ).getMainWndow(), prefs, "georefCsv", ff ); if ( file != null ) { try { points.loadPointsFromFile( file ); AffineTransformation.approximate( points.getPoints() ); points.fireTableDataChanged(); } catch ( IOException e ) { // TODO Auto-generated catch block e.printStackTrace(); } } } private void saveCsv() { ApplicationContainer<?> appContainer = rightModule.getApplicationContainer(); File file = null; Preferences prefs = Preferences.userNodeForPackage( GeoreferencingControlPanel.class ); List<IGeoFileFilter> ff = new ArrayList<IGeoFileFilter>(); ff.add( new IGeoFileFilter( "csv", "pkt", "pas" ) ); file = GenericFileChooser.showSaveDialog( FILECHOOSERTYPE.geoDataFile, appContainer, ( (IGeoDesktop) appContainer ).getMainWndow(), prefs, "georefCsv", ff ); if ( file != null ) { try { points.savePointsToFile( file ); } catch ( IOException e ) { // TODO Auto-generated catch block e.printStackTrace(); } } } private String findGdal() { Preferences prefs = Preferences.userNodeForPackage( GeoreferencingControlPanel.class ); String prefix = prefs.get( "gdal_location", "" ); InputStream in = null; try { ProcessBuilder pb = new ProcessBuilder(); pb.command( "gdalwarp" ); Process p = pb.start(); in = p.getInputStream(); String s = new String( IOUtils.toByteArray( in ), "UTF-8" ); if ( p.waitFor() != 1 || !s.startsWith( "Usage:" ) ) { JFileChooser dlg = new JFileChooser( new File( prefix ) ); dlg.setDialogTitle( Messages.get( "$DI10092" ) ); dlg.setFileSelectionMode( JFileChooser.DIRECTORIES_ONLY ); int res = dlg.showDialog( this, Messages.get( "$DI10093" ) ); if ( res != JFileChooser.APPROVE_OPTION ) { return null; } if ( !new File( dlg.getSelectedFile(), "gdalwarp" ).exists() && !new File( dlg.getSelectedFile(), "gdalwarp.exe" ).exists() ) { return null; } prefix = dlg.getSelectedFile().toString() + File.separator; prefs.put( "gdal_location", prefix ); } } catch ( Throwable e ) { JFileChooser dlg = new JFileChooser( new File( prefix ) ); dlg.setDialogTitle( Messages.get( "$DI10092" ) ); dlg.setFileSelectionMode( JFileChooser.DIRECTORIES_ONLY ); int res = dlg.showDialog( this, Messages.get( "$DI10093" ) ); if ( res != JFileChooser.APPROVE_OPTION ) { return null; } if ( !new File( dlg.getSelectedFile(), "gdalwarp" ).exists() && !new File( dlg.getSelectedFile(), "gdalwarp.exe" ).exists() ) { return null; } prefix = dlg.getSelectedFile().toString() + File.separator; prefs.put( "gdal_location", prefix ); } finally { if ( in != null ) { try { in.close(); } catch ( IOException e ) { // probably was not open } } } return prefix; } private void startTransformation() { ApplicationContainer<?> appContainer = rightModule.getApplicationContainer(); File file = null; if ( "Application".equalsIgnoreCase( appContainer.getViewPlatform() ) ) { Preferences prefs = Preferences.userNodeForPackage( GeoreferencingControlPanel.class ); List<IGeoFileFilter> ff = new ArrayList<IGeoFileFilter>(); ff.add( IGeoFileFilter.TIFF ); ff.add( IGeoFileFilter.PNG ); file = GenericFileChooser.showSaveDialog( FILECHOOSERTYPE.geoDataFile, appContainer, ( (IGeoDesktop) appContainer ).getMainWndow(), prefs, "georefTarget", ff ); } if ( file != null ) { runGdal( file ); } } private void runGdal( File file ) { PrintStream out = null; try { out = new PrintStream( new FileOutputStream( worldFile ) ); // since source projection was identity, we can use the transform directly as .wld double[] trans = AffineTransformation.approximate( points.getPoints() ); for ( double d : trans ) { out.println( d ); } String prefix = findGdal(); if ( prefix == null ) { JOptionPane.showMessageDialog( this, Messages.get( "$DI10094" ) ); return; } GeoRefCommand command = new GeoRefCommand( prefix, left.getCoordinateSystem().getPrefixedName(), sourceFile, file ); final ProcessMonitor pm = ProcessMonitorFactory.createDialogProcessMonitor( rightModule.getApplicationContainer().getViewPlatform(), Messages.get( "$DI10087" ), Messages.get( "$DI10088", file ), 0, 100, command ); command.setProcessMonitor( pm ); command.addListener( new CommandProcessedListener() { @Override public void commandProcessed( CommandProcessedEvent event ) { try { pm.cancel(); JOptionPane.showMessageDialog( rightModule.getApplicationContainer().getMainWndow(), Messages.get( "$DI10089" ), Messages.get( "$DI10090" ), JOptionPane.INFORMATION_MESSAGE ); } catch ( Exception e ) { e.printStackTrace(); } } } ); rightModule.getApplicationContainer().getCommandProcessor().executeASychronously( command ); } catch ( FileNotFoundException e ) { // unlikely, as the parent directory exists for sure } finally { if ( out != null ) { out.close(); } } } private void loadRaster() { ApplicationContainer<?> appContainer = rightModule.getApplicationContainer(); File file = null; if ( "Application".equalsIgnoreCase( appContainer.getViewPlatform() ) ) { Preferences prefs = Preferences.userNodeForPackage( GeoreferencingControlPanel.class ); List<IGeoFileFilter> ff = new ArrayList<IGeoFileFilter>(); ff.add( IGeoFileFilter.TIFF ); ff.add( IGeoFileFilter.PNG ); file = GenericFileChooser.showOpenDialog( FILECHOOSERTYPE.geoDataFile, appContainer, ( (IGeoDesktop) appContainer ).getMainWndow(), prefs, "georefLoad", ff ); } if ( file != null ) { sourceFile = file; PrintStream out = null; try { worldFile = new File( file.toString().substring( 0, file.toString().length() - 4 ) + ".wld" ); out = new PrintStream( new FileOutputStream( worldFile ) ); // use the image coordinate system here (identity matrix, no translation), just flip out.println( 1 ); out.println( 0 ); out.println( 0 ); out.println( -1 ); out.println( 0 ); out.println( 0 ); // zoom to scale = 1, 0/0 ZoomCommand cmd = new ZoomCommand( right ); Container c = (Container) rightModule.getGUIContainer(); Envelope env = GeometryFactory.createEnvelope( 0, -c.getHeight(), c.getWidth(), 0, null ); cmd.setZoomBox( env, c.getWidth(), c.getHeight() ); appContainer.getCommandProcessor().executeSychronously( cmd, true ); } catch ( Throwable e ) { e.printStackTrace(); } finally { if (out != null) out.close(); } String crsName = right.getCoordinateSystem().getPrefixedName(); final AddFileLayerCommand command = new AddFileLayerCommand( right, file, null, null, null, crsName ); final ProcessMonitor pm = ProcessMonitorFactory.createDialogProcessMonitor( appContainer.getViewPlatform(), Messages.get( "$MD11264" ), Messages.get( "$MD11265", file ), 0, -1, command ); command.setProcessMonitor( pm ); command.addListener( new CommandProcessedListener() { @Override public void commandProcessed( CommandProcessedEvent event ) { buttons.enable( true ); try { pm.cancel(); } catch ( Exception e ) { e.printStackTrace(); } } } ); appContainer.getCommandProcessor().executeASychronously( command ); } } private static Layer addPointsLayer( MapModel mm ) { if ( mm.getLayerByIdentifier( new Identifier( "georef" ) ) != null ) { return mm.getLayerByIdentifier( new Identifier( "georef" ) ); } Envelope env = mm.getEnvelope(); MemoryDatasourceType mdst = new MemoryDatasourceType(); EnvelopeType et = new EnvelopeType(); et.setMinx( env.getMin().getX() ); et.setMiny( env.getMin().getY() ); et.setMaxx( env.getMax().getX() ); et.setMaxy( env.getMax().getY() ); et.setCrs( mm.getEnvelope().getCoordinateSystem().getPrefixedName() ); mdst.setExtent( et ); mdst.setMinScaleDenominator( 0d ); mdst.setMaxScaleDenominator( 100000000d ); Datasource ds = new MemoryDatasource( mdst, null, null, FeatureFactory.createFeatureCollection( null, 0 ) ); Identifier id = new Identifier( "georef" ); SystemLayer newLayer = new SystemLayer( mm, id, id.getValue(), "", singletonList( ds ), Collections.<MetadataURL> emptyList() ); newLayer.setEditable( false ); newLayer.setVisibleInLayerTree( false ); newLayer.setVisible( true ); List<StyleType> styleTypes = new ArrayList<StyleType>(); StyleType st = new StyleType(); DirectStyleType dst = new DirectStyleType(); String litSld = "<?xml version=\"1.0\"?>" + "<StyledLayerDescriptor xmlns=\"http://www.opengis.net/sld\" xmlns:gml=\"http://www.opengis.net/gml\" xmlns:ogc=\"http://www.opengis.net/ogc\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" version=\"1.0.0\">" + " <NamedLayer>" + " <Name>georef</Name>" + " <UserStyle xmlns=\"http://www.opengis.net/sld\" xmlns:gml=\"http://www.opengis.net/gml\" xmlns:ogc=\"http://www.opengis.net/ogc\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">" + " <Name>default</Name>" + " <Title>default</Title>" + " <Abstract>default3</Abstract>" + " <FeatureTypeStyle>" + " <Title>Regel 1</Title>" + " <Rule>" + " <Name>default</Name>" + " <Title>default</Title>" + " <Abstract>default</Abstract>" + " <MinScaleDenominator>0.0</MinScaleDenominator>" + " <MaxScaleDenominator>1.0E9</MaxScaleDenominator>" + " <PointSymbolizer>" + " <Graphic>" + " <Mark>" + " <WellKnownName>x</WellKnownName>" + " <Fill>" + " <CssParameter name=\"fill\">#808080</CssParameter>" + " <CssParameter name=\"fill-opacity\">1.0</CssParameter>" + " </Fill>" + " <Stroke>" + " <CssParameter name=\"stroke\">#8f242a</CssParameter>" + " </Stroke>" + " </Mark>" + " <Opacity>1.0</Opacity>" + " <Size>20.0</Size>" + " <Rotation>0.0</Rotation>" + " </Graphic>" + " </PointSymbolizer>" + " </Rule>" + " </FeatureTypeStyle>" + " </UserStyle>" + " </NamedLayer>" + "</StyledLayerDescriptor>"; dst.setSld( litSld ); JAXBElement<DirectStyleType> dsjx = new ObjectFactory().createDirectStyle( dst ); st.setNamedStyle( dsjx ); dst.setCurrent( true ); styleTypes.add( st ); StyledLayerDescriptor sld = null; try { sld = SLDFactory.createSLD( litSld ); NamedStyle nStyle = new DirectStyle( dst, (UserStyle) sld.getNamedLayers()[0].getStyles()[0], newLayer ); nStyle.setCurrent( true ); ArrayList<NamedStyle> styles = new ArrayList<NamedStyle>(); styles.add( nStyle ); newLayer.setStyles( styles ); } catch ( Throwable e ) { // TODO Auto-generated catch block e.printStackTrace(); } mm.insert( newLayer, mm.getLayerGroups().get( 0 ), null, true ); return newLayer; } static class Buttons { JButton load, loadTable, saveTable, delete, reset, start; JToggleButton activate; JComboBox transformList; JTable table; void enable( boolean b ) { loadTable.setEnabled( b ); saveTable.setEnabled( b ); delete.setEnabled( b ); reset.setEnabled( b ); start.setEnabled( b ); activate.setEnabled( b ); transformList.setEnabled( b ); table.setEnabled( b ); } } }