/*
* uDig - User Friendly Desktop Internet GIS client
* (C) HydroloGIS - www.hydrologis.com
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* (http://www.eclipse.org/legal/epl-v10.html), and the HydroloGIS BSD
* License v1.0 (http://udig.refractions.net/files/hsd3-v10.html).
*/
package org.locationtech.udig.tools.jgrass.geopaparazzi;
import java.io.File;
import java.io.FileInputStream;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.apache.commons.io.FileUtils;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.wizard.Wizard;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IImportWizard;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.progress.IProgressService;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.feature.DefaultFeatureCollection;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.geometry.jts.JTS;
import org.geotools.referencing.CRS;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.locationtech.udig.project.ui.ApplicationGIS;
import org.locationtech.udig.tools.jgrass.JGrassToolsPlugin;
import org.locationtech.udig.ui.ExceptionDetailsDialog;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.MultiLineString;
import com.vividsolutions.jts.geom.Point;
/**
* The wizard to import for geopaparazzi data.
*
* @author Andrea Antonello (www.hydrologis.com)
*/
@SuppressWarnings("nls")
public class ImportGeopaparazziFolderWizard extends Wizard implements IImportWizard {
private static final String[] GEOPAPARAZZI_NOTES_DESCRIPTIONFIELDS = {"DESCRIPTION", "TIMESTAMP", "ALTIM"};
private static final String GEOPAPARAZZI_NOTES_OUTPUTSHAPEFILENAME = "notes.shp";
private ImportGeopaparazziFolderWizardPage mainPage;
private static GeometryFactory gF = new GeometryFactory();
private CoordinateReferenceSystem mapCrs;
private boolean canFinish = true;
public ImportGeopaparazziFolderWizard() {
super();
}
@Override
public boolean canFinish() {
return canFinish;
}
public boolean performFinish() {
Display.getDefault().asyncExec(new Runnable(){
public void run() {
try {
IWorkbench wb = PlatformUI.getWorkbench();
IProgressService ps = wb.getProgressService();
ps.busyCursorWhile(new IRunnableWithProgress(){
public void run( IProgressMonitor pm ) {
String geopaparazziFolderPath = mainPage.getGeopaparazziFolderPath();
File geopapFolderFile = new File(geopaparazziFolderPath);
File geopapDatabaseFile = new File(geopaparazziFolderPath, "geopaparazzi.db");
if (!geopapDatabaseFile.exists()) {
MessageDialog.openError(getShell(), "Missing database",
"The geopaparazzi database file (geopaparazzi.db) is missing. Check the inserted path.");
return;
}
String outputFolderPath = mainPage.getOutputFolderPath();
File outputFolderFile = new File(outputFolderPath);
mapCrs = ApplicationGIS.getActiveMap().getViewportModel().getCRS();
Connection connection = null;
try {
// create a database connection
connection = DriverManager.getConnection("jdbc:sqlite:" + geopapDatabaseFile.getAbsolutePath());
if (geopapDatabaseFile.exists()) {
/*
* import notes as shapefile
*/
notesToShapefile(connection, outputFolderFile, pm);
/*
* import gps logs as shapefiles, once as lines and once as points
*/
gpsLogToShapefiles(connection, outputFolderFile, pm);
}
/*
* import media as point shapefile, containin gthe path
*/
mediaToShapeFile(geopapFolderFile, outputFolderFile, pm);
} catch (Exception e) {
String message = "An error occurred while importing from geopaparazzi.";
ExceptionDetailsDialog.openError(null, message, IStatus.ERROR, JGrassToolsPlugin.PLUGIN_ID, e);
} finally {
try {
if (connection != null)
connection.close();
} catch (SQLException e) {
// connection close failed.
String message = "An error occurred while closing the database connection.";
ExceptionDetailsDialog
.openError(null, message, IStatus.ERROR, JGrassToolsPlugin.PLUGIN_ID, e);
}
}
}
});
} catch (Exception e1) {
e1.printStackTrace();
String message = "An error occurred while extracting the data from the database.";
ExceptionDetailsDialog.openError(null, message, IStatus.ERROR, JGrassToolsPlugin.PLUGIN_ID, e1);
}
}
});
return true;
}
public void init( IWorkbench workbench, IStructuredSelection selection ) {
setWindowTitle("Geopaparazzi Import Wizard"); // NON-NLS-1
setNeedsProgressMonitor(true);
mainPage = new ImportGeopaparazziFolderWizardPage("Import GeoPaparazzi Data Folder", selection);
}
public void addPages() {
super.addPages();
addPage(mainPage);
// make sure sqlite driver are there
try {
Class.forName("org.sqlite.JDBC");
canFinish = true;
} catch (Exception e) {
String message = "An error occurred while loading the database drivers. Check your installation.";
ExceptionDetailsDialog.openError(null, message, IStatus.ERROR, JGrassToolsPlugin.PLUGIN_ID, e);
canFinish = false;
}
}
private void notesToShapefile( Connection connection, File outputFolderFile, IProgressMonitor pm ) throws Exception {
File outputShapeFile = new File(outputFolderFile, GEOPAPARAZZI_NOTES_OUTPUTSHAPEFILENAME);
SimpleFeatureTypeBuilder b = new SimpleFeatureTypeBuilder();
b.setName("geopaparazzinotes"); //$NON-NLS-1$
b.setCRS(mapCrs);
b.add("the_geom", Point.class); //$NON-NLS-1$
for( String fieldName : GEOPAPARAZZI_NOTES_DESCRIPTIONFIELDS ) {
b.add(fieldName, String.class);
}
SimpleFeatureType featureType = b.buildFeatureType();
MathTransform transform = CRS.findMathTransform(DefaultGeographicCRS.WGS84, mapCrs);
pm.beginTask("Import notes...", IProgressMonitor.UNKNOWN);
DefaultFeatureCollection newCollection = new DefaultFeatureCollection();
Statement statement = null;
try {
statement = connection.createStatement();
statement.setQueryTimeout(30); // set timeout to 30 sec.
ResultSet rs = statement.executeQuery("select lat, lon, altim, ts, text from notes");
int i = 0;
while( rs.next() ) {
double lat = rs.getDouble("lat");
double lon = rs.getDouble("lon");
double altim = rs.getDouble("altim");
String dateTimeString = rs.getString("ts");
String text = rs.getString("text");
if (lat == 0 || lon == 0) {
continue;
}
// and then create the features
Coordinate c = new Coordinate(lon, lat);
Point point = gF.createPoint(c);
Geometry reprojectPoint = JTS.transform(point, transform);
SimpleFeatureBuilder builder = new SimpleFeatureBuilder(featureType);
Object[] values = new Object[]{reprojectPoint, text, dateTimeString, String.valueOf(altim)};
builder.addAll(values);
SimpleFeature feature = builder.buildFeature(featureType.getTypeName() + "." + i++);
newCollection.add(feature);
pm.worked(1);
}
} finally {
pm.done();
if (statement != null)
statement.close();
}
ShapefileDataStoreFactory factory = new ShapefileDataStoreFactory();
Map<String, Serializable> params = new HashMap<String, Serializable>();
params.put("url", outputShapeFile.toURI().toURL());
params.put("create spatial index", Boolean.TRUE);
ShapefileDataStore dStore = (ShapefileDataStore) factory.createNewDataStore(params);
dStore.createSchema(featureType);
dStore.forceSchemaCRS(mapCrs);
JGrassToolsPlugin.getDefault().writeToShapefile(dStore, newCollection);
JGrassToolsPlugin.getDefault().addServiceToCatalogAndMap(outputShapeFile.getAbsolutePath(), true, true,
new NullProgressMonitor());
}
private void gpsLogToShapefiles( Connection connection, File outputFolderFile, IProgressMonitor pm ) throws Exception {
File outputLinesShapeFile = new File(outputFolderFile, "gpslines.shp");
Statement statement = connection.createStatement();
statement.setQueryTimeout(30); // set timeout to 30 sec.
List<GpsLog> logsList = new ArrayList<ImportGeopaparazziFolderWizard.GpsLog>();
// first get the logs
ResultSet rs = statement.executeQuery("select _id, startts, endts, text from gpslogs");
while( rs.next() ) {
long id = rs.getLong("_id");
String startDateTimeString = rs.getString("startts");
String endDateTimeString = rs.getString("endts");
String text = rs.getString("text");
GpsLog log = new GpsLog();
log.id = id;
log.startTime = startDateTimeString;
log.endTime = endDateTimeString;
log.text = text;
logsList.add(log);
}
statement.close();
try {
// then the log data
for( GpsLog log : logsList ) {
long logId = log.id;
String query = "select lat, lon, altim, ts from gpslog_data where logid = " + logId + " order by ts";
Statement newStatement = connection.createStatement();
newStatement.setQueryTimeout(30);
ResultSet result = newStatement.executeQuery(query);
while( result.next() ) {
double lat = result.getDouble("lat");
double lon = result.getDouble("lon");
double altim = result.getDouble("altim");
String dateTimeString = result.getString("ts");
GpsPoint gPoint = new GpsPoint();
gPoint.lon = lon;
gPoint.lat = lat;
gPoint.altim = altim;
gPoint.utctime = dateTimeString;
log.points.add(gPoint);
}
newStatement.close();
}
} catch (Exception e) {
e.printStackTrace();
String message = "An error occurred while reading the gps logs.";
ExceptionDetailsDialog.openError(null, message, IStatus.ERROR, JGrassToolsPlugin.PLUGIN_ID, e);
return;
}
/*
* create the lines shapefile
*/
SimpleFeatureTypeBuilder b = new SimpleFeatureTypeBuilder();
b.setName("geopaparazzinotes");
b.setCRS(mapCrs);
b.add("the_geom", MultiLineString.class);
b.add("STARTDATE", String.class);
b.add("ENDDATE", String.class);
b.add("DESCR", String.class);
SimpleFeatureType featureType = b.buildFeatureType();
try {
MathTransform transform = CRS.findMathTransform(DefaultGeographicCRS.WGS84, mapCrs);
pm.beginTask("Import gps to lines...", logsList.size());
DefaultFeatureCollection newCollection = new DefaultFeatureCollection();
int index = 0;
for( GpsLog log : logsList ) {
List<GpsPoint> points = log.points;
List<Coordinate> coordList = new ArrayList<Coordinate>();
String startDate = log.startTime;
String endDate = log.endTime;
for( GpsPoint gpsPoint : points ) {
Coordinate c = new Coordinate(gpsPoint.lon, gpsPoint.lat);
coordList.add(c);
}
Coordinate[] coordArray = (Coordinate[]) coordList.toArray(new Coordinate[coordList.size()]);
if (coordArray.length < 2) {
continue;
}
LineString lineString = gF.createLineString(coordArray);
LineString reprojectLineString = (LineString) JTS.transform(lineString, transform);
MultiLineString multiLineString = gF.createMultiLineString(new LineString[]{reprojectLineString});
SimpleFeatureBuilder builder = new SimpleFeatureBuilder(featureType);
Object[] values = new Object[]{multiLineString, startDate, endDate, log.text};
builder.addAll(values);
SimpleFeature feature = builder.buildFeature(featureType.getTypeName() + "." + index++);
newCollection.add(feature);
pm.worked(1);
}
pm.done();
ShapefileDataStoreFactory factory = new ShapefileDataStoreFactory();
Map<String, Serializable> params = new HashMap<String, Serializable>();
params.put("url", outputLinesShapeFile.toURI().toURL());
params.put("create spatial index", Boolean.TRUE);
ShapefileDataStore dStore = (ShapefileDataStore) factory.createNewDataStore(params);
dStore.createSchema(featureType);
dStore.forceSchemaCRS(mapCrs);
JGrassToolsPlugin.getDefault().writeToShapefile(dStore, newCollection);
JGrassToolsPlugin.getDefault().addServiceToCatalogAndMap(outputLinesShapeFile.getAbsolutePath(), true, true,
new NullProgressMonitor());
} catch (Exception e1) {
JGrassToolsPlugin.log(e1.getLocalizedMessage(), e1);
e1.printStackTrace();
}
/*
* create the points shapefile
*/
File outputPointsShapeFile = new File(outputFolderFile, "gpspoints.shp");
b = new SimpleFeatureTypeBuilder();
b.setName("geopaparazzinotes");
b.setCRS(mapCrs);
b.add("the_geom", Point.class);
b.add("ALTIMETRY", String.class);
b.add("DATE", String.class);
featureType = b.buildFeatureType();
try {
MathTransform transform = CRS.findMathTransform(DefaultGeographicCRS.WGS84, mapCrs);
pm.beginTask("Import gps to points...", logsList.size());
DefaultFeatureCollection newCollection = new DefaultFeatureCollection();
int index = 0;
for( GpsLog log : logsList ) {
List<GpsPoint> gpsPointList = log.points;
for( GpsPoint gpsPoint : gpsPointList ) {
Coordinate c = new Coordinate(gpsPoint.lon, gpsPoint.lat);
Point point = gF.createPoint(c);
Point reprojectPoint = (Point) JTS.transform(point, transform);
Object[] values = new Object[]{reprojectPoint, String.valueOf(gpsPoint.altim), gpsPoint.utctime};
SimpleFeatureBuilder builder = new SimpleFeatureBuilder(featureType);
builder.addAll(values);
SimpleFeature feature = builder.buildFeature(featureType.getTypeName() + "." + index++);
newCollection.add(feature);
}
pm.worked(1);
}
pm.done();
ShapefileDataStoreFactory factory = new ShapefileDataStoreFactory();
Map<String, Serializable> params = new HashMap<String, Serializable>();
params.put("url", outputPointsShapeFile.toURI().toURL());
params.put("create spatial index", Boolean.TRUE);
ShapefileDataStore dStore = (ShapefileDataStore) factory.createNewDataStore(params);
dStore.createSchema(featureType);
dStore.forceSchemaCRS(mapCrs);
JGrassToolsPlugin.getDefault().writeToShapefile(dStore, newCollection);
JGrassToolsPlugin.getDefault().addServiceToCatalogAndMap(outputPointsShapeFile.getAbsolutePath(), true, true,
new NullProgressMonitor());
} catch (Exception e1) {
JGrassToolsPlugin.log(e1.getLocalizedMessage(), e1);
e1.printStackTrace();
}
}
private void mediaToShapeFile( File geopapFolderFile, File outputFolderFile, IProgressMonitor pm ) throws Exception {
File folder = new File(geopapFolderFile, "media");
if (!folder.exists()) {
// try to see if it is an old version of geopaparazzi
folder = new File(geopapFolderFile, "pictures");
if (!folder.exists()) {
// ignoring non existing things
return;
}
}
// create destination folder
String imageFolderName = "media";
File[] listFiles = folder.listFiles();
List<String> nonTakenFilesList = new ArrayList<String>();
pm.beginTask("Importing media...", listFiles.length);
try {
/*
* create the points shapefile
*/
File outputPointsShapeFile = new File(outputFolderFile, "mediapoints.shp");
SimpleFeatureTypeBuilder b = new SimpleFeatureTypeBuilder();
b.setName("geopaparazzinotes");
b.setCRS(mapCrs);
b.add("the_geom", Point.class);
b.add("ALTIMETRY", String.class);
b.add("DATE", String.class);
b.add("AZIMUTH", Double.class);
b.add("IMAGE", String.class);
SimpleFeatureType featureType = b.buildFeatureType();
MathTransform transform = CRS.findMathTransform(DefaultGeographicCRS.WGS84, mapCrs);
DefaultFeatureCollection newCollection = new DefaultFeatureCollection();
for( File imageFile : listFiles ) {
String name = imageFile.getName();
if (name.endsWith("jpg") || imageFile.getName().endsWith("JPG") || imageFile.getName().endsWith("png")
|| imageFile.getName().endsWith("PNG") || imageFile.getName().endsWith("3gp")) {
String[] nameSplit = name.split("[_\\|.]"); //$NON-NLS-1$
String dateString = nameSplit[1];
String timeString = nameSplit[2];
Properties locationProperties = new Properties();
String mediaPath = imageFile.getAbsolutePath();
int lastDot = mediaPath.lastIndexOf("."); //$NON-NLS-1$
String nameNoExt = mediaPath.substring(0, lastDot);
String infoPath = nameNoExt + ".properties"; //$NON-NLS-1$
File infoFile = new File(infoPath);
if (!infoFile.exists()) {
nonTakenFilesList.add(mediaPath);
continue;
}
locationProperties.load(new FileInputStream(infoFile));
String azimuthString = locationProperties.getProperty("azimuth"); //$NON-NLS-1$
String latString = locationProperties.getProperty("latitude"); //$NON-NLS-1$
String lonString = locationProperties.getProperty("longitude"); //$NON-NLS-1$
String altimString = locationProperties.getProperty("altim"); //$NON-NLS-1$
Double azimuth = -9999.0;
if (azimuthString != null)
azimuth = Double.parseDouble(azimuthString);
double lat = 0.0;
double lon = 0.0;
if (latString.contains("/")) {
// this is an exif string
lat = exifFormat2degreeDecimal(latString);
lon = exifFormat2degreeDecimal(lonString);
} else {
lat = Double.parseDouble(latString);
lon = Double.parseDouble(lonString);
}
double altim = Double.parseDouble(altimString);
Coordinate c = new Coordinate(lon, lat);
Point point = gF.createPoint(c);
String imageRelativePath = imageFolderName + "/" + imageFile.getName();
File newImageFile = new File(outputFolderFile, imageRelativePath);
FileUtils.copyFile(imageFile, newImageFile);
Point reprojectPoint = (Point) JTS.transform(point, transform);
String dateTime = dateString + timeString;
Object[] values = new Object[]{reprojectPoint, String.valueOf(altim), dateTime, azimuth, imageRelativePath};
SimpleFeatureBuilder builder = new SimpleFeatureBuilder(featureType);
builder.addAll(values);
SimpleFeature feature = builder.buildFeature(null);
newCollection.add(feature);
}
pm.worked(1);
}
ShapefileDataStoreFactory factory = new ShapefileDataStoreFactory();
Map<String, Serializable> params = new HashMap<String, Serializable>();
params.put("url", outputPointsShapeFile.toURI().toURL());
params.put("create spatial index", Boolean.TRUE);
ShapefileDataStore dStore = (ShapefileDataStore) factory.createNewDataStore(params);
dStore.createSchema(featureType);
dStore.forceSchemaCRS(mapCrs);
JGrassToolsPlugin.getDefault().writeToShapefile(dStore, newCollection);
JGrassToolsPlugin.getDefault().addServiceToCatalogAndMap(outputPointsShapeFile.getAbsolutePath(), true, true,
new NullProgressMonitor());
} finally {
pm.done();
}
if (nonTakenFilesList.size() > 0) {
final StringBuilder sB = new StringBuilder();
sB.append("For the following media no *.properties file could be found:\n");
for( String p : nonTakenFilesList ) {
sB.append(p).append("\n");
}
Display.getDefault().asyncExec(new Runnable(){
public void run() {
Shell shell = PlatformUI.getWorkbench().getDisplay().getActiveShell();
MessageDialog.openWarning(shell, "Warning", sB.toString());
}
});
} else {
Display.getDefault().asyncExec(new Runnable(){
public void run() {
Shell shell = PlatformUI.getWorkbench().getDisplay().getActiveShell();
MessageDialog.openInformation(shell, "Info", "All media were successfully imported.");
}
});
}
}
private static class GpsPoint {
public double lat;
public double lon;
public double altim;
public String utctime;
}
private static class GpsLog {
public long id;
public String startTime;
public String endTime;
public String text;
public List<GpsPoint> points = new ArrayList<GpsPoint>();
}
/**
* Convert decimal degrees to exif format.
*
* @param decimalDegree the angle in decimal format.
* @return the exif format string.
*/
@SuppressWarnings("nls")
public static String degreeDecimal2ExifFormat( double decimalDegree ) {
StringBuilder sb = new StringBuilder();
sb.append((int) decimalDegree);
sb.append("/1,");
decimalDegree = (decimalDegree - (int) decimalDegree) * 60;
sb.append((int) decimalDegree);
sb.append("/1,");
decimalDegree = (decimalDegree - (int) decimalDegree) * 60000;
sb.append((int) decimalDegree);
sb.append("/1000");
return sb.toString();
}
/**
* Convert exif format to decimal degree.
*
* @param exifFormat the exif string of the gps position.
* @return the decimal degree.
*/
@SuppressWarnings("nls")
public static double exifFormat2degreeDecimal( String exifFormat ) {
// latitude=44/1,10/1,28110/1000
String[] exifSplit = exifFormat.trim().split(",");
String[] value = exifSplit[0].split("/");
double tmp1 = Double.parseDouble(value[0]);
double tmp2 = Double.parseDouble(value[1]);
double degree = tmp1 / tmp2;
value = exifSplit[1].split("/");
tmp1 = Double.parseDouble(value[0]);
tmp2 = Double.parseDouble(value[1]);
double minutes = tmp1 / tmp2;
value = exifSplit[2].split("/");
tmp1 = Double.parseDouble(value[0]);
tmp2 = Double.parseDouble(value[1]);
double seconds = tmp1 / tmp2;
double result = degree + (minutes / 60.0) + (seconds / 3600.0);
return result;
}
}