//$HeadURL$
/*---------------- FILE HEADER ------------------------------------------
This file is part of deegree.
Copyright (C) 2001-2012 by:
Department of Geography, University of Bonn
http://www.giub.uni-bonn.de/deegree/
lat/lon GmbH
http://www.lat-lon.de
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:
lat/lon GmbH
Aennchenstr. 19
53177 Bonn
Germany
E-Mail: info@lat-lon.de
Prof. Dr. Klaus Greve
Department of Geography
University of Bonn
Meckenheimer Allee 166
53115 Bonn
Germany
E-Mail: greve@giub.uni-bonn.de
---------------------------------------------------------------------------*/
package org.deegree.igeo.modules;
import static java.util.Collections.singletonList;
import static java.util.UUID.randomUUID;
import static java.util.prefs.Preferences.userNodeForPackage;
import static org.deegree.crs.coordinatesystems.GeographicCRS.WGS84;
import static org.deegree.framework.log.LoggerFactory.getLogger;
import static org.deegree.framework.util.CollectionUtils.collectionToString;
import static org.deegree.igeo.Version.getVersionNumber;
import static org.deegree.igeo.i18n.Messages.get;
import static org.deegree.model.spatialschema.GeometryFactory.createPoint;
import static org.deegree.ogcbase.CommonNamespaces.getNamespaceContext;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import java.util.prefs.Preferences;
import javax.swing.JFileChooser;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.Sanselan;
import org.apache.sanselan.common.IImageMetadata;
import org.apache.sanselan.formats.jpeg.JpegImageMetadata;
import org.apache.sanselan.formats.jpeg.exifRewrite.ExifRewriter;
import org.apache.sanselan.formats.tiff.TiffImageMetadata;
import org.apache.sanselan.formats.tiff.TiffImageMetadata.GPSInfo;
import org.apache.sanselan.formats.tiff.write.TiffImageWriterLossless;
import org.apache.sanselan.formats.tiff.write.TiffOutputSet;
import org.deegree.datatypes.QualifiedName;
import org.deegree.datatypes.Types;
import org.deegree.framework.log.ILogger;
import org.deegree.framework.util.Pair;
import org.deegree.framework.xml.NamespaceContext;
import org.deegree.igeo.commands.UnselectFeaturesCommand;
import org.deegree.igeo.config.LayerType.MetadataURL;
import org.deegree.igeo.dataadapter.DataAccessFactory;
import org.deegree.igeo.desktop.IGeoDesktop;
import org.deegree.igeo.i18n.Messages;
import org.deegree.igeo.mapmodel.Datasource;
import org.deegree.igeo.mapmodel.Layer;
import org.deegree.igeo.mapmodel.MapModel;
import org.deegree.igeo.modules.ActionDescription.ACTIONTYPE;
import org.deegree.igeo.modules.DefaultMapModule.SelectedFeaturesVisitor;
import org.deegree.igeo.views.DialogFactory;
import org.deegree.model.Identifier;
import org.deegree.model.crs.CRSFactory;
import org.deegree.model.crs.CRSTransformationException;
import org.deegree.model.crs.CoordinateSystem;
import org.deegree.model.crs.GeoTransformer;
import org.deegree.model.feature.Feature;
import org.deegree.model.feature.FeatureCollection;
import org.deegree.model.feature.FeatureFactory;
import org.deegree.model.feature.FeatureProperty;
import org.deegree.model.feature.schema.FeatureType;
import org.deegree.model.feature.schema.PropertyType;
import org.deegree.model.spatialschema.Geometry;
import org.deegree.model.spatialschema.Point;
/**
* Module for to read data from exif header of georeferenced jpeg images
*
* @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
* @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
* @author last edited by: $Author$
*
* @version $Revision$, $Date$
* @param <T>
*/
public class ExifModule<T> extends DefaultModule<T> {
private static final ILogger LOG = getLogger( ExifModule.class );
private static final NamespaceContext nsContext = getNamespaceContext();
private static URI APPNS;
private static final CoordinateSystem unrealWGS84 = CRSFactory.create( WGS84 );
private static final QualifiedName geometry, imageLocation;
private static final FeatureType featureType;
static {
APPNS = URI.create( "http://www.deegree.org/app" );
nsContext.addNamespace( "app", APPNS );
geometry = new QualifiedName( "app", "geometry", APPNS );
imageLocation = new QualifiedName( "app", "imageLocation", APPNS );
PropertyType[] pts = {
FeatureFactory.createGeometryPropertyType( geometry,
new QualifiedName( "gml", "PointPropertyType",
APPNS ), 1, 1 ),
FeatureFactory.createSimplePropertyType( imageLocation, Types.VARCHAR, 1, 1 ) };
featureType = FeatureFactory.createFeatureType( new QualifiedName( "app", "geoimage", APPNS ), false, pts );
}
static {
ActionDescription ad1 = new ActionDescription(
"addImages",
"opens a dialog for adding coordinates read from exif header of an image as new feature",
null, "add feature from exif header", ACTIONTYPE.PushButton,
null, null );
ActionDescription ad2 = new ActionDescription( "linkImage",
"links an image to an already existing point feature", null,
"link image to feature", ACTIONTYPE.PushButton, null, null );
moduleCapabilities = new ModuleCapabilities( ad1, ad2 );
}
/**
*
*/
public void addImages() {
if ( appContainer.getViewPlatform().equalsIgnoreCase( "application" ) ) {
Preferences prefs = userNodeForPackage( ExifModule.class );
String last = prefs.get( "lastExifDir" + getVersionNumber(), null );
JFileChooser chooser = new JFileChooser( last );
chooser.setFileSelectionMode( JFileChooser.FILES_AND_DIRECTORIES );
chooser.setMultiSelectionEnabled( true );
if ( chooser.showOpenDialog( ( (IGeoDesktop) appContainer ).getMainWndow() ) == JFileChooser.APPROVE_OPTION ) {
String name = DialogFactory.openInputDialog( appContainer.getViewPlatform(), getViewForm(),
get( "$MD10558" ), get( "$MD10559" ) );
if ( name == null ) {
return;
}
String mmId = getInitParameter( "assignedMapModel" );
MapModel mm = appContainer.getMapModel( new Identifier( mmId ) );
CoordinateSystem crs = mm.getCoordinateSystem();
GeoTransformer transformer = null;
if ( !mm.getCoordinateSystem().getCRS().equals( WGS84 ) ) {
transformer = new GeoTransformer( crs );
}
FeatureCollection col = FeatureFactory.createFeatureCollection( randomUUID().toString(), 0 );
// col.setFeatureType( featureType );
LinkedList<String> errors = new LinkedList<String>();
for ( File sel : chooser.getSelectedFiles() ) {
if ( sel.isDirectory() ) {
File[] fs = sel.listFiles();
if ( fs != null ) {
for ( File f : fs ) {
handleResult( obtainFeatureFromImage( f, transformer ), errors, col );
}
}
} else {
handleResult( obtainFeatureFromImage( sel, transformer ), errors, col );
}
}
if ( !errors.isEmpty() ) {
DialogFactory.openWarningDialog( appContainer.getViewPlatform(),
( (IGeoDesktop) appContainer ).getMainWndow(),
get( "$MD10895", collectionToString( errors, "\n" ) ),
get( "$DI10036" ) );
}
Datasource ds = DataAccessFactory.createDatasource( UUID.randomUUID().toString(), col );
Layer layer = new Layer( mm, new Identifier( name ), name, name, singletonList( ds ),
Collections.<MetadataURL> emptyList() );
try {
appContainer.getCommandProcessor().executeSychronously( new UnselectFeaturesCommand( mm, false ),
true );
} catch ( Exception e ) {
LOG.logError( e.getMessage(), e );
DialogFactory.openErrorDialog( appContainer.getViewPlatform(), null, Messages.get( "$MD11238" ),
Messages.get( "$MD11239" ), e );
return;
}
List<Layer> list = mm.getLayersSelectedForAction( MapModel.SELECTION_ACTION );
Layer selLayer = list.isEmpty() ? mm.getLayerGroups().get( 0 ).getLayers().get( 0 ) : list.get( 0 );
mm.insert( layer, selLayer.getParent(), selLayer, false );
layer.fireRepaintEvent();
prefs.put( "lastExifDir" + getVersionNumber(), chooser.getSelectedFile().getParent() );
}
}
}
private void handleResult( Pair<Feature, String> pair, LinkedList<String> errors, FeatureCollection fc ) {
Feature feat = pair.first;
if ( feat != null ) {
try {
fc.add( feat );
} catch ( Exception e ) {
LOG.logError( "Unknown error", e );
}
} else {
errors.add( pair.second );
}
}
private Pair<Feature, String> obtainFeatureFromImage( File file, GeoTransformer transformer ) {
try {
IImageMetadata metadata = Sanselan.getMetadata( file );
GPSInfo gps = null;
if ( metadata instanceof JpegImageMetadata ) {
gps = ( (JpegImageMetadata) metadata ).getExif().getGPS();
} else if ( metadata instanceof TiffImageMetadata ) {
gps = ( (TiffImageMetadata) metadata ).getGPS();
} else {
DialogFactory.openErrorDialog( appContainer.getViewPlatform(),
( (IGeoDesktop) appContainer ).getMainWndow(), get( "$MD10898" ),
get( "$DI10017" ) );
return new Pair<Feature, String>();
}
Point point = createPoint( gps.getLongitudeAsDegreesEast(), gps.getLatitudeAsDegreesNorth(), unrealWGS84 );
point = (Point) transformer.transform( point );
FeatureProperty geom = FeatureFactory.createFeatureProperty( geometry, point );
FeatureProperty loc = FeatureFactory.createFeatureProperty( imageLocation, file.getAbsolutePath() );
return new Pair<Feature, String>( FeatureFactory.createFeature( randomUUID().toString(), featureType,
new FeatureProperty[] { loc, geom } ), null );
} catch ( IOException e ) {
LOG.logError( "While loading a file: ", e );
return new Pair<Feature, String>( null, e.getLocalizedMessage() ); // do not include file (it's usually
// included in the message)
} catch ( ImageReadException e ) {
LOG.logError( "While loading a file: ", e );
return new Pair<Feature, String>( null, file.getName() + ": " + e.getLocalizedMessage() );
} catch ( IllegalArgumentException e ) {
LOG.logError( "Unknown error", e );
return new Pair<Feature, String>( null, file.getName() + ": " + e.getLocalizedMessage() );
} catch ( CRSTransformationException e ) {
LOG.logError( "Unknown error", e );
return new Pair<Feature, String>( null, file.getName() + ": " + e.getLocalizedMessage() );
}
}
/**
*
*/
public void linkImage() {
if ( appContainer.getViewPlatform().equalsIgnoreCase( "application" ) ) {
String mmId = getInitParameter( "assignedMapModel" );
MapModel mm = appContainer.getMapModel( new Identifier( mmId ) );
SelectedFeaturesVisitor visitor = new SelectedFeaturesVisitor( 2 );
try {
mm.walkLayerTree( visitor );
if ( visitor.col.size() == 1 ) {
Geometry g = visitor.col.getFeature( 0 ).getDefaultGeometryPropertyValue();
if ( !( g instanceof Point ) ) {
return;
}
Point pt = (Point) g;
if ( !pt.getCoordinateSystem().equals( unrealWGS84 ) ) {
GeoTransformer trans = new GeoTransformer( unrealWGS84 );
pt = (Point) trans.transform( pt );
}
Preferences prefs = userNodeForPackage( ExifModule.class );
String last = prefs.get( "lastExifSaveDir" + getVersionNumber(), null );
JFileChooser chooser = new JFileChooser( last );
if ( chooser.showOpenDialog( ( (IGeoDesktop) appContainer ).getMainWndow() ) == JFileChooser.APPROVE_OPTION ) {
File file = chooser.getSelectedFile();
IImageMetadata metadata = Sanselan.getMetadata( file );
TiffOutputSet outputSet = null;
if ( metadata instanceof JpegImageMetadata ) {
TiffImageMetadata exif = ( (JpegImageMetadata) metadata ).getExif();
outputSet = exif.getOutputSet();
} else if ( metadata instanceof TiffImageMetadata ) {
outputSet = ( (TiffImageMetadata) metadata ).getOutputSet();
}
if ( metadata == null ) {
outputSet = new TiffOutputSet();
}
if ( outputSet == null ) {
DialogFactory.openErrorDialog( appContainer.getViewPlatform(),
( (IGeoDesktop) appContainer ).getMainWndow(),
get( "$MD10898" ), get( "$DI10017" ) );
return;
}
outputSet.setGPSInDegrees( pt.getX(), pt.getY() );
ByteArrayOutputStream out = new ByteArrayOutputStream();
FileInputStream in = new FileInputStream( file );
byte[] buf = new byte[65536];
int read;
while ( ( read = in.read( buf ) ) != -1 ) {
out.write( buf, 0, read );
}
in.close();
out.close();
byte[] bs = out.toByteArray();
FileOutputStream os = new FileOutputStream( file );
if ( metadata == null || metadata instanceof JpegImageMetadata ) {
new ExifRewriter().updateExifMetadataLossless( bs, os, outputSet );
} else {
new TiffImageWriterLossless( bs ).write( os, outputSet );
}
prefs.put( "lastExifSaveDir" + getVersionNumber(), file.getParent() );
DialogFactory.openInformationDialog( appContainer.getViewPlatform(),
( (IGeoDesktop) appContainer ).getMainWndow(),
get( "$MD10900" ), get( "$DI10018" ) );
}
} else {
DialogFactory.openErrorDialog( appContainer.getViewPlatform(),
( (IGeoDesktop) appContainer ).getMainWndow(), get( "$MD10897" ),
get( "$DI10017" ) );
}
} catch ( IOException e ) {
DialogFactory.openErrorDialog( appContainer.getViewPlatform(),
( (IGeoDesktop) appContainer ).getMainWndow(), get( "$MD10899", e ),
get( "$DI10017" ) );
} catch ( Exception e ) {
LOG.logError( "Unknown error", e );
}
}
}
}